From 4b0440901278a07b52a015fdd802ad07a14a816b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hern=C3=A1n=20Wilkinson?= Date: Wed, 29 Jul 2026 11:11:31 -0300 Subject: [PATCH] Add SqueakLiveTyping VM build process (ARM + x64 on Win/Linux/Mac) Adds a first-class, CI-driven build for the LiveTyping variant of the Spur 64-bit Cog VM, branded SqueakLiveTyping, coexisting with the stock Squeak VM. - src/spur64.cog.livetyping: dedicated generated source tree (LiveTypingCoInterpreter / LiveTypingCogit, VMMaker.oscog-eem.3776), split out from the stock src/spur64.cog so both VMs can be built. - building//squeak.cog.spur.livetyping for the six 64-bit targets (macos64x64, macos64ARMv8, win64x64, win64ARMv8, linux64x64, linux64ARMv8), mirroring each platform's squeak.cog.spur flavor but pointing VMSRCDIR/--with-src at the livetyping tree and branding the artifact SqueakLiveTyping (macOS Makefile.app.squeaklivetyping; Windows VM:=SqueakLiveTyping + SqueakLiveTyping.def.in/.rc/.exe.manifest; Linux INSTALLDIR sqcogspurlt64...). - CI: add squeak.cog.spur.livetyping to the matrix.flavor of the six platform workflows, with matrix excludes so LiveTyping builds only the supported 64-bit / threaded-heartbeat legs. Verified locally: macos64ARMv8 fast build produces SqueakLiveTyping.app whose binary reports LiveTypingCoInterpreter/LiveTypingCogit. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/linux-arm64.yml | 1 + .github/workflows/linux.yml | 8 + .github/workflows/macos-arm.yml | 1 + .github/workflows/macos.yml | 1 + .github/workflows/win-arm.yml | 1 + .github/workflows/win.yml | 5 + .../build.assert/mvm | 51 + .../build.debug/mvm | 51 + .../squeak.cog.spur.livetyping/build/mvm | 58 + .../squeak.cog.spur.livetyping/makeclean | 15 + .../squeak.cog.spur.livetyping/makedirty | 15 + .../squeak.cog.spur.livetyping/plugins.ext | 26 + .../squeak.cog.spur.livetyping/plugins.int | 40 + .../build.assert.itimerheartbeat/mvm | 33 + .../build.assert/mvm | 52 + .../build.debug.itimerheartbeat/mvm | 33 + .../build.debug/mvm | 52 + .../build.itimerheartbeat/mvm | 34 + .../squeak.cog.spur.livetyping/build/mvm | 53 + .../squeak.cog.spur.livetyping/makeallclean | 15 + .../squeak.cog.spur.livetyping/makealldirty | 15 + .../squeak.cog.spur.livetyping/makethbdirty | 15 + .../squeak.cog.spur.livetyping/plugins.ext | 27 + .../squeak.cog.spur.livetyping/plugins.int | 40 + .../common/Makefile.app.squeaklivetyping | 25 + .../squeak.cog.spur.livetyping/Makefile | 18 + .../squeak.cog.spur.livetyping/mvm | 47 + .../squeak.cog.spur.livetyping/plugins.ext | 25 + .../squeak.cog.spur.livetyping/plugins.int | 41 + .../common/Makefile.app.squeaklivetyping | 25 + .../squeak.cog.spur.livetyping/Makefile | 14 + .../macos64x64/squeak.cog.spur.livetyping/mvm | 47 + .../squeak.cog.spur.livetyping/plugins.ext | 24 + .../squeak.cog.spur.livetyping/plugins.int | 41 + .../squeak.cog.spur.livetyping/Croquet.def.in | 3 + .../Croquet.exe.manifest | 43 + .../squeak.cog.spur.livetyping/Croquet.ico | Bin 0 -> 1406 bytes .../squeak.cog.spur.livetyping/Croquet.rc | 32 + .../GreenCogSqueak.ico | Bin 0 -> 69927 bytes .../squeak.cog.spur.livetyping/Makefile | 22 + .../SqueakLiveTyping.def.in | 3 + .../SqueakLiveTyping.exe.manifest | 43 + .../SqueakLiveTyping.rc | 32 + .../win64ARMv8/squeak.cog.spur.livetyping/mvm | 53 + .../squeak.cog.spur.livetyping/plugins.ext | 14 + .../squeak.cog.spur.livetyping/plugins.int | 39 + .../squeak.cog.spur.livetyping/squeak.ico | Bin 0 -> 1078 bytes .../squeak.cog.spur.livetyping/Croquet.def.in | 3 + .../Croquet.exe.manifest | 43 + .../squeak.cog.spur.livetyping/Croquet.ico | Bin 0 -> 1406 bytes .../squeak.cog.spur.livetyping/Croquet.rc | 32 + .../GreenCogSqueak.ico | Bin 0 -> 69927 bytes .../squeak.cog.spur.livetyping/Makefile | 22 + .../SqueakLiveTyping.def.in | 3 + .../SqueakLiveTyping.exe.manifest | 43 + .../SqueakLiveTyping.rc | 32 + .../win64x64/squeak.cog.spur.livetyping/mvm | 53 + .../squeak.cog.spur.livetyping/plugins.ext | 18 + .../squeak.cog.spur.livetyping/plugins.int | 39 + .../squeak.cog.spur.livetyping/squeak.ico | Bin 0 -> 1078 bytes src/spur64.cog.livetyping/_variable_order | 18 + src/spur64.cog.livetyping/cogit.c | 29 + src/spur64.cog.livetyping/cogit.h | 150 + src/spur64.cog.livetyping/cogitARMv8.c | 38745 +++++++ src/spur64.cog.livetyping/cogitX64SysV.c | 35613 +++++++ src/spur64.cog.livetyping/cogitX64WIN64.c | 35675 +++++++ src/spur64.cog.livetyping/cogmethod.h | 39 + src/spur64.cog.livetyping/cointerp.c | 88351 +++++++++++++++ src/spur64.cog.livetyping/cointerp.h | 558 + src/spur64.cog.livetyping/cointerpmt.c | 87672 +++++++++++++++ src/spur64.cog.livetyping/gcc3x-cointerp.c | 88358 ++++++++++++++++ src/spur64.cog.livetyping/gcc3x-cointerpmt.c | 87679 +++++++++++++++ src/spur64.cog.livetyping/interp.h | 57 + src/spur64.cog.livetyping/variable_order | 18 + src/spur64.cog.livetyping/vmCallback.h | 41 + 75 files changed, 464524 insertions(+) create mode 100755 building/linux64ARMv8/squeak.cog.spur.livetyping/build.assert/mvm create mode 100755 building/linux64ARMv8/squeak.cog.spur.livetyping/build.debug/mvm create mode 100755 building/linux64ARMv8/squeak.cog.spur.livetyping/build/mvm create mode 100755 building/linux64ARMv8/squeak.cog.spur.livetyping/makeclean create mode 100755 building/linux64ARMv8/squeak.cog.spur.livetyping/makedirty create mode 100644 building/linux64ARMv8/squeak.cog.spur.livetyping/plugins.ext create mode 100644 building/linux64ARMv8/squeak.cog.spur.livetyping/plugins.int create mode 100755 building/linux64x64/squeak.cog.spur.livetyping/build.assert.itimerheartbeat/mvm create mode 100755 building/linux64x64/squeak.cog.spur.livetyping/build.assert/mvm create mode 100755 building/linux64x64/squeak.cog.spur.livetyping/build.debug.itimerheartbeat/mvm create mode 100755 building/linux64x64/squeak.cog.spur.livetyping/build.debug/mvm create mode 100755 building/linux64x64/squeak.cog.spur.livetyping/build.itimerheartbeat/mvm create mode 100755 building/linux64x64/squeak.cog.spur.livetyping/build/mvm create mode 100755 building/linux64x64/squeak.cog.spur.livetyping/makeallclean create mode 100755 building/linux64x64/squeak.cog.spur.livetyping/makealldirty create mode 100755 building/linux64x64/squeak.cog.spur.livetyping/makethbdirty create mode 100644 building/linux64x64/squeak.cog.spur.livetyping/plugins.ext create mode 100644 building/linux64x64/squeak.cog.spur.livetyping/plugins.int create mode 100644 building/macos64ARMv8/common/Makefile.app.squeaklivetyping create mode 100644 building/macos64ARMv8/squeak.cog.spur.livetyping/Makefile create mode 100755 building/macos64ARMv8/squeak.cog.spur.livetyping/mvm create mode 100644 building/macos64ARMv8/squeak.cog.spur.livetyping/plugins.ext create mode 100644 building/macos64ARMv8/squeak.cog.spur.livetyping/plugins.int create mode 100644 building/macos64x64/common/Makefile.app.squeaklivetyping create mode 100644 building/macos64x64/squeak.cog.spur.livetyping/Makefile create mode 100755 building/macos64x64/squeak.cog.spur.livetyping/mvm create mode 100644 building/macos64x64/squeak.cog.spur.livetyping/plugins.ext create mode 100644 building/macos64x64/squeak.cog.spur.livetyping/plugins.int create mode 100644 building/win64ARMv8/squeak.cog.spur.livetyping/Croquet.def.in create mode 100644 building/win64ARMv8/squeak.cog.spur.livetyping/Croquet.exe.manifest create mode 100644 building/win64ARMv8/squeak.cog.spur.livetyping/Croquet.ico create mode 100644 building/win64ARMv8/squeak.cog.spur.livetyping/Croquet.rc create mode 100644 building/win64ARMv8/squeak.cog.spur.livetyping/GreenCogSqueak.ico create mode 100644 building/win64ARMv8/squeak.cog.spur.livetyping/Makefile create mode 100644 building/win64ARMv8/squeak.cog.spur.livetyping/SqueakLiveTyping.def.in create mode 100644 building/win64ARMv8/squeak.cog.spur.livetyping/SqueakLiveTyping.exe.manifest create mode 100644 building/win64ARMv8/squeak.cog.spur.livetyping/SqueakLiveTyping.rc create mode 100755 building/win64ARMv8/squeak.cog.spur.livetyping/mvm create mode 100644 building/win64ARMv8/squeak.cog.spur.livetyping/plugins.ext create mode 100644 building/win64ARMv8/squeak.cog.spur.livetyping/plugins.int create mode 100644 building/win64ARMv8/squeak.cog.spur.livetyping/squeak.ico create mode 100644 building/win64x64/squeak.cog.spur.livetyping/Croquet.def.in create mode 100644 building/win64x64/squeak.cog.spur.livetyping/Croquet.exe.manifest create mode 100644 building/win64x64/squeak.cog.spur.livetyping/Croquet.ico create mode 100644 building/win64x64/squeak.cog.spur.livetyping/Croquet.rc create mode 100644 building/win64x64/squeak.cog.spur.livetyping/GreenCogSqueak.ico create mode 100644 building/win64x64/squeak.cog.spur.livetyping/Makefile create mode 100644 building/win64x64/squeak.cog.spur.livetyping/SqueakLiveTyping.def.in create mode 100644 building/win64x64/squeak.cog.spur.livetyping/SqueakLiveTyping.exe.manifest create mode 100644 building/win64x64/squeak.cog.spur.livetyping/SqueakLiveTyping.rc create mode 100755 building/win64x64/squeak.cog.spur.livetyping/mvm create mode 100644 building/win64x64/squeak.cog.spur.livetyping/plugins.ext create mode 100644 building/win64x64/squeak.cog.spur.livetyping/plugins.int create mode 100644 building/win64x64/squeak.cog.spur.livetyping/squeak.ico create mode 100644 src/spur64.cog.livetyping/_variable_order create mode 100644 src/spur64.cog.livetyping/cogit.c create mode 100644 src/spur64.cog.livetyping/cogit.h create mode 100644 src/spur64.cog.livetyping/cogitARMv8.c create mode 100644 src/spur64.cog.livetyping/cogitX64SysV.c create mode 100644 src/spur64.cog.livetyping/cogitX64WIN64.c create mode 100644 src/spur64.cog.livetyping/cogmethod.h create mode 100644 src/spur64.cog.livetyping/cointerp.c create mode 100644 src/spur64.cog.livetyping/cointerp.h create mode 100644 src/spur64.cog.livetyping/cointerpmt.c create mode 100644 src/spur64.cog.livetyping/gcc3x-cointerp.c create mode 100644 src/spur64.cog.livetyping/gcc3x-cointerpmt.c create mode 100644 src/spur64.cog.livetyping/interp.h create mode 100644 src/spur64.cog.livetyping/variable_order create mode 100644 src/spur64.cog.livetyping/vmCallback.h diff --git a/.github/workflows/linux-arm64.yml b/.github/workflows/linux-arm64.yml index 55f68f7fa4..23a73a4452 100644 --- a/.github/workflows/linux-arm64.yml +++ b/.github/workflows/linux-arm64.yml @@ -51,6 +51,7 @@ jobs: - linux64ARMv8 flavor: - squeak.cog.spur + - squeak.cog.spur.livetyping # LiveTyping VM (src/spur64.cog.livetyping) # - squeak.cogmt.spur - squeak.stack.spur mode: diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 39e83f1ac9..c5ca56363a 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -54,6 +54,7 @@ jobs: - linux32x86 flavor: - squeak.cog.spur + - squeak.cog.spur.livetyping # LiveTyping VM (src/spur64.cog.livetyping) heartbeat: - threaded - itimer @@ -61,6 +62,13 @@ jobs: - fast - debug - assert + exclude: + # LiveTyping is a 64-bit-only, threaded-heartbeat variant: + # no linux32x86 build dir, and only the threaded build* dirs exist for it. + - arch: linux32x86 + flavor: squeak.cog.spur.livetyping + - flavor: squeak.cog.spur.livetyping + heartbeat: itimer include: - arch: linux64x64 flavor: squeak.sista.spur diff --git a/.github/workflows/macos-arm.yml b/.github/workflows/macos-arm.yml index aff663983d..50442463c1 100644 --- a/.github/workflows/macos-arm.yml +++ b/.github/workflows/macos-arm.yml @@ -47,6 +47,7 @@ jobs: - macos64ARMv8 flavor: - squeak.cog.spur + - squeak.cog.spur.livetyping # LiveTyping VM (src/spur64.cog.livetyping) # - squeak.sista.spur # -Werror bc. decl. missing - squeak.stack.spur mode: diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index bbe7fffc15..7d84532db4 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -48,6 +48,7 @@ jobs: # - macos32x86 # Might be deprecated in general flavor: - squeak.cog.spur + - squeak.cog.spur.livetyping # LiveTyping VM (src/spur64.cog.livetyping) # - squeak.sista.spur # -Werror bc. decl. missing - squeak.stack.spur mode: diff --git a/.github/workflows/win-arm.yml b/.github/workflows/win-arm.yml index 65c4754557..0faf3c7d57 100644 --- a/.github/workflows/win-arm.yml +++ b/.github/workflows/win-arm.yml @@ -50,6 +50,7 @@ jobs: - win64ARMv8 flavor: - squeak.cog.spur + - squeak.cog.spur.livetyping # LiveTyping VM (src/spur64.cog.livetyping) - squeak.stack.spur - squeak.sista.spur mode: diff --git a/.github/workflows/win.yml b/.github/workflows/win.yml index fcb3e80072..b9effc228f 100644 --- a/.github/workflows/win.yml +++ b/.github/workflows/win.yml @@ -51,12 +51,17 @@ jobs: - win32x86 flavor: - squeak.cog.spur + - squeak.cog.spur.livetyping # LiveTyping VM (src/spur64.cog.livetyping) - squeak.stack.spur - squeak.sista.spur mode: - fast - debug - assert + exclude: + # LiveTyping is a 64-bit-only variant; there is no win32x86 build dir for it + - arch: win32x86 + flavor: squeak.cog.spur.livetyping runs-on: windows-2022 # use clang 19 for now name: ${{ matrix.flavor }} for ${{ matrix.arch }}${{ matrix.mode == 'debug' && ' (DEBUG)' || matrix.mode == 'assert' && ' (ASSERT)' || '' }} diff --git a/building/linux64ARMv8/squeak.cog.spur.livetyping/build.assert/mvm b/building/linux64ARMv8/squeak.cog.spur.livetyping/build.assert/mvm new file mode 100755 index 0000000000..5bdceb2ee0 --- /dev/null +++ b/building/linux64ARMv8/squeak.cog.spur.livetyping/build.assert/mvm @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +set -e +# assert Cog Spur VM with VM profiler and threaded heartbeat +INSTALLDIR=assert/sqcogspurlt64ARMv8linuxht +# armv8.N-a all fail in signalSemaphoreWithIndex for N in 1,2,3,4,5 +MACHINE="-march=armv8-a -mtune=cortex-a72" +OPT="-g3 -O1 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -DDEBUGVM=0" + +if [ `uname` = "OpenBSD" ]; then + DUALMAP="-DDUAL_MAPPED_CODE_ZONE=0" +# librt and libpthread functions now supplied by libc. +# Many Linux systems supply empty library files but +# OpenBSD does not. + LIBRT="" +# OpenBSD has problems with fast-bitblt under gcc and clang + FASTBITBLT="" +# Prefer clang over gcc if available + if [ -n "$(command -v clang || true)" ]; then CC=clang; else CC=gcc; fi +else # non-OpenBSD Linux + DUALMAP="-DDUAL_MAPPED_CODE_ZONE=1" + LIBRT="-lrt" +# Linux on ARMv8 properly compiles fast-bitblt under gcc +# but compiles-and-segfaults with clang. Use gcc for now. + FASTBITBLT=" --enable-fast-bitblt " + CC=gcc +fi + +if [ $# -ge 1 ]; then + INSTALLDIR="$1"; shift +fi +if ../../../../scripts/checkSCCSversion ; then exit 1; fi +echo -n "clean? " +read a +case $a in +n|no|N|NO) echo "ok but this isn't safe!!";; +*) rm -f config.h; test -f Makefile && make reallyclean +esac +../../../../scripts/copylinuxpluginspecfiles +test -f config.h || ../../../../platforms/unix/config/configure \ + --with-vmversion=5.0 --with-src=src/spur64.cog.livetyping \ + --without-vm-display-fbdev --without-npsqueak \ + --with-scriptname=spur64 \ + CC=$CC \ + CFLAGS="$MACHINE $OPT -DCOGMTVM=0 $DUALMAP" \ + LIBS=$LIBRT + +rm -f vm/sqUnixMain.o # nuke version info +rm -rf ../../../../products/$INSTALLDIR +# prefer make install prefix=`readlink -f \`pwd\`/../../../../products/$INSTALLDIR` +# but older linux readlinks lack the -f flag +make install-squeak install-plugins prefix=`(cd ../../../../;pwd)`/products/$INSTALLDIR 2>&1 | tee LOG ; test ${PIPESTATUS[0]} -eq 0 diff --git a/building/linux64ARMv8/squeak.cog.spur.livetyping/build.debug/mvm b/building/linux64ARMv8/squeak.cog.spur.livetyping/build.debug/mvm new file mode 100755 index 0000000000..cea86a96c5 --- /dev/null +++ b/building/linux64ARMv8/squeak.cog.spur.livetyping/build.debug/mvm @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +set -e +# debug Cog Spur VM with VM profiler and threaded heartbeat +INSTALLDIR=debug/sqcogspurlt64ARMv8linuxht +# armv8.N-a all fail in signalSemaphoreWithIndex for N in 1,2,3,4,5 +MACHINE="-march=armv8-a -mtune=cortex-a72" +OPT="-g3 -O0 -DDEBUGVM=1 -DAIO_DEBUG=1" + +if [ `uname` = "OpenBSD" ]; then + DUALMAP="-DDUAL_MAPPED_CODE_ZONE=0" +# librt and libpthread functions now supplied by libc. +# Many Linux systems supply empty library files but +# OpenBSD does not. + LIBRT="" +# OpenBSD has problems with fast-bitblt under gcc and clang + FASTBITBLT="" +# Prefer clang over gcc if available + if [ -n "$(command -v clang || true)" ]; then CC=clang; else CC=gcc; fi +else # non-OpenBSD Linux + DUALMAP="-DDUAL_MAPPED_CODE_ZONE=1" + LIBRT="-lrt" +# Linux on ARMv8 properly compiles fast-bitblt under gcc +# but compiles-and-segfaults with clang. Use gcc for now. + FASTBITBLT=" --enable-fast-bitblt " + CC=gcc +fi + +if [ $# -ge 1 ]; then + INSTALLDIR="$1"; shift +fi +if ../../../../scripts/checkSCCSversion ; then exit 1; fi +echo -n "clean? " +read a +case $a in +n|no|N|NO) echo "ok but this isn't safe!!";; +*) rm -f config.h; test -f Makefile && make reallyclean +esac +../../../../scripts/copylinuxpluginspecfiles +test -f config.h || ../../../../platforms/unix/config/configure \ + --with-vmversion=5.0 --with-src=src/spur64.cog.livetyping \ + --without-vm-display-fbdev --without-npsqueak \ + --with-scriptname=spur64 \ + CC=$CC \ + CFLAGS="$MACHINE $OPT -DCOGMTVM=0 $DUALMAP" \ + LIBS=$LIBRT + +rm -f vm/sqUnixMain.o # nuke version info +rm -rf ../../../../products/$INSTALLDIR +# prefer make install prefix=`readlink -f \`pwd\`/../../../../products/$INSTALLDIR` +# but older linux readlinks lack the -f flag +make install-squeak install-plugins prefix=`(cd ../../../../;pwd)`/products/$INSTALLDIR 2>&1 | tee LOG ; test ${PIPESTATUS[0]} -eq 0 diff --git a/building/linux64ARMv8/squeak.cog.spur.livetyping/build/mvm b/building/linux64ARMv8/squeak.cog.spur.livetyping/build/mvm new file mode 100755 index 0000000000..b9d09f5e1d --- /dev/null +++ b/building/linux64ARMv8/squeak.cog.spur.livetyping/build/mvm @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +set -e +# Cog Spur VM with VM profiler and threaded heartbeat +INSTALLDIR=sqcogspurlt64ARMv8linuxht +# armv8.N-a all fail in signalSemaphoreWithIndex for N in 1,2,3,4,5 +# tpr - add enablementisation of fast bitblt ben avison code +MACHINE="-march=armv8-a -mtune=cortex-a72" +OPT="-g -O2 -DNDEBUG -DDEBUGVM=0 -D_GNU_SOURCE -DUSEEVDEV" +##OPT="-g -O3 -DMUSL -DNDEBUG -DDEBUGVM=0" + +if [ `uname` = "OpenBSD" ]; then + OPT="$OPT -DMUSL" + CFLAGS="$CFLAGS $OPT -I/usr/local/include" + LIBS="$LIBS -lexecinfo" + LDFLAGS="$LDFLAGS -L/usr/local/lib" + DUALMAP="-DDUAL_MAPPED_CODE_ZONE=0" +# librt and libpthread functions now supplied by libc. +# Many Linux systems supply empty library files but +# OpenBSD does not. + LIBRT="" +# OpenBSD has problems with fast-bitblt under gcc and clang + FASTBITBLT="" +# Prefer clang over gcc if available + if [ -n "$(command -v clang || true)" ]; then CC=clang; else CC=gcc; fi +else # non-OpenBSD Linux + DUALMAP="-DDUAL_MAPPED_CODE_ZONE=1" + LIBRT="-lrt" +# Linux on ARMv8 properly compiles fast-bitblt under gcc +# but compiles-and-segfaults with clang. Use gcc for now. + FASTBITBLT=" --enable-fast-bitblt " + CC=gcc +fi + +if [ $# -ge 1 ]; then + INSTALLDIR="$1"; shift +fi +if ../../../../scripts/checkSCCSversion ; then exit 1; fi +echo -n "clean? " +read a +case $a in +n|no|N|NO) echo "ok but this isn't safe!!";; +*) rm -f config.h; test -f Makefile && make reallyclean +esac +../../../../scripts/copylinuxpluginspecfiles +test -f config.h || ../../../../platforms/unix/config/configure \ + --with-vmversion=5.0 --with-src=src/spur64.cog.livetyping \ + --without-npsqueak \ + --with-scriptname=spur64 \ + $FASTBITBLT \ + CC=$CC \ + CFLAGS="$MACHINE $OPT -DCOGMTVM=0 $DUALMAP" \ + LIBS=$LIBRT +## --without-vm-display-fbdev --without-npsqueak \ +rm -f vm/sqUnixMain.o # nuke version info +rm -rf ../../../../products/$INSTALLDIR +# prefer make install prefix=`readlink -f \`pwd\`/../../../../products/$INSTALLDIR` +# but older linux readlinks lack the -f flag +make install-squeak install-plugins prefix=`(cd ../../../../;pwd)`/products/$INSTALLDIR 2>&1 | tee LOG ; test ${PIPESTATUS[0]} -eq 0 diff --git a/building/linux64ARMv8/squeak.cog.spur.livetyping/makeclean b/building/linux64ARMv8/squeak.cog.spur.livetyping/makeclean new file mode 100755 index 0000000000..a7cac6bd4e --- /dev/null +++ b/building/linux64ARMv8/squeak.cog.spur.livetyping/makeclean @@ -0,0 +1,15 @@ +#!/bin/sh -e +trap 'exit 2' HUP INT PIPE TERM +if [ "$1" = -fork ]; then + shift + for d in `dirname $0`/build*; do + (cd ./$d + echo y | ./mvm "$@") & + done + wait +else + for d in `dirname $0`/build*; do + (cd ./$d + echo y | ./mvm "$@") + done +fi diff --git a/building/linux64ARMv8/squeak.cog.spur.livetyping/makedirty b/building/linux64ARMv8/squeak.cog.spur.livetyping/makedirty new file mode 100755 index 0000000000..11f39e8808 --- /dev/null +++ b/building/linux64ARMv8/squeak.cog.spur.livetyping/makedirty @@ -0,0 +1,15 @@ +#!/bin/sh -e +trap 'exit 2' HUP INT PIPE TERM +if [ "$1" = -fork ]; then + shift + for d in `dirname $0`/build*; do + (cd ./$d + echo n | ./mvm "$@") & + done + wait +else + for d in `dirname $0`/build*; do + (cd ./$d + echo n | ./mvm "$@") + done +fi diff --git a/building/linux64ARMv8/squeak.cog.spur.livetyping/plugins.ext b/building/linux64ARMv8/squeak.cog.spur.livetyping/plugins.ext new file mode 100644 index 0000000000..c06e7f571b --- /dev/null +++ b/building/linux64ARMv8/squeak.cog.spur.livetyping/plugins.ext @@ -0,0 +1,26 @@ +# Copied, perhaps edited, from ../../../src/examplePlugins.ext +EXTERNAL_PLUGINS = \ +B3DAcceleratorPlugin \ +BochsIA32Plugin \ +BochsX64Plugin \ +ClipboardExtendedPlugin \ +FileAttributesPlugin \ +MIDIPlugin \ +Squeak3D \ +SqueakFFIPrims \ +SqueakSSL \ +LocalePlugin \ +UnicodePlugin \ +UnixOSProcessPlugin \ +UUIDPlugin \ +ImmX11Plugin \ +XDisplayControlPlugin \ +DESPlugin \ +MD5Plugin \ +SHA2Plugin \ +VectorEnginePlugin \ +PseudoTTYPlugin \ +## CameraPlugin -- fails on OpenBSD +# GdbARMPlugin # fails to compile +# GdbARMv8Plugin # ditto + diff --git a/building/linux64ARMv8/squeak.cog.spur.livetyping/plugins.int b/building/linux64ARMv8/squeak.cog.spur.livetyping/plugins.int new file mode 100644 index 0000000000..fa7d7987ab --- /dev/null +++ b/building/linux64ARMv8/squeak.cog.spur.livetyping/plugins.int @@ -0,0 +1,40 @@ +# Copied, perhaps edited, from ../../../src/examplePlugins.int +INTERNAL_PLUGINS = \ +ADPCMCodecPlugin \ +AioPlugin \ +AsynchFilePlugin \ +B2DPlugin \ +BitBltPlugin \ +BMPReadWriterPlugin \ +CameraPlugin \ +CroquetPlugin \ +HostWindowPlugin \ +ZipPlugin \ +DropPlugin \ +DSAPrims \ +FFTPlugin \ +FileCopyPlugin \ +FilePlugin \ +FileDialogPlugin \ +Float64ArrayPlugin \ +FloatArrayPlugin \ +FloatMathPlugin \ +IA32ABI \ +JoystickTabletPlugin \ +JPEGReaderPlugin \ +JPEGReadWriter2Plugin \ +Klatt \ +LargeIntegers \ +Matrix2x3Plugin \ +MiscPrimitivePlugin \ +Mpeg3Plugin \ +RePlugin \ +SecurityPlugin \ +SerialPlugin \ +SocketPlugin \ +SoundCodecPrims \ +SoundGenerationPlugin \ +SoundPlugin \ +StarSqueakPlugin \ +SurfacePlugin \ +VMProfileLinuxSupportPlugin diff --git a/building/linux64x64/squeak.cog.spur.livetyping/build.assert.itimerheartbeat/mvm b/building/linux64x64/squeak.cog.spur.livetyping/build.assert.itimerheartbeat/mvm new file mode 100755 index 0000000000..d29cc2be8c --- /dev/null +++ b/building/linux64x64/squeak.cog.spur.livetyping/build.assert.itimerheartbeat/mvm @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -e +# assert VM with VM profiler and itimer heartbeat +INSTALLDIR=assert/sqcogspurlt64linux +OPT="-g3 -O1 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -DDEBUGVM=0" + +# Prefer clang over gcc if available +if [ -n "$(command -v clang || true)" ]; then CC=clang; else CC=gcc; fi + +if [ $# -ge 1 ]; then + INSTALLDIR="$1"; shift +fi + +if ../../../../scripts/checkSCCSversion ; then exit 1; fi +echo -n "clean? " +read a +case $a in +n|no|N|NO) echo "ok but this isn't safe!!";; +*) test -f Makefile && make reallyclean +esac +../../../../scripts/copylinuxpluginspecfiles +test -f config.h || ../../../../platforms/unix/config/configure --without-npsqueak \ + --with-vmversion=5.0 \ + --with-src=src/spur64.cog.livetyping \ + --with-scriptname=spur64 \ + TARGET_ARCH="-m64" \ + CC=$CC \ + CFLAGS="$OPT -msse2 -DCOGMTVM=0 -DITIMER_HEARTBEAT=1" +rm -f vm/sqUnixMain.o # nuke version info +rm -rf ../../../../products/$INSTALLDIR +# prefer make install prefix=`readlink -f \`pwd\`/../../../../products/$INSTALLDIR` +# but older linux readlinks lack the -f flag +make install-squeak install-plugins prefix=`(cd ../../../../;pwd)`/products/$INSTALLDIR 2>&1 | tee LOG ; test ${PIPESTATUS[0]} -eq 0 diff --git a/building/linux64x64/squeak.cog.spur.livetyping/build.assert/mvm b/building/linux64x64/squeak.cog.spur.livetyping/build.assert/mvm new file mode 100755 index 0000000000..befea63267 --- /dev/null +++ b/building/linux64x64/squeak.cog.spur.livetyping/build.assert/mvm @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +set -e +# assert VM with VM profiler and threaded heartbeat +INSTALLDIR=assert/sqcogspurlt64linuxht +OPT="-g3 -O1 -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer -DDEBUGVM=0" + +# Prefer clang over gcc if available +if [ -n "$(command -v clang || true)" ]; then CC=clang; else CC=gcc; fi + +# librt and libpthread funs now supplied by libc +# Many Linux systems supply empty library files +# but OpenBSD does not. +if [ `uname` = "OpenBSD" ]; then LIBRT=""; else LIBRT="-lrt"; fi + +CFLAGS="$OPT -msse2 -DCOGMTVM=0" +LIBS=$LIBRT +LDFLAGS="" + +# Special treatment for OpenBSD Linux +if [ `uname` = "OpenBSD" ]; then + CFLAGS="$CFLAGS -I/usr/local/include" + LIBS="$LIBS -lexecinfo" + LDFLAGS="$LDFLAGS -L/usr/local/lib" +fi + +if [ $# -ge 1 ]; then + INSTALLDIR="$1"; shift +fi + +if ../../../../scripts/checkSCCSversion ; then exit 1; fi +echo -n "clean? " +read a +case $a in +n|no|N|NO) echo "ok but this isn't safe!!";; +*) test -f Makefile && make reallyclean +esac +../../../../scripts/copylinuxpluginspecfiles +test -f config.h || ../../../../platforms/unix/config/configure --without-npsqueak \ + --with-vmversion=5.0 \ + --with-src=src/spur64.cog.livetyping \ + --with-scriptname=spur64 \ + TARGET_ARCH="-m64" \ + CC=$CC \ + CFLAGS="$CFLAGS" \ + LIBS="$LIBS" \ + LDFLAGS="$LDFLAGS" + +rm -f vm/sqUnixMain.o # nuke version info +rm -rf ../../../../products/$INSTALLDIR +# prefer make install prefix=`readlink -f \`pwd\`/../../../../products/$INSTALLDIR` +# but older linux readlinks lack the -f flag +make install-squeak install-plugins prefix=`(cd ../../../../;pwd)`/products/$INSTALLDIR 2>&1 | tee LOG ; test ${PIPESTATUS[0]} -eq 0 diff --git a/building/linux64x64/squeak.cog.spur.livetyping/build.debug.itimerheartbeat/mvm b/building/linux64x64/squeak.cog.spur.livetyping/build.debug.itimerheartbeat/mvm new file mode 100755 index 0000000000..4662bed210 --- /dev/null +++ b/building/linux64x64/squeak.cog.spur.livetyping/build.debug.itimerheartbeat/mvm @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +set -e +# debug Spur VM with VM profiler and itimer heartbeat +INSTALLDIR=debug/sqcogspurlt64linux +OPT="-g3 -O0 -DDEBUGVM=1" + +# Prefer clang over gcc if available +if [ -n "$(command -v clang || true)" ]; then CC=clang; else CC=gcc; fi + +if [ $# -ge 1 ]; then + INSTALLDIR="$1"; shift +fi + +if ../../../../scripts/checkSCCSversion ; then exit 1; fi +echo -n "clean? " +read a +case $a in +n|no|N|NO) echo "ok but this isn't safe!!";; +*) test -f Makefile && make reallyclean +esac +../../../../scripts/copylinuxpluginspecfiles +test -f config.h || ../../../../platforms/unix/config/configure --without-npsqueak \ + --with-vmversion=5.0 \ + --with-src=src/spur64.cog.livetyping \ + --with-scriptname=spur64 \ + TARGET_ARCH="-m64" \ + CC=$CC \ + CFLAGS="$OPT -msse2 -DCOGMTVM=0 -DITIMER_HEARTBEAT=1" +rm -f vm/sqUnixMain.o # nuke version info +rm -rf ../../../../products/$INSTALLDIR +# prefer make install prefix=`readlink -f \`pwd\`/../../../../products/$INSTALLDIR` +# but older linux readlinks lack the -f flag +make install-squeak install-plugins prefix=`(cd ../../../../;pwd)`/products/$INSTALLDIR 2>&1 | tee LOG ; test ${PIPESTATUS[0]} -eq 0 diff --git a/building/linux64x64/squeak.cog.spur.livetyping/build.debug/mvm b/building/linux64x64/squeak.cog.spur.livetyping/build.debug/mvm new file mode 100755 index 0000000000..644a0766ac --- /dev/null +++ b/building/linux64x64/squeak.cog.spur.livetyping/build.debug/mvm @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +set -e +# debug Spur VM with VM profiler and threaded heartbeat +INSTALLDIR=debug/sqcogspurlt64linuxht +OPT="-g3 -O0 -DDEBUGVM=1" + +# Prefer clang over gcc if available +if [ -n "$(command -v clang || true)" ]; then CC=clang; else CC=gcc; fi + +# librt and libpthread funs now supplied by libc +# Many Linux systems supply empty library files +# but OpenBSD does not. +if [ `uname` = "OpenBSD" ]; then LIBRT=""; else LIBRT="-lrt"; fi + +CFLAGS="$OPT -msse2 -DCOGMTVM=0" +LIBS=$LIBRT +LDFLAGS="" + +# Special treatment for OpenBSD Linux +if [ `uname` = "OpenBSD" ]; then + CFLAGS="$CFLAGS -I/usr/local/include" + LIBS="$LIBS -lexecinfo" + LDFLAGS="$LDFLAGS -L/usr/local/lib" +fi + +if [ $# -ge 1 ]; then + INSTALLDIR="$1"; shift +fi + +if ../../../../scripts/checkSCCSversion ; then exit 1; fi +echo -n "clean? " +read a +case $a in +n|no|N|NO) echo "ok but this isn't safe!!";; +*) test -f Makefile && make reallyclean +esac +../../../../scripts/copylinuxpluginspecfiles +test -f config.h || ../../../../platforms/unix/config/configure --without-npsqueak \ + --with-vmversion=5.0 \ + --with-src=src/spur64.cog.livetyping \ + --with-scriptname=spur64 \ + TARGET_ARCH="-m64" \ + CC=$CC \ + CFLAGS="$CFLAGS" \ + LIBS="$LIBS" \ + LDFLAGS="$LDFLAGS" + +rm -f vm/sqUnixMain.o # nuke version info +rm -rf ../../../../products/$INSTALLDIR +# prefer make install prefix=`readlink -f \`pwd\`/../../../../products/$INSTALLDIR` +# but older linux readlinks lack the -f flag +make install-squeak install-plugins prefix=`(cd ../../../../;pwd)`/products/$INSTALLDIR 2>&1 | tee LOG ; test ${PIPESTATUS[0]} -eq 0 diff --git a/building/linux64x64/squeak.cog.spur.livetyping/build.itimerheartbeat/mvm b/building/linux64x64/squeak.cog.spur.livetyping/build.itimerheartbeat/mvm new file mode 100755 index 0000000000..d3a8bc012e --- /dev/null +++ b/building/linux64x64/squeak.cog.spur.livetyping/build.itimerheartbeat/mvm @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -e +# Spur VM with VM profiler and itimer heartbeat +INSTALLDIR=sqcogspurlt64linux +# Some gcc versions create a broken VM using -O2 +OPT="-g -O2 -DNDEBUG -DDEBUGVM=0" + +# Prefer clang over gcc if available +if [ -n "$(command -v clang || true)" ]; then CC=clang; else CC=gcc; fi + +if [ $# -ge 1 ]; then + INSTALLDIR="$1"; shift +fi + +if ../../../../scripts/checkSCCSversion ; then exit 1; fi +echo -n "clean? " +read a +case $a in +n|no|N|NO) echo "ok but this isn't safe!!";; +*) test -f Makefile && make reallyclean +esac +../../../../scripts/copylinuxpluginspecfiles +test -f config.h || ../../../../platforms/unix/config/configure --without-npsqueak \ + --with-vmversion=5.0 \ + --with-src=src/spur64.cog.livetyping \ + --with-scriptname=spur64 \ + TARGET_ARCH="-m64" \ + CC=$CC \ + CFLAGS="$OPT -msse2 -DCOGMTVM=0 -DITIMER_HEARTBEAT=1" +rm -f vm/sqUnixMain.o # nuke version info +rm -rf ../../../../products/$INSTALLDIR +# prefer make install prefix=`readlink -f \`pwd\`/../../../../products/$INSTALLDIR` +# but older linux readlinks lack the -f flag +make install-squeak install-plugins prefix=`(cd ../../../../;pwd)`/products/$INSTALLDIR 2>&1 | tee LOG ; test ${PIPESTATUS[0]} -eq 0 diff --git a/building/linux64x64/squeak.cog.spur.livetyping/build/mvm b/building/linux64x64/squeak.cog.spur.livetyping/build/mvm new file mode 100755 index 0000000000..829825bd71 --- /dev/null +++ b/building/linux64x64/squeak.cog.spur.livetyping/build/mvm @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +set -e +# Spur VM with VM profiler and threaded heartbeat +INSTALLDIR=sqcogspurlt64linuxht +# Some gcc versions create a broken VM using -O2 +OPT="-g -O2 -DNDEBUG -DDEBUGVM=0" + +# Prefer clang over gcc if available +if [ -n "$(command -v clang || true)" ]; then CC=clang; else CC=gcc; fi + +# librt and libpthread funs now supplied by libc +# Many Linux systems supply empty library files +# but OpenBSD does not. +if [ `uname` = "OpenBSD" ]; then LIBRT=""; else LIBRT="-lrt"; fi + +# Special treatment for OpenBSD Linux +if [ `uname` = "OpenBSD" ]; then + OPT="$OPT -DMUSL" + CFLAGS="$CFLAGS $OPT -I/usr/local/include" + LIBS="$LIBS -lexecinfo" + LDFLAGS="$LDFLAGS -L/usr/local/lib" +else + CFLAGS="$CFLAGS $OPT -msse2 -DCOGMTVM=0" + LIBS="$LIBS -lrt" + LDFLAGS="$LDFLAGS" +fi + +if [ $# -ge 1 ]; then + INSTALLDIR="$1"; shift +fi + +if ../../../../scripts/checkSCCSversion ; then exit 1; fi +echo -n "clean? " +read a +case $a in +n|no|N|NO) echo "ok but this isn't safe!!";; +*) test -f Makefile && make reallyclean +esac +../../../../scripts/copylinuxpluginspecfiles +test -f config.h || ../../../../platforms/unix/config/configure --without-npsqueak \ + --with-vmversion=5.0 \ + --with-src=src/spur64.cog.livetyping \ + --with-scriptname=spur64 \ + TARGET_ARCH="-m64" \ + CC=$CC \ + CFLAGS="$CFLAGS" \ + LIBS="$LIBS" \ + LDFLAGS="$LDFLAGS" +rm -f vm/sqUnixMain.o # nuke version info +rm -rf ../../../../products/$INSTALLDIR +# prefer make install prefix=`readlink -f \`pwd\`/../../../../products/$INSTALLDIR` +# but older linux readlinks lack the -f flag +make install-squeak install-plugins prefix=`(cd ../../../../;pwd)`/products/$INSTALLDIR 2>&1 | tee LOG ; test ${PIPESTATUS[0]} -eq 0 diff --git a/building/linux64x64/squeak.cog.spur.livetyping/makeallclean b/building/linux64x64/squeak.cog.spur.livetyping/makeallclean new file mode 100755 index 0000000000..a7cac6bd4e --- /dev/null +++ b/building/linux64x64/squeak.cog.spur.livetyping/makeallclean @@ -0,0 +1,15 @@ +#!/bin/sh -e +trap 'exit 2' HUP INT PIPE TERM +if [ "$1" = -fork ]; then + shift + for d in `dirname $0`/build*; do + (cd ./$d + echo y | ./mvm "$@") & + done + wait +else + for d in `dirname $0`/build*; do + (cd ./$d + echo y | ./mvm "$@") + done +fi diff --git a/building/linux64x64/squeak.cog.spur.livetyping/makealldirty b/building/linux64x64/squeak.cog.spur.livetyping/makealldirty new file mode 100755 index 0000000000..11f39e8808 --- /dev/null +++ b/building/linux64x64/squeak.cog.spur.livetyping/makealldirty @@ -0,0 +1,15 @@ +#!/bin/sh -e +trap 'exit 2' HUP INT PIPE TERM +if [ "$1" = -fork ]; then + shift + for d in `dirname $0`/build*; do + (cd ./$d + echo n | ./mvm "$@") & + done + wait +else + for d in `dirname $0`/build*; do + (cd ./$d + echo n | ./mvm "$@") + done +fi diff --git a/building/linux64x64/squeak.cog.spur.livetyping/makethbdirty b/building/linux64x64/squeak.cog.spur.livetyping/makethbdirty new file mode 100755 index 0000000000..4ebd17bc6a --- /dev/null +++ b/building/linux64x64/squeak.cog.spur.livetyping/makethbdirty @@ -0,0 +1,15 @@ +#!/bin/sh -e +trap 'exit 2' HUP INT PIPE TERM +if [ "$1" = -fork ]; then + shift + for d in build.debug build.assert build; do + (cd ./$d + echo n | ./mvm "$@") & + done + wait +else + for d in build.debug build.assert build; do + (cd ./$d + echo n | ./mvm "$@") + done +fi diff --git a/building/linux64x64/squeak.cog.spur.livetyping/plugins.ext b/building/linux64x64/squeak.cog.spur.livetyping/plugins.ext new file mode 100644 index 0000000000..fc8936d3bd --- /dev/null +++ b/building/linux64x64/squeak.cog.spur.livetyping/plugins.ext @@ -0,0 +1,27 @@ +# Copied, perhaps edited, from ../../../src/examplePlugins.ext +EXTERNAL_PLUGINS = \ +MIDIPlugin \ +B3DAcceleratorPlugin \ +ClipboardExtendedPlugin \ +BochsIA32Plugin \ +BochsX64Plugin \ +GdbARMPlugin \ +GdbARMv8Plugin \ +FileAttributesPlugin \ +Squeak3D \ +SqueakFFIPrims \ +SqueakSSL \ +LocalePlugin \ +UnicodePlugin \ +UnixOSProcessPlugin \ +UUIDPlugin \ +ImmX11Plugin \ +XDisplayControlPlugin \ +DESPlugin \ +MD5Plugin \ +SHA2Plugin \ +VectorEnginePlugin \ +HelloWorldPlugin \ +HelloExternalWorldPlugin \ +PseudoTTYPlugin \ + diff --git a/building/linux64x64/squeak.cog.spur.livetyping/plugins.int b/building/linux64x64/squeak.cog.spur.livetyping/plugins.int new file mode 100644 index 0000000000..fa7d7987ab --- /dev/null +++ b/building/linux64x64/squeak.cog.spur.livetyping/plugins.int @@ -0,0 +1,40 @@ +# Copied, perhaps edited, from ../../../src/examplePlugins.int +INTERNAL_PLUGINS = \ +ADPCMCodecPlugin \ +AioPlugin \ +AsynchFilePlugin \ +B2DPlugin \ +BitBltPlugin \ +BMPReadWriterPlugin \ +CameraPlugin \ +CroquetPlugin \ +HostWindowPlugin \ +ZipPlugin \ +DropPlugin \ +DSAPrims \ +FFTPlugin \ +FileCopyPlugin \ +FilePlugin \ +FileDialogPlugin \ +Float64ArrayPlugin \ +FloatArrayPlugin \ +FloatMathPlugin \ +IA32ABI \ +JoystickTabletPlugin \ +JPEGReaderPlugin \ +JPEGReadWriter2Plugin \ +Klatt \ +LargeIntegers \ +Matrix2x3Plugin \ +MiscPrimitivePlugin \ +Mpeg3Plugin \ +RePlugin \ +SecurityPlugin \ +SerialPlugin \ +SocketPlugin \ +SoundCodecPrims \ +SoundGenerationPlugin \ +SoundPlugin \ +StarSqueakPlugin \ +SurfacePlugin \ +VMProfileLinuxSupportPlugin diff --git a/building/macos64ARMv8/common/Makefile.app.squeaklivetyping b/building/macos64ARMv8/common/Makefile.app.squeaklivetyping new file mode 100644 index 0000000000..e5364e63fe --- /dev/null +++ b/building/macos64ARMv8/common/Makefile.app.squeaklivetyping @@ -0,0 +1,25 @@ +############################################################################## +# Generic Makefile for Mac OS X SqueakLiveTyping Cog Cocoa VM +# +# Same as Makefile.app.squeak but brands the produced application bundle as +# SqueakLiveTyping so the LiveTyping VM ships as a distinct artifact alongside +# the stock Squeak VM. The executable, icons and sources file remain Squeak's. + +# The caller should set VMSRCDIR to point to the relevant VM source +# e.g. VMSRCDIR:= ../../../src/spur64.cog.livetyping + +# Produce SqueakLiveTyping.app, SqueakLiveTypingAssert.app & SqueakLiveTypingDebug.app +APPNAME:=SqueakLiveTyping +APPNAMEDEF:=$(APPNAME) +APPIDENTIFIER:=org.squeak.$(APPNAME) + +APPPOST?=overwriteSqueakIcon + +# Now include the Makefile proper, which is common to all Mac OS builds. +# +include ../common/Makefile.app + +# Replace the standard yellow VM icon with the green one used for Cog VMs + +overwriteSqueakIcon: $(APP)/Contents/Resources/Squeak.icns + cp -p $(OSXDIR)/GreenCogSqueak.icns $(APP)/Contents/Resources/Squeak.icns diff --git a/building/macos64ARMv8/squeak.cog.spur.livetyping/Makefile b/building/macos64ARMv8/squeak.cog.spur.livetyping/Makefile new file mode 100644 index 0000000000..e60fa011fa --- /dev/null +++ b/building/macos64ARMv8/squeak.cog.spur.livetyping/Makefile @@ -0,0 +1,18 @@ +############################################################################## +# Makefile for Mac OS X Cog Spur SqueakLiveTyping Cocoa VM using clang and gnu make 3.81 +# Do make init to allow make -n to function. +# + +BIT_IDENTICAL_FLOATING_POINT=BIT_IDENTICAL_FLOATING_POINT + +VMSRCDIR:= ../../../src/spur64.cog.livetyping +SOURCEFILE:=SqueakV60.sources + +ifeq ($(THREADING),multi) +COGDEFS:=-DPRINT_DL_ERRORS=1 +endif + +# Now include the Makefile proper, which is common to all Mac OS builds. +# This variant brands the app bundle as SqueakLiveTyping. +# +include ../common/Makefile.app.squeaklivetyping diff --git a/building/macos64ARMv8/squeak.cog.spur.livetyping/mvm b/building/macos64ARMv8/squeak.cog.spur.livetyping/mvm new file mode 100755 index 0000000000..a58a6df2b8 --- /dev/null +++ b/building/macos64ARMv8/squeak.cog.spur.livetyping/mvm @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -e +# A=all(a,d,f) T=Threaded a=assert d=debug f=fast +A=;D=;F=;T= +if [ $# = 0 ]; then + A=1;D=1;F=1 +else + while getopts 'ASTadf?' opt "$@"; do + case $opt in + A) A=1;D=1;F=1;; + S) echo -S not yet implemented\; use -A for now 1>&1; exit 1;; + T) T=THREADING=multi;; + a) A=1;; + d) D=1;; + f) F=1;; + *) echo usage $0 [-A] [-a\ [-d] [-f]; exit 0;; + esac + done + if [ "$1" = -- ]; then + if [ "$A$D$F" = "" ]; then + A=1;D=1;F=1 + fi + fi + shift `expr $OPTIND - 1` +fi +if ../../../scripts/checkSCCSversion ; then exit 1; fi +if [ -z "$T" ]; then + if [ -n "$D" ]; then + make $@ debug 2>&1 | tee LOGD ; test ${PIPESTATUS[0]} -eq 0 + fi + if [ -n "$A" ]; then + make $@ assert 2>&1 | tee LOGA ; test ${PIPESTATUS[0]} -eq 0 + fi + if [ -n "$F" ]; then + make $@ 2>&1 | tee LOGF ; test ${PIPESTATUS[0]} -eq 0 + fi +else + if [ -n "$D" ]; then + make $@ $T debug 2>&1 | tee LOGTD ; test ${PIPESTATUS[0]} -eq 0 + fi + if [ -n "$A" ]; then + make $@ $T assert 2>&1 | tee LOGTA ; test ${PIPESTATUS[0]} -eq 0 + fi + if [ -n "$F" ]; then + make $@ $T 2>&1 | tee LOGTF ; test ${PIPESTATUS[0]} -eq 0 + fi +fi diff --git a/building/macos64ARMv8/squeak.cog.spur.livetyping/plugins.ext b/building/macos64ARMv8/squeak.cog.spur.livetyping/plugins.ext new file mode 100644 index 0000000000..ed3cc77c91 --- /dev/null +++ b/building/macos64ARMv8/squeak.cog.spur.livetyping/plugins.ext @@ -0,0 +1,25 @@ +# Copied, perhaps edited, from ../../src/examplePlugins.ext +EXTERNAL_PLUGINS = \ +B3DAcceleratorPlugin \ +BochsIA32Plugin \ +BochsX64Plugin \ +CameraPlugin \ +ClipboardExtendedPlugin \ +CroquetPlugin \ +GdbARMPlugin \ +GdbARMv8Plugin \ +Mpeg3Plugin \ +SqueakFFIPrims \ +Squeak3D \ +SqueakSSL \ +UnixOSProcessPlugin \ +DESPlugin \ +MD5Plugin \ +SHA2Plugin \ +VectorEnginePlugin \ +# Not working because of deprecated functionality \ +# Not working because no 64-bit QuickTime \ +# MIDIPlugin \ +# not working (because of Cocoa vs Carbon): \ +# QuicktimePlugin \ +# JoystickTabletPlugin diff --git a/building/macos64ARMv8/squeak.cog.spur.livetyping/plugins.int b/building/macos64ARMv8/squeak.cog.spur.livetyping/plugins.int new file mode 100644 index 0000000000..9f846ed305 --- /dev/null +++ b/building/macos64ARMv8/squeak.cog.spur.livetyping/plugins.int @@ -0,0 +1,41 @@ +# Copied, perhaps edited, from ../../src/examplePlugins.int +INTERNAL_PLUGINS = \ +ADPCMCodecPlugin \ +AioPlugin \ +AsynchFilePlugin \ +B2DPlugin \ +BitBltPlugin \ +BMPReadWriterPlugin \ +ZipPlugin \ +DropPlugin \ +DSAPrims \ +FFTPlugin \ +FilePlugin \ +FileAttributesPlugin \ +FileDialogPlugin \ +Float64ArrayPlugin \ +FloatArrayPlugin \ +FloatMathPlugin \ +HostWindowPlugin \ +IA32ABI \ +JPEGReaderPlugin \ +JPEGReadWriter2Plugin \ +Klatt \ +LargeIntegers \ +LocalePlugin \ +Matrix2x3Plugin \ +MiscPrimitivePlugin \ +RePlugin \ +SecurityPlugin \ +SocketPlugin \ +SoundCodecPrims \ +SoundGenerationPlugin \ +SoundPlugin \ +StarSqueakPlugin \ +SurfacePlugin \ +UUIDPlugin \ +VMProfileMacSupportPlugin \ +# InternetConfigPlugin \ +# JoystickTabletPlugin \ +# MacMenubarPlugin # Does not work under Cocoa \ +# SerialPlugin # Does not compile under 64-bits diff --git a/building/macos64x64/common/Makefile.app.squeaklivetyping b/building/macos64x64/common/Makefile.app.squeaklivetyping new file mode 100644 index 0000000000..e5364e63fe --- /dev/null +++ b/building/macos64x64/common/Makefile.app.squeaklivetyping @@ -0,0 +1,25 @@ +############################################################################## +# Generic Makefile for Mac OS X SqueakLiveTyping Cog Cocoa VM +# +# Same as Makefile.app.squeak but brands the produced application bundle as +# SqueakLiveTyping so the LiveTyping VM ships as a distinct artifact alongside +# the stock Squeak VM. The executable, icons and sources file remain Squeak's. + +# The caller should set VMSRCDIR to point to the relevant VM source +# e.g. VMSRCDIR:= ../../../src/spur64.cog.livetyping + +# Produce SqueakLiveTyping.app, SqueakLiveTypingAssert.app & SqueakLiveTypingDebug.app +APPNAME:=SqueakLiveTyping +APPNAMEDEF:=$(APPNAME) +APPIDENTIFIER:=org.squeak.$(APPNAME) + +APPPOST?=overwriteSqueakIcon + +# Now include the Makefile proper, which is common to all Mac OS builds. +# +include ../common/Makefile.app + +# Replace the standard yellow VM icon with the green one used for Cog VMs + +overwriteSqueakIcon: $(APP)/Contents/Resources/Squeak.icns + cp -p $(OSXDIR)/GreenCogSqueak.icns $(APP)/Contents/Resources/Squeak.icns diff --git a/building/macos64x64/squeak.cog.spur.livetyping/Makefile b/building/macos64x64/squeak.cog.spur.livetyping/Makefile new file mode 100644 index 0000000000..d24ae82386 --- /dev/null +++ b/building/macos64x64/squeak.cog.spur.livetyping/Makefile @@ -0,0 +1,14 @@ +############################################################################## +# Makefile for Mac OS X Cog Spur SqueakLiveTyping Cocoa VM using clang and gnu make 3.81 +# Do make init to allow make -n to function. +# + +BIT_IDENTICAL_FLOATING_POINT=BIT_IDENTICAL_FLOATING_POINT + +VMSRCDIR:= ../../../src/spur64.cog.livetyping +SOURCEFILE:=SqueakV60.sources + +# Now include the Makefile proper, which is common to all Mac OS builds. +# This variant brands the app bundle as SqueakLiveTyping. +# +include ../common/Makefile.app.squeaklivetyping diff --git a/building/macos64x64/squeak.cog.spur.livetyping/mvm b/building/macos64x64/squeak.cog.spur.livetyping/mvm new file mode 100755 index 0000000000..a58a6df2b8 --- /dev/null +++ b/building/macos64x64/squeak.cog.spur.livetyping/mvm @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -e +# A=all(a,d,f) T=Threaded a=assert d=debug f=fast +A=;D=;F=;T= +if [ $# = 0 ]; then + A=1;D=1;F=1 +else + while getopts 'ASTadf?' opt "$@"; do + case $opt in + A) A=1;D=1;F=1;; + S) echo -S not yet implemented\; use -A for now 1>&1; exit 1;; + T) T=THREADING=multi;; + a) A=1;; + d) D=1;; + f) F=1;; + *) echo usage $0 [-A] [-a\ [-d] [-f]; exit 0;; + esac + done + if [ "$1" = -- ]; then + if [ "$A$D$F" = "" ]; then + A=1;D=1;F=1 + fi + fi + shift `expr $OPTIND - 1` +fi +if ../../../scripts/checkSCCSversion ; then exit 1; fi +if [ -z "$T" ]; then + if [ -n "$D" ]; then + make $@ debug 2>&1 | tee LOGD ; test ${PIPESTATUS[0]} -eq 0 + fi + if [ -n "$A" ]; then + make $@ assert 2>&1 | tee LOGA ; test ${PIPESTATUS[0]} -eq 0 + fi + if [ -n "$F" ]; then + make $@ 2>&1 | tee LOGF ; test ${PIPESTATUS[0]} -eq 0 + fi +else + if [ -n "$D" ]; then + make $@ $T debug 2>&1 | tee LOGTD ; test ${PIPESTATUS[0]} -eq 0 + fi + if [ -n "$A" ]; then + make $@ $T assert 2>&1 | tee LOGTA ; test ${PIPESTATUS[0]} -eq 0 + fi + if [ -n "$F" ]; then + make $@ $T 2>&1 | tee LOGTF ; test ${PIPESTATUS[0]} -eq 0 + fi +fi diff --git a/building/macos64x64/squeak.cog.spur.livetyping/plugins.ext b/building/macos64x64/squeak.cog.spur.livetyping/plugins.ext new file mode 100644 index 0000000000..22e6ea21ad --- /dev/null +++ b/building/macos64x64/squeak.cog.spur.livetyping/plugins.ext @@ -0,0 +1,24 @@ +# Copied, perhaps edited, from ../../src/examplePlugins.ext +EXTERNAL_PLUGINS = \ +B3DAcceleratorPlugin \ +BochsIA32Plugin \ +BochsX64Plugin \ +CameraPlugin \ +ClipboardExtendedPlugin \ +CroquetPlugin \ +GdbARMPlugin \ +GdbARMv8Plugin \ +Mpeg3Plugin \ +SqueakFFIPrims \ +Squeak3D \ +SqueakSSL \ +UnixOSProcessPlugin \ +DESPlugin \ +MD5Plugin \ +SHA2Plugin \ +VectorEnginePlugin \ +# Not working because no 64-bit QuickTime \ +# MIDIPlugin \ +# not working (because of Cocoa vs Carbon): \ +# QuicktimePlugin \ +# JoystickTabletPlugin diff --git a/building/macos64x64/squeak.cog.spur.livetyping/plugins.int b/building/macos64x64/squeak.cog.spur.livetyping/plugins.int new file mode 100644 index 0000000000..9f846ed305 --- /dev/null +++ b/building/macos64x64/squeak.cog.spur.livetyping/plugins.int @@ -0,0 +1,41 @@ +# Copied, perhaps edited, from ../../src/examplePlugins.int +INTERNAL_PLUGINS = \ +ADPCMCodecPlugin \ +AioPlugin \ +AsynchFilePlugin \ +B2DPlugin \ +BitBltPlugin \ +BMPReadWriterPlugin \ +ZipPlugin \ +DropPlugin \ +DSAPrims \ +FFTPlugin \ +FilePlugin \ +FileAttributesPlugin \ +FileDialogPlugin \ +Float64ArrayPlugin \ +FloatArrayPlugin \ +FloatMathPlugin \ +HostWindowPlugin \ +IA32ABI \ +JPEGReaderPlugin \ +JPEGReadWriter2Plugin \ +Klatt \ +LargeIntegers \ +LocalePlugin \ +Matrix2x3Plugin \ +MiscPrimitivePlugin \ +RePlugin \ +SecurityPlugin \ +SocketPlugin \ +SoundCodecPrims \ +SoundGenerationPlugin \ +SoundPlugin \ +StarSqueakPlugin \ +SurfacePlugin \ +UUIDPlugin \ +VMProfileMacSupportPlugin \ +# InternetConfigPlugin \ +# JoystickTabletPlugin \ +# MacMenubarPlugin # Does not work under Cocoa \ +# SerialPlugin # Does not compile under 64-bits diff --git a/building/win64ARMv8/squeak.cog.spur.livetyping/Croquet.def.in b/building/win64ARMv8/squeak.cog.spur.livetyping/Croquet.def.in new file mode 100644 index 0000000000..070ebef801 --- /dev/null +++ b/building/win64ARMv8/squeak.cog.spur.livetyping/Croquet.def.in @@ -0,0 +1,3 @@ +; Set the total stack size to 4 megabytes (0x400000), reserving 4Mb and +; committing 64k (0x10000) +STACKSIZE 0x400000,0x10000 diff --git a/building/win64ARMv8/squeak.cog.spur.livetyping/Croquet.exe.manifest b/building/win64ARMv8/squeak.cog.spur.livetyping/Croquet.exe.manifest new file mode 100644 index 0000000000..d5d38efd1c --- /dev/null +++ b/building/win64ARMv8/squeak.cog.spur.livetyping/Croquet.exe.manifest @@ -0,0 +1,43 @@ + + + +Croquet Smalltalk Virtual Machine + + + true/pm + PerMonitor + + + + + + + + + + + + + + + + + + + + diff --git a/building/win64ARMv8/squeak.cog.spur.livetyping/Croquet.ico b/building/win64ARMv8/squeak.cog.spur.livetyping/Croquet.ico new file mode 100644 index 0000000000000000000000000000000000000000..2063d2205a2b8a2bcc32d1a36ba31872121ac77d GIT binary patch literal 1406 zcmeH{F-Tic6o$X^UgEpYyl3LeyRk{bGbU-t=qv~wopp0@lkP5VF0L-(=o%=57Eu(W z)bdrnV3Va#JX zoiEJKKjZxTJJZt#EHA%ccJ?Xj>#y0_`N-B*_m<8f7&PJjnv0i^CbmgH`AJP9V{4%l*ml887-BvMRVk$Rx89OvC! z*+V2EV`}}c$ttfJrE|mzQrUFgh9VY{ky^=9N}LkOIOEiGSzRQ{Xe#z7=qO^Okaz_5 p#*`V1PFTDoB%>6?*f6w_6cIBJOq_9*GeuR^{)vi>B9;0t;x||CQV{?E literal 0 HcmV?d00001 diff --git a/building/win64ARMv8/squeak.cog.spur.livetyping/Croquet.rc b/building/win64ARMv8/squeak.cog.spur.livetyping/Croquet.rc new file mode 100644 index 0000000000..fc7e089542 --- /dev/null +++ b/building/win64ARMv8/squeak.cog.spur.livetyping/Croquet.rc @@ -0,0 +1,32 @@ +#ifdef _WIN32 +1 ICON DISCARDABLE "Croquet.ico" +2 ICON DISCARDABLE "Croquet.ico" +3 ICON DISCARDABLE "Croquet.ico" + +1 VERSIONINFO + FILEVERSION FILEVERSIONVALUES + PRODUCTVERSION 5,0,0,0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0xaL + FILEOS 0x10001L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" // Lang=US English, CharSet=Windows Multilin + BEGIN + VALUE "CompanyName", "Squeak.org\0" + VALUE "FileDescription", "Croquet Cog Spur Virtual Machine\0" + VALUE "FileVersion", FILEVERSIONSTRING + VALUE "LegalCopyright", "Copyright \251 Squeak.org 1996-2014\0" + VALUE "ProductName", "Croquet Cog Spur\0" + VALUE "ProductVersion", "5.0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END +#endif diff --git a/building/win64ARMv8/squeak.cog.spur.livetyping/GreenCogSqueak.ico b/building/win64ARMv8/squeak.cog.spur.livetyping/GreenCogSqueak.ico new file mode 100644 index 0000000000000000000000000000000000000000..f6d03cf9fa70a6531d7be789b8fbb849128e1655 GIT binary patch literal 69927 zcmd?PcU)FGx(2#FdI#yfs0fG@0i|~om8PJeAWfu7mm(cOK|~O#B1HuSMQMT{y(oep zO$4a|(tAf~<|gdz%$}J&bLPys=iEPTe!u5i>q~o|yh+|9Zx)8(FanI58-q`7jGqR> zj$;@mD*F5X{TSv5<4{29w%r&OFM(k+Fm5+L0fqqz9L5jRxgnGwQ2cKAz1(kq{^MU9 zED5E+YX2JkEbgzV|MNFgKM4dQgdl`41Qo=ufZkCYN<(>2-aqn#fB)lv2TQU-C_xxA zurUS+$OzuU!^b-a|L+F$j^a=nj4*~!fte+iFjtBtRc|G8; z82}tM0)hP8SwJ}?0I+U$fa~M{cn_EcU>Ru$HV8>n7b7Q=8+_-4Kr;SW*h54{Y~cFb zYa*m3a^axdp#(BYayL~!jT*V0GaKZ?d;lBe1h^?~Ks)BRT%UPOpt@n)o_`{o5QpI;e+U#m$q7hD4gi^{Gr(!cf7A4V z^=Hqn8$0KlZNSBMc0kTv^&OG{<^Nef;87i=3y^rG2AFdX0gBrFV1K6^5bak7%;VyK zV0!OgWv}o6Z0!$Nfn1g#=C}cFnhQ|Q2?C*J6>xmm2psyN0w~J%0p_ejK;o$ypgKYq z0OkMLyujf!$>XpG^Z?6Y1XwbBwzC23IRn7#C;`?5#}#VxDwO-{xd}OK!2eUQ?IWB3 zgR-%H4uFNz0?dXI;QC-(Jgny-#0ywwJRQKDgfjlw7=X&blt^5$EXF15CamKuIe_nA zm(yj)!JQg{24FrAqfk~gE5JG+uh+}~Gll6Wj|GI^p5Mw@!*_%?F#R#ZJf=W$1xom{ z@rMsNp}B^@jps*VBrIgkhYA%oNCQ{^W(DiAg>_j& z7{R)cKD5XHW&+>ez;wg~@ycNWm=krU4EEh1EdLqW#CkSBSj+(^GPnVC3^yS2WdS%=g0sKmW6bzEm>cZl+i=`IhGVW6 z*4qf>qw;9%p>d9E4w~oHEPz~}0g#qKU3`cC(R@sS9PdGn$OfS~h@NQ7z`WQe$OV;0 zb2<^`35H|Q38pbYnZE)PzB1-OwSxuk*};;a{8w;1y@zGsT7#LucJxCYXpYZAdEems z62uQA`*&0s zi$Xn!VydL>ST4-_7RoP! z8L+(pP@mm!ETFo=XxlM%0x{TbQH%}($^5fGi4(^j&@E#%Q1&xeha(lho8|%3%R+!A zk_XTv?gKQNB7kNUV(ky;6$CUE>|oC*T#sgWp-qBqxB|Ji!u~IZ@=udKhT{IXXZw5U zXivExhr<~%2Jin$TmjpR><^UBu^v6LTCrhs(WdFR?a>Blj0?u z*O^fM88V<)ssqm4JP)*c&4JGH<=?Ps33Pq|#Vz~>?S%8-bk;>c8wUHq0m?6C0j$r3 z!1*^Yzwf!vw)LbejN>mlTnifj#xhif)b91EcGWShVvz!U!)QGZwG-=zH)fAF}7%t@{> z0X#n_CkV<7gYqq@-eL!dPGY=-DwsKC`EDFc!}p^Hls@c$fSbhpzo_=#Pv#}G$1ajD z!|?zxdk7cUZa%`l*3U)L2)SU&u!G76!<@iLY&av73m)JJ=#B_^Y2UQ`B^{r@5$9TGzz z9_Z{T0-*|V8bTR@9paDN&^wAlX($iMi^`y~s18)u|5d*rU7|e-F9hO))=hH=Ct7CO z5O#i!>+tioapkKyof^gOBu zZW={h>bTt$vmQV42;3fy2i|?5z`EB1u&Qu}K`sX%&^k#6v7c3$>y}loJMiue-OU^S z4VGz)Sx0rCx=@`k-~UGV2c6&ua8!cv(tW2tngiojdvLDR5-7Kv1+r~A;Mjm6IDA{a z0TR)LI0bP6LPs=AuKrm61t8n11C*Q3f^#jFz_{5SoNh7)f^pLQs7{#g@54XH{`(C7 z;y)^jYy{FDHN@ZSO>st)QKVHoU{5VS*z-XEkbZ>w?@tGT=vQ^1{n2!pgo^Y!K0dw& zEO(WVoXAhJ$#_|`?>L}og?T^h1vGX1fTofkke2fRoFV01nD37P$wD%bY{cO|X$P_i zdm+%+l!ZXH0?k8IH#uCVjj#%)9qc_kn|y@lfKP0I_{%=PJE#E8PS}9UB~BwjAMQ-L zzxE%z_|m#xf5H|V_^JqqyP&VB4W0oT;W^|TEL+aJ1MAX;bt3!oOJ_(Hl8Iy^4v5Qt zCm?&r0)6Rj_NUV+enI0S*}Lq0qs;&k>Af-i7C{{^0<`ZxgoAUB{N69p2}TEKYK z6}YX30M8${fZ3WGI6A2f=!ZoCejhx8A)gWQ8K5)JL&jlfX^|@*aYpk+00POn^1JL~ z@EvjaqdkUy|7k!vpn;I2V`uOVuZ~`_x^tzKVh^Pcj5C5jzP`)M66|qe6g~(0>&H zY!L_BV0|_a$Y!CrVt`~>-M-QqUU_c`ae#4>5Qx*iGmgk$zC-u~_{IizOnM@|B!Kv# zN5H=#WQO+$Um_7Pk&l+IUbV|;;P#0LW57Oq6cBuaXEJ#9-93w=^EUE3!ZSDS7m$wt z`T%#&%gFx%eWgT0`@r5oS)e&$_ERg-;1eZ1RUnjW&wG^bzE4^3O#I+O5Yd?c5C_Bs zae_eH{#8I@n*-vc#6{_1n>sge;jJ|=`e+9{25*4i?#K1 zZuP}%dGrSZqXy`!t+oa>Z{2`|k#sTQ^gFk|u_G|1cD_3?{9U`z$jOdJ9PQ&9J_kR$SMAkN6|vi^7Yo-sbaGbRhvrVN4gqSx+O^53(TtEH?z= z_|F0!9u59sj(~$>d($KUeI_3u%7gQ=k{ysY@&bm>B7nK`Fxb;A2#BZQScZE1%a}oW zNBmG5kRKTV@kaRNM@H!=A8If1`%J+3GRF((J_rJydk3Uxqb614d|OiKH4VO2Bn z$x5LeNA?ieP?U~*nrMt5e$an~ZT-#_`YSMhc*aEBQsMh8$mtZUSCHs8e-J8zd{de* z?huSazOq~>8~MAg?-_#UK4eq>{XmPy56_O3SUJnjUHv2fG9ToO{2$17hJ1i-@Qket z{oAr60F!}`gFwD#G?!62@^e3i^3j+yRQXl?jc0oheFuFx8R<~Q;qF=2ZN9I*_BZ5;9!BLDPDsFTxBwh+|MN!T9b z8%I8PSHPGUtz4b3%h#PuGT>rQhAiik-ht^iKZ{&xNp>^UaruKmMW_(Zv(le5g z1>2Af5d+5ox?e!t&>aVAmlVv0bcpiM;{BDs0Zc>vCjiqHj~0z3!#S6Z`z zpH%>5IkYv%--UQ0T}eQm((ow{q44_?y=z1Hs7=V1jK%=sg!sYrYIpvlyBFlg*wsC3 zhbn{`1Zq3F4~c+z;aDPS;{j})&}P@4+$QIs41#>ny1&Z{J`qR66>*NIoFOpD3hp_{F)8mOBD1ih!(x7hqo@2Q+ub;S-G|bpL?v0+3xoa~F;IEi_*t z(A~$c=k7b4!^n0)pa1TBMm7rBSu`&Y(E5hfK4g#KJsII=9>CQq33R@g0~veeDqeBE z1Of4bN&Ju-;)u9H;(y$4lS-IKX9T_r12Jt$;BIFkxY`~7)IR6~)(?jPK?|%G*%)*O zhjjZ#xx06EP(HHf#&BF5#Nhaa;{x$VK6CU$HVVxL0+<#C`vl#0Aik&!+E1YM0Nw4N z`v52huZ;uHH5~#nOlbi{40>Mo!qrE9f<6gjCCk|xZ zX#u_3OTe_!9vBr`gA-5B0ygcvPob>8;ZLMO^_2P+-2cY%0{X`X0M(0qfcQ1MGpdI5 zAsdIr3(`O0%?sxtibLEH2gKhRj$g>X*4%F;{@?PK?2C|NWt{jG_VS` zgKdL+(S8H<0n$5K1JGR?(#`L-8CTB^h$=V%MZrEm`|JQ{4eWY z0>_;-VP04cjURLmjBGz44oHeZKl@Gie-ZLTZAW7Vl}C4&zHrQq!2f8DG(&mlo(`@1 z=$#$Pc;xSAh{<3`=47vD=4LP%g6bXitUYAe({4 zF51U0LjS=y+&3O12Dlf@00;M&xV7Jb?jX_oDg?The9H!Kd*S-p1J9aEunoU#5Yhpf zQ}>~sk&Q#+2k|e2{Ere}`md<|NjeUWE=IV%f7#`a#x9yusD8B0qHi+bS(kK@4^XTL z0tz2CKw-cLD1N~Am4m;MNG?p&>K8Mj- zh}Ldoj~+3s!F`At+Y$TM6XtOtFkfpmiG{t38880FZ# zPvw}G2JG*}0lO48VE=psu&=}aD-f%H0Q$cy1=trMmLR?!1?+M#|0_|zF(U=&hJ=5! zo9G)3bdQ4c1lK!cyNRzdbU+@+4<_+DC&cYv1vICbAduh1h~@~xL-st8pTvbw-{`Ix z?b+2x0O=qv5S=&yM883tKMF)2DFV?S8bEaU6cAm3&$ZvN{`+$k#xFrEK%n;=Wgw~} z0Ytmi!Qru!fMSRr?wjEr2(2OYth?V7a2AV!qlv0h^t_D7w}sB{h!f)Ww{{0c{2`#T z;CQe$IKBkm7a`Dd z1!5Iq?GHfzmmyG|`HR2hi#!dEyJ&)w1JH*tA_?s$oLASNeSzbiFqa*0Ws3o=5+iV> z(ESJe#xEanLfjw_$A1>^2`P!h#4pQKo8{O6trv!XFI^Ilr$PNATZ-&~5I$fv-v`XE zTLP2Ymx0NIGccKR`3;M%z+@R>Uyg(vd6mmt%$L7=PaiTI4fx;6Lf()o(h1Ug6x}wwpZ{$SgZ3!sTQM}JwILV8>Aw>g z@D<@XZ+*8sI_IMOl_5m|JbNeX&f|0_GYgKTB6gtKd=7+uivihluR+;TEvQ_s+eO97 zyT81bExiS~^Ti;1Gy$A!GX$ii(B>oV&tW~NE^Dd+jGI6f?&aR>p4$+2bjCM?91xfP zB=8e@?tWW$iSh^Bb6kg~=HKk^5n@xgpPJm2i{?ZQ>>IdOWUUbe@*htF(=I3A*?$8B ze7ys%_1^@Sx?F%_vo7FxD+X{S(2l)=`Lm(EQQ0{96?m?{4M)arK5{&0Uyv*_C=;Dm z`5=e?G@!b~h+{EU0_1!A+dzwV3hL?G?)M3(@1G(*aC~LKetH4>J`3W-@9)py`!fWn zZ?qpn`c8rQkxekCXo3|BK>Tq(3_a%fC*hth7LNIUWiS7h)4%_34~_&+htP|DkuVy5 z{}2axpgkhugyzR1$PdjCbS6MR?`S{%i!-t>(56vDa{}rB&SgAu0<^~d@xJfhXY$|A ziS{8>L6BQzd(iLvsxItlZ!et)0kDq4R?! z8^z`pyM?KlkhVSF~?ahjpPk|1Sgju7nj|1KNrtxK<49 zwgsInk^hnw@8~~JL2;&V9YTE3^E51T6qZGG{D?z8@XAHq&+t5cG(qOnJL@`M_+V`vKDFcM^SFSC zYq;48()(5?&rSG=4}N@EQC9jw(x$Y>{wNc(@|kw?i>KcytI-e~yJ5R$NpHBFg}%-u zYIx5XWoLT&)AUj22yV10@8N2Xud!6PNJLX=`zR^oYuS2Me~&O2rgKU7Ua8mA)HJ|ozgDeJ-7oE9w%Ov@ zjWUXa@xHc)Tz)EF##Qz0Y|21INMMfa>bj%hNCLlLsr5lg-tM(-N86W`8OMl=@oLY1 z@4rW8BpWE(W;3K&R^?fjm|?{y(Nd#i?`HVCgMmI|I&J=W$tS$xC#0B9r6+F`hwsW9 zS7GnZQt^ah53G1(vhQ(L2et9Ne7!Lb#@?DQo&<}tjz_9`l&c+tnj5ko^zJ-y=%c6bBje}ZRjcC8PKV(&A2UJh<)C`fL38!qof~m+{^tli_KWwvnyPi(yH5Hh zexT`1-=TWfB#qaOl-KOO$CPat7D&G)JoeUxQbrd4-O(4#Ij-cn1NcTbv$>x=^f??h zU%szuE4l{>`pE+>o8}rxk3O~cuX_t^TSnJkL^^NY@eDBa)EuJ=WvB2ZeB4$PR1@Yo z@xczNJ7%uLeRWpCkY{UZuCrbEh63GJw^!1l`(@HB%u-jWw@r%ma?q9_bf)EohxHgW7A6Q5qhJZLxyC8%L11s zZ>6?HQxa65+2}M!@qWu){FD)Hh~B4No2zf@TlZRNImZCiOz9~v%@#R}nbLBiF5#g= z`J~trc1-H2ws2v{J<^x1zK#+2)hE8C%j?XnhxU3OdHL4V5nk$NUlh8nV_y0xn|WGFzu-CU#Fa&*Aki{J(EZzyg_1==;+mcFn6&ys7A)GlE?>k zb-Q$;rFHk3lyn8h8R5g@KiaO=23l%`-Vmfzj=#C!D%khwoWf$?fHduuU`*KcQ(2(Z z8kgr!Yq#RUX2Sw|>*Ea@JpG?&LgS}OKF&X0go>UHr@N6&e7$Q@R91d(S!J1&LQCe^ zl>4q8lNXB)F*kH?r0RJcd8E|pAwCa|Hx@fsH+Qb5qr3B~Y!64LE{cN&y3Hp7%klAV-?V1CAdy7wKX~_CQ@v}NU)0>s!3Pf| z#(p}}%l(_Qvv@TFo3}W-dvO#x1Ujrb&!^%td%{&N1a(Jubq*FC5YlNYuxyenJnS>h zleAJ&uF^Umq>&e*ZAZy?6z_bial@-{)#>sV?9iE6nC zp4Wv(E}tQ1yEmmcZBpmGvgnP6Qx*ijzy4=V_ zr>L)R^`p=jD}`qLrTC|lu2jrz0&knlF4(A+A2M3@dHBWn>P&cP_*u8IY|p$&1o3qvnr$0-aO|lI$HE_21ym$5y&Cbm&m5_yWZqwUuzPq~k&xF^NA0T19 z9ap1&VO%>5T81Ba;&sW>PnaV7AB2&{moysGtFs2`aHd+;C>O+^zWBeFFu_CIBkV+B$*JkzN zjz6Rv_D{xtVD`?*ZcfV8R~ytlO5Zcd*G^S#Xz`gCd;f7HK`6vZriFj-f?|;eHnQ&_ z-|c}c)19r8uN@@BKe3zWexf*zM|og8>Oc`W$Ea^z;kU>>k^Gl6y~pZcigl%qAt?z`ZeNK-|Yq^{-lk(=f2%EsSmnR zdTqD;t~;sY*+{ui6a_LY8$Cj6s9;C+3gvCS3Im$TtF1mxMGk|N#s%uF4{Y>>my2KO zHQP4x6}ICgNHEgd%P;Y=ymB`_KG@b6W-?~q^s2Tq(VKP7<4X0L<}htoJQJ19?|DTU1e9$-153{7%9q-q=xP=baeQ zy@?ZY9u0k*8`0OWw=G(Zr}(zEZ;q6`j7&0oFrBkG#>RO-3-1UMmO_G!)+h*nIrJ%o ztq8Ardg=Rq^KY_yNV*B%X`6X?9A`?oT}DUH$dE-s=0_4`bpuc9+J`u$J?G4g_3QPo z>iF8>%bhb0v0_<{!C18Bt_D2cnr&9}`;>{xA)Ot0_})s2*WD;cE^6Splw*cLX6q|_ z*E5fk69prrWOP;{&kwkGPu6Wb{^HwkvLIkje#*lIwU2_G>wS*)@O83i4!f`4qs@aSe!4!_o=y6BwOEyxVC09zn!1;gdtC6=B3X`+-$y=^&X{Ha z>BUvIg~gYvQvOnkM%%jUCT4cesYxEEYvZl(Ejl)j9P2ZuC}dp{>fSv1Xk4jhQ|#oW zSL8mgCwyJYXj`h2JWrJSTGU&Dxmv|bNBcG+W*VuTN5wchPC3zkv;fk)m-I<*1;5${- z`~3QBGVE8HHY)H`q!e^5`2yuQPGCRkAO3vj`NCN(EFqR%IBfY0uZhw7$iUGKTqhePOENDW06c;CDD-JPZzsK+Q%4ETORM_%EUhHOtzYAo$VZT0Lo+1l-$|w zWYvU*#i!Cdwc%=M)1OEpKq5-_i9Xw_OEz%i1ufAIj({zCA!o)%XEH{eja`Co9sgeF^r7j$Wzt zsEE=laWdR_b2oH6=ys!O|Smy;+St!igA8S)tR{WvMqit-wnqdq%vRU zKN@8?5`>BMva}7xM}&vcx{E$rVV9NWiFxdPcICFkn>S=e)Cp27_b&(Ba%6ooIwKvB z*7MlH*!oJVsr_1~fn%oeo1v$KEJLO>-!>nbTr0I(>U-5AX_InC(wKG6kYvs zl{?0qO)mK6V=X)R4A}d6MXfR!FLrEM?S)Z`Wf!CDLAoxlJUTbtHd>#rx{)13?H8$L z#V+>+#p8A;v`TuL#rR?-8pI3dN4$+>WZE=a-#jMPsJM_))*vi$JB>lX{u#%Syqe7K zHGW&u=Wl%*vS+2$u5)#WJ8!gj%$+Q6D?gduu5FZ=c(BN}qr4^Ph&KK;=3Ar0*L}&b zc^&-E8RzR}3kzemNNY_7?3`(O>XnaPCX0M!J4t<=qg8HNj>9Y=f9~t>#c6!@r}2(q zX^P|JZT4Th=CGg)*V&IX-FfMH{O<2;abE4KZ&o_uk_vYwaNM6*dVY#aj!1Rps4nzx zmpmVl5?VZ#kIx_Z;YG=dky-z5pO<-d9Fx7}#~TlPkIYQYH=xfIWf9Td!|J>`yDeoq zH9&&Jp7hX9s0lip$W8CVOPyuL7AG3jbH~Ki{`1x4>(lQ~P?1au#;ML$7>?P+Q@pZ$ z`u$i9%{Qrg0=H_Pm zpByWDAIXYx+4_qF_zW6XMahoS^7Sn}H<`WXY|cqO?uF@AGQaT|)F=Ziy3rKG$Ad4N zcBUlBcq?ezFl-?AG$DNCtjC>sF^{gG=KJ#z5iyilyJjC=CjBT0URp4&Ec?F6xyNIj z50|wRd;F@Nlz`S_ii03?Hax(e;C^-BIoh&2&*Hbklpb(BW>h2&t#gyBp_ywddq1&% zH6~lqJ~iX6>}LU`z8abF`{QK~+Xk0kuUxyL)f4G9^gYOsc>S_$S>bhW7b83@tPHm% zVK&%Q7C<#|Jne}gPTK#qO2XZ7@HcsB67A~gz6^Ux%$Ieti&b-rEhf#3zD$F7c$s8z3Z+& zbvijtJTH@x%g%7Hxz6(JvjUfT!&_DE8~h|~O%h}Ks%x5DV{sjKe5M;Gx(khXoTQ4+ z_#LRgaXg(KqVzk&O)sB(fSpfThmx^dY~cKiSGU2&du#W*x(plZTrWTyQ(&4UpEQ>* zeS62|xzW0353$pck_4x=`j6U#eq~%C7T{hCD|&1oFaOZTnYD+ad04Q`T8O;yL6MFeZevifiX4mXIN`A%}=`$*LPxG{d&X?E53 zD-WL1^yDb#z-mtY3{RxmbE=zH6TeDbXt-4b;*KnTnqr$@{LFYr9gAAwy3L)g>lkcc zpZlY|>r*Q0Gk*NS;?XOM{Ht~al-PJM&YmR0i$Q;*^pI)NK=Iu6yl~%qSIUusVp$&# zC(rp=uYvU`jse1}OeYQReiRmQ^RaH7Ye|vSS?Mf@8Q1ibY#r<#kO{cjZSmeDV0&et z{pr4e=6a`D$E2#)eO1m^JO|pZlqN+Ewh1+81J=Tu*K|c#--c=(*J&afi>6K#o2$oD z>e^G4^B|)y#Cfvhj*zrpHoo`0lBu47glD5G0XaWc)=VFQg2i$Dup=y=6$Xw0*RA8SxR>+YZOkvQo^X}WW2X~&X6uJDK zx1Ty14ER)pO@?=DU7P&4W$k`d^e%Ko^@OGf(>^8&CTw8_zynE)qvM4?oY5?X>|)GTMjo-tL|= zDqv>F-OeI4wd<_u)S`+wZdR?!@v-k~d@eR2YF0U7Y$g1VCynEe;d$Zc1|2Y*!Bf)J zt#N?68P}+1t!OlGZ+dCzx?sxDIFIbKbU~>f8M0P1Z}t%eemgp>uy2Q~b@=|a2UT)C zZCHq=hEr?1)7EQe>G#*oS?`qF4!zy1G(Gb!vobo3id{0{ULnPt4ZAE}h+34gghI6M zB%}9Y_T5unvMt8Nrl0&i|G^SO3PToQxYdG zEeOaOTvp$@T$MBX)bDkKLS#Q@uXX7t^-ps-Ro&owy^KO8xmwY(+0JD|ff;Y-SUbw9 z4Em(TdFe6Yv^&9A-I=TrKIzrjTZsWPB6lCsu7>FQ={9|?G>us)t2|pgcj-b9H&+m2w;gc6no&AUh)!|7vMk$dy~d7gKU3Z**)tz+8Dd&8{c^m>gK8%by-MoZ{R6p1WCf(rxK; zPixI>Z0i&A6*flKij%^Toy(LvLrEh?UG3Zl#Z?_t)-N zvLx=6l@E$y^glaQxebyxcxn9)zz%BkCp=S8UG z6?@L%QQwA<2a{1cSL|j3j4TU_s#ueU^7bA*>aIrEmuMU%bTjxjdPLC z{?stIOztj8b0Cj-{(zhUzF z+>V=dGo^yme&t(bM!AzYXHHAKdKSLc&2MWXXo`<>#Or>{AM+iHhbO&if zU8_&eSln1G3l(JAj8YjaYX9h7%OoSLi9I`hE{esT^im%E-_&}PExN2_yp*IyKVD&Hly7Nv@;7-&@i6pT0t;_>U-#?~y4tRb~aHsCuiWuN6y46H}J9A^YLko{BuZNiw|Tu$4FHA9ck_&mxD@D`rY3iohcFz(qNBJYHNt)rm^}V^}R}PzSnVP@$-~g zO2fT=#*XMa8=Y3)#w}!@gS3`<{qIZ=_$*rm6qBfydp_$$Zysjnb5K z<0ZfEH=l0&^jVOo4vgfPc>L70asAT^lB1u7A{s9UWjrCjGDO+nCCGVQ$w**vxK4Fo z0@mWQu(mMy(+eoRf3EBiJfXK4Ab2kHhKX*2*FN5hYaf~PX@=V8wMDKMbSpgQbiSkQ)Xj_&nOu&!SYVSV zhs#f?g47z+z>zmq)} z>;9Bmid=(ypq_e1<2(4eSUB!P>C-+cA5c4c0@4_DP>*KUD2d zc1>)-s3jn_Xlt^R+d{1so$E~{&`?xRizBxVzfy*)6%I#%lYWZHRic^#O%~Xuc?Ip!$&R zWOVcH#WwqnHQnPOuWBtnYjl6~9*sM{R<->yceZjZXkm7~a>RtSJuczG$+Ryx9;f@U zp%1;{O~V~oX6h)8oVi9TA9lym$WPp8lc+;vG55hltneS#4K# zdbEen+!ZEUKC+VGG5zc@)l>&wraY$U{Bx>%^NLEyRX#p)b&avZ{w}@V?GdCAhgvQ; zHzy^`@O!9MjlPhxoF%t5O8Xp0hr?rOI-~x4BDutGKyr2~ex9vmXWX&xmT?MERC_2j zvy;T#Q|;!d=V+QUG>b3B2_$-Kp3iw&>`m8{(}pL^R=m2_*rjow23uraCU)`-_--Ki z)627IDQWWK*F-W87WR6JiFY5I+rHUbv|M4af0SUXy^P*w>5X`DFL`w4d*7aArj#v8 zanO}KZ7)djvh)bamK*VPaSyUb7Uo~0YdhcT8!>;tsRo6auzxk|dhX_SyROZXvPU^) z;Go`*NquWR9VhxE3-?=$EioI<=QTVA(znJ}I6M6|=5uGQGOgV&cn#rk1?z0xIrX(L zRZpS0=^gF?hfH|H7_nQBVwg#+Bs=$k?h8+yO1@zh4))~H=6s@(y z2JhLicjl`5X988JCTB75Rx*J1gQmm}L&T8Sq;x*i)GO*G_> zNL34V==r+Go3Oo`l7=Ejelu}CZK&+N%bQE-Hq|{f5R~9gQXiUiM7hoo}@- zgtb#%=&0tsX}o$e?e*BH1rgg-*KSqCsdL8D?;-PFeIp7|-_-F77 zi}&vA4BT_pKBeUH#CiH)XUB{53q+av{+f&_*8|Ut-4;&O$5+qSvwixtH|M%cmg6hW z!2-@U`Ay-$4&%a|O9S5HJYqW!A|BAy6A%SOiAvaC<`;7*j}O&RNGQ;5DHd}mX$bKf z`;w%sZ>l5wQOt`^xv6O^Hg`=)ykh8D*6BS-A{;v(D4phh&b0v+Pyf-`(}i!T_zb(< zFSxpsJF$wjSg3DY%&EcW+>FIL`Za@TZnW2s4AuGdQG>_^tc&b3I)T&kBaUtq-xw>0Uq zYW&_4yrTG6)~S!s`%YA-7eP0EwZMXXMrZjLc~Oimdko9L(9_W`7!upfRubK=D90P= z9PP8U)F_F*lEGh+?9#GNzfn%>+Q`yOndjh-`1wJqgCw_1I2u|qJcubn#VAWg&4vTJ zZ$NS$I{*D5wGYxEN{_g zuIotO)fRR#WsW=Cp--~tP7`GgmDK*wm!Haiu1B`aaeB3Qx`#$`PUlE(PsNqKYt^OTR90M!J)!Y6l}(+=WSYA1P@2J1QE=r(PO*SGN82WgW{Z4Q ziI=w{rQ;?Qx3ppe-*9C5fXqViNQAT)t!ox5&BY>%k-$Wq)Vy<}cj{Nkx$Zgp5)@&5dEYx(_^3|#e0ZRf5^~U{ zmMjNXE%sh;Z&HlK@_}cOB-z8f`3xq4F-= z$v1~{%Dk81bKa;!m{g*Ia4%ei7HyUuexk}N; z`!&`!cd{~_w+9G6B!>99U(7c<>u0}z!;soF=x2t`dEJgpuAHp7cslXSG?qg0!?bhs zY8<>=b01zc&b+>+zDm0%hxo}p(oU_d?m|q|d~Jd2`#87XP_2iA#Wg+g(y8<>W*@XA zv*m)naeaF7Rz?4^zmypJLuEakdM-&51`0dk4&!dOVgar|5{cfCqc!}?Jo90=syyao z@*>l2dFNJDm31jal3UGbOhG^M{Dw<)-(F=4aX#DMkgnHzI+Tm`^ypTQf1ZEI zqrAg@ifo-}DfimK%P#)xGC!-Q8Q23(-v7L`we&O-Z(q!)#iO~QLbdfvV)u#G2?RDK z4c{-3ZyueRaBH6$sr_c;vH_p(9`#mtj~VTaGD|vc{c>;NvsO8q zka(*LKN3Tm8O5r`URz#nrI&C&Qpb8ru)^wdb+V@qT{h(b#ze*Pwz+bDr|-N5a&NY} zU;HeT{h9df80~!pqc3EYr>aJmog)SkY!6AiHxKpV&1Yr4c-Psj_}Y<0ZgtB|lF*Dm z_LXDr&c9^5InqQ%fZuFOh}XDn8+2SDD0tE@FN)NE^{hwy8r_73#)DQeqHtdBLlK+H zpHviE6IaTr0;ckEh!>xUW=9;jH~5m{mE*{X3Ivu%_om(lm$PGZW~}K?eP)Jm=*NBBdff3 z!TTZ+zT=5|chhVWxoV$A@NzZ0df)Y7>`*|KS4KZgp<4nu6`xr1W~^2Ld%5P7mDr~H z#|IgH)}He|`(xiX%jr$2_B_Gdb=F59+o7Lunuh+VX~wD+m!2<}{xyZBaijw$2X1@gPSi)khm zxq89Xeb!7*-?n5Yna~<@XprZn4=9;gj+81tRkg7>C^B>DmGB$4P;hpeh{PP)bsL`Xxi)_en}q8PKg^`ZZYe!A5f$jy zxwiUYu-tJ_HPr_sSy_E1J@xYEbyDmrZt@am%b@9Jqq*&JiD0K-Ax#&i^NKW2B9kWa z&XH%Dwlk42m)9K*!h2D9ZMJcmOeZymF5{-Mh*x8>=afp*qZPfkCJtL7rIULpYgHr_ zb0tGv8f3Jjep-8r{m|PvdezO`iEU%@W{?3P!L&i6*e(4)V&?BpJ1u*u?HlPQwHz9{ zPW!bbciBa|OCOzh{@_(g^D3^~`L+I9)#>Ug&ZnH0c+bTznI}6|mTYTZCnQtYOn4AA zG2?Ep$R9Lq@!-Z)y}P<@#kPAlu3g&FzCgQ_St8S{ZyIC!fFbp!P``hXZ_7b!vO>L7 z<8XRH>c@lEy_nQy$@bbjD|+M6R?MRxApLVWIoVWDVa5I)w`lrC#&)gE~`tx14>pOL@_Y?!038KxW8FL)Y zC)t`E;8jx1ROyq;(1ufiMn8(QEz@(Jy3Eh?oG*N@y0v+dIrfk0=f9NS@C$$quv#)c z7v25B?!b9BcZ)Yj?v_SK#Ul#X+rCD-w)?4|IZ)>luEd-WK#NlZ%B4Nw_~l|>tl*kl z-tM#Fv&zfW<8MYU8k(C{(r-0@d3rIp3IhHmCu}e|2`;Suu@g`L;5Zg%6ac_1HVc~n z+6BD-zg}^vT7#d`QhLmZ76PXf1Wq~t^mW%y8bhM%NZ948GqdmixxdwAm~O`tfAi6Q z7eSz$4utb_!4HTIm-Dl&=Nz8^AQMDeR{eher_R#s(1s}zR{-SxI?tN!X{C0YoF3xB zV_XL)EoxVO%1oBSeH1PHV>#iY|4T{d^4xeCg6X|l*}rD4yXTX&131vj>DAla!M8}- zc9hkR8^!6s++{0Pf?fJO77Pe!9AaKV(>TODaTH*+9-?FqNzGxgzw%Klb7N)NIK4`9Y*@ z4_5kmb|j%$Bh8yi0X?bL5p%Z2od9! zU(Bz5<(}r^wiX^P^v#xYeYX1qfo<#nmQ+3;5W=PmK|e`u(U9mTY5^=lY~ENkT3C^0)OD0udE}2mnf~{d5pN%M-Wt6cxGpzrd-0-WwO^T?zo;X_Yw_qa%4m%5!_s@3R&3d=;m(}+d(GDhsTI%IhT!- zUXUyY-7?^oF51y6{*gOABnTrGg-Ug+0KUVzhicYNl z1OiS6`rt5&X8eWo6ZK?2P6N0Dpt(M;^Zws@;wvAoR^cx={=5>i7>rLbdr6G2M;S~n zo)nyF0H9Jg#@Nn}ZSoERlxJV@q*p&pc>8~O=a2nXtlXicD5xF}cV;m3H={uf=U3;> zEY0ZDV5C5RHBx+HHuto+4%%@i;u>8|UBxvVp3?8mifj9JJ zeFhxY7YTPNc*Lxv9g+oNzECzzb0oT4<#1TZv32Kq6AKTg0=P5I&+0xi-LLR-3IsE8 zN}!8@BX$E^3+OKfB9B&_0x&uCzoh80q}}ZcAN?yoqS^^}cKGlYl{Kqk5w%|t7-*Uk zEk0?WlFb4d9H(g62h)M~``euJX(!=9kM+A^?=R8JL}@Qd|kp zE|n|({Z)aICou2_UHN|f#|`>fjdx6I?*II^zvww4GOF1}avmOukv3NL)L}CM0F9cI za>H}uL;gjNe(7_c_nzmx82{x}8l_v}sJJ`OZ@)w(>N;r<;*Y_Ara%58fmEZNw>p}; z3_0=|=;Qw)+yE;^?cSopE`*Dc6__aSF4hEliYDUk4g>-H;LR|Ze#7GDrLUdatn7unkbJs#blO((F?VP) zWnoSOVz#M**oAT{Tg>1KxUM}(ZO8oCO~k_OU7o%_ZksIn4~N!Kl^RJ_*~UM z%}&&q^SNw##u7hyIMo0^Lt0p?adauku>7L$c)=V)c~ZyWvKs8)?b1nd|*=@1AaPZt8BF+ucrK{tO@Ps*R$zE&lEZM$?iy2qxE z%7zAu836d`$Z93pdATO-Z_C~%g1hpeQ;b_)qadIMP(~LOGP*#q-xt!S`H2+SOjn*` zSVb?S%|I~(7;-%}N;%U!l4X2C4BA=&0U_JyhdTOWNkQ}0-%~)<2IV7!ZR)5_)sDch{xmY$WSI=D8&L+>@c5x<{Mw|b23a%QKP{<;<7QVb281i z9EFP~hUtdQ1|NjG(wKvFqFYIHIkc%4f7>gb|C~R4#xL@CFLwfX(vR-|@K=8|{Nq`E(sc z-cxx{57|a8tclK>zYqL@PJr7WDgky-JDw^TJ#3}N=LZ4j;>Sb+s7EQrUh<=hSF0=? z=w<5c)0m@1`@DMyEzQhWd}4<7eCPxmoR~j?K);6iGhR&CIc067Eau4-u z<}sTJ5N!A&&f#gDH*6PCZH&WY=1Gn(1I0{BI&o*CuZ4fX+W5QJyyqDx7*VsS_1Rsb8*ul|p3{`v2H=c8W0_Vcnt zEl&0id?P2W`n#Ni0rJ*$7aNOpa2ZJIIFaSJODctV}WQ2!^%3!fmh!&}4&i9sXcJ(Wm zX9mK#X*n&F94pzuV+Xsv`q2+A21d(J!5%4t$}?Z1Md&8!yhftfNpQ0Q!?`a~^t-Ew zxd)Kph%?wWa?U4yC0UPNuf!zSWFs(&wrY!RJB|FRc2SRH$Mmx<{1)KnWWH#N8i!kd zN3*>h4;}Yz-Kv{;h5Mtw0f^mT8V{}k@WrBO?$=5o zf&hx8{l3;z?TE#^3E3X4tn`{^z1ws0+Rw>FJl>V0DTY?G#jE;5$a{s=D;yCTf9}Xaq{BQ{M05f-$s)9hY z*ew-iWjUhzNL<~GR^=nG90sUb<+(@b!qX>p>hL~@E5OCATVAb)jDA*fv=4}*?6b9J zY~MJ`NHmVAs8k)7jjii)!6r8*Wwa4=5y4hivJn7Ux^)t+fx@!JnAOxi@OnK|_3@-o zvOG;W_Z=E1+J#QitJ0=P=GfBFx&vT!j)0G#cvCu!XpfI;4q+4ff(Q`IKE?&CWhjCG ze9D4YP@*l&vi>g|Uy+{0i1`>RzE%ys_REgpHII7B&r1~xXxe#kW~bKq2?FdNb1-Ow zfbB(VBzsDLvz-di2mnmO=rRtJuH+z#D=OE68=Vh=yp#Ff35>q_?O*c3qti#!$?9Sp zp6E)a3&$2k4zib3g5ctoZ-;lXl}hn*8?7xsOSO4IiF}_j^Flb7}&rEdH}t> z5nwbjFtTfa9X?i6%VirWf8;L}06w6J!z@WKbBX%r8KUn~Lf#3PqF+~Rm35uHJEW_6 zN2d&xVv%k}4c#Q-ft4!sQL^%t*>y+EUux3}t*TYg9K_tN!%uS^Q9 zejK^@g+6BAvW-l#cKWien;KldYfEJYwSslvlNVmNO8xL#>YD>qDl$0aSwE!gCE_7( zn?)~HcF}??8sotoqXXK{vkC`@Nq<>J{y7ZTgqIqdcLID#CxU`x;tM-$^?5X1r=2{6 z&YvbjpQ1kT6(E=|@BWV;e15TvbvrITLCVgL;}Zbv>|Xhe;gK8!ut(zX;t+*T2{t1D zxFO)won!5CgM9!QyMs)#EJ2_cB7CBJ+`$FDxoYy4KhZuJ-E{R_B*^vi-fE?4X_hOE9vmJW9Z77p~XNanh!4_6uj zxnjLeV;zo%dZ~j1U4dW;f*KR@%IYOO;gSA!Dm&6yTwfFDipPHZ=d3A=MN)QroY{_V zM{jXbR^OJBS5*N^H~Vp;9= z_*6I_Q8t`D!n3kCMM_bo6{guaTn0~qBI8p9+u#nwZF+4G*8w8(dv@EV?G@XuV#g2Y zTEIsX-v8DDJwAAa4(Hwa8tctgKou*a8mgt!q9I}`F!jNPZ^<46I3wSQjURe8wU?)k#MfBdJOi&jZ?U^_K1qoWyw_?x5K(actAhI=-)&A#3A!W9Gu|ABvFj8q{7c*aMePP~e0BuWa(oseUf{tazf-!8Ijt-IJSHr_ zX`OK7*7@5N-vPnS?;V4EgqAst^~k9>zih@8ckoF=3IfyKRUv!SH0JkFdzgo8jU#xoN00FX9MX;NpEhpX@tp50u?8Izq zL=CBJnW&T)=v%?Ia#{IXe?y+|>7-M#d!DL0CttpzJ|eit5Yh)cC&<{%W#ySWY&OG&_;GPhb>Z~9=yOS5NMp;7z?5!R@Z}&~mCgacjt?Gk zcsso@&Tq#Dx6V&1os?A}B2#2f`XkNcleSSCqI;fnBaJlBg>H1lrWtAB-FDhG43)E~p<{~5B{alyJ=w+v@-g4nlcTNeu zgH9WnFT&N?%>zRhsy3-xiPI8)H9YFXPN_eRTS4H0-<$gN>wj?ha&he3;GqkB-WhNp zBzmWHep@$In`)~herm8O0l-b5VJQgEVET|`@IlaRPNpGad9LsDC1Z*yk8nssGu_%5 zBUktlBq&pJ1B=?CGr@TmK!9zi5PGG2Opo~y?RUm=q(!zx$O@{SdU=l?6+ZHJs#=Ry zhTC4Di-ljS-S@?bN{<+@mCMkJeT$6YwRmHKCsipQ-)BxZHmLL#CJ_Pwdd_eV} z#Lrzjb^h;me-+No`I}OlW2>XwIc_|B-pe2wfGmI>b_z^?saE>u=v3fmP3iH%HKOU3*uGy<+48ymaq;0& zs&iOi0(Bq|PLS-$&FVc#Hnz@bbZ>j3g5N(&x^cuEJni=|k$v1vds;ixM*xm*XnGbw zbX~%6ZP@k5uI}LW4vJiX(cRS5`2iv7SKZ9B8bGiKO??}Ih%#eJ89Vej2`~yn$%1Xu;!bWApaukx5Jh@!CtJgWOyU|#uT*)iOU_Pp}BO%s0h zovZH(9C(Me!QY|u@Fd<(qC{9D>vpdpawb{PblRlM@sH_R$D8Hk&Fa7HfqLNQ>B;#A z8aTGm#g=^NxbEw<7hz-EsFXTE!qt%nW_UdG#ZHg;z>EZnZnzwDf(W#nRHPjCk)ckd zS|(sX67T%ic5lAn>D{-f=+!^(0J8H_lT+&-)NooHAG^Rn_wd8=ac+=Z1T@XC-Pkmh$Sya6@zf>g<0X;<&nB1&-I?_83DaUlMQs8IxC$m7ZS;@~;K?IO7x$J?a&XIYqursz07P zDs*-#@yv$Y8l9M{eH(FfR{Wfxkz%e3V+H9{bL2%5@ieUZVYqzc>6Yo6mQO(dn;ay- zK?58()5V>zc-||!f3lMy5Woa32?vk7Cm8~!4dg@Kbg&?P$fM8J&-jWkt&uIv4cZJw z;Rz80cwZp*gLxxOf8`S80+x4Kcurg19lGds;I|V|*&`p;Rr24_3q*cooW5qsy-7XJ z4=D_^V@Pb%Xr(QO->c(%tD5G~E*0$j_X>pHD|&8oX=A3rrjL2fo=3s^tGOcjco6Wh z8&ik!E-oL2Fb=$6WOPodzX-?Tfhc7Iwv=KafNq}i#z~%RnRj;f7JKVY&p7pm%OMvy z-Of)nr#%2ri4UYXCCCR%C-mjgq0xbD9%Fzz-MG-QPL_#l2$ogg809DM!k|V$=ZXx) zm~62dEknNvI{hFsLG{i+&2rukr(;~1B*k`}G^M=rNqQjWFSM$C+ycT1KKoy%(||{5 zP%bTW0@4t;9rPIOs#9i2STuT9;2lHjkUe*W@A4$MRrMA7)&uJneaEv73x_gQUAR~G z{4W{g6GM=!|+(`9hVx6fZ7%UN68(U4rVg-!Y!8u~7~F0g5m z3q8|>W4BQB%)8(Zy1%*pDV=U`#<{+o-YY+~*#eqCVEao?I=!D~BmnpjIIwhqu#%k# z%!JMfqRUKt=gE?XW`{^Te}$YBS__IgVHnQa$3&!V$h z#S1DqKd}w?9^N85(h9We$+|NBi(;`h6gYOJg1{f>G2x#UfzyrkAlI>;toRmhno~8+ zH(`=o7@ElJwRtE1q4(L+3f`^V&)nDbGrv+@F*b``T%UXIi{VXGG7f@q4YAw?Y*5a& zROIeX3-Og~h{#%=IfOe3!c^u#Q#qKv3wkOP1u zyQOROaWNSsY&a=fZIX3e&;jHGJTS@*K)=}m~@7iQeklShNeAq_D)T=VFgBOjCf$HYm_y=6Y)UkXOR|TahbAp*6XH&1-0}McPMj)cpLR?ak?8K}^ZrM7Rjtt+V>4Vf zZt0Z8t4@j>0L@rCfSC(}I`%PtwThO9lk=1XG5CG9|FjHPiAhT!i0P|9;3vqU+*9Lo zYWK44fosRGQyeCHNekEOiNn`w)CUk)0bUs$&l+GKI|jE9I|arjW1_F{7%9xdbp`w{kJ_t$Me|L^srId zY5VS5mqRAi5iSJLdhjwIp33p+x26wxP587f00uCwlA}Jwrxhslm^&W(jPq^)Ezdpj z)JM6j`xpFS_wnnW)LjbejtG;c&S`0DbN=rxb@%b|wBGRs@K{t8ACZ783ju3pwm1~@ zAqTIo>NZW7HwEQwU9fCF$&F=m9nOyltWl8sIXd5uS5qZ1zQTIo4Vp^_3vs+jZI85iUt+OEfIC!k$IZHq-%WfIh_X`&5g8@)l9L>yaz6WuFsCXpwA9(q$t z)p0yVJ00x+*8MI)Y2RSId4={kWLnM_KEDOn*2xZlH$TP2SLD#6Ug3NtKj`8|TadA= z^6>&w>mg&lc23oXbMvn<*oHvG)^5)NfRE*vM(F))~WSr)i07h4-&bd+!mhHAQmm7Y) zE+BfadOkCa`R+Ln)ro3uH#ngkz*#?_wUJh zU%$&wzR;>&?*Im5quX>5&)tb`*m7Hh!ydZOY{TV3<886C_s}-+O34@$$ro26=SVvMz0yHS`X@LbPyNP|^6$@xK zP8NF7gMQwxs^yY}J|-ug?Zq({y~r0{&PSbU=YiMzzM1sbfi%?lo*rhiv+m6rYAYOUEifG|5quf{e;qUbPU@!@c?rF=X|b0 zbe`;t$8QpyF`v16A!OY)@3Ju}DL*zRpyyP~pEAleT{pP#v)UHR)0_s@Y71-aDnP#> zMwv)U$eRw6$XV!tiG0*eVil&09GyYr!~l7v zkmsn(hgaGhGQ~c>0m%8_7)Xv^r;R7IkpmKr=xn>D_fq4Zv;~+TQ&(ckA*%N5`*j5Y zJv_ttDd)^{4^p0EZNT~sx$lEj#1%rldq97z1#VX>*OmutZ2uayThG36M}?pA2I|93 zS)Xw+6nd;?re&P6Z z={!M~^Z!)=K&O4Hz6ommjBVHM0x$DvifLfU#ln+m8_C;_@me}_2?Wk7Za8Rmno>e*L;gMuLR&034Sch7ROxP+s%TlZ9fsseX z$@Uu@0Rfs@uWZd{8r`A9OE@?tzgPxw-sKpB^7q4lU0A zwF3xu@Tv*6eO6om8n#`(WsxI~AAsSLlxNO_f0$k{t9Q_aBafWwiB%n*$~7qtJ^GQY|fyQQ6k#8g9}hSKnqw;2{GTQmYYHl00Crn-KwSce<4>=MmKDy+Ek+1em%lwo_r`Fwj8?zP( zz?l5lE@0ABLK&V@@4iq;1tME$)!8f~&7fhyH1t9#(aamdmT!429dp?(e4wS-PT_IY zn%h|bLvZ7jft*y!^8S`8ER{7L>V55CojxytMs1=}Wa}I^ z4Z7_(KMAALf@PsQ-{1(w(6S#D8PH(;{Zfn1+JCK$#u%`6W@xGp1HJRhD7SN>K~R*U zL%>*36=Uc5t{Oh_ly&YD`793~9x^$Py_R0|qAv5KM*U&et%s-ls?HNR+F{$0nY|ap zahU&vboGtF3bqS@d<&(%mK^oBM9Ro-1U4u)v7QNatS29<&X8_iM~3*OO^`cbyV(va zuVILu_{aZ60pKPJ_n1e&<3%vY(XYa>tE9}BnoeH)dX8kfmO*6LKWe@d5L|g&%EkUO09VR0ge;ryQO(HdB$Nl_VgVN`|Uiupm4V z&Ek+}YXp=;Sh9qe)x5RA=9he3r4hGW*3EhI!G@AwQy1B+>%8f(lg-)t>HyD!xdPn6 zTKvLFc`J7Z?twIxZY4W_7G^JNDNQ-g6IiyJvGZBgdYM=9b)K4+{lRr)edpJ*j|_H- zY^?(lv>v!jiO=G@xz-hlUM_@6b_}^1J3q$e;un}Or}z=i8Y$YuIXhfOBKYKmiLfDC z@^Q{3ix~I04N16*Clem)FZNK~Xw^6-@C462VjIvGm);PLhy-VMNG ztOgz;;Xd+?M~H$wLtD4$4X!I}TW=$`s<=V_>mDp|>7ulHHgu zQjRgqM%-T0Sn+_C?uH33Y2{!1Y%k_dcCI{|Fb0e(cF-kJ#y;5gl&#=-ARP19dcskr zPnEB`3M;~gN8(zBS8Xshz)B%wK01Y7d6t1LTlIV&dZ7iKdUUu-SkI~Fz)fJ+GKs^? zi7vveh$wpVW#Wnx1Da_=d8ve+aVwMQKY~aI>jT*xjy2xOtWHX!qFT-7?Mh|F=DOpbq8= zE@GTTM9Ygv$RR3vEPmEQ7XY^@80Dkk;XC2Dg&uYtkZ@J#K-TLi{io^Hp*{E{xc67A zkO&W!HtwawXaj&qRT$GazY{^A@2}E8qE?(2$sCp)Z{l912*HOTxp)GQYRN0op%;#g zo@7Fwny^VEM3xpHPrkGQx%h$%We!+`dRIcE&_M`YFsaD)m6S4cKyvMcKhQ43ja99j>KxzlqC%AyQ>wEMF z@l!R37bm5h-C#XgWi0RZ7GkVYSpdFe=wtVhX<3&13DQ^9$2=GU(}npS0>AN)fiGIc z?Ox<)gXQHKv0@_(jd=$Y85}gP?jZxuHp)D$*9&kp^U2F*<%v%B;0IBCX+zj|zlk4! z4FI}%&NHUyx-3lAJM!LmEZDTJc+FT45P1cL0s!1(K3oa8UN&=9?5yO>hvK~P5Y4;^ zID!PcR=1GGP_eN}CkfS{V|&80UnYmr10wkqVGGyxx0C7%I+Kmlo#iLA54={d)P9-T z)I82g-2_1WVtIGlujuQyZ|T2emA^-Dy4%M!5-zk6prU>1ciyr)r&V^_SEw(Qzg<9% z!#e<4nEQ94ualkQIWHQ}lV}zBDMW^IjXAH7r!XYh#eooMVOjADW;^0aVI0`>@ z3&;}~OcSn6Ei^>JTYF!{U(1qVt&kY|Xaj&vflE{B`pK_jM@)%T-z%_JSK)9j%GxW$ zs+eWSOCNf*Lk&eH4w}4D)ulemm(~OidTC*l3(toc@AmhRG`!kI^M!fto>PGw0C<@s zcLVrRP5|<>ZwTNGK(uXuUIwWj(9Bm``8qPc9(3^anK3!fGYmKCeFEny81TZ)=!41P zJ;y7|dUEt#0)A!Z;2Znk)NZpa#+QKNi%pMKQ*^pA293SSo6w%vNme0cJmXvWLg&^W zPjY79S(0rJ6~%QUJCr;h%u^j)-wHiw)LB+_cARzR0G)(Ba@3Q{(9yGf@yLdp>1F2; zyW~QP(*xu}n2fT_Kb2$G&}Sh>wKXVF_lj4cL5oOkL9*C0#9IXxvJ!^+Cjk*H+;FB+ zj(n*qJ7vvFC)8}Q!66p{EB3>5mV*G8=6nzbK5Q?`W!<8}7O{OnNL&ciX+S`>Q%I)+ zacaaHbvEk!ZUd|E&HqK$3I58I&Bv^)H$A%#PLrIUT95sm0@5?}TCf+$4{q7~cC2M_ z;W`C?<|$5s5CMJ=)*9C`Phwts&J_7P?+53i<*8Kp>Ko(>t?Ff2^NitTp9*E!fMz{o zbXK7E*A*!^MSDev9Bb($uxVGvPojsFii=4Okl+V1wk||8X%+U0eJsgF=w}_#BdW;= zIyK)!-gu@O0iqnXNItF^^ALt{eH20M{*ZzR=MBoB!b1Cyui-2qs zLQ{V)Bzev%^f8nJy*sl^R z)V93BiQN3vU3bF|%eg+%FxDs@c$LOc7gNtso9ZdSI`!0Z@JV)KlJ)avOr+5H8M3^K zLxwjl@eokhrha5gs@E7wyJFtCjo>05a_BHO3O!b6Xr;`&UrK!Fp$8wjWaIuldHF}> zAy@Q_kq9nc2+5GQ*V{fT5{sg^ZD_@9Yadh!0t}lqB+MKUS?} zXh6=l4KlCAmNUKBiMmTZ>{`wk`AJ|~MTV=y?OSUWuF`41)sqZtE}i%P9rzuM>8M-epP2Kz{(}leT+1kv(&W>E z`HJtcD^{Bx`@O~0?4FY6iSx#-PNCPi2OpYJrMaAVWCIG%I#tiEiV!j_*rxR@Z&~5$ zi+upd64tVGwVrnXtC*6?ic@iJ&1#CX7aZs8SfM)3jWRvw;Zs)P8bA#el+D8~HChf# z+4hwUK;Er27Kx}?SKmZ;#1EaUOVliK5dieUh4mF&UH zs|zn0FyngNjscODbmY&~t~a;T)n&d)@`y(~qPy_I3%e_>xT3rA$}2-?zjib`zD+wU zU1(hw80QRaJIh!NT^m@`Y0$C1&&qv%$JcAvwkcl+rQ|8I!h)4pNy{y?N^{_fU9IJ5 z6Uk+4n5KNR4SR(iW#wZ<*UCSpS8T*xF?4nO-dV)+HC)1jZaMq`yCl`g-KMZF9QGw2 zWg$uAq~aLB!|Kpf923li(*2pVfqZU8wqQCYCgodRTh4m$)`MJsBO4Ek{JVyz0h{ME z;Bs{~azWS@bIyBhX@S@Q5Y^-5^Lo9CpwLb#FYu`8p z2v}q6YXz-LTSgFCrnmW0uDgM(V_C5C4L>hnVS}J;d1G{1Ft4}Uf3`9MLHh^&wz z%E&a9df@PG$&FMs*VyT?85aox4oUfUgdyY39=8p5jQEv&%` zIs`2})+~)fCc`?riPaVwiYC;NzYvYdxCUHw(M8=8p74ZK5)XaoL%SzF z@rgm_!JOlIvT{9JtBh=ru%!MQx&snHkTa$A=0eMF%WXT%H-@i#o%f|2YREVRA(b;d z=Pd_@4rG4G*ZQ(dFTLT<6WRgH%qf&AWK7SfMrEf)`W?K2`?6^K9v%^w&;o|v=J~~( z%2Qr^enkhlTGECGq-7ia=1;{%EK4n$okQxp4Z? ztZoS4{5qJ?jPeQyl&1@gG7X=a)rST-99s@|AwCOMQ(Yz|8-c7 z2wq$XkvWy3jmieNT#7w1lTyx)2@Mn^%T?nX0FXNNA$9JPbSQdS@b-k2cebo<#q_kj zso$~051gF6T5h8huBx|AHKcp#rI$wF8RBuP^5Tmxj?)0{sL-ad05GI<611ZqT&)xj zckNqEKUoUC%FNSDUojzKltsc9A|OChep=_E!PDoo9DuQlR92hdDS$-az_@&e3c>~f zLMZ|Pcu}*Q6wa?(z6&emMg7it_>TN$aS=sYd1R%H>*P)-0L;zI$;qd?8C~=NPs#a5 z!qdVe@(~CK3Pd`hn*)HHI3ksteJPxqhU;e_m0uzVkS8hZMo=g`X^GY!*fODCW+_Hc zQHRix^*8tsgAag9qDvx=kMyHhd9Z>egv}k_pnLJZ8r1r6^sjXt;4@lLc30AP#&w!X zJ!?VBZ_5*O_wV07fum;tZn>p9eE4wiIS!tgu(MIl&^9;ooL`hs{)VLKE#|6!4nCQG z{`u#}vkF`XSW-ZV_eu13EVI~3p7C1?VjyLwkvsCf)k@nRrs;Q49ojx)J?xOYP`2eF z=PUVPU9?!$d#7wP!k;pGvc>2pcGvLsAE0OJt&Oev#>(`)jF zUFI|Gid_!vQ@!EXIKLd9{F0o0NjUN&7pi!7Tz;mUE7@FRn!EqT?YP>_#Gm@qr@GI5?sMH;?|RoUvx^#d9tb*qT<0qS zu6);rm&PPdpups_gRG(U`OklTBKP|1ukXI_g)aoZ4MG!@PLMJG@$s=jo!}Q=|7FZe z&^SS}E8>lfa8Pk0=;64USgi0kbl_<{Fst%NSB6PiF6Hma69`y1wJO4Q08lRS5da7R zB<-Lb09f^xVh12k(7d;kFD}SeaCm6s(RmKe zPeDl8I6V-q6N~|e2QWSWDuB!EOeO%o8@EHNM-BjpavdNX;)Hr^LM0}rcJWe@^;X=E zeB>kDAOG}4!TgEZE$DXI{HP2%$FTtz7>>zW zbGimFJ3AYRE6(I+6ad%}1Qw0Jkaq#$bSVJTRh}lh@qy>{)unkwv3VU-**+-{(AhXY z5c5I#eqQKPc}@}fbK^3KMYh-IKN-M{0uN(s|Mz+(|25+peA_LKWt_YzCElYytKG%o z2iG>o13xc&*~_{IKJbCv!ycw6B{+2GQ1=_Z@f+PIKl#b396JCWemMnTyUtf=SN+m& zWAkGl`&f6?RaXrLfDeE8!;M5u0 zB>*td+3K_CffwZPeE^W7vq&z;@#QJ)vlC7=Wop>phG11M84!I+gG(^)N5_6oqH+WkFxCgA;|&&xS4)1-QA z;SU8^uPhP|m@o!)mtr}K%UO5)2c2fD695qZ#3w${J@u(i?SAqnf3myRz3$b$@r`fn ze*gD>zgQB|vG7zQtLmisEW8#!e%wP&v|a$X=9+7w4G(_sgG(Upt^e)c{%!wULaA&< zeK>WN3i&QT;dxj}aRuoZ0^FZv4wMz3&JBK!?6#L`jwlB1c%+^=c(Ur!PObua)`jS@ zX)b#lDbs&RHm*Z!>|@$!?!`=H+pqKbaC!xR(Ngc=;q-F+xn=o@4wyeJm+?mcK#sqn z*#`n&6Oe6!Kpdmmu~?jciB+E@r;oHKuQ{$IbEHL6zPGS2-yLK3J@3w<@$1701pw{> z5CpgwIHw>mt29@31hWbZ;r#3f*aav6aLXemnX&``+@4Ri|E%_l+UCz_#h=%HQ-`4r z=_8;%`<6#mTO{N$>_4zQnTgF>1H2yKR4{4uu`S=>i=m zyAICVTuD<%}s!CAuMafFsqzvEW+~*6oIIkUfJ^Rcr=(?|p`hgb)bWNP>U^SP&~xZKdK*M8DbsMFxS$3~@j#gAtmXlTd~xASfUh zNT4EvaiZn(V`^=SepEz6{GeJ8h(rrv4w=q5dpO_qzt*~*cfHSk-hKAoXPt6R-&+x8lpp`~<608LvV5Hx#I$k-@2*{{Ipw7r|^^lI) zyWp7)RwMxbfe(CO#DhW5zx+!*K~)XHgoko6#tC0ToAGd5{GpoOsqpGGNt~$n?oFJd z_!q&@q_qGboP2FPaf^CIR^vGXz&$UZc@hm+@grLR_W5GG1p&Q)UlS0SSzW7C<@hKejNY&j8dg7ETWL*z+$*&0hdJ75MW4Kgc031DvkOvmvu7 z0RZ^-F77e-_do@HZws^yutp#)z=wlX1Mo>HeW@$Ag#d?p>Hyb*69dy&2d;1$*Fs+$0*|JnObZk1>_0GB{$zqR+VQ3puLJ^xi% zQsZ7AOrS@Il)L{D7+&!;P-p-$7Rqu z&S-Snll6g*XC`Lzl*2?c8?=*dh23Z|X>Zc^#yP>Ji4%<$*MWh!cD`%c?v$VTS6HVx z5MR@{47mXqr_tl80U!X-1mNLC)PHFJyQJRlsu}=_{~mxZivZZI0oY}203jevp$dVz zfv_fk3UBqFx@sGI0Ehr!3$P&Yv-e++7O@vTyXR#W ziV!H^SI2TDAPsIPYfpdYs_dgmu2GIK-`YeQTk+Ks2niT`zq5x^j zIRcWhJRjhx(TwM`+05{b`cFf4%K18X{FLrOyn5rE;B#hYzD$RI-?i~P8%=rgztNqB zck0oS_iZ%Cbh=aYI>7Gln-2R%@@2)R&A9zkpM-z~fL0Q({$olgs>pBK8$P`9UDsnpi@XfS*tTzM#fbXcc9kl&Ha5kzySsg$Fj2ez=IVZl}BLb ztYC$eUz9_`u_)j#w@!*UQQ|UK>V+k=ywL_c2&^ECXDDtNG^LC){;3tCrXk#ETH2m1 zI6?i_8*}E)rWc-I4biYsdbE=>_DS{DZNaa;NQ=(rm2{nQ_z>rr|3M&=x_(46qnug! zSh44HAbPtO=!E?<5==T}-;Cj4^R$1f9O~1$tAE-Rno9cqIHpAnq7^zKXq3Me~EQv&Up0oD%{$wik|)o|=)%q&(MEQ3S9J&SvF4zPEk z1t<&+z_$f;7T~9zqy_lIv>pOd7^^tyJQ8zRhkph?gP45_=L7;jUB<5H#HCz%Fi61X z3Plk3F-vuvEdj%|S=DE-gSV1EX^5j!9;<`1e_p;1c+-E<9XjgA9VOPi=ht4xcM8-|3js6$;4jI?MaQ+za2RI+Xc;T@ZU91n$?Dz> zVC8R3-`~cy?xbtl-$t)-Cw(ts=1QM*Gx8;xgR?U(E|%XZ;3u!RMagps3wKQ_(Ib@8 zrA)fIs&(Nr9O*vMJY2KuUeZb~Ew2e%Ga1OYaGuis%W{@6K7Am;P|&RQ^uq(%emG?& z4jQ-8u^_r1ge70n;Ey|0K|WG)dRBYZVhu3@KyL5*HQ?U+b5GykFIxcV!P1b+9v_W( zw=`q61GWf&x-GB(u&^lk4d#Lh)m1Lq4KBPb*21_vFB}LP^D9cN1-4jlrt}CX{lWiW zWiw`T0zij<+5hj+Ie>2kN&u8+03-l-5~?=N-Jo%5jo$-M)3`VR@SX+9#lasPRXBV8 z6+OzhqCrCd?82?P1-@e-jiJg}=hsGH>Y`lci`p23e~5+y!tUp-0_5Y|Q4SiVO ze`P0HwdH={-$>QAwex3OHJN(DFZCk2tgREj+Hh0Ozs*lRf>MNRUFIs@l0R(KT z`Q5fyez|QW0<0CtU(O49cLBhJ1PxbZ=%N)%BF(dS%JRIyctsj?y}X}Qo+2m?`(dHY zjKy3I&m=`)D=b2R-7e2;=?r(I8&o6S;nLmQ&f+JWc+!!cd?`cD&71unfA+ou`c;er ztFn5f@fL9x^uXxm(Lwzu4ti*OP+~+6Kj>Bn0)@j7R3x?;2%Eeg?bc{4*;g~Bwlphl zC?=svuzDUQop`k8ufof} z$D--_za$LXR+(U@BwG9bNbzlsS-49$X*XP6D*%QvrO?nwpHO6I>!$K`K~93kv}xy- zc)7uQehr@ml&aOAhrR;clOi|EHcAU(pd6k^U~uRdb%-Rij3CW{S6#vg8qx@e=*?%h zLezX5KMb10Cz5goOjdp^C>t{~()Fv`SYb`7lpESawazv5FrKYR{$BVI&pz5R8h2Tf znt;ADD|SB|KOBmIWs;6|HR0xmn#O#U`6NB*ntWbUT>1j#)(5;xXl-^-!b=;w(X|23drfW+p0Ft837M3efpKJz?;eF9F}HzqX>> zs+*S`OXI%$JFmE?HjP%MrJ+Q%?wWL6LVCCk?!yL$EjHsOZQm+rVul1jVv~`=QfVqn z<;c*mJ83q;(iuVMl)dbJuY1AIedPQXacj@gl;c>X=u~`f0VDC{kcEFFdyd*9^_kOr+`M`_uagy?oa1ywhsQ;0k+;7x_Wg7P(+@A9 z1OFMMjhJ4#ndSG=c@l=o@29J2He7Bx+Ajyri^1LaJ0}jf7ehI%AGa*T|EgbDE~c&5 ziAr#}@2%`4g&smXozdW-{|sA_srYv-exyy*wXxuv!8cRD5cm^vqF`{|(qwjRbUJS>LKb`Kc~gKyTU>UT&e z+)Y%s-R5tvChJW^J{W>f_=YyCWxP?*{c#X+5gHZq&_P2^K9IWf8TA+L)SU5Z@4~sF za2H19ASnISBh4pnowt=Rj;6QMRGuk3<+a{we%a&uzrc~()b(CITLe}d@=gG$Ry!oL z#3q%KZs;HRf=95J!#yyGKXFjfu3JxE03-vHhGHowv;{xl#gLXXOiutU=b)+ZGYpd! zigkV-bl@#P4o|r4!I?lg-B%NQOk4U>{?|GfY)VKVG&SK?R(?4ap!;XP|5l^+S8`t? zM*EbqZSuK$m3U0dvf71hzY75K@A_8VW|2@>yr=9v1cviO&s7sLd-Q$4tXWybDjoxk zg&!)?h;y%=Z|865fgQMD0|xDhPl2y|Swk=F2|zFZpAyEcL%ueIA1#V@@X57Jn>jJe zS<0opBP7RX7K)a|kG=8%SBJ+f+8O#$`NCl>-{6i-2BnAf7*DVITJd>Gbu6~$8f(=@ z;rCOmVn#kcmCu;-^Xe8hj5ln!-9{{cP{&Zu zkU=wGyG0+N77o&+`0k^(U5!xi+R`tiVY9mSiwkuL{9G(fbK=o*cS?+>^b2sQx1y_~ zw>{w9&sLG(h7pL35SR(delvQ0T9q<_NmhStP&;jaaO@H5XrBWBIxQ#xHOE%FWOrAq zA?@6R6Hhv^QkSEadb>(y=6Z%7HQ`G+w+Q?Q!HpQ+DLDCIdWQdLnkZ=pLxwYvLkOim75$h1{~3+5 z`WHYB<+(M5p&=j)5S$C=iix$=LbF1!dVSP6?H*0wDPSe!%a#53U?grECEJzpM^a{8XUA z{7kgY27FMcMUnA$u8q`lIT&gl^F#z?Cd5~`thNtp4Ti_|Y*4+4(^4RWX zedrwNGtzW%Z@dCcLRL4-bPHNqLm)kDw0Rf3&9@z%5rC`qag{VE! zhO#XxfDm5^Gk#)KdDsr1Xr&o4$xB%& zFZBfQ_=zKJ>B596s2I00o`#Zvj1&Mn$I97-Q$au+@VDtm+c(4~Ep^8(%1vF=J34p> z0ia6)0Kl^y;5xjpGCFi{Y4o)}yLt5WKRY1c9~>=xL8{md%mg_%Y1DcSmgc`nlYT!n z=A6O+?6rBog+nHv#;bLFuw&-^ZF)q{`X&NEBgwwKGd0||6g7%^@X>c24(njjXA_^+ z7y!h-P~4Xk$H-uZjOz7^LX!C~CFZ8z8I8Uy<#QvyHdJ!-{%b66evhiu-uNu?;BtNJ0wSRircC>VjygFmRI3JFV zgYazJAe1ebKdUjiXP|s2v~NKzV9QcQP-*iPq$O_qWI`^_!KIZy0oE7xcRwKe-v} z2sBvT?u;X8VQs<{#`zAP0O#l2IS=5Dj?W<^z;a91pE+0htr)%6GnzkRPM?b(kM@4g zc(mu9+5nuU?Z9q5gr*aJ4*&cNiYNkr$A~}v;4eN^bpifa>Uh9OuVOdaJkrw}Xaq^h zLjd58jesD1@~{IS@8A}I@HD6$vNjABrXncQktxKzo*^BUd$t*V3Pj8LM??7KYn}T6bRo{vRtZHDhRPZLu`BGL-!u#4oCCU}s<` zue@5lQQh%#z~~iQBZErslXiO4H$!0>0kn;Ub44YnY?O|JK2%gb^~D=`sNvitKoIcO z-QEYQ9@<{b4X@S2Y!mn?@|Jh2zW=UzeyFr%NB|fhXQ`bMq-;f4(b-qCeTeo@n>S1{ z`@Praze!5n7#MNko99LgXNv*vd3Xfqci%^i)2i=UNlPtTFdQbLhN5=* z)caF)l@TD9u2F#v2)9uD#%Hz2O1i)#}P&coU zy|#=g;HEu%HxR)(MhH~lWr3)%@!==j#JFoc^jS#1fyB#LYdF2Q;Xb~^>|bk!W42(} zO6)pK_B)ih*1;`bKkxCGGFAvu-=dUY*y!y@1pZC`W!3hShwBZw$E)q9s*Va}X-ZF# z4~_un*#bleAP}}o0MILJt+~Tfa7B1*9RY``-Edl?MY5?%D z2m{=NlLlrE2Ef*44C#|6Wwf+)C#};*{iia(Hp{eyGqm_A6nEb$$YId+4EcZ=HVq_w z!f6lSXMh$DEK9#utuPV$s#AY@ZnSvcxzT|?kzXly-$#J|wqiNK^{c?j;Oz}1(qiZ) z42N(07$_&iFa;~bt<}&*&Ivf6#K7FXNo(Ks{cE)hwmCPwU1KDzU{%21PUAkfx(F1KKH|;r^=;LY~f=ppbz+=gpsBd0P=3iz}*<=MywO}{{gtp%+LZT92f;k zeRuLFKK>y}8aj;tNCybRPg^oz9>CB4%h6NLe%I&?q+}Z~ch+J5LeHKbE~)2P9W?lJ zyouF{rgl<#tzuQM7N8>10#tkUx_tO@=^8aq{%-_>YHQ}B?=p)51cH!5vF$WGmTa*s z^7S{AI|SQV{$|SRPVA*g2EuTNVpMxGbE;IJJH_QGRO*qB>@> zHnmGzD;|s@yxa19MndloWj~-WX*V%@AEf&JMV*HdNU2zOIQ$%nAc&iI(xp{$Zpx^2 zorxPd%k9MN>QBIw03e_d0`~H+niSkZs&yybdX)6Y%eE~15daKIYZ;(oYS)+)RseFa z(qk#qA8@Fx;Xy&dJCQ{RHO~GR`=A7y+5G-ynhmQ zv@TBU`DLV-+w})Jk@RCh<=Tph$W|qq_aV$tjxj?=B3naBg7&KMkl}cr~Uss)yTV zr4dIgd6Izk!7{WNBc|$v64RifQC$;uH1yOb>*Jm;Q32j3?M`Z>R&`dv-LlH(~g4S;(2&}hE= zOvwvrI7edu&6$^LNSTqw-1xR~4ge6izwen8E3@jdH;+LB;(otb9TrqOdFYBa8>dW# z0ef!l=aUZ2$xla0-S%U~UsQiSEyP?et1#bs1!9{poRx6vlt-PpUC9Bmk-@)M+EU$9 z0)TL$)aNc-ZVPGB6OUhR;;(z?=t1&D2p|9s$g=;>HlXJA!b}M##GEJr0Lw-IAOujN zyxNPxZmmTCyveIPIY2fmO9lFr03eK#v2y3cpNB#?xxVo?G&#uL^^EF(IdMB7)MD~|i~Hgscd z4*gCM_|JHe>SrdZ=^o5UK%Z8S^n*M9toRw>Q}qcV2tMD&`zr~9Ps&PQaNfZg4qK0!M%hC9G0|zwsm3rdEIQ1lCG--6KZYgjfjNBH9W7cCknZY=--&~5`P0Pgg=qj!_i3=eGu042n#6oWl}z$iDT)HlCF z`~0^Qw?bzc?m4r6=2vX^9zT2E?yxZ6?$ds0#ES`d-~{Gm1!G_Dl}&2^jLNIBGld9I zYB#sS{H;BW3I|)(MDD6E=|u=5R$8(D=C}c*C1C27jP7ZQ%KUoFVXn5#TWM zH|0$MvI*~;fMY!~D}F}!RDBxPXg>XL4sCr}uRd~m&;Fd}7dB$}IdL?CJ^xgx170!$ zX4$Oj_5*mrXfIaj-4^oIaQhQi%lpDlj?PgER(~u@00O|Jtf!tU%`rE}RRGYNBWwc@ z0;v5b-|_0v|MY|l5S;)(TmW*dh*N+kT;NN3(r_4b;~O>NA5z>p1`zQ|ZC3j(DdhoZ z%q&`BT`#xfZhl``k$8Rpt>ai2KstY>hTz*rqth=?yMI`wT9{6vvav#aS1B~OzkI)!sTwP8r&oITU zwsfmI@J*OuEI+~ki@yTDb${WpBZ=-Q^`BJWpOQ|PIgtVYwq;Ehi%e+&C*AR_qmRR& zUh$!%5Tlxss|7qIU`Z#-cj|9^v-GatmwSNel+uRP^wcL7hTnaE1*v?OQ=&_KgvH>F zzfu^N2Vl@L02zDK<7qgVzRUa7EJWsd2BySld;p^l@Z^p`+wehV0%p(%03MvV<^Rz# zCXfSoKU#ZzDc3I`iM72~m$t(kuD9*tFcaXQ)gX9yn$Q}KfXLI1d%j;~?3vY|8XgFR z)|DE~070DJHsm>j^$gDgpbhY3pLvXNt3aO;0BW!O#3zly^ivic_^09(M<2iVQ=`+A zLR`~n0JmuY0E~bxa26YkcAa(A=sH5Yk`p5)wGbc$;mHB>%H0WwnwAGgzWy4CS%Gnu z0W3^%C+#Vd@@kpRM;PVXQ~%+o9vN1+oBxy4>)$AK&3s6tBtK^V5s2q}N==8YHe%=_ zCIX@OCjS$kkb!gvx-XnB4!%a?pw;$R8h_PC0%6bhX@&k#*$39z(yjo$M-fyG;-4(% z2DJB9br_0VW!bMbNleLpQ+jab=7hV@c5|%z~isB^8#QqAU$ZEXZ;b$`sah4^!U2ZGYJymaZ@5ZxA+>YRs0>z< z)1EGMq@IS6<#aKr)K&D69>OTWc?mE%U8}8{evQ>u3aMMf_4ThoQ`$g$i>+S&WOU=ck#Y&QBa^h{N3z z0Qhy625v~pPM8ol(E?z6hhNja2!M|mHPL3fydLU`QTP8f&hWuZC!v;H3nPGd{>XxbO%rAsh@f_VBEGQoq;MQ()GjT zk4BKVul!=ifo}2f6avRZ05rg7RDr0NTVG81l1Pe(4#C z8!)W6r#wM>{fCzdJ8)Ab)~yh!)L^Hc_~nEV*;ai0)ta<*_PYT+Ka9z$-x=x|FnwUV zPSW+$_x%H95wxbf8R@$6VanFbs;8f>=0O8MC}QSLlQ0rM@^Mj~mh`&=Dl>2Pm6YrJ zS3Ukr7l(GA6e#~vR$33+tabP6)UWN&X-_Qx<)I^k+b~ij?Z+J}r@rke0dn;~@x+xd7;cUyqfp z_%f1$~GN>RN7_73Q&w*k8APq{Um z*KJw#Z+@5OV({O>xrHfC{@0b+`>CjFz{~_}($fY^{KYr(|IG6GIZ|F9*a`2a@2Bg> z&+vN%oDnw|JW!8U=MPEH{E_T?%Gt|c-Ws^)0h$Z(0=IKJ69JI&E4(@(0U$9o9HDc= z6`FHtIkU_emq@GpftrVM18CeTFRS{XRUym9qxVuC+SwY)*@_v;mv*jG-cbvH8D-Dh8q7#< zAQ2b9C0+W}4^R9oTGKCIQ|{;U7&lKi&N7^@oc=7$&G|J&1$Nu6sQ(>6NkcuN^p`WG zn{GIeX9@v z04WA=v?l%Phqq~l+@x>Hd`7mHVqNoE8EKRb8F< z!n-YI+XX-${Ac_-bqT=Ax=Uh)9P)MH34_T=+ZPBhP#aGA8W!+P`H81KCMD?rI&SK~ z#w}-rz%Bn(D%kTi{xfb5@d3^=E>t(pEqTalLwPZ{Y5#hLx(Cb}AotUyo_=_*tTpBI z;*ET09Nz6+>}opayzbq z&-|hAWeFUfE4k&pr3{`!;Q0W~J^Fxm8Lc2lS)vgDv~|#pAR&EE5Fi}B_F_2SwYd@7 zEd;hz05sr-Rp)N^{K+Z*lmMVhFfpKR-3gl|07zS{nVj_1PWdU9a3&c_d1_HqefcH= zz%3OYeMwJhY5TxaZhgbu1k}pYpRH^<aaK1o9oWpB59iSEOtpMS(fIcHUn!(iefbwXctj?m~sM=CFwowz< z76H%zU+whp&E4*K>W&z=1Gu`-t?alf@EsO+1$UQ^ZU@ZJCZEHZC_o~$&j81bC7B4rGf)5a-&Y6ZtHaPY3j*Ip$0!8O%6fOiY- zc$L2Ce^+_4LK8#Tv|E&NI^mFT#jUOoS<{hL*69vacMWqnDx-T+kaY4}JBgdLS?O_8 zCutI1sl#i$>#J$5|CMPWpq$$x2#%U{hQP13>G!&6A&o559i?OOL-F>*5?`Vx{}Udn ze6@&)BXKpNtJ@Woq8t`~lb+95cMp}+TKRDb-L5Qe#?`Q{{qc)-WfGP$n|M1DUfb4> z-n4(a%Gq?qHN(-?pYiC|pYW!9#&>Frm4`1taqamhbE7-{`gnB8qgn|&DU@5qH@;N@ zK&waWf({Dig{S!SVejtJm;HT}9|bEv`01}rr44Bpw6&?SAJ+Q&Zt`p-j5K9gp_5%l zd)RZL&HiJp+_GsJkVa*V9P0q(WN866(LpB9zvoFuBHgGJKdU}eE$PzHDkNXh*H&6p zB0npBYmn^JT?d;n>@5It&@M;3+F+VGoG!V8vAX1Ur;B(3*R(1y4^46Bc%6I zBWO!{C?|#wka|mjN9EB}8>Q=#QXi%ZC|;LWol|j1U*iT>Oo~gow2S^712e1sNoQ-N z8?B#o-9Swm+${o=pyHx`J!2lxpoJm?=I<~!I_o83i@U6jzV>SUjs*PBnAOjZN4u7H zDP^6U+J@CtIrO)xx$G{&WcU?eE)w{X-#<@F#ToyS)bk6=kX6)zg;WOJAl!a|fzqSgRR)E`FVd@UOppW%R|D z=(i-lQVUX&yT0m_(diG~?Q}*MQL?N0X)gm^1+%T@cmEDc03aAv^+Lg6Z2_hzX%tt& zRX{zo^8$dds@DCX?eg2*hT~Qoiy6NnBoK_|R?067@gt=66DCWV&%NX(ElsYemf}&Q z2h;$y*{TqzYio+x5@j}G)q_CUdzQshcAr{6nivZJwHskjg#bdqKlUlXkY52>)vp0* zgbY05=xdoIRfd=HYWJ-Uh7q7*1TGJz==?(Q`#*6okOnwffWb{!YR}@GOFc?d>g%lz znwF^xAwr1AlyTFTGE6USW>_!n8lXF)x=r8vW~h#Sn&e~rF!2}Pk^iSZv+^apUp|A; zwy$*)rSyP%rcqp(LD4$5WzGd0U4Wkp0G)OIZhb>;W%TuTNeEnjcyz{(?j4=^V|&E| zlHL_qnlAIsD!=sy=vBF?Ko4DBE(X07MlU{?D*SL#1= zGgdw3ulWhE4RQh9KOMF#x>)1tPl=IUN9Ga+MZ2*u(L(pR(M^4QmtkUrGm7ht9BJ1z z+o1u_5T+i>T8yR9*j?D~e)&B)df-9|K9T@j5Xj9;WiKp+q#0LaI>jJCjP4I~Q{pQ%0oPu=sY zYM_Z1%lPnfKlv^4apZ_)V}orq0aAOEwyFUftpH$( zu}mqz$D&mP{CaBIpZePKz}I}&p991Q0_Om50T}>S-vIjxKMAg11ms^$OJA8)&dtmgX++aIQ-%O z=b2-*W#CmF-8o!=47d$?(Ip+_uZk|OERU9CSL9z>R+xlB1cC(t0)V>&g5zPW+Gr#O zx7z3fMuFWeI=tw1&C`zBmLe2N9|Qoe`fd~LQke*s_dn|+_fr!ORC`b~LO^1NT5TCQ zcjZJ10N4sU6a0$f?)jb{yYip^Hw3`AY6E~DAa@#o`efiE03_^u%^fO2Kq35GxPLMn zO#mUlstZpD#0B&kRKPFBSezFF%xe;GFUl>tfKad&KpHVa)c^`lP^MM`Uz%(*4_}&m z(IS8>k~HE9i7=4cdq;(GVJIhN4bX{bHePGuTL3evtv`kK=#&5;ZdM-3!@xEhFdbkg z-5Ji+FE5%xQrG-5%d7c2;FC{zI&~j_aUtNj1hN1gn9Tz+fonnQ4wlpXMJQnvbfhO2 zWdb^HC+i*`z;glmWdVM9MVz-PyR5|rK_Iu|=E;@Zefgursx z2!g&aU@NeD&-~s3&imfsL8;7>cCh#~$Xh@>2jR_1wuacnIGhO}US zQiiyj_V}tRw*^!NQKPd4s5MU6iwQty`BTREo3iJXrz+b?&DC#f?2Cu^MSpc9xu;S`6 zUIaE)ep|2p;yZvZyC{2T<&d<1rO|>^b_;|h@u0Q^W2I%gu)4&ep?0cF@9p6|YNlS< zU2YF;nic|R09*(T3pr87vK%gY@X?%cWTj`-$HG~JF;dv0HrufUpixXY!o+)i(Ww4< zS+o1W543>2k9*rgul6>8=95c8z^krR?XtoHI8^^7z3p>Q*Rr$#E})q00ctvVy`M+@ z*UHYSKR>SEpVu0Il>nGmIPL|-c}pN6Fvr;dW9T-h9pbzW{2F5eUu}p0SQeKq&*K-Z z8Vae(2BB^dECj?9Xf=hCJ4y|m0GKg#l2Oy9@1K!xI@5q}v-z@un->Slt3jOrND{!0 zAz$1MkhkZr!A|GfyEWc(qDZI(0Kn%0`~n!DZ^2){RY9f?3>MfG0k9wtS`!d!0yy^E zTyC4{WAD#OUIai^dI5h`z?Ve;*u7i?0sdue2fP)K;5f`(1PKw^S=tBuX&<$!Eo#qh zT^3Aw_P}jHaIv_lEwslP3F9S;0Qk+vzC(|kdF5yIzqz`hXad;^upK}kkdxWkvIcOp z0wC~#Ra*g|hi9k0^AGqV4E8+vcOHF}!N6Qt1JFcxUoCh6G^zT_0>3Z)l+V(*Ovut$ zajeEVCm_8>&oa?!ztnvT0KhKLFV0K;S^#Jw7UV||NB~3#bQ*vd4ZwO&FQEdc%i7QS zk0|0&{aDT;?52&fv{hGW55k~a-qINQJwc#BK<2=MruYX;Gy;!NAH2!m=A*pUz!pO$ z>1M^x3a@3Y0etG{gF6C5@l(KW%Wv`sOTgWw8Tj_e1z;;^y`L}el4e&_eOB0AdO3gL zuz)67z%MHQE`hHV0B8V32$UMo0DQYom6wA*&9+v1=F}^{1i--$ihKp-xMf;kINPRTN>G;(dyY}Xc`t(KXL4XZ;td*vhE{Fa91e%u;><5aK@`!u+ygh*Nhz_>8ME~u=x0<0dgIS+6%X{lNUXd{FG zHH7p38`1we2dl4Zd_d!!7n+H#|nGS_ILE7Bgqst7CU zATRAh0HqyJ{5g~Gc0?Jijns*L-6P(tuNkO2|%&kY-Va+wJj6#Z47)g)GI)c0i#Z>IrhsQloMQkID>0sz2U07wfkMG~`W zNH8D(ybTajMX-1^5IZ0QtPY}q0Dpm-m7j}=UqBnt1Rq2Qs2xkv$XJ<{rJ2A0I z05lh>E(Da?Doq4Up!g6%Y6tu$O~L9vy=MRK$@XwC!MR+(u^S#%@-5ZDp`Y(H7Ov7H7`>-8CuwgYTK7dQ*zj+Ew=GBn|0 zCAC#`upj_-h~mb+2uFrqNrtv@5z(r);+;@A!W zka}rU8k>eA^KnC4x6{4q$FF?uv;N?zm$L_FwLe@B=2ms6my4m|Rvs(=iYC*bV$hWb z-suED-TNWv;A1fDJ)MQx3mR19fk5*tc8ZIGI{8AS|! zOyUz>LMQ)gI`R7EOv+0`+T4WoRt2}!M6pKo-F`(11#S)(dG#Rjarr~w*^bx#EI|FbllIm(Xama>z~UI z$TDgzMp`#<*rZDr6m)cEI|KlYX)O)M=8%G?mvCcoQzvd_w}U!N%&KT$AWgY6X{!P( zpyvXfeh^6M5E=ji0f1#yx4MQFAZsl^%z^;G-)aDqYbmUJHq@=s5dwgp3keSVt+IuA z#F$L81&4Y}TW!q6l%YUILI$_DH$s5>g(M(7@g_APiDPP9%1H5DwBkD!V& zJn>thsi&Wp@R-(}Fwr&O@29JNpM*OfBRaVNRJ4G?5zI=tP_LO|Kp(qX;J4f0C$4CI z(E?mvoj!hEb_UtBT%DBQWqMBHAMg|9$Z#gi`oXVDfF`{JS0SJ&H>a9`p_K) z!n+m(EEMKsFBBFL@Q}D3p;1k+(qg1_CqB4h)LNTzwnUFs0ML-i{%%wxRT>N6c`A^T zOX_N^#rrG%uPdJO?Dst5WiG`+K+~j&VU?U0Ah~ithuROAuo_vV)1b?a^`sxGGZTiY z)@=tCiVDbG0N*}B15g^$S^%)h$fQi3_TqriA^>$zz(17Lo{6JAE}Ex&P*^CIS@n5> z+D#Z*%Cd(=2??g&sY^mY1hndHdy0tsf?lYaccQ}D&;VKJe_ zx5@uUdmE7IU{R2S4Z!{2kNi9j;QRlg+y>08KtCB+<4L>tocCUExu49V6iTchH`wJ4 z=!?Ik;dz2Qo^r`szx0NE0JdDw0g&|4 z0k|Eh!#|!#C{HCEkV{q{BRrtHGc6^YG#nUurO0i|s==UY7Ga_OaM4E${cTPBp~N-r zDz}_tjUY%lP5K1&>4pRVO^g6&3Yc2m+jymSxwF_Z;gxnPr#c8g0X!fdYXvsQS$P37 zyM3+jd&Exc`S(cW-=lAW7pgGD_$+|!fG)NG0H2%)|Ht0pY5SYzch0M?d((b|0INUe z08pO+)dzxd+b?Q=c}y7a^DvNR6CuE0Fa{#@2XEUi#sh!Sa0vXehrAZmKVawnes2He z{(o)*;wL`O07N^W##*bYb|VzBNwZx70CoeqtOb1`kY@q{{?G`4JUq1U-1q#j!9f-37!0}>1GOpq)d6RO>1U__tH&)zRAA^@DN`0knei&$?j0+fn|tt>{Md zZGV5|{sR0W1S|}++FLMqzh6MlLqG@&178B5oFk}%{4qfMRgEC<^mhjo4@QZ*{2{M- zo|*!Xv(op$A0YrGt`B}R0!Ed-xIbGJ0DyH%h6)8f2G9+tlt$4w!s#Hag;0o%zon$b zufhamsZOlw6x~?>4%kIw1AhC-=XU_uK9FBb6tLs=nj(Rq3qkSdv!A$M^-Y;ap7n(N z1ibA_SMFzZ*6D*URwH(aE`ybv!$7PA31QtK4}OJ@wThrluBadGkyiw*3SqU3R%-A2 z+jPX?Zz=hb26tnI0D45B(ByBOF00?RHbLB^NxD95)5Hx;y0~3NSvei0{viJ?5L9yze)!z2Xw;fOp6b zIN(8VM>O|rN&UVEtA5o={nRI#0C5NeK(07%4=CPoCCq}uxHU$=M>GHgL00)t!idL` zw%rEM?pW$fKNP$4eBW@!S1vsLg`;Q8-+*2@6*lm*=V zFa(0CwnZ>BZVLfv0u}^N@8<+pz<z&SkQTn2%QVD%Gq9Q0L{9`!z2K)LPLYzG?kpY*8x+<*V+v!8cy zZ2_%lTSDzI?a0pk#%Eu5>E!J|iGH?ifV!&stmej>O6vfxct2B6UlKCVaaT{nyozfJz{<=2-0$bC zpL_Zf^`BdPb)R1Wzxyy-fypxf<%zr9qhGwsivs+#jM`(eDDF8P3 z5dbtc?Qcp|iXsRkAXxP+JlH1ihM_EiAz_z3=w+z>L*Q>!e%!6<-)aHW+|;g4xnT<3 zqB&{-K)nK9*s&c**f~Og)x9qS5CE+ZARcR>aq0Un{13d<=NEajm-lHbey!rx22cUJ zm0!73a}Ed*41mOHqTD}q?gbYsMHnDdK^+U@PTO1e+^=1C*`=HXSPSq=Nkt1dG^ZWE zR{v-L2L<{A2muN0gX5wdXy zNRbW#QVfW6NB~7TDAM~M5s)r|bV3(Ir1##d^r}DvR0M?3l_I?=9h929_^t1K>)y5A zee3S5o!RTm-h1{rb22lTv(N9`%)RgEJKSA;4u`fI(Ll2);g{Im-wSU?ah{gG4;)hn zYgdfLV2=?+j93*ybgZ~_Bdbju2-guMG)vjc+8sLB)%_G;_$B`UzIjA?5VOl-ewm+! zD;%IoFIp%5Reera9&F#9HGVRoKMU@jTR+M*7ysOP`Yo)A4~!&FXu;+-MW2tgq~4(L zH4Hx28F|;AXnyaxP$=mxrC%~!Qk0Wg&M2j24i?&}Y5X}dRws6jSy(P}UzD39GeB_M zYw~cXboXgR@OICF@xt$AORt5e?*j>$2YUm~10H4?x8(G+nmecY>n1+ZT?;mTM^P2{ zBtfW3NJ#uh&(Mft#^afl)pEt+hLQN~g-{va1}3$dedI@u@)nr1|j3(8kvim02`5a)AppYhSyjl%R-pozV?+;egLH$QXhFonCP?vtBr+ zx&{RWODJQMS5krx#|DL=KFMjb{)ClK-+u74ZkaF#t|mv%{sf%gyU7pv1cFxfj7f| z9Xr{+sv$FkHdv*_;QvX_cCX5Rw%7S_kGG<@d4THQKU=u0=)jjF z=Re3A_lOB|_hpWqCh=|2ubwFT&18GqPY_1KvEN;8tKAUewJxk@g2^oo+|>eU9564E z`z(Jc+v$oll_U;sXSM!hWbpk@uiu-C)H2Iz`3Y^pToLPS*Q>s=oM(QC6>>Xham53F zu%%z?v}JK!I~i^_Ar0)RNd1rAPrMCsv*_k=Um$-Y)y!}4oqA) zZ|kU;jNi&U~dHk=Rd`wAa9Bvc(HMdX5Wii|VZE;-I*BF23x#)mK1%_0;2ge?)3#beu$H zI*eb8+Atc)(0ORJYnS%kviddRbx^Nu7$mCoxiL1t>86>cYll@%%-1u>)#xCqEdc((OwlJ%BX6(4ky zW2)>*@p3#{6VkTmA6(G6mNx5Wvg5f){B__BLe1;~>@@qKw!^r*#UNj=RGR@z{tT8G zGKdz(7^B!vkYGSVno()#i_g`4ORsh!zzjeG(#6c2*Ex=AblMN#WI?OeYp7k=`AaETk5mYrr_*Lt)nKGoFKPEMA?lGk1DVJ`{-+^_`ZJQiO zEd@!Ws>oEoeWn+qs)wSvx%aqz|7oM~&h&9_4;pf(1s+806m>>yH&8fwNOsfUp{Ugim4COe@7lP- zs17`y|$y6xY9JoOQ1 zQ?mP*brGnT(52{f?hl74CL5STRECXz&m_;h>{*BLfo4ZATarb!5Pem$U z#lnq{z@##nme;0X1X}#tM@F@X{h;^vK#NvuK3ZkV?{UK0v!!!4?s0D3Y_0Km)D9-B z$cjZ+mJoV&zQq~n^4srtDDla*Mc~@Fgx2qk>tOLt7G?n!*kW10WvQ@rY&N_xdfHWB zNxYw;r@yzoH@lUwWaQi@w80<*4G6hV)!m*3Dw>DnaK%NDO@+vfrRbp-V~L8AHNW2r zUGKV%#Qo6q{lJPmoVjL4mXA1mXgQPOeLPFROk~^Ax8Dxo&eryDYn6Gzf?_d^04&3d zlRxx=3)Q|mfT}M8wK>f|sAvkUbQJ7HQWD9~V|%PwjNAdUGf{M{=7oUV33&}PobN}^ zau-L((IC}P9FSISq=bo^&7TfQHT1`|fc9YBxN6xBIL`MOYP!%*iDzWQ>WHKC(-bRM>L;1JZP z)Kl)TCE^T)^wbP2!E z{dFd^-93tLPrkM%`rSyUSWoo*n!OQw(+rEPXgBgB14fTuruG^w)i*^=c3Yes*q@XI zej^|u>QfeZXnoKTpII#4K-oe}Zew06~3Pfsq*EQQ2X$|1UFUy(1velx5`6khE%K(rrht>yfUt>i!8owU@ zG(w%2clay*U8G>748@2AuZl2Ju$%(N6M z^fm81{pol=Li|mI%sLpAG_^pA@L`g*FzKcPwA7qo*N|HXa-ANM!Mjb1BbcZs;7FK) z%hqkk8W&VtOhh{;x7^IoRa7gQ#awBxS3#q=WD-+{_N(NF1fdHN}u))e&Nau_$VsA`I@@7l0s}BAgV_-!mU%zSBf$|fAyQK9YFfKECjlnIb`8( zQh$LouzyDW6t+lgg7h?k+K^&gWzk52k-D13}Y>>4XrkOqDQRzQ7~r{dhsYn zCDlBWn0S7QlE~#aZQ}tYRsgv)-4yGyt###SrD?WbwmXsZTWq-PU-d;#jVJJA6F;WD zkVr~avX18l%$smxi!=bpSMFQJOLEzyB=ur>t?dHmPrL^rU!f7~U4tBN-pUudee_Q# zThmI@7+W?c59$#kt@X)egx(oGXucy6+4SpbP>eL90bGUX-VA5Vzj{ygyNO~^?9q?x zaYpO3+*c!Jo(@DoFIDY9xMr4;4e0!Wj-Ck(u+Kcb8Wb#Z@JH8J9WdquJr2(nZatMg z5M3CzN{JUJ!^`vM&vyqAfD1sm`$BA^C7_IDH=8jJ0 zT_lW#F~q!yzNay zm_h~tDIO!$YWfr$t?&yddYqR{;!G`xfYZJGp3X}S{c}X75f!(ZIvz<=78b1;kd=$L z3?$?ns~<^SEJ?3cz{$bxg-b*Te0f!S`x$ z2lto7_lpPbKEX0Prc-s`@Al#koKbA8v}HpNb8j(@-ZTMp+9FUCgi+2nTseongnQ_a z#(Yfeq}5Y>P&a7e|tF`%OpiJ8usG+v+)D1$CPlB6;K^h#MR zX27walh6o&A72?Y{dSUhp+21dgc2b3SKRqs*5vhs6LY}~P%6-XpK{o9!$W${4S8n4 z1J+o2mz@vuUg&cwyVU|QuVTKR8g0wjqDw=hTWc>)AMeh#vx8aMXEM`Yn?l$Xyoa0o_hg*{=0AQh?X2hzP3>u?)h?1rTG%;lYp-vp= zmA`}v^%TJ2vabia%i{`{Z%uq2`@?{hKXZvr*o> zw=<7rT#XD>UwP!PoUv?((;97k-(w40JfdQsH$ z<3e5o4vxhlcDCwyASwerBg^d@ln`yE>m1~$!kB*1?G|? zVW$Nm$WltXTP_VN(s(&8aQ+rg7}_RY%qC=6h%Sx9df(KmDkob@O@J;bnmc$M0giu0PnaDBETz=53l_ z|FPJ+JZ*svS2^S3Wm#kBYuKlFaRYYp!eci6EWbV>^0RSf$v*4G*~PvMh}PP`vI6$k)K)qx_l^)L6=PP5HwF7-S@=N^T&n~EbeS7*SyQ1YYSi`mXQD#Uu zeW=FHkiP%3lm`aBk$K`5XlxE=?65%MXzCWAV=U}NoA*3LwmSBviInpq=ymf&xmI#y zL8N4LP0d=fu-Z@%xos%xIj zJgzHXp3~A}O#?}?u@G(|PaMX}*w`k=YaO&}$4RTg9k^I~c(7D=!Nht-R|C#kTGrq~KL-R2;`2 zF0cQdKTEsSvG1uH%yr__y z53MG>D}G?*GnG%u;%DuH$f8#YY?xvch;;9H=}9ynOKo=Z*~G8ltPKAY<=6YhWI<0v zBU6uk#c(sGfBHmp=D!5I-y&fEI)_sQg&|t>LC&z(UmoYrs=KTty)6-hJoPLF@pqAe z#$KFhxK?C#-#$++GZNt%L#6ei4UXD^bEi{OKsB_<-2Ht8nQh79LX53>@w z%2vEtz_-BX$6(M(w(lh4#S6qB6F)3nH>~9xM8GD(KZgIFkNfqJUwDPQ`NR{KeJPcR z*3@w+Y+_KdR(irv0v8{vxk>rpW8S!!4-2bbv|3THYdc)|LD54|b>O5Sme4FFQ3;31 zh7~%sxWde=?#^ABqQ=P|va+sHR|QuCbzP3@UD{bcXd*xAo}pPXJ64<2SsXY{T}$h<#oCNQECC;wAGl0 z2PK5HEK$?~4e~ksz1^`SIM?fySqSo_Msg9?9UtFlIYMR@_(uLvjY94zU0cmz)q|ik zD>uqGzU=iotksM1^b#Alh#ojF(8eZ-ayUOL@AqlfCqOMgHmBfBJj0xAynwSWOv#K6 z3f|*sYo-&ace(Jp%HBp^s-f3kU%GUq*18j)Rzx|AxwgxzBqP$YIuf_;V(CPIO$msY zE#@|f9*ixFbw#OKB5bCN&nD-Qf82g;OW30dVixsUR@Y03W*|@hh3kg{Ysyg6QlE(( zao z=_LFTYZc3D_W=Wjv*TP&+P@fHUp){(d) zTnqPyI6FPu#Vdc-k7w)ii+AS}BZng3F0EBF*H>t(mY`;){BvThHVY#oCywn335-iK zN8PqGt_b$!+>+MtXzQvKzYPS^rV7l40?J4jS}r1;mfw1+0klge0;RYlo8fDBfzq36*9dw=` z^_VqY5i6FNsknkHbo5DV`a*WnKMi)LVjgSuRkKB|QQAq<*tD{Mus;KW(fhSh*e^fU zW+RnKyAagRgK8BG7LD9}7bD&1V;F5XUoen50Ha_aIG1Tzf2R5h$iw3vr5lNmK+5lc zK(CHK7Uf^2lxd&_tOzx9?CwdC^tHP3jL;UN&(Q!1ewP}!H2%aNiW-hnS{Y_4+e0H$ z1IXr=vgPmD8C@8E{5n2R`WyVTlOaDw!vNmgqWgne+8{8ffE3i_g}K))JU?Mt_fJ(r zKvWpbgwNTlyXTiPTwV7uQcPDC;J`eTN6{z+jp^3?S^JM}bK!4b5)=ETpQ^1KmPy>^ zub%Ty1Xs}loga3o<|2v3)||Z``)?!ZSYhHSJ2C-d4hgsRs&`p-GN!Y<{YdUBTD*V# zS;J7YTY1pZ_!@qm#!###9Ov*7Gps39SKkaWl!T~O3HkrnfAx)#i`vFo3FC{FfJlU7 zQ?zANuE)4t&F2|eaH22h+wrlc0AM<3{6t=b;c=3p*`{E4*HK@V(((CG&|Ze_sTF?P zsM+x-C@1eA_p``J4#y*l(RU=r`sH3optj?QZOD=s33l*&&nTtwV+9zksLyj&&n{Pi z#~FgEklXlyg3&_5E56PIU-z>`halfkj+IAtj&jMkKG&zLi>lMX`;U4#cG8=7B?nog z?5auRKY&_qg5v8psUF?lS05V$wuGZ35TFqGcNL0zASlYC*u z8eV#M$jiflyIXDo5n#@Fb1fb#ACLy&Lc{VW^YW}Vq^_O5hJ}lDe^%?iT6jL!nZ&Kte$h|mpkgA>EmhneAjhMYS z$+!sch&owt%6jyV&dzsY$Q%Gc^T2?vZ|x4CG3aH0f<4eNz>EStJe1LTEmn@UHk`Og z|LKGnNgoCPm%b!KFT#Oj19?ddl|k}rqFi%OaO?TB&mi|gN;yX7LN>o@fj(v-RC#A; zn~G@hWgIs&W(vCRks^I3q~23z`wk%i4EXTf5oCJy{i2qgWyh59v;-uq0g-NduJ^*u zq2XqFuFCoRp3qj@KsNA0zVi3)+rcVubWqWoi0*_{v?3!SJwYcI`CSNEh0FE55?Bju z0wh3=&>#2=aBIl1>YrQU-Qsio96PNn95B9T|o92e8wmRvtV zuJ2b)GY08}nS^3p%YhocuF&_@$Cg(jB zFNw;7GpRGU3WSDUerLIx;%3x~&JrKn`*o4j#j)`&0MwS}PyI4b6|`1a`JU>el6t>J z*+5AiUn3%uV?p8(;Hcq3!vfeYi9BSMY60FgF&IxosRu@Kn%vpz9c{?b8+HIPdc9p| z6~|j=tH09#PGyRm@Dq*0St|dL7dN-QATPT3FG*N11a$5_*d|c-f;eBkzgMH2`{pdj z8FF29iYPy@;N0KtTKvx(aJs^x>dxyr0jr znt0#-L|6LqpRb&{hYXujHE#;{JzvqAhNGaN*0qhHV4Mz8VC&S1NJ;%cQzZ9v%>MB$ z3digK%6u`iRcgI!BUV5F;ho>h_lKX~2BBwslAMKrF`VJP;Fma=^*kcHD)Vv&5&D7k z_1ozFZ1v<2!ltNYIwpj^4I znEO2`$%BPE*qXQ8(C0Dv{)^sDWCuV0w4Z;e1lJvHZG8*yV-d-p1i;(Xp#{eZC>wG; z{1!5=Z}&||aNC+A$5pgYr{{Ddbr3?sO7LmL9c~JKCxsOokX|cTk*8kTx;iGl4y(sB z1LE!hqiSq_UH0DwV|4M3@;+d!J)l`cwimo*JE?k3v2S}|PhLn=z}Jpn*z|vKVrw=O z2SX^tf|ML4KBs~yXUV!Z#>&1>NPAlx)LZ#74~>w*RvOQu&a z#=z9zmoUSin&*18&tbGBZ@l(Da4msg={)g;u!0WBcqG$oEwUJ)m1M8`mo& zIBO3nKgLmpxP@##DeqS04e8$)kYHR(iw2Nb#cSwz@R(01|kw6E_*I*s(DLT@yv? zh2tOGnbkw-6`7zxeAgkZar;Z4@Rgt`3M75n0ATUq*DCeG!OEy~RpP~VF#Gc_vmvN) zOg?!@3P~FTTkUh8l2m#N&{!D=X3@l(mpfbn&e|qNgANL^?=~FOy3}|h3iKpE5rWy1 zdE-}tXwROJ2t;{i$PFdOSJwMe70MDL_wqs6_#NF@qghoe`CWyM-Rpub_UT7#BB(Gp z&-|1&10d1}S?xM!o#Ys(y|#8=>!?a>wH~0R@Qe$(aQpF|W4$FLpX4;(jpg_0b>eam zk_F&K6GvVqXB`8hDwncvf>MYFuJ+Ov{+#GOtSIkz5LoaXRww(r6`%oP3de>gR0%gy z!vAlS9IWk+;zh4+XUsb;91U$=^yYWC1tTdf(A7P1ZZ#PY_AZp;1ysqaqJM_mw6y*_ z+FvoK+ng!tpDevdej0n zxesS?ee)dMwH^1nFEU!`ZSt8QmNW&$xi7;DLGCcj_FiWrWm^ENwl3+@@3%PcSOt#^ z!z(I;X{L{WU4(@qj$<4UBe7vxRq;hkBX531Lj<`kEp$tV!c1k ztq)$sNo^>2BXu&GXjo=B_D<>cVj=jk){yd9S8AcDxbNTKggi$xfn78yX_!s)o*NxJ zi_Uhh%2BoZ#aFXGgBDuGjbqLr!xHv)YLA}OqzX8}NruS-n4Uz~R#NCMK3Z?8Cn9l8 zx#viy-^|%lr_PZXRC;-86aaSP%kv6upb%(XFTT7mFTQ?Qusf?YX&%ZRw5z*&OLY3p z?TWF|77L3jjVQStt<2SuY_`T>_%COWgmlVT2*>q-m^2Yo3#gqeoJ4Y1+qdJCddw5DaJlW^Ph4Qzb!mYSmruVOfX1>8J505- zjXY%^H9h(|ooa*Me`8iRz(RmRVb@9m6+ zCn`QwSQPHmET$ydN%w|&Ls4=pALqqw2A?CCR~XRtcc{X8+@p0f zu@q!sQMrH018;t|yBI>19Npzw*s>DKmKM6T_^<=GoeuycnzIn_J!W$PQsGqr?pSE8 zcuuk3@Ny2Ki_-XLU)9q}eM!$tRIx#Y+!MQ4>(jeYTGFO(l-J_5vfCOWqXaZn1%!hf zlL#}_R_DZ+LvlpWs)Y{%Z2~YXoSw9CYsE;azCMb33ZHIR9D**<-MUiTGZXeUiU-KH|#yuDt@AUWhW7dn{aAg21-W@_bEfiGh z2nAdUuC{`qoEschp6CLz!qpOT=k0J9T4Wx+IUX!<1s=k}(W$CDYWO1N686o^FufjE zCs^scSP~6FZIz;^M*yMAl#v`wrqc5mU3?4JhlFBl;1Ijk0=9}bzlR!%KeWW^`2P!$ zJ9rG%=3*ikYf`dLaxbXnoP~|tgl! zAjic@vT6B_?dZC8p1$|C<`wq1f=y^{OJydh;atK_MG)Dvr!!wLp|Gl>IN*4}?Uvyw z=emNo?q|Er3}PZY&nuY`lG|M|o3?c7QV zm2{aAf9Oiq(n8@Vl@d=CYy~|2r7Q(GKY=PQn`SnQyUx%HZN%9?t%ukey`k3huv&BZFB!SA!(@3T69AOa4y=|5t1@$@ZR=bcm&1NVW;2Mk*7$qpPNQ4^9d zGl>|Y;8~6%g14EmAXK6NU1R4gn3Pw`2}}S$^3T^$!x5&`0$UF<071Kl#5=&{mf5GU zroPcU$yoP8J~xN+@$;3)meliuRtcBT(i|*T)gs$C2k6Gr&l5ELH)Y&8nK%<^rO1gX zl3MAd7~PK@0f^l%6|)yT`Kxb8R)_;=fCnETsc0~@wel-BPyClz()^~74g%yi3;EL4 zV0BAUq+)oBYmaHxx#`SrDk&H8k2$y&S0bT*=uG2p&%tmzaCC?|Wzi5d6k*83{;BtM z#L`7F!WlF7aIPMt?a-rNnwVJ+i9;(Y=Ub>xqq(jM;$O2IPT+{u^ad#19a(4Y%P}_b zxaj(1-P%E><{Qz;a-sc+PuU}ug5|*0KIYeFz-1ta2MxF&+$9YGN9PBEuwZIx5T-x` z*y(XiQ}?ala3@}zP?Fz?Hbhj-(*S{)&&*<2HNOXD{uZeG$#0Y6xbc>3&ZC++gbh^p zL@g3x8Yv6w0Wi>050wiPDN_R}L>isOa3`O{x)fFL+)PX`OdJxh{RU9WD(o!&dgXJN zbW39Qw4h$Fv81g&T~Qc2qHiaL&v)Rnd+r1 zXOoM;X(_p{|19T#tM<%Y=^fO|duqjBi>c~V%=YX4TRi=G9Ti4WH z)2f}+bX9lyC5n~KR9-IskF@ij?wnRotN&f%0jG)0J{U^mBik%k&@u5E3#KVw*Lbrf zCrD~X_u^b)Cuef1$+x^S+DUNN#i^TY_Do6|EXRUbtaEl-ym1GDg6bharU%+MdoS{D zD(VSs>f1Y>CC&~rFm37GE-ul7Kq*JMjes3v8S9M&F}4skip6xwz4HhuA*U<^Cn?`s zFMby^orcfG4_D5UnVA;;U2KzErGoZshvbbV@76nOyI7t#m^eWCkcq98ULn5ugfisS z=^g85`P7q7eu)05=?t8m9GQJ6gGai@?W+s7aTNgsQopBj_pP#Zr~pBxCNL*~LI3(D z2v7q5?}><@3i>zymH$@+0)r?RS+1E$Xhta3Z$9^53$*`~E*p{vHbqp&{bD zQ+sdk-hwHfTb)0KOYO$Xd-JCFzhzKb80TLd0{_+hZ$!&Prz$Kf5==o)`H-re?ym`kixQ}=)b!E)fhwsrbh(IVzovr@amsT4w!fE;DI9! zJn;M9e87(9Se3tdJfLc|rT(ug$;h|JH|z^ft8z@f&75;7x@GY$@=71A*Nr z@qjCVt;z7fQ^GcY3J*_UIfXJ2>kw>M2n;5C$mO3Zw<=ZY{)CV6-~j^g3oyotCyTj` zC&TjLi3_je8Hxn(z&BPrfMvncVTJLUIfhFLjw)@0RwO4d1?h99ZFmDA)?tE95h3;+ zHav3(Kc0C&3{P4}sC&z8JTOg&y^2t`A%fppErxJ-bA(6@$h0cJWq!d zo_kCd&wYx-bN@l&dD^7#>>nlYpi~AtWdqLzH7Bh(;kf@H2pN=<*9Wl$&%chRf6a?` z8uY=t9fjbXkAm>dhe7yfyJ2{fMmsztjvh}{#Erk{EAjKM&3_#HjYAfeMri9TkU!OD z$v(BKkd-)m_HG$I?N<@r<3})_y+!N{a3HS%o>FWBG6dcK68pdWKtYrMIhY54fmncG zsvS@vBc3Xodxt!e<0p_$hllvlZGb=^YJv_g@K^VLA`%cA=pp%6z=iT0U;`@y*C9%P mE7dvp5ydb-3TFSG%>G+B7$63y!D56MiT=fZ<-cYB5BfjfDg4s_ literal 0 HcmV?d00001 diff --git a/building/win64ARMv8/squeak.cog.spur.livetyping/Makefile b/building/win64ARMv8/squeak.cog.spur.livetyping/Makefile new file mode 100644 index 0000000000..6f6a2bca76 --- /dev/null +++ b/building/win64ARMv8/squeak.cog.spur.livetyping/Makefile @@ -0,0 +1,22 @@ +#################################################################################### +# Makefile for Win64ARMv8 Cog Spur SqueakLiveTyping VM using LLVM/Clang +# Do make init to allow make -n to function. +#################################################################################### + +VM:=SqueakLiveTyping +VMSRCDIR:=../../../src/spur64.cog.livetyping + +# This variable is set by VS Native Tools Command Prompts +ifeq ($(VSCMD_ARG_HOST_ARCH),) + ifeq ($(MSYSTEM),MSYS) + # In a generic MSYS2 environment, compile against WinSDK + include ../common/Makefile.WinSDK + else + # In other envs (e.g. CLANGARM64), compile against MinGW + include ../common/Makefile + endif +else + # Legacy. All *.msvc.* makefiles should be removed at some point. + include ../common/Makefile.msvc +# include ../common/Makefile.WinSDK +endif diff --git a/building/win64ARMv8/squeak.cog.spur.livetyping/SqueakLiveTyping.def.in b/building/win64ARMv8/squeak.cog.spur.livetyping/SqueakLiveTyping.def.in new file mode 100644 index 0000000000..070ebef801 --- /dev/null +++ b/building/win64ARMv8/squeak.cog.spur.livetyping/SqueakLiveTyping.def.in @@ -0,0 +1,3 @@ +; Set the total stack size to 4 megabytes (0x400000), reserving 4Mb and +; committing 64k (0x10000) +STACKSIZE 0x400000,0x10000 diff --git a/building/win64ARMv8/squeak.cog.spur.livetyping/SqueakLiveTyping.exe.manifest b/building/win64ARMv8/squeak.cog.spur.livetyping/SqueakLiveTyping.exe.manifest new file mode 100644 index 0000000000..ba6186b524 --- /dev/null +++ b/building/win64ARMv8/squeak.cog.spur.livetyping/SqueakLiveTyping.exe.manifest @@ -0,0 +1,43 @@ + + + +SqueakLiveTyping Smalltalk Virtual Machine + + + true/pm + PerMonitor + + + + + + + + + + + + + + + + + + + + diff --git a/building/win64ARMv8/squeak.cog.spur.livetyping/SqueakLiveTyping.rc b/building/win64ARMv8/squeak.cog.spur.livetyping/SqueakLiveTyping.rc new file mode 100644 index 0000000000..3c1bcc6777 --- /dev/null +++ b/building/win64ARMv8/squeak.cog.spur.livetyping/SqueakLiveTyping.rc @@ -0,0 +1,32 @@ +#ifdef _WIN32 +1 ICON DISCARDABLE "GreenCogSqueak.ico" +2 ICON DISCARDABLE "squeak2.ico" +3 ICON DISCARDABLE "squeak3.ico" + +1 VERSIONINFO + FILEVERSION FILEVERSIONVALUES + PRODUCTVERSION 5,0,0,0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0xaL + FILEOS 0x10001L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" // Lang=US English, CharSet=Windows Multilin + BEGIN + VALUE "CompanyName", "Squeak.org\0" + VALUE "FileDescription", "SqueakLiveTyping Cog Spur Virtual Machine\0" + VALUE "FileVersion", FILEVERSIONSTRING + VALUE "LegalCopyright", "Copyright \251 Squeak.org 1996-2014\0" + VALUE "ProductName", "SqueakLiveTyping Cog Spur\0" + VALUE "ProductVersion", "5.0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END +#endif diff --git a/building/win64ARMv8/squeak.cog.spur.livetyping/mvm b/building/win64ARMv8/squeak.cog.spur.livetyping/mvm new file mode 100755 index 0000000000..0800b5acbc --- /dev/null +++ b/building/win64ARMv8/squeak.cog.spur.livetyping/mvm @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +set -e +# A=all(a,d,f) T=Threaded a=assert d=debug f=fast +A=;D=;F=;T= +if [ $# = 0 ]; then + A=1;D=1;F=1 +else + while getopts 'ASTadf?' opt "$@"; do + case $opt in + A) A=1;D=1;F=1;; + S) echo -S not yet implemented\; use -A for now 1>&1; exit 1;; + T) T=1;; + a) A=1;; + d) D=1;; + f) F=1;; + *) echo usage $0 [-A] [-a\ [-d] [-f]; exit 0;; + esac + done + if [ "$1" = -- ]; then + if [ "$A$D$F" = "" ]; then + A=1;D=1;F=1 + fi + fi + shift `expr $OPTIND - 1` # drop options, pass other args to make +fi +if ../../../scripts/checkSCCSversion ; then exit 1; fi +if [ -z "$T" ]; then # single-threaded VM + if [ -n "$D" ]; then + rm -rf builddbg/vm/*.exe + make $@ debug 2>&1 | tee LOGD ; test ${PIPESTATUS[0]} -eq 0 + fi + if [ -n "$A" ]; then + rm -rf buildast/vm/*.exe + make $@ assert 2>&1 | tee LOGA ; test ${PIPESTATUS[0]} -eq 0 + fi + if [ -n "$F" ]; then + rm -rf build/vm/*.exe + make $@ product 2>&1 | tee LOGF ; test ${PIPESTATUS[0]} -eq 0 + fi +else # multi-threaded VM + if [ -n "$D" ]; then + rm -rf buildmtdbg/vm/*.exe + make $@ debugmt 2>&1 | tee LOGTD ; test ${PIPESTATUS[0]} -eq 0 + fi + if [ -n "$A" ]; then + rm -rf buildmtast/vm/*.exe + make $@ assertmt 2>&1 | tee LOGTA ; test ${PIPESTATUS[0]} -eq 0 + fi + if [ -n "$F" ]; then + rm -rf buildmt/vm/*.exe + make $@ productmt 2>&1 | tee LOGTF ; test ${PIPESTATUS[0]} -eq 0 + fi +fi diff --git a/building/win64ARMv8/squeak.cog.spur.livetyping/plugins.ext b/building/win64ARMv8/squeak.cog.spur.livetyping/plugins.ext new file mode 100644 index 0000000000..f3989042b4 --- /dev/null +++ b/building/win64ARMv8/squeak.cog.spur.livetyping/plugins.ext @@ -0,0 +1,14 @@ +# Copied, perhaps edited, from ../../../src/examplePlugins.ext +EXTERNAL_PLUGINS = \ +B3DAcceleratorPlugin \ +CameraPlugin \ +FileAttributesPlugin \ +Squeak3D \ +SqueakFFIPrims \ +SqueakSSL \ +UUIDPlugin \ +Win32OSProcessPlugin \ +DESPlugin \ +MD5Plugin \ +SHA2Plugin \ +VectorEnginePlugin diff --git a/building/win64ARMv8/squeak.cog.spur.livetyping/plugins.int b/building/win64ARMv8/squeak.cog.spur.livetyping/plugins.int new file mode 100644 index 0000000000..31597b9c30 --- /dev/null +++ b/building/win64ARMv8/squeak.cog.spur.livetyping/plugins.int @@ -0,0 +1,39 @@ +# Copied, perhaps edited, from ../../../src/examplePlugins.int +INTERNAL_PLUGINS = \ +ADPCMCodecPlugin \ +AsynchFilePlugin \ +B2DPlugin \ +BitBltPlugin \ +BMPReadWriterPlugin \ +ClipboardExtendedPlugin \ +CroquetPlugin \ +ZipPlugin \ +DropPlugin \ +DSAPrims \ +FFTPlugin \ +FilePlugin \ +FileDialogPlugin \ +Float64ArrayPlugin \ +FloatArrayPlugin \ +FloatMathPlugin \ +HostWindowPlugin \ +IA32ABI \ +JoystickTabletPlugin \ +JPEGReaderPlugin \ +JPEGReadWriter2Plugin \ +Klatt \ +LargeIntegers \ +LocalePlugin \ +Matrix2x3Plugin \ +MIDIPlugin \ +MiscPrimitivePlugin \ +Mpeg3Plugin \ +RePlugin \ +SecurityPlugin \ +SerialPlugin \ +SocketPlugin \ +SoundCodecPrims \ +SoundGenerationPlugin \ +SoundPlugin \ +StarSqueakPlugin \ +SurfacePlugin diff --git a/building/win64ARMv8/squeak.cog.spur.livetyping/squeak.ico b/building/win64ARMv8/squeak.cog.spur.livetyping/squeak.ico new file mode 100644 index 0000000000000000000000000000000000000000..cf96e91964f62b5df614b9b307b740e375553041 GIT binary patch literal 1078 zcmc&yF>b>!45ZTn_7vp_o{Bu9Px2OT)@E(`lAqI2o3*ibloS|&ffndc+7ypGij-`M zbaIh#yb0y&BJu%yyA}W5iCo`A?ud=hPiQCaSVcT?&&7iT<9#t$0cG1ZSQ2Wls1iZ( zFhu_+Nyh}I?zlxXB?PRJ8F+XhC1f~>xTad^%F_e&+%LBG*zg>)Zs}8a7AQrkk3&m4 zIC6=p7CP};J@S^*+Oki>w7>9?^6W!!<}+qVm4ESTOGFF*>RY;z##frH0F!lOg9;D7 z_y8>d3%~w<3g?`YKSJA_Z@}l`G2>t`(`K*&+KhA7F#`5-8hA0TydkgI?i(Uep4X|* z%k)_D#3B;&qu03h3%ID3lbns%l7=+on4LWfSnhYl>asI`(6Fml4YUCtK8D>I%|U18 K?-9rOlY9fXY&i!2 literal 0 HcmV?d00001 diff --git a/building/win64x64/squeak.cog.spur.livetyping/Croquet.def.in b/building/win64x64/squeak.cog.spur.livetyping/Croquet.def.in new file mode 100644 index 0000000000..070ebef801 --- /dev/null +++ b/building/win64x64/squeak.cog.spur.livetyping/Croquet.def.in @@ -0,0 +1,3 @@ +; Set the total stack size to 4 megabytes (0x400000), reserving 4Mb and +; committing 64k (0x10000) +STACKSIZE 0x400000,0x10000 diff --git a/building/win64x64/squeak.cog.spur.livetyping/Croquet.exe.manifest b/building/win64x64/squeak.cog.spur.livetyping/Croquet.exe.manifest new file mode 100644 index 0000000000..d5d38efd1c --- /dev/null +++ b/building/win64x64/squeak.cog.spur.livetyping/Croquet.exe.manifest @@ -0,0 +1,43 @@ + + + +Croquet Smalltalk Virtual Machine + + + true/pm + PerMonitor + + + + + + + + + + + + + + + + + + + + diff --git a/building/win64x64/squeak.cog.spur.livetyping/Croquet.ico b/building/win64x64/squeak.cog.spur.livetyping/Croquet.ico new file mode 100644 index 0000000000000000000000000000000000000000..2063d2205a2b8a2bcc32d1a36ba31872121ac77d GIT binary patch literal 1406 zcmeH{F-Tic6o$X^UgEpYyl3LeyRk{bGbU-t=qv~wopp0@lkP5VF0L-(=o%=57Eu(W z)bdrnV3Va#JX zoiEJKKjZxTJJZt#EHA%ccJ?Xj>#y0_`N-B*_m<8f7&PJjnv0i^CbmgH`AJP9V{4%l*ml887-BvMRVk$Rx89OvC! z*+V2EV`}}c$ttfJrE|mzQrUFgh9VY{ky^=9N}LkOIOEiGSzRQ{Xe#z7=qO^Okaz_5 p#*`V1PFTDoB%>6?*f6w_6cIBJOq_9*GeuR^{)vi>B9;0t;x||CQV{?E literal 0 HcmV?d00001 diff --git a/building/win64x64/squeak.cog.spur.livetyping/Croquet.rc b/building/win64x64/squeak.cog.spur.livetyping/Croquet.rc new file mode 100644 index 0000000000..fc7e089542 --- /dev/null +++ b/building/win64x64/squeak.cog.spur.livetyping/Croquet.rc @@ -0,0 +1,32 @@ +#ifdef _WIN32 +1 ICON DISCARDABLE "Croquet.ico" +2 ICON DISCARDABLE "Croquet.ico" +3 ICON DISCARDABLE "Croquet.ico" + +1 VERSIONINFO + FILEVERSION FILEVERSIONVALUES + PRODUCTVERSION 5,0,0,0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0xaL + FILEOS 0x10001L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" // Lang=US English, CharSet=Windows Multilin + BEGIN + VALUE "CompanyName", "Squeak.org\0" + VALUE "FileDescription", "Croquet Cog Spur Virtual Machine\0" + VALUE "FileVersion", FILEVERSIONSTRING + VALUE "LegalCopyright", "Copyright \251 Squeak.org 1996-2014\0" + VALUE "ProductName", "Croquet Cog Spur\0" + VALUE "ProductVersion", "5.0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END +#endif diff --git a/building/win64x64/squeak.cog.spur.livetyping/GreenCogSqueak.ico b/building/win64x64/squeak.cog.spur.livetyping/GreenCogSqueak.ico new file mode 100644 index 0000000000000000000000000000000000000000..f6d03cf9fa70a6531d7be789b8fbb849128e1655 GIT binary patch literal 69927 zcmd?PcU)FGx(2#FdI#yfs0fG@0i|~om8PJeAWfu7mm(cOK|~O#B1HuSMQMT{y(oep zO$4a|(tAf~<|gdz%$}J&bLPys=iEPTe!u5i>q~o|yh+|9Zx)8(FanI58-q`7jGqR> zj$;@mD*F5X{TSv5<4{29w%r&OFM(k+Fm5+L0fqqz9L5jRxgnGwQ2cKAz1(kq{^MU9 zED5E+YX2JkEbgzV|MNFgKM4dQgdl`41Qo=ufZkCYN<(>2-aqn#fB)lv2TQU-C_xxA zurUS+$OzuU!^b-a|L+F$j^a=nj4*~!fte+iFjtBtRc|G8; z82}tM0)hP8SwJ}?0I+U$fa~M{cn_EcU>Ru$HV8>n7b7Q=8+_-4Kr;SW*h54{Y~cFb zYa*m3a^axdp#(BYayL~!jT*V0GaKZ?d;lBe1h^?~Ks)BRT%UPOpt@n)o_`{o5QpI;e+U#m$q7hD4gi^{Gr(!cf7A4V z^=Hqn8$0KlZNSBMc0kTv^&OG{<^Nef;87i=3y^rG2AFdX0gBrFV1K6^5bak7%;VyK zV0!OgWv}o6Z0!$Nfn1g#=C}cFnhQ|Q2?C*J6>xmm2psyN0w~J%0p_ejK;o$ypgKYq z0OkMLyujf!$>XpG^Z?6Y1XwbBwzC23IRn7#C;`?5#}#VxDwO-{xd}OK!2eUQ?IWB3 zgR-%H4uFNz0?dXI;QC-(Jgny-#0ywwJRQKDgfjlw7=X&blt^5$EXF15CamKuIe_nA zm(yj)!JQg{24FrAqfk~gE5JG+uh+}~Gll6Wj|GI^p5Mw@!*_%?F#R#ZJf=W$1xom{ z@rMsNp}B^@jps*VBrIgkhYA%oNCQ{^W(DiAg>_j& z7{R)cKD5XHW&+>ez;wg~@ycNWm=krU4EEh1EdLqW#CkSBSj+(^GPnVC3^yS2WdS%=g0sKmW6bzEm>cZl+i=`IhGVW6 z*4qf>qw;9%p>d9E4w~oHEPz~}0g#qKU3`cC(R@sS9PdGn$OfS~h@NQ7z`WQe$OV;0 zb2<^`35H|Q38pbYnZE)PzB1-OwSxuk*};;a{8w;1y@zGsT7#LucJxCYXpYZAdEems z62uQA`*&0s zi$Xn!VydL>ST4-_7RoP! z8L+(pP@mm!ETFo=XxlM%0x{TbQH%}($^5fGi4(^j&@E#%Q1&xeha(lho8|%3%R+!A zk_XTv?gKQNB7kNUV(ky;6$CUE>|oC*T#sgWp-qBqxB|Ji!u~IZ@=udKhT{IXXZw5U zXivExhr<~%2Jin$TmjpR><^UBu^v6LTCrhs(WdFR?a>Blj0?u z*O^fM88V<)ssqm4JP)*c&4JGH<=?Ps33Pq|#Vz~>?S%8-bk;>c8wUHq0m?6C0j$r3 z!1*^Yzwf!vw)LbejN>mlTnifj#xhif)b91EcGWShVvz!U!)QGZwG-=zH)fAF}7%t@{> z0X#n_CkV<7gYqq@-eL!dPGY=-DwsKC`EDFc!}p^Hls@c$fSbhpzo_=#Pv#}G$1ajD z!|?zxdk7cUZa%`l*3U)L2)SU&u!G76!<@iLY&av73m)JJ=#B_^Y2UQ`B^{r@5$9TGzz z9_Z{T0-*|V8bTR@9paDN&^wAlX($iMi^`y~s18)u|5d*rU7|e-F9hO))=hH=Ct7CO z5O#i!>+tioapkKyof^gOBu zZW={h>bTt$vmQV42;3fy2i|?5z`EB1u&Qu}K`sX%&^k#6v7c3$>y}loJMiue-OU^S z4VGz)Sx0rCx=@`k-~UGV2c6&ua8!cv(tW2tngiojdvLDR5-7Kv1+r~A;Mjm6IDA{a z0TR)LI0bP6LPs=AuKrm61t8n11C*Q3f^#jFz_{5SoNh7)f^pLQs7{#g@54XH{`(C7 z;y)^jYy{FDHN@ZSO>st)QKVHoU{5VS*z-XEkbZ>w?@tGT=vQ^1{n2!pgo^Y!K0dw& zEO(WVoXAhJ$#_|`?>L}og?T^h1vGX1fTofkke2fRoFV01nD37P$wD%bY{cO|X$P_i zdm+%+l!ZXH0?k8IH#uCVjj#%)9qc_kn|y@lfKP0I_{%=PJE#E8PS}9UB~BwjAMQ-L zzxE%z_|m#xf5H|V_^JqqyP&VB4W0oT;W^|TEL+aJ1MAX;bt3!oOJ_(Hl8Iy^4v5Qt zCm?&r0)6Rj_NUV+enI0S*}Lq0qs;&k>Af-i7C{{^0<`ZxgoAUB{N69p2}TEKYK z6}YX30M8${fZ3WGI6A2f=!ZoCejhx8A)gWQ8K5)JL&jlfX^|@*aYpk+00POn^1JL~ z@EvjaqdkUy|7k!vpn;I2V`uOVuZ~`_x^tzKVh^Pcj5C5jzP`)M66|qe6g~(0>&H zY!L_BV0|_a$Y!CrVt`~>-M-QqUU_c`ae#4>5Qx*iGmgk$zC-u~_{IizOnM@|B!Kv# zN5H=#WQO+$Um_7Pk&l+IUbV|;;P#0LW57Oq6cBuaXEJ#9-93w=^EUE3!ZSDS7m$wt z`T%#&%gFx%eWgT0`@r5oS)e&$_ERg-;1eZ1RUnjW&wG^bzE4^3O#I+O5Yd?c5C_Bs zae_eH{#8I@n*-vc#6{_1n>sge;jJ|=`e+9{25*4i?#K1 zZuP}%dGrSZqXy`!t+oa>Z{2`|k#sTQ^gFk|u_G|1cD_3?{9U`z$jOdJ9PQ&9J_kR$SMAkN6|vi^7Yo-sbaGbRhvrVN4gqSx+O^53(TtEH?z= z_|F0!9u59sj(~$>d($KUeI_3u%7gQ=k{ysY@&bm>B7nK`Fxb;A2#BZQScZE1%a}oW zNBmG5kRKTV@kaRNM@H!=A8If1`%J+3GRF((J_rJydk3Uxqb614d|OiKH4VO2Bn z$x5LeNA?ieP?U~*nrMt5e$an~ZT-#_`YSMhc*aEBQsMh8$mtZUSCHs8e-J8zd{de* z?huSazOq~>8~MAg?-_#UK4eq>{XmPy56_O3SUJnjUHv2fG9ToO{2$17hJ1i-@Qket z{oAr60F!}`gFwD#G?!62@^e3i^3j+yRQXl?jc0oheFuFx8R<~Q;qF=2ZN9I*_BZ5;9!BLDPDsFTxBwh+|MN!T9b z8%I8PSHPGUtz4b3%h#PuGT>rQhAiik-ht^iKZ{&xNp>^UaruKmMW_(Zv(le5g z1>2Af5d+5ox?e!t&>aVAmlVv0bcpiM;{BDs0Zc>vCjiqHj~0z3!#S6Z`z zpH%>5IkYv%--UQ0T}eQm((ow{q44_?y=z1Hs7=V1jK%=sg!sYrYIpvlyBFlg*wsC3 zhbn{`1Zq3F4~c+z;aDPS;{j})&}P@4+$QIs41#>ny1&Z{J`qR66>*NIoFOpD3hp_{F)8mOBD1ih!(x7hqo@2Q+ub;S-G|bpL?v0+3xoa~F;IEi_*t z(A~$c=k7b4!^n0)pa1TBMm7rBSu`&Y(E5hfK4g#KJsII=9>CQq33R@g0~veeDqeBE z1Of4bN&Ju-;)u9H;(y$4lS-IKX9T_r12Jt$;BIFkxY`~7)IR6~)(?jPK?|%G*%)*O zhjjZ#xx06EP(HHf#&BF5#Nhaa;{x$VK6CU$HVVxL0+<#C`vl#0Aik&!+E1YM0Nw4N z`v52huZ;uHH5~#nOlbi{40>Mo!qrE9f<6gjCCk|xZ zX#u_3OTe_!9vBr`gA-5B0ygcvPob>8;ZLMO^_2P+-2cY%0{X`X0M(0qfcQ1MGpdI5 zAsdIr3(`O0%?sxtibLEH2gKhRj$g>X*4%F;{@?PK?2C|NWt{jG_VS` zgKdL+(S8H<0n$5K1JGR?(#`L-8CTB^h$=V%MZrEm`|JQ{4eWY z0>_;-VP04cjURLmjBGz44oHeZKl@Gie-ZLTZAW7Vl}C4&zHrQq!2f8DG(&mlo(`@1 z=$#$Pc;xSAh{<3`=47vD=4LP%g6bXitUYAe({4 zF51U0LjS=y+&3O12Dlf@00;M&xV7Jb?jX_oDg?The9H!Kd*S-p1J9aEunoU#5Yhpf zQ}>~sk&Q#+2k|e2{Ere}`md<|NjeUWE=IV%f7#`a#x9yusD8B0qHi+bS(kK@4^XTL z0tz2CKw-cLD1N~Am4m;MNG?p&>K8Mj- zh}Ldoj~+3s!F`At+Y$TM6XtOtFkfpmiG{t38880FZ# zPvw}G2JG*}0lO48VE=psu&=}aD-f%H0Q$cy1=trMmLR?!1?+M#|0_|zF(U=&hJ=5! zo9G)3bdQ4c1lK!cyNRzdbU+@+4<_+DC&cYv1vICbAduh1h~@~xL-st8pTvbw-{`Ix z?b+2x0O=qv5S=&yM883tKMF)2DFV?S8bEaU6cAm3&$ZvN{`+$k#xFrEK%n;=Wgw~} z0Ytmi!Qru!fMSRr?wjEr2(2OYth?V7a2AV!qlv0h^t_D7w}sB{h!f)Ww{{0c{2`#T z;CQe$IKBkm7a`Dd z1!5Iq?GHfzmmyG|`HR2hi#!dEyJ&)w1JH*tA_?s$oLASNeSzbiFqa*0Ws3o=5+iV> z(ESJe#xEanLfjw_$A1>^2`P!h#4pQKo8{O6trv!XFI^Ilr$PNATZ-&~5I$fv-v`XE zTLP2Ymx0NIGccKR`3;M%z+@R>Uyg(vd6mmt%$L7=PaiTI4fx;6Lf()o(h1Ug6x}wwpZ{$SgZ3!sTQM}JwILV8>Aw>g z@D<@XZ+*8sI_IMOl_5m|JbNeX&f|0_GYgKTB6gtKd=7+uivihluR+;TEvQ_s+eO97 zyT81bExiS~^Ti;1Gy$A!GX$ii(B>oV&tW~NE^Dd+jGI6f?&aR>p4$+2bjCM?91xfP zB=8e@?tWW$iSh^Bb6kg~=HKk^5n@xgpPJm2i{?ZQ>>IdOWUUbe@*htF(=I3A*?$8B ze7ys%_1^@Sx?F%_vo7FxD+X{S(2l)=`Lm(EQQ0{96?m?{4M)arK5{&0Uyv*_C=;Dm z`5=e?G@!b~h+{EU0_1!A+dzwV3hL?G?)M3(@1G(*aC~LKetH4>J`3W-@9)py`!fWn zZ?qpn`c8rQkxekCXo3|BK>Tq(3_a%fC*hth7LNIUWiS7h)4%_34~_&+htP|DkuVy5 z{}2axpgkhugyzR1$PdjCbS6MR?`S{%i!-t>(56vDa{}rB&SgAu0<^~d@xJfhXY$|A ziS{8>L6BQzd(iLvsxItlZ!et)0kDq4R?! z8^z`pyM?KlkhVSF~?ahjpPk|1Sgju7nj|1KNrtxK<49 zwgsInk^hnw@8~~JL2;&V9YTE3^E51T6qZGG{D?z8@XAHq&+t5cG(qOnJL@`M_+V`vKDFcM^SFSC zYq;48()(5?&rSG=4}N@EQC9jw(x$Y>{wNc(@|kw?i>KcytI-e~yJ5R$NpHBFg}%-u zYIx5XWoLT&)AUj22yV10@8N2Xud!6PNJLX=`zR^oYuS2Me~&O2rgKU7Ua8mA)HJ|ozgDeJ-7oE9w%Ov@ zjWUXa@xHc)Tz)EF##Qz0Y|21INMMfa>bj%hNCLlLsr5lg-tM(-N86W`8OMl=@oLY1 z@4rW8BpWE(W;3K&R^?fjm|?{y(Nd#i?`HVCgMmI|I&J=W$tS$xC#0B9r6+F`hwsW9 zS7GnZQt^ah53G1(vhQ(L2et9Ne7!Lb#@?DQo&<}tjz_9`l&c+tnj5ko^zJ-y=%c6bBje}ZRjcC8PKV(&A2UJh<)C`fL38!qof~m+{^tli_KWwvnyPi(yH5Hh zexT`1-=TWfB#qaOl-KOO$CPat7D&G)JoeUxQbrd4-O(4#Ij-cn1NcTbv$>x=^f??h zU%szuE4l{>`pE+>o8}rxk3O~cuX_t^TSnJkL^^NY@eDBa)EuJ=WvB2ZeB4$PR1@Yo z@xczNJ7%uLeRWpCkY{UZuCrbEh63GJw^!1l`(@HB%u-jWw@r%ma?q9_bf)EohxHgW7A6Q5qhJZLxyC8%L11s zZ>6?HQxa65+2}M!@qWu){FD)Hh~B4No2zf@TlZRNImZCiOz9~v%@#R}nbLBiF5#g= z`J~trc1-H2ws2v{J<^x1zK#+2)hE8C%j?XnhxU3OdHL4V5nk$NUlh8nV_y0xn|WGFzu-CU#Fa&*Aki{J(EZzyg_1==;+mcFn6&ys7A)GlE?>k zb-Q$;rFHk3lyn8h8R5g@KiaO=23l%`-Vmfzj=#C!D%khwoWf$?fHduuU`*KcQ(2(Z z8kgr!Yq#RUX2Sw|>*Ea@JpG?&LgS}OKF&X0go>UHr@N6&e7$Q@R91d(S!J1&LQCe^ zl>4q8lNXB)F*kH?r0RJcd8E|pAwCa|Hx@fsH+Qb5qr3B~Y!64LE{cN&y3Hp7%klAV-?V1CAdy7wKX~_CQ@v}NU)0>s!3Pf| z#(p}}%l(_Qvv@TFo3}W-dvO#x1Ujrb&!^%td%{&N1a(Jubq*FC5YlNYuxyenJnS>h zleAJ&uF^Umq>&e*ZAZy?6z_bial@-{)#>sV?9iE6nC zp4Wv(E}tQ1yEmmcZBpmGvgnP6Qx*ijzy4=V_ zr>L)R^`p=jD}`qLrTC|lu2jrz0&knlF4(A+A2M3@dHBWn>P&cP_*u8IY|p$&1o3qvnr$0-aO|lI$HE_21ym$5y&Cbm&m5_yWZqwUuzPq~k&xF^NA0T19 z9ap1&VO%>5T81Ba;&sW>PnaV7AB2&{moysGtFs2`aHd+;C>O+^zWBeFFu_CIBkV+B$*JkzN zjz6Rv_D{xtVD`?*ZcfV8R~ytlO5Zcd*G^S#Xz`gCd;f7HK`6vZriFj-f?|;eHnQ&_ z-|c}c)19r8uN@@BKe3zWexf*zM|og8>Oc`W$Ea^z;kU>>k^Gl6y~pZcigl%qAt?z`ZeNK-|Yq^{-lk(=f2%EsSmnR zdTqD;t~;sY*+{ui6a_LY8$Cj6s9;C+3gvCS3Im$TtF1mxMGk|N#s%uF4{Y>>my2KO zHQP4x6}ICgNHEgd%P;Y=ymB`_KG@b6W-?~q^s2Tq(VKP7<4X0L<}htoJQJ19?|DTU1e9$-153{7%9q-q=xP=baeQ zy@?ZY9u0k*8`0OWw=G(Zr}(zEZ;q6`j7&0oFrBkG#>RO-3-1UMmO_G!)+h*nIrJ%o ztq8Ardg=Rq^KY_yNV*B%X`6X?9A`?oT}DUH$dE-s=0_4`bpuc9+J`u$J?G4g_3QPo z>iF8>%bhb0v0_<{!C18Bt_D2cnr&9}`;>{xA)Ot0_})s2*WD;cE^6Splw*cLX6q|_ z*E5fk69prrWOP;{&kwkGPu6Wb{^HwkvLIkje#*lIwU2_G>wS*)@O83i4!f`4qs@aSe!4!_o=y6BwOEyxVC09zn!1;gdtC6=B3X`+-$y=^&X{Ha z>BUvIg~gYvQvOnkM%%jUCT4cesYxEEYvZl(Ejl)j9P2ZuC}dp{>fSv1Xk4jhQ|#oW zSL8mgCwyJYXj`h2JWrJSTGU&Dxmv|bNBcG+W*VuTN5wchPC3zkv;fk)m-I<*1;5${- z`~3QBGVE8HHY)H`q!e^5`2yuQPGCRkAO3vj`NCN(EFqR%IBfY0uZhw7$iUGKTqhePOENDW06c;CDD-JPZzsK+Q%4ETORM_%EUhHOtzYAo$VZT0Lo+1l-$|w zWYvU*#i!Cdwc%=M)1OEpKq5-_i9Xw_OEz%i1ufAIj({zCA!o)%XEH{eja`Co9sgeF^r7j$Wzt zsEE=laWdR_b2oH6=ys!O|Smy;+St!igA8S)tR{WvMqit-wnqdq%vRU zKN@8?5`>BMva}7xM}&vcx{E$rVV9NWiFxdPcICFkn>S=e)Cp27_b&(Ba%6ooIwKvB z*7MlH*!oJVsr_1~fn%oeo1v$KEJLO>-!>nbTr0I(>U-5AX_InC(wKG6kYvs zl{?0qO)mK6V=X)R4A}d6MXfR!FLrEM?S)Z`Wf!CDLAoxlJUTbtHd>#rx{)13?H8$L z#V+>+#p8A;v`TuL#rR?-8pI3dN4$+>WZE=a-#jMPsJM_))*vi$JB>lX{u#%Syqe7K zHGW&u=Wl%*vS+2$u5)#WJ8!gj%$+Q6D?gduu5FZ=c(BN}qr4^Ph&KK;=3Ar0*L}&b zc^&-E8RzR}3kzemNNY_7?3`(O>XnaPCX0M!J4t<=qg8HNj>9Y=f9~t>#c6!@r}2(q zX^P|JZT4Th=CGg)*V&IX-FfMH{O<2;abE4KZ&o_uk_vYwaNM6*dVY#aj!1Rps4nzx zmpmVl5?VZ#kIx_Z;YG=dky-z5pO<-d9Fx7}#~TlPkIYQYH=xfIWf9Td!|J>`yDeoq zH9&&Jp7hX9s0lip$W8CVOPyuL7AG3jbH~Ki{`1x4>(lQ~P?1au#;ML$7>?P+Q@pZ$ z`u$i9%{Qrg0=H_Pm zpByWDAIXYx+4_qF_zW6XMahoS^7Sn}H<`WXY|cqO?uF@AGQaT|)F=Ziy3rKG$Ad4N zcBUlBcq?ezFl-?AG$DNCtjC>sF^{gG=KJ#z5iyilyJjC=CjBT0URp4&Ec?F6xyNIj z50|wRd;F@Nlz`S_ii03?Hax(e;C^-BIoh&2&*Hbklpb(BW>h2&t#gyBp_ywddq1&% zH6~lqJ~iX6>}LU`z8abF`{QK~+Xk0kuUxyL)f4G9^gYOsc>S_$S>bhW7b83@tPHm% zVK&%Q7C<#|Jne}gPTK#qO2XZ7@HcsB67A~gz6^Ux%$Ieti&b-rEhf#3zD$F7c$s8z3Z+& zbvijtJTH@x%g%7Hxz6(JvjUfT!&_DE8~h|~O%h}Ks%x5DV{sjKe5M;Gx(khXoTQ4+ z_#LRgaXg(KqVzk&O)sB(fSpfThmx^dY~cKiSGU2&du#W*x(plZTrWTyQ(&4UpEQ>* zeS62|xzW0353$pck_4x=`j6U#eq~%C7T{hCD|&1oFaOZTnYD+ad04Q`T8O;yL6MFeZevifiX4mXIN`A%}=`$*LPxG{d&X?E53 zD-WL1^yDb#z-mtY3{RxmbE=zH6TeDbXt-4b;*KnTnqr$@{LFYr9gAAwy3L)g>lkcc zpZlY|>r*Q0Gk*NS;?XOM{Ht~al-PJM&YmR0i$Q;*^pI)NK=Iu6yl~%qSIUusVp$&# zC(rp=uYvU`jse1}OeYQReiRmQ^RaH7Ye|vSS?Mf@8Q1ibY#r<#kO{cjZSmeDV0&et z{pr4e=6a`D$E2#)eO1m^JO|pZlqN+Ewh1+81J=Tu*K|c#--c=(*J&afi>6K#o2$oD z>e^G4^B|)y#Cfvhj*zrpHoo`0lBu47glD5G0XaWc)=VFQg2i$Dup=y=6$Xw0*RA8SxR>+YZOkvQo^X}WW2X~&X6uJDK zx1Ty14ER)pO@?=DU7P&4W$k`d^e%Ko^@OGf(>^8&CTw8_zynE)qvM4?oY5?X>|)GTMjo-tL|= zDqv>F-OeI4wd<_u)S`+wZdR?!@v-k~d@eR2YF0U7Y$g1VCynEe;d$Zc1|2Y*!Bf)J zt#N?68P}+1t!OlGZ+dCzx?sxDIFIbKbU~>f8M0P1Z}t%eemgp>uy2Q~b@=|a2UT)C zZCHq=hEr?1)7EQe>G#*oS?`qF4!zy1G(Gb!vobo3id{0{ULnPt4ZAE}h+34gghI6M zB%}9Y_T5unvMt8Nrl0&i|G^SO3PToQxYdG zEeOaOTvp$@T$MBX)bDkKLS#Q@uXX7t^-ps-Ro&owy^KO8xmwY(+0JD|ff;Y-SUbw9 z4Em(TdFe6Yv^&9A-I=TrKIzrjTZsWPB6lCsu7>FQ={9|?G>us)t2|pgcj-b9H&+m2w;gc6no&AUh)!|7vMk$dy~d7gKU3Z**)tz+8Dd&8{c^m>gK8%by-MoZ{R6p1WCf(rxK; zPixI>Z0i&A6*flKij%^Toy(LvLrEh?UG3Zl#Z?_t)-N zvLx=6l@E$y^glaQxebyxcxn9)zz%BkCp=S8UG z6?@L%QQwA<2a{1cSL|j3j4TU_s#ueU^7bA*>aIrEmuMU%bTjxjdPLC z{?stIOztj8b0Cj-{(zhUzF z+>V=dGo^yme&t(bM!AzYXHHAKdKSLc&2MWXXo`<>#Or>{AM+iHhbO&if zU8_&eSln1G3l(JAj8YjaYX9h7%OoSLi9I`hE{esT^im%E-_&}PExN2_yp*IyKVD&Hly7Nv@;7-&@i6pT0t;_>U-#?~y4tRb~aHsCuiWuN6y46H}J9A^YLko{BuZNiw|Tu$4FHA9ck_&mxD@D`rY3iohcFz(qNBJYHNt)rm^}V^}R}PzSnVP@$-~g zO2fT=#*XMa8=Y3)#w}!@gS3`<{qIZ=_$*rm6qBfydp_$$Zysjnb5K z<0ZfEH=l0&^jVOo4vgfPc>L70asAT^lB1u7A{s9UWjrCjGDO+nCCGVQ$w**vxK4Fo z0@mWQu(mMy(+eoRf3EBiJfXK4Ab2kHhKX*2*FN5hYaf~PX@=V8wMDKMbSpgQbiSkQ)Xj_&nOu&!SYVSV zhs#f?g47z+z>zmq)} z>;9Bmid=(ypq_e1<2(4eSUB!P>C-+cA5c4c0@4_DP>*KUD2d zc1>)-s3jn_Xlt^R+d{1so$E~{&`?xRizBxVzfy*)6%I#%lYWZHRic^#O%~Xuc?Ip!$&R zWOVcH#WwqnHQnPOuWBtnYjl6~9*sM{R<->yceZjZXkm7~a>RtSJuczG$+Ryx9;f@U zp%1;{O~V~oX6h)8oVi9TA9lym$WPp8lc+;vG55hltneS#4K# zdbEen+!ZEUKC+VGG5zc@)l>&wraY$U{Bx>%^NLEyRX#p)b&avZ{w}@V?GdCAhgvQ; zHzy^`@O!9MjlPhxoF%t5O8Xp0hr?rOI-~x4BDutGKyr2~ex9vmXWX&xmT?MERC_2j zvy;T#Q|;!d=V+QUG>b3B2_$-Kp3iw&>`m8{(}pL^R=m2_*rjow23uraCU)`-_--Ki z)627IDQWWK*F-W87WR6JiFY5I+rHUbv|M4af0SUXy^P*w>5X`DFL`w4d*7aArj#v8 zanO}KZ7)djvh)bamK*VPaSyUb7Uo~0YdhcT8!>;tsRo6auzxk|dhX_SyROZXvPU^) z;Go`*NquWR9VhxE3-?=$EioI<=QTVA(znJ}I6M6|=5uGQGOgV&cn#rk1?z0xIrX(L zRZpS0=^gF?hfH|H7_nQBVwg#+Bs=$k?h8+yO1@zh4))~H=6s@(y z2JhLicjl`5X988JCTB75Rx*J1gQmm}L&T8Sq;x*i)GO*G_> zNL34V==r+Go3Oo`l7=Ejelu}CZK&+N%bQE-Hq|{f5R~9gQXiUiM7hoo}@- zgtb#%=&0tsX}o$e?e*BH1rgg-*KSqCsdL8D?;-PFeIp7|-_-F77 zi}&vA4BT_pKBeUH#CiH)XUB{53q+av{+f&_*8|Ut-4;&O$5+qSvwixtH|M%cmg6hW z!2-@U`Ay-$4&%a|O9S5HJYqW!A|BAy6A%SOiAvaC<`;7*j}O&RNGQ;5DHd}mX$bKf z`;w%sZ>l5wQOt`^xv6O^Hg`=)ykh8D*6BS-A{;v(D4phh&b0v+Pyf-`(}i!T_zb(< zFSxpsJF$wjSg3DY%&EcW+>FIL`Za@TZnW2s4AuGdQG>_^tc&b3I)T&kBaUtq-xw>0Uq zYW&_4yrTG6)~S!s`%YA-7eP0EwZMXXMrZjLc~Oimdko9L(9_W`7!upfRubK=D90P= z9PP8U)F_F*lEGh+?9#GNzfn%>+Q`yOndjh-`1wJqgCw_1I2u|qJcubn#VAWg&4vTJ zZ$NS$I{*D5wGYxEN{_g zuIotO)fRR#WsW=Cp--~tP7`GgmDK*wm!Haiu1B`aaeB3Qx`#$`PUlE(PsNqKYt^OTR90M!J)!Y6l}(+=WSYA1P@2J1QE=r(PO*SGN82WgW{Z4Q ziI=w{rQ;?Qx3ppe-*9C5fXqViNQAT)t!ox5&BY>%k-$Wq)Vy<}cj{Nkx$Zgp5)@&5dEYx(_^3|#e0ZRf5^~U{ zmMjNXE%sh;Z&HlK@_}cOB-z8f`3xq4F-= z$v1~{%Dk81bKa;!m{g*Ia4%ei7HyUuexk}N; z`!&`!cd{~_w+9G6B!>99U(7c<>u0}z!;soF=x2t`dEJgpuAHp7cslXSG?qg0!?bhs zY8<>=b01zc&b+>+zDm0%hxo}p(oU_d?m|q|d~Jd2`#87XP_2iA#Wg+g(y8<>W*@XA zv*m)naeaF7Rz?4^zmypJLuEakdM-&51`0dk4&!dOVgar|5{cfCqc!}?Jo90=syyao z@*>l2dFNJDm31jal3UGbOhG^M{Dw<)-(F=4aX#DMkgnHzI+Tm`^ypTQf1ZEI zqrAg@ifo-}DfimK%P#)xGC!-Q8Q23(-v7L`we&O-Z(q!)#iO~QLbdfvV)u#G2?RDK z4c{-3ZyueRaBH6$sr_c;vH_p(9`#mtj~VTaGD|vc{c>;NvsO8q zka(*LKN3Tm8O5r`URz#nrI&C&Qpb8ru)^wdb+V@qT{h(b#ze*Pwz+bDr|-N5a&NY} zU;HeT{h9df80~!pqc3EYr>aJmog)SkY!6AiHxKpV&1Yr4c-Psj_}Y<0ZgtB|lF*Dm z_LXDr&c9^5InqQ%fZuFOh}XDn8+2SDD0tE@FN)NE^{hwy8r_73#)DQeqHtdBLlK+H zpHviE6IaTr0;ckEh!>xUW=9;jH~5m{mE*{X3Ivu%_om(lm$PGZW~}K?eP)Jm=*NBBdff3 z!TTZ+zT=5|chhVWxoV$A@NzZ0df)Y7>`*|KS4KZgp<4nu6`xr1W~^2Ld%5P7mDr~H z#|IgH)}He|`(xiX%jr$2_B_Gdb=F59+o7Lunuh+VX~wD+m!2<}{xyZBaijw$2X1@gPSi)khm zxq89Xeb!7*-?n5Yna~<@XprZn4=9;gj+81tRkg7>C^B>DmGB$4P;hpeh{PP)bsL`Xxi)_en}q8PKg^`ZZYe!A5f$jy zxwiUYu-tJ_HPr_sSy_E1J@xYEbyDmrZt@am%b@9Jqq*&JiD0K-Ax#&i^NKW2B9kWa z&XH%Dwlk42m)9K*!h2D9ZMJcmOeZymF5{-Mh*x8>=afp*qZPfkCJtL7rIULpYgHr_ zb0tGv8f3Jjep-8r{m|PvdezO`iEU%@W{?3P!L&i6*e(4)V&?BpJ1u*u?HlPQwHz9{ zPW!bbciBa|OCOzh{@_(g^D3^~`L+I9)#>Ug&ZnH0c+bTznI}6|mTYTZCnQtYOn4AA zG2?Ep$R9Lq@!-Z)y}P<@#kPAlu3g&FzCgQ_St8S{ZyIC!fFbp!P``hXZ_7b!vO>L7 z<8XRH>c@lEy_nQy$@bbjD|+M6R?MRxApLVWIoVWDVa5I)w`lrC#&)gE~`tx14>pOL@_Y?!038KxW8FL)Y zC)t`E;8jx1ROyq;(1ufiMn8(QEz@(Jy3Eh?oG*N@y0v+dIrfk0=f9NS@C$$quv#)c z7v25B?!b9BcZ)Yj?v_SK#Ul#X+rCD-w)?4|IZ)>luEd-WK#NlZ%B4Nw_~l|>tl*kl z-tM#Fv&zfW<8MYU8k(C{(r-0@d3rIp3IhHmCu}e|2`;Suu@g`L;5Zg%6ac_1HVc~n z+6BD-zg}^vT7#d`QhLmZ76PXf1Wq~t^mW%y8bhM%NZ948GqdmixxdwAm~O`tfAi6Q z7eSz$4utb_!4HTIm-Dl&=Nz8^AQMDeR{eher_R#s(1s}zR{-SxI?tN!X{C0YoF3xB zV_XL)EoxVO%1oBSeH1PHV>#iY|4T{d^4xeCg6X|l*}rD4yXTX&131vj>DAla!M8}- zc9hkR8^!6s++{0Pf?fJO77Pe!9AaKV(>TODaTH*+9-?FqNzGxgzw%Klb7N)NIK4`9Y*@ z4_5kmb|j%$Bh8yi0X?bL5p%Z2od9! zU(Bz5<(}r^wiX^P^v#xYeYX1qfo<#nmQ+3;5W=PmK|e`u(U9mTY5^=lY~ENkT3C^0)OD0udE}2mnf~{d5pN%M-Wt6cxGpzrd-0-WwO^T?zo;X_Yw_qa%4m%5!_s@3R&3d=;m(}+d(GDhsTI%IhT!- zUXUyY-7?^oF51y6{*gOABnTrGg-Ug+0KUVzhicYNl z1OiS6`rt5&X8eWo6ZK?2P6N0Dpt(M;^Zws@;wvAoR^cx={=5>i7>rLbdr6G2M;S~n zo)nyF0H9Jg#@Nn}ZSoERlxJV@q*p&pc>8~O=a2nXtlXicD5xF}cV;m3H={uf=U3;> zEY0ZDV5C5RHBx+HHuto+4%%@i;u>8|UBxvVp3?8mifj9JJ zeFhxY7YTPNc*Lxv9g+oNzECzzb0oT4<#1TZv32Kq6AKTg0=P5I&+0xi-LLR-3IsE8 zN}!8@BX$E^3+OKfB9B&_0x&uCzoh80q}}ZcAN?yoqS^^}cKGlYl{Kqk5w%|t7-*Uk zEk0?WlFb4d9H(g62h)M~``euJX(!=9kM+A^?=R8JL}@Qd|kp zE|n|({Z)aICou2_UHN|f#|`>fjdx6I?*II^zvww4GOF1}avmOukv3NL)L}CM0F9cI za>H}uL;gjNe(7_c_nzmx82{x}8l_v}sJJ`OZ@)w(>N;r<;*Y_Ara%58fmEZNw>p}; z3_0=|=;Qw)+yE;^?cSopE`*Dc6__aSF4hEliYDUk4g>-H;LR|Ze#7GDrLUdatn7unkbJs#blO((F?VP) zWnoSOVz#M**oAT{Tg>1KxUM}(ZO8oCO~k_OU7o%_ZksIn4~N!Kl^RJ_*~UM z%}&&q^SNw##u7hyIMo0^Lt0p?adauku>7L$c)=V)c~ZyWvKs8)?b1nd|*=@1AaPZt8BF+ucrK{tO@Ps*R$zE&lEZM$?iy2qxE z%7zAu836d`$Z93pdATO-Z_C~%g1hpeQ;b_)qadIMP(~LOGP*#q-xt!S`H2+SOjn*` zSVb?S%|I~(7;-%}N;%U!l4X2C4BA=&0U_JyhdTOWNkQ}0-%~)<2IV7!ZR)5_)sDch{xmY$WSI=D8&L+>@c5x<{Mw|b23a%QKP{<;<7QVb281i z9EFP~hUtdQ1|NjG(wKvFqFYIHIkc%4f7>gb|C~R4#xL@CFLwfX(vR-|@K=8|{Nq`E(sc z-cxx{57|a8tclK>zYqL@PJr7WDgky-JDw^TJ#3}N=LZ4j;>Sb+s7EQrUh<=hSF0=? z=w<5c)0m@1`@DMyEzQhWd}4<7eCPxmoR~j?K);6iGhR&CIc067Eau4-u z<}sTJ5N!A&&f#gDH*6PCZH&WY=1Gn(1I0{BI&o*CuZ4fX+W5QJyyqDx7*VsS_1Rsb8*ul|p3{`v2H=c8W0_Vcnt zEl&0id?P2W`n#Ni0rJ*$7aNOpa2ZJIIFaSJODctV}WQ2!^%3!fmh!&}4&i9sXcJ(Wm zX9mK#X*n&F94pzuV+Xsv`q2+A21d(J!5%4t$}?Z1Md&8!yhftfNpQ0Q!?`a~^t-Ew zxd)Kph%?wWa?U4yC0UPNuf!zSWFs(&wrY!RJB|FRc2SRH$Mmx<{1)KnWWH#N8i!kd zN3*>h4;}Yz-Kv{;h5Mtw0f^mT8V{}k@WrBO?$=5o zf&hx8{l3;z?TE#^3E3X4tn`{^z1ws0+Rw>FJl>V0DTY?G#jE;5$a{s=D;yCTf9}Xaq{BQ{M05f-$s)9hY z*ew-iWjUhzNL<~GR^=nG90sUb<+(@b!qX>p>hL~@E5OCATVAb)jDA*fv=4}*?6b9J zY~MJ`NHmVAs8k)7jjii)!6r8*Wwa4=5y4hivJn7Ux^)t+fx@!JnAOxi@OnK|_3@-o zvOG;W_Z=E1+J#QitJ0=P=GfBFx&vT!j)0G#cvCu!XpfI;4q+4ff(Q`IKE?&CWhjCG ze9D4YP@*l&vi>g|Uy+{0i1`>RzE%ys_REgpHII7B&r1~xXxe#kW~bKq2?FdNb1-Ow zfbB(VBzsDLvz-di2mnmO=rRtJuH+z#D=OE68=Vh=yp#Ff35>q_?O*c3qti#!$?9Sp zp6E)a3&$2k4zib3g5ctoZ-;lXl}hn*8?7xsOSO4IiF}_j^Flb7}&rEdH}t> z5nwbjFtTfa9X?i6%VirWf8;L}06w6J!z@WKbBX%r8KUn~Lf#3PqF+~Rm35uHJEW_6 zN2d&xVv%k}4c#Q-ft4!sQL^%t*>y+EUux3}t*TYg9K_tN!%uS^Q9 zejK^@g+6BAvW-l#cKWien;KldYfEJYwSslvlNVmNO8xL#>YD>qDl$0aSwE!gCE_7( zn?)~HcF}??8sotoqXXK{vkC`@Nq<>J{y7ZTgqIqdcLID#CxU`x;tM-$^?5X1r=2{6 z&YvbjpQ1kT6(E=|@BWV;e15TvbvrITLCVgL;}Zbv>|Xhe;gK8!ut(zX;t+*T2{t1D zxFO)won!5CgM9!QyMs)#EJ2_cB7CBJ+`$FDxoYy4KhZuJ-E{R_B*^vi-fE?4X_hOE9vmJW9Z77p~XNanh!4_6uj zxnjLeV;zo%dZ~j1U4dW;f*KR@%IYOO;gSA!Dm&6yTwfFDipPHZ=d3A=MN)QroY{_V zM{jXbR^OJBS5*N^H~Vp;9= z_*6I_Q8t`D!n3kCMM_bo6{guaTn0~qBI8p9+u#nwZF+4G*8w8(dv@EV?G@XuV#g2Y zTEIsX-v8DDJwAAa4(Hwa8tctgKou*a8mgt!q9I}`F!jNPZ^<46I3wSQjURe8wU?)k#MfBdJOi&jZ?U^_K1qoWyw_?x5K(actAhI=-)&A#3A!W9Gu|ABvFj8q{7c*aMePP~e0BuWa(oseUf{tazf-!8Ijt-IJSHr_ zX`OK7*7@5N-vPnS?;V4EgqAst^~k9>zih@8ckoF=3IfyKRUv!SH0JkFdzgo8jU#xoN00FX9MX;NpEhpX@tp50u?8Izq zL=CBJnW&T)=v%?Ia#{IXe?y+|>7-M#d!DL0CttpzJ|eit5Yh)cC&<{%W#ySWY&OG&_;GPhb>Z~9=yOS5NMp;7z?5!R@Z}&~mCgacjt?Gk zcsso@&Tq#Dx6V&1os?A}B2#2f`XkNcleSSCqI;fnBaJlBg>H1lrWtAB-FDhG43)E~p<{~5B{alyJ=w+v@-g4nlcTNeu zgH9WnFT&N?%>zRhsy3-xiPI8)H9YFXPN_eRTS4H0-<$gN>wj?ha&he3;GqkB-WhNp zBzmWHep@$In`)~herm8O0l-b5VJQgEVET|`@IlaRPNpGad9LsDC1Z*yk8nssGu_%5 zBUktlBq&pJ1B=?CGr@TmK!9zi5PGG2Opo~y?RUm=q(!zx$O@{SdU=l?6+ZHJs#=Ry zhTC4Di-ljS-S@?bN{<+@mCMkJeT$6YwRmHKCsipQ-)BxZHmLL#CJ_Pwdd_eV} z#Lrzjb^h;me-+No`I}OlW2>XwIc_|B-pe2wfGmI>b_z^?saE>u=v3fmP3iH%HKOU3*uGy<+48ymaq;0& zs&iOi0(Bq|PLS-$&FVc#Hnz@bbZ>j3g5N(&x^cuEJni=|k$v1vds;ixM*xm*XnGbw zbX~%6ZP@k5uI}LW4vJiX(cRS5`2iv7SKZ9B8bGiKO??}Ih%#eJ89Vej2`~yn$%1Xu;!bWApaukx5Jh@!CtJgWOyU|#uT*)iOU_Pp}BO%s0h zovZH(9C(Me!QY|u@Fd<(qC{9D>vpdpawb{PblRlM@sH_R$D8Hk&Fa7HfqLNQ>B;#A z8aTGm#g=^NxbEw<7hz-EsFXTE!qt%nW_UdG#ZHg;z>EZnZnzwDf(W#nRHPjCk)ckd zS|(sX67T%ic5lAn>D{-f=+!^(0J8H_lT+&-)NooHAG^Rn_wd8=ac+=Z1T@XC-Pkmh$Sya6@zf>g<0X;<&nB1&-I?_83DaUlMQs8IxC$m7ZS;@~;K?IO7x$J?a&XIYqursz07P zDs*-#@yv$Y8l9M{eH(FfR{Wfxkz%e3V+H9{bL2%5@ieUZVYqzc>6Yo6mQO(dn;ay- zK?58()5V>zc-||!f3lMy5Woa32?vk7Cm8~!4dg@Kbg&?P$fM8J&-jWkt&uIv4cZJw z;Rz80cwZp*gLxxOf8`S80+x4Kcurg19lGds;I|V|*&`p;Rr24_3q*cooW5qsy-7XJ z4=D_^V@Pb%Xr(QO->c(%tD5G~E*0$j_X>pHD|&8oX=A3rrjL2fo=3s^tGOcjco6Wh z8&ik!E-oL2Fb=$6WOPodzX-?Tfhc7Iwv=KafNq}i#z~%RnRj;f7JKVY&p7pm%OMvy z-Of)nr#%2ri4UYXCCCR%C-mjgq0xbD9%Fzz-MG-QPL_#l2$ogg809DM!k|V$=ZXx) zm~62dEknNvI{hFsLG{i+&2rukr(;~1B*k`}G^M=rNqQjWFSM$C+ycT1KKoy%(||{5 zP%bTW0@4t;9rPIOs#9i2STuT9;2lHjkUe*W@A4$MRrMA7)&uJneaEv73x_gQUAR~G z{4W{g6GM=!|+(`9hVx6fZ7%UN68(U4rVg-!Y!8u~7~F0g5m z3q8|>W4BQB%)8(Zy1%*pDV=U`#<{+o-YY+~*#eqCVEao?I=!D~BmnpjIIwhqu#%k# z%!JMfqRUKt=gE?XW`{^Te}$YBS__IgVHnQa$3&!V$h z#S1DqKd}w?9^N85(h9We$+|NBi(;`h6gYOJg1{f>G2x#UfzyrkAlI>;toRmhno~8+ zH(`=o7@ElJwRtE1q4(L+3f`^V&)nDbGrv+@F*b``T%UXIi{VXGG7f@q4YAw?Y*5a& zROIeX3-Og~h{#%=IfOe3!c^u#Q#qKv3wkOP1u zyQOROaWNSsY&a=fZIX3e&;jHGJTS@*K)=}m~@7iQeklShNeAq_D)T=VFgBOjCf$HYm_y=6Y)UkXOR|TahbAp*6XH&1-0}McPMj)cpLR?ak?8K}^ZrM7Rjtt+V>4Vf zZt0Z8t4@j>0L@rCfSC(}I`%PtwThO9lk=1XG5CG9|FjHPiAhT!i0P|9;3vqU+*9Lo zYWK44fosRGQyeCHNekEOiNn`w)CUk)0bUs$&l+GKI|jE9I|arjW1_F{7%9xdbp`w{kJ_t$Me|L^srId zY5VS5mqRAi5iSJLdhjwIp33p+x26wxP587f00uCwlA}Jwrxhslm^&W(jPq^)Ezdpj z)JM6j`xpFS_wnnW)LjbejtG;c&S`0DbN=rxb@%b|wBGRs@K{t8ACZ783ju3pwm1~@ zAqTIo>NZW7HwEQwU9fCF$&F=m9nOyltWl8sIXd5uS5qZ1zQTIo4Vp^_3vs+jZI85iUt+OEfIC!k$IZHq-%WfIh_X`&5g8@)l9L>yaz6WuFsCXpwA9(q$t z)p0yVJ00x+*8MI)Y2RSId4={kWLnM_KEDOn*2xZlH$TP2SLD#6Ug3NtKj`8|TadA= z^6>&w>mg&lc23oXbMvn<*oHvG)^5)NfRE*vM(F))~WSr)i07h4-&bd+!mhHAQmm7Y) zE+BfadOkCa`R+Ln)ro3uH#ngkz*#?_wUJh zU%$&wzR;>&?*Im5quX>5&)tb`*m7Hh!ydZOY{TV3<886C_s}-+O34@$$ro26=SVvMz0yHS`X@LbPyNP|^6$@xK zP8NF7gMQwxs^yY}J|-ug?Zq({y~r0{&PSbU=YiMzzM1sbfi%?lo*rhiv+m6rYAYOUEifG|5quf{e;qUbPU@!@c?rF=X|b0 zbe`;t$8QpyF`v16A!OY)@3Ju}DL*zRpyyP~pEAleT{pP#v)UHR)0_s@Y71-aDnP#> zMwv)U$eRw6$XV!tiG0*eVil&09GyYr!~l7v zkmsn(hgaGhGQ~c>0m%8_7)Xv^r;R7IkpmKr=xn>D_fq4Zv;~+TQ&(ckA*%N5`*j5Y zJv_ttDd)^{4^p0EZNT~sx$lEj#1%rldq97z1#VX>*OmutZ2uayThG36M}?pA2I|93 zS)Xw+6nd;?re&P6Z z={!M~^Z!)=K&O4Hz6ommjBVHM0x$DvifLfU#ln+m8_C;_@me}_2?Wk7Za8Rmno>e*L;gMuLR&034Sch7ROxP+s%TlZ9fsseX z$@Uu@0Rfs@uWZd{8r`A9OE@?tzgPxw-sKpB^7q4lU0A zwF3xu@Tv*6eO6om8n#`(WsxI~AAsSLlxNO_f0$k{t9Q_aBafWwiB%n*$~7qtJ^GQY|fyQQ6k#8g9}hSKnqw;2{GTQmYYHl00Crn-KwSce<4>=MmKDy+Ek+1em%lwo_r`Fwj8?zP( zz?l5lE@0ABLK&V@@4iq;1tME$)!8f~&7fhyH1t9#(aamdmT!429dp?(e4wS-PT_IY zn%h|bLvZ7jft*y!^8S`8ER{7L>V55CojxytMs1=}Wa}I^ z4Z7_(KMAALf@PsQ-{1(w(6S#D8PH(;{Zfn1+JCK$#u%`6W@xGp1HJRhD7SN>K~R*U zL%>*36=Uc5t{Oh_ly&YD`793~9x^$Py_R0|qAv5KM*U&et%s-ls?HNR+F{$0nY|ap zahU&vboGtF3bqS@d<&(%mK^oBM9Ro-1U4u)v7QNatS29<&X8_iM~3*OO^`cbyV(va zuVILu_{aZ60pKPJ_n1e&<3%vY(XYa>tE9}BnoeH)dX8kfmO*6LKWe@d5L|g&%EkUO09VR0ge;ryQO(HdB$Nl_VgVN`|Uiupm4V z&Ek+}YXp=;Sh9qe)x5RA=9he3r4hGW*3EhI!G@AwQy1B+>%8f(lg-)t>HyD!xdPn6 zTKvLFc`J7Z?twIxZY4W_7G^JNDNQ-g6IiyJvGZBgdYM=9b)K4+{lRr)edpJ*j|_H- zY^?(lv>v!jiO=G@xz-hlUM_@6b_}^1J3q$e;un}Or}z=i8Y$YuIXhfOBKYKmiLfDC z@^Q{3ix~I04N16*Clem)FZNK~Xw^6-@C462VjIvGm);PLhy-VMNG ztOgz;;Xd+?M~H$wLtD4$4X!I}TW=$`s<=V_>mDp|>7ulHHgu zQjRgqM%-T0Sn+_C?uH33Y2{!1Y%k_dcCI{|Fb0e(cF-kJ#y;5gl&#=-ARP19dcskr zPnEB`3M;~gN8(zBS8Xshz)B%wK01Y7d6t1LTlIV&dZ7iKdUUu-SkI~Fz)fJ+GKs^? zi7vveh$wpVW#Wnx1Da_=d8ve+aVwMQKY~aI>jT*xjy2xOtWHX!qFT-7?Mh|F=DOpbq8= zE@GTTM9Ygv$RR3vEPmEQ7XY^@80Dkk;XC2Dg&uYtkZ@J#K-TLi{io^Hp*{E{xc67A zkO&W!HtwawXaj&qRT$GazY{^A@2}E8qE?(2$sCp)Z{l912*HOTxp)GQYRN0op%;#g zo@7Fwny^VEM3xpHPrkGQx%h$%We!+`dRIcE&_M`YFsaD)m6S4cKyvMcKhQ43ja99j>KxzlqC%AyQ>wEMF z@l!R37bm5h-C#XgWi0RZ7GkVYSpdFe=wtVhX<3&13DQ^9$2=GU(}npS0>AN)fiGIc z?Ox<)gXQHKv0@_(jd=$Y85}gP?jZxuHp)D$*9&kp^U2F*<%v%B;0IBCX+zj|zlk4! z4FI}%&NHUyx-3lAJM!LmEZDTJc+FT45P1cL0s!1(K3oa8UN&=9?5yO>hvK~P5Y4;^ zID!PcR=1GGP_eN}CkfS{V|&80UnYmr10wkqVGGyxx0C7%I+Kmlo#iLA54={d)P9-T z)I82g-2_1WVtIGlujuQyZ|T2emA^-Dy4%M!5-zk6prU>1ciyr)r&V^_SEw(Qzg<9% z!#e<4nEQ94ualkQIWHQ}lV}zBDMW^IjXAH7r!XYh#eooMVOjADW;^0aVI0`>@ z3&;}~OcSn6Ei^>JTYF!{U(1qVt&kY|Xaj&vflE{B`pK_jM@)%T-z%_JSK)9j%GxW$ zs+eWSOCNf*Lk&eH4w}4D)ulemm(~OidTC*l3(toc@AmhRG`!kI^M!fto>PGw0C<@s zcLVrRP5|<>ZwTNGK(uXuUIwWj(9Bm``8qPc9(3^anK3!fGYmKCeFEny81TZ)=!41P zJ;y7|dUEt#0)A!Z;2Znk)NZpa#+QKNi%pMKQ*^pA293SSo6w%vNme0cJmXvWLg&^W zPjY79S(0rJ6~%QUJCr;h%u^j)-wHiw)LB+_cARzR0G)(Ba@3Q{(9yGf@yLdp>1F2; zyW~QP(*xu}n2fT_Kb2$G&}Sh>wKXVF_lj4cL5oOkL9*C0#9IXxvJ!^+Cjk*H+;FB+ zj(n*qJ7vvFC)8}Q!66p{EB3>5mV*G8=6nzbK5Q?`W!<8}7O{OnNL&ciX+S`>Q%I)+ zacaaHbvEk!ZUd|E&HqK$3I58I&Bv^)H$A%#PLrIUT95sm0@5?}TCf+$4{q7~cC2M_ z;W`C?<|$5s5CMJ=)*9C`Phwts&J_7P?+53i<*8Kp>Ko(>t?Ff2^NitTp9*E!fMz{o zbXK7E*A*!^MSDev9Bb($uxVGvPojsFii=4Okl+V1wk||8X%+U0eJsgF=w}_#BdW;= zIyK)!-gu@O0iqnXNItF^^ALt{eH20M{*ZzR=MBoB!b1Cyui-2qs zLQ{V)Bzev%^f8nJy*sl^R z)V93BiQN3vU3bF|%eg+%FxDs@c$LOc7gNtso9ZdSI`!0Z@JV)KlJ)avOr+5H8M3^K zLxwjl@eokhrha5gs@E7wyJFtCjo>05a_BHO3O!b6Xr;`&UrK!Fp$8wjWaIuldHF}> zAy@Q_kq9nc2+5GQ*V{fT5{sg^ZD_@9Yadh!0t}lqB+MKUS?} zXh6=l4KlCAmNUKBiMmTZ>{`wk`AJ|~MTV=y?OSUWuF`41)sqZtE}i%P9rzuM>8M-epP2Kz{(}leT+1kv(&W>E z`HJtcD^{Bx`@O~0?4FY6iSx#-PNCPi2OpYJrMaAVWCIG%I#tiEiV!j_*rxR@Z&~5$ zi+upd64tVGwVrnXtC*6?ic@iJ&1#CX7aZs8SfM)3jWRvw;Zs)P8bA#el+D8~HChf# z+4hwUK;Er27Kx}?SKmZ;#1EaUOVliK5dieUh4mF&UH zs|zn0FyngNjscODbmY&~t~a;T)n&d)@`y(~qPy_I3%e_>xT3rA$}2-?zjib`zD+wU zU1(hw80QRaJIh!NT^m@`Y0$C1&&qv%$JcAvwkcl+rQ|8I!h)4pNy{y?N^{_fU9IJ5 z6Uk+4n5KNR4SR(iW#wZ<*UCSpS8T*xF?4nO-dV)+HC)1jZaMq`yCl`g-KMZF9QGw2 zWg$uAq~aLB!|Kpf923li(*2pVfqZU8wqQCYCgodRTh4m$)`MJsBO4Ek{JVyz0h{ME z;Bs{~azWS@bIyBhX@S@Q5Y^-5^Lo9CpwLb#FYu`8p z2v}q6YXz-LTSgFCrnmW0uDgM(V_C5C4L>hnVS}J;d1G{1Ft4}Uf3`9MLHh^&wz z%E&a9df@PG$&FMs*VyT?85aox4oUfUgdyY39=8p5jQEv&%` zIs`2})+~)fCc`?riPaVwiYC;NzYvYdxCUHw(M8=8p74ZK5)XaoL%SzF z@rgm_!JOlIvT{9JtBh=ru%!MQx&snHkTa$A=0eMF%WXT%H-@i#o%f|2YREVRA(b;d z=Pd_@4rG4G*ZQ(dFTLT<6WRgH%qf&AWK7SfMrEf)`W?K2`?6^K9v%^w&;o|v=J~~( z%2Qr^enkhlTGECGq-7ia=1;{%EK4n$okQxp4Z? ztZoS4{5qJ?jPeQyl&1@gG7X=a)rST-99s@|AwCOMQ(Yz|8-c7 z2wq$XkvWy3jmieNT#7w1lTyx)2@Mn^%T?nX0FXNNA$9JPbSQdS@b-k2cebo<#q_kj zso$~051gF6T5h8huBx|AHKcp#rI$wF8RBuP^5Tmxj?)0{sL-ad05GI<611ZqT&)xj zckNqEKUoUC%FNSDUojzKltsc9A|OChep=_E!PDoo9DuQlR92hdDS$-az_@&e3c>~f zLMZ|Pcu}*Q6wa?(z6&emMg7it_>TN$aS=sYd1R%H>*P)-0L;zI$;qd?8C~=NPs#a5 z!qdVe@(~CK3Pd`hn*)HHI3ksteJPxqhU;e_m0uzVkS8hZMo=g`X^GY!*fODCW+_Hc zQHRix^*8tsgAag9qDvx=kMyHhd9Z>egv}k_pnLJZ8r1r6^sjXt;4@lLc30AP#&w!X zJ!?VBZ_5*O_wV07fum;tZn>p9eE4wiIS!tgu(MIl&^9;ooL`hs{)VLKE#|6!4nCQG z{`u#}vkF`XSW-ZV_eu13EVI~3p7C1?VjyLwkvsCf)k@nRrs;Q49ojx)J?xOYP`2eF z=PUVPU9?!$d#7wP!k;pGvc>2pcGvLsAE0OJt&Oev#>(`)jF zUFI|Gid_!vQ@!EXIKLd9{F0o0NjUN&7pi!7Tz;mUE7@FRn!EqT?YP>_#Gm@qr@GI5?sMH;?|RoUvx^#d9tb*qT<0qS zu6);rm&PPdpups_gRG(U`OklTBKP|1ukXI_g)aoZ4MG!@PLMJG@$s=jo!}Q=|7FZe z&^SS}E8>lfa8Pk0=;64USgi0kbl_<{Fst%NSB6PiF6Hma69`y1wJO4Q08lRS5da7R zB<-Lb09f^xVh12k(7d;kFD}SeaCm6s(RmKe zPeDl8I6V-q6N~|e2QWSWDuB!EOeO%o8@EHNM-BjpavdNX;)Hr^LM0}rcJWe@^;X=E zeB>kDAOG}4!TgEZE$DXI{HP2%$FTtz7>>zW zbGimFJ3AYRE6(I+6ad%}1Qw0Jkaq#$bSVJTRh}lh@qy>{)unkwv3VU-**+-{(AhXY z5c5I#eqQKPc}@}fbK^3KMYh-IKN-M{0uN(s|Mz+(|25+peA_LKWt_YzCElYytKG%o z2iG>o13xc&*~_{IKJbCv!ycw6B{+2GQ1=_Z@f+PIKl#b396JCWemMnTyUtf=SN+m& zWAkGl`&f6?RaXrLfDeE8!;M5u0 zB>*td+3K_CffwZPeE^W7vq&z;@#QJ)vlC7=Wop>phG11M84!I+gG(^)N5_6oqH+WkFxCgA;|&&xS4)1-QA z;SU8^uPhP|m@o!)mtr}K%UO5)2c2fD695qZ#3w${J@u(i?SAqnf3myRz3$b$@r`fn ze*gD>zgQB|vG7zQtLmisEW8#!e%wP&v|a$X=9+7w4G(_sgG(Upt^e)c{%!wULaA&< zeK>WN3i&QT;dxj}aRuoZ0^FZv4wMz3&JBK!?6#L`jwlB1c%+^=c(Ur!PObua)`jS@ zX)b#lDbs&RHm*Z!>|@$!?!`=H+pqKbaC!xR(Ngc=;q-F+xn=o@4wyeJm+?mcK#sqn z*#`n&6Oe6!Kpdmmu~?jciB+E@r;oHKuQ{$IbEHL6zPGS2-yLK3J@3w<@$1701pw{> z5CpgwIHw>mt29@31hWbZ;r#3f*aav6aLXemnX&``+@4Ri|E%_l+UCz_#h=%HQ-`4r z=_8;%`<6#mTO{N$>_4zQnTgF>1H2yKR4{4uu`S=>i=m zyAICVTuD<%}s!CAuMafFsqzvEW+~*6oIIkUfJ^Rcr=(?|p`hgb)bWNP>U^SP&~xZKdK*M8DbsMFxS$3~@j#gAtmXlTd~xASfUh zNT4EvaiZn(V`^=SepEz6{GeJ8h(rrv4w=q5dpO_qzt*~*cfHSk-hKAoXPt6R-&+x8lpp`~<608LvV5Hx#I$k-@2*{{Ipw7r|^^lI) zyWp7)RwMxbfe(CO#DhW5zx+!*K~)XHgoko6#tC0ToAGd5{GpoOsqpGGNt~$n?oFJd z_!q&@q_qGboP2FPaf^CIR^vGXz&$UZc@hm+@grLR_W5GG1p&Q)UlS0SSzW7C<@hKejNY&j8dg7ETWL*z+$*&0hdJ75MW4Kgc031DvkOvmvu7 z0RZ^-F77e-_do@HZws^yutp#)z=wlX1Mo>HeW@$Ag#d?p>Hyb*69dy&2d;1$*Fs+$0*|JnObZk1>_0GB{$zqR+VQ3puLJ^xi% zQsZ7AOrS@Il)L{D7+&!;P-p-$7Rqu z&S-Snll6g*XC`Lzl*2?c8?=*dh23Z|X>Zc^#yP>Ji4%<$*MWh!cD`%c?v$VTS6HVx z5MR@{47mXqr_tl80U!X-1mNLC)PHFJyQJRlsu}=_{~mxZivZZI0oY}203jevp$dVz zfv_fk3UBqFx@sGI0Ehr!3$P&Yv-e++7O@vTyXR#W ziV!H^SI2TDAPsIPYfpdYs_dgmu2GIK-`YeQTk+Ks2niT`zq5x^j zIRcWhJRjhx(TwM`+05{b`cFf4%K18X{FLrOyn5rE;B#hYzD$RI-?i~P8%=rgztNqB zck0oS_iZ%Cbh=aYI>7Gln-2R%@@2)R&A9zkpM-z~fL0Q({$olgs>pBK8$P`9UDsnpi@XfS*tTzM#fbXcc9kl&Ha5kzySsg$Fj2ez=IVZl}BLb ztYC$eUz9_`u_)j#w@!*UQQ|UK>V+k=ywL_c2&^ECXDDtNG^LC){;3tCrXk#ETH2m1 zI6?i_8*}E)rWc-I4biYsdbE=>_DS{DZNaa;NQ=(rm2{nQ_z>rr|3M&=x_(46qnug! zSh44HAbPtO=!E?<5==T}-;Cj4^R$1f9O~1$tAE-Rno9cqIHpAnq7^zKXq3Me~EQv&Up0oD%{$wik|)o|=)%q&(MEQ3S9J&SvF4zPEk z1t<&+z_$f;7T~9zqy_lIv>pOd7^^tyJQ8zRhkph?gP45_=L7;jUB<5H#HCz%Fi61X z3Plk3F-vuvEdj%|S=DE-gSV1EX^5j!9;<`1e_p;1c+-E<9XjgA9VOPi=ht4xcM8-|3js6$;4jI?MaQ+za2RI+Xc;T@ZU91n$?Dz> zVC8R3-`~cy?xbtl-$t)-Cw(ts=1QM*Gx8;xgR?U(E|%XZ;3u!RMagps3wKQ_(Ib@8 zrA)fIs&(Nr9O*vMJY2KuUeZb~Ew2e%Ga1OYaGuis%W{@6K7Am;P|&RQ^uq(%emG?& z4jQ-8u^_r1ge70n;Ey|0K|WG)dRBYZVhu3@KyL5*HQ?U+b5GykFIxcV!P1b+9v_W( zw=`q61GWf&x-GB(u&^lk4d#Lh)m1Lq4KBPb*21_vFB}LP^D9cN1-4jlrt}CX{lWiW zWiw`T0zij<+5hj+Ie>2kN&u8+03-l-5~?=N-Jo%5jo$-M)3`VR@SX+9#lasPRXBV8 z6+OzhqCrCd?82?P1-@e-jiJg}=hsGH>Y`lci`p23e~5+y!tUp-0_5Y|Q4SiVO ze`P0HwdH={-$>QAwex3OHJN(DFZCk2tgREj+Hh0Ozs*lRf>MNRUFIs@l0R(KT z`Q5fyez|QW0<0CtU(O49cLBhJ1PxbZ=%N)%BF(dS%JRIyctsj?y}X}Qo+2m?`(dHY zjKy3I&m=`)D=b2R-7e2;=?r(I8&o6S;nLmQ&f+JWc+!!cd?`cD&71unfA+ou`c;er ztFn5f@fL9x^uXxm(Lwzu4ti*OP+~+6Kj>Bn0)@j7R3x?;2%Eeg?bc{4*;g~Bwlphl zC?=svuzDUQop`k8ufof} z$D--_za$LXR+(U@BwG9bNbzlsS-49$X*XP6D*%QvrO?nwpHO6I>!$K`K~93kv}xy- zc)7uQehr@ml&aOAhrR;clOi|EHcAU(pd6k^U~uRdb%-Rij3CW{S6#vg8qx@e=*?%h zLezX5KMb10Cz5goOjdp^C>t{~()Fv`SYb`7lpESawazv5FrKYR{$BVI&pz5R8h2Tf znt;ADD|SB|KOBmIWs;6|HR0xmn#O#U`6NB*ntWbUT>1j#)(5;xXl-^-!b=;w(X|23drfW+p0Ft837M3efpKJz?;eF9F}HzqX>> zs+*S`OXI%$JFmE?HjP%MrJ+Q%?wWL6LVCCk?!yL$EjHsOZQm+rVul1jVv~`=QfVqn z<;c*mJ83q;(iuVMl)dbJuY1AIedPQXacj@gl;c>X=u~`f0VDC{kcEFFdyd*9^_kOr+`M`_uagy?oa1ywhsQ;0k+;7x_Wg7P(+@A9 z1OFMMjhJ4#ndSG=c@l=o@29J2He7Bx+Ajyri^1LaJ0}jf7ehI%AGa*T|EgbDE~c&5 ziAr#}@2%`4g&smXozdW-{|sA_srYv-exyy*wXxuv!8cRD5cm^vqF`{|(qwjRbUJS>LKb`Kc~gKyTU>UT&e z+)Y%s-R5tvChJW^J{W>f_=YyCWxP?*{c#X+5gHZq&_P2^K9IWf8TA+L)SU5Z@4~sF za2H19ASnISBh4pnowt=Rj;6QMRGuk3<+a{we%a&uzrc~()b(CITLe}d@=gG$Ry!oL z#3q%KZs;HRf=95J!#yyGKXFjfu3JxE03-vHhGHowv;{xl#gLXXOiutU=b)+ZGYpd! zigkV-bl@#P4o|r4!I?lg-B%NQOk4U>{?|GfY)VKVG&SK?R(?4ap!;XP|5l^+S8`t? zM*EbqZSuK$m3U0dvf71hzY75K@A_8VW|2@>yr=9v1cviO&s7sLd-Q$4tXWybDjoxk zg&!)?h;y%=Z|865fgQMD0|xDhPl2y|Swk=F2|zFZpAyEcL%ueIA1#V@@X57Jn>jJe zS<0opBP7RX7K)a|kG=8%SBJ+f+8O#$`NCl>-{6i-2BnAf7*DVITJd>Gbu6~$8f(=@ z;rCOmVn#kcmCu;-^Xe8hj5ln!-9{{cP{&Zu zkU=wGyG0+N77o&+`0k^(U5!xi+R`tiVY9mSiwkuL{9G(fbK=o*cS?+>^b2sQx1y_~ zw>{w9&sLG(h7pL35SR(delvQ0T9q<_NmhStP&;jaaO@H5XrBWBIxQ#xHOE%FWOrAq zA?@6R6Hhv^QkSEadb>(y=6Z%7HQ`G+w+Q?Q!HpQ+DLDCIdWQdLnkZ=pLxwYvLkOim75$h1{~3+5 z`WHYB<+(M5p&=j)5S$C=iix$=LbF1!dVSP6?H*0wDPSe!%a#53U?grECEJzpM^a{8XUA z{7kgY27FMcMUnA$u8q`lIT&gl^F#z?Cd5~`thNtp4Ti_|Y*4+4(^4RWX zedrwNGtzW%Z@dCcLRL4-bPHNqLm)kDw0Rf3&9@z%5rC`qag{VE! zhO#XxfDm5^Gk#)KdDsr1Xr&o4$xB%& zFZBfQ_=zKJ>B596s2I00o`#Zvj1&Mn$I97-Q$au+@VDtm+c(4~Ep^8(%1vF=J34p> z0ia6)0Kl^y;5xjpGCFi{Y4o)}yLt5WKRY1c9~>=xL8{md%mg_%Y1DcSmgc`nlYT!n z=A6O+?6rBog+nHv#;bLFuw&-^ZF)q{`X&NEBgwwKGd0||6g7%^@X>c24(njjXA_^+ z7y!h-P~4Xk$H-uZjOz7^LX!C~CFZ8z8I8Uy<#QvyHdJ!-{%b66evhiu-uNu?;BtNJ0wSRircC>VjygFmRI3JFV zgYazJAe1ebKdUjiXP|s2v~NKzV9QcQP-*iPq$O_qWI`^_!KIZy0oE7xcRwKe-v} z2sBvT?u;X8VQs<{#`zAP0O#l2IS=5Dj?W<^z;a91pE+0htr)%6GnzkRPM?b(kM@4g zc(mu9+5nuU?Z9q5gr*aJ4*&cNiYNkr$A~}v;4eN^bpifa>Uh9OuVOdaJkrw}Xaq^h zLjd58jesD1@~{IS@8A}I@HD6$vNjABrXncQktxKzo*^BUd$t*V3Pj8LM??7KYn}T6bRo{vRtZHDhRPZLu`BGL-!u#4oCCU}s<` zue@5lQQh%#z~~iQBZErslXiO4H$!0>0kn;Ub44YnY?O|JK2%gb^~D=`sNvitKoIcO z-QEYQ9@<{b4X@S2Y!mn?@|Jh2zW=UzeyFr%NB|fhXQ`bMq-;f4(b-qCeTeo@n>S1{ z`@Praze!5n7#MNko99LgXNv*vd3Xfqci%^i)2i=UNlPtTFdQbLhN5=* z)caF)l@TD9u2F#v2)9uD#%Hz2O1i)#}P&coU zy|#=g;HEu%HxR)(MhH~lWr3)%@!==j#JFoc^jS#1fyB#LYdF2Q;Xb~^>|bk!W42(} zO6)pK_B)ih*1;`bKkxCGGFAvu-=dUY*y!y@1pZC`W!3hShwBZw$E)q9s*Va}X-ZF# z4~_un*#bleAP}}o0MILJt+~Tfa7B1*9RY``-Edl?MY5?%D z2m{=NlLlrE2Ef*44C#|6Wwf+)C#};*{iia(Hp{eyGqm_A6nEb$$YId+4EcZ=HVq_w z!f6lSXMh$DEK9#utuPV$s#AY@ZnSvcxzT|?kzXly-$#J|wqiNK^{c?j;Oz}1(qiZ) z42N(07$_&iFa;~bt<}&*&Ivf6#K7FXNo(Ks{cE)hwmCPwU1KDzU{%21PUAkfx(F1KKH|;r^=;LY~f=ppbz+=gpsBd0P=3iz}*<=MywO}{{gtp%+LZT92f;k zeRuLFKK>y}8aj;tNCybRPg^oz9>CB4%h6NLe%I&?q+}Z~ch+J5LeHKbE~)2P9W?lJ zyouF{rgl<#tzuQM7N8>10#tkUx_tO@=^8aq{%-_>YHQ}B?=p)51cH!5vF$WGmTa*s z^7S{AI|SQV{$|SRPVA*g2EuTNVpMxGbE;IJJH_QGRO*qB>@> zHnmGzD;|s@yxa19MndloWj~-WX*V%@AEf&JMV*HdNU2zOIQ$%nAc&iI(xp{$Zpx^2 zorxPd%k9MN>QBIw03e_d0`~H+niSkZs&yybdX)6Y%eE~15daKIYZ;(oYS)+)RseFa z(qk#qA8@Fx;Xy&dJCQ{RHO~GR`=A7y+5G-ynhmQ zv@TBU`DLV-+w})Jk@RCh<=Tph$W|qq_aV$tjxj?=B3naBg7&KMkl}cr~Uss)yTV zr4dIgd6Izk!7{WNBc|$v64RifQC$;uH1yOb>*Jm;Q32j3?M`Z>R&`dv-LlH(~g4S;(2&}hE= zOvwvrI7edu&6$^LNSTqw-1xR~4ge6izwen8E3@jdH;+LB;(otb9TrqOdFYBa8>dW# z0ef!l=aUZ2$xla0-S%U~UsQiSEyP?et1#bs1!9{poRx6vlt-PpUC9Bmk-@)M+EU$9 z0)TL$)aNc-ZVPGB6OUhR;;(z?=t1&D2p|9s$g=;>HlXJA!b}M##GEJr0Lw-IAOujN zyxNPxZmmTCyveIPIY2fmO9lFr03eK#v2y3cpNB#?xxVo?G&#uL^^EF(IdMB7)MD~|i~Hgscd z4*gCM_|JHe>SrdZ=^o5UK%Z8S^n*M9toRw>Q}qcV2tMD&`zr~9Ps&PQaNfZg4qK0!M%hC9G0|zwsm3rdEIQ1lCG--6KZYgjfjNBH9W7cCknZY=--&~5`P0Pgg=qj!_i3=eGu042n#6oWl}z$iDT)HlCF z`~0^Qw?bzc?m4r6=2vX^9zT2E?yxZ6?$ds0#ES`d-~{Gm1!G_Dl}&2^jLNIBGld9I zYB#sS{H;BW3I|)(MDD6E=|u=5R$8(D=C}c*C1C27jP7ZQ%KUoFVXn5#TWM zH|0$MvI*~;fMY!~D}F}!RDBxPXg>XL4sCr}uRd~m&;Fd}7dB$}IdL?CJ^xgx170!$ zX4$Oj_5*mrXfIaj-4^oIaQhQi%lpDlj?PgER(~u@00O|Jtf!tU%`rE}RRGYNBWwc@ z0;v5b-|_0v|MY|l5S;)(TmW*dh*N+kT;NN3(r_4b;~O>NA5z>p1`zQ|ZC3j(DdhoZ z%q&`BT`#xfZhl``k$8Rpt>ai2KstY>hTz*rqth=?yMI`wT9{6vvav#aS1B~OzkI)!sTwP8r&oITU zwsfmI@J*OuEI+~ki@yTDb${WpBZ=-Q^`BJWpOQ|PIgtVYwq;Ehi%e+&C*AR_qmRR& zUh$!%5Tlxss|7qIU`Z#-cj|9^v-GatmwSNel+uRP^wcL7hTnaE1*v?OQ=&_KgvH>F zzfu^N2Vl@L02zDK<7qgVzRUa7EJWsd2BySld;p^l@Z^p`+wehV0%p(%03MvV<^Rz# zCXfSoKU#ZzDc3I`iM72~m$t(kuD9*tFcaXQ)gX9yn$Q}KfXLI1d%j;~?3vY|8XgFR z)|DE~070DJHsm>j^$gDgpbhY3pLvXNt3aO;0BW!O#3zly^ivic_^09(M<2iVQ=`+A zLR`~n0JmuY0E~bxa26YkcAa(A=sH5Yk`p5)wGbc$;mHB>%H0WwnwAGgzWy4CS%Gnu z0W3^%C+#Vd@@kpRM;PVXQ~%+o9vN1+oBxy4>)$AK&3s6tBtK^V5s2q}N==8YHe%=_ zCIX@OCjS$kkb!gvx-XnB4!%a?pw;$R8h_PC0%6bhX@&k#*$39z(yjo$M-fyG;-4(% z2DJB9br_0VW!bMbNleLpQ+jab=7hV@c5|%z~isB^8#QqAU$ZEXZ;b$`sah4^!U2ZGYJymaZ@5ZxA+>YRs0>z< z)1EGMq@IS6<#aKr)K&D69>OTWc?mE%U8}8{evQ>u3aMMf_4ThoQ`$g$i>+S&WOU=ck#Y&QBa^h{N3z z0Qhy625v~pPM8ol(E?z6hhNja2!M|mHPL3fydLU`QTP8f&hWuZC!v;H3nPGd{>XxbO%rAsh@f_VBEGQoq;MQ()GjT zk4BKVul!=ifo}2f6avRZ05rg7RDr0NTVG81l1Pe(4#C z8!)W6r#wM>{fCzdJ8)Ab)~yh!)L^Hc_~nEV*;ai0)ta<*_PYT+Ka9z$-x=x|FnwUV zPSW+$_x%H95wxbf8R@$6VanFbs;8f>=0O8MC}QSLlQ0rM@^Mj~mh`&=Dl>2Pm6YrJ zS3Ukr7l(GA6e#~vR$33+tabP6)UWN&X-_Qx<)I^k+b~ij?Z+J}r@rke0dn;~@x+xd7;cUyqfp z_%f1$~GN>RN7_73Q&w*k8APq{Um z*KJw#Z+@5OV({O>xrHfC{@0b+`>CjFz{~_}($fY^{KYr(|IG6GIZ|F9*a`2a@2Bg> z&+vN%oDnw|JW!8U=MPEH{E_T?%Gt|c-Ws^)0h$Z(0=IKJ69JI&E4(@(0U$9o9HDc= z6`FHtIkU_emq@GpftrVM18CeTFRS{XRUym9qxVuC+SwY)*@_v;mv*jG-cbvH8D-Dh8q7#< zAQ2b9C0+W}4^R9oTGKCIQ|{;U7&lKi&N7^@oc=7$&G|J&1$Nu6sQ(>6NkcuN^p`WG zn{GIeX9@v z04WA=v?l%Phqq~l+@x>Hd`7mHVqNoE8EKRb8F< z!n-YI+XX-${Ac_-bqT=Ax=Uh)9P)MH34_T=+ZPBhP#aGA8W!+P`H81KCMD?rI&SK~ z#w}-rz%Bn(D%kTi{xfb5@d3^=E>t(pEqTalLwPZ{Y5#hLx(Cb}AotUyo_=_*tTpBI z;*ET09Nz6+>}opayzbq z&-|hAWeFUfE4k&pr3{`!;Q0W~J^Fxm8Lc2lS)vgDv~|#pAR&EE5Fi}B_F_2SwYd@7 zEd;hz05sr-Rp)N^{K+Z*lmMVhFfpKR-3gl|07zS{nVj_1PWdU9a3&c_d1_HqefcH= zz%3OYeMwJhY5TxaZhgbu1k}pYpRH^<aaK1o9oWpB59iSEOtpMS(fIcHUn!(iefbwXctj?m~sM=CFwowz< z76H%zU+whp&E4*K>W&z=1Gu`-t?alf@EsO+1$UQ^ZU@ZJCZEHZC_o~$&j81bC7B4rGf)5a-&Y6ZtHaPY3j*Ip$0!8O%6fOiY- zc$L2Ce^+_4LK8#Tv|E&NI^mFT#jUOoS<{hL*69vacMWqnDx-T+kaY4}JBgdLS?O_8 zCutI1sl#i$>#J$5|CMPWpq$$x2#%U{hQP13>G!&6A&o559i?OOL-F>*5?`Vx{}Udn ze6@&)BXKpNtJ@Woq8t`~lb+95cMp}+TKRDb-L5Qe#?`Q{{qc)-WfGP$n|M1DUfb4> z-n4(a%Gq?qHN(-?pYiC|pYW!9#&>Frm4`1taqamhbE7-{`gnB8qgn|&DU@5qH@;N@ zK&waWf({Dig{S!SVejtJm;HT}9|bEv`01}rr44Bpw6&?SAJ+Q&Zt`p-j5K9gp_5%l zd)RZL&HiJp+_GsJkVa*V9P0q(WN866(LpB9zvoFuBHgGJKdU}eE$PzHDkNXh*H&6p zB0npBYmn^JT?d;n>@5It&@M;3+F+VGoG!V8vAX1Ur;B(3*R(1y4^46Bc%6I zBWO!{C?|#wka|mjN9EB}8>Q=#QXi%ZC|;LWol|j1U*iT>Oo~gow2S^712e1sNoQ-N z8?B#o-9Swm+${o=pyHx`J!2lxpoJm?=I<~!I_o83i@U6jzV>SUjs*PBnAOjZN4u7H zDP^6U+J@CtIrO)xx$G{&WcU?eE)w{X-#<@F#ToyS)bk6=kX6)zg;WOJAl!a|fzqSgRR)E`FVd@UOppW%R|D z=(i-lQVUX&yT0m_(diG~?Q}*MQL?N0X)gm^1+%T@cmEDc03aAv^+Lg6Z2_hzX%tt& zRX{zo^8$dds@DCX?eg2*hT~Qoiy6NnBoK_|R?067@gt=66DCWV&%NX(ElsYemf}&Q z2h;$y*{TqzYio+x5@j}G)q_CUdzQshcAr{6nivZJwHskjg#bdqKlUlXkY52>)vp0* zgbY05=xdoIRfd=HYWJ-Uh7q7*1TGJz==?(Q`#*6okOnwffWb{!YR}@GOFc?d>g%lz znwF^xAwr1AlyTFTGE6USW>_!n8lXF)x=r8vW~h#Sn&e~rF!2}Pk^iSZv+^apUp|A; zwy$*)rSyP%rcqp(LD4$5WzGd0U4Wkp0G)OIZhb>;W%TuTNeEnjcyz{(?j4=^V|&E| zlHL_qnlAIsD!=sy=vBF?Ko4DBE(X07MlU{?D*SL#1= zGgdw3ulWhE4RQh9KOMF#x>)1tPl=IUN9Ga+MZ2*u(L(pR(M^4QmtkUrGm7ht9BJ1z z+o1u_5T+i>T8yR9*j?D~e)&B)df-9|K9T@j5Xj9;WiKp+q#0LaI>jJCjP4I~Q{pQ%0oPu=sY zYM_Z1%lPnfKlv^4apZ_)V}orq0aAOEwyFUftpH$( zu}mqz$D&mP{CaBIpZePKz}I}&p991Q0_Om50T}>S-vIjxKMAg11ms^$OJA8)&dtmgX++aIQ-%O z=b2-*W#CmF-8o!=47d$?(Ip+_uZk|OERU9CSL9z>R+xlB1cC(t0)V>&g5zPW+Gr#O zx7z3fMuFWeI=tw1&C`zBmLe2N9|Qoe`fd~LQke*s_dn|+_fr!ORC`b~LO^1NT5TCQ zcjZJ10N4sU6a0$f?)jb{yYip^Hw3`AY6E~DAa@#o`efiE03_^u%^fO2Kq35GxPLMn zO#mUlstZpD#0B&kRKPFBSezFF%xe;GFUl>tfKad&KpHVa)c^`lP^MM`Uz%(*4_}&m z(IS8>k~HE9i7=4cdq;(GVJIhN4bX{bHePGuTL3evtv`kK=#&5;ZdM-3!@xEhFdbkg z-5Ji+FE5%xQrG-5%d7c2;FC{zI&~j_aUtNj1hN1gn9Tz+fonnQ4wlpXMJQnvbfhO2 zWdb^HC+i*`z;glmWdVM9MVz-PyR5|rK_Iu|=E;@Zefgursx z2!g&aU@NeD&-~s3&imfsL8;7>cCh#~$Xh@>2jR_1wuacnIGhO}US zQiiyj_V}tRw*^!NQKPd4s5MU6iwQty`BTREo3iJXrz+b?&DC#f?2Cu^MSpc9xu;S`6 zUIaE)ep|2p;yZvZyC{2T<&d<1rO|>^b_;|h@u0Q^W2I%gu)4&ep?0cF@9p6|YNlS< zU2YF;nic|R09*(T3pr87vK%gY@X?%cWTj`-$HG~JF;dv0HrufUpixXY!o+)i(Ww4< zS+o1W543>2k9*rgul6>8=95c8z^krR?XtoHI8^^7z3p>Q*Rr$#E})q00ctvVy`M+@ z*UHYSKR>SEpVu0Il>nGmIPL|-c}pN6Fvr;dW9T-h9pbzW{2F5eUu}p0SQeKq&*K-Z z8Vae(2BB^dECj?9Xf=hCJ4y|m0GKg#l2Oy9@1K!xI@5q}v-z@un->Slt3jOrND{!0 zAz$1MkhkZr!A|GfyEWc(qDZI(0Kn%0`~n!DZ^2){RY9f?3>MfG0k9wtS`!d!0yy^E zTyC4{WAD#OUIai^dI5h`z?Ve;*u7i?0sdue2fP)K;5f`(1PKw^S=tBuX&<$!Eo#qh zT^3Aw_P}jHaIv_lEwslP3F9S;0Qk+vzC(|kdF5yIzqz`hXad;^upK}kkdxWkvIcOp z0wC~#Ra*g|hi9k0^AGqV4E8+vcOHF}!N6Qt1JFcxUoCh6G^zT_0>3Z)l+V(*Ovut$ zajeEVCm_8>&oa?!ztnvT0KhKLFV0K;S^#Jw7UV||NB~3#bQ*vd4ZwO&FQEdc%i7QS zk0|0&{aDT;?52&fv{hGW55k~a-qINQJwc#BK<2=MruYX;Gy;!NAH2!m=A*pUz!pO$ z>1M^x3a@3Y0etG{gF6C5@l(KW%Wv`sOTgWw8Tj_e1z;;^y`L}el4e&_eOB0AdO3gL zuz)67z%MHQE`hHV0B8V32$UMo0DQYom6wA*&9+v1=F}^{1i--$ihKp-xMf;kINPRTN>G;(dyY}Xc`t(KXL4XZ;td*vhE{Fa91e%u;><5aK@`!u+ygh*Nhz_>8ME~u=x0<0dgIS+6%X{lNUXd{FG zHH7p38`1we2dl4Zd_d!!7n+H#|nGS_ILE7Bgqst7CU zATRAh0HqyJ{5g~Gc0?Jijns*L-6P(tuNkO2|%&kY-Va+wJj6#Z47)g)GI)c0i#Z>IrhsQloMQkID>0sz2U07wfkMG~`W zNH8D(ybTajMX-1^5IZ0QtPY}q0Dpm-m7j}=UqBnt1Rq2Qs2xkv$XJ<{rJ2A0I z05lh>E(Da?Doq4Up!g6%Y6tu$O~L9vy=MRK$@XwC!MR+(u^S#%@-5ZDp`Y(H7Ov7H7`>-8CuwgYTK7dQ*zj+Ew=GBn|0 zCAC#`upj_-h~mb+2uFrqNrtv@5z(r);+;@A!W zka}rU8k>eA^KnC4x6{4q$FF?uv;N?zm$L_FwLe@B=2ms6my4m|Rvs(=iYC*bV$hWb z-suED-TNWv;A1fDJ)MQx3mR19fk5*tc8ZIGI{8AS|! zOyUz>LMQ)gI`R7EOv+0`+T4WoRt2}!M6pKo-F`(11#S)(dG#Rjarr~w*^bx#EI|FbllIm(Xama>z~UI z$TDgzMp`#<*rZDr6m)cEI|KlYX)O)M=8%G?mvCcoQzvd_w}U!N%&KT$AWgY6X{!P( zpyvXfeh^6M5E=ji0f1#yx4MQFAZsl^%z^;G-)aDqYbmUJHq@=s5dwgp3keSVt+IuA z#F$L81&4Y}TW!q6l%YUILI$_DH$s5>g(M(7@g_APiDPP9%1H5DwBkD!V& zJn>thsi&Wp@R-(}Fwr&O@29JNpM*OfBRaVNRJ4G?5zI=tP_LO|Kp(qX;J4f0C$4CI z(E?mvoj!hEb_UtBT%DBQWqMBHAMg|9$Z#gi`oXVDfF`{JS0SJ&H>a9`p_K) z!n+m(EEMKsFBBFL@Q}D3p;1k+(qg1_CqB4h)LNTzwnUFs0ML-i{%%wxRT>N6c`A^T zOX_N^#rrG%uPdJO?Dst5WiG`+K+~j&VU?U0Ah~ithuROAuo_vV)1b?a^`sxGGZTiY z)@=tCiVDbG0N*}B15g^$S^%)h$fQi3_TqriA^>$zz(17Lo{6JAE}Ex&P*^CIS@n5> z+D#Z*%Cd(=2??g&sY^mY1hndHdy0tsf?lYaccQ}D&;VKJe_ zx5@uUdmE7IU{R2S4Z!{2kNi9j;QRlg+y>08KtCB+<4L>tocCUExu49V6iTchH`wJ4 z=!?Ik;dz2Qo^r`szx0NE0JdDw0g&|4 z0k|Eh!#|!#C{HCEkV{q{BRrtHGc6^YG#nUurO0i|s==UY7Ga_OaM4E${cTPBp~N-r zDz}_tjUY%lP5K1&>4pRVO^g6&3Yc2m+jymSxwF_Z;gxnPr#c8g0X!fdYXvsQS$P37 zyM3+jd&Exc`S(cW-=lAW7pgGD_$+|!fG)NG0H2%)|Ht0pY5SYzch0M?d((b|0INUe z08pO+)dzxd+b?Q=c}y7a^DvNR6CuE0Fa{#@2XEUi#sh!Sa0vXehrAZmKVawnes2He z{(o)*;wL`O07N^W##*bYb|VzBNwZx70CoeqtOb1`kY@q{{?G`4JUq1U-1q#j!9f-37!0}>1GOpq)d6RO>1U__tH&)zRAA^@DN`0knei&$?j0+fn|tt>{Md zZGV5|{sR0W1S|}++FLMqzh6MlLqG@&178B5oFk}%{4qfMRgEC<^mhjo4@QZ*{2{M- zo|*!Xv(op$A0YrGt`B}R0!Ed-xIbGJ0DyH%h6)8f2G9+tlt$4w!s#Hag;0o%zon$b zufhamsZOlw6x~?>4%kIw1AhC-=XU_uK9FBb6tLs=nj(Rq3qkSdv!A$M^-Y;ap7n(N z1ibA_SMFzZ*6D*URwH(aE`ybv!$7PA31QtK4}OJ@wThrluBadGkyiw*3SqU3R%-A2 z+jPX?Zz=hb26tnI0D45B(ByBOF00?RHbLB^NxD95)5Hx;y0~3NSvei0{viJ?5L9yze)!z2Xw;fOp6b zIN(8VM>O|rN&UVEtA5o={nRI#0C5NeK(07%4=CPoCCq}uxHU$=M>GHgL00)t!idL` zw%rEM?pW$fKNP$4eBW@!S1vsLg`;Q8-+*2@6*lm*=V zFa(0CwnZ>BZVLfv0u}^N@8<+pz<z&SkQTn2%QVD%Gq9Q0L{9`!z2K)LPLYzG?kpY*8x+<*V+v!8cy zZ2_%lTSDzI?a0pk#%Eu5>E!J|iGH?ifV!&stmej>O6vfxct2B6UlKCVaaT{nyozfJz{<=2-0$bC zpL_Zf^`BdPb)R1Wzxyy-fypxf<%zr9qhGwsivs+#jM`(eDDF8P3 z5dbtc?Qcp|iXsRkAXxP+JlH1ihM_EiAz_z3=w+z>L*Q>!e%!6<-)aHW+|;g4xnT<3 zqB&{-K)nK9*s&c**f~Og)x9qS5CE+ZARcR>aq0Un{13d<=NEajm-lHbey!rx22cUJ zm0!73a}Ed*41mOHqTD}q?gbYsMHnDdK^+U@PTO1e+^=1C*`=HXSPSq=Nkt1dG^ZWE zR{v-L2L<{A2muN0gX5wdXy zNRbW#QVfW6NB~7TDAM~M5s)r|bV3(Ir1##d^r}DvR0M?3l_I?=9h929_^t1K>)y5A zee3S5o!RTm-h1{rb22lTv(N9`%)RgEJKSA;4u`fI(Ll2);g{Im-wSU?ah{gG4;)hn zYgdfLV2=?+j93*ybgZ~_Bdbju2-guMG)vjc+8sLB)%_G;_$B`UzIjA?5VOl-ewm+! zD;%IoFIp%5Reera9&F#9HGVRoKMU@jTR+M*7ysOP`Yo)A4~!&FXu;+-MW2tgq~4(L zH4Hx28F|;AXnyaxP$=mxrC%~!Qk0Wg&M2j24i?&}Y5X}dRws6jSy(P}UzD39GeB_M zYw~cXboXgR@OICF@xt$AORt5e?*j>$2YUm~10H4?x8(G+nmecY>n1+ZT?;mTM^P2{ zBtfW3NJ#uh&(Mft#^afl)pEt+hLQN~g-{va1}3$dedI@u@)nr1|j3(8kvim02`5a)AppYhSyjl%R-pozV?+;egLH$QXhFonCP?vtBr+ zx&{RWODJQMS5krx#|DL=KFMjb{)ClK-+u74ZkaF#t|mv%{sf%gyU7pv1cFxfj7f| z9Xr{+sv$FkHdv*_;QvX_cCX5Rw%7S_kGG<@d4THQKU=u0=)jjF z=Re3A_lOB|_hpWqCh=|2ubwFT&18GqPY_1KvEN;8tKAUewJxk@g2^oo+|>eU9564E z`z(Jc+v$oll_U;sXSM!hWbpk@uiu-C)H2Iz`3Y^pToLPS*Q>s=oM(QC6>>Xham53F zu%%z?v}JK!I~i^_Ar0)RNd1rAPrMCsv*_k=Um$-Y)y!}4oqA) zZ|kU;jNi&U~dHk=Rd`wAa9Bvc(HMdX5Wii|VZE;-I*BF23x#)mK1%_0;2ge?)3#beu$H zI*eb8+Atc)(0ORJYnS%kviddRbx^Nu7$mCoxiL1t>86>cYll@%%-1u>)#xCqEdc((OwlJ%BX6(4ky zW2)>*@p3#{6VkTmA6(G6mNx5Wvg5f){B__BLe1;~>@@qKw!^r*#UNj=RGR@z{tT8G zGKdz(7^B!vkYGSVno()#i_g`4ORsh!zzjeG(#6c2*Ex=AblMN#WI?OeYp7k=`AaETk5mYrr_*Lt)nKGoFKPEMA?lGk1DVJ`{-+^_`ZJQiO zEd@!Ws>oEoeWn+qs)wSvx%aqz|7oM~&h&9_4;pf(1s+806m>>yH&8fwNOsfUp{Ugim4COe@7lP- zs17`y|$y6xY9JoOQ1 zQ?mP*brGnT(52{f?hl74CL5STRECXz&m_;h>{*BLfo4ZATarb!5Pem$U z#lnq{z@##nme;0X1X}#tM@F@X{h;^vK#NvuK3ZkV?{UK0v!!!4?s0D3Y_0Km)D9-B z$cjZ+mJoV&zQq~n^4srtDDla*Mc~@Fgx2qk>tOLt7G?n!*kW10WvQ@rY&N_xdfHWB zNxYw;r@yzoH@lUwWaQi@w80<*4G6hV)!m*3Dw>DnaK%NDO@+vfrRbp-V~L8AHNW2r zUGKV%#Qo6q{lJPmoVjL4mXA1mXgQPOeLPFROk~^Ax8Dxo&eryDYn6Gzf?_d^04&3d zlRxx=3)Q|mfT}M8wK>f|sAvkUbQJ7HQWD9~V|%PwjNAdUGf{M{=7oUV33&}PobN}^ zau-L((IC}P9FSISq=bo^&7TfQHT1`|fc9YBxN6xBIL`MOYP!%*iDzWQ>WHKC(-bRM>L;1JZP z)Kl)TCE^T)^wbP2!E z{dFd^-93tLPrkM%`rSyUSWoo*n!OQw(+rEPXgBgB14fTuruG^w)i*^=c3Yes*q@XI zej^|u>QfeZXnoKTpII#4K-oe}Zew06~3Pfsq*EQQ2X$|1UFUy(1velx5`6khE%K(rrht>yfUt>i!8owU@ zG(w%2clay*U8G>748@2AuZl2Ju$%(N6M z^fm81{pol=Li|mI%sLpAG_^pA@L`g*FzKcPwA7qo*N|HXa-ANM!Mjb1BbcZs;7FK) z%hqkk8W&VtOhh{;x7^IoRa7gQ#awBxS3#q=WD-+{_N(NF1fdHN}u))e&Nau_$VsA`I@@7l0s}BAgV_-!mU%zSBf$|fAyQK9YFfKECjlnIb`8( zQh$LouzyDW6t+lgg7h?k+K^&gWzk52k-D13}Y>>4XrkOqDQRzQ7~r{dhsYn zCDlBWn0S7QlE~#aZQ}tYRsgv)-4yGyt###SrD?WbwmXsZTWq-PU-d;#jVJJA6F;WD zkVr~avX18l%$smxi!=bpSMFQJOLEzyB=ur>t?dHmPrL^rU!f7~U4tBN-pUudee_Q# zThmI@7+W?c59$#kt@X)egx(oGXucy6+4SpbP>eL90bGUX-VA5Vzj{ygyNO~^?9q?x zaYpO3+*c!Jo(@DoFIDY9xMr4;4e0!Wj-Ck(u+Kcb8Wb#Z@JH8J9WdquJr2(nZatMg z5M3CzN{JUJ!^`vM&vyqAfD1sm`$BA^C7_IDH=8jJ0 zT_lW#F~q!yzNay zm_h~tDIO!$YWfr$t?&yddYqR{;!G`xfYZJGp3X}S{c}X75f!(ZIvz<=78b1;kd=$L z3?$?ns~<^SEJ?3cz{$bxg-b*Te0f!S`x$ z2lto7_lpPbKEX0Prc-s`@Al#koKbA8v}HpNb8j(@-ZTMp+9FUCgi+2nTseongnQ_a z#(Yfeq}5Y>P&a7e|tF`%OpiJ8usG+v+)D1$CPlB6;K^h#MR zX27walh6o&A72?Y{dSUhp+21dgc2b3SKRqs*5vhs6LY}~P%6-XpK{o9!$W${4S8n4 z1J+o2mz@vuUg&cwyVU|QuVTKR8g0wjqDw=hTWc>)AMeh#vx8aMXEM`Yn?l$Xyoa0o_hg*{=0AQh?X2hzP3>u?)h?1rTG%;lYp-vp= zmA`}v^%TJ2vabia%i{`{Z%uq2`@?{hKXZvr*o> zw=<7rT#XD>UwP!PoUv?((;97k-(w40JfdQsH$ z<3e5o4vxhlcDCwyASwerBg^d@ln`yE>m1~$!kB*1?G|? zVW$Nm$WltXTP_VN(s(&8aQ+rg7}_RY%qC=6h%Sx9df(KmDkob@O@J;bnmc$M0giu0PnaDBETz=53l_ z|FPJ+JZ*svS2^S3Wm#kBYuKlFaRYYp!eci6EWbV>^0RSf$v*4G*~PvMh}PP`vI6$k)K)qx_l^)L6=PP5HwF7-S@=N^T&n~EbeS7*SyQ1YYSi`mXQD#Uu zeW=FHkiP%3lm`aBk$K`5XlxE=?65%MXzCWAV=U}NoA*3LwmSBviInpq=ymf&xmI#y zL8N4LP0d=fu-Z@%xos%xIj zJgzHXp3~A}O#?}?u@G(|PaMX}*w`k=YaO&}$4RTg9k^I~c(7D=!Nht-R|C#kTGrq~KL-R2;`2 zF0cQdKTEsSvG1uH%yr__y z53MG>D}G?*GnG%u;%DuH$f8#YY?xvch;;9H=}9ynOKo=Z*~G8ltPKAY<=6YhWI<0v zBU6uk#c(sGfBHmp=D!5I-y&fEI)_sQg&|t>LC&z(UmoYrs=KTty)6-hJoPLF@pqAe z#$KFhxK?C#-#$++GZNt%L#6ei4UXD^bEi{OKsB_<-2Ht8nQh79LX53>@w z%2vEtz_-BX$6(M(w(lh4#S6qB6F)3nH>~9xM8GD(KZgIFkNfqJUwDPQ`NR{KeJPcR z*3@w+Y+_KdR(irv0v8{vxk>rpW8S!!4-2bbv|3THYdc)|LD54|b>O5Sme4FFQ3;31 zh7~%sxWde=?#^ABqQ=P|va+sHR|QuCbzP3@UD{bcXd*xAo}pPXJ64<2SsXY{T}$h<#oCNQECC;wAGl0 z2PK5HEK$?~4e~ksz1^`SIM?fySqSo_Msg9?9UtFlIYMR@_(uLvjY94zU0cmz)q|ik zD>uqGzU=iotksM1^b#Alh#ojF(8eZ-ayUOL@AqlfCqOMgHmBfBJj0xAynwSWOv#K6 z3f|*sYo-&ace(Jp%HBp^s-f3kU%GUq*18j)Rzx|AxwgxzBqP$YIuf_;V(CPIO$msY zE#@|f9*ixFbw#OKB5bCN&nD-Qf82g;OW30dVixsUR@Y03W*|@hh3kg{Ysyg6QlE(( zao z=_LFTYZc3D_W=Wjv*TP&+P@fHUp){(d) zTnqPyI6FPu#Vdc-k7w)ii+AS}BZng3F0EBF*H>t(mY`;){BvThHVY#oCywn335-iK zN8PqGt_b$!+>+MtXzQvKzYPS^rV7l40?J4jS}r1;mfw1+0klge0;RYlo8fDBfzq36*9dw=` z^_VqY5i6FNsknkHbo5DV`a*WnKMi)LVjgSuRkKB|QQAq<*tD{Mus;KW(fhSh*e^fU zW+RnKyAagRgK8BG7LD9}7bD&1V;F5XUoen50Ha_aIG1Tzf2R5h$iw3vr5lNmK+5lc zK(CHK7Uf^2lxd&_tOzx9?CwdC^tHP3jL;UN&(Q!1ewP}!H2%aNiW-hnS{Y_4+e0H$ z1IXr=vgPmD8C@8E{5n2R`WyVTlOaDw!vNmgqWgne+8{8ffE3i_g}K))JU?Mt_fJ(r zKvWpbgwNTlyXTiPTwV7uQcPDC;J`eTN6{z+jp^3?S^JM}bK!4b5)=ETpQ^1KmPy>^ zub%Ty1Xs}loga3o<|2v3)||Z``)?!ZSYhHSJ2C-d4hgsRs&`p-GN!Y<{YdUBTD*V# zS;J7YTY1pZ_!@qm#!###9Ov*7Gps39SKkaWl!T~O3HkrnfAx)#i`vFo3FC{FfJlU7 zQ?zANuE)4t&F2|eaH22h+wrlc0AM<3{6t=b;c=3p*`{E4*HK@V(((CG&|Ze_sTF?P zsM+x-C@1eA_p``J4#y*l(RU=r`sH3optj?QZOD=s33l*&&nTtwV+9zksLyj&&n{Pi z#~FgEklXlyg3&_5E56PIU-z>`halfkj+IAtj&jMkKG&zLi>lMX`;U4#cG8=7B?nog z?5auRKY&_qg5v8psUF?lS05V$wuGZ35TFqGcNL0zASlYC*u z8eV#M$jiflyIXDo5n#@Fb1fb#ACLy&Lc{VW^YW}Vq^_O5hJ}lDe^%?iT6jL!nZ&Kte$h|mpkgA>EmhneAjhMYS z$+!sch&owt%6jyV&dzsY$Q%Gc^T2?vZ|x4CG3aH0f<4eNz>EStJe1LTEmn@UHk`Og z|LKGnNgoCPm%b!KFT#Oj19?ddl|k}rqFi%OaO?TB&mi|gN;yX7LN>o@fj(v-RC#A; zn~G@hWgIs&W(vCRks^I3q~23z`wk%i4EXTf5oCJy{i2qgWyh59v;-uq0g-NduJ^*u zq2XqFuFCoRp3qj@KsNA0zVi3)+rcVubWqWoi0*_{v?3!SJwYcI`CSNEh0FE55?Bju z0wh3=&>#2=aBIl1>YrQU-Qsio96PNn95B9T|o92e8wmRvtV zuJ2b)GY08}nS^3p%YhocuF&_@$Cg(jB zFNw;7GpRGU3WSDUerLIx;%3x~&JrKn`*o4j#j)`&0MwS}PyI4b6|`1a`JU>el6t>J z*+5AiUn3%uV?p8(;Hcq3!vfeYi9BSMY60FgF&IxosRu@Kn%vpz9c{?b8+HIPdc9p| z6~|j=tH09#PGyRm@Dq*0St|dL7dN-QATPT3FG*N11a$5_*d|c-f;eBkzgMH2`{pdj z8FF29iYPy@;N0KtTKvx(aJs^x>dxyr0jr znt0#-L|6LqpRb&{hYXujHE#;{JzvqAhNGaN*0qhHV4Mz8VC&S1NJ;%cQzZ9v%>MB$ z3digK%6u`iRcgI!BUV5F;ho>h_lKX~2BBwslAMKrF`VJP;Fma=^*kcHD)Vv&5&D7k z_1ozFZ1v<2!ltNYIwpj^4I znEO2`$%BPE*qXQ8(C0Dv{)^sDWCuV0w4Z;e1lJvHZG8*yV-d-p1i;(Xp#{eZC>wG; z{1!5=Z}&||aNC+A$5pgYr{{Ddbr3?sO7LmL9c~JKCxsOokX|cTk*8kTx;iGl4y(sB z1LE!hqiSq_UH0DwV|4M3@;+d!J)l`cwimo*JE?k3v2S}|PhLn=z}Jpn*z|vKVrw=O z2SX^tf|ML4KBs~yXUV!Z#>&1>NPAlx)LZ#74~>w*RvOQu&a z#=z9zmoUSin&*18&tbGBZ@l(Da4msg={)g;u!0WBcqG$oEwUJ)m1M8`mo& zIBO3nKgLmpxP@##DeqS04e8$)kYHR(iw2Nb#cSwz@R(01|kw6E_*I*s(DLT@yv? zh2tOGnbkw-6`7zxeAgkZar;Z4@Rgt`3M75n0ATUq*DCeG!OEy~RpP~VF#Gc_vmvN) zOg?!@3P~FTTkUh8l2m#N&{!D=X3@l(mpfbn&e|qNgANL^?=~FOy3}|h3iKpE5rWy1 zdE-}tXwROJ2t;{i$PFdOSJwMe70MDL_wqs6_#NF@qghoe`CWyM-Rpub_UT7#BB(Gp z&-|1&10d1}S?xM!o#Ys(y|#8=>!?a>wH~0R@Qe$(aQpF|W4$FLpX4;(jpg_0b>eam zk_F&K6GvVqXB`8hDwncvf>MYFuJ+Ov{+#GOtSIkz5LoaXRww(r6`%oP3de>gR0%gy z!vAlS9IWk+;zh4+XUsb;91U$=^yYWC1tTdf(A7P1ZZ#PY_AZp;1ysqaqJM_mw6y*_ z+FvoK+ng!tpDevdej0n zxesS?ee)dMwH^1nFEU!`ZSt8QmNW&$xi7;DLGCcj_FiWrWm^ENwl3+@@3%PcSOt#^ z!z(I;X{L{WU4(@qj$<4UBe7vxRq;hkBX531Lj<`kEp$tV!c1k ztq)$sNo^>2BXu&GXjo=B_D<>cVj=jk){yd9S8AcDxbNTKggi$xfn78yX_!s)o*NxJ zi_Uhh%2BoZ#aFXGgBDuGjbqLr!xHv)YLA}OqzX8}NruS-n4Uz~R#NCMK3Z?8Cn9l8 zx#viy-^|%lr_PZXRC;-86aaSP%kv6upb%(XFTT7mFTQ?Qusf?YX&%ZRw5z*&OLY3p z?TWF|77L3jjVQStt<2SuY_`T>_%COWgmlVT2*>q-m^2Yo3#gqeoJ4Y1+qdJCddw5DaJlW^Ph4Qzb!mYSmruVOfX1>8J505- zjXY%^H9h(|ooa*Me`8iRz(RmRVb@9m6+ zCn`QwSQPHmET$ydN%w|&Ls4=pALqqw2A?CCR~XRtcc{X8+@p0f zu@q!sQMrH018;t|yBI>19Npzw*s>DKmKM6T_^<=GoeuycnzIn_J!W$PQsGqr?pSE8 zcuuk3@Ny2Ki_-XLU)9q}eM!$tRIx#Y+!MQ4>(jeYTGFO(l-J_5vfCOWqXaZn1%!hf zlL#}_R_DZ+LvlpWs)Y{%Z2~YXoSw9CYsE;azCMb33ZHIR9D**<-MUiTGZXeUiU-KH|#yuDt@AUWhW7dn{aAg21-W@_bEfiGh z2nAdUuC{`qoEschp6CLz!qpOT=k0J9T4Wx+IUX!<1s=k}(W$CDYWO1N686o^FufjE zCs^scSP~6FZIz;^M*yMAl#v`wrqc5mU3?4JhlFBl;1Ijk0=9}bzlR!%KeWW^`2P!$ zJ9rG%=3*ikYf`dLaxbXnoP~|tgl! zAjic@vT6B_?dZC8p1$|C<`wq1f=y^{OJydh;atK_MG)Dvr!!wLp|Gl>IN*4}?Uvyw z=emNo?q|Er3}PZY&nuY`lG|M|o3?c7QV zm2{aAf9Oiq(n8@Vl@d=CYy~|2r7Q(GKY=PQn`SnQyUx%HZN%9?t%ukey`k3huv&BZFB!SA!(@3T69AOa4y=|5t1@$@ZR=bcm&1NVW;2Mk*7$qpPNQ4^9d zGl>|Y;8~6%g14EmAXK6NU1R4gn3Pw`2}}S$^3T^$!x5&`0$UF<071Kl#5=&{mf5GU zroPcU$yoP8J~xN+@$;3)meliuRtcBT(i|*T)gs$C2k6Gr&l5ELH)Y&8nK%<^rO1gX zl3MAd7~PK@0f^l%6|)yT`Kxb8R)_;=fCnETsc0~@wel-BPyClz()^~74g%yi3;EL4 zV0BAUq+)oBYmaHxx#`SrDk&H8k2$y&S0bT*=uG2p&%tmzaCC?|Wzi5d6k*83{;BtM z#L`7F!WlF7aIPMt?a-rNnwVJ+i9;(Y=Ub>xqq(jM;$O2IPT+{u^ad#19a(4Y%P}_b zxaj(1-P%E><{Qz;a-sc+PuU}ug5|*0KIYeFz-1ta2MxF&+$9YGN9PBEuwZIx5T-x` z*y(XiQ}?ala3@}zP?Fz?Hbhj-(*S{)&&*<2HNOXD{uZeG$#0Y6xbc>3&ZC++gbh^p zL@g3x8Yv6w0Wi>050wiPDN_R}L>isOa3`O{x)fFL+)PX`OdJxh{RU9WD(o!&dgXJN zbW39Qw4h$Fv81g&T~Qc2qHiaL&v)Rnd+r1 zXOoM;X(_p{|19T#tM<%Y=^fO|duqjBi>c~V%=YX4TRi=G9Ti4WH z)2f}+bX9lyC5n~KR9-IskF@ij?wnRotN&f%0jG)0J{U^mBik%k&@u5E3#KVw*Lbrf zCrD~X_u^b)Cuef1$+x^S+DUNN#i^TY_Do6|EXRUbtaEl-ym1GDg6bharU%+MdoS{D zD(VSs>f1Y>CC&~rFm37GE-ul7Kq*JMjes3v8S9M&F}4skip6xwz4HhuA*U<^Cn?`s zFMby^orcfG4_D5UnVA;;U2KzErGoZshvbbV@76nOyI7t#m^eWCkcq98ULn5ugfisS z=^g85`P7q7eu)05=?t8m9GQJ6gGai@?W+s7aTNgsQopBj_pP#Zr~pBxCNL*~LI3(D z2v7q5?}><@3i>zymH$@+0)r?RS+1E$Xhta3Z$9^53$*`~E*p{vHbqp&{bD zQ+sdk-hwHfTb)0KOYO$Xd-JCFzhzKb80TLd0{_+hZ$!&Prz$Kf5==o)`H-re?ym`kixQ}=)b!E)fhwsrbh(IVzovr@amsT4w!fE;DI9! zJn;M9e87(9Se3tdJfLc|rT(ug$;h|JH|z^ft8z@f&75;7x@GY$@=71A*Nr z@qjCVt;z7fQ^GcY3J*_UIfXJ2>kw>M2n;5C$mO3Zw<=ZY{)CV6-~j^g3oyotCyTj` zC&TjLi3_je8Hxn(z&BPrfMvncVTJLUIfhFLjw)@0RwO4d1?h99ZFmDA)?tE95h3;+ zHav3(Kc0C&3{P4}sC&z8JTOg&y^2t`A%fppErxJ-bA(6@$h0cJWq!d zo_kCd&wYx-bN@l&dD^7#>>nlYpi~AtWdqLzH7Bh(;kf@H2pN=<*9Wl$&%chRf6a?` z8uY=t9fjbXkAm>dhe7yfyJ2{fMmsztjvh}{#Erk{EAjKM&3_#HjYAfeMri9TkU!OD z$v(BKkd-)m_HG$I?N<@r<3})_y+!N{a3HS%o>FWBG6dcK68pdWKtYrMIhY54fmncG zsvS@vBc3Xodxt!e<0p_$hllvlZGb=^YJv_g@K^VLA`%cA=pp%6z=iT0U;`@y*C9%P mE7dvp5ydb-3TFSG%>G+B7$63y!D56MiT=fZ<-cYB5BfjfDg4s_ literal 0 HcmV?d00001 diff --git a/building/win64x64/squeak.cog.spur.livetyping/Makefile b/building/win64x64/squeak.cog.spur.livetyping/Makefile new file mode 100644 index 0000000000..754a08756f --- /dev/null +++ b/building/win64x64/squeak.cog.spur.livetyping/Makefile @@ -0,0 +1,22 @@ +#################################################################################### +# Makefile for Win64 Cog Spur SqueakLiveTyping VM using LLVM/Clang +# Do make init to allow make -n to function. +#################################################################################### + +VM:=SqueakLiveTyping +VMSRCDIR:=../../../src/spur64.cog.livetyping + +# This variable is set by VS Native Tools Command Prompts +ifeq ($(VSCMD_ARG_HOST_ARCH),) + ifeq ($(MSYSTEM),MSYS) + # In a generic MSYS2 environment, compile against WinSDK + include ../common/Makefile.WinSDK + else + # In other envs (e.g. MINGW64 or CLANG64), compile against MinGW + include ../common/Makefile + endif +else + # Legacy. All *.msvc.* makefiles should be removed at some point. + include ../common/Makefile.msvc +# include ../common/Makefile.WinSDK +endif diff --git a/building/win64x64/squeak.cog.spur.livetyping/SqueakLiveTyping.def.in b/building/win64x64/squeak.cog.spur.livetyping/SqueakLiveTyping.def.in new file mode 100644 index 0000000000..070ebef801 --- /dev/null +++ b/building/win64x64/squeak.cog.spur.livetyping/SqueakLiveTyping.def.in @@ -0,0 +1,3 @@ +; Set the total stack size to 4 megabytes (0x400000), reserving 4Mb and +; committing 64k (0x10000) +STACKSIZE 0x400000,0x10000 diff --git a/building/win64x64/squeak.cog.spur.livetyping/SqueakLiveTyping.exe.manifest b/building/win64x64/squeak.cog.spur.livetyping/SqueakLiveTyping.exe.manifest new file mode 100644 index 0000000000..ba6186b524 --- /dev/null +++ b/building/win64x64/squeak.cog.spur.livetyping/SqueakLiveTyping.exe.manifest @@ -0,0 +1,43 @@ + + + +SqueakLiveTyping Smalltalk Virtual Machine + + + true/pm + PerMonitor + + + + + + + + + + + + + + + + + + + + diff --git a/building/win64x64/squeak.cog.spur.livetyping/SqueakLiveTyping.rc b/building/win64x64/squeak.cog.spur.livetyping/SqueakLiveTyping.rc new file mode 100644 index 0000000000..3c1bcc6777 --- /dev/null +++ b/building/win64x64/squeak.cog.spur.livetyping/SqueakLiveTyping.rc @@ -0,0 +1,32 @@ +#ifdef _WIN32 +1 ICON DISCARDABLE "GreenCogSqueak.ico" +2 ICON DISCARDABLE "squeak2.ico" +3 ICON DISCARDABLE "squeak3.ico" + +1 VERSIONINFO + FILEVERSION FILEVERSIONVALUES + PRODUCTVERSION 5,0,0,0 + FILEFLAGSMASK 0x3fL + FILEFLAGS 0xaL + FILEOS 0x10001L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904E4" // Lang=US English, CharSet=Windows Multilin + BEGIN + VALUE "CompanyName", "Squeak.org\0" + VALUE "FileDescription", "SqueakLiveTyping Cog Spur Virtual Machine\0" + VALUE "FileVersion", FILEVERSIONSTRING + VALUE "LegalCopyright", "Copyright \251 Squeak.org 1996-2014\0" + VALUE "ProductName", "SqueakLiveTyping Cog Spur\0" + VALUE "ProductVersion", "5.0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END +#endif diff --git a/building/win64x64/squeak.cog.spur.livetyping/mvm b/building/win64x64/squeak.cog.spur.livetyping/mvm new file mode 100755 index 0000000000..0800b5acbc --- /dev/null +++ b/building/win64x64/squeak.cog.spur.livetyping/mvm @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +set -e +# A=all(a,d,f) T=Threaded a=assert d=debug f=fast +A=;D=;F=;T= +if [ $# = 0 ]; then + A=1;D=1;F=1 +else + while getopts 'ASTadf?' opt "$@"; do + case $opt in + A) A=1;D=1;F=1;; + S) echo -S not yet implemented\; use -A for now 1>&1; exit 1;; + T) T=1;; + a) A=1;; + d) D=1;; + f) F=1;; + *) echo usage $0 [-A] [-a\ [-d] [-f]; exit 0;; + esac + done + if [ "$1" = -- ]; then + if [ "$A$D$F" = "" ]; then + A=1;D=1;F=1 + fi + fi + shift `expr $OPTIND - 1` # drop options, pass other args to make +fi +if ../../../scripts/checkSCCSversion ; then exit 1; fi +if [ -z "$T" ]; then # single-threaded VM + if [ -n "$D" ]; then + rm -rf builddbg/vm/*.exe + make $@ debug 2>&1 | tee LOGD ; test ${PIPESTATUS[0]} -eq 0 + fi + if [ -n "$A" ]; then + rm -rf buildast/vm/*.exe + make $@ assert 2>&1 | tee LOGA ; test ${PIPESTATUS[0]} -eq 0 + fi + if [ -n "$F" ]; then + rm -rf build/vm/*.exe + make $@ product 2>&1 | tee LOGF ; test ${PIPESTATUS[0]} -eq 0 + fi +else # multi-threaded VM + if [ -n "$D" ]; then + rm -rf buildmtdbg/vm/*.exe + make $@ debugmt 2>&1 | tee LOGTD ; test ${PIPESTATUS[0]} -eq 0 + fi + if [ -n "$A" ]; then + rm -rf buildmtast/vm/*.exe + make $@ assertmt 2>&1 | tee LOGTA ; test ${PIPESTATUS[0]} -eq 0 + fi + if [ -n "$F" ]; then + rm -rf buildmt/vm/*.exe + make $@ productmt 2>&1 | tee LOGTF ; test ${PIPESTATUS[0]} -eq 0 + fi +fi diff --git a/building/win64x64/squeak.cog.spur.livetyping/plugins.ext b/building/win64x64/squeak.cog.spur.livetyping/plugins.ext new file mode 100644 index 0000000000..4e508e4021 --- /dev/null +++ b/building/win64x64/squeak.cog.spur.livetyping/plugins.ext @@ -0,0 +1,18 @@ +# Copied, perhaps edited, from ../../../src/examplePlugins.ext +EXTERNAL_PLUGINS = \ +B3DAcceleratorPlugin \ +CameraPlugin \ +FileAttributesPlugin \ +Squeak3D \ +SqueakFFIPrims \ +SqueakSSL \ +UUIDPlugin \ +Win32OSProcessPlugin \ +DESPlugin \ +MD5Plugin \ +SHA2Plugin \ +VectorEnginePlugin +#BochsX64Plugin \ +#BochsIA32Plugin \ +#GdbARMPlugin \ +#GdbARMv8Plugin \ No newline at end of file diff --git a/building/win64x64/squeak.cog.spur.livetyping/plugins.int b/building/win64x64/squeak.cog.spur.livetyping/plugins.int new file mode 100644 index 0000000000..31597b9c30 --- /dev/null +++ b/building/win64x64/squeak.cog.spur.livetyping/plugins.int @@ -0,0 +1,39 @@ +# Copied, perhaps edited, from ../../../src/examplePlugins.int +INTERNAL_PLUGINS = \ +ADPCMCodecPlugin \ +AsynchFilePlugin \ +B2DPlugin \ +BitBltPlugin \ +BMPReadWriterPlugin \ +ClipboardExtendedPlugin \ +CroquetPlugin \ +ZipPlugin \ +DropPlugin \ +DSAPrims \ +FFTPlugin \ +FilePlugin \ +FileDialogPlugin \ +Float64ArrayPlugin \ +FloatArrayPlugin \ +FloatMathPlugin \ +HostWindowPlugin \ +IA32ABI \ +JoystickTabletPlugin \ +JPEGReaderPlugin \ +JPEGReadWriter2Plugin \ +Klatt \ +LargeIntegers \ +LocalePlugin \ +Matrix2x3Plugin \ +MIDIPlugin \ +MiscPrimitivePlugin \ +Mpeg3Plugin \ +RePlugin \ +SecurityPlugin \ +SerialPlugin \ +SocketPlugin \ +SoundCodecPrims \ +SoundGenerationPlugin \ +SoundPlugin \ +StarSqueakPlugin \ +SurfacePlugin diff --git a/building/win64x64/squeak.cog.spur.livetyping/squeak.ico b/building/win64x64/squeak.cog.spur.livetyping/squeak.ico new file mode 100644 index 0000000000000000000000000000000000000000..cf96e91964f62b5df614b9b307b740e375553041 GIT binary patch literal 1078 zcmc&yF>b>!45ZTn_7vp_o{Bu9Px2OT)@E(`lAqI2o3*ibloS|&ffndc+7ypGij-`M zbaIh#yb0y&BJu%yyA}W5iCo`A?ud=hPiQCaSVcT?&&7iT<9#t$0cG1ZSQ2Wls1iZ( zFhu_+Nyh}I?zlxXB?PRJ8F+XhC1f~>xTad^%F_e&+%LBG*zg>)Zs}8a7AQrkk3&m4 zIC6=p7CP};J@S^*+Oki>w7>9?^6W!!<}+qVm4ESTOGFF*>RY;z##frH0F!lOg9;D7 z_y8>d3%~w<3g?`YKSJA_Z@}l`G2>t`(`K*&+KhA7F#`5-8hA0TydkgI?i(Uep4X|* z%k)_D#3B;&qu03h3%ID3lbns%l7=+on4LWfSnhYl>asI`(6Fml4YUCtK8D>I%|U18 K?-9rOlY9fXY&i!2 literal 0 HcmV?d00001 diff --git a/src/spur64.cog.livetyping/_variable_order b/src/spur64.cog.livetyping/_variable_order new file mode 100644 index 0000000000..a142fd684b --- /dev/null +++ b/src/spur64.cog.livetyping/_variable_order @@ -0,0 +1,18 @@ +_stackLimit +_stackPointer +_framePointer +_CStackPointer +_CFramePointer +_CReturnAddress +_scavengeThreshold +_freeStart +_needGCFlag +_specialObjectsOop +_primFailCode +_primTraceLogIndex +_primitiveFunctionPointer +_newMethod +_instructionPointer +_argumentCount +_nextProfileTick +_primTraceLog diff --git a/src/spur64.cog.livetyping/cogit.c b/src/spur64.cog.livetyping/cogit.c new file mode 100644 index 0000000000..3bfffa3de8 --- /dev/null +++ b/src/spur64.cog.livetyping/cogit.c @@ -0,0 +1,29 @@ +/* Automatically generated by + Cogit VMMaker.oscog-eem.3587 uuid: 7ecbbf94-b756-45f8-9bb8-78fd8aa32a03 + */ + +#if !SysV && !_WIN64 +# define SysV 1 +#endif + +#if defined(__arm64__) || defined(__aarch64__) || defined(__ARM_ARCH_ISA_A64) || (__ARM_ARCH >= 8) || defined(ARM64) || defined(ARMv8) + +# define ISA "ARMv8" +# include "cogitARMv8.c" + +#elif SysV && (defined(x86_64) || defined(__amd64) || defined(__x86_64) || defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64)) + +# define ISA "X64" +# define ABI "SysV" +# include "cogitX64SysV.c" + +#elif _WIN64 && (defined(x86_64) || defined(__amd64) || defined(__x86_64) || defined(__amd64__) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64)) + +# define ISA "X64" +# define ABI "_WIN64" +# include "cogitX64WIN64.c" + +#else +# error As yet no Cogit implementation appears to exist for your platform. +# error Consider implementing it, starting by adding a subclass of CogAbstractInstruction. +#endif diff --git a/src/spur64.cog.livetyping/cogit.h b/src/spur64.cog.livetyping/cogit.h new file mode 100644 index 0000000000..80640b7eb3 --- /dev/null +++ b/src/spur64.cog.livetyping/cogit.h @@ -0,0 +1,150 @@ +/* Automatically generated by + CCodeGenerator * VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba + (Cog-eem.511, Compiler-ct.535) + */ + + +/*** Function Prototypes ***/ + + +#if !PRODUCTION && defined(PlatformNoDbgRegParms) +# define NoDbgRegParms PlatformNoDbgRegParms +#endif + +#if !defined(NoDbgRegParms) +# define NoDbgRegParms /*empty*/ +#endif + + + +#if !defined(NeverInline) +# define NeverInline /*empty*/ +#endif + +extern sqInt abortOffset(void); +extern void addCogMethodsToHeapMap(void); +extern sqInt bytecodePCForstartBcpcin(sqInt mcpc, sqInt startbcpc, CogBlockMethod *cogMethod); +extern sqInt checkIntegrityOfObjectReferencesInCode(sqInt gcModes); +extern char * codeEntryFor(char *address); +extern char * codeEntryNameFor(char *address); +extern usqInt cogCodeBase(void); +extern sqInt cogCodeConstituents(sqInt withDetails); +extern CogMethod * cogFullBlockMethodnumCopied(sqInt aMethodObj, sqInt numCopied); +extern void cogitPostGCAction(sqInt gcMode); +extern CogMethod * cogMNUPICSelectorreceivermethodOperandnumArgs(sqInt selector, sqInt rcvr, sqInt methodOperand, sqInt numArgs); +extern CogMethod * cogselector(sqInt aMethodObj, sqInt aSelectorOop); +extern void compactCogCompiledCode(void); +extern usqInt computeGoodVarBaseAddress(void); +extern int defaultCogCodeSize(void); +extern CogBlockMethod * findMethodForStartBcpcinHomeMethod(sqInt startbcpc, CogMethod *cogMethod); +extern void flagCogMethodForBecome(CogMethod *cogMethod); +extern void followForwardedLiteralsIn(CogMethod *cogMethod); +extern void followMovableLiteralsAndUpdateYoungReferrers(void); +extern void freeBecomeFlaggedMethods(void); +extern void freeCogMethod(CogMethod *cogMethod); +extern void freeUnmarkedMachineCode(void); +#if VMInvestigations +extern usqInt getMethodZoneBase(void); +#endif /* VMInvestigations */ +extern void initializeCodeZoneFromupTo(sqInt startAddress, sqInt endAddress); +extern int isPCWithinMethodZone(usqInt address); +extern sqInt isSendReturnPC(sqInt retpc); +extern CogMethod * linkedSendTargetOrNilAt(sqInt retpc); +extern void linkSendAtintooffsetreceiver(sqInt callSiteReturnAddress, CogMethod *sendingMethod, CogMethod *targetMethod, sqInt theEntryOffset, sqInt receiver); +extern void mapObjectReferencesInMachineCode(sqInt gcMode); +extern void markAndTraceMachineCodeOfMarkedMethods(void); +extern void markMethodAndReferents(CogBlockMethod *aCogMethod); +extern usqInt maxCogMethodAddress(void); +extern usqInt mcPCForBackwardBranchstartBcpcin(sqInt bcpc, sqInt startbcpc, CogBlockMethod *cogMethod); +extern sqInt mnuOffset(void); +extern sqInt patchToOpenPICFornumArgsreceiver(sqInt selector, sqInt numArgs, sqInt receiver); +#if VMInvestigations +extern void printPCMapPairsFor(CogMethod *cogMethod); +#endif /* VMInvestigations */ +extern void setSelectorOfto(CogMethod *cogMethod, sqInt aSelectorOop); +extern sqInt traceLinkedSendOffset(void); +extern void unflagBecomeFlaggedMethods(void); +extern void unlinkAllSends(void); +extern void unlinkSendsLinkedForInvalidClasses(void); +extern void unlinkSendsOfisMNUSelector(sqInt selector, sqInt isMNUSelector); +extern void unlinkSendsToMethodsSuchThatAndFreeIf(sqInt (*criterion)(CogMethod *), sqInt freeIfTrue); +extern void unlinkSendsToandFreeIf(sqInt targetMethodObject, sqInt freeIfTrue); +extern void voidCogCompiledCode(void); +extern CogMethod * cogMethodContaining(usqInt mcpc); +extern sqInt kosherYoungReferrers(void); +extern CogMethod * methodFor(void *address); +#if VMInvestigations +extern sqInt methodsCompiledToMachineCodeInto(sqInt arrayObj); +#endif /* VMInvestigations */ +extern sqInt numMethods(void); +extern sqInt numMethodsOfType(sqInt cogMethodType); +extern char * whereIsMaybeCodeThing(sqInt anOop); +#if VMInvestigations +extern char * abiName(void); +#endif /* VMInvestigations */ +extern sqInt cogMethodHasExternalPrim(CogMethod *aCogMethod); +extern sqInt cogMethodHasMachineCodePrim(CogMethod *aCogMethod); +#if VMInvestigations +extern sqInt cogMethodImportsintodetailCPICs(CogMethod *cogMethod, sqInt data, sqInt detailCPICs); +#endif /* VMInvestigations */ +extern sqInt genQuickReturnConst(void); +extern sqInt genQuickReturnInstVar(void); +extern sqInt genQuickReturnSelf(void); +#if VMInvestigations +extern char * isaName(void); +#endif /* VMInvestigations */ +extern sqInt mapPCDataForinto(CogMethod *cogMethod, sqInt arrayObj); +extern void callCogCodePopReceiverArg0Regs(void); +extern void callCogCodePopReceiverArg1Arg0Regs(void); + + +/*** Global Variables ***/ +extern sqInt blockNoContextSwitchOffset; +extern sqInt cbEntryOffset; +extern sqInt cbNoSwitchEntryOffset; +extern sqInt ceBaseFrameReturnTrampoline; +extern void (*ceCall0ArgsPIC)(void); +extern void (*ceCall1ArgsPIC)(void); +extern void (*ceCall2ArgsPIC)(void); +extern void (*ceCallCogCodePopReceiverAndClassRegs)(void); +extern void (*ceCallCogCodePopReceiverArg0Regs)(void); +extern void (*ceCallCogCodePopReceiverArg1Arg0Regs)(void); +extern void (*ceCallCogCodePopReceiverReg)(void); +extern sqInt ceCannotResumeTrampoline; +extern void (*ceCaptureCStackPointers)(void); +extern sqInt ceCheckForInterruptTrampoline; +extern void (*ceEnterCogCodePopReceiverReg)(void); +extern usqIntptr_t (*ceGetFP)(void); +extern usqIntptr_t (*ceGetSP)(void); +extern void (*ceInvokeInterpret)(void); +extern sqInt ceReturnToInterpreterTrampoline; +extern sqInt cmEntryOffset; +extern sqInt cmNoCheckEntryOffset; +extern usqInt methodZoneBase; +extern sqInt missOffset; +extern int traceFlags ; + + +/*** Macros ***/ +#define blockAlignment() 8 +#define ceBaseFrameReturnPC() ceBaseFrameReturnTrampoline +#define ceCannotResumePC() ((usqInt)ceCannotResumeTrampoline) +#define ceReturnToInterpreterPC() ((usqInt)ceReturnToInterpreterTrampoline) +#define entryOffset() cmEntryOffset +#define interpretOffset() missOffset +#define maxCogCodeSize() (16*1024*1024) +#define minCogMethodAddress() methodZoneBase +#define noCheckEntryOffset() cmNoCheckEntryOffset +#define noContextSwitchBlockEntryOffset() blockNoContextSwitchOffset +#define printOnTrace() (traceFlags & 1) +#define recordBlockTrace() (traceFlags & 4) +#define recordEventTrace() (traceFlags & 16) +#define recordFastCCallPrimTrace() (traceFlags & 128) +#define recordFFIOrThreadTrace() (traceFlags & 256) +#define recordOverflowTrace() (traceFlags & 32) +#define recordPrimTrace() (traceFlags & 8) +#define recordSendTrace() (traceFlags & 2) +#define numRegArgs() 2 +#define fullBlockEntryOffset() cbEntryOffset +#define fullBlockNoContextSwitchEntryOffset() cbNoSwitchEntryOffset + diff --git a/src/spur64.cog.livetyping/cogitARMv8.c b/src/spur64.cog.livetyping/cogitARMv8.c new file mode 100644 index 0000000000..ae192059d4 --- /dev/null +++ b/src/spur64.cog.livetyping/cogitARMv8.c @@ -0,0 +1,38745 @@ +/* Automatically generated by + CCodeGenerator * VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba + (Cog-eem.511, Compiler-ct.535) + from + LiveTypingCogit * VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba + */ +static char __buildInfo[] = "LiveTypingCogit * VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba " __DATE__ ; +char *__cogitBuildInfo = __buildInfo; + + + +#include "sqConfig.h" +#include +#include +#include +#include +#include "sqPlatformSpecific.h" +#include "sqMemoryAccess.h" +#include "sqCogStackAlignment.h" +#include "dispdbg.h" +#include "cogmethod.h" +#include "cointerp.h" +#include "cogit.h" +#include "codeZoneControlARM64.h" + + +/*** Constants ***/ +#define ABICalleeSavedRegisterMask 0x1FF80000 +#define ABICallerSavedRegisterMask 0x3FFFF +#define ABIResultReg 0 +#define AddCqR 106 +#define AddCqRR 123 +#define AddCwR 114 +#define AddRdRd 132 +#define AddRR 100 +#define AddRRR 126 +#define AL 14 +#define AlignmentNops 3 +#define AltBlockCreationBytecodeSize 3 +#define AltFirstSpecialSelector 96 +#define AltNumSpecialSelectors 32 +#define AndCqR 108 +#define AndCqRR 124 +#define AndCwR 116 +#define AndRR 102 +#define AnnotationShift 5 +#define Arg0Reg 7 +#define Arg1Reg 8 +#define ArithmeticAdd 0 +#define ArithmeticAddS 1 +#define ArithmeticShiftRightCqR 91 +#define ArithmeticShiftRightCqRR 128 +#define ArithmeticShiftRightRR 92 +#define ArithmeticSub 2 +#define ArithmeticSubS 3 +#define BadRegisterSet 1 +#define BlockCreationBytecodeSize 4 +#define BytecodeSetHasDirectedSuperSend 1 +#define CArg0Reg 0 +#define CArg1Reg 1 +#define CArg2Reg 2 +#define CArg3Reg 3 +#define CASAL 176 +#define Call 6 +#define CallerSavedRegisterMask 0x101F0 +#define CallFull 7 +#define CBNZ 177 +#define CBZ 178 +#define CC 3 +#define CCMPNE 179 +#if !defined(CheckRememberedInTrampoline) /* Allow this to be overridden on the compiler command line */ +# define CheckRememberedInTrampoline 0 +#endif +#define CLREX 181 +#define ClassArray 7 +#define ClassArrayCompactIndex 51 +#define ClassBlockClosureCompactIndex 37 +#define ClassFloatCompactIndex 34 +#define ClassFullBlockClosureCompactIndex 38 +#define ClassLargeNegativeIntegerCompactIndex 32 +#define ClassLargePositiveIntegerCompactIndex 33 +#define ClassMethodContextCompactIndex 36 +#define ClassPointCompactIndex 54 +#define ClassReg 4 +#define ClosureFirstCopiedValueIndex 3 +#define ClosureIndex 4 +#define ClosureNumArgsIndex 2 +#define ClosureOuterContextIndex 0 +#define ClosureStartPCIndex 1 +#define ClzRR 157 +#define CMBlock 4 +#define CMClosedPIC 2 +#define CMFree 1 +#define CMMaxUsageCount 7 +#define CMMethod 5 +#define CMMethodFlaggedForBecome 6 +#define CMOpenPIC 3 +#define CmpC32R 113 +#define CmpCqR 105 +#define CmpCwR 112 +#define CmpRdRd 131 +#define CmpRR 99 +#define CompletePrimitive 4 +#define ConstZero 1 +#define ConvertRdR 146 +#define ConvertRRd 145 +#define CS 2 +#define CSET 180 +#define DC 169 +#define DC_CIVAC 14 +#define DC_CVAC 16 +#define DC_CVAU 19 +#if !defined(Debug) /* Allow this to be overridden on the compiler command line */ +# define Debug DEBUGVM +#endif +#define DisplacementMask 0x1F +#define DisplacementX2N 0 +#define DivRdRd 135 +#define DivRRR 163 +#define DPFPReg0 0 +#define DPFPReg1 1 +#define DPFPReg2 2 +#define DPFPReg3 3 +#define DPFPReg4 4 +#define DPFPReg5 5 +#define DPFPReg6 6 +#define DPFPReg7 7 +#define DSB 172 +#define DSB_ALL 3 +#define DSB_ISH 2 +#define EncounteredUnknownBytecode -6 +#define EQ 0 +#define Extra0Reg 19 +#define Extra1Reg 20 +#define Extra2Reg 21 +#define Fill32 4 +#define FirstAnnotation 64 +#define FirstJump 12 +#define FirstSpecialSelector 176 +#define FoxCallerSavedIP 8 +#define FoxIFSavedIP -32 +#define FoxMethod -8 +#define FoxMFReceiver -24 +#define FoxSavedFP 0 +#define FoxThisContext -16 +#define FP 29 +#define FPReg 29 +#define FullClosureCompiledBlockIndex 1 +#define FullClosureFirstCopiedValueIndex 4 +#define FullClosureReceiverIndex 3 +#define GCModeBecome 8 +#define GCModeFull 1 +#define GCModeNewSpace 2 +#define GE 10 +#define GT 12 +#define HasBytecodePC 5 +#define HashMultiplyConstant 1664525 +#define HashMultiplyMask 0xFFFFFFF +#define HeaderIndex 0 +#define HI 8 +#define IC 170 +#define IC_IVAU 2 +#if !defined(IMMUTABILITY) /* Allow this to be overridden on the compiler command line */ +# define IMMUTABILITY 0 +#endif +#define InFullBlock 2 +#define InstanceSpecificationIndex 2 +#define InstructionPointerIndex 1 +#define InsufficientCodeSpace -2 +#define InVanillaBlock 1 +#define ISB 173 +#define IsAbsPCReference 3 +#define IsAnnotationExtension 1 +#define IsDirectedSuperBindingSend 10 +#define IsDirectedSuperSend 9 +#define IsDisplacementX2N 0 +#define IsNSSendCall null +#define IsObjectReference 2 +#define IsRelativeCall 4 +#define IsSendCall 7 +#define IsSuperSend 8 +#define Jump 16 +#define JumpAbove 33 +#define JumpAboveOrEqual 32 +#define JumpBelow 31 +#define JumpBelowOrEqual 34 +#define JumpCarry 25 +#define JumpFPEqual 35 +#define JumpFPGreater 39 +#define JumpFPGreaterOrEqual 40 +#define JumpFPLess 37 +#define JumpFPLessOrEqual 38 +#define JumpFPNotEqual 36 +#define JumpFPOrdered 41 +#define JumpFPUnordered 42 +#define JumpFull 12 +#define JumpGreater 29 +#define JumpGreaterOrEqual 28 +#define JumpLess 27 +#define JumpLessOrEqual 30 +#define JumpLong 13 +#define JumpLongNonZero 15 +#define JumpLongZero 14 +#define JumpMulOverflow 23 +#define JumpNegative 19 +#define JumpNoCarry 26 +#define JumpNoMulOverflow 24 +#define JumpNonNegative 20 +#define JumpNonZero 18 +#define JumpNoOverflow 22 +#define JumpOverflow 21 +#define JumpR 10 +#define JumpZero 17 +#define Label 1 +#define LargeContextSlots 62 +#define LastJump 42 +#define LDAXR 182 +#define LE 13 +#define LinkReg 30 +#define Literal 2 +#define LiteralStart 1 +#define LoadEffectiveAddressMwrR 88 +#define LogicalAndS 3 +#define LogicalOr 1 +#define LogicalShiftLeftCqR 95 +#define LogicalShiftLeftCqRR 129 +#define LogicalShiftLeftRR 96 +#define LogicalShiftRightCqR 93 +#define LogicalShiftRightCqRR 130 +#define LogicalShiftRightRR 94 +#define LogicalXor 2 +#define LR 30 +#define LS 9 +#define LT 11 +#define MapEnd 0 +#define MaxCompiledPrimitiveIndex 582 +#define MaxCPICCases 6 +#define MaxMethodSize 65535 +#define MaxNegativeErrorCode -8 +#define MaxStackAllocSize 1572864 +#define MaxStackCheckOffset 0xFFF +#define MaxX2NDisplacement 992 +#define MethodCacheClass 2 +#define MethodCacheMask 0xFFC +#define MethodCacheMethod 3 +#define MethodCacheSelector 1 +#define MethodIndex 3 +#define MethodTooBig -4 +#define MFMethodFlagHasContextFlag 1 +#define MFMethodFlagIsBlockFlag 2 +#define MI 4 +#define MoveAbR 48 +#define MoveAwR 44 +#define MoveAwRR 165 +#define MoveC32R 71 +#define MoveCqR 69 +#define MoveCwR 70 +#define MoveM16rR 57 +#define MoveM32rR 61 +#define MoveM64rRd 75 +#define MoveMbrR 65 +#define MoveMwrR 50 +#define MovePerfCnt64RL 159 +#define MoveRAb 49 +#define MoveRAw 46 +#define MoveRdM64r 76 +#define MoveRdR 73 +#define MoveRM16r 58 +#define MoveRM32r 62 +#define MoveRMbr 66 +#define MoveRMwr 51 +#define MoveRR 43 +#define MoveRRAw 166 +#define MoveRRd 72 +#define MoveRX16rR 60 +#define MoveRX32rR 64 +#define MoveRXbrR 68 +#define MoveRXwrR 53 +#define MoveX16rRR 59 +#define MoveX32rRR 63 +#define MoveXbrRR 67 +#define MoveXwrRR 52 +#define MRS_CTR_EL0 174 +#define MRS_ID_AA64ISAR0_EL1 175 +#define MSubRRR 164 +#define MulOverflowRRR 161 +#define MulRdRd 134 +#define MulRRR 160 +#define NativePopR 84 +#define NativePopRR 168 +#define NativePushR 85 +#define NativePushRR 167 +#define NativeRetN 86 +#define NativeSPReg 31 +#define NE 1 +#define NeedsMergeFixupFlag 2 +#define NeedsNonMergeFixupFlag 1 +#define NegateR 89 +#define NOP 3573751839U +#define Nop 5 +#define NoReg -1 +#define NotFullyInitialized -1 +#define NumObjRefsInRuntime 0 +#define NumSendTrampolines 4 +#define NumSpecialSelectors 32 +#define NumStoreTrampolines 5 +#define NumTrampolines (77 + (IMMUTABILITY ? 5 : 0)) +#define OrCqR 109 +#define OrCqRR 125 +#define OrCwR 117 +#define OrRR 103 +#define PL 5 +#define PopR 80 +#define PrefetchAw 87 +#define PrimCallCollectsProfileSamples 16 +#define PrimCallIsExternalCall 32 +#define PrimCallMayEndureCodeCompaction 8 +#define PrimCallNeedsNewMethod 4 +#define PrimCallOnSmalltalkStack 1 +#define PrimCallOnSmalltalkStackAlign2x 2 +#define PrimNumberExternalCall 117 +#define PrimNumberFFICall 120 +#define PushCq 82 +#define PushCw 83 +#define PushR 81 +#define R0 0 +#define R1 1 +#define R17 17 +#define ReceiverIndex 5 +#define ReceiverResultReg 5 +#define RetN 9 +#define RISCTempReg 9 +#define RotateLeftCqR 97 +#define RotateRightCqR 98 +#define SelectorCannotInterpret 34 +#define SelectorDoesNotUnderstand 20 +#define SenderIndex 0 +#define SendNumArgsReg 6 +#define ShouldNotJIT -8 +#define SignExtend16RR 152 +#define SignExtend32RR 153 +#define SignExtend8RR 151 +#define SmallContextSlots 22 +#define SP 31 +#define SPReg 16 +#define SpecialSelectors 23 +#define SqrtRd 136 +#define SSBaseOffset 1 +#define SSConstant 2 +#define SSRegister 3 +#define SSSpill 4 +#define STLR 184 +#define STLXR 183 +#define StackPointerIndex 2 +#define Stop 11 +#define SubCqR 107 +#define SubCwR 115 +#define SubRdRd 133 +#define SubRR 101 +#define SubRRR 127 +#define SXTX 7 +#define TempReg 0 +#define TstCqR 110 +#define UnfailingPrimitive 3 +#define UnimplementedPrimitive -7 +#define UXTX 3 +#define ValueIndex 1 +#define VarBaseReg 28 +#define VC 7 +#define VS 6 +#define XorCqR 111 +#define XorCwR 118 +#define XorRdRd 137 +#define XorRR 104 +#define XZR 31 +#define YoungSelectorInPIC -5 +#define ZeroExtend16RR 155 +#define ZeroExtend32RR 156 +#define ZeroExtend8RR 154 + +typedef struct _AbstractInstruction { + unsigned char opcode; + unsigned char machineCodeSize; + unsigned char maxSize; + unsigned char annotation; + unsigned int machineCode[3]; + usqInt operands[3]; + usqInt address; + struct _AbstractInstruction *dependent; + } AbstractInstruction; + +#define CogARMv8Compiler AbstractInstruction +#define CogAbstractInstruction AbstractInstruction + + +typedef struct { + AbstractInstruction *fakeHeader; + AbstractInstruction *fillInstruction; + sqInt numArgs; + sqInt numCopied; + sqInt numInitialNils; + sqInt startpc; + AbstractInstruction *entryLabel; + AbstractInstruction *stackCheckLabel; + sqInt span; + sqInt hasInstVarRef; + } BlockStart; + +#define CogBlockStart BlockStart + + +typedef struct _BytecodeDescriptor { + sqInt (*generator)(void); + sqInt NoDbgRegParms (*spanFunction)(struct _BytecodeDescriptor *,sqInt,sqInt,sqInt); + sqInt NoDbgRegParms (*needsFrameFunction)(sqInt); + signed char stackDelta; + unsigned char opcode; + unsigned char numBytes; + unsigned isBranchTrue : 1; + unsigned isBranchFalse : 1; + unsigned isReturn : 1; + unsigned isBlockCreation : 1; + unsigned isMapped : 1; + unsigned isMappedInBlock : 1; + unsigned isExtension : 1; + unsigned isInstVarRef : 1; + unsigned is1ByteInstVarStore : 1; + unsigned hasUnsafeJump : 1; + } BytecodeDescriptor; + +#define CogBytecodeDescriptor BytecodeDescriptor + + +typedef struct { + sqInt (*primitiveGenerator)(void); + sqInt primNumArgs; + } PrimitiveDescriptor; + +#define CogPrimitiveDescriptor PrimitiveDescriptor + + +typedef struct { + char type; + char spilled; + signed char liveRegister; + signed char registerr; + sqInt offset; + sqInt constant; + sqInt bcptr; + } SimStackEntry; + +#define CogSimStackEntry SimStackEntry + + +typedef struct { + AbstractInstruction *targetInstruction; + unsigned char simStackPtr; + char isTargetOfBackwardBranch; + unsigned short instructionIndex; + } BytecodeFixup; + +#define CogSSBytecodeFixup BytecodeFixup +#define CogBytecodeFixup BytecodeFixup + + +typedef struct { + sqInt isReceiverResultRegLive; + SimStackEntry *ssEntry; + } CogSSOptStatus; + + + +/*** Function Prototypes ***/ + + +#if !PRODUCTION && defined(PlatformNoDbgRegParms) +# define NoDbgRegParms PlatformNoDbgRegParms +#endif + +#if !defined(NoDbgRegParms) +# define NoDbgRegParms /*empty*/ +#endif + + + +#if !defined(NeverInline) +# define NeverInline /*empty*/ +#endif + +static NoDbgRegParms AbstractInstruction * addDependent(AbstractInstruction *self_in_CogAbstractInstruction, AbstractInstruction *anInstruction); +static NoDbgRegParms sqInt availableFloatRegisterOrNoneFor(AbstractInstruction *self_in_CogAbstractInstruction, sqInt liveRegsMask); +static NoDbgRegParms sqInt availableRegisterOrNoneFor(AbstractInstruction *self_in_CogAbstractInstruction, sqInt liveRegsMask); +static NoDbgRegParms AbstractInstruction * cloneLiteralFrom(AbstractInstruction *self_in_CogAbstractInstruction, AbstractInstruction *existingLiteral); +static NoDbgRegParms sqInt concretizeAt(AbstractInstruction *self_in_CogAbstractInstruction, sqInt actualAddress); +static NoDbgRegParms sqInt genLoadCStackPointer(AbstractInstruction *self_in_CogAbstractInstruction); +static NoDbgRegParms sqInt genLoadStackPointerForPrimCall(AbstractInstruction *self_in_CogAbstractInstruction, sqInt spareReg); +static NoDbgRegParms sqInt genLoadStackPointersForPrimCall(AbstractInstruction *self_in_CogAbstractInstruction, sqInt spareReg); +static NoDbgRegParms AbstractInstruction * genSwapRRScratch(AbstractInstruction *self_in_CogAbstractInstruction, sqInt regA, sqInt regB, sqInt regTmp); +static NoDbgRegParms AbstractInstruction * genWriteCResultIntoReg(AbstractInstruction *self_in_CogAbstractInstruction, sqInt abstractRegister); +static NoDbgRegParms sqInt has64BitPerformanceCounter(AbstractInstruction *self_in_CogAbstractInstruction); +static NoDbgRegParms AbstractInstruction * initializeSharableLiteral(AbstractInstruction *self_in_CogAbstractInstruction, sqInt literal); +static NoDbgRegParms AbstractInstruction * initializeUniqueLiteral(AbstractInstruction *self_in_CogAbstractInstruction, sqInt literal); +static NoDbgRegParms AbstractInstruction * jmpTarget(AbstractInstruction *self_in_CogAbstractInstruction, AbstractInstruction *anAbstractInstruction); +static NoDbgRegParms AbstractInstruction * relocateJumpLongBeforeFollowingAddressby(AbstractInstruction *self_in_CogAbstractInstruction, sqInt pc, sqInt delta); +static NoDbgRegParms AbstractInstruction * relocateJumpLongConditionalBeforeFollowingAddressby(AbstractInstruction *self_in_CogAbstractInstruction, sqInt pc, sqInt delta); +static NoDbgRegParms AbstractInstruction * resolveJumpTarget(AbstractInstruction *self_in_CogAbstractInstruction); +static NoDbgRegParms sqInt rewriteConditionalJumpLongAttarget(AbstractInstruction *self_in_CogAbstractInstruction, sqInt callSiteReturnAddress, sqInt callTargetAddress); +static NoDbgRegParms sqInt addrnrdimmshiftBy12(AbstractInstruction *self_in_CogARMv8Compiler, sqInt rn, sqInt rd, sqInt offset, sqInt shiftBy12); +static NoDbgRegParms sqInt brlinkreg(AbstractInstruction *self_in_CogARMv8Compiler, sqInt link, sqInt reg); +static NoDbgRegParms sqInt callFullInstructionByteSize(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt callInstructionByteSize(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt callTargetFromReturnAddress(AbstractInstruction *self_in_CogARMv8Compiler, sqInt mcpc); +static NoDbgRegParms sqInt cmpC32RTempByteSize(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt computeJumpTargetOffset(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt computeLowBit(AbstractInstruction *self_in_CogARMv8Compiler, sqInt nArg); +static NoDbgRegParms sqInt computeMaximumSize(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt concretizeConditionalJump(AbstractInstruction *self_in_CogARMv8Compiler, sqInt conditionCode); +static NoDbgRegParms sqInt concretizeCwRArithmeticRd(AbstractInstruction *self_in_CogARMv8Compiler, sqInt arithOp, sqInt destRegOrXZR); +static NoDbgRegParms sqInt concretizeCwRLogical(AbstractInstruction *self_in_CogARMv8Compiler, sqInt op); +static NoDbgRegParms sqInt concretizeDataCacheControl(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt concretizeFill32(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt concretizeLiteral(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt concretizeLoadEffectiveAddressMwrR(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt concretizeLogicalOpCqRDest(AbstractInstruction *self_in_CogARMv8Compiler, sqInt op, sqInt destReg); +static NoDbgRegParms sqInt concretizeMoveCqR(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt concretizeMoveMSrR(AbstractInstruction *self_in_CogARMv8Compiler, sqInt unitSizeLog2MinusOne); +static NoDbgRegParms sqInt concretizeMovePerfCnt64RL(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt concretizeMoveRMSr(AbstractInstruction *self_in_CogARMv8Compiler, sqInt unitSizeLog2MinusOne); +static NoDbgRegParms sqInt concretizeMoveRXSrR(AbstractInstruction *self_in_CogARMv8Compiler, sqInt unitSizeLog2MinusOne); +static NoDbgRegParms sqInt concretizeMoveXSrRR(AbstractInstruction *self_in_CogARMv8Compiler, sqInt unitSizeLog2MinusOne); +static NoDbgRegParms sqInt concretizeMulOverflowJump(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt concretizeRRArithmeticRd(AbstractInstruction *self_in_CogARMv8Compiler, sqInt arithOp, sqInt rd); +static NoDbgRegParms sqInt concretizeRRLogical(AbstractInstruction *self_in_CogARMv8Compiler, sqInt logicalOp); +static NoDbgRegParms sqInt concretizeSignExtendRR(AbstractInstruction *self_in_CogARMv8Compiler, sqInt width); +static NoDbgRegParms sqInt concretizeSTLR(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt concretizeZeroExtendRR(AbstractInstruction *self_in_CogARMv8Compiler, sqInt width); +static NoDbgRegParms sqInt countLeadingOnes(AbstractInstruction *self_in_CogARMv8Compiler, sqInt anInteger); +static NoDbgRegParms sqInt countTrailingOnes(AbstractInstruction *self_in_CogARMv8Compiler, sqInt anInteger); +static NoDbgRegParms sqInt countTrailingZeros(AbstractInstruction *self_in_CogARMv8Compiler, sqInt anInteger); +static NoDbgRegParms usqInt decodeNimmsimmr(AbstractInstruction *self_in_CogARMv8Compiler, sqInt n, sqInt imms, sqInt immr); +#if __linux__ +static NoDbgRegParms AbstractInstruction * detectFeaturesOnLinux(AbstractInstruction *self_in_CogARMv8Compiler); +#endif /* __linux__ */ +#if !__APPLE__ && !__linux__ +static NoDbgRegParms AbstractInstruction * detectFeaturesOnRawMachine(AbstractInstruction *self_in_CogARMv8Compiler); +#endif /* !__APPLE__ && !__linux__ */ +#if _WIN64 +static NoDbgRegParms AbstractInstruction * detectFeaturesOnWindows(AbstractInstruction *self_in_CogARMv8Compiler); +#endif /* _WIN64 */ +static NoDbgRegParms sqInt dispatchConcretize(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt emitLdfprnrtimmshiftBy12at(AbstractInstruction *self_in_CogARMv8Compiler, sqInt baseReg, sqInt targetDPReg, sqInt offset, sqInt shiftBy12, sqInt instrOffset); +static NoDbgRegParms sqInt emitLdrnrtimmshiftBy12at(AbstractInstruction *self_in_CogARMv8Compiler, sqInt unitSizeLog2MinusOne, sqInt baseReg, sqInt targetReg, sqInt offset, sqInt shiftBy12, sqInt instrOffset); +static NoDbgRegParms sqInt emitMoveCwintoRat(AbstractInstruction *self_in_CogARMv8Compiler, usqInt constantArg, sqInt destReg, sqInt offsetBytes); +static NoDbgRegParms sqInt emitStrnrtimmshiftBy12(AbstractInstruction *self_in_CogARMv8Compiler, sqInt unitSizeLog2MinusOne, sqInt baseReg, sqInt sourceReg, sqInt offset, sqInt shiftBy12); +static NoDbgRegParms sqInt fullCallsAreRelative(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms AbstractInstruction * genDivRRQuoRem(AbstractInstruction *self_in_CogARMv8Compiler, sqInt regDivisor, sqInt regDividend, sqInt regQuotient, sqInt regRemainder); +static NoDbgRegParms AbstractInstruction * generateDCacheFlush(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms AbstractInstruction * generateICacheFlush(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt genLoadCStackPointers(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms AbstractInstruction * genLoadNativeSPRegWithAlignedSPReg(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt genLoadStackPointers(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms AbstractInstruction * genMarshallNArgsargargargarg(AbstractInstruction *self_in_CogARMv8Compiler, sqInt numArgs, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3); +static NoDbgRegParms AbstractInstruction * genMulOverflowRR(AbstractInstruction *self_in_CogARMv8Compiler, sqInt regSource, sqInt regDest); +static NoDbgRegParms AbstractInstruction * genMulRR(AbstractInstruction *self_in_CogARMv8Compiler, sqInt regSource, sqInt regDest); +static NoDbgRegParms AbstractInstruction * genPushRegisterArgsForAbortMissNumArgs(AbstractInstruction *self_in_CogARMv8Compiler, sqInt numArgs); +static NoDbgRegParms AbstractInstruction * genPushRegisterArgsForNumArgsscratchReg(AbstractInstruction *self_in_CogARMv8Compiler, sqInt numArgs, sqInt ignored); +static NoDbgRegParms sqInt genRemoveNArgsFromStack(AbstractInstruction *self_in_CogARMv8Compiler, sqInt n); +static NoDbgRegParms sqInt genRestoreRegs(AbstractInstruction *self_in_CogARMv8Compiler, sqInt regMask); +static NoDbgRegParms sqInt genSaveRegs(AbstractInstruction *self_in_CogARMv8Compiler, sqInt regMask); +static NoDbgRegParms sqInt genSaveStackPointers(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms AbstractInstruction * genSubstituteReturnAddress(AbstractInstruction *self_in_CogARMv8Compiler, sqInt retpc); +static NoDbgRegParms sqInt hasVarBaseRegister(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms unsigned int inlineCacheTagAt(AbstractInstruction *self_in_CogARMv8Compiler, sqInt callSiteReturnAddress); +static NoDbgRegParms sqInt instructionAddressBefore(AbstractInstruction *self_in_CogARMv8Compiler, sqInt mcpc); +static NoDbgRegParms unsigned int instructionAt(AbstractInstruction *self_in_CogARMv8Compiler, sqInt pc); +static NoDbgRegParms int instructionIsADR(AbstractInstruction *self_in_CogARMv8Compiler, sqInt word); +static NoDbgRegParms int instructionIsImm26BorBL(AbstractInstruction *self_in_CogARMv8Compiler, usqInt word); +static NoDbgRegParms int instructionIsLoadStore(AbstractInstruction *self_in_CogARMv8Compiler, sqInt instr); +static NoDbgRegParms int instructionIsPCRelativeLoad(AbstractInstruction *self_in_CogARMv8Compiler, sqInt instr); +static NoDbgRegParms sqInt instructionSizeAt(AbstractInstruction *self_in_CogARMv8Compiler, sqInt pc); +static NoDbgRegParms int inverseForArithOp(AbstractInstruction *self_in_CogARMv8Compiler, sqInt arithOp); +static NoDbgRegParms int isAddressRelativeToVarBase(AbstractInstruction *self_in_CogARMv8Compiler, usqInt varAddress); +static NoDbgRegParms sqInt isCallPrecedingReturnPC(AbstractInstruction *self_in_CogARMv8Compiler, sqInt mcpc); +static NoDbgRegParms int isFullJumpAtPC(AbstractInstruction *self_in_CogARMv8Compiler, sqInt mcpc); +static NoDbgRegParms sqInt isImm12orImm9offset(AbstractInstruction *self_in_CogARMv8Compiler, sqInt offset); +static NoDbgRegParms int isInImmediateBranchAndLinkRange(AbstractInstruction *self_in_CogARMv8Compiler, sqIntptr_t offset); +static NoDbgRegParms int isInImmediateBranchRange(AbstractInstruction *self_in_CogARMv8Compiler, usqIntptr_t offset); +static NoDbgRegParms int isInImmediateJumpRange(AbstractInstruction *self_in_CogARMv8Compiler, usqIntptr_t operand); +static NoDbgRegParms sqInt isInImmediateOffsetRange(AbstractInstruction *self_in_CogARMv8Compiler, sqInt offset); +static NoDbgRegParms sqInt isJump(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt isJumpAt(AbstractInstruction *self_in_CogARMv8Compiler, sqInt pc); +static NoDbgRegParms sqInt isPCDependent(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt isShiftedMask(AbstractInstruction *self_in_CogARMv8Compiler, sqInt anInteger); +static NoDbgRegParms sqInt isUnsigned12BitMultipleOf8(AbstractInstruction *self_in_CogARMv8Compiler, sqInt anInteger); +static NoDbgRegParms sqInt jumpLongByteSize(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt jumpLongConditionalByteSize(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt jumpLongTargetBeforeFollowingAddress(AbstractInstruction *self_in_CogARMv8Compiler, sqInt mcpc); +static NoDbgRegParms usqInt jumpTargetPCAt(AbstractInstruction *self_in_CogARMv8Compiler, sqInt pc); +static NoDbgRegParms unsigned int literal32BeforeFollowingAddress(AbstractInstruction *self_in_CogARMv8Compiler, sqInt followingAddress); +static NoDbgRegParms sqInt literalBeforeFollowingAddress(AbstractInstruction *self_in_CogARMv8Compiler, sqInt followingAddress); +static NoDbgRegParms sqInt literalLoadInstructionBytes(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt loadLiteralByteSize(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt loadPICLiteralByteSize(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt machineCodeBytes(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt machineCodeWords(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt movernrd(AbstractInstruction *self_in_CogARMv8Compiler, sqInt srcReg, sqInt destReg); +static NoDbgRegParms sqInt numDCacheFlushOpcodes(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt numICacheFlushOpcodes(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt numIntRegArgs(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt outOfLineLiteralOpcodeLimit(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms AbstractInstruction * padIfPossibleWithStopsFromto(AbstractInstruction *self_in_CogARMv8Compiler, sqInt startAddr, sqInt endAddr); +static NoDbgRegParms sqInt pcRelativeAddressAt(AbstractInstruction *self_in_CogARMv8Compiler, sqInt mcpc); +static NoDbgRegParms sqInt perfCounterRatePerUsec(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt performanceCounterTicksPerMicrosecond(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt prnimmshiftBy12(AbstractInstruction *self_in_CogARMv8Compiler, sqInt baseReg, sqInt offset, sqInt shiftBy12); +static NoDbgRegParms sqInt pushLinkRegisterByteSize(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms AbstractInstruction * relocateCallBeforeReturnPCby(AbstractInstruction *self_in_CogARMv8Compiler, sqInt retpc, sqInt delta); +static NoDbgRegParms AbstractInstruction * relocateMethodReferenceBeforeAddressby(AbstractInstruction *self_in_CogARMv8Compiler, sqInt pc, sqInt delta); +static NoDbgRegParms sqInt rewriteCallAttarget(AbstractInstruction *self_in_CogARMv8Compiler, usqInt callSiteReturnAddress, usqInt callTargetAddress); +static NoDbgRegParms sqInt rewriteImm19JumpBeforetarget(AbstractInstruction *self_in_CogARMv8Compiler, sqInt followingAddress, sqInt targetAddress); +static NoDbgRegParms sqInt rewriteImm26JumpBeforetarget(AbstractInstruction *self_in_CogARMv8Compiler, sqInt followingAddress, sqInt targetAddress); +static NoDbgRegParms sqInt rewriteInlineCacheAttagtarget(AbstractInstruction *self_in_CogARMv8Compiler, usqInt callSiteReturnAddress, sqInt cacheTag, usqInt callTargetAddress); +static NoDbgRegParms AbstractInstruction * rewriteInlineCacheTagat(AbstractInstruction *self_in_CogARMv8Compiler, sqInt cacheTag, sqInt callSiteReturnAddress); +static NoDbgRegParms AbstractInstruction * setLiteralSize(AbstractInstruction *self_in_CogARMv8Compiler, sqInt sizeOfLiteral); +static NoDbgRegParms sqInt setsConditionCodesFor(AbstractInstruction *self_in_CogARMv8Compiler, sqInt aConditionalJumpOpcode); +static NoDbgRegParms unsigned char sizePCDependentInstructionAt(AbstractInstruction *self_in_CogARMv8Compiler, sqInt eventualAbsoluteAddress); +static NoDbgRegParms AbstractInstruction * stopsFromto(AbstractInstruction *self_in_CogARMv8Compiler, sqInt startAddr, sqInt endAddr); +static NoDbgRegParms AbstractInstruction * storeLiteral32beforeFollowingAddress(AbstractInstruction *self_in_CogARMv8Compiler, sqInt literal, sqInt followingAddress); +static NoDbgRegParms AbstractInstruction * storeLiteralbeforeFollowingAddress(AbstractInstruction *self_in_CogARMv8Compiler, sqInt literal, sqInt followingAddress); +static NoDbgRegParms sqInt strnrtimmshiftBy12(AbstractInstruction *self_in_CogARMv8Compiler, sqInt unitSizeLog2MinusOne, sqInt baseReg, sqInt targetReg, sqInt offset, sqInt shiftBy12); +static NoDbgRegParms sqInt usesOutOfLineLiteral(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms sqInt widthOfPCRelativeLoad(AbstractInstruction *self_in_CogARMv8Compiler, sqInt instr); +static NoDbgRegParms sqInt zoneCallsAreRelative(AbstractInstruction *self_in_CogARMv8Compiler); +static NoDbgRegParms CogMethod * cmHomeMethod(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMBlock(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMClosedPIC(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMFree(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMMethodEtAl(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMOpenPIC(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms sqInt isBranch(BytecodeDescriptor *self_in_CogBytecodeDescriptor); +static NoDbgRegParms sqInt isConditionalBranch(BytecodeDescriptor *self_in_CogBytecodeDescriptor); +static NoDbgRegParms AbstractInstruction * gAddCqR(sqInt quickConstant, sqInt reg); +static NoDbgRegParms AbstractInstruction * gAddCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg); +static NoDbgRegParms AbstractInstruction * gAndCqR(sqInt quickConstant, sqInt reg); +static NoDbgRegParms AbstractInstruction * gAndCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg); +static NoDbgRegParms AbstractInstruction * gArithmeticShiftRightCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg); +extern sqInt abortOffset(void); +static void addCleanBlockStarts(void); +extern void addCogMethodsToHeapMap(void); +static NoDbgRegParms sqInt addressIsInCurrentCompilation(sqInt address); +static NoDbgRegParms sqInt addressIsInFixups(BytecodeFixup *address); +static NoDbgRegParms sqInt addressOfEndOfCaseinCPIC(sqInt n, CogMethod *cPIC); +static void alignMethodZoneBase(void); +static NoDbgRegParms sqInt alignUptoRoutineBoundary(sqInt anAddress); +static sqInt allMachineCodeObjectReferencesValid(void); +static sqInt allMethodsHaveCorrectHeader(void); +static NoDbgRegParms AbstractInstruction * annotateAbsolutePCRef(AbstractInstruction *abstractInstruction); +static NoDbgRegParms AbstractInstruction * annotateBytecode(AbstractInstruction *abstractInstruction); +static NoDbgRegParms AbstractInstruction * annotateobjRef(AbstractInstruction *abstractInstruction, sqInt anOop); +static NoDbgRegParms void assertSaneJumpTarget(AbstractInstruction *jumpTarget); +static NoDbgRegParms sqInt availableRegisterOrNoneIn(sqInt liveRegsMask); +static NoDbgRegParms usqInt blockDispatchTargetsForperformarg(CogMethod *cogMethod, usqInt (*binaryFunction)(sqInt mcpc, sqInt arg), sqInt arg); +extern sqInt bytecodePCForstartBcpcin(sqInt mcpc, sqInt startbcpc, CogBlockMethod *cogMethod); +static NoDbgRegParms AbstractInstruction * gCmpCqR(sqInt quickConstant, sqInt reg); +static void callCogCodePopReceiver(void); +static void callCogCodePopReceiverAndClassRegs(void); +static NoDbgRegParms sqInt ceCPICMissreceiver(CogMethod *cPIC, sqInt receiver); +static NoDbgRegParms sqInt ceSICMiss(sqInt receiver); +static NoDbgRegParms sqInt checkIfValidOopRefAndTargetpccogMethod(sqInt annotation, char *mcpc, CogMethod *cogMethod); +static NoDbgRegParms sqInt checkIfValidOopRefpccogMethod(sqInt annotation, char *mcpc, CogMethod *cogMethod); +extern sqInt checkIntegrityOfObjectReferencesInCode(sqInt gcModes); +static NoDbgRegParms sqInt checkMaybeObjRefInClosedPIC(sqInt maybeObject); +static NoDbgRegParms sqInt checkValidObjectReferencesInClosedPIC(CogMethod *cPIC); +static NoDbgRegParms NeverInline sqInt cleanUpFailingCogCodeConstituents(CogMethod *cogMethodArg); +static NoDbgRegParms sqInt closedPICRefersToUnmarkedObject(CogMethod *cPIC); +extern char * codeEntryFor(char *address); +extern char * codeEntryNameFor(char *address); +extern usqInt cogCodeBase(void); +extern sqInt cogCodeConstituents(sqInt withDetails); +static NoDbgRegParms void cogExtendPICCaseNMethodtagisMNUCase(CogMethod *cPIC, sqInt caseNMethod, sqInt caseNTag, sqInt isMNUCase); +extern CogMethod * cogFullBlockMethodnumCopied(sqInt aMethodObj, sqInt numCopied); +extern void cogitPostGCAction(sqInt gcMode); +static NoDbgRegParms sqInt cogMethodDoesntLookKosher(CogMethod *cogMethod); +extern CogMethod * cogMNUPICSelectorreceivermethodOperandnumArgs(sqInt selector, sqInt rcvr, sqInt methodOperand, sqInt numArgs); +static NoDbgRegParms CogMethod * cogOpenPICSelectornumArgs(sqInt selector, sqInt numArgs); +static NoDbgRegParms CogMethod * cogPICSelectornumArgsCase0MethodCase1MethodtagisMNUCase(sqInt selector, sqInt numArgs, CogMethod *case0CogMethod, sqInt case1MethodOrNil, sqInt case1Tag, sqInt isMNUCase); +extern CogMethod * cogselector(sqInt aMethodObj, sqInt aSelectorOop); +static NoDbgRegParms sqInt collectCogConstituentForAnnotationMcpcBcpcMethod(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *cogMethodArg); +static NoDbgRegParms sqInt collectCogConstituentOnSpurForAnnotationMcpcBcpcData(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *dataArg); +static NoDbgRegParms sqInt collectCogMethodConstituentOnSpurofClass(CogMethod *cogMethod, sqInt dataClass); +static NoDbgRegParms sqInt collectCogMethodConstituent(CogMethod *cogMethod); +extern void compactCogCompiledCode(void); +static void compactPICsWithFreedTargets(void); +static AbstractInstruction * compileAbort(void); +static NoDbgRegParms sqInt compileBlockDispatchFromto(sqInt lowBlockStartIndex, sqInt highBlockStartIndex); +static NoDbgRegParms void compileBlockEntry(BlockStart *blockStart); +static NoDbgRegParms void compileCallFornumArgsargargargargresultRegregsToSave(void *aRoutine, sqInt numArgs, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3, sqInt resultRegOrNone, sqInt regMask); +static AbstractInstruction * compileCPICEntry(void); +static NoDbgRegParms sqInt compileEntireFullBlockMethod(sqInt numCopied); +static void compileEntry(void); +static sqInt compileFullBlockEntry(void); +static sqInt compileMethodBody(void); +static NoDbgRegParms sqInt compilePICAbort(sqInt numArgs); +static NoDbgRegParms AbstractInstruction * compileStackOverflowCheck(sqInt canContextSwitch); +static NoDbgRegParms void compileTrampolineFornumArgsargargargargregsToSavepushLinkRegresultReg(void *aRoutine, sqInt numArgs, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3, sqInt regMask, sqInt pushLinkReg, sqInt resultRegOrNone); +static void computeEntryOffsets(void); +static void computeFullBlockEntryOffsets(void); +extern usqInt computeGoodVarBaseAddress(void); +static void computeMaximumSizes(void); +static NoDbgRegParms sqInt configureCPICCase0Case1MethodtagisMNUCasenumArgsdelta(CogMethod *cPIC, CogMethod *case0CogMethod, sqInt case1Method, sqInt case1Tag, sqInt isMNUCase, sqInt numArgs, sqInt addrDelta); +static NoDbgRegParms sqInt configureMNUCPICmethodOperandnumArgsdelta(CogMethod *cPIC, sqInt methodOperand, sqInt numArgs, sqInt addrDelta); +#if VMInvestigations +static NoDbgRegParms sqInt countCogMethodDataImportspccount(sqInt annotation, char *mcpc, sqInt *countPtr); +#endif /* VMInvestigations */ +static NoDbgRegParms sqInt cPICCompactAndIsNowEmpty(CogMethod *cPIC); +static NoDbgRegParms sqInt cPICHasForwardedClass(CogMethod *cPIC); +static NoDbgRegParms sqInt cPICHasFreedTargets(CogMethod *cPIC); +static sqInt cPICPrototypeCaseOffset(void); +static NoDbgRegParms sqInt cPICHasTarget(CogMethod *cPIC, CogMethod *targetMethod); +static NoDbgRegParms sqInt createCPICData(CogMethod *cPIC); +static NoDbgRegParms AbstractInstruction * gDivRRQuoRem(sqInt rDivisor, sqInt rDividend, sqInt rQuotient, sqInt rRemainder); +extern int defaultCogCodeSize(void); +static NoDbgRegParms sqInt deltaToSkipPrimAndErrorStoreInheader(sqInt aMethodObj, sqInt aMethodHeader); +static NoDbgRegParms sqInt endPCOf(sqInt aMethod); +static void enterCogCodePopReceiver(void); +static NoDbgRegParms sqInt entryPointTagIsSelector(sqInt entryPoint); +static NoDbgRegParms sqInt expectedClosedPICPrototype(CogMethod *cPIC); +static sqInt extABytecode(void); +static sqInt extBBytecode(void); +static NoDbgRegParms sqInt fillInBlockHeadersAt(sqInt startAddress); +static NoDbgRegParms void fillInMethodHeadersizeselector(CogMethod *method, sqInt size, sqInt selector); +static NoDbgRegParms sqInt findBackwardBranchIsBackwardBranchMcpcBcpcMatchingBcpc(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *targetBcpc); +static NoDbgRegParms usqInt findBlockMethodWithEntrystartBcpc(sqInt blockEntryMcpc, sqInt startBcpc); +static NoDbgRegParms sqInt findMapLocationForMcpcinMethod(usqInt targetMcpc, CogMethod *cogMethod); +extern CogBlockMethod * findMethodForStartBcpcinHomeMethod(sqInt startbcpc, CogMethod *cogMethod); +static NoDbgRegParms sqInt findIsBackwardBranchMcpcBcpcMatchingMcpc(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *targetMcpc); +static NoDbgRegParms usqInt firstMappedPCFor(CogMethod *cogMethod); +static sqInt firstPrototypeMethodOop(void); +static NoDbgRegParms BytecodeFixup * fixupAt(sqInt fixupPC); +extern void flagCogMethodForBecome(CogMethod *cogMethod); +static NoDbgRegParms void followForwardedLiteralsImplementationIn(CogMethod *cogMethod); +extern void followForwardedLiteralsIn(CogMethod *cogMethod); +extern void followMovableLiteralsAndUpdateYoungReferrers(void); +extern void freeBecomeFlaggedMethods(void); +extern void freeCogMethod(CogMethod *cogMethod); +extern void freeUnmarkedMachineCode(void); +static NoDbgRegParms AbstractInstruction * genCallMustBeBooleanFor(sqInt boolean); +static NoDbgRegParms AbstractInstruction * genConditionalBranchoperand(sqInt opcode, sqInt operandOne); +static NoDbgRegParms void (*genEnilopmartForandandforCallcalled(sqInt regArg1, sqInt regArg2OrNone, sqInt regArg3OrNone, sqInt forCall, char *trampolineName))(void); +static NoDbgRegParms void genEnilopmartReturn(sqInt forCall); +static NoDbgRegParms NeverInline void generateCaptureCStackPointers(sqInt captureFramePointer); +static void generateClosedPICPrototype(void); +static CogMethod * generateCogFullBlock(void); +static NoDbgRegParms CogMethod * generateCogMethod(sqInt selector); +static NoDbgRegParms sqInt generateMapAtstart(usqInt addressOrNull, usqInt startAddress); +static void generateOpenPICPrototype(void); +static void generateStackPointerCapture(void); +static void generateTrampolines(void); +static NoDbgRegParms BytecodeDescriptor * generatorForPC(sqInt pc); +static void genGetLeafCallStackPointers(void); +static NoDbgRegParms usqInt genInnerPICAbortTrampoline(char *name); +static void (*genInvokeInterpretTrampoline(void))(void); +static NoDbgRegParms void genLoadInlineCacheWithSelector(sqInt selectorIndex); +static usqInt genReturnToInterpreterTrampoline(void); +static NoDbgRegParms sqInt genSmalltalkToCStackSwitch(sqInt pushLinkReg); +static NoDbgRegParms usqInt genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(void *aRoutine, char *trampolineName, sqInt numArgs, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3, sqInt regMask, sqInt pushLinkReg, sqInt resultRegOrNone, sqInt appendBoolean); +static NoDbgRegParms void genTrampolineReturn(sqInt lnkRegWasPushed); +static NoDbgRegParms AbstractInstruction * gen(sqInt opcode); +static NoDbgRegParms AbstractInstruction * genoperand(sqInt opcode, sqInt operand); +static NoDbgRegParms AbstractInstruction * genoperandoperand(sqInt opcode, sqInt operandOne, sqInt operandTwo); +static NoDbgRegParms AbstractInstruction * genoperandoperandoperand(sqInt opcode, sqInt operandOne, sqInt operandTwo, sqInt operandThree); +static NoDbgRegParms sqInt getCogCodeConstituents(sqInt withDetails); +static NoDbgRegParms sqInt getLiteral(sqInt litIndex); +#if VMInvestigations +extern usqInt getMethodZoneBase(void); +#endif /* VMInvestigations */ +static sqInt getOpcodeIndex(void); +static NoDbgRegParms sqInt incrementUsageOfTargetIfLinkedSendmcpcignored(sqInt annotation, char *mcpc, sqInt superfluity); +static NoDbgRegParms sqInt indexForSelectorin(sqInt selector, CogMethod *cogMethod); +extern void initializeCodeZoneFromupTo(sqInt startAddress, sqInt endAddress); +static sqInt initialMethodUsageCount(void); +static int initialOpenPICUsageCount(void); +static NoDbgRegParms int intraCPICTargetinvokesMNUIn(sqInt targetMcpc, CogMethod *cPIC); +static NoDbgRegParms sqInt inverseBranchFor(sqInt opcode); +#if VMInvestigations +static NoDbgRegParms sqInt investigationArrayForClosedPIC(CogMethod *cPIC); +#endif /* VMInvestigations */ +#if VMInvestigations +static NoDbgRegParms sqInt investigationTargetForEntryPointin(sqInt entryPoint, CogMethod *cPIC); +#endif /* VMInvestigations */ +extern int isPCWithinMethodZone(usqInt address); +extern sqInt isSendReturnPC(sqInt retpc); +static NoDbgRegParms AbstractInstruction * gJumpFPEqual(void *jumpTarget); +static NoDbgRegParms AbstractInstruction * gJumpFPGreaterOrEqual(void *jumpTarget); +static NoDbgRegParms AbstractInstruction * gJumpFPGreater(void *jumpTarget); +static NoDbgRegParms AbstractInstruction * gJumpFPNotEqual(void *jumpTarget); +static NoDbgRegParms AbstractInstruction * gLogicalShiftLeftCqR(sqInt quickConstant, sqInt reg); +static NoDbgRegParms AbstractInstruction * gLogicalShiftLeftCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg); +static NoDbgRegParms AbstractInstruction * gLogicalShiftRightCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg); +static AbstractInstruction * lastOpcode(void); +extern CogMethod * linkedSendTargetOrNilAt(sqInt retpc); +extern void linkSendAtintooffsetreceiver(sqInt callSiteReturnAddress, CogMethod *sendingMethod, CogMethod *targetMethod, sqInt theEntryOffset, sqInt receiver); +static NoDbgRegParms BytecodeDescriptor * loadBytesAndGetDescriptor(sqInt pc); +static NoDbgRegParms AbstractInstruction * gMoveCqR(sqInt quickConstant, sqInt reg); +static NoDbgRegParms AbstractInstruction * gMovePerfCnt64RL(sqInt destReg, sqInt liveRegisterMask); +static NoDbgRegParms AbstractInstruction * gMoveRMwr(sqInt sourceReg, sqInt offset, sqInt baseReg); +static NoDbgRegParms sqInt mapEndFor(CogMethod *cogMethod); +static NoDbgRegParms sqInt mapForperformUntilarg(CogMethod *cogMethod, sqInt (*functionSymbol)(sqInt annotation, char *mcpc, CogMethod *arg), void *arg); +static NoDbgRegParms sqInt mapObjectReferencesInClosedPIC(CogMethod *cPIC); +static void mapObjectReferencesInGeneratedRuntime(void); +static void mapObjectReferencesInMachineCodeForBecome(void); +static void mapObjectReferencesInMachineCodeForFullGC(void); +static void mapObjectReferencesInMachineCodeForYoungGC(void); +extern void mapObjectReferencesInMachineCode(sqInt gcMode); +extern void markAndTraceMachineCodeOfMarkedMethods(void); +static void markAndTraceObjectReferencesInGeneratedRuntime(void); +static NoDbgRegParms sqInt markAndTraceOrFreeCogMethodfirstVisit(CogMethod *cogMethod, sqInt firstVisit); +static NoDbgRegParms sqInt markAndTraceOrFreePICTargetin(sqInt entryPoint, CogMethod *cPIC); +static NoDbgRegParms sqInt markLiteralsAndUnlinkIfUnmarkedSendpcmethod(sqInt annotation, char *mcpc, CogMethod *cogMethod); +static NoDbgRegParms sqInt markLiteralspcmethod(sqInt annotation, char *mcpc, CogMethod *cogMethod); +extern void markMethodAndReferents(CogBlockMethod *aCogMethod); +extern usqInt maxCogMethodAddress(void); +static NoDbgRegParms sqInt maximumDistanceFromCodeZone(sqInt anAddress); +static NoDbgRegParms sqInt maybeFreeCogMethodDoesntLookKosher(CogMethod *cogMethod); +static int mclassCouldBeContext(void); +static int mclassIsSmallInteger(void); +extern usqInt mcPCForBackwardBranchstartBcpcin(sqInt bcpc, sqInt startbcpc, CogBlockMethod *cogMethod); +static NoDbgRegParms sqInt methodhasSameCodeAscheckPenultimate(sqInt methodA, sqInt methodB, sqInt comparePenultimateLiteral); +extern sqInt mnuOffset(void); +static NoDbgRegParms AbstractInstruction * gNativePopR(sqInt reg); +static NoDbgRegParms AbstractInstruction * gNativePushR(sqInt reg); +static NoDbgRegParms AbstractInstruction * gNativeRetN(sqInt offset); +static NoDbgRegParms sqInt needsFrameIfImmutability(sqInt stackDelta); +static NoDbgRegParms sqInt needsFrameIfInBlock(sqInt stackDelta); +static NoDbgRegParms sqInt needsFrameIfReadMediated(sqInt stackDelta); +static NoDbgRegParms sqInt needsFrameNever(sqInt stackDelta); +static NoDbgRegParms sqInt noAssertMethodClassAssociationOf(sqInt methodPointer); +static sqInt noCogMethodsMaximallyMarked(void); +static NoDbgRegParms int noTargetsFreeInClosedPIC(CogMethod *cPIC); +static NoDbgRegParms AbstractInstruction * gOrCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg); +static NoDbgRegParms sqInt outputInstructionsAt(sqInt startAddress); +static NoDbgRegParms sqInt outputInstructionsForGeneratedRuntimeAt(sqInt startAddress); +extern sqInt patchToOpenPICFornumArgsreceiver(sqInt selector, sqInt numArgs, sqInt receiver); +static sqInt picAbortDiscriminatorValue(void); +static sqInt picInterpretAbortOffset(void); +extern void printCogMethodFor(void *address); +#if VMInvestigations +extern void printPCMapPairsFor(CogMethod *cogMethod); +#endif /* VMInvestigations */ +extern void printTrampolineTable(void); +static sqInt processorHasDivQuoRemAndMClassIsSmallInteger(void); +static sqInt processorHasMultiplyAndMClassIsSmallInteger(void); +static NoDbgRegParms void recordGeneratedRunTimeaddress(char *aString, sqInt address); +extern int recordPrimTraceFunc(void); +static void recordRunTimeObjectReferences(void); +static NoDbgRegParms sqInt registerMaskFor(sqInt reg); +static NoDbgRegParms sqInt registerMaskForand(sqInt reg1, sqInt reg2); +static NoDbgRegParms void relocateCallsAndSelfReferencesInMethod(CogMethod *cogMethod); +static NoDbgRegParms void relocateCallsInClosedPIC(CogMethod *cPIC); +static NoDbgRegParms sqInt relocateIfCallOrMethodReferencemcpcdelta(sqInt annotation, char *mcpc, void *refDeltaArg); +static NoDbgRegParms sqInt remapIfObjectRefpchasYoung(sqInt annotation, char *mcpc, void *hasYoungPtr); +static NoDbgRegParms sqInt remapMaybeObjRefInClosedPICAt(sqInt mcpc); +static NoDbgRegParms void rewriteCPICCaseAttagobjReftarget(sqInt followingAddress, sqInt newTag, sqInt newObjRef, sqInt newTarget); +static NoDbgRegParms AbstractInstruction * gSubRRR(sqInt subReg, sqInt fromReg, sqInt destReg); +static sqInt scanForCleanBlocks(void); +extern void setSelectorOfto(CogMethod *cogMethod, sqInt aSelectorOop); +static NoDbgRegParms sqInt spanForCleanBlockStartingAt(sqInt startPC); +#if VMInvestigations +static NoDbgRegParms sqInt storeCogMethodDataImportspcpointer(sqInt annotation, char *mcpc, usqInt **importsPtrPtr); +#endif /* VMInvestigations */ +#if VMInvestigations +static NoDbgRegParms sqInt stringForEntryPointexistingImportslimit(char *entryPoint, sqInt imports, sqInt *nextStoreLocation); +#endif /* VMInvestigations */ +static sqInt subsequentPrototypeMethodOop(void); +static NoDbgRegParms AbstractInstruction * gTstCqR(sqInt quickConstant, sqInt reg); +extern sqInt traceLinkedSendOffset(void); +static NoDbgRegParms char * trampolineNamenumArgs(char *routinePrefix, sqInt numArgs); +static NoDbgRegParms char * trampolineNamenumArgslimit(char *routinePrefix, int numArgs, sqInt argsLimit); +static NoDbgRegParms char * trampolineNamenumRegArgs(char *routinePrefix, sqInt numArgs); +extern void unflagBecomeFlaggedMethods(void); +static sqInt unknownBytecode(void); +extern void unlinkAllSends(void); +static NoDbgRegParms sqInt unlinkIfFreeOrLinkedSendpcof(sqInt annotation, char *mcpc, void *theSelector); +static NoDbgRegParms sqInt unlinkIfInvalidClassSendpcignored(sqInt annotation, char *mcpc, sqInt superfluity); +static NoDbgRegParms sqInt unlinkIfLinkedSendToFreepcignored(sqInt annotation, char *mcpc, sqInt superfluity); +static NoDbgRegParms sqInt unlinkIfLinkedSendpcif(sqInt annotation, char *mcpc, sqInt (*criterion)(CogMethod *)); +static NoDbgRegParms sqInt unlinkIfLinkedSendpcignored(sqInt annotation, char *mcpc, sqInt superfluity); +static NoDbgRegParms sqInt unlinkIfLinkedSendpcto(sqInt annotation, char *mcpc, CogMethod *theCogMethod); +extern void unlinkSendsLinkedForInvalidClasses(void); +extern void unlinkSendsOfisMNUSelector(sqInt selector, sqInt isMNUSelector); +static void unlinkSendsToFree(void); +extern void unlinkSendsToMethodsSuchThatAndFreeIf(sqInt (*criterion)(CogMethod *), sqInt freeIfTrue); +extern void unlinkSendsToandFreeIf(sqInt targetMethodObject, sqInt freeIfTrue); +static NoDbgRegParms sqInt v3BlockCodeSize(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +static NoDbgRegParms sqInt v3LongForwardBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +static NoDbgRegParms sqInt v3LongBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +static NoDbgRegParms sqInt v3ShortForwardBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +static NoDbgRegParms sqInt v4BlockCodeSize(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +static NoDbgRegParms sqInt v4LongForwardBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +static NoDbgRegParms sqInt v4LongBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +extern void voidCogCompiledCode(void); +static void zeroOpcodeIndex(void); +static NoDbgRegParms void addToOpenPICList(CogMethod *anOpenPIC); +static NoDbgRegParms void addToYoungReferrers(CogMethod *writableCogMethod); +static NoDbgRegParms usqInt allocate(sqInt numBytes); +extern CogMethod * cogMethodContaining(usqInt mcpc); +static void compactCompiledCode(void); +static NoDbgRegParms void ensureInYoungReferrers(CogMethod *cogMethod); +static NoDbgRegParms void freeMethod(CogMethod *cogMethod); +static void freeOlderMethodsForCompaction(void); +extern sqInt kosherYoungReferrers(void); +extern CogMethod * methodFor(void *address); +#if VMInvestigations +extern sqInt methodsCompiledToMachineCodeInto(sqInt arrayObj); +#endif /* VMInvestigations */ +extern sqInt numMethods(void); +extern sqInt numMethodsOfType(sqInt cogMethodType); +static NoDbgRegParms sqInt occurrencesInYoungReferrers(CogMethod *cogMethod); +static NoDbgRegParms CogMethod * openPICWithSelector(sqInt aSelector); +static void planCompaction(void); +extern void printCogMethods(void); +extern void printCogMethodsOfType(sqInt cmType); +extern void printCogMethodsWithMethod(sqInt methodOop); +extern void printCogMethodsWithPrimitive(sqInt primIdx); +extern void printCogMethodsWithSelector(sqInt selectorOop); +extern void printCogYoungReferrers(void); +extern sqInt printOpenPICList(void); +static sqInt pruneYoungReferrers(void); +static sqInt relocateAndPruneYoungReferrers(void); +static sqInt relocateMethodsPreCompaction(void); +static NoDbgRegParms sqInt removeFromOpenPICList(CogMethod *anOpenPIC); +static NoDbgRegParms int roundUpLength(sqInt numBytes); +static void voidOpenPICList(void); +static void voidUnpairedMethodList(void); +extern char * whereIsMaybeCodeThing(sqInt anOop); +static sqInt zoneAlignment(void); +static NoDbgRegParms sqInt checkValidObjectReference(sqInt anOop); +static NoDbgRegParms AbstractInstruction * genCmpClassFloatCompactIndexR(sqInt reg); +static NoDbgRegParms AbstractInstruction * genCmpClassMethodContextCompactIndexR(sqInt reg); +static NoDbgRegParms sqInt genGetMethodHeaderOfintoscratch(sqInt methodReg, sqInt headerReg, sqInt scratchReg); +static NoDbgRegParms AbstractInstruction * genJumpNotSmallIntegersInandscratch(sqInt aRegister, sqInt bRegister, sqInt scratchRegister); +static NoDbgRegParms sqInt genLoadSlotsourceRegdestReg(sqInt index, sqInt sourceReg, sqInt destReg); +static sqInt genPrimitiveAdd(void); +static sqInt genPrimitiveBitAnd(void); +static sqInt genPrimitiveBitOr(void); +static sqInt genPrimitiveBitShift(void); +static sqInt genPrimitiveBitXor(void); +static sqInt genPrimitiveClass(void); +static sqInt genPrimitiveDiv(void); +static sqInt genPrimitiveDivide(void); +static sqInt genPrimitiveEqual(void); +static sqInt genPrimitiveFloatAdd(void); +static sqInt genPrimitiveFloatDivide(void); +static sqInt genPrimitiveFloatMultiply(void); +static sqInt genPrimitiveFloatSubtract(void); +static sqInt genPrimitiveGreaterOrEqual(void); +static sqInt genPrimitiveGreaterThan(void); +static sqInt genPrimitiveHighBit(void); +static sqInt genPrimitiveIdentical(void); +static sqInt genPrimitiveLessOrEqual(void); +static sqInt genPrimitiveLessThan(void); +static sqInt genPrimitiveMaxIdentityHash(void); +static sqInt genPrimitiveMod(void); +static sqInt genPrimitiveMultiply(void); +static sqInt genPrimitiveNewMethod(void); +static sqInt genPrimitiveNotEqual(void); +static sqInt genPrimitiveNotIdentical(void); +static sqInt genPrimitiveQuo(void); +static sqInt genPrimitiveSubtract(void); +static NoDbgRegParms sqInt genSmallIntegerComparison(sqInt jumpOpcode); +static NoDbgRegParms sqInt isUnannotatableConstant(SimStackEntry *simStackEntry); +static NoDbgRegParms sqInt classForInlineCacheTag(sqInt classIndex); +static NoDbgRegParms sqInt genAddSmallIntegerTagsTo(sqInt aRegister); +static NoDbgRegParms sqInt genClearAndSetSmallIntegerTagsIn(sqInt scratchReg); +static NoDbgRegParms sqInt genConvertBitsToSmallFloatInscratch(sqInt reg, sqInt scratch); +static NoDbgRegParms void genConvertCharacterToSmallIntegerInReg(sqInt reg); +static NoDbgRegParms sqInt genConvertIntegerInRegtoSmallIntegerInReg(sqInt srcReg, sqInt destReg); +static NoDbgRegParms sqInt genConvertIntegerToSmallIntegerInReg(sqInt reg); +static NoDbgRegParms sqInt genConvertSmallFloatToSmallFloatHashAsIntegerInRegscratch(sqInt reg, sqInt scratch); +static NoDbgRegParms void genConvertSmallIntegerToCharacterInReg(sqInt reg); +static NoDbgRegParms sqInt genConvertSmallIntegerToIntegerInReg(sqInt reg); +static NoDbgRegParms sqInt genFetchRegArgsForPerformWithArguments(sqInt sizeReg); +static NoDbgRegParms sqInt genFloatArithmeticpreOpCheckboxed(sqInt arithmeticOperator, AbstractInstruction *(*preOpCheckOrNil)(int rcvrReg, int argReg), sqInt rcvrBoxed); +static NoDbgRegParms sqInt genFloatComparisonorIntegerComparisoninvertboxed(AbstractInstruction *(*jumpFPOpcodeGenerator)(void *), sqInt jumpOpcode, sqInt invertComparison, sqInt rcvrBoxed); +static NoDbgRegParms sqInt genGetHashFieldNonImmOfasSmallIntegerInto(sqInt instReg, sqInt destReg); +static NoDbgRegParms sqInt genGetHashFieldNonImmOfinto(sqInt instReg, sqInt destReg); +static NoDbgRegParms AbstractInstruction * genGetInlineCacheClassTagFromintoforEntry(sqInt sourceReg, sqInt destReg, sqInt forEntry); +static NoDbgRegParms sqInt genGetNumBytesOfinto(sqInt srcReg, sqInt destReg); +static NoDbgRegParms sqInt genGetOverflowSlotsOfinto(sqInt srcReg, sqInt destReg); +static NoDbgRegParms sqInt genGetSmallFloatValueOfscratchinto(sqInt oopReg, sqInt scratch, sqInt dpReg); +static NoDbgRegParms AbstractInstruction * genInstantiate64BitPositiveIntegerValueintoscratchRegmayFail(sqInt valueReg, sqInt resultReg, sqInt scratch, sqInt mayFail); +static NoDbgRegParms AbstractInstruction * genInstantiate64BitSignedIntegerValueintoscratchRegmayFail(sqInt valueReg, sqInt resultReg, sqInt scratch, sqInt mayFail); +static NoDbgRegParms AbstractInstruction * genInstantiateFloatValueintoscratchRegscratchRegmayFail(sqInt dpreg, sqInt resultReg, sqInt scratch1, sqInt scratch2, sqInt mayFail); +static NoDbgRegParms AbstractInstruction * genJumpIsSmallIntegerValuescratch(sqInt aRegister, sqInt scratchReg); +static NoDbgRegParms AbstractInstruction * genJumpNotCharacter(sqInt reg); +static NoDbgRegParms AbstractInstruction * genJumpNotSmallFloatValueBitsscratch(sqInt reg, sqInt exponent); +static NoDbgRegParms AbstractInstruction * genJumpNotSmallFloat(sqInt reg); +static NoDbgRegParms AbstractInstruction * genJumpNotSmallIntegerValuescratch(sqInt aRegister, sqInt scratchReg); +static NoDbgRegParms AbstractInstruction * genJumpNotSmallInteger(sqInt reg); +static NoDbgRegParms AbstractInstruction * genJumpSmallInteger(sqInt aRegister); +static NoDbgRegParms sqInt genPrimitiveAtPutSigned(sqInt signedVersion); +static NoDbgRegParms sqInt genPrimitiveAtSigned(sqInt signedVersion); +static sqInt genPrimitiveFloatEqual(void); +static sqInt genPrimitiveFloatGreaterOrEqual(void); +static sqInt genPrimitiveFloatGreaterThan(void); +static sqInt genPrimitiveFloatLessOrEqual(void); +static sqInt genPrimitiveFloatLessThan(void); +static sqInt genPrimitiveFloatNotEqual(void); +static sqInt genPrimitiveIdentityHash(void); +static sqInt genPrimitiveImmediateAsInteger(void); +static sqInt genPrimitiveNew(void); +static sqInt genPrimitiveNewWithArg(void); +static sqInt genPrimitiveShallowCopy(void); +static sqInt genPrimitiveSlotAt(void); +static sqInt genPrimitiveSmallFloatAdd(void); +static sqInt genPrimitiveSmallFloatDivide(void); +static sqInt genPrimitiveSmallFloatEqual(void); +static sqInt genPrimitiveSmallFloatGreaterOrEqual(void); +static sqInt genPrimitiveSmallFloatGreaterThan(void); +static sqInt genPrimitiveSmallFloatLessOrEqual(void); +static sqInt genPrimitiveSmallFloatLessThan(void); +static sqInt genPrimitiveSmallFloatMultiply(void); +static sqInt genPrimitiveSmallFloatNotEqual(void); +static sqInt genPrimitiveSmallFloatSquareRoot(void); +static sqInt genPrimitiveSmallFloatSubtract(void); +static sqInt genPrimitiveStringAt(void); +static sqInt genPrimitiveStringAtPut(void); +static sqInt genPrimitiveUninitializedNewWithArg(void); +static NoDbgRegParms sqInt genPureFloatArithmeticpreOpCheckboxed(sqInt arithmeticOperator, AbstractInstruction *(*preOpCheckOrNil)(int rcvrReg, int argReg), sqInt rcvrBoxed); +static NoDbgRegParms sqInt genPureFloatComparisoninvertboxed(AbstractInstruction *(*jumpFPOpcodeGenerator)(void *), sqInt invertComparison, sqInt rcvrBoxed); +static NoDbgRegParms sqInt genRemoveSmallIntegerTagsInScratchReg(sqInt scratchReg); +static NoDbgRegParms sqInt genShiftAwaySmallIntegerTagsInScratchReg(sqInt scratchReg); +static NoDbgRegParms sqInt genSmallIntegerComparisonorDoubleComparisoninvert(sqInt jumpOpcode, AbstractInstruction * NoDbgRegParms (*jumpFPOpcodeGenerator)(void *), sqInt invertComparison); +static NoDbgRegParms sqInt getLiteralCountOfplusOneinBytesintoscratch(sqInt methodReg, sqInt plusOne, sqInt inBytes, sqInt litCountReg, sqInt scratchReg); +static NoDbgRegParms sqInt inlineCacheTagForInstance(sqInt oop); +static sqInt log2BytesPerWord(void); +static void maybeGenerateSelectorIndexDereferenceRoutine(void); +static sqInt numSmallIntegerBits(void); +static sqInt numSmallIntegerTagBits(void); +static NoDbgRegParms sqInt validInlineCacheTag(sqInt classIndexOrTagPattern); +static void callStoreCheckTrampoline(void); +static NoDbgRegParms sqInt checkValidOopReference(sqInt anOop); +static void compileInvokeCENewHashOf(void); +static NoDbgRegParms AbstractInstruction * compileJumpToRetryOfonPrimitiveFail(void (*primitiveRoutine)(void), sqInt primIndex); +static NoDbgRegParms sqInt couldBeObject(sqInt literal); +static NoDbgRegParms usqInt genActiveContextTrampolineLargeinBlockcalled(sqInt isLarge, sqInt isInBlock, char *aString); +static NoDbgRegParms AbstractInstruction * genCheckRememberedBitOfscratch(sqInt objReg, sqInt scratchReg); +static NoDbgRegParms sqInt genConvertCharacterToCodeInReg(sqInt reg); +static NoDbgRegParms sqInt genConvertIntegerToCharacterInReg(sqInt reg); +static NoDbgRegParms sqInt genCreateFullClosurenumArgsnumCopiedignoreContextcontextNumArgslargeinBlock(sqInt compiledBlock, sqInt numArgs, sqInt numCopied, sqInt ignoreContext, sqInt contextNumArgs, sqInt contextIsLarge, sqInt contextIsBlock); +static NoDbgRegParms sqInt genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(sqInt reg, sqInt scratch, void *fwdJumpTarget, void *nonFwdJumpTargetOrZero); +static NoDbgRegParms sqInt genEnsureOopInRegNotForwardedscratchRegupdatingSlotin(sqInt reg, sqInt scratch, sqInt index, sqInt objReg); +static NoDbgRegParms sqInt genGetActiveContextLargeinBlock(sqInt isLarge, sqInt isInBlock); +static NoDbgRegParms sqInt genGetActiveContextNumArgslargeinBlock(sqInt numArgs, sqInt isLargeContext, sqInt isInBlock); +static NoDbgRegParms sqInt genGetBitsofFormatByteOfinto(sqInt mask, sqInt sourceReg, sqInt destReg); +static NoDbgRegParms sqInt genGetClassIndexOfNonImminto(sqInt sourceReg, sqInt destReg); +static NoDbgRegParms sqInt genGetClassObjectOfClassIndexintoscratchReg(sqInt instReg, sqInt destReg, sqInt scratchReg); +static NoDbgRegParms sqInt genGetClassObjectOfintoscratchRegmayBeAForwarder(sqInt instReg, sqInt destReg, sqInt scratchReg, sqInt mayBeForwarder); +static NoDbgRegParms AbstractInstruction * genGetClassTagOfintoscratchReg(sqInt instReg, sqInt destReg, sqInt scratchReg); +static NoDbgRegParms sqInt genGetCompactClassIndexNonImmOfinto(sqInt instReg, sqInt destReg); +static NoDbgRegParms sqInt genGetDoubleValueOfinto(sqInt srcReg, sqInt destFPReg); +static NoDbgRegParms sqInt genGetFormatOfinto(sqInt srcReg, sqInt destReg); +static NoDbgRegParms sqInt genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(sqInt sourceReg, sqInt destReg, sqInt scratchRegOrNone); +static NoDbgRegParms sqInt genGetNumSlotsOfinto(sqInt srcReg, sqInt destReg); +static NoDbgRegParms sqInt genGetRawSlotSizeOfNonImminto(sqInt sourceReg, sqInt destReg); +static NoDbgRegParms AbstractInstruction * genJumpImmediate(sqInt aRegister); +#if IMMUTABILITY +static NoDbgRegParms AbstractInstruction * genJumpImmutablescratchReg(sqInt sourceReg, sqInt scratchReg); +#endif /* IMMUTABILITY */ +#if IMMUTABILITY +static NoDbgRegParms AbstractInstruction * genJumpMutablescratchReg(sqInt sourceReg, sqInt scratchReg); +#endif /* IMMUTABILITY */ +static NoDbgRegParms sqInt genNewArrayOfSizeinitialized(sqInt size, sqInt initialize); +static NoDbgRegParms sqInt genNoPopCreateClosureAtnumArgsnumCopiedcontextNumArgslargeinBlock(sqInt bcpc, sqInt numArgs, sqInt numCopied, sqInt ctxtNumArgs, sqInt isLargeCtxt, sqInt isInBlock); +static sqInt genPrimitiveAsCharacter(void); +static sqInt genPrimitiveAsFloat(void); +static sqInt genPrimitiveAt(void); +static sqInt genPrimitiveAtPut(void); +static sqInt genPrimitiveFloatSquareRoot(void); +static NoDbgRegParms sqInt genPrimitiveIdenticalOrNotIf(sqInt orNot); +static sqInt genPrimitiveIntegerAt(void); +static sqInt genPrimitiveIntegerAtPut(void); +static sqInt genPrimitiveMakePoint(void); +static sqInt genPrimitiveObjectAt(void); +static sqInt genPrimitiveSize(void); +static sqInt genPrimitiveStringCompareWith(void); +static sqInt genPrimitiveStringReplace(void); +static NoDbgRegParms sqInt genSetSmallIntegerTagsIn(sqInt scratchReg); +static usqInt genStoreCheckContextReceiverTrampoline(void); +static NoDbgRegParms sqInt genStoreCheckReceiverRegvalueRegscratchReginFrame(sqInt destReg, sqInt valueReg, sqInt scratchReg, sqInt inFrame); +static NoDbgRegParms sqInt genStoreSourceRegslotIndexdestRegscratchReginFrameneedsStoreCheck(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt inFrame, sqInt needsStoreCheck); +static NoDbgRegParms sqInt genStoreSourceRegslotIndexintoNewObjectInDestReg(sqInt sourceReg, sqInt index, sqInt destReg); +#if IMMUTABILITY +static NoDbgRegParms usqInt genStoreTrampolineCalledinstVarIndex(char *trampolineName, sqInt instVarIndex); +#endif /* IMMUTABILITY */ +#if IMMUTABILITY +static NoDbgRegParms sqInt genStoreWithImmutabilityAndStoreCheckSourceRegslotIndexdestRegscratchRegneedRestoreRcvr(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt needRestoreRcvr); +#endif /* IMMUTABILITY */ +#if IMMUTABILITY +static NoDbgRegParms sqInt genStoreWithImmutabilityButNoStoreCheckSourceRegslotIndexdestRegscratchRegneedRestoreRcvr(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt needRestoreRcvr); +#endif /* IMMUTABILITY */ +#if IMMUTABILITY +static NoDbgRegParms sqInt genStoreWithImmutabilityCheckSourceRegslotIndexdestRegscratchRegneedsStoreCheckneedRestoreRcvr(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt needsStoreCheck, sqInt needRestoreRcvr); +#endif /* IMMUTABILITY */ +static sqInt getActiveContextAllocatesInMachineCode(void); +static NoDbgRegParms sqInt inlineCacheTagIsYoung(sqInt cacheTag); +static NoDbgRegParms AbstractInstruction * jumpNotCharacterUnsignedValueInRegister(sqInt reg); +static NoDbgRegParms sqInt markAndTraceLiteralinatpc(sqInt literal, CogMethod *cogMethodOrNil, usqInt address); +static NoDbgRegParms void markAndTraceLiteralinat(sqInt literal, CogMethod *cogMethod, sqInt *address); +static NoDbgRegParms void markAndTraceUpdatedLiteralin(sqInt objOop, CogMethod *cogMethodOrNil); +static NoDbgRegParms sqInt maybeCompileRetryOfonPrimitiveFailflagsjumpcontinue(void (*primitiveRoutine)(void), sqInt primIndex, sqInt flags, AbstractInstruction *jmp, AbstractInstruction *label); +static NoDbgRegParms sqInt maybeShiftClassTagRegisterForMethodCacheProbe(sqInt classTagReg); +static sqInt numCharacterBits(void); +static NoDbgRegParms sqInt remapObject(sqInt objOop); +static NoDbgRegParms sqInt remapOop(sqInt objOop); +static NoDbgRegParms sqInt shouldAnnotateObjectReference(sqInt anOop); +static NoDbgRegParms sqInt slotOffsetOfInstVarIndex(sqInt index); +static NoDbgRegParms SimStackEntry * ensureSpilledAtfrom(SimStackEntry *self_in_CogSimStackEntry, sqInt baseOffset, sqInt baseRegister); +static NoDbgRegParms sqInt isSameEntryAs(SimStackEntry *self_in_CogSimStackEntry, SimStackEntry *ssEntry); +static NoDbgRegParms sqInt mayBeAForwarder(SimStackEntry *self_in_CogSimStackEntry); +static NoDbgRegParms SimStackEntry * popToReg(SimStackEntry *self_in_CogSimStackEntry, sqInt reg); +static NoDbgRegParms sqInt registerMask(SimStackEntry *self_in_CogSimStackEntry); +static NoDbgRegParms sqInt registerMaskOrNone(SimStackEntry *self_in_CogSimStackEntry); +static NoDbgRegParms sqInt registerOrNone(SimStackEntry *self_in_CogSimStackEntry); +static NoDbgRegParms SimStackEntry * storeToReg(SimStackEntry *self_in_CogSimStackEntry, sqInt reg); +static NoDbgRegParms int isMergeFixup(BytecodeFixup *self_in_CogSSBytecodeFixup); +static sqInt generateRunTimeTrampolines(void); +static NoDbgRegParms void genMoveCurrentMethodOopToRegister(sqInt reg); +static NoDbgRegParms sqInt genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(sqInt popBoolean, sqInt slotIndex, sqInt needsStoreCheck, sqInt needsImmCheck); +static NoDbgRegParms sqInt genStorePopRemoteTempAtneedsStoreCheck(sqInt popBoolean, sqInt slotIndex, sqInt remoteTempIndex, sqInt needsStoreCheck); +static NoDbgRegParms sqInt genStorePopTemporaryVariable(sqInt popBoolean, sqInt tempIndex); +static sqInt genUpArrowReturn(void); +static NoDbgRegParms void initSimStackForFramefulMethod(sqInt startpc); +static NoDbgRegParms void initSimStackForFramelessMethod(sqInt startpc); +static void generateObjectRepresentationTrampolines(void); +static sqInt genPrimitiveSlotAtPut(void); +static NoDbgRegParms AbstractInstruction * allocateLiteral(sqInt aLiteral); +static NoDbgRegParms sqInt literalInstructionInRange(AbstractInstruction *litInst); +static NoDbgRegParms AbstractInstruction * checkLiteral32forInstruction(sqInt literal, AbstractInstruction *anInstruction); +static NoDbgRegParms AbstractInstruction * checkLiteralforInstruction(sqInt literal, AbstractInstruction *anInstruction); +static NoDbgRegParms sqInt dumpLiterals(sqInt generateBranchAround); +static NoDbgRegParms AbstractInstruction * locateLiteralsize(sqInt aLiteral, sqInt litSize); +#if VMInvestigations +extern char * abiName(void); +#endif /* VMInvestigations */ +extern sqInt cogMethodHasExternalPrim(CogMethod *aCogMethod); +extern sqInt cogMethodHasMachineCodePrim(CogMethod *aCogMethod); +#if VMInvestigations +extern sqInt cogMethodImportsintodetailCPICs(CogMethod *cogMethod, sqInt data, sqInt detailCPICs); +#endif /* VMInvestigations */ +static sqInt compileBlockDispatch(void); +static void compileGetErrorCode(void); +static sqInt compileInterpreterPrimitive(void); +static NoDbgRegParms sqInt compileInterpreterPrimitiveflags(void (*primitiveRoutine)(void), sqInt flags); +static NoDbgRegParms sqInt compileOnStackExternalPrimitiveflags(void (*primitiveRoutine)(void), sqInt flags); +static NoDbgRegParms AbstractInstruction * compileOpenPICMethodCacheProbeForwithShiftbaseRegOrNone(sqInt selector, sqInt shift, sqInt baseRegOrNone); +static NoDbgRegParms void compileOpenPICnumArgs(sqInt selector, sqInt numArgs); +static NoDbgRegParms AbstractInstruction * compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(sqInt selectorReg, sqInt shift, sqInt baseRegOrNone); +static sqInt compilePrimitive(void); +static sqInt extendedPushBytecode(void); +static sqInt extendedStoreAndPopBytecode(void); +static sqInt extendedStoreBytecode(void); +static NoDbgRegParms sqInt frameOffsetOfTemporary(sqInt index); +static sqInt genCallMappedInlinedPrimitive(void); +static NoDbgRegParms AbstractInstruction * genDoubleFailIfZeroArgRcvrarg(int rcvrReg, int argReg); +static sqInt genExtendedSendBytecode(void); +static sqInt genExtendedSuperBytecode(void); +static sqInt genExtJumpIfFalse(void); +static sqInt genExtJumpIfTrue(void); +static sqInt genExtNopBytecode(void); +static sqInt genExtPushCharacterBytecode(void); +static sqInt genExtPushIntegerBytecode(void); +static sqInt genExtPushLiteralBytecode(void); +static sqInt genExtPushLiteralVariableBytecode(void); +static sqInt genExtPushPseudoVariable(void); +static sqInt genExtPushReceiverVariableBytecode(void); +static sqInt genExtSendBytecode(void); +static sqInt genExtSendSuperBytecode(void); +static sqInt genExtStoreAndPopLiteralVariableBytecode(void); +static sqInt genExtStoreAndPopReceiverVariableBytecode(void); +static sqInt genExtStoreLiteralVariableBytecode(void); +static sqInt genExtStoreReceiverVariableBytecode(void); +static sqInt genExtUnconditionalJump(void); +static sqInt genFastPrimFail(void); +static NoDbgRegParms void genFastPrimTraceUsingandsetNewMethod(sqInt r1, sqInt r2, sqInt setNewMethod); +static void genLoadNewMethod(void); +static sqInt genLongJumpIfFalse(void); +static sqInt genLongJumpIfTrue(void); +static sqInt genLongPushTemporaryVariableBytecode(void); +static sqInt genLongStoreAndPopTemporaryVariableBytecode(void); +static sqInt genLongStoreTemporaryVariableBytecode(void); +static sqInt genLongUnconditionalBackwardJump(void); +static sqInt genLongUnconditionalForwardJump(void); +static NoDbgRegParms sqInt genLookupForPerformNumArgs(sqInt numArgs); +static NoDbgRegParms AbstractInstruction * genMoveConstantR(sqInt constant, sqInt reg); +static NoDbgRegParms usqInt genMustBeBooleanTrampolineForcalled(sqInt boolean, char *trampolineName); +static sqInt genPrimitiveHashMultiply(void); +static NoDbgRegParms void genPrimReturnEnterCogCodeEnilopmart(sqInt profiling); +static sqInt genPushConstantFalseBytecode(void); +static sqInt genPushConstantNilBytecode(void); +static sqInt genPushConstantOneBytecode(void); +static sqInt genPushConstantTrueBytecode(void); +static sqInt genPushConstantZeroBytecode(void); +static sqInt genPushLiteralConstantBytecode(void); +static sqInt genPushLiteralVariable16CasesBytecode(void); +static sqInt genPushLiteralVariableBytecode(void); +static sqInt genPushQuickIntegerConstantBytecode(void); +static sqInt genPushReceiverVariableBytecode(void); +static sqInt genPushTemporaryVariableBytecode(void); +extern sqInt genQuickReturnConst(void); +extern sqInt genQuickReturnInstVar(void); +extern sqInt genQuickReturnSelf(void); +static sqInt genReturnFalse(void); +static sqInt genReturnNil(void); +static sqInt genReturnNilFromBlock(void); +static sqInt genReturnTrue(void); +static sqInt genSecondExtendedSendBytecode(void); +static sqInt genSendLiteralSelector0ArgsBytecode(void); +static sqInt genSendLiteralSelector1ArgBytecode(void); +static sqInt genSendLiteralSelector2ArgsBytecode(void); +static sqInt genShortJumpIfFalse(void); +static sqInt genShortJumpIfTrue(void); +static sqInt genShortUnconditionalJump(void); +static sqInt genSpecialSelectorEqualsEquals(void); +static sqInt genSpecialSelectorNotEqualsEquals(void); +static sqInt genSpecialSelectorSend(void); +static sqInt genStoreAndPopReceiverVariableBytecode(void); +static sqInt genStoreAndPopRemoteTempLongBytecode(void); +static sqInt genStoreAndPopTemporaryVariableBytecode(void); +static sqInt genStoreRemoteTempLongBytecode(void); +static NoDbgRegParms void genTakeProfileSample(AbstractInstruction *continueLabel); +static sqInt genUnconditionalTrapBytecode(void); +#if VMInvestigations +extern char * isaName(void); +#endif /* VMInvestigations */ +extern sqInt mapPCDataForinto(CogMethod *cogMethod, sqInt arrayObj); +static sqInt numSpecialSelectors(void); +static NoDbgRegParms usqInt pcDataForBlockEntryMethod(sqInt blockEntryMcpc, sqInt cogMethod); +static NoDbgRegParms sqInt pcDataForAnnotationMcpcBcpcMethod(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *cogMethodArg); +static PrimitiveDescriptor * primitiveGeneratorOrNil(void); +static NoDbgRegParms int registerisInMask(sqInt reg, sqInt mask); +static NoDbgRegParms int registerisNotInMask(sqInt reg, sqInt mask); +static NoDbgRegParms BlockStart * addBlockStartAtnumArgsnumCopiedspan(sqInt bytecodepc, sqInt numArgs, sqInt numCopied, sqInt span); +static NoDbgRegParms void adjustArgumentsForPerform(sqInt numArgs); +static NoDbgRegParms sqInt allocateRegForStackEntryAtnotConflictingWith(sqInt index, sqInt regMask); +static NoDbgRegParms sqInt allocateRegNotConflictingWith(sqInt regMask); +static NoDbgRegParms sqInt anyReferencesToRegisterinTopNItems(sqInt reg, sqInt n); +extern void callCogCodePopReceiverArg0Regs(void); +extern void callCogCodePopReceiverArg1Arg0Regs(void); +static sqInt compileBlockBodies(void); +static NoDbgRegParms void compileBlockFrameBuild(BlockStart *blockStart); +static NoDbgRegParms void compileBlockFramelessEntry(BlockStart *blockStart); +static NoDbgRegParms sqInt compileBytecodesFromthrough(sqInt start, sqInt end); +static NoDbgRegParms CogMethod * compileCogFullBlockMethod(sqInt numCopied); +static NoDbgRegParms CogMethod * compileCogMethod(sqInt selector); +static sqInt compileEntireMethod(void); +static void compileFrameBuild(void); +static NoDbgRegParms void compileFullBlockFramelessEntry(sqInt numCopied); +static NoDbgRegParms void compileFullBlockMethodFrameBuild(sqInt numCopied); +#if IMMUTABILITY +static void compileTwoPathFrameBuild(void); +#endif /* IMMUTABILITY */ +static void compileTwoPathFramelessInit(void); +static NoDbgRegParms sqInt cPICMissTrampolineFor(sqInt numArgs); +static sqInt doubleExtendedDoAnythingBytecode(void); +static sqInt duplicateTopBytecode(void); +static NoDbgRegParms BytecodeFixup * ensureFixupAt(sqInt targetPC); +static NoDbgRegParms BytecodeFixup * ensureNonMergeFixupAt(sqInt targetPC); +static void ensureReceiverResultRegContainsSelf(void); +static NoDbgRegParms void evaluateat(BytecodeDescriptor *descriptor, sqInt pc); +static NoDbgRegParms sqInt eventualTargetOf(sqInt targetBytecodePC); +static NoDbgRegParms sqInt freeAnyRegNotConflictingWith(sqInt regMask); +static sqInt genBlockReturn(void); +static NoDbgRegParms void (*genCallPICEnilopmartNumArgs(sqInt numArgs))(void); +static sqInt genCallPrimitiveBytecode(void); +static sqInt genExternalizePointersForPrimitiveCall(void); +static AbstractInstruction * genExternalizeStackPointerForFastPrimitiveCall(void); +static sqInt genExtPushClosureBytecode(void); +static sqInt genExtPushFullClosureBytecode(void); +static void generateEnilopmarts(void); +static NoDbgRegParms sqInt generateInstructionsAt(sqInt eventualAbsoluteAddress); +static void generateMissAbortTrampolines(void); +static void generateSendTrampolines(void); +static void generateTracingTrampolines(void); +static NoDbgRegParms sqInt genForwardersInlinedIdenticalOrNotIf(sqInt orNot); +static NoDbgRegParms sqInt genIdenticalNoBranchArgIsConstantrcvrIsConstantargRegrcvrRegorNotIf(sqInt argIsConstant, sqInt rcvrIsConstant, sqInt argReg, sqInt rcvrRegOrNone, sqInt orNot); +static NoDbgRegParms sqInt genInlinedIdenticalOrNotIf(sqInt orNot); +static NoDbgRegParms sqInt genJumpBackTo(sqInt targetBytecodePC); +static NoDbgRegParms sqInt genJumpIfto(sqInt boolean, sqInt targetBytecodePC); +static NoDbgRegParms sqInt genJumpTo(sqInt targetBytecodePC); +static NoDbgRegParms sqInt genMarshalledSendnumArgssendTable(sqInt selectorIndex, sqInt numArgs, sqInt *sendTable); +static NoDbgRegParms usqInt genMethodAbortTrampolineFor(sqInt numArgs); +static NoDbgRegParms usqInt genPICAbortTrampolineFor(sqInt numArgs); +static NoDbgRegParms usqInt genPICMissTrampolineFor(sqInt numArgs); +static sqInt genPopStackBytecode(void); +static sqInt genPrimitiveClosureValue(void); +static sqInt genPrimitiveFullClosureValue(void); +static sqInt genPrimitivePerform(void); +static sqInt genPrimitivePerformWithArguments(void); +static sqInt genPushActiveContextBytecode(void); +static sqInt genPushClosureCopyCopiedValuesBytecode(void); +static NoDbgRegParms sqInt genPushLiteralIndex(sqInt literalIndex); +static NoDbgRegParms sqInt genPushLiteralVariable(sqInt literalIndex); +static NoDbgRegParms sqInt genPushMaybeContextReceiverVariable(sqInt slotIndex); +static sqInt genPushNewArrayBytecode(void); +static sqInt genPushReceiverBytecode(void); +static NoDbgRegParms sqInt genPushReceiverVariable(sqInt index); +static void genPushRegisterArgs(void); +static sqInt genPushRemoteTempLongBytecode(void); +static NoDbgRegParms sqInt genPushTemporaryVariable(sqInt index); +static sqInt genReturnReceiver(void); +static sqInt genReturnTopFromBlock(void); +static sqInt genReturnTopFromMethod(void); +static NoDbgRegParms sqInt genSendDirectedSupernumArgs(sqInt selectorIndex, sqInt numArgs); +static NoDbgRegParms sqInt genSendSupernumArgs(sqInt selectorIndex, sqInt numArgs); +static NoDbgRegParms usqInt genSendTrampolineFornumArgscalledargargargarg(void *aRoutine, sqInt numArgs, char *aString, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3); +static NoDbgRegParms sqInt genSendnumArgs(sqInt selectorIndex, sqInt numArgs); +static sqInt genSpecialSelectorArithmetic(void); +static sqInt genSpecialSelectorClass(void); +static sqInt genSpecialSelectorComparison(void); +static sqInt genStaticallyResolvedSpecialSelectorComparison(void); +static NoDbgRegParms sqInt genStorePopLiteralVariableneedsStoreCheckneedsImmutabilityCheck(sqInt popBoolean, sqInt litVarIndex, sqInt needsStoreCheck, sqInt needsImmCheck); +static NoDbgRegParms sqInt genStorePopMaybeContextReceiverVariableneedsStoreCheckneedsImmutabilityCheck(sqInt popBoolean, sqInt slotIndex, sqInt needsStoreCheck, sqInt needsImmCheck); +static NoDbgRegParms sqInt genVanillaInlinedIdenticalOrNotIf(sqInt orNot); +static NoDbgRegParms void initSimStackForFramelessBlock(sqInt startpc); +static NoDbgRegParms sqInt isNonForwarderReceiver(sqInt reg); +static sqInt liveRegisters(void); +static NoDbgRegParms sqInt mapDeadDescriptorIfNeeded(BytecodeDescriptor *descriptor); +static NoDbgRegParms void marshallSendArguments(sqInt numArgs); +static sqInt maybeCompilingFirstPassOfBlockWithInitialPushNil(void); +static NoDbgRegParms sqInt mergeWithFixupIfRequired(BytecodeFixup *fixup); +static NoDbgRegParms sqInt methodAbortTrampolineFor(sqInt numArgs); +static NoDbgRegParms sqInt needsFrameIfMod16GENumArgs(sqInt stackDelta); +static NoDbgRegParms sqInt needsFrameIfStackGreaterThanOne(sqInt stackDelta); +static NoDbgRegParms sqInt numberOfSpillsInTopNItems(sqInt n); +static NoDbgRegParms sqInt picAbortTrampolineFor(sqInt numArgs); +static void preserveTopInClassRegIfIsReceiverResultReg(void); +static sqInt prevInstIsPCAnnotated(void); +static int receiverIsInReceiverResultReg(void); +static NoDbgRegParms void reinitializeFixupsFromthrough(sqInt start, sqInt end); +static NoDbgRegParms sqInt scanBlock(BlockStart *blockStart); +static sqInt scanMethod(void); +static NoDbgRegParms sqInt squeakV3orSistaV1PushNilSizenumInitialNils(sqInt aMethodObj, sqInt numInitialNils); +static NoDbgRegParms sqInt squeakV3orSistaV1NumPushNils(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +static NoDbgRegParms void ssAllocateRequiredRegMaskupThroughupThroughNative(sqInt requiredRegsMask, sqInt stackPtr, sqInt nativeStackPtr); +static NoDbgRegParms void ssFlushUpThroughReceiverVariable(sqInt slotIndex); +static NoDbgRegParms void ssFlushUpThroughTemporaryVariable(sqInt tempIndex); +static NoDbgRegParms void ssPop(sqInt n); +static NoDbgRegParms sqInt ssPushAnnotatedConstant(sqInt literal); +static NoDbgRegParms sqInt ssPushBaseoffset(sqInt reg, sqInt offset); +static NoDbgRegParms sqInt ssPushConstant(sqInt literal); +static NoDbgRegParms sqInt ssPushDesc(SimStackEntry simStackEntry); +static NoDbgRegParms sqInt ssPushRegister(sqInt reg); +static NoDbgRegParms void ssPush(sqInt n); +static SimStackEntry ssSelfDescriptor(void); +static NoDbgRegParms void ssStoreAndReplacePoptoReg(sqInt popBoolean, sqInt reg); +static NoDbgRegParms sqInt ssStorePoptoPreferredReg(sqInt popBoolean, sqInt preferredReg); +static NoDbgRegParms void ssStorePoptoReg(sqInt popBoolean, sqInt reg); +static SimStackEntry * ssTop(void); +static NoDbgRegParms SimStackEntry * ssValue(sqInt n); +static NoDbgRegParms sqInt stackEntryIsBoolean(SimStackEntry *simStackEntry); +static sqInt tempsValidAndVolatileEntriesSpilled(void); +static NoDbgRegParms sqInt tryCollapseTempVectorInitializationOfSize(sqInt slots); +static sqInt violatesEnsureSpilledSpillAssert(void); +static void voidReceiverResultRegContainsSelf(void); + + +/*** Variables ***/ +static AbstractInstruction aMethodLabel; +static AbstractInstruction * abstractOpcodes; +static AbstractInstruction * const backEnd = &aMethodLabel; +static usqInt baseAddress; +static sqInt blockCount; +static AbstractInstruction * blockEntryLabel; +static AbstractInstruction * blockEntryNoContextSwitch; +static BlockStart * blockStarts; +static sqInt breakMethod; +static sqInt byte0; +static sqInt byte1; +static sqInt byte2; +static sqInt byte3; +static sqInt bytecodePC; +static sqInt bytecodeSetOffset; +static sqInt ceCollectArgumentsTypeFrameful; +static sqInt ceCollectArgumentsTypeFramelessSpilled; +static sqInt ceCollectArgumentsTypeFramelessUnspilled1Args; +static sqInt ceCollectArgumentsTypeFramelessUnspilled2Args; +static sqInt ceCollectInstVarType; +static sqInt ceCollectRemoteTempVarType; +static sqInt ceCollectReturnType; +static sqInt ceCollectTempVarType; +static sqInt ceCollectTypesForPrimitiveSlotAtPut; +static sqInt ceCPICMissTrampoline; +static sqInt ceDereferenceSelectorIndex; +static sqInt ceFetchContextInstVarTrampoline; +static void (*ceFlushDCache)(usqIntptr_t from, usqIntptr_t to); +static void (*ceFlushICache)(usqIntptr_t from, usqIntptr_t to); +static sqInt ceInlineNewHashTrampoline; +static sqInt ceLargeActiveContextInBlockTrampoline; +static sqInt ceLargeActiveContextInFullBlockTrampoline; +static sqInt ceLargeActiveContextInMethodTrampoline; +static sqInt ceMethodAbortTrampoline; +static sqInt ceNonLocalReturnTrampoline; +static sqInt cePICAbortTrampoline; +static sqInt cePrimReturnEnterCogCode; +static sqInt cePrimReturnEnterCogCodeProfiling; +static sqInt ceReapAndResetErrorCodeTrampoline; +static sqInt ceScheduleScavengeTrampoline; +static sqInt ceSendMustBeBooleanAddFalseTrampoline; +static sqInt ceSendMustBeBooleanAddTrueTrampoline; +static sqInt ceSmallActiveContextInBlockTrampoline; +static sqInt ceSmallActiveContextInFullBlockTrampoline; +static sqInt ceSmallActiveContextInMethodTrampoline; +static sqInt ceStoreCheckContextReceiverTrampoline; +static sqInt ceStoreCheckTrampoline; +static sqInt ceStoreContextInstVarTrampoline; +static sqInt ceTraceBlockActivationTrampoline; +static sqInt ceTraceLinkedSendTrampoline; +static sqInt ceTraceStoreTrampoline; +static sqInt checkedEntryAlignment; +static sqInt closedPICSize; +static usqInt codeBase; +#if DUAL_MAPPED_CODE_ZONE +static sqInt codeToDataDelta; +#else +# define codeToDataDelta 0 +#endif +static sqInt cogConstituentIndex; +static sqInt compactionInProgress; +static sqInt compilationPass; +static sqInt compilationTrace; +static sqInt cPICCaseSize; +static sqInt cPICEndOfCodeOffset; +static sqInt cPICEndSize; +static CogMethod * cPICPrototype; +static sqInt currentCallCleanUpSize; +static sqInt directedSuperBindingSendTrampolines[NumSendTrampolines]; +static sqInt directedSuperSendTrampolines[NumSendTrampolines]; +static AbstractInstruction * endCPICCase0; +static sqInt endPC; +static AbstractInstruction * entry; +static sqInt entryPointMask; +static CogMethod * enumeratingCogMethod; +static sqInt expectedFPAlignment; +static sqInt expectedSPAlignment; +static sqInt extA; +static sqInt extB; +static sqInt firstCPICCaseOffset; +static sqInt firstOpcodeIndex; +static sqInt firstSend; +static BytecodeFixup * fixups; +static AbstractInstruction * fullBlockEntry; +static AbstractInstruction * fullBlockNoContextSwitchEntry; +static BytecodeDescriptor generatorTable[512] = { + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushReceiverBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantTrueBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantFalseBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantNilBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushQuickIntegerConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushQuickIntegerConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushQuickIntegerConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushQuickIntegerConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genReturnReceiver, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnTrue, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnFalse, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnNil, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnTopFromMethod, 0, needsFrameIfInBlock, -1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnTopFromBlock, 0, needsFrameNever, -1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { extendedPushBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { extendedStoreBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 0, 0 }, + { extendedStoreAndPopBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 0, 0 }, + { genExtendedSendBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { doubleExtendedDoAnythingBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genExtendedSuperBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0 }, + { genSecondExtendedSendBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genPopStackBytecode, 0, needsFrameNever, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { duplicateTopBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushActiveContextBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushNewArrayBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genCallPrimitiveBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushRemoteTempLongBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreRemoteTempLongBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopRemoteTempLongBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushClosureCopyCopiedValuesBytecode, v3BlockCodeSize, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongUnconditionalBackwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongUnconditionalBackwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongUnconditionalBackwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongUnconditionalBackwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongUnconditionalForwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genLongUnconditionalForwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genLongUnconditionalForwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genLongUnconditionalForwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genLongJumpIfTrue, v3LongForwardBranchDistance, 0, 0, 0, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongJumpIfTrue, v3LongForwardBranchDistance, 0, 0, 0, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongJumpIfTrue, v3LongForwardBranchDistance, 0, 0, 0, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongJumpIfTrue, v3LongForwardBranchDistance, 0, 0, 0, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongJumpIfFalse, v3LongForwardBranchDistance, 0, 0, 0, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongJumpIfFalse, v3LongForwardBranchDistance, 0, 0, 0, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongJumpIfFalse, v3LongForwardBranchDistance, 0, 0, 0, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongJumpIfFalse, v3LongForwardBranchDistance, 0, 0, 0, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, AddRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, SubRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpLess, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpGreater, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpLessOrEqual, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpGreaterOrEqual, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpZero, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpNonZero, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, AndRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, OrRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorEqualsEquals, 0, needsFrameNever, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genSpecialSelectorClass, 0, needsFrameIfStackGreaterThanOne, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genSpecialSelectorNotEqualsEquals, 0, needsFrameNever, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushReceiverBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantTrueBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantFalseBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantNilBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantZeroBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantOneBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genExtPushPseudoVariable, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { duplicateTopBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { genReturnReceiver, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnTrue, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnFalse, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnNil, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnTopFromMethod, 0, needsFrameIfInBlock, -1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnNilFromBlock, 0, needsFrameNever, -1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 }, + { genReturnTopFromBlock, 0, needsFrameNever, -1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 }, + { genExtNopBytecode, 0, needsFrameNever, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, AddRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, SubRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpLess, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpGreater, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpLessOrEqual, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpGreaterOrEqual, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpZero, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpNonZero, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, AndRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, OrRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorEqualsEquals, 0, needsFrameNever, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genSpecialSelectorClass, 0, needsFrameIfStackGreaterThanOne, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genSpecialSelectorNotEqualsEquals, 0, needsFrameNever, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPopStackBytecode, 0, needsFrameNever, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genUnconditionalTrapBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { extABytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { extBBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { genExtPushReceiverVariableBytecode, 0, needsFrameIfReadMediated, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genExtPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genExtPushLiteralBytecode, 0, needsFrameNever, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genLongPushTemporaryVariableBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { genPushNewArrayBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genExtPushIntegerBytecode, 0, needsFrameNever, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genExtPushCharacterBytecode, 0, needsFrameNever, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genExtSendBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genExtSendSuperBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genCallMappedInlinedPrimitive, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }, + { genExtUnconditionalJump, v4LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genExtJumpIfTrue, v4LongBranchDistance, 0, 0, 0, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genExtJumpIfFalse, v4LongBranchDistance, 0, 0, 0, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genExtStoreAndPopReceiverVariableBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 0, 0 }, + { genExtStoreAndPopLiteralVariableBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 0, 0, 0 }, + { genLongStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genExtStoreReceiverVariableBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 0, 0 }, + { genExtStoreLiteralVariableBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 0, 0, 0 }, + { genLongStoreTemporaryVariableBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { genCallPrimitiveBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, + { genExtPushFullClosureBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genExtPushClosureBytecode, v4BlockCodeSize, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + { genPushRemoteTempLongBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreRemoteTempLongBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopRemoteTempLongBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 } +}; +static sqInt guardPageSize; +static sqInt initialPC; +static sqInt introspectionData; +static sqInt introspectionDataIndex; +static sqInt lastDumpedLiteralIndex; +static sqInt lastSend; +static usqInt limitAddress; +static AbstractInstruction * literals; +static sqInt literalsSize; +static sqInt maxLitIndex; +static sqInt methodAbortTrampolines[4]; +static sqInt methodBytesFreedSinceLastCompaction; +static sqInt methodCount; +static sqInt methodHeader; +static AbstractInstruction * const methodLabel = &aMethodLabel; +static sqInt methodObj; +static sqInt methodOrBlockNumArgs; +static sqInt methodOrBlockNumTemps; +static usqIntptr_t minValidCallAddress; +static usqInt mzFreeStart; +static sqInt nextLiteralIndex; +static sqInt nInstructionsLimit; +static AbstractInstruction * noCheckEntry; +static sqInt numAbstractOpcodes; +static sqInt numExtB; +static usqInt objectReferencesInRuntime[NumObjRefsInRuntime+1]; +static sqInt opcodeIndex; +static CogMethod *openPICList = 0; +static sqInt openPICSize; +static sqInt ordinarySendTrampolines[NumSendTrampolines]; +static sqInt picAbortTrampolines[4]; +static AbstractInstruction * picInterpretAbort; +static sqInt picMissTrampolines[4]; +static AbstractInstruction * picMNUAbort; +static BytecodeDescriptor * prevBCDescriptor; +static PrimitiveDescriptor primitiveGeneratorTable[MaxCompiledPrimitiveIndex+1] = { + { 0, -1 }, + { genPrimitiveAdd, 1 }, + { genPrimitiveSubtract, 1 }, + { genPrimitiveLessThan, 1 }, + { genPrimitiveGreaterThan, 1 }, + { genPrimitiveLessOrEqual, 1 }, + { genPrimitiveGreaterOrEqual, 1 }, + { genPrimitiveEqual, 1 }, + { genPrimitiveNotEqual, 1 }, + { genPrimitiveMultiply, 1 }, + { genPrimitiveDivide, 1 }, + { genPrimitiveMod, 1 }, + { genPrimitiveDiv, 1 }, + { genPrimitiveQuo, 1 }, + { genPrimitiveBitAnd, 1 }, + { genPrimitiveBitOr, 1 }, + { genPrimitiveBitXor, 1 }, + { genPrimitiveBitShift, 1 }, + { genPrimitiveMakePoint, 1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveAsFloat, 0 }, + { genPrimitiveFloatAdd, 1 }, + { genPrimitiveFloatSubtract, 1 }, + { genPrimitiveFloatLessThan, 1 }, + { genPrimitiveFloatGreaterThan, 1 }, + { genPrimitiveFloatLessOrEqual, 1 }, + { genPrimitiveFloatGreaterOrEqual, 1 }, + { genPrimitiveFloatEqual, 1 }, + { genPrimitiveFloatNotEqual, 1 }, + { genPrimitiveFloatMultiply, 1 }, + { genPrimitiveFloatDivide, 1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveFloatSquareRoot, 0 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveAt, 1 }, + { genPrimitiveAtPut, 2 }, + { genPrimitiveSize, 0 }, + { genPrimitiveStringAt, 1 }, + { genPrimitiveStringAtPut, 2 }, + { genFastPrimFail, -1 }, + { genFastPrimFail, -1 }, + { genFastPrimFail, -1 }, + { genPrimitiveObjectAt, 1 }, + { 0, -1 }, + { genPrimitiveNew, 0 }, + { genPrimitiveNewWithArg, 1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveIdentityHash, 0 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveNewMethod, 2 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitivePerform, -1 }, + { genPrimitivePerformWithArguments, 2 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveStringReplace, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveIdentical, 1 }, + { genPrimitiveClass, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveShallowCopy, 0 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveStringCompareWith, 1 }, + { genPrimitiveHashMultiply, 0 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveIntegerAt, 1 }, + { genPrimitiveIntegerAtPut, 2 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveNotIdentical, 1 }, + { genPrimitiveAsCharacter, -1 }, + { genPrimitiveImmediateAsInteger, 0 }, + { 0, -1 }, + { genPrimitiveSlotAt, 1 }, + { genPrimitiveSlotAtPut, 2 }, + { genPrimitiveIdentityHash, 0 }, + { genPrimitiveMaxIdentityHash, 0 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genFastPrimFail, -1 }, + { genFastPrimFail, -1 }, + { 0, -1 }, + { genPrimitiveClosureValue, 0 }, + { genPrimitiveClosureValue, 1 }, + { genPrimitiveClosureValue, 2 }, + { genPrimitiveClosureValue, 3 }, + { genPrimitiveClosureValue, 4 }, + { 0, -1 }, + { genPrimitiveFullClosureValue, -1 }, + { 0, -1 }, + { genPrimitiveFullClosureValue, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveClosureValue, 0 }, + { genPrimitiveClosureValue, 1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveSmallFloatAdd, 1 }, + { genPrimitiveSmallFloatSubtract, 1 }, + { genPrimitiveSmallFloatLessThan, 1 }, + { genPrimitiveSmallFloatGreaterThan, 1 }, + { genPrimitiveSmallFloatLessOrEqual, 1 }, + { genPrimitiveSmallFloatGreaterOrEqual, 1 }, + { genPrimitiveSmallFloatEqual, 1 }, + { genPrimitiveSmallFloatNotEqual, 1 }, + { genPrimitiveSmallFloatMultiply, 1 }, + { genPrimitiveSmallFloatDivide, 1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveSmallFloatSquareRoot, 0 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveHighBit, 0 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveUninitializedNewWithArg, 1 } +}; +static sqInt primitiveIndex; +static sqInt receiverTags; +static sqInt runtimeObjectRefIndex; +static AbstractInstruction * sendMiss; +static sqInt simNativeStackPtr; +static sqInt simSpillBase; +static SimStackEntry simStack[70]; +static sqInt simStackPtr; +static AbstractInstruction * stackCheckLabel; +static AbstractInstruction * stackOverflowCall; +static sqInt superSendTrampolines[NumSendTrampolines]; +static sqInt tempOop; +static char *trampolineAddresses[NumTrampolines*2]; +static sqInt trampolineTableIndex; +static sqInt uncheckedEntryAlignment; +static usqInt unpairedMethodList; +static sqInt varBaseAddress; +static usqInt youngReferrers; +static int labelCounter; +static unsigned char codeModified; +static unsigned char dataCacheFlushRequired; +static unsigned char dataCacheLineLength; +static unsigned char deadCode; +static unsigned char directedSendUsesBinding; +static unsigned char hasAtomicInstructions; +static unsigned char hasMovableLiteral; +static unsigned char hasYoungReferent; +static unsigned char inBlock; +static unsigned char instructionCacheFlushRequired; +static unsigned char instructionCacheLineLength; +static unsigned char needsFrame; +static unsigned char regArgsHaveBeenPushed; +static unsigned char useTwoPaths; +#if IMMUTABILITY +static sqInt ceStoreTrampolines[5];; +#endif +#if WIN64 +static HANDLE Win32ProcessHandle; +#endif +sqInt blockNoContextSwitchOffset; +sqInt cbEntryOffset; +sqInt cbNoSwitchEntryOffset; +sqInt ceBaseFrameReturnTrampoline; +void (*ceCall0ArgsPIC)(void); +void (*ceCall1ArgsPIC)(void); +void (*ceCall2ArgsPIC)(void); +void (*ceCallCogCodePopReceiverAndClassRegs)(void); +void (*ceCallCogCodePopReceiverArg0Regs)(void); +void (*ceCallCogCodePopReceiverArg1Arg0Regs)(void); +void (*ceCallCogCodePopReceiverReg)(void); +sqInt ceCannotResumeTrampoline; +void (*ceCaptureCStackPointers)(void); +sqInt ceCheckForInterruptTrampoline; +void (*ceEnterCogCodePopReceiverReg)(void); +usqIntptr_t (*ceGetFP)(void); +usqIntptr_t (*ceGetSP)(void); +void (*ceInvokeInterpret)(void); +sqInt ceReturnToInterpreterTrampoline; +#if !defined(cFramePointerInUse) +sqInt cFramePointerInUse; +#endif +sqInt cmEntryOffset; +sqInt cmNoCheckEntryOffset; +usqInt methodZoneBase; +sqInt missOffset; +void (*realCECallCogCodePopReceiverAndClassRegs)(void); +void (*realCECallCogCodePopReceiverArg0Regs)(void); +void (*realCECallCogCodePopReceiverArg1Arg0Regs)(void); +void (*realCECallCogCodePopReceiverReg)(void); +void (*realCEEnterCogCodePopReceiverReg)(void); +const char * traceFlagsMeanings[] = { + "1: print trace", "2: trace sends", "4: trace block activations", "8: trace interpreter primitives", + "16: trace VM events (context switches, GCs, etc)", "32: trace stack overflow (poll for events hook)", + "64: trace linked sends", "128: trace fast C call interpreter primitives", "256: trace FFI/thread events", + null + }; +sqInt traceStores; +int traceFlags = 8 /* prim trace log on by default */; + + +/*** Macros ***/ +#define dataCacheFlushRequired(ign) dataCacheFlushRequired +#define dataCacheLineLength(ign) dataCacheLineLength +#define flushDCacheFromto(me,startAddress,endAddress) ceFlushDCache(startAddress,endAddress) +#define inlineCacheValueForSelectorin(backEnd,selector,aCogMethod) indexForSelectorin(selector,aCogMethod) +#define instructionCacheFlushRequired(ign) instructionCacheFlushRequired +#define instructionCacheLineLength(ign) instructionCacheLineLength +#if !defined(needsCodeZoneExecuteWriteSwitch) +#define needsCodeZoneExecuteWriteSwitch(ign) 0 +#endif /* !defined(needsCodeZoneExecuteWriteSwitch) */ +#define roundUpToMethodAlignment(ignored,numBytes) (((numBytes) + 15) & -16) +#define setDataCacheFlushRequired(ign,b) dataCacheFlushRequired = b +#define setDataCacheLineLength(ign,n) dataCacheLineLength = n +#define setInstructionCacheFlushRequired(ign,b) instructionCacheFlushRequired = b +#define setInstructionCacheLineLength(ign,n) instructionCacheLineLength = n +#define cPICNumCases stackCheckOffset +#define cPICNumCasesHack hack hack hack i.e. the getter macro does all the work +#define abstractInstructionAt(index) (&abstractOpcodes[index]) +#define addressIsInInstructions(address) (!((usqInt)(address) & (BytesPerWord-1)) \ + && (address) >= &abstractOpcodes[0] \ + && (address) < &abstractOpcodes[opcodeIndex]) +#define allocateBlockStarts(numBlocks) do { \ + blockStarts = (numBlocks) ? alloca(sizeof(BlockStart) * (numBlocks)) : 0; \ +} while (0) +#define assertValidDualZone() true +#define assertValidDualZoneReadAddress(address) 0 +#define assertValidDualZoneWriteAddress(address) 0 +#define backEnd() backEnd +#define blockAlignment() 8 +#define blockStartAt(index) (&blockStarts[index]) +#define bump64BitPerfCounterByUsecs(usecs) 0 // simulation only +#define ceBaseFrameReturnPC() ceBaseFrameReturnTrampoline +#define ceCannotResumePC() ((usqInt)ceCannotResumeTrampoline) +#define ceCheckForInterruptTrampoline() ceCheckForInterruptTrampoline +#define ceReturnToInterpreterPC() ((usqInt)ceReturnToInterpreterTrampoline) +#define codeByteAtput(address,value) byteAtput((char *)(address) + codeToDataDelta, value) +#define codeLong32Atput(address,value) long32Atput((char *)(address) + codeToDataDelta, value) +#define codeLong64Atput(address,value) long64Atput((char *)(address) + codeToDataDelta, value) +#define codeLongAtput(address,value) longAtput((char *)(address) + codeToDataDelta, value) +#define codeMemcpy(dest,src,bytes) memcpy(dest,src,bytes) +#define codeMemmove(dest,src,bytes) memmove((char *)(dest)+codeToDataDelta,src,bytes) +#define cr() putchar('\n') +#if !defined(ensureExecutableCodeZoneAt) +#define ensureExecutableCodeZoneAt(underscoreLine) 0 +#endif /* !defined(ensureExecutableCodeZoneAt) */ +#if !defined(ensureWritableCodeZoneAt) +#define ensureWritableCodeZoneAt(underscoreLine) 0 +#endif /* !defined(ensureWritableCodeZoneAt) */ +#define entryOffset() cmEntryOffset +#define generatorAt(index) (&generatorTable[index]) +#define getCodeToDataDelta() codeToDataDelta +#define getIsObjectReference() 2 +#define halt() warning("halt") +#define haltmsg(msg) warning("halt: " msg) +#define interpretOffset() missOffset +#define mapPerMethodProfile() 0 +#define maxCogCodeSize() (16*1024*1024) +#define maybeBreakGeneratingFromto(address,end) 0 +#define maybeBreakGeneratingInstructionWithIndex(i) 0 +#define maybeHaltIfDebugPC() 0 +#define methodLabel() methodLabel +#define methodZoneBase() methodZoneBase +#define minCogMethodAddress() methodZoneBase +#define moveProfileToMethods() 0 +#define noCheckEntryOffset() cmNoCheckEntryOffset +#define noContextSwitchBlockEntryOffset() blockNoContextSwitchOffset +#define notYetImplemented() warning("not yet implemented") +#define null 0 +#define printNum(n) printf("%" PRIdSQINT, (sqInt) (n)) +#define printOnTrace() (traceFlags & 1) +#define recordBlockTrace() (traceFlags & 4) +#define recordEventTrace() (traceFlags & 16) +#define recordFastCCallPrimTrace() (traceFlags & 128) +#define recordFFIOrThreadTrace() (traceFlags & 256) +#define recordOverflowTrace() (traceFlags & 32) +#define recordPrimTrace() (traceFlags & 8) +#define recordSendTrace() (traceFlags & 2) +#define reportError(n) warning("compilation error") +#define setHasMovableLiteral(b) (hasMovableLiteral = (b)) +#define setHasYoungReferent(b) (hasYoungReferent = (b)) +#define varBaseAddress() varBaseAddress +#define nextOpenPIC methodObject +#define nextOpenPICHack hack hack hack i.e. the getter macro does all the work +#define freeStart() mzFreeStart +#define limitZony() ((CogMethod *)mzFreeStart) +#define methodBytesFreedSinceLastCompaction() methodBytesFreedSinceLastCompaction +#define youngReferrers() youngReferrers +#define numRegArgs() 2 +#define maybeConstant(sse) ((sse)->constant) +#define literalInstructionAt(index) (&literals[index]) +#define fullBlockEntryOffset() cbEntryOffset +#define fullBlockNoContextSwitchEntryOffset() cbNoSwitchEntryOffset +#define needsFrame() needsFrame +#define fixupAtIndex(index) (&fixups[index]) +#define simNativeStackAt(index) (simNativeStack + (index)) +#define simSelf() simStack +#define simStackAt(index) (simStack + (index)) +#define traceDescriptor(ign) 0 +#define traceFixupmerge(igu,ana) 0 +#define traceMerge(ign) 0 +#define traceSimStack() 0 +#define traceSpill(ign) 0 +#define allocatype(numElements, elementType) alloca((numElements)*sizeof(elementType)) +#define numElementsIn(anArray) (sizeof(anArray)/sizeof(anArray[0])) +#define oopisGreaterThanOrEqualTo(anOop,otherOop) ((usqInt)(anOop) >= (usqInt)(otherOop)) +#define oopisGreaterThanOrEqualToandLessThanOrEqualTo(anOop,baseOop,limitOop) ((usqInt)(anOop) >= (usqInt)(baseOop) && (usqInt)(anOop) <= (usqInt)(limitOop)) +#define oopisGreaterThanOrEqualToandLessThan(anOop,baseOop,limitOop) ((usqInt)(anOop) >= (usqInt)(baseOop) && (usqInt)(anOop) < (usqInt)(limitOop)) +#define oopisGreaterThan(anOop,otherOop) ((usqInt)(anOop) > (usqInt)(otherOop)) +#define oopisGreaterThanandLessThan(anOop,baseOop,limitOop) ((usqInt)(anOop) > (usqInt)(baseOop) && (usqInt)(anOop) < (usqInt)(limitOop)) +#define oopisLessThanOrEqualTo(anOop,otherOop) ((usqInt)(anOop) <= (usqInt)(otherOop)) +#define oopisLessThan(anOop,otherOop) ((usqInt)(anOop) < (usqInt)(otherOop)) + + +/*** Methods ***/ + + /* CogAbstractInstruction>>#addDependent: */ +static NoDbgRegParms AbstractInstruction * +addDependent(AbstractInstruction *self_in_CogAbstractInstruction, AbstractInstruction *anInstruction) +{ + if ((self_in_CogAbstractInstruction->dependent)) { + (anInstruction->dependent = (self_in_CogAbstractInstruction->dependent)); + } + return ((self_in_CogAbstractInstruction->dependent) = anInstruction); +} + + +/* Answer an unused abstract register in the liveRegMask. + Subclasses with more registers can override to answer them. */ + + /* CogAbstractInstruction>>#availableFloatRegisterOrNoneFor: */ +static NoDbgRegParms sqInt +availableFloatRegisterOrNoneFor(AbstractInstruction *self_in_CogAbstractInstruction, sqInt liveRegsMask) +{ + if (!(((liveRegsMask & ((1U << DPFPReg0))) != 0))) { + return DPFPReg0; + } + if (!(((liveRegsMask & ((1U << DPFPReg1))) != 0))) { + return DPFPReg1; + } + if (!(((liveRegsMask & ((1U << DPFPReg2))) != 0))) { + return DPFPReg2; + } + if (!(((liveRegsMask & ((1U << DPFPReg3))) != 0))) { + return DPFPReg3; + } + if (!(((liveRegsMask & ((1U << DPFPReg4))) != 0))) { + return DPFPReg4; + } + if (!(((liveRegsMask & ((1U << DPFPReg5))) != 0))) { + return DPFPReg5; + } + if (!(((liveRegsMask & ((1U << DPFPReg6))) != 0))) { + return DPFPReg6; + } + if (!(((liveRegsMask & ((1U << DPFPReg7))) != 0))) { + return DPFPReg7; + } + return NoReg; +} + + +/* Answer an unused abstract register in the liveRegMask. + Subclasses with more registers can override to answer them. + N.B. Do /not/ allocate TempReg. */ + + /* CogAbstractInstruction>>#availableRegisterOrNoneFor: */ +static NoDbgRegParms sqInt +availableRegisterOrNoneFor(AbstractInstruction *self_in_CogAbstractInstruction, sqInt liveRegsMask) +{ + if (!(((liveRegsMask & ((1U << Arg1Reg))) != 0))) { + return Arg1Reg; + } + if (!(((liveRegsMask & ((1U << Arg0Reg))) != 0))) { + return Arg0Reg; + } + if (!(((liveRegsMask & ((1U << SendNumArgsReg))) != 0))) { + return SendNumArgsReg; + } + if (!(((liveRegsMask & ((1U << ClassReg))) != 0))) { + return ClassReg; + } + if (!(((liveRegsMask & ((1U << ReceiverResultReg))) != 0))) { + return ReceiverResultReg; + } + return NoReg; +} + + +/* For out-of-line literal support, clone a literal from a literal. */ + + /* CogAbstractInstruction>>#cloneLiteralFrom: */ +static NoDbgRegParms AbstractInstruction * +cloneLiteralFrom(AbstractInstruction *self_in_CogAbstractInstruction, AbstractInstruction *existingLiteral) +{ + assert((((existingLiteral->opcode)) == Literal) + && ((!((self_in_CogAbstractInstruction->dependent))) + && (!((self_in_CogAbstractInstruction->address))))); + (self_in_CogAbstractInstruction->opcode) = Literal; + (self_in_CogAbstractInstruction->annotation) = (existingLiteral->annotation); + ((self_in_CogAbstractInstruction->operands))[0] = (((existingLiteral->operands))[0]); + ((self_in_CogAbstractInstruction->operands))[1] = (((existingLiteral->operands))[1]); + ((self_in_CogAbstractInstruction->operands))[2] = (((existingLiteral->operands))[2]); + return self_in_CogAbstractInstruction; +} + + +/* Generate concrete machine code for the instruction at actualAddress, + setting machineCodeSize, and answer the following address. */ + + /* CogAbstractInstruction>>#concretizeAt: */ +static NoDbgRegParms sqInt +concretizeAt(AbstractInstruction *self_in_CogAbstractInstruction, sqInt actualAddress) +{ + (self_in_CogAbstractInstruction->address) = actualAddress; + (self_in_CogAbstractInstruction->machineCodeSize) = dispatchConcretize(self_in_CogAbstractInstruction); + assert((((self_in_CogAbstractInstruction->maxSize)) == null) + || (((self_in_CogAbstractInstruction->maxSize)) >= ((self_in_CogAbstractInstruction->machineCodeSize)))); + return actualAddress + ((self_in_CogAbstractInstruction->machineCodeSize)); +} + + +/* Load the stack pointer register with that of the C stack, effecting + a switch to the C stack. Used when machine code calls into the + CoInterpreter run-time (e.g. to invoke interpreter primitives). */ + + /* CogAbstractInstruction>>#genLoadCStackPointer */ +static NoDbgRegParms sqInt +genLoadCStackPointer(AbstractInstruction *self_in_CogAbstractInstruction) +{ + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(cStackPointerAddress(), genoperandoperand(MoveAwR, cStackPointerAddress(), NativeSPReg)); + return 0; +} + + +/* Switch back to the Smalltalk stack where there may be a C return address + on top of stack below + the last primitive argument. Assign SPReg first because typically it is + used immediately afterwards. + */ + + /* CogAbstractInstruction>>#genLoadStackPointerForPrimCall: */ +static NoDbgRegParms sqInt +genLoadStackPointerForPrimCall(AbstractInstruction *self_in_CogAbstractInstruction, sqInt spareReg) +{ + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(stackPointerAddress(), genoperandoperand(MoveAwR, stackPointerAddress(), SPReg)); + return 0; +} + + +/* Switch back to the Smalltalk stack where there may be a C return address + on top of stack below + the last primitive argument. Assign SPReg first because typically it is + used immediately afterwards. + */ + + /* CogAbstractInstruction>>#genLoadStackPointersForPrimCall: */ +static NoDbgRegParms sqInt +genLoadStackPointersForPrimCall(AbstractInstruction *self_in_CogAbstractInstruction, sqInt spareReg) +{ + genLoadStackPointers(self_in_CogAbstractInstruction); + return 0; +} + + +/* Generic register swap code. Subclasses for processors that have a true + exchange operation will override to use it. */ + + /* CogAbstractInstruction>>#genSwapR:R:Scratch: */ +static NoDbgRegParms AbstractInstruction * +genSwapRRScratch(AbstractInstruction *self_in_CogAbstractInstruction, sqInt regA, sqInt regB, sqInt regTmp) +{ + AbstractInstruction *first; + + first = genoperandoperand(MoveRR, regA, regTmp); + + /* MoveR:R: */ + genoperandoperand(MoveRR, regB, regA); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, regB); + return first; +} + + /* CogAbstractInstruction>>#genWriteCResultIntoReg: */ +static NoDbgRegParms AbstractInstruction * +genWriteCResultIntoReg(AbstractInstruction *self_in_CogAbstractInstruction, sqInt abstractRegister) +{ + if ((abstractRegister != NoReg) + && (abstractRegister != ABIResultReg)) { + genoperandoperand(MoveRR, ABIResultReg, abstractRegister); + } + return self_in_CogAbstractInstruction; +} + + +/* At least x86, x86_64, AArch32 and AArch64 have a 64-bit performance + counter. Subclasses can turn this off if rquired. */ + + /* CogAbstractInstruction>>#has64BitPerformanceCounter */ +static NoDbgRegParms sqInt +has64BitPerformanceCounter(AbstractInstruction *self_in_CogAbstractInstruction) +{ + return 1; +} + + +/* For out-of-line literal support, initialize a sharable literal. */ + + /* CogAbstractInstruction>>#initializeSharableLiteral: */ +static NoDbgRegParms AbstractInstruction * +initializeSharableLiteral(AbstractInstruction *self_in_CogAbstractInstruction, sqInt literal) +{ + (self_in_CogAbstractInstruction->opcode) = Literal; + + /* separate := nil for Slang */ + (self_in_CogAbstractInstruction->annotation) = null; + (self_in_CogAbstractInstruction->address) = null; + (self_in_CogAbstractInstruction->dependent) = null; + ((self_in_CogAbstractInstruction->operands))[0] = literal; + ((self_in_CogAbstractInstruction->operands))[1] = (1 + ((((usqInt)(BytesPerOop) << 1)))); + ((self_in_CogAbstractInstruction->operands))[2] = -1; + return self_in_CogAbstractInstruction; +} + + +/* For out-of-line literal support, initialize an unsharable literal. */ + + /* CogAbstractInstruction>>#initializeUniqueLiteral: */ +static NoDbgRegParms AbstractInstruction * +initializeUniqueLiteral(AbstractInstruction *self_in_CogAbstractInstruction, sqInt literal) +{ + (self_in_CogAbstractInstruction->opcode) = Literal; + + /* separate := nil for Slang */ + (self_in_CogAbstractInstruction->annotation) = null; + (self_in_CogAbstractInstruction->address) = null; + (self_in_CogAbstractInstruction->dependent) = null; + ((self_in_CogAbstractInstruction->operands))[0] = literal; + ((self_in_CogAbstractInstruction->operands))[1] = (0 + ((((usqInt)(BytesPerOop) << 1)))); + ((self_in_CogAbstractInstruction->operands))[2] = -1; + return self_in_CogAbstractInstruction; +} + + +/* Set the target of a jump instruction. These all have the target in the + first operand. */ + + /* CogAbstractInstruction>>#jmpTarget: */ +static NoDbgRegParms AbstractInstruction * +jmpTarget(AbstractInstruction *self_in_CogAbstractInstruction, AbstractInstruction *anAbstractInstruction) +{ + /* check for inadvertent smashing of already-set jmpTargets; development only */ + ((self_in_CogAbstractInstruction->operands))[0] = (((usqInt)anAbstractInstruction)); + return anAbstractInstruction; +} + + +/* We assume here that calls and jumps look the same as regards their + displacement. This works on at least x86, ARM and x86_64. Processors on + which that isn't the + case can override as necessary. */ + + /* CogAbstractInstruction>>#relocateJumpLongBeforeFollowingAddress:by: */ +static NoDbgRegParms AbstractInstruction * +relocateJumpLongBeforeFollowingAddressby(AbstractInstruction *self_in_CogAbstractInstruction, sqInt pc, sqInt delta) +{ + relocateCallBeforeReturnPCby(self_in_CogAbstractInstruction, pc, delta); + return self_in_CogAbstractInstruction; +} + + +/* Relocate a long conditional jump before pc. Default to relocating a + non-conditional jump. + Processors that have different formats for conditional and unconditional + jumps override. */ + + /* CogAbstractInstruction>>#relocateJumpLongConditionalBeforeFollowingAddress:by: */ +static NoDbgRegParms AbstractInstruction * +relocateJumpLongConditionalBeforeFollowingAddressby(AbstractInstruction *self_in_CogAbstractInstruction, sqInt pc, sqInt delta) +{ + relocateJumpLongBeforeFollowingAddressby(self_in_CogAbstractInstruction, pc, delta); + return self_in_CogAbstractInstruction; +} + + /* CogAbstractInstruction>>#resolveJumpTarget */ +static NoDbgRegParms AbstractInstruction * +resolveJumpTarget(AbstractInstruction *self_in_CogAbstractInstruction) +{ + BytecodeFixup *fixup; + + assert(isJump(self_in_CogAbstractInstruction)); + fixup = ((BytecodeFixup *) (((self_in_CogAbstractInstruction->operands))[0])); + if (addressIsInFixups(fixup)) { + assert(addressIsInInstructions((fixup->targetInstruction))); + jmpTarget(self_in_CogAbstractInstruction, (fixup->targetInstruction)); + } + return self_in_CogAbstractInstruction; +} + + +/* Rewrite a conditional jump long to jump to target. This version defaults + to using + rewriteJumpLongAt:, which works for many ISAs. Subclasses override if + necessary. */ + + /* CogAbstractInstruction>>#rewriteConditionalJumpLongAt:target: */ +static NoDbgRegParms sqInt +rewriteConditionalJumpLongAttarget(AbstractInstruction *self_in_CogAbstractInstruction, sqInt callSiteReturnAddress, sqInt callTargetAddress) +{ + return rewriteImm26JumpBeforetarget(self_in_CogAbstractInstruction, callSiteReturnAddress, callTargetAddress); +} + + +/* C6.2.4 ADD (immediate) p761 */ + + /* CogARMv8Compiler>>#addrn:rd:imm:shiftBy12: */ +static NoDbgRegParms sqInt +addrnrdimmshiftBy12(AbstractInstruction *self_in_CogARMv8Compiler, sqInt rn, sqInt rd, sqInt offset, sqInt shiftBy12) +{ + assert(((offset >= 0) && (offset <= (0xFFF)))); + return (((0x91000000U + ((shiftBy12 + ? 0x400000 + : 0))) + ((((usqInt)(offset) << 10)))) + ((((usqInt)(rn) << 5)))) + rd; +} + + /* CogARMv8Compiler>>#brlink:reg: */ +static NoDbgRegParms sqInt +brlinkreg(AbstractInstruction *self_in_CogARMv8Compiler, sqInt link, sqInt reg) +{ + return ((0xD61F0000U) + ((link + ? 0x200000 + : 0))) + ((((usqInt)(reg) << 5))); +} + + /* CogARMv8Compiler>>#callFullInstructionByteSize */ +static NoDbgRegParms sqInt +callFullInstructionByteSize(AbstractInstruction *self_in_CogARMv8Compiler) +{ + return 8; +} + + +/* ARM calls and jumps span +/- 32 mb, more than enough for intra-zone calls + and jumps. + */ + + /* CogARMv8Compiler>>#callInstructionByteSize */ +static NoDbgRegParms sqInt +callInstructionByteSize(AbstractInstruction *self_in_CogARMv8Compiler) +{ + return 4; +} + + /* CogARMv8Compiler>>#callTargetFromReturnAddress: */ +static NoDbgRegParms sqInt +callTargetFromReturnAddress(AbstractInstruction *self_in_CogARMv8Compiler, sqInt mcpc) +{ + unsigned int instr; + + instr = long32At((void *)(mcpc - 4)); + + /* C6.2.26 B C6-799 + C6.2.33 BL C6-812 */ + assert(((((usqInt)(instr)) >> 26) == 37) + || ((((usqInt)(instr)) >> 26) == 5)); + return (((((sqLong) (((usqInt)((((usqInt)((instr & (0x3FFFFFF))) << 38)))))))) >> 36) + (mcpc - 4); +} + + /* CogARMv8Compiler>>#cmpC32RTempByteSize */ +static NoDbgRegParms sqInt +cmpC32RTempByteSize(AbstractInstruction *self_in_CogARMv8Compiler) +{ + return 8; +} + + /* CogARMv8Compiler>>#computeJumpTargetOffset */ +static NoDbgRegParms sqInt +computeJumpTargetOffset(AbstractInstruction *self_in_CogARMv8Compiler) +{ + AbstractInstruction *jumpTarget; + + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogARMv8Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + return (((sqLong) jumpTarget)) - (((sqLong) ((self_in_CogARMv8Compiler->address)))); +} + + +/* Answer the index of the low order one bit. + 2r00101000 lowBit (Answers: 4) + 2r-00101000 lowBit (Answers: 4) + This is an implementation of SmallInteger>>lowBit */ + + /* CogARMv8Compiler>>#computeLowBit: */ +static NoDbgRegParms sqInt +computeLowBit(AbstractInstruction *self_in_CogARMv8Compiler, sqInt nArg) +{ + sqInt n; + sqInt result; + + if (!nArg) { + return 0; + } + n = nArg; + result = 1; + while ((!(n & 0xFF))) { + result += 8; + n = (((usqInt)(n)) >> 8); + } + if ((!(n & 15))) { + result += 4; + n = (((usqInt)(n)) >> 4); + } + if ((!(n & 3))) { + result += 2; + n = (((usqInt)(n)) >> 2); + } + return (((n & 1) != 0) + ? result + : result + 1); +} + + +/* Because we don't use Thumb, each ARMv8 instruction has 4 bytes. Several + abstract opcodes need more than one instruction. Instructions that refer + to constants and/or literals depend on literals being stored out-of-line + or encoded + in immediate instruction fields (i.e. we only support + OutOfLineLiteralsManager. + N.B. The ^N forms are to get around the old bytecode compiler's long + branch limits which are exceeded when each case jumps around the + otherwise. */ + + /* CogARMv8Compiler>>#computeMaximumSize */ +static NoDbgRegParms sqInt +computeMaximumSize(AbstractInstruction *self_in_CogARMv8Compiler) +{ + sqInt constant; + sqInt imm; + sqInt immediate; + sqInt immrSqInt; + usqInt mask; + usqInt nImms; + usqInt nUsqInt; + sqInt numLeadingOnes; + sqInt numTrailingOnes; + sqInt rotateCount; + sqInt size; + + switch ((self_in_CogARMv8Compiler->opcode)) { + case Label: + return 0; + + case Literal: + /* begin literalSize */ + assert(((self_in_CogARMv8Compiler->opcode)) == Literal); + return (((self_in_CogARMv8Compiler->operands))[1] + ? (((usqInt)((((self_in_CogARMv8Compiler->operands))[1]))) >> 1) & 15 + : BytesPerOop); + + case AlignmentNops: + return (((self_in_CogARMv8Compiler->operands))[0]) - 4; + + case CallFull: + case JumpFull: + case JumpLongZero: + case JumpLongNonZero: + case JumpMulOverflow: + case JumpNoMulOverflow: + case JumpFPOrdered: + case JumpFPUnordered: + case AddCwR: + case AndCwR: + case CmpCwR: + case CmpC32R: + case OrCwR: + case SubCwR: + case XorCwR: + case PushCw: + case PushCq: + return 8; + + case RetN: + case NativeRetN: + return (((self_in_CogARMv8Compiler->operands))[0] + ? 8 + : 4); + + case AddCqR: + case AddCqRR: + case CmpCqR: + case SubCqR: + case LoadEffectiveAddressMwrR: + immediate = ((sqLong) (((self_in_CogARMv8Compiler->operands))[0])); + + /* begin isPossiblyShiftableNegatableImm12:ifTrue:ifFalse: */ + if (((immediate >= (-4096)) && (immediate <= (0xFFF)))) { + return 4; + } + if (((!(immediate & (0xFFF)))) + && (((((immediate) >> 12) >= (-4096)) && (((immediate) >> 12) <= (0xFFF))))) { + return 4; + } + return 8; + + case AndCqR: + case OrCqR: + case OrCqRR: + case TstCqR: + case XorCqR: + constant = ((self_in_CogARMv8Compiler->operands))[0]; + + /* begin isImmNImmSImmREncodableBitmask:ifTrue:ifFalse: */ + if (((constant >= -1) && (constant <= 0))) { + return 8; + } + imm = constant; + + /* First, determine the element size. */ + size = 32; + while (1) { + mask = (1ULL << size) - 1; + if (!(((imm & mask) != ((((usqInt)(imm)) >> size) & mask) + ? ((size = size * 2), + 0) + : size > 2))) break; + size = size / 2; + } + + /* Second, determine the rotation to make the element be: 0^m 1^n. */ + mask = ((usqInt)((0xFFFFFFFFFFFFFFFFULL))) >> (64 - size); + imm = imm & mask; + if (isShiftedMask(self_in_CogARMv8Compiler, imm)) { + rotateCount = countTrailingZeros(self_in_CogARMv8Compiler, imm); + numTrailingOnes = countTrailingOnes(self_in_CogARMv8Compiler, ((usqInt)(imm)) >> rotateCount); + } + else { + imm = imm | (~(usqIntptr_t)mask); + if (!(isShiftedMask(self_in_CogARMv8Compiler, imm))) { + return 8; + } + numLeadingOnes = countLeadingOnes(self_in_CogARMv8Compiler, imm); + rotateCount = 64 - numLeadingOnes; + numTrailingOnes = (numLeadingOnes + (countTrailingOnes(self_in_CogARMv8Compiler, imm))) - (64 - size); + } + + /* Encode in Immr the number of RORs it would take to get *from* 0^m 1^n + to our target value, where I is the number of RORs to go the opposite direction. */ + assert(size > rotateCount); + immrSqInt = (size - rotateCount) & (size - 1); + + /* If size has a 1 in the n'th bit, create a value that has zeroes in bits [0, n] and ones above that. */ + nImms = ((sqInt)((usqInt)((~(usqIntptr_t)(size - 1))) << 1)); + + /* Or the CTO value into the low bits, which must be below the Nth bit mentioned above. */ + nImms = nImms | (numTrailingOnes - 1); + + /* Extract the seventh bit and toggle it to create the N field. */ + nUsqInt = (((nImms) >> 6) & 1) ^ 1; + nImms = nImms & 0x3F; + assert((decodeNimmsimmr(self_in_CogARMv8Compiler, nUsqInt, nImms, immrSqInt)) == (((usqInt) constant))); + return 4; + + case AndCqRR: + constant = ((self_in_CogARMv8Compiler->operands))[0]; + + /* begin isImmNImmSImmREncodableBitmask:ifTrue:ifFalse: */ + if (((constant >= -1) && (constant <= 0))) { + return ((((self_in_CogARMv8Compiler->operands))[2]) == SP + ? 12 + : 8); + } + imm = constant; + + /* First, determine the element size. */ + size = 32; + while (1) { + mask = (1ULL << size) - 1; + if (!(((imm & mask) != ((((usqInt)(imm)) >> size) & mask) + ? ((size = size * 2), + 0) + : size > 2))) break; + size = size / 2; + } + + /* Second, determine the rotation to make the element be: 0^m 1^n. */ + mask = ((usqInt)((0xFFFFFFFFFFFFFFFFULL))) >> (64 - size); + imm = imm & mask; + if (isShiftedMask(self_in_CogARMv8Compiler, imm)) { + rotateCount = countTrailingZeros(self_in_CogARMv8Compiler, imm); + numTrailingOnes = countTrailingOnes(self_in_CogARMv8Compiler, ((usqInt)(imm)) >> rotateCount); + } + else { + imm = imm | (~(usqIntptr_t)mask); + if (!(isShiftedMask(self_in_CogARMv8Compiler, imm))) { + return ((((self_in_CogARMv8Compiler->operands))[2]) == SP + ? 12 + : 8); + } + numLeadingOnes = countLeadingOnes(self_in_CogARMv8Compiler, imm); + rotateCount = 64 - numLeadingOnes; + numTrailingOnes = (numLeadingOnes + (countTrailingOnes(self_in_CogARMv8Compiler, imm))) - (64 - size); + } + + /* Encode in Immr the number of RORs it would take to get *from* 0^m 1^n + to our target value, where I is the number of RORs to go the opposite direction. */ + assert(size > rotateCount); + immrSqInt = (size - rotateCount) & (size - 1); + + /* If size has a 1 in the n'th bit, create a value that has zeroes in bits [0, n] and ones above that. */ + nImms = ((sqInt)((usqInt)((~(usqIntptr_t)(size - 1))) << 1)); + + /* Or the CTO value into the low bits, which must be below the Nth bit mentioned above. */ + nImms = nImms | (numTrailingOnes - 1); + + /* Extract the seventh bit and toggle it to create the N field. */ + nUsqInt = (((nImms) >> 6) & 1) ^ 1; + nImms = nImms & 0x3F; + assert((decodeNimmsimmr(self_in_CogARMv8Compiler, nUsqInt, nImms, immrSqInt)) == (((usqInt) constant))); + return ((((self_in_CogARMv8Compiler->operands))[2]) == SP + ? 8 + : 4); + + case SubRR: + case SubRRR: + return ((((self_in_CogARMv8Compiler->operands))[0]) == SP + ? 8 + : 4); + + case MulOverflowRRR: + return 12; + + case MoveAwR: + return (((((((self_in_CogARMv8Compiler->operands))[0]) >= ((varBaseAddress) - (0x100))) && ((((self_in_CogARMv8Compiler->operands))[0]) <= (((varBaseAddress) + (0x1000)) - 1)))) + || (/* addressIsInCurrentCompilation: */ + ((((usqInt)(((self_in_CogARMv8Compiler->operands))[0]))) >= ((methodLabel->address))) + && ((((usqInt)(((self_in_CogARMv8Compiler->operands))[0]))) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize))))) + ? ((((self_in_CogARMv8Compiler->operands))[1]) != SP + ? 4 + : 8) + : ((((self_in_CogARMv8Compiler->operands))[1]) != SP + ? 8 + : 12)); + + case MoveRAw: + return (((((((self_in_CogARMv8Compiler->operands))[1]) >= ((varBaseAddress) - (0x100))) && ((((self_in_CogARMv8Compiler->operands))[1]) <= (((varBaseAddress) + (0x1000)) - 1)))) + || (/* addressIsInCurrentCompilation: */ + ((((usqInt)(((self_in_CogARMv8Compiler->operands))[1]))) >= ((methodLabel->address))) + && ((((usqInt)(((self_in_CogARMv8Compiler->operands))[1]))) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize))))) + ? ((((self_in_CogARMv8Compiler->operands))[0]) != SP + ? 4 + : 8) + : ((((self_in_CogARMv8Compiler->operands))[0]) != SP + ? 8 + : 12)); + + case MoveAwRR: + assert(isAddressRelativeToVarBase(self_in_CogARMv8Compiler, ((self_in_CogARMv8Compiler->operands))[0])); + return (((((self_in_CogARMv8Compiler->operands))[1]) == SP) + || ((((self_in_CogARMv8Compiler->operands))[2]) == SP) + ? 8 + : 4); + + case MoveRRAw: + assert(isAddressRelativeToVarBase(self_in_CogARMv8Compiler, ((self_in_CogARMv8Compiler->operands))[2])); + return (((((self_in_CogARMv8Compiler->operands))[0]) == SP) + || ((((self_in_CogARMv8Compiler->operands))[1]) == SP) + ? 8 + : 4); + + case MoveAbR: + case PrefetchAw: + return ((((((self_in_CogARMv8Compiler->operands))[0]) >= ((varBaseAddress) - (0x100))) && ((((self_in_CogARMv8Compiler->operands))[0]) <= (((varBaseAddress) + (0x1000)) - 1))) + ? 4 + : 8); + + case MoveRAb: + return ((((((self_in_CogARMv8Compiler->operands))[1]) >= ((varBaseAddress) - (0x100))) && ((((self_in_CogARMv8Compiler->operands))[1]) <= (((varBaseAddress) + (0x1000)) - 1))) + ? 4 + : 8); + + case MoveMwrR: + case MoveM32rR: + case MoveM16rR: + case MoveMbrR: + return (isImm12orImm9offset(self_in_CogARMv8Compiler, ((self_in_CogARMv8Compiler->operands))[0]) + ? 4 + : 8); + + case MoveRMwr: + case MoveRM32r: + case MoveRM16r: + case MoveRMbr: + return (isImm12orImm9offset(self_in_CogARMv8Compiler, ((self_in_CogARMv8Compiler->operands))[1]) + ? 4 + : 8); + + case MoveM64rRd: + return (isUnsigned12BitMultipleOf8(self_in_CogARMv8Compiler, ((self_in_CogARMv8Compiler->operands))[0]) + ? 4 + : 8); + + case MoveRdM64r: + return (isUnsigned12BitMultipleOf8(self_in_CogARMv8Compiler, ((self_in_CogARMv8Compiler->operands))[1]) + ? 4 + : 8); + + default: + return 4; + } + + /* Noops & Pseudo Ops + Control + Arithmetic + N.B. For three operand logical ops only support AndCqRR with a NativeSPReg target, used for alignment purposes. + ARMv8 Specific Arithmetic + Data Movement */ + return 0; +} + + +/* Will get inlined into concretizeAt: switch. */ +/* Sizing/generating jumps. + Jump targets can be to absolute addresses or other abstract instructions. + Generating initial trampolines instructions may have no maxSize and be to + absolute addresses. + Otherwise instructions must have a machineCodeSize which must be kept to. */ + + /* CogARMv8Compiler>>#concretizeConditionalJump: */ +static NoDbgRegParms sqInt +concretizeConditionalJump(AbstractInstruction *self_in_CogARMv8Compiler, sqInt conditionCode) +{ + AbstractInstruction *jumpTarget; + sqInt offset; + + /* begin computeJumpTargetOffset */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogARMv8Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset = (((sqLong) jumpTarget)) - (((sqLong) ((self_in_CogARMv8Compiler->address)))); + assert((offset != 0) + && (isInImmediateBranchRange(self_in_CogARMv8Compiler, offset))); + ((self_in_CogARMv8Compiler->machineCode))[0] = ((/* begin cond:offset: */ + assert((offset & 3) == 0), +((0x54000000) + ((((usqInt)((offset & (0x1FFFFF))) << 3)))) + conditionCode)); + return 4; +} + + +/* cogit processor disassembleInstructionAt: 0 In: machineCode object */ +/* cogit processor disassembleInstructionAt: 4 In: machineCode object */ + + /* CogARMv8Compiler>>#concretizeCwRArithmetic:Rd: */ +static NoDbgRegParms sqInt +concretizeCwRArithmeticRd(AbstractInstruction *self_in_CogARMv8Compiler, sqInt arithOp, sqInt destRegOrXZR) +{ + sqInt instrBytes; + + instrBytes = emitMoveCwintoRat(self_in_CogARMv8Compiler, ((self_in_CogARMv8Compiler->operands))[0], RISCTempReg, 0); + + /* ADD (extended register) on page C6-758 + ADDS (extended register) on page C6-766 + SUB (extended register) on page C6-1308 + SUBS (extended register) on page C6-1318 */ + ((self_in_CogARMv8Compiler->machineCode))[instrBytes / 4] = ((((((0x8B200000U) + ((((usqInt)(arithOp) << 29)))) + ((((usqInt)(RISCTempReg) << 16)))) + ((((usqInt)(UXTX) << 13)))) + ((((usqInt)((((self_in_CogARMv8Compiler->operands))[1])) << 5)))) + destRegOrXZR); + return instrBytes + 4; +} + + /* CogARMv8Compiler>>#concretizeCwRLogical: */ +static NoDbgRegParms sqInt +concretizeCwRLogical(AbstractInstruction *self_in_CogARMv8Compiler, sqInt op) +{ + sqInt constant; + sqInt destReg; + sqInt offset; + + constant = ((self_in_CogARMv8Compiler->operands))[0]; + destReg = ((self_in_CogARMv8Compiler->operands))[1]; + offset = emitMoveCwintoRat(self_in_CogARMv8Compiler, constant, RISCTempReg, 0); + + /* AND (shifted register) - 64-bit variant on page C6-777 + BIC (shifted register) - 64-bit variant on page C6-808 + ORR (shifted register) - 64-bit variant on page C6-1127 + ORN (shifted register) - 64-bit variant on page C6-1123 + EOR (shifted register) - 64-bit variant on page C6-898 + EON (shifted register) - 64-bit variant on page C6-894 + ANDS (shifted register) - 64-bit variant on page C6-781 + BICS (shifted register) - 64-bit variant on page C6-810 */ + ((self_in_CogARMv8Compiler->machineCode))[offset / 4] = (((((0x8A000000U) + ((((usqInt)(op) << 29)))) + ((((usqInt)(RISCTempReg) << 16)))) + ((((usqInt)(destReg) << 5)))) + destReg); + return offset + 4; +} + + +/* Issue a DC CIVAC, CVAC or CVAU + C5.3.14 DC CIVAC, Data or unified Cache line Clean and Invalidate by VA to + PoC C5-486 Clean and Invalidate data cache by address to Point of + Coherency. C5.3.16 DC CVAC, Data or unified Cache line Clean by VA to + PoC C5-490 Clean data cache by address to Point of Coherency. + C5.3.19 DC CVAU, Clean data cache by address to Point of + Unification C5-496 Clean data cache by address to Point of Unification. */ +/* (operands at: 0) is the target register, accessed within + concretizeCacheControlOp1:CRm:Op2: + */ + + /* CogARMv8Compiler>>#concretizeDataCacheControl */ +static NoDbgRegParms sqInt +concretizeDataCacheControl(AbstractInstruction *self_in_CogARMv8Compiler) +{ + int cacheOpcode; + + switch (((self_in_CogARMv8Compiler->operands))[1]) { + case DC_CIVAC: + cacheOpcode = 14; + break; + case DC_CVAC: + cacheOpcode = 10; + break; + case DC_CVAU: + cacheOpcode = 11; + break; + default: + error("Case not found and no otherwise clause"); + } + + /* begin concretizeCacheControlOp1:CRm:Op2: */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0xD50B7000U) + ((((usqInt)(cacheOpcode) << 8)))) + (32)) + (((self_in_CogARMv8Compiler->operands))[0])); + + /* CRn + cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; +} + + +/* fill with operand 0 according to the processor's endianness */ + + /* CogARMv8Compiler>>#concretizeFill32 */ +static NoDbgRegParms sqInt +concretizeFill32(AbstractInstruction *self_in_CogARMv8Compiler) +{ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((self_in_CogARMv8Compiler->operands))[0]); + return 4; +} + + +/* Generate an out-of-line literal. Copy the value and any annotation from + the stand-in in the literals manager. */ + + /* CogARMv8Compiler>>#concretizeLiteral */ +static NoDbgRegParms sqInt +concretizeLiteral(AbstractInstruction *self_in_CogARMv8Compiler) +{ + usqInt literal; + AbstractInstruction *literalAsInstruction; + + literalAsInstruction = ((AbstractInstruction *) (((self_in_CogARMv8Compiler->operands))[0])); + literal = (/* isAnInstruction: */ + (addressIsInInstructions(literalAsInstruction)) + || (literalAsInstruction == (methodLabel)) + ? (literalAsInstruction->address) + : ((usqInt)literalAsInstruction)); + assert((((self_in_CogARMv8Compiler->dependent))) + && (((((self_in_CogARMv8Compiler->dependent))->opcode)) == Literal)); + if ((((self_in_CogARMv8Compiler->dependent))->annotation)) { + assert(!((self_in_CogARMv8Compiler->annotation))); + (self_in_CogARMv8Compiler->annotation) = (((self_in_CogARMv8Compiler->dependent))->annotation); + } + if ((((self_in_CogARMv8Compiler->dependent))->address)) { + assert(((((self_in_CogARMv8Compiler->dependent))->address)) == ((self_in_CogARMv8Compiler->address))); + } + (((self_in_CogARMv8Compiler->dependent))->address = (self_in_CogARMv8Compiler->address)); + ((self_in_CogARMv8Compiler->machineCode))[0] = (literal & 0xFFFFFFFFU); + if (((/* begin literalSize */ + assert(((self_in_CogARMv8Compiler->opcode)) == Literal), + (((self_in_CogARMv8Compiler->operands))[1] + ? (((usqInt)((((self_in_CogARMv8Compiler->operands))[1]))) >> 1) & 15 + : BytesPerOop))) == 4) { + return 4; + } + ((self_in_CogARMv8Compiler->machineCode))[1] = ((literal) >> 32); + return 8; +} + + /* CogARMv8Compiler>>#concretizeLoadEffectiveAddressMwrR */ +static NoDbgRegParms sqInt +concretizeLoadEffectiveAddressMwrR(AbstractInstruction *self_in_CogARMv8Compiler) +{ + sqInt baseReg; + sqInt destReg; + sqInt instrBytes; + sqInt offset; + + offset = ((self_in_CogARMv8Compiler->operands))[0]; + baseReg = ((self_in_CogARMv8Compiler->operands))[1]; + destReg = ((self_in_CogARMv8Compiler->operands))[2]; + + /* begin isPossiblyShiftableImm12:ifTrue:ifFalse: */ + if (((offset >= 0) && (offset <= (0xFFF)))) { + /* C6.2.4 ADD (immediate) C6-761 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0x91000000U) + ((((usqInt)(offset) << 10)))) + ((((usqInt)(baseReg) << 5)))) + destReg); + return 4; + } + if (((!(offset & (0xFFF)))) + && (((((offset) >> 12) >= 0) && (((offset) >> 12) <= (0xFFF))))) { + /* C6.2.4 ADD (immediate) C6-761 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0x91000000U) + (((usqInt)(offset)) >> 2)) + ((((usqInt)(baseReg) << 5)))) + destReg); + return 4; + } + /* end isPossiblyShiftableImm12:ifTrue:ifFalse: */ +l1: + + /* begin isPossiblyShiftableImm12:ifTrue:ifFalse: */ + if ((((-offset) >= 0) && ((-offset) <= (0xFFF)))) { + /* C6.2.308 SUB (immediate) C6-1311 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0xD1000000U) + ((((usqInt)((-offset)) << 10)))) + ((((usqInt)(baseReg) << 5)))) + destReg); + return 4; + } + if (((!((-offset) & (0xFFF)))) + && ((((((-offset)) >> 12) >= 0) && ((((-offset)) >> 12) <= (0xFFF))))) { + /* C6.2.308 SUB (immediate) C6-1311 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0xD1000000U) + (((usqInt)((-offset))) >> 2)) + ((((usqInt)(baseReg) << 5)))) + destReg); + return 4; + } + /* end isPossiblyShiftableImm12:ifTrue:ifFalse: */ +l2: + instrBytes = emitMoveCwintoRat(self_in_CogARMv8Compiler, offset, RISCTempReg, 0); + + /* C6.2.3 ADD (extended register) C6-758 */ + ((self_in_CogARMv8Compiler->machineCode))[instrBytes / 4] = (((((0x8B200000U) + ((((usqInt)(destReg) << 16)))) + ((((usqInt)(UXTX) << 13)))) + ((((usqInt)(RISCTempReg) << 5)))) + destReg); + return instrBytes + 4; +} + + +/* AND (immediate) - 64-bit variant on page C6-775 + ORR (immediate) - 64-bit variant on page C6-1125 + EOR (immediate) - 64-bit variant on page C6-896 + ANDS (immediate) - 64-bit variant on page C6-779 + C6.2.329 TST (immediate) C6-1346 */ + + /* CogARMv8Compiler>>#concretizeLogicalOp:CqRDest: */ +static NoDbgRegParms sqInt +concretizeLogicalOpCqRDest(AbstractInstruction *self_in_CogARMv8Compiler, sqInt op, sqInt destReg) +{ + sqInt constant; + sqInt effectiveDestReg; + sqInt imm; + sqInt immrSqInt; + usqInt mask; + usqInt nImms; + usqInt nUsqInt; + sqInt numLeadingOnes; + sqInt numTrailingOnes; + sqInt offset; + sqInt rotateCount; + sqInt size; + sqInt srcReg; + + constant = ((sqLong) (((self_in_CogARMv8Compiler->operands))[0])); + srcReg = ((self_in_CogARMv8Compiler->operands))[1]; + + /* N.B. For three operand logical ops only support AndCq: const R: reg R: NativeSPReg, which is used for alignment. */ + effectiveDestReg = ((((self_in_CogARMv8Compiler->opcode)) == AndCqRR) + && (destReg == SP) + ? RISCTempReg + : destReg); + + /* begin isImmNImmSImmREncodableBitmask:ifTrue:ifFalse: */ + if (((constant >= -1) && (constant <= 0))) { + offset = emitMoveCwintoRat(self_in_CogARMv8Compiler, constant, RISCTempReg, 0); + + /* OPC N + 00 0 AND (shifted register) - 64-bit variant on page C6-777 + 00 1 BIC (shifted register) - 64-bit variant on page C6-808 + 01 0 ORR (shifted register) - 64-bit variant on page C6-1127 + 01 1 ORN (shifted register) - 64-bit variant on page C6-1123 + 10 0 EOR (shifted register) - 64-bit variant on page C6-898 + 10 1 EON (shifted register) - 64-bit variant on page C6-894 + 11 0 ANDS (shifted register) - 64-bit variant on page C6-781 + 11 0 BICS (shifted register) - 64-bit variant on page C6-810 */ + ((self_in_CogARMv8Compiler->machineCode))[offset / 4] = (((((0x8A000000U) + ((((usqInt)(op) << 29)))) + ((((usqInt)(RISCTempReg) << 16)))) + ((((usqInt)(srcReg) << 5)))) + effectiveDestReg); + offset += 4; + goto l1; + } + imm = constant; + + /* First, determine the element size. */ + size = 32; + while (1) { + mask = (1ULL << size) - 1; + if (!(((imm & mask) != ((((usqInt)(imm)) >> size) & mask) + ? ((size = size * 2), + 0) + : size > 2))) break; + size = size / 2; + } + + /* Second, determine the rotation to make the element be: 0^m 1^n. */ + mask = ((usqInt)((0xFFFFFFFFFFFFFFFFULL))) >> (64 - size); + imm = imm & mask; + if (isShiftedMask(self_in_CogARMv8Compiler, imm)) { + rotateCount = countTrailingZeros(self_in_CogARMv8Compiler, imm); + numTrailingOnes = countTrailingOnes(self_in_CogARMv8Compiler, ((usqInt)(imm)) >> rotateCount); + } + else { + imm = imm | (~(usqIntptr_t)mask); + if (!(isShiftedMask(self_in_CogARMv8Compiler, imm))) { + offset = emitMoveCwintoRat(self_in_CogARMv8Compiler, constant, RISCTempReg, 0); + + /* OPC N + 00 0 AND (shifted register) - 64-bit variant on page C6-777 + 00 1 BIC (shifted register) - 64-bit variant on page C6-808 + 01 0 ORR (shifted register) - 64-bit variant on page C6-1127 + 01 1 ORN (shifted register) - 64-bit variant on page C6-1123 + 10 0 EOR (shifted register) - 64-bit variant on page C6-898 + 10 1 EON (shifted register) - 64-bit variant on page C6-894 + 11 0 ANDS (shifted register) - 64-bit variant on page C6-781 + 11 0 BICS (shifted register) - 64-bit variant on page C6-810 */ + ((self_in_CogARMv8Compiler->machineCode))[offset / 4] = (((((0x8A000000U) + ((((usqInt)(op) << 29)))) + ((((usqInt)(RISCTempReg) << 16)))) + ((((usqInt)(srcReg) << 5)))) + effectiveDestReg); + offset += 4; + goto l1; + } + numLeadingOnes = countLeadingOnes(self_in_CogARMv8Compiler, imm); + rotateCount = 64 - numLeadingOnes; + numTrailingOnes = (numLeadingOnes + (countTrailingOnes(self_in_CogARMv8Compiler, imm))) - (64 - size); + } + + /* Encode in Immr the number of RORs it would take to get *from* 0^m 1^n + to our target value, where I is the number of RORs to go the opposite direction. */ + assert(size > rotateCount); + immrSqInt = (size - rotateCount) & (size - 1); + + /* If size has a 1 in the n'th bit, create a value that has zeroes in bits [0, n] and ones above that. */ + nImms = ((sqInt)((usqInt)((~(usqIntptr_t)(size - 1))) << 1)); + + /* Or the CTO value into the low bits, which must be below the Nth bit mentioned above. */ + nImms = nImms | (numTrailingOnes - 1); + + /* Extract the seventh bit and toggle it to create the N field. */ + nUsqInt = (((nImms) >> 6) & 1) ^ 1; + nImms = nImms & 0x3F; + assert((decodeNimmsimmr(self_in_CogARMv8Compiler, nUsqInt, nImms, immrSqInt)) == (((usqInt) constant))); + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((((0x92000000U) + ((((usqInt)(op) << 29)))) + ((nUsqInt << 22))) + ((((usqInt)(immrSqInt) << 16)))) + ((nImms << 10))) + ((((usqInt)(srcReg) << 5)))) + effectiveDestReg); + offset = 4; + /* end isImmNImmSImmREncodableBitmask:ifTrue:ifFalse: */ +l1: + if (!((((self_in_CogARMv8Compiler->opcode)) == AndCqRR) + && (destReg == SP))) { + return offset; + } + ((self_in_CogARMv8Compiler->machineCode))[offset / 4] = (movernrd(self_in_CogARMv8Compiler, effectiveDestReg, destReg)); + return offset + 4; +} + + +/* C3.3.4 Move (immediate) C3-215 + + The Move (immediate) instructions are aliases for a single MOVZ, MOVN, or + ORR (immediate with zero register), + instruction to load an immediate value into the destination register. An + assembler must permit a signed or + unsigned immediate, as long as its binary representation can be generated + using one of these instructions, + and an assembler error results if the immediate cannot be generated in + this way. On disassembly, it is + unspecified whether the immediate is output as a signed or an unsigned + value. + C6.2.191 MOVZ C6-1102 Move wide with zero moves an optionally-shifted + 16-bit immediate value to a register. + C6.2.190 MOVN C6-1100 Move wide with NOT moves the inverse of an + optionally-shifted 16-bit immediate value to a register. + C6.2.204 ORR (immediate) C6-1125 + Bitwise OR (immediate) performs a bitwise (inclusive) OR of a register + value and an immediate + register value, and writes the result to the destination register. */ + + /* CogARMv8Compiler>>#concretizeMoveCqR */ +static NoDbgRegParms sqInt +concretizeMoveCqR(AbstractInstruction *self_in_CogARMv8Compiler) +{ + sqInt constant; + sqInt destReg; + sqInt imm; + sqInt immrSqInt; + sqInt lowBit; + sqInt lowBitMod16; + sqInt mask; + usqInt maskUsqInt; + usqInt nImms; + usqInt nUsqInt; + sqInt numLeadingOnes; + sqInt numTrailingOnes; + sqInt rotateCount; + sqInt size; + + constant = ((self_in_CogARMv8Compiler->operands))[0]; + destReg = ((self_in_CogARMv8Compiler->operands))[1]; + if (destReg != SP) { + lowBit = (constant > 0 + ? computeLowBit(self_in_CogARMv8Compiler, constant) + : 0); + lowBitMod16 = (lowBit / 16) * 16; + mask = ((sqInt)((usqInt)((0xFFFF)) << lowBitMod16)); + if ((constant & mask) == constant) { + /* Use MOVZ */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0xD2800000U) + ((((usqInt)((lowBitMod16 / 16)) << 21)))) + ((((usqInt)((((usqInt)(constant)) >> lowBitMod16)) << 5)))) + destReg); + return 4; + } + lowBit = ((((sqLong) constant)) < -1 + ? computeLowBit(self_in_CogARMv8Compiler, ~(usqIntptr_t)constant) + : 0); + if ((((sqLong) (constant | mask))) == -1) { + /* Use MOVN */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0x92800000U) + ((((usqInt)((lowBitMod16 / 16)) << 21)))) + ((((usqInt)(((((usqInt)((~(usqIntptr_t)constant))) >> lowBitMod16) & mask)) << 5)))) + destReg); + return 4; + } + } + + /* begin isImmNImmSImmREncodableBitmask:ifTrue:ifFalse: */ + if (((constant >= -1) && (constant <= 0))) { + return emitMoveCwintoRat(self_in_CogARMv8Compiler, constant, destReg, 0); + } + imm = constant; + + /* First, determine the element size. */ + size = 32; + while (1) { + maskUsqInt = (1ULL << size) - 1; + if (!(((imm & maskUsqInt) != ((((usqInt)(imm)) >> size) & maskUsqInt) + ? ((size = size * 2), + 0) + : size > 2))) break; + size = size / 2; + } + + /* Second, determine the rotation to make the element be: 0^m 1^n. */ + maskUsqInt = ((usqInt)((0xFFFFFFFFFFFFFFFFULL))) >> (64 - size); + imm = imm & maskUsqInt; + if (isShiftedMask(self_in_CogARMv8Compiler, imm)) { + rotateCount = countTrailingZeros(self_in_CogARMv8Compiler, imm); + numTrailingOnes = countTrailingOnes(self_in_CogARMv8Compiler, ((usqInt)(imm)) >> rotateCount); + } + else { + imm = imm | (~(usqIntptr_t)maskUsqInt); + if (!(isShiftedMask(self_in_CogARMv8Compiler, imm))) { + return emitMoveCwintoRat(self_in_CogARMv8Compiler, constant, destReg, 0); + } + numLeadingOnes = countLeadingOnes(self_in_CogARMv8Compiler, imm); + rotateCount = 64 - numLeadingOnes; + numTrailingOnes = (numLeadingOnes + (countTrailingOnes(self_in_CogARMv8Compiler, imm))) - (64 - size); + } + + /* Encode in Immr the number of RORs it would take to get *from* 0^m 1^n + to our target value, where I is the number of RORs to go the opposite direction. */ + assert(size > rotateCount); + immrSqInt = (size - rotateCount) & (size - 1); + + /* If size has a 1 in the n'th bit, create a value that has zeroes in bits [0, n] and ones above that. */ + nImms = ((sqInt)((usqInt)((~(usqIntptr_t)(size - 1))) << 1)); + + /* Or the CTO value into the low bits, which must be below the Nth bit mentioned above. */ + nImms = nImms | (numTrailingOnes - 1); + + /* Extract the seventh bit and toggle it to create the N field. */ + nUsqInt = (((nImms) >> 6) & 1) ^ 1; + nImms = nImms & 0x3F; + assert((decodeNimmsimmr(self_in_CogARMv8Compiler, nUsqInt, nImms, immrSqInt)) == (((usqInt) constant))); + return (/* Use ORR */ + + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((((0xB2000000U) + ((nUsqInt << 22))) + ((((usqInt)(immrSqInt) << 16)))) + ((nImms << 10))) + ((((usqInt)(XZR) << 5)))) + destReg), + 4); +} + + +/* Mwr/M32r/M16r/Mbr - memory unit whose address is a constant M away from an + address in a register + */ + + /* CogARMv8Compiler>>#concretizeMoveMSrR: */ +static NoDbgRegParms sqInt +concretizeMoveMSrR(AbstractInstruction *self_in_CogARMv8Compiler, sqInt unitSizeLog2MinusOne) +{ + return emitLdrnrtimmshiftBy12at(self_in_CogARMv8Compiler, unitSizeLog2MinusOne, ((self_in_CogARMv8Compiler->operands))[1], ((sqLong) (((self_in_CogARMv8Compiler->operands))[2])), ((self_in_CogARMv8Compiler->operands))[0], 0, 0); +} + + +/* C6.2.192 MRS Move System Register p C6-1104 */ +/* D13.4.2 PMCCNTR_EL0, Counter-timer Virtual Count register p + D13-3531 this is a cycle frequency counter + */ +/* D13.8.26 CNTVCT_EL0, Counter-timer Virtual Count register p + D13-3774 this is a 24MHz to 1GHz counter depending on implementation + */ +/* MRS , PMCCNTR_EL0 op0:0b11 op1:0b011 CRn:0b1001 CRm:0b1101 op2:0b000 + MRS , CNTVCT_EL0 op0:0b11 op1:0b011 CRn:0b1110 CRm:0b0000 op2:0b010 */ +/* Use CNTVCT_EL0; we would prefer to use PMCCNTR_EL0 but at least macOS + makes it a priviledged instruction. + */ + + /* CogARMv8Compiler>>#concretizeMovePerfCnt64RL */ +static NoDbgRegParms sqInt +concretizeMovePerfCnt64RL(AbstractInstruction *self_in_CogARMv8Compiler) +{ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((3577471040U) + (((self_in_CogARMv8Compiler->operands))[0])); + + /* MRS + op0 + op1 + CRn + CRm + op2 */ + return 4; +} + + +/* Mwr/M32r/M16r/Mbr - memory unit whose address is a constant M away from an + address in a register + */ + + /* CogARMv8Compiler>>#concretizeMoveRMSr: */ +static NoDbgRegParms sqInt +concretizeMoveRMSr(AbstractInstruction *self_in_CogARMv8Compiler, sqInt unitSizeLog2MinusOne) +{ + return emitStrnrtimmshiftBy12(self_in_CogARMv8Compiler, unitSizeLog2MinusOne, ((self_in_CogARMv8Compiler->operands))[2], ((self_in_CogARMv8Compiler->operands))[0], ((sqLong) (((self_in_CogARMv8Compiler->operands))[1])), 0); +} + + +/* Xwr/X32r/X16r/Xbr - memory unit whose address is r * unit size away from + an address in a register + */ +/* C6.2.274 STR (register) C6-1242 */ + + /* CogARMv8Compiler>>#concretizeMoveRXSrR: */ +static NoDbgRegParms sqInt +concretizeMoveRXSrR(AbstractInstruction *self_in_CogARMv8Compiler, sqInt unitSizeLog2MinusOne) +{ + sqInt base; + sqInt index; + sqInt src; + + src = ((self_in_CogARMv8Compiler->operands))[0]; + index = ((self_in_CogARMv8Compiler->operands))[1]; + base = ((self_in_CogARMv8Compiler->operands))[2]; + assert(!(((SP == src) + || (SP == index)))); + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((((((((usqInt)(unitSizeLog2MinusOne) << 30))) + (0x38200000)) + ((((usqInt)(index) << 16)))) + ((((usqInt)(UXTX) << 13)))) + (6144)) + ((((usqInt)(base) << 5)))) + src); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; +} + + +/* Xwr/X32r/X16r/Xbr - memory unit whose address is r * unit size away from + an address in a register + */ +/* C6.2.132 LDR (register) C6-981 */ + + /* CogARMv8Compiler>>#concretizeMoveXSrRR: */ +static NoDbgRegParms sqInt +concretizeMoveXSrRR(AbstractInstruction *self_in_CogARMv8Compiler, sqInt unitSizeLog2MinusOne) +{ + sqInt base; + sqInt dest; + sqInt index; + + index = ((self_in_CogARMv8Compiler->operands))[0]; + base = ((self_in_CogARMv8Compiler->operands))[1]; + dest = ((self_in_CogARMv8Compiler->operands))[2]; + assert(!((SP == dest))); + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((((((((usqInt)(unitSizeLog2MinusOne) << 30))) + (0x38600000)) + ((((usqInt)(index) << 16)))) + ((((usqInt)(UXTX) << 13)))) + (6144)) + ((((usqInt)(base) << 5)))) + dest); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; +} + + +/* Sizing/generating jumps. + Jump targets can be to absolute addresses or other abstract instructions. + Generating initial trampolines instructions may have no maxSize and be to + absolute addresses. + Otherwise instructions must have a machineCodeSize which must be kept to. */ + + /* CogARMv8Compiler>>#concretizeMulOverflowJump */ +static NoDbgRegParms sqInt +concretizeMulOverflowJump(AbstractInstruction *self_in_CogARMv8Compiler) +{ + sqInt offset; + + /* -4 because the jump is from the second word... */ + offset = (computeJumpTargetOffset(self_in_CogARMv8Compiler)) - 4; + assert((offset != 0) + && (isInImmediateBranchRange(self_in_CogARMv8Compiler, offset))); + + /* See concretizeMulOverflowRRR + RISCTempReg := RISCTempReg + CArg1Reg/sign. + JumpZero/NonZero */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0x8B000000U) + ((((usqInt)(ArithmeticAddS) << 29)))) + ((((usqInt)(CArg1Reg) << 16)))) + ((((usqInt)(RISCTempReg) << 5)))) + RISCTempReg); + ((self_in_CogARMv8Compiler->machineCode))[1] = ((/* begin cond:offset: */ + assert((offset & 3) == 0), +((0x54000000) + ((((usqInt)((offset & (0x1FFFFF))) << 3)))) + ((((self_in_CogARMv8Compiler->opcode)) == JumpMulOverflow + ? NE + : EQ)))); + return 8; +} + + +/* rd := regA op regB */ + + /* CogARMv8Compiler>>#concretizeRRArithmetic:Rd: */ +static NoDbgRegParms sqInt +concretizeRRArithmeticRd(AbstractInstruction *self_in_CogARMv8Compiler, sqInt arithOp, sqInt rd) +{ + sqInt regA; + sqInt regB; + + regB = ((self_in_CogARMv8Compiler->operands))[0]; + regA = ((self_in_CogARMv8Compiler->operands))[1]; + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object + cogit processor disassembleInstructionAt: 4 In: machineCode object */ + if (SP == regB) { + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((((0x8B200000U) + ((((usqInt)(arithOp) << 29)))) + ((((usqInt)(regA) << 16)))) + ((((usqInt)(UXTX) << 13)))) + ((((usqInt)(regB) << 5)))) + rd); + if (!((arithOp == ArithmeticSub) + || (arithOp == ArithmeticSubS))) { + return 4; + } + ((self_in_CogARMv8Compiler->machineCode))[1] = (((((0x8B000000U) + ((((usqInt)(arithOp) << 29)))) + ((((usqInt)(rd) << 16)))) + ((((usqInt)(XZR) << 5)))) + rd); + return 8; + } + + /* Arithmetic with the sp; we must use the extended register forms, and negate + on subtract because we can't have Rm as SP, Rm = 31 is interoreted as XZR. + ADD (extended register) on page C6-758 + ADDS (extended register) on page C6-766 + SUB (extended register) on page C6-1308 + SUBS (extended register) on page C6-1318 */ + if (SP == regA) { + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((((0x8B200000U) + ((((usqInt)(arithOp) << 29)))) + ((((usqInt)(regB) << 16)))) + ((((usqInt)(UXTX) << 13)))) + ((((usqInt)(regA) << 5)))) + rd); + return 4; + } + + /* Arithmetic with the sp; we must use the extended register forms. + ADD (extended register) on page C6-758 + ADDS (extended register) on page C6-766 + SUB (extended register) on page C6-1308 + SUBS (extended register) on page C6-1318 + CMN (extended register) on page C6-850 + CMP (extended register) on page C6-856 */ + assert((regA != SP) + && (regB != SP)); + + /* ADD (shifted register) on page C6-763 + ADDS (shifted register) on page C6-771 + CMN (shifted register) on page C6-854 + CMP (shifted register) on page C6-860 + SUB (shifted register) on page C6-1313 + SUBS (shifted register) on page C6-1323 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0x8B000000U) + ((((usqInt)(arithOp) << 29)))) + ((((usqInt)(regB) << 16)))) + ((((usqInt)(regA) << 5)))) + rd); + return 4; +} + + +/* AND (shifted register) - 64-bit variant on page C6-777 + BIC (shifted register) - 64-bit variant on page C6-808 + ORR (shifted register) - 64-bit variant on page C6-1127 + ORN (shifted register) - 64-bit variant on page C6-1123 + EOR (shifted register) - 64-bit variant on page C6-898 + EON (shifted register) - 64-bit variant on page C6-894 + ANDS (shifted register) - 64-bit variant on page C6-781 + BICS (shifted register) - 64-bit variant on page C6-810 */ + + /* CogARMv8Compiler>>#concretizeRRLogical: */ +static NoDbgRegParms sqInt +concretizeRRLogical(AbstractInstruction *self_in_CogARMv8Compiler, sqInt logicalOp) +{ + sqInt destReg; + sqInt srcReg; + + srcReg = ((self_in_CogARMv8Compiler->operands))[0]; + destReg = ((self_in_CogARMv8Compiler->operands))[1]; + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0x8A000000U) + ((((usqInt)(logicalOp) << 29)))) + ((((usqInt)(srcReg) << 16)))) + ((((usqInt)(destReg) << 5)))) + destReg); + return 4; +} + + +/* C6.2.320 SXTB C6-1332 + C6.2.321 SXTH C6-1334 + C6.2.322 SXTW C6-1336 */ + + /* CogARMv8Compiler>>#concretizeSignExtendRR: */ +static NoDbgRegParms sqInt +concretizeSignExtendRR(AbstractInstruction *self_in_CogARMv8Compiler, sqInt width) +{ + sqInt rd; + sqInt rn; + + rn = ((self_in_CogARMv8Compiler->operands))[0]; + rd = ((self_in_CogARMv8Compiler->operands))[1]; + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0x93400000U) + ((((usqInt)((width - 1)) << 10)))) + ((((usqInt)(rn) << 5)))) + rd); + return 4; +} + + +/* Xt */ +/* Xn */ + + /* CogARMv8Compiler>>#concretizeSTLR */ +static NoDbgRegParms sqInt +concretizeSTLR(AbstractInstruction *self_in_CogARMv8Compiler) +{ + sqInt baseReg; + sqInt valueReg; + + valueReg = ((self_in_CogARMv8Compiler->operands))[0]; + baseReg = ((self_in_CogARMv8Compiler->operands))[1]; + ((self_in_CogARMv8Compiler->machineCode))[0] = (((0xC89FFC00U) + ((((usqInt)(baseReg) << 5)))) + valueReg); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object '00000000: stlr x0, [x1]' */ + return 4; +} + + +/* C6.2.333 UBFX C6-1353 + C6.2.341 UXTB C6-1364 + C6.2.342 UXTH C6-1366 */ + + /* CogARMv8Compiler>>#concretizeZeroExtendRR: */ +static NoDbgRegParms sqInt +concretizeZeroExtendRR(AbstractInstruction *self_in_CogARMv8Compiler, sqInt width) +{ + sqInt rd; + sqInt rn; + + rn = ((self_in_CogARMv8Compiler->operands))[0]; + rd = ((self_in_CogARMv8Compiler->operands))[1]; + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0xD3400000U) + ((((usqInt)((width - 1)) << 10)))) + ((((usqInt)(rn) << 5)))) + rd); + return 4; +} + + /* CogARMv8Compiler>>#countLeadingOnes: */ +static NoDbgRegParms sqInt +countLeadingOnes(AbstractInstruction *self_in_CogARMv8Compiler, sqInt anInteger) +{ + sqInt count; + sqInt logBase2Shift; + sqInt shift; + sqInt theInteger; + + if (anInteger >= 0) { + return 0; + } + theInteger = anInteger; + count = 0; + for (logBase2Shift = 5; logBase2Shift >= 0; logBase2Shift += -1) { + shift = 1ULL << logBase2Shift; + if (((theInteger) >> shift) == -1) { + count += shift; + } + else { + theInteger = (theInteger) >> shift; + } + } + return (theInteger == -1 + ? count + 1 + : count); +} + + /* CogARMv8Compiler>>#countTrailingOnes: */ +static NoDbgRegParms sqInt +countTrailingOnes(AbstractInstruction *self_in_CogARMv8Compiler, sqInt anInteger) +{ + sqInt bits; + sqInt count; + + bits = anInteger; + count = 0; + while (((bits & 0xFFFF) == 0xFFFF)) { + bits = (((usqInt)(bits)) >> 16); + count += 16; + } + while (((bits & 0xFF) == 0xFF)) { + bits = (((usqInt)(bits)) >> 8); + count += 8; + } + if (((bits & 15) == 15)) { + bits = (((usqInt)(bits)) >> 4); + count += 4; + } + if (((bits & 3) == 3)) { + bits = (((usqInt)(bits)) >> 2); + count += 2; + } + return (((bits & 1) == 1) + ? count + 1 + : count); +} + + +/* a.k.a. anInteger lowBit - 1 */ + + /* CogARMv8Compiler>>#countTrailingZeros: */ +static NoDbgRegParms sqInt +countTrailingZeros(AbstractInstruction *self_in_CogARMv8Compiler, sqInt anInteger) +{ + sqInt n; + sqInt result; + + assert(anInteger != 0); + n = anInteger; + result = 0; + while ((!(n & 0xFF))) { + result += 8; + n = (((usqInt)(n)) >> 8); + } + if ((!(n & 15))) { + result += 4; + n = (((usqInt)(n)) >> 4); + } + if ((!(n & 3))) { + result += 2; + n = (((usqInt)(n)) >> 2); + } + return (((n & 1) != 0) + ? result + : result + 1); +} + + +/* See aarch64/instrs/integer/bitmasks/DecodeBitMasks J1-7389. */ + + /* CogARMv8Compiler>>#decodeN:imms:immr: */ +static NoDbgRegParms usqInt +decodeNimmsimmr(AbstractInstruction *self_in_CogARMv8Compiler, sqInt n, sqInt imms, sqInt immr) +{ + usqInt bits; + sqInt immediate; + usqInt mask; + sqInt rotation; + sqInt width; + + bits = 0; + width = 0; + assert((((n >= 0) && (n <= 1))) + && ((((imms >= 0) && (imms <= 0x3F))) + && (((immr >= 0) && (immr <= 0x3F))))); + + /* For logical immediates an all-ones value of S is reserved since it would generate a useless all-ones result (many times) */ + if (imms == 0x3F) { + return 0; + } + if (n == 1) { + width = 64; + mask = 0xFFFFFFFFFFFFFFFFULL; + bits = imms; + rotation = immr; + } + else { + if (imms < 32) { + width = 32; + bits = imms; + } + else { + if (imms < 48) { + width = 16; + bits = imms & 15; + } + else { + if (imms < 56) { + width = 8; + bits = imms & 7; + } + else { + if (imms < 60) { + width = 4; + bits = imms & 3; + } + else { + if (imms < 0x3E) { + width = 2; + bits = imms & 1; + } + else { + error("invalid logical immediate"); + } + } + } + } + } + mask = (1ULL << width) - 1; + rotation = immr & (width - 1); + } + if ((width - 1) == bits) { + return 0; + } + immediate = (1ULL << (bits + 1)) - 1; + if (rotation > 0) { + immediate = ((sqInt)((usqInt)(immediate) << (width - rotation))); + } + if (((width >= 2) && (width <= 32))) { + immediate = (((width < 0) ? (((usqInt)(immediate)) >> (-width)) : ((((usqInt)(immediate) << width))))) | immediate; + } + return immediate; +} + + +/* Do a throw-away compilation to read CTR_EL0 and initialize ctrEl0. + Some linux kernels trap and synthesize access to ID_AA64ISAR0_EL1, + and some do not, so use getauxval(3) to access value(s) derived + there-from, i.e. whether the processor has atomic instructions. */ + + /* CogARMv8Compiler>>#detectFeaturesOnLinux */ +#if __linux__ +static NoDbgRegParms AbstractInstruction * +detectFeaturesOnLinux(AbstractInstruction *self_in_CogARMv8Compiler) +{ + sqInt ctrEL0; + usqIntptr_t fixupSize; + usqIntptr_t (*getFeatureReg)(void); + usqIntptr_t opcodeSize; + usqInt startAddress; + + startAddress = methodZoneBase; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 4; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + firstOpcodeIndex = 0x10000; + nextLiteralIndex = (lastDumpedLiteralIndex = 0); + labelCounter = 0; + getFeatureReg = ((usqIntptr_t (*)(void)) startAddress); + + /* Return the value of CTR_EL0; that's the control register that defines the vital statistics of the processor's caches. */ + gen(Nop); + genoperand(MRS_CTR_EL0, ABIResultReg); + + /* RetN: */ + genoperand(RetN, 0); + + /* do something anodyne so it is easy to distinguish MRS_CTR_EL0 being an illegal instruction rather than the code zone not being executable. */ + outputInstructionsForGeneratedRuntimeAt(startAddress); + + /* begin resetMethodZoneBase: */ + methodZoneBase = startAddress; + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } + ctrEL0 = getFeatureReg(); + + /* see e.g. CogARMv8Compiler class>>printCTR_EL0:, concretizeCacheControlOp1:CRm:Op2: & + http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.100403_0200_00_en/lau1443435580346.html + DminLine & IminLine are Log2 words; 16 words miniumum */ + setDataCacheFlushRequired(self_in_CogARMv8Compiler, (!(ctrEL0 & (0x10000000)))); + setDataCacheLineLength(self_in_CogARMv8Compiler, 4ULL << ((((usqInt)(ctrEL0)) >> 16) & 15)); + if (!(dataCacheLineLength(self_in_CogARMv8Compiler))) { + setDataCacheLineLength(self_in_CogARMv8Compiler, 64); + } + setInstructionCacheFlushRequired(self_in_CogARMv8Compiler, (!(ctrEL0 & (0x20000000)))); + setInstructionCacheLineLength(self_in_CogARMv8Compiler, 4ULL << (ctrEL0 & 15)); + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + if (!(instructionCacheLineLength(self_in_CogARMv8Compiler))) { + setInstructionCacheLineLength(self_in_CogARMv8Compiler, 64); + } + return self_in_CogARMv8Compiler; +} +#endif /* __linux__ */ + + +/* Do throw-away compilations to read CTR_EL0 & ID_AA64ISAR0_EL1 and + initialize ctrEl0 & idISAR0 + */ + + /* CogARMv8Compiler>>#detectFeaturesOnRawMachine */ +#if !__APPLE__ && !__linux__ +static NoDbgRegParms AbstractInstruction * +detectFeaturesOnRawMachine(AbstractInstruction *self_in_CogARMv8Compiler) +{ + sqInt ctrEL0; + usqIntptr_t fixupSize; + usqIntptr_t (*getFeatureReg)(void); + usqIntptr_t opcodeSize; + usqInt startAddress; + + startAddress = methodZoneBase; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 4; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + firstOpcodeIndex = 0x10000; + nextLiteralIndex = (lastDumpedLiteralIndex = 0); + labelCounter = 0; + getFeatureReg = ((usqIntptr_t (*)(void)) startAddress); + + /* Return the value of CTR_EL0; that's the control register that defines the vital statistics of the processor's caches. */ + gen(Nop); + genoperand(MRS_CTR_EL0, ABIResultReg); + + /* RetN: */ + genoperand(RetN, 0); + + /* do something anodyne so it is easy to distinguish MRS_CTR_EL0 being an illegal instruction rather than the code zone not being executable. */ + outputInstructionsForGeneratedRuntimeAt(startAddress); + + /* begin resetMethodZoneBase: */ + methodZoneBase = startAddress; + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } + ctrEL0 = getFeatureReg(); + + /* see e.g. CogARMv8Compiler class>>printCTR_EL0:, concretizeCacheControlOp1:CRm:Op2: & + http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.100403_0200_00_en/lau1443435580346.html + DminLine & IminLine are Log2 words; 16 words miniumum */ + setDataCacheFlushRequired(self_in_CogARMv8Compiler, (!(ctrEL0 & (0x10000000)))); + setDataCacheLineLength(self_in_CogARMv8Compiler, 4ULL << ((((usqInt)(ctrEL0)) >> 16) & 15)); + if (!(dataCacheLineLength(self_in_CogARMv8Compiler))) { + setDataCacheLineLength(self_in_CogARMv8Compiler, 64); + } + setInstructionCacheFlushRequired(self_in_CogARMv8Compiler, (!(ctrEL0 & (0x20000000)))); + setInstructionCacheLineLength(self_in_CogARMv8Compiler, 4ULL << (ctrEL0 & 15)); + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + if (!(instructionCacheLineLength(self_in_CogARMv8Compiler))) { + setInstructionCacheLineLength(self_in_CogARMv8Compiler, 64); + } + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + firstOpcodeIndex = 0x10000; + nextLiteralIndex = (lastDumpedLiteralIndex = 0); + gen(Nop); + genoperand(MRS_ID_AA64ISAR0_EL1, ABIResultReg); + + /* RetN: */ + genoperand(RetN, 0); + + /* do something anodyne so it is easy to distinguish MRS_ID_AA64ISAR0_EL1 being an illegal instruction rather than the code zone not being executable. */ + outputInstructionsForGeneratedRuntimeAt(startAddress); + return self_in_CogARMv8Compiler; +} +#endif /* !__APPLE__ && !__linux__ */ + + +/* Windows does not allow access to ctl_el0, so derive cache information etc + from GetLogicalProcessorInformationEx + which answers a SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, containing a + CACHE_DESCRIPTOR. + */ + + /* CogARMv8Compiler>>#detectFeaturesOnWindows */ +#if _WIN64 +static NoDbgRegParms AbstractInstruction * +detectFeaturesOnWindows(AbstractInstruction *self_in_CogARMv8Compiler) +{ + extern void getCacheDimensions(sqInt *dataLineLength, sqInt *instrLineLength); + + return self_in_CogARMv8Compiler; +} +#endif /* _WIN64 */ + + +/* Attempt to generate concrete machine code for the instruction at address. + This is the inner dispatch of concretizeAt: actualAddress which exists + only to get around the branch size limits in the SqueakV3 (blue book + derived) bytecode set. */ + + /* CogARMv8Compiler>>#dispatchConcretize */ +static NoDbgRegParms sqInt +dispatchConcretize(AbstractInstruction *self_in_CogARMv8Compiler) +{ + sqInt addressReg; + sqInt baseReg; + sqInt CRm; + sqInt condition; + sqInt constant; + sqInt constantReg; + AbstractInstruction *dependentChain; + sqInt dest; + sqInt destAddr; + sqInt destReg; + sqInt flags; + sqInt instrBytes; + sqInt instrOffset; + AbstractInstruction *jumpTarget; + sqInt maskReg; + sqInt offset; + sqInt pairAddress; + sqInt rA; + sqInt rD; + sqInt rd; + sqInt reg; + sqInt reg1; + sqInt reg2; + sqInt reg3; + sqInt rM; + usqInt rN; + sqInt rNSqInt; + sqInt rn; + sqInt shiftCountReg; + sqInt shiftedReg; + sqInt srcAddr; + sqInt srcReg; + sqInt statusReg; + sqInt targetReg; + sqInt valueReg; + + switch ((self_in_CogARMv8Compiler->opcode)) { + case Label: + /* begin concretizeLabel */ + dependentChain = (self_in_CogARMv8Compiler->dependent); + while (!(!dependentChain)) { + /* begin updateLabel: */ + assert((((dependentChain->opcode)) == MoveCwR) + || (((dependentChain->opcode)) == PushCw)); + ((dependentChain->operands))[0] = (((self_in_CogARMv8Compiler->address)) + (((self_in_CogARMv8Compiler->operands))[1])); + dependentChain = (dependentChain->dependent); + } + return 0; + + case Literal: + return concretizeLiteral(self_in_CogARMv8Compiler); + + case AlignmentNops: + /* begin concretizeAlignmentNops */ + assert((((self_in_CogARMv8Compiler->machineCodeSize)) % 4) == 0); + ((self_in_CogARMv8Compiler->machineCode))[0] = NOP; + return (self_in_CogARMv8Compiler->machineCodeSize); + + case Fill32: + /* begin concretizeFill32 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((self_in_CogARMv8Compiler->operands))[0]); + return 4; + + case Nop: + /* begin concretizeNop */ + ((self_in_CogARMv8Compiler->machineCode))[0] = NOP; + return 4; + + case Call: + /* begin concretizeCall */ + offset = ((sqLong) ((((self_in_CogARMv8Compiler->operands))[0]) - ((self_in_CogARMv8Compiler->address)))); + assert((!(offset & 3))); + assert(isInImmediateBranchAndLinkRange(self_in_CogARMv8Compiler, offset)); + + /* C6.2.33 BL C6-812 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((/* begin bl: */ + assert((!(offset & 3))), +(0x94000000U) + (((offset) >> 2) & (0x3FFFFFF)))); + return 4; + + case CallFull: + /* begin concretizeCallJumpFull: */ + /* begin longJumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogARMv8Compiler->operands))[0])); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + instrOffset = emitMoveCwintoRat(self_in_CogARMv8Compiler, ((usqInt)jumpTarget), RISCTempReg, 0); + ((self_in_CogARMv8Compiler->machineCode))[instrOffset / 4] = (brlinkreg(self_in_CogARMv8Compiler, 1, RISCTempReg)); + assert(instrOffset == (literalLoadInstructionBytes(self_in_CogARMv8Compiler))); + return instrOffset + 4; + + case JumpR: + /* begin concretizeJumpR */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((0xD6000000U) + ((((usqInt)(XZR) << 16)))) + ((((usqInt)((((self_in_CogARMv8Compiler->operands))[0])) << 5)))); + return 4; + + case JumpFull: + /* begin concretizeCallJumpFull: */ + /* begin longJumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogARMv8Compiler->operands))[0])); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + instrOffset = emitMoveCwintoRat(self_in_CogARMv8Compiler, ((usqInt)jumpTarget), RISCTempReg, 0); + ((self_in_CogARMv8Compiler->machineCode))[instrOffset / 4] = (brlinkreg(self_in_CogARMv8Compiler, 0, RISCTempReg)); + assert(instrOffset == (literalLoadInstructionBytes(self_in_CogARMv8Compiler))); + return instrOffset + 4; + + case JumpLong: + /* begin concretizeJumpLong */ + offset = (((self_in_CogARMv8Compiler->operands))[0]) - ((self_in_CogARMv8Compiler->address)); + assert((!(offset & 3))); + assert(((((offset) >> 2) >= (-67108864)) && (((offset) >> 2) <= (0x3FFFFFF)))); + ((self_in_CogARMv8Compiler->machineCode))[0] = ((0x14000000) + (((offset) >> 2) & (0x3FFFFFF))); + return 4; + + case JumpLongZero: + /* begin concretizeConditionalLongJump: */ + offset = (((self_in_CogARMv8Compiler->operands))[0]) - (((self_in_CogARMv8Compiler->address)) + 4); + assert((!(offset & 3))); + assert(((((offset) >> 2) >= (-67108864)) && (((offset) >> 2) <= (0x3FFFFFF)))); + ((self_in_CogARMv8Compiler->machineCode))[0] = ((0x54000040) + NE); + ((self_in_CogARMv8Compiler->machineCode))[1] = ((0x14000000) + (((offset) >> 2) & (0x3FFFFFF))); + + /* skip next instruction */ + return 8; + + case JumpLongNonZero: + /* begin concretizeConditionalLongJump: */ + offset = (((self_in_CogARMv8Compiler->operands))[0]) - (((self_in_CogARMv8Compiler->address)) + 4); + assert((!(offset & 3))); + assert(((((offset) >> 2) >= (-67108864)) && (((offset) >> 2) <= (0x3FFFFFF)))); + ((self_in_CogARMv8Compiler->machineCode))[0] = ((0x54000040) + EQ); + ((self_in_CogARMv8Compiler->machineCode))[1] = ((0x14000000) + (((offset) >> 2) & (0x3FFFFFF))); + + /* skip next instruction */ + return 8; + + case Jump: + return concretizeConditionalJump(self_in_CogARMv8Compiler, AL); + + case JumpZero: + case JumpFPEqual: + return concretizeConditionalJump(self_in_CogARMv8Compiler, EQ); + + case JumpNonZero: + case JumpFPNotEqual: + return concretizeConditionalJump(self_in_CogARMv8Compiler, NE); + + case JumpNegative: + return concretizeConditionalJump(self_in_CogARMv8Compiler, MI); + + case JumpNonNegative: + return concretizeConditionalJump(self_in_CogARMv8Compiler, PL); + + case JumpOverflow: + case JumpFPUnordered: + return concretizeConditionalJump(self_in_CogARMv8Compiler, VS); + + case JumpNoOverflow: + case JumpFPOrdered: + return concretizeConditionalJump(self_in_CogARMv8Compiler, VC); + + case JumpMulOverflow: + case JumpNoMulOverflow: + return concretizeMulOverflowJump(self_in_CogARMv8Compiler); + + case JumpCarry: + case JumpAboveOrEqual: + return concretizeConditionalJump(self_in_CogARMv8Compiler, CS); + + case JumpNoCarry: + case JumpBelow: + return concretizeConditionalJump(self_in_CogARMv8Compiler, CC); + + case JumpLess: + case JumpFPLess: + return concretizeConditionalJump(self_in_CogARMv8Compiler, LT); + + case JumpGreaterOrEqual: + case JumpFPGreaterOrEqual: + return concretizeConditionalJump(self_in_CogARMv8Compiler, GE); + + case JumpGreater: + case JumpFPGreater: + return concretizeConditionalJump(self_in_CogARMv8Compiler, GT); + + case JumpLessOrEqual: + case JumpFPLessOrEqual: + return concretizeConditionalJump(self_in_CogARMv8Compiler, LE); + + case JumpAbove: + return concretizeConditionalJump(self_in_CogARMv8Compiler, HI); + + case JumpBelowOrEqual: + return concretizeConditionalJump(self_in_CogARMv8Compiler, LS); + + case RetN: + /* begin concretizeRetN */ + offset = ((self_in_CogARMv8Compiler->operands))[0]; + + /* C6.2.218 RET p1147 */ + if (!offset) { + ((self_in_CogARMv8Compiler->machineCode))[0] = (((0xD6400000U) + ((((usqInt)(XZR) << 16)))) + ((((usqInt)(LR) << 5)))); + return 4; + } + + /* C6.2.4 ADD (immediate) p761 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (addrnrdimmshiftBy12(self_in_CogARMv8Compiler, SPReg, SPReg, offset, 0)); + ((self_in_CogARMv8Compiler->machineCode))[1] = (((0xD6400000U) + ((((usqInt)(XZR) << 16)))) + ((((usqInt)(LR) << 5)))); + return 8; + + case NativeRetN: + /* begin concretizeNativeRetN */ + offset = ((self_in_CogARMv8Compiler->operands))[0]; + + /* C6.2.218 RET p1147 */ + if (!offset) { + ((self_in_CogARMv8Compiler->machineCode))[0] = (((0xD6400000U) + ((((usqInt)(XZR) << 16)))) + ((((usqInt)(LR) << 5)))); + return 4; + } + + /* C6.2.4 ADD (immediate) p761 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (addrnrdimmshiftBy12(self_in_CogARMv8Compiler, SP, SP, offset, 0)); + ((self_in_CogARMv8Compiler->machineCode))[1] = (((0xD6400000U) + ((((usqInt)(XZR) << 16)))) + ((((usqInt)(LR) << 5)))); + return 8; + + case Stop: + /* begin concretizeStop */ + ((self_in_CogARMv8Compiler->machineCode))[0] = 0xD4400000U /* stop */; + return 4; + + case AddCqR: + destReg = ((self_in_CogARMv8Compiler->operands))[1]; + + /* begin concretizeAddCqRDest: */ + constant = ((sqLong) (((self_in_CogARMv8Compiler->operands))[0])); + srcReg = ((self_in_CogARMv8Compiler->operands))[1]; + + /* begin isPossiblyShiftableImm12:ifTrue:ifFalse: */ + if (((constant >= 0) && (constant <= (0xFFF)))) { + /* C6.2.4 ADD (immediate) C6-761 + C6.2.8 ADDS (immediate) C6-769 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((((usqInt)(((srcReg == SP + ? /* ADD */ 0x122 + : /* ADDS */ 354))) << 23))) + ((((usqInt)(constant) << 10)))) + ((((usqInt)(srcReg) << 5)))) + destReg); + return 4; + } + if (((!(constant & (0xFFF)))) + && (((((constant) >> 12) >= 0) && (((constant) >> 12) <= (0xFFF))))) { + /* C6.2.4 ADD (immediate) C6-761 + C6.2.8 ADDS (immediate) C6-769 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((((usqInt)(((srcReg == SP + ? /* ADD */ 0x122 + : /* ADDS */ 354))) << 23))) + ((((usqInt)(constant)) >> 2) + (0x400000))) + ((((usqInt)(srcReg) << 5)))) + destReg); + return 4; + } + /* end isPossiblyShiftableImm12:ifTrue:ifFalse: */ +l1: + + /* begin isPossiblyShiftableImm12:ifTrue:ifFalse: */ + if ((((-constant) >= 0) && ((-constant) <= (0xFFF)))) { + /* C6.2.308 SUB (immediate) C6-1311 + C6.2.314 SUBS (immediate) C6-1321 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((((usqInt)(((srcReg == SP + ? /* SUB */ 418 + : /* SUBS */ 482))) << 23))) + ((((usqInt)((-constant)) << 10)))) + ((((usqInt)(srcReg) << 5)))) + destReg); + return 4; + } + if (((!((-constant) & (0xFFF)))) + && ((((((-constant)) >> 12) >= 0) && ((((-constant)) >> 12) <= (0xFFF))))) { + /* C6.2.308 SUB (immediate) C6-1311 + C6.2.314 SUBS (immediate) C6-1321 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((((usqInt)(((srcReg == SP + ? /* SUB */ 418 + : /* SUBS */ 482))) << 23))) + ((((usqInt)((-constant))) >> 2) + (0x400000))) + ((((usqInt)(srcReg) << 5)))) + destReg); + return 4; + } + /* end isPossiblyShiftableImm12:ifTrue:ifFalse: */ +l2: + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object + cogit processor disassembleInstructionAt: 4 In: machineCode object */ + offset = emitMoveCwintoRat(self_in_CogARMv8Compiler, constant, RISCTempReg, 0); + + /* C6.2.7 ADDS (extended register) C6-766 */ + ((self_in_CogARMv8Compiler->machineCode))[offset / 4] = (((((0xAB200000U) + ((((usqInt)(RISCTempReg) << 16)))) + ((((usqInt)(SXTX) << 13)))) + ((((usqInt)(srcReg) << 5)))) + destReg); + return offset + 4; + + case AddCqRR: + destReg = ((self_in_CogARMv8Compiler->operands))[2]; + + /* begin concretizeAddCqRDest: */ + constant = ((sqLong) (((self_in_CogARMv8Compiler->operands))[0])); + srcReg = ((self_in_CogARMv8Compiler->operands))[1]; + + /* begin isPossiblyShiftableImm12:ifTrue:ifFalse: */ + if (((constant >= 0) && (constant <= (0xFFF)))) { + /* C6.2.4 ADD (immediate) C6-761 + C6.2.8 ADDS (immediate) C6-769 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((((usqInt)(((srcReg == SP + ? /* ADD */ 0x122 + : /* ADDS */ 354))) << 23))) + ((((usqInt)(constant) << 10)))) + ((((usqInt)(srcReg) << 5)))) + destReg); + return 4; + } + if (((!(constant & (0xFFF)))) + && (((((constant) >> 12) >= 0) && (((constant) >> 12) <= (0xFFF))))) { + /* C6.2.4 ADD (immediate) C6-761 + C6.2.8 ADDS (immediate) C6-769 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((((usqInt)(((srcReg == SP + ? /* ADD */ 0x122 + : /* ADDS */ 354))) << 23))) + ((((usqInt)(constant)) >> 2) + (0x400000))) + ((((usqInt)(srcReg) << 5)))) + destReg); + return 4; + } + /* end isPossiblyShiftableImm12:ifTrue:ifFalse: */ +l3: + + /* begin isPossiblyShiftableImm12:ifTrue:ifFalse: */ + if ((((-constant) >= 0) && ((-constant) <= (0xFFF)))) { + /* C6.2.308 SUB (immediate) C6-1311 + C6.2.314 SUBS (immediate) C6-1321 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((((usqInt)(((srcReg == SP + ? /* SUB */ 418 + : /* SUBS */ 482))) << 23))) + ((((usqInt)((-constant)) << 10)))) + ((((usqInt)(srcReg) << 5)))) + destReg); + return 4; + } + if (((!((-constant) & (0xFFF)))) + && ((((((-constant)) >> 12) >= 0) && ((((-constant)) >> 12) <= (0xFFF))))) { + /* C6.2.308 SUB (immediate) C6-1311 + C6.2.314 SUBS (immediate) C6-1321 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((((usqInt)(((srcReg == SP + ? /* SUB */ 418 + : /* SUBS */ 482))) << 23))) + ((((usqInt)((-constant))) >> 2) + (0x400000))) + ((((usqInt)(srcReg) << 5)))) + destReg); + return 4; + } + /* end isPossiblyShiftableImm12:ifTrue:ifFalse: */ +l4: + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object + cogit processor disassembleInstructionAt: 4 In: machineCode object */ + offset = emitMoveCwintoRat(self_in_CogARMv8Compiler, constant, RISCTempReg, 0); + + /* C6.2.7 ADDS (extended register) C6-766 */ + ((self_in_CogARMv8Compiler->machineCode))[offset / 4] = (((((0xAB200000U) + ((((usqInt)(RISCTempReg) << 16)))) + ((((usqInt)(SXTX) << 13)))) + ((((usqInt)(srcReg) << 5)))) + destReg); + return offset + 4; + + case AndCqR: + return concretizeLogicalOpCqRDest(self_in_CogARMv8Compiler, LogicalAndS, ((self_in_CogARMv8Compiler->operands))[1]); + + case AndCqRR: + return concretizeLogicalOpCqRDest(self_in_CogARMv8Compiler, LogicalAndS, ((self_in_CogARMv8Compiler->operands))[2]); + + case OrCqR: + return concretizeLogicalOpCqRDest(self_in_CogARMv8Compiler, LogicalOr, ((self_in_CogARMv8Compiler->operands))[1]); + + case OrCqRR: + return concretizeLogicalOpCqRDest(self_in_CogARMv8Compiler, LogicalOr, ((self_in_CogARMv8Compiler->operands))[2]); + + case CmpCqR: + /* begin concretizeCmpCqR */ + constant = ((sqLong) (((self_in_CogARMv8Compiler->operands))[0])); + rn = ((self_in_CogARMv8Compiler->operands))[1]; + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + + /* begin isPossiblyShiftableImm12:ifTrue:ifFalse: */ + if (((constant >= 0) && (constant <= (0xFFF)))) { + /* C6.2.61 CMP (immediate) C6-858 + C6.2.314 SUBS (immediate) C6-1321 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0xF1000000U) + ((((usqInt)(constant) << 10)))) + ((((usqInt)(rn) << 5)))) + XZR); + return 4; + } + if (((!(constant & (0xFFF)))) + && (((((constant) >> 12) >= 0) && (((constant) >> 12) <= (0xFFF))))) { + /* C6.2.61 CMP (immediate) C6-858 + C6.2.314 SUBS (immediate) C6-1321 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0xF1000000U) + ((((usqInt)(constant)) >> 2) + (0x400000))) + ((((usqInt)(rn) << 5)))) + XZR); + return 4; + } + /* end isPossiblyShiftableImm12:ifTrue:ifFalse: */ +l5: + + /* begin isPossiblyShiftableImm12:ifTrue:ifFalse: */ + if ((((-constant) >= 0) && ((-constant) <= (0xFFF)))) { + /* C6.2.58 CMN (immediate) C6-852 + C6.2.8 ADDS (immediate) C6-769 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0xB1000000U) + ((((usqInt)((-constant)) << 10)))) + ((((usqInt)(rn) << 5)))) + XZR); + return 4; + } + if (((!((-constant) & (0xFFF)))) + && ((((((-constant)) >> 12) >= 0) && ((((-constant)) >> 12) <= (0xFFF))))) { + /* C6.2.58 CMN (immediate) C6-852 + C6.2.8 ADDS (immediate) C6-769 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0xB1000000U) + ((((usqInt)((-constant))) >> 2) + (0x400000))) + ((((usqInt)(rn) << 5)))) + XZR); + return 4; + } + /* end isPossiblyShiftableImm12:ifTrue:ifFalse: */ +l6: + offset = emitMoveCwintoRat(self_in_CogARMv8Compiler, constant, RISCTempReg, 0); + + /* C6.2.60 CMP (extended register) C6-856 */ + ((self_in_CogARMv8Compiler->machineCode))[offset / 4] = (((((0xEB200000U) + ((((usqInt)(RISCTempReg) << 16)))) + ((((usqInt)(UXTX) << 13)))) + ((((usqInt)(rn) << 5)))) + XZR); + return offset + 4; + + case SubCqR: + /* begin concretizeSubCqR */ + constant = ((sqLong) (((self_in_CogARMv8Compiler->operands))[0])); + reg = ((self_in_CogARMv8Compiler->operands))[1]; + + /* begin isPossiblyShiftableImm12:ifTrue:ifFalse: */ + if (((constant >= 0) && (constant <= (0xFFF)))) { + /* C6.2.308 SUB (immediate) C6-1311 + C6.2.314 SUBS (immediate) C6-1321 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((((usqInt)(((reg == SP + ? 418 + : 482))) << 23))) + ((((usqInt)(constant) << 10)))) + ((((usqInt)(reg) << 5)))) + reg); + return 4; + } + if (((!(constant & (0xFFF)))) + && (((((constant) >> 12) >= 0) && (((constant) >> 12) <= (0xFFF))))) { + /* C6.2.308 SUB (immediate) C6-1311 + C6.2.314 SUBS (immediate) C6-1321 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((((usqInt)(((reg == SP + ? 418 + : 482))) << 23))) + ((((usqInt)(constant)) >> 2) + (0x400000))) + ((((usqInt)(reg) << 5)))) + reg); + return 4; + } + /* end isPossiblyShiftableImm12:ifTrue:ifFalse: */ +l7: + + /* begin isPossiblyShiftableImm12:ifTrue:ifFalse: */ + if ((((-constant) >= 0) && ((-constant) <= (0xFFF)))) { + /* C6.2.4 ADDS (immediate) C6-761 + C6.2.8 ADDS (immediate) C6-769 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((((usqInt)(((reg == SP + ? 0x122 + : 354))) << 23))) + ((((usqInt)((-constant)) << 10)))) + ((((usqInt)(reg) << 5)))) + reg); + return 4; + } + if (((!((-constant) & (0xFFF)))) + && ((((((-constant)) >> 12) >= 0) && ((((-constant)) >> 12) <= (0xFFF))))) { + /* C6.2.4 ADDS (immediate) C6-761 + C6.2.8 ADDS (immediate) C6-769 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((((usqInt)(((reg == SP + ? 0x122 + : 354))) << 23))) + ((((usqInt)((-constant))) >> 2) + (0x400000))) + ((((usqInt)(reg) << 5)))) + reg); + return 4; + } + /* end isPossiblyShiftableImm12:ifTrue:ifFalse: */ +l8: + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object + cogit processor disassembleInstructionAt: 4 In: machineCode object */ + offset = emitMoveCwintoRat(self_in_CogARMv8Compiler, constant, RISCTempReg, 0); + assert(!((SP == reg))); + + /* C6.2.313 SUBS (extended register) C6-1318 */ + ((self_in_CogARMv8Compiler->machineCode))[offset / 4] = (((((0x8B000000U) + ((((usqInt)(ArithmeticSubS) << 29)))) + ((((usqInt)(RISCTempReg) << 16)))) + ((((usqInt)(reg) << 5)))) + reg); + return offset + 4; + + case TstCqR: + return concretizeLogicalOpCqRDest(self_in_CogARMv8Compiler, LogicalAndS, XZR); + + case XorCqR: + return concretizeLogicalOpCqRDest(self_in_CogARMv8Compiler, LogicalXor, ((self_in_CogARMv8Compiler->operands))[1]); + + case AddCwR: + return concretizeCwRArithmeticRd(self_in_CogARMv8Compiler, ArithmeticAddS, ((self_in_CogARMv8Compiler->operands))[1]); + + case AndCwR: + return concretizeCwRLogical(self_in_CogARMv8Compiler, LogicalAndS); + + case CmpCwR: + return concretizeCwRArithmeticRd(self_in_CogARMv8Compiler, ArithmeticSubS, XZR); + + case CmpC32R: + /* begin concretizeCmpC32R */ + constant = ((self_in_CogARMv8Compiler->operands))[0]; + rn = ((self_in_CogARMv8Compiler->operands))[1]; + assert(addressIsInCurrentCompilation((((self_in_CogARMv8Compiler->dependent))->address))); + assert((((((self_in_CogARMv8Compiler->dependent))->address)) % 4) == 0); + assert((SQABS((((usqInt)((((self_in_CogARMv8Compiler->dependent))->address)))) - (((usqInt)((self_in_CogARMv8Compiler->address)))))) < (0x40000)); + + /* C6.2.131 LDR (literal) C6-979 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((0x18000000) + ((((usqInt)((((((self_in_CogARMv8Compiler->dependent))->address)) - ((self_in_CogARMv8Compiler->address)))) << 3)))) + RISCTempReg); + + /* 5 - 2 + C6.2.60 CMP (extended register) C6-856 */ + ((self_in_CogARMv8Compiler->machineCode))[1] = (((((0x6B200000) + ((((usqInt)(RISCTempReg) << 16)))) + ((((usqInt)(UXTX) << 13)))) + ((((usqInt)(rn) << 5)))) + XZR); + + /* N.B. sf == 0!! */ + return 8; + + case OrCwR: + return concretizeCwRLogical(self_in_CogARMv8Compiler, LogicalOr); + + case SubCwR: + return concretizeCwRArithmeticRd(self_in_CogARMv8Compiler, ArithmeticSubS, ((self_in_CogARMv8Compiler->operands))[1]); + + case XorCwR: + return concretizeCwRLogical(self_in_CogARMv8Compiler, LogicalXor); + + case AddRR: + return concretizeRRArithmeticRd(self_in_CogARMv8Compiler, ArithmeticAddS, ((self_in_CogARMv8Compiler->operands))[1]); + + case AndRR: + return concretizeRRLogical(self_in_CogARMv8Compiler, LogicalAndS); + + case CmpRR: + return concretizeRRArithmeticRd(self_in_CogARMv8Compiler, ArithmeticSubS, XZR); + + case OrRR: + return concretizeRRLogical(self_in_CogARMv8Compiler, LogicalOr); + + case SubRR: + return concretizeRRArithmeticRd(self_in_CogARMv8Compiler, ArithmeticSubS, ((self_in_CogARMv8Compiler->operands))[1]); + + case XorRR: + return concretizeRRLogical(self_in_CogARMv8Compiler, LogicalXor); + + case AddRRR: + return concretizeRRArithmeticRd(self_in_CogARMv8Compiler, ArithmeticAddS, ((self_in_CogARMv8Compiler->operands))[2]); + + case SubRRR: + return concretizeRRArithmeticRd(self_in_CogARMv8Compiler, ArithmeticSubS, ((self_in_CogARMv8Compiler->operands))[2]); + + case AddRdRd: + /* begin concretizeMathRdRd: */ + srcReg = ((self_in_CogARMv8Compiler->operands))[0]; + destReg = ((self_in_CogARMv8Compiler->operands))[1]; + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0x1E600000) + ((((usqInt)(srcReg) << 16)))) + (0x2800)) + ((((usqInt)(destReg) << 5)))) + destReg); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case CmpRdRd: + /* begin concretizeCmpRdRd */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0x1E600000) + ((((usqInt)((((self_in_CogARMv8Compiler->operands))[0])) << 16)))) + (0x2000)) + ((((usqInt)((((self_in_CogARMv8Compiler->operands))[1])) << 5)))); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case DivRdRd: + /* begin concretizeMathRdRd: */ + srcReg = ((self_in_CogARMv8Compiler->operands))[0]; + destReg = ((self_in_CogARMv8Compiler->operands))[1]; + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0x1E600000) + ((((usqInt)(srcReg) << 16)))) + (6144)) + ((((usqInt)(destReg) << 5)))) + destReg); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case MulRdRd: + /* begin concretizeMathRdRd: */ + srcReg = ((self_in_CogARMv8Compiler->operands))[0]; + destReg = ((self_in_CogARMv8Compiler->operands))[1]; + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0x1E600000) + ((((usqInt)(srcReg) << 16)))) + (0x800)) + ((((usqInt)(destReg) << 5)))) + destReg); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case SubRdRd: + /* begin concretizeMathRdRd: */ + srcReg = ((self_in_CogARMv8Compiler->operands))[0]; + destReg = ((self_in_CogARMv8Compiler->operands))[1]; + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0x1E600000) + ((((usqInt)(srcReg) << 16)))) + (14336)) + ((((usqInt)(destReg) << 5)))) + destReg); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case XorRdRd: + /* begin concretizeXorRdRd */ + srcReg = ((self_in_CogARMv8Compiler->operands))[0]; + destReg = ((self_in_CogARMv8Compiler->operands))[1]; + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0x2E200000) + ((((usqInt)(srcReg) << 16)))) + (7168)) + ((((usqInt)(destReg) << 5)))) + destReg); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case SqrtRd: + /* begin concretizeSqrtRd */ + reg = ((self_in_CogARMv8Compiler->operands))[0]; + ((self_in_CogARMv8Compiler->machineCode))[0] = (((509722624) + ((((usqInt)(reg) << 5)))) + reg); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case NegateR: + /* begin concretizeNegateR */ + reg = ((self_in_CogARMv8Compiler->operands))[0]; + assert(!((SP == reg))); + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0xCB000000U) + ((((usqInt)(reg) << 16)))) + ((((usqInt)(XZR) << 5)))) + reg); + return 4; + + case LoadEffectiveAddressMwrR: + return concretizeLoadEffectiveAddressMwrR(self_in_CogARMv8Compiler); + + case ArithmeticShiftRightCqR: + /* begin concretizeArithmeticShiftRightCqR */ + constant = ((self_in_CogARMv8Compiler->operands))[0]; + reg = ((self_in_CogARMv8Compiler->operands))[1]; + assert(((constant >= 1) && (constant <= 0x3F))); + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0x93400000U) + ((((usqInt)(constant) << 16)))) + (0xFC00)) + ((((usqInt)(reg) << 5)))) + reg); + return 4; + + case ArithmeticShiftRightCqRR: + /* begin concretizeArithmeticShiftRightCqRR */ + constant = ((self_in_CogARMv8Compiler->operands))[0]; + srcReg = ((self_in_CogARMv8Compiler->operands))[1]; + destReg = ((self_in_CogARMv8Compiler->operands))[2]; + assert(((constant >= 1) && (constant <= 0x3F))); + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0x93400000U) + ((((usqInt)(constant) << 16)))) + (0xFC00)) + ((((usqInt)(srcReg) << 5)))) + destReg); + return 4; + + case LogicalShiftRightCqR: + /* begin concretizeLogicalShiftRightCqR */ + constant = ((self_in_CogARMv8Compiler->operands))[0]; + reg = ((self_in_CogARMv8Compiler->operands))[1]; + assert(((constant >= 1) && (constant <= 0x3F))); + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0xD3400000U) + ((((usqInt)(constant) << 16)))) + (0xFC00)) + ((((usqInt)(reg) << 5)))) + reg); + return 4; + + case LogicalShiftRightCqRR: + /* begin concretizeLogicalShiftRightCqRR */ + constant = ((self_in_CogARMv8Compiler->operands))[0]; + srcReg = ((self_in_CogARMv8Compiler->operands))[1]; + destReg = ((self_in_CogARMv8Compiler->operands))[2]; + assert(((constant >= 1) && (constant <= 0x3F))); + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0xD3400000U) + ((((usqInt)(constant) << 16)))) + (0xFC00)) + ((((usqInt)(srcReg) << 5)))) + destReg); + return 4; + + case LogicalShiftLeftCqR: + /* begin concretizeLogicalShiftLeftCqR */ + constant = ((self_in_CogARMv8Compiler->operands))[0]; + reg = ((self_in_CogARMv8Compiler->operands))[1]; + assert(((constant >= 1) && (constant <= 0x3F))); + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0xD3400000U) + ((((usqInt)((64 - constant)) << 16)))) + ((((usqInt)((0x3F - constant)) << 10)))) + ((((usqInt)(reg) << 5)))) + reg); + return 4; + + case LogicalShiftLeftCqRR: + /* begin concretizeLogicalShiftLeftCqRR */ + constant = ((self_in_CogARMv8Compiler->operands))[0]; + srcReg = ((self_in_CogARMv8Compiler->operands))[1]; + destReg = ((self_in_CogARMv8Compiler->operands))[2]; + assert(((constant >= 1) && (constant <= 0x3F))); + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0xD3400000U) + ((((usqInt)((64 - constant)) << 16)))) + ((((usqInt)((0x3F - constant)) << 10)))) + ((((usqInt)(srcReg) << 5)))) + destReg); + return 4; + + case ArithmeticShiftRightRR: + /* begin concretizeShiftRR: */ + shiftCountReg = ((self_in_CogARMv8Compiler->operands))[0]; + shiftedReg = ((self_in_CogARMv8Compiler->operands))[1]; + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0x9AC00000U) + ((((usqInt)(shiftCountReg) << 16)))) + (0x2800)) + ((((usqInt)(shiftedReg) << 5)))) + shiftedReg); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case LogicalShiftLeftRR: + /* begin concretizeShiftRR: */ + shiftCountReg = ((self_in_CogARMv8Compiler->operands))[0]; + shiftedReg = ((self_in_CogARMv8Compiler->operands))[1]; + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0x9AC00000U) + ((((usqInt)(shiftCountReg) << 16)))) + (0x2000)) + ((((usqInt)(shiftedReg) << 5)))) + shiftedReg); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case LogicalShiftRightRR: + /* begin concretizeShiftRR: */ + shiftCountReg = ((self_in_CogARMv8Compiler->operands))[0]; + shiftedReg = ((self_in_CogARMv8Compiler->operands))[1]; + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0x9AC00000U) + ((((usqInt)(shiftCountReg) << 16)))) + (9216)) + ((((usqInt)(shiftedReg) << 5)))) + shiftedReg); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case RotateRightCqR: + case RotateLeftCqR: + /* begin concretizeRotateCqR */ + constant = ((self_in_CogARMv8Compiler->operands))[0]; + reg = ((self_in_CogARMv8Compiler->operands))[1]; + assert(((constant >= 1) && (constant <= 0x3F))); + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0x93C00000U) + ((((usqInt)(reg) << 16)))) + ((((usqInt)(((RotateRightCqR == ((self_in_CogARMv8Compiler->opcode)) + ? constant + : 64 - constant))) << 10)))) + ((((usqInt)(reg) << 5)))) + reg); + + /* + (1 << 10) + cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case ClzRR: + /* begin concretizeClzRR */ + maskReg = ((self_in_CogARMv8Compiler->operands))[0]; + dest = ((self_in_CogARMv8Compiler->operands))[1]; + ((self_in_CogARMv8Compiler->machineCode))[0] = (((3670020096U) + ((((usqInt)(maskReg) << 5)))) + dest); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case MulRRR: + /* begin concretizeMulRRR */ + reg1 = ((self_in_CogARMv8Compiler->operands))[0]; + reg2 = ((self_in_CogARMv8Compiler->operands))[1]; + reg3 = ((self_in_CogARMv8Compiler->operands))[2]; + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0x9B000000U) + ((((usqInt)(reg1) << 16)))) + ((((usqInt)(XZR) << 10)))) + ((((usqInt)(reg2) << 5)))) + reg3); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case MulOverflowRRR: + /* begin concretizeMulOverflowRRR */ + reg1 = ((self_in_CogARMv8Compiler->operands))[0]; + reg2 = ((self_in_CogARMv8Compiler->operands))[1]; + reg3 = ((self_in_CogARMv8Compiler->operands))[2]; + + /* RISCTempReg := high(reg1 * reg2); must precede destructive MUL */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0x9B400000U) + ((((usqInt)(reg1) << 16)))) + ((((usqInt)(XZR) << 10)))) + ((((usqInt)(reg2) << 5)))) + RISCTempReg); + + /* reg3 := reg1 * reg2 */ + ((self_in_CogARMv8Compiler->machineCode))[1] = (((((0x9B000000U) + ((((usqInt)(reg1) << 16)))) + ((((usqInt)(XZR) << 10)))) + ((((usqInt)(reg2) << 5)))) + reg3); + + /* CArg1Reg := sign(reg3) */ + ((self_in_CogARMv8Compiler->machineCode))[2] = (((0xD37FFC00U) + ((((usqInt)(reg3) << 5)))) + CArg1Reg); + return 12; + + case DivRRR: + /* begin concretizeDivRRR */ + reg1 = ((self_in_CogARMv8Compiler->operands))[0]; + reg2 = ((self_in_CogARMv8Compiler->operands))[1]; + reg3 = ((self_in_CogARMv8Compiler->operands))[2]; + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0x9AC00000U) + ((((usqInt)(reg2) << 16)))) + (0xC00)) + ((((usqInt)(reg1) << 5)))) + reg3); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case MSubRRR: + /* begin concretizeMSubRRR */ + rM = ((usqInt)((((self_in_CogARMv8Compiler->operands))[0]))) >> 5; + rN = (((self_in_CogARMv8Compiler->operands))[0]) & 0x1F; + rA = ((self_in_CogARMv8Compiler->operands))[1]; + rD = ((self_in_CogARMv8Compiler->operands))[2]; + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((((0x9B000000U) + ((((usqInt)(rM) << 16)))) + (0x8000)) + ((((usqInt)(rA) << 10)))) + ((rN << 5))) + rD); + return 4; + + case DC: + return concretizeDataCacheControl(self_in_CogARMv8Compiler); + + case DSB: + /* begin concretizeDataSynchronizationBarrier */ + CRm = ((((usqInt)((((self_in_CogARMv8Compiler->operands))[0])) << 2))) + (((self_in_CogARMv8Compiler->operands))[1]); + ((self_in_CogARMv8Compiler->machineCode))[0] = (0xD503309FU + ((((usqInt)(CRm) << 8)))); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case IC: + /* begin concretizeInstructionCacheControl */ + /* begin concretizeCacheControlOp1:CRm:Op2: */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((3574297888U) + (((self_in_CogARMv8Compiler->operands))[0])); + + /* CRn + cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case ISB: + /* begin concretizeInstructionSynchronizationBarrier */ + CRm = 15; + ((self_in_CogARMv8Compiler->machineCode))[0] = (0xD50330DFU + ((((usqInt)(CRm) << 8)))); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case MRS_CTR_EL0: + /* begin concretizeMRS_CTR_EL0 */ + /* begin concretizeMRSOp1:CRn:CRm:Op2: */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((3577413664U) + (((self_in_CogARMv8Compiler->operands))[0])); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case MRS_ID_AA64ISAR0_EL1: + /* begin concretizeMRS_ID_AA64ISAR0_EL1 */ + /* begin concretizeMRSOp1:CRn:CRm:Op2: */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((3577218560U) + (((self_in_CogARMv8Compiler->operands))[0])); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case MoveCqR: + return concretizeMoveCqR(self_in_CogARMv8Compiler); + + case MoveCwR: + return emitMoveCwintoRat(self_in_CogARMv8Compiler, ((self_in_CogARMv8Compiler->operands))[0], ((self_in_CogARMv8Compiler->operands))[1], 0); + + case MoveC32R: + /* begin concretizeMoveC32R */ + constant = ((self_in_CogARMv8Compiler->operands))[0]; + rn = ((self_in_CogARMv8Compiler->operands))[1]; + assert(addressIsInCurrentCompilation((((self_in_CogARMv8Compiler->dependent))->address))); + assert((((((self_in_CogARMv8Compiler->dependent))->address)) % 4) == 0); + assert((((((self_in_CogARMv8Compiler->dependent))->address)) - ((self_in_CogARMv8Compiler->address))) < (0x100000)); + + /* C6.2.143 LDRSW (literal) C6-1008 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((0x98000000U) + ((((usqInt)(((((((self_in_CogARMv8Compiler->dependent))->address)) - ((self_in_CogARMv8Compiler->address))) & (0x1FFFFF))) << 3)))) + rn); + + /* 5 - 2 */ + return 4; + + case MoveRR: + /* begin concretizeMoveRR */ + srcReg = ((self_in_CogARMv8Compiler->operands))[0]; + destReg = ((self_in_CogARMv8Compiler->operands))[1]; + + /* C6.2.184 MOV (to/from SP) p1089 + C6.2.188 MOV (register) p1096 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((srcReg == SP) + || (destReg == SP) + ? ((0x91000000U) + ((((usqInt)(srcReg) << 5)))) + destReg + : (((0xAA000000U) + ((((usqInt)(srcReg) << 16)))) + ((((usqInt)(XZR) << 5)))) + destReg)); + return 4; + + case MoveRRd: + /* begin concretizeMoveRRd */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((0x9E670000U) + ((((usqInt)((((self_in_CogARMv8Compiler->operands))[0])) << 5)))) + (((self_in_CogARMv8Compiler->operands))[1])); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case MoveRdR: + /* begin concretizeMoveRdR */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((0x9E660000U) + ((((usqInt)((((self_in_CogARMv8Compiler->operands))[0])) << 5)))) + (((self_in_CogARMv8Compiler->operands))[1])); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case MoveAwR: + /* begin concretizeMoveAwR */ + srcAddr = ((self_in_CogARMv8Compiler->operands))[0]; + destReg = ((self_in_CogARMv8Compiler->operands))[1]; + + /* ldr srcReg, [VarBaseReg, #offset] except that this is illegal for SP/X31 */ + if (((srcAddr >= ((varBaseAddress) - (0x100))) && (srcAddr <= (((varBaseAddress) + (0x1000)) - 1)))) { + if (destReg != SP) { + return emitLdrnrtimmshiftBy12at(self_in_CogARMv8Compiler, 3, VarBaseReg, destReg, srcAddr - (varBaseAddress), 0, 0); + } + instrOffset = emitLdrnrtimmshiftBy12at(self_in_CogARMv8Compiler, 3, VarBaseReg, RISCTempReg, srcAddr - (varBaseAddress), 0, 0); + ((self_in_CogARMv8Compiler->machineCode))[instrOffset / 4] = (movernrd(self_in_CogARMv8Compiler, RISCTempReg, destReg)); + return instrOffset + 4; + } + instrOffset = emitMoveCwintoRat(self_in_CogARMv8Compiler, srcAddr, RISCTempReg, 0); + if (SP != destReg) { + return emitLdrnrtimmshiftBy12at(self_in_CogARMv8Compiler, 3, RISCTempReg, destReg, 0, 0, instrOffset); + } + instrOffset = emitLdrnrtimmshiftBy12at(self_in_CogARMv8Compiler, 3, RISCTempReg, RISCTempReg, 0, 0, instrOffset); + ((self_in_CogARMv8Compiler->machineCode))[instrOffset / 4] = (movernrd(self_in_CogARMv8Compiler, RISCTempReg, destReg)); + return instrOffset + 4; + + case MoveRAw: + /* begin concretizeMoveRAw */ + srcReg = ((self_in_CogARMv8Compiler->operands))[0]; + destAddr = ((self_in_CogARMv8Compiler->operands))[1]; + + /* str srcReg, [VarBaseReg, #offset] except that this is illegal for srcReg = SP/X31 */ + if (((destAddr >= ((varBaseAddress) - (0x100))) && (destAddr <= (((varBaseAddress) + (0x1000)) - 1)))) { + if (srcReg != SP) { + ((self_in_CogARMv8Compiler->machineCode))[0] = (strnrtimmshiftBy12(self_in_CogARMv8Compiler, 3, VarBaseReg, srcReg, destAddr - (varBaseAddress), 0)); + return 4; + } + ((self_in_CogARMv8Compiler->machineCode))[0] = (movernrd(self_in_CogARMv8Compiler, srcReg, RISCTempReg)); + ((self_in_CogARMv8Compiler->machineCode))[1] = (strnrtimmshiftBy12(self_in_CogARMv8Compiler, 3, VarBaseReg, RISCTempReg, destAddr - (varBaseAddress), 0)); + return 8; + } + instrOffset = emitMoveCwintoRat(self_in_CogARMv8Compiler, destAddr, RISCTempReg, 0); + if (SP != srcReg) { + ((self_in_CogARMv8Compiler->machineCode))[instrOffset / 4] = (strnrtimmshiftBy12(self_in_CogARMv8Compiler, 3, RISCTempReg, srcReg, 0, 0)); + return instrOffset + 4; + } + ((self_in_CogARMv8Compiler->machineCode))[instrOffset / 4] = (movernrd(self_in_CogARMv8Compiler, srcReg, CArg1Reg)); + ((self_in_CogARMv8Compiler->machineCode))[(instrOffset / 4) + 1] = (strnrtimmshiftBy12(self_in_CogARMv8Compiler, 3, RISCTempReg, CArg1Reg, 0, 0)); + return instrOffset + 8; + + case MoveAwRR: + /* begin concretizeMoveAwRR */ + pairAddress = ((self_in_CogARMv8Compiler->operands))[0]; + reg1 = ((self_in_CogARMv8Compiler->operands))[1]; + reg2 = ((self_in_CogARMv8Compiler->operands))[2]; + assert(reg1 != reg2); + assert((reg1 != RISCTempReg) + && (reg2 != RISCTempReg)); + assert(isAddressRelativeToVarBase(self_in_CogARMv8Compiler, pairAddress)); + if (!(asserta(((pairAddress - (varBaseAddress)) / 8) < (64)))) { + error("unhandled immediate offset in concretizeMoveAwRR"); + } + if (reg1 == SP) { + reg1 = RISCTempReg; + } + if (reg2 == SP) { + reg2 = RISCTempReg; + } + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0xA9400000U) + ((((usqInt)(((pairAddress - (varBaseAddress)) / 8)) << 15)))) + ((((usqInt)(reg2) << 10)))) + ((((usqInt)(VarBaseReg) << 5)))) + reg1); + if ((reg1 != RISCTempReg) + && (reg2 != RISCTempReg)) { + return 4; + } + ((self_in_CogARMv8Compiler->machineCode))[1] = (movernrd(self_in_CogARMv8Compiler, RISCTempReg, SP)); + return 8; + + case MoveRRAw: + /* begin concretizeMoveRRAw */ + reg1 = ((self_in_CogARMv8Compiler->operands))[0]; + reg2 = ((self_in_CogARMv8Compiler->operands))[1]; + pairAddress = ((self_in_CogARMv8Compiler->operands))[2]; + assert(reg1 != reg2); + assert((reg1 != RISCTempReg) + && (reg2 != RISCTempReg)); + assert(isAddressRelativeToVarBase(self_in_CogARMv8Compiler, pairAddress)); + if (!(asserta(((pairAddress - (varBaseAddress)) / 8) < (64)))) { + error("unhandled immediate offset in concretizeMoveRRAw"); + } + if (reg1 == SP) { + reg1 = RISCTempReg; + } + if (reg2 == SP) { + reg2 = RISCTempReg; + } + if ((reg1 == RISCTempReg) + || (reg2 == RISCTempReg)) { + ((self_in_CogARMv8Compiler->machineCode))[0] = (movernrd(self_in_CogARMv8Compiler, SP, RISCTempReg)); + offset = 1; + } + else { + offset = 0; + } + ((self_in_CogARMv8Compiler->machineCode))[offset] = (((((0xA9000000U) + ((((usqInt)(((pairAddress - (varBaseAddress)) / 8)) << 15)))) + ((((usqInt)(reg2) << 10)))) + ((((usqInt)(VarBaseReg) << 5)))) + reg1); + + /* cogit processor + disassembleInstructionAt: 0 In: machineCode object; + disassembleInstructionAt: 4 In: machineCode object */ + return (offset + 1) * 4; + + case MoveAbR: + /* begin concretizeMoveAbR */ + srcAddr = ((self_in_CogARMv8Compiler->operands))[0]; + destReg = ((self_in_CogARMv8Compiler->operands))[1]; + assert(!((SP == destReg))); + + /* ldr srcReg, [VarBaseReg, #offset] except that this is illegal for SP/X31 */ + if (((srcAddr >= ((varBaseAddress) - (0x100))) && (srcAddr <= (((varBaseAddress) + (0x1000)) - 1)))) { + return emitLdrnrtimmshiftBy12at(self_in_CogARMv8Compiler, 0, VarBaseReg, destReg, srcAddr - (varBaseAddress), 0, 0); + } + instrOffset = emitMoveCwintoRat(self_in_CogARMv8Compiler, srcAddr, RISCTempReg, 0); + return emitLdrnrtimmshiftBy12at(self_in_CogARMv8Compiler, 0, RISCTempReg, destReg, 0, 0, instrOffset); + + case MoveRAb: + /* begin concretizeMoveRAb */ + srcReg = ((self_in_CogARMv8Compiler->operands))[0]; + destAddr = ((self_in_CogARMv8Compiler->operands))[1]; + assert(!((SP == srcReg))); + if (((destAddr >= ((varBaseAddress) - (0x100))) && (destAddr <= (((varBaseAddress) + (0x1000)) - 1)))) { + ((self_in_CogARMv8Compiler->machineCode))[0] = (strnrtimmshiftBy12(self_in_CogARMv8Compiler, 0, VarBaseReg, srcReg, destAddr - (varBaseAddress), 0)); + return 4; + } + instrOffset = emitMoveCwintoRat(self_in_CogARMv8Compiler, destAddr, RISCTempReg, 0); + ((self_in_CogARMv8Compiler->machineCode))[instrOffset / 4] = (strnrtimmshiftBy12(self_in_CogARMv8Compiler, 0, RISCTempReg, srcReg, 0, 0)); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return instrOffset + 4; + + case MoveMwrR: + return concretizeMoveMSrR(self_in_CogARMv8Compiler, 3); + + case MoveM32rR: + return concretizeMoveMSrR(self_in_CogARMv8Compiler, 2); + + case MoveM16rR: + return concretizeMoveMSrR(self_in_CogARMv8Compiler, 1); + + case MoveMbrR: + return concretizeMoveMSrR(self_in_CogARMv8Compiler, 0); + + case MoveRMwr: + return concretizeMoveRMSr(self_in_CogARMv8Compiler, 3); + + case MoveRM32r: + return concretizeMoveRMSr(self_in_CogARMv8Compiler, 2); + + case MoveRM16r: + return concretizeMoveRMSr(self_in_CogARMv8Compiler, 1); + + case MoveRMbr: + return concretizeMoveRMSr(self_in_CogARMv8Compiler, 0); + + case MoveXwrRR: + return concretizeMoveXSrRR(self_in_CogARMv8Compiler, 3); + + case MoveX32rRR: + return concretizeMoveXSrRR(self_in_CogARMv8Compiler, 2); + + case MoveX16rRR: + return concretizeMoveXSrRR(self_in_CogARMv8Compiler, 1); + + case MoveXbrRR: + return concretizeMoveXSrRR(self_in_CogARMv8Compiler, 0); + + case MoveRXwrR: + return concretizeMoveRXSrR(self_in_CogARMv8Compiler, 3); + + case MoveRX32rR: + return concretizeMoveRXSrR(self_in_CogARMv8Compiler, 2); + + case MoveRX16rR: + return concretizeMoveRXSrR(self_in_CogARMv8Compiler, 1); + + case MoveRXbrR: + return concretizeMoveRXSrR(self_in_CogARMv8Compiler, 0); + + case MoveM64rRd: + /* begin concretizeMoveM64rRd */ + offset = ((self_in_CogARMv8Compiler->operands))[0]; + baseReg = ((self_in_CogARMv8Compiler->operands))[1]; + rd = ((self_in_CogARMv8Compiler->operands))[2]; + assert((!(offset & 7))); + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0xFD400000U) + ((((usqInt)(offset) << 7)))) + ((((usqInt)(baseReg) << 5)))) + rd); + + /* 10 - 3 + cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case MoveRdM64r: + /* begin concretizeMoveRdM64r */ + rd = ((self_in_CogARMv8Compiler->operands))[0]; + offset = ((self_in_CogARMv8Compiler->operands))[1]; + baseReg = ((self_in_CogARMv8Compiler->operands))[2]; + assert((!(offset & 7))); + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0xFD000000U) + ((((usqInt)(offset) << 7)))) + ((((usqInt)(baseReg) << 5)))) + rd); + + /* 10 - 3 + cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case PopR: + /* begin concretizePopR */ + reg = ((self_in_CogARMv8Compiler->operands))[0]; + assert(!((SP == reg))); + ((self_in_CogARMv8Compiler->machineCode))[0] = (((4164977664U) + ((((usqInt)(SPReg) << 5)))) + reg); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case PushR: + /* begin concretizePushR */ + reg = ((self_in_CogARMv8Compiler->operands))[0]; + assert(!((SP == reg))); + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0xF8000000U) + ((((usqInt)((-8 & (0x1FF))) << 12)))) + (0xC00)) + ((((usqInt)(SPReg) << 5)))) + reg); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case NativePopRR: + /* begin concretizeNativePopRR */ + reg1 = ((self_in_CogARMv8Compiler->operands))[0]; + reg2 = ((self_in_CogARMv8Compiler->operands))[1]; + + /* Post-index */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0xA8C10000U) + ((((usqInt)(reg2) << 10)))) + ((((usqInt)(SP) << 5)))) + reg1); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case NativePushRR: + /* begin concretizeNativePushRR */ + reg1 = ((self_in_CogARMv8Compiler->operands))[0]; + reg2 = ((self_in_CogARMv8Compiler->operands))[1]; + + /* Pre-index */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0xA9BF0000U) + ((((usqInt)(reg2) << 10)))) + ((((usqInt)(SP) << 5)))) + reg1); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case PushCq: + /* begin concretizePushCq */ + ((self_in_CogARMv8Compiler->operands))[1] = RISCTempReg; + instrBytes = concretizeMoveCqR(self_in_CogARMv8Compiler); + + /* C6.2.273 STR (immediate) Pre-index C6-1239 */ + ((self_in_CogARMv8Compiler->machineCode))[instrBytes / 4] = (((((0xF8000000U) + ((((usqInt)((-8 & (0x1FF))) << 12)))) + (0xC00)) + ((((usqInt)(SPReg) << 5)))) + RISCTempReg); + return instrBytes + 4; + + case PushCw: + /* begin concretizePushCw */ + instrBytes = emitMoveCwintoRat(self_in_CogARMv8Compiler, ((self_in_CogARMv8Compiler->operands))[0], RISCTempReg, 0); + + /* C6.2.273 STR (immediate) Pre-index C6-1239 */ + ((self_in_CogARMv8Compiler->machineCode))[instrBytes / 4] = (((((0xF8000000U) + ((((usqInt)((-8 & (0x1FF))) << 12)))) + (0xC00)) + ((((usqInt)(SPReg) << 5)))) + RISCTempReg); + return instrBytes + 4; + + case PrefetchAw: + /* begin concretizePrefetchAw */ + srcAddr = ((self_in_CogARMv8Compiler->operands))[0]; + if (((srcAddr >= ((varBaseAddress) - (0x100))) && (srcAddr <= (((varBaseAddress) + (0x1000)) - 1)))) { + if (srcAddr < (varBaseAddress)) { + error("shouldBeImplemented"); + return 4; + } + ((self_in_CogARMv8Compiler->machineCode))[0] = (prnimmshiftBy12(self_in_CogARMv8Compiler, VarBaseReg, srcAddr - (varBaseAddress), 0)); + return 4; + } + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + asserta((emitMoveCwintoRat(self_in_CogARMv8Compiler, srcAddr, RISCTempReg, 0)) == 4); + ((self_in_CogARMv8Compiler->machineCode))[1] = (((0xF8800000U) + ((((usqInt)(RISCTempReg) << 5))))); + return 8; + + case ConvertRRd: + /* begin concretizeConvertRRd */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((0x9E620000U) + ((((usqInt)((((self_in_CogARMv8Compiler->operands))[0])) << 5)))) + (((self_in_CogARMv8Compiler->operands))[1])); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case ConvertRdR: + /* begin concretizeConvertRdR */ + ((self_in_CogARMv8Compiler->machineCode))[0] = (((0x9E780000U) + ((((usqInt)((((self_in_CogARMv8Compiler->operands))[0])) << 5)))) + (((self_in_CogARMv8Compiler->operands))[1])); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case SignExtend8RR: + return concretizeSignExtendRR(self_in_CogARMv8Compiler, 8); + + case SignExtend16RR: + return concretizeSignExtendRR(self_in_CogARMv8Compiler, 16); + + case SignExtend32RR: + return concretizeSignExtendRR(self_in_CogARMv8Compiler, 32); + + case ZeroExtend8RR: + return concretizeZeroExtendRR(self_in_CogARMv8Compiler, 8); + + case ZeroExtend16RR: + return concretizeZeroExtendRR(self_in_CogARMv8Compiler, 16); + + case ZeroExtend32RR: + return concretizeZeroExtendRR(self_in_CogARMv8Compiler, 32); + + case LDAXR: + /* begin concretizeLDAXR */ + targetReg = ((self_in_CogARMv8Compiler->operands))[0]; + addressReg = ((self_in_CogARMv8Compiler->operands))[1]; + ((self_in_CogARMv8Compiler->machineCode))[0] = (((3361733632U) + ((((usqInt)(addressReg) << 5)))) + targetReg); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case STLXR: + /* begin concretizeSTLXR */ + valueReg = ((self_in_CogARMv8Compiler->operands))[0]; + addressReg = ((self_in_CogARMv8Compiler->operands))[1]; + statusReg = ((self_in_CogARMv8Compiler->operands))[2]; + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0xC8000000U) + ((((usqInt)(statusReg) << 16)))) + (0xFC00)) + ((((usqInt)(addressReg) << 5)))) + valueReg); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case CLREX: + /* begin concretizeCLREX */ + ((self_in_CogARMv8Compiler->machineCode))[0] = 0xD5033F5FU; + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case STLR: + return concretizeSTLR(self_in_CogARMv8Compiler); + + case CASAL: + /* begin concretizeCASAL */ + constantReg = ((self_in_CogARMv8Compiler->operands))[0]; + valueReg = ((self_in_CogARMv8Compiler->operands))[1]; + baseReg = ((self_in_CogARMv8Compiler->operands))[2]; + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0xC8E00000U) + ((((usqInt)(constantReg) << 16)))) + (0xFC00)) + ((((usqInt)(baseReg) << 5)))) + valueReg); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case CCMPNE: + /* begin concretizeCCMPNE */ + rM = ((self_in_CogARMv8Compiler->operands))[0]; + rNSqInt = ((self_in_CogARMv8Compiler->operands))[1]; + flags = ((self_in_CogARMv8Compiler->operands))[2]; + ((self_in_CogARMv8Compiler->machineCode))[0] = (((((0xFA400000U) + ((((usqInt)(rM) << 16)))) + ((((usqInt)(NE) << 12)))) + ((((usqInt)(rNSqInt) << 5)))) + flags); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case CSET: + /* begin concretizeCSET */ + reg = ((self_in_CogARMv8Compiler->operands))[0]; + condition = ((self_in_CogARMv8Compiler->operands))[1]; + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0x9A9F0000U) + ((((usqInt)((condition ^ 1)) << 12)))) + (0x7E0)) + reg); + + /* strange, but true + cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case CBNZ: + case CBZ: + /* begin concretizeCB */ + /* begin computeJumpTargetOffset */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogARMv8Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset = (((sqLong) jumpTarget)) - (((sqLong) ((self_in_CogARMv8Compiler->address)))); + assert((offset != 0) + && (isInImmediateBranchRange(self_in_CogARMv8Compiler, offset))); + reg = ((self_in_CogARMv8Compiler->operands))[1]; + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0xB4000000U) + ((((self_in_CogARMv8Compiler->opcode)) == CBNZ + ? 0x1000000 + : 0))) + ((((usqInt)((offset & (0x1FFFFF))) << 3)))) + reg); + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object */ + return 4; + + case MovePerfCnt64RL: + return concretizeMovePerfCnt64RL(self_in_CogARMv8Compiler); + + default: + error("Case not found and no otherwise clause"); + } + + /* Noops & Pseudo Ops + Control + call code within code space + call code anywhere in address space + jump within address space + jump within code space + jump within a method, etc + unsigned lower + unsigned greater or equal + Arithmetic + ARM Specific Arithmetic + ARM Specific Cache Control + Data Movement + Conversion + [ConvertRRs] -> [^self concretizeConvertRRs]. + [ConvertRsR] -> [^self concretizeConvertRsR]. + [ConvertRsRd] -> [^self concretizeConvertRsRd]. + [ConvertRdRs] -> [^self concretizeConvertRdRs]. + Multi-processing + miscellaneous */ + return 0; +} + + +/* C7.2.184 LDR (immediate, SIMD&FP) C7-1800 + C7.2.186 LDR (register, SIMD&FP) C7-1806 */ +/* cogit processor disassembleInstructionAt: instrOffset In: machineCode + object + */ +/* Unsigned offset, C7-1801 */ + + /* CogARMv8Compiler>>#emitLdfprn:rt:imm:shiftBy12:at: */ +static NoDbgRegParms sqInt +emitLdfprnrtimmshiftBy12at(AbstractInstruction *self_in_CogARMv8Compiler, sqInt baseReg, sqInt targetDPReg, sqInt offset, sqInt shiftBy12, sqInt instrOffset) +{ + sqInt instrBytes; + + if (((offset % 8) == 0) + && ((((offset / 8) >= 0) && ((offset / 8) <= (0xFFF))))) { + ((self_in_CogARMv8Compiler->machineCode))[instrOffset / 4] = ((((0xFD400000U) + ((((usqInt)(offset) << 7)))) + ((((usqInt)(baseReg) << 5)))) + targetDPReg); + + /* 10 - 3 */ + return instrOffset + 4; + } + + /* No unscaled signed 9-bit offset because there is only post-index and pre-index, see C7-1800 + C6.2.132 LDR (register) C6-981 */ + instrBytes = emitMoveCwintoRat(self_in_CogARMv8Compiler, offset, RISCTempReg, instrOffset); + + /* C7.2.186 LDR (register, SIMD&FP) C7-1806 */ + ((self_in_CogARMv8Compiler->machineCode))[instrBytes / 4] = ((((((0xFC600000U) + ((((usqInt)(RISCTempReg) << 16)))) + ((((usqInt)(UXTX) << 13)))) + (0x800)) + ((((usqInt)(baseReg) << 5)))) + targetDPReg); + return instrBytes + 4; +} + + +/* C6.2.130 LDR (immediate) C6-976 + C6.2.132 LDR (register) C6-981 + C6.2.136 LDRH (immediate) C6-990 + C6.2.166 LDUR C6-1058 + C6.2.134 LDRB (immediate) C6-985 + C6.2.135 LDRB (register) C6-988 */ +/* cogit processor disassembleInstructionAt: instrOffset In: machineCode + object + */ + + /* CogARMv8Compiler>>#emitLd:rn:rt:imm:shiftBy12:at: */ +static NoDbgRegParms sqInt +emitLdrnrtimmshiftBy12at(AbstractInstruction *self_in_CogARMv8Compiler, sqInt unitSizeLog2MinusOne, sqInt baseReg, sqInt targetReg, sqInt offset, sqInt shiftBy12, sqInt instrOffset) +{ + sqInt instrBytes; + sqInt unitSize; + + unitSize = 1ULL << unitSizeLog2MinusOne; + + /* Unsigned offset, C6-977 */ + if (((offset % unitSize) == 0) + && ((((offset / unitSize) >= 0) && ((offset / unitSize) <= (0xFFF))))) { + ((self_in_CogARMv8Compiler->machineCode))[instrOffset / 4] = ((((((((usqInt)(unitSizeLog2MinusOne) << 30))) + (0x39400000)) + ((((usqInt)(offset) << (10 - unitSizeLog2MinusOne))))) + ((((usqInt)(baseReg) << 5)))) + targetReg); + return instrOffset + 4; + } + if (((offset >= -256) && (offset <= 0xFF))) { + ((self_in_CogARMv8Compiler->machineCode))[instrOffset / 4] = ((((((((usqInt)(unitSizeLog2MinusOne) << 30))) + (0x38400000)) + (((offset & 0x1FF) << 12))) + ((((usqInt)(baseReg) << 5)))) + targetReg); + return instrOffset + 4; + } + + /* Unscaled signed 9-bit offset, C6-1058 + C6.2.132 LDR (register) C6-981 */ + instrBytes = emitMoveCwintoRat(self_in_CogARMv8Compiler, offset, RISCTempReg, instrOffset); + ((self_in_CogARMv8Compiler->machineCode))[instrBytes / 4] = ((((((((((usqInt)(unitSizeLog2MinusOne) << 30))) + (0x38600000)) + ((((usqInt)(RISCTempReg) << 16)))) + ((((usqInt)(UXTX) << 13)))) + (0x800)) + ((((usqInt)(baseReg) << 5)))) + targetReg); + return instrBytes + 4; +} + + +/* Emit a load of constant into destReg. Answer the number of bytes of + machine code + generated. Literals are stored out-of-line; emit a LDR (literal) with the + relevant offset. */ + + /* CogARMv8Compiler>>#emitMoveCw:intoR:at: */ +static NoDbgRegParms sqInt +emitMoveCwintoRat(AbstractInstruction *self_in_CogARMv8Compiler, usqInt constantArg, sqInt destReg, sqInt offsetBytes) +{ + sqInt aligned; + usqInt constant; + sqInt pcRelativeOffset; + usqInt unaligned; + + assert(!((destReg == SP))); + + /* cogit processor disassembleInstructionAt: offsetBytes // 4 In: machineCode object */ + constant = (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) constantArg))) + || ((((AbstractInstruction *) constantArg)) == (methodLabel)) + ? ((((AbstractInstruction *) constantArg))->address) + : constantArg); + if (((((self_in_CogARMv8Compiler->opcode)) == MoveCwR) + || (((self_in_CogARMv8Compiler->opcode)) == PushCw)) + && (/* inCurrentCompilation: */ + (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) constant))) + || ((((AbstractInstruction *) constant)) == (methodLabel))) + || (/* addressIsInCurrentCompilation: */ + ((((usqInt)constant)) >= ((methodLabel->address))) + && ((((usqInt)constant)) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize))))))) { + /* C6.2.10 ADR C6-773 */ + unaligned = constant & 3; + aligned = constant - unaligned; + pcRelativeOffset = ((aligned - (((sqInt)((self_in_CogARMv8Compiler->address)))))) >> 2; + assert(((pcRelativeOffset >= (-262144)) && (pcRelativeOffset <= (0x3FFFF)))); + ((self_in_CogARMv8Compiler->machineCode))[offsetBytes / 4] = (((((unaligned << 29)) + (0x10000000)) + ((((usqInt)((pcRelativeOffset & (0x7FFFF))) << 5)))) + destReg); + return offsetBytes + 4; + } + assert(addressIsInCurrentCompilation((((self_in_CogARMv8Compiler->dependent))->address))); + assert((((((self_in_CogARMv8Compiler->dependent))->address)) % 4) == 0); + assert((SQABS((((usqInt)((((self_in_CogARMv8Compiler->dependent))->address)))) - (((usqInt)(((self_in_CogARMv8Compiler->address)) + offsetBytes))))) < (0x100000)); + + /* C6.2.131 LDR (literal) C6-979 */ + ((self_in_CogARMv8Compiler->machineCode))[offsetBytes / 4] = (((0x58000000) + ((((usqInt)(((((((self_in_CogARMv8Compiler->dependent))->address)) - (((self_in_CogARMv8Compiler->address)) + offsetBytes)) & (0x1FFFFF))) << 3)))) + destReg); + + /* 5 - 2 */ + return offsetBytes + 4; +} + + +/* C6.2.273 STR (immediate) C6-1239 + C6.2.274 STR (register) C6-1242 + C6.2.275 STRB (immediate) C6-1244 */ + + /* CogARMv8Compiler>>#emitSt:rn:rt:imm:shiftBy12: */ +static NoDbgRegParms sqInt +emitStrnrtimmshiftBy12(AbstractInstruction *self_in_CogARMv8Compiler, sqInt unitSizeLog2MinusOne, sqInt baseReg, sqInt sourceReg, sqInt offset, sqInt shiftBy12) +{ + sqInt instrBytes; + sqInt unitSize; + + unitSize = 1ULL << unitSizeLog2MinusOne; + assert(!((SP == sourceReg))); + assert(!((baseReg == sourceReg))); + + /* Unsigned offset, C6-1240 */ + if (((offset % unitSize) == 0) + && ((((offset / unitSize) >= 0) && ((offset / unitSize) <= (0xFFF))))) { + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((((((usqInt)(unitSizeLog2MinusOne) << 30))) + (0x39000000)) + ((((usqInt)(offset) << (10 - unitSizeLog2MinusOne))))) + ((((usqInt)(baseReg) << 5)))) + sourceReg); + return 4; + } + if (((offset >= -256) && (offset <= 0xFF))) { + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((((((usqInt)(unitSizeLog2MinusOne) << 30))) + (0x38000000)) + (((offset & 0x1FF) << 12))) + ((((usqInt)(baseReg) << 5)))) + sourceReg); + return 4; + } + + /* Unscaled signed 9-bit offset, C6-1244 + If the offset fits in an add or subtract immediate then do an + add into RISCTempReg, avoiding using an out-of-line literal... */ + if (/* isPossiblyShiftableNegatableImm12: */ + (((offset >= (-4096)) && (offset <= (0xFFF)))) + || (((!(offset & (0xFFF)))) + && (((((offset) >> 12) >= (-4096)) && (((offset) >> 12) <= (0xFFF)))))) { + /* begin isPossiblyShiftableImm12:ifTrue:ifFalse: */ + if (((offset >= 0) && (offset <= (0xFFF)))) { + /* C6.2.8 ADDS (immediate) C6-769 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0xB1000000U) + ((((usqInt)(offset) << 10)))) + ((((usqInt)(baseReg) << 5)))) + RISCTempReg); + goto l2; + } + if (((!(offset & (0xFFF)))) + && (((((offset) >> 12) >= 0) && (((offset) >> 12) <= (0xFFF))))) { + /* C6.2.8 ADDS (immediate) C6-769 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0xB1000000U) + ((((usqInt)(offset)) >> 2) + (0x400000))) + ((((usqInt)(baseReg) << 5)))) + RISCTempReg); + goto l2; + } + + /* begin isPossiblyShiftableImm12:ifTrue:ifFalse: */ + if ((((-offset) >= 0) && ((-offset) <= (0xFFF)))) { + /* C6.2.314 SUBS (immediate) C6-1321 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0xF1000000U) + ((((usqInt)((-offset)) << 10)))) + ((((usqInt)(baseReg) << 5)))) + RISCTempReg); + goto l1; + } + if (((!((-offset) & (0xFFF)))) + && ((((((-offset)) >> 12) >= 0) && ((((-offset)) >> 12) <= (0xFFF))))) { + /* C6.2.314 SUBS (immediate) C6-1321 */ + ((self_in_CogARMv8Compiler->machineCode))[0] = ((((0xF1000000U) + ((((usqInt)((-offset))) >> 2) + (0x400000))) + ((((usqInt)(baseReg) << 5)))) + RISCTempReg); + goto l1; + } + error("cannot happen"); + /* end isPossiblyShiftableImm12:ifTrue:ifFalse: */ +l1: + /* end isPossiblyShiftableImm12:ifTrue:ifFalse: */ +l2: + ((self_in_CogARMv8Compiler->machineCode))[1] = ((((((((((usqInt)(unitSizeLog2MinusOne) << 30))) + (0x38200000)) + ((((usqInt)(XZR) << 16)))) + ((((usqInt)(UXTX) << 13)))) + (0x800)) + ((((usqInt)(RISCTempReg) << 5)))) + sourceReg); + return 8; + } + + /* cogit processor disassembleInstructionAt: 0 In: machineCode object + cogit processor disassembleInstructionAt: 4 In: machineCode object */ + instrBytes = emitMoveCwintoRat(self_in_CogARMv8Compiler, offset, RISCTempReg, 0); + assert(instrBytes == 4); + ((self_in_CogARMv8Compiler->machineCode))[1] = ((((((((((usqInt)(unitSizeLog2MinusOne) << 30))) + (0x38200000)) + ((((usqInt)(RISCTempReg) << 16)))) + ((((usqInt)(UXTX) << 13)))) + (0x800)) + ((((usqInt)(baseReg) << 5)))) + sourceReg); + return 8; +} + + +/* Answer if CallFull and/or JumpFull are relative and hence need relocating + on method + compation. If so, they are annotated with IsRelativeCall in methods and + relocated in + relocateIfCallOrMethodReference:mcpc:delta: */ + + /* CogARMv8Compiler>>#fullCallsAreRelative */ +static NoDbgRegParms sqInt +fullCallsAreRelative(AbstractInstruction *self_in_CogARMv8Compiler) +{ + return 0; +} + + +/* Divide regDividend by regDivisor storing the quotient in regQuotient and + remainder in regRemainder. + MSUB Multiply-subtract MSUB C6-1109 + SDIV Signed divide SDIV C6-1174 */ +/* For the MSUB to work we must preserve regDivisor and regDividend for the + MSUB; i.e. the DivRRR must not overwrite either regDivisor or regDividend. */ + + /* CogARMv8Compiler>>#genDivR:R:Quo:Rem: */ +static NoDbgRegParms AbstractInstruction * +genDivRRQuoRem(AbstractInstruction *self_in_CogARMv8Compiler, sqInt regDivisor, sqInt regDividend, sqInt regQuotient, sqInt regRemainder) +{ + AbstractInstruction *instr; + sqInt safeRegQuotient; + + safeRegQuotient = ((regQuotient == regDividend) + || (regQuotient == regDivisor) + ? RISCTempReg + : regQuotient); + instr = genoperandoperandoperand(DivRRR, regDividend, regDivisor, safeRegQuotient); + + /* MSUB , , , , Xd := Xa - (Xn * Xm) + MSUB regRemainder, regQuotient, regDivisor, regQuotient */ + genoperandoperandoperand(MSubRRR, ((((usqInt)(safeRegQuotient) << 5))) + regDivisor, regDividend, regRemainder); + if (safeRegQuotient != regQuotient) { + genoperandoperand(MoveRR, safeRegQuotient, regQuotient); + } + return instr; +} + + +/* Use the DC instruction to implement ceFlushDCache(void *start, void *end); + see flushDCacheFrom:to:. + If there is a dual mapped zone then clean data via DC_CVAU as address + + codeToDataDelta, then invalidate data at address via CIVAC. */ +/* D4.4.7 About cache maintenance in AArch64 state D4-2478 + + Terminology for Clean, Invalidate, and Clean and Invalidate + instructions D4-2479 ... + - For instructions operating by VA, the following conceptual points are + defined: D4-2480 Point of Unification (PoU) + The PoU for a PE is the point by which the instruction and data caches and + the translation table walks of that + PE are guaranteed to see the same copy of a memory location. In many + cases, the Point of Unification is the + point in a uniprocessor memory system by which the instruction and data + caches and the translation table + walks have merged. + + The PoU for an Inner Shareable shareability domain is the point by which + the instruction and data caches + and the translation table walks of all the PEs in that Inner Shareable + shareability domain are guaranteed to + see the same copy of a memory location. Defining this point permits + self-modifying software to ensure future + instruction fetches are associated with the modified version of the + software by using the standard correctness + policy of: + 1. Clean data cache entry by address. + 2. Invalidate instruction cache entry by address. + + Example code for cache maintenance instructions D4-2490 - D4-2491 */ + + /* CogARMv8Compiler>>#generateDCacheFlush */ +static NoDbgRegParms AbstractInstruction * +generateDCacheFlush(AbstractInstruction *self_in_CogARMv8Compiler) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *loop; + sqInt mask; + sqInt quickConstant; + + +# if __APPLE__ && __MACH__ + /* Apple's cache flush code on Apple Silicon is simple and effective; we can't easily better it... */ +# elif _WIN64 +# else + assert((getCodeToDataDelta()) != 0); + + /* Mask is large enough to encompass the method zone and has the correct minimum alignment. */ + mask = (1ULL << (highBit(limitAddress))) - (dataCacheLineLength(self_in_CogARMv8Compiler)); + + /* Since this is used from C code we must use only caller-saved registers. + C arg registers 2 & 3 are such a convenient pair of caller-saved registers. */ + gAndCqRR(mask, CArg0Reg, CArg2Reg); + gAddCqRR(getCodeToDataDelta(), CArg2Reg, CArg3Reg); + loop = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* see concretizeDataCacheControl */ + genoperandoperand(DC, CArg3Reg, DC_CVAU); + genoperandoperand(DC, CArg2Reg, DC_CIVAC); + quickConstant = dataCacheLineLength(self_in_CogARMv8Compiler); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, CArg2Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + quickConstant = dataCacheLineLength(self_in_CogARMv8Compiler); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, CArg3Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* begin CmpR:R: */ + assert(!(0 /* (CArg1Reg = SPReg) */)); + genoperandoperand(CmpRR, CArg1Reg, CArg2Reg); + + /* JumpBelow: */ + genConditionalBranchoperand(JumpBelow, ((sqInt)loop)); + + /* RetN: */ + genoperand(RetN, 0); +# endif // __APPLE__ && __MACH__ || _WIN64 + + return self_in_CogARMv8Compiler; +} + + +/* Use DC VAUC, DSB, IC IVAU, and ISB instructions to implement + ceFlushICache(void *start, void *end); see flushICacheFrom:to:. + One might think that if there is a dual zone then data at address + + codeToDataDelta must be cleaned, + but this isn't the case. All we need to do is clean data at address via DC + VAUC and instructions via IC IVAU. */ +/* B2.2.5 Concurrent modification and execution of + instructions B2-112 + ...to avoid UNPREDICTABLE or CONSTRAINED UNPREDICTABLE behavior, + instruction modifications must be explicitly synchronized before they are + executed. The required synchronization is as follows: + + 1. No PE must be executing an instruction when another PE is modifying + that instruction. + + 2. To ensure that the modified instructions are observable, a PE that is + writing the instructions must issue the following sequence of instructions + and operations: + + ; Coherency example for data and instruction accesses within the same + Inner Shareable domain. + ; enter this code with containing a new 32-bit instruction, to be + held in Cacheable space at a location pointed to by Xn. + + STR Wt, [Xn] + DC CVAU, Xn ; Clean data cache by VA to point of unification (PoU) + DSB ISH ; Ensure visibility of the data cleaned from cache + IC IVAU, Xn ; Invalidate instruction cache by VA to PoU + DSB ISH + + Note + - The DC CVAU operation is not required if the area of memory is either + Non-cacheable or Write-Through Cacheable. + - If the contents of physical memory differ between the mappings, changing + the mapping of VAs to PAs can cause + the instructions to be concurrently modified by one PE and executed by + another PE. If the modifications affect + instructions other than those listed as being acceptable for modification, + synchronization must be used to avoid + UNPREDICTABLE or CONSTRAINED UNPREDICTABLE behavior. + + 3. In a multiprocessor system, the IC IVAU is broadcast to all PEs within + the Inner Shareable domain of the PE running this sequence. + However, when the modified instructions are observable, each PE that is + executing the modified instructions must issue the following + instruction to ensure execution of the modified instructions: + + ISB ; Synchronize fetched instruction stream */ +/* D4.4.7 About cache maintenance in AArch64 state D4-2478 + + Terminology for Clean, Invalidate, and Clean and Invalidate + instructions D4-2479 ... + - For instructions operating by VA, the following conceptual points are + defined: D4-2480 Point of Unification (PoU) + The PoU for a PE is the point by which the instruction and data caches and + the translation table walks of that + PE are guaranteed to see the same copy of a memory location. In many + cases, the Point of Unification is the + point in a uniprocessor memory system by which the instruction and data + caches and the translation table + walks have merged. + + The PoU for an Inner Shareable shareability domain is the point by which + the instruction and data caches + and the translation table walks of all the PEs in that Inner Shareable + shareability domain are guaranteed to + see the same copy of a memory location. Defining this point permits + self-modifying software to ensure future + instruction fetches are associated with the modified version of the + software by using the standard correctness + policy of: + 1. Clean data cache entry by address. + 2. Invalidate instruction cache entry by address. + + Example code for cache maintenance instructions D4-2490 - D4-2491 */ + + /* CogARMv8Compiler>>#generateICacheFlush */ +static NoDbgRegParms AbstractInstruction * +generateICacheFlush(AbstractInstruction *self_in_CogARMv8Compiler) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *loop; + sqInt mask; + sqInt quickConstant; + + +# if !(__APPLE__ && __MACH__) + /* See concretizeCacheControlOp1:CRm:Op2: & + http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.100403_0200_00_en/lau1443435580346.html */ + if (dataCacheFlushRequired(self_in_CogARMv8Compiler)) { + mask = (1ULL << (highBit(limitAddress))) - (dataCacheLineLength(self_in_CogARMv8Compiler)); + gAndCqRR(mask, CArg0Reg, CArg2Reg); + loop = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* see concretizeDataCacheControl */ + genoperandoperand(DC, CArg2Reg, DC_CVAU); + quickConstant = dataCacheLineLength(self_in_CogARMv8Compiler); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, CArg2Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* begin CmpR:R: */ + assert(!(0 /* (CArg1Reg = SPReg) */)); + genoperandoperand(CmpRR, CArg1Reg, CArg2Reg); + + /* JumpBelow: */ + genConditionalBranchoperand(JumpBelow, ((sqInt)loop)); + } + + /* CTR_EL0.IDC is zero; must clean data cache to point of unification. + Since this is used from C code we must use only caller-saved registers. + C arg registers 2 & 3 are as such a convenient pair of caller-saved registers. + Mask is large enough to encompass the method zone and has the correct minimum alignment. */ + genoperandoperand(DSB, DSB_ISH, DSB_ALL); + if (instructionCacheFlushRequired(self_in_CogARMv8Compiler)) { + mask = (1ULL << (highBit(limitAddress))) - (instructionCacheLineLength(self_in_CogARMv8Compiler)); + gAndCqRR(mask, CArg0Reg, CArg2Reg); + loop = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* see concretizeDataCacheControl */ + genoperandoperand(IC, CArg2Reg, IC_IVAU); + quickConstant = instructionCacheLineLength(self_in_CogARMv8Compiler); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, CArg2Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* begin CmpR:R: */ + assert(!(0 /* (CArg1Reg = SPReg) */)); + genoperandoperand(CmpRR, CArg1Reg, CArg2Reg); + + /* JumpBelow: */ + genConditionalBranchoperand(JumpBelow, ((sqInt)loop)); + + /* clean (flush) address */ + genoperandoperand(DSB, DSB_ISH, DSB_ALL); + } + + /* CTR_EL0.DIC is zero; must clean instruction cache to point of unification. + Mask is large enough to encompass the method zone and has the correct minimum alignment. */ + gen(ISB); + + /* RetN: */ + genoperand(RetN, 0); +# endif // !(__APPLE__ && __MACH__) + + return self_in_CogARMv8Compiler; +} + + +/* Load the frame and stack pointer registers with those of the C stack, + effecting a switch to the C stack. Used when machine code calls into + the CoInterpreter run-time (e.g. to invoke interpreter primitives). + N.B. CoInterpreter stack layout dictates that the stack pointer should be + loaded first. + The stack zone is allocated on the C stack before the interpreter runs and + hence before CStackPointer and CFramePointer are captured. So when running + in machine + code the native stack pointer and frame pointer appear to be on a colder + part of the + stack to CStackPointer and CFramePointer. When CStackPointerhas been set + and the frame pointer is still in machine code the current frame looks + like it has lots of + stack. If the frame pointer was set to CFramePointer before hand then it + would be beyond the stack pointer for that one instruction. */ +/* Load the frame and stack pointer registers with those of the C stack, + effecting a switch to the C stack. Used when machine code calls into + the CoInterpreter run-time (e.g. to invoke interpreter primitives). + Override to try and use MoveAwRR/ldp */ + + /* CogARMv8Compiler>>#genLoadCStackPointers */ +static NoDbgRegParms sqInt +genLoadCStackPointers(AbstractInstruction *self_in_CogARMv8Compiler) +{ + if (((cStackPointerAddress()) + 8) == (cFramePointerAddress())) { + genoperandoperandoperand(MoveAwRR, cStackPointerAddress(), SP, FP); + return 0; + } + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(cStackPointerAddress(), genoperandoperand(MoveAwR, cStackPointerAddress(), NativeSPReg)); + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(cFramePointerAddress(), genoperandoperand(MoveAwR, cFramePointerAddress(), FPReg)); + return 0; +} + + /* CogARMv8Compiler>>#genLoadNativeSPRegWithAlignedSPReg */ +static NoDbgRegParms AbstractInstruction * +genLoadNativeSPRegWithAlignedSPReg(AbstractInstruction *self_in_CogARMv8Compiler) +{ + gAndCqRR((-1 - (((BytesPerWord * 2) - 1))), SPReg, SP); + return self_in_CogARMv8Compiler; +} + + +/* Switch back to the Smalltalk stack. Assign SPReg first + because typically it is used immediately afterwards. */ +/* Switch back to the Smalltalk stack. Assign SPReg first + because typically it is used immediately afterwards. + Override to try and use MoveAwRR/ldp */ + + /* CogARMv8Compiler>>#genLoadStackPointers */ +static NoDbgRegParms sqInt +genLoadStackPointers(AbstractInstruction *self_in_CogARMv8Compiler) +{ + if (((stackPointerAddress()) + 8) == (framePointerAddress())) { + genoperandoperandoperand(MoveAwRR, stackPointerAddress(), SPReg, FPReg); + return 0; + } + if (((framePointerAddress()) + 8) == (stackPointerAddress())) { + genoperandoperandoperand(MoveAwRR, framePointerAddress(), FPReg, SPReg); + return 0; + } + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(stackPointerAddress(), genoperandoperand(MoveAwR, stackPointerAddress(), SPReg)); + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(framePointerAddress(), genoperandoperand(MoveAwR, framePointerAddress(), FPReg)); + return 0; +} + + +/* Generate the code to pass up to four arguments in a C run-time call. Hack: + each argument is + either a negative number, which encodes a constant, or a non-negative + number, that of a register. + The encoding for constants is defined by trampolineArgConstant: & + trampolineArgValue:. Pass a constant as the result of + trampolineArgConstant:. + Run-time calls have no more than four arguments, so chosen so that on ARM, + where in its C ABI the + first four integer arguments are passed in registers, all arguments can be + passed in registers. We + defer to the back end to generate this code not so much that the back end + knows whether it uses + the stack or registers to pass arguments (it does, but...). In fact we + defer for an extremely evil reason. + Doing so allows the x64 (where up to 6 args are passed) to assign the + register arguments in an order + that allows some of the argument registers to be used for specific + abstract registers, specifically + ReceiverResultReg and ClassReg. This is evil, evil, evil, but also it's + really nice to keep using the old + register assignments the original author has grown accustomed to. */ + + /* CogARMv8Compiler>>#genMarshallNArgs:arg:arg:arg:arg: */ +static NoDbgRegParms AbstractInstruction * +genMarshallNArgsargargargarg(AbstractInstruction *self_in_CogARMv8Compiler, sqInt numArgs, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3) +{ + AbstractInstruction *anInstruction; + + if (!numArgs) { + return self_in_CogARMv8Compiler; + } + + /* Avoid arg regs being overwritten before they are read. */ + if (numArgs > 1) { + if ((!(regOrConst1 < NoReg)) + && (regOrConst1 == CArg0Reg)) { + /* MoveR:R: */ + genoperandoperand(MoveRR, regOrConst1, Extra0Reg); + return genMarshallNArgsargargargarg(self_in_CogARMv8Compiler, numArgs, regOrConst0, Extra0Reg, regOrConst2, regOrConst3); + } + if (numArgs > 2) { + if ((!(regOrConst2 < NoReg)) + && ((regOrConst2 == CArg0Reg) + || (regOrConst2 == CArg1Reg))) { + /* MoveR:R: */ + genoperandoperand(MoveRR, regOrConst2, Extra1Reg); + return genMarshallNArgsargargargarg(self_in_CogARMv8Compiler, numArgs, regOrConst0, regOrConst1, Extra1Reg, regOrConst3); + } + if (numArgs > 3) { + if ((!(regOrConst3 < NoReg)) + && ((regOrConst3 == CArg0Reg) + || ((regOrConst3 == CArg1Reg) + || (regOrConst3 == CArg2Reg)))) { + /* MoveR:R: */ + genoperandoperand(MoveRR, regOrConst3, Extra2Reg); + return genMarshallNArgsargargargarg(self_in_CogARMv8Compiler, numArgs, regOrConst0, regOrConst1, regOrConst2, Extra2Reg); + } + } + } + } + if (regOrConst0 < NoReg) { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, -2 - regOrConst0, CArg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(-2 - regOrConst0, BytesPerOop)); + } + } + else { + if (regOrConst0) { + /* MoveR:R: */ + genoperandoperand(MoveRR, regOrConst0, CArg0Reg); + } + } + if (numArgs == 1) { + return self_in_CogARMv8Compiler; + } + if (regOrConst1 < NoReg) { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, -2 - regOrConst1, CArg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(-2 - regOrConst1, BytesPerOop)); + } + } + else { + if (regOrConst1 != CArg1Reg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, regOrConst1, CArg1Reg); + } + } + if (numArgs == 2) { + return self_in_CogARMv8Compiler; + } + if (regOrConst2 < NoReg) { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, -2 - regOrConst2, CArg2Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(-2 - regOrConst2, BytesPerOop)); + } + } + else { + if (regOrConst2 != CArg2Reg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, regOrConst2, CArg2Reg); + } + } + if (numArgs == 3) { + return self_in_CogARMv8Compiler; + } + if (regOrConst3 < NoReg) { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, -2 - regOrConst3, CArg3Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(-2 - regOrConst3, BytesPerOop)); + } + } + else { + if (regOrConst3 != CArg3Reg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, regOrConst3, CArg3Reg); + } + } + return self_in_CogARMv8Compiler; +} + + /* CogARMv8Compiler>>#genMulOverflowR:R: */ +static NoDbgRegParms AbstractInstruction * +genMulOverflowRR(AbstractInstruction *self_in_CogARMv8Compiler, sqInt regSource, sqInt regDest) +{ + return genoperandoperandoperand(MulOverflowRRR, regSource, regDest, regDest); +} + + /* CogARMv8Compiler>>#genMulR:R: */ +static NoDbgRegParms AbstractInstruction * +genMulRR(AbstractInstruction *self_in_CogARMv8Compiler, sqInt regSource, sqInt regDest) +{ + return genoperandoperandoperand(MulRRR, regSource, regDest, regDest); +} + + +/* Ensure that the register args are pushed before the outer and + inner retpcs at an entry miss for arity <= self numRegArgs. The + outer retpc is that of a call at a send site. The inner is the call + from a method or PIC abort/miss to the trampoline. */ +/* Putting the receiver and args above the return address means the + CoInterpreter has a single machine-code frame format which saves + us a lot of work. */ +/* Iff there are register args convert + sp -> outerRetpc (send site retpc) + linkReg = innerRetpc (PIC abort/miss retpc) + to + base -> receiver + (arg0) + (arg1) + sp -> outerRetpc (send site retpc) + sp -> linkReg/innerRetpc (PIC abort/miss retpc) */ + + /* CogARMv8Compiler>>#genPushRegisterArgsForAbortMissNumArgs: */ +static NoDbgRegParms AbstractInstruction * +genPushRegisterArgsForAbortMissNumArgs(AbstractInstruction *self_in_CogARMv8Compiler, sqInt numArgs) +{ + AbstractInstruction *anInstruction; + + if (numArgs <= (numRegArgs())) { + assert((numRegArgs()) <= 2); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, 0, SPReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, ReceiverResultReg, 0, SPReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + if (numArgs > 0) { + /* PushR: */ + genoperand(PushR, Arg0Reg); + if (numArgs > 1) { + /* PushR: */ + genoperand(PushR, Arg1Reg); + } + } + + /* PushR: */ + genoperand(PushR, TempReg); + } + + /* PushR: */ + genoperand(PushR, LinkReg); + return self_in_CogARMv8Compiler; +} + + +/* Ensure that the register args are pushed before the retpc for arity <= + self numRegArgs. + */ +/* This is easy on a RISC like ARM because the return address is in the link + register. Putting + the receiver and args above the return address means the CoInterpreter has + a single + machine-code frame format which saves us a lot of work + NOTA BENE: we do NOT push the return address here, which means it must be + dealt with later. */ + + /* CogARMv8Compiler>>#genPushRegisterArgsForNumArgs:scratchReg: */ +static NoDbgRegParms AbstractInstruction * +genPushRegisterArgsForNumArgsscratchReg(AbstractInstruction *self_in_CogARMv8Compiler, sqInt numArgs, sqInt ignored) +{ + if (numArgs <= (numRegArgs())) { + assert((numRegArgs()) <= 2); + + /* PushR: */ + genoperand(PushR, ReceiverResultReg); + if (numArgs > 0) { + /* PushR: */ + genoperand(PushR, Arg0Reg); + if (numArgs > 1) { + /* PushR: */ + genoperand(PushR, Arg1Reg); + } + } + } + return self_in_CogARMv8Compiler; +} + + +/* This is a no-op on ARM64 since the ABI passes up to 6 args in registers + and trampolines currently observe that limit, using only 4. + */ + + /* CogARMv8Compiler>>#genRemoveNArgsFromStack: */ +static NoDbgRegParms sqInt +genRemoveNArgsFromStack(AbstractInstruction *self_in_CogARMv8Compiler, sqInt n) +{ + assert(n <= 6); + return 0; +} + + +/* Restore the registers in regMask as saved by genSaveRegs:. + See + http://infocenter.arm.com/help/topic/com.arm.doc.den0028b/ARM_DEN0028B_SMC_Calling_Convention.pdf + N.B. Alignment is handled by + genAlignCStackSavingRegisters:numArgs:wordAlignment:. + */ + + /* CogARMv8Compiler>>#genRestoreRegs: */ +static NoDbgRegParms sqInt +genRestoreRegs(AbstractInstruction *self_in_CogARMv8Compiler, sqInt regMask) +{ + sqInt nRegs; + int pair; + sqInt reg; + + if (!regMask) { + return 0; + } + assert(!((registerisInMask(RISCTempReg, regMask)))); + nRegs = 0; + for (reg = R1; reg <= R17; reg += 1) { + if (((regMask & (((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)))) != 0)) { + nRegs += 1; + } + } + pair = ((!(nRegs & 1)) + ? NoReg + : RISCTempReg); + for (reg = R1; reg <= R17; reg += 1) { + if (((regMask & (((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)))) != 0)) { + if (pair == NoReg) { + pair = reg; + } + else { + genoperandoperand(NativePopRR, reg, pair); + pair = NoReg; + } + } + } + assert(pair == NoReg); + return 0; +} + + +/* Save the registers in regMask for a call into the C run-time from a + trampoline. See + http://infocenter.arm.com/help/topic/com.arm.doc.den0028b/ARM_DEN0028B_SMC_Calling_Convention.pdf + N.B. Alignment is handled by + genAlignCStackSavingRegisters:numArgs:wordAlignment:. + */ + + /* CogARMv8Compiler>>#genSaveRegs: */ +static NoDbgRegParms sqInt +genSaveRegs(AbstractInstruction *self_in_CogARMv8Compiler, sqInt regMask) +{ + sqInt pair; + sqInt reg; + + if (!regMask) { + return 0; + } + assert(!((registerisInMask(RISCTempReg, regMask)))); + pair = NoReg; + for (reg = R17; reg >= R0; reg += -1) { + if (((regMask & (((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)))) != 0)) { + if (pair == NoReg) { + pair = reg; + } + else { + genoperandoperand(NativePushRR, pair, reg); + pair = NoReg; + } + } + } + if (pair != NoReg) { + genoperandoperand(NativePushRR, pair, RISCTempReg); + } + return 0; +} + + +/* Save the frame and stack pointer registers to the framePointer + and stackPointer variables. Used to save the machine code frame + for use by the run-time when calling into the CoInterpreter run-time. */ +/* Save the frame and stack pointer registers to the framePointer + and stackPointer variables. Used to save the machine code frame + for use by the run-time when calling into the CoInterpreter run-time. + Override to try and use MoveRRAw/stp */ + + /* CogARMv8Compiler>>#genSaveStackPointers */ +static NoDbgRegParms sqInt +genSaveStackPointers(AbstractInstruction *self_in_CogARMv8Compiler) +{ + if (((stackPointerAddress()) + 8) == (framePointerAddress())) { + genoperandoperandoperand(MoveRRAw, SPReg, FPReg, stackPointerAddress()); + return 0; + } + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(framePointerAddress(), genoperandoperand(MoveRAw, FPReg, framePointerAddress())); + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(stackPointerAddress(), genoperandoperand(MoveRAw, SPReg, stackPointerAddress())); + return 0; +} + + /* CogARMv8Compiler>>#genSubstituteReturnAddress: */ +static NoDbgRegParms AbstractInstruction * +genSubstituteReturnAddress(AbstractInstruction *self_in_CogARMv8Compiler, sqInt retpc) +{ + return checkLiteralforInstruction(retpc, genoperandoperand(MoveCwR, retpc, LR)); +} + + +/* Answer if the processor has a dedicated callee-saved register to point to + the base of commonly-accessed variables. On ARMv8 we use R27 for this. */ + + /* CogARMv8Compiler>>#hasVarBaseRegister */ +static NoDbgRegParms sqInt +hasVarBaseRegister(AbstractInstruction *self_in_CogARMv8Compiler) +{ + return 1; +} + + /* CogARMv8Compiler>>#inlineCacheTagAt: */ +static NoDbgRegParms unsigned int +inlineCacheTagAt(AbstractInstruction *self_in_CogARMv8Compiler, sqInt callSiteReturnAddress) +{ + unsigned int instr; + sqInt pc; + + /* begin pcRelativeAddressAt: */ + instr = long32At((void *)(((usqInt)(callSiteReturnAddress - 8)))); + + /* cogit processor disassembleInstructionAt: mcpc In: objectMemory memory */ + assert(instructionIsPCRelativeLoad(self_in_CogARMv8Compiler, instr)); + pc = (((((usqInt)(instr)) >> 3) & (0x3FFFFC)) - ((((usqInt)(instr)) >> 2) & (0x200000))) + (((usqInt)(callSiteReturnAddress - 8))); + + /* begin instructionAt: */ + return long32At((void *)(pc)); +} + + +/* Answer the instruction address immediately preceding mcpc. */ + + /* CogARMv8Compiler>>#instructionAddressBefore: */ +static NoDbgRegParms sqInt +instructionAddressBefore(AbstractInstruction *self_in_CogARMv8Compiler, sqInt mcpc) +{ + return mcpc - 4; +} + + /* CogARMv8Compiler>>#instructionAt: */ +static NoDbgRegParms unsigned int +instructionAt(AbstractInstruction *self_in_CogARMv8Compiler, sqInt pc) +{ + return long32At((void *)(pc)); +} + + +/* C6.2.10 ADR C6-773 */ + + /* CogARMv8Compiler>>#instructionIsADR: */ +static NoDbgRegParms int +instructionIsADR(AbstractInstruction *self_in_CogARMv8Compiler, sqInt word) +{ + return ((((usqInt)(word)) >> 24) & 159) == 16; +} + + +/* C6.2.26 B C6-799 + C6.2.33 BL C6-812 */ +/* BL is 2r100101, B is 2r000101 */ + + /* CogARMv8Compiler>>#instructionIsImm26BorBL: */ +static NoDbgRegParms int +instructionIsImm26BorBL(AbstractInstruction *self_in_CogARMv8Compiler, usqInt word) +{ + return (((word) >> 26) & 0x1F) == 5; +} + + +/* C4.1 A64 instruction set encoding on page C4-252 */ + + /* CogARMv8Compiler>>#instructionIsLoadStore: */ +static NoDbgRegParms int +instructionIsLoadStore(AbstractInstruction *self_in_CogARMv8Compiler, sqInt instr) +{ + return ((((usqInt)(instr)) >> 25) & 5) == 4; +} + + +/* C6.2.131 LDR (literal) C6-979 + C6.2.143 LDRSW (literal) C6-1008 */ + + /* CogARMv8Compiler>>#instructionIsPCRelativeLoad: */ +static NoDbgRegParms int +instructionIsPCRelativeLoad(AbstractInstruction *self_in_CogARMv8Compiler, sqInt instr) +{ + return (instr & 0x3F000000) == 0x18000000; +} + + +/* Answer the instruction size at pc.Simple on ARM ;-) */ + + /* CogARMv8Compiler>>#instructionSizeAt: */ +static NoDbgRegParms sqInt +instructionSizeAt(AbstractInstruction *self_in_CogARMv8Compiler, sqInt pc) +{ + return 4; +} + + +/* Several of the opcodes are inverses. Answer the inverse for an opcode if + it has one. + See Table A3-2 in sec A3.4 Data-processing instructions of the AARM. */ + + /* CogARMv8Compiler>>#inverseForArithOp: */ +static NoDbgRegParms int +inverseForArithOp(AbstractInstruction *self_in_CogARMv8Compiler, sqInt arithOp) +{ + switch (arithOp) { + case ArithmeticAdd: + return ArithmeticSub; + + case ArithmeticAddS: + return ArithmeticSubS; + + case ArithmeticSub: + return ArithmeticAdd; + + case ArithmeticSubS: + return ArithmeticAddS; + + default: + error("Case not found and no otherwise clause"); + } + return 0; +} + + +/* Support for addressing variables off the dedicated VarBaseReg */ + + /* CogARMv8Compiler>>#isAddressRelativeToVarBase: */ +static NoDbgRegParms int +isAddressRelativeToVarBase(AbstractInstruction *self_in_CogARMv8Compiler, usqInt varAddress) +{ + return ((varAddress >= ((varBaseAddress) - (0x100))) && (varAddress <= (((varBaseAddress) + (0x1000)) - 1))); +} + + +/* Assuming mcpc is a send return pc answer if the instruction before it is a + call (not a CallFull). + */ +/* There are two types of calls: BL & BLR; BLR is used for CallFull */ + + /* CogARMv8Compiler>>#isCallPrecedingReturnPC: */ +static NoDbgRegParms sqInt +isCallPrecedingReturnPC(AbstractInstruction *self_in_CogARMv8Compiler, sqInt mcpc) +{ + unsigned int instruction; + + instruction = long32At((void *)(mcpc - 4)); + return ((((usqInt)(instruction)) >> 26) == 37) + || ((instruction | (0x3E0)) == 3594453984U); +} + + /* CogARMv8Compiler>>#isFullJumpAtPC: */ +static NoDbgRegParms int +isFullJumpAtPC(AbstractInstruction *self_in_CogARMv8Compiler, sqInt mcpc) +{ + return ((long32At((void *)(mcpc))) | (0x3E0)) == 3592356832U; +} + + +/* ARM64 load/store immediate offsets have an 11 bit unsigned amd a 9 bit + signed form. + */ + + /* CogARMv8Compiler>>#isImm12orImm9offset: */ +static NoDbgRegParms sqInt +isImm12orImm9offset(AbstractInstruction *self_in_CogARMv8Compiler, sqInt offset) +{ + return (offset >= -256) + && (offset < 0x1000); +} + + +/* ARM64 calls span +/- 128 mb. + C6.2.33 BL C6-812 */ + + /* CogARMv8Compiler>>#isInImmediateBranchAndLinkRange: */ +static NoDbgRegParms int +isInImmediateBranchAndLinkRange(AbstractInstruction *self_in_CogARMv8Compiler, sqIntptr_t offset) +{ + assert((!(offset & 3))); + return (((((((sqLong) offset))) >> 27) >= -1) && ((((((sqLong) offset))) >> 27) <= 0)); +} + + +/* ARM64 calls and jumps span +/- 1 mb. */ + + /* CogARMv8Compiler>>#isInImmediateBranchRange: */ +static NoDbgRegParms int +isInImmediateBranchRange(AbstractInstruction *self_in_CogARMv8Compiler, usqIntptr_t offset) +{ + assert((!(offset & 3))); + return (((((((sqLong) offset))) >> 18) >= -1) && ((((((sqLong) offset))) >> 18) <= 0)); +} + + +/* ARMv8 calls and jumps span +/- 128 mb, more than enough for intra-zone + calls and jumps. + */ + + /* CogARMv8Compiler>>#isInImmediateJumpRange: */ +static NoDbgRegParms int +isInImmediateJumpRange(AbstractInstruction *self_in_CogARMv8Compiler, usqIntptr_t operand) +{ + assert((!(operand & 3))); + return (((((int) operand)) >= ((-0x7FFFFFFF-1))) && ((((int) operand)) <= (0x7FFFFFF))); +} + + +/* ARM64 load/store immediates have an 11 bit unsigned amd a 9 bit signed + form. + */ + + /* CogARMv8Compiler>>#isInImmediateOffsetRange: */ +static NoDbgRegParms sqInt +isInImmediateOffsetRange(AbstractInstruction *self_in_CogARMv8Compiler, sqInt offset) +{ + return (offset >= -256) + && (offset < 0x1000); +} + + /* CogARMv8Compiler>>#isJump */ +static NoDbgRegParms sqInt +isJump(AbstractInstruction *self_in_CogARMv8Compiler) +{ + return (((((self_in_CogARMv8Compiler->opcode)) >= FirstJump) && (((self_in_CogARMv8Compiler->opcode)) <= LastJump))) + || (((((self_in_CogARMv8Compiler->opcode)) >= CBNZ) && (((self_in_CogARMv8Compiler->opcode)) <= CBZ))); +} + + +/* C4.1 A64 instruction set encoding on page C4-252 + C4.1.3 Branches, Exception Generating and System instructions */ +/* cogit processor disassembleInstructionAt: pc In: objectMemory memory */ + + /* CogARMv8Compiler>>#isJumpAt: */ +static NoDbgRegParms sqInt +isJumpAt(AbstractInstruction *self_in_CogARMv8Compiler, sqInt pc) +{ + sqInt op0_101_op1MSB; + + op0_101_op1MSB = (((usqInt)((long32At((void *)(pc))))) >> 25); + return (op0_101_op1MSB == 42) + || ((op0_101_op1MSB == 107) + || (((op0_101_op1MSB & 0x7E) == 74) + || ((op0_101_op1MSB & 0x7E) == 10))); +} + + +/* Answer if the receiver is a pc-dependent instruction. With out-of-line + literals any instruction + that refers to a literal depends on the address of the literal, so add + them in addition to the jumps. */ + + /* CogARMv8Compiler>>#isPCDependent */ +static NoDbgRegParms sqInt +isPCDependent(AbstractInstruction *self_in_CogARMv8Compiler) +{ + return (isJump(self_in_CogARMv8Compiler)) + || ((((self_in_CogARMv8Compiler->opcode)) == AlignmentNops) + || ((((self_in_CogARMv8Compiler->opcode)) != Literal) + && ((((self_in_CogARMv8Compiler->dependent))) + && (((((self_in_CogARMv8Compiler->dependent))->opcode)) == Literal)))); +} + + /* CogARMv8Compiler>>#isShiftedMask: */ +static NoDbgRegParms sqInt +isShiftedMask(AbstractInstruction *self_in_CogARMv8Compiler, sqInt anInteger) +{ + sqInt bits; + + return (anInteger != 0) + && (((bits = (anInteger - 1) | anInteger), + (bits & (bits + 1)) == 0)); +} + + /* CogARMv8Compiler>>#isUnsigned12BitMultipleOf8: */ +static NoDbgRegParms sqInt +isUnsigned12BitMultipleOf8(AbstractInstruction *self_in_CogARMv8Compiler, sqInt anInteger) +{ + return ((anInteger % 8) == 0) + && ((((anInteger / 8) >= 0) && ((anInteger / 8) <= (0xFFF)))); +} + + +/* Branch/Call ranges. Jump[Cond] can be generated as short as possible. + Call/Jump[Cond]Long must be generated + in the same number of bytes irrespective of displacement since their + targets may be updated, but they need only + span 16Mb, the maximum size of the code zone. This allows e.g. ARM to use + single-word call and jump instructions + for most calls and jumps. CallFull/JumpFull must also be generated in the + same number of bytes irrespective of + displacement for the same reason, but they must be able to span the full + (32-bit or 64-bit) address space because + they are used to call code in the C runtime, which may be distant from the + code zone + */ + + /* CogARMv8Compiler>>#jumpLongByteSize */ +static NoDbgRegParms sqInt +jumpLongByteSize(AbstractInstruction *self_in_CogARMv8Compiler) +{ + return 4; +} + + /* CogARMv8Compiler>>#jumpLongConditionalByteSize */ +static NoDbgRegParms sqInt +jumpLongConditionalByteSize(AbstractInstruction *self_in_CogARMv8Compiler) +{ + return 8; +} + + +/* C6.2.26 B C6-799 */ + + /* CogARMv8Compiler>>#jumpLongTargetBeforeFollowingAddress: */ +static NoDbgRegParms sqInt +jumpLongTargetBeforeFollowingAddress(AbstractInstruction *self_in_CogARMv8Compiler, sqInt mcpc) +{ + return callTargetFromReturnAddress(self_in_CogARMv8Compiler, mcpc); +} + + +/* cogit processor disassembleInstructionAt: pc In: objectMemory memory */ + + /* CogARMv8Compiler>>#jumpTargetPCAt: */ +static NoDbgRegParms usqInt +jumpTargetPCAt(AbstractInstruction *self_in_CogARMv8Compiler, sqInt pc) +{ + usqInt operand; + unsigned int word; + + word = long32At((void *)(pc)); + assert((((usqInt)(word)) >> 26) == 21); + operand = (((usqInt)(word)) >> 5) & 0x7FFFF; + if (((operand & 0x40000) != 0)) { + operand -= 0x80000; + } + return (operand * 4) + pc; +} + + +/* Answer the 32-bit constant loaded by the instruction sequence just before + this address: + CmpC32R MoveC32R */ + + /* CogARMv8Compiler>>#literal32BeforeFollowingAddress: */ +static NoDbgRegParms unsigned int +literal32BeforeFollowingAddress(AbstractInstruction *self_in_CogARMv8Compiler, sqInt followingAddress) +{ + unsigned int instr; + sqInt mcpc; + sqInt pc; + + mcpc = (instructionIsPCRelativeLoad(self_in_CogARMv8Compiler, long32At((void *)(followingAddress - 4))) + ? followingAddress + : followingAddress - 4); + + /* begin instructionAddressBefore: */ + mcpc -= 4; + instr = long32At((void *)(mcpc)); + + /* cogit processor disassembleInstructionAt: mcpc In: objectMemory memory */ + assert(instructionIsPCRelativeLoad(self_in_CogARMv8Compiler, instr)); + pc = (((((usqInt)(instr)) >> 3) & (0x3FFFFC)) - ((((usqInt)(instr)) >> 2) & (0x200000))) + mcpc; + + /* begin instructionAt: */ + return long32At((void *)(pc)); +} + + +/* Answer the literal referenced by the instruction immediately preceding + followingAddress. ArithCwR MoveCwR PushCw */ + + /* CogARMv8Compiler>>#literalBeforeFollowingAddress: */ +static NoDbgRegParms sqInt +literalBeforeFollowingAddress(AbstractInstruction *self_in_CogARMv8Compiler, sqInt followingAddress) +{ + return longAt((void *)(pcRelativeAddressAt(self_in_CogARMv8Compiler, instructionAddressBefore(self_in_CogARMv8Compiler, (instructionIsPCRelativeLoad(self_in_CogARMv8Compiler, long32At((void *)(followingAddress - 4))) + ? followingAddress + : followingAddress - 4))))); +} + + +/* Answer the size of a literal load instruction (which does not include the + size of the literal). + With out-of-line literals this is always a single LDR instruction that + refers to the literal. + */ + + /* CogARMv8Compiler>>#literalLoadInstructionBytes */ +static NoDbgRegParms sqInt +literalLoadInstructionBytes(AbstractInstruction *self_in_CogARMv8Compiler) +{ + return 4; +} + + +/* Answer the byte size of a MoveCwR opcode's corresponding machine code. + On ARMv8 this is a single instruction pc-relative register load */ + + /* CogARMv8Compiler>>#loadLiteralByteSize */ +static NoDbgRegParms sqInt +loadLiteralByteSize(AbstractInstruction *self_in_CogARMv8Compiler) +{ + return 4; +} + + +/* Answer the byte size of a MoveCwR opcode's corresponding machine code + when the argument is a PIC. This is for the self-reference at the end of a + closed PIC. On ARM this is a single instruction pc-relative register load. */ + + /* CogARMv8Compiler>>#loadPICLiteralByteSize */ +static NoDbgRegParms sqInt +loadPICLiteralByteSize(AbstractInstruction *self_in_CogARMv8Compiler) +{ + return 4; +} + + +/* Answer the maximum number of bytes of machine code generated for any + abstract instruction. + Likely to be quite different for AARCH64 + */ + + /* CogARMv8Compiler>>#machineCodeBytes */ +static NoDbgRegParms sqInt +machineCodeBytes(AbstractInstruction *self_in_CogARMv8Compiler) +{ + return 12; +} + + +/* Answer the maximum number of words of machine code generated for any + abstract instruction. + Likely to be quite different for AARCH64 + */ + + /* CogARMv8Compiler>>#machineCodeWords */ +static NoDbgRegParms sqInt +machineCodeWords(AbstractInstruction *self_in_CogARMv8Compiler) +{ + return (machineCodeBytes(self_in_CogARMv8Compiler)) / 4; +} + + /* CogARMv8Compiler>>#movern:rd: */ +static NoDbgRegParms sqInt +movernrd(AbstractInstruction *self_in_CogARMv8Compiler, sqInt srcReg, sqInt destReg) +{ + return addrnrdimmshiftBy12(self_in_CogARMv8Compiler, srcReg, destReg, 0, 0); +} + + /* CogARMv8Compiler>>#numDCacheFlushOpcodes */ +static NoDbgRegParms sqInt +numDCacheFlushOpcodes(AbstractInstruction *self_in_CogARMv8Compiler) +{ + +# if DUAL_MAPPED_CODE_ZONE + return (getCodeToDataDelta() + ? 15 + : 0); +# else + return 0; +# endif +} + + +/* Apple's code is simple and works; we can't better it... */ + + /* CogARMv8Compiler>>#numICacheFlushOpcodes */ +static NoDbgRegParms sqInt +numICacheFlushOpcodes(AbstractInstruction *self_in_CogARMv8Compiler) +{ + +# if __APPLE__ + return 0; +# elif _WIN64 + return 0; +# else + return 24; +# endif +} + + +/* See e.g. + http://infocenter.arm.com/help/topic/com.arm.doc.den0028b/ARM_DEN0028B_SMC_Calling_Convention.pdf + Table 3-1 Register Usage in AArch64 SMC32, HVC32, SMC64, and HVC64 calls */ + + /* CogARMv8Compiler>>#numIntRegArgs */ +static NoDbgRegParms sqInt +numIntRegArgs(AbstractInstruction *self_in_CogARMv8Compiler) +{ + return 6; +} + + +/* The maximum offset in a LDR (literal) is -2^18 to 2^18-1. + And this is multiplied by 4 to produce the effective address. + This is a huge range; we have no grounds for concern. */ + + /* CogARMv8Compiler>>#outOfLineLiteralOpcodeLimit */ +static NoDbgRegParms sqInt +outOfLineLiteralOpcodeLimit(AbstractInstruction *self_in_CogARMv8Compiler) +{ + return 0x3FFFF; +} + + /* CogARMv8Compiler>>#padIfPossibleWithStopsFrom:to: */ +static NoDbgRegParms AbstractInstruction * +padIfPossibleWithStopsFromto(AbstractInstruction *self_in_CogARMv8Compiler, sqInt startAddr, sqInt endAddr) +{ + sqInt nullBytes; + sqInt p; + + nullBytes = ((endAddr - startAddr) + 1) % 4; + stopsFromto(self_in_CogARMv8Compiler, startAddr, endAddr - nullBytes); + for (p = ((endAddr - nullBytes) + 1); p <= endAddr; p += 1) { + codeByteAtput((void *)(p),0xFF); + } + return self_in_CogARMv8Compiler; +} + + +/* Extract the address of the adr rX, offset instruction at address mcpc + C6.2.131 LDR (literal) C6-979 */ + + /* CogARMv8Compiler>>#pcRelativeAddressAt: */ +static NoDbgRegParms sqInt +pcRelativeAddressAt(AbstractInstruction *self_in_CogARMv8Compiler, sqInt mcpc) +{ + unsigned int instr; + + instr = long32At((void *)(mcpc)); + + /* cogit processor disassembleInstructionAt: mcpc In: objectMemory memory */ + assert(instructionIsPCRelativeLoad(self_in_CogARMv8Compiler, instr)); + return (((((usqInt)(instr)) >> 3) & (0x3FFFFC)) - ((((usqInt)(instr)) >> 2) & (0x200000))) + mcpc; +} + + +/* Alas the cycle counter PMCCNTR_EL0 is restricted on macOS. Only CNTVCT_EL0 + is available + */ + + /* CogARMv8Compiler>>#perfCounterRatePerUsec */ +static NoDbgRegParms sqInt +perfCounterRatePerUsec(AbstractInstruction *self_in_CogARMv8Compiler) +{ + return 24; +} + + +/* On Apple M1 the performance counter appears to be 41.67 MHz clock, 24 + ticks per microsecond. + */ + + /* CogARMv8Compiler>>#performanceCounterTicksPerMicrosecond */ +static NoDbgRegParms sqInt +performanceCounterTicksPerMicrosecond(AbstractInstruction *self_in_CogARMv8Compiler) +{ + return 24; +} + + +/* C6.2.211 PRFM (immediate) C6-1136 */ +/* Unsigned offset, C6-1136 */ +/* This is the only case we can make use of so far... */ + + /* CogARMv8Compiler>>#prn:imm:shiftBy12: */ +static NoDbgRegParms sqInt +prnimmshiftBy12(AbstractInstruction *self_in_CogARMv8Compiler, sqInt baseReg, sqInt offset, sqInt shiftBy12) +{ + assert(((offset % 8) == 0) + && ((((offset / 8) >= 0) && ((offset / 8) <= (0xFFF))))); + return (((0xF9800000U) + ((((usqInt)(offset) << 7)))) + ((((usqInt)(baseReg) << 5)))); +} + + /* CogARMv8Compiler>>#pushLinkRegisterByteSize */ +static NoDbgRegParms sqInt +pushLinkRegisterByteSize(AbstractInstruction *self_in_CogARMv8Compiler) +{ + return 4; +} + + +/* C6.2.26 B C6-799 + C6.2.33 BL C6-812 */ + + /* CogARMv8Compiler>>#relocateCallBeforeReturnPC:by: */ +static NoDbgRegParms AbstractInstruction * +relocateCallBeforeReturnPCby(AbstractInstruction *self_in_CogARMv8Compiler, sqInt retpc, sqInt delta) +{ + unsigned int distanceDiv4; + unsigned int instr; + + assert((delta % 4) == 0); + if (delta) { + instr = long32At((void *)(retpc - 4)); + assert(instructionIsImm26BorBL(self_in_CogARMv8Compiler, instr)); + distanceDiv4 = instr & (0x7FFFFFF); + distanceDiv4 += delta / 4; + codeLong32Atput((void *)(retpc - 4),(instr & 0xFC000000U) | (distanceDiv4 & 0x3FFFFFF)); + } + return self_in_CogARMv8Compiler; +} + + +/* We generate the method address using pc-relative addressing. + Simply check that pc-relative addressing is being used. c.f. + emitMoveCw:intoR:at: */ + + /* CogARMv8Compiler>>#relocateMethodReferenceBeforeAddress:by: */ +static NoDbgRegParms AbstractInstruction * +relocateMethodReferenceBeforeAddressby(AbstractInstruction *self_in_CogARMv8Compiler, sqInt pc, sqInt delta) +{ + assert((instructionIsADR(self_in_CogARMv8Compiler, instructionAt(self_in_CogARMv8Compiler, pc - 4))) + || (instructionIsADR(self_in_CogARMv8Compiler, instructionAt(self_in_CogARMv8Compiler, pc - 8)))); + return self_in_CogARMv8Compiler; +} + + +/* Rewrite a call instruction to call a different target. This variant is + used to link PICs + in ceSendMiss et al, and to rewrite cached primitive calls. Answer the + extent of + the code change which is used to compute the range of the icache to flush. */ + + /* CogARMv8Compiler>>#rewriteCallAt:target: */ +static NoDbgRegParms sqInt +rewriteCallAttarget(AbstractInstruction *self_in_CogARMv8Compiler, usqInt callSiteReturnAddress, usqInt callTargetAddress) +{ + codeLong32Atput((void *)(callSiteReturnAddress - 4),(/* begin bl: */ + assert((!((callTargetAddress - (callSiteReturnAddress - 4)) & 3))), + (0x94000000U) + ((((callTargetAddress - (callSiteReturnAddress - 4))) >> 2) & (0x3FFFFFF)))); + return 4; +} + + +/* Rewrite a long jump/call instruction to jump/call to a different target. + Answer the extent of the code change which is used to compute the range of + the icache to flush. + C6.2.25 B.cond C6-798 */ +/* cogit processor disassembleInstructionAt: followingAddress - 4 In: + objectMemory memory + */ + + /* CogARMv8Compiler>>#rewriteImm19JumpBefore:target: */ +static NoDbgRegParms sqInt +rewriteImm19JumpBeforetarget(AbstractInstruction *self_in_CogARMv8Compiler, sqInt followingAddress, sqInt targetAddress) +{ + sqInt instrOpcode; + unsigned int instruction; + sqInt mcpc; + sqInt offset; + + mcpc = followingAddress - 4; + offset = targetAddress - mcpc; + instruction = long32At((void *)(followingAddress - 4)); + instrOpcode = ((usqInt)(instruction)) >> 26; + assert(instrOpcode == 21); + codeLong32Atput((void *)(mcpc),(((((usqInt)(instrOpcode) << 26))) + ((((usqInt)((((offset) >> 2) & (0x7FFFF))) << 5)))) + (instruction & 15)); + return 4; +} + + +/* Rewrite a long jump/call instruction to jump/call to a different target. + Answer the extent of the code change which is used to compute the range of + the icache to flush. + C6.2.26 B C6-799 + C6.2.33 BL C6-812 */ +/* cogit processor disassembleInstructionAt: followingAddress - 4 In: + objectMemory memory + */ + + /* CogARMv8Compiler>>#rewriteImm26JumpBefore:target: */ +static NoDbgRegParms sqInt +rewriteImm26JumpBeforetarget(AbstractInstruction *self_in_CogARMv8Compiler, sqInt followingAddress, sqInt targetAddress) +{ + sqInt instrOpcode; + sqInt mcpc; + sqInt offset; + + mcpc = followingAddress - 4; + offset = targetAddress - mcpc; + instrOpcode = ((usqInt)((long32At((void *)(followingAddress - 4))))) >> 26; + assert((instrOpcode == 5) + || (instrOpcode == 37)); + codeLong32Atput((void *)(mcpc),((((usqInt)(instrOpcode) << 26))) + (((offset) >> 2) & (0x3FFFFFF))); + return 4; +} + + +/* Rewrite an inline cache to call a different target for a new tag. This + variant is used + to link unlinked sends in ceSend:to:numArgs: et al. Answer the extent of + the code + change which is used to compute the range of the icache to flush. + N.B. On 64-bit platforms the inline cache tag is only 32-bits wide, hence + this code + is very similar to that for ARM32 CogOutOfLineLiteralsARMCompiler. */ + + /* CogARMv8Compiler>>#rewriteInlineCacheAt:tag:target: */ +static NoDbgRegParms sqInt +rewriteInlineCacheAttagtarget(AbstractInstruction *self_in_CogARMv8Compiler, usqInt callSiteReturnAddress, sqInt cacheTag, usqInt callTargetAddress) +{ + sqInt call; + usqInt callDistance; + + callDistance = ((usqInt) (int)(callTargetAddress - (callSiteReturnAddress - 4))); + + /* return offset */ + assert(isInImmediateJumpRange(self_in_CogARMv8Compiler, callDistance)); + + /* begin bl: */ + assert((!(callDistance & 3))); + call = (0x94000000U) + ((((sqInt)(callDistance)) >> 2) & (0x3FFFFFF)); + codeLong32Atput((void *)(callSiteReturnAddress - 4),call); + codeLong32Atput((void *)(pcRelativeAddressAt(self_in_CogARMv8Compiler, callSiteReturnAddress - 8)),((usqInt) (int)cacheTag)); + assert((((int) (inlineCacheTagAt(self_in_CogARMv8Compiler, callSiteReturnAddress)))) == cacheTag); + + /* self cCode: '' + inSmalltalk: [cogit disassembleFrom: callSiteReturnAddress - 8 to: (self pcRelativeAddressAt: callSiteReturnAddress - 8)]. */ + return 4; +} + + +/* Rewrite an inline cache with a new tag. This variant is used + by the garbage collector. This cannot happen in 64-bits as cache tags are + guaranteed to be 32-bits or less. */ + + /* CogARMv8Compiler>>#rewriteInlineCacheTag:at: */ +static NoDbgRegParms AbstractInstruction * +rewriteInlineCacheTagat(AbstractInstruction *self_in_CogARMv8Compiler, sqInt cacheTag, sqInt callSiteReturnAddress) +{ + error("should not happen"); + return self_in_CogARMv8Compiler; +} + + +/* Set the size field. The only complication is that in Smalltalk (operands + at: 1) may be nil. */ + + /* CogARMv8Compiler>>#setLiteralSize: */ +static NoDbgRegParms AbstractInstruction * +setLiteralSize(AbstractInstruction *self_in_CogARMv8Compiler, sqInt sizeOfLiteral) +{ + sqInt operand; + + ((self_in_CogARMv8Compiler->operands))[1] = ((((operand = ((self_in_CogARMv8Compiler->operands))[1]) + ? ((operand | 30) - 30) + : 0)) + ((((usqInt)(sizeOfLiteral) << 1)))); + return self_in_CogARMv8Compiler; +} + + +/* to save Slang from having to be a real compiler (it can't inline switches + that return) + */ +/* Answer if the receiver's opcode sets the condition codes correctly for the + given conditional jump opcode. + ARMv8 appears not to set condition codes at all in its shift + instruction(s), which are aliases for + SBFM (signed bit field move) and C6.2.332 UBFM. + + C6.2.232 SBFM C6-1170 + C6.2.332 UBFM C6-1351 */ + + /* CogARMv8Compiler>>#setsConditionCodesFor: */ +static NoDbgRegParms sqInt +setsConditionCodesFor(AbstractInstruction *self_in_CogARMv8Compiler, sqInt aConditionalJumpOpcode) +{ + return 0; +} + + +/* Size a jump and set its address. The target may be another instruction + or an absolute address. On entry the address inst var holds our virtual + address. On exit address is set to eventualAbsoluteAddress, which is + where this instruction will be output. The span of a jump to a following + instruction is therefore between that instruction's address and this + instruction's address ((which are both still their virtual addresses), but + the span of a jump to a preceding instruction or to an absolute address is + between that instruction's address (which by now is its eventual absolute + address) or absolute address and eventualAbsoluteAddress. + + ARMv8 is simple; the 26-bit call/jump range (for a signed 28 bit extent, + +/- 128Mb) and + 19 bit conditional branch range (for a signed 21 bit extent, +/- 1Mb) + means no short + jumps. This routine only has to determine the targets of jumps, not + determine sizes. + + This version also deals with out-of-line literals. If this is the real + literal, update the stand-in in literalsManager with the address (because + instructions referring to the literal are referring to the stand-in). If + this is annotated with + IsObjectReference transfer the annotation to the stand-in, whence it will + be transferred to the real literal, simplifying update of literals. */ + + /* CogARMv8Compiler>>#sizePCDependentInstructionAt: */ +static NoDbgRegParms unsigned char +sizePCDependentInstructionAt(AbstractInstruction *self_in_CogARMv8Compiler, sqInt eventualAbsoluteAddress) +{ + sqInt alignment; + + if (((self_in_CogARMv8Compiler->opcode)) == AlignmentNops) { + (self_in_CogARMv8Compiler->address) = eventualAbsoluteAddress; + alignment = ((self_in_CogARMv8Compiler->operands))[0]; + return ((self_in_CogARMv8Compiler->machineCodeSize) = ((eventualAbsoluteAddress + (alignment - 1)) & (-alignment)) - eventualAbsoluteAddress); + } + assert((isJump(self_in_CogARMv8Compiler)) + || ((((self_in_CogARMv8Compiler->opcode)) == Call) + || ((((self_in_CogARMv8Compiler->opcode)) == CallFull) + || ((((self_in_CogARMv8Compiler->dependent))) + && (((((self_in_CogARMv8Compiler->dependent))->opcode)) == Literal))))); + if (isJump(self_in_CogARMv8Compiler)) { + resolveJumpTarget(self_in_CogARMv8Compiler); + } + (self_in_CogARMv8Compiler->address) = eventualAbsoluteAddress; + if ((((self_in_CogARMv8Compiler->dependent))) + && (((((self_in_CogARMv8Compiler->dependent))->opcode)) == Literal)) { + if (((self_in_CogARMv8Compiler->opcode)) == Literal) { + (((self_in_CogARMv8Compiler->dependent))->address = (self_in_CogARMv8Compiler->address)); + } + if (((self_in_CogARMv8Compiler->annotation)) == (getIsObjectReference())) { + (((self_in_CogARMv8Compiler->dependent))->annotation = (self_in_CogARMv8Compiler->annotation)); + (self_in_CogARMv8Compiler->annotation) = null; + } + } + return ((self_in_CogARMv8Compiler->machineCodeSize) = (self_in_CogARMv8Compiler->maxSize)); +} + + /* CogARMv8Compiler>>#stopsFrom:to: */ +static NoDbgRegParms AbstractInstruction * +stopsFromto(AbstractInstruction *self_in_CogARMv8Compiler, sqInt startAddr, sqInt endAddr) +{ + sqInt addr; + + assert((((endAddr - startAddr) + 1) % 4) == 0); + for (addr = startAddr; addr <= endAddr; addr += 4) { + codeLong32Atput((void *)(addr),0xD4400000U /* stop */); + } + return self_in_CogARMv8Compiler; +} + + +/* Rewrite the 32-bit literal in the instruction immediately preceding + followingAddress. + */ + + /* CogARMv8Compiler>>#storeLiteral32:beforeFollowingAddress: */ +static NoDbgRegParms AbstractInstruction * +storeLiteral32beforeFollowingAddress(AbstractInstruction *self_in_CogARMv8Compiler, sqInt literal, sqInt followingAddress) +{ + codeLong32Atput((void *)(pcRelativeAddressAt(self_in_CogARMv8Compiler, instructionAddressBefore(self_in_CogARMv8Compiler, (instructionIsPCRelativeLoad(self_in_CogARMv8Compiler, long32At((void *)(followingAddress - 4))) + ? followingAddress + : followingAddress - 4)))),literal); + return self_in_CogARMv8Compiler; +} + + +/* Rewrite the literal in the instruction immediately preceding + followingAddress. + */ + + /* CogARMv8Compiler>>#storeLiteral:beforeFollowingAddress: */ +static NoDbgRegParms AbstractInstruction * +storeLiteralbeforeFollowingAddress(AbstractInstruction *self_in_CogARMv8Compiler, sqInt literal, sqInt followingAddress) +{ + codeLongAtput((void *)(pcRelativeAddressAt(self_in_CogARMv8Compiler, instructionAddressBefore(self_in_CogARMv8Compiler, (instructionIsPCRelativeLoad(self_in_CogARMv8Compiler, long32At((void *)(followingAddress - 4))) + ? followingAddress + : followingAddress - 4)))),literal); + return self_in_CogARMv8Compiler; +} + + +/* C6.2.273 STR (immediate) C6-1239 + C6.2.275 STRB (immediate) C6-1244 */ + + /* CogARMv8Compiler>>#st:rn:rt:imm:shiftBy12: */ +static NoDbgRegParms sqInt +strnrtimmshiftBy12(AbstractInstruction *self_in_CogARMv8Compiler, sqInt unitSizeLog2MinusOne, sqInt baseReg, sqInt targetReg, sqInt offset, sqInt shiftBy12) +{ + sqInt unitSize; + + unitSize = 1ULL << unitSizeLog2MinusOne; + assert(!((SP == targetReg))); + assert(!((baseReg == targetReg))); + + /* Unsigned offset, C6-1240 */ + if (((offset % unitSize) == 0) + && ((((offset / unitSize) >= 0) && ((offset / unitSize) <= (0xFFF))))) { + return (((((((usqInt)(unitSizeLog2MinusOne) << 30))) + (0x39000000)) + ((((usqInt)(offset) << (10 - unitSizeLog2MinusOne))))) + ((((usqInt)(baseReg) << 5)))) + targetReg; + } + + /* Unscaled signed 9-bit offset, C6-1244 */ + if (!(asserta(((offset >= -256) && (offset <= 0xFF))))) { + error("unhandled immediate offset in store"); + } + return (((((((usqInt)(unitSizeLog2MinusOne) << 30))) + (0x38000000)) + (((offset & 0x1FF) << 12))) + ((((usqInt)(baseReg) << 5)))) + targetReg; +} + + +/* Answer if the receiver uses an out-of-line literal. Needs only to work for + the opcodes created with gen:literal:, gen:literal:operand:, + gen:operand:literal: */ + + /* CogARMv8Compiler>>#usesOutOfLineLiteral */ +static NoDbgRegParms sqInt +usesOutOfLineLiteral(AbstractInstruction *self_in_CogARMv8Compiler) +{ + sqInt constant; + sqInt imm; + sqInt immediate; + sqInt immrSqInt; + usqInt mask; + usqInt nImms; + usqInt nUsqInt; + sqInt numLeadingOnes; + sqInt numTrailingOnes; + sqInt rotateCount; + sqInt size; + + switch ((self_in_CogARMv8Compiler->opcode)) { + case CallFull: + case JumpFull: + case AddCwR: + case AndCwR: + case CmpCwR: + case CmpC32R: + case OrCwR: + case SubCwR: + case XorCwR: + case MoveC32R: + return 1; + + case AddCqR: + case AddCqRR: + case SubCqR: + case CmpCqR: + case LoadEffectiveAddressMwrR: + immediate = ((sqLong) (((self_in_CogARMv8Compiler->operands))[0])); + + /* begin isPossiblyShiftableNegatableImm12:ifTrue:ifFalse: */ + if (((immediate >= (-4096)) && (immediate <= (0xFFF)))) { + return 0; + } + if (((!(immediate & (0xFFF)))) + && (((((immediate) >> 12) >= (-4096)) && (((immediate) >> 12) <= (0xFFF))))) { + return 0; + } + return 1; + + case AndCqR: + case AndCqRR: + case OrCqRR: + case OrCqR: + case TstCqR: + case XorCqR: + constant = ((self_in_CogARMv8Compiler->operands))[0]; + + /* begin isImmNImmSImmREncodableBitmask:ifTrue:ifFalse: */ + if (((constant >= -1) && (constant <= 0))) { + return 1; + } + imm = constant; + + /* First, determine the element size. */ + size = 32; + while (1) { + mask = (1ULL << size) - 1; + if (!(((imm & mask) != ((((usqInt)(imm)) >> size) & mask) + ? ((size = size * 2), + 0) + : size > 2))) break; + size = size / 2; + } + + /* Second, determine the rotation to make the element be: 0^m 1^n. */ + mask = ((usqInt)((0xFFFFFFFFFFFFFFFFULL))) >> (64 - size); + imm = imm & mask; + if (isShiftedMask(self_in_CogARMv8Compiler, imm)) { + rotateCount = countTrailingZeros(self_in_CogARMv8Compiler, imm); + numTrailingOnes = countTrailingOnes(self_in_CogARMv8Compiler, ((usqInt)(imm)) >> rotateCount); + } + else { + imm = imm | (~(usqIntptr_t)mask); + if (!(isShiftedMask(self_in_CogARMv8Compiler, imm))) { + return 1; + } + numLeadingOnes = countLeadingOnes(self_in_CogARMv8Compiler, imm); + rotateCount = 64 - numLeadingOnes; + numTrailingOnes = (numLeadingOnes + (countTrailingOnes(self_in_CogARMv8Compiler, imm))) - (64 - size); + } + + /* Encode in Immr the number of RORs it would take to get *from* 0^m 1^n + to our target value, where I is the number of RORs to go the opposite direction. */ + assert(size > rotateCount); + immrSqInt = (size - rotateCount) & (size - 1); + + /* If size has a 1 in the n'th bit, create a value that has zeroes in bits [0, n] and ones above that. */ + nImms = ((sqInt)((usqInt)((~(usqIntptr_t)(size - 1))) << 1)); + + /* Or the CTO value into the low bits, which must be below the Nth bit mentioned above. */ + nImms = nImms | (numTrailingOnes - 1); + + /* Extract the seventh bit and toggle it to create the N field. */ + nUsqInt = (((nImms) >> 6) & 1) ^ 1; + nImms = nImms & 0x3F; + assert((decodeNimmsimmr(self_in_CogARMv8Compiler, nUsqInt, nImms, immrSqInt)) == (((usqInt) constant))); + return 0; + + case MoveCqR: + case PushCq: + if ((((((sqLong) (((self_in_CogARMv8Compiler->operands))[0]))) >= (-65536)) && ((((sqLong) (((self_in_CogARMv8Compiler->operands))[0]))) <= (0xFFFF)))) { + return 0; + } + immediate = ((sqLong) (((self_in_CogARMv8Compiler->operands))[0])); + + /* begin isPossiblyShiftableImm12:ifTrue:ifFalse: */ + if (((immediate >= 0) && (immediate <= (0xFFF)))) { + return 0; + } + if (((!(immediate & (0xFFF)))) + && (((((immediate) >> 12) >= 0) && (((immediate) >> 12) <= (0xFFF))))) { + return 0; + } + /* end isPossiblyShiftableImm12:ifTrue:ifFalse: */ +l1: + immediate = -(((sqLong) (((self_in_CogARMv8Compiler->operands))[0]))); + + /* begin isPossiblyShiftableImm12:ifTrue:ifFalse: */ + if (((immediate >= 0) && (immediate <= (0xFFF)))) { + return 0; + } + if (((!(immediate & (0xFFF)))) + && (((((immediate) >> 12) >= 0) && (((immediate) >> 12) <= (0xFFF))))) { + return 0; + } + /* end isPossiblyShiftableImm12:ifTrue:ifFalse: */ +l2: + constant = ((sqLong) (((self_in_CogARMv8Compiler->operands))[0])); + + /* begin isImmNImmSImmREncodableBitmask:ifTrue:ifFalse: */ + if (((constant >= -1) && (constant <= 0))) { + return 1; + } + imm = constant; + + /* First, determine the element size. */ + size = 32; + while (1) { + mask = (1ULL << size) - 1; + if (!(((imm & mask) != ((((usqInt)(imm)) >> size) & mask) + ? ((size = size * 2), + 0) + : size > 2))) break; + size = size / 2; + } + + /* Second, determine the rotation to make the element be: 0^m 1^n. */ + mask = ((usqInt)((0xFFFFFFFFFFFFFFFFULL))) >> (64 - size); + imm = imm & mask; + if (isShiftedMask(self_in_CogARMv8Compiler, imm)) { + rotateCount = countTrailingZeros(self_in_CogARMv8Compiler, imm); + numTrailingOnes = countTrailingOnes(self_in_CogARMv8Compiler, ((usqInt)(imm)) >> rotateCount); + } + else { + imm = imm | (~(usqIntptr_t)mask); + if (!(isShiftedMask(self_in_CogARMv8Compiler, imm))) { + return 1; + } + numLeadingOnes = countLeadingOnes(self_in_CogARMv8Compiler, imm); + rotateCount = 64 - numLeadingOnes; + numTrailingOnes = (numLeadingOnes + (countTrailingOnes(self_in_CogARMv8Compiler, imm))) - (64 - size); + } + + /* Encode in Immr the number of RORs it would take to get *from* 0^m 1^n + to our target value, where I is the number of RORs to go the opposite direction. */ + assert(size > rotateCount); + immrSqInt = (size - rotateCount) & (size - 1); + + /* If size has a 1 in the n'th bit, create a value that has zeroes in bits [0, n] and ones above that. */ + nImms = ((sqInt)((usqInt)((~(usqIntptr_t)(size - 1))) << 1)); + + /* Or the CTO value into the low bits, which must be below the Nth bit mentioned above. */ + nImms = nImms | (numTrailingOnes - 1); + + /* Extract the seventh bit and toggle it to create the N field. */ + nUsqInt = (((nImms) >> 6) & 1) ^ 1; + nImms = nImms & 0x3F; + assert((decodeNimmsimmr(self_in_CogARMv8Compiler, nUsqInt, nImms, immrSqInt)) == (((usqInt) constant))); + return 0; + + case MoveCwR: + case PushCw: + return !(/* inCurrentCompilation: */ + (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) (((self_in_CogARMv8Compiler->operands))[0])))) + || ((((AbstractInstruction *) (((self_in_CogARMv8Compiler->operands))[0]))) == (methodLabel))) + || (/* addressIsInCurrentCompilation: */ + ((((usqInt)(((self_in_CogARMv8Compiler->operands))[0]))) >= ((methodLabel->address))) + && ((((usqInt)(((self_in_CogARMv8Compiler->operands))[0]))) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize)))))); + + case MoveAwR: + case MoveAbR: + case PrefetchAw: + return !((((((self_in_CogARMv8Compiler->operands))[0]) >= ((varBaseAddress) - (0x100))) && ((((self_in_CogARMv8Compiler->operands))[0]) <= (((varBaseAddress) + (0x1000)) - 1)))); + + case MoveRAw: + case MoveRAb: + return !((((((self_in_CogARMv8Compiler->operands))[1]) >= ((varBaseAddress) - (0x100))) && ((((self_in_CogARMv8Compiler->operands))[1]) <= (((varBaseAddress) + (0x1000)) - 1)))); + + case MoveMwrR: + case MoveMbrR: + case MoveM16rR: + return !(isImm12orImm9offset(self_in_CogARMv8Compiler, ((sqLong) (((self_in_CogARMv8Compiler->operands))[0])))); + + case MoveRMwr: + case MoveRMbr: + case MoveRM16r: + immediate = ((sqLong) (((self_in_CogARMv8Compiler->operands))[1])); + + /* begin isPossiblyShiftableNegatableImm12:ifTrue:ifFalse: */ + if (((immediate >= (-4096)) && (immediate <= (0xFFF)))) { + return 0; + } + if (((!(immediate & (0xFFF)))) + && (((((immediate) >> 12) >= (-4096)) && (((immediate) >> 12) <= (0xFFF))))) { + return 0; + } + return 1; + + case MoveRdM64r: + return !(isUnsigned12BitMultipleOf8(self_in_CogARMv8Compiler, ((self_in_CogARMv8Compiler->operands))[1])); + + case MoveM64rRd: + return !(isUnsigned12BitMultipleOf8(self_in_CogARMv8Compiler, ((self_in_CogARMv8Compiler->operands))[0])); + + default: + assert(0); + } + + /* Arithmetic + Data Movement */ + return 0; +} + + +/* C6.2.131 LDR (literal) C6-979 */ + + /* CogARMv8Compiler>>#widthOfPCRelativeLoad: */ +static NoDbgRegParms sqInt +widthOfPCRelativeLoad(AbstractInstruction *self_in_CogARMv8Compiler, sqInt instr) +{ + return (((((usqInt)(instr)) >> 30)) == 1 + ? 8 + : 4); +} + + +/* Answer if Call and JumpLong are relative and hence need to take the + caller's relocation delta into account during code compaction, rather than + just the + callee's delta. */ + + /* CogARMv8Compiler>>#zoneCallsAreRelative */ +static NoDbgRegParms sqInt +zoneCallsAreRelative(AbstractInstruction *self_in_CogARMv8Compiler) +{ + return 1; +} + + /* CogBlockMethod>>#cmHomeMethod */ +static NoDbgRegParms CogMethod * +cmHomeMethod(CogBlockMethod *self_in_CogBlockMethod) +{ + return (((self_in_CogBlockMethod->cmType)) >= CMMethod + ? ((CogMethod *) self_in_CogBlockMethod) + : (assert(CMBlock == (self_in_CogBlockMethod->cmType)), + ((CogMethod *) ((((usqInt)self_in_CogBlockMethod)) - ((self_in_CogBlockMethod->homeOffset)))))); +} + + /* CogBlockMethod>>#isCMBlock */ +static NoDbgRegParms int +isCMBlock(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMBlock; +} + + /* CogBlockMethod>>#isCMClosedPIC */ +static NoDbgRegParms int +isCMClosedPIC(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMClosedPIC; +} + + /* CogBlockMethod>>#isCMFree */ +static NoDbgRegParms int +isCMFree(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMFree; +} + + /* CogBlockMethod>>#isCMMethodEtAl */ +static NoDbgRegParms int +isCMMethodEtAl(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) >= CMMethod; +} + + /* CogBlockMethod>>#isCMOpenPIC */ +static NoDbgRegParms int +isCMOpenPIC(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMOpenPIC; +} + + /* CogBytecodeDescriptor>>#isBranch */ +static NoDbgRegParms sqInt +isBranch(BytecodeDescriptor *self_in_CogBytecodeDescriptor) +{ + return (((self_in_CogBytecodeDescriptor->spanFunction))) + && (!((self_in_CogBytecodeDescriptor->isBlockCreation))); +} + + /* CogBytecodeDescriptor>>#isConditionalBranch */ +static NoDbgRegParms sqInt +isConditionalBranch(BytecodeDescriptor *self_in_CogBytecodeDescriptor) +{ + return ((self_in_CogBytecodeDescriptor->isBranchTrue)) + || ((self_in_CogBytecodeDescriptor->isBranchFalse)); +} + + /* Cogit>>#AddCq:R: */ +static NoDbgRegParms AbstractInstruction * +gAddCqR(sqInt quickConstant, sqInt reg) +{ + AbstractInstruction *anInstruction; + + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return anInstruction; +} + + +/* N.B. if the condition codes don't require setting and three address + arithmetic is unavailable, then LoadEffectiveAddressMw:r:R: can be used + instead. + */ + + /* Cogit>>#AddCq:R:R: */ +static NoDbgRegParms AbstractInstruction * +gAddCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *anInstruction; + + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(AddCqRR, quickConstant, srcReg, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return anInstruction; +} + + /* Cogit>>#AndCq:R: */ +static NoDbgRegParms AbstractInstruction * +gAndCqR(sqInt quickConstant, sqInt reg) +{ + AbstractInstruction *anInstruction; + + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return anInstruction; +} + + /* Cogit>>#AndCq:R:R: */ +static NoDbgRegParms AbstractInstruction * +gAndCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *anInstruction; + + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(AndCqRR, quickConstant, srcReg, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return anInstruction; +} + + +/* destReg := (signed)srcReg >> quickConstant */ + + /* Cogit>>#ArithmeticShiftRightCq:R:R: */ +static NoDbgRegParms AbstractInstruction * +gArithmeticShiftRightCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg) +{ + return genoperandoperandoperand(ArithmeticShiftRightCqRR, quickConstant, srcReg, destReg); +} + + /* Cogit>>#abortOffset */ +sqInt +abortOffset(void) +{ + return missOffset; +} + + /* Cogit>>#addCleanBlockStarts */ +static void +addCleanBlockStarts(void) +{ + sqInt i; + sqInt lit; + sqInt startPCOrNil; + sqInt toDoLimit; + + toDoLimit = literalCountOf(methodObj); + for (i = 1; i <= toDoLimit; i += 1) { + lit = fetchPointerofObject(i, methodObj); + if ((startPCOrNil = startPCOrNilOfLiteralin(lit, methodObj))) { + maxLitIndex = ((maxLitIndex < i) ? i : maxLitIndex); + addBlockStartAtnumArgsnumCopiedspan(startPCOrNil - 1, argumentCountOfClosure(lit), copiedValueCountOfClosure(lit), spanForCleanBlockStartingAt(startPCOrNil - 1)); + } + } +} + + +/* Perform an integrity/leak check using the heapMap. + Set a bit at each cog method's header. */ + + /* Cogit>>#addCogMethodsToHeapMap */ +void +addCogMethodsToHeapMap(void) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + heapMapAtWordPut(cogMethod, 1); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } +} + + /* Cogit>>#addressIsInCurrentCompilation: */ +static NoDbgRegParms sqInt +addressIsInCurrentCompilation(sqInt address) +{ + return ((((usqInt)address)) >= ((methodLabel->address))) + && ((((usqInt)address)) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize)))); +} + + /* Cogit>>#addressIsInFixups: */ +static NoDbgRegParms sqInt +addressIsInFixups(BytecodeFixup *address) +{ + return (BytecodeFixup *)address >= fixups && (BytecodeFixup *)address < (fixups + numAbstractOpcodes); +} + + +/* calculate the end of the n'th case statement - which is complicated + because we have case 1 right at the top of our CPIC and then build up from + the last one. Yes I know this sounds strange, but trust me - I'm an + Engineer, we do things backwards all the emit + */ + + /* Cogit>>#addressOfEndOfCase:inCPIC: */ +static NoDbgRegParms sqInt +addressOfEndOfCaseinCPIC(sqInt n, CogMethod *cPIC) +{ + assert((n >= 1) + && (n <= MaxCPICCases)); + return (n == 1 + ? (((sqInt)cPIC)) + firstCPICCaseOffset + : ((((sqInt)cPIC)) + firstCPICCaseOffset) + (((MaxCPICCases + 1) - n) * cPICCaseSize)); +} + + +/* Align methodZoneBase to that for the start of a method. */ + + /* Cogit>>#alignMethodZoneBase */ +static void +alignMethodZoneBase(void) +{ + usqInt oldBase; + + oldBase = methodZoneBase; + methodZoneBase = roundUpToMethodAlignment(backEnd, methodZoneBase); + stopsFromto(backEnd, oldBase, methodZoneBase - 1); +} + + /* Cogit>>#alignUptoRoutineBoundary: */ +static NoDbgRegParms sqInt +alignUptoRoutineBoundary(sqInt anAddress) +{ + return (((anAddress + 7) | 7) - 7); +} + + +/* Check that all methods have valid selectors, and that all linked sends are + to valid targets and have valid cache tags + */ + + /* Cogit>>#allMachineCodeObjectReferencesValid */ +static sqInt +allMachineCodeObjectReferencesValid(void) +{ + CogMethod *cogMethod; + sqInt ok; + + ok = 1; + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (!(((cogMethod->cmType)) == CMFree)) { + if (!(asserta(checkValidOopReference((cogMethod->selector))))) { + ok = 0; + } + if (!(asserta((cogMethodDoesntLookKosher(cogMethod)) == 0))) { + ok = 0; + } + } + if ((((cogMethod->cmType)) >= CMMethod) + || (((cogMethod->cmType)) == CMOpenPIC)) { + if (!(asserta((mapForperformUntilarg(cogMethod, checkIfValidOopRefAndTargetpccogMethod, cogMethod)) == 0))) { + ok = 0; + } + } + if (((cogMethod->cmType)) == CMClosedPIC) { + if (!(asserta(noTargetsFreeInClosedPIC(cogMethod)))) { + ok = 0; + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + return ok; +} + + /* Cogit>>#allMethodsHaveCorrectHeader */ +static sqInt +allMethodsHaveCorrectHeader(void) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + if (!(((cogMethod->objectHeader)) == (nullHeaderForMachineCodeMethod()))) { + return 0; + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + return 1; +} + + /* Cogit>>#annotateAbsolutePCRef: */ +static NoDbgRegParms AbstractInstruction * +annotateAbsolutePCRef(AbstractInstruction *abstractInstruction) +{ + (abstractInstruction->annotation = IsAbsPCReference); + return abstractInstruction; +} + + /* Cogit>>#annotateBytecode: */ +static NoDbgRegParms AbstractInstruction * +annotateBytecode(AbstractInstruction *abstractInstruction) +{ + (abstractInstruction->annotation = HasBytecodePC); + return abstractInstruction; +} + + /* Cogit>>#annotate:objRef: */ +static NoDbgRegParms AbstractInstruction * +annotateobjRef(AbstractInstruction *abstractInstruction, sqInt anOop) +{ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(anOop)) + && ((oopisGreaterThan(anOop, classTableRootObj())) + || (oopisLessThan(anOop, nilObject())))) { + setHasMovableLiteral(1); + if (isYoungObject(anOop)) { + setHasYoungReferent(1); + } + (abstractInstruction->annotation = IsObjectReference); + } + return abstractInstruction; +} + + /* Cogit>>#assertSaneJumpTarget: */ +static NoDbgRegParms void +assertSaneJumpTarget(AbstractInstruction *jumpTarget) +{ + assert((!closedPICSize) + || ((!openPICSize) + || ((addressIsInInstructions(jumpTarget)) + || ((((((usqInt)jumpTarget)) >= codeBase) && ((((usqInt)jumpTarget)) <= ((((sqInt)(limitZony()))) + (((closedPICSize < openPICSize) ? openPICSize : closedPICSize))))))))); +} + + +/* Answer an unused abstract register in the registerMask, or NoReg if none. */ + + /* Cogit>>#availableRegisterOrNoneIn: */ +static NoDbgRegParms sqInt +availableRegisterOrNoneIn(sqInt liveRegsMask) +{ + sqInt reg; + + if (liveRegsMask) { + for (reg = 0; reg <= 0x1F; reg += 1) { + if (((liveRegsMask & (1ULL << reg)) != 0)) { + return reg; + } + } + } + return NoReg; +} + + +/* Evaluate binaryFunction with the block start mcpc and supplied arg for + each entry in the block dispatch. If the function answers non-zero answer + the value + it answered. Used to update back-references to the home method in + compaction. */ + + /* Cogit>>#blockDispatchTargetsFor:perform:arg: */ +static NoDbgRegParms usqInt +blockDispatchTargetsForperformarg(CogMethod *cogMethod, usqInt (*binaryFunction)(sqInt mcpc, sqInt arg), sqInt arg) +{ + sqInt blockEntry; + sqInt end; + sqInt pc; + usqInt result; + usqInt targetpc; + + if (!((cogMethod->blockEntryOffset))) { + return null; + } + blockEntry = ((cogMethod->blockEntryOffset)) + (((sqInt)cogMethod)); + pc = blockEntry; + end = (mapEndFor(cogMethod)) - 1; + while (pc < end) { + if (isJumpAt(backEnd, pc)) { + targetpc = jumpTargetPCAt(backEnd, pc); + if (targetpc < blockEntry) { + result = binaryFunction(targetpc, arg); + if (result) { + return result; + } + } + } + pc += instructionSizeAt(backEnd, pc); + } + return 0; +} + + +/* Answer the zero-relative bytecode pc matching the machine code pc argument + in cogMethod, given the start of the bytecodes for cogMethod's block or + method object. */ + + /* Cogit>>#bytecodePCFor:startBcpc:in: */ +sqInt +bytecodePCForstartBcpcin(sqInt mcpc, sqInt startbcpc, CogBlockMethod *cogMethod) +{ + sqInt aMethodObj; + sqInt annotation; + sqInt bcpc; + sqInt bsOffset; + sqInt byte; + BytecodeDescriptor *descriptor; + sqInt distance; + sqInt endbcpc; + CogMethod *homeMethod; + sqInt isBackwardBranch; + sqInt isInBlock; + sqInt latestContinuation; + sqInt map; + sqInt mapByte; + usqInt mcpcUsqInt; + sqInt nExts; + sqInt nextBcpc; + sqInt result; + sqInt targetPC; + + /* begin mapFor:bcpc:performUntil:arg: */ + descriptor = ((BytecodeDescriptor *) 0); + latestContinuation = 0; + mapByte = 0; + nextBcpc = 0; + assert(((cogMethod->stackCheckOffset)) > 0); + mcpcUsqInt = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + + /* The stack check maps to the start of the first bytecode, + the first bytecode being effectively after frame build. */ + result = findIsBackwardBranchMcpcBcpcMatchingMcpc(null, 0 + ((((usqInt)(HasBytecodePC) << 1))), ((char *) mcpcUsqInt), startbcpc, ((void *)mcpc)); + if (result) { + return result; + } + bcpc = startbcpc; + + /* In both CMMethod and CMBlock cases find the start of the map and + skip forward to the bytecode pc map entry for the stack check. */ + if (((cogMethod->cmType)) >= CMMethod) { + isInBlock = (cogMethod->cpicHasMNUCaseOrCMIsFullBlock); + homeMethod = ((CogMethod *) cogMethod); + assert(startbcpc == (startPCOfMethodHeader((homeMethod->methodHeader)))); + map = ((((usqInt)homeMethod)) + ((homeMethod->blockSize))) - 1; + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert((annotation == IsAbsPCReference) + || ((annotation == IsObjectReference) + || ((annotation == IsRelativeCall) + || (annotation == IsDisplacementX2N)))); + latestContinuation = startbcpc; + aMethodObj = (homeMethod->methodObject); + endbcpc = (numBytesOf(aMethodObj)) - 1; + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + + /* If the method has a primitive, skip it and the error code store, if any; + Logically. these come before the stack check and so must be ignored. */ + bcpc += deltaToSkipPrimAndErrorStoreInheader(aMethodObj, (homeMethod->methodHeader)); + } + else { + isInBlock = 1; + assert(bcpc == ((cogMethod->startpc))); + homeMethod = cmHomeMethod(cogMethod); + map = findMapLocationForMcpcinMethod((((usqInt)cogMethod)) + (sizeof(CogBlockMethod)), homeMethod); + assert(map != 0); + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert(((((usqInt)(annotation)) >> AnnotationShift) == HasBytecodePC) + || ((((usqInt)(annotation)) >> AnnotationShift) == IsDisplacementX2N)); + + /* fiducial */ + while (((annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift)) != HasBytecodePC) { + map -= 1; + } + + /* skip fiducial; i.e. the map entry for the pc immediately following the method header. */ + map -= 1; + aMethodObj = (homeMethod->methodObject); + bcpc = startbcpc - (/* blockCreationBytecodeSizeForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? AltBlockCreationBytecodeSize + : BlockCreationBytecodeSize)); + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + endbcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, -1, aMethodObj) + : 0)); + bcpc = startbcpc; + } + nExts = 0; + enumeratingCogMethod = homeMethod; + + /* Now skip up through the bytecode pc map entry for the stack check. */ + while ((((usqInt)((byteAt((void *)(map))))) >> AnnotationShift) != HasBytecodePC) { + map -= 1; + } + map -= 1; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + mcpcUsqInt += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + if (annotation >= HasBytecodePC) { + if ((annotation == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + while (1) { + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + if (isInBlock) { + if (bcpc >= endbcpc) { + return 0; + } + } + else { + if (((descriptor->isReturn)) + && (bcpc >= latestContinuation)) { + return 0; + } + if ((isBranch(descriptor)) + || ((descriptor->isBlockCreation))) { + /* begin latestContinuationPCFor:at:exts:in: */ + distance = ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj); + targetPC = (bcpc + ((descriptor->numBytes))) + (((distance < 0) ? 0 : distance)); + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + } + } + nextBcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj) + : 0)); + if (((descriptor->isMapped)) + || (isInBlock + && ((descriptor->isMappedInBlock)))) break; + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + isBackwardBranch = (isBranch(descriptor)) + && ((/* begin isBackwardBranch:at:exts:in: */ + assert(((descriptor->spanFunction))), + (((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj)) < 0)); + result = findIsBackwardBranchMcpcBcpcMatchingMcpc(descriptor, (isBackwardBranch + ? ((((usqInt)(annotation) << 1))) + 1 + : ((sqInt)((usqInt)(annotation) << 1))), ((char *) mcpcUsqInt), (isBackwardBranch + ? bcpc - (2 * nExts) + : bcpc), ((void *)mcpc)); + if (result) { + return result; + } + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + } + else { + assert(((((usqInt)(mapByte)) >> AnnotationShift) == IsDisplacementX2N) + || ((((usqInt)(mapByte)) >> AnnotationShift) == IsAnnotationExtension)); + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpcUsqInt += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + + /* defensive; we exit on bcpc */ + return 0; +} + + /* Cogit>>#CmpCq:R: */ +static NoDbgRegParms AbstractInstruction * +gCmpCqR(sqInt quickConstant, sqInt reg) +{ + AbstractInstruction *anInstruction; + + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return anInstruction; +} + + +/* This is a static version of ceCallCogCodePopReceiverReg for break-pointing + when debugging in C. Marked so the code generator won't delete it. */ + + /* Cogit>>#callCogCodePopReceiver */ +static void +callCogCodePopReceiver(void) +{ + realCECallCogCodePopReceiverReg(); + + /* (and this exists only to reference Debug) */ + if (!Debug) { + error("what??"); + } +} + + +/* This is a static version of ceCallCogCodePopReceiverAndClassRegs for + break-pointing when debugging in C. Marked so the code generator + won't delete it. */ + + /* Cogit>>#callCogCodePopReceiverAndClassRegs */ +static void +callCogCodePopReceiverAndClassRegs(void) +{ + realCECallCogCodePopReceiverAndClassRegs(); +} + + +/* Code entry closed PIC miss. A send has fallen + through a closed (finite) polymorphic inline cache. + Either extend it or patch the send site to an open PIC. + The stack looks like: + receiver + args + sp=> sender return address */ +/* Marked so the code generator won't delete it. */ + + /* Cogit>>#ceCPICMiss:receiver: */ +static NoDbgRegParms sqInt +ceCPICMissreceiver(CogMethod *cPIC, sqInt receiver) +{ + sqInt cacheTag; + int errorSelectorOrNil; + sqInt methodOrSelectorIndex; + sqInt newTargetMethodOrNil; + sqInt outerReturn; + sqInt result; + sqInt selector; + + assert(addressCouldBeOop(receiver)); + if (isOopForwarded(receiver)) { + return ceSendFromInLineCacheMiss(cPIC); + } + outerReturn = stackTop(); + assert(!(((inlineCacheTagAt(backEnd, outerReturn)) == (picAbortDiscriminatorValue())))); + if (((cPIC->cPICNumCases)) < MaxCPICCases) { + selector = (cPIC->selector); + + /* begin lookup:for:methodAndErrorSelectorInto: */ + methodOrSelectorIndex = lookupOrdinaryreceiver(selector, receiver); + if ((((usqInt)methodOrSelectorIndex)) > (maxLookupNoMNUErrorCode())) { + if (!(isOopCompiledMethod(methodOrSelectorIndex))) { + newTargetMethodOrNil = methodOrSelectorIndex; + errorSelectorOrNil = SelectorCannotInterpret; + goto l1; + } + if ((!(methodHasCogMethod(methodOrSelectorIndex))) + && (methodShouldBeCogged(methodOrSelectorIndex))) { + /* We assume cog:selector: will *not* reclaim the method zone */ + cogselector(methodOrSelectorIndex, selector); + } + newTargetMethodOrNil = methodOrSelectorIndex; + errorSelectorOrNil = null; + goto l1; + } + if (methodOrSelectorIndex == SelectorDoesNotUnderstand) { + methodOrSelectorIndex = lookupMNUreceiver(splObj(SelectorDoesNotUnderstand), receiver); + if ((((usqInt)methodOrSelectorIndex)) > (maxLookupNoMNUErrorCode())) { + assert(isOopCompiledMethod(methodOrSelectorIndex)); + if ((!(methodHasCogMethod(methodOrSelectorIndex))) + && (methodShouldBeCogged(methodOrSelectorIndex))) { + /* We assume cog:selector: will *not* reclaim the method zone */ + cogselector(methodOrSelectorIndex, splObj(SelectorDoesNotUnderstand)); + } + newTargetMethodOrNil = methodOrSelectorIndex; + errorSelectorOrNil = SelectorDoesNotUnderstand; + goto l1; + } + newTargetMethodOrNil = null; + errorSelectorOrNil = SelectorDoesNotUnderstand; + goto l1; + } + newTargetMethodOrNil = null; + errorSelectorOrNil = methodOrSelectorIndex; + /* end lookup:for:methodAndErrorSelectorInto: */ +l1:; + } + else { + newTargetMethodOrNil = (errorSelectorOrNil = null); + } + + /* We assume lookupAndCog:for: will *not* reclaim the method zone */ + assert(outerReturn == (stackTop())); + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + cacheTag = inlineCacheTagForInstance(receiver); + if ((((cPIC->cPICNumCases)) >= MaxCPICCases) + || (/* closedPICInappropriateForCacheTag:targetMethod:orErrorSelector: */ + ((errorSelectorOrNil) + && (errorSelectorOrNil != SelectorDoesNotUnderstand)) + || ((!newTargetMethodOrNil) + || (isYoung(newTargetMethodOrNil))))) { + result = patchToOpenPICFornumArgsreceiver((cPIC->selector), (cPIC->cmNumArgs), receiver); + assert(!result); + + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } + return ceSendFromInLineCacheMiss(cPIC); + } + + /* Now extend the PIC with the new case. */ + cogExtendPICCaseNMethodtagisMNUCase(cPIC, newTargetMethodOrNil, cacheTag, errorSelectorOrNil == SelectorDoesNotUnderstand); + + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } + + /* Jump back into the pic at its entry in case this is an MNU. */ + executeCogPICfromLinkedSendWithReceiverandCacheTag(cPIC, receiver, inlineCacheTagAt(backEnd, outerReturn)); + + /* NOTREACHED */ + return null; +} + + +/* An in-line cache check in a method has failed. The failing entry check has + jumped to the ceMethodAbort abort call at the start of the method which + has called this routine. + If possible allocate a closed PIC for the current and existing classes. + The stack looks like: + receiver + args + sender return address + sp=> ceMethodAbort call return address + So we can find the method that did the failing entry check at + ceMethodAbort call return address - missOffset + and we can find the send site from the outer return address. + Invoked from a trampoline. Marked so the code generator won't delete + it. */ + + /* Cogit>>#ceSICMiss: */ +static NoDbgRegParms sqInt +ceSICMiss(sqInt receiver) +{ + sqInt cacheTag; + unsigned int codeInstruction; + unsigned int dataInstruction; + int errorSelectorOrNil; + sqInt extent; + usqInt innerReturn; + sqInt methodOrSelectorIndex; + sqInt newTargetMethodOrNil; + usqInt outerReturn; + CogMethod *pic; + sqInt result; + sqInt selector; + CogMethod *targetMethod; + + assert(addressCouldBeOop(receiver)); + + /* Whether we can relink to a PIC or not we need to pop off the inner return and identify the target method. */ + innerReturn = ((usqInt)(popStack())); + targetMethod = ((CogMethod *) (innerReturn - missOffset)); + if (isOopForwarded(receiver)) { + return ceSendFromInLineCacheMiss(targetMethod); + } + outerReturn = ((usqInt)(stackTop())); + assert(((outerReturn >= methodZoneBase) && (outerReturn <= (freeStart())))); + assert(((targetMethod->selector)) != (nilObject())); + selector = (targetMethod->selector); + + /* begin lookup:for:methodAndErrorSelectorInto: */ + methodOrSelectorIndex = lookupOrdinaryreceiver(selector, receiver); + if ((((usqInt)methodOrSelectorIndex)) > (maxLookupNoMNUErrorCode())) { + if (!(isOopCompiledMethod(methodOrSelectorIndex))) { + newTargetMethodOrNil = methodOrSelectorIndex; + errorSelectorOrNil = SelectorCannotInterpret; + goto l1; + } + if ((!(methodHasCogMethod(methodOrSelectorIndex))) + && (methodShouldBeCogged(methodOrSelectorIndex))) { + /* We assume cog:selector: will *not* reclaim the method zone */ + cogselector(methodOrSelectorIndex, selector); + } + newTargetMethodOrNil = methodOrSelectorIndex; + errorSelectorOrNil = null; + goto l1; + } + if (methodOrSelectorIndex == SelectorDoesNotUnderstand) { + methodOrSelectorIndex = lookupMNUreceiver(splObj(SelectorDoesNotUnderstand), receiver); + if ((((usqInt)methodOrSelectorIndex)) > (maxLookupNoMNUErrorCode())) { + assert(isOopCompiledMethod(methodOrSelectorIndex)); + if ((!(methodHasCogMethod(methodOrSelectorIndex))) + && (methodShouldBeCogged(methodOrSelectorIndex))) { + /* We assume cog:selector: will *not* reclaim the method zone */ + cogselector(methodOrSelectorIndex, splObj(SelectorDoesNotUnderstand)); + } + newTargetMethodOrNil = methodOrSelectorIndex; + errorSelectorOrNil = SelectorDoesNotUnderstand; + goto l1; + } + newTargetMethodOrNil = null; + errorSelectorOrNil = SelectorDoesNotUnderstand; + goto l1; + } + newTargetMethodOrNil = null; + errorSelectorOrNil = methodOrSelectorIndex; + /* end lookup:for:methodAndErrorSelectorInto: */ +l1: + + /* We assume lookupAndCog:for: will *not* reclaim the method zone */ + assert(outerReturn == (stackTop())); + cacheTag = inlineCacheTagForInstance(receiver); + if ((/* closedPICInappropriateForCacheTag:targetMethod:orErrorSelector: */ + ((errorSelectorOrNil) + && (errorSelectorOrNil != SelectorDoesNotUnderstand)) + || ((!newTargetMethodOrNil) + || (isYoung(newTargetMethodOrNil)))) + || ((inlineCacheTagAt(backEnd, outerReturn)) == 0 /* picAbortDiscriminatorValue */)) { + result = patchToOpenPICFornumArgsreceiver((targetMethod->selector), (targetMethod->cmNumArgs), receiver); + assert(!result); + return ceSendFromInLineCacheMiss(targetMethod); + } + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + + /* See if an Open PIC is already available. */ + pic = openPICWithSelector((targetMethod->selector)); + if (!pic) { + /* otherwise attempt to create a closed PIC for the two cases. */ + pic = cogPICSelectornumArgsCase0MethodCase1MethodtagisMNUCase((targetMethod->selector), (targetMethod->cmNumArgs), targetMethod, newTargetMethodOrNil, cacheTag, errorSelectorOrNil == SelectorDoesNotUnderstand); + if ((((((sqInt)pic)) >= MaxNegativeErrorCode) && ((((sqInt)pic)) <= -1))) { + /* For some reason the PIC couldn't be generated, most likely a lack of code memory. + Continue as if this is an unlinked send. */ + if ((((sqInt)pic)) == InsufficientCodeSpace) { + callForCogCompiledCodeCompaction(); + } + + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } + return ceSendFromInLineCacheMiss(targetMethod); + } + } + + /* Relink the send site to the pic. If to an open PIC then reset the cache tag to the selector, + for the benefit of the cacheTag assert check in checkIfValidOopRef:pc:cogMethod: et al. */ + extent = (((pic->cmType)) == CMOpenPIC + ? rewriteInlineCacheAttagtarget(backEnd, outerReturn, inlineCacheValueForSelectorin(backEnd, (targetMethod->selector), mframeHomeMethodExport()), (((sqInt)pic)) + cmEntryOffset) + : rewriteCallAttarget(backEnd, outerReturn, (((sqInt)pic)) + cmEntryOffset)); + (((usqInt)pic)) + closedPICSize; + + /* begin assertValidDualZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + /* begin assertCoherentCodeAt:delta: */ + codeInstruction = long32At((void *)((((usqInt)pic)) + cmNoCheckEntryOffset)); + dataInstruction = long32At((void *)(((((usqInt)pic)) + cmNoCheckEntryOffset) + codeToDataDelta)); + assert(codeInstruction == dataInstruction); +# endif // DUAL_MAPPED_CODE_ZONE + + + /* These also implicitly flush the read/write mapped dual zone to the read/execute zone. */ + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) (((usqInt)pic))), ((((usqInt)pic)) + closedPICSize) - (((usqInt)pic))); + sys_icache_invalidate(((void *) (((usqInt)pic))), ((((usqInt)pic)) + closedPICSize) - (((usqInt)pic))); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) (((usqInt)pic))), (((((usqInt)pic)) + closedPICSize) - (((usqInt)pic))) + 1); +# else // __APPLE__ && __MACH__ || _WIN64 + ceFlushICache(((usqInt)pic), (((usqInt)pic)) + closedPICSize); +# endif + + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) ((((usqInt)outerReturn)) - extent)), (((usqInt)outerReturn)) - ((((usqInt)outerReturn)) - extent)); + sys_icache_invalidate(((void *) ((((usqInt)outerReturn)) - extent)), (((usqInt)outerReturn)) - ((((usqInt)outerReturn)) - extent)); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) ((((usqInt)outerReturn)) - extent)), ((((usqInt)outerReturn)) - ((((usqInt)outerReturn)) - extent)) + 1); +# else // __APPLE__ && __MACH__ || _WIN64 + ceFlushICache((((usqInt)outerReturn)) - extent, ((usqInt)outerReturn)); +# endif + + + /* Jump back into the pic at its entry in case this is an MNU (newTargetMethodOrNil is nil) */ + executeCogPICfromLinkedSendWithReceiverandCacheTag(pic, receiver, inlineCacheTagAt(backEnd, outerReturn)); + + /* NOTREACHED */ + return null; +} + + +/* Check for a valid object reference, if any, at a map entry. Answer a code + unique to each error for debugging. */ + + /* Cogit>>#checkIfValidOopRefAndTarget:pc:cogMethod: */ +static NoDbgRegParms sqInt +checkIfValidOopRefAndTargetpccogMethod(sqInt annotation, char *mcpc, CogMethod *cogMethod) +{ + unsigned int cacheTag1; + sqInt entryPoint; + sqInt entryPointSqInt; + unsigned int instr; + sqInt literal; + sqInt offsetSqInt; + sqInt pc; + sqInt *sendTable1; + CogMethod *targetMethod1; + + if (annotation == IsObjectReference) { + literal = longAt((void *)(((usqInt)mcpc))); + if (!(asserta(checkValidOopReference(literal)))) { + return 1; + } + if ((couldBeObject(literal)) + && (isReallyYoungObject(literal))) { + if (!(asserta(((((CogMethod *) cogMethod))->cmRefersToYoung)))) { + return 2; + } + } + } + if (annotation >= IsSendCall) { + if (!(asserta(isCMMethodEtAl(((CogBlockMethod *) (((CogMethod *) cogMethod))))))) { + return 3; + } + + /* begin entryCacheTagAndCouldBeObjectAt:annotation:into: */ + /* begin inlineCacheTagAt: */ + /* begin pcRelativeAddressAt: */ + instr = long32At((void *)(((usqInt)((((sqInt)mcpc)) - 8)))); + + /* cogit processor disassembleInstructionAt: mcpc In: objectMemory memory */ + assert(instructionIsPCRelativeLoad(backEnd, instr)); + pc = (((((usqInt)(instr)) >> 3) & (0x3FFFFC)) - ((((usqInt)(instr)) >> 2) & (0x200000))) + (((usqInt)((((sqInt)mcpc)) - 8))); + cacheTag1 = long32At((void *)(pc)); + entryPointSqInt = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + + /* in-line cache tags are the selectors of sends if sends are unlinked, + the selectors of super sends (entry offset = cmNoCheckEntryOffset), + the selectors of open PIC sends (entry offset = cmEntryOffset, target is an Open PIC) + or in-line cache tags (classes, class indices, immediate bit patterns, etc). + Note that selectors can be immediate so there is no guarantee that they + are markable/remappable objects. Writing the block invocation three times + allows Slang to inline tagCouldBeObject tests. */ + entryPoint = entryPointSqInt; + if (entryPointTagIsSelector(entryPoint)) { + if ((((int) cacheTag1)) < 0) { + if ((-(((int) cacheTag1))) > NumSpecialSelectors) { + return 7; + } + } + else { + if (cacheTag1 >= (literalCountOf((enumeratingCogMethod->methodObject)))) { + return 8; + } + } + } + else { + if (!(asserta(validInlineCacheTag(cacheTag1)))) { + return 9; + } + } + if (entryPoint > methodZoneBase) { + /* It's a linked send; find which kind. */ + + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (!(asserta((isCMMethodEtAl(((CogBlockMethod *) targetMethod1))) + || ((isCMClosedPIC(((CogBlockMethod *) targetMethod1))) + || (isCMOpenPIC(((CogBlockMethod *) targetMethod1))))))) { + return 10; + } + } + } + return 0; +} + + +/* Check for a valid object reference, if any, at a map entry. Answer a code + unique to each error for debugging. */ + + /* Cogit>>#checkIfValidOopRef:pc:cogMethod: */ +static NoDbgRegParms sqInt +checkIfValidOopRefpccogMethod(sqInt annotation, char *mcpc, CogMethod *cogMethod) +{ + sqInt entryPoint; + unsigned int instr; + sqInt literal; + sqInt offset; + sqInt offsetSqInt; + sqInt pc; + unsigned int selectorOrCacheTag; + sqInt *sendTable; + + if (annotation == IsObjectReference) { + literal = longAt((void *)(((usqInt)mcpc))); + if (!(checkValidOopReference(literal))) { + fprintf(getTranscript(), + "object ref leak in CM %p @ %p\n", + cogMethod, + mcpc); + eek(); + return 1; + } + } + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint <= methodZoneBase) { + offset = entryPoint; + } + else { + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable = superSendTrampolines; + } + } + } + offset = offsetSqInt; + } + + /* begin inlineCacheTagAt: */ + /* begin pcRelativeAddressAt: */ + instr = long32At((void *)(((usqInt)((((sqInt)mcpc)) - 8)))); + + /* cogit processor disassembleInstructionAt: mcpc In: objectMemory memory */ + assert(instructionIsPCRelativeLoad(backEnd, instr)); + pc = (((((usqInt)(instr)) >> 3) & (0x3FFFFC)) - ((((usqInt)(instr)) >> 2) & (0x200000))) + (((usqInt)((((sqInt)mcpc)) - 8))); + selectorOrCacheTag = long32At((void *)(pc)); + if ((entryPoint > methodZoneBase) + && ((offset != cmNoCheckEntryOffset) + && (!((((((CogMethod *) (entryPoint - offset)))->cmType)) == CMOpenPIC)))) { + if (!(validInlineCacheTag(selectorOrCacheTag))) { + fprintf(getTranscript(), + "cache tag leak in CM %p @ %p\n", + cogMethod, + mcpc); + eek(); + return 1; + } + } + else { + } + } + return 0; +} + + +/* Answer if all references to objects in machine-code are valid. */ + + /* Cogit>>#checkIntegrityOfObjectReferencesInCode: */ +sqInt +checkIntegrityOfObjectReferencesInCode(sqInt gcModes) +{ + CogMethod *cogMethod; + sqInt count; + sqInt ok; + + cogMethod = ((CogMethod *) methodZoneBase); + ok = 1; + while (cogMethod < (limitZony())) { + if (!(((cogMethod->cmType)) == CMFree)) { + if ((cogMethod->cmRefersToYoung)) { + if (((count = occurrencesInYoungReferrers(cogMethod))) != 1) { + fprintf(getTranscript(), + "young referrer CM %p", + cogMethod); + if (count) { + fprintf(getTranscript(), + " is in youngReferrers %d times!\n", + ((int) count)); + eek(); + } + else { + fprintf(getTranscript(), " is not in youngReferrers\n"); + eek(); + } + ok = 0; + } + } + if (!(checkValidOopReference((cogMethod->selector)))) { + fprintf(getTranscript(), + "object leak in CM %p selector\n", + cogMethod); + eek(); + ok = 0; + } + if (((cogMethod->cmType)) >= CMMethod) { + assert(((cogMethod->objectHeader)) == (nullHeaderForMachineCodeMethod())); + if (!(checkValidObjectReference((cogMethod->methodObject)))) { + fprintf(getTranscript(), + "object leak in CM %p methodObject\n", + cogMethod); + eek(); + ok = 0; + } + if (!(isOopCompiledMethod((cogMethod->methodObject)))) { + fprintf(getTranscript(), + "non-method in CM %p methodObject\n", + cogMethod); + eek(); + ok = 0; + } + if (mapForperformUntilarg(cogMethod, checkIfValidOopRefpccogMethod, cogMethod)) { + ok = 0; + } + if (((isYoungObject((cogMethod->methodObject))) + || (isYoung((cogMethod->selector)))) + && (!((cogMethod->cmRefersToYoung)))) { + fprintf(getTranscript(), + "CM %p refers to young but not marked as such\n", + cogMethod); + eek(); + ok = 0; + } + } + else { + if (((cogMethod->cmType)) == CMClosedPIC) { + if (!(checkValidObjectReferencesInClosedPIC(cogMethod))) { + ok = 0; + } + } + else { + if (((cogMethod->cmType)) == CMOpenPIC) { + if (mapForperformUntilarg(cogMethod, checkIfValidOopRefpccogMethod, cogMethod)) { + ok = 0; + } + } + } + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + return ok; +} + + /* Cogit>>#checkMaybeObjRefInClosedPIC: */ +static NoDbgRegParms sqInt +checkMaybeObjRefInClosedPIC(sqInt maybeObject) +{ + if (!maybeObject) { + return 1; + } + if (!(couldBeObject(maybeObject))) { + return 1; + } + return checkValidObjectReference(maybeObject); +} + + /* Cogit>>#checkValidObjectReferencesInClosedPIC: */ +static NoDbgRegParms sqInt +checkValidObjectReferencesInClosedPIC(CogMethod *cPIC) +{ + sqInt i; + sqInt ok; + sqInt pc; + + ok = 1; + pc = (((sqInt)cPIC)) + firstCPICCaseOffset; + + /* first we check the obj ref at the beginning of the CPIC */ + if (!(checkMaybeObjRefInClosedPIC(literalBeforeFollowingAddress(backEnd, pc - (jumpLongByteSize(backEnd)))))) { + fprintf(getTranscript(), + "object leak in CPIC %p @ %p\n", + cPIC, + ((void *)(pc - (jumpLongByteSize(backEnd))))); + ok = 0; + } + + /* Next we step over each case that is in use. We find the end address of the cPICNumCases'th case and can then just step forward by the case size thereafter */ + pc = addressOfEndOfCaseinCPIC((cPIC->cPICNumCases), cPIC); + + /* For each case we check any object reference at the end address - sizeof(conditional instruction) and then increment the end address by case size */ + for (i = 2; i <= ((cPIC->cPICNumCases)); i += 1) { + if (!(checkMaybeObjRefInClosedPIC(literalBeforeFollowingAddress(backEnd, (pc - (jumpLongConditionalByteSize(backEnd))) - (cmpC32RTempByteSize(backEnd)))))) { + fprintf(getTranscript(), + "object leak in CPIC %p @ %p\n", + cPIC, + ((void *)(pc - (jumpLongConditionalByteSize(backEnd))))); + ok = 0; + } + pc += cPICCaseSize; + } + return ok; +} + + +/* i.e. this should never be called, so keep it out of the main path. */ + + /* Cogit>>#cleanUpFailingCogCodeConstituents: */ +static NoDbgRegParms NeverInline sqInt +cleanUpFailingCogCodeConstituents(CogMethod *cogMethodArg) +{ + CogMethod *cogMethod; + + cogMethod = cogMethodArg; + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) == CMClosedPIC) { + ((((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)))->methodObject = 0); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + + /* would like to assert this, but it requires the leak checked be run :-( + self assert: self allMachineCodeObjectReferencesValid. */ + popRemappableOop(); + return null; +} + + +/* Answer if the ClosedPIC refers to any unmarked objects or freed/freeable + target methods, + applying markAndTraceOrFreeCogMethod:firstVisit: to those targets to + determine if freed/freeable. + */ + + /* Cogit>>#closedPICRefersToUnmarkedObject: */ +static NoDbgRegParms sqInt +closedPICRefersToUnmarkedObject(CogMethod *cPIC) +{ + sqInt i; + sqInt object; + sqInt pc; + + if (!((isImmediate((cPIC->selector))) + || (isMarked((cPIC->selector))))) { + return 1; + } + + /* First jump is unconditional; subsequent ones are conditional. + Check the potential method oop for the first case only. + Inline cache tags for the 1st case are at the send site. */ + pc = addressOfEndOfCaseinCPIC(1, cPIC); + if (couldBeObject((object = literalBeforeFollowingAddress(backEnd, pc - (jumpLongByteSize(backEnd)))))) { + if (!(isMarked(object))) { + return 1; + } + } + + /* Check the first target */ + if (markAndTraceOrFreePICTargetin(jumpLongTargetBeforeFollowingAddress(backEnd, pc), cPIC)) { + return 1; + } + for (i = 2; i <= ((cPIC->cPICNumCases)); i += 1) { + pc = addressOfEndOfCaseinCPIC(i, cPIC); + + /* Check the potential method oop for subsequent cases. */ + if (couldBeObject((object = literalBeforeFollowingAddress(backEnd, (pc - (jumpLongConditionalByteSize(backEnd))) - (cmpC32RTempByteSize(backEnd)))))) { + if (!(isMarked(object))) { + return 1; + } + } + + /* Check subsequent targets */ + if (markAndTraceOrFreePICTargetin(jumpLongTargetBeforeFollowingAddress(backEnd, pc), cPIC)) { + return 1; + } + } + return 0; +} + + /* Cogit>>#codeEntryFor: */ +char * +codeEntryFor(char *address) +{ + char *end; + sqInt i; + char *name; + char *start; + + /* begin trampolineRangesDo: */ + for (i = 0; i <= (trampolineTableIndex - 3); i += 2) { + name = trampolineAddresses[i]; + start = trampolineAddresses[i + 1]; + end = (trampolineAddresses[i + 3]) - 1; + if (((address >= start) && (address <= end))) { + return start; + } + } + return null; +} + + /* Cogit>>#codeEntryNameFor: */ +char * +codeEntryNameFor(char *address) +{ + char *end; + sqInt i; + char *name; + char *start; + + /* begin trampolineRangesDo: */ + for (i = 0; i <= (trampolineTableIndex - 3); i += 2) { + name = trampolineAddresses[i]; + start = trampolineAddresses[i + 1]; + end = (trampolineAddresses[i + 3]) - 1; + if (((address >= start) && (address <= end))) { + return name; + } + } + return null; +} + + +/* used e.g. in the platform's backtrace generators. Declared api to place it + in cogit.h + */ + + /* Cogit>>#cogCodeBase */ +usqInt +cogCodeBase(void) +{ + return codeBase; +} + + +/* Answer the contents of the code zone as an array of pair-wise element, + address in ascending address order. + Answer a string for a runtime routine or abstract label (beginning, end, + etc), a CompiledMethod for a CMMethod, + or a selector (presumably a Symbol) for a PIC. + If withDetails is true + - answer machine-code to bytecode pc mapping information for methods + - answer class, target pair information for closed PIC */ + + /* Cogit>>#cogCodeConstituents: */ +sqInt +cogCodeConstituents(sqInt withDetails) +{ + sqInt constituents; + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + constituents = getCogCodeConstituents(withDetails); + + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } + return constituents; +} + + +/* Extend the cPIC with the supplied case. If caseNMethod is cogged dispatch + direct to + its unchecked entry-point. If caseNMethod is not cogged, jump to the fast + interpreter dispatch, and if isMNUCase then dispatch to fast MNU + invocation and mark the cPIC as + having the MNU case for cache flushing. */ + + /* Cogit>>#cogExtendPIC:CaseNMethod:tag:isMNUCase: */ +static NoDbgRegParms void +cogExtendPICCaseNMethodtagisMNUCase(CogMethod *cPIC, sqInt caseNMethod, sqInt caseNTag, sqInt isMNUCase) +{ + sqInt address; + usqInt addressFollowingJump; + unsigned int codeInstruction; + unsigned int dataInstruction; + usqInt jumpTargetAddr; + sqInt operand; + usqIntptr_t target; + sqInt targetSqInt; + + compilationBreakpointclassTagisMNUCase((cPIC->selector), caseNTag, isMNUCase); + assert(!(inlineCacheTagIsYoung(caseNTag))); + + /* Caller patches to open pic if caseNMethod is young. */ + assert((caseNMethod) + && (!(isYoung(caseNMethod)))); + if ((!isMNUCase) + && (methodHasCogMethod(caseNMethod))) { + operand = 0; + target = (((sqInt)(cogMethodOf(caseNMethod)))) + cmNoCheckEntryOffset; + } + else { + operand = caseNMethod; + if (isMNUCase) { + /* cpicHasMNUCase: */ + ((((CogMethod *) ((((usqInt)cPIC)) + codeToDataDelta)))->cpicHasMNUCaseOrCMIsFullBlock) = 1; + target = (((sqInt)cPIC)) + (sizeof(CogMethod)); + } + else { + target = (((sqInt)cPIC)) + (picInterpretAbortOffset()); + } + } + + /* this isn't an MNU and we have an already cogged method to jump to + find the end address of the new case */ + address = addressOfEndOfCaseinCPIC(((cPIC->cPICNumCases)) + 1, cPIC); + rewriteCPICCaseAttagobjReftarget(address, caseNTag, operand, target); + + /* finally, rewrite the jump 3 instr before firstCPICCaseOffset to jump to the beginning of this new case */ + targetSqInt = address - cPICCaseSize; + + /* begin rewriteCPIC:caseJumpTo: */ + addressFollowingJump = (((((sqInt)cPIC)) + firstCPICCaseOffset) - (jumpLongByteSize(backEnd))) - (loadLiteralByteSize(backEnd)); + jumpTargetAddr = targetSqInt; + + /* begin rewriteCPICJumpAt:target: */ + rewriteImm19JumpBeforetarget(backEnd, addressFollowingJump, jumpTargetAddr); + + /* and update the header flag for the number of cases */ + ((((CogMethod *) ((((usqInt)cPIC)) + codeToDataDelta)))->cPICNumCases = ((cPIC->cPICNumCases)) + 1); + + /* This also implicitly flushes the read/write mapped dual zone to the read/execute zone. */ + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) (((usqInt)cPIC))), ((((usqInt)cPIC)) + closedPICSize) - (((usqInt)cPIC))); + sys_icache_invalidate(((void *) (((usqInt)cPIC))), ((((usqInt)cPIC)) + closedPICSize) - (((usqInt)cPIC))); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) (((usqInt)cPIC))), (((((usqInt)cPIC)) + closedPICSize) - (((usqInt)cPIC))) + 1); +# else // __APPLE__ && __MACH__ || _WIN64 + ceFlushICache(((usqInt)cPIC), (((usqInt)cPIC)) + closedPICSize); +# endif + + (((usqInt)cPIC)) + closedPICSize; + + /* begin assertValidDualZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + /* begin assertCoherentCodeAt:delta: */ + codeInstruction = long32At((void *)((((usqInt)cPIC)) + cmNoCheckEntryOffset)); + dataInstruction = long32At((void *)(((((usqInt)cPIC)) + cmNoCheckEntryOffset) + codeToDataDelta)); + assert(codeInstruction == dataInstruction); +# endif // DUAL_MAPPED_CODE_ZONE +} + + +/* Attempt to produce a machine code method for the bytecode method + object aMethodObj. N.B. If there is no code memory available do *NOT* + attempt to reclaim the method zone. Certain clients (e.g. ceSICMiss:) + depend on the zone remaining constant across method generation. */ + + /* Cogit>>#cogFullBlockMethod:numCopied: */ +CogMethod * +cogFullBlockMethodnumCopied(sqInt aMethodObj, sqInt numCopied) +{ + CogMethod *cogMethod; + + assert(!((methodHasCogMethod(aMethodObj)))); + if (aMethodObj == breakMethod) { + haltmsg("Compilation of breakMethod"); + } + ensureNoForwardedLiteralsIn(aMethodObj); + + /* If the generators for the alternate bytecode set are missing then interpret. */ + if (methodUsesAlternateBytecodeSet(aMethodObj)) { + if ((numElementsIn(generatorTable)) <= 0x100) { + return null; + } + bytecodeSetOffset = 0x100; + } + else { + bytecodeSetOffset = 0; + } + assert(isFullBlockMethod(aMethodObj)); + methodObj = aMethodObj; + methodHeader = methodHeaderOf(aMethodObj); + + /* lazy initialization */ + receiverTags = -1; + cogMethod = compileCogFullBlockMethod(numCopied); + if ((((((sqInt)cogMethod)) >= MaxNegativeErrorCode) && ((((sqInt)cogMethod)) <= -1))) { + if ((((sqInt)cogMethod)) == InsufficientCodeSpace) { + callForCogCompiledCodeCompaction(); + } + + /* Right now no errors should be reported, so nothing more to do. + self reportError: (self cCoerceSimple: cogMethod to: #sqInt). */ + return null; + } + + /* coInterpreter printCogMethod: cogMethod. + coInterpreter symbolicMethod: aMethodObj. + self assertValidMethodMap: cogMethod. + self disassembler disassembleMethod: cogMethod. + printInstructions := clickConfirm := true */ + return cogMethod; +} + + /* Cogit>>#cogitPostGCAction: */ +void +cogitPostGCAction(sqInt gcMode) +{ + + /* Post-GC update every full method's objectHeader to whatever it needs to be */ + assert(allMethodsHaveCorrectHeader()); + + /* The youngReferrers should be correct after a GC since that is the point at which it is + pruned. But at other times false positives or free methods on the list are acceptable. */ + assert(((!(gcMode & (GCModeFull + GCModeNewSpace)))) + || (kosherYoungReferrers())); + + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +} + + +/* Check that the header fields onf a non-free method are consistent with + the type. Answer 0 if it is ok, otherwise answer a code for the error. */ + + /* Cogit>>#cogMethodDoesntLookKosher: */ +static NoDbgRegParms sqInt +cogMethodDoesntLookKosher(CogMethod *cogMethod) +{ + if (((((cogMethod->blockSize)) & (BytesPerWord - 1)) != 0) + || ((((cogMethod->blockSize)) < (sizeof(CogMethod))) + || (((cogMethod->blockSize)) >= 0x8000))) { + return 1; + } + if (((cogMethod->cmType)) == CMFree) { + return 2; + } + if (((cogMethod->cmType)) >= CMMethod) { + if (!((((((cogMethod->methodHeader))) & 7) == 1))) { + return 11; + } + if (!(couldBeObject((cogMethod->methodObject)))) { + return 12; + } + if ((((cogMethod->stackCheckOffset)) > 0) + && (((cogMethod->stackCheckOffset)) < cmNoCheckEntryOffset)) { + return 13; + } + return 0; + } + if (((cogMethod->cmType)) == CMOpenPIC) { + if (((cogMethod->blockSize)) != openPICSize) { + return 21; + } + if ((cogMethod->methodHeader)) { + return 22; + } + + /* Check the nextOpenPIC link unless we're compacting */ + if (((cogMethod->objectHeader)) >= 0) { + if (!((((cogMethod->methodObject)) == 0) + || (compactionInProgress + || (((cogMethod->methodObject)) == (((usqInt)(methodFor(((void *)((cogMethod->methodObject))))))))))) { + return 23; + } + } + if ((cogMethod->stackCheckOffset)) { + return 24; + } + return 0; + } + if (((cogMethod->cmType)) == CMClosedPIC) { + if (((cogMethod->blockSize)) != closedPICSize) { + return 0x1F; + } + if (!(((((cogMethod->cPICNumCases)) >= 1) && (((cogMethod->cPICNumCases)) <= MaxCPICCases)))) { + return 32; + } + if ((cogMethod->methodHeader)) { + return 33; + } + if ((cogMethod->methodObject)) { + return 34; + } + return 0; + } + return 9; +} + + +/* Attempt to create a one-case PIC for an MNU. + The tag for the case is at the send site and so doesn't need to be + generated. + */ + + /* Cogit>>#cogMNUPICSelector:receiver:methodOperand:numArgs: */ +CogMethod * +cogMNUPICSelectorreceivermethodOperandnumArgs(sqInt selector, sqInt rcvr, sqInt methodOperand, sqInt numArgs) +{ + CogMethod *actualPIC; + unsigned int codeInstruction; + unsigned int dataInstruction; + usqInt startAddress; + CogMethod *writablePIC; + + if ((isYoung(selector)) + || ((inlineCacheTagForInstance(rcvr)) == 0 /* picAbortDiscriminatorValue */)) { + return 0; + } + compilationBreakpointclassTagisMNUCase(selector, fetchClassTagOf(rcvr), 1); + assert(endCPICCase0); + + /* get memory in the code zone for the CPIC; if that fails we return an error code for the sender to use to work out how to blow up */ + startAddress = allocate(closedPICSize); + if (!startAddress) { + callForCogCompiledCodeCompaction(); + return 0; + } + maybeBreakGeneratingFromto(startAddress, startAddress + closedPICSize); + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + writablePIC = ((CogMethod *) ((((usqInt)startAddress)) + codeToDataDelta)); + + /* memcpy the prototype across to our allocated space; because anything else would be silly */ + codeMemcpy(writablePIC, cPICPrototype, closedPICSize); + + /* begin fillInCPICHeader:numArgs:numCases:hasMNUCase:selector: */ + assert(!(isYoung(selector))); + (writablePIC->cmType = CMClosedPIC); + (writablePIC->objectHeader = 0); + (writablePIC->blockSize = closedPICSize); + (writablePIC->methodObject = 0); + (writablePIC->methodHeader = 0); + (writablePIC->selector = selector); + (writablePIC->cmNumArgs = numArgs); + (writablePIC->cmHasMovableLiteral = 0); + (writablePIC->cmRefersToYoung = 0); + (writablePIC->cmUsageCount = CMMaxUsageCount / 2); + + /* cpicHasMNUCase: */ + (writablePIC->cpicHasMNUCaseOrCMIsFullBlock) = 1; + (writablePIC->cPICNumCases = 1); + (writablePIC->blockEntryOffset = 0); + assert(isCMClosedPIC(((CogBlockMethod *) writablePIC))); + assert(((writablePIC->selector)) == selector); + assert(((writablePIC->cmNumArgs)) == numArgs); + assert(((writablePIC->cPICNumCases)) == 1); + assert(closedPICSize == (roundUpLength(closedPICSize))); + configureMNUCPICmethodOperandnumArgsdelta((actualPIC = ((CogMethod *) startAddress)), methodOperand, numArgs, startAddress - (((usqInt)cPICPrototype))); + + /* This also implicitly flushes the read/write mapped dual zone to the read/execute zone. */ + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) startAddress), (startAddress + closedPICSize) - startAddress); + sys_icache_invalidate(((void *) startAddress), (startAddress + closedPICSize) - startAddress); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) startAddress), ((startAddress + closedPICSize) - startAddress) + 1); +# else // __APPLE__ && __MACH__ || _WIN64 + ceFlushICache(startAddress, startAddress + closedPICSize); +# endif + + assert((callTargetFromReturnAddress(backEnd, startAddress + missOffset)) == (picAbortTrampolineFor(numArgs))); + + /* begin assertValidDualZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + /* begin assertCoherentCodeAt:delta: */ + codeInstruction = long32At((void *)(startAddress + cmNoCheckEntryOffset)); + dataInstruction = long32At((void *)((startAddress + cmNoCheckEntryOffset) + codeToDataDelta)); + assert(codeInstruction == dataInstruction); +# endif // DUAL_MAPPED_CODE_ZONE + + return actualPIC; +} + + +/* Create an Open PIC. Temporarily create a direct call of + ceSendFromOpenPIC:. Should become a probe of the first-level method lookup + cache followed by a + call of ceSendFromOpenPIC: if the probe fails. */ + + /* Cogit>>#cogOpenPICSelector:numArgs: */ +static NoDbgRegParms CogMethod * +cogOpenPICSelectornumArgs(sqInt selector, sqInt numArgs) +{ + unsigned int codeInstruction; + sqInt codeSize; + unsigned int dataInstruction; + sqInt end; + usqIntptr_t fixupSize; + sqInt mapSize; + usqIntptr_t opcodeSize; + CogMethod *pic; + usqInt startAddress; + + compilationBreakpointisMNUCase(selector, 0); + startAddress = allocate(openPICSize); + if (!startAddress) { + return ((CogMethod *) InsufficientCodeSpace); + } + (methodLabel->address = startAddress); + (methodLabel->dependent = null); + + /* stack allocate the various collections so that they + are effectively garbage collected on return. */ + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 100; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + firstOpcodeIndex = 0x10000; + nextLiteralIndex = (lastDumpedLiteralIndex = 0); + labelCounter = 0; + compileOpenPICnumArgs(selector, numArgs); + computeMaximumSizes(); + concretizeAt(methodLabel, startAddress); + codeSize = generateInstructionsAt(startAddress + (sizeof(CogMethod))); + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + mapSize = generateMapAtstart((startAddress + openPICSize) - 1, startAddress + cmNoCheckEntryOffset); + assert((((entry->address)) - startAddress) == cmEntryOffset); + assert(((roundUpLength((sizeof(CogMethod)) + codeSize)) + (roundUpLength(mapSize))) <= openPICSize); + end = outputInstructionsAt(startAddress + (sizeof(CogMethod))); + pic = ((CogMethod *) ((((usqInt)startAddress)) + codeToDataDelta)); + + /* begin fillInOPICHeader:numArgs:selector: */ + (pic->cmType = CMOpenPIC); + (pic->objectHeader = 0); + (pic->blockSize = openPICSize); + + /* pic methodObject: 0. + This is also the nextOpenPIC link so don't initialize it */ + addToOpenPICList(pic); + (pic->methodHeader = 0); + (pic->selector = selector); + (pic->cmNumArgs = numArgs); + (pic->cmHasMovableLiteral = isNonImmediate(selector)); + if ((pic->cmRefersToYoung = isYoung(selector))) { + addToYoungReferrers(pic); + } + (pic->cmUsageCount = initialOpenPICUsageCount()); + + /* cpicHasMNUCase: */ + (pic->cpicHasMNUCaseOrCMIsFullBlock) = 0; + (pic->cPICNumCases = 0); + (pic->blockEntryOffset = 0); + + /* This also implicitly flushes the read/write mapped dual zone to the read/execute zone. */ + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) ((((usqInt)pic)) - codeToDataDelta)), (((((usqInt)pic)) - codeToDataDelta) + openPICSize) - ((((usqInt)pic)) - codeToDataDelta)); + sys_icache_invalidate(((void *) ((((usqInt)pic)) - codeToDataDelta)), (((((usqInt)pic)) - codeToDataDelta) + openPICSize) - ((((usqInt)pic)) - codeToDataDelta)); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) ((((usqInt)pic)) - codeToDataDelta)), ((((((usqInt)pic)) - codeToDataDelta) + openPICSize) - ((((usqInt)pic)) - codeToDataDelta)) + 1); +# else // __APPLE__ && __MACH__ || _WIN64 + ceFlushICache((((usqInt)pic)) - codeToDataDelta, ((((usqInt)pic)) - codeToDataDelta) + openPICSize); +# endif + + assert(isCMOpenPIC(((CogBlockMethod *) pic))); + assert(((pic->selector)) == selector); + assert(((pic->cmNumArgs)) == numArgs); + assert((callTargetFromReturnAddress(backEnd, ((((sqInt)pic)) - codeToDataDelta) + missOffset)) == (picAbortTrampolineFor(numArgs))); + assert(openPICSize == (roundUpLength(openPICSize))); + ((((usqInt)pic)) - codeToDataDelta) + openPICSize; + + /* begin assertValidDualZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + /* begin assertCoherentCodeAt:delta: */ + codeInstruction = long32At((void *)(((((usqInt)pic)) - codeToDataDelta) + cmNoCheckEntryOffset)); + dataInstruction = long32At((void *)((((((usqInt)pic)) - codeToDataDelta) + cmNoCheckEntryOffset) + codeToDataDelta)); + assert(codeInstruction == dataInstruction); +# endif // DUAL_MAPPED_CODE_ZONE + + return ((CogMethod *) startAddress); +} + + +/* Attempt to create a two-case PIC for case0CogMethod and + case1Method,case1Tag. The tag for case0CogMethod is at the send site and + so doesn't need to be generated. + case1Method may be any of + - a Cog method; link to its unchecked entry-point + - a CompiledMethod; link to ceInterpretMethodFromPIC: + - a CompiledMethod; link to ceMNUFromPICMNUMethod:receiver: */ + + /* Cogit>>#cogPICSelector:numArgs:Case0Method:Case1Method:tag:isMNUCase: */ +static NoDbgRegParms CogMethod * +cogPICSelectornumArgsCase0MethodCase1MethodtagisMNUCase(sqInt selector, sqInt numArgs, CogMethod *case0CogMethod, sqInt case1MethodOrNil, sqInt case1Tag, sqInt isMNUCase) +{ + CogMethod *actualPIC; + usqInt startAddress; + CogMethod *writablePIC; + + if (isYoung(selector)) { + return ((CogMethod *) YoungSelectorInPIC); + } + compilationBreakpointclassTagisMNUCase(selector, case1Tag, isMNUCase); + + /* get memory in the code zone for the CPIC; if that fails we return an error code for the sender to use to work out how to blow up */ + startAddress = allocate(closedPICSize); + if (!startAddress) { + return ((CogMethod *) InsufficientCodeSpace); + } + maybeBreakGeneratingFromto(startAddress, startAddress + closedPICSize); + writablePIC = ((CogMethod *) ((((usqInt)startAddress)) + codeToDataDelta)); + + /* memcpy the prototype across to our allocated space; because anything else would be silly */ + codeMemcpy(writablePIC, cPICPrototype, closedPICSize); + + /* begin fillInCPICHeader:numArgs:numCases:hasMNUCase:selector: */ + assert(!(isYoung(selector))); + (writablePIC->cmType = CMClosedPIC); + (writablePIC->objectHeader = 0); + (writablePIC->blockSize = closedPICSize); + (writablePIC->methodObject = 0); + (writablePIC->methodHeader = 0); + (writablePIC->selector = selector); + (writablePIC->cmNumArgs = numArgs); + (writablePIC->cmHasMovableLiteral = 0); + (writablePIC->cmRefersToYoung = 0); + (writablePIC->cmUsageCount = CMMaxUsageCount / 2); + + /* cpicHasMNUCase: */ + (writablePIC->cpicHasMNUCaseOrCMIsFullBlock) = isMNUCase; + (writablePIC->cPICNumCases = 2); + (writablePIC->blockEntryOffset = 0); + assert(isCMClosedPIC(((CogBlockMethod *) writablePIC))); + assert(((writablePIC->selector)) == selector); + assert(((writablePIC->cmNumArgs)) == numArgs); + assert(((writablePIC->cPICNumCases)) == 2); + assert(closedPICSize == (roundUpLength(closedPICSize))); + configureCPICCase0Case1MethodtagisMNUCasenumArgsdelta((actualPIC = ((CogMethod *) startAddress)), case0CogMethod, case1MethodOrNil, case1Tag, isMNUCase, numArgs, startAddress - (((usqInt)cPICPrototype))); + assert((callTargetFromReturnAddress(backEnd, startAddress + missOffset)) == (picAbortTrampolineFor(numArgs))); + return actualPIC; +} + + +/* Attempt to produce a machine code method for the bytecode method + object aMethodObj. N.B. If there is no code memory available do *NOT* + attempt to reclaim the method zone. Certain clients (e.g. ceSICMiss:) + depend on the zone remaining constant across method generation. */ + + /* Cogit>>#cog:selector: */ +CogMethod * +cogselector(sqInt aMethodObj, sqInt aSelectorOop) +{ + CogMethod *cogMethod; + sqInt selector; + + + /* In Newspeak we support anonymous accessors and hence tolerate the same + method being cogged multiple times. But only if the method class association is nil. */ + assert(!((methodHasCogMethod(aMethodObj)))); + selector = (aSelectorOop == (nilObject()) + ? maybeSelectorOfMethod(aMethodObj) + : aSelectorOop); + + /* coInterpreter stringOf: selector */ + if (selector) { + compilationBreakpointisMNUCase(selector, 0); + } + if (aMethodObj == breakMethod) { + haltmsg("Compilation of breakMethod"); + } + ensureNoForwardedLiteralsIn(aMethodObj); + + /* If the generators for the alternate bytecode set are missing then interpret. */ + if (methodUsesAlternateBytecodeSet(aMethodObj)) { + if ((numElementsIn(generatorTable)) <= 0x100) { + return null; + } + bytecodeSetOffset = 0x100; + } + else { + bytecodeSetOffset = 0; + } + assert(!((isFullBlockMethod(aMethodObj)))); + methodObj = aMethodObj; + methodHeader = methodHeaderOf(aMethodObj); + + /* lazy initialization */ + receiverTags = -1; + cogMethod = compileCogMethod(aSelectorOop); + if ((((((sqInt)cogMethod)) >= MaxNegativeErrorCode) && ((((sqInt)cogMethod)) <= -1))) { + if ((((sqInt)cogMethod)) == InsufficientCodeSpace) { + callForCogCompiledCodeCompaction(); + } + + /* Right now no errors should be reported, so nothing more to do. + self reportError: (self cCoerceSimple: cogMethod to: #sqInt). */ + return null; + } + + /* coInterpreter printCogMethod: cogMethod. + coInterpreter symbolicMethod: aMethodObj. + self assertValidMethodMap: cogMethod. + self disassembler disassembleMethod: cogMethod. + printInstructions := clickConfirm := true */ + return cogMethod; +} + + /* Cogit>>#collectCogConstituentFor:Annotation:Mcpc:Bcpc:Method: */ +static NoDbgRegParms sqInt +collectCogConstituentForAnnotationMcpcBcpcMethod(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *cogMethodArg) +{ + sqInt address; + sqInt annotation; + sqInt entryPoint; + sqInt offsetSqInt; + CogMethod *selfOfMethodObject; + sqInt *sendTable1; + CogMethod *targetMethod1; + + if (!descriptor) { + return 0; + } + if (!((descriptor->isMapped))) { + return 0; + } + address = positive64BitIntegerFor(((usqInt)mcpc)); + if (!address) { + return PrimErrNoMemory; + } + storePointerUncheckedofObjectwithValue(cogConstituentIndex, topRemappableOop(), address); + storePointerUncheckedofObjectwithValue(cogConstituentIndex + 1, topRemappableOop(), (((usqInt)bcpc << 3) | 1)); + cogConstituentIndex += 2; + + /* Collect any first case classTags for closed PICs. */ + if (((!(isBackwardBranchAndAnnotation & 1))) + && (/* isSendAnnotation: */ + ((((usqInt)(isBackwardBranchAndAnnotation)) >> 1) >= IsSendCall))) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + annotation = ((usqInt)(isBackwardBranchAndAnnotation)) >> 1; + + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (((targetMethod1->cmType)) == CMClosedPIC) { + selfOfMethodObject = ((CogMethod *) ((((usqInt)targetMethod1)) + codeToDataDelta)); + (selfOfMethodObject->methodObject = classForInlineCacheTag(inlineCacheTagAt(backEnd, ((sqInt)mcpc)))); + } + } + } + return 0; +} + + /* Cogit>>#collectCogConstituentOnSpurFor:Annotation:Mcpc:Bcpc:Data: */ +static NoDbgRegParms sqInt +collectCogConstituentOnSpurForAnnotationMcpcBcpcData(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *dataArg) +{ + sqInt annotation; + sqInt entryPoint; + sqInt offsetSqInt; + CogMethod *selfOfMethodObject; + sqInt *sendTable1; + CogMethod *targetMethod1; + + if (!descriptor) { + return 0; + } + if (!((descriptor->isMapped))) { + return 0; + } + longAtput(((0 + cogConstituentIndex) * BytesPerWord) + (((char *)dataArg)),((usqInt)mcpc)); + longAtput(((1 + cogConstituentIndex) * BytesPerWord) + (((char *)dataArg)),bcpc); + cogConstituentIndex += 2; + + /* Collect any first case classTags for closed PICs. */ + if (((!(isBackwardBranchAndAnnotation & 1))) + && (/* isSendAnnotation: */ + ((((usqInt)(isBackwardBranchAndAnnotation)) >> 1) >= IsSendCall))) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + annotation = ((usqInt)(isBackwardBranchAndAnnotation)) >> 1; + + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (((targetMethod1->cmType)) == CMClosedPIC) { + selfOfMethodObject = ((CogMethod *) ((((usqInt)targetMethod1)) + codeToDataDelta)); + (selfOfMethodObject->methodObject = classForInlineCacheTag(inlineCacheTagAt(backEnd, ((sqInt)mcpc)))); + } + } + } + return 0; +} + + +/* Answer a description of the mapping between machine code pointers and + bytecode pointers for the Cog Method. + First value is the address of the cog method. + Following values are pairs of machine code pc and bytecode pc. + dataClass is DoubleWordArray or WordArray as appropriate. */ + + /* Cogit>>#collectCogMethodConstituentOnSpur:ofClass: */ +static NoDbgRegParms sqInt +collectCogMethodConstituentOnSpurofClass(CogMethod *cogMethod, sqInt dataClass) +{ + sqInt aMethodObj; + sqInt annotation; + void *arg; + sqInt bcpc; + sqInt bsOffset; + sqInt byte; + CogBlockMethod *cogBlockMethod; + sqInt data; + BytecodeDescriptor *descriptor; + sqInt distance; + sqInt endbcpc; + sqInt errCode; + CogMethod *homeMethod; + sqInt isBackwardBranch; + sqInt isInBlock; + sqInt latestContinuation; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt nExts; + sqInt nextBcpc; + sqInt nSlots; + sqInt result; + sqInt startbcpc; + sqInt targetPC; + + if (!(((cogMethod->cmType)) >= CMMethod)) { + return positive64BitIntegerFor(((usqInt)cogMethod)); + } + cogBlockMethod = ((CogBlockMethod *) cogMethod); + if (!((cogBlockMethod->stackCheckOffset))) { + return positive64BitIntegerFor(((usqInt)cogMethod)); + } + + /* isFrameless ? */ + + /* +1 for first address */ + nSlots = ((((byteSizeOf((cogMethod->methodObject))) - (startPCOfMethodHeader((cogMethod->methodHeader)))) * 2) + (minSlotsForShortening())) + 1; + data = instantiateClassindexableSize(dataClass, nSlots); + if (!data) { + return null; + } + longAtput(firstIndexableField(data),((usqInt)cogMethod)); + cogConstituentIndex = 1; + startbcpc = startPCOfMethod((cogMethod->methodObject)); + arg = firstIndexableField(data); + + /* begin mapFor:bcpc:performUntil:arg: */ + descriptor = ((BytecodeDescriptor *) 0); + latestContinuation = 0; + mapByte = 0; + nextBcpc = 0; + assert(((cogBlockMethod->stackCheckOffset)) > 0); + mcpc = (((usqInt)cogBlockMethod)) + ((cogBlockMethod->stackCheckOffset)); + + /* The stack check maps to the start of the first bytecode, + the first bytecode being effectively after frame build. */ + result = collectCogConstituentOnSpurForAnnotationMcpcBcpcData(null, 0 + ((((usqInt)(HasBytecodePC) << 1))), ((char *) mcpc), startbcpc, arg); + if (result) { + errCode = result; + goto l1; + } + bcpc = startbcpc; + + /* In both CMMethod and CMBlock cases find the start of the map and + skip forward to the bytecode pc map entry for the stack check. */ + if (((cogBlockMethod->cmType)) >= CMMethod) { + isInBlock = (cogBlockMethod->cpicHasMNUCaseOrCMIsFullBlock); + homeMethod = ((CogMethod *) cogBlockMethod); + assert(startbcpc == (startPCOfMethodHeader((homeMethod->methodHeader)))); + map = ((((usqInt)homeMethod)) + ((homeMethod->blockSize))) - 1; + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert((annotation == IsAbsPCReference) + || ((annotation == IsObjectReference) + || ((annotation == IsRelativeCall) + || (annotation == IsDisplacementX2N)))); + latestContinuation = startbcpc; + aMethodObj = (homeMethod->methodObject); + endbcpc = (numBytesOf(aMethodObj)) - 1; + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + + /* If the method has a primitive, skip it and the error code store, if any; + Logically. these come before the stack check and so must be ignored. */ + bcpc += deltaToSkipPrimAndErrorStoreInheader(aMethodObj, (homeMethod->methodHeader)); + } + else { + isInBlock = 1; + assert(bcpc == ((cogBlockMethod->startpc))); + homeMethod = cmHomeMethod(cogBlockMethod); + map = findMapLocationForMcpcinMethod((((usqInt)cogBlockMethod)) + (sizeof(CogBlockMethod)), homeMethod); + assert(map != 0); + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert(((((usqInt)(annotation)) >> AnnotationShift) == HasBytecodePC) + || ((((usqInt)(annotation)) >> AnnotationShift) == IsDisplacementX2N)); + + /* fiducial */ + while (((annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift)) != HasBytecodePC) { + map -= 1; + } + + /* skip fiducial; i.e. the map entry for the pc immediately following the method header. */ + map -= 1; + aMethodObj = (homeMethod->methodObject); + bcpc = startbcpc - (/* blockCreationBytecodeSizeForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? AltBlockCreationBytecodeSize + : BlockCreationBytecodeSize)); + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + endbcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, -1, aMethodObj) + : 0)); + bcpc = startbcpc; + } + nExts = 0; + enumeratingCogMethod = homeMethod; + + /* Now skip up through the bytecode pc map entry for the stack check. */ + while ((((usqInt)((byteAt((void *)(map))))) >> AnnotationShift) != HasBytecodePC) { + map -= 1; + } + map -= 1; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + mcpc += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + if (annotation >= HasBytecodePC) { + if ((annotation == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + while (1) { + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + if (isInBlock) { + if (bcpc >= endbcpc) { + errCode = 0; + goto l1; + } + } + else { + if (((descriptor->isReturn)) + && (bcpc >= latestContinuation)) { + errCode = 0; + goto l1; + } + if ((isBranch(descriptor)) + || ((descriptor->isBlockCreation))) { + /* begin latestContinuationPCFor:at:exts:in: */ + distance = ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj); + targetPC = (bcpc + ((descriptor->numBytes))) + (((distance < 0) ? 0 : distance)); + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + } + } + nextBcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj) + : 0)); + if (((descriptor->isMapped)) + || (isInBlock + && ((descriptor->isMappedInBlock)))) break; + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + isBackwardBranch = (isBranch(descriptor)) + && ((/* begin isBackwardBranch:at:exts:in: */ + assert(((descriptor->spanFunction))), + (((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj)) < 0)); + result = collectCogConstituentOnSpurForAnnotationMcpcBcpcData(descriptor, (isBackwardBranch + ? ((((usqInt)(annotation) << 1))) + 1 + : ((sqInt)((usqInt)(annotation) << 1))), ((char *) mcpc), (isBackwardBranch + ? bcpc - (2 * nExts) + : bcpc), arg); + if (result) { + errCode = result; + goto l1; + } + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + } + else { + assert(((((usqInt)(mapByte)) >> AnnotationShift) == IsDisplacementX2N) + || ((((usqInt)(mapByte)) >> AnnotationShift) == IsAnnotationExtension)); + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + + /* defensive; we exit on bcpc */ + errCode = 0; + /* end mapFor:bcpc:performUntil:arg: */ +l1: + if (errCode) { + return null; + } + if (cogConstituentIndex < nSlots) { + shortentoIndexableSize(data, cogConstituentIndex); + } + return data; +} + + +/* Answer a description of the mapping between machine code pointers and + bytecode pointers for the Cog Method. + First value is the address of the cog method. + Following values are pairs of machine code pc and bytecode pc */ + + /* Cogit>>#collectCogMethodConstituent: */ +static NoDbgRegParms sqInt +collectCogMethodConstituent(CogMethod *cogMethod) +{ + sqInt address; + sqInt aMethodObj; + sqInt annotation; + sqInt bcpc; + sqInt bsOffset; + sqInt byte; + CogBlockMethod *cogBlockMethod; + sqInt data; + BytecodeDescriptor *descriptor; + sqInt distance; + sqInt endbcpc; + sqInt errCode; + CogMethod *homeMethod; + sqInt isBackwardBranch; + sqInt isInBlock; + sqInt latestContinuation; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt nExts; + sqInt nextBcpc; + sqInt nSlots; + sqInt result; + sqInt startbcpc; + sqInt targetPC; + + if (!(((cogMethod->cmType)) >= CMMethod)) { + return positive64BitIntegerFor(((usqInt)cogMethod)); + } + cogBlockMethod = ((CogBlockMethod *) cogMethod); + if (!((cogBlockMethod->stackCheckOffset))) { + return positive64BitIntegerFor(((usqInt)cogMethod)); + } + + /* isFrameless ? */ + + /* +1 for first address */ + nSlots = ((((byteSizeOf((cogMethod->methodObject))) - (startPCOfMethodHeader((cogMethod->methodHeader)))) * 2) + (minSlotsForShortening())) + 1; + data = instantiateClassindexableSize(splObj(ClassArray), nSlots); + if (!data) { + return null; + } + pushRemappableOop(data); + + /* The iteration assumes the object is the top remappable oop */ + address = positive64BitIntegerFor(((usqInt)cogMethod)); + if (!address) { + popRemappableOop(); + return null; + } + storePointerUncheckedofObjectwithValue(0, topRemappableOop(), address); + cogConstituentIndex = 1; + startbcpc = startPCOfMethod((cogMethod->methodObject)); + + /* begin mapFor:bcpc:performUntil:arg: */ + descriptor = ((BytecodeDescriptor *) 0); + latestContinuation = 0; + mapByte = 0; + nextBcpc = 0; + assert(((cogBlockMethod->stackCheckOffset)) > 0); + mcpc = (((usqInt)cogBlockMethod)) + ((cogBlockMethod->stackCheckOffset)); + + /* The stack check maps to the start of the first bytecode, + the first bytecode being effectively after frame build. */ + result = collectCogConstituentForAnnotationMcpcBcpcMethod(null, 0 + ((((usqInt)(HasBytecodePC) << 1))), ((char *) mcpc), startbcpc, ((void *)cogMethod)); + if (result) { + errCode = result; + goto l1; + } + bcpc = startbcpc; + + /* In both CMMethod and CMBlock cases find the start of the map and + skip forward to the bytecode pc map entry for the stack check. */ + if (((cogBlockMethod->cmType)) >= CMMethod) { + isInBlock = (cogBlockMethod->cpicHasMNUCaseOrCMIsFullBlock); + homeMethod = ((CogMethod *) cogBlockMethod); + assert(startbcpc == (startPCOfMethodHeader((homeMethod->methodHeader)))); + map = ((((usqInt)homeMethod)) + ((homeMethod->blockSize))) - 1; + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert((annotation == IsAbsPCReference) + || ((annotation == IsObjectReference) + || ((annotation == IsRelativeCall) + || (annotation == IsDisplacementX2N)))); + latestContinuation = startbcpc; + aMethodObj = (homeMethod->methodObject); + endbcpc = (numBytesOf(aMethodObj)) - 1; + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + + /* If the method has a primitive, skip it and the error code store, if any; + Logically. these come before the stack check and so must be ignored. */ + bcpc += deltaToSkipPrimAndErrorStoreInheader(aMethodObj, (homeMethod->methodHeader)); + } + else { + isInBlock = 1; + assert(bcpc == ((cogBlockMethod->startpc))); + homeMethod = cmHomeMethod(cogBlockMethod); + map = findMapLocationForMcpcinMethod((((usqInt)cogBlockMethod)) + (sizeof(CogBlockMethod)), homeMethod); + assert(map != 0); + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert(((((usqInt)(annotation)) >> AnnotationShift) == HasBytecodePC) + || ((((usqInt)(annotation)) >> AnnotationShift) == IsDisplacementX2N)); + + /* fiducial */ + while (((annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift)) != HasBytecodePC) { + map -= 1; + } + + /* skip fiducial; i.e. the map entry for the pc immediately following the method header. */ + map -= 1; + aMethodObj = (homeMethod->methodObject); + bcpc = startbcpc - (/* blockCreationBytecodeSizeForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? AltBlockCreationBytecodeSize + : BlockCreationBytecodeSize)); + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + endbcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, -1, aMethodObj) + : 0)); + bcpc = startbcpc; + } + nExts = 0; + enumeratingCogMethod = homeMethod; + + /* Now skip up through the bytecode pc map entry for the stack check. */ + while ((((usqInt)((byteAt((void *)(map))))) >> AnnotationShift) != HasBytecodePC) { + map -= 1; + } + map -= 1; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + mcpc += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + if (annotation >= HasBytecodePC) { + if ((annotation == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + while (1) { + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + if (isInBlock) { + if (bcpc >= endbcpc) { + errCode = 0; + goto l1; + } + } + else { + if (((descriptor->isReturn)) + && (bcpc >= latestContinuation)) { + errCode = 0; + goto l1; + } + if ((isBranch(descriptor)) + || ((descriptor->isBlockCreation))) { + /* begin latestContinuationPCFor:at:exts:in: */ + distance = ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj); + targetPC = (bcpc + ((descriptor->numBytes))) + (((distance < 0) ? 0 : distance)); + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + } + } + nextBcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj) + : 0)); + if (((descriptor->isMapped)) + || (isInBlock + && ((descriptor->isMappedInBlock)))) break; + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + isBackwardBranch = (isBranch(descriptor)) + && ((/* begin isBackwardBranch:at:exts:in: */ + assert(((descriptor->spanFunction))), + (((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj)) < 0)); + result = collectCogConstituentForAnnotationMcpcBcpcMethod(descriptor, (isBackwardBranch + ? ((((usqInt)(annotation) << 1))) + 1 + : ((sqInt)((usqInt)(annotation) << 1))), ((char *) mcpc), (isBackwardBranch + ? bcpc - (2 * nExts) + : bcpc), ((void *)cogMethod)); + if (result) { + errCode = result; + goto l1; + } + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + } + else { + assert(((((usqInt)(mapByte)) >> AnnotationShift) == IsDisplacementX2N) + || ((((usqInt)(mapByte)) >> AnnotationShift) == IsAnnotationExtension)); + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + + /* defensive; we exit on bcpc */ + errCode = 0; + /* end mapFor:bcpc:performUntil:arg: */ +l1: + if (errCode) { + popRemappableOop(); + return null; + } + if (cogConstituentIndex < nSlots) { + shortentoIndexableSize(topRemappableOop(), cogConstituentIndex); + } + return popRemappableOop(); +} + + /* Cogit>>#compactCogCompiledCode */ +void +compactCogCompiledCode(void) +{ + sqInt endAddress; + + assertValidDualZone(); + assert(noCogMethodsMaximallyMarked()); + moveProfileToMethods(); + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + markActiveMethodsAndReferents(); + freeOlderMethodsForCompaction(); + compactPICsWithFreedTargets(); + planCompaction(); + updateStackZoneReferencesToCompiledCodePreCompaction(); + relocateMethodsPreCompaction(); + assertValidDualZone(); + compactCompiledCode(); + stopsFromto(backEnd, freeStart(), (youngReferrers) - 1); + endAddress = ((usqInt)(youngReferrers)); + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) (((usqInt)methodZoneBase))), endAddress - (((usqInt)methodZoneBase))); + sys_icache_invalidate(((void *) (((usqInt)methodZoneBase))), endAddress - (((usqInt)methodZoneBase))); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) (((usqInt)methodZoneBase))), (endAddress - (((usqInt)methodZoneBase))) + 1); +# else // __APPLE__ && __MACH__ || _WIN64 + ceFlushICache(((usqInt)methodZoneBase), endAddress); +# endif + + assert(allMethodsHaveCorrectHeader()); + assert(kosherYoungReferrers()); + assertValidDualZone(); +} + + /* Cogit>>#compactPICsWithFreedTargets */ +static void +compactPICsWithFreedTargets(void) +{ + CogMethod *cogMethod; + sqInt count; + + cogMethod = ((CogMethod *) methodZoneBase); + count = 0; + while (cogMethod < (limitZony())) { + if ((((cogMethod->cmType)) == CMClosedPIC) + && (cPICCompactAndIsNowEmpty(cogMethod))) { + ((((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)))->cmType = CMFree); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + count += 1; + } + assert(count == (numMethods())); +} + + +/* The start of a CogMethod has a call to a run-time abort routine that + either handles an in-line cache failure or a stack overflow. The routine + selects the + path depending on ReceiverResultReg; if zero it takes the stack overflow + path; if nonzero the in-line cache miss path. Neither of these paths + returns. The abort routine must be called; In the callee the method is + located by + adding the relevant offset to the return address of the call. + + N.B. This code must match that in compilePICAbort: so that the offset of + the return address of the call is the same in methods and closed PICs. */ + + /* Cogit>>#compileAbort */ +static AbstractInstruction * +compileAbort(void) +{ + AbstractInstruction *anInstruction; + sqInt callTarget; + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, 0, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + stackOverflowCall = anInstruction; + + /* If there is a link register it must be saved (pushed onto the stack) before it + is smashed by the abort call, and hence needs to be manually handled here */ + sendMiss = genoperand(PushR, LinkReg); + callTarget = methodAbortTrampolineFor(methodOrBlockNumArgs); + + /* begin Call: */ + return genoperand(Call, callTarget); +} + + /* Cogit>>#compileBlockDispatchFrom:to: */ +static NoDbgRegParms sqInt +compileBlockDispatchFromto(sqInt lowBlockStartIndex, sqInt highBlockStartIndex) +{ + AbstractInstruction *anInstruction; + BlockStart *blockStart; + sqInt halfWay; + AbstractInstruction *jmp; + sqInt quickConstant; + + if (lowBlockStartIndex == highBlockStartIndex) { + blockStart = blockStartAt(lowBlockStartIndex); + + /* Jump: */ + genoperand(Jump, ((sqInt)((blockStart->entryLabel)))); + return null; + } + halfWay = (highBlockStartIndex + lowBlockStartIndex) / 2; + assert(((halfWay >= lowBlockStartIndex) && (halfWay <= highBlockStartIndex))); + blockStart = blockStartAt(halfWay); + + /* N.B. FLAGS := TempReg - startpc */ + quickConstant = (((usqInt)(((blockStart->startpc)) + 1) << 3) | 1); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + if (lowBlockStartIndex == halfWay) { + /* JumpLessOrEqual: */ + genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)((blockStart->entryLabel)))); + compileBlockDispatchFromto(halfWay + 1, highBlockStartIndex); + return null; + } + if ((halfWay + 1) == highBlockStartIndex) { + blockStart = blockStartAt(highBlockStartIndex); + + /* JumpGreater: */ + genConditionalBranchoperand(JumpGreater, ((sqInt)((blockStart->entryLabel)))); + return compileBlockDispatchFromto(lowBlockStartIndex, halfWay); + } + jmp = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + compileBlockDispatchFromto(lowBlockStartIndex, halfWay); + if (halfWay == highBlockStartIndex) { + blockStart = blockStartAt(highBlockStartIndex); + jmpTarget(jmp, (blockStart->entryLabel)); + } + else { + jmpTarget(jmp, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + compileBlockDispatchFromto(halfWay + 1, highBlockStartIndex); + } + return 0; +} + + +/* Compile a block's entry. This looks like a dummy CogBlockMethod header + (for frame parsing) + followed by either a frame build, if a frame is required, or nothing. The + CogMethodHeader's objectHeader field is a back pointer to the method, but + this can't be filled in until code generation. */ + + /* Cogit>>#compileBlockEntry: */ +static NoDbgRegParms void +compileBlockEntry(BlockStart *blockStart) +{ + AbstractInstruction *abstractInstruction; + sqInt alignment; + + alignment = blockAlignment(); + + /* begin AlignmentNops: */ + genoperand(AlignmentNops, alignment); + (blockStart->fakeHeader = genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + switch (sizeof(CogBlockMethod)) { + case 8: + /* Fill32: */ + genoperand(Fill32, 0); + + /* Fill32: */ + genoperand(Fill32, 0); + break; + case 12: + /* Fill32: */ + genoperand(Fill32, 0); + + /* Fill32: */ + genoperand(Fill32, 0); + + /* Fill32: */ + genoperand(Fill32, 0); + break; + case 16: + /* Fill32: */ + genoperand(Fill32, 0); + + /* Fill32: */ + genoperand(Fill32, 0); + + /* Fill32: */ + genoperand(Fill32, 0); + + /* Fill32: */ + genoperand(Fill32, 0); + break; + default: + error("Case not found and no otherwise clause"); + } + + /* gets filled in later with numArgs et al + gets filled in later with numArgs et al */ + (blockStart->entryLabel = genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + if (needsFrame) { + compileBlockFrameBuild(blockStart); + if (recordBlockTrace()) { + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceTraceBlockActivationTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + } + } + else { + compileBlockFramelessEntry(blockStart); + } +} + + +/* Generate a call to aRoutine with up to 4 arguments. If resultRegOrNone is + not NoReg assign the C result to resultRegOrNone. If saveRegs, save all + registers. Hack: a negative arg value indicates an abstract register, a + non-negative value + indicates a constant. The encoding for constants is defined by + trampolineArgConstant: & trampolineArgValue:. Pass a constant as the + result of trampolineArgConstant:. */ + + /* Cogit>>#compileCallFor:numArgs:arg:arg:arg:arg:resultReg:regsToSave: */ +static NoDbgRegParms void +compileCallFornumArgsargargargargresultRegregsToSave(void *aRoutine, sqInt numArgs, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3, sqInt resultRegOrNone, sqInt regMask) +{ + AbstractInstruction *abstractInstruction; + sqInt callTargetSqInt; + const int cStackAlignment = STACK_ALIGN_BYTES & STACK_ALIGN_MASK; + sqInt regsToSave; + + regsToSave = (resultRegOrNone == NoReg + ? regMask + : ((regMask | (((resultRegOrNone < 0) ? (((usqInt)(1)) >> (-resultRegOrNone)) : (1ULL << resultRegOrNone)))) - (((resultRegOrNone < 0) ? (((usqInt)(1)) >> (-resultRegOrNone)) : (1ULL << resultRegOrNone))))); + if (cStackAlignment > BytesPerWord) { + /* genAlignCStackGivenRegistersToBeSaved:numArgs:wordAlignment: */ + } + genSaveRegs(backEnd, regsToSave); + genMarshallNArgsargargargarg(backEnd, numArgs, regOrConst0, regOrConst1, regOrConst2, regOrConst3); + + /* begin CallAnyFixedRT: */ + if (isInImmediateBranchAndLinkRange(backEnd, maximumDistanceFromCodeZone(((usqInt)aRoutine)))) { + callTargetSqInt = ((usqIntptr_t)aRoutine); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, callTargetSqInt); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* #CallFullRT: #CallFull: #gen:literal: */ + checkLiteralforInstruction(((usqIntptr_t)aRoutine), genoperand(CallFull, ((usqIntptr_t)aRoutine))); + } + genWriteCResultIntoReg(backEnd, resultRegOrNone); + + /* begin genRemoveNArgsFromStack: */ + assert(numArgs <= 6); + genRestoreRegs(backEnd, regsToSave); +} + + +/* Compile the cache tag computation and the first comparison. Answer the + address of that comparison. */ + + /* Cogit>>#compileCPICEntry */ +static AbstractInstruction * +compileCPICEntry(void) +{ + entry = genGetInlineCacheClassTagFromintoforEntry(ReceiverResultReg, TempReg, 1); + + /* begin CmpR:R: */ + assert(!(0 /* (ClassReg = SPReg) */)); + genoperandoperand(CmpRR, ClassReg, TempReg); + return genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +} + + +/* Compile the abstract instructions for the entire full block method. */ + + /* Cogit>>#compileEntireFullBlockMethod: */ +static NoDbgRegParms sqInt +compileEntireFullBlockMethod(sqInt numCopied) +{ + sqInt result; + + /* begin preenMethodLabel */ + /* setLabelOffset: */ + ((methodLabel->operands))[1] = 0; + compileFullBlockEntry(); + + /* Frame build */ + compileFullBlockMethodFrameBuild(numCopied); + + /* Method body */ + if (((result = compileMethodBody())) < 0) { + return result; + } + assert(blockCount == 0); + return 0; +} + + +/* The entry code to a method checks that the class of the current receiver + matches that in the inline cache. Other non-obvious elements are that its + alignment must be + different from the alignment of the noCheckEntry so that the method map + machinery can distinguish normal and super sends (super sends bind to the + noCheckEntry). */ + + /* Cogit>>#compileEntry */ +static void +compileEntry(void) +{ + AbstractInstruction *inst; + + entry = genGetInlineCacheClassTagFromintoforEntry(ReceiverResultReg, TempReg, 1); + + /* begin CmpR:R: */ + assert(!(0 /* (ClassReg = SPReg) */)); + genoperandoperand(CmpRR, ClassReg, TempReg); + + /* JumpNonZero: */ + genConditionalBranchoperand(JumpNonZero, ((sqInt)sendMiss)); + noCheckEntry = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + if (((traceFlags & 64) == 64)) { + /* begin saveAndRestoreLinkRegAround: */ + inst = genoperand(PushR, LinkReg); + + /* #CallFullRT: #CallFull: #gen:literal: */ + checkLiteralforInstruction(ceTraceLinkedSendTrampoline, genoperand(CallFull, ceTraceLinkedSendTrampoline)); + + /* PopR: */ + genoperand(PopR, LinkReg); + } +} + + +/* Compile the abstract instructions for the entire method, including blocks. */ +/* Abort for stack overflow on full block activation (no inline cache miss + possible). The flag is SendNumArgsReg. */ + + /* Cogit>>#compileFullBlockEntry */ +static sqInt +compileFullBlockEntry(void) +{ + AbstractInstruction *anInstruction; + sqInt callTarget; + AbstractInstruction *jumpNoContextSwitch; + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, 0, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + stackOverflowCall = anInstruction; + + /* PushR: */ + genoperand(PushR, LinkReg); + + /* Since the only case in which this is called is the + stack overflow case we can reuse the trampoline. */ + callTarget = methodAbortTrampolineFor(methodOrBlockNumArgs); + + /* begin Call: */ + genoperand(Call, callTarget); + + /* Entries + No context switch entry */ + anInstruction = genoperandoperand(MoveCqR, 0, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + fullBlockNoContextSwitchEntry = anInstruction; + jumpNoContextSwitch = genoperand(Jump, ((sqInt)0)); + + /* AlignmentNops: */ + genoperand(AlignmentNops, ((BytesPerWord < 8) ? 8 : BytesPerWord)); + + /* Context switch entry (use ReceiverResultReg as a non-zero value; it's shorter). */ + fullBlockEntry = genoperandoperand(MoveRR, ReceiverResultReg, SendNumArgsReg); + jmpTarget(jumpNoContextSwitch, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + +/* Compile the top-level method body. */ + + /* Cogit>>#compileMethodBody */ +static sqInt +compileMethodBody(void) +{ + if (endPC < initialPC) { + return 0; + } + return compileBytecodesFromthrough(initialPC + (deltaToSkipPrimAndErrorStoreInheader(methodObj, methodHeader)), endPC); +} + + +/* The start of a PIC has a call to a run-time abort routine that either + handles a dispatch to an + interpreted method or a dispatch of an MNU case. The routine selects the + path by testing + ClassReg, which holds the inline cache tag; if equal to the + picAbortDiscriminatorValue (zero) + it takes the MNU path; if nonzero the dispatch to interpreter path. + Neither of these paths + returns. The abort routine must be called; In the callee the PIC is + located by adding the + relevant offset to the return address of the call. + + N.B. This code must match that in compileAbort so that the offset of the + return address of + the call is the same in methods and closed PICs. */ + + /* Cogit>>#compilePICAbort: */ +static NoDbgRegParms sqInt +compilePICAbort(sqInt numArgs) +{ + AbstractInstruction *anInstruction; + sqInt callTarget; + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, 0 /* picAbortDiscriminatorValue */, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0 /* picAbortDiscriminatorValue */, BytesPerOop)); + } + picMNUAbort = anInstruction; + + /* If there is a link register it must be saved (pushed onto the stack) before it + is smashed by the abort call, and hence needs to be manually handled here */ + picInterpretAbort = genoperand(PushR, LinkReg); + callTarget = picAbortTrampolineFor(numArgs); + + /* begin Call: */ + genoperand(Call, callTarget); + return 0; +} + + +/* Compile the compare of stackLimit against the stack pointer, jumping to + the stackOverflowCall if + the stack pointer is below the limit. Answer a bytecode annotated label + that follows the sequence. + + The stack check functions both as a genuine stack limit check to prevent + calls overflowing stack pages, + and as an event/context-switch break out. To cause an event check + (including a check for a required + context switch), stackLimit is set to the highest possible value, and + hence all stack limit checks will + fail. A path in the stack overflow abort then arranges to call event + checking if it has been requested. + + Certain block activations (e.g. valueNoContextSwitch:) must not context + switch, and in that + case, SendNumArgs is set to zero to communicate to the stack overflow + abort that it should + not perform event/context-switch (yet). */ + + /* Cogit>>#compileStackOverflowCheck: */ +static NoDbgRegParms AbstractInstruction * +compileStackOverflowCheck(sqInt canContextSwitch) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpSkip; + AbstractInstruction *label; + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(stackLimitAddress(), genoperandoperand(MoveAwR, stackLimitAddress(), TempReg)); + + /* N.B. We do not expect the backEnd to provide CmpR: SPReg R: reg, only CmpR: reg R: SPReg */ + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, SPReg); + if (canContextSwitch) { + /* JumpBelow: */ + genConditionalBranchoperand(JumpBelow, ((sqInt)stackOverflowCall)); + label = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + } + else { + jumpSkip = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, 0, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* Jump: */ + genoperand(Jump, ((sqInt)stackOverflowCall)); + jmpTarget(jumpSkip, (label = genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + } + + /* begin annotateBytecode: */ + (label->annotation = HasBytecodePC); + return label; +} + + +/* Generate a trampoline with up to four arguments. Generate either a call or + a jump to aRoutine + as requested by callJumpBar. If generating a call and resultRegOrNone is + not NoReg pass the C + result back in resultRegOrNone. + Hack: a negative value indicates an abstract register, a non-negative + value indicates a constant. */ + + /* Cogit>>#compileTrampolineFor:numArgs:arg:arg:arg:arg:regsToSave:pushLinkReg:resultReg: */ +static NoDbgRegParms void +compileTrampolineFornumArgsargargargargregsToSavepushLinkRegresultReg(void *aRoutine, sqInt numArgs, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3, sqInt regMask, sqInt pushLinkReg, sqInt resultRegOrNone) +{ + genSmalltalkToCStackSwitch(pushLinkReg); + compileCallFornumArgsargargargargresultRegregsToSave(aRoutine, numArgs, regOrConst0, regOrConst1, regOrConst2, regOrConst3, resultRegOrNone, regMask); + genLoadStackPointers(backEnd); + genTrampolineReturn(pushLinkReg); +} + + +/* Generate the entry code for a method to determine cmEntryOffset and + cmNoCheckEntryOffset. We + need cmNoCheckEntryOffset up front to be able to generate the map starting + from cmNoCheckEntryOffset */ +/* stack allocate the various collections so that they + are effectively garbage collected on return. */ + + /* Cogit>>#computeEntryOffsets */ +static void +computeEntryOffsets(void) +{ + usqIntptr_t fixupSize; + usqIntptr_t opcodeSize; + AbstractInstruction *sendMissCall; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 24; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + firstOpcodeIndex = 0x10000; + nextLiteralIndex = (lastDumpedLiteralIndex = 0); + labelCounter = 0; + methodOrBlockNumArgs = 0; + sendMissCall = compileAbort(); + compileEntry(); + computeMaximumSizes(); + generateInstructionsAt(methodZoneBase + (sizeof(CogMethod))); + cmEntryOffset = ((entry->address)) - methodZoneBase; + cmNoCheckEntryOffset = ((noCheckEntry->address)) - methodZoneBase; + missOffset = (((sendMissCall->address)) + ((sendMissCall->machineCodeSize))) - methodZoneBase; + entryPointMask = BytesPerWord - 1; + while ((cmEntryOffset & entryPointMask) == (cmNoCheckEntryOffset & entryPointMask)) { + entryPointMask = (entryPointMask + entryPointMask) + 1; + } + if (entryPointMask >= (roundUpToMethodAlignment(backEnd, 1))) { + error("cannot differentiate checked and unchecked entry-points with current cog method alignment"); + } + checkedEntryAlignment = cmEntryOffset & entryPointMask; + uncheckedEntryAlignment = cmNoCheckEntryOffset & entryPointMask; + assert(checkedEntryAlignment != uncheckedEntryAlignment); +} + + +/* Generate the entry code for a method to determine cmEntryOffset and + cmNoCheckEntryOffset. We + need cmNoCheckEntryOffset up front to be able to generate the map starting + from cmNoCheckEntryOffset */ +/* stack allocate the various collections so that they + are effectively garbage collected on return. */ + + /* Cogit>>#computeFullBlockEntryOffsets */ +static void +computeFullBlockEntryOffsets(void) +{ + usqIntptr_t fixupSize; + usqIntptr_t opcodeSize; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 24; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + firstOpcodeIndex = 0x10000; + nextLiteralIndex = (lastDumpedLiteralIndex = 0); + labelCounter = 0; + methodOrBlockNumArgs = 0; + compileFullBlockEntry(); + computeMaximumSizes(); + generateInstructionsAt(methodZoneBase + (sizeof(CogMethod))); + cbEntryOffset = ((fullBlockEntry->address)) - methodZoneBase; + cbNoSwitchEntryOffset = ((fullBlockNoContextSwitchEntry->address)) - methodZoneBase; +} + + +/* While we order variables in the CoInterpreter in order of dynamic + frequency, and hence + expect that stackPointer will be output first, C optimizers and linkers + may get their own + ideas and ``improve upon'' this ordering. So we cannot depend on + stackPointer being + at the lowest address of the variables we want to access through + VarBaseReg. Here we + choose the minimum amongst a set to try to choose a varBaseAddress that is + just less + than but within range of all variables we want to access through it. */ + + /* Cogit>>#computeGoodVarBaseAddress */ +usqInt +computeGoodVarBaseAddress(void) +{ + usqInt minAddress; + + /* stackLimit was e.g. lowest using the clang toolchain on MacOS X (prior to the use of variable_order) */ + minAddress = stackLimitAddress(); + if ((stackPointerAddress()) < minAddress) { + minAddress = stackPointerAddress(); + } + if ((framePointerAddress()) < minAddress) { + minAddress = framePointerAddress(); + } + if ((instructionPointerAddress()) < minAddress) { + minAddress = instructionPointerAddress(); + } + if ((argumentCountAddress()) < minAddress) { + minAddress = argumentCountAddress(); + } + if ((primFailCodeAddress()) < minAddress) { + minAddress = primFailCodeAddress(); + } + return minAddress; +} + + +/* This pass assigns maximum sizes to all abstract instructions and + eliminates jump fixups. + It hence assigns the maximum address an instruction will occur at which + allows the next + pass to conservatively size jumps. */ + + /* Cogit>>#computeMaximumSizes */ +static void +computeMaximumSizes(void) +{ + AbstractInstruction *abstractInstruction; + sqInt i; + sqInt relativeAddress; + + dumpLiterals(0); + relativeAddress = 0; + for (i = 0; i < opcodeIndex; i += 1) { + maybeBreakGeneratingInstructionWithIndex(i); + abstractInstruction = abstractInstructionAt(i); + (abstractInstruction->address = relativeAddress); + (abstractInstruction->maxSize = computeMaximumSize(abstractInstruction)); + relativeAddress += (abstractInstruction->maxSize); + } +} + + +/* Configure a copy of the prototype CPIC for a two-case PIC for + case0CogMethod and + case1Method + case1Tag. + The tag for case0CogMethod is at the send site and so doesn't need to be + generated. case1Method may be any of + - a Cog method; jump to its unchecked entry-point + - a CompiledMethod; jump to the ceInterpretFromPIC trampoline + - nil; call ceMNUFromPIC + addDelta is the address change from the prototype to the new CPIC + location, needed + because the loading of the CPIC label at the end may use a literal instead + of a pc relative load. */ +/* self disassembleFrom: cPIC asInteger + (self sizeof: CogMethod) to: cPIC + asInteger + closedPICSize + */ + + /* Cogit>>#configureCPIC:Case0:Case1Method:tag:isMNUCase:numArgs:delta: */ +static NoDbgRegParms sqInt +configureCPICCase0Case1MethodtagisMNUCasenumArgsdelta(CogMethod *cPIC, CogMethod *case0CogMethod, sqInt case1Method, sqInt case1Tag, sqInt isMNUCase, sqInt numArgs, sqInt addrDelta) +{ + sqInt callTargetAddress; + sqInt caseEndAddress; + sqInt operand; + sqInt pc; + usqIntptr_t targetEntry; + + assert(case1Method); + + /* adjust the call at missOffset, the ceAbortXArgs */ + rewriteCallAttarget(backEnd, (((sqInt)cPIC)) + missOffset, picAbortTrampolineFor(numArgs)); + assert(!(inlineCacheTagIsYoung(case1Tag))); + if ((!isMNUCase) + && (methodHasCogMethod(case1Method))) { + operand = 0; + targetEntry = (((sqInt)(cogMethodOf(case1Method)))) + cmNoCheckEntryOffset; + } + else { + operand = ((!case1Method) + || (isYoungObject(case1Method)) + ? 0 + : case1Method); + targetEntry = (case1Method + ? (((sqInt)cPIC)) + (picInterpretAbortOffset()) + : (((sqInt)cPIC)) + (sizeof(CogMethod))); + } + + /* We do not scavenge PICs, hence we cannot cache the MNU method if it is in new space. + set the jump to the case0 method */ + + /* rewriteJumpLongAt:target: */ + rewriteImm26JumpBeforetarget(backEnd, (((sqInt)cPIC)) + firstCPICCaseOffset, (((sqInt)case0CogMethod)) + cmNoCheckEntryOffset); + caseEndAddress = addressOfEndOfCaseinCPIC(2, cPIC); + + /* update the cpic case */ + rewriteCPICCaseAttagobjReftarget(caseEndAddress, case1Tag, operand, ((sqInt)((isMNUCase + ? (((sqInt)cPIC)) + (sizeof(CogMethod)) + : targetEntry)))); + + /* update the loading of the CPIC address */ + pc = ((((sqInt)cPIC)) + cPICEndOfCodeOffset) - (jumpLongByteSize(backEnd)); + + /* begin relocateMethodReferenceBeforeAddress:by: */ + assert((instructionIsADR(backEnd, instructionAt(backEnd, pc - 4))) + || (instructionIsADR(backEnd, instructionAt(backEnd, pc - 8)))); + + /* write the final desperate jump to cePICMissXArgs */ + callTargetAddress = cPICMissTrampolineFor(numArgs); + + /* begin rewriteJumpLongAt:target: */ + rewriteImm26JumpBeforetarget(backEnd, (((sqInt)cPIC)) + cPICEndOfCodeOffset, callTargetAddress); + return 0; +} + + +/* Configure a copy of the prototype CPIC for a one-case MNU CPIC that calls + ceMNUFromPIC for + case0Tag The tag for case0 is at the send site and so doesn't need to be + generated. addDelta is the address change from the prototype to the new + CPIC location, needed + because the loading of the CPIC label at the end may be a literal instead + of a pc-relative load. */ +/* adjust the jump at missOffset, the ceAbortXArgs */ + + /* Cogit>>#configureMNUCPIC:methodOperand:numArgs:delta: */ +static NoDbgRegParms sqInt +configureMNUCPICmethodOperandnumArgsdelta(CogMethod *cPIC, sqInt methodOperand, sqInt numArgs, sqInt addrDelta) +{ + usqInt addressFollowingJump; + sqInt callTargetAddress; + usqInt jumpTargetAddr; + sqInt operand; + sqInt pc; + sqInt target; + + rewriteCallAttarget(backEnd, (((sqInt)cPIC)) + missOffset, picAbortTrampolineFor(numArgs)); + + /* We do not scavenge PICs, hence we cannot cache the MNU method if it is in new space. */ + operand = ((!methodOperand) + || (isYoungObject(methodOperand)) + ? 0 + : methodOperand); + + /* set the jump to the case0 method */ + callTargetAddress = (((sqInt)cPIC)) + (sizeof(CogMethod)); + + /* begin rewriteJumpLongAt:target: */ + rewriteImm26JumpBeforetarget(backEnd, (((sqInt)cPIC)) + firstCPICCaseOffset, callTargetAddress); + storeLiteralbeforeFollowingAddress(backEnd, operand, ((((sqInt)cPIC)) + firstCPICCaseOffset) - (jumpLongByteSize(backEnd))); + + /* rewrite the final desperate jump to cePICMissXArgs */ + callTargetAddress = cPICMissTrampolineFor(numArgs); + + /* begin rewriteJumpLongAt:target: */ + rewriteImm26JumpBeforetarget(backEnd, (((sqInt)cPIC)) + cPICEndOfCodeOffset, callTargetAddress); + pc = ((((sqInt)cPIC)) + cPICEndOfCodeOffset) - (jumpLongByteSize(backEnd)); + + /* begin relocateMethodReferenceBeforeAddress:by: */ + assert((instructionIsADR(backEnd, instructionAt(backEnd, pc - 4))) + || (instructionIsADR(backEnd, instructionAt(backEnd, pc - 8)))); + + /* finally, rewrite the jump 3 instr before firstCPICCaseOffset to jump to the end of case 2, missing the actual case */ + target = addressOfEndOfCaseinCPIC(2, cPIC); + + /* begin rewriteCPIC:caseJumpTo: */ + addressFollowingJump = (((((sqInt)cPIC)) + firstCPICCaseOffset) - (jumpLongByteSize(backEnd))) - (loadLiteralByteSize(backEnd)); + jumpTargetAddr = target; + + /* begin rewriteCPICJumpAt:target: */ + rewriteImm19JumpBeforetarget(backEnd, addressFollowingJump, jumpTargetAddr); + return 0; +} + + +/* A CogMethod effectively imports the literals it uses and the methods it + calls. Count these elements so an array of imports can be computed. */ + + /* Cogit>>#countCogMethodDataImports:pc:count: */ +#if VMInvestigations +static NoDbgRegParms sqInt +countCogMethodDataImportspccount(sqInt annotation, char *mcpc, sqInt *countPtr) +{ + unsigned int cacheTag1; + sqInt entryPointSqInt; + unsigned int instr; + sqInt pc; + + if ((annotation == IsObjectReference) + || (annotation == IsRelativeCall)) { + countPtr[0] = ((countPtr[0]) + 1); + } + if (annotation >= IsSendCall) { + /* begin entryCacheTagAndCouldBeObjectAt:annotation:into: */ + /* begin inlineCacheTagAt: */ + /* begin pcRelativeAddressAt: */ + instr = long32At((void *)(((usqInt)((((sqInt)mcpc)) - 8)))); + + /* cogit processor disassembleInstructionAt: mcpc In: objectMemory memory */ + assert(instructionIsPCRelativeLoad(backEnd, instr)); + pc = (((((usqInt)(instr)) >> 3) & (0x3FFFFC)) - ((((usqInt)(instr)) >> 2) & (0x200000))) + (((usqInt)((((sqInt)mcpc)) - 8))); + cacheTag1 = long32At((void *)(pc)); + entryPointSqInt = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + + /* in-line cache tags are the selectors of sends if sends are unlinked, + the selectors of super sends (entry offset = cmNoCheckEntryOffset), + the selectors of open PIC sends (entry offset = cmEntryOffset, target is an Open PIC) + or in-line cache tags (classes, class indices, immediate bit patterns, etc). + Note that selectors can be immediate so there is no guarantee that they + are markable/remappable objects. Writing the block invocation three times + allows Slang to inline tagCouldBeObject tests. */ + countPtr[0] = ((countPtr[0]) + 1); + } + return 0; +} +#endif /* VMInvestigations */ + + +/* Scan the CPIC for target methods that have been freed and eliminate them. + Since the first entry cannot be eliminated, answer that the PIC should be + freed if the first entry is to a free target. Answer if the PIC is now + empty or should be freed. */ + + /* Cogit>>#cPICCompactAndIsNowEmpty: */ +static NoDbgRegParms sqInt +cPICCompactAndIsNowEmpty(CogMethod *cPIC) +{ + usqInt addressFollowingJump; + sqInt entryPoint; + sqInt i; + usqInt jumpTargetAddr; + sqInt methods[MaxCPICCases]; + sqInt pc; + int tags[MaxCPICCases]; + CogMethod *targetMethod; + sqInt targets[MaxCPICCases]; + sqInt used; + sqInt valid; + + used = 0; + for (i = 1; i <= ((cPIC->cPICNumCases)); i += 1) { + pc = addressOfEndOfCaseinCPIC(i, cPIC); + entryPoint = (jumpLongTargetBeforeFollowingAddress(backEnd, pc)); + valid = 1; + + /* Collect all target triples except for triples whose entry-point is a freed method */ + if (!(/* containsAddress: */ + ((((usqInt)cPIC)) <= (((usqInt)entryPoint))) + && (((((usqInt)cPIC)) + ((cPIC->blockSize))) >= (((usqInt)entryPoint))))) { + targetMethod = ((CogMethod *) (entryPoint - cmNoCheckEntryOffset)); + assert((isCMMethodEtAl(((CogBlockMethod *) targetMethod))) + || (isCMFree(((CogBlockMethod *) targetMethod)))); + if (((targetMethod->cmType)) == CMFree) { + if (i == 1) { + return 1; + } + valid = 0; + } + } + if (valid) { + tags[used] = ((i > 1 + ? literal32BeforeFollowingAddress(backEnd, pc - (jumpLongConditionalByteSize(backEnd))) + : 0)); + targets[used] = entryPoint; + methods[used] = (literalBeforeFollowingAddress(backEnd, pc - ((i == 1 + ? jumpLongByteSize(backEnd) + : (jumpLongConditionalByteSize(backEnd)) + (cmpC32RTempByteSize(backEnd)))))); + used += 1; + } + } + if (used == ((cPIC->cPICNumCases))) { + return 0; + } + if (!used) { + return 1; + } + ((((CogMethod *) ((((usqInt)cPIC)) + codeToDataDelta)))->cPICNumCases = used); + if (used == 1) { + pc = addressOfEndOfCaseinCPIC(2, cPIC); + + /* begin rewriteCPIC:caseJumpTo: */ + addressFollowingJump = (((((sqInt)cPIC)) + firstCPICCaseOffset) - (jumpLongByteSize(backEnd))) - (loadLiteralByteSize(backEnd)); + jumpTargetAddr = pc; + + /* begin rewriteCPICJumpAt:target: */ + rewriteImm19JumpBeforetarget(backEnd, addressFollowingJump, jumpTargetAddr); + return 0; + } + + /* the first entry cannot change... */ + for (i = 1; i < used; i += 1) { + pc = addressOfEndOfCaseinCPIC(i + 1, cPIC); + rewriteCPICCaseAttagobjReftarget(pc, tags[i], methods[i], targets[i]); + } + + /* finally, rewrite the jump 3 instr before firstCPICCaseOffset to jump to the beginning of this new case */ + + /* begin rewriteCPIC:caseJumpTo: */ + addressFollowingJump = (((((sqInt)cPIC)) + firstCPICCaseOffset) - (jumpLongByteSize(backEnd))) - (loadLiteralByteSize(backEnd)); + jumpTargetAddr = pc - cPICCaseSize; + + /* begin rewriteCPICJumpAt:target: */ + rewriteImm19JumpBeforetarget(backEnd, addressFollowingJump, jumpTargetAddr); + return 0; +} + + +/* The first case in a CPIC doesn't have a class reference so we need only + step over actually usd subsequent cases. + */ + + /* Cogit>>#cPICHasForwardedClass: */ +static NoDbgRegParms sqInt +cPICHasForwardedClass(CogMethod *cPIC) +{ + unsigned int classIndex; + sqInt i; + sqInt pc; + + + /* start by finding the address of the topmost case, the cPICNumCases'th one */ + pc = (addressOfEndOfCaseinCPIC((cPIC->cPICNumCases), cPIC)) - (jumpLongConditionalByteSize(backEnd)); + for (i = 2; i <= ((cPIC->cPICNumCases)); i += 1) { + classIndex = literal32BeforeFollowingAddress(backEnd, pc); + if (isForwardedClassIndex(classIndex)) { + return 1; + } + + /* since we started at the top, we can just add the case size each time to move on to the next case */ + pc += cPICCaseSize; + } + return 0; +} + + +/* scan the CPIC for target methods that have been freed. */ + + /* Cogit>>#cPICHasFreedTargets: */ +static NoDbgRegParms sqInt +cPICHasFreedTargets(CogMethod *cPIC) +{ + sqInt entryPoint; + sqInt i; + sqInt pc; + CogMethod *targetMethod; + + for (i = 1; i <= ((cPIC->cPICNumCases)); i += 1) { + pc = addressOfEndOfCaseinCPIC(i, cPIC); + entryPoint = (jumpLongTargetBeforeFollowingAddress(backEnd, pc)); + + /* Find target from jump. Ignore jumps to the interpret and MNU calls within this PIC */ + if (!(/* containsAddress: */ + ((((usqInt)cPIC)) <= (((usqInt)entryPoint))) + && (((((usqInt)cPIC)) + ((cPIC->blockSize))) >= (((usqInt)entryPoint))))) { + targetMethod = ((CogMethod *) (entryPoint - cmNoCheckEntryOffset)); + assert((isCMMethodEtAl(((CogBlockMethod *) targetMethod))) + || (isCMFree(((CogBlockMethod *) targetMethod)))); + if (((targetMethod->cmType)) == CMFree) { + return 1; + } + } + } + return 0; +} + + +/* Whimsey; we want 16rCA5E10 + cPICPrototypeCaseOffset to be somewhere in + the middle of the zone. + */ + + /* Cogit>>#cPICPrototypeCaseOffset */ +static sqInt +cPICPrototypeCaseOffset(void) +{ + return ((methodZoneBase + (youngReferrers)) / 2) - 13262352; +} + + +/* Are any of the jumps from this CPIC to targetMethod? */ + + /* Cogit>>#cPIC:HasTarget: */ +static NoDbgRegParms sqInt +cPICHasTarget(CogMethod *cPIC, CogMethod *targetMethod) +{ + sqInt i; + sqInt pc; + usqInt target; + + target = (((usqInt)targetMethod)) + cmNoCheckEntryOffset; + pc = (((sqInt)cPIC)) + firstCPICCaseOffset; + + /* Since this is a fast test doing simple compares we don't need to care that some + cases have nonsense addresses in there. Just zip on through. + First jump is unconditional; subsequent ones are conditional */ + if (target == (jumpLongTargetBeforeFollowingAddress(backEnd, pc))) { + return 1; + } + for (i = 2; i <= MaxCPICCases; i += 1) { + pc += cPICCaseSize; + if (target == (jumpLongTargetBeforeFollowingAddress(backEnd, pc))) { + return 1; + } + } + return 0; +} + + +/* Answer an Array of the PIC's selector, followed by class and + targetMethod/doesNotUnderstand: for each entry in the PIC. + */ + + /* Cogit>>#createCPICData: */ +static NoDbgRegParms sqInt +createCPICData(CogMethod *cPIC) +{ + sqInt class; + sqInt entryPoint; + sqInt i; + sqInt pc; + sqInt picData; + sqInt target; + CogMethod *targetMethod; + + assert((((cPIC->methodObject)) == 0) + || (addressCouldBeOop((cPIC->methodObject)))); + picData = instantiateClassindexableSize(classArray(), (((cPIC->cPICNumCases)) * 2) + 1); + if (!picData) { + return picData; + } + storePointerUncheckedofObjectwithValue(0, picData, (cPIC->selector)); + for (i = 1; i <= ((cPIC->cPICNumCases)); i += 1) { + pc = addressOfEndOfCaseinCPIC(i, cPIC); + if (i == 1) { + /* first case may have been collected and stored here by collectCogConstituentFor:Annotation:Mcpc:Bcpc:Method: */ + class = (cPIC->methodObject); + if (!class) { + class = nilObject(); + } + entryPoint = jumpLongTargetBeforeFollowingAddress(backEnd, pc); + } + else { + class = classForInlineCacheTag(literal32BeforeFollowingAddress(backEnd, pc - (jumpLongConditionalByteSize(backEnd)))); + entryPoint = jumpLongTargetBeforeFollowingAddress(backEnd, pc); + } + + /* Find target from jump. A jump to the MNU entry-point should collect #doesNotUnderstand: */ + if (/* containsAddress: */ + ((((usqInt)cPIC)) <= (((usqInt)entryPoint))) + && (((((usqInt)cPIC)) + ((cPIC->blockSize))) >= (((usqInt)entryPoint)))) { + target = splObj(SelectorDoesNotUnderstand); + } + else { + targetMethod = ((CogMethod *) (entryPoint - cmNoCheckEntryOffset)); + assert(isCMMethodEtAl(((CogBlockMethod *) targetMethod))); + target = (targetMethod->methodObject); + } + storePointerUncheckedofObjectwithValue((i * 2) - 1, picData, class); + storePointerUncheckedofObjectwithValue(i * 2, picData, target); + } + beRootIfOld(picData); + ((((CogMethod *) ((((usqInt)cPIC)) + codeToDataDelta)))->methodObject = 0); + return picData; +} + + +/* Division is a little weird on some processors. Defer to the backEnd + to allow it to generate any special code it may need to. */ + + /* Cogit>>#DivR:R:Quo:Rem: */ +static NoDbgRegParms AbstractInstruction * +gDivRRQuoRem(sqInt rDivisor, sqInt rDividend, sqInt rQuotient, sqInt rRemainder) +{ + genDivRRQuoRem(backEnd, rDivisor, rDividend, rQuotient, rRemainder); + return abstractInstructionAt(opcodeIndex - 1); +} + + +/* Return the default number of bytes to allocate for native code at startup. + The actual value can be set via vmParameterAt: and/or a preference in the + ini file. */ + + /* Cogit>>#defaultCogCodeSize */ +int +defaultCogCodeSize(void) +{ + return 0x1A0000; +} + + +/* Answer the number of bytecodes to skip to get to the first bytecode + past the primitive call and any store of the error code. */ + + /* Cogit>>#deltaToSkipPrimAndErrorStoreIn:header: */ +static NoDbgRegParms sqInt +deltaToSkipPrimAndErrorStoreInheader(sqInt aMethodObj, sqInt aMethodHeader) +{ + return (/* methodUsesPrimitiveErrorCode:header: */ + ((primitiveIndexOfMethodheader(aMethodObj, aMethodHeader)) > 0) + && ((longStoreBytecodeForHeader(aMethodHeader)) == (fetchByteofObject((startPCOfMethodHeader(aMethodHeader)) + (sizeOfCallPrimitiveBytecode(aMethodHeader)), aMethodObj))) + ? (sizeOfCallPrimitiveBytecode(aMethodHeader)) + (sizeOfLongStoreTempBytecode(aMethodHeader)) + : 0); +} + + /* Cogit>>#endPCOf: */ +static NoDbgRegParms sqInt +endPCOf(sqInt aMethod) +{ + sqInt bsOffset; + sqInt byte; + BytecodeDescriptor *descriptor; + sqInt distance; + sqInt end; + sqInt latestContinuation; + sqInt nExts; + sqInt pc; + sqInt prim; + sqInt targetPC; + + pc = (latestContinuation = startPCOfMethod(aMethod)); + if (((prim = primitiveIndexOf(aMethod))) > 0) { + if (isQuickPrimitiveIndex(prim)) { + return pc - 1; + } + } + bsOffset = /* bytecodeSetOffsetFor: */ + (methodUsesAlternateBytecodeSet(aMethod) + ? 0x100 + : 0); + nExts = 0; + end = numBytesOf(aMethod); + while (pc <= end) { + byte = fetchByteofObject(pc, aMethod); + descriptor = generatorAt(byte + bsOffset); + if (((descriptor->isReturn)) + && (pc >= latestContinuation)) { + end = pc; + } + if ((isBranch(descriptor)) + || ((descriptor->isBlockCreation))) { + distance = ((descriptor->spanFunction))(descriptor, pc, nExts, aMethod); + targetPC = (pc + ((descriptor->numBytes))) + distance; + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + if ((descriptor->isBlockCreation)) { + pc += distance; + } + } + else { + } + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + pc += (descriptor->numBytes); + } + return end; +} + + +/* This is a static version of ceEnterCogCodePopReceiverReg for + break-pointing when debugging in C. Marked so the code generator + won't delete it. */ + + /* Cogit>>#enterCogCodePopReceiver */ +static void +enterCogCodePopReceiver(void) +{ + realCEEnterCogCodePopReceiverReg(); + + /* (and this exists only to reference Debug) */ + if (!Debug) { + error("what??"); + } +} + + +/* Answer if the entryPoint's tag is expected to be a selector reference, as + opposed to a class tag. + */ + + /* Cogit>>#entryPointTagIsSelector: */ +static NoDbgRegParms sqInt +entryPointTagIsSelector(sqInt entryPoint) +{ + return (entryPoint < methodZoneBase) + || (((entryPoint & entryPointMask) == uncheckedEntryAlignment) + || (((entryPoint & entryPointMask) == checkedEntryAlignment) + && ((((((CogMethod *) (entryPoint - cmEntryOffset)))->cmType)) == CMOpenPIC))); +} + + +/* Use asserts to check if the ClosedPICPrototype is as expected from + compileClosedPICPrototype, and can be updated as required via + rewriteCPICCaseAt:tag:objRef:target:. If all asserts pass, answer + 0, otherwise answer a bit mask identifying all the errors. */ +/* self disassembleFrom: methodZoneBase + (self sizeof: CogMethod) to: + methodZoneBase + closedPICSize + */ + + /* Cogit>>#expectedClosedPICPrototype: */ +static NoDbgRegParms sqInt +expectedClosedPICPrototype(CogMethod *cPIC) +{ + unsigned int classTag; + sqInt classTagPC; + sqInt entryPoint; + sqInt errors; + sqInt i; + sqInt methodObjPC; + sqInt object; + usqInt pc; + + errors = 0; + pc = (((usqInt)cPIC)) + firstCPICCaseOffset; + + /* First jump is unconditional; subsequent ones are conditional */ + object = literalBeforeFollowingAddress(backEnd, pc - (jumpLongByteSize(backEnd))); + if (!(asserta(object == (firstPrototypeMethodOop())))) { + errors = 1; + } + entryPoint = jumpLongTargetBeforeFollowingAddress(backEnd, pc); + if (!(asserta(entryPoint == ((cPICPrototypeCaseOffset()) + 13262352)))) { + errors += 2; + } + for (i = 1; i < MaxCPICCases; i += 1) { + pc += cPICCaseSize; + + /* verify information in case is as expected. */ + methodObjPC = (pc - (jumpLongConditionalByteSize(backEnd))) - (cmpC32RTempByteSize(backEnd)); + object = literalBeforeFollowingAddress(backEnd, methodObjPC); + if (!(asserta(object == ((subsequentPrototypeMethodOop()) + i)))) { + errors = errors | 4; + } + classTagPC = pc - (jumpLongConditionalByteSize(backEnd)); + classTag = literal32BeforeFollowingAddress(backEnd, classTagPC); + if (!(asserta(classTag == (0xBABE1F15U + i)))) { + errors = errors | 8; + } + entryPoint = jumpLongTargetBeforeFollowingAddress(backEnd, pc); + if (!(asserta(entryPoint == (((cPICPrototypeCaseOffset()) + 13262352) + (i * 16))))) { + errors = errors | 16; + } + + /* change case via rewriteCPICCaseAt:tag:objRef:target: */ + rewriteCPICCaseAttagobjReftarget(pc, classTag ^ 0x5A5A5A5A, object ^ 0xA5A5A5A5U, entryPoint ^ 0x55AA50); + object = literalBeforeFollowingAddress(backEnd, methodObjPC); + if (!(asserta(object == (((subsequentPrototypeMethodOop()) + i) ^ 0xA5A5A5A5U)))) { + errors = errors | 32; + } + classTag = literal32BeforeFollowingAddress(backEnd, classTagPC); + if (!(asserta(classTag == ((0xBABE1F15U + i) ^ 0x5A5A5A5A)))) { + errors = errors | 64; + } + entryPoint = jumpLongTargetBeforeFollowingAddress(backEnd, pc); + if (!(asserta(entryPoint == ((((cPICPrototypeCaseOffset()) + 13262352) + (i * 16)) ^ 0x55AA50)))) { + errors = errors | 128; + } + + /* finally restore case to the original state */ + rewriteCPICCaseAttagobjReftarget(pc, classTag ^ 0x5A5A5A5A, object ^ 0xA5A5A5A5U, entryPoint ^ 0x55AA50); + } + entryPoint = jumpLongTargetBeforeFollowingAddress(backEnd, (((usqInt)cPIC)) + cPICEndOfCodeOffset); + if (!(asserta(entryPoint == (cPICMissTrampolineFor(0))))) { + errors += 0x100; + } + return errors; +} + + +/* 224 11100000 aaaaaaaa Extend A (Ext A = Ext A prev * 256 + Ext A) */ + + /* Cogit>>#extABytecode */ +static sqInt +extABytecode(void) +{ + extA = (((((usqInt)(extA) << 8)))) + byte1; + return 0; +} + + +/* 225 11100001 sbbbbbbb Extend B (Ext B = Ext B prev * 256 + Ext B) */ + + /* Cogit>>#extBBytecode */ +static sqInt +extBBytecode(void) +{ + extB = ((numExtB == 0) + && (byte1 > 0x7F) + ? byte1 - 0x100 + : (((((usqInt)(extB) << 8)))) + byte1); + numExtB += 1; + return 0; +} + + +/* Fill in the block headers now we know the exact layout of the code. */ + + /* Cogit>>#fillInBlockHeadersAt: */ +static NoDbgRegParms sqInt +fillInBlockHeadersAt(sqInt startAddress) +{ + sqInt aCogMethodOrInteger; + CogBlockMethod *blockHeader; + BlockStart *blockStart; + sqInt i; + + if (!(needsFrame + && (blockCount > 0))) { + return null; + } + if (blockNoContextSwitchOffset) { + assert(blockNoContextSwitchOffset == (((blockEntryLabel->address)) - ((blockEntryNoContextSwitch->address)))); + } + else { + blockNoContextSwitchOffset = ((blockEntryLabel->address)) - ((blockEntryNoContextSwitch->address)); + } + for (i = 0; i < blockCount; i += 1) { + blockStart = blockStartAt(i); + aCogMethodOrInteger = (((blockStart->fakeHeader))->address); + + /* begin writableBlockMethodFor: */ + blockHeader = ((CogBlockMethod *) ((((usqInt)aCogMethodOrInteger)) + codeToDataDelta)); + (blockHeader->homeOffset = ((((blockStart->fakeHeader))->address)) - startAddress); + (blockHeader->startpc = (blockStart->startpc)); + (blockHeader->cmType = CMBlock); + (blockHeader->cmNumArgs = (blockStart->numArgs)); + (blockHeader->cbUsesInstVars = (blockStart->hasInstVarRef)); + (blockHeader->stackCheckOffset = ((blockStart->stackCheckLabel) + ? ((((blockStart->stackCheckLabel))->address)) - ((((blockStart->fakeHeader))->address)) + : 0)); + } + return 0; +} + + +/* Fill in the header for theCogMethod method. This may be located at the + writable mapping. */ + + /* Cogit>>#fillInMethodHeader:size:selector: */ +static NoDbgRegParms void +fillInMethodHeadersizeselector(CogMethod *method, sqInt size, sqInt selector) +{ + sqInt actualMethodLocation; + unsigned int codeInstruction; + unsigned int dataInstruction; + CogMethod *originalMethod; + sqInt rawHeader; + + actualMethodLocation = (((usqInt)method)) - codeToDataDelta; + (method->cmType = CMMethod); + (method->objectHeader = nullHeaderForMachineCodeMethod()); + (method->blockSize = size); + (method->methodObject = methodObj); + rawHeader = rawHeaderOf(methodObj); + + /* If the method has already been cogged (e.g. Newspeak accessors) then + leave the original method attached to its cog method, but get the right header. */ + if (isCogMethodReference(rawHeader)) { + originalMethod = ((CogMethod *) rawHeader); + assert(((originalMethod->blockSize)) == size); + assert(methodHeader == ((originalMethod->methodHeader))); + } + else { + rawHeaderOfput(methodObj, actualMethodLocation); + } + (method->methodHeader = methodHeader); + (method->selector = selector); + (method->cmNumArgs = argumentCountOfMethodHeader(methodHeader)); + (method->cmHasMovableLiteral = hasMovableLiteral); + if ((method->cmRefersToYoung = hasYoungReferent)) { + addToYoungReferrers(method); + } + (method->cmUsageCount = initialMethodUsageCount()); + + /* cpicHasMNUCase: */ + (method->cpicHasMNUCaseOrCMIsFullBlock) = 0; + (method->cmUsesPenultimateLit = maxLitIndex >= ((literalCountOfMethodHeader(methodHeader)) - 2)); + (method->blockEntryOffset = (blockEntryLabel + ? ((blockEntryLabel->address)) - actualMethodLocation + : 0)); + + /* This can be an error check since a large stackCheckOffset is caused by compiling + a machine-code primitive, and hence depends on the Cogit, not the input method. */ + if (needsFrame) { + if (!((((stackCheckLabel->address)) - actualMethodLocation) <= MaxStackCheckOffset)) { + error("too much code for stack check offset"); + } + } + (method->stackCheckOffset = (needsFrame + ? ((stackCheckLabel->address)) - actualMethodLocation + : 0)); + assert((callTargetFromReturnAddress(backEnd, actualMethodLocation + missOffset)) == (methodAbortTrampolineFor((method->cmNumArgs)))); + assert(size == (roundUpLength(size))); + + /* begin assertValidDualZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + /* begin assertCoherentCodeAt:delta: */ + codeInstruction = long32At((void *)(actualMethodLocation + cmNoCheckEntryOffset)); + dataInstruction = long32At((void *)((actualMethodLocation + cmNoCheckEntryOffset) + codeToDataDelta)); + assert(codeInstruction == dataInstruction); +# endif // DUAL_MAPPED_CODE_ZONE +} + + /* Cogit>>#findBackwardBranch:IsBackwardBranch:Mcpc:Bcpc:MatchingBcpc: */ +static NoDbgRegParms sqInt +findBackwardBranchIsBackwardBranchMcpcBcpcMatchingBcpc(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *targetBcpc) +{ + return ((((isBackwardBranchAndAnnotation & 1) != 0)) + && ((((sqInt)targetBcpc)) == bcpc) + ? ((sqInt)mcpc) + : 0); +} + + /* Cogit>>#findBlockMethodWithEntry:startBcpc: */ +static NoDbgRegParms usqInt +findBlockMethodWithEntrystartBcpc(sqInt blockEntryMcpc, sqInt startBcpc) +{ + CogBlockMethod *cogBlockMethod; + + cogBlockMethod = ((CogBlockMethod *) (blockEntryMcpc - (sizeof(CogBlockMethod)))); + if (((cogBlockMethod->startpc)) == startBcpc) { + return ((usqInt)cogBlockMethod); + } + return 0; +} + + /* Cogit>>#findMapLocationForMcpc:inMethod: */ +static NoDbgRegParms sqInt +findMapLocationForMcpcinMethod(usqInt targetMcpc, CogMethod *cogMethod) +{ + sqInt annotation; + sqInt map; + sqInt mapByte; + usqInt mcpc; + + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + if (mcpc == targetMcpc) { + return map; + } + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + if (annotation != IsAnnotationExtension) { + mcpc += 4 /* codeGranularity */ * ((annotation + ? mapByte & DisplacementMask + : ((sqInt)((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift)))); + } + if (mcpc >= targetMcpc) { + assert(mcpc == targetMcpc); + if (!annotation) { + map -= 1; + mapByte = byteAt((void *)(map)); + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + assert(annotation > IsAnnotationExtension); + } + return map; + } + map -= 1; + } + return 0; +} + + +/* Find the CMMethod or CMBlock that has zero-relative startbcpc as its first + bytecode pc. + As this is for cannot resume processing and/or conversion to machine-code + on backward + branch, it doesn't have to be fast. Enumerate block returns and map to + bytecode pcs. */ + + /* Cogit>>#findMethodForStartBcpc:inHomeMethod: */ +CogBlockMethod * +findMethodForStartBcpcinHomeMethod(sqInt startbcpc, CogMethod *cogMethod) +{ + assert(isCMMethodEtAl(((CogBlockMethod *) cogMethod))); + if (startbcpc == (startPCOfMethodHeader((cogMethod->methodHeader)))) { + return ((CogBlockMethod *) cogMethod); + } + assert(((cogMethod->blockEntryOffset)) != 0); + return ((CogBlockMethod *) (blockDispatchTargetsForperformarg(cogMethod, findBlockMethodWithEntrystartBcpc, startbcpc))); +} + + +/* Machine code addresses map to the following bytecode for all bytecodes + except backward branches, where they map to the backward branch itself. + This is so that loops continue, rather than terminate prematurely. */ + + /* Cogit>>#find:IsBackwardBranch:Mcpc:Bcpc:MatchingMcpc: */ +static NoDbgRegParms sqInt +findIsBackwardBranchMcpcBcpcMatchingMcpc(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *targetMcpc) +{ + return (targetMcpc == mcpc + ? ((!descriptor) + || (((isBackwardBranchAndAnnotation & 1) != 0)) + ? bcpc + : bcpc + ((descriptor->numBytes))) + : 0); +} + + /* Cogit>>#firstMappedPCFor: */ +static NoDbgRegParms usqInt +firstMappedPCFor(CogMethod *cogMethod) +{ + return ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); +} + + +/* Answer a fake value for the first method oop in the PIC prototype. + Since we use MoveUniqueCw:R: it must not be confused with a + method-relative address. */ + + /* Cogit>>#firstPrototypeMethodOop */ +static sqInt +firstPrototypeMethodOop(void) +{ + return (/* addressIsInCurrentCompilation: */ + ((((usqInt)0x5EAF00D)) >= ((methodLabel->address))) + && ((((usqInt)0x5EAF00D)) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize)))) + ? 0xCA7F00D + : 0x5EAF00D); +} + + /* Cogit>>#fixupAt: */ +static NoDbgRegParms BytecodeFixup * +fixupAt(sqInt fixupPC) +{ + return fixupAtIndex(fixupPC - initialPC); +} + + /* Cogit>>#flagCogMethodForBecome: */ +void +flagCogMethodForBecome(CogMethod *cogMethod) +{ + assert(isCMMethodEtAl(((CogBlockMethod *) cogMethod))); + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + ((((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)))->cmType = CMMethodFlaggedForBecome); +} + + /* Cogit>>#followForwardedLiteralsImplementationIn: */ +static NoDbgRegParms void +followForwardedLiteralsImplementationIn(CogMethod *cogMethod) +{ + sqInt annotation; + sqInt hasYoungObj; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + CogMethod *writableCogMethod; + + assert((!(isCMMethodEtAl(((CogBlockMethod *) cogMethod)))) + || (!(isForwarded((cogMethod->methodObject))))); + writableCogMethod = ((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)); + hasYoungObj = isYoung((cogMethod->methodObject)); + if (shouldRemapOop((cogMethod->selector))) { + (writableCogMethod->selector = remapObj((cogMethod->selector))); + if (isYoung((cogMethod->selector))) { + hasYoungObj = 1; + } + } + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = remapIfObjectRefpchasYoung(annotation, ((char *) mcpc), ((void *)((&hasYoungObj)))); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1: + if (hasYoungObj) { + ensureInYoungReferrers(cogMethod); + } + else { + (writableCogMethod->cmRefersToYoung = 0); + } +} + + /* Cogit>>#followForwardedLiteralsIn: */ +void +followForwardedLiteralsIn(CogMethod *cogMethod) +{ + unsigned char wasInYoungReferrers; + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + wasInYoungReferrers = (cogMethod->cmRefersToYoung); + followForwardedLiteralsImplementationIn(cogMethod); + if (wasInYoungReferrers + && (!((cogMethod->cmRefersToYoung)))) { + pruneYoungReferrers(); + } + + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +} + + +/* To avoid runtime checks on literal variable and literal accesses in == and + ~~, + we follow literals in methods having movable literals in the postBecome + action. To avoid scanning every method, we annotate cogMethods with the + cmHasMovableLiteral flag. */ + + /* Cogit>>#followMovableLiteralsAndUpdateYoungReferrers */ +void +followMovableLiteralsAndUpdateYoungReferrers(void) +{ + CogMethod *cogMethod; + sqInt endAddress; + + assert(kosherYoungReferrers()); + + /* methodZone firstBogusYoungReferrer + methodZone occurrencesInYoungReferrers: methodZone firstBogusYoungReferrer */ + codeModified = 0; + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (!((((cogMethod->cmType)) == CMFree) + || (((cogMethod->cmType)) == CMMethodFlaggedForBecome))) { + if ((cogMethod->cmHasMovableLiteral)) { + followForwardedLiteralsImplementationIn(cogMethod); + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + pruneYoungReferrers(); + if (codeModified) { + endAddress = freeStart(); + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) (((usqInt)codeBase))), endAddress - (((usqInt)codeBase))); + sys_icache_invalidate(((void *) (((usqInt)codeBase))), endAddress - (((usqInt)codeBase))); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) (((usqInt)codeBase))), (endAddress - (((usqInt)codeBase))) + 1); +# else // __APPLE__ && __MACH__ || _WIN64 + ceFlushICache(((usqInt)codeBase), endAddress); +# endif + + } + + /* After updating oops in inline caches we need to flush the icache. + And ensure code zone is executable. May have pruned young referrers... */ + + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +} + + +/* N.B. because becomeEffectFlags indicates whether jitted methods were + becommed or not, if this method is called flagged methods exist, will be + freed, and so on. So there is no need to check. Just do it. */ + + /* Cogit>>#freeBecomeFlaggedMethods */ +void +freeBecomeFlaggedMethods(void) +{ + CogMethod *cogMethod; + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) == CMMethodFlaggedForBecome) { + freeMethod(cogMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + unlinkSendsToFree(); + + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +} + + /* Cogit>>#freeCogMethod: */ +void +freeCogMethod(CogMethod *cogMethod) +{ + moveProfileToMethods(); + freeMethod(cogMethod); + + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +} + + +/* Free machine-code methods whose compiled methods are unmarked + and open PICs whose selectors are not marked, and closed PICs that + refer to unmarked objects. */ + + /* Cogit>>#freeUnmarkedMachineCode */ +void +freeUnmarkedMachineCode(void) +{ + CogMethod *cogMethod; + sqInt freedMethod; + + moveProfileToMethods(); + freedMethod = 0; + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if ((((cogMethod->cmType)) >= CMMethod) + && (!(isMarked((cogMethod->methodObject))))) { + freedMethod = 1; + freeMethod(cogMethod); + } + if ((((cogMethod->cmType)) == CMOpenPIC) + && ((!(isImmediate((cogMethod->selector)))) + && (!(isMarked((cogMethod->selector)))))) { + freedMethod = 1; + freeMethod(cogMethod); + } + if ((((cogMethod->cmType)) == CMClosedPIC) + && (closedPICRefersToUnmarkedObject(cogMethod))) { + freedMethod = 1; + freeMethod(cogMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if (freedMethod) { + unlinkSendsToFree(); + } + + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +} + + +/* Call ceSendMustBeBooleanTo: via the relevant trampoline. */ + + /* Cogit>>#genCallMustBeBooleanFor: */ +static NoDbgRegParms AbstractInstruction * +genCallMustBeBooleanFor(sqInt boolean) +{ + AbstractInstruction *abstractInstruction; + sqInt callTarget; + + callTarget = (boolean == (falseObject()) + ? ceSendMustBeBooleanAddFalseTrampoline + : ceSendMustBeBooleanAddTrueTrampoline); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, callTarget); + (abstractInstruction->annotation = IsRelativeCall); + return abstractInstruction; +} + + /* Cogit>>#genConditionalBranch:operand: */ +static NoDbgRegParms AbstractInstruction * +genConditionalBranchoperand(sqInt opcode, sqInt operandOne) +{ + AbstractInstruction *branch; + AbstractInstruction *self_in_CogARMv8Compiler; + + self_in_CogARMv8Compiler = abstractInstructionAt(opcodeIndex - 1); + branch = genoperand(opcode, operandOne); + + /* begin noteFollowingConditionalBranch: */ + if ((((self_in_CogARMv8Compiler->opcode)) == MulOverflowRRR) + && ((((branch->opcode)) == JumpOverflow) + || (((branch->opcode)) == JumpNoOverflow))) { + (branch->opcode = (((branch->opcode)) == JumpOverflow + ? JumpMulOverflow + : JumpNoMulOverflow)); + } + return branch; +} + + +/* An enilopmart (the reverse of a trampoline) is a piece of code that makes + the system-call-like transition from the C runtime into generated machine + code. The desired arguments and entry-point are pushed on a stackPage's + stack. The enilopmart pops off the values to be loaded into registers and + then executes a return instruction to pop off the entry-point and jump to + it. + BEFORE AFTER (stacks grow down) + whatever stackPointer -> whatever + target address => reg1 = reg1val, etc + reg1val pc = target address + reg2val + stackPointer -> reg3val */ + + /* Cogit>>#genEnilopmartFor:and:and:forCall:called: */ +static NoDbgRegParms void +(*genEnilopmartForandandforCallcalled(sqInt regArg1, sqInt regArg2OrNone, sqInt regArg3OrNone, sqInt forCall, char *trampolineName))(void) +{ + AbstractInstruction *anInstruction; + sqInt endAddress; + usqInt enilopmart; + sqInt quickConstant; + sqInt size; + + zeroOpcodeIndex(); + quickConstant = varBaseAddress; + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, quickConstant, VarBaseReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + genLoadStackPointers(backEnd); + if (regArg3OrNone != NoReg) { + /* PopR: */ + genoperand(PopR, regArg3OrNone); + } + if (regArg2OrNone != NoReg) { + /* PopR: */ + genoperand(PopR, regArg2OrNone); + } + + /* PopR: */ + genoperand(PopR, regArg1); + genEnilopmartReturn(forCall); + computeMaximumSizes(); + size = generateInstructionsAt(methodZoneBase); + endAddress = outputInstructionsAt(methodZoneBase); + assert((methodZoneBase + size) == endAddress); + enilopmart = methodZoneBase; + methodZoneBase = alignUptoRoutineBoundary(endAddress); + stopsFromto(backEnd, endAddress, methodZoneBase - 1); + recordGeneratedRunTimeaddress(trampolineName, enilopmart); + return ((void (*)(void)) enilopmart); +} + + +/* An enilopmart (the reverse of a trampoline) is a piece of code that makes + the system-call-like transition from the C runtime into generated machine + code. At the point the enilopmart enters machine code via a return + instruction, any argument registers have been loaded with their values and + the stack, if + for call, looks like + ret pc + stackPointer -> target address + + and if not for call, looks like + whatever + stackPointer -> target address + + If forCall and running on a CISC, ret pc must be left on the stack. If + forCall and + running on a RISC, ret pc must be popped into LinkReg. In either case, + target address must be removed from the stack and jumped/returned to. */ + + /* Cogit>>#genEnilopmartReturn: */ +static NoDbgRegParms void +genEnilopmartReturn(sqInt forCall) +{ + if (forCall) { + /* PopR: */ + genoperand(PopR, RISCTempReg); + + /* PopR: */ + genoperand(PopR, LinkReg); + + /* JumpR: */ + genoperand(JumpR, RISCTempReg); + } + else { + /* PopR: */ + genoperand(PopR, RISCTempReg); + + /* JumpR: */ + genoperand(JumpR, RISCTempReg); + } +} + + +/* Generate the routine that writes the current values of the C frame and + stack pointers into + variables. These are used to establish the C stack in trampolines back + into the C run-time. + This routine assumes the system's frame pointer is the same as that used + in generated code. */ + + /* Cogit>>#generateCaptureCStackPointers: */ +static NoDbgRegParms NeverInline void +generateCaptureCStackPointers(sqInt captureFramePointer) +{ + AbstractInstruction *anInstruction; + sqInt callerSavedReg; + usqIntptr_t fixupSize; + sqInt offset; + usqIntptr_t opcodeSize; + sqInt pushedVarBaseReg; + sqInt quickConstant; + usqInt startAddress; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 32; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + firstOpcodeIndex = 0x10000; + nextLiteralIndex = (lastDumpedLiteralIndex = 0); + labelCounter = 0; + startAddress = methodZoneBase; + + /* Must happen first; value may be used in accessing any of the following addresses */ + callerSavedReg = 0; + pushedVarBaseReg = 0; + if (!(((CallerSavedRegisterMask & ((1U << VarBaseReg))) != 0))) { + /* VarBaseReg is not caller-saved; must save and restore it, either by using an available caller-saved reg or push/pop. */ + + /* TempReg used below */ + callerSavedReg = availableRegisterOrNoneIn(((ABICallerSavedRegisterMask | ((1U << TempReg))) - ((1U << TempReg)))); + if (callerSavedReg == NoReg) { + gNativePushR(VarBaseReg); + pushedVarBaseReg = 1; + } + else { + /* MoveR:R: */ + genoperandoperand(MoveRR, VarBaseReg, callerSavedReg); + } + } + quickConstant = varBaseAddress; + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, quickConstant, VarBaseReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + if (captureFramePointer) { + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(cFramePointerAddress(), genoperandoperand(MoveRAw, FPReg, cFramePointerAddress())); + } + + /* Capture the stack pointer prior to the call. If we've pushed VarBaseReg take that into account. */ + if (pushedVarBaseReg) { + offset = (pushedVarBaseReg + ? 0 /* leafCallStackPointerDelta */ + BytesPerWord + : 0 /* leafCallStackPointerDelta */); + + /* begin LoadEffectiveAddressMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(LoadEffectiveAddressMwrR, offset, NativeSPReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(cStackPointerAddress(), genoperandoperand(MoveRAw, TempReg, cStackPointerAddress())); + } + else { + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(cStackPointerAddress(), genoperandoperand(MoveRAw, NativeSPReg, cStackPointerAddress())); + } + if (!(((CallerSavedRegisterMask & ((1U << VarBaseReg))) != 0))) { + if (pushedVarBaseReg) { + gNativePopR(VarBaseReg); + } + else { + /* MoveR:R: */ + genoperandoperand(MoveRR, callerSavedReg, VarBaseReg); + } + } + gNativeRetN(0); + outputInstructionsForGeneratedRuntimeAt(startAddress); + + /* This also implicitly flushes the read/write mapped dual zone to the read/execute zone. */ + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) (((usqInt)startAddress))), (((usqInt)methodZoneBase)) - (((usqInt)startAddress))); + sys_icache_invalidate(((void *) (((usqInt)startAddress))), (((usqInt)methodZoneBase)) - (((usqInt)startAddress))); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) (((usqInt)startAddress))), ((((usqInt)methodZoneBase)) - (((usqInt)startAddress))) + 1); +# else // __APPLE__ && __MACH__ || _WIN64 + ceFlushICache(((usqInt)startAddress), ((usqInt)methodZoneBase)); +# endif + + recordGeneratedRunTimeaddress("ceCaptureCStackPointers", startAddress); + ceCaptureCStackPointers = ((void (*)(void)) startAddress); +} + + +/* Generate the prototype ClosedPIC to determine how much space a full closed + PIC takes. + When we first allocate a closed PIC it only has one or two cases and we + want to grow it. + So we have to determine how big a full one is before hand. */ +/* stack allocate the various collections so that they + are effectively garbage collected on return. */ + + /* Cogit>>#generateClosedPICPrototype */ +static void +generateClosedPICPrototype(void) +{ + AbstractInstruction *anInstruction; + CogMethod *cPIC; + AbstractInstruction * cPICEndOfCodeLabel; + sqInt endAddress; + AbstractInstruction * endCPICCase1; + usqIntptr_t fixupSize; + sqInt h; + AbstractInstruction *jumpNext; + sqInt jumpTarget; + sqInt numArgs; + usqIntptr_t opcodeSize; + sqInt wordConstant; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = MaxCPICCases * 9; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + firstOpcodeIndex = 0x10000; + nextLiteralIndex = (lastDumpedLiteralIndex = 0); + labelCounter = 0; + (methodLabel->address = methodZoneBase); + (methodLabel->dependent = null); + + /* begin compileClosedPICPrototype */ + compilePICAbort((numArgs = 0)); + jumpNext = compileCPICEntry(); + + /* At the end of the entry code we need to jump to the first case code, which is actually the last chunk. + On each extension we must update this jump to move back one case. */ + wordConstant = firstPrototypeMethodOop(); + + /* begin MoveUniqueCw:R: */ + /* begin gen:uniqueLiteral:operand: */ + anInstruction = genoperandoperand(MoveCwR, wordConstant, SendNumArgsReg); + assert(usesOutOfLineLiteral(anInstruction)); + (anInstruction->dependent = allocateLiteral(wordConstant)); + jumpTarget = (((methodZoneBase + (youngReferrers)) / 2) - 13262352) + 13262352; + + /* begin JumpLong: */ + genoperand(JumpLong, jumpTarget); + endCPICCase0 = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + for (h = 1; h < MaxCPICCases; h += 1) { + if (h == (MaxCPICCases - 1)) { + jmpTarget(jumpNext, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } + wordConstant = (subsequentPrototypeMethodOop()) + h; + + /* begin MoveUniqueCw:R: */ + /* begin gen:uniqueLiteral:operand: */ + anInstruction = genoperandoperand(MoveCwR, wordConstant, SendNumArgsReg); + assert(usesOutOfLineLiteral(anInstruction)); + (anInstruction->dependent = allocateLiteral(wordConstant)); + + /* 16rBABE1F15+h is the class tag for the Nth case */ + + /* #CmpC32:R: #gen:literal32:operand: */ + checkLiteral32forInstruction(0xBABE1F15U + h, genoperandoperand(CmpC32R, 0xBABE1F15U + h, TempReg)); + jumpTarget = ((((methodZoneBase + (youngReferrers)) / 2) - 13262352) + 13262352) + (h * 16); + + /* begin JumpLongZero: */ + genConditionalBranchoperand(JumpLongZero, ((sqInt)jumpTarget)); + if (h == 1) { + endCPICCase1 = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + } + } + + /* #MoveCw:R: #gen:literal:operand: */ + checkLiteralforInstruction((methodLabel->address), genoperandoperand(MoveCwR, (methodLabel->address), ClassReg)); + jumpTarget = cPICMissTrampolineFor(numArgs); + + /* begin JumpLong: */ + genoperand(JumpLong, jumpTarget); + cPICEndOfCodeLabel = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + dumpLiterals(0); + computeMaximumSizes(); + cPIC = ((CogMethod *) methodZoneBase); + closedPICSize = (sizeof(CogMethod)) + (generateInstructionsAt(methodZoneBase + (sizeof(CogMethod)))); + endAddress = outputInstructionsAt(methodZoneBase + (sizeof(CogMethod))); + assert((methodZoneBase + closedPICSize) == endAddress); + firstCPICCaseOffset = ((endCPICCase0->address)) - methodZoneBase; + cPICEndOfCodeOffset = ((cPICEndOfCodeLabel->address)) - methodZoneBase; + cPICCaseSize = ((endCPICCase1->address)) - ((endCPICCase0->address)); + cPICEndSize = closedPICSize - (((MaxCPICCases - 1) * cPICCaseSize) + firstCPICCaseOffset); + closedPICSize = roundUpToMethodAlignment(backEnd, closedPICSize); + assert(((picInterpretAbort->address)) == (((methodLabel->address)) + (picInterpretAbortOffset()))); + assert((expectedClosedPICPrototype(cPIC)) == 0); + + /* tpr this is a little tiresome but after any assert checking we need to 0 out the case0 objRef rather than leaving 16r5EAF00D lying around */ + storeLiteralbeforeFollowingAddress(backEnd, 0, ((endCPICCase0->address)) - (jumpLongByteSize(backEnd))); + + /* finish up */ + recordGeneratedRunTimeaddress("cPICPrototype", methodZoneBase); + + /* update the methodZoneBase so we keep the prototype around for later use; it is copied into new PICs */ + methodZoneBase = alignUptoRoutineBoundary(endAddress); + + /* self disassembleFrom: cPICPrototype + (self sizeof: CogMethod) to: cPICPrototype + closedPICSize - 1 */ + cPICPrototype = cPIC; +} + + +/* We handle jump sizing simply. First we make a pass that asks each + instruction to compute its maximum size. Then we make a pass that + sizes jumps based on the maxmimum sizes. Then we make a pass + that fixes up jumps. When fixing up a jump the jump is not allowed to + choose a smaller offset but must stick to the size set in the second pass. */ + + /* Cogit>>#generateCogFullBlock */ +static CogMethod * +generateCogFullBlock(void) +{ + sqInt codeSize; + usqIntptr_t headerSize; + sqInt mapSize; + CogMethod *method; + sqInt result; + usqInt startAddress; + int totalSize; + + headerSize = sizeof(CogMethod); + (methodLabel->address = freeStart()); + computeMaximumSizes(); + concretizeAt(methodLabel, freeStart()); + codeSize = generateInstructionsAt(((methodLabel->address)) + headerSize); + mapSize = generateMapAtstart(null, ((methodLabel->address)) + cbNoSwitchEntryOffset); + totalSize = roundUpToMethodAlignment(backEnd, (headerSize + codeSize) + mapSize); + if (totalSize > MaxMethodSize) { + return ((CogMethod *) MethodTooBig); + } + startAddress = allocate(totalSize); + if (!startAddress) { + return ((CogMethod *) InsufficientCodeSpace); + } + assert((startAddress + cbEntryOffset) == ((fullBlockEntry->address))); + assert((startAddress + cbNoSwitchEntryOffset) == ((fullBlockNoContextSwitchEntry->address))); + result = outputInstructionsAt(startAddress + headerSize); + assert(((startAddress + headerSize) + codeSize) == result); + padIfPossibleWithStopsFromto(backEnd, result, ((startAddress + totalSize) - mapSize) - 1); + generateMapAtstart((startAddress + totalSize) - 1, startAddress + cbNoSwitchEntryOffset); + method = ((CogMethod *) ((((usqInt)startAddress)) + codeToDataDelta)); + fillInMethodHeadersizeselector(method, totalSize, nilObject()); + (method->cpicHasMNUCaseOrCMIsFullBlock = 1); + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) startAddress), (startAddress + totalSize) - startAddress); + sys_icache_invalidate(((void *) startAddress), (startAddress + totalSize) - startAddress); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) startAddress), ((startAddress + totalSize) - startAddress) + 1); +# else + ceFlushICache(startAddress, startAddress + totalSize); +# endif + + return ((CogMethod *) startAddress); +} + + +/* We handle jump sizing simply. First we make a pass that asks each + instruction to compute its maximum size. Then we make a pass that + sizes jumps based on the maxmimum sizes. Then we make a pass + that fixes up jumps. When fixing up a jump the jump is not allowed to + choose a smaller offset but must stick to the size set in the second pass. */ + + /* Cogit>>#generateCogMethod: */ +static NoDbgRegParms CogMethod * +generateCogMethod(sqInt selector) +{ + sqInt codeSize; + usqIntptr_t headerSize; + sqInt mapSize; + sqInt result; + usqInt startAddress; + int totalSize; + + headerSize = sizeof(CogMethod); + (methodLabel->address = freeStart()); + computeMaximumSizes(); + concretizeAt(methodLabel, freeStart()); + codeSize = generateInstructionsAt(((methodLabel->address)) + headerSize); + mapSize = generateMapAtstart(null, ((methodLabel->address)) + cmNoCheckEntryOffset); + totalSize = roundUpToMethodAlignment(backEnd, (headerSize + codeSize) + mapSize); + if (totalSize > MaxMethodSize) { + return ((CogMethod *) MethodTooBig); + } + startAddress = allocate(totalSize); + if (!startAddress) { + return ((CogMethod *) InsufficientCodeSpace); + } + assert((startAddress + cmEntryOffset) == ((entry->address))); + assert((startAddress + cmNoCheckEntryOffset) == ((noCheckEntry->address))); + result = outputInstructionsAt(startAddress + headerSize); + assert(((startAddress + headerSize) + codeSize) == result); + padIfPossibleWithStopsFromto(backEnd, result, ((startAddress + totalSize) - mapSize) - 1); + generateMapAtstart((startAddress + totalSize) - 1, startAddress + cmNoCheckEntryOffset); + fillInBlockHeadersAt(startAddress); + fillInMethodHeadersizeselector(((CogMethod *) ((((usqInt)startAddress)) + codeToDataDelta)), totalSize, selector); + + /* This also implicitly flushes the read/write mapped dual zone to the read/execute zone. */ + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) startAddress), (startAddress + totalSize) - startAddress); + sys_icache_invalidate(((void *) startAddress), (startAddress + totalSize) - startAddress); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) startAddress), ((startAddress + totalSize) - startAddress) + 1); +# else + ceFlushICache(startAddress, startAddress + totalSize); +# endif + + return ((CogMethod *) startAddress); +} + + +/* Generate the method map at addressrNull (or compute it if addressOrNull is + null). Answer the length of the map in byes. Each entry in the map is in + two parts. In the + least signficant bits are a displacement of how far from the start or + previous entry, + unless it is an IsAnnotationExtension byte, in which case those bits are + the extension. + In the most signficant bits are the type of annotation at the point + reached. A null + byte ends the map. */ + + /* Cogit>>#generateMapAt:start: */ +static NoDbgRegParms sqInt +generateMapAtstart(usqInt addressOrNull, usqInt startAddress) +{ + unsigned char annotation; + sqInt delta; + sqInt i; + AbstractInstruction *instruction; + sqInt length; + usqInt location; + sqInt mapEntry; + sqInt maxDelta; + usqInt mcpc; + + delta = 0; + length = 0; + location = startAddress; + for (i = 0; i < opcodeIndex; i += 1) { + instruction = abstractInstructionAt(i); + if ((annotation = (instruction->annotation))) { + /* begin assertValidAnnotation:for: */ + assert((annotation != (getIsObjectReference())) + || (((instruction->opcode)) == Literal)); + mcpc = /* mapEntryAddress */ + (((instruction->opcode)) == Literal + ? (instruction->address) + : ((instruction->address)) + ((instruction->machineCodeSize))); + while (((delta = (mcpc - location) / 4 /* codeGranularity */)) > DisplacementMask) { + maxDelta = (((((delta < MaxX2NDisplacement) ? delta : MaxX2NDisplacement)) | DisplacementMask) - DisplacementMask); + assert((((usqInt)(maxDelta)) >> AnnotationShift) <= DisplacementMask); + if (addressOrNull) { + /* begin addToMap:instruction:byte:at:for: */ + codeByteAtput((void *)(addressOrNull - length),(((usqInt)(maxDelta)) >> AnnotationShift) + DisplacementX2N); + } + location += maxDelta * 4 /* codeGranularity */; + length += 1; + } + if (addressOrNull) { + mapEntry = delta + ((((usqInt)((((annotation < IsSendCall) ? annotation : IsSendCall))) << AnnotationShift))); + + /* begin addToMap:instruction:byte:at:for: */ + codeByteAtput((void *)(addressOrNull - length),mapEntry); + } + location += delta * 4 /* codeGranularity */; + length += 1; + if (annotation > IsSendCall) { + if (addressOrNull) { + mapEntry = ((((usqInt)(IsAnnotationExtension) << AnnotationShift))) + (annotation - IsSendCall); + + /* begin addToMap:instruction:byte:at:for: */ + codeByteAtput((void *)(addressOrNull - length),mapEntry); + } + length += 1; + } + } + } + if (addressOrNull) { + /* begin addToMap:instruction:byte:at:for: */ + codeByteAtput((void *)(addressOrNull - length),MapEnd); + } + return length + 1; +} + + +/* Generate the prototype OpenPIC to determine how much space an open PIC + takes. + */ +/* stack allocate the various collections so that they + are effectively garbage collected on return. */ + + /* Cogit>>#generateOpenPICPrototype */ +static void +generateOpenPICPrototype(void) +{ + sqInt codeSize; + usqIntptr_t fixupSize; + sqInt mapSize; + usqIntptr_t opcodeSize; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 100; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + firstOpcodeIndex = 0x10000; + nextLiteralIndex = (lastDumpedLiteralIndex = 0); + labelCounter = 0; + (methodLabel->address = methodZoneBase); + (methodLabel->dependent = null); + + /* Need a real selector here so that the map accomodates the annotations for the selector. + Use self numRegArgs to generate the longest possible code sequence due to + genPushRegisterArgsForNumArgs: */ + compileOpenPICnumArgs(specialSelector(0), numRegArgs()); + computeMaximumSizes(); + concretizeAt(methodLabel, methodZoneBase); + codeSize = generateInstructionsAt(methodZoneBase + (sizeof(CogMethod))); + mapSize = generateMapAtstart(null, methodZoneBase + cmNoCheckEntryOffset); + + /* self cCode: '' + inSmalltalk: + [| end | + end := self outputInstructionsAt: methodZoneBase + headerSize. + self disassembleFrom: methodZoneBase + (self sizeof: CogMethod) to: end - 1. + self halt] */ + openPICSize = (roundUpLength((sizeof(CogMethod)) + codeSize)) + (roundUpToMethodAlignment(backEnd, mapSize)); +} + + +/* Generate a routine ceCaptureCStackPointers that will capture the C stack + pointer, and, if it is in use, the C frame pointer. These are used in + trampolines to call + run-time routines in the interpreter from machine-code. */ + + /* Cogit>>#generateStackPointerCapture */ +static void +generateStackPointerCapture(void) +{ + usqInt oldMethodZoneBase; + sqInt oldTrampolineTableIndex; + + +# if defined(cFramePointerInUse) + assertCStackWellAligned(); + generateCaptureCStackPointers(cFramePointerInUse); +# else + /* For the benefit of the following assert, assume the minimum at first. */ + cFramePointerInUse = 0; + assertCStackWellAligned(); + oldMethodZoneBase = methodZoneBase; + oldTrampolineTableIndex = trampolineTableIndex; + generateCaptureCStackPointers(1); + ceCaptureCStackPointers(); + if (!((cFramePointerInUse = checkIfCFramePointerInUse()))) { + methodZoneBase = oldMethodZoneBase; + trampolineTableIndex = oldTrampolineTableIndex; + generateCaptureCStackPointers(0); + } +# endif // defined(cFramePointerInUse) + + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + assertCStackWellAligned(); +} + + +/* Generate the run-time entries and exits at the base of the native code + zone and update the base. + Read the class-side method trampolines for documentation on the various + trampolines + */ + + /* Cogit>>#generateTrampolines */ +static void +generateTrampolines(void) +{ + usqIntptr_t fixupSize; + usqInt methodZoneStart; + usqIntptr_t opcodeSize; + + methodZoneStart = methodZoneBase; + (methodLabel->address = methodZoneStart); + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 80; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + firstOpcodeIndex = 0x10000; + nextLiteralIndex = (lastDumpedLiteralIndex = 0); + labelCounter = 0; + setHasYoungReferent(0); + maybeGenerateSelectorIndexDereferenceRoutine(); + generateSendTrampolines(); + generateMissAbortTrampolines(); + generateObjectRepresentationTrampolines(); + generateRunTimeTrampolines(); + generateEnilopmarts(); + generateTracingTrampolines(); +} + + /* Cogit>>#generatorForPC: */ +static NoDbgRegParms BytecodeDescriptor * +generatorForPC(sqInt pc) +{ + return generatorAt(bytecodeSetOffset + (fetchByteofObject(pc, methodObj))); +} + + +/* Generate a pair of routines that answer the frame pointer, and the stack + pointer immediately + after a leaf call, used for checking stack pointer alignment, frame + pointer usage, etc. N.B. + these are exported to the CoInterpreter et al via Cogit + class>>mustBeGlobal:. + */ + + /* Cogit>>#genGetLeafCallStackPointers */ +static void +genGetLeafCallStackPointers(void) +{ + usqIntptr_t fixupSize; + usqIntptr_t opcodeSize; + usqInt startAddress; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 4; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + firstOpcodeIndex = 0x10000; + nextLiteralIndex = (lastDumpedLiteralIndex = 0); + labelCounter = 0; + startAddress = methodZoneBase; + + /* MoveR:R: */ + genoperandoperand(MoveRR, FPReg, ABIResultReg); + + /* RetN: */ + genoperand(RetN, 0); + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress("ceGetFP", startAddress); + ceGetFP = ((usqIntptr_t (*)(void)) startAddress); + startAddress = methodZoneBase; + zeroOpcodeIndex(); + + /* MoveR:R: */ + genoperandoperand(MoveRR, NativeSPReg, ABIResultReg); + + /* RetN: */ + genoperand(RetN, 0); + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress("ceGetSP", startAddress); + ceGetSP = ((usqIntptr_t (*)(void)) startAddress); +} + + +/* Generate the abort for a PIC. This abort performs either a call of + ceInterpretMethodFromPIC:receiver: to handle invoking an uncogged target + or a call of ceMNUFromPICMNUMethod:receiver: to handle an MNU dispatch + in a closed PIC. It distinguishes the two by testing ClassReg. If the + register is zero then this is an MNU. + + This poses a problem in 32-bit Spur, where zero is the cache tag for + immediate characters (tag pattern 2r10) because SmallIntegers have tag + patterns 2r11 + and 2r01, so anding with 1 reduces these to 0 & 1. We solve the ambiguity + by patching send sites with a 0 cache tag to open PICs instead of closed + PICs. */ + + /* Cogit>>#genInnerPICAbortTrampoline: */ +static NoDbgRegParms usqInt +genInnerPICAbortTrampoline(char *name) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpMNUCase; + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0 /* picAbortDiscriminatorValue */, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0 /* picAbortDiscriminatorValue */, BytesPerOop)); + } + jumpMNUCase = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + compileTrampolineFornumArgsargargargargregsToSavepushLinkRegresultReg(ceInterpretMethodFromPICreceiver, 2, SendNumArgsReg, ReceiverResultReg, null, null, 0 /* emptyRegisterMask */, 0, NoReg); + jmpTarget(jumpMNUCase, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceMNUFromPICMNUMethodreceiver, name, 2, SendNumArgsReg, ReceiverResultReg, null, null, 0 /* emptyRegisterMask */, 0, NoReg, 1); +} + + +/* Switch to the C stack (do *not* save the Smalltalk stack pointers; + this is the caller's responsibility), and invoke interpret PDQ. */ + + /* Cogit>>#genInvokeInterpretTrampoline */ +static void +(*genInvokeInterpretTrampoline(void))(void) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + usqInt startAddress; + + startAddress = methodZoneBase; + zeroOpcodeIndex(); + quickConstant = varBaseAddress; + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, quickConstant, VarBaseReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + if (cFramePointerInUse) { + genLoadCStackPointers(backEnd); + } + else { + genLoadCStackPointer(backEnd); + } + + /* Sideways call interpret so that the stack looks correct, for exception handling etc */ + genMarshallNArgsargargargarg(backEnd, 0, null, null, null, null); + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(cReturnAddressAddress(), genoperandoperand(MoveAwR, cReturnAddressAddress(), LinkReg)); + + /* #JumpFullRT: #JumpFull: #gen:literal: */ + checkLiteralforInstruction(((sqInt)(((usqInt)interpret))), genoperand(JumpFull, ((sqInt)(((usqInt)interpret))))); + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress("ceInvokeInterpret", startAddress); + return ((void (*)(void)) startAddress); +} + + +/* The in-line cache for a send is implemented as a constant load into + ClassReg. We always use a 32-bit load, even in 64-bits. + + In the initial (unlinked) state the in-line cache is notionally loaded + with the selector. + But since in 64-bits an arbitrary selector oop won't fit in a 32-bit + constant load, we + instead load the cache with the selector's index, either into the literal + frame of the + current method, or into the special selector array. Negative values are + 1-relative indices into the special selector array. + + When a send is linked, the load of the selector, or selector index, is + overwritten with a + load of the receiver's class, or class tag. Hence, the 64-bit VM is + currently constrained + to use class indices as cache tags. If out-of-line literals are used, + distinct caches /must + not/ share acche locations, for if they do, send cacheing will be confused + by the sharing. + Hence we use the MoveUniqueC32:R: instruction that will not share literal + locations. */ + + /* Cogit>>#genLoadInlineCacheWithSelector: */ +static NoDbgRegParms void +genLoadInlineCacheWithSelector(sqInt selectorIndex) +{ + AbstractInstruction *anInstruction; + sqInt cacheValue; + AbstractInstruction *existingInst; + sqInt i; + sqInt initialNumLiterals; + AbstractInstruction *literalInstruction; + AbstractInstruction *newInst; + AbstractInstruction *newLiterals; + + assert((selectorIndex < 0 + ? (((-selectorIndex) >= 1) && ((-selectorIndex) <= (numSpecialSelectors()))) + : ((selectorIndex >= 0) && (selectorIndex <= ((literalCountOf(methodObj)) - 1))))); + cacheValue = selectorIndex; + + /* begin MoveUniqueC32:R: */ + /* begin gen:uniqueLiteral32:operand: */ + anInstruction = genoperandoperand(MoveC32R, cacheValue, ClassReg); + assert(usesOutOfLineLiteral(anInstruction)); + if (nextLiteralIndex >= literalsSize) { + initialNumLiterals = literalsSize + 8; + + /* begin allocateLiterals: */ + if (initialNumLiterals > literalsSize) { + newLiterals = calloc(initialNumLiterals, sizeof(CogAbstractInstruction)); + + /* Must copy across state (not using realloc, cuz...) and + must also update existing instructions to refer to the new ones... + It's either this or modify all generation routines to be able to retry + with more literals after running out of literals. */ + if (literals) { + for (i = 0; i < nextLiteralIndex; i += 1) { + existingInst = literalInstructionAt(i); + newInst = (&(newLiterals[i])); + cloneLiteralFrom(newInst, existingInst); + assert(!((existingInst->dependent))); + (existingInst->dependent = newInst); + } + for (i = 0; i < opcodeIndex; i += 1) { + existingInst = abstractInstructionAt(i); + if ((((existingInst->dependent))) + && (((((existingInst->dependent))->opcode)) == Literal)) { + (existingInst->dependent = (((existingInst->dependent))->dependent)); + } + } + } + free(literals); + literals = newLiterals; + literalsSize = initialNumLiterals; + } + } + literalInstruction = literalInstructionAt(nextLiteralIndex); + initializeUniqueLiteral(literalInstruction, cacheValue); + nextLiteralIndex += 1; + + /* Record the opcodeIndex of the first dependent instruction (the first instruction that references an out-of-line literal) */ + if (firstOpcodeIndex > opcodeIndex) { + firstOpcodeIndex = opcodeIndex - 1; + } + setLiteralSize(literalInstruction, 4); + (anInstruction->dependent = literalInstruction); +} + + /* Cogit>>#genReturnToInterpreterTrampoline */ +static usqInt +genReturnToInterpreterTrampoline(void) +{ + AbstractInstruction *anInstruction; + usqInt startAddress; + + startAddress = methodZoneBase; + zeroOpcodeIndex(); + + /* Push the result, set the instruction pointer to the interpreter frame's saved ip, + set the method and the bytecode set offset, then call interpret. */ + + /* PushR: */ + genoperand(PushR, ReceiverResultReg); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, FoxIFSavedIP, FPReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(FoxIFSavedIP, BytesPerOop)); + } + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(instructionPointerAddress(), genoperandoperand(MoveRAw, TempReg, instructionPointerAddress())); + genSmalltalkToCStackSwitch(0); + + /* pushLinkReg + Sideways call interpret so that the stack looks correct, for exception handling etc */ + genMarshallNArgsargargargarg(backEnd, 0, null, null, null, null); + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(cReturnAddressAddress(), genoperandoperand(MoveAwR, cReturnAddressAddress(), LinkReg)); + + /* #JumpFullRT: #JumpFull: #gen:literal: */ + checkLiteralforInstruction(((sqInt)(((usqInt)interpret))), genoperand(JumpFull, ((sqInt)(((usqInt)interpret))))); + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress("ceReturnToInterpreterTrampoline", startAddress); + return startAddress; +} + + +/* If the client requires, then on an ARM-like RISC processor, the return + address needs to + be pushed to the stack so that the interpreter sees the same stack layout + as on CISC. + */ + + /* Cogit>>#genSmalltalkToCStackSwitch: */ +static NoDbgRegParms sqInt +genSmalltalkToCStackSwitch(sqInt pushLinkReg) +{ + if (pushLinkReg) { + /* PushR: */ + genoperand(PushR, LinkReg); + } + genSaveStackPointers(backEnd); + if (cFramePointerInUse) { + genLoadCStackPointers(backEnd); + } + else { + genLoadCStackPointer(backEnd); + } + return 0; +} + + +/* Generate a trampoline with up to four arguments. Generate either a call or + a jump to aRoutineOrNil + as requested by callJumpBar. If generating a call and resultRegOrNone is + not NoReg pass the C result + back in resultRegOrNone. + Hack: a negative value indicates an abstract register, a non-negative + value indicates a constant. */ + + /* Cogit>>#genTrampolineFor:called:numArgs:arg:arg:arg:arg:regsToSave:pushLinkReg:resultReg:appendOpcodes: */ +static NoDbgRegParms usqInt +genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(void *aRoutine, char *trampolineName, sqInt numArgs, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3, sqInt regMask, sqInt pushLinkReg, sqInt resultRegOrNone, sqInt appendBoolean) +{ + unsigned int codeInstruction; + unsigned int dataInstruction; + usqInt startAddress; + + startAddress = methodZoneBase; + if (!appendBoolean) { + zeroOpcodeIndex(); + } + compileTrampolineFornumArgsargargargargregsToSavepushLinkRegresultReg(aRoutine, numArgs, regOrConst0, regOrConst1, regOrConst2, regOrConst3, regMask, pushLinkReg, resultRegOrNone); + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress(trampolineName, startAddress); + recordRunTimeObjectReferences(); + + /* begin assertValidDualZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + /* begin assertCoherentCodeAt:delta: */ + codeInstruction = long32At((void *)(codeBase + cmNoCheckEntryOffset)); + dataInstruction = long32At((void *)((codeBase + cmNoCheckEntryOffset) + codeToDataDelta)); + assert(codeInstruction == dataInstruction); +# endif // DUAL_MAPPED_CODE_ZONE + + return startAddress; +} + + +/* To return from a trampoline call we have to take the return address off + the stack, + iof it has been saved */ + + /* Cogit>>#genTrampolineReturn: */ +static NoDbgRegParms void +genTrampolineReturn(sqInt lnkRegWasPushed) +{ + if (lnkRegWasPushed) { + /* PopR: */ + genoperand(PopR, LinkReg); + + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, 0); + } +} + + +/* */ + + /* Cogit>>#gen: */ +static NoDbgRegParms AbstractInstruction * +gen(sqInt opcode) +{ + AbstractInstruction *abstractInstruction; + + assert(opcodeIndex < numAbstractOpcodes); + abstractInstruction = abstractInstructionAt(opcodeIndex); + opcodeIndex += 1; + (abstractInstruction->opcode = opcode); + return abstractInstruction; +} + + +/* */ +/* */ + + /* Cogit>>#gen:operand: */ +static NoDbgRegParms AbstractInstruction * +genoperand(sqInt opcode, sqInt operand) +{ + AbstractInstruction *abstractInstruction; + + assert(opcodeIndex < numAbstractOpcodes); + abstractInstruction = abstractInstructionAt(opcodeIndex); + opcodeIndex += 1; + (abstractInstruction->opcode = opcode); + ((abstractInstruction->operands))[0] = operand; + return abstractInstruction; +} + + +/* */ +/* */ +/* */ + + /* Cogit>>#gen:operand:operand: */ +static NoDbgRegParms AbstractInstruction * +genoperandoperand(sqInt opcode, sqInt operandOne, sqInt operandTwo) +{ + AbstractInstruction *abstractInstruction; + + assert(opcodeIndex < numAbstractOpcodes); + abstractInstruction = abstractInstructionAt(opcodeIndex); + opcodeIndex += 1; + (abstractInstruction->opcode = opcode); + ((abstractInstruction->operands))[0] = operandOne; + ((abstractInstruction->operands))[1] = operandTwo; + return abstractInstruction; +} + + +/* */ +/* */ +/* */ +/* */ + + /* Cogit>>#gen:operand:operand:operand: */ +static NoDbgRegParms AbstractInstruction * +genoperandoperandoperand(sqInt opcode, sqInt operandOne, sqInt operandTwo, sqInt operandThree) +{ + AbstractInstruction *abstractInstruction; + + assert(opcodeIndex < numAbstractOpcodes); + abstractInstruction = abstractInstructionAt(opcodeIndex); + opcodeIndex += 1; + (abstractInstruction->opcode = opcode); + ((abstractInstruction->operands))[0] = operandOne; + ((abstractInstruction->operands))[1] = operandTwo; + ((abstractInstruction->operands))[2] = operandThree; + return abstractInstruction; +} + + +/* Answer the contents of the code zone as an array of pair-wise element, + address in ascending address order. + Answer a string for a runtime routine or abstract label (beginning, end, + etc), a CompiledMethod for a CMMethod, + or a selector (presumably a Symbol) for a PIC. + If withDetails is true + - answer machine-code to bytecode pc mapping information for methods + - answer class, target pair information for closed PIC + N.B. Since the class tag for the first case of a closed PIC is stored at + the send site, it must be collected + by scanning methods (see + collectCogConstituentFor:Annotation:Mcpc:Bcpc:Method:). Since closed PICs + are never shared they always come after the method that references them, + so we don't need an extra pass + to collect the first case class tags, which are (temporarily) assigned to + each closed PIC's methodObject field. + But we do need to reset the methodObject fields to zero. This is done in + createPICData:, unless memory + runs out, in which case it is done by cleanUpFailingCogCodeConstituents:. */ + + /* Cogit>>#getCogCodeConstituents: */ +static NoDbgRegParms sqInt +getCogCodeConstituents(sqInt withDetails) +{ + CogMethod *cogMethod; + sqInt constituents; + sqInt count; + sqInt dataClass; + sqInt i; + sqInt label; + sqInt profileData; + sqInt value; + + dataClass = 0; + + /* + 3 for start, freeStart and end */ + count = (trampolineTableIndex / 2) + 3; + cogMethod = ((CogMethod *) methodZoneBase); + + /* we /could/ use methodZone's methodCount, but as of 5/22/2025 it isn't decremented + in freeMethod:, so keep things simple and count exactly how many methods exist now. */ + while (cogMethod < (limitZony())) { + if (!(((cogMethod->cmType)) == CMFree)) { + count += 1; + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + constituents = instantiateClassindexableSize(classArray(), count * 2); + if (!constituents) { + return constituents; + } + pushRemappableOop(constituents); + if ((!((label = stringForCString("CogCode")))) + || (!((value = positive64BitIntegerFor(codeBase))))) { + popRemappableOop(); + return null; + } + storePointerUncheckedofObjectwithValue(0, topRemappableOop(), label); + storePointerUncheckedofObjectwithValue(1, topRemappableOop(), value); + for (i = 0; i < trampolineTableIndex; i += 2) { + if ((!((label = stringForCString(trampolineAddresses[i])))) + || (!((value = positive64BitIntegerFor(((usqInt)(trampolineAddresses[i + 1]))))))) { + popRemappableOop(); + return null; + } + storePointerUncheckedofObjectwithValue(2 + i, topRemappableOop(), label); + storePointerUncheckedofObjectwithValue(3 + i, topRemappableOop(), value); + } + count = trampolineTableIndex + 2; + if (withDetails) { + dataClass = classDoubleWordArray(); + if (dataClass == (nilObject())) { + dataClass = null; + } + } + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (!(((cogMethod->cmType)) == CMFree)) { + profileData = /* profileDataFor:withDetails: */ + (((cogMethod->cmType)) >= CMMethod + ? (cogMethod->methodObject) + : (withDetails + && (((cogMethod->cmType)) == CMClosedPIC) + ? createCPICData(cogMethod) + : (cogMethod->selector))); + if (!profileData) { + return cleanUpFailingCogCodeConstituents(cogMethod); + } + storePointerUncheckedofObjectwithValue(count, topRemappableOop(), profileData); + value = (withDetails + ? (dataClass + ? collectCogMethodConstituentOnSpurofClass(cogMethod, dataClass) + : collectCogMethodConstituent(cogMethod)) + : positive64BitIntegerFor(((usqInt)cogMethod))); + if (!value) { + return cleanUpFailingCogCodeConstituents(cogMethod); + } + storePointerUncheckedofObjectwithValue(count + 1, topRemappableOop(), value); + count += 2; + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if ((!((label = stringForCString("CCFree")))) + || (!((value = positive64BitIntegerFor(mzFreeStart))))) { + popRemappableOop(); + return null; + } + storePointerUncheckedofObjectwithValue(count, topRemappableOop(), label); + storePointerUncheckedofObjectwithValue(count + 1, topRemappableOop(), value); + if ((!((label = stringForCString("CCEnd")))) + || (!((value = positive64BitIntegerFor(limitAddress))))) { + popRemappableOop(); + return null; + } + storePointerUncheckedofObjectwithValue(count + 2, topRemappableOop(), label); + storePointerUncheckedofObjectwithValue(count + 3, topRemappableOop(), value); + constituents = popRemappableOop(); + beRootIfOld(constituents); + + /* would like to assert this, but it requires the leak checked be run :-( + self assert: self allMachineCodeObjectReferencesValid. */ + return constituents; +} + + /* Cogit>>#getLiteral: */ +static NoDbgRegParms sqInt +getLiteral(sqInt litIndex) +{ + if (maxLitIndex < litIndex) { + maxLitIndex = litIndex; + } + return literalofMethod(litIndex, methodObj); +} + + /* Cogit>>#getMethodZoneBase */ +#if VMInvestigations +usqInt +getMethodZoneBase(void) +{ + return methodZoneBase; +} +#endif /* VMInvestigations */ + + +/* Access for the literal manager. */ + + /* Cogit>>#getOpcodeIndex */ +static sqInt +getOpcodeIndex(void) +{ + return opcodeIndex; +} + + /* Cogit>>#incrementUsageOfTargetIfLinkedSend:mcpc:ignored: */ +static NoDbgRegParms sqInt +incrementUsageOfTargetIfLinkedSendmcpcignored(sqInt annotation, char *mcpc, sqInt superfluity) +{ + sqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + + if (annotation >= IsSendCall) { + assert(annotation != IsNSSendCall); + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (((targetMethod1->cmUsageCount)) < (CMMaxUsageCount / 2)) { + ((((CogMethod *) ((((usqInt)targetMethod1)) + codeToDataDelta)))->cmUsageCount = ((targetMethod1->cmUsageCount)) + 1); + } + } + } + return 0; +} + + +/* Answer the value to put in an inline-cache that is being loaded with the + selector. Usually this is simply the selector, but in 64-bits the cache is + only 32-bits wide + and so the cache is loaded with the index of the selector. */ +/* First search the special selectors; there are only 32 of them so this + shouldn't take too long. + We could short-circuit this by keeping a hint bit in the target method, or + by maintaining the + maximum range of selector oops in specialSelectors since they're likely to + cluster. + */ + + /* Cogit>>#indexForSelector:in: */ +static NoDbgRegParms sqInt +indexForSelectorin(sqInt selector, CogMethod *cogMethod) +{ + sqInt i; + sqInt methodOop; + sqInt toDoLimit; + + for (i = 0; i < NumSpecialSelectors; i += 1) { + if (selector == (specialSelector(i))) { + return -1 - i; + } + } + methodOop = (cogMethod->methodObject); + + /* Then search the method's literal frame... open code fetchPointer:ofObject: for speed... */ + toDoLimit = literalCountOfMethodHeader((cogMethod->methodHeader)); + for (i = LiteralStart; i <= toDoLimit; i += 1) { + if ((longAt((void *)(((i * BytesPerOop) + BaseHeaderSize) + methodOop))) == selector) { + assert(selector == (literalofMethod(i - 1, methodOop))); + return i - 1; + } + } + error("could not find selector in method when unlinking send site"); + return 0; +} + + /* Cogit>>#initializeCodeZoneFrom:upTo: */ +void +initializeCodeZoneFromupTo(sqInt startAddress, sqInt endAddress) +{ + sqInt endAddressSqInt; + usqInt endAddressUsqInt; + AbstractInstruction *existingInst; + usqIntptr_t fixupSize; + sqInt i; + AbstractInstruction *newInst; + AbstractInstruction *newLiterals; + sqInt numberOfAbstractOpcodes; + usqIntptr_t opcodeSize; + usqInt startAddressUsqInt; + + /* begin initializeBackend */ + (methodLabel->machineCodeSize = 0); + (methodLabel->opcode = Label); + ((methodLabel->operands))[0] = 0; + ((methodLabel->operands))[1] = 0; + + /* begin initializeVarBase */ + assert((!((registerMaskFor(VarBaseReg)) & CallerSavedRegisterMask))); + varBaseAddress = computeGoodVarBaseAddress(); + assert((stackLimitAddress()) >= varBaseAddress); + assert((cStackPointerAddress()) >= varBaseAddress); + assert((cFramePointerAddress()) >= varBaseAddress); + assert((cReturnAddressAddress()) >= varBaseAddress); + assert((nextProfileTickAddress()) >= varBaseAddress); + + /* begin allocateLiterals: */ + if (4 > literalsSize) { + newLiterals = calloc(4, sizeof(CogAbstractInstruction)); + + /* Must copy across state (not using realloc, cuz...) and + must also update existing instructions to refer to the new ones... + It's either this or modify all generation routines to be able to retry + with more literals after running out of literals. */ + if (literals) { + for (i = 0; i < nextLiteralIndex; i += 1) { + existingInst = literalInstructionAt(i); + newInst = (&(newLiterals[i])); + cloneLiteralFrom(newInst, existingInst); + assert(!((existingInst->dependent))); + (existingInst->dependent = newInst); + } + for (i = 0; i < opcodeIndex; i += 1) { + existingInst = abstractInstructionAt(i); + if ((((existingInst->dependent))) + && (((((existingInst->dependent))->opcode)) == Literal)) { + (existingInst->dependent = (((existingInst->dependent))->dependent)); + } + } + } + free(literals); + literals = newLiterals; + literalsSize = 4; + } + + /* begin resetLiterals */ + /* an impossibly high value */ + firstOpcodeIndex = 0x10000; + nextLiteralIndex = (lastDumpedLiteralIndex = 0); + + /* If the platform requires flipping between executable and writable + states, the system expects to start in the writable state. */ + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + sqMakeMemoryExecutableFromToCodeToDataDelta(startAddress, endAddress, +# if DUAL_MAPPED_CODE_ZONE + (&codeToDataDelta) +# else + null +# endif + ); + codeBase = (methodZoneBase = startAddress); + stopsFromto(backEnd, startAddress, endAddress - 1); + + /* begin manageFrom:to: */ + mzFreeStart = (baseAddress = methodZoneBase); + youngReferrers = (limitAddress = endAddress); + openPICList = null; + methodBytesFreedSinceLastCompaction = 0; + methodCount = 0; + assertValidDualZone(); + + /* begin detectFeatures */ +# if __APPLE__ + /* begin detectFeaturesOnMacOS */ +# else +# if __linux__ + detectFeaturesOnLinux(backEnd); +# else +# if _WIN64 + detectFeaturesOnWindows(backEnd); +# else + detectFeaturesOnRawMachine(backEnd); +# endif + +# endif + +# endif // __APPLE__ + + + /* begin maybeGenerateCacheFlush */ + if ((numICacheFlushOpcodes(backEnd)) > 0) { + numberOfAbstractOpcodes = numICacheFlushOpcodes(backEnd); + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = numberOfAbstractOpcodes; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + firstOpcodeIndex = 0x10000; + nextLiteralIndex = (lastDumpedLiteralIndex = 0); + labelCounter = 0; + startAddressUsqInt = methodZoneBase; + generateICacheFlush(backEnd); + outputInstructionsForGeneratedRuntimeAt(startAddressUsqInt); + recordGeneratedRunTimeaddress("ceFlushICache", startAddressUsqInt); + ceFlushICache = ((void (*)(usqIntptr_t,usqIntptr_t)) startAddressUsqInt); + + /* begin initialFlushICacheFrom:to: */ +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) startAddressUsqInt), (methodZoneBase - startAddressUsqInt) + 1); + sys_icache_invalidate(((void *) startAddressUsqInt), (methodZoneBase - startAddressUsqInt) + 1); +# elif _WIN64 + FlushInstructionCache((Win32ProcessHandle = GetCurrentProcess()), ((void *) startAddressUsqInt), (methodZoneBase - startAddressUsqInt) + 1); +# elif __GNUC__ + __clear_cache(((char *) startAddressUsqInt), ((char *) methodZoneBase)); +# else + error("cache flushing method unknown for this platform"); +# endif + + } + if ((numDCacheFlushOpcodes(backEnd)) > 0) { + numberOfAbstractOpcodes = numDCacheFlushOpcodes(backEnd); + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = numberOfAbstractOpcodes; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + firstOpcodeIndex = 0x10000; + nextLiteralIndex = (lastDumpedLiteralIndex = 0); + labelCounter = 0; + startAddressUsqInt = methodZoneBase; + generateDCacheFlush(backEnd); + outputInstructionsForGeneratedRuntimeAt(startAddressUsqInt); + recordGeneratedRunTimeaddress("ceFlushDCache", startAddressUsqInt); + ceFlushDCache = ((void (*)(usqIntptr_t,usqIntptr_t)) startAddressUsqInt); + + /* begin initialFlushICacheFrom:to: */ +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) startAddressUsqInt), (methodZoneBase - startAddressUsqInt) + 1); + sys_icache_invalidate(((void *) startAddressUsqInt), (methodZoneBase - startAddressUsqInt) + 1); +# elif _WIN64 + FlushInstructionCache((Win32ProcessHandle = GetCurrentProcess()), ((void *) startAddressUsqInt), (methodZoneBase - startAddressUsqInt) + 1); +# elif __GNUC__ + __clear_cache(((char *) startAddressUsqInt), ((char *) methodZoneBase)); +# else + error("cache flushing method unknown for this platform"); +# endif + + } + if (!((numICacheFlushOpcodes(backEnd)) + (numDCacheFlushOpcodes(backEnd)))) { + /* begin initializeCacheFlush */ +# if _WIN64 + Win32ProcessHandle = GetCurrentProcess(); +# endif + + } + + /* self generateVMOwnerLockFunctions. */ + genGetLeafCallStackPointers(); + generateStackPointerCapture(); + generateTrampolines(); + computeEntryOffsets(); + computeFullBlockEntryOffsets(); + generateClosedPICPrototype(); + alignMethodZoneBase(); + + /* None of the above is executed beyond ceCheckFeatures, so a bulk flush now is the leanest thing to do. */ + endAddressSqInt = ((usqInt)methodZoneBase); + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) startAddress), endAddressSqInt - startAddress); + sys_icache_invalidate(((void *) startAddress), endAddressSqInt - startAddress); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) startAddress), (endAddressSqInt - startAddress) + 1); +# else + ceFlushICache(startAddress, endAddressSqInt); +# endif + + startAddressUsqInt = startAddress; + endAddressUsqInt = ((usqInt)methodZoneBase); + + /* begin maybeFlushWritableZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + if (codeToDataDelta > 0) { + flushDCacheFromto(backEnd, startAddressUsqInt, endAddressUsqInt); + } +# endif + + + /* Repeat so that now the methodZone ignores the generated run-time. */ + + /* begin manageFrom:to: */ + mzFreeStart = (baseAddress = methodZoneBase); + youngReferrers = (limitAddress = endAddress); + openPICList = null; + methodBytesFreedSinceLastCompaction = 0; + methodCount = 0; + + /* N.B. this is assumed to be the last thing done in initialization; see Cogit>>initialized. + This is done only to compute openPICSize; the generated code is discarded. */ + generateOpenPICPrototype(); +} + + +/* Answer a usage count that reflects likely long-term usage. + Answer 1 for non-primitives or quick primitives (inst var accessors), + 2 for methods with interpreter primitives, and 3 for compiled primitives. */ + + /* Cogit>>#initialMethodUsageCount */ +static sqInt +initialMethodUsageCount(void) +{ + if ((primitiveIndex == 1) + || (isQuickPrimitiveIndex(primitiveIndex))) { + return 1; + } + if (!(primitiveGeneratorOrNil())) { + return 2; + } + return 3; +} + + +/* Answer a usage count that reflects likely long-term usage. */ + + /* Cogit>>#initialOpenPICUsageCount */ +static int +initialOpenPICUsageCount(void) +{ + return CMMaxUsageCount - 1; +} + + +/* Answer if entryPoint, which must be either the MNU or interpret abort in + cPIC (see compileClosedPICPrototype), invokes the MNU abort. The corollary + is that + it invokes the interpret abort. */ + + /* Cogit>>#intraCPICTarget:invokesMNUIn: */ +static NoDbgRegParms int +intraCPICTargetinvokesMNUIn(sqInt targetMcpc, CogMethod *cPIC) +{ + assert(((targetMcpc >= (((usqInt)(cPIC + 1)))) && (targetMcpc <= ((((usqInt)cPIC)) + cmEntryOffset)))); + return targetMcpc == (((usqInt)(cPIC + 1))); +} + + /* Cogit>>#inverseBranchFor: */ +static NoDbgRegParms sqInt +inverseBranchFor(sqInt opcode) +{ + switch (opcode) { + case JumpLongZero: + return JumpLongNonZero; + + case JumpLongNonZero: + return JumpLongZero; + + case JumpZero: + return JumpNonZero; + + case JumpNonZero: + return JumpZero; + + case JumpNegative: + return JumpNonNegative; + + case JumpNonNegative: + return JumpNegative; + + case JumpOverflow: + return JumpNoOverflow; + + case JumpNoOverflow: + return JumpOverflow; + + case JumpCarry: + return JumpNoCarry; + + case JumpNoCarry: + return JumpCarry; + + case JumpLess: + return JumpGreaterOrEqual; + + case JumpGreaterOrEqual: + return JumpLess; + + case JumpGreater: + return JumpLessOrEqual; + + case JumpLessOrEqual: + return JumpGreater; + + case JumpBelow: + return JumpAboveOrEqual; + + case JumpAboveOrEqual: + return JumpBelow; + + case JumpAbove: + return JumpBelowOrEqual; + + case JumpBelowOrEqual: + return JumpAbove; + + default: + error("Case not found and no otherwise clause"); + } + error("invalid opcode for inverse"); + return 0; +} + + +/* Answer a pair-wise Array of cPIC selector, target for the first case, and + then class index, target for the remaining cases. + If a case in an MNU case then target is the doesNotUnderstand: selector. */ + + /* Cogit>>#investigationArrayForClosedPIC: */ +#if VMInvestigations +static NoDbgRegParms sqInt +investigationArrayForClosedPIC(CogMethod *cPIC) +{ + sqInt data; + sqInt entryPoint; + sqInt i; + sqInt mcpc; + sqInt nc; + + data = instantiateClassindexableSize(classArray(), (nc = ((cPIC->cPICNumCases)) * 2)); + if (!data) { + return null; + } + entryPoint = jumpLongTargetBeforeFollowingAddress(backEnd, addressOfEndOfCaseinCPIC(1, cPIC)); + storePointerUncheckedofObjectwithValue(0, data, (cPIC->selector)); + storePointerUncheckedofObjectwithValue(1, data, investigationTargetForEntryPointin(entryPoint, cPIC)); + + /* store class indices backwards because the PIC grows backwards. */ + for (i = 2; i <= ((cPIC->cPICNumCases)); i += 1) { + mcpc = addressOfEndOfCaseinCPIC(i, cPIC); + storePointerUncheckedofObjectwithValue((nc -= 1), data, investigationTargetForEntryPointin(jumpLongTargetBeforeFollowingAddress(backEnd, mcpc), cPIC)); + storePointerUncheckedofObjectwithValue((nc -= 1), data, (((usqInt)(literal32BeforeFollowingAddress(backEnd, mcpc - (jumpLongConditionalByteSize(backEnd)))) << 3) | 1)); + } + return data; +} +#endif /* VMInvestigations */ + + +/* Resolve a jump dispatch in a closed PIC into either a method or, if it is + an MNU dispatch, the MNU selector. + */ + + /* Cogit>>#investigationTargetForEntryPoint:in: */ +#if VMInvestigations +static NoDbgRegParms sqInt +investigationTargetForEntryPointin(sqInt entryPoint, CogMethod *cPIC) +{ + return (/* containsAddress: */ + ((((usqInt)cPIC)) <= (((usqInt)entryPoint))) + && (((((usqInt)cPIC)) + ((cPIC->blockSize))) >= (((usqInt)entryPoint))) + ? (intraCPICTargetinvokesMNUIn(entryPoint, cPIC) + ? splObj(SelectorDoesNotUnderstand) + : positive64BitIntegerFor(((usqInt)interpret))) + : ((((CogMethod *) (entryPoint - cmNoCheckEntryOffset)))->methodObject)); +} +#endif /* VMInvestigations */ + + +/* Useful for debugging, asserts, etc */ + + /* Cogit>>#isPCWithinMethodZone: */ +int +isPCWithinMethodZone(usqInt address) +{ + return ((address >= methodZoneBase) && (address <= (freeStart()))); +} + + +/* Answer if the instruction preceding retpc is a call instruction. */ + + /* Cogit>>#isSendReturnPC: */ +sqInt +isSendReturnPC(sqInt retpc) +{ + sqInt target; + + if (!(isCallPrecedingReturnPC(backEnd, retpc))) { + return 0; + } + target = callTargetFromReturnAddress(backEnd, retpc); + return (((target >= firstSend) && (target <= lastSend))) + || (((target >= methodZoneBase) && (target <= (freeStart())))); +} + + +/* Floating-point jumps are a little weird on some processors. Defer to + the backEnd to allow it to generate any special code it may need to. */ + + /* Cogit>>#JumpFPEqual: */ +static NoDbgRegParms AbstractInstruction * +gJumpFPEqual(void *jumpTarget) +{ + return genoperand(JumpFPEqual, ((sqInt)jumpTarget)); +} + + +/* Floating-point jumps are a little weird on some processors. Defer to + the backEnd to allow it to generate any special code it may need to. */ + + /* Cogit>>#JumpFPGreaterOrEqual: */ +static NoDbgRegParms AbstractInstruction * +gJumpFPGreaterOrEqual(void *jumpTarget) +{ + return genoperand(JumpFPGreaterOrEqual, ((sqInt)jumpTarget)); +} + + +/* Floating-point jumps are a little weird on some processors. Defer to + the backEnd to allow it to generate any special code it may need to. */ + + /* Cogit>>#JumpFPGreater: */ +static NoDbgRegParms AbstractInstruction * +gJumpFPGreater(void *jumpTarget) +{ + return genoperand(JumpFPGreater, ((sqInt)jumpTarget)); +} + + +/* Floating-point jumps are a little weird on some processors. Defer to + the backEnd to allow it to generate any special code it may need to. */ + + /* Cogit>>#JumpFPNotEqual: */ +static NoDbgRegParms AbstractInstruction * +gJumpFPNotEqual(void *jumpTarget) +{ + return genoperand(JumpFPNotEqual, ((sqInt)jumpTarget)); +} + + /* Cogit>>#LogicalShiftLeftCq:R: */ +static NoDbgRegParms AbstractInstruction * +gLogicalShiftLeftCqR(sqInt quickConstant, sqInt reg) +{ + return genoperandoperand(LogicalShiftLeftCqR, quickConstant, reg); +} + + +/* destReg := srcReg << quickConstant */ + + /* Cogit>>#LogicalShiftLeftCq:R:R: */ +static NoDbgRegParms AbstractInstruction * +gLogicalShiftLeftCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg) +{ + return genoperandoperandoperand(LogicalShiftLeftCqRR, quickConstant, srcReg, destReg); +} + + +/* destReg := (unsigned)srcReg >> quickConstant */ + + /* Cogit>>#LogicalShiftRightCq:R:R: */ +static NoDbgRegParms AbstractInstruction * +gLogicalShiftRightCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg) +{ + return genoperandoperandoperand(LogicalShiftRightCqRR, quickConstant, srcReg, destReg); +} + + /* Cogit>>#lastOpcode */ +static AbstractInstruction * +lastOpcode(void) +{ + assert(opcodeIndex > 0); + return abstractInstructionAt(opcodeIndex - 1); +} + + +/* Answer the linked send targetof the send before returnAddress, if valid, + otherwise nil + */ +/* Answer if the instruction preceding retpc is a call instruction. */ + + /* Cogit>>#linkedSendTargetOrNilAt: */ +CogMethod * +linkedSendTargetOrNilAt(sqInt retpc) +{ + sqInt target; + CogMethod *targetCogMethod; + + if (!(isCallPrecedingReturnPC(backEnd, retpc))) { + return null; + } + target = callTargetFromReturnAddress(backEnd, retpc); + if (!(((target >= methodZoneBase) && (target <= (freeStart()))))) { + return null; + } + if ((targetCogMethod = methodFor(((void *)target)))) { + if (((targetCogMethod->cmType)) == CMMethod) { + return targetCogMethod; + } + } + return null; +} + + /* Cogit>>#linkSendAt:in:to:offset:receiver: */ +void +linkSendAtintooffsetreceiver(sqInt callSiteReturnAddress, CogMethod *sendingMethod, CogMethod *targetMethod, sqInt theEntryOffset, sqInt receiver) +{ + sqInt extent; + sqInt inlineCacheTag; + + assert((theEntryOffset == cmEntryOffset) + || (theEntryOffset == cmNoCheckEntryOffset)); + assert(((callSiteReturnAddress >= methodZoneBase) && (callSiteReturnAddress <= (freeStart())))); + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + if (theEntryOffset == cmNoCheckEntryOffset) { + extent = rewriteCallAttarget(backEnd, callSiteReturnAddress, (((sqInt)targetMethod)) + cmNoCheckEntryOffset); + } + else { + inlineCacheTag = inlineCacheTagForInstance(receiver); + extent = rewriteInlineCacheAttagtarget(backEnd, callSiteReturnAddress, inlineCacheTag, (((sqInt)targetMethod)) + theEntryOffset); + } + + /* no need to change selector cache tag */ + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) ((((usqInt)callSiteReturnAddress)) - extent)), (((usqInt)callSiteReturnAddress)) - ((((usqInt)callSiteReturnAddress)) - extent)); + sys_icache_invalidate(((void *) ((((usqInt)callSiteReturnAddress)) - extent)), (((usqInt)callSiteReturnAddress)) - ((((usqInt)callSiteReturnAddress)) - extent)); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) ((((usqInt)callSiteReturnAddress)) - extent)), ((((usqInt)callSiteReturnAddress)) - ((((usqInt)callSiteReturnAddress)) - extent)) + 1); +# else // __APPLE__ && __MACH__ || _WIN64 + ceFlushICache((((usqInt)callSiteReturnAddress)) - extent, ((usqInt)callSiteReturnAddress)); +# endif +} + + /* Cogit>>#loadBytesAndGetDescriptor: */ +static NoDbgRegParms BytecodeDescriptor * +loadBytesAndGetDescriptor(sqInt pc) +{ + BytecodeDescriptor *descriptor; + + byte0 = (fetchByteofObject(pc, methodObj)) + bytecodeSetOffset; + descriptor = generatorAt(byte0); + + /* begin loadSubsequentBytesForDescriptor:at: */ + if (((descriptor->numBytes)) > 1) { + byte1 = fetchByteofObject(pc + 1, methodObj); + if (((descriptor->numBytes)) > 2) { + byte2 = fetchByteofObject(pc + 2, methodObj); + if (((descriptor->numBytes)) > 3) { + byte3 = fetchByteofObject(pc + 3, methodObj); + if (((descriptor->numBytes)) > 4) { + notYetImplemented(); + } + } + } + } + return descriptor; +} + + /* Cogit>>#MoveCq:R: */ +static NoDbgRegParms AbstractInstruction * +gMoveCqR(sqInt quickConstant, sqInt reg) +{ + AbstractInstruction *anInstruction; + + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, quickConstant, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return anInstruction; +} + + /* Cogit>>#MovePerfCnt64R:L: */ +static NoDbgRegParms AbstractInstruction * +gMovePerfCnt64RL(sqInt destReg, sqInt liveRegisterMask) +{ + assert(BytesPerWord == 8); + return genoperandoperand(MovePerfCnt64RL, destReg, liveRegisterMask); +} + + /* Cogit>>#MoveR:Mw:r: */ +static NoDbgRegParms AbstractInstruction * +gMoveRMwr(sqInt sourceReg, sqInt offset, sqInt baseReg) +{ + AbstractInstruction *anInstruction; + + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, sourceReg, offset, baseReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + return anInstruction; +} + + +/* Answer the address of the null byte at the end of the method map. */ + + /* Cogit>>#mapEndFor: */ +static NoDbgRegParms sqInt +mapEndFor(CogMethod *cogMethod) +{ + sqInt end; + + end = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + while ((byteAt((void *)(end))) != MapEnd) { + end -= 1; + assert(end > (firstMappedPCFor(cogMethod))); + } + return (end & ~3); +} + + +/* Unlinking/GC/Disassembly support */ +/* most of the time arg is a CogMethod... */ + + /* Cogit>>#mapFor:performUntil:arg: */ +static NoDbgRegParms sqInt +mapForperformUntilarg(CogMethod *cogMethod, sqInt (*functionSymbol)(sqInt annotation, char *mcpc, CogMethod *arg), void *arg) +{ + sqInt annotation; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = functionSymbol(annotation, (((char *) mcpc)), arg); + if (result) { + return result; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + return 0; +} + + +/* Remap all object references in the closed PIC. Answer if any references + are young. + Set codeModified if any modifications are made. */ + + /* Cogit>>#mapObjectReferencesInClosedPIC: */ +static NoDbgRegParms sqInt +mapObjectReferencesInClosedPIC(CogMethod *cPIC) +{ + sqInt i; + sqInt pc; + sqInt refersToYoung; + + pc = addressOfEndOfCaseinCPIC(1, cPIC); + + /* first we check the potential method oop load at the beginning of the CPIC */ + refersToYoung = remapMaybeObjRefInClosedPICAt(pc - (jumpLongByteSize(backEnd))); + + /* We find the end address of the cPICNumCases'th case and can then just step forward by the case size thereafter */ + pc = addressOfEndOfCaseinCPIC((cPIC->cPICNumCases), cPIC); + + /* Next we check the potential class ref in the compare instruction, and the potential method oop load for each case. */ + for (i = 2; i <= ((cPIC->cPICNumCases)); i += 1) { + if (remapMaybeObjRefInClosedPICAt((pc - (jumpLongConditionalByteSize(backEnd))) - (cmpC32RTempByteSize(backEnd)))) { + refersToYoung = 1; + } + pc += cPICCaseSize; + } + return refersToYoung; +} + + +/* Update all references to objects in the generated runtime. */ + + /* Cogit>>#mapObjectReferencesInGeneratedRuntime */ +static void +mapObjectReferencesInGeneratedRuntime(void) +{ + sqInt i; + sqInt literal; + sqInt mappedLiteral; + usqInt mcpc; + + for (i = 0; i < runtimeObjectRefIndex; i += 1) { + mcpc = objectReferencesInRuntime[i]; + literal = longAt((void *)(mcpc)); + mappedLiteral = remapObject(literal); + if (mappedLiteral != literal) { + /* begin setCodeModified */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + if (!codeModified) { + codeModified = 1; + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + } + } + else { + codeModified = 1; + } + + /* begin storeLiteral:atAnnotatedAddress:using: */ + codeLongAtput((void *)(mcpc),mappedLiteral); + } + } +} + + +/* Update all references to objects in machine code for a become. + Unlike incrementalGC or fullGC a method that does not refer to young may + refer to young as a result of the become operation. Unlike incrementalGC + or fullGC the reference from a Cog method to its methodObject *must not* + change since the two are two halves of the same object. */ + + /* Cogit>>#mapObjectReferencesInMachineCodeForBecome */ +static void +mapObjectReferencesInMachineCodeForBecome(void) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt endAddress; + sqInt freedPIC; + sqInt hasYoungObj; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt remappedMethod; + sqInt result; + CogMethod *writableCogMethod; + + hasYoungObj = 0; + codeModified = (freedPIC = 0); + mapObjectReferencesInGeneratedRuntime(); + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + assert(!hasYoungObj); + if (!(((cogMethod->cmType)) == CMFree)) { + assert((cogMethodDoesntLookKosher(cogMethod)) == 0); + writableCogMethod = ((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)); + (writableCogMethod->selector = remapOop((cogMethod->selector))); + if (((cogMethod->cmType)) == CMClosedPIC) { + if ((isYoung((cogMethod->selector))) + || (mapObjectReferencesInClosedPIC(cogMethod))) { + freedPIC = 1; + freeMethod(cogMethod); + } + } + else { + if (isYoung((cogMethod->selector))) { + hasYoungObj = 1; + } + if (((cogMethod->cmType)) >= CMMethod) { + assert(((cogMethod->objectHeader)) == (nullHeaderForMachineCodeMethod())); + remappedMethod = remapOop((cogMethod->methodObject)); + if (remappedMethod != ((cogMethod->methodObject))) { + if (methodHasCogMethod(remappedMethod)) { + error("attempt to become two cogged methods"); + } + if (!(withoutForwardingOnandwithsendToCogit((cogMethod->methodObject), remappedMethod, (cogMethod->cmUsesPenultimateLit), methodhasSameCodeAscheckPenultimate))) { + error("attempt to become cogged method into different method"); + } + + /* For non-Newspeak there should ne a one-to-one mapping between bytecoded and + cog methods. For Newspeak not necessarily, but only for anonymous accessors. + Only reset the method object's header if it is referring to this CogMethod. */ + if ((rawHeaderOf((cogMethod->methodObject))) == (((sqInt)cogMethod))) { + rawHeaderOfput((cogMethod->methodObject), (cogMethod->methodHeader)); + (writableCogMethod->methodHeader = rawHeaderOf(remappedMethod)); + (writableCogMethod->methodObject = remappedMethod); + rawHeaderOfput(remappedMethod, ((sqInt)cogMethod)); + } + else { + assert((noAssertMethodClassAssociationOf((cogMethod->methodObject))) == (nilObject())); + (writableCogMethod->methodHeader = rawHeaderOf(remappedMethod)); + (writableCogMethod->methodObject = remappedMethod); + } + } + if (isYoung((cogMethod->methodObject))) { + hasYoungObj = 1; + } + } + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = remapIfObjectRefpchasYoung(annotation, ((char *) mcpc), ((void *)((&hasYoungObj)))); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1: + if (hasYoungObj) { + ensureInYoungReferrers(cogMethod); + hasYoungObj = 0; + } + else { + (cogMethod->cmRefersToYoung = 0); + } + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + + /* we /must/ prune youngReferrers here because a) the [cogMethod cmRefersToYoung: false] + block could have removed a method and subsequently it could be added back, and b) we + can not tolerate duplicates in the youngReferrers list. */ + pruneYoungReferrers(); + if (freedPIC) { + unlinkSendsToFree(); + } + if (codeModified) { + endAddress = freeStart(); + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) (((usqInt)codeBase))), endAddress - (((usqInt)codeBase))); + sys_icache_invalidate(((void *) (((usqInt)codeBase))), endAddress - (((usqInt)codeBase))); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) (((usqInt)codeBase))), (endAddress - (((usqInt)codeBase))) + 1); +# else // __APPLE__ && __MACH__ || _WIN64 + ceFlushICache(((usqInt)codeBase), endAddress); +# endif + + } +} + + +/* Update all references to objects in machine code for a full gc. Since + the current (New)ObjectMemory GC makes everything old in a full GC + a method not referring to young will not refer to young afterwards */ + + /* Cogit>>#mapObjectReferencesInMachineCodeForFullGC */ +static void +mapObjectReferencesInMachineCodeForFullGC(void) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt endAddress; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + CogMethod *writableCogMethod; + + codeModified = 0; + mapObjectReferencesInGeneratedRuntime(); + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (!(((cogMethod->cmType)) == CMFree)) { + assert((cogMethodDoesntLookKosher(cogMethod)) == 0); + writableCogMethod = ((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)); + (writableCogMethod->selector = remapOop((cogMethod->selector))); + if (((cogMethod->cmType)) == CMClosedPIC) { + assert(!((cogMethod->cmRefersToYoung))); + mapObjectReferencesInClosedPIC(cogMethod); + } + else { + if (((cogMethod->cmType)) >= CMMethod) { + assert(((cogMethod->objectHeader)) == (nullHeaderForMachineCodeMethod())); + (writableCogMethod->methodObject = remapOop((cogMethod->methodObject))); + } + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = remapIfObjectRefpchasYoung(annotation, ((char *) mcpc), 0); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + pruneYoungReferrers(); + if (codeModified) { + endAddress = freeStart(); + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) (((usqInt)codeBase))), endAddress - (((usqInt)codeBase))); + sys_icache_invalidate(((void *) (((usqInt)codeBase))), endAddress - (((usqInt)codeBase))); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) (((usqInt)codeBase))), (endAddress - (((usqInt)codeBase))) + 1); +# else // __APPLE__ && __MACH__ || _WIN64 + ceFlushICache(((usqInt)codeBase), endAddress); +# endif + + } +} + + +/* Update all references to objects in machine code for either a Spur + scavenging gc + or a Squeak V3 incremental GC. Avoid scanning all code by using the + youngReferrers list. In a young gc a method referring to young may no + longer refer to young, but a + method not referring to young cannot and will not refer to young + afterwards. */ + + /* Cogit>>#mapObjectReferencesInMachineCodeForYoungGC */ +static void +mapObjectReferencesInMachineCodeForYoungGC(void) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt endAddress; + sqInt hasYoungObj; + sqInt map; + sqInt mapByte; + usqInt mcpc; + usqInt pointer; + sqInt result; + CogMethod *writableCogMethod; + sqInt zoneIsWritable; + + codeModified = (zoneIsWritable = (hasYoungObj = 0)); + pointer = youngReferrers; + while (pointer < limitAddress) { + assert(!hasYoungObj); + cogMethod = ((CogMethod *) (longAt((void *)(pointer)))); + if (((cogMethod->cmType)) == CMFree) { + assert(!((cogMethod->cmRefersToYoung))); + } + else { + assert((cogMethodDoesntLookKosher(cogMethod)) == 0); + if ((cogMethod->cmRefersToYoung)) { + assert((isCMMethodEtAl(((CogBlockMethod *) cogMethod))) + || (isCMOpenPIC(((CogBlockMethod *) cogMethod)))); + if (!zoneIsWritable) { + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + zoneIsWritable = 1; + } + writableCogMethod = ((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)); + (writableCogMethod->selector = remapOop((cogMethod->selector))); + if (isYoung((cogMethod->selector))) { + hasYoungObj = 1; + } + if (((cogMethod->cmType)) >= CMMethod) { + assert(((cogMethod->objectHeader)) == (nullHeaderForMachineCodeMethod())); + (writableCogMethod->methodObject = remapOop((cogMethod->methodObject))); + if (isYoung((cogMethod->methodObject))) { + hasYoungObj = 1; + } + } + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = remapIfObjectRefpchasYoung(annotation, ((char *) mcpc), ((void *)((&hasYoungObj)))); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1: + if (hasYoungObj) { + hasYoungObj = 0; + } + else { + (writableCogMethod->cmRefersToYoung = 0); + } + } + } + pointer += BytesPerWord; + } + pruneYoungReferrers(); + if (codeModified) { + endAddress = freeStart(); + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) (((usqInt)methodZoneBase))), endAddress - (((usqInt)methodZoneBase))); + sys_icache_invalidate(((void *) (((usqInt)methodZoneBase))), endAddress - (((usqInt)methodZoneBase))); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) (((usqInt)methodZoneBase))), (endAddress - (((usqInt)methodZoneBase))) + 1); +# else // __APPLE__ && __MACH__ || _WIN64 + ceFlushICache(((usqInt)methodZoneBase), endAddress); +# endif + + } +} + + +/* Update all references to objects in machine code. */ + + /* Cogit>>#mapObjectReferencesInMachineCode: */ +void +mapObjectReferencesInMachineCode(sqInt gcMode) +{ + switch (gcMode) { + case GCModeNewSpace: + /* N.B. do *not* ensureWritableCodeZone for every scavenge. */ + mapObjectReferencesInMachineCodeForYoungGC(); + break; + case GCModeFull: + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + mapObjectReferencesInMachineCodeForFullGC(); + break; + case GCModeBecome: + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + mapObjectReferencesInMachineCodeForBecome(); + break; + default: + error("Case not found and no otherwise clause"); + } + mapPerMethodProfile(); + if (!(asserta((freeStart()) <= (youngReferrers)))) { + error("youngReferrers list overflowed"); + } +} + + +/* Mark objects in machine-code of marked methods (or open PICs with marked + selectors). + */ + + /* Cogit>>#markAndTraceMachineCodeOfMarkedMethods */ +void +markAndTraceMachineCodeOfMarkedMethods(void) +{ + sqInt annotation; + sqInt annotationSqInt; + CogMethod *cogMethod; + sqInt endAddress; + sqInt map; + sqInt mapByte; + sqInt mapByteSqInt; + sqInt mapSqInt; + usqInt mcpc; + usqInt mcpcUsqInt; + sqInt result; + sqInt resultSqInt; + + if (leakCheckFullGC()) { + asserta(allMachineCodeObjectReferencesValid()); + } + codeModified = 0; + markAndTraceObjectReferencesInGeneratedRuntime(); + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if ((((cogMethod->cmType)) >= CMMethod) + && (isMarked((cogMethod->methodObject)))) { + /* begin markAndTraceLiteralsIn: */ + assert(((isCMMethodEtAl(((CogBlockMethod *) cogMethod))) + && (isMarked((cogMethod->methodObject)))) + || ((isCMOpenPIC(((CogBlockMethod *) cogMethod))) + && ((isImmediate((cogMethod->selector))) + || (isMarked((cogMethod->selector)))))); + markAndTraceLiteralinat((cogMethod->selector), cogMethod, (&((cogMethod->selector)))); + + /* begin maybeMarkIRCsIn: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = markLiteralspcmethod(annotation, ((char *) mcpc), cogMethod); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + if ((((cogMethod->cmType)) == CMOpenPIC) + && ((isImmediate((cogMethod->selector))) + || (isMarked((cogMethod->selector))))) { + /* begin markAndTraceLiteralsIn: */ + assert(((isCMMethodEtAl(((CogBlockMethod *) cogMethod))) + && (isMarked((cogMethod->methodObject)))) + || ((isCMOpenPIC(((CogBlockMethod *) cogMethod))) + && ((isImmediate((cogMethod->selector))) + || (isMarked((cogMethod->selector)))))); + markAndTraceLiteralinat((cogMethod->selector), cogMethod, (&((cogMethod->selector)))); + + /* begin maybeMarkIRCsIn: */ + mapByteSqInt = 0; + mcpcUsqInt = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + mapSqInt = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByteSqInt = byteAt((void *)(mapSqInt)))) != MapEnd) { + if (mapByteSqInt >= FirstAnnotation) { + mcpcUsqInt += (mapByteSqInt & DisplacementMask) * 4 /* codeGranularity */; + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotationSqInt = ((usqInt)(mapByteSqInt)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByteSqInt = byteAt((void *)(mapSqInt - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotationSqInt += mapByteSqInt & DisplacementMask; + mapSqInt -= 1; + } + resultSqInt = markLiteralspcmethod(annotationSqInt, ((char *) mcpcUsqInt), cogMethod); + if (resultSqInt) { + goto l2; + } + } + else { + if (mapByteSqInt < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpcUsqInt += ((((usqInt)((mapByteSqInt - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + mapSqInt -= 1; + } + /* end mapFor:performUntil:arg: */ +l2:; + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if (leakCheckFullGC()) { + asserta(allMachineCodeObjectReferencesValid()); + } + if (codeModified) { + endAddress = freeStart(); + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) (((usqInt)methodZoneBase))), endAddress - (((usqInt)methodZoneBase))); + sys_icache_invalidate(((void *) (((usqInt)methodZoneBase))), endAddress - (((usqInt)methodZoneBase))); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) (((usqInt)methodZoneBase))), (endAddress - (((usqInt)methodZoneBase))) + 1); +# else // __APPLE__ && __MACH__ || _WIN64 + ceFlushICache(((usqInt)methodZoneBase), endAddress); +# endif + + } +} + + +/* Mark and trace any object references in the generated run-time. */ + + /* Cogit>>#markAndTraceObjectReferencesInGeneratedRuntime */ +static void +markAndTraceObjectReferencesInGeneratedRuntime(void) +{ + sqInt i; + sqInt literal; + usqInt mcpc; + + for (i = 0; i < runtimeObjectRefIndex; i += 1) { + mcpc = objectReferencesInRuntime[i]; + literal = longAt((void *)(mcpc)); + markAndTraceLiteralinatpc(literal, ((CogMethod *) null), ((usqInt)mcpc)); + } +} + + +/* Mark and trace objects in the argument and free if it is appropriate. + Answer if the method has been freed. firstVisit is a hint used to avoid + scanning methods we've already seen. False positives are fine. + For a CMMethod this + frees if the bytecode method isnt marked, + marks and traces object literals and selectors, + unlinks sends to targets that should be freed. + For a CMClosedPIC this + frees if it refers to anything that should be freed or isn't marked. + For a CMOpenPIC this + frees if the selector isn't marked. */ +/* this recurses at most one level down */ + + /* Cogit>>#markAndTraceOrFreeCogMethod:firstVisit: */ +static NoDbgRegParms sqInt +markAndTraceOrFreeCogMethodfirstVisit(CogMethod *cogMethod, sqInt firstVisit) +{ + sqInt annotation; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + + if (((cogMethod->cmType)) == CMFree) { + return 1; + } + assert((cogMethodDoesntLookKosher(cogMethod)) == 0); + if (((cogMethod->cmType)) >= CMMethod) { + if (!(isMarked((cogMethod->methodObject)))) { + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + freeMethod(cogMethod); + return 1; + } + if (firstVisit) { + /* begin markLiteralsAndUnlinkUnmarkedSendsIn: */ + assert(isCMMethodEtAl(((CogBlockMethod *) cogMethod))); + assert(isMarked((cogMethod->methodObject))); + markAndTraceLiteralinat((cogMethod->selector), cogMethod, (&((cogMethod->selector)))); + + /* begin maybeMarkIRCsIn: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = markLiteralsAndUnlinkIfUnmarkedSendpcmethod(annotation, ((char *) mcpc), cogMethod); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + return 0; + } + if (((cogMethod->cmType)) == CMClosedPIC) { + if (!(closedPICRefersToUnmarkedObject(cogMethod))) { + return 0; + } + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + freeMethod(cogMethod); + return 1; + } + if (((cogMethod->cmType)) == CMOpenPIC) { + if (isMarked((cogMethod->selector))) { + return 0; + } + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + freeMethod(cogMethod); + return 1; + } + assert((isCMMethodEtAl(((CogBlockMethod *) cogMethod))) + || ((isCMClosedPIC(((CogBlockMethod *) cogMethod))) + || (isCMOpenPIC(((CogBlockMethod *) cogMethod))))); + return 0; +} + + +/* If entryPoint is that of some method, then mark and trace objects in it + and free if it is appropriate. + Answer if the method has been freed. */ + + /* Cogit>>#markAndTraceOrFreePICTarget:in: */ +static NoDbgRegParms sqInt +markAndTraceOrFreePICTargetin(sqInt entryPoint, CogMethod *cPIC) +{ + CogMethod *targetMethod; + + assert((entryPoint > methodZoneBase) + && (entryPoint < (freeStart()))); + if (/* containsAddress: */ + ((((usqInt)cPIC)) <= (((usqInt)entryPoint))) + && (((((usqInt)cPIC)) + ((cPIC->blockSize))) >= (((usqInt)entryPoint)))) { + return 0; + } + targetMethod = ((CogMethod *) (entryPoint - cmNoCheckEntryOffset)); + assert((isCMMethodEtAl(((CogBlockMethod *) targetMethod))) + || (isCMFree(((CogBlockMethod *) targetMethod)))); + return markAndTraceOrFreeCogMethodfirstVisit(targetMethod, (((usqInt)targetMethod)) > (((usqInt)cPIC))); +} + + +/* Mark and trace literals. Unlink sends that have unmarked cache tags or + targets. + */ + + /* Cogit>>#markLiteralsAndUnlinkIfUnmarkedSend:pc:method: */ +static NoDbgRegParms sqInt +markLiteralsAndUnlinkIfUnmarkedSendpcmethod(sqInt annotation, char *mcpc, CogMethod *cogMethod) +{ + unsigned int cacheTag1; + sqInt cacheTagMarked; + sqInt entryPointSqInt; + unsigned int instr; + sqInt literal; + sqInt offsetSqInt; + sqInt pc; + sqInt *sendTable1; + CogMethod *targetMethod1; + sqInt unlinkedRoutine; + + if (annotation == IsObjectReference) { + literal = longAt((void *)(((usqInt)mcpc))); + if (markAndTraceLiteralinatpc(literal, ((CogMethod *) cogMethod), ((usqInt)mcpc))) { + codeModified = 1; + } + } + if (annotation >= IsSendCall) { + /* begin entryCacheTagAndCouldBeObjectAt:annotation:into: */ + /* begin inlineCacheTagAt: */ + /* begin pcRelativeAddressAt: */ + instr = long32At((void *)(((usqInt)((((sqInt)mcpc)) - 8)))); + + /* cogit processor disassembleInstructionAt: mcpc In: objectMemory memory */ + assert(instructionIsPCRelativeLoad(backEnd, instr)); + pc = (((((usqInt)(instr)) >> 3) & (0x3FFFFC)) - ((((usqInt)(instr)) >> 2) & (0x200000))) + (((usqInt)((((sqInt)mcpc)) - 8))); + cacheTag1 = long32At((void *)(pc)); + entryPointSqInt = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + + /* in-line cache tags are the selectors of sends if sends are unlinked, + the selectors of super sends (entry offset = cmNoCheckEntryOffset), + the selectors of open PIC sends (entry offset = cmEntryOffset, target is an Open PIC) + or in-line cache tags (classes, class indices, immediate bit patterns, etc). + Note that selectors can be immediate so there is no guarantee that they + are markable/remappable objects. Writing the block invocation three times + allows Slang to inline tagCouldBeObject tests. */ + cacheTagMarked = 0; + if (entryPointSqInt > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPointSqInt - offsetSqInt)); + if ((!cacheTagMarked) + || (markAndTraceOrFreeCogMethodfirstVisit(targetMethod1, (((usqInt)targetMethod1)) > (((usqInt)mcpc))))) { + /* Either the cacheTag is unmarked (e.g. new class) or the target + has been freed (because it is unmarked), so unlink the send. */ + + /* begin unlinkSendAt:targetMethod:sendTable: */ + unlinkedRoutine = sendTable1[((((targetMethod1->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod1->cmNumArgs)) : (NumSendTrampolines - 1))]; + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + if (!codeModified) { + codeModified = 1; + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + } + } + else { + codeModified = 1; + } + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod1->selector), enumeratingCogMethod), unlinkedRoutine); + markAndTraceLiteralinat((targetMethod1->selector), targetMethod1, (&((targetMethod1->selector)))); + } + } + else { + } + } + return 0; +} + + +/* Mark and trace literals. + Additionally in Newspeak, void push implicits that have unmarked classes. */ + + /* Cogit>>#markLiterals:pc:method: */ +static NoDbgRegParms sqInt +markLiteralspcmethod(sqInt annotation, char *mcpc, CogMethod *cogMethod) +{ + unsigned int cacheTag1; + sqInt entryPointSqInt; + unsigned int instr; + sqInt literal; + sqInt pc; + + if (annotation == IsObjectReference) { + literal = longAt((void *)(((usqInt)mcpc))); + if (markAndTraceLiteralinatpc(literal, cogMethod, ((usqInt)mcpc))) { + codeModified = 1; + } + } + if (annotation >= IsSendCall) { + /* begin entryCacheTagAndCouldBeObjectAt:annotation:into: */ + /* begin inlineCacheTagAt: */ + /* begin pcRelativeAddressAt: */ + instr = long32At((void *)(((usqInt)((((sqInt)mcpc)) - 8)))); + + /* cogit processor disassembleInstructionAt: mcpc In: objectMemory memory */ + assert(instructionIsPCRelativeLoad(backEnd, instr)); + pc = (((((usqInt)(instr)) >> 3) & (0x3FFFFC)) - ((((usqInt)(instr)) >> 2) & (0x200000))) + (((usqInt)((((sqInt)mcpc)) - 8))); + cacheTag1 = long32At((void *)(pc)); + entryPointSqInt = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + + /* in-line cache tags are the selectors of sends if sends are unlinked, + the selectors of super sends (entry offset = cmNoCheckEntryOffset), + the selectors of open PIC sends (entry offset = cmEntryOffset, target is an Open PIC) + or in-line cache tags (classes, class indices, immediate bit patterns, etc). + Note that selectors can be immediate so there is no guarantee that they + are markable/remappable objects. Writing the block invocation three times + allows Slang to inline tagCouldBeObject tests. */ + } + return 0; +} + + /* Cogit>>#markMethodAndReferents: */ +void +markMethodAndReferents(CogBlockMethod *aCogMethod) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + CogMethod *writableMethod; + + assert((isCMMethodEtAl(aCogMethod)) + || (isCMBlock(aCogMethod))); + cogMethod = (((aCogMethod->cmType)) >= CMMethod + ? ((CogMethod *) aCogMethod) + : cmHomeMethod(aCogMethod)); + writableMethod = ((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)); + (writableMethod->cmUsageCount = CMMaxUsageCount); + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = incrementUsageOfTargetIfLinkedSendmcpcignored(annotation, ((char *) mcpc), 0); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; +} + + /* Cogit>>#maxCogMethodAddress */ +usqInt +maxCogMethodAddress(void) +{ + return ((usqInt)(limitZony())); +} + + /* Cogit>>#maximumDistanceFromCodeZone: */ +static NoDbgRegParms sqInt +maximumDistanceFromCodeZone(sqInt anAddress) +{ + return (anAddress > codeBase + ? anAddress - codeBase + : limitAddress - anAddress); +} + + +/* Check that the header fields are consistent with the type. + Answer 0 if it is ok, otherwise answer a code for the error. */ + + /* Cogit>>#maybeFreeCogMethodDoesntLookKosher: */ +static NoDbgRegParms sqInt +maybeFreeCogMethodDoesntLookKosher(CogMethod *cogMethod) +{ + sqInt result; + + result = cogMethodDoesntLookKosher(cogMethod); + return (result == 2 + ? 0 + : result); +} + + /* Cogit>>#mclassCouldBeContext */ +static int +mclassCouldBeContext(void) +{ + if (receiverTags < 0) { + receiverTags = receiverTagBitsForMethod(methodObj); + } + return ((receiverTags & ((1U << (numTagBits())))) != 0); +} + + /* Cogit>>#mclassIsSmallInteger */ +static int +mclassIsSmallInteger(void) +{ + if (receiverTags < 0) { + receiverTags = receiverTagBitsForMethod(methodObj); + } + return (((receiverTags) & 7) == 1); +} + + +/* Answer the absolute machine code pc matching the zero-relative + bytecode pc of a backward branch in cogMethod, given the start + of the bytecodes for cogMethod's block or method object. */ + + /* Cogit>>#mcPCForBackwardBranch:startBcpc:in: */ +usqInt +mcPCForBackwardBranchstartBcpcin(sqInt bcpc, sqInt startbcpc, CogBlockMethod *cogMethod) +{ + sqInt aMethodObj; + sqInt annotation; + sqInt bcpcSqInt; + sqInt bsOffset; + sqInt byte; + BytecodeDescriptor *descriptor; + sqInt distance; + sqInt endbcpc; + CogMethod *homeMethod; + sqInt isBackwardBranch; + sqInt isInBlock; + sqInt latestContinuation; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt nExts; + sqInt nextBcpc; + sqInt result; + sqInt targetPC; + + /* begin mapFor:bcpc:performUntil:arg: */ + descriptor = ((BytecodeDescriptor *) 0); + latestContinuation = 0; + mapByte = 0; + nextBcpc = 0; + assert(((cogMethod->stackCheckOffset)) > 0); + mcpc = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + + /* The stack check maps to the start of the first bytecode, + the first bytecode being effectively after frame build. */ + result = findBackwardBranchIsBackwardBranchMcpcBcpcMatchingBcpc(null, 0 + ((((usqInt)(HasBytecodePC) << 1))), ((char *) mcpc), startbcpc, ((void *)bcpc)); + if (result) { + return result; + } + bcpcSqInt = startbcpc; + + /* In both CMMethod and CMBlock cases find the start of the map and + skip forward to the bytecode pc map entry for the stack check. */ + if (((cogMethod->cmType)) >= CMMethod) { + isInBlock = (cogMethod->cpicHasMNUCaseOrCMIsFullBlock); + homeMethod = ((CogMethod *) cogMethod); + assert(startbcpc == (startPCOfMethodHeader((homeMethod->methodHeader)))); + map = ((((usqInt)homeMethod)) + ((homeMethod->blockSize))) - 1; + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert((annotation == IsAbsPCReference) + || ((annotation == IsObjectReference) + || ((annotation == IsRelativeCall) + || (annotation == IsDisplacementX2N)))); + latestContinuation = startbcpc; + aMethodObj = (homeMethod->methodObject); + endbcpc = (numBytesOf(aMethodObj)) - 1; + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + + /* If the method has a primitive, skip it and the error code store, if any; + Logically. these come before the stack check and so must be ignored. */ + bcpcSqInt += deltaToSkipPrimAndErrorStoreInheader(aMethodObj, (homeMethod->methodHeader)); + } + else { + isInBlock = 1; + assert(bcpcSqInt == ((cogMethod->startpc))); + homeMethod = cmHomeMethod(cogMethod); + map = findMapLocationForMcpcinMethod((((usqInt)cogMethod)) + (sizeof(CogBlockMethod)), homeMethod); + assert(map != 0); + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert(((((usqInt)(annotation)) >> AnnotationShift) == HasBytecodePC) + || ((((usqInt)(annotation)) >> AnnotationShift) == IsDisplacementX2N)); + + /* fiducial */ + while (((annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift)) != HasBytecodePC) { + map -= 1; + } + + /* skip fiducial; i.e. the map entry for the pc immediately following the method header. */ + map -= 1; + aMethodObj = (homeMethod->methodObject); + bcpcSqInt = startbcpc - (/* blockCreationBytecodeSizeForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? AltBlockCreationBytecodeSize + : BlockCreationBytecodeSize)); + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + byte = (fetchByteofObject(bcpcSqInt, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + endbcpc = (bcpcSqInt + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpcSqInt, -1, aMethodObj) + : 0)); + bcpcSqInt = startbcpc; + } + nExts = 0; + enumeratingCogMethod = homeMethod; + + /* Now skip up through the bytecode pc map entry for the stack check. */ + while ((((usqInt)((byteAt((void *)(map))))) >> AnnotationShift) != HasBytecodePC) { + map -= 1; + } + map -= 1; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + mcpc += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + if (annotation >= HasBytecodePC) { + if ((annotation == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + while (1) { + byte = (fetchByteofObject(bcpcSqInt, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + if (isInBlock) { + if (bcpcSqInt >= endbcpc) { + return 0; + } + } + else { + if (((descriptor->isReturn)) + && (bcpcSqInt >= latestContinuation)) { + return 0; + } + if ((isBranch(descriptor)) + || ((descriptor->isBlockCreation))) { + /* begin latestContinuationPCFor:at:exts:in: */ + distance = ((descriptor->spanFunction))(descriptor, bcpcSqInt, nExts, aMethodObj); + targetPC = (bcpcSqInt + ((descriptor->numBytes))) + (((distance < 0) ? 0 : distance)); + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + } + } + nextBcpc = (bcpcSqInt + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpcSqInt, nExts, aMethodObj) + : 0)); + if (((descriptor->isMapped)) + || (isInBlock + && ((descriptor->isMappedInBlock)))) break; + bcpcSqInt = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + isBackwardBranch = (isBranch(descriptor)) + && ((/* begin isBackwardBranch:at:exts:in: */ + assert(((descriptor->spanFunction))), + (((descriptor->spanFunction))(descriptor, bcpcSqInt, nExts, aMethodObj)) < 0)); + result = findBackwardBranchIsBackwardBranchMcpcBcpcMatchingBcpc(descriptor, (isBackwardBranch + ? ((((usqInt)(annotation) << 1))) + 1 + : ((sqInt)((usqInt)(annotation) << 1))), ((char *) mcpc), (isBackwardBranch + ? bcpcSqInt - (2 * nExts) + : bcpcSqInt), ((void *)bcpc)); + if (result) { + return result; + } + bcpcSqInt = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + } + else { + assert(((((usqInt)(mapByte)) >> AnnotationShift) == IsDisplacementX2N) + || ((((usqInt)(mapByte)) >> AnnotationShift) == IsAnnotationExtension)); + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + + /* defensive; we exit on bcpc */ + return 0; +} + + +/* For the purposes of become: see if the two methods are similar, i.e. can + be safely becommed. + This is pretty strict. All literals and bytecodes must be identical. Only + trailer bytes and header + flags can differ. */ + + /* Cogit>>#method:hasSameCodeAs:checkPenultimate: */ +static NoDbgRegParms sqInt +methodhasSameCodeAscheckPenultimate(sqInt methodA, sqInt methodB, sqInt comparePenultimateLiteral) +{ + sqInt bi; + sqInt endPCA; + sqInt headerA; + sqInt headerB; + sqInt li; + sqInt numLitsA; + + headerA = methodHeaderOf(methodA); + headerB = methodHeaderOf(methodB); + numLitsA = literalCountOfMethodHeader(headerA); + endPCA = endPCOf(methodA); + if (((argumentCountOfMethodHeader(headerA)) != (argumentCountOfMethodHeader(headerB))) + || (((temporaryCountOfMethodHeader(headerA)) != (temporaryCountOfMethodHeader(headerB))) + || (((primitiveIndexOfMethodheader(methodA, headerA)) != (primitiveIndexOfMethodheader(methodB, headerB))) + || ((numLitsA != (literalCountOfMethodHeader(headerB))) + || (endPCA > (numBytesOf(methodB))))))) { + return 0; + } + for (li = 1; li < numLitsA; li += 1) { + if ((fetchPointerofObject(li, methodA)) != (fetchPointerofObject(li, methodB))) { + if ((li < (numLitsA - 1)) + || (comparePenultimateLiteral)) { + return 0; + } + } + } + for (bi = (startPCOfMethodHeader(headerA)); bi <= endPCA; bi += 1) { + if ((fetchByteofObject(bi, methodA)) != (fetchByteofObject(bi, methodB))) { + return 0; + } + } + return 1; +} + + /* Cogit>>#mnuOffset */ +sqInt +mnuOffset(void) +{ + return missOffset; +} + + /* Cogit>>#NativePopR: */ +static NoDbgRegParms AbstractInstruction * +gNativePopR(sqInt reg) +{ + return genoperand(NativePopR, reg); +} + + /* Cogit>>#NativePushR: */ +static NoDbgRegParms AbstractInstruction * +gNativePushR(sqInt reg) +{ + return genoperand(NativePushR, reg); +} + + /* Cogit>>#NativeRetN: */ +static NoDbgRegParms AbstractInstruction * +gNativeRetN(sqInt offset) +{ + return genoperand(NativeRetN, offset); +} + + /* Cogit>>#needsFrameIfImmutability: */ +static NoDbgRegParms sqInt +needsFrameIfImmutability(sqInt stackDelta) +{ + return IMMUTABILITY; +} + + /* Cogit>>#needsFrameIfInBlock: */ +static NoDbgRegParms sqInt +needsFrameIfInBlock(sqInt stackDelta) +{ + return inBlock > 0; +} + + +/* Marks inst var read generators. We only need a frame if + accessing an inst var of a context which needs read mediation + (is computed from the context's frame when married). Assumes + an extensible bytecode with nst var index in the 2nd byte. */ + + /* Cogit>>#needsFrameIfReadMediated: */ +static NoDbgRegParms sqInt +needsFrameIfReadMediated(sqInt stackDelta) +{ + return isReadMediatedContextInstVarIndex(byte1 + extA); +} + + /* Cogit>>#needsFrameNever: */ +static NoDbgRegParms sqInt +needsFrameNever(sqInt stackDelta) +{ + return 0; +} + + /* Cogit>>#noAssertMethodClassAssociationOf: */ +static NoDbgRegParms sqInt +noAssertMethodClassAssociationOf(sqInt methodPointer) +{ + return literalofMethod((literalCountOfMethodHeader(noAssertHeaderOf(methodPointer))) - 1, methodPointer); +} + + +/* Check that no method is maximally marked. A maximal mark is an indication + the method has been scanned to increase the usage count of its referent + methods. */ + + /* Cogit>>#noCogMethodsMaximallyMarked */ +static sqInt +noCogMethodsMaximallyMarked(void) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if ((!(((cogMethod->cmType)) == CMFree)) + && (((cogMethod->cmUsageCount)) == CMMaxUsageCount)) { + return 0; + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + return 1; +} + + +/* Answer if all targets in the PIC are in-use methods. */ + + /* Cogit>>#noTargetsFreeInClosedPIC: */ +static NoDbgRegParms int +noTargetsFreeInClosedPIC(CogMethod *cPIC) +{ + return !(cPICHasFreedTargets(cPIC)); +} + + /* Cogit>>#OrCq:R:R: */ +static NoDbgRegParms AbstractInstruction * +gOrCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *anInstruction; + + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(OrCqRR, quickConstant, srcReg, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return anInstruction; +} + + +/* Store the generated machine code, answering the last address */ + + /* Cogit>>#outputInstructionsAt: */ +static NoDbgRegParms sqInt +outputInstructionsAt(sqInt startAddress) +{ + sqInt absoluteAddress; + AbstractInstruction *abstractInstruction; + sqInt i; + sqInt j; + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + absoluteAddress = startAddress; + for (i = 0; i < opcodeIndex; i += 1) { + maybeBreakGeneratingInstructionWithIndex(i); + abstractInstruction = abstractInstructionAt(i); + assert(((abstractInstruction->address)) == absoluteAddress); + + /* begin outputMachineCodeAt: */ + for (j = 0; j < ((abstractInstruction->machineCodeSize)); j += 4) { + codeLong32Atput((void *)(absoluteAddress + j),((abstractInstruction->machineCode))[j / 4]); + } + absoluteAddress += (abstractInstruction->machineCodeSize); + } + return absoluteAddress; +} + + +/* Output instructions generated for one of the generated run-time routines, + a trampoline, etc + */ + + /* Cogit>>#outputInstructionsForGeneratedRuntimeAt: */ +static NoDbgRegParms sqInt +outputInstructionsForGeneratedRuntimeAt(sqInt startAddress) +{ + sqInt endAddress; + sqInt size; + + computeMaximumSizes(); + (methodLabel->address = startAddress); + size = generateInstructionsAt(startAddress); + endAddress = outputInstructionsAt(startAddress); + assert((startAddress + size) == endAddress); + methodZoneBase = alignUptoRoutineBoundary(endAddress); + stopsFromto(backEnd, endAddress, methodZoneBase - 1); + return startAddress; +} + + +/* Code entry closed PIC full or miss to an instance of a young class or to a + young target method. + Attempt to patch the send site to an open PIC. Answer if the attempt + succeeded; in fact it will + only return if the attempt failed. + The stack looks like: + receiver + args + sp=> sender return address */ + + /* Cogit>>#patchToOpenPICFor:numArgs:receiver: */ +sqInt +patchToOpenPICFornumArgsreceiver(sqInt selector, sqInt numArgs, sqInt receiver) +{ + sqInt extent; + CogMethod *oPIC; + sqInt outerReturn; + + outerReturn = stackTop(); + + /* See if an Open PIC is already available. */ + oPIC = openPICWithSelector(selector); + if (!oPIC) { + /* otherwise attempt to create an Open PIC. */ + oPIC = cogOpenPICSelectornumArgs(selector, numArgs); + if ((((((sqInt)oPIC)) >= MaxNegativeErrorCode) && ((((sqInt)oPIC)) <= -1))) { + /* For some reason the PIC couldn't be generated, most likely a lack of code memory. */ + if ((((sqInt)oPIC)) == InsufficientCodeSpace) { + callForCogCompiledCodeCompaction(); + } + return 0; + } + } + + /* Relink the send site to the pic. Reset the cache tag to the selector, for the + benefit of the cacheTag assert check in checkIfValidOopRef:pc:cogMethod: et al. */ + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + extent = rewriteInlineCacheAttagtarget(backEnd, outerReturn, inlineCacheValueForSelectorin(backEnd, selector, mframeHomeMethodExport()), (((sqInt)oPIC)) + cmEntryOffset); + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) ((((usqInt)outerReturn)) - extent)), (((usqInt)outerReturn)) - ((((usqInt)outerReturn)) - extent)); + sys_icache_invalidate(((void *) ((((usqInt)outerReturn)) - extent)), (((usqInt)outerReturn)) - ((((usqInt)outerReturn)) - extent)); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) ((((usqInt)outerReturn)) - extent)), ((((usqInt)outerReturn)) - ((((usqInt)outerReturn)) - extent)) + 1); +# else // __APPLE__ && __MACH__ || _WIN64 + ceFlushICache((((usqInt)outerReturn)) - extent, ((usqInt)outerReturn)); +# endif + + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) (((usqInt)oPIC))), ((((usqInt)oPIC)) + openPICSize) - (((usqInt)oPIC))); + sys_icache_invalidate(((void *) (((usqInt)oPIC))), ((((usqInt)oPIC)) + openPICSize) - (((usqInt)oPIC))); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) (((usqInt)oPIC))), (((((usqInt)oPIC)) + openPICSize) - (((usqInt)oPIC))) + 1); +# else // __APPLE__ && __MACH__ || _WIN64 + ceFlushICache(((usqInt)oPIC), (((usqInt)oPIC)) + openPICSize); +# endif + + + /* Jump into the oPIC at its entry */ + executeCogMethodfromLinkedSendWithReceiver(oPIC, receiver); + + /* NOTREACHED */ + return 1; +} + + +/* This value is used to decide between MNU processing + or interpretation in the closed PIC aborts. */ + + /* Cogit>>#picAbortDiscriminatorValue */ +static sqInt +picAbortDiscriminatorValue(void) +{ + return 0; +} + + +/* Answer the start of the abort sequence for invoking the interpreter in a + closed PIC. + */ + + /* Cogit>>#picInterpretAbortOffset */ +static sqInt +picInterpretAbortOffset(void) +{ + return (interpretOffset()) - ((pushLinkRegisterByteSize(backEnd)) + (callInstructionByteSize(backEnd))); +} + + +/* useful for debugging */ + + /* Cogit>>#printCogMethodFor: */ +void +printCogMethodFor(void *address) +{ + CogMethod *cogMethod; + + if ((cogMethod = methodFor(address))) { + printCogMethod(cogMethod); + } + else { + if (codeEntryFor(address)) { + print("trampoline "); + print(codeEntryNameFor(address)); + cr(); + } + else { + print("not a method"); + cr(); + } + } +} + + /* Cogit>>#printPCMapPairsFor: */ +#if VMInvestigations +void +printPCMapPairsFor(CogMethod *cogMethod) +{ + unsigned char annotation; + sqInt map; + unsigned char mapByte; + usqInt mcpc; + sqInt stackCheckPC; + const char *type; + usqInt value; + + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + stackCheckPC = (((sqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + if (annotation == IsAnnotationExtension) { + value = (mapByte & DisplacementMask) + IsSendCall; + } + else { + value = annotation; + mcpc += 4 /* codeGranularity */ * ((annotation + ? mapByte & DisplacementMask + : ((sqInt)((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift)))); + } + if (((value >= IsDirectedSuperSend) && (value <= IsDirectedSuperBindingSend))) { + if (value == IsDirectedSuperSend) { + type = "DirectedSuperSend"; + } + else { + type = "DirectedSuperBindingSend"; + } + } + else { + switch (value) { + case IsDisplacementX2N: + type = "DisplacementX2N"; + break; + case IsAnnotationExtension: + type = "AnnotationExtension"; + break; + case IsObjectReference: + type = "ObjectReference"; + break; + case IsAbsPCReference: + type = "AbsPCReference"; + break; + case HasBytecodePC: + if (mcpc == stackCheckPC) { + type = "StackCheck/BCPC"; + } + else { + type = "HasBytecodePC"; + } + break; + case IsRelativeCall: + type = "RelativeCall"; + break; + case IsSendCall: + type = "SendCall"; + break; + case IsSuperSend: + type = "SuperSend"; + break; + default: + type = null; + } + } + if (type) { + fprintf(getTranscript(), + "%" PRIxSQINT ": %02x %ld (%s) 0x%x @ %p\n", + map, + mapByte, + annotation, + type, + ((int) (mapByte & DisplacementMask)), + ((void *)mcpc)); + } + else { + fprintf(getTranscript(), + "%" PRIxSQINT ": %02x %ld (??? %d) 0x%x @ %p\n", + map, + mapByte, + annotation, + ((int) value), + ((int) (mapByte & DisplacementMask)), + ((void *)mcpc)); + } + map -= 1; + } +} +#endif /* VMInvestigations */ + + +/* useful for debugging */ + + /* Cogit>>#printTrampolineTable */ +void +printTrampolineTable(void) +{ + sqInt i; + + for (i = 0; i < trampolineTableIndex; i += 2) { + fprintf(getTranscript(), + "%p: %s\n", + ((void *)(trampolineAddresses[i + 1])), + ((char *)(trampolineAddresses[i]))); + } +} + + /* Cogit>>#processorHasDivQuoRemAndMClassIsSmallInteger */ +static sqInt +processorHasDivQuoRemAndMClassIsSmallInteger(void) +{ + return mclassIsSmallInteger(); +} + + /* Cogit>>#processorHasMultiplyAndMClassIsSmallInteger */ +static sqInt +processorHasMultiplyAndMClassIsSmallInteger(void) +{ + return mclassIsSmallInteger(); +} + + /* Cogit>>#recordGeneratedRunTime:address: */ +static NoDbgRegParms void +recordGeneratedRunTimeaddress(char *aString, sqInt address) +{ + assert((trampolineTableIndex + 2) <= (NumTrampolines * 2)); + trampolineAddresses[trampolineTableIndex] = aString; + trampolineAddresses[trampolineTableIndex + 1] = (((char *) address)); + + /* self printTrampolineTable */ + trampolineTableIndex += 2; +} + + +/* This one for C support code. */ + + /* Cogit>>#recordPrimTraceFunc */ +int +recordPrimTraceFunc(void) +{ + return recordPrimTrace(); +} + + /* Cogit>>#recordRunTimeObjectReferences */ +static void +recordRunTimeObjectReferences(void) +{ + sqInt i; + AbstractInstruction *instruction; + + for (i = 0; i < opcodeIndex; i += 1) { + instruction = abstractInstructionAt(i); + if (((instruction->annotation)) == IsObjectReference) { + assert(runtimeObjectRefIndex < NumObjRefsInRuntime); + assert(!hasYoungReferent); + if (hasYoungReferent) { + error("attempt to generate run-time routine containing young object reference. Cannot initialize Cogit run-time."); + } + objectReferencesInRuntime[runtimeObjectRefIndex] = (((usqInt)(/* mapEntryAddress */ + (((instruction->opcode)) == Literal + ? (instruction->address) + : ((instruction->address)) + ((instruction->machineCodeSize)))))); + runtimeObjectRefIndex += 1; + } + } +} + + +/* N.B. (self registerMaskFor: NoReg) = 0 */ + + /* Cogit>>#registerMaskFor: */ +static NoDbgRegParms sqInt +registerMaskFor(sqInt reg) +{ + return ((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)); +} + + /* Cogit>>#registerMaskFor:and: */ +static NoDbgRegParms sqInt +registerMaskForand(sqInt reg1, sqInt reg2) +{ + return (1ULL << reg1) | (1ULL << reg2); +} + + /* Cogit>>#relocateCallsAndSelfReferencesInMethod: */ +static NoDbgRegParms void +relocateCallsAndSelfReferencesInMethod(CogMethod *cogMethod) +{ + sqInt annotation; + sqInt callDelta; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqLong refDelta; + sqInt result; + + refDelta = (cogMethod->objectHeader); + callDelta = refDelta; + assert((isCMMethodEtAl(((CogBlockMethod *) cogMethod))) + || (isCMOpenPIC(((CogBlockMethod *) cogMethod)))); + assert((callTargetFromReturnAddress(backEnd, (((sqInt)cogMethod)) + missOffset)) == ((isCMMethodEtAl(((CogBlockMethod *) cogMethod)) + ? methodAbortTrampolineFor((cogMethod->cmNumArgs)) + : picAbortTrampolineFor((cogMethod->cmNumArgs))))); + relocateCallBeforeReturnPCby(backEnd, (((sqInt)cogMethod)) + missOffset, -callDelta); + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = relocateIfCallOrMethodReferencemcpcdelta(annotation, ((char *) mcpc), ((void *)refDelta)); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; +} + + /* Cogit>>#relocateCallsInClosedPIC: */ +static NoDbgRegParms void +relocateCallsInClosedPIC(CogMethod *cPIC) +{ + sqInt callDelta; + sqInt entryPoint; + sqInt i; + sqInt pc; + sqInt pcSqInt; + sqLong refDelta; + CogMethod *targetMethod; + + refDelta = (cPIC->objectHeader); + callDelta = refDelta; + assert((callTargetFromReturnAddress(backEnd, (((sqInt)cPIC)) + missOffset)) == (picAbortTrampolineFor((cPIC->cmNumArgs)))); + relocateCallBeforeReturnPCby(backEnd, (((sqInt)cPIC)) + missOffset, -callDelta); + pc = (((sqInt)cPIC)) + firstCPICCaseOffset; + for (i = 1; i <= ((cPIC->cPICNumCases)); i += 1) { + pc = addressOfEndOfCaseinCPIC(i, cPIC); + entryPoint = (jumpLongTargetBeforeFollowingAddress(backEnd, pc)); + if (/* containsAddress: */ + ((((usqInt)cPIC)) <= (((usqInt)entryPoint))) + && (((((usqInt)cPIC)) + ((cPIC->blockSize))) >= (((usqInt)entryPoint)))) { + /* Interpret/MNU */ + } + else { + targetMethod = ((CogMethod *) (entryPoint - cmNoCheckEntryOffset)); + assert(isCMMethodEtAl(((CogBlockMethod *) targetMethod))); + if (i == 1) { + relocateJumpLongBeforeFollowingAddressby(backEnd, pc, -(callDelta - ((targetMethod->objectHeader)))); + } + else { + relocateJumpLongConditionalBeforeFollowingAddressby(backEnd, pc, -(callDelta - ((targetMethod->objectHeader)))); + } + } + } + assert(((cPIC->cPICNumCases)) > 0); + + /* Finally relocate the load of the PIC and the jump to the overflow routine ceCPICMiss:receiver: */ + pcSqInt = (addressOfEndOfCaseinCPIC(2, cPIC)) + (loadPICLiteralByteSize(backEnd)); + + /* begin relocateMethodReferenceBeforeAddress:by: */ + assert((instructionIsADR(backEnd, instructionAt(backEnd, pcSqInt - 4))) + || (instructionIsADR(backEnd, instructionAt(backEnd, pcSqInt - 8)))); + relocateJumpLongBeforeFollowingAddressby(backEnd, (((sqInt)cPIC)) + cPICEndOfCodeOffset, -callDelta); +} + + +/* To placate the C static type system... */ + + /* Cogit>>#relocateIfCallOrMethodReference:mcpc:delta: */ +static NoDbgRegParms sqInt +relocateIfCallOrMethodReferencemcpcdelta(sqInt annotation, char *mcpc, void *refDeltaArg) +{ + sqInt callDelta; + sqInt entryPoint; + sqInt offsetSqInt; + sqInt refDelta; + sqInt *sendTable1; + CogMethod *targetMethod; + sqInt unlinkedRoutine; + + refDelta = ((sqInt) refDeltaArg); + callDelta = refDelta; + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint <= methodZoneBase) { + relocateCallBeforeReturnPCby(backEnd, ((sqInt)mcpc), -callDelta); + return 0; + } + + /* send is not linked; just relocate + It's a linked send; find which kind. */ + + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod = ((CogMethod *) (entryPoint - offsetSqInt)); + if (!(((targetMethod->cmType)) == CMFree)) { + relocateCallBeforeReturnPCby(backEnd, ((sqInt)mcpc), -(callDelta - ((targetMethod->objectHeader)))); + + /* See comment in planCompaction */ + return 0; + } + + /* send target not freed; just relocate. + Target was freed; map back to an unlinked send; but include this method's reocation */ + unlinkedRoutine = sendTable1[((((targetMethod->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod->cmNumArgs)) : (NumSendTrampolines - 1))]; + unlinkedRoutine -= callDelta; + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod->selector), enumeratingCogMethod), unlinkedRoutine); + return 0; + } + if (annotation == IsRelativeCall) { + relocateCallBeforeReturnPCby(backEnd, ((sqInt)mcpc), -callDelta); + return 0; + } + if (annotation == IsAbsPCReference) { + /* begin relocateMethodReferenceBeforeAddress:by: */ + assert((instructionIsADR(backEnd, instructionAt(backEnd, (((sqInt)mcpc)) - 4))) + || (instructionIsADR(backEnd, instructionAt(backEnd, (((sqInt)mcpc)) - 8)))); + } + return 0; +} + + /* Cogit>>#remapIfObjectRef:pc:hasYoung: */ +static NoDbgRegParms sqInt +remapIfObjectRefpchasYoung(sqInt annotation, char *mcpc, void *hasYoungPtr) +{ + unsigned int cacheTag1; + sqInt entryPointSqInt; + unsigned int instr; + sqInt literal; + sqInt mappedLiteral; + sqInt offsetSqInt; + sqInt pc; + sqInt *sendTable1; + CogMethod *targetMethod1; + + if (annotation == IsObjectReference) { + literal = longAt((void *)(((usqInt)mcpc))); + if (couldBeObject(literal)) { + mappedLiteral = remapObject(literal); + if (literal != mappedLiteral) { + /* begin setCodeModified */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + if (!codeModified) { + codeModified = 1; + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + } + } + else { + codeModified = 1; + } + + /* begin storeLiteral:atAnnotatedAddress:using: */ + codeLongAtput((void *)(((usqInt)mcpc)),mappedLiteral); + } + if ((hasYoungPtr != 0) + && (isYoung(mappedLiteral))) { + (*((sqInt *) hasYoungPtr) = 1); + } + } + } + if (annotation >= IsSendCall) { + /* begin entryCacheTagAndCouldBeObjectAt:annotation:into: */ + /* begin inlineCacheTagAt: */ + /* begin pcRelativeAddressAt: */ + instr = long32At((void *)(((usqInt)((((sqInt)mcpc)) - 8)))); + + /* cogit processor disassembleInstructionAt: mcpc In: objectMemory memory */ + assert(instructionIsPCRelativeLoad(backEnd, instr)); + pc = (((((usqInt)(instr)) >> 3) & (0x3FFFFC)) - ((((usqInt)(instr)) >> 2) & (0x200000))) + (((usqInt)((((sqInt)mcpc)) - 8))); + cacheTag1 = long32At((void *)(pc)); + entryPointSqInt = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + + /* in-line cache tags are the selectors of sends if sends are unlinked, + the selectors of super sends (entry offset = cmNoCheckEntryOffset), + the selectors of open PIC sends (entry offset = cmEntryOffset, target is an Open PIC) + or in-line cache tags (classes, class indices, immediate bit patterns, etc). + Note that selectors can be immediate so there is no guarantee that they + are markable/remappable objects. Writing the block invocation three times + allows Slang to inline tagCouldBeObject tests. */ + if (hasYoungPtr) { + /* Since the unlinking routines may rewrite the cacheTag to the send's selector, and + since they don't have the cogMethod to hand and can't add it to youngReferrers, + the method must remain in youngReferrers if the targetMethod's selector is young. */ + if (entryPointSqInt > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPointSqInt - offsetSqInt)); + if (isYoung((targetMethod1->selector))) { + (*((sqInt *) hasYoungPtr) = 1); + } + } + } + } + return 0; +} + + +/* Remap a potential object reference from a closed PIC. + This may be an object reference, an inline cache tag or null. + Answer if the updated literal is young. + mcpc is the address of the next instruction following either + the load of the method literal or the compare of the class tag. */ + + /* Cogit>>#remapMaybeObjRefInClosedPICAt: */ +static NoDbgRegParms sqInt +remapMaybeObjRefInClosedPICAt(sqInt mcpc) +{ + sqInt object; + sqInt subject; + + object = literalBeforeFollowingAddress(backEnd, mcpc); + if (!(couldBeObject(object))) { + return 0; + } + subject = remapOop(object); + if (object != subject) { + /* begin setCodeModified */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + if (!codeModified) { + codeModified = 1; + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + } + } + else { + codeModified = 1; + } + storeLiteralbeforeFollowingAddress(backEnd, subject, mcpc); + } + return isYoungObject(subject); +} + + +/* Rewrite the three values involved in a CPIC case. Used by the initialize & + extend CPICs. + c.f. expectedClosedPICPrototype: */ +/* write the obj ref/operand via the second ldr */ + + /* Cogit>>#rewriteCPICCaseAt:tag:objRef:target: */ +static NoDbgRegParms void +rewriteCPICCaseAttagobjReftarget(sqInt followingAddress, sqInt newTag, sqInt newObjRef, sqInt newTarget) +{ + sqInt classTagPC; + sqInt methodObjPC; + + methodObjPC = (followingAddress - (jumpLongConditionalByteSize(backEnd))) - (cmpC32RTempByteSize(backEnd)); + storeLiteralbeforeFollowingAddress(backEnd, newObjRef, methodObjPC); + classTagPC = followingAddress - (jumpLongConditionalByteSize(backEnd)); + + /* rewite the tag via the first ldr */ + storeLiteral32beforeFollowingAddress(backEnd, newTag, classTagPC); + + /* write the jump address for the new target address */ + rewriteConditionalJumpLongAttarget(backEnd, followingAddress, newTarget); +} + + +/* destReg := fromReg - subReg */ + + /* Cogit>>#SubR:R:R: */ +static NoDbgRegParms AbstractInstruction * +gSubRRR(sqInt subReg, sqInt fromReg, sqInt destReg) +{ + return genoperandoperandoperand(SubRRR, subReg, fromReg, destReg); +} + + +/* Answer the number of clean blocks found in the literal frame */ + + /* Cogit>>#scanForCleanBlocks */ +static sqInt +scanForCleanBlocks(void) +{ + sqInt i; + sqInt lit; + sqInt numCleanBlocks; + sqInt toDoLimit; + + numCleanBlocks = 0; + toDoLimit = literalCountOf(methodObj); + for (i = 1; i <= toDoLimit; i += 1) { + lit = fetchPointerofObject(i, methodObj); + if (startPCOrNilOfLiteralin(lit, methodObj)) { + numCleanBlocks += 1; + } + } + return numCleanBlocks; +} + + +/* If a method is compiled to machine code via a block entry it won't have a + selector. A subsequent send can find the method and hence fill in the + selector. + */ +/* self disassembleMethod: cogMethod */ + + /* Cogit>>#setSelectorOf:to: */ +void +setSelectorOfto(CogMethod *cogMethod, sqInt aSelectorOop) +{ + compilationBreakpointisMNUCase(aSelectorOop, 0); + assert(isCMMethodEtAl(((CogBlockMethod *) cogMethod))); + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + ((((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)))->selector = aSelectorOop); + if (isYoung(aSelectorOop)) { + ensureInYoungReferrers(cogMethod); + } + + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +} + + /* Cogit>>#spanForCleanBlockStartingAt: */ +static NoDbgRegParms sqInt +spanForCleanBlockStartingAt(sqInt startPC) +{ + BytecodeDescriptor *descriptor; + sqInt end; + sqInt pc; + + pc = startPC; + end = numBytesOf(methodObj); + while (pc <= end) { + /* begin generatorForPC: */ + descriptor = generatorAt(bytecodeSetOffset + (fetchByteofObject(pc, methodObj))); + pc += (descriptor->numBytes); + if ((descriptor->isReturn)) { + return pc - startPC; + } + } + error("couldn't locate end of clean block"); + return 0; +} + + +/* Set up things conveniently for stringForEntryPoint:existingImports:limit: */ + + /* Cogit>>#storeCogMethodDataImports:pc:pointer: */ +#if VMInvestigations +static NoDbgRegParms sqInt +storeCogMethodDataImportspcpointer(sqInt annotation, char *mcpc, usqInt **importsPtrPtr) +{ + unsigned int cacheTag1; + sqInt entryPointSqInt; + sqInt import; + usqInt *importsPtr; + unsigned int instr; + sqInt offsetSqInt; + sqInt pc; + sqInt *sendTable1; + CogMethod *targetMethod1; + + primitiveIndex = (methodHeaderHasPrimitive((enumeratingCogMethod->methodHeader)) + ? primitiveIndexOfMethodheader((enumeratingCogMethod->methodObject), (enumeratingCogMethod->methodHeader)) + : 0); + importsPtr = importsPtrPtr[0]; + if (annotation == IsObjectReference) { + import = longAt((void *)(((usqInt)mcpc))); + importsPtr[0] = ((((usqInt)(mcpc - (((char *)enumeratingCogMethod))) << 3) | 1)); + importsPtr[1] = (positive64BitIntegerFor(import)); + importsPtr[2] = import; + importsPtr += 3; + } + if (annotation == IsRelativeCall) { + if ((import = stringForEntryPointexistingImportslimit(((char *)(callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)))), ((sqInt)(importsPtrPtr[1])), importsPtr))) { + importsPtr[0] = ((((usqInt)(mcpc - (((char *)enumeratingCogMethod))) << 3) | 1)); + importsPtr[1] = (positive64BitIntegerFor(callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)))); + importsPtr[2] = import; + } + else { + return PrimErrNoMemory; + } + importsPtr += 3; + } + if (annotation >= IsSendCall) { + /* begin entryCacheTagAndCouldBeObjectAt:annotation:into: */ + /* begin inlineCacheTagAt: */ + /* begin pcRelativeAddressAt: */ + instr = long32At((void *)(((usqInt)((((sqInt)mcpc)) - 8)))); + + /* cogit processor disassembleInstructionAt: mcpc In: objectMemory memory */ + assert(instructionIsPCRelativeLoad(backEnd, instr)); + pc = (((((usqInt)(instr)) >> 3) & (0x3FFFFC)) - ((((usqInt)(instr)) >> 2) & (0x200000))) + (((usqInt)((((sqInt)mcpc)) - 8))); + cacheTag1 = long32At((void *)(pc)); + entryPointSqInt = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + + /* in-line cache tags are the selectors of sends if sends are unlinked, + the selectors of super sends (entry offset = cmNoCheckEntryOffset), + the selectors of open PIC sends (entry offset = cmEntryOffset, target is an Open PIC) + or in-line cache tags (classes, class indices, immediate bit patterns, etc). + Note that selectors can be immediate so there is no guarantee that they + are markable/remappable objects. Writing the block invocation three times + allows Slang to inline tagCouldBeObject tests. */ + if (entryPointSqInt > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPointSqInt - offsetSqInt)); + import = (((targetMethod1->cmType)) >= CMMethod + ? (targetMethod1->methodObject) + : (importsPtrPtr[2] + ? (((targetMethod1->cmType)) == CMClosedPIC + ? investigationArrayForClosedPIC(targetMethod1) + : (assert(isCMOpenPIC(((CogBlockMethod *) targetMethod1))), + (targetMethod1->selector))) + : (targetMethod1->selector))); + } + else { + import = stringForEntryPointexistingImportslimit(((char *)entryPointSqInt), ((sqInt)(importsPtrPtr[1])), importsPtr); + } + + /* send is linked; store target method or Array of selector and cases for Closed PICs or selector for Open PICs + send is unlinked; store a string naming the trampoline, and try not to duplicate strings. */ + if (!import) { + return PrimErrNoMemory; + } + importsPtr[0] = ((((usqInt)(mcpc - (((char *)enumeratingCogMethod))) << 3) | 1)); + importsPtr[1] = (positive64BitIntegerFor(entryPointSqInt)); + importsPtr[2] = import; + importsPtr += 3; + } + importsPtrPtr[0] = importsPtr; + return 0; +} +#endif /* VMInvestigations */ + + +/* Search for an existing string for the entryPoint in imports up to + nextStoreLocation. If not found answer a new string. */ + + /* Cogit>>#stringForEntryPoint:existingImports:limit: */ +#if VMInvestigations +static NoDbgRegParms sqInt +stringForEntryPointexistingImportslimit(char *entryPoint, sqInt imports, sqInt *nextStoreLocation) +{ + char buffer[32]; + char *entryPointName; + sqInt import; + sqInt len; + sqInt *ptr; + + + /* If an external primitive is called on-stack then it may be reachable by a relative call. + Handle that here by answering the first literal, which holds the external primitive spec. */ + import = 0; + if (primitiveIndex > 0) { + /* This is a special case in Spur */ + if (entryPoint == (((char *)ceNewHashOf))) { + return stringForCString("ceNewHashOf:"); + } + if (entryPoint == (((char *)checkForAndFollowForwardedPrimitiveState))) { + return stringForCString("checkForAndFollowForwardedPrimitiveState"); + } + if (entryPoint == (((char *)ceCheckAndMaybeRetryPrimitive))) { + return stringForCString("ceCheckAndMaybeRetryPrimitive:"); + } + if (entryPoint == (((char *)(functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto((enumeratingCogMethod->methodObject), primitiveIndex, null))))) { + /* Answer the full external spec for external primitives. */ + if (primitiveIndex == PrimCallIsExternalCall) { + return fetchPointerofObject(1, (enumeratingCogMethod->methodObject)); + } + entryPointName = buffer; + snprintf(buffer, sizeof(buffer), "primitive #%ld", ((long)primitiveIndex)); + return stringForCString(entryPointName); + } + } + + /* Normally relative calls call trampolines. So search the trampoline table */ + assert((codeEntryFor(entryPoint)) == entryPoint); + entryPointName = codeEntryNameFor(entryPoint); + len = strlen(entryPointName); + ptr = (((sqInt *) (firstIndexableField(imports)))) + 2; + while ((ptr < nextStoreLocation) + && (!import)) { + if ((isBytes(ptr[0])) + && (((numBytesOf(ptr[0])) == len) + && ((strncmp(entryPointName, ((char *) (firstIndexableField(ptr[0]))), len)) == 0))) { + import = ptr[0]; + } + ptr += 3; + } + return (!(import) + ? stringForCString(entryPointName) + : import); +} +#endif /* VMInvestigations */ + + +/* Answer a fake value for the method oop in other than the first case in the + PIC prototype. + Since we use MoveUniqueCw:R: it must not be confused with a + method-relative address. + */ + + /* Cogit>>#subsequentPrototypeMethodOop */ +static sqInt +subsequentPrototypeMethodOop(void) +{ + return (/* addressIsInCurrentCompilation: */ + ((((usqInt)0xBADA550)) >= ((methodLabel->address))) + && ((((usqInt)0xBADA550)) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize)))) + ? 0xDEADEAD + : 0xBADA550); +} + + /* Cogit>>#TstCq:R: */ +static NoDbgRegParms AbstractInstruction * +gTstCqR(sqInt quickConstant, sqInt reg) +{ + AbstractInstruction *anInstruction; + + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(TstCqR, quickConstant, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return anInstruction; +} + + /* Cogit>>#traceLinkedSendOffset */ +sqInt +traceLinkedSendOffset(void) +{ + return (cmNoCheckEntryOffset + (callFullInstructionByteSize(backEnd))) + (pushLinkRegisterByteSize(backEnd)); +} + + /* Cogit>>#trampolineName:numArgs: */ +static NoDbgRegParms char * +trampolineNamenumArgs(char *routinePrefix, sqInt numArgs) +{ + char *theString; + + /* begin trampolineName:numArgs:limit: */ + theString = malloc((strlen(routinePrefix)) + 6); + sprintf(theString, "%s%cArgs", routinePrefix, ((((int) numArgs)) <= (NumSendTrampolines - 2) + ? '0' + (((int) numArgs)) + : 'N')); + return theString; +} + + +/* Malloc a string with the contents for the trampoline table */ + + /* Cogit>>#trampolineName:numArgs:limit: */ +static NoDbgRegParms char * +trampolineNamenumArgslimit(char *routinePrefix, int numArgs, sqInt argsLimit) +{ + char *theString; + + theString = malloc((strlen(routinePrefix)) + 6); + sprintf(theString, "%s%cArgs", routinePrefix, (numArgs <= argsLimit + ? '0' + numArgs + : 'N')); + return theString; +} + + /* Cogit>>#trampolineName:numRegArgs: */ +static NoDbgRegParms char * +trampolineNamenumRegArgs(char *routinePrefix, sqInt numArgs) +{ + char *theString; + + /* begin trampolineName:numArgs:limit: */ + theString = malloc((strlen(routinePrefix)) + 6); + sprintf(theString, "%s%cArgs", routinePrefix, ((((int) numArgs)) <= (numRegArgs()) + ? '0' + (((int) numArgs)) + : 'N')); + return theString; +} + + /* Cogit>>#unflagBecomeFlaggedMethods */ +void +unflagBecomeFlaggedMethods(void) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) == CMMethodFlaggedForBecome) { + ((((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)))->cmType = CMMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } +} + + /* Cogit>>#unknownBytecode */ +static sqInt +unknownBytecode(void) +{ + return EncounteredUnknownBytecode; +} + + +/* Unlink all sends in cog methods. */ + + /* Cogit>>#unlinkAllSends */ +void +unlinkAllSends(void) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt endAddress; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + + if (!methodZoneBase) { + return; + } + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + cogMethod = ((CogMethod *) methodZoneBase); + voidOpenPICList(); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = unlinkIfLinkedSendpcignored(annotation, ((char *) mcpc), 0); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + else { + if (!(((cogMethod->cmType)) == CMFree)) { + freeMethod(cogMethod); + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + + /* After updating inline caches we need to flush the icache. */ + endAddress = freeStart(); + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) (((usqInt)methodZoneBase))), endAddress - (((usqInt)methodZoneBase))); + sys_icache_invalidate(((void *) (((usqInt)methodZoneBase))), endAddress - (((usqInt)methodZoneBase))); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) (((usqInt)methodZoneBase))), (endAddress - (((usqInt)methodZoneBase))) + 1); +# else // __APPLE__ && __MACH__ || _WIN64 + ceFlushICache(((usqInt)methodZoneBase), endAddress); +# endif +} + + +/* To placate the C static type system... */ + + /* Cogit>>#unlinkIfFreeOrLinkedSend:pc:of: */ +static NoDbgRegParms sqInt +unlinkIfFreeOrLinkedSendpcof(sqInt annotation, char *mcpc, void *theSelector) +{ + sqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + sqInt unlinkedRoutine; + + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if ((((targetMethod1->cmType)) == CMFree) + || (((targetMethod1->selector)) == (((sqInt) theSelector)))) { + /* begin unlinkSendAt:targetMethod:sendTable: */ + unlinkedRoutine = sendTable1[((((targetMethod1->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod1->cmNumArgs)) : (NumSendTrampolines - 1))]; + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + if (!codeModified) { + codeModified = 1; + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + } + } + else { + codeModified = 1; + } + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod1->selector), enumeratingCogMethod), unlinkedRoutine); + } + } + } + return 0; +} + + /* Cogit>>#unlinkIfInvalidClassSend:pc:ignored: */ +static NoDbgRegParms sqInt +unlinkIfInvalidClassSendpcignored(sqInt annotation, char *mcpc, sqInt superfluity) +{ + sqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + sqInt unlinkedRoutine; + + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (!((/* annotationIsForUncheckedEntryPoint: */ + (annotation == IsSuperSend) + || (((annotation >= IsDirectedSuperSend) && (annotation <= IsDirectedSuperBindingSend)))) + || (((targetMethod1->cmType)) == CMOpenPIC))) { + if (!(isValidClassTag(inlineCacheTagAt(backEnd, ((sqInt)mcpc))))) { + /* begin unlinkSendAt:targetMethod:sendTable: */ + unlinkedRoutine = sendTable1[((((targetMethod1->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod1->cmNumArgs)) : (NumSendTrampolines - 1))]; + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + if (!codeModified) { + codeModified = 1; + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + } + } + else { + codeModified = 1; + } + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod1->selector), enumeratingCogMethod), unlinkedRoutine); + } + } + } + } + return 0; +} + + /* Cogit>>#unlinkIfLinkedSendToFree:pc:ignored: */ +static NoDbgRegParms sqInt +unlinkIfLinkedSendToFreepcignored(sqInt annotation, char *mcpc, sqInt superfluity) +{ + sqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + sqInt unlinkedRoutine; + + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (((targetMethod1->cmType)) == CMFree) { + /* begin unlinkSendAt:targetMethod:sendTable: */ + unlinkedRoutine = sendTable1[((((targetMethod1->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod1->cmNumArgs)) : (NumSendTrampolines - 1))]; + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + if (!codeModified) { + codeModified = 1; + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + } + } + else { + codeModified = 1; + } + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod1->selector), enumeratingCogMethod), unlinkedRoutine); + } + } + } + return 0; +} + + /* Cogit>>#unlinkIfLinkedSend:pc:if: */ +static NoDbgRegParms sqInt +unlinkIfLinkedSendpcif(sqInt annotation, char *mcpc, sqInt (*criterion)(CogMethod *)) +{ + sqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + sqInt unlinkedRoutine; + + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (criterion(targetMethod1)) { + /* begin unlinkSendAt:targetMethod:sendTable: */ + unlinkedRoutine = sendTable1[((((targetMethod1->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod1->cmNumArgs)) : (NumSendTrampolines - 1))]; + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + if (!codeModified) { + codeModified = 1; + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + } + } + else { + codeModified = 1; + } + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod1->selector), enumeratingCogMethod), unlinkedRoutine); + } + } + } + return 0; +} + + /* Cogit>>#unlinkIfLinkedSend:pc:ignored: */ +static NoDbgRegParms sqInt +unlinkIfLinkedSendpcignored(sqInt annotation, char *mcpc, sqInt superfluity) +{ + sqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + sqInt unlinkedRoutine; + + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + + /* begin unlinkSendAt:targetMethod:sendTable: */ + unlinkedRoutine = sendTable1[((((targetMethod1->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod1->cmNumArgs)) : (NumSendTrampolines - 1))]; + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + if (!codeModified) { + codeModified = 1; + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + } + } + else { + codeModified = 1; + } + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod1->selector), enumeratingCogMethod), unlinkedRoutine); + } + } + return 0; +} + + /* Cogit>>#unlinkIfLinkedSend:pc:to: */ +static NoDbgRegParms sqInt +unlinkIfLinkedSendpcto(sqInt annotation, char *mcpc, CogMethod *theCogMethod) +{ + sqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + sqInt unlinkedRoutine; + + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (targetMethod1 == theCogMethod) { + /* begin unlinkSendAt:targetMethod:sendTable: */ + unlinkedRoutine = sendTable1[((((targetMethod1->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod1->cmNumArgs)) : (NumSendTrampolines - 1))]; + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + if (!codeModified) { + codeModified = 1; + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + } + } + else { + codeModified = 1; + } + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod1->selector), enumeratingCogMethod), unlinkedRoutine); + } + } + } + return 0; +} + + +/* Unlink all sends in cog methods whose class tag is that of a forwarded + class. + */ + + /* Cogit>>#unlinkSendsLinkedForInvalidClasses */ +void +unlinkSendsLinkedForInvalidClasses(void) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt endAddress; + sqInt freedPIC; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + + if (!methodZoneBase) { + return; + } + cogMethod = ((CogMethod *) methodZoneBase); + codeModified = (freedPIC = 0); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = unlinkIfInvalidClassSendpcignored(annotation, ((char *) mcpc), 0); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + else { + if ((((cogMethod->cmType)) == CMClosedPIC) + && (cPICHasForwardedClass(cogMethod))) { + freeMethod(cogMethod); + freedPIC = 1; + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if (freedPIC) { + unlinkSendsToFree(); + } + else { + if (codeModified) { + endAddress = freeStart(); + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) (((usqInt)methodZoneBase))), endAddress - (((usqInt)methodZoneBase))); + sys_icache_invalidate(((void *) (((usqInt)methodZoneBase))), endAddress - (((usqInt)methodZoneBase))); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) (((usqInt)methodZoneBase))), (endAddress - (((usqInt)methodZoneBase))) + 1); +# else // __APPLE__ && __MACH__ || _WIN64 + ceFlushICache(((usqInt)methodZoneBase), endAddress); +# endif + + } + } +} + + +/* Unlink all sends in cog methods. Free all PICs with the selector, or + Closed PICs with an + MNU case if isMNUSelector. First check if any method actually has the + selector; if not + there can't be any linked send to it. This routine (including descendents) + is performance + critical. It contributes perhaps 30% of entire execution time in Compiler + recompileAll. */ + + /* Cogit>>#unlinkSendsOf:isMNUSelector: */ +void +unlinkSendsOfisMNUSelector(sqInt selector, sqInt isMNUSelector) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt endAddress; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt mustScanAndUnlink; + sqInt result; + + if (!methodZoneBase) { + return; + } + cogMethod = ((CogMethod *) methodZoneBase); + mustScanAndUnlink = 0; + if (isMNUSelector) { + while (cogMethod < (limitZony())) { + if (!(((cogMethod->cmType)) == CMFree)) { + if (/* cpicHasMNUCase */ + ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + && (((cogMethod->cmType)) == CMClosedPIC)) { + assert(isCMClosedPIC(((CogBlockMethod *) cogMethod))); + freeMethod(cogMethod); + mustScanAndUnlink = 1; + } + else { + if (((cogMethod->selector)) == selector) { + mustScanAndUnlink = 1; + if (/* isCMAnyPIC */ + (((cogMethod->cmType)) <= CMOpenPIC) + && (((cogMethod->cmType)) >= CMClosedPIC)) { + freeMethod(cogMethod); + } + } + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + } + else { + while (cogMethod < (limitZony())) { + if ((!(((cogMethod->cmType)) == CMFree)) + && (((cogMethod->selector)) == selector)) { + mustScanAndUnlink = 1; + if (/* isCMAnyPIC */ + (((cogMethod->cmType)) <= CMOpenPIC) + && (((cogMethod->cmType)) >= CMClosedPIC)) { + freeMethod(cogMethod); + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + } + if (!mustScanAndUnlink) { + return; + } + codeModified = 0; + voidOpenPICList(); + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = unlinkIfFreeOrLinkedSendpcof(annotation, ((char *) mcpc), ((CogMethod *) selector)); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + else { + if (((cogMethod->cmType)) == CMOpenPIC) { + /* begin setCodeModified */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + if (!codeModified) { + codeModified = 1; + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + } + } + else { + codeModified = 1; + } + addToOpenPICList(((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta))); + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if (codeModified) { + endAddress = freeStart(); + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) (((usqInt)methodZoneBase))), endAddress - (((usqInt)methodZoneBase))); + sys_icache_invalidate(((void *) (((usqInt)methodZoneBase))), endAddress - (((usqInt)methodZoneBase))); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) (((usqInt)methodZoneBase))), (endAddress - (((usqInt)methodZoneBase))) + 1); +# else // __APPLE__ && __MACH__ || _WIN64 + ceFlushICache(((usqInt)methodZoneBase), endAddress); +# endif + + } + + /* After possibly updating inline caches we need to flush the icache. + And ensure code zone is executable. May merely have freed methods... */ + + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +} + + +/* Unlink all sends in cog methods to free methods and/or pics. */ + + /* Cogit>>#unlinkSendsToFree */ +static void +unlinkSendsToFree(void) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt endAddress; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + + if (!methodZoneBase) { + return; + } + codeModified = 0; + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = unlinkIfLinkedSendToFreepcignored(annotation, ((char *) mcpc), 0); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + else { + if (((cogMethod->cmType)) == CMClosedPIC) { + assert(noTargetsFreeInClosedPIC(cogMethod)); + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if (codeModified) { + endAddress = freeStart(); + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) (((usqInt)methodZoneBase))), endAddress - (((usqInt)methodZoneBase))); + sys_icache_invalidate(((void *) (((usqInt)methodZoneBase))), endAddress - (((usqInt)methodZoneBase))); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) (((usqInt)methodZoneBase))), (endAddress - (((usqInt)methodZoneBase))) + 1); +# else // __APPLE__ && __MACH__ || _WIN64 + ceFlushICache(((usqInt)methodZoneBase), endAddress); +# endif + + } +} + + +/* Unlink all sends in cog methods to methods with a machine code + primitive, and free machine code primitive methods if freeIfTrue. + To avoid having to scan PICs, free any and all PICs */ + + /* Cogit>>#unlinkSendsToMethodsSuchThat:AndFreeIf: */ +void +unlinkSendsToMethodsSuchThatAndFreeIf(sqInt (*criterion)(CogMethod *), sqInt freeIfTrue) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt endAddress; + sqInt freedSomething; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + + if (!methodZoneBase) { + return; + } + codeModified = (freedSomething = 0); + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + if (freeIfTrue + && (criterion(cogMethod))) { + freeMethod(cogMethod); + freedSomething = 1; + } + else { + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = unlinkIfLinkedSendpcif(annotation, ((char *) mcpc), ((void *)criterion)); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + } + else { + if (((cogMethod->cmType)) == CMClosedPIC) { + freeMethod(cogMethod); + freedSomething = 1; + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if (freedSomething) { + unlinkSendsToFree(); + } + else { + if (codeModified) { + endAddress = freeStart(); + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) (((usqInt)methodZoneBase))), endAddress - (((usqInt)methodZoneBase))); + sys_icache_invalidate(((void *) (((usqInt)methodZoneBase))), endAddress - (((usqInt)methodZoneBase))); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) (((usqInt)methodZoneBase))), (endAddress - (((usqInt)methodZoneBase))) + 1); +# else // __APPLE__ && __MACH__ || _WIN64 + ceFlushICache(((usqInt)methodZoneBase), endAddress); +# endif + + } + } + + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +} + + +/* Unlink all sends in cog methods to a particular target method. + If targetMethodObject isn't actually a method (perhaps being + used via invokeAsMethod) then there's nothing to do. */ + + /* Cogit>>#unlinkSendsTo:andFreeIf: */ +void +unlinkSendsToandFreeIf(sqInt targetMethodObject, sqInt freeIfTrue) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt endAddress; + sqInt freedPIC; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + CogMethod *targetMethod; + + if (!((isOopCompiledMethod(targetMethodObject)) + && (methodHasCogMethod(targetMethodObject)))) { + return; + } + targetMethod = cogMethodOf(targetMethodObject); + if (!methodZoneBase) { + return; + } + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + codeModified = (freedPIC = 0); + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = unlinkIfLinkedSendpcto(annotation, ((char *) mcpc), targetMethod); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + else { + if ((((cogMethod->cmType)) == CMClosedPIC) + && (cPICHasTarget(cogMethod, targetMethod))) { + freeMethod(cogMethod); + freedPIC = 1; + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if (freeIfTrue) { + freeMethod(targetMethod); + } + if (freedPIC) { + unlinkSendsToFree(); + } + else { + if (codeModified) { + endAddress = freeStart(); + + /* begin flushICacheFrom:to: */ + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +# if __APPLE__ && __MACH__ + sys_dcache_flush(((void *) (((usqInt)methodZoneBase))), endAddress - (((usqInt)methodZoneBase))); + sys_icache_invalidate(((void *) (((usqInt)methodZoneBase))), endAddress - (((usqInt)methodZoneBase))); +# elif _WIN64 + FlushInstructionCache(Win32ProcessHandle, ((void *) (((usqInt)methodZoneBase))), (endAddress - (((usqInt)methodZoneBase))) + 1); +# else // __APPLE__ && __MACH__ || _WIN64 + ceFlushICache(((usqInt)methodZoneBase), endAddress); +# endif + + } + } + + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +} + + /* Cogit>>#v3:Block:Code:Size: */ +static NoDbgRegParms sqInt +v3BlockCodeSize(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + assert(nExts <= 0); + return ((((usqInt)((fetchByteofObject(pc + 2, aMethodObj))) << 8))) + (fetchByteofObject(pc + 3, aMethodObj)); +} + + +/* Answer the distance of a two byte forward long jump. */ + + /* Cogit>>#v3:LongForward:Branch:Distance: */ +static NoDbgRegParms sqInt +v3LongForwardBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + assert(nExts == 0); + return ((((fetchByteofObject(pc, aMethodObj)) & 3) << 8)) + (fetchByteofObject(pc + 1, aMethodObj)); +} + + +/* Answer the distance of a two byte forward long jump. */ + + /* Cogit>>#v3:Long:Branch:Distance: */ +static NoDbgRegParms sqInt +v3LongBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + assert(nExts == 0); + return ((((usqInt)((((fetchByteofObject(pc, aMethodObj)) & 7) - 4)) << 8))) + (fetchByteofObject(pc + 1, aMethodObj)); +} + + +/* N.B. This serves for both BlueBook/V3 and V4 short jumps. */ + + /* Cogit>>#v3:ShortForward:Branch:Distance: */ +static NoDbgRegParms sqInt +v3ShortForwardBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + assert(nExts == 0); + return ((fetchByteofObject(pc, aMethodObj)) & 7) + 1; +} + + +/* 253 11111101 eei i i kkk jjjjjjjj Push Closure Num Copied iii (+ Ext A + // 16 * 8) Num Args kkk (+ Ext A \\ 16 * 8) BlockSize jjjjjjjj (+ Ext B * + 256). ee = num extensions + */ + + /* Cogit>>#v4:Block:Code:Size: */ +static NoDbgRegParms sqInt +v4BlockCodeSize(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + sqInt byte; + sqInt byteOne; + sqInt extAValue; + sqInt extBValue; + sqInt extBValueSqInt; + sqInt extByte; + sqInt pcSqInt; + + byteOne = fetchByteofObject(pc + 1, aMethodObj); + + /* If nExts < 0 it isn't known and we rely on the number of extensions encoded in the eeiiikkk byte. */ + assert((nExts < 0) + || (nExts == (((usqInt)(byteOne)) >> 6))); + + /* begin parseV4Exts:priorTo:in:into: */ + extAValue = (extBValueSqInt = 0); + pcSqInt = (pc - (((usqInt)(byteOne)) >> 6)) - (((usqInt)(byteOne)) >> 6); + while (pcSqInt < pc) { + byte = fetchByteofObject(pcSqInt, aMethodObj); + pcSqInt += 1; + extByte = fetchByteofObject(pcSqInt, aMethodObj); + pcSqInt += 1; + assert((byte == 224) + || (byte == 225)); + if (byte == 224) { + extAValue = (((((usqInt)(extAValue) << 8)))) + extByte; + } + else { + extBValueSqInt = ((extBValueSqInt == 0) + && (extByte > 0x7F) + ? extByte - 0x100 + : (((((usqInt)(extBValueSqInt) << 8)))) + extByte); + } + } + extBValue = extBValueSqInt; + return (fetchByteofObject(pc + 2, aMethodObj)) + ((((usqInt)(extBValue) << 8))); +} + + +/* 242 11110010 i i i i i i i i Jump i i i i i i i i (+ Extend B * 256, + where bbbbbbbb = sddddddd, e.g. -32768 = i=0, a=0, s=1) + */ +/* 243 11110011 i i i i i i i i Pop and Jump 0n True i i i i i i i i (+ + Extend A * 256) + */ +/* 244 11110100 i i i i i i i i Pop and Jump 0n False i i i i i i i i (+ + Extend A * 256) + */ + + /* Cogit>>#v4:LongForward:Branch:Distance: */ +static NoDbgRegParms sqInt +v4LongForwardBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + sqInt byte; + sqInt extAValue; + sqInt extBValue; + sqInt extBValueSqInt; + sqInt extByte; + sqInt pcSqInt; + + assert(nExts >= 0); + + /* begin parseV4Exts:priorTo:in:into: */ + extAValue = (extBValueSqInt = 0); + pcSqInt = (pc - nExts) - nExts; + while (pcSqInt < pc) { + byte = fetchByteofObject(pcSqInt, aMethodObj); + pcSqInt += 1; + extByte = fetchByteofObject(pcSqInt, aMethodObj); + pcSqInt += 1; + assert((byte == 224) + || (byte == 225)); + if (byte == 224) { + extAValue = (((((usqInt)(extAValue) << 8)))) + extByte; + } + else { + extBValueSqInt = ((extBValueSqInt == 0) + && (extByte > 0x7F) + ? extByte - 0x100 + : (((((usqInt)(extBValueSqInt) << 8)))) + extByte); + } + } + extBValue = extBValueSqInt; + return (fetchByteofObject(pc + 1, aMethodObj)) + ((((usqInt)(extBValue) << 8))); +} + + +/* 242 11110010 i i i i i i i i Jump i i i i i i i i (+ Extend B * 256, + where bbbbbbbb = sddddddd, e.g. -32768 = i=0, a=0, s=1) + */ + + /* Cogit>>#v4:Long:Branch:Distance: */ +static NoDbgRegParms sqInt +v4LongBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + sqInt byte; + sqInt extAValue; + sqInt extBValue; + sqInt extBValueSqInt; + sqInt extByte; + sqInt pcSqInt; + + assert(nExts >= 0); + + /* begin parseV4Exts:priorTo:in:into: */ + extAValue = (extBValueSqInt = 0); + pcSqInt = (pc - nExts) - nExts; + while (pcSqInt < pc) { + byte = fetchByteofObject(pcSqInt, aMethodObj); + pcSqInt += 1; + extByte = fetchByteofObject(pcSqInt, aMethodObj); + pcSqInt += 1; + assert((byte == 224) + || (byte == 225)); + if (byte == 224) { + extAValue = (((((usqInt)(extAValue) << 8)))) + extByte; + } + else { + extBValueSqInt = ((extBValueSqInt == 0) + && (extByte > 0x7F) + ? extByte - 0x100 + : (((((usqInt)(extBValueSqInt) << 8)))) + extByte); + } + } + extBValue = extBValueSqInt; + return (fetchByteofObject(pc + 1, aMethodObj)) + ((((usqInt)(extBValue) << 8))); +} + + /* Cogit>>#voidCogCompiledCode */ +void +voidCogCompiledCode(void) +{ + CogMethod *cogMethod; + usqInt theLimitAddress; + usqInt theStartAddress; + + /* begin clearCogCompiledCode */ + cogMethod = ((CogMethod *) baseAddress); + while ((((usqInt)cogMethod)) < mzFreeStart) { + if (((cogMethod->cmType)) >= CMMethod) { + freeMethod(cogMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + theStartAddress = baseAddress; + theLimitAddress = limitAddress; + + /* begin manageFrom:to: */ + mzFreeStart = (baseAddress = theStartAddress); + youngReferrers = (limitAddress = theLimitAddress); + openPICList = null; + methodBytesFreedSinceLastCompaction = 0; + methodCount = 0; + + /* code must still be executable, e.g. for ceInvokeInterpret */ + + /* begin ensureExecutableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureExecutableCodeZoneAt(__LINE__); + } +} + + +/* Access for the object representations when they need to prepend code to + trampolines. + */ +/* Eliminate stale dependent info. */ + + /* Cogit>>#zeroOpcodeIndex */ +static void +zeroOpcodeIndex(void) +{ + sqInt i; + + for (i = 0; i < opcodeIndex; i += 1) { + ((abstractOpcodes[i]).dependent = null); + } + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + firstOpcodeIndex = 0x10000; + nextLiteralIndex = (lastDumpedLiteralIndex = 0); +} + + /* CogMethodZone>>#addToOpenPICList: */ +static NoDbgRegParms void +addToOpenPICList(CogMethod *anOpenPIC) +{ + assert(isCMOpenPIC(((CogBlockMethod *) anOpenPIC))); + assert((openPICList == null) + || (isCMOpenPIC(((CogBlockMethod *) openPICList)))); + assertValidDualZoneWriteAddress(anOpenPIC); + (anOpenPIC->nextOpenPIC = ((usqInt)openPICList)); + openPICList = ((CogMethod *) ((((usqInt)anOpenPIC)) - (getCodeToDataDelta()))); +} + + /* CogMethodZone>>#addToYoungReferrers: */ +static NoDbgRegParms void +addToYoungReferrers(CogMethod *writableCogMethod) +{ + assertValidDualZoneWriteAddress(writableCogMethod); + assert((occurrencesInYoungReferrers(writableCogMethod)) == 0); + assert((writableCogMethod->cmRefersToYoung)); + assert((youngReferrers <= limitAddress) + && (youngReferrers >= (limitAddress - (methodCount * BytesPerWord)))); + if (!(asserta((limitAddress - (methodCount * BytesPerWord)) >= mzFreeStart))) { + error("no room on youngReferrers list"); + } + youngReferrers -= BytesPerWord; + codeLongAtput((void *)(youngReferrers),(((usqInt)writableCogMethod)) - (getCodeToDataDelta())); +} + + /* CogMethodZone>>#allocate: */ +static NoDbgRegParms usqInt +allocate(sqInt numBytes) +{ + usqInt allocation; + int roundedBytes; + + roundedBytes = (numBytes + 7) & -8; + if ((mzFreeStart + roundedBytes) >= (limitAddress - (methodCount * BytesPerWord))) { + return 0; + } + allocation = mzFreeStart; + mzFreeStart += roundedBytes; + methodCount += 1; + return allocation; +} + + +/* Answer the method containing mcpc for the purposes of code zone + compaction, where mcpc is actually the value of instructionPointer at the + time of a compaction. */ + + /* CogMethodZone>>#cogMethodContaining: */ +CogMethod * +cogMethodContaining(usqInt mcpc) +{ + CogMethod *cogMethod; + CogMethod *prevMethod; + + if (mcpc > limitAddress) { + return null; + } + if (mcpc < baseAddress) { + /* assertMcpcIsPrimReturn: */ + assert((mcpc == cePrimReturnEnterCogCode) + || (mcpc == cePrimReturnEnterCogCodeProfiling)); + return null; + } + assert(mcpc < (freeStart())); + cogMethod = ((CogMethod *) baseAddress); + + /* Speed up the search by finding the entry in the openPICList before mcpc. + Typically 2 to 3% of the zone's entities are openPICs. The openPICList is ordered high to low. */ + if (openPICList) { + prevMethod = openPICList; + while (!((!prevMethod) + || ((((usqInt)prevMethod)) < mcpc))) { + prevMethod = ((CogMethod *) ((prevMethod->nextOpenPIC))); + } + if (prevMethod) { + cogMethod = prevMethod; + } + } + + /* Now search upward from either baseAddress or the first entry in the openPICList below mcpc. */ + while ((((usqInt)cogMethod)) < mcpc) { + prevMethod = cogMethod; + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + return prevMethod; +} + + /* CogMethodZone>>#compactCompiledCode */ +static void +compactCompiledCode(void) +{ + unsigned short bytes; + CogMethod *dest; + CogMethod *source; + CogMethod *writableVersion; + + compactionInProgress = 1; + methodCount = 0; + source = ((CogMethod *) baseAddress); + voidOpenPICList(); + while ((source < (limitZony())) + && (!(((source->cmType)) == CMFree))) { + assert((cogMethodDoesntLookKosher(source)) == 0); + writableVersion = ((CogMethod *) ((((usqInt)source)) + codeToDataDelta)); + (writableVersion->objectHeader = nullHeaderForMachineCodeMethod()); + if (((source->cmUsageCount)) > 0) { + (writableVersion->cmUsageCount = ((source->cmUsageCount)) / 2); + } + + /* begin maybeLinkOnUnpairedMethodList: */ + if (((source->cmType)) == CMOpenPIC) { + addToOpenPICList(writableVersion); + } + methodCount += 1; + source = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)source)) + ((source->blockSize))))); + } + if (source >= (limitZony())) { + haltmsg("no free methods; cannot compact."); + return; + } + dest = source; + while (source < (limitZony())) { + assert((maybeFreeCogMethodDoesntLookKosher(source)) == 0); + bytes = (source->blockSize); + if (!(((source->cmType)) == CMFree)) { + methodCount += 1; + codeMemmove(dest, source, bytes); + + /* begin maybeFlushWritableZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + if (codeToDataDelta > 0) { + flushDCacheFromto(backEnd, ((usqInt)dest), (((usqInt)dest)) + bytes); + } +# endif + + ((writableVersion = ((CogMethod *) ((((usqInt)dest)) + codeToDataDelta)))->objectHeader = nullHeaderForMachineCodeMethod()); + if (((dest->cmType)) >= CMMethod) { + /* For non-Newspeak there should be a one-to-one mapping between bytecoded and + cog methods. For Newspeak not necessarily, but only for anonymous accessors. + Only update the original method's header if it is referring to this CogMethod. */ + if ((((sqInt)(rawHeaderOf((dest->methodObject))))) == (((sqInt)source))) { + rawHeaderOfput((dest->methodObject), ((sqInt)dest)); + } + else { + assert((noAssertMethodClassAssociationOf((dest->methodObject))) == (nilObject())); + + /* begin linkOnUnpairedMethodList: */ + } + } + else { + /* begin clearSavedPICUsageCount: */ + if (((dest->cmType)) == CMOpenPIC) { + addToOpenPICList(writableVersion); + } + } + if (((dest->cmUsageCount)) > 0) { + (writableVersion->cmUsageCount = ((dest->cmUsageCount)) / 2); + } + + /* begin maybeFlushWritableZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + if (codeToDataDelta > 0) { + flushDCacheFromto(backEnd, ((usqInt)dest), ((usqInt)(dest + 1))); + } +# endif + + dest = ((CogMethod *) ((((usqInt)dest)) + bytes)); + } + source = ((CogMethod *) ((((usqInt)source)) + bytes)); + } + mzFreeStart = ((usqInt)dest); + methodBytesFreedSinceLastCompaction = 0; + compactionInProgress = 0; +} + + /* CogMethodZone>>#ensureInYoungReferrers: */ +static NoDbgRegParms void +ensureInYoungReferrers(CogMethod *cogMethod) +{ + CogMethod *writableMethod; + + assertValidDualZoneReadAddress(cogMethod); + if (!((cogMethod->cmRefersToYoung))) { + assert((occurrencesInYoungReferrers(cogMethod)) == 0); + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + ((writableMethod = ((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)))->cmRefersToYoung = 1); + addToYoungReferrers(writableMethod); + } +} + + /* CogMethodZone>>#freeMethod: */ +static NoDbgRegParms void +freeMethod(CogMethod *cogMethod) +{ + CogMethod *writableMethod; + + assert(!((isCMFree(((CogBlockMethod *) cogMethod))))); + assert((cogMethodDoesntLookKosher(cogMethod)) == 0); + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + if (((cogMethod->cmType)) >= CMMethod) { + if (((cogMethod->cmType)) == CMMethodFlaggedForBecome) { + } + else { + /* For non-Newspeak there should be a one-to-one mapping between bytecoded and + cog methods. For Newspeak not necessarily, but only for anonymous accessors. + Only reset the original method's header if it is referring to this CogMethod. */ + if ((((sqInt)(rawHeaderOf((cogMethod->methodObject))))) == (((sqInt)cogMethod))) { + rawHeaderOfput((cogMethod->methodObject), (cogMethod->methodHeader)); + } + else { + assert((noAssertMethodClassAssociationOf((cogMethod->methodObject))) == (nilObject())); + } + } + } + if (((cogMethod->cmType)) == CMOpenPIC) { + removeFromOpenPICList(cogMethod); + } + writableMethod = ((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)); + (writableMethod->cmRefersToYoung = 0); + (writableMethod->cmType = CMFree); + methodBytesFreedSinceLastCompaction += (cogMethod->blockSize); +} + + +/* Free methods, preferring older methods for compaction, up to some + fraction, currently a quarter. + */ + + /* CogMethodZone>>#freeOlderMethodsForCompaction */ +static void +freeOlderMethodsForCompaction(void) +{ + sqInt amountToFree; + CogMethod *cogMethod; + sqInt freeableUsage; + sqInt freedSoFar; + sqInt initialFreeSpace; + sqInt zoneSize; + + zoneSize = limitAddress - baseAddress; + initialFreeSpace = (limitAddress - mzFreeStart) + methodBytesFreedSinceLastCompaction; + freedSoFar = initialFreeSpace; + + /* 4 needs to be e.g. a start-up parameter */ + amountToFree = zoneSize / 4; + freeableUsage = 0; + do { + cogMethod = ((CogMethod *) baseAddress); + while (((((usqInt)cogMethod)) < mzFreeStart) + && (freedSoFar < amountToFree)) { + if (/* shouldFreeMethod:given: */ + (!(((cogMethod->cmType)) == CMFree)) + && (((cogMethod->cmUsageCount)) <= freeableUsage)) { + freeMethod(cogMethod); + freedSoFar += (cogMethod->blockSize); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + } while((freedSoFar < amountToFree) + && (((freeableUsage += 1)) < CMMaxUsageCount)); +} + + +/* Answer that all entries in youngReferrers are in-use and have the + cmRefersToYoung flag set. + Used to check that the youngreferrers pruning routines work correctly. */ + + /* CogMethodZone>>#kosherYoungReferrers */ +sqInt +kosherYoungReferrers(void) +{ + CogMethod *cogMethod; + usqInt pointer; + CogMethod *prevMethod; + + if ((youngReferrers > limitAddress) + || (youngReferrers < mzFreeStart)) { + return 0; + } + pointer = youngReferrers; + while (pointer < limitAddress) { + cogMethod = ((CogMethod *) (longAt((void *)(pointer)))); + if (!(((cogMethod->cmType)) == CMFree)) { + if (!((cogMethod->cmRefersToYoung))) { + return 0; + } + if ((occurrencesInYoungReferrers(cogMethod)) != 1) { + return 0; + } + } + pointer += BytesPerWord; + } + cogMethod = ((CogMethod *) baseAddress); + while (cogMethod < (limitZony())) { + prevMethod = cogMethod; + if (!(((cogMethod->cmType)) == CMFree)) { + if ((occurrencesInYoungReferrers(cogMethod)) != (((cogMethod->cmRefersToYoung) + ? 1 + : 0))) { + return 0; + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + if (cogMethod == prevMethod) { + return 0; + } + } + return 1; +} + + /* CogMethodZone>>#methodFor: */ +CogMethod * +methodFor(void *address) +{ + CogMethod *cogMethod; + CogMethod *nextMethod; + + if ((((usqInt)address)) < (methodZoneBase)) { + return null; + } + cogMethod = ((CogMethod *) baseAddress); + + /* Speed up the search by finding the entry in the openPICList before mcpc. + Typically 2 to 3% of the zone's entities are openPICs. The openPICList is ordered high to low. */ + if (openPICList) { + nextMethod = openPICList; + while (!((!nextMethod) + || ((((usqInt)nextMethod)) < (((usqInt)address))))) { + nextMethod = ((CogMethod *) ((nextMethod->nextOpenPIC))); + } + if (nextMethod) { + cogMethod = nextMethod; + } + } + while ((cogMethod < (limitZony())) + && ((((usqInt)cogMethod)) <= (((usqInt)address)))) { + nextMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + if (nextMethod == cogMethod) { + return null; + } + if (((((usqInt)address)) >= (((usqInt)cogMethod))) + && ((((usqInt)address)) < (((usqInt)nextMethod)))) { + return cogMethod; + } + cogMethod = nextMethod; + } + return null; +} + + /* CogMethodZone>>#methodsCompiledToMachineCodeInto: */ +#if VMInvestigations +sqInt +methodsCompiledToMachineCodeInto(sqInt arrayObj) +{ + CogMethod *cogMethod1; + sqInt methodIndex; + + methodIndex = 0; + + /* begin methodsDo: */ + cogMethod1 = ((CogMethod *) baseAddress); + while (cogMethod1 < (limitZony())) { + if (!(((cogMethod1->cmType)) == CMFree)) { + if (((cogMethod1->cmType)) >= CMMethod) { + storePointerUncheckedofObjectwithValue(methodIndex, arrayObj, (cogMethod1->methodObject)); + methodIndex += 1; + } + } + cogMethod1 = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod1)) + ((cogMethod1->blockSize))))); + } + return methodIndex; +} +#endif /* VMInvestigations */ + + /* CogMethodZone>>#numMethods */ +sqInt +numMethods(void) +{ + return methodCount; +} + + /* CogMethodZone>>#numMethodsOfType: */ +sqInt +numMethodsOfType(sqInt cogMethodType) +{ + CogMethod *cogMethod; + sqInt n; + + n = 0; + cogMethod = ((CogMethod *) baseAddress); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) == cogMethodType) { + n += 1; + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + return n; +} + + /* CogMethodZone>>#occurrencesInYoungReferrers: */ +static NoDbgRegParms sqInt +occurrencesInYoungReferrers(CogMethod *cogMethod) +{ + sqInt count; + usqInt pointer; + + assert(youngReferrers <= limitAddress); + count = 0; + pointer = youngReferrers; + while (pointer < limitAddress) { + if ((((sqInt)cogMethod)) == (longAt((void *)(pointer)))) { + count += 1; + } + pointer += BytesPerWord; + } + return count; +} + + /* CogMethodZone>>#openPICWithSelector: */ +static NoDbgRegParms CogMethod * +openPICWithSelector(sqInt aSelector) +{ + CogMethod *openPIC; + + openPIC = openPICList; + do { + if ((openPIC == null) + || (((openPIC->selector)) == aSelector)) { + return openPIC; + } + + /* N.B. Use self rather than coInterpreter to avoid attempting to cast nil. + Conversion to CogMethod done in the nextOpenPIC accessor. */ + openPIC = ((CogMethod *) ((openPIC->nextOpenPIC))); + } while(1); + return 0; +} + + +/* Some methods have been freed. Compute how much each survivor needs to + move during the ensuing compaction and record it in the objectHeader + field. + For Sista, where we want PICs to last so they can be observed, we need to + keep PICs unless + they are definitely unused. So we need to identify unused PICs. So in + planCompact, zero the + usage counts of all PICs, saving the actual usage count in + blockEntryOffset. Then in + relocateMethodsPreCompaction (actually in + relocateIfCallOrMethodReference:mcpc:delta:) restore the usage counts of + used PICs. Finally in compactCompiledCode, clear the blockEntryOffset + of the unused PICs; they will then have a zero count and be reclaimed in + the next code compaction. */ + + /* CogMethodZone>>#planCompaction */ +static void +planCompaction(void) +{ + CogMethod *cogMethod; + sqInt delta; + + delta = 0; + cogMethod = ((CogMethod *) baseAddress); + while ((((usqInt)cogMethod)) < mzFreeStart) { + if (((cogMethod->cmType)) == CMFree) { + delta -= (cogMethod->blockSize); + } + else { + assert((cogMethodDoesntLookKosher(cogMethod)) == 0); + ((((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)))->objectHeader = delta); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } +} + + +/* useful for debugging */ + + /* CogMethodZone>>#printCogMethods */ +void +printCogMethods(void) +{ + CogMethod *cogMethod; + sqInt nb; + sqInt nc; + sqInt nf; + sqInt nm; + sqInt no; + sqInt nu; + + /* begin printCogMethodsSummarizing: */ + nm = (nb = (nc = (no = (nf = (nu = 0))))); + cogMethod = ((CogMethod *) baseAddress); + while (cogMethod < (limitZony())) { + printCogMethod(cogMethod); + switch ((cogMethod->cmType)) { + case CMFree: + nf += 1; + break; + case CMMethod: + nm += 1; + break; + case CMMethodFlaggedForBecome: + nb += 1; + break; + case CMClosedPIC: + nc += 1; + break; + case CMOpenPIC: + no += 1; + break; + default: + nu += 1; + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + print("CMMethod "); + printNum(nm); + if (nb > 0) { + print(" (flagged for become: "); + printNum(nb); + print(")"); + } + print(" CMClosedPIC "); + printNum(nc); + print(" CMOpenPIC "); + printNum(no); + print(" CMFree "); + printNum(nf); + if (nu > 0) { + print(" UNKNOWN "); + printNum(nu); + } + print(" total "); + printNum(((((nm + nc) + no) + nf) + nu) + nb); + cr(); +} + + +/* useful for debugging */ + + /* CogMethodZone>>#printCogMethodsOfType: */ +void +printCogMethodsOfType(sqInt cmType) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) baseAddress); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) == cmType) { + printCogMethod(cogMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } +} + + +/* useful for debugging */ + + /* CogMethodZone>>#printCogMethodsWithMethod: */ +void +printCogMethodsWithMethod(sqInt methodOop) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) baseAddress); + while (cogMethod < (limitZony())) { + if ((!(((cogMethod->cmType)) == CMFree)) + && (((cogMethod->methodObject)) == methodOop)) { + printCogMethod(cogMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } +} + + +/* useful for debugging */ + + /* CogMethodZone>>#printCogMethodsWithPrimitive: */ +void +printCogMethodsWithPrimitive(sqInt primIdx) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) baseAddress); + while (cogMethod < (limitZony())) { + if ((((cogMethod->cmType)) >= CMMethod) + && (primIdx == (primitiveIndexOfMethodheader((cogMethod->methodObject), (cogMethod->methodHeader))))) { + printCogMethod(cogMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } +} + + +/* useful for debugging */ + + /* CogMethodZone>>#printCogMethodsWithSelector: */ +void +printCogMethodsWithSelector(sqInt selectorOop) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) baseAddress); + while (cogMethod < (limitZony())) { + if ((!(((cogMethod->cmType)) == CMFree)) + && (((cogMethod->selector)) == selectorOop)) { + printCogMethod(cogMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } +} + + +/* useful for debugging */ + + /* CogMethodZone>>#printCogYoungReferrers */ +void +printCogYoungReferrers(void) +{ + CogMethod *cogMethod; + usqInt pointer; + + pointer = youngReferrers; + while (pointer < limitAddress) { + cogMethod = ((CogMethod *) (longAt((void *)(pointer)))); + if (!((cogMethod->cmRefersToYoung))) { + print("*"); + } + if (((cogMethod->cmType)) == CMFree) { + print("!"); + } + if (!(((cogMethod->cmRefersToYoung)) + && (!(((cogMethod->cmType)) == CMFree)))) { + print(" "); + } + printCogMethod(cogMethod); + pointer += BytesPerWord; + } +} + + +/* useful for debugging */ + + /* CogMethodZone>>#printOpenPICList */ +sqInt +printOpenPICList(void) +{ + sqInt n; + CogMethod *openPIC; + + /* begin printOpenPICListSummarizing: */ + n = 0; + openPIC = openPICList; + while (!(openPIC == null)) { + n += 1; + printCogMethod(openPIC); + openPIC = ((CogMethod *) ((openPIC->nextOpenPIC))); + } + return n; +} + + /* CogMethodZone>>#pruneYoungReferrers */ +static sqInt +pruneYoungReferrers(void) +{ + usqInt dest; + usqInt next; + usqInt source; + + next = 0; + assert(youngReferrers <= limitAddress); + if (youngReferrers == limitAddress) { + return null; + } + dest = limitAddress; + while (1) { + next = dest - BytesPerWord; + if (!((next >= youngReferrers) + && (((((CogMethod *) (longAt((void *)(next)))))->cmRefersToYoung)))) break; + dest = next; + } + assert(dest >= youngReferrers); + source = dest - BytesPerWord; + while (source >= youngReferrers) { + if (((((CogMethod *) (longAt((void *)(source)))))->cmRefersToYoung)) { + assert(source < (dest - BytesPerWord)); + if (next) { + next = null; + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + } + + /* convenient first-time flag */ + codeLongAtput((void *)((dest -= BytesPerWord)),longAt((void *)(source))); + } + source -= BytesPerWord; + } + youngReferrers = dest; + assert(kosherYoungReferrers()); + return 0; +} + + /* CogMethodZone>>#relocateAndPruneYoungReferrers */ +static sqInt +relocateAndPruneYoungReferrers(void) +{ + CogMethod *cogMethod; + usqInt dest; + usqInt next; + usqInt source; + + cogMethod = ((CogMethod *) 0); + next = 0; + assert(youngReferrers <= limitAddress); + if (youngReferrers == limitAddress) { + return null; + } + dest = limitAddress; + while (1) { + next = dest - BytesPerWord; + if (!((next >= youngReferrers) + && ((!(isCMFree(((CogBlockMethod *) ((cogMethod = ((CogMethod *) (longAt((void *)(next)))))))))) + && ((cogMethod->cmRefersToYoung))))) break; + if ((cogMethod->objectHeader)) { + codeLongAtput((void *)(next),(((sqInt)cogMethod)) + ((cogMethod->objectHeader))); + } + dest = next; + } + assert(dest >= youngReferrers); + source = dest - BytesPerWord; + while (source >= youngReferrers) { + cogMethod = ((CogMethod *) (longAt((void *)(source)))); + if ((!(((cogMethod->cmType)) == CMFree)) + && ((cogMethod->cmRefersToYoung))) { + assert(source < (dest - BytesPerWord)); + if ((cogMethod->objectHeader)) { + cogMethod = ((CogMethod *) ((((sqInt)cogMethod)) + (((sqInt)((cogMethod->objectHeader)))))); + } + codeLongAtput((void *)((dest -= BytesPerWord)),((sqInt)cogMethod)); + } + source -= BytesPerWord; + } + + /* this assert must be deferred until after compaction. See the end of compactCogCompiledCode */ + /* self assert: self kosherYoungReferrers */ + youngReferrers = dest; + return 0; +} + + +/* All surviving methods have had the amount they are going to relocate by + stored in their objectHeader fields. Relocate all relative calls so that + after the compaction of both the method containing each call and the call + target the calls invoke the same target. */ + + /* CogMethodZone>>#relocateMethodsPreCompaction */ +static sqInt +relocateMethodsPreCompaction(void) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) baseAddress); + while ((((usqInt)cogMethod)) < mzFreeStart) { + if (!(((cogMethod->cmType)) == CMFree)) { + if (((cogMethod->cmType)) == CMClosedPIC) { + relocateCallsInClosedPIC(cogMethod); + } + else { + relocateCallsAndSelfReferencesInMethod(cogMethod); + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + relocateAndPruneYoungReferrers(); + return 1; +} + + /* CogMethodZone>>#removeFromOpenPICList: */ +static NoDbgRegParms sqInt +removeFromOpenPICList(CogMethod *anOpenPIC) +{ + CogMethod *prevPIC; + + assert(isCMOpenPIC(((CogBlockMethod *) anOpenPIC))); + if (!openPICList) { + return null; + } + assert((isCMOpenPIC(((CogBlockMethod *) openPICList))) + && ((!((openPICList->nextOpenPIC))) + || (isCMOpenPIC(((CogBlockMethod *) (((CogMethod *) ((openPICList->nextOpenPIC))))))))); + if (anOpenPIC == openPICList) { + /* N.B. Use self rather than coInterpreter to avoid attempting to cast nil. + Conversion to CogMethod done in the nextOpenPIC accessor. */ + openPICList = ((CogMethod *) ((anOpenPIC->nextOpenPIC))); + return null; + } + prevPIC = openPICList; + do { + assert((prevPIC != null) + && (isCMOpenPIC(((CogBlockMethod *) prevPIC)))); + if (((prevPIC->nextOpenPIC)) == (((usqInt)anOpenPIC))) { + ((((CogMethod *) ((((usqInt)prevPIC)) + codeToDataDelta)))->nextOpenPIC = (anOpenPIC->nextOpenPIC)); + return null; + } + prevPIC = ((CogMethod *) ((prevPIC->nextOpenPIC))); + } while(1); + return 0; +} + + +/* Determine the default alignment for the start of a CogMethod, which in + turn determines the size of the mask used to distinguish the checked and + unchecked entry-points, used to distinguish normal and super sends on + method unlinking. + This is passed onto the backEnd to allow processors with coarse + instructions (ARM) to increase the alignment if required. */ + + /* CogMethodZone>>#roundUpLength: */ +static NoDbgRegParms int +roundUpLength(sqInt numBytes) +{ + return roundUpToMethodAlignment(backEnd, numBytes); +} + + /* CogMethodZone>>#voidOpenPICList */ +static void +voidOpenPICList(void) +{ + openPICList = null; +} + + /* CogMethodZone>>#voidUnpairedMethodList */ +static void +voidUnpairedMethodList(void) +{ +} + + /* CogMethodZone>>#whereIsMaybeCodeThing: */ +char * +whereIsMaybeCodeThing(sqInt anOop) +{ + if (oopisGreaterThanOrEqualToandLessThan(anOop, codeBase, limitAddress)) { + if (oopisLessThan(anOop, minCogMethodAddress())) { + return " is in generated runtime"; + } + if (oopisLessThan(anOop, mzFreeStart)) { + return " is in generated methods"; + } + if (oopisLessThan(anOop, youngReferrers)) { + return " is in code zone"; + } + return " is in young referrers"; + } + return null; +} + + /* CogMethodZone>>#zoneAlignment */ +static sqInt +zoneAlignment(void) +{ + return 8; +} + + /* CogObjectRepresentation>>#checkValidObjectReference: */ +static NoDbgRegParms sqInt +checkValidObjectReference(sqInt anOop) +{ + return (!(isImmediate(anOop))) + && ((heapMapAtWord(pointerForOop(anOop))) != 0); +} + + /* CogObjectRepresentation>>#genCmpClassFloatCompactIndexR: */ +static NoDbgRegParms AbstractInstruction * +genCmpClassFloatCompactIndexR(sqInt reg) +{ + AbstractInstruction *anInstruction; + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, ClassFloatCompactIndex, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(ClassFloatCompactIndex, BytesPerOop)); + } + return anInstruction; +} + + /* CogObjectRepresentation>>#genCmpClassMethodContextCompactIndexR: */ +static NoDbgRegParms AbstractInstruction * +genCmpClassMethodContextCompactIndexR(sqInt reg) +{ + AbstractInstruction *anInstruction; + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, ClassMethodContextCompactIndex, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(ClassMethodContextCompactIndex, BytesPerOop)); + } + return anInstruction; +} + + +/* Get the method header (first word) of a CompiledMethod into headerReg. + Deal with the method possibly being cogged. */ + + /* CogObjectRepresentation>>#genGetMethodHeaderOf:into:scratch: */ +static NoDbgRegParms sqInt +genGetMethodHeaderOfintoscratch(sqInt methodReg, sqInt headerReg, sqInt scratchReg) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpNotCogged; + sqInt offset; + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, BaseHeaderSize, methodReg, headerReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + jumpNotCogged = genJumpSmallInteger(headerReg); + offset = offsetof(CogMethod, methodHeader); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, offset, headerReg, headerReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + jmpTarget(jumpNotCogged, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + +/* Generate a compare and branch to test if aRegister and bRegister contains + other than SmallIntegers, + i.e. don't branch if both aRegister and bRegister contain SmallIntegers. + Answer the jump. Destroy scratchRegister if required. */ + + /* CogObjectRepresentation>>#genJumpNotSmallIntegersIn:and:scratch: */ +static NoDbgRegParms AbstractInstruction * +genJumpNotSmallIntegersInandscratch(sqInt aRegister, sqInt bRegister, sqInt scratchRegister) +{ + /* MoveR:R: */ + genoperandoperand(MoveRR, aRegister, scratchRegister); + + /* AndR:R: */ + genoperandoperand(AndRR, bRegister, scratchRegister); + return genJumpNotSmallInteger(scratchRegister); +} + + /* CogObjectRepresentation>>#genLoadSlot:sourceReg:destReg: */ +static NoDbgRegParms sqInt +genLoadSlotsourceRegdestReg(sqInt index, sqInt sourceReg, sqInt destReg) +{ + AbstractInstruction *anInstruction; + sqInt offset; + + offset = (index * BytesPerWord) + BaseHeaderSize; + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, offset, sourceReg, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + return 0; +} + + /* CogObjectRepresentation>>#genPrimitiveAdd */ +static sqInt +genPrimitiveAdd(void) +{ + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpOvfl; + + if (!(mclassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ClassReg); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + genRemoveSmallIntegerTagsInScratchReg(ClassReg); + + /* AddR:R: */ + genoperandoperand(AddRR, ReceiverResultReg, ClassReg); + jumpOvfl = genConditionalBranchoperand(JumpOverflow, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpOvfl, jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveBitAnd */ +static sqInt +genPrimitiveBitAnd(void) +{ + AbstractInstruction *jumpNotSI; + + if (!(mclassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + + /* Whether the SmallInteger tags are zero or non-zero, anding them together will preserve them. */ + + /* AndR:R: */ + genoperandoperand(AndRR, Arg0Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveBitOr */ +static sqInt +genPrimitiveBitOr(void) +{ + AbstractInstruction *jumpNotSI; + + if (!(mclassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + + /* Whether the SmallInteger tags are zero or non-zero, oring them together will preserve them. */ + + /* OrR:R: */ + genoperandoperand(OrRR, Arg0Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + +/* rTemp := rArg0 + rClass := tTemp + rTemp := rTemp & 1 + jz nonInt + rClass >>= 1 + cmp 0,rClass + jge neg + cmp 31,rClass // numSmallIntegerBits, jge for sign + jge tooBig + rTemp := rReceiver + rTemp <<= rClass + rTemp >>= rClass (arithmetic) + cmp rTemp,rReceiver + jnz ovfl + rReceiver := rReceiver - 1 + rReceiver := rReceiver <<= rClass + rReceiver := rReceiver + 1 + ret + neg: + rClass := 0 - rClass + cmp 31,rClass // numSmallIntegerBits + jge inRange + rClass := 31 + inRange + rReceiver := rReceiver >>= rClass. + rReceiver := rReceiver | smallIntegerTags. + ret + ovfl + tooBig + nonInt: + fail + */ + + /* CogObjectRepresentation>>#genPrimitiveBitShift */ +static sqInt +genPrimitiveBitShift(void) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpInRange; + AbstractInstruction *jumpNegative; + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpOvfl; + AbstractInstruction *jumpTooBig; + sqInt quickConstant; + + if (!(mclassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ClassReg); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + genConvertSmallIntegerToIntegerInReg(ClassReg); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpNegative = genConditionalBranchoperand(JumpNegative, ((sqInt)0)); + quickConstant = numSmallIntegerBits(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpTooBig = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, TempReg); + + /* LogicalShiftLeftR:R: */ + genoperandoperand(LogicalShiftLeftRR, ClassReg, TempReg); + + /* ArithmeticShiftRightR:R: */ + genoperandoperand(ArithmeticShiftRightRR, ClassReg, TempReg); + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, ReceiverResultReg); + jumpOvfl = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genRemoveSmallIntegerTagsInScratchReg(ReceiverResultReg); + + /* LogicalShiftLeftR:R: */ + genoperandoperand(LogicalShiftLeftRR, ClassReg, ReceiverResultReg); + genAddSmallIntegerTagsTo(ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNegative, genoperand(NegateR, ClassReg)); + quickConstant = numSmallIntegerBits(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpInRange = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + quickConstant = numSmallIntegerBits(); + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, quickConstant, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jmpTarget(jumpInRange, genoperandoperand(ArithmeticShiftRightRR, ClassReg, ReceiverResultReg)); + genClearAndSetSmallIntegerTagsIn(ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSI, jmpTarget(jumpTooBig, jmpTarget(jumpOvfl, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveBitXor */ +static sqInt +genPrimitiveBitXor(void) +{ + AbstractInstruction *jumpNotSI; + + if (!(mclassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + + /* Clear one or the other tag so that xoring will preserve them. */ + genRemoveSmallIntegerTagsInScratchReg(Arg0Reg); + + /* XorR:R: */ + genoperandoperand(XorRR, Arg0Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveClass */ +static sqInt +genPrimitiveClass(void) +{ + sqInt reg; + + reg = ReceiverResultReg; + if (methodOrBlockNumArgs > 0) { + if (methodOrBlockNumArgs > 1) { + return UnimplementedPrimitive; + } + reg = Arg0Reg; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + } + if ((genGetClassObjectOfintoscratchRegmayBeAForwarder(reg, ReceiverResultReg, TempReg, reg != ReceiverResultReg)) == BadRegisterSet) { + genGetClassObjectOfintoscratchRegmayBeAForwarder(reg, ClassReg, TempReg, reg != ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, ReceiverResultReg); + } + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + return UnfailingPrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveDiv */ +static sqInt +genPrimitiveDiv(void) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *convert; + AbstractInstruction *jumpExact; + AbstractInstruction *jumpIsSI; + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpSameSign; + AbstractInstruction *jumpZero; + + if (!(processorHasDivQuoRemAndMClassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ClassReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, Arg1Reg); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + + /* We must shift away the tags, not just subtract them, so that the + overflow case doesn't actually overflow the machine instruction. */ + genShiftAwaySmallIntegerTagsInScratchReg(ClassReg); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpZero = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, TempReg); + genShiftAwaySmallIntegerTagsInScratchReg(TempReg); + gDivRRQuoRem(ClassReg, TempReg, TempReg, ClassReg); + + /* If remainder is zero we must check for overflow. */ + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpExact = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* If arg and remainder signs are different we must round down. */ + + /* XorR:R: */ + genoperandoperand(XorRR, ClassReg, Arg1Reg); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpSameSign = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, 1, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + jmpTarget(jumpSameSign, (convert = genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + genConvertIntegerInRegtoSmallIntegerInReg(TempReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* test for overflow; the only case is SmallInteger minVal // -1 */ + jmpTarget(jumpExact, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpIsSI = genJumpIsSmallIntegerValuescratch(TempReg, Arg1Reg); + jmpTarget(jumpIsSI, convert); + jmpTarget(jumpZero, jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveDivide */ +static sqInt +genPrimitiveDivide(void) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpInexact; + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpOverflow; + AbstractInstruction *jumpZero; + + if (!(processorHasDivQuoRemAndMClassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ClassReg); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + + /* We must shift away the tags, not just subtract them, so that the + overflow case doesn't actually overflow the machine instruction. */ + genShiftAwaySmallIntegerTagsInScratchReg(ClassReg); + jumpZero = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, TempReg); + genShiftAwaySmallIntegerTagsInScratchReg(TempReg); + gDivRRQuoRem(ClassReg, TempReg, TempReg, ClassReg); + + /* If remainder is non-zero fail. */ + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpInexact = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* test for overflow; the only case is SmallInteger minVal / -1 */ + jumpOverflow = genJumpNotSmallIntegerValuescratch(TempReg, Arg1Reg); + genConvertIntegerInRegtoSmallIntegerInReg(TempReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpOverflow, jmpTarget(jumpInexact, jmpTarget(jumpZero, jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveEqual */ +static sqInt +genPrimitiveEqual(void) +{ + return (numericPrimsMixComparison() + ? genSmallIntegerComparisonorDoubleComparisoninvert(JumpZero, gJumpFPEqual, 0) + : genSmallIntegerComparison(JumpZero)); +} + + /* CogObjectRepresentation>>#genPrimitiveFloatAdd */ +static sqInt +genPrimitiveFloatAdd(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(AddRdRd, null, 1) + : genPureFloatArithmeticpreOpCheckboxed(AddRdRd, null, 1)); +} + + /* CogObjectRepresentation>>#genPrimitiveFloatDivide */ +static sqInt +genPrimitiveFloatDivide(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(DivRdRd, genDoubleFailIfZeroArgRcvrarg, 1) + : genPureFloatArithmeticpreOpCheckboxed(DivRdRd, genDoubleFailIfZeroArgRcvrarg, 1)); +} + + /* CogObjectRepresentation>>#genPrimitiveFloatMultiply */ +static sqInt +genPrimitiveFloatMultiply(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(MulRdRd, null, 1) + : genPureFloatArithmeticpreOpCheckboxed(MulRdRd, null, 1)); +} + + /* CogObjectRepresentation>>#genPrimitiveFloatSubtract */ +static sqInt +genPrimitiveFloatSubtract(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(SubRdRd, null, 1) + : genPureFloatArithmeticpreOpCheckboxed(SubRdRd, null, 1)); +} + + /* CogObjectRepresentation>>#genPrimitiveGreaterOrEqual */ +static sqInt +genPrimitiveGreaterOrEqual(void) +{ + return (numericPrimsMixComparison() + ? genSmallIntegerComparisonorDoubleComparisoninvert(JumpGreaterOrEqual, gJumpFPGreaterOrEqual, 0) + : genSmallIntegerComparison(JumpGreaterOrEqual)); +} + + /* CogObjectRepresentation>>#genPrimitiveGreaterThan */ +static sqInt +genPrimitiveGreaterThan(void) +{ + return (numericPrimsMixComparison() + ? genSmallIntegerComparisonorDoubleComparisoninvert(JumpGreater, gJumpFPGreater, 0) + : genSmallIntegerComparison(JumpGreater)); +} + + +/* Defer to the backEnd to generate the best instruction sequence. + Some backends have an efficient count-leading-zeros that must + be inverted to produce the high bit, others have a genuine high + bit. Intel has both, the former only in newer revisions. + Use TempReg. If primitive fails, ReceiverResultReg must remain + unchanged (we CompletePrimitive) */ + + /* CogObjectRepresentation>>#genPrimitiveHighBit */ +static sqInt +genPrimitiveHighBit(void) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpNegativeReceiver; + sqInt quickConstant; + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpNegativeReceiver = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* remove excess tag bits from the receiver oop */ + gOrCqRR((1U << (numSmallIntegerTagBits())) - 1, ReceiverResultReg, ClassReg); + quickConstant = (numSmallIntegerTagBits()) - 1; + + /* begin ArithmeticShiftRightCq:R: */ + genoperandoperand(ArithmeticShiftRightCqR, quickConstant, ClassReg); + backEnd; + + /* begin genHighBitOfNonZeroReg:into: */ + + /* ClzR:R: */ + genoperandoperand(ClzRR, ClassReg, TempReg); + + /* Invert leading zeroas into highBit. + Note the nice bit trick below: + highBit_1based_of_small_int_value = (BytesPerWord * 8) - leadingZeroCout_of_oop - 1 toAccountForTagBit. + This is like 2 complements (- reg - 1) on (BytesPerWord * 8) log2 bits, or exactly a bit invert operation... */ + + /* #XorCq:R: #gen:literal:operand: */ + checkLiteralforInstruction((BytesPerWord * 8) - 1, genoperandoperand(XorCqR, (BytesPerWord * 8) - 1, TempReg)); + genConvertIntegerInRegtoSmallIntegerInReg(TempReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNegativeReceiver, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveIdentical */ +static sqInt +genPrimitiveIdentical(void) +{ + return genPrimitiveIdenticalOrNotIf(0); +} + + /* CogObjectRepresentation>>#genPrimitiveLessOrEqual */ +static sqInt +genPrimitiveLessOrEqual(void) +{ + return (numericPrimsMixComparison() + ? genSmallIntegerComparisonorDoubleComparisoninvert(JumpLessOrEqual, gJumpFPGreaterOrEqual, 1) + : genSmallIntegerComparison(JumpLessOrEqual)); +} + + /* CogObjectRepresentation>>#genPrimitiveLessThan */ +static sqInt +genPrimitiveLessThan(void) +{ + return (numericPrimsMixComparison() + ? genSmallIntegerComparisonorDoubleComparisoninvert(JumpLess, gJumpFPGreater, 1) + : genSmallIntegerComparison(JumpLess)); +} + + /* CogObjectRepresentation>>#genPrimitiveMaxIdentityHash */ +static sqInt +genPrimitiveMaxIdentityHash(void) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + + quickConstant = (((usqInt)(maxIdentityHash()) << 3) | 1); + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, quickConstant, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + return UnfailingPrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveMod */ +static sqInt +genPrimitiveMod(void) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpExact; + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpSameSign; + AbstractInstruction *jumpZero; + + if (!(processorHasDivQuoRemAndMClassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ClassReg); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + genRemoveSmallIntegerTagsInScratchReg(ClassReg); + jumpZero = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, Arg1Reg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, TempReg); + genRemoveSmallIntegerTagsInScratchReg(TempReg); + gDivRRQuoRem(ClassReg, TempReg, TempReg, ClassReg); + + /* If remainder is zero we're done. */ + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpExact = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* If arg and remainder signs are different we must reflect around zero. */ + + /* XorR:R: */ + genoperandoperand(XorRR, ClassReg, Arg1Reg); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpSameSign = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* XorR:R: */ + genoperandoperand(XorRR, ClassReg, Arg1Reg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg1Reg, ClassReg); + jmpTarget(jumpSameSign, jmpTarget(jumpExact, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + genSetSmallIntegerTagsIn(ClassReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpZero, jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveMultiply */ +static sqInt +genPrimitiveMultiply(void) +{ + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpOvfl; + + if (!(processorHasMultiplyAndMClassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ClassReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, Arg1Reg); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + genShiftAwaySmallIntegerTagsInScratchReg(ClassReg); + genRemoveSmallIntegerTagsInScratchReg(Arg1Reg); + + /* MulOverflowR:R: */ + genMulOverflowRR(backEnd, Arg1Reg, ClassReg); + jumpOvfl = genConditionalBranchoperand(JumpOverflow, ((sqInt)0)); + genSetSmallIntegerTagsIn(ClassReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpOvfl, jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + +/* subclasses override if they can */ + + /* CogObjectRepresentation>>#genPrimitiveNewMethod */ +static sqInt +genPrimitiveNewMethod(void) +{ + return UnimplementedPrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveNotEqual */ +static sqInt +genPrimitiveNotEqual(void) +{ + return (numericPrimsMixComparison() + ? genSmallIntegerComparisonorDoubleComparisoninvert(JumpNonZero, gJumpFPNotEqual, 0) + : genSmallIntegerComparison(JumpNonZero)); +} + + /* CogObjectRepresentation>>#genPrimitiveNotIdentical */ +static sqInt +genPrimitiveNotIdentical(void) +{ + return genPrimitiveIdenticalOrNotIf(1); +} + + /* CogObjectRepresentation>>#genPrimitiveQuo */ +static sqInt +genPrimitiveQuo(void) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *convert; + AbstractInstruction *jumpExact; + AbstractInstruction *jumpIsSI; + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpZero; + + if (!(processorHasDivQuoRemAndMClassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ClassReg); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + + /* We must shift away the tags, not just subtract them, so that the + overflow case doesn't actually overflow the machine instruction. */ + genShiftAwaySmallIntegerTagsInScratchReg(ClassReg); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpZero = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, TempReg); + genShiftAwaySmallIntegerTagsInScratchReg(TempReg); + gDivRRQuoRem(ClassReg, TempReg, TempReg, ClassReg); + + /* If remainder is zero we must check for overflow. */ + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpExact = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + convert = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + genConvertIntegerInRegtoSmallIntegerInReg(TempReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpExact, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpIsSI = genJumpIsSmallIntegerValuescratch(TempReg, Arg1Reg); + jmpTarget(jumpIsSI, convert); + jmpTarget(jumpZero, jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveSubtract */ +static sqInt +genPrimitiveSubtract(void) +{ + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpOvfl; + + if (!(mclassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, Arg0Reg, TempReg); + jumpOvfl = genConditionalBranchoperand(JumpOverflow, ((sqInt)0)); + genAddSmallIntegerTagsTo(TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpOvfl, jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genSmallIntegerComparison: */ +static NoDbgRegParms sqInt +genSmallIntegerComparison(sqInt jumpOpcode) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpFail; + AbstractInstruction *jumpTrue; + + if (!(mclassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpFail = genJumpNotSmallInteger(Arg0Reg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, ReceiverResultReg); + jumpTrue = genConditionalBranchoperand(jumpOpcode, 0); + + /* begin genMoveConstant:R: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(falseObject(), BytesPerOop)); + } + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpTrue, genMoveConstantR(trueObject(), ReceiverResultReg)); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpFail, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#isUnannotatableConstant: */ +static NoDbgRegParms sqInt +isUnannotatableConstant(SimStackEntry *simStackEntry) +{ + return (((simStackEntry->type)) == SSConstant) + && ((isImmediate((simStackEntry->constant))) + || (!(/* shouldAnnotateObjectReference: */ + (isNonImmediate((simStackEntry->constant))) + && ((oopisGreaterThan((simStackEntry->constant), classTableRootObj())) + || (oopisLessThan((simStackEntry->constant), nilObject())))))); +} + + /* CogObjectRepresentationFor64BitSpur>>#classForInlineCacheTag: */ +static NoDbgRegParms sqInt +classForInlineCacheTag(sqInt classIndex) +{ + return classOrNilAtIndex(classIndex); +} + + /* CogObjectRepresentationFor64BitSpur>>#genAddSmallIntegerTagsTo: */ +static NoDbgRegParms sqInt +genAddSmallIntegerTagsTo(sqInt aRegister) +{ + AbstractInstruction *anInstruction; + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, 1, aRegister); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + return 0; +} + + +/* Set the SmallInteger tag bits when the tag bits may be filled with + garbage. + */ + + /* CogObjectRepresentationFor64BitSpur>>#genClearAndSetSmallIntegerTagsIn: */ +static NoDbgRegParms sqInt +genClearAndSetSmallIntegerTagsIn(sqInt scratchReg) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + + quickConstant = -1 - (tagMask()); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* begin OrCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(OrCqR, 1, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + return 0; +} + + +/* Convert the in-SmallFloat64-range floating point value in integer register + into a tagged SmallFloat64 oop. + c.f. Spur64BitMemoryManager>>smallFloatObjectOf: */ + + /* CogObjectRepresentationFor64BitSpur>>#genConvertBitsToSmallFloatIn:scratch: */ +static NoDbgRegParms sqInt +genConvertBitsToSmallFloatInscratch(sqInt reg, sqInt scratch) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpZero; + sqInt quickConstant; + + /* RotateLeftCq:R: */ + genoperandoperand(RotateLeftCqR, 1, reg); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 1, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + jumpZero = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + quickConstant = ((sqInt)((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, quickConstant, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jmpTarget(jumpZero, genoperandoperand(LogicalShiftLeftCqR, numTagBits(), reg)); + quickConstant = smallFloatTag(); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return 0; +} + + +/* Convert the Character in reg to a SmallInteger, assuming + the Character's value is a valid character. */ + + /* CogObjectRepresentationFor64BitSpur>>#genConvertCharacterToSmallIntegerInReg: */ +static NoDbgRegParms void +genConvertCharacterToSmallIntegerInReg(sqInt reg) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + + quickConstant = (characterTag()) - (smallIntegerTag()); + + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, quickConstant, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } +} + + /* CogObjectRepresentationFor64BitSpur>>#genConvertIntegerInReg:toSmallIntegerInReg: */ +static NoDbgRegParms sqInt +genConvertIntegerInRegtoSmallIntegerInReg(sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *anInstruction; + + gLogicalShiftLeftCqRR(numTagBits(), srcReg, destReg); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, 1, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genConvertIntegerToSmallIntegerInReg: */ +static NoDbgRegParms sqInt +genConvertIntegerToSmallIntegerInReg(sqInt reg) +{ + AbstractInstruction *anInstruction; + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, numTagBits(), reg); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, 1, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + return 0; +} + + +/* Convert the SmallFloat in reg to its identityHash as a SmallInteger. + Rotate the sign bit from bit 3 (zero-relative) to the sign bit. + c.f. Spur64BitMemoryManager>>rotatedFloatBitsOf: */ + + /* CogObjectRepresentationFor64BitSpur>>#genConvertSmallFloatToSmallFloatHashAsIntegerInReg:scratch: */ +static NoDbgRegParms sqInt +genConvertSmallFloatToSmallFloatHashAsIntegerInRegscratch(sqInt reg, sqInt scratch) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + + assert(((((usqInt)((smallFloatTag()))) >> 1) - (smallIntegerTag())) == (smallIntegerTag())); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, 1, reg); + gAndCqRR(1U << ((numTagBits()) - 1), reg, scratch); + + /* SubR:R: */ + genoperandoperand(SubRR, scratch, reg); + quickConstant = (((usqInt)((smallFloatTag()))) >> 1) - (smallIntegerTag()); + + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, quickConstant, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + quickConstant = 0x3F - ((numTagBits()) - 1); + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, quickConstant, scratch); + + /* OrR:R: */ + genoperandoperand(OrRR, scratch, reg); + return 0; +} + + +/* Convert the SmallInteger in reg to a Character, assuming + the SmallInteger's value is a valid character. */ + + /* CogObjectRepresentationFor64BitSpur>>#genConvertSmallIntegerToCharacterInReg: */ +static NoDbgRegParms void +genConvertSmallIntegerToCharacterInReg(sqInt reg) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + + quickConstant = (characterTag()) - (smallIntegerTag()); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } +} + + /* CogObjectRepresentationFor64BitSpur>>#genConvertSmallIntegerToIntegerInReg: */ +static NoDbgRegParms sqInt +genConvertSmallIntegerToIntegerInReg(sqInt reg) +{ + /* ArithmeticShiftRightCq:R: */ + genoperandoperand(ArithmeticShiftRightCqR, numTagBits(), reg); + return 0; +} + + +/* The arguments are in an array in Arg1Reg. Its size is in sizeReg. + Load Arg0Reg and Arg1Reg with the first two slots, as appropriate. + Since objects always have at least one slot it is safe to load arg0 + without checking. + But the array could be at the end of memory so we must check that it has + two slots before it is safe to access the second slot. */ + + /* CogObjectRepresentationFor64BitSpur>>#genFetchRegArgsForPerformWithArguments: */ +static NoDbgRegParms sqInt +genFetchRegArgsForPerformWithArguments(sqInt sizeReg) +{ + AbstractInstruction *anInstruction; + sqInt offset; + AbstractInstruction *skip; + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, BaseHeaderSize, Arg1Reg, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 2, sizeReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(2, BytesPerOop)); + } + skip = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + offset = BaseHeaderSize + BytesPerWord; + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, offset, Arg1Reg, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + jmpTarget(skip, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genFloatArithmetic:preOpCheck:boxed: */ +static NoDbgRegParms sqInt +genFloatArithmeticpreOpCheckboxed(sqInt arithmeticOperator, AbstractInstruction *(*preOpCheckOrNil)(int rcvrReg, int argReg), sqInt rcvrBoxed) +{ + AbstractInstruction *doOp; + AbstractInstruction *jumpFailCheck; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpNotBoxedFloat; + AbstractInstruction *jumpNotSmallFloat; + AbstractInstruction *jumpNotSmallInteger; + + jumpFailCheck = ((AbstractInstruction *) 0); + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + if (rcvrBoxed) { + genGetDoubleValueOfinto(ReceiverResultReg, DPFPReg0); + } + else { + genGetSmallFloatValueOfscratchinto(ReceiverResultReg, TempReg, DPFPReg0); + } + jumpNotSmallFloat = genJumpNotSmallFloat(Arg0Reg); + genGetSmallFloatValueOfscratchinto(Arg0Reg, TempReg, DPFPReg1); + doOp = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + if (preOpCheckOrNil) { + jumpFailCheck = preOpCheckOrNil(DPFPReg0, DPFPReg1); + } + genoperandoperand(arithmeticOperator, DPFPReg1, DPFPReg0); + genInstantiateFloatValueintoscratchRegscratchRegmayFail(DPFPReg0, ReceiverResultReg, ClassReg, TempReg, 0); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSmallFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpNotSmallInteger = genJumpNotSmallInteger(Arg0Reg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, Arg1Reg); + genConvertSmallIntegerToIntegerInReg(Arg1Reg); + + /* ConvertR:Rd: */ + genoperandoperand(ConvertRRd, Arg1Reg, DPFPReg1); + + /* Jump: */ + genoperand(Jump, ((sqInt)doOp)); + jmpTarget(jumpNotSmallInteger, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpImmediate = genJumpImmediate(Arg0Reg); + genGetCompactClassIndexNonImmOfinto(Arg0Reg, SendNumArgsReg); + genCmpClassFloatCompactIndexR(SendNumArgsReg); + jumpNotBoxedFloat = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genGetDoubleValueOfinto(Arg0Reg, DPFPReg1); + + /* Jump: */ + genoperand(Jump, ((sqInt)doOp)); + jmpTarget(jumpImmediate, jmpTarget(jumpNotBoxedFloat, jmpTarget(jumpNotSmallInteger, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + if (preOpCheckOrNil) { + jmpTarget(jumpFailCheck, ((AbstractInstruction *) (((jumpNotSmallInteger->operands))[0]))); + } + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genFloatComparison:orIntegerComparison:invert:boxed: */ +static NoDbgRegParms sqInt +genFloatComparisonorIntegerComparisoninvertboxed(AbstractInstruction *(*jumpFPOpcodeGenerator)(void *), sqInt jumpOpcode, sqInt invertComparison, sqInt rcvrBoxed) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *compareFloat; + AbstractInstruction *jumpAmbiguous; + AbstractInstruction *jumpCond; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpNotBoxedFloat; + AbstractInstruction *jumpNotSmallFloat; + AbstractInstruction *jumpNotSmallInteger; + AbstractInstruction *jumpTrue; + AbstractInstruction *returnTrue; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + if (rcvrBoxed) { + genGetDoubleValueOfinto(ReceiverResultReg, DPFPReg0); + } + else { + genGetSmallFloatValueOfscratchinto(ReceiverResultReg, TempReg, DPFPReg0); + } + jumpNotSmallFloat = genJumpNotSmallFloat(Arg0Reg); + genGetSmallFloatValueOfscratchinto(Arg0Reg, TempReg, DPFPReg1); + compareFloat = (invertComparison + ? genoperandoperand(CmpRdRd, DPFPReg0, DPFPReg1) + : genoperandoperand(CmpRdRd, DPFPReg1, DPFPReg0)); + + /* May need to invert for NaNs */ + + /* FP jumps are a little weird */ + jumpCond = jumpFPOpcodeGenerator(0); + + /* begin genMoveConstant:R: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(falseObject(), BytesPerOop)); + } + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpCond, (returnTrue = genMoveConstantR(trueObject(), ReceiverResultReg))); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSmallFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpNotSmallInteger = genJumpNotSmallInteger(Arg0Reg); + + /* Test for ambiguity, that is when floatRcvr == (double) intArg */ + genConvertSmallIntegerToIntegerInReg(Arg0Reg); + + /* ConvertR:Rd: */ + genoperandoperand(ConvertRRd, Arg0Reg, DPFPReg1); + + /* CmpRd:Rd: */ + genoperandoperand(CmpRdRd, DPFPReg0, DPFPReg1); + + /* If floatRcvr == (double) intArg then use compareInt(intArg,(int64) floatRcvr) + else use compareFloat(floatRcvr,(double) intArg) */ + jumpAmbiguous = gJumpFPEqual(0); + + /* Case of non ambiguity, use compareFloat(floatRcvr,(double) intArg) */ + + /* Jump: */ + genoperand(Jump, ((sqInt)compareFloat)); + + /* Case of ambiguity, use compareInt((int64) floatRcvr, intArg) */ + jmpTarget(jumpAmbiguous, genoperandoperand(ConvertRdR, DPFPReg0, ReceiverResultReg)); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, ReceiverResultReg); + jumpTrue = genConditionalBranchoperand(jumpOpcode, 0); + + /* begin genMoveConstant:R: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(falseObject(), BytesPerOop)); + } + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpTrue, returnTrue); + + /* not a Small Float, nor Small Integer, check for Boxed Float argument */ + jmpTarget(jumpNotSmallInteger, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpImmediate = genJumpImmediate(Arg0Reg); + genGetCompactClassIndexNonImmOfinto(Arg0Reg, SendNumArgsReg); + genCmpClassFloatCompactIndexR(SendNumArgsReg); + jumpNotBoxedFloat = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genGetDoubleValueOfinto(Arg0Reg, DPFPReg1); + + /* Jump: */ + genoperand(Jump, ((sqInt)compareFloat)); + jmpTarget(jumpImmediate, jmpTarget(jumpNotBoxedFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + +/* Fetch the instance's identity hash into destReg, encoded as a + SmallInteger. + */ + + /* CogObjectRepresentationFor64BitSpur>>#genGetHashFieldNonImmOf:asSmallIntegerInto: */ +static NoDbgRegParms sqInt +genGetHashFieldNonImmOfasSmallIntegerInto(sqInt instReg, sqInt destReg) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, 0, instReg, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + quickConstant = (identityHashFullWordShift()) - (numTagBits()); + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, quickConstant, destReg); + quickConstant = ((sqInt)((usqInt)((identityHashHalfWordMask())) << (numTagBits()))); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + quickConstant = smallIntegerTag(); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* Shift and mask the field leaving room for the SmallInteger tag. */ + return 0; +} + + +/* Fetch the instance's identity hash into destReg, unencoded. */ + + /* CogObjectRepresentationFor64BitSpur>>#genGetHashFieldNonImmOf:into: */ +static NoDbgRegParms sqInt +genGetHashFieldNonImmOfinto(sqInt instReg, sqInt destReg) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, 0, instReg, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, 32, destReg); + quickConstant = identityHashHalfWordMask(); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return 0; +} + + +/* Extract the inline cache tag for the object in sourceReg into destReg. The + inline cache tag for a given object is the value loaded in inline caches + to distinguish + objects of different classes. In Spur this is either the tags for + immediates, or + the receiver's classIndex. Answer the label for the start of the sequence. */ + + /* CogObjectRepresentationFor64BitSpur>>#genGetInlineCacheClassTagFrom:into:forEntry: */ +static NoDbgRegParms AbstractInstruction * +genGetInlineCacheClassTagFromintoforEntry(sqInt sourceReg, sqInt destReg, sqInt forEntry) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *entryLabel; + AbstractInstruction *jumpImm; + sqInt quickConstant; + + if (forEntry) { + /* AlignmentNops: */ + genoperand(AlignmentNops, BytesPerWord); + } + entryLabel = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + gAndCqRR(tagMask(), sourceReg, destReg); + jumpImm = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Get least significant half of header word in destReg */ + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, 0, sourceReg, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + quickConstant = classIndexMask(); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jmpTarget(jumpImm, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return entryLabel; +} + + +/* Get the size in byte-sized slots of the object in srcReg into destReg. + srcReg may equal destReg. + destReg <- numSlots << self shiftForWord - (fmt bitAnd: 7). + Assumes the object in srcReg has a byte format, i.e. 16 to 23 or 24 to 31 */ + + /* CogObjectRepresentationFor64BitSpur>>#genGetNumBytesOf:into: */ +static NoDbgRegParms sqInt +genGetNumBytesOfinto(sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jmp; + sqInt quickConstant; + + genGetRawSlotSizeOfNonImminto(srcReg, destReg); + quickConstant = numSlotsMask(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jmp = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + genGetOverflowSlotsOfinto(srcReg, destReg); + jmpTarget(jmp, genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), destReg)); + genGetBitsofFormatByteOfinto(7, srcReg, TempReg); + + /* Now: fmt bitAnd: 3 in TempReg */ + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, destReg); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genGetOverflowSlotsOf:into: */ +static NoDbgRegParms sqInt +genGetOverflowSlotsOfinto(sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *anInstruction; + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, -BaseHeaderSize, srcReg, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(-BaseHeaderSize, BytesPerOop)); + } + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, 8, destReg); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, 8, destReg); + return 0; +} + + +/* Convert the SmallFloat oop in ooppReg into the corresponding float value + in dpReg. + c.f. Spur64BitMemoryManager>>smallFloatBitsOf: */ + + /* CogObjectRepresentationFor64BitSpur>>#genGetSmallFloatValueOf:scratch:into: */ +static NoDbgRegParms sqInt +genGetSmallFloatValueOfscratchinto(sqInt oopReg, sqInt scratch, sqInt dpReg) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpSFZero; + sqInt quickConstant; + + /* MoveR:R: */ + genoperandoperand(MoveRR, oopReg, scratch); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, numTagBits(), scratch); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 1, scratch); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + jumpSFZero = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + quickConstant = ((sqInt)((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, scratch); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jmpTarget(jumpSFZero, genoperandoperand(RotateRightCqR, 1, scratch)); + + /* begin MoveR:Rd: */ + assert(BytesPerWord == 8); + genoperandoperand(MoveRRd, scratch, dpReg); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genInstantiate64BitPositiveIntegerValue:into:scratchReg:mayFail: */ +static NoDbgRegParms AbstractInstruction * +genInstantiate64BitPositiveIntegerValueintoscratchRegmayFail(sqInt valueReg, sqInt resultReg, sqInt scratch, sqInt mayFail) +{ + AbstractInstruction *abstractInstruction; + sqInt allocSize; + AbstractInstruction *anInstruction; + AbstractInstruction *inst; + AbstractInstruction *jumpFail; + AbstractInstruction *jumpOK; + usqLong newLPIHeader; + sqInt quickConstant; + + jumpFail = ((AbstractInstruction *) 0); + allocSize = BaseHeaderSize + BytesPerWord; + newLPIHeader = headerForSlotsformatclassIndex(1, firstByteFormat(), ClassLargePositiveIntegerCompactIndex); + if (mayFail) { + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveAwR, freeStartAddress(), scratch)); + quickConstant = (getScavengeThreshold()) - allocSize; + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, scratch); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpFail = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, scratch, resultReg); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, allocSize, scratch); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(allocSize, BytesPerOop)); + } + } + else { + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveAwR, freeStartAddress(), resultReg)); + quickConstant = (getScavengeThreshold()) - allocSize; + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, resultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpOK = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + backEnd; + + /* begin saveAndRestoreLinkRegAround: */ + inst = genoperand(PushR, LinkReg); + abstractInstruction = genoperand(Call, ceScheduleScavengeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + + /* PopR: */ + genoperand(PopR, LinkReg); + jmpTarget(jumpOK, gAddCqRR(allocSize, resultReg, scratch)); + } + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveRAw, scratch, freeStartAddress())); + + /* begin genStoreHeader:intoNewInstance:using: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, newLPIHeader, scratch); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(newLPIHeader, BytesPerOop)); + } + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, scratch, 0, resultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, valueReg, BaseHeaderSize, resultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + return (mayFail + ? jumpFail + : 0); +} + + /* CogObjectRepresentationFor64BitSpur>>#genInstantiate64BitSignedIntegerValue:into:scratchReg:mayFail: */ +static NoDbgRegParms AbstractInstruction * +genInstantiate64BitSignedIntegerValueintoscratchRegmayFail(sqInt valueReg, sqInt resultReg, sqInt scratch, sqInt mayFail) +{ + AbstractInstruction *abstractInstruction; + sqInt allocSize; + AbstractInstruction *anInstruction; + AbstractInstruction *inst; + AbstractInstruction *jumpFail; + AbstractInstruction *jumpNeg; + AbstractInstruction *jumpOK; + usqLong newLNIHeader; + sqInt quickConstant; + + jumpFail = ((AbstractInstruction *) 0); + allocSize = BaseHeaderSize + BytesPerWord; + newLNIHeader = headerForSlotsformatclassIndex(1, firstByteFormat(), ClassLargeNegativeIntegerCompactIndex); + if (mayFail) { + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveAwR, freeStartAddress(), scratch)); + quickConstant = (getScavengeThreshold()) - allocSize; + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, scratch); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpFail = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, scratch, resultReg); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, allocSize, scratch); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(allocSize, BytesPerOop)); + } + } + else { + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveAwR, freeStartAddress(), resultReg)); + quickConstant = (getScavengeThreshold()) - allocSize; + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, resultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpOK = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + backEnd; + + /* begin saveAndRestoreLinkRegAround: */ + inst = genoperand(PushR, LinkReg); + abstractInstruction = genoperand(Call, ceScheduleScavengeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + + /* PopR: */ + genoperand(PopR, LinkReg); + jmpTarget(jumpOK, gAddCqRR(allocSize, resultReg, scratch)); + } + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveRAw, scratch, freeStartAddress())); + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, newLNIHeader, scratch); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(newLNIHeader, BytesPerOop)); + } + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, valueReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpNeg = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* We can avoid duplicating the large constant and a jump choosing between + the alternatives by incrementing the single constant if positive. The assert + checks that the hack works. Compact code is to be preferred because it is + an uncommon case; usually the value will fit in a SmallInteger. */ + assert((headerForSlotsformatclassIndex(0, 0, 1)) == 1); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, ClassLargePositiveIntegerCompactIndex - ClassLargeNegativeIntegerCompactIndex, scratch); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(ClassLargePositiveIntegerCompactIndex - ClassLargeNegativeIntegerCompactIndex, BytesPerOop)); + } + jmpTarget(jumpNeg, gMoveRMwr(scratch, 0, resultReg)); + + /* The old less compact code was + cogit CmpCq: 0 R: valueReg. + jumpNeg := cogit JumpLess: 0. + cogit MoveCq: newLPIHeader R: scratch1. + jumpJoin := cogit Jump: 0. + jumpNeg jmpTarget: + (cogit MoveCq: newLNIHeader R: scratch1). + jumpJoin jmpTarget: + (cogit MoveR: scratch1 Mw: 0 r: resultReg). */ + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, valueReg, BaseHeaderSize, resultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + return (mayFail + ? jumpFail + : 0); +} + + +/* Allocate a new float instance with the value in the double-precision + register dpreg. + Do *not* use scratch2, which exists only for compatibility with V3. + If the allocation succeeds assign it to resultReg. If mayFail is false + always allocate. + If mayFail is true and the allocation fails do *not* update resultReg. + If mayFail is true answer the jump to take on failure, otherwise answer + nil. */ +/* Override to answer a SmallFloat64 if possible. */ + + /* CogObjectRepresentationFor64BitSpur>>#genInstantiateFloatValue:into:scratchReg:scratchReg:mayFail: */ +static NoDbgRegParms AbstractInstruction * +genInstantiateFloatValueintoscratchRegscratchRegmayFail(sqInt dpreg, sqInt resultReg, sqInt scratch1, sqInt scratch2, sqInt mayFail) +{ + AbstractInstruction *abstractInstruction; + usqIntptr_t allocSize; + AbstractInstruction *anInstruction; + AbstractInstruction *inst; + AbstractInstruction *jumpFail; + AbstractInstruction *jumpMerge; + AbstractInstruction *jumpNotSF; + AbstractInstruction *jumpOK; + usqLong newFloatHeader; + sqInt quickConstant; + + jumpFail = ((AbstractInstruction *) 0); + + /* begin MoveRd:R: */ + assert(BytesPerWord == 8); + genoperandoperand(MoveRdR, dpreg, resultReg); + jumpNotSF = genJumpNotSmallFloatValueBitsscratch(resultReg, scratch1); + genConvertBitsToSmallFloatInscratch(resultReg, scratch1); + jumpMerge = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpNotSF, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + allocSize = BaseHeaderSize + (sizeof(double)); + if (mayFail) { + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveAwR, freeStartAddress(), scratch1)); + quickConstant = (getScavengeThreshold()) - allocSize; + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, scratch1); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpFail = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, scratch1, resultReg); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, allocSize, scratch1); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(allocSize, BytesPerOop)); + } + } + else { + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveAwR, freeStartAddress(), resultReg)); + quickConstant = (getScavengeThreshold()) - allocSize; + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, resultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpOK = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + backEnd; + + /* begin saveAndRestoreLinkRegAround: */ + inst = genoperand(PushR, LinkReg); + abstractInstruction = genoperand(Call, ceScheduleScavengeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + + /* PopR: */ + genoperand(PopR, LinkReg); + jmpTarget(jumpOK, gAddCqRR(allocSize, resultReg, scratch1)); + } + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveRAw, scratch1, freeStartAddress())); + newFloatHeader = headerForSlotsformatclassIndex((sizeof(double)) / BytesPerWord, firstLongFormat(), ClassFloatCompactIndex); + + /* begin genStoreHeader:intoNewInstance:using: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, newFloatHeader, scratch1); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(newFloatHeader, BytesPerOop)); + } + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, scratch1, 0, resultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* begin MoveRd:M64:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRdM64r, dpreg, BaseHeaderSize, resultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + jumpFail = (mayFail + ? jumpFail + : ((AbstractInstruction *) null)); + jmpTarget(jumpMerge, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return jumpFail; +} + + +/* Generate a test for aRegister containing an integer value in the + SmallInteger range, and a jump if so, answering the jump. + c.f. Spur64BitMemoryManager>>isIntegerValue: */ + + /* CogObjectRepresentationFor64BitSpur>>#genJumpIsSmallIntegerValue:scratch: */ +static NoDbgRegParms AbstractInstruction * +genJumpIsSmallIntegerValuescratch(sqInt aRegister, sqInt scratchReg) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + + gArithmeticShiftRightCqRR(0x3F - (numTagBits()), aRegister, scratchReg); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, 1, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + quickConstant = (1U << ((numTagBits()) + 1)) - 1; + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 1, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + return genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); +} + + +/* Generate a compare and branch to test if aRegister contains other than a + Character. + */ + + /* CogObjectRepresentationFor64BitSpur>>#genJumpNotCharacter: */ +static NoDbgRegParms AbstractInstruction * +genJumpNotCharacter(sqInt reg) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + + quickConstant = characterTag(); + + /* begin TstCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(TstCqR, quickConstant, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return genConditionalBranchoperand(JumpZero, ((sqInt)0)); +} + + +/* Generate a test to check that the integer register contains a floating + point value within the SmallFloat64 range, + and answer the jump. c.f. Spur64BitMemoryManager>>isSmallFloatValue: */ + + /* CogObjectRepresentationFor64BitSpur>>#genJumpNotSmallFloatValueBits:scratch: */ +static NoDbgRegParms AbstractInstruction * +genJumpNotSmallFloatValueBitsscratch(sqInt reg, sqInt exponent) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpFail; + AbstractInstruction *jumpMaxExponent; + AbstractInstruction *jumpMinExponent; + AbstractInstruction *jumpTest; + AbstractInstruction *jumpZeroMantissa; + sqInt quickConstant; + + gLogicalShiftLeftCqRR(1, reg, exponent); + quickConstant = (smallFloatMantissaBits()) + 1; + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, quickConstant, exponent); + quickConstant = smallFloatExponentOffset(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, exponent); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpMinExponent = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + quickConstant = 0xFF + (smallFloatExponentOffset()); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, exponent); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpMaxExponent = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + jumpFail = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpMinExponent, gTstCqR((1ULL << (smallFloatMantissaBits())) - 1, reg)); + jumpZeroMantissa = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + quickConstant = smallFloatExponentOffset(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, exponent); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpTest = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpZeroMantissa, gCmpCqR(0, exponent)); + jmpTarget(jumpTest, genConditionalBranchoperand(JumpNonZero, ((sqInt)jumpFail))); + jmpTarget(jumpMaxExponent, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return jumpFail; +} + + +/* Generate a compare and branch to test if aRegister contains other than a + SmallFloat. Answer the jump. */ + + /* CogObjectRepresentationFor64BitSpur>>#genJumpNotSmallFloat: */ +static NoDbgRegParms AbstractInstruction * +genJumpNotSmallFloat(sqInt reg) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + + quickConstant = smallFloatTag(); + + /* begin TstCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(TstCqR, quickConstant, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return genConditionalBranchoperand(JumpZero, ((sqInt)0)); +} + + +/* Generate a test for aRegister containing an integer value outside the + SmallInteger range, and a jump if so, answering the jump. + c.f. Spur64BitMemoryManager>>isIntegerValue: */ + + /* CogObjectRepresentationFor64BitSpur>>#genJumpNotSmallIntegerValue:scratch: */ +static NoDbgRegParms AbstractInstruction * +genJumpNotSmallIntegerValuescratch(sqInt aRegister, sqInt scratchReg) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + + gArithmeticShiftRightCqRR(0x3F - (numTagBits()), aRegister, scratchReg); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, 1, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + quickConstant = (1U << ((numTagBits()) + 1)) - 1; + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 1, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + return genConditionalBranchoperand(JumpGreater, ((sqInt)0)); +} + + +/* Generate a compare and branch to test if aRegister contains other than a + SmallInteger. + */ + + /* CogObjectRepresentationFor64BitSpur>>#genJumpNotSmallInteger: */ +static NoDbgRegParms AbstractInstruction * +genJumpNotSmallInteger(sqInt reg) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + + quickConstant = smallIntegerTag(); + + /* begin TstCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(TstCqR, quickConstant, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return genConditionalBranchoperand(JumpZero, ((sqInt)0)); +} + + +/* Generate a compare and branch to test if aRegister contains a + SmallInteger. Answer the jump, or UnimplementedOperation if this cannot be + done with + a single register. */ + + /* CogObjectRepresentationFor64BitSpur>>#genJumpSmallInteger: */ +static NoDbgRegParms AbstractInstruction * +genJumpSmallInteger(sqInt aRegister) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + + quickConstant = smallIntegerTag(); + + /* begin TstCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(TstCqR, quickConstant, aRegister); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +} + + +/* Generate the code for primitives 61 & 165, at:put:/basicAt:put: & + integerAt:put:. If signedVersion is true + then generate signed accesses to the bits classes (a la 164 & 165). If + signedVersion is false, + generate unsigned accesses (a la 60, 61, 63 & 64). */ +/* c.f. StackInterpreter>>stSizeOf: SpurMemoryManager>>lengthOf:format: + fixedFieldsOf:format:length: + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveAtPutSigned: */ +static NoDbgRegParms sqInt +genPrimitiveAtPutSigned(sqInt signedVersion) +{ + AbstractInstruction *anInstruction; + sqInt formatReg; + AbstractInstruction *jump64BitArgIsImmediate; + AbstractInstruction *jump64BitsOutOfBounds; + AbstractInstruction *jumpArrayOutOfBounds; + AbstractInstruction *jumpBadIndex; + AbstractInstruction *jumpBytesOutOfBounds; + AbstractInstruction *jumpBytesOutOfRange; + AbstractInstruction *jumpDoubleWordsOutOfRange; + AbstractInstruction *jumpFixedFieldsOutOfBounds; + AbstractInstruction *jumpHasFixedFields; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpImmutable; + AbstractInstruction *jumpIsBytes; + AbstractInstruction *jumpIsCompiledMethod; + AbstractInstruction *jumpIsContext; + AbstractInstruction *jumpIsShorts; + AbstractInstruction *jumpIsWords; + AbstractInstruction *jumpNegative; + AbstractInstruction *jumpNonSmallIntegerValue; + AbstractInstruction *jumpNot64BitIndexable; + AbstractInstruction *jumpNot8ByteInteger; + AbstractInstruction *jumpNotIndexableBits; + AbstractInstruction *jumpNotIndexablePointers; + AbstractInstruction *jumpNotPointers; + AbstractInstruction *jumpShortsOutOfBounds; + AbstractInstruction *jumpShortsOutOfRange; + AbstractInstruction *jumpWordsOutOfBounds; + AbstractInstruction *jumpWordsOutOfRange; + AbstractInstruction *methodInBounds; + sqInt nSlotsOrBytesReg; + sqInt quickConstant; + AbstractInstruction *rejoin; + + jumpDoubleWordsOutOfRange = ((AbstractInstruction *) 0); + jumpNegative = ((AbstractInstruction *) 0); + nSlotsOrBytesReg = ClassReg; + + /* begin genLoadArgAtDepth:into: */ + assert(1 < (numRegArgs())); + assert(0 < (numRegArgs())); + jumpImmediate = genJumpImmediate(ReceiverResultReg); + jumpBadIndex = genJumpNotSmallInteger(Arg0Reg); + genConvertSmallIntegerToIntegerInReg(Arg0Reg); + + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, 1, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } +# if IMMUTABILITY + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), TempReg); + + /* begin genJumpBaseHeaderImmutable: */ + quickConstant = immutableBitMask(); + anInstruction = genoperandoperand(TstCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpImmutable = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +# else // IMMUTABILITY + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), NoReg); +# endif // IMMUTABILITY + + genGetNumSlotsOfinto(ReceiverResultReg, nSlotsOrBytesReg); + + /* dispatch on format in a combination of highest dynamic frequency order first and convenience. + 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + quickConstant = weakArrayFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpNotPointers = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* optimistic store check; assume index in range (almost always is). */ + genStoreCheckReceiverRegvalueRegscratchReginFrame(ReceiverResultReg, Arg1Reg, TempReg, 0); + quickConstant = arrayFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpNotIndexablePointers = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + jumpHasFixedFields = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpArrayOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + quickConstant = ((usqInt)(BaseHeaderSize)) >> (shiftForWord()); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, Arg1Reg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpHasFixedFields, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genGetClassIndexOfNonImminto(ReceiverResultReg, formatReg); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, ClassMethodContextCompactIndex, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(ClassMethodContextCompactIndex, BytesPerOop)); + } + jumpIsContext = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* get # fixed fields in formatReg */ + genGetClassObjectOfClassIndexintoscratchReg(formatReg, Extra0Reg, TempReg); + genLoadSlotsourceRegdestReg(InstanceSpecificationIndex, Extra0Reg, formatReg); + genConvertSmallIntegerToIntegerInReg(formatReg); + quickConstant = fixedFieldsOfClassFormatMask(); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrBytesReg); + quickConstant = ((usqInt)(BaseHeaderSize)) >> (shiftForWord()); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpFixedFieldsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* AddR:R: */ + genoperandoperand(AddRR, formatReg, Arg0Reg); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, Arg1Reg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotPointers, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpNonSmallIntegerValue = genJumpNotSmallInteger(Arg1Reg); + quickConstant = firstByteFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsBytes = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + quickConstant = firstShortFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsShorts = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + quickConstant = firstLongFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsWords = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + quickConstant = sixtyFourBitIndexableFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpNotIndexableBits = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* fall through to 64-bit words */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, SendNumArgsReg); + genConvertSmallIntegerToIntegerInReg(SendNumArgsReg); + if (!signedVersion) { + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpNegative = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + } + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + rejoin = genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jump64BitsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + quickConstant = ((usqInt)(BaseHeaderSize)) >> (shiftForWord()); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, SendNumArgsReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* Handle LargePositiveInteger (unsigned) or LargePositiveInteger and LargeNegativeInteger (signed) + for sixtyFourBitIndexableFormat */ + jmpTarget(jumpNonSmallIntegerValue, gCmpCqR(sixtyFourBitIndexableFormat(), formatReg)); + jumpNot64BitIndexable = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + jump64BitArgIsImmediate = genJumpImmediate(Arg1Reg); + if (signedVersion) { + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, BaseHeaderSize, Arg1Reg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpDoubleWordsOutOfRange = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + } + + /* Test top bit of 64-bit word in large integer for range check. + Now check if the header is that of an 8 byte LargePositiveInteger */ + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, 0, Arg1Reg, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + quickConstant = headerForSlotsformatclassIndex(numSlotsMask(), formatMask(), classIndexMask()); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + quickConstant = headerForSlotsformatclassIndex(1, firstByteFormat(), ClassLargePositiveIntegerCompactIndex); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpNot8ByteInteger = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, BaseHeaderSize, Arg1Reg, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + + /* Jump: */ + genoperand(Jump, ((sqInt)rejoin)); + if (signedVersion) { + /* Now check if the header is that of an 8 byte LargeNegativeInteger */ + jmpTarget(jumpNot8ByteInteger, gCmpCqR(headerForSlotsformatclassIndex(1, firstByteFormat(), ClassLargeNegativeIntegerCompactIndex), SendNumArgsReg)); + jumpNot8ByteInteger = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, BaseHeaderSize, Arg1Reg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, 0, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, SendNumArgsReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)rejoin)); + } + if (signedVersion) { + jmpTarget(jumpIsWords, gArithmeticShiftRightCqRR(0x1F + (numTagBits()), Arg1Reg, TempReg)); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, 1, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 1, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + } + else { + jmpTarget(jumpIsWords, gCmpCqR((((usqInt)0xFFFFFFFFU << 3) | 1), Arg1Reg)); + } + jumpWordsOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - 2, nSlotsOrBytesReg); + quickConstant = (BytesPerWord / 4) - 1; + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrBytesReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpWordsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + quickConstant = ((usqInt)(BaseHeaderSize)) >> ((shiftForWord()) - 1); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* MoveR:X32r:R: */ + genoperandoperandoperand(MoveRX32rR, TempReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + if (signedVersion) { + jmpTarget(jumpIsBytes, gArithmeticShiftRightCqRR(7 + (numTagBits()), Arg1Reg, TempReg)); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, 1, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 1, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + } + else { + jmpTarget(jumpIsBytes, gCmpCqR((((usqInt)0xFF << 3) | 1), Arg1Reg)); + } + jumpBytesOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), nSlotsOrBytesReg); + gAndCqRR(BytesPerWord - 1, formatReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, nSlotsOrBytesReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpBytesOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + quickConstant = firstCompiledMethodFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsCompiledMethod = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + methodInBounds = genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, BaseHeaderSize, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + + /* MoveR:Xbr:R: */ + genoperandoperandoperand(MoveRXbrR, TempReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + if (signedVersion) { + jmpTarget(jumpIsShorts, gArithmeticShiftRightCqRR(15 + (numTagBits()), Arg1Reg, TempReg)); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, 1, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 1, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + } + else { + jmpTarget(jumpIsShorts, gCmpCqR((((usqInt)0xFFFF << 3) | 1), Arg1Reg)); + } + jumpShortsOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - 1, nSlotsOrBytesReg); + quickConstant = (BytesPerWord / 2) - 1; + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrBytesReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpShortsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg0Reg, ReceiverResultReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg0Reg, ReceiverResultReg); + + /* begin MoveR:M16:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRM16r, TempReg, BaseHeaderSize, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* Now check that the index is beyond the method's literals... */ + jmpTarget(jumpIsCompiledMethod, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + getLiteralCountOfplusOneinBytesintoscratch(ReceiverResultReg, 1, 1, nSlotsOrBytesReg, TempReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + + /* JumpBelow: */ + genConditionalBranchoperand(JumpBelow, ((sqInt)methodInBounds)); + jmpTarget(jumpNot8ByteInteger, jmpTarget(jump64BitArgIsImmediate, jmpTarget(jumpNot64BitIndexable, jmpTarget(jumpIsContext, jmpTarget(jumpNotIndexableBits, jmpTarget(jumpBytesOutOfRange, jmpTarget(jumpShortsOutOfRange, jmpTarget(jumpWordsOutOfRange, jmpTarget(jumpIsCompiledMethod, jmpTarget(jumpArrayOutOfBounds, jmpTarget(jumpBytesOutOfBounds, jmpTarget(jumpShortsOutOfBounds, jmpTarget(jumpWordsOutOfBounds, jmpTarget(jump64BitsOutOfBounds, jmpTarget(jumpNotIndexablePointers, jmpTarget(jumpFixedFieldsOutOfBounds, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))))))))))))))); + if (signedVersion) { + jmpTarget(jumpDoubleWordsOutOfRange, ((AbstractInstruction *) (((jumpIsContext->operands))[0]))); + } + else { + jmpTarget(jumpNegative, ((AbstractInstruction *) (((jumpIsContext->operands))[0]))); + } +# if IMMUTABILITY + jmpTarget(jumpImmutable, ((AbstractInstruction *) (((jumpIsContext->operands))[0]))); +# endif + + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, 1, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + genConvertIntegerToSmallIntegerInReg(Arg0Reg); + jmpTarget(jumpBadIndex, jmpTarget(jumpImmediate, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return 0; +} + + +/* Generate the code for primitives 60 & 164, at:/basicAt: & integerAt:. If + signedVersion is true + then generate signed accesses to the bits classes (a la 164 & 165). If + signedVersion is false, + generate unsigned accesses (a la 60, 61, 63 & 64). */ +/* c.f. StackInterpreter>>stSizeOf: SpurMemoryManager>>lengthOf:format: + fixedFieldsOf:format:length: + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveAtSigned: */ +static NoDbgRegParms sqInt +genPrimitiveAtSigned(sqInt signedVersion) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *convertToIntAndReturn; + sqInt formatReg; + AbstractInstruction *jumpArrayOutOfBounds; + AbstractInstruction *jumpBadIndex; + AbstractInstruction *jumpBytesOutOfBounds; + AbstractInstruction *jumpFixedFieldsOutOfBounds; + AbstractInstruction *jumpHasFixedFields; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpIsArray; + AbstractInstruction *jumpIsBytes; + AbstractInstruction *jumpIsContext; + AbstractInstruction *jumpIsLongs; + AbstractInstruction *jumpIsMethod; + AbstractInstruction *jumpIsShorts; + AbstractInstruction *jumpIsWords; + AbstractInstruction *jumpLongsOutOfBounds; + AbstractInstruction *jumpNotIndexable; + AbstractInstruction *jumpNotSmallInteger; + AbstractInstruction *jumpShortsOutOfBounds; + AbstractInstruction *jumpWordsOutOfBounds; + AbstractInstruction *methodInBounds; + sqInt nSlotsOrElementsReg; + sqInt quickConstant; + + nSlotsOrElementsReg = ClassReg; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpImmediate = genJumpImmediate(ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, Arg1Reg); + jumpBadIndex = genJumpNotSmallInteger(Arg0Reg); + genConvertSmallIntegerToIntegerInReg(Arg1Reg); + + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, 1, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), TempReg); + genGetNumSlotsOfinto(ReceiverResultReg, nSlotsOrElementsReg); + + /* dispatch on format in a combination of highest dynamic frequency order first and convenience. + 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + quickConstant = firstByteFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsBytes = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + quickConstant = arrayFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsArray = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + jumpNotIndexable = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + quickConstant = weakArrayFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpHasFixedFields = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + quickConstant = firstShortFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsShorts = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + quickConstant = firstLongFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsWords = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + quickConstant = sixtyFourBitIndexableFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsLongs = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + jmpTarget(jumpNotIndexable, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpNotIndexable = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpIsArray, + + (/* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)), + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg))); + jumpArrayOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + quickConstant = ((usqInt)(BaseHeaderSize)) >> (shiftForWord()); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, Arg1Reg, ReceiverResultReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsBytes, genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), nSlotsOrElementsReg)); + gAndCqRR(7, formatReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, nSlotsOrElementsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpBytesOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + quickConstant = firstCompiledMethodFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsMethod = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + methodInBounds = genoperandoperand(AddCqR, BaseHeaderSize, Arg1Reg); + if (usesOutOfLineLiteral(methodInBounds)) { + (methodInBounds->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + + /* MoveXbr:R:R: */ + genoperandoperandoperand(MoveXbrRR, Arg1Reg, ReceiverResultReg, ReceiverResultReg); + if (signedVersion) { + /* SignExtend8R:R: */ + genoperandoperand(SignExtend8RR, ReceiverResultReg, ReceiverResultReg); + } + convertToIntAndReturn = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + genConvertIntegerToSmallIntegerInReg(ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsShorts, gLogicalShiftLeftCqR((shiftForWord()) - 1, nSlotsOrElementsReg)); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, 3, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(3, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrElementsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpShortsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg1Reg, ReceiverResultReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg1Reg, ReceiverResultReg); + + /* begin MoveM16:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveM16rR, BaseHeaderSize, ReceiverResultReg, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + if (signedVersion) { + /* SignExtend16R:R: */ + genoperandoperand(SignExtend16RR, ReceiverResultReg, ReceiverResultReg); + } + + /* Jump: */ + genoperand(Jump, ((sqInt)convertToIntAndReturn)); + jmpTarget(jumpIsWords, gLogicalShiftLeftCqR((shiftForWord()) - 2, nSlotsOrElementsReg)); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, 1, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrElementsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpWordsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + quickConstant = ((usqInt)(BaseHeaderSize)) >> ((shiftForWord()) - 1); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* MoveX32r:R:R: */ + genoperandoperandoperand(MoveX32rRR, Arg1Reg, ReceiverResultReg, TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, ReceiverResultReg); + if (signedVersion) { + /* SignExtend32R:R: */ + genoperandoperand(SignExtend32RR, ReceiverResultReg, ReceiverResultReg); + } + + /* Jump: */ + genoperand(Jump, ((sqInt)convertToIntAndReturn)); + jmpTarget(jumpHasFixedFields, gAndCqR(classIndexMask(), TempReg)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, formatReg); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, ClassMethodContextCompactIndex, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(ClassMethodContextCompactIndex, BytesPerOop)); + } + jumpIsContext = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + genGetClassObjectOfClassIndexintoscratchReg(formatReg, Extra0Reg, TempReg); + genLoadSlotsourceRegdestReg(InstanceSpecificationIndex, Extra0Reg, formatReg); + genConvertSmallIntegerToIntegerInReg(formatReg); + quickConstant = fixedFieldsOfClassFormatMask(); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrElementsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpFixedFieldsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* index is (formatReg (fixed fields) + Arg1Reg (0-rel index)) * wordSize + baseHeaderSize */ + + /* AddR:R: */ + genoperandoperand(AddRR, formatReg, Arg1Reg); + quickConstant = ((usqInt)(BaseHeaderSize)) >> (shiftForWord()); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, Arg1Reg, ReceiverResultReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsLongs, + + (/* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)), + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg))); + jumpLongsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + quickConstant = ((usqInt)(BaseHeaderSize)) >> (shiftForWord()); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, Arg1Reg, ReceiverResultReg, ClassReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, TempReg); + if (signedVersion) { + /* ArithmeticShiftRightCq:R: */ + genoperandoperand(ArithmeticShiftRightCqR, numSmallIntegerBits(), TempReg); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 1, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + jumpNotSmallInteger = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, ReceiverResultReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)convertToIntAndReturn)); + jmpTarget(jumpNotSmallInteger, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genInstantiate64BitSignedIntegerValueintoscratchRegmayFail(ClassReg, ReceiverResultReg, TempReg, 0); + } + else { + quickConstant = (numSmallIntegerBits()) - 1; + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, quickConstant, TempReg); + anInstruction = genoperandoperand(CmpCqR, 0, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpNotSmallInteger = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, ReceiverResultReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)convertToIntAndReturn)); + jmpTarget(jumpNotSmallInteger, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genInstantiate64BitPositiveIntegerValueintoscratchRegmayFail(ClassReg, ReceiverResultReg, TempReg, 0); + } + + /* c.f. Spur64BitMemoryManager>>#isIntegerValue: */ + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsMethod, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* Now check that the index is beyond the method's literals... */ + getLiteralCountOfplusOneinBytesintoscratch(ReceiverResultReg, 1, 1, nSlotsOrElementsReg, TempReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + + /* JumpBelow: */ + genConditionalBranchoperand(JumpBelow, ((sqInt)methodInBounds)); + jmpTarget(jumpLongsOutOfBounds, jmpTarget(jumpFixedFieldsOutOfBounds, jmpTarget(jumpArrayOutOfBounds, jmpTarget(jumpBytesOutOfBounds, jmpTarget(jumpShortsOutOfBounds, jmpTarget(jumpWordsOutOfBounds, jmpTarget(jumpNotIndexable, jmpTarget(jumpIsContext, jmpTarget(jumpBadIndex, jmpTarget(jumpImmediate, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))))))))); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveFloatEqual */ +static sqInt +genPrimitiveFloatEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPEqual, JumpZero, 0, 1) + : genPureFloatComparisoninvertboxed(gJumpFPEqual, 0, 1)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveFloatGreaterOrEqual */ +static sqInt +genPrimitiveFloatGreaterOrEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreaterOrEqual, JumpGreaterOrEqual, 0, 1) + : genPureFloatComparisoninvertboxed(gJumpFPGreaterOrEqual, 0, 1)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveFloatGreaterThan */ +static sqInt +genPrimitiveFloatGreaterThan(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreater, JumpGreater, 0, 1) + : genPureFloatComparisoninvertboxed(gJumpFPGreater, 0, 1)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveFloatLessOrEqual */ +static sqInt +genPrimitiveFloatLessOrEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreaterOrEqual, JumpLessOrEqual, 1, 1) + : genPureFloatComparisoninvertboxed(gJumpFPGreaterOrEqual, 1, 1)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveFloatLessThan */ +static sqInt +genPrimitiveFloatLessThan(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreater, JumpLess, 1, 1) + : genPureFloatComparisoninvertboxed(gJumpFPGreater, 1, 1)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveFloatNotEqual */ +static sqInt +genPrimitiveFloatNotEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPNotEqual, JumpNonZero, 0, 1) + : genPureFloatComparisoninvertboxed(gJumpFPNotEqual, 0, 1)); +} + + +/* Arguably we should fail for immediates, but so far no one has complained, + so... + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveIdentityHash */ +static sqInt +genPrimitiveIdentityHash(void) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpImm; + AbstractInstruction *jumpNotCharacter; + AbstractInstruction *jumpNotSet; + sqInt quickConstant; + AbstractInstruction *ret; + + /* uses TstCqR */ + jumpImm = genJumpImmediate(ReceiverResultReg); + genGetHashFieldNonImmOfasSmallIntegerInto(ReceiverResultReg, TempReg); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, ConstZero, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(ConstZero, BytesPerOop)); + } + jumpNotSet = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, ReceiverResultReg); + ret = /* genPrimReturn */ + (methodOrBlockNumArgs <= (numRegArgs()) + ? genoperand(RetN, 0) + : genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord)); + jmpTarget(jumpImm, gAndCqRR(tagMask(), ReceiverResultReg, TempReg)); + quickConstant = characterTag(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpNotCharacter = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genConvertCharacterToSmallIntegerInReg(ReceiverResultReg); + ret = /* genPrimReturn */ + (methodOrBlockNumArgs <= (numRegArgs()) + ? genoperand(RetN, 0) + : genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord)); + jmpTarget(jumpNotCharacter, gCmpCqR(smallFloatTag(), TempReg)); + + /* JumpNonZero: */ + genConditionalBranchoperand(JumpNonZero, ((sqInt)ret)); + genConvertSmallFloatToSmallFloatHashAsIntegerInRegscratch(ReceiverResultReg, TempReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSet, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* For non-behaviors we can call ceNewHash to assign a fresh hash. + For behaviors we must fail into the interpreter primitive to enter the receiver into the class table. */ + if (!(primitiveIndex == 75)) { + return 0; + } + compileInvokeCENewHashOf(); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + return UnfailingPrimitive; +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveImmediateAsInteger */ +static sqInt +genPrimitiveImmediateAsInteger(void) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpNotCharacter; + sqInt quickConstant; + AbstractInstruction *ret; + + gAndCqRR(tagMask(), ReceiverResultReg, TempReg); + quickConstant = characterTag(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpNotCharacter = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genConvertCharacterToSmallIntegerInReg(ReceiverResultReg); + ret = /* genPrimReturn */ + (methodOrBlockNumArgs <= (numRegArgs()) + ? genoperand(RetN, 0) + : genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord)); + jmpTarget(jumpNotCharacter, gCmpCqR(smallFloatTag(), TempReg)); + + /* JumpNonZero: */ + genConditionalBranchoperand(JumpNonZero, ((sqInt)ret)); + genConvertSmallFloatToSmallFloatHashAsIntegerInRegscratch(ReceiverResultReg, TempReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + return UnfailingPrimitive; +} + + +/* Implement primitiveNew for convenient cases: + - the receiver has a hash + - the receiver is fixed size (excluding ephemerons to save instructions & + miniscule time) + - single word header/num slots < numSlotsMask + - the result fits in eden (actually below scavengeThreshold) + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveNew */ +static sqInt +genPrimitiveNew(void) +{ + AbstractInstruction *anInstruction; + sqInt byteSizeReg; + AbstractInstruction *fillLoop; + sqInt fillReg; + sqInt headerReg; + sqInt instSpecReg; + AbstractInstruction *jumpHasSlots; + AbstractInstruction *jumpNoSpace; + AbstractInstruction *jumpTooBig; + AbstractInstruction *jumpUnhashed; + AbstractInstruction *jumpVariableOrEphemeron; + sqInt quickConstant; + AbstractInstruction *skip; + + if (methodOrBlockNumArgs) { + return UnimplementedPrimitive; + } + + /* header will contain classIndex/class's hash & format & numSlots/fixed size and finally fill value (nilObject). */ + headerReg = (fillReg = SendNumArgsReg); + + /* inst spec will hold class's instance specification, then byte size and finally end of new object. */ + instSpecReg = (byteSizeReg = ClassReg); + + /* get freeStart as early as possible so as not to wait later... */ + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveAwR, freeStartAddress(), Arg1Reg)); + + /* get class's hash & fail if 0 */ + genGetHashFieldNonImmOfinto(ReceiverResultReg, headerReg); + jumpUnhashed = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* get class's format inst var for both inst spec (format field) and num fixed fields */ + genLoadSlotsourceRegdestReg(InstanceSpecificationIndex, ReceiverResultReg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, instSpecReg); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, fixedFieldsFieldWidth(), TempReg); + quickConstant = formatMask(); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + quickConstant = fixedFieldsOfClassFormatMask(); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, instSpecReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* fail if not fixed or if ephemeron (rare beasts so save the cycles) */ + quickConstant = nonIndexablePointerFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpVariableOrEphemeron = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + quickConstant = numSlotsMask(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, instSpecReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpTooBig = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* Add format to classIndex/format in header; the add in numSlots */ + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, instSpecReg, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, numSlotsFullShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* compute byte size; remember 0-sized objects still need 1 slot. */ + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, byteSizeReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpHasSlots = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, BaseHeaderSize * 2, byteSizeReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize * 2, BytesPerOop)); + } + skip = genoperand(Jump, ((sqInt)0)); + + /* round up to allocationUnit */ + jmpTarget(jumpHasSlots, gAddCqR(BaseHeaderSize / BytesPerWord, byteSizeReg)); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), byteSizeReg); + jmpTarget(skip, genoperandoperand(AddRR, Arg1Reg, byteSizeReg)); + + /* check if allocation fits (freeSize + byteSize < scavengeThreshold); scavengeThreshold is constant. */ + quickConstant = getScavengeThreshold(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, byteSizeReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpNoSpace = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* write back new freeStart; get result. byteSizeReg holds new freeStart, the limit of the object */ + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveRAw, byteSizeReg, freeStartAddress())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* write header */ + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, headerReg, 0, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* now fill */ + + /* begin LoadEffectiveAddressMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(LoadEffectiveAddressMwrR, BaseHeaderSize, ReceiverResultReg, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, nilObject(), fillReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(nilObject(), BytesPerOop)); + } + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + fillLoop = genoperandoperandoperand(MoveRMwr, fillReg, 0, Arg1Reg); + if (usesOutOfLineLiteral(fillLoop)) { + (fillLoop->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, 8, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(8, BytesPerOop)); + } + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, byteSizeReg); + + /* JumpAbove: */ + genConditionalBranchoperand(JumpAbove, ((sqInt)fillLoop)); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpUnhashed, jmpTarget(jumpVariableOrEphemeron, jmpTarget(jumpTooBig, jmpTarget(jumpNoSpace, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))); + return 0; +} + + +/* Implement primitiveNewWithArg for convenient cases: + - the receiver has a hash + - the receiver is variable and not compiled method + - single word header/num slots < numSlotsMask + - the result fits in eden + See superclass method for dynamic frequencies of formats. + For the moment we implement only arrayFormat, firstByteFormat & + firstLongFormat + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveNewWithArg */ +static sqInt +genPrimitiveNewWithArg(void) +{ + AbstractInstruction *anInstruction; + sqInt byteSizeReg; + AbstractInstruction *fillLoop; + sqInt fillReg; + sqInt headerReg; + sqInt instSpecReg; + AbstractInstruction *jumpArrayFormat; + AbstractInstruction *jumpArrayTooBig; + AbstractInstruction *jumpByteFormat; + AbstractInstruction *jumpBytePrepDone; + AbstractInstruction *jumpByteTooBig; + AbstractInstruction *jumpFailCuzFixed; + AbstractInstruction *jumpHasSlots; + AbstractInstruction *jumpLongPrepDone; + AbstractInstruction *jumpLongTooBig; + AbstractInstruction *jumpNElementsNonInt; + AbstractInstruction *jumpNoSpace; + AbstractInstruction *jumpUnhashed; + sqInt maxSlots; + sqInt quickConstant; + AbstractInstruction *skip; + + if (methodOrBlockNumArgs != 1) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* header will contain classIndex/class's hash & format & numSlots/fixed size */ + headerReg = SendNumArgsReg; + + /* Assume there's an available scratch register on 64-bit machines. This holds the saved numFixedFields and then the value to fill with */ + fillReg = Extra0Reg; + assert(fillReg > 0); + + /* inst spec will hold class's instance specification and then byte size */ + instSpecReg = (byteSizeReg = ClassReg); + + /* The max slots we'll allocate here are those for a single header */ + maxSlots = (numSlotsMask()) - 1; + + /* get freeStart as early as possible so as not to wait later... */ + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveAwR, freeStartAddress(), Arg1Reg)); + + /* get class's hash & fail if 0 */ + genGetHashFieldNonImmOfinto(ReceiverResultReg, headerReg); + jumpUnhashed = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* get index and fail if not a +ve integer */ + jumpNElementsNonInt = genJumpNotSmallInteger(Arg0Reg); + + /* get class's format inst var for inst spec (format field) */ + genLoadSlotsourceRegdestReg(InstanceSpecificationIndex, ReceiverResultReg, instSpecReg); + quickConstant = (fixedFieldsFieldWidth()) + (numSmallIntegerTagBits()); + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, quickConstant, instSpecReg); + quickConstant = formatMask(); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, instSpecReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* Add format to classIndex/format header now */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, instSpecReg, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* get integer value of num fields in fillReg now */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, fillReg); + genConvertSmallIntegerToIntegerInReg(fillReg); + + /* dispatch on format, failing if not variable or if compiled method */ + quickConstant = arrayFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, instSpecReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpArrayFormat = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + quickConstant = firstByteFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, instSpecReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpByteFormat = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + quickConstant = firstLongFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, instSpecReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpFailCuzFixed = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, (((usqInt)(maxSlots * 2) << 3) | 1), Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize((((usqInt)(maxSlots * 2) << 3) | 1), BytesPerOop)); + } + jumpLongTooBig = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* save num elements/slot size to instSpecReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, fillReg, instSpecReg); + + /* compute odd bits and add into headerReg; oddBits := 2 - nElements bitAnd: 1 */ + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, BytesPerWord / 4, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BytesPerWord / 4, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, instSpecReg, TempReg); + quickConstant = (BytesPerWord / 4) - 1; + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* round up num elements to numSlots in instSpecReg */ + quickConstant = (BytesPerWord / 4) - 1; + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, instSpecReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, (shiftForWord()) - 2, instSpecReg); + anInstruction = genoperandoperand(MoveCqR, 0, fillReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* go allocate */ + jumpLongPrepDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpByteFormat, gCmpCqR((((usqInt)(maxSlots * BytesPerWord) << 3) | 1), Arg0Reg)); + jumpByteTooBig = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* save num elements to instSpecReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, fillReg, instSpecReg); + + /* compute odd bits and add into headerReg; oddBits := 8 - nElements bitAnd: 7 */ + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, BytesPerWord, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BytesPerWord, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, instSpecReg, TempReg); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, BytesPerWord - 1, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BytesPerWord - 1, BytesPerOop)); + } + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* round up num elements to numSlots in instSpecReg */ + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, BytesPerWord - 1, instSpecReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BytesPerWord - 1, BytesPerOop)); + } + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, shiftForWord(), instSpecReg); + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, 0, fillReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* go allocate */ + jumpBytePrepDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpArrayFormat, gCmpCqR((((usqInt)maxSlots << 3) | 1), Arg0Reg)); + jumpArrayTooBig = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* save num elements/slot size to instSpecReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, fillReg, instSpecReg); + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, nilObject(), fillReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(nilObject(), BytesPerOop)); + } + + /* fall through to allocate */ + jmpTarget(jumpBytePrepDone, jmpTarget(jumpLongPrepDone, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + + /* store numSlots to headerReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, instSpecReg, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, numSlotsFullShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* compute byte size; remember 0-sized objects still need 1 slot. */ + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, byteSizeReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpHasSlots = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, BaseHeaderSize * 2, byteSizeReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize * 2, BytesPerOop)); + } + skip = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpHasSlots, gAddCqR(BaseHeaderSize / BytesPerWord, byteSizeReg)); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), byteSizeReg); + jmpTarget(skip, genoperandoperand(AddRR, Arg1Reg, byteSizeReg)); + + /* check if allocation fits */ + quickConstant = getScavengeThreshold(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, byteSizeReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpNoSpace = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* get result, increment freeStart and write it back. Arg1Reg holds new freeStart, the limit of the object */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveRAw, byteSizeReg, freeStartAddress())); + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, headerReg, 0, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* now fill */ + + /* begin LoadEffectiveAddressMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(LoadEffectiveAddressMwrR, BaseHeaderSize, ReceiverResultReg, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + fillLoop = genoperandoperandoperand(MoveRMwr, fillReg, 0, Arg1Reg); + if (usesOutOfLineLiteral(fillLoop)) { + (fillLoop->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, 8, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(8, BytesPerOop)); + } + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, byteSizeReg); + + /* JumpAbove: */ + genConditionalBranchoperand(JumpAbove, ((sqInt)fillLoop)); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNoSpace, jmpTarget(jumpUnhashed, jmpTarget(jumpFailCuzFixed, jmpTarget(jumpArrayTooBig, jmpTarget(jumpByteTooBig, jmpTarget(jumpLongTooBig, jmpTarget(jumpNElementsNonInt, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))))))); + return 0; +} + + +/* Implement primitiveShallowCopy/primitiveClone for convenient cases: + - the receiver is not a context + - the receiver is not a compiled method + - the result fits in eden (actually below scavengeThreshold) */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveShallowCopy */ +static sqInt +genPrimitiveShallowCopy(void) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *continuance; + AbstractInstruction *copyLoop; + sqInt formatReg; + AbstractInstruction *jumpEmpty; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpIsMethod; + AbstractInstruction *jumpNoSpace; + AbstractInstruction *jumpTooBig; + AbstractInstruction *jumpVariable; + sqInt ptrReg; + sqInt quickConstant; + sqInt resultReg; + sqInt slotsReg; + + jumpImmediate = genJumpImmediate(ReceiverResultReg); + resultReg = Arg0Reg; + slotsReg = Arg1Reg; + + /* get freeStart as early as possible so as not to wait later... */ + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveAwR, freeStartAddress(), resultReg)); + + /* formatReg := self formatOf: ReceiverResultReg */ + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (ptrReg = (formatReg = SendNumArgsReg)), NoReg); + quickConstant = firstCompiledMethodFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsMethod = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + quickConstant = indexablePointersFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpVariable = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + continuance = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + genGetRawSlotSizeOfNonImminto(ReceiverResultReg, slotsReg); + quickConstant = numSlotsMask(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, slotsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpTooBig = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, slotsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpEmpty = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* compute byte size for slots */ + quickConstant = BaseHeaderSize / BytesPerWord; + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, slotsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), slotsReg); + + /* AddR:R: */ + genoperandoperand(AddRR, resultReg, slotsReg); + quickConstant = getScavengeThreshold(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, slotsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* check if allocation fits (freeSize + byteSize < scavengeThreshold); scavengeThreshold is constant. */ + jumpNoSpace = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, resultReg, ptrReg); + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveRAw, slotsReg, freeStartAddress())); + + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, BytesPerWord * 2, slotsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BytesPerWord * 2, BytesPerOop)); + } + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, 0, ReceiverResultReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + quickConstant = headerForSlotsformatclassIndex(numSlotsMask(), formatMask(), classIndexMask()); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, TempReg, 0, resultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* write back new freeStart; get result. slotsReg holds new freeStart, the limit of the object + set up loop bounds + copy header, masking off irrelevant bits */ + copyLoop = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, BytesPerWord, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BytesPerWord, BytesPerOop)); + } + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, BytesPerWord, ptrReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BytesPerWord, BytesPerOop)); + } + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, 0, ReceiverResultReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, TempReg, 0, ptrReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* begin CmpR:R: */ + assert(!((ptrReg == SPReg))); + genoperandoperand(CmpRR, ptrReg, slotsReg); + + /* JumpAboveOrEqual: */ + genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)copyLoop)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, resultReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* If the receiver is variable pointers, fail if its a context, otherwise continue */ + jmpTarget(jumpVariable, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genGetClassIndexOfNonImminto(ReceiverResultReg, ClassReg); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, ClassMethodContextCompactIndex, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(ClassMethodContextCompactIndex, BytesPerOop)); + } + + /* JumpNonZero: */ + genConditionalBranchoperand(JumpNonZero, ((sqInt)continuance)); + jmpTarget(jumpImmediate, jmpTarget(jumpNoSpace, jmpTarget(jumpIsMethod, jmpTarget(jumpTooBig, jmpTarget(jumpEmpty, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))))); + return 0; +} + + +/* Generate the code for primitive 173, instVarAt:. Defer to + StackInterpreterPrimitives>>primitiveSlotAt for Contexts. + */ +/* c.f. StackInterpreter>>stSizeOf: SpurMemoryManager>>lengthOf:format: + fixedFieldsOf:format:length: + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSlotAt */ +static sqInt +genPrimitiveSlotAt(void) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *convertToIntAndReturn; + sqInt formatReg; + AbstractInstruction *jumpBadIndex; + AbstractInstruction *jumpBytesOutOfBounds; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpIsBytes; + AbstractInstruction *jumpIsContext; + AbstractInstruction *jumpIsMethod; + AbstractInstruction *jumpIsShorts; + AbstractInstruction *jumpIsWords; + AbstractInstruction *jumpLongsOutOfBounds; + AbstractInstruction *jumpNonPointers; + AbstractInstruction *jumpPointersOutOfBounds; + AbstractInstruction *jumpShortsOutOfBounds; + AbstractInstruction *jumpToReturnLargeInteger; + AbstractInstruction *jumpWordsOutOfBounds; + sqInt nSlotsOrElementsReg; + sqInt quickConstant; + + nSlotsOrElementsReg = ClassReg; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpImmediate = genJumpImmediate(ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, Arg1Reg); + jumpBadIndex = genJumpNotSmallInteger(Arg0Reg); + genConvertSmallIntegerToIntegerInReg(Arg1Reg); + + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, 1, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), TempReg); + genGetNumSlotsOfinto(ReceiverResultReg, nSlotsOrElementsReg); + + /* dispatch on format in a combination of highest dynamic frequency order first and convenience. + 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + quickConstant = sixtyFourBitIndexableFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpNonPointers = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + quickConstant = classIndexMask(); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, ClassMethodContextCompactIndex, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(ClassMethodContextCompactIndex, BytesPerOop)); + } + + /* Fail to StackInterpreterPrimitives>>primitiveSlotAt for Contexts */ + jumpIsContext = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpPointersOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* index is (formatReg Arg1Reg (0-rel index) * wordSize + baseHeaderSize */ + quickConstant = ((usqInt)(BaseHeaderSize)) >> (shiftForWord()); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, Arg1Reg, ReceiverResultReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNonPointers, gCmpCqR(firstByteFormat(), formatReg)); + jumpIsBytes = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + quickConstant = firstShortFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsShorts = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + quickConstant = firstLongFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsWords = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* fall through to objectMemory sixtyFourBitIndexableFormat */ + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpLongsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + quickConstant = ((usqInt)(BaseHeaderSize)) >> (shiftForWord()); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, Arg1Reg, ReceiverResultReg, ClassReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, TempReg); + quickConstant = (numSmallIntegerBits()) - 1; + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, quickConstant, TempReg); + anInstruction = genoperandoperand(CmpCqR, 0, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpToReturnLargeInteger = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genConvertIntegerInRegtoSmallIntegerInReg(ClassReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpToReturnLargeInteger, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genInstantiate64BitPositiveIntegerValueintoscratchRegmayFail(ClassReg, ReceiverResultReg, TempReg, 0); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsBytes, genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), nSlotsOrElementsReg)); + gAndCqRR(7, formatReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, nSlotsOrElementsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpBytesOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + quickConstant = firstCompiledMethodFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsMethod = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, BaseHeaderSize, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + + /* MoveXbr:R:R: */ + genoperandoperandoperand(MoveXbrRR, Arg1Reg, ReceiverResultReg, ReceiverResultReg); + convertToIntAndReturn = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + genConvertIntegerToSmallIntegerInReg(ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsShorts, gLogicalShiftLeftCqR((shiftForWord()) - 1, nSlotsOrElementsReg)); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, 3, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(3, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrElementsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpShortsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg1Reg, ReceiverResultReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg1Reg, ReceiverResultReg); + + /* begin MoveM16:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveM16rR, BaseHeaderSize, ReceiverResultReg, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + + /* Jump: */ + genoperand(Jump, ((sqInt)convertToIntAndReturn)); + jmpTarget(jumpIsWords, gLogicalShiftLeftCqR((shiftForWord()) - 2, nSlotsOrElementsReg)); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, 1, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrElementsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpWordsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + quickConstant = ((usqInt)(BaseHeaderSize)) >> ((shiftForWord()) - 1); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* MoveX32r:R:R: */ + genoperandoperandoperand(MoveX32rRR, Arg1Reg, ReceiverResultReg, TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, ReceiverResultReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)convertToIntAndReturn)); + jmpTarget(jumpLongsOutOfBounds, jmpTarget(jumpPointersOutOfBounds, jmpTarget(jumpBytesOutOfBounds, jmpTarget(jumpIsMethod, jmpTarget(jumpShortsOutOfBounds, jmpTarget(jumpWordsOutOfBounds, jmpTarget(jumpIsContext, jmpTarget(jumpBadIndex, jmpTarget(jumpImmediate, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))))))))); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatAdd */ +static sqInt +genPrimitiveSmallFloatAdd(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(AddRdRd, null, 0) + : genPureFloatArithmeticpreOpCheckboxed(AddRdRd, null, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatDivide */ +static sqInt +genPrimitiveSmallFloatDivide(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(DivRdRd, genDoubleFailIfZeroArgRcvrarg, 0) + : genPureFloatArithmeticpreOpCheckboxed(DivRdRd, genDoubleFailIfZeroArgRcvrarg, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatEqual */ +static sqInt +genPrimitiveSmallFloatEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPEqual, JumpZero, 0, 0) + : genPureFloatComparisoninvertboxed(gJumpFPEqual, 0, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatGreaterOrEqual */ +static sqInt +genPrimitiveSmallFloatGreaterOrEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreaterOrEqual, JumpGreaterOrEqual, 0, 0) + : genPureFloatComparisoninvertboxed(gJumpFPGreaterOrEqual, 0, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatGreaterThan */ +static sqInt +genPrimitiveSmallFloatGreaterThan(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreater, JumpGreater, 0, 0) + : genPureFloatComparisoninvertboxed(gJumpFPGreater, 0, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatLessOrEqual */ +static sqInt +genPrimitiveSmallFloatLessOrEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreaterOrEqual, JumpLessOrEqual, 1, 0) + : genPureFloatComparisoninvertboxed(gJumpFPGreaterOrEqual, 1, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatLessThan */ +static sqInt +genPrimitiveSmallFloatLessThan(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreater, JumpLess, 1, 0) + : genPureFloatComparisoninvertboxed(gJumpFPGreater, 1, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatMultiply */ +static sqInt +genPrimitiveSmallFloatMultiply(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(MulRdRd, null, 0) + : genPureFloatArithmeticpreOpCheckboxed(MulRdRd, null, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatNotEqual */ +static sqInt +genPrimitiveSmallFloatNotEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPNotEqual, JumpNonZero, 0, 0) + : genPureFloatComparisoninvertboxed(gJumpFPNotEqual, 0, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatSquareRoot */ +static sqInt +genPrimitiveSmallFloatSquareRoot(void) +{ + AbstractInstruction *jumpNegative; + + genGetSmallFloatValueOfscratchinto(ReceiverResultReg, SendNumArgsReg, DPFPReg0); + + /* XorRd:Rd: */ + genoperandoperand(XorRdRd, DPFPReg1, DPFPReg1); + + /* CmpRd:Rd: */ + genoperandoperand(CmpRdRd, DPFPReg0, DPFPReg1); + + /* +0.0 is all zeros */ + jumpNegative = gJumpFPGreater(0); + + /* SqrtRd: */ + genoperand(SqrtRd, DPFPReg0); + genInstantiateFloatValueintoscratchRegscratchRegmayFail(DPFPReg0, ReceiverResultReg, ClassReg, TempReg, 0); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNegative, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatSubtract */ +static sqInt +genPrimitiveSmallFloatSubtract(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(SubRdRd, null, 0) + : genPureFloatArithmeticpreOpCheckboxed(SubRdRd, null, 0)); +} + + +/* c.f. StackInterpreter>>stSizeOf: SpurMemoryManager>>lengthOf:format: + fixedFieldsOf:format:length: + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveStringAt */ +static sqInt +genPrimitiveStringAt(void) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *done; + sqInt formatReg; + AbstractInstruction *jumpBadIndex; + AbstractInstruction *jumpBytesOutOfBounds; + AbstractInstruction *jumpIsBytes; + AbstractInstruction *jumpIsShorts; + AbstractInstruction *jumpNotIndexable; + AbstractInstruction *jumpShortsOutOfBounds; + AbstractInstruction *jumpWordsDone; + AbstractInstruction *jumpWordsOutOfBounds; + AbstractInstruction *jumpWordTooBig; + sqInt quickConstant; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, Arg1Reg); + jumpBadIndex = genJumpNotSmallInteger(Arg0Reg); + genConvertSmallIntegerToIntegerInReg(Arg1Reg); + + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, 1, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), NoReg); + genGetNumSlotsOfinto(ReceiverResultReg, ClassReg); + + /* dispatch on format in a combination of highest dynamic frequency order first and convenience. + 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + quickConstant = firstByteFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsBytes = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + quickConstant = firstShortFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsShorts = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + quickConstant = firstLongFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpNotIndexable = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* fall through to double words */ + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - 2, ClassReg); + quickConstant = (BytesPerWord / 4) - 1; + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, ClassReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, ClassReg); + jumpWordsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + quickConstant = ((usqInt)(BaseHeaderSize)) >> ((shiftForWord()) - 1); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* MoveX32r:R:R: */ + genoperandoperandoperand(MoveX32rRR, Arg1Reg, ReceiverResultReg, TempReg); + jumpWordTooBig = jumpNotCharacterUnsignedValueInRegister(TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, ReceiverResultReg); + jumpWordsDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpIsBytes, genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), ClassReg)); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, BytesPerWord - 1, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BytesPerWord - 1, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, ClassReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, ClassReg); + jumpBytesOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, BaseHeaderSize, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + + /* MoveXbr:R:R: */ + genoperandoperandoperand(MoveXbrRR, Arg1Reg, ReceiverResultReg, ReceiverResultReg); + jmpTarget(jumpWordsDone, (done = genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + genConvertIntegerToCharacterInReg(ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsShorts, gLogicalShiftLeftCqR((shiftForWord()) - 1, ClassReg)); + quickConstant = (BytesPerWord / 2) - 1; + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, ClassReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, ClassReg); + jumpShortsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg1Reg, ReceiverResultReg); + + /* begin MoveM16:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveM16rR, BaseHeaderSize, ReceiverResultReg, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + + /* Jump: */ + genoperand(Jump, ((sqInt)done)); + jmpTarget(jumpWordTooBig, jmpTarget(jumpBytesOutOfBounds, jmpTarget(jumpShortsOutOfBounds, jmpTarget(jumpWordsOutOfBounds, jmpTarget(jumpNotIndexable, jmpTarget(jumpBadIndex, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))))); + return CompletePrimitive; +} + + +/* c.f. StackInterpreter>>stSizeOf: SpurMemoryManager>>lengthOf:format: + fixedFieldsOf:format:length: + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveStringAtPut */ +static sqInt +genPrimitiveStringAtPut(void) +{ + AbstractInstruction *anInstruction; + sqInt formatReg; + AbstractInstruction *jumpBadArg; + AbstractInstruction *jumpBadIndex; + AbstractInstruction *jumpBytesOutOfBounds; + AbstractInstruction *jumpBytesOutOfRange; + AbstractInstruction *jumpImmutable; + AbstractInstruction *jumpIsBytes; + AbstractInstruction *jumpIsCompiledMethod; + AbstractInstruction *jumpIsShorts; + AbstractInstruction *jumpNotString; + AbstractInstruction *jumpShortsOutOfBounds; + AbstractInstruction *jumpShortsOutOfRange; + AbstractInstruction *jumpWordsOutOfBounds; + AbstractInstruction *jumpWordsOutOfRange; + sqInt quickConstant; + + /* begin genLoadArgAtDepth:into: */ + assert(1 < (numRegArgs())); + assert(0 < (numRegArgs())); + jumpBadIndex = genJumpNotSmallInteger(Arg0Reg); + jumpBadArg = genJumpNotCharacter(Arg1Reg); + genConvertSmallIntegerToIntegerInReg(Arg0Reg); + + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, 1, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } +# if IMMUTABILITY + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), TempReg); + + /* begin genJumpBaseHeaderImmutable: */ + quickConstant = immutableBitMask(); + anInstruction = genoperandoperand(TstCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpImmutable = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +# else // IMMUTABILITY + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), NoReg); +# endif // IMMUTABILITY + + genGetNumSlotsOfinto(ReceiverResultReg, ClassReg); + + /* dispatch on format; words and/or bytes. + 0 to 8 = pointer objects, forwarders, reserved. + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + quickConstant = firstLongFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpNotString = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + quickConstant = firstCompiledMethodFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsCompiledMethod = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + quickConstant = firstByteFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsBytes = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + quickConstant = firstShortFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsShorts = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* fall through to double words */ + quickConstant = characterObjectOf((1U << (numCharacterBits())) - 1); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpWordsOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - 2, ClassReg); + quickConstant = (BytesPerWord / 4) - 1; + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, ClassReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, ClassReg); + jumpWordsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertCharacterToCodeInReg(TempReg); + quickConstant = ((usqInt)(BaseHeaderSize)) >> ((shiftForWord()) - 1); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* MoveR:X32r:R: */ + genoperandoperandoperand(MoveRX32rR, TempReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsShorts, gCmpCqR(characterObjectOf(0xFFFF), Arg1Reg)); + jumpShortsOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - 1, ClassReg); + quickConstant = (BytesPerWord / 2) - 1; + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, ClassReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, ClassReg); + jumpShortsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertCharacterToCodeInReg(TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg0Reg, ReceiverResultReg); + + /* begin MoveR:M16:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRM16r, TempReg, BaseHeaderSize, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsBytes, gCmpCqR(characterObjectOf(0xFF), Arg1Reg)); + jumpBytesOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), ClassReg); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, BytesPerWord - 1, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BytesPerWord - 1, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, ClassReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, ClassReg); + jumpBytesOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertCharacterToCodeInReg(TempReg); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, BaseHeaderSize, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + + /* MoveR:Xbr:R: */ + genoperandoperandoperand(MoveRXbrR, TempReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotString, jmpTarget(jumpBytesOutOfRange, jmpTarget(jumpShortsOutOfRange, jmpTarget(jumpWordsOutOfRange, jmpTarget(jumpIsCompiledMethod, jmpTarget(jumpBytesOutOfBounds, jmpTarget(jumpShortsOutOfBounds, jmpTarget(jumpWordsOutOfBounds, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))))))); +# if IMMUTABILITY + jmpTarget(jumpImmutable, ((AbstractInstruction *) (((jumpNotString->operands))[0]))); +# endif + + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, 1, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + genConvertIntegerToSmallIntegerInReg(Arg0Reg); + jmpTarget(jumpBadArg, jmpTarget(jumpBadIndex, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + +/* Implement primitiveUninitializedNewWithArg for convenient cases: + - the receiver has a hash + - the receiver is variable and not compiled method + - the result fits in eden + See superclass method for dynamic frequencies of formats. + For the moment we implement only arrayFormat, firstByteFormat & + firstLongFormat + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveUninitializedNewWithArg */ +static sqInt +genPrimitiveUninitializedNewWithArg(void) +{ + AbstractInstruction *anInstruction; + sqInt byteSizeReg; + sqInt fillReg; + sqInt headerReg; + sqInt instSpecReg; + AbstractInstruction *jumpByteFormat; + AbstractInstruction *jumpByteTooBig; + AbstractInstruction *jumpDoubleByteFormat; + AbstractInstruction *jumpDoubleBytePrepDone; + AbstractInstruction *jumpDoubleWordFormat; + AbstractInstruction *jumpDoubleWordPrepDone; + AbstractInstruction *jumpDoubleWordTooBig; + AbstractInstruction *jumpFailCuzFixed; + AbstractInstruction *jumpHasSlots; + AbstractInstruction *jumpLongPrepDone; + AbstractInstruction *jumpLongTooBig; + AbstractInstruction *jumpNElementsNonInt; + AbstractInstruction *jumpNoSpace; + AbstractInstruction *jumpNoSpaceBigObjects; + AbstractInstruction *jumpOverflowHeader; + AbstractInstruction *jumpShortTooBig; + AbstractInstruction *jumpUnhashed; + sqInt maxSlots; + sqInt quickConstant; + AbstractInstruction *skip; + + if (methodOrBlockNumArgs != 1) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* header will contain classIndex/class's hash & format & numSlots/fixed size */ + headerReg = SendNumArgsReg; + + /* Assume there's an available scratch register on 64-bit machines. This holds the saved numFixedFields and then the value to fill with */ + fillReg = Extra0Reg; + assert(fillReg > 0); + + /* inst spec will hold class's instance specification and then byte size */ + instSpecReg = (byteSizeReg = ClassReg); + + /* Allow a max of 1 MB */ + maxSlots = maxSlotsForNewSpaceAlloc(); + + /* get freeStart as early as possible so as not to wait later... */ + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveAwR, freeStartAddress(), Arg1Reg)); + + /* get class's hash & fail if 0 */ + genGetHashFieldNonImmOfinto(ReceiverResultReg, headerReg); + jumpUnhashed = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* get index and fail if not a +ve integer */ + jumpNElementsNonInt = genJumpNotSmallInteger(Arg0Reg); + + /* get class's format inst var for inst spec (format field) */ + genLoadSlotsourceRegdestReg(InstanceSpecificationIndex, ReceiverResultReg, instSpecReg); + quickConstant = (fixedFieldsFieldWidth()) + (numSmallIntegerTagBits()); + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, quickConstant, instSpecReg); + quickConstant = formatMask(); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, instSpecReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* Add format to classIndex/format header now */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, instSpecReg, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* get integer value of num fields in fillReg now */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, fillReg); + genConvertSmallIntegerToIntegerInReg(fillReg); + + /* dispatch on format, failing if not a byte format */ + quickConstant = firstByteFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, instSpecReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpByteFormat = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + quickConstant = firstShortFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, instSpecReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpDoubleByteFormat = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + quickConstant = sixtyFourBitIndexableFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, instSpecReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpDoubleWordFormat = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + quickConstant = firstLongFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, instSpecReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpFailCuzFixed = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* allocates a 32-bit array */ + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, (((usqInt)(maxSlots * 2) << 3) | 1), Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize((((usqInt)(maxSlots * 2) << 3) | 1), BytesPerOop)); + } + jumpLongTooBig = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* save num elements/slot size to instSpecReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, fillReg, instSpecReg); + + /* compute odd bits and add into headerReg; oddBits := 2 - nElements bitAnd: 1 */ + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, BytesPerWord / 4, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BytesPerWord / 4, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, instSpecReg, TempReg); + quickConstant = (BytesPerWord / 4) - 1; + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* round up num elements to numSlots in instSpecReg; (numElements / 2) ceiling */ + quickConstant = (BytesPerWord / 4) - 1; + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, instSpecReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, (shiftForWord()) - 2, instSpecReg); + + /* go allocate */ + /* allocates a 16-bit array */ + jumpLongPrepDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpDoubleByteFormat, gCmpCqR((((usqInt)(maxSlots * 4) << 3) | 1), Arg0Reg)); + jumpShortTooBig = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* save num elements to instSpecReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, fillReg, instSpecReg); + + /* compute odd bits and add into headerReg; oddBits := 4 - nElements bitAnd: 3 */ + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, BytesPerWord / 2, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BytesPerWord / 2, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, instSpecReg, TempReg); + quickConstant = (BytesPerWord / 2) - 1; + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* round up num elements to numSlots in instSpecReg; (numElements / 4) ceiling */ + quickConstant = (BytesPerWord / 2) - 1; + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, instSpecReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, (shiftForWord()) - 1, instSpecReg); + + /* go allocate */ + /* allocates a 64-bit array */ + jumpDoubleBytePrepDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpDoubleWordFormat, gCmpCqR((((usqInt)maxSlots << 3) | 1), Arg0Reg)); + jumpDoubleWordTooBig = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* save num elements to instSpecReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, fillReg, instSpecReg); + + /* go allocate */ + /* allocates a byte array */ + jumpDoubleWordPrepDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpByteFormat, gCmpCqR((((usqInt)(maxSlots * BytesPerWord) << 3) | 1), Arg0Reg)); + jumpByteTooBig = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* save num elements to instSpecReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, fillReg, instSpecReg); + + /* compute odd bits and add into headerReg; oddBits := 8 - nElements bitAnd: 7 */ + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, BytesPerWord, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BytesPerWord, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, instSpecReg, TempReg); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, BytesPerWord - 1, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BytesPerWord - 1, BytesPerOop)); + } + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* round up num elements to numSlots in instSpecReg; (numElements / 8) ceiling */ + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, BytesPerWord - 1, instSpecReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BytesPerWord - 1, BytesPerOop)); + } + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, shiftForWord(), instSpecReg); + + /* fall through to allocate */ + jmpTarget(jumpDoubleWordPrepDone, jmpTarget(jumpDoubleBytePrepDone, jmpTarget(jumpLongPrepDone, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + + /* if numSlots >= 255 -> overflow header -> handle it */ + quickConstant = numSlotsMask(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, instSpecReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpOverflowHeader = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* fallthrough: allocate objects with < 255 slots + store numSlots to headerReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, instSpecReg, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, numSlotsFullShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* compute byte size; remember 0-sized objects still need 1 slot. */ + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, byteSizeReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpHasSlots = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, BaseHeaderSize * 2, byteSizeReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize * 2, BytesPerOop)); + } + skip = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpHasSlots, gAddCqR(BaseHeaderSize / BytesPerWord, byteSizeReg)); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), byteSizeReg); + jmpTarget(skip, genoperandoperand(AddRR, Arg1Reg, byteSizeReg)); + + /* check if allocation fits */ + quickConstant = getScavengeThreshold(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, byteSizeReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpNoSpace = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* get result, increment freeStart and write it back. Arg1Reg holds new freeStart, the limit of the object */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveRAw, byteSizeReg, freeStartAddress())); + + /* write other half of header (numSlots/0 identityHash) */ + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, headerReg, 0, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* allocate object with up to 1 << 16 - 1 slots; numSlots >= 255 + store always 255 to headerReg */ + jmpTarget(jumpOverflowHeader, gMoveCqR(0xFF, TempReg)); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, numSlotsFullShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* calculate overflow header; Temp Reg can be resued as it already contains 255 as needed */ + + /* AddR:R: */ + genoperandoperand(AddRR, instSpecReg, TempReg); + + /* compute byte size */ + quickConstant = (BaseHeaderSize * 2) / BytesPerWord; + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, byteSizeReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), byteSizeReg); + + /* check if allocation fits */ + + /* AddR:R: */ + genoperandoperand(AddRR, Arg1Reg, byteSizeReg); + quickConstant = getScavengeThreshold(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, byteSizeReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpNoSpaceBigObjects = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* get result, increment freeStart and write it back. Arg1Reg holds new freeStart, the limit of the object */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveRAw, byteSizeReg, freeStartAddress())); + + /* write overflow part */ + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, TempReg, 0, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* write header */ + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, headerReg, BaseHeaderSize, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpShortTooBig, jmpTarget(jumpDoubleWordTooBig, jmpTarget(jumpNoSpaceBigObjects, jmpTarget(jumpNoSpace, jmpTarget(jumpUnhashed, jmpTarget(jumpFailCuzFixed, jmpTarget(jumpByteTooBig, jmpTarget(jumpLongTooBig, jmpTarget(jumpNElementsNonInt, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))))))))); + return 0; +} + + +/* In the Pure version, mixed arithmetic with SmallInteger is forbidden */ + + /* CogObjectRepresentationFor64BitSpur>>#genPureFloatArithmetic:preOpCheck:boxed: */ +static NoDbgRegParms sqInt +genPureFloatArithmeticpreOpCheckboxed(sqInt arithmeticOperator, AbstractInstruction *(*preOpCheckOrNil)(int rcvrReg, int argReg), sqInt rcvrBoxed) +{ + AbstractInstruction *doOp; + AbstractInstruction *jumpFailCheck; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpNotBoxedFloat; + AbstractInstruction *jumpNotSmallFloat; + + jumpFailCheck = ((AbstractInstruction *) 0); + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + if (rcvrBoxed) { + genGetDoubleValueOfinto(ReceiverResultReg, DPFPReg0); + } + else { + genGetSmallFloatValueOfscratchinto(ReceiverResultReg, TempReg, DPFPReg0); + } + jumpNotSmallFloat = genJumpNotSmallFloat(Arg0Reg); + genGetSmallFloatValueOfscratchinto(Arg0Reg, TempReg, DPFPReg1); + doOp = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + if (preOpCheckOrNil) { + jumpFailCheck = preOpCheckOrNil(DPFPReg0, DPFPReg1); + } + genoperandoperand(arithmeticOperator, DPFPReg1, DPFPReg0); + genInstantiateFloatValueintoscratchRegscratchRegmayFail(DPFPReg0, ReceiverResultReg, ClassReg, TempReg, 0); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSmallFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpImmediate = genJumpImmediate(Arg0Reg); + genGetCompactClassIndexNonImmOfinto(Arg0Reg, SendNumArgsReg); + genCmpClassFloatCompactIndexR(SendNumArgsReg); + jumpNotBoxedFloat = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genGetDoubleValueOfinto(Arg0Reg, DPFPReg1); + + /* Jump: */ + genoperand(Jump, ((sqInt)doOp)); + jmpTarget(jumpImmediate, jmpTarget(jumpNotBoxedFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + if (preOpCheckOrNil) { + jmpTarget(jumpFailCheck, ((AbstractInstruction *) (((jumpNotBoxedFloat->operands))[0]))); + } + return 0; +} + + +/* In the Pure version, mixed arithmetic with SmallInteger is forbidden */ + + /* CogObjectRepresentationFor64BitSpur>>#genPureFloatComparison:invert:boxed: */ +static NoDbgRegParms sqInt +genPureFloatComparisoninvertboxed(AbstractInstruction *(*jumpFPOpcodeGenerator)(void *), sqInt invertComparison, sqInt rcvrBoxed) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *compareFloat; + AbstractInstruction *jumpCond; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpNotBoxedFloat; + AbstractInstruction *jumpNotSmallFloat; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + if (rcvrBoxed) { + genGetDoubleValueOfinto(ReceiverResultReg, DPFPReg0); + } + else { + genGetSmallFloatValueOfscratchinto(ReceiverResultReg, TempReg, DPFPReg0); + } + jumpNotSmallFloat = genJumpNotSmallFloat(Arg0Reg); + genGetSmallFloatValueOfscratchinto(Arg0Reg, TempReg, DPFPReg1); + compareFloat = (invertComparison + ? genoperandoperand(CmpRdRd, DPFPReg0, DPFPReg1) + : genoperandoperand(CmpRdRd, DPFPReg1, DPFPReg0)); + + /* May need to invert for NaNs */ + + /* FP jumps are a little weird */ + jumpCond = jumpFPOpcodeGenerator(0); + + /* begin genMoveConstant:R: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(falseObject(), BytesPerOop)); + } + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpCond, genMoveConstantR(trueObject(), ReceiverResultReg)); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* not a Small Float, check for Boxed Float argument */ + jmpTarget(jumpNotSmallFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpImmediate = genJumpImmediate(Arg0Reg); + genGetCompactClassIndexNonImmOfinto(Arg0Reg, SendNumArgsReg); + genCmpClassFloatCompactIndexR(SendNumArgsReg); + jumpNotBoxedFloat = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genGetDoubleValueOfinto(Arg0Reg, DPFPReg1); + + /* Jump: */ + genoperand(Jump, ((sqInt)compareFloat)); + jmpTarget(jumpImmediate, jmpTarget(jumpNotBoxedFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + /* CogObjectRepresentationFor64BitSpur>>#genRemoveSmallIntegerTagsInScratchReg: */ +static NoDbgRegParms sqInt +genRemoveSmallIntegerTagsInScratchReg(sqInt scratchReg) +{ + AbstractInstruction *anInstruction; + + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, 1, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genShiftAwaySmallIntegerTagsInScratchReg: */ +static NoDbgRegParms sqInt +genShiftAwaySmallIntegerTagsInScratchReg(sqInt scratchReg) +{ + /* ArithmeticShiftRightCq:R: */ + genoperandoperand(ArithmeticShiftRightCqR, numTagBits(), scratchReg); + return 0; +} + + +/* Stack looks like + return address */ + + /* CogObjectRepresentationFor64BitSpur>>#genSmallIntegerComparison:orDoubleComparison:invert: */ +static NoDbgRegParms sqInt +genSmallIntegerComparisonorDoubleComparisoninvert(sqInt jumpOpcode, AbstractInstruction * NoDbgRegParms (*jumpFPOpcodeGenerator)(void *), sqInt invertComparison) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *compareIntFloat; + AbstractInstruction *jumpAmbiguous; + AbstractInstruction *jumpCond; + AbstractInstruction *jumpNotBoxedFloat; + AbstractInstruction *jumpNotFloatAtAll; + AbstractInstruction *jumpNotSmallFloat; + AbstractInstruction *jumpTrue; + sqInt r; + AbstractInstruction *returnTrue; + + r = genSmallIntegerComparison(jumpOpcode); + if (r < 0) { + return r; + } +# if defined(DPFPReg0) + jumpNotSmallFloat = genJumpNotSmallFloat(Arg0Reg); + genGetSmallFloatValueOfscratchinto(Arg0Reg, TempReg, DPFPReg1); + + /* Case of (int compare: float). Test for ambiguity, that is when (double) intRcvr == floatArg */ + compareIntFloat = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + genConvertSmallIntegerToIntegerInReg(ReceiverResultReg); + + /* ConvertR:Rd: */ + genoperandoperand(ConvertRRd, ReceiverResultReg, DPFPReg0); + + /* CmpRd:Rd: */ + genoperandoperand(CmpRdRd, DPFPReg0, DPFPReg1); + jumpAmbiguous = gJumpFPEqual(0); + + /* Case of non ambiguity, use compareFloat((double) intRcvr,floatArg) + May need to invert for NaNs */ + if (invertComparison) { + /* CmpRd:Rd: */ + genoperandoperand(CmpRdRd, DPFPReg0, DPFPReg1); + } + else { + /* CmpRd:Rd: */ + genoperandoperand(CmpRdRd, DPFPReg1, DPFPReg0); + } + + /* FP jumps are a little weird */ + jumpCond = jumpFPOpcodeGenerator(0); + + /* begin genMoveConstant:R: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(falseObject(), BytesPerOop)); + } + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpCond, (returnTrue = genMoveConstantR(trueObject(), ReceiverResultReg))); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* Case of ambiguity, use compareInt(intRcvr , (int64) floatArg) */ + jmpTarget(jumpAmbiguous, genoperandoperand(ConvertRdR, DPFPReg1, Arg0Reg)); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, ReceiverResultReg); + jumpTrue = genConditionalBranchoperand(jumpOpcode, 0); + + /* begin genMoveConstant:R: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(falseObject(), BytesPerOop)); + } + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpTrue, returnTrue); + + /* not a Small Float, check for Boxed Float argument */ + jmpTarget(jumpNotSmallFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpNotFloatAtAll = genJumpImmediate(Arg0Reg); + genGetCompactClassIndexNonImmOfinto(Arg0Reg, SendNumArgsReg); + genCmpClassFloatCompactIndexR(SendNumArgsReg); + jumpNotBoxedFloat = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* It was a Boxed Float, so convert the receiver to double and perform the (int compare: float) operation */ + genGetDoubleValueOfinto(Arg0Reg, DPFPReg1); + + /* Jump: */ + genoperand(Jump, ((sqInt)compareIntFloat)); + + /* not a Float, just let the primitive fall thru failure */ + jmpTarget(jumpNotBoxedFloat, jmpTarget(jumpNotFloatAtAll, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); +# endif // defined(DPFPReg0) + + + /* Fall through on non-SmallInteger argument. Argument may be a Float : let us check or fail + check for Small Float argument */ + return CompletePrimitive; +} + + +/* Get the literal count of a CompiledMethod into headerReg, plus one if + requested. If inBytes is true, scale the count by the word size. Deal with + the possibility of + the method being cogged. */ + + /* CogObjectRepresentationFor64BitSpur>>#getLiteralCountOf:plusOne:inBytes:into:scratch: */ +static NoDbgRegParms sqInt +getLiteralCountOfplusOneinBytesintoscratch(sqInt methodReg, sqInt plusOne, sqInt inBytes, sqInt litCountReg, sqInt scratchReg) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + + genGetMethodHeaderOfintoscratch(methodReg, litCountReg, scratchReg); + assert((1U << (numTagBits())) == BytesPerWord); + if (inBytes) { + quickConstant = ((sqInt)((usqInt)((alternateHeaderNumLiteralsMask())) << (numTagBits()))); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, litCountReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + } + else { + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, numTagBits(), litCountReg); + quickConstant = alternateHeaderNumLiteralsMask(); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, litCountReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + } + if (plusOne) { + quickConstant = (inBytes + ? LiteralStart * BytesPerWord + : LiteralStart); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, litCountReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + } + return 0; +} + + +/* Answer the relevant inline cache tag for an instance. + c.f. getInlineCacheClassTagFrom:into: & inlineCacheTagForClass: */ + + /* CogObjectRepresentationFor64BitSpur>>#inlineCacheTagForInstance: */ +static NoDbgRegParms sqInt +inlineCacheTagForInstance(sqInt oop) +{ + return (isImmediate(oop) + ? oop & (tagMask()) + : classIndexOf(oop)); +} + + /* CogObjectRepresentationFor64BitSpur>>#log2BytesPerWord */ +static sqInt +log2BytesPerWord(void) +{ + return 3; +} + + +/* Generate the routine that converts selector indices into selector objects. + It is called from the send trampolines. + If the selector index is negative, convert it into a positive index into + the special selectors array and index that. Otherwise, index the current + method. The routine uses Extra0Reg & Extra1Reg, which are available, since + they are not live at point of send. */ + + /* CogObjectRepresentationFor64BitSpur>>#maybeGenerateSelectorIndexDereferenceRoutine */ +static void +maybeGenerateSelectorIndexDereferenceRoutine(void) +{ + AbstractInstruction *anInstruction; + sqInt byteOffset; + AbstractInstruction *jumpFullBlock; + AbstractInstruction *jumpNegative; + AbstractInstruction *jumpNotBlock; + sqInt offset; + sqInt quickConstant; + + zeroOpcodeIndex(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpNegative = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, FoxMethod, FPReg, Extra0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(FoxMethod, BytesPerOop)); + } + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, 2, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(2, BytesPerOop)); + } + + /* begin TstCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(TstCqR, MFMethodFlagIsBlockFlag, Extra0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(MFMethodFlagIsBlockFlag, BytesPerOop)); + } + + /* Change selector index to 1-relative, skipping the method header */ + jumpNotBlock = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* If in a block, need to find the home method... If using full blocks, need to test the cpicHasMNUCaseOrCMIsFullBlock bit */ + quickConstant = -(zoneAlignment()); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, Extra0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + byteOffset = BaseHeaderSize + 1; + + /* begin MoveMb:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMbrR, byteOffset, Extra0Reg, Extra1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(byteOffset, BytesPerOop)); + } + + /* begin TstCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(TstCqR, 16, Extra1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(16, BytesPerOop)); + } + jumpFullBlock = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* begin MoveM16:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveM16rR, 0, Extra0Reg, Extra1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, Extra1Reg, Extra0Reg); + jmpTarget(jumpNotBlock, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jmpTarget(jumpFullBlock, ((AbstractInstruction *) (((jumpNotBlock->operands))[0]))); + + /* Now fetch the method object and index with the literal index to retrieve the selector */ + quickConstant = -(zoneAlignment()); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, Extra0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + offset = offsetof(CogMethod, methodObject); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, offset, Extra0Reg, Extra1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, ClassReg, Extra1Reg, ClassReg); + + /* RetN: */ + genoperand(RetN, 0); + jmpTarget(jumpNegative, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* NegateR: */ + genoperand(NegateR, ClassReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, 1, ClassReg); + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(specialObjectsOopAddress(), genoperandoperand(MoveAwR, specialObjectsOopAddress(), Extra0Reg)); + + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, 1, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + offset = (SpecialSelectors + 1) * BytesPerWord; + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, offset, Extra0Reg, Extra1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, ClassReg, Extra1Reg, ClassReg); + + /* RetN: */ + genoperand(RetN, 0); + + /* Index, including header size */ + ceDereferenceSelectorIndex = methodZoneBase; + outputInstructionsForGeneratedRuntimeAt(ceDereferenceSelectorIndex); + recordGeneratedRunTimeaddress("ceDereferenceSelectorIndex", ceDereferenceSelectorIndex); + recordRunTimeObjectReferences(); +} + + /* CogObjectRepresentationFor64BitSpur>>#numSmallIntegerBits */ +static sqInt +numSmallIntegerBits(void) +{ + return 61; +} + + /* CogObjectRepresentationFor64BitSpur>>#numSmallIntegerTagBits */ +static sqInt +numSmallIntegerTagBits(void) +{ + return 3; +} + + +/* The three valid tag patterns are 1 (SmallInteger), 2 (Character) and 3 + (SmallFloat64). + */ + + /* CogObjectRepresentationFor64BitSpur>>#validInlineCacheTag: */ +static NoDbgRegParms sqInt +validInlineCacheTag(sqInt classIndexOrTagPattern) +{ + return ((classIndexOrTagPattern >= 1) + && (classIndexOrTagPattern <= 3)) + || ((classAtIndex(classIndexOrTagPattern))); +} + + /* CogObjectRepresentationForSpur>>#callStoreCheckTrampoline */ +static void +callStoreCheckTrampoline(void) +{ + AbstractInstruction *abstractInstruction; + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreCheckTrampoline); + (abstractInstruction->annotation = IsRelativeCall); +} + + /* CogObjectRepresentationForSpur>>#checkValidOopReference: */ +static NoDbgRegParms sqInt +checkValidOopReference(sqInt anOop) +{ + return (isImmediate(anOop)) + || ((heapMapAtWord(pointerForOop(anOop))) != 0); +} + + /* CogObjectRepresentationForSpur>>#compileInvokeCENewHashOf */ +static void +compileInvokeCENewHashOf(void) +{ + AbstractInstruction *abstractInstruction; + int callerSavedRegsToBeSaved; + sqInt callTargetSqInt; + AbstractInstruction *cascade0; + AbstractInstruction *inst; + AbstractInstruction *lastInst; + int mustSaveSPReg; + sqInt reg; + int registersToBePushed; + sqInt spareCalleeSavedReg; + + backEnd; + + /* begin saveAndRestoreLinkRegAround: */ + inst = genoperand(PushR, LinkReg); + spareCalleeSavedReg = 0; + assert(!((((((1U << VarBaseReg)) & (registerMaskFor(LinkReg))) != 0)))); + callerSavedRegsToBeSaved = CallerSavedRegisterMask & ((1U << VarBaseReg)); + registersToBePushed = callerSavedRegsToBeSaved; + reg = 0; + while (registersToBePushed != 0) { + if (((registersToBePushed & 1) != 0)) { + /* PushR: */ + genoperand(PushR, reg); + } + reg += 1; + registersToBePushed = ((sqInt)(registersToBePushed)) >> 1; + } + + /* If the backEnd doesn't use the native SP register in generated code, and the non-native + SP is not callee-saved, then it too must be saved and restored around the call. */ + mustSaveSPReg = !(((ABICalleeSavedRegisterMask & ((1U << SPReg))) != 0)); + if (mustSaveSPReg) { + spareCalleeSavedReg = availableRegisterOrNoneIn(((ABICalleeSavedRegisterMask | ((1U << SPReg))) - ((1U << SPReg)))); + assert(!((spareCalleeSavedReg == NoReg))); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SPReg, spareCalleeSavedReg); + } + genMarshallNArgsargargargarg(backEnd, 1, ReceiverResultReg, null, null, null); + + /* begin CallAnyFixedRT: */ + if (isInImmediateBranchAndLinkRange(backEnd, maximumDistanceFromCodeZone(((usqInt)ceNewHashOf)))) { + callTargetSqInt = ((usqIntptr_t)ceNewHashOf); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, callTargetSqInt); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* #CallFullRT: #CallFull: #gen:literal: */ + checkLiteralforInstruction(((usqIntptr_t)ceNewHashOf), genoperand(CallFull, ((usqIntptr_t)ceNewHashOf))); + } + cascade0 = backEnd; + + /* genRemoveNArgsFromStack: */ + lastInst = genWriteCResultIntoReg(cascade0, ReceiverResultReg); + if (mustSaveSPReg) { + lastInst = genoperandoperand(MoveRR, spareCalleeSavedReg, SPReg); + } + while (reg > 0) { + reg -= 1; + if (((callerSavedRegsToBeSaved & (1ULL << reg)) != 0)) { + lastInst = genoperand(PopR, reg); + } + } + + /* PopR: */ + genoperand(PopR, LinkReg); +} + + +/* If primIndex has an accessorDepth, or is the external call, + compile and answer the jump to a retry routine. */ + + /* CogObjectRepresentationForSpur>>#compileJumpToRetryOf:onPrimitiveFail: */ +static NoDbgRegParms AbstractInstruction * +compileJumpToRetryOfonPrimitiveFail(void (*primitiveRoutine)(void), sqInt primIndex) +{ + AbstractInstruction *anInstruction; + + if ((accessorDepthForPrimitiveIndex(primIndex)) >= 0) { + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(primFailCodeAddress(), genoperandoperand(MoveAwR, primFailCodeAddress(), TempReg)); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + return genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + } + if (PrimNumberExternalCall == primIndex) { + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(primFailCodeAddress(), genoperandoperand(MoveAwR, primFailCodeAddress(), TempReg)); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, PrimErrNoMemory, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(PrimErrNoMemory, BytesPerOop)); + } + return genConditionalBranchoperand(JumpZero, ((sqInt)0)); + } + return null; +} + + /* CogObjectRepresentationForSpur>>#couldBeObject: */ +static NoDbgRegParms sqInt +couldBeObject(sqInt literal) +{ + return (isNonImmediate(literal)) + && (oopisGreaterThanOrEqualTo(literal, startOfMemory())); +} + + +/* Create a trampoline to answer the active context that will + answer it if a frame is already married, and create it otherwise. + Assume numArgs is in SendNumArgsReg and ClassReg is free. */ + + /* CogObjectRepresentationForSpur>>#genActiveContextTrampolineLarge:inBlock:called: */ +static NoDbgRegParms usqInt +genActiveContextTrampolineLargeinBlockcalled(sqInt isLarge, sqInt isInBlock, char *aString) +{ + usqInt startAddress; + + startAddress = methodZoneBase; + zeroOpcodeIndex(); + genGetActiveContextLargeinBlock(isLarge, isInBlock); + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress(aString, startAddress); + recordRunTimeObjectReferences(); + return startAddress; +} + + +/* Check the remembered bit of the object in objReg; answer the jump taken if + the bit is already set. + Only need to fetch the byte containing it, which reduces the size of the + mask constant. + */ + + /* CogObjectRepresentationForSpur>>#genCheckRememberedBitOf:scratch: */ +static NoDbgRegParms AbstractInstruction * +genCheckRememberedBitOfscratch(sqInt objReg, sqInt scratchReg) +{ + AbstractInstruction *anInstruction; + sqInt mask; + sqInt rememberedBitByteOffset; + + rememberedBitByteOffset = (rememberedBitShift()) / 8; + mask = 1ULL << ((rememberedBitShift()) % 8); + + /* begin MoveMb:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMbrR, rememberedBitByteOffset, objReg, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(rememberedBitByteOffset, BytesPerOop)); + } + + /* begin TstCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(TstCqR, mask, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(mask, BytesPerOop)); + } + return genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +} + + /* CogObjectRepresentationForSpur>>#genConvertCharacterToCodeInReg: */ +static NoDbgRegParms sqInt +genConvertCharacterToCodeInReg(sqInt reg) +{ + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, numTagBits(), reg); + return 0; +} + + /* CogObjectRepresentationForSpur>>#genConvertIntegerToCharacterInReg: */ +static NoDbgRegParms sqInt +genConvertIntegerToCharacterInReg(sqInt reg) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, numTagBits(), reg); + quickConstant = characterTag(); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return 0; +} + + +/* Create a full closure with the given values. */ + + /* CogObjectRepresentationForSpur>>#genCreateFullClosure:numArgs:numCopied:ignoreContext:contextNumArgs:large:inBlock: */ +static NoDbgRegParms sqInt +genCreateFullClosurenumArgsnumCopiedignoreContextcontextNumArgslargeinBlock(sqInt compiledBlock, sqInt numArgs, sqInt numCopied, sqInt ignoreContext, sqInt contextNumArgs, sqInt contextIsLarge, sqInt contextIsBlock) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + usqInt byteSize; + usqLong header; + sqInt numSlots; + sqInt offset; + sqInt quickConstant; + AbstractInstruction *skip; + + + /* First get thisContext into ReceiverResultReg and thence in ClassReg. */ + if (ignoreContext) { + /* begin genMoveConstant:R: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, nilObject(), ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(nilObject(), BytesPerOop)); + } + } + else { + genGetActiveContextNumArgslargeinBlock(contextNumArgs, contextIsLarge, contextIsBlock); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, ClassReg); + } + numSlots = FullClosureFirstCopiedValueIndex + numCopied; + byteSize = smallObjectBytesForSlots(numSlots); + header = headerForSlotsformatclassIndex(numSlots, indexablePointersFormat(), ClassFullBlockClosureCompactIndex); + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveAwR, freeStartAddress(), ReceiverResultReg)); + + /* begin genStoreHeader:intoNewInstance:using: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, header, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(header, BytesPerOop)); + } + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, TempReg, 0, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* begin LoadEffectiveAddressMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(LoadEffectiveAddressMwrR, byteSize, ReceiverResultReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(byteSize, BytesPerOop)); + } + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveRAw, TempReg, freeStartAddress())); + quickConstant = getScavengeThreshold(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + skip = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceScheduleScavengeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpTarget(skip, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + offset = (ClosureOuterContextIndex * BytesPerOop) + BaseHeaderSize; + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, ClassReg, offset, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* begin genMoveConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(compiledBlock)) + && ((oopisGreaterThan(compiledBlock, classTableRootObj())) + || (oopisLessThan(compiledBlock, nilObject())))) { + annotateobjRef(checkLiteralforInstruction(compiledBlock, genoperandoperand(MoveCwR, compiledBlock, TempReg)), compiledBlock); + } + else { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, compiledBlock, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(compiledBlock, BytesPerOop)); + } + } + offset = (ClosureStartPCIndex * BytesPerOop) + BaseHeaderSize; + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, TempReg, offset, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, (((usqInt)numArgs << 3) | 1), TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize((((usqInt)numArgs << 3) | 1), BytesPerOop)); + } + offset = (ClosureNumArgsIndex * BytesPerOop) + BaseHeaderSize; + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, TempReg, offset, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + return 0; +} + + +/* Make sure that the oop in reg is not forwarded. + Use the fact that isForwardedObjectClassIndexPun is a power of two to save + an instruction. */ +/* maybe a fixup or an instruction */ +/* maybe a fixup or an instruction */ + + /* CogObjectRepresentationForSpur>>#genEnsureOopInRegNotForwarded:scratchReg:ifForwarder:ifNotForwarder: */ +static NoDbgRegParms sqInt +genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(sqInt reg, sqInt scratch, void *fwdJumpTarget, void *nonFwdJumpTargetOrZero) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *finished; + AbstractInstruction *imm; + AbstractInstruction *ok; + sqInt quickConstant; + + assert(reg != scratch); + imm = genJumpImmediate(reg); + + /* notionally + self genGetClassIndexOfNonImm: reg into: scratch. + cogit CmpCq: objectMemory isForwardedObjectClassIndexPun R: TempReg. + but the following is an instruction shorter: */ + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, 0, reg, scratch); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + quickConstant = (classIndexMask()) - (isForwardedObjectClassIndexPun()); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, scratch); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + ok = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genLoadSlotsourceRegdestReg(0, reg, reg); + + /* Jump: */ + genoperand(Jump, ((sqInt)fwdJumpTarget)); + finished = (((usqInt)nonFwdJumpTargetOrZero) + ? nonFwdJumpTargetOrZero + : genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jmpTarget(imm, jmpTarget(ok, finished)); + return 0; +} + + +/* Make sure that the oop in reg is not forwarded, updating the slot in + objReg with the value. + */ + + /* CogObjectRepresentationForSpur>>#genEnsureOopInRegNotForwarded:scratchReg:updatingSlot:in: */ +static NoDbgRegParms sqInt +genEnsureOopInRegNotForwardedscratchRegupdatingSlotin(sqInt reg, sqInt scratch, sqInt index, sqInt objReg) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + AbstractInstruction *imm; + AbstractInstruction *inst; + AbstractInstruction *loop; + sqInt offset; + AbstractInstruction *ok; + sqInt quickConstant; + + + /* Open-code + self genEnsureOopInRegNotForwarded: reg + scratchReg: scratch + updatingMw: index * objectMemory wordSize + objectMemory baseHeaderSize + r: objReg. + to avoid calling the store check unless the receiver is forwarded. */ + assert((reg != scratch) + && (objReg != scratch)); + loop = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + imm = genJumpImmediate(reg); + + /* notionally + self genGetClassIndexOfNonImm: reg into: scratch. + cogit CmpCq: objectMemory isForwardedObjectClassIndexPun R: TempReg. + but the following is an instruction shorter: */ + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, 0, reg, scratch); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + quickConstant = (classIndexMask()) - (isForwardedObjectClassIndexPun()); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, scratch); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + ok = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genLoadSlotsourceRegdestReg(0, reg, reg); + offset = (index * BytesPerWord) + BaseHeaderSize; + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, reg, offset, objReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* Check that we're meeting the contract of ceStoreCheckContextReceiverTrampoline. */ + assert((reg == Arg0Reg) + && ((scratch == TempReg) + && (objReg == ReceiverResultReg))); + + /* If a frame has been built then (if a link register is in use) the link register has been saved. + If not it must be saved and restored around the call since it will be used to return from the method. */ + if (needsFrame) { + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreCheckContextReceiverTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + backEnd; + + /* begin saveAndRestoreLinkRegAround: */ + inst = genoperand(PushR, LinkReg); + abstractInstruction = genoperand(Call, ceStoreCheckContextReceiverTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + + /* PopR: */ + genoperand(PopR, LinkReg); + } + + /* Jump: */ + genoperand(Jump, ((sqInt)loop)); + jmpTarget(ok, jmpTarget(imm, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return 0; +} + + +/* Create a trampoline to answer the active context that will answer it if a + frame is already married, + and create it otherwise. Assume numArgs is in SendNumArgsReg and ClassReg + is free. + These include machine code implementations of + CoInterpreter>>#marryFrame:SP: + */ + + /* CogObjectRepresentationForSpur>>#genGetActiveContextLarge:inBlock: */ +static NoDbgRegParms sqInt +genGetActiveContextLargeinBlock(sqInt isLarge, sqInt isInBlock) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + int callerSavedRegsToBeSaved; + AbstractInstruction *continuation; + AbstractInstruction *exit; + usqLong header; + AbstractInstruction *inst; + AbstractInstruction *jumpNeedScavenge; + AbstractInstruction *jumpSingle; + AbstractInstruction *lastInst; + AbstractInstruction *loopHead; + int mustSaveSPReg; + sqInt offset; + sqInt quickConstant; + sqInt reg; + int registersToBePushed; + int slotSize; + sqInt spareCalleeSavedReg; + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, FoxMethod, FPReg, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(FoxMethod, BytesPerOop)); + } + + /* begin TstCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(TstCqR, MFMethodFlagHasContextFlag, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(MFMethodFlagHasContextFlag, BytesPerOop)); + } + + /* jump if flag bit not set */ + jumpSingle = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* since the flag bit was set, get the context in the receiver reg and return */ + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, FoxThisContext, FPReg, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(FoxThisContext, BytesPerOop)); + } + + /* RetN: */ + genoperand(RetN, 0); + jmpTarget(jumpSingle, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* OK, it doesn't exist; instantiate and initialize it, mimicking CoInterpreter>>#marryFrame:SP: + set the hasContext flag; See CoInterpreter class>>initializeFrameIndices */ + + /* begin OrCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(OrCqR, MFMethodFlagHasContextFlag, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(MFMethodFlagHasContextFlag, BytesPerOop)); + } + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, ClassReg, FoxMethod, FPReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(FoxMethod, BytesPerOop)); + } + + /* now get the home CogMethod into ClassReg and save for post-instantiation. */ + switch (isInBlock) { + case InFullBlock: + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, 3, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(3, BytesPerOop)); + } + break; + case InVanillaBlock: + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, 3, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(3, BytesPerOop)); + } + + /* begin MoveM16:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveM16rR, 0, ClassReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, ClassReg); + break; + case 0: + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, 1, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + break; + default: + error("Case not found and no otherwise clause"); + } + + /* -3 is -(hasContext+isBlock) flags + -1 is hasContext flag + instantiate the context... */ + slotSize = (isLarge + ? LargeContextSlots + : SmallContextSlots); + header = headerForSlotsformatclassIndex(slotSize, indexablePointersFormat(), ClassMethodContextCompactIndex); + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveAwR, freeStartAddress(), ReceiverResultReg)); + + /* begin genStoreHeader:intoNewInstance:using: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, header, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(header, BytesPerOop)); + } + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, TempReg, 0, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + offset = smallObjectBytesForSlots(slotSize); + + /* begin LoadEffectiveAddressMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(LoadEffectiveAddressMwrR, offset, ReceiverResultReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveRAw, TempReg, freeStartAddress())); + quickConstant = getScavengeThreshold(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpNeedScavenge = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* Now initialize the fields of the context. See CoInterpreter>>marryFrame:SP: + sender gets frame pointer as a SmallInteger */ + + /* begin genSetSmallIntegerTagsIn:into: */ + /* begin LoadEffectiveAddressMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + continuation = genoperandoperandoperand(LoadEffectiveAddressMwrR, 1, FPReg, TempReg); + if (usesOutOfLineLiteral(continuation)) { + (continuation->dependent = locateLiteralsize(1, BytesPerOop)); + } + offset = BaseHeaderSize + (SenderIndex * BytesPerOop); + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, TempReg, offset, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* pc gets frame caller as a SmallInteger */ + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, FoxSavedFP, FPReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(FoxSavedFP, BytesPerOop)); + } + genSetSmallIntegerTagsIn(TempReg); + offset = BaseHeaderSize + (InstructionPointerIndex * BytesPerOop); + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, TempReg, offset, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* Set the method field, freeing up ClassReg again, and frame's context field, */ + offset = offsetof(CogMethod, methodObject); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, offset, ClassReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + offset = BaseHeaderSize + (MethodIndex * BytesPerWord); + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, TempReg, offset, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, ReceiverResultReg, FoxThisContext, FPReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(FoxThisContext, BytesPerOop)); + } + + /* Now compute stack pointer; this is stackPointer (- 1 for return pc if a CISC) - framePointer - wordSize (1 each for saved pc, method, context, receiver) + 1 (1-relative) + numArgs + TPR note - the code here is actually doing + context stackPointer := ((((fp - sp) / wordSize) - [3|4]) + num args) asSmallInteger */ + gSubRRR(SPReg, FPReg, TempReg); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, log2BytesPerWord(), TempReg); + + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, 3, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(3, BytesPerOop)); + } + + /* AddR:R: */ + genoperandoperand(AddRR, SendNumArgsReg, TempReg); + + /* TempReg := FPReg - SPReg */ + genConvertIntegerToSmallIntegerInReg(TempReg); + offset = BaseHeaderSize + (StackPointerIndex * BytesPerOop); + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, TempReg, offset, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* Set closureOrNil to either the stacked receiver or nil */ + if (isInBlock > 0) { + /* begin LoadEffectiveAddressMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(LoadEffectiveAddressMwrR, 2, SendNumArgsReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(2, BytesPerOop)); + } + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, TempReg, FPReg, TempReg); + } + else { + /* begin genMoveConstant:R: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, nilObject(), TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(nilObject(), BytesPerOop)); + } + } + offset = BaseHeaderSize + (ClosureIndex * BytesPerOop); + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, TempReg, offset, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* Set the receiver */ + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, FoxMFReceiver, FPReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(FoxMFReceiver, BytesPerOop)); + } + offset = BaseHeaderSize + (ReceiverIndex * BytesPerOop); + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, TempReg, offset, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* Now copy the arguments. This is tricky because of the shortage of registers,. ClassReg ranges + from 1 to numArgs (SendNumArgsReg), and from ReceiverIndex + 1 to ReceiverIndex + numArgs. + 1 to: numArgs do: + [:i| + temp := longAt(FPReg + ((SendNumArgs - i + 2) * wordSize)). +2 for saved pc and savedfp + longAtput(FPReg + FoxMFReceiver + (i * wordSize), temp)] + TPR note: this is a prime candidate for passing off to the backend to do at least faintly optimal code */ + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, 1, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + + /* begin CmpR:R: */ + assert(!(0 /* (SendNumArgsReg = SPReg) */)); + loopHead = genoperandoperand(CmpRR, SendNumArgsReg, ClassReg); + exit = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SendNumArgsReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, ClassReg, TempReg); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, 2, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(2, BytesPerOop)); + } + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, TempReg, FPReg, TempReg); + quickConstant = ReceiverIndex + (BaseHeaderSize / BytesPerWord); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, TempReg, ClassReg, ReceiverResultReg); + quickConstant = (ReceiverIndex + (BaseHeaderSize / BytesPerWord)) - 1; + + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, quickConstant, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* Jump: */ + genoperand(Jump, ((sqInt)loopHead)); + + /* +2 for saved fp and saved pc + Now convert ClassReg from frame index to context index + convert back adding 1 ;-) */ + jmpTarget(exit, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* Now compute number of fields to initialize to nil; set ClassReg to the destination index in the new context... + destination index is index of first non-arg temp, hence add SendNumArgs and header words to ReceiverIndex + 1 */ + gAddCqRR((ReceiverIndex + 1) + (BaseHeaderSize / BytesPerWord), SendNumArgsReg, ClassReg); + gSubRRR(SPReg, FPReg, SendNumArgsReg); + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, nilObject(), TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(nilObject(), BytesPerOop)); + } + + /* ...and loop filling the context's temp slots with nils. N.B. if CISC there is a retpc on the stack */ + + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + loopHead = genoperandoperand(SubCqR, BytesPerWord, SendNumArgsReg); + if (usesOutOfLineLiteral(loopHead)) { + (loopHead->dependent = locateLiteralsize(BytesPerWord, BytesPerOop)); + } + quickConstant = (FoxSavedFP - FoxMFReceiver) - BytesPerWord; + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + exit = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, TempReg, ClassReg, ReceiverResultReg); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, 1, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + + /* Jump: */ + genoperand(Jump, ((sqInt)loopHead)); + jmpTarget(exit, genoperand(RetN, 0)); + jmpTarget(jumpNeedScavenge, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + backEnd; + + /* begin saveAndRestoreLinkRegAround: */ + inst = genoperand(PushR, LinkReg); + spareCalleeSavedReg = 0; + assert(!(((((((1U << ReceiverResultReg) | (1U << SendNumArgsReg)) | (1U << ClassReg)) & (registerMaskFor(LinkReg))) != 0)))); + callerSavedRegsToBeSaved = CallerSavedRegisterMask & (((1U << ReceiverResultReg) | (1U << SendNumArgsReg)) | (1U << ClassReg)); + registersToBePushed = callerSavedRegsToBeSaved; + reg = 0; + while (registersToBePushed != 0) { + if (((registersToBePushed & 1) != 0)) { + /* PushR: */ + genoperand(PushR, reg); + } + reg += 1; + registersToBePushed = ((sqInt)(registersToBePushed)) >> 1; + } + + /* If the backEnd doesn't use the native SP register in generated code, and the non-native + SP is not callee-saved, then it too must be saved and restored around the call. */ + mustSaveSPReg = !(((ABICalleeSavedRegisterMask & ((1U << SPReg))) != 0)); + if (mustSaveSPReg) { + spareCalleeSavedReg = availableRegisterOrNoneIn(((ABICalleeSavedRegisterMask | ((1U << SPReg))) - ((1U << SPReg)))); + assert(!((spareCalleeSavedReg == NoReg))); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SPReg, spareCalleeSavedReg); + } + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceScheduleScavengeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + lastInst = abstractInstruction; + if (mustSaveSPReg) { + lastInst = genoperandoperand(MoveRR, spareCalleeSavedReg, SPReg); + } + while (reg > 0) { + reg -= 1; + if (((callerSavedRegsToBeSaved & (1ULL << reg)) != 0)) { + lastInst = genoperand(PopR, reg); + } + } + + /* PopR: */ + genoperand(PopR, LinkReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)continuation)); + return 0; +} + + +/* Get the active context into ReceiverResultReg, creating it if necessary. */ + + /* CogObjectRepresentationForSpur>>#genGetActiveContextNumArgs:large:inBlock: */ +static NoDbgRegParms sqInt +genGetActiveContextNumArgslargeinBlock(sqInt numArgs, sqInt isLargeContext, sqInt isInBlock) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + sqInt routine; + + if (isLargeContext) { + switch (isInBlock) { + case 0: + routine = ceLargeActiveContextInMethodTrampoline; + break; + case InVanillaBlock: + routine = ceLargeActiveContextInBlockTrampoline; + break; + case InFullBlock: + routine = ceLargeActiveContextInFullBlockTrampoline; + break; + default: + error("Case not found and no otherwise clause"); + } + } + else { + switch (isInBlock) { + case 0: + routine = ceSmallActiveContextInMethodTrampoline; + break; + case InVanillaBlock: + routine = ceSmallActiveContextInBlockTrampoline; + break; + case InFullBlock: + routine = ceSmallActiveContextInFullBlockTrampoline; + break; + default: + error("Case not found and no otherwise clause"); + } + } + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, numArgs, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(numArgs, BytesPerOop)); + } + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, routine); + (abstractInstruction->annotation = IsRelativeCall); + return 0; +} + + /* CogObjectRepresentationForSpur>>#genGetBits:ofFormatByteOf:into: */ +static NoDbgRegParms sqInt +genGetBitsofFormatByteOfinto(sqInt mask, sqInt sourceReg, sqInt destReg) +{ + AbstractInstruction *anInstruction; + + /* begin MoveMb:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMbrR, 3, sourceReg, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(3, BytesPerOop)); + } + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, mask, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(mask, BytesPerOop)); + } + return 0; +} + + +/* Fetch the instance's class index into destReg. */ + + /* CogObjectRepresentationForSpur>>#genGetClassIndexOfNonImm:into: */ +static NoDbgRegParms sqInt +genGetClassIndexOfNonImminto(sqInt sourceReg, sqInt destReg) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, 0, sourceReg, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + quickConstant = classIndexMask(); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return 0; +} + + +/* Fetch the class object whose index is in instReg into destReg. + It is non-obvious, but the Cogit assumes loading a class does not involve + a runtime call, so do not call classAtIndex: */ + + /* CogObjectRepresentationForSpur>>#genGetClassObjectOfClassIndex:into:scratchReg: */ +static NoDbgRegParms sqInt +genGetClassObjectOfClassIndexintoscratchReg(sqInt instReg, sqInt destReg, sqInt scratchReg) +{ + AbstractInstruction *anInstruction; + sqInt offset; + sqInt quickConstant; + + assert(instReg != destReg); + assert(instReg != scratchReg); + assert(destReg != scratchReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, instReg, scratchReg); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, classTableMajorIndexShift(), scratchReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), scratchReg); + assert(!(shouldAnnotateObjectReference(classTableRootObj()))); + offset = (classTableRootObj()) + BaseHeaderSize; + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, offset, scratchReg, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* MoveR:R: */ + genoperandoperand(MoveRR, instReg, scratchReg); + quickConstant = classTableMinorIndexMask(); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + quickConstant = ((usqInt)(BaseHeaderSize)) >> (shiftForWord()); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, scratchReg, destReg, destReg); + return 0; +} + + +/* Fetch the instance's class into destReg. If the instance is not the + receiver and is forwarded, follow forwarding. */ + + /* CogObjectRepresentationForSpur>>#genGetClassObjectOf:into:scratchReg:mayBeAForwarder: */ +static NoDbgRegParms sqInt +genGetClassObjectOfintoscratchRegmayBeAForwarder(sqInt instReg, sqInt destReg, sqInt scratchReg, sqInt mayBeForwarder) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpIsImm; + AbstractInstruction *jumpNotForwarded; + AbstractInstruction *loop; + sqInt quickConstant; + + if ((instReg == destReg) + || ((instReg == scratchReg) + || (destReg == scratchReg))) { + return BadRegisterSet; + } + loop = genoperandoperand(MoveRR, instReg, scratchReg); + quickConstant = tagMask(); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsImm = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Get least significant half of header word in destReg */ + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, 0, instReg, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* mask off class index */ + quickConstant = classIndexMask(); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + if (mayBeForwarder) { + /* if it is forwarded... */ + quickConstant = isForwardedObjectClassIndexPun(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpNotForwarded = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* ...follow the forwarding pointer and loop to fetch its classIndex */ + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, BaseHeaderSize, instReg, instReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + + /* Jump: */ + genoperand(Jump, ((sqInt)loop)); + jmpTarget(jumpNotForwarded, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } + jmpTarget(jumpIsImm, genoperandoperand(MoveRR, scratchReg, destReg)); + if (scratchReg) { + genGetClassObjectOfClassIndexintoscratchReg(destReg, scratchReg, TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, scratchReg, destReg); + } + else { + /* PushR: */ + genoperand(PushR, instReg); + genGetClassObjectOfClassIndexintoscratchReg(destReg, instReg, TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, instReg, destReg); + + /* PopR: */ + genoperand(PopR, instReg); + } + return 0; +} + + /* CogObjectRepresentationForSpur>>#genGetClassTagOf:into:scratchReg: */ +static NoDbgRegParms AbstractInstruction * +genGetClassTagOfintoscratchReg(sqInt instReg, sqInt destReg, sqInt scratchReg) +{ + return genGetInlineCacheClassTagFromintoforEntry(instReg, destReg, 1); +} + + +/* Fetch the instance's class index into destReg. */ + + /* CogObjectRepresentationForSpur>>#genGetCompactClassIndexNonImmOf:into: */ +static NoDbgRegParms sqInt +genGetCompactClassIndexNonImmOfinto(sqInt instReg, sqInt destReg) +{ + return genGetClassIndexOfNonImminto(instReg, destReg); +} + + /* CogObjectRepresentationForSpur>>#genGetDoubleValueOf:into: */ +static NoDbgRegParms sqInt +genGetDoubleValueOfinto(sqInt srcReg, sqInt destFPReg) +{ + AbstractInstruction *anInstruction; + + /* begin MoveM64:r:Rd: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveM64rRd, BaseHeaderSize, srcReg, destFPReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + return 0; +} + + +/* Get the format field of the object in srcReg into destReg. + srcReg may equal destReg. */ + + /* CogObjectRepresentationForSpur>>#genGetFormatOf:into: */ +static NoDbgRegParms sqInt +genGetFormatOfinto(sqInt srcReg, sqInt destReg) +{ + return genGetBitsofFormatByteOfinto(formatMask(), srcReg, destReg); +} + + +/* Get the format of the object in sourceReg into destReg. If + scratchRegOrNone is not NoReg, load at least the least significant 32-bits + (64-bits in 64-bits) of the + header word, which contains the format, into scratchRegOrNone. */ + + /* CogObjectRepresentationForSpur>>#genGetFormatOf:into:leastSignificantHalfOfBaseHeaderIntoScratch: */ +static NoDbgRegParms sqInt +genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(sqInt sourceReg, sqInt destReg, sqInt scratchRegOrNone) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + + if (scratchRegOrNone == NoReg) { + /* begin MoveMb:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMbrR, 3, sourceReg, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(3, BytesPerOop)); + } + } + else { + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, 0, sourceReg, scratchRegOrNone); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + gLogicalShiftRightCqRR(formatShift(), scratchRegOrNone, destReg); + } + quickConstant = formatMask(); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return 0; +} + + +/* Get the size in word-sized slots of the object in srcReg into destReg. + srcReg may equal destReg. */ + + /* CogObjectRepresentationForSpur>>#genGetNumSlotsOf:into: */ +static NoDbgRegParms sqInt +genGetNumSlotsOfinto(sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jmp; + sqInt quickConstant; + + assert(srcReg != destReg); + genGetRawSlotSizeOfNonImminto(srcReg, destReg); + quickConstant = numSlotsMask(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jmp = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + genGetOverflowSlotsOfinto(srcReg, destReg); + jmpTarget(jmp, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + +/* The raw numSlots field is the most significant byte of the 64-bit header + word. MoveMbrR zero-extends. */ + + /* CogObjectRepresentationForSpur>>#genGetRawSlotSizeOfNonImm:into: */ +static NoDbgRegParms sqInt +genGetRawSlotSizeOfNonImminto(sqInt sourceReg, sqInt destReg) +{ + AbstractInstruction *anInstruction; + + + /* begin MoveMb:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMbrR, 7, sourceReg, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(7, BytesPerOop)); + } + return 0; +} + + /* CogObjectRepresentationForSpur>>#genJumpImmediate: */ +static NoDbgRegParms AbstractInstruction * +genJumpImmediate(sqInt aRegister) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + + quickConstant = tagMask(); + + /* begin TstCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(TstCqR, quickConstant, aRegister); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +} + + /* CogObjectRepresentationForSpur>>#genJumpImmutable:scratchReg: */ +#if IMMUTABILITY +static NoDbgRegParms AbstractInstruction * +genJumpImmutablescratchReg(sqInt sourceReg, sqInt scratchReg) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, 0, sourceReg, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* begin genJumpBaseHeaderImmutable: */ + quickConstant = immutableBitMask(); + anInstruction = genoperandoperand(TstCqR, quickConstant, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +} +#endif /* IMMUTABILITY */ + + /* CogObjectRepresentationForSpur>>#genJumpMutable:scratchReg: */ +#if IMMUTABILITY +static NoDbgRegParms AbstractInstruction * +genJumpMutablescratchReg(sqInt sourceReg, sqInt scratchReg) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, 0, sourceReg, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* begin genJumpBaseHeaderMutable: */ + quickConstant = immutableBitMask(); + anInstruction = genoperandoperand(TstCqR, quickConstant, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return genConditionalBranchoperand(JumpZero, ((sqInt)0)); +} +#endif /* IMMUTABILITY */ + + +/* Generate a call to code that allocates a new Array of size. + The Array should be initialized with nils iff initialize is true. + The size arg is passed in SendNumArgsReg, the result + must come back in ReceiverResultReg. */ + + /* CogObjectRepresentationForSpur>>#genNewArrayOfSize:initialized: */ +static NoDbgRegParms sqInt +genNewArrayOfSizeinitialized(sqInt size, sqInt initialize) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + usqLong header; + sqInt i; + sqInt offset; + sqInt quickConstant; + AbstractInstruction *skip; + + assert(size < (numSlotsMask())); + header = headerForSlotsformatclassIndex(size, arrayFormat(), ClassArrayCompactIndex); + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveAwR, freeStartAddress(), ReceiverResultReg)); + + /* begin genStoreHeader:intoNewInstance:using: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, header, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(header, BytesPerOop)); + } + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, TempReg, 0, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + if (initialize + && (size > 0)) { + /* begin genMoveConstant:R: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, nilObject(), TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(nilObject(), BytesPerOop)); + } + for (i = 0; i < size; i += 1) { + offset = (i * BytesPerWord) + BaseHeaderSize; + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, TempReg, offset, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + } + } + offset = smallObjectBytesForSlots(size); + + /* begin LoadEffectiveAddressMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(LoadEffectiveAddressMwrR, offset, ReceiverResultReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveRAw, TempReg, freeStartAddress())); + quickConstant = getScavengeThreshold(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + skip = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceScheduleScavengeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpTarget(skip, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + +/* Create a closure with the given startpc, numArgs and numCopied + within a context with ctxtNumArgs, large if isLargeCtxt that is in a + block if isInBlock. Do /not/ initialize the copied values. */ + + /* CogObjectRepresentationForSpur>>#genNoPopCreateClosureAt:numArgs:numCopied:contextNumArgs:large:inBlock: */ +static NoDbgRegParms sqInt +genNoPopCreateClosureAtnumArgsnumCopiedcontextNumArgslargeinBlock(sqInt bcpc, sqInt numArgs, sqInt numCopied, sqInt ctxtNumArgs, sqInt isLargeCtxt, sqInt isInBlock) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + usqInt byteSize; + usqLong header; + sqInt numSlots; + sqInt offset; + sqInt quickConstant; + AbstractInstruction *skip; + + + /* First get thisContext into ReceiverResultRega and thence in ClassReg. */ + genGetActiveContextNumArgslargeinBlock(ctxtNumArgs, isLargeCtxt, isInBlock); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, ClassReg); + numSlots = ClosureFirstCopiedValueIndex + numCopied; + byteSize = smallObjectBytesForSlots(numSlots); + header = headerForSlotsformatclassIndex(numSlots, indexablePointersFormat(), ClassBlockClosureCompactIndex); + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveAwR, freeStartAddress(), ReceiverResultReg)); + + /* begin genStoreHeader:intoNewInstance:using: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, header, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(header, BytesPerOop)); + } + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, TempReg, 0, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* begin LoadEffectiveAddressMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(LoadEffectiveAddressMwrR, byteSize, ReceiverResultReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(byteSize, BytesPerOop)); + } + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveRAw, TempReg, freeStartAddress())); + quickConstant = getScavengeThreshold(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + skip = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceScheduleScavengeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpTarget(skip, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + offset = (ClosureOuterContextIndex * BytesPerOop) + BaseHeaderSize; + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, ClassReg, offset, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, (((usqInt)bcpc << 3) | 1), TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize((((usqInt)bcpc << 3) | 1), BytesPerOop)); + } + offset = (ClosureStartPCIndex * BytesPerOop) + BaseHeaderSize; + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, TempReg, offset, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, (((usqInt)numArgs << 3) | 1), TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize((((usqInt)numArgs << 3) | 1), BytesPerOop)); + } + offset = (ClosureNumArgsIndex * BytesPerOop) + BaseHeaderSize; + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, TempReg, offset, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + return 0; +} + + /* CogObjectRepresentationForSpur>>#genPrimitiveAsCharacter */ +static sqInt +genPrimitiveAsCharacter(void) +{ + AbstractInstruction *jumpNotInt; + AbstractInstruction *jumpOutOfRange; + sqInt reg; + + jumpNotInt = ((AbstractInstruction *) 0); + if (methodOrBlockNumArgs) { + if (methodOrBlockNumArgs > 1) { + return UnimplementedPrimitive; + } + reg = Arg0Reg; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpNotInt = genJumpNotSmallInteger(reg); + } + else { + reg = ReceiverResultReg; + } + + /* MoveR:R: */ + genoperandoperand(MoveRR, reg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + jumpOutOfRange = jumpNotCharacterUnsignedValueInRegister(TempReg); + genConvertSmallIntegerToCharacterInReg(reg); + if (reg != ReceiverResultReg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, reg, ReceiverResultReg); + } + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpOutOfRange, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + if (reg != ReceiverResultReg) { + jmpTarget(jumpNotInt, ((AbstractInstruction *) (((jumpOutOfRange->operands))[0]))); + } + return CompletePrimitive; +} + + /* CogObjectRepresentationForSpur>>#genPrimitiveAsFloat */ +static sqInt +genPrimitiveAsFloat(void) +{ + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* ConvertR:Rd: */ + genoperandoperand(ConvertRRd, TempReg, DPFPReg0); + genInstantiateFloatValueintoscratchRegscratchRegmayFail(DPFPReg0, ReceiverResultReg, ClassReg, TempReg, 0); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + return UnfailingPrimitive; +} + + +/* Generate primitive 60, at: with unsigned access for pure bits classes. */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveAt */ +static sqInt +genPrimitiveAt(void) +{ + return genPrimitiveAtSigned(0); +} + + +/* Generate primitive 61, at:put: with unsigned access for pure bits classes. */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveAtPut */ +static sqInt +genPrimitiveAtPut(void) +{ + return genPrimitiveAtPutSigned(0); +} + + /* CogObjectRepresentationForSpur>>#genPrimitiveFloatSquareRoot */ +static sqInt +genPrimitiveFloatSquareRoot(void) +{ + genGetDoubleValueOfinto(ReceiverResultReg, DPFPReg0); + + /* SqrtRd: */ + genoperand(SqrtRd, DPFPReg0); + genInstantiateFloatValueintoscratchRegscratchRegmayFail(DPFPReg0, ReceiverResultReg, ClassReg, TempReg, 0); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + return UnfailingPrimitive; +} + + /* CogObjectRepresentationForSpur>>#genPrimitiveIdenticalOrNotIf: */ +static NoDbgRegParms sqInt +genPrimitiveIdenticalOrNotIf(sqInt orNot) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *comp; + AbstractInstruction *jumpCmp; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + assert(!(0 /* (Arg0Reg = SPReg) */)); + comp = genoperandoperand(CmpRR, Arg0Reg, ReceiverResultReg); + if (orNot) { + jumpCmp = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* genEnsureOopInRegNotForwarded:scratchReg:jumpBackTo: */ + genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(Arg0Reg, TempReg, comp, 0); + } + else { + jumpCmp = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + } + + /* begin genMoveConstant:R: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, trueObject(), ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(trueObject(), BytesPerOop)); + } + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpCmp, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + if (!orNot) { + /* genEnsureOopInRegNotForwarded:scratchReg:jumpBackTo: */ + genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(Arg0Reg, TempReg, comp, 0); + } + + /* begin genMoveConstant:R: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(falseObject(), BytesPerOop)); + } + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + return UnfailingPrimitive; +} + + +/* Generate primitive 164, at: with signed access for pure bits classes. */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveIntegerAt */ +static sqInt +genPrimitiveIntegerAt(void) +{ + return genPrimitiveAtSigned(1); +} + + +/* Generate primitive 165, at:put: with signed access for pure bits classes. */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveIntegerAtPut */ +static sqInt +genPrimitiveIntegerAtPut(void) +{ + return genPrimitiveAtPutSigned(1); +} + + +/* */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveMakePoint */ +static sqInt +genPrimitiveMakePoint(void) +{ + sqInt allocSize; + AbstractInstruction *anInstruction; + AbstractInstruction *jumpFail; + usqLong newPointHeader; + sqInt offset; + sqInt quickConstant; + sqInt resultReg; + sqInt scratchReg; + + resultReg = ClassReg; + scratchReg = SendNumArgsReg; + + /* */ + allocSize = BaseHeaderSize + (BytesPerWord * 2); + newPointHeader = headerForSlotsformatclassIndex(2, nonIndexablePointerFormat(), ClassPointCompactIndex); + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveAwR, freeStartAddress(), resultReg)); + + /* begin LoadEffectiveAddressMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(LoadEffectiveAddressMwrR, allocSize, resultReg, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(allocSize, BytesPerOop)); + } + quickConstant = getScavengeThreshold(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpFail = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(freeStartAddress(), genoperandoperand(MoveRAw, scratchReg, freeStartAddress())); + + /* begin genStoreHeader:intoNewInstance:using: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, newPointHeader, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(newPointHeader, BytesPerOop)); + } + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, scratchReg, 0, resultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, ReceiverResultReg, BaseHeaderSize, resultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + offset = BaseHeaderSize + BytesPerWord; + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, Arg0Reg, offset, resultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* MoveR:R: */ + genoperandoperand(MoveRR, resultReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpFail, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + /* CogObjectRepresentationForSpur>>#genPrimitiveObjectAt */ +static sqInt +genPrimitiveObjectAt(void) +{ + AbstractInstruction *anInstruction; + sqInt headerReg; + AbstractInstruction *jumpBadIndex; + AbstractInstruction *jumpBounds; + AbstractInstruction *jumpNotHeaderIndex; + sqInt quickConstant; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpBadIndex = genJumpNotSmallInteger(Arg0Reg); + genGetMethodHeaderOfintoscratch(ReceiverResultReg, (headerReg = Arg1Reg), TempReg); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, (((usqInt)1 << 3) | 1), Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize((((usqInt)1 << 3) | 1), BytesPerOop)); + } + jumpNotHeaderIndex = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, headerReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotHeaderIndex, gAndCqR((((usqInt)(alternateHeaderNumLiteralsMask()) << 3) | 1), headerReg)); + quickConstant = ((((usqInt)1 << 3) | 1)) - (smallIntegerTag()); + + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, quickConstant, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* begin CmpR:R: */ + assert(!((headerReg == SPReg))); + genoperandoperand(CmpRR, headerReg, Arg0Reg); + jumpBounds = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + genConvertSmallIntegerToIntegerInReg(Arg0Reg); + quickConstant = ((usqInt)(BaseHeaderSize)) >> (shiftForWord()); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, Arg0Reg, ReceiverResultReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpBounds, gAddCqR(((((usqInt)1 << 3) | 1)) - (smallIntegerTag()), Arg0Reg)); + jmpTarget(jumpBadIndex, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + +/* c.f. StackInterpreter>>stSizeOf: lengthOf:baseHeader:format: + fixedFieldsOf:format:length: + */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveSize */ +static sqInt +genPrimitiveSize(void) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jump32BitLongsDone; + AbstractInstruction *jump64BitLongsDone; + AbstractInstruction *jumpArrayDone; + AbstractInstruction *jumpBytesDone; + AbstractInstruction *jumpHasFixedFields; + AbstractInstruction *jumpImm; + AbstractInstruction *jumpIs32BitLongs; + AbstractInstruction *jumpIsBytes; + AbstractInstruction *jumpIsContext; + AbstractInstruction *jumpIsContext1; + AbstractInstruction *jumpIsShorts; + AbstractInstruction *jumpNotIndexable; + AbstractInstruction *jumpNotIndexable1; + AbstractInstruction *jumpShortsDone; + sqInt quickConstant; + + jumpImm = genJumpImmediate(ReceiverResultReg); + + /* begin genGetSizeOf:into:formatReg:scratchReg:abortJumpsInto: */ + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, SendNumArgsReg, TempReg); + genGetNumSlotsOfinto(ReceiverResultReg, ClassReg); + + /* dispatch on format in a combination of highest dynamic frequency order first and convenience. + 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + quickConstant = firstByteFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsBytes = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + quickConstant = arrayFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpArrayDone = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + jumpNotIndexable1 = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + quickConstant = weakArrayFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpHasFixedFields = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + quickConstant = firstShortFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsShorts = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + quickConstant = firstLongFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIs32BitLongs = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + quickConstant = sixtyFourBitIndexableFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jump64BitLongsDone = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + jmpTarget(jumpNotIndexable1, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpNotIndexable1 = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpIsBytes, genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), ClassReg)); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, BytesPerWord - 1, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BytesPerWord - 1, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, SendNumArgsReg, ClassReg); + jumpBytesDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpIsShorts, gLogicalShiftLeftCqR((shiftForWord()) - 1, ClassReg)); + quickConstant = (((usqInt)(BytesPerWord)) >> 1) - 1; + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, SendNumArgsReg, ClassReg); + jumpShortsDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpIs32BitLongs, gLogicalShiftLeftCqR((shiftForWord()) - 2, ClassReg)); + quickConstant = (((usqInt)(BytesPerWord)) >> 2) - 1; + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, SendNumArgsReg, ClassReg); + jump32BitLongsDone = genoperand(Jump, ((sqInt)0)); + + /* formatReg contains fmt, now up for grabs. + destReg contains numSlots, precious. + sourceReg must be preserved */ + jmpTarget(jumpHasFixedFields, gAndCqR(classIndexMask(), TempReg)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, SendNumArgsReg); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, ClassMethodContextCompactIndex, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(ClassMethodContextCompactIndex, BytesPerOop)); + } + jumpIsContext1 = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + genGetClassObjectOfClassIndexintoscratchReg(SendNumArgsReg, Extra0Reg, TempReg); + genLoadSlotsourceRegdestReg(InstanceSpecificationIndex, Extra0Reg, SendNumArgsReg); + genConvertSmallIntegerToIntegerInReg(SendNumArgsReg); + quickConstant = fixedFieldsOfClassFormatMask(); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, SendNumArgsReg, ClassReg); + jmpTarget(jumpArrayDone, jmpTarget(jump64BitLongsDone, jmpTarget(jump32BitLongsDone, jmpTarget(jumpShortsDone, jmpTarget(jumpBytesDone, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))))); + jumpNotIndexable = jumpNotIndexable1; + jumpIsContext = jumpIsContext1; + genConvertIntegerInRegtoSmallIntegerInReg(ClassReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpImm, jmpTarget(jumpNotIndexable, jmpTarget(jumpIsContext, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + return CompletePrimitive; +} + + +/* primitiveCompareWith: */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveStringCompareWith */ +static sqInt +genPrimitiveStringCompareWith(void) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *instr; + AbstractInstruction *jump; + AbstractInstruction *jumpAbove; + AbstractInstruction *jumpIncorrectFormat1; + AbstractInstruction *jumpIncorrectFormat2; + AbstractInstruction *jumpIncorrectFormat3; + AbstractInstruction *jumpIncorrectFormat4; + AbstractInstruction *jumpMidFailure; + AbstractInstruction *jumpSuccess; + sqInt minSizeReg; + sqInt quickConstant; + sqInt string1CharOrByteSizeReg; + sqInt string1Reg; + sqInt string2CharOrByteSizeReg; + sqInt string2Reg; + + + /* I redefine those name to ease program comprehension */ + string1Reg = ReceiverResultReg; + string2Reg = Arg0Reg; + string1CharOrByteSizeReg = Arg1Reg; + string2CharOrByteSizeReg = ClassReg; + minSizeReg = SendNumArgsReg; + + /* Load arguments in reg */ + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* checks if string1 is a byteobject and get its size in bytes */ + genGetFormatOfinto(string1Reg, TempReg); + quickConstant = firstByteFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIncorrectFormat1 = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + quickConstant = firstCompiledMethodFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIncorrectFormat2 = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + genGetNumSlotsOfinto(string1Reg, string1CharOrByteSizeReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), string1CharOrByteSizeReg); + gAndCqRR(BytesPerWord - 1, TempReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, string1CharOrByteSizeReg); + genGetFormatOfinto(string2Reg, TempReg); + quickConstant = firstByteFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIncorrectFormat3 = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + quickConstant = firstCompiledMethodFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIncorrectFormat4 = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + genGetNumSlotsOfinto(string2Reg, string2CharOrByteSizeReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), string2CharOrByteSizeReg); + gAndCqRR(BytesPerWord - 1, TempReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, string2CharOrByteSizeReg); + + /* Type and number of arguments are correct + Compute the min */ + + /* begin CmpR:R: */ + assert(!((string1CharOrByteSizeReg == SPReg))); + genoperandoperand(CmpRR, string1CharOrByteSizeReg, string2CharOrByteSizeReg); + jumpAbove = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, string1CharOrByteSizeReg, minSizeReg); + jump = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpAbove, genoperandoperand(MoveRR, string2CharOrByteSizeReg, minSizeReg)); + jmpTarget(jump, gCmpCqR(0, minSizeReg)); + + /* if one of the string is empty, no need to go through the comparing loop */ + /* Compare the bytes */ + jumpSuccess = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, BaseHeaderSize, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, BaseHeaderSize, minSizeReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + instr = genoperandoperandoperand(MoveXbrRR, TempReg, string1Reg, string1CharOrByteSizeReg); + + /* MoveXbr:R:R: */ + genoperandoperandoperand(MoveXbrRR, TempReg, string2Reg, string2CharOrByteSizeReg); + + /* SubR:R: */ + genoperandoperand(SubRR, string2CharOrByteSizeReg, string1CharOrByteSizeReg); + + /* the 2 compared characters are different, exit the loop */ + jumpMidFailure = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, 1, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, minSizeReg); + + /* JumpNonZero: */ + genConditionalBranchoperand(JumpNonZero, ((sqInt)instr)); + + /* all bytes from 1 to minSize are equal */ + genGetNumBytesOfinto(string1Reg, string1CharOrByteSizeReg); + genGetNumBytesOfinto(string2Reg, string2CharOrByteSizeReg); + jmpTarget(jumpSuccess, genoperandoperand(SubRR, string2CharOrByteSizeReg, string1CharOrByteSizeReg)); + jmpTarget(jumpMidFailure, genoperandoperand(MoveRR, string1CharOrByteSizeReg, ReceiverResultReg)); + genConvertIntegerToSmallIntegerInReg(ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIncorrectFormat4, jmpTarget(jumpIncorrectFormat3, jmpTarget(jumpIncorrectFormat2, jmpTarget(jumpIncorrectFormat1, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))); + return CompletePrimitive; +} + + +/* replaceFrom: start to: stop with: replacement startingAt: repStart. + + The primitive in the JIT tries to deal with two pathological cases, copy + of arrays and byteStrings, + which often copies only a dozen of fields and where switching to the C + runtime cost a lot. + + Based on heuristics on the method class, I generate a quick array path + (typically for Array), + a quick byteString path (typically for ByteString, ByteArray and + LargeInteger) or no quick + path at all (Typically for Bitmap). + + The many tests to ensure that the primitive won't fail are not super + optimised (multiple reloading + or stack arguments in registers) but this is still good enough and worth + it since we're avoiding + the Smalltalk to C stack switch. The tight copying loops are optimised. + + It is possible to build a bigger version with the 2 different paths but I + (Clement) believe this + is too big machine code wise to be worth it. + */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveStringReplace */ +static sqInt +genPrimitiveStringReplace(void) +{ + sqInt adjust; + AbstractInstruction *anInstruction; + sqInt arrayReg; + AbstractInstruction *inst; + AbstractInstruction *instr; + AbstractInstruction *jmpAlreadyRemembered; + AbstractInstruction *jmpDestYoung; + AbstractInstruction *jumpEmpty; + AbstractInstruction *jumpImm; + AbstractInstruction *jumpImmutable; + AbstractInstruction *jumpIncorrectFormat1; + AbstractInstruction *jumpIncorrectFormat2; + AbstractInstruction *jumpIncorrectFormat3; + AbstractInstruction *jumpIncorrectFormat4; + AbstractInstruction *jumpNotSmi1; + AbstractInstruction *jumpNotSmi2; + AbstractInstruction *jumpNotSmi3; + AbstractInstruction *jumpOutOfBounds1; + AbstractInstruction *jumpOutOfBounds2; + AbstractInstruction *jumpOutOfBounds3; + AbstractInstruction *jumpOutOfBounds4; + sqInt offset; + sqInt quickConstant; + sqInt replReg; + sqInt repStartReg; + sqInt result; + sqInt startReg; + sqInt stopReg; + + + /* Can I generate a quick path for this method ? */ + jumpOutOfBounds3 = ((AbstractInstruction *) 0); + jumpOutOfBounds4 = ((AbstractInstruction *) 0); + if (!((maybeMethodClassOfseemsToBeInstantiating(methodObj, arrayFormat())) + || (maybeMethodClassOfseemsToBeInstantiating(methodObj, firstByteFormat())))) { + return UnimplementedPrimitive; + } + + /* I redefine those name to ease program comprehension */ + arrayReg = ReceiverResultReg; + startReg = Arg0Reg; + stopReg = Arg1Reg; + replReg = ClassReg; + repStartReg = SendNumArgsReg; + + /* Load arguments in reg */ + + /* begin genStackArgAt:into: */ + offset = (0) * BytesPerWord; + anInstruction = genoperandoperandoperand(MoveMwrR, offset, SPReg, repStartReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* begin genStackArgAt:into: */ + offset = (1) * BytesPerWord; + anInstruction = genoperandoperandoperand(MoveMwrR, offset, SPReg, replReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* begin genStackArgAt:into: */ + offset = (2) * BytesPerWord; + anInstruction = genoperandoperandoperand(MoveMwrR, offset, SPReg, stopReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* begin genStackArgAt:into: */ + offset = (3) * BytesPerWord; + anInstruction = genoperandoperandoperand(MoveMwrR, offset, SPReg, startReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* start,stop,repStart Smis or fail the primitive */ + jumpNotSmi1 = genJumpNotSmallInteger(repStartReg); + jumpNotSmi2 = genJumpNotSmallInteger(stopReg); + jumpNotSmi3 = genJumpNotSmallInteger(startReg); + + /* repl non immediate or fail the primitive */ + jumpImm = genJumpImmediate(replReg); + + /* if start>stop primitive success */ + + /* begin CmpR:R: */ + assert(!((startReg == SPReg))); + genoperandoperand(CmpRR, startReg, stopReg); + jumpEmpty = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* If receiver immutable fail the primitive */ +# if IMMUTABILITY + jumpImmutable = genJumpImmutablescratchReg(ReceiverResultReg, TempReg); +# endif + + + /* 0 >= start, fail */ + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, (((usqInt)0 << 3) | 1), startReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize((((usqInt)0 << 3) | 1), BytesPerOop)); + } + jumpOutOfBounds1 = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + + /* 0 >= replStart, fail */ + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, (((usqInt)0 << 3) | 1), repStartReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize((((usqInt)0 << 3) | 1), BytesPerOop)); + } + jumpOutOfBounds2 = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + + /* --- Pointer object version --- */ + if (maybeMethodClassOfseemsToBeInstantiating(methodObj, arrayFormat())) { + /* Are they both array format ? */ + genGetFormatOfinto(arrayReg, TempReg); + genGetFormatOfinto(replReg, startReg); + quickConstant = arrayFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, startReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIncorrectFormat1 = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + quickConstant = arrayFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIncorrectFormat2 = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Both objects are arrays, */ + genGetNumSlotsOfinto(arrayReg, TempReg); + genConvertSmallIntegerToIntegerInReg(stopReg); + + /* arr size < stop */ + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, stopReg); + jumpOutOfBounds3 = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + + /* rep size < repStart - start + stop */ + genGetNumSlotsOfinto(replReg, TempReg); + + /* begin genStackArgAt:into: */ + offset = (3) * BytesPerWord; + anInstruction = genoperandoperandoperand(MoveMwrR, offset, SPReg, startReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + genConvertSmallIntegerToIntegerInReg(startReg); + genConvertSmallIntegerToIntegerInReg(repStartReg); + + /* SubR:R: */ + genoperandoperand(SubRR, startReg, stopReg); + + /* AddR:R: */ + genoperandoperand(AddRR, repStartReg, stopReg); + + /* stopReg: stop - start + repStart */ + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, stopReg); + jumpOutOfBounds4 = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + + /* Everything in bounds + PossibleRemembered object */ + + /* #MoveCw:R: #gen:literal:operand: */ + checkLiteralforInstruction(storeCheckBoundary(), genoperandoperand(MoveCwR, storeCheckBoundary(), TempReg)); + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, arrayReg); + jmpDestYoung = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + jmpAlreadyRemembered = genCheckRememberedBitOfscratch(arrayReg, TempReg); + backEnd; + + /* begin saveAndRestoreLinkRegAround: */ + inst = genoperand(PushR, LinkReg); + callStoreCheckTrampoline(); + + /* PopR: */ + genoperand(PopR, LinkReg); + jmpTarget(jmpDestYoung, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jmpTarget(jmpAlreadyRemembered, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* Copy the array */ + + /* begin genStackArgAt:into: */ + offset = (2) * BytesPerWord; + anInstruction = genoperandoperandoperand(MoveMwrR, offset, SPReg, stopReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + genConvertSmallIntegerToIntegerInReg(stopReg); + + /* Shift replReg to read it with startReg offset */ + + /* SubR:R: */ + genoperandoperand(SubRR, startReg, repStartReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), repStartReg); + + /* AddR:R: */ + genoperandoperand(AddRR, repStartReg, replReg); + adjust = (((usqInt)(BaseHeaderSize)) >> (shiftForWord())) - 1; + if (adjust) { + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, adjust, startReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(adjust, BytesPerOop)); + } + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, adjust, stopReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(adjust, BytesPerOop)); + } + } + + /* Tight copying loop */ + instr = genoperandoperandoperand(MoveXwrRR, startReg, replReg, TempReg); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, TempReg, startReg, arrayReg); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, 1, startReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + + /* begin CmpR:R: */ + assert(!((startReg == SPReg))); + genoperandoperand(CmpRR, startReg, stopReg); + + /* JumpAboveOrEqual: */ + genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)instr)); + jmpTarget(jumpEmpty, /* genPrimReturn */ + (methodOrBlockNumArgs <= (numRegArgs()) + ? genoperand(RetN, 0) + : genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord))); + + /* CANNOT REACH by falling though */ + jmpTarget(jumpIncorrectFormat1, jmpTarget(jumpIncorrectFormat2, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + } + + /* --- Byte object version --- */ + if (maybeMethodClassOfseemsToBeInstantiating(methodObj, firstByteFormat())) { + /* Are they both byte array format ? CompiledMethod excluded */ + genGetFormatOfinto(arrayReg, TempReg); + genGetFormatOfinto(replReg, repStartReg); + quickConstant = firstByteFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, repStartReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIncorrectFormat1 = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + quickConstant = firstCompiledMethodFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, repStartReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIncorrectFormat2 = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + quickConstant = firstByteFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIncorrectFormat3 = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + quickConstant = firstCompiledMethodFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIncorrectFormat4 = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* Both objects are byte arrays */ + genGetNumSlotsOfinto(arrayReg, startReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), startReg); + gAndCqRR(BytesPerWord - 1, TempReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, startReg); + genConvertSmallIntegerToIntegerInReg(stopReg); + + /* arr size < stop */ + + /* begin CmpR:R: */ + assert(!((startReg == SPReg))); + genoperandoperand(CmpRR, startReg, stopReg); + jumpOutOfBounds3 = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + + /* rep size < repStart - start + stop */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, repStartReg, TempReg); + + /* begin genStackArgAt:into: */ + offset = (0) * BytesPerWord; + anInstruction = genoperandoperandoperand(MoveMwrR, offset, SPReg, repStartReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* begin genStackArgAt:into: */ + offset = (3) * BytesPerWord; + anInstruction = genoperandoperandoperand(MoveMwrR, offset, SPReg, startReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + genConvertSmallIntegerToIntegerInReg(startReg); + genConvertSmallIntegerToIntegerInReg(repStartReg); + + /* SubR:R: */ + genoperandoperand(SubRR, startReg, stopReg); + + /* AddR:R: */ + genoperandoperand(AddRR, repStartReg, stopReg); + genGetNumSlotsOfinto(replReg, startReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), startReg); + gAndCqRR(BytesPerWord - 1, TempReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, startReg); + + /* stopReg: stop - start + repStart */ + + /* begin CmpR:R: */ + assert(!((startReg == SPReg))); + genoperandoperand(CmpRR, startReg, stopReg); + jumpOutOfBounds4 = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + + /* Everything in bounds + Copy the bytes */ + + /* begin genStackArgAt:into: */ + offset = (3) * BytesPerWord; + anInstruction = genoperandoperandoperand(MoveMwrR, offset, SPReg, startReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + genConvertSmallIntegerToIntegerInReg(startReg); + + /* begin genStackArgAt:into: */ + offset = (2) * BytesPerWord; + anInstruction = genoperandoperandoperand(MoveMwrR, offset, SPReg, stopReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + genConvertSmallIntegerToIntegerInReg(stopReg); + + /* Shift replReg to read it with startReg offset */ + + /* SubR:R: */ + genoperandoperand(SubRR, startReg, repStartReg); + + /* AddR:R: */ + genoperandoperand(AddRR, repStartReg, replReg); + adjust = BaseHeaderSize - 1; + if (adjust) { + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, adjust, startReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(adjust, BytesPerOop)); + } + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, adjust, stopReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(adjust, BytesPerOop)); + } + } + + /* Tight copying loop */ + instr = genoperandoperandoperand(MoveXbrRR, startReg, replReg, TempReg); + + /* MoveR:Xbr:R: */ + genoperandoperandoperand(MoveRXbrR, TempReg, startReg, arrayReg); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, 1, startReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + + /* begin CmpR:R: */ + assert(!((startReg == SPReg))); + genoperandoperand(CmpRR, startReg, stopReg); + + /* JumpAboveOrEqual: */ + genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)instr)); + jmpTarget(jumpEmpty, /* genPrimReturn */ + (methodOrBlockNumArgs <= (numRegArgs()) + ? genoperand(RetN, 0) + : genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord))); + + /* CANNOT REACH by falling though */ + jmpTarget(jumpIncorrectFormat4, jmpTarget(jumpIncorrectFormat3, jmpTarget(jumpIncorrectFormat2, jmpTarget(jumpIncorrectFormat1, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))); + } + if (((result = compileInterpreterPrimitive())) < 0) { + return result; + } + jmpTarget(jumpImm, jmpTarget(jumpNotSmi1, jmpTarget(jumpNotSmi2, jmpTarget(jumpNotSmi3, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))); + jmpTarget(jumpOutOfBounds1, jmpTarget(jumpOutOfBounds2, jmpTarget(jumpOutOfBounds3, jmpTarget(jumpOutOfBounds4, ((AbstractInstruction *) (((jumpImm->operands))[0])))))); +# if IMMUTABILITY + jmpTarget(jumpImmutable, ((AbstractInstruction *) (((jumpImm->operands))[0]))); +# endif + + return CompletePrimitive; +} + + /* CogObjectRepresentationForSpur>>#genSetSmallIntegerTagsIn: */ +static NoDbgRegParms sqInt +genSetSmallIntegerTagsIn(sqInt scratchReg) +{ + AbstractInstruction *anInstruction; + + /* begin OrCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(OrCqR, 1, scratchReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + return 0; +} + + +/* Create a trampoline to store-check the update of the receiver in a + closure's outerContext in compileBlockFrameBuild:. */ + + /* CogObjectRepresentationForSpur>>#genStoreCheckContextReceiverTrampoline */ +static usqInt +genStoreCheckContextReceiverTrampoline(void) +{ + usqInt startAddress; + + startAddress = methodZoneBase; + zeroOpcodeIndex(); + genStoreCheckReceiverRegvalueRegscratchReginFrame(ReceiverResultReg, Arg0Reg, TempReg, 0); + + /* RetN: */ + genoperand(RetN, 0); + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress("ceStoreCheckContextReceiver", startAddress); + recordRunTimeObjectReferences(); + return startAddress; +} + + +/* Generate the code for a store check of valueReg into destReg. */ + + /* CogObjectRepresentationForSpur>>#genStoreCheckReceiverReg:valueReg:scratchReg:inFrame: */ +static NoDbgRegParms sqInt +genStoreCheckReceiverRegvalueRegscratchReginFrame(sqInt destReg, sqInt valueReg, sqInt scratchReg, sqInt inFrame) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *inst; + AbstractInstruction *jmpAlreadyRemembered; + AbstractInstruction *jmpDestYoung; + AbstractInstruction *jmpImmediate; + AbstractInstruction *jmpSourceOld; + + + /* Is value stored an immediate? If so we're done */ + jmpAlreadyRemembered = ((AbstractInstruction *) 0); + jmpImmediate = genJumpImmediate(valueReg); + + /* Get the old/new boundary in scratchReg */ + + /* #MoveCw:R: #gen:literal:operand: */ + checkLiteralforInstruction(storeCheckBoundary(), genoperandoperand(MoveCwR, storeCheckBoundary(), scratchReg)); + + /* Is target young? If so we're done */ + + /* begin CmpR:R: */ + assert(!((scratchReg == SPReg))); + genoperandoperand(CmpRR, scratchReg, destReg); + jmpDestYoung = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* Is value stored old? If so we're done. */ + + /* begin CmpR:R: */ + assert(!((scratchReg == SPReg))); + genoperandoperand(CmpRR, scratchReg, valueReg); + jmpSourceOld = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* value is young and target is old. + Need to remember this only if the remembered bit is not already set. */ + if (!CheckRememberedInTrampoline) { + jmpAlreadyRemembered = genCheckRememberedBitOfscratch(destReg, scratchReg); + } + + /* Remembered bit is not set. Call store check to insert dest into remembered table. */ + assert(destReg == ReceiverResultReg); + + /* begin evaluateTrampolineCallBlock:protectLinkRegIfNot: */ + if (inFrame) { + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreCheckTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* begin saveAndRestoreLinkRegAround: */ + inst = genoperand(PushR, LinkReg); + abstractInstruction = genoperand(Call, ceStoreCheckTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + + /* PopR: */ + genoperand(PopR, LinkReg); + } + jmpTarget(jmpImmediate, jmpTarget(jmpDestYoung, jmpTarget(jmpSourceOld, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + if (!CheckRememberedInTrampoline) { + jmpTarget(jmpAlreadyRemembered, ((AbstractInstruction *) (((jmpSourceOld->operands))[0]))); + } + return 0; +} + + /* CogObjectRepresentationForSpur>>#genStoreSourceReg:slotIndex:destReg:scratchReg:inFrame:needsStoreCheck: */ +static NoDbgRegParms sqInt +genStoreSourceRegslotIndexdestRegscratchReginFrameneedsStoreCheck(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt inFrame, sqInt needsStoreCheck) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + sqInt offset; + + /* begin genTraceStores */ + if (traceStores > 0) { + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, TempReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceTraceStoreTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + } + + /* do the store */ + offset = (index * BytesPerWord) + BaseHeaderSize; + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, sourceReg, offset, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* now the check. needStoreCheck is false if the JIT has figured out that the value stored does not need the check (immediate, nil, true, false) */ + if (needsStoreCheck) { + return genStoreCheckReceiverRegvalueRegscratchReginFrame(destReg, sourceReg, scratchReg, inFrame); + } + return 0; +} + + +/* This method is used for unchecked stores in objects after their creation + (typically, inlined creation of Array, closures and some temp vectors). + Currently there is no need to do the immutability check here + */ + + /* CogObjectRepresentationForSpur>>#genStoreSourceReg:slotIndex:intoNewObjectInDestReg: */ +static NoDbgRegParms sqInt +genStoreSourceRegslotIndexintoNewObjectInDestReg(sqInt sourceReg, sqInt index, sqInt destReg) +{ + AbstractInstruction *anInstruction; + sqInt offset; + + offset = (index * BytesPerWord) + BaseHeaderSize; + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, sourceReg, offset, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + return 0; +} + + +/* Convention: + - RcvrResultReg holds the object mutated. + If immutability failure: + - TempReg holds the instance variable index mutated + if instVarIndex > numDedicatedStoreTrampoline + - ClassReg holds the value to store + Registers are not lived across this trampoline as the + immutability failure may need new stack frames. */ + + /* CogObjectRepresentationForSpur>>#genStoreTrampolineCalled:instVarIndex: */ +#if IMMUTABILITY +static NoDbgRegParms usqInt +genStoreTrampolineCalledinstVarIndex(char *trampolineName, sqInt instVarIndex) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpImmutable; + AbstractInstruction *jumpRC; + sqInt pushLinkReg; + sqInt quickConstant; + usqInt startAddress; + + startAddress = methodZoneBase; + zeroOpcodeIndex(); + if (CheckRememberedInTrampoline) { + /* begin genStoreTrampolineCheckingRememberedCalled:instVarIndex: */ + jumpImmutable = genJumpImmutablescratchReg(ReceiverResultReg, SendNumArgsReg); + + /* Store check + If on 64-bits and doing the remembered bit test here, we can combine the tests to fetch the header once. */ + quickConstant = 1U << (rememberedBitShift()); + + /* begin TstCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(TstCqR, quickConstant, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpRC = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* RetN: */ + genoperand(RetN, 0); + jmpTarget(jumpRC, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + compileTrampolineFornumArgsargargargargregsToSavepushLinkRegresultReg(remember, 1, ReceiverResultReg, null, null, null, 0 /* emptyRegisterMask */, 1, /* returnRegForStoreCheck */ + (((CallerSavedRegisterMask & ((1U << ReceiverResultReg))) != 0) + ? ReceiverResultReg + : ABIResultReg)); + jmpTarget(jumpImmutable, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + compileTrampolineFornumArgsargargargargregsToSavepushLinkRegresultReg(ceCannotAssignTowithIndexvalueToAssign, 3, ReceiverResultReg, (instVarIndex < (NumStoreTrampolines - 1) + ? (/* begin trampolineArgConstant: */ + assert(instVarIndex >= 0), + -2 - instVarIndex) + : TempReg), ClassReg, null, 0 /* emptyRegisterMask */, 1, NoReg); + } + else { + /* begin genStoreTrampolineNotCheckingRememberedCalled:instVarIndex: */ + genSmalltalkToCStackSwitch((pushLinkReg = 1 /* hasLinkRegister */)); + + /* SendNumArgsReg is mutated but we don't care as registers are not live across the trampoline. + There is no reason why registers cannot be saved over the remember: call, but since the + immutability check is a suspension point, registers cannot remain live. */ + jumpImmutable = genJumpImmutablescratchReg(ReceiverResultReg, SendNumArgsReg); + + /* Store check */ + compileCallFornumArgsargargargargresultRegregsToSave(remember, 1, ReceiverResultReg, null, null, null, /* returnRegForStoreCheck */ + (((CallerSavedRegisterMask & ((1U << ReceiverResultReg))) != 0) + ? ReceiverResultReg + : ABIResultReg), 0 /* emptyRegisterMask */); + genLoadStackPointers(backEnd); + genTrampolineReturn(pushLinkReg); + jmpTarget(jumpImmutable, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + compileCallFornumArgsargargargargresultRegregsToSave(ceCannotAssignTowithIndexvalueToAssign, 3, ReceiverResultReg, (instVarIndex < (NumStoreTrampolines - 1) + ? (/* begin trampolineArgConstant: */ + assert(instVarIndex >= 0), + -2 - instVarIndex) + : TempReg), ClassReg, null, NoReg, 0 /* emptyRegisterMask */); + genLoadStackPointers(backEnd); + genTrampolineReturn(pushLinkReg); + } + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress(trampolineName, startAddress); + recordRunTimeObjectReferences(); + return startAddress; +} +#endif /* IMMUTABILITY */ + + +/* Store check code is duplicated to use a single trampoline */ + + /* CogObjectRepresentationForSpur>>#genStoreWithImmutabilityAndStoreCheckSourceReg:slotIndex:destReg:scratchReg:needRestoreRcvr: */ +#if IMMUTABILITY +static NoDbgRegParms sqInt +genStoreWithImmutabilityAndStoreCheckSourceRegslotIndexdestRegscratchRegneedRestoreRcvr(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt needRestoreRcvr) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + AbstractInstruction *immutableJump; + AbstractInstruction *jmpAlreadyRemembered; + AbstractInstruction *jmpDestYoung; + AbstractInstruction *jmpImmediate; + AbstractInstruction *jmpSourceOld; + sqInt offset; + + jmpAlreadyRemembered = ((AbstractInstruction *) 0); + immutableJump = genJumpImmutablescratchReg(destReg, scratchReg); + + /* begin genTraceStores */ + if (traceStores > 0) { + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, TempReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceTraceStoreTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + } + + /* do the store */ + offset = (index * BytesPerWord) + BaseHeaderSize; + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, sourceReg, offset, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* store check */ + jmpImmediate = genJumpImmediate(sourceReg); + + /* Get the old/new boundary in scratchReg */ + + /* #MoveCw:R: #gen:literal:operand: */ + checkLiteralforInstruction(storeCheckBoundary(), genoperandoperand(MoveCwR, storeCheckBoundary(), scratchReg)); + + /* Is target young? If so we're done */ + + /* begin CmpR:R: */ + assert(!((scratchReg == SPReg))); + genoperandoperand(CmpRR, scratchReg, destReg); + jmpDestYoung = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* Is value stored old? If so we're done. */ + + /* begin CmpR:R: */ + assert(!((scratchReg == SPReg))); + genoperandoperand(CmpRR, scratchReg, sourceReg); + jmpSourceOld = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* value is young and target is old. + Need to remember this only if the remembered bit is not already set. */ + if (!CheckRememberedInTrampoline) { + jmpAlreadyRemembered = genCheckRememberedBitOfscratch(destReg, scratchReg); + } + + /* Set the inst var index for the benefit of the immutability check. The trampoline will + repeat the check to choose between the immutability violation and the store check. */ + jmpTarget(immutableJump, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin genStoreTrampolineCall: */ + assert(IMMUTABILITY); + if (index >= (NumStoreTrampolines - 1)) { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, index, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(index, BytesPerOop)); + } + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreTrampolines[NumStoreTrampolines - 1]); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreTrampolines[index]); + (abstractInstruction->annotation = IsRelativeCall); + } + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + if (needRestoreRcvr) { + genWriteCResultIntoReg(backEnd, ReceiverResultReg); + } + else { + if (((CallerSavedRegisterMask & ((1U << ReceiverResultReg))) != 0)) { + /* begin voidReceiverOptStatus */ + ((simSelf())->liveRegister = NoReg); + } + } + jmpTarget(jmpImmediate, jmpTarget(jmpDestYoung, jmpTarget(jmpSourceOld, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + if (!CheckRememberedInTrampoline) { + jmpTarget(jmpAlreadyRemembered, ((AbstractInstruction *) (((jmpSourceOld->operands))[0]))); + } + return 0; +} +#endif /* IMMUTABILITY */ + + +/* Gen an immutability check with no store check (e.g. assigning an immediate + literal) + */ +/* imm check has its own trampoline */ + + /* CogObjectRepresentationForSpur>>#genStoreWithImmutabilityButNoStoreCheckSourceReg:slotIndex:destReg:scratchReg:needRestoreRcvr: */ +#if IMMUTABILITY +static NoDbgRegParms sqInt +genStoreWithImmutabilityButNoStoreCheckSourceRegslotIndexdestRegscratchRegneedRestoreRcvr(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt needRestoreRcvr) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + AbstractInstruction *immutabilityFailure; + AbstractInstruction *mutableJump; + sqInt offset; + + mutableJump = genJumpMutablescratchReg(destReg, scratchReg); + + /* begin genStoreTrampolineCall: */ + assert(IMMUTABILITY); + if (index >= (NumStoreTrampolines - 1)) { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, index, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(index, BytesPerOop)); + } + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreTrampolines[NumStoreTrampolines - 1]); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreTrampolines[index]); + (abstractInstruction->annotation = IsRelativeCall); + } + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + + /* begin voidReceiverOptStatus */ + ((simSelf())->liveRegister = NoReg); + if (needRestoreRcvr) { + /* begin putSelfInReceiverResultReg */ + storeToReg(simSelf(), ReceiverResultReg); + } + immutabilityFailure = genoperand(Jump, ((sqInt)0)); + jmpTarget(mutableJump, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin genTraceStores */ + if (traceStores > 0) { + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, TempReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceTraceStoreTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + } + + /* do the store */ + offset = (index * BytesPerWord) + BaseHeaderSize; + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, sourceReg, offset, destReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + jmpTarget(immutabilityFailure, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} +#endif /* IMMUTABILITY */ + + +/* We know there is a frame as immutability check requires a frame */ +/* needRestoreRcvr has to be true to keep RcvrResultReg live with the + receiver in it across the trampoline + */ +/* Trampoline convention... */ + + /* CogObjectRepresentationForSpur>>#genStoreWithImmutabilityCheckSourceReg:slotIndex:destReg:scratchReg:needsStoreCheck:needRestoreRcvr: */ +#if IMMUTABILITY +static NoDbgRegParms sqInt +genStoreWithImmutabilityCheckSourceRegslotIndexdestRegscratchRegneedsStoreCheckneedRestoreRcvr(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt needsStoreCheck, sqInt needRestoreRcvr) +{ + assert(destReg == ReceiverResultReg); + assert(scratchReg == TempReg); + assert(sourceReg == ClassReg); + + /* The two paths are very different as the trampoline is shared in 1 case so we split here */ + if (needsStoreCheck) { + genStoreWithImmutabilityAndStoreCheckSourceRegslotIndexdestRegscratchRegneedRestoreRcvr(sourceReg, index, destReg, scratchReg, needRestoreRcvr); + } + else { + genStoreWithImmutabilityButNoStoreCheckSourceRegslotIndexdestRegscratchRegneedRestoreRcvr(sourceReg, index, destReg, scratchReg, needRestoreRcvr); + } + return 0; +} +#endif /* IMMUTABILITY */ + + +/* Make sure SendNumArgsReg and ClassReg are available in addition to + ReceiverResultReg and TempReg in + genGetActiveContextNumArgs:large:inBlock:. + */ + + /* CogObjectRepresentationForSpur>>#getActiveContextAllocatesInMachineCode */ +static sqInt +getActiveContextAllocatesInMachineCode(void) +{ + return 1; +} + + +/* Since all cache tags in Spur are class indices none of + them are young or have to be updated in a scavenge. */ + + /* CogObjectRepresentationForSpur>>#inlineCacheTagIsYoung: */ +static NoDbgRegParms sqInt +inlineCacheTagIsYoung(sqInt cacheTag) +{ + return 0; +} + + /* CogObjectRepresentationForSpur>>#jumpNotCharacterUnsignedValueInRegister: */ +static NoDbgRegParms AbstractInstruction * +jumpNotCharacterUnsignedValueInRegister(sqInt reg) +{ + AbstractInstruction *anInstruction; + sqInt quickConstant; + + quickConstant = (1U << (numCharacterBits())) - 1; + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return genConditionalBranchoperand(JumpAbove, ((sqInt)0)); +} + + +/* Mark and trace a literal in a machine code instruction preceding address + in cogMethodOrNil. + Answer if code was modified. */ + + /* CogObjectRepresentationForSpur>>#markAndTraceLiteral:in:atpc: */ +static NoDbgRegParms sqInt +markAndTraceLiteralinatpc(sqInt literal, CogMethod *cogMethodOrNil, usqInt address) +{ + sqInt objOop; + + if (!(couldBeObject(literal))) { + return 0; + } + assert(addressCouldBeObj(literal)); + if (!(isForwarded(literal))) { + markAndTrace(literal); + return 0; + } + + /* begin setCodeModified */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + if (!codeModified) { + codeModified = 1; + + /* begin ensureWritableCodeZone */ + if (needsCodeZoneExecuteWriteSwitch(backEnd)) { + ensureWritableCodeZoneAt(__LINE__); + } + } + } + else { + codeModified = 1; + } + objOop = followForwarded(literal); + storeLiteralbeforeFollowingAddress(backEnd, objOop, address); + markAndTraceUpdatedLiteralin(objOop, cogMethodOrNil); + return 1; +} + + +/* Mark and trace a literal in a sqInt variable of cogMethod. */ + + /* CogObjectRepresentationForSpur>>#markAndTraceLiteral:in:at: */ +static NoDbgRegParms void +markAndTraceLiteralinat(sqInt literal, CogMethod *cogMethod, sqInt *address) +{ + sqInt objOop; + + if (!(couldBeObject(literal))) { + return; + } + assert(addressCouldBeObj(literal)); + if (!(isForwarded(literal))) { + markAndTrace(literal); + return; + } + objOop = followForwarded(literal); + address[0] = objOop; + markAndTraceUpdatedLiteralin(objOop, cogMethod); +} + + +/* Common code to mark a literal in cogMethod and add + the cogMethod to youngReferrers if the literal is young. */ + + /* CogObjectRepresentationForSpur>>#markAndTraceUpdatedLiteral:in: */ +static NoDbgRegParms void +markAndTraceUpdatedLiteralin(sqInt objOop, CogMethod *cogMethodOrNil) +{ + if (isNonImmediate(objOop)) { + if ((cogMethodOrNil) + && (isYoungObject(objOop))) { + ensureInYoungReferrers(cogMethodOrNil); + } + markAndTrace(objOop); + } +} + + +/* If primIndex has an accessorDepth and fails, or it is external and fails + with PrimErrNoMemory, + call ceCheckAndMaybeRetryPrimitive if so If ceCheck.... answers true, + retry the primitive. */ + + /* CogObjectRepresentationForSpur>>#maybeCompileRetryOf:onPrimitiveFail:flags:jump:continue: */ +static NoDbgRegParms sqInt +maybeCompileRetryOfonPrimitiveFailflagsjumpcontinue(void (*primitiveRoutine)(void), sqInt primIndex, sqInt flags, AbstractInstruction *jmp, AbstractInstruction *label) +{ + if (!jmp) { + return 0; + } + jmpTarget(jmp, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* newMethod must be set unless it already has been. */ + if (!(((flags & PrimCallNeedsNewMethod) != 0))) { + genLoadNewMethod(); + } + + /* primitiveFunctionPointer must be set */ + + /* #MoveCw:R: #gen:literal:operand: */ + checkLiteralforInstruction(((sqInt)primitiveRoutine), genoperandoperand(MoveCwR, ((sqInt)primitiveRoutine), TempReg)); + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(primitiveFunctionPointerAddress(), genoperandoperand(MoveRAw, TempReg, primitiveFunctionPointerAddress())); + compileCallFornumArgsargargargargresultRegregsToSave( + ceCheckAndMaybeRetryPrimitive, + 1, + (/* begin trampolineArgConstant: */ + assert(primIndex >= 0), + -2 - primIndex), + null, + null, + null, + TempReg, + 0 /* emptyRegisterMask */); + + /* Jump: */ + genoperand(Jump, ((sqInt)label)); + return 0; +} + + +/* Generate a shift of the register containing the class tag in a method + cache probe. + c.f. SpurMemoryManager>>methodCacheHashOf:with: */ + + /* CogObjectRepresentationForSpur>>#maybeShiftClassTagRegisterForMethodCacheProbe: */ +static NoDbgRegParms sqInt +maybeShiftClassTagRegisterForMethodCacheProbe(sqInt classTagReg) +{ + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, 2, classTagReg); + return 0; +} + + /* CogObjectRepresentationForSpur>>#numCharacterBits */ +static sqInt +numCharacterBits(void) +{ + return 30; +} + + /* CogObjectRepresentationForSpur>>#remapObject: */ +static NoDbgRegParms sqInt +remapObject(sqInt objOop) +{ + assert(addressCouldBeObj(objOop)); + return (shouldRemapObj(objOop) + ? remapObj(objOop) + : objOop); +} + + /* CogObjectRepresentationForSpur>>#remapOop: */ +static NoDbgRegParms sqInt +remapOop(sqInt objOop) +{ + return (shouldRemapOop(objOop) + ? remapObj(objOop) + : objOop); +} + + +/* Objects in newSpace or oldSpace except nil, true, false & + classTableRootObj need to be annotated. + */ + + /* CogObjectRepresentationForSpur>>#shouldAnnotateObjectReference: */ +static NoDbgRegParms sqInt +shouldAnnotateObjectReference(sqInt anOop) +{ + return (isNonImmediate(anOop)) + && ((oopisGreaterThan(anOop, classTableRootObj())) + || (oopisLessThan(anOop, nilObject()))); +} + + /* CogObjectRepresentationForSpur>>#slotOffsetOfInstVarIndex: */ +static NoDbgRegParms sqInt +slotOffsetOfInstVarIndex(sqInt index) +{ + return (index * BytesPerWord) + BaseHeaderSize; +} + + /* CogSimStackEntry>>#ensureSpilledAt:from: */ +static NoDbgRegParms SimStackEntry * +ensureSpilledAtfrom(SimStackEntry *self_in_CogSimStackEntry, sqInt baseOffset, sqInt baseRegister) +{ + AbstractInstruction *anInstruction; + sqInt baseReg; + sqInt constant; + AbstractInstruction *inst; + sqInt offset; + + if ((self_in_CogSimStackEntry->spilled)) { + if (((self_in_CogSimStackEntry->type)) == SSSpill) { + assert(((((self_in_CogSimStackEntry->offset)) == baseOffset) + && (((self_in_CogSimStackEntry->registerr)) == baseRegister)) + || (violatesEnsureSpilledSpillAssert())); + return self_in_CogSimStackEntry; + } + } + assert(((self_in_CogSimStackEntry->type)) != SSSpill); + traceSpill(self_in_CogSimStackEntry); + if (((self_in_CogSimStackEntry->type)) == SSConstant) { + constant = (self_in_CogSimStackEntry->constant); + + /* begin genPushConstant: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(constant)) + && ((oopisGreaterThan(constant, classTableRootObj())) + || (oopisLessThan(constant, nilObject())))) { + inst = annotateobjRef(checkLiteralforInstruction(constant, genoperand(PushCw, constant)), constant); + } + else { + /* begin PushCq: */ + /* begin gen:quickConstant: */ + anInstruction = genoperand(PushCq, constant); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(constant, BytesPerOop)); + } + inst = anInstruction; + } + } + else { + if (((self_in_CogSimStackEntry->type)) == SSBaseOffset) { + offset = (self_in_CogSimStackEntry->offset); + baseReg = (self_in_CogSimStackEntry->registerr); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, offset, baseReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + inst = genoperand(PushR, TempReg); + } + else { + assert(((self_in_CogSimStackEntry->type)) == SSRegister); + inst = genoperand(PushR, (self_in_CogSimStackEntry->registerr)); + } + (self_in_CogSimStackEntry->type) = SSSpill; + (self_in_CogSimStackEntry->offset) = baseOffset; + (self_in_CogSimStackEntry->registerr) = baseRegister; + } + (self_in_CogSimStackEntry->spilled) = 1; + return self_in_CogSimStackEntry; +} + + /* CogSimStackEntry>>#isSameEntryAs: */ +static NoDbgRegParms sqInt +isSameEntryAs(SimStackEntry *self_in_CogSimStackEntry, SimStackEntry *ssEntry) +{ + return (((self_in_CogSimStackEntry->type)) == ((ssEntry->type))) + && ((((((self_in_CogSimStackEntry->type)) == SSBaseOffset) + || (((self_in_CogSimStackEntry->type)) == SSSpill)) + && ((((self_in_CogSimStackEntry->offset)) == ((ssEntry->offset))) + && (((self_in_CogSimStackEntry->registerr)) == ((ssEntry->registerr))))) + || (((((self_in_CogSimStackEntry->type)) == SSRegister) + && (((self_in_CogSimStackEntry->registerr)) == ((ssEntry->registerr)))) + || ((((self_in_CogSimStackEntry->type)) == SSConstant) + && (((self_in_CogSimStackEntry->constant)) == ((ssEntry->constant)))))); +} + + +/* Receiver is not a forwarder, except in blocks with no inst var access. + For now we optimize only the case where receiver is accessed in a method. */ + + /* CogSimStackEntry>>#mayBeAForwarder */ +static NoDbgRegParms sqInt +mayBeAForwarder(SimStackEntry *self_in_CogSimStackEntry) +{ + if ((((self_in_CogSimStackEntry->type)) == SSRegister) + && (isNonForwarderReceiver((self_in_CogSimStackEntry->registerr)))) { + return 0; + } + return ((self_in_CogSimStackEntry->type)) != SSConstant; +} + + /* CogSimStackEntry>>#popToReg: */ +static NoDbgRegParms SimStackEntry * +popToReg(SimStackEntry *self_in_CogSimStackEntry, sqInt reg) +{ + AbstractInstruction *anInstruction; + sqInt baseReg; + sqInt constant; + sqInt offset; + + if ((self_in_CogSimStackEntry->spilled)) { + /* PopR: */ + genoperand(PopR, reg); + } + else { + switch ((self_in_CogSimStackEntry->type)) { + case SSBaseOffset: + offset = (self_in_CogSimStackEntry->offset); + baseReg = (self_in_CogSimStackEntry->registerr); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, offset, baseReg, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + break; + case SSConstant: + constant = (self_in_CogSimStackEntry->constant); + + /* begin genMoveConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(constant)) + && ((oopisGreaterThan(constant, classTableRootObj())) + || (oopisLessThan(constant, nilObject())))) { + annotateobjRef(checkLiteralforInstruction(constant, genoperandoperand(MoveCwR, constant, reg)), constant); + } + else { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, constant, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(constant, BytesPerOop)); + } + } + break; + case SSRegister: + if (reg != ((self_in_CogSimStackEntry->registerr))) { + /* MoveR:R: */ + genoperandoperand(MoveRR, (self_in_CogSimStackEntry->registerr), reg); + } + else { + /* Label */ + genoperandoperand(Label, (labelCounter += 1), bytecodePC); + } + break; + default: + error("Case not found and no otherwise clause"); + } + } + return self_in_CogSimStackEntry; +} + + +/* Answer a bit mask for the receiver's register, if any. */ + + /* CogSimStackEntry>>#registerMask */ +static NoDbgRegParms sqInt +registerMask(SimStackEntry *self_in_CogSimStackEntry) +{ + return ((((self_in_CogSimStackEntry->type)) == SSBaseOffset) + || (((self_in_CogSimStackEntry->type)) == SSRegister) + ? ((((self_in_CogSimStackEntry->registerr)) < 0) ? (((usqInt)(1)) >> (-((self_in_CogSimStackEntry->registerr)))) : (1ULL << ((self_in_CogSimStackEntry->registerr)))) + : 0); +} + + /* CogSimStackEntry>>#registerMaskOrNone */ +static NoDbgRegParms sqInt +registerMaskOrNone(SimStackEntry *self_in_CogSimStackEntry) +{ + return (((self_in_CogSimStackEntry->type)) == SSRegister + ? ((((self_in_CogSimStackEntry->registerr)) < 0) ? (((usqInt)(1)) >> (-((self_in_CogSimStackEntry->registerr)))) : (1ULL << ((self_in_CogSimStackEntry->registerr)))) + : 0); +} + + /* CogSimStackEntry>>#registerOrNone */ +static NoDbgRegParms sqInt +registerOrNone(SimStackEntry *self_in_CogSimStackEntry) +{ + return (((self_in_CogSimStackEntry->type)) == SSRegister + ? (self_in_CogSimStackEntry->registerr) + : NoReg); +} + + /* CogSimStackEntry>>#storeToReg: */ +static NoDbgRegParms SimStackEntry * +storeToReg(SimStackEntry *self_in_CogSimStackEntry, sqInt reg) +{ + AbstractInstruction *anInstruction; + sqInt baseReg; + sqInt constant; + sqInt offset; + + switch ((self_in_CogSimStackEntry->type)) { + case SSBaseOffset: + case SSSpill: + offset = (self_in_CogSimStackEntry->offset); + baseReg = (self_in_CogSimStackEntry->registerr); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, offset, baseReg, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + break; + case SSConstant: + constant = (self_in_CogSimStackEntry->constant); + + /* begin genMoveConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(constant)) + && ((oopisGreaterThan(constant, classTableRootObj())) + || (oopisLessThan(constant, nilObject())))) { + annotateobjRef(checkLiteralforInstruction(constant, genoperandoperand(MoveCwR, constant, reg)), constant); + } + else { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, constant, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(constant, BytesPerOop)); + } + } + break; + case SSRegister: + if (reg != ((self_in_CogSimStackEntry->registerr))) { + /* MoveR:R: */ + genoperandoperand(MoveRR, (self_in_CogSimStackEntry->registerr), reg); + } + else { + /* Label */ + genoperandoperand(Label, (labelCounter += 1), bytecodePC); + } + break; + default: + error("Case not found and no otherwise clause"); + } + return self_in_CogSimStackEntry; +} + + /* CogSSBytecodeFixup>>#isMergeFixup */ +static NoDbgRegParms int +isMergeFixup(BytecodeFixup *self_in_CogSSBytecodeFixup) +{ + return (((usqInt)((self_in_CogSSBytecodeFixup->targetInstruction)))) == NeedsMergeFixupFlag; +} + + +/* Generate the run-time entries at the base of the native code zone and + update the base. + */ + + /* LiveTypingCogit>>#generateRunTimeTrampolines */ +static sqInt +generateRunTimeTrampolines(void) +{ + ceCollectInstVarType = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectInstVarTypeForatof, "ceCollectInstVarType", 3, ReceiverResultReg, Extra0Reg, TempReg, null, CallerSavedRegisterMask, 1, NoReg, 0); + + /* NOTE: the 2-arg trampolines stage their index in Extra1Reg, not Extra0Reg: the value is in + TempReg = CArg0Reg on ARMv8, and genMarshallNArgs relocates that value into Extra0Reg, which + would clobber an index kept there. (The 3-arg ceCollectInstVarType is safe with the index in + Extra0Reg because there its value is relocated into Extra1Reg instead.) */ + ceCollectTempVarType = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectTempVarTypeAtof, "ceCollectTempVarType", 2, Extra1Reg, TempReg, null, null, CallerSavedRegisterMask, 1, NoReg, 0); + ceCollectRemoteTempVarType = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectRemoteTempVarTypeAtof, "ceCollectRemoteTempVarType", 2, Extra1Reg, TempReg, null, null, CallerSavedRegisterMask, 1, NoReg, 0); + ceCollectArgumentsTypeFrameful = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectArgumentsTypeForFramefulActivation, "ceCollectArgsTypeFrameful", 0, null, null, null, null, 0 /* emptyRegisterMask */, 1, NoReg, 0); + ceCollectArgumentsTypeFramelessSpilled = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectArgumentsTypeForFramlessSpilledActivationOf, "ceCollectArgsTypeFramelessSpilled", 1, TempReg, null, null, null, (1U << ReceiverResultReg), 1, NoReg, 0); + ceCollectArgumentsTypeFramelessUnspilled1Args = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectArgumentsTypeForFramlessUnspilledActivationWithArgof, "ceCollectArgumentsTypeFramelessUnspilled1Args", 2, Arg0Reg, TempReg, null, null, ((1U << ReceiverResultReg) | (1U << Arg0Reg)) | (1U << Arg1Reg), 1, NoReg, 0); + ceCollectArgumentsTypeFramelessUnspilled2Args = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectArgumentsTypeForFramlessUnspilledActivationWithArgargof, "ceCollectArgumentsTypeFramelessUnspilled2Args", 3, Arg0Reg, Arg1Reg, TempReg, null, ((1U << ReceiverResultReg) | (1U << Arg0Reg)) | (1U << Arg1Reg), 1, NoReg, 0); + ceCollectReturnType = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectReturnTypeForof, "ceCollectReturnType", 2, TempReg, ReceiverResultReg, null, null, (1U << ReceiverResultReg), 1, NoReg, 0); + ceSendMustBeBooleanAddFalseTrampoline = genMustBeBooleanTrampolineForcalled(falseObject(), "ceSendMustBeBooleanAddFalseTrampoline"); + ceSendMustBeBooleanAddTrueTrampoline = genMustBeBooleanTrampolineForcalled(trueObject(), "ceSendMustBeBooleanAddTrueTrampoline"); + + /* begin genNonLocalReturnTrampoline */ + zeroOpcodeIndex(); + + /* write the return address to the coInterpreter instructionPointerAddress; + following the CallRT to this CISCs will have pushed it on the stack, so pop it first; RISCs will have it in + their link register so just write it directly. */ + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(instructionPointerAddress(), genoperandoperand(MoveRAw, LinkReg, instructionPointerAddress())); + ceNonLocalReturnTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceNonLocalReturn, "ceNonLocalReturnTrampoline", 1, ReceiverResultReg, null, null, null, 0 /* emptyRegisterMask */, 0, NoReg, 1); + + /* begin genCheckForInterruptsTrampoline */ + zeroOpcodeIndex(); + + /* if we have a link register we will assume that it does not get automatically pushed onto the stack + and thus there is no need to pop it before saving to instructionPointerAddress */ + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(instructionPointerAddress(), genoperandoperand(MoveRAw, LinkReg, instructionPointerAddress())); + ceCheckForInterruptTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCheckForInterrupt, "ceCheckForInterruptTrampoline", 0, null, null, null, null, 0 /* emptyRegisterMask */, 0, NoReg, 1); + + /* Neither of the context inst var access trampolines save registers. Their operation could cause + arbitrary update of stack frames, so the assumption is that callers flush the stack before calling + the context inst var access trampolines, and that everything except the result is dead afterwards. */ + ceFetchContextInstVarTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceContextinstVar, "ceFetchContextInstVarTrampoline", 2, ReceiverResultReg, SendNumArgsReg, null, null, 0 /* emptyRegisterMask */, 1, SendNumArgsReg, 0); + + /* to keep ReceiverResultReg live. */ + ceStoreContextInstVarTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceContextinstVarvalue, "ceStoreContextInstVarTrampoline", 3, ReceiverResultReg, SendNumArgsReg, ClassReg, null, 0 /* emptyRegisterMask */, 1, ReceiverResultReg, 0); + ceCannotResumeTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCannotResume, "ceCannotResumeTrampoline", 0, null, null, null, null, 0 /* emptyRegisterMask */, 1, NoReg, 0); + + /* ceInvokeInterpreter is an optimization and a work-around. Historically we used setjmp/longjmp to reenter the + interpreter at the current C stack base. The C stack base is set at start-up and on each callback enter and + callback return. The interpreter must be invoked whenever a non-machine-code method must be run. That might + be when invoking an interpreter method from one of the send linking routines (ceSend:...), or on continuing from + an evaluation primitive such as primitiveExecuteMethod. The problem here is that such primitives could have + been invoked by the interpreter or by machine code. So some form of non-local jump is required. But at least as + early as MSVC Community 2017, the Microshaft longjmp performs stack unwinding which gets hoplessly confused + (bless its little heart) by any stack switch between machine code and C stack, and raises a spurious + Stack cookie instrumentation code detected a stack-based buffer overrun + error from the bowels of gs_report.c _GSHandlerCheck. + Since the CoInterpreter maintains the base of the C stack in CFramePointer & CStackPointer, it is straight-forward + for us to simply call interpret after doing the switch to the C stack, avoiding the stack unwind issue altogether. */ + ceInvokeInterpret = genInvokeInterpretTrampoline(); + + /* These two are unusual; they are reached by return instructions. */ + ceReturnToInterpreterTrampoline = genReturnToInterpreterTrampoline(); + ceBaseFrameReturnTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceBaseFrameReturn, "ceBaseFrameReturnTrampoline", 1, ReceiverResultReg, null, null, null, 0 /* emptyRegisterMask */, 0, NoReg, 0); + return null; +} + + /* LiveTypingCogit>>#genMoveCurrentMethodOopToRegister: */ +static NoDbgRegParms void +genMoveCurrentMethodOopToRegister(sqInt reg) +{ + addDependent(methodLabel, annotateAbsolutePCRef(checkLiteralforInstruction(((sqInt)methodLabel), genoperandoperand(MoveCwR, ((sqInt)methodLabel), reg)))); +} + + /* LiveTypingCogit>>#genStorePop:ReceiverVariable:needsStoreCheck:needsImmutabilityCheck: */ +static NoDbgRegParms sqInt +genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(sqInt popBoolean, sqInt slotIndex, sqInt needsStoreCheck, sqInt needsImmCheck) +{ + AbstractInstruction *abstractInstruction; + sqInt i; + sqInt needsImmCheckSqInt; + sqInt needsStoreCheckSqInt; + sqInt topReg; + + ensureReceiverResultRegContainsSelf(); + storeToReg(ssTop(), TempReg); + + /* #MoveCw:R: #gen:literal:operand: */ + checkLiteralforInstruction(slotIndex, genoperandoperand(MoveCwR, slotIndex, Extra0Reg)); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectInstVarType); + (abstractInstruction->annotation = IsRelativeCall); + ssFlushUpThroughReceiverVariable(slotIndex); + preserveTopInClassRegIfIsReceiverResultReg(); + ensureReceiverResultRegContainsSelf(); + + /* In two path compilation the receiver is young AND mutable, hence no store check nor immutability check is needed */ + needsStoreCheckSqInt = (!useTwoPaths) + && (needsStoreCheck); + needsImmCheckSqInt = needsImmCheck + && (!useTwoPaths); + + /* begin genGenericStorePop:slotIndex:destReg:needsStoreCheck:needsRestoreRcvr:needsImmutabilityCheck: */ +# if IMMUTABILITY + if (needsImmCheckSqInt) { + /* begin ssAllocateRequiredReg:upThrough: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << ClassReg), simStackPtr - 1, simNativeStackPtr); + ssStoreAndReplacePoptoReg(popBoolean, ClassReg); + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + return genStoreWithImmutabilityCheckSourceRegslotIndexdestRegscratchRegneedsStoreCheckneedRestoreRcvr(ClassReg, slotIndex, ReceiverResultReg, TempReg, needsStoreCheckSqInt, 1); + } +# endif // IMMUTABILITY + + topReg = allocateRegForStackEntryAtnotConflictingWith(0, (1U << ReceiverResultReg)); + ssStorePoptoReg(popBoolean, topReg); + return genStoreSourceRegslotIndexdestRegscratchReginFrameneedsStoreCheck(topReg, slotIndex, ReceiverResultReg, TempReg, needsFrame, needsStoreCheckSqInt); +} + + +/* The only reason we assert needsFrame here is that in a frameless method + ReceiverResultReg must and does contain only self, but the ceStoreCheck + trampoline expects the target of the store to be in ReceiverResultReg. So + in a frameless method we would have a conflict between the receiver and + the temote temp store, unless we we smart enough to realise that + ReceiverResultReg was unused after the literal variable store, unlikely + given that methods return self by default. */ +/* Stage the trampoline arguments so they survive the ARM C-argument + marshaller. + The value goes in TempReg and the slot index in Extra1Reg. + TempReg = CArg0Reg on ARMv8, + so when genMarshallNArgs relocates the + value (the arg that aliases CArg0Reg) it moves it into + Extra0Reg; + staging the index in Extra1Reg (NOT Extra0Reg) keeps the index from being + clobbered + by that relocation, which would otherwise pass the value as + both tempVarIndex and assignedObject. + */ + + /* LiveTypingCogit>>#genStorePop:RemoteTemp:At:needsStoreCheck: */ +static NoDbgRegParms sqInt +genStorePopRemoteTempAtneedsStoreCheck(sqInt popBoolean, sqInt slotIndex, sqInt remoteTempIndex, sqInt needsStoreCheck) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + sqInt offset; + sqInt topReg; + + storeToReg(ssTop(), TempReg); + + /* #MoveCw:R: #gen:literal:operand: */ + checkLiteralforInstruction(slotIndex, genoperandoperand(MoveCwR, slotIndex, Extra1Reg)); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectRemoteTempVarType); + (abstractInstruction->annotation = IsRelativeCall); + assert(needsFrame); + + /* N.B. No need to check the stack for references because we generate code for + remote temp loads that stores the result in a register, deferring only the register push. */ + + /* begin ssAllocateRequiredReg: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << ReceiverResultReg), simStackPtr, simNativeStackPtr); + voidReceiverResultRegContainsSelf(); + offset = frameOffsetOfTemporary(remoteTempIndex); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, offset, FPReg, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* begin genGenericStorePop:slotIndex:destReg:needsStoreCheck:needsRestoreRcvr:needsImmutabilityCheck: */ +# if IMMUTABILITY +# endif + + topReg = allocateRegForStackEntryAtnotConflictingWith(0, (1U << ReceiverResultReg)); + ssStorePoptoReg(popBoolean, topReg); + return genStoreSourceRegslotIndexdestRegscratchReginFrameneedsStoreCheck(topReg, slotIndex, ReceiverResultReg, TempReg, needsFrame, needsStoreCheck); +} + + +/* Stage the trampoline arguments so they survive the ARM C-argument + marshaller. + The value goes in TempReg and the temp index in Extra1Reg. + TempReg = CArg0Reg on ARMv8, + so when genMarshallNArgs relocates the + value (the arg that aliases CArg0Reg) it moves it into + Extra0Reg; + staging the index in Extra1Reg (NOT Extra0Reg) keeps the index from being + clobbered + by that relocation, which would otherwise pass the value as + both tempVarIndex and assignedObject. + */ + + /* LiveTypingCogit>>#genStorePop:TemporaryVariable: */ +static NoDbgRegParms sqInt +genStorePopTemporaryVariable(sqInt popBoolean, sqInt tempIndex) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + sqInt offset; + sqInt reg; + + if (!(inBlock > 0)) { + storeToReg(ssTop(), TempReg); + + /* #MoveCw:R: #gen:literal:operand: */ + checkLiteralforInstruction(tempIndex, genoperandoperand(MoveCwR, tempIndex, Extra1Reg)); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectTempVarType); + (abstractInstruction->annotation = IsRelativeCall); + } + ssFlushUpThroughTemporaryVariable(tempIndex); + reg = ssStorePoptoPreferredReg(popBoolean, TempReg); + offset = frameOffsetOfTemporary(tempIndex); + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, reg, offset, FPReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + ((simStackAt(tempIndex + 1))->bcptr = bytecodePC); + return 0; +} + + +/* Generate a method return from within a method or a block. + Frameless method activation looks like + CISCs (x86): + receiver + args + sp-> ret pc. + RISCs (ARM): + receiver + args + ret pc in LR. + A fully framed activation is described in CoInterpreter + class>initializeFrameIndices. Return pops receiver and arguments off the + stack. Callee pushes the result. */ +/* Assumes return object to be in ReceiverResultReg already. + Collect the + method's return type before returning. + On architectures with a link + register (ARM), a frameless method holds the caller's + return address in + LinkReg and returns through it (see super's frameless RetN:), but the + + trampoline call is a branch-with-link that clobbers LinkReg. Save and + restore LinkReg + around the call so the return address survives. On CISC + (return address on the stack) + the call is stack-balanced and no + protection is needed. + */ + + /* LiveTypingCogit>>#genUpArrowReturn */ +static sqInt +genUpArrowReturn(void) +{ + AbstractInstruction *abstractInstruction; + sqInt i; + AbstractInstruction *inst; + sqInt offset; + + if (!(primitiveIndex > 0)) { + genMoveCurrentMethodOopToRegister(TempReg); + backEnd; + + /* begin saveAndRestoreLinkRegAround: */ + inst = genoperand(PushR, LinkReg); + abstractInstruction = genoperand(Call, ceCollectReturnType); + (abstractInstruction->annotation = IsRelativeCall); + + /* PopR: */ + genoperand(PopR, LinkReg); + } + + /* can't fall through */ + deadCode = 1; + if (inBlock > 0) { + assert(needsFrame); + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceNonLocalReturnTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + return 0; + } + if ( +# if IMMUTABILITY + needsFrame + && (!useTwoPaths) +# else + needsFrame +# endif + ) { + + /* MoveR:R: */ + genoperandoperand(MoveRR, FPReg, SPReg); + + /* PopR: */ + genoperand(PopR, FPReg); + + /* PopR: */ + genoperand(PopR, LinkReg); + + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + else { + offset = ((methodOrBlockNumArgs > (numRegArgs())) + || (regArgsHaveBeenPushed) + ? (methodOrBlockNumArgs + 1) * BytesPerWord + : 0); + + /* begin RetN: */ + genoperand(RetN, offset); + } + return 0; +} + + /* LiveTypingCogit>>#initSimStackForFramefulMethod: */ +static NoDbgRegParms void +initSimStackForFramefulMethod(sqInt startpc) +{ + AbstractInstruction *abstractInstruction; + SimStackEntry *cascade0; + SimStackEntry *desc; + sqInt i; + + /* N.B. Includes num args */ + simStackPtr = methodOrBlockNumTemps; + simSpillBase = methodOrBlockNumTemps + 1; + cascade0 = simSelf(); + (cascade0->type = SSBaseOffset); + (cascade0->spilled = 1); + (cascade0->registerr = FPReg); + (cascade0->offset = FoxMFReceiver); + (cascade0->liveRegister = NoReg); + + /* args */ + for (i = 1; i <= methodOrBlockNumArgs; i += 1) { + desc = simStackAt(i); + (desc->type = SSBaseOffset); + (desc->spilled = 1); + (desc->registerr = FPReg); + (desc->offset = FoxCallerSavedIP + (((methodOrBlockNumArgs - i) + 1) * BytesPerWord)); + (desc->bcptr = startpc); + } + + /* temps */ + for (i = (methodOrBlockNumArgs + 1); i <= simStackPtr; i += 1) { + desc = simStackAt(i); + (desc->type = SSBaseOffset); + (desc->spilled = 1); + (desc->registerr = FPReg); + (desc->offset = FoxMFReceiver - ((i - methodOrBlockNumArgs) * BytesPerWord)); + (desc->bcptr = startpc); + } + if ((inBlock > 0) + || (methodOrBlockNumArgs == 0)) { + return; + } + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectArgumentsTypeFrameful); + (abstractInstruction->annotation = IsRelativeCall); +} + + +/* Collect argument types on entry to a frameless method. Like + genUpArrowReturn, the + trampoline call is a branch-with-link that + clobbers LinkReg, and a frameless activation + holds the caller's return + address in LinkReg, so protect it with saveAndRestoreLinkRegAround: + (a + no-op on CISC). The spilled-args target reads the arguments at + stackPointer + 2*wordSize, + an offset that already accounts for this + protection push plus the trampoline's own + genSmalltalkToCStackSwitch + push, so no interpreter-side change is needed. + */ + + /* LiveTypingCogit>>#initSimStackForFramelessMethod: */ +static NoDbgRegParms void +initSimStackForFramelessMethod(sqInt startpc) +{ + AbstractInstruction *abstractInstruction; + SimStackEntry *cascade0; + SimStackEntry *desc; + sqInt i; + AbstractInstruction *inst; + + cascade0 = simSelf(); + (cascade0->type = SSRegister); + (cascade0->spilled = 0); + (cascade0->registerr = ReceiverResultReg); + (cascade0->liveRegister = ReceiverResultReg); + assert(methodOrBlockNumTemps == methodOrBlockNumArgs); + assert((numRegArgs()) <= 2); + if (((methodOrBlockNumArgs >= 1) && (methodOrBlockNumArgs <= (numRegArgs())))) { + desc = simStackAt(1); + (desc->type = SSRegister); + (desc->spilled = 0); + (desc->registerr = Arg0Reg); + (desc->bcptr = startpc); + if (methodOrBlockNumArgs > 1) { + desc = simStackAt(2); + (desc->type = SSRegister); + (desc->spilled = 0); + (desc->registerr = Arg1Reg); + (desc->bcptr = startpc); + } + } + else { + for (i = 1; i <= methodOrBlockNumArgs; i += 1) { + desc = simStackAt(i); + (desc->type = SSBaseOffset); + (desc->registerr = SPReg); + (desc->spilled = 1); + (desc->offset = (methodOrBlockNumArgs - i) * BytesPerWord); + (desc->bcptr = startpc); + } + } + simStackPtr = methodOrBlockNumArgs; + simSpillBase = methodOrBlockNumArgs + 1; + if ((inBlock > 0) + || (methodOrBlockNumArgs == 0)) { + return; + } + genMoveCurrentMethodOopToRegister(TempReg); + backEnd; + + /* begin saveAndRestoreLinkRegAround: */ + inst = genoperand(PushR, LinkReg); + switch (methodOrBlockNumArgs) { + case 1: + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectArgumentsTypeFramelessUnspilled1Args); + (abstractInstruction->annotation = IsRelativeCall); + break; + case 2: + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectArgumentsTypeFramelessUnspilled2Args); + (abstractInstruction->annotation = IsRelativeCall); + break; + default: + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectArgumentsTypeFramelessSpilled); + (abstractInstruction->annotation = IsRelativeCall); + } + + /* PopR: */ + genoperand(PopR, LinkReg); +} + + +/* Do the store check. Answer the argument for the benefit of the code + generator; ReceiverResultReg may be caller-saved and hence smashed by this + call. Answering + it allows the code generator to reload ReceiverResultReg cheaply. + In Spur the only thing we leave to the run-time is adding the receiver to + the remembered set and setting its isRemembered bit. */ + + /* LiveTypingCogObjectRepresentationFor64BitSpur>>#generateObjectRepresentationTrampolines */ +static void +generateObjectRepresentationTrampolines(void) +{ + sqInt instVarIndex; + AbstractInstruction *jumpSC; + + ceCollectTypesForPrimitiveSlotAtPut = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectTypesForPrimitiveSlotAtputof, "ceCollectTypesForPrimitiveSlotAtPut", 3, Arg0Reg, Arg1Reg, ReceiverResultReg, null, CallerSavedRegisterMask, 1, NoReg, 0); +# if IMMUTABILITY + for (instVarIndex = 0; instVarIndex < NumStoreTrampolines; instVarIndex += 1) { + ceStoreTrampolines[instVarIndex] = (genStoreTrampolineCalledinstVarIndex(trampolineNamenumArgslimit("ceStoreTrampoline", instVarIndex, NumStoreTrampolines - 2), instVarIndex)); + } +# endif // IMMUTABILITY + + + /* begin genStoreCheckTrampoline */ + if (CheckRememberedInTrampoline) { + zeroOpcodeIndex(); + jumpSC = genCheckRememberedBitOfscratch(ReceiverResultReg, ABIResultReg); + assert(((jumpSC->opcode)) == JumpNonZero); + (jumpSC->opcode = JumpZero); + + /* RetN: */ + genoperand(RetN, 0); + jmpTarget(jumpSC, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } + ceStoreCheckTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(remember, "ceStoreCheckTrampoline", 1, ReceiverResultReg, null, null, null, ((CallerSavedRegisterMask | ((1U << ReceiverResultReg))) - ((1U << ReceiverResultReg))), 1, /* returnRegForStoreCheck */ + (((CallerSavedRegisterMask & ((1U << ReceiverResultReg))) != 0) + ? ReceiverResultReg + : ABIResultReg), CheckRememberedInTrampoline); + ceStoreCheckContextReceiverTrampoline = genStoreCheckContextReceiverTrampoline(); + ceScheduleScavengeTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceScheduleScavenge, "ceScheduleScavengeTrampoline", 0, null, null, null, null, CallerSavedRegisterMask, 1, NoReg, 0); + ceSmallActiveContextInMethodTrampoline = genActiveContextTrampolineLargeinBlockcalled(0, 0, "ceSmallMethodContext"); + ceSmallActiveContextInBlockTrampoline = genActiveContextTrampolineLargeinBlockcalled(0, InVanillaBlock, "ceSmallBlockContext"); + ceSmallActiveContextInFullBlockTrampoline = genActiveContextTrampolineLargeinBlockcalled(0, InFullBlock, "ceSmallFullBlockContext"); + ceLargeActiveContextInMethodTrampoline = genActiveContextTrampolineLargeinBlockcalled(1, 0, "ceLargeMethodContext"); + ceLargeActiveContextInBlockTrampoline = genActiveContextTrampolineLargeinBlockcalled(1, InVanillaBlock, "ceLargeBlockContext"); + ceLargeActiveContextInFullBlockTrampoline = genActiveContextTrampolineLargeinBlockcalled(1, InFullBlock, "ceLargeFullBlockContext"); +} + + +/* Generate the code for primitive 174, instVarAt:put:. Defer to + StackInterpreterPrimitives>>primitiveSlotAtPut for Contexts. + */ +/* c.f. StackInterpreter>>stSizeOf: SpurMemoryManager>>lengthOf:format: + fixedFieldsOf:format:length: + */ +/* Assumes arguments to be in Arg0Reg and Arg1Reg, and receiver to be in + ReceiverResultReg. + Collect the assigned slot value's type before + performing the primitive store. + The at:put:/instVarAt:put: primitive is + compiled into a frameless method, which on + architectures with a link + register (ARM) keeps the caller's return address in LinkReg and + returns + through it. The ceCollectTypesForPrimitiveSlotAtPut trampoline call is a + + branch-with-link that clobbers LinkReg, so protect it with + saveAndRestoreLinkRegAround: + (a no-op on CISC, where the return address + is on the stack). Without this the frameless + primitive returns into + itself instead of its caller, corrupting the stack. + */ + + /* LiveTypingCogObjectRepresentationFor64BitSpur>>#genPrimitiveSlotAtPut */ +static sqInt +genPrimitiveSlotAtPut(void) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + sqInt formatReg; + AbstractInstruction *inst; + AbstractInstruction *jump64BitArgIsImmediate; + AbstractInstruction *jump64BitsOutOfBounds; + AbstractInstruction *jumpBadIndex; + AbstractInstruction *jumpBytesOutOfBounds; + AbstractInstruction *jumpBytesOutOfRange; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpImmutable; + AbstractInstruction *jumpIsBytes; + AbstractInstruction *jumpIsCompiledMethod; + AbstractInstruction *jumpIsContext; + AbstractInstruction *jumpIsShorts; + AbstractInstruction *jumpIsWords; + AbstractInstruction *jumpNonSmallIntegerValue; + AbstractInstruction *jumpNot64BitIndexable; + AbstractInstruction *jumpNot8ByteInteger; + AbstractInstruction *jumpNotIndexableBits; + AbstractInstruction *jumpNotPointers; + AbstractInstruction *jumpPointersOutOfBounds; + AbstractInstruction *jumpShortsOutOfBounds; + AbstractInstruction *jumpShortsOutOfRange; + AbstractInstruction *jumpWordsOutOfBounds; + AbstractInstruction *jumpWordsOutOfRange; + sqInt nSlotsOrBytesReg; + sqInt quickConstant; + AbstractInstruction *rejoin; + + backEnd; + + /* begin saveAndRestoreLinkRegAround: */ + inst = genoperand(PushR, LinkReg); + abstractInstruction = genoperand(Call, ceCollectTypesForPrimitiveSlotAtPut); + (abstractInstruction->annotation = IsRelativeCall); + + /* PopR: */ + genoperand(PopR, LinkReg); + nSlotsOrBytesReg = Extra0Reg; + + /* begin genLoadArgAtDepth:into: */ + assert(1 < (numRegArgs())); + assert(0 < (numRegArgs())); + jumpImmediate = genJumpImmediate(ReceiverResultReg); + jumpBadIndex = genJumpNotSmallInteger(Arg0Reg); + genConvertSmallIntegerToIntegerInReg(Arg0Reg); + + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, 1, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } +# if IMMUTABILITY + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), TempReg); + + /* begin genJumpBaseHeaderImmutable: */ + quickConstant = immutableBitMask(); + anInstruction = genoperandoperand(TstCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpImmutable = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +# else // IMMUTABILITY + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), NoReg); +# endif // IMMUTABILITY + + genGetNumSlotsOfinto(ReceiverResultReg, nSlotsOrBytesReg); + + /* dispatch on format in a combination of highest dynamic frequency order first and convenience. + 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + quickConstant = weakArrayFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpNotPointers = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + genGetClassIndexOfNonImminto(ReceiverResultReg, ClassReg); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, ClassMethodContextCompactIndex, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(ClassMethodContextCompactIndex, BytesPerOop)); + } + + /* Fail to StackInterpreterPrimitives>>primitiveSlotAtPut for Context */ + /* optimistic store check; assume index in range (almost always is). */ + jumpIsContext = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + genStoreCheckReceiverRegvalueRegscratchReginFrame(ReceiverResultReg, Arg1Reg, TempReg, 0); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpPointersOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + quickConstant = ((usqInt)(BaseHeaderSize)) >> (shiftForWord()); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, Arg1Reg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotPointers, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpNonSmallIntegerValue = genJumpNotSmallInteger(Arg1Reg); + quickConstant = firstByteFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsBytes = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + quickConstant = firstShortFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsShorts = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + quickConstant = firstLongFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsWords = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + quickConstant = sixtyFourBitIndexableFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpNotIndexableBits = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* fall through to 64-bit words */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, SendNumArgsReg); + genConvertSmallIntegerToIntegerInReg(SendNumArgsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + rejoin = genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jump64BitsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + quickConstant = ((usqInt)(BaseHeaderSize)) >> (shiftForWord()); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, SendNumArgsReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* Handle LargePositiveInteger (unsigned) or LargePositiveInteger and LargeNegativeInteger (signed) + for sixtyFourBitIndexableFormat */ + jmpTarget(jumpNonSmallIntegerValue, gCmpCqR(sixtyFourBitIndexableFormat(), formatReg)); + jumpNot64BitIndexable = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + jump64BitArgIsImmediate = genJumpImmediate(Arg1Reg); + + /* Now check if the header is that of an 8 byte LargePositiveInteger */ + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, 0, Arg1Reg, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + quickConstant = headerForSlotsformatclassIndex(numSlotsMask(), formatMask(), classIndexMask()); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + quickConstant = headerForSlotsformatclassIndex(1, firstByteFormat(), ClassLargePositiveIntegerCompactIndex); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpNot8ByteInteger = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, BaseHeaderSize, Arg1Reg, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + + /* Jump: */ + genoperand(Jump, ((sqInt)rejoin)); + jmpTarget(jumpIsWords, gCmpCqR((((usqInt)0xFFFFFFFFU << 3) | 1), Arg1Reg)); + jumpWordsOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - 2, nSlotsOrBytesReg); + quickConstant = (BytesPerWord / 4) - 1; + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrBytesReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpWordsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + quickConstant = ((usqInt)(BaseHeaderSize)) >> ((shiftForWord()) - 1); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* MoveR:X32r:R: */ + genoperandoperandoperand(MoveRX32rR, TempReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsShorts, gCmpCqR((((usqInt)0xFFFF << 3) | 1), Arg1Reg)); + jumpShortsOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - 1, nSlotsOrBytesReg); + quickConstant = (BytesPerWord / 2) - 1; + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrBytesReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpShortsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg0Reg, ReceiverResultReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg0Reg, ReceiverResultReg); + + /* begin MoveR:M16:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRM16r, TempReg, BaseHeaderSize, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsBytes, gCmpCqR((((usqInt)0xFF << 3) | 1), Arg1Reg)); + jumpBytesOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), nSlotsOrBytesReg); + gAndCqRR(BytesPerWord - 1, formatReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, nSlotsOrBytesReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpBytesOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + quickConstant = firstCompiledMethodFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, formatReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpIsCompiledMethod = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, BaseHeaderSize, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BaseHeaderSize, BytesPerOop)); + } + + /* MoveR:Xbr:R: */ + genoperandoperandoperand(MoveRXbrR, TempReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNot8ByteInteger, jmpTarget(jump64BitArgIsImmediate, jmpTarget(jumpNot64BitIndexable, jmpTarget(jumpIsContext, jmpTarget(jumpNotIndexableBits, jmpTarget(jumpBytesOutOfRange, jmpTarget(jumpShortsOutOfRange, jmpTarget(jumpWordsOutOfRange, jmpTarget(jumpIsCompiledMethod, jmpTarget(jumpPointersOutOfBounds, jmpTarget(jumpBytesOutOfBounds, jmpTarget(jumpShortsOutOfBounds, jmpTarget(jumpWordsOutOfBounds, jmpTarget(jump64BitsOutOfBounds, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))))))))))))); +# if IMMUTABILITY + jmpTarget(jumpImmutable, ((AbstractInstruction *) (((jumpIsContext->operands))[0]))); +# endif + + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, 1, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + genConvertIntegerToSmallIntegerInReg(Arg0Reg); + jmpTarget(jumpBadIndex, jmpTarget(jumpImmediate, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return 0; +} + + +/* Allocate an unsharable Literal instruction for the literal and answer it. */ + + /* OutOfLineLiteralsManager>>#allocateLiteral: */ +static NoDbgRegParms AbstractInstruction * +allocateLiteral(sqInt aLiteral) +{ + AbstractInstruction *existingInst; + sqInt i; + sqInt initialNumLiterals; + AbstractInstruction *litInst; + AbstractInstruction *newInst; + AbstractInstruction *newLiterals; + + if (nextLiteralIndex >= literalsSize) { + initialNumLiterals = literalsSize + 8; + + /* begin allocateLiterals: */ + if (initialNumLiterals > literalsSize) { + newLiterals = calloc(initialNumLiterals, sizeof(CogAbstractInstruction)); + + /* Must copy across state (not using realloc, cuz...) and + must also update existing instructions to refer to the new ones... + It's either this or modify all generation routines to be able to retry + with more literals after running out of literals. */ + if (literals) { + for (i = 0; i < nextLiteralIndex; i += 1) { + existingInst = literalInstructionAt(i); + newInst = (&(newLiterals[i])); + cloneLiteralFrom(newInst, existingInst); + assert(!((existingInst->dependent))); + (existingInst->dependent = newInst); + } + for (i = 0; i < opcodeIndex; i += 1) { + existingInst = abstractInstructionAt(i); + if ((((existingInst->dependent))) + && (((((existingInst->dependent))->opcode)) == Literal)) { + (existingInst->dependent = (((existingInst->dependent))->dependent)); + } + } + } + free(literals); + literals = newLiterals; + literalsSize = initialNumLiterals; + } + } + litInst = literalInstructionAt(nextLiteralIndex); + initializeUniqueLiteral(litInst, aLiteral); + nextLiteralIndex += 1; + + /* Record the opcodeIndex of the first dependent instruction (the first instruction that references an out-of-line literal) */ + if (firstOpcodeIndex > opcodeIndex) { + firstOpcodeIndex = opcodeIndex - 1; + } + return litInst; +} + + +/* A literal is in range if its opcode index is within + outOfLineLiteralOpcodeLimit, or if its index has yet to be assigned. */ + + /* OutOfLineLiteralsManager>>#literalInstructionInRange: */ +static NoDbgRegParms sqInt +literalInstructionInRange(AbstractInstruction *litInst) +{ + sqInt opcodeIdx; + + /* begin literalOpcodeIndex */ + assert(((litInst->opcode)) == Literal); + opcodeIdx = ((sqInt)(((litInst->operands))[2])); + return ((((sqInt)opcodeIdx)) < 0) + || ((assert((getOpcodeIndex()) >= opcodeIdx), + (opcodeIndex - opcodeIdx) < (outOfLineLiteralOpcodeLimit(backEnd)))); +} + + /* OutOfLineLiteralsManagerFor64Bits>>#checkLiteral32:forInstruction: */ +static NoDbgRegParms AbstractInstruction * +checkLiteral32forInstruction(sqInt literal, AbstractInstruction *anInstruction) +{ + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(literal, 4)); + } + return anInstruction; +} + + /* OutOfLineLiteralsManagerFor64Bits>>#checkLiteral:forInstruction: */ +static NoDbgRegParms AbstractInstruction * +checkLiteralforInstruction(sqInt literal, AbstractInstruction *anInstruction) +{ + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(literal, BytesPerOop)); + } + return anInstruction; +} + + +/* Output all pending literal instructions, making the originals dependents + on the generated ones + so that a later pass will copy the address of each generated literal inst + to its original in literals, + and hence allow the instruction using the literal to compute the correct + address. + Override to segregate 64-bit and 32-bit literals */ + + /* OutOfLineLiteralsManagerFor64Bits>>#dumpLiterals: */ +static NoDbgRegParms sqInt +dumpLiterals(sqInt generateBranchAround) +{ + AbstractInstruction *cascade0; + AbstractInstruction *cascade1; + sqInt i; + AbstractInstruction *jump; + AbstractInstruction *litInst; + + jump = ((AbstractInstruction *) 0); + if (generateBranchAround) { + jump = genoperand(Jump, ((sqInt)0)); + } + + /* Align and dump the 64-bit literals. + We may waste a slot here we could fill with a 32-bit literal, but for now, KISS... */ + genoperand(AlignmentNops, 8 /* literalAlignment */); + for (i = lastDumpedLiteralIndex; i < nextLiteralIndex; i += 1) { + litInst = literalInstructionAt(i); + if (((/* begin literalSize */ + assert(((litInst->opcode)) == Literal), + (((litInst->operands))[1] + ? (((usqInt)((((litInst->operands))[1]))) >> 1) & 15 + : BytesPerOop))) == 8) { + cascade0 = genoperand(Literal, ((litInst->operands))[0]); + (cascade0->dependent = litInst); + setLiteralSize(cascade0, 8); + + /* begin setLiteralOpcodeIndex: */ + assert(((litInst->opcode)) == Literal); + ((litInst->operands))[2] = opcodeIndex; + } + } + + /* Now dump the 32-bit literals */ + for (i = lastDumpedLiteralIndex; i < nextLiteralIndex; i += 1) { + litInst = literalInstructionAt(i); + if (((/* begin literalSize */ + assert(((litInst->opcode)) == Literal), + (((litInst->operands))[1] + ? (((usqInt)((((litInst->operands))[1]))) >> 1) & 15 + : BytesPerOop))) == 4) { + cascade1 = genoperand(Literal, ((litInst->operands))[0]); + (cascade1->dependent = litInst); + setLiteralSize(cascade1, 4); + + /* begin setLiteralOpcodeIndex: */ + assert(((litInst->opcode)) == Literal); + ((litInst->operands))[2] = opcodeIndex; + } + } + if (generateBranchAround) { + jmpTarget(jump, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } + firstOpcodeIndex = opcodeIndex; + lastDumpedLiteralIndex = nextLiteralIndex; + return 0; +} + + +/* Search for a Literal instruction that is in-range and answer it. Otherwise + allocate a new sharable Literal instruction for the literal and answer it. */ + + /* OutOfLineLiteralsManagerFor64Bits>>#locateLiteral:size: */ +static NoDbgRegParms AbstractInstruction * +locateLiteralsize(sqInt aLiteral, sqInt litSize) +{ + AbstractInstruction *existingInst; + sqInt i; + sqInt initialNumLiterals; + sqInt iSqInt; + AbstractInstruction *litInst; + AbstractInstruction *newInst; + AbstractInstruction *newLiterals; + + for (i = 0; i < nextLiteralIndex; i += 1) { + litInst = literalInstructionAt(i); + if ((((/* begin literalSize */ + assert(((litInst->opcode)) == Literal), + (((litInst->operands))[1] + ? (((usqInt)((((litInst->operands))[1]))) >> 1) & 15 + : BytesPerOop))) == litSize) + && (((((litInst->operands))[0]) == aLiteral) + && (((/* begin isSharable */ + assert(((litInst->opcode)) == Literal), + (((((litInst->operands))[1]) & 1) != 0))) + && (literalInstructionInRange(litInst))))) { + return litInst; + } + } + if (nextLiteralIndex >= literalsSize) { + initialNumLiterals = literalsSize + 8; + + /* begin allocateLiterals: */ + if (initialNumLiterals > literalsSize) { + newLiterals = calloc(initialNumLiterals, sizeof(CogAbstractInstruction)); + + /* Must copy across state (not using realloc, cuz...) and + must also update existing instructions to refer to the new ones... + It's either this or modify all generation routines to be able to retry + with more literals after running out of literals. */ + if (literals) { + for (iSqInt = 0; iSqInt < nextLiteralIndex; iSqInt += 1) { + existingInst = literalInstructionAt(iSqInt); + newInst = (&(newLiterals[iSqInt])); + cloneLiteralFrom(newInst, existingInst); + assert(!((existingInst->dependent))); + (existingInst->dependent = newInst); + } + for (iSqInt = 0; iSqInt < opcodeIndex; iSqInt += 1) { + existingInst = abstractInstructionAt(iSqInt); + if ((((existingInst->dependent))) + && (((((existingInst->dependent))->opcode)) == Literal)) { + (existingInst->dependent = (((existingInst->dependent))->dependent)); + } + } + } + free(literals); + literals = newLiterals; + literalsSize = initialNumLiterals; + } + } + litInst = literalInstructionAt(nextLiteralIndex); + initializeSharableLiteral(litInst, aLiteral); + setLiteralSize(litInst, litSize); + nextLiteralIndex += 1; + + /* Record the opcodeIndex of the first dependent instruction (the first instruction that references an out-of-line literal) */ + if (firstOpcodeIndex > opcodeIndex) { + firstOpcodeIndex = opcodeIndex - 1; + } + return litInst; +} + + +/* Answer the Cogit's ABI as a string, if it adheres to a specific one. + Otherwise answer nil + */ + + /* SimpleStackBasedCogit>>#abiName */ +#if VMInvestigations +char * +abiName(void) +{ + return +# if defined(ABI) + ABI +# else + null +# endif + ; +} +#endif /* VMInvestigations */ + + /* SimpleStackBasedCogit>>#cogMethodHasExternalPrim: */ +sqInt +cogMethodHasExternalPrim(CogMethod *aCogMethod) +{ + sqInt primIndex; + + primIndex = primitiveIndexOfMethodheader((aCogMethod->methodObject), (aCogMethod->methodHeader)); + return (primIndex == PrimNumberExternalCall) + || (primIndex == PrimNumberFFICall); +} + + /* SimpleStackBasedCogit>>#cogMethodHasMachineCodePrim: */ +sqInt +cogMethodHasMachineCodePrim(CogMethod *aCogMethod) +{ + sqInt primIndex; + + primIndex = primitiveIndexOfMethodheader((aCogMethod->methodObject), (aCogMethod->methodHeader)); + return (((primIndex >= 1) && (primIndex <= MaxCompiledPrimitiveIndex))) + && ((((primitiveGeneratorTable[primIndex]).primitiveGenerator))); +} + + +/* Populate the last element of the three element array data with a sequence + of relative pc/value/object triples for the object & code references + embedded in the CogMethod. + If detailCPICs is true, include the list of targets as an array for sites + linked to closed PICs. + */ + + /* SimpleStackBasedCogit>>#cogMethodImports:into:detailCPICs: */ +#if VMInvestigations +sqInt +cogMethodImportsintodetailCPICs(CogMethod *cogMethod, sqInt data, sqInt detailCPICs) +{ + char *abort; + sqInt annotation; + sqInt importCount; + sqInt importsArray; + usqInt importsPtr[3]; + sqInt map; + sqInt mapByte; + usqInt mcpc; + void (*pfp)(void); + sqInt result; + + importCount = 0; + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = countCogMethodDataImportspccount(annotation, ((char *) mcpc), (&importCount)); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1: + importCount = (importCount + 2) + 1; + + /* for methodObject & selector + for the abort */ + if ((((primitiveIndex = primitiveIndexOf((cogMethod->methodObject)))) > 0) + && ((!(isQuickPrimitiveIndex(primitiveIndex))) + && (((pfp = functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto((cogMethod->methodObject), primitiveIndex, null))) != 0))) { + importCount += 1; + } + else { + primitiveIndex = 0; + } + if ((importsArray = instantiateClassindexableSize(classArray(), importCount * 3))) { + abort = ((char *)(callTargetFromReturnAddress(backEnd, (((usqInt)cogMethod)) + missOffset))); + assert((codeEntryFor(abort)) == abort); + storePointerUncheckedofObjectwithValue(2, data, importsArray); + storePointerUncheckedofObjectwithValue(0, importsArray, (((usqInt)(offsetof(CogMethod, methodObject)) << 3) | 1)); + storePointerUncheckedofObjectwithValue(1, importsArray, positive64BitIntegerFor((cogMethod->methodObject))); + storePointerUncheckedofObjectwithValue(2, importsArray, (cogMethod->methodObject)); + storePointerUncheckedofObjectwithValue(3, importsArray, (((usqInt)(offsetof(CogMethod, selector)) << 3) | 1)); + storePointerUncheckedofObjectwithValue(4, importsArray, positive64BitIntegerFor((cogMethod->selector))); + storePointerUncheckedofObjectwithValue(5, importsArray, (cogMethod->selector)); + storePointerUncheckedofObjectwithValue(6, importsArray, (((usqInt)missOffset << 3) | 1)); + storePointerUncheckedofObjectwithValue(7, importsArray, positive64BitIntegerFor(callTargetFromReturnAddress(backEnd, (((usqInt)cogMethod)) + missOffset))); + storePointerUncheckedofObjectwithValue(8, importsArray, stringForCString(codeEntryNameFor(abort))); + + /* the first and second entries are the methodObject and selector in the CogMethod header + the third entry in imports is the abort call */ + if (primitiveIndex > 0) { + storePointerUncheckedofObjectwithValue(9, importsArray, (((usqInt)-1 << 3) | 1)); + storePointerUncheckedofObjectwithValue(10, importsArray, positive64BitIntegerFor(((usqInt)pfp))); + storePointerUncheckedofObjectwithValue(11, importsArray, stringForCString("primitiveFunction")); + importsPtr[0] = (((usqInt) ((((usqInt *) (firstIndexableField(importsArray)))) + 12))); + } + else { + importsPtr[0] = (((usqInt) ((((usqInt *) (firstIndexableField(importsArray)))) + 9))); + } + + /* pass both the next place to store in imports (importsPtr at: 0) and the imports array (importsPtr at: 1) to the store enumerator */ + importsPtr[1] = importsArray; + importsPtr[2] = detailCPICs; + enumeratingCogMethod = cogMethod; + + /* N.B. answers error code from storeCogMethodDataImports:pc:pointer: if any */ + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = storeCogMethodDataImportspcpointer(annotation, ((char *) mcpc), (&importsPtr)); + if (result) { + return result; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + return 0; + } + else { + return PrimErrNoMemory; + } +} +#endif /* VMInvestigations */ + + +/* Compile the jump instruction(s) at the end of the method that dispatch to + each block body. + */ + + /* SimpleStackBasedCogit>>#compileBlockDispatch */ +static sqInt +compileBlockDispatch(void) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpSkip; + + assert(blockCount > 0); + + /* Generate the special block entry for primitiveClosureValueNoContextSwitch + that signals a context-switch is not possible by zeroing SendNumArgsReg. + Even though conceptually this isn't needed if no blocks build a frame it has + to be present so that primitiveClosureValueNoContextSwitch can call it. */ + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, 0, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + blockEntryNoContextSwitch = anInstruction; + + /* Now generate a binary search through start pcs to jump to blocks. 1 block is a special case. */ + jumpSkip = genoperand(Jump, ((sqInt)0)); + + /* Set OK to context switch flag to non-zero. */ + blockEntryLabel = genoperandoperand(MoveRR, ReceiverResultReg, SendNumArgsReg); + jmpTarget(jumpSkip, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + if (blockCount > 1) { + genLoadSlotsourceRegdestReg(ClosureStartPCIndex, ReceiverResultReg, TempReg); + } + compileBlockDispatchFromto(0, blockCount - 1); + return 0; +} + + +/* After pushing the temporaries but before the stack limit check a primitive + method needs to fetch the error code, if any. If the primitive has failed, + call the trampoline + that will assign it to the last temp. */ + + /* SimpleStackBasedCogit>>#compileGetErrorCode */ +static void +compileGetErrorCode(void) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + AbstractInstruction *jmpNoError; + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(primFailCodeAddress(), genoperandoperand(MoveAwR, primFailCodeAddress(), TempReg)); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jmpNoError = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + addDependent(methodLabel, annotateAbsolutePCRef(checkLiteralforInstruction(((sqInt)methodLabel), genoperandoperand(MoveCwR, ((sqInt)methodLabel), ClassReg)))); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceReapAndResetErrorCodeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpTarget(jmpNoError, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); +} + + /* SimpleStackBasedCogit>>#compileInterpreterPrimitive */ +static sqInt +compileInterpreterPrimitive(void) +{ + sqInt flags; + void (*primitiveRoutine)(void); + + flags = 0; + primitiveRoutine = functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(methodObj, primitiveIndex, (&flags)); + assert((((flags & PrimCallCollectsProfileSamples) != 0) + ? ((flags & PrimCallNeedsNewMethod) != 0) + : 1)); + if (((flags & PrimCallOnSmalltalkStack) != 0)) { + return compileOnStackExternalPrimitiveflags(primitiveRoutine, flags); + } + return compileInterpreterPrimitiveflags(primitiveRoutine, flags); +} + + +/* Compile a call to an interpreter primitive. Call the C routine with the + usual stack-switching dance, test the primFailCode and then either + return on success or continue to the method body. */ + + /* SimpleStackBasedCogit>>#compileInterpreterPrimitive:flags: */ +static NoDbgRegParms sqInt +compileInterpreterPrimitiveflags(void (*primitiveRoutine)(void), sqInt flags) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *continueAfterRetry; + AbstractInstruction *jmpOnFailure; + AbstractInstruction *jmpRetryOrNil; + sqInt offset; + sqInt record; + + assert(!((registerisInMask(VarBaseReg, ABICallerSavedRegisterMask)))); + + /* Save processor fp, sp and return pc in the interpreter's frame stack and instruction pointers */ + genExternalizePointersForPrimitiveCall(); + + /* Switch to the C stack. */ + + /* begin genLoadCStackPointersForPrimCall */ + if (cFramePointerInUse) { + genLoadCStackPointers(backEnd); + } + else { + genLoadCStackPointer(backEnd); + } + + /* Old old full prim trace is in VMMaker-eem.550 and prior. + Old simpler full prim trace is in VMMaker-eem.2969 and prior. */ + if ((record = recordPrimTraceForMethod(methodObj))) { + genFastPrimTraceUsingandsetNewMethod(ClassReg, SendNumArgsReg, ((flags & PrimCallNeedsNewMethod) != 0)); + } + + /* Clear the primFailCode and set argumentCount */ + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, 0, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(primFailCodeAddress(), genoperandoperand(MoveRAw, TempReg, primFailCodeAddress())); + if (methodOrBlockNumArgs) { + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, methodOrBlockNumArgs, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(methodOrBlockNumArgs, BytesPerOop)); + } + } + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(argumentCountAddress(), genoperandoperand(MoveRAw, TempReg, argumentCountAddress())); + + /* If required, set newMethod */ + if ((!record) + && (((flags & PrimCallNeedsNewMethod) != 0))) { + genLoadNewMethod(); + } + + /* Invoke the primitive. If the primitive (potentially) contains a call-back then its code + may disappear and consequently we cannot return here, since here may evaporate. + Instead sideways-call the routine, substituting cePrimReturnEnterCogCode[Profiling] + as the return address, so the call always returns there. + To save complications also use this path when profiling, since profiling slows down + execution by much more than this sideways call and it means we have but one + version of the profiling sample code to worry about. */ + + /* #PrefetchAw: #gen:literal: */ + checkLiteralforInstruction(primFailCodeAddress(), genoperand(PrefetchAw, primFailCodeAddress())); + if (((flags & (PrimCallMayEndureCodeCompaction + PrimCallCollectsProfileSamples)) != 0)) { + /* The ceActivateFailingPrimitiveMethod: machinery can't handle framelessness. */ + needsFrame = 1; + genMarshallNArgsargargargarg(backEnd, 0, null, null, null, null); + + /* #genSubstituteReturnAddress: #MoveCw:R: #gen:literal:operand: */ + checkLiteralforInstruction((((flags & PrimCallCollectsProfileSamples) != 0) + ? cePrimReturnEnterCogCodeProfiling + : cePrimReturnEnterCogCode), genoperandoperand(MoveCwR, (((flags & PrimCallCollectsProfileSamples) != 0) + ? cePrimReturnEnterCogCodeProfiling + : cePrimReturnEnterCogCode), LR)); + + /* #JumpFullRT: #JumpFull: #gen:literal: */ + checkLiteralforInstruction(((sqInt)(((usqInt)primitiveRoutine))), genoperand(JumpFull, ((sqInt)(((usqInt)primitiveRoutine))))); + return 0; + } + + /* Call the C primitive routine. */ + genMarshallNArgsargargargarg(backEnd, 0, null, null, null, null); + + /* #CallFullRT: #CallFull: #gen:literal: */ + checkLiteralforInstruction(((usqInt)primitiveRoutine), genoperand(CallFull, ((usqInt)primitiveRoutine))); + + /* genRemoveNArgsFromStack: */ + jmpRetryOrNil = compileJumpToRetryOfonPrimitiveFail(primitiveRoutine, primitiveIndex); + + /* placing the test here attributes the tick to the primitive plus any checkForAndFollowForwardedPrimitiveState + scanning, but attributes all of a failing primitive to the current method (in ceStackOverflow: on frame build). */ + continueAfterRetry = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* Switch back to the Smalltalk stack. Stack better be in either of these two states: + success: stackPointer -> result (was receiver) + arg1 + ... + argN + return pc + failure: receiver + arg1 + ... + stackPointer -> argN + return pc */ + genLoadStackPointersForPrimCall(backEnd, ClassReg); + + /* genLoadStackPointersForPrimCall: leaves the stack in these states: + NoLinkRegister LinkRegister + success: result (was receiver) stackPointer -> result (was receiver) + stackPointer -> arg1 arg1 + ... ... + argN argN + return pc + failure: receiver receiver + arg1 arg1 + ... ... + argN stackPointer -> argN + stackPointer -> return pc + which corresponds to the stack on entry after pushRegisterArgs. + In either case we can write the instructionPointer to top of stack or load it into the LinkRegister to reestablish the return pc. */ + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(instructionPointerAddress(), genoperandoperand(MoveAwR, instructionPointerAddress(), LinkReg)); + + /* Test primitive failure */ + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(primFailCodeAddress(), genoperandoperand(MoveAwR, primFailCodeAddress(), TempReg)); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jmpOnFailure = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Fetch result from stack */ + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, 0, SPReg, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* RetN: */ + genoperand(RetN, BytesPerWord); + maybeCompileRetryOfonPrimitiveFailflagsjumpcontinue(primitiveRoutine, primitiveIndex, flags, jmpRetryOrNil, continueAfterRetry); + + /* Jump to restore of receiver reg and proceed to frame build for failure. */ + jmpTarget(jmpOnFailure, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* Restore receiver reg from stack. If on RISCs ret pc is in LinkReg, if on CISCs ret pc is on stack. */ + offset = BytesPerWord * (methodOrBlockNumArgs); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, offset, SPReg, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + return 0; +} + + +/* Compile a fast call of a C primitive using the current stack page, + avoiding the stack switch except on failure. + This convention still uses stackPointer and argumentCount to access + operands. Push all operands to the stack, + assign stackPointer, argumentCount, and zero primFailCode. Make the call + (saving a LinkReg if required). + Test for failure and return. On failure, if there is an accessor depth, + assign framePointer and newMethod, + do the stack switch, call checkForAndFollowForwardedPrimitiveState, and + loop back if forwarders are found. + Fall through to frame build. + + Things get messy when profiling. Because of the potential retry, and the + need to take the profile sample after a + retry if it occurs, two separate samplings must be done, one immediately + before returning on success (potentially + after retry), and one immediately before falling through to failure. + However, on failure there is no need to take + a sample immediately. Provided the stack limit is smashed then a sample + will be taken on frame build. */ + + /* SimpleStackBasedCogit>>#compileOnStackExternalPrimitive:flags: */ +static NoDbgRegParms sqInt +compileOnStackExternalPrimitiveflags(void (*primitiveRoutine)(void), sqInt flags) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + sqInt calleeSavedRegisterMask; + sqInt callTargetSqInt; + AbstractInstruction *continueAfterProfileSampleOnSuccess; + AbstractInstruction *jmpRetryOrFail; + AbstractInstruction *jumpToTakeSampleOnSuccess; + sqInt linkRegSaveRegister; + sqInt offset; + sqInt reg; + AbstractInstruction *retry; + AbstractInstruction *skip; + sqInt spRegSaveRegister; + + jumpToTakeSampleOnSuccess = ((AbstractInstruction *) 0); + assert(((flags & PrimCallOnSmalltalkStack) != 0)); + assert(!((registerisInMask(VarBaseReg, ABICallerSavedRegisterMask)))); + if (recordFastCCallPrimTraceForMethod(methodObj)) { + genFastPrimTraceUsingandsetNewMethod(ClassReg, SendNumArgsReg, 0); + } + + /* Clear the primFailCode and set argumentCount */ + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, 0, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(primFailCodeAddress(), genoperandoperand(MoveRAw, TempReg, primFailCodeAddress())); + if (methodOrBlockNumArgs) { + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, methodOrBlockNumArgs, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(methodOrBlockNumArgs, BytesPerOop)); + } + } + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(argumentCountAddress(), genoperandoperand(MoveRAw, TempReg, argumentCountAddress())); + genExternalizeStackPointerForFastPrimitiveCall(); + + /* If profiling also write the framePointer and instructionPointer so that + ceTakeProfileSample: can activate the profiling process immediately. */ + if (((flags & PrimCallCollectsProfileSamples) != 0)) { + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(framePointerAddress(), genoperandoperand(MoveRAw, FPReg, framePointerAddress())); + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(instructionPointerAddress(), genoperandoperand(MoveRAw, LinkReg, instructionPointerAddress())); + } + + /* We may need to save LinkReg and/or SPReg, and given the stack machinations + it is much easier to save them in callee saved registers than on the stack itself. */ + calleeSavedRegisterMask = ((ABICalleeSavedRegisterMask | ((1U << ClassReg))) - ((1U << ClassReg))); + linkRegSaveRegister = availableRegisterOrNoneIn(calleeSavedRegisterMask); + assert(!(((linkRegSaveRegister == NoReg) + || (linkRegSaveRegister == TempReg)))); + + /* MoveR:R: */ + genoperandoperand(MoveRR, LinkReg, linkRegSaveRegister); + calleeSavedRegisterMask = ((calleeSavedRegisterMask | (((linkRegSaveRegister < 0) ? (((usqInt)(1)) >> (-linkRegSaveRegister)) : (1ULL << linkRegSaveRegister)))) - (((linkRegSaveRegister < 0) ? (((usqInt)(1)) >> (-linkRegSaveRegister)) : (1ULL << linkRegSaveRegister)))); + spRegSaveRegister = NoReg; + if (!(((ABICalleeSavedRegisterMask & ((1U << SPReg))) != 0))) { + spRegSaveRegister = availableRegisterOrNoneIn(calleeSavedRegisterMask); + assert(!((spRegSaveRegister == NoReg))); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SPReg, spRegSaveRegister); + } + retry = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + if (((flags & PrimCallOnSmalltalkStackAlign2x) != 0)) { + gAndCqRR((-1 - (((BytesPerWord * 2) - 1))), SPReg, NativeSPReg); + } + else { + genLoadNativeSPRegWithAlignedSPReg(backEnd); + } + genMarshallNArgsargargargarg(backEnd, 0, null, null, null, null); + + /* If the primitive is in the interpreter then its address won't change relative to the code zone over time, + whereas if it is in a plugin its address could change if the module is un/re/over/loaded. + So if in the interpreter and in range use a normal call instruction. */ + if (((!(flags & PrimCallIsExternalCall))) + && (isInImmediateBranchAndLinkRange(backEnd, maximumDistanceFromCodeZone(((usqInt)primitiveRoutine))))) { + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ((usqInt)primitiveRoutine)); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* #CallFullRT: #CallFull: #gen:literal: */ + checkLiteralforInstruction(((usqInt)primitiveRoutine), genoperand(CallFull, ((usqInt)primitiveRoutine))); + } + + /* genRemoveNArgsFromStack: */ + + /* Remember to restore the native stack pointer to point to the C stack, + otherwise the Smalltalk frames will get overwritten on an interrupt. */ + genLoadCStackPointer(backEnd); + + /* test primFailCode and jump to failure sequence if non-zero */ + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(primFailCodeAddress(), genoperandoperand(MoveAwR, primFailCodeAddress(), TempReg)); + if (spRegSaveRegister != NoReg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, spRegSaveRegister, SPReg); + } + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jmpRetryOrFail = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* If profiling, check for a sample now. The alternative is to have two checks and two calls, + which would include the cost of checkForAndFollowForwardedPrimitiveState in the sample. + But that's excessive given that retry failures are very rare. */ + if (((flags & PrimCallCollectsProfileSamples) != 0)) { + /* begin genCheckForProfileTimerTick: */ + reg = Arg0Reg; + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(nextProfileTickAddress(), genoperandoperand(MoveAwR, nextProfileTickAddress(), Arg1Reg)); + gMovePerfCnt64RL(reg, (0)); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, reg); + jumpToTakeSampleOnSuccess = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + } + + /* At this point the primitive has cut back stackPointer to point to the result. */ + continueAfterProfileSampleOnSuccess = checkLiteralforInstruction(stackPointerAddress(), genoperandoperand(MoveAwR, stackPointerAddress(), TempReg)); + + /* get result and restore retpc */ + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, 0, TempReg, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + gAddCqRR(BytesPerWord, TempReg, SPReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, linkRegSaveRegister, LinkReg); + + /* RetN: */ + genoperand(RetN, 0); + if (((flags & PrimCallCollectsProfileSamples) != 0)) { + jmpTarget(jumpToTakeSampleOnSuccess, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genLoadNewMethod(); + genTakeProfileSample(continueAfterProfileSampleOnSuccess); + } + + /* primitive failure. if there is an accessor depth, scan and retry on failure (but what if failing for out of memory?). + Retry by jumping back to the retry label. */ + jmpTarget(jmpRetryOrFail, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + if ((accessorDepthForPrimitiveMethod(methodObj)) >= 0) { + /* Given that following primitive state to the accessor depth is recursive, we're asking for + trouble if we run the fixup on the Smalltalk stack page. Run it on the full C stack instead. + This won't be a performance issue since primitive failure should be very rare. + Note that this cannot loop on failure because after the first retry the forwarders will have + been followed and so checkForAndFollowForwardedPrimitiveState will answer false on a + second failure. */ + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(framePointerAddress(), genoperandoperand(MoveRAw, FPReg, framePointerAddress())); + + /* #MoveCw:R: #gen:literal:operand: */ + checkLiteralforInstruction(((usqInt)primitiveRoutine), genoperandoperand(MoveCwR, ((usqInt)primitiveRoutine), TempReg)); + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(primitiveFunctionPointerAddress(), genoperandoperand(MoveRAw, TempReg, primitiveFunctionPointerAddress())); + genLoadNewMethod(); + + /* begin genLoadCStackPointersForPrimCall */ + if (cFramePointerInUse) { + genLoadCStackPointers(backEnd); + } + else { + genLoadCStackPointer(backEnd); + } + genMarshallNArgsargargargarg(backEnd, 0, 0, 0, 0, 0); + + /* begin CallAnyFixedRT: */ + if (isInImmediateBranchAndLinkRange(backEnd, maximumDistanceFromCodeZone(((usqInt)checkForAndFollowForwardedPrimitiveState)))) { + callTargetSqInt = ((usqIntptr_t)checkForAndFollowForwardedPrimitiveState); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, callTargetSqInt); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* #CallFullRT: #CallFull: #gen:literal: */ + checkLiteralforInstruction(((usqIntptr_t)checkForAndFollowForwardedPrimitiveState), genoperand(CallFull, ((usqIntptr_t)checkForAndFollowForwardedPrimitiveState))); + } + genLoadStackPointersForPrimCall(backEnd, ClassReg); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, ABIResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + skip = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, 0, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(primFailCodeAddress(), genoperandoperand(MoveRAw, TempReg, primFailCodeAddress())); + + /* Jump: */ + genoperand(Jump, ((sqInt)retry)); + jmpTarget(skip, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } + else { + if (((flags & PrimCallOnSmalltalkStackAlign2x) != 0)) { + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(stackPointerAddress(), genoperandoperand(MoveAwR, stackPointerAddress(), SPReg)); + } + } + + /* must reload SPReg to undo any alignment change, + Remember to restore the native stack pointer to point to the C stack, + otherwise the Smalltalk frames will get overwritten on an interrupt. */ + genLoadCStackPointer(backEnd); + + /* If profiling, check for a sample now, in this case for failure. Avoid taking the sample. If the sample should + be taken, smash the stack limit and allow the subsequent post frame build stack check to do the sampling. */ + if (((flags & PrimCallCollectsProfileSamples) != 0)) { + /* begin genCheckForProfileTimerTick: */ + reg = Arg0Reg; + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(nextProfileTickAddress(), genoperandoperand(MoveAwR, nextProfileTickAddress(), Arg1Reg)); + gMovePerfCnt64RL(reg, (0)); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, reg); + skip = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* Invert the branch to jump if a sample should /not/ be taken. */ + (skip->opcode = inverseBranchFor((skip->opcode))); + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, -1, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(-1, BytesPerOop)); + } + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(stackLimitAddress(), genoperandoperand(MoveRAw, TempReg, stackLimitAddress())); + jmpTarget(skip, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } + + /* The LinkRegister now contains the return address either of the primitive call or of checkForAndFollowForwardedPrimitiveState. + It must be restored to the return address of the send invoking this primitive method. */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, linkRegSaveRegister, LinkReg); + + /* Finally remember to reload ReceiverResultReg if required. Even if + arguments have been pushed, the prolog sequence assumes it is live. */ + if (((ABICallerSavedRegisterMask & ((1U << ReceiverResultReg))) != 0)) { + offset = (methodOrBlockNumArgs) * BytesPerWord; + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, offset, SPReg, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + } + + /* continue to frame build... */ + return 0; +} + + +/* Compile one method cache probe in an OpenPIC's lookup of selector. + Answer the jump taken if the selector probe fails. + The class tag of the receiver must be in SendNumArgsReg. ClassReg and + TempReg are used as scratch registers. + On a hit, the offset of the entry is in ClassReg. */ + + /* SimpleStackBasedCogit>>#compileOpenPICMethodCacheProbeFor:withShift:baseRegOrNone: */ +static NoDbgRegParms AbstractInstruction * +compileOpenPICMethodCacheProbeForwithShiftbaseRegOrNone(sqInt selector, sqInt shift, sqInt baseRegOrNone) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpSelectorMiss; + sqInt offset; + + /* MoveR:R: */ + genoperandoperand(MoveRR, SendNumArgsReg, ClassReg); + maybeShiftClassTagRegisterForMethodCacheProbe(ClassReg); + annotateobjRef(checkLiteralforInstruction(selector, genoperandoperand(XorCwR, selector, ClassReg)), selector); + assert(shift <= (shiftForWord())); + + /* Need to shift the hash right by shift to form the probe, and then shift the probe left by shiftForWord to form the index. + So shift left by shiftForWord - shift and and with the shifted mask. */ + if (shift < (shiftForWord())) { + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - shift, ClassReg); + } + anInstruction = genoperandoperand(AndCqR, ((sqInt)((usqInt)(MethodCacheMask) << (shiftForWord()))), ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(((sqInt)((usqInt)(MethodCacheMask) << (shiftForWord()))), BytesPerOop)); + } + if (baseRegOrNone == NoReg) { + offset = (methodCacheAddress()) + ((((usqInt)(MethodCacheSelector) << (shiftForWord())))); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, offset, ClassReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + } + else { + /* AddR:R: */ + genoperandoperand(AddRR, baseRegOrNone, ClassReg); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, ((sqInt)((usqInt)(MethodCacheSelector) << (shiftForWord()))), ClassReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(((sqInt)((usqInt)(MethodCacheSelector) << (shiftForWord()))), BytesPerOop)); + } + } + annotateobjRef(checkLiteralforInstruction(selector, genoperandoperand(CmpCwR, selector, TempReg)), selector); + jumpSelectorMiss = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + if (baseRegOrNone == NoReg) { + offset = (methodCacheAddress()) + ((((usqInt)(MethodCacheClass) << (shiftForWord())))); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, offset, ClassReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + } + else { + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, ((sqInt)((usqInt)(MethodCacheClass) << (shiftForWord()))), ClassReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(((sqInt)((usqInt)(MethodCacheClass) << (shiftForWord()))), BytesPerOop)); + } + } + + /* begin CmpR:R: */ + assert(!(0 /* (SendNumArgsReg = SPReg) */)); + genoperandoperand(CmpRR, SendNumArgsReg, TempReg); + return jumpSelectorMiss; +} + + +/* Compile the code for an open PIC. Perform a probe of the first-level + method lookup cache followed by a call of ceSendFromInLineCacheMiss: if + the probe fails. */ + + /* SimpleStackBasedCogit>>#compileOpenPIC:numArgs: */ +static NoDbgRegParms void +compileOpenPICnumArgs(sqInt selector, sqInt numArgs) +{ + AbstractInstruction *anInstruction; + sqInt cacheBaseReg; + AbstractInstruction *itsAHit; + AbstractInstruction *jumpBCMethod; + AbstractInstruction *jumpClassMiss; + AbstractInstruction *jumpSelectorMiss; + sqInt offset; + + /* begin preenMethodLabel */ + /* setLabelOffset: */ + ((methodLabel->operands))[1] = 0; + compilePICAbort(numArgs); + entry = genGetClassTagOfintoscratchReg(ReceiverResultReg, SendNumArgsReg, TempReg); + cacheBaseReg = NoReg; + + /* Do first of three probes. See CoInterpreter>>lookupInMethodCacheSel:classTag: */ + jumpSelectorMiss = compileOpenPICMethodCacheProbeForwithShiftbaseRegOrNone(selector, 0, cacheBaseReg); + jumpClassMiss = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Fetch the method. The interpret trampoline requires the bytecoded method in SendNumArgsReg */ + offset = (cacheBaseReg == NoReg + ? (methodCacheAddress()) + ((((usqInt)(MethodCacheMethod) << (shiftForWord())))) + : ((sqInt)((usqInt)(MethodCacheMethod) << (shiftForWord())))); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + itsAHit = genoperandoperandoperand(MoveMwrR, offset, ClassReg, SendNumArgsReg); + if (usesOutOfLineLiteral(itsAHit)) { + (itsAHit->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* If the method is compiled jump to its unchecked entry-point, otherwise interpret it. */ + genLoadSlotsourceRegdestReg(HeaderIndex, SendNumArgsReg, ClassReg); + jumpBCMethod = genJumpImmediate(ClassReg); + jmpTarget(jumpBCMethod, picInterpretAbort); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, cmNoCheckEntryOffset, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(cmNoCheckEntryOffset, BytesPerOop)); + } + + /* JumpR: */ + genoperand(JumpR, ClassReg); + + /* First probe missed. Do second of three probes. Shift hash right one and retry. */ + jmpTarget(jumpSelectorMiss, jmpTarget(jumpClassMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + jumpSelectorMiss = compileOpenPICMethodCacheProbeForwithShiftbaseRegOrNone(selector, 1, cacheBaseReg); + + /* JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)itsAHit)); + + /* Second probe missed. Do last probe. Shift hash right two and retry. */ + jmpTarget(jumpSelectorMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpSelectorMiss = compileOpenPICMethodCacheProbeForwithShiftbaseRegOrNone(selector, 2, cacheBaseReg); + + /* JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)itsAHit)); + + /* Last probe missed. Call ceSendFromInLineCacheMiss: to do the full lookup. */ + jmpTarget(jumpSelectorMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + if ((numRegArgs()) > 0) { + genPushRegisterArgsForNumArgsscratchReg(backEnd, numArgs, SendNumArgsReg); + } + genSmalltalkToCStackSwitch(1); + addDependent(methodLabel, annotateAbsolutePCRef(checkLiteralforInstruction(((sqInt)methodLabel), genoperandoperand(MoveCwR, ((sqInt)methodLabel), SendNumArgsReg)))); + compileCallFornumArgsargargargargresultRegregsToSave(ceSendFromInLineCacheMiss, 1, SendNumArgsReg, null, null, null, NoReg, 0 /* emptyRegisterMask */); +} + + +/* Compile one method cache probe in a perform: primitive's lookup of + selector. Answer the jump taken if the selector probe fails. */ + + /* SimpleStackBasedCogit>>#compilePerformMethodCacheProbeFor:withShift:baseRegOrNone: */ +static NoDbgRegParms AbstractInstruction * +compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(sqInt selectorReg, sqInt shift, sqInt baseRegOrNone) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpSelectorMiss; + sqInt offset; + + /* MoveR:R: */ + genoperandoperand(MoveRR, SendNumArgsReg, ClassReg); + maybeShiftClassTagRegisterForMethodCacheProbe(ClassReg); + + /* XorR:R: */ + genoperandoperand(XorRR, selectorReg, ClassReg); + assert(shift <= (shiftForWord())); + + /* Need to shift the hash right by shift to form the probe, and then shift the probe left by shiftForWord to form the index. + So shift left by shiftForWord - shift and and with the shifted mask. */ + if (shift < (shiftForWord())) { + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - shift, ClassReg); + } + anInstruction = genoperandoperand(AndCqR, ((sqInt)((usqInt)(MethodCacheMask) << (shiftForWord()))), ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(((sqInt)((usqInt)(MethodCacheMask) << (shiftForWord()))), BytesPerOop)); + } + if (baseRegOrNone == NoReg) { + offset = (methodCacheAddress()) + ((((usqInt)(MethodCacheSelector) << (shiftForWord())))); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, offset, ClassReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + } + else { + /* AddR:R: */ + genoperandoperand(AddRR, baseRegOrNone, ClassReg); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, ((sqInt)((usqInt)(MethodCacheSelector) << (shiftForWord()))), ClassReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(((sqInt)((usqInt)(MethodCacheSelector) << (shiftForWord()))), BytesPerOop)); + } + } + + /* begin CmpR:R: */ + assert(!((selectorReg == SPReg))); + genoperandoperand(CmpRR, selectorReg, TempReg); + jumpSelectorMiss = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + if (baseRegOrNone == NoReg) { + offset = (methodCacheAddress()) + ((((usqInt)(MethodCacheClass) << (shiftForWord())))); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, offset, ClassReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + } + else { + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, ((sqInt)((usqInt)(MethodCacheClass) << (shiftForWord()))), ClassReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(((sqInt)((usqInt)(MethodCacheClass) << (shiftForWord()))), BytesPerOop)); + } + } + + /* begin CmpR:R: */ + assert(!(0 /* (SendNumArgsReg = SPReg) */)); + genoperandoperand(CmpRR, SendNumArgsReg, TempReg); + return jumpSelectorMiss; +} + + +/* Compile a primitive. If possible, performance-critical primitives will + be generated by their own routines (primitiveGenerator). Otherwise, + if there is a primitive at all, we call the C routine with the usual + stack-switching dance, test the primFailCode and then either return + on success or continue to the method body. */ + + /* SimpleStackBasedCogit>>#compilePrimitive */ +static sqInt +compilePrimitive(void) +{ + AbstractInstruction *anInstruction; + sqInt code; + sqInt flags; + AbstractInstruction *jumpNotSmallInteger; + sqInt opcodeIndexAtPrimitive; + PrimitiveDescriptor *primitiveDescriptor; + void (*primitiveRoutine)(void); + sqInt reg; + + flags = 0; + primitiveDescriptor = ((PrimitiveDescriptor *) 0); + if (!primitiveIndex) { + return 0; + } + + /* If a descriptor specifies an argument count (by numArgs >= 0) then it must match + for the generated code to be correct. For example for speed many primitives use + ResultReceiverReg instead of accessing the stack, so the receiver better be at + numArgs down the stack. Use the interpreter version if not. */ + if ((((primitiveDescriptor = primitiveGeneratorOrNil()))) + && ((((primitiveDescriptor->primitiveGenerator))) + && ((((primitiveDescriptor->primNumArgs)) < 0) + || (((primitiveDescriptor->primNumArgs)) == methodOrBlockNumArgs)))) { + /* Note opcodeIndex so that any arg load instructions + for unimplemented primitives can be discarded. */ + opcodeIndexAtPrimitive = opcodeIndex; + code = ((primitiveDescriptor->primitiveGenerator))(); + if ((code < 0) + && (code != UnimplementedPrimitive)) { + return code; + } + + /* Generator failed, so no point continuing... + If the primitive can never fail then there is nothing more that needs to be done. */ + if (code == UnfailingPrimitive) { + return 0; + } + + /* If the machine code version handles all cases the only reason to call the interpreter + primitive is to reap the primitive error code. Don't bother if it isn't used. */ + if ((code == CompletePrimitive) + && (!(/* methodUsesPrimitiveErrorCode:header: */ + ((primitiveIndexOfMethodheader(methodObj, methodHeader)) > 0) + && ((longStoreBytecodeForHeader(methodHeader)) == (fetchByteofObject((startPCOfMethodHeader(methodHeader)) + (sizeOfCallPrimitiveBytecode(methodHeader)), methodObj)))))) { + return 0; + } + + /* Discard any arg load code generated by the primitive generator. */ + if (code == UnimplementedPrimitive) { + opcodeIndex = opcodeIndexAtPrimitive; + } + } + primitiveRoutine = functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(methodObj, primitiveIndex, (&flags)); + if ((primitiveRoutine == 0) + || (primitiveRoutine == (((void (*)(void)) primitiveFail)))) { + return genFastPrimFail(); + } + + /* no primitive */ + if ((primitiveRoutine == (((void (*)(void)) primitiveHighResClock))) + && (methodOrBlockNumArgs == 0)) { + /* begin genPrimitiveHighResClock64 */ + reg = Arg0Reg; + assert(registerisNotInMask(reg, registerMaskForand(ReceiverResultReg, Arg1Reg))); + gMovePerfCnt64RL(reg, (0)); + gLogicalShiftRightCqRR((numSmallIntegerBits()) - 1, reg, Arg1Reg); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpNotSmallInteger = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genConvertIntegerInRegtoSmallIntegerInReg(reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSmallInteger, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genInstantiate64BitPositiveIntegerValueintoscratchRegmayFail(reg, ReceiverResultReg, Arg1Reg, 0); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + code = UnfailingPrimitive; + if (code != UnimplementedPrimitive) { + return code; + } + } + if (((flags & PrimCallOnSmalltalkStack) != 0)) { + return compileOnStackExternalPrimitiveflags(primitiveRoutine, flags); + } + return compileInterpreterPrimitiveflags(primitiveRoutine, flags); +} + + /* SimpleStackBasedCogit>>#extendedPushBytecode */ +static sqInt +extendedPushBytecode(void) +{ + usqInt variableIndex; + usqInt variableType; + + variableType = (((usqInt)(byte1)) >> 6) & 3; + variableIndex = byte1 & 0x3F; + if (!variableType) { + return genPushReceiverVariable(variableIndex); + } + if (variableType == 1) { + return genPushTemporaryVariable(variableIndex); + } + if (variableType == 2) { + return genPushLiteralIndex(variableIndex); + } + return genPushLiteralVariable(variableIndex); +} + + /* SimpleStackBasedCogit>>#extendedStoreAndPopBytecode */ +static sqInt +extendedStoreAndPopBytecode(void) +{ + AbstractInstruction *abstractInstruction; + usqInt variableIndex; + usqInt variableType; + + variableType = (((usqInt)(byte1)) >> 6) & 3; + variableIndex = byte1 & 0x3F; + if (!variableType) { + return genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(1, variableIndex, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } + if (variableType == 1) { + genStorePopTemporaryVariable(1, variableIndex); + + /* needs a fake map entry if Immutability is ON... */ +# if IMMUTABILITY + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); +# endif // IMMUTABILITY + + return 0; + } + if (variableType == 3) { + return genStorePopLiteralVariableneedsStoreCheckneedsImmutabilityCheck(1, variableIndex, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } + return EncounteredUnknownBytecode; +} + + /* SimpleStackBasedCogit>>#extendedStoreBytecode */ +static sqInt +extendedStoreBytecode(void) +{ + AbstractInstruction *abstractInstruction; + usqInt variableIndex; + usqInt variableType; + + variableType = (((usqInt)(byte1)) >> 6) & 3; + variableIndex = byte1 & 0x3F; + if (!variableType) { + return genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(0, variableIndex, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } + if (variableType == 1) { + genStorePopTemporaryVariable(0, variableIndex); + + /* needs a fake map entry if Immutability is ON... */ +# if IMMUTABILITY + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); +# endif // IMMUTABILITY + + return 0; + } + if (variableType == 3) { + return genStorePopLiteralVariableneedsStoreCheckneedsImmutabilityCheck(0, variableIndex, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } + return EncounteredUnknownBytecode; +} + + /* SimpleStackBasedCogit>>#frameOffsetOfTemporary: */ +static NoDbgRegParms sqInt +frameOffsetOfTemporary(sqInt index) +{ + return /* frameOffsetOfTemporary:numArgs: */ + (index < methodOrBlockNumArgs + ? FoxCallerSavedIP + ((methodOrBlockNumArgs - index) * BytesPerWord) + : (FoxMFReceiver - BytesPerWord) + ((methodOrBlockNumArgs - index) * BytesPerWord)); +} + + +/* Implemented with SistaCogit only */ + + /* SimpleStackBasedCogit>>#genCallMappedInlinedPrimitive */ +static sqInt +genCallMappedInlinedPrimitive(void) +{ + return EncounteredUnknownBytecode; +} + + /* SimpleStackBasedCogit>>#genDoubleFailIfZeroArgRcvr:arg: */ +static NoDbgRegParms AbstractInstruction * +genDoubleFailIfZeroArgRcvrarg(int rcvrReg, int argReg) +{ + AbstractInstruction *anInstruction; + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, 0, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* ConvertR:Rd: */ + genoperandoperand(ConvertRRd, TempReg, DPFPReg2); + + /* CmpRd:Rd: */ + genoperandoperand(CmpRdRd, DPFPReg2, argReg); + return gJumpFPEqual(0); +} + + +/* Can use any of the first 32 literals for the selector and pass up to 7 + arguments. + */ + + /* SimpleStackBasedCogit>>#genExtendedSendBytecode */ +static sqInt +genExtendedSendBytecode(void) +{ + return genSendnumArgs(byte1 & 0x1F, ((usqInt)(byte1)) >> 5); +} + + /* SimpleStackBasedCogit>>#genExtendedSuperBytecode */ +static sqInt +genExtendedSuperBytecode(void) +{ + return genSendSupernumArgs(byte1 & 0x1F, ((usqInt)(byte1)) >> 5); +} + + +/* 244 11110100 i i i i i i i i Pop and Jump 0n False i i i i i i i i (+ + Extend B * 256, where Extend B >= 0) + */ + + /* SimpleStackBasedCogit>>#genExtJumpIfFalse */ +static sqInt +genExtJumpIfFalse(void) +{ + sqInt distance; + sqInt target; + + distance = byte1 + ((((usqInt)(extB) << 8))); + assert(distance == (v4LongForwardBranchDistance(generatorAt(byte0), bytecodePC, ((extA != 0 + ? 1 + : 0)) + ((extB != 0 + ? 1 + : 0)), methodObj))); + extB = 0; + numExtB = 0; + target = (distance + 2) + bytecodePC; + return genJumpIfto(falseObject(), target); +} + + +/* 243 11110011 i i i i i i i i Pop and Jump 0n True i i i i i i i i (+ + Extend B * 256, where Extend B >= 0) + */ + + /* SimpleStackBasedCogit>>#genExtJumpIfTrue */ +static sqInt +genExtJumpIfTrue(void) +{ + sqInt distance; + sqInt target; + + distance = byte1 + ((((usqInt)(extB) << 8))); + assert(distance == (v4LongForwardBranchDistance(generatorAt(byte0), bytecodePC, ((extA != 0 + ? 1 + : 0)) + ((extB != 0 + ? 1 + : 0)), methodObj))); + extB = 0; + numExtB = 0; + target = (distance + 2) + bytecodePC; + return genJumpIfto(trueObject(), target); +} + + +/* NewspeakV4: 221 11011101 Nop */ +/* SistaV1: 91 01011011' Nop */ + + /* SimpleStackBasedCogit>>#genExtNopBytecode */ +static sqInt +genExtNopBytecode(void) +{ + extA = (numExtB = (extB = 0)); + return 0; +} + + +/* SistaV1: 233 11101001 iiiiiiii Push Character #iiiiiiii (+ Extend A * + 256) + */ + + /* SimpleStackBasedCogit>>#genExtPushCharacterBytecode */ +static sqInt +genExtPushCharacterBytecode(void) +{ + sqInt value; + + value = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + return ssPushConstant(characterObjectOf(value)); +} + + +/* NewsqueakV4: 229 11100101 iiiiiiii Push Integer #iiiiiiii (+ Extend B * + 256, where bbbbbbbb = sddddddd, e.g. -32768 = i=0, a=0, s=1) + SistaV1: 232 11101000 iiiiiiii Push Integer #iiiiiiii (+ Extend B * 256, + where bbbbbbbb = sddddddd, e.g. -32768 = i=0, a=0, s=1) + */ + + /* SimpleStackBasedCogit>>#genExtPushIntegerBytecode */ +static sqInt +genExtPushIntegerBytecode(void) +{ + sqInt value; + + value = byte1 + ((((usqInt)(extB) << 8))); + extB = 0; + numExtB = 0; + return ssPushConstant((((usqInt)value << 3) | 1)); +} + + +/* 228 11100100 i i i i i i i i Push Literal #iiiiiiii (+ Extend A * 256) */ + + /* SimpleStackBasedCogit>>#genExtPushLiteralBytecode */ +static sqInt +genExtPushLiteralBytecode(void) +{ + sqInt index; + + index = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + return genPushLiteralIndex(index); +} + + +/* 227 11100011 i i i i i i i i Push Literal Variable #iiiiiiii (+ Extend A + * 256) + */ + + /* SimpleStackBasedCogit>>#genExtPushLiteralVariableBytecode */ +static sqInt +genExtPushLiteralVariableBytecode(void) +{ + sqInt index; + + index = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + return genPushLiteralVariable(index); +} + + +/* SistaV1: * 82 01010010 Push thisContext, (then Extend B = 1 => push + thisProcess) + */ + + /* SimpleStackBasedCogit>>#genExtPushPseudoVariable */ +static sqInt +genExtPushPseudoVariable(void) +{ + sqInt ext; + + ext = extB; + extB = 0; + numExtB = 0; + switch (ext) { + case 0: + return genPushActiveContextBytecode(); + + default: + return EncounteredUnknownBytecode; + } + return 0; +} + + +/* 226 11100010 i i i i i i i i Push Receiver Variable #iiiiiiii (+ Extend A + * 256) + */ + + /* SimpleStackBasedCogit>>#genExtPushReceiverVariableBytecode */ +static sqInt +genExtPushReceiverVariableBytecode(void) +{ + sqInt index; + + index = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + return ((mclassCouldBeContext()) + && (isReadMediatedContextInstVarIndex(index)) + ? genPushMaybeContextReceiverVariable(index) + : genPushReceiverVariable(index)); +} + + +/* 238 11101110 i i i i i j j j Send Literal Selector #iiiii (+ Extend A * + 32) with jjj (+ Extend B * 8) Arguments + */ + + /* SimpleStackBasedCogit>>#genExtSendBytecode */ +static sqInt +genExtSendBytecode(void) +{ + sqInt litIndex; + usqInt nArgs; + + litIndex = (((usqInt)(byte1)) >> 3) + ((((usqInt)(extA) << 5))); + extA = 0; + nArgs = (byte1 & 7) + ((((usqInt)(extB) << 3))); + extB = 0; + numExtB = 0; + return genSendnumArgs(litIndex, nArgs); +} + + +/* 239 11101111 i i i i i j j j Send To Superclass Literal Selector #iiiii + (+ Extend A * 32) with jjj (+ Extend B * 8) Arguments + */ + + /* SimpleStackBasedCogit>>#genExtSendSuperBytecode */ +static sqInt +genExtSendSuperBytecode(void) +{ + int isDirected; + sqInt litIndex; + usqInt nArgs; + + if ((isDirected = extB >= 64)) { + extB = extB & 0x3F; + } + litIndex = (((usqInt)(byte1)) >> 3) + ((((usqInt)(extA) << 5))); + extA = 0; + nArgs = (byte1 & 7) + ((((usqInt)(extB) << 3))); + extB = 0; + numExtB = 0; + return (isDirected + ? genSendDirectedSupernumArgs(litIndex, nArgs) + : genSendSupernumArgs(litIndex, nArgs)); +} + + +/* 236 11101100 i i i i i i i i Pop and Store Literal Variable #iiiiiiii (+ + Extend A * 256) + */ + + /* SimpleStackBasedCogit>>#genExtStoreAndPopLiteralVariableBytecode */ +static sqInt +genExtStoreAndPopLiteralVariableBytecode(void) +{ + sqInt index; + + index = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + return genStorePopLiteralVariableneedsStoreCheckneedsImmutabilityCheck(1, index, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); +} + + +/* 235 11101011 i i i i i i i i Pop and Store Receiver Variable #iiiiiii (+ + Extend A * 256) + */ + + /* SimpleStackBasedCogit>>#genExtStoreAndPopReceiverVariableBytecode */ +static sqInt +genExtStoreAndPopReceiverVariableBytecode(void) +{ + sqInt index; + + index = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + if ((mclassCouldBeContext()) + && (isWriteMediatedContextInstVarIndex(index))) { + return genStorePopMaybeContextReceiverVariableneedsStoreCheckneedsImmutabilityCheck(1, index, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } + else { + return genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(1, index, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } +} + + +/* 233 11101001 i i i i i i i i Store Literal Variable #iiiiiiii (+ Extend A + * 256) + */ + + /* SimpleStackBasedCogit>>#genExtStoreLiteralVariableBytecode */ +static sqInt +genExtStoreLiteralVariableBytecode(void) +{ + sqInt index; + + index = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + return genStorePopLiteralVariableneedsStoreCheckneedsImmutabilityCheck(0, index, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); +} + + +/* 232 11101000 i i i i i i i i Store Receiver Variable #iiiiiii (+ Extend A + * 256) + */ + + /* SimpleStackBasedCogit>>#genExtStoreReceiverVariableBytecode */ +static sqInt +genExtStoreReceiverVariableBytecode(void) +{ + sqInt index; + + index = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + if ((mclassCouldBeContext()) + && (isWriteMediatedContextInstVarIndex(index))) { + return genStorePopMaybeContextReceiverVariableneedsStoreCheckneedsImmutabilityCheck(0, index, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } + else { + return genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(0, index, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } +} + + +/* 242 11110010 i i i i i i i i Jump i i i i i i i i (+ Extend B * 256, + where bbbbbbbb = sddddddd, e.g. -32768 = i=0, a=0, s=1) + */ + + /* SimpleStackBasedCogit>>#genExtUnconditionalJump */ +static sqInt +genExtUnconditionalJump(void) +{ + AbstractInstruction *abstractInstruction; + sqInt distance; + sqInt target; + + distance = byte1 + ((((usqInt)(extB) << 8))); + assert(distance == (v4LongBranchDistance(generatorAt(byte0), bytecodePC, ((extA != 0 + ? 1 + : 0)) + ((extB != 0 + ? 1 + : 0)), methodObj))); + extB = 0; + numExtB = 0; + target = (distance + 2) + bytecodePC; + if (distance < 0) { + return genJumpBackTo(target); + } + genJumpTo(target); + + /* The bytecode must be mapped since it can be either forward or backward, and + backwards branches must be mapped. So if forward, we need to map. */ + abstractInstruction = lastOpcode(); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + return 0; +} + + /* SimpleStackBasedCogit>>#genFastPrimFail */ +static sqInt +genFastPrimFail(void) +{ + primitiveIndex = 0; + return UnfailingPrimitive; +} + + +/* Support for compileInterpreterPrimitive. Generate inline code + so as to record the primitive trace as fast as possible. If setNewMethod + as a side-effect write the method to newMethod. */ + + /* SimpleStackBasedCogit>>#genFastPrimTraceUsing:and:setNewMethod: */ +static NoDbgRegParms void +genFastPrimTraceUsingandsetNewMethod(sqInt r1, sqInt r2, sqInt setNewMethod) +{ + AbstractInstruction *anInstruction; + sqInt offset; + + + /* #MoveAb:R: #gen:literal:operand: */ + checkLiteralforInstruction(primTraceLogIndexAddress(), genoperandoperand(MoveAbR, primTraceLogIndexAddress(), r2)); + + /* begin LoadEffectiveAddressMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(LoadEffectiveAddressMwrR, 1, r2, r1); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(1, BytesPerOop)); + } + + /* #MoveR:Ab: #gen:operand:literal: */ + checkLiteralforInstruction(primTraceLogIndexAddress(), genoperandoperand(MoveRAb, r1, primTraceLogIndexAddress())); + addDependent(methodLabel, annotateAbsolutePCRef(checkLiteralforInstruction(((sqInt)methodLabel), genoperandoperand(MoveCwR, ((sqInt)methodLabel), r1)))); + offset = offsetof(CogMethod, methodObject); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, offset, r1, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* #MoveCw:R: #gen:literal:operand: */ + checkLiteralforInstruction(((sqInt)(primTraceLogAddress())), genoperandoperand(MoveCwR, ((sqInt)(primTraceLogAddress())), r1)); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, TempReg, r2, r1); + if (setNewMethod) { + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(newMethodAddress(), genoperandoperand(MoveRAw, TempReg, newMethodAddress())); + + /* begin maybeGenZeroLRPStopUsecs */ +# if LRPCheck + if (checkingLongRunningPrimitives()) { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, 0, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(longRunningPrimitiveStopUsecsAddress(), genoperandoperand(MoveRAw, TempReg, longRunningPrimitiveStopUsecsAddress())); + } +# endif // LRPCheck + + } +} + + /* SimpleStackBasedCogit>>#genLoadNewMethod */ +static void +genLoadNewMethod(void) +{ + AbstractInstruction *anInstruction; + sqInt offset; + + addDependent(methodLabel, annotateAbsolutePCRef(checkLiteralforInstruction(((sqInt)methodLabel), genoperandoperand(MoveCwR, ((sqInt)methodLabel), ClassReg)))); + offset = offsetof(CogMethod, methodObject); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, offset, ClassReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(newMethodAddress(), genoperandoperand(MoveRAw, TempReg, newMethodAddress())); + + /* begin maybeGenZeroLRPStopUsecs */ +# if LRPCheck + if (checkingLongRunningPrimitives()) { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, 0, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(longRunningPrimitiveStopUsecsAddress(), genoperandoperand(MoveRAw, TempReg, longRunningPrimitiveStopUsecsAddress())); + } +# endif // LRPCheck +} + + /* SimpleStackBasedCogit>>#genLongJumpIfFalse */ +static sqInt +genLongJumpIfFalse(void) +{ + sqInt distance; + sqInt target; + + distance = v3LongForwardBranchDistance(generatorAt(byte0), bytecodePC, 0, methodObj); + target = (distance + 2) + bytecodePC; + return genJumpIfto(falseObject(), target); +} + + /* SimpleStackBasedCogit>>#genLongJumpIfTrue */ +static sqInt +genLongJumpIfTrue(void) +{ + sqInt distance; + sqInt target; + + distance = v3LongForwardBranchDistance(generatorAt(byte0), bytecodePC, 0, methodObj); + target = (distance + 2) + bytecodePC; + return genJumpIfto(trueObject(), target); +} + + +/* 230 11100110 i i i i i i i i Push Temporary Variable #iiiiiiii */ + + /* SimpleStackBasedCogit>>#genLongPushTemporaryVariableBytecode */ +static sqInt +genLongPushTemporaryVariableBytecode(void) +{ + return genPushTemporaryVariable(byte1); +} + + +/* 237 11101101 i i i i i i i i Pop and Store Temporary Variable #iiiiiiii */ + + /* SimpleStackBasedCogit>>#genLongStoreAndPopTemporaryVariableBytecode */ +static sqInt +genLongStoreAndPopTemporaryVariableBytecode(void) +{ + return genStorePopTemporaryVariable(1, byte1); +} + + +/* 234 11101010 i i i i i i i i Store Temporary Variable #iiiiiiii */ + + /* SimpleStackBasedCogit>>#genLongStoreTemporaryVariableBytecode */ +static sqInt +genLongStoreTemporaryVariableBytecode(void) +{ + return genStorePopTemporaryVariable(0, byte1); +} + + /* SimpleStackBasedCogit>>#genLongUnconditionalBackwardJump */ +static sqInt +genLongUnconditionalBackwardJump(void) +{ + sqInt distance; + + distance = v3LongBranchDistance(generatorAt(byte0), bytecodePC, 0, methodObj); + assert(distance < 0); + return genJumpBackTo((distance + 2) + bytecodePC); +} + + /* SimpleStackBasedCogit>>#genLongUnconditionalForwardJump */ +static sqInt +genLongUnconditionalForwardJump(void) +{ + sqInt distance; + sqInt targetpc; + + distance = v3LongBranchDistance(generatorAt(byte0), bytecodePC, 0, methodObj); + assert(distance >= 0); + targetpc = (distance + 2) + bytecodePC; + return genJumpTo(targetpc); +} + + +/* Compile the code for a probe of the first-level method cache for a perform + primitive. The selector is assumed to be in Arg0Reg. Defer to + adjustArgumentsForPerform: to + adjust the arguments before the jump to the method. */ +/* N.B. Can't assume TempReg already contains the tag because a method can + of course be invoked via the unchecked entry-point, e.g. as does perform:. */ + + /* SimpleStackBasedCogit>>#genLookupForPerformNumArgs: */ +static NoDbgRegParms sqInt +genLookupForPerformNumArgs(sqInt numArgs) +{ + AbstractInstruction *anInstruction; + sqInt cacheBaseReg; + AbstractInstruction *itsAHit; + AbstractInstruction *jumpBadNumArgs; + AbstractInstruction *jumpClassMiss; + AbstractInstruction *jumpInterpret; + AbstractInstruction *jumpSelectorMiss; + sqInt offset; + + genGetInlineCacheClassTagFromintoforEntry(ReceiverResultReg, SendNumArgsReg, 0); + cacheBaseReg = NoReg; + + /* Do first of three probes. See CoInterpreter>>lookupInMethodCacheSel:classTag: */ + jumpSelectorMiss = compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(Arg0Reg, 0, cacheBaseReg); + jumpClassMiss = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Fetch the method, and check if it is cogged. */ + offset = (cacheBaseReg == NoReg + ? (methodCacheAddress()) + ((((usqInt)(MethodCacheMethod) << (shiftForWord())))) + : ((sqInt)((usqInt)(MethodCacheMethod) << (shiftForWord())))); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + itsAHit = genoperandoperandoperand(MoveMwrR, offset, ClassReg, SendNumArgsReg); + if (usesOutOfLineLiteral(itsAHit)) { + (itsAHit->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* If the method is not compiled fall back on the interpreter primitive. */ + genLoadSlotsourceRegdestReg(HeaderIndex, SendNumArgsReg, ClassReg); + jumpInterpret = genJumpImmediate(ClassReg); + + /* check the argument count; if it's wrong fall back on the interpreter primitive. */ + + /* begin genLoadcmNumArgsOf:into: */ + offset = sizeof(((((CogMethod *) 0))->objectHeader)); + + /* begin MoveMb:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMbrR, offset, ClassReg, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, numArgs - 1, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(numArgs - 1, BytesPerOop)); + } + jumpBadNumArgs = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Adjust arguments and jump to the method's unchecked entry-point. */ + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, cmNoCheckEntryOffset, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(cmNoCheckEntryOffset, BytesPerOop)); + } + adjustArgumentsForPerform(numArgs); + + /* JumpR: */ + genoperand(JumpR, ClassReg); + + /* First probe missed. Do second of three probes. Shift hash right one and retry. */ + jmpTarget(jumpSelectorMiss, jmpTarget(jumpClassMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + jumpSelectorMiss = compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(Arg0Reg, 1, cacheBaseReg); + + /* JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)itsAHit)); + + /* Second probe missed. Do last probe. Shift hash right two and retry. */ + jmpTarget(jumpSelectorMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpSelectorMiss = compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(Arg0Reg, 2, cacheBaseReg); + + /* JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)itsAHit)); + + /* Last probe missed/not jitted/bad num args. Caller will generate the call to fall back on the interpreter primitive. */ + jmpTarget(jumpSelectorMiss, jmpTarget(jumpInterpret, jmpTarget(jumpBadNumArgs, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + return 0; +} + + +/* If the objectMemory allows it, generates a quick constant move, else + generates a word constant move + */ + + /* SimpleStackBasedCogit>>#genMoveConstant:R: */ +static NoDbgRegParms AbstractInstruction * +genMoveConstantR(sqInt constant, sqInt reg) +{ + AbstractInstruction *anInstruction; + + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(constant)) + && ((oopisGreaterThan(constant, classTableRootObj())) + || (oopisLessThan(constant, nilObject())))) { + return annotateobjRef(checkLiteralforInstruction(constant, genoperandoperand(MoveCwR, constant, reg)), constant); + } + else { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, constant, reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(constant, BytesPerOop)); + } + return anInstruction; + } +} + + /* SimpleStackBasedCogit>>#genMustBeBooleanTrampolineFor:called: */ +static NoDbgRegParms usqInt +genMustBeBooleanTrampolineForcalled(sqInt boolean, char *trampolineName) +{ + AbstractInstruction *anInstruction; + + zeroOpcodeIndex(); + + /* If the objectRepresentation does want true & false to be mobile then we need to record these addresses. */ + assert(!(shouldAnnotateObjectReference(boolean))); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, boolean, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(boolean, BytesPerOop)); + } + return genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceSendMustBeBoolean, trampolineName, 1, TempReg, null, null, null, 0 /* emptyRegisterMask */, 1, NoReg, 1); +} + + +/* Implement 28-bit hashMultiply for SmallInteger and LargePositiveInteger + receivers. + */ + + /* SimpleStackBasedCogit>>#genPrimitiveHashMultiply */ +static sqInt +genPrimitiveHashMultiply(void) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jmpFailImm; + AbstractInstruction *jmpFailNotPositiveLargeInt; + + if (mclassIsSmallInteger()) { + genConvertSmallIntegerToIntegerInReg(ReceiverResultReg); + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, HashMultiplyConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(HashMultiplyConstant, BytesPerOop)); + } + + /* MulR:R: */ + genMulRR(backEnd, TempReg, ReceiverResultReg); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, HashMultiplyMask, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(HashMultiplyMask, BytesPerOop)); + } + genConvertIntegerToSmallIntegerInReg(ReceiverResultReg); + + /* RetN: */ + genoperand(RetN, 0); + return CompletePrimitive; + } + + /* This check is necessary. LargeNegativeInteger is a subclass of LargePositiveInteger in Squeak, Pharo, Cuis, et al. */ + jmpFailImm = genJumpImmediate(ReceiverResultReg); + genGetCompactClassIndexNonImmOfinto(ReceiverResultReg, ClassReg); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, ClassLargePositiveIntegerCompactIndex, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(ClassLargePositiveIntegerCompactIndex, BytesPerOop)); + } + jmpFailNotPositiveLargeInt = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genLoadSlotsourceRegdestReg(0, ReceiverResultReg, ReceiverResultReg); + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, HashMultiplyConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(HashMultiplyConstant, BytesPerOop)); + } + + /* MulR:R: */ + genMulRR(backEnd, TempReg, ReceiverResultReg); + + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, HashMultiplyMask, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(HashMultiplyMask, BytesPerOop)); + } + genConvertIntegerToSmallIntegerInReg(ReceiverResultReg); + + /* RetN: */ + genoperand(RetN, 0); + jmpTarget(jmpFailImm, jmpTarget(jmpFailNotPositiveLargeInt, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + +/* Generate the substitute return code for an external or FFI primitive call. + On success simply return, extracting numArgs from newMethod. + On primitive failure call ceActivateFailingPrimitiveMethod: newMethod. */ + + /* SimpleStackBasedCogit>>#genPrimReturnEnterCogCodeEnilopmart: */ +static NoDbgRegParms void +genPrimReturnEnterCogCodeEnilopmart(sqInt profiling) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + sqInt callTargetSqInt; + AbstractInstruction *continuePostSample; + AbstractInstruction *jmpFail; + AbstractInstruction *jmpSample; + int newMethodReg; + sqInt quickConstant; + sqInt reg; + + continuePostSample = ((AbstractInstruction *) 0); + jmpSample = ((AbstractInstruction *) 0); + zeroOpcodeIndex(); + quickConstant = varBaseAddress; + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, quickConstant, VarBaseReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + if (profiling) { + /* begin genCheckForProfileTimerTick: */ + reg = Arg0Reg; + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(nextProfileTickAddress(), genoperandoperand(MoveAwR, nextProfileTickAddress(), Arg1Reg)); + gMovePerfCnt64RL(reg, (0)); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, reg); + jmpSample = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + continuePostSample = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + } + + /* Test primitive failure */ + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(primFailCodeAddress(), genoperandoperand(MoveAwR, primFailCodeAddress(), TempReg)); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jmpFail = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Switch back to the Smalltalk stack. Stack better be in either of these two states: + success: stackPointer -> result (was receiver) + arg1 + ... + argN + return pc + failure: receiver + arg1 + ... + stackPointer -> argN + return pc + We push the instructionPointer to reestablish the return pc in the success case, + but leave it to ceActivateFailingPrimitiveMethod: to do so in the failure case. */ + genLoadStackPointers(backEnd); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, 0, SPReg, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(instructionPointerAddress(), genoperandoperand(MoveAwR, instructionPointerAddress(), LinkReg)); + + /* RetN: */ + genoperand(RetN, BytesPerWord); + jmpTarget(jmpFail, checkLiteralforInstruction(newMethodAddress(), genoperandoperand(MoveAwR, newMethodAddress(), SendNumArgsReg))); + + /* Reload sp with CStackPointer; easier than popping args of checkProfileTick. */ + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(cStackPointerAddress(), genoperandoperand(MoveAwR, cStackPointerAddress(), SPReg)); + compileCallFornumArgsargargargargresultRegregsToSave(ceActivateFailingPrimitiveMethod, 1, SendNumArgsReg, null, null, null, NoReg, 0 /* emptyRegisterMask */); + + /* On Spur ceActivateFailingPrimitiveMethod: may retry the primitive and return if successful. + So continue by returning to the caller. + Switch back to the Smalltalk stack. Stack should be in this state: + success: stackPointer -> result (was receiver) + arg1 + ... + argN + return pc + We can push the instructionPointer or load it into the LinkRegister to reestablish the return pc */ + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(instructionPointerAddress(), genoperandoperand(MoveAwR, instructionPointerAddress(), LinkReg)); + genLoadStackPointers(backEnd); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, 0, SPReg, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* RetN: */ + genoperand(RetN, BytesPerWord); + if (profiling) { + jmpTarget(jmpSample, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* Test the register(s) containing nextProfileTick's value and jump to continue if not + greater than zero. See genCheckForProfileTimerTick: for the register assignments. */ + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* JumpLessOrEqual: */ + genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)continuePostSample)); + newMethodReg = CArg0Reg; + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(newMethodAddress(), genoperandoperand(MoveAwR, newMethodAddress(), newMethodReg)); + genMarshallNArgsargargargarg(backEnd, 0, null, null, null, null); + + /* begin CallAnyFixedRT: */ + if (isInImmediateBranchAndLinkRange(backEnd, maximumDistanceFromCodeZone(((usqInt)ceTakeProfileSample)))) { + callTargetSqInt = ((usqIntptr_t)ceTakeProfileSample); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, callTargetSqInt); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* #CallFullRT: #CallFull: #gen:literal: */ + checkLiteralforInstruction(((usqIntptr_t)ceTakeProfileSample), genoperand(CallFull, ((usqIntptr_t)ceTakeProfileSample))); + } + + /* genRemoveNArgsFromStack: */ + + /* Jump: */ + genoperand(Jump, ((sqInt)continuePostSample)); + } +} + + /* SimpleStackBasedCogit>>#genPushConstantFalseBytecode */ +static sqInt +genPushConstantFalseBytecode(void) +{ + return ssPushConstant(falseObject()); +} + + /* SimpleStackBasedCogit>>#genPushConstantNilBytecode */ +static sqInt +genPushConstantNilBytecode(void) +{ + return ssPushConstant(nilObject()); +} + + +/* 79 01001111 Push 1 */ + + /* SimpleStackBasedCogit>>#genPushConstantOneBytecode */ +static sqInt +genPushConstantOneBytecode(void) +{ + return ssPushConstant((((usqInt)1 << 3) | 1)); +} + + /* SimpleStackBasedCogit>>#genPushConstantTrueBytecode */ +static sqInt +genPushConstantTrueBytecode(void) +{ + return ssPushConstant(trueObject()); +} + + +/* 78 01001110 Push 0 */ + + /* SimpleStackBasedCogit>>#genPushConstantZeroBytecode */ +static sqInt +genPushConstantZeroBytecode(void) +{ + return ssPushConstant((((usqInt)0 << 3) | 1)); +} + + /* SimpleStackBasedCogit>>#genPushLiteralConstantBytecode */ +static sqInt +genPushLiteralConstantBytecode(void) +{ + return genPushLiteralIndex(byte0 & 0x1F); +} + + +/* 16-31 0001 i i i i Push Literal Variable #iiii */ + + /* SimpleStackBasedCogit>>#genPushLiteralVariable16CasesBytecode */ +static sqInt +genPushLiteralVariable16CasesBytecode(void) +{ + return genPushLiteralVariable(byte0 & 15); +} + + /* SimpleStackBasedCogit>>#genPushLiteralVariableBytecode */ +static sqInt +genPushLiteralVariableBytecode(void) +{ + return genPushLiteralVariable(byte0 & 0x1F); +} + + /* SimpleStackBasedCogit>>#genPushQuickIntegerConstantBytecode */ +static sqInt +genPushQuickIntegerConstantBytecode(void) +{ + return ssPushConstant((((usqInt)(byte0 - 117) << 3) | 1)); +} + + /* SimpleStackBasedCogit>>#genPushReceiverVariableBytecode */ +static sqInt +genPushReceiverVariableBytecode(void) +{ + return genPushReceiverVariable(byte0 & 15); +} + + /* SimpleStackBasedCogit>>#genPushTemporaryVariableBytecode */ +static sqInt +genPushTemporaryVariableBytecode(void) +{ + return genPushTemporaryVariable(byte0 & 15); +} + + +/* because selected by CoInterpreter>>quickPrimitiveGeneratorFor: */ + + /* SimpleStackBasedCogit>>#genQuickReturnConst */ +sqInt +genQuickReturnConst(void) +{ + AbstractInstruction *anInstruction; + sqInt constant; + + constant = quickPrimitiveConstantFor(primitiveIndex); + + /* begin genMoveConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(constant)) + && ((oopisGreaterThan(constant, classTableRootObj())) + || (oopisLessThan(constant, nilObject())))) { + annotateobjRef(checkLiteralforInstruction(constant, genoperandoperand(MoveCwR, constant, ReceiverResultReg)), constant); + } + else { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, constant, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(constant, BytesPerOop)); + } + } + genUpArrowReturn(); + return UnfailingPrimitive; +} + + +/* because selected by CoInterpreter>>quickPrimitiveGeneratorFor: */ + + /* SimpleStackBasedCogit>>#genQuickReturnInstVar */ +sqInt +genQuickReturnInstVar(void) +{ + sqInt index; + + index = quickPrimitiveInstVarIndexFor(primitiveIndex); + genLoadSlotsourceRegdestReg(index, ReceiverResultReg, ReceiverResultReg); + genUpArrowReturn(); + return UnfailingPrimitive; +} + + +/* because selected by CoInterpreter>>quickPrimitiveGeneratorFor: */ + + /* SimpleStackBasedCogit>>#genQuickReturnSelf */ +sqInt +genQuickReturnSelf(void) +{ + genUpArrowReturn(); + return UnfailingPrimitive; +} + + /* SimpleStackBasedCogit>>#genReturnFalse */ +static sqInt +genReturnFalse(void) +{ + AbstractInstruction *anInstruction; + + /* begin genMoveConstant:R: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(falseObject(), BytesPerOop)); + } + return genUpArrowReturn(); +} + + /* SimpleStackBasedCogit>>#genReturnNil */ +static sqInt +genReturnNil(void) +{ + AbstractInstruction *anInstruction; + + /* begin genMoveConstant:R: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, nilObject(), ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(nilObject(), BytesPerOop)); + } + return genUpArrowReturn(); +} + + /* SimpleStackBasedCogit>>#genReturnNilFromBlock */ +static sqInt +genReturnNilFromBlock(void) +{ + AbstractInstruction *anInstruction; + + assert(inBlock > 0); + + /* begin genMoveConstant:R: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, nilObject(), ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(nilObject(), BytesPerOop)); + } + return genBlockReturn(); +} + + /* SimpleStackBasedCogit>>#genReturnTrue */ +static sqInt +genReturnTrue(void) +{ + AbstractInstruction *anInstruction; + + /* begin genMoveConstant:R: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, trueObject(), ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(trueObject(), BytesPerOop)); + } + return genUpArrowReturn(); +} + + +/* Can use any of the first 64 literals for the selector and pass up to 3 + arguments. + */ + + /* SimpleStackBasedCogit>>#genSecondExtendedSendBytecode */ +static sqInt +genSecondExtendedSendBytecode(void) +{ + return genSendnumArgs(byte1 & 0x3F, ((usqInt)(byte1)) >> 6); +} + + /* SimpleStackBasedCogit>>#genSendLiteralSelector0ArgsBytecode */ +static sqInt +genSendLiteralSelector0ArgsBytecode(void) +{ + return genSendnumArgs(byte0 & 15, 0); +} + + /* SimpleStackBasedCogit>>#genSendLiteralSelector1ArgBytecode */ +static sqInt +genSendLiteralSelector1ArgBytecode(void) +{ + return genSendnumArgs(byte0 & 15, 1); +} + + /* SimpleStackBasedCogit>>#genSendLiteralSelector2ArgsBytecode */ +static sqInt +genSendLiteralSelector2ArgsBytecode(void) +{ + return genSendnumArgs(byte0 & 15, 2); +} + + /* SimpleStackBasedCogit>>#genShortJumpIfFalse */ +static sqInt +genShortJumpIfFalse(void) +{ + sqInt distance; + sqInt target; + + distance = v3ShortForwardBranchDistance(generatorAt(byte0), bytecodePC, 0, methodObj); + target = (distance + 1) + bytecodePC; + return genJumpIfto(falseObject(), target); +} + + /* SimpleStackBasedCogit>>#genShortJumpIfTrue */ +static sqInt +genShortJumpIfTrue(void) +{ + sqInt distance; + sqInt target; + + distance = v3ShortForwardBranchDistance(generatorAt(byte0), bytecodePC, 0, methodObj); + target = (distance + 1) + bytecodePC; + return genJumpIfto(trueObject(), target); +} + + /* SimpleStackBasedCogit>>#genShortUnconditionalJump */ +static sqInt +genShortUnconditionalJump(void) +{ + sqInt distance; + sqInt target; + + distance = v3ShortForwardBranchDistance(generatorAt(byte0), bytecodePC, 0, methodObj); + target = (distance + 1) + bytecodePC; + return genJumpTo(target); +} + + /* SimpleStackBasedCogit>>#genSpecialSelectorEqualsEquals */ +static sqInt +genSpecialSelectorEqualsEquals(void) +{ + return genInlinedIdenticalOrNotIf(0); +} + + /* SimpleStackBasedCogit>>#genSpecialSelectorNotEqualsEquals */ +static sqInt +genSpecialSelectorNotEqualsEquals(void) +{ + return genInlinedIdenticalOrNotIf(1); +} + + /* SimpleStackBasedCogit>>#genSpecialSelectorSend */ +static sqInt +genSpecialSelectorSend(void) +{ + sqInt index; + sqInt numArgs; + + index = byte0 - (/* firstSpecialSelectorBytecodeOffset */ + (bytecodeSetOffset == 0x100 + ? AltFirstSpecialSelector + 0x100 + : FirstSpecialSelector)); + numArgs = specialSelectorNumArgs(index); + return genSendnumArgs((-index) - 1, numArgs); +} + + /* SimpleStackBasedCogit>>#genStoreAndPopReceiverVariableBytecode */ +static sqInt +genStoreAndPopReceiverVariableBytecode(void) +{ + return genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(1, byte0 & 7, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); +} + + /* SimpleStackBasedCogit>>#genStoreAndPopRemoteTempLongBytecode */ +static sqInt +genStoreAndPopRemoteTempLongBytecode(void) +{ + return genStorePopRemoteTempAtneedsStoreCheck(1, byte1, byte2, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject())))))); +} + + /* SimpleStackBasedCogit>>#genStoreAndPopTemporaryVariableBytecode */ +static sqInt +genStoreAndPopTemporaryVariableBytecode(void) +{ + return genStorePopTemporaryVariable(1, byte0 & 7); +} + + /* SimpleStackBasedCogit>>#genStoreRemoteTempLongBytecode */ +static sqInt +genStoreRemoteTempLongBytecode(void) +{ + return genStorePopRemoteTempAtneedsStoreCheck(0, byte1, byte2, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject())))))); +} + + +/* Test if nextProfileTick is non-zero and if so call ceTakeProfileSample on + the C stack and jump to continueLabel. If nextProfileTick is not greater + than zero + jump to continueLabel without calling. */ +/* Test the register(s) containing nextProfileTick's value and jump to + continue if not + greater than zero. See genCheckForProfileTimerTick: for the register + assignments. + */ + + /* SimpleStackBasedCogit>>#genTakeProfileSample: */ +static NoDbgRegParms void +genTakeProfileSample(AbstractInstruction *continueLabel) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + sqInt callTargetSqInt; + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* JumpLessOrEqual: */ + genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)continueLabel)); + + /* in 32-bits if the top 32-bits is negative the value is negative */ + + /* begin genLoadCStackPointersForPrimCall */ + if (cFramePointerInUse) { + genLoadCStackPointers(backEnd); + } + else { + genLoadCStackPointer(backEnd); + } + + /* genAlignCStackGivenRegistersToBeSaved:numArgs:wordAlignment: */ + genMarshallNArgsargargargarg(backEnd, 0, null, null, null, null); + + /* begin CallAnyFixedRT: */ + if (isInImmediateBranchAndLinkRange(backEnd, maximumDistanceFromCodeZone(((usqInt)ceTakeProfileSample)))) { + callTargetSqInt = ((usqIntptr_t)ceTakeProfileSample); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, callTargetSqInt); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* #CallFullRT: #CallFull: #gen:literal: */ + checkLiteralforInstruction(((usqIntptr_t)ceTakeProfileSample), genoperand(CallFull, ((usqIntptr_t)ceTakeProfileSample))); + } + + /* no need to remove stacked args since the stack pointers are immediately reloaded with the Smalltalk stack pointers. */ + genLoadStackPointersForPrimCall(backEnd, ClassReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)continueLabel)); +} + + +/* SistaV1: * 217 Trap */ + + /* SimpleStackBasedCogit>>#genUnconditionalTrapBytecode */ +static sqInt +genUnconditionalTrapBytecode(void) +{ + return EncounteredUnknownBytecode; +} + + +/* Answer the Cogit's ISA as a string */ + + /* SimpleStackBasedCogit>>#isaName */ +#if VMInvestigations +char * +isaName(void) +{ + return ISA; +} +#endif /* VMInvestigations */ + + +/* Collect the branch and send data for cogMethod, storing it into arrayObj. */ + + /* SimpleStackBasedCogit>>#mapPCDataFor:into: */ +sqInt +mapPCDataForinto(CogMethod *cogMethod, sqInt arrayObj) +{ + sqInt aMethodObj; + sqInt annotation; + sqInt bcpc; + sqInt bsOffset; + sqInt byte; + CogBlockMethod *cogMethod1; + BytecodeDescriptor *descriptor; + sqInt distance; + sqInt endbcpc; + usqInt errCode; + CogMethod *homeMethod; + sqInt isBackwardBranch; + sqInt isInBlock; + sqInt latestContinuation; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt nExts; + sqInt nextBcpc; + sqInt result; + sqInt startbcpc; + sqInt targetPC; + + introspectionDataIndex = 0; + introspectionData = arrayObj; + if (!((cogMethod->stackCheckOffset))) { + assert(introspectionDataIndex == 0); + if ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) { + storePointerUncheckedofObjectwithValue(0, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(1, introspectionData, (((usqInt)cbNoSwitchEntryOffset << 3) | 1)); + storePointerUncheckedofObjectwithValue(2, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(3, introspectionData, (((usqInt)cbEntryOffset << 3) | 1)); + } + else { + storePointerUncheckedofObjectwithValue(0, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(1, introspectionData, (((usqInt)cmEntryOffset << 3) | 1)); + storePointerUncheckedofObjectwithValue(2, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(3, introspectionData, (((usqInt)cmNoCheckEntryOffset << 3) | 1)); + } + return 4; + } + cogMethod1 = ((CogBlockMethod *) cogMethod); + startbcpc = startPCOfMethod((cogMethod->methodObject)); + + /* begin mapFor:bcpc:performUntil:arg: */ + descriptor = ((BytecodeDescriptor *) 0); + latestContinuation = 0; + mapByte = 0; + nextBcpc = 0; + assert(((cogMethod1->stackCheckOffset)) > 0); + mcpc = (((usqInt)cogMethod1)) + ((cogMethod1->stackCheckOffset)); + + /* The stack check maps to the start of the first bytecode, + the first bytecode being effectively after frame build. */ + result = pcDataForAnnotationMcpcBcpcMethod(null, 0 + ((((usqInt)(HasBytecodePC) << 1))), ((char *) mcpc), startbcpc, ((void *)cogMethod)); + if (result) { + errCode = result; + goto l1; + } + bcpc = startbcpc; + + /* In both CMMethod and CMBlock cases find the start of the map and + skip forward to the bytecode pc map entry for the stack check. */ + if (((cogMethod1->cmType)) >= CMMethod) { + isInBlock = (cogMethod1->cpicHasMNUCaseOrCMIsFullBlock); + homeMethod = ((CogMethod *) cogMethod1); + assert(startbcpc == (startPCOfMethodHeader((homeMethod->methodHeader)))); + map = ((((usqInt)homeMethod)) + ((homeMethod->blockSize))) - 1; + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert((annotation == IsAbsPCReference) + || ((annotation == IsObjectReference) + || ((annotation == IsRelativeCall) + || (annotation == IsDisplacementX2N)))); + latestContinuation = startbcpc; + aMethodObj = (homeMethod->methodObject); + endbcpc = (numBytesOf(aMethodObj)) - 1; + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + + /* If the method has a primitive, skip it and the error code store, if any; + Logically. these come before the stack check and so must be ignored. */ + bcpc += deltaToSkipPrimAndErrorStoreInheader(aMethodObj, (homeMethod->methodHeader)); + } + else { + isInBlock = 1; + assert(bcpc == ((cogMethod1->startpc))); + homeMethod = cmHomeMethod(cogMethod1); + map = findMapLocationForMcpcinMethod((((usqInt)cogMethod1)) + (sizeof(CogBlockMethod)), homeMethod); + assert(map != 0); + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert(((((usqInt)(annotation)) >> AnnotationShift) == HasBytecodePC) + || ((((usqInt)(annotation)) >> AnnotationShift) == IsDisplacementX2N)); + + /* fiducial */ + while (((annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift)) != HasBytecodePC) { + map -= 1; + } + + /* skip fiducial; i.e. the map entry for the pc immediately following the method header. */ + map -= 1; + aMethodObj = (homeMethod->methodObject); + bcpc = startbcpc - (/* blockCreationBytecodeSizeForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? AltBlockCreationBytecodeSize + : BlockCreationBytecodeSize)); + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + endbcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, -1, aMethodObj) + : 0)); + bcpc = startbcpc; + } + nExts = 0; + enumeratingCogMethod = homeMethod; + + /* Now skip up through the bytecode pc map entry for the stack check. */ + while ((((usqInt)((byteAt((void *)(map))))) >> AnnotationShift) != HasBytecodePC) { + map -= 1; + } + map -= 1; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + mcpc += (mapByte & DisplacementMask) * 4 /* codeGranularity */; + if (annotation >= HasBytecodePC) { + if ((annotation == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + while (1) { + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + if (isInBlock) { + if (bcpc >= endbcpc) { + errCode = 0; + goto l1; + } + } + else { + if (((descriptor->isReturn)) + && (bcpc >= latestContinuation)) { + errCode = 0; + goto l1; + } + if ((isBranch(descriptor)) + || ((descriptor->isBlockCreation))) { + /* begin latestContinuationPCFor:at:exts:in: */ + distance = ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj); + targetPC = (bcpc + ((descriptor->numBytes))) + (((distance < 0) ? 0 : distance)); + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + } + } + nextBcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj) + : 0)); + if (((descriptor->isMapped)) + || (isInBlock + && ((descriptor->isMappedInBlock)))) break; + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + isBackwardBranch = (isBranch(descriptor)) + && ((/* begin isBackwardBranch:at:exts:in: */ + assert(((descriptor->spanFunction))), + (((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj)) < 0)); + result = pcDataForAnnotationMcpcBcpcMethod(descriptor, (isBackwardBranch + ? ((((usqInt)(annotation) << 1))) + 1 + : ((sqInt)((usqInt)(annotation) << 1))), ((char *) mcpc), (isBackwardBranch + ? bcpc - (2 * nExts) + : bcpc), ((void *)cogMethod)); + if (result) { + errCode = result; + goto l1; + } + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + } + else { + assert(((((usqInt)(mapByte)) >> AnnotationShift) == IsDisplacementX2N) + || ((((usqInt)(mapByte)) >> AnnotationShift) == IsAnnotationExtension)); + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))) * 4 /* codeGranularity */; + } + } + map -= 1; + } + + /* defensive; we exit on bcpc */ + errCode = 0; + /* end mapFor:bcpc:performUntil:arg: */ +l1: + if (errCode) { + assert(errCode == PrimErrNoMemory); + return -1; + } + if ((cogMethod->blockEntryOffset)) { + errCode = blockDispatchTargetsForperformarg(cogMethod, pcDataForBlockEntryMethod, ((sqInt)cogMethod)); + if (errCode) { + assert(errCode == PrimErrNoMemory); + return -1; + } + } + return introspectionDataIndex; +} + + /* SimpleStackBasedCogit>>#numSpecialSelectors */ +static sqInt +numSpecialSelectors(void) +{ + return (bytecodeSetOffset == 0x100 + ? AltNumSpecialSelectors + : NumSpecialSelectors); +} + + +/* Collect the branch and send data for the block method starting at + blockEntryMcpc, storing it into picData. + */ + + /* SimpleStackBasedCogit>>#pcDataForBlockEntry:Method: */ +static NoDbgRegParms usqInt +pcDataForBlockEntryMethod(sqInt blockEntryMcpc, sqInt cogMethod) +{ + storePointerUncheckedofObjectwithValue(introspectionDataIndex, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 1, introspectionData, (((usqInt)(blockEntryMcpc - blockNoContextSwitchOffset) << 3) | 1)); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 2, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 3, introspectionData, (((usqInt)blockEntryMcpc << 3) | 1)); + introspectionDataIndex += 4; + return 0; +} + + /* SimpleStackBasedCogit>>#pcDataFor:Annotation:Mcpc:Bcpc:Method: */ +static NoDbgRegParms sqInt +pcDataForAnnotationMcpcBcpcMethod(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *cogMethodArg) +{ + sqInt actualBcpc; + sqInt actualMcpc; + + if (!descriptor) { + assert(introspectionDataIndex == 0); + if (((((CogMethod *) cogMethodArg))->cpicHasMNUCaseOrCMIsFullBlock)) { + storePointerUncheckedofObjectwithValue(introspectionDataIndex, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 1, introspectionData, (((usqInt)cbNoSwitchEntryOffset << 3) | 1)); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 2, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 3, introspectionData, (((usqInt)cbEntryOffset << 3) | 1)); + } + else { + storePointerUncheckedofObjectwithValue(introspectionDataIndex, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 1, introspectionData, (((usqInt)cmEntryOffset << 3) | 1)); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 2, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 3, introspectionData, (((usqInt)cmNoCheckEntryOffset << 3) | 1)); + } + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 4, introspectionData, (((usqInt)(bcpc + 1) << 3) | 1)); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 5, introspectionData, (((usqInt)(((((CogMethod *) cogMethodArg))->stackCheckOffset)) << 3) | 1)); + introspectionDataIndex += 6; + return 0; + } + + /* this is the stackCheck offset */ + if ((((usqInt)(isBackwardBranchAndAnnotation)) >> 1) >= HasBytecodePC) { + actualBcpc = (((isBackwardBranchAndAnnotation & 1) != 0) + ? bcpc + 1 + : (bcpc + ((descriptor->numBytes))) + 1); + actualMcpc = (((usqInt)mcpc)) - (((usqInt)cogMethodArg)); + storePointerUncheckedofObjectwithValue(introspectionDataIndex, introspectionData, (((usqInt)actualBcpc << 3) | 1)); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 1, introspectionData, (((usqInt)actualMcpc << 3) | 1)); + introspectionDataIndex += 2; + } + return 0; +} + + +/* If there is a generator for the current primitive then answer it; + otherwise answer nil. */ + + /* SimpleStackBasedCogit>>#primitiveGeneratorOrNil */ +static PrimitiveDescriptor * +primitiveGeneratorOrNil(void) +{ + PrimitiveDescriptor *primitiveDescriptor; + + if (isQuickPrimitiveIndex(primitiveIndex)) { + /* an unused one */ + primitiveDescriptor = (&(primitiveGeneratorTable[0])); + (primitiveDescriptor->primitiveGenerator = quickPrimitiveGeneratorFor(primitiveIndex)); + return primitiveDescriptor; + } + if (((primitiveIndex >= 1) && (primitiveIndex <= MaxCompiledPrimitiveIndex))) { + /* for debugging, allow excluding specific primitives */ + return (&(primitiveGeneratorTable[primitiveIndex])); + } + return null; +} + + /* SimpleStackBasedCogit>>#register:isInMask: */ +static NoDbgRegParms int +registerisInMask(sqInt reg, sqInt mask) +{ + return ((mask & (((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)))) != 0); +} + + /* SimpleStackBasedCogit>>#register:isNotInMask: */ +static NoDbgRegParms int +registerisNotInMask(sqInt reg, sqInt mask) +{ + return (!(mask & (((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg))))); +} + + +/* Add a blockStart for an embedded block. For a binary tree walk block + dispatch blocks must be compiled in pc/depth-first order but are scanned + in breadth-first + order, so do an insertion sort (which of course is really a bubble sort + because we + have to move everything higher to make room). */ + + /* StackToRegisterMappingCogit>>#addBlockStartAt:numArgs:numCopied:span: */ +static NoDbgRegParms BlockStart * +addBlockStartAtnumArgsnumCopiedspan(sqInt bytecodepc, sqInt numArgs, sqInt numCopied, sqInt span) +{ + BlockStart *blockStart; + sqInt i; + sqInt j; + + + /* Transcript ensureCr; nextPutAll: 'addBlockStartAt: '; print: bytecodepc; cr; flush. */ + if (blockCount > 0) { + i = blockCount - 1; + while (1) { + blockStart = (&(blockStarts[i])); + + /* check for repeat addition during recompilation due to initialNil miscount. */ + if (((blockStart->startpc)) == bytecodepc) { + return blockStart; + } + if (!((((blockStart->startpc)) > bytecodepc) + && (i > 0))) break; + i -= 1; + } + for (j = blockCount; j >= (i + 1); j += -1) { + blockStarts[j] = (blockStarts[j - 1]); + } + blockStart = (&(blockStarts[i + 1])); + } + else { + blockStart = (&(blockStarts[blockCount])); + } + blockCount += 1; + (blockStart->startpc = bytecodepc); + (blockStart->numArgs = numArgs); + (blockStart->numCopied = numCopied); + (blockStart->numInitialNils = 0); + (blockStart->stackCheckLabel = null); + (blockStart->hasInstVarRef = 0); + (blockStart->span = span); + return blockStart; +} + + +/* e.g. Receiver Receiver or Receiver Receiver (RISC) + Selector/Arg0 => Arg1 Selector/Arg0 => Arg1 + Arg1 Arg2 Arg1 Arg2 + Arg2 Arg3 Arg2 sp-> Arg3 + Arg3 sp-> retpc sp-> Arg3 + sp-> retpc */ +/* Generate code to adjust the possibly stacked arguments immediately + before jumping to a method looked up by a perform primitive. */ + + /* StackToRegisterMappingCogit>>#adjustArgumentsForPerform: */ +static NoDbgRegParms void +adjustArgumentsForPerform(sqInt numArgs) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *anInstruction1; + sqInt index; + sqInt offset; + sqInt quickConstant; + + assert((numRegArgs()) <= 2); + assert(numArgs >= 1); + if (numArgs <= (numRegArgs())) { + if (numArgs == 2) { + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, Arg0Reg); + } + return; + } + + /* If the arity is one more than the max numRegArgs, the receiver and all arguments have to be removed from the stack. */ + if (((numRegArgs()) + 1) == numArgs) { + if ((numRegArgs()) == 2) { + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, 0, SPReg, Arg1Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, BytesPerWord, SPReg, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BytesPerWord, BytesPerOop)); + } + } + else { + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, 0, SPReg, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + } + quickConstant = (numArgs + 1) * BytesPerWord; + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, SPReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + return; + } + + /* e.g. Receiver Receiver + Selector/Arg0 => Arg1 + Arg1 Arg2 + Arg2 sp-> Arg3 + sp-> Arg3 */ + for (index = (numArgs - 2); index >= 0; index += -1) { + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, index * BytesPerWord, SPReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(index * BytesPerWord, BytesPerOop)); + } + offset = (index + 1) * BytesPerWord; + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction1 = genoperandoperandoperand(MoveRMwr, TempReg, offset, SPReg); + if (usesOutOfLineLiteral(anInstruction1)) { + (anInstruction1->dependent = locateLiteralsize(offset, BytesPerOop)); + } + } + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, BytesPerWord, SPReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BytesPerWord, BytesPerOop)); + } +} + + +/* If the stack entry is already in a register not conflicting with regMask, + answers it, + else allocate a new register not conflicting with reg mask + */ + + /* StackToRegisterMappingCogit>>#allocateRegForStackEntryAt:notConflictingWith: */ +static NoDbgRegParms sqInt +allocateRegForStackEntryAtnotConflictingWith(sqInt index, sqInt regMask) +{ + sqInt mask; + SimStackEntry *stackEntry; + + /* begin ssValue: */ + stackEntry = simStackAt(simStackPtr - index); + mask = registerMaskOrNone(stackEntry); + if ((mask != 0) + && ((!(mask & regMask)))) { + /* When one does pushDup on a SSRegister + followed by an operation reusing the register + but mutating the value of the register, then the value that was + dup is now refering to the register with a mutated value which + is incorrect. This problem is serious as it can happen also with + multiple unsafe instructions (no dup involved) + This is not a problem if the operation reusing the register is not + mutating the value of if all the dup values are used in the same + operation. + => I think we should introduce allocateRegMutatingStackEntryAt: + notConflictingWith:upThrough:, used by operations mutating the reg + value and flushing partially the stack if somewhere between simSpill + and the ssEntries used by the operation someone uses also the same + register. */ + return registerOrNone(stackEntry); + } + return allocateRegNotConflictingWith(regMask); +} + + +/* if there's a free register, use it */ + + /* StackToRegisterMappingCogit>>#allocateRegNotConflictingWith: */ +static NoDbgRegParms sqInt +allocateRegNotConflictingWith(sqInt regMask) +{ + sqInt reg; + + reg = availableRegisterOrNoneFor(backEnd, (liveRegisters()) | regMask); + if (reg == NoReg) { + reg = freeAnyRegNotConflictingWith(regMask); + } + + /* No free register, choose one that does not conflict with regMask */ + if (reg == ReceiverResultReg) { + voidReceiverResultRegContainsSelf(); + } + + /* If we've allocated RcvrResultReg, it's not live anymore */ + return reg; +} + + /* StackToRegisterMappingCogit>>#anyReferencesToRegister:inTopNItems: */ +static NoDbgRegParms sqInt +anyReferencesToRegisterinTopNItems(sqInt reg, sqInt n) +{ + sqInt i; + sqInt regMask; + + regMask = ((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)); + for (i = simStackPtr; i >= ((simStackPtr - n) + 1); i += -1) { + if ((((registerMask(simStackAt(i))) & regMask) != 0)) { + return 1; + } + } + return 0; +} + + +/* This is a static version of ceCallCogCodePopReceiverArg0Regs + for break-pointing when debugging in C. */ +/* This exists only for break-pointing. */ + + /* StackToRegisterMappingCogit>>#callCogCodePopReceiverArg0Regs */ +void +callCogCodePopReceiverArg0Regs(void) +{ + realCECallCogCodePopReceiverArg0Regs(); +} + + +/* This is a static version of ceCallCogCodePopReceiverArg1Arg0Regs + for break-pointing when debugging in C. */ +/* This exists only for break-pointing. */ + + /* StackToRegisterMappingCogit>>#callCogCodePopReceiverArg1Arg0Regs */ +void +callCogCodePopReceiverArg1Arg0Regs(void) +{ + realCECallCogCodePopReceiverArg1Arg0Regs(); +} + + /* StackToRegisterMappingCogit>>#compileBlockBodies */ +static sqInt +compileBlockBodies(void) +{ + BlockStart *blockStart; + sqInt compiledBlocksCount; + sqInt initialCounterIndex; + sqInt initialOpcodeIndex; + sqInt initialStackPtr; + sqInt (* const pushNilSizeFunction)(sqInt,sqInt) = squeakV3orSistaV1PushNilSizenumInitialNils; + sqInt result; + sqInt savedFirstOpcodeIndex; + sqInt savedLastDumpedLiteralIndex; + unsigned char savedNeedsFrame; + sqInt savedNextLiteralIndex; + sqInt savedNumArgs; + sqInt savedNumTemps; + + initialStackPtr = 0; + assert(blockCount > 0); + + /* scanBlock: in compileBlockEntry: sets both of these appropriately for each block. */ + savedNeedsFrame = needsFrame; + savedNumArgs = methodOrBlockNumArgs; + savedNumTemps = methodOrBlockNumTemps; + inBlock = InVanillaBlock; + compiledBlocksCount = 0; + while (compiledBlocksCount < blockCount) { + compilationPass = 1; + blockStart = blockStartAt(compiledBlocksCount); + if (((result = scanBlock(blockStart))) < 0) { + return result; + } + initialOpcodeIndex = opcodeIndex; + + /* for SistaCogit */ + initialCounterIndex = 0 /* maybeCounterIndex */; + + /* begin saveForRecompile */ + savedFirstOpcodeIndex = firstOpcodeIndex; + savedNextLiteralIndex = nextLiteralIndex; + savedLastDumpedLiteralIndex = lastDumpedLiteralIndex; + while (1) { + compileBlockEntry(blockStart); + initialStackPtr = simStackPtr; + if (((result = compileBytecodesFromthrough(((blockStart->startpc)) + (pushNilSizeFunction(methodObj, ((blockStart->numInitialNils)))), (((blockStart->startpc)) + ((blockStart->span))) - 1))) < 0) { + return result; + } + + /* If the final simStackPtr is less than the initial simStackPtr then scanBlock: over- + estimated the number of initial nils (because it assumed one or more pushNils to + produce an operand were pushNils to initialize temps. This is very rare, so + compensate by checking, adjusting numInitialNils and recompiling the block body. + N.B. No need to reinitialize the literalsManager because it answers existing literals. */ + if (initialStackPtr == simStackPtr) break; + assert((initialStackPtr > simStackPtr) + || (deadCode)); + + /* for asserts */ + compilationPass += 1; + (blockStart->numInitialNils = (((blockStart->numInitialNils)) + simStackPtr) - initialStackPtr); + (((blockStart->fakeHeader))->dependent = null); + reinitializeFixupsFromthrough(((blockStart->startpc)) + ((blockStart->numInitialNils)), (((blockStart->startpc)) + ((blockStart->span))) - 1); + bzero(abstractOpcodes + initialOpcodeIndex, + (opcodeIndex - initialOpcodeIndex) * sizeof(AbstractInstruction)); + opcodeIndex = initialOpcodeIndex; + + /* begin resetForRecompile */ + firstOpcodeIndex = savedFirstOpcodeIndex; + nextLiteralIndex = savedNextLiteralIndex; + lastDumpedLiteralIndex = savedLastDumpedLiteralIndex; + } + compiledBlocksCount += 1; + } + needsFrame = savedNeedsFrame; + methodOrBlockNumArgs = savedNumArgs; + methodOrBlockNumTemps = savedNumTemps; + return 0; +} + + +/* Build a frame for a block activation. See CoInterpreter + class>>initializeFrameIndices. closure (in ReceiverResultReg) + arg0 + ... + argN + caller's saved ip/this stackPage (for a base frame) + fp-> saved fp + method + context (uninitialized?) + receiver + first temp + ... + sp-> Nth temp + Avoid use of SendNumArgsReg which is the flag determining whether + context switch is allowed on stack-overflow. */ +/* Build a frame for a block activation. See CoInterpreter + class>>initializeFrameIndices. Override to push the register receiver and + register arguments, if any, and to correctly + initialize the explicitly nilled/pushed temp entries (they are /not/ of + type constant nil). */ + + /* StackToRegisterMappingCogit>>#compileBlockFrameBuild: */ +static NoDbgRegParms void +compileBlockFrameBuild(BlockStart *blockStart) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + AbstractInstruction *cascade0; + sqInt i; + sqInt ign; + + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + + /* PushR: */ + genoperand(PushR, LinkReg); + + /* PushR: */ + genoperand(PushR, FPReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SPReg, FPReg); + + /* Think of ClassReg as ClosureReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, ClassReg); + + /* The block method field must have its MFMethodFlagIsBlockFlag bit set. + We arrange this using a labelOffset. A hack, but it works. */ + cascade0 = (blockStart->fakeHeader); + addDependent(cascade0, annotateAbsolutePCRef(checkLiteralforInstruction(((sqInt)((blockStart->fakeHeader))), genoperand(PushCw, ((sqInt)((blockStart->fakeHeader))))))); + + /* setLabelOffset: */ + ((cascade0->operands))[1] = MFMethodFlagIsBlockFlag; + + /* method */ + + /* begin genPushConstant: */ + /* begin PushCq: */ + /* begin gen:quickConstant: */ + anInstruction = genoperand(PushCq, nilObject()); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(nilObject(), BytesPerOop)); + } + + /* Fetch home receiver from outer context. closure is on stack and initially in ReceiverResultReg. + It is safe to use Arg0Reg because reg args are pushed by the value primitives if there are any. */ + if ((blockStart->hasInstVarRef)) { + genLoadSlotsourceRegdestReg(ClosureOuterContextIndex, ClassReg, ReceiverResultReg); + genLoadSlotsourceRegdestReg(ReceiverIndex, ReceiverResultReg, Arg0Reg); + genEnsureOopInRegNotForwardedscratchRegupdatingSlotin(Arg0Reg, TempReg, ReceiverIndex, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ReceiverResultReg); + } + else { + genLoadSlotsourceRegdestReg(ClosureOuterContextIndex, ClassReg, Arg0Reg); + genLoadSlotsourceRegdestReg(ReceiverIndex, Arg0Reg, ReceiverResultReg); + } + + /* Use ReceiverResultReg for Context to agree with store check trampoline */ + + /* PushR: */ + genoperand(PushR, ReceiverResultReg); + for (i = 0; i < ((blockStart->numCopied)); i += 1) { + genLoadSlotsourceRegdestReg(i + ClosureFirstCopiedValueIndex, ClassReg, TempReg); + + /* PushR: */ + genoperand(PushR, TempReg); + } + (blockStart->stackCheckLabel = compileStackOverflowCheck(1)); + methodOrBlockNumTemps = (((blockStart->numArgs)) + ((blockStart->numCopied))) + ((blockStart->numInitialNils)); + initSimStackForFramefulMethod((blockStart->startpc)); + if (((blockStart->numInitialNils)) > 0) { + if (((blockStart->numInitialNils)) > 1) { + /* begin genMoveConstant:R: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, nilObject(), TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(nilObject(), BytesPerOop)); + } + for (ign = 1; ign <= ((blockStart->numInitialNils)); ign += 1) { + /* PushR: */ + genoperand(PushR, TempReg); + } + } + else { + /* begin genPushConstant: */ + /* begin PushCq: */ + /* begin gen:quickConstant: */ + anInstruction = genoperand(PushCq, nilObject()); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(nilObject(), BytesPerOop)); + } + } + } +} + + +/* Make sure ReceiverResultReg holds the receiver, loaded from the closure, + which is what is initially in ReceiverResultReg. We must annotate the + first instruction in vanilla blocks so that + findMethodForStartBcpc:inHomeMethod: can function. We need two annotations + because the first is a fiducial. */ +/* Make sure ReceiverResultReg holds the receiver, loaded from + the closure, which is what is initially in ReceiverResultReg */ + + /* StackToRegisterMappingCogit>>#compileBlockFramelessEntry: */ +static NoDbgRegParms void +compileBlockFramelessEntry(BlockStart *blockStart) +{ + AbstractInstruction *abstractInstruction; + + methodOrBlockNumTemps = (((blockStart->numArgs)) + ((blockStart->numCopied))) + ((blockStart->numInitialNils)); + initSimStackForFramelessBlock((blockStart->startpc)); + if ((blockStart->entryLabel)) { + abstractInstruction = (blockStart->entryLabel); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + } + if ((blockStart->hasInstVarRef)) { + genLoadSlotsourceRegdestReg(ClosureOuterContextIndex, ReceiverResultReg, ReceiverResultReg); + genLoadSlotsourceRegdestReg(ReceiverIndex, ReceiverResultReg, Arg0Reg); + genEnsureOopInRegNotForwardedscratchRegupdatingSlotin(Arg0Reg, TempReg, ReceiverIndex, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ReceiverResultReg); + } + else { + genLoadSlotsourceRegdestReg(ClosureOuterContextIndex, ReceiverResultReg, TempReg); + genLoadSlotsourceRegdestReg(ReceiverIndex, TempReg, ReceiverResultReg); + } +} + + +/* Loop over bytecodes, dispatching to the generator for each bytecode, + handling fixups in due course. + */ + + /* StackToRegisterMappingCogit>>#compileBytecodesFrom:through: */ +static NoDbgRegParms sqInt +compileBytecodesFromthrough(sqInt start, sqInt end) +{ + BytecodeDescriptor *descriptor; + BytecodeFixup *fixup; + sqInt nExts; + sqInt nextOpcodeIndex; + sqInt result; + sqInt theOpcodeIndex; + + traceSimStack(); + bytecodePC = start; + nExts = (result = 0); + descriptor = null; + deadCode = 0; + while (1) { + maybeHaltIfDebugPC(); + mergeWithFixupIfRequired((fixup = fixupAt(bytecodePC))); + descriptor = loadBytesAndGetDescriptor(bytecodePC); + nextOpcodeIndex = opcodeIndex; + result = (deadCode + ? mapDeadDescriptorIfNeeded(descriptor) + : ((descriptor->generator))()); + if (!result) { + /* begin assertExtsAreConsumed: */ + if (!((descriptor->isExtension))) { + assert((extA == 0) + && ((extB == 0) + && (numExtB == 0))); + } + } + traceDescriptor(descriptor); + traceSimStack(); + + /* begin patchFixupTargetIfNeeded:nextOpcodeIndex: */ + if ((((((usqInt)((fixup->targetInstruction)))) >= NeedsNonMergeFixupFlag) && ((((usqInt)((fixup->targetInstruction)))) <= NeedsMergeFixupFlag))) { + /* There is a fixup for this bytecode. It must point to the first generated + instruction for this bytecode. If there isn't one we need to add a label. */ + if (opcodeIndex == nextOpcodeIndex) { + /* Label */ + genoperandoperand(Label, (labelCounter += 1), bytecodePC); + } + (fixup->targetInstruction = abstractInstructionAt(nextOpcodeIndex)); + } + theOpcodeIndex = opcodeIndex; + + /* begin maybeDumpLiterals:opcodeIndex: */ + if ((/* mustDumpLiterals: */ + (theOpcodeIndex >= firstOpcodeIndex) + && ((theOpcodeIndex - firstOpcodeIndex) >= (outOfLineLiteralOpcodeLimit(backEnd)))) + || ((/* isUnconditionalBranch */ + (isBranch(descriptor)) + && (!(((descriptor->isBranchTrue)) + || ((descriptor->isBranchFalse))))) + || ((descriptor->isReturn)))) { + dumpLiterals(!((/* isUnconditionalBranch */ + (isBranch(descriptor)) + && (!(((descriptor->isBranchTrue)) + || ((descriptor->isBranchFalse))))) + || ((descriptor->isReturn)))); + } + bytecodePC = (bytecodePC + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bytecodePC, nExts, methodObj) + : 0)); + if (!((result == 0) + && (bytecodePC <= end))) break; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + + /* begin checkEnoughOpcodes */ + if (opcodeIndex > numAbstractOpcodes) { + error("Cog JIT internal error. Too many abstract opcodes. Num opcodes heuristic is too optimistic."); + } + return result; +} + + /* StackToRegisterMappingCogit>>#compileCogFullBlockMethod: */ +static NoDbgRegParms CogMethod * +compileCogFullBlockMethod(sqInt numCopied) +{ + usqIntptr_t allocBytes; + usqIntptr_t fixupBytes; + sqInt numBlocks; + sqInt numBytecodes; + sqInt numCleanBlocks; + usqIntptr_t opcodeBytes; + sqInt result; + + methodOrBlockNumTemps = tempCountOf(methodObj); + setHasMovableLiteral(0); + setHasYoungReferent(isYoungObject(methodObj)); + methodOrBlockNumArgs = argumentCountOf(methodObj); + inBlock = InFullBlock; + maxLitIndex = -1; + assert((primitiveIndexOf(methodObj)) == 0); + initialPC = startPCOfMethod(methodObj); + + /* initial estimate. Actual endPC is determined in scanMethod. */ + endPC = numBytesOf(methodObj); + numBytecodes = (endPC - initialPC) + 1; + primitiveIndex = 0; + + /* begin allocateOpcodes:bytecodes:ifFail: */ + numAbstractOpcodes = (numBytecodes + 10) * 10 /* estimateOfAbstractOpcodesPerBytecodes */; + opcodeBytes = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupBytes = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + allocBytes = opcodeBytes + fixupBytes; + + /* Document the fact that the MaxStackAllocSize ensures that the number of abstract + opcodes fits in a 16 bit integer (e.g. CogBytecodeFixup's instructionIndex). */ + assert((((sizeof(CogAbstractInstruction)) + (sizeof(CogBytecodeFixup))) * 0xC000) > MaxStackAllocSize); + if (allocBytes > MaxStackAllocSize) { + return ((CogMethod *) MethodTooBig); + } + abstractOpcodes = alloca(allocBytes); + bzero(abstractOpcodes, allocBytes); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeBytes)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + firstOpcodeIndex = 0x10000; + nextLiteralIndex = (lastDumpedLiteralIndex = 0); + labelCounter = 0; + /* end allocateOpcodes:bytecodes:ifFail: */ +l1: + if (((numBlocks = scanMethod())) < 0) { + return ((CogMethod *) numBlocks); + } + assert(numBlocks == 0); + numCleanBlocks = scanForCleanBlocks(); + assert(numCleanBlocks == 0); + allocateBlockStarts(numBlocks + numCleanBlocks); + blockCount = 0; + if (numCleanBlocks > 0) { + addCleanBlockStarts(); + } + + /* Inaccurate error code, but it'll do. This will likely never fail. */ + blockEntryLabel = null; + (methodLabel->dependent = null); + if (((result = compileEntireFullBlockMethod(numCopied))) < 0) { + return ((CogMethod *) result); + } + return generateCogFullBlock(); +} + + /* StackToRegisterMappingCogit>>#compileCogMethod: */ +static NoDbgRegParms CogMethod * +compileCogMethod(sqInt selector) +{ + usqIntptr_t allocBytes; + int extra; + usqIntptr_t fixupBytes; + sqInt methodFoundInvalidPostScanRV; + sqInt numBlocks; + sqInt numBytecodes; + sqInt numCleanBlocks; + usqIntptr_t opcodeBytes; + sqInt result; + + methodOrBlockNumTemps = tempCountOf(methodObj); + setHasMovableLiteral(0); + setHasYoungReferent((isYoungObject(methodObj)) + || (isYoung(selector))); + methodOrBlockNumArgs = argumentCountOf(methodObj); + inBlock = 0; + maxLitIndex = -1; + extra = ((((primitiveIndex = primitiveIndexOf(methodObj))) > 0) + && (!(isQuickPrimitiveIndex(primitiveIndex))) + ? 30 + : 10); + initialPC = startPCOfMethod(methodObj); + + /* initial estimate. Actual endPC is determined in scanMethod. */ + endPC = (isQuickPrimitiveIndex(primitiveIndex) + ? initialPC - 1 + : numBytesOf(methodObj)); + numBytecodes = (endPC - initialPC) + 1; + + /* begin allocateOpcodes:bytecodes:ifFail: */ + numAbstractOpcodes = (numBytecodes + extra) * 10 /* estimateOfAbstractOpcodesPerBytecodes */; + opcodeBytes = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupBytes = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + allocBytes = opcodeBytes + fixupBytes; + + /* Document the fact that the MaxStackAllocSize ensures that the number of abstract + opcodes fits in a 16 bit integer (e.g. CogBytecodeFixup's instructionIndex). */ + assert((((sizeof(CogAbstractInstruction)) + (sizeof(CogBytecodeFixup))) * 0xC000) > MaxStackAllocSize); + if (allocBytes > MaxStackAllocSize) { + return ((CogMethod *) MethodTooBig); + } + abstractOpcodes = alloca(allocBytes); + bzero(abstractOpcodes, allocBytes); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeBytes)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + firstOpcodeIndex = 0x10000; + nextLiteralIndex = (lastDumpedLiteralIndex = 0); + labelCounter = 0; + /* end allocateOpcodes:bytecodes:ifFail: */ +l1: + if (((numBlocks = scanMethod())) < 0) { + return ((CogMethod *) numBlocks); + } + numCleanBlocks = scanForCleanBlocks(); + + /* begin methodFoundInvalidPostScan */ + if (!needsFrame) { + methodFoundInvalidPostScanRV = methodOrBlockNumTemps > methodOrBlockNumArgs; + goto l2; + } + methodFoundInvalidPostScanRV = 0; + /* end methodFoundInvalidPostScan */ +l2: + if (methodFoundInvalidPostScanRV) { + return ((CogMethod *) ShouldNotJIT); + } + allocateBlockStarts(numBlocks + numCleanBlocks); + blockCount = 0; + if (numCleanBlocks > 0) { + addCleanBlockStarts(); + } + + /* Inaccurate error code, but it'll do. This will likely never fail. */ + blockEntryLabel = null; + (methodLabel->dependent = null); + if (((result = compileEntireMethod())) < 0) { + return ((CogMethod *) result); + } + return generateCogMethod(selector); +} + + +/* Compile the abstract instructions for the entire method, including blocks. */ +/* Compile the abstract instructions for the entire method, including blocks. */ + + /* StackToRegisterMappingCogit>>#compileEntireMethod */ +static sqInt +compileEntireMethod(void) +{ + sqInt result; + + regArgsHaveBeenPushed = 0; + + /* begin preenMethodLabel */ + /* setLabelOffset: */ + ((methodLabel->operands))[1] = 0; + compileAbort(); + compileEntry(); + if (((result = compilePrimitive())) < 0) { + return result; + } + compileFrameBuild(); + if (((result = compileMethodBody())) < 0) { + return result; + } + if (!blockCount) { + return 0; + } + if (((result = compileBlockBodies())) < 0) { + return result; + } + return compileBlockDispatch(); +} + + +/* Build a frame for a CogMethod activation. See CoInterpreter + class>>initializeFrameIndices. receiver (in ReceiverResultReg) + arg0 + ... + argN + caller's saved ip/this stackPage (for a base frame) + fp-> saved fp + method + context (uninitialized?) + receiver + first temp + ... + sp-> Nth temp + If there is a primitive and an error code the Nth temp is the error code. + Ensure SendNumArgsReg is set early on (incidentally to nilObj) because + it is the flag determining whether context switch is allowed on + stack-overflow. */ +/* Build a frame for a CogMethod activation. See CoInterpreter + class>>initializeFrameIndices. Override to push the register receiver and + register arguments, if any. */ + + /* StackToRegisterMappingCogit>>#compileFrameBuild */ +static void +compileFrameBuild(void) +{ + AbstractInstruction *anInstruction; + sqInt i; + sqInt toDoLimit; + + +# if IMMUTABILITY + if (useTwoPaths) { + compileTwoPathFrameBuild(); + return; + } +# endif + + if (!needsFrame) { + if (useTwoPaths) { + compileTwoPathFramelessInit(); + } + initSimStackForFramelessMethod(initialPC); + return; + } + assert(!(useTwoPaths)); + genPushRegisterArgs(); + if (!needsFrame) { + return; + } + + /* PushR: */ + genoperand(PushR, LinkReg); + + /* PushR: */ + genoperand(PushR, FPReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SPReg, FPReg); + addDependent(methodLabel, annotateAbsolutePCRef(checkLiteralforInstruction(((sqInt)methodLabel), genoperand(PushCw, ((sqInt)methodLabel))))); + + /* begin genMoveConstant:R: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, nilObject(), SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(nilObject(), BytesPerOop)); + } + + /* PushR: */ + genoperand(PushR, SendNumArgsReg); + + /* PushR: */ + genoperand(PushR, ReceiverResultReg); + toDoLimit = temporaryCountOfMethodHeader(methodHeader); + for (i = (methodOrBlockNumArgs + 1); i <= toDoLimit; i += 1) { + /* PushR: */ + genoperand(PushR, SendNumArgsReg); + } + if (/* methodUsesPrimitiveErrorCode:header: */ + ((primitiveIndexOfMethodheader(methodObj, methodHeader)) > 0) + && ((longStoreBytecodeForHeader(methodHeader)) == (fetchByteofObject((startPCOfMethodHeader(methodHeader)) + (sizeOfCallPrimitiveBytecode(methodHeader)), methodObj)))) { + compileGetErrorCode(); + } + stackCheckLabel = compileStackOverflowCheck(canContextSwitchIfActivatingheader(methodObj, methodHeader)); + initSimStackForFramefulMethod(initialPC); +} + + +/* Make sure ReceiverResultReg holds the receiver, loaded from the closure, + which is what is initially in ReceiverResultReg. */ +/* Make sure ReceiverResultReg holds the receiver, loaded from + the closure, which is what is initially in ReceiverResultReg */ + + /* StackToRegisterMappingCogit>>#compileFullBlockFramelessEntry: */ +static NoDbgRegParms void +compileFullBlockFramelessEntry(sqInt numCopied) +{ + initSimStackForFramelessBlock(initialPC); + genLoadSlotsourceRegdestReg(FullClosureReceiverIndex, ReceiverResultReg, Arg0Reg); + genEnsureOopInRegNotForwardedscratchRegupdatingSlotin(Arg0Reg, TempReg, FullClosureReceiverIndex, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ReceiverResultReg); +} + + +/* Build a frame for a block activation. See CoInterpreter + class>>initializeFrameIndices. closure (in ReceiverResultReg) + arg0 + ... + argN + caller's saved ip/this stackPage (for a base frame) + fp-> saved fp + method + context (uninitialized?) + receiver + first temp + ... + sp-> Nth temp + Avoid use of SendNumArgsReg which is the flag determining whether + context switch is allowed on stack-overflow. */ + + /* StackToRegisterMappingCogit>>#compileFullBlockMethodFrameBuild: */ +static NoDbgRegParms void +compileFullBlockMethodFrameBuild(sqInt numCopied) +{ + AbstractInstruction *anInstruction; + sqInt i; + sqInt toDoLimit; + + if (useTwoPaths) { + /* method with only inst var store, we compile only slow path for now */ + useTwoPaths = 0; +# if IMMUTABILITY + needsFrame = 1; +# endif + + } + if (!needsFrame) { + /* it is OK for numCopied to be non-zero provided that the block does not actually use the copied values. + There are some blocks like this, e.g. that simply reference copied values to mark them as used for Slang. + See e.g. CroquetPlugin>>#primitiveGatherEntropy which contains the block [bufPtr. bufSize. false], + which the bytecode compiler optimizes to [false]. */ + compileFullBlockFramelessEntry(numCopied); + initSimStackForFramelessBlock(initialPC); + return; + } + if (!needsFrame) { + return; + } + + /* PushR: */ + genoperand(PushR, LinkReg); + + /* PushR: */ + genoperand(PushR, FPReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SPReg, FPReg); + + /* Think of ClassReg as ClosureReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, ClassReg); + + /* The block method field must have its MFMethodFlagIsBlockFlag bit set. + We arrange this using a labelOffset. A hack, but it works. */ + addDependent(methodLabel, annotateAbsolutePCRef(checkLiteralforInstruction(((sqInt)methodLabel), genoperand(PushCw, ((sqInt)methodLabel))))); + + /* setLabelOffset: */ + ((methodLabel->operands))[1] = MFMethodFlagIsBlockFlag; + + /* method */ + + /* begin genMoveConstant:R: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, nilObject(), SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(nilObject(), BytesPerOop)); + } + + /* PushR: */ + genoperand(PushR, SendNumArgsReg); + genLoadSlotsourceRegdestReg(FullClosureReceiverIndex, ClassReg, Arg0Reg); + genEnsureOopInRegNotForwardedscratchRegupdatingSlotin(Arg0Reg, TempReg, FullClosureReceiverIndex, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ReceiverResultReg); + + /* PushR: */ + genoperand(PushR, ReceiverResultReg); + for (i = 0; i < numCopied; i += 1) { + genLoadSlotsourceRegdestReg(i + FullClosureFirstCopiedValueIndex, ClassReg, TempReg); + + /* PushR: */ + genoperand(PushR, TempReg); + } + + /* Push slots for temps */ + toDoLimit = temporaryCountOfMethodHeader(methodHeader); + for (i = ((methodOrBlockNumArgs + numCopied) + 1); i <= toDoLimit; i += 1) { + /* PushR: */ + genoperand(PushR, SendNumArgsReg); + } + stackCheckLabel = compileStackOverflowCheck(1); + initSimStackForFramefulMethod(initialPC); +} + + +/* Build a frame for a CogMethod activation. See CoInterpreter + class>>initializeFrameIndices. receiver (in ReceiverResultReg) + arg0 + ... + argN + caller's saved ip/this stackPage (for a base frame) + fp-> saved fp + method + context (uninitialized?) + receiver + first temp + ... + sp-> Nth temp + If there is a primitive and an error code the Nth temp is the error code. + Ensure SendNumArgsReg is set early on (incidentally to nilObj) because + it is the flag determining whether context switch is allowed on + stack-overflow. */ +/* We are in a method where the frame is needed *only* for instance variable + store, typically a setter method. + This case has 20% overhead with Immutability compared to setter without + immutability because of the stack + frame creation. We compile two path, one where the object is immutable, + one where it isn't. At the beginning + of the frame build, we take one path or the other depending on the + receiver mutability. + + Note: this specific case happens only where there are only instance + variabel stores. We could do something + similar for literal variable stores, but we don't as it's too uncommon. + */ + + /* StackToRegisterMappingCogit>>#compileTwoPathFrameBuild */ +#if IMMUTABILITY +static void +compileTwoPathFrameBuild(void) +{ + AbstractInstruction *anInstruction; + sqInt i; + AbstractInstruction *jumpImmutable; + AbstractInstruction *jumpOld; + sqInt quickConstant; + sqInt toDoLimit; + + assert(useTwoPaths); + assert(blockCount == 0); + jumpImmutable = genJumpImmutablescratchReg(ReceiverResultReg, TempReg); + + /* begin genJumpInOldSpace: */ + quickConstant = storeCheckBoundary(); + anInstruction = genoperandoperand(CmpCqR, quickConstant, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpOld = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* first path. The receiver is mutable */ + assert(!needsFrame); + initSimStackForFramelessMethod(initialPC); + + /* begin compileMethodBody */ + if (endPC < initialPC) { + goto l1; + } + compileBytecodesFromthrough(initialPC + (deltaToSkipPrimAndErrorStoreInheader(methodObj, methodHeader)), endPC); + /* end compileMethodBody */ +l1: + + /* second path. The receiver is mutable */ + + /* reset because it impacts inst var store compilation */ + useTwoPaths = 0; + needsFrame = 1; + jmpTarget(jumpOld, jmpTarget(jumpImmutable, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + genPushRegisterArgs(); + if (!needsFrame) { + return; + } + + /* PushR: */ + genoperand(PushR, LinkReg); + + /* PushR: */ + genoperand(PushR, FPReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SPReg, FPReg); + addDependent(methodLabel, annotateAbsolutePCRef(checkLiteralforInstruction(((sqInt)methodLabel), genoperand(PushCw, ((sqInt)methodLabel))))); + + /* begin genMoveConstant:R: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, nilObject(), SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(nilObject(), BytesPerOop)); + } + + /* PushR: */ + genoperand(PushR, SendNumArgsReg); + + /* PushR: */ + genoperand(PushR, ReceiverResultReg); + toDoLimit = temporaryCountOfMethodHeader(methodHeader); + for (i = (methodOrBlockNumArgs + 1); i <= toDoLimit; i += 1) { + /* PushR: */ + genoperand(PushR, SendNumArgsReg); + } + if (/* methodUsesPrimitiveErrorCode:header: */ + ((primitiveIndexOfMethodheader(methodObj, methodHeader)) > 0) + && ((longStoreBytecodeForHeader(methodHeader)) == (fetchByteofObject((startPCOfMethodHeader(methodHeader)) + (sizeOfCallPrimitiveBytecode(methodHeader)), methodObj)))) { + compileGetErrorCode(); + } + stackCheckLabel = compileStackOverflowCheck(canContextSwitchIfActivatingheader(methodObj, methodHeader)); + initSimStackForFramefulMethod(initialPC); +} +#endif /* IMMUTABILITY */ + + +/* We are in a frameless method with at least two inst var stores. We compile + two paths, + one where the object is in new space, and one where it isn't. At the + beginning + of the method, we take one path or the other depending on the receiver + being in newSpace. + */ + + /* StackToRegisterMappingCogit>>#compileTwoPathFramelessInit */ +static void +compileTwoPathFramelessInit(void) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpOld; + sqInt quickConstant; + + assert(!(IMMUTABILITY)); + assert(!(needsFrame)); + assert(useTwoPaths); + + /* begin genJumpInOldSpace: */ + quickConstant = storeCheckBoundary(); + anInstruction = genoperandoperand(CmpCqR, quickConstant, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpOld = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* first path. The receiver is young */ + initSimStackForFramelessMethod(initialPC); + + /* begin compileMethodBody */ + if (endPC < initialPC) { + goto l1; + } + compileBytecodesFromthrough(initialPC + (deltaToSkipPrimAndErrorStoreInheader(methodObj, methodHeader)), endPC); + /* end compileMethodBody */ +l1: + + /* second path. The receiver is old */ + + /* reset because it impacts inst var store compilation */ + useTwoPaths = 0; + jmpTarget(jumpOld, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); +} + + /* StackToRegisterMappingCogit>>#cPICMissTrampolineFor: */ +static NoDbgRegParms sqInt +cPICMissTrampolineFor(sqInt numArgs) +{ + return picMissTrampolines[((numArgs < ((numRegArgs()) + 1)) ? numArgs : ((numRegArgs()) + 1))]; +} + + +/* Replaces the Blue Book double-extended send [132], in which the first byte + was wasted on 8 bits of argument count. + Here we use 3 bits for the operation sub-type (opType), and the remaining + 5 bits for argument count where needed. + The last byte give access to 256 instVars or literals. + See also secondExtendedSendBytecode + */ + + /* StackToRegisterMappingCogit>>#doubleExtendedDoAnythingBytecode */ +static sqInt +doubleExtendedDoAnythingBytecode(void) +{ + AbstractInstruction *abstractInstruction; + sqInt opType; + + opType = ((usqInt)(byte1)) >> 5; + if (!opType) { + return genSendnumArgs(byte2, byte1 & 0x1F); + } + if (opType == 1) { + return genSendSupernumArgs(byte2, byte1 & 0x1F); + } + + /* We need a map entry for this bytecode for correct parsing. + The sends will get an IsSend entry anyway. The other cases need a fake one. */ + switch (opType) { + case 2: + if (isReadMediatedContextInstVarIndex(byte2)) { + genPushMaybeContextReceiverVariable(byte2); + } + else { + genPushReceiverVariable(byte2); + + /* begin annotateInstructionForBytecode */ + abstractInstruction = (prevInstIsPCAnnotated() + ? gen(Nop) + : genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + return 0; + } + break; + case 3: + genPushLiteralIndex(byte2); + + /* begin annotateInstructionForBytecode */ + abstractInstruction = (prevInstIsPCAnnotated() + ? gen(Nop) + : genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + return 0; + + case 4: + genPushLiteralVariable(byte2); + break; + case 7: + /* genStorePop:LiteralVariable: */ + genStorePopLiteralVariableneedsStoreCheckneedsImmutabilityCheck(0, byte2, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); +# if IMMUTABILITY + /* genStorePop:LiteralVariable: annotates; don't annotate twice */ + return 0; +# endif + + + default: + if (isWriteMediatedContextInstVarIndex(byte2)) { + /* genStorePop:MaybeContextReceiverVariable: */ + genStorePopMaybeContextReceiverVariableneedsStoreCheckneedsImmutabilityCheck(opType == 6, byte2, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } + else { + /* genStorePop:ReceiverVariable: */ + genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(opType == 6, byte2, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } +# if IMMUTABILITY + /* genStorePop:...ReceiverVariable: annotate; don't annotate twice */ + return 0; +# endif + +; + } + + /* 5 & 6 + We need a map entry for this bytecode for correct parsing (if the method builds a frame). */ + assert(needsFrame); + + /* genPushMaybeContextInstVar, pushLitVar, store & storePop all generate code */ + assert(!(prevInstIsPCAnnotated())); + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + return 0; +} + + /* StackToRegisterMappingCogit>>#duplicateTopBytecode */ +static sqInt +duplicateTopBytecode(void) +{ + SimStackEntry desc; + + /* begin ssTopDescriptor */ + desc = simStack[simStackPtr]; + return ssPushDesc(desc); +} + + +/* Make sure there's a flagged fixup at the target pc in fixups. + Initially a fixup's target is just a flag. Later on it is replaced with a + proper instruction. */ + + /* StackToRegisterMappingCogit>>#ensureFixupAt: */ +static NoDbgRegParms BytecodeFixup * +ensureFixupAt(sqInt targetPC) +{ + BytecodeFixup *fixup; + + /* begin fixupAt: */ + fixup = fixupAtIndex(targetPC - initialPC); + traceFixupmerge(fixup, 1); + if ((((usqInt)((fixup->targetInstruction)))) <= NeedsNonMergeFixupFlag) { + /* begin becomeMergeFixup */ + (fixup->targetInstruction) = ((AbstractInstruction *) NeedsMergeFixupFlag); + (fixup->simStackPtr = simStackPtr); + } + else { + if ((fixup->isTargetOfBackwardBranch)) { + (fixup->simStackPtr = simStackPtr); + } + else { + assert(((fixup->simStackPtr)) == simStackPtr); + } + } + + /* convert a non-merge into a merge */ + return fixup; +} + + +/* Make sure there's a flagged fixup at the target pc in fixups. + Initially a fixup's target is just a flag. Later on it is replaced with a + proper instruction. */ + + /* StackToRegisterMappingCogit>>#ensureNonMergeFixupAt: */ +static NoDbgRegParms BytecodeFixup * +ensureNonMergeFixupAt(sqInt targetPC) +{ + BytecodeFixup *fixup; + + /* begin fixupAt: */ + fixup = fixupAtIndex(targetPC - initialPC); + traceFixupmerge(fixup, 1); + if (!((fixup->targetInstruction))) { + /* begin becomeNonMergeFixup */ + (fixup->targetInstruction) = ((AbstractInstruction *) NeedsNonMergeFixupFlag); + } + return fixup; +} + + /* StackToRegisterMappingCogit>>#ensureReceiverResultRegContainsSelf */ +static void +ensureReceiverResultRegContainsSelf(void) +{ + if (needsFrame) { + if (!((((simSelf())->liveRegister)) == ReceiverResultReg)) { + /* begin ssAllocateRequiredReg: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << ReceiverResultReg), simStackPtr, simNativeStackPtr); + storeToReg(simSelf(), ReceiverResultReg); + ((simSelf())->liveRegister = ReceiverResultReg); + } + } + else { + assert(((((simSelf())->type)) == SSRegister) + && (((((simSelf())->registerr)) == ReceiverResultReg) + && (receiverIsInReceiverResultReg()))); + } +} + + /* StackToRegisterMappingCogit>>#evaluate:at: */ +static NoDbgRegParms void +evaluateat(BytecodeDescriptor *descriptor, sqInt pc) +{ + byte0 = fetchByteofObject(pc, methodObj); + assert(descriptor == (generatorAt(bytecodeSetOffset + byte0))); + + /* begin loadSubsequentBytesForDescriptor:at: */ + if (((descriptor->numBytes)) > 1) { + byte1 = fetchByteofObject(pc + 1, methodObj); + if (((descriptor->numBytes)) > 2) { + byte2 = fetchByteofObject(pc + 2, methodObj); + if (((descriptor->numBytes)) > 3) { + byte3 = fetchByteofObject(pc + 3, methodObj); + if (((descriptor->numBytes)) > 4) { + notYetImplemented(); + } + } + } + } + ((descriptor->generator))(); +} + + +/* Attempt to follow a branch to a pc. Handle branches to unconditional jumps + and branches to push: aBoolean; conditional branch pairs. If the branch + cannot be + followed answer targetBytecodePC. It is not possible to follow jumps to + conditional branches because the stack changes depth. That following is + left to the genJumpIf:to: + clients. */ + + /* StackToRegisterMappingCogit>>#eventualTargetOf: */ +static NoDbgRegParms sqInt +eventualTargetOf(sqInt targetBytecodePC) +{ + sqInt cond; + sqInt currentTarget; + BytecodeDescriptor *descriptor; + sqInt nExts; + sqInt nextPC; + sqInt span; + + cond = 0; + descriptor = ((BytecodeDescriptor *) 0); + nextPC = (currentTarget = targetBytecodePC); + while (1) { + nExts = 0; + while (1) { + /* begin generatorForPC: */ + descriptor = generatorAt(bytecodeSetOffset + (fetchByteofObject(nextPC, methodObj))); + if ((descriptor->isReturn)) { + return currentTarget; + } + if (!((descriptor->isExtension))) break; + nExts += 1; + nextPC += (descriptor->numBytes); + } + if (/* isUnconditionalBranch */ + (isBranch(descriptor)) + && (!(((descriptor->isBranchTrue)) + || ((descriptor->isBranchFalse))))) { + span = ((descriptor->spanFunction))(descriptor, nextPC, nExts, methodObj); + if (span < 0) { + return currentTarget; + } + + /* Do *not* follow backward branches; these are interrupt points and should not be elided. */ + nextPC = (nextPC + ((descriptor->numBytes))) + span; + } + else { + if (((descriptor->generator)) == genPushConstantTrueBytecode) { + cond = 1; + } + else { + if (((descriptor->generator)) == genPushConstantFalseBytecode) { + cond = 0; + } + else { + return currentTarget; + } + } + + /* Don't step into loops across a pushTrue; jump:if: boundary, so as not to confuse stack depth fixup. */ + if (((fixupAt(nextPC))->isTargetOfBackwardBranch)) { + return currentTarget; + } + nextPC = eventualTargetOf(nextPC + ((descriptor->numBytes))); + nExts = 0; + while (1) { + /* begin generatorForPC: */ + descriptor = generatorAt(bytecodeSetOffset + (fetchByteofObject(nextPC, methodObj))); + if ((descriptor->isReturn)) { + return currentTarget; + } + if (!((descriptor->isExtension))) break; + nExts += 1; + nextPC += (descriptor->numBytes); + } + if (!(isBranch(descriptor))) { + return currentTarget; + } + if (/* isUnconditionalBranch */ + (isBranch(descriptor)) + && (!(((descriptor->isBranchTrue)) + || ((descriptor->isBranchFalse))))) { + return currentTarget; + } + nextPC = (cond == ((descriptor->isBranchTrue)) + ? (nextPC + ((descriptor->numBytes))) + (((descriptor->spanFunction))(descriptor, nextPC, nExts, methodObj)) + : nextPC + ((descriptor->numBytes))); + } + currentTarget = nextPC; + } + return 0; +} + + +/* Spill the closest register on stack not conflicting with regMask. + Assertion Failure if regMask has already all the registers */ + + /* StackToRegisterMappingCogit>>#freeAnyRegNotConflictingWith: */ +static NoDbgRegParms sqInt +freeAnyRegNotConflictingWith(sqInt regMask) +{ + SimStackEntry *desc; + sqInt index; + sqInt reg; + + assert(needsFrame); + reg = NoReg; + index = ((simSpillBase < 0) ? 0 : simSpillBase); + while ((reg == NoReg) + && (index < simStackPtr)) { + desc = simStackAt(index); + if (((desc->type)) == SSRegister) { + if (!(((regMask & (((((desc->registerr)) < 0) ? (((usqInt)(1)) >> (-((desc->registerr)))) : (1ULL << ((desc->registerr)))))) != 0))) { + reg = (desc->registerr); + } + } + index += 1; + } + assert(!((reg == NoReg))); + + /* begin ssAllocateRequiredReg: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)), simStackPtr, simNativeStackPtr); + return reg; +} + + +/* Return from block, assuming result already loaded into ReceiverResultReg. */ +/* Return from block, assuming result already loaded into ReceiverResultReg. */ + + /* StackToRegisterMappingCogit>>#genBlockReturn */ +static sqInt +genBlockReturn(void) +{ + if (needsFrame) { + /* MoveR:R: */ + genoperandoperand(MoveRR, FPReg, SPReg); + + /* PopR: */ + genoperand(PopR, FPReg); + + /* PopR: */ + genoperand(PopR, LinkReg); + } + + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + + /* can't fall through */ + deadCode = 1; + return 0; +} + + +/* Generate special versions of the ceCallCogCodePopReceiverAndClassRegs + enilopmart that also pop register args from the stack to undo the pushing + of register args in the abort/miss trampolines. */ + + /* StackToRegisterMappingCogit>>#genCallPICEnilopmartNumArgs: */ +static NoDbgRegParms void +(*genCallPICEnilopmartNumArgs(sqInt numArgs))(void) +{ + AbstractInstruction *anInstruction; + sqInt endAddress; + usqInt enilopmart; + sqInt quickConstant; + sqInt size; + + zeroOpcodeIndex(); + quickConstant = varBaseAddress; + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, quickConstant, VarBaseReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + genLoadStackPointers(backEnd); + + /* PopR: */ + genoperand(PopR, ClassReg); + + /* PopR: */ + genoperand(PopR, TempReg); + + /* PopR: */ + genoperand(PopR, LinkReg); + if (numArgs > 0) { + if (numArgs > 1) { + /* PopR: */ + genoperand(PopR, Arg1Reg); + assert((numRegArgs()) == 2); + } + + /* PopR: */ + genoperand(PopR, Arg0Reg); + } + + /* PopR: */ + genoperand(PopR, ReceiverResultReg); + + /* JumpR: */ + genoperand(JumpR, TempReg); + computeMaximumSizes(); + size = generateInstructionsAt(methodZoneBase); + endAddress = outputInstructionsAt(methodZoneBase); + assert((methodZoneBase + size) == endAddress); + enilopmart = methodZoneBase; + methodZoneBase = alignUptoRoutineBoundary(endAddress); + stopsFromto(backEnd, endAddress, methodZoneBase - 1); + recordGeneratedRunTimeaddress(trampolineNamenumRegArgs("ceCallPIC", numArgs), enilopmart); + return ((void (*)(void)) enilopmart); +} + + +/* SistaV1: ** 248 (2) 11111000 iiiiiiii mssjjjjj Call Primitive #iiiiiiii + + (jjjjj * 256) + m=1 means inlined primitive, no hard return after execution. + ss defines the unsafe operation set used to encode the operations. + (ss = 0 means sista unsafe operations, ss = 01 means lowcode operations, + other numbers are as yet used). + See SistaCogit genCallPrimitiveBytecode, EncoderForSistaV1's class comment + and StackInterpreter>>#callPrimitiveBytecode for more information. */ + + /* StackToRegisterMappingCogit>>#genCallPrimitiveBytecode */ +static sqInt +genCallPrimitiveBytecode(void) +{ + usqInt prim; + usqInt primSet; + + if (byte2 < 128) { + return (bytecodePC == initialPC + ? 0 + : EncounteredUnknownBytecode); + } + prim = ((((usqInt)((byte2 - 128)) << 8))) + byte1; + primSet = ((prim) >> 13) & 3; + prim = prim & 0x1FFF; + return EncounteredUnknownBytecode; +} + + +/* Override to push the register receiver and register arguments, if any. */ + + /* StackToRegisterMappingCogit>>#genExternalizePointersForPrimitiveCall */ +static sqInt +genExternalizePointersForPrimitiveCall(void) +{ + genPushRegisterArgs(); + + /* #MoveR:Aw: #gen:operand:literal: */ + checkLiteralforInstruction(instructionPointerAddress(), genoperandoperand(MoveRAw, LinkReg, instructionPointerAddress())); + + /* Set coInterpreter stackPointer to the topmost argument, skipping the return address. */ + return genSaveStackPointers(backEnd); +} + + +/* Override to push the register receiver and register arguments, if any. */ + + /* StackToRegisterMappingCogit>>#genExternalizeStackPointerForFastPrimitiveCall */ +static AbstractInstruction * +genExternalizeStackPointerForFastPrimitiveCall(void) +{ + genPushRegisterArgs(); + return checkLiteralforInstruction(stackPointerAddress(), genoperandoperand(MoveRAw, SPReg, stackPointerAddress())); +} + + +/* Block compilation. At this point in the method create the block. Note its + start and defer generating code for it until after the method and any + other preceding + blocks. The block's actual code will be compiled later. */ +/* 253 11111101 eei i i kkk jjjjjjjj Push Closure Num Copied iii (+ Ext A + // 16 * 8) Num Args kkk (+ Ext A \\ 16 * 8) BlockSize jjjjjjjj (+ Ext B * + 256). ee = num extensions + */ + + /* StackToRegisterMappingCogit>>#genExtPushClosureBytecode */ +static sqInt +genExtPushClosureBytecode(void) +{ + sqInt i; + usqInt numArgs; + usqInt numCopied; + sqInt reg; + sqInt startpc; + + assert(needsFrame); + startpc = bytecodePC + (((generatorAt(byte0))->numBytes)); + addBlockStartAtnumArgsnumCopiedspan(startpc, (numArgs = (byte1 & 7) + ((extA % 16) * 8)), (numCopied = ((((usqInt)(byte1)) >> 3) & 7) + ((extA / 16) * 8)), byte2 + ((((usqInt)(extB) << 8)))); + + /* 0 relative */ + extA = (numExtB = (extB = 0)); + + /* begin genInlineClosure:numArgs:numCopied: */ + assert(getActiveContextAllocatesInMachineCode()); + voidReceiverResultRegContainsSelf(); + + /* begin ssAllocateCallReg:and:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(CallerSavedRegisterMask | (((1U << ReceiverResultReg) | (1U << SendNumArgsReg)) | (1U << ClassReg)), simStackPtr, simNativeStackPtr); + genNoPopCreateClosureAtnumArgsnumCopiedcontextNumArgslargeinBlock(startpc + 1, numArgs, numCopied, methodOrBlockNumArgs, methodNeedsLargeContext(methodObj), inBlock); + + /* 1 relative */ + for (i = 1; i <= numCopied; i += 1) { + reg = ssStorePoptoPreferredReg(1, TempReg); + genStoreSourceRegslotIndexintoNewObjectInDestReg(reg, (ClosureFirstCopiedValueIndex + numCopied) - i, ReceiverResultReg); + } + ssPushRegister(ReceiverResultReg); + return 0; +} + + +/* Full Block creation compilation. The block's actual code will be compiled + separatedly. + */ +/* * 255 11111111 xxxxxxxx siyyyyyy push Closure Compiled block literal + index xxxxxxxx (+ Extend A * 256) numCopied yyyyyy receiverOnStack: s = 1 + ignoreOuterContext: i = 1 + */ + + /* StackToRegisterMappingCogit>>#genExtPushFullClosureBytecode */ +static sqInt +genExtPushFullClosureBytecode(void) +{ + sqInt compiledBlock; + sqInt i; + int ignoreContext; + sqInt numCopied; + int receiverIsOnStack; + sqInt reg; + SimStackEntry *selfOfStoreToReg; + + assert(needsFrame); + compiledBlock = getLiteral(byte1 + ((((usqInt)(extA) << 8)))); + extA = 0; + numCopied = byte2 & (0x3F); + receiverIsOnStack = ((byte2 & (128)) != 0); + ignoreContext = ((byte2 & (64)) != 0); + voidReceiverResultRegContainsSelf(); + + /* begin ssAllocateCallReg:and:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(CallerSavedRegisterMask | (((1U << ReceiverResultReg) | (1U << SendNumArgsReg)) | (1U << ClassReg)), simStackPtr, simNativeStackPtr); + genCreateFullClosurenumArgsnumCopiedignoreContextcontextNumArgslargeinBlock(compiledBlock, argumentCountOf(compiledBlock), numCopied, ignoreContext, methodOrBlockNumArgs, methodNeedsLargeContext(methodObj), inBlock); + + /* Closure in ReceiverResultReg */ + for (i = 1; i <= numCopied; i += 1) { + reg = ssStorePoptoPreferredReg(1, TempReg); + genStoreSourceRegslotIndexintoNewObjectInDestReg(reg, (FullClosureFirstCopiedValueIndex + numCopied) - i, ReceiverResultReg); + } + if (receiverIsOnStack) { + reg = ssStorePoptoPreferredReg(1, TempReg); + } + else { + selfOfStoreToReg = simSelf(); + storeToReg(selfOfStoreToReg, (reg = TempReg)); + } + genStoreSourceRegslotIndexintoNewObjectInDestReg(reg, FullClosureReceiverIndex, ReceiverResultReg); + ssPushRegister(ReceiverResultReg); + return 0; +} + + +/* Enilopmarts transfer control from C into machine code (backwards + trampolines). + */ +/* Enilopmarts transfer control from C into machine code (backwards + trampolines). Override to add version for generic and PIC-specific entry + with reg args. */ + + /* StackToRegisterMappingCogit>>#generateEnilopmarts */ +static void +generateEnilopmarts(void) +{ + +# if Debug + /* begin genEnilopmartFor:forCall:called: */ + realCEEnterCogCodePopReceiverReg = genEnilopmartForandandforCallcalled(ReceiverResultReg, NoReg, NoReg, 0, "realCEEnterCogCodePopReceiverReg"); + ceEnterCogCodePopReceiverReg = enterCogCodePopReceiver; + + /* begin genEnilopmartFor:forCall:called: */ + realCECallCogCodePopReceiverReg = genEnilopmartForandandforCallcalled(ReceiverResultReg, NoReg, NoReg, 1, "realCECallCogCodePopReceiverReg"); + ceCallCogCodePopReceiverReg = callCogCodePopReceiver; + + /* begin genEnilopmartFor:and:forCall:called: */ + realCECallCogCodePopReceiverAndClassRegs = genEnilopmartForandandforCallcalled(ReceiverResultReg, ClassReg, NoReg, 1, "realCECallCogCodePopReceiverAndClassRegs"); + ceCallCogCodePopReceiverAndClassRegs = callCogCodePopReceiverAndClassRegs; +# else // Debug + /* begin genEnilopmartFor:forCall:called: */ + ceEnterCogCodePopReceiverReg = genEnilopmartForandandforCallcalled(ReceiverResultReg, NoReg, NoReg, 0, "ceEnterCogCodePopReceiverReg"); + ceCallCogCodePopReceiverReg = genEnilopmartForandandforCallcalled(ReceiverResultReg, NoReg, NoReg, 1, "ceCallCogCodePopReceiverReg"); + ceCallCogCodePopReceiverAndClassRegs = genEnilopmartForandandforCallcalled(ReceiverResultReg, ClassReg, NoReg, 1, "ceCallCogCodePopReceiverAndClassRegs"); +# endif // Debug + + genPrimReturnEnterCogCodeEnilopmart(0); + cePrimReturnEnterCogCode = methodZoneBase; + outputInstructionsForGeneratedRuntimeAt(cePrimReturnEnterCogCode); + recordGeneratedRunTimeaddress("cePrimReturnEnterCogCode", cePrimReturnEnterCogCode); + genPrimReturnEnterCogCodeEnilopmart(1); + cePrimReturnEnterCogCodeProfiling = methodZoneBase; + outputInstructionsForGeneratedRuntimeAt(cePrimReturnEnterCogCodeProfiling); + recordGeneratedRunTimeaddress("cePrimReturnEnterCogCodeProfiling", cePrimReturnEnterCogCodeProfiling); +# if Debug + /* begin genEnilopmartFor:and:forCall:called: */ + realCECallCogCodePopReceiverArg0Regs = genEnilopmartForandandforCallcalled(ReceiverResultReg, Arg0Reg, NoReg, 1, "realCECallCogCodePopReceiverArg0Regs"); + ceCallCogCodePopReceiverArg0Regs = callCogCodePopReceiverArg0Regs; + realCECallCogCodePopReceiverArg1Arg0Regs = genEnilopmartForandandforCallcalled(ReceiverResultReg, Arg0Reg, Arg1Reg, 1, "realCECallCogCodePopReceiverArg1Arg0Regs"); + ceCallCogCodePopReceiverArg1Arg0Regs = callCogCodePopReceiverArg1Arg0Regs; +# else // Debug + /* begin genEnilopmartFor:and:forCall:called: */ + ceCallCogCodePopReceiverArg0Regs = genEnilopmartForandandforCallcalled(ReceiverResultReg, Arg0Reg, NoReg, 1, "ceCallCogCodePopReceiverArg0Regs"); + ceCallCogCodePopReceiverArg1Arg0Regs = genEnilopmartForandandforCallcalled(ReceiverResultReg, Arg0Reg, Arg1Reg, 1, "ceCallCogCodePopReceiverArg1Arg0Regs"); +# endif // Debug + + + /* These are special versions of the ceCallCogCodePopReceiverAndClassRegs enilopmart that also + pop register args from the stack to undo the pushing of register args in the abort/miss trampolines. */ + ceCall0ArgsPIC = genCallPICEnilopmartNumArgs(0); + if ((numRegArgs()) >= 1) { + ceCall1ArgsPIC = genCallPICEnilopmartNumArgs(1); + if ((numRegArgs()) >= 2) { + ceCall2ArgsPIC = genCallPICEnilopmartNumArgs(2); + assert((numRegArgs()) == 2); + } + } +} + + +/* Size pc-dependent instructions and assign eventual addresses to all + instructions. Answer the size of the code. + Compute forward branches based on virtual address (abstract code starts at + 0), assuming that any branches branched over are long. + Compute backward branches based on actual address. + Reuse the fixups array to record the pc-dependent instructions that need + to have + their code generation postponed until after the others. + + Override to add handling for null branches (branches to the immediately + following instruction) occasioned by StackToRegisterMapping's following of + jumps. */ + + /* StackToRegisterMappingCogit>>#generateInstructionsAt: */ +static NoDbgRegParms sqInt +generateInstructionsAt(sqInt eventualAbsoluteAddress) +{ + sqInt absoluteAddress; + AbstractInstruction *abstractInstruction; + BytecodeFixup *fixup; + sqInt i; + sqInt j; + sqInt pcDependentIndex; + + absoluteAddress = eventualAbsoluteAddress; + pcDependentIndex = 0; + for (i = 0; i < opcodeIndex; i += 1) { + maybeBreakGeneratingInstructionWithIndex(i); + abstractInstruction = abstractInstructionAt(i); + + /* N.B. if you want to break in resizing, break here, note the instruction index, back up to the + sender, restart, and step into computeMaximumSizes, breaking at this instruction's index. */ + maybeBreakGeneratingFromto(absoluteAddress, absoluteAddress + ((abstractInstruction->maxSize))); + if (isPCDependent(abstractInstruction)) { + sizePCDependentInstructionAt(abstractInstruction, absoluteAddress); + if ((isJump(abstractInstruction)) + && ((((i + 1) < opcodeIndex) + && ((((AbstractInstruction *) (((abstractInstruction->operands))[0]))) == (abstractInstructionAt(i + 1)))) + || (((i + 2) < opcodeIndex) + && (((((AbstractInstruction *) (((abstractInstruction->operands))[0]))) == (abstractInstructionAt(i + 2))) + && ((((abstractInstructionAt(i + 1))->opcode)) == Nop))))) { + (abstractInstruction->opcode = Nop); + concretizeAt(abstractInstruction, absoluteAddress); + } + else { + fixup = fixupAtIndex(pcDependentIndex); + pcDependentIndex += 1; + (fixup->instructionIndex = i); + } + absoluteAddress += (abstractInstruction->machineCodeSize); + } + else { + absoluteAddress = concretizeAt(abstractInstruction, absoluteAddress); + + /* N.B. if you want to break in resizing, break here, note the instruction index, back up to the + sender, restart, and step into computeMaximumSizes, breaking at this instruction's index. */ + assert(((abstractInstruction->machineCodeSize)) == ((abstractInstruction->maxSize))); + } + } + for (j = 0; j < pcDependentIndex; j += 1) { + fixup = fixupAtIndex(j); + maybeBreakGeneratingInstructionWithIndex((fixup->instructionIndex)); + abstractInstruction = abstractInstructionAt((fixup->instructionIndex)); + maybeBreakGeneratingFromto((abstractInstruction->address), (((abstractInstruction->address)) + ((abstractInstruction->maxSize))) - 1); + concretizeAt(abstractInstruction, (abstractInstruction->address)); + } + return absoluteAddress - eventualAbsoluteAddress; +} + + +/* Generate the run-time entries for the various method and PIC entry misses + and aborts. + Read the class-side method trampolines for documentation on the various + trampolines + */ + + /* StackToRegisterMappingCogit>>#generateMissAbortTrampolines */ +static void +generateMissAbortTrampolines(void) +{ + sqInt numArgs; + sqInt toDoLimit; + sqInt toDoLimit1; + sqInt toDoLimit2; + + toDoLimit = (numRegArgs()) + 1; + for (numArgs = 0; numArgs <= toDoLimit; numArgs += 1) { + methodAbortTrampolines[numArgs] = (genMethodAbortTrampolineFor(numArgs)); + } + toDoLimit1 = (numRegArgs()) + 1; + for (numArgs = 0; numArgs <= toDoLimit1; numArgs += 1) { + picAbortTrampolines[numArgs] = (genPICAbortTrampolineFor(numArgs)); + } + toDoLimit2 = (numRegArgs()) + 1; + for (numArgs = 0; numArgs <= toDoLimit2; numArgs += 1) { + picMissTrampolines[numArgs] = (genPICMissTrampolineFor(numArgs)); + } + ceReapAndResetErrorCodeTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceReapAndResetErrorCodeFor, "ceReapAndResetErrorCodeTrampoline", 1, ClassReg, null, null, null, 0 /* emptyRegisterMask */, 1, NoReg, 0); +} + + +/* Override to generate code to push the register arg(s) for <= numRegArg + arity sends. + */ + + /* StackToRegisterMappingCogit>>#generateSendTrampolines */ +static void +generateSendTrampolines(void) +{ + sqInt numArgs; + + for (numArgs = 0; numArgs < NumSendTrampolines; numArgs += 1) { + ordinarySendTrampolines[numArgs] = (genSendTrampolineFornumArgscalledargargargarg( + ceSendsupertonumArgs, + numArgs, + trampolineNamenumArgs("ceSend", numArgs), + ClassReg, + (/* begin trampolineArgConstant: */ + assert(0 >= 0), + -2 - 0), + ReceiverResultReg, + /* numArgsOrSendNumArgsReg: */ + (numArgs <= (NumSendTrampolines - 2) + ? (/* begin trampolineArgConstant: */ + assert(numArgs >= 0), + -2 - numArgs) + : SendNumArgsReg))); + } + + /* Generate these in the middle so they are within [firstSend, lastSend]. */ + for (numArgs = 0; numArgs < NumSendTrampolines; numArgs += 1) { + directedSuperSendTrampolines[numArgs] = (genSendTrampolineFornumArgscalledargargargarg(ceSendabovetonumArgs, numArgs, trampolineNamenumArgs("ceDirectedSuperSend", numArgs), ClassReg, TempReg, ReceiverResultReg, /* numArgsOrSendNumArgsReg: */ + (numArgs <= (NumSendTrampolines - 2) + ? (/* begin trampolineArgConstant: */ + assert(numArgs >= 0), + -2 - numArgs) + : SendNumArgsReg))); + directedSuperBindingSendTrampolines[numArgs] = (genSendTrampolineFornumArgscalledargargargarg(ceSendaboveClassBindingtonumArgs, numArgs, trampolineNamenumArgs("ceDirectedSuperBindingSend", numArgs), ClassReg, TempReg, ReceiverResultReg, /* numArgsOrSendNumArgsReg: */ + (numArgs <= (NumSendTrampolines - 2) + ? (/* begin trampolineArgConstant: */ + assert(numArgs >= 0), + -2 - numArgs) + : SendNumArgsReg))); + } + for (numArgs = 0; numArgs < NumSendTrampolines; numArgs += 1) { + superSendTrampolines[numArgs] = (genSendTrampolineFornumArgscalledargargargarg( + ceSendsupertonumArgs, + numArgs, + trampolineNamenumArgs("ceSuperSend", numArgs), + ClassReg, + (/* begin trampolineArgConstant: */ + assert(1 >= 0), + -2 - 1), + ReceiverResultReg, + /* numArgsOrSendNumArgsReg: */ + (numArgs <= (NumSendTrampolines - 2) + ? (/* begin trampolineArgConstant: */ + assert(numArgs >= 0), + -2 - numArgs) + : SendNumArgsReg))); + } + firstSend = ordinarySendTrampolines[0]; + lastSend = superSendTrampolines[NumSendTrampolines - 1]; +} + + +/* Generate trampolines for tracing. In the simulator we can save a lot of + time and avoid noise instructions in the lastNInstructions log by + short-cutting these + trampolines, but we need them in the real vm. */ + + /* StackToRegisterMappingCogit>>#generateTracingTrampolines */ +static void +generateTracingTrampolines(void) +{ + ceTraceLinkedSendTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceTraceLinkedSend, "ceTraceLinkedSendTrampoline", 1, ReceiverResultReg, null, null, null, CallerSavedRegisterMask, 1, NoReg, 0); + ceTraceBlockActivationTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceTraceBlockActivation, "ceTraceBlockActivationTrampoline", 0, null, null, null, null, CallerSavedRegisterMask, 1, NoReg, 0); + ceTraceStoreTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceTraceStoreOfinto, "ceTraceStoreTrampoline", 2, TempReg, ReceiverResultReg, null, null, CallerSavedRegisterMask, 1, NoReg, 0); +} + + /* StackToRegisterMappingCogit>>#genForwardersInlinedIdenticalOrNotIf: */ +static NoDbgRegParms sqInt +genForwardersInlinedIdenticalOrNotIf(sqInt orNot) +{ + AbstractInstruction *anInstruction; + sqInt argConstant; + sqInt argNeedsReg; + sqInt argReg; + sqInt argRegSqInt; + BytecodeDescriptor *branchDescriptor; + BytecodeDescriptor *branchDescriptor1; + sqInt constant; + BytecodeFixup *fixup; + sqInt i; + void *jumpTarget; + AbstractInstruction *label; + sqInt nExts; + sqInt nextPC; + sqInt nextPCSqInt; + sqInt postBranchPC; + sqInt postBranchPCSqInt; + BytecodeDescriptor *primDescriptor; + sqInt rcvrConstant; + sqInt rcvrNeedsReg; + sqInt rcvrReg; + sqInt rcvrRegSqInt; + sqInt reg; + sqInt rNextSqInt; + sqInt rTopSqInt; + BytecodeFixup *self_in_CogBytecodeFixup; + SimStackEntry *simStackEntry; + sqInt targetBytecodePC; + sqInt targetBytecodePCSqInt; + sqInt topRegistersMask; + sqInt unforwardArg; + sqInt unforwardRcvr; + + unforwardRcvr = mayBeAForwarder(ssValue(1)); + unforwardArg = mayBeAForwarder(ssTop()); + if ((!unforwardRcvr) + && (!unforwardArg)) { + return genVanillaInlinedIdenticalOrNotIf(orNot); + } + assert(unforwardArg + || (unforwardRcvr)); + + /* We use reg for non annotable constants to avoid duplicating objRef. */ + + /* begin ssValue: */ + simStackEntry = simStackAt(simStackPtr - 1); + rcvrConstant = (((simStackEntry->type)) == SSConstant) + && ((isImmediate((simStackEntry->constant))) + || (!(/* shouldAnnotateObjectReference: */ + (isNonImmediate((simStackEntry->constant))) + && ((oopisGreaterThan((simStackEntry->constant), classTableRootObj())) + || (oopisLessThan((simStackEntry->constant), nilObject())))))); + + /* begin ssTop */ + simStackEntry = simStackAt(simStackPtr); + argConstant = (((simStackEntry->type)) == SSConstant) + && ((isImmediate((simStackEntry->constant))) + || (!(/* shouldAnnotateObjectReference: */ + (isNonImmediate((simStackEntry->constant))) + && ((oopisGreaterThan((simStackEntry->constant), classTableRootObj())) + || (oopisLessThan((simStackEntry->constant), nilObject())))))); + + /* begin extractMaybeBranchDescriptorInto: */ + branchDescriptor1 = ((BytecodeDescriptor *) 0); + primDescriptor = generatorAt(byte0); + nextPCSqInt = bytecodePC + ((primDescriptor->numBytes)); + nExts = 0; + while (1) { + while (1) { + /* begin generatorForPC: */ + branchDescriptor1 = generatorAt(bytecodeSetOffset + (fetchByteofObject(nextPCSqInt, methodObj))); + if (!((branchDescriptor1->isExtension))) break; + nExts += 1; + nextPCSqInt += (branchDescriptor1->numBytes); + } + if (!(/* isUnconditionalBranch */ + (isBranch(branchDescriptor1)) + && (!(((branchDescriptor1->isBranchTrue)) + || ((branchDescriptor1->isBranchFalse)))))) break; + nextPCSqInt = eventualTargetOf((nextPCSqInt + ((branchDescriptor1->numBytes))) + (((branchDescriptor1->spanFunction))(branchDescriptor1, nextPCSqInt, nExts, methodObj))); + } + targetBytecodePCSqInt = (postBranchPCSqInt = 0); + if (((branchDescriptor1->isBranchTrue)) + || ((branchDescriptor1->isBranchFalse))) { + targetBytecodePCSqInt = eventualTargetOf((nextPCSqInt + ((branchDescriptor1->numBytes))) + (((branchDescriptor1->spanFunction))(branchDescriptor1, nextPCSqInt, nExts, methodObj))); + postBranchPCSqInt = eventualTargetOf(nextPCSqInt + ((branchDescriptor1->numBytes))); + } + else { + nextPCSqInt = bytecodePC + ((primDescriptor->numBytes)); + } + branchDescriptor = branchDescriptor1; + nextPC = nextPCSqInt; + postBranchPC = postBranchPCSqInt; + targetBytecodePC = targetBytecodePCSqInt; + argNeedsReg = !argConstant; + rcvrNeedsReg = !rcvrConstant; + + /* begin allocateEqualsEqualsRegistersArgNeedsReg:rcvrNeedsReg:into: */ + assert(argNeedsReg + || (rcvrNeedsReg)); + argRegSqInt = (rcvrRegSqInt = NoReg); + if (argNeedsReg) { + if (rcvrNeedsReg) { + /* begin allocateRegForStackTopTwoEntriesInto: */ + topRegistersMask = 0; + rTopSqInt = (rNextSqInt = NoReg); + if ((registerOrNone(ssTop())) != NoReg) { + rTopSqInt = registerOrNone(ssTop()); + } + if ((registerOrNone(ssValue(1))) != NoReg) { + reg = (rNextSqInt = registerOrNone(ssValue(1))); + + /* begin registerMaskFor: */ + topRegistersMask = ((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)); + } + if (rTopSqInt == NoReg) { + rTopSqInt = allocateRegNotConflictingWith(topRegistersMask); + } + if (rNextSqInt == NoReg) { + rNextSqInt = allocateRegNotConflictingWith(((rTopSqInt < 0) ? (((usqInt)(1)) >> (-rTopSqInt)) : (1ULL << rTopSqInt))); + } + assert(!(((rTopSqInt == NoReg) + || (rNextSqInt == NoReg)))); + argRegSqInt = rTopSqInt; + rcvrRegSqInt = rNextSqInt; + popToReg(ssTop(), argRegSqInt); + popToReg(ssValue(1), rcvrRegSqInt); + } + else { + argRegSqInt = allocateRegForStackEntryAtnotConflictingWith(0, 0); + popToReg(ssTop(), argRegSqInt); + + /* If the receiver is a spilled constant we need to pop it from the stack. */ + if (((ssValue(1))->spilled)) { + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, BytesPerWord, SPReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BytesPerWord, BytesPerOop)); + } + } + } + } + else { + assert(rcvrNeedsReg); + assert(!((((ssTop())->spilled)))); + rcvrRegSqInt = allocateRegForStackEntryAtnotConflictingWith(1, 0); + popToReg(ssValue(1), rcvrRegSqInt); + } + assert(!((argNeedsReg + && (argRegSqInt == NoReg)))); + assert(!((rcvrNeedsReg + && (rcvrRegSqInt == NoReg)))); + rcvrReg = rcvrRegSqInt; + argReg = argRegSqInt; + + /* If not followed by a branch, resolve to true or false. */ + if (!(((branchDescriptor->isBranchTrue)) + || ((branchDescriptor->isBranchFalse)))) { + return genIdenticalNoBranchArgIsConstantrcvrIsConstantargRegrcvrRegorNotIf(argConstant, rcvrConstant, argReg, rcvrReg, orNot); + } + + /* If branching the stack must be flushed for the merge */ + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= (simStackPtr - 2)) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < (simStackPtr - 2)) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : (simStackPtr - 2))); i <= (simStackPtr - 2); i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = (simStackPtr - 2) + 1; + } + label = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin genCmpArgIsConstant:rcvrIsConstant:argReg:rcvrReg: */ + assert((argReg != NoReg) + || (rcvrReg != NoReg)); + if (argConstant) { + constant = ((ssTop())->constant); + + /* begin genCmpConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(constant)) + && ((oopisGreaterThan(constant, classTableRootObj())) + || (oopisLessThan(constant, nilObject())))) { + annotateobjRef(checkLiteralforInstruction(constant, genoperandoperand(CmpCwR, constant, rcvrReg)), constant); + } + else { + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, constant, rcvrReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(constant, BytesPerOop)); + } + } + } + else { + if (rcvrConstant) { + constant = ((ssValue(1))->constant); + + /* begin genCmpConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(constant)) + && ((oopisGreaterThan(constant, classTableRootObj())) + || (oopisLessThan(constant, nilObject())))) { + annotateobjRef(checkLiteralforInstruction(constant, genoperandoperand(CmpCwR, constant, argReg)), constant); + } + else { + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, constant, argReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(constant, BytesPerOop)); + } + } + } + else { + /* begin CmpR:R: */ + assert(!((argReg == SPReg))); + genoperandoperand(CmpRR, argReg, rcvrReg); + } + } + ssPop(2); + + /* Since there is a following conditional jump bytecode (unless there is deadCode), + define non-merge fixups and leave the cond bytecode to set the mergeness. */ + + /* begin fixupAt: */ + self_in_CogBytecodeFixup = fixupAtIndex(nextPC - initialPC); + if ((self_in_CogBytecodeFixup->targetInstruction)) { + assert(!(deadCode)); + } + else { + deadCode = 1; + ensureFixupAt(targetBytecodePC); + ensureFixupAt(postBranchPC); + } + if (orNot == ((branchDescriptor->isBranchTrue))) { + fixup = ensureNonMergeFixupAt(targetBytecodePC); + jumpTarget = ensureNonMergeFixupAt(postBranchPC); + + /* begin JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)jumpTarget)); + } + else { + fixup = ensureNonMergeFixupAt(postBranchPC); + jumpTarget = ensureNonMergeFixupAt(targetBytecodePC); + + /* begin JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)jumpTarget)); + } + + /* orNot is true for ~~ + a == b ifTrue: ... or a ~~ b ifFalse: ... jump on equal to target pc + a == b ifFalse: ... or a ~~ b ifTrue: ... jump on equal to post-branch pc + The forwarders checks need to jump back to the comparison (label) if a forwarder is found, else + jump forward either to the next forwarder check or to the postBranch or branch target (fixup). */ + if (unforwardArg + && (unforwardRcvr)) { + /* genEnsureOopInRegNotForwarded:scratchReg:jumpBackTo: */ + genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(argReg, TempReg, label, 0); + } + genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder((unforwardRcvr + ? rcvrReg + : argReg), TempReg, label, fixup); + + /* Not reached, execution flow has jumped to fixup */ + if (!deadCode) { + ssPushConstant(trueObject()); + } + return 0; +} + + +/* Generates the machine code for #== in the case where the instruction is + not followed by a branch + */ + + /* StackToRegisterMappingCogit>>#genIdenticalNoBranchArgIsConstant:rcvrIsConstant:argReg:rcvrReg:orNotIf: */ +static NoDbgRegParms sqInt +genIdenticalNoBranchArgIsConstantrcvrIsConstantargRegrcvrRegorNotIf(sqInt argIsConstant, sqInt rcvrIsConstant, sqInt argReg, sqInt rcvrRegOrNone, sqInt orNot) +{ + AbstractInstruction *anInstruction; + sqInt constant; + AbstractInstruction *jumpEqual; + AbstractInstruction *jumpNotEqual; + AbstractInstruction *label; + sqInt resultReg; + + label = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin genCmpArgIsConstant:rcvrIsConstant:argReg:rcvrReg: */ + assert((argReg != NoReg) + || (rcvrRegOrNone != NoReg)); + if (argIsConstant) { + constant = ((ssTop())->constant); + + /* begin genCmpConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(constant)) + && ((oopisGreaterThan(constant, classTableRootObj())) + || (oopisLessThan(constant, nilObject())))) { + annotateobjRef(checkLiteralforInstruction(constant, genoperandoperand(CmpCwR, constant, rcvrRegOrNone)), constant); + } + else { + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, constant, rcvrRegOrNone); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(constant, BytesPerOop)); + } + } + } + else { + if (rcvrIsConstant) { + constant = ((ssValue(1))->constant); + + /* begin genCmpConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(constant)) + && ((oopisGreaterThan(constant, classTableRootObj())) + || (oopisLessThan(constant, nilObject())))) { + annotateobjRef(checkLiteralforInstruction(constant, genoperandoperand(CmpCwR, constant, argReg)), constant); + } + else { + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, constant, argReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(constant, BytesPerOop)); + } + } + } + else { + /* begin CmpR:R: */ + assert(!((argReg == SPReg))); + genoperandoperand(CmpRR, argReg, rcvrRegOrNone); + } + } + ssPop(2); + resultReg = (rcvrRegOrNone == NoReg + ? argReg + : rcvrRegOrNone); + jumpEqual = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + if (!argIsConstant) { + /* genEnsureOopInRegNotForwarded:scratchReg:jumpBackTo: */ + genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(argReg, TempReg, label, 0); + } + if (!rcvrIsConstant) { + /* genEnsureOopInRegNotForwarded:scratchReg:jumpBackTo: */ + genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(rcvrRegOrNone, TempReg, label, 0); + } + if (orNot) { + /* begin genMoveConstant:R: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, trueObject(), resultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(trueObject(), BytesPerOop)); + } + } + else { + /* begin genMoveConstant:R: */ + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, falseObject(), resultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(falseObject(), BytesPerOop)); + } + } + jumpNotEqual = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpEqual, (orNot + ? genMoveConstantR(falseObject(), resultReg) + : genMoveConstantR(trueObject(), resultReg))); + jmpTarget(jumpNotEqual, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + ssPushRegister(resultReg); + return 0; +} + + +/* Decompose code generation for #== into a common constant-folding version, + followed by a double dispatch through the objectRepresentation to a + version that doesn't deal with forwarders and a version that does. */ + + /* StackToRegisterMappingCogit>>#genInlinedIdenticalOrNotIf: */ +static NoDbgRegParms sqInt +genInlinedIdenticalOrNotIf(sqInt orNot) +{ + BytecodeDescriptor *primDescriptor; + sqInt result; + + primDescriptor = generatorAt(byte0); + if ((isUnannotatableConstant(ssTop())) + && (isUnannotatableConstant(ssValue(1)))) { + assert(!((primDescriptor->isMapped))); + if ((orNot + ? (((ssTop())->constant)) != (((ssValue(1))->constant)) + : (((ssTop())->constant)) == (((ssValue(1))->constant)))) { + result = trueObject(); + } + else { + result = falseObject(); + } + ssPop(2); + return ssPushConstant(result); + } + return genForwardersInlinedIdenticalOrNotIf(orNot); +} + + /* StackToRegisterMappingCogit>>#genJumpBackTo: */ +static NoDbgRegParms sqInt +genJumpBackTo(sqInt targetBytecodePC) +{ + AbstractInstruction *abstractInstruction; + sqInt i; + void *jumpTarget; + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + + /* can't fall through */ + deadCode = 1; + + /* #MoveAw:R: #gen:literal:operand: */ + checkLiteralforInstruction(stackLimitAddress(), genoperandoperand(MoveAwR, stackLimitAddress(), TempReg)); + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, SPReg); + + /* begin fixupAt: */ + jumpTarget = fixupAtIndex(targetBytecodePC - initialPC); + genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)jumpTarget)); + abstractInstruction = genoperand(Call, ceCheckForInterruptTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + + /* begin fixupAt: */ + jumpTarget = fixupAtIndex(targetBytecodePC - initialPC); + genoperand(Jump, ((sqInt)jumpTarget)); + return 0; +} + + /* StackToRegisterMappingCogit>>#genJumpIf:to: */ +static NoDbgRegParms sqInt +genJumpIfto(sqInt boolean, sqInt targetBytecodePC) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + SimStackEntry *desc; + sqInt eventualTarget; + BytecodeFixup *fixup; + sqInt i; + void *jumpTarget; + AbstractInstruction *ok; + sqInt quickConstant; + + eventualTarget = eventualTargetOf(targetBytecodePC); + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= (simStackPtr - 1)) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < (simStackPtr - 1)) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : (simStackPtr - 1))); i < simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = (simStackPtr - 1) + 1; + } + + /* begin ssTop */ + desc = simStackAt(simStackPtr); + ssPop(1); + if (/* stackEntryIsBoolean: */ + (((desc->type)) == SSConstant) + && ((((desc->constant)) == (trueObject())) + || (((desc->constant)) == (falseObject())))) { + /* Must arrange there's a fixup at the target whether it is jumped to or + not so that the simStackPtr can be kept correct. */ + fixup = ensureFixupAt(eventualTarget); + + /* Must annotate the bytecode for correct pc mapping. */ + abstractInstruction = (((desc->constant)) == boolean + ? genoperand(Jump, ((sqInt)fixup)) + : (prevInstIsPCAnnotated() + ? gen(Nop) + : genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + extA = 0; + return 0; + } + popToReg(desc, TempReg); + + /* Cunning trick by LPD. If true and false are contiguous subtract the smaller. + Correct result is either 0 or the distance between them. If result is not 0 or + their distance send mustBeBoolean. */ + assert((objectAfter(falseObject())) == (trueObject())); + + /* begin genSubConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(boolean)) + && ((oopisGreaterThan(boolean, classTableRootObj())) + || (oopisLessThan(boolean, nilObject())))) { + annotateobjRef(checkLiteralforInstruction(boolean, genoperandoperand(SubCwR, boolean, TempReg)), TempReg); + } + else { + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, boolean, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(boolean, BytesPerOop)); + } + } + jumpTarget = ensureFixupAt(eventualTarget); + + /* begin JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)jumpTarget)); + if (((extA & 1) != 0)) { + extA = 0; + abstractInstruction = lastOpcode(); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + return 0; + } + extA = 0; + quickConstant = (boolean == (falseObject()) + ? (trueObject()) - (falseObject()) + : (falseObject()) - (trueObject())); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + ok = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + genCallMustBeBooleanFor(boolean); + jmpTarget(ok, annotateBytecode(genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return 0; +} + + /* StackToRegisterMappingCogit>>#genJumpTo: */ +static NoDbgRegParms sqInt +genJumpTo(sqInt targetBytecodePC) +{ + sqInt eventualTarget; + BytecodeFixup *fixup; + BytecodeDescriptor *generator; + sqInt i; + + eventualTarget = eventualTargetOf(targetBytecodePC); + if ((eventualTarget > bytecodePC) + && ((/* stackTopIsBoolean */ + (simStackPtr >= methodOrBlockNumArgs) + && (stackEntryIsBoolean(ssTop()))) + && (isConditionalBranch(generator = generatorForPC(eventualTarget))))) { + eventualTarget = (eventualTarget + ((generator->numBytes))) + ((((generator->isBranchTrue)) == ((((ssTop())->constant)) == (trueObject())) + ? ((generator->spanFunction))(generator, eventualTarget, 0, methodObj) + : 0)); + ssPop(1); + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + fixup = ensureFixupAt(eventualTarget); + ssPop(-1); + } + else { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + fixup = ensureFixupAt(eventualTarget); + } + + /* can't fall through */ + deadCode = 1; + + /* Jump: */ + genoperand(Jump, ((sqInt)fixup)); + return 0; +} + + /* StackToRegisterMappingCogit>>#genMarshalledSend:numArgs:sendTable: */ +static NoDbgRegParms sqInt +genMarshalledSendnumArgssendTable(sqInt selectorIndex, sqInt numArgs, sqInt *sendTable) +{ + AbstractInstruction *anInstruction; + sqInt annotation; + AbstractInstruction *instruction; + + assert(needsFrame); + + /* begin annotationForSendTable: */ + if (sendTable == ordinarySendTrampolines) { + annotation = IsSendCall; + goto l1; + } + if (sendTable == directedSuperSendTrampolines) { + annotation = IsDirectedSuperSend; + goto l1; + } + if (sendTable == directedSuperBindingSendTrampolines) { + annotation = IsDirectedSuperBindingSend; + goto l1; + } + assert(sendTable == superSendTrampolines); + annotation = IsSuperSend; + /* end annotationForSendTable: */ +l1: + + /* Deal with stale super sends; see SpurMemoryManager's class comment. */ + if (/* annotationIsForUncheckedEntryPoint: */ + (annotation == IsSuperSend) + || (((annotation >= IsDirectedSuperSend) && (annotation <= IsDirectedSuperBindingSend)))) { + /* begin genEnsureOopInRegNotForwarded:scratchReg: */ + instruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(ReceiverResultReg, TempReg, instruction, 0); + } + + /* 0 through (NumSendTrampolines - 2) numArgs sends have the arg count implciti in the trampoline. + The last send trampoline (NumSendTrampolines - 1) passes numArgs in SendNumArgsReg. */ + if (numArgs >= (NumSendTrampolines - 1)) { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, numArgs, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(numArgs, BytesPerOop)); + } + } + if (((annotation >= IsDirectedSuperSend) && (annotation <= IsDirectedSuperBindingSend))) { + /* begin genMoveConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(tempOop)) + && ((oopisGreaterThan(tempOop, classTableRootObj())) + || (oopisLessThan(tempOop, nilObject())))) { + annotateobjRef(checkLiteralforInstruction(tempOop, genoperandoperand(MoveCwR, tempOop, TempReg)), tempOop); + } + else { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, tempOop, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(tempOop, BytesPerOop)); + } + } + } + genLoadInlineCacheWithSelector(selectorIndex); + ((genoperand(Call, sendTable[((numArgs < (NumSendTrampolines - 1)) ? numArgs : (NumSendTrampolines - 1))]))->annotation = annotation); + + /* begin voidReceiverOptStatus */ + ((simSelf())->liveRegister = NoReg); + return ssPushRegister(ReceiverResultReg); +} + + +/* Generate the abort for a method. This abort performs either a call of + ceSICMiss: to handle a single-in-line cache miss or a call of + ceStackOverflow: to handle a + stack overflow. It distinguishes the two by testing ResultReceiverReg. If + the register is zero then this is a stack-overflow because a) the receiver + has already + been pushed and so can be set to zero before calling the abort, and b) the + receiver must always contain an object (and hence be non-zero) on SIC + miss. */ + + /* StackToRegisterMappingCogit>>#genMethodAbortTrampolineFor: */ +static NoDbgRegParms usqInt +genMethodAbortTrampolineFor(sqInt numArgs) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpSICMiss; + + zeroOpcodeIndex(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, 0, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + jumpSICMiss = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* The abort sequence has pushed the LinkReg a second time - because a stack + overflow can only happen after building a frame, which pushes LinkReg anyway, and + we still need to push LinkReg in case we get to this routine from a sendMissAbort. + (On ARM there is a simpler way; use two separate abort calls since all instructions are 32-bits + but on x86 the zero receiver reg, call methodAbort sequence is smaller; we may fix this one day). + Overwrite that duplicate with the right one - the return address for the call to the abort trampoline. + The only reason it matters is an assert in ceStackOverflow: uses it */ + + /* begin MoveR:Mw:r: */ + /* begin gen:operand:quickConstant:operand: */ + anInstruction = genoperandoperandoperand(MoveRMwr, LinkReg, 0, SPReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(0, BytesPerOop)); + } + compileTrampolineFornumArgsargargargargregsToSavepushLinkRegresultReg(ceStackOverflow, 1, SendNumArgsReg, null, null, null, 0 /* emptyRegisterMask */, 0, NoReg); + + /* The LinkReg has already been set above. */ + jmpTarget(jumpSICMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genPushRegisterArgsForAbortMissNumArgs(backEnd, numArgs); + return genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceSICMiss, trampolineNamenumRegArgs("ceMethodAbort", numArgs), 1, ReceiverResultReg, null, null, null, 0 /* emptyRegisterMask */, 0, NoReg, 1); +} + + +/* Generate the abort for a PIC. This abort performs either a call of + ceInterpretMethodFromPIC:receiver: to handle invoking an uncogged + target or a call of ceMNUFromPICMNUMethod:receiver: to handle an + MNU dispatch in a closed PIC. It distinguishes the two by testing + ClassReg. If the register is zero then this is an MNU. */ + + /* StackToRegisterMappingCogit>>#genPICAbortTrampolineFor: */ +static NoDbgRegParms usqInt +genPICAbortTrampolineFor(sqInt numArgs) +{ + zeroOpcodeIndex(); + genPushRegisterArgsForAbortMissNumArgs(backEnd, numArgs); + return genInnerPICAbortTrampoline(trampolineNamenumRegArgs("cePICAbort", numArgs)); +} + + /* StackToRegisterMappingCogit>>#genPICMissTrampolineFor: */ +static NoDbgRegParms usqInt +genPICMissTrampolineFor(sqInt numArgs) +{ + usqInt startAddress; + + startAddress = methodZoneBase; + zeroOpcodeIndex(); + + /* N.B. a closed PIC jumps to the miss routine, not calls it, so there is only one retpc on the stack. */ + genPushRegisterArgsForNumArgsscratchReg(backEnd, numArgs, SendNumArgsReg); + genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCPICMissreceiver, trampolineNamenumRegArgs("cePICMiss", numArgs), 2, ClassReg, ReceiverResultReg, null, null, 0 /* emptyRegisterMask */, 1, NoReg, 1); + return startAddress; +} + + /* StackToRegisterMappingCogit>>#genPopStackBytecode */ +static sqInt +genPopStackBytecode(void) +{ + AbstractInstruction *anInstruction; + + if (((ssTop())->spilled)) { + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, BytesPerWord, SPReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BytesPerWord, BytesPerOop)); + } + } + ssPop(1); + return 0; +} + + +/* Check the argument count. Fail if wrong. + Get the method from the outerContext and see if it is cogged. If so, jump + to the + block entry or the no-context-switch entry, as appropriate, and we're + done. If not, + invoke the interpreter primitive. */ +/* Check the argument count. Fail if wrong. + Get the method from the outerContext and see if it is cogged. If so, jump + to the + block entry or the no-context-switch entry, as appropriate, and we're + done. If not, + invoke the interpreter primitive. + Override to push the register args first. */ + + /* StackToRegisterMappingCogit>>#genPrimitiveClosureValue */ +static sqInt +genPrimitiveClosureValue(void) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpBCMethod; + AbstractInstruction *jumpFail1; + AbstractInstruction *jumpFail2; + AbstractInstruction *jumpFail3; + AbstractInstruction *jumpFail4; + AbstractInstruction *jumpFailNArgs; + sqInt offset; + void (*primitiveRoutine)(void); + sqInt quickConstant; + sqInt result; + + genPushRegisterArgs(); + genLoadSlotsourceRegdestReg(ClosureNumArgsIndex, ReceiverResultReg, TempReg); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, (((usqInt)methodOrBlockNumArgs << 3) | 1), TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize((((usqInt)methodOrBlockNumArgs << 3) | 1), BytesPerOop)); + } + jumpFailNArgs = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genLoadSlotsourceRegdestReg(ClosureOuterContextIndex, ReceiverResultReg, ClassReg); + jumpFail1 = genJumpImmediate(ClassReg); + genGetCompactClassIndexNonImmOfinto(ClassReg, TempReg); + genCmpClassMethodContextCompactIndexR(TempReg); + jumpFail2 = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* We defer unforwarding the receiver to the prologue; scanning blocks + for inst var refs and only unforwarding if the block refers to inst vars. */ + genLoadSlotsourceRegdestReg(MethodIndex, ClassReg, SendNumArgsReg); + jumpFail3 = genJumpImmediate(SendNumArgsReg); + genGetFormatOfinto(SendNumArgsReg, TempReg); + quickConstant = firstCompiledMethodFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpFail4 = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + genLoadSlotsourceRegdestReg(HeaderIndex, SendNumArgsReg, ClassReg); + jumpBCMethod = genJumpImmediate(ClassReg); + offset = offsetof(CogMethod, blockEntryOffset); + + /* begin MoveM16:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveM16rR, offset, ClassReg, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* AddR:R: */ + genoperandoperand(AddRR, ClassReg, TempReg); + primitiveRoutine = functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(methodObj, primitiveIndex, null); + if (primitiveRoutine == primitiveClosureValueNoContextSwitch) { + if (!blockNoContextSwitchOffset) { + return NotFullyInitialized; + } + + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, blockNoContextSwitchOffset, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(blockNoContextSwitchOffset, BytesPerOop)); + } + } + + /* JumpR: */ + genoperand(JumpR, TempReg); + jmpTarget(jumpBCMethod, jmpTarget(jumpFail1, jmpTarget(jumpFail2, jmpTarget(jumpFail3, jmpTarget(jumpFail4, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))))); + if (((result = compileInterpreterPrimitiveflags(primitiveRoutine, primitivePropertyFlagsprimIndex(methodObj, primitiveIndex)))) < 0) { + return result; + } + jmpTarget(jumpFailNArgs, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + +/* Check the argument count. Fail if wrong. + Get the method from the outerContext and see if it is cogged. If so, jump + to the + block entry or the no-context-switch entry, as appropriate, and we're + done. If not, + invoke the interpreter primitive. */ +/* Override to push the register args first. */ + + /* StackToRegisterMappingCogit>>#genPrimitiveFullClosureValue */ +static sqInt +genPrimitiveFullClosureValue(void) +{ + AbstractInstruction *anInstruction; + AbstractInstruction *jumpBCMethod; + AbstractInstruction *jumpFail4; + AbstractInstruction *jumpFailImmediateMethod; + AbstractInstruction *jumpFailNArgs; + void (*primitiveRoutine)(void); + sqInt quickConstant; + sqInt result; + + genPushRegisterArgs(); + genLoadSlotsourceRegdestReg(ClosureNumArgsIndex, ReceiverResultReg, TempReg); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, (((usqInt)methodOrBlockNumArgs << 3) | 1), TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize((((usqInt)methodOrBlockNumArgs << 3) | 1), BytesPerOop)); + } + jumpFailNArgs = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* We defer unforwarding the receiver to the prologue; scanning blocks + for inst var refs and only unforwarding if the block refers to inst vars. */ + genLoadSlotsourceRegdestReg(FullClosureCompiledBlockIndex, ReceiverResultReg, SendNumArgsReg); + jumpFailImmediateMethod = genJumpImmediate(SendNumArgsReg); + genGetFormatOfinto(SendNumArgsReg, TempReg); + quickConstant = firstCompiledMethodFormat(); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, quickConstant, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + jumpFail4 = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + genLoadSlotsourceRegdestReg(HeaderIndex, SendNumArgsReg, ClassReg); + jumpBCMethod = genJumpImmediate(ClassReg); + primitiveRoutine = functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(methodObj, primitiveIndex, null); + quickConstant = (primitiveRoutine == primitiveFullClosureValueNoContextSwitch + ? fullBlockNoContextSwitchEntryOffset() + : fullBlockEntryOffset()); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, quickConstant, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(quickConstant, BytesPerOop)); + } + + /* JumpR: */ + genoperand(JumpR, ClassReg); + jmpTarget(jumpBCMethod, jmpTarget(jumpFailImmediateMethod, jmpTarget(jumpFail4, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + if (((result = compileInterpreterPrimitiveflags(primitiveRoutine, primitivePropertyFlagsprimIndex(methodObj, primitiveIndex)))) < 0) { + return result; + } + jmpTarget(jumpFailNArgs, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + +/* Generate an in-line perform primitive. The lookup code requires the + selector to be in Arg0Reg. + adjustArgumentsForPerform: adjusts the arguments once + genLookupForPerformNumArgs: has generated the code for the lookup. */ + + /* StackToRegisterMappingCogit>>#genPrimitivePerform */ +static sqInt +genPrimitivePerform(void) +{ + AbstractInstruction *anInstruction; + sqInt offset; + + if (methodOrBlockNumArgs > (numRegArgs())) { + offset = (methodOrBlockNumArgs - 1) * BytesPerWord; + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, offset, SPReg, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + } + return genLookupForPerformNumArgs(methodOrBlockNumArgs); +} + + +/* Generate an in-line perform:withArguments: primitive. The lookup code + requires the selector to be in Arg0Reg + and the array to be in Arg1Reg. The primitive will only handle cases 0 to + numRegArgs. Is it worth it you ask? + Here are arguemnt count requencies for a short run of Croquet/Virtend + which show that even for V3, with only + one rtegister arg, it very much is (but we wimp out on V3 cuz of the + complexity of checking the Array): + [0] = 253743 52.5% + [1] = 116117 24%/76.5% + [2] = 99876 20.7%/97.2% + [3] = 12837 2.7%/99.9% + [4] = 209 + [5] = 84 + [6] = 2 + [7] = 313 + [8] = 0 + [9] = 0 + [10] = 0 + [11] = 1 + [12] = 0 + [13] = 0 + [14] = 0 + [15] = 0 */ + + /* StackToRegisterMappingCogit>>#genPrimitivePerformWithArguments */ +static sqInt +genPrimitivePerformWithArguments(void) +{ + AbstractInstruction *anInstruction; + sqInt cacheBaseReg; + AbstractInstruction *itsAHit; + AbstractInstruction *jumpBadNumArgs1; + AbstractInstruction *jumpBadNumArgs2; + AbstractInstruction *jumpClassMiss; + AbstractInstruction *jumpImmArray; + AbstractInstruction *jumpInterpret; + AbstractInstruction *jumpSelectorMiss; + sqInt offset; + + /* begin genLookupForPerformWithArguments */ + jumpImmArray = genJumpImmediate(Arg1Reg); + genGetInlineCacheClassTagFromintoforEntry(ReceiverResultReg, SendNumArgsReg, 0); + cacheBaseReg = NoReg; + + /* Do first of three probes. See CoInterpreter>>lookupInMethodCacheSel:classTag: */ + jumpSelectorMiss = compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(Arg0Reg, 0, cacheBaseReg); + jumpClassMiss = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Fetch the method, and check if it is cogged. */ + offset = (cacheBaseReg == NoReg + ? (methodCacheAddress()) + ((((usqInt)(MethodCacheMethod) << (shiftForWord())))) + : ((sqInt)((usqInt)(MethodCacheMethod) << (shiftForWord())))); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + itsAHit = genoperandoperandoperand(MoveMwrR, offset, ClassReg, SendNumArgsReg); + if (usesOutOfLineLiteral(itsAHit)) { + (itsAHit->dependent = locateLiteralsize(offset, BytesPerOop)); + } + + /* If the method is not compiled fall back on the interpreter primitive. */ + genLoadSlotsourceRegdestReg(HeaderIndex, SendNumArgsReg, ClassReg); + jumpInterpret = genJumpImmediate(ClassReg); + + /* check the argument count; if it's wrong fall back on the interpreter primitive. */ + + /* begin genLoadcmNumArgsOf:into: */ + offset = sizeof(((((CogMethod *) 0))->objectHeader)); + + /* begin MoveMb:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMbrR, offset, ClassReg, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + genGetRawSlotSizeOfNonImminto(Arg1Reg, TempReg); + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, SendNumArgsReg); + jumpBadNumArgs1 = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, numRegArgs(), SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(numRegArgs(), BytesPerOop)); + } + jumpBadNumArgs2 = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + + /* Fetch arguments and jump to the method's unchecked entry-point. */ + genFetchRegArgsForPerformWithArguments(TempReg); + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, cmNoCheckEntryOffset, ClassReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(cmNoCheckEntryOffset, BytesPerOop)); + } + + /* JumpR: */ + genoperand(JumpR, ClassReg); + + /* First probe missed. Do second of three probes. Shift hash right one and retry. */ + jmpTarget(jumpSelectorMiss, jmpTarget(jumpClassMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + jumpSelectorMiss = compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(Arg0Reg, 1, cacheBaseReg); + + /* JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)itsAHit)); + + /* Second probe missed. Do last probe. Shift hash right two and retry. */ + jmpTarget(jumpSelectorMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpSelectorMiss = compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(Arg0Reg, 2, cacheBaseReg); + + /* JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)itsAHit)); + + /* Last probe missed/not jitted/bad num args. Caller will generate the call to fall back on the interpreter primitive. */ + jmpTarget(jumpImmArray, jmpTarget(jumpSelectorMiss, jmpTarget(jumpInterpret, jmpTarget(jumpBadNumArgs1, jmpTarget(jumpBadNumArgs2, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))))); + return 0; +} + + /* StackToRegisterMappingCogit>>#genPushActiveContextBytecode */ +static sqInt +genPushActiveContextBytecode(void) +{ + assert(needsFrame); + voidReceiverResultRegContainsSelf(); + + /* begin ssAllocateCallReg:and:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(CallerSavedRegisterMask | (((1U << ReceiverResultReg) | (1U << SendNumArgsReg)) | (1U << ClassReg)), simStackPtr, simNativeStackPtr); + genGetActiveContextNumArgslargeinBlock(methodOrBlockNumArgs, methodNeedsLargeContext(methodObj), inBlock); + return ssPushRegister(ReceiverResultReg); +} + + +/* Block compilation. At this point in the method create the block. Note its + start and defer generating code for it until after the method and any + other preceding + blocks. The block's actual code will be compiled later. */ +/* 143 10001111 llllkkkk jjjjjjjj iiiiiiii Push Closure Num Copied llll Num + Args kkkk BlockSize jjjjjjjjiiiiiiii */ + + /* StackToRegisterMappingCogit>>#genPushClosureCopyCopiedValuesBytecode */ +static sqInt +genPushClosureCopyCopiedValuesBytecode(void) +{ + sqInt i; + usqInt numArgs; + sqInt numCopied; + sqInt reg; + sqInt startpc; + + assert(needsFrame); + startpc = bytecodePC + (((generatorAt(byte0))->numBytes)); + addBlockStartAtnumArgsnumCopiedspan(startpc, (numArgs = byte1 & 15), (numCopied = ((usqInt)(byte1)) >> 4), ((((usqInt)(byte2) << 8))) + byte3); + + /* 0 relative */ + + /* begin genInlineClosure:numArgs:numCopied: */ + assert(getActiveContextAllocatesInMachineCode()); + voidReceiverResultRegContainsSelf(); + + /* begin ssAllocateCallReg:and:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(CallerSavedRegisterMask | (((1U << ReceiverResultReg) | (1U << SendNumArgsReg)) | (1U << ClassReg)), simStackPtr, simNativeStackPtr); + genNoPopCreateClosureAtnumArgsnumCopiedcontextNumArgslargeinBlock(startpc + 1, numArgs, numCopied, methodOrBlockNumArgs, methodNeedsLargeContext(methodObj), inBlock); + + /* 1 relative */ + for (i = 1; i <= numCopied; i += 1) { + reg = ssStorePoptoPreferredReg(1, TempReg); + genStoreSourceRegslotIndexintoNewObjectInDestReg(reg, (ClosureFirstCopiedValueIndex + numCopied) - i, ReceiverResultReg); + } + ssPushRegister(ReceiverResultReg); + return 0; +} + + +/* */ +/* Override to avoid the BytecodeSetHasDirectedSuperSend check, which is + unnecessary here given the simulation stack. */ + + /* StackToRegisterMappingCogit>>#genPushLiteralIndex: */ +static NoDbgRegParms sqInt +genPushLiteralIndex(sqInt literalIndex) +{ + sqInt literal; + + literal = getLiteral(literalIndex); + return ssPushConstant(literal); +} + + /* StackToRegisterMappingCogit>>#genPushLiteralVariable: */ +static NoDbgRegParms sqInt +genPushLiteralVariable(sqInt literalIndex) +{ + AbstractInstruction *anInstruction; + sqInt association; + sqInt bcpc; + BytecodeDescriptor *descriptor1; + sqInt eA; + sqInt eB; + sqInt freeReg; + sqInt savedB0; + sqInt savedB1; + sqInt savedB2; + sqInt savedB3; + sqInt savedEA; + sqInt savedEB; + sqInt savedNEB; + + association = getLiteral(literalIndex); + + /* If followed by a directed super send bytecode, avoid generating any code yet. + The association will be passed to the directed send trampoline in a register + and fully dereferenced only when first linked. It will be ignored in later sends. */ + assert(!(directedSendUsesBinding)); + + /* begin nextDescriptorExtensionsAndNextPCInto: */ + descriptor1 = generatorAt(byte0); + savedB0 = byte0; + savedB1 = byte1; + savedB2 = byte2; + savedB3 = byte3; + savedEA = extA; + savedEB = extB; + savedNEB = numExtB; + bcpc = bytecodePC + ((descriptor1->numBytes)); + do { + if (bcpc > endPC) { + goto l1; + } + descriptor1 = loadBytesAndGetDescriptor(bcpc); + if (!((descriptor1->isExtension))) { + eA = extA; + eB = extB; + extA = savedEA; + extB = savedEB; + numExtB = savedNEB; + byte0 = savedB0; + byte1 = savedB1; + byte2 = savedB2; + byte3 = savedB3; + if (/* isDirectedSuper:extA:extB: */ + (descriptor1) + && ((((descriptor1->generator)) == genExtSendSuperBytecode) + && (eB >= 64))) { + ssPushConstant(association); + directedSendUsesBinding = 1; + return 0; + } + goto l1; + } + ((descriptor1->generator))(); + bcpc += (descriptor1->numBytes); + } while(1); + /* end nextDescriptorExtensionsAndNextPCInto: */ +l1: + freeReg = allocateRegNotConflictingWith(0); + + /* N.B. Do _not_ use ReceiverResultReg to avoid overwriting receiver in assignment in frameless methods. + So far descriptors are not rich enough to describe the entire dereference so generate the register + load but don't push the result. There is an order-of-evaluation issue if we defer the dereference. */ + + /* begin genMoveConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(association)) + && ((oopisGreaterThan(association, classTableRootObj())) + || (oopisLessThan(association, nilObject())))) { + annotateobjRef(checkLiteralforInstruction(association, genoperandoperand(MoveCwR, association, TempReg)), association); + } + else { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, association, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(association, BytesPerOop)); + } + } + genLoadSlotsourceRegdestReg(ValueIndex, TempReg, freeReg); + ssPushRegister(freeReg); + return 0; +} + + /* StackToRegisterMappingCogit>>#genPushMaybeContextReceiverVariable: */ +static NoDbgRegParms sqInt +genPushMaybeContextReceiverVariable(sqInt slotIndex) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + AbstractInstruction *jmpDone; + AbstractInstruction *jmpSingle; + + /* begin ssAllocateCallReg:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(CallerSavedRegisterMask | (((1U << ReceiverResultReg)) | ((1U << SendNumArgsReg))), simStackPtr, simNativeStackPtr); + ensureReceiverResultRegContainsSelf(); + + /* begin genPushMaybeContextSlotIndex: */ + assert(needsFrame); + if (((CallerSavedRegisterMask & ((1U << ReceiverResultReg))) != 0)) { + /* We have no way of reloading ReceiverResultReg since we need the inst var value as the result. */ + voidReceiverResultRegContainsSelf(); + } + + /* See CoInterpreter>>contextInstructionPointer:frame: for an explanation + of the instruction pointer slot handling. */ + if (slotIndex == InstructionPointerIndex) { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, slotIndex, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(slotIndex, BytesPerOop)); + } + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceFetchContextInstVarTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + return ssPushRegister(SendNumArgsReg); + } + genLoadSlotsourceRegdestReg(SenderIndex, ReceiverResultReg, TempReg); + jmpSingle = genJumpNotSmallInteger(TempReg); + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, slotIndex, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(slotIndex, BytesPerOop)); + } + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceFetchContextInstVarTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jmpSingle, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genLoadSlotsourceRegdestReg(slotIndex, ReceiverResultReg, SendNumArgsReg); + jmpTarget(jmpDone, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return ssPushRegister(SendNumArgsReg); +} + + /* StackToRegisterMappingCogit>>#genPushNewArrayBytecode */ +static sqInt +genPushNewArrayBytecode(void) +{ + sqInt i; + sqInt iSqInt; + int popValues; + usqInt size; + + assert(needsFrame); + voidReceiverResultRegContainsSelf(); + if ((popValues = byte1 > 0x7F)) { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (iSqInt = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); iSqInt <= simStackPtr; iSqInt += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(iSqInt), frameOffsetOfTemporary(iSqInt - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + } + else { + /* begin ssAllocateCallReg:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(CallerSavedRegisterMask | (((1U << SendNumArgsReg)) | ((1U << ReceiverResultReg))), simStackPtr, simNativeStackPtr); + } + size = byte1 & 0x7F; + if (!popValues) { + if (tryCollapseTempVectorInitializationOfSize(size)) { + return 0; + } + } + genNewArrayOfSizeinitialized(size, !popValues); + if (popValues) { + for (i = (size - 1); i >= 0; i += -1) { + /* PopR: */ + genoperand(PopR, TempReg); + genStoreSourceRegslotIndexintoNewObjectInDestReg(TempReg, i, ReceiverResultReg); + } + ssPop(size); + } + return ssPushRegister(ReceiverResultReg); +} + + /* StackToRegisterMappingCogit>>#genPushReceiverBytecode */ +static sqInt +genPushReceiverBytecode(void) +{ + if ((((simSelf())->liveRegister)) == ReceiverResultReg) { + return ssPushRegister(ReceiverResultReg); + } + return ssPushDesc(ssSelfDescriptor()); +} + + /* StackToRegisterMappingCogit>>#genPushReceiverVariable: */ +static NoDbgRegParms sqInt +genPushReceiverVariable(sqInt index) +{ + ensureReceiverResultRegContainsSelf(); + return ssPushBaseoffset(ReceiverResultReg, slotOffsetOfInstVarIndex(index)); +} + + +/* Ensure that the register args are pushed before the retpc for methods with + arity <= self numRegArgs. + */ +/* This isn't as clumsy on a RISC. But putting the receiver and + args above the return address means the CoInterpreter has a + single machine-code frame format which saves us a lot of work. */ + + /* StackToRegisterMappingCogit>>#genPushRegisterArgs */ +static void +genPushRegisterArgs(void) +{ + if (!(regArgsHaveBeenPushed + || (methodOrBlockNumArgs > (numRegArgs())))) { + genPushRegisterArgsForNumArgsscratchReg(backEnd, methodOrBlockNumArgs, SendNumArgsReg); + regArgsHaveBeenPushed = 1; + } +} + + /* StackToRegisterMappingCogit>>#genPushRemoteTempLongBytecode */ +static sqInt +genPushRemoteTempLongBytecode(void) +{ + AbstractInstruction *anInstruction; + sqInt offset; + sqInt remoteTempReg; + sqInt tempVectReg; + + tempVectReg = allocateRegNotConflictingWith(0); + offset = frameOffsetOfTemporary(byte2); + + /* begin MoveMw:r:R: */ + /* begin gen:quickConstant:operand:operand: */ + anInstruction = genoperandoperandoperand(MoveMwrR, offset, FPReg, tempVectReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(offset, BytesPerOop)); + } + remoteTempReg = availableRegisterOrNoneFor(backEnd, (liveRegisters()) | (((tempVectReg < 0) ? (((usqInt)(1)) >> (-tempVectReg)) : (1ULL << tempVectReg)))); + if (remoteTempReg == NoReg) { + remoteTempReg = tempVectReg; + } + genLoadSlotsourceRegdestReg(byte1, tempVectReg, remoteTempReg); + return ssPushRegister(remoteTempReg); +} + + +/* If a frameless method (not a block), only argument temps can be accessed. + This is assured by the use of needsFrameIfMod16GENumArgs: in pushTemp. */ + + /* StackToRegisterMappingCogit>>#genPushTemporaryVariable: */ +static NoDbgRegParms sqInt +genPushTemporaryVariable(sqInt index) +{ + assert((inBlock > 0) + || (needsFrame + || (index < methodOrBlockNumArgs))); + return ssPushDesc(simStack[index + 1]); +} + + +/* In a frameless method ReceiverResultReg already contains self. + In a frameful method, ReceiverResultReg /may/ contain self. */ + + /* StackToRegisterMappingCogit>>#genReturnReceiver */ +static sqInt +genReturnReceiver(void) +{ + if (needsFrame) { + if (!((((simSelf())->liveRegister)) == ReceiverResultReg)) { + /* begin putSelfInReceiverResultReg */ + storeToReg(simSelf(), ReceiverResultReg); + } + } + return genUpArrowReturn(); +} + + /* StackToRegisterMappingCogit>>#genReturnTopFromBlock */ +static sqInt +genReturnTopFromBlock(void) +{ + assert(inBlock > 0); + popToReg(ssTop(), ReceiverResultReg); + ssPop(1); + return genBlockReturn(); +} + + /* StackToRegisterMappingCogit>>#genReturnTopFromMethod */ +static sqInt +genReturnTopFromMethod(void) +{ + popToReg(ssTop(), ReceiverResultReg); + ssPop(1); + return genUpArrowReturn(); +} + + /* StackToRegisterMappingCogit>>#genSendDirectedSuper:numArgs: */ +static NoDbgRegParms sqInt +genSendDirectedSupernumArgs(sqInt selectorIndex, sqInt numArgs) +{ + sqInt result; + + assert((((ssTop())->type)) == SSConstant); + tempOop = ((ssTop())->constant); + ssPop(1); + marshallSendArguments(numArgs); + result = genMarshalledSendnumArgssendTable(selectorIndex, numArgs, (directedSendUsesBinding + ? directedSuperBindingSendTrampolines + : directedSuperSendTrampolines)); + directedSendUsesBinding = 0; + return result; +} + + /* StackToRegisterMappingCogit>>#genSendSuper:numArgs: */ +static NoDbgRegParms sqInt +genSendSupernumArgs(sqInt selectorIndex, sqInt numArgs) +{ + marshallSendArguments(numArgs); + return genMarshalledSendnumArgssendTable(selectorIndex, numArgs, superSendTrampolines); +} + + +/* Generate a trampoline with four arguments. + Hack: a negative value indicates an abstract register, a non-negative + value indicates a constant. */ + + /* StackToRegisterMappingCogit>>#genSendTrampolineFor:numArgs:called:arg:arg:arg:arg: */ +static NoDbgRegParms usqInt +genSendTrampolineFornumArgscalledargargargarg(void *aRoutine, sqInt numArgs, char *aString, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3) +{ + sqInt routine; + usqInt startAddress; + + startAddress = methodZoneBase; + zeroOpcodeIndex(); + genPushRegisterArgsForNumArgsscratchReg(backEnd, numArgs, SendNumArgsReg); + + /* If we're on a 64-bit platform we use 32-bit inline caches. These don't have room for a selector oop, only + a selector index. If so, call the selectorIndexDereferenceRoutine to convert the index into a selector oop. */ + if ((routine = ceDereferenceSelectorIndex)) { + /* Explicitly save LinkReg via ExtraReg2; it's presumably faster than pushing/popping */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, LinkReg, Extra2Reg); + + /* Call: */ + genoperand(Call, routine); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Extra2Reg, LinkReg); + } + genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(aRoutine, aString, 4, regOrConst0, regOrConst1, regOrConst2, regOrConst3, 0 /* emptyRegisterMask */, 1, NoReg, 1); + return startAddress; +} + + /* StackToRegisterMappingCogit>>#genSend:numArgs: */ +static NoDbgRegParms sqInt +genSendnumArgs(sqInt selectorIndex, sqInt numArgs) +{ + marshallSendArguments(numArgs); + return genMarshalledSendnumArgssendTable(selectorIndex, numArgs, ordinarySendTrampolines); +} + + /* StackToRegisterMappingCogit>>#genSpecialSelectorArithmetic */ +static sqInt +genSpecialSelectorArithmetic(void) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + sqInt argInt; + int argIsConst; + sqInt argIsInt; + sqInt i; + sqInt index; + AbstractInstruction *jumpContinue; + AbstractInstruction *jumpNotSmallInts; + BytecodeDescriptor *primDescriptor; + sqInt rcvrInt; + int rcvrIsConst; + sqInt rcvrIsInt; + sqInt result; + SimStackEntry *selfOfIsSameEntryAs; + + primDescriptor = generatorAt(byte0); + argIsInt = ((argIsConst = (((ssTop())->type)) == SSConstant)) + && ((((((argInt = ((ssTop())->constant)))) & 7) == 1)); + rcvrIsInt = (((rcvrIsConst = (((ssValue(1))->type)) == SSConstant)) + && ((((((rcvrInt = ((ssValue(1))->constant)))) & 7) == 1))) + || ((mclassIsSmallInteger()) + && (((selfOfIsSameEntryAs = ssValue(1)), + isSameEntryAs(selfOfIsSameEntryAs, simSelf())))); + if (argIsInt + && (rcvrIsInt + && (rcvrIsConst))) { + rcvrInt = (rcvrInt >> 3); + argInt = (argInt >> 3); + switch ((primDescriptor->opcode)) { + case AddRR: + result = rcvrInt + argInt; + break; + case SubRR: + result = rcvrInt - argInt; + break; + case AndRR: + result = rcvrInt & argInt; + break; + case OrRR: + result = rcvrInt | argInt; + break; + default: + error("Case not found and no otherwise clause"); + } + if (isIntegerValue(result)) { + /* Must annotate the bytecode for correct pc mapping. */ + ssPop(2); + return ssPushAnnotatedConstant((((usqInt)result << 3) | 1)); + } + return genSpecialSelectorSend(); + } + + /* If there's any constant involved other than a SmallInteger don't attempt to inline. */ + if ((rcvrIsConst + && (!rcvrIsInt)) + || (argIsConst + && (!argIsInt))) { + return genSpecialSelectorSend(); + } + + /* If we know nothing about the types then better not to inline as the inline cache and + primitive code is not terribly slow so wasting time on duplicating tag tests is pointless. */ + if (!(argIsInt + || (rcvrIsInt))) { + return genSpecialSelectorSend(); + } + if (argIsInt) { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= (simStackPtr - 2)) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < (simStackPtr - 2)) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : (simStackPtr - 2))); i <= (simStackPtr - 2); i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = (simStackPtr - 2) + 1; + } + popToReg(ssValue(1), ReceiverResultReg); + ssPop(2); + } + else { + marshallSendArguments(1); + } + jumpNotSmallInts = (rcvrIsInt + && (argIsInt) + ? ((AbstractInstruction *) null) + : (argIsInt + ? genJumpNotSmallInteger(ReceiverResultReg) + : (rcvrIsInt + ? genJumpNotSmallInteger(Arg0Reg) + : genJumpNotSmallIntegersInandscratch(ReceiverResultReg, Arg0Reg, TempReg)))); + switch ((primDescriptor->opcode)) { + case AddRR: + if (argIsInt) { + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, argInt - ConstZero, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(argInt - ConstZero, BytesPerOop)); + } + jumpContinue = genConditionalBranchoperand(JumpNoOverflow, ((sqInt)0)); + + /* overflow; must undo the damage before continuing */ + + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, argInt - ConstZero, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(argInt - ConstZero, BytesPerOop)); + } + } + else { + genRemoveSmallIntegerTagsInScratchReg(ReceiverResultReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg0Reg, ReceiverResultReg); + jumpContinue = genConditionalBranchoperand(JumpNoOverflow, ((sqInt)0)); + + /* overflow; must undo the damage before continuing */ + if (rcvrIsInt + && (rcvrIsConst)) { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, rcvrInt, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(rcvrInt, BytesPerOop)); + } + } + else { + /* SubR:R: */ + genoperandoperand(SubRR, Arg0Reg, ReceiverResultReg); + genSetSmallIntegerTagsIn(ReceiverResultReg); + } + } + break; + case SubRR: + if (argIsInt) { + /* begin SubCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(SubCqR, argInt - ConstZero, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(argInt - ConstZero, BytesPerOop)); + } + jumpContinue = genConditionalBranchoperand(JumpNoOverflow, ((sqInt)0)); + + /* overflow; must undo the damage before continuing */ + + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, argInt - ConstZero, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(argInt - ConstZero, BytesPerOop)); + } + } + else { + genRemoveSmallIntegerTagsInScratchReg(Arg0Reg); + + /* SubR:R: */ + genoperandoperand(SubRR, Arg0Reg, ReceiverResultReg); + jumpContinue = genConditionalBranchoperand(JumpNoOverflow, ((sqInt)0)); + + /* overflow; must undo the damage before continuing */ + + /* AddR:R: */ + genoperandoperand(AddRR, Arg0Reg, ReceiverResultReg); + genSetSmallIntegerTagsIn(Arg0Reg); + } + break; + case AndRR: + if (argIsInt) { + /* begin AndCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AndCqR, argInt, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(argInt, BytesPerOop)); + } + } + else { + /* AndR:R: */ + genoperandoperand(AndRR, Arg0Reg, ReceiverResultReg); + } + jumpContinue = (jumpNotSmallInts + ? genoperand(Jump, ((sqInt)0)) + : ((AbstractInstruction *) null)); + break; + case OrRR: + if (argIsInt) { + /* begin OrCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(OrCqR, argInt, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(argInt, BytesPerOop)); + } + } + else { + /* OrR:R: */ + genoperandoperand(OrRR, Arg0Reg, ReceiverResultReg); + } + jumpContinue = (jumpNotSmallInts + ? genoperand(Jump, ((sqInt)0)) + : ((AbstractInstruction *) null)); + break; + default: + error("Case not found and no otherwise clause"); + } + if (jumpNotSmallInts) { + jmpTarget(jumpNotSmallInts, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } + else { + if (!jumpContinue) { + /* begin annotateInstructionForBytecode */ + abstractInstruction = (prevInstIsPCAnnotated() + ? gen(Nop) + : genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + ssPushRegister(ReceiverResultReg); + return 0; + } + } + if (argIsInt) { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, argInt, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(argInt, BytesPerOop)); + } + } + index = byte0 - (/* firstSpecialSelectorBytecodeOffset */ + (bytecodeSetOffset == 0x100 + ? AltFirstSpecialSelector + 0x100 + : FirstSpecialSelector)); + genMarshalledSendnumArgssendTable((-index) - 1, 1, ordinarySendTrampolines); + jmpTarget(jumpContinue, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + /* StackToRegisterMappingCogit>>#genSpecialSelectorClass */ +static sqInt +genSpecialSelectorClass(void) +{ + sqInt requiredReg1; + sqInt topReg; + + topReg = registerOrNone(ssTop()); + ssPop(1); + if ((topReg == NoReg) + || (topReg == ClassReg)) { + requiredReg1 = (topReg = SendNumArgsReg); + + /* begin ssAllocateRequiredReg:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((((requiredReg1 < 0) ? (((usqInt)(1)) >> (-requiredReg1)) : (1ULL << requiredReg1))) | ((1U << ClassReg)), simStackPtr, simNativeStackPtr); + } + else { + /* begin ssAllocateRequiredReg: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << ClassReg), simStackPtr, simNativeStackPtr); + } + ssPush(1); + popToReg(ssTop(), topReg); + genGetClassObjectOfintoscratchRegmayBeAForwarder(topReg, ClassReg, TempReg, mayBeAForwarder(ssTop())); + ssPop(1); + return ssPushRegister(ClassReg); +} + + /* StackToRegisterMappingCogit>>#genSpecialSelectorComparison */ +static sqInt +genSpecialSelectorComparison(void) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + sqInt argInt; + sqInt argIsIntConst; + BytecodeDescriptor *branchDescriptor; + BytecodeDescriptor *branchDescriptor1; + sqInt i; + sqInt index; + sqInt inlineCAB; + AbstractInstruction *jumpNotSmallInts; + void *jumpTarget; + sqInt nExts; + sqInt nextPC; + sqInt nextPCSqInt; + sqInt postBranchPC; + sqInt postBranchPCSqInt; + BytecodeDescriptor *primDescriptor; + BytecodeDescriptor *primDescriptor1; + int rcvrIsConst; + sqInt rcvrIsInt; + SimStackEntry *selfOfIsSameEntryAs; + sqInt targetBytecodePC; + sqInt targetPC; + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= (simStackPtr - 2)) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < (simStackPtr - 2)) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : (simStackPtr - 2))); i <= (simStackPtr - 2); i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = (simStackPtr - 2) + 1; + } + primDescriptor = generatorAt(byte0); + argIsIntConst = ((((ssTop())->type)) == SSConstant) + && ((((((argInt = ((ssTop())->constant)))) & 7) == 1)); + rcvrIsInt = (((rcvrIsConst = (((ssValue(1))->type)) == SSConstant)) + && (((((((ssValue(1))->constant))) & 7) == 1))) + || ((mclassIsSmallInteger()) + && (((selfOfIsSameEntryAs = ssValue(1)), + isSameEntryAs(selfOfIsSameEntryAs, simSelf())))); + if (argIsIntConst + && (rcvrIsInt + && (rcvrIsConst))) { + return genStaticallyResolvedSpecialSelectorComparison(); + } + + /* begin extractMaybeBranchDescriptorInto: */ + branchDescriptor1 = ((BytecodeDescriptor *) 0); + primDescriptor1 = generatorAt(byte0); + nextPCSqInt = bytecodePC + ((primDescriptor1->numBytes)); + nExts = 0; + while (1) { + while (1) { + /* begin generatorForPC: */ + branchDescriptor1 = generatorAt(bytecodeSetOffset + (fetchByteofObject(nextPCSqInt, methodObj))); + if (!((branchDescriptor1->isExtension))) break; + nExts += 1; + nextPCSqInt += (branchDescriptor1->numBytes); + } + if (!(/* isUnconditionalBranch */ + (isBranch(branchDescriptor1)) + && (!(((branchDescriptor1->isBranchTrue)) + || ((branchDescriptor1->isBranchFalse)))))) break; + nextPCSqInt = eventualTargetOf((nextPCSqInt + ((branchDescriptor1->numBytes))) + (((branchDescriptor1->spanFunction))(branchDescriptor1, nextPCSqInt, nExts, methodObj))); + } + targetBytecodePC = (postBranchPCSqInt = 0); + if (((branchDescriptor1->isBranchTrue)) + || ((branchDescriptor1->isBranchFalse))) { + targetBytecodePC = eventualTargetOf((nextPCSqInt + ((branchDescriptor1->numBytes))) + (((branchDescriptor1->spanFunction))(branchDescriptor1, nextPCSqInt, nExts, methodObj))); + postBranchPCSqInt = eventualTargetOf(nextPCSqInt + ((branchDescriptor1->numBytes))); + } + else { + nextPCSqInt = bytecodePC + ((primDescriptor1->numBytes)); + } + branchDescriptor = branchDescriptor1; + nextPC = nextPCSqInt; + postBranchPC = postBranchPCSqInt; + targetPC = targetBytecodePC; + + /* Only interested in inlining if followed by a conditional branch. */ + inlineCAB = ((branchDescriptor->isBranchTrue)) + || ((branchDescriptor->isBranchFalse)); + + /* Further, only interested in inlining = and ~= if there's a SmallInteger constant involved. + The relational operators successfully statically predict SmallIntegers; the equality operators do not. */ + if (inlineCAB + && ((((primDescriptor->opcode)) == JumpZero) + || (((primDescriptor->opcode)) == JumpNonZero))) { + inlineCAB = argIsIntConst + || (rcvrIsInt); + } + if (!inlineCAB) { + return genSpecialSelectorSend(); + } + if (argIsIntConst) { + popToReg(ssValue(1), ReceiverResultReg); + ssPop(2); + } + else { + marshallSendArguments(1); + } + jumpNotSmallInts = (rcvrIsInt + && (argIsIntConst) + ? ((AbstractInstruction *) null) + : (argIsIntConst + ? genJumpNotSmallInteger(ReceiverResultReg) + : (rcvrIsInt + ? genJumpNotSmallInteger(Arg0Reg) + : genJumpNotSmallIntegersInandscratch(ReceiverResultReg, Arg0Reg, TempReg)))); + if (argIsIntConst) { + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, argInt, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(argInt, BytesPerOop)); + } + } + else { + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, ReceiverResultReg); + } + + /* Cmp is weird/backwards so invert the comparison. Further since there is a following conditional + jump bytecode define non-merge fixups and leave the cond bytecode to set the mergeness. */ + genConditionalBranchoperand(((branchDescriptor->isBranchTrue) + ? (primDescriptor->opcode) + : inverseBranchFor((primDescriptor->opcode))), ((usqInt)(ensureNonMergeFixupAt(targetPC)))); + jumpTarget = ensureNonMergeFixupAt(postBranchPC); + + /* begin Jump: */ + genoperand(Jump, ((sqInt)jumpTarget)); + if (!jumpNotSmallInts) { + /* begin annotateInstructionForBytecode */ + abstractInstruction = (prevInstIsPCAnnotated() + ? gen(Nop) + : genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + ensureFixupAt(postBranchPC); + ensureFixupAt(targetPC); + deadCode = 1; + return 0; + } + jmpTarget(jumpNotSmallInts, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + if (argIsIntConst) { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, argInt, Arg0Reg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(argInt, BytesPerOop)); + } + } + index = byte0 - (/* firstSpecialSelectorBytecodeOffset */ + (bytecodeSetOffset == 0x100 + ? AltFirstSpecialSelector + 0x100 + : FirstSpecialSelector)); + return genMarshalledSendnumArgssendTable((-index) - 1, 1, ordinarySendTrampolines); +} + + +/* Assumes both operands are ints */ + + /* StackToRegisterMappingCogit>>#genStaticallyResolvedSpecialSelectorComparison */ +static sqInt +genStaticallyResolvedSpecialSelectorComparison(void) +{ + sqInt argInt; + BytecodeDescriptor *primDescriptor; + sqInt rcvrInt; + int result; + + primDescriptor = generatorAt(byte0); + argInt = ((ssTop())->constant); + rcvrInt = ((ssValue(1))->constant); + + /* In Simulator ints are unsigned... */ + switch ((primDescriptor->opcode)) { + case JumpLess: + result = rcvrInt < argInt; + break; + case JumpLessOrEqual: + result = rcvrInt <= argInt; + break; + case JumpGreater: + result = rcvrInt > argInt; + break; + case JumpGreaterOrEqual: + result = rcvrInt >= argInt; + break; + case JumpZero: + result = rcvrInt == argInt; + break; + case JumpNonZero: + result = rcvrInt != argInt; + break; + default: + error("Case not found and no otherwise clause"); + } + + /* Must annotate the bytecode for correct pc mapping. */ + ssPop(2); + return ssPushAnnotatedConstant((result + ? trueObject() + : falseObject())); +} + + +/* We need a frame because the association has to be in ReceiverResultReg for + the various trampolines + and ReceiverResultReg holds only the receiver in frameless methods. + */ + + /* StackToRegisterMappingCogit>>#genStorePop:LiteralVariable:needsStoreCheck:needsImmutabilityCheck: */ +static NoDbgRegParms sqInt +genStorePopLiteralVariableneedsStoreCheckneedsImmutabilityCheck(sqInt popBoolean, sqInt litVarIndex, sqInt needsStoreCheck, sqInt needsImmCheck) +{ + AbstractInstruction *anInstruction; + sqInt association; + sqInt i; + sqInt topReg; + + assert(needsFrame); + + /* begin genLoadLiteralVariable:in: */ + association = getLiteral(litVarIndex); + voidReceiverResultRegContainsSelf(); + + /* begin ssAllocateRequiredReg: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << ReceiverResultReg), simStackPtr, simNativeStackPtr); + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(association)) + && ((oopisGreaterThan(association, classTableRootObj())) + || (oopisLessThan(association, nilObject())))) { + annotateobjRef(checkLiteralforInstruction(association, genoperandoperand(MoveCwR, association, ReceiverResultReg)), association); + } + else { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, association, ReceiverResultReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(association, BytesPerOop)); + } + } + + /* begin genGenericStorePop:slotIndex:destReg:needsStoreCheck:needsRestoreRcvr:needsImmutabilityCheck: */ +# if IMMUTABILITY + if (needsImmCheck) { + /* begin ssAllocateRequiredReg:upThrough: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << ClassReg), simStackPtr - 1, simNativeStackPtr); + ssStoreAndReplacePoptoReg(popBoolean, ClassReg); + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + return genStoreWithImmutabilityCheckSourceRegslotIndexdestRegscratchRegneedsStoreCheckneedRestoreRcvr(ClassReg, ValueIndex, ReceiverResultReg, TempReg, needsStoreCheck, 0); + } +# endif // IMMUTABILITY + + topReg = allocateRegForStackEntryAtnotConflictingWith(0, (1U << ReceiverResultReg)); + ssStorePoptoReg(popBoolean, topReg); + return genStoreSourceRegslotIndexdestRegscratchReginFrameneedsStoreCheck(topReg, ValueIndex, ReceiverResultReg, TempReg, needsFrame, needsStoreCheck); +} + + +/* The reason we need a frame here is that assigning to an inst var of a + context may + involve wholesale reorganization of stack pages, and the only way to + preserve the + execution state of an activation in that case is if it has a frame. */ + + /* StackToRegisterMappingCogit>>#genStorePop:MaybeContextReceiverVariable:needsStoreCheck:needsImmutabilityCheck: */ +static NoDbgRegParms sqInt +genStorePopMaybeContextReceiverVariableneedsStoreCheckneedsImmutabilityCheck(sqInt popBoolean, sqInt slotIndex, sqInt needsStoreCheck, sqInt needsImmCheck) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *anInstruction; + sqInt i; + AbstractInstruction *immutabilityFailure; + AbstractInstruction *mutableJump; + + assert(needsFrame); + ssFlushUpThroughReceiverVariable(slotIndex); + preserveTopInClassRegIfIsReceiverResultReg(); + ensureReceiverResultRegContainsSelf(); + + /* begin genGenericStorePop:MaybeContextSlotIndex:needsStoreCheck:needsRestoreRcvr:needsImmutabilityCheck: */ + immutabilityFailure = ((AbstractInstruction *) 0); + assert(needsFrame); +# if IMMUTABILITY + if (needsImmCheck) { + mutableJump = genJumpMutablescratchReg(ReceiverResultReg, TempReg); + + /* begin genStoreTrampolineCall: */ + assert(IMMUTABILITY); + if (slotIndex >= (NumStoreTrampolines - 1)) { + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, slotIndex, TempReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(slotIndex, BytesPerOop)); + } + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreTrampolines[NumStoreTrampolines - 1]); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreTrampolines[slotIndex]); + (abstractInstruction->annotation = IsRelativeCall); + } + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + + /* begin putSelfInReceiverResultReg */ + storeToReg(simSelf(), ReceiverResultReg); + immutabilityFailure = genoperand(Jump, ((sqInt)0)); + jmpTarget(mutableJump, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } +# endif // IMMUTABILITY + + ssPop(1); + + /* begin ssAllocateCallReg:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(CallerSavedRegisterMask | (((1U << ClassReg)) | ((1U << SendNumArgsReg))), simStackPtr, simNativeStackPtr); + ssPush(1); + genLoadSlotsourceRegdestReg(SenderIndex, ReceiverResultReg, TempReg); + ssStoreAndReplacePoptoReg(popBoolean, ClassReg); + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + + /* begin MoveCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(MoveCqR, slotIndex, SendNumArgsReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(slotIndex, BytesPerOop)); + } + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreContextInstVarTrampoline); + (abstractInstruction->annotation = IsRelativeCall); +# if IMMUTABILITY + if (needsImmCheck) { + jmpTarget(immutabilityFailure, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } +# endif + + return 0; +} + + /* StackToRegisterMappingCogit>>#genVanillaInlinedIdenticalOrNotIf: */ +static NoDbgRegParms sqInt +genVanillaInlinedIdenticalOrNotIf(sqInt orNot) +{ + AbstractInstruction *anInstruction; + int argIsConstant; + sqInt argNeedsReg; + sqInt argReg; + sqInt argRegSqInt; + BytecodeDescriptor *branchDescriptor; + BytecodeDescriptor *branchDescriptor1; + sqInt constant; + sqInt i; + void *jumpTarget; + sqInt nExts; + sqInt nextPC; + sqInt nextPCSqInt; + sqInt postBranchPC; + sqInt postBranchPCSqInt; + BytecodeDescriptor *primDescriptor; + sqInt rcvrIsConstant; + sqInt rcvrNeedsReg; + sqInt rcvrReg; + sqInt rcvrRegSqInt; + sqInt reg; + sqInt rNextSqInt; + sqInt rTopSqInt; + BytecodeFixup *self_in_CogBytecodeFixup; + sqInt targetBytecodePC; + sqInt targetPC; + sqInt topRegistersMask; + + /* begin extractMaybeBranchDescriptorInto: */ + branchDescriptor1 = ((BytecodeDescriptor *) 0); + primDescriptor = generatorAt(byte0); + nextPCSqInt = bytecodePC + ((primDescriptor->numBytes)); + nExts = 0; + while (1) { + while (1) { + /* begin generatorForPC: */ + branchDescriptor1 = generatorAt(bytecodeSetOffset + (fetchByteofObject(nextPCSqInt, methodObj))); + if (!((branchDescriptor1->isExtension))) break; + nExts += 1; + nextPCSqInt += (branchDescriptor1->numBytes); + } + if (!(/* isUnconditionalBranch */ + (isBranch(branchDescriptor1)) + && (!(((branchDescriptor1->isBranchTrue)) + || ((branchDescriptor1->isBranchFalse)))))) break; + nextPCSqInt = eventualTargetOf((nextPCSqInt + ((branchDescriptor1->numBytes))) + (((branchDescriptor1->spanFunction))(branchDescriptor1, nextPCSqInt, nExts, methodObj))); + } + targetBytecodePC = (postBranchPCSqInt = 0); + if (((branchDescriptor1->isBranchTrue)) + || ((branchDescriptor1->isBranchFalse))) { + targetBytecodePC = eventualTargetOf((nextPCSqInt + ((branchDescriptor1->numBytes))) + (((branchDescriptor1->spanFunction))(branchDescriptor1, nextPCSqInt, nExts, methodObj))); + postBranchPCSqInt = eventualTargetOf(nextPCSqInt + ((branchDescriptor1->numBytes))); + } + else { + nextPCSqInt = bytecodePC + ((primDescriptor->numBytes)); + } + branchDescriptor = branchDescriptor1; + nextPC = nextPCSqInt; + postBranchPC = postBranchPCSqInt; + targetPC = targetBytecodePC; + argIsConstant = (((ssTop())->type)) == SSConstant; + + /* They can't be both constants to use correct machine opcodes. + However annotable constants can't be resolved statically, hence we need to careful. */ + rcvrIsConstant = (!argIsConstant) + && ((((ssValue(1))->type)) == SSConstant); + argNeedsReg = !argIsConstant; + rcvrNeedsReg = !rcvrIsConstant; + + /* begin allocateEqualsEqualsRegistersArgNeedsReg:rcvrNeedsReg:into: */ + assert(argNeedsReg + || (rcvrNeedsReg)); + argRegSqInt = (rcvrRegSqInt = NoReg); + if (argNeedsReg) { + if (rcvrNeedsReg) { + /* begin allocateRegForStackTopTwoEntriesInto: */ + topRegistersMask = 0; + rTopSqInt = (rNextSqInt = NoReg); + if ((registerOrNone(ssTop())) != NoReg) { + rTopSqInt = registerOrNone(ssTop()); + } + if ((registerOrNone(ssValue(1))) != NoReg) { + reg = (rNextSqInt = registerOrNone(ssValue(1))); + + /* begin registerMaskFor: */ + topRegistersMask = ((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)); + } + if (rTopSqInt == NoReg) { + rTopSqInt = allocateRegNotConflictingWith(topRegistersMask); + } + if (rNextSqInt == NoReg) { + rNextSqInt = allocateRegNotConflictingWith(((rTopSqInt < 0) ? (((usqInt)(1)) >> (-rTopSqInt)) : (1ULL << rTopSqInt))); + } + assert(!(((rTopSqInt == NoReg) + || (rNextSqInt == NoReg)))); + argRegSqInt = rTopSqInt; + rcvrRegSqInt = rNextSqInt; + popToReg(ssTop(), argRegSqInt); + popToReg(ssValue(1), rcvrRegSqInt); + } + else { + argRegSqInt = allocateRegForStackEntryAtnotConflictingWith(0, 0); + popToReg(ssTop(), argRegSqInt); + + /* If the receiver is a spilled constant we need to pop it from the stack. */ + if (((ssValue(1))->spilled)) { + /* begin AddCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(AddCqR, BytesPerWord, SPReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(BytesPerWord, BytesPerOop)); + } + } + } + } + else { + assert(rcvrNeedsReg); + assert(!((((ssTop())->spilled)))); + rcvrRegSqInt = allocateRegForStackEntryAtnotConflictingWith(1, 0); + popToReg(ssValue(1), rcvrRegSqInt); + } + assert(!((argNeedsReg + && (argRegSqInt == NoReg)))); + assert(!((rcvrNeedsReg + && (rcvrRegSqInt == NoReg)))); + rcvrReg = rcvrRegSqInt; + argReg = argRegSqInt; + + /* If not followed by a branch, resolve to true or false. */ + if (!(((branchDescriptor->isBranchTrue)) + || ((branchDescriptor->isBranchFalse)))) { + return genIdenticalNoBranchArgIsConstantrcvrIsConstantargRegrcvrRegorNotIf(argIsConstant, rcvrIsConstant, argReg, rcvrReg, orNot); + } + + /* If branching the stack must be flushed for the merge */ + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= (simStackPtr - 2)) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < (simStackPtr - 2)) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : (simStackPtr - 2))); i <= (simStackPtr - 2); i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = (simStackPtr - 2) + 1; + } + + /* begin genCmpArgIsConstant:rcvrIsConstant:argReg:rcvrReg: */ + assert((argReg != NoReg) + || (rcvrReg != NoReg)); + if (argIsConstant) { + constant = ((ssTop())->constant); + + /* begin genCmpConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(constant)) + && ((oopisGreaterThan(constant, classTableRootObj())) + || (oopisLessThan(constant, nilObject())))) { + annotateobjRef(checkLiteralforInstruction(constant, genoperandoperand(CmpCwR, constant, rcvrReg)), constant); + } + else { + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, constant, rcvrReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(constant, BytesPerOop)); + } + } + } + else { + if (rcvrIsConstant) { + constant = ((ssValue(1))->constant); + + /* begin genCmpConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(constant)) + && ((oopisGreaterThan(constant, classTableRootObj())) + || (oopisLessThan(constant, nilObject())))) { + annotateobjRef(checkLiteralforInstruction(constant, genoperandoperand(CmpCwR, constant, argReg)), constant); + } + else { + /* begin CmpCq:R: */ + /* begin gen:quickConstant:operand: */ + anInstruction = genoperandoperand(CmpCqR, constant, argReg); + if (usesOutOfLineLiteral(anInstruction)) { + (anInstruction->dependent = locateLiteralsize(constant, BytesPerOop)); + } + } + } + else { + /* begin CmpR:R: */ + assert(!((argReg == SPReg))); + genoperandoperand(CmpRR, argReg, rcvrReg); + } + } + ssPop(2); + + /* Further since there is a following conditional jump bytecode, define + non-merge fixups and leave the cond bytecode to set the mergeness. */ + + /* begin fixupAt: */ + self_in_CogBytecodeFixup = fixupAtIndex(nextPC - initialPC); + if ((self_in_CogBytecodeFixup->targetInstruction)) { + assert(!(deadCode)); + } + else { + deadCode = 1; + ensureFixupAt(targetPC); + ensureFixupAt(postBranchPC); + } + if (orNot == ((branchDescriptor->isBranchTrue))) { + ensureNonMergeFixupAt(targetPC); + jumpTarget = ensureNonMergeFixupAt(postBranchPC); + + /* begin JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)jumpTarget)); + jumpTarget = ensureNonMergeFixupAt(targetPC); + + /* begin Jump: */ + genoperand(Jump, ((sqInt)jumpTarget)); + } + else { + jumpTarget = ensureNonMergeFixupAt(targetPC); + + /* begin JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)jumpTarget)); + jumpTarget = ensureNonMergeFixupAt(postBranchPC); + + /* begin Jump: */ + genoperand(Jump, ((sqInt)jumpTarget)); + } + + /* orNot is true for ~~ + a == b ifTrue: ... or a ~~ b ifFalse: ... jump on equal to target pc + a == b ifFalse: ... or a ~~ b ifTrue: ... jump on equal to post-branch pc + Not reached, execution flow has jumped to fixup */ + if (!deadCode) { + ssPushConstant(trueObject()); + } + return 0; +} + + +/* The register receiver (the closure itself) and args are pushed by the + closure value primitive(s) + and hence a frameless block has all arguments and copied values pushed to + the stack. However, + the method receiver (self) is put in the ReceiverResultReg by the block + entry. + */ + + /* StackToRegisterMappingCogit>>#initSimStackForFramelessBlock: */ +static NoDbgRegParms void +initSimStackForFramelessBlock(sqInt startpc) +{ + SimStackEntry *cascade0; + SimStackEntry *desc; + sqInt i; + + cascade0 = simSelf(); + (cascade0->type = SSRegister); + (cascade0->spilled = 0); + (cascade0->registerr = ReceiverResultReg); + (cascade0->liveRegister = ReceiverResultReg); + assert(methodOrBlockNumTemps >= methodOrBlockNumArgs); + for (i = 1; i <= methodOrBlockNumTemps; i += 1) { + desc = simStackAt(i); + (desc->type = SSBaseOffset); + (desc->spilled = 1); + (desc->registerr = SPReg); + (desc->offset = (methodOrBlockNumArgs - i) * BytesPerWord); + (desc->bcptr = startpc); + } + + /* N.B. Includes num args */ + simStackPtr = methodOrBlockNumTemps; + simSpillBase = methodOrBlockNumTemps + 1; +} + + +/* Do not inline (inBlock access) */ + + /* StackToRegisterMappingCogit>>#isNonForwarderReceiver: */ +static NoDbgRegParms sqInt +isNonForwarderReceiver(sqInt reg) +{ + return ((((simSelf())->liveRegister)) == ReceiverResultReg) + && ((inBlock == 0) + && (reg == ReceiverResultReg)); +} + + /* StackToRegisterMappingCogit>>#liveRegisters */ +static sqInt +liveRegisters(void) +{ + sqInt i; + sqInt regsSet; + + if (needsFrame) { + regsSet = 0; + } + else { + regsSet = (1U << ReceiverResultReg); + if ((methodOrBlockNumArgs <= (numRegArgs())) + && (methodOrBlockNumArgs > 0)) { + regsSet = regsSet | ((1U << Arg0Reg)); + if (((numRegArgs()) > 1) + && (methodOrBlockNumArgs > 1)) { + regsSet = regsSet | ((1U << Arg1Reg)); + } + } + } + for (i = (((simSpillBase < 0) ? 0 : simSpillBase)); i <= simStackPtr; i += 1) { + regsSet = regsSet | (registerMask(simStackAt(i))); + } + return regsSet; +} + + +/* insert nops for dead code that is mapped so that bc + to mc mapping is not many to one */ + + /* StackToRegisterMappingCogit>>#mapDeadDescriptorIfNeeded: */ +static NoDbgRegParms sqInt +mapDeadDescriptorIfNeeded(BytecodeDescriptor *descriptor) +{ + AbstractInstruction *abstractInstruction; + + if (((descriptor->isMapped)) + || ((inBlock > 0) + && ((descriptor->isMappedInBlock)))) { + abstractInstruction = gen(Nop); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + } + return 0; +} + + +/* Spill everything on the simulated stack that needs spilling (that below + receiver and arguments). + Marshall receiver and arguments to stack and/or registers depending on arg + count. If the args don't fit in registers push receiver and args (spill + everything), but still assign + the receiver to ReceiverResultReg. */ + + /* StackToRegisterMappingCogit>>#marshallSendArguments: */ +static NoDbgRegParms void +marshallSendArguments(sqInt numArgs) +{ + sqInt anyRefs; + SimStackEntry *cascade0; + sqInt i; + sqInt numSpilled; + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= ((simStackPtr - numArgs) - 1)) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < ((simStackPtr - numArgs) - 1)) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : ((simStackPtr - numArgs) - 1))); i < (simStackPtr - numArgs); i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = ((simStackPtr - numArgs) - 1) + 1; + } + if (numArgs > (numRegArgs())) { + numSpilled = numberOfSpillsInTopNItems(numArgs + 1); + anyRefs = anyReferencesToRegisterinTopNItems(ReceiverResultReg, numArgs + 1); + if ((numSpilled > 0) + || (anyRefs)) { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + storeToReg(simStackAt(simStackPtr - numArgs), ReceiverResultReg); + } + else { + cascade0 = simStackAt(simStackPtr - numArgs); + storeToReg(cascade0, ReceiverResultReg); + (cascade0->type = SSRegister); + (cascade0->registerr = ReceiverResultReg); + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + } + } + else { + if (numArgs > 0) { + if (((numRegArgs()) > 1) + && (numArgs > 1)) { + /* begin ssAllocateRequiredReg:upThrough: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << Arg0Reg), simStackPtr - 2, simNativeStackPtr); + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << Arg1Reg), simStackPtr - 1, simNativeStackPtr); + } + else { + /* begin ssAllocateRequiredReg:upThrough: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << Arg0Reg), simStackPtr - 1, simNativeStackPtr); + } + } + if (((numRegArgs()) > 1) + && (numArgs > 1)) { + popToReg(simStackAt(simStackPtr), Arg1Reg); + } + if (numArgs > 0) { + popToReg(simStackAt((simStackPtr - numArgs) + 1), Arg0Reg); + } + popToReg(simStackAt(simStackPtr - numArgs), ReceiverResultReg); + } + + /* If there are no spills and no references to ReceiverResultReg + the fetch of ReceiverResultReg from the stack can be avoided + by assigning directly to ReceiverResultReg and pushing it. + Move the args to the register arguments, being careful to do + so last to first so e.g. previous contents don't get overwritten. + Also check for any arg registers in use by other args. */ + ssPop(numArgs + 1); +} + + +/* For assert checking; or rather for avoiding assert fails when dealing with + the hack for block temps in the SqueakV3PlusClosures bytecode set. + */ + + /* StackToRegisterMappingCogit>>#maybeCompilingFirstPassOfBlockWithInitialPushNil */ +static sqInt +maybeCompilingFirstPassOfBlockWithInitialPushNil(void) +{ + return (inBlock == InVanillaBlock) + && ((methodOrBlockNumTemps > methodOrBlockNumArgs) + && (compilationPass == 1)); +} + + +/* If this bytecode has a fixup, some kind of merge needs to be done. There + are 4 cases: + 1) the bytecode has no fixup (fixup isNotAFixup) + do nothing + 2) the bytecode has a non merge fixup + the fixup has needsNonMergeFixup. + The code generating non merge fixup (currently only special selector code) + is responsible + for the merge so no need to do it. + We set deadCode to false as the instruction can be reached from jumps. + 3) the bytecode has a merge fixup, but execution flow *cannot* fall + through to the merge point. + the fixup has needsMergeFixup and deadCode = true. + ignores the current simStack as it does not mean anything + restores the simStack to the state the jumps to the merge point expects it + to be. + 4) the bytecode has a merge fixup and execution flow *can* fall through to + the merge point. + the fixup has needsMergeFixup and deadCode = false. + flushes the stack to the stack pointer so the fall through execution path + simStack is + in the state the merge point expects it to be. + restores the simStack to the state the jumps to the merge point expects it + to be. + + In addition, if this is a backjump merge point, we patch the fixup to hold + the current simStackPtr + for later assertions. */ + + /* StackToRegisterMappingCogit>>#mergeWithFixupIfRequired: */ +static NoDbgRegParms sqInt +mergeWithFixupIfRequired(BytecodeFixup *fixup) +{ + SimStackEntry *cascade0; + sqInt i; + + /* begin assertCorrectSimStackPtr */ + assert((simSpillBase >= methodOrBlockNumTemps) + || ((maybeCompilingFirstPassOfBlockWithInitialPushNil()) + && (simSpillBase > methodOrBlockNumArgs))); + if (needsFrame + && (simSpillBase > 0)) { + assert(((((simStackAt(simSpillBase - 1))->spilled)) == 1) + || ((maybeCompilingFirstPassOfBlockWithInitialPushNil()) + && (simSpillBase > methodOrBlockNumArgs))); + assert((simSpillBase > simStackPtr) + || ((((simStackAt(simSpillBase))->spilled)) == 0)); + } + + /* case 1 */ + if (!((fixup->targetInstruction))) { + return 0; + } + + /* case 2 */ + if ((((usqInt)((fixup->targetInstruction)))) == NeedsNonMergeFixupFlag) { + deadCode = 0; + return 0; + } + + /* cases 3 and 4 */ + assert(isMergeFixup(fixup)); + traceMerge(fixup); + if (deadCode) { + assert((((fixup->simStackPtr)) >= methodOrBlockNumTemps) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil())); + simStackPtr = (fixup->simStackPtr); + } + else { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + } + + /* case 3 + Would like to assert fixup simStackPtr >= methodOrBlockNumTemps + but can't because of the initialNils hack. + case 4 + cases 3 and 4 */ + deadCode = 0; + if ((fixup->isTargetOfBackwardBranch)) { + (fixup->simStackPtr = simStackPtr); + } + (fixup->targetInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + assert(simStackPtr == ((fixup->simStackPtr))); + + /* begin restoreSimStackAtMergePoint: */ + /* begin voidReceiverOptStatus */ + ((simSelf())->liveRegister = NoReg); + for (i = (methodOrBlockNumTemps + 1); i <= simStackPtr; i += 1) { + cascade0 = simStackAt(i); + (cascade0->type = SSSpill); + (cascade0->offset = FoxMFReceiver - ((i - methodOrBlockNumArgs) * BytesPerOop)); + (cascade0->registerr = FPReg); + (cascade0->spilled = 1); + } + simSpillBase = simStackPtr + 1; + return 0; +} + + /* StackToRegisterMappingCogit>>#methodAbortTrampolineFor: */ +static NoDbgRegParms sqInt +methodAbortTrampolineFor(sqInt numArgs) +{ + return methodAbortTrampolines[((numArgs < ((numRegArgs()) + 1)) ? numArgs : ((numRegArgs()) + 1))]; +} + + /* StackToRegisterMappingCogit>>#needsFrameIfMod16GENumArgs: */ +static NoDbgRegParms sqInt +needsFrameIfMod16GENumArgs(sqInt stackDelta) +{ + return (byte0 % 16) >= methodOrBlockNumArgs; +} + + +/* As of August 2013, the code generator can't deal with spills in frameless + methods (the + issue is to do with the stack offset to get at an argument, which is + changed when there's a spill). + In e.g. TextColor>>#dominates: other ^other class == self class the second + send of class + needs also rto allocate a register that the first one used, but the first + one's register can't be + spilled. So avoid this by only allowing class to be sent if the stack + contains a single element. */ + + /* StackToRegisterMappingCogit>>#needsFrameIfStackGreaterThanOne: */ +static NoDbgRegParms sqInt +needsFrameIfStackGreaterThanOne(sqInt stackDelta) +{ + return stackDelta > 1; +} + + /* StackToRegisterMappingCogit>>#numberOfSpillsInTopNItems: */ +static NoDbgRegParms sqInt +numberOfSpillsInTopNItems(sqInt n) +{ + sqInt i; + + for (i = simStackPtr; i >= ((simStackPtr - n) + 1); i += -1) { + if ((((simStackAt(i))->type)) == SSSpill) { + return n - (simStackPtr - i); + } + } + return 0; +} + + /* StackToRegisterMappingCogit>>#picAbortTrampolineFor: */ +static NoDbgRegParms sqInt +picAbortTrampolineFor(sqInt numArgs) +{ + return picAbortTrampolines[((numArgs < ((numRegArgs()) + 1)) ? numArgs : ((numRegArgs()) + 1))]; +} + + +/* Try and keep the register contents for ReceiverResultReg if it is top of + stack + */ + + /* StackToRegisterMappingCogit>>#preserveTopInClassRegIfIsReceiverResultReg */ +static void +preserveTopInClassRegIfIsReceiverResultReg(void) +{ + if (((((ssTop())->type)) == SSRegister) + && ((((ssTop())->registerr)) == ReceiverResultReg)) { + /* begin ssAllocateRequiredReg: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << ClassReg), simStackPtr, simNativeStackPtr); + storeToReg(ssTop(), ClassReg); + ((ssTop())->registerr = ClassReg); + } +} + + /* StackToRegisterMappingCogit>>#prevInstIsPCAnnotated */ +static sqInt +prevInstIsPCAnnotated(void) +{ + sqInt prevIndex; + AbstractInstruction *prevInst; + + if (!(opcodeIndex > 0)) { + return 0; + } + prevIndex = opcodeIndex - 1; + while (1) { + if (prevIndex <= 0) { + return 0; + } + prevInst = abstractInstructionAt(prevIndex); + if (((!((prevInst->annotation)) + ? 0 + : (prevInst->annotation))) >= HasBytecodePC) { + return 1; + } + if (!(((prevInst->opcode)) == Label)) break; + prevIndex -= 1; + } + return 0; +} + + +/* Used to mark ReceiverResultReg as dead or not containing simSelf. + Used when the simStack has already been flushed, e.g. for sends. */ + + /* StackToRegisterMappingCogit>>#receiverIsInReceiverResultReg */ +static int +receiverIsInReceiverResultReg(void) +{ + return (((simSelf())->liveRegister)) == ReceiverResultReg; +} + + +/* When a block must be recompiled due to overestimating the + numInitialNils fixups must be restored, which means rescannning + since backward branches need their targets initialized. */ + + /* StackToRegisterMappingCogit>>#reinitializeFixupsFrom:through: */ +static NoDbgRegParms void +reinitializeFixupsFromthrough(sqInt start, sqInt end) +{ + BytecodeDescriptor *descriptor; + sqInt distance; + BytecodeFixup *fixup; + sqInt nExts; + sqInt pc; + BytecodeFixup *self_in_CogSSBytecodeFixup; + sqInt targetPC; + + pc = start; + nExts = 0; + while (pc <= end) { + /* begin fixupAt: */ + self_in_CogSSBytecodeFixup = fixupAtIndex(pc - initialPC); + (self_in_CogSSBytecodeFixup->targetInstruction) = 0; + (self_in_CogSSBytecodeFixup->simStackPtr) = 0; + byte0 = (fetchByteofObject(pc, methodObj)) + bytecodeSetOffset; + descriptor = generatorAt(byte0); + if ((isBranch(descriptor)) + && ((/* begin isBackwardBranch:at:exts:in: */ + assert(((descriptor->spanFunction))), + (((descriptor->spanFunction))(descriptor, pc, nExts, methodObj)) < 0))) { + distance = ((descriptor->spanFunction))(descriptor, pc, nExts, methodObj); + targetPC = (pc + ((descriptor->numBytes))) + distance; + + /* begin initializeFixupAt: */ + /* begin fixupAt: */ + fixup = fixupAtIndex(targetPC - initialPC); + (fixup->targetInstruction) = ((AbstractInstruction *) NeedsMergeFixupFlag); + + /* begin setIsBackwardBranchFixup */ + (fixup->isTargetOfBackwardBranch) = 1; + } + if ((descriptor->isBlockCreation)) { + distance = ((descriptor->spanFunction))(descriptor, pc, nExts, methodObj); + pc = (pc + ((descriptor->numBytes))) + distance; + } + else { + pc += (descriptor->numBytes); + } + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } +} + + +/* Scan the block to determine if the block needs a frame or not */ + + /* StackToRegisterMappingCogit>>#scanBlock: */ +static NoDbgRegParms sqInt +scanBlock(BlockStart *blockStart) +{ + BytecodeDescriptor *descriptor; + sqInt end; + sqInt framelessStackDelta; + sqInt nExts; + sqInt numPushNils; + sqInt (* const numPushNilsFunction)(struct _BytecodeDescriptor *,sqInt,sqInt,sqInt) = squeakV3orSistaV1NumPushNils; + sqInt pc; + sqInt pushingNils; + + needsFrame = 0; + prevBCDescriptor = null; + methodOrBlockNumArgs = (blockStart->numArgs); + inBlock = InVanillaBlock; + pc = (blockStart->startpc); + end = ((blockStart->startpc)) + ((blockStart->span)); + framelessStackDelta = (nExts = (extA = (numExtB = (extB = 0)))); + pushingNils = 1; + while (pc < end) { + descriptor = loadBytesAndGetDescriptor(pc); + if ((descriptor->isExtension)) { + ((descriptor->generator))(); + } + if (!needsFrame) { + if ((!((descriptor->needsFrameFunction))) + || (((descriptor->needsFrameFunction))(framelessStackDelta))) { + needsFrame = 1; + } + else { + framelessStackDelta += (descriptor->stackDelta); + } + } + + /* begin maybeNoteDescriptor:blockStart: */ + if ((descriptor->isInstVarRef)) { + (blockStart->hasInstVarRef = 1); + } + if (pushingNils + && (!((descriptor->isExtension)))) { + /* Count the initial number of pushed nils acting as temp initializers. We can't tell + whether an initial pushNil is an operand reference or a temp initializer, except + when the pushNil is a jump target (has a fixup), which never happens: + self systemNavigation browseAllSelect: + [:m| | ebc | + (ebc := m embeddedBlockClosures + select: [:ea| ea decompile statements first isMessage] + thenCollect: [:ea| ea decompile statements first selector]) notEmpty + and: [(#(whileTrue whileFalse whileTrue: whileFalse:) intersection: ebc) notEmpty]] + or if the bytecode set has a push multiple nils bytecode. We simply count initial nils. + Rarely we may end up over-estimating. We will correct by checking the stack depth + at the end of the block in compileBlockBodies. */ + if (((numPushNils = numPushNilsFunction(descriptor, pc, nExts, methodObj))) > 0) { + assert(((descriptor->numBytes)) == 1); + (blockStart->numInitialNils = ((blockStart->numInitialNils)) + numPushNils); + } + else { + pushingNils = 0; + } + } + pc = (pc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, pc, nExts, methodObj) + : 0)); + if ((descriptor->isExtension)) { + nExts += 1; + } + else { + nExts = (extA = (numExtB = (extB = 0))); + } + prevBCDescriptor = descriptor; + } + + /* It would be nice of this wasn't necessary but alas we need to do the eager + scan for frameless methods so that we don't end up popping too much off + the simulated stack, e.g. for pushNil; returnTopFromBlock methods. */ + if (!needsFrame) { + assert((framelessStackDelta >= 0) + && (((blockStart->numInitialNils)) >= framelessStackDelta)); + (blockStart->numInitialNils = ((blockStart->numInitialNils)) - framelessStackDelta); + } + return 0; +} + + +/* Scan the method (and all embedded blocks) to determine + - what the last bytecode is; extra bytes at the end of a method are used + to encode things like source pointers or temp names + - if the method needs a frame or not + - what are the targets of any backward branches. + - how many blocks it creates + Answer the block count or on error a negative error code */ + + /* StackToRegisterMappingCogit>>#scanMethod */ +static sqInt +scanMethod(void) +{ + BytecodeDescriptor *descriptor; + sqInt distance; + BytecodeFixup *fixup; + sqInt framelessStackDelta; + sqInt latestContinuation; + sqInt nExts; + sqInt numBlocks; + sqInt pc; + sqInt seenInstVarStore; + sqInt targetPC; + + needsFrame = (useTwoPaths = (seenInstVarStore = 0)); + prevBCDescriptor = null; + if ((primitiveIndex > 0) + && (isQuickPrimitiveIndex(primitiveIndex))) { + return 0; + } + pc = (latestContinuation = initialPC); + numBlocks = (framelessStackDelta = (nExts = (extA = (numExtB = (extB = 0))))); + while (pc <= endPC) { + descriptor = loadBytesAndGetDescriptor(pc); + if ((descriptor->isExtension)) { + if (((descriptor->opcode)) == Nop) { + return EncounteredUnknownBytecode; + } + + /* unknown bytecode tag; see Cogit class>>#generatorTableFrom: */ + ((descriptor->generator))(); + } + if (((descriptor->isReturn)) + && (pc >= latestContinuation)) { + endPC = pc; + } + if (!needsFrame) { + if ((!((descriptor->needsFrameFunction))) + || (((descriptor->needsFrameFunction))(framelessStackDelta))) { + /* With immutability we win simply by avoiding a frame build if the receiver is young and not immutable. */ +# if IMMUTABILITY + if ((descriptor->is1ByteInstVarStore)) { + useTwoPaths = 1; + } + else { + needsFrame = 1; + useTwoPaths = 0; + } +# else // IMMUTABILITY + needsFrame = 1; + useTwoPaths = 0; +# endif + + } + else { + framelessStackDelta += (descriptor->stackDelta); + + /* Without immutability we win if there are two or more stores and the receiver is new. */ +# if IMMUTABILITY +# else + if ((descriptor->is1ByteInstVarStore)) { + if (seenInstVarStore) { + useTwoPaths = 1; + } + else { + seenInstVarStore = 1; + } + } +# endif // IMMUTABILITY + + } + } + if (isBranch(descriptor)) { + distance = ((descriptor->spanFunction))(descriptor, pc, nExts, methodObj); + targetPC = (pc + ((descriptor->numBytes))) + distance; + + /* begin isBackwardBranch:at:exts:in: */ + assert(((descriptor->spanFunction))); + if ((((descriptor->spanFunction))(descriptor, pc, nExts, methodObj)) < 0) { + /* begin initializeFixupAt: */ + /* begin fixupAt: */ + fixup = fixupAtIndex(targetPC - initialPC); + (fixup->targetInstruction) = ((AbstractInstruction *) NeedsMergeFixupFlag); + + /* begin setIsBackwardBranchFixup */ + (fixup->isTargetOfBackwardBranch) = 1; + } + else { + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + } + } + if ((descriptor->isBlockCreation)) { + numBlocks += 1; + distance = ((descriptor->spanFunction))(descriptor, pc, nExts, methodObj); + targetPC = (pc + ((descriptor->numBytes))) + distance; + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + } + pc += (descriptor->numBytes); + nExts = ((descriptor->isExtension) + ? nExts + 1 + : (extA = (numExtB = (extB = 0)))); + prevBCDescriptor = descriptor; + } + return numBlocks; +} + + /* StackToRegisterMappingCogit>>#squeakV3orSistaV1PushNilSize:numInitialNils: */ +static NoDbgRegParms sqInt +squeakV3orSistaV1PushNilSizenumInitialNils(sqInt aMethodObj, sqInt numInitialNils) +{ + return (numInitialNils); +} + + /* StackToRegisterMappingCogit>>#squeakV3orSistaV1:Num:Push:Nils: */ +static NoDbgRegParms sqInt +squeakV3orSistaV1NumPushNils(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + return (/* v3:Num:Push:Nils: */ + (((descriptor->generator)) == genPushConstantNilBytecode + ? 1 + : 0)); +} + + /* StackToRegisterMappingCogit>>#ssAllocateRequiredRegMask:upThrough:upThroughNative: */ +static NoDbgRegParms void +ssAllocateRequiredRegMaskupThroughupThroughNative(sqInt requiredRegsMask, sqInt stackPtr, sqInt nativeStackPtr) +{ + sqInt i; + sqInt iSqInt; + sqInt lastRequired; + sqInt lastRequiredNative; + sqInt liveRegs; + + lastRequired = -1; + lastRequiredNative = -1; + + /* compute live regs while noting the last occurrence of required regs. + If these are not free we must spill from simSpillBase to last occurrence. + Note we are conservative here; we could allocate FPReg in frameless methods. */ + liveRegs = (1U << FPReg) | (1U << SPReg); + for (i = (((simSpillBase < 0) ? 0 : simSpillBase)); i <= stackPtr; i += 1) { + liveRegs = liveRegs | (registerMask(simStackAt(i))); + if ((((registerMask(simStackAt(i))) & requiredRegsMask) != 0)) { + lastRequired = i; + } + } + + /* If any of requiredRegsMask are live we must spill. */ + if (((liveRegs & requiredRegsMask) != 0)) { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= lastRequired) { + for (iSqInt = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < lastRequired) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : lastRequired)); iSqInt <= lastRequired; iSqInt += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(iSqInt), frameOffsetOfTemporary(iSqInt - 1), FPReg); + } + simSpillBase = lastRequired + 1; + } + assert(!(((((liveRegisters()) & requiredRegsMask) != 0)))); + } +} + + +/* Any occurrences on the stack of the value being stored (which is the top + of stack) + must be flushed, and hence any values colder than them stack. */ + + /* StackToRegisterMappingCogit>>#ssFlushUpThroughReceiverVariable: */ +static NoDbgRegParms void +ssFlushUpThroughReceiverVariable(sqInt slotIndex) +{ + SimStackEntry *desc; + sqInt i; + sqInt index; + + + /* begin ssFlushUpThrough: */ + assert(simSpillBase >= 0); + for (index = (simStackPtr - 1); index >= simSpillBase; index += -1) { + if (((desc = simStackAt(index)), + (((desc->type)) == SSBaseOffset) + && ((((desc->registerr)) == ReceiverResultReg) + && (((desc->offset)) == (slotOffsetOfInstVarIndex(slotIndex)))))) { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= index) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < index) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : index)); i <= index; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = index + 1; + } + goto l1; + } + } + /* end ssFlushUpThrough: */ +l1:; +} + + +/* Any occurrences on the stack of the value being stored (which is the top + of stack) + must be flushed, and hence any values colder than them stack. */ + + /* StackToRegisterMappingCogit>>#ssFlushUpThroughTemporaryVariable: */ +static NoDbgRegParms void +ssFlushUpThroughTemporaryVariable(sqInt tempIndex) +{ + SimStackEntry *desc; + sqInt i; + sqInt index; + sqInt offset; + + offset = ((simStackAt(tempIndex + 1))->offset); + assert(offset == (frameOffsetOfTemporary(tempIndex))); + + /* begin ssFlushUpThrough: */ + assert(simSpillBase >= 0); + for (index = (simStackPtr - 1); index >= simSpillBase; index += -1) { + if (((desc = simStackAt(index)), + (((desc->type)) == SSBaseOffset) + && ((((desc->registerr)) == FPReg) + && (((desc->offset)) == offset)))) { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= index) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < index) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : index)); i <= index; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = index + 1; + } + goto l1; + } + } + /* end ssFlushUpThrough: */ +l1:; +} + + /* StackToRegisterMappingCogit>>#ssPop: */ +static NoDbgRegParms void +ssPop(sqInt n) +{ + sqInt i; + sqInt toDoLimit; + + assert(((simStackPtr - n) >= methodOrBlockNumTemps) + || (((!needsFrame) + && ((simStackPtr - n) >= 0)) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil()))); + simStackPtr -= n; + + /* begin updateSimSpillBase */ + assert(((simSpillBase > methodOrBlockNumTemps) + && (simStackPtr >= methodOrBlockNumTemps)) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil())); + if (simSpillBase > simStackPtr) { + simSpillBase = simStackPtr + 1; + while (((simSpillBase - 1) > methodOrBlockNumTemps) + && (!(((simStackAt(simSpillBase - 1))->spilled)))) { + simSpillBase -= 1; + } + } + else { + while ((((simStackAt(simSpillBase))->spilled)) + && (simSpillBase <= simStackPtr)) { + simSpillBase += 1; + } + } + toDoLimit = (((simSpillBase - 1) < simStackPtr) ? (simSpillBase - 1) : simStackPtr); + for (i = (methodOrBlockNumTemps + 1); i <= toDoLimit; i += 1) { + assert((((simStackAt(i))->spilled)) == 1); + } + assert((simSpillBase > simStackPtr) + || ((((simStackAt(simSpillBase))->spilled)) == 0)); +} + + /* StackToRegisterMappingCogit>>#ssPushAnnotatedConstant: */ +static NoDbgRegParms sqInt +ssPushAnnotatedConstant(sqInt literal) +{ + AbstractInstruction *abstractInstruction; + + ssPushConstant(literal); + + /* begin annotateInstructionForBytecode */ + abstractInstruction = (prevInstIsPCAnnotated() + ? gen(Nop) + : genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + return 0; +} + + /* StackToRegisterMappingCogit>>#ssPushBase:offset: */ +static NoDbgRegParms sqInt +ssPushBaseoffset(sqInt reg, sqInt offset) +{ + SimStackEntry *cascade0; + sqInt i; + sqInt toDoLimit; + + ssPush(1); + + /* begin ssTop */ + cascade0 = simStackAt(simStackPtr); + (cascade0->type = SSBaseOffset); + (cascade0->spilled = 0); + (cascade0->registerr = reg); + (cascade0->offset = offset); + (cascade0->bcptr = bytecodePC); + + /* begin updateSimSpillBase */ + assert(((simSpillBase > methodOrBlockNumTemps) + && (simStackPtr >= methodOrBlockNumTemps)) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil())); + if (simSpillBase > simStackPtr) { + simSpillBase = simStackPtr + 1; + while (((simSpillBase - 1) > methodOrBlockNumTemps) + && (!(((simStackAt(simSpillBase - 1))->spilled)))) { + simSpillBase -= 1; + } + } + else { + while ((((simStackAt(simSpillBase))->spilled)) + && (simSpillBase <= simStackPtr)) { + simSpillBase += 1; + } + } + toDoLimit = (((simSpillBase - 1) < simStackPtr) ? (simSpillBase - 1) : simStackPtr); + for (i = (methodOrBlockNumTemps + 1); i <= toDoLimit; i += 1) { + assert((((simStackAt(i))->spilled)) == 1); + } + assert((simSpillBase > simStackPtr) + || ((((simStackAt(simSpillBase))->spilled)) == 0)); + return 0; +} + + /* StackToRegisterMappingCogit>>#ssPushConstant: */ +static NoDbgRegParms sqInt +ssPushConstant(sqInt literal) +{ + SimStackEntry *cascade0; + sqInt i; + sqInt toDoLimit; + + ssPush(1); + + /* begin ssTop */ + cascade0 = simStackAt(simStackPtr); + (cascade0->type = SSConstant); + (cascade0->spilled = 0); + (cascade0->constant = literal); + (cascade0->bcptr = bytecodePC); + + /* begin updateSimSpillBase */ + assert(((simSpillBase > methodOrBlockNumTemps) + && (simStackPtr >= methodOrBlockNumTemps)) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil())); + if (simSpillBase > simStackPtr) { + simSpillBase = simStackPtr + 1; + while (((simSpillBase - 1) > methodOrBlockNumTemps) + && (!(((simStackAt(simSpillBase - 1))->spilled)))) { + simSpillBase -= 1; + } + } + else { + while ((((simStackAt(simSpillBase))->spilled)) + && (simSpillBase <= simStackPtr)) { + simSpillBase += 1; + } + } + toDoLimit = (((simSpillBase - 1) < simStackPtr) ? (simSpillBase - 1) : simStackPtr); + for (i = (methodOrBlockNumTemps + 1); i <= toDoLimit; i += 1) { + assert((((simStackAt(i))->spilled)) == 1); + } + assert((simSpillBase > simStackPtr) + || ((((simStackAt(simSpillBase))->spilled)) == 0)); + return 0; +} + + /* StackToRegisterMappingCogit>>#ssPushDesc: */ +static NoDbgRegParms sqInt +ssPushDesc(SimStackEntry simStackEntry) +{ + sqInt i; + sqInt toDoLimit; + + if (((simStackEntry.type)) == SSSpill) { + (simStackEntry.type = SSBaseOffset); + } + (simStackEntry.spilled = 0); + (simStackEntry.bcptr = bytecodePC); + simStack[(simStackPtr += 1)] = simStackEntry; + + /* begin updateSimSpillBase */ + assert(((simSpillBase > methodOrBlockNumTemps) + && (simStackPtr >= methodOrBlockNumTemps)) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil())); + if (simSpillBase > simStackPtr) { + simSpillBase = simStackPtr + 1; + while (((simSpillBase - 1) > methodOrBlockNumTemps) + && (!(((simStackAt(simSpillBase - 1))->spilled)))) { + simSpillBase -= 1; + } + } + else { + while ((((simStackAt(simSpillBase))->spilled)) + && (simSpillBase <= simStackPtr)) { + simSpillBase += 1; + } + } + toDoLimit = (((simSpillBase - 1) < simStackPtr) ? (simSpillBase - 1) : simStackPtr); + for (i = (methodOrBlockNumTemps + 1); i <= toDoLimit; i += 1) { + assert((((simStackAt(i))->spilled)) == 1); + } + assert((simSpillBase > simStackPtr) + || ((((simStackAt(simSpillBase))->spilled)) == 0)); + return 0; +} + + /* StackToRegisterMappingCogit>>#ssPushRegister: */ +static NoDbgRegParms sqInt +ssPushRegister(sqInt reg) +{ + SimStackEntry *cascade0; + sqInt i; + sqInt toDoLimit; + + ssPush(1); + + /* begin ssTop */ + cascade0 = simStackAt(simStackPtr); + (cascade0->type = SSRegister); + (cascade0->spilled = 0); + (cascade0->registerr = reg); + (cascade0->bcptr = bytecodePC); + + /* begin updateSimSpillBase */ + assert(((simSpillBase > methodOrBlockNumTemps) + && (simStackPtr >= methodOrBlockNumTemps)) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil())); + if (simSpillBase > simStackPtr) { + simSpillBase = simStackPtr + 1; + while (((simSpillBase - 1) > methodOrBlockNumTemps) + && (!(((simStackAt(simSpillBase - 1))->spilled)))) { + simSpillBase -= 1; + } + } + else { + while ((((simStackAt(simSpillBase))->spilled)) + && (simSpillBase <= simStackPtr)) { + simSpillBase += 1; + } + } + toDoLimit = (((simSpillBase - 1) < simStackPtr) ? (simSpillBase - 1) : simStackPtr); + for (i = (methodOrBlockNumTemps + 1); i <= toDoLimit; i += 1) { + assert((((simStackAt(i))->spilled)) == 1); + } + assert((simSpillBase > simStackPtr) + || ((((simStackAt(simSpillBase))->spilled)) == 0)); + return 0; +} + + /* StackToRegisterMappingCogit>>#ssPush: */ +static NoDbgRegParms void +ssPush(sqInt n) +{ + simStackPtr += n; +} + + /* StackToRegisterMappingCogit>>#ssSelfDescriptor */ +static SimStackEntry +ssSelfDescriptor(void) +{ + return simStack[0]; +} + + +/* In addition to ssStorePop:toReg:, if this is a store and not + a popInto I change the simulated stack to use the register + for the top value */ + + /* StackToRegisterMappingCogit>>#ssStoreAndReplacePop:toReg: */ +static NoDbgRegParms void +ssStoreAndReplacePoptoReg(sqInt popBoolean, sqInt reg) +{ + char topSpilled; + + topSpilled = ((ssTop())->spilled); + ssStorePoptoReg(popBoolean + || (topSpilled), reg); + if (!popBoolean) { + if (!topSpilled) { + ssPop(1); + } + ssPushRegister(reg); + } +} + + +/* Store or pop the top simulated stack entry to a register. + Use preferredReg if the entry is not itself a register. + Answer the actual register the result ends up in. */ + + /* StackToRegisterMappingCogit>>#ssStorePop:toPreferredReg: */ +static NoDbgRegParms sqInt +ssStorePoptoPreferredReg(sqInt popBoolean, sqInt preferredReg) +{ + sqInt actualReg; + + actualReg = preferredReg; + if ((((ssTop())->type)) == SSRegister) { + assert(!(((ssTop())->spilled))); + actualReg = ((ssTop())->registerr); + } + ssStorePoptoReg(popBoolean, actualReg); + return actualReg; +} + + +/* Store or pop the top simulated stack entry to a register. + N.B.: popToReg: and storeToReg: does not generate anything if + it moves a register to the same register. */ + + /* StackToRegisterMappingCogit>>#ssStorePop:toReg: */ +static NoDbgRegParms void +ssStorePoptoReg(sqInt popBoolean, sqInt reg) +{ + if (popBoolean) { + popToReg(ssTop(), reg); + ssPop(1); + } + else { + storeToReg(ssTop(), reg); + } +} + + /* StackToRegisterMappingCogit>>#ssTop */ +static SimStackEntry * +ssTop(void) +{ + return simStackAt(simStackPtr); +} + + /* StackToRegisterMappingCogit>>#ssValue: */ +static NoDbgRegParms SimStackEntry * +ssValue(sqInt n) +{ + return simStackAt(simStackPtr - n); +} + + /* StackToRegisterMappingCogit>>#stackEntryIsBoolean: */ +static NoDbgRegParms sqInt +stackEntryIsBoolean(SimStackEntry *simStackEntry) +{ + return (((simStackEntry->type)) == SSConstant) + && ((((simStackEntry->constant)) == (trueObject())) + || (((simStackEntry->constant)) == (falseObject()))); +} + + +/* Answer if the stack is valid up to, but not including, simSpillBase. */ + + /* StackToRegisterMappingCogit>>#tempsValidAndVolatileEntriesSpilled */ +static sqInt +tempsValidAndVolatileEntriesSpilled(void) +{ + sqInt culprit; + sqInt i; + + culprit = 0; + for (i = 1; i <= methodOrBlockNumTemps; i += 1) { + if (!(((((simStackAt(i))->type)) == SSBaseOffset) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil()))) { + if (!culprit) { + culprit = i; + } + return 0; + } + } + for (i = (methodOrBlockNumTemps + 1); i < simSpillBase; i += 1) { + if (!(((simStackAt(i))->spilled))) { + if (!culprit) { + culprit = i; + } + return 0; + } + } + return 1; +} + + +/* If the sequence of bytecodes is + push: (Array new: 1) + popIntoTemp: tempIndex + pushConstant: const or pushTemp: n + popIntoTemp: 0 inVectorAt: tempIndex + collapse this into + tempAt: tempIndex put: {const or temp} + and answer true, otherwise answer false. + One might think that we should look for a sequence of more than + one pushes and pops but this is extremely rare. + Exclude pushRcvr: n to avoid potential complications with context inst + vars. */ + + /* StackToRegisterMappingCogit>>#tryCollapseTempVectorInitializationOfSize: */ +static NoDbgRegParms sqInt +tryCollapseTempVectorInitializationOfSize(sqInt slots) +{ + sqInt pc; + BytecodeDescriptor *pushArrayDesc; + BytecodeDescriptor *pushValueDesc; + sqInt reg; + sqInt remoteTempIndex; + BytecodeDescriptor *storeArrayDesc; + BytecodeDescriptor *storeValueDesc; + usqInt tempIndex; + + if (slots != 1) { + return 0; + } + + /* begin generatorForPC: */ + pushArrayDesc = generatorAt(bytecodeSetOffset + (fetchByteofObject(bytecodePC, methodObj))); + assert(((pushArrayDesc->generator)) == genPushNewArrayBytecode); + pc = bytecodePC + ((pushArrayDesc->numBytes)); + + /* begin generatorForPC: */ + storeArrayDesc = generatorAt(bytecodeSetOffset + (fetchByteofObject(pc, methodObj))); + if (((storeArrayDesc->generator)) == genStoreAndPopTemporaryVariableBytecode) { + tempIndex = (fetchByteofObject(bytecodePC + ((pushArrayDesc->numBytes)), methodObj)) & 7; + } + else { + if (!(((storeArrayDesc->generator)) == genLongStoreAndPopTemporaryVariableBytecode)) { + return 0; + } + tempIndex = fetchByteofObject((bytecodePC + ((pushArrayDesc->numBytes))) + 1, methodObj); + } + pc = (bytecodePC + ((pushArrayDesc->numBytes))) + ((storeArrayDesc->numBytes)); + + /* begin generatorForPC: */ + pushValueDesc = generatorAt(bytecodeSetOffset + (fetchByteofObject(pc, methodObj))); + if (!((((pushValueDesc->generator)) == genPushLiteralConstantBytecode) + || ((((pushValueDesc->generator)) == genPushQuickIntegerConstantBytecode) + || (((pushValueDesc->generator)) == genPushTemporaryVariableBytecode)))) { + return 0; + } + pc = ((bytecodePC + ((pushArrayDesc->numBytes))) + ((storeArrayDesc->numBytes))) + ((pushValueDesc->numBytes)); + + /* begin generatorForPC: */ + storeValueDesc = generatorAt(bytecodeSetOffset + (fetchByteofObject(pc, methodObj))); + remoteTempIndex = fetchByteofObject((((bytecodePC + ((pushArrayDesc->numBytes))) + ((storeArrayDesc->numBytes))) + ((pushValueDesc->numBytes))) + 2, methodObj); + if (!((((storeValueDesc->generator)) == genStoreAndPopRemoteTempLongBytecode) + && (tempIndex == remoteTempIndex))) { + return 0; + } + genNewArrayOfSizeinitialized(1, 0); + evaluateat(pushValueDesc, (bytecodePC + ((pushArrayDesc->numBytes))) + ((storeArrayDesc->numBytes))); + reg = ssStorePoptoPreferredReg(1, TempReg); + genStoreSourceRegslotIndexintoNewObjectInDestReg(reg, 0, ReceiverResultReg); + ssPushRegister(ReceiverResultReg); + evaluateat(storeArrayDesc, bytecodePC + ((pushArrayDesc->numBytes))); + bytecodePC = ((bytecodePC + ((storeArrayDesc->numBytes))) + ((pushValueDesc->numBytes))) + ((storeValueDesc->numBytes)); + + /* + pushArrayDesc numBytes this gets added by nextBytecodePCFor:at:exts:in: */ + return 1; +} + + /* StackToRegisterMappingCogit>>#violatesEnsureSpilledSpillAssert */ +static sqInt +violatesEnsureSpilledSpillAssert(void) +{ + return 1; +} + + +/* Used when ReceiverResultReg is allocated for other than simSelf, and + there may be references to ReceiverResultReg which need to be spilled. */ + + /* StackToRegisterMappingCogit>>#voidReceiverResultRegContainsSelf */ +static void +voidReceiverResultRegContainsSelf(void) +{ + sqInt i; + sqInt iSqInt; + sqInt spillIndex; + + /* begin voidReceiverOptStatus */ + ((simSelf())->liveRegister = NoReg); + spillIndex = 0; + for (i = ((((methodOrBlockNumTemps + 1) < simSpillBase) ? simSpillBase : (methodOrBlockNumTemps + 1))); i <= simStackPtr; i += 1) { + if ((registerOrNone(simStackAt(i))) == ReceiverResultReg) { + spillIndex = i; + } + } + if (spillIndex > 0) { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= spillIndex) { + for (iSqInt = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < spillIndex) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : spillIndex)); iSqInt <= spillIndex; iSqInt += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(iSqInt), frameOffsetOfTemporary(iSqInt - 1), FPReg); + } + simSpillBase = spillIndex + 1; + } + } +} diff --git a/src/spur64.cog.livetyping/cogitX64SysV.c b/src/spur64.cog.livetyping/cogitX64SysV.c new file mode 100644 index 0000000000..663926bf63 --- /dev/null +++ b/src/spur64.cog.livetyping/cogitX64SysV.c @@ -0,0 +1,35613 @@ +/* Automatically generated by + CCodeGenerator * VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba + (Cog-eem.511, Compiler-ct.535) + from + LiveTypingCogit * VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba + */ +static char __buildInfo[] = "LiveTypingCogit * VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba " __DATE__ ; +char *__cogitBuildInfo = __buildInfo; + + + +#include "sqConfig.h" +#include +#include +#include +#include +#include "sqPlatformSpecific.h" +#include "sqMemoryAccess.h" +#include "sqCogStackAlignment.h" +#include "dispdbg.h" +#include "cogmethod.h" +#include "cointerp.h" +#include "cogit.h" + + +/*** Constants ***/ +#define ABICalleeSavedRegisterMask 0xF008 +#define ABICallerSavedRegisterMask 0xFC7 +#define ABIResultReg 0 +#define AddCqR 106 +#define AddCwR 114 +#define AddcCqR 120 +#define AddcRR 119 +#define AddRdRd 132 +#define AddRR 100 +#define AddRsRs 139 +#define AlignmentNops 3 +#define AltBlockCreationBytecodeSize 3 +#define AltFirstSpecialSelector 96 +#define AltNumSpecialSelectors 32 +#define AndCqR 108 +#define AndCwR 116 +#define AndRR 102 +#define AnnotationShift 5 +#define Arg0Reg 7 +#define Arg1Reg 6 +#define ArithmeticShiftRightCqR 91 +#define ArithmeticShiftRightRR 92 +#define BadRegisterSet 1 +#define BlockCreationBytecodeSize 4 +#define BSR 169 +#define BytecodeSetHasDirectedSuperSend 1 +#define CArg0Reg 7 +#define CArg1Reg 6 +#define CArg2Reg 2 +#define CArg3Reg 1 +#define Call 6 +#define CallerSavedRegisterMask 0x2C6 +#define CallFull 7 +#define CallR 8 +#define CDQ 161 +#if !defined(CheckRememberedInTrampoline) /* Allow this to be overridden on the compiler command line */ +# define CheckRememberedInTrampoline 0 +#endif +#define CLD 165 +#define ClassArray 7 +#define ClassArrayCompactIndex 51 +#define ClassBlockClosureCompactIndex 37 +#define ClassFloatCompactIndex 34 +#define ClassFullBlockClosureCompactIndex 38 +#define ClassLargeNegativeIntegerCompactIndex 32 +#define ClassLargePositiveIntegerCompactIndex 33 +#define ClassMethodContextCompactIndex 36 +#define ClassPointCompactIndex 54 +#define ClassReg 1 +#define ClosureFirstCopiedValueIndex 3 +#define ClosureIndex 4 +#define ClosureNumArgsIndex 2 +#define ClosureOuterContextIndex 0 +#define ClosureStartPCIndex 1 +#define ClzRR 157 +#define CMBlock 4 +#define CMClosedPIC 2 +#define CMFree 1 +#define CMMaxUsageCount 7 +#define CMMethod 5 +#define CMMethodFlaggedForBecome 6 +#define CMOpenPIC 3 +#define CmpC32R 113 +#define CmpCqR 105 +#define CmpCwR 112 +#define CmpRdRd 131 +#define CmpRR 99 +#define CmpRsRs 138 +#define CompletePrimitive 4 +#define ConstZero 1 +#define ConvertRdR 146 +#define ConvertRdRs 148 +#define ConvertRRd 145 +#define ConvertRRs 150 +#define ConvertRsR 149 +#define ConvertRsRd 147 +#define CPUID 160 +#if !defined(Debug) /* Allow this to be overridden on the compiler command line */ +# define Debug DEBUGVM +#endif +#define DisplacementMask 0x1F +#define DisplacementX2N 0 +#define DivRdRd 135 +#define DivRsRs 142 +#define DPFPReg0 0 +#define DPFPReg1 1 +#define DPFPReg2 2 +#define DPFPReg3 3 +#define DPFPReg4 4 +#define DPFPReg5 5 +#define DPFPReg6 6 +#define DPFPReg7 7 +#define EncounteredUnknownBytecode -6 +#define Extra0Reg 10 +#define Extra1Reg 11 +#define Extra2Reg 12 +#define Extra3Reg 13 +#define Extra4Reg 14 +#define Extra5Reg 15 +#define Fill32 4 +#define FirstAnnotation 64 +#define FirstJump 12 +#define FirstShortJump 16 +#define FirstSpecialSelector 176 +#define FoxCallerSavedIP 8 +#define FoxIFSavedIP -32 +#define FoxMethod -8 +#define FoxMFReceiver -24 +#define FoxSavedFP 0 +#define FoxThisContext -16 +#define FPReg 5 +#define FullClosureCompiledBlockIndex 1 +#define FullClosureFirstCopiedValueIndex 4 +#define FullClosureReceiverIndex 3 +#define GCModeBecome 8 +#define GCModeFull 1 +#define GCModeNewSpace 2 +#define HasBytecodePC 5 +#define HashMultiplyConstant 1664525 +#define HashMultiplyMask 0xFFFFFFF +#define HeaderIndex 0 +#define IDIVR 162 +#if !defined(IMMUTABILITY) /* Allow this to be overridden on the compiler command line */ +# define IMMUTABILITY 0 +#endif +#define IMULRR 163 +#define InFullBlock 2 +#define InstanceSpecificationIndex 2 +#define InstructionPointerIndex 1 +#define InsufficientCodeSpace -2 +#define InVanillaBlock 1 +#define IsAbsPCReference 3 +#define IsAnnotationExtension 1 +#define IsDirectedSuperBindingSend 10 +#define IsDirectedSuperSend 9 +#define IsDisplacementX2N 0 +#define IsNSSendCall null +#define IsObjectReference 2 +#define IsRelativeCall 4 +#define IsSendCall 7 +#define IsSuperSend 8 +#define Jump 16 +#define JumpAbove 33 +#define JumpAboveOrEqual 32 +#define JumpBelow 31 +#define JumpBelowOrEqual 34 +#define JumpCarry 25 +#define JumpFPEqual 35 +#define JumpFPGreater 39 +#define JumpFPGreaterOrEqual 40 +#define JumpFPLess 37 +#define JumpFPLessOrEqual 38 +#define JumpFPNotEqual 36 +#define JumpFPOrdered 41 +#define JumpFPUnordered 42 +#define JumpFull 12 +#define JumpGreater 29 +#define JumpGreaterOrEqual 28 +#define JumpLess 27 +#define JumpLessOrEqual 30 +#define JumpLong 13 +#define JumpLongNonZero 15 +#define JumpLongZero 14 +#define JumpNegative 19 +#define JumpNoCarry 26 +#define JumpNonNegative 20 +#define JumpNonZero 18 +#define JumpNoOverflow 22 +#define JumpOverflow 21 +#define JumpR 10 +#define JumpZero 17 +#define Label 1 +#define LargeContextSlots 62 +#define LastJump 42 +#define Literal 2 +#define LiteralStart 1 +#define LoadEffectiveAddressMwrR 88 +#define LogicalShiftLeftCqR 95 +#define LogicalShiftLeftRR 96 +#define LogicalShiftRightCqR 93 +#define LogicalShiftRightRR 94 +#define MapEnd 0 +#define MaxCompiledPrimitiveIndex 582 +#define MaxCPICCases 6 +#define MaxMethodSize 65535 +#define MaxNegativeErrorCode -8 +#define MaxStackAllocSize 1572864 +#define MaxStackCheckOffset 0xFFF +#define MaxX2NDisplacement 992 +#define MethodCacheClass 2 +#define MethodCacheMask 0xFFC +#define MethodCacheMethod 3 +#define MethodCacheSelector 1 +#define MethodIndex 3 +#define MethodTooBig -4 +#define MFMethodFlagHasContextFlag 1 +#define MFMethodFlagIsBlockFlag 2 +#define MOVSB 167 +#define MOVSQ 168 +#define ModReg 3 +#define ModRegInd 0 +#define ModRegRegDisp32 2 +#define ModRegRegDisp8 1 +#define MoveA32R 45 +#define MoveAbR 48 +#define MoveAwR 44 +#define MoveC32R 71 +#define MoveCqR 69 +#define MoveCwR 70 +#define MoveM16rR 57 +#define MoveM32rR 61 +#define MoveM32rRs 78 +#define MoveM64rRd 75 +#define MoveM8rR 54 +#define MoveMbrR 65 +#define MoveMs8rR 55 +#define MoveMwrR 50 +#define MovePerfCnt64RL 159 +#define MoveRA32 47 +#define MoveRAb 49 +#define MoveRAw 46 +#define MoveRdM64r 76 +#define MoveRdR 73 +#define MoveRdRd 74 +#define MoveRM16r 58 +#define MoveRM32r 62 +#define MoveRM8r 56 +#define MoveRMbr 66 +#define MoveRMwr 51 +#define MoveRR 43 +#define MoveRRd 72 +#define MoveRsM32r 79 +#define MoveRsRs 77 +#define MoveRX32rR 64 +#define MoveRXbrR 68 +#define MoveRXwrR 53 +#define MoveX32rRR 63 +#define MoveXbrRR 67 +#define MoveXwrRR 52 +#define MulRdRd 134 +#define MulRsRs 141 +#define NativeSPReg 4 +#define NeedsMergeFixupFlag 2 +#define NeedsNonMergeFixupFlag 1 +#define NegateR 89 +#define Nop 5 +#define NoReg -1 +#define NotFullyInitialized -1 +#define NumObjRefsInRuntime 0 +#define NumSendTrampolines 4 +#define NumSpecialSelectors 32 +#define NumStoreTrampolines 5 +#define NumTrampolines (75 + (IMMUTABILITY ? 5 : 0)) +#define OrCqR 109 +#define OrCwR 117 +#define OrRR 103 +#define PopR 80 +#define PrefetchAw 87 +#define PrimCallCollectsProfileSamples 16 +#define PrimCallIsExternalCall 32 +#define PrimCallMayEndureCodeCompaction 8 +#define PrimCallNeedsNewMethod 4 +#define PrimCallOnSmalltalkStack 1 +#define PrimCallOnSmalltalkStackAlign2x 2 +#define PrimNumberExternalCall 117 +#define PrimNumberFFICall 120 +#define PushCq 82 +#define PushCw 83 +#define PushR 81 +#define R12 12 +#define R13 13 +#define R15 15 +#define RAX 0 +#define RBP 5 +#define RBX 3 +#define RCX 1 +#define RDI 7 +#define RDX 2 +#define REP 166 +#define ReceiverIndex 5 +#define ReceiverResultReg 2 +#define RetN 9 +#define RISCTempReg 8 +#define RotateLeftCqR 97 +#define RotateRightCqR 98 +#define RSI 6 +#define RSP 4 +#define SelectorCannotInterpret 34 +#define SelectorDoesNotUnderstand 20 +#define SenderIndex 0 +#define SendNumArgsReg 9 +#define ShouldNotJIT -8 +#define SIB1 0 +#define SIB4 2 +#define SIB8 3 +#define SignExtend16RR 152 +#define SignExtend32RR 153 +#define SignExtend8RR 151 +#define SmallContextSlots 22 +#define SPReg 4 +#define SpecialSelectors 23 +#define SqrtRd 136 +#define SqrtRs 143 +#define SSBaseOffset 1 +#define SSConstant 2 +#define SSRegister 3 +#define SSSpill 4 +#define StackPointerIndex 2 +#define Stop 11 +#define SubbCqR 122 +#define SubbRR 121 +#define SubCqR 107 +#define SubCwR 115 +#define SubRdRd 133 +#define SubRR 101 +#define SubRsRs 140 +#define TempReg 0 +#define TstCqR 110 +#define UnfailingPrimitive 3 +#define UnimplementedPrimitive -7 +#define ValueIndex 1 +#define VarBaseReg 3 +#define XCHGRR 164 +#define XMM0L 0 +#define XorCqR 111 +#define XorCwR 118 +#define XorRdRd 137 +#define XorRR 104 +#define XorRsRs 144 +#define YoungSelectorInPIC -5 +#define ZeroExtend16RR 155 +#define ZeroExtend32RR 156 +#define ZeroExtend8RR 154 + +typedef struct _AbstractInstruction { + unsigned char opcode; + unsigned char machineCodeSize; + unsigned char maxSize; + unsigned char annotation; + usqInt operands[3]; + usqInt address; + struct _AbstractInstruction *dependent; + unsigned char machineCode[14]; + } AbstractInstruction; + +#define CogInLineLiteralsX64Compiler AbstractInstruction +#define CogX64Compiler AbstractInstruction +#define CogAbstractInstruction AbstractInstruction + + +typedef struct { + AbstractInstruction *fakeHeader; + AbstractInstruction *fillInstruction; + sqInt numArgs; + sqInt numCopied; + sqInt numInitialNils; + sqInt startpc; + AbstractInstruction *entryLabel; + AbstractInstruction *stackCheckLabel; + sqInt span; + sqInt hasInstVarRef; + } BlockStart; + +#define CogBlockStart BlockStart + + +typedef struct _BytecodeDescriptor { + sqInt (*generator)(void); + sqInt NoDbgRegParms (*spanFunction)(struct _BytecodeDescriptor *,sqInt,sqInt,sqInt); + sqInt NoDbgRegParms (*needsFrameFunction)(sqInt); + signed char stackDelta; + unsigned char opcode; + unsigned char numBytes; + unsigned isBranchTrue : 1; + unsigned isBranchFalse : 1; + unsigned isReturn : 1; + unsigned isBlockCreation : 1; + unsigned isMapped : 1; + unsigned isMappedInBlock : 1; + unsigned isExtension : 1; + unsigned isInstVarRef : 1; + unsigned is1ByteInstVarStore : 1; + unsigned hasUnsafeJump : 1; + } BytecodeDescriptor; + +#define CogBytecodeDescriptor BytecodeDescriptor + + +typedef struct { + sqInt (*primitiveGenerator)(void); + sqInt primNumArgs; + } PrimitiveDescriptor; + +#define CogPrimitiveDescriptor PrimitiveDescriptor + + +typedef struct { + char type; + char spilled; + signed char liveRegister; + signed char registerr; + sqInt offset; + sqInt constant; + sqInt bcptr; + } SimStackEntry; + +#define CogSimStackEntry SimStackEntry + + +typedef struct { + AbstractInstruction *targetInstruction; + unsigned char simStackPtr; + char isTargetOfBackwardBranch; + unsigned short instructionIndex; + } BytecodeFixup; + +#define CogSSBytecodeFixup BytecodeFixup +#define CogBytecodeFixup BytecodeFixup + + +typedef struct { + sqInt isReceiverResultRegLive; + SimStackEntry *ssEntry; + } CogSSOptStatus; + + + +/*** Function Prototypes ***/ + + +#if !PRODUCTION && defined(PlatformNoDbgRegParms) +# define NoDbgRegParms PlatformNoDbgRegParms +#endif + +#if !defined(NoDbgRegParms) +# define NoDbgRegParms /*empty*/ +#endif + + + +#if !defined(NeverInline) +# define NeverInline /*empty*/ +#endif + +static NoDbgRegParms AbstractInstruction * addDependent(AbstractInstruction *self_in_CogAbstractInstruction, AbstractInstruction *anInstruction); +static NoDbgRegParms sqInt availableFloatRegisterOrNoneFor(AbstractInstruction *self_in_CogAbstractInstruction, sqInt liveRegsMask); +static NoDbgRegParms AbstractInstruction * cloneLiteralFrom(AbstractInstruction *self_in_CogAbstractInstruction, AbstractInstruction *existingLiteral); +static NoDbgRegParms sqInt concretizeAt(AbstractInstruction *self_in_CogAbstractInstruction, sqInt actualAddress); +static NoDbgRegParms sqInt genLoadCStackPointer(AbstractInstruction *self_in_CogAbstractInstruction); +static NoDbgRegParms sqInt genLoadCStackPointers(AbstractInstruction *self_in_CogAbstractInstruction); +static NoDbgRegParms sqInt genLoadStackPointerForPrimCall(AbstractInstruction *self_in_CogAbstractInstruction, sqInt spareReg); +static NoDbgRegParms sqInt genLoadStackPointers(AbstractInstruction *self_in_CogAbstractInstruction); +static NoDbgRegParms sqInt genLoadStackPointersForPrimCall(AbstractInstruction *self_in_CogAbstractInstruction, sqInt spareReg); +static NoDbgRegParms sqInt genSaveStackPointers(AbstractInstruction *self_in_CogAbstractInstruction); +static NoDbgRegParms AbstractInstruction * genWriteCResultIntoReg(AbstractInstruction *self_in_CogAbstractInstruction, sqInt abstractRegister); +static NoDbgRegParms sqInt has64BitPerformanceCounter(AbstractInstruction *self_in_CogAbstractInstruction); +static NoDbgRegParms AbstractInstruction * initializeSharableLiteral(AbstractInstruction *self_in_CogAbstractInstruction, sqInt literal); +static NoDbgRegParms AbstractInstruction * initializeUniqueLiteral(AbstractInstruction *self_in_CogAbstractInstruction, sqInt literal); +static NoDbgRegParms sqInt instructionIsPCRelativeLoad(AbstractInstruction *self_in_CogAbstractInstruction, sqInt instr); +static NoDbgRegParms int isJump(AbstractInstruction *self_in_CogAbstractInstruction); +static NoDbgRegParms sqInt perfCounterRatePerUsec(AbstractInstruction *self_in_CogAbstractInstruction); +static NoDbgRegParms AbstractInstruction * relocateJumpLongBeforeFollowingAddressby(AbstractInstruction *self_in_CogAbstractInstruction, sqInt pc, sqInt delta); +static NoDbgRegParms AbstractInstruction * relocateJumpLongConditionalBeforeFollowingAddressby(AbstractInstruction *self_in_CogAbstractInstruction, sqInt pc, sqInt delta); +static NoDbgRegParms AbstractInstruction * resolveJumpTarget(AbstractInstruction *self_in_CogAbstractInstruction); +static NoDbgRegParms sqInt rewriteConditionalJumpLongAttarget(AbstractInstruction *self_in_CogAbstractInstruction, sqInt callSiteReturnAddress, sqInt callTargetAddress); +static NoDbgRegParms sqInt widthOfPCRelativeLoad(AbstractInstruction *self_in_CogAbstractInstruction, sqInt instr); +static NoDbgRegParms CogMethod * cmHomeMethod(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMBlock(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMClosedPIC(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMFree(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMMethodEtAl(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMOpenPIC(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms sqInt isBranch(BytecodeDescriptor *self_in_CogBytecodeDescriptor); +static NoDbgRegParms sqInt isConditionalBranch(BytecodeDescriptor *self_in_CogBytecodeDescriptor); +static NoDbgRegParms int computeSizeOfArithCqR(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler); +static NoDbgRegParms int computeSizeOfArithCwR(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler); +static NoDbgRegParms sqInt concretizeArithCwR(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt x64opcode); +static NoDbgRegParms sqInt concretizeMoveCwR(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler); +static NoDbgRegParms unsigned int inlineCacheTagAt(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt callSiteReturnAddress); +static NoDbgRegParms sqInt isPCDependent(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler); +static NoDbgRegParms unsigned int literal32BeforeFollowingAddress(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt followingAddress); +static NoDbgRegParms sqInt literalBeforeFollowingAddress(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt followingAddress); +static NoDbgRegParms sqInt loadLiteralByteSize(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler); +static NoDbgRegParms unsigned char sizePCDependentInstructionAt(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt eventualAbsoluteAddress); +static NoDbgRegParms AbstractInstruction * storeLiteralbeforeFollowingAddress(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt literal, sqInt followingAddress); +static NoDbgRegParms AbstractInstruction * gAddCqR(sqInt quickConstant, sqInt reg); +static NoDbgRegParms AbstractInstruction * gAddCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg); +static NoDbgRegParms AbstractInstruction * gAndCqR(sqInt quickConstant, sqInt reg); +static NoDbgRegParms AbstractInstruction * gAndCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg); +static NoDbgRegParms AbstractInstruction * gArithmeticShiftRightCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg); +extern sqInt abortOffset(void); +static NoDbgRegParms int abstractInstructionfollows(AbstractInstruction *theAbstractInstruction, AbstractInstruction *anAbstractInstruction); +static void addCleanBlockStarts(void); +extern void addCogMethodsToHeapMap(void); +static NoDbgRegParms sqInt addressIsInFixups(BytecodeFixup *address); +static NoDbgRegParms sqInt addressOfEndOfCaseinCPIC(sqInt n, CogMethod *cPIC); +static void alignMethodZoneBase(void); +static NoDbgRegParms sqInt alignUptoRoutineBoundary(sqInt anAddress); +static sqInt allMachineCodeObjectReferencesValid(void); +static sqInt allMethodsHaveCorrectHeader(void); +static NoDbgRegParms AbstractInstruction * annotateAbsolutePCRef(AbstractInstruction *abstractInstruction); +static NoDbgRegParms AbstractInstruction * annotateBytecode(AbstractInstruction *abstractInstruction); +static NoDbgRegParms AbstractInstruction * annotateobjRef(AbstractInstruction *abstractInstruction, sqInt anOop); +static NoDbgRegParms void assertSaneJumpTarget(AbstractInstruction *jumpTarget); +static NoDbgRegParms sqInt availableRegisterOrNoneIn(sqInt liveRegsMask); +static NoDbgRegParms usqInt blockDispatchTargetsForperformarg(CogMethod *cogMethod, usqInt (*binaryFunction)(sqInt mcpc, sqInt arg), sqInt arg); +extern sqInt bytecodePCForstartBcpcin(sqInt mcpc, sqInt startbcpc, CogBlockMethod *cogMethod); +static NoDbgRegParms AbstractInstruction * gCall(sqInt callTarget); +static NoDbgRegParms AbstractInstruction * gCmpCqR(sqInt quickConstant, sqInt reg); +static void callCogCodePopReceiver(void); +static void callCogCodePopReceiverAndClassRegs(void); +static NoDbgRegParms sqInt ceCPICMissreceiver(CogMethod *cPIC, sqInt receiver); +static NoDbgRegParms sqInt ceSICMiss(sqInt receiver); +static NoDbgRegParms sqInt checkIfValidOopRefAndTargetpccogMethod(sqInt annotation, char *mcpc, CogMethod *cogMethod); +static NoDbgRegParms sqInt checkIfValidOopRefpccogMethod(sqInt annotation, char *mcpc, CogMethod *cogMethod); +extern sqInt checkIntegrityOfObjectReferencesInCode(sqInt gcModes); +static NoDbgRegParms sqInt checkMaybeObjRefInClosedPIC(sqInt maybeObject); +static NoDbgRegParms sqInt checkValidObjectReferencesInClosedPIC(CogMethod *cPIC); +static NoDbgRegParms NeverInline sqInt cleanUpFailingCogCodeConstituents(CogMethod *cogMethodArg); +static NoDbgRegParms sqInt closedPICRefersToUnmarkedObject(CogMethod *cPIC); +extern char * codeEntryFor(char *address); +extern char * codeEntryNameFor(char *address); +extern usqInt cogCodeBase(void); +extern sqInt cogCodeConstituents(sqInt withDetails); +static NoDbgRegParms void cogExtendPICCaseNMethodtagisMNUCase(CogMethod *cPIC, sqInt caseNMethod, sqInt caseNTag, sqInt isMNUCase); +extern CogMethod * cogFullBlockMethodnumCopied(sqInt aMethodObj, sqInt numCopied); +extern void cogitPostGCAction(sqInt gcMode); +static NoDbgRegParms sqInt cogMethodDoesntLookKosher(CogMethod *cogMethod); +extern CogMethod * cogMNUPICSelectorreceivermethodOperandnumArgs(sqInt selector, sqInt rcvr, sqInt methodOperand, sqInt numArgs); +static NoDbgRegParms CogMethod * cogOpenPICSelectornumArgs(sqInt selector, sqInt numArgs); +static NoDbgRegParms CogMethod * cogPICSelectornumArgsCase0MethodCase1MethodtagisMNUCase(sqInt selector, sqInt numArgs, CogMethod *case0CogMethod, sqInt case1MethodOrNil, sqInt case1Tag, sqInt isMNUCase); +extern CogMethod * cogselector(sqInt aMethodObj, sqInt aSelectorOop); +static NoDbgRegParms sqInt collectCogConstituentForAnnotationMcpcBcpcMethod(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *cogMethodArg); +static NoDbgRegParms sqInt collectCogConstituentOnSpurForAnnotationMcpcBcpcData(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *dataArg); +static NoDbgRegParms sqInt collectCogMethodConstituentOnSpurofClass(CogMethod *cogMethod, sqInt dataClass); +static NoDbgRegParms sqInt collectCogMethodConstituent(CogMethod *cogMethod); +extern void compactCogCompiledCode(void); +static void compactPICsWithFreedTargets(void); +static AbstractInstruction * compileAbort(void); +static NoDbgRegParms sqInt compileBlockDispatchFromto(sqInt lowBlockStartIndex, sqInt highBlockStartIndex); +static NoDbgRegParms void compileBlockEntry(BlockStart *blockStart); +static NoDbgRegParms void compileCallFornumArgsargargargargresultRegregsToSave(void *aRoutine, sqInt numArgs, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3, sqInt resultRegOrNone, sqInt regMask); +static AbstractInstruction * compileCPICEntry(void); +static NoDbgRegParms sqInt compileEntireFullBlockMethod(sqInt numCopied); +static void compileEntry(void); +static sqInt compileFullBlockEntry(void); +static sqInt compileMethodBody(void); +static NoDbgRegParms sqInt compilePICAbort(sqInt numArgs); +static NoDbgRegParms AbstractInstruction * compileStackOverflowCheck(sqInt canContextSwitch); +static NoDbgRegParms void compileTrampolineFornumArgsargargargargregsToSavepushLinkRegresultReg(void *aRoutine, sqInt numArgs, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3, sqInt regMask, sqInt pushLinkReg, sqInt resultRegOrNone); +static void computeEntryOffsets(void); +static void computeFullBlockEntryOffsets(void); +extern usqInt computeGoodVarBaseAddress(void); +static void computeMaximumSizes(void); +static NoDbgRegParms sqInt configureCPICCase0Case1MethodtagisMNUCasenumArgsdelta(CogMethod *cPIC, CogMethod *case0CogMethod, sqInt case1Method, sqInt case1Tag, sqInt isMNUCase, sqInt numArgs, sqInt addrDelta); +static NoDbgRegParms sqInt configureMNUCPICmethodOperandnumArgsdelta(CogMethod *cPIC, sqInt methodOperand, sqInt numArgs, sqInt addrDelta); +#if VMInvestigations +static NoDbgRegParms sqInt countCogMethodDataImportspccount(sqInt annotation, char *mcpc, sqInt *countPtr); +#endif /* VMInvestigations */ +static NoDbgRegParms sqInt cPICCompactAndIsNowEmpty(CogMethod *cPIC); +static NoDbgRegParms sqInt cPICHasForwardedClass(CogMethod *cPIC); +static NoDbgRegParms sqInt cPICHasFreedTargets(CogMethod *cPIC); +static sqInt cPICPrototypeCaseOffset(void); +static NoDbgRegParms sqInt cPICHasTarget(CogMethod *cPIC, CogMethod *targetMethod); +static NoDbgRegParms sqInt createCPICData(CogMethod *cPIC); +static NoDbgRegParms AbstractInstruction * gDivRRQuoRem(sqInt rDivisor, sqInt rDividend, sqInt rQuotient, sqInt rRemainder); +extern int defaultCogCodeSize(void); +static NoDbgRegParms sqInt deltaToSkipPrimAndErrorStoreInheader(sqInt aMethodObj, sqInt aMethodHeader); +static NoDbgRegParms sqInt endPCOf(sqInt aMethod); +static void enterCogCodePopReceiver(void); +static NoDbgRegParms sqInt entryPointTagIsSelector(sqInt entryPoint); +static NoDbgRegParms sqInt expectedClosedPICPrototype(CogMethod *cPIC); +static sqInt extABytecode(void); +static sqInt extBBytecode(void); +static NoDbgRegParms sqInt fillInBlockHeadersAt(sqInt startAddress); +static NoDbgRegParms void fillInMethodHeadersizeselector(CogMethod *method, sqInt size, sqInt selector); +static NoDbgRegParms sqInt findBackwardBranchIsBackwardBranchMcpcBcpcMatchingBcpc(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *targetBcpc); +static NoDbgRegParms usqInt findBlockMethodWithEntrystartBcpc(sqInt blockEntryMcpc, sqInt startBcpc); +static NoDbgRegParms sqInt findMapLocationForMcpcinMethod(usqInt targetMcpc, CogMethod *cogMethod); +extern CogBlockMethod * findMethodForStartBcpcinHomeMethod(sqInt startbcpc, CogMethod *cogMethod); +static NoDbgRegParms sqInt findIsBackwardBranchMcpcBcpcMatchingMcpc(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *targetMcpc); +static NoDbgRegParms usqInt firstMappedPCFor(CogMethod *cogMethod); +static sqInt firstPrototypeMethodOop(void); +static NoDbgRegParms BytecodeFixup * fixupAt(sqInt fixupPC); +extern void flagCogMethodForBecome(CogMethod *cogMethod); +static NoDbgRegParms void followForwardedLiteralsImplementationIn(CogMethod *cogMethod); +extern void followForwardedLiteralsIn(CogMethod *cogMethod); +extern void followMovableLiteralsAndUpdateYoungReferrers(void); +extern void freeBecomeFlaggedMethods(void); +extern void freeCogMethod(CogMethod *cogMethod); +extern void freeUnmarkedMachineCode(void); +static NoDbgRegParms AbstractInstruction * genCallMustBeBooleanFor(sqInt boolean); +static NoDbgRegParms AbstractInstruction * genConditionalBranchoperand(sqInt opcode, sqInt operandOne); +static NoDbgRegParms void (*genEnilopmartForandandforCallcalled(sqInt regArg1, sqInt regArg2OrNone, sqInt regArg3OrNone, sqInt forCall, char *trampolineName))(void); +static NoDbgRegParms void genEnilopmartReturn(sqInt forCall); +static NoDbgRegParms NeverInline void generateCaptureCStackPointers(sqInt captureFramePointer); +static void generateClosedPICPrototype(void); +static CogMethod * generateCogFullBlock(void); +static NoDbgRegParms CogMethod * generateCogMethod(sqInt selector); +static NoDbgRegParms sqInt generateMapAtstart(usqInt addressOrNull, usqInt startAddress); +static void generateOpenPICPrototype(void); +static void generateStackPointerCapture(void); +static void generateTrampolines(void); +static NoDbgRegParms BytecodeDescriptor * generatorForPC(sqInt pc); +static void genGetLeafCallStackPointers(void); +static NoDbgRegParms usqInt genInnerPICAbortTrampoline(char *name); +static void (*genInvokeInterpretTrampoline(void))(void); +static NoDbgRegParms void genLoadInlineCacheWithSelector(sqInt selectorIndex); +static usqInt genReturnToInterpreterTrampoline(void); +static NoDbgRegParms sqInt genSmalltalkToCStackSwitch(sqInt pushLinkReg); +static NoDbgRegParms usqInt genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(void *aRoutine, char *trampolineName, sqInt numArgs, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3, sqInt regMask, sqInt pushLinkReg, sqInt resultRegOrNone, sqInt appendBoolean); +static NoDbgRegParms void genTrampolineReturn(sqInt lnkRegWasPushed); +static NoDbgRegParms AbstractInstruction * gen(sqInt opcode); +static NoDbgRegParms AbstractInstruction * genoperand(sqInt opcode, sqInt operand); +static NoDbgRegParms AbstractInstruction * genoperandoperand(sqInt opcode, sqInt operandOne, sqInt operandTwo); +static NoDbgRegParms AbstractInstruction * genoperandoperandoperand(sqInt opcode, sqInt operandOne, sqInt operandTwo, sqInt operandThree); +static NoDbgRegParms sqInt getCogCodeConstituents(sqInt withDetails); +static NoDbgRegParms sqInt getLiteral(sqInt litIndex); +#if VMInvestigations +extern usqInt getMethodZoneBase(void); +#endif /* VMInvestigations */ +static NoDbgRegParms sqInt incrementUsageOfTargetIfLinkedSendmcpcignored(sqInt annotation, char *mcpc, sqInt superfluity); +static NoDbgRegParms sqInt indexForSelectorin(sqInt selector, CogMethod *cogMethod); +extern void initializeCodeZoneFromupTo(sqInt startAddress, sqInt endAddress); +static sqInt initialMethodUsageCount(void); +static int initialOpenPICUsageCount(void); +static NoDbgRegParms int intraCPICTargetinvokesMNUIn(sqInt targetMcpc, CogMethod *cPIC); +static NoDbgRegParms sqInt inverseBranchFor(sqInt opcode); +#if VMInvestigations +static NoDbgRegParms sqInt investigationArrayForClosedPIC(CogMethod *cPIC); +#endif /* VMInvestigations */ +#if VMInvestigations +static NoDbgRegParms sqInt investigationTargetForEntryPointin(sqInt entryPoint, CogMethod *cPIC); +#endif /* VMInvestigations */ +extern int isPCWithinMethodZone(usqInt address); +extern sqInt isSendReturnPC(sqInt retpc); +static NoDbgRegParms AbstractInstruction * gJumpFPEqual(void *jumpTarget); +static NoDbgRegParms AbstractInstruction * gJumpFPGreaterOrEqual(void *jumpTarget); +static NoDbgRegParms AbstractInstruction * gJumpFPGreater(void *jumpTarget); +static NoDbgRegParms AbstractInstruction * gJumpFPNotEqual(void *jumpTarget); +static NoDbgRegParms AbstractInstruction * gLogicalShiftLeftCqR(sqInt quickConstant, sqInt reg); +static NoDbgRegParms AbstractInstruction * gLogicalShiftLeftCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg); +static NoDbgRegParms AbstractInstruction * gLogicalShiftRightCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg); +static AbstractInstruction * lastOpcode(void); +extern CogMethod * linkedSendTargetOrNilAt(sqInt retpc); +extern void linkSendAtintooffsetreceiver(sqInt callSiteReturnAddress, CogMethod *sendingMethod, CogMethod *targetMethod, sqInt theEntryOffset, sqInt receiver); +static NoDbgRegParms BytecodeDescriptor * loadBytesAndGetDescriptor(sqInt pc); +static NoDbgRegParms AbstractInstruction * gMovePerfCnt64RL(sqInt destReg, sqInt liveRegisterMask); +static NoDbgRegParms sqInt mapEndFor(CogMethod *cogMethod); +static NoDbgRegParms sqInt mapForperformUntilarg(CogMethod *cogMethod, sqInt (*functionSymbol)(sqInt annotation, char *mcpc, CogMethod *arg), void *arg); +static NoDbgRegParms sqInt mapObjectReferencesInClosedPIC(CogMethod *cPIC); +static void mapObjectReferencesInGeneratedRuntime(void); +static void mapObjectReferencesInMachineCodeForBecome(void); +static void mapObjectReferencesInMachineCodeForFullGC(void); +static void mapObjectReferencesInMachineCodeForYoungGC(void); +extern void mapObjectReferencesInMachineCode(sqInt gcMode); +extern void markAndTraceMachineCodeOfMarkedMethods(void); +static void markAndTraceObjectReferencesInGeneratedRuntime(void); +static NoDbgRegParms sqInt markAndTraceOrFreeCogMethodfirstVisit(CogMethod *cogMethod, sqInt firstVisit); +static NoDbgRegParms sqInt markAndTraceOrFreePICTargetin(sqInt entryPoint, CogMethod *cPIC); +static NoDbgRegParms sqInt markLiteralsAndUnlinkIfUnmarkedSendpcmethod(sqInt annotation, char *mcpc, CogMethod *cogMethod); +static NoDbgRegParms sqInt markLiteralspcmethod(sqInt annotation, char *mcpc, CogMethod *cogMethod); +extern void markMethodAndReferents(CogBlockMethod *aCogMethod); +extern usqInt maxCogMethodAddress(void); +static NoDbgRegParms sqInt maximumDistanceFromCodeZone(sqInt anAddress); +static NoDbgRegParms sqInt maybeFreeCogMethodDoesntLookKosher(CogMethod *cogMethod); +static int mclassCouldBeContext(void); +static int mclassIsSmallInteger(void); +extern usqInt mcPCForBackwardBranchstartBcpcin(sqInt bcpc, sqInt startbcpc, CogBlockMethod *cogMethod); +static NoDbgRegParms sqInt methodhasSameCodeAscheckPenultimate(sqInt methodA, sqInt methodB, sqInt comparePenultimateLiteral); +extern sqInt mnuOffset(void); +static NoDbgRegParms AbstractInstruction * gNativePopR(sqInt reg); +static NoDbgRegParms AbstractInstruction * gNativePushR(sqInt reg); +static NoDbgRegParms AbstractInstruction * gNativeRetN(sqInt offset); +static NoDbgRegParms sqInt needsFrameIfImmutability(sqInt stackDelta); +static NoDbgRegParms sqInt needsFrameIfInBlock(sqInt stackDelta); +static NoDbgRegParms sqInt needsFrameIfReadMediated(sqInt stackDelta); +static NoDbgRegParms sqInt needsFrameNever(sqInt stackDelta); +static NoDbgRegParms sqInt noAssertMethodClassAssociationOf(sqInt methodPointer); +static sqInt noCogMethodsMaximallyMarked(void); +static NoDbgRegParms int noTargetsFreeInClosedPIC(CogMethod *cPIC); +static NoDbgRegParms AbstractInstruction * gOrCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg); +static NoDbgRegParms sqInt outputInstructionsAt(sqInt startAddress); +static NoDbgRegParms sqInt outputInstructionsForGeneratedRuntimeAt(sqInt startAddress); +extern sqInt patchToOpenPICFornumArgsreceiver(sqInt selector, sqInt numArgs, sqInt receiver); +static sqInt picAbortDiscriminatorValue(void); +static sqInt picInterpretAbortOffset(void); +extern void printCogMethodFor(void *address); +#if VMInvestigations +extern void printPCMapPairsFor(CogMethod *cogMethod); +#endif /* VMInvestigations */ +extern void printTrampolineTable(void); +static sqInt processorHasDivQuoRemAndMClassIsSmallInteger(void); +static sqInt processorHasMultiplyAndMClassIsSmallInteger(void); +static NoDbgRegParms void recordGeneratedRunTimeaddress(char *aString, sqInt address); +extern int recordPrimTraceFunc(void); +static void recordRunTimeObjectReferences(void); +static NoDbgRegParms sqInt registerMaskFor(sqInt reg); +static NoDbgRegParms sqInt registerMaskForand(sqInt reg1, sqInt reg2); +static NoDbgRegParms void relocateCallsAndSelfReferencesInMethod(CogMethod *cogMethod); +static NoDbgRegParms void relocateCallsInClosedPIC(CogMethod *cPIC); +static NoDbgRegParms sqInt relocateIfCallOrMethodReferencemcpcdelta(sqInt annotation, char *mcpc, void *refDeltaArg); +static NoDbgRegParms sqInt remapIfObjectRefpchasYoung(sqInt annotation, char *mcpc, void *hasYoungPtr); +static NoDbgRegParms sqInt remapMaybeObjRefInClosedPICAt(sqInt mcpc); +static NoDbgRegParms void rewriteCPICCaseAttagobjReftarget(sqInt followingAddress, sqInt newTag, sqInt newObjRef, sqInt newTarget); +static NoDbgRegParms AbstractInstruction * gSubRRR(sqInt subReg, sqInt fromReg, sqInt destReg); +static sqInt scanForCleanBlocks(void); +extern void setSelectorOfto(CogMethod *cogMethod, sqInt aSelectorOop); +static NoDbgRegParms sqInt spanForCleanBlockStartingAt(sqInt startPC); +#if VMInvestigations +static NoDbgRegParms sqInt storeCogMethodDataImportspcpointer(sqInt annotation, char *mcpc, usqInt **importsPtrPtr); +#endif /* VMInvestigations */ +#if VMInvestigations +static NoDbgRegParms sqInt stringForEntryPointexistingImportslimit(char *entryPoint, sqInt imports, sqInt *nextStoreLocation); +#endif /* VMInvestigations */ +static sqInt subsequentPrototypeMethodOop(void); +static NoDbgRegParms AbstractInstruction * gTstCqR(sqInt quickConstant, sqInt reg); +extern sqInt traceLinkedSendOffset(void); +static NoDbgRegParms char * trampolineNamenumArgs(char *routinePrefix, sqInt numArgs); +static NoDbgRegParms char * trampolineNamenumArgslimit(char *routinePrefix, int numArgs, sqInt argsLimit); +static NoDbgRegParms char * trampolineNamenumRegArgs(char *routinePrefix, sqInt numArgs); +extern void unflagBecomeFlaggedMethods(void); +static sqInt unknownBytecode(void); +extern void unlinkAllSends(void); +static NoDbgRegParms sqInt unlinkIfFreeOrLinkedSendpcof(sqInt annotation, char *mcpc, void *theSelector); +static NoDbgRegParms sqInt unlinkIfInvalidClassSendpcignored(sqInt annotation, char *mcpc, sqInt superfluity); +static NoDbgRegParms sqInt unlinkIfLinkedSendToFreepcignored(sqInt annotation, char *mcpc, sqInt superfluity); +static NoDbgRegParms sqInt unlinkIfLinkedSendpcif(sqInt annotation, char *mcpc, sqInt (*criterion)(CogMethod *)); +static NoDbgRegParms sqInt unlinkIfLinkedSendpcignored(sqInt annotation, char *mcpc, sqInt superfluity); +static NoDbgRegParms sqInt unlinkIfLinkedSendpcto(sqInt annotation, char *mcpc, CogMethod *theCogMethod); +extern void unlinkSendsLinkedForInvalidClasses(void); +extern void unlinkSendsOfisMNUSelector(sqInt selector, sqInt isMNUSelector); +static void unlinkSendsToFree(void); +extern void unlinkSendsToMethodsSuchThatAndFreeIf(sqInt (*criterion)(CogMethod *), sqInt freeIfTrue); +extern void unlinkSendsToandFreeIf(sqInt targetMethodObject, sqInt freeIfTrue); +static NoDbgRegParms sqInt v3BlockCodeSize(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +static NoDbgRegParms sqInt v3LongForwardBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +static NoDbgRegParms sqInt v3LongBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +static NoDbgRegParms sqInt v3ShortForwardBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +static NoDbgRegParms sqInt v4BlockCodeSize(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +static NoDbgRegParms sqInt v4LongForwardBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +static NoDbgRegParms sqInt v4LongBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +extern void voidCogCompiledCode(void); +static void zeroOpcodeIndex(void); +static NoDbgRegParms void addToOpenPICList(CogMethod *anOpenPIC); +static NoDbgRegParms void addToYoungReferrers(CogMethod *writableCogMethod); +static NoDbgRegParms usqInt allocate(sqInt numBytes); +extern CogMethod * cogMethodContaining(usqInt mcpc); +static void compactCompiledCode(void); +static NoDbgRegParms void ensureInYoungReferrers(CogMethod *cogMethod); +static NoDbgRegParms void freeMethod(CogMethod *cogMethod); +static void freeOlderMethodsForCompaction(void); +extern sqInt kosherYoungReferrers(void); +extern CogMethod * methodFor(void *address); +#if VMInvestigations +extern sqInt methodsCompiledToMachineCodeInto(sqInt arrayObj); +#endif /* VMInvestigations */ +extern sqInt numMethods(void); +extern sqInt numMethodsOfType(sqInt cogMethodType); +static NoDbgRegParms sqInt occurrencesInYoungReferrers(CogMethod *cogMethod); +static NoDbgRegParms CogMethod * openPICWithSelector(sqInt aSelector); +static void planCompaction(void); +extern void printCogMethods(void); +extern void printCogMethodsOfType(sqInt cmType); +extern void printCogMethodsWithMethod(sqInt methodOop); +extern void printCogMethodsWithPrimitive(sqInt primIdx); +extern void printCogMethodsWithSelector(sqInt selectorOop); +extern void printCogYoungReferrers(void); +extern sqInt printOpenPICList(void); +static sqInt pruneYoungReferrers(void); +static sqInt relocateAndPruneYoungReferrers(void); +static sqInt relocateMethodsPreCompaction(void); +static NoDbgRegParms sqInt removeFromOpenPICList(CogMethod *anOpenPIC); +static NoDbgRegParms int roundUpLength(sqInt numBytes); +static void voidOpenPICList(void); +static void voidUnpairedMethodList(void); +extern char * whereIsMaybeCodeThing(sqInt anOop); +static sqInt zoneAlignment(void); +static NoDbgRegParms sqInt checkValidObjectReference(sqInt anOop); +static NoDbgRegParms AbstractInstruction * genCmpClassFloatCompactIndexR(sqInt reg); +static NoDbgRegParms AbstractInstruction * genCmpClassMethodContextCompactIndexR(sqInt reg); +static NoDbgRegParms sqInt genGetMethodHeaderOfintoscratch(sqInt methodReg, sqInt headerReg, sqInt scratchReg); +static NoDbgRegParms AbstractInstruction * genJumpNotSmallIntegersInandscratch(sqInt aRegister, sqInt bRegister, sqInt scratchRegister); +static NoDbgRegParms sqInt genLoadSlotsourceRegdestReg(sqInt index, sqInt sourceReg, sqInt destReg); +static sqInt genPrimitiveAdd(void); +static sqInt genPrimitiveBitAnd(void); +static sqInt genPrimitiveBitOr(void); +static sqInt genPrimitiveBitShift(void); +static sqInt genPrimitiveBitXor(void); +static sqInt genPrimitiveClass(void); +static sqInt genPrimitiveDiv(void); +static sqInt genPrimitiveDivide(void); +static sqInt genPrimitiveEqual(void); +static sqInt genPrimitiveFloatAdd(void); +static sqInt genPrimitiveFloatDivide(void); +static sqInt genPrimitiveFloatMultiply(void); +static sqInt genPrimitiveFloatSubtract(void); +static sqInt genPrimitiveGreaterOrEqual(void); +static sqInt genPrimitiveGreaterThan(void); +static sqInt genPrimitiveHighBit(void); +static sqInt genPrimitiveIdentical(void); +static sqInt genPrimitiveLessOrEqual(void); +static sqInt genPrimitiveLessThan(void); +static sqInt genPrimitiveMaxIdentityHash(void); +static sqInt genPrimitiveMod(void); +static sqInt genPrimitiveMultiply(void); +static sqInt genPrimitiveNewMethod(void); +static sqInt genPrimitiveNotEqual(void); +static sqInt genPrimitiveNotIdentical(void); +static sqInt genPrimitiveQuo(void); +static sqInt genPrimitiveSubtract(void); +static NoDbgRegParms sqInt genSmallIntegerComparison(sqInt jumpOpcode); +static NoDbgRegParms sqInt isUnannotatableConstant(SimStackEntry *simStackEntry); +static NoDbgRegParms sqInt classForInlineCacheTag(sqInt classIndex); +static NoDbgRegParms sqInt genAddSmallIntegerTagsTo(sqInt aRegister); +static NoDbgRegParms sqInt genClearAndSetSmallIntegerTagsIn(sqInt scratchReg); +static NoDbgRegParms sqInt genConvertBitsToSmallFloatInscratch(sqInt reg, sqInt scratch); +static NoDbgRegParms void genConvertCharacterToSmallIntegerInReg(sqInt reg); +static NoDbgRegParms sqInt genConvertIntegerInRegtoSmallIntegerInReg(sqInt srcReg, sqInt destReg); +static NoDbgRegParms sqInt genConvertIntegerToSmallIntegerInReg(sqInt reg); +static NoDbgRegParms sqInt genConvertSmallFloatToSmallFloatHashAsIntegerInRegscratch(sqInt reg, sqInt scratch); +static NoDbgRegParms void genConvertSmallIntegerToCharacterInReg(sqInt reg); +static NoDbgRegParms sqInt genConvertSmallIntegerToIntegerInReg(sqInt reg); +static NoDbgRegParms sqInt genFetchRegArgsForPerformWithArguments(sqInt sizeReg); +static NoDbgRegParms sqInt genFloatArithmeticpreOpCheckboxed(sqInt arithmeticOperator, AbstractInstruction *(*preOpCheckOrNil)(int rcvrReg, int argReg), sqInt rcvrBoxed); +static NoDbgRegParms sqInt genFloatComparisonorIntegerComparisoninvertboxed(AbstractInstruction *(*jumpFPOpcodeGenerator)(void *), sqInt jumpOpcode, sqInt invertComparison, sqInt rcvrBoxed); +static NoDbgRegParms sqInt genGetHashFieldNonImmOfasSmallIntegerInto(sqInt instReg, sqInt destReg); +static NoDbgRegParms sqInt genGetHashFieldNonImmOfinto(sqInt instReg, sqInt destReg); +static NoDbgRegParms AbstractInstruction * genGetInlineCacheClassTagFromintoforEntry(sqInt sourceReg, sqInt destReg, sqInt forEntry); +static NoDbgRegParms sqInt genGetNumBytesOfinto(sqInt srcReg, sqInt destReg); +static NoDbgRegParms sqInt genGetOverflowSlotsOfinto(sqInt srcReg, sqInt destReg); +static NoDbgRegParms sqInt genGetSmallFloatValueOfscratchinto(sqInt oopReg, sqInt scratch, sqInt dpReg); +static NoDbgRegParms AbstractInstruction * genInstantiate64BitPositiveIntegerValueintoscratchRegmayFail(sqInt valueReg, sqInt resultReg, sqInt scratch, sqInt mayFail); +static NoDbgRegParms AbstractInstruction * genInstantiate64BitSignedIntegerValueintoscratchRegmayFail(sqInt valueReg, sqInt resultReg, sqInt scratch, sqInt mayFail); +static NoDbgRegParms AbstractInstruction * genInstantiateFloatValueintoscratchRegscratchRegmayFail(sqInt dpreg, sqInt resultReg, sqInt scratch1, sqInt scratch2, sqInt mayFail); +static NoDbgRegParms AbstractInstruction * genJumpIsSmallIntegerValuescratch(sqInt aRegister, sqInt scratchReg); +static NoDbgRegParms AbstractInstruction * genJumpNotCharacter(sqInt reg); +static NoDbgRegParms AbstractInstruction * genJumpNotSmallFloatValueBitsscratch(sqInt reg, sqInt exponent); +static NoDbgRegParms AbstractInstruction * genJumpNotSmallFloat(sqInt reg); +static NoDbgRegParms AbstractInstruction * genJumpNotSmallIntegerValuescratch(sqInt aRegister, sqInt scratchReg); +static NoDbgRegParms AbstractInstruction * genJumpNotSmallInteger(sqInt reg); +static NoDbgRegParms AbstractInstruction * genJumpSmallInteger(sqInt aRegister); +static NoDbgRegParms sqInt genPrimitiveAtPutSigned(sqInt signedVersion); +static NoDbgRegParms sqInt genPrimitiveAtSigned(sqInt signedVersion); +static sqInt genPrimitiveFloatEqual(void); +static sqInt genPrimitiveFloatGreaterOrEqual(void); +static sqInt genPrimitiveFloatGreaterThan(void); +static sqInt genPrimitiveFloatLessOrEqual(void); +static sqInt genPrimitiveFloatLessThan(void); +static sqInt genPrimitiveFloatNotEqual(void); +static sqInt genPrimitiveIdentityHash(void); +static sqInt genPrimitiveImmediateAsInteger(void); +static sqInt genPrimitiveNew(void); +static sqInt genPrimitiveNewWithArg(void); +static sqInt genPrimitiveShallowCopy(void); +static sqInt genPrimitiveSlotAt(void); +static sqInt genPrimitiveSmallFloatAdd(void); +static sqInt genPrimitiveSmallFloatDivide(void); +static sqInt genPrimitiveSmallFloatEqual(void); +static sqInt genPrimitiveSmallFloatGreaterOrEqual(void); +static sqInt genPrimitiveSmallFloatGreaterThan(void); +static sqInt genPrimitiveSmallFloatLessOrEqual(void); +static sqInt genPrimitiveSmallFloatLessThan(void); +static sqInt genPrimitiveSmallFloatMultiply(void); +static sqInt genPrimitiveSmallFloatNotEqual(void); +static sqInt genPrimitiveSmallFloatSquareRoot(void); +static sqInt genPrimitiveSmallFloatSubtract(void); +static sqInt genPrimitiveStringAt(void); +static sqInt genPrimitiveStringAtPut(void); +static sqInt genPrimitiveUninitializedNewWithArg(void); +static NoDbgRegParms sqInt genPureFloatArithmeticpreOpCheckboxed(sqInt arithmeticOperator, AbstractInstruction *(*preOpCheckOrNil)(int rcvrReg, int argReg), sqInt rcvrBoxed); +static NoDbgRegParms sqInt genPureFloatComparisoninvertboxed(AbstractInstruction *(*jumpFPOpcodeGenerator)(void *), sqInt invertComparison, sqInt rcvrBoxed); +static NoDbgRegParms sqInt genRemoveSmallIntegerTagsInScratchReg(sqInt scratchReg); +static NoDbgRegParms sqInt genShiftAwaySmallIntegerTagsInScratchReg(sqInt scratchReg); +static NoDbgRegParms sqInt genSmallIntegerComparisonorDoubleComparisoninvert(sqInt jumpOpcode, AbstractInstruction * NoDbgRegParms (*jumpFPOpcodeGenerator)(void *), sqInt invertComparison); +static NoDbgRegParms sqInt getLiteralCountOfplusOneinBytesintoscratch(sqInt methodReg, sqInt plusOne, sqInt inBytes, sqInt litCountReg, sqInt scratchReg); +static NoDbgRegParms sqInt inlineCacheTagForInstance(sqInt oop); +static sqInt log2BytesPerWord(void); +static void maybeGenerateSelectorIndexDereferenceRoutine(void); +static sqInt numSmallIntegerBits(void); +static sqInt numSmallIntegerTagBits(void); +static NoDbgRegParms sqInt validInlineCacheTag(sqInt classIndexOrTagPattern); +static void callStoreCheckTrampoline(void); +static NoDbgRegParms sqInt checkValidOopReference(sqInt anOop); +static NoDbgRegParms AbstractInstruction * compileJumpToRetryOfonPrimitiveFail(void (*primitiveRoutine)(void), sqInt primIndex); +static NoDbgRegParms sqInt couldBeObject(sqInt literal); +static NoDbgRegParms usqInt genActiveContextTrampolineLargeinBlockcalled(sqInt isLarge, sqInt isInBlock, char *aString); +static NoDbgRegParms AbstractInstruction * genCheckRememberedBitOfscratch(sqInt objReg, sqInt scratchReg); +static NoDbgRegParms sqInt genConvertCharacterToCodeInReg(sqInt reg); +static NoDbgRegParms sqInt genConvertIntegerToCharacterInReg(sqInt reg); +static NoDbgRegParms sqInt genCreateFullClosurenumArgsnumCopiedignoreContextcontextNumArgslargeinBlock(sqInt compiledBlock, sqInt numArgs, sqInt numCopied, sqInt ignoreContext, sqInt contextNumArgs, sqInt contextIsLarge, sqInt contextIsBlock); +static NoDbgRegParms sqInt genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(sqInt reg, sqInt scratch, void *fwdJumpTarget, void *nonFwdJumpTargetOrZero); +static NoDbgRegParms sqInt genEnsureOopInRegNotForwardedscratchRegupdatingSlotin(sqInt reg, sqInt scratch, sqInt index, sqInt objReg); +static NoDbgRegParms sqInt genGetActiveContextLargeinBlock(sqInt isLarge, sqInt isInBlock); +static NoDbgRegParms sqInt genGetActiveContextNumArgslargeinBlock(sqInt numArgs, sqInt isLargeContext, sqInt isInBlock); +static NoDbgRegParms sqInt genGetBitsofFormatByteOfinto(sqInt mask, sqInt sourceReg, sqInt destReg); +static NoDbgRegParms sqInt genGetClassIndexOfNonImminto(sqInt sourceReg, sqInt destReg); +static NoDbgRegParms sqInt genGetClassObjectOfClassIndexintoscratchReg(sqInt instReg, sqInt destReg, sqInt scratchReg); +static NoDbgRegParms sqInt genGetClassObjectOfintoscratchRegmayBeAForwarder(sqInt instReg, sqInt destReg, sqInt scratchReg, sqInt mayBeForwarder); +static NoDbgRegParms AbstractInstruction * genGetClassTagOfintoscratchReg(sqInt instReg, sqInt destReg, sqInt scratchReg); +static NoDbgRegParms sqInt genGetCompactClassIndexNonImmOfinto(sqInt instReg, sqInt destReg); +static NoDbgRegParms sqInt genGetDoubleValueOfinto(sqInt srcReg, sqInt destFPReg); +static NoDbgRegParms sqInt genGetFormatOfinto(sqInt srcReg, sqInt destReg); +static NoDbgRegParms sqInt genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(sqInt sourceReg, sqInt destReg, sqInt scratchRegOrNone); +static NoDbgRegParms sqInt genGetNumSlotsOfinto(sqInt srcReg, sqInt destReg); +static NoDbgRegParms sqInt genGetRawSlotSizeOfNonImminto(sqInt sourceReg, sqInt destReg); +static NoDbgRegParms AbstractInstruction * genJumpImmediate(sqInt aRegister); +#if IMMUTABILITY +static NoDbgRegParms AbstractInstruction * genJumpImmutablescratchReg(sqInt sourceReg, sqInt scratchReg); +#endif /* IMMUTABILITY */ +#if IMMUTABILITY +static NoDbgRegParms AbstractInstruction * genJumpMutablescratchReg(sqInt sourceReg, sqInt scratchReg); +#endif /* IMMUTABILITY */ +static NoDbgRegParms sqInt genNewArrayOfSizeinitialized(sqInt size, sqInt initialize); +static NoDbgRegParms sqInt genNoPopCreateClosureAtnumArgsnumCopiedcontextNumArgslargeinBlock(sqInt bcpc, sqInt numArgs, sqInt numCopied, sqInt ctxtNumArgs, sqInt isLargeCtxt, sqInt isInBlock); +static sqInt genPrimitiveAsCharacter(void); +static sqInt genPrimitiveAsFloat(void); +static sqInt genPrimitiveAt(void); +static sqInt genPrimitiveAtPut(void); +static sqInt genPrimitiveFloatSquareRoot(void); +static NoDbgRegParms sqInt genPrimitiveIdenticalOrNotIf(sqInt orNot); +static sqInt genPrimitiveIntegerAt(void); +static sqInt genPrimitiveIntegerAtPut(void); +static sqInt genPrimitiveMakePoint(void); +static sqInt genPrimitiveObjectAt(void); +static sqInt genPrimitiveSize(void); +static sqInt genPrimitiveStringCompareWith(void); +static sqInt genPrimitiveStringReplace(void); +static NoDbgRegParms sqInt genSetSmallIntegerTagsIn(sqInt scratchReg); +static usqInt genStoreCheckContextReceiverTrampoline(void); +static NoDbgRegParms sqInt genStoreCheckReceiverRegvalueRegscratchReginFrame(sqInt destReg, sqInt valueReg, sqInt scratchReg, sqInt inFrame); +static NoDbgRegParms sqInt genStoreSourceRegslotIndexdestRegscratchReginFrameneedsStoreCheck(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt inFrame, sqInt needsStoreCheck); +static NoDbgRegParms sqInt genStoreSourceRegslotIndexintoNewObjectInDestReg(sqInt sourceReg, sqInt index, sqInt destReg); +#if IMMUTABILITY +static NoDbgRegParms usqInt genStoreTrampolineCalledinstVarIndex(char *trampolineName, sqInt instVarIndex); +#endif /* IMMUTABILITY */ +#if IMMUTABILITY +static NoDbgRegParms sqInt genStoreWithImmutabilityAndStoreCheckSourceRegslotIndexdestRegscratchRegneedRestoreRcvr(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt needRestoreRcvr); +#endif /* IMMUTABILITY */ +#if IMMUTABILITY +static NoDbgRegParms sqInt genStoreWithImmutabilityButNoStoreCheckSourceRegslotIndexdestRegscratchRegneedRestoreRcvr(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt needRestoreRcvr); +#endif /* IMMUTABILITY */ +#if IMMUTABILITY +static NoDbgRegParms sqInt genStoreWithImmutabilityCheckSourceRegslotIndexdestRegscratchRegneedsStoreCheckneedRestoreRcvr(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt needsStoreCheck, sqInt needRestoreRcvr); +#endif /* IMMUTABILITY */ +static sqInt getActiveContextAllocatesInMachineCode(void); +static NoDbgRegParms sqInt inlineCacheTagIsYoung(sqInt cacheTag); +static NoDbgRegParms AbstractInstruction * jumpNotCharacterUnsignedValueInRegister(sqInt reg); +static NoDbgRegParms sqInt markAndTraceLiteralinatpc(sqInt literal, CogMethod *cogMethodOrNil, usqInt address); +static NoDbgRegParms void markAndTraceLiteralinat(sqInt literal, CogMethod *cogMethod, sqInt *address); +static NoDbgRegParms void markAndTraceUpdatedLiteralin(sqInt objOop, CogMethod *cogMethodOrNil); +static NoDbgRegParms sqInt maybeCompileRetryOfonPrimitiveFailflagsjumpcontinue(void (*primitiveRoutine)(void), sqInt primIndex, sqInt flags, AbstractInstruction *jmp, AbstractInstruction *label); +static NoDbgRegParms sqInt maybeShiftClassTagRegisterForMethodCacheProbe(sqInt classTagReg); +static sqInt numCharacterBits(void); +static NoDbgRegParms sqInt remapObject(sqInt objOop); +static NoDbgRegParms sqInt remapOop(sqInt objOop); +static NoDbgRegParms sqInt shouldAnnotateObjectReference(sqInt anOop); +static NoDbgRegParms sqInt slotOffsetOfInstVarIndex(sqInt index); +static NoDbgRegParms SimStackEntry * ensureSpilledAtfrom(SimStackEntry *self_in_CogSimStackEntry, sqInt baseOffset, sqInt baseRegister); +static NoDbgRegParms sqInt isSameEntryAs(SimStackEntry *self_in_CogSimStackEntry, SimStackEntry *ssEntry); +static NoDbgRegParms sqInt mayBeAForwarder(SimStackEntry *self_in_CogSimStackEntry); +static NoDbgRegParms SimStackEntry * popToReg(SimStackEntry *self_in_CogSimStackEntry, sqInt reg); +static NoDbgRegParms sqInt registerMask(SimStackEntry *self_in_CogSimStackEntry); +static NoDbgRegParms sqInt registerMaskOrNone(SimStackEntry *self_in_CogSimStackEntry); +static NoDbgRegParms sqInt registerOrNone(SimStackEntry *self_in_CogSimStackEntry); +static NoDbgRegParms SimStackEntry * storeToReg(SimStackEntry *self_in_CogSimStackEntry, sqInt reg); +static NoDbgRegParms int isMergeFixup(BytecodeFixup *self_in_CogSSBytecodeFixup); +static NoDbgRegParms sqInt availableRegisterOrNoneFor(AbstractInstruction *self_in_CogX64Compiler, sqInt liveRegsMask); +static NoDbgRegParms sqInt callFullInstructionByteSize(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt callFullTargetFromReturnAddress(AbstractInstruction *self_in_CogX64Compiler, sqInt callSiteReturnAddress); +static NoDbgRegParms sqInt callInstructionByteSize(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms usqInt callTargetFromReturnAddress(AbstractInstruction *self_in_CogX64Compiler, sqInt callSiteReturnAddress); +static NoDbgRegParms AbstractInstruction * cFloatResultToRd(AbstractInstruction *self_in_CogX64Compiler, sqInt reg); +static NoDbgRegParms AbstractInstruction * cFloatResultToRs(AbstractInstruction *self_in_CogX64Compiler, sqInt reg); +static NoDbgRegParms sqInt cmpC32RTempByteSize(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt computeMaximumSize(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms int computeShiftRRSize(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt computeSizeOfPushCw(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt concretizeArithCqRWithROraxOpcode(AbstractInstruction *self_in_CogX64Compiler, sqInt regOpcode, sqInt raxOpcode); +static NoDbgRegParms sqInt concretizeFill32(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt concretizeMovePerfCnt64RL(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt concretizeMoveRX32rR(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt concretizeMoveX32rRR(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt concretizeOpRR(AbstractInstruction *self_in_CogX64Compiler, sqInt x64opcode); +static NoDbgRegParms sqInt concretizePrefetchAw(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt concretizeReverseOpRR(AbstractInstruction *self_in_CogX64Compiler, sqInt x64opcode); +static NoDbgRegParms sqInt concretizeXCHGRR(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms AbstractInstruction * detectFeatures(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt dispatchConcretize(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt dispatchConcretizeProcessorSpecific(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt fullCallsAreRelative(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms AbstractInstruction * genDivRRQuoRem(AbstractInstruction *self_in_CogX64Compiler, sqInt abstractRegDivisor, sqInt abstractRegDividend, sqInt abstractRegQuotient, sqInt abstractRegRemainder); +static NoDbgRegParms AbstractInstruction * genMemCopytoconstantSize(AbstractInstruction *self_in_CogX64Compiler, sqInt originalSourceReg, sqInt originalDestReg, sqInt size); +static NoDbgRegParms AbstractInstruction * genMemCopytosize(AbstractInstruction *self_in_CogX64Compiler, sqInt originalSourceReg, sqInt originalDestReg, sqInt originalSize); +static NoDbgRegParms AbstractInstruction * genMulRR(AbstractInstruction *self_in_CogX64Compiler, sqInt regSource, sqInt regDest); +static NoDbgRegParms AbstractInstruction * genPushRegisterArgsForAbortMissNumArgs(AbstractInstruction *self_in_CogX64Compiler, sqInt numArgs); +static NoDbgRegParms AbstractInstruction * genPushRegisterArgsForNumArgsscratchReg(AbstractInstruction *self_in_CogX64Compiler, sqInt numArgs, sqInt scratchReg); +static NoDbgRegParms sqInt genRemoveNArgsFromStack(AbstractInstruction *self_in_CogX64Compiler, sqInt n); +static NoDbgRegParms sqInt genRestoreRegs(AbstractInstruction *self_in_CogX64Compiler, sqInt regMask); +static NoDbgRegParms sqInt genSaveRegs(AbstractInstruction *self_in_CogX64Compiler, sqInt regMask); +static NoDbgRegParms AbstractInstruction * genSubstituteReturnAddress(AbstractInstruction *self_in_CogX64Compiler, sqInt retpc); +static NoDbgRegParms AbstractInstruction * genSwapRRScratch(AbstractInstruction *self_in_CogX64Compiler, sqInt regA, sqInt regB, sqInt regTmp); +static NoDbgRegParms int hasLZCNTInstructions(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt hasVarBaseRegister(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt instructionSizeAt(AbstractInstruction *self_in_CogX64Compiler, sqInt pc); +static NoDbgRegParms int is32BitSignedImmediate(AbstractInstruction *self_in_CogX64Compiler, sqInt a64BitUnsignedOperand); +static NoDbgRegParms int isCallPrecedingReturnPC(AbstractInstruction *self_in_CogX64Compiler, sqInt mcpc); +static NoDbgRegParms sqInt isJumpAt(AbstractInstruction *self_in_CogX64Compiler, sqInt pc); +static NoDbgRegParms int isQuick(AbstractInstruction *self_in_CogX64Compiler, usqIntptr_t operand); +static NoDbgRegParms sqInt isWithinMwOffsetRange(AbstractInstruction *self_in_CogX64Compiler, sqInt anAddress); +static NoDbgRegParms AbstractInstruction * jmpTarget(AbstractInstruction *self_in_CogX64Compiler, AbstractInstruction *anAbstractInstruction); +static NoDbgRegParms sqInt jumpLongByteSize(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt jumpLongConditionalByteSize(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms usqInt jumpLongTargetBeforeFollowingAddress(AbstractInstruction *self_in_CogX64Compiler, sqInt mcpc); +static NoDbgRegParms usqInt jumpTargetPCAt(AbstractInstruction *self_in_CogX64Compiler, sqInt pc); +static NoDbgRegParms sqInt leafCallStackPointerDelta(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt loadPICLiteralByteSize(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt machineCodeAt(AbstractInstruction *self_in_CogX64Compiler, sqInt anOffset); +static NoDbgRegParms sqInt machineCodeBytes(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms usqInt modRMRO(AbstractInstruction *self_in_CogX64Compiler, sqInt mod, sqInt regMode, sqInt regOpcode); +static NoDbgRegParms sqInt numIntRegArgs(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms AbstractInstruction * padIfPossibleWithStopsFromto(AbstractInstruction *self_in_CogX64Compiler, sqInt startAddr, sqInt endAddr); +static NoDbgRegParms sqInt performanceCounterTicksPerMicrosecond(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms AbstractInstruction * relocateCallBeforeReturnPCby(AbstractInstruction *self_in_CogX64Compiler, sqInt retpc, sqInt delta); +static NoDbgRegParms AbstractInstruction * relocateMethodReferenceBeforeAddressby(AbstractInstruction *self_in_CogX64Compiler, sqInt pc, sqInt delta); +static NoDbgRegParms sqInt rewriteCallAttarget(AbstractInstruction *self_in_CogX64Compiler, usqInt callSiteReturnAddress, usqInt callTargetAddress); +static NoDbgRegParms AbstractInstruction * rewriteCPICJumpAttarget(AbstractInstruction *self_in_CogX64Compiler, usqInt addressFollowingJump, usqInt jumpTargetAddr); +static NoDbgRegParms sqInt rewriteInlineCacheAttagtarget(AbstractInstruction *self_in_CogX64Compiler, usqInt callSiteReturnAddress, sqInt cacheTag, usqInt callTargetAddress); +static NoDbgRegParms AbstractInstruction * rewriteInlineCacheTagat(AbstractInstruction *self_in_CogX64Compiler, sqInt cacheTag, sqInt callSiteReturnAddress); +static NoDbgRegParms usqInt rexRxb(AbstractInstruction *self_in_CogX64Compiler, sqInt reg, sqInt sibReg, sqInt fieldReg); +static NoDbgRegParms usqInt rexwrxb(AbstractInstruction *self_in_CogX64Compiler, sqInt width64, sqInt reg, sqInt sibReg, sqInt fieldReg); +static NoDbgRegParms sqInt setsConditionCodesFor(AbstractInstruction *self_in_CogX64Compiler, sqInt aConditionalJumpOpcode); +static NoDbgRegParms int sizeHasModrmat(AbstractInstruction *self_in_CogX64Compiler, sqInt op, sqInt pc); +static NoDbgRegParms int sizeImmediateGroup1at(AbstractInstruction *self_in_CogX64Compiler, sqInt op, sqInt pc); +static NoDbgRegParms AbstractInstruction * stopsFromto(AbstractInstruction *self_in_CogX64Compiler, sqInt startAddr, sqInt endAddr); +static NoDbgRegParms AbstractInstruction * storeLiteral32beforeFollowingAddress(AbstractInstruction *self_in_CogX64Compiler, sqInt literal, sqInt followingAddress); +static NoDbgRegParms usqInt sib(AbstractInstruction *self_in_CogX64Compiler, sqInt scale, sqInt indexReg, sqInt baseReg); +static NoDbgRegParms int twoByteInstructionSizeAt(AbstractInstruction *self_in_CogX64Compiler, sqInt pc); +static NoDbgRegParms sqInt zoneCallsAreRelative(AbstractInstruction *self_in_CogX64Compiler); +static sqInt generateRunTimeTrampolines(void); +static NoDbgRegParms void genMoveCurrentMethodOopToRegister(sqInt reg); +static NoDbgRegParms sqInt genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(sqInt popBoolean, sqInt slotIndex, sqInt needsStoreCheck, sqInt needsImmCheck); +static NoDbgRegParms sqInt genStorePopRemoteTempAtneedsStoreCheck(sqInt popBoolean, sqInt slotIndex, sqInt remoteTempIndex, sqInt needsStoreCheck); +static NoDbgRegParms sqInt genStorePopTemporaryVariable(sqInt popBoolean, sqInt tempIndex); +static sqInt genUpArrowReturn(void); +static NoDbgRegParms void initSimStackForFramefulMethod(sqInt startpc); +static NoDbgRegParms void initSimStackForFramelessMethod(sqInt startpc); +static void generateObjectRepresentationTrampolines(void); +static sqInt genPrimitiveSlotAtPut(void); +#if VMInvestigations +extern char * abiName(void); +#endif /* VMInvestigations */ +extern sqInt cogMethodHasExternalPrim(CogMethod *aCogMethod); +extern sqInt cogMethodHasMachineCodePrim(CogMethod *aCogMethod); +#if VMInvestigations +extern sqInt cogMethodImportsintodetailCPICs(CogMethod *cogMethod, sqInt data, sqInt detailCPICs); +#endif /* VMInvestigations */ +static sqInt compileBlockDispatch(void); +static void compileGetErrorCode(void); +static sqInt compileInterpreterPrimitive(void); +static NoDbgRegParms sqInt compileInterpreterPrimitiveflags(void (*primitiveRoutine)(void), sqInt flags); +static NoDbgRegParms sqInt compileOnStackExternalPrimitiveflags(void (*primitiveRoutine)(void), sqInt flags); +static NoDbgRegParms AbstractInstruction * compileOpenPICMethodCacheProbeForwithShiftbaseRegOrNone(sqInt selector, sqInt shift, sqInt baseRegOrNone); +static NoDbgRegParms void compileOpenPICnumArgs(sqInt selector, sqInt numArgs); +static NoDbgRegParms AbstractInstruction * compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(sqInt selectorReg, sqInt shift, sqInt baseRegOrNone); +static sqInt compilePrimitive(void); +static sqInt extendedPushBytecode(void); +static sqInt extendedStoreAndPopBytecode(void); +static sqInt extendedStoreBytecode(void); +static NoDbgRegParms sqInt frameOffsetOfTemporary(sqInt index); +static sqInt genCallMappedInlinedPrimitive(void); +static NoDbgRegParms AbstractInstruction * genDoubleFailIfZeroArgRcvrarg(int rcvrReg, int argReg); +static sqInt genExtendedSendBytecode(void); +static sqInt genExtendedSuperBytecode(void); +static sqInt genExtJumpIfFalse(void); +static sqInt genExtJumpIfTrue(void); +static sqInt genExtNopBytecode(void); +static sqInt genExtPushCharacterBytecode(void); +static sqInt genExtPushIntegerBytecode(void); +static sqInt genExtPushLiteralBytecode(void); +static sqInt genExtPushLiteralVariableBytecode(void); +static sqInt genExtPushPseudoVariable(void); +static sqInt genExtPushReceiverVariableBytecode(void); +static sqInt genExtSendBytecode(void); +static sqInt genExtSendSuperBytecode(void); +static sqInt genExtStoreAndPopLiteralVariableBytecode(void); +static sqInt genExtStoreAndPopReceiverVariableBytecode(void); +static sqInt genExtStoreLiteralVariableBytecode(void); +static sqInt genExtStoreReceiverVariableBytecode(void); +static sqInt genExtUnconditionalJump(void); +static sqInt genFastPrimFail(void); +static NoDbgRegParms void genFastPrimTraceUsingandsetNewMethod(sqInt r1, sqInt r2, sqInt setNewMethod); +static void genLoadNewMethod(void); +static sqInt genLongJumpIfFalse(void); +static sqInt genLongJumpIfTrue(void); +static sqInt genLongPushTemporaryVariableBytecode(void); +static sqInt genLongStoreAndPopTemporaryVariableBytecode(void); +static sqInt genLongStoreTemporaryVariableBytecode(void); +static sqInt genLongUnconditionalBackwardJump(void); +static sqInt genLongUnconditionalForwardJump(void); +static NoDbgRegParms sqInt genLookupForPerformNumArgs(sqInt numArgs); +static NoDbgRegParms usqInt genMustBeBooleanTrampolineForcalled(sqInt boolean, char *trampolineName); +static sqInt genPrimitiveHashMultiply(void); +static NoDbgRegParms void genPrimReturnEnterCogCodeEnilopmart(sqInt profiling); +static sqInt genPushConstantFalseBytecode(void); +static sqInt genPushConstantNilBytecode(void); +static sqInt genPushConstantOneBytecode(void); +static sqInt genPushConstantTrueBytecode(void); +static sqInt genPushConstantZeroBytecode(void); +static sqInt genPushLiteralConstantBytecode(void); +static sqInt genPushLiteralVariable16CasesBytecode(void); +static sqInt genPushLiteralVariableBytecode(void); +static sqInt genPushQuickIntegerConstantBytecode(void); +static sqInt genPushReceiverVariableBytecode(void); +static sqInt genPushTemporaryVariableBytecode(void); +extern sqInt genQuickReturnConst(void); +extern sqInt genQuickReturnInstVar(void); +extern sqInt genQuickReturnSelf(void); +static sqInt genReturnFalse(void); +static sqInt genReturnNil(void); +static sqInt genReturnNilFromBlock(void); +static sqInt genReturnTrue(void); +static sqInt genSecondExtendedSendBytecode(void); +static sqInt genSendLiteralSelector0ArgsBytecode(void); +static sqInt genSendLiteralSelector1ArgBytecode(void); +static sqInt genSendLiteralSelector2ArgsBytecode(void); +static sqInt genShortJumpIfFalse(void); +static sqInt genShortJumpIfTrue(void); +static sqInt genShortUnconditionalJump(void); +static sqInt genSpecialSelectorEqualsEquals(void); +static sqInt genSpecialSelectorNotEqualsEquals(void); +static sqInt genSpecialSelectorSend(void); +static sqInt genStoreAndPopReceiverVariableBytecode(void); +static sqInt genStoreAndPopRemoteTempLongBytecode(void); +static sqInt genStoreAndPopTemporaryVariableBytecode(void); +static sqInt genStoreRemoteTempLongBytecode(void); +static NoDbgRegParms void genTakeProfileSample(AbstractInstruction *continueLabel); +static sqInt genUnconditionalTrapBytecode(void); +#if VMInvestigations +extern char * isaName(void); +#endif /* VMInvestigations */ +extern sqInt mapPCDataForinto(CogMethod *cogMethod, sqInt arrayObj); +static sqInt numSpecialSelectors(void); +static NoDbgRegParms usqInt pcDataForBlockEntryMethod(sqInt blockEntryMcpc, sqInt cogMethod); +static NoDbgRegParms sqInt pcDataForAnnotationMcpcBcpcMethod(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *cogMethodArg); +static PrimitiveDescriptor * primitiveGeneratorOrNil(void); +static NoDbgRegParms int registerisInMask(sqInt reg, sqInt mask); +static NoDbgRegParms int registerisNotInMask(sqInt reg, sqInt mask); +static NoDbgRegParms BlockStart * addBlockStartAtnumArgsnumCopiedspan(sqInt bytecodepc, sqInt numArgs, sqInt numCopied, sqInt span); +static NoDbgRegParms void adjustArgumentsForPerform(sqInt numArgs); +static NoDbgRegParms sqInt allocateRegForStackEntryAtnotConflictingWith(sqInt index, sqInt regMask); +static NoDbgRegParms sqInt allocateRegNotConflictingWith(sqInt regMask); +static NoDbgRegParms sqInt anyReferencesToRegisterinTopNItems(sqInt reg, sqInt n); +extern void callCogCodePopReceiverArg0Regs(void); +extern void callCogCodePopReceiverArg1Arg0Regs(void); +static sqInt compileBlockBodies(void); +static NoDbgRegParms void compileBlockFrameBuild(BlockStart *blockStart); +static NoDbgRegParms void compileBlockFramelessEntry(BlockStart *blockStart); +static NoDbgRegParms sqInt compileBytecodesFromthrough(sqInt start, sqInt end); +static NoDbgRegParms CogMethod * compileCogFullBlockMethod(sqInt numCopied); +static NoDbgRegParms CogMethod * compileCogMethod(sqInt selector); +static sqInt compileEntireMethod(void); +static void compileFrameBuild(void); +static NoDbgRegParms void compileFullBlockFramelessEntry(sqInt numCopied); +static NoDbgRegParms void compileFullBlockMethodFrameBuild(sqInt numCopied); +#if IMMUTABILITY +static void compileTwoPathFrameBuild(void); +#endif /* IMMUTABILITY */ +static void compileTwoPathFramelessInit(void); +static NoDbgRegParms sqInt cPICMissTrampolineFor(sqInt numArgs); +static sqInt doubleExtendedDoAnythingBytecode(void); +static sqInt duplicateTopBytecode(void); +static NoDbgRegParms BytecodeFixup * ensureFixupAt(sqInt targetPC); +static NoDbgRegParms BytecodeFixup * ensureNonMergeFixupAt(sqInt targetPC); +static void ensureReceiverResultRegContainsSelf(void); +static NoDbgRegParms void evaluateat(BytecodeDescriptor *descriptor, sqInt pc); +static NoDbgRegParms sqInt eventualTargetOf(sqInt targetBytecodePC); +static NoDbgRegParms sqInt freeAnyRegNotConflictingWith(sqInt regMask); +static sqInt genBlockReturn(void); +static NoDbgRegParms void (*genCallPICEnilopmartNumArgs(sqInt numArgs))(void); +static sqInt genCallPrimitiveBytecode(void); +static sqInt genExternalizePointersForPrimitiveCall(void); +static AbstractInstruction * genExternalizeStackPointerForFastPrimitiveCall(void); +static sqInt genExtPushClosureBytecode(void); +static sqInt genExtPushFullClosureBytecode(void); +static void generateEnilopmarts(void); +static NoDbgRegParms sqInt generateInstructionsAt(sqInt eventualAbsoluteAddress); +static void generateMissAbortTrampolines(void); +static void generateSendTrampolines(void); +static void generateTracingTrampolines(void); +static NoDbgRegParms sqInt genForwardersInlinedIdenticalOrNotIf(sqInt orNot); +static NoDbgRegParms sqInt genIdenticalNoBranchArgIsConstantrcvrIsConstantargRegrcvrRegorNotIf(sqInt argIsConstant, sqInt rcvrIsConstant, sqInt argReg, sqInt rcvrRegOrNone, sqInt orNot); +static NoDbgRegParms sqInt genInlinedIdenticalOrNotIf(sqInt orNot); +static NoDbgRegParms sqInt genJumpBackTo(sqInt targetBytecodePC); +static NoDbgRegParms sqInt genJumpIfto(sqInt boolean, sqInt targetBytecodePC); +static NoDbgRegParms sqInt genJumpTo(sqInt targetBytecodePC); +static NoDbgRegParms sqInt genMarshalledSendnumArgssendTable(sqInt selectorIndex, sqInt numArgs, sqInt *sendTable); +static NoDbgRegParms usqInt genMethodAbortTrampolineFor(sqInt numArgs); +static NoDbgRegParms usqInt genPICAbortTrampolineFor(sqInt numArgs); +static NoDbgRegParms usqInt genPICMissTrampolineFor(sqInt numArgs); +static sqInt genPopStackBytecode(void); +static sqInt genPrimitiveClosureValue(void); +static sqInt genPrimitiveFullClosureValue(void); +static sqInt genPrimitivePerform(void); +static sqInt genPrimitivePerformWithArguments(void); +static sqInt genPushActiveContextBytecode(void); +static sqInt genPushClosureCopyCopiedValuesBytecode(void); +static NoDbgRegParms sqInt genPushLiteralIndex(sqInt literalIndex); +static NoDbgRegParms sqInt genPushLiteralVariable(sqInt literalIndex); +static NoDbgRegParms sqInt genPushMaybeContextReceiverVariable(sqInt slotIndex); +static sqInt genPushNewArrayBytecode(void); +static sqInt genPushReceiverBytecode(void); +static NoDbgRegParms sqInt genPushReceiverVariable(sqInt index); +static void genPushRegisterArgs(void); +static sqInt genPushRemoteTempLongBytecode(void); +static NoDbgRegParms sqInt genPushTemporaryVariable(sqInt index); +static sqInt genReturnReceiver(void); +static sqInt genReturnTopFromBlock(void); +static sqInt genReturnTopFromMethod(void); +static NoDbgRegParms sqInt genSendDirectedSupernumArgs(sqInt selectorIndex, sqInt numArgs); +static NoDbgRegParms sqInt genSendSupernumArgs(sqInt selectorIndex, sqInt numArgs); +static NoDbgRegParms usqInt genSendTrampolineFornumArgscalledargargargarg(void *aRoutine, sqInt numArgs, char *aString, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3); +static NoDbgRegParms sqInt genSendnumArgs(sqInt selectorIndex, sqInt numArgs); +static sqInt genSpecialSelectorArithmetic(void); +static sqInt genSpecialSelectorClass(void); +static sqInt genSpecialSelectorComparison(void); +static sqInt genStaticallyResolvedSpecialSelectorComparison(void); +static NoDbgRegParms sqInt genStorePopLiteralVariableneedsStoreCheckneedsImmutabilityCheck(sqInt popBoolean, sqInt litVarIndex, sqInt needsStoreCheck, sqInt needsImmCheck); +static NoDbgRegParms sqInt genStorePopMaybeContextReceiverVariableneedsStoreCheckneedsImmutabilityCheck(sqInt popBoolean, sqInt slotIndex, sqInt needsStoreCheck, sqInt needsImmCheck); +static NoDbgRegParms sqInt genVanillaInlinedIdenticalOrNotIf(sqInt orNot); +static NoDbgRegParms void initSimStackForFramelessBlock(sqInt startpc); +static NoDbgRegParms sqInt isNonForwarderReceiver(sqInt reg); +static sqInt liveRegisters(void); +static NoDbgRegParms sqInt mapDeadDescriptorIfNeeded(BytecodeDescriptor *descriptor); +static NoDbgRegParms void marshallSendArguments(sqInt numArgs); +static sqInt maybeCompilingFirstPassOfBlockWithInitialPushNil(void); +static NoDbgRegParms sqInt mergeWithFixupIfRequired(BytecodeFixup *fixup); +static NoDbgRegParms sqInt methodAbortTrampolineFor(sqInt numArgs); +static NoDbgRegParms sqInt needsFrameIfMod16GENumArgs(sqInt stackDelta); +static NoDbgRegParms sqInt needsFrameIfStackGreaterThanOne(sqInt stackDelta); +static NoDbgRegParms sqInt numberOfSpillsInTopNItems(sqInt n); +static NoDbgRegParms sqInt picAbortTrampolineFor(sqInt numArgs); +static void preserveTopInClassRegIfIsReceiverResultReg(void); +static sqInt prevInstIsPCAnnotated(void); +static int receiverIsInReceiverResultReg(void); +static NoDbgRegParms void reinitializeFixupsFromthrough(sqInt start, sqInt end); +static NoDbgRegParms sqInt scanBlock(BlockStart *blockStart); +static sqInt scanMethod(void); +static NoDbgRegParms sqInt squeakV3orSistaV1PushNilSizenumInitialNils(sqInt aMethodObj, sqInt numInitialNils); +static NoDbgRegParms sqInt squeakV3orSistaV1NumPushNils(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +static NoDbgRegParms void ssAllocateRequiredRegMaskupThroughupThroughNative(sqInt requiredRegsMask, sqInt stackPtr, sqInt nativeStackPtr); +static NoDbgRegParms void ssFlushUpThroughReceiverVariable(sqInt slotIndex); +static NoDbgRegParms void ssFlushUpThroughTemporaryVariable(sqInt tempIndex); +static NoDbgRegParms void ssPop(sqInt n); +static NoDbgRegParms sqInt ssPushAnnotatedConstant(sqInt literal); +static NoDbgRegParms sqInt ssPushBaseoffset(sqInt reg, sqInt offset); +static NoDbgRegParms sqInt ssPushConstant(sqInt literal); +static NoDbgRegParms sqInt ssPushDesc(SimStackEntry simStackEntry); +static NoDbgRegParms sqInt ssPushRegister(sqInt reg); +static NoDbgRegParms void ssPush(sqInt n); +static SimStackEntry ssSelfDescriptor(void); +static NoDbgRegParms void ssStoreAndReplacePoptoReg(sqInt popBoolean, sqInt reg); +static NoDbgRegParms sqInt ssStorePoptoPreferredReg(sqInt popBoolean, sqInt preferredReg); +static NoDbgRegParms void ssStorePoptoReg(sqInt popBoolean, sqInt reg); +static SimStackEntry * ssTop(void); +static NoDbgRegParms SimStackEntry * ssValue(sqInt n); +static NoDbgRegParms sqInt stackEntryIsBoolean(SimStackEntry *simStackEntry); +static sqInt tempsValidAndVolatileEntriesSpilled(void); +static NoDbgRegParms sqInt tryCollapseTempVectorInitializationOfSize(sqInt slots); +static sqInt violatesEnsureSpilledSpillAssert(void); +static void voidReceiverResultRegContainsSelf(void); + + +/*** Variables ***/ +static AbstractInstruction aMethodLabel; +static AbstractInstruction * abstractOpcodes; +static AbstractInstruction * const backEnd = &aMethodLabel; +static usqInt baseAddress; +static sqInt blockCount; +static AbstractInstruction * blockEntryLabel; +static AbstractInstruction * blockEntryNoContextSwitch; +static BlockStart * blockStarts; +static sqInt breakMethod; +static sqInt byte0; +static sqInt byte1; +static sqInt byte2; +static sqInt byte3; +static sqInt bytecodePC; +static sqInt bytecodeSetOffset; +static sqInt ceCollectArgumentsTypeFrameful; +static sqInt ceCollectArgumentsTypeFramelessSpilled; +static sqInt ceCollectArgumentsTypeFramelessUnspilled1Args; +static sqInt ceCollectArgumentsTypeFramelessUnspilled2Args; +static sqInt ceCollectInstVarType; +static sqInt ceCollectRemoteTempVarType; +static sqInt ceCollectReturnType; +static sqInt ceCollectTempVarType; +static sqInt ceCollectTypesForPrimitiveSlotAtPut; +static sqInt ceCPICMissTrampoline; +static sqInt ceDereferenceSelectorIndex; +static sqInt ceFetchContextInstVarTrampoline; +#if DUAL_MAPPED_CODE_ZONE +static void (*ceFlushDCache)(usqIntptr_t from, usqIntptr_t to); +#endif +static sqInt ceFlushICache; +static sqInt ceInlineNewHashTrampoline; +static sqInt ceLargeActiveContextInBlockTrampoline; +static sqInt ceLargeActiveContextInFullBlockTrampoline; +static sqInt ceLargeActiveContextInMethodTrampoline; +static sqInt ceMethodAbortTrampoline; +static sqInt ceNonLocalReturnTrampoline; +static sqInt cePICAbortTrampoline; +static sqInt cePrimReturnEnterCogCode; +static sqInt cePrimReturnEnterCogCodeProfiling; +static sqInt ceReapAndResetErrorCodeTrampoline; +static sqInt ceScheduleScavengeTrampoline; +static sqInt ceSendMustBeBooleanAddFalseTrampoline; +static sqInt ceSendMustBeBooleanAddTrueTrampoline; +static sqInt ceSmallActiveContextInBlockTrampoline; +static sqInt ceSmallActiveContextInFullBlockTrampoline; +static sqInt ceSmallActiveContextInMethodTrampoline; +static sqInt ceStoreCheckContextReceiverTrampoline; +static sqInt ceStoreCheckTrampoline; +static sqInt ceStoreContextInstVarTrampoline; +static sqInt ceTraceBlockActivationTrampoline; +static sqInt ceTraceLinkedSendTrampoline; +static sqInt ceTraceStoreTrampoline; +static sqInt checkedEntryAlignment; +static sqInt closedPICSize; +static usqInt codeBase; +static sqInt codeModified; +#if DUAL_MAPPED_CODE_ZONE +static sqInt codeToDataDelta; +#else +# define codeToDataDelta 0 +#endif +static sqInt cogConstituentIndex; +static sqInt compactionInProgress; +static sqInt compilationPass; +static sqInt compilationTrace; +static sqInt cPICCaseSize; +static sqInt cPICEndOfCodeOffset; +static sqInt cPICEndSize; +static CogMethod * cPICPrototype; +static usqIntptr_t cpuidWord1; +static sqInt currentCallCleanUpSize; +static sqInt deadCode; +static sqInt directedSendUsesBinding; +static sqInt directedSuperBindingSendTrampolines[NumSendTrampolines]; +static sqInt directedSuperSendTrampolines[NumSendTrampolines]; +static AbstractInstruction * endCPICCase0; +static sqInt endPC; +static AbstractInstruction * entry; +static sqInt entryPointMask; +static CogMethod * enumeratingCogMethod; +static sqInt expectedFPAlignment; +static sqInt expectedSPAlignment; +static sqInt extA; +static sqInt extB; +static sqInt firstCPICCaseOffset; +static sqInt firstSend; +static BytecodeFixup * fixups; +static AbstractInstruction * fullBlockEntry; +static AbstractInstruction * fullBlockNoContextSwitchEntry; +static BytecodeDescriptor generatorTable[512] = { + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushReceiverBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantTrueBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantFalseBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantNilBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushQuickIntegerConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushQuickIntegerConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushQuickIntegerConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushQuickIntegerConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genReturnReceiver, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnTrue, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnFalse, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnNil, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnTopFromMethod, 0, needsFrameIfInBlock, -1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnTopFromBlock, 0, needsFrameNever, -1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { extendedPushBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { extendedStoreBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 0, 0 }, + { extendedStoreAndPopBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 0, 0 }, + { genExtendedSendBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { doubleExtendedDoAnythingBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genExtendedSuperBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0 }, + { genSecondExtendedSendBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genPopStackBytecode, 0, needsFrameNever, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { duplicateTopBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushActiveContextBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushNewArrayBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genCallPrimitiveBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushRemoteTempLongBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreRemoteTempLongBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopRemoteTempLongBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushClosureCopyCopiedValuesBytecode, v3BlockCodeSize, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongUnconditionalBackwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongUnconditionalBackwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongUnconditionalBackwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongUnconditionalBackwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongUnconditionalForwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genLongUnconditionalForwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genLongUnconditionalForwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genLongUnconditionalForwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genLongJumpIfTrue, v3LongForwardBranchDistance, 0, 0, 0, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongJumpIfTrue, v3LongForwardBranchDistance, 0, 0, 0, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongJumpIfTrue, v3LongForwardBranchDistance, 0, 0, 0, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongJumpIfTrue, v3LongForwardBranchDistance, 0, 0, 0, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongJumpIfFalse, v3LongForwardBranchDistance, 0, 0, 0, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongJumpIfFalse, v3LongForwardBranchDistance, 0, 0, 0, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongJumpIfFalse, v3LongForwardBranchDistance, 0, 0, 0, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongJumpIfFalse, v3LongForwardBranchDistance, 0, 0, 0, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, AddRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, SubRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpLess, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpGreater, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpLessOrEqual, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpGreaterOrEqual, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpZero, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpNonZero, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, AndRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, OrRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorEqualsEquals, 0, needsFrameNever, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genSpecialSelectorClass, 0, needsFrameIfStackGreaterThanOne, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genSpecialSelectorNotEqualsEquals, 0, needsFrameNever, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushReceiverBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantTrueBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantFalseBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantNilBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantZeroBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantOneBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genExtPushPseudoVariable, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { duplicateTopBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { genReturnReceiver, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnTrue, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnFalse, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnNil, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnTopFromMethod, 0, needsFrameIfInBlock, -1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnNilFromBlock, 0, needsFrameNever, -1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 }, + { genReturnTopFromBlock, 0, needsFrameNever, -1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 }, + { genExtNopBytecode, 0, needsFrameNever, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, AddRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, SubRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpLess, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpGreater, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpLessOrEqual, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpGreaterOrEqual, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpZero, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpNonZero, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, AndRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, OrRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorEqualsEquals, 0, needsFrameNever, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genSpecialSelectorClass, 0, needsFrameIfStackGreaterThanOne, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genSpecialSelectorNotEqualsEquals, 0, needsFrameNever, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPopStackBytecode, 0, needsFrameNever, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genUnconditionalTrapBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { extABytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { extBBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { genExtPushReceiverVariableBytecode, 0, needsFrameIfReadMediated, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genExtPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genExtPushLiteralBytecode, 0, needsFrameNever, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genLongPushTemporaryVariableBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { genPushNewArrayBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genExtPushIntegerBytecode, 0, needsFrameNever, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genExtPushCharacterBytecode, 0, needsFrameNever, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genExtSendBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genExtSendSuperBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genCallMappedInlinedPrimitive, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }, + { genExtUnconditionalJump, v4LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genExtJumpIfTrue, v4LongBranchDistance, 0, 0, 0, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genExtJumpIfFalse, v4LongBranchDistance, 0, 0, 0, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genExtStoreAndPopReceiverVariableBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 0, 0 }, + { genExtStoreAndPopLiteralVariableBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 0, 0, 0 }, + { genLongStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genExtStoreReceiverVariableBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 0, 0 }, + { genExtStoreLiteralVariableBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 0, 0, 0 }, + { genLongStoreTemporaryVariableBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { genCallPrimitiveBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, + { genExtPushFullClosureBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genExtPushClosureBytecode, v4BlockCodeSize, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + { genPushRemoteTempLongBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreRemoteTempLongBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopRemoteTempLongBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 } +}; +static sqInt guardPageSize; +static sqInt hasMovableLiteral; +static sqInt hasYoungReferent; +static sqInt inBlock; +static sqInt initialPC; +static sqInt introspectionData; +static sqInt introspectionDataIndex; +static sqInt lastSend; +static usqInt limitAddress; +static sqInt maxLitIndex; +static sqInt methodAbortTrampolines[4]; +static sqInt methodBytesFreedSinceLastCompaction; +static sqInt methodCount; +static sqInt methodHeader; +static AbstractInstruction * const methodLabel = &aMethodLabel; +static sqInt methodObj; +static sqInt methodOrBlockNumArgs; +static sqInt methodOrBlockNumTemps; +static usqIntptr_t minValidCallAddress; +static usqInt mzFreeStart; +static sqInt needsFrame; +static sqInt nInstructionsLimit; +static AbstractInstruction * noCheckEntry; +static sqInt numAbstractOpcodes; +static sqInt numExtB; +static usqInt objectReferencesInRuntime[NumObjRefsInRuntime+1]; +static sqInt opcodeIndex; +static CogMethod *openPICList = 0; +static sqInt openPICSize; +static sqInt ordinarySendTrampolines[NumSendTrampolines]; +static sqInt picAbortTrampolines[4]; +static AbstractInstruction * picInterpretAbort; +static sqInt picMissTrampolines[4]; +static AbstractInstruction * picMNUAbort; +static BytecodeDescriptor * prevBCDescriptor; +static PrimitiveDescriptor primitiveGeneratorTable[MaxCompiledPrimitiveIndex+1] = { + { 0, -1 }, + { genPrimitiveAdd, 1 }, + { genPrimitiveSubtract, 1 }, + { genPrimitiveLessThan, 1 }, + { genPrimitiveGreaterThan, 1 }, + { genPrimitiveLessOrEqual, 1 }, + { genPrimitiveGreaterOrEqual, 1 }, + { genPrimitiveEqual, 1 }, + { genPrimitiveNotEqual, 1 }, + { genPrimitiveMultiply, 1 }, + { genPrimitiveDivide, 1 }, + { genPrimitiveMod, 1 }, + { genPrimitiveDiv, 1 }, + { genPrimitiveQuo, 1 }, + { genPrimitiveBitAnd, 1 }, + { genPrimitiveBitOr, 1 }, + { genPrimitiveBitXor, 1 }, + { genPrimitiveBitShift, 1 }, + { genPrimitiveMakePoint, 1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveAsFloat, 0 }, + { genPrimitiveFloatAdd, 1 }, + { genPrimitiveFloatSubtract, 1 }, + { genPrimitiveFloatLessThan, 1 }, + { genPrimitiveFloatGreaterThan, 1 }, + { genPrimitiveFloatLessOrEqual, 1 }, + { genPrimitiveFloatGreaterOrEqual, 1 }, + { genPrimitiveFloatEqual, 1 }, + { genPrimitiveFloatNotEqual, 1 }, + { genPrimitiveFloatMultiply, 1 }, + { genPrimitiveFloatDivide, 1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveFloatSquareRoot, 0 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveAt, 1 }, + { genPrimitiveAtPut, 2 }, + { genPrimitiveSize, 0 }, + { genPrimitiveStringAt, 1 }, + { genPrimitiveStringAtPut, 2 }, + { genFastPrimFail, -1 }, + { genFastPrimFail, -1 }, + { genFastPrimFail, -1 }, + { genPrimitiveObjectAt, 1 }, + { 0, -1 }, + { genPrimitiveNew, 0 }, + { genPrimitiveNewWithArg, 1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveIdentityHash, 0 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveNewMethod, 2 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitivePerform, -1 }, + { genPrimitivePerformWithArguments, 2 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveStringReplace, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveIdentical, 1 }, + { genPrimitiveClass, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveShallowCopy, 0 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveStringCompareWith, 1 }, + { genPrimitiveHashMultiply, 0 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveIntegerAt, 1 }, + { genPrimitiveIntegerAtPut, 2 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveNotIdentical, 1 }, + { genPrimitiveAsCharacter, -1 }, + { genPrimitiveImmediateAsInteger, 0 }, + { 0, -1 }, + { genPrimitiveSlotAt, 1 }, + { genPrimitiveSlotAtPut, 2 }, + { genPrimitiveIdentityHash, 0 }, + { genPrimitiveMaxIdentityHash, 0 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genFastPrimFail, -1 }, + { genFastPrimFail, -1 }, + { 0, -1 }, + { genPrimitiveClosureValue, 0 }, + { genPrimitiveClosureValue, 1 }, + { genPrimitiveClosureValue, 2 }, + { genPrimitiveClosureValue, 3 }, + { genPrimitiveClosureValue, 4 }, + { 0, -1 }, + { genPrimitiveFullClosureValue, -1 }, + { 0, -1 }, + { genPrimitiveFullClosureValue, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveClosureValue, 0 }, + { genPrimitiveClosureValue, 1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveSmallFloatAdd, 1 }, + { genPrimitiveSmallFloatSubtract, 1 }, + { genPrimitiveSmallFloatLessThan, 1 }, + { genPrimitiveSmallFloatGreaterThan, 1 }, + { genPrimitiveSmallFloatLessOrEqual, 1 }, + { genPrimitiveSmallFloatGreaterOrEqual, 1 }, + { genPrimitiveSmallFloatEqual, 1 }, + { genPrimitiveSmallFloatNotEqual, 1 }, + { genPrimitiveSmallFloatMultiply, 1 }, + { genPrimitiveSmallFloatDivide, 1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveSmallFloatSquareRoot, 0 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveHighBit, 0 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveUninitializedNewWithArg, 1 } +}; +static sqInt primitiveIndex; +static sqInt receiverTags; +static sqInt regArgsHaveBeenPushed; +static sqInt runtimeObjectRefIndex; +static AbstractInstruction * sendMiss; +static sqInt simNativeStackPtr; +static sqInt simSpillBase; +static SimStackEntry simStack[70]; +static sqInt simStackPtr; +static AbstractInstruction * stackCheckLabel; +static AbstractInstruction * stackOverflowCall; +static sqInt superSendTrampolines[NumSendTrampolines]; +static sqInt tempOop; +static char *trampolineAddresses[NumTrampolines*2]; +static sqInt trampolineTableIndex; +static sqInt uncheckedEntryAlignment; +static usqInt unpairedMethodList; +static sqInt useTwoPaths; +static sqInt varBaseAddress; +static usqInt youngReferrers; +static const int cStackAlignment = STACK_ALIGN_BYTES & STACK_ALIGN_MASK; +static int labelCounter; +#if IMMUTABILITY +static sqInt ceStoreTrampolines[5];; +#endif +sqInt blockNoContextSwitchOffset; +sqInt cbEntryOffset; +sqInt cbNoSwitchEntryOffset; +sqInt ceBaseFrameReturnTrampoline; +void (*ceCall0ArgsPIC)(void); +void (*ceCall1ArgsPIC)(void); +void (*ceCall2ArgsPIC)(void); +void (*ceCallCogCodePopReceiverAndClassRegs)(void); +void (*ceCallCogCodePopReceiverArg0Regs)(void); +void (*ceCallCogCodePopReceiverArg1Arg0Regs)(void); +void (*ceCallCogCodePopReceiverReg)(void); +sqInt ceCannotResumeTrampoline; +void (*ceCaptureCStackPointers)(void); +sqInt ceCheckForInterruptTrampoline; +void (*ceEnterCogCodePopReceiverReg)(void); +usqIntptr_t (*ceGetFP)(void); +usqIntptr_t (*ceGetSP)(void); +void (*ceInvokeInterpret)(void); +sqInt ceReturnToInterpreterTrampoline; +#if !defined(cFramePointerInUse) +sqInt cFramePointerInUse; +#endif +sqInt cmEntryOffset; +sqInt cmNoCheckEntryOffset; +usqInt methodZoneBase; +sqInt missOffset; +void (*realCECallCogCodePopReceiverAndClassRegs)(void); +void (*realCECallCogCodePopReceiverArg0Regs)(void); +void (*realCECallCogCodePopReceiverArg1Arg0Regs)(void); +void (*realCECallCogCodePopReceiverReg)(void); +void (*realCEEnterCogCodePopReceiverReg)(void); +const char * traceFlagsMeanings[] = { + "1: print trace", "2: trace sends", "4: trace block activations", "8: trace interpreter primitives", + "16: trace VM events (context switches, GCs, etc)", "32: trace stack overflow (poll for events hook)", + "64: trace linked sends", "128: trace fast C call interpreter primitives", "256: trace FFI/thread events", + null + }; +sqInt traceStores; +int traceFlags = 8 /* prim trace log on by default */; + + +/*** Macros ***/ +#define roundUpToMethodAlignment(ignored,numBytes) ((numBytes) + 7 & -8) +#define cPICNumCases stackCheckOffset +#define cPICNumCasesHack hack hack hack i.e. the getter macro does all the work +#define abstractInstructionAt(index) (&abstractOpcodes[index]) +#define addressIsInInstructions(address) (!((usqInt)(address) & (BytesPerWord-1)) \ + && (address) >= &abstractOpcodes[0] \ + && (address) < &abstractOpcodes[opcodeIndex]) +#define allocateBlockStarts(numBlocks) do { \ + blockStarts = (numBlocks) ? alloca(sizeof(BlockStart) * (numBlocks)) : 0; \ +} while (0) +#define assertValidDualZone() true +#define assertValidDualZoneReadAddress(address) 0 +#define assertValidDualZoneWriteAddress(address) 0 +#define backEnd() backEnd +#define blockAlignment() 8 +#define blockStartAt(index) (&blockStarts[index]) +#define bump64BitPerfCounterByUsecs(usecs) 0 // simulation only +#define ceBaseFrameReturnPC() ceBaseFrameReturnTrampoline +#define ceCannotResumePC() ((usqInt)ceCannotResumeTrampoline) +#define ceCheckForInterruptTrampoline() ceCheckForInterruptTrampoline +#define ceReturnToInterpreterPC() ((usqInt)ceReturnToInterpreterTrampoline) +#define codeByteAtput(address,value) byteAtput((char *)(address) + codeToDataDelta, value) +#define codeLong32Atput(address,value) long32Atput((char *)(address) + codeToDataDelta, value) +#define codeLong64Atput(address,value) long64Atput((char *)(address) + codeToDataDelta, value) +#define codeLongAtput(address,value) longAtput((char *)(address) + codeToDataDelta, value) +#define codeMemcpy(dest,src,bytes) memcpy(dest,src,bytes) +#define codeMemmove(dest,src,bytes) memmove((char *)(dest)+codeToDataDelta,src,bytes) +#define cr() putchar('\n') +#if !defined(ensureExecutableCodeZoneAt) +#define ensureExecutableCodeZoneAt(underscoreLine) 0 +#endif /* !defined(ensureExecutableCodeZoneAt) */ +#if !defined(ensureWritableCodeZoneAt) +#define ensureWritableCodeZoneAt(underscoreLine) 0 +#endif /* !defined(ensureWritableCodeZoneAt) */ +#define entryOffset() cmEntryOffset +#define generatorAt(index) (&generatorTable[index]) +#define getCodeToDataDelta() codeToDataDelta +#define getIsObjectReference() 2 +#define halt() warning("halt") +#define haltmsg(msg) warning("halt: " msg) +#define interpretOffset() missOffset +#define mapPerMethodProfile() 0 +#define maxCogCodeSize() (16*1024*1024) +#define maybeBreakGeneratingFromto(address,end) 0 +#define maybeBreakGeneratingInstructionWithIndex(i) 0 +#define maybeHaltIfDebugPC() 0 +#define methodLabel() methodLabel +#define methodZoneBase() methodZoneBase +#define minCogMethodAddress() methodZoneBase +#define moveProfileToMethods() 0 +#define noCheckEntryOffset() cmNoCheckEntryOffset +#define noContextSwitchBlockEntryOffset() blockNoContextSwitchOffset +#define notYetImplemented() warning("not yet implemented") +#define null 0 +#define printNum(n) printf("%" PRIdSQINT, (sqInt) (n)) +#define printOnTrace() (traceFlags & 1) +#define recordBlockTrace() (traceFlags & 4) +#define recordEventTrace() (traceFlags & 16) +#define recordFastCCallPrimTrace() (traceFlags & 128) +#define recordFFIOrThreadTrace() (traceFlags & 256) +#define recordOverflowTrace() (traceFlags & 32) +#define recordPrimTrace() (traceFlags & 8) +#define recordSendTrace() (traceFlags & 2) +#define reportError(n) warning("compilation error") +#define setHasMovableLiteral(b) (hasMovableLiteral = (b)) +#define setHasYoungReferent(b) (hasYoungReferent = (b)) +#define varBaseAddress() varBaseAddress +#define nextOpenPIC methodObject +#define nextOpenPICHack hack hack hack i.e. the getter macro does all the work +#define freeStart() mzFreeStart +#define limitZony() ((CogMethod *)mzFreeStart) +#define methodBytesFreedSinceLastCompaction() methodBytesFreedSinceLastCompaction +#define youngReferrers() youngReferrers +#define numRegArgs() 2 +#define maybeConstant(sse) ((sse)->constant) +#define cpuidWord1(ign) cpuidWord1 +#define flushDCacheFromto(me,startAddress,endAddress) 0 +#define flushICacheFromto(me,startAddress,endAddress) 0 +#define inlineCacheValueForSelectorin(backEnd,selector,aCogMethod) indexForSelectorin(selector,aCogMethod) +#define setCpuidWord1(ign,word) cpuidWord1 = word +#define unalignedLong32At(byteAddress) long32At(byteAddress) +#define unalignedLong32Atput(byteAddress,aWord) long32Atput(byteAddress,aWord) +#define unalignedLongAt(byteAddress) longAt(byteAddress) +#define unalignedLongAtput(byteAddress,aWord) longAtput(byteAddress,aWord) +#define fullBlockEntryOffset() cbEntryOffset +#define fullBlockNoContextSwitchEntryOffset() cbNoSwitchEntryOffset +#define needsFrame() needsFrame +#define fixupAtIndex(index) (&fixups[index]) +#define simNativeStackAt(index) (simNativeStack + (index)) +#define simSelf() simStack +#define simStackAt(index) (simStack + (index)) +#define traceDescriptor(ign) 0 +#define traceFixupmerge(igu,ana) 0 +#define traceMerge(ign) 0 +#define traceSimStack() 0 +#define traceSpill(ign) 0 +#define allocatype(numElements, elementType) alloca((numElements)*sizeof(elementType)) +#define numElementsIn(anArray) (sizeof(anArray)/sizeof(anArray[0])) +#define oopisGreaterThanOrEqualTo(anOop,otherOop) ((usqInt)(anOop) >= (usqInt)(otherOop)) +#define oopisGreaterThanOrEqualToandLessThanOrEqualTo(anOop,baseOop,limitOop) ((usqInt)(anOop) >= (usqInt)(baseOop) && (usqInt)(anOop) <= (usqInt)(limitOop)) +#define oopisGreaterThanOrEqualToandLessThan(anOop,baseOop,limitOop) ((usqInt)(anOop) >= (usqInt)(baseOop) && (usqInt)(anOop) < (usqInt)(limitOop)) +#define oopisGreaterThan(anOop,otherOop) ((usqInt)(anOop) > (usqInt)(otherOop)) +#define oopisGreaterThanandLessThan(anOop,baseOop,limitOop) ((usqInt)(anOop) > (usqInt)(baseOop) && (usqInt)(anOop) < (usqInt)(limitOop)) +#define oopisLessThanOrEqualTo(anOop,otherOop) ((usqInt)(anOop) <= (usqInt)(otherOop)) +#define oopisLessThan(anOop,otherOop) ((usqInt)(anOop) < (usqInt)(otherOop)) + + +/*** Methods ***/ + + /* CogAbstractInstruction>>#addDependent: */ +static NoDbgRegParms AbstractInstruction * +addDependent(AbstractInstruction *self_in_CogAbstractInstruction, AbstractInstruction *anInstruction) +{ + if ((self_in_CogAbstractInstruction->dependent)) { + (anInstruction->dependent = (self_in_CogAbstractInstruction->dependent)); + } + return ((self_in_CogAbstractInstruction->dependent) = anInstruction); +} + + +/* Answer an unused abstract register in the liveRegMask. + Subclasses with more registers can override to answer them. */ + + /* CogAbstractInstruction>>#availableFloatRegisterOrNoneFor: */ +static NoDbgRegParms sqInt +availableFloatRegisterOrNoneFor(AbstractInstruction *self_in_CogAbstractInstruction, sqInt liveRegsMask) +{ + if (!(((liveRegsMask & ((1U << DPFPReg0))) != 0))) { + return DPFPReg0; + } + if (!(((liveRegsMask & ((1U << DPFPReg1))) != 0))) { + return DPFPReg1; + } + if (!(((liveRegsMask & ((1U << DPFPReg2))) != 0))) { + return DPFPReg2; + } + if (!(((liveRegsMask & ((1U << DPFPReg3))) != 0))) { + return DPFPReg3; + } + if (!(((liveRegsMask & ((1U << DPFPReg4))) != 0))) { + return DPFPReg4; + } + if (!(((liveRegsMask & ((1U << DPFPReg5))) != 0))) { + return DPFPReg5; + } + if (!(((liveRegsMask & ((1U << DPFPReg6))) != 0))) { + return DPFPReg6; + } + if (!(((liveRegsMask & ((1U << DPFPReg7))) != 0))) { + return DPFPReg7; + } + return NoReg; +} + + +/* For out-of-line literal support, clone a literal from a literal. */ + + /* CogAbstractInstruction>>#cloneLiteralFrom: */ +static NoDbgRegParms AbstractInstruction * +cloneLiteralFrom(AbstractInstruction *self_in_CogAbstractInstruction, AbstractInstruction *existingLiteral) +{ + assert((((existingLiteral->opcode)) == Literal) + && ((!((self_in_CogAbstractInstruction->dependent))) + && (!((self_in_CogAbstractInstruction->address))))); + (self_in_CogAbstractInstruction->opcode) = Literal; + (self_in_CogAbstractInstruction->annotation) = (existingLiteral->annotation); + ((self_in_CogAbstractInstruction->operands))[0] = (((existingLiteral->operands))[0]); + ((self_in_CogAbstractInstruction->operands))[1] = (((existingLiteral->operands))[1]); + ((self_in_CogAbstractInstruction->operands))[2] = (((existingLiteral->operands))[2]); + return self_in_CogAbstractInstruction; +} + + +/* Generate concrete machine code for the instruction at actualAddress, + setting machineCodeSize, and answer the following address. */ + + /* CogAbstractInstruction>>#concretizeAt: */ +static NoDbgRegParms sqInt +concretizeAt(AbstractInstruction *self_in_CogAbstractInstruction, sqInt actualAddress) +{ + (self_in_CogAbstractInstruction->address) = actualAddress; + (self_in_CogAbstractInstruction->machineCodeSize) = dispatchConcretize(self_in_CogAbstractInstruction); + assert((((self_in_CogAbstractInstruction->maxSize)) == null) + || (((self_in_CogAbstractInstruction->maxSize)) >= ((self_in_CogAbstractInstruction->machineCodeSize)))); + return actualAddress + ((self_in_CogAbstractInstruction->machineCodeSize)); +} + + +/* Load the stack pointer register with that of the C stack, effecting + a switch to the C stack. Used when machine code calls into the + CoInterpreter run-time (e.g. to invoke interpreter primitives). */ + + /* CogAbstractInstruction>>#genLoadCStackPointer */ +static NoDbgRegParms sqInt +genLoadCStackPointer(AbstractInstruction *self_in_CogAbstractInstruction) +{ + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, cStackPointerAddress(), NativeSPReg); + return 0; +} + + +/* Load the frame and stack pointer registers with those of the C stack, + effecting a switch to the C stack. Used when machine code calls into + the CoInterpreter run-time (e.g. to invoke interpreter primitives). + N.B. CoInterpreter stack layout dictates that the stack pointer should be + loaded first. + The stack zone is allocated on the C stack before the interpreter runs and + hence before CStackPointer and CFramePointer are captured. So when running + in machine + code the native stack pointer and frame pointer appear to be on a colder + part of the + stack to CStackPointer and CFramePointer. When CStackPointerhas been set + and the frame pointer is still in machine code the current frame looks + like it has lots of + stack. If the frame pointer was set to CFramePointer before hand then it + would be beyond the stack pointer for that one instruction. */ + + /* CogAbstractInstruction>>#genLoadCStackPointers */ +static NoDbgRegParms sqInt +genLoadCStackPointers(AbstractInstruction *self_in_CogAbstractInstruction) +{ + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, cStackPointerAddress(), NativeSPReg); + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, cFramePointerAddress(), FPReg); + return 0; +} + + +/* Switch back to the Smalltalk stack where there may be a C return address + on top of stack below + the last primitive argument. Assign SPReg first because typically it is + used immediately afterwards. + */ + + /* CogAbstractInstruction>>#genLoadStackPointerForPrimCall: */ +static NoDbgRegParms sqInt +genLoadStackPointerForPrimCall(AbstractInstruction *self_in_CogAbstractInstruction, sqInt spareReg) +{ + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, stackPointerAddress(), spareReg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, BytesPerWord, spareReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, spareReg, SPReg); + return 0; +} + + +/* Switch back to the Smalltalk stack. Assign SPReg first + because typically it is used immediately afterwards. */ + + /* CogAbstractInstruction>>#genLoadStackPointers */ +static NoDbgRegParms sqInt +genLoadStackPointers(AbstractInstruction *self_in_CogAbstractInstruction) +{ + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, stackPointerAddress(), SPReg); + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, framePointerAddress(), FPReg); + return 0; +} + + +/* Switch back to the Smalltalk stack where there may be a C return address + on top of stack below + the last primitive argument. Assign SPReg first because typically it is + used immediately afterwards. + */ + + /* CogAbstractInstruction>>#genLoadStackPointersForPrimCall: */ +static NoDbgRegParms sqInt +genLoadStackPointersForPrimCall(AbstractInstruction *self_in_CogAbstractInstruction, sqInt spareReg) +{ + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, stackPointerAddress(), spareReg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, BytesPerWord, spareReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, spareReg, SPReg); + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, framePointerAddress(), FPReg); + return 0; +} + + +/* Save the frame and stack pointer registers to the framePointer + and stackPointer variables. Used to save the machine code frame + for use by the run-time when calling into the CoInterpreter run-time. */ + + /* CogAbstractInstruction>>#genSaveStackPointers */ +static NoDbgRegParms sqInt +genSaveStackPointers(AbstractInstruction *self_in_CogAbstractInstruction) +{ + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, FPReg, framePointerAddress()); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, SPReg, stackPointerAddress()); + return 0; +} + + /* CogAbstractInstruction>>#genWriteCResultIntoReg: */ +static NoDbgRegParms AbstractInstruction * +genWriteCResultIntoReg(AbstractInstruction *self_in_CogAbstractInstruction, sqInt abstractRegister) +{ + if ((abstractRegister != NoReg) + && (abstractRegister != ABIResultReg)) { + genoperandoperand(MoveRR, ABIResultReg, abstractRegister); + } + return self_in_CogAbstractInstruction; +} + + +/* At least x86, x86_64, AArch32 and AArch64 have a 64-bit performance + counter. Subclasses can turn this off if rquired. */ + + /* CogAbstractInstruction>>#has64BitPerformanceCounter */ +static NoDbgRegParms sqInt +has64BitPerformanceCounter(AbstractInstruction *self_in_CogAbstractInstruction) +{ + return 1; +} + + +/* For out-of-line literal support, initialize a sharable literal. */ + + /* CogAbstractInstruction>>#initializeSharableLiteral: */ +static NoDbgRegParms AbstractInstruction * +initializeSharableLiteral(AbstractInstruction *self_in_CogAbstractInstruction, sqInt literal) +{ + (self_in_CogAbstractInstruction->opcode) = Literal; + + /* separate := nil for Slang */ + (self_in_CogAbstractInstruction->annotation) = null; + (self_in_CogAbstractInstruction->address) = null; + (self_in_CogAbstractInstruction->dependent) = null; + ((self_in_CogAbstractInstruction->operands))[0] = literal; + ((self_in_CogAbstractInstruction->operands))[1] = (1 + ((((usqInt)(BytesPerOop) << 1)))); + ((self_in_CogAbstractInstruction->operands))[2] = -1; + return self_in_CogAbstractInstruction; +} + + +/* For out-of-line literal support, initialize an unsharable literal. */ + + /* CogAbstractInstruction>>#initializeUniqueLiteral: */ +static NoDbgRegParms AbstractInstruction * +initializeUniqueLiteral(AbstractInstruction *self_in_CogAbstractInstruction, sqInt literal) +{ + (self_in_CogAbstractInstruction->opcode) = Literal; + + /* separate := nil for Slang */ + (self_in_CogAbstractInstruction->annotation) = null; + (self_in_CogAbstractInstruction->address) = null; + (self_in_CogAbstractInstruction->dependent) = null; + ((self_in_CogAbstractInstruction->operands))[0] = literal; + ((self_in_CogAbstractInstruction->operands))[1] = (0 + ((((usqInt)(BytesPerOop) << 1)))); + ((self_in_CogAbstractInstruction->operands))[2] = -1; + return self_in_CogAbstractInstruction; +} + + +/* This need only be implemented if a subclass uses out-of-line literals. + Currently this is true only of ARM processors that have fixed length + instructions. + */ + + /* CogAbstractInstruction>>#instructionIsPCRelativeLoad: */ +static NoDbgRegParms sqInt +instructionIsPCRelativeLoad(AbstractInstruction *self_in_CogAbstractInstruction, sqInt instr) +{ + return 0; +} + + /* CogAbstractInstruction>>#isJump */ +static NoDbgRegParms int +isJump(AbstractInstruction *self_in_CogAbstractInstruction) +{ + return ((((self_in_CogAbstractInstruction->opcode)) >= FirstJump) && (((self_in_CogAbstractInstruction->opcode)) <= LastJump)); +} + + +/* Always implement this to avoid a check in bump64BitPerfCounterByUsecs:. + Assume a cycle count. Processors using less that a cycle count override. */ + + /* CogAbstractInstruction>>#perfCounterRatePerUsec */ +static NoDbgRegParms sqInt +perfCounterRatePerUsec(AbstractInstruction *self_in_CogAbstractInstruction) +{ + return 1000; +} + + +/* We assume here that calls and jumps look the same as regards their + displacement. This works on at least x86, ARM and x86_64. Processors on + which that isn't the + case can override as necessary. */ + + /* CogAbstractInstruction>>#relocateJumpLongBeforeFollowingAddress:by: */ +static NoDbgRegParms AbstractInstruction * +relocateJumpLongBeforeFollowingAddressby(AbstractInstruction *self_in_CogAbstractInstruction, sqInt pc, sqInt delta) +{ + relocateCallBeforeReturnPCby(self_in_CogAbstractInstruction, pc, delta); + return self_in_CogAbstractInstruction; +} + + +/* Relocate a long conditional jump before pc. Default to relocating a + non-conditional jump. + Processors that have different formats for conditional and unconditional + jumps override. */ + + /* CogAbstractInstruction>>#relocateJumpLongConditionalBeforeFollowingAddress:by: */ +static NoDbgRegParms AbstractInstruction * +relocateJumpLongConditionalBeforeFollowingAddressby(AbstractInstruction *self_in_CogAbstractInstruction, sqInt pc, sqInt delta) +{ + relocateJumpLongBeforeFollowingAddressby(self_in_CogAbstractInstruction, pc, delta); + return self_in_CogAbstractInstruction; +} + + /* CogAbstractInstruction>>#resolveJumpTarget */ +static NoDbgRegParms AbstractInstruction * +resolveJumpTarget(AbstractInstruction *self_in_CogAbstractInstruction) +{ + BytecodeFixup *fixup; + + assert(isJump(self_in_CogAbstractInstruction)); + fixup = ((BytecodeFixup *) (((self_in_CogAbstractInstruction->operands))[0])); + if (addressIsInFixups(fixup)) { + assert(addressIsInInstructions((fixup->targetInstruction))); + jmpTarget(self_in_CogAbstractInstruction, (fixup->targetInstruction)); + } + return self_in_CogAbstractInstruction; +} + + +/* Rewrite a conditional jump long to jump to target. This version defaults + to using + rewriteJumpLongAt:, which works for many ISAs. Subclasses override if + necessary. */ + + /* CogAbstractInstruction>>#rewriteConditionalJumpLongAt:target: */ +static NoDbgRegParms sqInt +rewriteConditionalJumpLongAttarget(AbstractInstruction *self_in_CogAbstractInstruction, sqInt callSiteReturnAddress, sqInt callTargetAddress) +{ + return rewriteCallAttarget(self_in_CogAbstractInstruction, callSiteReturnAddress, callTargetAddress); +} + + +/* It is assumed that a PC-relative load moves wordSize bytes. Subclasses + override as appropriate. + */ + + /* CogAbstractInstruction>>#widthOfPCRelativeLoad: */ +static NoDbgRegParms sqInt +widthOfPCRelativeLoad(AbstractInstruction *self_in_CogAbstractInstruction, sqInt instr) +{ + assert(instructionIsPCRelativeLoad(self_in_CogAbstractInstruction, instr)); + return BytesPerWord; +} + + /* CogBlockMethod>>#cmHomeMethod */ +static NoDbgRegParms CogMethod * +cmHomeMethod(CogBlockMethod *self_in_CogBlockMethod) +{ + return (((self_in_CogBlockMethod->cmType)) >= CMMethod + ? ((CogMethod *) self_in_CogBlockMethod) + : (assert(CMBlock == (self_in_CogBlockMethod->cmType)), + ((CogMethod *) ((((usqInt)self_in_CogBlockMethod)) - ((self_in_CogBlockMethod->homeOffset)))))); +} + + /* CogBlockMethod>>#isCMBlock */ +static NoDbgRegParms int +isCMBlock(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMBlock; +} + + /* CogBlockMethod>>#isCMClosedPIC */ +static NoDbgRegParms int +isCMClosedPIC(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMClosedPIC; +} + + /* CogBlockMethod>>#isCMFree */ +static NoDbgRegParms int +isCMFree(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMFree; +} + + /* CogBlockMethod>>#isCMMethodEtAl */ +static NoDbgRegParms int +isCMMethodEtAl(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) >= CMMethod; +} + + /* CogBlockMethod>>#isCMOpenPIC */ +static NoDbgRegParms int +isCMOpenPIC(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMOpenPIC; +} + + /* CogBytecodeDescriptor>>#isBranch */ +static NoDbgRegParms sqInt +isBranch(BytecodeDescriptor *self_in_CogBytecodeDescriptor) +{ + return (((self_in_CogBytecodeDescriptor->spanFunction))) + && (!((self_in_CogBytecodeDescriptor->isBlockCreation))); +} + + /* CogBytecodeDescriptor>>#isConditionalBranch */ +static NoDbgRegParms sqInt +isConditionalBranch(BytecodeDescriptor *self_in_CogBytecodeDescriptor) +{ + return ((self_in_CogBytecodeDescriptor->isBranchTrue)) + || ((self_in_CogBytecodeDescriptor->isBranchFalse)); +} + + +/* With CqR we assume constants are 32-bits or less. */ + + /* CogInLineLiteralsX64Compiler>>#computeSizeOfArithCqR */ +static NoDbgRegParms int +computeSizeOfArithCqR(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler) +{ + if (isQuick(self_in_CogInLineLiteralsX64Compiler, ((self_in_CogInLineLiteralsX64Compiler->operands))[0])) { + return 4; + } + if (is32BitSignedImmediate(self_in_CogInLineLiteralsX64Compiler, ((self_in_CogInLineLiteralsX64Compiler->operands))[0])) { + return (((self_in_CogInLineLiteralsX64Compiler->operands))[1] + ? 7 + : 6); + } + return 13; +} + + /* CogInLineLiteralsX64Compiler>>#computeSizeOfArithCwR */ +static NoDbgRegParms int +computeSizeOfArithCwR(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler) +{ + return 13; +} + + /* CogInLineLiteralsX64Compiler>>#concretizeArithCwR: */ +static NoDbgRegParms sqInt +concretizeArithCwR(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt x64opcode) +{ + sqInt reg; + sqInt reverse; + sqInt value; + + value = ((self_in_CogInLineLiteralsX64Compiler->operands))[0]; + reg = ((self_in_CogInLineLiteralsX64Compiler->operands))[1]; + + /* Tst & Cmp; backwards */ + reverse = (x64opcode == 133) + || (x64opcode == 57); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[0] = (rexRxb(self_in_CogInLineLiteralsX64Compiler, RISCTempReg, 0, RISCTempReg)); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[1] = (184 + (RISCTempReg & 7)); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[2] = (value & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[3] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 24) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[6] = ((((usqInt)(value)) >> 32) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[7] = ((((usqInt)(value)) >> 40) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[8] = ((((usqInt)(value)) >> 48) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[9] = ((((usqInt)(value)) >> 56) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[10] = ((reverse + ? rexRxb(self_in_CogInLineLiteralsX64Compiler, RISCTempReg, 0, reg) + : rexRxb(self_in_CogInLineLiteralsX64Compiler, reg, 0, RISCTempReg))); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[11] = x64opcode; + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[12] = ((reverse + ? modRMRO(self_in_CogInLineLiteralsX64Compiler, ModReg, reg, RISCTempReg) + : modRMRO(self_in_CogInLineLiteralsX64Compiler, ModReg, RISCTempReg, reg))); + assert((((self_in_CogInLineLiteralsX64Compiler->machineCode))[12]) > 144); + return 13; +} + + +/* Will get inlined into concretizeAt: switch. */ + + /* CogInLineLiteralsX64Compiler>>#concretizeMoveCwR */ +static NoDbgRegParms sqInt +concretizeMoveCwR(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler) +{ + sqInt offset; + sqInt reg; + sqInt value; + + value = ((self_in_CogInLineLiteralsX64Compiler->operands))[0]; + reg = ((self_in_CogInLineLiteralsX64Compiler->operands))[1]; + if (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) value))) + || ((((AbstractInstruction *) value)) == (methodLabel))) { + value = ((((AbstractInstruction *) value))->address); + } + if (/* addressIsInCurrentCompilation: */ + ((((usqInt)value)) >= ((methodLabel->address))) + && ((((usqInt)value)) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize))))) { + offset = value - (((self_in_CogInLineLiteralsX64Compiler->address)) + 7); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[0] = (rexRxb(self_in_CogInLineLiteralsX64Compiler, reg, 0, 0)); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[1] = 141; + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[2] = (modRMRO(self_in_CogInLineLiteralsX64Compiler, ModRegInd, 5, reg)); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[3] = (offset & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[4] = ((((usqInt)(offset)) >> 8) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[5] = ((((usqInt)(offset)) >> 16) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[6] = ((((usqInt)(offset)) >> 24) & 0xFF); + + /* LoadEffectiveAddress */ + return 7; + } + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[0] = (rexRxb(self_in_CogInLineLiteralsX64Compiler, 0, 0, reg)); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[1] = (184 + (reg & 7)); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[2] = (value & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[3] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 24) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[6] = ((((usqInt)(value)) >> 32) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[7] = ((((usqInt)(value)) >> 40) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[8] = ((((usqInt)(value)) >> 48) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[9] = ((((usqInt)(value)) >> 56) & 0xFF); + + /* Add a nop to disambiguate between MoveCwR/PushCw and ArithCwR, which ends with a (self mod: ModReg RM: 0 RO: 0) */ + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[10] = 144; + return 11; +} + + +/* Answer the inline cache tag for the return address of a send. */ + + /* CogInLineLiteralsX64Compiler>>#inlineCacheTagAt: */ +static NoDbgRegParms unsigned int +inlineCacheTagAt(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt callSiteReturnAddress) +{ + return literal32BeforeFollowingAddress(self_in_CogInLineLiteralsX64Compiler, ((usqInt)(callSiteReturnAddress - 5))); +} + + +/* Answer if the receiver is a pc-dependent instruction. */ + + /* CogInLineLiteralsX64Compiler>>#isPCDependent */ +static NoDbgRegParms sqInt +isPCDependent(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler) +{ + return (isJump(self_in_CogInLineLiteralsX64Compiler)) + || (((self_in_CogInLineLiteralsX64Compiler->opcode)) == AlignmentNops); +} + + +/* Answer the 32-bit literal embedded in the instruction immediately + preceding followingAddress. + */ + + /* CogInLineLiteralsX64Compiler>>#literal32BeforeFollowingAddress: */ +static NoDbgRegParms unsigned int +literal32BeforeFollowingAddress(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt followingAddress) +{ + return ((unsigned int) (unalignedLong32At((void *)(followingAddress - 4)))); +} + + +/* Answer the literal embedded in the instruction immediately preceding + followingAddress. This is used in the MoveCwR, PushCw and ArithCwR cases; + these are distinguished by a + nop following the literal load in MoveCwR, a 16r48 + reg ending the PushCw + sequence, and + a (self mod: ModReg RM: rX RO: rY) ending the ArithCwR sequence, which is + at least 16rC0. */ + + /* CogInLineLiteralsX64Compiler>>#literalBeforeFollowingAddress: */ +static NoDbgRegParms sqInt +literalBeforeFollowingAddress(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt followingAddress) +{ + sqInt base; + sqInt lastByte; + + lastByte = byteAt((void *)(followingAddress - 1)); + + /* ArithCwR */ + base = followingAddress - ((lastByte == 144 + ? 9 + : (lastByte < 144 + ? 10 + : 11))); + return unalignedLongAt((void *)(base)); +} + + /* CogInLineLiteralsX64Compiler>>#loadLiteralByteSize */ +static NoDbgRegParms sqInt +loadLiteralByteSize(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler) +{ + return 11 /* moveCwRByteSize */; +} + + +/* Size a jump and set its address. The target may be another instruction + or an absolute address. On entry the address inst var holds our virtual + address. On exit address is set to eventualAbsoluteAddress, which is + where this instruction will be output. The span of a jump to a following + instruction is therefore between that instruction's address and this + instruction's address ((which are both still their virtual addresses), but + the span of a jump to a preceding instruction or to an absolute address is + between that instruction's address (which by now is its eventual absolute + address) or absolute address and eventualAbsoluteAddress. */ + + /* CogInLineLiteralsX64Compiler>>#sizePCDependentInstructionAt: */ +static NoDbgRegParms unsigned char +sizePCDependentInstructionAt(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt eventualAbsoluteAddress) +{ + AbstractInstruction *abstractInstruction; + sqInt alignment; + sqInt maximumSpan; + sqInt target; + + if (((self_in_CogInLineLiteralsX64Compiler->opcode)) == AlignmentNops) { + (self_in_CogInLineLiteralsX64Compiler->address) = eventualAbsoluteAddress; + alignment = ((self_in_CogInLineLiteralsX64Compiler->operands))[0]; + return ((self_in_CogInLineLiteralsX64Compiler->machineCodeSize) = ((eventualAbsoluteAddress + (alignment - 1)) & (-alignment)) - eventualAbsoluteAddress); + } + assert(isJump(self_in_CogInLineLiteralsX64Compiler)); + target = ((self_in_CogInLineLiteralsX64Compiler->operands))[0]; + abstractInstruction = ((AbstractInstruction *) target); + if (/* isAnInstruction: */ + (addressIsInInstructions(abstractInstruction)) + || (abstractInstruction == (methodLabel))) { + maximumSpan = ((abstractInstruction->address)) - (((abstractInstructionfollows(self_in_CogInLineLiteralsX64Compiler, abstractInstruction) + ? eventualAbsoluteAddress + : (self_in_CogInLineLiteralsX64Compiler->address))) + 2); + } + else { + maximumSpan = target - (eventualAbsoluteAddress + 2); + } + (self_in_CogInLineLiteralsX64Compiler->address) = eventualAbsoluteAddress; + if (((self_in_CogInLineLiteralsX64Compiler->opcode)) >= FirstShortJump) { + (self_in_CogInLineLiteralsX64Compiler->machineCodeSize) = (isQuick(self_in_CogInLineLiteralsX64Compiler, maximumSpan) + ? 2 + : (((self_in_CogInLineLiteralsX64Compiler->opcode)) == Jump + ? 5 + : 6)); + } + else { + switch ((self_in_CogInLineLiteralsX64Compiler->opcode)) { + case JumpLong: + (self_in_CogInLineLiteralsX64Compiler->machineCodeSize) = 5; + break; + case JumpFull: + (self_in_CogInLineLiteralsX64Compiler->machineCodeSize) = 12; + break; + case JumpLongZero: + case JumpLongNonZero: + (self_in_CogInLineLiteralsX64Compiler->machineCodeSize) = 6; + break; + default: + error("Case not found and no otherwise clause"); + } + } + return (self_in_CogInLineLiteralsX64Compiler->machineCodeSize); +} + + +/* Rewrite the literal in the instruction immediately preceding + followingAddress. This is used in the MoveCwR, PushCw and CmpCwR cases; + these are distinguished by a + nop following the literal load in MoveCwR, a 16r50 + reg ending the PushCw + sequence, and + a (self mod: ModReg RM: rX RO: rY) ending the CmpCwR sequence, which is at + least 16rC0. */ + + /* CogInLineLiteralsX64Compiler>>#storeLiteral:beforeFollowingAddress: */ +static NoDbgRegParms AbstractInstruction * +storeLiteralbeforeFollowingAddress(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt literal, sqInt followingAddress) +{ + sqInt base; + sqInt lastByte; + + lastByte = byteAt((void *)(followingAddress - 1)); + + /* ArithCwR */ + base = followingAddress - ((lastByte <= 144 + ? (lastByte == 144 + ? 9 + : 10) + : 11)); + unalignedLongAtput((void *)(base),literal); + return self_in_CogInLineLiteralsX64Compiler; +} + + /* Cogit>>#AddCq:R: */ +static NoDbgRegParms AbstractInstruction * +gAddCqR(sqInt quickConstant, sqInt reg) +{ + return genoperandoperand(AddCqR, quickConstant, reg); +} + + +/* N.B. if the condition codes don't require setting and three address + arithmetic is unavailable, then LoadEffectiveAddressMw:r:R: can be used + instead. + */ + + /* Cogit>>#AddCq:R:R: */ +static NoDbgRegParms AbstractInstruction * +gAddCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *first; + + if (srcReg == destReg) { + return genoperandoperand(AddCqR, quickConstant, destReg); + } + first = genoperandoperand(MoveRR, srcReg, destReg); + + /* #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, quickConstant, destReg); + return first; +} + + /* Cogit>>#AndCq:R: */ +static NoDbgRegParms AbstractInstruction * +gAndCqR(sqInt quickConstant, sqInt reg) +{ + return genoperandoperand(AndCqR, quickConstant, reg); +} + + /* Cogit>>#AndCq:R:R: */ +static NoDbgRegParms AbstractInstruction * +gAndCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *first; + + if (srcReg == destReg) { + return genoperandoperand(AndCqR, quickConstant, destReg); + } + first = genoperandoperand(MoveRR, srcReg, destReg); + + /* #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, destReg); + return first; +} + + +/* destReg := (signed)srcReg >> quickConstant */ + + /* Cogit>>#ArithmeticShiftRightCq:R:R: */ +static NoDbgRegParms AbstractInstruction * +gArithmeticShiftRightCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *first; + + first = genoperandoperand(MoveRR, srcReg, destReg); + genoperandoperand(ArithmeticShiftRightCqR, quickConstant, destReg); + return first; +} + + /* Cogit>>#abortOffset */ +sqInt +abortOffset(void) +{ + return missOffset; +} + + /* Cogit>>#abstractInstruction:follows: */ +static NoDbgRegParms int +abstractInstructionfollows(AbstractInstruction *theAbstractInstruction, AbstractInstruction *anAbstractInstruction) +{ + return theAbstractInstruction > anAbstractInstruction; +} + + /* Cogit>>#addCleanBlockStarts */ +static void +addCleanBlockStarts(void) +{ + sqInt i; + sqInt lit; + sqInt startPCOrNil; + sqInt toDoLimit; + + toDoLimit = literalCountOf(methodObj); + for (i = 1; i <= toDoLimit; i += 1) { + lit = fetchPointerofObject(i, methodObj); + if ((startPCOrNil = startPCOrNilOfLiteralin(lit, methodObj))) { + maxLitIndex = ((maxLitIndex < i) ? i : maxLitIndex); + addBlockStartAtnumArgsnumCopiedspan(startPCOrNil - 1, argumentCountOfClosure(lit), copiedValueCountOfClosure(lit), spanForCleanBlockStartingAt(startPCOrNil - 1)); + } + } +} + + +/* Perform an integrity/leak check using the heapMap. + Set a bit at each cog method's header. */ + + /* Cogit>>#addCogMethodsToHeapMap */ +void +addCogMethodsToHeapMap(void) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + heapMapAtWordPut(cogMethod, 1); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } +} + + /* Cogit>>#addressIsInFixups: */ +static NoDbgRegParms sqInt +addressIsInFixups(BytecodeFixup *address) +{ + return (BytecodeFixup *)address >= fixups && (BytecodeFixup *)address < (fixups + numAbstractOpcodes); +} + + +/* calculate the end of the n'th case statement - which is complicated + because we have case 1 right at the top of our CPIC and then build up from + the last one. Yes I know this sounds strange, but trust me - I'm an + Engineer, we do things backwards all the emit + */ + + /* Cogit>>#addressOfEndOfCase:inCPIC: */ +static NoDbgRegParms sqInt +addressOfEndOfCaseinCPIC(sqInt n, CogMethod *cPIC) +{ + assert((n >= 1) + && (n <= MaxCPICCases)); + return (n == 1 + ? (((sqInt)cPIC)) + firstCPICCaseOffset + : ((((sqInt)cPIC)) + firstCPICCaseOffset) + (((MaxCPICCases + 1) - n) * cPICCaseSize)); +} + + +/* Align methodZoneBase to that for the start of a method. */ + + /* Cogit>>#alignMethodZoneBase */ +static void +alignMethodZoneBase(void) +{ + usqInt oldBase; + + oldBase = methodZoneBase; + methodZoneBase = roundUpToMethodAlignment(backEnd, methodZoneBase); + stopsFromto(backEnd, oldBase, methodZoneBase - 1); +} + + /* Cogit>>#alignUptoRoutineBoundary: */ +static NoDbgRegParms sqInt +alignUptoRoutineBoundary(sqInt anAddress) +{ + return (((anAddress + 7) | 7) - 7); +} + + +/* Check that all methods have valid selectors, and that all linked sends are + to valid targets and have valid cache tags + */ + + /* Cogit>>#allMachineCodeObjectReferencesValid */ +static sqInt +allMachineCodeObjectReferencesValid(void) +{ + CogMethod *cogMethod; + sqInt ok; + + ok = 1; + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (!(((cogMethod->cmType)) == CMFree)) { + if (!(asserta(checkValidOopReference((cogMethod->selector))))) { + ok = 0; + } + if (!(asserta((cogMethodDoesntLookKosher(cogMethod)) == 0))) { + ok = 0; + } + } + if ((((cogMethod->cmType)) >= CMMethod) + || (((cogMethod->cmType)) == CMOpenPIC)) { + if (!(asserta((mapForperformUntilarg(cogMethod, checkIfValidOopRefAndTargetpccogMethod, cogMethod)) == 0))) { + ok = 0; + } + } + if (((cogMethod->cmType)) == CMClosedPIC) { + if (!(asserta(noTargetsFreeInClosedPIC(cogMethod)))) { + ok = 0; + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + return ok; +} + + /* Cogit>>#allMethodsHaveCorrectHeader */ +static sqInt +allMethodsHaveCorrectHeader(void) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + if (!(((cogMethod->objectHeader)) == (nullHeaderForMachineCodeMethod()))) { + return 0; + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + return 1; +} + + /* Cogit>>#annotateAbsolutePCRef: */ +static NoDbgRegParms AbstractInstruction * +annotateAbsolutePCRef(AbstractInstruction *abstractInstruction) +{ + (abstractInstruction->annotation = IsAbsPCReference); + return abstractInstruction; +} + + /* Cogit>>#annotateBytecode: */ +static NoDbgRegParms AbstractInstruction * +annotateBytecode(AbstractInstruction *abstractInstruction) +{ + (abstractInstruction->annotation = HasBytecodePC); + return abstractInstruction; +} + + /* Cogit>>#annotate:objRef: */ +static NoDbgRegParms AbstractInstruction * +annotateobjRef(AbstractInstruction *abstractInstruction, sqInt anOop) +{ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(anOop)) + && ((oopisGreaterThan(anOop, classTableRootObj())) + || (oopisLessThan(anOop, nilObject())))) { + setHasMovableLiteral(1); + if (isYoungObject(anOop)) { + setHasYoungReferent(1); + } + (abstractInstruction->annotation = IsObjectReference); + } + return abstractInstruction; +} + + /* Cogit>>#assertSaneJumpTarget: */ +static NoDbgRegParms void +assertSaneJumpTarget(AbstractInstruction *jumpTarget) +{ + assert((!closedPICSize) + || ((!openPICSize) + || ((addressIsInInstructions(jumpTarget)) + || ((((((usqInt)jumpTarget)) >= codeBase) && ((((usqInt)jumpTarget)) <= ((((sqInt)(limitZony()))) + (((closedPICSize < openPICSize) ? openPICSize : closedPICSize))))))))); +} + + +/* Answer an unused abstract register in the registerMask, or NoReg if none. */ + + /* Cogit>>#availableRegisterOrNoneIn: */ +static NoDbgRegParms sqInt +availableRegisterOrNoneIn(sqInt liveRegsMask) +{ + sqInt reg; + + if (liveRegsMask) { + for (reg = 0; reg <= 0x1F; reg += 1) { + if (((liveRegsMask & (1ULL << reg)) != 0)) { + return reg; + } + } + } + return NoReg; +} + + +/* Evaluate binaryFunction with the block start mcpc and supplied arg for + each entry in the block dispatch. If the function answers non-zero answer + the value + it answered. Used to update back-references to the home method in + compaction. */ + + /* Cogit>>#blockDispatchTargetsFor:perform:arg: */ +static NoDbgRegParms usqInt +blockDispatchTargetsForperformarg(CogMethod *cogMethod, usqInt (*binaryFunction)(sqInt mcpc, sqInt arg), sqInt arg) +{ + sqInt blockEntry; + sqInt end; + sqInt pc; + usqInt result; + usqInt targetpc; + + if (!((cogMethod->blockEntryOffset))) { + return null; + } + blockEntry = ((cogMethod->blockEntryOffset)) + (((sqInt)cogMethod)); + pc = blockEntry; + end = (mapEndFor(cogMethod)) - 1; + while (pc < end) { + if (isJumpAt(backEnd, pc)) { + targetpc = jumpTargetPCAt(backEnd, pc); + if (targetpc < blockEntry) { + result = binaryFunction(targetpc, arg); + if (result) { + return result; + } + } + } + pc += instructionSizeAt(backEnd, pc); + } + return 0; +} + + +/* Answer the zero-relative bytecode pc matching the machine code pc argument + in cogMethod, given the start of the bytecodes for cogMethod's block or + method object. */ + + /* Cogit>>#bytecodePCFor:startBcpc:in: */ +sqInt +bytecodePCForstartBcpcin(sqInt mcpc, sqInt startbcpc, CogBlockMethod *cogMethod) +{ + sqInt aMethodObj; + sqInt annotation; + sqInt bcpc; + sqInt bsOffset; + sqInt byte; + BytecodeDescriptor *descriptor; + sqInt distance; + sqInt endbcpc; + CogMethod *homeMethod; + sqInt isBackwardBranch; + sqInt isInBlock; + sqInt latestContinuation; + sqInt map; + sqInt mapByte; + usqInt mcpcUsqInt; + sqInt nExts; + sqInt nextBcpc; + sqInt result; + sqInt targetPC; + + /* begin mapFor:bcpc:performUntil:arg: */ + descriptor = ((BytecodeDescriptor *) 0); + latestContinuation = 0; + mapByte = 0; + nextBcpc = 0; + assert(((cogMethod->stackCheckOffset)) > 0); + mcpcUsqInt = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + + /* The stack check maps to the start of the first bytecode, + the first bytecode being effectively after frame build. */ + result = findIsBackwardBranchMcpcBcpcMatchingMcpc(null, 0 + ((((usqInt)(HasBytecodePC) << 1))), ((char *) mcpcUsqInt), startbcpc, ((void *)mcpc)); + if (result) { + return result; + } + bcpc = startbcpc; + + /* In both CMMethod and CMBlock cases find the start of the map and + skip forward to the bytecode pc map entry for the stack check. */ + if (((cogMethod->cmType)) >= CMMethod) { + isInBlock = (cogMethod->cpicHasMNUCaseOrCMIsFullBlock); + homeMethod = ((CogMethod *) cogMethod); + assert(startbcpc == (startPCOfMethodHeader((homeMethod->methodHeader)))); + map = ((((usqInt)homeMethod)) + ((homeMethod->blockSize))) - 1; + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert((annotation == IsAbsPCReference) + || ((annotation == IsObjectReference) + || ((annotation == IsRelativeCall) + || (annotation == IsDisplacementX2N)))); + latestContinuation = startbcpc; + aMethodObj = (homeMethod->methodObject); + endbcpc = (numBytesOf(aMethodObj)) - 1; + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + + /* If the method has a primitive, skip it and the error code store, if any; + Logically. these come before the stack check and so must be ignored. */ + bcpc += deltaToSkipPrimAndErrorStoreInheader(aMethodObj, (homeMethod->methodHeader)); + } + else { + isInBlock = 1; + assert(bcpc == ((cogMethod->startpc))); + homeMethod = cmHomeMethod(cogMethod); + map = findMapLocationForMcpcinMethod((((usqInt)cogMethod)) + (sizeof(CogBlockMethod)), homeMethod); + assert(map != 0); + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert(((((usqInt)(annotation)) >> AnnotationShift) == HasBytecodePC) + || ((((usqInt)(annotation)) >> AnnotationShift) == IsDisplacementX2N)); + + /* fiducial */ + while (((annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift)) != HasBytecodePC) { + map -= 1; + } + + /* skip fiducial; i.e. the map entry for the pc immediately following the method header. */ + map -= 1; + aMethodObj = (homeMethod->methodObject); + bcpc = startbcpc - (/* blockCreationBytecodeSizeForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? AltBlockCreationBytecodeSize + : BlockCreationBytecodeSize)); + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + endbcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, -1, aMethodObj) + : 0)); + bcpc = startbcpc; + } + nExts = 0; + enumeratingCogMethod = homeMethod; + + /* Now skip up through the bytecode pc map entry for the stack check. */ + while ((((usqInt)((byteAt((void *)(map))))) >> AnnotationShift) != HasBytecodePC) { + map -= 1; + } + map -= 1; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + mcpcUsqInt += (mapByte & DisplacementMask); + if (annotation >= HasBytecodePC) { + if ((annotation == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + while (1) { + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + if (isInBlock) { + if (bcpc >= endbcpc) { + return 0; + } + } + else { + if (((descriptor->isReturn)) + && (bcpc >= latestContinuation)) { + return 0; + } + if ((isBranch(descriptor)) + || ((descriptor->isBlockCreation))) { + /* begin latestContinuationPCFor:at:exts:in: */ + distance = ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj); + targetPC = (bcpc + ((descriptor->numBytes))) + (((distance < 0) ? 0 : distance)); + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + } + } + nextBcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj) + : 0)); + if (((descriptor->isMapped)) + || (isInBlock + && ((descriptor->isMappedInBlock)))) break; + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + isBackwardBranch = (isBranch(descriptor)) + && ((/* begin isBackwardBranch:at:exts:in: */ + assert(((descriptor->spanFunction))), + (((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj)) < 0)); + result = findIsBackwardBranchMcpcBcpcMatchingMcpc(descriptor, (isBackwardBranch + ? ((((usqInt)(annotation) << 1))) + 1 + : ((sqInt)((usqInt)(annotation) << 1))), ((char *) mcpcUsqInt), (isBackwardBranch + ? bcpc - (2 * nExts) + : bcpc), ((void *)mcpc)); + if (result) { + return result; + } + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + } + else { + assert(((((usqInt)(mapByte)) >> AnnotationShift) == IsDisplacementX2N) + || ((((usqInt)(mapByte)) >> AnnotationShift) == IsAnnotationExtension)); + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpcUsqInt += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + + /* defensive; we exit on bcpc */ + return 0; +} + + /* Cogit>>#Call: */ +static NoDbgRegParms AbstractInstruction * +gCall(sqInt callTarget) +{ + return genoperand(Call, callTarget); +} + + /* Cogit>>#CmpCq:R: */ +static NoDbgRegParms AbstractInstruction * +gCmpCqR(sqInt quickConstant, sqInt reg) +{ + return genoperandoperand(CmpCqR, quickConstant, reg); +} + + +/* This is a static version of ceCallCogCodePopReceiverReg for break-pointing + when debugging in C. Marked so the code generator won't delete it. */ + + /* Cogit>>#callCogCodePopReceiver */ +static void +callCogCodePopReceiver(void) +{ + realCECallCogCodePopReceiverReg(); + + /* (and this exists only to reference Debug) */ + if (!Debug) { + error("what??"); + } +} + + +/* This is a static version of ceCallCogCodePopReceiverAndClassRegs for + break-pointing when debugging in C. Marked so the code generator + won't delete it. */ + + /* Cogit>>#callCogCodePopReceiverAndClassRegs */ +static void +callCogCodePopReceiverAndClassRegs(void) +{ + realCECallCogCodePopReceiverAndClassRegs(); +} + + +/* Code entry closed PIC miss. A send has fallen + through a closed (finite) polymorphic inline cache. + Either extend it or patch the send site to an open PIC. + The stack looks like: + receiver + args + sp=> sender return address */ +/* Marked so the code generator won't delete it. */ + + /* Cogit>>#ceCPICMiss:receiver: */ +static NoDbgRegParms sqInt +ceCPICMissreceiver(CogMethod *cPIC, sqInt receiver) +{ + sqInt cacheTag; + int errorSelectorOrNil; + sqInt methodOrSelectorIndex; + sqInt newTargetMethodOrNil; + sqInt outerReturn; + sqInt result; + sqInt selector; + + assert(addressCouldBeOop(receiver)); + if (isOopForwarded(receiver)) { + return ceSendFromInLineCacheMiss(cPIC); + } + outerReturn = stackTop(); + assert(!(((inlineCacheTagAt(backEnd, outerReturn)) == (picAbortDiscriminatorValue())))); + if (((cPIC->cPICNumCases)) < MaxCPICCases) { + selector = (cPIC->selector); + + /* begin lookup:for:methodAndErrorSelectorInto: */ + methodOrSelectorIndex = lookupOrdinaryreceiver(selector, receiver); + if ((((usqInt)methodOrSelectorIndex)) > (maxLookupNoMNUErrorCode())) { + if (!(isOopCompiledMethod(methodOrSelectorIndex))) { + newTargetMethodOrNil = methodOrSelectorIndex; + errorSelectorOrNil = SelectorCannotInterpret; + goto l1; + } + if ((!(methodHasCogMethod(methodOrSelectorIndex))) + && (methodShouldBeCogged(methodOrSelectorIndex))) { + /* We assume cog:selector: will *not* reclaim the method zone */ + cogselector(methodOrSelectorIndex, selector); + } + newTargetMethodOrNil = methodOrSelectorIndex; + errorSelectorOrNil = null; + goto l1; + } + if (methodOrSelectorIndex == SelectorDoesNotUnderstand) { + methodOrSelectorIndex = lookupMNUreceiver(splObj(SelectorDoesNotUnderstand), receiver); + if ((((usqInt)methodOrSelectorIndex)) > (maxLookupNoMNUErrorCode())) { + assert(isOopCompiledMethod(methodOrSelectorIndex)); + if ((!(methodHasCogMethod(methodOrSelectorIndex))) + && (methodShouldBeCogged(methodOrSelectorIndex))) { + /* We assume cog:selector: will *not* reclaim the method zone */ + cogselector(methodOrSelectorIndex, splObj(SelectorDoesNotUnderstand)); + } + newTargetMethodOrNil = methodOrSelectorIndex; + errorSelectorOrNil = SelectorDoesNotUnderstand; + goto l1; + } + newTargetMethodOrNil = null; + errorSelectorOrNil = SelectorDoesNotUnderstand; + goto l1; + } + newTargetMethodOrNil = null; + errorSelectorOrNil = methodOrSelectorIndex; + /* end lookup:for:methodAndErrorSelectorInto: */ +l1:; + } + else { + newTargetMethodOrNil = (errorSelectorOrNil = null); + } + + /* We assume lookupAndCog:for: will *not* reclaim the method zone */ + assert(outerReturn == (stackTop())); + + /* begin ensureWritableCodeZone */ + cacheTag = inlineCacheTagForInstance(receiver); + if ((((cPIC->cPICNumCases)) >= MaxCPICCases) + || (/* closedPICInappropriateForCacheTag:targetMethod:orErrorSelector: */ + ((errorSelectorOrNil) + && (errorSelectorOrNil != SelectorDoesNotUnderstand)) + || ((!newTargetMethodOrNil) + || (isYoung(newTargetMethodOrNil))))) { + result = patchToOpenPICFornumArgsreceiver((cPIC->selector), (cPIC->cmNumArgs), receiver); + assert(!result); + + /* begin ensureExecutableCodeZone */ + return ceSendFromInLineCacheMiss(cPIC); + } + + /* Now extend the PIC with the new case. */ + cogExtendPICCaseNMethodtagisMNUCase(cPIC, newTargetMethodOrNil, cacheTag, errorSelectorOrNil == SelectorDoesNotUnderstand); + + /* begin ensureExecutableCodeZone */ + + /* Jump back into the pic at its entry in case this is an MNU. */ + executeCogPICfromLinkedSendWithReceiverandCacheTag(cPIC, receiver, literal32BeforeFollowingAddress(backEnd, ((usqInt)(outerReturn - 5)))); + + /* NOTREACHED */ + return null; +} + + +/* An in-line cache check in a method has failed. The failing entry check has + jumped to the ceMethodAbort abort call at the start of the method which + has called this routine. + If possible allocate a closed PIC for the current and existing classes. + The stack looks like: + receiver + args + sender return address + sp=> ceMethodAbort call return address + So we can find the method that did the failing entry check at + ceMethodAbort call return address - missOffset + and we can find the send site from the outer return address. + Invoked from a trampoline. Marked so the code generator won't delete + it. */ + + /* Cogit>>#ceSICMiss: */ +static NoDbgRegParms sqInt +ceSICMiss(sqInt receiver) +{ + sqInt cacheTag; + int errorSelectorOrNil; + sqInt extent; + usqInt innerReturn; + sqInt methodOrSelectorIndex; + sqInt newTargetMethodOrNil; + usqInt outerReturn; + CogMethod *pic; + sqInt result; + sqInt selector; + CogMethod *targetMethod; + + assert(addressCouldBeOop(receiver)); + + /* Whether we can relink to a PIC or not we need to pop off the inner return and identify the target method. */ + innerReturn = ((usqInt)(popStack())); + targetMethod = ((CogMethod *) (innerReturn - missOffset)); + if (isOopForwarded(receiver)) { + return ceSendFromInLineCacheMiss(targetMethod); + } + outerReturn = ((usqInt)(stackTop())); + assert(((outerReturn >= methodZoneBase) && (outerReturn <= (freeStart())))); + assert(((targetMethod->selector)) != (nilObject())); + selector = (targetMethod->selector); + + /* begin lookup:for:methodAndErrorSelectorInto: */ + methodOrSelectorIndex = lookupOrdinaryreceiver(selector, receiver); + if ((((usqInt)methodOrSelectorIndex)) > (maxLookupNoMNUErrorCode())) { + if (!(isOopCompiledMethod(methodOrSelectorIndex))) { + newTargetMethodOrNil = methodOrSelectorIndex; + errorSelectorOrNil = SelectorCannotInterpret; + goto l1; + } + if ((!(methodHasCogMethod(methodOrSelectorIndex))) + && (methodShouldBeCogged(methodOrSelectorIndex))) { + /* We assume cog:selector: will *not* reclaim the method zone */ + cogselector(methodOrSelectorIndex, selector); + } + newTargetMethodOrNil = methodOrSelectorIndex; + errorSelectorOrNil = null; + goto l1; + } + if (methodOrSelectorIndex == SelectorDoesNotUnderstand) { + methodOrSelectorIndex = lookupMNUreceiver(splObj(SelectorDoesNotUnderstand), receiver); + if ((((usqInt)methodOrSelectorIndex)) > (maxLookupNoMNUErrorCode())) { + assert(isOopCompiledMethod(methodOrSelectorIndex)); + if ((!(methodHasCogMethod(methodOrSelectorIndex))) + && (methodShouldBeCogged(methodOrSelectorIndex))) { + /* We assume cog:selector: will *not* reclaim the method zone */ + cogselector(methodOrSelectorIndex, splObj(SelectorDoesNotUnderstand)); + } + newTargetMethodOrNil = methodOrSelectorIndex; + errorSelectorOrNil = SelectorDoesNotUnderstand; + goto l1; + } + newTargetMethodOrNil = null; + errorSelectorOrNil = SelectorDoesNotUnderstand; + goto l1; + } + newTargetMethodOrNil = null; + errorSelectorOrNil = methodOrSelectorIndex; + /* end lookup:for:methodAndErrorSelectorInto: */ +l1: + + /* We assume lookupAndCog:for: will *not* reclaim the method zone */ + assert(outerReturn == (stackTop())); + cacheTag = inlineCacheTagForInstance(receiver); + if ((/* closedPICInappropriateForCacheTag:targetMethod:orErrorSelector: */ + ((errorSelectorOrNil) + && (errorSelectorOrNil != SelectorDoesNotUnderstand)) + || ((!newTargetMethodOrNil) + || (isYoung(newTargetMethodOrNil)))) + || ((literal32BeforeFollowingAddress(backEnd, ((usqInt)(outerReturn - 5)))) == 0 /* picAbortDiscriminatorValue */)) { + result = patchToOpenPICFornumArgsreceiver((targetMethod->selector), (targetMethod->cmNumArgs), receiver); + assert(!result); + return ceSendFromInLineCacheMiss(targetMethod); + } + + /* begin ensureWritableCodeZone */ + + /* See if an Open PIC is already available. */ + pic = openPICWithSelector((targetMethod->selector)); + if (!pic) { + /* otherwise attempt to create a closed PIC for the two cases. */ + pic = cogPICSelectornumArgsCase0MethodCase1MethodtagisMNUCase((targetMethod->selector), (targetMethod->cmNumArgs), targetMethod, newTargetMethodOrNil, cacheTag, errorSelectorOrNil == SelectorDoesNotUnderstand); + if ((((((sqInt)pic)) >= MaxNegativeErrorCode) && ((((sqInt)pic)) <= -1))) { + /* For some reason the PIC couldn't be generated, most likely a lack of code memory. + Continue as if this is an unlinked send. */ + if ((((sqInt)pic)) == InsufficientCodeSpace) { + callForCogCompiledCodeCompaction(); + } + + /* begin ensureExecutableCodeZone */ + return ceSendFromInLineCacheMiss(targetMethod); + } + } + + /* Relink the send site to the pic. If to an open PIC then reset the cache tag to the selector, + for the benefit of the cacheTag assert check in checkIfValidOopRef:pc:cogMethod: et al. */ + extent = (((pic->cmType)) == CMOpenPIC + ? rewriteInlineCacheAttagtarget(backEnd, outerReturn, inlineCacheValueForSelectorin(backEnd, (targetMethod->selector), mframeHomeMethodExport()), (((sqInt)pic)) + cmEntryOffset) + : rewriteCallAttarget(backEnd, outerReturn, (((sqInt)pic)) + cmEntryOffset)); + (((usqInt)pic)) + closedPICSize; + + /* begin assertValidDualZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + assertCoherentCodeAtdelta(backEnd, (((usqInt)pic)) + cmNoCheckEntryOffset, codeToDataDelta); +# endif + + + /* These also implicitly flush the read/write mapped dual zone to the read/execute zone. */ + flushICacheFromto(backEnd, ((usqInt)pic), (((usqInt)pic)) + closedPICSize); + flushICacheFromto(backEnd, (((usqInt)outerReturn)) - extent, ((usqInt)outerReturn)); + + /* Jump back into the pic at its entry in case this is an MNU (newTargetMethodOrNil is nil) */ + executeCogPICfromLinkedSendWithReceiverandCacheTag(pic, receiver, literal32BeforeFollowingAddress(backEnd, ((usqInt)(outerReturn - 5)))); + + /* NOTREACHED */ + return null; +} + + +/* Check for a valid object reference, if any, at a map entry. Answer a code + unique to each error for debugging. */ + + /* Cogit>>#checkIfValidOopRefAndTarget:pc:cogMethod: */ +static NoDbgRegParms sqInt +checkIfValidOopRefAndTargetpccogMethod(sqInt annotation, char *mcpc, CogMethod *cogMethod) +{ + unsigned int cacheTag1; + usqInt entryPoint; + usqInt entryPointUsqInt; + sqInt literal; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + + if (annotation == IsObjectReference) { + literal = literalBeforeFollowingAddress(backEnd, ((usqInt)mcpc)); + if (!(asserta(checkValidOopReference(literal)))) { + return 1; + } + if ((couldBeObject(literal)) + && (isReallyYoungObject(literal))) { + if (!(asserta(((((CogMethod *) cogMethod))->cmRefersToYoung)))) { + return 2; + } + } + } + if (annotation >= IsSendCall) { + if (!(asserta(isCMMethodEtAl(((CogBlockMethod *) (((CogMethod *) cogMethod))))))) { + return 3; + } + + /* begin entryCacheTagAndCouldBeObjectAt:annotation:into: */ + cacheTag1 = literal32BeforeFollowingAddress(backEnd, ((usqInt)((((sqInt)mcpc)) - 5))); + entryPointUsqInt = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + + /* in-line cache tags are the selectors of sends if sends are unlinked, + the selectors of super sends (entry offset = cmNoCheckEntryOffset), + the selectors of open PIC sends (entry offset = cmEntryOffset, target is an Open PIC) + or in-line cache tags (classes, class indices, immediate bit patterns, etc). + Note that selectors can be immediate so there is no guarantee that they + are markable/remappable objects. Writing the block invocation three times + allows Slang to inline tagCouldBeObject tests. */ + entryPoint = entryPointUsqInt; + if (entryPointTagIsSelector(entryPoint)) { + if ((((int) cacheTag1)) < 0) { + if ((-(((int) cacheTag1))) > NumSpecialSelectors) { + return 7; + } + } + else { + if (cacheTag1 >= (literalCountOf((enumeratingCogMethod->methodObject)))) { + return 8; + } + } + } + else { + if (!(asserta(validInlineCacheTag(cacheTag1)))) { + return 9; + } + } + if (entryPoint > methodZoneBase) { + /* It's a linked send; find which kind. */ + + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (!(asserta((isCMMethodEtAl(((CogBlockMethod *) targetMethod1))) + || ((isCMClosedPIC(((CogBlockMethod *) targetMethod1))) + || (isCMOpenPIC(((CogBlockMethod *) targetMethod1))))))) { + return 10; + } + } + } + return 0; +} + + +/* Check for a valid object reference, if any, at a map entry. Answer a code + unique to each error for debugging. */ + + /* Cogit>>#checkIfValidOopRef:pc:cogMethod: */ +static NoDbgRegParms sqInt +checkIfValidOopRefpccogMethod(sqInt annotation, char *mcpc, CogMethod *cogMethod) +{ + usqInt entryPoint; + sqInt literal; + usqInt offset; + sqInt offsetSqInt; + unsigned int selectorOrCacheTag; + sqInt *sendTable; + + if (annotation == IsObjectReference) { + literal = literalBeforeFollowingAddress(backEnd, ((usqInt)mcpc)); + if (!(checkValidOopReference(literal))) { + fprintf(getTranscript(), + "object ref leak in CM %p @ %p\n", + cogMethod, + mcpc); + eek(); + return 1; + } + } + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint <= methodZoneBase) { + offset = entryPoint; + } + else { + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable = superSendTrampolines; + } + } + } + offset = offsetSqInt; + } + selectorOrCacheTag = literal32BeforeFollowingAddress(backEnd, ((usqInt)((((sqInt)mcpc)) - 5))); + if ((entryPoint > methodZoneBase) + && ((offset != cmNoCheckEntryOffset) + && (!((((((CogMethod *) (entryPoint - offset)))->cmType)) == CMOpenPIC)))) { + if (!(validInlineCacheTag(selectorOrCacheTag))) { + fprintf(getTranscript(), + "cache tag leak in CM %p @ %p\n", + cogMethod, + mcpc); + eek(); + return 1; + } + } + else { + } + } + return 0; +} + + +/* Answer if all references to objects in machine-code are valid. */ + + /* Cogit>>#checkIntegrityOfObjectReferencesInCode: */ +sqInt +checkIntegrityOfObjectReferencesInCode(sqInt gcModes) +{ + CogMethod *cogMethod; + sqInt count; + sqInt ok; + + cogMethod = ((CogMethod *) methodZoneBase); + ok = 1; + while (cogMethod < (limitZony())) { + if (!(((cogMethod->cmType)) == CMFree)) { + if ((cogMethod->cmRefersToYoung)) { + if (((count = occurrencesInYoungReferrers(cogMethod))) != 1) { + fprintf(getTranscript(), + "young referrer CM %p", + cogMethod); + if (count) { + fprintf(getTranscript(), + " is in youngReferrers %d times!\n", + ((int) count)); + eek(); + } + else { + fprintf(getTranscript(), " is not in youngReferrers\n"); + eek(); + } + ok = 0; + } + } + if (!(checkValidOopReference((cogMethod->selector)))) { + fprintf(getTranscript(), + "object leak in CM %p selector\n", + cogMethod); + eek(); + ok = 0; + } + if (((cogMethod->cmType)) >= CMMethod) { + assert(((cogMethod->objectHeader)) == (nullHeaderForMachineCodeMethod())); + if (!(checkValidObjectReference((cogMethod->methodObject)))) { + fprintf(getTranscript(), + "object leak in CM %p methodObject\n", + cogMethod); + eek(); + ok = 0; + } + if (!(isOopCompiledMethod((cogMethod->methodObject)))) { + fprintf(getTranscript(), + "non-method in CM %p methodObject\n", + cogMethod); + eek(); + ok = 0; + } + if (mapForperformUntilarg(cogMethod, checkIfValidOopRefpccogMethod, cogMethod)) { + ok = 0; + } + if (((isYoungObject((cogMethod->methodObject))) + || (isYoung((cogMethod->selector)))) + && (!((cogMethod->cmRefersToYoung)))) { + fprintf(getTranscript(), + "CM %p refers to young but not marked as such\n", + cogMethod); + eek(); + ok = 0; + } + } + else { + if (((cogMethod->cmType)) == CMClosedPIC) { + if (!(checkValidObjectReferencesInClosedPIC(cogMethod))) { + ok = 0; + } + } + else { + if (((cogMethod->cmType)) == CMOpenPIC) { + if (mapForperformUntilarg(cogMethod, checkIfValidOopRefpccogMethod, cogMethod)) { + ok = 0; + } + } + } + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + return ok; +} + + /* Cogit>>#checkMaybeObjRefInClosedPIC: */ +static NoDbgRegParms sqInt +checkMaybeObjRefInClosedPIC(sqInt maybeObject) +{ + if (!maybeObject) { + return 1; + } + if (!(couldBeObject(maybeObject))) { + return 1; + } + return checkValidObjectReference(maybeObject); +} + + /* Cogit>>#checkValidObjectReferencesInClosedPIC: */ +static NoDbgRegParms sqInt +checkValidObjectReferencesInClosedPIC(CogMethod *cPIC) +{ + sqInt i; + sqInt ok; + sqInt pc; + + ok = 1; + pc = (((sqInt)cPIC)) + firstCPICCaseOffset; + + /* first we check the obj ref at the beginning of the CPIC */ + if (!(checkMaybeObjRefInClosedPIC(literalBeforeFollowingAddress(backEnd, pc - (jumpLongByteSize(backEnd)))))) { + fprintf(getTranscript(), + "object leak in CPIC %p @ %p\n", + cPIC, + ((void *)(pc - (jumpLongByteSize(backEnd))))); + ok = 0; + } + + /* Next we step over each case that is in use. We find the end address of the cPICNumCases'th case and can then just step forward by the case size thereafter */ + pc = addressOfEndOfCaseinCPIC((cPIC->cPICNumCases), cPIC); + + /* For each case we check any object reference at the end address - sizeof(conditional instruction) and then increment the end address by case size */ + for (i = 2; i <= ((cPIC->cPICNumCases)); i += 1) { + if (!(checkMaybeObjRefInClosedPIC(literalBeforeFollowingAddress(backEnd, (pc - (jumpLongConditionalByteSize(backEnd))) - (cmpC32RTempByteSize(backEnd)))))) { + fprintf(getTranscript(), + "object leak in CPIC %p @ %p\n", + cPIC, + ((void *)(pc - (jumpLongConditionalByteSize(backEnd))))); + ok = 0; + } + pc += cPICCaseSize; + } + return ok; +} + + +/* i.e. this should never be called, so keep it out of the main path. */ + + /* Cogit>>#cleanUpFailingCogCodeConstituents: */ +static NoDbgRegParms NeverInline sqInt +cleanUpFailingCogCodeConstituents(CogMethod *cogMethodArg) +{ + CogMethod *cogMethod; + + cogMethod = cogMethodArg; + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) == CMClosedPIC) { + ((((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)))->methodObject = 0); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + + /* would like to assert this, but it requires the leak checked be run :-( + self assert: self allMachineCodeObjectReferencesValid. */ + popRemappableOop(); + return null; +} + + +/* Answer if the ClosedPIC refers to any unmarked objects or freed/freeable + target methods, + applying markAndTraceOrFreeCogMethod:firstVisit: to those targets to + determine if freed/freeable. + */ + + /* Cogit>>#closedPICRefersToUnmarkedObject: */ +static NoDbgRegParms sqInt +closedPICRefersToUnmarkedObject(CogMethod *cPIC) +{ + sqInt i; + sqInt object; + sqInt pc; + + if (!((isImmediate((cPIC->selector))) + || (isMarked((cPIC->selector))))) { + return 1; + } + + /* First jump is unconditional; subsequent ones are conditional. + Check the potential method oop for the first case only. + Inline cache tags for the 1st case are at the send site. */ + pc = addressOfEndOfCaseinCPIC(1, cPIC); + if (couldBeObject((object = literalBeforeFollowingAddress(backEnd, pc - (jumpLongByteSize(backEnd)))))) { + if (!(isMarked(object))) { + return 1; + } + } + + /* Check the first target */ + if (markAndTraceOrFreePICTargetin(jumpLongTargetBeforeFollowingAddress(backEnd, pc), cPIC)) { + return 1; + } + for (i = 2; i <= ((cPIC->cPICNumCases)); i += 1) { + pc = addressOfEndOfCaseinCPIC(i, cPIC); + + /* Check the potential method oop for subsequent cases. */ + if (couldBeObject((object = literalBeforeFollowingAddress(backEnd, (pc - (jumpLongConditionalByteSize(backEnd))) - (cmpC32RTempByteSize(backEnd)))))) { + if (!(isMarked(object))) { + return 1; + } + } + + /* Check subsequent targets */ + if (markAndTraceOrFreePICTargetin(jumpLongTargetBeforeFollowingAddress(backEnd, pc), cPIC)) { + return 1; + } + } + return 0; +} + + /* Cogit>>#codeEntryFor: */ +char * +codeEntryFor(char *address) +{ + char *end; + sqInt i; + char *name; + char *start; + + /* begin trampolineRangesDo: */ + for (i = 0; i <= (trampolineTableIndex - 3); i += 2) { + name = trampolineAddresses[i]; + start = trampolineAddresses[i + 1]; + end = (trampolineAddresses[i + 3]) - 1; + if (((address >= start) && (address <= end))) { + return start; + } + } + return null; +} + + /* Cogit>>#codeEntryNameFor: */ +char * +codeEntryNameFor(char *address) +{ + char *end; + sqInt i; + char *name; + char *start; + + /* begin trampolineRangesDo: */ + for (i = 0; i <= (trampolineTableIndex - 3); i += 2) { + name = trampolineAddresses[i]; + start = trampolineAddresses[i + 1]; + end = (trampolineAddresses[i + 3]) - 1; + if (((address >= start) && (address <= end))) { + return name; + } + } + return null; +} + + +/* used e.g. in the platform's backtrace generators. Declared api to place it + in cogit.h + */ + + /* Cogit>>#cogCodeBase */ +usqInt +cogCodeBase(void) +{ + return codeBase; +} + + +/* Answer the contents of the code zone as an array of pair-wise element, + address in ascending address order. + Answer a string for a runtime routine or abstract label (beginning, end, + etc), a CompiledMethod for a CMMethod, + or a selector (presumably a Symbol) for a PIC. + If withDetails is true + - answer machine-code to bytecode pc mapping information for methods + - answer class, target pair information for closed PIC */ + + /* Cogit>>#cogCodeConstituents: */ +sqInt +cogCodeConstituents(sqInt withDetails) +{ + sqInt constituents; + + /* begin ensureWritableCodeZone */ + constituents = getCogCodeConstituents(withDetails); + + /* begin ensureExecutableCodeZone */ + return constituents; +} + + +/* Extend the cPIC with the supplied case. If caseNMethod is cogged dispatch + direct to + its unchecked entry-point. If caseNMethod is not cogged, jump to the fast + interpreter dispatch, and if isMNUCase then dispatch to fast MNU + invocation and mark the cPIC as + having the MNU case for cache flushing. */ + + /* Cogit>>#cogExtendPIC:CaseNMethod:tag:isMNUCase: */ +static NoDbgRegParms void +cogExtendPICCaseNMethodtagisMNUCase(CogMethod *cPIC, sqInt caseNMethod, sqInt caseNTag, sqInt isMNUCase) +{ + sqInt address; + sqInt operand; + usqIntptr_t target; + sqInt targetSqInt; + + compilationBreakpointclassTagisMNUCase((cPIC->selector), caseNTag, isMNUCase); + assert(!(inlineCacheTagIsYoung(caseNTag))); + + /* Caller patches to open pic if caseNMethod is young. */ + assert((caseNMethod) + && (!(isYoung(caseNMethod)))); + if ((!isMNUCase) + && (methodHasCogMethod(caseNMethod))) { + operand = 0; + target = (((sqInt)(cogMethodOf(caseNMethod)))) + cmNoCheckEntryOffset; + } + else { + operand = caseNMethod; + if (isMNUCase) { + /* cpicHasMNUCase: */ + ((((CogMethod *) ((((usqInt)cPIC)) + codeToDataDelta)))->cpicHasMNUCaseOrCMIsFullBlock) = 1; + target = (((sqInt)cPIC)) + (sizeof(CogMethod)); + } + else { + target = (((sqInt)cPIC)) + (picInterpretAbortOffset()); + } + } + + /* this isn't an MNU and we have an already cogged method to jump to + find the end address of the new case */ + address = addressOfEndOfCaseinCPIC(((cPIC->cPICNumCases)) + 1, cPIC); + rewriteCPICCaseAttagobjReftarget(address, caseNTag, operand, target); + + /* finally, rewrite the jump 3 instr before firstCPICCaseOffset to jump to the beginning of this new case */ + targetSqInt = address - cPICCaseSize; + + /* begin rewriteCPIC:caseJumpTo: */ + rewriteCPICJumpAttarget(backEnd, (((((sqInt)cPIC)) + firstCPICCaseOffset) - (jumpLongByteSize(backEnd))) - 11, targetSqInt); + + /* and update the header flag for the number of cases */ + ((((CogMethod *) ((((usqInt)cPIC)) + codeToDataDelta)))->cPICNumCases = ((cPIC->cPICNumCases)) + 1); + + /* This also implicitly flushes the read/write mapped dual zone to the read/execute zone. */ + flushICacheFromto(backEnd, ((usqInt)cPIC), (((usqInt)cPIC)) + closedPICSize); + (((usqInt)cPIC)) + closedPICSize; + + /* begin assertValidDualZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + assertCoherentCodeAtdelta(backEnd, (((usqInt)cPIC)) + cmNoCheckEntryOffset, codeToDataDelta); +# endif +} + + +/* Attempt to produce a machine code method for the bytecode method + object aMethodObj. N.B. If there is no code memory available do *NOT* + attempt to reclaim the method zone. Certain clients (e.g. ceSICMiss:) + depend on the zone remaining constant across method generation. */ + + /* Cogit>>#cogFullBlockMethod:numCopied: */ +CogMethod * +cogFullBlockMethodnumCopied(sqInt aMethodObj, sqInt numCopied) +{ + CogMethod *cogMethod; + + assert(!((methodHasCogMethod(aMethodObj)))); + if (aMethodObj == breakMethod) { + haltmsg("Compilation of breakMethod"); + } + ensureNoForwardedLiteralsIn(aMethodObj); + + /* If the generators for the alternate bytecode set are missing then interpret. */ + if (methodUsesAlternateBytecodeSet(aMethodObj)) { + if ((numElementsIn(generatorTable)) <= 0x100) { + return null; + } + bytecodeSetOffset = 0x100; + } + else { + bytecodeSetOffset = 0; + } + assert(isFullBlockMethod(aMethodObj)); + methodObj = aMethodObj; + methodHeader = methodHeaderOf(aMethodObj); + + /* lazy initialization */ + receiverTags = -1; + cogMethod = compileCogFullBlockMethod(numCopied); + if ((((((sqInt)cogMethod)) >= MaxNegativeErrorCode) && ((((sqInt)cogMethod)) <= -1))) { + if ((((sqInt)cogMethod)) == InsufficientCodeSpace) { + callForCogCompiledCodeCompaction(); + } + + /* Right now no errors should be reported, so nothing more to do. + self reportError: (self cCoerceSimple: cogMethod to: #sqInt). */ + return null; + } + + /* coInterpreter printCogMethod: cogMethod. + coInterpreter symbolicMethod: aMethodObj. + self assertValidMethodMap: cogMethod. + self disassembler disassembleMethod: cogMethod. + printInstructions := clickConfirm := true */ + return cogMethod; +} + + /* Cogit>>#cogitPostGCAction: */ +void +cogitPostGCAction(sqInt gcMode) +{ + + /* Post-GC update every full method's objectHeader to whatever it needs to be */ + assert(allMethodsHaveCorrectHeader()); + + /* The youngReferrers should be correct after a GC since that is the point at which it is + pruned. But at other times false positives or free methods on the list are acceptable. */ + assert(((!(gcMode & (GCModeFull + GCModeNewSpace)))) + || (kosherYoungReferrers())); + + /* begin ensureExecutableCodeZone */ +} + + +/* Check that the header fields onf a non-free method are consistent with + the type. Answer 0 if it is ok, otherwise answer a code for the error. */ + + /* Cogit>>#cogMethodDoesntLookKosher: */ +static NoDbgRegParms sqInt +cogMethodDoesntLookKosher(CogMethod *cogMethod) +{ + if (((((cogMethod->blockSize)) & (BytesPerWord - 1)) != 0) + || ((((cogMethod->blockSize)) < (sizeof(CogMethod))) + || (((cogMethod->blockSize)) >= 0x8000))) { + return 1; + } + if (((cogMethod->cmType)) == CMFree) { + return 2; + } + if (((cogMethod->cmType)) >= CMMethod) { + if (!((((((cogMethod->methodHeader))) & 7) == 1))) { + return 11; + } + if (!(couldBeObject((cogMethod->methodObject)))) { + return 12; + } + if ((((cogMethod->stackCheckOffset)) > 0) + && (((cogMethod->stackCheckOffset)) < cmNoCheckEntryOffset)) { + return 13; + } + return 0; + } + if (((cogMethod->cmType)) == CMOpenPIC) { + if (((cogMethod->blockSize)) != openPICSize) { + return 21; + } + if ((cogMethod->methodHeader)) { + return 22; + } + + /* Check the nextOpenPIC link unless we're compacting */ + if (((cogMethod->objectHeader)) >= 0) { + if (!((((cogMethod->methodObject)) == 0) + || (compactionInProgress + || (((cogMethod->methodObject)) == (((usqInt)(methodFor(((void *)((cogMethod->methodObject))))))))))) { + return 23; + } + } + if ((cogMethod->stackCheckOffset)) { + return 24; + } + return 0; + } + if (((cogMethod->cmType)) == CMClosedPIC) { + if (((cogMethod->blockSize)) != closedPICSize) { + return 0x1F; + } + if (!(((((cogMethod->cPICNumCases)) >= 1) && (((cogMethod->cPICNumCases)) <= MaxCPICCases)))) { + return 32; + } + if ((cogMethod->methodHeader)) { + return 33; + } + if ((cogMethod->methodObject)) { + return 34; + } + return 0; + } + return 9; +} + + +/* Attempt to create a one-case PIC for an MNU. + The tag for the case is at the send site and so doesn't need to be + generated. + */ + + /* Cogit>>#cogMNUPICSelector:receiver:methodOperand:numArgs: */ +CogMethod * +cogMNUPICSelectorreceivermethodOperandnumArgs(sqInt selector, sqInt rcvr, sqInt methodOperand, sqInt numArgs) +{ + CogMethod *actualPIC; + usqInt startAddress; + CogMethod *writablePIC; + + if ((isYoung(selector)) + || ((inlineCacheTagForInstance(rcvr)) == 0 /* picAbortDiscriminatorValue */)) { + return 0; + } + compilationBreakpointclassTagisMNUCase(selector, fetchClassTagOf(rcvr), 1); + assert(endCPICCase0); + + /* get memory in the code zone for the CPIC; if that fails we return an error code for the sender to use to work out how to blow up */ + startAddress = allocate(closedPICSize); + if (!startAddress) { + callForCogCompiledCodeCompaction(); + return 0; + } + maybeBreakGeneratingFromto(startAddress, startAddress + closedPICSize); + + /* begin ensureWritableCodeZone */ + writablePIC = ((CogMethod *) ((((usqInt)startAddress)) + codeToDataDelta)); + + /* memcpy the prototype across to our allocated space; because anything else would be silly */ + codeMemcpy(writablePIC, cPICPrototype, closedPICSize); + + /* begin fillInCPICHeader:numArgs:numCases:hasMNUCase:selector: */ + assert(!(isYoung(selector))); + (writablePIC->cmType = CMClosedPIC); + (writablePIC->objectHeader = 0); + (writablePIC->blockSize = closedPICSize); + (writablePIC->methodObject = 0); + (writablePIC->methodHeader = 0); + (writablePIC->selector = selector); + (writablePIC->cmNumArgs = numArgs); + (writablePIC->cmHasMovableLiteral = 0); + (writablePIC->cmRefersToYoung = 0); + (writablePIC->cmUsageCount = CMMaxUsageCount / 2); + + /* cpicHasMNUCase: */ + (writablePIC->cpicHasMNUCaseOrCMIsFullBlock) = 1; + (writablePIC->cPICNumCases = 1); + (writablePIC->blockEntryOffset = 0); + assert(isCMClosedPIC(((CogBlockMethod *) writablePIC))); + assert(((writablePIC->selector)) == selector); + assert(((writablePIC->cmNumArgs)) == numArgs); + assert(((writablePIC->cPICNumCases)) == 1); + assert(closedPICSize == (roundUpLength(closedPICSize))); + configureMNUCPICmethodOperandnumArgsdelta((actualPIC = ((CogMethod *) startAddress)), methodOperand, numArgs, startAddress - (((usqInt)cPICPrototype))); + + /* This also implicitly flushes the read/write mapped dual zone to the read/execute zone. */ + flushICacheFromto(backEnd, startAddress, startAddress + closedPICSize); + assert((callTargetFromReturnAddress(backEnd, startAddress + missOffset)) == (picAbortTrampolineFor(numArgs))); + + /* begin assertValidDualZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + assertCoherentCodeAtdelta(backEnd, startAddress + cmNoCheckEntryOffset, codeToDataDelta); +# endif + + return actualPIC; +} + + +/* Create an Open PIC. Temporarily create a direct call of + ceSendFromOpenPIC:. Should become a probe of the first-level method lookup + cache followed by a + call of ceSendFromOpenPIC: if the probe fails. */ + + /* Cogit>>#cogOpenPICSelector:numArgs: */ +static NoDbgRegParms CogMethod * +cogOpenPICSelectornumArgs(sqInt selector, sqInt numArgs) +{ + sqInt codeSize; + sqInt end; + usqIntptr_t fixupSize; + sqInt mapSize; + usqIntptr_t opcodeSize; + CogMethod *pic; + usqInt startAddress; + + compilationBreakpointisMNUCase(selector, 0); + startAddress = allocate(openPICSize); + if (!startAddress) { + return ((CogMethod *) InsufficientCodeSpace); + } + (methodLabel->address = startAddress); + (methodLabel->dependent = null); + + /* stack allocate the various collections so that they + are effectively garbage collected on return. */ + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 100; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + compileOpenPICnumArgs(selector, numArgs); + computeMaximumSizes(); + concretizeAt(methodLabel, startAddress); + codeSize = generateInstructionsAt(startAddress + (sizeof(CogMethod))); + + /* begin ensureWritableCodeZone */ + mapSize = generateMapAtstart((startAddress + openPICSize) - 1, startAddress + cmNoCheckEntryOffset); + assert((((entry->address)) - startAddress) == cmEntryOffset); + assert(((roundUpLength((sizeof(CogMethod)) + codeSize)) + (roundUpLength(mapSize))) <= openPICSize); + end = outputInstructionsAt(startAddress + (sizeof(CogMethod))); + pic = ((CogMethod *) ((((usqInt)startAddress)) + codeToDataDelta)); + + /* begin fillInOPICHeader:numArgs:selector: */ + (pic->cmType = CMOpenPIC); + (pic->objectHeader = 0); + (pic->blockSize = openPICSize); + + /* pic methodObject: 0. + This is also the nextOpenPIC link so don't initialize it */ + addToOpenPICList(pic); + (pic->methodHeader = 0); + (pic->selector = selector); + (pic->cmNumArgs = numArgs); + (pic->cmHasMovableLiteral = isNonImmediate(selector)); + if ((pic->cmRefersToYoung = isYoung(selector))) { + addToYoungReferrers(pic); + } + (pic->cmUsageCount = initialOpenPICUsageCount()); + + /* cpicHasMNUCase: */ + (pic->cpicHasMNUCaseOrCMIsFullBlock) = 0; + (pic->cPICNumCases = 0); + (pic->blockEntryOffset = 0); + + /* This also implicitly flushes the read/write mapped dual zone to the read/execute zone. */ + flushICacheFromto(backEnd, (((usqInt)pic)) - codeToDataDelta, ((((usqInt)pic)) - codeToDataDelta) + openPICSize); + assert(isCMOpenPIC(((CogBlockMethod *) pic))); + assert(((pic->selector)) == selector); + assert(((pic->cmNumArgs)) == numArgs); + assert((callTargetFromReturnAddress(backEnd, ((((sqInt)pic)) - codeToDataDelta) + missOffset)) == (picAbortTrampolineFor(numArgs))); + assert(openPICSize == (roundUpLength(openPICSize))); + ((((usqInt)pic)) - codeToDataDelta) + openPICSize; + + /* begin assertValidDualZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + assertCoherentCodeAtdelta(backEnd, ((((usqInt)pic)) - codeToDataDelta) + cmNoCheckEntryOffset, codeToDataDelta); +# endif + + return ((CogMethod *) startAddress); +} + + +/* Attempt to create a two-case PIC for case0CogMethod and + case1Method,case1Tag. The tag for case0CogMethod is at the send site and + so doesn't need to be generated. + case1Method may be any of + - a Cog method; link to its unchecked entry-point + - a CompiledMethod; link to ceInterpretMethodFromPIC: + - a CompiledMethod; link to ceMNUFromPICMNUMethod:receiver: */ + + /* Cogit>>#cogPICSelector:numArgs:Case0Method:Case1Method:tag:isMNUCase: */ +static NoDbgRegParms CogMethod * +cogPICSelectornumArgsCase0MethodCase1MethodtagisMNUCase(sqInt selector, sqInt numArgs, CogMethod *case0CogMethod, sqInt case1MethodOrNil, sqInt case1Tag, sqInt isMNUCase) +{ + CogMethod *actualPIC; + usqInt startAddress; + CogMethod *writablePIC; + + if (isYoung(selector)) { + return ((CogMethod *) YoungSelectorInPIC); + } + compilationBreakpointclassTagisMNUCase(selector, case1Tag, isMNUCase); + + /* get memory in the code zone for the CPIC; if that fails we return an error code for the sender to use to work out how to blow up */ + startAddress = allocate(closedPICSize); + if (!startAddress) { + return ((CogMethod *) InsufficientCodeSpace); + } + maybeBreakGeneratingFromto(startAddress, startAddress + closedPICSize); + writablePIC = ((CogMethod *) ((((usqInt)startAddress)) + codeToDataDelta)); + + /* memcpy the prototype across to our allocated space; because anything else would be silly */ + codeMemcpy(writablePIC, cPICPrototype, closedPICSize); + + /* begin fillInCPICHeader:numArgs:numCases:hasMNUCase:selector: */ + assert(!(isYoung(selector))); + (writablePIC->cmType = CMClosedPIC); + (writablePIC->objectHeader = 0); + (writablePIC->blockSize = closedPICSize); + (writablePIC->methodObject = 0); + (writablePIC->methodHeader = 0); + (writablePIC->selector = selector); + (writablePIC->cmNumArgs = numArgs); + (writablePIC->cmHasMovableLiteral = 0); + (writablePIC->cmRefersToYoung = 0); + (writablePIC->cmUsageCount = CMMaxUsageCount / 2); + + /* cpicHasMNUCase: */ + (writablePIC->cpicHasMNUCaseOrCMIsFullBlock) = isMNUCase; + (writablePIC->cPICNumCases = 2); + (writablePIC->blockEntryOffset = 0); + assert(isCMClosedPIC(((CogBlockMethod *) writablePIC))); + assert(((writablePIC->selector)) == selector); + assert(((writablePIC->cmNumArgs)) == numArgs); + assert(((writablePIC->cPICNumCases)) == 2); + assert(closedPICSize == (roundUpLength(closedPICSize))); + configureCPICCase0Case1MethodtagisMNUCasenumArgsdelta((actualPIC = ((CogMethod *) startAddress)), case0CogMethod, case1MethodOrNil, case1Tag, isMNUCase, numArgs, startAddress - (((usqInt)cPICPrototype))); + assert((callTargetFromReturnAddress(backEnd, startAddress + missOffset)) == (picAbortTrampolineFor(numArgs))); + return actualPIC; +} + + +/* Attempt to produce a machine code method for the bytecode method + object aMethodObj. N.B. If there is no code memory available do *NOT* + attempt to reclaim the method zone. Certain clients (e.g. ceSICMiss:) + depend on the zone remaining constant across method generation. */ + + /* Cogit>>#cog:selector: */ +CogMethod * +cogselector(sqInt aMethodObj, sqInt aSelectorOop) +{ + CogMethod *cogMethod; + sqInt selector; + + + /* In Newspeak we support anonymous accessors and hence tolerate the same + method being cogged multiple times. But only if the method class association is nil. */ + assert(!((methodHasCogMethod(aMethodObj)))); + selector = (aSelectorOop == (nilObject()) + ? maybeSelectorOfMethod(aMethodObj) + : aSelectorOop); + + /* coInterpreter stringOf: selector */ + if (selector) { + compilationBreakpointisMNUCase(selector, 0); + } + if (aMethodObj == breakMethod) { + haltmsg("Compilation of breakMethod"); + } + ensureNoForwardedLiteralsIn(aMethodObj); + + /* If the generators for the alternate bytecode set are missing then interpret. */ + if (methodUsesAlternateBytecodeSet(aMethodObj)) { + if ((numElementsIn(generatorTable)) <= 0x100) { + return null; + } + bytecodeSetOffset = 0x100; + } + else { + bytecodeSetOffset = 0; + } + assert(!((isFullBlockMethod(aMethodObj)))); + methodObj = aMethodObj; + methodHeader = methodHeaderOf(aMethodObj); + + /* lazy initialization */ + receiverTags = -1; + cogMethod = compileCogMethod(aSelectorOop); + if ((((((sqInt)cogMethod)) >= MaxNegativeErrorCode) && ((((sqInt)cogMethod)) <= -1))) { + if ((((sqInt)cogMethod)) == InsufficientCodeSpace) { + callForCogCompiledCodeCompaction(); + } + + /* Right now no errors should be reported, so nothing more to do. + self reportError: (self cCoerceSimple: cogMethod to: #sqInt). */ + return null; + } + + /* coInterpreter printCogMethod: cogMethod. + coInterpreter symbolicMethod: aMethodObj. + self assertValidMethodMap: cogMethod. + self disassembler disassembleMethod: cogMethod. + printInstructions := clickConfirm := true */ + return cogMethod; +} + + /* Cogit>>#collectCogConstituentFor:Annotation:Mcpc:Bcpc:Method: */ +static NoDbgRegParms sqInt +collectCogConstituentForAnnotationMcpcBcpcMethod(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *cogMethodArg) +{ + sqInt address; + sqInt annotation; + usqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + + if (!descriptor) { + return 0; + } + if (!((descriptor->isMapped))) { + return 0; + } + address = positive64BitIntegerFor(((usqInt)mcpc)); + if (!address) { + return PrimErrNoMemory; + } + storePointerUncheckedofObjectwithValue(cogConstituentIndex, topRemappableOop(), address); + storePointerUncheckedofObjectwithValue(cogConstituentIndex + 1, topRemappableOop(), (((usqInt)bcpc << 3) | 1)); + cogConstituentIndex += 2; + + /* Collect any first case classTags for closed PICs. */ + if (((!(isBackwardBranchAndAnnotation & 1))) + && (/* isSendAnnotation: */ + ((((usqInt)(isBackwardBranchAndAnnotation)) >> 1) >= IsSendCall))) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + annotation = ((usqInt)(isBackwardBranchAndAnnotation)) >> 1; + + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (((targetMethod1->cmType)) == CMClosedPIC) { + ((((CogMethod *) ((((usqInt)targetMethod1)) + codeToDataDelta)))->methodObject = classForInlineCacheTag(literal32BeforeFollowingAddress(backEnd, ((usqInt)((((sqInt)mcpc)) - 5))))); + } + } + } + return 0; +} + + /* Cogit>>#collectCogConstituentOnSpurFor:Annotation:Mcpc:Bcpc:Data: */ +static NoDbgRegParms sqInt +collectCogConstituentOnSpurForAnnotationMcpcBcpcData(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *dataArg) +{ + sqInt annotation; + usqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + + if (!descriptor) { + return 0; + } + if (!((descriptor->isMapped))) { + return 0; + } + longAtput(((0 + cogConstituentIndex) * BytesPerWord) + (((char *)dataArg)),((usqInt)mcpc)); + longAtput(((1 + cogConstituentIndex) * BytesPerWord) + (((char *)dataArg)),bcpc); + cogConstituentIndex += 2; + + /* Collect any first case classTags for closed PICs. */ + if (((!(isBackwardBranchAndAnnotation & 1))) + && (/* isSendAnnotation: */ + ((((usqInt)(isBackwardBranchAndAnnotation)) >> 1) >= IsSendCall))) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + annotation = ((usqInt)(isBackwardBranchAndAnnotation)) >> 1; + + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (((targetMethod1->cmType)) == CMClosedPIC) { + ((((CogMethod *) ((((usqInt)targetMethod1)) + codeToDataDelta)))->methodObject = classForInlineCacheTag(literal32BeforeFollowingAddress(backEnd, ((usqInt)((((sqInt)mcpc)) - 5))))); + } + } + } + return 0; +} + + +/* Answer a description of the mapping between machine code pointers and + bytecode pointers for the Cog Method. + First value is the address of the cog method. + Following values are pairs of machine code pc and bytecode pc. + dataClass is DoubleWordArray or WordArray as appropriate. */ + + /* Cogit>>#collectCogMethodConstituentOnSpur:ofClass: */ +static NoDbgRegParms sqInt +collectCogMethodConstituentOnSpurofClass(CogMethod *cogMethod, sqInt dataClass) +{ + sqInt aMethodObj; + sqInt annotation; + void *arg; + sqInt bcpc; + sqInt bsOffset; + sqInt byte; + CogBlockMethod *cogBlockMethod; + sqInt data; + BytecodeDescriptor *descriptor; + sqInt distance; + sqInt endbcpc; + sqInt errCode; + CogMethod *homeMethod; + sqInt isBackwardBranch; + sqInt isInBlock; + sqInt latestContinuation; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt nExts; + sqInt nextBcpc; + sqInt nSlots; + sqInt result; + sqInt startbcpc; + sqInt targetPC; + + if (!(((cogMethod->cmType)) >= CMMethod)) { + return positive64BitIntegerFor(((usqInt)cogMethod)); + } + cogBlockMethod = ((CogBlockMethod *) cogMethod); + if (!((cogBlockMethod->stackCheckOffset))) { + return positive64BitIntegerFor(((usqInt)cogMethod)); + } + + /* isFrameless ? */ + + /* +1 for first address */ + nSlots = ((((byteSizeOf((cogMethod->methodObject))) - (startPCOfMethodHeader((cogMethod->methodHeader)))) * 2) + (minSlotsForShortening())) + 1; + data = instantiateClassindexableSize(dataClass, nSlots); + if (!data) { + return null; + } + longAtput(firstIndexableField(data),((usqInt)cogMethod)); + cogConstituentIndex = 1; + startbcpc = startPCOfMethod((cogMethod->methodObject)); + arg = firstIndexableField(data); + + /* begin mapFor:bcpc:performUntil:arg: */ + descriptor = ((BytecodeDescriptor *) 0); + latestContinuation = 0; + mapByte = 0; + nextBcpc = 0; + assert(((cogBlockMethod->stackCheckOffset)) > 0); + mcpc = (((usqInt)cogBlockMethod)) + ((cogBlockMethod->stackCheckOffset)); + + /* The stack check maps to the start of the first bytecode, + the first bytecode being effectively after frame build. */ + result = collectCogConstituentOnSpurForAnnotationMcpcBcpcData(null, 0 + ((((usqInt)(HasBytecodePC) << 1))), ((char *) mcpc), startbcpc, arg); + if (result) { + errCode = result; + goto l1; + } + bcpc = startbcpc; + + /* In both CMMethod and CMBlock cases find the start of the map and + skip forward to the bytecode pc map entry for the stack check. */ + if (((cogBlockMethod->cmType)) >= CMMethod) { + isInBlock = (cogBlockMethod->cpicHasMNUCaseOrCMIsFullBlock); + homeMethod = ((CogMethod *) cogBlockMethod); + assert(startbcpc == (startPCOfMethodHeader((homeMethod->methodHeader)))); + map = ((((usqInt)homeMethod)) + ((homeMethod->blockSize))) - 1; + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert((annotation == IsAbsPCReference) + || ((annotation == IsObjectReference) + || ((annotation == IsRelativeCall) + || (annotation == IsDisplacementX2N)))); + latestContinuation = startbcpc; + aMethodObj = (homeMethod->methodObject); + endbcpc = (numBytesOf(aMethodObj)) - 1; + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + + /* If the method has a primitive, skip it and the error code store, if any; + Logically. these come before the stack check and so must be ignored. */ + bcpc += deltaToSkipPrimAndErrorStoreInheader(aMethodObj, (homeMethod->methodHeader)); + } + else { + isInBlock = 1; + assert(bcpc == ((cogBlockMethod->startpc))); + homeMethod = cmHomeMethod(cogBlockMethod); + map = findMapLocationForMcpcinMethod((((usqInt)cogBlockMethod)) + (sizeof(CogBlockMethod)), homeMethod); + assert(map != 0); + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert(((((usqInt)(annotation)) >> AnnotationShift) == HasBytecodePC) + || ((((usqInt)(annotation)) >> AnnotationShift) == IsDisplacementX2N)); + + /* fiducial */ + while (((annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift)) != HasBytecodePC) { + map -= 1; + } + + /* skip fiducial; i.e. the map entry for the pc immediately following the method header. */ + map -= 1; + aMethodObj = (homeMethod->methodObject); + bcpc = startbcpc - (/* blockCreationBytecodeSizeForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? AltBlockCreationBytecodeSize + : BlockCreationBytecodeSize)); + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + endbcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, -1, aMethodObj) + : 0)); + bcpc = startbcpc; + } + nExts = 0; + enumeratingCogMethod = homeMethod; + + /* Now skip up through the bytecode pc map entry for the stack check. */ + while ((((usqInt)((byteAt((void *)(map))))) >> AnnotationShift) != HasBytecodePC) { + map -= 1; + } + map -= 1; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + mcpc += (mapByte & DisplacementMask); + if (annotation >= HasBytecodePC) { + if ((annotation == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + while (1) { + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + if (isInBlock) { + if (bcpc >= endbcpc) { + errCode = 0; + goto l1; + } + } + else { + if (((descriptor->isReturn)) + && (bcpc >= latestContinuation)) { + errCode = 0; + goto l1; + } + if ((isBranch(descriptor)) + || ((descriptor->isBlockCreation))) { + /* begin latestContinuationPCFor:at:exts:in: */ + distance = ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj); + targetPC = (bcpc + ((descriptor->numBytes))) + (((distance < 0) ? 0 : distance)); + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + } + } + nextBcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj) + : 0)); + if (((descriptor->isMapped)) + || (isInBlock + && ((descriptor->isMappedInBlock)))) break; + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + isBackwardBranch = (isBranch(descriptor)) + && ((/* begin isBackwardBranch:at:exts:in: */ + assert(((descriptor->spanFunction))), + (((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj)) < 0)); + result = collectCogConstituentOnSpurForAnnotationMcpcBcpcData(descriptor, (isBackwardBranch + ? ((((usqInt)(annotation) << 1))) + 1 + : ((sqInt)((usqInt)(annotation) << 1))), ((char *) mcpc), (isBackwardBranch + ? bcpc - (2 * nExts) + : bcpc), arg); + if (result) { + errCode = result; + goto l1; + } + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + } + else { + assert(((((usqInt)(mapByte)) >> AnnotationShift) == IsDisplacementX2N) + || ((((usqInt)(mapByte)) >> AnnotationShift) == IsAnnotationExtension)); + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + + /* defensive; we exit on bcpc */ + errCode = 0; + /* end mapFor:bcpc:performUntil:arg: */ +l1: + if (errCode) { + return null; + } + if (cogConstituentIndex < nSlots) { + shortentoIndexableSize(data, cogConstituentIndex); + } + return data; +} + + +/* Answer a description of the mapping between machine code pointers and + bytecode pointers for the Cog Method. + First value is the address of the cog method. + Following values are pairs of machine code pc and bytecode pc */ + + /* Cogit>>#collectCogMethodConstituent: */ +static NoDbgRegParms sqInt +collectCogMethodConstituent(CogMethod *cogMethod) +{ + sqInt address; + sqInt aMethodObj; + sqInt annotation; + sqInt bcpc; + sqInt bsOffset; + sqInt byte; + CogBlockMethod *cogBlockMethod; + sqInt data; + BytecodeDescriptor *descriptor; + sqInt distance; + sqInt endbcpc; + sqInt errCode; + CogMethod *homeMethod; + sqInt isBackwardBranch; + sqInt isInBlock; + sqInt latestContinuation; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt nExts; + sqInt nextBcpc; + sqInt nSlots; + sqInt result; + sqInt startbcpc; + sqInt targetPC; + + if (!(((cogMethod->cmType)) >= CMMethod)) { + return positive64BitIntegerFor(((usqInt)cogMethod)); + } + cogBlockMethod = ((CogBlockMethod *) cogMethod); + if (!((cogBlockMethod->stackCheckOffset))) { + return positive64BitIntegerFor(((usqInt)cogMethod)); + } + + /* isFrameless ? */ + + /* +1 for first address */ + nSlots = ((((byteSizeOf((cogMethod->methodObject))) - (startPCOfMethodHeader((cogMethod->methodHeader)))) * 2) + (minSlotsForShortening())) + 1; + data = instantiateClassindexableSize(splObj(ClassArray), nSlots); + if (!data) { + return null; + } + pushRemappableOop(data); + + /* The iteration assumes the object is the top remappable oop */ + address = positive64BitIntegerFor(((usqInt)cogMethod)); + if (!address) { + popRemappableOop(); + return null; + } + storePointerUncheckedofObjectwithValue(0, topRemappableOop(), address); + cogConstituentIndex = 1; + startbcpc = startPCOfMethod((cogMethod->methodObject)); + + /* begin mapFor:bcpc:performUntil:arg: */ + descriptor = ((BytecodeDescriptor *) 0); + latestContinuation = 0; + mapByte = 0; + nextBcpc = 0; + assert(((cogBlockMethod->stackCheckOffset)) > 0); + mcpc = (((usqInt)cogBlockMethod)) + ((cogBlockMethod->stackCheckOffset)); + + /* The stack check maps to the start of the first bytecode, + the first bytecode being effectively after frame build. */ + result = collectCogConstituentForAnnotationMcpcBcpcMethod(null, 0 + ((((usqInt)(HasBytecodePC) << 1))), ((char *) mcpc), startbcpc, ((void *)cogMethod)); + if (result) { + errCode = result; + goto l1; + } + bcpc = startbcpc; + + /* In both CMMethod and CMBlock cases find the start of the map and + skip forward to the bytecode pc map entry for the stack check. */ + if (((cogBlockMethod->cmType)) >= CMMethod) { + isInBlock = (cogBlockMethod->cpicHasMNUCaseOrCMIsFullBlock); + homeMethod = ((CogMethod *) cogBlockMethod); + assert(startbcpc == (startPCOfMethodHeader((homeMethod->methodHeader)))); + map = ((((usqInt)homeMethod)) + ((homeMethod->blockSize))) - 1; + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert((annotation == IsAbsPCReference) + || ((annotation == IsObjectReference) + || ((annotation == IsRelativeCall) + || (annotation == IsDisplacementX2N)))); + latestContinuation = startbcpc; + aMethodObj = (homeMethod->methodObject); + endbcpc = (numBytesOf(aMethodObj)) - 1; + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + + /* If the method has a primitive, skip it and the error code store, if any; + Logically. these come before the stack check and so must be ignored. */ + bcpc += deltaToSkipPrimAndErrorStoreInheader(aMethodObj, (homeMethod->methodHeader)); + } + else { + isInBlock = 1; + assert(bcpc == ((cogBlockMethod->startpc))); + homeMethod = cmHomeMethod(cogBlockMethod); + map = findMapLocationForMcpcinMethod((((usqInt)cogBlockMethod)) + (sizeof(CogBlockMethod)), homeMethod); + assert(map != 0); + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert(((((usqInt)(annotation)) >> AnnotationShift) == HasBytecodePC) + || ((((usqInt)(annotation)) >> AnnotationShift) == IsDisplacementX2N)); + + /* fiducial */ + while (((annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift)) != HasBytecodePC) { + map -= 1; + } + + /* skip fiducial; i.e. the map entry for the pc immediately following the method header. */ + map -= 1; + aMethodObj = (homeMethod->methodObject); + bcpc = startbcpc - (/* blockCreationBytecodeSizeForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? AltBlockCreationBytecodeSize + : BlockCreationBytecodeSize)); + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + endbcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, -1, aMethodObj) + : 0)); + bcpc = startbcpc; + } + nExts = 0; + enumeratingCogMethod = homeMethod; + + /* Now skip up through the bytecode pc map entry for the stack check. */ + while ((((usqInt)((byteAt((void *)(map))))) >> AnnotationShift) != HasBytecodePC) { + map -= 1; + } + map -= 1; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + mcpc += (mapByte & DisplacementMask); + if (annotation >= HasBytecodePC) { + if ((annotation == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + while (1) { + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + if (isInBlock) { + if (bcpc >= endbcpc) { + errCode = 0; + goto l1; + } + } + else { + if (((descriptor->isReturn)) + && (bcpc >= latestContinuation)) { + errCode = 0; + goto l1; + } + if ((isBranch(descriptor)) + || ((descriptor->isBlockCreation))) { + /* begin latestContinuationPCFor:at:exts:in: */ + distance = ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj); + targetPC = (bcpc + ((descriptor->numBytes))) + (((distance < 0) ? 0 : distance)); + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + } + } + nextBcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj) + : 0)); + if (((descriptor->isMapped)) + || (isInBlock + && ((descriptor->isMappedInBlock)))) break; + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + isBackwardBranch = (isBranch(descriptor)) + && ((/* begin isBackwardBranch:at:exts:in: */ + assert(((descriptor->spanFunction))), + (((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj)) < 0)); + result = collectCogConstituentForAnnotationMcpcBcpcMethod(descriptor, (isBackwardBranch + ? ((((usqInt)(annotation) << 1))) + 1 + : ((sqInt)((usqInt)(annotation) << 1))), ((char *) mcpc), (isBackwardBranch + ? bcpc - (2 * nExts) + : bcpc), ((void *)cogMethod)); + if (result) { + errCode = result; + goto l1; + } + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + } + else { + assert(((((usqInt)(mapByte)) >> AnnotationShift) == IsDisplacementX2N) + || ((((usqInt)(mapByte)) >> AnnotationShift) == IsAnnotationExtension)); + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + + /* defensive; we exit on bcpc */ + errCode = 0; + /* end mapFor:bcpc:performUntil:arg: */ +l1: + if (errCode) { + popRemappableOop(); + return null; + } + if (cogConstituentIndex < nSlots) { + shortentoIndexableSize(topRemappableOop(), cogConstituentIndex); + } + return popRemappableOop(); +} + + /* Cogit>>#compactCogCompiledCode */ +void +compactCogCompiledCode(void) +{ + assertValidDualZone(); + assert(noCogMethodsMaximallyMarked()); + moveProfileToMethods(); + + /* begin ensureWritableCodeZone */ + markActiveMethodsAndReferents(); + freeOlderMethodsForCompaction(); + compactPICsWithFreedTargets(); + planCompaction(); + updateStackZoneReferencesToCompiledCodePreCompaction(); + relocateMethodsPreCompaction(); + assertValidDualZone(); + compactCompiledCode(); + stopsFromto(backEnd, freeStart(), (youngReferrers) - 1); + flushICacheFromto(backEnd, ((usqInt)methodZoneBase), ((usqInt)(youngReferrers))); + assert(allMethodsHaveCorrectHeader()); + assert(kosherYoungReferrers()); + assertValidDualZone(); +} + + /* Cogit>>#compactPICsWithFreedTargets */ +static void +compactPICsWithFreedTargets(void) +{ + CogMethod *cogMethod; + sqInt count; + + cogMethod = ((CogMethod *) methodZoneBase); + count = 0; + while (cogMethod < (limitZony())) { + if ((((cogMethod->cmType)) == CMClosedPIC) + && (cPICCompactAndIsNowEmpty(cogMethod))) { + ((((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)))->cmType = CMFree); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + count += 1; + } + assert(count == (numMethods())); +} + + +/* The start of a CogMethod has a call to a run-time abort routine that + either handles an in-line cache failure or a stack overflow. The routine + selects the + path depending on ReceiverResultReg; if zero it takes the stack overflow + path; if nonzero the in-line cache miss path. Neither of these paths + returns. The abort routine must be called; In the callee the method is + located by + adding the relevant offset to the return address of the call. + + N.B. This code must match that in compilePICAbort: so that the offset of + the return address of the call is the same in methods and closed PICs. */ + + /* Cogit>>#compileAbort */ +static AbstractInstruction * +compileAbort(void) +{ + stackOverflowCall = genoperandoperand(MoveCqR, 0, ReceiverResultReg); + return (sendMiss = gCall(methodAbortTrampolineFor(methodOrBlockNumArgs))); +} + + /* Cogit>>#compileBlockDispatchFrom:to: */ +static NoDbgRegParms sqInt +compileBlockDispatchFromto(sqInt lowBlockStartIndex, sqInt highBlockStartIndex) +{ + BlockStart *blockStart; + sqInt halfWay; + AbstractInstruction *jmp; + + if (lowBlockStartIndex == highBlockStartIndex) { + blockStart = blockStartAt(lowBlockStartIndex); + + /* Jump: */ + genoperand(Jump, ((sqInt)((blockStart->entryLabel)))); + return null; + } + halfWay = (highBlockStartIndex + lowBlockStartIndex) / 2; + assert(((halfWay >= lowBlockStartIndex) && (halfWay <= highBlockStartIndex))); + blockStart = blockStartAt(halfWay); + + /* N.B. FLAGS := TempReg - startpc */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, (((usqInt)(((blockStart->startpc)) + 1) << 3) | 1), TempReg); + if (lowBlockStartIndex == halfWay) { + /* JumpLessOrEqual: */ + genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)((blockStart->entryLabel)))); + compileBlockDispatchFromto(halfWay + 1, highBlockStartIndex); + return null; + } + if ((halfWay + 1) == highBlockStartIndex) { + blockStart = blockStartAt(highBlockStartIndex); + + /* JumpGreater: */ + genConditionalBranchoperand(JumpGreater, ((sqInt)((blockStart->entryLabel)))); + return compileBlockDispatchFromto(lowBlockStartIndex, halfWay); + } + jmp = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + compileBlockDispatchFromto(lowBlockStartIndex, halfWay); + if (halfWay == highBlockStartIndex) { + blockStart = blockStartAt(highBlockStartIndex); + jmpTarget(jmp, (blockStart->entryLabel)); + } + else { + jmpTarget(jmp, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + compileBlockDispatchFromto(halfWay + 1, highBlockStartIndex); + } + return 0; +} + + +/* Compile a block's entry. This looks like a dummy CogBlockMethod header + (for frame parsing) + followed by either a frame build, if a frame is required, or nothing. The + CogMethodHeader's objectHeader field is a back pointer to the method, but + this can't be filled in until code generation. */ + + /* Cogit>>#compileBlockEntry: */ +static NoDbgRegParms void +compileBlockEntry(BlockStart *blockStart) +{ + AbstractInstruction *abstractInstruction; + sqInt alignment; + + alignment = blockAlignment(); + + /* begin AlignmentNops: */ + genoperand(AlignmentNops, alignment); + (blockStart->fakeHeader = genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + switch (sizeof(CogBlockMethod)) { + case 8: + /* Fill32: */ + genoperand(Fill32, 0); + + /* Fill32: */ + genoperand(Fill32, 0); + break; + case 12: + /* Fill32: */ + genoperand(Fill32, 0); + + /* Fill32: */ + genoperand(Fill32, 0); + + /* Fill32: */ + genoperand(Fill32, 0); + break; + case 16: + /* Fill32: */ + genoperand(Fill32, 0); + + /* Fill32: */ + genoperand(Fill32, 0); + + /* Fill32: */ + genoperand(Fill32, 0); + + /* Fill32: */ + genoperand(Fill32, 0); + break; + default: + error("Case not found and no otherwise clause"); + } + + /* gets filled in later with numArgs et al + gets filled in later with numArgs et al */ + (blockStart->entryLabel = genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + if (needsFrame) { + compileBlockFrameBuild(blockStart); + if (recordBlockTrace()) { + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceTraceBlockActivationTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + } + } + else { + compileBlockFramelessEntry(blockStart); + } +} + + +/* Generate a call to aRoutine with up to 4 arguments. If resultRegOrNone is + not NoReg assign the C result to resultRegOrNone. If saveRegs, save all + registers. Hack: a negative arg value indicates an abstract register, a + non-negative value + indicates a constant. The encoding for constants is defined by + trampolineArgConstant: & trampolineArgValue:. Pass a constant as the + result of trampolineArgConstant:. */ + + /* Cogit>>#compileCallFor:numArgs:arg:arg:arg:arg:resultReg:regsToSave: */ +static NoDbgRegParms void +compileCallFornumArgsargargargargresultRegregsToSave(void *aRoutine, sqInt numArgs, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3, sqInt resultRegOrNone, sqInt regMask) +{ + AbstractInstruction *abstractInstruction; + sqInt callTargetSqInt; + sqInt delta; + sqInt numRegsPushed; + usqInt regMaskCopy; + sqInt regsToSave; + sqInt wordsPushedModAlignment; + + regsToSave = (resultRegOrNone == NoReg + ? regMask + : ((regMask | (((resultRegOrNone < 0) ? (((usqInt)(1)) >> (-resultRegOrNone)) : (1ULL << resultRegOrNone)))) - (((resultRegOrNone < 0) ? (((usqInt)(1)) >> (-resultRegOrNone)) : (1ULL << resultRegOrNone))))); + if (cStackAlignment > BytesPerWord) { + /* begin genAlignCStackGivenRegistersToBeSaved:numArgs:wordAlignment: */ + regMaskCopy = ((usqInt)regsToSave); + numRegsPushed = 0; + while (regMaskCopy != 0) { + numRegsPushed += regMaskCopy & 1; + regMaskCopy = ((regMaskCopy) >> 1); + } + if ((numRegsPushed == 0) + && ((numIntRegArgs(backEnd)) >= numArgs)) { + goto l1; + } + wordsPushedModAlignment = (numRegsPushed + ((((numArgs - (numIntRegArgs(backEnd))) < 0) ? 0 : (numArgs - (numIntRegArgs(backEnd)))))) % (cStackAlignment / BytesPerWord); + if (wordsPushedModAlignment) { + delta = (cStackAlignment / BytesPerWord) - wordsPushedModAlignment; + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, delta * BytesPerWord, SPReg); + } + /* end genAlignCStackGivenRegistersToBeSaved:numArgs:wordAlignment: */ +l1:; + } + genSaveRegs(backEnd, regsToSave); + + /* begin genMarshallNArgs:arg:arg:arg:arg: */ + + /* WIN64 ABI always reserves shadow space on the stack for callee to save up to 4 register parameters */ + assert(numArgs <= 4); + if (numArgs > 0) { + if (regOrConst0 < NoReg) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, -2 - regOrConst0, CArg0Reg); + } + else { + if (regOrConst0 != CArg0Reg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, regOrConst0, CArg0Reg); + } + } + if (numArgs > 1) { + if (regOrConst1 < NoReg) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, -2 - regOrConst1, CArg1Reg); + } + else { + if (regOrConst1 != CArg1Reg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, regOrConst1, CArg1Reg); + } + } + if (numArgs > 2) { + if (regOrConst2 < NoReg) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, -2 - regOrConst2, CArg2Reg); + } + else { + if (regOrConst2 != CArg2Reg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, regOrConst2, CArg2Reg); + } + } + if (numArgs > 3) { + if (regOrConst3 < NoReg) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, -2 - regOrConst3, CArg3Reg); + } + else { + if (regOrConst3 != CArg3Reg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, regOrConst3, CArg3Reg); + } + } + } + } + } + } + + /* begin CallAnyFixedRT: */ + if (is32BitSignedImmediate(backEnd, maximumDistanceFromCodeZone(((usqInt)aRoutine)))) { + callTargetSqInt = ((usqIntptr_t)aRoutine); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, callTargetSqInt); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* #CallFullRT: #CallFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(CallFull, ((usqIntptr_t)aRoutine)); + } + genWriteCResultIntoReg(backEnd, resultRegOrNone); + + /* begin genRemoveNArgsFromStack: */ + assert(numArgs <= 4); + genRestoreRegs(backEnd, regsToSave); +} + + +/* Compile the cache tag computation and the first comparison. Answer the + address of that comparison. */ + + /* Cogit>>#compileCPICEntry */ +static AbstractInstruction * +compileCPICEntry(void) +{ + entry = genGetInlineCacheClassTagFromintoforEntry(ReceiverResultReg, TempReg, 1); + + /* begin CmpR:R: */ + assert(!(0 /* (ClassReg = SPReg) */)); + genoperandoperand(CmpRR, ClassReg, TempReg); + return genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +} + + +/* Compile the abstract instructions for the entire full block method. */ + + /* Cogit>>#compileEntireFullBlockMethod: */ +static NoDbgRegParms sqInt +compileEntireFullBlockMethod(sqInt numCopied) +{ + sqInt result; + + /* begin preenMethodLabel */ + /* setLabelOffset: */ + ((methodLabel->operands))[1] = 0; + compileFullBlockEntry(); + + /* Frame build */ + compileFullBlockMethodFrameBuild(numCopied); + + /* Method body */ + if (((result = compileMethodBody())) < 0) { + return result; + } + assert(blockCount == 0); + return 0; +} + + +/* The entry code to a method checks that the class of the current receiver + matches that in the inline cache. Other non-obvious elements are that its + alignment must be + different from the alignment of the noCheckEntry so that the method map + machinery can distinguish normal and super sends (super sends bind to the + noCheckEntry). */ + + /* Cogit>>#compileEntry */ +static void +compileEntry(void) +{ + entry = genGetInlineCacheClassTagFromintoforEntry(ReceiverResultReg, TempReg, 1); + + /* begin CmpR:R: */ + assert(!(0 /* (ClassReg = SPReg) */)); + genoperandoperand(CmpRR, ClassReg, TempReg); + + /* JumpNonZero: */ + genConditionalBranchoperand(JumpNonZero, ((sqInt)sendMiss)); + noCheckEntry = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + if (((traceFlags & 64) == 64)) { + /* #CallFullRT: #CallFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(CallFull, ceTraceLinkedSendTrampoline); + } +} + + +/* Compile the abstract instructions for the entire method, including blocks. */ +/* Abort for stack overflow on full block activation (no inline cache miss + possible). The flag is SendNumArgsReg. */ + + /* Cogit>>#compileFullBlockEntry */ +static sqInt +compileFullBlockEntry(void) +{ + sqInt callTarget; + AbstractInstruction *jumpNoContextSwitch; + + stackOverflowCall = genoperandoperand(MoveCqR, 0, ReceiverResultReg); + + /* Since the only case in which this is called is the + stack overflow case we can reuse the trampoline. */ + callTarget = methodAbortTrampolineFor(methodOrBlockNumArgs); + + /* begin Call: */ + genoperand(Call, callTarget); + + /* Entries + No context switch entry */ + fullBlockNoContextSwitchEntry = genoperandoperand(MoveCqR, 0, SendNumArgsReg); + jumpNoContextSwitch = genoperand(Jump, ((sqInt)0)); + + /* AlignmentNops: */ + genoperand(AlignmentNops, ((BytesPerWord < 8) ? 8 : BytesPerWord)); + + /* Context switch entry (use ReceiverResultReg as a non-zero value; it's shorter). */ + fullBlockEntry = genoperandoperand(MoveRR, ReceiverResultReg, SendNumArgsReg); + jmpTarget(jumpNoContextSwitch, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + +/* Compile the top-level method body. */ + + /* Cogit>>#compileMethodBody */ +static sqInt +compileMethodBody(void) +{ + if (endPC < initialPC) { + return 0; + } + return compileBytecodesFromthrough(initialPC + (deltaToSkipPrimAndErrorStoreInheader(methodObj, methodHeader)), endPC); +} + + +/* The start of a PIC has a call to a run-time abort routine that either + handles a dispatch to an + interpreted method or a dispatch of an MNU case. The routine selects the + path by testing + ClassReg, which holds the inline cache tag; if equal to the + picAbortDiscriminatorValue (zero) + it takes the MNU path; if nonzero the dispatch to interpreter path. + Neither of these paths + returns. The abort routine must be called; In the callee the PIC is + located by adding the + relevant offset to the return address of the call. + + N.B. This code must match that in compileAbort so that the offset of the + return address of + the call is the same in methods and closed PICs. */ + + /* Cogit>>#compilePICAbort: */ +static NoDbgRegParms sqInt +compilePICAbort(sqInt numArgs) +{ + sqInt callTarget; + + picMNUAbort = genoperandoperand(MoveCqR, 0 /* picAbortDiscriminatorValue */, ClassReg); + callTarget = picAbortTrampolineFor(numArgs); + + /* begin Call: */ + picInterpretAbort = genoperand(Call, callTarget); + return 0; +} + + +/* Compile the compare of stackLimit against the stack pointer, jumping to + the stackOverflowCall if + the stack pointer is below the limit. Answer a bytecode annotated label + that follows the sequence. + + The stack check functions both as a genuine stack limit check to prevent + calls overflowing stack pages, + and as an event/context-switch break out. To cause an event check + (including a check for a required + context switch), stackLimit is set to the highest possible value, and + hence all stack limit checks will + fail. A path in the stack overflow abort then arranges to call event + checking if it has been requested. + + Certain block activations (e.g. valueNoContextSwitch:) must not context + switch, and in that + case, SendNumArgs is set to zero to communicate to the stack overflow + abort that it should + not perform event/context-switch (yet). */ + + /* Cogit>>#compileStackOverflowCheck: */ +static NoDbgRegParms AbstractInstruction * +compileStackOverflowCheck(sqInt canContextSwitch) +{ + AbstractInstruction *jumpSkip; + AbstractInstruction *label; + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, stackLimitAddress(), TempReg); + + /* N.B. We do not expect the backEnd to provide CmpR: SPReg R: reg, only CmpR: reg R: SPReg */ + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, SPReg); + if (canContextSwitch) { + /* JumpBelow: */ + genConditionalBranchoperand(JumpBelow, ((sqInt)stackOverflowCall)); + label = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + } + else { + jumpSkip = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, SendNumArgsReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)stackOverflowCall)); + jmpTarget(jumpSkip, (label = genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + } + + /* begin annotateBytecode: */ + (label->annotation = HasBytecodePC); + return label; +} + + +/* Generate a trampoline with up to four arguments. Generate either a call or + a jump to aRoutine + as requested by callJumpBar. If generating a call and resultRegOrNone is + not NoReg pass the C + result back in resultRegOrNone. + Hack: a negative value indicates an abstract register, a non-negative + value indicates a constant. */ + + /* Cogit>>#compileTrampolineFor:numArgs:arg:arg:arg:arg:regsToSave:pushLinkReg:resultReg: */ +static NoDbgRegParms void +compileTrampolineFornumArgsargargargargregsToSavepushLinkRegresultReg(void *aRoutine, sqInt numArgs, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3, sqInt regMask, sqInt pushLinkReg, sqInt resultRegOrNone) +{ + genSmalltalkToCStackSwitch(pushLinkReg); + compileCallFornumArgsargargargargresultRegregsToSave(aRoutine, numArgs, regOrConst0, regOrConst1, regOrConst2, regOrConst3, resultRegOrNone, regMask); + genLoadStackPointers(backEnd); + genTrampolineReturn(pushLinkReg); +} + + +/* Generate the entry code for a method to determine cmEntryOffset and + cmNoCheckEntryOffset. We + need cmNoCheckEntryOffset up front to be able to generate the map starting + from cmNoCheckEntryOffset */ +/* stack allocate the various collections so that they + are effectively garbage collected on return. */ + + /* Cogit>>#computeEntryOffsets */ +static void +computeEntryOffsets(void) +{ + usqIntptr_t fixupSize; + usqIntptr_t opcodeSize; + AbstractInstruction *sendMissCall; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 24; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + methodOrBlockNumArgs = 0; + sendMissCall = compileAbort(); + compileEntry(); + computeMaximumSizes(); + generateInstructionsAt(methodZoneBase + (sizeof(CogMethod))); + cmEntryOffset = ((entry->address)) - methodZoneBase; + cmNoCheckEntryOffset = ((noCheckEntry->address)) - methodZoneBase; + missOffset = (((sendMissCall->address)) + ((sendMissCall->machineCodeSize))) - methodZoneBase; + entryPointMask = BytesPerWord - 1; + while ((cmEntryOffset & entryPointMask) == (cmNoCheckEntryOffset & entryPointMask)) { + entryPointMask = (entryPointMask + entryPointMask) + 1; + } + if (entryPointMask >= (roundUpToMethodAlignment(backEnd, 1))) { + error("cannot differentiate checked and unchecked entry-points with current cog method alignment"); + } + checkedEntryAlignment = cmEntryOffset & entryPointMask; + uncheckedEntryAlignment = cmNoCheckEntryOffset & entryPointMask; + assert(checkedEntryAlignment != uncheckedEntryAlignment); +} + + +/* Generate the entry code for a method to determine cmEntryOffset and + cmNoCheckEntryOffset. We + need cmNoCheckEntryOffset up front to be able to generate the map starting + from cmNoCheckEntryOffset */ +/* stack allocate the various collections so that they + are effectively garbage collected on return. */ + + /* Cogit>>#computeFullBlockEntryOffsets */ +static void +computeFullBlockEntryOffsets(void) +{ + usqIntptr_t fixupSize; + usqIntptr_t opcodeSize; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 24; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + methodOrBlockNumArgs = 0; + compileFullBlockEntry(); + computeMaximumSizes(); + generateInstructionsAt(methodZoneBase + (sizeof(CogMethod))); + cbEntryOffset = ((fullBlockEntry->address)) - methodZoneBase; + cbNoSwitchEntryOffset = ((fullBlockNoContextSwitchEntry->address)) - methodZoneBase; +} + + +/* While we order variables in the CoInterpreter in order of dynamic + frequency, and hence + expect that stackPointer will be output first, C optimizers and linkers + may get their own + ideas and ``improve upon'' this ordering. So we cannot depend on + stackPointer being + at the lowest address of the variables we want to access through + VarBaseReg. Here we + choose the minimum amongst a set to try to choose a varBaseAddress that is + just less + than but within range of all variables we want to access through it. */ + + /* Cogit>>#computeGoodVarBaseAddress */ +usqInt +computeGoodVarBaseAddress(void) +{ + usqInt minAddress; + + /* stackLimit was e.g. lowest using the clang toolchain on MacOS X (prior to the use of variable_order) */ + minAddress = stackLimitAddress(); + if ((stackPointerAddress()) < minAddress) { + minAddress = stackPointerAddress(); + } + if ((framePointerAddress()) < minAddress) { + minAddress = framePointerAddress(); + } + if ((instructionPointerAddress()) < minAddress) { + minAddress = instructionPointerAddress(); + } + if ((argumentCountAddress()) < minAddress) { + minAddress = argumentCountAddress(); + } + if ((primFailCodeAddress()) < minAddress) { + minAddress = primFailCodeAddress(); + } + return minAddress; +} + + +/* This pass assigns maximum sizes to all abstract instructions and + eliminates jump fixups. + It hence assigns the maximum address an instruction will occur at which + allows the next + pass to conservatively size jumps. */ + + /* Cogit>>#computeMaximumSizes */ +static void +computeMaximumSizes(void) +{ + AbstractInstruction *abstractInstruction; + sqInt i; + sqInt relativeAddress; + + relativeAddress = 0; + for (i = 0; i < opcodeIndex; i += 1) { + maybeBreakGeneratingInstructionWithIndex(i); + abstractInstruction = abstractInstructionAt(i); + (abstractInstruction->address = relativeAddress); + (abstractInstruction->maxSize = computeMaximumSize(abstractInstruction)); + relativeAddress += (abstractInstruction->maxSize); + } +} + + +/* Configure a copy of the prototype CPIC for a two-case PIC for + case0CogMethod and + case1Method + case1Tag. + The tag for case0CogMethod is at the send site and so doesn't need to be + generated. case1Method may be any of + - a Cog method; jump to its unchecked entry-point + - a CompiledMethod; jump to the ceInterpretFromPIC trampoline + - nil; call ceMNUFromPIC + addDelta is the address change from the prototype to the new CPIC + location, needed + because the loading of the CPIC label at the end may use a literal instead + of a pc relative load. */ +/* self disassembleFrom: cPIC asInteger + (self sizeof: CogMethod) to: cPIC + asInteger + closedPICSize + */ + + /* Cogit>>#configureCPIC:Case0:Case1Method:tag:isMNUCase:numArgs:delta: */ +static NoDbgRegParms sqInt +configureCPICCase0Case1MethodtagisMNUCasenumArgsdelta(CogMethod *cPIC, CogMethod *case0CogMethod, sqInt case1Method, sqInt case1Tag, sqInt isMNUCase, sqInt numArgs, sqInt addrDelta) +{ + sqInt caseEndAddress; + sqInt jumpTargetAddr; + sqInt operand; + sqInt pc; + usqIntptr_t targetEntry; + + assert(case1Method); + + /* adjust the call at missOffset, the ceAbortXArgs */ + rewriteCallAttarget(backEnd, (((sqInt)cPIC)) + missOffset, picAbortTrampolineFor(numArgs)); + assert(!(inlineCacheTagIsYoung(case1Tag))); + if ((!isMNUCase) + && (methodHasCogMethod(case1Method))) { + operand = 0; + targetEntry = (((sqInt)(cogMethodOf(case1Method)))) + cmNoCheckEntryOffset; + } + else { + operand = ((!case1Method) + || (isYoungObject(case1Method)) + ? 0 + : case1Method); + targetEntry = (case1Method + ? (((sqInt)cPIC)) + (picInterpretAbortOffset()) + : (((sqInt)cPIC)) + (sizeof(CogMethod))); + } + + /* We do not scavenge PICs, hence we cannot cache the MNU method if it is in new space. + set the jump to the case0 method */ + + /* rewriteJumpLongAt:target: */ + rewriteCallAttarget(backEnd, (((sqInt)cPIC)) + firstCPICCaseOffset, (((sqInt)case0CogMethod)) + cmNoCheckEntryOffset); + caseEndAddress = addressOfEndOfCaseinCPIC(2, cPIC); + + /* update the cpic case */ + rewriteCPICCaseAttagobjReftarget(caseEndAddress, case1Tag, operand, ((sqInt)((isMNUCase + ? (((sqInt)cPIC)) + (sizeof(CogMethod)) + : targetEntry)))); + + /* update the loading of the CPIC address */ + pc = ((((sqInt)cPIC)) + cPICEndOfCodeOffset) - (jumpLongByteSize(backEnd)); + + /* begin relocateMethodReferenceBeforeAddress:by: */ + assert((((byteAt((void *)(pc - 6))) == 141) + && (((byteAt((void *)(pc - 5))) | (modRMRO(backEnd, 0, 0, 7))) == (modRMRO(backEnd, ModRegInd, 5, 7)))) + || (((byteAt((void *)(pc - 8))) == 141) + && (((byteAt((void *)(pc - 7))) | (modRMRO(backEnd, 0, 0, 7))) == (modRMRO(backEnd, ModRegInd, 5, 7))))); + + /* write the final desperate jump to cePICMissXArgs */ + jumpTargetAddr = cPICMissTrampolineFor(numArgs); + + /* begin rewriteJumpLongAt:target: */ + rewriteCallAttarget(backEnd, (((sqInt)cPIC)) + cPICEndOfCodeOffset, jumpTargetAddr); + return 0; +} + + +/* Configure a copy of the prototype CPIC for a one-case MNU CPIC that calls + ceMNUFromPIC for + case0Tag The tag for case0 is at the send site and so doesn't need to be + generated. addDelta is the address change from the prototype to the new + CPIC location, needed + because the loading of the CPIC label at the end may be a literal instead + of a pc-relative load. */ +/* adjust the jump at missOffset, the ceAbortXArgs */ + + /* Cogit>>#configureMNUCPIC:methodOperand:numArgs:delta: */ +static NoDbgRegParms sqInt +configureMNUCPICmethodOperandnumArgsdelta(CogMethod *cPIC, sqInt methodOperand, sqInt numArgs, sqInt addrDelta) +{ + sqInt jumpTargetAddr; + sqInt operand; + sqInt pc; + sqInt target; + + rewriteCallAttarget(backEnd, (((sqInt)cPIC)) + missOffset, picAbortTrampolineFor(numArgs)); + + /* We do not scavenge PICs, hence we cannot cache the MNU method if it is in new space. */ + operand = ((!methodOperand) + || (isYoungObject(methodOperand)) + ? 0 + : methodOperand); + + /* set the jump to the case0 method */ + jumpTargetAddr = (((sqInt)cPIC)) + (sizeof(CogMethod)); + + /* begin rewriteJumpLongAt:target: */ + rewriteCallAttarget(backEnd, (((sqInt)cPIC)) + firstCPICCaseOffset, jumpTargetAddr); + storeLiteralbeforeFollowingAddress(backEnd, operand, ((((sqInt)cPIC)) + firstCPICCaseOffset) - (jumpLongByteSize(backEnd))); + + /* rewrite the final desperate jump to cePICMissXArgs */ + jumpTargetAddr = cPICMissTrampolineFor(numArgs); + + /* begin rewriteJumpLongAt:target: */ + rewriteCallAttarget(backEnd, (((sqInt)cPIC)) + cPICEndOfCodeOffset, jumpTargetAddr); + pc = ((((sqInt)cPIC)) + cPICEndOfCodeOffset) - (jumpLongByteSize(backEnd)); + + /* begin relocateMethodReferenceBeforeAddress:by: */ + assert((((byteAt((void *)(pc - 6))) == 141) + && (((byteAt((void *)(pc - 5))) | (modRMRO(backEnd, 0, 0, 7))) == (modRMRO(backEnd, ModRegInd, 5, 7)))) + || (((byteAt((void *)(pc - 8))) == 141) + && (((byteAt((void *)(pc - 7))) | (modRMRO(backEnd, 0, 0, 7))) == (modRMRO(backEnd, ModRegInd, 5, 7))))); + + /* finally, rewrite the jump 3 instr before firstCPICCaseOffset to jump to the end of case 2, missing the actual case */ + target = addressOfEndOfCaseinCPIC(2, cPIC); + + /* begin rewriteCPIC:caseJumpTo: */ + rewriteCPICJumpAttarget(backEnd, (((((sqInt)cPIC)) + firstCPICCaseOffset) - (jumpLongByteSize(backEnd))) - 11, target); + return 0; +} + + +/* A CogMethod effectively imports the literals it uses and the methods it + calls. Count these elements so an array of imports can be computed. */ + + /* Cogit>>#countCogMethodDataImports:pc:count: */ +#if VMInvestigations +static NoDbgRegParms sqInt +countCogMethodDataImportspccount(sqInt annotation, char *mcpc, sqInt *countPtr) +{ + unsigned int cacheTag1; + usqInt entryPointUsqInt; + + if ((annotation == IsObjectReference) + || (annotation == IsRelativeCall)) { + countPtr[0] = ((countPtr[0]) + 1); + } + if (annotation >= IsSendCall) { + /* begin entryCacheTagAndCouldBeObjectAt:annotation:into: */ + cacheTag1 = literal32BeforeFollowingAddress(backEnd, ((usqInt)((((sqInt)mcpc)) - 5))); + entryPointUsqInt = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + + /* in-line cache tags are the selectors of sends if sends are unlinked, + the selectors of super sends (entry offset = cmNoCheckEntryOffset), + the selectors of open PIC sends (entry offset = cmEntryOffset, target is an Open PIC) + or in-line cache tags (classes, class indices, immediate bit patterns, etc). + Note that selectors can be immediate so there is no guarantee that they + are markable/remappable objects. Writing the block invocation three times + allows Slang to inline tagCouldBeObject tests. */ + countPtr[0] = ((countPtr[0]) + 1); + } + return 0; +} +#endif /* VMInvestigations */ + + +/* Scan the CPIC for target methods that have been freed and eliminate them. + Since the first entry cannot be eliminated, answer that the PIC should be + freed if the first entry is to a free target. Answer if the PIC is now + empty or should be freed. */ + + /* Cogit>>#cPICCompactAndIsNowEmpty: */ +static NoDbgRegParms sqInt +cPICCompactAndIsNowEmpty(CogMethod *cPIC) +{ + sqInt entryPoint; + sqInt i; + sqInt methods[MaxCPICCases]; + sqInt pc; + int tags[MaxCPICCases]; + CogMethod *targetMethod; + sqInt targets[MaxCPICCases]; + sqInt used; + sqInt valid; + + used = 0; + for (i = 1; i <= ((cPIC->cPICNumCases)); i += 1) { + pc = addressOfEndOfCaseinCPIC(i, cPIC); + entryPoint = (jumpLongTargetBeforeFollowingAddress(backEnd, pc)); + valid = 1; + + /* Collect all target triples except for triples whose entry-point is a freed method */ + if (!(/* containsAddress: */ + ((((usqInt)cPIC)) <= (((usqInt)entryPoint))) + && (((((usqInt)cPIC)) + ((cPIC->blockSize))) >= (((usqInt)entryPoint))))) { + targetMethod = ((CogMethod *) (entryPoint - cmNoCheckEntryOffset)); + assert((isCMMethodEtAl(((CogBlockMethod *) targetMethod))) + || (isCMFree(((CogBlockMethod *) targetMethod)))); + if (((targetMethod->cmType)) == CMFree) { + if (i == 1) { + return 1; + } + valid = 0; + } + } + if (valid) { + tags[used] = ((i > 1 + ? literal32BeforeFollowingAddress(backEnd, pc - (jumpLongConditionalByteSize(backEnd))) + : 0)); + targets[used] = entryPoint; + methods[used] = (literalBeforeFollowingAddress(backEnd, pc - ((i == 1 + ? jumpLongByteSize(backEnd) + : (jumpLongConditionalByteSize(backEnd)) + (cmpC32RTempByteSize(backEnd)))))); + used += 1; + } + } + if (used == ((cPIC->cPICNumCases))) { + return 0; + } + if (!used) { + return 1; + } + ((((CogMethod *) ((((usqInt)cPIC)) + codeToDataDelta)))->cPICNumCases = used); + if (used == 1) { + pc = addressOfEndOfCaseinCPIC(2, cPIC); + + /* begin rewriteCPIC:caseJumpTo: */ + rewriteCPICJumpAttarget(backEnd, (((((sqInt)cPIC)) + firstCPICCaseOffset) - (jumpLongByteSize(backEnd))) - 11, pc); + return 0; + } + + /* the first entry cannot change... */ + for (i = 1; i < used; i += 1) { + pc = addressOfEndOfCaseinCPIC(i + 1, cPIC); + rewriteCPICCaseAttagobjReftarget(pc, tags[i], methods[i], targets[i]); + } + + /* finally, rewrite the jump 3 instr before firstCPICCaseOffset to jump to the beginning of this new case */ + + /* begin rewriteCPIC:caseJumpTo: */ + rewriteCPICJumpAttarget(backEnd, (((((sqInt)cPIC)) + firstCPICCaseOffset) - (jumpLongByteSize(backEnd))) - 11, pc - cPICCaseSize); + return 0; +} + + +/* The first case in a CPIC doesn't have a class reference so we need only + step over actually usd subsequent cases. + */ + + /* Cogit>>#cPICHasForwardedClass: */ +static NoDbgRegParms sqInt +cPICHasForwardedClass(CogMethod *cPIC) +{ + unsigned int classIndex; + sqInt i; + sqInt pc; + + + /* start by finding the address of the topmost case, the cPICNumCases'th one */ + pc = (addressOfEndOfCaseinCPIC((cPIC->cPICNumCases), cPIC)) - (jumpLongConditionalByteSize(backEnd)); + for (i = 2; i <= ((cPIC->cPICNumCases)); i += 1) { + classIndex = literal32BeforeFollowingAddress(backEnd, pc); + if (isForwardedClassIndex(classIndex)) { + return 1; + } + + /* since we started at the top, we can just add the case size each time to move on to the next case */ + pc += cPICCaseSize; + } + return 0; +} + + +/* scan the CPIC for target methods that have been freed. */ + + /* Cogit>>#cPICHasFreedTargets: */ +static NoDbgRegParms sqInt +cPICHasFreedTargets(CogMethod *cPIC) +{ + sqInt entryPoint; + sqInt i; + sqInt pc; + CogMethod *targetMethod; + + for (i = 1; i <= ((cPIC->cPICNumCases)); i += 1) { + pc = addressOfEndOfCaseinCPIC(i, cPIC); + entryPoint = (jumpLongTargetBeforeFollowingAddress(backEnd, pc)); + + /* Find target from jump. Ignore jumps to the interpret and MNU calls within this PIC */ + if (!(/* containsAddress: */ + ((((usqInt)cPIC)) <= (((usqInt)entryPoint))) + && (((((usqInt)cPIC)) + ((cPIC->blockSize))) >= (((usqInt)entryPoint))))) { + targetMethod = ((CogMethod *) (entryPoint - cmNoCheckEntryOffset)); + assert((isCMMethodEtAl(((CogBlockMethod *) targetMethod))) + || (isCMFree(((CogBlockMethod *) targetMethod)))); + if (((targetMethod->cmType)) == CMFree) { + return 1; + } + } + } + return 0; +} + + +/* Whimsey; we want 16rCA5E10 + cPICPrototypeCaseOffset to be somewhere in + the middle of the zone. + */ + + /* Cogit>>#cPICPrototypeCaseOffset */ +static sqInt +cPICPrototypeCaseOffset(void) +{ + return ((methodZoneBase + (youngReferrers)) / 2) - 13262352; +} + + +/* Are any of the jumps from this CPIC to targetMethod? */ + + /* Cogit>>#cPIC:HasTarget: */ +static NoDbgRegParms sqInt +cPICHasTarget(CogMethod *cPIC, CogMethod *targetMethod) +{ + sqInt i; + sqInt pc; + usqInt target; + + target = (((usqInt)targetMethod)) + cmNoCheckEntryOffset; + pc = (((sqInt)cPIC)) + firstCPICCaseOffset; + + /* Since this is a fast test doing simple compares we don't need to care that some + cases have nonsense addresses in there. Just zip on through. + First jump is unconditional; subsequent ones are conditional */ + if (target == (jumpLongTargetBeforeFollowingAddress(backEnd, pc))) { + return 1; + } + for (i = 2; i <= MaxCPICCases; i += 1) { + pc += cPICCaseSize; + if (target == (jumpLongTargetBeforeFollowingAddress(backEnd, pc))) { + return 1; + } + } + return 0; +} + + +/* Answer an Array of the PIC's selector, followed by class and + targetMethod/doesNotUnderstand: for each entry in the PIC. + */ + + /* Cogit>>#createCPICData: */ +static NoDbgRegParms sqInt +createCPICData(CogMethod *cPIC) +{ + sqInt class; + usqInt entryPoint; + sqInt i; + sqInt pc; + sqInt picData; + sqInt target; + CogMethod *targetMethod; + + assert((((cPIC->methodObject)) == 0) + || (addressCouldBeOop((cPIC->methodObject)))); + picData = instantiateClassindexableSize(classArray(), (((cPIC->cPICNumCases)) * 2) + 1); + if (!picData) { + return picData; + } + storePointerUncheckedofObjectwithValue(0, picData, (cPIC->selector)); + for (i = 1; i <= ((cPIC->cPICNumCases)); i += 1) { + pc = addressOfEndOfCaseinCPIC(i, cPIC); + if (i == 1) { + /* first case may have been collected and stored here by collectCogConstituentFor:Annotation:Mcpc:Bcpc:Method: */ + class = (cPIC->methodObject); + if (!class) { + class = nilObject(); + } + entryPoint = jumpLongTargetBeforeFollowingAddress(backEnd, pc); + } + else { + class = classForInlineCacheTag(literal32BeforeFollowingAddress(backEnd, pc - (jumpLongConditionalByteSize(backEnd)))); + entryPoint = jumpLongTargetBeforeFollowingAddress(backEnd, pc); + } + + /* Find target from jump. A jump to the MNU entry-point should collect #doesNotUnderstand: */ + if (/* containsAddress: */ + ((((usqInt)cPIC)) <= (((usqInt)entryPoint))) + && (((((usqInt)cPIC)) + ((cPIC->blockSize))) >= (((usqInt)entryPoint)))) { + target = splObj(SelectorDoesNotUnderstand); + } + else { + targetMethod = ((CogMethod *) (entryPoint - cmNoCheckEntryOffset)); + assert(isCMMethodEtAl(((CogBlockMethod *) targetMethod))); + target = (targetMethod->methodObject); + } + storePointerUncheckedofObjectwithValue((i * 2) - 1, picData, class); + storePointerUncheckedofObjectwithValue(i * 2, picData, target); + } + beRootIfOld(picData); + ((((CogMethod *) ((((usqInt)cPIC)) + codeToDataDelta)))->methodObject = 0); + return picData; +} + + +/* Division is a little weird on some processors. Defer to the backEnd + to allow it to generate any special code it may need to. */ + + /* Cogit>>#DivR:R:Quo:Rem: */ +static NoDbgRegParms AbstractInstruction * +gDivRRQuoRem(sqInt rDivisor, sqInt rDividend, sqInt rQuotient, sqInt rRemainder) +{ + genDivRRQuoRem(backEnd, rDivisor, rDividend, rQuotient, rRemainder); + return abstractInstructionAt(opcodeIndex - 1); +} + + +/* Return the default number of bytes to allocate for native code at startup. + The actual value can be set via vmParameterAt: and/or a preference in the + ini file. */ + + /* Cogit>>#defaultCogCodeSize */ +int +defaultCogCodeSize(void) +{ + return 1433600; +} + + +/* Answer the number of bytecodes to skip to get to the first bytecode + past the primitive call and any store of the error code. */ + + /* Cogit>>#deltaToSkipPrimAndErrorStoreIn:header: */ +static NoDbgRegParms sqInt +deltaToSkipPrimAndErrorStoreInheader(sqInt aMethodObj, sqInt aMethodHeader) +{ + return (/* methodUsesPrimitiveErrorCode:header: */ + ((primitiveIndexOfMethodheader(aMethodObj, aMethodHeader)) > 0) + && ((longStoreBytecodeForHeader(aMethodHeader)) == (fetchByteofObject((startPCOfMethodHeader(aMethodHeader)) + (sizeOfCallPrimitiveBytecode(aMethodHeader)), aMethodObj))) + ? (sizeOfCallPrimitiveBytecode(aMethodHeader)) + (sizeOfLongStoreTempBytecode(aMethodHeader)) + : 0); +} + + /* Cogit>>#endPCOf: */ +static NoDbgRegParms sqInt +endPCOf(sqInt aMethod) +{ + sqInt bsOffset; + sqInt byte; + BytecodeDescriptor *descriptor; + sqInt distance; + sqInt end; + sqInt latestContinuation; + sqInt nExts; + sqInt pc; + sqInt prim; + sqInt targetPC; + + pc = (latestContinuation = startPCOfMethod(aMethod)); + if (((prim = primitiveIndexOf(aMethod))) > 0) { + if (isQuickPrimitiveIndex(prim)) { + return pc - 1; + } + } + bsOffset = /* bytecodeSetOffsetFor: */ + (methodUsesAlternateBytecodeSet(aMethod) + ? 0x100 + : 0); + nExts = 0; + end = numBytesOf(aMethod); + while (pc <= end) { + byte = fetchByteofObject(pc, aMethod); + descriptor = generatorAt(byte + bsOffset); + if (((descriptor->isReturn)) + && (pc >= latestContinuation)) { + end = pc; + } + if ((isBranch(descriptor)) + || ((descriptor->isBlockCreation))) { + distance = ((descriptor->spanFunction))(descriptor, pc, nExts, aMethod); + targetPC = (pc + ((descriptor->numBytes))) + distance; + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + if ((descriptor->isBlockCreation)) { + pc += distance; + } + } + else { + } + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + pc += (descriptor->numBytes); + } + return end; +} + + +/* This is a static version of ceEnterCogCodePopReceiverReg for + break-pointing when debugging in C. Marked so the code generator + won't delete it. */ + + /* Cogit>>#enterCogCodePopReceiver */ +static void +enterCogCodePopReceiver(void) +{ + realCEEnterCogCodePopReceiverReg(); + + /* (and this exists only to reference Debug) */ + if (!Debug) { + error("what??"); + } +} + + +/* Answer if the entryPoint's tag is expected to be a selector reference, as + opposed to a class tag. + */ + + /* Cogit>>#entryPointTagIsSelector: */ +static NoDbgRegParms sqInt +entryPointTagIsSelector(sqInt entryPoint) +{ + return (entryPoint < methodZoneBase) + || (((entryPoint & entryPointMask) == uncheckedEntryAlignment) + || (((entryPoint & entryPointMask) == checkedEntryAlignment) + && ((((((CogMethod *) (entryPoint - cmEntryOffset)))->cmType)) == CMOpenPIC))); +} + + +/* Use asserts to check if the ClosedPICPrototype is as expected from + compileClosedPICPrototype, and can be updated as required via + rewriteCPICCaseAt:tag:objRef:target:. If all asserts pass, answer + 0, otherwise answer a bit mask identifying all the errors. */ +/* self disassembleFrom: methodZoneBase + (self sizeof: CogMethod) to: + methodZoneBase + closedPICSize + */ + + /* Cogit>>#expectedClosedPICPrototype: */ +static NoDbgRegParms sqInt +expectedClosedPICPrototype(CogMethod *cPIC) +{ + unsigned int classTag; + sqInt classTagPC; + usqInt entryPoint; + sqInt errors; + sqInt i; + sqInt methodObjPC; + sqInt object; + usqInt pc; + + errors = 0; + pc = (((usqInt)cPIC)) + firstCPICCaseOffset; + + /* First jump is unconditional; subsequent ones are conditional */ + object = literalBeforeFollowingAddress(backEnd, pc - (jumpLongByteSize(backEnd))); + if (!(asserta(object == (firstPrototypeMethodOop())))) { + errors = 1; + } + entryPoint = jumpLongTargetBeforeFollowingAddress(backEnd, pc); + if (!(asserta(entryPoint == ((cPICPrototypeCaseOffset()) + 13262352)))) { + errors += 2; + } + for (i = 1; i < MaxCPICCases; i += 1) { + pc += cPICCaseSize; + + /* verify information in case is as expected. */ + methodObjPC = (pc - (jumpLongConditionalByteSize(backEnd))) - (cmpC32RTempByteSize(backEnd)); + object = literalBeforeFollowingAddress(backEnd, methodObjPC); + if (!(asserta(object == ((subsequentPrototypeMethodOop()) + i)))) { + errors = errors | 4; + } + classTagPC = pc - (jumpLongConditionalByteSize(backEnd)); + classTag = literal32BeforeFollowingAddress(backEnd, classTagPC); + if (!(asserta(classTag == (0xBABE1F15U + i)))) { + errors = errors | 8; + } + entryPoint = jumpLongTargetBeforeFollowingAddress(backEnd, pc); + if (!(asserta(entryPoint == (((cPICPrototypeCaseOffset()) + 13262352) + (i * 16))))) { + errors = errors | 16; + } + + /* change case via rewriteCPICCaseAt:tag:objRef:target: */ + rewriteCPICCaseAttagobjReftarget(pc, classTag ^ 0x5A5A5A5A, object ^ 0xA5A5A5A5U, entryPoint ^ 0x55AA50); + object = literalBeforeFollowingAddress(backEnd, methodObjPC); + if (!(asserta(object == (((subsequentPrototypeMethodOop()) + i) ^ 0xA5A5A5A5U)))) { + errors = errors | 32; + } + classTag = literal32BeforeFollowingAddress(backEnd, classTagPC); + if (!(asserta(classTag == ((0xBABE1F15U + i) ^ 0x5A5A5A5A)))) { + errors = errors | 64; + } + entryPoint = jumpLongTargetBeforeFollowingAddress(backEnd, pc); + if (!(asserta(entryPoint == ((((cPICPrototypeCaseOffset()) + 13262352) + (i * 16)) ^ 0x55AA50)))) { + errors = errors | 128; + } + + /* finally restore case to the original state */ + rewriteCPICCaseAttagobjReftarget(pc, classTag ^ 0x5A5A5A5A, object ^ 0xA5A5A5A5U, entryPoint ^ 0x55AA50); + } + entryPoint = jumpLongTargetBeforeFollowingAddress(backEnd, (((usqInt)cPIC)) + cPICEndOfCodeOffset); + if (!(asserta(entryPoint == (cPICMissTrampolineFor(0))))) { + errors += 0x100; + } + return errors; +} + + +/* 224 11100000 aaaaaaaa Extend A (Ext A = Ext A prev * 256 + Ext A) */ + + /* Cogit>>#extABytecode */ +static sqInt +extABytecode(void) +{ + extA = (((((usqInt)(extA) << 8)))) + byte1; + return 0; +} + + +/* 225 11100001 sbbbbbbb Extend B (Ext B = Ext B prev * 256 + Ext B) */ + + /* Cogit>>#extBBytecode */ +static sqInt +extBBytecode(void) +{ + extB = ((numExtB == 0) + && (byte1 > 0x7F) + ? byte1 - 0x100 + : (((((usqInt)(extB) << 8)))) + byte1); + numExtB += 1; + return 0; +} + + +/* Fill in the block headers now we know the exact layout of the code. */ + + /* Cogit>>#fillInBlockHeadersAt: */ +static NoDbgRegParms sqInt +fillInBlockHeadersAt(sqInt startAddress) +{ + sqInt aCogMethodOrInteger; + CogBlockMethod *blockHeader; + BlockStart *blockStart; + sqInt i; + + if (!(needsFrame + && (blockCount > 0))) { + return null; + } + if (blockNoContextSwitchOffset) { + assert(blockNoContextSwitchOffset == (((blockEntryLabel->address)) - ((blockEntryNoContextSwitch->address)))); + } + else { + blockNoContextSwitchOffset = ((blockEntryLabel->address)) - ((blockEntryNoContextSwitch->address)); + } + for (i = 0; i < blockCount; i += 1) { + blockStart = blockStartAt(i); + aCogMethodOrInteger = (((blockStart->fakeHeader))->address); + + /* begin writableBlockMethodFor: */ + blockHeader = ((CogBlockMethod *) ((((usqInt)aCogMethodOrInteger)) + codeToDataDelta)); + (blockHeader->homeOffset = ((((blockStart->fakeHeader))->address)) - startAddress); + (blockHeader->startpc = (blockStart->startpc)); + (blockHeader->cmType = CMBlock); + (blockHeader->cmNumArgs = (blockStart->numArgs)); + (blockHeader->cbUsesInstVars = (blockStart->hasInstVarRef)); + (blockHeader->stackCheckOffset = ((blockStart->stackCheckLabel) + ? ((((blockStart->stackCheckLabel))->address)) - ((((blockStart->fakeHeader))->address)) + : 0)); + } + return 0; +} + + +/* Fill in the header for theCogMethod method. This may be located at the + writable mapping. */ + + /* Cogit>>#fillInMethodHeader:size:selector: */ +static NoDbgRegParms void +fillInMethodHeadersizeselector(CogMethod *method, sqInt size, sqInt selector) +{ + sqInt actualMethodLocation; + CogMethod *originalMethod; + sqInt rawHeader; + + actualMethodLocation = (((usqInt)method)) - codeToDataDelta; + (method->cmType = CMMethod); + (method->objectHeader = nullHeaderForMachineCodeMethod()); + (method->blockSize = size); + (method->methodObject = methodObj); + rawHeader = rawHeaderOf(methodObj); + + /* If the method has already been cogged (e.g. Newspeak accessors) then + leave the original method attached to its cog method, but get the right header. */ + if (isCogMethodReference(rawHeader)) { + originalMethod = ((CogMethod *) rawHeader); + assert(((originalMethod->blockSize)) == size); + assert(methodHeader == ((originalMethod->methodHeader))); + } + else { + rawHeaderOfput(methodObj, actualMethodLocation); + } + (method->methodHeader = methodHeader); + (method->selector = selector); + (method->cmNumArgs = argumentCountOfMethodHeader(methodHeader)); + (method->cmHasMovableLiteral = hasMovableLiteral); + if ((method->cmRefersToYoung = hasYoungReferent)) { + addToYoungReferrers(method); + } + (method->cmUsageCount = initialMethodUsageCount()); + + /* cpicHasMNUCase: */ + (method->cpicHasMNUCaseOrCMIsFullBlock) = 0; + (method->cmUsesPenultimateLit = maxLitIndex >= ((literalCountOfMethodHeader(methodHeader)) - 2)); + (method->blockEntryOffset = (blockEntryLabel + ? ((blockEntryLabel->address)) - actualMethodLocation + : 0)); + + /* This can be an error check since a large stackCheckOffset is caused by compiling + a machine-code primitive, and hence depends on the Cogit, not the input method. */ + if (needsFrame) { + if (!((((stackCheckLabel->address)) - actualMethodLocation) <= MaxStackCheckOffset)) { + error("too much code for stack check offset"); + } + } + (method->stackCheckOffset = (needsFrame + ? ((stackCheckLabel->address)) - actualMethodLocation + : 0)); + assert((callTargetFromReturnAddress(backEnd, actualMethodLocation + missOffset)) == (methodAbortTrampolineFor((method->cmNumArgs)))); + assert(size == (roundUpLength(size))); + + /* begin assertValidDualZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + assertCoherentCodeAtdelta(backEnd, actualMethodLocation + cmNoCheckEntryOffset, codeToDataDelta); +# endif +} + + /* Cogit>>#findBackwardBranch:IsBackwardBranch:Mcpc:Bcpc:MatchingBcpc: */ +static NoDbgRegParms sqInt +findBackwardBranchIsBackwardBranchMcpcBcpcMatchingBcpc(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *targetBcpc) +{ + return ((((isBackwardBranchAndAnnotation & 1) != 0)) + && ((((sqInt)targetBcpc)) == bcpc) + ? ((sqInt)mcpc) + : 0); +} + + /* Cogit>>#findBlockMethodWithEntry:startBcpc: */ +static NoDbgRegParms usqInt +findBlockMethodWithEntrystartBcpc(sqInt blockEntryMcpc, sqInt startBcpc) +{ + CogBlockMethod *cogBlockMethod; + + cogBlockMethod = ((CogBlockMethod *) (blockEntryMcpc - (sizeof(CogBlockMethod)))); + if (((cogBlockMethod->startpc)) == startBcpc) { + return ((usqInt)cogBlockMethod); + } + return 0; +} + + /* Cogit>>#findMapLocationForMcpc:inMethod: */ +static NoDbgRegParms sqInt +findMapLocationForMcpcinMethod(usqInt targetMcpc, CogMethod *cogMethod) +{ + sqInt annotation; + sqInt map; + sqInt mapByte; + usqInt mcpc; + + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + if (mcpc == targetMcpc) { + return map; + } + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + if (annotation != IsAnnotationExtension) { + mcpc += 1 /* codeGranularity */ * ((annotation + ? mapByte & DisplacementMask + : ((sqInt)((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift)))); + } + if (mcpc >= targetMcpc) { + assert(mcpc == targetMcpc); + if (!annotation) { + map -= 1; + mapByte = byteAt((void *)(map)); + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + assert(annotation > IsAnnotationExtension); + } + return map; + } + map -= 1; + } + return 0; +} + + +/* Find the CMMethod or CMBlock that has zero-relative startbcpc as its first + bytecode pc. + As this is for cannot resume processing and/or conversion to machine-code + on backward + branch, it doesn't have to be fast. Enumerate block returns and map to + bytecode pcs. */ + + /* Cogit>>#findMethodForStartBcpc:inHomeMethod: */ +CogBlockMethod * +findMethodForStartBcpcinHomeMethod(sqInt startbcpc, CogMethod *cogMethod) +{ + assert(isCMMethodEtAl(((CogBlockMethod *) cogMethod))); + if (startbcpc == (startPCOfMethodHeader((cogMethod->methodHeader)))) { + return ((CogBlockMethod *) cogMethod); + } + assert(((cogMethod->blockEntryOffset)) != 0); + return ((CogBlockMethod *) (blockDispatchTargetsForperformarg(cogMethod, findBlockMethodWithEntrystartBcpc, startbcpc))); +} + + +/* Machine code addresses map to the following bytecode for all bytecodes + except backward branches, where they map to the backward branch itself. + This is so that loops continue, rather than terminate prematurely. */ + + /* Cogit>>#find:IsBackwardBranch:Mcpc:Bcpc:MatchingMcpc: */ +static NoDbgRegParms sqInt +findIsBackwardBranchMcpcBcpcMatchingMcpc(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *targetMcpc) +{ + return (targetMcpc == mcpc + ? ((!descriptor) + || (((isBackwardBranchAndAnnotation & 1) != 0)) + ? bcpc + : bcpc + ((descriptor->numBytes))) + : 0); +} + + /* Cogit>>#firstMappedPCFor: */ +static NoDbgRegParms usqInt +firstMappedPCFor(CogMethod *cogMethod) +{ + return ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); +} + + +/* Answer a fake value for the first method oop in the PIC prototype. + Since we use MoveUniqueCw:R: it must not be confused with a + method-relative address. */ + + /* Cogit>>#firstPrototypeMethodOop */ +static sqInt +firstPrototypeMethodOop(void) +{ + return (/* addressIsInCurrentCompilation: */ + ((((usqInt)0x5EAF00D)) >= ((methodLabel->address))) + && ((((usqInt)0x5EAF00D)) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize)))) + ? 0xCA7F00D + : 0x5EAF00D); +} + + /* Cogit>>#fixupAt: */ +static NoDbgRegParms BytecodeFixup * +fixupAt(sqInt fixupPC) +{ + return fixupAtIndex(fixupPC - initialPC); +} + + /* Cogit>>#flagCogMethodForBecome: */ +void +flagCogMethodForBecome(CogMethod *cogMethod) +{ + assert(isCMMethodEtAl(((CogBlockMethod *) cogMethod))); + + /* begin ensureWritableCodeZone */ + ((((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)))->cmType = CMMethodFlaggedForBecome); +} + + /* Cogit>>#followForwardedLiteralsImplementationIn: */ +static NoDbgRegParms void +followForwardedLiteralsImplementationIn(CogMethod *cogMethod) +{ + sqInt annotation; + sqInt hasYoungObj; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + CogMethod *writableCogMethod; + + assert((!(isCMMethodEtAl(((CogBlockMethod *) cogMethod)))) + || (!(isForwarded((cogMethod->methodObject))))); + writableCogMethod = ((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)); + hasYoungObj = isYoung((cogMethod->methodObject)); + if (shouldRemapOop((cogMethod->selector))) { + (writableCogMethod->selector = remapObj((cogMethod->selector))); + if (isYoung((cogMethod->selector))) { + hasYoungObj = 1; + } + } + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = remapIfObjectRefpchasYoung(annotation, ((char *) mcpc), ((void *)((&hasYoungObj)))); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1: + if (hasYoungObj) { + ensureInYoungReferrers(cogMethod); + } + else { + (writableCogMethod->cmRefersToYoung = 0); + } +} + + /* Cogit>>#followForwardedLiteralsIn: */ +void +followForwardedLiteralsIn(CogMethod *cogMethod) +{ + unsigned char wasInYoungReferrers; + + /* begin ensureWritableCodeZone */ + wasInYoungReferrers = (cogMethod->cmRefersToYoung); + followForwardedLiteralsImplementationIn(cogMethod); + if (wasInYoungReferrers + && (!((cogMethod->cmRefersToYoung)))) { + pruneYoungReferrers(); + } + + /* begin ensureExecutableCodeZone */ +} + + +/* To avoid runtime checks on literal variable and literal accesses in == and + ~~, + we follow literals in methods having movable literals in the postBecome + action. To avoid scanning every method, we annotate cogMethods with the + cmHasMovableLiteral flag. */ + + /* Cogit>>#followMovableLiteralsAndUpdateYoungReferrers */ +void +followMovableLiteralsAndUpdateYoungReferrers(void) +{ + CogMethod *cogMethod; + + assert(kosherYoungReferrers()); + + /* methodZone firstBogusYoungReferrer + methodZone occurrencesInYoungReferrers: methodZone firstBogusYoungReferrer */ + codeModified = 0; + + /* begin ensureWritableCodeZone */ + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (!((((cogMethod->cmType)) == CMFree) + || (((cogMethod->cmType)) == CMMethodFlaggedForBecome))) { + if ((cogMethod->cmHasMovableLiteral)) { + followForwardedLiteralsImplementationIn(cogMethod); + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + pruneYoungReferrers(); + if (codeModified) { + flushICacheFromto(backEnd, ((usqInt)codeBase), freeStart()); + } + + /* After updating oops in inline caches we need to flush the icache. + And ensure code zone is executable. May have pruned young referrers... */ + + /* begin ensureExecutableCodeZone */ +} + + +/* N.B. because becomeEffectFlags indicates whether jitted methods were + becommed or not, if this method is called flagged methods exist, will be + freed, and so on. So there is no need to check. Just do it. */ + + /* Cogit>>#freeBecomeFlaggedMethods */ +void +freeBecomeFlaggedMethods(void) +{ + CogMethod *cogMethod; + + /* begin ensureWritableCodeZone */ + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) == CMMethodFlaggedForBecome) { + freeMethod(cogMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + unlinkSendsToFree(); + + /* begin ensureExecutableCodeZone */ +} + + /* Cogit>>#freeCogMethod: */ +void +freeCogMethod(CogMethod *cogMethod) +{ + moveProfileToMethods(); + freeMethod(cogMethod); + + /* begin ensureExecutableCodeZone */ +} + + +/* Free machine-code methods whose compiled methods are unmarked + and open PICs whose selectors are not marked, and closed PICs that + refer to unmarked objects. */ + + /* Cogit>>#freeUnmarkedMachineCode */ +void +freeUnmarkedMachineCode(void) +{ + CogMethod *cogMethod; + sqInt freedMethod; + + moveProfileToMethods(); + freedMethod = 0; + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if ((((cogMethod->cmType)) >= CMMethod) + && (!(isMarked((cogMethod->methodObject))))) { + freedMethod = 1; + freeMethod(cogMethod); + } + if ((((cogMethod->cmType)) == CMOpenPIC) + && ((!(isImmediate((cogMethod->selector)))) + && (!(isMarked((cogMethod->selector)))))) { + freedMethod = 1; + freeMethod(cogMethod); + } + if ((((cogMethod->cmType)) == CMClosedPIC) + && (closedPICRefersToUnmarkedObject(cogMethod))) { + freedMethod = 1; + freeMethod(cogMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if (freedMethod) { + unlinkSendsToFree(); + } + + /* begin ensureExecutableCodeZone */ +} + + +/* Call ceSendMustBeBooleanTo: via the relevant trampoline. */ + + /* Cogit>>#genCallMustBeBooleanFor: */ +static NoDbgRegParms AbstractInstruction * +genCallMustBeBooleanFor(sqInt boolean) +{ + AbstractInstruction *abstractInstruction; + sqInt callTarget; + + callTarget = (boolean == (falseObject()) + ? ceSendMustBeBooleanAddFalseTrampoline + : ceSendMustBeBooleanAddTrueTrampoline); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, callTarget); + (abstractInstruction->annotation = IsRelativeCall); + return abstractInstruction; +} + + /* Cogit>>#genConditionalBranch:operand: */ +static NoDbgRegParms AbstractInstruction * +genConditionalBranchoperand(sqInt opcode, sqInt operandOne) +{ + return genoperand(opcode, operandOne); +} + + +/* An enilopmart (the reverse of a trampoline) is a piece of code that makes + the system-call-like transition from the C runtime into generated machine + code. The desired arguments and entry-point are pushed on a stackPage's + stack. The enilopmart pops off the values to be loaded into registers and + then executes a return instruction to pop off the entry-point and jump to + it. + BEFORE AFTER (stacks grow down) + whatever stackPointer -> whatever + target address => reg1 = reg1val, etc + reg1val pc = target address + reg2val + stackPointer -> reg3val */ + + /* Cogit>>#genEnilopmartFor:and:and:forCall:called: */ +static NoDbgRegParms void +(*genEnilopmartForandandforCallcalled(sqInt regArg1, sqInt regArg2OrNone, sqInt regArg3OrNone, sqInt forCall, char *trampolineName))(void) +{ + sqInt endAddress; + usqInt enilopmart; + sqInt size; + + zeroOpcodeIndex(); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, varBaseAddress, VarBaseReg); + genLoadStackPointers(backEnd); + if (regArg3OrNone != NoReg) { + /* PopR: */ + genoperand(PopR, regArg3OrNone); + } + if (regArg2OrNone != NoReg) { + /* PopR: */ + genoperand(PopR, regArg2OrNone); + } + + /* PopR: */ + genoperand(PopR, regArg1); + genEnilopmartReturn(forCall); + computeMaximumSizes(); + size = generateInstructionsAt(methodZoneBase); + endAddress = outputInstructionsAt(methodZoneBase); + assert((methodZoneBase + size) == endAddress); + enilopmart = methodZoneBase; + methodZoneBase = alignUptoRoutineBoundary(endAddress); + stopsFromto(backEnd, endAddress, methodZoneBase - 1); + recordGeneratedRunTimeaddress(trampolineName, enilopmart); + return ((void (*)(void)) enilopmart); +} + + +/* An enilopmart (the reverse of a trampoline) is a piece of code that makes + the system-call-like transition from the C runtime into generated machine + code. At the point the enilopmart enters machine code via a return + instruction, any argument registers have been loaded with their values and + the stack, if + for call, looks like + ret pc + stackPointer -> target address + + and if not for call, looks like + whatever + stackPointer -> target address + + If forCall and running on a CISC, ret pc must be left on the stack. If + forCall and + running on a RISC, ret pc must be popped into LinkReg. In either case, + target address must be removed from the stack and jumped/returned to. */ + + /* Cogit>>#genEnilopmartReturn: */ +static NoDbgRegParms void +genEnilopmartReturn(sqInt forCall) +{ + /* RetN: */ + genoperand(RetN, 0); +} + + +/* Generate the routine that writes the current values of the C frame and + stack pointers into + variables. These are used to establish the C stack in trampolines back + into the C run-time. + This routine assumes the system's frame pointer is the same as that used + in generated code. */ + + /* Cogit>>#generateCaptureCStackPointers: */ +static NoDbgRegParms NeverInline void +generateCaptureCStackPointers(sqInt captureFramePointer) +{ + sqInt callerSavedReg; + usqIntptr_t fixupSize; + sqInt offset; + usqIntptr_t opcodeSize; + sqInt pushedVarBaseReg; + usqInt startAddress; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 32; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + startAddress = methodZoneBase; + + /* Must happen first; value may be used in accessing any of the following addresses */ + callerSavedReg = 0; + pushedVarBaseReg = 0; + if (!(((CallerSavedRegisterMask & ((1U << VarBaseReg))) != 0))) { + /* VarBaseReg is not caller-saved; must save and restore it, either by using an available caller-saved reg or push/pop. */ + + /* TempReg used below */ + callerSavedReg = availableRegisterOrNoneIn(((ABICallerSavedRegisterMask | ((1U << TempReg))) - ((1U << TempReg)))); + if (callerSavedReg == NoReg) { + gNativePushR(VarBaseReg); + pushedVarBaseReg = 1; + } + else { + /* MoveR:R: */ + genoperandoperand(MoveRR, VarBaseReg, callerSavedReg); + } + } + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, varBaseAddress, VarBaseReg); + if (captureFramePointer) { + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, FPReg, cFramePointerAddress()); + } + + /* Capture the stack pointer prior to the call. If we've pushed VarBaseReg take that into account. */ + offset = (pushedVarBaseReg + ? (leafCallStackPointerDelta(backEnd)) + BytesPerWord + : leafCallStackPointerDelta(backEnd)); + + /* begin LoadEffectiveAddressMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(LoadEffectiveAddressMwrR, offset, NativeSPReg, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, cStackPointerAddress()); + if (!(((CallerSavedRegisterMask & ((1U << VarBaseReg))) != 0))) { + if (pushedVarBaseReg) { + gNativePopR(VarBaseReg); + } + else { + /* MoveR:R: */ + genoperandoperand(MoveRR, callerSavedReg, VarBaseReg); + } + } + gNativeRetN(0); + outputInstructionsForGeneratedRuntimeAt(startAddress); + + /* This also implicitly flushes the read/write mapped dual zone to the read/execute zone. */ + flushICacheFromto(backEnd, ((usqInt)startAddress), ((usqInt)methodZoneBase)); + recordGeneratedRunTimeaddress("ceCaptureCStackPointers", startAddress); + ceCaptureCStackPointers = ((void (*)(void)) startAddress); +} + + +/* Generate the prototype ClosedPIC to determine how much space a full closed + PIC takes. + When we first allocate a closed PIC it only has one or two cases and we + want to grow it. + So we have to determine how big a full one is before hand. */ +/* stack allocate the various collections so that they + are effectively garbage collected on return. */ + + /* Cogit>>#generateClosedPICPrototype */ +static void +generateClosedPICPrototype(void) +{ + CogMethod *cPIC; + AbstractInstruction * cPICEndOfCodeLabel; + sqInt endAddress; + AbstractInstruction * endCPICCase1; + usqIntptr_t fixupSize; + sqInt h; + AbstractInstruction *jumpNext; + sqInt jumpTarget; + sqInt numArgs; + usqIntptr_t opcodeSize; + sqInt wordConstant; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = MaxCPICCases * 9; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + (methodLabel->address = methodZoneBase); + (methodLabel->dependent = null); + + /* begin compileClosedPICPrototype */ + compilePICAbort((numArgs = 0)); + jumpNext = compileCPICEntry(); + + /* At the end of the entry code we need to jump to the first case code, which is actually the last chunk. + On each extension we must update this jump to move back one case. */ + wordConstant = firstPrototypeMethodOop(); + + /* begin MoveUniqueCw:R: */ + /* gen:uniqueLiteral:operand: */ + /* uniqueLiteral:forInstruction: */ + genoperandoperand(MoveCwR, wordConstant, SendNumArgsReg); + jumpTarget = (((methodZoneBase + (youngReferrers)) / 2) - 13262352) + 13262352; + + /* begin JumpLong: */ + genoperand(JumpLong, jumpTarget); + endCPICCase0 = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + for (h = 1; h < MaxCPICCases; h += 1) { + if (h == (MaxCPICCases - 1)) { + jmpTarget(jumpNext, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } + wordConstant = (subsequentPrototypeMethodOop()) + h; + + /* begin MoveUniqueCw:R: */ + /* gen:uniqueLiteral:operand: */ + /* uniqueLiteral:forInstruction: */ + genoperandoperand(MoveCwR, wordConstant, SendNumArgsReg); + + /* 16rBABE1F15+h is the class tag for the Nth case */ + + /* #CmpC32:R: #gen:literal32:operand: #checkLiteral32:forInstruction: */ + genoperandoperand(CmpC32R, 0xBABE1F15U + h, TempReg); + jumpTarget = ((((methodZoneBase + (youngReferrers)) / 2) - 13262352) + 13262352) + (h * 16); + + /* begin JumpLongZero: */ + genConditionalBranchoperand(JumpLongZero, ((sqInt)jumpTarget)); + if (h == 1) { + endCPICCase1 = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + } + } + + /* #MoveCw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveCwR, (methodLabel->address), ClassReg); + jumpTarget = cPICMissTrampolineFor(numArgs); + + /* begin JumpLong: */ + genoperand(JumpLong, jumpTarget); + cPICEndOfCodeLabel = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + computeMaximumSizes(); + cPIC = ((CogMethod *) methodZoneBase); + closedPICSize = (sizeof(CogMethod)) + (generateInstructionsAt(methodZoneBase + (sizeof(CogMethod)))); + endAddress = outputInstructionsAt(methodZoneBase + (sizeof(CogMethod))); + assert((methodZoneBase + closedPICSize) == endAddress); + firstCPICCaseOffset = ((endCPICCase0->address)) - methodZoneBase; + cPICEndOfCodeOffset = ((cPICEndOfCodeLabel->address)) - methodZoneBase; + cPICCaseSize = ((endCPICCase1->address)) - ((endCPICCase0->address)); + cPICEndSize = closedPICSize - (((MaxCPICCases - 1) * cPICCaseSize) + firstCPICCaseOffset); + closedPICSize = roundUpToMethodAlignment(backEnd, closedPICSize); + assert(((picInterpretAbort->address)) == (((methodLabel->address)) + (picInterpretAbortOffset()))); + assert((expectedClosedPICPrototype(cPIC)) == 0); + + /* tpr this is a little tiresome but after any assert checking we need to 0 out the case0 objRef rather than leaving 16r5EAF00D lying around */ + storeLiteralbeforeFollowingAddress(backEnd, 0, ((endCPICCase0->address)) - (jumpLongByteSize(backEnd))); + + /* finish up */ + recordGeneratedRunTimeaddress("cPICPrototype", methodZoneBase); + + /* update the methodZoneBase so we keep the prototype around for later use; it is copied into new PICs */ + methodZoneBase = alignUptoRoutineBoundary(endAddress); + + /* self disassembleFrom: cPICPrototype + (self sizeof: CogMethod) to: cPICPrototype + closedPICSize - 1 */ + cPICPrototype = cPIC; +} + + +/* We handle jump sizing simply. First we make a pass that asks each + instruction to compute its maximum size. Then we make a pass that + sizes jumps based on the maxmimum sizes. Then we make a pass + that fixes up jumps. When fixing up a jump the jump is not allowed to + choose a smaller offset but must stick to the size set in the second pass. */ + + /* Cogit>>#generateCogFullBlock */ +static CogMethod * +generateCogFullBlock(void) +{ + sqInt codeSize; + usqIntptr_t headerSize; + sqInt mapSize; + CogMethod *method; + sqInt result; + usqInt startAddress; + int totalSize; + + headerSize = sizeof(CogMethod); + (methodLabel->address = freeStart()); + computeMaximumSizes(); + concretizeAt(methodLabel, freeStart()); + codeSize = generateInstructionsAt(((methodLabel->address)) + headerSize); + mapSize = generateMapAtstart(null, ((methodLabel->address)) + cbNoSwitchEntryOffset); + totalSize = roundUpToMethodAlignment(backEnd, (headerSize + codeSize) + mapSize); + if (totalSize > MaxMethodSize) { + return ((CogMethod *) MethodTooBig); + } + startAddress = allocate(totalSize); + if (!startAddress) { + return ((CogMethod *) InsufficientCodeSpace); + } + assert((startAddress + cbEntryOffset) == ((fullBlockEntry->address))); + assert((startAddress + cbNoSwitchEntryOffset) == ((fullBlockNoContextSwitchEntry->address))); + result = outputInstructionsAt(startAddress + headerSize); + assert(((startAddress + headerSize) + codeSize) == result); + padIfPossibleWithStopsFromto(backEnd, result, ((startAddress + totalSize) - mapSize) - 1); + generateMapAtstart((startAddress + totalSize) - 1, startAddress + cbNoSwitchEntryOffset); + method = ((CogMethod *) ((((usqInt)startAddress)) + codeToDataDelta)); + fillInMethodHeadersizeselector(method, totalSize, nilObject()); + (method->cpicHasMNUCaseOrCMIsFullBlock = 1); + flushICacheFromto(backEnd, startAddress, startAddress + totalSize); + return ((CogMethod *) startAddress); +} + + +/* We handle jump sizing simply. First we make a pass that asks each + instruction to compute its maximum size. Then we make a pass that + sizes jumps based on the maxmimum sizes. Then we make a pass + that fixes up jumps. When fixing up a jump the jump is not allowed to + choose a smaller offset but must stick to the size set in the second pass. */ + + /* Cogit>>#generateCogMethod: */ +static NoDbgRegParms CogMethod * +generateCogMethod(sqInt selector) +{ + sqInt codeSize; + usqIntptr_t headerSize; + sqInt mapSize; + sqInt result; + usqInt startAddress; + int totalSize; + + headerSize = sizeof(CogMethod); + (methodLabel->address = freeStart()); + computeMaximumSizes(); + concretizeAt(methodLabel, freeStart()); + codeSize = generateInstructionsAt(((methodLabel->address)) + headerSize); + mapSize = generateMapAtstart(null, ((methodLabel->address)) + cmNoCheckEntryOffset); + totalSize = roundUpToMethodAlignment(backEnd, (headerSize + codeSize) + mapSize); + if (totalSize > MaxMethodSize) { + return ((CogMethod *) MethodTooBig); + } + startAddress = allocate(totalSize); + if (!startAddress) { + return ((CogMethod *) InsufficientCodeSpace); + } + assert((startAddress + cmEntryOffset) == ((entry->address))); + assert((startAddress + cmNoCheckEntryOffset) == ((noCheckEntry->address))); + result = outputInstructionsAt(startAddress + headerSize); + assert(((startAddress + headerSize) + codeSize) == result); + padIfPossibleWithStopsFromto(backEnd, result, ((startAddress + totalSize) - mapSize) - 1); + generateMapAtstart((startAddress + totalSize) - 1, startAddress + cmNoCheckEntryOffset); + fillInBlockHeadersAt(startAddress); + fillInMethodHeadersizeselector(((CogMethod *) ((((usqInt)startAddress)) + codeToDataDelta)), totalSize, selector); + + /* This also implicitly flushes the read/write mapped dual zone to the read/execute zone. */ + flushICacheFromto(backEnd, startAddress, startAddress + totalSize); + return ((CogMethod *) startAddress); +} + + +/* Generate the method map at addressrNull (or compute it if addressOrNull is + null). Answer the length of the map in byes. Each entry in the map is in + two parts. In the + least signficant bits are a displacement of how far from the start or + previous entry, + unless it is an IsAnnotationExtension byte, in which case those bits are + the extension. + In the most signficant bits are the type of annotation at the point + reached. A null + byte ends the map. */ + + /* Cogit>>#generateMapAt:start: */ +static NoDbgRegParms sqInt +generateMapAtstart(usqInt addressOrNull, usqInt startAddress) +{ + unsigned char annotation; + sqInt delta; + sqInt i; + AbstractInstruction *instruction; + sqInt length; + usqInt location; + sqInt mapEntry; + sqInt maxDelta; + usqInt mcpc; + + delta = 0; + length = 0; + location = startAddress; + for (i = 0; i < opcodeIndex; i += 1) { + instruction = abstractInstructionAt(i); + if ((annotation = (instruction->annotation))) { + mcpc = ((instruction->address)) + ((instruction->machineCodeSize)); + while (((delta = (mcpc - location) / 1 /* codeGranularity */)) > DisplacementMask) { + maxDelta = (((((delta < MaxX2NDisplacement) ? delta : MaxX2NDisplacement)) | DisplacementMask) - DisplacementMask); + assert((((usqInt)(maxDelta)) >> AnnotationShift) <= DisplacementMask); + if (addressOrNull) { + /* begin addToMap:instruction:byte:at:for: */ + codeByteAtput((void *)(addressOrNull - length),(((usqInt)(maxDelta)) >> AnnotationShift) + DisplacementX2N); + } + location += maxDelta; + length += 1; + } + if (addressOrNull) { + mapEntry = delta + ((((usqInt)((((annotation < IsSendCall) ? annotation : IsSendCall))) << AnnotationShift))); + + /* begin addToMap:instruction:byte:at:for: */ + codeByteAtput((void *)(addressOrNull - length),mapEntry); + } + location += delta; + length += 1; + if (annotation > IsSendCall) { + if (addressOrNull) { + mapEntry = ((((usqInt)(IsAnnotationExtension) << AnnotationShift))) + (annotation - IsSendCall); + + /* begin addToMap:instruction:byte:at:for: */ + codeByteAtput((void *)(addressOrNull - length),mapEntry); + } + length += 1; + } + } + } + if (addressOrNull) { + /* begin addToMap:instruction:byte:at:for: */ + codeByteAtput((void *)(addressOrNull - length),MapEnd); + } + return length + 1; +} + + +/* Generate the prototype OpenPIC to determine how much space an open PIC + takes. + */ +/* stack allocate the various collections so that they + are effectively garbage collected on return. */ + + /* Cogit>>#generateOpenPICPrototype */ +static void +generateOpenPICPrototype(void) +{ + sqInt codeSize; + usqIntptr_t fixupSize; + sqInt mapSize; + usqIntptr_t opcodeSize; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 100; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + (methodLabel->address = methodZoneBase); + (methodLabel->dependent = null); + + /* Need a real selector here so that the map accomodates the annotations for the selector. + Use self numRegArgs to generate the longest possible code sequence due to + genPushRegisterArgsForNumArgs: */ + compileOpenPICnumArgs(specialSelector(0), numRegArgs()); + computeMaximumSizes(); + concretizeAt(methodLabel, methodZoneBase); + codeSize = generateInstructionsAt(methodZoneBase + (sizeof(CogMethod))); + mapSize = generateMapAtstart(null, methodZoneBase + cmNoCheckEntryOffset); + + /* self cCode: '' + inSmalltalk: + [| end | + end := self outputInstructionsAt: methodZoneBase + headerSize. + self disassembleFrom: methodZoneBase + (self sizeof: CogMethod) to: end - 1. + self halt] */ + openPICSize = (roundUpLength((sizeof(CogMethod)) + codeSize)) + (roundUpToMethodAlignment(backEnd, mapSize)); +} + + +/* Generate a routine ceCaptureCStackPointers that will capture the C stack + pointer, and, if it is in use, the C frame pointer. These are used in + trampolines to call + run-time routines in the interpreter from machine-code. */ + + /* Cogit>>#generateStackPointerCapture */ +static void +generateStackPointerCapture(void) +{ + usqInt oldMethodZoneBase; + sqInt oldTrampolineTableIndex; + + +# if defined(cFramePointerInUse) + assertCStackWellAligned(); + generateCaptureCStackPointers(cFramePointerInUse); +# else + /* For the benefit of the following assert, assume the minimum at first. */ + cFramePointerInUse = 0; + assertCStackWellAligned(); + oldMethodZoneBase = methodZoneBase; + oldTrampolineTableIndex = trampolineTableIndex; + generateCaptureCStackPointers(1); + ceCaptureCStackPointers(); + if (!((cFramePointerInUse = checkIfCFramePointerInUse()))) { + methodZoneBase = oldMethodZoneBase; + trampolineTableIndex = oldTrampolineTableIndex; + generateCaptureCStackPointers(0); + } +# endif // defined(cFramePointerInUse) + + + /* begin ensureWritableCodeZone */ + assertCStackWellAligned(); +} + + +/* Generate the run-time entries and exits at the base of the native code + zone and update the base. + Read the class-side method trampolines for documentation on the various + trampolines + */ + + /* Cogit>>#generateTrampolines */ +static void +generateTrampolines(void) +{ + usqIntptr_t fixupSize; + usqInt methodZoneStart; + usqIntptr_t opcodeSize; + + methodZoneStart = methodZoneBase; + (methodLabel->address = methodZoneStart); + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 80; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + setHasYoungReferent(0); + maybeGenerateSelectorIndexDereferenceRoutine(); + generateSendTrampolines(); + generateMissAbortTrampolines(); + generateObjectRepresentationTrampolines(); + generateRunTimeTrampolines(); + generateEnilopmarts(); + generateTracingTrampolines(); +} + + /* Cogit>>#generatorForPC: */ +static NoDbgRegParms BytecodeDescriptor * +generatorForPC(sqInt pc) +{ + return generatorAt(bytecodeSetOffset + (fetchByteofObject(pc, methodObj))); +} + + +/* Generate a pair of routines that answer the frame pointer, and the stack + pointer immediately + after a leaf call, used for checking stack pointer alignment, frame + pointer usage, etc. N.B. + these are exported to the CoInterpreter et al via Cogit + class>>mustBeGlobal:. + */ + + /* Cogit>>#genGetLeafCallStackPointers */ +static void +genGetLeafCallStackPointers(void) +{ + usqIntptr_t fixupSize; + usqIntptr_t opcodeSize; + usqInt startAddress; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 4; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + startAddress = methodZoneBase; + + /* MoveR:R: */ + genoperandoperand(MoveRR, FPReg, ABIResultReg); + + /* RetN: */ + genoperand(RetN, 0); + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress("ceGetFP", startAddress); + ceGetFP = ((usqIntptr_t (*)(void)) startAddress); + startAddress = methodZoneBase; + zeroOpcodeIndex(); + + /* MoveR:R: */ + genoperandoperand(MoveRR, NativeSPReg, ABIResultReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, leafCallStackPointerDelta(backEnd), ABIResultReg); + + /* RetN: */ + genoperand(RetN, 0); + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress("ceGetSP", startAddress); + ceGetSP = ((usqIntptr_t (*)(void)) startAddress); +} + + +/* Generate the abort for a PIC. This abort performs either a call of + ceInterpretMethodFromPIC:receiver: to handle invoking an uncogged target + or a call of ceMNUFromPICMNUMethod:receiver: to handle an MNU dispatch + in a closed PIC. It distinguishes the two by testing ClassReg. If the + register is zero then this is an MNU. + + This poses a problem in 32-bit Spur, where zero is the cache tag for + immediate characters (tag pattern 2r10) because SmallIntegers have tag + patterns 2r11 + and 2r01, so anding with 1 reduces these to 0 & 1. We solve the ambiguity + by patching send sites with a 0 cache tag to open PICs instead of closed + PICs. */ + + /* Cogit>>#genInnerPICAbortTrampoline: */ +static NoDbgRegParms usqInt +genInnerPICAbortTrampoline(char *name) +{ + AbstractInstruction *jumpMNUCase; + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0 /* picAbortDiscriminatorValue */, ClassReg); + jumpMNUCase = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + compileTrampolineFornumArgsargargargargregsToSavepushLinkRegresultReg(ceInterpretMethodFromPICreceiver, 2, SendNumArgsReg, ReceiverResultReg, null, null, 0 /* emptyRegisterMask */, 0, NoReg); + jmpTarget(jumpMNUCase, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceMNUFromPICMNUMethodreceiver, name, 2, SendNumArgsReg, ReceiverResultReg, null, null, 0 /* emptyRegisterMask */, 0, NoReg, 1); +} + + +/* Switch to the C stack (do *not* save the Smalltalk stack pointers; + this is the caller's responsibility), and invoke interpret PDQ. */ + + /* Cogit>>#genInvokeInterpretTrampoline */ +static void +(*genInvokeInterpretTrampoline(void))(void) +{ + usqInt startAddress; + + startAddress = methodZoneBase; + zeroOpcodeIndex(); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, varBaseAddress, VarBaseReg); + if (cFramePointerInUse) { + genLoadCStackPointers(backEnd); + } + else { + genLoadCStackPointer(backEnd); + } + + /* Sideways call interpret so that the stack looks correct, for exception handling etc */ + + /* begin genMarshallNArgs:arg:arg:arg:arg: */ + + /* WIN64 ABI always reserves shadow space on the stack for callee to save up to 4 register parameters */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, cReturnAddressAddress(), ABIResultReg); + + /* #genSubstituteReturnAddressR: #PushR: */ + genoperand(PushR, ABIResultReg); + + /* #JumpFullRT: #JumpFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(JumpFull, ((sqInt)(((usqInt)interpret)))); + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress("ceInvokeInterpret", startAddress); + return ((void (*)(void)) startAddress); +} + + +/* The in-line cache for a send is implemented as a constant load into + ClassReg. We always use a 32-bit load, even in 64-bits. + + In the initial (unlinked) state the in-line cache is notionally loaded + with the selector. + But since in 64-bits an arbitrary selector oop won't fit in a 32-bit + constant load, we + instead load the cache with the selector's index, either into the literal + frame of the + current method, or into the special selector array. Negative values are + 1-relative indices into the special selector array. + + When a send is linked, the load of the selector, or selector index, is + overwritten with a + load of the receiver's class, or class tag. Hence, the 64-bit VM is + currently constrained + to use class indices as cache tags. If out-of-line literals are used, + distinct caches /must + not/ share acche locations, for if they do, send cacheing will be confused + by the sharing. + Hence we use the MoveUniqueC32:R: instruction that will not share literal + locations. */ + + /* Cogit>>#genLoadInlineCacheWithSelector: */ +static NoDbgRegParms void +genLoadInlineCacheWithSelector(sqInt selectorIndex) +{ + sqInt cacheValue; + + assert((selectorIndex < 0 + ? (((-selectorIndex) >= 1) && ((-selectorIndex) <= (numSpecialSelectors()))) + : ((selectorIndex >= 0) && (selectorIndex <= ((literalCountOf(methodObj)) - 1))))); + cacheValue = selectorIndex; + + /* #MoveUniqueC32:R: #gen:uniqueLiteral32:operand: #uniqueLiteral32:forInstruction: */ + genoperandoperand(MoveC32R, cacheValue, ClassReg); +} + + /* Cogit>>#genReturnToInterpreterTrampoline */ +static usqInt +genReturnToInterpreterTrampoline(void) +{ + usqInt startAddress; + + startAddress = methodZoneBase; + zeroOpcodeIndex(); + + /* Push the result, set the instruction pointer to the interpreter frame's saved ip, + set the method and the bytecode set offset, then call interpret. */ + + /* PushR: */ + genoperand(PushR, ReceiverResultReg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, FoxIFSavedIP, FPReg, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, instructionPointerAddress()); + genSmalltalkToCStackSwitch(0); + + /* pushLinkReg + Sideways call interpret so that the stack looks correct, for exception handling etc */ + + /* begin genMarshallNArgs:arg:arg:arg:arg: */ + + /* WIN64 ABI always reserves shadow space on the stack for callee to save up to 4 register parameters */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, cReturnAddressAddress(), ABIResultReg); + + /* #genSubstituteReturnAddressR: #PushR: */ + genoperand(PushR, ABIResultReg); + + /* #JumpFullRT: #JumpFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(JumpFull, ((sqInt)(((usqInt)interpret)))); + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress("ceReturnToInterpreterTrampoline", startAddress); + return startAddress; +} + + +/* If the client requires, then on an ARM-like RISC processor, the return + address needs to + be pushed to the stack so that the interpreter sees the same stack layout + as on CISC. + */ + + /* Cogit>>#genSmalltalkToCStackSwitch: */ +static NoDbgRegParms sqInt +genSmalltalkToCStackSwitch(sqInt pushLinkReg) +{ + genSaveStackPointers(backEnd); + if (cFramePointerInUse) { + genLoadCStackPointers(backEnd); + } + else { + genLoadCStackPointer(backEnd); + } + return 0; +} + + +/* Generate a trampoline with up to four arguments. Generate either a call or + a jump to aRoutineOrNil + as requested by callJumpBar. If generating a call and resultRegOrNone is + not NoReg pass the C result + back in resultRegOrNone. + Hack: a negative value indicates an abstract register, a non-negative + value indicates a constant. */ + + /* Cogit>>#genTrampolineFor:called:numArgs:arg:arg:arg:arg:regsToSave:pushLinkReg:resultReg:appendOpcodes: */ +static NoDbgRegParms usqInt +genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(void *aRoutine, char *trampolineName, sqInt numArgs, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3, sqInt regMask, sqInt pushLinkReg, sqInt resultRegOrNone, sqInt appendBoolean) +{ + usqInt startAddress; + + startAddress = methodZoneBase; + if (!appendBoolean) { + zeroOpcodeIndex(); + } + compileTrampolineFornumArgsargargargargregsToSavepushLinkRegresultReg(aRoutine, numArgs, regOrConst0, regOrConst1, regOrConst2, regOrConst3, regMask, pushLinkReg, resultRegOrNone); + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress(trampolineName, startAddress); + recordRunTimeObjectReferences(); + + /* begin assertValidDualZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + assertCoherentCodeAtdelta(backEnd, codeBase + cmNoCheckEntryOffset, codeToDataDelta); +# endif + + return startAddress; +} + + +/* To return from a trampoline call we have to take the return address off + the stack, + iof it has been saved */ + + /* Cogit>>#genTrampolineReturn: */ +static NoDbgRegParms void +genTrampolineReturn(sqInt lnkRegWasPushed) +{ + /* RetN: */ + genoperand(RetN, 0); +} + + +/* */ + + /* Cogit>>#gen: */ +static NoDbgRegParms AbstractInstruction * +gen(sqInt opcode) +{ + AbstractInstruction *abstractInstruction; + + assert(opcodeIndex < numAbstractOpcodes); + abstractInstruction = abstractInstructionAt(opcodeIndex); + opcodeIndex += 1; + (abstractInstruction->opcode = opcode); + return abstractInstruction; +} + + +/* */ +/* */ + + /* Cogit>>#gen:operand: */ +static NoDbgRegParms AbstractInstruction * +genoperand(sqInt opcode, sqInt operand) +{ + AbstractInstruction *abstractInstruction; + + assert(opcodeIndex < numAbstractOpcodes); + abstractInstruction = abstractInstructionAt(opcodeIndex); + opcodeIndex += 1; + (abstractInstruction->opcode = opcode); + ((abstractInstruction->operands))[0] = operand; + return abstractInstruction; +} + + +/* */ +/* */ +/* */ + + /* Cogit>>#gen:operand:operand: */ +static NoDbgRegParms AbstractInstruction * +genoperandoperand(sqInt opcode, sqInt operandOne, sqInt operandTwo) +{ + AbstractInstruction *abstractInstruction; + + assert(opcodeIndex < numAbstractOpcodes); + abstractInstruction = abstractInstructionAt(opcodeIndex); + opcodeIndex += 1; + (abstractInstruction->opcode = opcode); + ((abstractInstruction->operands))[0] = operandOne; + ((abstractInstruction->operands))[1] = operandTwo; + return abstractInstruction; +} + + +/* */ +/* */ +/* */ +/* */ + + /* Cogit>>#gen:operand:operand:operand: */ +static NoDbgRegParms AbstractInstruction * +genoperandoperandoperand(sqInt opcode, sqInt operandOne, sqInt operandTwo, sqInt operandThree) +{ + AbstractInstruction *abstractInstruction; + + assert(opcodeIndex < numAbstractOpcodes); + abstractInstruction = abstractInstructionAt(opcodeIndex); + opcodeIndex += 1; + (abstractInstruction->opcode = opcode); + ((abstractInstruction->operands))[0] = operandOne; + ((abstractInstruction->operands))[1] = operandTwo; + ((abstractInstruction->operands))[2] = operandThree; + return abstractInstruction; +} + + +/* Answer the contents of the code zone as an array of pair-wise element, + address in ascending address order. + Answer a string for a runtime routine or abstract label (beginning, end, + etc), a CompiledMethod for a CMMethod, + or a selector (presumably a Symbol) for a PIC. + If withDetails is true + - answer machine-code to bytecode pc mapping information for methods + - answer class, target pair information for closed PIC + N.B. Since the class tag for the first case of a closed PIC is stored at + the send site, it must be collected + by scanning methods (see + collectCogConstituentFor:Annotation:Mcpc:Bcpc:Method:). Since closed PICs + are never shared they always come after the method that references them, + so we don't need an extra pass + to collect the first case class tags, which are (temporarily) assigned to + each closed PIC's methodObject field. + But we do need to reset the methodObject fields to zero. This is done in + createPICData:, unless memory + runs out, in which case it is done by cleanUpFailingCogCodeConstituents:. */ + + /* Cogit>>#getCogCodeConstituents: */ +static NoDbgRegParms sqInt +getCogCodeConstituents(sqInt withDetails) +{ + CogMethod *cogMethod; + sqInt constituents; + sqInt count; + sqInt dataClass; + sqInt i; + sqInt label; + sqInt profileData; + sqInt value; + + dataClass = 0; + + /* + 3 for start, freeStart and end */ + count = (trampolineTableIndex / 2) + 3; + cogMethod = ((CogMethod *) methodZoneBase); + + /* we /could/ use methodZone's methodCount, but as of 5/22/2025 it isn't decremented + in freeMethod:, so keep things simple and count exactly how many methods exist now. */ + while (cogMethod < (limitZony())) { + if (!(((cogMethod->cmType)) == CMFree)) { + count += 1; + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + constituents = instantiateClassindexableSize(classArray(), count * 2); + if (!constituents) { + return constituents; + } + pushRemappableOop(constituents); + if ((!((label = stringForCString("CogCode")))) + || (!((value = positive64BitIntegerFor(codeBase))))) { + popRemappableOop(); + return null; + } + storePointerUncheckedofObjectwithValue(0, topRemappableOop(), label); + storePointerUncheckedofObjectwithValue(1, topRemappableOop(), value); + for (i = 0; i < trampolineTableIndex; i += 2) { + if ((!((label = stringForCString(trampolineAddresses[i])))) + || (!((value = positive64BitIntegerFor(((usqInt)(trampolineAddresses[i + 1]))))))) { + popRemappableOop(); + return null; + } + storePointerUncheckedofObjectwithValue(2 + i, topRemappableOop(), label); + storePointerUncheckedofObjectwithValue(3 + i, topRemappableOop(), value); + } + count = trampolineTableIndex + 2; + if (withDetails) { + dataClass = classDoubleWordArray(); + if (dataClass == (nilObject())) { + dataClass = null; + } + } + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (!(((cogMethod->cmType)) == CMFree)) { + profileData = /* profileDataFor:withDetails: */ + (((cogMethod->cmType)) >= CMMethod + ? (cogMethod->methodObject) + : (withDetails + && (((cogMethod->cmType)) == CMClosedPIC) + ? createCPICData(cogMethod) + : (cogMethod->selector))); + if (!profileData) { + return cleanUpFailingCogCodeConstituents(cogMethod); + } + storePointerUncheckedofObjectwithValue(count, topRemappableOop(), profileData); + value = (withDetails + ? (dataClass + ? collectCogMethodConstituentOnSpurofClass(cogMethod, dataClass) + : collectCogMethodConstituent(cogMethod)) + : positive64BitIntegerFor(((usqInt)cogMethod))); + if (!value) { + return cleanUpFailingCogCodeConstituents(cogMethod); + } + storePointerUncheckedofObjectwithValue(count + 1, topRemappableOop(), value); + count += 2; + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if ((!((label = stringForCString("CCFree")))) + || (!((value = positive64BitIntegerFor(mzFreeStart))))) { + popRemappableOop(); + return null; + } + storePointerUncheckedofObjectwithValue(count, topRemappableOop(), label); + storePointerUncheckedofObjectwithValue(count + 1, topRemappableOop(), value); + if ((!((label = stringForCString("CCEnd")))) + || (!((value = positive64BitIntegerFor(limitAddress))))) { + popRemappableOop(); + return null; + } + storePointerUncheckedofObjectwithValue(count + 2, topRemappableOop(), label); + storePointerUncheckedofObjectwithValue(count + 3, topRemappableOop(), value); + constituents = popRemappableOop(); + beRootIfOld(constituents); + + /* would like to assert this, but it requires the leak checked be run :-( + self assert: self allMachineCodeObjectReferencesValid. */ + return constituents; +} + + /* Cogit>>#getLiteral: */ +static NoDbgRegParms sqInt +getLiteral(sqInt litIndex) +{ + if (maxLitIndex < litIndex) { + maxLitIndex = litIndex; + } + return literalofMethod(litIndex, methodObj); +} + + /* Cogit>>#getMethodZoneBase */ +#if VMInvestigations +usqInt +getMethodZoneBase(void) +{ + return methodZoneBase; +} +#endif /* VMInvestigations */ + + /* Cogit>>#incrementUsageOfTargetIfLinkedSend:mcpc:ignored: */ +static NoDbgRegParms sqInt +incrementUsageOfTargetIfLinkedSendmcpcignored(sqInt annotation, char *mcpc, sqInt superfluity) +{ + usqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + + if (annotation >= IsSendCall) { + assert(annotation != IsNSSendCall); + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (((targetMethod1->cmUsageCount)) < (CMMaxUsageCount / 2)) { + ((((CogMethod *) ((((usqInt)targetMethod1)) + codeToDataDelta)))->cmUsageCount = ((targetMethod1->cmUsageCount)) + 1); + } + } + } + return 0; +} + + +/* Answer the value to put in an inline-cache that is being loaded with the + selector. Usually this is simply the selector, but in 64-bits the cache is + only 32-bits wide + and so the cache is loaded with the index of the selector. */ +/* First search the special selectors; there are only 32 of them so this + shouldn't take too long. + We could short-circuit this by keeping a hint bit in the target method, or + by maintaining the + maximum range of selector oops in specialSelectors since they're likely to + cluster. + */ + + /* Cogit>>#indexForSelector:in: */ +static NoDbgRegParms sqInt +indexForSelectorin(sqInt selector, CogMethod *cogMethod) +{ + sqInt i; + sqInt methodOop; + sqInt toDoLimit; + + for (i = 0; i < NumSpecialSelectors; i += 1) { + if (selector == (specialSelector(i))) { + return -1 - i; + } + } + methodOop = (cogMethod->methodObject); + + /* Then search the method's literal frame... open code fetchPointer:ofObject: for speed... */ + toDoLimit = literalCountOfMethodHeader((cogMethod->methodHeader)); + for (i = LiteralStart; i <= toDoLimit; i += 1) { + if ((longAt((void *)(((i * BytesPerOop) + BaseHeaderSize) + methodOop))) == selector) { + assert(selector == (literalofMethod(i - 1, methodOop))); + return i - 1; + } + } + error("could not find selector in method when unlinking send site"); + return 0; +} + + /* Cogit>>#initializeCodeZoneFrom:upTo: */ +void +initializeCodeZoneFromupTo(sqInt startAddress, sqInt endAddress) +{ + usqInt endAddressUsqInt; + usqInt startAddressUsqInt; + + /* begin initializeBackend */ + (methodLabel->machineCodeSize = 0); + (methodLabel->opcode = Label); + ((methodLabel->operands))[0] = 0; + ((methodLabel->operands))[1] = 0; + + /* begin initializeVarBase */ + assert((!((registerMaskFor(VarBaseReg)) & CallerSavedRegisterMask))); + varBaseAddress = computeGoodVarBaseAddress(); + assert((stackLimitAddress()) >= varBaseAddress); + assert((cStackPointerAddress()) >= varBaseAddress); + assert((cFramePointerAddress()) >= varBaseAddress); + assert((cReturnAddressAddress()) >= varBaseAddress); + assert((nextProfileTickAddress()) >= varBaseAddress); + + /* If the platform requires flipping between executable and writable + states, the system expects to start in the writable state. */ + + /* begin ensureWritableCodeZone */ + sqMakeMemoryExecutableFromToCodeToDataDelta(startAddress, endAddress, +# if DUAL_MAPPED_CODE_ZONE + (&codeToDataDelta) +# else + null +# endif + ); + codeBase = (methodZoneBase = startAddress); + stopsFromto(backEnd, startAddress, endAddress - 1); + + /* begin manageFrom:to: */ + mzFreeStart = (baseAddress = methodZoneBase); + youngReferrers = (limitAddress = endAddress); + openPICList = null; + methodBytesFreedSinceLastCompaction = 0; + methodCount = 0; + assertValidDualZone(); + detectFeatures(backEnd); + + /* begin maybeGenerateCacheFlush */ + + /* initializeCacheFlush */ + + /* self generateVMOwnerLockFunctions. */ + genGetLeafCallStackPointers(); + generateStackPointerCapture(); + generateTrampolines(); + computeEntryOffsets(); + computeFullBlockEntryOffsets(); + generateClosedPICPrototype(); + alignMethodZoneBase(); + + /* None of the above is executed beyond ceCheckFeatures, so a bulk flush now is the leanest thing to do. */ + flushICacheFromto(backEnd, startAddress, ((usqInt)methodZoneBase)); + startAddressUsqInt = startAddress; + endAddressUsqInt = ((usqInt)methodZoneBase); + + /* begin maybeFlushWritableZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + if (codeToDataDelta > 0) { + flushDCacheFromto(backEnd, startAddressUsqInt, endAddressUsqInt); + } +# endif + + + /* Repeat so that now the methodZone ignores the generated run-time. */ + + /* begin manageFrom:to: */ + mzFreeStart = (baseAddress = methodZoneBase); + youngReferrers = (limitAddress = endAddress); + openPICList = null; + methodBytesFreedSinceLastCompaction = 0; + methodCount = 0; + + /* N.B. this is assumed to be the last thing done in initialization; see Cogit>>initialized. + This is done only to compute openPICSize; the generated code is discarded. */ + generateOpenPICPrototype(); +} + + +/* Answer a usage count that reflects likely long-term usage. + Answer 1 for non-primitives or quick primitives (inst var accessors), + 2 for methods with interpreter primitives, and 3 for compiled primitives. */ + + /* Cogit>>#initialMethodUsageCount */ +static sqInt +initialMethodUsageCount(void) +{ + if ((primitiveIndex == 1) + || (isQuickPrimitiveIndex(primitiveIndex))) { + return 1; + } + if (!(primitiveGeneratorOrNil())) { + return 2; + } + return 3; +} + + +/* Answer a usage count that reflects likely long-term usage. */ + + /* Cogit>>#initialOpenPICUsageCount */ +static int +initialOpenPICUsageCount(void) +{ + return CMMaxUsageCount - 1; +} + + +/* Answer if entryPoint, which must be either the MNU or interpret abort in + cPIC (see compileClosedPICPrototype), invokes the MNU abort. The corollary + is that + it invokes the interpret abort. */ + + /* Cogit>>#intraCPICTarget:invokesMNUIn: */ +static NoDbgRegParms int +intraCPICTargetinvokesMNUIn(sqInt targetMcpc, CogMethod *cPIC) +{ + assert(((targetMcpc >= (((usqInt)(cPIC + 1)))) && (targetMcpc <= ((((usqInt)cPIC)) + cmEntryOffset)))); + return targetMcpc == (((usqInt)(cPIC + 1))); +} + + /* Cogit>>#inverseBranchFor: */ +static NoDbgRegParms sqInt +inverseBranchFor(sqInt opcode) +{ + switch (opcode) { + case JumpLongZero: + return JumpLongNonZero; + + case JumpLongNonZero: + return JumpLongZero; + + case JumpZero: + return JumpNonZero; + + case JumpNonZero: + return JumpZero; + + case JumpNegative: + return JumpNonNegative; + + case JumpNonNegative: + return JumpNegative; + + case JumpOverflow: + return JumpNoOverflow; + + case JumpNoOverflow: + return JumpOverflow; + + case JumpCarry: + return JumpNoCarry; + + case JumpNoCarry: + return JumpCarry; + + case JumpLess: + return JumpGreaterOrEqual; + + case JumpGreaterOrEqual: + return JumpLess; + + case JumpGreater: + return JumpLessOrEqual; + + case JumpLessOrEqual: + return JumpGreater; + + case JumpBelow: + return JumpAboveOrEqual; + + case JumpAboveOrEqual: + return JumpBelow; + + case JumpAbove: + return JumpBelowOrEqual; + + case JumpBelowOrEqual: + return JumpAbove; + + default: + error("Case not found and no otherwise clause"); + } + error("invalid opcode for inverse"); + return 0; +} + + +/* Answer a pair-wise Array of cPIC selector, target for the first case, and + then class index, target for the remaining cases. + If a case in an MNU case then target is the doesNotUnderstand: selector. */ + + /* Cogit>>#investigationArrayForClosedPIC: */ +#if VMInvestigations +static NoDbgRegParms sqInt +investigationArrayForClosedPIC(CogMethod *cPIC) +{ + sqInt data; + usqInt entryPoint; + sqInt i; + sqInt mcpc; + sqInt nc; + + data = instantiateClassindexableSize(classArray(), (nc = ((cPIC->cPICNumCases)) * 2)); + if (!data) { + return null; + } + entryPoint = jumpLongTargetBeforeFollowingAddress(backEnd, addressOfEndOfCaseinCPIC(1, cPIC)); + storePointerUncheckedofObjectwithValue(0, data, (cPIC->selector)); + storePointerUncheckedofObjectwithValue(1, data, investigationTargetForEntryPointin(entryPoint, cPIC)); + + /* store class indices backwards because the PIC grows backwards. */ + for (i = 2; i <= ((cPIC->cPICNumCases)); i += 1) { + mcpc = addressOfEndOfCaseinCPIC(i, cPIC); + storePointerUncheckedofObjectwithValue((nc -= 1), data, investigationTargetForEntryPointin(jumpLongTargetBeforeFollowingAddress(backEnd, mcpc), cPIC)); + storePointerUncheckedofObjectwithValue((nc -= 1), data, (((usqInt)(literal32BeforeFollowingAddress(backEnd, mcpc - (jumpLongConditionalByteSize(backEnd)))) << 3) | 1)); + } + return data; +} +#endif /* VMInvestigations */ + + +/* Resolve a jump dispatch in a closed PIC into either a method or, if it is + an MNU dispatch, the MNU selector. + */ + + /* Cogit>>#investigationTargetForEntryPoint:in: */ +#if VMInvestigations +static NoDbgRegParms sqInt +investigationTargetForEntryPointin(sqInt entryPoint, CogMethod *cPIC) +{ + return (/* containsAddress: */ + ((((usqInt)cPIC)) <= (((usqInt)entryPoint))) + && (((((usqInt)cPIC)) + ((cPIC->blockSize))) >= (((usqInt)entryPoint))) + ? (intraCPICTargetinvokesMNUIn(entryPoint, cPIC) + ? splObj(SelectorDoesNotUnderstand) + : positive64BitIntegerFor(((usqInt)interpret))) + : ((((CogMethod *) (entryPoint - cmNoCheckEntryOffset)))->methodObject)); +} +#endif /* VMInvestigations */ + + +/* Useful for debugging, asserts, etc */ + + /* Cogit>>#isPCWithinMethodZone: */ +int +isPCWithinMethodZone(usqInt address) +{ + return ((address >= methodZoneBase) && (address <= (freeStart()))); +} + + +/* Answer if the instruction preceding retpc is a call instruction. */ + + /* Cogit>>#isSendReturnPC: */ +sqInt +isSendReturnPC(sqInt retpc) +{ + usqInt target; + + if (!(isCallPrecedingReturnPC(backEnd, retpc))) { + return 0; + } + target = callTargetFromReturnAddress(backEnd, retpc); + return (((target >= firstSend) && (target <= lastSend))) + || (((target >= methodZoneBase) && (target <= (freeStart())))); +} + + +/* Floating-point jumps are a little weird on some processors. Defer to + the backEnd to allow it to generate any special code it may need to. */ + + /* Cogit>>#JumpFPEqual: */ +static NoDbgRegParms AbstractInstruction * +gJumpFPEqual(void *jumpTarget) +{ + AbstractInstruction *jumpToTarget; + AbstractInstruction *jumpUnordered; + + /* begin genJumpFPEqual: */ + /* begin genJumpFPOrderedAnd:to: */ + jumpUnordered = genoperand(JumpFPUnordered, ((sqInt)jumpTarget)); + jumpToTarget = genoperand(JumpFPEqual, ((sqInt)jumpTarget)); + jmpTarget(jumpUnordered, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return jumpToTarget; +} + + +/* Floating-point jumps are a little weird on some processors. Defer to + the backEnd to allow it to generate any special code it may need to. */ + + /* Cogit>>#JumpFPGreaterOrEqual: */ +static NoDbgRegParms AbstractInstruction * +gJumpFPGreaterOrEqual(void *jumpTarget) +{ + return genoperand(JumpFPGreaterOrEqual, ((sqInt)jumpTarget)); +} + + +/* Floating-point jumps are a little weird on some processors. Defer to + the backEnd to allow it to generate any special code it may need to. */ + + /* Cogit>>#JumpFPGreater: */ +static NoDbgRegParms AbstractInstruction * +gJumpFPGreater(void *jumpTarget) +{ + return genoperand(JumpFPGreater, ((sqInt)jumpTarget)); +} + + +/* Floating-point jumps are a little weird on some processors. Defer to + the backEnd to allow it to generate any special code it may need to. */ + + /* Cogit>>#JumpFPNotEqual: */ +static NoDbgRegParms AbstractInstruction * +gJumpFPNotEqual(void *jumpTarget) +{ + AbstractInstruction *jumpToTarget; + AbstractInstruction *jumpUnordered; + + /* begin genJumpFPNotEqual: */ + /* begin genJumpFPUnorderedOr:to: */ + jumpToTarget = genoperand(JumpFPNotEqual, ((sqInt)jumpTarget)); + jumpUnordered = genoperand(JumpFPUnordered, ((sqInt)jumpTarget)); + addDependent(jumpToTarget, jumpUnordered); + return jumpToTarget; +} + + /* Cogit>>#LogicalShiftLeftCq:R: */ +static NoDbgRegParms AbstractInstruction * +gLogicalShiftLeftCqR(sqInt quickConstant, sqInt reg) +{ + return genoperandoperand(LogicalShiftLeftCqR, quickConstant, reg); +} + + +/* destReg := srcReg << quickConstant */ + + /* Cogit>>#LogicalShiftLeftCq:R:R: */ +static NoDbgRegParms AbstractInstruction * +gLogicalShiftLeftCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *first; + + first = genoperandoperand(MoveRR, srcReg, destReg); + genoperandoperand(LogicalShiftLeftCqR, quickConstant, destReg); + return first; +} + + +/* destReg := (unsigned)srcReg >> quickConstant */ + + /* Cogit>>#LogicalShiftRightCq:R:R: */ +static NoDbgRegParms AbstractInstruction * +gLogicalShiftRightCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *first; + + first = genoperandoperand(MoveRR, srcReg, destReg); + genoperandoperand(LogicalShiftRightCqR, quickConstant, destReg); + return first; +} + + /* Cogit>>#lastOpcode */ +static AbstractInstruction * +lastOpcode(void) +{ + assert(opcodeIndex > 0); + return abstractInstructionAt(opcodeIndex - 1); +} + + +/* Answer the linked send targetof the send before returnAddress, if valid, + otherwise nil + */ +/* Answer if the instruction preceding retpc is a call instruction. */ + + /* Cogit>>#linkedSendTargetOrNilAt: */ +CogMethod * +linkedSendTargetOrNilAt(sqInt retpc) +{ + usqInt target; + CogMethod *targetCogMethod; + + if (!(isCallPrecedingReturnPC(backEnd, retpc))) { + return null; + } + target = callTargetFromReturnAddress(backEnd, retpc); + if (!(((target >= methodZoneBase) && (target <= (freeStart()))))) { + return null; + } + if ((targetCogMethod = methodFor(((void *)target)))) { + if (((targetCogMethod->cmType)) == CMMethod) { + return targetCogMethod; + } + } + return null; +} + + /* Cogit>>#linkSendAt:in:to:offset:receiver: */ +void +linkSendAtintooffsetreceiver(sqInt callSiteReturnAddress, CogMethod *sendingMethod, CogMethod *targetMethod, sqInt theEntryOffset, sqInt receiver) +{ + sqInt extent; + sqInt inlineCacheTag; + + assert((theEntryOffset == cmEntryOffset) + || (theEntryOffset == cmNoCheckEntryOffset)); + assert(((callSiteReturnAddress >= methodZoneBase) && (callSiteReturnAddress <= (freeStart())))); + + /* begin ensureWritableCodeZone */ + if (theEntryOffset == cmNoCheckEntryOffset) { + extent = rewriteCallAttarget(backEnd, callSiteReturnAddress, (((sqInt)targetMethod)) + cmNoCheckEntryOffset); + } + else { + inlineCacheTag = inlineCacheTagForInstance(receiver); + extent = rewriteInlineCacheAttagtarget(backEnd, callSiteReturnAddress, inlineCacheTag, (((sqInt)targetMethod)) + theEntryOffset); + } + + /* no need to change selector cache tag */ + flushICacheFromto(backEnd, (((usqInt)callSiteReturnAddress)) - extent, ((usqInt)callSiteReturnAddress)); +} + + /* Cogit>>#loadBytesAndGetDescriptor: */ +static NoDbgRegParms BytecodeDescriptor * +loadBytesAndGetDescriptor(sqInt pc) +{ + BytecodeDescriptor *descriptor; + + byte0 = (fetchByteofObject(pc, methodObj)) + bytecodeSetOffset; + descriptor = generatorAt(byte0); + + /* begin loadSubsequentBytesForDescriptor:at: */ + if (((descriptor->numBytes)) > 1) { + byte1 = fetchByteofObject(pc + 1, methodObj); + if (((descriptor->numBytes)) > 2) { + byte2 = fetchByteofObject(pc + 2, methodObj); + if (((descriptor->numBytes)) > 3) { + byte3 = fetchByteofObject(pc + 3, methodObj); + if (((descriptor->numBytes)) > 4) { + notYetImplemented(); + } + } + } + } + return descriptor; +} + + /* Cogit>>#MovePerfCnt64R:L: */ +static NoDbgRegParms AbstractInstruction * +gMovePerfCnt64RL(sqInt destReg, sqInt liveRegisterMask) +{ + assert(BytesPerWord == 8); + return genoperandoperand(MovePerfCnt64RL, destReg, liveRegisterMask); +} + + +/* Answer the address of the null byte at the end of the method map. */ + + /* Cogit>>#mapEndFor: */ +static NoDbgRegParms sqInt +mapEndFor(CogMethod *cogMethod) +{ + sqInt end; + + end = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + while ((byteAt((void *)(end))) != MapEnd) { + end -= 1; + assert(end > (firstMappedPCFor(cogMethod))); + } + return end; +} + + +/* Unlinking/GC/Disassembly support */ +/* most of the time arg is a CogMethod... */ + + /* Cogit>>#mapFor:performUntil:arg: */ +static NoDbgRegParms sqInt +mapForperformUntilarg(CogMethod *cogMethod, sqInt (*functionSymbol)(sqInt annotation, char *mcpc, CogMethod *arg), void *arg) +{ + sqInt annotation; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = functionSymbol(annotation, (((char *) mcpc)), arg); + if (result) { + return result; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + return 0; +} + + +/* Remap all object references in the closed PIC. Answer if any references + are young. + Set codeModified if any modifications are made. */ + + /* Cogit>>#mapObjectReferencesInClosedPIC: */ +static NoDbgRegParms sqInt +mapObjectReferencesInClosedPIC(CogMethod *cPIC) +{ + sqInt i; + sqInt pc; + sqInt refersToYoung; + + pc = addressOfEndOfCaseinCPIC(1, cPIC); + + /* first we check the potential method oop load at the beginning of the CPIC */ + refersToYoung = remapMaybeObjRefInClosedPICAt(pc - (jumpLongByteSize(backEnd))); + + /* We find the end address of the cPICNumCases'th case and can then just step forward by the case size thereafter */ + pc = addressOfEndOfCaseinCPIC((cPIC->cPICNumCases), cPIC); + + /* Next we check the potential class ref in the compare instruction, and the potential method oop load for each case. */ + for (i = 2; i <= ((cPIC->cPICNumCases)); i += 1) { + if (remapMaybeObjRefInClosedPICAt((pc - (jumpLongConditionalByteSize(backEnd))) - (cmpC32RTempByteSize(backEnd)))) { + refersToYoung = 1; + } + pc += cPICCaseSize; + } + return refersToYoung; +} + + +/* Update all references to objects in the generated runtime. */ + + /* Cogit>>#mapObjectReferencesInGeneratedRuntime */ +static void +mapObjectReferencesInGeneratedRuntime(void) +{ + sqInt i; + sqInt literal; + sqInt mappedLiteral; + usqInt mcpc; + + for (i = 0; i < runtimeObjectRefIndex; i += 1) { + mcpc = objectReferencesInRuntime[i]; + literal = literalBeforeFollowingAddress(backEnd, mcpc); + mappedLiteral = remapObject(literal); + if (mappedLiteral != literal) { + /* begin setCodeModified */ + codeModified = 1; + + /* storeLiteral:atAnnotatedAddress:using: */ + storeLiteralbeforeFollowingAddress(backEnd, mappedLiteral, mcpc); + } + } +} + + +/* Update all references to objects in machine code for a become. + Unlike incrementalGC or fullGC a method that does not refer to young may + refer to young as a result of the become operation. Unlike incrementalGC + or fullGC the reference from a Cog method to its methodObject *must not* + change since the two are two halves of the same object. */ + + /* Cogit>>#mapObjectReferencesInMachineCodeForBecome */ +static void +mapObjectReferencesInMachineCodeForBecome(void) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt freedPIC; + sqInt hasYoungObj; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt remappedMethod; + sqInt result; + CogMethod *writableCogMethod; + + hasYoungObj = 0; + codeModified = (freedPIC = 0); + mapObjectReferencesInGeneratedRuntime(); + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + assert(!hasYoungObj); + if (!(((cogMethod->cmType)) == CMFree)) { + assert((cogMethodDoesntLookKosher(cogMethod)) == 0); + writableCogMethod = ((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)); + (writableCogMethod->selector = remapOop((cogMethod->selector))); + if (((cogMethod->cmType)) == CMClosedPIC) { + if ((isYoung((cogMethod->selector))) + || (mapObjectReferencesInClosedPIC(cogMethod))) { + freedPIC = 1; + freeMethod(cogMethod); + } + } + else { + if (isYoung((cogMethod->selector))) { + hasYoungObj = 1; + } + if (((cogMethod->cmType)) >= CMMethod) { + assert(((cogMethod->objectHeader)) == (nullHeaderForMachineCodeMethod())); + remappedMethod = remapOop((cogMethod->methodObject)); + if (remappedMethod != ((cogMethod->methodObject))) { + if (methodHasCogMethod(remappedMethod)) { + error("attempt to become two cogged methods"); + } + if (!(withoutForwardingOnandwithsendToCogit((cogMethod->methodObject), remappedMethod, (cogMethod->cmUsesPenultimateLit), methodhasSameCodeAscheckPenultimate))) { + error("attempt to become cogged method into different method"); + } + + /* For non-Newspeak there should ne a one-to-one mapping between bytecoded and + cog methods. For Newspeak not necessarily, but only for anonymous accessors. + Only reset the method object's header if it is referring to this CogMethod. */ + if ((rawHeaderOf((cogMethod->methodObject))) == (((sqInt)cogMethod))) { + rawHeaderOfput((cogMethod->methodObject), (cogMethod->methodHeader)); + (writableCogMethod->methodHeader = rawHeaderOf(remappedMethod)); + (writableCogMethod->methodObject = remappedMethod); + rawHeaderOfput(remappedMethod, ((sqInt)cogMethod)); + } + else { + assert((noAssertMethodClassAssociationOf((cogMethod->methodObject))) == (nilObject())); + (writableCogMethod->methodHeader = rawHeaderOf(remappedMethod)); + (writableCogMethod->methodObject = remappedMethod); + } + } + if (isYoung((cogMethod->methodObject))) { + hasYoungObj = 1; + } + } + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = remapIfObjectRefpchasYoung(annotation, ((char *) mcpc), ((void *)((&hasYoungObj)))); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1: + if (hasYoungObj) { + ensureInYoungReferrers(cogMethod); + hasYoungObj = 0; + } + else { + (cogMethod->cmRefersToYoung = 0); + } + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + + /* we /must/ prune youngReferrers here because a) the [cogMethod cmRefersToYoung: false] + block could have removed a method and subsequently it could be added back, and b) we + can not tolerate duplicates in the youngReferrers list. */ + pruneYoungReferrers(); + if (freedPIC) { + unlinkSendsToFree(); + } + if (codeModified) { + flushICacheFromto(backEnd, ((usqInt)codeBase), freeStart()); + } +} + + +/* Update all references to objects in machine code for a full gc. Since + the current (New)ObjectMemory GC makes everything old in a full GC + a method not referring to young will not refer to young afterwards */ + + /* Cogit>>#mapObjectReferencesInMachineCodeForFullGC */ +static void +mapObjectReferencesInMachineCodeForFullGC(void) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + CogMethod *writableCogMethod; + + codeModified = 0; + mapObjectReferencesInGeneratedRuntime(); + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (!(((cogMethod->cmType)) == CMFree)) { + assert((cogMethodDoesntLookKosher(cogMethod)) == 0); + writableCogMethod = ((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)); + (writableCogMethod->selector = remapOop((cogMethod->selector))); + if (((cogMethod->cmType)) == CMClosedPIC) { + assert(!((cogMethod->cmRefersToYoung))); + mapObjectReferencesInClosedPIC(cogMethod); + } + else { + if (((cogMethod->cmType)) >= CMMethod) { + assert(((cogMethod->objectHeader)) == (nullHeaderForMachineCodeMethod())); + (writableCogMethod->methodObject = remapOop((cogMethod->methodObject))); + } + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = remapIfObjectRefpchasYoung(annotation, ((char *) mcpc), 0); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + pruneYoungReferrers(); + if (codeModified) { + flushICacheFromto(backEnd, ((usqInt)codeBase), freeStart()); + } +} + + +/* Update all references to objects in machine code for either a Spur + scavenging gc + or a Squeak V3 incremental GC. Avoid scanning all code by using the + youngReferrers list. In a young gc a method referring to young may no + longer refer to young, but a + method not referring to young cannot and will not refer to young + afterwards. */ + + /* Cogit>>#mapObjectReferencesInMachineCodeForYoungGC */ +static void +mapObjectReferencesInMachineCodeForYoungGC(void) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt hasYoungObj; + sqInt map; + sqInt mapByte; + usqInt mcpc; + usqInt pointer; + sqInt result; + CogMethod *writableCogMethod; + sqInt zoneIsWritable; + + codeModified = (zoneIsWritable = (hasYoungObj = 0)); + pointer = youngReferrers; + while (pointer < limitAddress) { + assert(!hasYoungObj); + cogMethod = ((CogMethod *) (longAt((void *)(pointer)))); + if (((cogMethod->cmType)) == CMFree) { + assert(!((cogMethod->cmRefersToYoung))); + } + else { + assert((cogMethodDoesntLookKosher(cogMethod)) == 0); + if ((cogMethod->cmRefersToYoung)) { + assert((isCMMethodEtAl(((CogBlockMethod *) cogMethod))) + || (isCMOpenPIC(((CogBlockMethod *) cogMethod)))); + if (!zoneIsWritable) { + /* begin ensureWritableCodeZone */ + zoneIsWritable = 1; + } + writableCogMethod = ((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)); + (writableCogMethod->selector = remapOop((cogMethod->selector))); + if (isYoung((cogMethod->selector))) { + hasYoungObj = 1; + } + if (((cogMethod->cmType)) >= CMMethod) { + assert(((cogMethod->objectHeader)) == (nullHeaderForMachineCodeMethod())); + (writableCogMethod->methodObject = remapOop((cogMethod->methodObject))); + if (isYoung((cogMethod->methodObject))) { + hasYoungObj = 1; + } + } + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = remapIfObjectRefpchasYoung(annotation, ((char *) mcpc), ((void *)((&hasYoungObj)))); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1: + if (hasYoungObj) { + hasYoungObj = 0; + } + else { + (writableCogMethod->cmRefersToYoung = 0); + } + } + } + pointer += BytesPerWord; + } + pruneYoungReferrers(); + if (codeModified) { + flushICacheFromto(backEnd, ((usqInt)methodZoneBase), freeStart()); + } +} + + +/* Update all references to objects in machine code. */ + + /* Cogit>>#mapObjectReferencesInMachineCode: */ +void +mapObjectReferencesInMachineCode(sqInt gcMode) +{ + switch (gcMode) { + case GCModeNewSpace: + /* N.B. do *not* ensureWritableCodeZone for every scavenge. */ + mapObjectReferencesInMachineCodeForYoungGC(); + break; + case GCModeFull: + /* begin ensureWritableCodeZone */ + mapObjectReferencesInMachineCodeForFullGC(); + break; + case GCModeBecome: + /* begin ensureWritableCodeZone */ + mapObjectReferencesInMachineCodeForBecome(); + break; + default: + error("Case not found and no otherwise clause"); + } + mapPerMethodProfile(); + if (!(asserta((freeStart()) <= (youngReferrers)))) { + error("youngReferrers list overflowed"); + } +} + + +/* Mark objects in machine-code of marked methods (or open PICs with marked + selectors). + */ + + /* Cogit>>#markAndTraceMachineCodeOfMarkedMethods */ +void +markAndTraceMachineCodeOfMarkedMethods(void) +{ + sqInt annotation; + sqInt annotationSqInt; + CogMethod *cogMethod; + sqInt map; + sqInt mapByte; + sqInt mapByteSqInt; + sqInt mapSqInt; + usqInt mcpc; + usqInt mcpcUsqInt; + sqInt result; + sqInt resultSqInt; + + if (leakCheckFullGC()) { + asserta(allMachineCodeObjectReferencesValid()); + } + codeModified = 0; + markAndTraceObjectReferencesInGeneratedRuntime(); + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if ((((cogMethod->cmType)) >= CMMethod) + && (isMarked((cogMethod->methodObject)))) { + /* begin markAndTraceLiteralsIn: */ + assert(((isCMMethodEtAl(((CogBlockMethod *) cogMethod))) + && (isMarked((cogMethod->methodObject)))) + || ((isCMOpenPIC(((CogBlockMethod *) cogMethod))) + && ((isImmediate((cogMethod->selector))) + || (isMarked((cogMethod->selector)))))); + markAndTraceLiteralinat((cogMethod->selector), cogMethod, (&((cogMethod->selector)))); + + /* begin maybeMarkIRCsIn: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = markLiteralspcmethod(annotation, ((char *) mcpc), cogMethod); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + if ((((cogMethod->cmType)) == CMOpenPIC) + && ((isImmediate((cogMethod->selector))) + || (isMarked((cogMethod->selector))))) { + /* begin markAndTraceLiteralsIn: */ + assert(((isCMMethodEtAl(((CogBlockMethod *) cogMethod))) + && (isMarked((cogMethod->methodObject)))) + || ((isCMOpenPIC(((CogBlockMethod *) cogMethod))) + && ((isImmediate((cogMethod->selector))) + || (isMarked((cogMethod->selector)))))); + markAndTraceLiteralinat((cogMethod->selector), cogMethod, (&((cogMethod->selector)))); + + /* begin maybeMarkIRCsIn: */ + mapByteSqInt = 0; + mcpcUsqInt = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + mapSqInt = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByteSqInt = byteAt((void *)(mapSqInt)))) != MapEnd) { + if (mapByteSqInt >= FirstAnnotation) { + mcpcUsqInt += (mapByteSqInt & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotationSqInt = ((usqInt)(mapByteSqInt)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByteSqInt = byteAt((void *)(mapSqInt - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotationSqInt += mapByteSqInt & DisplacementMask; + mapSqInt -= 1; + } + resultSqInt = markLiteralspcmethod(annotationSqInt, ((char *) mcpcUsqInt), cogMethod); + if (resultSqInt) { + goto l2; + } + } + else { + if (mapByteSqInt < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpcUsqInt += ((((usqInt)((mapByteSqInt - DisplacementX2N)) << AnnotationShift))); + } + } + mapSqInt -= 1; + } + /* end mapFor:performUntil:arg: */ +l2:; + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if (leakCheckFullGC()) { + asserta(allMachineCodeObjectReferencesValid()); + } + if (codeModified) { + flushICacheFromto(backEnd, ((usqInt)methodZoneBase), freeStart()); + } +} + + +/* Mark and trace any object references in the generated run-time. */ + + /* Cogit>>#markAndTraceObjectReferencesInGeneratedRuntime */ +static void +markAndTraceObjectReferencesInGeneratedRuntime(void) +{ + sqInt i; + sqInt literal; + usqInt mcpc; + + for (i = 0; i < runtimeObjectRefIndex; i += 1) { + mcpc = objectReferencesInRuntime[i]; + literal = literalBeforeFollowingAddress(backEnd, mcpc); + markAndTraceLiteralinatpc(literal, ((CogMethod *) null), ((usqInt)mcpc)); + } +} + + +/* Mark and trace objects in the argument and free if it is appropriate. + Answer if the method has been freed. firstVisit is a hint used to avoid + scanning methods we've already seen. False positives are fine. + For a CMMethod this + frees if the bytecode method isnt marked, + marks and traces object literals and selectors, + unlinks sends to targets that should be freed. + For a CMClosedPIC this + frees if it refers to anything that should be freed or isn't marked. + For a CMOpenPIC this + frees if the selector isn't marked. */ +/* this recurses at most one level down */ + + /* Cogit>>#markAndTraceOrFreeCogMethod:firstVisit: */ +static NoDbgRegParms sqInt +markAndTraceOrFreeCogMethodfirstVisit(CogMethod *cogMethod, sqInt firstVisit) +{ + sqInt annotation; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + + if (((cogMethod->cmType)) == CMFree) { + return 1; + } + assert((cogMethodDoesntLookKosher(cogMethod)) == 0); + if (((cogMethod->cmType)) >= CMMethod) { + if (!(isMarked((cogMethod->methodObject)))) { + /* begin ensureWritableCodeZone */ + freeMethod(cogMethod); + return 1; + } + if (firstVisit) { + /* begin markLiteralsAndUnlinkUnmarkedSendsIn: */ + assert(isCMMethodEtAl(((CogBlockMethod *) cogMethod))); + assert(isMarked((cogMethod->methodObject))); + markAndTraceLiteralinat((cogMethod->selector), cogMethod, (&((cogMethod->selector)))); + + /* begin maybeMarkIRCsIn: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = markLiteralsAndUnlinkIfUnmarkedSendpcmethod(annotation, ((char *) mcpc), cogMethod); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + return 0; + } + if (((cogMethod->cmType)) == CMClosedPIC) { + if (!(closedPICRefersToUnmarkedObject(cogMethod))) { + return 0; + } + + /* begin ensureWritableCodeZone */ + freeMethod(cogMethod); + return 1; + } + if (((cogMethod->cmType)) == CMOpenPIC) { + if (isMarked((cogMethod->selector))) { + return 0; + } + + /* begin ensureWritableCodeZone */ + freeMethod(cogMethod); + return 1; + } + assert((isCMMethodEtAl(((CogBlockMethod *) cogMethod))) + || ((isCMClosedPIC(((CogBlockMethod *) cogMethod))) + || (isCMOpenPIC(((CogBlockMethod *) cogMethod))))); + return 0; +} + + +/* If entryPoint is that of some method, then mark and trace objects in it + and free if it is appropriate. + Answer if the method has been freed. */ + + /* Cogit>>#markAndTraceOrFreePICTarget:in: */ +static NoDbgRegParms sqInt +markAndTraceOrFreePICTargetin(sqInt entryPoint, CogMethod *cPIC) +{ + CogMethod *targetMethod; + + assert((entryPoint > methodZoneBase) + && (entryPoint < (freeStart()))); + if (/* containsAddress: */ + ((((usqInt)cPIC)) <= (((usqInt)entryPoint))) + && (((((usqInt)cPIC)) + ((cPIC->blockSize))) >= (((usqInt)entryPoint)))) { + return 0; + } + targetMethod = ((CogMethod *) (entryPoint - cmNoCheckEntryOffset)); + assert((isCMMethodEtAl(((CogBlockMethod *) targetMethod))) + || (isCMFree(((CogBlockMethod *) targetMethod)))); + return markAndTraceOrFreeCogMethodfirstVisit(targetMethod, (((usqInt)targetMethod)) > (((usqInt)cPIC))); +} + + +/* Mark and trace literals. Unlink sends that have unmarked cache tags or + targets. + */ + + /* Cogit>>#markLiteralsAndUnlinkIfUnmarkedSend:pc:method: */ +static NoDbgRegParms sqInt +markLiteralsAndUnlinkIfUnmarkedSendpcmethod(sqInt annotation, char *mcpc, CogMethod *cogMethod) +{ + unsigned int cacheTag1; + sqInt cacheTagMarked; + usqInt entryPointUsqInt; + sqInt literal; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + sqInt unlinkedRoutine; + + if (annotation == IsObjectReference) { + literal = literalBeforeFollowingAddress(backEnd, ((usqInt)mcpc)); + if (markAndTraceLiteralinatpc(literal, ((CogMethod *) cogMethod), ((usqInt)mcpc))) { + codeModified = 1; + } + } + if (annotation >= IsSendCall) { + /* begin entryCacheTagAndCouldBeObjectAt:annotation:into: */ + cacheTag1 = literal32BeforeFollowingAddress(backEnd, ((usqInt)((((sqInt)mcpc)) - 5))); + entryPointUsqInt = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + + /* in-line cache tags are the selectors of sends if sends are unlinked, + the selectors of super sends (entry offset = cmNoCheckEntryOffset), + the selectors of open PIC sends (entry offset = cmEntryOffset, target is an Open PIC) + or in-line cache tags (classes, class indices, immediate bit patterns, etc). + Note that selectors can be immediate so there is no guarantee that they + are markable/remappable objects. Writing the block invocation three times + allows Slang to inline tagCouldBeObject tests. */ + cacheTagMarked = 0; + if (entryPointUsqInt > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPointUsqInt - offsetSqInt)); + if ((!cacheTagMarked) + || (markAndTraceOrFreeCogMethodfirstVisit(targetMethod1, (((usqInt)targetMethod1)) > (((usqInt)mcpc))))) { + /* Either the cacheTag is unmarked (e.g. new class) or the target + has been freed (because it is unmarked), so unlink the send. */ + + /* begin unlinkSendAt:targetMethod:sendTable: */ + unlinkedRoutine = sendTable1[((((targetMethod1->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod1->cmNumArgs)) : (NumSendTrampolines - 1))]; + codeModified = 1; + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod1->selector), enumeratingCogMethod), unlinkedRoutine); + markAndTraceLiteralinat((targetMethod1->selector), targetMethod1, (&((targetMethod1->selector)))); + } + } + else { + } + } + return 0; +} + + +/* Mark and trace literals. + Additionally in Newspeak, void push implicits that have unmarked classes. */ + + /* Cogit>>#markLiterals:pc:method: */ +static NoDbgRegParms sqInt +markLiteralspcmethod(sqInt annotation, char *mcpc, CogMethod *cogMethod) +{ + unsigned int cacheTag1; + usqInt entryPointUsqInt; + sqInt literal; + + if (annotation == IsObjectReference) { + literal = literalBeforeFollowingAddress(backEnd, ((usqInt)mcpc)); + if (markAndTraceLiteralinatpc(literal, cogMethod, ((usqInt)mcpc))) { + codeModified = 1; + } + } + if (annotation >= IsSendCall) { + /* begin entryCacheTagAndCouldBeObjectAt:annotation:into: */ + cacheTag1 = literal32BeforeFollowingAddress(backEnd, ((usqInt)((((sqInt)mcpc)) - 5))); + entryPointUsqInt = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + + /* in-line cache tags are the selectors of sends if sends are unlinked, + the selectors of super sends (entry offset = cmNoCheckEntryOffset), + the selectors of open PIC sends (entry offset = cmEntryOffset, target is an Open PIC) + or in-line cache tags (classes, class indices, immediate bit patterns, etc). + Note that selectors can be immediate so there is no guarantee that they + are markable/remappable objects. Writing the block invocation three times + allows Slang to inline tagCouldBeObject tests. */ + } + return 0; +} + + /* Cogit>>#markMethodAndReferents: */ +void +markMethodAndReferents(CogBlockMethod *aCogMethod) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + CogMethod *writableMethod; + + assert((isCMMethodEtAl(aCogMethod)) + || (isCMBlock(aCogMethod))); + cogMethod = (((aCogMethod->cmType)) >= CMMethod + ? ((CogMethod *) aCogMethod) + : cmHomeMethod(aCogMethod)); + writableMethod = ((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)); + (writableMethod->cmUsageCount = CMMaxUsageCount); + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = incrementUsageOfTargetIfLinkedSendmcpcignored(annotation, ((char *) mcpc), 0); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; +} + + /* Cogit>>#maxCogMethodAddress */ +usqInt +maxCogMethodAddress(void) +{ + return ((usqInt)(limitZony())); +} + + /* Cogit>>#maximumDistanceFromCodeZone: */ +static NoDbgRegParms sqInt +maximumDistanceFromCodeZone(sqInt anAddress) +{ + return (anAddress > codeBase + ? anAddress - codeBase + : limitAddress - anAddress); +} + + +/* Check that the header fields are consistent with the type. + Answer 0 if it is ok, otherwise answer a code for the error. */ + + /* Cogit>>#maybeFreeCogMethodDoesntLookKosher: */ +static NoDbgRegParms sqInt +maybeFreeCogMethodDoesntLookKosher(CogMethod *cogMethod) +{ + sqInt result; + + result = cogMethodDoesntLookKosher(cogMethod); + return (result == 2 + ? 0 + : result); +} + + /* Cogit>>#mclassCouldBeContext */ +static int +mclassCouldBeContext(void) +{ + if (receiverTags < 0) { + receiverTags = receiverTagBitsForMethod(methodObj); + } + return ((receiverTags & ((1U << (numTagBits())))) != 0); +} + + /* Cogit>>#mclassIsSmallInteger */ +static int +mclassIsSmallInteger(void) +{ + if (receiverTags < 0) { + receiverTags = receiverTagBitsForMethod(methodObj); + } + return (((receiverTags) & 7) == 1); +} + + +/* Answer the absolute machine code pc matching the zero-relative + bytecode pc of a backward branch in cogMethod, given the start + of the bytecodes for cogMethod's block or method object. */ + + /* Cogit>>#mcPCForBackwardBranch:startBcpc:in: */ +usqInt +mcPCForBackwardBranchstartBcpcin(sqInt bcpc, sqInt startbcpc, CogBlockMethod *cogMethod) +{ + sqInt aMethodObj; + sqInt annotation; + sqInt bcpcSqInt; + sqInt bsOffset; + sqInt byte; + BytecodeDescriptor *descriptor; + sqInt distance; + sqInt endbcpc; + CogMethod *homeMethod; + sqInt isBackwardBranch; + sqInt isInBlock; + sqInt latestContinuation; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt nExts; + sqInt nextBcpc; + sqInt result; + sqInt targetPC; + + /* begin mapFor:bcpc:performUntil:arg: */ + descriptor = ((BytecodeDescriptor *) 0); + latestContinuation = 0; + mapByte = 0; + nextBcpc = 0; + assert(((cogMethod->stackCheckOffset)) > 0); + mcpc = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + + /* The stack check maps to the start of the first bytecode, + the first bytecode being effectively after frame build. */ + result = findBackwardBranchIsBackwardBranchMcpcBcpcMatchingBcpc(null, 0 + ((((usqInt)(HasBytecodePC) << 1))), ((char *) mcpc), startbcpc, ((void *)bcpc)); + if (result) { + return result; + } + bcpcSqInt = startbcpc; + + /* In both CMMethod and CMBlock cases find the start of the map and + skip forward to the bytecode pc map entry for the stack check. */ + if (((cogMethod->cmType)) >= CMMethod) { + isInBlock = (cogMethod->cpicHasMNUCaseOrCMIsFullBlock); + homeMethod = ((CogMethod *) cogMethod); + assert(startbcpc == (startPCOfMethodHeader((homeMethod->methodHeader)))); + map = ((((usqInt)homeMethod)) + ((homeMethod->blockSize))) - 1; + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert((annotation == IsAbsPCReference) + || ((annotation == IsObjectReference) + || ((annotation == IsRelativeCall) + || (annotation == IsDisplacementX2N)))); + latestContinuation = startbcpc; + aMethodObj = (homeMethod->methodObject); + endbcpc = (numBytesOf(aMethodObj)) - 1; + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + + /* If the method has a primitive, skip it and the error code store, if any; + Logically. these come before the stack check and so must be ignored. */ + bcpcSqInt += deltaToSkipPrimAndErrorStoreInheader(aMethodObj, (homeMethod->methodHeader)); + } + else { + isInBlock = 1; + assert(bcpcSqInt == ((cogMethod->startpc))); + homeMethod = cmHomeMethod(cogMethod); + map = findMapLocationForMcpcinMethod((((usqInt)cogMethod)) + (sizeof(CogBlockMethod)), homeMethod); + assert(map != 0); + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert(((((usqInt)(annotation)) >> AnnotationShift) == HasBytecodePC) + || ((((usqInt)(annotation)) >> AnnotationShift) == IsDisplacementX2N)); + + /* fiducial */ + while (((annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift)) != HasBytecodePC) { + map -= 1; + } + + /* skip fiducial; i.e. the map entry for the pc immediately following the method header. */ + map -= 1; + aMethodObj = (homeMethod->methodObject); + bcpcSqInt = startbcpc - (/* blockCreationBytecodeSizeForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? AltBlockCreationBytecodeSize + : BlockCreationBytecodeSize)); + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + byte = (fetchByteofObject(bcpcSqInt, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + endbcpc = (bcpcSqInt + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpcSqInt, -1, aMethodObj) + : 0)); + bcpcSqInt = startbcpc; + } + nExts = 0; + enumeratingCogMethod = homeMethod; + + /* Now skip up through the bytecode pc map entry for the stack check. */ + while ((((usqInt)((byteAt((void *)(map))))) >> AnnotationShift) != HasBytecodePC) { + map -= 1; + } + map -= 1; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + mcpc += (mapByte & DisplacementMask); + if (annotation >= HasBytecodePC) { + if ((annotation == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + while (1) { + byte = (fetchByteofObject(bcpcSqInt, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + if (isInBlock) { + if (bcpcSqInt >= endbcpc) { + return 0; + } + } + else { + if (((descriptor->isReturn)) + && (bcpcSqInt >= latestContinuation)) { + return 0; + } + if ((isBranch(descriptor)) + || ((descriptor->isBlockCreation))) { + /* begin latestContinuationPCFor:at:exts:in: */ + distance = ((descriptor->spanFunction))(descriptor, bcpcSqInt, nExts, aMethodObj); + targetPC = (bcpcSqInt + ((descriptor->numBytes))) + (((distance < 0) ? 0 : distance)); + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + } + } + nextBcpc = (bcpcSqInt + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpcSqInt, nExts, aMethodObj) + : 0)); + if (((descriptor->isMapped)) + || (isInBlock + && ((descriptor->isMappedInBlock)))) break; + bcpcSqInt = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + isBackwardBranch = (isBranch(descriptor)) + && ((/* begin isBackwardBranch:at:exts:in: */ + assert(((descriptor->spanFunction))), + (((descriptor->spanFunction))(descriptor, bcpcSqInt, nExts, aMethodObj)) < 0)); + result = findBackwardBranchIsBackwardBranchMcpcBcpcMatchingBcpc(descriptor, (isBackwardBranch + ? ((((usqInt)(annotation) << 1))) + 1 + : ((sqInt)((usqInt)(annotation) << 1))), ((char *) mcpc), (isBackwardBranch + ? bcpcSqInt - (2 * nExts) + : bcpcSqInt), ((void *)bcpc)); + if (result) { + return result; + } + bcpcSqInt = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + } + else { + assert(((((usqInt)(mapByte)) >> AnnotationShift) == IsDisplacementX2N) + || ((((usqInt)(mapByte)) >> AnnotationShift) == IsAnnotationExtension)); + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + + /* defensive; we exit on bcpc */ + return 0; +} + + +/* For the purposes of become: see if the two methods are similar, i.e. can + be safely becommed. + This is pretty strict. All literals and bytecodes must be identical. Only + trailer bytes and header + flags can differ. */ + + /* Cogit>>#method:hasSameCodeAs:checkPenultimate: */ +static NoDbgRegParms sqInt +methodhasSameCodeAscheckPenultimate(sqInt methodA, sqInt methodB, sqInt comparePenultimateLiteral) +{ + sqInt bi; + sqInt endPCA; + sqInt headerA; + sqInt headerB; + sqInt li; + sqInt numLitsA; + + headerA = methodHeaderOf(methodA); + headerB = methodHeaderOf(methodB); + numLitsA = literalCountOfMethodHeader(headerA); + endPCA = endPCOf(methodA); + if (((argumentCountOfMethodHeader(headerA)) != (argumentCountOfMethodHeader(headerB))) + || (((temporaryCountOfMethodHeader(headerA)) != (temporaryCountOfMethodHeader(headerB))) + || (((primitiveIndexOfMethodheader(methodA, headerA)) != (primitiveIndexOfMethodheader(methodB, headerB))) + || ((numLitsA != (literalCountOfMethodHeader(headerB))) + || (endPCA > (numBytesOf(methodB))))))) { + return 0; + } + for (li = 1; li < numLitsA; li += 1) { + if ((fetchPointerofObject(li, methodA)) != (fetchPointerofObject(li, methodB))) { + if ((li < (numLitsA - 1)) + || (comparePenultimateLiteral)) { + return 0; + } + } + } + for (bi = (startPCOfMethodHeader(headerA)); bi <= endPCA; bi += 1) { + if ((fetchByteofObject(bi, methodA)) != (fetchByteofObject(bi, methodB))) { + return 0; + } + } + return 1; +} + + /* Cogit>>#mnuOffset */ +sqInt +mnuOffset(void) +{ + return missOffset; +} + + /* Cogit>>#NativePopR: */ +static NoDbgRegParms AbstractInstruction * +gNativePopR(sqInt reg) +{ + return genoperand(PopR, reg); +} + + /* Cogit>>#NativePushR: */ +static NoDbgRegParms AbstractInstruction * +gNativePushR(sqInt reg) +{ + return genoperand(PushR, reg); +} + + /* Cogit>>#NativeRetN: */ +static NoDbgRegParms AbstractInstruction * +gNativeRetN(sqInt offset) +{ + return genoperand(RetN, offset); +} + + /* Cogit>>#needsFrameIfImmutability: */ +static NoDbgRegParms sqInt +needsFrameIfImmutability(sqInt stackDelta) +{ + return IMMUTABILITY; +} + + /* Cogit>>#needsFrameIfInBlock: */ +static NoDbgRegParms sqInt +needsFrameIfInBlock(sqInt stackDelta) +{ + return inBlock > 0; +} + + +/* Marks inst var read generators. We only need a frame if + accessing an inst var of a context which needs read mediation + (is computed from the context's frame when married). Assumes + an extensible bytecode with nst var index in the 2nd byte. */ + + /* Cogit>>#needsFrameIfReadMediated: */ +static NoDbgRegParms sqInt +needsFrameIfReadMediated(sqInt stackDelta) +{ + return isReadMediatedContextInstVarIndex(byte1 + extA); +} + + /* Cogit>>#needsFrameNever: */ +static NoDbgRegParms sqInt +needsFrameNever(sqInt stackDelta) +{ + return 0; +} + + /* Cogit>>#noAssertMethodClassAssociationOf: */ +static NoDbgRegParms sqInt +noAssertMethodClassAssociationOf(sqInt methodPointer) +{ + return literalofMethod((literalCountOfMethodHeader(noAssertHeaderOf(methodPointer))) - 1, methodPointer); +} + + +/* Check that no method is maximally marked. A maximal mark is an indication + the method has been scanned to increase the usage count of its referent + methods. */ + + /* Cogit>>#noCogMethodsMaximallyMarked */ +static sqInt +noCogMethodsMaximallyMarked(void) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if ((!(((cogMethod->cmType)) == CMFree)) + && (((cogMethod->cmUsageCount)) == CMMaxUsageCount)) { + return 0; + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + return 1; +} + + +/* Answer if all targets in the PIC are in-use methods. */ + + /* Cogit>>#noTargetsFreeInClosedPIC: */ +static NoDbgRegParms int +noTargetsFreeInClosedPIC(CogMethod *cPIC) +{ + return !(cPICHasFreedTargets(cPIC)); +} + + /* Cogit>>#OrCq:R:R: */ +static NoDbgRegParms AbstractInstruction * +gOrCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *first; + + if (srcReg == destReg) { + return genoperandoperand(OrCqR, quickConstant, destReg); + } + first = genoperandoperand(MoveRR, srcReg, destReg); + + /* #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(OrCqR, quickConstant, destReg); + return first; +} + + +/* Store the generated machine code, answering the last address */ + + /* Cogit>>#outputInstructionsAt: */ +static NoDbgRegParms sqInt +outputInstructionsAt(sqInt startAddress) +{ + sqInt absoluteAddress; + AbstractInstruction *abstractInstruction; + sqInt i; + sqInt j; + + /* begin ensureWritableCodeZone */ + absoluteAddress = startAddress; + for (i = 0; i < opcodeIndex; i += 1) { + maybeBreakGeneratingInstructionWithIndex(i); + abstractInstruction = abstractInstructionAt(i); + assert(((abstractInstruction->address)) == absoluteAddress); + + /* begin outputMachineCodeAt: */ + for (j = 0; j < ((abstractInstruction->machineCodeSize)); j += 1) { + byteAtput((void *)(absoluteAddress + j),((abstractInstruction->machineCode))[j]); + } + absoluteAddress += (abstractInstruction->machineCodeSize); + } + return absoluteAddress; +} + + +/* Output instructions generated for one of the generated run-time routines, + a trampoline, etc + */ + + /* Cogit>>#outputInstructionsForGeneratedRuntimeAt: */ +static NoDbgRegParms sqInt +outputInstructionsForGeneratedRuntimeAt(sqInt startAddress) +{ + sqInt endAddress; + sqInt size; + + computeMaximumSizes(); + (methodLabel->address = startAddress); + size = generateInstructionsAt(startAddress); + endAddress = outputInstructionsAt(startAddress); + assert((startAddress + size) == endAddress); + methodZoneBase = alignUptoRoutineBoundary(endAddress); + stopsFromto(backEnd, endAddress, methodZoneBase - 1); + return startAddress; +} + + +/* Code entry closed PIC full or miss to an instance of a young class or to a + young target method. + Attempt to patch the send site to an open PIC. Answer if the attempt + succeeded; in fact it will + only return if the attempt failed. + The stack looks like: + receiver + args + sp=> sender return address */ + + /* Cogit>>#patchToOpenPICFor:numArgs:receiver: */ +sqInt +patchToOpenPICFornumArgsreceiver(sqInt selector, sqInt numArgs, sqInt receiver) +{ + sqInt extent; + CogMethod *oPIC; + sqInt outerReturn; + + outerReturn = stackTop(); + + /* See if an Open PIC is already available. */ + oPIC = openPICWithSelector(selector); + if (!oPIC) { + /* otherwise attempt to create an Open PIC. */ + oPIC = cogOpenPICSelectornumArgs(selector, numArgs); + if ((((((sqInt)oPIC)) >= MaxNegativeErrorCode) && ((((sqInt)oPIC)) <= -1))) { + /* For some reason the PIC couldn't be generated, most likely a lack of code memory. */ + if ((((sqInt)oPIC)) == InsufficientCodeSpace) { + callForCogCompiledCodeCompaction(); + } + return 0; + } + } + + /* Relink the send site to the pic. Reset the cache tag to the selector, for the + benefit of the cacheTag assert check in checkIfValidOopRef:pc:cogMethod: et al. */ + + /* begin ensureWritableCodeZone */ + extent = rewriteInlineCacheAttagtarget(backEnd, outerReturn, inlineCacheValueForSelectorin(backEnd, selector, mframeHomeMethodExport()), (((sqInt)oPIC)) + cmEntryOffset); + flushICacheFromto(backEnd, (((usqInt)outerReturn)) - extent, ((usqInt)outerReturn)); + flushICacheFromto(backEnd, ((usqInt)oPIC), (((usqInt)oPIC)) + openPICSize); + + /* Jump into the oPIC at its entry */ + executeCogMethodfromLinkedSendWithReceiver(oPIC, receiver); + + /* NOTREACHED */ + return 1; +} + + +/* This value is used to decide between MNU processing + or interpretation in the closed PIC aborts. */ + + /* Cogit>>#picAbortDiscriminatorValue */ +static sqInt +picAbortDiscriminatorValue(void) +{ + return 0; +} + + +/* Answer the start of the abort sequence for invoking the interpreter in a + closed PIC. + */ + + /* Cogit>>#picInterpretAbortOffset */ +static sqInt +picInterpretAbortOffset(void) +{ + return (interpretOffset()) - (callInstructionByteSize(backEnd)); +} + + +/* useful for debugging */ + + /* Cogit>>#printCogMethodFor: */ +void +printCogMethodFor(void *address) +{ + CogMethod *cogMethod; + + if ((cogMethod = methodFor(address))) { + printCogMethod(cogMethod); + } + else { + if (codeEntryFor(address)) { + print("trampoline "); + print(codeEntryNameFor(address)); + cr(); + } + else { + print("not a method"); + cr(); + } + } +} + + /* Cogit>>#printPCMapPairsFor: */ +#if VMInvestigations +void +printPCMapPairsFor(CogMethod *cogMethod) +{ + unsigned char annotation; + sqInt map; + unsigned char mapByte; + usqInt mcpc; + sqInt stackCheckPC; + const char *type; + usqInt value; + + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + stackCheckPC = (((sqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + if (annotation == IsAnnotationExtension) { + value = (mapByte & DisplacementMask) + IsSendCall; + } + else { + value = annotation; + mcpc += 1 /* codeGranularity */ * ((annotation + ? mapByte & DisplacementMask + : ((sqInt)((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift)))); + } + if (((value >= IsDirectedSuperSend) && (value <= IsDirectedSuperBindingSend))) { + if (value == IsDirectedSuperSend) { + type = "DirectedSuperSend"; + } + else { + type = "DirectedSuperBindingSend"; + } + } + else { + switch (value) { + case IsDisplacementX2N: + type = "DisplacementX2N"; + break; + case IsAnnotationExtension: + type = "AnnotationExtension"; + break; + case IsObjectReference: + type = "ObjectReference"; + break; + case IsAbsPCReference: + type = "AbsPCReference"; + break; + case HasBytecodePC: + if (mcpc == stackCheckPC) { + type = "StackCheck/BCPC"; + } + else { + type = "HasBytecodePC"; + } + break; + case IsRelativeCall: + type = "RelativeCall"; + break; + case IsSendCall: + type = "SendCall"; + break; + case IsSuperSend: + type = "SuperSend"; + break; + default: + type = null; + } + } + if (type) { + fprintf(getTranscript(), + "%" PRIxSQINT ": %02x %ld (%s) 0x%x @ %p\n", + map, + mapByte, + annotation, + type, + ((int) (mapByte & DisplacementMask)), + ((void *)mcpc)); + } + else { + fprintf(getTranscript(), + "%" PRIxSQINT ": %02x %ld (??? %d) 0x%x @ %p\n", + map, + mapByte, + annotation, + ((int) value), + ((int) (mapByte & DisplacementMask)), + ((void *)mcpc)); + } + map -= 1; + } +} +#endif /* VMInvestigations */ + + +/* useful for debugging */ + + /* Cogit>>#printTrampolineTable */ +void +printTrampolineTable(void) +{ + sqInt i; + + for (i = 0; i < trampolineTableIndex; i += 2) { + fprintf(getTranscript(), + "%p: %s\n", + ((void *)(trampolineAddresses[i + 1])), + ((char *)(trampolineAddresses[i]))); + } +} + + /* Cogit>>#processorHasDivQuoRemAndMClassIsSmallInteger */ +static sqInt +processorHasDivQuoRemAndMClassIsSmallInteger(void) +{ + return mclassIsSmallInteger(); +} + + /* Cogit>>#processorHasMultiplyAndMClassIsSmallInteger */ +static sqInt +processorHasMultiplyAndMClassIsSmallInteger(void) +{ + return mclassIsSmallInteger(); +} + + /* Cogit>>#recordGeneratedRunTime:address: */ +static NoDbgRegParms void +recordGeneratedRunTimeaddress(char *aString, sqInt address) +{ + assert((trampolineTableIndex + 2) <= (NumTrampolines * 2)); + trampolineAddresses[trampolineTableIndex] = aString; + trampolineAddresses[trampolineTableIndex + 1] = (((char *) address)); + + /* self printTrampolineTable */ + trampolineTableIndex += 2; +} + + +/* This one for C support code. */ + + /* Cogit>>#recordPrimTraceFunc */ +int +recordPrimTraceFunc(void) +{ + return recordPrimTrace(); +} + + /* Cogit>>#recordRunTimeObjectReferences */ +static void +recordRunTimeObjectReferences(void) +{ + sqInt i; + AbstractInstruction *instruction; + + for (i = 0; i < opcodeIndex; i += 1) { + instruction = abstractInstructionAt(i); + if (((instruction->annotation)) == IsObjectReference) { + assert(runtimeObjectRefIndex < NumObjRefsInRuntime); + assert(!hasYoungReferent); + if (hasYoungReferent) { + error("attempt to generate run-time routine containing young object reference. Cannot initialize Cogit run-time."); + } + objectReferencesInRuntime[runtimeObjectRefIndex] = (((usqInt)(((instruction->address)) + ((instruction->machineCodeSize))))); + runtimeObjectRefIndex += 1; + } + } +} + + +/* N.B. (self registerMaskFor: NoReg) = 0 */ + + /* Cogit>>#registerMaskFor: */ +static NoDbgRegParms sqInt +registerMaskFor(sqInt reg) +{ + return ((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)); +} + + /* Cogit>>#registerMaskFor:and: */ +static NoDbgRegParms sqInt +registerMaskForand(sqInt reg1, sqInt reg2) +{ + return (1ULL << reg1) | (1ULL << reg2); +} + + /* Cogit>>#relocateCallsAndSelfReferencesInMethod: */ +static NoDbgRegParms void +relocateCallsAndSelfReferencesInMethod(CogMethod *cogMethod) +{ + sqInt annotation; + sqInt callDelta; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqLong refDelta; + sqInt result; + + refDelta = (cogMethod->objectHeader); + callDelta = refDelta; + assert((isCMMethodEtAl(((CogBlockMethod *) cogMethod))) + || (isCMOpenPIC(((CogBlockMethod *) cogMethod)))); + assert((callTargetFromReturnAddress(backEnd, (((sqInt)cogMethod)) + missOffset)) == ((isCMMethodEtAl(((CogBlockMethod *) cogMethod)) + ? methodAbortTrampolineFor((cogMethod->cmNumArgs)) + : picAbortTrampolineFor((cogMethod->cmNumArgs))))); + relocateCallBeforeReturnPCby(backEnd, (((sqInt)cogMethod)) + missOffset, -callDelta); + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = relocateIfCallOrMethodReferencemcpcdelta(annotation, ((char *) mcpc), ((void *)refDelta)); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; +} + + /* Cogit>>#relocateCallsInClosedPIC: */ +static NoDbgRegParms void +relocateCallsInClosedPIC(CogMethod *cPIC) +{ + sqInt callDelta; + sqInt entryPoint; + sqInt i; + sqInt pc; + sqInt pcSqInt; + sqLong refDelta; + CogMethod *targetMethod; + + refDelta = (cPIC->objectHeader); + callDelta = refDelta; + assert((callTargetFromReturnAddress(backEnd, (((sqInt)cPIC)) + missOffset)) == (picAbortTrampolineFor((cPIC->cmNumArgs)))); + relocateCallBeforeReturnPCby(backEnd, (((sqInt)cPIC)) + missOffset, -callDelta); + pc = (((sqInt)cPIC)) + firstCPICCaseOffset; + for (i = 1; i <= ((cPIC->cPICNumCases)); i += 1) { + pc = addressOfEndOfCaseinCPIC(i, cPIC); + entryPoint = (jumpLongTargetBeforeFollowingAddress(backEnd, pc)); + if (/* containsAddress: */ + ((((usqInt)cPIC)) <= (((usqInt)entryPoint))) + && (((((usqInt)cPIC)) + ((cPIC->blockSize))) >= (((usqInt)entryPoint)))) { + /* Interpret/MNU */ + } + else { + targetMethod = ((CogMethod *) (entryPoint - cmNoCheckEntryOffset)); + assert(isCMMethodEtAl(((CogBlockMethod *) targetMethod))); + if (i == 1) { + relocateJumpLongBeforeFollowingAddressby(backEnd, pc, -(callDelta - ((targetMethod->objectHeader)))); + } + else { + relocateJumpLongConditionalBeforeFollowingAddressby(backEnd, pc, -(callDelta - ((targetMethod->objectHeader)))); + } + } + } + assert(((cPIC->cPICNumCases)) > 0); + + /* Finally relocate the load of the PIC and the jump to the overflow routine ceCPICMiss:receiver: */ + pcSqInt = (addressOfEndOfCaseinCPIC(2, cPIC)) + 7 /* loadPICLiteralByteSize */; + + /* begin relocateMethodReferenceBeforeAddress:by: */ + assert((((byteAt((void *)(pcSqInt - 6))) == 141) + && (((byteAt((void *)(pcSqInt - 5))) | (modRMRO(backEnd, 0, 0, 7))) == (modRMRO(backEnd, ModRegInd, 5, 7)))) + || (((byteAt((void *)(pcSqInt - 8))) == 141) + && (((byteAt((void *)(pcSqInt - 7))) | (modRMRO(backEnd, 0, 0, 7))) == (modRMRO(backEnd, ModRegInd, 5, 7))))); + relocateJumpLongBeforeFollowingAddressby(backEnd, (((sqInt)cPIC)) + cPICEndOfCodeOffset, -callDelta); +} + + +/* To placate the C static type system... */ + + /* Cogit>>#relocateIfCallOrMethodReference:mcpc:delta: */ +static NoDbgRegParms sqInt +relocateIfCallOrMethodReferencemcpcdelta(sqInt annotation, char *mcpc, void *refDeltaArg) +{ + sqInt callDelta; + usqInt entryPoint; + sqInt offsetSqInt; + sqInt refDelta; + sqInt *sendTable1; + CogMethod *targetMethod; + sqInt unlinkedRoutine; + + refDelta = ((sqInt) refDeltaArg); + callDelta = refDelta; + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint <= methodZoneBase) { + relocateCallBeforeReturnPCby(backEnd, ((sqInt)mcpc), -callDelta); + return 0; + } + + /* send is not linked; just relocate + It's a linked send; find which kind. */ + + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod = ((CogMethod *) (entryPoint - offsetSqInt)); + if (!(((targetMethod->cmType)) == CMFree)) { + relocateCallBeforeReturnPCby(backEnd, ((sqInt)mcpc), -(callDelta - ((targetMethod->objectHeader)))); + + /* See comment in planCompaction */ + return 0; + } + + /* send target not freed; just relocate. + Target was freed; map back to an unlinked send; but include this method's reocation */ + unlinkedRoutine = sendTable1[((((targetMethod->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod->cmNumArgs)) : (NumSendTrampolines - 1))]; + unlinkedRoutine -= callDelta; + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod->selector), enumeratingCogMethod), unlinkedRoutine); + return 0; + } + if (annotation == IsRelativeCall) { + relocateCallBeforeReturnPCby(backEnd, ((sqInt)mcpc), -callDelta); + return 0; + } + if (annotation == IsAbsPCReference) { + /* begin relocateMethodReferenceBeforeAddress:by: */ + assert((((byteAt((void *)((((sqInt)mcpc)) - 6))) == 141) + && (((byteAt((void *)((((sqInt)mcpc)) - 5))) | (modRMRO(backEnd, 0, 0, 7))) == (modRMRO(backEnd, ModRegInd, 5, 7)))) + || (((byteAt((void *)((((sqInt)mcpc)) - 8))) == 141) + && (((byteAt((void *)((((sqInt)mcpc)) - 7))) | (modRMRO(backEnd, 0, 0, 7))) == (modRMRO(backEnd, ModRegInd, 5, 7))))); + } + return 0; +} + + /* Cogit>>#remapIfObjectRef:pc:hasYoung: */ +static NoDbgRegParms sqInt +remapIfObjectRefpchasYoung(sqInt annotation, char *mcpc, void *hasYoungPtr) +{ + unsigned int cacheTag1; + usqInt entryPointUsqInt; + sqInt literal; + sqInt mappedLiteral; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + + if (annotation == IsObjectReference) { + literal = literalBeforeFollowingAddress(backEnd, ((usqInt)mcpc)); + if (couldBeObject(literal)) { + mappedLiteral = remapObject(literal); + if (literal != mappedLiteral) { + /* begin setCodeModified */ + codeModified = 1; + + /* storeLiteral:atAnnotatedAddress:using: */ + storeLiteralbeforeFollowingAddress(backEnd, mappedLiteral, ((usqInt)mcpc)); + } + if ((hasYoungPtr != 0) + && (isYoung(mappedLiteral))) { + (*((sqInt *) hasYoungPtr) = 1); + } + } + } + if (annotation >= IsSendCall) { + /* begin entryCacheTagAndCouldBeObjectAt:annotation:into: */ + cacheTag1 = literal32BeforeFollowingAddress(backEnd, ((usqInt)((((sqInt)mcpc)) - 5))); + entryPointUsqInt = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + + /* in-line cache tags are the selectors of sends if sends are unlinked, + the selectors of super sends (entry offset = cmNoCheckEntryOffset), + the selectors of open PIC sends (entry offset = cmEntryOffset, target is an Open PIC) + or in-line cache tags (classes, class indices, immediate bit patterns, etc). + Note that selectors can be immediate so there is no guarantee that they + are markable/remappable objects. Writing the block invocation three times + allows Slang to inline tagCouldBeObject tests. */ + if (hasYoungPtr) { + /* Since the unlinking routines may rewrite the cacheTag to the send's selector, and + since they don't have the cogMethod to hand and can't add it to youngReferrers, + the method must remain in youngReferrers if the targetMethod's selector is young. */ + if (entryPointUsqInt > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPointUsqInt - offsetSqInt)); + if (isYoung((targetMethod1->selector))) { + (*((sqInt *) hasYoungPtr) = 1); + } + } + } + } + return 0; +} + + +/* Remap a potential object reference from a closed PIC. + This may be an object reference, an inline cache tag or null. + Answer if the updated literal is young. + mcpc is the address of the next instruction following either + the load of the method literal or the compare of the class tag. */ + + /* Cogit>>#remapMaybeObjRefInClosedPICAt: */ +static NoDbgRegParms sqInt +remapMaybeObjRefInClosedPICAt(sqInt mcpc) +{ + sqInt object; + sqInt subject; + + object = literalBeforeFollowingAddress(backEnd, mcpc); + if (!(couldBeObject(object))) { + return 0; + } + subject = remapOop(object); + if (object != subject) { + /* begin setCodeModified */ + codeModified = 1; + storeLiteralbeforeFollowingAddress(backEnd, subject, mcpc); + } + return isYoungObject(subject); +} + + +/* Rewrite the three values involved in a CPIC case. Used by the initialize & + extend CPICs. + c.f. expectedClosedPICPrototype: */ +/* write the obj ref/operand via the second ldr */ + + /* Cogit>>#rewriteCPICCaseAt:tag:objRef:target: */ +static NoDbgRegParms void +rewriteCPICCaseAttagobjReftarget(sqInt followingAddress, sqInt newTag, sqInt newObjRef, sqInt newTarget) +{ + sqInt classTagPC; + sqInt methodObjPC; + + methodObjPC = (followingAddress - (jumpLongConditionalByteSize(backEnd))) - (cmpC32RTempByteSize(backEnd)); + storeLiteralbeforeFollowingAddress(backEnd, newObjRef, methodObjPC); + classTagPC = followingAddress - (jumpLongConditionalByteSize(backEnd)); + + /* rewite the tag via the first ldr */ + storeLiteral32beforeFollowingAddress(backEnd, newTag, classTagPC); + + /* write the jump address for the new target address */ + rewriteConditionalJumpLongAttarget(backEnd, followingAddress, newTarget); +} + + +/* destReg := fromReg - subReg */ + + /* Cogit>>#SubR:R:R: */ +static NoDbgRegParms AbstractInstruction * +gSubRRR(sqInt subReg, sqInt fromReg, sqInt destReg) +{ + AbstractInstruction *first; + + assert(subReg != destReg); + first = genoperandoperand(MoveRR, fromReg, destReg); + genoperandoperand(SubRR, subReg, destReg); + return first; +} + + +/* Answer the number of clean blocks found in the literal frame */ + + /* Cogit>>#scanForCleanBlocks */ +static sqInt +scanForCleanBlocks(void) +{ + sqInt i; + sqInt lit; + sqInt numCleanBlocks; + sqInt toDoLimit; + + numCleanBlocks = 0; + toDoLimit = literalCountOf(methodObj); + for (i = 1; i <= toDoLimit; i += 1) { + lit = fetchPointerofObject(i, methodObj); + if (startPCOrNilOfLiteralin(lit, methodObj)) { + numCleanBlocks += 1; + } + } + return numCleanBlocks; +} + + +/* If a method is compiled to machine code via a block entry it won't have a + selector. A subsequent send can find the method and hence fill in the + selector. + */ +/* self disassembleMethod: cogMethod */ + + /* Cogit>>#setSelectorOf:to: */ +void +setSelectorOfto(CogMethod *cogMethod, sqInt aSelectorOop) +{ + compilationBreakpointisMNUCase(aSelectorOop, 0); + assert(isCMMethodEtAl(((CogBlockMethod *) cogMethod))); + + /* begin ensureWritableCodeZone */ + ((((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)))->selector = aSelectorOop); + if (isYoung(aSelectorOop)) { + ensureInYoungReferrers(cogMethod); + } + + /* begin ensureExecutableCodeZone */ +} + + /* Cogit>>#spanForCleanBlockStartingAt: */ +static NoDbgRegParms sqInt +spanForCleanBlockStartingAt(sqInt startPC) +{ + BytecodeDescriptor *descriptor; + sqInt end; + sqInt pc; + + pc = startPC; + end = numBytesOf(methodObj); + while (pc <= end) { + /* begin generatorForPC: */ + descriptor = generatorAt(bytecodeSetOffset + (fetchByteofObject(pc, methodObj))); + pc += (descriptor->numBytes); + if ((descriptor->isReturn)) { + return pc - startPC; + } + } + error("couldn't locate end of clean block"); + return 0; +} + + +/* Set up things conveniently for stringForEntryPoint:existingImports:limit: */ + + /* Cogit>>#storeCogMethodDataImports:pc:pointer: */ +#if VMInvestigations +static NoDbgRegParms sqInt +storeCogMethodDataImportspcpointer(sqInt annotation, char *mcpc, usqInt **importsPtrPtr) +{ + unsigned int cacheTag1; + usqInt entryPointUsqInt; + sqInt import; + usqInt *importsPtr; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + + primitiveIndex = (methodHeaderHasPrimitive((enumeratingCogMethod->methodHeader)) + ? primitiveIndexOfMethodheader((enumeratingCogMethod->methodObject), (enumeratingCogMethod->methodHeader)) + : 0); + importsPtr = importsPtrPtr[0]; + if (annotation == IsObjectReference) { + import = literalBeforeFollowingAddress(backEnd, ((usqInt)mcpc)); + importsPtr[0] = ((((usqInt)(mcpc - (((char *)enumeratingCogMethod))) << 3) | 1)); + importsPtr[1] = (positive64BitIntegerFor(import)); + importsPtr[2] = import; + importsPtr += 3; + } + if (annotation == IsRelativeCall) { + if ((import = stringForEntryPointexistingImportslimit(((char *)(callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)))), ((sqInt)(importsPtrPtr[1])), importsPtr))) { + importsPtr[0] = ((((usqInt)(mcpc - (((char *)enumeratingCogMethod))) << 3) | 1)); + importsPtr[1] = (positive64BitIntegerFor(callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)))); + importsPtr[2] = import; + } + else { + return PrimErrNoMemory; + } + importsPtr += 3; + } + if (annotation >= IsSendCall) { + /* begin entryCacheTagAndCouldBeObjectAt:annotation:into: */ + cacheTag1 = literal32BeforeFollowingAddress(backEnd, ((usqInt)((((sqInt)mcpc)) - 5))); + entryPointUsqInt = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + + /* in-line cache tags are the selectors of sends if sends are unlinked, + the selectors of super sends (entry offset = cmNoCheckEntryOffset), + the selectors of open PIC sends (entry offset = cmEntryOffset, target is an Open PIC) + or in-line cache tags (classes, class indices, immediate bit patterns, etc). + Note that selectors can be immediate so there is no guarantee that they + are markable/remappable objects. Writing the block invocation three times + allows Slang to inline tagCouldBeObject tests. */ + if (entryPointUsqInt > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPointUsqInt - offsetSqInt)); + import = (((targetMethod1->cmType)) >= CMMethod + ? (targetMethod1->methodObject) + : (importsPtrPtr[2] + ? (((targetMethod1->cmType)) == CMClosedPIC + ? investigationArrayForClosedPIC(targetMethod1) + : (assert(isCMOpenPIC(((CogBlockMethod *) targetMethod1))), + (targetMethod1->selector))) + : (targetMethod1->selector))); + } + else { + import = stringForEntryPointexistingImportslimit(((char *)entryPointUsqInt), ((sqInt)(importsPtrPtr[1])), importsPtr); + } + + /* send is linked; store target method or Array of selector and cases for Closed PICs or selector for Open PICs + send is unlinked; store a string naming the trampoline, and try not to duplicate strings. */ + if (!import) { + return PrimErrNoMemory; + } + importsPtr[0] = ((((usqInt)(mcpc - (((char *)enumeratingCogMethod))) << 3) | 1)); + importsPtr[1] = (positive64BitIntegerFor(entryPointUsqInt)); + importsPtr[2] = import; + importsPtr += 3; + } + importsPtrPtr[0] = importsPtr; + return 0; +} +#endif /* VMInvestigations */ + + +/* Search for an existing string for the entryPoint in imports up to + nextStoreLocation. If not found answer a new string. */ + + /* Cogit>>#stringForEntryPoint:existingImports:limit: */ +#if VMInvestigations +static NoDbgRegParms sqInt +stringForEntryPointexistingImportslimit(char *entryPoint, sqInt imports, sqInt *nextStoreLocation) +{ + char buffer[32]; + char *entryPointName; + sqInt import; + sqInt len; + sqInt *ptr; + + + /* If an external primitive is called on-stack then it may be reachable by a relative call. + Handle that here by answering the first literal, which holds the external primitive spec. */ + import = 0; + if (primitiveIndex > 0) { + /* This is a special case in Spur */ + if (entryPoint == (((char *)ceNewHashOf))) { + return stringForCString("ceNewHashOf:"); + } + if (entryPoint == (((char *)checkForAndFollowForwardedPrimitiveState))) { + return stringForCString("checkForAndFollowForwardedPrimitiveState"); + } + if (entryPoint == (((char *)ceCheckAndMaybeRetryPrimitive))) { + return stringForCString("ceCheckAndMaybeRetryPrimitive:"); + } + if (entryPoint == (((char *)(functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto((enumeratingCogMethod->methodObject), primitiveIndex, null))))) { + /* Answer the full external spec for external primitives. */ + if (primitiveIndex == PrimCallIsExternalCall) { + return fetchPointerofObject(1, (enumeratingCogMethod->methodObject)); + } + entryPointName = buffer; + snprintf(buffer, sizeof(buffer), "primitive #%ld", ((long)primitiveIndex)); + return stringForCString(entryPointName); + } + } + + /* Normally relative calls call trampolines. So search the trampoline table */ + assert((codeEntryFor(entryPoint)) == entryPoint); + entryPointName = codeEntryNameFor(entryPoint); + len = strlen(entryPointName); + ptr = (((sqInt *) (firstIndexableField(imports)))) + 2; + while ((ptr < nextStoreLocation) + && (!import)) { + if ((isBytes(ptr[0])) + && (((numBytesOf(ptr[0])) == len) + && ((strncmp(entryPointName, ((char *) (firstIndexableField(ptr[0]))), len)) == 0))) { + import = ptr[0]; + } + ptr += 3; + } + return (!(import) + ? stringForCString(entryPointName) + : import); +} +#endif /* VMInvestigations */ + + +/* Answer a fake value for the method oop in other than the first case in the + PIC prototype. + Since we use MoveUniqueCw:R: it must not be confused with a + method-relative address. + */ + + /* Cogit>>#subsequentPrototypeMethodOop */ +static sqInt +subsequentPrototypeMethodOop(void) +{ + return (/* addressIsInCurrentCompilation: */ + ((((usqInt)0xBADA550)) >= ((methodLabel->address))) + && ((((usqInt)0xBADA550)) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize)))) + ? 0xDEADEAD + : 0xBADA550); +} + + /* Cogit>>#TstCq:R: */ +static NoDbgRegParms AbstractInstruction * +gTstCqR(sqInt quickConstant, sqInt reg) +{ + return genoperandoperand(TstCqR, quickConstant, reg); +} + + /* Cogit>>#traceLinkedSendOffset */ +sqInt +traceLinkedSendOffset(void) +{ + return (cmNoCheckEntryOffset + (callFullInstructionByteSize(backEnd))); +} + + /* Cogit>>#trampolineName:numArgs: */ +static NoDbgRegParms char * +trampolineNamenumArgs(char *routinePrefix, sqInt numArgs) +{ + char *theString; + + /* begin trampolineName:numArgs:limit: */ + theString = malloc((strlen(routinePrefix)) + 6); + sprintf(theString, "%s%cArgs", routinePrefix, ((((int) numArgs)) <= (NumSendTrampolines - 2) + ? '0' + (((int) numArgs)) + : 'N')); + return theString; +} + + +/* Malloc a string with the contents for the trampoline table */ + + /* Cogit>>#trampolineName:numArgs:limit: */ +static NoDbgRegParms char * +trampolineNamenumArgslimit(char *routinePrefix, int numArgs, sqInt argsLimit) +{ + char *theString; + + theString = malloc((strlen(routinePrefix)) + 6); + sprintf(theString, "%s%cArgs", routinePrefix, (numArgs <= argsLimit + ? '0' + numArgs + : 'N')); + return theString; +} + + /* Cogit>>#trampolineName:numRegArgs: */ +static NoDbgRegParms char * +trampolineNamenumRegArgs(char *routinePrefix, sqInt numArgs) +{ + char *theString; + + /* begin trampolineName:numArgs:limit: */ + theString = malloc((strlen(routinePrefix)) + 6); + sprintf(theString, "%s%cArgs", routinePrefix, ((((int) numArgs)) <= (numRegArgs()) + ? '0' + (((int) numArgs)) + : 'N')); + return theString; +} + + /* Cogit>>#unflagBecomeFlaggedMethods */ +void +unflagBecomeFlaggedMethods(void) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) == CMMethodFlaggedForBecome) { + ((((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)))->cmType = CMMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } +} + + /* Cogit>>#unknownBytecode */ +static sqInt +unknownBytecode(void) +{ + return EncounteredUnknownBytecode; +} + + +/* Unlink all sends in cog methods. */ + + /* Cogit>>#unlinkAllSends */ +void +unlinkAllSends(void) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + + if (!methodZoneBase) { + return; + } + + /* begin ensureWritableCodeZone */ + cogMethod = ((CogMethod *) methodZoneBase); + voidOpenPICList(); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = unlinkIfLinkedSendpcignored(annotation, ((char *) mcpc), 0); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + else { + if (!(((cogMethod->cmType)) == CMFree)) { + freeMethod(cogMethod); + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + + /* After updating inline caches we need to flush the icache. */ + flushICacheFromto(backEnd, ((usqInt)methodZoneBase), freeStart()); +} + + +/* To placate the C static type system... */ + + /* Cogit>>#unlinkIfFreeOrLinkedSend:pc:of: */ +static NoDbgRegParms sqInt +unlinkIfFreeOrLinkedSendpcof(sqInt annotation, char *mcpc, void *theSelector) +{ + usqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + sqInt unlinkedRoutine; + + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if ((((targetMethod1->cmType)) == CMFree) + || (((targetMethod1->selector)) == (((sqInt) theSelector)))) { + /* begin unlinkSendAt:targetMethod:sendTable: */ + unlinkedRoutine = sendTable1[((((targetMethod1->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod1->cmNumArgs)) : (NumSendTrampolines - 1))]; + codeModified = 1; + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod1->selector), enumeratingCogMethod), unlinkedRoutine); + } + } + } + return 0; +} + + /* Cogit>>#unlinkIfInvalidClassSend:pc:ignored: */ +static NoDbgRegParms sqInt +unlinkIfInvalidClassSendpcignored(sqInt annotation, char *mcpc, sqInt superfluity) +{ + usqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + sqInt unlinkedRoutine; + + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (!((/* annotationIsForUncheckedEntryPoint: */ + (annotation == IsSuperSend) + || (((annotation >= IsDirectedSuperSend) && (annotation <= IsDirectedSuperBindingSend)))) + || (((targetMethod1->cmType)) == CMOpenPIC))) { + if (!(isValidClassTag(literal32BeforeFollowingAddress(backEnd, ((usqInt)((((sqInt)mcpc)) - 5)))))) { + /* begin unlinkSendAt:targetMethod:sendTable: */ + unlinkedRoutine = sendTable1[((((targetMethod1->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod1->cmNumArgs)) : (NumSendTrampolines - 1))]; + codeModified = 1; + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod1->selector), enumeratingCogMethod), unlinkedRoutine); + } + } + } + } + return 0; +} + + /* Cogit>>#unlinkIfLinkedSendToFree:pc:ignored: */ +static NoDbgRegParms sqInt +unlinkIfLinkedSendToFreepcignored(sqInt annotation, char *mcpc, sqInt superfluity) +{ + usqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + sqInt unlinkedRoutine; + + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (((targetMethod1->cmType)) == CMFree) { + /* begin unlinkSendAt:targetMethod:sendTable: */ + unlinkedRoutine = sendTable1[((((targetMethod1->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod1->cmNumArgs)) : (NumSendTrampolines - 1))]; + codeModified = 1; + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod1->selector), enumeratingCogMethod), unlinkedRoutine); + } + } + } + return 0; +} + + /* Cogit>>#unlinkIfLinkedSend:pc:if: */ +static NoDbgRegParms sqInt +unlinkIfLinkedSendpcif(sqInt annotation, char *mcpc, sqInt (*criterion)(CogMethod *)) +{ + usqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + sqInt unlinkedRoutine; + + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (criterion(targetMethod1)) { + /* begin unlinkSendAt:targetMethod:sendTable: */ + unlinkedRoutine = sendTable1[((((targetMethod1->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod1->cmNumArgs)) : (NumSendTrampolines - 1))]; + codeModified = 1; + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod1->selector), enumeratingCogMethod), unlinkedRoutine); + } + } + } + return 0; +} + + /* Cogit>>#unlinkIfLinkedSend:pc:ignored: */ +static NoDbgRegParms sqInt +unlinkIfLinkedSendpcignored(sqInt annotation, char *mcpc, sqInt superfluity) +{ + usqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + sqInt unlinkedRoutine; + + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + + /* begin unlinkSendAt:targetMethod:sendTable: */ + unlinkedRoutine = sendTable1[((((targetMethod1->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod1->cmNumArgs)) : (NumSendTrampolines - 1))]; + codeModified = 1; + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod1->selector), enumeratingCogMethod), unlinkedRoutine); + } + } + return 0; +} + + /* Cogit>>#unlinkIfLinkedSend:pc:to: */ +static NoDbgRegParms sqInt +unlinkIfLinkedSendpcto(sqInt annotation, char *mcpc, CogMethod *theCogMethod) +{ + usqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + sqInt unlinkedRoutine; + + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (targetMethod1 == theCogMethod) { + /* begin unlinkSendAt:targetMethod:sendTable: */ + unlinkedRoutine = sendTable1[((((targetMethod1->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod1->cmNumArgs)) : (NumSendTrampolines - 1))]; + codeModified = 1; + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod1->selector), enumeratingCogMethod), unlinkedRoutine); + } + } + } + return 0; +} + + +/* Unlink all sends in cog methods whose class tag is that of a forwarded + class. + */ + + /* Cogit>>#unlinkSendsLinkedForInvalidClasses */ +void +unlinkSendsLinkedForInvalidClasses(void) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt freedPIC; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + + if (!methodZoneBase) { + return; + } + cogMethod = ((CogMethod *) methodZoneBase); + codeModified = (freedPIC = 0); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = unlinkIfInvalidClassSendpcignored(annotation, ((char *) mcpc), 0); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + else { + if ((((cogMethod->cmType)) == CMClosedPIC) + && (cPICHasForwardedClass(cogMethod))) { + freeMethod(cogMethod); + freedPIC = 1; + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if (freedPIC) { + unlinkSendsToFree(); + } + else { + if (codeModified) { + flushICacheFromto(backEnd, ((usqInt)methodZoneBase), freeStart()); + } + } +} + + +/* Unlink all sends in cog methods. Free all PICs with the selector, or + Closed PICs with an + MNU case if isMNUSelector. First check if any method actually has the + selector; if not + there can't be any linked send to it. This routine (including descendents) + is performance + critical. It contributes perhaps 30% of entire execution time in Compiler + recompileAll. */ + + /* Cogit>>#unlinkSendsOf:isMNUSelector: */ +void +unlinkSendsOfisMNUSelector(sqInt selector, sqInt isMNUSelector) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt mustScanAndUnlink; + sqInt result; + + if (!methodZoneBase) { + return; + } + cogMethod = ((CogMethod *) methodZoneBase); + mustScanAndUnlink = 0; + if (isMNUSelector) { + while (cogMethod < (limitZony())) { + if (!(((cogMethod->cmType)) == CMFree)) { + if (/* cpicHasMNUCase */ + ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + && (((cogMethod->cmType)) == CMClosedPIC)) { + assert(isCMClosedPIC(((CogBlockMethod *) cogMethod))); + freeMethod(cogMethod); + mustScanAndUnlink = 1; + } + else { + if (((cogMethod->selector)) == selector) { + mustScanAndUnlink = 1; + if (/* isCMAnyPIC */ + (((cogMethod->cmType)) <= CMOpenPIC) + && (((cogMethod->cmType)) >= CMClosedPIC)) { + freeMethod(cogMethod); + } + } + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + } + else { + while (cogMethod < (limitZony())) { + if ((!(((cogMethod->cmType)) == CMFree)) + && (((cogMethod->selector)) == selector)) { + mustScanAndUnlink = 1; + if (/* isCMAnyPIC */ + (((cogMethod->cmType)) <= CMOpenPIC) + && (((cogMethod->cmType)) >= CMClosedPIC)) { + freeMethod(cogMethod); + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + } + if (!mustScanAndUnlink) { + return; + } + codeModified = 0; + voidOpenPICList(); + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = unlinkIfFreeOrLinkedSendpcof(annotation, ((char *) mcpc), ((CogMethod *) selector)); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + else { + if (((cogMethod->cmType)) == CMOpenPIC) { + /* begin setCodeModified */ + codeModified = 1; + addToOpenPICList(((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta))); + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if (codeModified) { + flushICacheFromto(backEnd, ((usqInt)methodZoneBase), freeStart()); + } + + /* After possibly updating inline caches we need to flush the icache. + And ensure code zone is executable. May merely have freed methods... */ + + /* begin ensureExecutableCodeZone */ +} + + +/* Unlink all sends in cog methods to free methods and/or pics. */ + + /* Cogit>>#unlinkSendsToFree */ +static void +unlinkSendsToFree(void) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + + if (!methodZoneBase) { + return; + } + codeModified = 0; + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = unlinkIfLinkedSendToFreepcignored(annotation, ((char *) mcpc), 0); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + else { + if (((cogMethod->cmType)) == CMClosedPIC) { + assert(noTargetsFreeInClosedPIC(cogMethod)); + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if (codeModified) { + flushICacheFromto(backEnd, ((usqInt)methodZoneBase), freeStart()); + } +} + + +/* Unlink all sends in cog methods to methods with a machine code + primitive, and free machine code primitive methods if freeIfTrue. + To avoid having to scan PICs, free any and all PICs */ + + /* Cogit>>#unlinkSendsToMethodsSuchThat:AndFreeIf: */ +void +unlinkSendsToMethodsSuchThatAndFreeIf(sqInt (*criterion)(CogMethod *), sqInt freeIfTrue) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt freedSomething; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + + if (!methodZoneBase) { + return; + } + codeModified = (freedSomething = 0); + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + if (freeIfTrue + && (criterion(cogMethod))) { + freeMethod(cogMethod); + freedSomething = 1; + } + else { + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = unlinkIfLinkedSendpcif(annotation, ((char *) mcpc), ((void *)criterion)); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + } + else { + if (((cogMethod->cmType)) == CMClosedPIC) { + freeMethod(cogMethod); + freedSomething = 1; + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if (freedSomething) { + unlinkSendsToFree(); + } + else { + if (codeModified) { + flushICacheFromto(backEnd, ((usqInt)methodZoneBase), freeStart()); + } + } + + /* begin ensureExecutableCodeZone */ +} + + +/* Unlink all sends in cog methods to a particular target method. + If targetMethodObject isn't actually a method (perhaps being + used via invokeAsMethod) then there's nothing to do. */ + + /* Cogit>>#unlinkSendsTo:andFreeIf: */ +void +unlinkSendsToandFreeIf(sqInt targetMethodObject, sqInt freeIfTrue) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt freedPIC; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + CogMethod *targetMethod; + + if (!((isOopCompiledMethod(targetMethodObject)) + && (methodHasCogMethod(targetMethodObject)))) { + return; + } + targetMethod = cogMethodOf(targetMethodObject); + if (!methodZoneBase) { + return; + } + + /* begin ensureWritableCodeZone */ + codeModified = (freedPIC = 0); + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = unlinkIfLinkedSendpcto(annotation, ((char *) mcpc), targetMethod); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + else { + if ((((cogMethod->cmType)) == CMClosedPIC) + && (cPICHasTarget(cogMethod, targetMethod))) { + freeMethod(cogMethod); + freedPIC = 1; + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if (freeIfTrue) { + freeMethod(targetMethod); + } + if (freedPIC) { + unlinkSendsToFree(); + } + else { + if (codeModified) { + flushICacheFromto(backEnd, ((usqInt)methodZoneBase), freeStart()); + } + } + + /* begin ensureExecutableCodeZone */ +} + + /* Cogit>>#v3:Block:Code:Size: */ +static NoDbgRegParms sqInt +v3BlockCodeSize(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + assert(nExts <= 0); + return ((((usqInt)((fetchByteofObject(pc + 2, aMethodObj))) << 8))) + (fetchByteofObject(pc + 3, aMethodObj)); +} + + +/* Answer the distance of a two byte forward long jump. */ + + /* Cogit>>#v3:LongForward:Branch:Distance: */ +static NoDbgRegParms sqInt +v3LongForwardBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + assert(nExts == 0); + return ((((fetchByteofObject(pc, aMethodObj)) & 3) << 8)) + (fetchByteofObject(pc + 1, aMethodObj)); +} + + +/* Answer the distance of a two byte forward long jump. */ + + /* Cogit>>#v3:Long:Branch:Distance: */ +static NoDbgRegParms sqInt +v3LongBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + assert(nExts == 0); + return ((((usqInt)((((fetchByteofObject(pc, aMethodObj)) & 7) - 4)) << 8))) + (fetchByteofObject(pc + 1, aMethodObj)); +} + + +/* N.B. This serves for both BlueBook/V3 and V4 short jumps. */ + + /* Cogit>>#v3:ShortForward:Branch:Distance: */ +static NoDbgRegParms sqInt +v3ShortForwardBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + assert(nExts == 0); + return ((fetchByteofObject(pc, aMethodObj)) & 7) + 1; +} + + +/* 253 11111101 eei i i kkk jjjjjjjj Push Closure Num Copied iii (+ Ext A + // 16 * 8) Num Args kkk (+ Ext A \\ 16 * 8) BlockSize jjjjjjjj (+ Ext B * + 256). ee = num extensions + */ + + /* Cogit>>#v4:Block:Code:Size: */ +static NoDbgRegParms sqInt +v4BlockCodeSize(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + sqInt byte; + sqInt byteOne; + sqInt extAValue; + sqInt extBValue; + sqInt extBValueSqInt; + sqInt extByte; + sqInt pcSqInt; + + byteOne = fetchByteofObject(pc + 1, aMethodObj); + + /* If nExts < 0 it isn't known and we rely on the number of extensions encoded in the eeiiikkk byte. */ + assert((nExts < 0) + || (nExts == (((usqInt)(byteOne)) >> 6))); + + /* begin parseV4Exts:priorTo:in:into: */ + extAValue = (extBValueSqInt = 0); + pcSqInt = (pc - (((usqInt)(byteOne)) >> 6)) - (((usqInt)(byteOne)) >> 6); + while (pcSqInt < pc) { + byte = fetchByteofObject(pcSqInt, aMethodObj); + pcSqInt += 1; + extByte = fetchByteofObject(pcSqInt, aMethodObj); + pcSqInt += 1; + assert((byte == 224) + || (byte == 225)); + if (byte == 224) { + extAValue = (((((usqInt)(extAValue) << 8)))) + extByte; + } + else { + extBValueSqInt = ((extBValueSqInt == 0) + && (extByte > 0x7F) + ? extByte - 0x100 + : (((((usqInt)(extBValueSqInt) << 8)))) + extByte); + } + } + extBValue = extBValueSqInt; + return (fetchByteofObject(pc + 2, aMethodObj)) + ((((usqInt)(extBValue) << 8))); +} + + +/* 242 11110010 i i i i i i i i Jump i i i i i i i i (+ Extend B * 256, + where bbbbbbbb = sddddddd, e.g. -32768 = i=0, a=0, s=1) + */ +/* 243 11110011 i i i i i i i i Pop and Jump 0n True i i i i i i i i (+ + Extend A * 256) + */ +/* 244 11110100 i i i i i i i i Pop and Jump 0n False i i i i i i i i (+ + Extend A * 256) + */ + + /* Cogit>>#v4:LongForward:Branch:Distance: */ +static NoDbgRegParms sqInt +v4LongForwardBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + sqInt byte; + sqInt extAValue; + sqInt extBValue; + sqInt extBValueSqInt; + sqInt extByte; + sqInt pcSqInt; + + assert(nExts >= 0); + + /* begin parseV4Exts:priorTo:in:into: */ + extAValue = (extBValueSqInt = 0); + pcSqInt = (pc - nExts) - nExts; + while (pcSqInt < pc) { + byte = fetchByteofObject(pcSqInt, aMethodObj); + pcSqInt += 1; + extByte = fetchByteofObject(pcSqInt, aMethodObj); + pcSqInt += 1; + assert((byte == 224) + || (byte == 225)); + if (byte == 224) { + extAValue = (((((usqInt)(extAValue) << 8)))) + extByte; + } + else { + extBValueSqInt = ((extBValueSqInt == 0) + && (extByte > 0x7F) + ? extByte - 0x100 + : (((((usqInt)(extBValueSqInt) << 8)))) + extByte); + } + } + extBValue = extBValueSqInt; + return (fetchByteofObject(pc + 1, aMethodObj)) + ((((usqInt)(extBValue) << 8))); +} + + +/* 242 11110010 i i i i i i i i Jump i i i i i i i i (+ Extend B * 256, + where bbbbbbbb = sddddddd, e.g. -32768 = i=0, a=0, s=1) + */ + + /* Cogit>>#v4:Long:Branch:Distance: */ +static NoDbgRegParms sqInt +v4LongBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + sqInt byte; + sqInt extAValue; + sqInt extBValue; + sqInt extBValueSqInt; + sqInt extByte; + sqInt pcSqInt; + + assert(nExts >= 0); + + /* begin parseV4Exts:priorTo:in:into: */ + extAValue = (extBValueSqInt = 0); + pcSqInt = (pc - nExts) - nExts; + while (pcSqInt < pc) { + byte = fetchByteofObject(pcSqInt, aMethodObj); + pcSqInt += 1; + extByte = fetchByteofObject(pcSqInt, aMethodObj); + pcSqInt += 1; + assert((byte == 224) + || (byte == 225)); + if (byte == 224) { + extAValue = (((((usqInt)(extAValue) << 8)))) + extByte; + } + else { + extBValueSqInt = ((extBValueSqInt == 0) + && (extByte > 0x7F) + ? extByte - 0x100 + : (((((usqInt)(extBValueSqInt) << 8)))) + extByte); + } + } + extBValue = extBValueSqInt; + return (fetchByteofObject(pc + 1, aMethodObj)) + ((((usqInt)(extBValue) << 8))); +} + + /* Cogit>>#voidCogCompiledCode */ +void +voidCogCompiledCode(void) +{ + CogMethod *cogMethod; + usqInt theLimitAddress; + usqInt theStartAddress; + + /* begin clearCogCompiledCode */ + cogMethod = ((CogMethod *) baseAddress); + while ((((usqInt)cogMethod)) < mzFreeStart) { + if (((cogMethod->cmType)) >= CMMethod) { + freeMethod(cogMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + theStartAddress = baseAddress; + theLimitAddress = limitAddress; + + /* begin manageFrom:to: */ + mzFreeStart = (baseAddress = theStartAddress); + youngReferrers = (limitAddress = theLimitAddress); + openPICList = null; + methodBytesFreedSinceLastCompaction = 0; + methodCount = 0; + + /* code must still be executable, e.g. for ceInvokeInterpret */ + + /* begin ensureExecutableCodeZone */ +} + + +/* Access for the object representations when they need to prepend code to + trampolines. + */ +/* Eliminate stale dependent info. */ + + /* Cogit>>#zeroOpcodeIndex */ +static void +zeroOpcodeIndex(void) +{ + sqInt i; + + for (i = 0; i < opcodeIndex; i += 1) { + ((abstractOpcodes[i]).dependent = null); + } + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; +} + + /* CogMethodZone>>#addToOpenPICList: */ +static NoDbgRegParms void +addToOpenPICList(CogMethod *anOpenPIC) +{ + assert(isCMOpenPIC(((CogBlockMethod *) anOpenPIC))); + assert((openPICList == null) + || (isCMOpenPIC(((CogBlockMethod *) openPICList)))); + assertValidDualZoneWriteAddress(anOpenPIC); + (anOpenPIC->nextOpenPIC = ((usqInt)openPICList)); + openPICList = ((CogMethod *) ((((usqInt)anOpenPIC)) - (getCodeToDataDelta()))); +} + + /* CogMethodZone>>#addToYoungReferrers: */ +static NoDbgRegParms void +addToYoungReferrers(CogMethod *writableCogMethod) +{ + assertValidDualZoneWriteAddress(writableCogMethod); + assert((occurrencesInYoungReferrers(writableCogMethod)) == 0); + assert((writableCogMethod->cmRefersToYoung)); + assert((youngReferrers <= limitAddress) + && (youngReferrers >= (limitAddress - (methodCount * BytesPerWord)))); + if (!(asserta((limitAddress - (methodCount * BytesPerWord)) >= mzFreeStart))) { + error("no room on youngReferrers list"); + } + youngReferrers -= BytesPerWord; + codeLongAtput((void *)(youngReferrers),(((usqInt)writableCogMethod)) - (getCodeToDataDelta())); +} + + /* CogMethodZone>>#allocate: */ +static NoDbgRegParms usqInt +allocate(sqInt numBytes) +{ + usqInt allocation; + int roundedBytes; + + roundedBytes = (numBytes + 7) & -8; + if ((mzFreeStart + roundedBytes) >= (limitAddress - (methodCount * BytesPerWord))) { + return 0; + } + allocation = mzFreeStart; + mzFreeStart += roundedBytes; + methodCount += 1; + return allocation; +} + + +/* Answer the method containing mcpc for the purposes of code zone + compaction, where mcpc is actually the value of instructionPointer at the + time of a compaction. */ + + /* CogMethodZone>>#cogMethodContaining: */ +CogMethod * +cogMethodContaining(usqInt mcpc) +{ + CogMethod *cogMethod; + CogMethod *prevMethod; + + if (mcpc > limitAddress) { + return null; + } + if (mcpc < baseAddress) { + /* assertMcpcIsPrimReturn: */ + assert((mcpc == cePrimReturnEnterCogCode) + || (mcpc == cePrimReturnEnterCogCodeProfiling)); + return null; + } + assert(mcpc < (freeStart())); + cogMethod = ((CogMethod *) baseAddress); + + /* Speed up the search by finding the entry in the openPICList before mcpc. + Typically 2 to 3% of the zone's entities are openPICs. The openPICList is ordered high to low. */ + if (openPICList) { + prevMethod = openPICList; + while (!((!prevMethod) + || ((((usqInt)prevMethod)) < mcpc))) { + prevMethod = ((CogMethod *) ((prevMethod->nextOpenPIC))); + } + if (prevMethod) { + cogMethod = prevMethod; + } + } + + /* Now search upward from either baseAddress or the first entry in the openPICList below mcpc. */ + while ((((usqInt)cogMethod)) < mcpc) { + prevMethod = cogMethod; + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + return prevMethod; +} + + /* CogMethodZone>>#compactCompiledCode */ +static void +compactCompiledCode(void) +{ + unsigned short bytes; + CogMethod *dest; + CogMethod *source; + CogMethod *writableVersion; + + compactionInProgress = 1; + methodCount = 0; + source = ((CogMethod *) baseAddress); + voidOpenPICList(); + while ((source < (limitZony())) + && (!(((source->cmType)) == CMFree))) { + assert((cogMethodDoesntLookKosher(source)) == 0); + writableVersion = ((CogMethod *) ((((usqInt)source)) + codeToDataDelta)); + (writableVersion->objectHeader = nullHeaderForMachineCodeMethod()); + if (((source->cmUsageCount)) > 0) { + (writableVersion->cmUsageCount = ((source->cmUsageCount)) / 2); + } + + /* begin maybeLinkOnUnpairedMethodList: */ + if (((source->cmType)) == CMOpenPIC) { + addToOpenPICList(writableVersion); + } + methodCount += 1; + source = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)source)) + ((source->blockSize))))); + } + if (source >= (limitZony())) { + haltmsg("no free methods; cannot compact."); + return; + } + dest = source; + while (source < (limitZony())) { + assert((maybeFreeCogMethodDoesntLookKosher(source)) == 0); + bytes = (source->blockSize); + if (!(((source->cmType)) == CMFree)) { + methodCount += 1; + codeMemmove(dest, source, bytes); + + /* begin maybeFlushWritableZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + if (codeToDataDelta > 0) { + flushDCacheFromto(backEnd, ((usqInt)dest), (((usqInt)dest)) + bytes); + } +# endif + + ((writableVersion = ((CogMethod *) ((((usqInt)dest)) + codeToDataDelta)))->objectHeader = nullHeaderForMachineCodeMethod()); + if (((dest->cmType)) >= CMMethod) { + /* For non-Newspeak there should be a one-to-one mapping between bytecoded and + cog methods. For Newspeak not necessarily, but only for anonymous accessors. + Only update the original method's header if it is referring to this CogMethod. */ + if ((((sqInt)(rawHeaderOf((dest->methodObject))))) == (((sqInt)source))) { + rawHeaderOfput((dest->methodObject), ((sqInt)dest)); + } + else { + assert((noAssertMethodClassAssociationOf((dest->methodObject))) == (nilObject())); + + /* begin linkOnUnpairedMethodList: */ + } + } + else { + /* begin clearSavedPICUsageCount: */ + if (((dest->cmType)) == CMOpenPIC) { + addToOpenPICList(writableVersion); + } + } + if (((dest->cmUsageCount)) > 0) { + (writableVersion->cmUsageCount = ((dest->cmUsageCount)) / 2); + } + + /* begin maybeFlushWritableZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + if (codeToDataDelta > 0) { + flushDCacheFromto(backEnd, ((usqInt)dest), ((usqInt)(dest + 1))); + } +# endif + + dest = ((CogMethod *) ((((usqInt)dest)) + bytes)); + } + source = ((CogMethod *) ((((usqInt)source)) + bytes)); + } + mzFreeStart = ((usqInt)dest); + methodBytesFreedSinceLastCompaction = 0; + compactionInProgress = 0; +} + + /* CogMethodZone>>#ensureInYoungReferrers: */ +static NoDbgRegParms void +ensureInYoungReferrers(CogMethod *cogMethod) +{ + CogMethod *writableMethod; + + assertValidDualZoneReadAddress(cogMethod); + if (!((cogMethod->cmRefersToYoung))) { + assert((occurrencesInYoungReferrers(cogMethod)) == 0); + + /* begin ensureWritableCodeZone */ + ((writableMethod = ((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)))->cmRefersToYoung = 1); + addToYoungReferrers(writableMethod); + } +} + + /* CogMethodZone>>#freeMethod: */ +static NoDbgRegParms void +freeMethod(CogMethod *cogMethod) +{ + CogMethod *writableMethod; + + assert(!((isCMFree(((CogBlockMethod *) cogMethod))))); + assert((cogMethodDoesntLookKosher(cogMethod)) == 0); + + /* begin ensureWritableCodeZone */ + if (((cogMethod->cmType)) >= CMMethod) { + if (((cogMethod->cmType)) == CMMethodFlaggedForBecome) { + } + else { + /* For non-Newspeak there should be a one-to-one mapping between bytecoded and + cog methods. For Newspeak not necessarily, but only for anonymous accessors. + Only reset the original method's header if it is referring to this CogMethod. */ + if ((((sqInt)(rawHeaderOf((cogMethod->methodObject))))) == (((sqInt)cogMethod))) { + rawHeaderOfput((cogMethod->methodObject), (cogMethod->methodHeader)); + } + else { + assert((noAssertMethodClassAssociationOf((cogMethod->methodObject))) == (nilObject())); + } + } + } + if (((cogMethod->cmType)) == CMOpenPIC) { + removeFromOpenPICList(cogMethod); + } + writableMethod = ((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)); + (writableMethod->cmRefersToYoung = 0); + (writableMethod->cmType = CMFree); + methodBytesFreedSinceLastCompaction += (cogMethod->blockSize); +} + + +/* Free methods, preferring older methods for compaction, up to some + fraction, currently a quarter. + */ + + /* CogMethodZone>>#freeOlderMethodsForCompaction */ +static void +freeOlderMethodsForCompaction(void) +{ + sqInt amountToFree; + CogMethod *cogMethod; + sqInt freeableUsage; + sqInt freedSoFar; + sqInt initialFreeSpace; + sqInt zoneSize; + + zoneSize = limitAddress - baseAddress; + initialFreeSpace = (limitAddress - mzFreeStart) + methodBytesFreedSinceLastCompaction; + freedSoFar = initialFreeSpace; + + /* 4 needs to be e.g. a start-up parameter */ + amountToFree = zoneSize / 4; + freeableUsage = 0; + do { + cogMethod = ((CogMethod *) baseAddress); + while (((((usqInt)cogMethod)) < mzFreeStart) + && (freedSoFar < amountToFree)) { + if (/* shouldFreeMethod:given: */ + (!(((cogMethod->cmType)) == CMFree)) + && (((cogMethod->cmUsageCount)) <= freeableUsage)) { + freeMethod(cogMethod); + freedSoFar += (cogMethod->blockSize); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + } while((freedSoFar < amountToFree) + && (((freeableUsage += 1)) < CMMaxUsageCount)); +} + + +/* Answer that all entries in youngReferrers are in-use and have the + cmRefersToYoung flag set. + Used to check that the youngreferrers pruning routines work correctly. */ + + /* CogMethodZone>>#kosherYoungReferrers */ +sqInt +kosherYoungReferrers(void) +{ + CogMethod *cogMethod; + usqInt pointer; + CogMethod *prevMethod; + + if ((youngReferrers > limitAddress) + || (youngReferrers < mzFreeStart)) { + return 0; + } + pointer = youngReferrers; + while (pointer < limitAddress) { + cogMethod = ((CogMethod *) (longAt((void *)(pointer)))); + if (!(((cogMethod->cmType)) == CMFree)) { + if (!((cogMethod->cmRefersToYoung))) { + return 0; + } + if ((occurrencesInYoungReferrers(cogMethod)) != 1) { + return 0; + } + } + pointer += BytesPerWord; + } + cogMethod = ((CogMethod *) baseAddress); + while (cogMethod < (limitZony())) { + prevMethod = cogMethod; + if (!(((cogMethod->cmType)) == CMFree)) { + if ((occurrencesInYoungReferrers(cogMethod)) != (((cogMethod->cmRefersToYoung) + ? 1 + : 0))) { + return 0; + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + if (cogMethod == prevMethod) { + return 0; + } + } + return 1; +} + + /* CogMethodZone>>#methodFor: */ +CogMethod * +methodFor(void *address) +{ + CogMethod *cogMethod; + CogMethod *nextMethod; + + if ((((usqInt)address)) < (methodZoneBase)) { + return null; + } + cogMethod = ((CogMethod *) baseAddress); + + /* Speed up the search by finding the entry in the openPICList before mcpc. + Typically 2 to 3% of the zone's entities are openPICs. The openPICList is ordered high to low. */ + if (openPICList) { + nextMethod = openPICList; + while (!((!nextMethod) + || ((((usqInt)nextMethod)) < (((usqInt)address))))) { + nextMethod = ((CogMethod *) ((nextMethod->nextOpenPIC))); + } + if (nextMethod) { + cogMethod = nextMethod; + } + } + while ((cogMethod < (limitZony())) + && ((((usqInt)cogMethod)) <= (((usqInt)address)))) { + nextMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + if (nextMethod == cogMethod) { + return null; + } + if (((((usqInt)address)) >= (((usqInt)cogMethod))) + && ((((usqInt)address)) < (((usqInt)nextMethod)))) { + return cogMethod; + } + cogMethod = nextMethod; + } + return null; +} + + /* CogMethodZone>>#methodsCompiledToMachineCodeInto: */ +#if VMInvestigations +sqInt +methodsCompiledToMachineCodeInto(sqInt arrayObj) +{ + CogMethod *cogMethod1; + sqInt methodIndex; + + methodIndex = 0; + + /* begin methodsDo: */ + cogMethod1 = ((CogMethod *) baseAddress); + while (cogMethod1 < (limitZony())) { + if (!(((cogMethod1->cmType)) == CMFree)) { + if (((cogMethod1->cmType)) >= CMMethod) { + storePointerUncheckedofObjectwithValue(methodIndex, arrayObj, (cogMethod1->methodObject)); + methodIndex += 1; + } + } + cogMethod1 = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod1)) + ((cogMethod1->blockSize))))); + } + return methodIndex; +} +#endif /* VMInvestigations */ + + /* CogMethodZone>>#numMethods */ +sqInt +numMethods(void) +{ + return methodCount; +} + + /* CogMethodZone>>#numMethodsOfType: */ +sqInt +numMethodsOfType(sqInt cogMethodType) +{ + CogMethod *cogMethod; + sqInt n; + + n = 0; + cogMethod = ((CogMethod *) baseAddress); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) == cogMethodType) { + n += 1; + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + return n; +} + + /* CogMethodZone>>#occurrencesInYoungReferrers: */ +static NoDbgRegParms sqInt +occurrencesInYoungReferrers(CogMethod *cogMethod) +{ + sqInt count; + usqInt pointer; + + assert(youngReferrers <= limitAddress); + count = 0; + pointer = youngReferrers; + while (pointer < limitAddress) { + if ((((sqInt)cogMethod)) == (longAt((void *)(pointer)))) { + count += 1; + } + pointer += BytesPerWord; + } + return count; +} + + /* CogMethodZone>>#openPICWithSelector: */ +static NoDbgRegParms CogMethod * +openPICWithSelector(sqInt aSelector) +{ + CogMethod *openPIC; + + openPIC = openPICList; + do { + if ((openPIC == null) + || (((openPIC->selector)) == aSelector)) { + return openPIC; + } + + /* N.B. Use self rather than coInterpreter to avoid attempting to cast nil. + Conversion to CogMethod done in the nextOpenPIC accessor. */ + openPIC = ((CogMethod *) ((openPIC->nextOpenPIC))); + } while(1); + return 0; +} + + +/* Some methods have been freed. Compute how much each survivor needs to + move during the ensuing compaction and record it in the objectHeader + field. + For Sista, where we want PICs to last so they can be observed, we need to + keep PICs unless + they are definitely unused. So we need to identify unused PICs. So in + planCompact, zero the + usage counts of all PICs, saving the actual usage count in + blockEntryOffset. Then in + relocateMethodsPreCompaction (actually in + relocateIfCallOrMethodReference:mcpc:delta:) restore the usage counts of + used PICs. Finally in compactCompiledCode, clear the blockEntryOffset + of the unused PICs; they will then have a zero count and be reclaimed in + the next code compaction. */ + + /* CogMethodZone>>#planCompaction */ +static void +planCompaction(void) +{ + CogMethod *cogMethod; + sqInt delta; + + delta = 0; + cogMethod = ((CogMethod *) baseAddress); + while ((((usqInt)cogMethod)) < mzFreeStart) { + if (((cogMethod->cmType)) == CMFree) { + delta -= (cogMethod->blockSize); + } + else { + assert((cogMethodDoesntLookKosher(cogMethod)) == 0); + ((((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)))->objectHeader = delta); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } +} + + +/* useful for debugging */ + + /* CogMethodZone>>#printCogMethods */ +void +printCogMethods(void) +{ + CogMethod *cogMethod; + sqInt nb; + sqInt nc; + sqInt nf; + sqInt nm; + sqInt no; + sqInt nu; + + /* begin printCogMethodsSummarizing: */ + nm = (nb = (nc = (no = (nf = (nu = 0))))); + cogMethod = ((CogMethod *) baseAddress); + while (cogMethod < (limitZony())) { + printCogMethod(cogMethod); + switch ((cogMethod->cmType)) { + case CMFree: + nf += 1; + break; + case CMMethod: + nm += 1; + break; + case CMMethodFlaggedForBecome: + nb += 1; + break; + case CMClosedPIC: + nc += 1; + break; + case CMOpenPIC: + no += 1; + break; + default: + nu += 1; + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + print("CMMethod "); + printNum(nm); + if (nb > 0) { + print(" (flagged for become: "); + printNum(nb); + print(")"); + } + print(" CMClosedPIC "); + printNum(nc); + print(" CMOpenPIC "); + printNum(no); + print(" CMFree "); + printNum(nf); + if (nu > 0) { + print(" UNKNOWN "); + printNum(nu); + } + print(" total "); + printNum(((((nm + nc) + no) + nf) + nu) + nb); + cr(); +} + + +/* useful for debugging */ + + /* CogMethodZone>>#printCogMethodsOfType: */ +void +printCogMethodsOfType(sqInt cmType) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) baseAddress); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) == cmType) { + printCogMethod(cogMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } +} + + +/* useful for debugging */ + + /* CogMethodZone>>#printCogMethodsWithMethod: */ +void +printCogMethodsWithMethod(sqInt methodOop) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) baseAddress); + while (cogMethod < (limitZony())) { + if ((!(((cogMethod->cmType)) == CMFree)) + && (((cogMethod->methodObject)) == methodOop)) { + printCogMethod(cogMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } +} + + +/* useful for debugging */ + + /* CogMethodZone>>#printCogMethodsWithPrimitive: */ +void +printCogMethodsWithPrimitive(sqInt primIdx) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) baseAddress); + while (cogMethod < (limitZony())) { + if ((((cogMethod->cmType)) >= CMMethod) + && (primIdx == (primitiveIndexOfMethodheader((cogMethod->methodObject), (cogMethod->methodHeader))))) { + printCogMethod(cogMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } +} + + +/* useful for debugging */ + + /* CogMethodZone>>#printCogMethodsWithSelector: */ +void +printCogMethodsWithSelector(sqInt selectorOop) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) baseAddress); + while (cogMethod < (limitZony())) { + if ((!(((cogMethod->cmType)) == CMFree)) + && (((cogMethod->selector)) == selectorOop)) { + printCogMethod(cogMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } +} + + +/* useful for debugging */ + + /* CogMethodZone>>#printCogYoungReferrers */ +void +printCogYoungReferrers(void) +{ + CogMethod *cogMethod; + usqInt pointer; + + pointer = youngReferrers; + while (pointer < limitAddress) { + cogMethod = ((CogMethod *) (longAt((void *)(pointer)))); + if (!((cogMethod->cmRefersToYoung))) { + print("*"); + } + if (((cogMethod->cmType)) == CMFree) { + print("!"); + } + if (!(((cogMethod->cmRefersToYoung)) + && (!(((cogMethod->cmType)) == CMFree)))) { + print(" "); + } + printCogMethod(cogMethod); + pointer += BytesPerWord; + } +} + + +/* useful for debugging */ + + /* CogMethodZone>>#printOpenPICList */ +sqInt +printOpenPICList(void) +{ + sqInt n; + CogMethod *openPIC; + + /* begin printOpenPICListSummarizing: */ + n = 0; + openPIC = openPICList; + while (!(openPIC == null)) { + n += 1; + printCogMethod(openPIC); + openPIC = ((CogMethod *) ((openPIC->nextOpenPIC))); + } + return n; +} + + /* CogMethodZone>>#pruneYoungReferrers */ +static sqInt +pruneYoungReferrers(void) +{ + usqInt dest; + usqInt next; + usqInt source; + + next = 0; + assert(youngReferrers <= limitAddress); + if (youngReferrers == limitAddress) { + return null; + } + dest = limitAddress; + while (1) { + next = dest - BytesPerWord; + if (!((next >= youngReferrers) + && (((((CogMethod *) (longAt((void *)(next)))))->cmRefersToYoung)))) break; + dest = next; + } + assert(dest >= youngReferrers); + source = dest - BytesPerWord; + while (source >= youngReferrers) { + if (((((CogMethod *) (longAt((void *)(source)))))->cmRefersToYoung)) { + assert(source < (dest - BytesPerWord)); + if (next) { + next = null; + + /* begin ensureWritableCodeZone */ + } + + /* convenient first-time flag */ + codeLongAtput((void *)((dest -= BytesPerWord)),longAt((void *)(source))); + } + source -= BytesPerWord; + } + youngReferrers = dest; + assert(kosherYoungReferrers()); + return 0; +} + + /* CogMethodZone>>#relocateAndPruneYoungReferrers */ +static sqInt +relocateAndPruneYoungReferrers(void) +{ + CogMethod *cogMethod; + usqInt dest; + usqInt next; + usqInt source; + + cogMethod = ((CogMethod *) 0); + next = 0; + assert(youngReferrers <= limitAddress); + if (youngReferrers == limitAddress) { + return null; + } + dest = limitAddress; + while (1) { + next = dest - BytesPerWord; + if (!((next >= youngReferrers) + && ((!(isCMFree(((CogBlockMethod *) ((cogMethod = ((CogMethod *) (longAt((void *)(next)))))))))) + && ((cogMethod->cmRefersToYoung))))) break; + if ((cogMethod->objectHeader)) { + codeLongAtput((void *)(next),(((sqInt)cogMethod)) + ((cogMethod->objectHeader))); + } + dest = next; + } + assert(dest >= youngReferrers); + source = dest - BytesPerWord; + while (source >= youngReferrers) { + cogMethod = ((CogMethod *) (longAt((void *)(source)))); + if ((!(((cogMethod->cmType)) == CMFree)) + && ((cogMethod->cmRefersToYoung))) { + assert(source < (dest - BytesPerWord)); + if ((cogMethod->objectHeader)) { + cogMethod = ((CogMethod *) ((((sqInt)cogMethod)) + (((sqInt)((cogMethod->objectHeader)))))); + } + codeLongAtput((void *)((dest -= BytesPerWord)),((sqInt)cogMethod)); + } + source -= BytesPerWord; + } + + /* this assert must be deferred until after compaction. See the end of compactCogCompiledCode */ + /* self assert: self kosherYoungReferrers */ + youngReferrers = dest; + return 0; +} + + +/* All surviving methods have had the amount they are going to relocate by + stored in their objectHeader fields. Relocate all relative calls so that + after the compaction of both the method containing each call and the call + target the calls invoke the same target. */ + + /* CogMethodZone>>#relocateMethodsPreCompaction */ +static sqInt +relocateMethodsPreCompaction(void) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) baseAddress); + while ((((usqInt)cogMethod)) < mzFreeStart) { + if (!(((cogMethod->cmType)) == CMFree)) { + if (((cogMethod->cmType)) == CMClosedPIC) { + relocateCallsInClosedPIC(cogMethod); + } + else { + relocateCallsAndSelfReferencesInMethod(cogMethod); + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + relocateAndPruneYoungReferrers(); + return 1; +} + + /* CogMethodZone>>#removeFromOpenPICList: */ +static NoDbgRegParms sqInt +removeFromOpenPICList(CogMethod *anOpenPIC) +{ + CogMethod *prevPIC; + + assert(isCMOpenPIC(((CogBlockMethod *) anOpenPIC))); + if (!openPICList) { + return null; + } + assert((isCMOpenPIC(((CogBlockMethod *) openPICList))) + && ((!((openPICList->nextOpenPIC))) + || (isCMOpenPIC(((CogBlockMethod *) (((CogMethod *) ((openPICList->nextOpenPIC))))))))); + if (anOpenPIC == openPICList) { + /* N.B. Use self rather than coInterpreter to avoid attempting to cast nil. + Conversion to CogMethod done in the nextOpenPIC accessor. */ + openPICList = ((CogMethod *) ((anOpenPIC->nextOpenPIC))); + return null; + } + prevPIC = openPICList; + do { + assert((prevPIC != null) + && (isCMOpenPIC(((CogBlockMethod *) prevPIC)))); + if (((prevPIC->nextOpenPIC)) == (((usqInt)anOpenPIC))) { + ((((CogMethod *) ((((usqInt)prevPIC)) + codeToDataDelta)))->nextOpenPIC = (anOpenPIC->nextOpenPIC)); + return null; + } + prevPIC = ((CogMethod *) ((prevPIC->nextOpenPIC))); + } while(1); + return 0; +} + + +/* Determine the default alignment for the start of a CogMethod, which in + turn determines the size of the mask used to distinguish the checked and + unchecked entry-points, used to distinguish normal and super sends on + method unlinking. + This is passed onto the backEnd to allow processors with coarse + instructions (ARM) to increase the alignment if required. */ + + /* CogMethodZone>>#roundUpLength: */ +static NoDbgRegParms int +roundUpLength(sqInt numBytes) +{ + return roundUpToMethodAlignment(backEnd, numBytes); +} + + /* CogMethodZone>>#voidOpenPICList */ +static void +voidOpenPICList(void) +{ + openPICList = null; +} + + /* CogMethodZone>>#voidUnpairedMethodList */ +static void +voidUnpairedMethodList(void) +{ +} + + /* CogMethodZone>>#whereIsMaybeCodeThing: */ +char * +whereIsMaybeCodeThing(sqInt anOop) +{ + if (oopisGreaterThanOrEqualToandLessThan(anOop, codeBase, limitAddress)) { + if (oopisLessThan(anOop, minCogMethodAddress())) { + return " is in generated runtime"; + } + if (oopisLessThan(anOop, mzFreeStart)) { + return " is in generated methods"; + } + if (oopisLessThan(anOop, youngReferrers)) { + return " is in code zone"; + } + return " is in young referrers"; + } + return null; +} + + /* CogMethodZone>>#zoneAlignment */ +static sqInt +zoneAlignment(void) +{ + return 8; +} + + /* CogObjectRepresentation>>#checkValidObjectReference: */ +static NoDbgRegParms sqInt +checkValidObjectReference(sqInt anOop) +{ + return (!(isImmediate(anOop))) + && ((heapMapAtWord(pointerForOop(anOop))) != 0); +} + + /* CogObjectRepresentation>>#genCmpClassFloatCompactIndexR: */ +static NoDbgRegParms AbstractInstruction * +genCmpClassFloatCompactIndexR(sqInt reg) +{ + return genoperandoperand(CmpCqR, ClassFloatCompactIndex, reg); +} + + /* CogObjectRepresentation>>#genCmpClassMethodContextCompactIndexR: */ +static NoDbgRegParms AbstractInstruction * +genCmpClassMethodContextCompactIndexR(sqInt reg) +{ + return genoperandoperand(CmpCqR, ClassMethodContextCompactIndex, reg); +} + + +/* Get the method header (first word) of a CompiledMethod into headerReg. + Deal with the method possibly being cogged. */ + + /* CogObjectRepresentation>>#genGetMethodHeaderOf:into:scratch: */ +static NoDbgRegParms sqInt +genGetMethodHeaderOfintoscratch(sqInt methodReg, sqInt headerReg, sqInt scratchReg) +{ + AbstractInstruction *jumpNotCogged; + sqInt offset; + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BaseHeaderSize, methodReg, headerReg); + jumpNotCogged = genJumpSmallInteger(headerReg); + offset = offsetof(CogMethod, methodHeader); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, headerReg, headerReg); + jmpTarget(jumpNotCogged, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + +/* Generate a compare and branch to test if aRegister and bRegister contains + other than SmallIntegers, + i.e. don't branch if both aRegister and bRegister contain SmallIntegers. + Answer the jump. Destroy scratchRegister if required. */ + + /* CogObjectRepresentation>>#genJumpNotSmallIntegersIn:and:scratch: */ +static NoDbgRegParms AbstractInstruction * +genJumpNotSmallIntegersInandscratch(sqInt aRegister, sqInt bRegister, sqInt scratchRegister) +{ + /* MoveR:R: */ + genoperandoperand(MoveRR, aRegister, scratchRegister); + + /* AndR:R: */ + genoperandoperand(AndRR, bRegister, scratchRegister); + return genJumpNotSmallInteger(scratchRegister); +} + + /* CogObjectRepresentation>>#genLoadSlot:sourceReg:destReg: */ +static NoDbgRegParms sqInt +genLoadSlotsourceRegdestReg(sqInt index, sqInt sourceReg, sqInt destReg) +{ + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, (index * BytesPerWord) + BaseHeaderSize, sourceReg, destReg); + return 0; +} + + /* CogObjectRepresentation>>#genPrimitiveAdd */ +static sqInt +genPrimitiveAdd(void) +{ + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpOvfl; + + if (!(mclassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ClassReg); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + genRemoveSmallIntegerTagsInScratchReg(ClassReg); + + /* AddR:R: */ + genoperandoperand(AddRR, ReceiverResultReg, ClassReg); + jumpOvfl = genConditionalBranchoperand(JumpOverflow, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpOvfl, jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveBitAnd */ +static sqInt +genPrimitiveBitAnd(void) +{ + AbstractInstruction *jumpNotSI; + + if (!(mclassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + + /* Whether the SmallInteger tags are zero or non-zero, anding them together will preserve them. */ + + /* AndR:R: */ + genoperandoperand(AndRR, Arg0Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveBitOr */ +static sqInt +genPrimitiveBitOr(void) +{ + AbstractInstruction *jumpNotSI; + + if (!(mclassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + + /* Whether the SmallInteger tags are zero or non-zero, oring them together will preserve them. */ + + /* OrR:R: */ + genoperandoperand(OrRR, Arg0Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + +/* rTemp := rArg0 + rClass := tTemp + rTemp := rTemp & 1 + jz nonInt + rClass >>= 1 + cmp 0,rClass + jge neg + cmp 31,rClass // numSmallIntegerBits, jge for sign + jge tooBig + rTemp := rReceiver + rTemp <<= rClass + rTemp >>= rClass (arithmetic) + cmp rTemp,rReceiver + jnz ovfl + rReceiver := rReceiver - 1 + rReceiver := rReceiver <<= rClass + rReceiver := rReceiver + 1 + ret + neg: + rClass := 0 - rClass + cmp 31,rClass // numSmallIntegerBits + jge inRange + rClass := 31 + inRange + rReceiver := rReceiver >>= rClass. + rReceiver := rReceiver | smallIntegerTags. + ret + ovfl + tooBig + nonInt: + fail + */ + + /* CogObjectRepresentation>>#genPrimitiveBitShift */ +static sqInt +genPrimitiveBitShift(void) +{ + AbstractInstruction *jumpInRange; + AbstractInstruction *jumpNegative; + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpOvfl; + AbstractInstruction *jumpTooBig; + + if (!(mclassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ClassReg); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + genConvertSmallIntegerToIntegerInReg(ClassReg); + if (!(setsConditionCodesFor(lastOpcode(), JumpNegative))) { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ClassReg); + } + jumpNegative = genConditionalBranchoperand(JumpNegative, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, numSmallIntegerBits(), ClassReg); + jumpTooBig = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, TempReg); + + /* LogicalShiftLeftR:R: */ + genoperandoperand(LogicalShiftLeftRR, ClassReg, TempReg); + + /* ArithmeticShiftRightR:R: */ + genoperandoperand(ArithmeticShiftRightRR, ClassReg, TempReg); + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, ReceiverResultReg); + jumpOvfl = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genRemoveSmallIntegerTagsInScratchReg(ReceiverResultReg); + + /* LogicalShiftLeftR:R: */ + genoperandoperand(LogicalShiftLeftRR, ClassReg, ReceiverResultReg); + genAddSmallIntegerTagsTo(ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNegative, genoperand(NegateR, ClassReg)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, numSmallIntegerBits(), ClassReg); + jumpInRange = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, numSmallIntegerBits(), ClassReg); + jmpTarget(jumpInRange, genoperandoperand(ArithmeticShiftRightRR, ClassReg, ReceiverResultReg)); + genClearAndSetSmallIntegerTagsIn(ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSI, jmpTarget(jumpTooBig, jmpTarget(jumpOvfl, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveBitXor */ +static sqInt +genPrimitiveBitXor(void) +{ + AbstractInstruction *jumpNotSI; + + if (!(mclassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + + /* Clear one or the other tag so that xoring will preserve them. */ + genRemoveSmallIntegerTagsInScratchReg(Arg0Reg); + + /* XorR:R: */ + genoperandoperand(XorRR, Arg0Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveClass */ +static sqInt +genPrimitiveClass(void) +{ + sqInt reg; + + reg = ReceiverResultReg; + if (methodOrBlockNumArgs > 0) { + if (methodOrBlockNumArgs > 1) { + return UnimplementedPrimitive; + } + reg = Arg0Reg; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + } + if ((genGetClassObjectOfintoscratchRegmayBeAForwarder(reg, ReceiverResultReg, TempReg, reg != ReceiverResultReg)) == BadRegisterSet) { + genGetClassObjectOfintoscratchRegmayBeAForwarder(reg, ClassReg, TempReg, reg != ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, ReceiverResultReg); + } + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + return UnfailingPrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveDiv */ +static sqInt +genPrimitiveDiv(void) +{ + AbstractInstruction *convert; + AbstractInstruction *jumpExact; + AbstractInstruction *jumpIsSI; + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpSameSign; + AbstractInstruction *jumpZero; + + if (!(processorHasDivQuoRemAndMClassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ClassReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, Arg1Reg); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + + /* We must shift away the tags, not just subtract them, so that the + overflow case doesn't actually overflow the machine instruction. */ + genShiftAwaySmallIntegerTagsInScratchReg(ClassReg); + if (!(setsConditionCodesFor(lastOpcode(), JumpZero))) { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ClassReg); + } + jumpZero = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, TempReg); + genShiftAwaySmallIntegerTagsInScratchReg(TempReg); + gDivRRQuoRem(ClassReg, TempReg, TempReg, ClassReg); + + /* If remainder is zero we must check for overflow. */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ClassReg); + jumpExact = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* If arg and remainder signs are different we must round down. */ + + /* XorR:R: */ + genoperandoperand(XorRR, ClassReg, Arg1Reg); + if (!(setsConditionCodesFor(lastOpcode(), JumpZero))) { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, Arg1Reg); + } + jumpSameSign = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 1, TempReg); + jmpTarget(jumpSameSign, (convert = genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + genConvertIntegerInRegtoSmallIntegerInReg(TempReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* test for overflow; the only case is SmallInteger minVal // -1 */ + jmpTarget(jumpExact, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpIsSI = genJumpIsSmallIntegerValuescratch(TempReg, Arg1Reg); + jmpTarget(jumpIsSI, convert); + jmpTarget(jumpZero, jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveDivide */ +static sqInt +genPrimitiveDivide(void) +{ + AbstractInstruction *jumpInexact; + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpOverflow; + AbstractInstruction *jumpZero; + + if (!(processorHasDivQuoRemAndMClassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ClassReg); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + + /* We must shift away the tags, not just subtract them, so that the + overflow case doesn't actually overflow the machine instruction. */ + genShiftAwaySmallIntegerTagsInScratchReg(ClassReg); + jumpZero = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, TempReg); + genShiftAwaySmallIntegerTagsInScratchReg(TempReg); + gDivRRQuoRem(ClassReg, TempReg, TempReg, ClassReg); + + /* If remainder is non-zero fail. */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ClassReg); + jumpInexact = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* test for overflow; the only case is SmallInteger minVal / -1 */ + jumpOverflow = genJumpNotSmallIntegerValuescratch(TempReg, Arg1Reg); + genConvertIntegerInRegtoSmallIntegerInReg(TempReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpOverflow, jmpTarget(jumpInexact, jmpTarget(jumpZero, jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveEqual */ +static sqInt +genPrimitiveEqual(void) +{ + return (numericPrimsMixComparison() + ? genSmallIntegerComparisonorDoubleComparisoninvert(JumpZero, gJumpFPEqual, 0) + : genSmallIntegerComparison(JumpZero)); +} + + /* CogObjectRepresentation>>#genPrimitiveFloatAdd */ +static sqInt +genPrimitiveFloatAdd(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(AddRdRd, null, 1) + : genPureFloatArithmeticpreOpCheckboxed(AddRdRd, null, 1)); +} + + /* CogObjectRepresentation>>#genPrimitiveFloatDivide */ +static sqInt +genPrimitiveFloatDivide(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(DivRdRd, genDoubleFailIfZeroArgRcvrarg, 1) + : genPureFloatArithmeticpreOpCheckboxed(DivRdRd, genDoubleFailIfZeroArgRcvrarg, 1)); +} + + /* CogObjectRepresentation>>#genPrimitiveFloatMultiply */ +static sqInt +genPrimitiveFloatMultiply(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(MulRdRd, null, 1) + : genPureFloatArithmeticpreOpCheckboxed(MulRdRd, null, 1)); +} + + /* CogObjectRepresentation>>#genPrimitiveFloatSubtract */ +static sqInt +genPrimitiveFloatSubtract(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(SubRdRd, null, 1) + : genPureFloatArithmeticpreOpCheckboxed(SubRdRd, null, 1)); +} + + /* CogObjectRepresentation>>#genPrimitiveGreaterOrEqual */ +static sqInt +genPrimitiveGreaterOrEqual(void) +{ + return (numericPrimsMixComparison() + ? genSmallIntegerComparisonorDoubleComparisoninvert(JumpGreaterOrEqual, gJumpFPGreaterOrEqual, 0) + : genSmallIntegerComparison(JumpGreaterOrEqual)); +} + + /* CogObjectRepresentation>>#genPrimitiveGreaterThan */ +static sqInt +genPrimitiveGreaterThan(void) +{ + return (numericPrimsMixComparison() + ? genSmallIntegerComparisonorDoubleComparisoninvert(JumpGreater, gJumpFPGreater, 0) + : genSmallIntegerComparison(JumpGreater)); +} + + +/* Defer to the backEnd to generate the best instruction sequence. + Some backends have an efficient count-leading-zeros that must + be inverted to produce the high bit, others have a genuine high + bit. Intel has both, the former only in newer revisions. + Use TempReg. If primitive fails, ReceiverResultReg must remain + unchanged (we CompletePrimitive) */ + + /* CogObjectRepresentation>>#genPrimitiveHighBit */ +static sqInt +genPrimitiveHighBit(void) +{ + AbstractInstruction *jumpNegativeReceiver; + sqInt quickConstant; + AbstractInstruction *self_in_CogX64Compiler; + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ReceiverResultReg); + jumpNegativeReceiver = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* remove excess tag bits from the receiver oop */ + gOrCqRR((1U << (numSmallIntegerTagBits())) - 1, ReceiverResultReg, ClassReg); + quickConstant = (numSmallIntegerTagBits()) - 1; + + /* begin ArithmeticShiftRightCq:R: */ + genoperandoperand(ArithmeticShiftRightCqR, quickConstant, ClassReg); + self_in_CogX64Compiler = ((AbstractInstruction *) (backEnd)); + + /* begin genHighBitOfNonZeroReg:into: */ + if ((((cpuidWord1(self_in_CogX64Compiler)) & (32)) != 0)) { + if (!((((cpuidWord1(self_in_CogX64Compiler)) & (32)) != 0))) { + return UnimplementedPrimitive; + } + + /* ClzR:R: */ + genoperandoperand(ClzRR, ClassReg, TempReg); + + /* Invert leading zeroas into highBit. + Note the nice bit trick below: + highBit_1based_of_small_int_value = (BytesPerWord * 8) - leadingZeroCout_of_oop - 1 toAccountForTagBit. + This is like 2 complements (- reg - 1) on (BytesPerWord * 8) log2 bits, or exactly a bit invert operation... */ + + /* #XorCq:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(XorCqR, (BytesPerWord * 8) - 1, TempReg); + goto l1; + } + genoperandoperand(BSR, ClassReg, TempReg); + goto l1; +l1: + genConvertIntegerInRegtoSmallIntegerInReg(TempReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNegativeReceiver, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveIdentical */ +static sqInt +genPrimitiveIdentical(void) +{ + return genPrimitiveIdenticalOrNotIf(0); +} + + /* CogObjectRepresentation>>#genPrimitiveLessOrEqual */ +static sqInt +genPrimitiveLessOrEqual(void) +{ + return (numericPrimsMixComparison() + ? genSmallIntegerComparisonorDoubleComparisoninvert(JumpLessOrEqual, gJumpFPGreaterOrEqual, 1) + : genSmallIntegerComparison(JumpLessOrEqual)); +} + + /* CogObjectRepresentation>>#genPrimitiveLessThan */ +static sqInt +genPrimitiveLessThan(void) +{ + return (numericPrimsMixComparison() + ? genSmallIntegerComparisonorDoubleComparisoninvert(JumpLess, gJumpFPGreater, 1) + : genSmallIntegerComparison(JumpLess)); +} + + /* CogObjectRepresentation>>#genPrimitiveMaxIdentityHash */ +static sqInt +genPrimitiveMaxIdentityHash(void) +{ + sqInt quickConstant; + + quickConstant = (((usqInt)(maxIdentityHash()) << 3) | 1); + + /* begin MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, quickConstant, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + return UnfailingPrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveMod */ +static sqInt +genPrimitiveMod(void) +{ + AbstractInstruction *jumpExact; + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpSameSign; + AbstractInstruction *jumpZero; + + if (!(processorHasDivQuoRemAndMClassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ClassReg); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + genRemoveSmallIntegerTagsInScratchReg(ClassReg); + jumpZero = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, Arg1Reg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, TempReg); + genRemoveSmallIntegerTagsInScratchReg(TempReg); + gDivRRQuoRem(ClassReg, TempReg, TempReg, ClassReg); + + /* If remainder is zero we're done. */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ClassReg); + jumpExact = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* If arg and remainder signs are different we must reflect around zero. */ + + /* XorR:R: */ + genoperandoperand(XorRR, ClassReg, Arg1Reg); + if (!(setsConditionCodesFor(lastOpcode(), JumpZero))) { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, Arg1Reg); + } + jumpSameSign = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* XorR:R: */ + genoperandoperand(XorRR, ClassReg, Arg1Reg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg1Reg, ClassReg); + jmpTarget(jumpSameSign, jmpTarget(jumpExact, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + genSetSmallIntegerTagsIn(ClassReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpZero, jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveMultiply */ +static sqInt +genPrimitiveMultiply(void) +{ + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpOvfl; + + if (!(processorHasMultiplyAndMClassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ClassReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, Arg1Reg); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + genShiftAwaySmallIntegerTagsInScratchReg(ClassReg); + genRemoveSmallIntegerTagsInScratchReg(Arg1Reg); + + /* MulOverflowR:R: */ + genMulRR(backEnd, Arg1Reg, ClassReg); + jumpOvfl = genConditionalBranchoperand(JumpOverflow, ((sqInt)0)); + genSetSmallIntegerTagsIn(ClassReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpOvfl, jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + +/* subclasses override if they can */ + + /* CogObjectRepresentation>>#genPrimitiveNewMethod */ +static sqInt +genPrimitiveNewMethod(void) +{ + return UnimplementedPrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveNotEqual */ +static sqInt +genPrimitiveNotEqual(void) +{ + return (numericPrimsMixComparison() + ? genSmallIntegerComparisonorDoubleComparisoninvert(JumpNonZero, gJumpFPNotEqual, 0) + : genSmallIntegerComparison(JumpNonZero)); +} + + /* CogObjectRepresentation>>#genPrimitiveNotIdentical */ +static sqInt +genPrimitiveNotIdentical(void) +{ + return genPrimitiveIdenticalOrNotIf(1); +} + + /* CogObjectRepresentation>>#genPrimitiveQuo */ +static sqInt +genPrimitiveQuo(void) +{ + AbstractInstruction *convert; + AbstractInstruction *jumpExact; + AbstractInstruction *jumpIsSI; + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpZero; + + if (!(processorHasDivQuoRemAndMClassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ClassReg); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + + /* We must shift away the tags, not just subtract them, so that the + overflow case doesn't actually overflow the machine instruction. */ + genShiftAwaySmallIntegerTagsInScratchReg(ClassReg); + if (!(setsConditionCodesFor(lastOpcode(), JumpZero))) { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ClassReg); + } + jumpZero = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, TempReg); + genShiftAwaySmallIntegerTagsInScratchReg(TempReg); + gDivRRQuoRem(ClassReg, TempReg, TempReg, ClassReg); + + /* If remainder is zero we must check for overflow. */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ClassReg); + jumpExact = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + convert = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + genConvertIntegerInRegtoSmallIntegerInReg(TempReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpExact, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpIsSI = genJumpIsSmallIntegerValuescratch(TempReg, Arg1Reg); + jmpTarget(jumpIsSI, convert); + jmpTarget(jumpZero, jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveSubtract */ +static sqInt +genPrimitiveSubtract(void) +{ + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpOvfl; + + if (!(mclassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, Arg0Reg, TempReg); + jumpOvfl = genConditionalBranchoperand(JumpOverflow, ((sqInt)0)); + genAddSmallIntegerTagsTo(TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpOvfl, jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genSmallIntegerComparison: */ +static NoDbgRegParms sqInt +genSmallIntegerComparison(sqInt jumpOpcode) +{ + AbstractInstruction *jumpFail; + AbstractInstruction *jumpTrue; + + if (!(mclassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpFail = genJumpNotSmallInteger(Arg0Reg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, ReceiverResultReg); + jumpTrue = genConditionalBranchoperand(jumpOpcode, 0); + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpTrue, /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, trueObject(), ReceiverResultReg)); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpFail, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#isUnannotatableConstant: */ +static NoDbgRegParms sqInt +isUnannotatableConstant(SimStackEntry *simStackEntry) +{ + return (((simStackEntry->type)) == SSConstant) + && ((isImmediate((simStackEntry->constant))) + || (!(/* shouldAnnotateObjectReference: */ + (isNonImmediate((simStackEntry->constant))) + && ((oopisGreaterThan((simStackEntry->constant), classTableRootObj())) + || (oopisLessThan((simStackEntry->constant), nilObject())))))); +} + + /* CogObjectRepresentationFor64BitSpur>>#classForInlineCacheTag: */ +static NoDbgRegParms sqInt +classForInlineCacheTag(sqInt classIndex) +{ + return classOrNilAtIndex(classIndex); +} + + /* CogObjectRepresentationFor64BitSpur>>#genAddSmallIntegerTagsTo: */ +static NoDbgRegParms sqInt +genAddSmallIntegerTagsTo(sqInt aRegister) +{ + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, aRegister); + return 0; +} + + +/* Set the SmallInteger tag bits when the tag bits may be filled with + garbage. + */ + + /* CogObjectRepresentationFor64BitSpur>>#genClearAndSetSmallIntegerTagsIn: */ +static NoDbgRegParms sqInt +genClearAndSetSmallIntegerTagsIn(sqInt scratchReg) +{ + sqInt quickConstant; + + quickConstant = -1 - (tagMask()); + + /* begin AndCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, scratchReg); + + /* #OrCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(OrCqR, 1, scratchReg); + return 0; +} + + +/* Convert the in-SmallFloat64-range floating point value in integer register + into a tagged SmallFloat64 oop. + c.f. Spur64BitMemoryManager>>smallFloatObjectOf: */ + + /* CogObjectRepresentationFor64BitSpur>>#genConvertBitsToSmallFloatIn:scratch: */ +static NoDbgRegParms sqInt +genConvertBitsToSmallFloatInscratch(sqInt reg, sqInt scratch) +{ + AbstractInstruction *jumpZero; + sqInt quickConstant; + + /* RotateLeftCq:R: */ + genoperandoperand(RotateLeftCqR, 1, reg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 1, reg); + jumpZero = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + quickConstant = ((sqInt)((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + + /* begin SubCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, quickConstant, reg); + jmpTarget(jumpZero, genoperandoperand(LogicalShiftLeftCqR, numTagBits(), reg)); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, smallFloatTag(), reg); + return 0; +} + + +/* Convert the Character in reg to a SmallInteger, assuming + the Character's value is a valid character. */ + + /* CogObjectRepresentationFor64BitSpur>>#genConvertCharacterToSmallIntegerInReg: */ +static NoDbgRegParms void +genConvertCharacterToSmallIntegerInReg(sqInt reg) +{ + sqInt quickConstant; + + quickConstant = (characterTag()) - (smallIntegerTag()); + + /* begin SubCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, quickConstant, reg); +} + + /* CogObjectRepresentationFor64BitSpur>>#genConvertIntegerInReg:toSmallIntegerInReg: */ +static NoDbgRegParms sqInt +genConvertIntegerInRegtoSmallIntegerInReg(sqInt srcReg, sqInt destReg) +{ + gLogicalShiftLeftCqRR(numTagBits(), srcReg, destReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, destReg); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genConvertIntegerToSmallIntegerInReg: */ +static NoDbgRegParms sqInt +genConvertIntegerToSmallIntegerInReg(sqInt reg) +{ + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, numTagBits(), reg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, reg); + return 0; +} + + +/* Convert the SmallFloat in reg to its identityHash as a SmallInteger. + Rotate the sign bit from bit 3 (zero-relative) to the sign bit. + c.f. Spur64BitMemoryManager>>rotatedFloatBitsOf: */ + + /* CogObjectRepresentationFor64BitSpur>>#genConvertSmallFloatToSmallFloatHashAsIntegerInReg:scratch: */ +static NoDbgRegParms sqInt +genConvertSmallFloatToSmallFloatHashAsIntegerInRegscratch(sqInt reg, sqInt scratch) +{ + sqInt quickConstant; + + assert(((((usqInt)((smallFloatTag()))) >> 1) - (smallIntegerTag())) == (smallIntegerTag())); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, 1, reg); + gAndCqRR(1U << ((numTagBits()) - 1), reg, scratch); + + /* SubR:R: */ + genoperandoperand(SubRR, scratch, reg); + quickConstant = (((usqInt)((smallFloatTag()))) >> 1) - (smallIntegerTag()); + + /* begin SubCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, quickConstant, reg); + quickConstant = 0x3F - ((numTagBits()) - 1); + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, quickConstant, scratch); + + /* OrR:R: */ + genoperandoperand(OrRR, scratch, reg); + return 0; +} + + +/* Convert the SmallInteger in reg to a Character, assuming + the SmallInteger's value is a valid character. */ + + /* CogObjectRepresentationFor64BitSpur>>#genConvertSmallIntegerToCharacterInReg: */ +static NoDbgRegParms void +genConvertSmallIntegerToCharacterInReg(sqInt reg) +{ + sqInt quickConstant; + + quickConstant = (characterTag()) - (smallIntegerTag()); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, quickConstant, reg); +} + + /* CogObjectRepresentationFor64BitSpur>>#genConvertSmallIntegerToIntegerInReg: */ +static NoDbgRegParms sqInt +genConvertSmallIntegerToIntegerInReg(sqInt reg) +{ + /* ArithmeticShiftRightCq:R: */ + genoperandoperand(ArithmeticShiftRightCqR, numTagBits(), reg); + return 0; +} + + +/* The arguments are in an array in Arg1Reg. Its size is in sizeReg. + Load Arg0Reg and Arg1Reg with the first two slots, as appropriate. + Since objects always have at least one slot it is safe to load arg0 + without checking. + But the array could be at the end of memory so we must check that it has + two slots before it is safe to access the second slot. */ + + /* CogObjectRepresentationFor64BitSpur>>#genFetchRegArgsForPerformWithArguments: */ +static NoDbgRegParms sqInt +genFetchRegArgsForPerformWithArguments(sqInt sizeReg) +{ + AbstractInstruction *skip; + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BaseHeaderSize, Arg1Reg, Arg0Reg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 2, sizeReg); + skip = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BaseHeaderSize + BytesPerWord, Arg1Reg, Arg1Reg); + jmpTarget(skip, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genFloatArithmetic:preOpCheck:boxed: */ +static NoDbgRegParms sqInt +genFloatArithmeticpreOpCheckboxed(sqInt arithmeticOperator, AbstractInstruction *(*preOpCheckOrNil)(int rcvrReg, int argReg), sqInt rcvrBoxed) +{ + AbstractInstruction *doOp; + AbstractInstruction *jumpFailCheck; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpNotBoxedFloat; + AbstractInstruction *jumpNotSmallFloat; + AbstractInstruction *jumpNotSmallInteger; + + jumpFailCheck = ((AbstractInstruction *) 0); + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + if (rcvrBoxed) { + genGetDoubleValueOfinto(ReceiverResultReg, DPFPReg0); + } + else { + genGetSmallFloatValueOfscratchinto(ReceiverResultReg, TempReg, DPFPReg0); + } + jumpNotSmallFloat = genJumpNotSmallFloat(Arg0Reg); + genGetSmallFloatValueOfscratchinto(Arg0Reg, TempReg, DPFPReg1); + doOp = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + if (preOpCheckOrNil) { + jumpFailCheck = preOpCheckOrNil(DPFPReg0, DPFPReg1); + } + genoperandoperand(arithmeticOperator, DPFPReg1, DPFPReg0); + genInstantiateFloatValueintoscratchRegscratchRegmayFail(DPFPReg0, ReceiverResultReg, ClassReg, TempReg, 0); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSmallFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpNotSmallInteger = genJumpNotSmallInteger(Arg0Reg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, Arg1Reg); + genConvertSmallIntegerToIntegerInReg(Arg1Reg); + + /* ConvertR:Rd: */ + genoperandoperand(ConvertRRd, Arg1Reg, DPFPReg1); + + /* Jump: */ + genoperand(Jump, ((sqInt)doOp)); + jmpTarget(jumpNotSmallInteger, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpImmediate = genJumpImmediate(Arg0Reg); + genGetCompactClassIndexNonImmOfinto(Arg0Reg, SendNumArgsReg); + genCmpClassFloatCompactIndexR(SendNumArgsReg); + jumpNotBoxedFloat = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genGetDoubleValueOfinto(Arg0Reg, DPFPReg1); + + /* Jump: */ + genoperand(Jump, ((sqInt)doOp)); + jmpTarget(jumpImmediate, jmpTarget(jumpNotBoxedFloat, jmpTarget(jumpNotSmallInteger, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + if (preOpCheckOrNil) { + jmpTarget(jumpFailCheck, ((AbstractInstruction *) (((jumpNotSmallInteger->operands))[0]))); + } + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genFloatComparison:orIntegerComparison:invert:boxed: */ +static NoDbgRegParms sqInt +genFloatComparisonorIntegerComparisoninvertboxed(AbstractInstruction *(*jumpFPOpcodeGenerator)(void *), sqInt jumpOpcode, sqInt invertComparison, sqInt rcvrBoxed) +{ + AbstractInstruction *compareFloat; + AbstractInstruction *jumpAmbiguous; + AbstractInstruction *jumpCond; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpNotBoxedFloat; + AbstractInstruction *jumpNotSmallFloat; + AbstractInstruction *jumpNotSmallInteger; + AbstractInstruction *jumpTrue; + AbstractInstruction *returnTrue; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + if (rcvrBoxed) { + genGetDoubleValueOfinto(ReceiverResultReg, DPFPReg0); + } + else { + genGetSmallFloatValueOfscratchinto(ReceiverResultReg, TempReg, DPFPReg0); + } + jumpNotSmallFloat = genJumpNotSmallFloat(Arg0Reg); + genGetSmallFloatValueOfscratchinto(Arg0Reg, TempReg, DPFPReg1); + compareFloat = (invertComparison + ? genoperandoperand(CmpRdRd, DPFPReg0, DPFPReg1) + : genoperandoperand(CmpRdRd, DPFPReg1, DPFPReg0)); + + /* May need to invert for NaNs */ + + /* FP jumps are a little weird */ + jumpCond = jumpFPOpcodeGenerator(0); + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpCond, (returnTrue = /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, trueObject(), ReceiverResultReg))); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSmallFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpNotSmallInteger = genJumpNotSmallInteger(Arg0Reg); + + /* Test for ambiguity, that is when floatRcvr == (double) intArg */ + genConvertSmallIntegerToIntegerInReg(Arg0Reg); + + /* ConvertR:Rd: */ + genoperandoperand(ConvertRRd, Arg0Reg, DPFPReg1); + + /* CmpRd:Rd: */ + genoperandoperand(CmpRdRd, DPFPReg0, DPFPReg1); + + /* If floatRcvr == (double) intArg then use compareInt(intArg,(int64) floatRcvr) + else use compareFloat(floatRcvr,(double) intArg) */ + jumpAmbiguous = gJumpFPEqual(0); + + /* Case of non ambiguity, use compareFloat(floatRcvr,(double) intArg) */ + + /* Jump: */ + genoperand(Jump, ((sqInt)compareFloat)); + + /* Case of ambiguity, use compareInt((int64) floatRcvr, intArg) */ + jmpTarget(jumpAmbiguous, genoperandoperand(ConvertRdR, DPFPReg0, ReceiverResultReg)); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, ReceiverResultReg); + jumpTrue = genConditionalBranchoperand(jumpOpcode, 0); + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpTrue, returnTrue); + + /* not a Small Float, nor Small Integer, check for Boxed Float argument */ + jmpTarget(jumpNotSmallInteger, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpImmediate = genJumpImmediate(Arg0Reg); + genGetCompactClassIndexNonImmOfinto(Arg0Reg, SendNumArgsReg); + genCmpClassFloatCompactIndexR(SendNumArgsReg); + jumpNotBoxedFloat = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genGetDoubleValueOfinto(Arg0Reg, DPFPReg1); + + /* Jump: */ + genoperand(Jump, ((sqInt)compareFloat)); + jmpTarget(jumpImmediate, jmpTarget(jumpNotBoxedFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + +/* Fetch the instance's identity hash into destReg, encoded as a + SmallInteger. + */ + + /* CogObjectRepresentationFor64BitSpur>>#genGetHashFieldNonImmOf:asSmallIntegerInto: */ +static NoDbgRegParms sqInt +genGetHashFieldNonImmOfasSmallIntegerInto(sqInt instReg, sqInt destReg) +{ + sqInt quickConstant; + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, instReg, destReg); + quickConstant = (identityHashFullWordShift()) - (numTagBits()); + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, quickConstant, destReg); + quickConstant = ((sqInt)((usqInt)((identityHashHalfWordMask())) << (numTagBits()))); + + /* begin AndCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, destReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, smallIntegerTag(), destReg); + + /* Shift and mask the field leaving room for the SmallInteger tag. */ + return 0; +} + + +/* Fetch the instance's identity hash into destReg, unencoded. */ + + /* CogObjectRepresentationFor64BitSpur>>#genGetHashFieldNonImmOf:into: */ +static NoDbgRegParms sqInt +genGetHashFieldNonImmOfinto(sqInt instReg, sqInt destReg) +{ + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, instReg, destReg); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, 32, destReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, identityHashHalfWordMask(), destReg); + return 0; +} + + +/* Extract the inline cache tag for the object in sourceReg into destReg. The + inline cache tag for a given object is the value loaded in inline caches + to distinguish + objects of different classes. In Spur this is either the tags for + immediates, or + the receiver's classIndex. Answer the label for the start of the sequence. */ + + /* CogObjectRepresentationFor64BitSpur>>#genGetInlineCacheClassTagFrom:into:forEntry: */ +static NoDbgRegParms AbstractInstruction * +genGetInlineCacheClassTagFromintoforEntry(sqInt sourceReg, sqInt destReg, sqInt forEntry) +{ + AbstractInstruction *entryLabel; + AbstractInstruction *jumpImm; + + if (forEntry) { + /* AlignmentNops: */ + genoperand(AlignmentNops, BytesPerWord); + } + entryLabel = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + gAndCqRR(tagMask(), sourceReg, destReg); + jumpImm = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Get least significant half of header word in destReg */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, sourceReg, destReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, classIndexMask(), destReg); + jmpTarget(jumpImm, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return entryLabel; +} + + +/* Get the size in byte-sized slots of the object in srcReg into destReg. + srcReg may equal destReg. + destReg <- numSlots << self shiftForWord - (fmt bitAnd: 7). + Assumes the object in srcReg has a byte format, i.e. 16 to 23 or 24 to 31 */ + + /* CogObjectRepresentationFor64BitSpur>>#genGetNumBytesOf:into: */ +static NoDbgRegParms sqInt +genGetNumBytesOfinto(sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *jmp; + + genGetRawSlotSizeOfNonImminto(srcReg, destReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, numSlotsMask(), destReg); + jmp = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + genGetOverflowSlotsOfinto(srcReg, destReg); + jmpTarget(jmp, genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), destReg)); + genGetBitsofFormatByteOfinto(7, srcReg, TempReg); + + /* Now: fmt bitAnd: 3 in TempReg */ + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, destReg); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genGetOverflowSlotsOf:into: */ +static NoDbgRegParms sqInt +genGetOverflowSlotsOfinto(sqInt srcReg, sqInt destReg) +{ + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, -BaseHeaderSize, srcReg, destReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, 8, destReg); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, 8, destReg); + return 0; +} + + +/* Convert the SmallFloat oop in ooppReg into the corresponding float value + in dpReg. + c.f. Spur64BitMemoryManager>>smallFloatBitsOf: */ + + /* CogObjectRepresentationFor64BitSpur>>#genGetSmallFloatValueOf:scratch:into: */ +static NoDbgRegParms sqInt +genGetSmallFloatValueOfscratchinto(sqInt oopReg, sqInt scratch, sqInt dpReg) +{ + AbstractInstruction *jumpSFZero; + sqInt quickConstant; + + /* MoveR:R: */ + genoperandoperand(MoveRR, oopReg, scratch); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, numTagBits(), scratch); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 1, scratch); + jumpSFZero = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + quickConstant = ((sqInt)((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, quickConstant, scratch); + jmpTarget(jumpSFZero, genoperandoperand(RotateRightCqR, 1, scratch)); + + /* begin MoveR:Rd: */ + assert(BytesPerWord == 8); + genoperandoperand(MoveRRd, scratch, dpReg); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genInstantiate64BitPositiveIntegerValue:into:scratchReg:mayFail: */ +static NoDbgRegParms AbstractInstruction * +genInstantiate64BitPositiveIntegerValueintoscratchRegmayFail(sqInt valueReg, sqInt resultReg, sqInt scratch, sqInt mayFail) +{ + AbstractInstruction *abstractInstruction; + sqInt allocSize; + AbstractInstruction *jumpFail; + AbstractInstruction *jumpOK; + usqLong newLPIHeader; + sqInt quickConstant; + + jumpFail = ((AbstractInstruction *) 0); + allocSize = BaseHeaderSize + BytesPerWord; + newLPIHeader = headerForSlotsformatclassIndex(1, firstByteFormat(), ClassLargePositiveIntegerCompactIndex); + if (mayFail) { + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), scratch); + quickConstant = (getScavengeThreshold()) - allocSize; + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, scratch); + jumpFail = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, scratch, resultReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, allocSize, scratch); + } + else { + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), resultReg); + quickConstant = (getScavengeThreshold()) - allocSize; + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, resultReg); + jumpOK = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceScheduleScavengeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpTarget(jumpOK, gAddCqRR(allocSize, resultReg, scratch)); + } + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, scratch, freeStartAddress()); + + /* begin genStoreHeader:intoNewInstance:using: */ + /* MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, newLPIHeader, scratch); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, scratch, 0, resultReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, valueReg, BaseHeaderSize, resultReg); + return (mayFail + ? jumpFail + : 0); +} + + /* CogObjectRepresentationFor64BitSpur>>#genInstantiate64BitSignedIntegerValue:into:scratchReg:mayFail: */ +static NoDbgRegParms AbstractInstruction * +genInstantiate64BitSignedIntegerValueintoscratchRegmayFail(sqInt valueReg, sqInt resultReg, sqInt scratch, sqInt mayFail) +{ + AbstractInstruction *abstractInstruction; + sqInt allocSize; + AbstractInstruction *jumpFail; + AbstractInstruction *jumpNeg; + AbstractInstruction *jumpOK; + usqLong newLNIHeader; + sqInt quickConstant; + + jumpFail = ((AbstractInstruction *) 0); + allocSize = BaseHeaderSize + BytesPerWord; + newLNIHeader = headerForSlotsformatclassIndex(1, firstByteFormat(), ClassLargeNegativeIntegerCompactIndex); + if (mayFail) { + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), scratch); + quickConstant = (getScavengeThreshold()) - allocSize; + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, scratch); + jumpFail = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, scratch, resultReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, allocSize, scratch); + } + else { + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), resultReg); + quickConstant = (getScavengeThreshold()) - allocSize; + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, resultReg); + jumpOK = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceScheduleScavengeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpTarget(jumpOK, gAddCqRR(allocSize, resultReg, scratch)); + } + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, scratch, freeStartAddress()); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, newLNIHeader, scratch); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, valueReg); + jumpNeg = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* We can avoid duplicating the large constant and a jump choosing between + the alternatives by incrementing the single constant if positive. The assert + checks that the hack works. Compact code is to be preferred because it is + an uncommon case; usually the value will fit in a SmallInteger. */ + assert((headerForSlotsformatclassIndex(0, 0, 1)) == 1); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ClassLargePositiveIntegerCompactIndex - ClassLargeNegativeIntegerCompactIndex, scratch); + jmpTarget(jumpNeg, genoperandoperandoperand(MoveRMwr, scratch, 0, resultReg)); + + /* The old less compact code was + cogit CmpCq: 0 R: valueReg. + jumpNeg := cogit JumpLess: 0. + cogit MoveCq: newLPIHeader R: scratch1. + jumpJoin := cogit Jump: 0. + jumpNeg jmpTarget: + (cogit MoveCq: newLNIHeader R: scratch1). + jumpJoin jmpTarget: + (cogit MoveR: scratch1 Mw: 0 r: resultReg). */ + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, valueReg, BaseHeaderSize, resultReg); + return (mayFail + ? jumpFail + : 0); +} + + +/* Allocate a new float instance with the value in the double-precision + register dpreg. + Do *not* use scratch2, which exists only for compatibility with V3. + If the allocation succeeds assign it to resultReg. If mayFail is false + always allocate. + If mayFail is true and the allocation fails do *not* update resultReg. + If mayFail is true answer the jump to take on failure, otherwise answer + nil. */ +/* Override to answer a SmallFloat64 if possible. */ + + /* CogObjectRepresentationFor64BitSpur>>#genInstantiateFloatValue:into:scratchReg:scratchReg:mayFail: */ +static NoDbgRegParms AbstractInstruction * +genInstantiateFloatValueintoscratchRegscratchRegmayFail(sqInt dpreg, sqInt resultReg, sqInt scratch1, sqInt scratch2, sqInt mayFail) +{ + AbstractInstruction *abstractInstruction; + usqIntptr_t allocSize; + AbstractInstruction *jumpFail; + AbstractInstruction *jumpMerge; + AbstractInstruction *jumpNotSF; + AbstractInstruction *jumpOK; + usqLong newFloatHeader; + sqInt quickConstant; + + jumpFail = ((AbstractInstruction *) 0); + + /* begin MoveRd:R: */ + assert(BytesPerWord == 8); + genoperandoperand(MoveRdR, dpreg, resultReg); + jumpNotSF = genJumpNotSmallFloatValueBitsscratch(resultReg, scratch1); + genConvertBitsToSmallFloatInscratch(resultReg, scratch1); + jumpMerge = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpNotSF, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + allocSize = BaseHeaderSize + (sizeof(double)); + if (mayFail) { + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), scratch1); + quickConstant = (getScavengeThreshold()) - allocSize; + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, scratch1); + jumpFail = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, scratch1, resultReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, allocSize, scratch1); + } + else { + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), resultReg); + quickConstant = (getScavengeThreshold()) - allocSize; + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, resultReg); + jumpOK = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceScheduleScavengeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpTarget(jumpOK, gAddCqRR(allocSize, resultReg, scratch1)); + } + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, scratch1, freeStartAddress()); + newFloatHeader = headerForSlotsformatclassIndex((sizeof(double)) / BytesPerWord, firstLongFormat(), ClassFloatCompactIndex); + + /* begin genStoreHeader:intoNewInstance:using: */ + /* MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, newFloatHeader, scratch1); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, scratch1, 0, resultReg); + + /* #MoveRd:M64:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRdM64r, dpreg, BaseHeaderSize, resultReg); + jumpFail = (mayFail + ? jumpFail + : ((AbstractInstruction *) null)); + jmpTarget(jumpMerge, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return jumpFail; +} + + +/* Generate a test for aRegister containing an integer value in the + SmallInteger range, and a jump if so, answering the jump. + c.f. Spur64BitMemoryManager>>isIntegerValue: */ + + /* CogObjectRepresentationFor64BitSpur>>#genJumpIsSmallIntegerValue:scratch: */ +static NoDbgRegParms AbstractInstruction * +genJumpIsSmallIntegerValuescratch(sqInt aRegister, sqInt scratchReg) +{ + sqInt quickConstant; + + gArithmeticShiftRightCqRR(0x3F - (numTagBits()), aRegister, scratchReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, scratchReg); + quickConstant = (1U << ((numTagBits()) + 1)) - 1; + + /* begin AndCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, scratchReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 1, scratchReg); + return genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); +} + + +/* Generate a compare and branch to test if aRegister contains other than a + Character. + */ + + /* CogObjectRepresentationFor64BitSpur>>#genJumpNotCharacter: */ +static NoDbgRegParms AbstractInstruction * +genJumpNotCharacter(sqInt reg) +{ + /* #TstCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, characterTag(), reg); + return genConditionalBranchoperand(JumpZero, ((sqInt)0)); +} + + +/* Generate a test to check that the integer register contains a floating + point value within the SmallFloat64 range, + and answer the jump. c.f. Spur64BitMemoryManager>>isSmallFloatValue: */ + + /* CogObjectRepresentationFor64BitSpur>>#genJumpNotSmallFloatValueBits:scratch: */ +static NoDbgRegParms AbstractInstruction * +genJumpNotSmallFloatValueBitsscratch(sqInt reg, sqInt exponent) +{ + AbstractInstruction *jumpFail; + AbstractInstruction *jumpMaxExponent; + AbstractInstruction *jumpMinExponent; + AbstractInstruction *jumpTest; + AbstractInstruction *jumpZeroMantissa; + sqInt quickConstant; + + gLogicalShiftLeftCqRR(1, reg, exponent); + quickConstant = (smallFloatMantissaBits()) + 1; + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, quickConstant, exponent); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, smallFloatExponentOffset(), exponent); + jumpMinExponent = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + quickConstant = 0xFF + (smallFloatExponentOffset()); + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, exponent); + jumpMaxExponent = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + jumpFail = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpMinExponent, gTstCqR((1ULL << (smallFloatMantissaBits())) - 1, reg)); + jumpZeroMantissa = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, smallFloatExponentOffset(), exponent); + jumpTest = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpZeroMantissa, genoperandoperand(CmpCqR, 0, exponent)); + jmpTarget(jumpTest, genConditionalBranchoperand(JumpNonZero, ((sqInt)jumpFail))); + jmpTarget(jumpMaxExponent, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return jumpFail; +} + + +/* Generate a compare and branch to test if aRegister contains other than a + SmallFloat. Answer the jump. */ + + /* CogObjectRepresentationFor64BitSpur>>#genJumpNotSmallFloat: */ +static NoDbgRegParms AbstractInstruction * +genJumpNotSmallFloat(sqInt reg) +{ + /* #TstCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, smallFloatTag(), reg); + return genConditionalBranchoperand(JumpZero, ((sqInt)0)); +} + + +/* Generate a test for aRegister containing an integer value outside the + SmallInteger range, and a jump if so, answering the jump. + c.f. Spur64BitMemoryManager>>isIntegerValue: */ + + /* CogObjectRepresentationFor64BitSpur>>#genJumpNotSmallIntegerValue:scratch: */ +static NoDbgRegParms AbstractInstruction * +genJumpNotSmallIntegerValuescratch(sqInt aRegister, sqInt scratchReg) +{ + sqInt quickConstant; + + gArithmeticShiftRightCqRR(0x3F - (numTagBits()), aRegister, scratchReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, scratchReg); + quickConstant = (1U << ((numTagBits()) + 1)) - 1; + + /* begin AndCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, scratchReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 1, scratchReg); + return genConditionalBranchoperand(JumpGreater, ((sqInt)0)); +} + + +/* Generate a compare and branch to test if aRegister contains other than a + SmallInteger. + */ + + /* CogObjectRepresentationFor64BitSpur>>#genJumpNotSmallInteger: */ +static NoDbgRegParms AbstractInstruction * +genJumpNotSmallInteger(sqInt reg) +{ + /* #TstCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, smallIntegerTag(), reg); + return genConditionalBranchoperand(JumpZero, ((sqInt)0)); +} + + +/* Generate a compare and branch to test if aRegister contains a + SmallInteger. Answer the jump, or UnimplementedOperation if this cannot be + done with + a single register. */ + + /* CogObjectRepresentationFor64BitSpur>>#genJumpSmallInteger: */ +static NoDbgRegParms AbstractInstruction * +genJumpSmallInteger(sqInt aRegister) +{ + /* #TstCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, smallIntegerTag(), aRegister); + return genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +} + + +/* Generate the code for primitives 61 & 165, at:put:/basicAt:put: & + integerAt:put:. If signedVersion is true + then generate signed accesses to the bits classes (a la 164 & 165). If + signedVersion is false, + generate unsigned accesses (a la 60, 61, 63 & 64). */ +/* c.f. StackInterpreter>>stSizeOf: SpurMemoryManager>>lengthOf:format: + fixedFieldsOf:format:length: + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveAtPutSigned: */ +static NoDbgRegParms sqInt +genPrimitiveAtPutSigned(sqInt signedVersion) +{ + sqInt formatReg; + AbstractInstruction *jump64BitArgIsImmediate; + AbstractInstruction *jump64BitsOutOfBounds; + AbstractInstruction *jumpArrayOutOfBounds; + AbstractInstruction *jumpBadIndex; + AbstractInstruction *jumpBytesOutOfBounds; + AbstractInstruction *jumpBytesOutOfRange; + AbstractInstruction *jumpDoubleWordsOutOfRange; + AbstractInstruction *jumpFixedFieldsOutOfBounds; + AbstractInstruction *jumpHasFixedFields; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpImmutable; + AbstractInstruction *jumpIsBytes; + AbstractInstruction *jumpIsCompiledMethod; + AbstractInstruction *jumpIsContext; + AbstractInstruction *jumpIsShorts; + AbstractInstruction *jumpIsWords; + AbstractInstruction *jumpNegative; + AbstractInstruction *jumpNonSmallIntegerValue; + AbstractInstruction *jumpNot64BitIndexable; + AbstractInstruction *jumpNot8ByteInteger; + AbstractInstruction *jumpNotIndexableBits; + AbstractInstruction *jumpNotIndexablePointers; + AbstractInstruction *jumpNotPointers; + AbstractInstruction *jumpShortsOutOfBounds; + AbstractInstruction *jumpShortsOutOfRange; + AbstractInstruction *jumpWordsOutOfBounds; + AbstractInstruction *jumpWordsOutOfRange; + AbstractInstruction *methodInBounds; + sqInt nSlotsOrBytesReg; + sqInt quickConstant; + AbstractInstruction *rejoin; + + jumpDoubleWordsOutOfRange = ((AbstractInstruction *) 0); + jumpNegative = ((AbstractInstruction *) 0); + nSlotsOrBytesReg = ClassReg; + + /* begin genLoadArgAtDepth:into: */ + assert(1 < (numRegArgs())); + assert(0 < (numRegArgs())); + jumpImmediate = genJumpImmediate(ReceiverResultReg); + jumpBadIndex = genJumpNotSmallInteger(Arg0Reg); + genConvertSmallIntegerToIntegerInReg(Arg0Reg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 1, Arg0Reg); +# if IMMUTABILITY + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), TempReg); + + /* begin genJumpBaseHeaderImmutable: */ + /* TstCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, immutableBitMask(), TempReg); + jumpImmutable = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +# else // IMMUTABILITY + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), NoReg); +# endif // IMMUTABILITY + + genGetNumSlotsOfinto(ReceiverResultReg, nSlotsOrBytesReg); + + /* dispatch on format in a combination of highest dynamic frequency order first and convenience. + 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, weakArrayFormat(), formatReg); + jumpNotPointers = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* optimistic store check; assume index in range (almost always is). */ + genStoreCheckReceiverRegvalueRegscratchReginFrame(ReceiverResultReg, Arg1Reg, TempReg, 0); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, arrayFormat(), formatReg); + jumpNotIndexablePointers = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + jumpHasFixedFields = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpArrayOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), Arg0Reg); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, Arg1Reg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpHasFixedFields, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genGetClassIndexOfNonImminto(ReceiverResultReg, formatReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ClassMethodContextCompactIndex, formatReg); + jumpIsContext = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* get # fixed fields in formatReg */ + genGetClassObjectOfClassIndexintoscratchReg(formatReg, Extra0Reg, TempReg); + genLoadSlotsourceRegdestReg(InstanceSpecificationIndex, Extra0Reg, formatReg); + genConvertSmallIntegerToIntegerInReg(formatReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, fixedFieldsOfClassFormatMask(), formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrBytesReg); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), formatReg); + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpFixedFieldsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* AddR:R: */ + genoperandoperand(AddRR, formatReg, Arg0Reg); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, Arg1Reg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotPointers, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpNonSmallIntegerValue = genJumpNotSmallInteger(Arg1Reg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), formatReg); + jumpIsBytes = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstShortFormat(), formatReg); + jumpIsShorts = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstLongFormat(), formatReg); + jumpIsWords = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, sixtyFourBitIndexableFormat(), formatReg); + jumpNotIndexableBits = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* fall through to 64-bit words */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, SendNumArgsReg); + genConvertSmallIntegerToIntegerInReg(SendNumArgsReg); + if (!signedVersion) { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, SendNumArgsReg); + jumpNegative = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + } + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + rejoin = genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jump64BitsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), Arg0Reg); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, SendNumArgsReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* Handle LargePositiveInteger (unsigned) or LargePositiveInteger and LargeNegativeInteger (signed) + for sixtyFourBitIndexableFormat */ + jmpTarget(jumpNonSmallIntegerValue, genoperandoperand(CmpCqR, sixtyFourBitIndexableFormat(), formatReg)); + jumpNot64BitIndexable = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + jump64BitArgIsImmediate = genJumpImmediate(Arg1Reg); + if (signedVersion) { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BaseHeaderSize, Arg1Reg, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, TempReg); + jumpDoubleWordsOutOfRange = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + } + + /* Test top bit of 64-bit word in large integer for range check. + Now check if the header is that of an 8 byte LargePositiveInteger */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, Arg1Reg, SendNumArgsReg); + quickConstant = headerForSlotsformatclassIndex(numSlotsMask(), formatMask(), classIndexMask()); + + /* begin AndCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, SendNumArgsReg); + quickConstant = headerForSlotsformatclassIndex(1, firstByteFormat(), ClassLargePositiveIntegerCompactIndex); + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, SendNumArgsReg); + jumpNot8ByteInteger = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BaseHeaderSize, Arg1Reg, SendNumArgsReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)rejoin)); + if (signedVersion) { + /* Now check if the header is that of an 8 byte LargeNegativeInteger */ + jmpTarget(jumpNot8ByteInteger, gCmpCqR(headerForSlotsformatclassIndex(1, firstByteFormat(), ClassLargeNegativeIntegerCompactIndex), SendNumArgsReg)); + jumpNot8ByteInteger = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BaseHeaderSize, Arg1Reg, TempReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, SendNumArgsReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, SendNumArgsReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)rejoin)); + } + if (signedVersion) { + jmpTarget(jumpIsWords, gArithmeticShiftRightCqRR(0x1F + (numTagBits()), Arg1Reg, TempReg)); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 1, TempReg); + } + else { + jmpTarget(jumpIsWords, genoperandoperand(CmpCqR, (((usqInt)0xFFFFFFFFU << 3) | 1), Arg1Reg)); + } + jumpWordsOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - 2, nSlotsOrBytesReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 4) - 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrBytesReg); + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpWordsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> ((shiftForWord()) - 1), Arg0Reg); + + /* MoveR:X32r:R: */ + genoperandoperandoperand(MoveRX32rR, TempReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + if (signedVersion) { + jmpTarget(jumpIsBytes, gArithmeticShiftRightCqRR(7 + (numTagBits()), Arg1Reg, TempReg)); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 1, TempReg); + } + else { + jmpTarget(jumpIsBytes, genoperandoperand(CmpCqR, (((usqInt)0xFF << 3) | 1), Arg1Reg)); + } + jumpBytesOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), nSlotsOrBytesReg); + gAndCqRR(BytesPerWord - 1, formatReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, nSlotsOrBytesReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpBytesOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), formatReg); + jumpIsCompiledMethod = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + methodInBounds = genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BaseHeaderSize, Arg0Reg); + + /* MoveR:Xbr:R: */ + genoperandoperandoperand(MoveRXbrR, TempReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + if (signedVersion) { + jmpTarget(jumpIsShorts, gArithmeticShiftRightCqRR(15 + (numTagBits()), Arg1Reg, TempReg)); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 1, TempReg); + } + else { + jmpTarget(jumpIsShorts, genoperandoperand(CmpCqR, (((usqInt)0xFFFF << 3) | 1), Arg1Reg)); + } + jumpShortsOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - 1, nSlotsOrBytesReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 2) - 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrBytesReg); + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpShortsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg0Reg, ReceiverResultReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg0Reg, ReceiverResultReg); + + /* #MoveR:M16:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRM16r, TempReg, BaseHeaderSize, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* Now check that the index is beyond the method's literals... */ + jmpTarget(jumpIsCompiledMethod, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + getLiteralCountOfplusOneinBytesintoscratch(ReceiverResultReg, 1, 1, nSlotsOrBytesReg, TempReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + + /* JumpBelow: */ + genConditionalBranchoperand(JumpBelow, ((sqInt)methodInBounds)); + jmpTarget(jumpNot8ByteInteger, jmpTarget(jump64BitArgIsImmediate, jmpTarget(jumpNot64BitIndexable, jmpTarget(jumpIsContext, jmpTarget(jumpNotIndexableBits, jmpTarget(jumpBytesOutOfRange, jmpTarget(jumpShortsOutOfRange, jmpTarget(jumpWordsOutOfRange, jmpTarget(jumpIsCompiledMethod, jmpTarget(jumpArrayOutOfBounds, jmpTarget(jumpBytesOutOfBounds, jmpTarget(jumpShortsOutOfBounds, jmpTarget(jumpWordsOutOfBounds, jmpTarget(jump64BitsOutOfBounds, jmpTarget(jumpNotIndexablePointers, jmpTarget(jumpFixedFieldsOutOfBounds, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))))))))))))))); + if (signedVersion) { + jmpTarget(jumpDoubleWordsOutOfRange, ((AbstractInstruction *) (((jumpIsContext->operands))[0]))); + } + else { + jmpTarget(jumpNegative, ((AbstractInstruction *) (((jumpIsContext->operands))[0]))); + } +# if IMMUTABILITY + jmpTarget(jumpImmutable, ((AbstractInstruction *) (((jumpIsContext->operands))[0]))); +# endif + + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, Arg0Reg); + genConvertIntegerToSmallIntegerInReg(Arg0Reg); + jmpTarget(jumpBadIndex, jmpTarget(jumpImmediate, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return 0; +} + + +/* Generate the code for primitives 60 & 164, at:/basicAt: & integerAt:. If + signedVersion is true + then generate signed accesses to the bits classes (a la 164 & 165). If + signedVersion is false, + generate unsigned accesses (a la 60, 61, 63 & 64). */ +/* c.f. StackInterpreter>>stSizeOf: SpurMemoryManager>>lengthOf:format: + fixedFieldsOf:format:length: + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveAtSigned: */ +static NoDbgRegParms sqInt +genPrimitiveAtSigned(sqInt signedVersion) +{ + AbstractInstruction *convertToIntAndReturn; + sqInt formatReg; + AbstractInstruction *jumpArrayOutOfBounds; + AbstractInstruction *jumpBadIndex; + AbstractInstruction *jumpBytesOutOfBounds; + AbstractInstruction *jumpFixedFieldsOutOfBounds; + AbstractInstruction *jumpHasFixedFields; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpIsArray; + AbstractInstruction *jumpIsBytes; + AbstractInstruction *jumpIsContext; + AbstractInstruction *jumpIsLongs; + AbstractInstruction *jumpIsMethod; + AbstractInstruction *jumpIsShorts; + AbstractInstruction *jumpIsWords; + AbstractInstruction *jumpLongsOutOfBounds; + AbstractInstruction *jumpNotIndexable; + AbstractInstruction *jumpNotSmallInteger; + AbstractInstruction *jumpShortsOutOfBounds; + AbstractInstruction *jumpWordsOutOfBounds; + AbstractInstruction *methodInBounds; + sqInt nSlotsOrElementsReg; + sqInt quickConstant; + + nSlotsOrElementsReg = ClassReg; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpImmediate = genJumpImmediate(ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, Arg1Reg); + jumpBadIndex = genJumpNotSmallInteger(Arg0Reg); + genConvertSmallIntegerToIntegerInReg(Arg1Reg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 1, Arg1Reg); + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), TempReg); + genGetNumSlotsOfinto(ReceiverResultReg, nSlotsOrElementsReg); + + /* dispatch on format in a combination of highest dynamic frequency order first and convenience. + 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), formatReg); + jumpIsBytes = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, arrayFormat(), formatReg); + jumpIsArray = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + jumpNotIndexable = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, weakArrayFormat(), formatReg); + jumpHasFixedFields = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstShortFormat(), formatReg); + jumpIsShorts = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstLongFormat(), formatReg); + jumpIsWords = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, sixtyFourBitIndexableFormat(), formatReg); + jumpIsLongs = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + jmpTarget(jumpNotIndexable, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpNotIndexable = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpIsArray, + + (/* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)), + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg))); + jumpArrayOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), Arg1Reg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, Arg1Reg, ReceiverResultReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsBytes, genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), nSlotsOrElementsReg)); + gAndCqRR(7, formatReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, nSlotsOrElementsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpBytesOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), formatReg); + jumpIsMethod = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + methodInBounds = genoperandoperand(AddCqR, BaseHeaderSize, Arg1Reg); + if (signedVersion) { + /* MoveXbr:R:R: */ + genoperandoperandoperand(MoveXbrRR, Arg1Reg, ReceiverResultReg, ReceiverResultReg); + } + else { + /* formatReg already contains a value <= 16r1f, so no need to zero it */ + + /* MoveXbr:R:R: */ + genoperandoperandoperand(MoveXbrRR, Arg1Reg, ReceiverResultReg, formatReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, formatReg, ReceiverResultReg); + } + if (signedVersion) { + /* SignExtend8R:R: */ + genoperandoperand(SignExtend8RR, ReceiverResultReg, ReceiverResultReg); + } + convertToIntAndReturn = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + genConvertIntegerToSmallIntegerInReg(ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsShorts, gLogicalShiftLeftCqR((shiftForWord()) - 1, nSlotsOrElementsReg)); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, 3, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrElementsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpShortsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg1Reg, ReceiverResultReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg1Reg, ReceiverResultReg); + + /* #MoveM16:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveM16rR, BaseHeaderSize, ReceiverResultReg, ReceiverResultReg); + if (signedVersion) { + /* SignExtend16R:R: */ + genoperandoperand(SignExtend16RR, ReceiverResultReg, ReceiverResultReg); + } + + /* Jump: */ + genoperand(Jump, ((sqInt)convertToIntAndReturn)); + jmpTarget(jumpIsWords, gLogicalShiftLeftCqR((shiftForWord()) - 2, nSlotsOrElementsReg)); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrElementsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpWordsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> ((shiftForWord()) - 1), Arg1Reg); + + /* MoveX32r:R:R: */ + genoperandoperandoperand(MoveX32rRR, Arg1Reg, ReceiverResultReg, TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, ReceiverResultReg); + if (signedVersion) { + /* SignExtend32R:R: */ + genoperandoperand(SignExtend32RR, ReceiverResultReg, ReceiverResultReg); + } + + /* Jump: */ + genoperand(Jump, ((sqInt)convertToIntAndReturn)); + jmpTarget(jumpHasFixedFields, genoperandoperand(AndCqR, classIndexMask(), TempReg)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, formatReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ClassMethodContextCompactIndex, TempReg); + jumpIsContext = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + genGetClassObjectOfClassIndexintoscratchReg(formatReg, Extra0Reg, TempReg); + genLoadSlotsourceRegdestReg(InstanceSpecificationIndex, Extra0Reg, formatReg); + genConvertSmallIntegerToIntegerInReg(formatReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, fixedFieldsOfClassFormatMask(), formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrElementsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpFixedFieldsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* index is (formatReg (fixed fields) + Arg1Reg (0-rel index)) * wordSize + baseHeaderSize */ + + /* AddR:R: */ + genoperandoperand(AddRR, formatReg, Arg1Reg); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), Arg1Reg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, Arg1Reg, ReceiverResultReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsLongs, + + (/* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)), + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg))); + jumpLongsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), Arg1Reg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, Arg1Reg, ReceiverResultReg, ClassReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, TempReg); + if (signedVersion) { + /* ArithmeticShiftRightCq:R: */ + genoperandoperand(ArithmeticShiftRightCqR, numSmallIntegerBits(), TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 1, TempReg); + jumpNotSmallInteger = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, ReceiverResultReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)convertToIntAndReturn)); + jmpTarget(jumpNotSmallInteger, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genInstantiate64BitSignedIntegerValueintoscratchRegmayFail(ClassReg, ReceiverResultReg, TempReg, 0); + } + else { + quickConstant = (numSmallIntegerBits()) - 1; + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, quickConstant, TempReg); + if (!(setsConditionCodesFor(lastOpcode(), JumpZero))) { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, TempReg); + } + jumpNotSmallInteger = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, ReceiverResultReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)convertToIntAndReturn)); + jmpTarget(jumpNotSmallInteger, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genInstantiate64BitPositiveIntegerValueintoscratchRegmayFail(ClassReg, ReceiverResultReg, TempReg, 0); + } + + /* c.f. Spur64BitMemoryManager>>#isIntegerValue: */ + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsMethod, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* Now check that the index is beyond the method's literals... */ + getLiteralCountOfplusOneinBytesintoscratch(ReceiverResultReg, 1, 1, nSlotsOrElementsReg, TempReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + + /* JumpBelow: */ + genConditionalBranchoperand(JumpBelow, ((sqInt)methodInBounds)); + jmpTarget(jumpLongsOutOfBounds, jmpTarget(jumpFixedFieldsOutOfBounds, jmpTarget(jumpArrayOutOfBounds, jmpTarget(jumpBytesOutOfBounds, jmpTarget(jumpShortsOutOfBounds, jmpTarget(jumpWordsOutOfBounds, jmpTarget(jumpNotIndexable, jmpTarget(jumpIsContext, jmpTarget(jumpBadIndex, jmpTarget(jumpImmediate, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))))))))); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveFloatEqual */ +static sqInt +genPrimitiveFloatEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPEqual, JumpZero, 0, 1) + : genPureFloatComparisoninvertboxed(gJumpFPEqual, 0, 1)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveFloatGreaterOrEqual */ +static sqInt +genPrimitiveFloatGreaterOrEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreaterOrEqual, JumpGreaterOrEqual, 0, 1) + : genPureFloatComparisoninvertboxed(gJumpFPGreaterOrEqual, 0, 1)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveFloatGreaterThan */ +static sqInt +genPrimitiveFloatGreaterThan(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreater, JumpGreater, 0, 1) + : genPureFloatComparisoninvertboxed(gJumpFPGreater, 0, 1)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveFloatLessOrEqual */ +static sqInt +genPrimitiveFloatLessOrEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreaterOrEqual, JumpLessOrEqual, 1, 1) + : genPureFloatComparisoninvertboxed(gJumpFPGreaterOrEqual, 1, 1)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveFloatLessThan */ +static sqInt +genPrimitiveFloatLessThan(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreater, JumpLess, 1, 1) + : genPureFloatComparisoninvertboxed(gJumpFPGreater, 1, 1)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveFloatNotEqual */ +static sqInt +genPrimitiveFloatNotEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPNotEqual, JumpNonZero, 0, 1) + : genPureFloatComparisoninvertboxed(gJumpFPNotEqual, 0, 1)); +} + + +/* Arguably we should fail for immediates, but so far no one has complained, + so... + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveIdentityHash */ +static sqInt +genPrimitiveIdentityHash(void) +{ + AbstractInstruction *abstractInstruction; + int callerSavedRegsToBeSaved; + sqInt callTargetSqInt; + AbstractInstruction *cascade0; + AbstractInstruction *jumpImm; + AbstractInstruction *jumpNotCharacter; + AbstractInstruction *jumpNotSet; + AbstractInstruction *lastInst; + int mustSaveSPReg; + sqInt reg; + int registersToBePushed; + AbstractInstruction *ret; + AbstractInstruction *self_in_CogX64Compiler; + sqInt spareCalleeSavedReg; + + /* uses TstCqR */ + jumpImm = genJumpImmediate(ReceiverResultReg); + genGetHashFieldNonImmOfasSmallIntegerInto(ReceiverResultReg, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ConstZero, TempReg); + jumpNotSet = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, ReceiverResultReg); + ret = /* genPrimReturn */ + (methodOrBlockNumArgs <= (numRegArgs()) + ? genoperand(RetN, 0) + : genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord)); + jmpTarget(jumpImm, gAndCqRR(tagMask(), ReceiverResultReg, TempReg)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, characterTag(), TempReg); + jumpNotCharacter = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genConvertCharacterToSmallIntegerInReg(ReceiverResultReg); + ret = /* genPrimReturn */ + (methodOrBlockNumArgs <= (numRegArgs()) + ? genoperand(RetN, 0) + : genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord)); + jmpTarget(jumpNotCharacter, genoperandoperand(CmpCqR, smallFloatTag(), TempReg)); + + /* JumpNonZero: */ + genConditionalBranchoperand(JumpNonZero, ((sqInt)ret)); + genConvertSmallFloatToSmallFloatHashAsIntegerInRegscratch(ReceiverResultReg, TempReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSet, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* For non-behaviors we can call ceNewHash to assign a fresh hash. + For behaviors we must fail into the interpreter primitive to enter the receiver into the class table. */ + if (!(primitiveIndex == 75)) { + return 0; + } + + /* begin compileInvokeCENewHashOf */ + /* begin saveRegistersInMask:around: */ + spareCalleeSavedReg = 0; + callerSavedRegsToBeSaved = CallerSavedRegisterMask & ((1U << VarBaseReg)); + registersToBePushed = callerSavedRegsToBeSaved; + reg = 0; + while (registersToBePushed != 0) { + if (((registersToBePushed & 1) != 0)) { + /* PushR: */ + genoperand(PushR, reg); + } + reg += 1; + registersToBePushed = ((sqInt)(registersToBePushed)) >> 1; + } + + /* If the backEnd doesn't use the native SP register in generated code, and the non-native + SP is not callee-saved, then it too must be saved and restored around the call. */ + mustSaveSPReg = 0 /* (usesNativeSPReg not) */; + if (mustSaveSPReg) { + spareCalleeSavedReg = availableRegisterOrNoneIn(((ABICalleeSavedRegisterMask | ((1U << SPReg))) - ((1U << SPReg)))); + assert(!((spareCalleeSavedReg == NoReg))); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SPReg, spareCalleeSavedReg); + } + self_in_CogX64Compiler = ((AbstractInstruction *) (backEnd)); + + /* begin genMarshallNArgs:arg:arg:arg:arg: */ + + /* WIN64 ABI always reserves shadow space on the stack for callee to save up to 4 register parameters */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, CArg0Reg); + + /* begin CallAnyFixedRT: */ + if (is32BitSignedImmediate(backEnd, maximumDistanceFromCodeZone(((usqInt)ceNewHashOf)))) { + callTargetSqInt = ((usqIntptr_t)ceNewHashOf); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, callTargetSqInt); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* #CallFullRT: #CallFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(CallFull, ((usqIntptr_t)ceNewHashOf)); + } + cascade0 = backEnd; + + /* begin genRemoveNArgsFromStack: */ + lastInst = genWriteCResultIntoReg(cascade0, ReceiverResultReg); + if (mustSaveSPReg) { + lastInst = genoperandoperand(MoveRR, spareCalleeSavedReg, SPReg); + } + while (reg > 0) { + reg -= 1; + if (((callerSavedRegsToBeSaved & (1ULL << reg)) != 0)) { + lastInst = genoperand(PopR, reg); + } + } + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + return UnfailingPrimitive; +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveImmediateAsInteger */ +static sqInt +genPrimitiveImmediateAsInteger(void) +{ + AbstractInstruction *jumpNotCharacter; + AbstractInstruction *ret; + + gAndCqRR(tagMask(), ReceiverResultReg, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, characterTag(), TempReg); + jumpNotCharacter = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genConvertCharacterToSmallIntegerInReg(ReceiverResultReg); + ret = /* genPrimReturn */ + (methodOrBlockNumArgs <= (numRegArgs()) + ? genoperand(RetN, 0) + : genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord)); + jmpTarget(jumpNotCharacter, genoperandoperand(CmpCqR, smallFloatTag(), TempReg)); + + /* JumpNonZero: */ + genConditionalBranchoperand(JumpNonZero, ((sqInt)ret)); + genConvertSmallFloatToSmallFloatHashAsIntegerInRegscratch(ReceiverResultReg, TempReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + return UnfailingPrimitive; +} + + +/* Implement primitiveNew for convenient cases: + - the receiver has a hash + - the receiver is fixed size (excluding ephemerons to save instructions & + miniscule time) + - single word header/num slots < numSlotsMask + - the result fits in eden (actually below scavengeThreshold) + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveNew */ +static sqInt +genPrimitiveNew(void) +{ + sqInt byteSizeReg; + AbstractInstruction *fillLoop; + sqInt fillReg; + sqInt headerReg; + sqInt instSpecReg; + AbstractInstruction *jumpHasSlots; + AbstractInstruction *jumpNoSpace; + AbstractInstruction *jumpTooBig; + AbstractInstruction *jumpUnhashed; + AbstractInstruction *jumpVariableOrEphemeron; + AbstractInstruction *skip; + + if (methodOrBlockNumArgs) { + return UnimplementedPrimitive; + } + + /* header will contain classIndex/class's hash & format & numSlots/fixed size and finally fill value (nilObject). */ + headerReg = (fillReg = SendNumArgsReg); + + /* inst spec will hold class's instance specification, then byte size and finally end of new object. */ + instSpecReg = (byteSizeReg = ClassReg); + + /* get freeStart as early as possible so as not to wait later... */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), Arg1Reg); + + /* get class's hash & fail if 0 */ + genGetHashFieldNonImmOfinto(ReceiverResultReg, headerReg); + jumpUnhashed = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* get class's format inst var for both inst spec (format field) and num fixed fields */ + genLoadSlotsourceRegdestReg(InstanceSpecificationIndex, ReceiverResultReg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, instSpecReg); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, fixedFieldsFieldWidth(), TempReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, formatMask(), TempReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, fixedFieldsOfClassFormatMask(), instSpecReg); + + /* fail if not fixed or if ephemeron (rare beasts so save the cycles) */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, nonIndexablePointerFormat(), TempReg); + jumpVariableOrEphemeron = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, numSlotsMask(), instSpecReg); + jumpTooBig = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* Add format to classIndex/format in header; the add in numSlots */ + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, instSpecReg, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, numSlotsFullShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* compute byte size; remember 0-sized objects still need 1 slot. */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, byteSizeReg); + jumpHasSlots = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, BaseHeaderSize * 2, byteSizeReg); + skip = genoperand(Jump, ((sqInt)0)); + + /* round up to allocationUnit */ + jmpTarget(jumpHasSlots, genoperandoperand(AddCqR, BaseHeaderSize / BytesPerWord, byteSizeReg)); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), byteSizeReg); + jmpTarget(skip, genoperandoperand(AddRR, Arg1Reg, byteSizeReg)); + + /* check if allocation fits (freeSize + byteSize < scavengeThreshold); scavengeThreshold is constant. */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, getScavengeThreshold(), byteSizeReg); + jumpNoSpace = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* write back new freeStart; get result. byteSizeReg holds new freeStart, the limit of the object */ + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, byteSizeReg, freeStartAddress()); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* write header */ + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, headerReg, 0, Arg1Reg); + + /* now fill */ + + /* #LoadEffectiveAddressMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(LoadEffectiveAddressMwrR, BaseHeaderSize, ReceiverResultReg, Arg1Reg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, nilObject(), fillReg); + fillLoop = genoperandoperandoperand(MoveRMwr, fillReg, 0, Arg1Reg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 8, Arg1Reg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, byteSizeReg); + + /* JumpAbove: */ + genConditionalBranchoperand(JumpAbove, ((sqInt)fillLoop)); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpUnhashed, jmpTarget(jumpVariableOrEphemeron, jmpTarget(jumpTooBig, jmpTarget(jumpNoSpace, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))); + return 0; +} + + +/* Implement primitiveNewWithArg for convenient cases: + - the receiver has a hash + - the receiver is variable and not compiled method + - single word header/num slots < numSlotsMask + - the result fits in eden + See superclass method for dynamic frequencies of formats. + For the moment we implement only arrayFormat, firstByteFormat & + firstLongFormat + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveNewWithArg */ +static sqInt +genPrimitiveNewWithArg(void) +{ + sqInt byteSizeReg; + AbstractInstruction *fillLoop; + sqInt fillReg; + sqInt headerReg; + sqInt instSpecReg; + AbstractInstruction *jumpArrayFormat; + AbstractInstruction *jumpArrayTooBig; + AbstractInstruction *jumpByteFormat; + AbstractInstruction *jumpBytePrepDone; + AbstractInstruction *jumpByteTooBig; + AbstractInstruction *jumpFailCuzFixed; + AbstractInstruction *jumpHasSlots; + AbstractInstruction *jumpLongPrepDone; + AbstractInstruction *jumpLongTooBig; + AbstractInstruction *jumpNElementsNonInt; + AbstractInstruction *jumpNoSpace; + AbstractInstruction *jumpUnhashed; + sqInt maxSlots; + sqInt quickConstant; + AbstractInstruction *skip; + + if (methodOrBlockNumArgs != 1) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* header will contain classIndex/class's hash & format & numSlots/fixed size */ + headerReg = SendNumArgsReg; + + /* Assume there's an available scratch register on 64-bit machines. This holds the saved numFixedFields and then the value to fill with */ + fillReg = Extra0Reg; + assert(fillReg > 0); + + /* inst spec will hold class's instance specification and then byte size */ + instSpecReg = (byteSizeReg = ClassReg); + + /* The max slots we'll allocate here are those for a single header */ + maxSlots = (numSlotsMask()) - 1; + + /* get freeStart as early as possible so as not to wait later... */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), Arg1Reg); + + /* get class's hash & fail if 0 */ + genGetHashFieldNonImmOfinto(ReceiverResultReg, headerReg); + jumpUnhashed = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* get index and fail if not a +ve integer */ + jumpNElementsNonInt = genJumpNotSmallInteger(Arg0Reg); + + /* get class's format inst var for inst spec (format field) */ + genLoadSlotsourceRegdestReg(InstanceSpecificationIndex, ReceiverResultReg, instSpecReg); + quickConstant = (fixedFieldsFieldWidth()) + (numSmallIntegerTagBits()); + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, quickConstant, instSpecReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, formatMask(), instSpecReg); + + /* Add format to classIndex/format header now */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, instSpecReg, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* get integer value of num fields in fillReg now */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, fillReg); + genConvertSmallIntegerToIntegerInReg(fillReg); + + /* dispatch on format, failing if not variable or if compiled method */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, arrayFormat(), instSpecReg); + jumpArrayFormat = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), instSpecReg); + jumpByteFormat = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstLongFormat(), instSpecReg); + jumpFailCuzFixed = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, (((usqInt)(maxSlots * 2) << 3) | 1), Arg0Reg); + jumpLongTooBig = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* save num elements/slot size to instSpecReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, fillReg, instSpecReg); + + /* compute odd bits and add into headerReg; oddBits := 2 - nElements bitAnd: 1 */ + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, BytesPerWord / 4, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, instSpecReg, TempReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 4) - 1, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* round up num elements to numSlots in instSpecReg */ + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, (BytesPerWord / 4) - 1, instSpecReg); + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, (shiftForWord()) - 2, instSpecReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, fillReg); + + /* go allocate */ + jumpLongPrepDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpByteFormat, genoperandoperand(CmpCqR, (((usqInt)(maxSlots * BytesPerWord) << 3) | 1), Arg0Reg)); + jumpByteTooBig = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* save num elements to instSpecReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, fillReg, instSpecReg); + + /* compute odd bits and add into headerReg; oddBits := 8 - nElements bitAnd: 7 */ + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, BytesPerWord, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, instSpecReg, TempReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, BytesPerWord - 1, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* round up num elements to numSlots in instSpecReg */ + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BytesPerWord - 1, instSpecReg); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, shiftForWord(), instSpecReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, fillReg); + + /* go allocate */ + jumpBytePrepDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpArrayFormat, genoperandoperand(CmpCqR, (((usqInt)maxSlots << 3) | 1), Arg0Reg)); + jumpArrayTooBig = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* save num elements/slot size to instSpecReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, fillReg, instSpecReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, nilObject(), fillReg); + + /* fall through to allocate */ + jmpTarget(jumpBytePrepDone, jmpTarget(jumpLongPrepDone, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + + /* store numSlots to headerReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, instSpecReg, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, numSlotsFullShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* compute byte size; remember 0-sized objects still need 1 slot. */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, byteSizeReg); + jumpHasSlots = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, BaseHeaderSize * 2, byteSizeReg); + skip = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpHasSlots, genoperandoperand(AddCqR, BaseHeaderSize / BytesPerWord, byteSizeReg)); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), byteSizeReg); + jmpTarget(skip, genoperandoperand(AddRR, Arg1Reg, byteSizeReg)); + + /* check if allocation fits */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, getScavengeThreshold(), byteSizeReg); + jumpNoSpace = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* get result, increment freeStart and write it back. Arg1Reg holds new freeStart, the limit of the object */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, byteSizeReg, freeStartAddress()); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, headerReg, 0, ReceiverResultReg); + + /* now fill */ + + /* #LoadEffectiveAddressMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(LoadEffectiveAddressMwrR, BaseHeaderSize, ReceiverResultReg, Arg1Reg); + fillLoop = genoperandoperandoperand(MoveRMwr, fillReg, 0, Arg1Reg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 8, Arg1Reg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, byteSizeReg); + + /* JumpAbove: */ + genConditionalBranchoperand(JumpAbove, ((sqInt)fillLoop)); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNoSpace, jmpTarget(jumpUnhashed, jmpTarget(jumpFailCuzFixed, jmpTarget(jumpArrayTooBig, jmpTarget(jumpByteTooBig, jmpTarget(jumpLongTooBig, jmpTarget(jumpNElementsNonInt, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))))))); + return 0; +} + + +/* Implement primitiveShallowCopy/primitiveClone for convenient cases: + - the receiver is not a context + - the receiver is not a compiled method + - the result fits in eden (actually below scavengeThreshold) */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveShallowCopy */ +static sqInt +genPrimitiveShallowCopy(void) +{ + AbstractInstruction *continuance; + AbstractInstruction *copyLoop; + sqInt formatReg; + AbstractInstruction *jumpEmpty; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpIsMethod; + AbstractInstruction *jumpNoSpace; + AbstractInstruction *jumpTooBig; + AbstractInstruction *jumpVariable; + sqInt ptrReg; + sqInt quickConstant; + sqInt resultReg; + sqInt slotsReg; + + jumpImmediate = genJumpImmediate(ReceiverResultReg); + resultReg = Arg0Reg; + slotsReg = Arg1Reg; + + /* get freeStart as early as possible so as not to wait later... */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), resultReg); + + /* formatReg := self formatOf: ReceiverResultReg */ + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (ptrReg = (formatReg = SendNumArgsReg)), NoReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), formatReg); + jumpIsMethod = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, indexablePointersFormat(), formatReg); + jumpVariable = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + continuance = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + genGetRawSlotSizeOfNonImminto(ReceiverResultReg, slotsReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, numSlotsMask(), slotsReg); + jumpTooBig = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, slotsReg); + jumpEmpty = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* compute byte size for slots */ + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BaseHeaderSize / BytesPerWord, slotsReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), slotsReg); + + /* AddR:R: */ + genoperandoperand(AddRR, resultReg, slotsReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, getScavengeThreshold(), slotsReg); + + /* check if allocation fits (freeSize + byteSize < scavengeThreshold); scavengeThreshold is constant. */ + jumpNoSpace = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, resultReg, ptrReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, slotsReg, freeStartAddress()); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, BytesPerWord * 2, slotsReg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, ReceiverResultReg, TempReg); + quickConstant = headerForSlotsformatclassIndex(numSlotsMask(), formatMask(), classIndexMask()); + + /* begin AndCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, 0, resultReg); + + /* write back new freeStart; get result. slotsReg holds new freeStart, the limit of the object + set up loop bounds + copy header, masking off irrelevant bits */ + copyLoop = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BytesPerWord, ReceiverResultReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BytesPerWord, ptrReg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, ReceiverResultReg, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, 0, ptrReg); + + /* begin CmpR:R: */ + assert(!((ptrReg == SPReg))); + genoperandoperand(CmpRR, ptrReg, slotsReg); + + /* JumpAboveOrEqual: */ + genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)copyLoop)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, resultReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* If the receiver is variable pointers, fail if its a context, otherwise continue */ + jmpTarget(jumpVariable, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genGetClassIndexOfNonImminto(ReceiverResultReg, ClassReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ClassMethodContextCompactIndex, ClassReg); + + /* JumpNonZero: */ + genConditionalBranchoperand(JumpNonZero, ((sqInt)continuance)); + jmpTarget(jumpImmediate, jmpTarget(jumpNoSpace, jmpTarget(jumpIsMethod, jmpTarget(jumpTooBig, jmpTarget(jumpEmpty, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))))); + return 0; +} + + +/* Generate the code for primitive 173, instVarAt:. Defer to + StackInterpreterPrimitives>>primitiveSlotAt for Contexts. + */ +/* c.f. StackInterpreter>>stSizeOf: SpurMemoryManager>>lengthOf:format: + fixedFieldsOf:format:length: + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSlotAt */ +static sqInt +genPrimitiveSlotAt(void) +{ + AbstractInstruction *convertToIntAndReturn; + sqInt formatReg; + AbstractInstruction *jumpBadIndex; + AbstractInstruction *jumpBytesOutOfBounds; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpIsBytes; + AbstractInstruction *jumpIsContext; + AbstractInstruction *jumpIsMethod; + AbstractInstruction *jumpIsShorts; + AbstractInstruction *jumpIsWords; + AbstractInstruction *jumpLongsOutOfBounds; + AbstractInstruction *jumpNonPointers; + AbstractInstruction *jumpPointersOutOfBounds; + AbstractInstruction *jumpShortsOutOfBounds; + AbstractInstruction *jumpToReturnLargeInteger; + AbstractInstruction *jumpWordsOutOfBounds; + sqInt nSlotsOrElementsReg; + sqInt quickConstant; + + nSlotsOrElementsReg = ClassReg; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpImmediate = genJumpImmediate(ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, Arg1Reg); + jumpBadIndex = genJumpNotSmallInteger(Arg0Reg); + genConvertSmallIntegerToIntegerInReg(Arg1Reg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 1, Arg1Reg); + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), TempReg); + genGetNumSlotsOfinto(ReceiverResultReg, nSlotsOrElementsReg); + + /* dispatch on format in a combination of highest dynamic frequency order first and convenience. + 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, sixtyFourBitIndexableFormat(), formatReg); + jumpNonPointers = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, classIndexMask(), TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ClassMethodContextCompactIndex, TempReg); + + /* Fail to StackInterpreterPrimitives>>primitiveSlotAt for Contexts */ + jumpIsContext = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpPointersOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* index is (formatReg Arg1Reg (0-rel index) * wordSize + baseHeaderSize */ + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), Arg1Reg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, Arg1Reg, ReceiverResultReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNonPointers, genoperandoperand(CmpCqR, firstByteFormat(), formatReg)); + jumpIsBytes = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstShortFormat(), formatReg); + jumpIsShorts = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstLongFormat(), formatReg); + jumpIsWords = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* fall through to objectMemory sixtyFourBitIndexableFormat */ + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpLongsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), Arg1Reg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, Arg1Reg, ReceiverResultReg, ClassReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, TempReg); + quickConstant = (numSmallIntegerBits()) - 1; + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, quickConstant, TempReg); + if (!(setsConditionCodesFor(lastOpcode(), JumpZero))) { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, TempReg); + } + jumpToReturnLargeInteger = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genConvertIntegerInRegtoSmallIntegerInReg(ClassReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpToReturnLargeInteger, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genInstantiate64BitPositiveIntegerValueintoscratchRegmayFail(ClassReg, ReceiverResultReg, TempReg, 0); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsBytes, genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), nSlotsOrElementsReg)); + gAndCqRR(7, formatReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, nSlotsOrElementsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpBytesOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), formatReg); + jumpIsMethod = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BaseHeaderSize, Arg1Reg); + + /* formatReg already contains a value <= 16r1f, so no need to zero it */ + + /* MoveXbr:R:R: */ + genoperandoperandoperand(MoveXbrRR, Arg1Reg, ReceiverResultReg, formatReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, formatReg, ReceiverResultReg); + convertToIntAndReturn = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + genConvertIntegerToSmallIntegerInReg(ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsShorts, gLogicalShiftLeftCqR((shiftForWord()) - 1, nSlotsOrElementsReg)); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, 3, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrElementsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpShortsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg1Reg, ReceiverResultReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg1Reg, ReceiverResultReg); + + /* #MoveM16:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveM16rR, BaseHeaderSize, ReceiverResultReg, ReceiverResultReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)convertToIntAndReturn)); + jmpTarget(jumpIsWords, gLogicalShiftLeftCqR((shiftForWord()) - 2, nSlotsOrElementsReg)); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrElementsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpWordsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> ((shiftForWord()) - 1), Arg1Reg); + + /* MoveX32r:R:R: */ + genoperandoperandoperand(MoveX32rRR, Arg1Reg, ReceiverResultReg, TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, ReceiverResultReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)convertToIntAndReturn)); + jmpTarget(jumpLongsOutOfBounds, jmpTarget(jumpPointersOutOfBounds, jmpTarget(jumpBytesOutOfBounds, jmpTarget(jumpIsMethod, jmpTarget(jumpShortsOutOfBounds, jmpTarget(jumpWordsOutOfBounds, jmpTarget(jumpIsContext, jmpTarget(jumpBadIndex, jmpTarget(jumpImmediate, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))))))))); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatAdd */ +static sqInt +genPrimitiveSmallFloatAdd(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(AddRdRd, null, 0) + : genPureFloatArithmeticpreOpCheckboxed(AddRdRd, null, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatDivide */ +static sqInt +genPrimitiveSmallFloatDivide(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(DivRdRd, genDoubleFailIfZeroArgRcvrarg, 0) + : genPureFloatArithmeticpreOpCheckboxed(DivRdRd, genDoubleFailIfZeroArgRcvrarg, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatEqual */ +static sqInt +genPrimitiveSmallFloatEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPEqual, JumpZero, 0, 0) + : genPureFloatComparisoninvertboxed(gJumpFPEqual, 0, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatGreaterOrEqual */ +static sqInt +genPrimitiveSmallFloatGreaterOrEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreaterOrEqual, JumpGreaterOrEqual, 0, 0) + : genPureFloatComparisoninvertboxed(gJumpFPGreaterOrEqual, 0, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatGreaterThan */ +static sqInt +genPrimitiveSmallFloatGreaterThan(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreater, JumpGreater, 0, 0) + : genPureFloatComparisoninvertboxed(gJumpFPGreater, 0, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatLessOrEqual */ +static sqInt +genPrimitiveSmallFloatLessOrEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreaterOrEqual, JumpLessOrEqual, 1, 0) + : genPureFloatComparisoninvertboxed(gJumpFPGreaterOrEqual, 1, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatLessThan */ +static sqInt +genPrimitiveSmallFloatLessThan(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreater, JumpLess, 1, 0) + : genPureFloatComparisoninvertboxed(gJumpFPGreater, 1, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatMultiply */ +static sqInt +genPrimitiveSmallFloatMultiply(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(MulRdRd, null, 0) + : genPureFloatArithmeticpreOpCheckboxed(MulRdRd, null, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatNotEqual */ +static sqInt +genPrimitiveSmallFloatNotEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPNotEqual, JumpNonZero, 0, 0) + : genPureFloatComparisoninvertboxed(gJumpFPNotEqual, 0, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatSquareRoot */ +static sqInt +genPrimitiveSmallFloatSquareRoot(void) +{ + AbstractInstruction *jumpNegative; + + genGetSmallFloatValueOfscratchinto(ReceiverResultReg, SendNumArgsReg, DPFPReg0); + + /* XorRd:Rd: */ + genoperandoperand(XorRdRd, DPFPReg1, DPFPReg1); + + /* CmpRd:Rd: */ + genoperandoperand(CmpRdRd, DPFPReg0, DPFPReg1); + + /* +0.0 is all zeros */ + jumpNegative = gJumpFPGreater(0); + + /* SqrtRd: */ + genoperand(SqrtRd, DPFPReg0); + genInstantiateFloatValueintoscratchRegscratchRegmayFail(DPFPReg0, ReceiverResultReg, ClassReg, TempReg, 0); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNegative, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatSubtract */ +static sqInt +genPrimitiveSmallFloatSubtract(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(SubRdRd, null, 0) + : genPureFloatArithmeticpreOpCheckboxed(SubRdRd, null, 0)); +} + + +/* c.f. StackInterpreter>>stSizeOf: SpurMemoryManager>>lengthOf:format: + fixedFieldsOf:format:length: + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveStringAt */ +static sqInt +genPrimitiveStringAt(void) +{ + AbstractInstruction *done; + sqInt formatReg; + AbstractInstruction *jumpBadIndex; + AbstractInstruction *jumpBytesOutOfBounds; + AbstractInstruction *jumpIsBytes; + AbstractInstruction *jumpIsShorts; + AbstractInstruction *jumpNotIndexable; + AbstractInstruction *jumpShortsOutOfBounds; + AbstractInstruction *jumpWordsDone; + AbstractInstruction *jumpWordsOutOfBounds; + AbstractInstruction *jumpWordTooBig; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, Arg1Reg); + jumpBadIndex = genJumpNotSmallInteger(Arg0Reg); + genConvertSmallIntegerToIntegerInReg(Arg1Reg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 1, Arg1Reg); + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), NoReg); + genGetNumSlotsOfinto(ReceiverResultReg, ClassReg); + + /* dispatch on format in a combination of highest dynamic frequency order first and convenience. + 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), formatReg); + jumpIsBytes = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstShortFormat(), formatReg); + jumpIsShorts = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstLongFormat(), formatReg); + jumpNotIndexable = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* fall through to double words */ + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - 2, ClassReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 4) - 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, ClassReg); + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, ClassReg); + jumpWordsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> ((shiftForWord()) - 1), Arg1Reg); + + /* MoveX32r:R:R: */ + genoperandoperandoperand(MoveX32rRR, Arg1Reg, ReceiverResultReg, TempReg); + jumpWordTooBig = jumpNotCharacterUnsignedValueInRegister(TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, ReceiverResultReg); + jumpWordsDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpIsBytes, genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), ClassReg)); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, BytesPerWord - 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, ClassReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, ClassReg); + jumpBytesOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BaseHeaderSize, Arg1Reg); + + /* MoveXbr:R:R: */ + genoperandoperandoperand(MoveXbrRR, Arg1Reg, ReceiverResultReg, ReceiverResultReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, 0xFF, ReceiverResultReg); + jmpTarget(jumpWordsDone, (done = genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + genConvertIntegerToCharacterInReg(ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsShorts, gLogicalShiftLeftCqR((shiftForWord()) - 1, ClassReg)); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 2) - 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, ClassReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, ClassReg); + jumpShortsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg1Reg, ReceiverResultReg); + + /* #MoveM16:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveM16rR, BaseHeaderSize, ReceiverResultReg, ReceiverResultReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)done)); + jmpTarget(jumpWordTooBig, jmpTarget(jumpBytesOutOfBounds, jmpTarget(jumpShortsOutOfBounds, jmpTarget(jumpWordsOutOfBounds, jmpTarget(jumpNotIndexable, jmpTarget(jumpBadIndex, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))))); + return CompletePrimitive; +} + + +/* c.f. StackInterpreter>>stSizeOf: SpurMemoryManager>>lengthOf:format: + fixedFieldsOf:format:length: + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveStringAtPut */ +static sqInt +genPrimitiveStringAtPut(void) +{ + sqInt formatReg; + AbstractInstruction *jumpBadArg; + AbstractInstruction *jumpBadIndex; + AbstractInstruction *jumpBytesOutOfBounds; + AbstractInstruction *jumpBytesOutOfRange; + AbstractInstruction *jumpImmutable; + AbstractInstruction *jumpIsBytes; + AbstractInstruction *jumpIsCompiledMethod; + AbstractInstruction *jumpIsShorts; + AbstractInstruction *jumpNotString; + AbstractInstruction *jumpShortsOutOfBounds; + AbstractInstruction *jumpShortsOutOfRange; + AbstractInstruction *jumpWordsOutOfBounds; + AbstractInstruction *jumpWordsOutOfRange; + sqInt quickConstant; + + /* begin genLoadArgAtDepth:into: */ + assert(1 < (numRegArgs())); + assert(0 < (numRegArgs())); + jumpBadIndex = genJumpNotSmallInteger(Arg0Reg); + jumpBadArg = genJumpNotCharacter(Arg1Reg); + genConvertSmallIntegerToIntegerInReg(Arg0Reg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 1, Arg0Reg); +# if IMMUTABILITY + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), TempReg); + + /* begin genJumpBaseHeaderImmutable: */ + /* TstCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, immutableBitMask(), TempReg); + jumpImmutable = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +# else // IMMUTABILITY + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), NoReg); +# endif // IMMUTABILITY + + genGetNumSlotsOfinto(ReceiverResultReg, ClassReg); + + /* dispatch on format; words and/or bytes. + 0 to 8 = pointer objects, forwarders, reserved. + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstLongFormat(), formatReg); + jumpNotString = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), formatReg); + jumpIsCompiledMethod = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), formatReg); + jumpIsBytes = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstShortFormat(), formatReg); + jumpIsShorts = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* fall through to double words */ + quickConstant = characterObjectOf((1U << (numCharacterBits())) - 1); + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, Arg1Reg); + jumpWordsOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - 2, ClassReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 4) - 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, ClassReg); + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, ClassReg); + jumpWordsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertCharacterToCodeInReg(TempReg); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> ((shiftForWord()) - 1), Arg0Reg); + + /* MoveR:X32r:R: */ + genoperandoperandoperand(MoveRX32rR, TempReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsShorts, gCmpCqR(characterObjectOf(0xFFFF), Arg1Reg)); + jumpShortsOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - 1, ClassReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 2) - 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, ClassReg); + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, ClassReg); + jumpShortsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertCharacterToCodeInReg(TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg0Reg, ReceiverResultReg); + + /* #MoveR:M16:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRM16r, TempReg, BaseHeaderSize, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsBytes, gCmpCqR(characterObjectOf(0xFF), Arg1Reg)); + jumpBytesOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), ClassReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, BytesPerWord - 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, ClassReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, ClassReg); + jumpBytesOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertCharacterToCodeInReg(TempReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BaseHeaderSize, Arg0Reg); + + /* MoveR:Xbr:R: */ + genoperandoperandoperand(MoveRXbrR, TempReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotString, jmpTarget(jumpBytesOutOfRange, jmpTarget(jumpShortsOutOfRange, jmpTarget(jumpWordsOutOfRange, jmpTarget(jumpIsCompiledMethod, jmpTarget(jumpBytesOutOfBounds, jmpTarget(jumpShortsOutOfBounds, jmpTarget(jumpWordsOutOfBounds, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))))))); +# if IMMUTABILITY + jmpTarget(jumpImmutable, ((AbstractInstruction *) (((jumpNotString->operands))[0]))); +# endif + + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, Arg0Reg); + genConvertIntegerToSmallIntegerInReg(Arg0Reg); + jmpTarget(jumpBadArg, jmpTarget(jumpBadIndex, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + +/* Implement primitiveUninitializedNewWithArg for convenient cases: + - the receiver has a hash + - the receiver is variable and not compiled method + - the result fits in eden + See superclass method for dynamic frequencies of formats. + For the moment we implement only arrayFormat, firstByteFormat & + firstLongFormat + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveUninitializedNewWithArg */ +static sqInt +genPrimitiveUninitializedNewWithArg(void) +{ + sqInt byteSizeReg; + sqInt fillReg; + sqInt headerReg; + sqInt instSpecReg; + AbstractInstruction *jumpByteFormat; + AbstractInstruction *jumpByteTooBig; + AbstractInstruction *jumpDoubleByteFormat; + AbstractInstruction *jumpDoubleBytePrepDone; + AbstractInstruction *jumpDoubleWordFormat; + AbstractInstruction *jumpDoubleWordPrepDone; + AbstractInstruction *jumpDoubleWordTooBig; + AbstractInstruction *jumpFailCuzFixed; + AbstractInstruction *jumpHasSlots; + AbstractInstruction *jumpLongPrepDone; + AbstractInstruction *jumpLongTooBig; + AbstractInstruction *jumpNElementsNonInt; + AbstractInstruction *jumpNoSpace; + AbstractInstruction *jumpNoSpaceBigObjects; + AbstractInstruction *jumpOverflowHeader; + AbstractInstruction *jumpShortTooBig; + AbstractInstruction *jumpUnhashed; + sqInt maxSlots; + sqInt quickConstant; + AbstractInstruction *skip; + + if (methodOrBlockNumArgs != 1) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* header will contain classIndex/class's hash & format & numSlots/fixed size */ + headerReg = SendNumArgsReg; + + /* Assume there's an available scratch register on 64-bit machines. This holds the saved numFixedFields and then the value to fill with */ + fillReg = Extra0Reg; + assert(fillReg > 0); + + /* inst spec will hold class's instance specification and then byte size */ + instSpecReg = (byteSizeReg = ClassReg); + + /* Allow a max of 1 MB */ + maxSlots = maxSlotsForNewSpaceAlloc(); + + /* get freeStart as early as possible so as not to wait later... */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), Arg1Reg); + + /* get class's hash & fail if 0 */ + genGetHashFieldNonImmOfinto(ReceiverResultReg, headerReg); + jumpUnhashed = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* get index and fail if not a +ve integer */ + jumpNElementsNonInt = genJumpNotSmallInteger(Arg0Reg); + + /* get class's format inst var for inst spec (format field) */ + genLoadSlotsourceRegdestReg(InstanceSpecificationIndex, ReceiverResultReg, instSpecReg); + quickConstant = (fixedFieldsFieldWidth()) + (numSmallIntegerTagBits()); + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, quickConstant, instSpecReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, formatMask(), instSpecReg); + + /* Add format to classIndex/format header now */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, instSpecReg, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* get integer value of num fields in fillReg now */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, fillReg); + genConvertSmallIntegerToIntegerInReg(fillReg); + + /* dispatch on format, failing if not a byte format */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), instSpecReg); + jumpByteFormat = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstShortFormat(), instSpecReg); + jumpDoubleByteFormat = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, sixtyFourBitIndexableFormat(), instSpecReg); + jumpDoubleWordFormat = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstLongFormat(), instSpecReg); + jumpFailCuzFixed = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* allocates a 32-bit array */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, (((usqInt)(maxSlots * 2) << 3) | 1), Arg0Reg); + jumpLongTooBig = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* save num elements/slot size to instSpecReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, fillReg, instSpecReg); + + /* compute odd bits and add into headerReg; oddBits := 2 - nElements bitAnd: 1 */ + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, BytesPerWord / 4, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, instSpecReg, TempReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 4) - 1, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* round up num elements to numSlots in instSpecReg; (numElements / 2) ceiling */ + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, (BytesPerWord / 4) - 1, instSpecReg); + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, (shiftForWord()) - 2, instSpecReg); + + /* go allocate */ + /* allocates a 16-bit array */ + jumpLongPrepDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpDoubleByteFormat, genoperandoperand(CmpCqR, (((usqInt)(maxSlots * 4) << 3) | 1), Arg0Reg)); + jumpShortTooBig = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* save num elements to instSpecReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, fillReg, instSpecReg); + + /* compute odd bits and add into headerReg; oddBits := 4 - nElements bitAnd: 3 */ + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, BytesPerWord / 2, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, instSpecReg, TempReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 2) - 1, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* round up num elements to numSlots in instSpecReg; (numElements / 4) ceiling */ + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, (BytesPerWord / 2) - 1, instSpecReg); + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, (shiftForWord()) - 1, instSpecReg); + + /* go allocate */ + /* allocates a 64-bit array */ + jumpDoubleBytePrepDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpDoubleWordFormat, genoperandoperand(CmpCqR, (((usqInt)maxSlots << 3) | 1), Arg0Reg)); + jumpDoubleWordTooBig = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* save num elements to instSpecReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, fillReg, instSpecReg); + + /* go allocate */ + /* allocates a byte array */ + jumpDoubleWordPrepDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpByteFormat, genoperandoperand(CmpCqR, (((usqInt)(maxSlots * BytesPerWord) << 3) | 1), Arg0Reg)); + jumpByteTooBig = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* save num elements to instSpecReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, fillReg, instSpecReg); + + /* compute odd bits and add into headerReg; oddBits := 8 - nElements bitAnd: 7 */ + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, BytesPerWord, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, instSpecReg, TempReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, BytesPerWord - 1, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* round up num elements to numSlots in instSpecReg; (numElements / 8) ceiling */ + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BytesPerWord - 1, instSpecReg); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, shiftForWord(), instSpecReg); + + /* fall through to allocate */ + jmpTarget(jumpDoubleWordPrepDone, jmpTarget(jumpDoubleBytePrepDone, jmpTarget(jumpLongPrepDone, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + + /* if numSlots >= 255 -> overflow header -> handle it */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, numSlotsMask(), instSpecReg); + jumpOverflowHeader = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* fallthrough: allocate objects with < 255 slots + store numSlots to headerReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, instSpecReg, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, numSlotsFullShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* compute byte size; remember 0-sized objects still need 1 slot. */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, byteSizeReg); + jumpHasSlots = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, BaseHeaderSize * 2, byteSizeReg); + skip = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpHasSlots, genoperandoperand(AddCqR, BaseHeaderSize / BytesPerWord, byteSizeReg)); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), byteSizeReg); + jmpTarget(skip, genoperandoperand(AddRR, Arg1Reg, byteSizeReg)); + + /* check if allocation fits */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, getScavengeThreshold(), byteSizeReg); + jumpNoSpace = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* get result, increment freeStart and write it back. Arg1Reg holds new freeStart, the limit of the object */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, byteSizeReg, freeStartAddress()); + + /* write other half of header (numSlots/0 identityHash) */ + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, headerReg, 0, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* allocate object with up to 1 << 16 - 1 slots; numSlots >= 255 + store always 255 to headerReg */ + jmpTarget(jumpOverflowHeader, genoperandoperand(MoveCqR, 0xFF, TempReg)); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, numSlotsFullShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* calculate overflow header; Temp Reg can be resued as it already contains 255 as needed */ + + /* AddR:R: */ + genoperandoperand(AddRR, instSpecReg, TempReg); + + /* compute byte size */ + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, (BaseHeaderSize * 2) / BytesPerWord, byteSizeReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), byteSizeReg); + + /* check if allocation fits */ + + /* AddR:R: */ + genoperandoperand(AddRR, Arg1Reg, byteSizeReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, getScavengeThreshold(), byteSizeReg); + jumpNoSpaceBigObjects = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* get result, increment freeStart and write it back. Arg1Reg holds new freeStart, the limit of the object */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, byteSizeReg, freeStartAddress()); + + /* write overflow part */ + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, 0, ReceiverResultReg); + + /* write header */ + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, headerReg, BaseHeaderSize, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpShortTooBig, jmpTarget(jumpDoubleWordTooBig, jmpTarget(jumpNoSpaceBigObjects, jmpTarget(jumpNoSpace, jmpTarget(jumpUnhashed, jmpTarget(jumpFailCuzFixed, jmpTarget(jumpByteTooBig, jmpTarget(jumpLongTooBig, jmpTarget(jumpNElementsNonInt, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))))))))); + return 0; +} + + +/* In the Pure version, mixed arithmetic with SmallInteger is forbidden */ + + /* CogObjectRepresentationFor64BitSpur>>#genPureFloatArithmetic:preOpCheck:boxed: */ +static NoDbgRegParms sqInt +genPureFloatArithmeticpreOpCheckboxed(sqInt arithmeticOperator, AbstractInstruction *(*preOpCheckOrNil)(int rcvrReg, int argReg), sqInt rcvrBoxed) +{ + AbstractInstruction *doOp; + AbstractInstruction *jumpFailCheck; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpNotBoxedFloat; + AbstractInstruction *jumpNotSmallFloat; + + jumpFailCheck = ((AbstractInstruction *) 0); + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + if (rcvrBoxed) { + genGetDoubleValueOfinto(ReceiverResultReg, DPFPReg0); + } + else { + genGetSmallFloatValueOfscratchinto(ReceiverResultReg, TempReg, DPFPReg0); + } + jumpNotSmallFloat = genJumpNotSmallFloat(Arg0Reg); + genGetSmallFloatValueOfscratchinto(Arg0Reg, TempReg, DPFPReg1); + doOp = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + if (preOpCheckOrNil) { + jumpFailCheck = preOpCheckOrNil(DPFPReg0, DPFPReg1); + } + genoperandoperand(arithmeticOperator, DPFPReg1, DPFPReg0); + genInstantiateFloatValueintoscratchRegscratchRegmayFail(DPFPReg0, ReceiverResultReg, ClassReg, TempReg, 0); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSmallFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpImmediate = genJumpImmediate(Arg0Reg); + genGetCompactClassIndexNonImmOfinto(Arg0Reg, SendNumArgsReg); + genCmpClassFloatCompactIndexR(SendNumArgsReg); + jumpNotBoxedFloat = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genGetDoubleValueOfinto(Arg0Reg, DPFPReg1); + + /* Jump: */ + genoperand(Jump, ((sqInt)doOp)); + jmpTarget(jumpImmediate, jmpTarget(jumpNotBoxedFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + if (preOpCheckOrNil) { + jmpTarget(jumpFailCheck, ((AbstractInstruction *) (((jumpNotBoxedFloat->operands))[0]))); + } + return 0; +} + + +/* In the Pure version, mixed arithmetic with SmallInteger is forbidden */ + + /* CogObjectRepresentationFor64BitSpur>>#genPureFloatComparison:invert:boxed: */ +static NoDbgRegParms sqInt +genPureFloatComparisoninvertboxed(AbstractInstruction *(*jumpFPOpcodeGenerator)(void *), sqInt invertComparison, sqInt rcvrBoxed) +{ + AbstractInstruction *compareFloat; + AbstractInstruction *jumpCond; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpNotBoxedFloat; + AbstractInstruction *jumpNotSmallFloat; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + if (rcvrBoxed) { + genGetDoubleValueOfinto(ReceiverResultReg, DPFPReg0); + } + else { + genGetSmallFloatValueOfscratchinto(ReceiverResultReg, TempReg, DPFPReg0); + } + jumpNotSmallFloat = genJumpNotSmallFloat(Arg0Reg); + genGetSmallFloatValueOfscratchinto(Arg0Reg, TempReg, DPFPReg1); + compareFloat = (invertComparison + ? genoperandoperand(CmpRdRd, DPFPReg0, DPFPReg1) + : genoperandoperand(CmpRdRd, DPFPReg1, DPFPReg0)); + + /* May need to invert for NaNs */ + + /* FP jumps are a little weird */ + jumpCond = jumpFPOpcodeGenerator(0); + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpCond, /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, trueObject(), ReceiverResultReg)); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* not a Small Float, check for Boxed Float argument */ + jmpTarget(jumpNotSmallFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpImmediate = genJumpImmediate(Arg0Reg); + genGetCompactClassIndexNonImmOfinto(Arg0Reg, SendNumArgsReg); + genCmpClassFloatCompactIndexR(SendNumArgsReg); + jumpNotBoxedFloat = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genGetDoubleValueOfinto(Arg0Reg, DPFPReg1); + + /* Jump: */ + genoperand(Jump, ((sqInt)compareFloat)); + jmpTarget(jumpImmediate, jmpTarget(jumpNotBoxedFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + /* CogObjectRepresentationFor64BitSpur>>#genRemoveSmallIntegerTagsInScratchReg: */ +static NoDbgRegParms sqInt +genRemoveSmallIntegerTagsInScratchReg(sqInt scratchReg) +{ + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 1, scratchReg); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genShiftAwaySmallIntegerTagsInScratchReg: */ +static NoDbgRegParms sqInt +genShiftAwaySmallIntegerTagsInScratchReg(sqInt scratchReg) +{ + /* ArithmeticShiftRightCq:R: */ + genoperandoperand(ArithmeticShiftRightCqR, numTagBits(), scratchReg); + return 0; +} + + +/* Stack looks like + return address */ + + /* CogObjectRepresentationFor64BitSpur>>#genSmallIntegerComparison:orDoubleComparison:invert: */ +static NoDbgRegParms sqInt +genSmallIntegerComparisonorDoubleComparisoninvert(sqInt jumpOpcode, AbstractInstruction * NoDbgRegParms (*jumpFPOpcodeGenerator)(void *), sqInt invertComparison) +{ + AbstractInstruction *compareIntFloat; + AbstractInstruction *jumpAmbiguous; + AbstractInstruction *jumpCond; + AbstractInstruction *jumpNotBoxedFloat; + AbstractInstruction *jumpNotFloatAtAll; + AbstractInstruction *jumpNotSmallFloat; + AbstractInstruction *jumpTrue; + sqInt r; + AbstractInstruction *returnTrue; + + r = genSmallIntegerComparison(jumpOpcode); + if (r < 0) { + return r; + } +# if defined(DPFPReg0) + jumpNotSmallFloat = genJumpNotSmallFloat(Arg0Reg); + genGetSmallFloatValueOfscratchinto(Arg0Reg, TempReg, DPFPReg1); + + /* Case of (int compare: float). Test for ambiguity, that is when (double) intRcvr == floatArg */ + compareIntFloat = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + genConvertSmallIntegerToIntegerInReg(ReceiverResultReg); + + /* ConvertR:Rd: */ + genoperandoperand(ConvertRRd, ReceiverResultReg, DPFPReg0); + + /* CmpRd:Rd: */ + genoperandoperand(CmpRdRd, DPFPReg0, DPFPReg1); + jumpAmbiguous = gJumpFPEqual(0); + + /* Case of non ambiguity, use compareFloat((double) intRcvr,floatArg) + May need to invert for NaNs */ + if (invertComparison) { + /* CmpRd:Rd: */ + genoperandoperand(CmpRdRd, DPFPReg0, DPFPReg1); + } + else { + /* CmpRd:Rd: */ + genoperandoperand(CmpRdRd, DPFPReg1, DPFPReg0); + } + + /* FP jumps are a little weird */ + jumpCond = jumpFPOpcodeGenerator(0); + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpCond, (returnTrue = /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, trueObject(), ReceiverResultReg))); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* Case of ambiguity, use compareInt(intRcvr , (int64) floatArg) */ + jmpTarget(jumpAmbiguous, genoperandoperand(ConvertRdR, DPFPReg1, Arg0Reg)); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, ReceiverResultReg); + jumpTrue = genConditionalBranchoperand(jumpOpcode, 0); + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpTrue, returnTrue); + + /* not a Small Float, check for Boxed Float argument */ + jmpTarget(jumpNotSmallFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpNotFloatAtAll = genJumpImmediate(Arg0Reg); + genGetCompactClassIndexNonImmOfinto(Arg0Reg, SendNumArgsReg); + genCmpClassFloatCompactIndexR(SendNumArgsReg); + jumpNotBoxedFloat = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* It was a Boxed Float, so convert the receiver to double and perform the (int compare: float) operation */ + genGetDoubleValueOfinto(Arg0Reg, DPFPReg1); + + /* Jump: */ + genoperand(Jump, ((sqInt)compareIntFloat)); + + /* not a Float, just let the primitive fall thru failure */ + jmpTarget(jumpNotBoxedFloat, jmpTarget(jumpNotFloatAtAll, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); +# endif // defined(DPFPReg0) + + + /* Fall through on non-SmallInteger argument. Argument may be a Float : let us check or fail + check for Small Float argument */ + return CompletePrimitive; +} + + +/* Get the literal count of a CompiledMethod into headerReg, plus one if + requested. If inBytes is true, scale the count by the word size. Deal with + the possibility of + the method being cogged. */ + + /* CogObjectRepresentationFor64BitSpur>>#getLiteralCountOf:plusOne:inBytes:into:scratch: */ +static NoDbgRegParms sqInt +getLiteralCountOfplusOneinBytesintoscratch(sqInt methodReg, sqInt plusOne, sqInt inBytes, sqInt litCountReg, sqInt scratchReg) +{ + sqInt quickConstant; + + genGetMethodHeaderOfintoscratch(methodReg, litCountReg, scratchReg); + assert((1U << (numTagBits())) == BytesPerWord); + if (inBytes) { + quickConstant = ((sqInt)((usqInt)((alternateHeaderNumLiteralsMask())) << (numTagBits()))); + + /* begin AndCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, litCountReg); + } + else { + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, numTagBits(), litCountReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, alternateHeaderNumLiteralsMask(), litCountReg); + } + if (plusOne) { + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, (inBytes + ? LiteralStart * BytesPerWord + : LiteralStart), litCountReg); + } + return 0; +} + + +/* Answer the relevant inline cache tag for an instance. + c.f. getInlineCacheClassTagFrom:into: & inlineCacheTagForClass: */ + + /* CogObjectRepresentationFor64BitSpur>>#inlineCacheTagForInstance: */ +static NoDbgRegParms sqInt +inlineCacheTagForInstance(sqInt oop) +{ + return (isImmediate(oop) + ? oop & (tagMask()) + : classIndexOf(oop)); +} + + /* CogObjectRepresentationFor64BitSpur>>#log2BytesPerWord */ +static sqInt +log2BytesPerWord(void) +{ + return 3; +} + + +/* Generate the routine that converts selector indices into selector objects. + It is called from the send trampolines. + If the selector index is negative, convert it into a positive index into + the special selectors array and index that. Otherwise, index the current + method. The routine uses Extra0Reg & Extra1Reg, which are available, since + they are not live at point of send. */ + + /* CogObjectRepresentationFor64BitSpur>>#maybeGenerateSelectorIndexDereferenceRoutine */ +static void +maybeGenerateSelectorIndexDereferenceRoutine(void) +{ + sqInt byteOffset; + AbstractInstruction *jumpFullBlock; + AbstractInstruction *jumpNegative; + AbstractInstruction *jumpNotBlock; + sqInt offset; + + zeroOpcodeIndex(); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ClassReg); + jumpNegative = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, FoxMethod, FPReg, Extra0Reg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 2, ClassReg); + + /* #TstCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, MFMethodFlagIsBlockFlag, Extra0Reg); + + /* Change selector index to 1-relative, skipping the method header */ + jumpNotBlock = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* If in a block, need to find the home method... If using full blocks, need to test the cpicHasMNUCaseOrCMIsFullBlock bit */ + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, -(zoneAlignment()), Extra0Reg); + byteOffset = BaseHeaderSize + 1; + + /* #MoveMb:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMbrR, byteOffset, Extra0Reg, Extra1Reg); + + /* #TstCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, 16, Extra1Reg); + jumpFullBlock = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #MoveM16:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveM16rR, 0, Extra0Reg, Extra1Reg); + + /* SubR:R: */ + genoperandoperand(SubRR, Extra1Reg, Extra0Reg); + jmpTarget(jumpNotBlock, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jmpTarget(jumpFullBlock, ((AbstractInstruction *) (((jumpNotBlock->operands))[0]))); + + /* Now fetch the method object and index with the literal index to retrieve the selector */ + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, -(zoneAlignment()), Extra0Reg); + offset = offsetof(CogMethod, methodObject); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, Extra0Reg, Extra1Reg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, ClassReg, Extra1Reg, ClassReg); + + /* RetN: */ + genoperand(RetN, 0); + jmpTarget(jumpNegative, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* NegateR: */ + genoperand(NegateR, ClassReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, 1, ClassReg); + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, specialObjectsOopAddress(), Extra0Reg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 1, ClassReg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, (SpecialSelectors + 1) * BytesPerWord, Extra0Reg, Extra1Reg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, ClassReg, Extra1Reg, ClassReg); + + /* RetN: */ + genoperand(RetN, 0); + + /* Index, including header size */ + ceDereferenceSelectorIndex = methodZoneBase; + outputInstructionsForGeneratedRuntimeAt(ceDereferenceSelectorIndex); + recordGeneratedRunTimeaddress("ceDereferenceSelectorIndex", ceDereferenceSelectorIndex); + recordRunTimeObjectReferences(); +} + + /* CogObjectRepresentationFor64BitSpur>>#numSmallIntegerBits */ +static sqInt +numSmallIntegerBits(void) +{ + return 61; +} + + /* CogObjectRepresentationFor64BitSpur>>#numSmallIntegerTagBits */ +static sqInt +numSmallIntegerTagBits(void) +{ + return 3; +} + + +/* The three valid tag patterns are 1 (SmallInteger), 2 (Character) and 3 + (SmallFloat64). + */ + + /* CogObjectRepresentationFor64BitSpur>>#validInlineCacheTag: */ +static NoDbgRegParms sqInt +validInlineCacheTag(sqInt classIndexOrTagPattern) +{ + return ((classIndexOrTagPattern >= 1) + && (classIndexOrTagPattern <= 3)) + || ((classAtIndex(classIndexOrTagPattern))); +} + + /* CogObjectRepresentationForSpur>>#callStoreCheckTrampoline */ +static void +callStoreCheckTrampoline(void) +{ + AbstractInstruction *abstractInstruction; + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreCheckTrampoline); + (abstractInstruction->annotation = IsRelativeCall); +} + + /* CogObjectRepresentationForSpur>>#checkValidOopReference: */ +static NoDbgRegParms sqInt +checkValidOopReference(sqInt anOop) +{ + return (isImmediate(anOop)) + || ((heapMapAtWord(pointerForOop(anOop))) != 0); +} + + +/* If primIndex has an accessorDepth, or is the external call, + compile and answer the jump to a retry routine. */ + + /* CogObjectRepresentationForSpur>>#compileJumpToRetryOf:onPrimitiveFail: */ +static NoDbgRegParms AbstractInstruction * +compileJumpToRetryOfonPrimitiveFail(void (*primitiveRoutine)(void), sqInt primIndex) +{ + if ((accessorDepthForPrimitiveIndex(primIndex)) >= 0) { + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, primFailCodeAddress(), TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, TempReg); + return genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + } + if (PrimNumberExternalCall == primIndex) { + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, primFailCodeAddress(), TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, PrimErrNoMemory, TempReg); + return genConditionalBranchoperand(JumpZero, ((sqInt)0)); + } + return null; +} + + /* CogObjectRepresentationForSpur>>#couldBeObject: */ +static NoDbgRegParms sqInt +couldBeObject(sqInt literal) +{ + return (isNonImmediate(literal)) + && (oopisGreaterThanOrEqualTo(literal, startOfMemory())); +} + + +/* Create a trampoline to answer the active context that will + answer it if a frame is already married, and create it otherwise. + Assume numArgs is in SendNumArgsReg and ClassReg is free. */ + + /* CogObjectRepresentationForSpur>>#genActiveContextTrampolineLarge:inBlock:called: */ +static NoDbgRegParms usqInt +genActiveContextTrampolineLargeinBlockcalled(sqInt isLarge, sqInt isInBlock, char *aString) +{ + usqInt startAddress; + + startAddress = methodZoneBase; + zeroOpcodeIndex(); + genGetActiveContextLargeinBlock(isLarge, isInBlock); + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress(aString, startAddress); + recordRunTimeObjectReferences(); + return startAddress; +} + + +/* Check the remembered bit of the object in objReg; answer the jump taken if + the bit is already set. + Only need to fetch the byte containing it, which reduces the size of the + mask constant. + */ + + /* CogObjectRepresentationForSpur>>#genCheckRememberedBitOf:scratch: */ +static NoDbgRegParms AbstractInstruction * +genCheckRememberedBitOfscratch(sqInt objReg, sqInt scratchReg) +{ + sqInt mask; + sqInt rememberedBitByteOffset; + + rememberedBitByteOffset = (rememberedBitShift()) / 8; + mask = 1ULL << ((rememberedBitShift()) % 8); + + /* #MoveMb:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMbrR, rememberedBitByteOffset, objReg, scratchReg); + + /* #TstCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, mask, scratchReg); + return genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +} + + /* CogObjectRepresentationForSpur>>#genConvertCharacterToCodeInReg: */ +static NoDbgRegParms sqInt +genConvertCharacterToCodeInReg(sqInt reg) +{ + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, numTagBits(), reg); + return 0; +} + + /* CogObjectRepresentationForSpur>>#genConvertIntegerToCharacterInReg: */ +static NoDbgRegParms sqInt +genConvertIntegerToCharacterInReg(sqInt reg) +{ + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, numTagBits(), reg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, characterTag(), reg); + return 0; +} + + +/* Create a full closure with the given values. */ + + /* CogObjectRepresentationForSpur>>#genCreateFullClosure:numArgs:numCopied:ignoreContext:contextNumArgs:large:inBlock: */ +static NoDbgRegParms sqInt +genCreateFullClosurenumArgsnumCopiedignoreContextcontextNumArgslargeinBlock(sqInt compiledBlock, sqInt numArgs, sqInt numCopied, sqInt ignoreContext, sqInt contextNumArgs, sqInt contextIsLarge, sqInt contextIsBlock) +{ + AbstractInstruction *abstractInstruction; + usqInt byteSize; + usqLong header; + sqInt numSlots; + AbstractInstruction *skip; + + + /* First get thisContext into ReceiverResultReg and thence in ClassReg. */ + if (ignoreContext) { + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, nilObject(), ClassReg); + } + else { + genGetActiveContextNumArgslargeinBlock(contextNumArgs, contextIsLarge, contextIsBlock); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, ClassReg); + } + numSlots = FullClosureFirstCopiedValueIndex + numCopied; + byteSize = smallObjectBytesForSlots(numSlots); + header = headerForSlotsformatclassIndex(numSlots, indexablePointersFormat(), ClassFullBlockClosureCompactIndex); + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), ReceiverResultReg); + + /* begin genStoreHeader:intoNewInstance:using: */ + /* MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, header, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, 0, ReceiverResultReg); + + /* #LoadEffectiveAddressMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(LoadEffectiveAddressMwrR, byteSize, ReceiverResultReg, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, freeStartAddress()); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, getScavengeThreshold(), TempReg); + skip = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceScheduleScavengeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpTarget(skip, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ClassReg, (ClosureOuterContextIndex * BytesPerOop) + BaseHeaderSize, ReceiverResultReg); + + /* genMoveConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(compiledBlock)) + && ((oopisGreaterThan(compiledBlock, classTableRootObj())) + || (oopisLessThan(compiledBlock, nilObject())))) { + annotateobjRef(genoperandoperand(MoveCwR, compiledBlock, TempReg), compiledBlock); + } + else { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, compiledBlock, TempReg); + } + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, (ClosureStartPCIndex * BytesPerOop) + BaseHeaderSize, ReceiverResultReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, (((usqInt)numArgs << 3) | 1), TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, (ClosureNumArgsIndex * BytesPerOop) + BaseHeaderSize, ReceiverResultReg); + return 0; +} + + +/* Make sure that the oop in reg is not forwarded. + Use the fact that isForwardedObjectClassIndexPun is a power of two to save + an instruction. */ +/* maybe a fixup or an instruction */ +/* maybe a fixup or an instruction */ + + /* CogObjectRepresentationForSpur>>#genEnsureOopInRegNotForwarded:scratchReg:ifForwarder:ifNotForwarder: */ +static NoDbgRegParms sqInt +genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(sqInt reg, sqInt scratch, void *fwdJumpTarget, void *nonFwdJumpTargetOrZero) +{ + AbstractInstruction *finished; + AbstractInstruction *imm; + AbstractInstruction *ok; + sqInt quickConstant; + + assert(reg != scratch); + imm = genJumpImmediate(reg); + + /* notionally + self genGetClassIndexOfNonImm: reg into: scratch. + cogit CmpCq: objectMemory isForwardedObjectClassIndexPun R: TempReg. + but the following is an instruction shorter: */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, reg, scratch); + quickConstant = (classIndexMask()) - (isForwardedObjectClassIndexPun()); + + /* begin AndCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, scratch); + ok = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genLoadSlotsourceRegdestReg(0, reg, reg); + + /* Jump: */ + genoperand(Jump, ((sqInt)fwdJumpTarget)); + finished = (((usqInt)nonFwdJumpTargetOrZero) + ? nonFwdJumpTargetOrZero + : genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jmpTarget(imm, jmpTarget(ok, finished)); + return 0; +} + + +/* Make sure that the oop in reg is not forwarded, updating the slot in + objReg with the value. + */ + + /* CogObjectRepresentationForSpur>>#genEnsureOopInRegNotForwarded:scratchReg:updatingSlot:in: */ +static NoDbgRegParms sqInt +genEnsureOopInRegNotForwardedscratchRegupdatingSlotin(sqInt reg, sqInt scratch, sqInt index, sqInt objReg) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *imm; + AbstractInstruction *loop; + AbstractInstruction *ok; + sqInt quickConstant; + + + /* Open-code + self genEnsureOopInRegNotForwarded: reg + scratchReg: scratch + updatingMw: index * objectMemory wordSize + objectMemory baseHeaderSize + r: objReg. + to avoid calling the store check unless the receiver is forwarded. */ + assert((reg != scratch) + && (objReg != scratch)); + loop = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + imm = genJumpImmediate(reg); + + /* notionally + self genGetClassIndexOfNonImm: reg into: scratch. + cogit CmpCq: objectMemory isForwardedObjectClassIndexPun R: TempReg. + but the following is an instruction shorter: */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, reg, scratch); + quickConstant = (classIndexMask()) - (isForwardedObjectClassIndexPun()); + + /* begin AndCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, scratch); + ok = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genLoadSlotsourceRegdestReg(0, reg, reg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, reg, (index * BytesPerWord) + BaseHeaderSize, objReg); + + /* Check that we're meeting the contract of ceStoreCheckContextReceiverTrampoline. */ + assert((reg == Arg0Reg) + && ((scratch == TempReg) + && (objReg == ReceiverResultReg))); + + /* If a frame has been built then (if a link register is in use) the link register has been saved. + If not it must be saved and restored around the call since it will be used to return from the method. */ + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreCheckContextReceiverTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + + /* Jump: */ + genoperand(Jump, ((sqInt)loop)); + jmpTarget(ok, jmpTarget(imm, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return 0; +} + + +/* Create a trampoline to answer the active context that will answer it if a + frame is already married, + and create it otherwise. Assume numArgs is in SendNumArgsReg and ClassReg + is free. + These include machine code implementations of + CoInterpreter>>#marryFrame:SP: + */ + + /* CogObjectRepresentationForSpur>>#genGetActiveContextLarge:inBlock: */ +static NoDbgRegParms sqInt +genGetActiveContextLargeinBlock(sqInt isLarge, sqInt isInBlock) +{ + AbstractInstruction *abstractInstruction; + int callerSavedRegsToBeSaved; + AbstractInstruction *continuation; + AbstractInstruction *exit; + usqLong header; + AbstractInstruction *jumpNeedScavenge; + AbstractInstruction *jumpSingle; + AbstractInstruction *lastInst; + AbstractInstruction *loopHead; + int mustSaveSPReg; + sqInt offset; + sqInt reg; + int registersToBePushed; + int slotSize; + sqInt spareCalleeSavedReg; + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, FoxMethod, FPReg, ClassReg); + + /* #TstCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, MFMethodFlagHasContextFlag, ClassReg); + + /* jump if flag bit not set */ + jumpSingle = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* since the flag bit was set, get the context in the receiver reg and return */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, FoxThisContext, FPReg, ReceiverResultReg); + + /* RetN: */ + genoperand(RetN, 0); + jmpTarget(jumpSingle, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* OK, it doesn't exist; instantiate and initialize it, mimicking CoInterpreter>>#marryFrame:SP: + set the hasContext flag; See CoInterpreter class>>initializeFrameIndices */ + + /* #OrCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(OrCqR, MFMethodFlagHasContextFlag, ClassReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ClassReg, FoxMethod, FPReg); + + /* now get the home CogMethod into ClassReg and save for post-instantiation. */ + switch (isInBlock) { + case InFullBlock: + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 3, ClassReg); + break; + case InVanillaBlock: + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 3, ClassReg); + + /* #MoveM16:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveM16rR, 0, ClassReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, ClassReg); + break; + case 0: + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 1, ClassReg); + break; + default: + error("Case not found and no otherwise clause"); + } + + /* -3 is -(hasContext+isBlock) flags + -1 is hasContext flag + instantiate the context... */ + slotSize = (isLarge + ? LargeContextSlots + : SmallContextSlots); + header = headerForSlotsformatclassIndex(slotSize, indexablePointersFormat(), ClassMethodContextCompactIndex); + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), ReceiverResultReg); + + /* begin genStoreHeader:intoNewInstance:using: */ + /* MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, header, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, 0, ReceiverResultReg); + offset = smallObjectBytesForSlots(slotSize); + + /* begin LoadEffectiveAddressMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(LoadEffectiveAddressMwrR, offset, ReceiverResultReg, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, freeStartAddress()); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, getScavengeThreshold(), TempReg); + jumpNeedScavenge = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* Now initialize the fields of the context. See CoInterpreter>>marryFrame:SP: + sender gets frame pointer as a SmallInteger */ + continuation = genoperandoperandoperand(LoadEffectiveAddressMwrR, 1, FPReg, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, BaseHeaderSize + (SenderIndex * BytesPerOop), ReceiverResultReg); + + /* pc gets frame caller as a SmallInteger */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, FoxSavedFP, FPReg, TempReg); + genSetSmallIntegerTagsIn(TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, BaseHeaderSize + (InstructionPointerIndex * BytesPerOop), ReceiverResultReg); + + /* Set the method field, freeing up ClassReg again, and frame's context field, */ + offset = offsetof(CogMethod, methodObject); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, ClassReg, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, BaseHeaderSize + (MethodIndex * BytesPerWord), ReceiverResultReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ReceiverResultReg, FoxThisContext, FPReg); + + /* Now compute stack pointer; this is stackPointer (- 1 for return pc if a CISC) - framePointer - wordSize (1 each for saved pc, method, context, receiver) + 1 (1-relative) + numArgs + TPR note - the code here is actually doing + context stackPointer := ((((fp - sp) / wordSize) - [3|4]) + num args) asSmallInteger */ + gSubRRR(SPReg, FPReg, TempReg); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, log2BytesPerWord(), TempReg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 4, TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, SendNumArgsReg, TempReg); + + /* TempReg := FPReg - SPReg */ + genConvertIntegerToSmallIntegerInReg(TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, BaseHeaderSize + (StackPointerIndex * BytesPerOop), ReceiverResultReg); + + /* Set closureOrNil to either the stacked receiver or nil */ + if (isInBlock > 0) { + /* #LoadEffectiveAddressMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(LoadEffectiveAddressMwrR, 2, SendNumArgsReg, TempReg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, TempReg, FPReg, TempReg); + } + else { + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, nilObject(), TempReg); + } + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, BaseHeaderSize + (ClosureIndex * BytesPerOop), ReceiverResultReg); + + /* Set the receiver */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, FoxMFReceiver, FPReg, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, BaseHeaderSize + (ReceiverIndex * BytesPerOop), ReceiverResultReg); + + /* Now copy the arguments. This is tricky because of the shortage of registers,. ClassReg ranges + from 1 to numArgs (SendNumArgsReg), and from ReceiverIndex + 1 to ReceiverIndex + numArgs. + 1 to: numArgs do: + [:i| + temp := longAt(FPReg + ((SendNumArgs - i + 2) * wordSize)). +2 for saved pc and savedfp + longAtput(FPReg + FoxMFReceiver + (i * wordSize), temp)] + TPR note: this is a prime candidate for passing off to the backend to do at least faintly optimal code */ + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 1, ClassReg); + + /* begin CmpR:R: */ + assert(!(0 /* (SendNumArgsReg = SPReg) */)); + loopHead = genoperandoperand(CmpRR, SendNumArgsReg, ClassReg); + exit = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SendNumArgsReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, ClassReg, TempReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 2, TempReg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, TempReg, FPReg, TempReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ReceiverIndex + (BaseHeaderSize / BytesPerWord), ClassReg); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, TempReg, ClassReg, ReceiverResultReg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, (ReceiverIndex + (BaseHeaderSize / BytesPerWord)) - 1, ClassReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)loopHead)); + + /* +2 for saved fp and saved pc + Now convert ClassReg from frame index to context index + convert back adding 1 ;-) */ + jmpTarget(exit, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* Now compute number of fields to initialize to nil; set ClassReg to the destination index in the new context... + destination index is index of first non-arg temp, hence add SendNumArgs and header words to ReceiverIndex + 1 */ + gAddCqRR((ReceiverIndex + 1) + (BaseHeaderSize / BytesPerWord), SendNumArgsReg, ClassReg); + gSubRRR(SPReg, FPReg, SendNumArgsReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, nilObject(), TempReg); + + /* ...and loop filling the context's temp slots with nils. N.B. if CISC there is a retpc on the stack */ + loopHead = genoperandoperand(SubCqR, BytesPerWord, SendNumArgsReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, FoxSavedFP - FoxMFReceiver, SendNumArgsReg); + exit = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, TempReg, ClassReg, ReceiverResultReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, ClassReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)loopHead)); + jmpTarget(exit, genoperand(RetN, 0)); + jmpTarget(jumpNeedScavenge, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin saveRegistersInMask:around: */ + spareCalleeSavedReg = 0; + callerSavedRegsToBeSaved = CallerSavedRegisterMask & (((1U << ReceiverResultReg) | (1U << SendNumArgsReg)) | (1U << ClassReg)); + registersToBePushed = callerSavedRegsToBeSaved; + reg = 0; + while (registersToBePushed != 0) { + if (((registersToBePushed & 1) != 0)) { + /* PushR: */ + genoperand(PushR, reg); + } + reg += 1; + registersToBePushed = ((sqInt)(registersToBePushed)) >> 1; + } + + /* If the backEnd doesn't use the native SP register in generated code, and the non-native + SP is not callee-saved, then it too must be saved and restored around the call. */ + mustSaveSPReg = 0 /* (usesNativeSPReg not) */; + if (mustSaveSPReg) { + spareCalleeSavedReg = availableRegisterOrNoneIn(((ABICalleeSavedRegisterMask | ((1U << SPReg))) - ((1U << SPReg)))); + assert(!((spareCalleeSavedReg == NoReg))); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SPReg, spareCalleeSavedReg); + } + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceScheduleScavengeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + lastInst = abstractInstruction; + if (mustSaveSPReg) { + lastInst = genoperandoperand(MoveRR, spareCalleeSavedReg, SPReg); + } + while (reg > 0) { + reg -= 1; + if (((callerSavedRegsToBeSaved & (1ULL << reg)) != 0)) { + lastInst = genoperand(PopR, reg); + } + } + + /* Jump: */ + genoperand(Jump, ((sqInt)continuation)); + return 0; +} + + +/* Get the active context into ReceiverResultReg, creating it if necessary. */ + + /* CogObjectRepresentationForSpur>>#genGetActiveContextNumArgs:large:inBlock: */ +static NoDbgRegParms sqInt +genGetActiveContextNumArgslargeinBlock(sqInt numArgs, sqInt isLargeContext, sqInt isInBlock) +{ + AbstractInstruction *abstractInstruction; + sqInt routine; + + if (isLargeContext) { + switch (isInBlock) { + case 0: + routine = ceLargeActiveContextInMethodTrampoline; + break; + case InVanillaBlock: + routine = ceLargeActiveContextInBlockTrampoline; + break; + case InFullBlock: + routine = ceLargeActiveContextInFullBlockTrampoline; + break; + default: + error("Case not found and no otherwise clause"); + } + } + else { + switch (isInBlock) { + case 0: + routine = ceSmallActiveContextInMethodTrampoline; + break; + case InVanillaBlock: + routine = ceSmallActiveContextInBlockTrampoline; + break; + case InFullBlock: + routine = ceSmallActiveContextInFullBlockTrampoline; + break; + default: + error("Case not found and no otherwise clause"); + } + } + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, numArgs, SendNumArgsReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, routine); + (abstractInstruction->annotation = IsRelativeCall); + return 0; +} + + /* CogObjectRepresentationForSpur>>#genGetBits:ofFormatByteOf:into: */ +static NoDbgRegParms sqInt +genGetBitsofFormatByteOfinto(sqInt mask, sqInt sourceReg, sqInt destReg) +{ + /* #MoveMb:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMbrR, 3, sourceReg, destReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, mask, destReg); + return 0; +} + + +/* Fetch the instance's class index into destReg. */ + + /* CogObjectRepresentationForSpur>>#genGetClassIndexOfNonImm:into: */ +static NoDbgRegParms sqInt +genGetClassIndexOfNonImminto(sqInt sourceReg, sqInt destReg) +{ + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, sourceReg, destReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, classIndexMask(), destReg); + return 0; +} + + +/* Fetch the class object whose index is in instReg into destReg. + It is non-obvious, but the Cogit assumes loading a class does not involve + a runtime call, so do not call classAtIndex: */ + + /* CogObjectRepresentationForSpur>>#genGetClassObjectOfClassIndex:into:scratchReg: */ +static NoDbgRegParms sqInt +genGetClassObjectOfClassIndexintoscratchReg(sqInt instReg, sqInt destReg, sqInt scratchReg) +{ + assert(instReg != destReg); + assert(instReg != scratchReg); + assert(destReg != scratchReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, instReg, scratchReg); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, classTableMajorIndexShift(), scratchReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), scratchReg); + assert(!(shouldAnnotateObjectReference(classTableRootObj()))); + if (isWithinMwOffsetRange(backEnd, (classTableRootObj()) + BaseHeaderSize)) { + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, (classTableRootObj()) + BaseHeaderSize, scratchReg, destReg); + } + else { + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, classTableRootObj(), scratchReg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BaseHeaderSize, scratchReg, destReg); + } + + /* MoveR:R: */ + genoperandoperand(MoveRR, instReg, scratchReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, classTableMinorIndexMask(), scratchReg); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), scratchReg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, scratchReg, destReg, destReg); + return 0; +} + + +/* Fetch the instance's class into destReg. If the instance is not the + receiver and is forwarded, follow forwarding. */ + + /* CogObjectRepresentationForSpur>>#genGetClassObjectOf:into:scratchReg:mayBeAForwarder: */ +static NoDbgRegParms sqInt +genGetClassObjectOfintoscratchRegmayBeAForwarder(sqInt instReg, sqInt destReg, sqInt scratchReg, sqInt mayBeForwarder) +{ + AbstractInstruction *jumpIsImm; + AbstractInstruction *jumpNotForwarded; + AbstractInstruction *loop; + + if ((instReg == destReg) + || ((instReg == scratchReg) + || (destReg == scratchReg))) { + return BadRegisterSet; + } + loop = genoperandoperand(MoveRR, instReg, scratchReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, tagMask(), scratchReg); + jumpIsImm = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Get least significant half of header word in destReg */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, instReg, scratchReg); + + /* mask off class index */ + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, classIndexMask(), scratchReg); + if (mayBeForwarder) { + /* if it is forwarded... */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, isForwardedObjectClassIndexPun(), scratchReg); + jumpNotForwarded = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* ...follow the forwarding pointer and loop to fetch its classIndex */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BaseHeaderSize, instReg, instReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)loop)); + jmpTarget(jumpNotForwarded, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } + jmpTarget(jumpIsImm, genoperandoperand(MoveRR, scratchReg, destReg)); + if (scratchReg) { + genGetClassObjectOfClassIndexintoscratchReg(destReg, scratchReg, TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, scratchReg, destReg); + } + else { + /* PushR: */ + genoperand(PushR, instReg); + genGetClassObjectOfClassIndexintoscratchReg(destReg, instReg, TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, instReg, destReg); + + /* PopR: */ + genoperand(PopR, instReg); + } + return 0; +} + + /* CogObjectRepresentationForSpur>>#genGetClassTagOf:into:scratchReg: */ +static NoDbgRegParms AbstractInstruction * +genGetClassTagOfintoscratchReg(sqInt instReg, sqInt destReg, sqInt scratchReg) +{ + return genGetInlineCacheClassTagFromintoforEntry(instReg, destReg, 1); +} + + +/* Fetch the instance's class index into destReg. */ + + /* CogObjectRepresentationForSpur>>#genGetCompactClassIndexNonImmOf:into: */ +static NoDbgRegParms sqInt +genGetCompactClassIndexNonImmOfinto(sqInt instReg, sqInt destReg) +{ + return genGetClassIndexOfNonImminto(instReg, destReg); +} + + /* CogObjectRepresentationForSpur>>#genGetDoubleValueOf:into: */ +static NoDbgRegParms sqInt +genGetDoubleValueOfinto(sqInt srcReg, sqInt destFPReg) +{ + /* #MoveM64:r:Rd: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveM64rRd, BaseHeaderSize, srcReg, destFPReg); + return 0; +} + + +/* Get the format field of the object in srcReg into destReg. + srcReg may equal destReg. */ + + /* CogObjectRepresentationForSpur>>#genGetFormatOf:into: */ +static NoDbgRegParms sqInt +genGetFormatOfinto(sqInt srcReg, sqInt destReg) +{ + return genGetBitsofFormatByteOfinto(formatMask(), srcReg, destReg); +} + + +/* Get the format of the object in sourceReg into destReg. If + scratchRegOrNone is not NoReg, load at least the least significant 32-bits + (64-bits in 64-bits) of the + header word, which contains the format, into scratchRegOrNone. */ + + /* CogObjectRepresentationForSpur>>#genGetFormatOf:into:leastSignificantHalfOfBaseHeaderIntoScratch: */ +static NoDbgRegParms sqInt +genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(sqInt sourceReg, sqInt destReg, sqInt scratchRegOrNone) +{ + if (scratchRegOrNone == NoReg) { + /* #MoveMb:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMbrR, 3, sourceReg, destReg); + } + else { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, sourceReg, scratchRegOrNone); + gLogicalShiftRightCqRR(formatShift(), scratchRegOrNone, destReg); + } + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, formatMask(), destReg); + return 0; +} + + +/* Get the size in word-sized slots of the object in srcReg into destReg. + srcReg may equal destReg. */ + + /* CogObjectRepresentationForSpur>>#genGetNumSlotsOf:into: */ +static NoDbgRegParms sqInt +genGetNumSlotsOfinto(sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *jmp; + + assert(srcReg != destReg); + genGetRawSlotSizeOfNonImminto(srcReg, destReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, numSlotsMask(), destReg); + jmp = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + genGetOverflowSlotsOfinto(srcReg, destReg); + jmpTarget(jmp, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + +/* The raw numSlots field is the most significant byte of the 64-bit header + word. MoveMbrR zero-extends. */ + + /* CogObjectRepresentationForSpur>>#genGetRawSlotSizeOfNonImm:into: */ +static NoDbgRegParms sqInt +genGetRawSlotSizeOfNonImminto(sqInt sourceReg, sqInt destReg) +{ + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, destReg); + + /* #MoveMb:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMbrR, 7, sourceReg, destReg); + return 0; +} + + /* CogObjectRepresentationForSpur>>#genJumpImmediate: */ +static NoDbgRegParms AbstractInstruction * +genJumpImmediate(sqInt aRegister) +{ + /* #TstCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, tagMask(), aRegister); + return genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +} + + /* CogObjectRepresentationForSpur>>#genJumpImmutable:scratchReg: */ +#if IMMUTABILITY +static NoDbgRegParms AbstractInstruction * +genJumpImmutablescratchReg(sqInt sourceReg, sqInt scratchReg) +{ + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, sourceReg, scratchReg); + + /* begin genJumpBaseHeaderImmutable: */ + /* TstCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, immutableBitMask(), scratchReg); + return genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +} +#endif /* IMMUTABILITY */ + + /* CogObjectRepresentationForSpur>>#genJumpMutable:scratchReg: */ +#if IMMUTABILITY +static NoDbgRegParms AbstractInstruction * +genJumpMutablescratchReg(sqInt sourceReg, sqInt scratchReg) +{ + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, sourceReg, scratchReg); + + /* begin genJumpBaseHeaderMutable: */ + /* TstCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, immutableBitMask(), scratchReg); + return genConditionalBranchoperand(JumpZero, ((sqInt)0)); +} +#endif /* IMMUTABILITY */ + + +/* Generate a call to code that allocates a new Array of size. + The Array should be initialized with nils iff initialize is true. + The size arg is passed in SendNumArgsReg, the result + must come back in ReceiverResultReg. */ + + /* CogObjectRepresentationForSpur>>#genNewArrayOfSize:initialized: */ +static NoDbgRegParms sqInt +genNewArrayOfSizeinitialized(sqInt size, sqInt initialize) +{ + AbstractInstruction *abstractInstruction; + usqLong header; + sqInt i; + sqInt offset; + AbstractInstruction *skip; + + assert(size < (numSlotsMask())); + header = headerForSlotsformatclassIndex(size, arrayFormat(), ClassArrayCompactIndex); + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), ReceiverResultReg); + + /* begin genStoreHeader:intoNewInstance:using: */ + /* MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, header, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, 0, ReceiverResultReg); + if (initialize + && (size > 0)) { + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, nilObject(), TempReg); + for (i = 0; i < size; i += 1) { + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, (i * BytesPerWord) + BaseHeaderSize, ReceiverResultReg); + } + } + offset = smallObjectBytesForSlots(size); + + /* begin LoadEffectiveAddressMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(LoadEffectiveAddressMwrR, offset, ReceiverResultReg, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, freeStartAddress()); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, getScavengeThreshold(), TempReg); + skip = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceScheduleScavengeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpTarget(skip, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + +/* Create a closure with the given startpc, numArgs and numCopied + within a context with ctxtNumArgs, large if isLargeCtxt that is in a + block if isInBlock. Do /not/ initialize the copied values. */ + + /* CogObjectRepresentationForSpur>>#genNoPopCreateClosureAt:numArgs:numCopied:contextNumArgs:large:inBlock: */ +static NoDbgRegParms sqInt +genNoPopCreateClosureAtnumArgsnumCopiedcontextNumArgslargeinBlock(sqInt bcpc, sqInt numArgs, sqInt numCopied, sqInt ctxtNumArgs, sqInt isLargeCtxt, sqInt isInBlock) +{ + AbstractInstruction *abstractInstruction; + usqInt byteSize; + usqLong header; + sqInt numSlots; + AbstractInstruction *skip; + + + /* First get thisContext into ReceiverResultRega and thence in ClassReg. */ + genGetActiveContextNumArgslargeinBlock(ctxtNumArgs, isLargeCtxt, isInBlock); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, ClassReg); + numSlots = ClosureFirstCopiedValueIndex + numCopied; + byteSize = smallObjectBytesForSlots(numSlots); + header = headerForSlotsformatclassIndex(numSlots, indexablePointersFormat(), ClassBlockClosureCompactIndex); + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), ReceiverResultReg); + + /* begin genStoreHeader:intoNewInstance:using: */ + /* MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, header, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, 0, ReceiverResultReg); + + /* #LoadEffectiveAddressMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(LoadEffectiveAddressMwrR, byteSize, ReceiverResultReg, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, freeStartAddress()); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, getScavengeThreshold(), TempReg); + skip = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceScheduleScavengeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpTarget(skip, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ClassReg, (ClosureOuterContextIndex * BytesPerOop) + BaseHeaderSize, ReceiverResultReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, (((usqInt)bcpc << 3) | 1), TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, (ClosureStartPCIndex * BytesPerOop) + BaseHeaderSize, ReceiverResultReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, (((usqInt)numArgs << 3) | 1), TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, (ClosureNumArgsIndex * BytesPerOop) + BaseHeaderSize, ReceiverResultReg); + return 0; +} + + /* CogObjectRepresentationForSpur>>#genPrimitiveAsCharacter */ +static sqInt +genPrimitiveAsCharacter(void) +{ + AbstractInstruction *jumpNotInt; + AbstractInstruction *jumpOutOfRange; + sqInt reg; + + jumpNotInt = ((AbstractInstruction *) 0); + if (methodOrBlockNumArgs) { + if (methodOrBlockNumArgs > 1) { + return UnimplementedPrimitive; + } + reg = Arg0Reg; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpNotInt = genJumpNotSmallInteger(reg); + } + else { + reg = ReceiverResultReg; + } + + /* MoveR:R: */ + genoperandoperand(MoveRR, reg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + jumpOutOfRange = jumpNotCharacterUnsignedValueInRegister(TempReg); + genConvertSmallIntegerToCharacterInReg(reg); + if (reg != ReceiverResultReg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, reg, ReceiverResultReg); + } + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpOutOfRange, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + if (reg != ReceiverResultReg) { + jmpTarget(jumpNotInt, ((AbstractInstruction *) (((jumpOutOfRange->operands))[0]))); + } + return CompletePrimitive; +} + + /* CogObjectRepresentationForSpur>>#genPrimitiveAsFloat */ +static sqInt +genPrimitiveAsFloat(void) +{ + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* ConvertR:Rd: */ + genoperandoperand(ConvertRRd, TempReg, DPFPReg0); + genInstantiateFloatValueintoscratchRegscratchRegmayFail(DPFPReg0, ReceiverResultReg, ClassReg, TempReg, 0); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + return UnfailingPrimitive; +} + + +/* Generate primitive 60, at: with unsigned access for pure bits classes. */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveAt */ +static sqInt +genPrimitiveAt(void) +{ + return genPrimitiveAtSigned(0); +} + + +/* Generate primitive 61, at:put: with unsigned access for pure bits classes. */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveAtPut */ +static sqInt +genPrimitiveAtPut(void) +{ + return genPrimitiveAtPutSigned(0); +} + + /* CogObjectRepresentationForSpur>>#genPrimitiveFloatSquareRoot */ +static sqInt +genPrimitiveFloatSquareRoot(void) +{ + genGetDoubleValueOfinto(ReceiverResultReg, DPFPReg0); + + /* SqrtRd: */ + genoperand(SqrtRd, DPFPReg0); + genInstantiateFloatValueintoscratchRegscratchRegmayFail(DPFPReg0, ReceiverResultReg, ClassReg, TempReg, 0); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + return UnfailingPrimitive; +} + + /* CogObjectRepresentationForSpur>>#genPrimitiveIdenticalOrNotIf: */ +static NoDbgRegParms sqInt +genPrimitiveIdenticalOrNotIf(sqInt orNot) +{ + AbstractInstruction *comp; + AbstractInstruction *jumpCmp; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + assert(!(0 /* (Arg0Reg = SPReg) */)); + comp = genoperandoperand(CmpRR, Arg0Reg, ReceiverResultReg); + if (orNot) { + jumpCmp = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* genEnsureOopInRegNotForwarded:scratchReg:jumpBackTo: */ + genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(Arg0Reg, TempReg, comp, 0); + } + else { + jumpCmp = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + } + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, trueObject(), ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpCmp, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + if (!orNot) { + /* genEnsureOopInRegNotForwarded:scratchReg:jumpBackTo: */ + genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(Arg0Reg, TempReg, comp, 0); + } + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + return UnfailingPrimitive; +} + + +/* Generate primitive 164, at: with signed access for pure bits classes. */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveIntegerAt */ +static sqInt +genPrimitiveIntegerAt(void) +{ + return genPrimitiveAtSigned(1); +} + + +/* Generate primitive 165, at:put: with signed access for pure bits classes. */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveIntegerAtPut */ +static sqInt +genPrimitiveIntegerAtPut(void) +{ + return genPrimitiveAtPutSigned(1); +} + + +/* */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveMakePoint */ +static sqInt +genPrimitiveMakePoint(void) +{ + sqInt allocSize; + AbstractInstruction *jumpFail; + usqLong newPointHeader; + sqInt resultReg; + sqInt scratchReg; + + resultReg = ClassReg; + scratchReg = SendNumArgsReg; + + /* */ + allocSize = BaseHeaderSize + (BytesPerWord * 2); + newPointHeader = headerForSlotsformatclassIndex(2, nonIndexablePointerFormat(), ClassPointCompactIndex); + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), resultReg); + + /* #LoadEffectiveAddressMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(LoadEffectiveAddressMwrR, allocSize, resultReg, scratchReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, getScavengeThreshold(), scratchReg); + jumpFail = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, scratchReg, freeStartAddress()); + + /* begin genStoreHeader:intoNewInstance:using: */ + /* MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, newPointHeader, scratchReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, scratchReg, 0, resultReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ReceiverResultReg, BaseHeaderSize, resultReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, Arg0Reg, BaseHeaderSize + BytesPerWord, resultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, resultReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpFail, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + /* CogObjectRepresentationForSpur>>#genPrimitiveObjectAt */ +static sqInt +genPrimitiveObjectAt(void) +{ + sqInt headerReg; + AbstractInstruction *jumpBadIndex; + AbstractInstruction *jumpBounds; + AbstractInstruction *jumpNotHeaderIndex; + sqInt quickConstant; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpBadIndex = genJumpNotSmallInteger(Arg0Reg); + genGetMethodHeaderOfintoscratch(ReceiverResultReg, (headerReg = Arg1Reg), TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, (((usqInt)1 << 3) | 1), Arg0Reg); + jumpNotHeaderIndex = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, headerReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotHeaderIndex, gAndCqR((((usqInt)(alternateHeaderNumLiteralsMask()) << 3) | 1), headerReg)); + quickConstant = ((((usqInt)1 << 3) | 1)) - (smallIntegerTag()); + + /* begin SubCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, quickConstant, Arg0Reg); + assert(!((headerReg == SPReg))); + genoperandoperand(CmpRR, headerReg, Arg0Reg); + jumpBounds = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + genConvertSmallIntegerToIntegerInReg(Arg0Reg); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), Arg0Reg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, Arg0Reg, ReceiverResultReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpBounds, gAddCqR(((((usqInt)1 << 3) | 1)) - (smallIntegerTag()), Arg0Reg)); + jmpTarget(jumpBadIndex, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + +/* c.f. StackInterpreter>>stSizeOf: lengthOf:baseHeader:format: + fixedFieldsOf:format:length: + */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveSize */ +static sqInt +genPrimitiveSize(void) +{ + AbstractInstruction *jump32BitLongsDone; + AbstractInstruction *jump64BitLongsDone; + AbstractInstruction *jumpArrayDone; + AbstractInstruction *jumpBytesDone; + AbstractInstruction *jumpHasFixedFields; + AbstractInstruction *jumpImm; + AbstractInstruction *jumpIs32BitLongs; + AbstractInstruction *jumpIsBytes; + AbstractInstruction *jumpIsContext; + AbstractInstruction *jumpIsContext1; + AbstractInstruction *jumpIsShorts; + AbstractInstruction *jumpNotIndexable; + AbstractInstruction *jumpNotIndexable1; + AbstractInstruction *jumpShortsDone; + + jumpImm = genJumpImmediate(ReceiverResultReg); + + /* begin genGetSizeOf:into:formatReg:scratchReg:abortJumpsInto: */ + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, SendNumArgsReg, TempReg); + genGetNumSlotsOfinto(ReceiverResultReg, ClassReg); + + /* dispatch on format in a combination of highest dynamic frequency order first and convenience. + 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), SendNumArgsReg); + jumpIsBytes = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, arrayFormat(), SendNumArgsReg); + jumpArrayDone = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + jumpNotIndexable1 = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, weakArrayFormat(), SendNumArgsReg); + jumpHasFixedFields = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstShortFormat(), SendNumArgsReg); + jumpIsShorts = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstLongFormat(), SendNumArgsReg); + jumpIs32BitLongs = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, sixtyFourBitIndexableFormat(), SendNumArgsReg); + jump64BitLongsDone = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + jmpTarget(jumpNotIndexable1, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpNotIndexable1 = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpIsBytes, genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), ClassReg)); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, BytesPerWord - 1, SendNumArgsReg); + + /* SubR:R: */ + genoperandoperand(SubRR, SendNumArgsReg, ClassReg); + jumpBytesDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpIsShorts, gLogicalShiftLeftCqR((shiftForWord()) - 1, ClassReg)); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (((usqInt)(BytesPerWord)) >> 1) - 1, SendNumArgsReg); + + /* SubR:R: */ + genoperandoperand(SubRR, SendNumArgsReg, ClassReg); + jumpShortsDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpIs32BitLongs, gLogicalShiftLeftCqR((shiftForWord()) - 2, ClassReg)); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (((usqInt)(BytesPerWord)) >> 2) - 1, SendNumArgsReg); + + /* SubR:R: */ + genoperandoperand(SubRR, SendNumArgsReg, ClassReg); + jump32BitLongsDone = genoperand(Jump, ((sqInt)0)); + + /* formatReg contains fmt, now up for grabs. + destReg contains numSlots, precious. + sourceReg must be preserved */ + jmpTarget(jumpHasFixedFields, genoperandoperand(AndCqR, classIndexMask(), TempReg)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, SendNumArgsReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ClassMethodContextCompactIndex, TempReg); + jumpIsContext1 = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + genGetClassObjectOfClassIndexintoscratchReg(SendNumArgsReg, Extra0Reg, TempReg); + genLoadSlotsourceRegdestReg(InstanceSpecificationIndex, Extra0Reg, SendNumArgsReg); + genConvertSmallIntegerToIntegerInReg(SendNumArgsReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, fixedFieldsOfClassFormatMask(), SendNumArgsReg); + + /* SubR:R: */ + genoperandoperand(SubRR, SendNumArgsReg, ClassReg); + jmpTarget(jumpArrayDone, jmpTarget(jump64BitLongsDone, jmpTarget(jump32BitLongsDone, jmpTarget(jumpShortsDone, jmpTarget(jumpBytesDone, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))))); + jumpNotIndexable = jumpNotIndexable1; + jumpIsContext = jumpIsContext1; + genConvertIntegerInRegtoSmallIntegerInReg(ClassReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpImm, jmpTarget(jumpNotIndexable, jmpTarget(jumpIsContext, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + return CompletePrimitive; +} + + +/* primitiveCompareWith: */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveStringCompareWith */ +static sqInt +genPrimitiveStringCompareWith(void) +{ + AbstractInstruction *instr; + AbstractInstruction *jump; + AbstractInstruction *jumpAbove; + AbstractInstruction *jumpIncorrectFormat1; + AbstractInstruction *jumpIncorrectFormat2; + AbstractInstruction *jumpIncorrectFormat3; + AbstractInstruction *jumpIncorrectFormat4; + AbstractInstruction *jumpMidFailure; + AbstractInstruction *jumpSuccess; + sqInt minSizeReg; + sqInt string1CharOrByteSizeReg; + sqInt string1Reg; + sqInt string2CharOrByteSizeReg; + sqInt string2Reg; + + + /* I redefine those name to ease program comprehension */ + string1Reg = ReceiverResultReg; + string2Reg = Arg0Reg; + string1CharOrByteSizeReg = Arg1Reg; + string2CharOrByteSizeReg = ClassReg; + minSizeReg = SendNumArgsReg; + + /* Load arguments in reg */ + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* checks if string1 is a byteobject and get its size in bytes */ + genGetFormatOfinto(string1Reg, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), TempReg); + jumpIncorrectFormat1 = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), TempReg); + jumpIncorrectFormat2 = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + genGetNumSlotsOfinto(string1Reg, string1CharOrByteSizeReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), string1CharOrByteSizeReg); + gAndCqRR(BytesPerWord - 1, TempReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, string1CharOrByteSizeReg); + genGetFormatOfinto(string2Reg, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), TempReg); + jumpIncorrectFormat3 = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), TempReg); + jumpIncorrectFormat4 = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + genGetNumSlotsOfinto(string2Reg, string2CharOrByteSizeReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), string2CharOrByteSizeReg); + gAndCqRR(BytesPerWord - 1, TempReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, string2CharOrByteSizeReg); + + /* Type and number of arguments are correct + Compute the min */ + + /* begin CmpR:R: */ + assert(!((string1CharOrByteSizeReg == SPReg))); + genoperandoperand(CmpRR, string1CharOrByteSizeReg, string2CharOrByteSizeReg); + jumpAbove = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, string1CharOrByteSizeReg, minSizeReg); + jump = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpAbove, genoperandoperand(MoveRR, string2CharOrByteSizeReg, minSizeReg)); + jmpTarget(jump, genoperandoperand(CmpCqR, 0, minSizeReg)); + + /* if one of the string is empty, no need to go through the comparing loop */ + /* Compare the bytes */ + jumpSuccess = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, BaseHeaderSize, TempReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BaseHeaderSize, minSizeReg); + instr = genoperandoperandoperand(MoveXbrRR, TempReg, string1Reg, string1CharOrByteSizeReg); + + /* MoveXbr:R:R: */ + genoperandoperandoperand(MoveXbrRR, TempReg, string2Reg, string2CharOrByteSizeReg); + + /* SubR:R: */ + genoperandoperand(SubRR, string2CharOrByteSizeReg, string1CharOrByteSizeReg); + + /* the 2 compared characters are different, exit the loop */ + jumpMidFailure = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, TempReg); + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, minSizeReg); + + /* JumpNonZero: */ + genConditionalBranchoperand(JumpNonZero, ((sqInt)instr)); + + /* all bytes from 1 to minSize are equal */ + genGetNumBytesOfinto(string1Reg, string1CharOrByteSizeReg); + genGetNumBytesOfinto(string2Reg, string2CharOrByteSizeReg); + jmpTarget(jumpSuccess, genoperandoperand(SubRR, string2CharOrByteSizeReg, string1CharOrByteSizeReg)); + jmpTarget(jumpMidFailure, genoperandoperand(MoveRR, string1CharOrByteSizeReg, ReceiverResultReg)); + genConvertIntegerToSmallIntegerInReg(ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIncorrectFormat4, jmpTarget(jumpIncorrectFormat3, jmpTarget(jumpIncorrectFormat2, jmpTarget(jumpIncorrectFormat1, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))); + return CompletePrimitive; +} + + +/* replaceFrom: start to: stop with: replacement startingAt: repStart. + + The primitive in the JIT tries to deal with two pathological cases, copy + of arrays and byteStrings, + which often copies only a dozen of fields and where switching to the C + runtime cost a lot. + + Based on heuristics on the method class, I generate a quick array path + (typically for Array), + a quick byteString path (typically for ByteString, ByteArray and + LargeInteger) or no quick + path at all (Typically for Bitmap). + + The many tests to ensure that the primitive won't fail are not super + optimised (multiple reloading + or stack arguments in registers) but this is still good enough and worth + it since we're avoiding + the Smalltalk to C stack switch. The tight copying loops are optimised. + + It is possible to build a bigger version with the 2 different paths but I + (Clement) believe this + is too big machine code wise to be worth it. + */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveStringReplace */ +static sqInt +genPrimitiveStringReplace(void) +{ + sqInt adjust; + sqInt arrayReg; + AbstractInstruction *instr; + AbstractInstruction *jmpAlreadyRemembered; + AbstractInstruction *jmpDestYoung; + AbstractInstruction *jumpEmpty; + AbstractInstruction *jumpImm; + AbstractInstruction *jumpImmutable; + AbstractInstruction *jumpIncorrectFormat1; + AbstractInstruction *jumpIncorrectFormat2; + AbstractInstruction *jumpIncorrectFormat3; + AbstractInstruction *jumpIncorrectFormat4; + AbstractInstruction *jumpNotSmi1; + AbstractInstruction *jumpNotSmi2; + AbstractInstruction *jumpNotSmi3; + AbstractInstruction *jumpOutOfBounds1; + AbstractInstruction *jumpOutOfBounds2; + AbstractInstruction *jumpOutOfBounds3; + AbstractInstruction *jumpOutOfBounds4; + sqInt offset; + sqInt replReg; + sqInt repStartReg; + sqInt result; + sqInt startReg; + sqInt stopReg; + + + /* Can I generate a quick path for this method ? */ + jumpOutOfBounds3 = ((AbstractInstruction *) 0); + jumpOutOfBounds4 = ((AbstractInstruction *) 0); + if (!((maybeMethodClassOfseemsToBeInstantiating(methodObj, arrayFormat())) + || (maybeMethodClassOfseemsToBeInstantiating(methodObj, firstByteFormat())))) { + return UnimplementedPrimitive; + } + + /* I redefine those name to ease program comprehension */ + arrayReg = ReceiverResultReg; + startReg = Arg0Reg; + stopReg = Arg1Reg; + replReg = ClassReg; + repStartReg = SendNumArgsReg; + + /* Load arguments in reg */ + + /* begin genStackArgAt:into: */ + offset = (1) * BytesPerWord; + genoperandoperandoperand(MoveMwrR, offset, SPReg, repStartReg); + + /* begin genStackArgAt:into: */ + offset = (2) * BytesPerWord; + genoperandoperandoperand(MoveMwrR, offset, SPReg, replReg); + + /* begin genStackArgAt:into: */ + offset = (3) * BytesPerWord; + genoperandoperandoperand(MoveMwrR, offset, SPReg, stopReg); + + /* begin genStackArgAt:into: */ + offset = (4) * BytesPerWord; + genoperandoperandoperand(MoveMwrR, offset, SPReg, startReg); + + /* start,stop,repStart Smis or fail the primitive */ + jumpNotSmi1 = genJumpNotSmallInteger(repStartReg); + jumpNotSmi2 = genJumpNotSmallInteger(stopReg); + jumpNotSmi3 = genJumpNotSmallInteger(startReg); + + /* repl non immediate or fail the primitive */ + jumpImm = genJumpImmediate(replReg); + + /* if start>stop primitive success */ + + /* begin CmpR:R: */ + assert(!((startReg == SPReg))); + genoperandoperand(CmpRR, startReg, stopReg); + jumpEmpty = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* If receiver immutable fail the primitive */ +# if IMMUTABILITY + jumpImmutable = genJumpImmutablescratchReg(ReceiverResultReg, TempReg); +# endif + + + /* 0 >= start, fail */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, (((usqInt)0 << 3) | 1), startReg); + jumpOutOfBounds1 = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + + /* 0 >= replStart, fail */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, (((usqInt)0 << 3) | 1), repStartReg); + jumpOutOfBounds2 = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + + /* --- Pointer object version --- */ + if (maybeMethodClassOfseemsToBeInstantiating(methodObj, arrayFormat())) { + /* Are they both array format ? */ + genGetFormatOfinto(arrayReg, TempReg); + genGetFormatOfinto(replReg, startReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, arrayFormat(), startReg); + jumpIncorrectFormat1 = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, arrayFormat(), TempReg); + jumpIncorrectFormat2 = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Both objects are arrays, */ + genGetNumSlotsOfinto(arrayReg, TempReg); + genConvertSmallIntegerToIntegerInReg(stopReg); + + /* arr size < stop */ + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, stopReg); + jumpOutOfBounds3 = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + + /* rep size < repStart - start + stop */ + genGetNumSlotsOfinto(replReg, TempReg); + + /* begin genStackArgAt:into: */ + offset = (4) * BytesPerWord; + genoperandoperandoperand(MoveMwrR, offset, SPReg, startReg); + genConvertSmallIntegerToIntegerInReg(startReg); + genConvertSmallIntegerToIntegerInReg(repStartReg); + + /* SubR:R: */ + genoperandoperand(SubRR, startReg, stopReg); + + /* AddR:R: */ + genoperandoperand(AddRR, repStartReg, stopReg); + + /* stopReg: stop - start + repStart */ + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, stopReg); + jumpOutOfBounds4 = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + + /* Everything in bounds + PossibleRemembered object */ + + /* #MoveCw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveCwR, storeCheckBoundary(), TempReg); + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, arrayReg); + jmpDestYoung = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + jmpAlreadyRemembered = genCheckRememberedBitOfscratch(arrayReg, TempReg); + + /* saveAndRestoreLinkRegAround: */ + callStoreCheckTrampoline(); + jmpTarget(jmpDestYoung, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jmpTarget(jmpAlreadyRemembered, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* Copy the array */ + + /* begin genStackArgAt:into: */ + offset = (3) * BytesPerWord; + genoperandoperandoperand(MoveMwrR, offset, SPReg, stopReg); + genConvertSmallIntegerToIntegerInReg(stopReg); + + /* Shift replReg to read it with startReg offset */ + + /* SubR:R: */ + genoperandoperand(SubRR, startReg, repStartReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), repStartReg); + + /* AddR:R: */ + genoperandoperand(AddRR, repStartReg, replReg); + adjust = (((usqInt)(BaseHeaderSize)) >> (shiftForWord())) - 1; + if (adjust) { + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, adjust, startReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, adjust, stopReg); + } + + /* Tight copying loop */ + instr = genoperandoperandoperand(MoveXwrRR, startReg, replReg, TempReg); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, TempReg, startReg, arrayReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, startReg); + + /* begin CmpR:R: */ + assert(!((startReg == SPReg))); + genoperandoperand(CmpRR, startReg, stopReg); + + /* JumpAboveOrEqual: */ + genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)instr)); + jmpTarget(jumpEmpty, /* genPrimReturn */ + (methodOrBlockNumArgs <= (numRegArgs()) + ? genoperand(RetN, 0) + : genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord))); + + /* CANNOT REACH by falling though */ + jmpTarget(jumpIncorrectFormat1, jmpTarget(jumpIncorrectFormat2, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + } + + /* --- Byte object version --- */ + if (maybeMethodClassOfseemsToBeInstantiating(methodObj, firstByteFormat())) { + /* Are they both byte array format ? CompiledMethod excluded */ + genGetFormatOfinto(arrayReg, TempReg); + genGetFormatOfinto(replReg, repStartReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), repStartReg); + jumpIncorrectFormat1 = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), repStartReg); + jumpIncorrectFormat2 = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), TempReg); + jumpIncorrectFormat3 = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), TempReg); + jumpIncorrectFormat4 = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* Both objects are byte arrays */ + genGetNumSlotsOfinto(arrayReg, startReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), startReg); + gAndCqRR(BytesPerWord - 1, TempReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, startReg); + genConvertSmallIntegerToIntegerInReg(stopReg); + + /* arr size < stop */ + + /* begin CmpR:R: */ + assert(!((startReg == SPReg))); + genoperandoperand(CmpRR, startReg, stopReg); + jumpOutOfBounds3 = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + + /* rep size < repStart - start + stop */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, repStartReg, TempReg); + + /* begin genStackArgAt:into: */ + offset = (1) * BytesPerWord; + genoperandoperandoperand(MoveMwrR, offset, SPReg, repStartReg); + + /* begin genStackArgAt:into: */ + offset = (4) * BytesPerWord; + genoperandoperandoperand(MoveMwrR, offset, SPReg, startReg); + genConvertSmallIntegerToIntegerInReg(startReg); + genConvertSmallIntegerToIntegerInReg(repStartReg); + + /* SubR:R: */ + genoperandoperand(SubRR, startReg, stopReg); + + /* AddR:R: */ + genoperandoperand(AddRR, repStartReg, stopReg); + genGetNumSlotsOfinto(replReg, startReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), startReg); + gAndCqRR(BytesPerWord - 1, TempReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, startReg); + + /* stopReg: stop - start + repStart */ + + /* begin CmpR:R: */ + assert(!((startReg == SPReg))); + genoperandoperand(CmpRR, startReg, stopReg); + jumpOutOfBounds4 = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + + /* Everything in bounds + Copy the bytes */ + + /* begin genStackArgAt:into: */ + offset = (4) * BytesPerWord; + genoperandoperandoperand(MoveMwrR, offset, SPReg, startReg); + genConvertSmallIntegerToIntegerInReg(startReg); + + /* begin genStackArgAt:into: */ + offset = (3) * BytesPerWord; + genoperandoperandoperand(MoveMwrR, offset, SPReg, stopReg); + genConvertSmallIntegerToIntegerInReg(stopReg); + + /* Shift replReg to read it with startReg offset */ + + /* SubR:R: */ + genoperandoperand(SubRR, startReg, repStartReg); + + /* AddR:R: */ + genoperandoperand(AddRR, repStartReg, replReg); + adjust = BaseHeaderSize - 1; + if (adjust) { + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, adjust, startReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, adjust, stopReg); + } + + /* Tight copying loop */ + instr = genoperandoperandoperand(MoveXbrRR, startReg, replReg, TempReg); + + /* MoveR:Xbr:R: */ + genoperandoperandoperand(MoveRXbrR, TempReg, startReg, arrayReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, startReg); + + /* begin CmpR:R: */ + assert(!((startReg == SPReg))); + genoperandoperand(CmpRR, startReg, stopReg); + + /* JumpAboveOrEqual: */ + genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)instr)); + jmpTarget(jumpEmpty, /* genPrimReturn */ + (methodOrBlockNumArgs <= (numRegArgs()) + ? genoperand(RetN, 0) + : genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord))); + + /* CANNOT REACH by falling though */ + jmpTarget(jumpIncorrectFormat4, jmpTarget(jumpIncorrectFormat3, jmpTarget(jumpIncorrectFormat2, jmpTarget(jumpIncorrectFormat1, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))); + } + if (((result = compileInterpreterPrimitive())) < 0) { + return result; + } + jmpTarget(jumpImm, jmpTarget(jumpNotSmi1, jmpTarget(jumpNotSmi2, jmpTarget(jumpNotSmi3, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))); + jmpTarget(jumpOutOfBounds1, jmpTarget(jumpOutOfBounds2, jmpTarget(jumpOutOfBounds3, jmpTarget(jumpOutOfBounds4, ((AbstractInstruction *) (((jumpImm->operands))[0])))))); +# if IMMUTABILITY + jmpTarget(jumpImmutable, ((AbstractInstruction *) (((jumpImm->operands))[0]))); +# endif + + return CompletePrimitive; +} + + /* CogObjectRepresentationForSpur>>#genSetSmallIntegerTagsIn: */ +static NoDbgRegParms sqInt +genSetSmallIntegerTagsIn(sqInt scratchReg) +{ + /* #OrCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(OrCqR, 1, scratchReg); + return 0; +} + + +/* Create a trampoline to store-check the update of the receiver in a + closure's outerContext in compileBlockFrameBuild:. */ + + /* CogObjectRepresentationForSpur>>#genStoreCheckContextReceiverTrampoline */ +static usqInt +genStoreCheckContextReceiverTrampoline(void) +{ + usqInt startAddress; + + startAddress = methodZoneBase; + zeroOpcodeIndex(); + genStoreCheckReceiverRegvalueRegscratchReginFrame(ReceiverResultReg, Arg0Reg, TempReg, 0); + + /* RetN: */ + genoperand(RetN, 0); + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress("ceStoreCheckContextReceiver", startAddress); + recordRunTimeObjectReferences(); + return startAddress; +} + + +/* Generate the code for a store check of valueReg into destReg. */ + + /* CogObjectRepresentationForSpur>>#genStoreCheckReceiverReg:valueReg:scratchReg:inFrame: */ +static NoDbgRegParms sqInt +genStoreCheckReceiverRegvalueRegscratchReginFrame(sqInt destReg, sqInt valueReg, sqInt scratchReg, sqInt inFrame) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *jmpAlreadyRemembered; + AbstractInstruction *jmpDestYoung; + AbstractInstruction *jmpImmediate; + AbstractInstruction *jmpSourceOld; + + + /* Is value stored an immediate? If so we're done */ + jmpAlreadyRemembered = ((AbstractInstruction *) 0); + jmpImmediate = genJumpImmediate(valueReg); + + /* Get the old/new boundary in scratchReg */ + + /* #MoveCw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveCwR, storeCheckBoundary(), scratchReg); + + /* Is target young? If so we're done */ + + /* begin CmpR:R: */ + assert(!((scratchReg == SPReg))); + genoperandoperand(CmpRR, scratchReg, destReg); + jmpDestYoung = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* Is value stored old? If so we're done. */ + + /* begin CmpR:R: */ + assert(!((scratchReg == SPReg))); + genoperandoperand(CmpRR, scratchReg, valueReg); + jmpSourceOld = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* value is young and target is old. + Need to remember this only if the remembered bit is not already set. */ + if (!CheckRememberedInTrampoline) { + jmpAlreadyRemembered = genCheckRememberedBitOfscratch(destReg, scratchReg); + } + + /* Remembered bit is not set. Call store check to insert dest into remembered table. */ + assert(destReg == ReceiverResultReg); + + /* begin evaluateTrampolineCallBlock:protectLinkRegIfNot: */ + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreCheckTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpTarget(jmpImmediate, jmpTarget(jmpDestYoung, jmpTarget(jmpSourceOld, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + if (!CheckRememberedInTrampoline) { + jmpTarget(jmpAlreadyRemembered, ((AbstractInstruction *) (((jmpSourceOld->operands))[0]))); + } + return 0; +} + + /* CogObjectRepresentationForSpur>>#genStoreSourceReg:slotIndex:destReg:scratchReg:inFrame:needsStoreCheck: */ +static NoDbgRegParms sqInt +genStoreSourceRegslotIndexdestRegscratchReginFrameneedsStoreCheck(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt inFrame, sqInt needsStoreCheck) +{ + AbstractInstruction *abstractInstruction; + + /* begin genTraceStores */ + if (traceStores > 0) { + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, TempReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceTraceStoreTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + } + + /* do the store */ + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, sourceReg, (index * BytesPerWord) + BaseHeaderSize, destReg); + + /* now the check. needStoreCheck is false if the JIT has figured out that the value stored does not need the check (immediate, nil, true, false) */ + if (needsStoreCheck) { + return genStoreCheckReceiverRegvalueRegscratchReginFrame(destReg, sourceReg, scratchReg, inFrame); + } + return 0; +} + + +/* This method is used for unchecked stores in objects after their creation + (typically, inlined creation of Array, closures and some temp vectors). + Currently there is no need to do the immutability check here + */ + + /* CogObjectRepresentationForSpur>>#genStoreSourceReg:slotIndex:intoNewObjectInDestReg: */ +static NoDbgRegParms sqInt +genStoreSourceRegslotIndexintoNewObjectInDestReg(sqInt sourceReg, sqInt index, sqInt destReg) +{ + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, sourceReg, (index * BytesPerWord) + BaseHeaderSize, destReg); + return 0; +} + + +/* Convention: + - RcvrResultReg holds the object mutated. + If immutability failure: + - TempReg holds the instance variable index mutated + if instVarIndex > numDedicatedStoreTrampoline + - ClassReg holds the value to store + Registers are not lived across this trampoline as the + immutability failure may need new stack frames. */ + + /* CogObjectRepresentationForSpur>>#genStoreTrampolineCalled:instVarIndex: */ +#if IMMUTABILITY +static NoDbgRegParms usqInt +genStoreTrampolineCalledinstVarIndex(char *trampolineName, sqInt instVarIndex) +{ + AbstractInstruction *jumpImmutable; + AbstractInstruction *jumpRC; + sqInt pushLinkReg; + sqInt quickConstant; + usqInt startAddress; + + startAddress = methodZoneBase; + zeroOpcodeIndex(); + if (CheckRememberedInTrampoline) { + /* begin genStoreTrampolineCheckingRememberedCalled:instVarIndex: */ + jumpImmutable = genJumpImmutablescratchReg(ReceiverResultReg, SendNumArgsReg); + + /* Store check + If on 64-bits and doing the remembered bit test here, we can combine the tests to fetch the header once. */ + quickConstant = 1U << (rememberedBitShift()); + + /* begin TstCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, quickConstant, SendNumArgsReg); + jumpRC = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* RetN: */ + genoperand(RetN, 0); + jmpTarget(jumpRC, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + compileTrampolineFornumArgsargargargargregsToSavepushLinkRegresultReg(remember, 1, ReceiverResultReg, null, null, null, 0 /* emptyRegisterMask */, 1, /* returnRegForStoreCheck */ + (((CallerSavedRegisterMask & ((1U << ReceiverResultReg))) != 0) + ? ReceiverResultReg + : ABIResultReg)); + jmpTarget(jumpImmutable, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + compileTrampolineFornumArgsargargargargregsToSavepushLinkRegresultReg(ceCannotAssignTowithIndexvalueToAssign, 3, ReceiverResultReg, (instVarIndex < (NumStoreTrampolines - 1) + ? (/* begin trampolineArgConstant: */ + assert(instVarIndex >= 0), + -2 - instVarIndex) + : TempReg), ClassReg, null, 0 /* emptyRegisterMask */, 1, NoReg); + } + else { + /* begin genStoreTrampolineNotCheckingRememberedCalled:instVarIndex: */ + genSmalltalkToCStackSwitch((pushLinkReg = 0 /* hasLinkRegister */)); + + /* SendNumArgsReg is mutated but we don't care as registers are not live across the trampoline. + There is no reason why registers cannot be saved over the remember: call, but since the + immutability check is a suspension point, registers cannot remain live. */ + jumpImmutable = genJumpImmutablescratchReg(ReceiverResultReg, SendNumArgsReg); + + /* Store check */ + compileCallFornumArgsargargargargresultRegregsToSave(remember, 1, ReceiverResultReg, null, null, null, /* returnRegForStoreCheck */ + (((CallerSavedRegisterMask & ((1U << ReceiverResultReg))) != 0) + ? ReceiverResultReg + : ABIResultReg), 0 /* emptyRegisterMask */); + genLoadStackPointers(backEnd); + genTrampolineReturn(pushLinkReg); + jmpTarget(jumpImmutable, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + compileCallFornumArgsargargargargresultRegregsToSave(ceCannotAssignTowithIndexvalueToAssign, 3, ReceiverResultReg, (instVarIndex < (NumStoreTrampolines - 1) + ? (/* begin trampolineArgConstant: */ + assert(instVarIndex >= 0), + -2 - instVarIndex) + : TempReg), ClassReg, null, NoReg, 0 /* emptyRegisterMask */); + genLoadStackPointers(backEnd); + genTrampolineReturn(pushLinkReg); + } + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress(trampolineName, startAddress); + recordRunTimeObjectReferences(); + return startAddress; +} +#endif /* IMMUTABILITY */ + + +/* Store check code is duplicated to use a single trampoline */ + + /* CogObjectRepresentationForSpur>>#genStoreWithImmutabilityAndStoreCheckSourceReg:slotIndex:destReg:scratchReg:needRestoreRcvr: */ +#if IMMUTABILITY +static NoDbgRegParms sqInt +genStoreWithImmutabilityAndStoreCheckSourceRegslotIndexdestRegscratchRegneedRestoreRcvr(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt needRestoreRcvr) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *immutableJump; + AbstractInstruction *jmpAlreadyRemembered; + AbstractInstruction *jmpDestYoung; + AbstractInstruction *jmpImmediate; + AbstractInstruction *jmpSourceOld; + + jmpAlreadyRemembered = ((AbstractInstruction *) 0); + immutableJump = genJumpImmutablescratchReg(destReg, scratchReg); + + /* begin genTraceStores */ + if (traceStores > 0) { + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, TempReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceTraceStoreTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + } + + /* do the store */ + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, sourceReg, (index * BytesPerWord) + BaseHeaderSize, destReg); + + /* store check */ + jmpImmediate = genJumpImmediate(sourceReg); + + /* Get the old/new boundary in scratchReg */ + + /* #MoveCw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveCwR, storeCheckBoundary(), scratchReg); + + /* Is target young? If so we're done */ + + /* begin CmpR:R: */ + assert(!((scratchReg == SPReg))); + genoperandoperand(CmpRR, scratchReg, destReg); + jmpDestYoung = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* Is value stored old? If so we're done. */ + + /* begin CmpR:R: */ + assert(!((scratchReg == SPReg))); + genoperandoperand(CmpRR, scratchReg, sourceReg); + jmpSourceOld = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* value is young and target is old. + Need to remember this only if the remembered bit is not already set. */ + if (!CheckRememberedInTrampoline) { + jmpAlreadyRemembered = genCheckRememberedBitOfscratch(destReg, scratchReg); + } + + /* Set the inst var index for the benefit of the immutability check. The trampoline will + repeat the check to choose between the immutability violation and the store check. */ + jmpTarget(immutableJump, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin genStoreTrampolineCall: */ + assert(IMMUTABILITY); + if (index >= (NumStoreTrampolines - 1)) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, index, TempReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreTrampolines[NumStoreTrampolines - 1]); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreTrampolines[index]); + (abstractInstruction->annotation = IsRelativeCall); + } + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + if (needRestoreRcvr) { + genWriteCResultIntoReg(backEnd, ReceiverResultReg); + } + else { + if (((CallerSavedRegisterMask & ((1U << ReceiverResultReg))) != 0)) { + /* begin voidReceiverOptStatus */ + ((simSelf())->liveRegister = NoReg); + } + } + jmpTarget(jmpImmediate, jmpTarget(jmpDestYoung, jmpTarget(jmpSourceOld, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + if (!CheckRememberedInTrampoline) { + jmpTarget(jmpAlreadyRemembered, ((AbstractInstruction *) (((jmpSourceOld->operands))[0]))); + } + return 0; +} +#endif /* IMMUTABILITY */ + + +/* Gen an immutability check with no store check (e.g. assigning an immediate + literal) + */ +/* imm check has its own trampoline */ + + /* CogObjectRepresentationForSpur>>#genStoreWithImmutabilityButNoStoreCheckSourceReg:slotIndex:destReg:scratchReg:needRestoreRcvr: */ +#if IMMUTABILITY +static NoDbgRegParms sqInt +genStoreWithImmutabilityButNoStoreCheckSourceRegslotIndexdestRegscratchRegneedRestoreRcvr(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt needRestoreRcvr) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *immutabilityFailure; + AbstractInstruction *mutableJump; + + mutableJump = genJumpMutablescratchReg(destReg, scratchReg); + + /* begin genStoreTrampolineCall: */ + assert(IMMUTABILITY); + if (index >= (NumStoreTrampolines - 1)) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, index, TempReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreTrampolines[NumStoreTrampolines - 1]); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreTrampolines[index]); + (abstractInstruction->annotation = IsRelativeCall); + } + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + + /* begin voidReceiverOptStatus */ + ((simSelf())->liveRegister = NoReg); + if (needRestoreRcvr) { + /* begin putSelfInReceiverResultReg */ + storeToReg(simSelf(), ReceiverResultReg); + } + immutabilityFailure = genoperand(Jump, ((sqInt)0)); + jmpTarget(mutableJump, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin genTraceStores */ + if (traceStores > 0) { + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, TempReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceTraceStoreTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + } + + /* do the store */ + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, sourceReg, (index * BytesPerWord) + BaseHeaderSize, destReg); + jmpTarget(immutabilityFailure, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} +#endif /* IMMUTABILITY */ + + +/* We know there is a frame as immutability check requires a frame */ +/* needRestoreRcvr has to be true to keep RcvrResultReg live with the + receiver in it across the trampoline + */ +/* Trampoline convention... */ + + /* CogObjectRepresentationForSpur>>#genStoreWithImmutabilityCheckSourceReg:slotIndex:destReg:scratchReg:needsStoreCheck:needRestoreRcvr: */ +#if IMMUTABILITY +static NoDbgRegParms sqInt +genStoreWithImmutabilityCheckSourceRegslotIndexdestRegscratchRegneedsStoreCheckneedRestoreRcvr(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt needsStoreCheck, sqInt needRestoreRcvr) +{ + assert(destReg == ReceiverResultReg); + assert(scratchReg == TempReg); + assert(sourceReg == ClassReg); + + /* The two paths are very different as the trampoline is shared in 1 case so we split here */ + if (needsStoreCheck) { + genStoreWithImmutabilityAndStoreCheckSourceRegslotIndexdestRegscratchRegneedRestoreRcvr(sourceReg, index, destReg, scratchReg, needRestoreRcvr); + } + else { + genStoreWithImmutabilityButNoStoreCheckSourceRegslotIndexdestRegscratchRegneedRestoreRcvr(sourceReg, index, destReg, scratchReg, needRestoreRcvr); + } + return 0; +} +#endif /* IMMUTABILITY */ + + +/* Make sure SendNumArgsReg and ClassReg are available in addition to + ReceiverResultReg and TempReg in + genGetActiveContextNumArgs:large:inBlock:. + */ + + /* CogObjectRepresentationForSpur>>#getActiveContextAllocatesInMachineCode */ +static sqInt +getActiveContextAllocatesInMachineCode(void) +{ + return 1; +} + + +/* Since all cache tags in Spur are class indices none of + them are young or have to be updated in a scavenge. */ + + /* CogObjectRepresentationForSpur>>#inlineCacheTagIsYoung: */ +static NoDbgRegParms sqInt +inlineCacheTagIsYoung(sqInt cacheTag) +{ + return 0; +} + + /* CogObjectRepresentationForSpur>>#jumpNotCharacterUnsignedValueInRegister: */ +static NoDbgRegParms AbstractInstruction * +jumpNotCharacterUnsignedValueInRegister(sqInt reg) +{ + sqInt quickConstant; + + quickConstant = (1U << (numCharacterBits())) - 1; + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, reg); + return genConditionalBranchoperand(JumpAbove, ((sqInt)0)); +} + + +/* Mark and trace a literal in a machine code instruction preceding address + in cogMethodOrNil. + Answer if code was modified. */ + + /* CogObjectRepresentationForSpur>>#markAndTraceLiteral:in:atpc: */ +static NoDbgRegParms sqInt +markAndTraceLiteralinatpc(sqInt literal, CogMethod *cogMethodOrNil, usqInt address) +{ + sqInt objOop; + + if (!(couldBeObject(literal))) { + return 0; + } + assert(addressCouldBeObj(literal)); + if (!(isForwarded(literal))) { + markAndTrace(literal); + return 0; + } + + /* begin setCodeModified */ + codeModified = 1; + objOop = followForwarded(literal); + storeLiteralbeforeFollowingAddress(backEnd, objOop, address); + markAndTraceUpdatedLiteralin(objOop, cogMethodOrNil); + return 1; +} + + +/* Mark and trace a literal in a sqInt variable of cogMethod. */ + + /* CogObjectRepresentationForSpur>>#markAndTraceLiteral:in:at: */ +static NoDbgRegParms void +markAndTraceLiteralinat(sqInt literal, CogMethod *cogMethod, sqInt *address) +{ + sqInt objOop; + + if (!(couldBeObject(literal))) { + return; + } + assert(addressCouldBeObj(literal)); + if (!(isForwarded(literal))) { + markAndTrace(literal); + return; + } + objOop = followForwarded(literal); + address[0] = objOop; + markAndTraceUpdatedLiteralin(objOop, cogMethod); +} + + +/* Common code to mark a literal in cogMethod and add + the cogMethod to youngReferrers if the literal is young. */ + + /* CogObjectRepresentationForSpur>>#markAndTraceUpdatedLiteral:in: */ +static NoDbgRegParms void +markAndTraceUpdatedLiteralin(sqInt objOop, CogMethod *cogMethodOrNil) +{ + if (isNonImmediate(objOop)) { + if ((cogMethodOrNil) + && (isYoungObject(objOop))) { + ensureInYoungReferrers(cogMethodOrNil); + } + markAndTrace(objOop); + } +} + + +/* If primIndex has an accessorDepth and fails, or it is external and fails + with PrimErrNoMemory, + call ceCheckAndMaybeRetryPrimitive if so If ceCheck.... answers true, + retry the primitive. */ + + /* CogObjectRepresentationForSpur>>#maybeCompileRetryOf:onPrimitiveFail:flags:jump:continue: */ +static NoDbgRegParms sqInt +maybeCompileRetryOfonPrimitiveFailflagsjumpcontinue(void (*primitiveRoutine)(void), sqInt primIndex, sqInt flags, AbstractInstruction *jmp, AbstractInstruction *label) +{ + if (!jmp) { + return 0; + } + jmpTarget(jmp, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* newMethod must be set unless it already has been. */ + if (!(((flags & PrimCallNeedsNewMethod) != 0))) { + genLoadNewMethod(); + } + + /* primitiveFunctionPointer must be set */ + + /* #MoveCw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveCwR, ((sqInt)primitiveRoutine), TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, primitiveFunctionPointerAddress()); + compileCallFornumArgsargargargargresultRegregsToSave( + ceCheckAndMaybeRetryPrimitive, + 1, + (/* begin trampolineArgConstant: */ + assert(primIndex >= 0), + -2 - primIndex), + null, + null, + null, + TempReg, + 0 /* emptyRegisterMask */); + + /* Jump: */ + genoperand(Jump, ((sqInt)label)); + return 0; +} + + +/* Generate a shift of the register containing the class tag in a method + cache probe. + c.f. SpurMemoryManager>>methodCacheHashOf:with: */ + + /* CogObjectRepresentationForSpur>>#maybeShiftClassTagRegisterForMethodCacheProbe: */ +static NoDbgRegParms sqInt +maybeShiftClassTagRegisterForMethodCacheProbe(sqInt classTagReg) +{ + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, 2, classTagReg); + return 0; +} + + /* CogObjectRepresentationForSpur>>#numCharacterBits */ +static sqInt +numCharacterBits(void) +{ + return 30; +} + + /* CogObjectRepresentationForSpur>>#remapObject: */ +static NoDbgRegParms sqInt +remapObject(sqInt objOop) +{ + assert(addressCouldBeObj(objOop)); + return (shouldRemapObj(objOop) + ? remapObj(objOop) + : objOop); +} + + /* CogObjectRepresentationForSpur>>#remapOop: */ +static NoDbgRegParms sqInt +remapOop(sqInt objOop) +{ + return (shouldRemapOop(objOop) + ? remapObj(objOop) + : objOop); +} + + +/* Objects in newSpace or oldSpace except nil, true, false & + classTableRootObj need to be annotated. + */ + + /* CogObjectRepresentationForSpur>>#shouldAnnotateObjectReference: */ +static NoDbgRegParms sqInt +shouldAnnotateObjectReference(sqInt anOop) +{ + return (isNonImmediate(anOop)) + && ((oopisGreaterThan(anOop, classTableRootObj())) + || (oopisLessThan(anOop, nilObject()))); +} + + /* CogObjectRepresentationForSpur>>#slotOffsetOfInstVarIndex: */ +static NoDbgRegParms sqInt +slotOffsetOfInstVarIndex(sqInt index) +{ + return (index * BytesPerWord) + BaseHeaderSize; +} + + /* CogSimStackEntry>>#ensureSpilledAt:from: */ +static NoDbgRegParms SimStackEntry * +ensureSpilledAtfrom(SimStackEntry *self_in_CogSimStackEntry, sqInt baseOffset, sqInt baseRegister) +{ + AbstractInstruction *inst; + + if ((self_in_CogSimStackEntry->spilled)) { + if (((self_in_CogSimStackEntry->type)) == SSSpill) { + assert(((((self_in_CogSimStackEntry->offset)) == baseOffset) + && (((self_in_CogSimStackEntry->registerr)) == baseRegister)) + || (violatesEnsureSpilledSpillAssert())); + return self_in_CogSimStackEntry; + } + } + assert(((self_in_CogSimStackEntry->type)) != SSSpill); + traceSpill(self_in_CogSimStackEntry); + if (((self_in_CogSimStackEntry->type)) == SSConstant) { + inst = /* genPushConstant: */ + (/* shouldAnnotateObjectReference: */ + (isNonImmediate((self_in_CogSimStackEntry->constant))) + && ((oopisGreaterThan((self_in_CogSimStackEntry->constant), classTableRootObj())) + || (oopisLessThan((self_in_CogSimStackEntry->constant), nilObject()))) + ? annotateobjRef(genoperand(PushCw, (self_in_CogSimStackEntry->constant)), (self_in_CogSimStackEntry->constant)) + : genoperand(PushCq, (self_in_CogSimStackEntry->constant))); + } + else { + if (((self_in_CogSimStackEntry->type)) == SSBaseOffset) { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, (self_in_CogSimStackEntry->offset), (self_in_CogSimStackEntry->registerr), TempReg); + inst = genoperand(PushR, TempReg); + } + else { + assert(((self_in_CogSimStackEntry->type)) == SSRegister); + inst = genoperand(PushR, (self_in_CogSimStackEntry->registerr)); + } + (self_in_CogSimStackEntry->type) = SSSpill; + (self_in_CogSimStackEntry->offset) = baseOffset; + (self_in_CogSimStackEntry->registerr) = baseRegister; + } + (self_in_CogSimStackEntry->spilled) = 1; + return self_in_CogSimStackEntry; +} + + /* CogSimStackEntry>>#isSameEntryAs: */ +static NoDbgRegParms sqInt +isSameEntryAs(SimStackEntry *self_in_CogSimStackEntry, SimStackEntry *ssEntry) +{ + return (((self_in_CogSimStackEntry->type)) == ((ssEntry->type))) + && ((((((self_in_CogSimStackEntry->type)) == SSBaseOffset) + || (((self_in_CogSimStackEntry->type)) == SSSpill)) + && ((((self_in_CogSimStackEntry->offset)) == ((ssEntry->offset))) + && (((self_in_CogSimStackEntry->registerr)) == ((ssEntry->registerr))))) + || (((((self_in_CogSimStackEntry->type)) == SSRegister) + && (((self_in_CogSimStackEntry->registerr)) == ((ssEntry->registerr)))) + || ((((self_in_CogSimStackEntry->type)) == SSConstant) + && (((self_in_CogSimStackEntry->constant)) == ((ssEntry->constant)))))); +} + + +/* Receiver is not a forwarder, except in blocks with no inst var access. + For now we optimize only the case where receiver is accessed in a method. */ + + /* CogSimStackEntry>>#mayBeAForwarder */ +static NoDbgRegParms sqInt +mayBeAForwarder(SimStackEntry *self_in_CogSimStackEntry) +{ + if ((((self_in_CogSimStackEntry->type)) == SSRegister) + && (isNonForwarderReceiver((self_in_CogSimStackEntry->registerr)))) { + return 0; + } + return ((self_in_CogSimStackEntry->type)) != SSConstant; +} + + /* CogSimStackEntry>>#popToReg: */ +static NoDbgRegParms SimStackEntry * +popToReg(SimStackEntry *self_in_CogSimStackEntry, sqInt reg) +{ + if ((self_in_CogSimStackEntry->spilled)) { + /* PopR: */ + genoperand(PopR, reg); + } + else { + switch ((self_in_CogSimStackEntry->type)) { + case SSBaseOffset: + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, (self_in_CogSimStackEntry->offset), (self_in_CogSimStackEntry->registerr), reg); + break; + case SSConstant: + /* genMoveConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate((self_in_CogSimStackEntry->constant))) + && ((oopisGreaterThan((self_in_CogSimStackEntry->constant), classTableRootObj())) + || (oopisLessThan((self_in_CogSimStackEntry->constant), nilObject())))) { + annotateobjRef(genoperandoperand(MoveCwR, (self_in_CogSimStackEntry->constant), reg), (self_in_CogSimStackEntry->constant)); + } + else { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, (self_in_CogSimStackEntry->constant), reg); + } + break; + case SSRegister: + if (reg != ((self_in_CogSimStackEntry->registerr))) { + /* MoveR:R: */ + genoperandoperand(MoveRR, (self_in_CogSimStackEntry->registerr), reg); + } + else { + /* Label */ + genoperandoperand(Label, (labelCounter += 1), bytecodePC); + } + break; + default: + error("Case not found and no otherwise clause"); + } + } + return self_in_CogSimStackEntry; +} + + +/* Answer a bit mask for the receiver's register, if any. */ + + /* CogSimStackEntry>>#registerMask */ +static NoDbgRegParms sqInt +registerMask(SimStackEntry *self_in_CogSimStackEntry) +{ + return ((((self_in_CogSimStackEntry->type)) == SSBaseOffset) + || (((self_in_CogSimStackEntry->type)) == SSRegister) + ? ((((self_in_CogSimStackEntry->registerr)) < 0) ? (((usqInt)(1)) >> (-((self_in_CogSimStackEntry->registerr)))) : (1ULL << ((self_in_CogSimStackEntry->registerr)))) + : 0); +} + + /* CogSimStackEntry>>#registerMaskOrNone */ +static NoDbgRegParms sqInt +registerMaskOrNone(SimStackEntry *self_in_CogSimStackEntry) +{ + return (((self_in_CogSimStackEntry->type)) == SSRegister + ? ((((self_in_CogSimStackEntry->registerr)) < 0) ? (((usqInt)(1)) >> (-((self_in_CogSimStackEntry->registerr)))) : (1ULL << ((self_in_CogSimStackEntry->registerr)))) + : 0); +} + + /* CogSimStackEntry>>#registerOrNone */ +static NoDbgRegParms sqInt +registerOrNone(SimStackEntry *self_in_CogSimStackEntry) +{ + return (((self_in_CogSimStackEntry->type)) == SSRegister + ? (self_in_CogSimStackEntry->registerr) + : NoReg); +} + + /* CogSimStackEntry>>#storeToReg: */ +static NoDbgRegParms SimStackEntry * +storeToReg(SimStackEntry *self_in_CogSimStackEntry, sqInt reg) +{ + switch ((self_in_CogSimStackEntry->type)) { + case SSBaseOffset: + case SSSpill: + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, (self_in_CogSimStackEntry->offset), (self_in_CogSimStackEntry->registerr), reg); + break; + case SSConstant: + /* genMoveConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate((self_in_CogSimStackEntry->constant))) + && ((oopisGreaterThan((self_in_CogSimStackEntry->constant), classTableRootObj())) + || (oopisLessThan((self_in_CogSimStackEntry->constant), nilObject())))) { + annotateobjRef(genoperandoperand(MoveCwR, (self_in_CogSimStackEntry->constant), reg), (self_in_CogSimStackEntry->constant)); + } + else { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, (self_in_CogSimStackEntry->constant), reg); + } + break; + case SSRegister: + if (reg != ((self_in_CogSimStackEntry->registerr))) { + /* MoveR:R: */ + genoperandoperand(MoveRR, (self_in_CogSimStackEntry->registerr), reg); + } + else { + /* Label */ + genoperandoperand(Label, (labelCounter += 1), bytecodePC); + } + break; + default: + error("Case not found and no otherwise clause"); + } + return self_in_CogSimStackEntry; +} + + /* CogSSBytecodeFixup>>#isMergeFixup */ +static NoDbgRegParms int +isMergeFixup(BytecodeFixup *self_in_CogSSBytecodeFixup) +{ + return (((usqInt)((self_in_CogSSBytecodeFixup->targetInstruction)))) == NeedsMergeFixupFlag; +} + + +/* Answer an unused abstract register in the liveRegMask. + Subclasses with more registers can override to answer them. + N.B. Do /not/ allocate TempReg. */ +/* Answer an unused abstract register in the liveRegMask. + Subclasses with more registers can override to answer them. + N.B. Do /not/ allocate TempReg. */ + + /* CogX64Compiler>>#availableRegisterOrNoneFor: */ +static NoDbgRegParms sqInt +availableRegisterOrNoneFor(AbstractInstruction *self_in_CogX64Compiler, sqInt liveRegsMask) +{ + if (!(((liveRegsMask & ((1U << Extra5Reg))) != 0))) { + return Extra5Reg; + } + if (!(((liveRegsMask & ((1U << Extra4Reg))) != 0))) { + return Extra4Reg; + } + if (!(((liveRegsMask & ((1U << Extra3Reg))) != 0))) { + return Extra3Reg; + } + if (!(((liveRegsMask & ((1U << Extra2Reg))) != 0))) { + return Extra2Reg; + } + if (!(((liveRegsMask & ((1U << Extra1Reg))) != 0))) { + return Extra1Reg; + } + if (!(((liveRegsMask & ((1U << Extra0Reg))) != 0))) { + return Extra0Reg; + } + if (!(((liveRegsMask & ((1U << Arg1Reg))) != 0))) { + return Arg1Reg; + } + if (!(((liveRegsMask & ((1U << Arg0Reg))) != 0))) { + return Arg0Reg; + } + if (!(((liveRegsMask & ((1U << SendNumArgsReg))) != 0))) { + return SendNumArgsReg; + } + if (!(((liveRegsMask & ((1U << ClassReg))) != 0))) { + return ClassReg; + } + if (!(((liveRegsMask & ((1U << ReceiverResultReg))) != 0))) { + return ReceiverResultReg; + } + return NoReg; +} + + /* CogX64Compiler>>#callFullInstructionByteSize */ +static NoDbgRegParms sqInt +callFullInstructionByteSize(AbstractInstruction *self_in_CogX64Compiler) +{ + return 12; +} + + +/* Answer the address the full call immediately preceding + callSiteReturnAddress will jump to. + */ + + /* CogX64Compiler>>#callFullTargetFromReturnAddress: */ +static NoDbgRegParms sqInt +callFullTargetFromReturnAddress(AbstractInstruction *self_in_CogX64Compiler, sqInt callSiteReturnAddress) +{ + return unalignedLongAt((void *)((callSiteReturnAddress - 2) - 8)); +} + + /* CogX64Compiler>>#callInstructionByteSize */ +static NoDbgRegParms sqInt +callInstructionByteSize(AbstractInstruction *self_in_CogX64Compiler) +{ + return 5; +} + + +/* Answer the address the call immediately preceding callSiteReturnAddress + will jump to. + */ + + /* CogX64Compiler>>#callTargetFromReturnAddress: */ +static NoDbgRegParms usqInt +callTargetFromReturnAddress(AbstractInstruction *self_in_CogX64Compiler, sqInt callSiteReturnAddress) +{ + unsigned int callDistance; + + callDistance = literal32BeforeFollowingAddress(self_in_CogX64Compiler, callSiteReturnAddress); + return ((usqInt)(callSiteReturnAddress + (((int) callDistance)))); +} + + /* CogX64Compiler>>#cFloatResultToRd: */ +static NoDbgRegParms AbstractInstruction * +cFloatResultToRd(AbstractInstruction *self_in_CogX64Compiler, sqInt reg) +{ + if (XMM0L != reg) { + /* MoveRd:Rd: */ + genoperandoperand(MoveRdRd, XMM0L, reg); + } + return self_in_CogX64Compiler; +} + + /* CogX64Compiler>>#cFloatResultToRs: */ +static NoDbgRegParms AbstractInstruction * +cFloatResultToRs(AbstractInstruction *self_in_CogX64Compiler, sqInt reg) +{ + if (XMM0L != reg) { + /* MoveRs:Rs: */ + genoperandoperand(MoveRsRs, XMM0L, reg); + } + return self_in_CogX64Compiler; +} + + /* CogX64Compiler>>#cmpC32RTempByteSize */ +static NoDbgRegParms sqInt +cmpC32RTempByteSize(AbstractInstruction *self_in_CogX64Compiler) +{ + return 5; +} + + +/* Compute the maximum size for each opcode. This allows jump offsets to + be determined, provided that all backward branches are long branches. */ +/* N.B. The ^N forms are to get around the bytecode compiler's long branch + limits which are exceeded when each case jumps around the otherwise. */ + + /* CogX64Compiler>>#computeMaximumSize */ +static NoDbgRegParms sqInt +computeMaximumSize(AbstractInstruction *self_in_CogX64Compiler) +{ + switch ((self_in_CogX64Compiler->opcode)) { + case Label: + return 0; + + case AlignmentNops: + return (((self_in_CogX64Compiler->operands))[0]) - 1; + + case Fill32: + case IMULRR: + case BSR: + case SignExtend8RR: + case SignExtend16RR: + case ZeroExtend8RR: + case ZeroExtend16RR: + return 4; + + case Nop: + case REP: + case CLD: + case MOVSB: + case Stop: + return 1; + + case CDQ: + case CPUID: + case MOVSQ: + return 2; + + case IDIVR: + case CallR: + case JumpR: + case AddRR: + case AddcRR: + case AndRR: + case CmpRR: + case OrRR: + case XorRR: + case SubRR: + case SubbRR: + case NegateR: + case MoveRR: + case SignExtend32RR: + return 3; + + case XCHGRR: + return (((((self_in_CogX64Compiler->operands))[0]) == RAX) + || ((((self_in_CogX64Compiler->operands))[1]) == RAX) + ? 2 + : 3); + + case CallFull: + return 12; + + case Call: + case ClzRR: + case MoveRRd: + case MoveRdR: + case ConvertRRd: + case ConvertRdR: + return 5; + + case JumpFull: + resolveJumpTarget(self_in_CogX64Compiler); + return 12; + + case JumpLong: + case Jump: + resolveJumpTarget(self_in_CogX64Compiler); + return 5; + + case JumpZero: + case JumpNonZero: + case JumpNegative: + case JumpNonNegative: + case JumpOverflow: + case JumpNoOverflow: + case JumpCarry: + case JumpNoCarry: + case JumpLess: + case JumpGreaterOrEqual: + case JumpGreater: + case JumpLessOrEqual: + case JumpBelow: + case JumpAboveOrEqual: + case JumpAbove: + case JumpBelowOrEqual: + case JumpLongZero: + case JumpLongNonZero: + case JumpFPEqual: + case JumpFPNotEqual: + case JumpFPLess: + case JumpFPGreaterOrEqual: + case JumpFPGreater: + case JumpFPLessOrEqual: + case JumpFPOrdered: + case JumpFPUnordered: + resolveJumpTarget(self_in_CogX64Compiler); + return 6; + + case RetN: + return (((self_in_CogX64Compiler->operands))[0] + ? 3 + : 1); + + case AddCqR: + case AddcCqR: + case AndCqR: + case CmpCqR: + case OrCqR: + case SubCqR: + case SubbCqR: + case TstCqR: + case XorCqR: + /* begin computeSizeOfArithCqR */ + if (isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0])) { + return 4; + } + if (is32BitSignedImmediate(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0])) { + return (((self_in_CogX64Compiler->operands))[1] + ? 7 + : 6); + } + return 13; + + case AddCwR: + case AndCwR: + case CmpCwR: + case OrCwR: + case SubCwR: + case XorCwR: + return 13; + + case CmpC32R: + return ((((self_in_CogX64Compiler->operands))[1]) <= 7 + ? (((self_in_CogX64Compiler->operands))[1] + ? 6 + : 5) + : 7); + + case LoadEffectiveAddressMwrR: + return ((isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0]) + ? 4 + : 7)) + ((((((self_in_CogX64Compiler->operands))[1]) & 7) == RSP + ? 1 + : 0)); + + case LogicalShiftLeftCqR: + case LogicalShiftRightCqR: + case ArithmeticShiftRightCqR: + case RotateRightCqR: + case RotateLeftCqR: + return ((((self_in_CogX64Compiler->operands))[0]) == 1 + ? 3 + : 4); + + case LogicalShiftLeftRR: + case LogicalShiftRightRR: + case ArithmeticShiftRightRR: + return computeShiftRRSize(self_in_CogX64Compiler); + + case AddRdRd: + case CmpRdRd: + case SubRdRd: + case MulRdRd: + case DivRdRd: + case XorRdRd: + case AddRsRs: + case SubRsRs: + case MulRsRs: + case DivRsRs: + case MoveRdRd: + case MoveRsRs: + case ConvertRRs: + case ConvertRsR: + case ConvertRsRd: + case ConvertRdRs: + return 4 + ((((((self_in_CogX64Compiler->operands))[1]) > 7) + || ((((self_in_CogX64Compiler->operands))[0]) > 7) + ? 1 + : 0)); + + case SqrtRd: + case SqrtRs: + return 4 + (((((self_in_CogX64Compiler->operands))[0]) > 7 + ? 1 + : 0)); + + case CmpRsRs: + case XorRsRs: + return 3 + ((((((self_in_CogX64Compiler->operands))[1]) > 7) + || ((((self_in_CogX64Compiler->operands))[0]) > 7) + ? 1 + : 0)); + + case MoveCqR: + return (((self_in_CogX64Compiler->operands))[0] + ? (is32BitSignedImmediate(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0]) + ? 7 + : 10 /* (moveCwRByteSize - 1) */) + : 3); + + case MoveCwR: + return (/* inCurrentCompilation: */ + (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])))) + || ((((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0]))) == (methodLabel))) + || (/* addressIsInCurrentCompilation: */ + ((((usqInt)(((self_in_CogX64Compiler->operands))[0]))) >= ((methodLabel->address))) + && ((((usqInt)(((self_in_CogX64Compiler->operands))[0]))) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize))))) + ? 7 + : 11 /* moveCwRByteSize */); + + case MoveC32R: + return 7; + + case MoveAwR: + return (/* isAddressRelativeToVarBase: */ + ((((self_in_CogX64Compiler->operands))[0]) >= (varBaseAddress)) + && (((((self_in_CogX64Compiler->operands))[0]) - (varBaseAddress)) < (0x100000)) + ? (isQuick(self_in_CogX64Compiler, (((self_in_CogX64Compiler->operands))[0]) - (varBaseAddress)) + ? ((((((self_in_CogX64Compiler->operands))[0]) - (varBaseAddress)) == 0) + && (((((self_in_CogX64Compiler->operands))[1]) & 7) != RBP) + ? 3 + : 4) + : 7) + : (((self_in_CogX64Compiler->operands))[1] + ? (((((self_in_CogX64Compiler->operands))[1]) == RBP) + || ((((self_in_CogX64Compiler->operands))[1]) == RSP) + ? 12 + : 14) + : 10)); + + case MoveA32R: + return (((self_in_CogX64Compiler->operands))[1] + ? 13 + : 9); + + case MoveRAw: + return (/* isAddressRelativeToVarBase: */ + ((((self_in_CogX64Compiler->operands))[1]) >= (varBaseAddress)) + && (((((self_in_CogX64Compiler->operands))[1]) - (varBaseAddress)) < (0x100000)) + ? (isQuick(self_in_CogX64Compiler, (((self_in_CogX64Compiler->operands))[1]) - (varBaseAddress)) + ? ((((((self_in_CogX64Compiler->operands))[1]) - (varBaseAddress)) == 0) + && (((((self_in_CogX64Compiler->operands))[0]) & 7) != RBP) + ? 3 + : 4) + : 7) + : (((self_in_CogX64Compiler->operands))[0] + ? (((((self_in_CogX64Compiler->operands))[0]) == RBP) + || ((((self_in_CogX64Compiler->operands))[0]) == RSP) + ? 13 + : 14) + : 10)); + + case MoveRA32: + return (((self_in_CogX64Compiler->operands))[0] + ? 13 + : 9); + + case MoveAbR: + return (/* isAddressRelativeToVarBase: */ + ((((self_in_CogX64Compiler->operands))[0]) >= (varBaseAddress)) + && (((((self_in_CogX64Compiler->operands))[0]) - (varBaseAddress)) < (0x100000)) + ? (isQuick(self_in_CogX64Compiler, (((self_in_CogX64Compiler->operands))[0]) - (varBaseAddress)) + ? ((((((self_in_CogX64Compiler->operands))[0]) - (varBaseAddress)) == 0) + && (((((self_in_CogX64Compiler->operands))[1]) & 7) != RBP) + ? 3 + : 4) + : 7) + : (((self_in_CogX64Compiler->operands))[1] + ? 14 + : 10)); + + case MoveRAb: + return (/* isAddressRelativeToVarBase: */ + ((((self_in_CogX64Compiler->operands))[1]) >= (varBaseAddress)) + && (((((self_in_CogX64Compiler->operands))[1]) - (varBaseAddress)) < (0x100000)) + ? (isQuick(self_in_CogX64Compiler, (((self_in_CogX64Compiler->operands))[1]) - (varBaseAddress)) + ? ((((((self_in_CogX64Compiler->operands))[1]) - (varBaseAddress)) == 0) + && (((((self_in_CogX64Compiler->operands))[0]) & 7) != RBP) + ? 3 + : 4) + : 7) + : (((self_in_CogX64Compiler->operands))[0] + ? 14 + : 10)); + + case MoveRMwr: + assert(is32BitSignedImmediate(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[1])); + return ((isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[1]) + ? (((((self_in_CogX64Compiler->operands))[1]) == 0) + && (((((self_in_CogX64Compiler->operands))[2]) & 7) != RBP) + ? 3 + : 4) + : 7)) + ((((((self_in_CogX64Compiler->operands))[2]) & 7) == RSP + ? 1 + : 0)); + + case MoveRM32r: + return (((isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[1]) + ? (((((self_in_CogX64Compiler->operands))[1]) == 0) + && (((((self_in_CogX64Compiler->operands))[2]) & 6) != RSP) + ? 2 + : 3) + : 6)) + (((((((self_in_CogX64Compiler->operands))[2]) & 7) == RSP) + && ((((self_in_CogX64Compiler->operands))[1]) != 0) + ? 1 + : 0))) + ((((((self_in_CogX64Compiler->operands))[2]) > 7) + || ((((self_in_CogX64Compiler->operands))[0]) > 7) + ? 1 + : 0)); + + case MoveRsM32r: + case MoveRdM64r: + return (((isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[1]) + ? (((((self_in_CogX64Compiler->operands))[1]) == 0) + && (((((self_in_CogX64Compiler->operands))[2]) & 6) != RSP) + ? 4 + : 5) + : 8)) + (((((((self_in_CogX64Compiler->operands))[2]) & 7) == RSP) + && ((((self_in_CogX64Compiler->operands))[1]) != 0) + ? 1 + : 0))) + ((((((self_in_CogX64Compiler->operands))[2]) > 7) + || ((((self_in_CogX64Compiler->operands))[0]) > 7) + ? 1 + : 0)); + + case MoveMbrR: + case MoveM8rR: + case MoveMs8rR: + case MoveMwrR: + assert(is32BitSignedImmediate(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0])); + return ((isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0]) + ? (((((self_in_CogX64Compiler->operands))[0]) == 0) + && (((((self_in_CogX64Compiler->operands))[1]) & 7) != RBP) + ? 3 + : 4) + : 7)) + ((((((self_in_CogX64Compiler->operands))[1]) & 7) == RSP + ? 1 + : 0)); + + case MoveRMbr: + case MoveRM8r: + assert(is32BitSignedImmediate(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[1])); + return ((isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[1]) + ? (((((self_in_CogX64Compiler->operands))[1]) == 0) + && (((((self_in_CogX64Compiler->operands))[0]) & 7) != RBP) + ? 3 + : 4) + : 7)) + ((((((self_in_CogX64Compiler->operands))[2]) & 7) == RSP + ? 1 + : 0)); + + case MoveM16rR: + assert(is32BitSignedImmediate(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0])); + return ((isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0]) + ? (((((self_in_CogX64Compiler->operands))[0]) == 0) + && (((((self_in_CogX64Compiler->operands))[1]) & 7) != RBP) + ? 4 + : 5) + : 8)) + ((((((self_in_CogX64Compiler->operands))[1]) & 7) == RSP + ? 1 + : 0)); + + case MoveRM16r: + assert(is32BitSignedImmediate(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[1])); + return (((isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[1]) + ? 4 + : 7)) + ((((((self_in_CogX64Compiler->operands))[2]) & 7) == RSP + ? 1 + : 0))) + ((((((self_in_CogX64Compiler->operands))[0]) > 7) + || ((((self_in_CogX64Compiler->operands))[2]) > 7) + ? 1 + : 0)); + + case MoveM32rR: + return (((isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0]) + ? (((((self_in_CogX64Compiler->operands))[0]) == 0) + && (((((self_in_CogX64Compiler->operands))[1]) & 6) != RSP) + ? 2 + : 3) + : 6)) + (((((((self_in_CogX64Compiler->operands))[1]) & 7) == RSP) + && ((((self_in_CogX64Compiler->operands))[0]) != 0) + ? 1 + : 0))) + ((((((self_in_CogX64Compiler->operands))[1]) > 7) + || ((((self_in_CogX64Compiler->operands))[2]) > 7) + ? 1 + : 0)); + + case MoveM32rRs: + case MoveM64rRd: + return (((isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0]) + ? (((((self_in_CogX64Compiler->operands))[0]) == 0) + && (((((self_in_CogX64Compiler->operands))[1]) & 6) != RSP) + ? 4 + : 5) + : 8)) + (((((((self_in_CogX64Compiler->operands))[1]) & 7) == RSP) + && ((((self_in_CogX64Compiler->operands))[0]) != 0) + ? 1 + : 0))) + ((((((self_in_CogX64Compiler->operands))[1]) > 7) + || ((((self_in_CogX64Compiler->operands))[2]) > 7) + ? 1 + : 0)); + + case MoveXbrRR: + assert((((self_in_CogX64Compiler->operands))[0]) != RSP); + return (((((self_in_CogX64Compiler->operands))[1]) & 7) == RBP + ? 5 + : 4); + + case MoveRXbrR: + assert((((self_in_CogX64Compiler->operands))[1]) != RSP); + return ((((((self_in_CogX64Compiler->operands))[0]) > 3) + || (((((self_in_CogX64Compiler->operands))[1]) > 7) + || ((((self_in_CogX64Compiler->operands))[2]) > 7)) + ? 4 + : 3)) + ((((((self_in_CogX64Compiler->operands))[2]) & 7) == RBP + ? 1 + : 0)); + + case MoveXwrRR: + assert((((self_in_CogX64Compiler->operands))[0]) != RSP); + return (((((self_in_CogX64Compiler->operands))[1]) == RBP) + || ((((self_in_CogX64Compiler->operands))[1]) == R13) + ? 5 + : 4); + + case MoveRXwrR: + assert((((self_in_CogX64Compiler->operands))[1]) != RSP); + return (((((self_in_CogX64Compiler->operands))[2]) == RBP) + || ((((self_in_CogX64Compiler->operands))[2]) == R13) + ? 5 + : 4); + + case MoveX32rRR: + assert((((self_in_CogX64Compiler->operands))[0]) != RSP); + return ((((((self_in_CogX64Compiler->operands))[1]) == RBP) + || ((((self_in_CogX64Compiler->operands))[1]) == R13) + ? 7 + : 6)) + ((((((self_in_CogX64Compiler->operands))[0]) > 7) + || (((((self_in_CogX64Compiler->operands))[1]) > 7) + || ((((self_in_CogX64Compiler->operands))[2]) > 7)) + ? 1 + : 0)); + + case MoveRX32rR: + assert((((self_in_CogX64Compiler->operands))[1]) != RSP); + return ((((((self_in_CogX64Compiler->operands))[2]) == RBP) + || ((((self_in_CogX64Compiler->operands))[2]) == R13) + ? 4 + : 3)) + ((((((self_in_CogX64Compiler->operands))[0]) > 7) + || (((((self_in_CogX64Compiler->operands))[1]) > 7) + || ((((self_in_CogX64Compiler->operands))[2]) > 7)) + ? 1 + : 0)); + + case PopR: + case PushR: + return ((((self_in_CogX64Compiler->operands))[0]) < 8 + ? 1 + : 2); + + case PushCq: + return (isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0]) + ? 2 + : (is32BitSignedImmediate(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0]) + ? 5 + : computeSizeOfPushCw(self_in_CogX64Compiler))); + + case PushCw: + return computeSizeOfPushCw(self_in_CogX64Compiler); + + case PrefetchAw: + return (/* isAddressRelativeToVarBase: */ + ((((self_in_CogX64Compiler->operands))[0]) >= (varBaseAddress)) + && (((((self_in_CogX64Compiler->operands))[0]) - (varBaseAddress)) < (0x100000)) + ? 7 + : 0); + + case ZeroExtend32RR: + return 2 + ((((((self_in_CogX64Compiler->operands))[1]) > 7) + || ((((self_in_CogX64Compiler->operands))[0]) > 7) + ? 1 + : 0)); + + case MovePerfCnt64RL: + return concretizeMovePerfCnt64RL(self_in_CogX64Compiler); + + default: + error("Case not found and no otherwise clause"); + } + + /* Noops & Pseudo Ops + Specific Arithmetic + Processor Specific Data Movement + Control + Arithmetic + Data Movement + N.B. Always inlined. + Conversion + it is easier to calculate the right value by concretizing. We concretize twice, but so what? */ + return 0; +} + + +/* On the x86 the only instructions that shift by the value of a + register require the shift count to be in %ecx. So we may + have to use swap instructions to get the count into ecx. */ + + /* CogX64Compiler>>#computeShiftRRSize */ +static NoDbgRegParms int +computeShiftRRSize(AbstractInstruction *self_in_CogX64Compiler) +{ + sqInt shiftCountReg; + + shiftCountReg = ((self_in_CogX64Compiler->operands))[0]; + return (shiftCountReg == RCX + ? 3 + : (shiftCountReg + ? (/* XCHG r1,r2 */ + + 9) + : (/* XCHG RAX,r2 */ + + 7))); +} + + /* CogX64Compiler>>#computeSizeOfPushCw */ +static NoDbgRegParms sqInt +computeSizeOfPushCw(AbstractInstruction *self_in_CogX64Compiler) +{ + return (/* inCurrentCompilation: */ + (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])))) + || ((((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0]))) == (methodLabel))) + || (/* addressIsInCurrentCompilation: */ + ((((usqInt)(((self_in_CogX64Compiler->operands))[0]))) >= ((methodLabel->address))) + && ((((usqInt)(((self_in_CogX64Compiler->operands))[0]))) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize))))) + ? 9 + : 12 /* pushCwByteSize */); +} + + +/* Will get inlined into concretizeAt: switch. */ + + /* CogX64Compiler>>#concretizeArithCqRWithRO:raxOpcode: */ +static NoDbgRegParms sqInt +concretizeArithCqRWithROraxOpcode(AbstractInstruction *self_in_CogX64Compiler, sqInt regOpcode, sqInt raxOpcode) +{ + sqInt reg; + sqInt value; + + value = ((self_in_CogX64Compiler->operands))[0]; + reg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + if (isQuick(self_in_CogX64Compiler, value)) { + ((self_in_CogX64Compiler->machineCode))[1] = 131; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, regOpcode)); + ((self_in_CogX64Compiler->machineCode))[3] = (value & 0xFF); + return 4; + } + if (is32BitSignedImmediate(self_in_CogX64Compiler, value)) { + if (!reg) { + ((self_in_CogX64Compiler->machineCode))[1] = raxOpcode; + ((self_in_CogX64Compiler->machineCode))[2] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 24) & 0xFF); + return 6; + } + ((self_in_CogX64Compiler->machineCode))[1] = 129; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, regOpcode)); + ((self_in_CogX64Compiler->machineCode))[3] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(value)) >> 24) & 0xFF); + return 7; + } + return concretizeArithCwR(self_in_CogX64Compiler, (raxOpcode == 61 + ? 57 + : raxOpcode - 2)); +} + + /* CogX64Compiler>>#concretizeFill32 */ +static NoDbgRegParms sqInt +concretizeFill32(AbstractInstruction *self_in_CogX64Compiler) +{ + usqIntptr_t word; + + word = ((self_in_CogX64Compiler->operands))[0]; + ((self_in_CogX64Compiler->machineCode))[0] = (word & 0xFF); + ((self_in_CogX64Compiler->machineCode))[1] = (((word) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[2] = (((word) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = (((word) >> 24) & 0xFF); + return 4; +} + + +/* Generate code for + 0x0: 50 pushq %rax + 0x1: 52 pushq %rdx + 0x2: 0f 31 rdtsc + 0x4: 48 c1 e2 20 shlq $0x20, %rdx + 0x8: 48 09 d0 orq %rdx, %rax + 0xb: 48 89 f8 movq %rdi, %rax + 0xe: 5a popq %rdx + 0xf: 58 popq %rax + et al */ + + /* CogX64Compiler>>#concretizeMovePerfCnt64RL */ +static NoDbgRegParms sqInt +concretizeMovePerfCnt64RL(AbstractInstruction *self_in_CogX64Compiler) +{ + sqInt liveRegisterMask; + sqInt offset; + sqInt reg; + + reg = ((self_in_CogX64Compiler->operands))[0]; + liveRegisterMask = ((self_in_CogX64Compiler->operands))[1]; + offset = 0; + if (((liveRegisterMask & ((1U << RAX))) != 0)) { + ((self_in_CogX64Compiler->machineCode))[0] = 80; + offset += 1; + } + if (((liveRegisterMask & ((1U << RDX))) != 0)) { + ((self_in_CogX64Compiler->machineCode))[offset] = 82; + offset += 1; + } + + /* too lazy to define the swap cases for the moment... */ + assert(!((reg == RDX))); + ((self_in_CogX64Compiler->machineCode))[offset] = 15; + ((self_in_CogX64Compiler->machineCode))[offset + 1] = 49; + ((self_in_CogX64Compiler->machineCode))[offset + 2] = (rexRxb(self_in_CogX64Compiler, 0, 0, RDX)); + ((self_in_CogX64Compiler->machineCode))[offset + 3] = 193; + ((self_in_CogX64Compiler->machineCode))[offset + 4] = (modRMRO(self_in_CogX64Compiler, ModReg, RDX, 4)); + ((self_in_CogX64Compiler->machineCode))[offset + 5] = 32; + ((self_in_CogX64Compiler->machineCode))[offset + 6] = (rexRxb(self_in_CogX64Compiler, RAX, 0, RDX)); + ((self_in_CogX64Compiler->machineCode))[offset + 7] = 11; + ((self_in_CogX64Compiler->machineCode))[offset + 8] = (modRMRO(self_in_CogX64Compiler, ModReg, RDX, RAX)); + + /* rdtsc + shlq $0x20, %rdx + orq %rax, %rdx (%rax := %rax bitOr: %rdx */ + offset += 9; + if (reg) { + ((self_in_CogX64Compiler->machineCode))[offset] = (rexRxb(self_in_CogX64Compiler, RAX, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[offset + 1] = 137; + ((self_in_CogX64Compiler->machineCode))[offset + 2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, RAX)); + + /* movq %rDEST, %rax */ + offset += 3; + } + if (((liveRegisterMask & ((1U << RDX))) != 0)) { + ((self_in_CogX64Compiler->machineCode))[offset] = 90; + offset += 1; + } + if (((liveRegisterMask & ((1U << RAX))) != 0)) { + ((self_in_CogX64Compiler->machineCode))[offset] = 88; + offset += 1; + } + return offset; +} + + /* CogX64Compiler>>#concretizeMoveRX32rR */ +static NoDbgRegParms sqInt +concretizeMoveRX32rR(AbstractInstruction *self_in_CogX64Compiler) +{ + sqInt base; + sqInt index; + sqInt offset; + sqInt src; + + src = ((self_in_CogX64Compiler->operands))[0]; + index = ((self_in_CogX64Compiler->operands))[1]; + base = ((self_in_CogX64Compiler->operands))[2]; + if ((index > 7) + || ((base > 7) + || (src > 7))) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 0, src, index, base)); + offset = 1; + } + else { + offset = 0; + } + if ((base & 7) != RBP) { + ((self_in_CogX64Compiler->machineCode))[offset] = 137; + ((self_in_CogX64Compiler->machineCode))[offset + 1] = (modRMRO(self_in_CogX64Compiler, ModRegInd, 4, src)); + ((self_in_CogX64Compiler->machineCode))[offset + 2] = (sib(self_in_CogX64Compiler, SIB4, index, base)); + return offset + 3; + } + ((self_in_CogX64Compiler->machineCode))[offset] = 137; + ((self_in_CogX64Compiler->machineCode))[offset + 1] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, 4, src)); + ((self_in_CogX64Compiler->machineCode))[offset + 2] = (sib(self_in_CogX64Compiler, SIB4, index, base)); + ((self_in_CogX64Compiler->machineCode))[offset + 3] = 0; + return offset + 4; +} + + +/* MoveX32rRR is expected to zero-extend, so explicitly zero the destination. */ + + /* CogX64Compiler>>#concretizeMoveX32rRR */ +static NoDbgRegParms sqInt +concretizeMoveX32rRR(AbstractInstruction *self_in_CogX64Compiler) +{ + sqInt base; + sqInt dest; + sqInt index; + sqInt offset; + + index = ((self_in_CogX64Compiler->operands))[0]; + base = ((self_in_CogX64Compiler->operands))[1]; + dest = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, dest, 0, dest)); + ((self_in_CogX64Compiler->machineCode))[1] = 49; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, dest, dest)); + if ((index > 7) + || ((base > 7) + || (dest > 7))) { + ((self_in_CogX64Compiler->machineCode))[3] = (rexwrxb(self_in_CogX64Compiler, 0, dest, index, base)); + offset = 1; + } + else { + offset = 0; + } + if ((base & 7) != RBP) { + ((self_in_CogX64Compiler->machineCode))[offset + 3] = 139; + ((self_in_CogX64Compiler->machineCode))[offset + 4] = (modRMRO(self_in_CogX64Compiler, ModRegInd, 4, dest)); + ((self_in_CogX64Compiler->machineCode))[offset + 5] = (sib(self_in_CogX64Compiler, SIB4, index, base)); + return offset + 6; + } + ((self_in_CogX64Compiler->machineCode))[offset + 3] = 139; + ((self_in_CogX64Compiler->machineCode))[offset + 4] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, 4, dest)); + ((self_in_CogX64Compiler->machineCode))[offset + 5] = (sib(self_in_CogX64Compiler, SIB4, index, base)); + ((self_in_CogX64Compiler->machineCode))[offset + 6] = 0; + return offset + 7; +} + + /* CogX64Compiler>>#concretizeOpRR: */ +static NoDbgRegParms sqInt +concretizeOpRR(AbstractInstruction *self_in_CogX64Compiler, sqInt x64opcode) +{ + sqInt regLHS; + sqInt regRHS; + + regLHS = ((self_in_CogX64Compiler->operands))[0]; + regRHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, regRHS, 0, regLHS)); + ((self_in_CogX64Compiler->machineCode))[1] = x64opcode; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, regLHS, regRHS)); + return 3; +} + + +/* We support only prefetches for addresses that are variables relative to + VarBase + */ + + /* CogX64Compiler>>#concretizePrefetchAw */ +static NoDbgRegParms sqInt +concretizePrefetchAw(AbstractInstruction *self_in_CogX64Compiler) +{ + sqInt offset; + sqInt operand; + + operand = ((self_in_CogX64Compiler->operands))[0]; + if (!(/* isAddressRelativeToVarBase: */ + (operand >= (varBaseAddress)) + && ((operand - (varBaseAddress)) < (0x100000)))) { + return 0; + } + offset = operand - (varBaseAddress); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = 24; + ((self_in_CogX64Compiler->machineCode))[2] = 147; + ((self_in_CogX64Compiler->machineCode))[3] = (offset & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offset)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offset)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = (((usqInt)(offset)) >> 24); + return 7; +} + + +/* CmpRR/MoveRR RHS LHS computes LHS - RHS, i.e. apparently reversed. You + have to think subtract. */ + + /* CogX64Compiler>>#concretizeReverseOpRR: */ +static NoDbgRegParms sqInt +concretizeReverseOpRR(AbstractInstruction *self_in_CogX64Compiler, sqInt x64opcode) +{ + sqInt regLHS; + sqInt regRHS; + + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, regRHS, 0, regLHS)); + ((self_in_CogX64Compiler->machineCode))[1] = x64opcode; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, regLHS, regRHS)); + return 3; +} + + /* CogX64Compiler>>#concretizeXCHGRR */ +static NoDbgRegParms sqInt +concretizeXCHGRR(AbstractInstruction *self_in_CogX64Compiler) +{ + sqInt r1; + sqInt r2; + + r1 = ((self_in_CogX64Compiler->operands))[0]; + r2 = ((self_in_CogX64Compiler->operands))[1]; + if (!r2) { + r2 = r1; + r1 = RAX; + } + if (!r1) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, r2)); + ((self_in_CogX64Compiler->machineCode))[1] = (144 + (r2 % 8)); + return 2; + } + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, r1, 0, r2)); + ((self_in_CogX64Compiler->machineCode))[1] = 135; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, r2, r1)); + return 3; +} + + +/* Do a throw-away compilation to get at the cpuid info and initialize + cpuidWord1 N.B. All of MSVC, gcc & clang have intrinsics for this, so if + you have the energy + by all means reimplement as an #if _MSC_VER...#elif __GNUC__ #else ... + saga. + */ + + /* CogX64Compiler>>#detectFeatures */ +static NoDbgRegParms AbstractInstruction * +detectFeatures(AbstractInstruction *self_in_CogX64Compiler) +{ + usqIntptr_t (*cpuid)(void); + usqIntptr_t fixupSize; + usqIntptr_t opcodeSize; + usqInt startAddress; + + startAddress = methodZoneBase; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 10; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + cpuid = ((usqIntptr_t (*)(void)) startAddress); + + /* PushR: */ + genoperand(PushR, RDX); + + /* PushR: */ + genoperand(PushR, RCX); + + /* PushR: */ + genoperand(PushR, RBX); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0x80000001U, RAX); + gen(CPUID); + + /* MoveR:R: */ + genoperandoperand(MoveRR, RCX, RAX); + + /* PopR: */ + genoperand(PopR, RBX); + + /* PopR: */ + genoperand(PopR, RCX); + + /* PopR: */ + genoperand(PopR, RDX); + + /* RetN: */ + genoperand(RetN, 0); + outputInstructionsForGeneratedRuntimeAt(startAddress); + + /* begin resetMethodZoneBase: */ + methodZoneBase = startAddress; + setCpuidWord1(self_in_CogX64Compiler, cpuid()); + return self_in_CogX64Compiler; +} + + +/* Attempt to generate concrete machine code for the instruction at address. + This is the inner dispatch of concretizeAt: actualAddress which exists + only to get around the branch size limits in the SqueakV3 (blue book + derived) bytecode set. */ + + /* CogX64Compiler>>#dispatchConcretize */ +static NoDbgRegParms sqInt +dispatchConcretize(AbstractInstruction *self_in_CogX64Compiler) +{ + sqInt addressOperand; + sqInt base; + sqInt baseReg; + AbstractInstruction *dependentChain; + sqInt dest; + sqInt destReg; + sqInt distance; + sqInt i; + sqInt index; + AbstractInstruction *jumpTarget; + sqInt maskReg; + int offset; + sqInt offset1; + sqInt offsetSqInt; + sqInt operand; + sqInt reg; + sqInt regLHS; + sqInt regRHS; + sqInt regToShift; + sqInt save0; + sqInt save1; + sqInt savedSize; + sqInt shiftCountReg; + sqInt skip; + sqInt src; + sqInt srcReg; + sqInt value; + sqInt valueSqInt; + usqIntptr_t word; + + if (((self_in_CogX64Compiler->opcode)) >= CPUID) { + return dispatchConcretizeProcessorSpecific(self_in_CogX64Compiler); + } + switch ((self_in_CogX64Compiler->opcode)) { + case Label: + /* begin concretizeLabel */ + dependentChain = (self_in_CogX64Compiler->dependent); + while (!(!dependentChain)) { + /* begin updateLabel: */ + assert((((dependentChain->opcode)) == MoveCwR) + || (((dependentChain->opcode)) == PushCw)); + ((dependentChain->operands))[0] = (((self_in_CogX64Compiler->address)) + (((self_in_CogX64Compiler->operands))[1])); + dependentChain = (dependentChain->dependent); + } + return 0; + + case AlignmentNops: + /* begin concretizeAlignmentNops */ + for (i = 0; i < ((self_in_CogX64Compiler->machineCodeSize)); i += 1) { + ((self_in_CogX64Compiler->machineCode))[i] = 144; + } + return (self_in_CogX64Compiler->machineCodeSize); + + case Fill32: + /* begin concretizeFill32 */ + word = ((self_in_CogX64Compiler->operands))[0]; + ((self_in_CogX64Compiler->machineCode))[0] = (word & 0xFF); + ((self_in_CogX64Compiler->machineCode))[1] = (((word) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[2] = (((word) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = (((word) >> 24) & 0xFF); + return 4; + + case Nop: + /* begin concretizeNop */ + ((self_in_CogX64Compiler->machineCode))[0] = 144; + return 1; + + case Call: + /* begin concretizeCall */ + assert((((self_in_CogX64Compiler->operands))[0]) != 0); + offset = (((int) (((self_in_CogX64Compiler->operands))[0]))) - (((int) (((self_in_CogX64Compiler->address)) + 5))); + ((self_in_CogX64Compiler->machineCode))[0] = 232; + ((self_in_CogX64Compiler->machineCode))[1] = (offset & 0xFF); + ((self_in_CogX64Compiler->machineCode))[2] = ((((usqInt)(offset)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offset)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offset)) >> 24) & 0xFF); + return 5; + + case CallR: + /* begin concretizeCallR */ + reg = ((self_in_CogX64Compiler->operands))[0]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = 0xFF; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 2)); + return 3; + + case CallFull: + /* begin concretizeCallFull */ + operand = ((self_in_CogX64Compiler->operands))[0]; + ((self_in_CogX64Compiler->machineCode))[0] = 72; + ((self_in_CogX64Compiler->machineCode))[1] = 184; + ((self_in_CogX64Compiler->machineCode))[2] = (operand & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(operand)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(operand)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(operand)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(operand)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(operand)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8] = ((((usqInt)(operand)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[9] = ((((usqInt)(operand)) >> 56) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[10] = 0xFF; + ((self_in_CogX64Compiler->machineCode))[11] = (modRMRO(self_in_CogX64Compiler, ModReg, RAX, 2)); + return 12; + + case JumpR: + /* begin concretizeJumpR */ + reg = ((self_in_CogX64Compiler->operands))[0]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = 0xFF; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 4)); + return 3; + + case JumpFull: + /* begin concretizeJumpFull */ + operand = ((self_in_CogX64Compiler->operands))[0]; + ((self_in_CogX64Compiler->machineCode))[0] = 72; + ((self_in_CogX64Compiler->machineCode))[1] = 184; + ((self_in_CogX64Compiler->machineCode))[2] = (operand & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(operand)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(operand)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(operand)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(operand)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(operand)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8] = ((((usqInt)(operand)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[9] = ((((usqInt)(operand)) >> 56) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[10] = 0xFF; + ((self_in_CogX64Compiler->machineCode))[11] = (modRMRO(self_in_CogX64Compiler, ModReg, RAX, 4)); + return 12; + + case JumpLong: + /* begin concretizeJumpLong */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset = ((int) ((((char *)jumpTarget)) - (((char *)(((self_in_CogX64Compiler->address)) + 5))))); + ((self_in_CogX64Compiler->machineCode))[0] = 233; + ((self_in_CogX64Compiler->machineCode))[1] = (offset & 0xFF); + ((self_in_CogX64Compiler->machineCode))[2] = ((((usqInt)(offset)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offset)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offset)) >> 24) & 0xFF); + return 5; + + case JumpLongZero: + case JumpZero: + case JumpFPEqual: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (116); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (132); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpLongNonZero: + case JumpNonZero: + case JumpFPNotEqual: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (117); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (133); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case Jump: + /* begin concretizeJump */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset = ((int) ((((char *)jumpTarget)) - (((char *)(((self_in_CogX64Compiler->address)) + 2))))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset))) { + ((self_in_CogX64Compiler->machineCode))[0] = 235; + ((self_in_CogX64Compiler->machineCode))[1] = (offset & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + offset = ((int) ((((char *)jumpTarget)) - (((char *)(((self_in_CogX64Compiler->address)) + 5))))); + ((self_in_CogX64Compiler->machineCode))[0] = 233; + ((self_in_CogX64Compiler->machineCode))[1] = (offset & 0xFF); + ((self_in_CogX64Compiler->machineCode))[2] = ((((usqInt)(offset)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offset)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offset)) >> 24) & 0xFF); + return 5; + + case JumpNegative: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (120); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (136); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpNonNegative: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (121); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (137); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpOverflow: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (112); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (128); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpNoOverflow: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (113); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (129); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpCarry: + case JumpBelow: + case JumpFPLess: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (114); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (130); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpNoCarry: + case JumpAboveOrEqual: + case JumpFPGreaterOrEqual: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (115); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (131); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpLess: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (0x7C); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (140); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpGreaterOrEqual: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (125); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (141); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpGreater: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (0x7F); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (143); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpLessOrEqual: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (0x7E); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (142); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpAbove: + case JumpFPGreater: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (119); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (135); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpBelowOrEqual: + case JumpFPLessOrEqual: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (118); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (134); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpFPOrdered: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (123); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (139); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpFPUnordered: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (122); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (138); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case RetN: + /* begin concretizeRetN */ + offsetSqInt = ((self_in_CogX64Compiler->operands))[0]; + if (!offsetSqInt) { + ((self_in_CogX64Compiler->machineCode))[0] = 195; + return 1; + } + ((self_in_CogX64Compiler->machineCode))[0] = 194; + ((self_in_CogX64Compiler->machineCode))[1] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[2] = (((usqInt)(offsetSqInt)) >> 8); + return 3; + + case Stop: + /* begin concretizeStop */ + ((self_in_CogX64Compiler->machineCode))[0] = 204; + return 1; + + case AddCqR: + return concretizeArithCqRWithROraxOpcode(self_in_CogX64Compiler, 0, 5); + + case AddcCqR: + return concretizeArithCqRWithROraxOpcode(self_in_CogX64Compiler, 2, 20); + + case AddCwR: + return concretizeArithCwR(self_in_CogX64Compiler, 3); + + case AddRR: + return concretizeOpRR(self_in_CogX64Compiler, 3); + + case AddRsRs: + /* begin concretizeSEEOpRsRs: */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 88; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 4; + + case AddRdRd: + /* begin concretizeSEE2OpRdRd: */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 242; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 88; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 4; + + case AndCqR: + return concretizeArithCqRWithROraxOpcode(self_in_CogX64Compiler, 4, 37); + + case AndCwR: + return concretizeArithCwR(self_in_CogX64Compiler, 35); + + case AndRR: + return concretizeOpRR(self_in_CogX64Compiler, 35); + + case TstCqR: + /* begin concretizeTstCqR */ + value = ((self_in_CogX64Compiler->operands))[0]; + reg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + if (isQuick(self_in_CogX64Compiler, value)) { + ((self_in_CogX64Compiler->machineCode))[1] = 246; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 0)); + ((self_in_CogX64Compiler->machineCode))[3] = (value & 0xFF); + return 4; + } + if (is32BitSignedImmediate(self_in_CogX64Compiler, value)) { + if (!reg) { + ((self_in_CogX64Compiler->machineCode))[1] = 169; + ((self_in_CogX64Compiler->machineCode))[2] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 24) & 0xFF); + return 6; + } + ((self_in_CogX64Compiler->machineCode))[1] = 247; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 0)); + ((self_in_CogX64Compiler->machineCode))[3] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(value)) >> 24) & 0xFF); + return 7; + } + return concretizeArithCwR(self_in_CogX64Compiler, 133); + + case CmpCqR: + return concretizeArithCqRWithROraxOpcode(self_in_CogX64Compiler, 7, 61); + + case CmpCwR: + return concretizeArithCwR(self_in_CogX64Compiler, 57); + + case CmpC32R: + /* begin concretizeCmpC32R */ + value = ((self_in_CogX64Compiler->operands))[0]; + reg = ((self_in_CogX64Compiler->operands))[1]; + if (reg) { + if (reg > 7) { + ((self_in_CogX64Compiler->machineCode))[0] = 65; + skip = 2; + } + else { + skip = 1; + } + ((self_in_CogX64Compiler->machineCode))[skip - 1] = 129; + ((self_in_CogX64Compiler->machineCode))[skip] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 7)); + } + else { + ((self_in_CogX64Compiler->machineCode))[0] = 61; + skip = 0; + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 2] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 3] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = ((((usqInt)(value)) >> 24) & 0xFF); + return 5 + skip; + + case CmpRR: + return concretizeReverseOpRR(self_in_CogX64Compiler, 57); + + case CmpRdRd: + /* begin concretizeCmpRdRd */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 102; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 46; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 4; + + case CmpRsRs: + /* begin concretizeCmpRsRs */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 46; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 3; + + case DivRdRd: + /* begin concretizeSEE2OpRdRd: */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 242; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 94; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 4; + + case DivRsRs: + /* begin concretizeSEEOpRsRs: */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 94; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 4; + + case MulRdRd: + /* begin concretizeSEE2OpRdRd: */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 242; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 89; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 4; + + case MulRsRs: + /* begin concretizeSEEOpRsRs: */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 89; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 4; + + case OrCqR: + return concretizeArithCqRWithROraxOpcode(self_in_CogX64Compiler, 1, 13); + + case OrCwR: + return concretizeArithCwR(self_in_CogX64Compiler, 11); + + case OrRR: + return concretizeOpRR(self_in_CogX64Compiler, 11); + + case SubCqR: + return concretizeArithCqRWithROraxOpcode(self_in_CogX64Compiler, 5, 45); + + case SubbCqR: + return concretizeArithCqRWithROraxOpcode(self_in_CogX64Compiler, 3, 29); + + case SubCwR: + return concretizeArithCwR(self_in_CogX64Compiler, 43); + + case SubRR: + return concretizeOpRR(self_in_CogX64Compiler, 43); + + case SubRdRd: + /* begin concretizeSEE2OpRdRd: */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 242; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 92; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 4; + + case SubRsRs: + /* begin concretizeSEEOpRsRs: */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 92; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 4; + + case SqrtRd: + /* begin concretizeSqrtRd */ + reg = ((self_in_CogX64Compiler->operands))[0]; + ((self_in_CogX64Compiler->machineCode))[0] = 242; + if (reg <= 7) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, reg, 0, reg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 81; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, reg)); + return skip + 4; + + case SqrtRs: + /* begin concretizeSqrtRs */ + reg = ((self_in_CogX64Compiler->operands))[0]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + if (reg <= 7) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, reg, 0, reg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 81; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, reg)); + return skip + 4; + + case XorCqR: + return concretizeArithCqRWithROraxOpcode(self_in_CogX64Compiler, 6, 35); + + case XorCwR: + return concretizeArithCwR(self_in_CogX64Compiler, 51); + + case XorRR: + return concretizeOpRR(self_in_CogX64Compiler, 51); + + case XorRdRd: + /* begin concretizeXorRdRd */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 102; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 87; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 4; + + case XorRsRs: + /* begin concretizeXorRsRs */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 15; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 87; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 3; + + case NegateR: + /* begin concretizeNegateR */ + reg = ((self_in_CogX64Compiler->operands))[0]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = 247; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 3)); + return 3; + + case LoadEffectiveAddressMwrR: + /* begin concretizeLoadEffectiveAddressMwrR */ + offsetSqInt = ((self_in_CogX64Compiler->operands))[0]; + srcReg = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 141; + if ((srcReg != RSP) + && (srcReg != R12)) { + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + return 4; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 7; + } + + /* ESP/R12: */ + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + return 5; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 8; + + case RotateLeftCqR: + /* begin concretizeShiftCqRegOpcode: */ + distance = ((self_in_CogX64Compiler->operands))[0]; + assert(((distance >= 1) && (distance <= 0x3F))); + reg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + if (distance == 1) { + ((self_in_CogX64Compiler->machineCode))[1] = 209; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 0)); + return 3; + } + ((self_in_CogX64Compiler->machineCode))[1] = 193; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 0)); + ((self_in_CogX64Compiler->machineCode))[3] = distance; + return 4; + + case RotateRightCqR: + /* begin concretizeShiftCqRegOpcode: */ + distance = ((self_in_CogX64Compiler->operands))[0]; + assert(((distance >= 1) && (distance <= 0x3F))); + reg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + if (distance == 1) { + ((self_in_CogX64Compiler->machineCode))[1] = 209; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 1)); + return 3; + } + ((self_in_CogX64Compiler->machineCode))[1] = 193; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 1)); + ((self_in_CogX64Compiler->machineCode))[3] = distance; + return 4; + + case ArithmeticShiftRightCqR: + /* begin concretizeShiftCqRegOpcode: */ + distance = ((self_in_CogX64Compiler->operands))[0]; + assert(((distance >= 1) && (distance <= 0x3F))); + reg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + if (distance == 1) { + ((self_in_CogX64Compiler->machineCode))[1] = 209; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 7)); + return 3; + } + ((self_in_CogX64Compiler->machineCode))[1] = 193; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 7)); + ((self_in_CogX64Compiler->machineCode))[3] = distance; + return 4; + + case LogicalShiftRightCqR: + /* begin concretizeShiftCqRegOpcode: */ + distance = ((self_in_CogX64Compiler->operands))[0]; + assert(((distance >= 1) && (distance <= 0x3F))); + reg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + if (distance == 1) { + ((self_in_CogX64Compiler->machineCode))[1] = 209; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 5)); + return 3; + } + ((self_in_CogX64Compiler->machineCode))[1] = 193; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 5)); + ((self_in_CogX64Compiler->machineCode))[3] = distance; + return 4; + + case LogicalShiftLeftCqR: + /* begin concretizeShiftCqRegOpcode: */ + distance = ((self_in_CogX64Compiler->operands))[0]; + assert(((distance >= 1) && (distance <= 0x3F))); + reg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + if (distance == 1) { + ((self_in_CogX64Compiler->machineCode))[1] = 209; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 4)); + return 3; + } + ((self_in_CogX64Compiler->machineCode))[1] = 193; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 4)); + ((self_in_CogX64Compiler->machineCode))[3] = distance; + return 4; + + case ArithmeticShiftRightRR: + /* begin concretizeShiftRegRegOpcode: */ + shiftCountReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + if (shiftCountReg == RCX) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, destReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 211; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, destReg, 7)); + return 3; + } + regToShift = (destReg == shiftCountReg + ? RCX + : (destReg == RCX + ? shiftCountReg + : destReg)); + if (!shiftCountReg) { + ((self_in_CogX64Compiler->machineCode))[0] = 72; + ((self_in_CogX64Compiler->machineCode))[1] = (144 + RCX); + ((self_in_CogX64Compiler->machineCode))[2] = (rexRxb(self_in_CogX64Compiler, 0, 0, regToShift)); + ((self_in_CogX64Compiler->machineCode))[3] = 211; + ((self_in_CogX64Compiler->machineCode))[4] = (modRMRO(self_in_CogX64Compiler, ModReg, regToShift, 7)); + ((self_in_CogX64Compiler->machineCode))[5] = 72; + ((self_in_CogX64Compiler->machineCode))[6] = (144 + RCX); + return 7; + } + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, shiftCountReg, 0, RCX)); + ((self_in_CogX64Compiler->machineCode))[1] = 135; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, RCX, shiftCountReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (rexRxb(self_in_CogX64Compiler, 0, 0, regToShift)); + ((self_in_CogX64Compiler->machineCode))[4] = 211; + ((self_in_CogX64Compiler->machineCode))[5] = (modRMRO(self_in_CogX64Compiler, ModReg, regToShift, 7)); + ((self_in_CogX64Compiler->machineCode))[6] = (rexRxb(self_in_CogX64Compiler, shiftCountReg, 0, RCX)); + ((self_in_CogX64Compiler->machineCode))[7] = 135; + ((self_in_CogX64Compiler->machineCode))[8] = (modRMRO(self_in_CogX64Compiler, ModReg, RCX, shiftCountReg)); + + /* XCHG R?X,RCX + SAR RCX,R!X + XCHG R?X,RCX */ + return 9; + + case LogicalShiftLeftRR: + /* begin concretizeShiftRegRegOpcode: */ + shiftCountReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + if (shiftCountReg == RCX) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, destReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 211; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, destReg, 4)); + return 3; + } + regToShift = (destReg == shiftCountReg + ? RCX + : (destReg == RCX + ? shiftCountReg + : destReg)); + if (!shiftCountReg) { + ((self_in_CogX64Compiler->machineCode))[0] = 72; + ((self_in_CogX64Compiler->machineCode))[1] = (144 + RCX); + ((self_in_CogX64Compiler->machineCode))[2] = (rexRxb(self_in_CogX64Compiler, 0, 0, regToShift)); + ((self_in_CogX64Compiler->machineCode))[3] = 211; + ((self_in_CogX64Compiler->machineCode))[4] = (modRMRO(self_in_CogX64Compiler, ModReg, regToShift, 4)); + ((self_in_CogX64Compiler->machineCode))[5] = 72; + ((self_in_CogX64Compiler->machineCode))[6] = (144 + RCX); + return 7; + } + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, shiftCountReg, 0, RCX)); + ((self_in_CogX64Compiler->machineCode))[1] = 135; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, RCX, shiftCountReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (rexRxb(self_in_CogX64Compiler, 0, 0, regToShift)); + ((self_in_CogX64Compiler->machineCode))[4] = 211; + ((self_in_CogX64Compiler->machineCode))[5] = (modRMRO(self_in_CogX64Compiler, ModReg, regToShift, 4)); + ((self_in_CogX64Compiler->machineCode))[6] = (rexRxb(self_in_CogX64Compiler, shiftCountReg, 0, RCX)); + ((self_in_CogX64Compiler->machineCode))[7] = 135; + ((self_in_CogX64Compiler->machineCode))[8] = (modRMRO(self_in_CogX64Compiler, ModReg, RCX, shiftCountReg)); + + /* XCHG R?X,RCX + SAR RCX,R!X + XCHG R?X,RCX */ + return 9; + + case ClzRR: + /* begin concretizeClzRR */ + assert(hasLZCNTInstructions(self_in_CogX64Compiler)); + maskReg = ((self_in_CogX64Compiler->operands))[0]; + dest = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + ((self_in_CogX64Compiler->machineCode))[1] = (rexwrxb(self_in_CogX64Compiler, 1, dest, 0, maskReg)); + ((self_in_CogX64Compiler->machineCode))[2] = 15; + ((self_in_CogX64Compiler->machineCode))[3] = 189; + ((self_in_CogX64Compiler->machineCode))[4] = (modRMRO(self_in_CogX64Compiler, ModReg, maskReg, dest)); + return 5; + + case MoveCqR: + /* begin concretizeMoveCqR */ + value = ((self_in_CogX64Compiler->operands))[0]; + reg = ((self_in_CogX64Compiler->operands))[1]; + if (is32BitSignedImmediate(self_in_CogX64Compiler, value)) { + if (!value) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, reg, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = 49; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, reg)); + return 3; + } + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = 199; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 0)); + ((self_in_CogX64Compiler->machineCode))[3] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(value)) >> 24) & 0xFF); + return 7; + } + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = (184 + (reg & 7)); + ((self_in_CogX64Compiler->machineCode))[2] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(value)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(value)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8] = ((((usqInt)(value)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[9] = ((((usqInt)(value)) >> 56) & 0xFF); + return 10; + + case MoveCwR: + /* begin concretizeMoveCwR */ + value = ((self_in_CogX64Compiler->operands))[0]; + reg = ((self_in_CogX64Compiler->operands))[1]; + if (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) value))) + || ((((AbstractInstruction *) value)) == (methodLabel))) { + value = ((((AbstractInstruction *) value))->address); + } + if (/* addressIsInCurrentCompilation: */ + ((((usqInt)value)) >= ((methodLabel->address))) + && ((((usqInt)value)) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize))))) { + offsetSqInt = value - (((self_in_CogX64Compiler->address)) + 7); + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, reg, 0, 0)); + ((self_in_CogX64Compiler->machineCode))[1] = 141; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, 5, reg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + + /* LoadEffectiveAddress */ + return 7; + } + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = (184 + (reg & 7)); + ((self_in_CogX64Compiler->machineCode))[2] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(value)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(value)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8] = ((((usqInt)(value)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[9] = ((((usqInt)(value)) >> 56) & 0xFF); + + /* Add a nop to disambiguate between MoveCwR/PushCw and ArithCwR, which ends with a (self mod: ModReg RM: 0 RO: 0) */ + ((self_in_CogX64Compiler->machineCode))[10] = 144; + return 11; + + case MoveC32R: + /* begin concretizeMoveC32R */ + value = ((self_in_CogX64Compiler->operands))[0]; + reg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = 199; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 0)); + ((self_in_CogX64Compiler->machineCode))[3] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(value)) >> 24) & 0xFF); + return 7; + + case MoveRR: + return concretizeReverseOpRR(self_in_CogX64Compiler, 137); + + case MoveAwR: + /* begin concretizeMoveAwR */ + addressOperand = ((self_in_CogX64Compiler->operands))[0]; + if (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) addressOperand))) + || ((((AbstractInstruction *) addressOperand)) == (methodLabel))) { + addressOperand = ((((AbstractInstruction *) addressOperand))->address); + } + if (/* isAddressRelativeToVarBase: */ + (addressOperand >= (varBaseAddress)) + && ((addressOperand - (varBaseAddress)) < (0x100000))) { + save0 = ((self_in_CogX64Compiler->operands))[0]; + save1 = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->operands))[0] = (addressOperand - (varBaseAddress)); + ((self_in_CogX64Compiler->operands))[1] = RBX; + ((self_in_CogX64Compiler->operands))[2] = save1; + + /* begin concretizeMoveMwrR */ + offsetSqInt = ((self_in_CogX64Compiler->operands))[0]; + srcReg = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 139; + if ((srcReg != RSP) + && (srcReg != R12)) { + if ((offsetSqInt == 0) + && ((srcReg != RBP) + && (srcReg != R13))) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + savedSize = 3; + goto l1; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + savedSize = 4; + goto l1; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + savedSize = 7; + goto l1; + } + + /* RSP: */ + if (!offsetSqInt) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + savedSize = 4; + goto l1; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + savedSize = 5; + goto l1; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + savedSize = 8; + /* end concretizeMoveMwrR */ +l1: + ((self_in_CogX64Compiler->operands))[0] = save0; + ((self_in_CogX64Compiler->operands))[1] = save1; + ((self_in_CogX64Compiler->operands))[2] = 0; + return savedSize; + } + reg = ((self_in_CogX64Compiler->operands))[1]; + + /* If fetching RAX, fetch directly, otherwise, because of instruction encoding limitations, the register + _must_ be fetched through RAX. If reg = RBP or RSP simply fetch directly, otherwise swap RAX with + the register before and after the fetch through RAX. We avoid swapping before hand with RBP + and RSP because setting RSP to whatever the contents of RAX is can cause disastrous results if + an interrupt is delivered immediately after that point. See mail threads beginning with + http://lists.squeakfoundation.org/pipermail/vm-dev/2019-September/031428.html + http://lists.squeakfoundation.org/pipermail/vm-dev/2019-October/031499.html */ + if ((reg == RAX) + || ((reg == RBP) + || (reg == RSP))) { + offset1 = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = (144 + (reg % 8)); + offset1 = 2; + } + ((self_in_CogX64Compiler->machineCode))[0 + offset1] = 72; + ((self_in_CogX64Compiler->machineCode))[1 + offset1] = 161; + ((self_in_CogX64Compiler->machineCode))[2 + offset1] = (addressOperand & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3 + offset1] = ((((usqInt)(addressOperand)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4 + offset1] = ((((usqInt)(addressOperand)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5 + offset1] = ((((usqInt)(addressOperand)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6 + offset1] = ((((usqInt)(addressOperand)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7 + offset1] = ((((usqInt)(addressOperand)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8 + offset1] = ((((usqInt)(addressOperand)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[9 + offset1] = ((((usqInt)(addressOperand)) >> 56) & 0xFF); + if (!reg) { + return 10; + } + + /* Now effect the assignment via xchg, which saves a byte over a move */ + if ((reg == RBP) + || (reg == RSP)) { + ((self_in_CogX64Compiler->machineCode))[10] = (rexRxb(self_in_CogX64Compiler, RAX, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[11] = (144 + (reg % 8)); + return 12; + } + ((self_in_CogX64Compiler->machineCode))[12] = (((self_in_CogX64Compiler->machineCode))[0]); + ((self_in_CogX64Compiler->machineCode))[13] = (((self_in_CogX64Compiler->machineCode))[1]); + return 14; + + case MoveA32R: + /* begin concretizeMoveA32R */ + addressOperand = ((self_in_CogX64Compiler->operands))[0]; + if (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) addressOperand))) + || ((((AbstractInstruction *) addressOperand)) == (methodLabel))) { + addressOperand = ((((AbstractInstruction *) addressOperand))->address); + } + reg = ((self_in_CogX64Compiler->operands))[1]; + if (reg) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = (144 + (reg % 8)); + offsetSqInt = 2; + } + else { + offsetSqInt = 0; + } + ((self_in_CogX64Compiler->machineCode))[0 + offsetSqInt] = 161; + ((self_in_CogX64Compiler->machineCode))[1 + offsetSqInt] = (addressOperand & 0xFF); + ((self_in_CogX64Compiler->machineCode))[2 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 56) & 0xFF); + if (!reg) { + return 9; + } + ((self_in_CogX64Compiler->machineCode))[11] = (((self_in_CogX64Compiler->machineCode))[0]); + ((self_in_CogX64Compiler->machineCode))[12] = (((self_in_CogX64Compiler->machineCode))[1]); + return 13; + + case MoveRAw: + /* begin concretizeMoveRAw */ + reg = ((self_in_CogX64Compiler->operands))[0]; + addressOperand = ((self_in_CogX64Compiler->operands))[1]; + if (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) addressOperand))) + || ((((AbstractInstruction *) addressOperand)) == (methodLabel))) { + addressOperand = ((((AbstractInstruction *) addressOperand))->address); + } + if (/* isAddressRelativeToVarBase: */ + (addressOperand >= (varBaseAddress)) + && ((addressOperand - (varBaseAddress)) < (0x100000))) { + save1 = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->operands))[1] = (addressOperand - (varBaseAddress)); + ((self_in_CogX64Compiler->operands))[2] = RBX; + + /* begin concretizeMoveRMwr */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + offsetSqInt = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, srcReg, 0, destReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 137; + if ((destReg != RSP) + && (destReg != R12)) { + if ((offsetSqInt == 0) + && ((destReg != RBP) + && (destReg != R13))) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, destReg, srcReg)); + savedSize = 3; + goto l2; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + savedSize = 4; + goto l2; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + savedSize = 7; + goto l2; + } + + /* RSP: */ + if (!offsetSqInt) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + savedSize = 4; + goto l2; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + savedSize = 5; + goto l2; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + savedSize = 8; + /* end concretizeMoveRMwr */ +l2: + ((self_in_CogX64Compiler->operands))[1] = save1; + ((self_in_CogX64Compiler->operands))[2] = 0; + return savedSize; + } + + /* If storing RAX, store directly, otherwise, because of instruction encoding limitations, the register + _must_ be stored through RAX. If reg = RBP or RSP simply store directly, otherwise swap RAX with + the register before and after the store through RAX. We avoid swapping before hand with RBP + and RSP because setting RSP to whatever the contents of RAX is can cause disastrous results if + an interrupt is delivered immediately after that point. See mail threads beginning with + http://lists.squeakfoundation.org/pipermail/vm-dev/2019-September/031428.html + http://lists.squeakfoundation.org/pipermail/vm-dev/2019-October/031499.html */ + if ((reg == RAX) + || ((reg == RBP) + || (reg == RSP))) { + offset1 = 0; + } + else { + if ((reg == RBP) + || (reg == RSP)) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, reg, 0, RAX)); + ((self_in_CogX64Compiler->machineCode))[1] = 137; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, RAX, reg)); + offset1 = 3; + } + else { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, RAX, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = (144 + (reg % 8)); + offset1 = 2; + } + } + ((self_in_CogX64Compiler->machineCode))[0 + offset1] = 72; + ((self_in_CogX64Compiler->machineCode))[1 + offset1] = 163; + ((self_in_CogX64Compiler->machineCode))[2 + offset1] = (addressOperand & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3 + offset1] = ((((usqInt)(addressOperand)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4 + offset1] = ((((usqInt)(addressOperand)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5 + offset1] = ((((usqInt)(addressOperand)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6 + offset1] = ((((usqInt)(addressOperand)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7 + offset1] = ((((usqInt)(addressOperand)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8 + offset1] = ((((usqInt)(addressOperand)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[9 + offset1] = ((((usqInt)(addressOperand)) >> 56) & 0xFF); + if (!reg) { + return 10; + } + if ((reg == RBP) + || (reg == RSP)) { + return 13; + } + + /* Now effect the assignment via xchg, which restores RAX */ + ((self_in_CogX64Compiler->machineCode))[12] = (((self_in_CogX64Compiler->machineCode))[0]); + ((self_in_CogX64Compiler->machineCode))[13] = (((self_in_CogX64Compiler->machineCode))[1]); + return 14; + + case MoveRA32: + /* begin concretizeMoveRA32 */ + reg = ((self_in_CogX64Compiler->operands))[0]; + addressOperand = ((self_in_CogX64Compiler->operands))[1]; + if (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) addressOperand))) + || ((((AbstractInstruction *) addressOperand)) == (methodLabel))) { + addressOperand = ((((AbstractInstruction *) addressOperand))->address); + } + if (reg) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = (144 + (reg % 8)); + offsetSqInt = 2; + } + else { + offsetSqInt = 0; + } + ((self_in_CogX64Compiler->machineCode))[0 + offsetSqInt] = 163; + ((self_in_CogX64Compiler->machineCode))[1 + offsetSqInt] = (addressOperand & 0xFF); + ((self_in_CogX64Compiler->machineCode))[2 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 56) & 0xFF); + if (!reg) { + return 9; + } + ((self_in_CogX64Compiler->machineCode))[11] = (((self_in_CogX64Compiler->machineCode))[0]); + ((self_in_CogX64Compiler->machineCode))[12] = (((self_in_CogX64Compiler->machineCode))[1]); + return 13; + + case MoveAbR: + /* begin concretizeMoveAbR */ + addressOperand = ((self_in_CogX64Compiler->operands))[0]; + if (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) addressOperand))) + || ((((AbstractInstruction *) addressOperand)) == (methodLabel))) { + addressOperand = ((((AbstractInstruction *) addressOperand))->address); + } + if (/* isAddressRelativeToVarBase: */ + (addressOperand >= (varBaseAddress)) + && ((addressOperand - (varBaseAddress)) < (0x100000))) { + save0 = ((self_in_CogX64Compiler->operands))[0]; + save1 = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->operands))[0] = (addressOperand - (varBaseAddress)); + ((self_in_CogX64Compiler->operands))[1] = RBX; + ((self_in_CogX64Compiler->operands))[2] = save1; + + /* begin concretizeMoveMbrR */ + offsetSqInt = ((self_in_CogX64Compiler->operands))[0]; + srcReg = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 138; + if ((srcReg != RSP) + && (srcReg != R12)) { + if ((offsetSqInt == 0) + && ((srcReg != RBP) + && (srcReg != R13))) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + savedSize = 3; + goto l3; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + savedSize = 4; + goto l3; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + savedSize = 7; + goto l3; + } + + /* RSP: */ + if (!offsetSqInt) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + savedSize = 4; + goto l3; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + savedSize = 5; + goto l3; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + savedSize = 8; + /* end concretizeMoveMbrR */ +l3: + ((self_in_CogX64Compiler->operands))[0] = save0; + ((self_in_CogX64Compiler->operands))[1] = save1; + ((self_in_CogX64Compiler->operands))[2] = 0; + return savedSize; + } + reg = ((self_in_CogX64Compiler->operands))[1]; + if (reg) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = (144 + (reg % 8)); + offset1 = 2; + } + else { + offset1 = 0; + } + ((self_in_CogX64Compiler->machineCode))[0 + offset1] = 72; + ((self_in_CogX64Compiler->machineCode))[1 + offset1] = 160; + ((self_in_CogX64Compiler->machineCode))[2 + offset1] = (addressOperand & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3 + offset1] = ((((usqInt)(addressOperand)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4 + offset1] = ((((usqInt)(addressOperand)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5 + offset1] = ((((usqInt)(addressOperand)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6 + offset1] = ((((usqInt)(addressOperand)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7 + offset1] = ((((usqInt)(addressOperand)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8 + offset1] = ((((usqInt)(addressOperand)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[9 + offset1] = ((((usqInt)(addressOperand)) >> 56) & 0xFF); + if (!reg) { + return 10; + } + ((self_in_CogX64Compiler->machineCode))[12] = (((self_in_CogX64Compiler->machineCode))[0]); + ((self_in_CogX64Compiler->machineCode))[13] = (((self_in_CogX64Compiler->machineCode))[1]); + return 14; + + case MoveRAb: + /* begin concretizeMoveRAb */ + reg = ((self_in_CogX64Compiler->operands))[0]; + addressOperand = ((self_in_CogX64Compiler->operands))[1]; + if (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) addressOperand))) + || ((((AbstractInstruction *) addressOperand)) == (methodLabel))) { + addressOperand = ((((AbstractInstruction *) addressOperand))->address); + } + if (/* isAddressRelativeToVarBase: */ + (addressOperand >= (varBaseAddress)) + && ((addressOperand - (varBaseAddress)) < (0x100000))) { + save1 = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->operands))[1] = (addressOperand - (varBaseAddress)); + ((self_in_CogX64Compiler->operands))[2] = RBX; + + /* begin concretizeMoveRMbr */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + offsetSqInt = ((self_in_CogX64Compiler->operands))[1]; + baseReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, srcReg, 0, baseReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 136; + if ((baseReg != RSP) + && (baseReg != R12)) { + if ((offsetSqInt == 0) + && ((baseReg != RBP) + && (baseReg != R13))) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, baseReg, srcReg)); + savedSize = 3; + goto l4; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, baseReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + savedSize = 4; + goto l4; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, baseReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + savedSize = 7; + goto l4; + } + + /* RSP: */ + if (!offsetSqInt) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, baseReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, baseReg)); + savedSize = 4; + goto l4; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, baseReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, baseReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + savedSize = 5; + goto l4; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, baseReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, baseReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + savedSize = 8; + /* end concretizeMoveRMbr */ +l4: + ((self_in_CogX64Compiler->operands))[1] = save1; + ((self_in_CogX64Compiler->operands))[2] = 0; + return savedSize; + } + if (reg) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = (144 + (reg % 8)); + offset1 = 2; + } + else { + offset1 = 0; + } + ((self_in_CogX64Compiler->machineCode))[0 + offset1] = 72; + ((self_in_CogX64Compiler->machineCode))[1 + offset1] = 162; + ((self_in_CogX64Compiler->machineCode))[2 + offset1] = (addressOperand & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3 + offset1] = ((((usqInt)(addressOperand)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4 + offset1] = ((((usqInt)(addressOperand)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5 + offset1] = ((((usqInt)(addressOperand)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6 + offset1] = ((((usqInt)(addressOperand)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7 + offset1] = ((((usqInt)(addressOperand)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8 + offset1] = ((((usqInt)(addressOperand)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[9 + offset1] = ((((usqInt)(addressOperand)) >> 56) & 0xFF); + if (!reg) { + return 10; + } + ((self_in_CogX64Compiler->machineCode))[12] = (((self_in_CogX64Compiler->machineCode))[0]); + ((self_in_CogX64Compiler->machineCode))[13] = (((self_in_CogX64Compiler->machineCode))[1]); + return 14; + + case MoveMbrR: + case MoveM8rR: + /* begin concretizeMoveMbrR */ + offsetSqInt = ((self_in_CogX64Compiler->operands))[0]; + srcReg = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 138; + if ((srcReg != RSP) + && (srcReg != R12)) { + if ((offsetSqInt == 0) + && ((srcReg != RBP) + && (srcReg != R13))) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + return 3; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + return 4; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 7; + } + + /* RSP: */ + if (!offsetSqInt) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + return 4; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + return 5; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 8; + + case MoveRMbr: + case MoveRM8r: + /* begin concretizeMoveRMbr */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + offsetSqInt = ((self_in_CogX64Compiler->operands))[1]; + baseReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, srcReg, 0, baseReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 136; + if ((baseReg != RSP) + && (baseReg != R12)) { + if ((offsetSqInt == 0) + && ((baseReg != RBP) + && (baseReg != R13))) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, baseReg, srcReg)); + return 3; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, baseReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + return 4; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, baseReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 7; + } + + /* RSP: */ + if (!offsetSqInt) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, baseReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, baseReg)); + return 4; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, baseReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, baseReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + return 5; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, baseReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, baseReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 8; + + case MoveM16rR: + /* begin concretizeMoveM16rR */ + offsetSqInt = ((self_in_CogX64Compiler->operands))[0]; + srcReg = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 15; + ((self_in_CogX64Compiler->machineCode))[2] = 183; + if ((srcReg != RSP) + && (srcReg != R12)) { + if ((offsetSqInt == 0) + && ((srcReg != RBP) + && (srcReg != R13))) { + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + return 4; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + return 5; + } + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 8; + } + + /* RSP & R12: */ + if ((offsetSqInt == 0) + && ((srcReg != RBP) + && (srcReg != R13))) { + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + return 5; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[5] = (offsetSqInt & 0xFF); + return 6; + } + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[5] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 9; + + case MoveRM16r: + /* begin concretizeMoveRM16r */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + offsetSqInt = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = 102; + if ((srcReg > 7) + || (destReg > 7)) { + ((self_in_CogX64Compiler->machineCode))[1] = (rexwrxb(self_in_CogX64Compiler, 0, srcReg, 0, destReg)); + skip = 1; + } + else { + skip = 0; + } + if ((destReg & 7) != RSP) { + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 137; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (offsetSqInt & 0xFF); + return skip + 4; + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 137; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return skip + 7; + } + + /* RSP: */ + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 137; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (offsetSqInt & 0xFF); + return skip + 5; + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 137; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return skip + 8; + + case MoveM32rR: + /* begin concretizeMoveM32rR */ + offsetSqInt = ((self_in_CogX64Compiler->operands))[0]; + srcReg = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + skip = 1; + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 0, destReg, 0, srcReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip] = 139; + if (!offsetSqInt) { + if ((srcReg & 6) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + return skip + 2; + } + if ((srcReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + return skip + 3; + } + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + if ((srcReg & 7) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (offsetSqInt & 0xFF); + return skip + 3; + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (offsetSqInt & 0xFF); + return skip + 4; + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + if ((srcReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + skip += 1; + } + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return skip + 6; + + case MoveM32rRs: + /* begin concretizeMoveM32rRs */ + offsetSqInt = ((self_in_CogX64Compiler->operands))[0]; + srcReg = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = 102; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, destReg, 0, srcReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 110; + if (!offsetSqInt) { + if ((srcReg & 6) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + return skip + 4; + } + if ((srcReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + return skip + 5; + } + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + if ((srcReg & 7) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (offsetSqInt & 0xFF); + return skip + 5; + } + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = (offsetSqInt & 0xFF); + return skip + 6; + } + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + if ((srcReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + skip += 1; + } + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return skip + 8; + + case MoveM64rRd: + /* begin concretizeMoveM64rRd */ + offsetSqInt = ((self_in_CogX64Compiler->operands))[0]; + srcReg = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, destReg, 0, srcReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 0x7E; + if (!offsetSqInt) { + if ((srcReg & 6) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + return skip + 4; + } + if ((srcReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + return skip + 5; + } + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + if ((srcReg & 7) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (offsetSqInt & 0xFF); + return skip + 5; + } + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = (offsetSqInt & 0xFF); + return skip + 6; + } + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + if ((srcReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + skip += 1; + } + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return skip + 8; + + case MoveMwrR: + /* begin concretizeMoveMwrR */ + offsetSqInt = ((self_in_CogX64Compiler->operands))[0]; + srcReg = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 139; + if ((srcReg != RSP) + && (srcReg != R12)) { + if ((offsetSqInt == 0) + && ((srcReg != RBP) + && (srcReg != R13))) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + return 3; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + return 4; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 7; + } + + /* RSP: */ + if (!offsetSqInt) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + return 4; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + return 5; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 8; + + case MoveXbrRR: + /* begin concretizeMoveXbrRR */ + index = ((self_in_CogX64Compiler->operands))[0]; + base = ((self_in_CogX64Compiler->operands))[1]; + dest = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, dest, index, base)); + ((self_in_CogX64Compiler->machineCode))[1] = 138; + if ((base != RBP) + && (base != R13)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, 4, dest)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, index, base)); + return 4; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, 4, dest)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, index, base)); + ((self_in_CogX64Compiler->machineCode))[4] = 0; + return 5; + + case MoveRXbrR: + /* begin concretizeMoveRXbrR */ + src = ((self_in_CogX64Compiler->operands))[0]; + index = ((self_in_CogX64Compiler->operands))[1]; + base = ((self_in_CogX64Compiler->operands))[2]; + offsetSqInt = 0; + if ((src > 3) + || ((base > 7) + || (index > 7))) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, src, index, base)); + offsetSqInt = 1; + } + ((self_in_CogX64Compiler->machineCode))[0 + offsetSqInt] = 136; + if ((base & 7) != RBP) { + ((self_in_CogX64Compiler->machineCode))[1 + offsetSqInt] = (modRMRO(self_in_CogX64Compiler, ModRegInd, 4, src)); + ((self_in_CogX64Compiler->machineCode))[2 + offsetSqInt] = (sib(self_in_CogX64Compiler, SIB1, index, base)); + return 3 + offsetSqInt; + } + + /* RBP,R13 */ + ((self_in_CogX64Compiler->machineCode))[1 + offsetSqInt] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, 4, src)); + ((self_in_CogX64Compiler->machineCode))[2 + offsetSqInt] = (sib(self_in_CogX64Compiler, SIB1, index, base)); + ((self_in_CogX64Compiler->machineCode))[3 + offsetSqInt] = 0; + return 4 + offsetSqInt; + + case MoveXwrRR: + /* begin concretizeMoveXwrRR */ + index = ((self_in_CogX64Compiler->operands))[0]; + base = ((self_in_CogX64Compiler->operands))[1]; + dest = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, dest, index, base)); + if ((base != RBP) + && (base != R13)) { + ((self_in_CogX64Compiler->machineCode))[1] = 139; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, 4, dest)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB8, index, base)); + return 4; + } + ((self_in_CogX64Compiler->machineCode))[1] = 139; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, 4, dest)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB8, index, base)); + ((self_in_CogX64Compiler->machineCode))[4] = 0; + return 5; + + case MoveRXwrR: + /* begin concretizeMoveRXwrR */ + src = ((self_in_CogX64Compiler->operands))[0]; + index = ((self_in_CogX64Compiler->operands))[1]; + base = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, src, index, base)); + if ((base != RBP) + && (base != R13)) { + ((self_in_CogX64Compiler->machineCode))[1] = 137; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, 4, src)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB8, index, base)); + return 4; + } + ((self_in_CogX64Compiler->machineCode))[1] = 137; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, 4, src)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB8, index, base)); + ((self_in_CogX64Compiler->machineCode))[4] = 0; + return 5; + + case MoveX32rRR: + return concretizeMoveX32rRR(self_in_CogX64Compiler); + + case MoveRX32rR: + return concretizeMoveRX32rR(self_in_CogX64Compiler); + + case MoveRMwr: + /* begin concretizeMoveRMwr */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + offsetSqInt = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, srcReg, 0, destReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 137; + if ((destReg != RSP) + && (destReg != R12)) { + if ((offsetSqInt == 0) + && ((destReg != RBP) + && (destReg != R13))) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, destReg, srcReg)); + return 3; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + return 4; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 7; + } + + /* RSP: */ + if (!offsetSqInt) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + return 4; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + return 5; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 8; + + case MoveRM32r: + /* begin concretizeMoveRM32r */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + offsetSqInt = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + skip = 1; + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 0, srcReg, 0, destReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip] = 137; + if (!offsetSqInt) { + if ((destReg & 6) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModRegInd, destReg, srcReg)); + return skip + 2; + } + if ((destReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModRegInd, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + return skip + 3; + } + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + if ((destReg & 7) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (offsetSqInt & 0xFF); + return skip + 3; + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (offsetSqInt & 0xFF); + return skip + 4; + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, destReg, srcReg)); + if ((destReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + skip += 1; + } + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return skip + 6; + + case MoveRsM32r: + /* begin concretizeMoveRsM32r */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + offsetSqInt = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = 102; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, srcReg, 0, destReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 0x7E; + if (!offsetSqInt) { + if ((destReg & 6) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegInd, destReg, srcReg)); + return skip + 4; + } + if ((destReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegInd, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + return skip + 5; + } + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + if ((destReg & 7) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (offsetSqInt & 0xFF); + return skip + 5; + } + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = (offsetSqInt & 0xFF); + return skip + 6; + } + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, destReg, srcReg)); + if ((destReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + skip += 1; + } + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return skip + 8; + + case MoveRdM64r: + /* begin concretizeMoveRdM64r */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + offsetSqInt = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = 102; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, srcReg, 0, destReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 214; + if (!offsetSqInt) { + if ((destReg & 6) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegInd, destReg, srcReg)); + return skip + 4; + } + if ((destReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegInd, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + return skip + 5; + } + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + if ((destReg & 7) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (offsetSqInt & 0xFF); + return skip + 5; + } + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = (offsetSqInt & 0xFF); + return skip + 6; + } + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, destReg, srcReg)); + if ((destReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + skip += 1; + } + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return skip + 8; + + case MoveRdR: + /* begin concretizeMoveRdR */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 102; + ((self_in_CogX64Compiler->machineCode))[1] = (rexRxb(self_in_CogX64Compiler, srcReg, 0, destReg)); + ((self_in_CogX64Compiler->machineCode))[2] = 15; + ((self_in_CogX64Compiler->machineCode))[3] = 0x7E; + ((self_in_CogX64Compiler->machineCode))[4] = (modRMRO(self_in_CogX64Compiler, ModReg, destReg, srcReg)); + return 5; + + case MoveRRd: + /* begin concretizeMoveRRd */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 102; + ((self_in_CogX64Compiler->machineCode))[1] = (rexRxb(self_in_CogX64Compiler, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[2] = 15; + ((self_in_CogX64Compiler->machineCode))[3] = 110; + ((self_in_CogX64Compiler->machineCode))[4] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return 5; + + case MoveRdRd: + /* begin concretizeMoveRdRd */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 242; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, srcReg, 0, destReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 17; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, destReg, srcReg)); + return skip + 4; + + case MoveRsRs: + /* begin concretizeMoveRsRs */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, srcReg, 0, destReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 17; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, destReg, srcReg)); + return skip + 4; + + case PopR: + /* begin concretizePopR */ + reg = ((self_in_CogX64Compiler->operands))[0]; + if (reg < 8) { + ((self_in_CogX64Compiler->machineCode))[0] = (88 + reg); + return 1; + } + ((self_in_CogX64Compiler->machineCode))[0] = 65; + ((self_in_CogX64Compiler->machineCode))[1] = (88 + (reg - 8)); + return 2; + + case PushR: + /* begin concretizePushR */ + reg = ((self_in_CogX64Compiler->operands))[0]; + if (reg < 8) { + ((self_in_CogX64Compiler->machineCode))[0] = (80 + reg); + return 1; + } + ((self_in_CogX64Compiler->machineCode))[0] = 65; + ((self_in_CogX64Compiler->machineCode))[1] = (80 + (reg - 8)); + return 2; + + case PushCq: + /* begin concretizePushCq */ + value = ((self_in_CogX64Compiler->operands))[0]; + if (isQuick(self_in_CogX64Compiler, value)) { + ((self_in_CogX64Compiler->machineCode))[0] = 106; + ((self_in_CogX64Compiler->machineCode))[1] = (value & 0xFF); + return 2; + } + if (is32BitSignedImmediate(self_in_CogX64Compiler, value)) { + ((self_in_CogX64Compiler->machineCode))[0] = 104; + ((self_in_CogX64Compiler->machineCode))[1] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[2] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 24) & 0xFF); + return 5; + } + + /* begin concretizePushCw */ + valueSqInt = ((self_in_CogX64Compiler->operands))[0]; + if (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) valueSqInt))) + || ((((AbstractInstruction *) valueSqInt)) == (methodLabel))) { + valueSqInt = ((((AbstractInstruction *) valueSqInt))->address); + } + if (/* addressIsInCurrentCompilation: */ + ((((usqInt)valueSqInt)) >= ((methodLabel->address))) + && ((((usqInt)valueSqInt)) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize))))) { + offsetSqInt = valueSqInt - (((self_in_CogX64Compiler->address)) + 7); + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, RISCTempReg, 0, 0)); + ((self_in_CogX64Compiler->machineCode))[1] = 141; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, 5, RISCTempReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = 65; + ((self_in_CogX64Compiler->machineCode))[8] = (72 + RISCTempReg); + + /* LoadEffectiveAddress */ + return 9; + } + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, RISCTempReg, 0, RISCTempReg)); + ((self_in_CogX64Compiler->machineCode))[1] = (184 + (RISCTempReg & 7)); + ((self_in_CogX64Compiler->machineCode))[2] = (valueSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(valueSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(valueSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(valueSqInt)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(valueSqInt)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(valueSqInt)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8] = ((((usqInt)(valueSqInt)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[9] = ((((usqInt)(valueSqInt)) >> 56) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[10] = 65; + ((self_in_CogX64Compiler->machineCode))[11] = (72 + RISCTempReg); + assert((((self_in_CogX64Compiler->machineCode))[11]) < 144); + return 12; + + case PushCw: + /* begin concretizePushCw */ + value = ((self_in_CogX64Compiler->operands))[0]; + if (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) value))) + || ((((AbstractInstruction *) value)) == (methodLabel))) { + value = ((((AbstractInstruction *) value))->address); + } + if (/* addressIsInCurrentCompilation: */ + ((((usqInt)value)) >= ((methodLabel->address))) + && ((((usqInt)value)) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize))))) { + offsetSqInt = value - (((self_in_CogX64Compiler->address)) + 7); + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, RISCTempReg, 0, 0)); + ((self_in_CogX64Compiler->machineCode))[1] = 141; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, 5, RISCTempReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = 65; + ((self_in_CogX64Compiler->machineCode))[8] = (72 + RISCTempReg); + + /* LoadEffectiveAddress */ + return 9; + } + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, RISCTempReg, 0, RISCTempReg)); + ((self_in_CogX64Compiler->machineCode))[1] = (184 + (RISCTempReg & 7)); + ((self_in_CogX64Compiler->machineCode))[2] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(value)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(value)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8] = ((((usqInt)(value)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[9] = ((((usqInt)(value)) >> 56) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[10] = 65; + ((self_in_CogX64Compiler->machineCode))[11] = (72 + RISCTempReg); + assert((((self_in_CogX64Compiler->machineCode))[11]) < 144); + return 12; + + case PrefetchAw: + return concretizePrefetchAw(self_in_CogX64Compiler); + + case ConvertRRd: + /* begin concretizeConvertRRd */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 242; + ((self_in_CogX64Compiler->machineCode))[1] = (rexRxb(self_in_CogX64Compiler, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[2] = 15; + ((self_in_CogX64Compiler->machineCode))[3] = 42; + ((self_in_CogX64Compiler->machineCode))[4] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return 5; + + case ConvertRdR: + /* begin concretizeConvertRdR */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 242; + ((self_in_CogX64Compiler->machineCode))[1] = (rexRxb(self_in_CogX64Compiler, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[2] = 15; + ((self_in_CogX64Compiler->machineCode))[3] = 45; + ((self_in_CogX64Compiler->machineCode))[4] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return 5; + + case ConvertRRs: + /* begin concretizeConvertRRs */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, destReg, 0, srcReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 42; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return skip + 4; + + case ConvertRsR: + /* begin concretizeConvertRsR */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, destReg, 0, srcReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 45; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return skip + 4; + + case ConvertRsRd: + /* begin concretizeConvertRsRd */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, destReg, 0, srcReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 90; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return skip + 4; + + case ConvertRdRs: + /* begin concretizeConvertRdRs */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 242; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, destReg, 0, srcReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 90; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return skip + 4; + + case SignExtend8RR: + /* begin concretizeSignExtend8RR */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 1, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 15; + ((self_in_CogX64Compiler->machineCode))[2] = 190; + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return 4; + + case SignExtend16RR: + /* begin concretizeSignExtend16RR */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 1, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 15; + ((self_in_CogX64Compiler->machineCode))[2] = 191; + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return 4; + + case SignExtend32RR: + /* begin concretizeSignExtend32RR */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 1, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 99; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return 3; + + case ZeroExtend8RR: + /* begin concretizeZeroExtend8RR */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 1, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 15; + ((self_in_CogX64Compiler->machineCode))[2] = 182; + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return 4; + + case ZeroExtend16RR: + /* begin concretizeZeroExtend16RR */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 1, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 15; + ((self_in_CogX64Compiler->machineCode))[2] = 183; + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return 4; + + case ZeroExtend32RR: + /* begin concretizeZeroExtend32RR */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + skip = 1; + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 0, destReg, 0, srcReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip] = 139; + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return skip + 2; + + case MovePerfCnt64RL: + return concretizeMovePerfCnt64RL(self_in_CogX64Compiler); + + default: + error("Case not found and no otherwise clause"); + } + + /* Noops & Pseudo Ops + Control + Table B-1 Intel 64 and IA-32 Architectures Software Developer's Manual Volume 1: Basic Architecture + Arithmetic + Data Movement + Conversion + miscellaneous */ + return 0; +} + + +/* Attempt to generate concrete machine code for the instruction at address. + This is part of the inner dispatch of concretizeAt: actualAddress which + exists only + to get around the number of literals limits in the SqueakV3 (blue book + derived) bytecode set. */ + + /* CogX64Compiler>>#dispatchConcretizeProcessorSpecific */ +static NoDbgRegParms sqInt +dispatchConcretizeProcessorSpecific(AbstractInstruction *self_in_CogX64Compiler) +{ + sqInt dest; + sqInt maskReg; + sqInt reg1; + sqInt reg2; + sqInt regDivisor; + + switch ((self_in_CogX64Compiler->opcode)) { + case CPUID: + /* begin concretizeCPUID */ + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = 162; + return 2; + + case CDQ: + /* begin concretizeCDQ */ + ((self_in_CogX64Compiler->machineCode))[0] = 72; + ((self_in_CogX64Compiler->machineCode))[1] = 153; + return 2; + + case IDIVR: + /* begin concretizeIDIVR */ + regDivisor = ((self_in_CogX64Compiler->operands))[0]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, regDivisor)); + ((self_in_CogX64Compiler->machineCode))[1] = 247; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, regDivisor, 7)); + return 3; + + case IMULRR: + /* begin concretizeMulRR */ + reg1 = ((self_in_CogX64Compiler->operands))[0]; + reg2 = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, reg2, 0, reg1)); + ((self_in_CogX64Compiler->machineCode))[1] = 15; + ((self_in_CogX64Compiler->machineCode))[2] = 175; + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModReg, reg1, reg2)); + return 4; + + case XCHGRR: + return concretizeXCHGRR(self_in_CogX64Compiler); + + case REP: + /* begin concretizeREP */ + ((self_in_CogX64Compiler->machineCode))[0] = 243; + return 1; + + case CLD: + /* begin concretizeCLD */ + ((self_in_CogX64Compiler->machineCode))[0] = 0xFC; + return 1; + + case MOVSB: + /* begin concretizeMOVSB */ + ((self_in_CogX64Compiler->machineCode))[0] = 164; + return 1; + + case MOVSQ: + /* begin concretizeMOVSQ */ + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 1, 0, 0, 0)); + ((self_in_CogX64Compiler->machineCode))[1] = 165; + return 2; + + case BSR: + /* begin concretizeBSR */ + maskReg = ((self_in_CogX64Compiler->operands))[0]; + dest = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 1, dest, 0, maskReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 15; + ((self_in_CogX64Compiler->machineCode))[2] = 189; + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModReg, maskReg, dest)); + return 4; + + default: + error("Case not found and no otherwise clause"); + } + + /* Specific Arithmetic + Specific Data Movement */ + return 0; +} + + /* CogX64Compiler>>#fullCallsAreRelative */ +static NoDbgRegParms sqInt +fullCallsAreRelative(AbstractInstruction *self_in_CogX64Compiler) +{ + return 0; +} + + /* CogX64Compiler>>#genDivR:R:Quo:Rem: */ +static NoDbgRegParms AbstractInstruction * +genDivRRQuoRem(AbstractInstruction *self_in_CogX64Compiler, sqInt abstractRegDivisor, sqInt abstractRegDividend, sqInt abstractRegQuotient, sqInt abstractRegRemainder) +{ + sqInt rDividend; + sqInt rDivisor; + sqInt reg; + sqInt rQuotient; + sqInt rRemainder; + sqInt rUnused; + sqInt saveRestoreEAX; + sqInt saveRestoreEDX; + sqInt saveRestoreExchanged; + + assert(abstractRegDividend != abstractRegDivisor); + assert(abstractRegQuotient != abstractRegRemainder); + rDividend = abstractRegDividend; + rDivisor = abstractRegDivisor; + rQuotient = abstractRegQuotient; + rRemainder = abstractRegRemainder; + + /* IDIV r does a signed divide of RDX:RAX by r, RAX := Quotient, RDX := Remainder. + Since we must sign extend the dividend into RDX we must substitute another register if RDX is an input. */ + if ((rDividend == RDX) + || (rDivisor == RDX)) { + /* Slang, sigh... */ + rUnused = RAX; + while (rUnused <= RDI) { + if ((rUnused != RSP) + && ((rUnused != RBP) + && ((rUnused != RDX) + && ((rUnused != rDividend) + && ((rUnused != rDivisor) + && ((rUnused != rQuotient) + && (rUnused != rRemainder))))))) { + /* PushR: */ + genoperand(PushR, rUnused); + + /* MoveR:R: */ + genoperandoperand(MoveRR, RDX, rUnused); + if (rDividend == RDX) { + genDivRRQuoRem(self_in_CogX64Compiler, rDivisor, rUnused, rQuotient, rRemainder); + } + else { + genDivRRQuoRem(self_in_CogX64Compiler, rUnused, rDividend, rQuotient, rRemainder); + } + + /* PopR: */ + genoperand(PopR, rUnused); + return self_in_CogX64Compiler; + } + rUnused += 1; + } + error("couldn't find unused register in genDivR:R:Quo:Rem:"); + } + + /* If either output does not include RAX or RDX we must save and restore RAX and/or RDX. */ + if ((saveRestoreEAX = (rQuotient != RAX) + && (rRemainder != RAX))) { + /* PushR: */ + genoperand(PushR, RAX); + } + if ((saveRestoreEDX = (rQuotient != RDX) + && (rRemainder != RDX))) { + /* PushR: */ + genoperand(PushR, RDX); + } + saveRestoreExchanged = -1; + if (rDividend) { + if (rDivisor) { + /* MoveR:R: */ + genoperandoperand(MoveRR, rDividend, RAX); + } + else { + if (((rDividend != rQuotient) + && (rDividend != rRemainder)) + && ((rDividend != RDX) + || (!saveRestoreEDX))) { + reg = (saveRestoreExchanged = rDividend); + + /* begin PushR: */ + genoperand(PushR, reg); + } + genoperandoperand(XCHGRR, rDivisor, rDividend); + } + } + + /* CDQ sign-extends RAX into RDX as required for IDIV */ + gen(CDQ); + genoperand(IDIVR, (rDivisor + ? rDivisor + : rDividend)); + + /* Must not overwrite result while juggling */ + if ((rQuotient == RDX) + && (rRemainder == RAX)) { + genoperandoperand(XCHGRR, rQuotient, rRemainder); + } + else { + if (rQuotient == RDX) { + if (rRemainder != RDX) { + /* MoveR:R: */ + genoperandoperand(MoveRR, RDX, rRemainder); + } + if (rQuotient) { + /* MoveR:R: */ + genoperandoperand(MoveRR, RAX, rQuotient); + } + } + else { + if (rQuotient) { + /* MoveR:R: */ + genoperandoperand(MoveRR, RAX, rQuotient); + } + if (rRemainder != RDX) { + /* MoveR:R: */ + genoperandoperand(MoveRR, RDX, rRemainder); + } + } + } + if (saveRestoreExchanged >= 0) { + /* PopR: */ + genoperand(PopR, saveRestoreExchanged); + } + if (saveRestoreEDX) { + /* PopR: */ + genoperand(PopR, RDX); + } + if (saveRestoreEAX) { + /* PopR: */ + genoperand(PopR, RAX); + } + return self_in_CogX64Compiler; +} + + +/* Get the abstract registers for ECX, EDI and ESI */ + + /* CogX64Compiler>>#genMemCopy:to:constantSize: */ +static NoDbgRegParms AbstractInstruction * +genMemCopytoconstantSize(AbstractInstruction *self_in_CogX64Compiler, sqInt originalSourceReg, sqInt originalDestReg, sqInt size) +{ + sqInt countReg; + sqInt destReg; + AbstractInstruction *inst; + sqInt numbytes; + sqInt numwords; + sqInt sourceReg; + + sourceReg = RSI; + destReg = RDI; + countReg = RCX; + + /* Put the source in ESI and the dest in EDI */ + inst = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + if (originalSourceReg != sourceReg) { + if (originalDestReg == sourceReg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, originalDestReg, TempReg); + } + + /* MoveR:R: */ + genoperandoperand(MoveRR, originalSourceReg, sourceReg); + } + if (originalDestReg != destReg) { + if (originalDestReg == sourceReg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, destReg); + } + else { + /* MoveR:R: */ + genoperandoperand(MoveRR, originalDestReg, destReg); + } + } + + /* Store the count and clear the direction */ + gen(CLD); + + /* First copy the bytes */ + numbytes = size & 7; + if (numbytes > 0) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, numbytes, countReg); + gen(REP); + gen(MOVSB); + } + + /* Now copy the qwords */ + numwords = size / 8; + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, numwords, countReg); + gen(REP); + gen(MOVSQ); + return self_in_CogX64Compiler; +} + + +/* Get the abstract registers for ECX, EDI and ESI */ + + /* CogX64Compiler>>#genMemCopy:to:size: */ +static NoDbgRegParms AbstractInstruction * +genMemCopytosize(AbstractInstruction *self_in_CogX64Compiler, sqInt originalSourceReg, sqInt originalDestReg, sqInt originalSize) +{ + sqInt countReg; + sqInt destReg; + AbstractInstruction *inst; + sqInt size; + sqInt sourceReg; + sqInt spilledSize; + + sourceReg = RSI; + destReg = RDI; + countReg = RCX; + + /* TODO: Avoid spilling */ + spilledSize = 0; + if ((originalSize == sourceReg) + || (originalSize == destReg)) { + /* PushR: */ + genoperand(PushR, originalSize); + spilledSize = 1; + } + + /* Put the source in ESI and the dest in EDI */ + inst = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + if (originalSourceReg != sourceReg) { + if (originalDestReg == sourceReg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, originalDestReg, TempReg); + } + + /* MoveR:R: */ + genoperandoperand(MoveRR, originalSourceReg, sourceReg); + } + if (originalDestReg != destReg) { + if (originalDestReg == sourceReg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, destReg); + } + else { + /* MoveR:R: */ + genoperandoperand(MoveRR, originalDestReg, destReg); + } + } + + /* Put the original size register in somewhere different than ECX */ + if (spilledSize) { + /* PopR: */ + genoperand(PopR, TempReg); + size = TempReg; + } + else { + if (originalSize == countReg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, originalSize, TempReg); + size = TempReg; + } + else { + size = originalSize; + } + } + gen(CLD); + + /* First copy the bytes */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, size, countReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, 7, countReg); + gen(REP); + gen(MOVSB); + + /* Now copy the words */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, size, countReg); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, 3, countReg); + gen(REP); + gen(MOVSQ); + return self_in_CogX64Compiler; +} + + /* CogX64Compiler>>#genMulR:R: */ +static NoDbgRegParms AbstractInstruction * +genMulRR(AbstractInstruction *self_in_CogX64Compiler, sqInt regSource, sqInt regDest) +{ + return genoperandoperand(IMULRR, regSource, regDest); +} + + +/* Ensure that the register args are pushed before the outer and + inner retpcs at an entry miss for arity <= self numRegArgs. The + outer retpc is that of a call at a send site. The inner is the call + from a method or PIC abort/miss to the trampoline. */ +/* This won't be as clumsy on a RISC. But putting the receiver and + args above the return address means the CoInterpreter has a + single machine-code frame format which saves us a lot of work. */ +/* Iff there are register args convert + base -> outerRetpc (send site retpc) + sp -> innerRetpc (PIC abort/miss retpc) + to + base -> receiver + (arg0) + (arg1) + outerRetpc + sp -> innerRetpc (PIC abort/miss retpc) */ + + /* CogX64Compiler>>#genPushRegisterArgsForAbortMissNumArgs: */ +static NoDbgRegParms AbstractInstruction * +genPushRegisterArgsForAbortMissNumArgs(AbstractInstruction *self_in_CogX64Compiler, sqInt numArgs) +{ + if (numArgs <= (numRegArgs())) { + assert((numRegArgs()) <= 2); + if (!numArgs) { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, SPReg, TempReg); + + /* PushR: */ + genoperand(PushR, TempReg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord * 2, SPReg, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, BytesPerWord, SPReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ReceiverResultReg, 2 * BytesPerWord, SPReg); + return self_in_CogX64Compiler; + } + if (numArgs == 1) { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord, SPReg, TempReg); + + /* PushR: */ + genoperand(PushR, TempReg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord, SPReg, TempReg); + + /* PushR: */ + genoperand(PushR, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ReceiverResultReg, 3 * BytesPerWord, SPReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, Arg0Reg, 2 * BytesPerWord, SPReg); + return self_in_CogX64Compiler; + } + if (numArgs == 2) { + /* PushR: */ + genoperand(PushR, Arg1Reg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord * 2, SPReg, TempReg); + + /* PushR: */ + genoperand(PushR, TempReg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord * 2, SPReg, TempReg); + + /* PushR: */ + genoperand(PushR, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ReceiverResultReg, 4 * BytesPerWord, SPReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, Arg0Reg, 3 * BytesPerWord, SPReg); + return self_in_CogX64Compiler; + } + } + return self_in_CogX64Compiler; +} + + +/* Ensure that the register args are pushed before the retpc for arity <= + self numRegArgs. This + isn't as clumsy on a RISC. But putting the receiver and args above the + return address + means the CoInterpreter has a single machine-code frame format which saves + us a lot of work. + N.B. Take great care to /not/ smash TempReg, which is used in directed + send marshalling. + We could use XCHG to swap the ReceiverResultReg and top-of-stack return + address, pushing the + the ret pc (now in ReceiverResultReg) later, but XCHG is very slow. We can + use SendNumArgsReg + because it is only live in sends of arity >= (NumSendTrampolines - 1). */ + + /* CogX64Compiler>>#genPushRegisterArgsForNumArgs:scratchReg: */ +static NoDbgRegParms AbstractInstruction * +genPushRegisterArgsForNumArgsscratchReg(AbstractInstruction *self_in_CogX64Compiler, sqInt numArgs, sqInt scratchReg) +{ + assert((numRegArgs()) < (NumSendTrampolines - 1)); + if (numArgs <= (numRegArgs())) { + assert((numRegArgs()) <= 2); + + /* these two variants show the same performance on Intel Core i7, but the second one may be shorter. */ + + /* a.k.a. + cogit gen: XCHGMwrR operand: 0 operand: SPReg operand: ReceiverResultReg. + but XCHG is slow. */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, SPReg, scratchReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ReceiverResultReg, 0, SPReg); + if (numArgs > 0) { + /* PushR: */ + genoperand(PushR, Arg0Reg); + if (numArgs > 1) { + /* PushR: */ + genoperand(PushR, Arg1Reg); + } + } + + /* PushR: */ + genoperand(PushR, scratchReg); + } + return self_in_CogX64Compiler; +} + + +/* This is a no-op on x64 SysV since the ABI passes up to 6 args in registers + and trampolines currently observe a limit of 4. + But the WIN64 ABI always reserves shadow space for saving up to 4 + parameter registers (even if less than 4 args). + */ + + /* CogX64Compiler>>#genRemoveNArgsFromStack: */ +static NoDbgRegParms sqInt +genRemoveNArgsFromStack(AbstractInstruction *self_in_CogX64Compiler, sqInt n) +{ + assert(n <= 4); + return 0; +} + + +/* Restore the registers in regMask as saved by genSaveRegs:. */ + + /* CogX64Compiler>>#genRestoreRegs: */ +static NoDbgRegParms sqInt +genRestoreRegs(AbstractInstruction *self_in_CogX64Compiler, sqInt regMask) +{ + sqInt reg; + + assert(!((((regMask & (registerMaskForand(RSP, RBP))) != 0)))); + for (reg = RAX; reg <= R15; reg += 1) { + if (((regMask & (((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)))) != 0)) { + /* PopR: */ + genoperand(PopR, reg); + } + } + return 0; +} + + +/* Save the registers in regMask for a call into the C run-time from a + trampoline. + */ + + /* CogX64Compiler>>#genSaveRegs: */ +static NoDbgRegParms sqInt +genSaveRegs(AbstractInstruction *self_in_CogX64Compiler, sqInt regMask) +{ + sqInt reg; + + assert(!((((regMask & (registerMaskForand(RSP, RBP))) != 0)))); + for (reg = R15; reg >= RAX; reg += -1) { + if (((regMask & (((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)))) != 0)) { + /* PushR: */ + genoperand(PushR, reg); + } + } + return 0; +} + + /* CogX64Compiler>>#genSubstituteReturnAddress: */ +static NoDbgRegParms AbstractInstruction * +genSubstituteReturnAddress(AbstractInstruction *self_in_CogX64Compiler, sqInt retpc) +{ + return genoperand(PushCw, retpc); +} + + /* CogX64Compiler>>#genSwapR:R:Scratch: */ +static NoDbgRegParms AbstractInstruction * +genSwapRRScratch(AbstractInstruction *self_in_CogX64Compiler, sqInt regA, sqInt regB, sqInt regTmp) +{ + return genoperandoperand(XCHGRR, regA, regB); +} + + +/* Answer if we support LZCNT */ + + /* CogX64Compiler>>#hasLZCNTInstructions */ +static NoDbgRegParms int +hasLZCNTInstructions(AbstractInstruction *self_in_CogX64Compiler) +{ + return (((cpuidWord1(self_in_CogX64Compiler)) & (32)) != 0); +} + + +/* Answer if the processor has a dedicated callee-saved register to point to + the base of commonly-accessed variables. */ + + /* CogX64Compiler>>#hasVarBaseRegister */ +static NoDbgRegParms sqInt +hasVarBaseRegister(AbstractInstruction *self_in_CogX64Compiler) +{ + return 1; +} + + +/* Answer the instruction size at pc. This is used in method disassembly + to decode the jumps in block dispatch to discover where block methods + occur within a larger method. This is very far from a full decode. */ + + /* CogX64Compiler>>#instructionSizeAt: */ +static NoDbgRegParms sqInt +instructionSizeAt(AbstractInstruction *self_in_CogX64Compiler, sqInt pc) +{ + sqInt op; + + op = byteAt((void *)(pc)); + if ((op & 0xF8) == 72) { + return 1 + (instructionSizeAt(self_in_CogX64Compiler, pc + 1)); + } + switch (op) { + case 15: + return twoByteInstructionSizeAt(self_in_CogX64Compiler, pc); + + case 61: + case 233: + return 5; + + case 112: + case 113: + case 114: + case 115: + case 116: + case 117: + case 118: + case 119: + case 120: + case 121: + case 122: + case 123: + case 0x7C: + case 125: + case 0x7E: + case 0x7F: + case 137: + case 235: + return 2; + + case 131: + return sizeImmediateGroup1at(self_in_CogX64Compiler, op, pc); + + case 139: + return sizeHasModrmat(self_in_CogX64Compiler, op, pc); + + case 144: + case 204: + return 1; + + default: + error("Case not found and no otherwise clause"); + } + return 0; +} + + +/* Top 32 bits all the same as the bottom 32 bits' sign bit implies we can + use a sign-extended 4 byte offset. + */ + + /* CogX64Compiler>>#is32BitSignedImmediate: */ +static NoDbgRegParms int +is32BitSignedImmediate(AbstractInstruction *self_in_CogX64Compiler, sqInt a64BitUnsignedOperand) +{ + return (((int) a64BitUnsignedOperand)) == (((sqLong) a64BitUnsignedOperand)); +} + + +/* Assuming mcpc is a send return pc answer if the instruction before it is a + call (not a CallFull). + */ + + /* CogX64Compiler>>#isCallPrecedingReturnPC: */ +static NoDbgRegParms int +isCallPrecedingReturnPC(AbstractInstruction *self_in_CogX64Compiler, sqInt mcpc) +{ + return (byteAt((void *)(mcpc - 5))) == 232; +} + + /* CogX64Compiler>>#isJumpAt: */ +static NoDbgRegParms sqInt +isJumpAt(AbstractInstruction *self_in_CogX64Compiler, sqInt pc) +{ + sqInt op; + + op = byteAt((void *)(pc)); + return (((op >= 112) && (op <= 0x7F))) + || ((op == 233) + || ((op == 235) + || (((op == 15) + && ((((byteAt((void *)(pc + 1))) >= 128) && ((byteAt((void *)(pc + 1))) <= 143)))) + || ((op == 72) + && (((byteAt((void *)(pc + 1))) == 161) + && (((byteAt((void *)(pc + 10))) == 0xFF) + && ((byteAt((void *)(pc + 11))) == 224))))))); +} + + /* CogX64Compiler>>#isQuick: */ +static NoDbgRegParms int +isQuick(AbstractInstruction *self_in_CogX64Compiler, usqIntptr_t operand) +{ + return (((((sqLong) operand)) >= -128) && ((((sqLong) operand)) <= 0x7F)); +} + + +/* Answer if an address can be accessed using the offset in a MoveMw:r:R: or + similar instruction. + We assume this is true for 32-bit processors and expect 64-bit processors + to answer false + for values in the interpreter or the object memory. Restrict our use of + offsets to reference + addresses within the method zone, rather than checking for a 32-bit + offset, so as to keep the + simulator and real VM in sync. */ + + /* CogX64Compiler>>#isWithinMwOffsetRange: */ +static NoDbgRegParms sqInt +isWithinMwOffsetRange(AbstractInstruction *self_in_CogX64Compiler, sqInt anAddress) +{ + return /* addressIsInCodeZone: */ + ((((usqInt)anAddress)) >= codeBase) + && ((((usqInt)anAddress)) < limitAddress); +} + + +/* Set the target of a jump instruction. These all have the target in the + first operand. */ +/* Set the target of a jump instruction. These all have the target in the + first operand. + Override to cope with JumpFPNotEqual where because of IEEE NaN conformance + and the behaviour of COMISD/UCOMISD we generate two jumps to the same + target. */ + + /* CogX64Compiler>>#jmpTarget: */ +static NoDbgRegParms AbstractInstruction * +jmpTarget(AbstractInstruction *self_in_CogX64Compiler, AbstractInstruction *anAbstractInstruction) +{ + AbstractInstruction *aDependent; + + aDependent = (self_in_CogX64Compiler->dependent); + while (aDependent) { + jmpTarget(aDependent, anAbstractInstruction); + aDependent = (aDependent->dependent); + } + + /* check for inadvertent smashing of already-set jmpTargets; development only */ + ((self_in_CogX64Compiler->operands))[0] = (((usqInt)anAbstractInstruction)); + return anAbstractInstruction; +} + + +/* Branch/Call ranges. Jump[Cond] can be generated as short as possible. + Call/Jump[Cond]Long must be generated + in the same number of bytes irrespective of displacement since their + targets may be updated, but they need only + span 16Mb, the maximum size of the code zone. This allows e.g. ARM to use + single-word call and jump instructions + for most calls and jumps. CallFull/JumpFull must also be generated in the + same number of bytes irrespective of + displacement for the same reason, but they must be able to span the full + (32-bit or 64-bit) address space because + they are used to call code in the C runtime, which may be distant from the + code zone + */ + + /* CogX64Compiler>>#jumpLongByteSize */ +static NoDbgRegParms sqInt +jumpLongByteSize(AbstractInstruction *self_in_CogX64Compiler) +{ + return 5; +} + + /* CogX64Compiler>>#jumpLongConditionalByteSize */ +static NoDbgRegParms sqInt +jumpLongConditionalByteSize(AbstractInstruction *self_in_CogX64Compiler) +{ + return 6; +} + + +/* Answer the target address for the long jump immediately preceding mcpc */ + + /* CogX64Compiler>>#jumpLongTargetBeforeFollowingAddress: */ +static NoDbgRegParms usqInt +jumpLongTargetBeforeFollowingAddress(AbstractInstruction *self_in_CogX64Compiler, sqInt mcpc) +{ + return callTargetFromReturnAddress(self_in_CogX64Compiler, mcpc); +} + + /* CogX64Compiler>>#jumpTargetPCAt: */ +static NoDbgRegParms usqInt +jumpTargetPCAt(AbstractInstruction *self_in_CogX64Compiler, sqInt pc) +{ + sqInt byte; + sqInt offset; + sqInt size; + + size = instructionSizeAt(self_in_CogX64Compiler, pc); + if (size == 2) { + byte = byteAt((void *)(pc + 1)); + offset = (byte & 128 + ? byte - 0x100 + : byte); + } + else { + byte = byteAt((void *)((pc + size) - 1)); + offset = (byte & 128 + ? byte - 0x100 + : byte); + offset = ((((usqInt)(offset) << 8))) + (byteAt((void *)((pc + size) - 2))); + offset = ((((usqInt)(offset) << 8))) + (byteAt((void *)((pc + size) - 3))); + offset = ((((usqInt)(offset) << 8))) + (byteAt((void *)((pc + size) - 4))); + } + return (pc + size) + offset; +} + + +/* Answer the delta from the stack pointer after a call to the stack pointer + immediately prior to the call. This is used to compute the stack pointer + immediately prior to a call from within a leaf routine, which in turn is + used to capture the c stack pointer to use in trampolines back into the C + run-time. */ + + /* CogX64Compiler>>#leafCallStackPointerDelta */ +static NoDbgRegParms sqInt +leafCallStackPointerDelta(AbstractInstruction *self_in_CogX64Compiler) +{ + return 8; +} + + +/* Answer the byte size of a MoveCwR opcode's corresponding machine code + when the argument is a PIC. This is for the self-reference at the end of a + closed PIC: leaq 0xffffffffffffff2b(%rip), %rcx : 48 8D 0D 2B FF FF FF */ + + /* CogX64Compiler>>#loadPICLiteralByteSize */ +static NoDbgRegParms sqInt +loadPICLiteralByteSize(AbstractInstruction *self_in_CogX64Compiler) +{ + return 7; +} + + /* CogX64Compiler>>#machineCodeAt: */ +static NoDbgRegParms sqInt +machineCodeAt(AbstractInstruction *self_in_CogX64Compiler, sqInt anOffset) +{ + return ((self_in_CogX64Compiler->machineCode))[anOffset]; +} + + +/* Answer the maximum number of bytes of machine code generated for any + abstract instruction. + e.g. xchg %rdx, %rax; movq $0x12345678ABCDEF0, %(rax); xchg %rdx, %rax => + 48 92 48 A3 F0 DE BC 9A 78 56 34 12 48 92 + */ + + /* CogX64Compiler>>#machineCodeBytes */ +static NoDbgRegParms sqInt +machineCodeBytes(AbstractInstruction *self_in_CogX64Compiler) +{ + return 14; +} + + +/* See ModR/M byte & opcode syntax + In addition to the notation shown above in 'Mnemonic Syntax' on page 43, + the following notation indicates the size and type of operands in the + syntax of an instruction opcode: + /digit Indicates that the ModRM byte specifies only one register or memory + (r/m) operand. + The digit is specified by the ModRM reg field and is used as an + instruction-opcode extension. + Valid digit values range from 0 to 7. + /r Indicates that the ModRM byte specifies both a register operand and a + reg/mem (register or memory) operand. */ + + /* CogX64Compiler>>#mod:RM:RO: */ +static NoDbgRegParms usqInt +modRMRO(AbstractInstruction *self_in_CogX64Compiler, sqInt mod, sqInt regMode, sqInt regOpcode) +{ + return (((((usqInt)(mod) << 6))) + (((regOpcode & 7) << 3))) + (regMode & 7); +} + + /* CogX64Compiler>>#numIntRegArgs */ +static NoDbgRegParms sqInt +numIntRegArgs(AbstractInstruction *self_in_CogX64Compiler) +{ + return 6; +} + + /* CogX64Compiler>>#padIfPossibleWithStopsFrom:to: */ +static NoDbgRegParms AbstractInstruction * +padIfPossibleWithStopsFromto(AbstractInstruction *self_in_CogX64Compiler, sqInt startAddr, sqInt endAddr) +{ + stopsFromto(self_in_CogX64Compiler, startAddr, endAddr); + return self_in_CogX64Compiler; +} + + +/* On Intel the performance counter appears to be a nanosecond clock, + at least on intel Core etc. So presume a 1GHz processor */ + + /* CogX64Compiler>>#performanceCounterTicksPerMicrosecond */ +static NoDbgRegParms sqInt +performanceCounterTicksPerMicrosecond(AbstractInstruction *self_in_CogX64Compiler) +{ + return 1000; +} + + /* CogX64Compiler>>#relocateCallBeforeReturnPC:by: */ +static NoDbgRegParms AbstractInstruction * +relocateCallBeforeReturnPCby(AbstractInstruction *self_in_CogX64Compiler, sqInt retpc, sqInt delta) +{ + sqInt distance; + + if (delta) { + distance = ((((((usqInt)((byteAt((void *)(retpc - 1)))) << 24))) + ((((usqInt)((byteAt((void *)(retpc - 2)))) << 16)))) + ((((usqInt)((byteAt((void *)(retpc - 3)))) << 8)))) + (byteAt((void *)(retpc - 4))); + distance += delta; + byteAtput((void *)(retpc - 1),(((usqInt)(distance)) >> 24) & 0xFF); + byteAtput((void *)(retpc - 2),(((usqInt)(distance)) >> 16) & 0xFF); + byteAtput((void *)(retpc - 3),(((usqInt)(distance)) >> 8) & 0xFF); + byteAtput((void *)(retpc - 4),distance & 0xFF); + } + return self_in_CogX64Compiler; +} + + +/* We generate the method address using pc-relative addressing. + Simply check that rip-relative addressing is being used. c.f. + concretizeMoveCwR */ + + /* CogX64Compiler>>#relocateMethodReferenceBeforeAddress:by: */ +static NoDbgRegParms AbstractInstruction * +relocateMethodReferenceBeforeAddressby(AbstractInstruction *self_in_CogX64Compiler, sqInt pc, sqInt delta) +{ + assert((((byteAt((void *)(pc - 6))) == 141) + && (((byteAt((void *)(pc - 5))) | (modRMRO(self_in_CogX64Compiler, 0, 0, 7))) == (modRMRO(self_in_CogX64Compiler, ModRegInd, 5, 7)))) + || (((byteAt((void *)(pc - 8))) == 141) + && (((byteAt((void *)(pc - 7))) | (modRMRO(self_in_CogX64Compiler, 0, 0, 7))) == (modRMRO(self_in_CogX64Compiler, ModRegInd, 5, 7))))); + return self_in_CogX64Compiler; +} + + +/* Rewrite a call instruction to call a different target. This variant is + used to link PICs + in ceSendMiss et al, and to rewrite cached primitive calls. Answer the + extent of + the code change which is used to compute the range of the icache to flush. */ +/* self cCode: '' + inSmalltalk: [cogit disassembleFrom: callSiteReturnAddress - 10 to: + callSiteReturnAddress - 1]. */ + + /* CogX64Compiler>>#rewriteCallAt:target: */ +static NoDbgRegParms sqInt +rewriteCallAttarget(AbstractInstruction *self_in_CogX64Compiler, usqInt callSiteReturnAddress, usqInt callTargetAddress) +{ + usqInt callDistance; + + callDistance = ((usqInt) (int)(callTargetAddress - callSiteReturnAddress)); + byteAtput((void *)(callSiteReturnAddress - 1),((callDistance) >> 24) & 0xFF); + byteAtput((void *)(callSiteReturnAddress - 2),((callDistance) >> 16) & 0xFF); + byteAtput((void *)(callSiteReturnAddress - 3),((callDistance) >> 8) & 0xFF); + byteAtput((void *)(callSiteReturnAddress - 4),callDistance & 0xFF); + assert((callTargetFromReturnAddress(self_in_CogX64Compiler, callSiteReturnAddress)) == callTargetAddress); + + /* self cCode: '' + inSmalltalk: [cogit disassembleFrom: callSiteReturnAddress - 10 to: callSiteReturnAddress - 1]. */ + return 5; +} + + +/* Rewrite the short jump instruction to jump to a new cpic case target. */ +/* prevent type inference for avoiding warning on abs */ + + /* CogX64Compiler>>#rewriteCPICJumpAt:target: */ +static NoDbgRegParms AbstractInstruction * +rewriteCPICJumpAttarget(AbstractInstruction *self_in_CogX64Compiler, usqInt addressFollowingJump, usqInt jumpTargetAddr) +{ + sqInt callDistance; + + callDistance = jumpTargetAddr - addressFollowingJump; + assert((SQABS(callDistance)) < 128); + byteAtput((void *)(addressFollowingJump - 1),callDistance & 0xFF); + return self_in_CogX64Compiler; +} + + +/* Rewrite an inline cache to call a different target for a new tag. This + variant is used + to link unlinked sends in ceSend:to:numArgs: et al. Answer the extent of + the code + change which is used to compute the range of the icache to flush. + N.B. On 64-bit platforms the inline cache tag is only 32-bits wide, hence + this code + is identical to that for the IA32. */ +/* self cCode: '' + inSmalltalk: [cogit disassembleFrom: callSiteReturnAddress - 12 to: + callSiteReturnAddress - 1]. */ + + /* CogX64Compiler>>#rewriteInlineCacheAt:tag:target: */ +static NoDbgRegParms sqInt +rewriteInlineCacheAttagtarget(AbstractInstruction *self_in_CogX64Compiler, usqInt callSiteReturnAddress, sqInt cacheTag, usqInt callTargetAddress) +{ + usqInt callDistance; + + callDistance = ((usqInt) (int)(callTargetAddress - callSiteReturnAddress)); + byteAtput((void *)(callSiteReturnAddress - 1),((callDistance) >> 24) & 0xFF); + byteAtput((void *)(callSiteReturnAddress - 2),((callDistance) >> 16) & 0xFF); + byteAtput((void *)(callSiteReturnAddress - 3),((callDistance) >> 8) & 0xFF); + byteAtput((void *)(callSiteReturnAddress - 4),callDistance & 0xFF); + byteAtput((void *)(callSiteReturnAddress - 6),(((usqInt)(cacheTag)) >> 24) & 0xFF); + byteAtput((void *)(callSiteReturnAddress - 7),(((usqInt)(cacheTag)) >> 16) & 0xFF); + byteAtput((void *)(callSiteReturnAddress - 8),(((usqInt)(cacheTag)) >> 8) & 0xFF); + byteAtput((void *)(callSiteReturnAddress - 9),cacheTag & 0xFF); + assert((callTargetFromReturnAddress(self_in_CogX64Compiler, callSiteReturnAddress)) == callTargetAddress); + + /* self cCode: '' + inSmalltalk: [cogit disassembleFrom: callSiteReturnAddress - 12 to: callSiteReturnAddress - 1]. */ + return 12; +} + + +/* Rewrite an inline cache with a new tag. This variant is used + by the garbage collector. */ + + /* CogX64Compiler>>#rewriteInlineCacheTag:at: */ +static NoDbgRegParms AbstractInstruction * +rewriteInlineCacheTagat(AbstractInstruction *self_in_CogX64Compiler, sqInt cacheTag, sqInt callSiteReturnAddress) +{ + unalignedLong32Atput((void *)(callSiteReturnAddress - 9),cacheTag); + return self_in_CogX64Compiler; +} + + +/* <0-15> */ +/* <0-15> */ +/* <0-15> */ + + /* CogX64Compiler>>#rexR:x:b: */ +static NoDbgRegParms usqInt +rexRxb(AbstractInstruction *self_in_CogX64Compiler, sqInt reg, sqInt sibReg, sqInt fieldReg) +{ + return rexwrxb(self_in_CogX64Compiler, 1, reg, sibReg, fieldReg); +} + + +/* */ +/* <0-15> */ +/* <0-15> */ +/* <0-15> */ +/* Given width64, the R register, sib register, and modrm/sib/reg field + register, answer the correctly encoded REX prefix byte. + See AMD64 Architecture Programmer's Manual Volume 3: General-Purpose and + System Instructions, Table 1-11 */ + + /* CogX64Compiler>>#rexw:r:x:b: */ +static NoDbgRegParms usqInt +rexwrxb(AbstractInstruction *self_in_CogX64Compiler, sqInt width64, sqInt reg, sqInt sibReg, sqInt fieldReg) +{ + usqInt regBits; + + regBits = ((((reg & 8)) >> 1) + (((sibReg & 8)) >> 2)) + (((usqInt)(fieldReg)) >> 3); + return ((width64 + ? 72 + : 64)) + regBits; +} + + +/* to save Slang from having to be a real compiler (it can't inline switches + that return) + */ +/* Answer if the receiver's opcode sets the condition codes correctly for the + given conditional jump opcode. + */ + + /* CogX64Compiler>>#setsConditionCodesFor: */ +static NoDbgRegParms sqInt +setsConditionCodesFor(AbstractInstruction *self_in_CogX64Compiler, sqInt aConditionalJumpOpcode) +{ + switch ((self_in_CogX64Compiler->opcode)) { + case ArithmeticShiftRightCqR: + case ArithmeticShiftRightRR: + case LogicalShiftLeftCqR: + case LogicalShiftLeftRR: + return ((aConditionalJumpOpcode >= JumpZero) && (aConditionalJumpOpcode <= JumpNonNegative)); + + case LogicalShiftRightCqR: + return 0; + + case XorRR: + return 1; + + case ClzRR: + return (aConditionalJumpOpcode == JumpZero) + || ((aConditionalJumpOpcode == JumpNonZero) + || ((aConditionalJumpOpcode == JumpCarry) + || (aConditionalJumpOpcode == JumpNoCarry))); + + default: + haltmsg("unhandled opcode in setsConditionCodesFor:"); + return 0; + } + return 0; +} + + /* CogX64Compiler>>#sizeHasModrm:at: */ +static NoDbgRegParms int +sizeHasModrmat(AbstractInstruction *self_in_CogX64Compiler, sqInt op, sqInt pc) +{ + sqInt mod; + sqInt modrm; + usqInt rm; + usqInt ro; + + modrm = byteAt((void *)(pc + 1)); + mod = ((usqInt)(modrm)) >> 6; + ro = (((usqInt)(modrm)) >> 3) & 7; + rm = modrm & 7; + if (mod == 3) { + return 2; + } + if (rm != 4) { + switch (mod) { + case 0: + return (rm == 5 + ? 6 + : 3); + + case 1: + return 3; + + case 2: + return 6; + + default: + error("Case not found and no otherwise clause"); + } + } + + /* no SIB byte */ + haltmsg("fall through in sizeHasModrm:at:"); + return 0; +} + + +/* see [1] p A-7, p A-13 */ + + /* CogX64Compiler>>#sizeImmediateGroup1:at: */ +static NoDbgRegParms int +sizeImmediateGroup1at(AbstractInstruction *self_in_CogX64Compiler, sqInt op, sqInt pc) +{ + sqInt mod; + sqInt modrm; + usqInt rm; + usqInt ro; + + modrm = byteAt((void *)(pc + 1)); + mod = ((usqInt)(modrm)) >> 6; + ro = (((usqInt)(modrm)) >> 3) & 7; + rm = modrm & 7; + switch (ro) { + case 7: + return (op == 129 + ? 6 + : 3); + + default: + error("Case not found and no otherwise clause"); + } + return 0; +} + + /* CogX64Compiler>>#stopsFrom:to: */ +static NoDbgRegParms AbstractInstruction * +stopsFromto(AbstractInstruction *self_in_CogX64Compiler, sqInt startAddr, sqInt endAddr) +{ + memset(((void *)startAddr), 204 /* stop */, (endAddr - startAddr) + 1); + return self_in_CogX64Compiler; +} + + +/* Rewrite the 32-bit literal in the instruction immediately preceding + followingAddress. + */ + + /* CogX64Compiler>>#storeLiteral32:beforeFollowingAddress: */ +static NoDbgRegParms AbstractInstruction * +storeLiteral32beforeFollowingAddress(AbstractInstruction *self_in_CogX64Compiler, sqInt literal, sqInt followingAddress) +{ + byteAtput((void *)(followingAddress - 1),(((usqInt)(literal)) >> 24) & 0xFF); + byteAtput((void *)(followingAddress - 2),(((usqInt)(literal)) >> 16) & 0xFF); + byteAtput((void *)(followingAddress - 3),(((usqInt)(literal)) >> 8) & 0xFF); + byteAtput((void *)(followingAddress - 4),literal & 0xFF); + return self_in_CogX64Compiler; +} + + /* CogX64Compiler>>#s:i:b: */ +static NoDbgRegParms usqInt +sib(AbstractInstruction *self_in_CogX64Compiler, sqInt scale, sqInt indexReg, sqInt baseReg) +{ + return (((((usqInt)(scale) << 6))) + (((indexReg & 7) << 3))) + (baseReg & 7); +} + + /* CogX64Compiler>>#twoByteInstructionSizeAt: */ +static NoDbgRegParms int +twoByteInstructionSizeAt(AbstractInstruction *self_in_CogX64Compiler, sqInt pc) +{ + sqInt op; + + op = byteAt((void *)(pc + 1)); + switch (op & 240) { + case 128: + /* long conditional jumps */ + return 6; + + default: + error("Case not found and no otherwise clause"); + } + return 0; +} + + +/* Answer if Call and JumpLong are relative and hence need to take the + caller's relocation delta into account during code compaction, rather than + just the + callee's delta. */ + + /* CogX64Compiler>>#zoneCallsAreRelative */ +static NoDbgRegParms sqInt +zoneCallsAreRelative(AbstractInstruction *self_in_CogX64Compiler) +{ + return 1; +} + + +/* Generate the run-time entries at the base of the native code zone and + update the base. + */ + + /* LiveTypingCogit>>#generateRunTimeTrampolines */ +static sqInt +generateRunTimeTrampolines(void) +{ + ceCollectInstVarType = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectInstVarTypeForatof, "ceCollectInstVarType", 3, ReceiverResultReg, Extra0Reg, TempReg, null, CallerSavedRegisterMask, 1, NoReg, 0); + + /* NOTE: the 2-arg trampolines stage their index in Extra1Reg, not Extra0Reg: the value is in + TempReg = CArg0Reg on ARMv8, and genMarshallNArgs relocates that value into Extra0Reg, which + would clobber an index kept there. (The 3-arg ceCollectInstVarType is safe with the index in + Extra0Reg because there its value is relocated into Extra1Reg instead.) */ + ceCollectTempVarType = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectTempVarTypeAtof, "ceCollectTempVarType", 2, Extra1Reg, TempReg, null, null, CallerSavedRegisterMask, 1, NoReg, 0); + ceCollectRemoteTempVarType = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectRemoteTempVarTypeAtof, "ceCollectRemoteTempVarType", 2, Extra1Reg, TempReg, null, null, CallerSavedRegisterMask, 1, NoReg, 0); + ceCollectArgumentsTypeFrameful = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectArgumentsTypeForFramefulActivation, "ceCollectArgsTypeFrameful", 0, null, null, null, null, 0 /* emptyRegisterMask */, 1, NoReg, 0); + ceCollectArgumentsTypeFramelessSpilled = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectArgumentsTypeForFramlessSpilledActivationOf, "ceCollectArgsTypeFramelessSpilled", 1, TempReg, null, null, null, (1U << ReceiverResultReg), 1, NoReg, 0); + ceCollectArgumentsTypeFramelessUnspilled1Args = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectArgumentsTypeForFramlessUnspilledActivationWithArgof, "ceCollectArgumentsTypeFramelessUnspilled1Args", 2, Arg0Reg, TempReg, null, null, ((1U << ReceiverResultReg) | (1U << Arg0Reg)) | (1U << Arg1Reg), 1, NoReg, 0); + ceCollectArgumentsTypeFramelessUnspilled2Args = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectArgumentsTypeForFramlessUnspilledActivationWithArgargof, "ceCollectArgumentsTypeFramelessUnspilled2Args", 3, Arg0Reg, Arg1Reg, TempReg, null, ((1U << ReceiverResultReg) | (1U << Arg0Reg)) | (1U << Arg1Reg), 1, NoReg, 0); + ceCollectReturnType = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectReturnTypeForof, "ceCollectReturnType", 2, TempReg, ReceiverResultReg, null, null, (1U << ReceiverResultReg), 1, NoReg, 0); + ceSendMustBeBooleanAddFalseTrampoline = genMustBeBooleanTrampolineForcalled(falseObject(), "ceSendMustBeBooleanAddFalseTrampoline"); + ceSendMustBeBooleanAddTrueTrampoline = genMustBeBooleanTrampolineForcalled(trueObject(), "ceSendMustBeBooleanAddTrueTrampoline"); + + /* begin genNonLocalReturnTrampoline */ + zeroOpcodeIndex(); + + /* write the return address to the coInterpreter instructionPointerAddress; + following the CallRT to this CISCs will have pushed it on the stack, so pop it first; RISCs will have it in + their link register so just write it directly. */ + + /* PopR: */ + genoperand(PopR, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, instructionPointerAddress()); + ceNonLocalReturnTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceNonLocalReturn, "ceNonLocalReturnTrampoline", 1, ReceiverResultReg, null, null, null, 0 /* emptyRegisterMask */, 0, NoReg, 1); + + /* begin genCheckForInterruptsTrampoline */ + zeroOpcodeIndex(); + + /* if we have a link register we will assume that it does not get automatically pushed onto the stack + and thus there is no need to pop it before saving to instructionPointerAddress */ + + /* PopR: */ + genoperand(PopR, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, instructionPointerAddress()); + ceCheckForInterruptTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCheckForInterrupt, "ceCheckForInterruptTrampoline", 0, null, null, null, null, 0 /* emptyRegisterMask */, 0, NoReg, 1); + + /* Neither of the context inst var access trampolines save registers. Their operation could cause + arbitrary update of stack frames, so the assumption is that callers flush the stack before calling + the context inst var access trampolines, and that everything except the result is dead afterwards. */ + ceFetchContextInstVarTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceContextinstVar, "ceFetchContextInstVarTrampoline", 2, ReceiverResultReg, SendNumArgsReg, null, null, 0 /* emptyRegisterMask */, 1, SendNumArgsReg, 0); + + /* to keep ReceiverResultReg live. */ + ceStoreContextInstVarTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceContextinstVarvalue, "ceStoreContextInstVarTrampoline", 3, ReceiverResultReg, SendNumArgsReg, ClassReg, null, 0 /* emptyRegisterMask */, 1, ReceiverResultReg, 0); + ceCannotResumeTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCannotResume, "ceCannotResumeTrampoline", 0, null, null, null, null, 0 /* emptyRegisterMask */, 1, NoReg, 0); + + /* ceInvokeInterpreter is an optimization and a work-around. Historically we used setjmp/longjmp to reenter the + interpreter at the current C stack base. The C stack base is set at start-up and on each callback enter and + callback return. The interpreter must be invoked whenever a non-machine-code method must be run. That might + be when invoking an interpreter method from one of the send linking routines (ceSend:...), or on continuing from + an evaluation primitive such as primitiveExecuteMethod. The problem here is that such primitives could have + been invoked by the interpreter or by machine code. So some form of non-local jump is required. But at least as + early as MSVC Community 2017, the Microshaft longjmp performs stack unwinding which gets hoplessly confused + (bless its little heart) by any stack switch between machine code and C stack, and raises a spurious + Stack cookie instrumentation code detected a stack-based buffer overrun + error from the bowels of gs_report.c _GSHandlerCheck. + Since the CoInterpreter maintains the base of the C stack in CFramePointer & CStackPointer, it is straight-forward + for us to simply call interpret after doing the switch to the C stack, avoiding the stack unwind issue altogether. */ + ceInvokeInterpret = genInvokeInterpretTrampoline(); + + /* These two are unusual; they are reached by return instructions. */ + ceReturnToInterpreterTrampoline = genReturnToInterpreterTrampoline(); + ceBaseFrameReturnTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceBaseFrameReturn, "ceBaseFrameReturnTrampoline", 1, ReceiverResultReg, null, null, null, 0 /* emptyRegisterMask */, 0, NoReg, 0); + return null; +} + + /* LiveTypingCogit>>#genMoveCurrentMethodOopToRegister: */ +static NoDbgRegParms void +genMoveCurrentMethodOopToRegister(sqInt reg) +{ + addDependent(methodLabel, annotateAbsolutePCRef(genoperandoperand(MoveCwR, ((sqInt)methodLabel), reg))); +} + + /* LiveTypingCogit>>#genStorePop:ReceiverVariable:needsStoreCheck:needsImmutabilityCheck: */ +static NoDbgRegParms sqInt +genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(sqInt popBoolean, sqInt slotIndex, sqInt needsStoreCheck, sqInt needsImmCheck) +{ + AbstractInstruction *abstractInstruction; + sqInt i; + sqInt needsImmCheckSqInt; + sqInt needsStoreCheckSqInt; + sqInt topReg; + + ensureReceiverResultRegContainsSelf(); + storeToReg(ssTop(), TempReg); + + /* #MoveCw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveCwR, slotIndex, Extra0Reg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectInstVarType); + (abstractInstruction->annotation = IsRelativeCall); + ssFlushUpThroughReceiverVariable(slotIndex); + preserveTopInClassRegIfIsReceiverResultReg(); + ensureReceiverResultRegContainsSelf(); + + /* In two path compilation the receiver is young AND mutable, hence no store check nor immutability check is needed */ + needsStoreCheckSqInt = (!useTwoPaths) + && (needsStoreCheck); + needsImmCheckSqInt = needsImmCheck + && (!useTwoPaths); + + /* begin genGenericStorePop:slotIndex:destReg:needsStoreCheck:needsRestoreRcvr:needsImmutabilityCheck: */ +# if IMMUTABILITY + if (needsImmCheckSqInt) { + /* begin ssAllocateRequiredReg:upThrough: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << ClassReg), simStackPtr - 1, simNativeStackPtr); + ssStoreAndReplacePoptoReg(popBoolean, ClassReg); + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + return genStoreWithImmutabilityCheckSourceRegslotIndexdestRegscratchRegneedsStoreCheckneedRestoreRcvr(ClassReg, slotIndex, ReceiverResultReg, TempReg, needsStoreCheckSqInt, 1); + } +# endif // IMMUTABILITY + + topReg = allocateRegForStackEntryAtnotConflictingWith(0, (1U << ReceiverResultReg)); + ssStorePoptoReg(popBoolean, topReg); + return genStoreSourceRegslotIndexdestRegscratchReginFrameneedsStoreCheck(topReg, slotIndex, ReceiverResultReg, TempReg, needsFrame, needsStoreCheckSqInt); +} + + +/* The only reason we assert needsFrame here is that in a frameless method + ReceiverResultReg must and does contain only self, but the ceStoreCheck + trampoline expects the target of the store to be in ReceiverResultReg. So + in a frameless method we would have a conflict between the receiver and + the temote temp store, unless we we smart enough to realise that + ReceiverResultReg was unused after the literal variable store, unlikely + given that methods return self by default. */ +/* Stage the trampoline arguments so they survive the ARM C-argument + marshaller. + The value goes in TempReg and the slot index in Extra1Reg. + TempReg = CArg0Reg on ARMv8, + so when genMarshallNArgs relocates the + value (the arg that aliases CArg0Reg) it moves it into + Extra0Reg; + staging the index in Extra1Reg (NOT Extra0Reg) keeps the index from being + clobbered + by that relocation, which would otherwise pass the value as + both tempVarIndex and assignedObject. + */ + + /* LiveTypingCogit>>#genStorePop:RemoteTemp:At:needsStoreCheck: */ +static NoDbgRegParms sqInt +genStorePopRemoteTempAtneedsStoreCheck(sqInt popBoolean, sqInt slotIndex, sqInt remoteTempIndex, sqInt needsStoreCheck) +{ + AbstractInstruction *abstractInstruction; + sqInt offset; + sqInt topReg; + + storeToReg(ssTop(), TempReg); + + /* #MoveCw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveCwR, slotIndex, Extra1Reg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectRemoteTempVarType); + (abstractInstruction->annotation = IsRelativeCall); + assert(needsFrame); + + /* N.B. No need to check the stack for references because we generate code for + remote temp loads that stores the result in a register, deferring only the register push. */ + + /* begin ssAllocateRequiredReg: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << ReceiverResultReg), simStackPtr, simNativeStackPtr); + voidReceiverResultRegContainsSelf(); + offset = frameOffsetOfTemporary(remoteTempIndex); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, FPReg, ReceiverResultReg); + + /* begin genGenericStorePop:slotIndex:destReg:needsStoreCheck:needsRestoreRcvr:needsImmutabilityCheck: */ +# if IMMUTABILITY +# endif + + topReg = allocateRegForStackEntryAtnotConflictingWith(0, (1U << ReceiverResultReg)); + ssStorePoptoReg(popBoolean, topReg); + return genStoreSourceRegslotIndexdestRegscratchReginFrameneedsStoreCheck(topReg, slotIndex, ReceiverResultReg, TempReg, needsFrame, needsStoreCheck); +} + + +/* Stage the trampoline arguments so they survive the ARM C-argument + marshaller. + The value goes in TempReg and the temp index in Extra1Reg. + TempReg = CArg0Reg on ARMv8, + so when genMarshallNArgs relocates the + value (the arg that aliases CArg0Reg) it moves it into + Extra0Reg; + staging the index in Extra1Reg (NOT Extra0Reg) keeps the index from being + clobbered + by that relocation, which would otherwise pass the value as + both tempVarIndex and assignedObject. + */ + + /* LiveTypingCogit>>#genStorePop:TemporaryVariable: */ +static NoDbgRegParms sqInt +genStorePopTemporaryVariable(sqInt popBoolean, sqInt tempIndex) +{ + AbstractInstruction *abstractInstruction; + sqInt offset; + sqInt reg; + + if (!(inBlock > 0)) { + storeToReg(ssTop(), TempReg); + + /* #MoveCw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveCwR, tempIndex, Extra1Reg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectTempVarType); + (abstractInstruction->annotation = IsRelativeCall); + } + ssFlushUpThroughTemporaryVariable(tempIndex); + reg = ssStorePoptoPreferredReg(popBoolean, TempReg); + offset = frameOffsetOfTemporary(tempIndex); + + /* begin MoveR:Mw:r: */ + /* gen:operand:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, reg, offset, FPReg); + ((simStackAt(tempIndex + 1))->bcptr = bytecodePC); + return 0; +} + + +/* Generate a method return from within a method or a block. + Frameless method activation looks like + CISCs (x86): + receiver + args + sp-> ret pc. + RISCs (ARM): + receiver + args + ret pc in LR. + A fully framed activation is described in CoInterpreter + class>initializeFrameIndices. Return pops receiver and arguments off the + stack. Callee pushes the result. */ +/* Assumes return object to be in ReceiverResultReg already. + Collect the + method's return type before returning. + On architectures with a link + register (ARM), a frameless method holds the caller's + return address in + LinkReg and returns through it (see super's frameless RetN:), but the + + trampoline call is a branch-with-link that clobbers LinkReg. Save and + restore LinkReg + around the call so the return address survives. On CISC + (return address on the stack) + the call is stack-balanced and no + protection is needed. + */ + + /* LiveTypingCogit>>#genUpArrowReturn */ +static sqInt +genUpArrowReturn(void) +{ + AbstractInstruction *abstractInstruction; + sqInt i; + sqInt offset; + + if (!(primitiveIndex > 0)) { + genMoveCurrentMethodOopToRegister(TempReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectReturnType); + (abstractInstruction->annotation = IsRelativeCall); + } + + /* can't fall through */ + deadCode = 1; + if (inBlock > 0) { + assert(needsFrame); + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceNonLocalReturnTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + return 0; + } + if ( +# if IMMUTABILITY + needsFrame + && (!useTwoPaths) +# else + needsFrame +# endif + ) { + + /* MoveR:R: */ + genoperandoperand(MoveRR, FPReg, SPReg); + + /* PopR: */ + genoperand(PopR, FPReg); + + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + else { + offset = ((methodOrBlockNumArgs > (numRegArgs())) + || (regArgsHaveBeenPushed) + ? (methodOrBlockNumArgs + 1) * BytesPerWord + : 0); + + /* begin RetN: */ + genoperand(RetN, offset); + } + return 0; +} + + /* LiveTypingCogit>>#initSimStackForFramefulMethod: */ +static NoDbgRegParms void +initSimStackForFramefulMethod(sqInt startpc) +{ + AbstractInstruction *abstractInstruction; + SimStackEntry *cascade0; + SimStackEntry *desc; + sqInt i; + + /* N.B. Includes num args */ + simStackPtr = methodOrBlockNumTemps; + simSpillBase = methodOrBlockNumTemps + 1; + cascade0 = simSelf(); + (cascade0->type = SSBaseOffset); + (cascade0->spilled = 1); + (cascade0->registerr = FPReg); + (cascade0->offset = FoxMFReceiver); + (cascade0->liveRegister = NoReg); + + /* args */ + for (i = 1; i <= methodOrBlockNumArgs; i += 1) { + desc = simStackAt(i); + (desc->type = SSBaseOffset); + (desc->spilled = 1); + (desc->registerr = FPReg); + (desc->offset = FoxCallerSavedIP + (((methodOrBlockNumArgs - i) + 1) * BytesPerWord)); + (desc->bcptr = startpc); + } + + /* temps */ + for (i = (methodOrBlockNumArgs + 1); i <= simStackPtr; i += 1) { + desc = simStackAt(i); + (desc->type = SSBaseOffset); + (desc->spilled = 1); + (desc->registerr = FPReg); + (desc->offset = FoxMFReceiver - ((i - methodOrBlockNumArgs) * BytesPerWord)); + (desc->bcptr = startpc); + } + if ((inBlock > 0) + || (methodOrBlockNumArgs == 0)) { + return; + } + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectArgumentsTypeFrameful); + (abstractInstruction->annotation = IsRelativeCall); +} + + +/* Collect argument types on entry to a frameless method. Like + genUpArrowReturn, the + trampoline call is a branch-with-link that + clobbers LinkReg, and a frameless activation + holds the caller's return + address in LinkReg, so protect it with saveAndRestoreLinkRegAround: + (a + no-op on CISC). The spilled-args target reads the arguments at + stackPointer + 2*wordSize, + an offset that already accounts for this + protection push plus the trampoline's own + genSmalltalkToCStackSwitch + push, so no interpreter-side change is needed. + */ + + /* LiveTypingCogit>>#initSimStackForFramelessMethod: */ +static NoDbgRegParms void +initSimStackForFramelessMethod(sqInt startpc) +{ + AbstractInstruction *abstractInstruction; + SimStackEntry *cascade0; + SimStackEntry *desc; + sqInt i; + + cascade0 = simSelf(); + (cascade0->type = SSRegister); + (cascade0->spilled = 0); + (cascade0->registerr = ReceiverResultReg); + (cascade0->liveRegister = ReceiverResultReg); + assert(methodOrBlockNumTemps == methodOrBlockNumArgs); + assert((numRegArgs()) <= 2); + if (((methodOrBlockNumArgs >= 1) && (methodOrBlockNumArgs <= (numRegArgs())))) { + desc = simStackAt(1); + (desc->type = SSRegister); + (desc->spilled = 0); + (desc->registerr = Arg0Reg); + (desc->bcptr = startpc); + if (methodOrBlockNumArgs > 1) { + desc = simStackAt(2); + (desc->type = SSRegister); + (desc->spilled = 0); + (desc->registerr = Arg1Reg); + (desc->bcptr = startpc); + } + } + else { + for (i = 1; i <= methodOrBlockNumArgs; i += 1) { + desc = simStackAt(i); + (desc->type = SSBaseOffset); + (desc->registerr = SPReg); + (desc->spilled = 1); + (desc->offset = ((methodOrBlockNumArgs + 1) - i) * BytesPerWord); + (desc->bcptr = startpc); + } + } + simStackPtr = methodOrBlockNumArgs; + simSpillBase = methodOrBlockNumArgs + 1; + if ((inBlock > 0) + || (methodOrBlockNumArgs == 0)) { + return; + } + genMoveCurrentMethodOopToRegister(TempReg); + + /* saveAndRestoreLinkRegAround: */ + switch (methodOrBlockNumArgs) { + case 1: + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectArgumentsTypeFramelessUnspilled1Args); + (abstractInstruction->annotation = IsRelativeCall); + break; + case 2: + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectArgumentsTypeFramelessUnspilled2Args); + (abstractInstruction->annotation = IsRelativeCall); + break; + default: + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectArgumentsTypeFramelessSpilled); + (abstractInstruction->annotation = IsRelativeCall); + } +} + + +/* Do the store check. Answer the argument for the benefit of the code + generator; ReceiverResultReg may be caller-saved and hence smashed by this + call. Answering + it allows the code generator to reload ReceiverResultReg cheaply. + In Spur the only thing we leave to the run-time is adding the receiver to + the remembered set and setting its isRemembered bit. */ + + /* LiveTypingCogObjectRepresentationFor64BitSpur>>#generateObjectRepresentationTrampolines */ +static void +generateObjectRepresentationTrampolines(void) +{ + sqInt instVarIndex; + AbstractInstruction *jumpSC; + + ceCollectTypesForPrimitiveSlotAtPut = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectTypesForPrimitiveSlotAtputof, "ceCollectTypesForPrimitiveSlotAtPut", 3, Arg0Reg, Arg1Reg, ReceiverResultReg, null, CallerSavedRegisterMask, 1, NoReg, 0); +# if IMMUTABILITY + for (instVarIndex = 0; instVarIndex < NumStoreTrampolines; instVarIndex += 1) { + ceStoreTrampolines[instVarIndex] = (genStoreTrampolineCalledinstVarIndex(trampolineNamenumArgslimit("ceStoreTrampoline", instVarIndex, NumStoreTrampolines - 2), instVarIndex)); + } +# endif // IMMUTABILITY + + + /* begin genStoreCheckTrampoline */ + if (CheckRememberedInTrampoline) { + zeroOpcodeIndex(); + jumpSC = genCheckRememberedBitOfscratch(ReceiverResultReg, ABIResultReg); + assert(((jumpSC->opcode)) == JumpNonZero); + (jumpSC->opcode = JumpZero); + + /* RetN: */ + genoperand(RetN, 0); + jmpTarget(jumpSC, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } + ceStoreCheckTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(remember, "ceStoreCheckTrampoline", 1, ReceiverResultReg, null, null, null, ((CallerSavedRegisterMask | ((1U << ReceiverResultReg))) - ((1U << ReceiverResultReg))), 1, /* returnRegForStoreCheck */ + (((CallerSavedRegisterMask & ((1U << ReceiverResultReg))) != 0) + ? ReceiverResultReg + : ABIResultReg), CheckRememberedInTrampoline); + ceStoreCheckContextReceiverTrampoline = genStoreCheckContextReceiverTrampoline(); + ceScheduleScavengeTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceScheduleScavenge, "ceScheduleScavengeTrampoline", 0, null, null, null, null, CallerSavedRegisterMask, 1, NoReg, 0); + ceSmallActiveContextInMethodTrampoline = genActiveContextTrampolineLargeinBlockcalled(0, 0, "ceSmallMethodContext"); + ceSmallActiveContextInBlockTrampoline = genActiveContextTrampolineLargeinBlockcalled(0, InVanillaBlock, "ceSmallBlockContext"); + ceSmallActiveContextInFullBlockTrampoline = genActiveContextTrampolineLargeinBlockcalled(0, InFullBlock, "ceSmallFullBlockContext"); + ceLargeActiveContextInMethodTrampoline = genActiveContextTrampolineLargeinBlockcalled(1, 0, "ceLargeMethodContext"); + ceLargeActiveContextInBlockTrampoline = genActiveContextTrampolineLargeinBlockcalled(1, InVanillaBlock, "ceLargeBlockContext"); + ceLargeActiveContextInFullBlockTrampoline = genActiveContextTrampolineLargeinBlockcalled(1, InFullBlock, "ceLargeFullBlockContext"); +} + + +/* Generate the code for primitive 174, instVarAt:put:. Defer to + StackInterpreterPrimitives>>primitiveSlotAtPut for Contexts. + */ +/* c.f. StackInterpreter>>stSizeOf: SpurMemoryManager>>lengthOf:format: + fixedFieldsOf:format:length: + */ +/* Assumes arguments to be in Arg0Reg and Arg1Reg, and receiver to be in + ReceiverResultReg. + Collect the assigned slot value's type before + performing the primitive store. + The at:put:/instVarAt:put: primitive is + compiled into a frameless method, which on + architectures with a link + register (ARM) keeps the caller's return address in LinkReg and + returns + through it. The ceCollectTypesForPrimitiveSlotAtPut trampoline call is a + + branch-with-link that clobbers LinkReg, so protect it with + saveAndRestoreLinkRegAround: + (a no-op on CISC, where the return address + is on the stack). Without this the frameless + primitive returns into + itself instead of its caller, corrupting the stack. + */ + + /* LiveTypingCogObjectRepresentationFor64BitSpur>>#genPrimitiveSlotAtPut */ +static sqInt +genPrimitiveSlotAtPut(void) +{ + AbstractInstruction *abstractInstruction; + sqInt formatReg; + AbstractInstruction *jump64BitArgIsImmediate; + AbstractInstruction *jump64BitsOutOfBounds; + AbstractInstruction *jumpBadIndex; + AbstractInstruction *jumpBytesOutOfBounds; + AbstractInstruction *jumpBytesOutOfRange; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpImmutable; + AbstractInstruction *jumpIsBytes; + AbstractInstruction *jumpIsCompiledMethod; + AbstractInstruction *jumpIsContext; + AbstractInstruction *jumpIsShorts; + AbstractInstruction *jumpIsWords; + AbstractInstruction *jumpNonSmallIntegerValue; + AbstractInstruction *jumpNot64BitIndexable; + AbstractInstruction *jumpNot8ByteInteger; + AbstractInstruction *jumpNotIndexableBits; + AbstractInstruction *jumpNotPointers; + AbstractInstruction *jumpPointersOutOfBounds; + AbstractInstruction *jumpShortsOutOfBounds; + AbstractInstruction *jumpShortsOutOfRange; + AbstractInstruction *jumpWordsOutOfBounds; + AbstractInstruction *jumpWordsOutOfRange; + sqInt nSlotsOrBytesReg; + sqInt quickConstant; + AbstractInstruction *rejoin; + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectTypesForPrimitiveSlotAtPut); + (abstractInstruction->annotation = IsRelativeCall); + nSlotsOrBytesReg = Extra0Reg; + + /* begin genLoadArgAtDepth:into: */ + assert(1 < (numRegArgs())); + assert(0 < (numRegArgs())); + jumpImmediate = genJumpImmediate(ReceiverResultReg); + jumpBadIndex = genJumpNotSmallInteger(Arg0Reg); + genConvertSmallIntegerToIntegerInReg(Arg0Reg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 1, Arg0Reg); +# if IMMUTABILITY + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), TempReg); + + /* begin genJumpBaseHeaderImmutable: */ + /* TstCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, immutableBitMask(), TempReg); + jumpImmutable = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +# else // IMMUTABILITY + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), NoReg); +# endif // IMMUTABILITY + + genGetNumSlotsOfinto(ReceiverResultReg, nSlotsOrBytesReg); + + /* dispatch on format in a combination of highest dynamic frequency order first and convenience. + 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, weakArrayFormat(), formatReg); + jumpNotPointers = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + genGetClassIndexOfNonImminto(ReceiverResultReg, ClassReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ClassMethodContextCompactIndex, ClassReg); + + /* Fail to StackInterpreterPrimitives>>primitiveSlotAtPut for Context */ + /* optimistic store check; assume index in range (almost always is). */ + jumpIsContext = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + genStoreCheckReceiverRegvalueRegscratchReginFrame(ReceiverResultReg, Arg1Reg, TempReg, 0); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpPointersOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), Arg0Reg); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, Arg1Reg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotPointers, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpNonSmallIntegerValue = genJumpNotSmallInteger(Arg1Reg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), formatReg); + jumpIsBytes = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstShortFormat(), formatReg); + jumpIsShorts = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstLongFormat(), formatReg); + jumpIsWords = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, sixtyFourBitIndexableFormat(), formatReg); + jumpNotIndexableBits = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* fall through to 64-bit words */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, SendNumArgsReg); + genConvertSmallIntegerToIntegerInReg(SendNumArgsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + rejoin = genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jump64BitsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), Arg0Reg); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, SendNumArgsReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* Handle LargePositiveInteger (unsigned) or LargePositiveInteger and LargeNegativeInteger (signed) + for sixtyFourBitIndexableFormat */ + jmpTarget(jumpNonSmallIntegerValue, genoperandoperand(CmpCqR, sixtyFourBitIndexableFormat(), formatReg)); + jumpNot64BitIndexable = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + jump64BitArgIsImmediate = genJumpImmediate(Arg1Reg); + + /* Now check if the header is that of an 8 byte LargePositiveInteger */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, Arg1Reg, SendNumArgsReg); + quickConstant = headerForSlotsformatclassIndex(numSlotsMask(), formatMask(), classIndexMask()); + + /* begin AndCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, SendNumArgsReg); + quickConstant = headerForSlotsformatclassIndex(1, firstByteFormat(), ClassLargePositiveIntegerCompactIndex); + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, SendNumArgsReg); + jumpNot8ByteInteger = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BaseHeaderSize, Arg1Reg, SendNumArgsReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)rejoin)); + jmpTarget(jumpIsWords, genoperandoperand(CmpCqR, (((usqInt)0xFFFFFFFFU << 3) | 1), Arg1Reg)); + jumpWordsOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - 2, nSlotsOrBytesReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 4) - 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrBytesReg); + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpWordsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> ((shiftForWord()) - 1), Arg0Reg); + + /* MoveR:X32r:R: */ + genoperandoperandoperand(MoveRX32rR, TempReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsShorts, genoperandoperand(CmpCqR, (((usqInt)0xFFFF << 3) | 1), Arg1Reg)); + jumpShortsOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - 1, nSlotsOrBytesReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 2) - 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrBytesReg); + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpShortsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg0Reg, ReceiverResultReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg0Reg, ReceiverResultReg); + + /* #MoveR:M16:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRM16r, TempReg, BaseHeaderSize, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsBytes, genoperandoperand(CmpCqR, (((usqInt)0xFF << 3) | 1), Arg1Reg)); + jumpBytesOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), nSlotsOrBytesReg); + gAndCqRR(BytesPerWord - 1, formatReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, nSlotsOrBytesReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpBytesOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), formatReg); + jumpIsCompiledMethod = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BaseHeaderSize, Arg0Reg); + + /* MoveR:Xbr:R: */ + genoperandoperandoperand(MoveRXbrR, TempReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNot8ByteInteger, jmpTarget(jump64BitArgIsImmediate, jmpTarget(jumpNot64BitIndexable, jmpTarget(jumpIsContext, jmpTarget(jumpNotIndexableBits, jmpTarget(jumpBytesOutOfRange, jmpTarget(jumpShortsOutOfRange, jmpTarget(jumpWordsOutOfRange, jmpTarget(jumpIsCompiledMethod, jmpTarget(jumpPointersOutOfBounds, jmpTarget(jumpBytesOutOfBounds, jmpTarget(jumpShortsOutOfBounds, jmpTarget(jumpWordsOutOfBounds, jmpTarget(jump64BitsOutOfBounds, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))))))))))))); +# if IMMUTABILITY + jmpTarget(jumpImmutable, ((AbstractInstruction *) (((jumpIsContext->operands))[0]))); +# endif + + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, Arg0Reg); + genConvertIntegerToSmallIntegerInReg(Arg0Reg); + jmpTarget(jumpBadIndex, jmpTarget(jumpImmediate, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return 0; +} + + +/* Answer the Cogit's ABI as a string, if it adheres to a specific one. + Otherwise answer nil + */ + + /* SimpleStackBasedCogit>>#abiName */ +#if VMInvestigations +char * +abiName(void) +{ + return +# if defined(ABI) + ABI +# else + null +# endif + ; +} +#endif /* VMInvestigations */ + + /* SimpleStackBasedCogit>>#cogMethodHasExternalPrim: */ +sqInt +cogMethodHasExternalPrim(CogMethod *aCogMethod) +{ + sqInt primIndex; + + primIndex = primitiveIndexOfMethodheader((aCogMethod->methodObject), (aCogMethod->methodHeader)); + return (primIndex == PrimNumberExternalCall) + || (primIndex == PrimNumberFFICall); +} + + /* SimpleStackBasedCogit>>#cogMethodHasMachineCodePrim: */ +sqInt +cogMethodHasMachineCodePrim(CogMethod *aCogMethod) +{ + sqInt primIndex; + + primIndex = primitiveIndexOfMethodheader((aCogMethod->methodObject), (aCogMethod->methodHeader)); + return (((primIndex >= 1) && (primIndex <= MaxCompiledPrimitiveIndex))) + && ((((primitiveGeneratorTable[primIndex]).primitiveGenerator))); +} + + +/* Populate the last element of the three element array data with a sequence + of relative pc/value/object triples for the object & code references + embedded in the CogMethod. + If detailCPICs is true, include the list of targets as an array for sites + linked to closed PICs. + */ + + /* SimpleStackBasedCogit>>#cogMethodImports:into:detailCPICs: */ +#if VMInvestigations +sqInt +cogMethodImportsintodetailCPICs(CogMethod *cogMethod, sqInt data, sqInt detailCPICs) +{ + char *abort; + sqInt annotation; + sqInt importCount; + sqInt importsArray; + usqInt importsPtr[3]; + sqInt map; + sqInt mapByte; + usqInt mcpc; + void (*pfp)(void); + sqInt result; + + importCount = 0; + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = countCogMethodDataImportspccount(annotation, ((char *) mcpc), (&importCount)); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1: + importCount = (importCount + 2) + 1; + + /* for methodObject & selector + for the abort */ + if ((((primitiveIndex = primitiveIndexOf((cogMethod->methodObject)))) > 0) + && ((!(isQuickPrimitiveIndex(primitiveIndex))) + && (((pfp = functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto((cogMethod->methodObject), primitiveIndex, null))) != 0))) { + importCount += 1; + } + else { + primitiveIndex = 0; + } + if ((importsArray = instantiateClassindexableSize(classArray(), importCount * 3))) { + abort = ((char *)(callTargetFromReturnAddress(backEnd, (((usqInt)cogMethod)) + missOffset))); + assert((codeEntryFor(abort)) == abort); + storePointerUncheckedofObjectwithValue(2, data, importsArray); + storePointerUncheckedofObjectwithValue(0, importsArray, (((usqInt)(offsetof(CogMethod, methodObject)) << 3) | 1)); + storePointerUncheckedofObjectwithValue(1, importsArray, positive64BitIntegerFor((cogMethod->methodObject))); + storePointerUncheckedofObjectwithValue(2, importsArray, (cogMethod->methodObject)); + storePointerUncheckedofObjectwithValue(3, importsArray, (((usqInt)(offsetof(CogMethod, selector)) << 3) | 1)); + storePointerUncheckedofObjectwithValue(4, importsArray, positive64BitIntegerFor((cogMethod->selector))); + storePointerUncheckedofObjectwithValue(5, importsArray, (cogMethod->selector)); + storePointerUncheckedofObjectwithValue(6, importsArray, (((usqInt)missOffset << 3) | 1)); + storePointerUncheckedofObjectwithValue(7, importsArray, positive64BitIntegerFor(callTargetFromReturnAddress(backEnd, (((usqInt)cogMethod)) + missOffset))); + storePointerUncheckedofObjectwithValue(8, importsArray, stringForCString(codeEntryNameFor(abort))); + + /* the first and second entries are the methodObject and selector in the CogMethod header + the third entry in imports is the abort call */ + if (primitiveIndex > 0) { + storePointerUncheckedofObjectwithValue(9, importsArray, (((usqInt)-1 << 3) | 1)); + storePointerUncheckedofObjectwithValue(10, importsArray, positive64BitIntegerFor(((usqInt)pfp))); + storePointerUncheckedofObjectwithValue(11, importsArray, stringForCString("primitiveFunction")); + importsPtr[0] = (((usqInt) ((((usqInt *) (firstIndexableField(importsArray)))) + 12))); + } + else { + importsPtr[0] = (((usqInt) ((((usqInt *) (firstIndexableField(importsArray)))) + 9))); + } + + /* pass both the next place to store in imports (importsPtr at: 0) and the imports array (importsPtr at: 1) to the store enumerator */ + importsPtr[1] = importsArray; + importsPtr[2] = detailCPICs; + enumeratingCogMethod = cogMethod; + + /* N.B. answers error code from storeCogMethodDataImports:pc:pointer: if any */ + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = storeCogMethodDataImportspcpointer(annotation, ((char *) mcpc), (&importsPtr)); + if (result) { + return result; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + return 0; + } + else { + return PrimErrNoMemory; + } +} +#endif /* VMInvestigations */ + + +/* Compile the jump instruction(s) at the end of the method that dispatch to + each block body. + */ + + /* SimpleStackBasedCogit>>#compileBlockDispatch */ +static sqInt +compileBlockDispatch(void) +{ + AbstractInstruction *jumpSkip; + + assert(blockCount > 0); + + /* Generate the special block entry for primitiveClosureValueNoContextSwitch + that signals a context-switch is not possible by zeroing SendNumArgsReg. + Even though conceptually this isn't needed if no blocks build a frame it has + to be present so that primitiveClosureValueNoContextSwitch can call it. */ + blockEntryNoContextSwitch = genoperandoperand(MoveCqR, 0, SendNumArgsReg); + + /* Now generate a binary search through start pcs to jump to blocks. 1 block is a special case. */ + jumpSkip = genoperand(Jump, ((sqInt)0)); + + /* Set OK to context switch flag to non-zero. */ + blockEntryLabel = genoperandoperand(MoveRR, ReceiverResultReg, SendNumArgsReg); + jmpTarget(jumpSkip, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + if (blockCount > 1) { + genLoadSlotsourceRegdestReg(ClosureStartPCIndex, ReceiverResultReg, TempReg); + } + compileBlockDispatchFromto(0, blockCount - 1); + return 0; +} + + +/* After pushing the temporaries but before the stack limit check a primitive + method needs to fetch the error code, if any. If the primitive has failed, + call the trampoline + that will assign it to the last temp. */ + + /* SimpleStackBasedCogit>>#compileGetErrorCode */ +static void +compileGetErrorCode(void) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *jmpNoError; + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, primFailCodeAddress(), TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, TempReg); + jmpNoError = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + addDependent(methodLabel, annotateAbsolutePCRef(genoperandoperand(MoveCwR, ((sqInt)methodLabel), ClassReg))); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceReapAndResetErrorCodeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpTarget(jmpNoError, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); +} + + /* SimpleStackBasedCogit>>#compileInterpreterPrimitive */ +static sqInt +compileInterpreterPrimitive(void) +{ + sqInt flags; + void (*primitiveRoutine)(void); + + flags = 0; + primitiveRoutine = functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(methodObj, primitiveIndex, (&flags)); + assert((((flags & PrimCallCollectsProfileSamples) != 0) + ? ((flags & PrimCallNeedsNewMethod) != 0) + : 1)); + if (((flags & PrimCallOnSmalltalkStack) != 0)) { + return compileOnStackExternalPrimitiveflags(primitiveRoutine, flags); + } + return compileInterpreterPrimitiveflags(primitiveRoutine, flags); +} + + +/* Compile a call to an interpreter primitive. Call the C routine with the + usual stack-switching dance, test the primFailCode and then either + return on success or continue to the method body. */ + + /* SimpleStackBasedCogit>>#compileInterpreterPrimitive:flags: */ +static NoDbgRegParms sqInt +compileInterpreterPrimitiveflags(void (*primitiveRoutine)(void), sqInt flags) +{ + AbstractInstruction *continueAfterRetry; + AbstractInstruction *jmpOnFailure; + AbstractInstruction *jmpRetryOrNil; + sqInt record; + + assert(!((registerisInMask(VarBaseReg, ABICallerSavedRegisterMask)))); + + /* Save processor fp, sp and return pc in the interpreter's frame stack and instruction pointers */ + genExternalizePointersForPrimitiveCall(); + + /* Switch to the C stack. */ + + /* begin genLoadCStackPointersForPrimCall */ + if (cFramePointerInUse) { + genLoadCStackPointers(backEnd); + } + else { + genLoadCStackPointer(backEnd); + } + + /* Old old full prim trace is in VMMaker-eem.550 and prior. + Old simpler full prim trace is in VMMaker-eem.2969 and prior. */ + if ((record = recordPrimTraceForMethod(methodObj))) { + genFastPrimTraceUsingandsetNewMethod(ClassReg, SendNumArgsReg, ((flags & PrimCallNeedsNewMethod) != 0)); + } + + /* Clear the primFailCode and set argumentCount */ + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, primFailCodeAddress()); + if (methodOrBlockNumArgs) { + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, methodOrBlockNumArgs, TempReg); + } + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, argumentCountAddress()); + + /* If required, set newMethod */ + if ((!record) + && (((flags & PrimCallNeedsNewMethod) != 0))) { + genLoadNewMethod(); + } + + /* Invoke the primitive. If the primitive (potentially) contains a call-back then its code + may disappear and consequently we cannot return here, since here may evaporate. + Instead sideways-call the routine, substituting cePrimReturnEnterCogCode[Profiling] + as the return address, so the call always returns there. + To save complications also use this path when profiling, since profiling slows down + execution by much more than this sideways call and it means we have but one + version of the profiling sample code to worry about. */ + + /* #PrefetchAw: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(PrefetchAw, primFailCodeAddress()); + if (((flags & (PrimCallMayEndureCodeCompaction + PrimCallCollectsProfileSamples)) != 0)) { + /* The ceActivateFailingPrimitiveMethod: machinery can't handle framelessness. */ + needsFrame = 1; + + /* begin genMarshallNArgs:arg:arg:arg:arg: */ + + /* WIN64 ABI always reserves shadow space on the stack for callee to save up to 4 register parameters */ + + /* #genSubstituteReturnAddress: #PushCw: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(PushCw, (((flags & PrimCallCollectsProfileSamples) != 0) + ? cePrimReturnEnterCogCodeProfiling + : cePrimReturnEnterCogCode)); + + /* #JumpFullRT: #JumpFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(JumpFull, ((sqInt)(((usqInt)primitiveRoutine)))); + return 0; + } + + /* Call the C primitive routine. */ + + /* begin genMarshallNArgs:arg:arg:arg:arg: */ + + /* WIN64 ABI always reserves shadow space on the stack for callee to save up to 4 register parameters */ + + /* #CallFullRT: #CallFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(CallFull, ((usqInt)primitiveRoutine)); + + /* begin genRemoveNArgsFromStack: */ + jmpRetryOrNil = compileJumpToRetryOfonPrimitiveFail(primitiveRoutine, primitiveIndex); + + /* placing the test here attributes the tick to the primitive plus any checkForAndFollowForwardedPrimitiveState + scanning, but attributes all of a failing primitive to the current method (in ceStackOverflow: on frame build). */ + continueAfterRetry = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* Switch back to the Smalltalk stack. Stack better be in either of these two states: + success: stackPointer -> result (was receiver) + arg1 + ... + argN + return pc + failure: receiver + arg1 + ... + stackPointer -> argN + return pc */ + genLoadStackPointersForPrimCall(backEnd, ClassReg); + + /* genLoadStackPointersForPrimCall: leaves the stack in these states: + NoLinkRegister LinkRegister + success: result (was receiver) stackPointer -> result (was receiver) + stackPointer -> arg1 arg1 + ... ... + argN argN + return pc + failure: receiver receiver + arg1 arg1 + ... ... + argN stackPointer -> argN + stackPointer -> return pc + which corresponds to the stack on entry after pushRegisterArgs. + In either case we can write the instructionPointer to top of stack or load it into the LinkRegister to reestablish the return pc. */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, instructionPointerAddress(), ClassReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ClassReg, 0, SPReg); + + /* Test primitive failure */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, primFailCodeAddress(), TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, TempReg); + jmpOnFailure = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Fetch result from stack */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord, SPReg, ReceiverResultReg); + + /* RetN: */ + genoperand(RetN, BytesPerWord); + maybeCompileRetryOfonPrimitiveFailflagsjumpcontinue(primitiveRoutine, primitiveIndex, flags, jmpRetryOrNil, continueAfterRetry); + + /* Jump to restore of receiver reg and proceed to frame build for failure. */ + jmpTarget(jmpOnFailure, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* Restore receiver reg from stack. If on RISCs ret pc is in LinkReg, if on CISCs ret pc is on stack. */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord * (methodOrBlockNumArgs + 1), SPReg, ReceiverResultReg); + return 0; +} + + +/* Compile a fast call of a C primitive using the current stack page, + avoiding the stack switch except on failure. + This convention still uses stackPointer and argumentCount to access + operands. Push all operands to the stack, + assign stackPointer, argumentCount, and zero primFailCode. Make the call + (saving a LinkReg if required). + Test for failure and return. On failure, if there is an accessor depth, + assign framePointer and newMethod, + do the stack switch, call checkForAndFollowForwardedPrimitiveState, and + loop back if forwarders are found. + Fall through to frame build. + + Things get messy when profiling. Because of the potential retry, and the + need to take the profile sample after a + retry if it occurs, two separate samplings must be done, one immediately + before returning on success (potentially + after retry), and one immediately before falling through to failure. + However, on failure there is no need to take + a sample immediately. Provided the stack limit is smashed then a sample + will be taken on frame build. */ + + /* SimpleStackBasedCogit>>#compileOnStackExternalPrimitive:flags: */ +static NoDbgRegParms sqInt +compileOnStackExternalPrimitiveflags(void (*primitiveRoutine)(void), sqInt flags) +{ + AbstractInstruction *abstractInstruction; + sqInt calleeSavedRegisterMask; + sqInt callTargetSqInt; + AbstractInstruction *continueAfterProfileSampleOnSuccess; + AbstractInstruction *jmpRetryOrFail; + AbstractInstruction *jumpToTakeSampleOnSuccess; + sqInt reg; + sqInt retpcOffset; + AbstractInstruction *retry; + AbstractInstruction *skip; + sqInt spRegSaveRegister; + + jumpToTakeSampleOnSuccess = ((AbstractInstruction *) 0); + assert(((flags & PrimCallOnSmalltalkStack) != 0)); + assert(!((registerisInMask(VarBaseReg, ABICallerSavedRegisterMask)))); + if (recordFastCCallPrimTraceForMethod(methodObj)) { + genFastPrimTraceUsingandsetNewMethod(ClassReg, SendNumArgsReg, 0); + } + + /* Clear the primFailCode and set argumentCount */ + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, primFailCodeAddress()); + if (methodOrBlockNumArgs) { + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, methodOrBlockNumArgs, TempReg); + } + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, argumentCountAddress()); + genExternalizeStackPointerForFastPrimitiveCall(); + + /* If profiling also write the framePointer and instructionPointer so that + ceTakeProfileSample: can activate the profiling process immediately. */ + if (((flags & PrimCallCollectsProfileSamples) != 0)) { + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, FPReg, framePointerAddress()); + if (regArgsHaveBeenPushed) { + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, SendNumArgsReg, instructionPointerAddress()); + } + else { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, SPReg, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, instructionPointerAddress()); + } + } + + /* We may need to save LinkReg and/or SPReg, and given the stack machinations + it is much easier to save them in callee saved registers than on the stack itself. */ + calleeSavedRegisterMask = ((ABICalleeSavedRegisterMask | ((1U << ClassReg))) - ((1U << ClassReg))); + spRegSaveRegister = NoReg; + retry = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + if (((flags & PrimCallOnSmalltalkStackAlign2x) != 0)) { + gAndCqRR((-1 - (((BytesPerWord * 2) - 1))), SPReg, NativeSPReg); + } + else { + } + + /* begin genMarshallNArgs:arg:arg:arg:arg: */ + + /* WIN64 ABI always reserves shadow space on the stack for callee to save up to 4 register parameters */ + + /* If the primitive is in the interpreter then its address won't change relative to the code zone over time, + whereas if it is in a plugin its address could change if the module is un/re/over/loaded. + So if in the interpreter and in range use a normal call instruction. */ + if (((!(flags & PrimCallIsExternalCall))) + && (is32BitSignedImmediate(backEnd, maximumDistanceFromCodeZone(((usqInt)primitiveRoutine))))) { + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ((usqInt)primitiveRoutine)); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* #CallFullRT: #CallFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(CallFull, ((usqInt)primitiveRoutine)); + } + + /* begin genRemoveNArgsFromStack: */ + + /* Remember to restore the native stack pointer to point to the C stack, + otherwise the Smalltalk frames will get overwritten on an interrupt. */ + + /* test primFailCode and jump to failure sequence if non-zero */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, primFailCodeAddress(), TempReg); + if (spRegSaveRegister != NoReg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, spRegSaveRegister, SPReg); + } + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, TempReg); + jmpRetryOrFail = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* If profiling, check for a sample now. The alternative is to have two checks and two calls, + which would include the cost of checkForAndFollowForwardedPrimitiveState in the sample. + But that's excessive given that retry failures are very rare. */ + if (((flags & PrimCallCollectsProfileSamples) != 0)) { + /* begin genCheckForProfileTimerTick: */ + reg = RAX; + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, nextProfileTickAddress(), Arg1Reg); + gMovePerfCnt64RL(reg, (0)); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, reg); + jumpToTakeSampleOnSuccess = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + } + + /* At this point the primitive has cut back stackPointer to point to the result. */ + continueAfterProfileSampleOnSuccess = genoperandoperand(MoveAwR, stackPointerAddress(), TempReg); + + /* get result and restore retpc */ + + /* The original retpc is (argumentCount + 1) words below stackPointer. */ + retpcOffset = -((methodOrBlockNumArgs + 1) * BytesPerWord); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, retpcOffset, TempReg, ClassReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, SPReg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, TempReg, ReceiverResultReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ClassReg, 0, TempReg); + + /* RetN: */ + genoperand(RetN, 0); + if (((flags & PrimCallCollectsProfileSamples) != 0)) { + jmpTarget(jumpToTakeSampleOnSuccess, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genLoadNewMethod(); + genTakeProfileSample(continueAfterProfileSampleOnSuccess); + } + + /* primitive failure. if there is an accessor depth, scan and retry on failure (but what if failing for out of memory?). + Retry by jumping back to the retry label. */ + jmpTarget(jmpRetryOrFail, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + if ((accessorDepthForPrimitiveMethod(methodObj)) >= 0) { + /* Given that following primitive state to the accessor depth is recursive, we're asking for + trouble if we run the fixup on the Smalltalk stack page. Run it on the full C stack instead. + This won't be a performance issue since primitive failure should be very rare. + Note that this cannot loop on failure because after the first retry the forwarders will have + been followed and so checkForAndFollowForwardedPrimitiveState will answer false on a + second failure. */ + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, FPReg, framePointerAddress()); + + /* #MoveCw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveCwR, ((usqInt)primitiveRoutine), TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, primitiveFunctionPointerAddress()); + genLoadNewMethod(); + + /* begin genLoadCStackPointersForPrimCall */ + if (cFramePointerInUse) { + genLoadCStackPointers(backEnd); + } + else { + genLoadCStackPointer(backEnd); + } + + /* begin genMarshallNArgs:arg:arg:arg:arg: */ + + /* WIN64 ABI always reserves shadow space on the stack for callee to save up to 4 register parameters */ + + /* begin CallAnyFixedRT: */ + if (is32BitSignedImmediate(backEnd, maximumDistanceFromCodeZone(((usqInt)checkForAndFollowForwardedPrimitiveState)))) { + callTargetSqInt = ((usqIntptr_t)checkForAndFollowForwardedPrimitiveState); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, callTargetSqInt); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* #CallFullRT: #CallFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(CallFull, ((usqIntptr_t)checkForAndFollowForwardedPrimitiveState)); + } + genLoadStackPointersForPrimCall(backEnd, ClassReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ABIResultReg); + skip = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, primFailCodeAddress()); + + /* Jump: */ + genoperand(Jump, ((sqInt)retry)); + jmpTarget(skip, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } + else { + if (((flags & PrimCallOnSmalltalkStackAlign2x) != 0)) { + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, stackPointerAddress(), TempReg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, BytesPerWord, TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, SPReg); + } + } + + /* must reload SPReg to undo any alignment change, + Remember to restore the native stack pointer to point to the C stack, + otherwise the Smalltalk frames will get overwritten on an interrupt. */ + + /* If profiling, check for a sample now, in this case for failure. Avoid taking the sample. If the sample should + be taken, smash the stack limit and allow the subsequent post frame build stack check to do the sampling. */ + if (((flags & PrimCallCollectsProfileSamples) != 0)) { + /* begin genCheckForProfileTimerTick: */ + reg = RAX; + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, nextProfileTickAddress(), Arg1Reg); + gMovePerfCnt64RL(reg, (0)); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, reg); + skip = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* Invert the branch to jump if a sample should /not/ be taken. */ + (skip->opcode = inverseBranchFor((skip->opcode))); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, -1, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, stackLimitAddress()); + jmpTarget(skip, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } + + /* The LinkRegister now contains the return address either of the primitive call or of checkForAndFollowForwardedPrimitiveState. + It must be restored to the return address of the send invoking this primitive method. */ + + /* Finally remember to reload ReceiverResultReg if required. Even if + arguments have been pushed, the prolog sequence assumes it is live. */ + if (((ABICallerSavedRegisterMask & ((1U << ReceiverResultReg))) != 0)) { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, (methodOrBlockNumArgs + 1) * BytesPerWord, SPReg, ReceiverResultReg); + } + + /* continue to frame build... */ + return 0; +} + + +/* Compile one method cache probe in an OpenPIC's lookup of selector. + Answer the jump taken if the selector probe fails. + The class tag of the receiver must be in SendNumArgsReg. ClassReg and + TempReg are used as scratch registers. + On a hit, the offset of the entry is in ClassReg. */ + + /* SimpleStackBasedCogit>>#compileOpenPICMethodCacheProbeFor:withShift:baseRegOrNone: */ +static NoDbgRegParms AbstractInstruction * +compileOpenPICMethodCacheProbeForwithShiftbaseRegOrNone(sqInt selector, sqInt shift, sqInt baseRegOrNone) +{ + AbstractInstruction *jumpSelectorMiss; + sqInt offset; + + /* MoveR:R: */ + genoperandoperand(MoveRR, SendNumArgsReg, ClassReg); + maybeShiftClassTagRegisterForMethodCacheProbe(ClassReg); + annotateobjRef(genoperandoperand(XorCwR, selector, ClassReg), selector); + assert(shift <= (shiftForWord())); + + /* Need to shift the hash right by shift to form the probe, and then shift the probe left by shiftForWord to form the index. + So shift left by shiftForWord - shift and and with the shifted mask. */ + if (shift < (shiftForWord())) { + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - shift, ClassReg); + } + genoperandoperand(AndCqR, ((sqInt)((usqInt)(MethodCacheMask) << (shiftForWord()))), ClassReg); + if (baseRegOrNone == NoReg) { + offset = (methodCacheAddress()) + ((((usqInt)(MethodCacheSelector) << (shiftForWord())))); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, ClassReg, TempReg); + } + else { + /* AddR:R: */ + genoperandoperand(AddRR, baseRegOrNone, ClassReg); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, ((sqInt)((usqInt)(MethodCacheSelector) << (shiftForWord()))), ClassReg, TempReg); + } + annotateobjRef(genoperandoperand(CmpCwR, selector, TempReg), selector); + jumpSelectorMiss = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + if (baseRegOrNone == NoReg) { + offset = (methodCacheAddress()) + ((((usqInt)(MethodCacheClass) << (shiftForWord())))); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, ClassReg, TempReg); + } + else { + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, ((sqInt)((usqInt)(MethodCacheClass) << (shiftForWord()))), ClassReg, TempReg); + } + assert(!(0 /* (SendNumArgsReg = SPReg) */)); + genoperandoperand(CmpRR, SendNumArgsReg, TempReg); + return jumpSelectorMiss; +} + + +/* Compile the code for an open PIC. Perform a probe of the first-level + method lookup cache followed by a call of ceSendFromInLineCacheMiss: if + the probe fails. */ + + /* SimpleStackBasedCogit>>#compileOpenPIC:numArgs: */ +static NoDbgRegParms void +compileOpenPICnumArgs(sqInt selector, sqInt numArgs) +{ + sqInt cacheBaseReg; + AbstractInstruction *itsAHit; + AbstractInstruction *jumpBCMethod; + AbstractInstruction *jumpClassMiss; + AbstractInstruction *jumpSelectorMiss; + sqInt offset; + sqInt quickConstant; + sqInt reg; + + /* begin preenMethodLabel */ + /* setLabelOffset: */ + ((methodLabel->operands))[1] = 0; + compilePICAbort(numArgs); + entry = genGetClassTagOfintoscratchReg(ReceiverResultReg, SendNumArgsReg, TempReg); + cacheBaseReg = NoReg; + if (!(/* #isWithinMwOffsetRange: #addressIsInCodeZone: */ + ((((usqInt)(methodCacheAddress()))) >= codeBase) + && ((((usqInt)(methodCacheAddress()))) < limitAddress))) { + quickConstant = methodCacheAddress(); + reg = (cacheBaseReg = Extra0Reg); + + /* begin MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, quickConstant, reg); + } + + /* Do first of three probes. See CoInterpreter>>lookupInMethodCacheSel:classTag: */ + jumpSelectorMiss = compileOpenPICMethodCacheProbeForwithShiftbaseRegOrNone(selector, 0, cacheBaseReg); + jumpClassMiss = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Fetch the method. The interpret trampoline requires the bytecoded method in SendNumArgsReg */ + offset = (cacheBaseReg == NoReg + ? (methodCacheAddress()) + ((((usqInt)(MethodCacheMethod) << (shiftForWord())))) + : ((sqInt)((usqInt)(MethodCacheMethod) << (shiftForWord())))); + + /* begin MoveMw:r:R: */ + itsAHit = genoperandoperandoperand(MoveMwrR, offset, ClassReg, SendNumArgsReg); + + /* If the method is compiled jump to its unchecked entry-point, otherwise interpret it. */ + genLoadSlotsourceRegdestReg(HeaderIndex, SendNumArgsReg, ClassReg); + jumpBCMethod = genJumpImmediate(ClassReg); + jmpTarget(jumpBCMethod, picInterpretAbort); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, cmNoCheckEntryOffset, ClassReg); + + /* JumpR: */ + genoperand(JumpR, ClassReg); + + /* First probe missed. Do second of three probes. Shift hash right one and retry. */ + jmpTarget(jumpSelectorMiss, jmpTarget(jumpClassMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + jumpSelectorMiss = compileOpenPICMethodCacheProbeForwithShiftbaseRegOrNone(selector, 1, cacheBaseReg); + + /* JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)itsAHit)); + + /* Second probe missed. Do last probe. Shift hash right two and retry. */ + jmpTarget(jumpSelectorMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpSelectorMiss = compileOpenPICMethodCacheProbeForwithShiftbaseRegOrNone(selector, 2, cacheBaseReg); + + /* JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)itsAHit)); + + /* Last probe missed. Call ceSendFromInLineCacheMiss: to do the full lookup. */ + jmpTarget(jumpSelectorMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + if ((numRegArgs()) > 0) { + genPushRegisterArgsForNumArgsscratchReg(backEnd, numArgs, SendNumArgsReg); + } + genSmalltalkToCStackSwitch(1); + addDependent(methodLabel, annotateAbsolutePCRef(genoperandoperand(MoveCwR, ((sqInt)methodLabel), SendNumArgsReg))); + compileCallFornumArgsargargargargresultRegregsToSave(ceSendFromInLineCacheMiss, 1, SendNumArgsReg, null, null, null, NoReg, 0 /* emptyRegisterMask */); +} + + +/* Compile one method cache probe in a perform: primitive's lookup of + selector. Answer the jump taken if the selector probe fails. */ + + /* SimpleStackBasedCogit>>#compilePerformMethodCacheProbeFor:withShift:baseRegOrNone: */ +static NoDbgRegParms AbstractInstruction * +compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(sqInt selectorReg, sqInt shift, sqInt baseRegOrNone) +{ + AbstractInstruction *jumpSelectorMiss; + sqInt offset; + + /* MoveR:R: */ + genoperandoperand(MoveRR, SendNumArgsReg, ClassReg); + maybeShiftClassTagRegisterForMethodCacheProbe(ClassReg); + + /* XorR:R: */ + genoperandoperand(XorRR, selectorReg, ClassReg); + assert(shift <= (shiftForWord())); + + /* Need to shift the hash right by shift to form the probe, and then shift the probe left by shiftForWord to form the index. + So shift left by shiftForWord - shift and and with the shifted mask. */ + if (shift < (shiftForWord())) { + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - shift, ClassReg); + } + genoperandoperand(AndCqR, ((sqInt)((usqInt)(MethodCacheMask) << (shiftForWord()))), ClassReg); + if (baseRegOrNone == NoReg) { + offset = (methodCacheAddress()) + ((((usqInt)(MethodCacheSelector) << (shiftForWord())))); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, ClassReg, TempReg); + } + else { + /* AddR:R: */ + genoperandoperand(AddRR, baseRegOrNone, ClassReg); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, ((sqInt)((usqInt)(MethodCacheSelector) << (shiftForWord()))), ClassReg, TempReg); + } + assert(!((selectorReg == SPReg))); + genoperandoperand(CmpRR, selectorReg, TempReg); + jumpSelectorMiss = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + if (baseRegOrNone == NoReg) { + offset = (methodCacheAddress()) + ((((usqInt)(MethodCacheClass) << (shiftForWord())))); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, ClassReg, TempReg); + } + else { + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, ((sqInt)((usqInt)(MethodCacheClass) << (shiftForWord()))), ClassReg, TempReg); + } + assert(!(0 /* (SendNumArgsReg = SPReg) */)); + genoperandoperand(CmpRR, SendNumArgsReg, TempReg); + return jumpSelectorMiss; +} + + +/* Compile a primitive. If possible, performance-critical primitives will + be generated by their own routines (primitiveGenerator). Otherwise, + if there is a primitive at all, we call the C routine with the usual + stack-switching dance, test the primFailCode and then either return + on success or continue to the method body. */ + + /* SimpleStackBasedCogit>>#compilePrimitive */ +static sqInt +compilePrimitive(void) +{ + sqInt code; + sqInt flags; + AbstractInstruction *jumpNotSmallInteger; + sqInt opcodeIndexAtPrimitive; + PrimitiveDescriptor *primitiveDescriptor; + void (*primitiveRoutine)(void); + sqInt reg; + + flags = 0; + primitiveDescriptor = ((PrimitiveDescriptor *) 0); + if (!primitiveIndex) { + return 0; + } + + /* If a descriptor specifies an argument count (by numArgs >= 0) then it must match + for the generated code to be correct. For example for speed many primitives use + ResultReceiverReg instead of accessing the stack, so the receiver better be at + numArgs down the stack. Use the interpreter version if not. */ + if ((((primitiveDescriptor = primitiveGeneratorOrNil()))) + && ((((primitiveDescriptor->primitiveGenerator))) + && ((((primitiveDescriptor->primNumArgs)) < 0) + || (((primitiveDescriptor->primNumArgs)) == methodOrBlockNumArgs)))) { + /* Note opcodeIndex so that any arg load instructions + for unimplemented primitives can be discarded. */ + opcodeIndexAtPrimitive = opcodeIndex; + code = ((primitiveDescriptor->primitiveGenerator))(); + if ((code < 0) + && (code != UnimplementedPrimitive)) { + return code; + } + + /* Generator failed, so no point continuing... + If the primitive can never fail then there is nothing more that needs to be done. */ + if (code == UnfailingPrimitive) { + return 0; + } + + /* If the machine code version handles all cases the only reason to call the interpreter + primitive is to reap the primitive error code. Don't bother if it isn't used. */ + if ((code == CompletePrimitive) + && (!(/* methodUsesPrimitiveErrorCode:header: */ + ((primitiveIndexOfMethodheader(methodObj, methodHeader)) > 0) + && ((longStoreBytecodeForHeader(methodHeader)) == (fetchByteofObject((startPCOfMethodHeader(methodHeader)) + (sizeOfCallPrimitiveBytecode(methodHeader)), methodObj)))))) { + return 0; + } + + /* Discard any arg load code generated by the primitive generator. */ + if (code == UnimplementedPrimitive) { + opcodeIndex = opcodeIndexAtPrimitive; + } + } + primitiveRoutine = functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(methodObj, primitiveIndex, (&flags)); + if ((primitiveRoutine == 0) + || (primitiveRoutine == (((void (*)(void)) primitiveFail)))) { + return genFastPrimFail(); + } + + /* no primitive */ + if ((primitiveRoutine == (((void (*)(void)) primitiveHighResClock))) + && (methodOrBlockNumArgs == 0)) { + /* begin genPrimitiveHighResClock64 */ + reg = RAX; + assert(registerisNotInMask(reg, registerMaskForand(ReceiverResultReg, Arg1Reg))); + gMovePerfCnt64RL(reg, (0)); + gLogicalShiftRightCqRR((numSmallIntegerBits()) - 1, reg, Arg1Reg); + if (!(setsConditionCodesFor(lastOpcode(), JumpZero))) { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, Arg1Reg); + } + jumpNotSmallInteger = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genConvertIntegerInRegtoSmallIntegerInReg(reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSmallInteger, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genInstantiate64BitPositiveIntegerValueintoscratchRegmayFail(reg, ReceiverResultReg, Arg1Reg, 0); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + code = UnfailingPrimitive; + if (code != UnimplementedPrimitive) { + return code; + } + } + if (((flags & PrimCallOnSmalltalkStack) != 0)) { + return compileOnStackExternalPrimitiveflags(primitiveRoutine, flags); + } + return compileInterpreterPrimitiveflags(primitiveRoutine, flags); +} + + /* SimpleStackBasedCogit>>#extendedPushBytecode */ +static sqInt +extendedPushBytecode(void) +{ + usqInt variableIndex; + usqInt variableType; + + variableType = (((usqInt)(byte1)) >> 6) & 3; + variableIndex = byte1 & 0x3F; + if (!variableType) { + return genPushReceiverVariable(variableIndex); + } + if (variableType == 1) { + return genPushTemporaryVariable(variableIndex); + } + if (variableType == 2) { + return genPushLiteralIndex(variableIndex); + } + return genPushLiteralVariable(variableIndex); +} + + /* SimpleStackBasedCogit>>#extendedStoreAndPopBytecode */ +static sqInt +extendedStoreAndPopBytecode(void) +{ + AbstractInstruction *abstractInstruction; + usqInt variableIndex; + usqInt variableType; + + variableType = (((usqInt)(byte1)) >> 6) & 3; + variableIndex = byte1 & 0x3F; + if (!variableType) { + return genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(1, variableIndex, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } + if (variableType == 1) { + genStorePopTemporaryVariable(1, variableIndex); + + /* needs a fake map entry if Immutability is ON... */ +# if IMMUTABILITY + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); +# endif // IMMUTABILITY + + return 0; + } + if (variableType == 3) { + return genStorePopLiteralVariableneedsStoreCheckneedsImmutabilityCheck(1, variableIndex, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } + return EncounteredUnknownBytecode; +} + + /* SimpleStackBasedCogit>>#extendedStoreBytecode */ +static sqInt +extendedStoreBytecode(void) +{ + AbstractInstruction *abstractInstruction; + usqInt variableIndex; + usqInt variableType; + + variableType = (((usqInt)(byte1)) >> 6) & 3; + variableIndex = byte1 & 0x3F; + if (!variableType) { + return genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(0, variableIndex, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } + if (variableType == 1) { + genStorePopTemporaryVariable(0, variableIndex); + + /* needs a fake map entry if Immutability is ON... */ +# if IMMUTABILITY + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); +# endif // IMMUTABILITY + + return 0; + } + if (variableType == 3) { + return genStorePopLiteralVariableneedsStoreCheckneedsImmutabilityCheck(0, variableIndex, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } + return EncounteredUnknownBytecode; +} + + /* SimpleStackBasedCogit>>#frameOffsetOfTemporary: */ +static NoDbgRegParms sqInt +frameOffsetOfTemporary(sqInt index) +{ + return /* frameOffsetOfTemporary:numArgs: */ + (index < methodOrBlockNumArgs + ? FoxCallerSavedIP + ((methodOrBlockNumArgs - index) * BytesPerWord) + : (FoxMFReceiver - BytesPerWord) + ((methodOrBlockNumArgs - index) * BytesPerWord)); +} + + +/* Implemented with SistaCogit only */ + + /* SimpleStackBasedCogit>>#genCallMappedInlinedPrimitive */ +static sqInt +genCallMappedInlinedPrimitive(void) +{ + return EncounteredUnknownBytecode; +} + + /* SimpleStackBasedCogit>>#genDoubleFailIfZeroArgRcvr:arg: */ +static NoDbgRegParms AbstractInstruction * +genDoubleFailIfZeroArgRcvrarg(int rcvrReg, int argReg) +{ + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, TempReg); + + /* ConvertR:Rd: */ + genoperandoperand(ConvertRRd, TempReg, DPFPReg2); + + /* CmpRd:Rd: */ + genoperandoperand(CmpRdRd, DPFPReg2, argReg); + return gJumpFPEqual(0); +} + + +/* Can use any of the first 32 literals for the selector and pass up to 7 + arguments. + */ + + /* SimpleStackBasedCogit>>#genExtendedSendBytecode */ +static sqInt +genExtendedSendBytecode(void) +{ + return genSendnumArgs(byte1 & 0x1F, ((usqInt)(byte1)) >> 5); +} + + /* SimpleStackBasedCogit>>#genExtendedSuperBytecode */ +static sqInt +genExtendedSuperBytecode(void) +{ + return genSendSupernumArgs(byte1 & 0x1F, ((usqInt)(byte1)) >> 5); +} + + +/* 244 11110100 i i i i i i i i Pop and Jump 0n False i i i i i i i i (+ + Extend B * 256, where Extend B >= 0) + */ + + /* SimpleStackBasedCogit>>#genExtJumpIfFalse */ +static sqInt +genExtJumpIfFalse(void) +{ + sqInt distance; + sqInt target; + + distance = byte1 + ((((usqInt)(extB) << 8))); + assert(distance == (v4LongForwardBranchDistance(generatorAt(byte0), bytecodePC, ((extA != 0 + ? 1 + : 0)) + ((extB != 0 + ? 1 + : 0)), methodObj))); + extB = 0; + numExtB = 0; + target = (distance + 2) + bytecodePC; + return genJumpIfto(falseObject(), target); +} + + +/* 243 11110011 i i i i i i i i Pop and Jump 0n True i i i i i i i i (+ + Extend B * 256, where Extend B >= 0) + */ + + /* SimpleStackBasedCogit>>#genExtJumpIfTrue */ +static sqInt +genExtJumpIfTrue(void) +{ + sqInt distance; + sqInt target; + + distance = byte1 + ((((usqInt)(extB) << 8))); + assert(distance == (v4LongForwardBranchDistance(generatorAt(byte0), bytecodePC, ((extA != 0 + ? 1 + : 0)) + ((extB != 0 + ? 1 + : 0)), methodObj))); + extB = 0; + numExtB = 0; + target = (distance + 2) + bytecodePC; + return genJumpIfto(trueObject(), target); +} + + +/* NewspeakV4: 221 11011101 Nop */ +/* SistaV1: 91 01011011' Nop */ + + /* SimpleStackBasedCogit>>#genExtNopBytecode */ +static sqInt +genExtNopBytecode(void) +{ + extA = (numExtB = (extB = 0)); + return 0; +} + + +/* SistaV1: 233 11101001 iiiiiiii Push Character #iiiiiiii (+ Extend A * + 256) + */ + + /* SimpleStackBasedCogit>>#genExtPushCharacterBytecode */ +static sqInt +genExtPushCharacterBytecode(void) +{ + sqInt value; + + value = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + return ssPushConstant(characterObjectOf(value)); +} + + +/* NewsqueakV4: 229 11100101 iiiiiiii Push Integer #iiiiiiii (+ Extend B * + 256, where bbbbbbbb = sddddddd, e.g. -32768 = i=0, a=0, s=1) + SistaV1: 232 11101000 iiiiiiii Push Integer #iiiiiiii (+ Extend B * 256, + where bbbbbbbb = sddddddd, e.g. -32768 = i=0, a=0, s=1) + */ + + /* SimpleStackBasedCogit>>#genExtPushIntegerBytecode */ +static sqInt +genExtPushIntegerBytecode(void) +{ + sqInt value; + + value = byte1 + ((((usqInt)(extB) << 8))); + extB = 0; + numExtB = 0; + return ssPushConstant((((usqInt)value << 3) | 1)); +} + + +/* 228 11100100 i i i i i i i i Push Literal #iiiiiiii (+ Extend A * 256) */ + + /* SimpleStackBasedCogit>>#genExtPushLiteralBytecode */ +static sqInt +genExtPushLiteralBytecode(void) +{ + sqInt index; + + index = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + return genPushLiteralIndex(index); +} + + +/* 227 11100011 i i i i i i i i Push Literal Variable #iiiiiiii (+ Extend A + * 256) + */ + + /* SimpleStackBasedCogit>>#genExtPushLiteralVariableBytecode */ +static sqInt +genExtPushLiteralVariableBytecode(void) +{ + sqInt index; + + index = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + return genPushLiteralVariable(index); +} + + +/* SistaV1: * 82 01010010 Push thisContext, (then Extend B = 1 => push + thisProcess) + */ + + /* SimpleStackBasedCogit>>#genExtPushPseudoVariable */ +static sqInt +genExtPushPseudoVariable(void) +{ + sqInt ext; + + ext = extB; + extB = 0; + numExtB = 0; + switch (ext) { + case 0: + return genPushActiveContextBytecode(); + + default: + return EncounteredUnknownBytecode; + } + return 0; +} + + +/* 226 11100010 i i i i i i i i Push Receiver Variable #iiiiiiii (+ Extend A + * 256) + */ + + /* SimpleStackBasedCogit>>#genExtPushReceiverVariableBytecode */ +static sqInt +genExtPushReceiverVariableBytecode(void) +{ + sqInt index; + + index = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + return ((mclassCouldBeContext()) + && (isReadMediatedContextInstVarIndex(index)) + ? genPushMaybeContextReceiverVariable(index) + : genPushReceiverVariable(index)); +} + + +/* 238 11101110 i i i i i j j j Send Literal Selector #iiiii (+ Extend A * + 32) with jjj (+ Extend B * 8) Arguments + */ + + /* SimpleStackBasedCogit>>#genExtSendBytecode */ +static sqInt +genExtSendBytecode(void) +{ + sqInt litIndex; + usqInt nArgs; + + litIndex = (((usqInt)(byte1)) >> 3) + ((((usqInt)(extA) << 5))); + extA = 0; + nArgs = (byte1 & 7) + ((((usqInt)(extB) << 3))); + extB = 0; + numExtB = 0; + return genSendnumArgs(litIndex, nArgs); +} + + +/* 239 11101111 i i i i i j j j Send To Superclass Literal Selector #iiiii + (+ Extend A * 32) with jjj (+ Extend B * 8) Arguments + */ + + /* SimpleStackBasedCogit>>#genExtSendSuperBytecode */ +static sqInt +genExtSendSuperBytecode(void) +{ + int isDirected; + sqInt litIndex; + usqInt nArgs; + + if ((isDirected = extB >= 64)) { + extB = extB & 0x3F; + } + litIndex = (((usqInt)(byte1)) >> 3) + ((((usqInt)(extA) << 5))); + extA = 0; + nArgs = (byte1 & 7) + ((((usqInt)(extB) << 3))); + extB = 0; + numExtB = 0; + return (isDirected + ? genSendDirectedSupernumArgs(litIndex, nArgs) + : genSendSupernumArgs(litIndex, nArgs)); +} + + +/* 236 11101100 i i i i i i i i Pop and Store Literal Variable #iiiiiiii (+ + Extend A * 256) + */ + + /* SimpleStackBasedCogit>>#genExtStoreAndPopLiteralVariableBytecode */ +static sqInt +genExtStoreAndPopLiteralVariableBytecode(void) +{ + sqInt index; + + index = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + return genStorePopLiteralVariableneedsStoreCheckneedsImmutabilityCheck(1, index, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); +} + + +/* 235 11101011 i i i i i i i i Pop and Store Receiver Variable #iiiiiii (+ + Extend A * 256) + */ + + /* SimpleStackBasedCogit>>#genExtStoreAndPopReceiverVariableBytecode */ +static sqInt +genExtStoreAndPopReceiverVariableBytecode(void) +{ + sqInt index; + + index = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + if ((mclassCouldBeContext()) + && (isWriteMediatedContextInstVarIndex(index))) { + return genStorePopMaybeContextReceiverVariableneedsStoreCheckneedsImmutabilityCheck(1, index, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } + else { + return genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(1, index, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } +} + + +/* 233 11101001 i i i i i i i i Store Literal Variable #iiiiiiii (+ Extend A + * 256) + */ + + /* SimpleStackBasedCogit>>#genExtStoreLiteralVariableBytecode */ +static sqInt +genExtStoreLiteralVariableBytecode(void) +{ + sqInt index; + + index = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + return genStorePopLiteralVariableneedsStoreCheckneedsImmutabilityCheck(0, index, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); +} + + +/* 232 11101000 i i i i i i i i Store Receiver Variable #iiiiiii (+ Extend A + * 256) + */ + + /* SimpleStackBasedCogit>>#genExtStoreReceiverVariableBytecode */ +static sqInt +genExtStoreReceiverVariableBytecode(void) +{ + sqInt index; + + index = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + if ((mclassCouldBeContext()) + && (isWriteMediatedContextInstVarIndex(index))) { + return genStorePopMaybeContextReceiverVariableneedsStoreCheckneedsImmutabilityCheck(0, index, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } + else { + return genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(0, index, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } +} + + +/* 242 11110010 i i i i i i i i Jump i i i i i i i i (+ Extend B * 256, + where bbbbbbbb = sddddddd, e.g. -32768 = i=0, a=0, s=1) + */ + + /* SimpleStackBasedCogit>>#genExtUnconditionalJump */ +static sqInt +genExtUnconditionalJump(void) +{ + AbstractInstruction *abstractInstruction; + sqInt distance; + sqInt target; + + distance = byte1 + ((((usqInt)(extB) << 8))); + assert(distance == (v4LongBranchDistance(generatorAt(byte0), bytecodePC, ((extA != 0 + ? 1 + : 0)) + ((extB != 0 + ? 1 + : 0)), methodObj))); + extB = 0; + numExtB = 0; + target = (distance + 2) + bytecodePC; + if (distance < 0) { + return genJumpBackTo(target); + } + genJumpTo(target); + + /* The bytecode must be mapped since it can be either forward or backward, and + backwards branches must be mapped. So if forward, we need to map. */ + abstractInstruction = lastOpcode(); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + return 0; +} + + /* SimpleStackBasedCogit>>#genFastPrimFail */ +static sqInt +genFastPrimFail(void) +{ + primitiveIndex = 0; + return UnfailingPrimitive; +} + + +/* Support for compileInterpreterPrimitive. Generate inline code + so as to record the primitive trace as fast as possible. If setNewMethod + as a side-effect write the method to newMethod. */ + + /* SimpleStackBasedCogit>>#genFastPrimTraceUsing:and:setNewMethod: */ +static NoDbgRegParms void +genFastPrimTraceUsingandsetNewMethod(sqInt r1, sqInt r2, sqInt setNewMethod) +{ + sqInt offset; + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, r2); + + /* #MoveAb:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAbR, primTraceLogIndexAddress(), r2); + + /* #LoadEffectiveAddressMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(LoadEffectiveAddressMwrR, 1, r2, r1); + + /* #MoveR:Ab: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAb, r1, primTraceLogIndexAddress()); + addDependent(methodLabel, annotateAbsolutePCRef(genoperandoperand(MoveCwR, ((sqInt)methodLabel), r1))); + offset = offsetof(CogMethod, methodObject); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, r1, TempReg); + + /* #MoveCw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveCwR, ((sqInt)(primTraceLogAddress())), r1); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, TempReg, r2, r1); + if (setNewMethod) { + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, newMethodAddress()); + + /* begin maybeGenZeroLRPStopUsecs */ +# if LRPCheck + if (checkingLongRunningPrimitives()) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, longRunningPrimitiveStopUsecsAddress()); + } +# endif // LRPCheck + + } +} + + /* SimpleStackBasedCogit>>#genLoadNewMethod */ +static void +genLoadNewMethod(void) +{ + sqInt offset; + + addDependent(methodLabel, annotateAbsolutePCRef(genoperandoperand(MoveCwR, ((sqInt)methodLabel), ClassReg))); + offset = offsetof(CogMethod, methodObject); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, ClassReg, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, newMethodAddress()); +# if LRPCheck + if (checkingLongRunningPrimitives()) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, longRunningPrimitiveStopUsecsAddress()); + } +# endif // LRPCheck +} + + /* SimpleStackBasedCogit>>#genLongJumpIfFalse */ +static sqInt +genLongJumpIfFalse(void) +{ + sqInt distance; + sqInt target; + + distance = v3LongForwardBranchDistance(generatorAt(byte0), bytecodePC, 0, methodObj); + target = (distance + 2) + bytecodePC; + return genJumpIfto(falseObject(), target); +} + + /* SimpleStackBasedCogit>>#genLongJumpIfTrue */ +static sqInt +genLongJumpIfTrue(void) +{ + sqInt distance; + sqInt target; + + distance = v3LongForwardBranchDistance(generatorAt(byte0), bytecodePC, 0, methodObj); + target = (distance + 2) + bytecodePC; + return genJumpIfto(trueObject(), target); +} + + +/* 230 11100110 i i i i i i i i Push Temporary Variable #iiiiiiii */ + + /* SimpleStackBasedCogit>>#genLongPushTemporaryVariableBytecode */ +static sqInt +genLongPushTemporaryVariableBytecode(void) +{ + return genPushTemporaryVariable(byte1); +} + + +/* 237 11101101 i i i i i i i i Pop and Store Temporary Variable #iiiiiiii */ + + /* SimpleStackBasedCogit>>#genLongStoreAndPopTemporaryVariableBytecode */ +static sqInt +genLongStoreAndPopTemporaryVariableBytecode(void) +{ + return genStorePopTemporaryVariable(1, byte1); +} + + +/* 234 11101010 i i i i i i i i Store Temporary Variable #iiiiiiii */ + + /* SimpleStackBasedCogit>>#genLongStoreTemporaryVariableBytecode */ +static sqInt +genLongStoreTemporaryVariableBytecode(void) +{ + return genStorePopTemporaryVariable(0, byte1); +} + + /* SimpleStackBasedCogit>>#genLongUnconditionalBackwardJump */ +static sqInt +genLongUnconditionalBackwardJump(void) +{ + sqInt distance; + + distance = v3LongBranchDistance(generatorAt(byte0), bytecodePC, 0, methodObj); + assert(distance < 0); + return genJumpBackTo((distance + 2) + bytecodePC); +} + + /* SimpleStackBasedCogit>>#genLongUnconditionalForwardJump */ +static sqInt +genLongUnconditionalForwardJump(void) +{ + sqInt distance; + sqInt targetpc; + + distance = v3LongBranchDistance(generatorAt(byte0), bytecodePC, 0, methodObj); + assert(distance >= 0); + targetpc = (distance + 2) + bytecodePC; + return genJumpTo(targetpc); +} + + +/* Compile the code for a probe of the first-level method cache for a perform + primitive. The selector is assumed to be in Arg0Reg. Defer to + adjustArgumentsForPerform: to + adjust the arguments before the jump to the method. */ +/* N.B. Can't assume TempReg already contains the tag because a method can + of course be invoked via the unchecked entry-point, e.g. as does perform:. */ + + /* SimpleStackBasedCogit>>#genLookupForPerformNumArgs: */ +static NoDbgRegParms sqInt +genLookupForPerformNumArgs(sqInt numArgs) +{ + sqInt cacheBaseReg; + AbstractInstruction *itsAHit; + AbstractInstruction *jumpBadNumArgs; + AbstractInstruction *jumpClassMiss; + AbstractInstruction *jumpInterpret; + AbstractInstruction *jumpSelectorMiss; + sqInt offset; + sqInt quickConstant; + sqInt reg; + + genGetInlineCacheClassTagFromintoforEntry(ReceiverResultReg, SendNumArgsReg, 0); + cacheBaseReg = NoReg; + if (!(/* #isWithinMwOffsetRange: #addressIsInCodeZone: */ + ((((usqInt)(methodCacheAddress()))) >= codeBase) + && ((((usqInt)(methodCacheAddress()))) < limitAddress))) { + quickConstant = methodCacheAddress(); + reg = (cacheBaseReg = Extra0Reg); + + /* begin MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, quickConstant, reg); + } + + /* Do first of three probes. See CoInterpreter>>lookupInMethodCacheSel:classTag: */ + jumpSelectorMiss = compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(Arg0Reg, 0, cacheBaseReg); + jumpClassMiss = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Fetch the method, and check if it is cogged. */ + offset = (cacheBaseReg == NoReg + ? (methodCacheAddress()) + ((((usqInt)(MethodCacheMethod) << (shiftForWord())))) + : ((sqInt)((usqInt)(MethodCacheMethod) << (shiftForWord())))); + + /* begin MoveMw:r:R: */ + itsAHit = genoperandoperandoperand(MoveMwrR, offset, ClassReg, SendNumArgsReg); + + /* If the method is not compiled fall back on the interpreter primitive. */ + genLoadSlotsourceRegdestReg(HeaderIndex, SendNumArgsReg, ClassReg); + jumpInterpret = genJumpImmediate(ClassReg); + + /* check the argument count; if it's wrong fall back on the interpreter primitive. */ + + /* begin genLoadcmNumArgsOf:into: */ + /* MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, SendNumArgsReg); + offset = sizeof(((((CogMethod *) 0))->objectHeader)); + + /* begin MoveMb:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMbrR, offset, ClassReg, SendNumArgsReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, numArgs - 1, SendNumArgsReg); + jumpBadNumArgs = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Adjust arguments and jump to the method's unchecked entry-point. */ + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, cmNoCheckEntryOffset, ClassReg); + adjustArgumentsForPerform(numArgs); + + /* JumpR: */ + genoperand(JumpR, ClassReg); + + /* First probe missed. Do second of three probes. Shift hash right one and retry. */ + jmpTarget(jumpSelectorMiss, jmpTarget(jumpClassMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + jumpSelectorMiss = compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(Arg0Reg, 1, cacheBaseReg); + + /* JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)itsAHit)); + + /* Second probe missed. Do last probe. Shift hash right two and retry. */ + jmpTarget(jumpSelectorMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpSelectorMiss = compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(Arg0Reg, 2, cacheBaseReg); + + /* JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)itsAHit)); + + /* Last probe missed/not jitted/bad num args. Caller will generate the call to fall back on the interpreter primitive. */ + jmpTarget(jumpSelectorMiss, jmpTarget(jumpInterpret, jmpTarget(jumpBadNumArgs, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + return 0; +} + + /* SimpleStackBasedCogit>>#genMustBeBooleanTrampolineFor:called: */ +static NoDbgRegParms usqInt +genMustBeBooleanTrampolineForcalled(sqInt boolean, char *trampolineName) +{ + zeroOpcodeIndex(); + + /* If the objectRepresentation does want true & false to be mobile then we need to record these addresses. */ + assert(!(shouldAnnotateObjectReference(boolean))); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, boolean, TempReg); + return genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceSendMustBeBoolean, trampolineName, 1, TempReg, null, null, null, 0 /* emptyRegisterMask */, 1, NoReg, 1); +} + + +/* Implement 28-bit hashMultiply for SmallInteger and LargePositiveInteger + receivers. + */ + + /* SimpleStackBasedCogit>>#genPrimitiveHashMultiply */ +static sqInt +genPrimitiveHashMultiply(void) +{ + AbstractInstruction *jmpFailImm; + AbstractInstruction *jmpFailNotPositiveLargeInt; + + if (mclassIsSmallInteger()) { + genConvertSmallIntegerToIntegerInReg(ReceiverResultReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, HashMultiplyConstant, TempReg); + + /* MulR:R: */ + genMulRR(backEnd, TempReg, ReceiverResultReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, HashMultiplyMask, ReceiverResultReg); + genConvertIntegerToSmallIntegerInReg(ReceiverResultReg); + + /* RetN: */ + genoperand(RetN, 0); + return CompletePrimitive; + } + + /* This check is necessary. LargeNegativeInteger is a subclass of LargePositiveInteger in Squeak, Pharo, Cuis, et al. */ + jmpFailImm = genJumpImmediate(ReceiverResultReg); + genGetCompactClassIndexNonImmOfinto(ReceiverResultReg, ClassReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ClassLargePositiveIntegerCompactIndex, ClassReg); + jmpFailNotPositiveLargeInt = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genLoadSlotsourceRegdestReg(0, ReceiverResultReg, ReceiverResultReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, HashMultiplyConstant, TempReg); + + /* MulR:R: */ + genMulRR(backEnd, TempReg, ReceiverResultReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, HashMultiplyMask, ReceiverResultReg); + genConvertIntegerToSmallIntegerInReg(ReceiverResultReg); + + /* RetN: */ + genoperand(RetN, 0); + jmpTarget(jmpFailImm, jmpTarget(jmpFailNotPositiveLargeInt, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + +/* Generate the substitute return code for an external or FFI primitive call. + On success simply return, extracting numArgs from newMethod. + On primitive failure call ceActivateFailingPrimitiveMethod: newMethod. */ + + /* SimpleStackBasedCogit>>#genPrimReturnEnterCogCodeEnilopmart: */ +static NoDbgRegParms void +genPrimReturnEnterCogCodeEnilopmart(sqInt profiling) +{ + AbstractInstruction *abstractInstruction; + sqInt callTargetSqInt; + AbstractInstruction *continuePostSample; + AbstractInstruction *jmpFail; + AbstractInstruction *jmpSample; + int newMethodReg; + sqInt reg; + + continuePostSample = ((AbstractInstruction *) 0); + jmpSample = ((AbstractInstruction *) 0); + zeroOpcodeIndex(); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, varBaseAddress, VarBaseReg); + if (profiling) { + /* begin genCheckForProfileTimerTick: */ + reg = RAX; + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, nextProfileTickAddress(), Arg1Reg); + gMovePerfCnt64RL(reg, (0)); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, reg); + jmpSample = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + continuePostSample = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + } + + /* Test primitive failure */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, primFailCodeAddress(), TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, TempReg); + jmpFail = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Switch back to the Smalltalk stack. Stack better be in either of these two states: + success: stackPointer -> result (was receiver) + arg1 + ... + argN + return pc + failure: receiver + arg1 + ... + stackPointer -> argN + return pc + We push the instructionPointer to reestablish the return pc in the success case, + but leave it to ceActivateFailingPrimitiveMethod: to do so in the failure case. */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, instructionPointerAddress(), ClassReg); + genLoadStackPointers(backEnd); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, SPReg, ReceiverResultReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ClassReg, 0, SPReg); + + /* RetN: */ + genoperand(RetN, 0); + jmpTarget(jmpFail, genoperandoperand(MoveAwR, newMethodAddress(), SendNumArgsReg)); + + /* Reload sp with CStackPointer; easier than popping args of checkProfileTick. */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, cStackPointerAddress(), SPReg); + compileCallFornumArgsargargargargresultRegregsToSave(ceActivateFailingPrimitiveMethod, 1, SendNumArgsReg, null, null, null, NoReg, 0 /* emptyRegisterMask */); + + /* On Spur ceActivateFailingPrimitiveMethod: may retry the primitive and return if successful. + So continue by returning to the caller. + Switch back to the Smalltalk stack. Stack should be in this state: + success: stackPointer -> result (was receiver) + arg1 + ... + argN + return pc + We can push the instructionPointer or load it into the LinkRegister to reestablish the return pc */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, instructionPointerAddress(), ClassReg); + genLoadStackPointers(backEnd); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord, SPReg, ReceiverResultReg); + + /* PushR: */ + genoperand(PushR, ClassReg); + + /* RetN: */ + genoperand(RetN, BytesPerWord); + if (profiling) { + jmpTarget(jmpSample, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* Test the register(s) containing nextProfileTick's value and jump to continue if not + greater than zero. See genCheckForProfileTimerTick: for the register assignments. */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, Arg1Reg); + + /* JumpLessOrEqual: */ + genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)continuePostSample)); + newMethodReg = CArg0Reg; + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, newMethodAddress(), newMethodReg); + + /* begin genMarshallNArgs:arg:arg:arg:arg: */ + + /* WIN64 ABI always reserves shadow space on the stack for callee to save up to 4 register parameters */ + + /* begin CallAnyFixedRT: */ + if (is32BitSignedImmediate(backEnd, maximumDistanceFromCodeZone(((usqInt)ceTakeProfileSample)))) { + callTargetSqInt = ((usqIntptr_t)ceTakeProfileSample); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, callTargetSqInt); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* #CallFullRT: #CallFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(CallFull, ((usqIntptr_t)ceTakeProfileSample)); + } + + /* begin genRemoveNArgsFromStack: */ + + /* Jump: */ + genoperand(Jump, ((sqInt)continuePostSample)); + } +} + + /* SimpleStackBasedCogit>>#genPushConstantFalseBytecode */ +static sqInt +genPushConstantFalseBytecode(void) +{ + return ssPushConstant(falseObject()); +} + + /* SimpleStackBasedCogit>>#genPushConstantNilBytecode */ +static sqInt +genPushConstantNilBytecode(void) +{ + return ssPushConstant(nilObject()); +} + + +/* 79 01001111 Push 1 */ + + /* SimpleStackBasedCogit>>#genPushConstantOneBytecode */ +static sqInt +genPushConstantOneBytecode(void) +{ + return ssPushConstant((((usqInt)1 << 3) | 1)); +} + + /* SimpleStackBasedCogit>>#genPushConstantTrueBytecode */ +static sqInt +genPushConstantTrueBytecode(void) +{ + return ssPushConstant(trueObject()); +} + + +/* 78 01001110 Push 0 */ + + /* SimpleStackBasedCogit>>#genPushConstantZeroBytecode */ +static sqInt +genPushConstantZeroBytecode(void) +{ + return ssPushConstant((((usqInt)0 << 3) | 1)); +} + + /* SimpleStackBasedCogit>>#genPushLiteralConstantBytecode */ +static sqInt +genPushLiteralConstantBytecode(void) +{ + return genPushLiteralIndex(byte0 & 0x1F); +} + + +/* 16-31 0001 i i i i Push Literal Variable #iiii */ + + /* SimpleStackBasedCogit>>#genPushLiteralVariable16CasesBytecode */ +static sqInt +genPushLiteralVariable16CasesBytecode(void) +{ + return genPushLiteralVariable(byte0 & 15); +} + + /* SimpleStackBasedCogit>>#genPushLiteralVariableBytecode */ +static sqInt +genPushLiteralVariableBytecode(void) +{ + return genPushLiteralVariable(byte0 & 0x1F); +} + + /* SimpleStackBasedCogit>>#genPushQuickIntegerConstantBytecode */ +static sqInt +genPushQuickIntegerConstantBytecode(void) +{ + return ssPushConstant((((usqInt)(byte0 - 117) << 3) | 1)); +} + + /* SimpleStackBasedCogit>>#genPushReceiverVariableBytecode */ +static sqInt +genPushReceiverVariableBytecode(void) +{ + return genPushReceiverVariable(byte0 & 15); +} + + /* SimpleStackBasedCogit>>#genPushTemporaryVariableBytecode */ +static sqInt +genPushTemporaryVariableBytecode(void) +{ + return genPushTemporaryVariable(byte0 & 15); +} + + +/* because selected by CoInterpreter>>quickPrimitiveGeneratorFor: */ + + /* SimpleStackBasedCogit>>#genQuickReturnConst */ +sqInt +genQuickReturnConst(void) +{ + sqInt constant; + + constant = quickPrimitiveConstantFor(primitiveIndex); + + /* genMoveConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(constant)) + && ((oopisGreaterThan(constant, classTableRootObj())) + || (oopisLessThan(constant, nilObject())))) { + annotateobjRef(genoperandoperand(MoveCwR, constant, ReceiverResultReg), constant); + } + else { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, constant, ReceiverResultReg); + } + genUpArrowReturn(); + return UnfailingPrimitive; +} + + +/* because selected by CoInterpreter>>quickPrimitiveGeneratorFor: */ + + /* SimpleStackBasedCogit>>#genQuickReturnInstVar */ +sqInt +genQuickReturnInstVar(void) +{ + sqInt index; + + index = quickPrimitiveInstVarIndexFor(primitiveIndex); + genLoadSlotsourceRegdestReg(index, ReceiverResultReg, ReceiverResultReg); + genUpArrowReturn(); + return UnfailingPrimitive; +} + + +/* because selected by CoInterpreter>>quickPrimitiveGeneratorFor: */ + + /* SimpleStackBasedCogit>>#genQuickReturnSelf */ +sqInt +genQuickReturnSelf(void) +{ + genUpArrowReturn(); + return UnfailingPrimitive; +} + + /* SimpleStackBasedCogit>>#genReturnFalse */ +static sqInt +genReturnFalse(void) +{ + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + return genUpArrowReturn(); +} + + /* SimpleStackBasedCogit>>#genReturnNil */ +static sqInt +genReturnNil(void) +{ + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, nilObject(), ReceiverResultReg); + return genUpArrowReturn(); +} + + /* SimpleStackBasedCogit>>#genReturnNilFromBlock */ +static sqInt +genReturnNilFromBlock(void) +{ + assert(inBlock > 0); + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, nilObject(), ReceiverResultReg); + return genBlockReturn(); +} + + /* SimpleStackBasedCogit>>#genReturnTrue */ +static sqInt +genReturnTrue(void) +{ + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, trueObject(), ReceiverResultReg); + return genUpArrowReturn(); +} + + +/* Can use any of the first 64 literals for the selector and pass up to 3 + arguments. + */ + + /* SimpleStackBasedCogit>>#genSecondExtendedSendBytecode */ +static sqInt +genSecondExtendedSendBytecode(void) +{ + return genSendnumArgs(byte1 & 0x3F, ((usqInt)(byte1)) >> 6); +} + + /* SimpleStackBasedCogit>>#genSendLiteralSelector0ArgsBytecode */ +static sqInt +genSendLiteralSelector0ArgsBytecode(void) +{ + return genSendnumArgs(byte0 & 15, 0); +} + + /* SimpleStackBasedCogit>>#genSendLiteralSelector1ArgBytecode */ +static sqInt +genSendLiteralSelector1ArgBytecode(void) +{ + return genSendnumArgs(byte0 & 15, 1); +} + + /* SimpleStackBasedCogit>>#genSendLiteralSelector2ArgsBytecode */ +static sqInt +genSendLiteralSelector2ArgsBytecode(void) +{ + return genSendnumArgs(byte0 & 15, 2); +} + + /* SimpleStackBasedCogit>>#genShortJumpIfFalse */ +static sqInt +genShortJumpIfFalse(void) +{ + sqInt distance; + sqInt target; + + distance = v3ShortForwardBranchDistance(generatorAt(byte0), bytecodePC, 0, methodObj); + target = (distance + 1) + bytecodePC; + return genJumpIfto(falseObject(), target); +} + + /* SimpleStackBasedCogit>>#genShortJumpIfTrue */ +static sqInt +genShortJumpIfTrue(void) +{ + sqInt distance; + sqInt target; + + distance = v3ShortForwardBranchDistance(generatorAt(byte0), bytecodePC, 0, methodObj); + target = (distance + 1) + bytecodePC; + return genJumpIfto(trueObject(), target); +} + + /* SimpleStackBasedCogit>>#genShortUnconditionalJump */ +static sqInt +genShortUnconditionalJump(void) +{ + sqInt distance; + sqInt target; + + distance = v3ShortForwardBranchDistance(generatorAt(byte0), bytecodePC, 0, methodObj); + target = (distance + 1) + bytecodePC; + return genJumpTo(target); +} + + /* SimpleStackBasedCogit>>#genSpecialSelectorEqualsEquals */ +static sqInt +genSpecialSelectorEqualsEquals(void) +{ + return genInlinedIdenticalOrNotIf(0); +} + + /* SimpleStackBasedCogit>>#genSpecialSelectorNotEqualsEquals */ +static sqInt +genSpecialSelectorNotEqualsEquals(void) +{ + return genInlinedIdenticalOrNotIf(1); +} + + /* SimpleStackBasedCogit>>#genSpecialSelectorSend */ +static sqInt +genSpecialSelectorSend(void) +{ + sqInt index; + sqInt numArgs; + + index = byte0 - (/* firstSpecialSelectorBytecodeOffset */ + (bytecodeSetOffset == 0x100 + ? AltFirstSpecialSelector + 0x100 + : FirstSpecialSelector)); + numArgs = specialSelectorNumArgs(index); + return genSendnumArgs((-index) - 1, numArgs); +} + + /* SimpleStackBasedCogit>>#genStoreAndPopReceiverVariableBytecode */ +static sqInt +genStoreAndPopReceiverVariableBytecode(void) +{ + return genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(1, byte0 & 7, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); +} + + /* SimpleStackBasedCogit>>#genStoreAndPopRemoteTempLongBytecode */ +static sqInt +genStoreAndPopRemoteTempLongBytecode(void) +{ + return genStorePopRemoteTempAtneedsStoreCheck(1, byte1, byte2, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject())))))); +} + + /* SimpleStackBasedCogit>>#genStoreAndPopTemporaryVariableBytecode */ +static sqInt +genStoreAndPopTemporaryVariableBytecode(void) +{ + return genStorePopTemporaryVariable(1, byte0 & 7); +} + + /* SimpleStackBasedCogit>>#genStoreRemoteTempLongBytecode */ +static sqInt +genStoreRemoteTempLongBytecode(void) +{ + return genStorePopRemoteTempAtneedsStoreCheck(0, byte1, byte2, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject())))))); +} + + +/* Test if nextProfileTick is non-zero and if so call ceTakeProfileSample on + the C stack and jump to continueLabel. If nextProfileTick is not greater + than zero + jump to continueLabel without calling. */ +/* Test the register(s) containing nextProfileTick's value and jump to + continue if not + greater than zero. See genCheckForProfileTimerTick: for the register + assignments. + */ + + /* SimpleStackBasedCogit>>#genTakeProfileSample: */ +static NoDbgRegParms void +genTakeProfileSample(AbstractInstruction *continueLabel) +{ + AbstractInstruction *abstractInstruction; + sqInt callTargetSqInt; + sqInt delta; + sqInt numRegsPushed; + usqInt regMaskCopy; + sqInt wordsPushedModAlignment; + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, Arg1Reg); + + /* JumpLessOrEqual: */ + genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)continueLabel)); + + /* in 32-bits if the top 32-bits is negative the value is negative */ + + /* begin genLoadCStackPointersForPrimCall */ + if (cFramePointerInUse) { + genLoadCStackPointers(backEnd); + } + else { + genLoadCStackPointer(backEnd); + } + + /* begin genAlignCStackGivenRegistersToBeSaved:numArgs:wordAlignment: */ + regMaskCopy = ((usqInt)0); + numRegsPushed = 0; + while (regMaskCopy != 0) { + numRegsPushed += regMaskCopy & 1; + regMaskCopy = ((regMaskCopy) >> 1); + } + if ((numRegsPushed == 0)) { + goto l1; + } + wordsPushedModAlignment = (numRegsPushed + ((((1 - (numIntRegArgs(backEnd))) < 0) ? 0 : (1 - (numIntRegArgs(backEnd)))))) % (cStackAlignment / BytesPerWord); + if (wordsPushedModAlignment) { + delta = (cStackAlignment / BytesPerWord) - wordsPushedModAlignment; + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, delta * BytesPerWord, SPReg); + } + /* end genAlignCStackGivenRegistersToBeSaved:numArgs:wordAlignment: */ +l1: + + /* begin genMarshallNArgs:arg:arg:arg:arg: */ + + /* WIN64 ABI always reserves shadow space on the stack for callee to save up to 4 register parameters */ + + /* begin CallAnyFixedRT: */ + if (is32BitSignedImmediate(backEnd, maximumDistanceFromCodeZone(((usqInt)ceTakeProfileSample)))) { + callTargetSqInt = ((usqIntptr_t)ceTakeProfileSample); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, callTargetSqInt); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* #CallFullRT: #CallFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(CallFull, ((usqIntptr_t)ceTakeProfileSample)); + } + + /* no need to remove stacked args since the stack pointers are immediately reloaded with the Smalltalk stack pointers. */ + genLoadStackPointersForPrimCall(backEnd, ClassReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)continueLabel)); +} + + +/* SistaV1: * 217 Trap */ + + /* SimpleStackBasedCogit>>#genUnconditionalTrapBytecode */ +static sqInt +genUnconditionalTrapBytecode(void) +{ + return EncounteredUnknownBytecode; +} + + +/* Answer the Cogit's ISA as a string */ + + /* SimpleStackBasedCogit>>#isaName */ +#if VMInvestigations +char * +isaName(void) +{ + return ISA; +} +#endif /* VMInvestigations */ + + +/* Collect the branch and send data for cogMethod, storing it into arrayObj. */ + + /* SimpleStackBasedCogit>>#mapPCDataFor:into: */ +sqInt +mapPCDataForinto(CogMethod *cogMethod, sqInt arrayObj) +{ + sqInt aMethodObj; + sqInt annotation; + sqInt bcpc; + sqInt bsOffset; + sqInt byte; + CogBlockMethod *cogMethod1; + BytecodeDescriptor *descriptor; + sqInt distance; + sqInt endbcpc; + usqInt errCode; + CogMethod *homeMethod; + sqInt isBackwardBranch; + sqInt isInBlock; + sqInt latestContinuation; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt nExts; + sqInt nextBcpc; + sqInt result; + sqInt startbcpc; + sqInt targetPC; + + introspectionDataIndex = 0; + introspectionData = arrayObj; + if (!((cogMethod->stackCheckOffset))) { + assert(introspectionDataIndex == 0); + if ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) { + storePointerUncheckedofObjectwithValue(0, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(1, introspectionData, (((usqInt)cbNoSwitchEntryOffset << 3) | 1)); + storePointerUncheckedofObjectwithValue(2, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(3, introspectionData, (((usqInt)cbEntryOffset << 3) | 1)); + } + else { + storePointerUncheckedofObjectwithValue(0, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(1, introspectionData, (((usqInt)cmEntryOffset << 3) | 1)); + storePointerUncheckedofObjectwithValue(2, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(3, introspectionData, (((usqInt)cmNoCheckEntryOffset << 3) | 1)); + } + return 4; + } + cogMethod1 = ((CogBlockMethod *) cogMethod); + startbcpc = startPCOfMethod((cogMethod->methodObject)); + + /* begin mapFor:bcpc:performUntil:arg: */ + descriptor = ((BytecodeDescriptor *) 0); + latestContinuation = 0; + mapByte = 0; + nextBcpc = 0; + assert(((cogMethod1->stackCheckOffset)) > 0); + mcpc = (((usqInt)cogMethod1)) + ((cogMethod1->stackCheckOffset)); + + /* The stack check maps to the start of the first bytecode, + the first bytecode being effectively after frame build. */ + result = pcDataForAnnotationMcpcBcpcMethod(null, 0 + ((((usqInt)(HasBytecodePC) << 1))), ((char *) mcpc), startbcpc, ((void *)cogMethod)); + if (result) { + errCode = result; + goto l1; + } + bcpc = startbcpc; + + /* In both CMMethod and CMBlock cases find the start of the map and + skip forward to the bytecode pc map entry for the stack check. */ + if (((cogMethod1->cmType)) >= CMMethod) { + isInBlock = (cogMethod1->cpicHasMNUCaseOrCMIsFullBlock); + homeMethod = ((CogMethod *) cogMethod1); + assert(startbcpc == (startPCOfMethodHeader((homeMethod->methodHeader)))); + map = ((((usqInt)homeMethod)) + ((homeMethod->blockSize))) - 1; + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert((annotation == IsAbsPCReference) + || ((annotation == IsObjectReference) + || ((annotation == IsRelativeCall) + || (annotation == IsDisplacementX2N)))); + latestContinuation = startbcpc; + aMethodObj = (homeMethod->methodObject); + endbcpc = (numBytesOf(aMethodObj)) - 1; + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + + /* If the method has a primitive, skip it and the error code store, if any; + Logically. these come before the stack check and so must be ignored. */ + bcpc += deltaToSkipPrimAndErrorStoreInheader(aMethodObj, (homeMethod->methodHeader)); + } + else { + isInBlock = 1; + assert(bcpc == ((cogMethod1->startpc))); + homeMethod = cmHomeMethod(cogMethod1); + map = findMapLocationForMcpcinMethod((((usqInt)cogMethod1)) + (sizeof(CogBlockMethod)), homeMethod); + assert(map != 0); + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert(((((usqInt)(annotation)) >> AnnotationShift) == HasBytecodePC) + || ((((usqInt)(annotation)) >> AnnotationShift) == IsDisplacementX2N)); + + /* fiducial */ + while (((annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift)) != HasBytecodePC) { + map -= 1; + } + + /* skip fiducial; i.e. the map entry for the pc immediately following the method header. */ + map -= 1; + aMethodObj = (homeMethod->methodObject); + bcpc = startbcpc - (/* blockCreationBytecodeSizeForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? AltBlockCreationBytecodeSize + : BlockCreationBytecodeSize)); + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + endbcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, -1, aMethodObj) + : 0)); + bcpc = startbcpc; + } + nExts = 0; + enumeratingCogMethod = homeMethod; + + /* Now skip up through the bytecode pc map entry for the stack check. */ + while ((((usqInt)((byteAt((void *)(map))))) >> AnnotationShift) != HasBytecodePC) { + map -= 1; + } + map -= 1; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + mcpc += (mapByte & DisplacementMask); + if (annotation >= HasBytecodePC) { + if ((annotation == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + while (1) { + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + if (isInBlock) { + if (bcpc >= endbcpc) { + errCode = 0; + goto l1; + } + } + else { + if (((descriptor->isReturn)) + && (bcpc >= latestContinuation)) { + errCode = 0; + goto l1; + } + if ((isBranch(descriptor)) + || ((descriptor->isBlockCreation))) { + /* begin latestContinuationPCFor:at:exts:in: */ + distance = ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj); + targetPC = (bcpc + ((descriptor->numBytes))) + (((distance < 0) ? 0 : distance)); + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + } + } + nextBcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj) + : 0)); + if (((descriptor->isMapped)) + || (isInBlock + && ((descriptor->isMappedInBlock)))) break; + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + isBackwardBranch = (isBranch(descriptor)) + && ((/* begin isBackwardBranch:at:exts:in: */ + assert(((descriptor->spanFunction))), + (((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj)) < 0)); + result = pcDataForAnnotationMcpcBcpcMethod(descriptor, (isBackwardBranch + ? ((((usqInt)(annotation) << 1))) + 1 + : ((sqInt)((usqInt)(annotation) << 1))), ((char *) mcpc), (isBackwardBranch + ? bcpc - (2 * nExts) + : bcpc), ((void *)cogMethod)); + if (result) { + errCode = result; + goto l1; + } + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + } + else { + assert(((((usqInt)(mapByte)) >> AnnotationShift) == IsDisplacementX2N) + || ((((usqInt)(mapByte)) >> AnnotationShift) == IsAnnotationExtension)); + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + + /* defensive; we exit on bcpc */ + errCode = 0; + /* end mapFor:bcpc:performUntil:arg: */ +l1: + if (errCode) { + assert(errCode == PrimErrNoMemory); + return -1; + } + if ((cogMethod->blockEntryOffset)) { + errCode = blockDispatchTargetsForperformarg(cogMethod, pcDataForBlockEntryMethod, ((sqInt)cogMethod)); + if (errCode) { + assert(errCode == PrimErrNoMemory); + return -1; + } + } + return introspectionDataIndex; +} + + /* SimpleStackBasedCogit>>#numSpecialSelectors */ +static sqInt +numSpecialSelectors(void) +{ + return (bytecodeSetOffset == 0x100 + ? AltNumSpecialSelectors + : NumSpecialSelectors); +} + + +/* Collect the branch and send data for the block method starting at + blockEntryMcpc, storing it into picData. + */ + + /* SimpleStackBasedCogit>>#pcDataForBlockEntry:Method: */ +static NoDbgRegParms usqInt +pcDataForBlockEntryMethod(sqInt blockEntryMcpc, sqInt cogMethod) +{ + storePointerUncheckedofObjectwithValue(introspectionDataIndex, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 1, introspectionData, (((usqInt)(blockEntryMcpc - blockNoContextSwitchOffset) << 3) | 1)); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 2, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 3, introspectionData, (((usqInt)blockEntryMcpc << 3) | 1)); + introspectionDataIndex += 4; + return 0; +} + + /* SimpleStackBasedCogit>>#pcDataFor:Annotation:Mcpc:Bcpc:Method: */ +static NoDbgRegParms sqInt +pcDataForAnnotationMcpcBcpcMethod(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *cogMethodArg) +{ + sqInt actualBcpc; + sqInt actualMcpc; + + if (!descriptor) { + assert(introspectionDataIndex == 0); + if (((((CogMethod *) cogMethodArg))->cpicHasMNUCaseOrCMIsFullBlock)) { + storePointerUncheckedofObjectwithValue(introspectionDataIndex, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 1, introspectionData, (((usqInt)cbNoSwitchEntryOffset << 3) | 1)); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 2, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 3, introspectionData, (((usqInt)cbEntryOffset << 3) | 1)); + } + else { + storePointerUncheckedofObjectwithValue(introspectionDataIndex, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 1, introspectionData, (((usqInt)cmEntryOffset << 3) | 1)); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 2, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 3, introspectionData, (((usqInt)cmNoCheckEntryOffset << 3) | 1)); + } + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 4, introspectionData, (((usqInt)(bcpc + 1) << 3) | 1)); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 5, introspectionData, (((usqInt)(((((CogMethod *) cogMethodArg))->stackCheckOffset)) << 3) | 1)); + introspectionDataIndex += 6; + return 0; + } + + /* this is the stackCheck offset */ + if ((((usqInt)(isBackwardBranchAndAnnotation)) >> 1) >= HasBytecodePC) { + actualBcpc = (((isBackwardBranchAndAnnotation & 1) != 0) + ? bcpc + 1 + : (bcpc + ((descriptor->numBytes))) + 1); + actualMcpc = (((usqInt)mcpc)) - (((usqInt)cogMethodArg)); + storePointerUncheckedofObjectwithValue(introspectionDataIndex, introspectionData, (((usqInt)actualBcpc << 3) | 1)); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 1, introspectionData, (((usqInt)actualMcpc << 3) | 1)); + introspectionDataIndex += 2; + } + return 0; +} + + +/* If there is a generator for the current primitive then answer it; + otherwise answer nil. */ + + /* SimpleStackBasedCogit>>#primitiveGeneratorOrNil */ +static PrimitiveDescriptor * +primitiveGeneratorOrNil(void) +{ + PrimitiveDescriptor *primitiveDescriptor; + + if (isQuickPrimitiveIndex(primitiveIndex)) { + /* an unused one */ + primitiveDescriptor = (&(primitiveGeneratorTable[0])); + (primitiveDescriptor->primitiveGenerator = quickPrimitiveGeneratorFor(primitiveIndex)); + return primitiveDescriptor; + } + if (((primitiveIndex >= 1) && (primitiveIndex <= MaxCompiledPrimitiveIndex))) { + /* for debugging, allow excluding specific primitives */ + return (&(primitiveGeneratorTable[primitiveIndex])); + } + return null; +} + + /* SimpleStackBasedCogit>>#register:isInMask: */ +static NoDbgRegParms int +registerisInMask(sqInt reg, sqInt mask) +{ + return ((mask & (((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)))) != 0); +} + + /* SimpleStackBasedCogit>>#register:isNotInMask: */ +static NoDbgRegParms int +registerisNotInMask(sqInt reg, sqInt mask) +{ + return (!(mask & (((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg))))); +} + + +/* Add a blockStart for an embedded block. For a binary tree walk block + dispatch blocks must be compiled in pc/depth-first order but are scanned + in breadth-first + order, so do an insertion sort (which of course is really a bubble sort + because we + have to move everything higher to make room). */ + + /* StackToRegisterMappingCogit>>#addBlockStartAt:numArgs:numCopied:span: */ +static NoDbgRegParms BlockStart * +addBlockStartAtnumArgsnumCopiedspan(sqInt bytecodepc, sqInt numArgs, sqInt numCopied, sqInt span) +{ + BlockStart *blockStart; + sqInt i; + sqInt j; + + + /* Transcript ensureCr; nextPutAll: 'addBlockStartAt: '; print: bytecodepc; cr; flush. */ + if (blockCount > 0) { + i = blockCount - 1; + while (1) { + blockStart = (&(blockStarts[i])); + + /* check for repeat addition during recompilation due to initialNil miscount. */ + if (((blockStart->startpc)) == bytecodepc) { + return blockStart; + } + if (!((((blockStart->startpc)) > bytecodepc) + && (i > 0))) break; + i -= 1; + } + for (j = blockCount; j >= (i + 1); j += -1) { + blockStarts[j] = (blockStarts[j - 1]); + } + blockStart = (&(blockStarts[i + 1])); + } + else { + blockStart = (&(blockStarts[blockCount])); + } + blockCount += 1; + (blockStart->startpc = bytecodepc); + (blockStart->numArgs = numArgs); + (blockStart->numCopied = numCopied); + (blockStart->numInitialNils = 0); + (blockStart->stackCheckLabel = null); + (blockStart->hasInstVarRef = 0); + (blockStart->span = span); + return blockStart; +} + + +/* e.g. Receiver Receiver or Receiver Receiver (RISC) + Selector/Arg0 => Arg1 Selector/Arg0 => Arg1 + Arg1 Arg2 Arg1 Arg2 + Arg2 Arg3 Arg2 sp-> Arg3 + Arg3 sp-> retpc sp-> Arg3 + sp-> retpc */ +/* Generate code to adjust the possibly stacked arguments immediately + before jumping to a method looked up by a perform primitive. */ + + /* StackToRegisterMappingCogit>>#adjustArgumentsForPerform: */ +static NoDbgRegParms void +adjustArgumentsForPerform(sqInt numArgs) +{ + sqInt index; + + assert((numRegArgs()) <= 2); + assert(numArgs >= 1); + if (numArgs <= (numRegArgs())) { + if (numArgs == 2) { + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, Arg0Reg); + } + return; + } + + /* If the arity is one more than the max numRegArgs, the receiver and all arguments have to be removed from the stack. */ + if (((numRegArgs()) + 1) == numArgs) { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, SPReg, TempReg); + if ((numRegArgs()) == 2) { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord, SPReg, Arg1Reg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord * 2, SPReg, Arg0Reg); + } + else { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord, SPReg, Arg0Reg); + } + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, (numArgs + 1) * BytesPerWord, SPReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, 0, SPReg); + return; + } + + /* e.g. Receiver Receiver + Selector/Arg0 => Arg1 + Arg1 Arg2 + Arg2 sp-> Arg3 + sp-> Arg3 */ + for (index = (numArgs - 1); index >= 1; index += -1) { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, index * BytesPerWord, SPReg, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, (index + 1) * BytesPerWord, SPReg); + } + + /* PopR: */ + genoperand(PopR, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, 0, SPReg); +} + + +/* If the stack entry is already in a register not conflicting with regMask, + answers it, + else allocate a new register not conflicting with reg mask + */ + + /* StackToRegisterMappingCogit>>#allocateRegForStackEntryAt:notConflictingWith: */ +static NoDbgRegParms sqInt +allocateRegForStackEntryAtnotConflictingWith(sqInt index, sqInt regMask) +{ + sqInt mask; + SimStackEntry *stackEntry; + + /* begin ssValue: */ + stackEntry = simStackAt(simStackPtr - index); + mask = registerMaskOrNone(stackEntry); + if ((mask != 0) + && ((!(mask & regMask)))) { + /* When one does pushDup on a SSRegister + followed by an operation reusing the register + but mutating the value of the register, then the value that was + dup is now refering to the register with a mutated value which + is incorrect. This problem is serious as it can happen also with + multiple unsafe instructions (no dup involved) + This is not a problem if the operation reusing the register is not + mutating the value of if all the dup values are used in the same + operation. + => I think we should introduce allocateRegMutatingStackEntryAt: + notConflictingWith:upThrough:, used by operations mutating the reg + value and flushing partially the stack if somewhere between simSpill + and the ssEntries used by the operation someone uses also the same + register. */ + return registerOrNone(stackEntry); + } + return allocateRegNotConflictingWith(regMask); +} + + +/* if there's a free register, use it */ + + /* StackToRegisterMappingCogit>>#allocateRegNotConflictingWith: */ +static NoDbgRegParms sqInt +allocateRegNotConflictingWith(sqInt regMask) +{ + sqInt reg; + + reg = availableRegisterOrNoneFor(backEnd, (liveRegisters()) | regMask); + if (reg == NoReg) { + reg = freeAnyRegNotConflictingWith(regMask); + } + + /* No free register, choose one that does not conflict with regMask */ + if (reg == ReceiverResultReg) { + voidReceiverResultRegContainsSelf(); + } + + /* If we've allocated RcvrResultReg, it's not live anymore */ + return reg; +} + + /* StackToRegisterMappingCogit>>#anyReferencesToRegister:inTopNItems: */ +static NoDbgRegParms sqInt +anyReferencesToRegisterinTopNItems(sqInt reg, sqInt n) +{ + sqInt i; + sqInt regMask; + + regMask = ((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)); + for (i = simStackPtr; i >= ((simStackPtr - n) + 1); i += -1) { + if ((((registerMask(simStackAt(i))) & regMask) != 0)) { + return 1; + } + } + return 0; +} + + +/* This is a static version of ceCallCogCodePopReceiverArg0Regs + for break-pointing when debugging in C. */ +/* This exists only for break-pointing. */ + + /* StackToRegisterMappingCogit>>#callCogCodePopReceiverArg0Regs */ +void +callCogCodePopReceiverArg0Regs(void) +{ + realCECallCogCodePopReceiverArg0Regs(); +} + + +/* This is a static version of ceCallCogCodePopReceiverArg1Arg0Regs + for break-pointing when debugging in C. */ +/* This exists only for break-pointing. */ + + /* StackToRegisterMappingCogit>>#callCogCodePopReceiverArg1Arg0Regs */ +void +callCogCodePopReceiverArg1Arg0Regs(void) +{ + realCECallCogCodePopReceiverArg1Arg0Regs(); +} + + /* StackToRegisterMappingCogit>>#compileBlockBodies */ +static sqInt +compileBlockBodies(void) +{ + BlockStart *blockStart; + sqInt compiledBlocksCount; + sqInt initialCounterIndex; + sqInt initialOpcodeIndex; + sqInt initialStackPtr; + sqInt (* const pushNilSizeFunction)(sqInt,sqInt) = squeakV3orSistaV1PushNilSizenumInitialNils; + sqInt result; + sqInt savedNeedsFrame; + sqInt savedNumArgs; + sqInt savedNumTemps; + + initialStackPtr = 0; + assert(blockCount > 0); + + /* scanBlock: in compileBlockEntry: sets both of these appropriately for each block. */ + savedNeedsFrame = needsFrame; + savedNumArgs = methodOrBlockNumArgs; + savedNumTemps = methodOrBlockNumTemps; + inBlock = InVanillaBlock; + compiledBlocksCount = 0; + while (compiledBlocksCount < blockCount) { + compilationPass = 1; + blockStart = blockStartAt(compiledBlocksCount); + if (((result = scanBlock(blockStart))) < 0) { + return result; + } + initialOpcodeIndex = opcodeIndex; + + /* for SistaCogit */ + initialCounterIndex = 0 /* maybeCounterIndex */; + while (1) { + compileBlockEntry(blockStart); + initialStackPtr = simStackPtr; + if (((result = compileBytecodesFromthrough(((blockStart->startpc)) + (pushNilSizeFunction(methodObj, ((blockStart->numInitialNils)))), (((blockStart->startpc)) + ((blockStart->span))) - 1))) < 0) { + return result; + } + + /* If the final simStackPtr is less than the initial simStackPtr then scanBlock: over- + estimated the number of initial nils (because it assumed one or more pushNils to + produce an operand were pushNils to initialize temps. This is very rare, so + compensate by checking, adjusting numInitialNils and recompiling the block body. + N.B. No need to reinitialize the literalsManager because it answers existing literals. */ + if (initialStackPtr == simStackPtr) break; + assert((initialStackPtr > simStackPtr) + || (deadCode)); + + /* for asserts */ + compilationPass += 1; + (blockStart->numInitialNils = (((blockStart->numInitialNils)) + simStackPtr) - initialStackPtr); + (((blockStart->fakeHeader))->dependent = null); + reinitializeFixupsFromthrough(((blockStart->startpc)) + ((blockStart->numInitialNils)), (((blockStart->startpc)) + ((blockStart->span))) - 1); + bzero(abstractOpcodes + initialOpcodeIndex, + (opcodeIndex - initialOpcodeIndex) * sizeof(AbstractInstruction)); + opcodeIndex = initialOpcodeIndex; + } + compiledBlocksCount += 1; + } + needsFrame = savedNeedsFrame; + methodOrBlockNumArgs = savedNumArgs; + methodOrBlockNumTemps = savedNumTemps; + return 0; +} + + +/* Build a frame for a block activation. See CoInterpreter + class>>initializeFrameIndices. closure (in ReceiverResultReg) + arg0 + ... + argN + caller's saved ip/this stackPage (for a base frame) + fp-> saved fp + method + context (uninitialized?) + receiver + first temp + ... + sp-> Nth temp + Avoid use of SendNumArgsReg which is the flag determining whether + context switch is allowed on stack-overflow. */ +/* Build a frame for a block activation. See CoInterpreter + class>>initializeFrameIndices. Override to push the register receiver and + register arguments, if any, and to correctly + initialize the explicitly nilled/pushed temp entries (they are /not/ of + type constant nil). */ + + /* StackToRegisterMappingCogit>>#compileBlockFrameBuild: */ +static NoDbgRegParms void +compileBlockFrameBuild(BlockStart *blockStart) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *cascade0; + sqInt i; + sqInt ign; + + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + + /* PushR: */ + genoperand(PushR, FPReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SPReg, FPReg); + + /* Think of ClassReg as ClosureReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, ClassReg); + + /* The block method field must have its MFMethodFlagIsBlockFlag bit set. + We arrange this using a labelOffset. A hack, but it works. */ + cascade0 = (blockStart->fakeHeader); + addDependent(cascade0, annotateAbsolutePCRef(genoperand(PushCw, ((sqInt)((blockStart->fakeHeader)))))); + + /* setLabelOffset: */ + ((cascade0->operands))[1] = MFMethodFlagIsBlockFlag; + + /* method */ + + /* genPushConstant: */ + genoperand(PushCq, nilObject()); + + /* Fetch home receiver from outer context. closure is on stack and initially in ReceiverResultReg. + It is safe to use Arg0Reg because reg args are pushed by the value primitives if there are any. */ + if ((blockStart->hasInstVarRef)) { + genLoadSlotsourceRegdestReg(ClosureOuterContextIndex, ClassReg, ReceiverResultReg); + genLoadSlotsourceRegdestReg(ReceiverIndex, ReceiverResultReg, Arg0Reg); + genEnsureOopInRegNotForwardedscratchRegupdatingSlotin(Arg0Reg, TempReg, ReceiverIndex, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ReceiverResultReg); + } + else { + genLoadSlotsourceRegdestReg(ClosureOuterContextIndex, ClassReg, Arg0Reg); + genLoadSlotsourceRegdestReg(ReceiverIndex, Arg0Reg, ReceiverResultReg); + } + + /* Use ReceiverResultReg for Context to agree with store check trampoline */ + + /* PushR: */ + genoperand(PushR, ReceiverResultReg); + for (i = 0; i < ((blockStart->numCopied)); i += 1) { + genLoadSlotsourceRegdestReg(i + ClosureFirstCopiedValueIndex, ClassReg, TempReg); + + /* PushR: */ + genoperand(PushR, TempReg); + } + (blockStart->stackCheckLabel = compileStackOverflowCheck(1)); + methodOrBlockNumTemps = (((blockStart->numArgs)) + ((blockStart->numCopied))) + ((blockStart->numInitialNils)); + initSimStackForFramefulMethod((blockStart->startpc)); + if (((blockStart->numInitialNils)) > 0) { + if (((blockStart->numInitialNils)) > 1) { + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, nilObject(), TempReg); + for (ign = 1; ign <= ((blockStart->numInitialNils)); ign += 1) { + /* PushR: */ + genoperand(PushR, TempReg); + } + } + else { + /* genPushConstant: */ + genoperand(PushCq, nilObject()); + } + } +} + + +/* Make sure ReceiverResultReg holds the receiver, loaded from the closure, + which is what is initially in ReceiverResultReg. We must annotate the + first instruction in vanilla blocks so that + findMethodForStartBcpc:inHomeMethod: can function. We need two annotations + because the first is a fiducial. */ +/* Make sure ReceiverResultReg holds the receiver, loaded from + the closure, which is what is initially in ReceiverResultReg */ + + /* StackToRegisterMappingCogit>>#compileBlockFramelessEntry: */ +static NoDbgRegParms void +compileBlockFramelessEntry(BlockStart *blockStart) +{ + AbstractInstruction *abstractInstruction; + + methodOrBlockNumTemps = (((blockStart->numArgs)) + ((blockStart->numCopied))) + ((blockStart->numInitialNils)); + initSimStackForFramelessBlock((blockStart->startpc)); + if ((blockStart->entryLabel)) { + abstractInstruction = (blockStart->entryLabel); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + } + if ((blockStart->hasInstVarRef)) { + genLoadSlotsourceRegdestReg(ClosureOuterContextIndex, ReceiverResultReg, ReceiverResultReg); + genLoadSlotsourceRegdestReg(ReceiverIndex, ReceiverResultReg, Arg0Reg); + genEnsureOopInRegNotForwardedscratchRegupdatingSlotin(Arg0Reg, TempReg, ReceiverIndex, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ReceiverResultReg); + } + else { + genLoadSlotsourceRegdestReg(ClosureOuterContextIndex, ReceiverResultReg, TempReg); + genLoadSlotsourceRegdestReg(ReceiverIndex, TempReg, ReceiverResultReg); + } +} + + +/* Loop over bytecodes, dispatching to the generator for each bytecode, + handling fixups in due course. + */ + + /* StackToRegisterMappingCogit>>#compileBytecodesFrom:through: */ +static NoDbgRegParms sqInt +compileBytecodesFromthrough(sqInt start, sqInt end) +{ + BytecodeDescriptor *descriptor; + BytecodeFixup *fixup; + sqInt nExts; + sqInt nextOpcodeIndex; + sqInt result; + + traceSimStack(); + bytecodePC = start; + nExts = (result = 0); + descriptor = null; + deadCode = 0; + while (1) { + maybeHaltIfDebugPC(); + mergeWithFixupIfRequired((fixup = fixupAt(bytecodePC))); + descriptor = loadBytesAndGetDescriptor(bytecodePC); + nextOpcodeIndex = opcodeIndex; + result = (deadCode + ? mapDeadDescriptorIfNeeded(descriptor) + : ((descriptor->generator))()); + if (!result) { + /* begin assertExtsAreConsumed: */ + if (!((descriptor->isExtension))) { + assert((extA == 0) + && ((extB == 0) + && (numExtB == 0))); + } + } + traceDescriptor(descriptor); + traceSimStack(); + + /* begin patchFixupTargetIfNeeded:nextOpcodeIndex: */ + if ((((((usqInt)((fixup->targetInstruction)))) >= NeedsNonMergeFixupFlag) && ((((usqInt)((fixup->targetInstruction)))) <= NeedsMergeFixupFlag))) { + /* There is a fixup for this bytecode. It must point to the first generated + instruction for this bytecode. If there isn't one we need to add a label. */ + if (opcodeIndex == nextOpcodeIndex) { + /* Label */ + genoperandoperand(Label, (labelCounter += 1), bytecodePC); + } + (fixup->targetInstruction = abstractInstructionAt(nextOpcodeIndex)); + } + bytecodePC = (bytecodePC + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bytecodePC, nExts, methodObj) + : 0)); + if (!((result == 0) + && (bytecodePC <= end))) break; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + + /* begin checkEnoughOpcodes */ + if (opcodeIndex > numAbstractOpcodes) { + error("Cog JIT internal error. Too many abstract opcodes. Num opcodes heuristic is too optimistic."); + } + return result; +} + + /* StackToRegisterMappingCogit>>#compileCogFullBlockMethod: */ +static NoDbgRegParms CogMethod * +compileCogFullBlockMethod(sqInt numCopied) +{ + usqIntptr_t allocBytes; + usqIntptr_t fixupBytes; + sqInt numBlocks; + sqInt numBytecodes; + sqInt numCleanBlocks; + usqIntptr_t opcodeBytes; + sqInt result; + + methodOrBlockNumTemps = tempCountOf(methodObj); + setHasMovableLiteral(0); + setHasYoungReferent(isYoungObject(methodObj)); + methodOrBlockNumArgs = argumentCountOf(methodObj); + inBlock = InFullBlock; + maxLitIndex = -1; + assert((primitiveIndexOf(methodObj)) == 0); + initialPC = startPCOfMethod(methodObj); + + /* initial estimate. Actual endPC is determined in scanMethod. */ + endPC = numBytesOf(methodObj); + numBytecodes = (endPC - initialPC) + 1; + primitiveIndex = 0; + + /* begin allocateOpcodes:bytecodes:ifFail: */ + numAbstractOpcodes = (numBytecodes + 10) * 10 /* estimateOfAbstractOpcodesPerBytecodes */; + opcodeBytes = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupBytes = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + allocBytes = opcodeBytes + fixupBytes; + + /* Document the fact that the MaxStackAllocSize ensures that the number of abstract + opcodes fits in a 16 bit integer (e.g. CogBytecodeFixup's instructionIndex). */ + assert((((sizeof(CogAbstractInstruction)) + (sizeof(CogBytecodeFixup))) * 0xC000) > MaxStackAllocSize); + if (allocBytes > MaxStackAllocSize) { + return ((CogMethod *) MethodTooBig); + } + abstractOpcodes = alloca(allocBytes); + bzero(abstractOpcodes, allocBytes); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeBytes)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + /* end allocateOpcodes:bytecodes:ifFail: */ +l1: + if (((numBlocks = scanMethod())) < 0) { + return ((CogMethod *) numBlocks); + } + assert(numBlocks == 0); + numCleanBlocks = scanForCleanBlocks(); + assert(numCleanBlocks == 0); + allocateBlockStarts(numBlocks + numCleanBlocks); + blockCount = 0; + if (numCleanBlocks > 0) { + addCleanBlockStarts(); + } + + /* Inaccurate error code, but it'll do. This will likely never fail. */ + blockEntryLabel = null; + (methodLabel->dependent = null); + if (((result = compileEntireFullBlockMethod(numCopied))) < 0) { + return ((CogMethod *) result); + } + return generateCogFullBlock(); +} + + /* StackToRegisterMappingCogit>>#compileCogMethod: */ +static NoDbgRegParms CogMethod * +compileCogMethod(sqInt selector) +{ + usqIntptr_t allocBytes; + int extra; + usqIntptr_t fixupBytes; + sqInt methodFoundInvalidPostScanRV; + sqInt numBlocks; + sqInt numBytecodes; + sqInt numCleanBlocks; + usqIntptr_t opcodeBytes; + sqInt result; + + methodOrBlockNumTemps = tempCountOf(methodObj); + setHasMovableLiteral(0); + setHasYoungReferent((isYoungObject(methodObj)) + || (isYoung(selector))); + methodOrBlockNumArgs = argumentCountOf(methodObj); + inBlock = 0; + maxLitIndex = -1; + extra = ((((primitiveIndex = primitiveIndexOf(methodObj))) > 0) + && (!(isQuickPrimitiveIndex(primitiveIndex))) + ? 30 + : 10); + initialPC = startPCOfMethod(methodObj); + + /* initial estimate. Actual endPC is determined in scanMethod. */ + endPC = (isQuickPrimitiveIndex(primitiveIndex) + ? initialPC - 1 + : numBytesOf(methodObj)); + numBytecodes = (endPC - initialPC) + 1; + + /* begin allocateOpcodes:bytecodes:ifFail: */ + numAbstractOpcodes = (numBytecodes + extra) * 10 /* estimateOfAbstractOpcodesPerBytecodes */; + opcodeBytes = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupBytes = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + allocBytes = opcodeBytes + fixupBytes; + + /* Document the fact that the MaxStackAllocSize ensures that the number of abstract + opcodes fits in a 16 bit integer (e.g. CogBytecodeFixup's instructionIndex). */ + assert((((sizeof(CogAbstractInstruction)) + (sizeof(CogBytecodeFixup))) * 0xC000) > MaxStackAllocSize); + if (allocBytes > MaxStackAllocSize) { + return ((CogMethod *) MethodTooBig); + } + abstractOpcodes = alloca(allocBytes); + bzero(abstractOpcodes, allocBytes); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeBytes)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + /* end allocateOpcodes:bytecodes:ifFail: */ +l1: + if (((numBlocks = scanMethod())) < 0) { + return ((CogMethod *) numBlocks); + } + numCleanBlocks = scanForCleanBlocks(); + + /* begin methodFoundInvalidPostScan */ + if (!needsFrame) { + methodFoundInvalidPostScanRV = methodOrBlockNumTemps > methodOrBlockNumArgs; + goto l2; + } + methodFoundInvalidPostScanRV = 0; + /* end methodFoundInvalidPostScan */ +l2: + if (methodFoundInvalidPostScanRV) { + return ((CogMethod *) ShouldNotJIT); + } + allocateBlockStarts(numBlocks + numCleanBlocks); + blockCount = 0; + if (numCleanBlocks > 0) { + addCleanBlockStarts(); + } + + /* Inaccurate error code, but it'll do. This will likely never fail. */ + blockEntryLabel = null; + (methodLabel->dependent = null); + if (((result = compileEntireMethod())) < 0) { + return ((CogMethod *) result); + } + return generateCogMethod(selector); +} + + +/* Compile the abstract instructions for the entire method, including blocks. */ +/* Compile the abstract instructions for the entire method, including blocks. */ + + /* StackToRegisterMappingCogit>>#compileEntireMethod */ +static sqInt +compileEntireMethod(void) +{ + sqInt result; + + regArgsHaveBeenPushed = 0; + + /* begin preenMethodLabel */ + /* setLabelOffset: */ + ((methodLabel->operands))[1] = 0; + compileAbort(); + compileEntry(); + if (((result = compilePrimitive())) < 0) { + return result; + } + compileFrameBuild(); + if (((result = compileMethodBody())) < 0) { + return result; + } + if (!blockCount) { + return 0; + } + if (((result = compileBlockBodies())) < 0) { + return result; + } + return compileBlockDispatch(); +} + + +/* Build a frame for a CogMethod activation. See CoInterpreter + class>>initializeFrameIndices. receiver (in ReceiverResultReg) + arg0 + ... + argN + caller's saved ip/this stackPage (for a base frame) + fp-> saved fp + method + context (uninitialized?) + receiver + first temp + ... + sp-> Nth temp + If there is a primitive and an error code the Nth temp is the error code. + Ensure SendNumArgsReg is set early on (incidentally to nilObj) because + it is the flag determining whether context switch is allowed on + stack-overflow. */ +/* Build a frame for a CogMethod activation. See CoInterpreter + class>>initializeFrameIndices. Override to push the register receiver and + register arguments, if any. */ + + /* StackToRegisterMappingCogit>>#compileFrameBuild */ +static void +compileFrameBuild(void) +{ + sqInt i; + sqInt toDoLimit; + + +# if IMMUTABILITY + if (useTwoPaths) { + compileTwoPathFrameBuild(); + return; + } +# endif + + if (!needsFrame) { + if (useTwoPaths) { + compileTwoPathFramelessInit(); + } + initSimStackForFramelessMethod(initialPC); + return; + } + assert(!(useTwoPaths)); + genPushRegisterArgs(); + if (!needsFrame) { + return; + } + + /* PushR: */ + genoperand(PushR, FPReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SPReg, FPReg); + addDependent(methodLabel, annotateAbsolutePCRef(genoperand(PushCw, ((sqInt)methodLabel)))); + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, nilObject(), SendNumArgsReg); + + /* PushR: */ + genoperand(PushR, SendNumArgsReg); + + /* PushR: */ + genoperand(PushR, ReceiverResultReg); + toDoLimit = temporaryCountOfMethodHeader(methodHeader); + for (i = (methodOrBlockNumArgs + 1); i <= toDoLimit; i += 1) { + /* PushR: */ + genoperand(PushR, SendNumArgsReg); + } + if (/* methodUsesPrimitiveErrorCode:header: */ + ((primitiveIndexOfMethodheader(methodObj, methodHeader)) > 0) + && ((longStoreBytecodeForHeader(methodHeader)) == (fetchByteofObject((startPCOfMethodHeader(methodHeader)) + (sizeOfCallPrimitiveBytecode(methodHeader)), methodObj)))) { + compileGetErrorCode(); + } + stackCheckLabel = compileStackOverflowCheck(canContextSwitchIfActivatingheader(methodObj, methodHeader)); + initSimStackForFramefulMethod(initialPC); +} + + +/* Make sure ReceiverResultReg holds the receiver, loaded from the closure, + which is what is initially in ReceiverResultReg. */ +/* Make sure ReceiverResultReg holds the receiver, loaded from + the closure, which is what is initially in ReceiverResultReg */ + + /* StackToRegisterMappingCogit>>#compileFullBlockFramelessEntry: */ +static NoDbgRegParms void +compileFullBlockFramelessEntry(sqInt numCopied) +{ + initSimStackForFramelessBlock(initialPC); + genLoadSlotsourceRegdestReg(FullClosureReceiverIndex, ReceiverResultReg, Arg0Reg); + genEnsureOopInRegNotForwardedscratchRegupdatingSlotin(Arg0Reg, TempReg, FullClosureReceiverIndex, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ReceiverResultReg); +} + + +/* Build a frame for a block activation. See CoInterpreter + class>>initializeFrameIndices. closure (in ReceiverResultReg) + arg0 + ... + argN + caller's saved ip/this stackPage (for a base frame) + fp-> saved fp + method + context (uninitialized?) + receiver + first temp + ... + sp-> Nth temp + Avoid use of SendNumArgsReg which is the flag determining whether + context switch is allowed on stack-overflow. */ + + /* StackToRegisterMappingCogit>>#compileFullBlockMethodFrameBuild: */ +static NoDbgRegParms void +compileFullBlockMethodFrameBuild(sqInt numCopied) +{ + sqInt i; + sqInt toDoLimit; + + if (useTwoPaths) { + /* method with only inst var store, we compile only slow path for now */ + useTwoPaths = 0; +# if IMMUTABILITY + needsFrame = 1; +# endif + + } + if (!needsFrame) { + /* it is OK for numCopied to be non-zero provided that the block does not actually use the copied values. + There are some blocks like this, e.g. that simply reference copied values to mark them as used for Slang. + See e.g. CroquetPlugin>>#primitiveGatherEntropy which contains the block [bufPtr. bufSize. false], + which the bytecode compiler optimizes to [false]. */ + compileFullBlockFramelessEntry(numCopied); + initSimStackForFramelessBlock(initialPC); + return; + } + if (!needsFrame) { + return; + } + + /* PushR: */ + genoperand(PushR, FPReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SPReg, FPReg); + + /* Think of ClassReg as ClosureReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, ClassReg); + + /* The block method field must have its MFMethodFlagIsBlockFlag bit set. + We arrange this using a labelOffset. A hack, but it works. */ + addDependent(methodLabel, annotateAbsolutePCRef(genoperand(PushCw, ((sqInt)methodLabel)))); + + /* setLabelOffset: */ + ((methodLabel->operands))[1] = MFMethodFlagIsBlockFlag; + + /* method */ + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, nilObject(), SendNumArgsReg); + + /* PushR: */ + genoperand(PushR, SendNumArgsReg); + genLoadSlotsourceRegdestReg(FullClosureReceiverIndex, ClassReg, Arg0Reg); + genEnsureOopInRegNotForwardedscratchRegupdatingSlotin(Arg0Reg, TempReg, FullClosureReceiverIndex, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ReceiverResultReg); + + /* PushR: */ + genoperand(PushR, ReceiverResultReg); + for (i = 0; i < numCopied; i += 1) { + genLoadSlotsourceRegdestReg(i + FullClosureFirstCopiedValueIndex, ClassReg, TempReg); + + /* PushR: */ + genoperand(PushR, TempReg); + } + + /* Push slots for temps */ + toDoLimit = temporaryCountOfMethodHeader(methodHeader); + for (i = ((methodOrBlockNumArgs + numCopied) + 1); i <= toDoLimit; i += 1) { + /* PushR: */ + genoperand(PushR, SendNumArgsReg); + } + stackCheckLabel = compileStackOverflowCheck(1); + initSimStackForFramefulMethod(initialPC); +} + + +/* Build a frame for a CogMethod activation. See CoInterpreter + class>>initializeFrameIndices. receiver (in ReceiverResultReg) + arg0 + ... + argN + caller's saved ip/this stackPage (for a base frame) + fp-> saved fp + method + context (uninitialized?) + receiver + first temp + ... + sp-> Nth temp + If there is a primitive and an error code the Nth temp is the error code. + Ensure SendNumArgsReg is set early on (incidentally to nilObj) because + it is the flag determining whether context switch is allowed on + stack-overflow. */ +/* We are in a method where the frame is needed *only* for instance variable + store, typically a setter method. + This case has 20% overhead with Immutability compared to setter without + immutability because of the stack + frame creation. We compile two path, one where the object is immutable, + one where it isn't. At the beginning + of the frame build, we take one path or the other depending on the + receiver mutability. + + Note: this specific case happens only where there are only instance + variabel stores. We could do something + similar for literal variable stores, but we don't as it's too uncommon. + */ + + /* StackToRegisterMappingCogit>>#compileTwoPathFrameBuild */ +#if IMMUTABILITY +static void +compileTwoPathFrameBuild(void) +{ + sqInt i; + AbstractInstruction *jumpImmutable; + AbstractInstruction *jumpOld; + sqInt toDoLimit; + + assert(useTwoPaths); + assert(blockCount == 0); + jumpImmutable = genJumpImmutablescratchReg(ReceiverResultReg, TempReg); + + /* begin genJumpInOldSpace: */ + /* CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, storeCheckBoundary(), ReceiverResultReg); + jumpOld = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* first path. The receiver is mutable */ + assert(!needsFrame); + initSimStackForFramelessMethod(initialPC); + + /* begin compileMethodBody */ + if (endPC < initialPC) { + goto l1; + } + compileBytecodesFromthrough(initialPC + (deltaToSkipPrimAndErrorStoreInheader(methodObj, methodHeader)), endPC); + /* end compileMethodBody */ +l1: + + /* second path. The receiver is mutable */ + + /* reset because it impacts inst var store compilation */ + useTwoPaths = 0; + needsFrame = 1; + jmpTarget(jumpOld, jmpTarget(jumpImmutable, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + genPushRegisterArgs(); + if (!needsFrame) { + return; + } + + /* PushR: */ + genoperand(PushR, FPReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SPReg, FPReg); + addDependent(methodLabel, annotateAbsolutePCRef(genoperand(PushCw, ((sqInt)methodLabel)))); + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, nilObject(), SendNumArgsReg); + + /* PushR: */ + genoperand(PushR, SendNumArgsReg); + + /* PushR: */ + genoperand(PushR, ReceiverResultReg); + toDoLimit = temporaryCountOfMethodHeader(methodHeader); + for (i = (methodOrBlockNumArgs + 1); i <= toDoLimit; i += 1) { + /* PushR: */ + genoperand(PushR, SendNumArgsReg); + } + if (/* methodUsesPrimitiveErrorCode:header: */ + ((primitiveIndexOfMethodheader(methodObj, methodHeader)) > 0) + && ((longStoreBytecodeForHeader(methodHeader)) == (fetchByteofObject((startPCOfMethodHeader(methodHeader)) + (sizeOfCallPrimitiveBytecode(methodHeader)), methodObj)))) { + compileGetErrorCode(); + } + stackCheckLabel = compileStackOverflowCheck(canContextSwitchIfActivatingheader(methodObj, methodHeader)); + initSimStackForFramefulMethod(initialPC); +} +#endif /* IMMUTABILITY */ + + +/* We are in a frameless method with at least two inst var stores. We compile + two paths, + one where the object is in new space, and one where it isn't. At the + beginning + of the method, we take one path or the other depending on the receiver + being in newSpace. + */ + + /* StackToRegisterMappingCogit>>#compileTwoPathFramelessInit */ +static void +compileTwoPathFramelessInit(void) +{ + AbstractInstruction *jumpOld; + + assert(!(IMMUTABILITY)); + assert(!(needsFrame)); + assert(useTwoPaths); + + /* begin genJumpInOldSpace: */ + /* CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, storeCheckBoundary(), ReceiverResultReg); + jumpOld = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* first path. The receiver is young */ + initSimStackForFramelessMethod(initialPC); + + /* begin compileMethodBody */ + if (endPC < initialPC) { + goto l1; + } + compileBytecodesFromthrough(initialPC + (deltaToSkipPrimAndErrorStoreInheader(methodObj, methodHeader)), endPC); + /* end compileMethodBody */ +l1: + + /* second path. The receiver is old */ + + /* reset because it impacts inst var store compilation */ + useTwoPaths = 0; + jmpTarget(jumpOld, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); +} + + /* StackToRegisterMappingCogit>>#cPICMissTrampolineFor: */ +static NoDbgRegParms sqInt +cPICMissTrampolineFor(sqInt numArgs) +{ + return picMissTrampolines[((numArgs < ((numRegArgs()) + 1)) ? numArgs : ((numRegArgs()) + 1))]; +} + + +/* Replaces the Blue Book double-extended send [132], in which the first byte + was wasted on 8 bits of argument count. + Here we use 3 bits for the operation sub-type (opType), and the remaining + 5 bits for argument count where needed. + The last byte give access to 256 instVars or literals. + See also secondExtendedSendBytecode + */ + + /* StackToRegisterMappingCogit>>#doubleExtendedDoAnythingBytecode */ +static sqInt +doubleExtendedDoAnythingBytecode(void) +{ + AbstractInstruction *abstractInstruction; + sqInt opType; + + opType = ((usqInt)(byte1)) >> 5; + if (!opType) { + return genSendnumArgs(byte2, byte1 & 0x1F); + } + if (opType == 1) { + return genSendSupernumArgs(byte2, byte1 & 0x1F); + } + + /* We need a map entry for this bytecode for correct parsing. + The sends will get an IsSend entry anyway. The other cases need a fake one. */ + switch (opType) { + case 2: + if (isReadMediatedContextInstVarIndex(byte2)) { + genPushMaybeContextReceiverVariable(byte2); + } + else { + genPushReceiverVariable(byte2); + + /* begin annotateInstructionForBytecode */ + abstractInstruction = (prevInstIsPCAnnotated() + ? gen(Nop) + : genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + return 0; + } + break; + case 3: + genPushLiteralIndex(byte2); + + /* begin annotateInstructionForBytecode */ + abstractInstruction = (prevInstIsPCAnnotated() + ? gen(Nop) + : genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + return 0; + + case 4: + genPushLiteralVariable(byte2); + break; + case 7: + /* genStorePop:LiteralVariable: */ + genStorePopLiteralVariableneedsStoreCheckneedsImmutabilityCheck(0, byte2, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); +# if IMMUTABILITY + /* genStorePop:LiteralVariable: annotates; don't annotate twice */ + return 0; +# endif + + + default: + if (isWriteMediatedContextInstVarIndex(byte2)) { + /* genStorePop:MaybeContextReceiverVariable: */ + genStorePopMaybeContextReceiverVariableneedsStoreCheckneedsImmutabilityCheck(opType == 6, byte2, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } + else { + /* genStorePop:ReceiverVariable: */ + genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(opType == 6, byte2, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } +# if IMMUTABILITY + /* genStorePop:...ReceiverVariable: annotate; don't annotate twice */ + return 0; +# endif + +; + } + + /* 5 & 6 + We need a map entry for this bytecode for correct parsing (if the method builds a frame). */ + assert(needsFrame); + + /* genPushMaybeContextInstVar, pushLitVar, store & storePop all generate code */ + assert(!(prevInstIsPCAnnotated())); + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + return 0; +} + + /* StackToRegisterMappingCogit>>#duplicateTopBytecode */ +static sqInt +duplicateTopBytecode(void) +{ + SimStackEntry desc; + + /* begin ssTopDescriptor */ + desc = simStack[simStackPtr]; + return ssPushDesc(desc); +} + + +/* Make sure there's a flagged fixup at the target pc in fixups. + Initially a fixup's target is just a flag. Later on it is replaced with a + proper instruction. */ + + /* StackToRegisterMappingCogit>>#ensureFixupAt: */ +static NoDbgRegParms BytecodeFixup * +ensureFixupAt(sqInt targetPC) +{ + BytecodeFixup *fixup; + + /* begin fixupAt: */ + fixup = fixupAtIndex(targetPC - initialPC); + traceFixupmerge(fixup, 1); + if ((((usqInt)((fixup->targetInstruction)))) <= NeedsNonMergeFixupFlag) { + /* begin becomeMergeFixup */ + (fixup->targetInstruction) = ((AbstractInstruction *) NeedsMergeFixupFlag); + (fixup->simStackPtr = simStackPtr); + } + else { + if ((fixup->isTargetOfBackwardBranch)) { + (fixup->simStackPtr = simStackPtr); + } + else { + assert(((fixup->simStackPtr)) == simStackPtr); + } + } + + /* convert a non-merge into a merge */ + return fixup; +} + + +/* Make sure there's a flagged fixup at the target pc in fixups. + Initially a fixup's target is just a flag. Later on it is replaced with a + proper instruction. */ + + /* StackToRegisterMappingCogit>>#ensureNonMergeFixupAt: */ +static NoDbgRegParms BytecodeFixup * +ensureNonMergeFixupAt(sqInt targetPC) +{ + BytecodeFixup *fixup; + + /* begin fixupAt: */ + fixup = fixupAtIndex(targetPC - initialPC); + traceFixupmerge(fixup, 1); + if (!((fixup->targetInstruction))) { + /* begin becomeNonMergeFixup */ + (fixup->targetInstruction) = ((AbstractInstruction *) NeedsNonMergeFixupFlag); + } + return fixup; +} + + /* StackToRegisterMappingCogit>>#ensureReceiverResultRegContainsSelf */ +static void +ensureReceiverResultRegContainsSelf(void) +{ + if (needsFrame) { + if (!((((simSelf())->liveRegister)) == ReceiverResultReg)) { + /* begin ssAllocateRequiredReg: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << ReceiverResultReg), simStackPtr, simNativeStackPtr); + storeToReg(simSelf(), ReceiverResultReg); + ((simSelf())->liveRegister = ReceiverResultReg); + } + } + else { + assert(((((simSelf())->type)) == SSRegister) + && (((((simSelf())->registerr)) == ReceiverResultReg) + && (receiverIsInReceiverResultReg()))); + } +} + + /* StackToRegisterMappingCogit>>#evaluate:at: */ +static NoDbgRegParms void +evaluateat(BytecodeDescriptor *descriptor, sqInt pc) +{ + byte0 = fetchByteofObject(pc, methodObj); + assert(descriptor == (generatorAt(bytecodeSetOffset + byte0))); + + /* begin loadSubsequentBytesForDescriptor:at: */ + if (((descriptor->numBytes)) > 1) { + byte1 = fetchByteofObject(pc + 1, methodObj); + if (((descriptor->numBytes)) > 2) { + byte2 = fetchByteofObject(pc + 2, methodObj); + if (((descriptor->numBytes)) > 3) { + byte3 = fetchByteofObject(pc + 3, methodObj); + if (((descriptor->numBytes)) > 4) { + notYetImplemented(); + } + } + } + } + ((descriptor->generator))(); +} + + +/* Attempt to follow a branch to a pc. Handle branches to unconditional jumps + and branches to push: aBoolean; conditional branch pairs. If the branch + cannot be + followed answer targetBytecodePC. It is not possible to follow jumps to + conditional branches because the stack changes depth. That following is + left to the genJumpIf:to: + clients. */ + + /* StackToRegisterMappingCogit>>#eventualTargetOf: */ +static NoDbgRegParms sqInt +eventualTargetOf(sqInt targetBytecodePC) +{ + sqInt cond; + sqInt currentTarget; + BytecodeDescriptor *descriptor; + sqInt nExts; + sqInt nextPC; + sqInt span; + + cond = 0; + descriptor = ((BytecodeDescriptor *) 0); + nextPC = (currentTarget = targetBytecodePC); + while (1) { + nExts = 0; + while (1) { + /* begin generatorForPC: */ + descriptor = generatorAt(bytecodeSetOffset + (fetchByteofObject(nextPC, methodObj))); + if ((descriptor->isReturn)) { + return currentTarget; + } + if (!((descriptor->isExtension))) break; + nExts += 1; + nextPC += (descriptor->numBytes); + } + if (/* isUnconditionalBranch */ + (isBranch(descriptor)) + && (!(((descriptor->isBranchTrue)) + || ((descriptor->isBranchFalse))))) { + span = ((descriptor->spanFunction))(descriptor, nextPC, nExts, methodObj); + if (span < 0) { + return currentTarget; + } + + /* Do *not* follow backward branches; these are interrupt points and should not be elided. */ + nextPC = (nextPC + ((descriptor->numBytes))) + span; + } + else { + if (((descriptor->generator)) == genPushConstantTrueBytecode) { + cond = 1; + } + else { + if (((descriptor->generator)) == genPushConstantFalseBytecode) { + cond = 0; + } + else { + return currentTarget; + } + } + + /* Don't step into loops across a pushTrue; jump:if: boundary, so as not to confuse stack depth fixup. */ + if (((fixupAt(nextPC))->isTargetOfBackwardBranch)) { + return currentTarget; + } + nextPC = eventualTargetOf(nextPC + ((descriptor->numBytes))); + nExts = 0; + while (1) { + /* begin generatorForPC: */ + descriptor = generatorAt(bytecodeSetOffset + (fetchByteofObject(nextPC, methodObj))); + if ((descriptor->isReturn)) { + return currentTarget; + } + if (!((descriptor->isExtension))) break; + nExts += 1; + nextPC += (descriptor->numBytes); + } + if (!(isBranch(descriptor))) { + return currentTarget; + } + if (/* isUnconditionalBranch */ + (isBranch(descriptor)) + && (!(((descriptor->isBranchTrue)) + || ((descriptor->isBranchFalse))))) { + return currentTarget; + } + nextPC = (cond == ((descriptor->isBranchTrue)) + ? (nextPC + ((descriptor->numBytes))) + (((descriptor->spanFunction))(descriptor, nextPC, nExts, methodObj)) + : nextPC + ((descriptor->numBytes))); + } + currentTarget = nextPC; + } + return 0; +} + + +/* Spill the closest register on stack not conflicting with regMask. + Assertion Failure if regMask has already all the registers */ + + /* StackToRegisterMappingCogit>>#freeAnyRegNotConflictingWith: */ +static NoDbgRegParms sqInt +freeAnyRegNotConflictingWith(sqInt regMask) +{ + SimStackEntry *desc; + sqInt index; + sqInt reg; + + assert(needsFrame); + reg = NoReg; + index = ((simSpillBase < 0) ? 0 : simSpillBase); + while ((reg == NoReg) + && (index < simStackPtr)) { + desc = simStackAt(index); + if (((desc->type)) == SSRegister) { + if (!(((regMask & (((((desc->registerr)) < 0) ? (((usqInt)(1)) >> (-((desc->registerr)))) : (1ULL << ((desc->registerr)))))) != 0))) { + reg = (desc->registerr); + } + } + index += 1; + } + assert(!((reg == NoReg))); + + /* begin ssAllocateRequiredReg: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)), simStackPtr, simNativeStackPtr); + return reg; +} + + +/* Return from block, assuming result already loaded into ReceiverResultReg. */ +/* Return from block, assuming result already loaded into ReceiverResultReg. */ + + /* StackToRegisterMappingCogit>>#genBlockReturn */ +static sqInt +genBlockReturn(void) +{ + if (needsFrame) { + /* MoveR:R: */ + genoperandoperand(MoveRR, FPReg, SPReg); + + /* PopR: */ + genoperand(PopR, FPReg); + } + + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + + /* can't fall through */ + deadCode = 1; + return 0; +} + + +/* Generate special versions of the ceCallCogCodePopReceiverAndClassRegs + enilopmart that also pop register args from the stack to undo the pushing + of register args in the abort/miss trampolines. */ + + /* StackToRegisterMappingCogit>>#genCallPICEnilopmartNumArgs: */ +static NoDbgRegParms void +(*genCallPICEnilopmartNumArgs(sqInt numArgs))(void) +{ + sqInt endAddress; + usqInt enilopmart; + sqInt size; + + zeroOpcodeIndex(); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, varBaseAddress, VarBaseReg); + genLoadStackPointers(backEnd); + + /* PopR: */ + genoperand(PopR, ClassReg); + + /* PopR: */ + genoperand(PopR, TempReg); + + /* PopR: */ + genoperand(PopR, SendNumArgsReg); + if (numArgs > 0) { + if (numArgs > 1) { + /* PopR: */ + genoperand(PopR, Arg1Reg); + assert((numRegArgs()) == 2); + } + + /* PopR: */ + genoperand(PopR, Arg0Reg); + } + + /* PopR: */ + genoperand(PopR, ReceiverResultReg); + + /* PushR: */ + genoperand(PushR, SendNumArgsReg); + + /* JumpR: */ + genoperand(JumpR, TempReg); + computeMaximumSizes(); + size = generateInstructionsAt(methodZoneBase); + endAddress = outputInstructionsAt(methodZoneBase); + assert((methodZoneBase + size) == endAddress); + enilopmart = methodZoneBase; + methodZoneBase = alignUptoRoutineBoundary(endAddress); + stopsFromto(backEnd, endAddress, methodZoneBase - 1); + recordGeneratedRunTimeaddress(trampolineNamenumRegArgs("ceCallPIC", numArgs), enilopmart); + return ((void (*)(void)) enilopmart); +} + + +/* SistaV1: ** 248 (2) 11111000 iiiiiiii mssjjjjj Call Primitive #iiiiiiii + + (jjjjj * 256) + m=1 means inlined primitive, no hard return after execution. + ss defines the unsafe operation set used to encode the operations. + (ss = 0 means sista unsafe operations, ss = 01 means lowcode operations, + other numbers are as yet used). + See SistaCogit genCallPrimitiveBytecode, EncoderForSistaV1's class comment + and StackInterpreter>>#callPrimitiveBytecode for more information. */ + + /* StackToRegisterMappingCogit>>#genCallPrimitiveBytecode */ +static sqInt +genCallPrimitiveBytecode(void) +{ + usqInt prim; + usqInt primSet; + + if (byte2 < 128) { + return (bytecodePC == initialPC + ? 0 + : EncounteredUnknownBytecode); + } + prim = ((((usqInt)((byte2 - 128)) << 8))) + byte1; + primSet = ((prim) >> 13) & 3; + prim = prim & 0x1FFF; + return EncounteredUnknownBytecode; +} + + +/* Override to push the register receiver and register arguments, if any. */ + + /* StackToRegisterMappingCogit>>#genExternalizePointersForPrimitiveCall */ +static sqInt +genExternalizePointersForPrimitiveCall(void) +{ + genPushRegisterArgs(); + + /* PopR: */ + genoperand(PopR, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, instructionPointerAddress()); + + /* Set coInterpreter stackPointer to the topmost argument, skipping the return address. */ + return genSaveStackPointers(backEnd); +} + + +/* Override to push the register receiver and register arguments, if any. */ + + /* StackToRegisterMappingCogit>>#genExternalizeStackPointerForFastPrimitiveCall */ +static AbstractInstruction * +genExternalizeStackPointerForFastPrimitiveCall(void) +{ + genPushRegisterArgs(); + gAddCqRR(BytesPerWord, SPReg, TempReg); + return genoperandoperand(MoveRAw, TempReg, stackPointerAddress()); +} + + +/* Block compilation. At this point in the method create the block. Note its + start and defer generating code for it until after the method and any + other preceding + blocks. The block's actual code will be compiled later. */ +/* 253 11111101 eei i i kkk jjjjjjjj Push Closure Num Copied iii (+ Ext A + // 16 * 8) Num Args kkk (+ Ext A \\ 16 * 8) BlockSize jjjjjjjj (+ Ext B * + 256). ee = num extensions + */ + + /* StackToRegisterMappingCogit>>#genExtPushClosureBytecode */ +static sqInt +genExtPushClosureBytecode(void) +{ + sqInt i; + usqInt numArgs; + usqInt numCopied; + sqInt reg; + sqInt startpc; + + assert(needsFrame); + startpc = bytecodePC + (((generatorAt(byte0))->numBytes)); + addBlockStartAtnumArgsnumCopiedspan(startpc, (numArgs = (byte1 & 7) + ((extA % 16) * 8)), (numCopied = ((((usqInt)(byte1)) >> 3) & 7) + ((extA / 16) * 8)), byte2 + ((((usqInt)(extB) << 8)))); + + /* 0 relative */ + extA = (numExtB = (extB = 0)); + + /* begin genInlineClosure:numArgs:numCopied: */ + assert(getActiveContextAllocatesInMachineCode()); + voidReceiverResultRegContainsSelf(); + + /* begin ssAllocateCallReg:and:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(CallerSavedRegisterMask | (((1U << ReceiverResultReg) | (1U << SendNumArgsReg)) | (1U << ClassReg)), simStackPtr, simNativeStackPtr); + genNoPopCreateClosureAtnumArgsnumCopiedcontextNumArgslargeinBlock(startpc + 1, numArgs, numCopied, methodOrBlockNumArgs, methodNeedsLargeContext(methodObj), inBlock); + + /* 1 relative */ + for (i = 1; i <= numCopied; i += 1) { + reg = ssStorePoptoPreferredReg(1, TempReg); + genStoreSourceRegslotIndexintoNewObjectInDestReg(reg, (ClosureFirstCopiedValueIndex + numCopied) - i, ReceiverResultReg); + } + ssPushRegister(ReceiverResultReg); + return 0; +} + + +/* Full Block creation compilation. The block's actual code will be compiled + separatedly. + */ +/* * 255 11111111 xxxxxxxx siyyyyyy push Closure Compiled block literal + index xxxxxxxx (+ Extend A * 256) numCopied yyyyyy receiverOnStack: s = 1 + ignoreOuterContext: i = 1 + */ + + /* StackToRegisterMappingCogit>>#genExtPushFullClosureBytecode */ +static sqInt +genExtPushFullClosureBytecode(void) +{ + sqInt compiledBlock; + sqInt i; + int ignoreContext; + sqInt numCopied; + int receiverIsOnStack; + sqInt reg; + SimStackEntry *selfOfStoreToReg; + + assert(needsFrame); + compiledBlock = getLiteral(byte1 + ((((usqInt)(extA) << 8)))); + extA = 0; + numCopied = byte2 & (0x3F); + receiverIsOnStack = ((byte2 & (128)) != 0); + ignoreContext = ((byte2 & (64)) != 0); + voidReceiverResultRegContainsSelf(); + + /* begin ssAllocateCallReg:and:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(CallerSavedRegisterMask | (((1U << ReceiverResultReg) | (1U << SendNumArgsReg)) | (1U << ClassReg)), simStackPtr, simNativeStackPtr); + genCreateFullClosurenumArgsnumCopiedignoreContextcontextNumArgslargeinBlock(compiledBlock, argumentCountOf(compiledBlock), numCopied, ignoreContext, methodOrBlockNumArgs, methodNeedsLargeContext(methodObj), inBlock); + + /* Closure in ReceiverResultReg */ + for (i = 1; i <= numCopied; i += 1) { + reg = ssStorePoptoPreferredReg(1, TempReg); + genStoreSourceRegslotIndexintoNewObjectInDestReg(reg, (FullClosureFirstCopiedValueIndex + numCopied) - i, ReceiverResultReg); + } + if (receiverIsOnStack) { + reg = ssStorePoptoPreferredReg(1, TempReg); + } + else { + selfOfStoreToReg = simSelf(); + storeToReg(selfOfStoreToReg, (reg = TempReg)); + } + genStoreSourceRegslotIndexintoNewObjectInDestReg(reg, FullClosureReceiverIndex, ReceiverResultReg); + ssPushRegister(ReceiverResultReg); + return 0; +} + + +/* Enilopmarts transfer control from C into machine code (backwards + trampolines). + */ +/* Enilopmarts transfer control from C into machine code (backwards + trampolines). Override to add version for generic and PIC-specific entry + with reg args. */ + + /* StackToRegisterMappingCogit>>#generateEnilopmarts */ +static void +generateEnilopmarts(void) +{ + +# if Debug + /* begin genEnilopmartFor:forCall:called: */ + realCEEnterCogCodePopReceiverReg = genEnilopmartForandandforCallcalled(ReceiverResultReg, NoReg, NoReg, 0, "realCEEnterCogCodePopReceiverReg"); + ceEnterCogCodePopReceiverReg = enterCogCodePopReceiver; + + /* begin genEnilopmartFor:forCall:called: */ + realCECallCogCodePopReceiverReg = genEnilopmartForandandforCallcalled(ReceiverResultReg, NoReg, NoReg, 1, "realCECallCogCodePopReceiverReg"); + ceCallCogCodePopReceiverReg = callCogCodePopReceiver; + + /* begin genEnilopmartFor:and:forCall:called: */ + realCECallCogCodePopReceiverAndClassRegs = genEnilopmartForandandforCallcalled(ReceiverResultReg, ClassReg, NoReg, 1, "realCECallCogCodePopReceiverAndClassRegs"); + ceCallCogCodePopReceiverAndClassRegs = callCogCodePopReceiverAndClassRegs; +# else // Debug + /* begin genEnilopmartFor:forCall:called: */ + ceEnterCogCodePopReceiverReg = genEnilopmartForandandforCallcalled(ReceiverResultReg, NoReg, NoReg, 0, "ceEnterCogCodePopReceiverReg"); + ceCallCogCodePopReceiverReg = genEnilopmartForandandforCallcalled(ReceiverResultReg, NoReg, NoReg, 1, "ceCallCogCodePopReceiverReg"); + ceCallCogCodePopReceiverAndClassRegs = genEnilopmartForandandforCallcalled(ReceiverResultReg, ClassReg, NoReg, 1, "ceCallCogCodePopReceiverAndClassRegs"); +# endif // Debug + + genPrimReturnEnterCogCodeEnilopmart(0); + cePrimReturnEnterCogCode = methodZoneBase; + outputInstructionsForGeneratedRuntimeAt(cePrimReturnEnterCogCode); + recordGeneratedRunTimeaddress("cePrimReturnEnterCogCode", cePrimReturnEnterCogCode); + genPrimReturnEnterCogCodeEnilopmart(1); + cePrimReturnEnterCogCodeProfiling = methodZoneBase; + outputInstructionsForGeneratedRuntimeAt(cePrimReturnEnterCogCodeProfiling); + recordGeneratedRunTimeaddress("cePrimReturnEnterCogCodeProfiling", cePrimReturnEnterCogCodeProfiling); +# if Debug + /* begin genEnilopmartFor:and:forCall:called: */ + realCECallCogCodePopReceiverArg0Regs = genEnilopmartForandandforCallcalled(ReceiverResultReg, Arg0Reg, NoReg, 1, "realCECallCogCodePopReceiverArg0Regs"); + ceCallCogCodePopReceiverArg0Regs = callCogCodePopReceiverArg0Regs; + realCECallCogCodePopReceiverArg1Arg0Regs = genEnilopmartForandandforCallcalled(ReceiverResultReg, Arg0Reg, Arg1Reg, 1, "realCECallCogCodePopReceiverArg1Arg0Regs"); + ceCallCogCodePopReceiverArg1Arg0Regs = callCogCodePopReceiverArg1Arg0Regs; +# else // Debug + /* begin genEnilopmartFor:and:forCall:called: */ + ceCallCogCodePopReceiverArg0Regs = genEnilopmartForandandforCallcalled(ReceiverResultReg, Arg0Reg, NoReg, 1, "ceCallCogCodePopReceiverArg0Regs"); + ceCallCogCodePopReceiverArg1Arg0Regs = genEnilopmartForandandforCallcalled(ReceiverResultReg, Arg0Reg, Arg1Reg, 1, "ceCallCogCodePopReceiverArg1Arg0Regs"); +# endif // Debug + + + /* These are special versions of the ceCallCogCodePopReceiverAndClassRegs enilopmart that also + pop register args from the stack to undo the pushing of register args in the abort/miss trampolines. */ + ceCall0ArgsPIC = genCallPICEnilopmartNumArgs(0); + if ((numRegArgs()) >= 1) { + ceCall1ArgsPIC = genCallPICEnilopmartNumArgs(1); + if ((numRegArgs()) >= 2) { + ceCall2ArgsPIC = genCallPICEnilopmartNumArgs(2); + assert((numRegArgs()) == 2); + } + } +} + + +/* Size pc-dependent instructions and assign eventual addresses to all + instructions. Answer the size of the code. + Compute forward branches based on virtual address (abstract code starts at + 0), assuming that any branches branched over are long. + Compute backward branches based on actual address. + Reuse the fixups array to record the pc-dependent instructions that need + to have + their code generation postponed until after the others. + + Override to add handling for null branches (branches to the immediately + following instruction) occasioned by StackToRegisterMapping's following of + jumps. */ + + /* StackToRegisterMappingCogit>>#generateInstructionsAt: */ +static NoDbgRegParms sqInt +generateInstructionsAt(sqInt eventualAbsoluteAddress) +{ + sqInt absoluteAddress; + AbstractInstruction *abstractInstruction; + BytecodeFixup *fixup; + sqInt i; + sqInt j; + sqInt pcDependentIndex; + + absoluteAddress = eventualAbsoluteAddress; + pcDependentIndex = 0; + for (i = 0; i < opcodeIndex; i += 1) { + maybeBreakGeneratingInstructionWithIndex(i); + abstractInstruction = abstractInstructionAt(i); + + /* N.B. if you want to break in resizing, break here, note the instruction index, back up to the + sender, restart, and step into computeMaximumSizes, breaking at this instruction's index. */ + maybeBreakGeneratingFromto(absoluteAddress, absoluteAddress + ((abstractInstruction->maxSize))); + if (isPCDependent(abstractInstruction)) { + sizePCDependentInstructionAt(abstractInstruction, absoluteAddress); + if ((isJump(abstractInstruction)) + && ((((i + 1) < opcodeIndex) + && ((((AbstractInstruction *) (((abstractInstruction->operands))[0]))) == (abstractInstructionAt(i + 1)))) + || (((i + 2) < opcodeIndex) + && (((((AbstractInstruction *) (((abstractInstruction->operands))[0]))) == (abstractInstructionAt(i + 2))) + && ((((abstractInstructionAt(i + 1))->opcode)) == Nop))))) { + (abstractInstruction->opcode = Nop); + concretizeAt(abstractInstruction, absoluteAddress); + } + else { + fixup = fixupAtIndex(pcDependentIndex); + pcDependentIndex += 1; + (fixup->instructionIndex = i); + } + absoluteAddress += (abstractInstruction->machineCodeSize); + } + else { + absoluteAddress = concretizeAt(abstractInstruction, absoluteAddress); + + /* N.B. if you want to break in resizing, break here, note the instruction index, back up to the + sender, restart, and step into computeMaximumSizes, breaking at this instruction's index. */ + assert(((abstractInstruction->machineCodeSize)) == ((abstractInstruction->maxSize))); + } + } + for (j = 0; j < pcDependentIndex; j += 1) { + fixup = fixupAtIndex(j); + maybeBreakGeneratingInstructionWithIndex((fixup->instructionIndex)); + abstractInstruction = abstractInstructionAt((fixup->instructionIndex)); + maybeBreakGeneratingFromto((abstractInstruction->address), (((abstractInstruction->address)) + ((abstractInstruction->maxSize))) - 1); + concretizeAt(abstractInstruction, (abstractInstruction->address)); + } + return absoluteAddress - eventualAbsoluteAddress; +} + + +/* Generate the run-time entries for the various method and PIC entry misses + and aborts. + Read the class-side method trampolines for documentation on the various + trampolines + */ + + /* StackToRegisterMappingCogit>>#generateMissAbortTrampolines */ +static void +generateMissAbortTrampolines(void) +{ + sqInt numArgs; + sqInt toDoLimit; + sqInt toDoLimit1; + sqInt toDoLimit2; + + toDoLimit = (numRegArgs()) + 1; + for (numArgs = 0; numArgs <= toDoLimit; numArgs += 1) { + methodAbortTrampolines[numArgs] = (genMethodAbortTrampolineFor(numArgs)); + } + toDoLimit1 = (numRegArgs()) + 1; + for (numArgs = 0; numArgs <= toDoLimit1; numArgs += 1) { + picAbortTrampolines[numArgs] = (genPICAbortTrampolineFor(numArgs)); + } + toDoLimit2 = (numRegArgs()) + 1; + for (numArgs = 0; numArgs <= toDoLimit2; numArgs += 1) { + picMissTrampolines[numArgs] = (genPICMissTrampolineFor(numArgs)); + } + ceReapAndResetErrorCodeTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceReapAndResetErrorCodeFor, "ceReapAndResetErrorCodeTrampoline", 1, ClassReg, null, null, null, 0 /* emptyRegisterMask */, 1, NoReg, 0); +} + + +/* Override to generate code to push the register arg(s) for <= numRegArg + arity sends. + */ + + /* StackToRegisterMappingCogit>>#generateSendTrampolines */ +static void +generateSendTrampolines(void) +{ + sqInt numArgs; + + for (numArgs = 0; numArgs < NumSendTrampolines; numArgs += 1) { + ordinarySendTrampolines[numArgs] = (genSendTrampolineFornumArgscalledargargargarg( + ceSendsupertonumArgs, + numArgs, + trampolineNamenumArgs("ceSend", numArgs), + ClassReg, + (/* begin trampolineArgConstant: */ + assert(0 >= 0), + -2 - 0), + ReceiverResultReg, + /* numArgsOrSendNumArgsReg: */ + (numArgs <= (NumSendTrampolines - 2) + ? (/* begin trampolineArgConstant: */ + assert(numArgs >= 0), + -2 - numArgs) + : SendNumArgsReg))); + } + + /* Generate these in the middle so they are within [firstSend, lastSend]. */ + for (numArgs = 0; numArgs < NumSendTrampolines; numArgs += 1) { + directedSuperSendTrampolines[numArgs] = (genSendTrampolineFornumArgscalledargargargarg(ceSendabovetonumArgs, numArgs, trampolineNamenumArgs("ceDirectedSuperSend", numArgs), ClassReg, TempReg, ReceiverResultReg, /* numArgsOrSendNumArgsReg: */ + (numArgs <= (NumSendTrampolines - 2) + ? (/* begin trampolineArgConstant: */ + assert(numArgs >= 0), + -2 - numArgs) + : SendNumArgsReg))); + directedSuperBindingSendTrampolines[numArgs] = (genSendTrampolineFornumArgscalledargargargarg(ceSendaboveClassBindingtonumArgs, numArgs, trampolineNamenumArgs("ceDirectedSuperBindingSend", numArgs), ClassReg, TempReg, ReceiverResultReg, /* numArgsOrSendNumArgsReg: */ + (numArgs <= (NumSendTrampolines - 2) + ? (/* begin trampolineArgConstant: */ + assert(numArgs >= 0), + -2 - numArgs) + : SendNumArgsReg))); + } + for (numArgs = 0; numArgs < NumSendTrampolines; numArgs += 1) { + superSendTrampolines[numArgs] = (genSendTrampolineFornumArgscalledargargargarg( + ceSendsupertonumArgs, + numArgs, + trampolineNamenumArgs("ceSuperSend", numArgs), + ClassReg, + (/* begin trampolineArgConstant: */ + assert(1 >= 0), + -2 - 1), + ReceiverResultReg, + /* numArgsOrSendNumArgsReg: */ + (numArgs <= (NumSendTrampolines - 2) + ? (/* begin trampolineArgConstant: */ + assert(numArgs >= 0), + -2 - numArgs) + : SendNumArgsReg))); + } + firstSend = ordinarySendTrampolines[0]; + lastSend = superSendTrampolines[NumSendTrampolines - 1]; +} + + +/* Generate trampolines for tracing. In the simulator we can save a lot of + time and avoid noise instructions in the lastNInstructions log by + short-cutting these + trampolines, but we need them in the real vm. */ + + /* StackToRegisterMappingCogit>>#generateTracingTrampolines */ +static void +generateTracingTrampolines(void) +{ + ceTraceLinkedSendTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceTraceLinkedSend, "ceTraceLinkedSendTrampoline", 1, ReceiverResultReg, null, null, null, CallerSavedRegisterMask, 1, NoReg, 0); + ceTraceBlockActivationTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceTraceBlockActivation, "ceTraceBlockActivationTrampoline", 0, null, null, null, null, CallerSavedRegisterMask, 1, NoReg, 0); + ceTraceStoreTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceTraceStoreOfinto, "ceTraceStoreTrampoline", 2, TempReg, ReceiverResultReg, null, null, CallerSavedRegisterMask, 1, NoReg, 0); +} + + /* StackToRegisterMappingCogit>>#genForwardersInlinedIdenticalOrNotIf: */ +static NoDbgRegParms sqInt +genForwardersInlinedIdenticalOrNotIf(sqInt orNot) +{ + sqInt argConstant; + sqInt argNeedsReg; + sqInt argReg; + sqInt argRegSqInt; + BytecodeDescriptor *branchDescriptor; + BytecodeDescriptor *branchDescriptor1; + BytecodeFixup *fixup; + sqInt i; + void *jumpTarget; + AbstractInstruction *label; + sqInt nExts; + sqInt nextPC; + sqInt nextPCSqInt; + sqInt postBranchPC; + sqInt postBranchPCSqInt; + BytecodeDescriptor *primDescriptor; + sqInt rcvrConstant; + sqInt rcvrNeedsReg; + sqInt rcvrReg; + sqInt rcvrRegSqInt; + sqInt reg; + sqInt rNextSqInt; + sqInt rTopSqInt; + BytecodeFixup *self_in_CogBytecodeFixup; + SimStackEntry *simStackEntry; + sqInt targetBytecodePC; + sqInt targetBytecodePCSqInt; + sqInt topRegistersMask; + sqInt unforwardArg; + sqInt unforwardRcvr; + + unforwardRcvr = mayBeAForwarder(ssValue(1)); + unforwardArg = mayBeAForwarder(ssTop()); + if ((!unforwardRcvr) + && (!unforwardArg)) { + return genVanillaInlinedIdenticalOrNotIf(orNot); + } + assert(unforwardArg + || (unforwardRcvr)); + + /* We use reg for non annotable constants to avoid duplicating objRef. */ + + /* begin ssValue: */ + simStackEntry = simStackAt(simStackPtr - 1); + rcvrConstant = (((simStackEntry->type)) == SSConstant) + && ((isImmediate((simStackEntry->constant))) + || (!(/* shouldAnnotateObjectReference: */ + (isNonImmediate((simStackEntry->constant))) + && ((oopisGreaterThan((simStackEntry->constant), classTableRootObj())) + || (oopisLessThan((simStackEntry->constant), nilObject())))))); + + /* begin ssTop */ + simStackEntry = simStackAt(simStackPtr); + argConstant = (((simStackEntry->type)) == SSConstant) + && ((isImmediate((simStackEntry->constant))) + || (!(/* shouldAnnotateObjectReference: */ + (isNonImmediate((simStackEntry->constant))) + && ((oopisGreaterThan((simStackEntry->constant), classTableRootObj())) + || (oopisLessThan((simStackEntry->constant), nilObject())))))); + + /* begin extractMaybeBranchDescriptorInto: */ + branchDescriptor1 = ((BytecodeDescriptor *) 0); + primDescriptor = generatorAt(byte0); + nextPCSqInt = bytecodePC + ((primDescriptor->numBytes)); + nExts = 0; + while (1) { + while (1) { + /* begin generatorForPC: */ + branchDescriptor1 = generatorAt(bytecodeSetOffset + (fetchByteofObject(nextPCSqInt, methodObj))); + if (!((branchDescriptor1->isExtension))) break; + nExts += 1; + nextPCSqInt += (branchDescriptor1->numBytes); + } + if (!(/* isUnconditionalBranch */ + (isBranch(branchDescriptor1)) + && (!(((branchDescriptor1->isBranchTrue)) + || ((branchDescriptor1->isBranchFalse)))))) break; + nextPCSqInt = eventualTargetOf((nextPCSqInt + ((branchDescriptor1->numBytes))) + (((branchDescriptor1->spanFunction))(branchDescriptor1, nextPCSqInt, nExts, methodObj))); + } + targetBytecodePCSqInt = (postBranchPCSqInt = 0); + if (((branchDescriptor1->isBranchTrue)) + || ((branchDescriptor1->isBranchFalse))) { + targetBytecodePCSqInt = eventualTargetOf((nextPCSqInt + ((branchDescriptor1->numBytes))) + (((branchDescriptor1->spanFunction))(branchDescriptor1, nextPCSqInt, nExts, methodObj))); + postBranchPCSqInt = eventualTargetOf(nextPCSqInt + ((branchDescriptor1->numBytes))); + } + else { + nextPCSqInt = bytecodePC + ((primDescriptor->numBytes)); + } + branchDescriptor = branchDescriptor1; + nextPC = nextPCSqInt; + postBranchPC = postBranchPCSqInt; + targetBytecodePC = targetBytecodePCSqInt; + argNeedsReg = !argConstant; + rcvrNeedsReg = !rcvrConstant; + + /* begin allocateEqualsEqualsRegistersArgNeedsReg:rcvrNeedsReg:into: */ + assert(argNeedsReg + || (rcvrNeedsReg)); + argRegSqInt = (rcvrRegSqInt = NoReg); + if (argNeedsReg) { + if (rcvrNeedsReg) { + /* begin allocateRegForStackTopTwoEntriesInto: */ + topRegistersMask = 0; + rTopSqInt = (rNextSqInt = NoReg); + if ((registerOrNone(ssTop())) != NoReg) { + rTopSqInt = registerOrNone(ssTop()); + } + if ((registerOrNone(ssValue(1))) != NoReg) { + reg = (rNextSqInt = registerOrNone(ssValue(1))); + + /* begin registerMaskFor: */ + topRegistersMask = ((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)); + } + if (rTopSqInt == NoReg) { + rTopSqInt = allocateRegNotConflictingWith(topRegistersMask); + } + if (rNextSqInt == NoReg) { + rNextSqInt = allocateRegNotConflictingWith(((rTopSqInt < 0) ? (((usqInt)(1)) >> (-rTopSqInt)) : (1ULL << rTopSqInt))); + } + assert(!(((rTopSqInt == NoReg) + || (rNextSqInt == NoReg)))); + argRegSqInt = rTopSqInt; + rcvrRegSqInt = rNextSqInt; + popToReg(ssTop(), argRegSqInt); + popToReg(ssValue(1), rcvrRegSqInt); + } + else { + argRegSqInt = allocateRegForStackEntryAtnotConflictingWith(0, 0); + popToReg(ssTop(), argRegSqInt); + + /* If the receiver is a spilled constant we need to pop it from the stack. */ + if (((ssValue(1))->spilled)) { + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BytesPerWord, SPReg); + } + } + } + else { + assert(rcvrNeedsReg); + assert(!((((ssTop())->spilled)))); + rcvrRegSqInt = allocateRegForStackEntryAtnotConflictingWith(1, 0); + popToReg(ssValue(1), rcvrRegSqInt); + } + assert(!((argNeedsReg + && (argRegSqInt == NoReg)))); + assert(!((rcvrNeedsReg + && (rcvrRegSqInt == NoReg)))); + rcvrReg = rcvrRegSqInt; + argReg = argRegSqInt; + + /* If not followed by a branch, resolve to true or false. */ + if (!(((branchDescriptor->isBranchTrue)) + || ((branchDescriptor->isBranchFalse)))) { + return genIdenticalNoBranchArgIsConstantrcvrIsConstantargRegrcvrRegorNotIf(argConstant, rcvrConstant, argReg, rcvrReg, orNot); + } + + /* If branching the stack must be flushed for the merge */ + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= (simStackPtr - 2)) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < (simStackPtr - 2)) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : (simStackPtr - 2))); i <= (simStackPtr - 2); i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = (simStackPtr - 2) + 1; + } + label = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin genCmpArgIsConstant:rcvrIsConstant:argReg:rcvrReg: */ + assert((argReg != NoReg) + || (rcvrReg != NoReg)); + if (argConstant) { + /* genCmpConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject())))) { + annotateobjRef(genoperandoperand(CmpCwR, ((ssTop())->constant), rcvrReg), ((ssTop())->constant)); + } + else { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ((ssTop())->constant), rcvrReg); + } + } + else { + if (rcvrConstant) { + /* genCmpConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssValue(1))->constant))) + && ((oopisGreaterThan(((ssValue(1))->constant), classTableRootObj())) + || (oopisLessThan(((ssValue(1))->constant), nilObject())))) { + annotateobjRef(genoperandoperand(CmpCwR, ((ssValue(1))->constant), argReg), ((ssValue(1))->constant)); + } + else { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ((ssValue(1))->constant), argReg); + } + } + else { + /* begin CmpR:R: */ + assert(!((argReg == SPReg))); + genoperandoperand(CmpRR, argReg, rcvrReg); + } + } + ssPop(2); + + /* Since there is a following conditional jump bytecode (unless there is deadCode), + define non-merge fixups and leave the cond bytecode to set the mergeness. */ + + /* begin fixupAt: */ + self_in_CogBytecodeFixup = fixupAtIndex(nextPC - initialPC); + if ((self_in_CogBytecodeFixup->targetInstruction)) { + assert(!(deadCode)); + } + else { + deadCode = 1; + ensureFixupAt(targetBytecodePC); + ensureFixupAt(postBranchPC); + } + if (orNot == ((branchDescriptor->isBranchTrue))) { + fixup = ensureNonMergeFixupAt(targetBytecodePC); + jumpTarget = ensureNonMergeFixupAt(postBranchPC); + + /* begin JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)jumpTarget)); + } + else { + fixup = ensureNonMergeFixupAt(postBranchPC); + jumpTarget = ensureNonMergeFixupAt(targetBytecodePC); + + /* begin JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)jumpTarget)); + } + + /* orNot is true for ~~ + a == b ifTrue: ... or a ~~ b ifFalse: ... jump on equal to target pc + a == b ifFalse: ... or a ~~ b ifTrue: ... jump on equal to post-branch pc + The forwarders checks need to jump back to the comparison (label) if a forwarder is found, else + jump forward either to the next forwarder check or to the postBranch or branch target (fixup). */ + if (unforwardArg + && (unforwardRcvr)) { + /* genEnsureOopInRegNotForwarded:scratchReg:jumpBackTo: */ + genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(argReg, TempReg, label, 0); + } + genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder((unforwardRcvr + ? rcvrReg + : argReg), TempReg, label, fixup); + + /* Not reached, execution flow has jumped to fixup */ + if (!deadCode) { + ssPushConstant(trueObject()); + } + return 0; +} + + +/* Generates the machine code for #== in the case where the instruction is + not followed by a branch + */ + + /* StackToRegisterMappingCogit>>#genIdenticalNoBranchArgIsConstant:rcvrIsConstant:argReg:rcvrReg:orNotIf: */ +static NoDbgRegParms sqInt +genIdenticalNoBranchArgIsConstantrcvrIsConstantargRegrcvrRegorNotIf(sqInt argIsConstant, sqInt rcvrIsConstant, sqInt argReg, sqInt rcvrRegOrNone, sqInt orNot) +{ + AbstractInstruction *jumpEqual; + AbstractInstruction *jumpNotEqual; + AbstractInstruction *label; + sqInt resultReg; + + label = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin genCmpArgIsConstant:rcvrIsConstant:argReg:rcvrReg: */ + assert((argReg != NoReg) + || (rcvrRegOrNone != NoReg)); + if (argIsConstant) { + /* genCmpConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject())))) { + annotateobjRef(genoperandoperand(CmpCwR, ((ssTop())->constant), rcvrRegOrNone), ((ssTop())->constant)); + } + else { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ((ssTop())->constant), rcvrRegOrNone); + } + } + else { + if (rcvrIsConstant) { + /* genCmpConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssValue(1))->constant))) + && ((oopisGreaterThan(((ssValue(1))->constant), classTableRootObj())) + || (oopisLessThan(((ssValue(1))->constant), nilObject())))) { + annotateobjRef(genoperandoperand(CmpCwR, ((ssValue(1))->constant), argReg), ((ssValue(1))->constant)); + } + else { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ((ssValue(1))->constant), argReg); + } + } + else { + /* begin CmpR:R: */ + assert(!((argReg == SPReg))); + genoperandoperand(CmpRR, argReg, rcvrRegOrNone); + } + } + ssPop(2); + resultReg = (rcvrRegOrNone == NoReg + ? argReg + : rcvrRegOrNone); + jumpEqual = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + if (!argIsConstant) { + /* genEnsureOopInRegNotForwarded:scratchReg:jumpBackTo: */ + genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(argReg, TempReg, label, 0); + } + if (!rcvrIsConstant) { + /* genEnsureOopInRegNotForwarded:scratchReg:jumpBackTo: */ + genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(rcvrRegOrNone, TempReg, label, 0); + } + if (orNot) { + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, trueObject(), resultReg); + } + else { + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, falseObject(), resultReg); + } + jumpNotEqual = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpEqual, (orNot + ? /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, falseObject(), resultReg) + : /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, trueObject(), resultReg))); + jmpTarget(jumpNotEqual, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + ssPushRegister(resultReg); + return 0; +} + + +/* Decompose code generation for #== into a common constant-folding version, + followed by a double dispatch through the objectRepresentation to a + version that doesn't deal with forwarders and a version that does. */ + + /* StackToRegisterMappingCogit>>#genInlinedIdenticalOrNotIf: */ +static NoDbgRegParms sqInt +genInlinedIdenticalOrNotIf(sqInt orNot) +{ + BytecodeDescriptor *primDescriptor; + sqInt result; + + primDescriptor = generatorAt(byte0); + if ((isUnannotatableConstant(ssTop())) + && (isUnannotatableConstant(ssValue(1)))) { + assert(!((primDescriptor->isMapped))); + if ((orNot + ? (((ssTop())->constant)) != (((ssValue(1))->constant)) + : (((ssTop())->constant)) == (((ssValue(1))->constant)))) { + result = trueObject(); + } + else { + result = falseObject(); + } + ssPop(2); + return ssPushConstant(result); + } + return genForwardersInlinedIdenticalOrNotIf(orNot); +} + + /* StackToRegisterMappingCogit>>#genJumpBackTo: */ +static NoDbgRegParms sqInt +genJumpBackTo(sqInt targetBytecodePC) +{ + AbstractInstruction *abstractInstruction; + sqInt i; + void *jumpTarget; + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + + /* can't fall through */ + deadCode = 1; + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, stackLimitAddress(), TempReg); + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, SPReg); + + /* begin fixupAt: */ + jumpTarget = fixupAtIndex(targetBytecodePC - initialPC); + genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)jumpTarget)); + abstractInstruction = genoperand(Call, ceCheckForInterruptTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + + /* begin fixupAt: */ + jumpTarget = fixupAtIndex(targetBytecodePC - initialPC); + genoperand(Jump, ((sqInt)jumpTarget)); + return 0; +} + + /* StackToRegisterMappingCogit>>#genJumpIf:to: */ +static NoDbgRegParms sqInt +genJumpIfto(sqInt boolean, sqInt targetBytecodePC) +{ + AbstractInstruction *abstractInstruction; + SimStackEntry *desc; + sqInt eventualTarget; + BytecodeFixup *fixup; + sqInt i; + void *jumpTarget; + AbstractInstruction *ok; + sqInt quickConstant; + + eventualTarget = eventualTargetOf(targetBytecodePC); + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= (simStackPtr - 1)) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < (simStackPtr - 1)) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : (simStackPtr - 1))); i < simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = (simStackPtr - 1) + 1; + } + + /* begin ssTop */ + desc = simStackAt(simStackPtr); + ssPop(1); + if (/* stackEntryIsBoolean: */ + (((desc->type)) == SSConstant) + && ((((desc->constant)) == (trueObject())) + || (((desc->constant)) == (falseObject())))) { + /* Must arrange there's a fixup at the target whether it is jumped to or + not so that the simStackPtr can be kept correct. */ + fixup = ensureFixupAt(eventualTarget); + + /* Must annotate the bytecode for correct pc mapping. */ + abstractInstruction = (((desc->constant)) == boolean + ? genoperand(Jump, ((sqInt)fixup)) + : (prevInstIsPCAnnotated() + ? gen(Nop) + : genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + extA = 0; + return 0; + } + popToReg(desc, TempReg); + + /* Cunning trick by LPD. If true and false are contiguous subtract the smaller. + Correct result is either 0 or the distance between them. If result is not 0 or + their distance send mustBeBoolean. */ + assert((objectAfter(falseObject())) == (trueObject())); + + /* genSubConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(boolean)) + && ((oopisGreaterThan(boolean, classTableRootObj())) + || (oopisLessThan(boolean, nilObject())))) { + annotateobjRef(genoperandoperand(SubCwR, boolean, TempReg), TempReg); + } + else { + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, boolean, TempReg); + } + jumpTarget = ensureFixupAt(eventualTarget); + + /* begin JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)jumpTarget)); + if (((extA & 1) != 0)) { + extA = 0; + abstractInstruction = lastOpcode(); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + return 0; + } + extA = 0; + quickConstant = (boolean == (falseObject()) + ? (trueObject()) - (falseObject()) + : (falseObject()) - (trueObject())); + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, TempReg); + ok = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + genCallMustBeBooleanFor(boolean); + jmpTarget(ok, annotateBytecode(genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return 0; +} + + /* StackToRegisterMappingCogit>>#genJumpTo: */ +static NoDbgRegParms sqInt +genJumpTo(sqInt targetBytecodePC) +{ + sqInt eventualTarget; + BytecodeFixup *fixup; + BytecodeDescriptor *generator; + sqInt i; + + eventualTarget = eventualTargetOf(targetBytecodePC); + if ((eventualTarget > bytecodePC) + && ((/* stackTopIsBoolean */ + (simStackPtr >= methodOrBlockNumArgs) + && (stackEntryIsBoolean(ssTop()))) + && (isConditionalBranch(generator = generatorForPC(eventualTarget))))) { + eventualTarget = (eventualTarget + ((generator->numBytes))) + ((((generator->isBranchTrue)) == ((((ssTop())->constant)) == (trueObject())) + ? ((generator->spanFunction))(generator, eventualTarget, 0, methodObj) + : 0)); + ssPop(1); + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + fixup = ensureFixupAt(eventualTarget); + ssPop(-1); + } + else { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + fixup = ensureFixupAt(eventualTarget); + } + + /* can't fall through */ + deadCode = 1; + + /* Jump: */ + genoperand(Jump, ((sqInt)fixup)); + return 0; +} + + /* StackToRegisterMappingCogit>>#genMarshalledSend:numArgs:sendTable: */ +static NoDbgRegParms sqInt +genMarshalledSendnumArgssendTable(sqInt selectorIndex, sqInt numArgs, sqInt *sendTable) +{ + sqInt annotation; + AbstractInstruction *instruction; + + assert(needsFrame); + + /* begin annotationForSendTable: */ + if (sendTable == ordinarySendTrampolines) { + annotation = IsSendCall; + goto l1; + } + if (sendTable == directedSuperSendTrampolines) { + annotation = IsDirectedSuperSend; + goto l1; + } + if (sendTable == directedSuperBindingSendTrampolines) { + annotation = IsDirectedSuperBindingSend; + goto l1; + } + assert(sendTable == superSendTrampolines); + annotation = IsSuperSend; + /* end annotationForSendTable: */ +l1: + + /* Deal with stale super sends; see SpurMemoryManager's class comment. */ + if (/* annotationIsForUncheckedEntryPoint: */ + (annotation == IsSuperSend) + || (((annotation >= IsDirectedSuperSend) && (annotation <= IsDirectedSuperBindingSend)))) { + /* begin genEnsureOopInRegNotForwarded:scratchReg: */ + instruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(ReceiverResultReg, TempReg, instruction, 0); + } + + /* 0 through (NumSendTrampolines - 2) numArgs sends have the arg count implciti in the trampoline. + The last send trampoline (NumSendTrampolines - 1) passes numArgs in SendNumArgsReg. */ + if (numArgs >= (NumSendTrampolines - 1)) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, numArgs, SendNumArgsReg); + } + if (((annotation >= IsDirectedSuperSend) && (annotation <= IsDirectedSuperBindingSend))) { + /* genMoveConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(tempOop)) + && ((oopisGreaterThan(tempOop, classTableRootObj())) + || (oopisLessThan(tempOop, nilObject())))) { + annotateobjRef(genoperandoperand(MoveCwR, tempOop, TempReg), tempOop); + } + else { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, tempOop, TempReg); + } + } + genLoadInlineCacheWithSelector(selectorIndex); + ((genoperand(Call, sendTable[((numArgs < (NumSendTrampolines - 1)) ? numArgs : (NumSendTrampolines - 1))]))->annotation = annotation); + + /* begin voidReceiverOptStatus */ + ((simSelf())->liveRegister = NoReg); + return ssPushRegister(ReceiverResultReg); +} + + +/* Generate the abort for a method. This abort performs either a call of + ceSICMiss: to handle a single-in-line cache miss or a call of + ceStackOverflow: to handle a + stack overflow. It distinguishes the two by testing ResultReceiverReg. If + the register is zero then this is a stack-overflow because a) the receiver + has already + been pushed and so can be set to zero before calling the abort, and b) the + receiver must always contain an object (and hence be non-zero) on SIC + miss. */ + + /* StackToRegisterMappingCogit>>#genMethodAbortTrampolineFor: */ +static NoDbgRegParms usqInt +genMethodAbortTrampolineFor(sqInt numArgs) +{ + AbstractInstruction *jumpSICMiss; + + zeroOpcodeIndex(); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ReceiverResultReg); + jumpSICMiss = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* The abort sequence has pushed the LinkReg a second time - because a stack + overflow can only happen after building a frame, which pushes LinkReg anyway, and + we still need to push LinkReg in case we get to this routine from a sendMissAbort. + (On ARM there is a simpler way; use two separate abort calls since all instructions are 32-bits + but on x86 the zero receiver reg, call methodAbort sequence is smaller; we may fix this one day). + Overwrite that duplicate with the right one - the return address for the call to the abort trampoline. + The only reason it matters is an assert in ceStackOverflow: uses it */ + compileTrampolineFornumArgsargargargargregsToSavepushLinkRegresultReg(ceStackOverflow, 1, SendNumArgsReg, null, null, null, 0 /* emptyRegisterMask */, 0, NoReg); + + /* The LinkReg has already been set above. */ + jmpTarget(jumpSICMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genPushRegisterArgsForAbortMissNumArgs(backEnd, numArgs); + return genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceSICMiss, trampolineNamenumRegArgs("ceMethodAbort", numArgs), 1, ReceiverResultReg, null, null, null, 0 /* emptyRegisterMask */, 0, NoReg, 1); +} + + +/* Generate the abort for a PIC. This abort performs either a call of + ceInterpretMethodFromPIC:receiver: to handle invoking an uncogged + target or a call of ceMNUFromPICMNUMethod:receiver: to handle an + MNU dispatch in a closed PIC. It distinguishes the two by testing + ClassReg. If the register is zero then this is an MNU. */ + + /* StackToRegisterMappingCogit>>#genPICAbortTrampolineFor: */ +static NoDbgRegParms usqInt +genPICAbortTrampolineFor(sqInt numArgs) +{ + zeroOpcodeIndex(); + genPushRegisterArgsForAbortMissNumArgs(backEnd, numArgs); + return genInnerPICAbortTrampoline(trampolineNamenumRegArgs("cePICAbort", numArgs)); +} + + /* StackToRegisterMappingCogit>>#genPICMissTrampolineFor: */ +static NoDbgRegParms usqInt +genPICMissTrampolineFor(sqInt numArgs) +{ + usqInt startAddress; + + startAddress = methodZoneBase; + zeroOpcodeIndex(); + + /* N.B. a closed PIC jumps to the miss routine, not calls it, so there is only one retpc on the stack. */ + genPushRegisterArgsForNumArgsscratchReg(backEnd, numArgs, SendNumArgsReg); + genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCPICMissreceiver, trampolineNamenumRegArgs("cePICMiss", numArgs), 2, ClassReg, ReceiverResultReg, null, null, 0 /* emptyRegisterMask */, 1, NoReg, 1); + return startAddress; +} + + /* StackToRegisterMappingCogit>>#genPopStackBytecode */ +static sqInt +genPopStackBytecode(void) +{ + if (((ssTop())->spilled)) { + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BytesPerWord, SPReg); + } + ssPop(1); + return 0; +} + + +/* Check the argument count. Fail if wrong. + Get the method from the outerContext and see if it is cogged. If so, jump + to the + block entry or the no-context-switch entry, as appropriate, and we're + done. If not, + invoke the interpreter primitive. */ +/* Check the argument count. Fail if wrong. + Get the method from the outerContext and see if it is cogged. If so, jump + to the + block entry or the no-context-switch entry, as appropriate, and we're + done. If not, + invoke the interpreter primitive. + Override to push the register args first. */ + + /* StackToRegisterMappingCogit>>#genPrimitiveClosureValue */ +static sqInt +genPrimitiveClosureValue(void) +{ + AbstractInstruction *jumpBCMethod; + AbstractInstruction *jumpFail1; + AbstractInstruction *jumpFail2; + AbstractInstruction *jumpFail3; + AbstractInstruction *jumpFail4; + AbstractInstruction *jumpFailNArgs; + sqInt offset; + void (*primitiveRoutine)(void); + sqInt result; + + genPushRegisterArgs(); + genLoadSlotsourceRegdestReg(ClosureNumArgsIndex, ReceiverResultReg, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, (((usqInt)methodOrBlockNumArgs << 3) | 1), TempReg); + jumpFailNArgs = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genLoadSlotsourceRegdestReg(ClosureOuterContextIndex, ReceiverResultReg, ClassReg); + jumpFail1 = genJumpImmediate(ClassReg); + genGetCompactClassIndexNonImmOfinto(ClassReg, TempReg); + genCmpClassMethodContextCompactIndexR(TempReg); + jumpFail2 = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* We defer unforwarding the receiver to the prologue; scanning blocks + for inst var refs and only unforwarding if the block refers to inst vars. */ + genLoadSlotsourceRegdestReg(MethodIndex, ClassReg, SendNumArgsReg); + jumpFail3 = genJumpImmediate(SendNumArgsReg); + genGetFormatOfinto(SendNumArgsReg, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), TempReg); + jumpFail4 = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + genLoadSlotsourceRegdestReg(HeaderIndex, SendNumArgsReg, ClassReg); + jumpBCMethod = genJumpImmediate(ClassReg); + offset = offsetof(CogMethod, blockEntryOffset); + + /* begin MoveM16:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveM16rR, offset, ClassReg, TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, ClassReg, TempReg); + primitiveRoutine = functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(methodObj, primitiveIndex, null); + if (primitiveRoutine == primitiveClosureValueNoContextSwitch) { + if (!blockNoContextSwitchOffset) { + return NotFullyInitialized; + } + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, blockNoContextSwitchOffset, TempReg); + } + + /* JumpR: */ + genoperand(JumpR, TempReg); + jmpTarget(jumpBCMethod, jmpTarget(jumpFail1, jmpTarget(jumpFail2, jmpTarget(jumpFail3, jmpTarget(jumpFail4, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))))); + if (((result = compileInterpreterPrimitiveflags(primitiveRoutine, primitivePropertyFlagsprimIndex(methodObj, primitiveIndex)))) < 0) { + return result; + } + jmpTarget(jumpFailNArgs, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + +/* Check the argument count. Fail if wrong. + Get the method from the outerContext and see if it is cogged. If so, jump + to the + block entry or the no-context-switch entry, as appropriate, and we're + done. If not, + invoke the interpreter primitive. */ +/* Override to push the register args first. */ + + /* StackToRegisterMappingCogit>>#genPrimitiveFullClosureValue */ +static sqInt +genPrimitiveFullClosureValue(void) +{ + AbstractInstruction *jumpBCMethod; + AbstractInstruction *jumpFail4; + AbstractInstruction *jumpFailImmediateMethod; + AbstractInstruction *jumpFailNArgs; + void (*primitiveRoutine)(void); + sqInt quickConstant; + sqInt result; + + genPushRegisterArgs(); + genLoadSlotsourceRegdestReg(ClosureNumArgsIndex, ReceiverResultReg, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, (((usqInt)methodOrBlockNumArgs << 3) | 1), TempReg); + jumpFailNArgs = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* We defer unforwarding the receiver to the prologue; scanning blocks + for inst var refs and only unforwarding if the block refers to inst vars. */ + genLoadSlotsourceRegdestReg(FullClosureCompiledBlockIndex, ReceiverResultReg, SendNumArgsReg); + jumpFailImmediateMethod = genJumpImmediate(SendNumArgsReg); + genGetFormatOfinto(SendNumArgsReg, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), TempReg); + jumpFail4 = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + genLoadSlotsourceRegdestReg(HeaderIndex, SendNumArgsReg, ClassReg); + jumpBCMethod = genJumpImmediate(ClassReg); + primitiveRoutine = functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(methodObj, primitiveIndex, null); + quickConstant = (primitiveRoutine == primitiveFullClosureValueNoContextSwitch + ? fullBlockNoContextSwitchEntryOffset() + : fullBlockEntryOffset()); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, quickConstant, ClassReg); + + /* JumpR: */ + genoperand(JumpR, ClassReg); + jmpTarget(jumpBCMethod, jmpTarget(jumpFailImmediateMethod, jmpTarget(jumpFail4, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + if (((result = compileInterpreterPrimitiveflags(primitiveRoutine, primitivePropertyFlagsprimIndex(methodObj, primitiveIndex)))) < 0) { + return result; + } + jmpTarget(jumpFailNArgs, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + +/* Generate an in-line perform primitive. The lookup code requires the + selector to be in Arg0Reg. + adjustArgumentsForPerform: adjusts the arguments once + genLookupForPerformNumArgs: has generated the code for the lookup. */ + + /* StackToRegisterMappingCogit>>#genPrimitivePerform */ +static sqInt +genPrimitivePerform(void) +{ + sqInt offset; + + if (methodOrBlockNumArgs > (numRegArgs())) { + offset = (methodOrBlockNumArgs) * BytesPerWord; + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, SPReg, Arg0Reg); + } + return genLookupForPerformNumArgs(methodOrBlockNumArgs); +} + + +/* Generate an in-line perform:withArguments: primitive. The lookup code + requires the selector to be in Arg0Reg + and the array to be in Arg1Reg. The primitive will only handle cases 0 to + numRegArgs. Is it worth it you ask? + Here are arguemnt count requencies for a short run of Croquet/Virtend + which show that even for V3, with only + one rtegister arg, it very much is (but we wimp out on V3 cuz of the + complexity of checking the Array): + [0] = 253743 52.5% + [1] = 116117 24%/76.5% + [2] = 99876 20.7%/97.2% + [3] = 12837 2.7%/99.9% + [4] = 209 + [5] = 84 + [6] = 2 + [7] = 313 + [8] = 0 + [9] = 0 + [10] = 0 + [11] = 1 + [12] = 0 + [13] = 0 + [14] = 0 + [15] = 0 */ + + /* StackToRegisterMappingCogit>>#genPrimitivePerformWithArguments */ +static sqInt +genPrimitivePerformWithArguments(void) +{ + sqInt cacheBaseReg; + AbstractInstruction *itsAHit; + AbstractInstruction *jumpBadNumArgs1; + AbstractInstruction *jumpBadNumArgs2; + AbstractInstruction *jumpClassMiss; + AbstractInstruction *jumpImmArray; + AbstractInstruction *jumpInterpret; + AbstractInstruction *jumpSelectorMiss; + sqInt offset; + sqInt quickConstant; + sqInt reg; + + /* begin genLookupForPerformWithArguments */ + jumpImmArray = genJumpImmediate(Arg1Reg); + genGetInlineCacheClassTagFromintoforEntry(ReceiverResultReg, SendNumArgsReg, 0); + cacheBaseReg = NoReg; + if (!(/* #isWithinMwOffsetRange: #addressIsInCodeZone: */ + ((((usqInt)(methodCacheAddress()))) >= codeBase) + && ((((usqInt)(methodCacheAddress()))) < limitAddress))) { + quickConstant = methodCacheAddress(); + reg = (cacheBaseReg = Extra0Reg); + + /* begin MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, quickConstant, reg); + } + + /* Do first of three probes. See CoInterpreter>>lookupInMethodCacheSel:classTag: */ + jumpSelectorMiss = compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(Arg0Reg, 0, cacheBaseReg); + jumpClassMiss = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Fetch the method, and check if it is cogged. */ + offset = (cacheBaseReg == NoReg + ? (methodCacheAddress()) + ((((usqInt)(MethodCacheMethod) << (shiftForWord())))) + : ((sqInt)((usqInt)(MethodCacheMethod) << (shiftForWord())))); + + /* begin MoveMw:r:R: */ + itsAHit = genoperandoperandoperand(MoveMwrR, offset, ClassReg, SendNumArgsReg); + + /* If the method is not compiled fall back on the interpreter primitive. */ + genLoadSlotsourceRegdestReg(HeaderIndex, SendNumArgsReg, ClassReg); + jumpInterpret = genJumpImmediate(ClassReg); + + /* check the argument count; if it's wrong fall back on the interpreter primitive. */ + + /* begin genLoadcmNumArgsOf:into: */ + /* MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, SendNumArgsReg); + offset = sizeof(((((CogMethod *) 0))->objectHeader)); + + /* begin MoveMb:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMbrR, offset, ClassReg, SendNumArgsReg); + genGetRawSlotSizeOfNonImminto(Arg1Reg, TempReg); + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, SendNumArgsReg); + jumpBadNumArgs1 = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, numRegArgs(), SendNumArgsReg); + jumpBadNumArgs2 = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + + /* Fetch arguments and jump to the method's unchecked entry-point. */ + genFetchRegArgsForPerformWithArguments(TempReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, cmNoCheckEntryOffset, ClassReg); + + /* JumpR: */ + genoperand(JumpR, ClassReg); + + /* First probe missed. Do second of three probes. Shift hash right one and retry. */ + jmpTarget(jumpSelectorMiss, jmpTarget(jumpClassMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + jumpSelectorMiss = compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(Arg0Reg, 1, cacheBaseReg); + + /* JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)itsAHit)); + + /* Second probe missed. Do last probe. Shift hash right two and retry. */ + jmpTarget(jumpSelectorMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpSelectorMiss = compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(Arg0Reg, 2, cacheBaseReg); + + /* JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)itsAHit)); + + /* Last probe missed/not jitted/bad num args. Caller will generate the call to fall back on the interpreter primitive. */ + jmpTarget(jumpImmArray, jmpTarget(jumpSelectorMiss, jmpTarget(jumpInterpret, jmpTarget(jumpBadNumArgs1, jmpTarget(jumpBadNumArgs2, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))))); + return 0; +} + + /* StackToRegisterMappingCogit>>#genPushActiveContextBytecode */ +static sqInt +genPushActiveContextBytecode(void) +{ + assert(needsFrame); + voidReceiverResultRegContainsSelf(); + + /* begin ssAllocateCallReg:and:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(CallerSavedRegisterMask | (((1U << ReceiverResultReg) | (1U << SendNumArgsReg)) | (1U << ClassReg)), simStackPtr, simNativeStackPtr); + genGetActiveContextNumArgslargeinBlock(methodOrBlockNumArgs, methodNeedsLargeContext(methodObj), inBlock); + return ssPushRegister(ReceiverResultReg); +} + + +/* Block compilation. At this point in the method create the block. Note its + start and defer generating code for it until after the method and any + other preceding + blocks. The block's actual code will be compiled later. */ +/* 143 10001111 llllkkkk jjjjjjjj iiiiiiii Push Closure Num Copied llll Num + Args kkkk BlockSize jjjjjjjjiiiiiiii */ + + /* StackToRegisterMappingCogit>>#genPushClosureCopyCopiedValuesBytecode */ +static sqInt +genPushClosureCopyCopiedValuesBytecode(void) +{ + sqInt i; + usqInt numArgs; + sqInt numCopied; + sqInt reg; + sqInt startpc; + + assert(needsFrame); + startpc = bytecodePC + (((generatorAt(byte0))->numBytes)); + addBlockStartAtnumArgsnumCopiedspan(startpc, (numArgs = byte1 & 15), (numCopied = ((usqInt)(byte1)) >> 4), ((((usqInt)(byte2) << 8))) + byte3); + + /* 0 relative */ + + /* begin genInlineClosure:numArgs:numCopied: */ + assert(getActiveContextAllocatesInMachineCode()); + voidReceiverResultRegContainsSelf(); + + /* begin ssAllocateCallReg:and:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(CallerSavedRegisterMask | (((1U << ReceiverResultReg) | (1U << SendNumArgsReg)) | (1U << ClassReg)), simStackPtr, simNativeStackPtr); + genNoPopCreateClosureAtnumArgsnumCopiedcontextNumArgslargeinBlock(startpc + 1, numArgs, numCopied, methodOrBlockNumArgs, methodNeedsLargeContext(methodObj), inBlock); + + /* 1 relative */ + for (i = 1; i <= numCopied; i += 1) { + reg = ssStorePoptoPreferredReg(1, TempReg); + genStoreSourceRegslotIndexintoNewObjectInDestReg(reg, (ClosureFirstCopiedValueIndex + numCopied) - i, ReceiverResultReg); + } + ssPushRegister(ReceiverResultReg); + return 0; +} + + +/* */ +/* Override to avoid the BytecodeSetHasDirectedSuperSend check, which is + unnecessary here given the simulation stack. */ + + /* StackToRegisterMappingCogit>>#genPushLiteralIndex: */ +static NoDbgRegParms sqInt +genPushLiteralIndex(sqInt literalIndex) +{ + sqInt literal; + + literal = getLiteral(literalIndex); + return ssPushConstant(literal); +} + + /* StackToRegisterMappingCogit>>#genPushLiteralVariable: */ +static NoDbgRegParms sqInt +genPushLiteralVariable(sqInt literalIndex) +{ + sqInt association; + sqInt bcpc; + BytecodeDescriptor *descriptor1; + sqInt eA; + sqInt eB; + sqInt freeReg; + sqInt savedB0; + sqInt savedB1; + sqInt savedB2; + sqInt savedB3; + sqInt savedEA; + sqInt savedEB; + sqInt savedNEB; + + association = getLiteral(literalIndex); + + /* If followed by a directed super send bytecode, avoid generating any code yet. + The association will be passed to the directed send trampoline in a register + and fully dereferenced only when first linked. It will be ignored in later sends. */ + assert(!(directedSendUsesBinding)); + + /* begin nextDescriptorExtensionsAndNextPCInto: */ + descriptor1 = generatorAt(byte0); + savedB0 = byte0; + savedB1 = byte1; + savedB2 = byte2; + savedB3 = byte3; + savedEA = extA; + savedEB = extB; + savedNEB = numExtB; + bcpc = bytecodePC + ((descriptor1->numBytes)); + do { + if (bcpc > endPC) { + goto l1; + } + descriptor1 = loadBytesAndGetDescriptor(bcpc); + if (!((descriptor1->isExtension))) { + eA = extA; + eB = extB; + extA = savedEA; + extB = savedEB; + numExtB = savedNEB; + byte0 = savedB0; + byte1 = savedB1; + byte2 = savedB2; + byte3 = savedB3; + if (/* isDirectedSuper:extA:extB: */ + (descriptor1) + && ((((descriptor1->generator)) == genExtSendSuperBytecode) + && (eB >= 64))) { + ssPushConstant(association); + directedSendUsesBinding = 1; + return 0; + } + goto l1; + } + ((descriptor1->generator))(); + bcpc += (descriptor1->numBytes); + } while(1); + /* end nextDescriptorExtensionsAndNextPCInto: */ +l1: + freeReg = allocateRegNotConflictingWith(0); + + /* N.B. Do _not_ use ReceiverResultReg to avoid overwriting receiver in assignment in frameless methods. + So far descriptors are not rich enough to describe the entire dereference so generate the register + load but don't push the result. There is an order-of-evaluation issue if we defer the dereference. */ + + /* genMoveConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(association)) + && ((oopisGreaterThan(association, classTableRootObj())) + || (oopisLessThan(association, nilObject())))) { + annotateobjRef(genoperandoperand(MoveCwR, association, TempReg), association); + } + else { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, association, TempReg); + } + genLoadSlotsourceRegdestReg(ValueIndex, TempReg, freeReg); + ssPushRegister(freeReg); + return 0; +} + + /* StackToRegisterMappingCogit>>#genPushMaybeContextReceiverVariable: */ +static NoDbgRegParms sqInt +genPushMaybeContextReceiverVariable(sqInt slotIndex) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *jmpDone; + AbstractInstruction *jmpSingle; + + /* begin ssAllocateCallReg:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(CallerSavedRegisterMask | (((1U << ReceiverResultReg)) | ((1U << SendNumArgsReg))), simStackPtr, simNativeStackPtr); + ensureReceiverResultRegContainsSelf(); + + /* begin genPushMaybeContextSlotIndex: */ + assert(needsFrame); + if (((CallerSavedRegisterMask & ((1U << ReceiverResultReg))) != 0)) { + /* We have no way of reloading ReceiverResultReg since we need the inst var value as the result. */ + voidReceiverResultRegContainsSelf(); + } + + /* See CoInterpreter>>contextInstructionPointer:frame: for an explanation + of the instruction pointer slot handling. */ + if (slotIndex == InstructionPointerIndex) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, slotIndex, SendNumArgsReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceFetchContextInstVarTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + return ssPushRegister(SendNumArgsReg); + } + genLoadSlotsourceRegdestReg(SenderIndex, ReceiverResultReg, TempReg); + jmpSingle = genJumpNotSmallInteger(TempReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, slotIndex, SendNumArgsReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceFetchContextInstVarTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jmpSingle, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genLoadSlotsourceRegdestReg(slotIndex, ReceiverResultReg, SendNumArgsReg); + jmpTarget(jmpDone, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return ssPushRegister(SendNumArgsReg); +} + + /* StackToRegisterMappingCogit>>#genPushNewArrayBytecode */ +static sqInt +genPushNewArrayBytecode(void) +{ + sqInt i; + sqInt iSqInt; + int popValues; + usqInt size; + + assert(needsFrame); + voidReceiverResultRegContainsSelf(); + if ((popValues = byte1 > 0x7F)) { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (iSqInt = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); iSqInt <= simStackPtr; iSqInt += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(iSqInt), frameOffsetOfTemporary(iSqInt - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + } + else { + /* begin ssAllocateCallReg:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(CallerSavedRegisterMask | (((1U << SendNumArgsReg)) | ((1U << ReceiverResultReg))), simStackPtr, simNativeStackPtr); + } + size = byte1 & 0x7F; + if (!popValues) { + if (tryCollapseTempVectorInitializationOfSize(size)) { + return 0; + } + } + genNewArrayOfSizeinitialized(size, !popValues); + if (popValues) { + for (i = (size - 1); i >= 0; i += -1) { + /* PopR: */ + genoperand(PopR, TempReg); + genStoreSourceRegslotIndexintoNewObjectInDestReg(TempReg, i, ReceiverResultReg); + } + ssPop(size); + } + return ssPushRegister(ReceiverResultReg); +} + + /* StackToRegisterMappingCogit>>#genPushReceiverBytecode */ +static sqInt +genPushReceiverBytecode(void) +{ + if ((((simSelf())->liveRegister)) == ReceiverResultReg) { + return ssPushRegister(ReceiverResultReg); + } + return ssPushDesc(ssSelfDescriptor()); +} + + /* StackToRegisterMappingCogit>>#genPushReceiverVariable: */ +static NoDbgRegParms sqInt +genPushReceiverVariable(sqInt index) +{ + ensureReceiverResultRegContainsSelf(); + return ssPushBaseoffset(ReceiverResultReg, slotOffsetOfInstVarIndex(index)); +} + + +/* Ensure that the register args are pushed before the retpc for methods with + arity <= self numRegArgs. + */ +/* This isn't as clumsy on a RISC. But putting the receiver and + args above the return address means the CoInterpreter has a + single machine-code frame format which saves us a lot of work. */ + + /* StackToRegisterMappingCogit>>#genPushRegisterArgs */ +static void +genPushRegisterArgs(void) +{ + if (!(regArgsHaveBeenPushed + || (methodOrBlockNumArgs > (numRegArgs())))) { + genPushRegisterArgsForNumArgsscratchReg(backEnd, methodOrBlockNumArgs, SendNumArgsReg); + regArgsHaveBeenPushed = 1; + } +} + + /* StackToRegisterMappingCogit>>#genPushRemoteTempLongBytecode */ +static sqInt +genPushRemoteTempLongBytecode(void) +{ + sqInt offset; + sqInt remoteTempReg; + sqInt tempVectReg; + + tempVectReg = allocateRegNotConflictingWith(0); + offset = frameOffsetOfTemporary(byte2); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, FPReg, tempVectReg); + remoteTempReg = availableRegisterOrNoneFor(backEnd, (liveRegisters()) | (((tempVectReg < 0) ? (((usqInt)(1)) >> (-tempVectReg)) : (1ULL << tempVectReg)))); + if (remoteTempReg == NoReg) { + remoteTempReg = tempVectReg; + } + genLoadSlotsourceRegdestReg(byte1, tempVectReg, remoteTempReg); + return ssPushRegister(remoteTempReg); +} + + +/* If a frameless method (not a block), only argument temps can be accessed. + This is assured by the use of needsFrameIfMod16GENumArgs: in pushTemp. */ + + /* StackToRegisterMappingCogit>>#genPushTemporaryVariable: */ +static NoDbgRegParms sqInt +genPushTemporaryVariable(sqInt index) +{ + assert((inBlock > 0) + || (needsFrame + || (index < methodOrBlockNumArgs))); + return ssPushDesc(simStack[index + 1]); +} + + +/* In a frameless method ReceiverResultReg already contains self. + In a frameful method, ReceiverResultReg /may/ contain self. */ + + /* StackToRegisterMappingCogit>>#genReturnReceiver */ +static sqInt +genReturnReceiver(void) +{ + if (needsFrame) { + if (!((((simSelf())->liveRegister)) == ReceiverResultReg)) { + /* begin putSelfInReceiverResultReg */ + storeToReg(simSelf(), ReceiverResultReg); + } + } + return genUpArrowReturn(); +} + + /* StackToRegisterMappingCogit>>#genReturnTopFromBlock */ +static sqInt +genReturnTopFromBlock(void) +{ + assert(inBlock > 0); + popToReg(ssTop(), ReceiverResultReg); + ssPop(1); + return genBlockReturn(); +} + + /* StackToRegisterMappingCogit>>#genReturnTopFromMethod */ +static sqInt +genReturnTopFromMethod(void) +{ + popToReg(ssTop(), ReceiverResultReg); + ssPop(1); + return genUpArrowReturn(); +} + + /* StackToRegisterMappingCogit>>#genSendDirectedSuper:numArgs: */ +static NoDbgRegParms sqInt +genSendDirectedSupernumArgs(sqInt selectorIndex, sqInt numArgs) +{ + sqInt result; + + assert((((ssTop())->type)) == SSConstant); + tempOop = ((ssTop())->constant); + ssPop(1); + marshallSendArguments(numArgs); + result = genMarshalledSendnumArgssendTable(selectorIndex, numArgs, (directedSendUsesBinding + ? directedSuperBindingSendTrampolines + : directedSuperSendTrampolines)); + directedSendUsesBinding = 0; + return result; +} + + /* StackToRegisterMappingCogit>>#genSendSuper:numArgs: */ +static NoDbgRegParms sqInt +genSendSupernumArgs(sqInt selectorIndex, sqInt numArgs) +{ + marshallSendArguments(numArgs); + return genMarshalledSendnumArgssendTable(selectorIndex, numArgs, superSendTrampolines); +} + + +/* Generate a trampoline with four arguments. + Hack: a negative value indicates an abstract register, a non-negative + value indicates a constant. */ + + /* StackToRegisterMappingCogit>>#genSendTrampolineFor:numArgs:called:arg:arg:arg:arg: */ +static NoDbgRegParms usqInt +genSendTrampolineFornumArgscalledargargargarg(void *aRoutine, sqInt numArgs, char *aString, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3) +{ + sqInt routine; + usqInt startAddress; + + startAddress = methodZoneBase; + zeroOpcodeIndex(); + genPushRegisterArgsForNumArgsscratchReg(backEnd, numArgs, SendNumArgsReg); + + /* If we're on a 64-bit platform we use 32-bit inline caches. These don't have room for a selector oop, only + a selector index. If so, call the selectorIndexDereferenceRoutine to convert the index into a selector oop. */ + if ((routine = ceDereferenceSelectorIndex)) { + /* Explicitly save LinkReg via ExtraReg2; it's presumably faster than pushing/popping */ + + /* Call: */ + genoperand(Call, routine); + } + genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(aRoutine, aString, 4, regOrConst0, regOrConst1, regOrConst2, regOrConst3, 0 /* emptyRegisterMask */, 1, NoReg, 1); + return startAddress; +} + + /* StackToRegisterMappingCogit>>#genSend:numArgs: */ +static NoDbgRegParms sqInt +genSendnumArgs(sqInt selectorIndex, sqInt numArgs) +{ + marshallSendArguments(numArgs); + return genMarshalledSendnumArgssendTable(selectorIndex, numArgs, ordinarySendTrampolines); +} + + /* StackToRegisterMappingCogit>>#genSpecialSelectorArithmetic */ +static sqInt +genSpecialSelectorArithmetic(void) +{ + AbstractInstruction *abstractInstruction; + sqInt argInt; + int argIsConst; + sqInt argIsInt; + sqInt i; + sqInt index; + AbstractInstruction *jumpContinue; + AbstractInstruction *jumpNotSmallInts; + BytecodeDescriptor *primDescriptor; + sqInt rcvrInt; + int rcvrIsConst; + sqInt rcvrIsInt; + sqInt result; + SimStackEntry *selfOfIsSameEntryAs; + + primDescriptor = generatorAt(byte0); + argIsInt = ((argIsConst = (((ssTop())->type)) == SSConstant)) + && ((((((argInt = ((ssTop())->constant)))) & 7) == 1)); + rcvrIsInt = (((rcvrIsConst = (((ssValue(1))->type)) == SSConstant)) + && ((((((rcvrInt = ((ssValue(1))->constant)))) & 7) == 1))) + || ((mclassIsSmallInteger()) + && (((selfOfIsSameEntryAs = ssValue(1)), + isSameEntryAs(selfOfIsSameEntryAs, simSelf())))); + if (argIsInt + && (rcvrIsInt + && (rcvrIsConst))) { + rcvrInt = (rcvrInt >> 3); + argInt = (argInt >> 3); + switch ((primDescriptor->opcode)) { + case AddRR: + result = rcvrInt + argInt; + break; + case SubRR: + result = rcvrInt - argInt; + break; + case AndRR: + result = rcvrInt & argInt; + break; + case OrRR: + result = rcvrInt | argInt; + break; + default: + error("Case not found and no otherwise clause"); + } + if (isIntegerValue(result)) { + /* Must annotate the bytecode for correct pc mapping. */ + ssPop(2); + return ssPushAnnotatedConstant((((usqInt)result << 3) | 1)); + } + return genSpecialSelectorSend(); + } + + /* If there's any constant involved other than a SmallInteger don't attempt to inline. */ + if ((rcvrIsConst + && (!rcvrIsInt)) + || (argIsConst + && (!argIsInt))) { + return genSpecialSelectorSend(); + } + + /* If we know nothing about the types then better not to inline as the inline cache and + primitive code is not terribly slow so wasting time on duplicating tag tests is pointless. */ + if (!(argIsInt + || (rcvrIsInt))) { + return genSpecialSelectorSend(); + } + if (argIsInt) { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= (simStackPtr - 2)) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < (simStackPtr - 2)) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : (simStackPtr - 2))); i <= (simStackPtr - 2); i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = (simStackPtr - 2) + 1; + } + popToReg(ssValue(1), ReceiverResultReg); + ssPop(2); + } + else { + marshallSendArguments(1); + } + jumpNotSmallInts = (rcvrIsInt + && (argIsInt) + ? ((AbstractInstruction *) null) + : (argIsInt + ? genJumpNotSmallInteger(ReceiverResultReg) + : (rcvrIsInt + ? genJumpNotSmallInteger(Arg0Reg) + : genJumpNotSmallIntegersInandscratch(ReceiverResultReg, Arg0Reg, TempReg)))); + switch ((primDescriptor->opcode)) { + case AddRR: + if (argIsInt) { + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, argInt - ConstZero, ReceiverResultReg); + jumpContinue = genConditionalBranchoperand(JumpNoOverflow, ((sqInt)0)); + + /* overflow; must undo the damage before continuing */ + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, argInt - ConstZero, ReceiverResultReg); + } + else { + genRemoveSmallIntegerTagsInScratchReg(ReceiverResultReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg0Reg, ReceiverResultReg); + jumpContinue = genConditionalBranchoperand(JumpNoOverflow, ((sqInt)0)); + + /* overflow; must undo the damage before continuing */ + if (rcvrIsInt + && (rcvrIsConst)) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, rcvrInt, ReceiverResultReg); + } + else { + /* SubR:R: */ + genoperandoperand(SubRR, Arg0Reg, ReceiverResultReg); + genSetSmallIntegerTagsIn(ReceiverResultReg); + } + } + break; + case SubRR: + if (argIsInt) { + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, argInt - ConstZero, ReceiverResultReg); + jumpContinue = genConditionalBranchoperand(JumpNoOverflow, ((sqInt)0)); + + /* overflow; must undo the damage before continuing */ + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, argInt - ConstZero, ReceiverResultReg); + } + else { + genRemoveSmallIntegerTagsInScratchReg(Arg0Reg); + + /* SubR:R: */ + genoperandoperand(SubRR, Arg0Reg, ReceiverResultReg); + jumpContinue = genConditionalBranchoperand(JumpNoOverflow, ((sqInt)0)); + + /* overflow; must undo the damage before continuing */ + + /* AddR:R: */ + genoperandoperand(AddRR, Arg0Reg, ReceiverResultReg); + genSetSmallIntegerTagsIn(Arg0Reg); + } + break; + case AndRR: + if (argIsInt) { + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, argInt, ReceiverResultReg); + } + else { + /* AndR:R: */ + genoperandoperand(AndRR, Arg0Reg, ReceiverResultReg); + } + jumpContinue = (jumpNotSmallInts + ? genoperand(Jump, ((sqInt)0)) + : ((AbstractInstruction *) null)); + break; + case OrRR: + if (argIsInt) { + /* #OrCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(OrCqR, argInt, ReceiverResultReg); + } + else { + /* OrR:R: */ + genoperandoperand(OrRR, Arg0Reg, ReceiverResultReg); + } + jumpContinue = (jumpNotSmallInts + ? genoperand(Jump, ((sqInt)0)) + : ((AbstractInstruction *) null)); + break; + default: + error("Case not found and no otherwise clause"); + } + if (jumpNotSmallInts) { + jmpTarget(jumpNotSmallInts, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } + else { + if (!jumpContinue) { + /* begin annotateInstructionForBytecode */ + abstractInstruction = (prevInstIsPCAnnotated() + ? gen(Nop) + : genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + ssPushRegister(ReceiverResultReg); + return 0; + } + } + if (argIsInt) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, argInt, Arg0Reg); + } + index = byte0 - (/* firstSpecialSelectorBytecodeOffset */ + (bytecodeSetOffset == 0x100 + ? AltFirstSpecialSelector + 0x100 + : FirstSpecialSelector)); + genMarshalledSendnumArgssendTable((-index) - 1, 1, ordinarySendTrampolines); + jmpTarget(jumpContinue, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + /* StackToRegisterMappingCogit>>#genSpecialSelectorClass */ +static sqInt +genSpecialSelectorClass(void) +{ + sqInt requiredReg1; + sqInt topReg; + + topReg = registerOrNone(ssTop()); + ssPop(1); + if ((topReg == NoReg) + || (topReg == ClassReg)) { + requiredReg1 = (topReg = SendNumArgsReg); + + /* begin ssAllocateRequiredReg:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((((requiredReg1 < 0) ? (((usqInt)(1)) >> (-requiredReg1)) : (1ULL << requiredReg1))) | ((1U << ClassReg)), simStackPtr, simNativeStackPtr); + } + else { + /* begin ssAllocateRequiredReg: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << ClassReg), simStackPtr, simNativeStackPtr); + } + ssPush(1); + popToReg(ssTop(), topReg); + genGetClassObjectOfintoscratchRegmayBeAForwarder(topReg, ClassReg, TempReg, mayBeAForwarder(ssTop())); + ssPop(1); + return ssPushRegister(ClassReg); +} + + /* StackToRegisterMappingCogit>>#genSpecialSelectorComparison */ +static sqInt +genSpecialSelectorComparison(void) +{ + AbstractInstruction *abstractInstruction; + sqInt argInt; + sqInt argIsIntConst; + BytecodeDescriptor *branchDescriptor; + BytecodeDescriptor *branchDescriptor1; + sqInt i; + sqInt index; + sqInt inlineCAB; + AbstractInstruction *jumpNotSmallInts; + void *jumpTarget; + sqInt nExts; + sqInt nextPC; + sqInt nextPCSqInt; + sqInt postBranchPC; + sqInt postBranchPCSqInt; + BytecodeDescriptor *primDescriptor; + BytecodeDescriptor *primDescriptor1; + int rcvrIsConst; + sqInt rcvrIsInt; + SimStackEntry *selfOfIsSameEntryAs; + sqInt targetBytecodePC; + sqInt targetPC; + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= (simStackPtr - 2)) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < (simStackPtr - 2)) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : (simStackPtr - 2))); i <= (simStackPtr - 2); i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = (simStackPtr - 2) + 1; + } + primDescriptor = generatorAt(byte0); + argIsIntConst = ((((ssTop())->type)) == SSConstant) + && ((((((argInt = ((ssTop())->constant)))) & 7) == 1)); + rcvrIsInt = (((rcvrIsConst = (((ssValue(1))->type)) == SSConstant)) + && (((((((ssValue(1))->constant))) & 7) == 1))) + || ((mclassIsSmallInteger()) + && (((selfOfIsSameEntryAs = ssValue(1)), + isSameEntryAs(selfOfIsSameEntryAs, simSelf())))); + if (argIsIntConst + && (rcvrIsInt + && (rcvrIsConst))) { + return genStaticallyResolvedSpecialSelectorComparison(); + } + + /* begin extractMaybeBranchDescriptorInto: */ + branchDescriptor1 = ((BytecodeDescriptor *) 0); + primDescriptor1 = generatorAt(byte0); + nextPCSqInt = bytecodePC + ((primDescriptor1->numBytes)); + nExts = 0; + while (1) { + while (1) { + /* begin generatorForPC: */ + branchDescriptor1 = generatorAt(bytecodeSetOffset + (fetchByteofObject(nextPCSqInt, methodObj))); + if (!((branchDescriptor1->isExtension))) break; + nExts += 1; + nextPCSqInt += (branchDescriptor1->numBytes); + } + if (!(/* isUnconditionalBranch */ + (isBranch(branchDescriptor1)) + && (!(((branchDescriptor1->isBranchTrue)) + || ((branchDescriptor1->isBranchFalse)))))) break; + nextPCSqInt = eventualTargetOf((nextPCSqInt + ((branchDescriptor1->numBytes))) + (((branchDescriptor1->spanFunction))(branchDescriptor1, nextPCSqInt, nExts, methodObj))); + } + targetBytecodePC = (postBranchPCSqInt = 0); + if (((branchDescriptor1->isBranchTrue)) + || ((branchDescriptor1->isBranchFalse))) { + targetBytecodePC = eventualTargetOf((nextPCSqInt + ((branchDescriptor1->numBytes))) + (((branchDescriptor1->spanFunction))(branchDescriptor1, nextPCSqInt, nExts, methodObj))); + postBranchPCSqInt = eventualTargetOf(nextPCSqInt + ((branchDescriptor1->numBytes))); + } + else { + nextPCSqInt = bytecodePC + ((primDescriptor1->numBytes)); + } + branchDescriptor = branchDescriptor1; + nextPC = nextPCSqInt; + postBranchPC = postBranchPCSqInt; + targetPC = targetBytecodePC; + + /* Only interested in inlining if followed by a conditional branch. */ + inlineCAB = ((branchDescriptor->isBranchTrue)) + || ((branchDescriptor->isBranchFalse)); + + /* Further, only interested in inlining = and ~= if there's a SmallInteger constant involved. + The relational operators successfully statically predict SmallIntegers; the equality operators do not. */ + if (inlineCAB + && ((((primDescriptor->opcode)) == JumpZero) + || (((primDescriptor->opcode)) == JumpNonZero))) { + inlineCAB = argIsIntConst + || (rcvrIsInt); + } + if (!inlineCAB) { + return genSpecialSelectorSend(); + } + if (argIsIntConst) { + popToReg(ssValue(1), ReceiverResultReg); + ssPop(2); + } + else { + marshallSendArguments(1); + } + jumpNotSmallInts = (rcvrIsInt + && (argIsIntConst) + ? ((AbstractInstruction *) null) + : (argIsIntConst + ? genJumpNotSmallInteger(ReceiverResultReg) + : (rcvrIsInt + ? genJumpNotSmallInteger(Arg0Reg) + : genJumpNotSmallIntegersInandscratch(ReceiverResultReg, Arg0Reg, TempReg)))); + if (argIsIntConst) { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, argInt, ReceiverResultReg); + } + else { + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, ReceiverResultReg); + } + + /* Cmp is weird/backwards so invert the comparison. Further since there is a following conditional + jump bytecode define non-merge fixups and leave the cond bytecode to set the mergeness. */ + genConditionalBranchoperand(((branchDescriptor->isBranchTrue) + ? (primDescriptor->opcode) + : inverseBranchFor((primDescriptor->opcode))), ((usqInt)(ensureNonMergeFixupAt(targetPC)))); + jumpTarget = ensureNonMergeFixupAt(postBranchPC); + + /* begin Jump: */ + genoperand(Jump, ((sqInt)jumpTarget)); + if (!jumpNotSmallInts) { + /* begin annotateInstructionForBytecode */ + abstractInstruction = (prevInstIsPCAnnotated() + ? gen(Nop) + : genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + ensureFixupAt(postBranchPC); + ensureFixupAt(targetPC); + deadCode = 1; + return 0; + } + jmpTarget(jumpNotSmallInts, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + if (argIsIntConst) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, argInt, Arg0Reg); + } + index = byte0 - (/* firstSpecialSelectorBytecodeOffset */ + (bytecodeSetOffset == 0x100 + ? AltFirstSpecialSelector + 0x100 + : FirstSpecialSelector)); + return genMarshalledSendnumArgssendTable((-index) - 1, 1, ordinarySendTrampolines); +} + + +/* Assumes both operands are ints */ + + /* StackToRegisterMappingCogit>>#genStaticallyResolvedSpecialSelectorComparison */ +static sqInt +genStaticallyResolvedSpecialSelectorComparison(void) +{ + sqInt argInt; + BytecodeDescriptor *primDescriptor; + sqInt rcvrInt; + int result; + + primDescriptor = generatorAt(byte0); + argInt = ((ssTop())->constant); + rcvrInt = ((ssValue(1))->constant); + + /* In Simulator ints are unsigned... */ + switch ((primDescriptor->opcode)) { + case JumpLess: + result = rcvrInt < argInt; + break; + case JumpLessOrEqual: + result = rcvrInt <= argInt; + break; + case JumpGreater: + result = rcvrInt > argInt; + break; + case JumpGreaterOrEqual: + result = rcvrInt >= argInt; + break; + case JumpZero: + result = rcvrInt == argInt; + break; + case JumpNonZero: + result = rcvrInt != argInt; + break; + default: + error("Case not found and no otherwise clause"); + } + + /* Must annotate the bytecode for correct pc mapping. */ + ssPop(2); + return ssPushAnnotatedConstant((result + ? trueObject() + : falseObject())); +} + + +/* We need a frame because the association has to be in ReceiverResultReg for + the various trampolines + and ReceiverResultReg holds only the receiver in frameless methods. + */ + + /* StackToRegisterMappingCogit>>#genStorePop:LiteralVariable:needsStoreCheck:needsImmutabilityCheck: */ +static NoDbgRegParms sqInt +genStorePopLiteralVariableneedsStoreCheckneedsImmutabilityCheck(sqInt popBoolean, sqInt litVarIndex, sqInt needsStoreCheck, sqInt needsImmCheck) +{ + sqInt association; + sqInt i; + sqInt topReg; + + assert(needsFrame); + + /* begin genLoadLiteralVariable:in: */ + association = getLiteral(litVarIndex); + voidReceiverResultRegContainsSelf(); + + /* begin ssAllocateRequiredReg: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << ReceiverResultReg), simStackPtr, simNativeStackPtr); + + /* genMoveConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(association)) + && ((oopisGreaterThan(association, classTableRootObj())) + || (oopisLessThan(association, nilObject())))) { + annotateobjRef(genoperandoperand(MoveCwR, association, ReceiverResultReg), association); + } + else { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, association, ReceiverResultReg); + } + + /* begin genGenericStorePop:slotIndex:destReg:needsStoreCheck:needsRestoreRcvr:needsImmutabilityCheck: */ +# if IMMUTABILITY + if (needsImmCheck) { + /* begin ssAllocateRequiredReg:upThrough: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << ClassReg), simStackPtr - 1, simNativeStackPtr); + ssStoreAndReplacePoptoReg(popBoolean, ClassReg); + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + return genStoreWithImmutabilityCheckSourceRegslotIndexdestRegscratchRegneedsStoreCheckneedRestoreRcvr(ClassReg, ValueIndex, ReceiverResultReg, TempReg, needsStoreCheck, 0); + } +# endif // IMMUTABILITY + + topReg = allocateRegForStackEntryAtnotConflictingWith(0, (1U << ReceiverResultReg)); + ssStorePoptoReg(popBoolean, topReg); + return genStoreSourceRegslotIndexdestRegscratchReginFrameneedsStoreCheck(topReg, ValueIndex, ReceiverResultReg, TempReg, needsFrame, needsStoreCheck); +} + + +/* The reason we need a frame here is that assigning to an inst var of a + context may + involve wholesale reorganization of stack pages, and the only way to + preserve the + execution state of an activation in that case is if it has a frame. */ + + /* StackToRegisterMappingCogit>>#genStorePop:MaybeContextReceiverVariable:needsStoreCheck:needsImmutabilityCheck: */ +static NoDbgRegParms sqInt +genStorePopMaybeContextReceiverVariableneedsStoreCheckneedsImmutabilityCheck(sqInt popBoolean, sqInt slotIndex, sqInt needsStoreCheck, sqInt needsImmCheck) +{ + AbstractInstruction *abstractInstruction; + sqInt i; + AbstractInstruction *immutabilityFailure; + AbstractInstruction *mutableJump; + + assert(needsFrame); + ssFlushUpThroughReceiverVariable(slotIndex); + preserveTopInClassRegIfIsReceiverResultReg(); + ensureReceiverResultRegContainsSelf(); + + /* begin genGenericStorePop:MaybeContextSlotIndex:needsStoreCheck:needsRestoreRcvr:needsImmutabilityCheck: */ + immutabilityFailure = ((AbstractInstruction *) 0); + assert(needsFrame); +# if IMMUTABILITY + if (needsImmCheck) { + mutableJump = genJumpMutablescratchReg(ReceiverResultReg, TempReg); + + /* begin genStoreTrampolineCall: */ + assert(IMMUTABILITY); + if (slotIndex >= (NumStoreTrampolines - 1)) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, slotIndex, TempReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreTrampolines[NumStoreTrampolines - 1]); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreTrampolines[slotIndex]); + (abstractInstruction->annotation = IsRelativeCall); + } + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + + /* begin putSelfInReceiverResultReg */ + storeToReg(simSelf(), ReceiverResultReg); + immutabilityFailure = genoperand(Jump, ((sqInt)0)); + jmpTarget(mutableJump, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } +# endif // IMMUTABILITY + + ssPop(1); + + /* begin ssAllocateCallReg:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(CallerSavedRegisterMask | (((1U << ClassReg)) | ((1U << SendNumArgsReg))), simStackPtr, simNativeStackPtr); + ssPush(1); + genLoadSlotsourceRegdestReg(SenderIndex, ReceiverResultReg, TempReg); + ssStoreAndReplacePoptoReg(popBoolean, ClassReg); + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, slotIndex, SendNumArgsReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreContextInstVarTrampoline); + (abstractInstruction->annotation = IsRelativeCall); +# if IMMUTABILITY + if (needsImmCheck) { + jmpTarget(immutabilityFailure, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } +# endif + + return 0; +} + + /* StackToRegisterMappingCogit>>#genVanillaInlinedIdenticalOrNotIf: */ +static NoDbgRegParms sqInt +genVanillaInlinedIdenticalOrNotIf(sqInt orNot) +{ + int argIsConstant; + sqInt argNeedsReg; + sqInt argReg; + sqInt argRegSqInt; + BytecodeDescriptor *branchDescriptor; + BytecodeDescriptor *branchDescriptor1; + sqInt i; + void *jumpTarget; + sqInt nExts; + sqInt nextPC; + sqInt nextPCSqInt; + sqInt postBranchPC; + sqInt postBranchPCSqInt; + BytecodeDescriptor *primDescriptor; + sqInt rcvrIsConstant; + sqInt rcvrNeedsReg; + sqInt rcvrReg; + sqInt rcvrRegSqInt; + sqInt reg; + sqInt rNextSqInt; + sqInt rTopSqInt; + BytecodeFixup *self_in_CogBytecodeFixup; + sqInt targetBytecodePC; + sqInt targetPC; + sqInt topRegistersMask; + + /* begin extractMaybeBranchDescriptorInto: */ + branchDescriptor1 = ((BytecodeDescriptor *) 0); + primDescriptor = generatorAt(byte0); + nextPCSqInt = bytecodePC + ((primDescriptor->numBytes)); + nExts = 0; + while (1) { + while (1) { + /* begin generatorForPC: */ + branchDescriptor1 = generatorAt(bytecodeSetOffset + (fetchByteofObject(nextPCSqInt, methodObj))); + if (!((branchDescriptor1->isExtension))) break; + nExts += 1; + nextPCSqInt += (branchDescriptor1->numBytes); + } + if (!(/* isUnconditionalBranch */ + (isBranch(branchDescriptor1)) + && (!(((branchDescriptor1->isBranchTrue)) + || ((branchDescriptor1->isBranchFalse)))))) break; + nextPCSqInt = eventualTargetOf((nextPCSqInt + ((branchDescriptor1->numBytes))) + (((branchDescriptor1->spanFunction))(branchDescriptor1, nextPCSqInt, nExts, methodObj))); + } + targetBytecodePC = (postBranchPCSqInt = 0); + if (((branchDescriptor1->isBranchTrue)) + || ((branchDescriptor1->isBranchFalse))) { + targetBytecodePC = eventualTargetOf((nextPCSqInt + ((branchDescriptor1->numBytes))) + (((branchDescriptor1->spanFunction))(branchDescriptor1, nextPCSqInt, nExts, methodObj))); + postBranchPCSqInt = eventualTargetOf(nextPCSqInt + ((branchDescriptor1->numBytes))); + } + else { + nextPCSqInt = bytecodePC + ((primDescriptor->numBytes)); + } + branchDescriptor = branchDescriptor1; + nextPC = nextPCSqInt; + postBranchPC = postBranchPCSqInt; + targetPC = targetBytecodePC; + argIsConstant = (((ssTop())->type)) == SSConstant; + + /* They can't be both constants to use correct machine opcodes. + However annotable constants can't be resolved statically, hence we need to careful. */ + rcvrIsConstant = (!argIsConstant) + && ((((ssValue(1))->type)) == SSConstant); + argNeedsReg = !argIsConstant; + rcvrNeedsReg = !rcvrIsConstant; + + /* begin allocateEqualsEqualsRegistersArgNeedsReg:rcvrNeedsReg:into: */ + assert(argNeedsReg + || (rcvrNeedsReg)); + argRegSqInt = (rcvrRegSqInt = NoReg); + if (argNeedsReg) { + if (rcvrNeedsReg) { + /* begin allocateRegForStackTopTwoEntriesInto: */ + topRegistersMask = 0; + rTopSqInt = (rNextSqInt = NoReg); + if ((registerOrNone(ssTop())) != NoReg) { + rTopSqInt = registerOrNone(ssTop()); + } + if ((registerOrNone(ssValue(1))) != NoReg) { + reg = (rNextSqInt = registerOrNone(ssValue(1))); + + /* begin registerMaskFor: */ + topRegistersMask = ((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)); + } + if (rTopSqInt == NoReg) { + rTopSqInt = allocateRegNotConflictingWith(topRegistersMask); + } + if (rNextSqInt == NoReg) { + rNextSqInt = allocateRegNotConflictingWith(((rTopSqInt < 0) ? (((usqInt)(1)) >> (-rTopSqInt)) : (1ULL << rTopSqInt))); + } + assert(!(((rTopSqInt == NoReg) + || (rNextSqInt == NoReg)))); + argRegSqInt = rTopSqInt; + rcvrRegSqInt = rNextSqInt; + popToReg(ssTop(), argRegSqInt); + popToReg(ssValue(1), rcvrRegSqInt); + } + else { + argRegSqInt = allocateRegForStackEntryAtnotConflictingWith(0, 0); + popToReg(ssTop(), argRegSqInt); + + /* If the receiver is a spilled constant we need to pop it from the stack. */ + if (((ssValue(1))->spilled)) { + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BytesPerWord, SPReg); + } + } + } + else { + assert(rcvrNeedsReg); + assert(!((((ssTop())->spilled)))); + rcvrRegSqInt = allocateRegForStackEntryAtnotConflictingWith(1, 0); + popToReg(ssValue(1), rcvrRegSqInt); + } + assert(!((argNeedsReg + && (argRegSqInt == NoReg)))); + assert(!((rcvrNeedsReg + && (rcvrRegSqInt == NoReg)))); + rcvrReg = rcvrRegSqInt; + argReg = argRegSqInt; + + /* If not followed by a branch, resolve to true or false. */ + if (!(((branchDescriptor->isBranchTrue)) + || ((branchDescriptor->isBranchFalse)))) { + return genIdenticalNoBranchArgIsConstantrcvrIsConstantargRegrcvrRegorNotIf(argIsConstant, rcvrIsConstant, argReg, rcvrReg, orNot); + } + + /* If branching the stack must be flushed for the merge */ + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= (simStackPtr - 2)) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < (simStackPtr - 2)) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : (simStackPtr - 2))); i <= (simStackPtr - 2); i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = (simStackPtr - 2) + 1; + } + + /* begin genCmpArgIsConstant:rcvrIsConstant:argReg:rcvrReg: */ + assert((argReg != NoReg) + || (rcvrReg != NoReg)); + if (argIsConstant) { + /* genCmpConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject())))) { + annotateobjRef(genoperandoperand(CmpCwR, ((ssTop())->constant), rcvrReg), ((ssTop())->constant)); + } + else { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ((ssTop())->constant), rcvrReg); + } + } + else { + if (rcvrIsConstant) { + /* genCmpConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssValue(1))->constant))) + && ((oopisGreaterThan(((ssValue(1))->constant), classTableRootObj())) + || (oopisLessThan(((ssValue(1))->constant), nilObject())))) { + annotateobjRef(genoperandoperand(CmpCwR, ((ssValue(1))->constant), argReg), ((ssValue(1))->constant)); + } + else { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ((ssValue(1))->constant), argReg); + } + } + else { + /* begin CmpR:R: */ + assert(!((argReg == SPReg))); + genoperandoperand(CmpRR, argReg, rcvrReg); + } + } + ssPop(2); + + /* Further since there is a following conditional jump bytecode, define + non-merge fixups and leave the cond bytecode to set the mergeness. */ + + /* begin fixupAt: */ + self_in_CogBytecodeFixup = fixupAtIndex(nextPC - initialPC); + if ((self_in_CogBytecodeFixup->targetInstruction)) { + assert(!(deadCode)); + } + else { + deadCode = 1; + ensureFixupAt(targetPC); + ensureFixupAt(postBranchPC); + } + if (orNot == ((branchDescriptor->isBranchTrue))) { + ensureNonMergeFixupAt(targetPC); + jumpTarget = ensureNonMergeFixupAt(postBranchPC); + + /* begin JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)jumpTarget)); + jumpTarget = ensureNonMergeFixupAt(targetPC); + + /* begin Jump: */ + genoperand(Jump, ((sqInt)jumpTarget)); + } + else { + jumpTarget = ensureNonMergeFixupAt(targetPC); + + /* begin JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)jumpTarget)); + jumpTarget = ensureNonMergeFixupAt(postBranchPC); + + /* begin Jump: */ + genoperand(Jump, ((sqInt)jumpTarget)); + } + + /* orNot is true for ~~ + a == b ifTrue: ... or a ~~ b ifFalse: ... jump on equal to target pc + a == b ifFalse: ... or a ~~ b ifTrue: ... jump on equal to post-branch pc + Not reached, execution flow has jumped to fixup */ + if (!deadCode) { + ssPushConstant(trueObject()); + } + return 0; +} + + +/* The register receiver (the closure itself) and args are pushed by the + closure value primitive(s) + and hence a frameless block has all arguments and copied values pushed to + the stack. However, + the method receiver (self) is put in the ReceiverResultReg by the block + entry. + */ + + /* StackToRegisterMappingCogit>>#initSimStackForFramelessBlock: */ +static NoDbgRegParms void +initSimStackForFramelessBlock(sqInt startpc) +{ + SimStackEntry *cascade0; + SimStackEntry *desc; + sqInt i; + + cascade0 = simSelf(); + (cascade0->type = SSRegister); + (cascade0->spilled = 0); + (cascade0->registerr = ReceiverResultReg); + (cascade0->liveRegister = ReceiverResultReg); + assert(methodOrBlockNumTemps >= methodOrBlockNumArgs); + for (i = 1; i <= methodOrBlockNumTemps; i += 1) { + desc = simStackAt(i); + (desc->type = SSBaseOffset); + (desc->spilled = 1); + (desc->registerr = SPReg); + (desc->offset = ((methodOrBlockNumArgs + 1) - i) * BytesPerWord); + (desc->bcptr = startpc); + } + + /* N.B. Includes num args */ + simStackPtr = methodOrBlockNumTemps; + simSpillBase = methodOrBlockNumTemps + 1; +} + + +/* Do not inline (inBlock access) */ + + /* StackToRegisterMappingCogit>>#isNonForwarderReceiver: */ +static NoDbgRegParms sqInt +isNonForwarderReceiver(sqInt reg) +{ + return ((((simSelf())->liveRegister)) == ReceiverResultReg) + && ((inBlock == 0) + && (reg == ReceiverResultReg)); +} + + /* StackToRegisterMappingCogit>>#liveRegisters */ +static sqInt +liveRegisters(void) +{ + sqInt i; + sqInt regsSet; + + if (needsFrame) { + regsSet = 0; + } + else { + regsSet = (1U << ReceiverResultReg); + if ((methodOrBlockNumArgs <= (numRegArgs())) + && (methodOrBlockNumArgs > 0)) { + regsSet = regsSet | ((1U << Arg0Reg)); + if (((numRegArgs()) > 1) + && (methodOrBlockNumArgs > 1)) { + regsSet = regsSet | ((1U << Arg1Reg)); + } + } + } + for (i = (((simSpillBase < 0) ? 0 : simSpillBase)); i <= simStackPtr; i += 1) { + regsSet = regsSet | (registerMask(simStackAt(i))); + } + return regsSet; +} + + +/* insert nops for dead code that is mapped so that bc + to mc mapping is not many to one */ + + /* StackToRegisterMappingCogit>>#mapDeadDescriptorIfNeeded: */ +static NoDbgRegParms sqInt +mapDeadDescriptorIfNeeded(BytecodeDescriptor *descriptor) +{ + AbstractInstruction *abstractInstruction; + + if (((descriptor->isMapped)) + || ((inBlock > 0) + && ((descriptor->isMappedInBlock)))) { + abstractInstruction = gen(Nop); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + } + return 0; +} + + +/* Spill everything on the simulated stack that needs spilling (that below + receiver and arguments). + Marshall receiver and arguments to stack and/or registers depending on arg + count. If the args don't fit in registers push receiver and args (spill + everything), but still assign + the receiver to ReceiverResultReg. */ + + /* StackToRegisterMappingCogit>>#marshallSendArguments: */ +static NoDbgRegParms void +marshallSendArguments(sqInt numArgs) +{ + sqInt anyRefs; + SimStackEntry *cascade0; + sqInt i; + sqInt numSpilled; + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= ((simStackPtr - numArgs) - 1)) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < ((simStackPtr - numArgs) - 1)) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : ((simStackPtr - numArgs) - 1))); i < (simStackPtr - numArgs); i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = ((simStackPtr - numArgs) - 1) + 1; + } + if (numArgs > (numRegArgs())) { + numSpilled = numberOfSpillsInTopNItems(numArgs + 1); + anyRefs = anyReferencesToRegisterinTopNItems(ReceiverResultReg, numArgs + 1); + if ((numSpilled > 0) + || (anyRefs)) { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + storeToReg(simStackAt(simStackPtr - numArgs), ReceiverResultReg); + } + else { + cascade0 = simStackAt(simStackPtr - numArgs); + storeToReg(cascade0, ReceiverResultReg); + (cascade0->type = SSRegister); + (cascade0->registerr = ReceiverResultReg); + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + } + } + else { + if (numArgs > 0) { + if (((numRegArgs()) > 1) + && (numArgs > 1)) { + /* begin ssAllocateRequiredReg:upThrough: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << Arg0Reg), simStackPtr - 2, simNativeStackPtr); + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << Arg1Reg), simStackPtr - 1, simNativeStackPtr); + } + else { + /* begin ssAllocateRequiredReg:upThrough: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << Arg0Reg), simStackPtr - 1, simNativeStackPtr); + } + } + if (((numRegArgs()) > 1) + && (numArgs > 1)) { + popToReg(simStackAt(simStackPtr), Arg1Reg); + } + if (numArgs > 0) { + popToReg(simStackAt((simStackPtr - numArgs) + 1), Arg0Reg); + } + popToReg(simStackAt(simStackPtr - numArgs), ReceiverResultReg); + } + + /* If there are no spills and no references to ReceiverResultReg + the fetch of ReceiverResultReg from the stack can be avoided + by assigning directly to ReceiverResultReg and pushing it. + Move the args to the register arguments, being careful to do + so last to first so e.g. previous contents don't get overwritten. + Also check for any arg registers in use by other args. */ + ssPop(numArgs + 1); +} + + +/* For assert checking; or rather for avoiding assert fails when dealing with + the hack for block temps in the SqueakV3PlusClosures bytecode set. + */ + + /* StackToRegisterMappingCogit>>#maybeCompilingFirstPassOfBlockWithInitialPushNil */ +static sqInt +maybeCompilingFirstPassOfBlockWithInitialPushNil(void) +{ + return (inBlock == InVanillaBlock) + && ((methodOrBlockNumTemps > methodOrBlockNumArgs) + && (compilationPass == 1)); +} + + +/* If this bytecode has a fixup, some kind of merge needs to be done. There + are 4 cases: + 1) the bytecode has no fixup (fixup isNotAFixup) + do nothing + 2) the bytecode has a non merge fixup + the fixup has needsNonMergeFixup. + The code generating non merge fixup (currently only special selector code) + is responsible + for the merge so no need to do it. + We set deadCode to false as the instruction can be reached from jumps. + 3) the bytecode has a merge fixup, but execution flow *cannot* fall + through to the merge point. + the fixup has needsMergeFixup and deadCode = true. + ignores the current simStack as it does not mean anything + restores the simStack to the state the jumps to the merge point expects it + to be. + 4) the bytecode has a merge fixup and execution flow *can* fall through to + the merge point. + the fixup has needsMergeFixup and deadCode = false. + flushes the stack to the stack pointer so the fall through execution path + simStack is + in the state the merge point expects it to be. + restores the simStack to the state the jumps to the merge point expects it + to be. + + In addition, if this is a backjump merge point, we patch the fixup to hold + the current simStackPtr + for later assertions. */ + + /* StackToRegisterMappingCogit>>#mergeWithFixupIfRequired: */ +static NoDbgRegParms sqInt +mergeWithFixupIfRequired(BytecodeFixup *fixup) +{ + SimStackEntry *cascade0; + sqInt i; + + /* begin assertCorrectSimStackPtr */ + assert((simSpillBase >= methodOrBlockNumTemps) + || ((maybeCompilingFirstPassOfBlockWithInitialPushNil()) + && (simSpillBase > methodOrBlockNumArgs))); + if (needsFrame + && (simSpillBase > 0)) { + assert(((((simStackAt(simSpillBase - 1))->spilled)) == 1) + || ((maybeCompilingFirstPassOfBlockWithInitialPushNil()) + && (simSpillBase > methodOrBlockNumArgs))); + assert((simSpillBase > simStackPtr) + || ((((simStackAt(simSpillBase))->spilled)) == 0)); + } + + /* case 1 */ + if (!((fixup->targetInstruction))) { + return 0; + } + + /* case 2 */ + if ((((usqInt)((fixup->targetInstruction)))) == NeedsNonMergeFixupFlag) { + deadCode = 0; + return 0; + } + + /* cases 3 and 4 */ + assert(isMergeFixup(fixup)); + traceMerge(fixup); + if (deadCode) { + assert((((fixup->simStackPtr)) >= methodOrBlockNumTemps) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil())); + simStackPtr = (fixup->simStackPtr); + } + else { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + } + + /* case 3 + Would like to assert fixup simStackPtr >= methodOrBlockNumTemps + but can't because of the initialNils hack. + case 4 + cases 3 and 4 */ + deadCode = 0; + if ((fixup->isTargetOfBackwardBranch)) { + (fixup->simStackPtr = simStackPtr); + } + (fixup->targetInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + assert(simStackPtr == ((fixup->simStackPtr))); + + /* begin restoreSimStackAtMergePoint: */ + /* begin voidReceiverOptStatus */ + ((simSelf())->liveRegister = NoReg); + for (i = (methodOrBlockNumTemps + 1); i <= simStackPtr; i += 1) { + cascade0 = simStackAt(i); + (cascade0->type = SSSpill); + (cascade0->offset = FoxMFReceiver - ((i - methodOrBlockNumArgs) * BytesPerOop)); + (cascade0->registerr = FPReg); + (cascade0->spilled = 1); + } + simSpillBase = simStackPtr + 1; + return 0; +} + + /* StackToRegisterMappingCogit>>#methodAbortTrampolineFor: */ +static NoDbgRegParms sqInt +methodAbortTrampolineFor(sqInt numArgs) +{ + return methodAbortTrampolines[((numArgs < ((numRegArgs()) + 1)) ? numArgs : ((numRegArgs()) + 1))]; +} + + /* StackToRegisterMappingCogit>>#needsFrameIfMod16GENumArgs: */ +static NoDbgRegParms sqInt +needsFrameIfMod16GENumArgs(sqInt stackDelta) +{ + return (byte0 % 16) >= methodOrBlockNumArgs; +} + + +/* As of August 2013, the code generator can't deal with spills in frameless + methods (the + issue is to do with the stack offset to get at an argument, which is + changed when there's a spill). + In e.g. TextColor>>#dominates: other ^other class == self class the second + send of class + needs also rto allocate a register that the first one used, but the first + one's register can't be + spilled. So avoid this by only allowing class to be sent if the stack + contains a single element. */ + + /* StackToRegisterMappingCogit>>#needsFrameIfStackGreaterThanOne: */ +static NoDbgRegParms sqInt +needsFrameIfStackGreaterThanOne(sqInt stackDelta) +{ + return stackDelta > 1; +} + + /* StackToRegisterMappingCogit>>#numberOfSpillsInTopNItems: */ +static NoDbgRegParms sqInt +numberOfSpillsInTopNItems(sqInt n) +{ + sqInt i; + + for (i = simStackPtr; i >= ((simStackPtr - n) + 1); i += -1) { + if ((((simStackAt(i))->type)) == SSSpill) { + return n - (simStackPtr - i); + } + } + return 0; +} + + /* StackToRegisterMappingCogit>>#picAbortTrampolineFor: */ +static NoDbgRegParms sqInt +picAbortTrampolineFor(sqInt numArgs) +{ + return picAbortTrampolines[((numArgs < ((numRegArgs()) + 1)) ? numArgs : ((numRegArgs()) + 1))]; +} + + +/* Try and keep the register contents for ReceiverResultReg if it is top of + stack + */ + + /* StackToRegisterMappingCogit>>#preserveTopInClassRegIfIsReceiverResultReg */ +static void +preserveTopInClassRegIfIsReceiverResultReg(void) +{ + if (((((ssTop())->type)) == SSRegister) + && ((((ssTop())->registerr)) == ReceiverResultReg)) { + /* begin ssAllocateRequiredReg: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << ClassReg), simStackPtr, simNativeStackPtr); + storeToReg(ssTop(), ClassReg); + ((ssTop())->registerr = ClassReg); + } +} + + /* StackToRegisterMappingCogit>>#prevInstIsPCAnnotated */ +static sqInt +prevInstIsPCAnnotated(void) +{ + sqInt prevIndex; + AbstractInstruction *prevInst; + + if (!(opcodeIndex > 0)) { + return 0; + } + prevIndex = opcodeIndex - 1; + while (1) { + if (prevIndex <= 0) { + return 0; + } + prevInst = abstractInstructionAt(prevIndex); + if (((!((prevInst->annotation)) + ? 0 + : (prevInst->annotation))) >= HasBytecodePC) { + return 1; + } + if (!(((prevInst->opcode)) == Label)) break; + prevIndex -= 1; + } + return 0; +} + + +/* Used to mark ReceiverResultReg as dead or not containing simSelf. + Used when the simStack has already been flushed, e.g. for sends. */ + + /* StackToRegisterMappingCogit>>#receiverIsInReceiverResultReg */ +static int +receiverIsInReceiverResultReg(void) +{ + return (((simSelf())->liveRegister)) == ReceiverResultReg; +} + + +/* When a block must be recompiled due to overestimating the + numInitialNils fixups must be restored, which means rescannning + since backward branches need their targets initialized. */ + + /* StackToRegisterMappingCogit>>#reinitializeFixupsFrom:through: */ +static NoDbgRegParms void +reinitializeFixupsFromthrough(sqInt start, sqInt end) +{ + BytecodeDescriptor *descriptor; + sqInt distance; + BytecodeFixup *fixup; + sqInt nExts; + sqInt pc; + BytecodeFixup *self_in_CogSSBytecodeFixup; + sqInt targetPC; + + pc = start; + nExts = 0; + while (pc <= end) { + /* begin fixupAt: */ + self_in_CogSSBytecodeFixup = fixupAtIndex(pc - initialPC); + (self_in_CogSSBytecodeFixup->targetInstruction) = 0; + (self_in_CogSSBytecodeFixup->simStackPtr) = 0; + byte0 = (fetchByteofObject(pc, methodObj)) + bytecodeSetOffset; + descriptor = generatorAt(byte0); + if ((isBranch(descriptor)) + && ((/* begin isBackwardBranch:at:exts:in: */ + assert(((descriptor->spanFunction))), + (((descriptor->spanFunction))(descriptor, pc, nExts, methodObj)) < 0))) { + distance = ((descriptor->spanFunction))(descriptor, pc, nExts, methodObj); + targetPC = (pc + ((descriptor->numBytes))) + distance; + + /* begin initializeFixupAt: */ + /* begin fixupAt: */ + fixup = fixupAtIndex(targetPC - initialPC); + (fixup->targetInstruction) = ((AbstractInstruction *) NeedsMergeFixupFlag); + + /* begin setIsBackwardBranchFixup */ + (fixup->isTargetOfBackwardBranch) = 1; + } + if ((descriptor->isBlockCreation)) { + distance = ((descriptor->spanFunction))(descriptor, pc, nExts, methodObj); + pc = (pc + ((descriptor->numBytes))) + distance; + } + else { + pc += (descriptor->numBytes); + } + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } +} + + +/* Scan the block to determine if the block needs a frame or not */ + + /* StackToRegisterMappingCogit>>#scanBlock: */ +static NoDbgRegParms sqInt +scanBlock(BlockStart *blockStart) +{ + BytecodeDescriptor *descriptor; + sqInt end; + sqInt framelessStackDelta; + sqInt nExts; + sqInt numPushNils; + sqInt (* const numPushNilsFunction)(struct _BytecodeDescriptor *,sqInt,sqInt,sqInt) = squeakV3orSistaV1NumPushNils; + sqInt pc; + sqInt pushingNils; + + needsFrame = 0; + prevBCDescriptor = null; + methodOrBlockNumArgs = (blockStart->numArgs); + inBlock = InVanillaBlock; + pc = (blockStart->startpc); + end = ((blockStart->startpc)) + ((blockStart->span)); + framelessStackDelta = (nExts = (extA = (numExtB = (extB = 0)))); + pushingNils = 1; + while (pc < end) { + descriptor = loadBytesAndGetDescriptor(pc); + if ((descriptor->isExtension)) { + ((descriptor->generator))(); + } + if (!needsFrame) { + if ((!((descriptor->needsFrameFunction))) + || (((descriptor->needsFrameFunction))(framelessStackDelta))) { + needsFrame = 1; + } + else { + framelessStackDelta += (descriptor->stackDelta); + } + } + + /* begin maybeNoteDescriptor:blockStart: */ + if ((descriptor->isInstVarRef)) { + (blockStart->hasInstVarRef = 1); + } + if (pushingNils + && (!((descriptor->isExtension)))) { + /* Count the initial number of pushed nils acting as temp initializers. We can't tell + whether an initial pushNil is an operand reference or a temp initializer, except + when the pushNil is a jump target (has a fixup), which never happens: + self systemNavigation browseAllSelect: + [:m| | ebc | + (ebc := m embeddedBlockClosures + select: [:ea| ea decompile statements first isMessage] + thenCollect: [:ea| ea decompile statements first selector]) notEmpty + and: [(#(whileTrue whileFalse whileTrue: whileFalse:) intersection: ebc) notEmpty]] + or if the bytecode set has a push multiple nils bytecode. We simply count initial nils. + Rarely we may end up over-estimating. We will correct by checking the stack depth + at the end of the block in compileBlockBodies. */ + if (((numPushNils = numPushNilsFunction(descriptor, pc, nExts, methodObj))) > 0) { + assert(((descriptor->numBytes)) == 1); + (blockStart->numInitialNils = ((blockStart->numInitialNils)) + numPushNils); + } + else { + pushingNils = 0; + } + } + pc = (pc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, pc, nExts, methodObj) + : 0)); + if ((descriptor->isExtension)) { + nExts += 1; + } + else { + nExts = (extA = (numExtB = (extB = 0))); + } + prevBCDescriptor = descriptor; + } + + /* It would be nice of this wasn't necessary but alas we need to do the eager + scan for frameless methods so that we don't end up popping too much off + the simulated stack, e.g. for pushNil; returnTopFromBlock methods. */ + if (!needsFrame) { + assert((framelessStackDelta >= 0) + && (((blockStart->numInitialNils)) >= framelessStackDelta)); + (blockStart->numInitialNils = ((blockStart->numInitialNils)) - framelessStackDelta); + } + return 0; +} + + +/* Scan the method (and all embedded blocks) to determine + - what the last bytecode is; extra bytes at the end of a method are used + to encode things like source pointers or temp names + - if the method needs a frame or not + - what are the targets of any backward branches. + - how many blocks it creates + Answer the block count or on error a negative error code */ + + /* StackToRegisterMappingCogit>>#scanMethod */ +static sqInt +scanMethod(void) +{ + BytecodeDescriptor *descriptor; + sqInt distance; + BytecodeFixup *fixup; + sqInt framelessStackDelta; + sqInt latestContinuation; + sqInt nExts; + sqInt numBlocks; + sqInt pc; + sqInt seenInstVarStore; + sqInt targetPC; + + needsFrame = (useTwoPaths = (seenInstVarStore = 0)); + prevBCDescriptor = null; + if ((primitiveIndex > 0) + && (isQuickPrimitiveIndex(primitiveIndex))) { + return 0; + } + pc = (latestContinuation = initialPC); + numBlocks = (framelessStackDelta = (nExts = (extA = (numExtB = (extB = 0))))); + while (pc <= endPC) { + descriptor = loadBytesAndGetDescriptor(pc); + if ((descriptor->isExtension)) { + if (((descriptor->opcode)) == Nop) { + return EncounteredUnknownBytecode; + } + + /* unknown bytecode tag; see Cogit class>>#generatorTableFrom: */ + ((descriptor->generator))(); + } + if (((descriptor->isReturn)) + && (pc >= latestContinuation)) { + endPC = pc; + } + if (!needsFrame) { + if ((!((descriptor->needsFrameFunction))) + || (((descriptor->needsFrameFunction))(framelessStackDelta))) { + /* With immutability we win simply by avoiding a frame build if the receiver is young and not immutable. */ +# if IMMUTABILITY + if ((descriptor->is1ByteInstVarStore)) { + useTwoPaths = 1; + } + else { + needsFrame = 1; + useTwoPaths = 0; + } +# else // IMMUTABILITY + needsFrame = 1; + useTwoPaths = 0; +# endif + + } + else { + framelessStackDelta += (descriptor->stackDelta); + + /* Without immutability we win if there are two or more stores and the receiver is new. */ +# if IMMUTABILITY +# else + if ((descriptor->is1ByteInstVarStore)) { + if (seenInstVarStore) { + useTwoPaths = 1; + } + else { + seenInstVarStore = 1; + } + } +# endif // IMMUTABILITY + + } + } + if (isBranch(descriptor)) { + distance = ((descriptor->spanFunction))(descriptor, pc, nExts, methodObj); + targetPC = (pc + ((descriptor->numBytes))) + distance; + + /* begin isBackwardBranch:at:exts:in: */ + assert(((descriptor->spanFunction))); + if ((((descriptor->spanFunction))(descriptor, pc, nExts, methodObj)) < 0) { + /* begin initializeFixupAt: */ + /* begin fixupAt: */ + fixup = fixupAtIndex(targetPC - initialPC); + (fixup->targetInstruction) = ((AbstractInstruction *) NeedsMergeFixupFlag); + + /* begin setIsBackwardBranchFixup */ + (fixup->isTargetOfBackwardBranch) = 1; + } + else { + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + } + } + if ((descriptor->isBlockCreation)) { + numBlocks += 1; + distance = ((descriptor->spanFunction))(descriptor, pc, nExts, methodObj); + targetPC = (pc + ((descriptor->numBytes))) + distance; + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + } + pc += (descriptor->numBytes); + nExts = ((descriptor->isExtension) + ? nExts + 1 + : (extA = (numExtB = (extB = 0)))); + prevBCDescriptor = descriptor; + } + return numBlocks; +} + + /* StackToRegisterMappingCogit>>#squeakV3orSistaV1PushNilSize:numInitialNils: */ +static NoDbgRegParms sqInt +squeakV3orSistaV1PushNilSizenumInitialNils(sqInt aMethodObj, sqInt numInitialNils) +{ + return (numInitialNils); +} + + /* StackToRegisterMappingCogit>>#squeakV3orSistaV1:Num:Push:Nils: */ +static NoDbgRegParms sqInt +squeakV3orSistaV1NumPushNils(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + return (/* v3:Num:Push:Nils: */ + (((descriptor->generator)) == genPushConstantNilBytecode + ? 1 + : 0)); +} + + /* StackToRegisterMappingCogit>>#ssAllocateRequiredRegMask:upThrough:upThroughNative: */ +static NoDbgRegParms void +ssAllocateRequiredRegMaskupThroughupThroughNative(sqInt requiredRegsMask, sqInt stackPtr, sqInt nativeStackPtr) +{ + sqInt i; + sqInt iSqInt; + sqInt lastRequired; + sqInt lastRequiredNative; + sqInt liveRegs; + + lastRequired = -1; + lastRequiredNative = -1; + + /* compute live regs while noting the last occurrence of required regs. + If these are not free we must spill from simSpillBase to last occurrence. + Note we are conservative here; we could allocate FPReg in frameless methods. */ + liveRegs = (1U << FPReg) | (1U << SPReg); + for (i = (((simSpillBase < 0) ? 0 : simSpillBase)); i <= stackPtr; i += 1) { + liveRegs = liveRegs | (registerMask(simStackAt(i))); + if ((((registerMask(simStackAt(i))) & requiredRegsMask) != 0)) { + lastRequired = i; + } + } + + /* If any of requiredRegsMask are live we must spill. */ + if (((liveRegs & requiredRegsMask) != 0)) { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= lastRequired) { + for (iSqInt = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < lastRequired) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : lastRequired)); iSqInt <= lastRequired; iSqInt += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(iSqInt), frameOffsetOfTemporary(iSqInt - 1), FPReg); + } + simSpillBase = lastRequired + 1; + } + assert(!(((((liveRegisters()) & requiredRegsMask) != 0)))); + } +} + + +/* Any occurrences on the stack of the value being stored (which is the top + of stack) + must be flushed, and hence any values colder than them stack. */ + + /* StackToRegisterMappingCogit>>#ssFlushUpThroughReceiverVariable: */ +static NoDbgRegParms void +ssFlushUpThroughReceiverVariable(sqInt slotIndex) +{ + SimStackEntry *desc; + sqInt i; + sqInt index; + + + /* begin ssFlushUpThrough: */ + assert(simSpillBase >= 0); + for (index = (simStackPtr - 1); index >= simSpillBase; index += -1) { + if (((desc = simStackAt(index)), + (((desc->type)) == SSBaseOffset) + && ((((desc->registerr)) == ReceiverResultReg) + && (((desc->offset)) == (slotOffsetOfInstVarIndex(slotIndex)))))) { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= index) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < index) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : index)); i <= index; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = index + 1; + } + goto l1; + } + } + /* end ssFlushUpThrough: */ +l1:; +} + + +/* Any occurrences on the stack of the value being stored (which is the top + of stack) + must be flushed, and hence any values colder than them stack. */ + + /* StackToRegisterMappingCogit>>#ssFlushUpThroughTemporaryVariable: */ +static NoDbgRegParms void +ssFlushUpThroughTemporaryVariable(sqInt tempIndex) +{ + SimStackEntry *desc; + sqInt i; + sqInt index; + sqInt offset; + + offset = ((simStackAt(tempIndex + 1))->offset); + assert(offset == (frameOffsetOfTemporary(tempIndex))); + + /* begin ssFlushUpThrough: */ + assert(simSpillBase >= 0); + for (index = (simStackPtr - 1); index >= simSpillBase; index += -1) { + if (((desc = simStackAt(index)), + (((desc->type)) == SSBaseOffset) + && ((((desc->registerr)) == FPReg) + && (((desc->offset)) == offset)))) { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= index) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < index) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : index)); i <= index; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = index + 1; + } + goto l1; + } + } + /* end ssFlushUpThrough: */ +l1:; +} + + /* StackToRegisterMappingCogit>>#ssPop: */ +static NoDbgRegParms void +ssPop(sqInt n) +{ + sqInt i; + sqInt toDoLimit; + + assert(((simStackPtr - n) >= methodOrBlockNumTemps) + || (((!needsFrame) + && ((simStackPtr - n) >= 0)) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil()))); + simStackPtr -= n; + + /* begin updateSimSpillBase */ + assert(((simSpillBase > methodOrBlockNumTemps) + && (simStackPtr >= methodOrBlockNumTemps)) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil())); + if (simSpillBase > simStackPtr) { + simSpillBase = simStackPtr + 1; + while (((simSpillBase - 1) > methodOrBlockNumTemps) + && (!(((simStackAt(simSpillBase - 1))->spilled)))) { + simSpillBase -= 1; + } + } + else { + while ((((simStackAt(simSpillBase))->spilled)) + && (simSpillBase <= simStackPtr)) { + simSpillBase += 1; + } + } + toDoLimit = (((simSpillBase - 1) < simStackPtr) ? (simSpillBase - 1) : simStackPtr); + for (i = (methodOrBlockNumTemps + 1); i <= toDoLimit; i += 1) { + assert((((simStackAt(i))->spilled)) == 1); + } + assert((simSpillBase > simStackPtr) + || ((((simStackAt(simSpillBase))->spilled)) == 0)); +} + + /* StackToRegisterMappingCogit>>#ssPushAnnotatedConstant: */ +static NoDbgRegParms sqInt +ssPushAnnotatedConstant(sqInt literal) +{ + AbstractInstruction *abstractInstruction; + + ssPushConstant(literal); + + /* begin annotateInstructionForBytecode */ + abstractInstruction = (prevInstIsPCAnnotated() + ? gen(Nop) + : genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + return 0; +} + + /* StackToRegisterMappingCogit>>#ssPushBase:offset: */ +static NoDbgRegParms sqInt +ssPushBaseoffset(sqInt reg, sqInt offset) +{ + SimStackEntry *cascade0; + sqInt i; + sqInt toDoLimit; + + ssPush(1); + + /* begin ssTop */ + cascade0 = simStackAt(simStackPtr); + (cascade0->type = SSBaseOffset); + (cascade0->spilled = 0); + (cascade0->registerr = reg); + (cascade0->offset = offset); + (cascade0->bcptr = bytecodePC); + + /* begin updateSimSpillBase */ + assert(((simSpillBase > methodOrBlockNumTemps) + && (simStackPtr >= methodOrBlockNumTemps)) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil())); + if (simSpillBase > simStackPtr) { + simSpillBase = simStackPtr + 1; + while (((simSpillBase - 1) > methodOrBlockNumTemps) + && (!(((simStackAt(simSpillBase - 1))->spilled)))) { + simSpillBase -= 1; + } + } + else { + while ((((simStackAt(simSpillBase))->spilled)) + && (simSpillBase <= simStackPtr)) { + simSpillBase += 1; + } + } + toDoLimit = (((simSpillBase - 1) < simStackPtr) ? (simSpillBase - 1) : simStackPtr); + for (i = (methodOrBlockNumTemps + 1); i <= toDoLimit; i += 1) { + assert((((simStackAt(i))->spilled)) == 1); + } + assert((simSpillBase > simStackPtr) + || ((((simStackAt(simSpillBase))->spilled)) == 0)); + return 0; +} + + /* StackToRegisterMappingCogit>>#ssPushConstant: */ +static NoDbgRegParms sqInt +ssPushConstant(sqInt literal) +{ + SimStackEntry *cascade0; + sqInt i; + sqInt toDoLimit; + + ssPush(1); + + /* begin ssTop */ + cascade0 = simStackAt(simStackPtr); + (cascade0->type = SSConstant); + (cascade0->spilled = 0); + (cascade0->constant = literal); + (cascade0->bcptr = bytecodePC); + + /* begin updateSimSpillBase */ + assert(((simSpillBase > methodOrBlockNumTemps) + && (simStackPtr >= methodOrBlockNumTemps)) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil())); + if (simSpillBase > simStackPtr) { + simSpillBase = simStackPtr + 1; + while (((simSpillBase - 1) > methodOrBlockNumTemps) + && (!(((simStackAt(simSpillBase - 1))->spilled)))) { + simSpillBase -= 1; + } + } + else { + while ((((simStackAt(simSpillBase))->spilled)) + && (simSpillBase <= simStackPtr)) { + simSpillBase += 1; + } + } + toDoLimit = (((simSpillBase - 1) < simStackPtr) ? (simSpillBase - 1) : simStackPtr); + for (i = (methodOrBlockNumTemps + 1); i <= toDoLimit; i += 1) { + assert((((simStackAt(i))->spilled)) == 1); + } + assert((simSpillBase > simStackPtr) + || ((((simStackAt(simSpillBase))->spilled)) == 0)); + return 0; +} + + /* StackToRegisterMappingCogit>>#ssPushDesc: */ +static NoDbgRegParms sqInt +ssPushDesc(SimStackEntry simStackEntry) +{ + sqInt i; + sqInt toDoLimit; + + if (((simStackEntry.type)) == SSSpill) { + (simStackEntry.type = SSBaseOffset); + } + (simStackEntry.spilled = 0); + (simStackEntry.bcptr = bytecodePC); + simStack[(simStackPtr += 1)] = simStackEntry; + + /* begin updateSimSpillBase */ + assert(((simSpillBase > methodOrBlockNumTemps) + && (simStackPtr >= methodOrBlockNumTemps)) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil())); + if (simSpillBase > simStackPtr) { + simSpillBase = simStackPtr + 1; + while (((simSpillBase - 1) > methodOrBlockNumTemps) + && (!(((simStackAt(simSpillBase - 1))->spilled)))) { + simSpillBase -= 1; + } + } + else { + while ((((simStackAt(simSpillBase))->spilled)) + && (simSpillBase <= simStackPtr)) { + simSpillBase += 1; + } + } + toDoLimit = (((simSpillBase - 1) < simStackPtr) ? (simSpillBase - 1) : simStackPtr); + for (i = (methodOrBlockNumTemps + 1); i <= toDoLimit; i += 1) { + assert((((simStackAt(i))->spilled)) == 1); + } + assert((simSpillBase > simStackPtr) + || ((((simStackAt(simSpillBase))->spilled)) == 0)); + return 0; +} + + /* StackToRegisterMappingCogit>>#ssPushRegister: */ +static NoDbgRegParms sqInt +ssPushRegister(sqInt reg) +{ + SimStackEntry *cascade0; + sqInt i; + sqInt toDoLimit; + + ssPush(1); + + /* begin ssTop */ + cascade0 = simStackAt(simStackPtr); + (cascade0->type = SSRegister); + (cascade0->spilled = 0); + (cascade0->registerr = reg); + (cascade0->bcptr = bytecodePC); + + /* begin updateSimSpillBase */ + assert(((simSpillBase > methodOrBlockNumTemps) + && (simStackPtr >= methodOrBlockNumTemps)) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil())); + if (simSpillBase > simStackPtr) { + simSpillBase = simStackPtr + 1; + while (((simSpillBase - 1) > methodOrBlockNumTemps) + && (!(((simStackAt(simSpillBase - 1))->spilled)))) { + simSpillBase -= 1; + } + } + else { + while ((((simStackAt(simSpillBase))->spilled)) + && (simSpillBase <= simStackPtr)) { + simSpillBase += 1; + } + } + toDoLimit = (((simSpillBase - 1) < simStackPtr) ? (simSpillBase - 1) : simStackPtr); + for (i = (methodOrBlockNumTemps + 1); i <= toDoLimit; i += 1) { + assert((((simStackAt(i))->spilled)) == 1); + } + assert((simSpillBase > simStackPtr) + || ((((simStackAt(simSpillBase))->spilled)) == 0)); + return 0; +} + + /* StackToRegisterMappingCogit>>#ssPush: */ +static NoDbgRegParms void +ssPush(sqInt n) +{ + simStackPtr += n; +} + + /* StackToRegisterMappingCogit>>#ssSelfDescriptor */ +static SimStackEntry +ssSelfDescriptor(void) +{ + return simStack[0]; +} + + +/* In addition to ssStorePop:toReg:, if this is a store and not + a popInto I change the simulated stack to use the register + for the top value */ + + /* StackToRegisterMappingCogit>>#ssStoreAndReplacePop:toReg: */ +static NoDbgRegParms void +ssStoreAndReplacePoptoReg(sqInt popBoolean, sqInt reg) +{ + char topSpilled; + + topSpilled = ((ssTop())->spilled); + ssStorePoptoReg(popBoolean + || (topSpilled), reg); + if (!popBoolean) { + if (!topSpilled) { + ssPop(1); + } + ssPushRegister(reg); + } +} + + +/* Store or pop the top simulated stack entry to a register. + Use preferredReg if the entry is not itself a register. + Answer the actual register the result ends up in. */ + + /* StackToRegisterMappingCogit>>#ssStorePop:toPreferredReg: */ +static NoDbgRegParms sqInt +ssStorePoptoPreferredReg(sqInt popBoolean, sqInt preferredReg) +{ + sqInt actualReg; + + actualReg = preferredReg; + if ((((ssTop())->type)) == SSRegister) { + assert(!(((ssTop())->spilled))); + actualReg = ((ssTop())->registerr); + } + ssStorePoptoReg(popBoolean, actualReg); + return actualReg; +} + + +/* Store or pop the top simulated stack entry to a register. + N.B.: popToReg: and storeToReg: does not generate anything if + it moves a register to the same register. */ + + /* StackToRegisterMappingCogit>>#ssStorePop:toReg: */ +static NoDbgRegParms void +ssStorePoptoReg(sqInt popBoolean, sqInt reg) +{ + if (popBoolean) { + popToReg(ssTop(), reg); + ssPop(1); + } + else { + storeToReg(ssTop(), reg); + } +} + + /* StackToRegisterMappingCogit>>#ssTop */ +static SimStackEntry * +ssTop(void) +{ + return simStackAt(simStackPtr); +} + + /* StackToRegisterMappingCogit>>#ssValue: */ +static NoDbgRegParms SimStackEntry * +ssValue(sqInt n) +{ + return simStackAt(simStackPtr - n); +} + + /* StackToRegisterMappingCogit>>#stackEntryIsBoolean: */ +static NoDbgRegParms sqInt +stackEntryIsBoolean(SimStackEntry *simStackEntry) +{ + return (((simStackEntry->type)) == SSConstant) + && ((((simStackEntry->constant)) == (trueObject())) + || (((simStackEntry->constant)) == (falseObject()))); +} + + +/* Answer if the stack is valid up to, but not including, simSpillBase. */ + + /* StackToRegisterMappingCogit>>#tempsValidAndVolatileEntriesSpilled */ +static sqInt +tempsValidAndVolatileEntriesSpilled(void) +{ + sqInt culprit; + sqInt i; + + culprit = 0; + for (i = 1; i <= methodOrBlockNumTemps; i += 1) { + if (!(((((simStackAt(i))->type)) == SSBaseOffset) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil()))) { + if (!culprit) { + culprit = i; + } + return 0; + } + } + for (i = (methodOrBlockNumTemps + 1); i < simSpillBase; i += 1) { + if (!(((simStackAt(i))->spilled))) { + if (!culprit) { + culprit = i; + } + return 0; + } + } + return 1; +} + + +/* If the sequence of bytecodes is + push: (Array new: 1) + popIntoTemp: tempIndex + pushConstant: const or pushTemp: n + popIntoTemp: 0 inVectorAt: tempIndex + collapse this into + tempAt: tempIndex put: {const or temp} + and answer true, otherwise answer false. + One might think that we should look for a sequence of more than + one pushes and pops but this is extremely rare. + Exclude pushRcvr: n to avoid potential complications with context inst + vars. */ + + /* StackToRegisterMappingCogit>>#tryCollapseTempVectorInitializationOfSize: */ +static NoDbgRegParms sqInt +tryCollapseTempVectorInitializationOfSize(sqInt slots) +{ + sqInt pc; + BytecodeDescriptor *pushArrayDesc; + BytecodeDescriptor *pushValueDesc; + sqInt reg; + sqInt remoteTempIndex; + BytecodeDescriptor *storeArrayDesc; + BytecodeDescriptor *storeValueDesc; + usqInt tempIndex; + + if (slots != 1) { + return 0; + } + + /* begin generatorForPC: */ + pushArrayDesc = generatorAt(bytecodeSetOffset + (fetchByteofObject(bytecodePC, methodObj))); + assert(((pushArrayDesc->generator)) == genPushNewArrayBytecode); + pc = bytecodePC + ((pushArrayDesc->numBytes)); + + /* begin generatorForPC: */ + storeArrayDesc = generatorAt(bytecodeSetOffset + (fetchByteofObject(pc, methodObj))); + if (((storeArrayDesc->generator)) == genStoreAndPopTemporaryVariableBytecode) { + tempIndex = (fetchByteofObject(bytecodePC + ((pushArrayDesc->numBytes)), methodObj)) & 7; + } + else { + if (!(((storeArrayDesc->generator)) == genLongStoreAndPopTemporaryVariableBytecode)) { + return 0; + } + tempIndex = fetchByteofObject((bytecodePC + ((pushArrayDesc->numBytes))) + 1, methodObj); + } + pc = (bytecodePC + ((pushArrayDesc->numBytes))) + ((storeArrayDesc->numBytes)); + + /* begin generatorForPC: */ + pushValueDesc = generatorAt(bytecodeSetOffset + (fetchByteofObject(pc, methodObj))); + if (!((((pushValueDesc->generator)) == genPushLiteralConstantBytecode) + || ((((pushValueDesc->generator)) == genPushQuickIntegerConstantBytecode) + || (((pushValueDesc->generator)) == genPushTemporaryVariableBytecode)))) { + return 0; + } + pc = ((bytecodePC + ((pushArrayDesc->numBytes))) + ((storeArrayDesc->numBytes))) + ((pushValueDesc->numBytes)); + + /* begin generatorForPC: */ + storeValueDesc = generatorAt(bytecodeSetOffset + (fetchByteofObject(pc, methodObj))); + remoteTempIndex = fetchByteofObject((((bytecodePC + ((pushArrayDesc->numBytes))) + ((storeArrayDesc->numBytes))) + ((pushValueDesc->numBytes))) + 2, methodObj); + if (!((((storeValueDesc->generator)) == genStoreAndPopRemoteTempLongBytecode) + && (tempIndex == remoteTempIndex))) { + return 0; + } + genNewArrayOfSizeinitialized(1, 0); + evaluateat(pushValueDesc, (bytecodePC + ((pushArrayDesc->numBytes))) + ((storeArrayDesc->numBytes))); + reg = ssStorePoptoPreferredReg(1, TempReg); + genStoreSourceRegslotIndexintoNewObjectInDestReg(reg, 0, ReceiverResultReg); + ssPushRegister(ReceiverResultReg); + evaluateat(storeArrayDesc, bytecodePC + ((pushArrayDesc->numBytes))); + bytecodePC = ((bytecodePC + ((storeArrayDesc->numBytes))) + ((pushValueDesc->numBytes))) + ((storeValueDesc->numBytes)); + + /* + pushArrayDesc numBytes this gets added by nextBytecodePCFor:at:exts:in: */ + return 1; +} + + /* StackToRegisterMappingCogit>>#violatesEnsureSpilledSpillAssert */ +static sqInt +violatesEnsureSpilledSpillAssert(void) +{ + return 1; +} + + +/* Used when ReceiverResultReg is allocated for other than simSelf, and + there may be references to ReceiverResultReg which need to be spilled. */ + + /* StackToRegisterMappingCogit>>#voidReceiverResultRegContainsSelf */ +static void +voidReceiverResultRegContainsSelf(void) +{ + sqInt i; + sqInt iSqInt; + sqInt spillIndex; + + /* begin voidReceiverOptStatus */ + ((simSelf())->liveRegister = NoReg); + spillIndex = 0; + for (i = ((((methodOrBlockNumTemps + 1) < simSpillBase) ? simSpillBase : (methodOrBlockNumTemps + 1))); i <= simStackPtr; i += 1) { + if ((registerOrNone(simStackAt(i))) == ReceiverResultReg) { + spillIndex = i; + } + } + if (spillIndex > 0) { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= spillIndex) { + for (iSqInt = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < spillIndex) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : spillIndex)); iSqInt <= spillIndex; iSqInt += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(iSqInt), frameOffsetOfTemporary(iSqInt - 1), FPReg); + } + simSpillBase = spillIndex + 1; + } + } +} diff --git a/src/spur64.cog.livetyping/cogitX64WIN64.c b/src/spur64.cog.livetyping/cogitX64WIN64.c new file mode 100644 index 0000000000..944058e955 --- /dev/null +++ b/src/spur64.cog.livetyping/cogitX64WIN64.c @@ -0,0 +1,35675 @@ +/* Automatically generated by + CCodeGenerator * VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba + (Cog-eem.511, Compiler-ct.535) + from + LiveTypingCogit * VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba + */ +static char __buildInfo[] = "LiveTypingCogit * VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba " __DATE__ ; +char *__cogitBuildInfo = __buildInfo; + + + +#include "sqConfig.h" +#include +#include +#include +#include +#include "sqPlatformSpecific.h" +#include "sqMemoryAccess.h" +#include "sqCogStackAlignment.h" +#include "dispdbg.h" +#include "cogmethod.h" +#include "cointerp.h" +#include "cogit.h" + + +/*** Constants ***/ +#define ABICalleeSavedRegisterMask 0xF0C8 +#define ABICallerSavedRegisterMask 0xF07 +#define ABIResultReg 0 +#define AddCqR 106 +#define AddCwR 114 +#define AddcCqR 120 +#define AddcRR 119 +#define AddRdRd 132 +#define AddRR 100 +#define AddRsRs 139 +#define AlignmentNops 3 +#define AltBlockCreationBytecodeSize 3 +#define AltFirstSpecialSelector 96 +#define AltNumSpecialSelectors 32 +#define AndCqR 108 +#define AndCwR 116 +#define AndRR 102 +#define AnnotationShift 5 +#define Arg0Reg 1 +#define Arg1Reg 2 +#define ArithmeticShiftRightCqR 91 +#define ArithmeticShiftRightRR 92 +#define BadRegisterSet 1 +#define BlockCreationBytecodeSize 4 +#define BSR 169 +#define BytecodeSetHasDirectedSuperSend 1 +#define CArg0Reg 1 +#define CArg1Reg 2 +#define CArg2Reg 8 +#define CArg3Reg 9 +#define Call 6 +#define CallerSavedRegisterMask 0x706 +#define CallFull 7 +#define CallR 8 +#define CDQ 161 +#if !defined(CheckRememberedInTrampoline) /* Allow this to be overridden on the compiler command line */ +# define CheckRememberedInTrampoline 0 +#endif +#define CLD 165 +#define ClassArray 7 +#define ClassArrayCompactIndex 51 +#define ClassBlockClosureCompactIndex 37 +#define ClassFloatCompactIndex 34 +#define ClassFullBlockClosureCompactIndex 38 +#define ClassLargeNegativeIntegerCompactIndex 32 +#define ClassLargePositiveIntegerCompactIndex 33 +#define ClassMethodContextCompactIndex 36 +#define ClassPointCompactIndex 54 +#define ClassReg 8 +#define ClosureFirstCopiedValueIndex 3 +#define ClosureIndex 4 +#define ClosureNumArgsIndex 2 +#define ClosureOuterContextIndex 0 +#define ClosureStartPCIndex 1 +#define ClzRR 157 +#define CMBlock 4 +#define CMClosedPIC 2 +#define CMFree 1 +#define CMMaxUsageCount 7 +#define CMMethod 5 +#define CMMethodFlaggedForBecome 6 +#define CMOpenPIC 3 +#define CmpC32R 113 +#define CmpCqR 105 +#define CmpCwR 112 +#define CmpRdRd 131 +#define CmpRR 99 +#define CmpRsRs 138 +#define CompletePrimitive 4 +#define ConstZero 1 +#define ConvertRdR 146 +#define ConvertRdRs 148 +#define ConvertRRd 145 +#define ConvertRRs 150 +#define ConvertRsR 149 +#define ConvertRsRd 147 +#define CPUID 160 +#if !defined(Debug) /* Allow this to be overridden on the compiler command line */ +# define Debug DEBUGVM +#endif +#define DisplacementMask 0x1F +#define DisplacementX2N 0 +#define DivRdRd 135 +#define DivRsRs 142 +#define DPFPReg0 0 +#define DPFPReg1 1 +#define DPFPReg2 2 +#define DPFPReg3 3 +#define DPFPReg4 4 +#define DPFPReg5 5 +#define DPFPReg6 6 +#define DPFPReg7 7 +#define EncounteredUnknownBytecode -6 +#define Extra0Reg 7 +#define Extra1Reg 6 +#define Extra2Reg 12 +#define Extra3Reg 13 +#define Extra4Reg 14 +#define Extra5Reg 15 +#define Fill32 4 +#define FirstAnnotation 64 +#define FirstJump 12 +#define FirstShortJump 16 +#define FirstSpecialSelector 176 +#define FoxCallerSavedIP 8 +#define FoxIFSavedIP -32 +#define FoxMethod -8 +#define FoxMFReceiver -24 +#define FoxSavedFP 0 +#define FoxThisContext -16 +#define FPReg 5 +#define FullClosureCompiledBlockIndex 1 +#define FullClosureFirstCopiedValueIndex 4 +#define FullClosureReceiverIndex 3 +#define GCModeBecome 8 +#define GCModeFull 1 +#define GCModeNewSpace 2 +#define HasBytecodePC 5 +#define HashMultiplyConstant 1664525 +#define HashMultiplyMask 0xFFFFFFF +#define HeaderIndex 0 +#define IDIVR 162 +#if !defined(IMMUTABILITY) /* Allow this to be overridden on the compiler command line */ +# define IMMUTABILITY 0 +#endif +#define IMULRR 163 +#define InFullBlock 2 +#define InstanceSpecificationIndex 2 +#define InstructionPointerIndex 1 +#define InsufficientCodeSpace -2 +#define InVanillaBlock 1 +#define IsAbsPCReference 3 +#define IsAnnotationExtension 1 +#define IsDirectedSuperBindingSend 10 +#define IsDirectedSuperSend 9 +#define IsDisplacementX2N 0 +#define IsNSSendCall null +#define IsObjectReference 2 +#define IsRelativeCall 4 +#define IsSendCall 7 +#define IsSuperSend 8 +#define Jump 16 +#define JumpAbove 33 +#define JumpAboveOrEqual 32 +#define JumpBelow 31 +#define JumpBelowOrEqual 34 +#define JumpCarry 25 +#define JumpFPEqual 35 +#define JumpFPGreater 39 +#define JumpFPGreaterOrEqual 40 +#define JumpFPLess 37 +#define JumpFPLessOrEqual 38 +#define JumpFPNotEqual 36 +#define JumpFPOrdered 41 +#define JumpFPUnordered 42 +#define JumpFull 12 +#define JumpGreater 29 +#define JumpGreaterOrEqual 28 +#define JumpLess 27 +#define JumpLessOrEqual 30 +#define JumpLong 13 +#define JumpLongNonZero 15 +#define JumpLongZero 14 +#define JumpNegative 19 +#define JumpNoCarry 26 +#define JumpNonNegative 20 +#define JumpNonZero 18 +#define JumpNoOverflow 22 +#define JumpOverflow 21 +#define JumpR 10 +#define JumpZero 17 +#define Label 1 +#define LargeContextSlots 62 +#define LastJump 42 +#define Literal 2 +#define LiteralStart 1 +#define LoadEffectiveAddressMwrR 88 +#define LogicalShiftLeftCqR 95 +#define LogicalShiftLeftRR 96 +#define LogicalShiftRightCqR 93 +#define LogicalShiftRightRR 94 +#define MapEnd 0 +#define MaxCompiledPrimitiveIndex 582 +#define MaxCPICCases 6 +#define MaxMethodSize 65535 +#define MaxNegativeErrorCode -8 +#define MaxStackAllocSize 1572864 +#define MaxStackCheckOffset 0xFFF +#define MaxX2NDisplacement 992 +#define MethodCacheClass 2 +#define MethodCacheMask 0xFFC +#define MethodCacheMethod 3 +#define MethodCacheSelector 1 +#define MethodIndex 3 +#define MethodTooBig -4 +#define MFMethodFlagHasContextFlag 1 +#define MFMethodFlagIsBlockFlag 2 +#define MOVSB 167 +#define MOVSQ 168 +#define ModReg 3 +#define ModRegInd 0 +#define ModRegRegDisp32 2 +#define ModRegRegDisp8 1 +#define MoveA32R 45 +#define MoveAbR 48 +#define MoveAwR 44 +#define MoveC32R 71 +#define MoveCqR 69 +#define MoveCwR 70 +#define MoveM16rR 57 +#define MoveM32rR 61 +#define MoveM32rRs 78 +#define MoveM64rRd 75 +#define MoveM8rR 54 +#define MoveMbrR 65 +#define MoveMs8rR 55 +#define MoveMwrR 50 +#define MovePerfCnt64RL 159 +#define MoveRA32 47 +#define MoveRAb 49 +#define MoveRAw 46 +#define MoveRdM64r 76 +#define MoveRdR 73 +#define MoveRdRd 74 +#define MoveRM16r 58 +#define MoveRM32r 62 +#define MoveRM8r 56 +#define MoveRMbr 66 +#define MoveRMwr 51 +#define MoveRR 43 +#define MoveRRd 72 +#define MoveRsM32r 79 +#define MoveRsRs 77 +#define MoveRX32rR 64 +#define MoveRXbrR 68 +#define MoveRXwrR 53 +#define MoveX32rRR 63 +#define MoveXbrRR 67 +#define MoveXwrRR 52 +#define MulRdRd 134 +#define MulRsRs 141 +#define NativeSPReg 4 +#define NeedsMergeFixupFlag 2 +#define NeedsNonMergeFixupFlag 1 +#define NegateR 89 +#define Nop 5 +#define NoReg -1 +#define NotFullyInitialized -1 +#define NumObjRefsInRuntime 0 +#define NumSendTrampolines 4 +#define NumSpecialSelectors 32 +#define NumStoreTrampolines 5 +#define NumTrampolines (75 + (IMMUTABILITY ? 5 : 0)) +#define OrCqR 109 +#define OrCwR 117 +#define OrRR 103 +#define PopR 80 +#define PrefetchAw 87 +#define PrimCallCollectsProfileSamples 16 +#define PrimCallIsExternalCall 32 +#define PrimCallMayEndureCodeCompaction 8 +#define PrimCallNeedsNewMethod 4 +#define PrimCallOnSmalltalkStack 1 +#define PrimCallOnSmalltalkStackAlign2x 2 +#define PrimNumberExternalCall 117 +#define PrimNumberFFICall 120 +#define PushCq 82 +#define PushCw 83 +#define PushR 81 +#define R12 12 +#define R13 13 +#define R15 15 +#define RAX 0 +#define RBP 5 +#define RBX 3 +#define RCX 1 +#define RDI 7 +#define RDX 2 +#define REP 166 +#define ReceiverIndex 5 +#define ReceiverResultReg 9 +#define RetN 9 +#define RISCTempReg 11 +#define RotateLeftCqR 97 +#define RotateRightCqR 98 +#define RSI 6 +#define RSP 4 +#define SelectorCannotInterpret 34 +#define SelectorDoesNotUnderstand 20 +#define SenderIndex 0 +#define SendNumArgsReg 10 +#define ShouldNotJIT -8 +#define SIB1 0 +#define SIB4 2 +#define SIB8 3 +#define SignExtend16RR 152 +#define SignExtend32RR 153 +#define SignExtend8RR 151 +#define SmallContextSlots 22 +#define SPReg 4 +#define SpecialSelectors 23 +#define SqrtRd 136 +#define SqrtRs 143 +#define SSBaseOffset 1 +#define SSConstant 2 +#define SSRegister 3 +#define SSSpill 4 +#define StackPointerIndex 2 +#define Stop 11 +#define SubbCqR 122 +#define SubbRR 121 +#define SubCqR 107 +#define SubCwR 115 +#define SubRdRd 133 +#define SubRR 101 +#define SubRsRs 140 +#define TempReg 0 +#define TstCqR 110 +#define UnfailingPrimitive 3 +#define UnimplementedPrimitive -7 +#define ValueIndex 1 +#define VarBaseReg 3 +#define XCHGRR 164 +#define XMM0L 0 +#define XorCqR 111 +#define XorCwR 118 +#define XorRdRd 137 +#define XorRR 104 +#define XorRsRs 144 +#define YoungSelectorInPIC -5 +#define ZeroExtend16RR 155 +#define ZeroExtend32RR 156 +#define ZeroExtend8RR 154 + +typedef struct _AbstractInstruction { + unsigned char opcode; + unsigned char machineCodeSize; + unsigned char maxSize; + unsigned char annotation; + usqInt operands[3]; + usqInt address; + struct _AbstractInstruction *dependent; + unsigned char machineCode[14]; + } AbstractInstruction; + +#define CogInLineLiteralsX64Compiler AbstractInstruction +#define CogX64Compiler AbstractInstruction +#define CogAbstractInstruction AbstractInstruction + + +typedef struct { + AbstractInstruction *fakeHeader; + AbstractInstruction *fillInstruction; + sqInt numArgs; + sqInt numCopied; + sqInt numInitialNils; + sqInt startpc; + AbstractInstruction *entryLabel; + AbstractInstruction *stackCheckLabel; + sqInt span; + sqInt hasInstVarRef; + } BlockStart; + +#define CogBlockStart BlockStart + + +typedef struct _BytecodeDescriptor { + sqInt (*generator)(void); + sqInt NoDbgRegParms (*spanFunction)(struct _BytecodeDescriptor *,sqInt,sqInt,sqInt); + sqInt NoDbgRegParms (*needsFrameFunction)(sqInt); + signed char stackDelta; + unsigned char opcode; + unsigned char numBytes; + unsigned isBranchTrue : 1; + unsigned isBranchFalse : 1; + unsigned isReturn : 1; + unsigned isBlockCreation : 1; + unsigned isMapped : 1; + unsigned isMappedInBlock : 1; + unsigned isExtension : 1; + unsigned isInstVarRef : 1; + unsigned is1ByteInstVarStore : 1; + unsigned hasUnsafeJump : 1; + } BytecodeDescriptor; + +#define CogBytecodeDescriptor BytecodeDescriptor + + +typedef struct { + sqInt (*primitiveGenerator)(void); + sqInt primNumArgs; + } PrimitiveDescriptor; + +#define CogPrimitiveDescriptor PrimitiveDescriptor + + +typedef struct { + char type; + char spilled; + signed char liveRegister; + signed char registerr; + sqInt offset; + sqInt constant; + sqInt bcptr; + } SimStackEntry; + +#define CogSimStackEntry SimStackEntry + + +typedef struct { + AbstractInstruction *targetInstruction; + unsigned char simStackPtr; + char isTargetOfBackwardBranch; + unsigned short instructionIndex; + } BytecodeFixup; + +#define CogSSBytecodeFixup BytecodeFixup +#define CogBytecodeFixup BytecodeFixup + + +typedef struct { + sqInt isReceiverResultRegLive; + SimStackEntry *ssEntry; + } CogSSOptStatus; + + + +/*** Function Prototypes ***/ + + +#if !PRODUCTION && defined(PlatformNoDbgRegParms) +# define NoDbgRegParms PlatformNoDbgRegParms +#endif + +#if !defined(NoDbgRegParms) +# define NoDbgRegParms /*empty*/ +#endif + + + +#if !defined(NeverInline) +# define NeverInline /*empty*/ +#endif + +static NoDbgRegParms AbstractInstruction * addDependent(AbstractInstruction *self_in_CogAbstractInstruction, AbstractInstruction *anInstruction); +static NoDbgRegParms sqInt availableFloatRegisterOrNoneFor(AbstractInstruction *self_in_CogAbstractInstruction, sqInt liveRegsMask); +static NoDbgRegParms AbstractInstruction * cloneLiteralFrom(AbstractInstruction *self_in_CogAbstractInstruction, AbstractInstruction *existingLiteral); +static NoDbgRegParms sqInt concretizeAt(AbstractInstruction *self_in_CogAbstractInstruction, sqInt actualAddress); +static NoDbgRegParms sqInt genLoadCStackPointer(AbstractInstruction *self_in_CogAbstractInstruction); +static NoDbgRegParms sqInt genLoadCStackPointers(AbstractInstruction *self_in_CogAbstractInstruction); +static NoDbgRegParms sqInt genLoadStackPointerForPrimCall(AbstractInstruction *self_in_CogAbstractInstruction, sqInt spareReg); +static NoDbgRegParms sqInt genLoadStackPointers(AbstractInstruction *self_in_CogAbstractInstruction); +static NoDbgRegParms sqInt genLoadStackPointersForPrimCall(AbstractInstruction *self_in_CogAbstractInstruction, sqInt spareReg); +static NoDbgRegParms sqInt genSaveStackPointers(AbstractInstruction *self_in_CogAbstractInstruction); +static NoDbgRegParms AbstractInstruction * genWriteCResultIntoReg(AbstractInstruction *self_in_CogAbstractInstruction, sqInt abstractRegister); +static NoDbgRegParms sqInt has64BitPerformanceCounter(AbstractInstruction *self_in_CogAbstractInstruction); +static NoDbgRegParms AbstractInstruction * initializeSharableLiteral(AbstractInstruction *self_in_CogAbstractInstruction, sqInt literal); +static NoDbgRegParms AbstractInstruction * initializeUniqueLiteral(AbstractInstruction *self_in_CogAbstractInstruction, sqInt literal); +static NoDbgRegParms sqInt instructionIsPCRelativeLoad(AbstractInstruction *self_in_CogAbstractInstruction, sqInt instr); +static NoDbgRegParms int isJump(AbstractInstruction *self_in_CogAbstractInstruction); +static NoDbgRegParms sqInt perfCounterRatePerUsec(AbstractInstruction *self_in_CogAbstractInstruction); +static NoDbgRegParms AbstractInstruction * relocateJumpLongBeforeFollowingAddressby(AbstractInstruction *self_in_CogAbstractInstruction, sqInt pc, sqInt delta); +static NoDbgRegParms AbstractInstruction * relocateJumpLongConditionalBeforeFollowingAddressby(AbstractInstruction *self_in_CogAbstractInstruction, sqInt pc, sqInt delta); +static NoDbgRegParms AbstractInstruction * resolveJumpTarget(AbstractInstruction *self_in_CogAbstractInstruction); +static NoDbgRegParms sqInt rewriteConditionalJumpLongAttarget(AbstractInstruction *self_in_CogAbstractInstruction, sqInt callSiteReturnAddress, sqInt callTargetAddress); +static NoDbgRegParms sqInt widthOfPCRelativeLoad(AbstractInstruction *self_in_CogAbstractInstruction, sqInt instr); +static NoDbgRegParms CogMethod * cmHomeMethod(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMBlock(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMClosedPIC(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMFree(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMMethodEtAl(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMOpenPIC(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms sqInt isBranch(BytecodeDescriptor *self_in_CogBytecodeDescriptor); +static NoDbgRegParms sqInt isConditionalBranch(BytecodeDescriptor *self_in_CogBytecodeDescriptor); +static NoDbgRegParms int computeSizeOfArithCqR(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler); +static NoDbgRegParms int computeSizeOfArithCwR(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler); +static NoDbgRegParms sqInt concretizeArithCwR(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt x64opcode); +static NoDbgRegParms sqInt concretizeMoveCwR(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler); +static NoDbgRegParms unsigned int inlineCacheTagAt(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt callSiteReturnAddress); +static NoDbgRegParms sqInt isPCDependent(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler); +static NoDbgRegParms unsigned int literal32BeforeFollowingAddress(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt followingAddress); +static NoDbgRegParms sqInt literalBeforeFollowingAddress(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt followingAddress); +static NoDbgRegParms sqInt loadLiteralByteSize(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler); +static NoDbgRegParms unsigned char sizePCDependentInstructionAt(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt eventualAbsoluteAddress); +static NoDbgRegParms AbstractInstruction * storeLiteralbeforeFollowingAddress(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt literal, sqInt followingAddress); +static NoDbgRegParms AbstractInstruction * gAddCqR(sqInt quickConstant, sqInt reg); +static NoDbgRegParms AbstractInstruction * gAddCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg); +static NoDbgRegParms AbstractInstruction * gAndCqR(sqInt quickConstant, sqInt reg); +static NoDbgRegParms AbstractInstruction * gAndCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg); +static NoDbgRegParms AbstractInstruction * gArithmeticShiftRightCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg); +extern sqInt abortOffset(void); +static NoDbgRegParms int abstractInstructionfollows(AbstractInstruction *theAbstractInstruction, AbstractInstruction *anAbstractInstruction); +static void addCleanBlockStarts(void); +extern void addCogMethodsToHeapMap(void); +static NoDbgRegParms sqInt addressIsInFixups(BytecodeFixup *address); +static NoDbgRegParms sqInt addressOfEndOfCaseinCPIC(sqInt n, CogMethod *cPIC); +static void alignMethodZoneBase(void); +static NoDbgRegParms sqInt alignUptoRoutineBoundary(sqInt anAddress); +static sqInt allMachineCodeObjectReferencesValid(void); +static sqInt allMethodsHaveCorrectHeader(void); +static NoDbgRegParms AbstractInstruction * annotateAbsolutePCRef(AbstractInstruction *abstractInstruction); +static NoDbgRegParms AbstractInstruction * annotateBytecode(AbstractInstruction *abstractInstruction); +static NoDbgRegParms AbstractInstruction * annotateobjRef(AbstractInstruction *abstractInstruction, sqInt anOop); +static NoDbgRegParms void assertSaneJumpTarget(AbstractInstruction *jumpTarget); +static NoDbgRegParms sqInt availableRegisterOrNoneIn(sqInt liveRegsMask); +static NoDbgRegParms usqInt blockDispatchTargetsForperformarg(CogMethod *cogMethod, usqInt (*binaryFunction)(sqInt mcpc, sqInt arg), sqInt arg); +extern sqInt bytecodePCForstartBcpcin(sqInt mcpc, sqInt startbcpc, CogBlockMethod *cogMethod); +static NoDbgRegParms AbstractInstruction * gCall(sqInt callTarget); +static NoDbgRegParms AbstractInstruction * gCmpCqR(sqInt quickConstant, sqInt reg); +static void callCogCodePopReceiver(void); +static void callCogCodePopReceiverAndClassRegs(void); +static NoDbgRegParms sqInt ceCPICMissreceiver(CogMethod *cPIC, sqInt receiver); +static NoDbgRegParms sqInt ceSICMiss(sqInt receiver); +static NoDbgRegParms sqInt checkIfValidOopRefAndTargetpccogMethod(sqInt annotation, char *mcpc, CogMethod *cogMethod); +static NoDbgRegParms sqInt checkIfValidOopRefpccogMethod(sqInt annotation, char *mcpc, CogMethod *cogMethod); +extern sqInt checkIntegrityOfObjectReferencesInCode(sqInt gcModes); +static NoDbgRegParms sqInt checkMaybeObjRefInClosedPIC(sqInt maybeObject); +static NoDbgRegParms sqInt checkValidObjectReferencesInClosedPIC(CogMethod *cPIC); +static NoDbgRegParms NeverInline sqInt cleanUpFailingCogCodeConstituents(CogMethod *cogMethodArg); +static NoDbgRegParms sqInt closedPICRefersToUnmarkedObject(CogMethod *cPIC); +extern char * codeEntryFor(char *address); +extern char * codeEntryNameFor(char *address); +extern usqInt cogCodeBase(void); +extern sqInt cogCodeConstituents(sqInt withDetails); +static NoDbgRegParms void cogExtendPICCaseNMethodtagisMNUCase(CogMethod *cPIC, sqInt caseNMethod, sqInt caseNTag, sqInt isMNUCase); +extern CogMethod * cogFullBlockMethodnumCopied(sqInt aMethodObj, sqInt numCopied); +extern void cogitPostGCAction(sqInt gcMode); +static NoDbgRegParms sqInt cogMethodDoesntLookKosher(CogMethod *cogMethod); +extern CogMethod * cogMNUPICSelectorreceivermethodOperandnumArgs(sqInt selector, sqInt rcvr, sqInt methodOperand, sqInt numArgs); +static NoDbgRegParms CogMethod * cogOpenPICSelectornumArgs(sqInt selector, sqInt numArgs); +static NoDbgRegParms CogMethod * cogPICSelectornumArgsCase0MethodCase1MethodtagisMNUCase(sqInt selector, sqInt numArgs, CogMethod *case0CogMethod, sqInt case1MethodOrNil, sqInt case1Tag, sqInt isMNUCase); +extern CogMethod * cogselector(sqInt aMethodObj, sqInt aSelectorOop); +static NoDbgRegParms sqInt collectCogConstituentForAnnotationMcpcBcpcMethod(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *cogMethodArg); +static NoDbgRegParms sqInt collectCogConstituentOnSpurForAnnotationMcpcBcpcData(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *dataArg); +static NoDbgRegParms sqInt collectCogMethodConstituentOnSpurofClass(CogMethod *cogMethod, sqInt dataClass); +static NoDbgRegParms sqInt collectCogMethodConstituent(CogMethod *cogMethod); +extern void compactCogCompiledCode(void); +static void compactPICsWithFreedTargets(void); +static AbstractInstruction * compileAbort(void); +static NoDbgRegParms sqInt compileBlockDispatchFromto(sqInt lowBlockStartIndex, sqInt highBlockStartIndex); +static NoDbgRegParms void compileBlockEntry(BlockStart *blockStart); +static NoDbgRegParms void compileCallFornumArgsargargargargresultRegregsToSave(void *aRoutine, sqInt numArgs, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3, sqInt resultRegOrNone, sqInt regMask); +static AbstractInstruction * compileCPICEntry(void); +static NoDbgRegParms sqInt compileEntireFullBlockMethod(sqInt numCopied); +static void compileEntry(void); +static sqInt compileFullBlockEntry(void); +static sqInt compileMethodBody(void); +static NoDbgRegParms sqInt compilePICAbort(sqInt numArgs); +static NoDbgRegParms AbstractInstruction * compileStackOverflowCheck(sqInt canContextSwitch); +static NoDbgRegParms void compileTrampolineFornumArgsargargargargregsToSavepushLinkRegresultReg(void *aRoutine, sqInt numArgs, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3, sqInt regMask, sqInt pushLinkReg, sqInt resultRegOrNone); +static void computeEntryOffsets(void); +static void computeFullBlockEntryOffsets(void); +extern usqInt computeGoodVarBaseAddress(void); +static void computeMaximumSizes(void); +static NoDbgRegParms sqInt configureCPICCase0Case1MethodtagisMNUCasenumArgsdelta(CogMethod *cPIC, CogMethod *case0CogMethod, sqInt case1Method, sqInt case1Tag, sqInt isMNUCase, sqInt numArgs, sqInt addrDelta); +static NoDbgRegParms sqInt configureMNUCPICmethodOperandnumArgsdelta(CogMethod *cPIC, sqInt methodOperand, sqInt numArgs, sqInt addrDelta); +#if VMInvestigations +static NoDbgRegParms sqInt countCogMethodDataImportspccount(sqInt annotation, char *mcpc, sqInt *countPtr); +#endif /* VMInvestigations */ +static NoDbgRegParms sqInt cPICCompactAndIsNowEmpty(CogMethod *cPIC); +static NoDbgRegParms sqInt cPICHasForwardedClass(CogMethod *cPIC); +static NoDbgRegParms sqInt cPICHasFreedTargets(CogMethod *cPIC); +static sqInt cPICPrototypeCaseOffset(void); +static NoDbgRegParms sqInt cPICHasTarget(CogMethod *cPIC, CogMethod *targetMethod); +static NoDbgRegParms sqInt createCPICData(CogMethod *cPIC); +static NoDbgRegParms AbstractInstruction * gDivRRQuoRem(sqInt rDivisor, sqInt rDividend, sqInt rQuotient, sqInt rRemainder); +extern int defaultCogCodeSize(void); +static NoDbgRegParms sqInt deltaToSkipPrimAndErrorStoreInheader(sqInt aMethodObj, sqInt aMethodHeader); +static NoDbgRegParms sqInt endPCOf(sqInt aMethod); +static void enterCogCodePopReceiver(void); +static NoDbgRegParms sqInt entryPointTagIsSelector(sqInt entryPoint); +static NoDbgRegParms sqInt expectedClosedPICPrototype(CogMethod *cPIC); +static sqInt extABytecode(void); +static sqInt extBBytecode(void); +static NoDbgRegParms sqInt fillInBlockHeadersAt(sqInt startAddress); +static NoDbgRegParms void fillInMethodHeadersizeselector(CogMethod *method, sqInt size, sqInt selector); +static NoDbgRegParms sqInt findBackwardBranchIsBackwardBranchMcpcBcpcMatchingBcpc(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *targetBcpc); +static NoDbgRegParms usqInt findBlockMethodWithEntrystartBcpc(sqInt blockEntryMcpc, sqInt startBcpc); +static NoDbgRegParms sqInt findMapLocationForMcpcinMethod(usqInt targetMcpc, CogMethod *cogMethod); +extern CogBlockMethod * findMethodForStartBcpcinHomeMethod(sqInt startbcpc, CogMethod *cogMethod); +static NoDbgRegParms sqInt findIsBackwardBranchMcpcBcpcMatchingMcpc(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *targetMcpc); +static NoDbgRegParms usqInt firstMappedPCFor(CogMethod *cogMethod); +static sqInt firstPrototypeMethodOop(void); +static NoDbgRegParms BytecodeFixup * fixupAt(sqInt fixupPC); +extern void flagCogMethodForBecome(CogMethod *cogMethod); +static NoDbgRegParms void followForwardedLiteralsImplementationIn(CogMethod *cogMethod); +extern void followForwardedLiteralsIn(CogMethod *cogMethod); +extern void followMovableLiteralsAndUpdateYoungReferrers(void); +extern void freeBecomeFlaggedMethods(void); +extern void freeCogMethod(CogMethod *cogMethod); +extern void freeUnmarkedMachineCode(void); +static NoDbgRegParms AbstractInstruction * genCallMustBeBooleanFor(sqInt boolean); +static NoDbgRegParms AbstractInstruction * genConditionalBranchoperand(sqInt opcode, sqInt operandOne); +static NoDbgRegParms void (*genEnilopmartForandandforCallcalled(sqInt regArg1, sqInt regArg2OrNone, sqInt regArg3OrNone, sqInt forCall, char *trampolineName))(void); +static NoDbgRegParms void genEnilopmartReturn(sqInt forCall); +static NoDbgRegParms NeverInline void generateCaptureCStackPointers(sqInt captureFramePointer); +static void generateClosedPICPrototype(void); +static CogMethod * generateCogFullBlock(void); +static NoDbgRegParms CogMethod * generateCogMethod(sqInt selector); +static NoDbgRegParms sqInt generateMapAtstart(usqInt addressOrNull, usqInt startAddress); +static void generateOpenPICPrototype(void); +static void generateStackPointerCapture(void); +static void generateTrampolines(void); +static NoDbgRegParms BytecodeDescriptor * generatorForPC(sqInt pc); +static void genGetLeafCallStackPointers(void); +static NoDbgRegParms usqInt genInnerPICAbortTrampoline(char *name); +static void (*genInvokeInterpretTrampoline(void))(void); +static NoDbgRegParms void genLoadInlineCacheWithSelector(sqInt selectorIndex); +static usqInt genReturnToInterpreterTrampoline(void); +static NoDbgRegParms sqInt genSmalltalkToCStackSwitch(sqInt pushLinkReg); +static NoDbgRegParms usqInt genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(void *aRoutine, char *trampolineName, sqInt numArgs, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3, sqInt regMask, sqInt pushLinkReg, sqInt resultRegOrNone, sqInt appendBoolean); +static NoDbgRegParms void genTrampolineReturn(sqInt lnkRegWasPushed); +static NoDbgRegParms AbstractInstruction * gen(sqInt opcode); +static NoDbgRegParms AbstractInstruction * genoperand(sqInt opcode, sqInt operand); +static NoDbgRegParms AbstractInstruction * genoperandoperand(sqInt opcode, sqInt operandOne, sqInt operandTwo); +static NoDbgRegParms AbstractInstruction * genoperandoperandoperand(sqInt opcode, sqInt operandOne, sqInt operandTwo, sqInt operandThree); +static NoDbgRegParms sqInt getCogCodeConstituents(sqInt withDetails); +static NoDbgRegParms sqInt getLiteral(sqInt litIndex); +#if VMInvestigations +extern usqInt getMethodZoneBase(void); +#endif /* VMInvestigations */ +static NoDbgRegParms sqInt incrementUsageOfTargetIfLinkedSendmcpcignored(sqInt annotation, char *mcpc, sqInt superfluity); +static NoDbgRegParms sqInt indexForSelectorin(sqInt selector, CogMethod *cogMethod); +extern void initializeCodeZoneFromupTo(sqInt startAddress, sqInt endAddress); +static sqInt initialMethodUsageCount(void); +static int initialOpenPICUsageCount(void); +static NoDbgRegParms int intraCPICTargetinvokesMNUIn(sqInt targetMcpc, CogMethod *cPIC); +static NoDbgRegParms sqInt inverseBranchFor(sqInt opcode); +#if VMInvestigations +static NoDbgRegParms sqInt investigationArrayForClosedPIC(CogMethod *cPIC); +#endif /* VMInvestigations */ +#if VMInvestigations +static NoDbgRegParms sqInt investigationTargetForEntryPointin(sqInt entryPoint, CogMethod *cPIC); +#endif /* VMInvestigations */ +extern int isPCWithinMethodZone(usqInt address); +extern sqInt isSendReturnPC(sqInt retpc); +static NoDbgRegParms AbstractInstruction * gJumpFPEqual(void *jumpTarget); +static NoDbgRegParms AbstractInstruction * gJumpFPGreaterOrEqual(void *jumpTarget); +static NoDbgRegParms AbstractInstruction * gJumpFPGreater(void *jumpTarget); +static NoDbgRegParms AbstractInstruction * gJumpFPNotEqual(void *jumpTarget); +static NoDbgRegParms AbstractInstruction * gLogicalShiftLeftCqR(sqInt quickConstant, sqInt reg); +static NoDbgRegParms AbstractInstruction * gLogicalShiftLeftCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg); +static NoDbgRegParms AbstractInstruction * gLogicalShiftRightCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg); +static AbstractInstruction * lastOpcode(void); +extern CogMethod * linkedSendTargetOrNilAt(sqInt retpc); +extern void linkSendAtintooffsetreceiver(sqInt callSiteReturnAddress, CogMethod *sendingMethod, CogMethod *targetMethod, sqInt theEntryOffset, sqInt receiver); +static NoDbgRegParms BytecodeDescriptor * loadBytesAndGetDescriptor(sqInt pc); +static NoDbgRegParms AbstractInstruction * gMovePerfCnt64RL(sqInt destReg, sqInt liveRegisterMask); +static NoDbgRegParms sqInt mapEndFor(CogMethod *cogMethod); +static NoDbgRegParms sqInt mapForperformUntilarg(CogMethod *cogMethod, sqInt (*functionSymbol)(sqInt annotation, char *mcpc, CogMethod *arg), void *arg); +static NoDbgRegParms sqInt mapObjectReferencesInClosedPIC(CogMethod *cPIC); +static void mapObjectReferencesInGeneratedRuntime(void); +static void mapObjectReferencesInMachineCodeForBecome(void); +static void mapObjectReferencesInMachineCodeForFullGC(void); +static void mapObjectReferencesInMachineCodeForYoungGC(void); +extern void mapObjectReferencesInMachineCode(sqInt gcMode); +extern void markAndTraceMachineCodeOfMarkedMethods(void); +static void markAndTraceObjectReferencesInGeneratedRuntime(void); +static NoDbgRegParms sqInt markAndTraceOrFreeCogMethodfirstVisit(CogMethod *cogMethod, sqInt firstVisit); +static NoDbgRegParms sqInt markAndTraceOrFreePICTargetin(sqInt entryPoint, CogMethod *cPIC); +static NoDbgRegParms sqInt markLiteralsAndUnlinkIfUnmarkedSendpcmethod(sqInt annotation, char *mcpc, CogMethod *cogMethod); +static NoDbgRegParms sqInt markLiteralspcmethod(sqInt annotation, char *mcpc, CogMethod *cogMethod); +extern void markMethodAndReferents(CogBlockMethod *aCogMethod); +extern usqInt maxCogMethodAddress(void); +static NoDbgRegParms sqInt maximumDistanceFromCodeZone(sqInt anAddress); +static NoDbgRegParms sqInt maybeFreeCogMethodDoesntLookKosher(CogMethod *cogMethod); +static int mclassCouldBeContext(void); +static int mclassIsSmallInteger(void); +extern usqInt mcPCForBackwardBranchstartBcpcin(sqInt bcpc, sqInt startbcpc, CogBlockMethod *cogMethod); +static NoDbgRegParms sqInt methodhasSameCodeAscheckPenultimate(sqInt methodA, sqInt methodB, sqInt comparePenultimateLiteral); +extern sqInt mnuOffset(void); +static NoDbgRegParms AbstractInstruction * gNativePopR(sqInt reg); +static NoDbgRegParms AbstractInstruction * gNativePushR(sqInt reg); +static NoDbgRegParms AbstractInstruction * gNativeRetN(sqInt offset); +static NoDbgRegParms sqInt needsFrameIfImmutability(sqInt stackDelta); +static NoDbgRegParms sqInt needsFrameIfInBlock(sqInt stackDelta); +static NoDbgRegParms sqInt needsFrameIfReadMediated(sqInt stackDelta); +static NoDbgRegParms sqInt needsFrameNever(sqInt stackDelta); +static NoDbgRegParms sqInt noAssertMethodClassAssociationOf(sqInt methodPointer); +static sqInt noCogMethodsMaximallyMarked(void); +static NoDbgRegParms int noTargetsFreeInClosedPIC(CogMethod *cPIC); +static NoDbgRegParms AbstractInstruction * gOrCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg); +static NoDbgRegParms sqInt outputInstructionsAt(sqInt startAddress); +static NoDbgRegParms sqInt outputInstructionsForGeneratedRuntimeAt(sqInt startAddress); +extern sqInt patchToOpenPICFornumArgsreceiver(sqInt selector, sqInt numArgs, sqInt receiver); +static sqInt picAbortDiscriminatorValue(void); +static sqInt picInterpretAbortOffset(void); +extern void printCogMethodFor(void *address); +#if VMInvestigations +extern void printPCMapPairsFor(CogMethod *cogMethod); +#endif /* VMInvestigations */ +extern void printTrampolineTable(void); +static sqInt processorHasDivQuoRemAndMClassIsSmallInteger(void); +static sqInt processorHasMultiplyAndMClassIsSmallInteger(void); +static NoDbgRegParms void recordGeneratedRunTimeaddress(char *aString, sqInt address); +extern int recordPrimTraceFunc(void); +static void recordRunTimeObjectReferences(void); +static NoDbgRegParms sqInt registerMaskFor(sqInt reg); +static NoDbgRegParms sqInt registerMaskForand(sqInt reg1, sqInt reg2); +static NoDbgRegParms void relocateCallsAndSelfReferencesInMethod(CogMethod *cogMethod); +static NoDbgRegParms void relocateCallsInClosedPIC(CogMethod *cPIC); +static NoDbgRegParms sqInt relocateIfCallOrMethodReferencemcpcdelta(sqInt annotation, char *mcpc, void *refDeltaArg); +static NoDbgRegParms sqInt remapIfObjectRefpchasYoung(sqInt annotation, char *mcpc, void *hasYoungPtr); +static NoDbgRegParms sqInt remapMaybeObjRefInClosedPICAt(sqInt mcpc); +static NoDbgRegParms void rewriteCPICCaseAttagobjReftarget(sqInt followingAddress, sqInt newTag, sqInt newObjRef, sqInt newTarget); +static NoDbgRegParms AbstractInstruction * gSubRRR(sqInt subReg, sqInt fromReg, sqInt destReg); +static sqInt scanForCleanBlocks(void); +extern void setSelectorOfto(CogMethod *cogMethod, sqInt aSelectorOop); +static NoDbgRegParms sqInt spanForCleanBlockStartingAt(sqInt startPC); +#if VMInvestigations +static NoDbgRegParms sqInt storeCogMethodDataImportspcpointer(sqInt annotation, char *mcpc, usqInt **importsPtrPtr); +#endif /* VMInvestigations */ +#if VMInvestigations +static NoDbgRegParms sqInt stringForEntryPointexistingImportslimit(char *entryPoint, sqInt imports, sqInt *nextStoreLocation); +#endif /* VMInvestigations */ +static sqInt subsequentPrototypeMethodOop(void); +static NoDbgRegParms AbstractInstruction * gTstCqR(sqInt quickConstant, sqInt reg); +extern sqInt traceLinkedSendOffset(void); +static NoDbgRegParms char * trampolineNamenumArgs(char *routinePrefix, sqInt numArgs); +static NoDbgRegParms char * trampolineNamenumArgslimit(char *routinePrefix, int numArgs, sqInt argsLimit); +static NoDbgRegParms char * trampolineNamenumRegArgs(char *routinePrefix, sqInt numArgs); +extern void unflagBecomeFlaggedMethods(void); +static sqInt unknownBytecode(void); +extern void unlinkAllSends(void); +static NoDbgRegParms sqInt unlinkIfFreeOrLinkedSendpcof(sqInt annotation, char *mcpc, void *theSelector); +static NoDbgRegParms sqInt unlinkIfInvalidClassSendpcignored(sqInt annotation, char *mcpc, sqInt superfluity); +static NoDbgRegParms sqInt unlinkIfLinkedSendToFreepcignored(sqInt annotation, char *mcpc, sqInt superfluity); +static NoDbgRegParms sqInt unlinkIfLinkedSendpcif(sqInt annotation, char *mcpc, sqInt (*criterion)(CogMethod *)); +static NoDbgRegParms sqInt unlinkIfLinkedSendpcignored(sqInt annotation, char *mcpc, sqInt superfluity); +static NoDbgRegParms sqInt unlinkIfLinkedSendpcto(sqInt annotation, char *mcpc, CogMethod *theCogMethod); +extern void unlinkSendsLinkedForInvalidClasses(void); +extern void unlinkSendsOfisMNUSelector(sqInt selector, sqInt isMNUSelector); +static void unlinkSendsToFree(void); +extern void unlinkSendsToMethodsSuchThatAndFreeIf(sqInt (*criterion)(CogMethod *), sqInt freeIfTrue); +extern void unlinkSendsToandFreeIf(sqInt targetMethodObject, sqInt freeIfTrue); +static NoDbgRegParms sqInt v3BlockCodeSize(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +static NoDbgRegParms sqInt v3LongForwardBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +static NoDbgRegParms sqInt v3LongBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +static NoDbgRegParms sqInt v3ShortForwardBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +static NoDbgRegParms sqInt v4BlockCodeSize(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +static NoDbgRegParms sqInt v4LongForwardBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +static NoDbgRegParms sqInt v4LongBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +extern void voidCogCompiledCode(void); +static void zeroOpcodeIndex(void); +static NoDbgRegParms void addToOpenPICList(CogMethod *anOpenPIC); +static NoDbgRegParms void addToYoungReferrers(CogMethod *writableCogMethod); +static NoDbgRegParms usqInt allocate(sqInt numBytes); +extern CogMethod * cogMethodContaining(usqInt mcpc); +static void compactCompiledCode(void); +static NoDbgRegParms void ensureInYoungReferrers(CogMethod *cogMethod); +static NoDbgRegParms void freeMethod(CogMethod *cogMethod); +static void freeOlderMethodsForCompaction(void); +extern sqInt kosherYoungReferrers(void); +extern CogMethod * methodFor(void *address); +#if VMInvestigations +extern sqInt methodsCompiledToMachineCodeInto(sqInt arrayObj); +#endif /* VMInvestigations */ +extern sqInt numMethods(void); +extern sqInt numMethodsOfType(sqInt cogMethodType); +static NoDbgRegParms sqInt occurrencesInYoungReferrers(CogMethod *cogMethod); +static NoDbgRegParms CogMethod * openPICWithSelector(sqInt aSelector); +static void planCompaction(void); +extern void printCogMethods(void); +extern void printCogMethodsOfType(sqInt cmType); +extern void printCogMethodsWithMethod(sqInt methodOop); +extern void printCogMethodsWithPrimitive(sqInt primIdx); +extern void printCogMethodsWithSelector(sqInt selectorOop); +extern void printCogYoungReferrers(void); +extern sqInt printOpenPICList(void); +static sqInt pruneYoungReferrers(void); +static sqInt relocateAndPruneYoungReferrers(void); +static sqInt relocateMethodsPreCompaction(void); +static NoDbgRegParms sqInt removeFromOpenPICList(CogMethod *anOpenPIC); +static NoDbgRegParms int roundUpLength(sqInt numBytes); +static void voidOpenPICList(void); +static void voidUnpairedMethodList(void); +extern char * whereIsMaybeCodeThing(sqInt anOop); +static sqInt zoneAlignment(void); +static NoDbgRegParms sqInt checkValidObjectReference(sqInt anOop); +static NoDbgRegParms AbstractInstruction * genCmpClassFloatCompactIndexR(sqInt reg); +static NoDbgRegParms AbstractInstruction * genCmpClassMethodContextCompactIndexR(sqInt reg); +static NoDbgRegParms sqInt genGetMethodHeaderOfintoscratch(sqInt methodReg, sqInt headerReg, sqInt scratchReg); +static NoDbgRegParms AbstractInstruction * genJumpNotSmallIntegersInandscratch(sqInt aRegister, sqInt bRegister, sqInt scratchRegister); +static NoDbgRegParms sqInt genLoadSlotsourceRegdestReg(sqInt index, sqInt sourceReg, sqInt destReg); +static sqInt genPrimitiveAdd(void); +static sqInt genPrimitiveBitAnd(void); +static sqInt genPrimitiveBitOr(void); +static sqInt genPrimitiveBitShift(void); +static sqInt genPrimitiveBitXor(void); +static sqInt genPrimitiveClass(void); +static sqInt genPrimitiveDiv(void); +static sqInt genPrimitiveDivide(void); +static sqInt genPrimitiveEqual(void); +static sqInt genPrimitiveFloatAdd(void); +static sqInt genPrimitiveFloatDivide(void); +static sqInt genPrimitiveFloatMultiply(void); +static sqInt genPrimitiveFloatSubtract(void); +static sqInt genPrimitiveGreaterOrEqual(void); +static sqInt genPrimitiveGreaterThan(void); +static sqInt genPrimitiveHighBit(void); +static sqInt genPrimitiveIdentical(void); +static sqInt genPrimitiveLessOrEqual(void); +static sqInt genPrimitiveLessThan(void); +static sqInt genPrimitiveMaxIdentityHash(void); +static sqInt genPrimitiveMod(void); +static sqInt genPrimitiveMultiply(void); +static sqInt genPrimitiveNewMethod(void); +static sqInt genPrimitiveNotEqual(void); +static sqInt genPrimitiveNotIdentical(void); +static sqInt genPrimitiveQuo(void); +static sqInt genPrimitiveSubtract(void); +static NoDbgRegParms sqInt genSmallIntegerComparison(sqInt jumpOpcode); +static NoDbgRegParms sqInt isUnannotatableConstant(SimStackEntry *simStackEntry); +static NoDbgRegParms sqInt classForInlineCacheTag(sqInt classIndex); +static NoDbgRegParms sqInt genAddSmallIntegerTagsTo(sqInt aRegister); +static NoDbgRegParms sqInt genClearAndSetSmallIntegerTagsIn(sqInt scratchReg); +static NoDbgRegParms sqInt genConvertBitsToSmallFloatInscratch(sqInt reg, sqInt scratch); +static NoDbgRegParms void genConvertCharacterToSmallIntegerInReg(sqInt reg); +static NoDbgRegParms sqInt genConvertIntegerInRegtoSmallIntegerInReg(sqInt srcReg, sqInt destReg); +static NoDbgRegParms sqInt genConvertIntegerToSmallIntegerInReg(sqInt reg); +static NoDbgRegParms sqInt genConvertSmallFloatToSmallFloatHashAsIntegerInRegscratch(sqInt reg, sqInt scratch); +static NoDbgRegParms void genConvertSmallIntegerToCharacterInReg(sqInt reg); +static NoDbgRegParms sqInt genConvertSmallIntegerToIntegerInReg(sqInt reg); +static NoDbgRegParms sqInt genFetchRegArgsForPerformWithArguments(sqInt sizeReg); +static NoDbgRegParms sqInt genFloatArithmeticpreOpCheckboxed(sqInt arithmeticOperator, AbstractInstruction *(*preOpCheckOrNil)(int rcvrReg, int argReg), sqInt rcvrBoxed); +static NoDbgRegParms sqInt genFloatComparisonorIntegerComparisoninvertboxed(AbstractInstruction *(*jumpFPOpcodeGenerator)(void *), sqInt jumpOpcode, sqInt invertComparison, sqInt rcvrBoxed); +static NoDbgRegParms sqInt genGetHashFieldNonImmOfasSmallIntegerInto(sqInt instReg, sqInt destReg); +static NoDbgRegParms sqInt genGetHashFieldNonImmOfinto(sqInt instReg, sqInt destReg); +static NoDbgRegParms AbstractInstruction * genGetInlineCacheClassTagFromintoforEntry(sqInt sourceReg, sqInt destReg, sqInt forEntry); +static NoDbgRegParms sqInt genGetNumBytesOfinto(sqInt srcReg, sqInt destReg); +static NoDbgRegParms sqInt genGetOverflowSlotsOfinto(sqInt srcReg, sqInt destReg); +static NoDbgRegParms sqInt genGetSmallFloatValueOfscratchinto(sqInt oopReg, sqInt scratch, sqInt dpReg); +static NoDbgRegParms AbstractInstruction * genInstantiate64BitPositiveIntegerValueintoscratchRegmayFail(sqInt valueReg, sqInt resultReg, sqInt scratch, sqInt mayFail); +static NoDbgRegParms AbstractInstruction * genInstantiate64BitSignedIntegerValueintoscratchRegmayFail(sqInt valueReg, sqInt resultReg, sqInt scratch, sqInt mayFail); +static NoDbgRegParms AbstractInstruction * genInstantiateFloatValueintoscratchRegscratchRegmayFail(sqInt dpreg, sqInt resultReg, sqInt scratch1, sqInt scratch2, sqInt mayFail); +static NoDbgRegParms AbstractInstruction * genJumpIsSmallIntegerValuescratch(sqInt aRegister, sqInt scratchReg); +static NoDbgRegParms AbstractInstruction * genJumpNotCharacter(sqInt reg); +static NoDbgRegParms AbstractInstruction * genJumpNotSmallFloatValueBitsscratch(sqInt reg, sqInt exponent); +static NoDbgRegParms AbstractInstruction * genJumpNotSmallFloat(sqInt reg); +static NoDbgRegParms AbstractInstruction * genJumpNotSmallIntegerValuescratch(sqInt aRegister, sqInt scratchReg); +static NoDbgRegParms AbstractInstruction * genJumpNotSmallInteger(sqInt reg); +static NoDbgRegParms AbstractInstruction * genJumpSmallInteger(sqInt aRegister); +static NoDbgRegParms sqInt genPrimitiveAtPutSigned(sqInt signedVersion); +static NoDbgRegParms sqInt genPrimitiveAtSigned(sqInt signedVersion); +static sqInt genPrimitiveFloatEqual(void); +static sqInt genPrimitiveFloatGreaterOrEqual(void); +static sqInt genPrimitiveFloatGreaterThan(void); +static sqInt genPrimitiveFloatLessOrEqual(void); +static sqInt genPrimitiveFloatLessThan(void); +static sqInt genPrimitiveFloatNotEqual(void); +static sqInt genPrimitiveIdentityHash(void); +static sqInt genPrimitiveImmediateAsInteger(void); +static sqInt genPrimitiveNew(void); +static sqInt genPrimitiveNewWithArg(void); +static sqInt genPrimitiveShallowCopy(void); +static sqInt genPrimitiveSlotAt(void); +static sqInt genPrimitiveSmallFloatAdd(void); +static sqInt genPrimitiveSmallFloatDivide(void); +static sqInt genPrimitiveSmallFloatEqual(void); +static sqInt genPrimitiveSmallFloatGreaterOrEqual(void); +static sqInt genPrimitiveSmallFloatGreaterThan(void); +static sqInt genPrimitiveSmallFloatLessOrEqual(void); +static sqInt genPrimitiveSmallFloatLessThan(void); +static sqInt genPrimitiveSmallFloatMultiply(void); +static sqInt genPrimitiveSmallFloatNotEqual(void); +static sqInt genPrimitiveSmallFloatSquareRoot(void); +static sqInt genPrimitiveSmallFloatSubtract(void); +static sqInt genPrimitiveStringAt(void); +static sqInt genPrimitiveStringAtPut(void); +static sqInt genPrimitiveUninitializedNewWithArg(void); +static NoDbgRegParms sqInt genPureFloatArithmeticpreOpCheckboxed(sqInt arithmeticOperator, AbstractInstruction *(*preOpCheckOrNil)(int rcvrReg, int argReg), sqInt rcvrBoxed); +static NoDbgRegParms sqInt genPureFloatComparisoninvertboxed(AbstractInstruction *(*jumpFPOpcodeGenerator)(void *), sqInt invertComparison, sqInt rcvrBoxed); +static NoDbgRegParms sqInt genRemoveSmallIntegerTagsInScratchReg(sqInt scratchReg); +static NoDbgRegParms sqInt genShiftAwaySmallIntegerTagsInScratchReg(sqInt scratchReg); +static NoDbgRegParms sqInt genSmallIntegerComparisonorDoubleComparisoninvert(sqInt jumpOpcode, AbstractInstruction * NoDbgRegParms (*jumpFPOpcodeGenerator)(void *), sqInt invertComparison); +static NoDbgRegParms sqInt getLiteralCountOfplusOneinBytesintoscratch(sqInt methodReg, sqInt plusOne, sqInt inBytes, sqInt litCountReg, sqInt scratchReg); +static NoDbgRegParms sqInt inlineCacheTagForInstance(sqInt oop); +static sqInt log2BytesPerWord(void); +static void maybeGenerateSelectorIndexDereferenceRoutine(void); +static sqInt numSmallIntegerBits(void); +static sqInt numSmallIntegerTagBits(void); +static NoDbgRegParms sqInt validInlineCacheTag(sqInt classIndexOrTagPattern); +static void callStoreCheckTrampoline(void); +static NoDbgRegParms sqInt checkValidOopReference(sqInt anOop); +static NoDbgRegParms AbstractInstruction * compileJumpToRetryOfonPrimitiveFail(void (*primitiveRoutine)(void), sqInt primIndex); +static NoDbgRegParms sqInt couldBeObject(sqInt literal); +static NoDbgRegParms usqInt genActiveContextTrampolineLargeinBlockcalled(sqInt isLarge, sqInt isInBlock, char *aString); +static NoDbgRegParms AbstractInstruction * genCheckRememberedBitOfscratch(sqInt objReg, sqInt scratchReg); +static NoDbgRegParms sqInt genConvertCharacterToCodeInReg(sqInt reg); +static NoDbgRegParms sqInt genConvertIntegerToCharacterInReg(sqInt reg); +static NoDbgRegParms sqInt genCreateFullClosurenumArgsnumCopiedignoreContextcontextNumArgslargeinBlock(sqInt compiledBlock, sqInt numArgs, sqInt numCopied, sqInt ignoreContext, sqInt contextNumArgs, sqInt contextIsLarge, sqInt contextIsBlock); +static NoDbgRegParms sqInt genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(sqInt reg, sqInt scratch, void *fwdJumpTarget, void *nonFwdJumpTargetOrZero); +static NoDbgRegParms sqInt genEnsureOopInRegNotForwardedscratchRegupdatingSlotin(sqInt reg, sqInt scratch, sqInt index, sqInt objReg); +static NoDbgRegParms sqInt genGetActiveContextLargeinBlock(sqInt isLarge, sqInt isInBlock); +static NoDbgRegParms sqInt genGetActiveContextNumArgslargeinBlock(sqInt numArgs, sqInt isLargeContext, sqInt isInBlock); +static NoDbgRegParms sqInt genGetBitsofFormatByteOfinto(sqInt mask, sqInt sourceReg, sqInt destReg); +static NoDbgRegParms sqInt genGetClassIndexOfNonImminto(sqInt sourceReg, sqInt destReg); +static NoDbgRegParms sqInt genGetClassObjectOfClassIndexintoscratchReg(sqInt instReg, sqInt destReg, sqInt scratchReg); +static NoDbgRegParms sqInt genGetClassObjectOfintoscratchRegmayBeAForwarder(sqInt instReg, sqInt destReg, sqInt scratchReg, sqInt mayBeForwarder); +static NoDbgRegParms AbstractInstruction * genGetClassTagOfintoscratchReg(sqInt instReg, sqInt destReg, sqInt scratchReg); +static NoDbgRegParms sqInt genGetCompactClassIndexNonImmOfinto(sqInt instReg, sqInt destReg); +static NoDbgRegParms sqInt genGetDoubleValueOfinto(sqInt srcReg, sqInt destFPReg); +static NoDbgRegParms sqInt genGetFormatOfinto(sqInt srcReg, sqInt destReg); +static NoDbgRegParms sqInt genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(sqInt sourceReg, sqInt destReg, sqInt scratchRegOrNone); +static NoDbgRegParms sqInt genGetNumSlotsOfinto(sqInt srcReg, sqInt destReg); +static NoDbgRegParms sqInt genGetRawSlotSizeOfNonImminto(sqInt sourceReg, sqInt destReg); +static NoDbgRegParms AbstractInstruction * genJumpImmediate(sqInt aRegister); +#if IMMUTABILITY +static NoDbgRegParms AbstractInstruction * genJumpImmutablescratchReg(sqInt sourceReg, sqInt scratchReg); +#endif /* IMMUTABILITY */ +#if IMMUTABILITY +static NoDbgRegParms AbstractInstruction * genJumpMutablescratchReg(sqInt sourceReg, sqInt scratchReg); +#endif /* IMMUTABILITY */ +static NoDbgRegParms sqInt genNewArrayOfSizeinitialized(sqInt size, sqInt initialize); +static NoDbgRegParms sqInt genNoPopCreateClosureAtnumArgsnumCopiedcontextNumArgslargeinBlock(sqInt bcpc, sqInt numArgs, sqInt numCopied, sqInt ctxtNumArgs, sqInt isLargeCtxt, sqInt isInBlock); +static sqInt genPrimitiveAsCharacter(void); +static sqInt genPrimitiveAsFloat(void); +static sqInt genPrimitiveAt(void); +static sqInt genPrimitiveAtPut(void); +static sqInt genPrimitiveFloatSquareRoot(void); +static NoDbgRegParms sqInt genPrimitiveIdenticalOrNotIf(sqInt orNot); +static sqInt genPrimitiveIntegerAt(void); +static sqInt genPrimitiveIntegerAtPut(void); +static sqInt genPrimitiveMakePoint(void); +static sqInt genPrimitiveObjectAt(void); +static sqInt genPrimitiveSize(void); +static sqInt genPrimitiveStringCompareWith(void); +static sqInt genPrimitiveStringReplace(void); +static NoDbgRegParms sqInt genSetSmallIntegerTagsIn(sqInt scratchReg); +static usqInt genStoreCheckContextReceiverTrampoline(void); +static NoDbgRegParms sqInt genStoreCheckReceiverRegvalueRegscratchReginFrame(sqInt destReg, sqInt valueReg, sqInt scratchReg, sqInt inFrame); +static NoDbgRegParms sqInt genStoreSourceRegslotIndexdestRegscratchReginFrameneedsStoreCheck(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt inFrame, sqInt needsStoreCheck); +static NoDbgRegParms sqInt genStoreSourceRegslotIndexintoNewObjectInDestReg(sqInt sourceReg, sqInt index, sqInt destReg); +#if IMMUTABILITY +static NoDbgRegParms usqInt genStoreTrampolineCalledinstVarIndex(char *trampolineName, sqInt instVarIndex); +#endif /* IMMUTABILITY */ +#if IMMUTABILITY +static NoDbgRegParms sqInt genStoreWithImmutabilityAndStoreCheckSourceRegslotIndexdestRegscratchRegneedRestoreRcvr(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt needRestoreRcvr); +#endif /* IMMUTABILITY */ +#if IMMUTABILITY +static NoDbgRegParms sqInt genStoreWithImmutabilityButNoStoreCheckSourceRegslotIndexdestRegscratchRegneedRestoreRcvr(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt needRestoreRcvr); +#endif /* IMMUTABILITY */ +#if IMMUTABILITY +static NoDbgRegParms sqInt genStoreWithImmutabilityCheckSourceRegslotIndexdestRegscratchRegneedsStoreCheckneedRestoreRcvr(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt needsStoreCheck, sqInt needRestoreRcvr); +#endif /* IMMUTABILITY */ +static sqInt getActiveContextAllocatesInMachineCode(void); +static NoDbgRegParms sqInt inlineCacheTagIsYoung(sqInt cacheTag); +static NoDbgRegParms AbstractInstruction * jumpNotCharacterUnsignedValueInRegister(sqInt reg); +static NoDbgRegParms sqInt markAndTraceLiteralinatpc(sqInt literal, CogMethod *cogMethodOrNil, usqInt address); +static NoDbgRegParms void markAndTraceLiteralinat(sqInt literal, CogMethod *cogMethod, sqInt *address); +static NoDbgRegParms void markAndTraceUpdatedLiteralin(sqInt objOop, CogMethod *cogMethodOrNil); +static NoDbgRegParms sqInt maybeCompileRetryOfonPrimitiveFailflagsjumpcontinue(void (*primitiveRoutine)(void), sqInt primIndex, sqInt flags, AbstractInstruction *jmp, AbstractInstruction *label); +static NoDbgRegParms sqInt maybeShiftClassTagRegisterForMethodCacheProbe(sqInt classTagReg); +static sqInt numCharacterBits(void); +static NoDbgRegParms sqInt remapObject(sqInt objOop); +static NoDbgRegParms sqInt remapOop(sqInt objOop); +static NoDbgRegParms sqInt shouldAnnotateObjectReference(sqInt anOop); +static NoDbgRegParms sqInt slotOffsetOfInstVarIndex(sqInt index); +static NoDbgRegParms SimStackEntry * ensureSpilledAtfrom(SimStackEntry *self_in_CogSimStackEntry, sqInt baseOffset, sqInt baseRegister); +static NoDbgRegParms sqInt isSameEntryAs(SimStackEntry *self_in_CogSimStackEntry, SimStackEntry *ssEntry); +static NoDbgRegParms sqInt mayBeAForwarder(SimStackEntry *self_in_CogSimStackEntry); +static NoDbgRegParms SimStackEntry * popToReg(SimStackEntry *self_in_CogSimStackEntry, sqInt reg); +static NoDbgRegParms sqInt registerMask(SimStackEntry *self_in_CogSimStackEntry); +static NoDbgRegParms sqInt registerMaskOrNone(SimStackEntry *self_in_CogSimStackEntry); +static NoDbgRegParms sqInt registerOrNone(SimStackEntry *self_in_CogSimStackEntry); +static NoDbgRegParms SimStackEntry * storeToReg(SimStackEntry *self_in_CogSimStackEntry, sqInt reg); +static NoDbgRegParms int isMergeFixup(BytecodeFixup *self_in_CogSSBytecodeFixup); +static NoDbgRegParms sqInt availableRegisterOrNoneFor(AbstractInstruction *self_in_CogX64Compiler, sqInt liveRegsMask); +static NoDbgRegParms sqInt callFullInstructionByteSize(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt callFullTargetFromReturnAddress(AbstractInstruction *self_in_CogX64Compiler, sqInt callSiteReturnAddress); +static NoDbgRegParms sqInt callInstructionByteSize(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms usqInt callTargetFromReturnAddress(AbstractInstruction *self_in_CogX64Compiler, sqInt callSiteReturnAddress); +static NoDbgRegParms AbstractInstruction * cFloatResultToRd(AbstractInstruction *self_in_CogX64Compiler, sqInt reg); +static NoDbgRegParms AbstractInstruction * cFloatResultToRs(AbstractInstruction *self_in_CogX64Compiler, sqInt reg); +static NoDbgRegParms sqInt cmpC32RTempByteSize(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt computeMaximumSize(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms int computeShiftRRSize(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt computeSizeOfPushCw(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt concretizeArithCqRWithROraxOpcode(AbstractInstruction *self_in_CogX64Compiler, sqInt regOpcode, sqInt raxOpcode); +static NoDbgRegParms sqInt concretizeFill32(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt concretizeMovePerfCnt64RL(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt concretizeMoveRX32rR(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt concretizeMoveX32rRR(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt concretizeOpRR(AbstractInstruction *self_in_CogX64Compiler, sqInt x64opcode); +static NoDbgRegParms sqInt concretizePrefetchAw(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt concretizeReverseOpRR(AbstractInstruction *self_in_CogX64Compiler, sqInt x64opcode); +static NoDbgRegParms sqInt concretizeXCHGRR(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms AbstractInstruction * detectFeatures(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt dispatchConcretize(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt dispatchConcretizeProcessorSpecific(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt fullCallsAreRelative(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms AbstractInstruction * genDivRRQuoRem(AbstractInstruction *self_in_CogX64Compiler, sqInt abstractRegDivisor, sqInt abstractRegDividend, sqInt abstractRegQuotient, sqInt abstractRegRemainder); +static NoDbgRegParms AbstractInstruction * genMemCopytoconstantSize(AbstractInstruction *self_in_CogX64Compiler, sqInt originalSourceReg, sqInt originalDestReg, sqInt size); +static NoDbgRegParms AbstractInstruction * genMemCopytosize(AbstractInstruction *self_in_CogX64Compiler, sqInt originalSourceReg, sqInt originalDestReg, sqInt originalSize); +static NoDbgRegParms AbstractInstruction * genMulRR(AbstractInstruction *self_in_CogX64Compiler, sqInt regSource, sqInt regDest); +static NoDbgRegParms AbstractInstruction * genPushRegisterArgsForAbortMissNumArgs(AbstractInstruction *self_in_CogX64Compiler, sqInt numArgs); +static NoDbgRegParms AbstractInstruction * genPushRegisterArgsForNumArgsscratchReg(AbstractInstruction *self_in_CogX64Compiler, sqInt numArgs, sqInt scratchReg); +static NoDbgRegParms sqInt genRemoveNArgsFromStack(AbstractInstruction *self_in_CogX64Compiler, sqInt n); +static NoDbgRegParms sqInt genRestoreRegs(AbstractInstruction *self_in_CogX64Compiler, sqInt regMask); +static NoDbgRegParms sqInt genSaveRegs(AbstractInstruction *self_in_CogX64Compiler, sqInt regMask); +static NoDbgRegParms AbstractInstruction * genSubstituteReturnAddress(AbstractInstruction *self_in_CogX64Compiler, sqInt retpc); +static NoDbgRegParms AbstractInstruction * genSwapRRScratch(AbstractInstruction *self_in_CogX64Compiler, sqInt regA, sqInt regB, sqInt regTmp); +static NoDbgRegParms int hasLZCNTInstructions(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt hasVarBaseRegister(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt instructionSizeAt(AbstractInstruction *self_in_CogX64Compiler, sqInt pc); +static NoDbgRegParms int is32BitSignedImmediate(AbstractInstruction *self_in_CogX64Compiler, sqInt a64BitUnsignedOperand); +static NoDbgRegParms int isCallPrecedingReturnPC(AbstractInstruction *self_in_CogX64Compiler, sqInt mcpc); +static NoDbgRegParms sqInt isJumpAt(AbstractInstruction *self_in_CogX64Compiler, sqInt pc); +static NoDbgRegParms int isQuick(AbstractInstruction *self_in_CogX64Compiler, usqIntptr_t operand); +static NoDbgRegParms sqInt isWithinMwOffsetRange(AbstractInstruction *self_in_CogX64Compiler, sqInt anAddress); +static NoDbgRegParms AbstractInstruction * jmpTarget(AbstractInstruction *self_in_CogX64Compiler, AbstractInstruction *anAbstractInstruction); +static NoDbgRegParms sqInt jumpLongByteSize(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt jumpLongConditionalByteSize(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms usqInt jumpLongTargetBeforeFollowingAddress(AbstractInstruction *self_in_CogX64Compiler, sqInt mcpc); +static NoDbgRegParms usqInt jumpTargetPCAt(AbstractInstruction *self_in_CogX64Compiler, sqInt pc); +static NoDbgRegParms sqInt leafCallStackPointerDelta(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt loadPICLiteralByteSize(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms sqInt machineCodeAt(AbstractInstruction *self_in_CogX64Compiler, sqInt anOffset); +static NoDbgRegParms sqInt machineCodeBytes(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms usqInt modRMRO(AbstractInstruction *self_in_CogX64Compiler, sqInt mod, sqInt regMode, sqInt regOpcode); +static NoDbgRegParms sqInt numIntRegArgs(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms AbstractInstruction * padIfPossibleWithStopsFromto(AbstractInstruction *self_in_CogX64Compiler, sqInt startAddr, sqInt endAddr); +static NoDbgRegParms sqInt performanceCounterTicksPerMicrosecond(AbstractInstruction *self_in_CogX64Compiler); +static NoDbgRegParms AbstractInstruction * relocateCallBeforeReturnPCby(AbstractInstruction *self_in_CogX64Compiler, sqInt retpc, sqInt delta); +static NoDbgRegParms AbstractInstruction * relocateMethodReferenceBeforeAddressby(AbstractInstruction *self_in_CogX64Compiler, sqInt pc, sqInt delta); +static NoDbgRegParms sqInt rewriteCallAttarget(AbstractInstruction *self_in_CogX64Compiler, usqInt callSiteReturnAddress, usqInt callTargetAddress); +static NoDbgRegParms AbstractInstruction * rewriteCPICJumpAttarget(AbstractInstruction *self_in_CogX64Compiler, usqInt addressFollowingJump, usqInt jumpTargetAddr); +static NoDbgRegParms sqInt rewriteInlineCacheAttagtarget(AbstractInstruction *self_in_CogX64Compiler, usqInt callSiteReturnAddress, sqInt cacheTag, usqInt callTargetAddress); +static NoDbgRegParms AbstractInstruction * rewriteInlineCacheTagat(AbstractInstruction *self_in_CogX64Compiler, sqInt cacheTag, sqInt callSiteReturnAddress); +static NoDbgRegParms usqInt rexRxb(AbstractInstruction *self_in_CogX64Compiler, sqInt reg, sqInt sibReg, sqInt fieldReg); +static NoDbgRegParms usqInt rexwrxb(AbstractInstruction *self_in_CogX64Compiler, sqInt width64, sqInt reg, sqInt sibReg, sqInt fieldReg); +static NoDbgRegParms sqInt setsConditionCodesFor(AbstractInstruction *self_in_CogX64Compiler, sqInt aConditionalJumpOpcode); +static NoDbgRegParms int sizeHasModrmat(AbstractInstruction *self_in_CogX64Compiler, sqInt op, sqInt pc); +static NoDbgRegParms int sizeImmediateGroup1at(AbstractInstruction *self_in_CogX64Compiler, sqInt op, sqInt pc); +static NoDbgRegParms AbstractInstruction * stopsFromto(AbstractInstruction *self_in_CogX64Compiler, sqInt startAddr, sqInt endAddr); +static NoDbgRegParms AbstractInstruction * storeLiteral32beforeFollowingAddress(AbstractInstruction *self_in_CogX64Compiler, sqInt literal, sqInt followingAddress); +static NoDbgRegParms usqInt sib(AbstractInstruction *self_in_CogX64Compiler, sqInt scale, sqInt indexReg, sqInt baseReg); +static NoDbgRegParms int twoByteInstructionSizeAt(AbstractInstruction *self_in_CogX64Compiler, sqInt pc); +static NoDbgRegParms sqInt zoneCallsAreRelative(AbstractInstruction *self_in_CogX64Compiler); +static sqInt generateRunTimeTrampolines(void); +static NoDbgRegParms void genMoveCurrentMethodOopToRegister(sqInt reg); +static NoDbgRegParms sqInt genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(sqInt popBoolean, sqInt slotIndex, sqInt needsStoreCheck, sqInt needsImmCheck); +static NoDbgRegParms sqInt genStorePopRemoteTempAtneedsStoreCheck(sqInt popBoolean, sqInt slotIndex, sqInt remoteTempIndex, sqInt needsStoreCheck); +static NoDbgRegParms sqInt genStorePopTemporaryVariable(sqInt popBoolean, sqInt tempIndex); +static sqInt genUpArrowReturn(void); +static NoDbgRegParms void initSimStackForFramefulMethod(sqInt startpc); +static NoDbgRegParms void initSimStackForFramelessMethod(sqInt startpc); +static void generateObjectRepresentationTrampolines(void); +static sqInt genPrimitiveSlotAtPut(void); +#if VMInvestigations +extern char * abiName(void); +#endif /* VMInvestigations */ +extern sqInt cogMethodHasExternalPrim(CogMethod *aCogMethod); +extern sqInt cogMethodHasMachineCodePrim(CogMethod *aCogMethod); +#if VMInvestigations +extern sqInt cogMethodImportsintodetailCPICs(CogMethod *cogMethod, sqInt data, sqInt detailCPICs); +#endif /* VMInvestigations */ +static sqInt compileBlockDispatch(void); +static void compileGetErrorCode(void); +static sqInt compileInterpreterPrimitive(void); +static NoDbgRegParms sqInt compileInterpreterPrimitiveflags(void (*primitiveRoutine)(void), sqInt flags); +static NoDbgRegParms sqInt compileOnStackExternalPrimitiveflags(void (*primitiveRoutine)(void), sqInt flags); +static NoDbgRegParms AbstractInstruction * compileOpenPICMethodCacheProbeForwithShiftbaseRegOrNone(sqInt selector, sqInt shift, sqInt baseRegOrNone); +static NoDbgRegParms void compileOpenPICnumArgs(sqInt selector, sqInt numArgs); +static NoDbgRegParms AbstractInstruction * compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(sqInt selectorReg, sqInt shift, sqInt baseRegOrNone); +static sqInt compilePrimitive(void); +static sqInt extendedPushBytecode(void); +static sqInt extendedStoreAndPopBytecode(void); +static sqInt extendedStoreBytecode(void); +static NoDbgRegParms sqInt frameOffsetOfTemporary(sqInt index); +static sqInt genCallMappedInlinedPrimitive(void); +static NoDbgRegParms AbstractInstruction * genDoubleFailIfZeroArgRcvrarg(int rcvrReg, int argReg); +static sqInt genExtendedSendBytecode(void); +static sqInt genExtendedSuperBytecode(void); +static sqInt genExtJumpIfFalse(void); +static sqInt genExtJumpIfTrue(void); +static sqInt genExtNopBytecode(void); +static sqInt genExtPushCharacterBytecode(void); +static sqInt genExtPushIntegerBytecode(void); +static sqInt genExtPushLiteralBytecode(void); +static sqInt genExtPushLiteralVariableBytecode(void); +static sqInt genExtPushPseudoVariable(void); +static sqInt genExtPushReceiverVariableBytecode(void); +static sqInt genExtSendBytecode(void); +static sqInt genExtSendSuperBytecode(void); +static sqInt genExtStoreAndPopLiteralVariableBytecode(void); +static sqInt genExtStoreAndPopReceiverVariableBytecode(void); +static sqInt genExtStoreLiteralVariableBytecode(void); +static sqInt genExtStoreReceiverVariableBytecode(void); +static sqInt genExtUnconditionalJump(void); +static sqInt genFastPrimFail(void); +static NoDbgRegParms void genFastPrimTraceUsingandsetNewMethod(sqInt r1, sqInt r2, sqInt setNewMethod); +static void genLoadNewMethod(void); +static sqInt genLongJumpIfFalse(void); +static sqInt genLongJumpIfTrue(void); +static sqInt genLongPushTemporaryVariableBytecode(void); +static sqInt genLongStoreAndPopTemporaryVariableBytecode(void); +static sqInt genLongStoreTemporaryVariableBytecode(void); +static sqInt genLongUnconditionalBackwardJump(void); +static sqInt genLongUnconditionalForwardJump(void); +static NoDbgRegParms sqInt genLookupForPerformNumArgs(sqInt numArgs); +static NoDbgRegParms usqInt genMustBeBooleanTrampolineForcalled(sqInt boolean, char *trampolineName); +static sqInt genPrimitiveHashMultiply(void); +static NoDbgRegParms void genPrimReturnEnterCogCodeEnilopmart(sqInt profiling); +static sqInt genPushConstantFalseBytecode(void); +static sqInt genPushConstantNilBytecode(void); +static sqInt genPushConstantOneBytecode(void); +static sqInt genPushConstantTrueBytecode(void); +static sqInt genPushConstantZeroBytecode(void); +static sqInt genPushLiteralConstantBytecode(void); +static sqInt genPushLiteralVariable16CasesBytecode(void); +static sqInt genPushLiteralVariableBytecode(void); +static sqInt genPushQuickIntegerConstantBytecode(void); +static sqInt genPushReceiverVariableBytecode(void); +static sqInt genPushTemporaryVariableBytecode(void); +extern sqInt genQuickReturnConst(void); +extern sqInt genQuickReturnInstVar(void); +extern sqInt genQuickReturnSelf(void); +static sqInt genReturnFalse(void); +static sqInt genReturnNil(void); +static sqInt genReturnNilFromBlock(void); +static sqInt genReturnTrue(void); +static sqInt genSecondExtendedSendBytecode(void); +static sqInt genSendLiteralSelector0ArgsBytecode(void); +static sqInt genSendLiteralSelector1ArgBytecode(void); +static sqInt genSendLiteralSelector2ArgsBytecode(void); +static sqInt genShortJumpIfFalse(void); +static sqInt genShortJumpIfTrue(void); +static sqInt genShortUnconditionalJump(void); +static sqInt genSpecialSelectorEqualsEquals(void); +static sqInt genSpecialSelectorNotEqualsEquals(void); +static sqInt genSpecialSelectorSend(void); +static sqInt genStoreAndPopReceiverVariableBytecode(void); +static sqInt genStoreAndPopRemoteTempLongBytecode(void); +static sqInt genStoreAndPopTemporaryVariableBytecode(void); +static sqInt genStoreRemoteTempLongBytecode(void); +static NoDbgRegParms void genTakeProfileSample(AbstractInstruction *continueLabel); +static sqInt genUnconditionalTrapBytecode(void); +#if VMInvestigations +extern char * isaName(void); +#endif /* VMInvestigations */ +extern sqInt mapPCDataForinto(CogMethod *cogMethod, sqInt arrayObj); +static sqInt numSpecialSelectors(void); +static NoDbgRegParms usqInt pcDataForBlockEntryMethod(sqInt blockEntryMcpc, sqInt cogMethod); +static NoDbgRegParms sqInt pcDataForAnnotationMcpcBcpcMethod(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *cogMethodArg); +static PrimitiveDescriptor * primitiveGeneratorOrNil(void); +static NoDbgRegParms int registerisInMask(sqInt reg, sqInt mask); +static NoDbgRegParms int registerisNotInMask(sqInt reg, sqInt mask); +static NoDbgRegParms BlockStart * addBlockStartAtnumArgsnumCopiedspan(sqInt bytecodepc, sqInt numArgs, sqInt numCopied, sqInt span); +static NoDbgRegParms void adjustArgumentsForPerform(sqInt numArgs); +static NoDbgRegParms sqInt allocateRegForStackEntryAtnotConflictingWith(sqInt index, sqInt regMask); +static NoDbgRegParms sqInt allocateRegNotConflictingWith(sqInt regMask); +static NoDbgRegParms sqInt anyReferencesToRegisterinTopNItems(sqInt reg, sqInt n); +extern void callCogCodePopReceiverArg0Regs(void); +extern void callCogCodePopReceiverArg1Arg0Regs(void); +static sqInt compileBlockBodies(void); +static NoDbgRegParms void compileBlockFrameBuild(BlockStart *blockStart); +static NoDbgRegParms void compileBlockFramelessEntry(BlockStart *blockStart); +static NoDbgRegParms sqInt compileBytecodesFromthrough(sqInt start, sqInt end); +static NoDbgRegParms CogMethod * compileCogFullBlockMethod(sqInt numCopied); +static NoDbgRegParms CogMethod * compileCogMethod(sqInt selector); +static sqInt compileEntireMethod(void); +static void compileFrameBuild(void); +static NoDbgRegParms void compileFullBlockFramelessEntry(sqInt numCopied); +static NoDbgRegParms void compileFullBlockMethodFrameBuild(sqInt numCopied); +#if IMMUTABILITY +static void compileTwoPathFrameBuild(void); +#endif /* IMMUTABILITY */ +static void compileTwoPathFramelessInit(void); +static NoDbgRegParms sqInt cPICMissTrampolineFor(sqInt numArgs); +static sqInt doubleExtendedDoAnythingBytecode(void); +static sqInt duplicateTopBytecode(void); +static NoDbgRegParms BytecodeFixup * ensureFixupAt(sqInt targetPC); +static NoDbgRegParms BytecodeFixup * ensureNonMergeFixupAt(sqInt targetPC); +static void ensureReceiverResultRegContainsSelf(void); +static NoDbgRegParms void evaluateat(BytecodeDescriptor *descriptor, sqInt pc); +static NoDbgRegParms sqInt eventualTargetOf(sqInt targetBytecodePC); +static NoDbgRegParms sqInt freeAnyRegNotConflictingWith(sqInt regMask); +static sqInt genBlockReturn(void); +static NoDbgRegParms void (*genCallPICEnilopmartNumArgs(sqInt numArgs))(void); +static sqInt genCallPrimitiveBytecode(void); +static sqInt genExternalizePointersForPrimitiveCall(void); +static AbstractInstruction * genExternalizeStackPointerForFastPrimitiveCall(void); +static sqInt genExtPushClosureBytecode(void); +static sqInt genExtPushFullClosureBytecode(void); +static void generateEnilopmarts(void); +static NoDbgRegParms sqInt generateInstructionsAt(sqInt eventualAbsoluteAddress); +static void generateMissAbortTrampolines(void); +static void generateSendTrampolines(void); +static void generateTracingTrampolines(void); +static NoDbgRegParms sqInt genForwardersInlinedIdenticalOrNotIf(sqInt orNot); +static NoDbgRegParms sqInt genIdenticalNoBranchArgIsConstantrcvrIsConstantargRegrcvrRegorNotIf(sqInt argIsConstant, sqInt rcvrIsConstant, sqInt argReg, sqInt rcvrRegOrNone, sqInt orNot); +static NoDbgRegParms sqInt genInlinedIdenticalOrNotIf(sqInt orNot); +static NoDbgRegParms sqInt genJumpBackTo(sqInt targetBytecodePC); +static NoDbgRegParms sqInt genJumpIfto(sqInt boolean, sqInt targetBytecodePC); +static NoDbgRegParms sqInt genJumpTo(sqInt targetBytecodePC); +static NoDbgRegParms sqInt genMarshalledSendnumArgssendTable(sqInt selectorIndex, sqInt numArgs, sqInt *sendTable); +static NoDbgRegParms usqInt genMethodAbortTrampolineFor(sqInt numArgs); +static NoDbgRegParms usqInt genPICAbortTrampolineFor(sqInt numArgs); +static NoDbgRegParms usqInt genPICMissTrampolineFor(sqInt numArgs); +static sqInt genPopStackBytecode(void); +static sqInt genPrimitiveClosureValue(void); +static sqInt genPrimitiveFullClosureValue(void); +static sqInt genPrimitivePerform(void); +static sqInt genPrimitivePerformWithArguments(void); +static sqInt genPushActiveContextBytecode(void); +static sqInt genPushClosureCopyCopiedValuesBytecode(void); +static NoDbgRegParms sqInt genPushLiteralIndex(sqInt literalIndex); +static NoDbgRegParms sqInt genPushLiteralVariable(sqInt literalIndex); +static NoDbgRegParms sqInt genPushMaybeContextReceiverVariable(sqInt slotIndex); +static sqInt genPushNewArrayBytecode(void); +static sqInt genPushReceiverBytecode(void); +static NoDbgRegParms sqInt genPushReceiverVariable(sqInt index); +static void genPushRegisterArgs(void); +static sqInt genPushRemoteTempLongBytecode(void); +static NoDbgRegParms sqInt genPushTemporaryVariable(sqInt index); +static sqInt genReturnReceiver(void); +static sqInt genReturnTopFromBlock(void); +static sqInt genReturnTopFromMethod(void); +static NoDbgRegParms sqInt genSendDirectedSupernumArgs(sqInt selectorIndex, sqInt numArgs); +static NoDbgRegParms sqInt genSendSupernumArgs(sqInt selectorIndex, sqInt numArgs); +static NoDbgRegParms usqInt genSendTrampolineFornumArgscalledargargargarg(void *aRoutine, sqInt numArgs, char *aString, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3); +static NoDbgRegParms sqInt genSendnumArgs(sqInt selectorIndex, sqInt numArgs); +static sqInt genSpecialSelectorArithmetic(void); +static sqInt genSpecialSelectorClass(void); +static sqInt genSpecialSelectorComparison(void); +static sqInt genStaticallyResolvedSpecialSelectorComparison(void); +static NoDbgRegParms sqInt genStorePopLiteralVariableneedsStoreCheckneedsImmutabilityCheck(sqInt popBoolean, sqInt litVarIndex, sqInt needsStoreCheck, sqInt needsImmCheck); +static NoDbgRegParms sqInt genStorePopMaybeContextReceiverVariableneedsStoreCheckneedsImmutabilityCheck(sqInt popBoolean, sqInt slotIndex, sqInt needsStoreCheck, sqInt needsImmCheck); +static NoDbgRegParms sqInt genVanillaInlinedIdenticalOrNotIf(sqInt orNot); +static NoDbgRegParms void initSimStackForFramelessBlock(sqInt startpc); +static NoDbgRegParms sqInt isNonForwarderReceiver(sqInt reg); +static sqInt liveRegisters(void); +static NoDbgRegParms sqInt mapDeadDescriptorIfNeeded(BytecodeDescriptor *descriptor); +static NoDbgRegParms void marshallSendArguments(sqInt numArgs); +static sqInt maybeCompilingFirstPassOfBlockWithInitialPushNil(void); +static NoDbgRegParms sqInt mergeWithFixupIfRequired(BytecodeFixup *fixup); +static NoDbgRegParms sqInt methodAbortTrampolineFor(sqInt numArgs); +static NoDbgRegParms sqInt needsFrameIfMod16GENumArgs(sqInt stackDelta); +static NoDbgRegParms sqInt needsFrameIfStackGreaterThanOne(sqInt stackDelta); +static NoDbgRegParms sqInt numberOfSpillsInTopNItems(sqInt n); +static NoDbgRegParms sqInt picAbortTrampolineFor(sqInt numArgs); +static void preserveTopInClassRegIfIsReceiverResultReg(void); +static sqInt prevInstIsPCAnnotated(void); +static int receiverIsInReceiverResultReg(void); +static NoDbgRegParms void reinitializeFixupsFromthrough(sqInt start, sqInt end); +static NoDbgRegParms sqInt scanBlock(BlockStart *blockStart); +static sqInt scanMethod(void); +static NoDbgRegParms sqInt squeakV3orSistaV1PushNilSizenumInitialNils(sqInt aMethodObj, sqInt numInitialNils); +static NoDbgRegParms sqInt squeakV3orSistaV1NumPushNils(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj); +static NoDbgRegParms void ssAllocateRequiredRegMaskupThroughupThroughNative(sqInt requiredRegsMask, sqInt stackPtr, sqInt nativeStackPtr); +static NoDbgRegParms void ssFlushUpThroughReceiverVariable(sqInt slotIndex); +static NoDbgRegParms void ssFlushUpThroughTemporaryVariable(sqInt tempIndex); +static NoDbgRegParms void ssPop(sqInt n); +static NoDbgRegParms sqInt ssPushAnnotatedConstant(sqInt literal); +static NoDbgRegParms sqInt ssPushBaseoffset(sqInt reg, sqInt offset); +static NoDbgRegParms sqInt ssPushConstant(sqInt literal); +static NoDbgRegParms sqInt ssPushDesc(SimStackEntry simStackEntry); +static NoDbgRegParms sqInt ssPushRegister(sqInt reg); +static NoDbgRegParms void ssPush(sqInt n); +static SimStackEntry ssSelfDescriptor(void); +static NoDbgRegParms void ssStoreAndReplacePoptoReg(sqInt popBoolean, sqInt reg); +static NoDbgRegParms sqInt ssStorePoptoPreferredReg(sqInt popBoolean, sqInt preferredReg); +static NoDbgRegParms void ssStorePoptoReg(sqInt popBoolean, sqInt reg); +static SimStackEntry * ssTop(void); +static NoDbgRegParms SimStackEntry * ssValue(sqInt n); +static NoDbgRegParms sqInt stackEntryIsBoolean(SimStackEntry *simStackEntry); +static sqInt tempsValidAndVolatileEntriesSpilled(void); +static NoDbgRegParms sqInt tryCollapseTempVectorInitializationOfSize(sqInt slots); +static sqInt violatesEnsureSpilledSpillAssert(void); +static void voidReceiverResultRegContainsSelf(void); + + +/*** Variables ***/ +static AbstractInstruction aMethodLabel; +static AbstractInstruction * abstractOpcodes; +static AbstractInstruction * const backEnd = &aMethodLabel; +static usqInt baseAddress; +static sqInt blockCount; +static AbstractInstruction * blockEntryLabel; +static AbstractInstruction * blockEntryNoContextSwitch; +static BlockStart * blockStarts; +static sqInt breakMethod; +static sqInt byte0; +static sqInt byte1; +static sqInt byte2; +static sqInt byte3; +static sqInt bytecodePC; +static sqInt bytecodeSetOffset; +static sqInt ceCollectArgumentsTypeFrameful; +static sqInt ceCollectArgumentsTypeFramelessSpilled; +static sqInt ceCollectArgumentsTypeFramelessUnspilled1Args; +static sqInt ceCollectArgumentsTypeFramelessUnspilled2Args; +static sqInt ceCollectInstVarType; +static sqInt ceCollectRemoteTempVarType; +static sqInt ceCollectReturnType; +static sqInt ceCollectTempVarType; +static sqInt ceCollectTypesForPrimitiveSlotAtPut; +static sqInt ceCPICMissTrampoline; +static sqInt ceDereferenceSelectorIndex; +static sqInt ceFetchContextInstVarTrampoline; +#if DUAL_MAPPED_CODE_ZONE +static void (*ceFlushDCache)(usqIntptr_t from, usqIntptr_t to); +#endif +static sqInt ceFlushICache; +static sqInt ceInlineNewHashTrampoline; +static sqInt ceLargeActiveContextInBlockTrampoline; +static sqInt ceLargeActiveContextInFullBlockTrampoline; +static sqInt ceLargeActiveContextInMethodTrampoline; +static sqInt ceMethodAbortTrampoline; +static sqInt ceNonLocalReturnTrampoline; +static sqInt cePICAbortTrampoline; +static sqInt cePrimReturnEnterCogCode; +static sqInt cePrimReturnEnterCogCodeProfiling; +static sqInt ceReapAndResetErrorCodeTrampoline; +static sqInt ceScheduleScavengeTrampoline; +static sqInt ceSendMustBeBooleanAddFalseTrampoline; +static sqInt ceSendMustBeBooleanAddTrueTrampoline; +static sqInt ceSmallActiveContextInBlockTrampoline; +static sqInt ceSmallActiveContextInFullBlockTrampoline; +static sqInt ceSmallActiveContextInMethodTrampoline; +static sqInt ceStoreCheckContextReceiverTrampoline; +static sqInt ceStoreCheckTrampoline; +static sqInt ceStoreContextInstVarTrampoline; +static sqInt ceTraceBlockActivationTrampoline; +static sqInt ceTraceLinkedSendTrampoline; +static sqInt ceTraceStoreTrampoline; +static sqInt checkedEntryAlignment; +static sqInt closedPICSize; +static usqInt codeBase; +static sqInt codeModified; +#if DUAL_MAPPED_CODE_ZONE +static sqInt codeToDataDelta; +#else +# define codeToDataDelta 0 +#endif +static sqInt cogConstituentIndex; +static sqInt compactionInProgress; +static sqInt compilationPass; +static sqInt compilationTrace; +static sqInt cPICCaseSize; +static sqInt cPICEndOfCodeOffset; +static sqInt cPICEndSize; +static CogMethod * cPICPrototype; +static usqIntptr_t cpuidWord1; +static sqInt currentCallCleanUpSize; +static sqInt deadCode; +static sqInt directedSendUsesBinding; +static sqInt directedSuperBindingSendTrampolines[NumSendTrampolines]; +static sqInt directedSuperSendTrampolines[NumSendTrampolines]; +static AbstractInstruction * endCPICCase0; +static sqInt endPC; +static AbstractInstruction * entry; +static sqInt entryPointMask; +static CogMethod * enumeratingCogMethod; +static sqInt expectedFPAlignment; +static sqInt expectedSPAlignment; +static sqInt extA; +static sqInt extB; +static sqInt firstCPICCaseOffset; +static sqInt firstSend; +static BytecodeFixup * fixups; +static AbstractInstruction * fullBlockEntry; +static AbstractInstruction * fullBlockNoContextSwitchEntry; +static BytecodeDescriptor generatorTable[512] = { + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushReceiverBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantTrueBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantFalseBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantNilBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushQuickIntegerConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushQuickIntegerConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushQuickIntegerConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushQuickIntegerConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genReturnReceiver, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnTrue, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnFalse, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnNil, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnTopFromMethod, 0, needsFrameIfInBlock, -1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnTopFromBlock, 0, needsFrameNever, -1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { extendedPushBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { extendedStoreBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 0, 0 }, + { extendedStoreAndPopBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 0, 0 }, + { genExtendedSendBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { doubleExtendedDoAnythingBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genExtendedSuperBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0 }, + { genSecondExtendedSendBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genPopStackBytecode, 0, needsFrameNever, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { duplicateTopBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushActiveContextBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushNewArrayBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genCallPrimitiveBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushRemoteTempLongBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreRemoteTempLongBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopRemoteTempLongBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushClosureCopyCopiedValuesBytecode, v3BlockCodeSize, 0, 0, 0, 4, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongUnconditionalBackwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongUnconditionalBackwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongUnconditionalBackwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongUnconditionalBackwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongUnconditionalForwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genLongUnconditionalForwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genLongUnconditionalForwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genLongUnconditionalForwardJump, v3LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genLongJumpIfTrue, v3LongForwardBranchDistance, 0, 0, 0, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongJumpIfTrue, v3LongForwardBranchDistance, 0, 0, 0, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongJumpIfTrue, v3LongForwardBranchDistance, 0, 0, 0, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongJumpIfTrue, v3LongForwardBranchDistance, 0, 0, 0, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongJumpIfFalse, v3LongForwardBranchDistance, 0, 0, 0, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongJumpIfFalse, v3LongForwardBranchDistance, 0, 0, 0, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongJumpIfFalse, v3LongForwardBranchDistance, 0, 0, 0, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genLongJumpIfFalse, v3LongForwardBranchDistance, 0, 0, 0, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, AddRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, SubRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpLess, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpGreater, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpLessOrEqual, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpGreaterOrEqual, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpZero, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpNonZero, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, AndRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, OrRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorEqualsEquals, 0, needsFrameNever, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genSpecialSelectorClass, 0, needsFrameIfStackGreaterThanOne, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genSpecialSelectorNotEqualsEquals, 0, needsFrameNever, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushReceiverVariableBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralVariable16CasesBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushLiteralConstantBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushTemporaryVariableBytecode, 0, needsFrameIfMod16GENumArgs, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushReceiverBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantTrueBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantFalseBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantNilBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantZeroBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPushConstantOneBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genExtPushPseudoVariable, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { duplicateTopBytecode, 0, needsFrameNever, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { genReturnReceiver, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnTrue, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnFalse, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnNil, 0, needsFrameIfInBlock, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnTopFromMethod, 0, needsFrameIfInBlock, -1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0 }, + { genReturnNilFromBlock, 0, needsFrameNever, -1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 }, + { genReturnTopFromBlock, 0, needsFrameNever, -1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 }, + { genExtNopBytecode, 0, needsFrameNever, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, AddRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, SubRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpLess, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpGreater, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpLessOrEqual, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpGreaterOrEqual, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpZero, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorComparison, 0, 0, 0, JumpNonZero, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, AndRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorArithmetic, 0, 0, 0, OrRR, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorEqualsEquals, 0, needsFrameNever, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genSpecialSelectorClass, 0, needsFrameIfStackGreaterThanOne, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genSpecialSelectorNotEqualsEquals, 0, needsFrameNever, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSpecialSelectorSend, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector0ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector1ArgBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genSendLiteralSelector2ArgsBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortUnconditionalJump, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfTrue, v3ShortForwardBranchDistance, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genShortJumpIfFalse, v3ShortForwardBranchDistance, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopReceiverVariableBytecode, 0, needsFrameIfImmutability, -1, 0, 1, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 1, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genPopStackBytecode, 0, needsFrameNever, -1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genUnconditionalTrapBytecode, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { extABytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { extBBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { genExtPushReceiverVariableBytecode, 0, needsFrameIfReadMediated, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 }, + { genExtPushLiteralVariableBytecode, 0, needsFrameNever, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genExtPushLiteralBytecode, 0, needsFrameNever, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genLongPushTemporaryVariableBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { genPushNewArrayBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genExtPushIntegerBytecode, 0, needsFrameNever, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genExtPushCharacterBytecode, 0, needsFrameNever, 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genExtSendBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genExtSendSuperBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genCallMappedInlinedPrimitive, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1 }, + { genExtUnconditionalJump, v4LongBranchDistance, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genExtJumpIfTrue, v4LongBranchDistance, 0, 0, 0, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genExtJumpIfFalse, v4LongBranchDistance, 0, 0, 0, 2, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0 }, + { genExtStoreAndPopReceiverVariableBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 0, 0 }, + { genExtStoreAndPopLiteralVariableBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 0, 0, 0 }, + { genLongStoreAndPopTemporaryVariableBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genExtStoreReceiverVariableBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 1, 0, 0 }, + { genExtStoreLiteralVariableBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, IMMUTABILITY, 0, 0, 0, 0, 0 }, + { genLongStoreTemporaryVariableBytecode, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { genCallPrimitiveBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, + { genExtPushFullClosureBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genExtPushClosureBytecode, v4BlockCodeSize, 0, 0, 0, 3, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }, + { genPushRemoteTempLongBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreRemoteTempLongBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { genStoreAndPopRemoteTempLongBytecode, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 }, + { unknownBytecode, 0, 0, 0, Nop, 3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 } +}; +static sqInt guardPageSize; +static sqInt hasMovableLiteral; +static sqInt hasYoungReferent; +static sqInt inBlock; +static sqInt initialPC; +static sqInt introspectionData; +static sqInt introspectionDataIndex; +static sqInt lastSend; +static usqInt limitAddress; +static sqInt maxLitIndex; +static sqInt methodAbortTrampolines[4]; +static sqInt methodBytesFreedSinceLastCompaction; +static sqInt methodCount; +static sqInt methodHeader; +static AbstractInstruction * const methodLabel = &aMethodLabel; +static sqInt methodObj; +static sqInt methodOrBlockNumArgs; +static sqInt methodOrBlockNumTemps; +static usqIntptr_t minValidCallAddress; +static usqInt mzFreeStart; +static sqInt needsFrame; +static sqInt nInstructionsLimit; +static AbstractInstruction * noCheckEntry; +static sqInt numAbstractOpcodes; +static sqInt numExtB; +static usqInt objectReferencesInRuntime[NumObjRefsInRuntime+1]; +static sqInt opcodeIndex; +static CogMethod *openPICList = 0; +static sqInt openPICSize; +static sqInt ordinarySendTrampolines[NumSendTrampolines]; +static sqInt picAbortTrampolines[4]; +static AbstractInstruction * picInterpretAbort; +static sqInt picMissTrampolines[4]; +static AbstractInstruction * picMNUAbort; +static BytecodeDescriptor * prevBCDescriptor; +static PrimitiveDescriptor primitiveGeneratorTable[MaxCompiledPrimitiveIndex+1] = { + { 0, -1 }, + { genPrimitiveAdd, 1 }, + { genPrimitiveSubtract, 1 }, + { genPrimitiveLessThan, 1 }, + { genPrimitiveGreaterThan, 1 }, + { genPrimitiveLessOrEqual, 1 }, + { genPrimitiveGreaterOrEqual, 1 }, + { genPrimitiveEqual, 1 }, + { genPrimitiveNotEqual, 1 }, + { genPrimitiveMultiply, 1 }, + { genPrimitiveDivide, 1 }, + { genPrimitiveMod, 1 }, + { genPrimitiveDiv, 1 }, + { genPrimitiveQuo, 1 }, + { genPrimitiveBitAnd, 1 }, + { genPrimitiveBitOr, 1 }, + { genPrimitiveBitXor, 1 }, + { genPrimitiveBitShift, 1 }, + { genPrimitiveMakePoint, 1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveAsFloat, 0 }, + { genPrimitiveFloatAdd, 1 }, + { genPrimitiveFloatSubtract, 1 }, + { genPrimitiveFloatLessThan, 1 }, + { genPrimitiveFloatGreaterThan, 1 }, + { genPrimitiveFloatLessOrEqual, 1 }, + { genPrimitiveFloatGreaterOrEqual, 1 }, + { genPrimitiveFloatEqual, 1 }, + { genPrimitiveFloatNotEqual, 1 }, + { genPrimitiveFloatMultiply, 1 }, + { genPrimitiveFloatDivide, 1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveFloatSquareRoot, 0 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveAt, 1 }, + { genPrimitiveAtPut, 2 }, + { genPrimitiveSize, 0 }, + { genPrimitiveStringAt, 1 }, + { genPrimitiveStringAtPut, 2 }, + { genFastPrimFail, -1 }, + { genFastPrimFail, -1 }, + { genFastPrimFail, -1 }, + { genPrimitiveObjectAt, 1 }, + { 0, -1 }, + { genPrimitiveNew, 0 }, + { genPrimitiveNewWithArg, 1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveIdentityHash, 0 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveNewMethod, 2 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitivePerform, -1 }, + { genPrimitivePerformWithArguments, 2 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveStringReplace, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveIdentical, 1 }, + { genPrimitiveClass, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveShallowCopy, 0 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveStringCompareWith, 1 }, + { genPrimitiveHashMultiply, 0 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveIntegerAt, 1 }, + { genPrimitiveIntegerAtPut, 2 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveNotIdentical, 1 }, + { genPrimitiveAsCharacter, -1 }, + { genPrimitiveImmediateAsInteger, 0 }, + { 0, -1 }, + { genPrimitiveSlotAt, 1 }, + { genPrimitiveSlotAtPut, 2 }, + { genPrimitiveIdentityHash, 0 }, + { genPrimitiveMaxIdentityHash, 0 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genFastPrimFail, -1 }, + { genFastPrimFail, -1 }, + { 0, -1 }, + { genPrimitiveClosureValue, 0 }, + { genPrimitiveClosureValue, 1 }, + { genPrimitiveClosureValue, 2 }, + { genPrimitiveClosureValue, 3 }, + { genPrimitiveClosureValue, 4 }, + { 0, -1 }, + { genPrimitiveFullClosureValue, -1 }, + { 0, -1 }, + { genPrimitiveFullClosureValue, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveClosureValue, 0 }, + { genPrimitiveClosureValue, 1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveSmallFloatAdd, 1 }, + { genPrimitiveSmallFloatSubtract, 1 }, + { genPrimitiveSmallFloatLessThan, 1 }, + { genPrimitiveSmallFloatGreaterThan, 1 }, + { genPrimitiveSmallFloatLessOrEqual, 1 }, + { genPrimitiveSmallFloatGreaterOrEqual, 1 }, + { genPrimitiveSmallFloatEqual, 1 }, + { genPrimitiveSmallFloatNotEqual, 1 }, + { genPrimitiveSmallFloatMultiply, 1 }, + { genPrimitiveSmallFloatDivide, 1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveSmallFloatSquareRoot, 0 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveHighBit, 0 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { 0, -1 }, + { genPrimitiveUninitializedNewWithArg, 1 } +}; +static sqInt primitiveIndex; +static sqInt receiverTags; +static sqInt regArgsHaveBeenPushed; +static sqInt runtimeObjectRefIndex; +static AbstractInstruction * sendMiss; +static sqInt simNativeStackPtr; +static sqInt simSpillBase; +static SimStackEntry simStack[70]; +static sqInt simStackPtr; +static AbstractInstruction * stackCheckLabel; +static AbstractInstruction * stackOverflowCall; +static sqInt superSendTrampolines[NumSendTrampolines]; +static sqInt tempOop; +static char *trampolineAddresses[NumTrampolines*2]; +static sqInt trampolineTableIndex; +static sqInt uncheckedEntryAlignment; +static usqInt unpairedMethodList; +static sqInt useTwoPaths; +static sqInt varBaseAddress; +static usqInt youngReferrers; +static const int cStackAlignment = STACK_ALIGN_BYTES & STACK_ALIGN_MASK; +static int labelCounter; +#if IMMUTABILITY +static sqInt ceStoreTrampolines[5];; +#endif +sqInt blockNoContextSwitchOffset; +sqInt cbEntryOffset; +sqInt cbNoSwitchEntryOffset; +sqInt ceBaseFrameReturnTrampoline; +void (*ceCall0ArgsPIC)(void); +void (*ceCall1ArgsPIC)(void); +void (*ceCall2ArgsPIC)(void); +void (*ceCallCogCodePopReceiverAndClassRegs)(void); +void (*ceCallCogCodePopReceiverArg0Regs)(void); +void (*ceCallCogCodePopReceiverArg1Arg0Regs)(void); +void (*ceCallCogCodePopReceiverReg)(void); +sqInt ceCannotResumeTrampoline; +void (*ceCaptureCStackPointers)(void); +sqInt ceCheckForInterruptTrampoline; +void (*ceEnterCogCodePopReceiverReg)(void); +usqIntptr_t (*ceGetFP)(void); +usqIntptr_t (*ceGetSP)(void); +void (*ceInvokeInterpret)(void); +sqInt ceReturnToInterpreterTrampoline; +#if !defined(cFramePointerInUse) +sqInt cFramePointerInUse; +#endif +sqInt cmEntryOffset; +sqInt cmNoCheckEntryOffset; +usqInt methodZoneBase; +sqInt missOffset; +void (*realCECallCogCodePopReceiverAndClassRegs)(void); +void (*realCECallCogCodePopReceiverArg0Regs)(void); +void (*realCECallCogCodePopReceiverArg1Arg0Regs)(void); +void (*realCECallCogCodePopReceiverReg)(void); +void (*realCEEnterCogCodePopReceiverReg)(void); +const char * traceFlagsMeanings[] = { + "1: print trace", "2: trace sends", "4: trace block activations", "8: trace interpreter primitives", + "16: trace VM events (context switches, GCs, etc)", "32: trace stack overflow (poll for events hook)", + "64: trace linked sends", "128: trace fast C call interpreter primitives", "256: trace FFI/thread events", + null + }; +sqInt traceStores; +int traceFlags = 8 /* prim trace log on by default */; + + +/*** Macros ***/ +#define roundUpToMethodAlignment(ignored,numBytes) ((numBytes) + 7 & -8) +#define cPICNumCases stackCheckOffset +#define cPICNumCasesHack hack hack hack i.e. the getter macro does all the work +#define abstractInstructionAt(index) (&abstractOpcodes[index]) +#define addressIsInInstructions(address) (!((usqInt)(address) & (BytesPerWord-1)) \ + && (address) >= &abstractOpcodes[0] \ + && (address) < &abstractOpcodes[opcodeIndex]) +#define allocateBlockStarts(numBlocks) do { \ + blockStarts = (numBlocks) ? alloca(sizeof(BlockStart) * (numBlocks)) : 0; \ +} while (0) +#define assertValidDualZone() true +#define assertValidDualZoneReadAddress(address) 0 +#define assertValidDualZoneWriteAddress(address) 0 +#define backEnd() backEnd +#define blockAlignment() 8 +#define blockStartAt(index) (&blockStarts[index]) +#define bump64BitPerfCounterByUsecs(usecs) 0 // simulation only +#define ceBaseFrameReturnPC() ceBaseFrameReturnTrampoline +#define ceCannotResumePC() ((usqInt)ceCannotResumeTrampoline) +#define ceCheckForInterruptTrampoline() ceCheckForInterruptTrampoline +#define ceReturnToInterpreterPC() ((usqInt)ceReturnToInterpreterTrampoline) +#define codeByteAtput(address,value) byteAtput((char *)(address) + codeToDataDelta, value) +#define codeLong32Atput(address,value) long32Atput((char *)(address) + codeToDataDelta, value) +#define codeLong64Atput(address,value) long64Atput((char *)(address) + codeToDataDelta, value) +#define codeLongAtput(address,value) longAtput((char *)(address) + codeToDataDelta, value) +#define codeMemcpy(dest,src,bytes) memcpy(dest,src,bytes) +#define codeMemmove(dest,src,bytes) memmove((char *)(dest)+codeToDataDelta,src,bytes) +#define cr() putchar('\n') +#if !defined(ensureExecutableCodeZoneAt) +#define ensureExecutableCodeZoneAt(underscoreLine) 0 +#endif /* !defined(ensureExecutableCodeZoneAt) */ +#if !defined(ensureWritableCodeZoneAt) +#define ensureWritableCodeZoneAt(underscoreLine) 0 +#endif /* !defined(ensureWritableCodeZoneAt) */ +#define entryOffset() cmEntryOffset +#define generatorAt(index) (&generatorTable[index]) +#define getCodeToDataDelta() codeToDataDelta +#define getIsObjectReference() 2 +#define halt() warning("halt") +#define haltmsg(msg) warning("halt: " msg) +#define interpretOffset() missOffset +#define mapPerMethodProfile() 0 +#define maxCogCodeSize() (16*1024*1024) +#define maybeBreakGeneratingFromto(address,end) 0 +#define maybeBreakGeneratingInstructionWithIndex(i) 0 +#define maybeHaltIfDebugPC() 0 +#define methodLabel() methodLabel +#define methodZoneBase() methodZoneBase +#define minCogMethodAddress() methodZoneBase +#define moveProfileToMethods() 0 +#define noCheckEntryOffset() cmNoCheckEntryOffset +#define noContextSwitchBlockEntryOffset() blockNoContextSwitchOffset +#define notYetImplemented() warning("not yet implemented") +#define null 0 +#define printNum(n) printf("%" PRIdSQINT, (sqInt) (n)) +#define printOnTrace() (traceFlags & 1) +#define recordBlockTrace() (traceFlags & 4) +#define recordEventTrace() (traceFlags & 16) +#define recordFastCCallPrimTrace() (traceFlags & 128) +#define recordFFIOrThreadTrace() (traceFlags & 256) +#define recordOverflowTrace() (traceFlags & 32) +#define recordPrimTrace() (traceFlags & 8) +#define recordSendTrace() (traceFlags & 2) +#define reportError(n) warning("compilation error") +#define setHasMovableLiteral(b) (hasMovableLiteral = (b)) +#define setHasYoungReferent(b) (hasYoungReferent = (b)) +#define varBaseAddress() varBaseAddress +#define nextOpenPIC methodObject +#define nextOpenPICHack hack hack hack i.e. the getter macro does all the work +#define freeStart() mzFreeStart +#define limitZony() ((CogMethod *)mzFreeStart) +#define methodBytesFreedSinceLastCompaction() methodBytesFreedSinceLastCompaction +#define youngReferrers() youngReferrers +#define numRegArgs() 2 +#define maybeConstant(sse) ((sse)->constant) +#define cpuidWord1(ign) cpuidWord1 +#define flushDCacheFromto(me,startAddress,endAddress) 0 +#define flushICacheFromto(me,startAddress,endAddress) 0 +#define inlineCacheValueForSelectorin(backEnd,selector,aCogMethod) indexForSelectorin(selector,aCogMethod) +#define setCpuidWord1(ign,word) cpuidWord1 = word +#define unalignedLong32At(byteAddress) long32At(byteAddress) +#define unalignedLong32Atput(byteAddress,aWord) long32Atput(byteAddress,aWord) +#define unalignedLongAt(byteAddress) longAt(byteAddress) +#define unalignedLongAtput(byteAddress,aWord) longAtput(byteAddress,aWord) +#define fullBlockEntryOffset() cbEntryOffset +#define fullBlockNoContextSwitchEntryOffset() cbNoSwitchEntryOffset +#define needsFrame() needsFrame +#define fixupAtIndex(index) (&fixups[index]) +#define simNativeStackAt(index) (simNativeStack + (index)) +#define simSelf() simStack +#define simStackAt(index) (simStack + (index)) +#define traceDescriptor(ign) 0 +#define traceFixupmerge(igu,ana) 0 +#define traceMerge(ign) 0 +#define traceSimStack() 0 +#define traceSpill(ign) 0 +#define allocatype(numElements, elementType) alloca((numElements)*sizeof(elementType)) +#define numElementsIn(anArray) (sizeof(anArray)/sizeof(anArray[0])) +#define oopisGreaterThanOrEqualTo(anOop,otherOop) ((usqInt)(anOop) >= (usqInt)(otherOop)) +#define oopisGreaterThanOrEqualToandLessThanOrEqualTo(anOop,baseOop,limitOop) ((usqInt)(anOop) >= (usqInt)(baseOop) && (usqInt)(anOop) <= (usqInt)(limitOop)) +#define oopisGreaterThanOrEqualToandLessThan(anOop,baseOop,limitOop) ((usqInt)(anOop) >= (usqInt)(baseOop) && (usqInt)(anOop) < (usqInt)(limitOop)) +#define oopisGreaterThan(anOop,otherOop) ((usqInt)(anOop) > (usqInt)(otherOop)) +#define oopisGreaterThanandLessThan(anOop,baseOop,limitOop) ((usqInt)(anOop) > (usqInt)(baseOop) && (usqInt)(anOop) < (usqInt)(limitOop)) +#define oopisLessThanOrEqualTo(anOop,otherOop) ((usqInt)(anOop) <= (usqInt)(otherOop)) +#define oopisLessThan(anOop,otherOop) ((usqInt)(anOop) < (usqInt)(otherOop)) + + +/*** Methods ***/ + + /* CogAbstractInstruction>>#addDependent: */ +static NoDbgRegParms AbstractInstruction * +addDependent(AbstractInstruction *self_in_CogAbstractInstruction, AbstractInstruction *anInstruction) +{ + if ((self_in_CogAbstractInstruction->dependent)) { + (anInstruction->dependent = (self_in_CogAbstractInstruction->dependent)); + } + return ((self_in_CogAbstractInstruction->dependent) = anInstruction); +} + + +/* Answer an unused abstract register in the liveRegMask. + Subclasses with more registers can override to answer them. */ + + /* CogAbstractInstruction>>#availableFloatRegisterOrNoneFor: */ +static NoDbgRegParms sqInt +availableFloatRegisterOrNoneFor(AbstractInstruction *self_in_CogAbstractInstruction, sqInt liveRegsMask) +{ + if (!(((liveRegsMask & ((1U << DPFPReg0))) != 0))) { + return DPFPReg0; + } + if (!(((liveRegsMask & ((1U << DPFPReg1))) != 0))) { + return DPFPReg1; + } + if (!(((liveRegsMask & ((1U << DPFPReg2))) != 0))) { + return DPFPReg2; + } + if (!(((liveRegsMask & ((1U << DPFPReg3))) != 0))) { + return DPFPReg3; + } + if (!(((liveRegsMask & ((1U << DPFPReg4))) != 0))) { + return DPFPReg4; + } + if (!(((liveRegsMask & ((1U << DPFPReg5))) != 0))) { + return DPFPReg5; + } + if (!(((liveRegsMask & ((1U << DPFPReg6))) != 0))) { + return DPFPReg6; + } + if (!(((liveRegsMask & ((1U << DPFPReg7))) != 0))) { + return DPFPReg7; + } + return NoReg; +} + + +/* For out-of-line literal support, clone a literal from a literal. */ + + /* CogAbstractInstruction>>#cloneLiteralFrom: */ +static NoDbgRegParms AbstractInstruction * +cloneLiteralFrom(AbstractInstruction *self_in_CogAbstractInstruction, AbstractInstruction *existingLiteral) +{ + assert((((existingLiteral->opcode)) == Literal) + && ((!((self_in_CogAbstractInstruction->dependent))) + && (!((self_in_CogAbstractInstruction->address))))); + (self_in_CogAbstractInstruction->opcode) = Literal; + (self_in_CogAbstractInstruction->annotation) = (existingLiteral->annotation); + ((self_in_CogAbstractInstruction->operands))[0] = (((existingLiteral->operands))[0]); + ((self_in_CogAbstractInstruction->operands))[1] = (((existingLiteral->operands))[1]); + ((self_in_CogAbstractInstruction->operands))[2] = (((existingLiteral->operands))[2]); + return self_in_CogAbstractInstruction; +} + + +/* Generate concrete machine code for the instruction at actualAddress, + setting machineCodeSize, and answer the following address. */ + + /* CogAbstractInstruction>>#concretizeAt: */ +static NoDbgRegParms sqInt +concretizeAt(AbstractInstruction *self_in_CogAbstractInstruction, sqInt actualAddress) +{ + (self_in_CogAbstractInstruction->address) = actualAddress; + (self_in_CogAbstractInstruction->machineCodeSize) = dispatchConcretize(self_in_CogAbstractInstruction); + assert((((self_in_CogAbstractInstruction->maxSize)) == null) + || (((self_in_CogAbstractInstruction->maxSize)) >= ((self_in_CogAbstractInstruction->machineCodeSize)))); + return actualAddress + ((self_in_CogAbstractInstruction->machineCodeSize)); +} + + +/* Load the stack pointer register with that of the C stack, effecting + a switch to the C stack. Used when machine code calls into the + CoInterpreter run-time (e.g. to invoke interpreter primitives). */ + + /* CogAbstractInstruction>>#genLoadCStackPointer */ +static NoDbgRegParms sqInt +genLoadCStackPointer(AbstractInstruction *self_in_CogAbstractInstruction) +{ + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, cStackPointerAddress(), NativeSPReg); + return 0; +} + + +/* Load the frame and stack pointer registers with those of the C stack, + effecting a switch to the C stack. Used when machine code calls into + the CoInterpreter run-time (e.g. to invoke interpreter primitives). + N.B. CoInterpreter stack layout dictates that the stack pointer should be + loaded first. + The stack zone is allocated on the C stack before the interpreter runs and + hence before CStackPointer and CFramePointer are captured. So when running + in machine + code the native stack pointer and frame pointer appear to be on a colder + part of the + stack to CStackPointer and CFramePointer. When CStackPointerhas been set + and the frame pointer is still in machine code the current frame looks + like it has lots of + stack. If the frame pointer was set to CFramePointer before hand then it + would be beyond the stack pointer for that one instruction. */ + + /* CogAbstractInstruction>>#genLoadCStackPointers */ +static NoDbgRegParms sqInt +genLoadCStackPointers(AbstractInstruction *self_in_CogAbstractInstruction) +{ + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, cStackPointerAddress(), NativeSPReg); + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, cFramePointerAddress(), FPReg); + return 0; +} + + +/* Switch back to the Smalltalk stack where there may be a C return address + on top of stack below + the last primitive argument. Assign SPReg first because typically it is + used immediately afterwards. + */ + + /* CogAbstractInstruction>>#genLoadStackPointerForPrimCall: */ +static NoDbgRegParms sqInt +genLoadStackPointerForPrimCall(AbstractInstruction *self_in_CogAbstractInstruction, sqInt spareReg) +{ + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, stackPointerAddress(), spareReg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, BytesPerWord, spareReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, spareReg, SPReg); + return 0; +} + + +/* Switch back to the Smalltalk stack. Assign SPReg first + because typically it is used immediately afterwards. */ + + /* CogAbstractInstruction>>#genLoadStackPointers */ +static NoDbgRegParms sqInt +genLoadStackPointers(AbstractInstruction *self_in_CogAbstractInstruction) +{ + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, stackPointerAddress(), SPReg); + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, framePointerAddress(), FPReg); + return 0; +} + + +/* Switch back to the Smalltalk stack where there may be a C return address + on top of stack below + the last primitive argument. Assign SPReg first because typically it is + used immediately afterwards. + */ + + /* CogAbstractInstruction>>#genLoadStackPointersForPrimCall: */ +static NoDbgRegParms sqInt +genLoadStackPointersForPrimCall(AbstractInstruction *self_in_CogAbstractInstruction, sqInt spareReg) +{ + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, stackPointerAddress(), spareReg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, BytesPerWord, spareReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, spareReg, SPReg); + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, framePointerAddress(), FPReg); + return 0; +} + + +/* Save the frame and stack pointer registers to the framePointer + and stackPointer variables. Used to save the machine code frame + for use by the run-time when calling into the CoInterpreter run-time. */ + + /* CogAbstractInstruction>>#genSaveStackPointers */ +static NoDbgRegParms sqInt +genSaveStackPointers(AbstractInstruction *self_in_CogAbstractInstruction) +{ + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, FPReg, framePointerAddress()); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, SPReg, stackPointerAddress()); + return 0; +} + + /* CogAbstractInstruction>>#genWriteCResultIntoReg: */ +static NoDbgRegParms AbstractInstruction * +genWriteCResultIntoReg(AbstractInstruction *self_in_CogAbstractInstruction, sqInt abstractRegister) +{ + if ((abstractRegister != NoReg) + && (abstractRegister != ABIResultReg)) { + genoperandoperand(MoveRR, ABIResultReg, abstractRegister); + } + return self_in_CogAbstractInstruction; +} + + +/* At least x86, x86_64, AArch32 and AArch64 have a 64-bit performance + counter. Subclasses can turn this off if rquired. */ + + /* CogAbstractInstruction>>#has64BitPerformanceCounter */ +static NoDbgRegParms sqInt +has64BitPerformanceCounter(AbstractInstruction *self_in_CogAbstractInstruction) +{ + return 1; +} + + +/* For out-of-line literal support, initialize a sharable literal. */ + + /* CogAbstractInstruction>>#initializeSharableLiteral: */ +static NoDbgRegParms AbstractInstruction * +initializeSharableLiteral(AbstractInstruction *self_in_CogAbstractInstruction, sqInt literal) +{ + (self_in_CogAbstractInstruction->opcode) = Literal; + + /* separate := nil for Slang */ + (self_in_CogAbstractInstruction->annotation) = null; + (self_in_CogAbstractInstruction->address) = null; + (self_in_CogAbstractInstruction->dependent) = null; + ((self_in_CogAbstractInstruction->operands))[0] = literal; + ((self_in_CogAbstractInstruction->operands))[1] = (1 + ((((usqInt)(BytesPerOop) << 1)))); + ((self_in_CogAbstractInstruction->operands))[2] = -1; + return self_in_CogAbstractInstruction; +} + + +/* For out-of-line literal support, initialize an unsharable literal. */ + + /* CogAbstractInstruction>>#initializeUniqueLiteral: */ +static NoDbgRegParms AbstractInstruction * +initializeUniqueLiteral(AbstractInstruction *self_in_CogAbstractInstruction, sqInt literal) +{ + (self_in_CogAbstractInstruction->opcode) = Literal; + + /* separate := nil for Slang */ + (self_in_CogAbstractInstruction->annotation) = null; + (self_in_CogAbstractInstruction->address) = null; + (self_in_CogAbstractInstruction->dependent) = null; + ((self_in_CogAbstractInstruction->operands))[0] = literal; + ((self_in_CogAbstractInstruction->operands))[1] = (0 + ((((usqInt)(BytesPerOop) << 1)))); + ((self_in_CogAbstractInstruction->operands))[2] = -1; + return self_in_CogAbstractInstruction; +} + + +/* This need only be implemented if a subclass uses out-of-line literals. + Currently this is true only of ARM processors that have fixed length + instructions. + */ + + /* CogAbstractInstruction>>#instructionIsPCRelativeLoad: */ +static NoDbgRegParms sqInt +instructionIsPCRelativeLoad(AbstractInstruction *self_in_CogAbstractInstruction, sqInt instr) +{ + return 0; +} + + /* CogAbstractInstruction>>#isJump */ +static NoDbgRegParms int +isJump(AbstractInstruction *self_in_CogAbstractInstruction) +{ + return ((((self_in_CogAbstractInstruction->opcode)) >= FirstJump) && (((self_in_CogAbstractInstruction->opcode)) <= LastJump)); +} + + +/* Always implement this to avoid a check in bump64BitPerfCounterByUsecs:. + Assume a cycle count. Processors using less that a cycle count override. */ + + /* CogAbstractInstruction>>#perfCounterRatePerUsec */ +static NoDbgRegParms sqInt +perfCounterRatePerUsec(AbstractInstruction *self_in_CogAbstractInstruction) +{ + return 1000; +} + + +/* We assume here that calls and jumps look the same as regards their + displacement. This works on at least x86, ARM and x86_64. Processors on + which that isn't the + case can override as necessary. */ + + /* CogAbstractInstruction>>#relocateJumpLongBeforeFollowingAddress:by: */ +static NoDbgRegParms AbstractInstruction * +relocateJumpLongBeforeFollowingAddressby(AbstractInstruction *self_in_CogAbstractInstruction, sqInt pc, sqInt delta) +{ + relocateCallBeforeReturnPCby(self_in_CogAbstractInstruction, pc, delta); + return self_in_CogAbstractInstruction; +} + + +/* Relocate a long conditional jump before pc. Default to relocating a + non-conditional jump. + Processors that have different formats for conditional and unconditional + jumps override. */ + + /* CogAbstractInstruction>>#relocateJumpLongConditionalBeforeFollowingAddress:by: */ +static NoDbgRegParms AbstractInstruction * +relocateJumpLongConditionalBeforeFollowingAddressby(AbstractInstruction *self_in_CogAbstractInstruction, sqInt pc, sqInt delta) +{ + relocateJumpLongBeforeFollowingAddressby(self_in_CogAbstractInstruction, pc, delta); + return self_in_CogAbstractInstruction; +} + + /* CogAbstractInstruction>>#resolveJumpTarget */ +static NoDbgRegParms AbstractInstruction * +resolveJumpTarget(AbstractInstruction *self_in_CogAbstractInstruction) +{ + BytecodeFixup *fixup; + + assert(isJump(self_in_CogAbstractInstruction)); + fixup = ((BytecodeFixup *) (((self_in_CogAbstractInstruction->operands))[0])); + if (addressIsInFixups(fixup)) { + assert(addressIsInInstructions((fixup->targetInstruction))); + jmpTarget(self_in_CogAbstractInstruction, (fixup->targetInstruction)); + } + return self_in_CogAbstractInstruction; +} + + +/* Rewrite a conditional jump long to jump to target. This version defaults + to using + rewriteJumpLongAt:, which works for many ISAs. Subclasses override if + necessary. */ + + /* CogAbstractInstruction>>#rewriteConditionalJumpLongAt:target: */ +static NoDbgRegParms sqInt +rewriteConditionalJumpLongAttarget(AbstractInstruction *self_in_CogAbstractInstruction, sqInt callSiteReturnAddress, sqInt callTargetAddress) +{ + return rewriteCallAttarget(self_in_CogAbstractInstruction, callSiteReturnAddress, callTargetAddress); +} + + +/* It is assumed that a PC-relative load moves wordSize bytes. Subclasses + override as appropriate. + */ + + /* CogAbstractInstruction>>#widthOfPCRelativeLoad: */ +static NoDbgRegParms sqInt +widthOfPCRelativeLoad(AbstractInstruction *self_in_CogAbstractInstruction, sqInt instr) +{ + assert(instructionIsPCRelativeLoad(self_in_CogAbstractInstruction, instr)); + return BytesPerWord; +} + + /* CogBlockMethod>>#cmHomeMethod */ +static NoDbgRegParms CogMethod * +cmHomeMethod(CogBlockMethod *self_in_CogBlockMethod) +{ + return (((self_in_CogBlockMethod->cmType)) >= CMMethod + ? ((CogMethod *) self_in_CogBlockMethod) + : (assert(CMBlock == (self_in_CogBlockMethod->cmType)), + ((CogMethod *) ((((usqInt)self_in_CogBlockMethod)) - ((self_in_CogBlockMethod->homeOffset)))))); +} + + /* CogBlockMethod>>#isCMBlock */ +static NoDbgRegParms int +isCMBlock(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMBlock; +} + + /* CogBlockMethod>>#isCMClosedPIC */ +static NoDbgRegParms int +isCMClosedPIC(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMClosedPIC; +} + + /* CogBlockMethod>>#isCMFree */ +static NoDbgRegParms int +isCMFree(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMFree; +} + + /* CogBlockMethod>>#isCMMethodEtAl */ +static NoDbgRegParms int +isCMMethodEtAl(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) >= CMMethod; +} + + /* CogBlockMethod>>#isCMOpenPIC */ +static NoDbgRegParms int +isCMOpenPIC(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMOpenPIC; +} + + /* CogBytecodeDescriptor>>#isBranch */ +static NoDbgRegParms sqInt +isBranch(BytecodeDescriptor *self_in_CogBytecodeDescriptor) +{ + return (((self_in_CogBytecodeDescriptor->spanFunction))) + && (!((self_in_CogBytecodeDescriptor->isBlockCreation))); +} + + /* CogBytecodeDescriptor>>#isConditionalBranch */ +static NoDbgRegParms sqInt +isConditionalBranch(BytecodeDescriptor *self_in_CogBytecodeDescriptor) +{ + return ((self_in_CogBytecodeDescriptor->isBranchTrue)) + || ((self_in_CogBytecodeDescriptor->isBranchFalse)); +} + + +/* With CqR we assume constants are 32-bits or less. */ + + /* CogInLineLiteralsX64Compiler>>#computeSizeOfArithCqR */ +static NoDbgRegParms int +computeSizeOfArithCqR(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler) +{ + if (isQuick(self_in_CogInLineLiteralsX64Compiler, ((self_in_CogInLineLiteralsX64Compiler->operands))[0])) { + return 4; + } + if (is32BitSignedImmediate(self_in_CogInLineLiteralsX64Compiler, ((self_in_CogInLineLiteralsX64Compiler->operands))[0])) { + return (((self_in_CogInLineLiteralsX64Compiler->operands))[1] + ? 7 + : 6); + } + return 13; +} + + /* CogInLineLiteralsX64Compiler>>#computeSizeOfArithCwR */ +static NoDbgRegParms int +computeSizeOfArithCwR(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler) +{ + return 13; +} + + /* CogInLineLiteralsX64Compiler>>#concretizeArithCwR: */ +static NoDbgRegParms sqInt +concretizeArithCwR(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt x64opcode) +{ + sqInt reg; + sqInt reverse; + sqInt value; + + value = ((self_in_CogInLineLiteralsX64Compiler->operands))[0]; + reg = ((self_in_CogInLineLiteralsX64Compiler->operands))[1]; + + /* Tst & Cmp; backwards */ + reverse = (x64opcode == 133) + || (x64opcode == 57); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[0] = (rexRxb(self_in_CogInLineLiteralsX64Compiler, RISCTempReg, 0, RISCTempReg)); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[1] = (184 + (RISCTempReg & 7)); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[2] = (value & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[3] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 24) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[6] = ((((usqInt)(value)) >> 32) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[7] = ((((usqInt)(value)) >> 40) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[8] = ((((usqInt)(value)) >> 48) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[9] = ((((usqInt)(value)) >> 56) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[10] = ((reverse + ? rexRxb(self_in_CogInLineLiteralsX64Compiler, RISCTempReg, 0, reg) + : rexRxb(self_in_CogInLineLiteralsX64Compiler, reg, 0, RISCTempReg))); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[11] = x64opcode; + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[12] = ((reverse + ? modRMRO(self_in_CogInLineLiteralsX64Compiler, ModReg, reg, RISCTempReg) + : modRMRO(self_in_CogInLineLiteralsX64Compiler, ModReg, RISCTempReg, reg))); + assert((((self_in_CogInLineLiteralsX64Compiler->machineCode))[12]) > 144); + return 13; +} + + +/* Will get inlined into concretizeAt: switch. */ + + /* CogInLineLiteralsX64Compiler>>#concretizeMoveCwR */ +static NoDbgRegParms sqInt +concretizeMoveCwR(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler) +{ + sqInt offset; + sqInt reg; + sqInt value; + + value = ((self_in_CogInLineLiteralsX64Compiler->operands))[0]; + reg = ((self_in_CogInLineLiteralsX64Compiler->operands))[1]; + if (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) value))) + || ((((AbstractInstruction *) value)) == (methodLabel))) { + value = ((((AbstractInstruction *) value))->address); + } + if (/* addressIsInCurrentCompilation: */ + ((((usqInt)value)) >= ((methodLabel->address))) + && ((((usqInt)value)) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize))))) { + offset = value - (((self_in_CogInLineLiteralsX64Compiler->address)) + 7); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[0] = (rexRxb(self_in_CogInLineLiteralsX64Compiler, reg, 0, 0)); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[1] = 141; + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[2] = (modRMRO(self_in_CogInLineLiteralsX64Compiler, ModRegInd, 5, reg)); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[3] = (offset & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[4] = ((((usqInt)(offset)) >> 8) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[5] = ((((usqInt)(offset)) >> 16) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[6] = ((((usqInt)(offset)) >> 24) & 0xFF); + + /* LoadEffectiveAddress */ + return 7; + } + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[0] = (rexRxb(self_in_CogInLineLiteralsX64Compiler, 0, 0, reg)); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[1] = (184 + (reg & 7)); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[2] = (value & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[3] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 24) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[6] = ((((usqInt)(value)) >> 32) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[7] = ((((usqInt)(value)) >> 40) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[8] = ((((usqInt)(value)) >> 48) & 0xFF); + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[9] = ((((usqInt)(value)) >> 56) & 0xFF); + + /* Add a nop to disambiguate between MoveCwR/PushCw and ArithCwR, which ends with a (self mod: ModReg RM: 0 RO: 0) */ + ((self_in_CogInLineLiteralsX64Compiler->machineCode))[10] = 144; + return 11; +} + + +/* Answer the inline cache tag for the return address of a send. */ + + /* CogInLineLiteralsX64Compiler>>#inlineCacheTagAt: */ +static NoDbgRegParms unsigned int +inlineCacheTagAt(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt callSiteReturnAddress) +{ + return literal32BeforeFollowingAddress(self_in_CogInLineLiteralsX64Compiler, ((usqInt)(callSiteReturnAddress - 5))); +} + + +/* Answer if the receiver is a pc-dependent instruction. */ + + /* CogInLineLiteralsX64Compiler>>#isPCDependent */ +static NoDbgRegParms sqInt +isPCDependent(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler) +{ + return (isJump(self_in_CogInLineLiteralsX64Compiler)) + || (((self_in_CogInLineLiteralsX64Compiler->opcode)) == AlignmentNops); +} + + +/* Answer the 32-bit literal embedded in the instruction immediately + preceding followingAddress. + */ + + /* CogInLineLiteralsX64Compiler>>#literal32BeforeFollowingAddress: */ +static NoDbgRegParms unsigned int +literal32BeforeFollowingAddress(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt followingAddress) +{ + return ((unsigned int) (unalignedLong32At((void *)(followingAddress - 4)))); +} + + +/* Answer the literal embedded in the instruction immediately preceding + followingAddress. This is used in the MoveCwR, PushCw and ArithCwR cases; + these are distinguished by a + nop following the literal load in MoveCwR, a 16r48 + reg ending the PushCw + sequence, and + a (self mod: ModReg RM: rX RO: rY) ending the ArithCwR sequence, which is + at least 16rC0. */ + + /* CogInLineLiteralsX64Compiler>>#literalBeforeFollowingAddress: */ +static NoDbgRegParms sqInt +literalBeforeFollowingAddress(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt followingAddress) +{ + sqInt base; + sqInt lastByte; + + lastByte = byteAt((void *)(followingAddress - 1)); + + /* ArithCwR */ + base = followingAddress - ((lastByte == 144 + ? 9 + : (lastByte < 144 + ? 10 + : 11))); + return unalignedLongAt((void *)(base)); +} + + /* CogInLineLiteralsX64Compiler>>#loadLiteralByteSize */ +static NoDbgRegParms sqInt +loadLiteralByteSize(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler) +{ + return 11 /* moveCwRByteSize */; +} + + +/* Size a jump and set its address. The target may be another instruction + or an absolute address. On entry the address inst var holds our virtual + address. On exit address is set to eventualAbsoluteAddress, which is + where this instruction will be output. The span of a jump to a following + instruction is therefore between that instruction's address and this + instruction's address ((which are both still their virtual addresses), but + the span of a jump to a preceding instruction or to an absolute address is + between that instruction's address (which by now is its eventual absolute + address) or absolute address and eventualAbsoluteAddress. */ + + /* CogInLineLiteralsX64Compiler>>#sizePCDependentInstructionAt: */ +static NoDbgRegParms unsigned char +sizePCDependentInstructionAt(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt eventualAbsoluteAddress) +{ + AbstractInstruction *abstractInstruction; + sqInt alignment; + sqInt maximumSpan; + sqInt target; + + if (((self_in_CogInLineLiteralsX64Compiler->opcode)) == AlignmentNops) { + (self_in_CogInLineLiteralsX64Compiler->address) = eventualAbsoluteAddress; + alignment = ((self_in_CogInLineLiteralsX64Compiler->operands))[0]; + return ((self_in_CogInLineLiteralsX64Compiler->machineCodeSize) = ((eventualAbsoluteAddress + (alignment - 1)) & (-alignment)) - eventualAbsoluteAddress); + } + assert(isJump(self_in_CogInLineLiteralsX64Compiler)); + target = ((self_in_CogInLineLiteralsX64Compiler->operands))[0]; + abstractInstruction = ((AbstractInstruction *) target); + if (/* isAnInstruction: */ + (addressIsInInstructions(abstractInstruction)) + || (abstractInstruction == (methodLabel))) { + maximumSpan = ((abstractInstruction->address)) - (((abstractInstructionfollows(self_in_CogInLineLiteralsX64Compiler, abstractInstruction) + ? eventualAbsoluteAddress + : (self_in_CogInLineLiteralsX64Compiler->address))) + 2); + } + else { + maximumSpan = target - (eventualAbsoluteAddress + 2); + } + (self_in_CogInLineLiteralsX64Compiler->address) = eventualAbsoluteAddress; + if (((self_in_CogInLineLiteralsX64Compiler->opcode)) >= FirstShortJump) { + (self_in_CogInLineLiteralsX64Compiler->machineCodeSize) = (isQuick(self_in_CogInLineLiteralsX64Compiler, maximumSpan) + ? 2 + : (((self_in_CogInLineLiteralsX64Compiler->opcode)) == Jump + ? 5 + : 6)); + } + else { + switch ((self_in_CogInLineLiteralsX64Compiler->opcode)) { + case JumpLong: + (self_in_CogInLineLiteralsX64Compiler->machineCodeSize) = 5; + break; + case JumpFull: + (self_in_CogInLineLiteralsX64Compiler->machineCodeSize) = 12; + break; + case JumpLongZero: + case JumpLongNonZero: + (self_in_CogInLineLiteralsX64Compiler->machineCodeSize) = 6; + break; + default: + error("Case not found and no otherwise clause"); + } + } + return (self_in_CogInLineLiteralsX64Compiler->machineCodeSize); +} + + +/* Rewrite the literal in the instruction immediately preceding + followingAddress. This is used in the MoveCwR, PushCw and CmpCwR cases; + these are distinguished by a + nop following the literal load in MoveCwR, a 16r50 + reg ending the PushCw + sequence, and + a (self mod: ModReg RM: rX RO: rY) ending the CmpCwR sequence, which is at + least 16rC0. */ + + /* CogInLineLiteralsX64Compiler>>#storeLiteral:beforeFollowingAddress: */ +static NoDbgRegParms AbstractInstruction * +storeLiteralbeforeFollowingAddress(AbstractInstruction *self_in_CogInLineLiteralsX64Compiler, sqInt literal, sqInt followingAddress) +{ + sqInt base; + sqInt lastByte; + + lastByte = byteAt((void *)(followingAddress - 1)); + + /* ArithCwR */ + base = followingAddress - ((lastByte <= 144 + ? (lastByte == 144 + ? 9 + : 10) + : 11)); + unalignedLongAtput((void *)(base),literal); + return self_in_CogInLineLiteralsX64Compiler; +} + + /* Cogit>>#AddCq:R: */ +static NoDbgRegParms AbstractInstruction * +gAddCqR(sqInt quickConstant, sqInt reg) +{ + return genoperandoperand(AddCqR, quickConstant, reg); +} + + +/* N.B. if the condition codes don't require setting and three address + arithmetic is unavailable, then LoadEffectiveAddressMw:r:R: can be used + instead. + */ + + /* Cogit>>#AddCq:R:R: */ +static NoDbgRegParms AbstractInstruction * +gAddCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *first; + + if (srcReg == destReg) { + return genoperandoperand(AddCqR, quickConstant, destReg); + } + first = genoperandoperand(MoveRR, srcReg, destReg); + + /* #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, quickConstant, destReg); + return first; +} + + /* Cogit>>#AndCq:R: */ +static NoDbgRegParms AbstractInstruction * +gAndCqR(sqInt quickConstant, sqInt reg) +{ + return genoperandoperand(AndCqR, quickConstant, reg); +} + + /* Cogit>>#AndCq:R:R: */ +static NoDbgRegParms AbstractInstruction * +gAndCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *first; + + if (srcReg == destReg) { + return genoperandoperand(AndCqR, quickConstant, destReg); + } + first = genoperandoperand(MoveRR, srcReg, destReg); + + /* #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, destReg); + return first; +} + + +/* destReg := (signed)srcReg >> quickConstant */ + + /* Cogit>>#ArithmeticShiftRightCq:R:R: */ +static NoDbgRegParms AbstractInstruction * +gArithmeticShiftRightCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *first; + + first = genoperandoperand(MoveRR, srcReg, destReg); + genoperandoperand(ArithmeticShiftRightCqR, quickConstant, destReg); + return first; +} + + /* Cogit>>#abortOffset */ +sqInt +abortOffset(void) +{ + return missOffset; +} + + /* Cogit>>#abstractInstruction:follows: */ +static NoDbgRegParms int +abstractInstructionfollows(AbstractInstruction *theAbstractInstruction, AbstractInstruction *anAbstractInstruction) +{ + return theAbstractInstruction > anAbstractInstruction; +} + + /* Cogit>>#addCleanBlockStarts */ +static void +addCleanBlockStarts(void) +{ + sqInt i; + sqInt lit; + sqInt startPCOrNil; + sqInt toDoLimit; + + toDoLimit = literalCountOf(methodObj); + for (i = 1; i <= toDoLimit; i += 1) { + lit = fetchPointerofObject(i, methodObj); + if ((startPCOrNil = startPCOrNilOfLiteralin(lit, methodObj))) { + maxLitIndex = ((maxLitIndex < i) ? i : maxLitIndex); + addBlockStartAtnumArgsnumCopiedspan(startPCOrNil - 1, argumentCountOfClosure(lit), copiedValueCountOfClosure(lit), spanForCleanBlockStartingAt(startPCOrNil - 1)); + } + } +} + + +/* Perform an integrity/leak check using the heapMap. + Set a bit at each cog method's header. */ + + /* Cogit>>#addCogMethodsToHeapMap */ +void +addCogMethodsToHeapMap(void) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + heapMapAtWordPut(cogMethod, 1); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } +} + + /* Cogit>>#addressIsInFixups: */ +static NoDbgRegParms sqInt +addressIsInFixups(BytecodeFixup *address) +{ + return (BytecodeFixup *)address >= fixups && (BytecodeFixup *)address < (fixups + numAbstractOpcodes); +} + + +/* calculate the end of the n'th case statement - which is complicated + because we have case 1 right at the top of our CPIC and then build up from + the last one. Yes I know this sounds strange, but trust me - I'm an + Engineer, we do things backwards all the emit + */ + + /* Cogit>>#addressOfEndOfCase:inCPIC: */ +static NoDbgRegParms sqInt +addressOfEndOfCaseinCPIC(sqInt n, CogMethod *cPIC) +{ + assert((n >= 1) + && (n <= MaxCPICCases)); + return (n == 1 + ? (((sqInt)cPIC)) + firstCPICCaseOffset + : ((((sqInt)cPIC)) + firstCPICCaseOffset) + (((MaxCPICCases + 1) - n) * cPICCaseSize)); +} + + +/* Align methodZoneBase to that for the start of a method. */ + + /* Cogit>>#alignMethodZoneBase */ +static void +alignMethodZoneBase(void) +{ + usqInt oldBase; + + oldBase = methodZoneBase; + methodZoneBase = roundUpToMethodAlignment(backEnd, methodZoneBase); + stopsFromto(backEnd, oldBase, methodZoneBase - 1); +} + + /* Cogit>>#alignUptoRoutineBoundary: */ +static NoDbgRegParms sqInt +alignUptoRoutineBoundary(sqInt anAddress) +{ + return (((anAddress + 7) | 7) - 7); +} + + +/* Check that all methods have valid selectors, and that all linked sends are + to valid targets and have valid cache tags + */ + + /* Cogit>>#allMachineCodeObjectReferencesValid */ +static sqInt +allMachineCodeObjectReferencesValid(void) +{ + CogMethod *cogMethod; + sqInt ok; + + ok = 1; + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (!(((cogMethod->cmType)) == CMFree)) { + if (!(asserta(checkValidOopReference((cogMethod->selector))))) { + ok = 0; + } + if (!(asserta((cogMethodDoesntLookKosher(cogMethod)) == 0))) { + ok = 0; + } + } + if ((((cogMethod->cmType)) >= CMMethod) + || (((cogMethod->cmType)) == CMOpenPIC)) { + if (!(asserta((mapForperformUntilarg(cogMethod, checkIfValidOopRefAndTargetpccogMethod, cogMethod)) == 0))) { + ok = 0; + } + } + if (((cogMethod->cmType)) == CMClosedPIC) { + if (!(asserta(noTargetsFreeInClosedPIC(cogMethod)))) { + ok = 0; + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + return ok; +} + + /* Cogit>>#allMethodsHaveCorrectHeader */ +static sqInt +allMethodsHaveCorrectHeader(void) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + if (!(((cogMethod->objectHeader)) == (nullHeaderForMachineCodeMethod()))) { + return 0; + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + return 1; +} + + /* Cogit>>#annotateAbsolutePCRef: */ +static NoDbgRegParms AbstractInstruction * +annotateAbsolutePCRef(AbstractInstruction *abstractInstruction) +{ + (abstractInstruction->annotation = IsAbsPCReference); + return abstractInstruction; +} + + /* Cogit>>#annotateBytecode: */ +static NoDbgRegParms AbstractInstruction * +annotateBytecode(AbstractInstruction *abstractInstruction) +{ + (abstractInstruction->annotation = HasBytecodePC); + return abstractInstruction; +} + + /* Cogit>>#annotate:objRef: */ +static NoDbgRegParms AbstractInstruction * +annotateobjRef(AbstractInstruction *abstractInstruction, sqInt anOop) +{ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(anOop)) + && ((oopisGreaterThan(anOop, classTableRootObj())) + || (oopisLessThan(anOop, nilObject())))) { + setHasMovableLiteral(1); + if (isYoungObject(anOop)) { + setHasYoungReferent(1); + } + (abstractInstruction->annotation = IsObjectReference); + } + return abstractInstruction; +} + + /* Cogit>>#assertSaneJumpTarget: */ +static NoDbgRegParms void +assertSaneJumpTarget(AbstractInstruction *jumpTarget) +{ + assert((!closedPICSize) + || ((!openPICSize) + || ((addressIsInInstructions(jumpTarget)) + || ((((((usqInt)jumpTarget)) >= codeBase) && ((((usqInt)jumpTarget)) <= ((((sqInt)(limitZony()))) + (((closedPICSize < openPICSize) ? openPICSize : closedPICSize))))))))); +} + + +/* Answer an unused abstract register in the registerMask, or NoReg if none. */ + + /* Cogit>>#availableRegisterOrNoneIn: */ +static NoDbgRegParms sqInt +availableRegisterOrNoneIn(sqInt liveRegsMask) +{ + sqInt reg; + + if (liveRegsMask) { + for (reg = 0; reg <= 0x1F; reg += 1) { + if (((liveRegsMask & (1ULL << reg)) != 0)) { + return reg; + } + } + } + return NoReg; +} + + +/* Evaluate binaryFunction with the block start mcpc and supplied arg for + each entry in the block dispatch. If the function answers non-zero answer + the value + it answered. Used to update back-references to the home method in + compaction. */ + + /* Cogit>>#blockDispatchTargetsFor:perform:arg: */ +static NoDbgRegParms usqInt +blockDispatchTargetsForperformarg(CogMethod *cogMethod, usqInt (*binaryFunction)(sqInt mcpc, sqInt arg), sqInt arg) +{ + sqInt blockEntry; + sqInt end; + sqInt pc; + usqInt result; + usqInt targetpc; + + if (!((cogMethod->blockEntryOffset))) { + return null; + } + blockEntry = ((cogMethod->blockEntryOffset)) + (((sqInt)cogMethod)); + pc = blockEntry; + end = (mapEndFor(cogMethod)) - 1; + while (pc < end) { + if (isJumpAt(backEnd, pc)) { + targetpc = jumpTargetPCAt(backEnd, pc); + if (targetpc < blockEntry) { + result = binaryFunction(targetpc, arg); + if (result) { + return result; + } + } + } + pc += instructionSizeAt(backEnd, pc); + } + return 0; +} + + +/* Answer the zero-relative bytecode pc matching the machine code pc argument + in cogMethod, given the start of the bytecodes for cogMethod's block or + method object. */ + + /* Cogit>>#bytecodePCFor:startBcpc:in: */ +sqInt +bytecodePCForstartBcpcin(sqInt mcpc, sqInt startbcpc, CogBlockMethod *cogMethod) +{ + sqInt aMethodObj; + sqInt annotation; + sqInt bcpc; + sqInt bsOffset; + sqInt byte; + BytecodeDescriptor *descriptor; + sqInt distance; + sqInt endbcpc; + CogMethod *homeMethod; + sqInt isBackwardBranch; + sqInt isInBlock; + sqInt latestContinuation; + sqInt map; + sqInt mapByte; + usqInt mcpcUsqInt; + sqInt nExts; + sqInt nextBcpc; + sqInt result; + sqInt targetPC; + + /* begin mapFor:bcpc:performUntil:arg: */ + descriptor = ((BytecodeDescriptor *) 0); + latestContinuation = 0; + mapByte = 0; + nextBcpc = 0; + assert(((cogMethod->stackCheckOffset)) > 0); + mcpcUsqInt = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + + /* The stack check maps to the start of the first bytecode, + the first bytecode being effectively after frame build. */ + result = findIsBackwardBranchMcpcBcpcMatchingMcpc(null, 0 + ((((usqInt)(HasBytecodePC) << 1))), ((char *) mcpcUsqInt), startbcpc, ((void *)mcpc)); + if (result) { + return result; + } + bcpc = startbcpc; + + /* In both CMMethod and CMBlock cases find the start of the map and + skip forward to the bytecode pc map entry for the stack check. */ + if (((cogMethod->cmType)) >= CMMethod) { + isInBlock = (cogMethod->cpicHasMNUCaseOrCMIsFullBlock); + homeMethod = ((CogMethod *) cogMethod); + assert(startbcpc == (startPCOfMethodHeader((homeMethod->methodHeader)))); + map = ((((usqInt)homeMethod)) + ((homeMethod->blockSize))) - 1; + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert((annotation == IsAbsPCReference) + || ((annotation == IsObjectReference) + || ((annotation == IsRelativeCall) + || (annotation == IsDisplacementX2N)))); + latestContinuation = startbcpc; + aMethodObj = (homeMethod->methodObject); + endbcpc = (numBytesOf(aMethodObj)) - 1; + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + + /* If the method has a primitive, skip it and the error code store, if any; + Logically. these come before the stack check and so must be ignored. */ + bcpc += deltaToSkipPrimAndErrorStoreInheader(aMethodObj, (homeMethod->methodHeader)); + } + else { + isInBlock = 1; + assert(bcpc == ((cogMethod->startpc))); + homeMethod = cmHomeMethod(cogMethod); + map = findMapLocationForMcpcinMethod((((usqInt)cogMethod)) + (sizeof(CogBlockMethod)), homeMethod); + assert(map != 0); + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert(((((usqInt)(annotation)) >> AnnotationShift) == HasBytecodePC) + || ((((usqInt)(annotation)) >> AnnotationShift) == IsDisplacementX2N)); + + /* fiducial */ + while (((annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift)) != HasBytecodePC) { + map -= 1; + } + + /* skip fiducial; i.e. the map entry for the pc immediately following the method header. */ + map -= 1; + aMethodObj = (homeMethod->methodObject); + bcpc = startbcpc - (/* blockCreationBytecodeSizeForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? AltBlockCreationBytecodeSize + : BlockCreationBytecodeSize)); + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + endbcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, -1, aMethodObj) + : 0)); + bcpc = startbcpc; + } + nExts = 0; + enumeratingCogMethod = homeMethod; + + /* Now skip up through the bytecode pc map entry for the stack check. */ + while ((((usqInt)((byteAt((void *)(map))))) >> AnnotationShift) != HasBytecodePC) { + map -= 1; + } + map -= 1; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + mcpcUsqInt += (mapByte & DisplacementMask); + if (annotation >= HasBytecodePC) { + if ((annotation == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + while (1) { + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + if (isInBlock) { + if (bcpc >= endbcpc) { + return 0; + } + } + else { + if (((descriptor->isReturn)) + && (bcpc >= latestContinuation)) { + return 0; + } + if ((isBranch(descriptor)) + || ((descriptor->isBlockCreation))) { + /* begin latestContinuationPCFor:at:exts:in: */ + distance = ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj); + targetPC = (bcpc + ((descriptor->numBytes))) + (((distance < 0) ? 0 : distance)); + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + } + } + nextBcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj) + : 0)); + if (((descriptor->isMapped)) + || (isInBlock + && ((descriptor->isMappedInBlock)))) break; + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + isBackwardBranch = (isBranch(descriptor)) + && ((/* begin isBackwardBranch:at:exts:in: */ + assert(((descriptor->spanFunction))), + (((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj)) < 0)); + result = findIsBackwardBranchMcpcBcpcMatchingMcpc(descriptor, (isBackwardBranch + ? ((((usqInt)(annotation) << 1))) + 1 + : ((sqInt)((usqInt)(annotation) << 1))), ((char *) mcpcUsqInt), (isBackwardBranch + ? bcpc - (2 * nExts) + : bcpc), ((void *)mcpc)); + if (result) { + return result; + } + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + } + else { + assert(((((usqInt)(mapByte)) >> AnnotationShift) == IsDisplacementX2N) + || ((((usqInt)(mapByte)) >> AnnotationShift) == IsAnnotationExtension)); + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpcUsqInt += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + + /* defensive; we exit on bcpc */ + return 0; +} + + /* Cogit>>#Call: */ +static NoDbgRegParms AbstractInstruction * +gCall(sqInt callTarget) +{ + return genoperand(Call, callTarget); +} + + /* Cogit>>#CmpCq:R: */ +static NoDbgRegParms AbstractInstruction * +gCmpCqR(sqInt quickConstant, sqInt reg) +{ + return genoperandoperand(CmpCqR, quickConstant, reg); +} + + +/* This is a static version of ceCallCogCodePopReceiverReg for break-pointing + when debugging in C. Marked so the code generator won't delete it. */ + + /* Cogit>>#callCogCodePopReceiver */ +static void +callCogCodePopReceiver(void) +{ + realCECallCogCodePopReceiverReg(); + + /* (and this exists only to reference Debug) */ + if (!Debug) { + error("what??"); + } +} + + +/* This is a static version of ceCallCogCodePopReceiverAndClassRegs for + break-pointing when debugging in C. Marked so the code generator + won't delete it. */ + + /* Cogit>>#callCogCodePopReceiverAndClassRegs */ +static void +callCogCodePopReceiverAndClassRegs(void) +{ + realCECallCogCodePopReceiverAndClassRegs(); +} + + +/* Code entry closed PIC miss. A send has fallen + through a closed (finite) polymorphic inline cache. + Either extend it or patch the send site to an open PIC. + The stack looks like: + receiver + args + sp=> sender return address */ +/* Marked so the code generator won't delete it. */ + + /* Cogit>>#ceCPICMiss:receiver: */ +static NoDbgRegParms sqInt +ceCPICMissreceiver(CogMethod *cPIC, sqInt receiver) +{ + sqInt cacheTag; + int errorSelectorOrNil; + sqInt methodOrSelectorIndex; + sqInt newTargetMethodOrNil; + sqInt outerReturn; + sqInt result; + sqInt selector; + + assert(addressCouldBeOop(receiver)); + if (isOopForwarded(receiver)) { + return ceSendFromInLineCacheMiss(cPIC); + } + outerReturn = stackTop(); + assert(!(((inlineCacheTagAt(backEnd, outerReturn)) == (picAbortDiscriminatorValue())))); + if (((cPIC->cPICNumCases)) < MaxCPICCases) { + selector = (cPIC->selector); + + /* begin lookup:for:methodAndErrorSelectorInto: */ + methodOrSelectorIndex = lookupOrdinaryreceiver(selector, receiver); + if ((((usqInt)methodOrSelectorIndex)) > (maxLookupNoMNUErrorCode())) { + if (!(isOopCompiledMethod(methodOrSelectorIndex))) { + newTargetMethodOrNil = methodOrSelectorIndex; + errorSelectorOrNil = SelectorCannotInterpret; + goto l1; + } + if ((!(methodHasCogMethod(methodOrSelectorIndex))) + && (methodShouldBeCogged(methodOrSelectorIndex))) { + /* We assume cog:selector: will *not* reclaim the method zone */ + cogselector(methodOrSelectorIndex, selector); + } + newTargetMethodOrNil = methodOrSelectorIndex; + errorSelectorOrNil = null; + goto l1; + } + if (methodOrSelectorIndex == SelectorDoesNotUnderstand) { + methodOrSelectorIndex = lookupMNUreceiver(splObj(SelectorDoesNotUnderstand), receiver); + if ((((usqInt)methodOrSelectorIndex)) > (maxLookupNoMNUErrorCode())) { + assert(isOopCompiledMethod(methodOrSelectorIndex)); + if ((!(methodHasCogMethod(methodOrSelectorIndex))) + && (methodShouldBeCogged(methodOrSelectorIndex))) { + /* We assume cog:selector: will *not* reclaim the method zone */ + cogselector(methodOrSelectorIndex, splObj(SelectorDoesNotUnderstand)); + } + newTargetMethodOrNil = methodOrSelectorIndex; + errorSelectorOrNil = SelectorDoesNotUnderstand; + goto l1; + } + newTargetMethodOrNil = null; + errorSelectorOrNil = SelectorDoesNotUnderstand; + goto l1; + } + newTargetMethodOrNil = null; + errorSelectorOrNil = methodOrSelectorIndex; + /* end lookup:for:methodAndErrorSelectorInto: */ +l1:; + } + else { + newTargetMethodOrNil = (errorSelectorOrNil = null); + } + + /* We assume lookupAndCog:for: will *not* reclaim the method zone */ + assert(outerReturn == (stackTop())); + + /* begin ensureWritableCodeZone */ + cacheTag = inlineCacheTagForInstance(receiver); + if ((((cPIC->cPICNumCases)) >= MaxCPICCases) + || (/* closedPICInappropriateForCacheTag:targetMethod:orErrorSelector: */ + ((errorSelectorOrNil) + && (errorSelectorOrNil != SelectorDoesNotUnderstand)) + || ((!newTargetMethodOrNil) + || (isYoung(newTargetMethodOrNil))))) { + result = patchToOpenPICFornumArgsreceiver((cPIC->selector), (cPIC->cmNumArgs), receiver); + assert(!result); + + /* begin ensureExecutableCodeZone */ + return ceSendFromInLineCacheMiss(cPIC); + } + + /* Now extend the PIC with the new case. */ + cogExtendPICCaseNMethodtagisMNUCase(cPIC, newTargetMethodOrNil, cacheTag, errorSelectorOrNil == SelectorDoesNotUnderstand); + + /* begin ensureExecutableCodeZone */ + + /* Jump back into the pic at its entry in case this is an MNU. */ + executeCogPICfromLinkedSendWithReceiverandCacheTag(cPIC, receiver, literal32BeforeFollowingAddress(backEnd, ((usqInt)(outerReturn - 5)))); + + /* NOTREACHED */ + return null; +} + + +/* An in-line cache check in a method has failed. The failing entry check has + jumped to the ceMethodAbort abort call at the start of the method which + has called this routine. + If possible allocate a closed PIC for the current and existing classes. + The stack looks like: + receiver + args + sender return address + sp=> ceMethodAbort call return address + So we can find the method that did the failing entry check at + ceMethodAbort call return address - missOffset + and we can find the send site from the outer return address. + Invoked from a trampoline. Marked so the code generator won't delete + it. */ + + /* Cogit>>#ceSICMiss: */ +static NoDbgRegParms sqInt +ceSICMiss(sqInt receiver) +{ + sqInt cacheTag; + int errorSelectorOrNil; + sqInt extent; + usqInt innerReturn; + sqInt methodOrSelectorIndex; + sqInt newTargetMethodOrNil; + usqInt outerReturn; + CogMethod *pic; + sqInt result; + sqInt selector; + CogMethod *targetMethod; + + assert(addressCouldBeOop(receiver)); + + /* Whether we can relink to a PIC or not we need to pop off the inner return and identify the target method. */ + innerReturn = ((usqInt)(popStack())); + targetMethod = ((CogMethod *) (innerReturn - missOffset)); + if (isOopForwarded(receiver)) { + return ceSendFromInLineCacheMiss(targetMethod); + } + outerReturn = ((usqInt)(stackTop())); + assert(((outerReturn >= methodZoneBase) && (outerReturn <= (freeStart())))); + assert(((targetMethod->selector)) != (nilObject())); + selector = (targetMethod->selector); + + /* begin lookup:for:methodAndErrorSelectorInto: */ + methodOrSelectorIndex = lookupOrdinaryreceiver(selector, receiver); + if ((((usqInt)methodOrSelectorIndex)) > (maxLookupNoMNUErrorCode())) { + if (!(isOopCompiledMethod(methodOrSelectorIndex))) { + newTargetMethodOrNil = methodOrSelectorIndex; + errorSelectorOrNil = SelectorCannotInterpret; + goto l1; + } + if ((!(methodHasCogMethod(methodOrSelectorIndex))) + && (methodShouldBeCogged(methodOrSelectorIndex))) { + /* We assume cog:selector: will *not* reclaim the method zone */ + cogselector(methodOrSelectorIndex, selector); + } + newTargetMethodOrNil = methodOrSelectorIndex; + errorSelectorOrNil = null; + goto l1; + } + if (methodOrSelectorIndex == SelectorDoesNotUnderstand) { + methodOrSelectorIndex = lookupMNUreceiver(splObj(SelectorDoesNotUnderstand), receiver); + if ((((usqInt)methodOrSelectorIndex)) > (maxLookupNoMNUErrorCode())) { + assert(isOopCompiledMethod(methodOrSelectorIndex)); + if ((!(methodHasCogMethod(methodOrSelectorIndex))) + && (methodShouldBeCogged(methodOrSelectorIndex))) { + /* We assume cog:selector: will *not* reclaim the method zone */ + cogselector(methodOrSelectorIndex, splObj(SelectorDoesNotUnderstand)); + } + newTargetMethodOrNil = methodOrSelectorIndex; + errorSelectorOrNil = SelectorDoesNotUnderstand; + goto l1; + } + newTargetMethodOrNil = null; + errorSelectorOrNil = SelectorDoesNotUnderstand; + goto l1; + } + newTargetMethodOrNil = null; + errorSelectorOrNil = methodOrSelectorIndex; + /* end lookup:for:methodAndErrorSelectorInto: */ +l1: + + /* We assume lookupAndCog:for: will *not* reclaim the method zone */ + assert(outerReturn == (stackTop())); + cacheTag = inlineCacheTagForInstance(receiver); + if ((/* closedPICInappropriateForCacheTag:targetMethod:orErrorSelector: */ + ((errorSelectorOrNil) + && (errorSelectorOrNil != SelectorDoesNotUnderstand)) + || ((!newTargetMethodOrNil) + || (isYoung(newTargetMethodOrNil)))) + || ((literal32BeforeFollowingAddress(backEnd, ((usqInt)(outerReturn - 5)))) == 0 /* picAbortDiscriminatorValue */)) { + result = patchToOpenPICFornumArgsreceiver((targetMethod->selector), (targetMethod->cmNumArgs), receiver); + assert(!result); + return ceSendFromInLineCacheMiss(targetMethod); + } + + /* begin ensureWritableCodeZone */ + + /* See if an Open PIC is already available. */ + pic = openPICWithSelector((targetMethod->selector)); + if (!pic) { + /* otherwise attempt to create a closed PIC for the two cases. */ + pic = cogPICSelectornumArgsCase0MethodCase1MethodtagisMNUCase((targetMethod->selector), (targetMethod->cmNumArgs), targetMethod, newTargetMethodOrNil, cacheTag, errorSelectorOrNil == SelectorDoesNotUnderstand); + if ((((((sqInt)pic)) >= MaxNegativeErrorCode) && ((((sqInt)pic)) <= -1))) { + /* For some reason the PIC couldn't be generated, most likely a lack of code memory. + Continue as if this is an unlinked send. */ + if ((((sqInt)pic)) == InsufficientCodeSpace) { + callForCogCompiledCodeCompaction(); + } + + /* begin ensureExecutableCodeZone */ + return ceSendFromInLineCacheMiss(targetMethod); + } + } + + /* Relink the send site to the pic. If to an open PIC then reset the cache tag to the selector, + for the benefit of the cacheTag assert check in checkIfValidOopRef:pc:cogMethod: et al. */ + extent = (((pic->cmType)) == CMOpenPIC + ? rewriteInlineCacheAttagtarget(backEnd, outerReturn, inlineCacheValueForSelectorin(backEnd, (targetMethod->selector), mframeHomeMethodExport()), (((sqInt)pic)) + cmEntryOffset) + : rewriteCallAttarget(backEnd, outerReturn, (((sqInt)pic)) + cmEntryOffset)); + (((usqInt)pic)) + closedPICSize; + + /* begin assertValidDualZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + assertCoherentCodeAtdelta(backEnd, (((usqInt)pic)) + cmNoCheckEntryOffset, codeToDataDelta); +# endif + + + /* These also implicitly flush the read/write mapped dual zone to the read/execute zone. */ + flushICacheFromto(backEnd, ((usqInt)pic), (((usqInt)pic)) + closedPICSize); + flushICacheFromto(backEnd, (((usqInt)outerReturn)) - extent, ((usqInt)outerReturn)); + + /* Jump back into the pic at its entry in case this is an MNU (newTargetMethodOrNil is nil) */ + executeCogPICfromLinkedSendWithReceiverandCacheTag(pic, receiver, literal32BeforeFollowingAddress(backEnd, ((usqInt)(outerReturn - 5)))); + + /* NOTREACHED */ + return null; +} + + +/* Check for a valid object reference, if any, at a map entry. Answer a code + unique to each error for debugging. */ + + /* Cogit>>#checkIfValidOopRefAndTarget:pc:cogMethod: */ +static NoDbgRegParms sqInt +checkIfValidOopRefAndTargetpccogMethod(sqInt annotation, char *mcpc, CogMethod *cogMethod) +{ + unsigned int cacheTag1; + usqInt entryPoint; + usqInt entryPointUsqInt; + sqInt literal; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + + if (annotation == IsObjectReference) { + literal = literalBeforeFollowingAddress(backEnd, ((usqInt)mcpc)); + if (!(asserta(checkValidOopReference(literal)))) { + return 1; + } + if ((couldBeObject(literal)) + && (isReallyYoungObject(literal))) { + if (!(asserta(((((CogMethod *) cogMethod))->cmRefersToYoung)))) { + return 2; + } + } + } + if (annotation >= IsSendCall) { + if (!(asserta(isCMMethodEtAl(((CogBlockMethod *) (((CogMethod *) cogMethod))))))) { + return 3; + } + + /* begin entryCacheTagAndCouldBeObjectAt:annotation:into: */ + cacheTag1 = literal32BeforeFollowingAddress(backEnd, ((usqInt)((((sqInt)mcpc)) - 5))); + entryPointUsqInt = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + + /* in-line cache tags are the selectors of sends if sends are unlinked, + the selectors of super sends (entry offset = cmNoCheckEntryOffset), + the selectors of open PIC sends (entry offset = cmEntryOffset, target is an Open PIC) + or in-line cache tags (classes, class indices, immediate bit patterns, etc). + Note that selectors can be immediate so there is no guarantee that they + are markable/remappable objects. Writing the block invocation three times + allows Slang to inline tagCouldBeObject tests. */ + entryPoint = entryPointUsqInt; + if (entryPointTagIsSelector(entryPoint)) { + if ((((int) cacheTag1)) < 0) { + if ((-(((int) cacheTag1))) > NumSpecialSelectors) { + return 7; + } + } + else { + if (cacheTag1 >= (literalCountOf((enumeratingCogMethod->methodObject)))) { + return 8; + } + } + } + else { + if (!(asserta(validInlineCacheTag(cacheTag1)))) { + return 9; + } + } + if (entryPoint > methodZoneBase) { + /* It's a linked send; find which kind. */ + + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (!(asserta((isCMMethodEtAl(((CogBlockMethod *) targetMethod1))) + || ((isCMClosedPIC(((CogBlockMethod *) targetMethod1))) + || (isCMOpenPIC(((CogBlockMethod *) targetMethod1))))))) { + return 10; + } + } + } + return 0; +} + + +/* Check for a valid object reference, if any, at a map entry. Answer a code + unique to each error for debugging. */ + + /* Cogit>>#checkIfValidOopRef:pc:cogMethod: */ +static NoDbgRegParms sqInt +checkIfValidOopRefpccogMethod(sqInt annotation, char *mcpc, CogMethod *cogMethod) +{ + usqInt entryPoint; + sqInt literal; + usqInt offset; + sqInt offsetSqInt; + unsigned int selectorOrCacheTag; + sqInt *sendTable; + + if (annotation == IsObjectReference) { + literal = literalBeforeFollowingAddress(backEnd, ((usqInt)mcpc)); + if (!(checkValidOopReference(literal))) { + fprintf(getTranscript(), + "object ref leak in CM %p @ %p\n", + cogMethod, + mcpc); + eek(); + return 1; + } + } + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint <= methodZoneBase) { + offset = entryPoint; + } + else { + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable = superSendTrampolines; + } + } + } + offset = offsetSqInt; + } + selectorOrCacheTag = literal32BeforeFollowingAddress(backEnd, ((usqInt)((((sqInt)mcpc)) - 5))); + if ((entryPoint > methodZoneBase) + && ((offset != cmNoCheckEntryOffset) + && (!((((((CogMethod *) (entryPoint - offset)))->cmType)) == CMOpenPIC)))) { + if (!(validInlineCacheTag(selectorOrCacheTag))) { + fprintf(getTranscript(), + "cache tag leak in CM %p @ %p\n", + cogMethod, + mcpc); + eek(); + return 1; + } + } + else { + } + } + return 0; +} + + +/* Answer if all references to objects in machine-code are valid. */ + + /* Cogit>>#checkIntegrityOfObjectReferencesInCode: */ +sqInt +checkIntegrityOfObjectReferencesInCode(sqInt gcModes) +{ + CogMethod *cogMethod; + sqInt count; + sqInt ok; + + cogMethod = ((CogMethod *) methodZoneBase); + ok = 1; + while (cogMethod < (limitZony())) { + if (!(((cogMethod->cmType)) == CMFree)) { + if ((cogMethod->cmRefersToYoung)) { + if (((count = occurrencesInYoungReferrers(cogMethod))) != 1) { + fprintf(getTranscript(), + "young referrer CM %p", + cogMethod); + if (count) { + fprintf(getTranscript(), + " is in youngReferrers %d times!\n", + ((int) count)); + eek(); + } + else { + fprintf(getTranscript(), " is not in youngReferrers\n"); + eek(); + } + ok = 0; + } + } + if (!(checkValidOopReference((cogMethod->selector)))) { + fprintf(getTranscript(), + "object leak in CM %p selector\n", + cogMethod); + eek(); + ok = 0; + } + if (((cogMethod->cmType)) >= CMMethod) { + assert(((cogMethod->objectHeader)) == (nullHeaderForMachineCodeMethod())); + if (!(checkValidObjectReference((cogMethod->methodObject)))) { + fprintf(getTranscript(), + "object leak in CM %p methodObject\n", + cogMethod); + eek(); + ok = 0; + } + if (!(isOopCompiledMethod((cogMethod->methodObject)))) { + fprintf(getTranscript(), + "non-method in CM %p methodObject\n", + cogMethod); + eek(); + ok = 0; + } + if (mapForperformUntilarg(cogMethod, checkIfValidOopRefpccogMethod, cogMethod)) { + ok = 0; + } + if (((isYoungObject((cogMethod->methodObject))) + || (isYoung((cogMethod->selector)))) + && (!((cogMethod->cmRefersToYoung)))) { + fprintf(getTranscript(), + "CM %p refers to young but not marked as such\n", + cogMethod); + eek(); + ok = 0; + } + } + else { + if (((cogMethod->cmType)) == CMClosedPIC) { + if (!(checkValidObjectReferencesInClosedPIC(cogMethod))) { + ok = 0; + } + } + else { + if (((cogMethod->cmType)) == CMOpenPIC) { + if (mapForperformUntilarg(cogMethod, checkIfValidOopRefpccogMethod, cogMethod)) { + ok = 0; + } + } + } + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + return ok; +} + + /* Cogit>>#checkMaybeObjRefInClosedPIC: */ +static NoDbgRegParms sqInt +checkMaybeObjRefInClosedPIC(sqInt maybeObject) +{ + if (!maybeObject) { + return 1; + } + if (!(couldBeObject(maybeObject))) { + return 1; + } + return checkValidObjectReference(maybeObject); +} + + /* Cogit>>#checkValidObjectReferencesInClosedPIC: */ +static NoDbgRegParms sqInt +checkValidObjectReferencesInClosedPIC(CogMethod *cPIC) +{ + sqInt i; + sqInt ok; + sqInt pc; + + ok = 1; + pc = (((sqInt)cPIC)) + firstCPICCaseOffset; + + /* first we check the obj ref at the beginning of the CPIC */ + if (!(checkMaybeObjRefInClosedPIC(literalBeforeFollowingAddress(backEnd, pc - (jumpLongByteSize(backEnd)))))) { + fprintf(getTranscript(), + "object leak in CPIC %p @ %p\n", + cPIC, + ((void *)(pc - (jumpLongByteSize(backEnd))))); + ok = 0; + } + + /* Next we step over each case that is in use. We find the end address of the cPICNumCases'th case and can then just step forward by the case size thereafter */ + pc = addressOfEndOfCaseinCPIC((cPIC->cPICNumCases), cPIC); + + /* For each case we check any object reference at the end address - sizeof(conditional instruction) and then increment the end address by case size */ + for (i = 2; i <= ((cPIC->cPICNumCases)); i += 1) { + if (!(checkMaybeObjRefInClosedPIC(literalBeforeFollowingAddress(backEnd, (pc - (jumpLongConditionalByteSize(backEnd))) - (cmpC32RTempByteSize(backEnd)))))) { + fprintf(getTranscript(), + "object leak in CPIC %p @ %p\n", + cPIC, + ((void *)(pc - (jumpLongConditionalByteSize(backEnd))))); + ok = 0; + } + pc += cPICCaseSize; + } + return ok; +} + + +/* i.e. this should never be called, so keep it out of the main path. */ + + /* Cogit>>#cleanUpFailingCogCodeConstituents: */ +static NoDbgRegParms NeverInline sqInt +cleanUpFailingCogCodeConstituents(CogMethod *cogMethodArg) +{ + CogMethod *cogMethod; + + cogMethod = cogMethodArg; + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) == CMClosedPIC) { + ((((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)))->methodObject = 0); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + + /* would like to assert this, but it requires the leak checked be run :-( + self assert: self allMachineCodeObjectReferencesValid. */ + popRemappableOop(); + return null; +} + + +/* Answer if the ClosedPIC refers to any unmarked objects or freed/freeable + target methods, + applying markAndTraceOrFreeCogMethod:firstVisit: to those targets to + determine if freed/freeable. + */ + + /* Cogit>>#closedPICRefersToUnmarkedObject: */ +static NoDbgRegParms sqInt +closedPICRefersToUnmarkedObject(CogMethod *cPIC) +{ + sqInt i; + sqInt object; + sqInt pc; + + if (!((isImmediate((cPIC->selector))) + || (isMarked((cPIC->selector))))) { + return 1; + } + + /* First jump is unconditional; subsequent ones are conditional. + Check the potential method oop for the first case only. + Inline cache tags for the 1st case are at the send site. */ + pc = addressOfEndOfCaseinCPIC(1, cPIC); + if (couldBeObject((object = literalBeforeFollowingAddress(backEnd, pc - (jumpLongByteSize(backEnd)))))) { + if (!(isMarked(object))) { + return 1; + } + } + + /* Check the first target */ + if (markAndTraceOrFreePICTargetin(jumpLongTargetBeforeFollowingAddress(backEnd, pc), cPIC)) { + return 1; + } + for (i = 2; i <= ((cPIC->cPICNumCases)); i += 1) { + pc = addressOfEndOfCaseinCPIC(i, cPIC); + + /* Check the potential method oop for subsequent cases. */ + if (couldBeObject((object = literalBeforeFollowingAddress(backEnd, (pc - (jumpLongConditionalByteSize(backEnd))) - (cmpC32RTempByteSize(backEnd)))))) { + if (!(isMarked(object))) { + return 1; + } + } + + /* Check subsequent targets */ + if (markAndTraceOrFreePICTargetin(jumpLongTargetBeforeFollowingAddress(backEnd, pc), cPIC)) { + return 1; + } + } + return 0; +} + + /* Cogit>>#codeEntryFor: */ +char * +codeEntryFor(char *address) +{ + char *end; + sqInt i; + char *name; + char *start; + + /* begin trampolineRangesDo: */ + for (i = 0; i <= (trampolineTableIndex - 3); i += 2) { + name = trampolineAddresses[i]; + start = trampolineAddresses[i + 1]; + end = (trampolineAddresses[i + 3]) - 1; + if (((address >= start) && (address <= end))) { + return start; + } + } + return null; +} + + /* Cogit>>#codeEntryNameFor: */ +char * +codeEntryNameFor(char *address) +{ + char *end; + sqInt i; + char *name; + char *start; + + /* begin trampolineRangesDo: */ + for (i = 0; i <= (trampolineTableIndex - 3); i += 2) { + name = trampolineAddresses[i]; + start = trampolineAddresses[i + 1]; + end = (trampolineAddresses[i + 3]) - 1; + if (((address >= start) && (address <= end))) { + return name; + } + } + return null; +} + + +/* used e.g. in the platform's backtrace generators. Declared api to place it + in cogit.h + */ + + /* Cogit>>#cogCodeBase */ +usqInt +cogCodeBase(void) +{ + return codeBase; +} + + +/* Answer the contents of the code zone as an array of pair-wise element, + address in ascending address order. + Answer a string for a runtime routine or abstract label (beginning, end, + etc), a CompiledMethod for a CMMethod, + or a selector (presumably a Symbol) for a PIC. + If withDetails is true + - answer machine-code to bytecode pc mapping information for methods + - answer class, target pair information for closed PIC */ + + /* Cogit>>#cogCodeConstituents: */ +sqInt +cogCodeConstituents(sqInt withDetails) +{ + sqInt constituents; + + /* begin ensureWritableCodeZone */ + constituents = getCogCodeConstituents(withDetails); + + /* begin ensureExecutableCodeZone */ + return constituents; +} + + +/* Extend the cPIC with the supplied case. If caseNMethod is cogged dispatch + direct to + its unchecked entry-point. If caseNMethod is not cogged, jump to the fast + interpreter dispatch, and if isMNUCase then dispatch to fast MNU + invocation and mark the cPIC as + having the MNU case for cache flushing. */ + + /* Cogit>>#cogExtendPIC:CaseNMethod:tag:isMNUCase: */ +static NoDbgRegParms void +cogExtendPICCaseNMethodtagisMNUCase(CogMethod *cPIC, sqInt caseNMethod, sqInt caseNTag, sqInt isMNUCase) +{ + sqInt address; + sqInt operand; + usqIntptr_t target; + sqInt targetSqInt; + + compilationBreakpointclassTagisMNUCase((cPIC->selector), caseNTag, isMNUCase); + assert(!(inlineCacheTagIsYoung(caseNTag))); + + /* Caller patches to open pic if caseNMethod is young. */ + assert((caseNMethod) + && (!(isYoung(caseNMethod)))); + if ((!isMNUCase) + && (methodHasCogMethod(caseNMethod))) { + operand = 0; + target = (((sqInt)(cogMethodOf(caseNMethod)))) + cmNoCheckEntryOffset; + } + else { + operand = caseNMethod; + if (isMNUCase) { + /* cpicHasMNUCase: */ + ((((CogMethod *) ((((usqInt)cPIC)) + codeToDataDelta)))->cpicHasMNUCaseOrCMIsFullBlock) = 1; + target = (((sqInt)cPIC)) + (sizeof(CogMethod)); + } + else { + target = (((sqInt)cPIC)) + (picInterpretAbortOffset()); + } + } + + /* this isn't an MNU and we have an already cogged method to jump to + find the end address of the new case */ + address = addressOfEndOfCaseinCPIC(((cPIC->cPICNumCases)) + 1, cPIC); + rewriteCPICCaseAttagobjReftarget(address, caseNTag, operand, target); + + /* finally, rewrite the jump 3 instr before firstCPICCaseOffset to jump to the beginning of this new case */ + targetSqInt = address - cPICCaseSize; + + /* begin rewriteCPIC:caseJumpTo: */ + rewriteCPICJumpAttarget(backEnd, (((((sqInt)cPIC)) + firstCPICCaseOffset) - (jumpLongByteSize(backEnd))) - 11, targetSqInt); + + /* and update the header flag for the number of cases */ + ((((CogMethod *) ((((usqInt)cPIC)) + codeToDataDelta)))->cPICNumCases = ((cPIC->cPICNumCases)) + 1); + + /* This also implicitly flushes the read/write mapped dual zone to the read/execute zone. */ + flushICacheFromto(backEnd, ((usqInt)cPIC), (((usqInt)cPIC)) + closedPICSize); + (((usqInt)cPIC)) + closedPICSize; + + /* begin assertValidDualZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + assertCoherentCodeAtdelta(backEnd, (((usqInt)cPIC)) + cmNoCheckEntryOffset, codeToDataDelta); +# endif +} + + +/* Attempt to produce a machine code method for the bytecode method + object aMethodObj. N.B. If there is no code memory available do *NOT* + attempt to reclaim the method zone. Certain clients (e.g. ceSICMiss:) + depend on the zone remaining constant across method generation. */ + + /* Cogit>>#cogFullBlockMethod:numCopied: */ +CogMethod * +cogFullBlockMethodnumCopied(sqInt aMethodObj, sqInt numCopied) +{ + CogMethod *cogMethod; + + assert(!((methodHasCogMethod(aMethodObj)))); + if (aMethodObj == breakMethod) { + haltmsg("Compilation of breakMethod"); + } + ensureNoForwardedLiteralsIn(aMethodObj); + + /* If the generators for the alternate bytecode set are missing then interpret. */ + if (methodUsesAlternateBytecodeSet(aMethodObj)) { + if ((numElementsIn(generatorTable)) <= 0x100) { + return null; + } + bytecodeSetOffset = 0x100; + } + else { + bytecodeSetOffset = 0; + } + assert(isFullBlockMethod(aMethodObj)); + methodObj = aMethodObj; + methodHeader = methodHeaderOf(aMethodObj); + + /* lazy initialization */ + receiverTags = -1; + cogMethod = compileCogFullBlockMethod(numCopied); + if ((((((sqInt)cogMethod)) >= MaxNegativeErrorCode) && ((((sqInt)cogMethod)) <= -1))) { + if ((((sqInt)cogMethod)) == InsufficientCodeSpace) { + callForCogCompiledCodeCompaction(); + } + + /* Right now no errors should be reported, so nothing more to do. + self reportError: (self cCoerceSimple: cogMethod to: #sqInt). */ + return null; + } + + /* coInterpreter printCogMethod: cogMethod. + coInterpreter symbolicMethod: aMethodObj. + self assertValidMethodMap: cogMethod. + self disassembler disassembleMethod: cogMethod. + printInstructions := clickConfirm := true */ + return cogMethod; +} + + /* Cogit>>#cogitPostGCAction: */ +void +cogitPostGCAction(sqInt gcMode) +{ + + /* Post-GC update every full method's objectHeader to whatever it needs to be */ + assert(allMethodsHaveCorrectHeader()); + + /* The youngReferrers should be correct after a GC since that is the point at which it is + pruned. But at other times false positives or free methods on the list are acceptable. */ + assert(((!(gcMode & (GCModeFull + GCModeNewSpace)))) + || (kosherYoungReferrers())); + + /* begin ensureExecutableCodeZone */ +} + + +/* Check that the header fields onf a non-free method are consistent with + the type. Answer 0 if it is ok, otherwise answer a code for the error. */ + + /* Cogit>>#cogMethodDoesntLookKosher: */ +static NoDbgRegParms sqInt +cogMethodDoesntLookKosher(CogMethod *cogMethod) +{ + if (((((cogMethod->blockSize)) & (BytesPerWord - 1)) != 0) + || ((((cogMethod->blockSize)) < (sizeof(CogMethod))) + || (((cogMethod->blockSize)) >= 0x8000))) { + return 1; + } + if (((cogMethod->cmType)) == CMFree) { + return 2; + } + if (((cogMethod->cmType)) >= CMMethod) { + if (!((((((cogMethod->methodHeader))) & 7) == 1))) { + return 11; + } + if (!(couldBeObject((cogMethod->methodObject)))) { + return 12; + } + if ((((cogMethod->stackCheckOffset)) > 0) + && (((cogMethod->stackCheckOffset)) < cmNoCheckEntryOffset)) { + return 13; + } + return 0; + } + if (((cogMethod->cmType)) == CMOpenPIC) { + if (((cogMethod->blockSize)) != openPICSize) { + return 21; + } + if ((cogMethod->methodHeader)) { + return 22; + } + + /* Check the nextOpenPIC link unless we're compacting */ + if (((cogMethod->objectHeader)) >= 0) { + if (!((((cogMethod->methodObject)) == 0) + || (compactionInProgress + || (((cogMethod->methodObject)) == (((usqInt)(methodFor(((void *)((cogMethod->methodObject))))))))))) { + return 23; + } + } + if ((cogMethod->stackCheckOffset)) { + return 24; + } + return 0; + } + if (((cogMethod->cmType)) == CMClosedPIC) { + if (((cogMethod->blockSize)) != closedPICSize) { + return 0x1F; + } + if (!(((((cogMethod->cPICNumCases)) >= 1) && (((cogMethod->cPICNumCases)) <= MaxCPICCases)))) { + return 32; + } + if ((cogMethod->methodHeader)) { + return 33; + } + if ((cogMethod->methodObject)) { + return 34; + } + return 0; + } + return 9; +} + + +/* Attempt to create a one-case PIC for an MNU. + The tag for the case is at the send site and so doesn't need to be + generated. + */ + + /* Cogit>>#cogMNUPICSelector:receiver:methodOperand:numArgs: */ +CogMethod * +cogMNUPICSelectorreceivermethodOperandnumArgs(sqInt selector, sqInt rcvr, sqInt methodOperand, sqInt numArgs) +{ + CogMethod *actualPIC; + usqInt startAddress; + CogMethod *writablePIC; + + if ((isYoung(selector)) + || ((inlineCacheTagForInstance(rcvr)) == 0 /* picAbortDiscriminatorValue */)) { + return 0; + } + compilationBreakpointclassTagisMNUCase(selector, fetchClassTagOf(rcvr), 1); + assert(endCPICCase0); + + /* get memory in the code zone for the CPIC; if that fails we return an error code for the sender to use to work out how to blow up */ + startAddress = allocate(closedPICSize); + if (!startAddress) { + callForCogCompiledCodeCompaction(); + return 0; + } + maybeBreakGeneratingFromto(startAddress, startAddress + closedPICSize); + + /* begin ensureWritableCodeZone */ + writablePIC = ((CogMethod *) ((((usqInt)startAddress)) + codeToDataDelta)); + + /* memcpy the prototype across to our allocated space; because anything else would be silly */ + codeMemcpy(writablePIC, cPICPrototype, closedPICSize); + + /* begin fillInCPICHeader:numArgs:numCases:hasMNUCase:selector: */ + assert(!(isYoung(selector))); + (writablePIC->cmType = CMClosedPIC); + (writablePIC->objectHeader = 0); + (writablePIC->blockSize = closedPICSize); + (writablePIC->methodObject = 0); + (writablePIC->methodHeader = 0); + (writablePIC->selector = selector); + (writablePIC->cmNumArgs = numArgs); + (writablePIC->cmHasMovableLiteral = 0); + (writablePIC->cmRefersToYoung = 0); + (writablePIC->cmUsageCount = CMMaxUsageCount / 2); + + /* cpicHasMNUCase: */ + (writablePIC->cpicHasMNUCaseOrCMIsFullBlock) = 1; + (writablePIC->cPICNumCases = 1); + (writablePIC->blockEntryOffset = 0); + assert(isCMClosedPIC(((CogBlockMethod *) writablePIC))); + assert(((writablePIC->selector)) == selector); + assert(((writablePIC->cmNumArgs)) == numArgs); + assert(((writablePIC->cPICNumCases)) == 1); + assert(closedPICSize == (roundUpLength(closedPICSize))); + configureMNUCPICmethodOperandnumArgsdelta((actualPIC = ((CogMethod *) startAddress)), methodOperand, numArgs, startAddress - (((usqInt)cPICPrototype))); + + /* This also implicitly flushes the read/write mapped dual zone to the read/execute zone. */ + flushICacheFromto(backEnd, startAddress, startAddress + closedPICSize); + assert((callTargetFromReturnAddress(backEnd, startAddress + missOffset)) == (picAbortTrampolineFor(numArgs))); + + /* begin assertValidDualZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + assertCoherentCodeAtdelta(backEnd, startAddress + cmNoCheckEntryOffset, codeToDataDelta); +# endif + + return actualPIC; +} + + +/* Create an Open PIC. Temporarily create a direct call of + ceSendFromOpenPIC:. Should become a probe of the first-level method lookup + cache followed by a + call of ceSendFromOpenPIC: if the probe fails. */ + + /* Cogit>>#cogOpenPICSelector:numArgs: */ +static NoDbgRegParms CogMethod * +cogOpenPICSelectornumArgs(sqInt selector, sqInt numArgs) +{ + sqInt codeSize; + sqInt end; + usqIntptr_t fixupSize; + sqInt mapSize; + usqIntptr_t opcodeSize; + CogMethod *pic; + usqInt startAddress; + + compilationBreakpointisMNUCase(selector, 0); + startAddress = allocate(openPICSize); + if (!startAddress) { + return ((CogMethod *) InsufficientCodeSpace); + } + (methodLabel->address = startAddress); + (methodLabel->dependent = null); + + /* stack allocate the various collections so that they + are effectively garbage collected on return. */ + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 100; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + compileOpenPICnumArgs(selector, numArgs); + computeMaximumSizes(); + concretizeAt(methodLabel, startAddress); + codeSize = generateInstructionsAt(startAddress + (sizeof(CogMethod))); + + /* begin ensureWritableCodeZone */ + mapSize = generateMapAtstart((startAddress + openPICSize) - 1, startAddress + cmNoCheckEntryOffset); + assert((((entry->address)) - startAddress) == cmEntryOffset); + assert(((roundUpLength((sizeof(CogMethod)) + codeSize)) + (roundUpLength(mapSize))) <= openPICSize); + end = outputInstructionsAt(startAddress + (sizeof(CogMethod))); + pic = ((CogMethod *) ((((usqInt)startAddress)) + codeToDataDelta)); + + /* begin fillInOPICHeader:numArgs:selector: */ + (pic->cmType = CMOpenPIC); + (pic->objectHeader = 0); + (pic->blockSize = openPICSize); + + /* pic methodObject: 0. + This is also the nextOpenPIC link so don't initialize it */ + addToOpenPICList(pic); + (pic->methodHeader = 0); + (pic->selector = selector); + (pic->cmNumArgs = numArgs); + (pic->cmHasMovableLiteral = isNonImmediate(selector)); + if ((pic->cmRefersToYoung = isYoung(selector))) { + addToYoungReferrers(pic); + } + (pic->cmUsageCount = initialOpenPICUsageCount()); + + /* cpicHasMNUCase: */ + (pic->cpicHasMNUCaseOrCMIsFullBlock) = 0; + (pic->cPICNumCases = 0); + (pic->blockEntryOffset = 0); + + /* This also implicitly flushes the read/write mapped dual zone to the read/execute zone. */ + flushICacheFromto(backEnd, (((usqInt)pic)) - codeToDataDelta, ((((usqInt)pic)) - codeToDataDelta) + openPICSize); + assert(isCMOpenPIC(((CogBlockMethod *) pic))); + assert(((pic->selector)) == selector); + assert(((pic->cmNumArgs)) == numArgs); + assert((callTargetFromReturnAddress(backEnd, ((((sqInt)pic)) - codeToDataDelta) + missOffset)) == (picAbortTrampolineFor(numArgs))); + assert(openPICSize == (roundUpLength(openPICSize))); + ((((usqInt)pic)) - codeToDataDelta) + openPICSize; + + /* begin assertValidDualZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + assertCoherentCodeAtdelta(backEnd, ((((usqInt)pic)) - codeToDataDelta) + cmNoCheckEntryOffset, codeToDataDelta); +# endif + + return ((CogMethod *) startAddress); +} + + +/* Attempt to create a two-case PIC for case0CogMethod and + case1Method,case1Tag. The tag for case0CogMethod is at the send site and + so doesn't need to be generated. + case1Method may be any of + - a Cog method; link to its unchecked entry-point + - a CompiledMethod; link to ceInterpretMethodFromPIC: + - a CompiledMethod; link to ceMNUFromPICMNUMethod:receiver: */ + + /* Cogit>>#cogPICSelector:numArgs:Case0Method:Case1Method:tag:isMNUCase: */ +static NoDbgRegParms CogMethod * +cogPICSelectornumArgsCase0MethodCase1MethodtagisMNUCase(sqInt selector, sqInt numArgs, CogMethod *case0CogMethod, sqInt case1MethodOrNil, sqInt case1Tag, sqInt isMNUCase) +{ + CogMethod *actualPIC; + usqInt startAddress; + CogMethod *writablePIC; + + if (isYoung(selector)) { + return ((CogMethod *) YoungSelectorInPIC); + } + compilationBreakpointclassTagisMNUCase(selector, case1Tag, isMNUCase); + + /* get memory in the code zone for the CPIC; if that fails we return an error code for the sender to use to work out how to blow up */ + startAddress = allocate(closedPICSize); + if (!startAddress) { + return ((CogMethod *) InsufficientCodeSpace); + } + maybeBreakGeneratingFromto(startAddress, startAddress + closedPICSize); + writablePIC = ((CogMethod *) ((((usqInt)startAddress)) + codeToDataDelta)); + + /* memcpy the prototype across to our allocated space; because anything else would be silly */ + codeMemcpy(writablePIC, cPICPrototype, closedPICSize); + + /* begin fillInCPICHeader:numArgs:numCases:hasMNUCase:selector: */ + assert(!(isYoung(selector))); + (writablePIC->cmType = CMClosedPIC); + (writablePIC->objectHeader = 0); + (writablePIC->blockSize = closedPICSize); + (writablePIC->methodObject = 0); + (writablePIC->methodHeader = 0); + (writablePIC->selector = selector); + (writablePIC->cmNumArgs = numArgs); + (writablePIC->cmHasMovableLiteral = 0); + (writablePIC->cmRefersToYoung = 0); + (writablePIC->cmUsageCount = CMMaxUsageCount / 2); + + /* cpicHasMNUCase: */ + (writablePIC->cpicHasMNUCaseOrCMIsFullBlock) = isMNUCase; + (writablePIC->cPICNumCases = 2); + (writablePIC->blockEntryOffset = 0); + assert(isCMClosedPIC(((CogBlockMethod *) writablePIC))); + assert(((writablePIC->selector)) == selector); + assert(((writablePIC->cmNumArgs)) == numArgs); + assert(((writablePIC->cPICNumCases)) == 2); + assert(closedPICSize == (roundUpLength(closedPICSize))); + configureCPICCase0Case1MethodtagisMNUCasenumArgsdelta((actualPIC = ((CogMethod *) startAddress)), case0CogMethod, case1MethodOrNil, case1Tag, isMNUCase, numArgs, startAddress - (((usqInt)cPICPrototype))); + assert((callTargetFromReturnAddress(backEnd, startAddress + missOffset)) == (picAbortTrampolineFor(numArgs))); + return actualPIC; +} + + +/* Attempt to produce a machine code method for the bytecode method + object aMethodObj. N.B. If there is no code memory available do *NOT* + attempt to reclaim the method zone. Certain clients (e.g. ceSICMiss:) + depend on the zone remaining constant across method generation. */ + + /* Cogit>>#cog:selector: */ +CogMethod * +cogselector(sqInt aMethodObj, sqInt aSelectorOop) +{ + CogMethod *cogMethod; + sqInt selector; + + + /* In Newspeak we support anonymous accessors and hence tolerate the same + method being cogged multiple times. But only if the method class association is nil. */ + assert(!((methodHasCogMethod(aMethodObj)))); + selector = (aSelectorOop == (nilObject()) + ? maybeSelectorOfMethod(aMethodObj) + : aSelectorOop); + + /* coInterpreter stringOf: selector */ + if (selector) { + compilationBreakpointisMNUCase(selector, 0); + } + if (aMethodObj == breakMethod) { + haltmsg("Compilation of breakMethod"); + } + ensureNoForwardedLiteralsIn(aMethodObj); + + /* If the generators for the alternate bytecode set are missing then interpret. */ + if (methodUsesAlternateBytecodeSet(aMethodObj)) { + if ((numElementsIn(generatorTable)) <= 0x100) { + return null; + } + bytecodeSetOffset = 0x100; + } + else { + bytecodeSetOffset = 0; + } + assert(!((isFullBlockMethod(aMethodObj)))); + methodObj = aMethodObj; + methodHeader = methodHeaderOf(aMethodObj); + + /* lazy initialization */ + receiverTags = -1; + cogMethod = compileCogMethod(aSelectorOop); + if ((((((sqInt)cogMethod)) >= MaxNegativeErrorCode) && ((((sqInt)cogMethod)) <= -1))) { + if ((((sqInt)cogMethod)) == InsufficientCodeSpace) { + callForCogCompiledCodeCompaction(); + } + + /* Right now no errors should be reported, so nothing more to do. + self reportError: (self cCoerceSimple: cogMethod to: #sqInt). */ + return null; + } + + /* coInterpreter printCogMethod: cogMethod. + coInterpreter symbolicMethod: aMethodObj. + self assertValidMethodMap: cogMethod. + self disassembler disassembleMethod: cogMethod. + printInstructions := clickConfirm := true */ + return cogMethod; +} + + /* Cogit>>#collectCogConstituentFor:Annotation:Mcpc:Bcpc:Method: */ +static NoDbgRegParms sqInt +collectCogConstituentForAnnotationMcpcBcpcMethod(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *cogMethodArg) +{ + sqInt address; + sqInt annotation; + usqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + + if (!descriptor) { + return 0; + } + if (!((descriptor->isMapped))) { + return 0; + } + address = positive64BitIntegerFor(((usqInt)mcpc)); + if (!address) { + return PrimErrNoMemory; + } + storePointerUncheckedofObjectwithValue(cogConstituentIndex, topRemappableOop(), address); + storePointerUncheckedofObjectwithValue(cogConstituentIndex + 1, topRemappableOop(), (((usqInt)bcpc << 3) | 1)); + cogConstituentIndex += 2; + + /* Collect any first case classTags for closed PICs. */ + if (((!(isBackwardBranchAndAnnotation & 1))) + && (/* isSendAnnotation: */ + ((((usqInt)(isBackwardBranchAndAnnotation)) >> 1) >= IsSendCall))) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + annotation = ((usqInt)(isBackwardBranchAndAnnotation)) >> 1; + + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (((targetMethod1->cmType)) == CMClosedPIC) { + ((((CogMethod *) ((((usqInt)targetMethod1)) + codeToDataDelta)))->methodObject = classForInlineCacheTag(literal32BeforeFollowingAddress(backEnd, ((usqInt)((((sqInt)mcpc)) - 5))))); + } + } + } + return 0; +} + + /* Cogit>>#collectCogConstituentOnSpurFor:Annotation:Mcpc:Bcpc:Data: */ +static NoDbgRegParms sqInt +collectCogConstituentOnSpurForAnnotationMcpcBcpcData(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *dataArg) +{ + sqInt annotation; + usqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + + if (!descriptor) { + return 0; + } + if (!((descriptor->isMapped))) { + return 0; + } + longAtput(((0 + cogConstituentIndex) * BytesPerWord) + (((char *)dataArg)),((usqInt)mcpc)); + longAtput(((1 + cogConstituentIndex) * BytesPerWord) + (((char *)dataArg)),bcpc); + cogConstituentIndex += 2; + + /* Collect any first case classTags for closed PICs. */ + if (((!(isBackwardBranchAndAnnotation & 1))) + && (/* isSendAnnotation: */ + ((((usqInt)(isBackwardBranchAndAnnotation)) >> 1) >= IsSendCall))) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + annotation = ((usqInt)(isBackwardBranchAndAnnotation)) >> 1; + + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (((targetMethod1->cmType)) == CMClosedPIC) { + ((((CogMethod *) ((((usqInt)targetMethod1)) + codeToDataDelta)))->methodObject = classForInlineCacheTag(literal32BeforeFollowingAddress(backEnd, ((usqInt)((((sqInt)mcpc)) - 5))))); + } + } + } + return 0; +} + + +/* Answer a description of the mapping between machine code pointers and + bytecode pointers for the Cog Method. + First value is the address of the cog method. + Following values are pairs of machine code pc and bytecode pc. + dataClass is DoubleWordArray or WordArray as appropriate. */ + + /* Cogit>>#collectCogMethodConstituentOnSpur:ofClass: */ +static NoDbgRegParms sqInt +collectCogMethodConstituentOnSpurofClass(CogMethod *cogMethod, sqInt dataClass) +{ + sqInt aMethodObj; + sqInt annotation; + void *arg; + sqInt bcpc; + sqInt bsOffset; + sqInt byte; + CogBlockMethod *cogBlockMethod; + sqInt data; + BytecodeDescriptor *descriptor; + sqInt distance; + sqInt endbcpc; + sqInt errCode; + CogMethod *homeMethod; + sqInt isBackwardBranch; + sqInt isInBlock; + sqInt latestContinuation; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt nExts; + sqInt nextBcpc; + sqInt nSlots; + sqInt result; + sqInt startbcpc; + sqInt targetPC; + + if (!(((cogMethod->cmType)) >= CMMethod)) { + return positive64BitIntegerFor(((usqInt)cogMethod)); + } + cogBlockMethod = ((CogBlockMethod *) cogMethod); + if (!((cogBlockMethod->stackCheckOffset))) { + return positive64BitIntegerFor(((usqInt)cogMethod)); + } + + /* isFrameless ? */ + + /* +1 for first address */ + nSlots = ((((byteSizeOf((cogMethod->methodObject))) - (startPCOfMethodHeader((cogMethod->methodHeader)))) * 2) + (minSlotsForShortening())) + 1; + data = instantiateClassindexableSize(dataClass, nSlots); + if (!data) { + return null; + } + longAtput(firstIndexableField(data),((usqInt)cogMethod)); + cogConstituentIndex = 1; + startbcpc = startPCOfMethod((cogMethod->methodObject)); + arg = firstIndexableField(data); + + /* begin mapFor:bcpc:performUntil:arg: */ + descriptor = ((BytecodeDescriptor *) 0); + latestContinuation = 0; + mapByte = 0; + nextBcpc = 0; + assert(((cogBlockMethod->stackCheckOffset)) > 0); + mcpc = (((usqInt)cogBlockMethod)) + ((cogBlockMethod->stackCheckOffset)); + + /* The stack check maps to the start of the first bytecode, + the first bytecode being effectively after frame build. */ + result = collectCogConstituentOnSpurForAnnotationMcpcBcpcData(null, 0 + ((((usqInt)(HasBytecodePC) << 1))), ((char *) mcpc), startbcpc, arg); + if (result) { + errCode = result; + goto l1; + } + bcpc = startbcpc; + + /* In both CMMethod and CMBlock cases find the start of the map and + skip forward to the bytecode pc map entry for the stack check. */ + if (((cogBlockMethod->cmType)) >= CMMethod) { + isInBlock = (cogBlockMethod->cpicHasMNUCaseOrCMIsFullBlock); + homeMethod = ((CogMethod *) cogBlockMethod); + assert(startbcpc == (startPCOfMethodHeader((homeMethod->methodHeader)))); + map = ((((usqInt)homeMethod)) + ((homeMethod->blockSize))) - 1; + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert((annotation == IsAbsPCReference) + || ((annotation == IsObjectReference) + || ((annotation == IsRelativeCall) + || (annotation == IsDisplacementX2N)))); + latestContinuation = startbcpc; + aMethodObj = (homeMethod->methodObject); + endbcpc = (numBytesOf(aMethodObj)) - 1; + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + + /* If the method has a primitive, skip it and the error code store, if any; + Logically. these come before the stack check and so must be ignored. */ + bcpc += deltaToSkipPrimAndErrorStoreInheader(aMethodObj, (homeMethod->methodHeader)); + } + else { + isInBlock = 1; + assert(bcpc == ((cogBlockMethod->startpc))); + homeMethod = cmHomeMethod(cogBlockMethod); + map = findMapLocationForMcpcinMethod((((usqInt)cogBlockMethod)) + (sizeof(CogBlockMethod)), homeMethod); + assert(map != 0); + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert(((((usqInt)(annotation)) >> AnnotationShift) == HasBytecodePC) + || ((((usqInt)(annotation)) >> AnnotationShift) == IsDisplacementX2N)); + + /* fiducial */ + while (((annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift)) != HasBytecodePC) { + map -= 1; + } + + /* skip fiducial; i.e. the map entry for the pc immediately following the method header. */ + map -= 1; + aMethodObj = (homeMethod->methodObject); + bcpc = startbcpc - (/* blockCreationBytecodeSizeForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? AltBlockCreationBytecodeSize + : BlockCreationBytecodeSize)); + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + endbcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, -1, aMethodObj) + : 0)); + bcpc = startbcpc; + } + nExts = 0; + enumeratingCogMethod = homeMethod; + + /* Now skip up through the bytecode pc map entry for the stack check. */ + while ((((usqInt)((byteAt((void *)(map))))) >> AnnotationShift) != HasBytecodePC) { + map -= 1; + } + map -= 1; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + mcpc += (mapByte & DisplacementMask); + if (annotation >= HasBytecodePC) { + if ((annotation == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + while (1) { + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + if (isInBlock) { + if (bcpc >= endbcpc) { + errCode = 0; + goto l1; + } + } + else { + if (((descriptor->isReturn)) + && (bcpc >= latestContinuation)) { + errCode = 0; + goto l1; + } + if ((isBranch(descriptor)) + || ((descriptor->isBlockCreation))) { + /* begin latestContinuationPCFor:at:exts:in: */ + distance = ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj); + targetPC = (bcpc + ((descriptor->numBytes))) + (((distance < 0) ? 0 : distance)); + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + } + } + nextBcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj) + : 0)); + if (((descriptor->isMapped)) + || (isInBlock + && ((descriptor->isMappedInBlock)))) break; + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + isBackwardBranch = (isBranch(descriptor)) + && ((/* begin isBackwardBranch:at:exts:in: */ + assert(((descriptor->spanFunction))), + (((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj)) < 0)); + result = collectCogConstituentOnSpurForAnnotationMcpcBcpcData(descriptor, (isBackwardBranch + ? ((((usqInt)(annotation) << 1))) + 1 + : ((sqInt)((usqInt)(annotation) << 1))), ((char *) mcpc), (isBackwardBranch + ? bcpc - (2 * nExts) + : bcpc), arg); + if (result) { + errCode = result; + goto l1; + } + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + } + else { + assert(((((usqInt)(mapByte)) >> AnnotationShift) == IsDisplacementX2N) + || ((((usqInt)(mapByte)) >> AnnotationShift) == IsAnnotationExtension)); + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + + /* defensive; we exit on bcpc */ + errCode = 0; + /* end mapFor:bcpc:performUntil:arg: */ +l1: + if (errCode) { + return null; + } + if (cogConstituentIndex < nSlots) { + shortentoIndexableSize(data, cogConstituentIndex); + } + return data; +} + + +/* Answer a description of the mapping between machine code pointers and + bytecode pointers for the Cog Method. + First value is the address of the cog method. + Following values are pairs of machine code pc and bytecode pc */ + + /* Cogit>>#collectCogMethodConstituent: */ +static NoDbgRegParms sqInt +collectCogMethodConstituent(CogMethod *cogMethod) +{ + sqInt address; + sqInt aMethodObj; + sqInt annotation; + sqInt bcpc; + sqInt bsOffset; + sqInt byte; + CogBlockMethod *cogBlockMethod; + sqInt data; + BytecodeDescriptor *descriptor; + sqInt distance; + sqInt endbcpc; + sqInt errCode; + CogMethod *homeMethod; + sqInt isBackwardBranch; + sqInt isInBlock; + sqInt latestContinuation; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt nExts; + sqInt nextBcpc; + sqInt nSlots; + sqInt result; + sqInt startbcpc; + sqInt targetPC; + + if (!(((cogMethod->cmType)) >= CMMethod)) { + return positive64BitIntegerFor(((usqInt)cogMethod)); + } + cogBlockMethod = ((CogBlockMethod *) cogMethod); + if (!((cogBlockMethod->stackCheckOffset))) { + return positive64BitIntegerFor(((usqInt)cogMethod)); + } + + /* isFrameless ? */ + + /* +1 for first address */ + nSlots = ((((byteSizeOf((cogMethod->methodObject))) - (startPCOfMethodHeader((cogMethod->methodHeader)))) * 2) + (minSlotsForShortening())) + 1; + data = instantiateClassindexableSize(splObj(ClassArray), nSlots); + if (!data) { + return null; + } + pushRemappableOop(data); + + /* The iteration assumes the object is the top remappable oop */ + address = positive64BitIntegerFor(((usqInt)cogMethod)); + if (!address) { + popRemappableOop(); + return null; + } + storePointerUncheckedofObjectwithValue(0, topRemappableOop(), address); + cogConstituentIndex = 1; + startbcpc = startPCOfMethod((cogMethod->methodObject)); + + /* begin mapFor:bcpc:performUntil:arg: */ + descriptor = ((BytecodeDescriptor *) 0); + latestContinuation = 0; + mapByte = 0; + nextBcpc = 0; + assert(((cogBlockMethod->stackCheckOffset)) > 0); + mcpc = (((usqInt)cogBlockMethod)) + ((cogBlockMethod->stackCheckOffset)); + + /* The stack check maps to the start of the first bytecode, + the first bytecode being effectively after frame build. */ + result = collectCogConstituentForAnnotationMcpcBcpcMethod(null, 0 + ((((usqInt)(HasBytecodePC) << 1))), ((char *) mcpc), startbcpc, ((void *)cogMethod)); + if (result) { + errCode = result; + goto l1; + } + bcpc = startbcpc; + + /* In both CMMethod and CMBlock cases find the start of the map and + skip forward to the bytecode pc map entry for the stack check. */ + if (((cogBlockMethod->cmType)) >= CMMethod) { + isInBlock = (cogBlockMethod->cpicHasMNUCaseOrCMIsFullBlock); + homeMethod = ((CogMethod *) cogBlockMethod); + assert(startbcpc == (startPCOfMethodHeader((homeMethod->methodHeader)))); + map = ((((usqInt)homeMethod)) + ((homeMethod->blockSize))) - 1; + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert((annotation == IsAbsPCReference) + || ((annotation == IsObjectReference) + || ((annotation == IsRelativeCall) + || (annotation == IsDisplacementX2N)))); + latestContinuation = startbcpc; + aMethodObj = (homeMethod->methodObject); + endbcpc = (numBytesOf(aMethodObj)) - 1; + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + + /* If the method has a primitive, skip it and the error code store, if any; + Logically. these come before the stack check and so must be ignored. */ + bcpc += deltaToSkipPrimAndErrorStoreInheader(aMethodObj, (homeMethod->methodHeader)); + } + else { + isInBlock = 1; + assert(bcpc == ((cogBlockMethod->startpc))); + homeMethod = cmHomeMethod(cogBlockMethod); + map = findMapLocationForMcpcinMethod((((usqInt)cogBlockMethod)) + (sizeof(CogBlockMethod)), homeMethod); + assert(map != 0); + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert(((((usqInt)(annotation)) >> AnnotationShift) == HasBytecodePC) + || ((((usqInt)(annotation)) >> AnnotationShift) == IsDisplacementX2N)); + + /* fiducial */ + while (((annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift)) != HasBytecodePC) { + map -= 1; + } + + /* skip fiducial; i.e. the map entry for the pc immediately following the method header. */ + map -= 1; + aMethodObj = (homeMethod->methodObject); + bcpc = startbcpc - (/* blockCreationBytecodeSizeForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? AltBlockCreationBytecodeSize + : BlockCreationBytecodeSize)); + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + endbcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, -1, aMethodObj) + : 0)); + bcpc = startbcpc; + } + nExts = 0; + enumeratingCogMethod = homeMethod; + + /* Now skip up through the bytecode pc map entry for the stack check. */ + while ((((usqInt)((byteAt((void *)(map))))) >> AnnotationShift) != HasBytecodePC) { + map -= 1; + } + map -= 1; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + mcpc += (mapByte & DisplacementMask); + if (annotation >= HasBytecodePC) { + if ((annotation == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + while (1) { + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + if (isInBlock) { + if (bcpc >= endbcpc) { + errCode = 0; + goto l1; + } + } + else { + if (((descriptor->isReturn)) + && (bcpc >= latestContinuation)) { + errCode = 0; + goto l1; + } + if ((isBranch(descriptor)) + || ((descriptor->isBlockCreation))) { + /* begin latestContinuationPCFor:at:exts:in: */ + distance = ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj); + targetPC = (bcpc + ((descriptor->numBytes))) + (((distance < 0) ? 0 : distance)); + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + } + } + nextBcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj) + : 0)); + if (((descriptor->isMapped)) + || (isInBlock + && ((descriptor->isMappedInBlock)))) break; + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + isBackwardBranch = (isBranch(descriptor)) + && ((/* begin isBackwardBranch:at:exts:in: */ + assert(((descriptor->spanFunction))), + (((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj)) < 0)); + result = collectCogConstituentForAnnotationMcpcBcpcMethod(descriptor, (isBackwardBranch + ? ((((usqInt)(annotation) << 1))) + 1 + : ((sqInt)((usqInt)(annotation) << 1))), ((char *) mcpc), (isBackwardBranch + ? bcpc - (2 * nExts) + : bcpc), ((void *)cogMethod)); + if (result) { + errCode = result; + goto l1; + } + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + } + else { + assert(((((usqInt)(mapByte)) >> AnnotationShift) == IsDisplacementX2N) + || ((((usqInt)(mapByte)) >> AnnotationShift) == IsAnnotationExtension)); + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + + /* defensive; we exit on bcpc */ + errCode = 0; + /* end mapFor:bcpc:performUntil:arg: */ +l1: + if (errCode) { + popRemappableOop(); + return null; + } + if (cogConstituentIndex < nSlots) { + shortentoIndexableSize(topRemappableOop(), cogConstituentIndex); + } + return popRemappableOop(); +} + + /* Cogit>>#compactCogCompiledCode */ +void +compactCogCompiledCode(void) +{ + assertValidDualZone(); + assert(noCogMethodsMaximallyMarked()); + moveProfileToMethods(); + + /* begin ensureWritableCodeZone */ + markActiveMethodsAndReferents(); + freeOlderMethodsForCompaction(); + compactPICsWithFreedTargets(); + planCompaction(); + updateStackZoneReferencesToCompiledCodePreCompaction(); + relocateMethodsPreCompaction(); + assertValidDualZone(); + compactCompiledCode(); + stopsFromto(backEnd, freeStart(), (youngReferrers) - 1); + flushICacheFromto(backEnd, ((usqInt)methodZoneBase), ((usqInt)(youngReferrers))); + assert(allMethodsHaveCorrectHeader()); + assert(kosherYoungReferrers()); + assertValidDualZone(); +} + + /* Cogit>>#compactPICsWithFreedTargets */ +static void +compactPICsWithFreedTargets(void) +{ + CogMethod *cogMethod; + sqInt count; + + cogMethod = ((CogMethod *) methodZoneBase); + count = 0; + while (cogMethod < (limitZony())) { + if ((((cogMethod->cmType)) == CMClosedPIC) + && (cPICCompactAndIsNowEmpty(cogMethod))) { + ((((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)))->cmType = CMFree); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + count += 1; + } + assert(count == (numMethods())); +} + + +/* The start of a CogMethod has a call to a run-time abort routine that + either handles an in-line cache failure or a stack overflow. The routine + selects the + path depending on ReceiverResultReg; if zero it takes the stack overflow + path; if nonzero the in-line cache miss path. Neither of these paths + returns. The abort routine must be called; In the callee the method is + located by + adding the relevant offset to the return address of the call. + + N.B. This code must match that in compilePICAbort: so that the offset of + the return address of the call is the same in methods and closed PICs. */ + + /* Cogit>>#compileAbort */ +static AbstractInstruction * +compileAbort(void) +{ + stackOverflowCall = genoperandoperand(MoveCqR, 0, ReceiverResultReg); + return (sendMiss = gCall(methodAbortTrampolineFor(methodOrBlockNumArgs))); +} + + /* Cogit>>#compileBlockDispatchFrom:to: */ +static NoDbgRegParms sqInt +compileBlockDispatchFromto(sqInt lowBlockStartIndex, sqInt highBlockStartIndex) +{ + BlockStart *blockStart; + sqInt halfWay; + AbstractInstruction *jmp; + + if (lowBlockStartIndex == highBlockStartIndex) { + blockStart = blockStartAt(lowBlockStartIndex); + + /* Jump: */ + genoperand(Jump, ((sqInt)((blockStart->entryLabel)))); + return null; + } + halfWay = (highBlockStartIndex + lowBlockStartIndex) / 2; + assert(((halfWay >= lowBlockStartIndex) && (halfWay <= highBlockStartIndex))); + blockStart = blockStartAt(halfWay); + + /* N.B. FLAGS := TempReg - startpc */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, (((usqInt)(((blockStart->startpc)) + 1) << 3) | 1), TempReg); + if (lowBlockStartIndex == halfWay) { + /* JumpLessOrEqual: */ + genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)((blockStart->entryLabel)))); + compileBlockDispatchFromto(halfWay + 1, highBlockStartIndex); + return null; + } + if ((halfWay + 1) == highBlockStartIndex) { + blockStart = blockStartAt(highBlockStartIndex); + + /* JumpGreater: */ + genConditionalBranchoperand(JumpGreater, ((sqInt)((blockStart->entryLabel)))); + return compileBlockDispatchFromto(lowBlockStartIndex, halfWay); + } + jmp = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + compileBlockDispatchFromto(lowBlockStartIndex, halfWay); + if (halfWay == highBlockStartIndex) { + blockStart = blockStartAt(highBlockStartIndex); + jmpTarget(jmp, (blockStart->entryLabel)); + } + else { + jmpTarget(jmp, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + compileBlockDispatchFromto(halfWay + 1, highBlockStartIndex); + } + return 0; +} + + +/* Compile a block's entry. This looks like a dummy CogBlockMethod header + (for frame parsing) + followed by either a frame build, if a frame is required, or nothing. The + CogMethodHeader's objectHeader field is a back pointer to the method, but + this can't be filled in until code generation. */ + + /* Cogit>>#compileBlockEntry: */ +static NoDbgRegParms void +compileBlockEntry(BlockStart *blockStart) +{ + AbstractInstruction *abstractInstruction; + sqInt alignment; + + alignment = blockAlignment(); + + /* begin AlignmentNops: */ + genoperand(AlignmentNops, alignment); + (blockStart->fakeHeader = genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + switch (sizeof(CogBlockMethod)) { + case 8: + /* Fill32: */ + genoperand(Fill32, 0); + + /* Fill32: */ + genoperand(Fill32, 0); + break; + case 12: + /* Fill32: */ + genoperand(Fill32, 0); + + /* Fill32: */ + genoperand(Fill32, 0); + + /* Fill32: */ + genoperand(Fill32, 0); + break; + case 16: + /* Fill32: */ + genoperand(Fill32, 0); + + /* Fill32: */ + genoperand(Fill32, 0); + + /* Fill32: */ + genoperand(Fill32, 0); + + /* Fill32: */ + genoperand(Fill32, 0); + break; + default: + error("Case not found and no otherwise clause"); + } + + /* gets filled in later with numArgs et al + gets filled in later with numArgs et al */ + (blockStart->entryLabel = genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + if (needsFrame) { + compileBlockFrameBuild(blockStart); + if (recordBlockTrace()) { + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceTraceBlockActivationTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + } + } + else { + compileBlockFramelessEntry(blockStart); + } +} + + +/* Generate a call to aRoutine with up to 4 arguments. If resultRegOrNone is + not NoReg assign the C result to resultRegOrNone. If saveRegs, save all + registers. Hack: a negative arg value indicates an abstract register, a + non-negative value + indicates a constant. The encoding for constants is defined by + trampolineArgConstant: & trampolineArgValue:. Pass a constant as the + result of trampolineArgConstant:. */ + + /* Cogit>>#compileCallFor:numArgs:arg:arg:arg:arg:resultReg:regsToSave: */ +static NoDbgRegParms void +compileCallFornumArgsargargargargresultRegregsToSave(void *aRoutine, sqInt numArgs, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3, sqInt resultRegOrNone, sqInt regMask) +{ + AbstractInstruction *abstractInstruction; + sqInt callTargetSqInt; + sqInt delta; + sqInt numRegsPushed; + usqInt regMaskCopy; + sqInt regsToSave; + sqInt wordsPushedModAlignment; + + regsToSave = (resultRegOrNone == NoReg + ? regMask + : ((regMask | (((resultRegOrNone < 0) ? (((usqInt)(1)) >> (-resultRegOrNone)) : (1ULL << resultRegOrNone)))) - (((resultRegOrNone < 0) ? (((usqInt)(1)) >> (-resultRegOrNone)) : (1ULL << resultRegOrNone))))); + if (cStackAlignment > BytesPerWord) { + /* begin genAlignCStackGivenRegistersToBeSaved:numArgs:wordAlignment: */ + regMaskCopy = ((usqInt)regsToSave); + numRegsPushed = 0; + while (regMaskCopy != 0) { + numRegsPushed += regMaskCopy & 1; + regMaskCopy = ((regMaskCopy) >> 1); + } + if ((numRegsPushed == 0) + && ((numIntRegArgs(backEnd)) >= numArgs)) { + goto l1; + } + wordsPushedModAlignment = (numRegsPushed + ((((numArgs - (numIntRegArgs(backEnd))) < 0) ? 0 : (numArgs - (numIntRegArgs(backEnd)))))) % (cStackAlignment / BytesPerWord); + if (wordsPushedModAlignment) { + delta = (cStackAlignment / BytesPerWord) - wordsPushedModAlignment; + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, delta * BytesPerWord, SPReg); + } + /* end genAlignCStackGivenRegistersToBeSaved:numArgs:wordAlignment: */ +l1:; + } + genSaveRegs(backEnd, regsToSave); + + /* begin genMarshallNArgs:arg:arg:arg:arg: */ + /* SubCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 32, RSP); + + /* WIN64 ABI always reserves shadow space on the stack for callee to save up to 4 register parameters */ + assert(numArgs <= 4); + if (numArgs > 0) { + if (regOrConst0 < NoReg) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, -2 - regOrConst0, CArg0Reg); + } + else { + if (regOrConst0 != CArg0Reg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, regOrConst0, CArg0Reg); + } + } + if (numArgs > 1) { + if (regOrConst1 < NoReg) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, -2 - regOrConst1, CArg1Reg); + } + else { + if (regOrConst1 != CArg1Reg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, regOrConst1, CArg1Reg); + } + } + if (numArgs > 2) { + if (regOrConst2 < NoReg) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, -2 - regOrConst2, CArg2Reg); + } + else { + if (regOrConst2 != CArg2Reg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, regOrConst2, CArg2Reg); + } + } + if (numArgs > 3) { + if (regOrConst3 < NoReg) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, -2 - regOrConst3, CArg3Reg); + } + else { + if (regOrConst3 != CArg3Reg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, regOrConst3, CArg3Reg); + } + } + } + } + } + } + + /* begin CallAnyFixedRT: */ + if (is32BitSignedImmediate(backEnd, maximumDistanceFromCodeZone(((usqInt)aRoutine)))) { + callTargetSqInt = ((usqIntptr_t)aRoutine); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, callTargetSqInt); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* #CallFullRT: #CallFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(CallFull, ((usqIntptr_t)aRoutine)); + } + genWriteCResultIntoReg(backEnd, resultRegOrNone); + + /* begin genRemoveNArgsFromStack: */ + assert(numArgs <= 4); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 32, RSP); + genRestoreRegs(backEnd, regsToSave); +} + + +/* Compile the cache tag computation and the first comparison. Answer the + address of that comparison. */ + + /* Cogit>>#compileCPICEntry */ +static AbstractInstruction * +compileCPICEntry(void) +{ + entry = genGetInlineCacheClassTagFromintoforEntry(ReceiverResultReg, TempReg, 1); + + /* begin CmpR:R: */ + assert(!(0 /* (ClassReg = SPReg) */)); + genoperandoperand(CmpRR, ClassReg, TempReg); + return genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +} + + +/* Compile the abstract instructions for the entire full block method. */ + + /* Cogit>>#compileEntireFullBlockMethod: */ +static NoDbgRegParms sqInt +compileEntireFullBlockMethod(sqInt numCopied) +{ + sqInt result; + + /* begin preenMethodLabel */ + /* setLabelOffset: */ + ((methodLabel->operands))[1] = 0; + compileFullBlockEntry(); + + /* Frame build */ + compileFullBlockMethodFrameBuild(numCopied); + + /* Method body */ + if (((result = compileMethodBody())) < 0) { + return result; + } + assert(blockCount == 0); + return 0; +} + + +/* The entry code to a method checks that the class of the current receiver + matches that in the inline cache. Other non-obvious elements are that its + alignment must be + different from the alignment of the noCheckEntry so that the method map + machinery can distinguish normal and super sends (super sends bind to the + noCheckEntry). */ + + /* Cogit>>#compileEntry */ +static void +compileEntry(void) +{ + entry = genGetInlineCacheClassTagFromintoforEntry(ReceiverResultReg, TempReg, 1); + + /* begin CmpR:R: */ + assert(!(0 /* (ClassReg = SPReg) */)); + genoperandoperand(CmpRR, ClassReg, TempReg); + + /* JumpNonZero: */ + genConditionalBranchoperand(JumpNonZero, ((sqInt)sendMiss)); + noCheckEntry = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + if (((traceFlags & 64) == 64)) { + /* #CallFullRT: #CallFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(CallFull, ceTraceLinkedSendTrampoline); + } +} + + +/* Compile the abstract instructions for the entire method, including blocks. */ +/* Abort for stack overflow on full block activation (no inline cache miss + possible). The flag is SendNumArgsReg. */ + + /* Cogit>>#compileFullBlockEntry */ +static sqInt +compileFullBlockEntry(void) +{ + sqInt callTarget; + AbstractInstruction *jumpNoContextSwitch; + + stackOverflowCall = genoperandoperand(MoveCqR, 0, ReceiverResultReg); + + /* Since the only case in which this is called is the + stack overflow case we can reuse the trampoline. */ + callTarget = methodAbortTrampolineFor(methodOrBlockNumArgs); + + /* begin Call: */ + genoperand(Call, callTarget); + + /* Entries + No context switch entry */ + fullBlockNoContextSwitchEntry = genoperandoperand(MoveCqR, 0, SendNumArgsReg); + jumpNoContextSwitch = genoperand(Jump, ((sqInt)0)); + + /* AlignmentNops: */ + genoperand(AlignmentNops, ((BytesPerWord < 8) ? 8 : BytesPerWord)); + + /* Context switch entry (use ReceiverResultReg as a non-zero value; it's shorter). */ + fullBlockEntry = genoperandoperand(MoveRR, ReceiverResultReg, SendNumArgsReg); + jmpTarget(jumpNoContextSwitch, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + +/* Compile the top-level method body. */ + + /* Cogit>>#compileMethodBody */ +static sqInt +compileMethodBody(void) +{ + if (endPC < initialPC) { + return 0; + } + return compileBytecodesFromthrough(initialPC + (deltaToSkipPrimAndErrorStoreInheader(methodObj, methodHeader)), endPC); +} + + +/* The start of a PIC has a call to a run-time abort routine that either + handles a dispatch to an + interpreted method or a dispatch of an MNU case. The routine selects the + path by testing + ClassReg, which holds the inline cache tag; if equal to the + picAbortDiscriminatorValue (zero) + it takes the MNU path; if nonzero the dispatch to interpreter path. + Neither of these paths + returns. The abort routine must be called; In the callee the PIC is + located by adding the + relevant offset to the return address of the call. + + N.B. This code must match that in compileAbort so that the offset of the + return address of + the call is the same in methods and closed PICs. */ + + /* Cogit>>#compilePICAbort: */ +static NoDbgRegParms sqInt +compilePICAbort(sqInt numArgs) +{ + sqInt callTarget; + + picMNUAbort = genoperandoperand(MoveCqR, 0 /* picAbortDiscriminatorValue */, ClassReg); + callTarget = picAbortTrampolineFor(numArgs); + + /* begin Call: */ + picInterpretAbort = genoperand(Call, callTarget); + return 0; +} + + +/* Compile the compare of stackLimit against the stack pointer, jumping to + the stackOverflowCall if + the stack pointer is below the limit. Answer a bytecode annotated label + that follows the sequence. + + The stack check functions both as a genuine stack limit check to prevent + calls overflowing stack pages, + and as an event/context-switch break out. To cause an event check + (including a check for a required + context switch), stackLimit is set to the highest possible value, and + hence all stack limit checks will + fail. A path in the stack overflow abort then arranges to call event + checking if it has been requested. + + Certain block activations (e.g. valueNoContextSwitch:) must not context + switch, and in that + case, SendNumArgs is set to zero to communicate to the stack overflow + abort that it should + not perform event/context-switch (yet). */ + + /* Cogit>>#compileStackOverflowCheck: */ +static NoDbgRegParms AbstractInstruction * +compileStackOverflowCheck(sqInt canContextSwitch) +{ + AbstractInstruction *jumpSkip; + AbstractInstruction *label; + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, stackLimitAddress(), TempReg); + + /* N.B. We do not expect the backEnd to provide CmpR: SPReg R: reg, only CmpR: reg R: SPReg */ + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, SPReg); + if (canContextSwitch) { + /* JumpBelow: */ + genConditionalBranchoperand(JumpBelow, ((sqInt)stackOverflowCall)); + label = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + } + else { + jumpSkip = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, SendNumArgsReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)stackOverflowCall)); + jmpTarget(jumpSkip, (label = genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + } + + /* begin annotateBytecode: */ + (label->annotation = HasBytecodePC); + return label; +} + + +/* Generate a trampoline with up to four arguments. Generate either a call or + a jump to aRoutine + as requested by callJumpBar. If generating a call and resultRegOrNone is + not NoReg pass the C + result back in resultRegOrNone. + Hack: a negative value indicates an abstract register, a non-negative + value indicates a constant. */ + + /* Cogit>>#compileTrampolineFor:numArgs:arg:arg:arg:arg:regsToSave:pushLinkReg:resultReg: */ +static NoDbgRegParms void +compileTrampolineFornumArgsargargargargregsToSavepushLinkRegresultReg(void *aRoutine, sqInt numArgs, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3, sqInt regMask, sqInt pushLinkReg, sqInt resultRegOrNone) +{ + genSmalltalkToCStackSwitch(pushLinkReg); + compileCallFornumArgsargargargargresultRegregsToSave(aRoutine, numArgs, regOrConst0, regOrConst1, regOrConst2, regOrConst3, resultRegOrNone, regMask); + genLoadStackPointers(backEnd); + genTrampolineReturn(pushLinkReg); +} + + +/* Generate the entry code for a method to determine cmEntryOffset and + cmNoCheckEntryOffset. We + need cmNoCheckEntryOffset up front to be able to generate the map starting + from cmNoCheckEntryOffset */ +/* stack allocate the various collections so that they + are effectively garbage collected on return. */ + + /* Cogit>>#computeEntryOffsets */ +static void +computeEntryOffsets(void) +{ + usqIntptr_t fixupSize; + usqIntptr_t opcodeSize; + AbstractInstruction *sendMissCall; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 24; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + methodOrBlockNumArgs = 0; + sendMissCall = compileAbort(); + compileEntry(); + computeMaximumSizes(); + generateInstructionsAt(methodZoneBase + (sizeof(CogMethod))); + cmEntryOffset = ((entry->address)) - methodZoneBase; + cmNoCheckEntryOffset = ((noCheckEntry->address)) - methodZoneBase; + missOffset = (((sendMissCall->address)) + ((sendMissCall->machineCodeSize))) - methodZoneBase; + entryPointMask = BytesPerWord - 1; + while ((cmEntryOffset & entryPointMask) == (cmNoCheckEntryOffset & entryPointMask)) { + entryPointMask = (entryPointMask + entryPointMask) + 1; + } + if (entryPointMask >= (roundUpToMethodAlignment(backEnd, 1))) { + error("cannot differentiate checked and unchecked entry-points with current cog method alignment"); + } + checkedEntryAlignment = cmEntryOffset & entryPointMask; + uncheckedEntryAlignment = cmNoCheckEntryOffset & entryPointMask; + assert(checkedEntryAlignment != uncheckedEntryAlignment); +} + + +/* Generate the entry code for a method to determine cmEntryOffset and + cmNoCheckEntryOffset. We + need cmNoCheckEntryOffset up front to be able to generate the map starting + from cmNoCheckEntryOffset */ +/* stack allocate the various collections so that they + are effectively garbage collected on return. */ + + /* Cogit>>#computeFullBlockEntryOffsets */ +static void +computeFullBlockEntryOffsets(void) +{ + usqIntptr_t fixupSize; + usqIntptr_t opcodeSize; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 24; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + methodOrBlockNumArgs = 0; + compileFullBlockEntry(); + computeMaximumSizes(); + generateInstructionsAt(methodZoneBase + (sizeof(CogMethod))); + cbEntryOffset = ((fullBlockEntry->address)) - methodZoneBase; + cbNoSwitchEntryOffset = ((fullBlockNoContextSwitchEntry->address)) - methodZoneBase; +} + + +/* While we order variables in the CoInterpreter in order of dynamic + frequency, and hence + expect that stackPointer will be output first, C optimizers and linkers + may get their own + ideas and ``improve upon'' this ordering. So we cannot depend on + stackPointer being + at the lowest address of the variables we want to access through + VarBaseReg. Here we + choose the minimum amongst a set to try to choose a varBaseAddress that is + just less + than but within range of all variables we want to access through it. */ + + /* Cogit>>#computeGoodVarBaseAddress */ +usqInt +computeGoodVarBaseAddress(void) +{ + usqInt minAddress; + + /* stackLimit was e.g. lowest using the clang toolchain on MacOS X (prior to the use of variable_order) */ + minAddress = stackLimitAddress(); + if ((stackPointerAddress()) < minAddress) { + minAddress = stackPointerAddress(); + } + if ((framePointerAddress()) < minAddress) { + minAddress = framePointerAddress(); + } + if ((instructionPointerAddress()) < minAddress) { + minAddress = instructionPointerAddress(); + } + if ((argumentCountAddress()) < minAddress) { + minAddress = argumentCountAddress(); + } + if ((primFailCodeAddress()) < minAddress) { + minAddress = primFailCodeAddress(); + } + return minAddress; +} + + +/* This pass assigns maximum sizes to all abstract instructions and + eliminates jump fixups. + It hence assigns the maximum address an instruction will occur at which + allows the next + pass to conservatively size jumps. */ + + /* Cogit>>#computeMaximumSizes */ +static void +computeMaximumSizes(void) +{ + AbstractInstruction *abstractInstruction; + sqInt i; + sqInt relativeAddress; + + relativeAddress = 0; + for (i = 0; i < opcodeIndex; i += 1) { + maybeBreakGeneratingInstructionWithIndex(i); + abstractInstruction = abstractInstructionAt(i); + (abstractInstruction->address = relativeAddress); + (abstractInstruction->maxSize = computeMaximumSize(abstractInstruction)); + relativeAddress += (abstractInstruction->maxSize); + } +} + + +/* Configure a copy of the prototype CPIC for a two-case PIC for + case0CogMethod and + case1Method + case1Tag. + The tag for case0CogMethod is at the send site and so doesn't need to be + generated. case1Method may be any of + - a Cog method; jump to its unchecked entry-point + - a CompiledMethod; jump to the ceInterpretFromPIC trampoline + - nil; call ceMNUFromPIC + addDelta is the address change from the prototype to the new CPIC + location, needed + because the loading of the CPIC label at the end may use a literal instead + of a pc relative load. */ +/* self disassembleFrom: cPIC asInteger + (self sizeof: CogMethod) to: cPIC + asInteger + closedPICSize + */ + + /* Cogit>>#configureCPIC:Case0:Case1Method:tag:isMNUCase:numArgs:delta: */ +static NoDbgRegParms sqInt +configureCPICCase0Case1MethodtagisMNUCasenumArgsdelta(CogMethod *cPIC, CogMethod *case0CogMethod, sqInt case1Method, sqInt case1Tag, sqInt isMNUCase, sqInt numArgs, sqInt addrDelta) +{ + sqInt caseEndAddress; + sqInt jumpTargetAddr; + sqInt operand; + sqInt pc; + usqIntptr_t targetEntry; + + assert(case1Method); + + /* adjust the call at missOffset, the ceAbortXArgs */ + rewriteCallAttarget(backEnd, (((sqInt)cPIC)) + missOffset, picAbortTrampolineFor(numArgs)); + assert(!(inlineCacheTagIsYoung(case1Tag))); + if ((!isMNUCase) + && (methodHasCogMethod(case1Method))) { + operand = 0; + targetEntry = (((sqInt)(cogMethodOf(case1Method)))) + cmNoCheckEntryOffset; + } + else { + operand = ((!case1Method) + || (isYoungObject(case1Method)) + ? 0 + : case1Method); + targetEntry = (case1Method + ? (((sqInt)cPIC)) + (picInterpretAbortOffset()) + : (((sqInt)cPIC)) + (sizeof(CogMethod))); + } + + /* We do not scavenge PICs, hence we cannot cache the MNU method if it is in new space. + set the jump to the case0 method */ + + /* rewriteJumpLongAt:target: */ + rewriteCallAttarget(backEnd, (((sqInt)cPIC)) + firstCPICCaseOffset, (((sqInt)case0CogMethod)) + cmNoCheckEntryOffset); + caseEndAddress = addressOfEndOfCaseinCPIC(2, cPIC); + + /* update the cpic case */ + rewriteCPICCaseAttagobjReftarget(caseEndAddress, case1Tag, operand, ((sqInt)((isMNUCase + ? (((sqInt)cPIC)) + (sizeof(CogMethod)) + : targetEntry)))); + + /* update the loading of the CPIC address */ + pc = ((((sqInt)cPIC)) + cPICEndOfCodeOffset) - (jumpLongByteSize(backEnd)); + + /* begin relocateMethodReferenceBeforeAddress:by: */ + assert((((byteAt((void *)(pc - 6))) == 141) + && (((byteAt((void *)(pc - 5))) | (modRMRO(backEnd, 0, 0, 7))) == (modRMRO(backEnd, ModRegInd, 5, 7)))) + || (((byteAt((void *)(pc - 8))) == 141) + && (((byteAt((void *)(pc - 7))) | (modRMRO(backEnd, 0, 0, 7))) == (modRMRO(backEnd, ModRegInd, 5, 7))))); + + /* write the final desperate jump to cePICMissXArgs */ + jumpTargetAddr = cPICMissTrampolineFor(numArgs); + + /* begin rewriteJumpLongAt:target: */ + rewriteCallAttarget(backEnd, (((sqInt)cPIC)) + cPICEndOfCodeOffset, jumpTargetAddr); + return 0; +} + + +/* Configure a copy of the prototype CPIC for a one-case MNU CPIC that calls + ceMNUFromPIC for + case0Tag The tag for case0 is at the send site and so doesn't need to be + generated. addDelta is the address change from the prototype to the new + CPIC location, needed + because the loading of the CPIC label at the end may be a literal instead + of a pc-relative load. */ +/* adjust the jump at missOffset, the ceAbortXArgs */ + + /* Cogit>>#configureMNUCPIC:methodOperand:numArgs:delta: */ +static NoDbgRegParms sqInt +configureMNUCPICmethodOperandnumArgsdelta(CogMethod *cPIC, sqInt methodOperand, sqInt numArgs, sqInt addrDelta) +{ + sqInt jumpTargetAddr; + sqInt operand; + sqInt pc; + sqInt target; + + rewriteCallAttarget(backEnd, (((sqInt)cPIC)) + missOffset, picAbortTrampolineFor(numArgs)); + + /* We do not scavenge PICs, hence we cannot cache the MNU method if it is in new space. */ + operand = ((!methodOperand) + || (isYoungObject(methodOperand)) + ? 0 + : methodOperand); + + /* set the jump to the case0 method */ + jumpTargetAddr = (((sqInt)cPIC)) + (sizeof(CogMethod)); + + /* begin rewriteJumpLongAt:target: */ + rewriteCallAttarget(backEnd, (((sqInt)cPIC)) + firstCPICCaseOffset, jumpTargetAddr); + storeLiteralbeforeFollowingAddress(backEnd, operand, ((((sqInt)cPIC)) + firstCPICCaseOffset) - (jumpLongByteSize(backEnd))); + + /* rewrite the final desperate jump to cePICMissXArgs */ + jumpTargetAddr = cPICMissTrampolineFor(numArgs); + + /* begin rewriteJumpLongAt:target: */ + rewriteCallAttarget(backEnd, (((sqInt)cPIC)) + cPICEndOfCodeOffset, jumpTargetAddr); + pc = ((((sqInt)cPIC)) + cPICEndOfCodeOffset) - (jumpLongByteSize(backEnd)); + + /* begin relocateMethodReferenceBeforeAddress:by: */ + assert((((byteAt((void *)(pc - 6))) == 141) + && (((byteAt((void *)(pc - 5))) | (modRMRO(backEnd, 0, 0, 7))) == (modRMRO(backEnd, ModRegInd, 5, 7)))) + || (((byteAt((void *)(pc - 8))) == 141) + && (((byteAt((void *)(pc - 7))) | (modRMRO(backEnd, 0, 0, 7))) == (modRMRO(backEnd, ModRegInd, 5, 7))))); + + /* finally, rewrite the jump 3 instr before firstCPICCaseOffset to jump to the end of case 2, missing the actual case */ + target = addressOfEndOfCaseinCPIC(2, cPIC); + + /* begin rewriteCPIC:caseJumpTo: */ + rewriteCPICJumpAttarget(backEnd, (((((sqInt)cPIC)) + firstCPICCaseOffset) - (jumpLongByteSize(backEnd))) - 11, target); + return 0; +} + + +/* A CogMethod effectively imports the literals it uses and the methods it + calls. Count these elements so an array of imports can be computed. */ + + /* Cogit>>#countCogMethodDataImports:pc:count: */ +#if VMInvestigations +static NoDbgRegParms sqInt +countCogMethodDataImportspccount(sqInt annotation, char *mcpc, sqInt *countPtr) +{ + unsigned int cacheTag1; + usqInt entryPointUsqInt; + + if ((annotation == IsObjectReference) + || (annotation == IsRelativeCall)) { + countPtr[0] = ((countPtr[0]) + 1); + } + if (annotation >= IsSendCall) { + /* begin entryCacheTagAndCouldBeObjectAt:annotation:into: */ + cacheTag1 = literal32BeforeFollowingAddress(backEnd, ((usqInt)((((sqInt)mcpc)) - 5))); + entryPointUsqInt = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + + /* in-line cache tags are the selectors of sends if sends are unlinked, + the selectors of super sends (entry offset = cmNoCheckEntryOffset), + the selectors of open PIC sends (entry offset = cmEntryOffset, target is an Open PIC) + or in-line cache tags (classes, class indices, immediate bit patterns, etc). + Note that selectors can be immediate so there is no guarantee that they + are markable/remappable objects. Writing the block invocation three times + allows Slang to inline tagCouldBeObject tests. */ + countPtr[0] = ((countPtr[0]) + 1); + } + return 0; +} +#endif /* VMInvestigations */ + + +/* Scan the CPIC for target methods that have been freed and eliminate them. + Since the first entry cannot be eliminated, answer that the PIC should be + freed if the first entry is to a free target. Answer if the PIC is now + empty or should be freed. */ + + /* Cogit>>#cPICCompactAndIsNowEmpty: */ +static NoDbgRegParms sqInt +cPICCompactAndIsNowEmpty(CogMethod *cPIC) +{ + sqInt entryPoint; + sqInt i; + sqInt methods[MaxCPICCases]; + sqInt pc; + int tags[MaxCPICCases]; + CogMethod *targetMethod; + sqInt targets[MaxCPICCases]; + sqInt used; + sqInt valid; + + used = 0; + for (i = 1; i <= ((cPIC->cPICNumCases)); i += 1) { + pc = addressOfEndOfCaseinCPIC(i, cPIC); + entryPoint = (jumpLongTargetBeforeFollowingAddress(backEnd, pc)); + valid = 1; + + /* Collect all target triples except for triples whose entry-point is a freed method */ + if (!(/* containsAddress: */ + ((((usqInt)cPIC)) <= (((usqInt)entryPoint))) + && (((((usqInt)cPIC)) + ((cPIC->blockSize))) >= (((usqInt)entryPoint))))) { + targetMethod = ((CogMethod *) (entryPoint - cmNoCheckEntryOffset)); + assert((isCMMethodEtAl(((CogBlockMethod *) targetMethod))) + || (isCMFree(((CogBlockMethod *) targetMethod)))); + if (((targetMethod->cmType)) == CMFree) { + if (i == 1) { + return 1; + } + valid = 0; + } + } + if (valid) { + tags[used] = ((i > 1 + ? literal32BeforeFollowingAddress(backEnd, pc - (jumpLongConditionalByteSize(backEnd))) + : 0)); + targets[used] = entryPoint; + methods[used] = (literalBeforeFollowingAddress(backEnd, pc - ((i == 1 + ? jumpLongByteSize(backEnd) + : (jumpLongConditionalByteSize(backEnd)) + (cmpC32RTempByteSize(backEnd)))))); + used += 1; + } + } + if (used == ((cPIC->cPICNumCases))) { + return 0; + } + if (!used) { + return 1; + } + ((((CogMethod *) ((((usqInt)cPIC)) + codeToDataDelta)))->cPICNumCases = used); + if (used == 1) { + pc = addressOfEndOfCaseinCPIC(2, cPIC); + + /* begin rewriteCPIC:caseJumpTo: */ + rewriteCPICJumpAttarget(backEnd, (((((sqInt)cPIC)) + firstCPICCaseOffset) - (jumpLongByteSize(backEnd))) - 11, pc); + return 0; + } + + /* the first entry cannot change... */ + for (i = 1; i < used; i += 1) { + pc = addressOfEndOfCaseinCPIC(i + 1, cPIC); + rewriteCPICCaseAttagobjReftarget(pc, tags[i], methods[i], targets[i]); + } + + /* finally, rewrite the jump 3 instr before firstCPICCaseOffset to jump to the beginning of this new case */ + + /* begin rewriteCPIC:caseJumpTo: */ + rewriteCPICJumpAttarget(backEnd, (((((sqInt)cPIC)) + firstCPICCaseOffset) - (jumpLongByteSize(backEnd))) - 11, pc - cPICCaseSize); + return 0; +} + + +/* The first case in a CPIC doesn't have a class reference so we need only + step over actually usd subsequent cases. + */ + + /* Cogit>>#cPICHasForwardedClass: */ +static NoDbgRegParms sqInt +cPICHasForwardedClass(CogMethod *cPIC) +{ + unsigned int classIndex; + sqInt i; + sqInt pc; + + + /* start by finding the address of the topmost case, the cPICNumCases'th one */ + pc = (addressOfEndOfCaseinCPIC((cPIC->cPICNumCases), cPIC)) - (jumpLongConditionalByteSize(backEnd)); + for (i = 2; i <= ((cPIC->cPICNumCases)); i += 1) { + classIndex = literal32BeforeFollowingAddress(backEnd, pc); + if (isForwardedClassIndex(classIndex)) { + return 1; + } + + /* since we started at the top, we can just add the case size each time to move on to the next case */ + pc += cPICCaseSize; + } + return 0; +} + + +/* scan the CPIC for target methods that have been freed. */ + + /* Cogit>>#cPICHasFreedTargets: */ +static NoDbgRegParms sqInt +cPICHasFreedTargets(CogMethod *cPIC) +{ + sqInt entryPoint; + sqInt i; + sqInt pc; + CogMethod *targetMethod; + + for (i = 1; i <= ((cPIC->cPICNumCases)); i += 1) { + pc = addressOfEndOfCaseinCPIC(i, cPIC); + entryPoint = (jumpLongTargetBeforeFollowingAddress(backEnd, pc)); + + /* Find target from jump. Ignore jumps to the interpret and MNU calls within this PIC */ + if (!(/* containsAddress: */ + ((((usqInt)cPIC)) <= (((usqInt)entryPoint))) + && (((((usqInt)cPIC)) + ((cPIC->blockSize))) >= (((usqInt)entryPoint))))) { + targetMethod = ((CogMethod *) (entryPoint - cmNoCheckEntryOffset)); + assert((isCMMethodEtAl(((CogBlockMethod *) targetMethod))) + || (isCMFree(((CogBlockMethod *) targetMethod)))); + if (((targetMethod->cmType)) == CMFree) { + return 1; + } + } + } + return 0; +} + + +/* Whimsey; we want 16rCA5E10 + cPICPrototypeCaseOffset to be somewhere in + the middle of the zone. + */ + + /* Cogit>>#cPICPrototypeCaseOffset */ +static sqInt +cPICPrototypeCaseOffset(void) +{ + return ((methodZoneBase + (youngReferrers)) / 2) - 13262352; +} + + +/* Are any of the jumps from this CPIC to targetMethod? */ + + /* Cogit>>#cPIC:HasTarget: */ +static NoDbgRegParms sqInt +cPICHasTarget(CogMethod *cPIC, CogMethod *targetMethod) +{ + sqInt i; + sqInt pc; + usqInt target; + + target = (((usqInt)targetMethod)) + cmNoCheckEntryOffset; + pc = (((sqInt)cPIC)) + firstCPICCaseOffset; + + /* Since this is a fast test doing simple compares we don't need to care that some + cases have nonsense addresses in there. Just zip on through. + First jump is unconditional; subsequent ones are conditional */ + if (target == (jumpLongTargetBeforeFollowingAddress(backEnd, pc))) { + return 1; + } + for (i = 2; i <= MaxCPICCases; i += 1) { + pc += cPICCaseSize; + if (target == (jumpLongTargetBeforeFollowingAddress(backEnd, pc))) { + return 1; + } + } + return 0; +} + + +/* Answer an Array of the PIC's selector, followed by class and + targetMethod/doesNotUnderstand: for each entry in the PIC. + */ + + /* Cogit>>#createCPICData: */ +static NoDbgRegParms sqInt +createCPICData(CogMethod *cPIC) +{ + sqInt class; + usqInt entryPoint; + sqInt i; + sqInt pc; + sqInt picData; + sqInt target; + CogMethod *targetMethod; + + assert((((cPIC->methodObject)) == 0) + || (addressCouldBeOop((cPIC->methodObject)))); + picData = instantiateClassindexableSize(classArray(), (((cPIC->cPICNumCases)) * 2) + 1); + if (!picData) { + return picData; + } + storePointerUncheckedofObjectwithValue(0, picData, (cPIC->selector)); + for (i = 1; i <= ((cPIC->cPICNumCases)); i += 1) { + pc = addressOfEndOfCaseinCPIC(i, cPIC); + if (i == 1) { + /* first case may have been collected and stored here by collectCogConstituentFor:Annotation:Mcpc:Bcpc:Method: */ + class = (cPIC->methodObject); + if (!class) { + class = nilObject(); + } + entryPoint = jumpLongTargetBeforeFollowingAddress(backEnd, pc); + } + else { + class = classForInlineCacheTag(literal32BeforeFollowingAddress(backEnd, pc - (jumpLongConditionalByteSize(backEnd)))); + entryPoint = jumpLongTargetBeforeFollowingAddress(backEnd, pc); + } + + /* Find target from jump. A jump to the MNU entry-point should collect #doesNotUnderstand: */ + if (/* containsAddress: */ + ((((usqInt)cPIC)) <= (((usqInt)entryPoint))) + && (((((usqInt)cPIC)) + ((cPIC->blockSize))) >= (((usqInt)entryPoint)))) { + target = splObj(SelectorDoesNotUnderstand); + } + else { + targetMethod = ((CogMethod *) (entryPoint - cmNoCheckEntryOffset)); + assert(isCMMethodEtAl(((CogBlockMethod *) targetMethod))); + target = (targetMethod->methodObject); + } + storePointerUncheckedofObjectwithValue((i * 2) - 1, picData, class); + storePointerUncheckedofObjectwithValue(i * 2, picData, target); + } + beRootIfOld(picData); + ((((CogMethod *) ((((usqInt)cPIC)) + codeToDataDelta)))->methodObject = 0); + return picData; +} + + +/* Division is a little weird on some processors. Defer to the backEnd + to allow it to generate any special code it may need to. */ + + /* Cogit>>#DivR:R:Quo:Rem: */ +static NoDbgRegParms AbstractInstruction * +gDivRRQuoRem(sqInt rDivisor, sqInt rDividend, sqInt rQuotient, sqInt rRemainder) +{ + genDivRRQuoRem(backEnd, rDivisor, rDividend, rQuotient, rRemainder); + return abstractInstructionAt(opcodeIndex - 1); +} + + +/* Return the default number of bytes to allocate for native code at startup. + The actual value can be set via vmParameterAt: and/or a preference in the + ini file. */ + + /* Cogit>>#defaultCogCodeSize */ +int +defaultCogCodeSize(void) +{ + return 1433600; +} + + +/* Answer the number of bytecodes to skip to get to the first bytecode + past the primitive call and any store of the error code. */ + + /* Cogit>>#deltaToSkipPrimAndErrorStoreIn:header: */ +static NoDbgRegParms sqInt +deltaToSkipPrimAndErrorStoreInheader(sqInt aMethodObj, sqInt aMethodHeader) +{ + return (/* methodUsesPrimitiveErrorCode:header: */ + ((primitiveIndexOfMethodheader(aMethodObj, aMethodHeader)) > 0) + && ((longStoreBytecodeForHeader(aMethodHeader)) == (fetchByteofObject((startPCOfMethodHeader(aMethodHeader)) + (sizeOfCallPrimitiveBytecode(aMethodHeader)), aMethodObj))) + ? (sizeOfCallPrimitiveBytecode(aMethodHeader)) + (sizeOfLongStoreTempBytecode(aMethodHeader)) + : 0); +} + + /* Cogit>>#endPCOf: */ +static NoDbgRegParms sqInt +endPCOf(sqInt aMethod) +{ + sqInt bsOffset; + sqInt byte; + BytecodeDescriptor *descriptor; + sqInt distance; + sqInt end; + sqInt latestContinuation; + sqInt nExts; + sqInt pc; + sqInt prim; + sqInt targetPC; + + pc = (latestContinuation = startPCOfMethod(aMethod)); + if (((prim = primitiveIndexOf(aMethod))) > 0) { + if (isQuickPrimitiveIndex(prim)) { + return pc - 1; + } + } + bsOffset = /* bytecodeSetOffsetFor: */ + (methodUsesAlternateBytecodeSet(aMethod) + ? 0x100 + : 0); + nExts = 0; + end = numBytesOf(aMethod); + while (pc <= end) { + byte = fetchByteofObject(pc, aMethod); + descriptor = generatorAt(byte + bsOffset); + if (((descriptor->isReturn)) + && (pc >= latestContinuation)) { + end = pc; + } + if ((isBranch(descriptor)) + || ((descriptor->isBlockCreation))) { + distance = ((descriptor->spanFunction))(descriptor, pc, nExts, aMethod); + targetPC = (pc + ((descriptor->numBytes))) + distance; + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + if ((descriptor->isBlockCreation)) { + pc += distance; + } + } + else { + } + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + pc += (descriptor->numBytes); + } + return end; +} + + +/* This is a static version of ceEnterCogCodePopReceiverReg for + break-pointing when debugging in C. Marked so the code generator + won't delete it. */ + + /* Cogit>>#enterCogCodePopReceiver */ +static void +enterCogCodePopReceiver(void) +{ + realCEEnterCogCodePopReceiverReg(); + + /* (and this exists only to reference Debug) */ + if (!Debug) { + error("what??"); + } +} + + +/* Answer if the entryPoint's tag is expected to be a selector reference, as + opposed to a class tag. + */ + + /* Cogit>>#entryPointTagIsSelector: */ +static NoDbgRegParms sqInt +entryPointTagIsSelector(sqInt entryPoint) +{ + return (entryPoint < methodZoneBase) + || (((entryPoint & entryPointMask) == uncheckedEntryAlignment) + || (((entryPoint & entryPointMask) == checkedEntryAlignment) + && ((((((CogMethod *) (entryPoint - cmEntryOffset)))->cmType)) == CMOpenPIC))); +} + + +/* Use asserts to check if the ClosedPICPrototype is as expected from + compileClosedPICPrototype, and can be updated as required via + rewriteCPICCaseAt:tag:objRef:target:. If all asserts pass, answer + 0, otherwise answer a bit mask identifying all the errors. */ +/* self disassembleFrom: methodZoneBase + (self sizeof: CogMethod) to: + methodZoneBase + closedPICSize + */ + + /* Cogit>>#expectedClosedPICPrototype: */ +static NoDbgRegParms sqInt +expectedClosedPICPrototype(CogMethod *cPIC) +{ + unsigned int classTag; + sqInt classTagPC; + usqInt entryPoint; + sqInt errors; + sqInt i; + sqInt methodObjPC; + sqInt object; + usqInt pc; + + errors = 0; + pc = (((usqInt)cPIC)) + firstCPICCaseOffset; + + /* First jump is unconditional; subsequent ones are conditional */ + object = literalBeforeFollowingAddress(backEnd, pc - (jumpLongByteSize(backEnd))); + if (!(asserta(object == (firstPrototypeMethodOop())))) { + errors = 1; + } + entryPoint = jumpLongTargetBeforeFollowingAddress(backEnd, pc); + if (!(asserta(entryPoint == ((cPICPrototypeCaseOffset()) + 13262352)))) { + errors += 2; + } + for (i = 1; i < MaxCPICCases; i += 1) { + pc += cPICCaseSize; + + /* verify information in case is as expected. */ + methodObjPC = (pc - (jumpLongConditionalByteSize(backEnd))) - (cmpC32RTempByteSize(backEnd)); + object = literalBeforeFollowingAddress(backEnd, methodObjPC); + if (!(asserta(object == ((subsequentPrototypeMethodOop()) + i)))) { + errors = errors | 4; + } + classTagPC = pc - (jumpLongConditionalByteSize(backEnd)); + classTag = literal32BeforeFollowingAddress(backEnd, classTagPC); + if (!(asserta(classTag == (0xBABE1F15U + i)))) { + errors = errors | 8; + } + entryPoint = jumpLongTargetBeforeFollowingAddress(backEnd, pc); + if (!(asserta(entryPoint == (((cPICPrototypeCaseOffset()) + 13262352) + (i * 16))))) { + errors = errors | 16; + } + + /* change case via rewriteCPICCaseAt:tag:objRef:target: */ + rewriteCPICCaseAttagobjReftarget(pc, classTag ^ 0x5A5A5A5A, object ^ 0xA5A5A5A5U, entryPoint ^ 0x55AA50); + object = literalBeforeFollowingAddress(backEnd, methodObjPC); + if (!(asserta(object == (((subsequentPrototypeMethodOop()) + i) ^ 0xA5A5A5A5U)))) { + errors = errors | 32; + } + classTag = literal32BeforeFollowingAddress(backEnd, classTagPC); + if (!(asserta(classTag == ((0xBABE1F15U + i) ^ 0x5A5A5A5A)))) { + errors = errors | 64; + } + entryPoint = jumpLongTargetBeforeFollowingAddress(backEnd, pc); + if (!(asserta(entryPoint == ((((cPICPrototypeCaseOffset()) + 13262352) + (i * 16)) ^ 0x55AA50)))) { + errors = errors | 128; + } + + /* finally restore case to the original state */ + rewriteCPICCaseAttagobjReftarget(pc, classTag ^ 0x5A5A5A5A, object ^ 0xA5A5A5A5U, entryPoint ^ 0x55AA50); + } + entryPoint = jumpLongTargetBeforeFollowingAddress(backEnd, (((usqInt)cPIC)) + cPICEndOfCodeOffset); + if (!(asserta(entryPoint == (cPICMissTrampolineFor(0))))) { + errors += 0x100; + } + return errors; +} + + +/* 224 11100000 aaaaaaaa Extend A (Ext A = Ext A prev * 256 + Ext A) */ + + /* Cogit>>#extABytecode */ +static sqInt +extABytecode(void) +{ + extA = (((((usqInt)(extA) << 8)))) + byte1; + return 0; +} + + +/* 225 11100001 sbbbbbbb Extend B (Ext B = Ext B prev * 256 + Ext B) */ + + /* Cogit>>#extBBytecode */ +static sqInt +extBBytecode(void) +{ + extB = ((numExtB == 0) + && (byte1 > 0x7F) + ? byte1 - 0x100 + : (((((usqInt)(extB) << 8)))) + byte1); + numExtB += 1; + return 0; +} + + +/* Fill in the block headers now we know the exact layout of the code. */ + + /* Cogit>>#fillInBlockHeadersAt: */ +static NoDbgRegParms sqInt +fillInBlockHeadersAt(sqInt startAddress) +{ + sqInt aCogMethodOrInteger; + CogBlockMethod *blockHeader; + BlockStart *blockStart; + sqInt i; + + if (!(needsFrame + && (blockCount > 0))) { + return null; + } + if (blockNoContextSwitchOffset) { + assert(blockNoContextSwitchOffset == (((blockEntryLabel->address)) - ((blockEntryNoContextSwitch->address)))); + } + else { + blockNoContextSwitchOffset = ((blockEntryLabel->address)) - ((blockEntryNoContextSwitch->address)); + } + for (i = 0; i < blockCount; i += 1) { + blockStart = blockStartAt(i); + aCogMethodOrInteger = (((blockStart->fakeHeader))->address); + + /* begin writableBlockMethodFor: */ + blockHeader = ((CogBlockMethod *) ((((usqInt)aCogMethodOrInteger)) + codeToDataDelta)); + (blockHeader->homeOffset = ((((blockStart->fakeHeader))->address)) - startAddress); + (blockHeader->startpc = (blockStart->startpc)); + (blockHeader->cmType = CMBlock); + (blockHeader->cmNumArgs = (blockStart->numArgs)); + (blockHeader->cbUsesInstVars = (blockStart->hasInstVarRef)); + (blockHeader->stackCheckOffset = ((blockStart->stackCheckLabel) + ? ((((blockStart->stackCheckLabel))->address)) - ((((blockStart->fakeHeader))->address)) + : 0)); + } + return 0; +} + + +/* Fill in the header for theCogMethod method. This may be located at the + writable mapping. */ + + /* Cogit>>#fillInMethodHeader:size:selector: */ +static NoDbgRegParms void +fillInMethodHeadersizeselector(CogMethod *method, sqInt size, sqInt selector) +{ + sqInt actualMethodLocation; + CogMethod *originalMethod; + sqInt rawHeader; + + actualMethodLocation = (((usqInt)method)) - codeToDataDelta; + (method->cmType = CMMethod); + (method->objectHeader = nullHeaderForMachineCodeMethod()); + (method->blockSize = size); + (method->methodObject = methodObj); + rawHeader = rawHeaderOf(methodObj); + + /* If the method has already been cogged (e.g. Newspeak accessors) then + leave the original method attached to its cog method, but get the right header. */ + if (isCogMethodReference(rawHeader)) { + originalMethod = ((CogMethod *) rawHeader); + assert(((originalMethod->blockSize)) == size); + assert(methodHeader == ((originalMethod->methodHeader))); + } + else { + rawHeaderOfput(methodObj, actualMethodLocation); + } + (method->methodHeader = methodHeader); + (method->selector = selector); + (method->cmNumArgs = argumentCountOfMethodHeader(methodHeader)); + (method->cmHasMovableLiteral = hasMovableLiteral); + if ((method->cmRefersToYoung = hasYoungReferent)) { + addToYoungReferrers(method); + } + (method->cmUsageCount = initialMethodUsageCount()); + + /* cpicHasMNUCase: */ + (method->cpicHasMNUCaseOrCMIsFullBlock) = 0; + (method->cmUsesPenultimateLit = maxLitIndex >= ((literalCountOfMethodHeader(methodHeader)) - 2)); + (method->blockEntryOffset = (blockEntryLabel + ? ((blockEntryLabel->address)) - actualMethodLocation + : 0)); + + /* This can be an error check since a large stackCheckOffset is caused by compiling + a machine-code primitive, and hence depends on the Cogit, not the input method. */ + if (needsFrame) { + if (!((((stackCheckLabel->address)) - actualMethodLocation) <= MaxStackCheckOffset)) { + error("too much code for stack check offset"); + } + } + (method->stackCheckOffset = (needsFrame + ? ((stackCheckLabel->address)) - actualMethodLocation + : 0)); + assert((callTargetFromReturnAddress(backEnd, actualMethodLocation + missOffset)) == (methodAbortTrampolineFor((method->cmNumArgs)))); + assert(size == (roundUpLength(size))); + + /* begin assertValidDualZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + assertCoherentCodeAtdelta(backEnd, actualMethodLocation + cmNoCheckEntryOffset, codeToDataDelta); +# endif +} + + /* Cogit>>#findBackwardBranch:IsBackwardBranch:Mcpc:Bcpc:MatchingBcpc: */ +static NoDbgRegParms sqInt +findBackwardBranchIsBackwardBranchMcpcBcpcMatchingBcpc(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *targetBcpc) +{ + return ((((isBackwardBranchAndAnnotation & 1) != 0)) + && ((((sqInt)targetBcpc)) == bcpc) + ? ((sqInt)mcpc) + : 0); +} + + /* Cogit>>#findBlockMethodWithEntry:startBcpc: */ +static NoDbgRegParms usqInt +findBlockMethodWithEntrystartBcpc(sqInt blockEntryMcpc, sqInt startBcpc) +{ + CogBlockMethod *cogBlockMethod; + + cogBlockMethod = ((CogBlockMethod *) (blockEntryMcpc - (sizeof(CogBlockMethod)))); + if (((cogBlockMethod->startpc)) == startBcpc) { + return ((usqInt)cogBlockMethod); + } + return 0; +} + + /* Cogit>>#findMapLocationForMcpc:inMethod: */ +static NoDbgRegParms sqInt +findMapLocationForMcpcinMethod(usqInt targetMcpc, CogMethod *cogMethod) +{ + sqInt annotation; + sqInt map; + sqInt mapByte; + usqInt mcpc; + + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + if (mcpc == targetMcpc) { + return map; + } + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + if (annotation != IsAnnotationExtension) { + mcpc += 1 /* codeGranularity */ * ((annotation + ? mapByte & DisplacementMask + : ((sqInt)((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift)))); + } + if (mcpc >= targetMcpc) { + assert(mcpc == targetMcpc); + if (!annotation) { + map -= 1; + mapByte = byteAt((void *)(map)); + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + assert(annotation > IsAnnotationExtension); + } + return map; + } + map -= 1; + } + return 0; +} + + +/* Find the CMMethod or CMBlock that has zero-relative startbcpc as its first + bytecode pc. + As this is for cannot resume processing and/or conversion to machine-code + on backward + branch, it doesn't have to be fast. Enumerate block returns and map to + bytecode pcs. */ + + /* Cogit>>#findMethodForStartBcpc:inHomeMethod: */ +CogBlockMethod * +findMethodForStartBcpcinHomeMethod(sqInt startbcpc, CogMethod *cogMethod) +{ + assert(isCMMethodEtAl(((CogBlockMethod *) cogMethod))); + if (startbcpc == (startPCOfMethodHeader((cogMethod->methodHeader)))) { + return ((CogBlockMethod *) cogMethod); + } + assert(((cogMethod->blockEntryOffset)) != 0); + return ((CogBlockMethod *) (blockDispatchTargetsForperformarg(cogMethod, findBlockMethodWithEntrystartBcpc, startbcpc))); +} + + +/* Machine code addresses map to the following bytecode for all bytecodes + except backward branches, where they map to the backward branch itself. + This is so that loops continue, rather than terminate prematurely. */ + + /* Cogit>>#find:IsBackwardBranch:Mcpc:Bcpc:MatchingMcpc: */ +static NoDbgRegParms sqInt +findIsBackwardBranchMcpcBcpcMatchingMcpc(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *targetMcpc) +{ + return (targetMcpc == mcpc + ? ((!descriptor) + || (((isBackwardBranchAndAnnotation & 1) != 0)) + ? bcpc + : bcpc + ((descriptor->numBytes))) + : 0); +} + + /* Cogit>>#firstMappedPCFor: */ +static NoDbgRegParms usqInt +firstMappedPCFor(CogMethod *cogMethod) +{ + return ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); +} + + +/* Answer a fake value for the first method oop in the PIC prototype. + Since we use MoveUniqueCw:R: it must not be confused with a + method-relative address. */ + + /* Cogit>>#firstPrototypeMethodOop */ +static sqInt +firstPrototypeMethodOop(void) +{ + return (/* addressIsInCurrentCompilation: */ + ((((usqInt)0x5EAF00D)) >= ((methodLabel->address))) + && ((((usqInt)0x5EAF00D)) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize)))) + ? 0xCA7F00D + : 0x5EAF00D); +} + + /* Cogit>>#fixupAt: */ +static NoDbgRegParms BytecodeFixup * +fixupAt(sqInt fixupPC) +{ + return fixupAtIndex(fixupPC - initialPC); +} + + /* Cogit>>#flagCogMethodForBecome: */ +void +flagCogMethodForBecome(CogMethod *cogMethod) +{ + assert(isCMMethodEtAl(((CogBlockMethod *) cogMethod))); + + /* begin ensureWritableCodeZone */ + ((((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)))->cmType = CMMethodFlaggedForBecome); +} + + /* Cogit>>#followForwardedLiteralsImplementationIn: */ +static NoDbgRegParms void +followForwardedLiteralsImplementationIn(CogMethod *cogMethod) +{ + sqInt annotation; + sqInt hasYoungObj; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + CogMethod *writableCogMethod; + + assert((!(isCMMethodEtAl(((CogBlockMethod *) cogMethod)))) + || (!(isForwarded((cogMethod->methodObject))))); + writableCogMethod = ((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)); + hasYoungObj = isYoung((cogMethod->methodObject)); + if (shouldRemapOop((cogMethod->selector))) { + (writableCogMethod->selector = remapObj((cogMethod->selector))); + if (isYoung((cogMethod->selector))) { + hasYoungObj = 1; + } + } + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = remapIfObjectRefpchasYoung(annotation, ((char *) mcpc), ((void *)((&hasYoungObj)))); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1: + if (hasYoungObj) { + ensureInYoungReferrers(cogMethod); + } + else { + (writableCogMethod->cmRefersToYoung = 0); + } +} + + /* Cogit>>#followForwardedLiteralsIn: */ +void +followForwardedLiteralsIn(CogMethod *cogMethod) +{ + unsigned char wasInYoungReferrers; + + /* begin ensureWritableCodeZone */ + wasInYoungReferrers = (cogMethod->cmRefersToYoung); + followForwardedLiteralsImplementationIn(cogMethod); + if (wasInYoungReferrers + && (!((cogMethod->cmRefersToYoung)))) { + pruneYoungReferrers(); + } + + /* begin ensureExecutableCodeZone */ +} + + +/* To avoid runtime checks on literal variable and literal accesses in == and + ~~, + we follow literals in methods having movable literals in the postBecome + action. To avoid scanning every method, we annotate cogMethods with the + cmHasMovableLiteral flag. */ + + /* Cogit>>#followMovableLiteralsAndUpdateYoungReferrers */ +void +followMovableLiteralsAndUpdateYoungReferrers(void) +{ + CogMethod *cogMethod; + + assert(kosherYoungReferrers()); + + /* methodZone firstBogusYoungReferrer + methodZone occurrencesInYoungReferrers: methodZone firstBogusYoungReferrer */ + codeModified = 0; + + /* begin ensureWritableCodeZone */ + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (!((((cogMethod->cmType)) == CMFree) + || (((cogMethod->cmType)) == CMMethodFlaggedForBecome))) { + if ((cogMethod->cmHasMovableLiteral)) { + followForwardedLiteralsImplementationIn(cogMethod); + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + pruneYoungReferrers(); + if (codeModified) { + flushICacheFromto(backEnd, ((usqInt)codeBase), freeStart()); + } + + /* After updating oops in inline caches we need to flush the icache. + And ensure code zone is executable. May have pruned young referrers... */ + + /* begin ensureExecutableCodeZone */ +} + + +/* N.B. because becomeEffectFlags indicates whether jitted methods were + becommed or not, if this method is called flagged methods exist, will be + freed, and so on. So there is no need to check. Just do it. */ + + /* Cogit>>#freeBecomeFlaggedMethods */ +void +freeBecomeFlaggedMethods(void) +{ + CogMethod *cogMethod; + + /* begin ensureWritableCodeZone */ + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) == CMMethodFlaggedForBecome) { + freeMethod(cogMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + unlinkSendsToFree(); + + /* begin ensureExecutableCodeZone */ +} + + /* Cogit>>#freeCogMethod: */ +void +freeCogMethod(CogMethod *cogMethod) +{ + moveProfileToMethods(); + freeMethod(cogMethod); + + /* begin ensureExecutableCodeZone */ +} + + +/* Free machine-code methods whose compiled methods are unmarked + and open PICs whose selectors are not marked, and closed PICs that + refer to unmarked objects. */ + + /* Cogit>>#freeUnmarkedMachineCode */ +void +freeUnmarkedMachineCode(void) +{ + CogMethod *cogMethod; + sqInt freedMethod; + + moveProfileToMethods(); + freedMethod = 0; + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if ((((cogMethod->cmType)) >= CMMethod) + && (!(isMarked((cogMethod->methodObject))))) { + freedMethod = 1; + freeMethod(cogMethod); + } + if ((((cogMethod->cmType)) == CMOpenPIC) + && ((!(isImmediate((cogMethod->selector)))) + && (!(isMarked((cogMethod->selector)))))) { + freedMethod = 1; + freeMethod(cogMethod); + } + if ((((cogMethod->cmType)) == CMClosedPIC) + && (closedPICRefersToUnmarkedObject(cogMethod))) { + freedMethod = 1; + freeMethod(cogMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if (freedMethod) { + unlinkSendsToFree(); + } + + /* begin ensureExecutableCodeZone */ +} + + +/* Call ceSendMustBeBooleanTo: via the relevant trampoline. */ + + /* Cogit>>#genCallMustBeBooleanFor: */ +static NoDbgRegParms AbstractInstruction * +genCallMustBeBooleanFor(sqInt boolean) +{ + AbstractInstruction *abstractInstruction; + sqInt callTarget; + + callTarget = (boolean == (falseObject()) + ? ceSendMustBeBooleanAddFalseTrampoline + : ceSendMustBeBooleanAddTrueTrampoline); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, callTarget); + (abstractInstruction->annotation = IsRelativeCall); + return abstractInstruction; +} + + /* Cogit>>#genConditionalBranch:operand: */ +static NoDbgRegParms AbstractInstruction * +genConditionalBranchoperand(sqInt opcode, sqInt operandOne) +{ + return genoperand(opcode, operandOne); +} + + +/* An enilopmart (the reverse of a trampoline) is a piece of code that makes + the system-call-like transition from the C runtime into generated machine + code. The desired arguments and entry-point are pushed on a stackPage's + stack. The enilopmart pops off the values to be loaded into registers and + then executes a return instruction to pop off the entry-point and jump to + it. + BEFORE AFTER (stacks grow down) + whatever stackPointer -> whatever + target address => reg1 = reg1val, etc + reg1val pc = target address + reg2val + stackPointer -> reg3val */ + + /* Cogit>>#genEnilopmartFor:and:and:forCall:called: */ +static NoDbgRegParms void +(*genEnilopmartForandandforCallcalled(sqInt regArg1, sqInt regArg2OrNone, sqInt regArg3OrNone, sqInt forCall, char *trampolineName))(void) +{ + sqInt endAddress; + usqInt enilopmart; + sqInt size; + + zeroOpcodeIndex(); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, varBaseAddress, VarBaseReg); + genLoadStackPointers(backEnd); + if (regArg3OrNone != NoReg) { + /* PopR: */ + genoperand(PopR, regArg3OrNone); + } + if (regArg2OrNone != NoReg) { + /* PopR: */ + genoperand(PopR, regArg2OrNone); + } + + /* PopR: */ + genoperand(PopR, regArg1); + genEnilopmartReturn(forCall); + computeMaximumSizes(); + size = generateInstructionsAt(methodZoneBase); + endAddress = outputInstructionsAt(methodZoneBase); + assert((methodZoneBase + size) == endAddress); + enilopmart = methodZoneBase; + methodZoneBase = alignUptoRoutineBoundary(endAddress); + stopsFromto(backEnd, endAddress, methodZoneBase - 1); + recordGeneratedRunTimeaddress(trampolineName, enilopmart); + return ((void (*)(void)) enilopmart); +} + + +/* An enilopmart (the reverse of a trampoline) is a piece of code that makes + the system-call-like transition from the C runtime into generated machine + code. At the point the enilopmart enters machine code via a return + instruction, any argument registers have been loaded with their values and + the stack, if + for call, looks like + ret pc + stackPointer -> target address + + and if not for call, looks like + whatever + stackPointer -> target address + + If forCall and running on a CISC, ret pc must be left on the stack. If + forCall and + running on a RISC, ret pc must be popped into LinkReg. In either case, + target address must be removed from the stack and jumped/returned to. */ + + /* Cogit>>#genEnilopmartReturn: */ +static NoDbgRegParms void +genEnilopmartReturn(sqInt forCall) +{ + /* RetN: */ + genoperand(RetN, 0); +} + + +/* Generate the routine that writes the current values of the C frame and + stack pointers into + variables. These are used to establish the C stack in trampolines back + into the C run-time. + This routine assumes the system's frame pointer is the same as that used + in generated code. */ + + /* Cogit>>#generateCaptureCStackPointers: */ +static NoDbgRegParms NeverInline void +generateCaptureCStackPointers(sqInt captureFramePointer) +{ + sqInt callerSavedReg; + usqIntptr_t fixupSize; + sqInt offset; + usqIntptr_t opcodeSize; + sqInt pushedVarBaseReg; + usqInt startAddress; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 32; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + startAddress = methodZoneBase; + + /* Must happen first; value may be used in accessing any of the following addresses */ + callerSavedReg = 0; + pushedVarBaseReg = 0; + if (!(((CallerSavedRegisterMask & ((1U << VarBaseReg))) != 0))) { + /* VarBaseReg is not caller-saved; must save and restore it, either by using an available caller-saved reg or push/pop. */ + + /* TempReg used below */ + callerSavedReg = availableRegisterOrNoneIn(((ABICallerSavedRegisterMask | ((1U << TempReg))) - ((1U << TempReg)))); + if (callerSavedReg == NoReg) { + gNativePushR(VarBaseReg); + pushedVarBaseReg = 1; + } + else { + /* MoveR:R: */ + genoperandoperand(MoveRR, VarBaseReg, callerSavedReg); + } + } + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, varBaseAddress, VarBaseReg); + if (captureFramePointer) { + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, FPReg, cFramePointerAddress()); + } + + /* Capture the stack pointer prior to the call. If we've pushed VarBaseReg take that into account. */ + offset = (pushedVarBaseReg + ? (leafCallStackPointerDelta(backEnd)) + BytesPerWord + : leafCallStackPointerDelta(backEnd)); + + /* begin LoadEffectiveAddressMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(LoadEffectiveAddressMwrR, offset, NativeSPReg, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, cStackPointerAddress()); + if (!(((CallerSavedRegisterMask & ((1U << VarBaseReg))) != 0))) { + if (pushedVarBaseReg) { + gNativePopR(VarBaseReg); + } + else { + /* MoveR:R: */ + genoperandoperand(MoveRR, callerSavedReg, VarBaseReg); + } + } + gNativeRetN(0); + outputInstructionsForGeneratedRuntimeAt(startAddress); + + /* This also implicitly flushes the read/write mapped dual zone to the read/execute zone. */ + flushICacheFromto(backEnd, ((usqInt)startAddress), ((usqInt)methodZoneBase)); + recordGeneratedRunTimeaddress("ceCaptureCStackPointers", startAddress); + ceCaptureCStackPointers = ((void (*)(void)) startAddress); +} + + +/* Generate the prototype ClosedPIC to determine how much space a full closed + PIC takes. + When we first allocate a closed PIC it only has one or two cases and we + want to grow it. + So we have to determine how big a full one is before hand. */ +/* stack allocate the various collections so that they + are effectively garbage collected on return. */ + + /* Cogit>>#generateClosedPICPrototype */ +static void +generateClosedPICPrototype(void) +{ + CogMethod *cPIC; + AbstractInstruction * cPICEndOfCodeLabel; + sqInt endAddress; + AbstractInstruction * endCPICCase1; + usqIntptr_t fixupSize; + sqInt h; + AbstractInstruction *jumpNext; + sqInt jumpTarget; + sqInt numArgs; + usqIntptr_t opcodeSize; + sqInt wordConstant; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = MaxCPICCases * 9; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + (methodLabel->address = methodZoneBase); + (methodLabel->dependent = null); + + /* begin compileClosedPICPrototype */ + compilePICAbort((numArgs = 0)); + jumpNext = compileCPICEntry(); + + /* At the end of the entry code we need to jump to the first case code, which is actually the last chunk. + On each extension we must update this jump to move back one case. */ + wordConstant = firstPrototypeMethodOop(); + + /* begin MoveUniqueCw:R: */ + /* gen:uniqueLiteral:operand: */ + /* uniqueLiteral:forInstruction: */ + genoperandoperand(MoveCwR, wordConstant, SendNumArgsReg); + jumpTarget = (((methodZoneBase + (youngReferrers)) / 2) - 13262352) + 13262352; + + /* begin JumpLong: */ + genoperand(JumpLong, jumpTarget); + endCPICCase0 = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + for (h = 1; h < MaxCPICCases; h += 1) { + if (h == (MaxCPICCases - 1)) { + jmpTarget(jumpNext, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } + wordConstant = (subsequentPrototypeMethodOop()) + h; + + /* begin MoveUniqueCw:R: */ + /* gen:uniqueLiteral:operand: */ + /* uniqueLiteral:forInstruction: */ + genoperandoperand(MoveCwR, wordConstant, SendNumArgsReg); + + /* 16rBABE1F15+h is the class tag for the Nth case */ + + /* #CmpC32:R: #gen:literal32:operand: #checkLiteral32:forInstruction: */ + genoperandoperand(CmpC32R, 0xBABE1F15U + h, TempReg); + jumpTarget = ((((methodZoneBase + (youngReferrers)) / 2) - 13262352) + 13262352) + (h * 16); + + /* begin JumpLongZero: */ + genConditionalBranchoperand(JumpLongZero, ((sqInt)jumpTarget)); + if (h == 1) { + endCPICCase1 = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + } + } + + /* #MoveCw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveCwR, (methodLabel->address), ClassReg); + jumpTarget = cPICMissTrampolineFor(numArgs); + + /* begin JumpLong: */ + genoperand(JumpLong, jumpTarget); + cPICEndOfCodeLabel = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + computeMaximumSizes(); + cPIC = ((CogMethod *) methodZoneBase); + closedPICSize = (sizeof(CogMethod)) + (generateInstructionsAt(methodZoneBase + (sizeof(CogMethod)))); + endAddress = outputInstructionsAt(methodZoneBase + (sizeof(CogMethod))); + assert((methodZoneBase + closedPICSize) == endAddress); + firstCPICCaseOffset = ((endCPICCase0->address)) - methodZoneBase; + cPICEndOfCodeOffset = ((cPICEndOfCodeLabel->address)) - methodZoneBase; + cPICCaseSize = ((endCPICCase1->address)) - ((endCPICCase0->address)); + cPICEndSize = closedPICSize - (((MaxCPICCases - 1) * cPICCaseSize) + firstCPICCaseOffset); + closedPICSize = roundUpToMethodAlignment(backEnd, closedPICSize); + assert(((picInterpretAbort->address)) == (((methodLabel->address)) + (picInterpretAbortOffset()))); + assert((expectedClosedPICPrototype(cPIC)) == 0); + + /* tpr this is a little tiresome but after any assert checking we need to 0 out the case0 objRef rather than leaving 16r5EAF00D lying around */ + storeLiteralbeforeFollowingAddress(backEnd, 0, ((endCPICCase0->address)) - (jumpLongByteSize(backEnd))); + + /* finish up */ + recordGeneratedRunTimeaddress("cPICPrototype", methodZoneBase); + + /* update the methodZoneBase so we keep the prototype around for later use; it is copied into new PICs */ + methodZoneBase = alignUptoRoutineBoundary(endAddress); + + /* self disassembleFrom: cPICPrototype + (self sizeof: CogMethod) to: cPICPrototype + closedPICSize - 1 */ + cPICPrototype = cPIC; +} + + +/* We handle jump sizing simply. First we make a pass that asks each + instruction to compute its maximum size. Then we make a pass that + sizes jumps based on the maxmimum sizes. Then we make a pass + that fixes up jumps. When fixing up a jump the jump is not allowed to + choose a smaller offset but must stick to the size set in the second pass. */ + + /* Cogit>>#generateCogFullBlock */ +static CogMethod * +generateCogFullBlock(void) +{ + sqInt codeSize; + usqIntptr_t headerSize; + sqInt mapSize; + CogMethod *method; + sqInt result; + usqInt startAddress; + int totalSize; + + headerSize = sizeof(CogMethod); + (methodLabel->address = freeStart()); + computeMaximumSizes(); + concretizeAt(methodLabel, freeStart()); + codeSize = generateInstructionsAt(((methodLabel->address)) + headerSize); + mapSize = generateMapAtstart(null, ((methodLabel->address)) + cbNoSwitchEntryOffset); + totalSize = roundUpToMethodAlignment(backEnd, (headerSize + codeSize) + mapSize); + if (totalSize > MaxMethodSize) { + return ((CogMethod *) MethodTooBig); + } + startAddress = allocate(totalSize); + if (!startAddress) { + return ((CogMethod *) InsufficientCodeSpace); + } + assert((startAddress + cbEntryOffset) == ((fullBlockEntry->address))); + assert((startAddress + cbNoSwitchEntryOffset) == ((fullBlockNoContextSwitchEntry->address))); + result = outputInstructionsAt(startAddress + headerSize); + assert(((startAddress + headerSize) + codeSize) == result); + padIfPossibleWithStopsFromto(backEnd, result, ((startAddress + totalSize) - mapSize) - 1); + generateMapAtstart((startAddress + totalSize) - 1, startAddress + cbNoSwitchEntryOffset); + method = ((CogMethod *) ((((usqInt)startAddress)) + codeToDataDelta)); + fillInMethodHeadersizeselector(method, totalSize, nilObject()); + (method->cpicHasMNUCaseOrCMIsFullBlock = 1); + flushICacheFromto(backEnd, startAddress, startAddress + totalSize); + return ((CogMethod *) startAddress); +} + + +/* We handle jump sizing simply. First we make a pass that asks each + instruction to compute its maximum size. Then we make a pass that + sizes jumps based on the maxmimum sizes. Then we make a pass + that fixes up jumps. When fixing up a jump the jump is not allowed to + choose a smaller offset but must stick to the size set in the second pass. */ + + /* Cogit>>#generateCogMethod: */ +static NoDbgRegParms CogMethod * +generateCogMethod(sqInt selector) +{ + sqInt codeSize; + usqIntptr_t headerSize; + sqInt mapSize; + sqInt result; + usqInt startAddress; + int totalSize; + + headerSize = sizeof(CogMethod); + (methodLabel->address = freeStart()); + computeMaximumSizes(); + concretizeAt(methodLabel, freeStart()); + codeSize = generateInstructionsAt(((methodLabel->address)) + headerSize); + mapSize = generateMapAtstart(null, ((methodLabel->address)) + cmNoCheckEntryOffset); + totalSize = roundUpToMethodAlignment(backEnd, (headerSize + codeSize) + mapSize); + if (totalSize > MaxMethodSize) { + return ((CogMethod *) MethodTooBig); + } + startAddress = allocate(totalSize); + if (!startAddress) { + return ((CogMethod *) InsufficientCodeSpace); + } + assert((startAddress + cmEntryOffset) == ((entry->address))); + assert((startAddress + cmNoCheckEntryOffset) == ((noCheckEntry->address))); + result = outputInstructionsAt(startAddress + headerSize); + assert(((startAddress + headerSize) + codeSize) == result); + padIfPossibleWithStopsFromto(backEnd, result, ((startAddress + totalSize) - mapSize) - 1); + generateMapAtstart((startAddress + totalSize) - 1, startAddress + cmNoCheckEntryOffset); + fillInBlockHeadersAt(startAddress); + fillInMethodHeadersizeselector(((CogMethod *) ((((usqInt)startAddress)) + codeToDataDelta)), totalSize, selector); + + /* This also implicitly flushes the read/write mapped dual zone to the read/execute zone. */ + flushICacheFromto(backEnd, startAddress, startAddress + totalSize); + return ((CogMethod *) startAddress); +} + + +/* Generate the method map at addressrNull (or compute it if addressOrNull is + null). Answer the length of the map in byes. Each entry in the map is in + two parts. In the + least signficant bits are a displacement of how far from the start or + previous entry, + unless it is an IsAnnotationExtension byte, in which case those bits are + the extension. + In the most signficant bits are the type of annotation at the point + reached. A null + byte ends the map. */ + + /* Cogit>>#generateMapAt:start: */ +static NoDbgRegParms sqInt +generateMapAtstart(usqInt addressOrNull, usqInt startAddress) +{ + unsigned char annotation; + sqInt delta; + sqInt i; + AbstractInstruction *instruction; + sqInt length; + usqInt location; + sqInt mapEntry; + sqInt maxDelta; + usqInt mcpc; + + delta = 0; + length = 0; + location = startAddress; + for (i = 0; i < opcodeIndex; i += 1) { + instruction = abstractInstructionAt(i); + if ((annotation = (instruction->annotation))) { + mcpc = ((instruction->address)) + ((instruction->machineCodeSize)); + while (((delta = (mcpc - location) / 1 /* codeGranularity */)) > DisplacementMask) { + maxDelta = (((((delta < MaxX2NDisplacement) ? delta : MaxX2NDisplacement)) | DisplacementMask) - DisplacementMask); + assert((((usqInt)(maxDelta)) >> AnnotationShift) <= DisplacementMask); + if (addressOrNull) { + /* begin addToMap:instruction:byte:at:for: */ + codeByteAtput((void *)(addressOrNull - length),(((usqInt)(maxDelta)) >> AnnotationShift) + DisplacementX2N); + } + location += maxDelta; + length += 1; + } + if (addressOrNull) { + mapEntry = delta + ((((usqInt)((((annotation < IsSendCall) ? annotation : IsSendCall))) << AnnotationShift))); + + /* begin addToMap:instruction:byte:at:for: */ + codeByteAtput((void *)(addressOrNull - length),mapEntry); + } + location += delta; + length += 1; + if (annotation > IsSendCall) { + if (addressOrNull) { + mapEntry = ((((usqInt)(IsAnnotationExtension) << AnnotationShift))) + (annotation - IsSendCall); + + /* begin addToMap:instruction:byte:at:for: */ + codeByteAtput((void *)(addressOrNull - length),mapEntry); + } + length += 1; + } + } + } + if (addressOrNull) { + /* begin addToMap:instruction:byte:at:for: */ + codeByteAtput((void *)(addressOrNull - length),MapEnd); + } + return length + 1; +} + + +/* Generate the prototype OpenPIC to determine how much space an open PIC + takes. + */ +/* stack allocate the various collections so that they + are effectively garbage collected on return. */ + + /* Cogit>>#generateOpenPICPrototype */ +static void +generateOpenPICPrototype(void) +{ + sqInt codeSize; + usqIntptr_t fixupSize; + sqInt mapSize; + usqIntptr_t opcodeSize; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 100; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + (methodLabel->address = methodZoneBase); + (methodLabel->dependent = null); + + /* Need a real selector here so that the map accomodates the annotations for the selector. + Use self numRegArgs to generate the longest possible code sequence due to + genPushRegisterArgsForNumArgs: */ + compileOpenPICnumArgs(specialSelector(0), numRegArgs()); + computeMaximumSizes(); + concretizeAt(methodLabel, methodZoneBase); + codeSize = generateInstructionsAt(methodZoneBase + (sizeof(CogMethod))); + mapSize = generateMapAtstart(null, methodZoneBase + cmNoCheckEntryOffset); + + /* self cCode: '' + inSmalltalk: + [| end | + end := self outputInstructionsAt: methodZoneBase + headerSize. + self disassembleFrom: methodZoneBase + (self sizeof: CogMethod) to: end - 1. + self halt] */ + openPICSize = (roundUpLength((sizeof(CogMethod)) + codeSize)) + (roundUpToMethodAlignment(backEnd, mapSize)); +} + + +/* Generate a routine ceCaptureCStackPointers that will capture the C stack + pointer, and, if it is in use, the C frame pointer. These are used in + trampolines to call + run-time routines in the interpreter from machine-code. */ + + /* Cogit>>#generateStackPointerCapture */ +static void +generateStackPointerCapture(void) +{ + usqInt oldMethodZoneBase; + sqInt oldTrampolineTableIndex; + + +# if defined(cFramePointerInUse) + assertCStackWellAligned(); + generateCaptureCStackPointers(cFramePointerInUse); +# else + /* For the benefit of the following assert, assume the minimum at first. */ + cFramePointerInUse = 0; + assertCStackWellAligned(); + oldMethodZoneBase = methodZoneBase; + oldTrampolineTableIndex = trampolineTableIndex; + generateCaptureCStackPointers(1); + ceCaptureCStackPointers(); + if (!((cFramePointerInUse = checkIfCFramePointerInUse()))) { + methodZoneBase = oldMethodZoneBase; + trampolineTableIndex = oldTrampolineTableIndex; + generateCaptureCStackPointers(0); + } +# endif // defined(cFramePointerInUse) + + + /* begin ensureWritableCodeZone */ + assertCStackWellAligned(); +} + + +/* Generate the run-time entries and exits at the base of the native code + zone and update the base. + Read the class-side method trampolines for documentation on the various + trampolines + */ + + /* Cogit>>#generateTrampolines */ +static void +generateTrampolines(void) +{ + usqIntptr_t fixupSize; + usqInt methodZoneStart; + usqIntptr_t opcodeSize; + + methodZoneStart = methodZoneBase; + (methodLabel->address = methodZoneStart); + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 80; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + setHasYoungReferent(0); + maybeGenerateSelectorIndexDereferenceRoutine(); + generateSendTrampolines(); + generateMissAbortTrampolines(); + generateObjectRepresentationTrampolines(); + generateRunTimeTrampolines(); + generateEnilopmarts(); + generateTracingTrampolines(); +} + + /* Cogit>>#generatorForPC: */ +static NoDbgRegParms BytecodeDescriptor * +generatorForPC(sqInt pc) +{ + return generatorAt(bytecodeSetOffset + (fetchByteofObject(pc, methodObj))); +} + + +/* Generate a pair of routines that answer the frame pointer, and the stack + pointer immediately + after a leaf call, used for checking stack pointer alignment, frame + pointer usage, etc. N.B. + these are exported to the CoInterpreter et al via Cogit + class>>mustBeGlobal:. + */ + + /* Cogit>>#genGetLeafCallStackPointers */ +static void +genGetLeafCallStackPointers(void) +{ + usqIntptr_t fixupSize; + usqIntptr_t opcodeSize; + usqInt startAddress; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 4; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + startAddress = methodZoneBase; + + /* MoveR:R: */ + genoperandoperand(MoveRR, FPReg, ABIResultReg); + + /* RetN: */ + genoperand(RetN, 0); + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress("ceGetFP", startAddress); + ceGetFP = ((usqIntptr_t (*)(void)) startAddress); + startAddress = methodZoneBase; + zeroOpcodeIndex(); + + /* MoveR:R: */ + genoperandoperand(MoveRR, NativeSPReg, ABIResultReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, leafCallStackPointerDelta(backEnd), ABIResultReg); + + /* RetN: */ + genoperand(RetN, 0); + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress("ceGetSP", startAddress); + ceGetSP = ((usqIntptr_t (*)(void)) startAddress); +} + + +/* Generate the abort for a PIC. This abort performs either a call of + ceInterpretMethodFromPIC:receiver: to handle invoking an uncogged target + or a call of ceMNUFromPICMNUMethod:receiver: to handle an MNU dispatch + in a closed PIC. It distinguishes the two by testing ClassReg. If the + register is zero then this is an MNU. + + This poses a problem in 32-bit Spur, where zero is the cache tag for + immediate characters (tag pattern 2r10) because SmallIntegers have tag + patterns 2r11 + and 2r01, so anding with 1 reduces these to 0 & 1. We solve the ambiguity + by patching send sites with a 0 cache tag to open PICs instead of closed + PICs. */ + + /* Cogit>>#genInnerPICAbortTrampoline: */ +static NoDbgRegParms usqInt +genInnerPICAbortTrampoline(char *name) +{ + AbstractInstruction *jumpMNUCase; + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0 /* picAbortDiscriminatorValue */, ClassReg); + jumpMNUCase = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + compileTrampolineFornumArgsargargargargregsToSavepushLinkRegresultReg(ceInterpretMethodFromPICreceiver, 2, SendNumArgsReg, ReceiverResultReg, null, null, 0 /* emptyRegisterMask */, 0, NoReg); + jmpTarget(jumpMNUCase, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceMNUFromPICMNUMethodreceiver, name, 2, SendNumArgsReg, ReceiverResultReg, null, null, 0 /* emptyRegisterMask */, 0, NoReg, 1); +} + + +/* Switch to the C stack (do *not* save the Smalltalk stack pointers; + this is the caller's responsibility), and invoke interpret PDQ. */ + + /* Cogit>>#genInvokeInterpretTrampoline */ +static void +(*genInvokeInterpretTrampoline(void))(void) +{ + usqInt startAddress; + + startAddress = methodZoneBase; + zeroOpcodeIndex(); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, varBaseAddress, VarBaseReg); + if (cFramePointerInUse) { + genLoadCStackPointers(backEnd); + } + else { + genLoadCStackPointer(backEnd); + } + + /* Sideways call interpret so that the stack looks correct, for exception handling etc */ + + /* begin genMarshallNArgs:arg:arg:arg:arg: */ + /* SubCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 32, RSP); + + /* WIN64 ABI always reserves shadow space on the stack for callee to save up to 4 register parameters */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, cReturnAddressAddress(), ABIResultReg); + + /* #genSubstituteReturnAddressR: #PushR: */ + genoperand(PushR, ABIResultReg); + + /* #JumpFullRT: #JumpFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(JumpFull, ((sqInt)(((usqInt)interpret)))); + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress("ceInvokeInterpret", startAddress); + return ((void (*)(void)) startAddress); +} + + +/* The in-line cache for a send is implemented as a constant load into + ClassReg. We always use a 32-bit load, even in 64-bits. + + In the initial (unlinked) state the in-line cache is notionally loaded + with the selector. + But since in 64-bits an arbitrary selector oop won't fit in a 32-bit + constant load, we + instead load the cache with the selector's index, either into the literal + frame of the + current method, or into the special selector array. Negative values are + 1-relative indices into the special selector array. + + When a send is linked, the load of the selector, or selector index, is + overwritten with a + load of the receiver's class, or class tag. Hence, the 64-bit VM is + currently constrained + to use class indices as cache tags. If out-of-line literals are used, + distinct caches /must + not/ share acche locations, for if they do, send cacheing will be confused + by the sharing. + Hence we use the MoveUniqueC32:R: instruction that will not share literal + locations. */ + + /* Cogit>>#genLoadInlineCacheWithSelector: */ +static NoDbgRegParms void +genLoadInlineCacheWithSelector(sqInt selectorIndex) +{ + sqInt cacheValue; + + assert((selectorIndex < 0 + ? (((-selectorIndex) >= 1) && ((-selectorIndex) <= (numSpecialSelectors()))) + : ((selectorIndex >= 0) && (selectorIndex <= ((literalCountOf(methodObj)) - 1))))); + cacheValue = selectorIndex; + + /* #MoveUniqueC32:R: #gen:uniqueLiteral32:operand: #uniqueLiteral32:forInstruction: */ + genoperandoperand(MoveC32R, cacheValue, ClassReg); +} + + /* Cogit>>#genReturnToInterpreterTrampoline */ +static usqInt +genReturnToInterpreterTrampoline(void) +{ + usqInt startAddress; + + startAddress = methodZoneBase; + zeroOpcodeIndex(); + + /* Push the result, set the instruction pointer to the interpreter frame's saved ip, + set the method and the bytecode set offset, then call interpret. */ + + /* PushR: */ + genoperand(PushR, ReceiverResultReg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, FoxIFSavedIP, FPReg, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, instructionPointerAddress()); + genSmalltalkToCStackSwitch(0); + + /* pushLinkReg + Sideways call interpret so that the stack looks correct, for exception handling etc */ + + /* begin genMarshallNArgs:arg:arg:arg:arg: */ + /* SubCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 32, RSP); + + /* WIN64 ABI always reserves shadow space on the stack for callee to save up to 4 register parameters */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, cReturnAddressAddress(), ABIResultReg); + + /* #genSubstituteReturnAddressR: #PushR: */ + genoperand(PushR, ABIResultReg); + + /* #JumpFullRT: #JumpFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(JumpFull, ((sqInt)(((usqInt)interpret)))); + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress("ceReturnToInterpreterTrampoline", startAddress); + return startAddress; +} + + +/* If the client requires, then on an ARM-like RISC processor, the return + address needs to + be pushed to the stack so that the interpreter sees the same stack layout + as on CISC. + */ + + /* Cogit>>#genSmalltalkToCStackSwitch: */ +static NoDbgRegParms sqInt +genSmalltalkToCStackSwitch(sqInt pushLinkReg) +{ + genSaveStackPointers(backEnd); + if (cFramePointerInUse) { + genLoadCStackPointers(backEnd); + } + else { + genLoadCStackPointer(backEnd); + } + return 0; +} + + +/* Generate a trampoline with up to four arguments. Generate either a call or + a jump to aRoutineOrNil + as requested by callJumpBar. If generating a call and resultRegOrNone is + not NoReg pass the C result + back in resultRegOrNone. + Hack: a negative value indicates an abstract register, a non-negative + value indicates a constant. */ + + /* Cogit>>#genTrampolineFor:called:numArgs:arg:arg:arg:arg:regsToSave:pushLinkReg:resultReg:appendOpcodes: */ +static NoDbgRegParms usqInt +genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(void *aRoutine, char *trampolineName, sqInt numArgs, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3, sqInt regMask, sqInt pushLinkReg, sqInt resultRegOrNone, sqInt appendBoolean) +{ + usqInt startAddress; + + startAddress = methodZoneBase; + if (!appendBoolean) { + zeroOpcodeIndex(); + } + compileTrampolineFornumArgsargargargargregsToSavepushLinkRegresultReg(aRoutine, numArgs, regOrConst0, regOrConst1, regOrConst2, regOrConst3, regMask, pushLinkReg, resultRegOrNone); + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress(trampolineName, startAddress); + recordRunTimeObjectReferences(); + + /* begin assertValidDualZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + assertCoherentCodeAtdelta(backEnd, codeBase + cmNoCheckEntryOffset, codeToDataDelta); +# endif + + return startAddress; +} + + +/* To return from a trampoline call we have to take the return address off + the stack, + iof it has been saved */ + + /* Cogit>>#genTrampolineReturn: */ +static NoDbgRegParms void +genTrampolineReturn(sqInt lnkRegWasPushed) +{ + /* RetN: */ + genoperand(RetN, 0); +} + + +/* */ + + /* Cogit>>#gen: */ +static NoDbgRegParms AbstractInstruction * +gen(sqInt opcode) +{ + AbstractInstruction *abstractInstruction; + + assert(opcodeIndex < numAbstractOpcodes); + abstractInstruction = abstractInstructionAt(opcodeIndex); + opcodeIndex += 1; + (abstractInstruction->opcode = opcode); + return abstractInstruction; +} + + +/* */ +/* */ + + /* Cogit>>#gen:operand: */ +static NoDbgRegParms AbstractInstruction * +genoperand(sqInt opcode, sqInt operand) +{ + AbstractInstruction *abstractInstruction; + + assert(opcodeIndex < numAbstractOpcodes); + abstractInstruction = abstractInstructionAt(opcodeIndex); + opcodeIndex += 1; + (abstractInstruction->opcode = opcode); + ((abstractInstruction->operands))[0] = operand; + return abstractInstruction; +} + + +/* */ +/* */ +/* */ + + /* Cogit>>#gen:operand:operand: */ +static NoDbgRegParms AbstractInstruction * +genoperandoperand(sqInt opcode, sqInt operandOne, sqInt operandTwo) +{ + AbstractInstruction *abstractInstruction; + + assert(opcodeIndex < numAbstractOpcodes); + abstractInstruction = abstractInstructionAt(opcodeIndex); + opcodeIndex += 1; + (abstractInstruction->opcode = opcode); + ((abstractInstruction->operands))[0] = operandOne; + ((abstractInstruction->operands))[1] = operandTwo; + return abstractInstruction; +} + + +/* */ +/* */ +/* */ +/* */ + + /* Cogit>>#gen:operand:operand:operand: */ +static NoDbgRegParms AbstractInstruction * +genoperandoperandoperand(sqInt opcode, sqInt operandOne, sqInt operandTwo, sqInt operandThree) +{ + AbstractInstruction *abstractInstruction; + + assert(opcodeIndex < numAbstractOpcodes); + abstractInstruction = abstractInstructionAt(opcodeIndex); + opcodeIndex += 1; + (abstractInstruction->opcode = opcode); + ((abstractInstruction->operands))[0] = operandOne; + ((abstractInstruction->operands))[1] = operandTwo; + ((abstractInstruction->operands))[2] = operandThree; + return abstractInstruction; +} + + +/* Answer the contents of the code zone as an array of pair-wise element, + address in ascending address order. + Answer a string for a runtime routine or abstract label (beginning, end, + etc), a CompiledMethod for a CMMethod, + or a selector (presumably a Symbol) for a PIC. + If withDetails is true + - answer machine-code to bytecode pc mapping information for methods + - answer class, target pair information for closed PIC + N.B. Since the class tag for the first case of a closed PIC is stored at + the send site, it must be collected + by scanning methods (see + collectCogConstituentFor:Annotation:Mcpc:Bcpc:Method:). Since closed PICs + are never shared they always come after the method that references them, + so we don't need an extra pass + to collect the first case class tags, which are (temporarily) assigned to + each closed PIC's methodObject field. + But we do need to reset the methodObject fields to zero. This is done in + createPICData:, unless memory + runs out, in which case it is done by cleanUpFailingCogCodeConstituents:. */ + + /* Cogit>>#getCogCodeConstituents: */ +static NoDbgRegParms sqInt +getCogCodeConstituents(sqInt withDetails) +{ + CogMethod *cogMethod; + sqInt constituents; + sqInt count; + sqInt dataClass; + sqInt i; + sqInt label; + sqInt profileData; + sqInt value; + + dataClass = 0; + + /* + 3 for start, freeStart and end */ + count = (trampolineTableIndex / 2) + 3; + cogMethod = ((CogMethod *) methodZoneBase); + + /* we /could/ use methodZone's methodCount, but as of 5/22/2025 it isn't decremented + in freeMethod:, so keep things simple and count exactly how many methods exist now. */ + while (cogMethod < (limitZony())) { + if (!(((cogMethod->cmType)) == CMFree)) { + count += 1; + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + constituents = instantiateClassindexableSize(classArray(), count * 2); + if (!constituents) { + return constituents; + } + pushRemappableOop(constituents); + if ((!((label = stringForCString("CogCode")))) + || (!((value = positive64BitIntegerFor(codeBase))))) { + popRemappableOop(); + return null; + } + storePointerUncheckedofObjectwithValue(0, topRemappableOop(), label); + storePointerUncheckedofObjectwithValue(1, topRemappableOop(), value); + for (i = 0; i < trampolineTableIndex; i += 2) { + if ((!((label = stringForCString(trampolineAddresses[i])))) + || (!((value = positive64BitIntegerFor(((usqInt)(trampolineAddresses[i + 1]))))))) { + popRemappableOop(); + return null; + } + storePointerUncheckedofObjectwithValue(2 + i, topRemappableOop(), label); + storePointerUncheckedofObjectwithValue(3 + i, topRemappableOop(), value); + } + count = trampolineTableIndex + 2; + if (withDetails) { + dataClass = classDoubleWordArray(); + if (dataClass == (nilObject())) { + dataClass = null; + } + } + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (!(((cogMethod->cmType)) == CMFree)) { + profileData = /* profileDataFor:withDetails: */ + (((cogMethod->cmType)) >= CMMethod + ? (cogMethod->methodObject) + : (withDetails + && (((cogMethod->cmType)) == CMClosedPIC) + ? createCPICData(cogMethod) + : (cogMethod->selector))); + if (!profileData) { + return cleanUpFailingCogCodeConstituents(cogMethod); + } + storePointerUncheckedofObjectwithValue(count, topRemappableOop(), profileData); + value = (withDetails + ? (dataClass + ? collectCogMethodConstituentOnSpurofClass(cogMethod, dataClass) + : collectCogMethodConstituent(cogMethod)) + : positive64BitIntegerFor(((usqInt)cogMethod))); + if (!value) { + return cleanUpFailingCogCodeConstituents(cogMethod); + } + storePointerUncheckedofObjectwithValue(count + 1, topRemappableOop(), value); + count += 2; + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if ((!((label = stringForCString("CCFree")))) + || (!((value = positive64BitIntegerFor(mzFreeStart))))) { + popRemappableOop(); + return null; + } + storePointerUncheckedofObjectwithValue(count, topRemappableOop(), label); + storePointerUncheckedofObjectwithValue(count + 1, topRemappableOop(), value); + if ((!((label = stringForCString("CCEnd")))) + || (!((value = positive64BitIntegerFor(limitAddress))))) { + popRemappableOop(); + return null; + } + storePointerUncheckedofObjectwithValue(count + 2, topRemappableOop(), label); + storePointerUncheckedofObjectwithValue(count + 3, topRemappableOop(), value); + constituents = popRemappableOop(); + beRootIfOld(constituents); + + /* would like to assert this, but it requires the leak checked be run :-( + self assert: self allMachineCodeObjectReferencesValid. */ + return constituents; +} + + /* Cogit>>#getLiteral: */ +static NoDbgRegParms sqInt +getLiteral(sqInt litIndex) +{ + if (maxLitIndex < litIndex) { + maxLitIndex = litIndex; + } + return literalofMethod(litIndex, methodObj); +} + + /* Cogit>>#getMethodZoneBase */ +#if VMInvestigations +usqInt +getMethodZoneBase(void) +{ + return methodZoneBase; +} +#endif /* VMInvestigations */ + + /* Cogit>>#incrementUsageOfTargetIfLinkedSend:mcpc:ignored: */ +static NoDbgRegParms sqInt +incrementUsageOfTargetIfLinkedSendmcpcignored(sqInt annotation, char *mcpc, sqInt superfluity) +{ + usqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + + if (annotation >= IsSendCall) { + assert(annotation != IsNSSendCall); + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (((targetMethod1->cmUsageCount)) < (CMMaxUsageCount / 2)) { + ((((CogMethod *) ((((usqInt)targetMethod1)) + codeToDataDelta)))->cmUsageCount = ((targetMethod1->cmUsageCount)) + 1); + } + } + } + return 0; +} + + +/* Answer the value to put in an inline-cache that is being loaded with the + selector. Usually this is simply the selector, but in 64-bits the cache is + only 32-bits wide + and so the cache is loaded with the index of the selector. */ +/* First search the special selectors; there are only 32 of them so this + shouldn't take too long. + We could short-circuit this by keeping a hint bit in the target method, or + by maintaining the + maximum range of selector oops in specialSelectors since they're likely to + cluster. + */ + + /* Cogit>>#indexForSelector:in: */ +static NoDbgRegParms sqInt +indexForSelectorin(sqInt selector, CogMethod *cogMethod) +{ + sqInt i; + sqInt methodOop; + sqInt toDoLimit; + + for (i = 0; i < NumSpecialSelectors; i += 1) { + if (selector == (specialSelector(i))) { + return -1 - i; + } + } + methodOop = (cogMethod->methodObject); + + /* Then search the method's literal frame... open code fetchPointer:ofObject: for speed... */ + toDoLimit = literalCountOfMethodHeader((cogMethod->methodHeader)); + for (i = LiteralStart; i <= toDoLimit; i += 1) { + if ((longAt((void *)(((i * BytesPerOop) + BaseHeaderSize) + methodOop))) == selector) { + assert(selector == (literalofMethod(i - 1, methodOop))); + return i - 1; + } + } + error("could not find selector in method when unlinking send site"); + return 0; +} + + /* Cogit>>#initializeCodeZoneFrom:upTo: */ +void +initializeCodeZoneFromupTo(sqInt startAddress, sqInt endAddress) +{ + usqInt endAddressUsqInt; + usqInt startAddressUsqInt; + + /* begin initializeBackend */ + (methodLabel->machineCodeSize = 0); + (methodLabel->opcode = Label); + ((methodLabel->operands))[0] = 0; + ((methodLabel->operands))[1] = 0; + + /* begin initializeVarBase */ + assert((!((registerMaskFor(VarBaseReg)) & CallerSavedRegisterMask))); + varBaseAddress = computeGoodVarBaseAddress(); + assert((stackLimitAddress()) >= varBaseAddress); + assert((cStackPointerAddress()) >= varBaseAddress); + assert((cFramePointerAddress()) >= varBaseAddress); + assert((cReturnAddressAddress()) >= varBaseAddress); + assert((nextProfileTickAddress()) >= varBaseAddress); + + /* If the platform requires flipping between executable and writable + states, the system expects to start in the writable state. */ + + /* begin ensureWritableCodeZone */ + sqMakeMemoryExecutableFromToCodeToDataDelta(startAddress, endAddress, +# if DUAL_MAPPED_CODE_ZONE + (&codeToDataDelta) +# else + null +# endif + ); + codeBase = (methodZoneBase = startAddress); + stopsFromto(backEnd, startAddress, endAddress - 1); + + /* begin manageFrom:to: */ + mzFreeStart = (baseAddress = methodZoneBase); + youngReferrers = (limitAddress = endAddress); + openPICList = null; + methodBytesFreedSinceLastCompaction = 0; + methodCount = 0; + assertValidDualZone(); + detectFeatures(backEnd); + + /* begin maybeGenerateCacheFlush */ + + /* initializeCacheFlush */ + + /* self generateVMOwnerLockFunctions. */ + genGetLeafCallStackPointers(); + generateStackPointerCapture(); + generateTrampolines(); + computeEntryOffsets(); + computeFullBlockEntryOffsets(); + generateClosedPICPrototype(); + alignMethodZoneBase(); + + /* None of the above is executed beyond ceCheckFeatures, so a bulk flush now is the leanest thing to do. */ + flushICacheFromto(backEnd, startAddress, ((usqInt)methodZoneBase)); + startAddressUsqInt = startAddress; + endAddressUsqInt = ((usqInt)methodZoneBase); + + /* begin maybeFlushWritableZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + if (codeToDataDelta > 0) { + flushDCacheFromto(backEnd, startAddressUsqInt, endAddressUsqInt); + } +# endif + + + /* Repeat so that now the methodZone ignores the generated run-time. */ + + /* begin manageFrom:to: */ + mzFreeStart = (baseAddress = methodZoneBase); + youngReferrers = (limitAddress = endAddress); + openPICList = null; + methodBytesFreedSinceLastCompaction = 0; + methodCount = 0; + + /* N.B. this is assumed to be the last thing done in initialization; see Cogit>>initialized. + This is done only to compute openPICSize; the generated code is discarded. */ + generateOpenPICPrototype(); +} + + +/* Answer a usage count that reflects likely long-term usage. + Answer 1 for non-primitives or quick primitives (inst var accessors), + 2 for methods with interpreter primitives, and 3 for compiled primitives. */ + + /* Cogit>>#initialMethodUsageCount */ +static sqInt +initialMethodUsageCount(void) +{ + if ((primitiveIndex == 1) + || (isQuickPrimitiveIndex(primitiveIndex))) { + return 1; + } + if (!(primitiveGeneratorOrNil())) { + return 2; + } + return 3; +} + + +/* Answer a usage count that reflects likely long-term usage. */ + + /* Cogit>>#initialOpenPICUsageCount */ +static int +initialOpenPICUsageCount(void) +{ + return CMMaxUsageCount - 1; +} + + +/* Answer if entryPoint, which must be either the MNU or interpret abort in + cPIC (see compileClosedPICPrototype), invokes the MNU abort. The corollary + is that + it invokes the interpret abort. */ + + /* Cogit>>#intraCPICTarget:invokesMNUIn: */ +static NoDbgRegParms int +intraCPICTargetinvokesMNUIn(sqInt targetMcpc, CogMethod *cPIC) +{ + assert(((targetMcpc >= (((usqInt)(cPIC + 1)))) && (targetMcpc <= ((((usqInt)cPIC)) + cmEntryOffset)))); + return targetMcpc == (((usqInt)(cPIC + 1))); +} + + /* Cogit>>#inverseBranchFor: */ +static NoDbgRegParms sqInt +inverseBranchFor(sqInt opcode) +{ + switch (opcode) { + case JumpLongZero: + return JumpLongNonZero; + + case JumpLongNonZero: + return JumpLongZero; + + case JumpZero: + return JumpNonZero; + + case JumpNonZero: + return JumpZero; + + case JumpNegative: + return JumpNonNegative; + + case JumpNonNegative: + return JumpNegative; + + case JumpOverflow: + return JumpNoOverflow; + + case JumpNoOverflow: + return JumpOverflow; + + case JumpCarry: + return JumpNoCarry; + + case JumpNoCarry: + return JumpCarry; + + case JumpLess: + return JumpGreaterOrEqual; + + case JumpGreaterOrEqual: + return JumpLess; + + case JumpGreater: + return JumpLessOrEqual; + + case JumpLessOrEqual: + return JumpGreater; + + case JumpBelow: + return JumpAboveOrEqual; + + case JumpAboveOrEqual: + return JumpBelow; + + case JumpAbove: + return JumpBelowOrEqual; + + case JumpBelowOrEqual: + return JumpAbove; + + default: + error("Case not found and no otherwise clause"); + } + error("invalid opcode for inverse"); + return 0; +} + + +/* Answer a pair-wise Array of cPIC selector, target for the first case, and + then class index, target for the remaining cases. + If a case in an MNU case then target is the doesNotUnderstand: selector. */ + + /* Cogit>>#investigationArrayForClosedPIC: */ +#if VMInvestigations +static NoDbgRegParms sqInt +investigationArrayForClosedPIC(CogMethod *cPIC) +{ + sqInt data; + usqInt entryPoint; + sqInt i; + sqInt mcpc; + sqInt nc; + + data = instantiateClassindexableSize(classArray(), (nc = ((cPIC->cPICNumCases)) * 2)); + if (!data) { + return null; + } + entryPoint = jumpLongTargetBeforeFollowingAddress(backEnd, addressOfEndOfCaseinCPIC(1, cPIC)); + storePointerUncheckedofObjectwithValue(0, data, (cPIC->selector)); + storePointerUncheckedofObjectwithValue(1, data, investigationTargetForEntryPointin(entryPoint, cPIC)); + + /* store class indices backwards because the PIC grows backwards. */ + for (i = 2; i <= ((cPIC->cPICNumCases)); i += 1) { + mcpc = addressOfEndOfCaseinCPIC(i, cPIC); + storePointerUncheckedofObjectwithValue((nc -= 1), data, investigationTargetForEntryPointin(jumpLongTargetBeforeFollowingAddress(backEnd, mcpc), cPIC)); + storePointerUncheckedofObjectwithValue((nc -= 1), data, (((usqInt)(literal32BeforeFollowingAddress(backEnd, mcpc - (jumpLongConditionalByteSize(backEnd)))) << 3) | 1)); + } + return data; +} +#endif /* VMInvestigations */ + + +/* Resolve a jump dispatch in a closed PIC into either a method or, if it is + an MNU dispatch, the MNU selector. + */ + + /* Cogit>>#investigationTargetForEntryPoint:in: */ +#if VMInvestigations +static NoDbgRegParms sqInt +investigationTargetForEntryPointin(sqInt entryPoint, CogMethod *cPIC) +{ + return (/* containsAddress: */ + ((((usqInt)cPIC)) <= (((usqInt)entryPoint))) + && (((((usqInt)cPIC)) + ((cPIC->blockSize))) >= (((usqInt)entryPoint))) + ? (intraCPICTargetinvokesMNUIn(entryPoint, cPIC) + ? splObj(SelectorDoesNotUnderstand) + : positive64BitIntegerFor(((usqInt)interpret))) + : ((((CogMethod *) (entryPoint - cmNoCheckEntryOffset)))->methodObject)); +} +#endif /* VMInvestigations */ + + +/* Useful for debugging, asserts, etc */ + + /* Cogit>>#isPCWithinMethodZone: */ +int +isPCWithinMethodZone(usqInt address) +{ + return ((address >= methodZoneBase) && (address <= (freeStart()))); +} + + +/* Answer if the instruction preceding retpc is a call instruction. */ + + /* Cogit>>#isSendReturnPC: */ +sqInt +isSendReturnPC(sqInt retpc) +{ + usqInt target; + + if (!(isCallPrecedingReturnPC(backEnd, retpc))) { + return 0; + } + target = callTargetFromReturnAddress(backEnd, retpc); + return (((target >= firstSend) && (target <= lastSend))) + || (((target >= methodZoneBase) && (target <= (freeStart())))); +} + + +/* Floating-point jumps are a little weird on some processors. Defer to + the backEnd to allow it to generate any special code it may need to. */ + + /* Cogit>>#JumpFPEqual: */ +static NoDbgRegParms AbstractInstruction * +gJumpFPEqual(void *jumpTarget) +{ + AbstractInstruction *jumpToTarget; + AbstractInstruction *jumpUnordered; + + /* begin genJumpFPEqual: */ + /* begin genJumpFPOrderedAnd:to: */ + jumpUnordered = genoperand(JumpFPUnordered, ((sqInt)jumpTarget)); + jumpToTarget = genoperand(JumpFPEqual, ((sqInt)jumpTarget)); + jmpTarget(jumpUnordered, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return jumpToTarget; +} + + +/* Floating-point jumps are a little weird on some processors. Defer to + the backEnd to allow it to generate any special code it may need to. */ + + /* Cogit>>#JumpFPGreaterOrEqual: */ +static NoDbgRegParms AbstractInstruction * +gJumpFPGreaterOrEqual(void *jumpTarget) +{ + return genoperand(JumpFPGreaterOrEqual, ((sqInt)jumpTarget)); +} + + +/* Floating-point jumps are a little weird on some processors. Defer to + the backEnd to allow it to generate any special code it may need to. */ + + /* Cogit>>#JumpFPGreater: */ +static NoDbgRegParms AbstractInstruction * +gJumpFPGreater(void *jumpTarget) +{ + return genoperand(JumpFPGreater, ((sqInt)jumpTarget)); +} + + +/* Floating-point jumps are a little weird on some processors. Defer to + the backEnd to allow it to generate any special code it may need to. */ + + /* Cogit>>#JumpFPNotEqual: */ +static NoDbgRegParms AbstractInstruction * +gJumpFPNotEqual(void *jumpTarget) +{ + AbstractInstruction *jumpToTarget; + AbstractInstruction *jumpUnordered; + + /* begin genJumpFPNotEqual: */ + /* begin genJumpFPUnorderedOr:to: */ + jumpToTarget = genoperand(JumpFPNotEqual, ((sqInt)jumpTarget)); + jumpUnordered = genoperand(JumpFPUnordered, ((sqInt)jumpTarget)); + addDependent(jumpToTarget, jumpUnordered); + return jumpToTarget; +} + + /* Cogit>>#LogicalShiftLeftCq:R: */ +static NoDbgRegParms AbstractInstruction * +gLogicalShiftLeftCqR(sqInt quickConstant, sqInt reg) +{ + return genoperandoperand(LogicalShiftLeftCqR, quickConstant, reg); +} + + +/* destReg := srcReg << quickConstant */ + + /* Cogit>>#LogicalShiftLeftCq:R:R: */ +static NoDbgRegParms AbstractInstruction * +gLogicalShiftLeftCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *first; + + first = genoperandoperand(MoveRR, srcReg, destReg); + genoperandoperand(LogicalShiftLeftCqR, quickConstant, destReg); + return first; +} + + +/* destReg := (unsigned)srcReg >> quickConstant */ + + /* Cogit>>#LogicalShiftRightCq:R:R: */ +static NoDbgRegParms AbstractInstruction * +gLogicalShiftRightCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *first; + + first = genoperandoperand(MoveRR, srcReg, destReg); + genoperandoperand(LogicalShiftRightCqR, quickConstant, destReg); + return first; +} + + /* Cogit>>#lastOpcode */ +static AbstractInstruction * +lastOpcode(void) +{ + assert(opcodeIndex > 0); + return abstractInstructionAt(opcodeIndex - 1); +} + + +/* Answer the linked send targetof the send before returnAddress, if valid, + otherwise nil + */ +/* Answer if the instruction preceding retpc is a call instruction. */ + + /* Cogit>>#linkedSendTargetOrNilAt: */ +CogMethod * +linkedSendTargetOrNilAt(sqInt retpc) +{ + usqInt target; + CogMethod *targetCogMethod; + + if (!(isCallPrecedingReturnPC(backEnd, retpc))) { + return null; + } + target = callTargetFromReturnAddress(backEnd, retpc); + if (!(((target >= methodZoneBase) && (target <= (freeStart()))))) { + return null; + } + if ((targetCogMethod = methodFor(((void *)target)))) { + if (((targetCogMethod->cmType)) == CMMethod) { + return targetCogMethod; + } + } + return null; +} + + /* Cogit>>#linkSendAt:in:to:offset:receiver: */ +void +linkSendAtintooffsetreceiver(sqInt callSiteReturnAddress, CogMethod *sendingMethod, CogMethod *targetMethod, sqInt theEntryOffset, sqInt receiver) +{ + sqInt extent; + sqInt inlineCacheTag; + + assert((theEntryOffset == cmEntryOffset) + || (theEntryOffset == cmNoCheckEntryOffset)); + assert(((callSiteReturnAddress >= methodZoneBase) && (callSiteReturnAddress <= (freeStart())))); + + /* begin ensureWritableCodeZone */ + if (theEntryOffset == cmNoCheckEntryOffset) { + extent = rewriteCallAttarget(backEnd, callSiteReturnAddress, (((sqInt)targetMethod)) + cmNoCheckEntryOffset); + } + else { + inlineCacheTag = inlineCacheTagForInstance(receiver); + extent = rewriteInlineCacheAttagtarget(backEnd, callSiteReturnAddress, inlineCacheTag, (((sqInt)targetMethod)) + theEntryOffset); + } + + /* no need to change selector cache tag */ + flushICacheFromto(backEnd, (((usqInt)callSiteReturnAddress)) - extent, ((usqInt)callSiteReturnAddress)); +} + + /* Cogit>>#loadBytesAndGetDescriptor: */ +static NoDbgRegParms BytecodeDescriptor * +loadBytesAndGetDescriptor(sqInt pc) +{ + BytecodeDescriptor *descriptor; + + byte0 = (fetchByteofObject(pc, methodObj)) + bytecodeSetOffset; + descriptor = generatorAt(byte0); + + /* begin loadSubsequentBytesForDescriptor:at: */ + if (((descriptor->numBytes)) > 1) { + byte1 = fetchByteofObject(pc + 1, methodObj); + if (((descriptor->numBytes)) > 2) { + byte2 = fetchByteofObject(pc + 2, methodObj); + if (((descriptor->numBytes)) > 3) { + byte3 = fetchByteofObject(pc + 3, methodObj); + if (((descriptor->numBytes)) > 4) { + notYetImplemented(); + } + } + } + } + return descriptor; +} + + /* Cogit>>#MovePerfCnt64R:L: */ +static NoDbgRegParms AbstractInstruction * +gMovePerfCnt64RL(sqInt destReg, sqInt liveRegisterMask) +{ + assert(BytesPerWord == 8); + return genoperandoperand(MovePerfCnt64RL, destReg, liveRegisterMask); +} + + +/* Answer the address of the null byte at the end of the method map. */ + + /* Cogit>>#mapEndFor: */ +static NoDbgRegParms sqInt +mapEndFor(CogMethod *cogMethod) +{ + sqInt end; + + end = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + while ((byteAt((void *)(end))) != MapEnd) { + end -= 1; + assert(end > (firstMappedPCFor(cogMethod))); + } + return end; +} + + +/* Unlinking/GC/Disassembly support */ +/* most of the time arg is a CogMethod... */ + + /* Cogit>>#mapFor:performUntil:arg: */ +static NoDbgRegParms sqInt +mapForperformUntilarg(CogMethod *cogMethod, sqInt (*functionSymbol)(sqInt annotation, char *mcpc, CogMethod *arg), void *arg) +{ + sqInt annotation; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = functionSymbol(annotation, (((char *) mcpc)), arg); + if (result) { + return result; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + return 0; +} + + +/* Remap all object references in the closed PIC. Answer if any references + are young. + Set codeModified if any modifications are made. */ + + /* Cogit>>#mapObjectReferencesInClosedPIC: */ +static NoDbgRegParms sqInt +mapObjectReferencesInClosedPIC(CogMethod *cPIC) +{ + sqInt i; + sqInt pc; + sqInt refersToYoung; + + pc = addressOfEndOfCaseinCPIC(1, cPIC); + + /* first we check the potential method oop load at the beginning of the CPIC */ + refersToYoung = remapMaybeObjRefInClosedPICAt(pc - (jumpLongByteSize(backEnd))); + + /* We find the end address of the cPICNumCases'th case and can then just step forward by the case size thereafter */ + pc = addressOfEndOfCaseinCPIC((cPIC->cPICNumCases), cPIC); + + /* Next we check the potential class ref in the compare instruction, and the potential method oop load for each case. */ + for (i = 2; i <= ((cPIC->cPICNumCases)); i += 1) { + if (remapMaybeObjRefInClosedPICAt((pc - (jumpLongConditionalByteSize(backEnd))) - (cmpC32RTempByteSize(backEnd)))) { + refersToYoung = 1; + } + pc += cPICCaseSize; + } + return refersToYoung; +} + + +/* Update all references to objects in the generated runtime. */ + + /* Cogit>>#mapObjectReferencesInGeneratedRuntime */ +static void +mapObjectReferencesInGeneratedRuntime(void) +{ + sqInt i; + sqInt literal; + sqInt mappedLiteral; + usqInt mcpc; + + for (i = 0; i < runtimeObjectRefIndex; i += 1) { + mcpc = objectReferencesInRuntime[i]; + literal = literalBeforeFollowingAddress(backEnd, mcpc); + mappedLiteral = remapObject(literal); + if (mappedLiteral != literal) { + /* begin setCodeModified */ + codeModified = 1; + + /* storeLiteral:atAnnotatedAddress:using: */ + storeLiteralbeforeFollowingAddress(backEnd, mappedLiteral, mcpc); + } + } +} + + +/* Update all references to objects in machine code for a become. + Unlike incrementalGC or fullGC a method that does not refer to young may + refer to young as a result of the become operation. Unlike incrementalGC + or fullGC the reference from a Cog method to its methodObject *must not* + change since the two are two halves of the same object. */ + + /* Cogit>>#mapObjectReferencesInMachineCodeForBecome */ +static void +mapObjectReferencesInMachineCodeForBecome(void) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt freedPIC; + sqInt hasYoungObj; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt remappedMethod; + sqInt result; + CogMethod *writableCogMethod; + + hasYoungObj = 0; + codeModified = (freedPIC = 0); + mapObjectReferencesInGeneratedRuntime(); + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + assert(!hasYoungObj); + if (!(((cogMethod->cmType)) == CMFree)) { + assert((cogMethodDoesntLookKosher(cogMethod)) == 0); + writableCogMethod = ((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)); + (writableCogMethod->selector = remapOop((cogMethod->selector))); + if (((cogMethod->cmType)) == CMClosedPIC) { + if ((isYoung((cogMethod->selector))) + || (mapObjectReferencesInClosedPIC(cogMethod))) { + freedPIC = 1; + freeMethod(cogMethod); + } + } + else { + if (isYoung((cogMethod->selector))) { + hasYoungObj = 1; + } + if (((cogMethod->cmType)) >= CMMethod) { + assert(((cogMethod->objectHeader)) == (nullHeaderForMachineCodeMethod())); + remappedMethod = remapOop((cogMethod->methodObject)); + if (remappedMethod != ((cogMethod->methodObject))) { + if (methodHasCogMethod(remappedMethod)) { + error("attempt to become two cogged methods"); + } + if (!(withoutForwardingOnandwithsendToCogit((cogMethod->methodObject), remappedMethod, (cogMethod->cmUsesPenultimateLit), methodhasSameCodeAscheckPenultimate))) { + error("attempt to become cogged method into different method"); + } + + /* For non-Newspeak there should ne a one-to-one mapping between bytecoded and + cog methods. For Newspeak not necessarily, but only for anonymous accessors. + Only reset the method object's header if it is referring to this CogMethod. */ + if ((rawHeaderOf((cogMethod->methodObject))) == (((sqInt)cogMethod))) { + rawHeaderOfput((cogMethod->methodObject), (cogMethod->methodHeader)); + (writableCogMethod->methodHeader = rawHeaderOf(remappedMethod)); + (writableCogMethod->methodObject = remappedMethod); + rawHeaderOfput(remappedMethod, ((sqInt)cogMethod)); + } + else { + assert((noAssertMethodClassAssociationOf((cogMethod->methodObject))) == (nilObject())); + (writableCogMethod->methodHeader = rawHeaderOf(remappedMethod)); + (writableCogMethod->methodObject = remappedMethod); + } + } + if (isYoung((cogMethod->methodObject))) { + hasYoungObj = 1; + } + } + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = remapIfObjectRefpchasYoung(annotation, ((char *) mcpc), ((void *)((&hasYoungObj)))); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1: + if (hasYoungObj) { + ensureInYoungReferrers(cogMethod); + hasYoungObj = 0; + } + else { + (cogMethod->cmRefersToYoung = 0); + } + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + + /* we /must/ prune youngReferrers here because a) the [cogMethod cmRefersToYoung: false] + block could have removed a method and subsequently it could be added back, and b) we + can not tolerate duplicates in the youngReferrers list. */ + pruneYoungReferrers(); + if (freedPIC) { + unlinkSendsToFree(); + } + if (codeModified) { + flushICacheFromto(backEnd, ((usqInt)codeBase), freeStart()); + } +} + + +/* Update all references to objects in machine code for a full gc. Since + the current (New)ObjectMemory GC makes everything old in a full GC + a method not referring to young will not refer to young afterwards */ + + /* Cogit>>#mapObjectReferencesInMachineCodeForFullGC */ +static void +mapObjectReferencesInMachineCodeForFullGC(void) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + CogMethod *writableCogMethod; + + codeModified = 0; + mapObjectReferencesInGeneratedRuntime(); + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (!(((cogMethod->cmType)) == CMFree)) { + assert((cogMethodDoesntLookKosher(cogMethod)) == 0); + writableCogMethod = ((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)); + (writableCogMethod->selector = remapOop((cogMethod->selector))); + if (((cogMethod->cmType)) == CMClosedPIC) { + assert(!((cogMethod->cmRefersToYoung))); + mapObjectReferencesInClosedPIC(cogMethod); + } + else { + if (((cogMethod->cmType)) >= CMMethod) { + assert(((cogMethod->objectHeader)) == (nullHeaderForMachineCodeMethod())); + (writableCogMethod->methodObject = remapOop((cogMethod->methodObject))); + } + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = remapIfObjectRefpchasYoung(annotation, ((char *) mcpc), 0); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + pruneYoungReferrers(); + if (codeModified) { + flushICacheFromto(backEnd, ((usqInt)codeBase), freeStart()); + } +} + + +/* Update all references to objects in machine code for either a Spur + scavenging gc + or a Squeak V3 incremental GC. Avoid scanning all code by using the + youngReferrers list. In a young gc a method referring to young may no + longer refer to young, but a + method not referring to young cannot and will not refer to young + afterwards. */ + + /* Cogit>>#mapObjectReferencesInMachineCodeForYoungGC */ +static void +mapObjectReferencesInMachineCodeForYoungGC(void) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt hasYoungObj; + sqInt map; + sqInt mapByte; + usqInt mcpc; + usqInt pointer; + sqInt result; + CogMethod *writableCogMethod; + sqInt zoneIsWritable; + + codeModified = (zoneIsWritable = (hasYoungObj = 0)); + pointer = youngReferrers; + while (pointer < limitAddress) { + assert(!hasYoungObj); + cogMethod = ((CogMethod *) (longAt((void *)(pointer)))); + if (((cogMethod->cmType)) == CMFree) { + assert(!((cogMethod->cmRefersToYoung))); + } + else { + assert((cogMethodDoesntLookKosher(cogMethod)) == 0); + if ((cogMethod->cmRefersToYoung)) { + assert((isCMMethodEtAl(((CogBlockMethod *) cogMethod))) + || (isCMOpenPIC(((CogBlockMethod *) cogMethod)))); + if (!zoneIsWritable) { + /* begin ensureWritableCodeZone */ + zoneIsWritable = 1; + } + writableCogMethod = ((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)); + (writableCogMethod->selector = remapOop((cogMethod->selector))); + if (isYoung((cogMethod->selector))) { + hasYoungObj = 1; + } + if (((cogMethod->cmType)) >= CMMethod) { + assert(((cogMethod->objectHeader)) == (nullHeaderForMachineCodeMethod())); + (writableCogMethod->methodObject = remapOop((cogMethod->methodObject))); + if (isYoung((cogMethod->methodObject))) { + hasYoungObj = 1; + } + } + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = remapIfObjectRefpchasYoung(annotation, ((char *) mcpc), ((void *)((&hasYoungObj)))); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1: + if (hasYoungObj) { + hasYoungObj = 0; + } + else { + (writableCogMethod->cmRefersToYoung = 0); + } + } + } + pointer += BytesPerWord; + } + pruneYoungReferrers(); + if (codeModified) { + flushICacheFromto(backEnd, ((usqInt)methodZoneBase), freeStart()); + } +} + + +/* Update all references to objects in machine code. */ + + /* Cogit>>#mapObjectReferencesInMachineCode: */ +void +mapObjectReferencesInMachineCode(sqInt gcMode) +{ + switch (gcMode) { + case GCModeNewSpace: + /* N.B. do *not* ensureWritableCodeZone for every scavenge. */ + mapObjectReferencesInMachineCodeForYoungGC(); + break; + case GCModeFull: + /* begin ensureWritableCodeZone */ + mapObjectReferencesInMachineCodeForFullGC(); + break; + case GCModeBecome: + /* begin ensureWritableCodeZone */ + mapObjectReferencesInMachineCodeForBecome(); + break; + default: + error("Case not found and no otherwise clause"); + } + mapPerMethodProfile(); + if (!(asserta((freeStart()) <= (youngReferrers)))) { + error("youngReferrers list overflowed"); + } +} + + +/* Mark objects in machine-code of marked methods (or open PICs with marked + selectors). + */ + + /* Cogit>>#markAndTraceMachineCodeOfMarkedMethods */ +void +markAndTraceMachineCodeOfMarkedMethods(void) +{ + sqInt annotation; + sqInt annotationSqInt; + CogMethod *cogMethod; + sqInt map; + sqInt mapByte; + sqInt mapByteSqInt; + sqInt mapSqInt; + usqInt mcpc; + usqInt mcpcUsqInt; + sqInt result; + sqInt resultSqInt; + + if (leakCheckFullGC()) { + asserta(allMachineCodeObjectReferencesValid()); + } + codeModified = 0; + markAndTraceObjectReferencesInGeneratedRuntime(); + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if ((((cogMethod->cmType)) >= CMMethod) + && (isMarked((cogMethod->methodObject)))) { + /* begin markAndTraceLiteralsIn: */ + assert(((isCMMethodEtAl(((CogBlockMethod *) cogMethod))) + && (isMarked((cogMethod->methodObject)))) + || ((isCMOpenPIC(((CogBlockMethod *) cogMethod))) + && ((isImmediate((cogMethod->selector))) + || (isMarked((cogMethod->selector)))))); + markAndTraceLiteralinat((cogMethod->selector), cogMethod, (&((cogMethod->selector)))); + + /* begin maybeMarkIRCsIn: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = markLiteralspcmethod(annotation, ((char *) mcpc), cogMethod); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + if ((((cogMethod->cmType)) == CMOpenPIC) + && ((isImmediate((cogMethod->selector))) + || (isMarked((cogMethod->selector))))) { + /* begin markAndTraceLiteralsIn: */ + assert(((isCMMethodEtAl(((CogBlockMethod *) cogMethod))) + && (isMarked((cogMethod->methodObject)))) + || ((isCMOpenPIC(((CogBlockMethod *) cogMethod))) + && ((isImmediate((cogMethod->selector))) + || (isMarked((cogMethod->selector)))))); + markAndTraceLiteralinat((cogMethod->selector), cogMethod, (&((cogMethod->selector)))); + + /* begin maybeMarkIRCsIn: */ + mapByteSqInt = 0; + mcpcUsqInt = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + mapSqInt = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByteSqInt = byteAt((void *)(mapSqInt)))) != MapEnd) { + if (mapByteSqInt >= FirstAnnotation) { + mcpcUsqInt += (mapByteSqInt & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotationSqInt = ((usqInt)(mapByteSqInt)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByteSqInt = byteAt((void *)(mapSqInt - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotationSqInt += mapByteSqInt & DisplacementMask; + mapSqInt -= 1; + } + resultSqInt = markLiteralspcmethod(annotationSqInt, ((char *) mcpcUsqInt), cogMethod); + if (resultSqInt) { + goto l2; + } + } + else { + if (mapByteSqInt < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpcUsqInt += ((((usqInt)((mapByteSqInt - DisplacementX2N)) << AnnotationShift))); + } + } + mapSqInt -= 1; + } + /* end mapFor:performUntil:arg: */ +l2:; + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if (leakCheckFullGC()) { + asserta(allMachineCodeObjectReferencesValid()); + } + if (codeModified) { + flushICacheFromto(backEnd, ((usqInt)methodZoneBase), freeStart()); + } +} + + +/* Mark and trace any object references in the generated run-time. */ + + /* Cogit>>#markAndTraceObjectReferencesInGeneratedRuntime */ +static void +markAndTraceObjectReferencesInGeneratedRuntime(void) +{ + sqInt i; + sqInt literal; + usqInt mcpc; + + for (i = 0; i < runtimeObjectRefIndex; i += 1) { + mcpc = objectReferencesInRuntime[i]; + literal = literalBeforeFollowingAddress(backEnd, mcpc); + markAndTraceLiteralinatpc(literal, ((CogMethod *) null), ((usqInt)mcpc)); + } +} + + +/* Mark and trace objects in the argument and free if it is appropriate. + Answer if the method has been freed. firstVisit is a hint used to avoid + scanning methods we've already seen. False positives are fine. + For a CMMethod this + frees if the bytecode method isnt marked, + marks and traces object literals and selectors, + unlinks sends to targets that should be freed. + For a CMClosedPIC this + frees if it refers to anything that should be freed or isn't marked. + For a CMOpenPIC this + frees if the selector isn't marked. */ +/* this recurses at most one level down */ + + /* Cogit>>#markAndTraceOrFreeCogMethod:firstVisit: */ +static NoDbgRegParms sqInt +markAndTraceOrFreeCogMethodfirstVisit(CogMethod *cogMethod, sqInt firstVisit) +{ + sqInt annotation; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + + if (((cogMethod->cmType)) == CMFree) { + return 1; + } + assert((cogMethodDoesntLookKosher(cogMethod)) == 0); + if (((cogMethod->cmType)) >= CMMethod) { + if (!(isMarked((cogMethod->methodObject)))) { + /* begin ensureWritableCodeZone */ + freeMethod(cogMethod); + return 1; + } + if (firstVisit) { + /* begin markLiteralsAndUnlinkUnmarkedSendsIn: */ + assert(isCMMethodEtAl(((CogBlockMethod *) cogMethod))); + assert(isMarked((cogMethod->methodObject))); + markAndTraceLiteralinat((cogMethod->selector), cogMethod, (&((cogMethod->selector)))); + + /* begin maybeMarkIRCsIn: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = markLiteralsAndUnlinkIfUnmarkedSendpcmethod(annotation, ((char *) mcpc), cogMethod); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + return 0; + } + if (((cogMethod->cmType)) == CMClosedPIC) { + if (!(closedPICRefersToUnmarkedObject(cogMethod))) { + return 0; + } + + /* begin ensureWritableCodeZone */ + freeMethod(cogMethod); + return 1; + } + if (((cogMethod->cmType)) == CMOpenPIC) { + if (isMarked((cogMethod->selector))) { + return 0; + } + + /* begin ensureWritableCodeZone */ + freeMethod(cogMethod); + return 1; + } + assert((isCMMethodEtAl(((CogBlockMethod *) cogMethod))) + || ((isCMClosedPIC(((CogBlockMethod *) cogMethod))) + || (isCMOpenPIC(((CogBlockMethod *) cogMethod))))); + return 0; +} + + +/* If entryPoint is that of some method, then mark and trace objects in it + and free if it is appropriate. + Answer if the method has been freed. */ + + /* Cogit>>#markAndTraceOrFreePICTarget:in: */ +static NoDbgRegParms sqInt +markAndTraceOrFreePICTargetin(sqInt entryPoint, CogMethod *cPIC) +{ + CogMethod *targetMethod; + + assert((entryPoint > methodZoneBase) + && (entryPoint < (freeStart()))); + if (/* containsAddress: */ + ((((usqInt)cPIC)) <= (((usqInt)entryPoint))) + && (((((usqInt)cPIC)) + ((cPIC->blockSize))) >= (((usqInt)entryPoint)))) { + return 0; + } + targetMethod = ((CogMethod *) (entryPoint - cmNoCheckEntryOffset)); + assert((isCMMethodEtAl(((CogBlockMethod *) targetMethod))) + || (isCMFree(((CogBlockMethod *) targetMethod)))); + return markAndTraceOrFreeCogMethodfirstVisit(targetMethod, (((usqInt)targetMethod)) > (((usqInt)cPIC))); +} + + +/* Mark and trace literals. Unlink sends that have unmarked cache tags or + targets. + */ + + /* Cogit>>#markLiteralsAndUnlinkIfUnmarkedSend:pc:method: */ +static NoDbgRegParms sqInt +markLiteralsAndUnlinkIfUnmarkedSendpcmethod(sqInt annotation, char *mcpc, CogMethod *cogMethod) +{ + unsigned int cacheTag1; + sqInt cacheTagMarked; + usqInt entryPointUsqInt; + sqInt literal; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + sqInt unlinkedRoutine; + + if (annotation == IsObjectReference) { + literal = literalBeforeFollowingAddress(backEnd, ((usqInt)mcpc)); + if (markAndTraceLiteralinatpc(literal, ((CogMethod *) cogMethod), ((usqInt)mcpc))) { + codeModified = 1; + } + } + if (annotation >= IsSendCall) { + /* begin entryCacheTagAndCouldBeObjectAt:annotation:into: */ + cacheTag1 = literal32BeforeFollowingAddress(backEnd, ((usqInt)((((sqInt)mcpc)) - 5))); + entryPointUsqInt = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + + /* in-line cache tags are the selectors of sends if sends are unlinked, + the selectors of super sends (entry offset = cmNoCheckEntryOffset), + the selectors of open PIC sends (entry offset = cmEntryOffset, target is an Open PIC) + or in-line cache tags (classes, class indices, immediate bit patterns, etc). + Note that selectors can be immediate so there is no guarantee that they + are markable/remappable objects. Writing the block invocation three times + allows Slang to inline tagCouldBeObject tests. */ + cacheTagMarked = 0; + if (entryPointUsqInt > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPointUsqInt - offsetSqInt)); + if ((!cacheTagMarked) + || (markAndTraceOrFreeCogMethodfirstVisit(targetMethod1, (((usqInt)targetMethod1)) > (((usqInt)mcpc))))) { + /* Either the cacheTag is unmarked (e.g. new class) or the target + has been freed (because it is unmarked), so unlink the send. */ + + /* begin unlinkSendAt:targetMethod:sendTable: */ + unlinkedRoutine = sendTable1[((((targetMethod1->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod1->cmNumArgs)) : (NumSendTrampolines - 1))]; + codeModified = 1; + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod1->selector), enumeratingCogMethod), unlinkedRoutine); + markAndTraceLiteralinat((targetMethod1->selector), targetMethod1, (&((targetMethod1->selector)))); + } + } + else { + } + } + return 0; +} + + +/* Mark and trace literals. + Additionally in Newspeak, void push implicits that have unmarked classes. */ + + /* Cogit>>#markLiterals:pc:method: */ +static NoDbgRegParms sqInt +markLiteralspcmethod(sqInt annotation, char *mcpc, CogMethod *cogMethod) +{ + unsigned int cacheTag1; + usqInt entryPointUsqInt; + sqInt literal; + + if (annotation == IsObjectReference) { + literal = literalBeforeFollowingAddress(backEnd, ((usqInt)mcpc)); + if (markAndTraceLiteralinatpc(literal, cogMethod, ((usqInt)mcpc))) { + codeModified = 1; + } + } + if (annotation >= IsSendCall) { + /* begin entryCacheTagAndCouldBeObjectAt:annotation:into: */ + cacheTag1 = literal32BeforeFollowingAddress(backEnd, ((usqInt)((((sqInt)mcpc)) - 5))); + entryPointUsqInt = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + + /* in-line cache tags are the selectors of sends if sends are unlinked, + the selectors of super sends (entry offset = cmNoCheckEntryOffset), + the selectors of open PIC sends (entry offset = cmEntryOffset, target is an Open PIC) + or in-line cache tags (classes, class indices, immediate bit patterns, etc). + Note that selectors can be immediate so there is no guarantee that they + are markable/remappable objects. Writing the block invocation three times + allows Slang to inline tagCouldBeObject tests. */ + } + return 0; +} + + /* Cogit>>#markMethodAndReferents: */ +void +markMethodAndReferents(CogBlockMethod *aCogMethod) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + CogMethod *writableMethod; + + assert((isCMMethodEtAl(aCogMethod)) + || (isCMBlock(aCogMethod))); + cogMethod = (((aCogMethod->cmType)) >= CMMethod + ? ((CogMethod *) aCogMethod) + : cmHomeMethod(aCogMethod)); + writableMethod = ((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)); + (writableMethod->cmUsageCount = CMMaxUsageCount); + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = incrementUsageOfTargetIfLinkedSendmcpcignored(annotation, ((char *) mcpc), 0); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; +} + + /* Cogit>>#maxCogMethodAddress */ +usqInt +maxCogMethodAddress(void) +{ + return ((usqInt)(limitZony())); +} + + /* Cogit>>#maximumDistanceFromCodeZone: */ +static NoDbgRegParms sqInt +maximumDistanceFromCodeZone(sqInt anAddress) +{ + return (anAddress > codeBase + ? anAddress - codeBase + : limitAddress - anAddress); +} + + +/* Check that the header fields are consistent with the type. + Answer 0 if it is ok, otherwise answer a code for the error. */ + + /* Cogit>>#maybeFreeCogMethodDoesntLookKosher: */ +static NoDbgRegParms sqInt +maybeFreeCogMethodDoesntLookKosher(CogMethod *cogMethod) +{ + sqInt result; + + result = cogMethodDoesntLookKosher(cogMethod); + return (result == 2 + ? 0 + : result); +} + + /* Cogit>>#mclassCouldBeContext */ +static int +mclassCouldBeContext(void) +{ + if (receiverTags < 0) { + receiverTags = receiverTagBitsForMethod(methodObj); + } + return ((receiverTags & ((1U << (numTagBits())))) != 0); +} + + /* Cogit>>#mclassIsSmallInteger */ +static int +mclassIsSmallInteger(void) +{ + if (receiverTags < 0) { + receiverTags = receiverTagBitsForMethod(methodObj); + } + return (((receiverTags) & 7) == 1); +} + + +/* Answer the absolute machine code pc matching the zero-relative + bytecode pc of a backward branch in cogMethod, given the start + of the bytecodes for cogMethod's block or method object. */ + + /* Cogit>>#mcPCForBackwardBranch:startBcpc:in: */ +usqInt +mcPCForBackwardBranchstartBcpcin(sqInt bcpc, sqInt startbcpc, CogBlockMethod *cogMethod) +{ + sqInt aMethodObj; + sqInt annotation; + sqInt bcpcSqInt; + sqInt bsOffset; + sqInt byte; + BytecodeDescriptor *descriptor; + sqInt distance; + sqInt endbcpc; + CogMethod *homeMethod; + sqInt isBackwardBranch; + sqInt isInBlock; + sqInt latestContinuation; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt nExts; + sqInt nextBcpc; + sqInt result; + sqInt targetPC; + + /* begin mapFor:bcpc:performUntil:arg: */ + descriptor = ((BytecodeDescriptor *) 0); + latestContinuation = 0; + mapByte = 0; + nextBcpc = 0; + assert(((cogMethod->stackCheckOffset)) > 0); + mcpc = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + + /* The stack check maps to the start of the first bytecode, + the first bytecode being effectively after frame build. */ + result = findBackwardBranchIsBackwardBranchMcpcBcpcMatchingBcpc(null, 0 + ((((usqInt)(HasBytecodePC) << 1))), ((char *) mcpc), startbcpc, ((void *)bcpc)); + if (result) { + return result; + } + bcpcSqInt = startbcpc; + + /* In both CMMethod and CMBlock cases find the start of the map and + skip forward to the bytecode pc map entry for the stack check. */ + if (((cogMethod->cmType)) >= CMMethod) { + isInBlock = (cogMethod->cpicHasMNUCaseOrCMIsFullBlock); + homeMethod = ((CogMethod *) cogMethod); + assert(startbcpc == (startPCOfMethodHeader((homeMethod->methodHeader)))); + map = ((((usqInt)homeMethod)) + ((homeMethod->blockSize))) - 1; + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert((annotation == IsAbsPCReference) + || ((annotation == IsObjectReference) + || ((annotation == IsRelativeCall) + || (annotation == IsDisplacementX2N)))); + latestContinuation = startbcpc; + aMethodObj = (homeMethod->methodObject); + endbcpc = (numBytesOf(aMethodObj)) - 1; + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + + /* If the method has a primitive, skip it and the error code store, if any; + Logically. these come before the stack check and so must be ignored. */ + bcpcSqInt += deltaToSkipPrimAndErrorStoreInheader(aMethodObj, (homeMethod->methodHeader)); + } + else { + isInBlock = 1; + assert(bcpcSqInt == ((cogMethod->startpc))); + homeMethod = cmHomeMethod(cogMethod); + map = findMapLocationForMcpcinMethod((((usqInt)cogMethod)) + (sizeof(CogBlockMethod)), homeMethod); + assert(map != 0); + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert(((((usqInt)(annotation)) >> AnnotationShift) == HasBytecodePC) + || ((((usqInt)(annotation)) >> AnnotationShift) == IsDisplacementX2N)); + + /* fiducial */ + while (((annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift)) != HasBytecodePC) { + map -= 1; + } + + /* skip fiducial; i.e. the map entry for the pc immediately following the method header. */ + map -= 1; + aMethodObj = (homeMethod->methodObject); + bcpcSqInt = startbcpc - (/* blockCreationBytecodeSizeForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? AltBlockCreationBytecodeSize + : BlockCreationBytecodeSize)); + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + byte = (fetchByteofObject(bcpcSqInt, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + endbcpc = (bcpcSqInt + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpcSqInt, -1, aMethodObj) + : 0)); + bcpcSqInt = startbcpc; + } + nExts = 0; + enumeratingCogMethod = homeMethod; + + /* Now skip up through the bytecode pc map entry for the stack check. */ + while ((((usqInt)((byteAt((void *)(map))))) >> AnnotationShift) != HasBytecodePC) { + map -= 1; + } + map -= 1; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + mcpc += (mapByte & DisplacementMask); + if (annotation >= HasBytecodePC) { + if ((annotation == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + while (1) { + byte = (fetchByteofObject(bcpcSqInt, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + if (isInBlock) { + if (bcpcSqInt >= endbcpc) { + return 0; + } + } + else { + if (((descriptor->isReturn)) + && (bcpcSqInt >= latestContinuation)) { + return 0; + } + if ((isBranch(descriptor)) + || ((descriptor->isBlockCreation))) { + /* begin latestContinuationPCFor:at:exts:in: */ + distance = ((descriptor->spanFunction))(descriptor, bcpcSqInt, nExts, aMethodObj); + targetPC = (bcpcSqInt + ((descriptor->numBytes))) + (((distance < 0) ? 0 : distance)); + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + } + } + nextBcpc = (bcpcSqInt + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpcSqInt, nExts, aMethodObj) + : 0)); + if (((descriptor->isMapped)) + || (isInBlock + && ((descriptor->isMappedInBlock)))) break; + bcpcSqInt = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + isBackwardBranch = (isBranch(descriptor)) + && ((/* begin isBackwardBranch:at:exts:in: */ + assert(((descriptor->spanFunction))), + (((descriptor->spanFunction))(descriptor, bcpcSqInt, nExts, aMethodObj)) < 0)); + result = findBackwardBranchIsBackwardBranchMcpcBcpcMatchingBcpc(descriptor, (isBackwardBranch + ? ((((usqInt)(annotation) << 1))) + 1 + : ((sqInt)((usqInt)(annotation) << 1))), ((char *) mcpc), (isBackwardBranch + ? bcpcSqInt - (2 * nExts) + : bcpcSqInt), ((void *)bcpc)); + if (result) { + return result; + } + bcpcSqInt = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + } + else { + assert(((((usqInt)(mapByte)) >> AnnotationShift) == IsDisplacementX2N) + || ((((usqInt)(mapByte)) >> AnnotationShift) == IsAnnotationExtension)); + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + + /* defensive; we exit on bcpc */ + return 0; +} + + +/* For the purposes of become: see if the two methods are similar, i.e. can + be safely becommed. + This is pretty strict. All literals and bytecodes must be identical. Only + trailer bytes and header + flags can differ. */ + + /* Cogit>>#method:hasSameCodeAs:checkPenultimate: */ +static NoDbgRegParms sqInt +methodhasSameCodeAscheckPenultimate(sqInt methodA, sqInt methodB, sqInt comparePenultimateLiteral) +{ + sqInt bi; + sqInt endPCA; + sqInt headerA; + sqInt headerB; + sqInt li; + sqInt numLitsA; + + headerA = methodHeaderOf(methodA); + headerB = methodHeaderOf(methodB); + numLitsA = literalCountOfMethodHeader(headerA); + endPCA = endPCOf(methodA); + if (((argumentCountOfMethodHeader(headerA)) != (argumentCountOfMethodHeader(headerB))) + || (((temporaryCountOfMethodHeader(headerA)) != (temporaryCountOfMethodHeader(headerB))) + || (((primitiveIndexOfMethodheader(methodA, headerA)) != (primitiveIndexOfMethodheader(methodB, headerB))) + || ((numLitsA != (literalCountOfMethodHeader(headerB))) + || (endPCA > (numBytesOf(methodB))))))) { + return 0; + } + for (li = 1; li < numLitsA; li += 1) { + if ((fetchPointerofObject(li, methodA)) != (fetchPointerofObject(li, methodB))) { + if ((li < (numLitsA - 1)) + || (comparePenultimateLiteral)) { + return 0; + } + } + } + for (bi = (startPCOfMethodHeader(headerA)); bi <= endPCA; bi += 1) { + if ((fetchByteofObject(bi, methodA)) != (fetchByteofObject(bi, methodB))) { + return 0; + } + } + return 1; +} + + /* Cogit>>#mnuOffset */ +sqInt +mnuOffset(void) +{ + return missOffset; +} + + /* Cogit>>#NativePopR: */ +static NoDbgRegParms AbstractInstruction * +gNativePopR(sqInt reg) +{ + return genoperand(PopR, reg); +} + + /* Cogit>>#NativePushR: */ +static NoDbgRegParms AbstractInstruction * +gNativePushR(sqInt reg) +{ + return genoperand(PushR, reg); +} + + /* Cogit>>#NativeRetN: */ +static NoDbgRegParms AbstractInstruction * +gNativeRetN(sqInt offset) +{ + return genoperand(RetN, offset); +} + + /* Cogit>>#needsFrameIfImmutability: */ +static NoDbgRegParms sqInt +needsFrameIfImmutability(sqInt stackDelta) +{ + return IMMUTABILITY; +} + + /* Cogit>>#needsFrameIfInBlock: */ +static NoDbgRegParms sqInt +needsFrameIfInBlock(sqInt stackDelta) +{ + return inBlock > 0; +} + + +/* Marks inst var read generators. We only need a frame if + accessing an inst var of a context which needs read mediation + (is computed from the context's frame when married). Assumes + an extensible bytecode with nst var index in the 2nd byte. */ + + /* Cogit>>#needsFrameIfReadMediated: */ +static NoDbgRegParms sqInt +needsFrameIfReadMediated(sqInt stackDelta) +{ + return isReadMediatedContextInstVarIndex(byte1 + extA); +} + + /* Cogit>>#needsFrameNever: */ +static NoDbgRegParms sqInt +needsFrameNever(sqInt stackDelta) +{ + return 0; +} + + /* Cogit>>#noAssertMethodClassAssociationOf: */ +static NoDbgRegParms sqInt +noAssertMethodClassAssociationOf(sqInt methodPointer) +{ + return literalofMethod((literalCountOfMethodHeader(noAssertHeaderOf(methodPointer))) - 1, methodPointer); +} + + +/* Check that no method is maximally marked. A maximal mark is an indication + the method has been scanned to increase the usage count of its referent + methods. */ + + /* Cogit>>#noCogMethodsMaximallyMarked */ +static sqInt +noCogMethodsMaximallyMarked(void) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if ((!(((cogMethod->cmType)) == CMFree)) + && (((cogMethod->cmUsageCount)) == CMMaxUsageCount)) { + return 0; + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + return 1; +} + + +/* Answer if all targets in the PIC are in-use methods. */ + + /* Cogit>>#noTargetsFreeInClosedPIC: */ +static NoDbgRegParms int +noTargetsFreeInClosedPIC(CogMethod *cPIC) +{ + return !(cPICHasFreedTargets(cPIC)); +} + + /* Cogit>>#OrCq:R:R: */ +static NoDbgRegParms AbstractInstruction * +gOrCqRR(sqInt quickConstant, sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *first; + + if (srcReg == destReg) { + return genoperandoperand(OrCqR, quickConstant, destReg); + } + first = genoperandoperand(MoveRR, srcReg, destReg); + + /* #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(OrCqR, quickConstant, destReg); + return first; +} + + +/* Store the generated machine code, answering the last address */ + + /* Cogit>>#outputInstructionsAt: */ +static NoDbgRegParms sqInt +outputInstructionsAt(sqInt startAddress) +{ + sqInt absoluteAddress; + AbstractInstruction *abstractInstruction; + sqInt i; + sqInt j; + + /* begin ensureWritableCodeZone */ + absoluteAddress = startAddress; + for (i = 0; i < opcodeIndex; i += 1) { + maybeBreakGeneratingInstructionWithIndex(i); + abstractInstruction = abstractInstructionAt(i); + assert(((abstractInstruction->address)) == absoluteAddress); + + /* begin outputMachineCodeAt: */ + for (j = 0; j < ((abstractInstruction->machineCodeSize)); j += 1) { + byteAtput((void *)(absoluteAddress + j),((abstractInstruction->machineCode))[j]); + } + absoluteAddress += (abstractInstruction->machineCodeSize); + } + return absoluteAddress; +} + + +/* Output instructions generated for one of the generated run-time routines, + a trampoline, etc + */ + + /* Cogit>>#outputInstructionsForGeneratedRuntimeAt: */ +static NoDbgRegParms sqInt +outputInstructionsForGeneratedRuntimeAt(sqInt startAddress) +{ + sqInt endAddress; + sqInt size; + + computeMaximumSizes(); + (methodLabel->address = startAddress); + size = generateInstructionsAt(startAddress); + endAddress = outputInstructionsAt(startAddress); + assert((startAddress + size) == endAddress); + methodZoneBase = alignUptoRoutineBoundary(endAddress); + stopsFromto(backEnd, endAddress, methodZoneBase - 1); + return startAddress; +} + + +/* Code entry closed PIC full or miss to an instance of a young class or to a + young target method. + Attempt to patch the send site to an open PIC. Answer if the attempt + succeeded; in fact it will + only return if the attempt failed. + The stack looks like: + receiver + args + sp=> sender return address */ + + /* Cogit>>#patchToOpenPICFor:numArgs:receiver: */ +sqInt +patchToOpenPICFornumArgsreceiver(sqInt selector, sqInt numArgs, sqInt receiver) +{ + sqInt extent; + CogMethod *oPIC; + sqInt outerReturn; + + outerReturn = stackTop(); + + /* See if an Open PIC is already available. */ + oPIC = openPICWithSelector(selector); + if (!oPIC) { + /* otherwise attempt to create an Open PIC. */ + oPIC = cogOpenPICSelectornumArgs(selector, numArgs); + if ((((((sqInt)oPIC)) >= MaxNegativeErrorCode) && ((((sqInt)oPIC)) <= -1))) { + /* For some reason the PIC couldn't be generated, most likely a lack of code memory. */ + if ((((sqInt)oPIC)) == InsufficientCodeSpace) { + callForCogCompiledCodeCompaction(); + } + return 0; + } + } + + /* Relink the send site to the pic. Reset the cache tag to the selector, for the + benefit of the cacheTag assert check in checkIfValidOopRef:pc:cogMethod: et al. */ + + /* begin ensureWritableCodeZone */ + extent = rewriteInlineCacheAttagtarget(backEnd, outerReturn, inlineCacheValueForSelectorin(backEnd, selector, mframeHomeMethodExport()), (((sqInt)oPIC)) + cmEntryOffset); + flushICacheFromto(backEnd, (((usqInt)outerReturn)) - extent, ((usqInt)outerReturn)); + flushICacheFromto(backEnd, ((usqInt)oPIC), (((usqInt)oPIC)) + openPICSize); + + /* Jump into the oPIC at its entry */ + executeCogMethodfromLinkedSendWithReceiver(oPIC, receiver); + + /* NOTREACHED */ + return 1; +} + + +/* This value is used to decide between MNU processing + or interpretation in the closed PIC aborts. */ + + /* Cogit>>#picAbortDiscriminatorValue */ +static sqInt +picAbortDiscriminatorValue(void) +{ + return 0; +} + + +/* Answer the start of the abort sequence for invoking the interpreter in a + closed PIC. + */ + + /* Cogit>>#picInterpretAbortOffset */ +static sqInt +picInterpretAbortOffset(void) +{ + return (interpretOffset()) - (callInstructionByteSize(backEnd)); +} + + +/* useful for debugging */ + + /* Cogit>>#printCogMethodFor: */ +void +printCogMethodFor(void *address) +{ + CogMethod *cogMethod; + + if ((cogMethod = methodFor(address))) { + printCogMethod(cogMethod); + } + else { + if (codeEntryFor(address)) { + print("trampoline "); + print(codeEntryNameFor(address)); + cr(); + } + else { + print("not a method"); + cr(); + } + } +} + + /* Cogit>>#printPCMapPairsFor: */ +#if VMInvestigations +void +printPCMapPairsFor(CogMethod *cogMethod) +{ + unsigned char annotation; + sqInt map; + unsigned char mapByte; + usqInt mcpc; + sqInt stackCheckPC; + const char *type; + usqInt value; + + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + stackCheckPC = (((sqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + if (annotation == IsAnnotationExtension) { + value = (mapByte & DisplacementMask) + IsSendCall; + } + else { + value = annotation; + mcpc += 1 /* codeGranularity */ * ((annotation + ? mapByte & DisplacementMask + : ((sqInt)((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift)))); + } + if (((value >= IsDirectedSuperSend) && (value <= IsDirectedSuperBindingSend))) { + if (value == IsDirectedSuperSend) { + type = "DirectedSuperSend"; + } + else { + type = "DirectedSuperBindingSend"; + } + } + else { + switch (value) { + case IsDisplacementX2N: + type = "DisplacementX2N"; + break; + case IsAnnotationExtension: + type = "AnnotationExtension"; + break; + case IsObjectReference: + type = "ObjectReference"; + break; + case IsAbsPCReference: + type = "AbsPCReference"; + break; + case HasBytecodePC: + if (mcpc == stackCheckPC) { + type = "StackCheck/BCPC"; + } + else { + type = "HasBytecodePC"; + } + break; + case IsRelativeCall: + type = "RelativeCall"; + break; + case IsSendCall: + type = "SendCall"; + break; + case IsSuperSend: + type = "SuperSend"; + break; + default: + type = null; + } + } + if (type) { + fprintf(getTranscript(), + "%" PRIxSQINT ": %02x %ld (%s) 0x%x @ %p\n", + map, + mapByte, + annotation, + type, + ((int) (mapByte & DisplacementMask)), + ((void *)mcpc)); + } + else { + fprintf(getTranscript(), + "%" PRIxSQINT ": %02x %ld (??? %d) 0x%x @ %p\n", + map, + mapByte, + annotation, + ((int) value), + ((int) (mapByte & DisplacementMask)), + ((void *)mcpc)); + } + map -= 1; + } +} +#endif /* VMInvestigations */ + + +/* useful for debugging */ + + /* Cogit>>#printTrampolineTable */ +void +printTrampolineTable(void) +{ + sqInt i; + + for (i = 0; i < trampolineTableIndex; i += 2) { + fprintf(getTranscript(), + "%p: %s\n", + ((void *)(trampolineAddresses[i + 1])), + ((char *)(trampolineAddresses[i]))); + } +} + + /* Cogit>>#processorHasDivQuoRemAndMClassIsSmallInteger */ +static sqInt +processorHasDivQuoRemAndMClassIsSmallInteger(void) +{ + return mclassIsSmallInteger(); +} + + /* Cogit>>#processorHasMultiplyAndMClassIsSmallInteger */ +static sqInt +processorHasMultiplyAndMClassIsSmallInteger(void) +{ + return mclassIsSmallInteger(); +} + + /* Cogit>>#recordGeneratedRunTime:address: */ +static NoDbgRegParms void +recordGeneratedRunTimeaddress(char *aString, sqInt address) +{ + assert((trampolineTableIndex + 2) <= (NumTrampolines * 2)); + trampolineAddresses[trampolineTableIndex] = aString; + trampolineAddresses[trampolineTableIndex + 1] = (((char *) address)); + + /* self printTrampolineTable */ + trampolineTableIndex += 2; +} + + +/* This one for C support code. */ + + /* Cogit>>#recordPrimTraceFunc */ +int +recordPrimTraceFunc(void) +{ + return recordPrimTrace(); +} + + /* Cogit>>#recordRunTimeObjectReferences */ +static void +recordRunTimeObjectReferences(void) +{ + sqInt i; + AbstractInstruction *instruction; + + for (i = 0; i < opcodeIndex; i += 1) { + instruction = abstractInstructionAt(i); + if (((instruction->annotation)) == IsObjectReference) { + assert(runtimeObjectRefIndex < NumObjRefsInRuntime); + assert(!hasYoungReferent); + if (hasYoungReferent) { + error("attempt to generate run-time routine containing young object reference. Cannot initialize Cogit run-time."); + } + objectReferencesInRuntime[runtimeObjectRefIndex] = (((usqInt)(((instruction->address)) + ((instruction->machineCodeSize))))); + runtimeObjectRefIndex += 1; + } + } +} + + +/* N.B. (self registerMaskFor: NoReg) = 0 */ + + /* Cogit>>#registerMaskFor: */ +static NoDbgRegParms sqInt +registerMaskFor(sqInt reg) +{ + return ((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)); +} + + /* Cogit>>#registerMaskFor:and: */ +static NoDbgRegParms sqInt +registerMaskForand(sqInt reg1, sqInt reg2) +{ + return (1ULL << reg1) | (1ULL << reg2); +} + + /* Cogit>>#relocateCallsAndSelfReferencesInMethod: */ +static NoDbgRegParms void +relocateCallsAndSelfReferencesInMethod(CogMethod *cogMethod) +{ + sqInt annotation; + sqInt callDelta; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqLong refDelta; + sqInt result; + + refDelta = (cogMethod->objectHeader); + callDelta = refDelta; + assert((isCMMethodEtAl(((CogBlockMethod *) cogMethod))) + || (isCMOpenPIC(((CogBlockMethod *) cogMethod)))); + assert((callTargetFromReturnAddress(backEnd, (((sqInt)cogMethod)) + missOffset)) == ((isCMMethodEtAl(((CogBlockMethod *) cogMethod)) + ? methodAbortTrampolineFor((cogMethod->cmNumArgs)) + : picAbortTrampolineFor((cogMethod->cmNumArgs))))); + relocateCallBeforeReturnPCby(backEnd, (((sqInt)cogMethod)) + missOffset, -callDelta); + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = relocateIfCallOrMethodReferencemcpcdelta(annotation, ((char *) mcpc), ((void *)refDelta)); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; +} + + /* Cogit>>#relocateCallsInClosedPIC: */ +static NoDbgRegParms void +relocateCallsInClosedPIC(CogMethod *cPIC) +{ + sqInt callDelta; + sqInt entryPoint; + sqInt i; + sqInt pc; + sqInt pcSqInt; + sqLong refDelta; + CogMethod *targetMethod; + + refDelta = (cPIC->objectHeader); + callDelta = refDelta; + assert((callTargetFromReturnAddress(backEnd, (((sqInt)cPIC)) + missOffset)) == (picAbortTrampolineFor((cPIC->cmNumArgs)))); + relocateCallBeforeReturnPCby(backEnd, (((sqInt)cPIC)) + missOffset, -callDelta); + pc = (((sqInt)cPIC)) + firstCPICCaseOffset; + for (i = 1; i <= ((cPIC->cPICNumCases)); i += 1) { + pc = addressOfEndOfCaseinCPIC(i, cPIC); + entryPoint = (jumpLongTargetBeforeFollowingAddress(backEnd, pc)); + if (/* containsAddress: */ + ((((usqInt)cPIC)) <= (((usqInt)entryPoint))) + && (((((usqInt)cPIC)) + ((cPIC->blockSize))) >= (((usqInt)entryPoint)))) { + /* Interpret/MNU */ + } + else { + targetMethod = ((CogMethod *) (entryPoint - cmNoCheckEntryOffset)); + assert(isCMMethodEtAl(((CogBlockMethod *) targetMethod))); + if (i == 1) { + relocateJumpLongBeforeFollowingAddressby(backEnd, pc, -(callDelta - ((targetMethod->objectHeader)))); + } + else { + relocateJumpLongConditionalBeforeFollowingAddressby(backEnd, pc, -(callDelta - ((targetMethod->objectHeader)))); + } + } + } + assert(((cPIC->cPICNumCases)) > 0); + + /* Finally relocate the load of the PIC and the jump to the overflow routine ceCPICMiss:receiver: */ + pcSqInt = (addressOfEndOfCaseinCPIC(2, cPIC)) + 7 /* loadPICLiteralByteSize */; + + /* begin relocateMethodReferenceBeforeAddress:by: */ + assert((((byteAt((void *)(pcSqInt - 6))) == 141) + && (((byteAt((void *)(pcSqInt - 5))) | (modRMRO(backEnd, 0, 0, 7))) == (modRMRO(backEnd, ModRegInd, 5, 7)))) + || (((byteAt((void *)(pcSqInt - 8))) == 141) + && (((byteAt((void *)(pcSqInt - 7))) | (modRMRO(backEnd, 0, 0, 7))) == (modRMRO(backEnd, ModRegInd, 5, 7))))); + relocateJumpLongBeforeFollowingAddressby(backEnd, (((sqInt)cPIC)) + cPICEndOfCodeOffset, -callDelta); +} + + +/* To placate the C static type system... */ + + /* Cogit>>#relocateIfCallOrMethodReference:mcpc:delta: */ +static NoDbgRegParms sqInt +relocateIfCallOrMethodReferencemcpcdelta(sqInt annotation, char *mcpc, void *refDeltaArg) +{ + sqInt callDelta; + usqInt entryPoint; + sqInt offsetSqInt; + sqInt refDelta; + sqInt *sendTable1; + CogMethod *targetMethod; + sqInt unlinkedRoutine; + + refDelta = ((sqInt) refDeltaArg); + callDelta = refDelta; + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint <= methodZoneBase) { + relocateCallBeforeReturnPCby(backEnd, ((sqInt)mcpc), -callDelta); + return 0; + } + + /* send is not linked; just relocate + It's a linked send; find which kind. */ + + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod = ((CogMethod *) (entryPoint - offsetSqInt)); + if (!(((targetMethod->cmType)) == CMFree)) { + relocateCallBeforeReturnPCby(backEnd, ((sqInt)mcpc), -(callDelta - ((targetMethod->objectHeader)))); + + /* See comment in planCompaction */ + return 0; + } + + /* send target not freed; just relocate. + Target was freed; map back to an unlinked send; but include this method's reocation */ + unlinkedRoutine = sendTable1[((((targetMethod->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod->cmNumArgs)) : (NumSendTrampolines - 1))]; + unlinkedRoutine -= callDelta; + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod->selector), enumeratingCogMethod), unlinkedRoutine); + return 0; + } + if (annotation == IsRelativeCall) { + relocateCallBeforeReturnPCby(backEnd, ((sqInt)mcpc), -callDelta); + return 0; + } + if (annotation == IsAbsPCReference) { + /* begin relocateMethodReferenceBeforeAddress:by: */ + assert((((byteAt((void *)((((sqInt)mcpc)) - 6))) == 141) + && (((byteAt((void *)((((sqInt)mcpc)) - 5))) | (modRMRO(backEnd, 0, 0, 7))) == (modRMRO(backEnd, ModRegInd, 5, 7)))) + || (((byteAt((void *)((((sqInt)mcpc)) - 8))) == 141) + && (((byteAt((void *)((((sqInt)mcpc)) - 7))) | (modRMRO(backEnd, 0, 0, 7))) == (modRMRO(backEnd, ModRegInd, 5, 7))))); + } + return 0; +} + + /* Cogit>>#remapIfObjectRef:pc:hasYoung: */ +static NoDbgRegParms sqInt +remapIfObjectRefpchasYoung(sqInt annotation, char *mcpc, void *hasYoungPtr) +{ + unsigned int cacheTag1; + usqInt entryPointUsqInt; + sqInt literal; + sqInt mappedLiteral; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + + if (annotation == IsObjectReference) { + literal = literalBeforeFollowingAddress(backEnd, ((usqInt)mcpc)); + if (couldBeObject(literal)) { + mappedLiteral = remapObject(literal); + if (literal != mappedLiteral) { + /* begin setCodeModified */ + codeModified = 1; + + /* storeLiteral:atAnnotatedAddress:using: */ + storeLiteralbeforeFollowingAddress(backEnd, mappedLiteral, ((usqInt)mcpc)); + } + if ((hasYoungPtr != 0) + && (isYoung(mappedLiteral))) { + (*((sqInt *) hasYoungPtr) = 1); + } + } + } + if (annotation >= IsSendCall) { + /* begin entryCacheTagAndCouldBeObjectAt:annotation:into: */ + cacheTag1 = literal32BeforeFollowingAddress(backEnd, ((usqInt)((((sqInt)mcpc)) - 5))); + entryPointUsqInt = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + + /* in-line cache tags are the selectors of sends if sends are unlinked, + the selectors of super sends (entry offset = cmNoCheckEntryOffset), + the selectors of open PIC sends (entry offset = cmEntryOffset, target is an Open PIC) + or in-line cache tags (classes, class indices, immediate bit patterns, etc). + Note that selectors can be immediate so there is no guarantee that they + are markable/remappable objects. Writing the block invocation three times + allows Slang to inline tagCouldBeObject tests. */ + if (hasYoungPtr) { + /* Since the unlinking routines may rewrite the cacheTag to the send's selector, and + since they don't have the cogMethod to hand and can't add it to youngReferrers, + the method must remain in youngReferrers if the targetMethod's selector is young. */ + if (entryPointUsqInt > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPointUsqInt - offsetSqInt)); + if (isYoung((targetMethod1->selector))) { + (*((sqInt *) hasYoungPtr) = 1); + } + } + } + } + return 0; +} + + +/* Remap a potential object reference from a closed PIC. + This may be an object reference, an inline cache tag or null. + Answer if the updated literal is young. + mcpc is the address of the next instruction following either + the load of the method literal or the compare of the class tag. */ + + /* Cogit>>#remapMaybeObjRefInClosedPICAt: */ +static NoDbgRegParms sqInt +remapMaybeObjRefInClosedPICAt(sqInt mcpc) +{ + sqInt object; + sqInt subject; + + object = literalBeforeFollowingAddress(backEnd, mcpc); + if (!(couldBeObject(object))) { + return 0; + } + subject = remapOop(object); + if (object != subject) { + /* begin setCodeModified */ + codeModified = 1; + storeLiteralbeforeFollowingAddress(backEnd, subject, mcpc); + } + return isYoungObject(subject); +} + + +/* Rewrite the three values involved in a CPIC case. Used by the initialize & + extend CPICs. + c.f. expectedClosedPICPrototype: */ +/* write the obj ref/operand via the second ldr */ + + /* Cogit>>#rewriteCPICCaseAt:tag:objRef:target: */ +static NoDbgRegParms void +rewriteCPICCaseAttagobjReftarget(sqInt followingAddress, sqInt newTag, sqInt newObjRef, sqInt newTarget) +{ + sqInt classTagPC; + sqInt methodObjPC; + + methodObjPC = (followingAddress - (jumpLongConditionalByteSize(backEnd))) - (cmpC32RTempByteSize(backEnd)); + storeLiteralbeforeFollowingAddress(backEnd, newObjRef, methodObjPC); + classTagPC = followingAddress - (jumpLongConditionalByteSize(backEnd)); + + /* rewite the tag via the first ldr */ + storeLiteral32beforeFollowingAddress(backEnd, newTag, classTagPC); + + /* write the jump address for the new target address */ + rewriteConditionalJumpLongAttarget(backEnd, followingAddress, newTarget); +} + + +/* destReg := fromReg - subReg */ + + /* Cogit>>#SubR:R:R: */ +static NoDbgRegParms AbstractInstruction * +gSubRRR(sqInt subReg, sqInt fromReg, sqInt destReg) +{ + AbstractInstruction *first; + + assert(subReg != destReg); + first = genoperandoperand(MoveRR, fromReg, destReg); + genoperandoperand(SubRR, subReg, destReg); + return first; +} + + +/* Answer the number of clean blocks found in the literal frame */ + + /* Cogit>>#scanForCleanBlocks */ +static sqInt +scanForCleanBlocks(void) +{ + sqInt i; + sqInt lit; + sqInt numCleanBlocks; + sqInt toDoLimit; + + numCleanBlocks = 0; + toDoLimit = literalCountOf(methodObj); + for (i = 1; i <= toDoLimit; i += 1) { + lit = fetchPointerofObject(i, methodObj); + if (startPCOrNilOfLiteralin(lit, methodObj)) { + numCleanBlocks += 1; + } + } + return numCleanBlocks; +} + + +/* If a method is compiled to machine code via a block entry it won't have a + selector. A subsequent send can find the method and hence fill in the + selector. + */ +/* self disassembleMethod: cogMethod */ + + /* Cogit>>#setSelectorOf:to: */ +void +setSelectorOfto(CogMethod *cogMethod, sqInt aSelectorOop) +{ + compilationBreakpointisMNUCase(aSelectorOop, 0); + assert(isCMMethodEtAl(((CogBlockMethod *) cogMethod))); + + /* begin ensureWritableCodeZone */ + ((((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)))->selector = aSelectorOop); + if (isYoung(aSelectorOop)) { + ensureInYoungReferrers(cogMethod); + } + + /* begin ensureExecutableCodeZone */ +} + + /* Cogit>>#spanForCleanBlockStartingAt: */ +static NoDbgRegParms sqInt +spanForCleanBlockStartingAt(sqInt startPC) +{ + BytecodeDescriptor *descriptor; + sqInt end; + sqInt pc; + + pc = startPC; + end = numBytesOf(methodObj); + while (pc <= end) { + /* begin generatorForPC: */ + descriptor = generatorAt(bytecodeSetOffset + (fetchByteofObject(pc, methodObj))); + pc += (descriptor->numBytes); + if ((descriptor->isReturn)) { + return pc - startPC; + } + } + error("couldn't locate end of clean block"); + return 0; +} + + +/* Set up things conveniently for stringForEntryPoint:existingImports:limit: */ + + /* Cogit>>#storeCogMethodDataImports:pc:pointer: */ +#if VMInvestigations +static NoDbgRegParms sqInt +storeCogMethodDataImportspcpointer(sqInt annotation, char *mcpc, usqInt **importsPtrPtr) +{ + unsigned int cacheTag1; + usqInt entryPointUsqInt; + sqInt import; + usqInt *importsPtr; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + + primitiveIndex = (methodHeaderHasPrimitive((enumeratingCogMethod->methodHeader)) + ? primitiveIndexOfMethodheader((enumeratingCogMethod->methodObject), (enumeratingCogMethod->methodHeader)) + : 0); + importsPtr = importsPtrPtr[0]; + if (annotation == IsObjectReference) { + import = literalBeforeFollowingAddress(backEnd, ((usqInt)mcpc)); + importsPtr[0] = ((((usqInt)(mcpc - (((char *)enumeratingCogMethod))) << 3) | 1)); + importsPtr[1] = (positive64BitIntegerFor(import)); + importsPtr[2] = import; + importsPtr += 3; + } + if (annotation == IsRelativeCall) { + if ((import = stringForEntryPointexistingImportslimit(((char *)(callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)))), ((sqInt)(importsPtrPtr[1])), importsPtr))) { + importsPtr[0] = ((((usqInt)(mcpc - (((char *)enumeratingCogMethod))) << 3) | 1)); + importsPtr[1] = (positive64BitIntegerFor(callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)))); + importsPtr[2] = import; + } + else { + return PrimErrNoMemory; + } + importsPtr += 3; + } + if (annotation >= IsSendCall) { + /* begin entryCacheTagAndCouldBeObjectAt:annotation:into: */ + cacheTag1 = literal32BeforeFollowingAddress(backEnd, ((usqInt)((((sqInt)mcpc)) - 5))); + entryPointUsqInt = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + + /* in-line cache tags are the selectors of sends if sends are unlinked, + the selectors of super sends (entry offset = cmNoCheckEntryOffset), + the selectors of open PIC sends (entry offset = cmEntryOffset, target is an Open PIC) + or in-line cache tags (classes, class indices, immediate bit patterns, etc). + Note that selectors can be immediate so there is no guarantee that they + are markable/remappable objects. Writing the block invocation three times + allows Slang to inline tagCouldBeObject tests. */ + if (entryPointUsqInt > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPointUsqInt - offsetSqInt)); + import = (((targetMethod1->cmType)) >= CMMethod + ? (targetMethod1->methodObject) + : (importsPtrPtr[2] + ? (((targetMethod1->cmType)) == CMClosedPIC + ? investigationArrayForClosedPIC(targetMethod1) + : (assert(isCMOpenPIC(((CogBlockMethod *) targetMethod1))), + (targetMethod1->selector))) + : (targetMethod1->selector))); + } + else { + import = stringForEntryPointexistingImportslimit(((char *)entryPointUsqInt), ((sqInt)(importsPtrPtr[1])), importsPtr); + } + + /* send is linked; store target method or Array of selector and cases for Closed PICs or selector for Open PICs + send is unlinked; store a string naming the trampoline, and try not to duplicate strings. */ + if (!import) { + return PrimErrNoMemory; + } + importsPtr[0] = ((((usqInt)(mcpc - (((char *)enumeratingCogMethod))) << 3) | 1)); + importsPtr[1] = (positive64BitIntegerFor(entryPointUsqInt)); + importsPtr[2] = import; + importsPtr += 3; + } + importsPtrPtr[0] = importsPtr; + return 0; +} +#endif /* VMInvestigations */ + + +/* Search for an existing string for the entryPoint in imports up to + nextStoreLocation. If not found answer a new string. */ + + /* Cogit>>#stringForEntryPoint:existingImports:limit: */ +#if VMInvestigations +static NoDbgRegParms sqInt +stringForEntryPointexistingImportslimit(char *entryPoint, sqInt imports, sqInt *nextStoreLocation) +{ + char buffer[32]; + char *entryPointName; + sqInt import; + sqInt len; + sqInt *ptr; + + + /* If an external primitive is called on-stack then it may be reachable by a relative call. + Handle that here by answering the first literal, which holds the external primitive spec. */ + import = 0; + if (primitiveIndex > 0) { + /* This is a special case in Spur */ + if (entryPoint == (((char *)ceNewHashOf))) { + return stringForCString("ceNewHashOf:"); + } + if (entryPoint == (((char *)checkForAndFollowForwardedPrimitiveState))) { + return stringForCString("checkForAndFollowForwardedPrimitiveState"); + } + if (entryPoint == (((char *)ceCheckAndMaybeRetryPrimitive))) { + return stringForCString("ceCheckAndMaybeRetryPrimitive:"); + } + if (entryPoint == (((char *)(functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto((enumeratingCogMethod->methodObject), primitiveIndex, null))))) { + /* Answer the full external spec for external primitives. */ + if (primitiveIndex == PrimCallIsExternalCall) { + return fetchPointerofObject(1, (enumeratingCogMethod->methodObject)); + } + entryPointName = buffer; + snprintf(buffer, sizeof(buffer), "primitive #%ld", ((long)primitiveIndex)); + return stringForCString(entryPointName); + } + } + + /* Normally relative calls call trampolines. So search the trampoline table */ + assert((codeEntryFor(entryPoint)) == entryPoint); + entryPointName = codeEntryNameFor(entryPoint); + len = strlen(entryPointName); + ptr = (((sqInt *) (firstIndexableField(imports)))) + 2; + while ((ptr < nextStoreLocation) + && (!import)) { + if ((isBytes(ptr[0])) + && (((numBytesOf(ptr[0])) == len) + && ((strncmp(entryPointName, ((char *) (firstIndexableField(ptr[0]))), len)) == 0))) { + import = ptr[0]; + } + ptr += 3; + } + return (!(import) + ? stringForCString(entryPointName) + : import); +} +#endif /* VMInvestigations */ + + +/* Answer a fake value for the method oop in other than the first case in the + PIC prototype. + Since we use MoveUniqueCw:R: it must not be confused with a + method-relative address. + */ + + /* Cogit>>#subsequentPrototypeMethodOop */ +static sqInt +subsequentPrototypeMethodOop(void) +{ + return (/* addressIsInCurrentCompilation: */ + ((((usqInt)0xBADA550)) >= ((methodLabel->address))) + && ((((usqInt)0xBADA550)) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize)))) + ? 0xDEADEAD + : 0xBADA550); +} + + /* Cogit>>#TstCq:R: */ +static NoDbgRegParms AbstractInstruction * +gTstCqR(sqInt quickConstant, sqInt reg) +{ + return genoperandoperand(TstCqR, quickConstant, reg); +} + + /* Cogit>>#traceLinkedSendOffset */ +sqInt +traceLinkedSendOffset(void) +{ + return (cmNoCheckEntryOffset + (callFullInstructionByteSize(backEnd))); +} + + /* Cogit>>#trampolineName:numArgs: */ +static NoDbgRegParms char * +trampolineNamenumArgs(char *routinePrefix, sqInt numArgs) +{ + char *theString; + + /* begin trampolineName:numArgs:limit: */ + theString = malloc((strlen(routinePrefix)) + 6); + sprintf(theString, "%s%cArgs", routinePrefix, ((((int) numArgs)) <= (NumSendTrampolines - 2) + ? '0' + (((int) numArgs)) + : 'N')); + return theString; +} + + +/* Malloc a string with the contents for the trampoline table */ + + /* Cogit>>#trampolineName:numArgs:limit: */ +static NoDbgRegParms char * +trampolineNamenumArgslimit(char *routinePrefix, int numArgs, sqInt argsLimit) +{ + char *theString; + + theString = malloc((strlen(routinePrefix)) + 6); + sprintf(theString, "%s%cArgs", routinePrefix, (numArgs <= argsLimit + ? '0' + numArgs + : 'N')); + return theString; +} + + /* Cogit>>#trampolineName:numRegArgs: */ +static NoDbgRegParms char * +trampolineNamenumRegArgs(char *routinePrefix, sqInt numArgs) +{ + char *theString; + + /* begin trampolineName:numArgs:limit: */ + theString = malloc((strlen(routinePrefix)) + 6); + sprintf(theString, "%s%cArgs", routinePrefix, ((((int) numArgs)) <= (numRegArgs()) + ? '0' + (((int) numArgs)) + : 'N')); + return theString; +} + + /* Cogit>>#unflagBecomeFlaggedMethods */ +void +unflagBecomeFlaggedMethods(void) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) == CMMethodFlaggedForBecome) { + ((((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)))->cmType = CMMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } +} + + /* Cogit>>#unknownBytecode */ +static sqInt +unknownBytecode(void) +{ + return EncounteredUnknownBytecode; +} + + +/* Unlink all sends in cog methods. */ + + /* Cogit>>#unlinkAllSends */ +void +unlinkAllSends(void) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + + if (!methodZoneBase) { + return; + } + + /* begin ensureWritableCodeZone */ + cogMethod = ((CogMethod *) methodZoneBase); + voidOpenPICList(); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = unlinkIfLinkedSendpcignored(annotation, ((char *) mcpc), 0); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + else { + if (!(((cogMethod->cmType)) == CMFree)) { + freeMethod(cogMethod); + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + + /* After updating inline caches we need to flush the icache. */ + flushICacheFromto(backEnd, ((usqInt)methodZoneBase), freeStart()); +} + + +/* To placate the C static type system... */ + + /* Cogit>>#unlinkIfFreeOrLinkedSend:pc:of: */ +static NoDbgRegParms sqInt +unlinkIfFreeOrLinkedSendpcof(sqInt annotation, char *mcpc, void *theSelector) +{ + usqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + sqInt unlinkedRoutine; + + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if ((((targetMethod1->cmType)) == CMFree) + || (((targetMethod1->selector)) == (((sqInt) theSelector)))) { + /* begin unlinkSendAt:targetMethod:sendTable: */ + unlinkedRoutine = sendTable1[((((targetMethod1->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod1->cmNumArgs)) : (NumSendTrampolines - 1))]; + codeModified = 1; + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod1->selector), enumeratingCogMethod), unlinkedRoutine); + } + } + } + return 0; +} + + /* Cogit>>#unlinkIfInvalidClassSend:pc:ignored: */ +static NoDbgRegParms sqInt +unlinkIfInvalidClassSendpcignored(sqInt annotation, char *mcpc, sqInt superfluity) +{ + usqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + sqInt unlinkedRoutine; + + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (!((/* annotationIsForUncheckedEntryPoint: */ + (annotation == IsSuperSend) + || (((annotation >= IsDirectedSuperSend) && (annotation <= IsDirectedSuperBindingSend)))) + || (((targetMethod1->cmType)) == CMOpenPIC))) { + if (!(isValidClassTag(literal32BeforeFollowingAddress(backEnd, ((usqInt)((((sqInt)mcpc)) - 5)))))) { + /* begin unlinkSendAt:targetMethod:sendTable: */ + unlinkedRoutine = sendTable1[((((targetMethod1->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod1->cmNumArgs)) : (NumSendTrampolines - 1))]; + codeModified = 1; + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod1->selector), enumeratingCogMethod), unlinkedRoutine); + } + } + } + } + return 0; +} + + /* Cogit>>#unlinkIfLinkedSendToFree:pc:ignored: */ +static NoDbgRegParms sqInt +unlinkIfLinkedSendToFreepcignored(sqInt annotation, char *mcpc, sqInt superfluity) +{ + usqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + sqInt unlinkedRoutine; + + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (((targetMethod1->cmType)) == CMFree) { + /* begin unlinkSendAt:targetMethod:sendTable: */ + unlinkedRoutine = sendTable1[((((targetMethod1->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod1->cmNumArgs)) : (NumSendTrampolines - 1))]; + codeModified = 1; + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod1->selector), enumeratingCogMethod), unlinkedRoutine); + } + } + } + return 0; +} + + /* Cogit>>#unlinkIfLinkedSend:pc:if: */ +static NoDbgRegParms sqInt +unlinkIfLinkedSendpcif(sqInt annotation, char *mcpc, sqInt (*criterion)(CogMethod *)) +{ + usqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + sqInt unlinkedRoutine; + + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (criterion(targetMethod1)) { + /* begin unlinkSendAt:targetMethod:sendTable: */ + unlinkedRoutine = sendTable1[((((targetMethod1->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod1->cmNumArgs)) : (NumSendTrampolines - 1))]; + codeModified = 1; + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod1->selector), enumeratingCogMethod), unlinkedRoutine); + } + } + } + return 0; +} + + /* Cogit>>#unlinkIfLinkedSend:pc:ignored: */ +static NoDbgRegParms sqInt +unlinkIfLinkedSendpcignored(sqInt annotation, char *mcpc, sqInt superfluity) +{ + usqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + sqInt unlinkedRoutine; + + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + + /* begin unlinkSendAt:targetMethod:sendTable: */ + unlinkedRoutine = sendTable1[((((targetMethod1->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod1->cmNumArgs)) : (NumSendTrampolines - 1))]; + codeModified = 1; + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod1->selector), enumeratingCogMethod), unlinkedRoutine); + } + } + return 0; +} + + /* Cogit>>#unlinkIfLinkedSend:pc:to: */ +static NoDbgRegParms sqInt +unlinkIfLinkedSendpcto(sqInt annotation, char *mcpc, CogMethod *theCogMethod) +{ + usqInt entryPoint; + sqInt offsetSqInt; + sqInt *sendTable1; + CogMethod *targetMethod1; + sqInt unlinkedRoutine; + + if (annotation >= IsSendCall) { + entryPoint = callTargetFromReturnAddress(backEnd, ((sqInt)mcpc)); + if (entryPoint > methodZoneBase) { + /* begin targetMethodAndSendTableFor:annotation:into: */ + /* begin offsetAndSendTableFor:annotation:into: */ + if (annotation == IsSendCall) { + offsetSqInt = cmEntryOffset; + sendTable1 = ordinarySendTrampolines; + } + else { + if (annotation == IsDirectedSuperSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperSendTrampolines; + } + else { + if (annotation == IsDirectedSuperBindingSend) { + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = directedSuperBindingSendTrampolines; + } + else { + assert(annotation == IsSuperSend); + offsetSqInt = cmNoCheckEntryOffset; + sendTable1 = superSendTrampolines; + } + } + } + targetMethod1 = ((CogMethod *) (entryPoint - offsetSqInt)); + if (targetMethod1 == theCogMethod) { + /* begin unlinkSendAt:targetMethod:sendTable: */ + unlinkedRoutine = sendTable1[((((targetMethod1->cmNumArgs)) < (NumSendTrampolines - 1)) ? ((targetMethod1->cmNumArgs)) : (NumSendTrampolines - 1))]; + codeModified = 1; + rewriteInlineCacheAttagtarget(backEnd, ((sqInt)mcpc), inlineCacheValueForSelectorin(backEnd, (targetMethod1->selector), enumeratingCogMethod), unlinkedRoutine); + } + } + } + return 0; +} + + +/* Unlink all sends in cog methods whose class tag is that of a forwarded + class. + */ + + /* Cogit>>#unlinkSendsLinkedForInvalidClasses */ +void +unlinkSendsLinkedForInvalidClasses(void) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt freedPIC; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + + if (!methodZoneBase) { + return; + } + cogMethod = ((CogMethod *) methodZoneBase); + codeModified = (freedPIC = 0); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = unlinkIfInvalidClassSendpcignored(annotation, ((char *) mcpc), 0); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + else { + if ((((cogMethod->cmType)) == CMClosedPIC) + && (cPICHasForwardedClass(cogMethod))) { + freeMethod(cogMethod); + freedPIC = 1; + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if (freedPIC) { + unlinkSendsToFree(); + } + else { + if (codeModified) { + flushICacheFromto(backEnd, ((usqInt)methodZoneBase), freeStart()); + } + } +} + + +/* Unlink all sends in cog methods. Free all PICs with the selector, or + Closed PICs with an + MNU case if isMNUSelector. First check if any method actually has the + selector; if not + there can't be any linked send to it. This routine (including descendents) + is performance + critical. It contributes perhaps 30% of entire execution time in Compiler + recompileAll. */ + + /* Cogit>>#unlinkSendsOf:isMNUSelector: */ +void +unlinkSendsOfisMNUSelector(sqInt selector, sqInt isMNUSelector) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt mustScanAndUnlink; + sqInt result; + + if (!methodZoneBase) { + return; + } + cogMethod = ((CogMethod *) methodZoneBase); + mustScanAndUnlink = 0; + if (isMNUSelector) { + while (cogMethod < (limitZony())) { + if (!(((cogMethod->cmType)) == CMFree)) { + if (/* cpicHasMNUCase */ + ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + && (((cogMethod->cmType)) == CMClosedPIC)) { + assert(isCMClosedPIC(((CogBlockMethod *) cogMethod))); + freeMethod(cogMethod); + mustScanAndUnlink = 1; + } + else { + if (((cogMethod->selector)) == selector) { + mustScanAndUnlink = 1; + if (/* isCMAnyPIC */ + (((cogMethod->cmType)) <= CMOpenPIC) + && (((cogMethod->cmType)) >= CMClosedPIC)) { + freeMethod(cogMethod); + } + } + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + } + else { + while (cogMethod < (limitZony())) { + if ((!(((cogMethod->cmType)) == CMFree)) + && (((cogMethod->selector)) == selector)) { + mustScanAndUnlink = 1; + if (/* isCMAnyPIC */ + (((cogMethod->cmType)) <= CMOpenPIC) + && (((cogMethod->cmType)) >= CMClosedPIC)) { + freeMethod(cogMethod); + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + } + if (!mustScanAndUnlink) { + return; + } + codeModified = 0; + voidOpenPICList(); + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = unlinkIfFreeOrLinkedSendpcof(annotation, ((char *) mcpc), ((CogMethod *) selector)); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + else { + if (((cogMethod->cmType)) == CMOpenPIC) { + /* begin setCodeModified */ + codeModified = 1; + addToOpenPICList(((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta))); + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if (codeModified) { + flushICacheFromto(backEnd, ((usqInt)methodZoneBase), freeStart()); + } + + /* After possibly updating inline caches we need to flush the icache. + And ensure code zone is executable. May merely have freed methods... */ + + /* begin ensureExecutableCodeZone */ +} + + +/* Unlink all sends in cog methods to free methods and/or pics. */ + + /* Cogit>>#unlinkSendsToFree */ +static void +unlinkSendsToFree(void) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + + if (!methodZoneBase) { + return; + } + codeModified = 0; + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = unlinkIfLinkedSendToFreepcignored(annotation, ((char *) mcpc), 0); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + else { + if (((cogMethod->cmType)) == CMClosedPIC) { + assert(noTargetsFreeInClosedPIC(cogMethod)); + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if (codeModified) { + flushICacheFromto(backEnd, ((usqInt)methodZoneBase), freeStart()); + } +} + + +/* Unlink all sends in cog methods to methods with a machine code + primitive, and free machine code primitive methods if freeIfTrue. + To avoid having to scan PICs, free any and all PICs */ + + /* Cogit>>#unlinkSendsToMethodsSuchThat:AndFreeIf: */ +void +unlinkSendsToMethodsSuchThatAndFreeIf(sqInt (*criterion)(CogMethod *), sqInt freeIfTrue) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt freedSomething; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + + if (!methodZoneBase) { + return; + } + codeModified = (freedSomething = 0); + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + if (freeIfTrue + && (criterion(cogMethod))) { + freeMethod(cogMethod); + freedSomething = 1; + } + else { + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = unlinkIfLinkedSendpcif(annotation, ((char *) mcpc), ((void *)criterion)); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + } + else { + if (((cogMethod->cmType)) == CMClosedPIC) { + freeMethod(cogMethod); + freedSomething = 1; + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if (freedSomething) { + unlinkSendsToFree(); + } + else { + if (codeModified) { + flushICacheFromto(backEnd, ((usqInt)methodZoneBase), freeStart()); + } + } + + /* begin ensureExecutableCodeZone */ +} + + +/* Unlink all sends in cog methods to a particular target method. + If targetMethodObject isn't actually a method (perhaps being + used via invokeAsMethod) then there's nothing to do. */ + + /* Cogit>>#unlinkSendsTo:andFreeIf: */ +void +unlinkSendsToandFreeIf(sqInt targetMethodObject, sqInt freeIfTrue) +{ + sqInt annotation; + CogMethod *cogMethod; + sqInt freedPIC; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt result; + CogMethod *targetMethod; + + if (!((isOopCompiledMethod(targetMethodObject)) + && (methodHasCogMethod(targetMethodObject)))) { + return; + } + targetMethod = cogMethodOf(targetMethodObject); + if (!methodZoneBase) { + return; + } + + /* begin ensureWritableCodeZone */ + codeModified = (freedPIC = 0); + cogMethod = ((CogMethod *) methodZoneBase); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) >= CMMethod) { + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = unlinkIfLinkedSendpcto(annotation, ((char *) mcpc), targetMethod); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1:; + } + else { + if ((((cogMethod->cmType)) == CMClosedPIC) + && (cPICHasTarget(cogMethod, targetMethod))) { + freeMethod(cogMethod); + freedPIC = 1; + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + if (freeIfTrue) { + freeMethod(targetMethod); + } + if (freedPIC) { + unlinkSendsToFree(); + } + else { + if (codeModified) { + flushICacheFromto(backEnd, ((usqInt)methodZoneBase), freeStart()); + } + } + + /* begin ensureExecutableCodeZone */ +} + + /* Cogit>>#v3:Block:Code:Size: */ +static NoDbgRegParms sqInt +v3BlockCodeSize(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + assert(nExts <= 0); + return ((((usqInt)((fetchByteofObject(pc + 2, aMethodObj))) << 8))) + (fetchByteofObject(pc + 3, aMethodObj)); +} + + +/* Answer the distance of a two byte forward long jump. */ + + /* Cogit>>#v3:LongForward:Branch:Distance: */ +static NoDbgRegParms sqInt +v3LongForwardBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + assert(nExts == 0); + return ((((fetchByteofObject(pc, aMethodObj)) & 3) << 8)) + (fetchByteofObject(pc + 1, aMethodObj)); +} + + +/* Answer the distance of a two byte forward long jump. */ + + /* Cogit>>#v3:Long:Branch:Distance: */ +static NoDbgRegParms sqInt +v3LongBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + assert(nExts == 0); + return ((((usqInt)((((fetchByteofObject(pc, aMethodObj)) & 7) - 4)) << 8))) + (fetchByteofObject(pc + 1, aMethodObj)); +} + + +/* N.B. This serves for both BlueBook/V3 and V4 short jumps. */ + + /* Cogit>>#v3:ShortForward:Branch:Distance: */ +static NoDbgRegParms sqInt +v3ShortForwardBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + assert(nExts == 0); + return ((fetchByteofObject(pc, aMethodObj)) & 7) + 1; +} + + +/* 253 11111101 eei i i kkk jjjjjjjj Push Closure Num Copied iii (+ Ext A + // 16 * 8) Num Args kkk (+ Ext A \\ 16 * 8) BlockSize jjjjjjjj (+ Ext B * + 256). ee = num extensions + */ + + /* Cogit>>#v4:Block:Code:Size: */ +static NoDbgRegParms sqInt +v4BlockCodeSize(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + sqInt byte; + sqInt byteOne; + sqInt extAValue; + sqInt extBValue; + sqInt extBValueSqInt; + sqInt extByte; + sqInt pcSqInt; + + byteOne = fetchByteofObject(pc + 1, aMethodObj); + + /* If nExts < 0 it isn't known and we rely on the number of extensions encoded in the eeiiikkk byte. */ + assert((nExts < 0) + || (nExts == (((usqInt)(byteOne)) >> 6))); + + /* begin parseV4Exts:priorTo:in:into: */ + extAValue = (extBValueSqInt = 0); + pcSqInt = (pc - (((usqInt)(byteOne)) >> 6)) - (((usqInt)(byteOne)) >> 6); + while (pcSqInt < pc) { + byte = fetchByteofObject(pcSqInt, aMethodObj); + pcSqInt += 1; + extByte = fetchByteofObject(pcSqInt, aMethodObj); + pcSqInt += 1; + assert((byte == 224) + || (byte == 225)); + if (byte == 224) { + extAValue = (((((usqInt)(extAValue) << 8)))) + extByte; + } + else { + extBValueSqInt = ((extBValueSqInt == 0) + && (extByte > 0x7F) + ? extByte - 0x100 + : (((((usqInt)(extBValueSqInt) << 8)))) + extByte); + } + } + extBValue = extBValueSqInt; + return (fetchByteofObject(pc + 2, aMethodObj)) + ((((usqInt)(extBValue) << 8))); +} + + +/* 242 11110010 i i i i i i i i Jump i i i i i i i i (+ Extend B * 256, + where bbbbbbbb = sddddddd, e.g. -32768 = i=0, a=0, s=1) + */ +/* 243 11110011 i i i i i i i i Pop and Jump 0n True i i i i i i i i (+ + Extend A * 256) + */ +/* 244 11110100 i i i i i i i i Pop and Jump 0n False i i i i i i i i (+ + Extend A * 256) + */ + + /* Cogit>>#v4:LongForward:Branch:Distance: */ +static NoDbgRegParms sqInt +v4LongForwardBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + sqInt byte; + sqInt extAValue; + sqInt extBValue; + sqInt extBValueSqInt; + sqInt extByte; + sqInt pcSqInt; + + assert(nExts >= 0); + + /* begin parseV4Exts:priorTo:in:into: */ + extAValue = (extBValueSqInt = 0); + pcSqInt = (pc - nExts) - nExts; + while (pcSqInt < pc) { + byte = fetchByteofObject(pcSqInt, aMethodObj); + pcSqInt += 1; + extByte = fetchByteofObject(pcSqInt, aMethodObj); + pcSqInt += 1; + assert((byte == 224) + || (byte == 225)); + if (byte == 224) { + extAValue = (((((usqInt)(extAValue) << 8)))) + extByte; + } + else { + extBValueSqInt = ((extBValueSqInt == 0) + && (extByte > 0x7F) + ? extByte - 0x100 + : (((((usqInt)(extBValueSqInt) << 8)))) + extByte); + } + } + extBValue = extBValueSqInt; + return (fetchByteofObject(pc + 1, aMethodObj)) + ((((usqInt)(extBValue) << 8))); +} + + +/* 242 11110010 i i i i i i i i Jump i i i i i i i i (+ Extend B * 256, + where bbbbbbbb = sddddddd, e.g. -32768 = i=0, a=0, s=1) + */ + + /* Cogit>>#v4:Long:Branch:Distance: */ +static NoDbgRegParms sqInt +v4LongBranchDistance(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + sqInt byte; + sqInt extAValue; + sqInt extBValue; + sqInt extBValueSqInt; + sqInt extByte; + sqInt pcSqInt; + + assert(nExts >= 0); + + /* begin parseV4Exts:priorTo:in:into: */ + extAValue = (extBValueSqInt = 0); + pcSqInt = (pc - nExts) - nExts; + while (pcSqInt < pc) { + byte = fetchByteofObject(pcSqInt, aMethodObj); + pcSqInt += 1; + extByte = fetchByteofObject(pcSqInt, aMethodObj); + pcSqInt += 1; + assert((byte == 224) + || (byte == 225)); + if (byte == 224) { + extAValue = (((((usqInt)(extAValue) << 8)))) + extByte; + } + else { + extBValueSqInt = ((extBValueSqInt == 0) + && (extByte > 0x7F) + ? extByte - 0x100 + : (((((usqInt)(extBValueSqInt) << 8)))) + extByte); + } + } + extBValue = extBValueSqInt; + return (fetchByteofObject(pc + 1, aMethodObj)) + ((((usqInt)(extBValue) << 8))); +} + + /* Cogit>>#voidCogCompiledCode */ +void +voidCogCompiledCode(void) +{ + CogMethod *cogMethod; + usqInt theLimitAddress; + usqInt theStartAddress; + + /* begin clearCogCompiledCode */ + cogMethod = ((CogMethod *) baseAddress); + while ((((usqInt)cogMethod)) < mzFreeStart) { + if (((cogMethod->cmType)) >= CMMethod) { + freeMethod(cogMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + theStartAddress = baseAddress; + theLimitAddress = limitAddress; + + /* begin manageFrom:to: */ + mzFreeStart = (baseAddress = theStartAddress); + youngReferrers = (limitAddress = theLimitAddress); + openPICList = null; + methodBytesFreedSinceLastCompaction = 0; + methodCount = 0; + + /* code must still be executable, e.g. for ceInvokeInterpret */ + + /* begin ensureExecutableCodeZone */ +} + + +/* Access for the object representations when they need to prepend code to + trampolines. + */ +/* Eliminate stale dependent info. */ + + /* Cogit>>#zeroOpcodeIndex */ +static void +zeroOpcodeIndex(void) +{ + sqInt i; + + for (i = 0; i < opcodeIndex; i += 1) { + ((abstractOpcodes[i]).dependent = null); + } + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; +} + + /* CogMethodZone>>#addToOpenPICList: */ +static NoDbgRegParms void +addToOpenPICList(CogMethod *anOpenPIC) +{ + assert(isCMOpenPIC(((CogBlockMethod *) anOpenPIC))); + assert((openPICList == null) + || (isCMOpenPIC(((CogBlockMethod *) openPICList)))); + assertValidDualZoneWriteAddress(anOpenPIC); + (anOpenPIC->nextOpenPIC = ((usqInt)openPICList)); + openPICList = ((CogMethod *) ((((usqInt)anOpenPIC)) - (getCodeToDataDelta()))); +} + + /* CogMethodZone>>#addToYoungReferrers: */ +static NoDbgRegParms void +addToYoungReferrers(CogMethod *writableCogMethod) +{ + assertValidDualZoneWriteAddress(writableCogMethod); + assert((occurrencesInYoungReferrers(writableCogMethod)) == 0); + assert((writableCogMethod->cmRefersToYoung)); + assert((youngReferrers <= limitAddress) + && (youngReferrers >= (limitAddress - (methodCount * BytesPerWord)))); + if (!(asserta((limitAddress - (methodCount * BytesPerWord)) >= mzFreeStart))) { + error("no room on youngReferrers list"); + } + youngReferrers -= BytesPerWord; + codeLongAtput((void *)(youngReferrers),(((usqInt)writableCogMethod)) - (getCodeToDataDelta())); +} + + /* CogMethodZone>>#allocate: */ +static NoDbgRegParms usqInt +allocate(sqInt numBytes) +{ + usqInt allocation; + int roundedBytes; + + roundedBytes = (numBytes + 7) & -8; + if ((mzFreeStart + roundedBytes) >= (limitAddress - (methodCount * BytesPerWord))) { + return 0; + } + allocation = mzFreeStart; + mzFreeStart += roundedBytes; + methodCount += 1; + return allocation; +} + + +/* Answer the method containing mcpc for the purposes of code zone + compaction, where mcpc is actually the value of instructionPointer at the + time of a compaction. */ + + /* CogMethodZone>>#cogMethodContaining: */ +CogMethod * +cogMethodContaining(usqInt mcpc) +{ + CogMethod *cogMethod; + CogMethod *prevMethod; + + if (mcpc > limitAddress) { + return null; + } + if (mcpc < baseAddress) { + /* assertMcpcIsPrimReturn: */ + assert((mcpc == cePrimReturnEnterCogCode) + || (mcpc == cePrimReturnEnterCogCodeProfiling)); + return null; + } + assert(mcpc < (freeStart())); + cogMethod = ((CogMethod *) baseAddress); + + /* Speed up the search by finding the entry in the openPICList before mcpc. + Typically 2 to 3% of the zone's entities are openPICs. The openPICList is ordered high to low. */ + if (openPICList) { + prevMethod = openPICList; + while (!((!prevMethod) + || ((((usqInt)prevMethod)) < mcpc))) { + prevMethod = ((CogMethod *) ((prevMethod->nextOpenPIC))); + } + if (prevMethod) { + cogMethod = prevMethod; + } + } + + /* Now search upward from either baseAddress or the first entry in the openPICList below mcpc. */ + while ((((usqInt)cogMethod)) < mcpc) { + prevMethod = cogMethod; + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + return prevMethod; +} + + /* CogMethodZone>>#compactCompiledCode */ +static void +compactCompiledCode(void) +{ + unsigned short bytes; + CogMethod *dest; + CogMethod *source; + CogMethod *writableVersion; + + compactionInProgress = 1; + methodCount = 0; + source = ((CogMethod *) baseAddress); + voidOpenPICList(); + while ((source < (limitZony())) + && (!(((source->cmType)) == CMFree))) { + assert((cogMethodDoesntLookKosher(source)) == 0); + writableVersion = ((CogMethod *) ((((usqInt)source)) + codeToDataDelta)); + (writableVersion->objectHeader = nullHeaderForMachineCodeMethod()); + if (((source->cmUsageCount)) > 0) { + (writableVersion->cmUsageCount = ((source->cmUsageCount)) / 2); + } + + /* begin maybeLinkOnUnpairedMethodList: */ + if (((source->cmType)) == CMOpenPIC) { + addToOpenPICList(writableVersion); + } + methodCount += 1; + source = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)source)) + ((source->blockSize))))); + } + if (source >= (limitZony())) { + haltmsg("no free methods; cannot compact."); + return; + } + dest = source; + while (source < (limitZony())) { + assert((maybeFreeCogMethodDoesntLookKosher(source)) == 0); + bytes = (source->blockSize); + if (!(((source->cmType)) == CMFree)) { + methodCount += 1; + codeMemmove(dest, source, bytes); + + /* begin maybeFlushWritableZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + if (codeToDataDelta > 0) { + flushDCacheFromto(backEnd, ((usqInt)dest), (((usqInt)dest)) + bytes); + } +# endif + + ((writableVersion = ((CogMethod *) ((((usqInt)dest)) + codeToDataDelta)))->objectHeader = nullHeaderForMachineCodeMethod()); + if (((dest->cmType)) >= CMMethod) { + /* For non-Newspeak there should be a one-to-one mapping between bytecoded and + cog methods. For Newspeak not necessarily, but only for anonymous accessors. + Only update the original method's header if it is referring to this CogMethod. */ + if ((((sqInt)(rawHeaderOf((dest->methodObject))))) == (((sqInt)source))) { + rawHeaderOfput((dest->methodObject), ((sqInt)dest)); + } + else { + assert((noAssertMethodClassAssociationOf((dest->methodObject))) == (nilObject())); + + /* begin linkOnUnpairedMethodList: */ + } + } + else { + /* begin clearSavedPICUsageCount: */ + if (((dest->cmType)) == CMOpenPIC) { + addToOpenPICList(writableVersion); + } + } + if (((dest->cmUsageCount)) > 0) { + (writableVersion->cmUsageCount = ((dest->cmUsageCount)) / 2); + } + + /* begin maybeFlushWritableZoneFrom:to: */ +# if DUAL_MAPPED_CODE_ZONE + if (codeToDataDelta > 0) { + flushDCacheFromto(backEnd, ((usqInt)dest), ((usqInt)(dest + 1))); + } +# endif + + dest = ((CogMethod *) ((((usqInt)dest)) + bytes)); + } + source = ((CogMethod *) ((((usqInt)source)) + bytes)); + } + mzFreeStart = ((usqInt)dest); + methodBytesFreedSinceLastCompaction = 0; + compactionInProgress = 0; +} + + /* CogMethodZone>>#ensureInYoungReferrers: */ +static NoDbgRegParms void +ensureInYoungReferrers(CogMethod *cogMethod) +{ + CogMethod *writableMethod; + + assertValidDualZoneReadAddress(cogMethod); + if (!((cogMethod->cmRefersToYoung))) { + assert((occurrencesInYoungReferrers(cogMethod)) == 0); + + /* begin ensureWritableCodeZone */ + ((writableMethod = ((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)))->cmRefersToYoung = 1); + addToYoungReferrers(writableMethod); + } +} + + /* CogMethodZone>>#freeMethod: */ +static NoDbgRegParms void +freeMethod(CogMethod *cogMethod) +{ + CogMethod *writableMethod; + + assert(!((isCMFree(((CogBlockMethod *) cogMethod))))); + assert((cogMethodDoesntLookKosher(cogMethod)) == 0); + + /* begin ensureWritableCodeZone */ + if (((cogMethod->cmType)) >= CMMethod) { + if (((cogMethod->cmType)) == CMMethodFlaggedForBecome) { + } + else { + /* For non-Newspeak there should be a one-to-one mapping between bytecoded and + cog methods. For Newspeak not necessarily, but only for anonymous accessors. + Only reset the original method's header if it is referring to this CogMethod. */ + if ((((sqInt)(rawHeaderOf((cogMethod->methodObject))))) == (((sqInt)cogMethod))) { + rawHeaderOfput((cogMethod->methodObject), (cogMethod->methodHeader)); + } + else { + assert((noAssertMethodClassAssociationOf((cogMethod->methodObject))) == (nilObject())); + } + } + } + if (((cogMethod->cmType)) == CMOpenPIC) { + removeFromOpenPICList(cogMethod); + } + writableMethod = ((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)); + (writableMethod->cmRefersToYoung = 0); + (writableMethod->cmType = CMFree); + methodBytesFreedSinceLastCompaction += (cogMethod->blockSize); +} + + +/* Free methods, preferring older methods for compaction, up to some + fraction, currently a quarter. + */ + + /* CogMethodZone>>#freeOlderMethodsForCompaction */ +static void +freeOlderMethodsForCompaction(void) +{ + sqInt amountToFree; + CogMethod *cogMethod; + sqInt freeableUsage; + sqInt freedSoFar; + sqInt initialFreeSpace; + sqInt zoneSize; + + zoneSize = limitAddress - baseAddress; + initialFreeSpace = (limitAddress - mzFreeStart) + methodBytesFreedSinceLastCompaction; + freedSoFar = initialFreeSpace; + + /* 4 needs to be e.g. a start-up parameter */ + amountToFree = zoneSize / 4; + freeableUsage = 0; + do { + cogMethod = ((CogMethod *) baseAddress); + while (((((usqInt)cogMethod)) < mzFreeStart) + && (freedSoFar < amountToFree)) { + if (/* shouldFreeMethod:given: */ + (!(((cogMethod->cmType)) == CMFree)) + && (((cogMethod->cmUsageCount)) <= freeableUsage)) { + freeMethod(cogMethod); + freedSoFar += (cogMethod->blockSize); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + } while((freedSoFar < amountToFree) + && (((freeableUsage += 1)) < CMMaxUsageCount)); +} + + +/* Answer that all entries in youngReferrers are in-use and have the + cmRefersToYoung flag set. + Used to check that the youngreferrers pruning routines work correctly. */ + + /* CogMethodZone>>#kosherYoungReferrers */ +sqInt +kosherYoungReferrers(void) +{ + CogMethod *cogMethod; + usqInt pointer; + CogMethod *prevMethod; + + if ((youngReferrers > limitAddress) + || (youngReferrers < mzFreeStart)) { + return 0; + } + pointer = youngReferrers; + while (pointer < limitAddress) { + cogMethod = ((CogMethod *) (longAt((void *)(pointer)))); + if (!(((cogMethod->cmType)) == CMFree)) { + if (!((cogMethod->cmRefersToYoung))) { + return 0; + } + if ((occurrencesInYoungReferrers(cogMethod)) != 1) { + return 0; + } + } + pointer += BytesPerWord; + } + cogMethod = ((CogMethod *) baseAddress); + while (cogMethod < (limitZony())) { + prevMethod = cogMethod; + if (!(((cogMethod->cmType)) == CMFree)) { + if ((occurrencesInYoungReferrers(cogMethod)) != (((cogMethod->cmRefersToYoung) + ? 1 + : 0))) { + return 0; + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + if (cogMethod == prevMethod) { + return 0; + } + } + return 1; +} + + /* CogMethodZone>>#methodFor: */ +CogMethod * +methodFor(void *address) +{ + CogMethod *cogMethod; + CogMethod *nextMethod; + + if ((((usqInt)address)) < (methodZoneBase)) { + return null; + } + cogMethod = ((CogMethod *) baseAddress); + + /* Speed up the search by finding the entry in the openPICList before mcpc. + Typically 2 to 3% of the zone's entities are openPICs. The openPICList is ordered high to low. */ + if (openPICList) { + nextMethod = openPICList; + while (!((!nextMethod) + || ((((usqInt)nextMethod)) < (((usqInt)address))))) { + nextMethod = ((CogMethod *) ((nextMethod->nextOpenPIC))); + } + if (nextMethod) { + cogMethod = nextMethod; + } + } + while ((cogMethod < (limitZony())) + && ((((usqInt)cogMethod)) <= (((usqInt)address)))) { + nextMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + if (nextMethod == cogMethod) { + return null; + } + if (((((usqInt)address)) >= (((usqInt)cogMethod))) + && ((((usqInt)address)) < (((usqInt)nextMethod)))) { + return cogMethod; + } + cogMethod = nextMethod; + } + return null; +} + + /* CogMethodZone>>#methodsCompiledToMachineCodeInto: */ +#if VMInvestigations +sqInt +methodsCompiledToMachineCodeInto(sqInt arrayObj) +{ + CogMethod *cogMethod1; + sqInt methodIndex; + + methodIndex = 0; + + /* begin methodsDo: */ + cogMethod1 = ((CogMethod *) baseAddress); + while (cogMethod1 < (limitZony())) { + if (!(((cogMethod1->cmType)) == CMFree)) { + if (((cogMethod1->cmType)) >= CMMethod) { + storePointerUncheckedofObjectwithValue(methodIndex, arrayObj, (cogMethod1->methodObject)); + methodIndex += 1; + } + } + cogMethod1 = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod1)) + ((cogMethod1->blockSize))))); + } + return methodIndex; +} +#endif /* VMInvestigations */ + + /* CogMethodZone>>#numMethods */ +sqInt +numMethods(void) +{ + return methodCount; +} + + /* CogMethodZone>>#numMethodsOfType: */ +sqInt +numMethodsOfType(sqInt cogMethodType) +{ + CogMethod *cogMethod; + sqInt n; + + n = 0; + cogMethod = ((CogMethod *) baseAddress); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) == cogMethodType) { + n += 1; + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + return n; +} + + /* CogMethodZone>>#occurrencesInYoungReferrers: */ +static NoDbgRegParms sqInt +occurrencesInYoungReferrers(CogMethod *cogMethod) +{ + sqInt count; + usqInt pointer; + + assert(youngReferrers <= limitAddress); + count = 0; + pointer = youngReferrers; + while (pointer < limitAddress) { + if ((((sqInt)cogMethod)) == (longAt((void *)(pointer)))) { + count += 1; + } + pointer += BytesPerWord; + } + return count; +} + + /* CogMethodZone>>#openPICWithSelector: */ +static NoDbgRegParms CogMethod * +openPICWithSelector(sqInt aSelector) +{ + CogMethod *openPIC; + + openPIC = openPICList; + do { + if ((openPIC == null) + || (((openPIC->selector)) == aSelector)) { + return openPIC; + } + + /* N.B. Use self rather than coInterpreter to avoid attempting to cast nil. + Conversion to CogMethod done in the nextOpenPIC accessor. */ + openPIC = ((CogMethod *) ((openPIC->nextOpenPIC))); + } while(1); + return 0; +} + + +/* Some methods have been freed. Compute how much each survivor needs to + move during the ensuing compaction and record it in the objectHeader + field. + For Sista, where we want PICs to last so they can be observed, we need to + keep PICs unless + they are definitely unused. So we need to identify unused PICs. So in + planCompact, zero the + usage counts of all PICs, saving the actual usage count in + blockEntryOffset. Then in + relocateMethodsPreCompaction (actually in + relocateIfCallOrMethodReference:mcpc:delta:) restore the usage counts of + used PICs. Finally in compactCompiledCode, clear the blockEntryOffset + of the unused PICs; they will then have a zero count and be reclaimed in + the next code compaction. */ + + /* CogMethodZone>>#planCompaction */ +static void +planCompaction(void) +{ + CogMethod *cogMethod; + sqInt delta; + + delta = 0; + cogMethod = ((CogMethod *) baseAddress); + while ((((usqInt)cogMethod)) < mzFreeStart) { + if (((cogMethod->cmType)) == CMFree) { + delta -= (cogMethod->blockSize); + } + else { + assert((cogMethodDoesntLookKosher(cogMethod)) == 0); + ((((CogMethod *) ((((usqInt)cogMethod)) + codeToDataDelta)))->objectHeader = delta); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } +} + + +/* useful for debugging */ + + /* CogMethodZone>>#printCogMethods */ +void +printCogMethods(void) +{ + CogMethod *cogMethod; + sqInt nb; + sqInt nc; + sqInt nf; + sqInt nm; + sqInt no; + sqInt nu; + + /* begin printCogMethodsSummarizing: */ + nm = (nb = (nc = (no = (nf = (nu = 0))))); + cogMethod = ((CogMethod *) baseAddress); + while (cogMethod < (limitZony())) { + printCogMethod(cogMethod); + switch ((cogMethod->cmType)) { + case CMFree: + nf += 1; + break; + case CMMethod: + nm += 1; + break; + case CMMethodFlaggedForBecome: + nb += 1; + break; + case CMClosedPIC: + nc += 1; + break; + case CMOpenPIC: + no += 1; + break; + default: + nu += 1; + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + print("CMMethod "); + printNum(nm); + if (nb > 0) { + print(" (flagged for become: "); + printNum(nb); + print(")"); + } + print(" CMClosedPIC "); + printNum(nc); + print(" CMOpenPIC "); + printNum(no); + print(" CMFree "); + printNum(nf); + if (nu > 0) { + print(" UNKNOWN "); + printNum(nu); + } + print(" total "); + printNum(((((nm + nc) + no) + nf) + nu) + nb); + cr(); +} + + +/* useful for debugging */ + + /* CogMethodZone>>#printCogMethodsOfType: */ +void +printCogMethodsOfType(sqInt cmType) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) baseAddress); + while (cogMethod < (limitZony())) { + if (((cogMethod->cmType)) == cmType) { + printCogMethod(cogMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } +} + + +/* useful for debugging */ + + /* CogMethodZone>>#printCogMethodsWithMethod: */ +void +printCogMethodsWithMethod(sqInt methodOop) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) baseAddress); + while (cogMethod < (limitZony())) { + if ((!(((cogMethod->cmType)) == CMFree)) + && (((cogMethod->methodObject)) == methodOop)) { + printCogMethod(cogMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } +} + + +/* useful for debugging */ + + /* CogMethodZone>>#printCogMethodsWithPrimitive: */ +void +printCogMethodsWithPrimitive(sqInt primIdx) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) baseAddress); + while (cogMethod < (limitZony())) { + if ((((cogMethod->cmType)) >= CMMethod) + && (primIdx == (primitiveIndexOfMethodheader((cogMethod->methodObject), (cogMethod->methodHeader))))) { + printCogMethod(cogMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } +} + + +/* useful for debugging */ + + /* CogMethodZone>>#printCogMethodsWithSelector: */ +void +printCogMethodsWithSelector(sqInt selectorOop) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) baseAddress); + while (cogMethod < (limitZony())) { + if ((!(((cogMethod->cmType)) == CMFree)) + && (((cogMethod->selector)) == selectorOop)) { + printCogMethod(cogMethod); + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } +} + + +/* useful for debugging */ + + /* CogMethodZone>>#printCogYoungReferrers */ +void +printCogYoungReferrers(void) +{ + CogMethod *cogMethod; + usqInt pointer; + + pointer = youngReferrers; + while (pointer < limitAddress) { + cogMethod = ((CogMethod *) (longAt((void *)(pointer)))); + if (!((cogMethod->cmRefersToYoung))) { + print("*"); + } + if (((cogMethod->cmType)) == CMFree) { + print("!"); + } + if (!(((cogMethod->cmRefersToYoung)) + && (!(((cogMethod->cmType)) == CMFree)))) { + print(" "); + } + printCogMethod(cogMethod); + pointer += BytesPerWord; + } +} + + +/* useful for debugging */ + + /* CogMethodZone>>#printOpenPICList */ +sqInt +printOpenPICList(void) +{ + sqInt n; + CogMethod *openPIC; + + /* begin printOpenPICListSummarizing: */ + n = 0; + openPIC = openPICList; + while (!(openPIC == null)) { + n += 1; + printCogMethod(openPIC); + openPIC = ((CogMethod *) ((openPIC->nextOpenPIC))); + } + return n; +} + + /* CogMethodZone>>#pruneYoungReferrers */ +static sqInt +pruneYoungReferrers(void) +{ + usqInt dest; + usqInt next; + usqInt source; + + next = 0; + assert(youngReferrers <= limitAddress); + if (youngReferrers == limitAddress) { + return null; + } + dest = limitAddress; + while (1) { + next = dest - BytesPerWord; + if (!((next >= youngReferrers) + && (((((CogMethod *) (longAt((void *)(next)))))->cmRefersToYoung)))) break; + dest = next; + } + assert(dest >= youngReferrers); + source = dest - BytesPerWord; + while (source >= youngReferrers) { + if (((((CogMethod *) (longAt((void *)(source)))))->cmRefersToYoung)) { + assert(source < (dest - BytesPerWord)); + if (next) { + next = null; + + /* begin ensureWritableCodeZone */ + } + + /* convenient first-time flag */ + codeLongAtput((void *)((dest -= BytesPerWord)),longAt((void *)(source))); + } + source -= BytesPerWord; + } + youngReferrers = dest; + assert(kosherYoungReferrers()); + return 0; +} + + /* CogMethodZone>>#relocateAndPruneYoungReferrers */ +static sqInt +relocateAndPruneYoungReferrers(void) +{ + CogMethod *cogMethod; + usqInt dest; + usqInt next; + usqInt source; + + cogMethod = ((CogMethod *) 0); + next = 0; + assert(youngReferrers <= limitAddress); + if (youngReferrers == limitAddress) { + return null; + } + dest = limitAddress; + while (1) { + next = dest - BytesPerWord; + if (!((next >= youngReferrers) + && ((!(isCMFree(((CogBlockMethod *) ((cogMethod = ((CogMethod *) (longAt((void *)(next)))))))))) + && ((cogMethod->cmRefersToYoung))))) break; + if ((cogMethod->objectHeader)) { + codeLongAtput((void *)(next),(((sqInt)cogMethod)) + ((cogMethod->objectHeader))); + } + dest = next; + } + assert(dest >= youngReferrers); + source = dest - BytesPerWord; + while (source >= youngReferrers) { + cogMethod = ((CogMethod *) (longAt((void *)(source)))); + if ((!(((cogMethod->cmType)) == CMFree)) + && ((cogMethod->cmRefersToYoung))) { + assert(source < (dest - BytesPerWord)); + if ((cogMethod->objectHeader)) { + cogMethod = ((CogMethod *) ((((sqInt)cogMethod)) + (((sqInt)((cogMethod->objectHeader)))))); + } + codeLongAtput((void *)((dest -= BytesPerWord)),((sqInt)cogMethod)); + } + source -= BytesPerWord; + } + + /* this assert must be deferred until after compaction. See the end of compactCogCompiledCode */ + /* self assert: self kosherYoungReferrers */ + youngReferrers = dest; + return 0; +} + + +/* All surviving methods have had the amount they are going to relocate by + stored in their objectHeader fields. Relocate all relative calls so that + after the compaction of both the method containing each call and the call + target the calls invoke the same target. */ + + /* CogMethodZone>>#relocateMethodsPreCompaction */ +static sqInt +relocateMethodsPreCompaction(void) +{ + CogMethod *cogMethod; + + cogMethod = ((CogMethod *) baseAddress); + while ((((usqInt)cogMethod)) < mzFreeStart) { + if (!(((cogMethod->cmType)) == CMFree)) { + if (((cogMethod->cmType)) == CMClosedPIC) { + relocateCallsInClosedPIC(cogMethod); + } + else { + relocateCallsAndSelfReferencesInMethod(cogMethod); + } + } + cogMethod = ((CogMethod *) (roundUpToMethodAlignment(backEnd, (((usqInt)cogMethod)) + ((cogMethod->blockSize))))); + } + relocateAndPruneYoungReferrers(); + return 1; +} + + /* CogMethodZone>>#removeFromOpenPICList: */ +static NoDbgRegParms sqInt +removeFromOpenPICList(CogMethod *anOpenPIC) +{ + CogMethod *prevPIC; + + assert(isCMOpenPIC(((CogBlockMethod *) anOpenPIC))); + if (!openPICList) { + return null; + } + assert((isCMOpenPIC(((CogBlockMethod *) openPICList))) + && ((!((openPICList->nextOpenPIC))) + || (isCMOpenPIC(((CogBlockMethod *) (((CogMethod *) ((openPICList->nextOpenPIC))))))))); + if (anOpenPIC == openPICList) { + /* N.B. Use self rather than coInterpreter to avoid attempting to cast nil. + Conversion to CogMethod done in the nextOpenPIC accessor. */ + openPICList = ((CogMethod *) ((anOpenPIC->nextOpenPIC))); + return null; + } + prevPIC = openPICList; + do { + assert((prevPIC != null) + && (isCMOpenPIC(((CogBlockMethod *) prevPIC)))); + if (((prevPIC->nextOpenPIC)) == (((usqInt)anOpenPIC))) { + ((((CogMethod *) ((((usqInt)prevPIC)) + codeToDataDelta)))->nextOpenPIC = (anOpenPIC->nextOpenPIC)); + return null; + } + prevPIC = ((CogMethod *) ((prevPIC->nextOpenPIC))); + } while(1); + return 0; +} + + +/* Determine the default alignment for the start of a CogMethod, which in + turn determines the size of the mask used to distinguish the checked and + unchecked entry-points, used to distinguish normal and super sends on + method unlinking. + This is passed onto the backEnd to allow processors with coarse + instructions (ARM) to increase the alignment if required. */ + + /* CogMethodZone>>#roundUpLength: */ +static NoDbgRegParms int +roundUpLength(sqInt numBytes) +{ + return roundUpToMethodAlignment(backEnd, numBytes); +} + + /* CogMethodZone>>#voidOpenPICList */ +static void +voidOpenPICList(void) +{ + openPICList = null; +} + + /* CogMethodZone>>#voidUnpairedMethodList */ +static void +voidUnpairedMethodList(void) +{ +} + + /* CogMethodZone>>#whereIsMaybeCodeThing: */ +char * +whereIsMaybeCodeThing(sqInt anOop) +{ + if (oopisGreaterThanOrEqualToandLessThan(anOop, codeBase, limitAddress)) { + if (oopisLessThan(anOop, minCogMethodAddress())) { + return " is in generated runtime"; + } + if (oopisLessThan(anOop, mzFreeStart)) { + return " is in generated methods"; + } + if (oopisLessThan(anOop, youngReferrers)) { + return " is in code zone"; + } + return " is in young referrers"; + } + return null; +} + + /* CogMethodZone>>#zoneAlignment */ +static sqInt +zoneAlignment(void) +{ + return 8; +} + + /* CogObjectRepresentation>>#checkValidObjectReference: */ +static NoDbgRegParms sqInt +checkValidObjectReference(sqInt anOop) +{ + return (!(isImmediate(anOop))) + && ((heapMapAtWord(pointerForOop(anOop))) != 0); +} + + /* CogObjectRepresentation>>#genCmpClassFloatCompactIndexR: */ +static NoDbgRegParms AbstractInstruction * +genCmpClassFloatCompactIndexR(sqInt reg) +{ + return genoperandoperand(CmpCqR, ClassFloatCompactIndex, reg); +} + + /* CogObjectRepresentation>>#genCmpClassMethodContextCompactIndexR: */ +static NoDbgRegParms AbstractInstruction * +genCmpClassMethodContextCompactIndexR(sqInt reg) +{ + return genoperandoperand(CmpCqR, ClassMethodContextCompactIndex, reg); +} + + +/* Get the method header (first word) of a CompiledMethod into headerReg. + Deal with the method possibly being cogged. */ + + /* CogObjectRepresentation>>#genGetMethodHeaderOf:into:scratch: */ +static NoDbgRegParms sqInt +genGetMethodHeaderOfintoscratch(sqInt methodReg, sqInt headerReg, sqInt scratchReg) +{ + AbstractInstruction *jumpNotCogged; + sqInt offset; + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BaseHeaderSize, methodReg, headerReg); + jumpNotCogged = genJumpSmallInteger(headerReg); + offset = offsetof(CogMethod, methodHeader); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, headerReg, headerReg); + jmpTarget(jumpNotCogged, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + +/* Generate a compare and branch to test if aRegister and bRegister contains + other than SmallIntegers, + i.e. don't branch if both aRegister and bRegister contain SmallIntegers. + Answer the jump. Destroy scratchRegister if required. */ + + /* CogObjectRepresentation>>#genJumpNotSmallIntegersIn:and:scratch: */ +static NoDbgRegParms AbstractInstruction * +genJumpNotSmallIntegersInandscratch(sqInt aRegister, sqInt bRegister, sqInt scratchRegister) +{ + /* MoveR:R: */ + genoperandoperand(MoveRR, aRegister, scratchRegister); + + /* AndR:R: */ + genoperandoperand(AndRR, bRegister, scratchRegister); + return genJumpNotSmallInteger(scratchRegister); +} + + /* CogObjectRepresentation>>#genLoadSlot:sourceReg:destReg: */ +static NoDbgRegParms sqInt +genLoadSlotsourceRegdestReg(sqInt index, sqInt sourceReg, sqInt destReg) +{ + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, (index * BytesPerWord) + BaseHeaderSize, sourceReg, destReg); + return 0; +} + + /* CogObjectRepresentation>>#genPrimitiveAdd */ +static sqInt +genPrimitiveAdd(void) +{ + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpOvfl; + + if (!(mclassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ClassReg); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + genRemoveSmallIntegerTagsInScratchReg(ClassReg); + + /* AddR:R: */ + genoperandoperand(AddRR, ReceiverResultReg, ClassReg); + jumpOvfl = genConditionalBranchoperand(JumpOverflow, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpOvfl, jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveBitAnd */ +static sqInt +genPrimitiveBitAnd(void) +{ + AbstractInstruction *jumpNotSI; + + if (!(mclassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + + /* Whether the SmallInteger tags are zero or non-zero, anding them together will preserve them. */ + + /* AndR:R: */ + genoperandoperand(AndRR, Arg0Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveBitOr */ +static sqInt +genPrimitiveBitOr(void) +{ + AbstractInstruction *jumpNotSI; + + if (!(mclassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + + /* Whether the SmallInteger tags are zero or non-zero, oring them together will preserve them. */ + + /* OrR:R: */ + genoperandoperand(OrRR, Arg0Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + +/* rTemp := rArg0 + rClass := tTemp + rTemp := rTemp & 1 + jz nonInt + rClass >>= 1 + cmp 0,rClass + jge neg + cmp 31,rClass // numSmallIntegerBits, jge for sign + jge tooBig + rTemp := rReceiver + rTemp <<= rClass + rTemp >>= rClass (arithmetic) + cmp rTemp,rReceiver + jnz ovfl + rReceiver := rReceiver - 1 + rReceiver := rReceiver <<= rClass + rReceiver := rReceiver + 1 + ret + neg: + rClass := 0 - rClass + cmp 31,rClass // numSmallIntegerBits + jge inRange + rClass := 31 + inRange + rReceiver := rReceiver >>= rClass. + rReceiver := rReceiver | smallIntegerTags. + ret + ovfl + tooBig + nonInt: + fail + */ + + /* CogObjectRepresentation>>#genPrimitiveBitShift */ +static sqInt +genPrimitiveBitShift(void) +{ + AbstractInstruction *jumpInRange; + AbstractInstruction *jumpNegative; + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpOvfl; + AbstractInstruction *jumpTooBig; + + if (!(mclassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ClassReg); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + genConvertSmallIntegerToIntegerInReg(ClassReg); + if (!(setsConditionCodesFor(lastOpcode(), JumpNegative))) { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ClassReg); + } + jumpNegative = genConditionalBranchoperand(JumpNegative, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, numSmallIntegerBits(), ClassReg); + jumpTooBig = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, TempReg); + + /* LogicalShiftLeftR:R: */ + genoperandoperand(LogicalShiftLeftRR, ClassReg, TempReg); + + /* ArithmeticShiftRightR:R: */ + genoperandoperand(ArithmeticShiftRightRR, ClassReg, TempReg); + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, ReceiverResultReg); + jumpOvfl = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genRemoveSmallIntegerTagsInScratchReg(ReceiverResultReg); + + /* LogicalShiftLeftR:R: */ + genoperandoperand(LogicalShiftLeftRR, ClassReg, ReceiverResultReg); + genAddSmallIntegerTagsTo(ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNegative, genoperand(NegateR, ClassReg)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, numSmallIntegerBits(), ClassReg); + jumpInRange = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, numSmallIntegerBits(), ClassReg); + jmpTarget(jumpInRange, genoperandoperand(ArithmeticShiftRightRR, ClassReg, ReceiverResultReg)); + genClearAndSetSmallIntegerTagsIn(ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSI, jmpTarget(jumpTooBig, jmpTarget(jumpOvfl, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveBitXor */ +static sqInt +genPrimitiveBitXor(void) +{ + AbstractInstruction *jumpNotSI; + + if (!(mclassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + + /* Clear one or the other tag so that xoring will preserve them. */ + genRemoveSmallIntegerTagsInScratchReg(Arg0Reg); + + /* XorR:R: */ + genoperandoperand(XorRR, Arg0Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveClass */ +static sqInt +genPrimitiveClass(void) +{ + sqInt reg; + + reg = ReceiverResultReg; + if (methodOrBlockNumArgs > 0) { + if (methodOrBlockNumArgs > 1) { + return UnimplementedPrimitive; + } + reg = Arg0Reg; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + } + if ((genGetClassObjectOfintoscratchRegmayBeAForwarder(reg, ReceiverResultReg, TempReg, reg != ReceiverResultReg)) == BadRegisterSet) { + genGetClassObjectOfintoscratchRegmayBeAForwarder(reg, ClassReg, TempReg, reg != ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, ReceiverResultReg); + } + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + return UnfailingPrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveDiv */ +static sqInt +genPrimitiveDiv(void) +{ + AbstractInstruction *convert; + AbstractInstruction *jumpExact; + AbstractInstruction *jumpIsSI; + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpSameSign; + AbstractInstruction *jumpZero; + + if (!(processorHasDivQuoRemAndMClassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ClassReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, Arg1Reg); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + + /* We must shift away the tags, not just subtract them, so that the + overflow case doesn't actually overflow the machine instruction. */ + genShiftAwaySmallIntegerTagsInScratchReg(ClassReg); + if (!(setsConditionCodesFor(lastOpcode(), JumpZero))) { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ClassReg); + } + jumpZero = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, TempReg); + genShiftAwaySmallIntegerTagsInScratchReg(TempReg); + gDivRRQuoRem(ClassReg, TempReg, TempReg, ClassReg); + + /* If remainder is zero we must check for overflow. */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ClassReg); + jumpExact = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* If arg and remainder signs are different we must round down. */ + + /* XorR:R: */ + genoperandoperand(XorRR, ClassReg, Arg1Reg); + if (!(setsConditionCodesFor(lastOpcode(), JumpZero))) { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, Arg1Reg); + } + jumpSameSign = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 1, TempReg); + jmpTarget(jumpSameSign, (convert = genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + genConvertIntegerInRegtoSmallIntegerInReg(TempReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* test for overflow; the only case is SmallInteger minVal // -1 */ + jmpTarget(jumpExact, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpIsSI = genJumpIsSmallIntegerValuescratch(TempReg, Arg1Reg); + jmpTarget(jumpIsSI, convert); + jmpTarget(jumpZero, jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveDivide */ +static sqInt +genPrimitiveDivide(void) +{ + AbstractInstruction *jumpInexact; + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpOverflow; + AbstractInstruction *jumpZero; + + if (!(processorHasDivQuoRemAndMClassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ClassReg); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + + /* We must shift away the tags, not just subtract them, so that the + overflow case doesn't actually overflow the machine instruction. */ + genShiftAwaySmallIntegerTagsInScratchReg(ClassReg); + jumpZero = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, TempReg); + genShiftAwaySmallIntegerTagsInScratchReg(TempReg); + gDivRRQuoRem(ClassReg, TempReg, TempReg, ClassReg); + + /* If remainder is non-zero fail. */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ClassReg); + jumpInexact = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* test for overflow; the only case is SmallInteger minVal / -1 */ + jumpOverflow = genJumpNotSmallIntegerValuescratch(TempReg, Arg1Reg); + genConvertIntegerInRegtoSmallIntegerInReg(TempReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpOverflow, jmpTarget(jumpInexact, jmpTarget(jumpZero, jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveEqual */ +static sqInt +genPrimitiveEqual(void) +{ + return (numericPrimsMixComparison() + ? genSmallIntegerComparisonorDoubleComparisoninvert(JumpZero, gJumpFPEqual, 0) + : genSmallIntegerComparison(JumpZero)); +} + + /* CogObjectRepresentation>>#genPrimitiveFloatAdd */ +static sqInt +genPrimitiveFloatAdd(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(AddRdRd, null, 1) + : genPureFloatArithmeticpreOpCheckboxed(AddRdRd, null, 1)); +} + + /* CogObjectRepresentation>>#genPrimitiveFloatDivide */ +static sqInt +genPrimitiveFloatDivide(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(DivRdRd, genDoubleFailIfZeroArgRcvrarg, 1) + : genPureFloatArithmeticpreOpCheckboxed(DivRdRd, genDoubleFailIfZeroArgRcvrarg, 1)); +} + + /* CogObjectRepresentation>>#genPrimitiveFloatMultiply */ +static sqInt +genPrimitiveFloatMultiply(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(MulRdRd, null, 1) + : genPureFloatArithmeticpreOpCheckboxed(MulRdRd, null, 1)); +} + + /* CogObjectRepresentation>>#genPrimitiveFloatSubtract */ +static sqInt +genPrimitiveFloatSubtract(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(SubRdRd, null, 1) + : genPureFloatArithmeticpreOpCheckboxed(SubRdRd, null, 1)); +} + + /* CogObjectRepresentation>>#genPrimitiveGreaterOrEqual */ +static sqInt +genPrimitiveGreaterOrEqual(void) +{ + return (numericPrimsMixComparison() + ? genSmallIntegerComparisonorDoubleComparisoninvert(JumpGreaterOrEqual, gJumpFPGreaterOrEqual, 0) + : genSmallIntegerComparison(JumpGreaterOrEqual)); +} + + /* CogObjectRepresentation>>#genPrimitiveGreaterThan */ +static sqInt +genPrimitiveGreaterThan(void) +{ + return (numericPrimsMixComparison() + ? genSmallIntegerComparisonorDoubleComparisoninvert(JumpGreater, gJumpFPGreater, 0) + : genSmallIntegerComparison(JumpGreater)); +} + + +/* Defer to the backEnd to generate the best instruction sequence. + Some backends have an efficient count-leading-zeros that must + be inverted to produce the high bit, others have a genuine high + bit. Intel has both, the former only in newer revisions. + Use TempReg. If primitive fails, ReceiverResultReg must remain + unchanged (we CompletePrimitive) */ + + /* CogObjectRepresentation>>#genPrimitiveHighBit */ +static sqInt +genPrimitiveHighBit(void) +{ + AbstractInstruction *jumpNegativeReceiver; + sqInt quickConstant; + AbstractInstruction *self_in_CogX64Compiler; + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ReceiverResultReg); + jumpNegativeReceiver = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* remove excess tag bits from the receiver oop */ + gOrCqRR((1U << (numSmallIntegerTagBits())) - 1, ReceiverResultReg, ClassReg); + quickConstant = (numSmallIntegerTagBits()) - 1; + + /* begin ArithmeticShiftRightCq:R: */ + genoperandoperand(ArithmeticShiftRightCqR, quickConstant, ClassReg); + self_in_CogX64Compiler = ((AbstractInstruction *) (backEnd)); + + /* begin genHighBitOfNonZeroReg:into: */ + if ((((cpuidWord1(self_in_CogX64Compiler)) & (32)) != 0)) { + if (!((((cpuidWord1(self_in_CogX64Compiler)) & (32)) != 0))) { + return UnimplementedPrimitive; + } + + /* ClzR:R: */ + genoperandoperand(ClzRR, ClassReg, TempReg); + + /* Invert leading zeroas into highBit. + Note the nice bit trick below: + highBit_1based_of_small_int_value = (BytesPerWord * 8) - leadingZeroCout_of_oop - 1 toAccountForTagBit. + This is like 2 complements (- reg - 1) on (BytesPerWord * 8) log2 bits, or exactly a bit invert operation... */ + + /* #XorCq:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(XorCqR, (BytesPerWord * 8) - 1, TempReg); + goto l1; + } + genoperandoperand(BSR, ClassReg, TempReg); + goto l1; +l1: + genConvertIntegerInRegtoSmallIntegerInReg(TempReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNegativeReceiver, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveIdentical */ +static sqInt +genPrimitiveIdentical(void) +{ + return genPrimitiveIdenticalOrNotIf(0); +} + + /* CogObjectRepresentation>>#genPrimitiveLessOrEqual */ +static sqInt +genPrimitiveLessOrEqual(void) +{ + return (numericPrimsMixComparison() + ? genSmallIntegerComparisonorDoubleComparisoninvert(JumpLessOrEqual, gJumpFPGreaterOrEqual, 1) + : genSmallIntegerComparison(JumpLessOrEqual)); +} + + /* CogObjectRepresentation>>#genPrimitiveLessThan */ +static sqInt +genPrimitiveLessThan(void) +{ + return (numericPrimsMixComparison() + ? genSmallIntegerComparisonorDoubleComparisoninvert(JumpLess, gJumpFPGreater, 1) + : genSmallIntegerComparison(JumpLess)); +} + + /* CogObjectRepresentation>>#genPrimitiveMaxIdentityHash */ +static sqInt +genPrimitiveMaxIdentityHash(void) +{ + sqInt quickConstant; + + quickConstant = (((usqInt)(maxIdentityHash()) << 3) | 1); + + /* begin MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, quickConstant, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + return UnfailingPrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveMod */ +static sqInt +genPrimitiveMod(void) +{ + AbstractInstruction *jumpExact; + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpSameSign; + AbstractInstruction *jumpZero; + + if (!(processorHasDivQuoRemAndMClassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ClassReg); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + genRemoveSmallIntegerTagsInScratchReg(ClassReg); + jumpZero = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, Arg1Reg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, TempReg); + genRemoveSmallIntegerTagsInScratchReg(TempReg); + gDivRRQuoRem(ClassReg, TempReg, TempReg, ClassReg); + + /* If remainder is zero we're done. */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ClassReg); + jumpExact = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* If arg and remainder signs are different we must reflect around zero. */ + + /* XorR:R: */ + genoperandoperand(XorRR, ClassReg, Arg1Reg); + if (!(setsConditionCodesFor(lastOpcode(), JumpZero))) { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, Arg1Reg); + } + jumpSameSign = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* XorR:R: */ + genoperandoperand(XorRR, ClassReg, Arg1Reg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg1Reg, ClassReg); + jmpTarget(jumpSameSign, jmpTarget(jumpExact, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + genSetSmallIntegerTagsIn(ClassReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpZero, jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveMultiply */ +static sqInt +genPrimitiveMultiply(void) +{ + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpOvfl; + + if (!(processorHasMultiplyAndMClassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ClassReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, Arg1Reg); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + genShiftAwaySmallIntegerTagsInScratchReg(ClassReg); + genRemoveSmallIntegerTagsInScratchReg(Arg1Reg); + + /* MulOverflowR:R: */ + genMulRR(backEnd, Arg1Reg, ClassReg); + jumpOvfl = genConditionalBranchoperand(JumpOverflow, ((sqInt)0)); + genSetSmallIntegerTagsIn(ClassReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpOvfl, jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + +/* subclasses override if they can */ + + /* CogObjectRepresentation>>#genPrimitiveNewMethod */ +static sqInt +genPrimitiveNewMethod(void) +{ + return UnimplementedPrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveNotEqual */ +static sqInt +genPrimitiveNotEqual(void) +{ + return (numericPrimsMixComparison() + ? genSmallIntegerComparisonorDoubleComparisoninvert(JumpNonZero, gJumpFPNotEqual, 0) + : genSmallIntegerComparison(JumpNonZero)); +} + + /* CogObjectRepresentation>>#genPrimitiveNotIdentical */ +static sqInt +genPrimitiveNotIdentical(void) +{ + return genPrimitiveIdenticalOrNotIf(1); +} + + /* CogObjectRepresentation>>#genPrimitiveQuo */ +static sqInt +genPrimitiveQuo(void) +{ + AbstractInstruction *convert; + AbstractInstruction *jumpExact; + AbstractInstruction *jumpIsSI; + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpZero; + + if (!(processorHasDivQuoRemAndMClassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ClassReg); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + + /* We must shift away the tags, not just subtract them, so that the + overflow case doesn't actually overflow the machine instruction. */ + genShiftAwaySmallIntegerTagsInScratchReg(ClassReg); + if (!(setsConditionCodesFor(lastOpcode(), JumpZero))) { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ClassReg); + } + jumpZero = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, TempReg); + genShiftAwaySmallIntegerTagsInScratchReg(TempReg); + gDivRRQuoRem(ClassReg, TempReg, TempReg, ClassReg); + + /* If remainder is zero we must check for overflow. */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ClassReg); + jumpExact = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + convert = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + genConvertIntegerInRegtoSmallIntegerInReg(TempReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpExact, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpIsSI = genJumpIsSmallIntegerValuescratch(TempReg, Arg1Reg); + jmpTarget(jumpIsSI, convert); + jmpTarget(jumpZero, jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genPrimitiveSubtract */ +static sqInt +genPrimitiveSubtract(void) +{ + AbstractInstruction *jumpNotSI; + AbstractInstruction *jumpOvfl; + + if (!(mclassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpNotSI = genJumpNotSmallInteger(Arg0Reg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, Arg0Reg, TempReg); + jumpOvfl = genConditionalBranchoperand(JumpOverflow, ((sqInt)0)); + genAddSmallIntegerTagsTo(TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpOvfl, jmpTarget(jumpNotSI, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#genSmallIntegerComparison: */ +static NoDbgRegParms sqInt +genSmallIntegerComparison(sqInt jumpOpcode) +{ + AbstractInstruction *jumpFail; + AbstractInstruction *jumpTrue; + + if (!(mclassIsSmallInteger())) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpFail = genJumpNotSmallInteger(Arg0Reg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, ReceiverResultReg); + jumpTrue = genConditionalBranchoperand(jumpOpcode, 0); + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpTrue, /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, trueObject(), ReceiverResultReg)); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpFail, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + /* CogObjectRepresentation>>#isUnannotatableConstant: */ +static NoDbgRegParms sqInt +isUnannotatableConstant(SimStackEntry *simStackEntry) +{ + return (((simStackEntry->type)) == SSConstant) + && ((isImmediate((simStackEntry->constant))) + || (!(/* shouldAnnotateObjectReference: */ + (isNonImmediate((simStackEntry->constant))) + && ((oopisGreaterThan((simStackEntry->constant), classTableRootObj())) + || (oopisLessThan((simStackEntry->constant), nilObject())))))); +} + + /* CogObjectRepresentationFor64BitSpur>>#classForInlineCacheTag: */ +static NoDbgRegParms sqInt +classForInlineCacheTag(sqInt classIndex) +{ + return classOrNilAtIndex(classIndex); +} + + /* CogObjectRepresentationFor64BitSpur>>#genAddSmallIntegerTagsTo: */ +static NoDbgRegParms sqInt +genAddSmallIntegerTagsTo(sqInt aRegister) +{ + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, aRegister); + return 0; +} + + +/* Set the SmallInteger tag bits when the tag bits may be filled with + garbage. + */ + + /* CogObjectRepresentationFor64BitSpur>>#genClearAndSetSmallIntegerTagsIn: */ +static NoDbgRegParms sqInt +genClearAndSetSmallIntegerTagsIn(sqInt scratchReg) +{ + sqInt quickConstant; + + quickConstant = -1 - (tagMask()); + + /* begin AndCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, scratchReg); + + /* #OrCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(OrCqR, 1, scratchReg); + return 0; +} + + +/* Convert the in-SmallFloat64-range floating point value in integer register + into a tagged SmallFloat64 oop. + c.f. Spur64BitMemoryManager>>smallFloatObjectOf: */ + + /* CogObjectRepresentationFor64BitSpur>>#genConvertBitsToSmallFloatIn:scratch: */ +static NoDbgRegParms sqInt +genConvertBitsToSmallFloatInscratch(sqInt reg, sqInt scratch) +{ + AbstractInstruction *jumpZero; + sqInt quickConstant; + + /* RotateLeftCq:R: */ + genoperandoperand(RotateLeftCqR, 1, reg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 1, reg); + jumpZero = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + quickConstant = ((sqInt)((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + + /* begin SubCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, quickConstant, reg); + jmpTarget(jumpZero, genoperandoperand(LogicalShiftLeftCqR, numTagBits(), reg)); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, smallFloatTag(), reg); + return 0; +} + + +/* Convert the Character in reg to a SmallInteger, assuming + the Character's value is a valid character. */ + + /* CogObjectRepresentationFor64BitSpur>>#genConvertCharacterToSmallIntegerInReg: */ +static NoDbgRegParms void +genConvertCharacterToSmallIntegerInReg(sqInt reg) +{ + sqInt quickConstant; + + quickConstant = (characterTag()) - (smallIntegerTag()); + + /* begin SubCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, quickConstant, reg); +} + + /* CogObjectRepresentationFor64BitSpur>>#genConvertIntegerInReg:toSmallIntegerInReg: */ +static NoDbgRegParms sqInt +genConvertIntegerInRegtoSmallIntegerInReg(sqInt srcReg, sqInt destReg) +{ + gLogicalShiftLeftCqRR(numTagBits(), srcReg, destReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, destReg); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genConvertIntegerToSmallIntegerInReg: */ +static NoDbgRegParms sqInt +genConvertIntegerToSmallIntegerInReg(sqInt reg) +{ + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, numTagBits(), reg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, reg); + return 0; +} + + +/* Convert the SmallFloat in reg to its identityHash as a SmallInteger. + Rotate the sign bit from bit 3 (zero-relative) to the sign bit. + c.f. Spur64BitMemoryManager>>rotatedFloatBitsOf: */ + + /* CogObjectRepresentationFor64BitSpur>>#genConvertSmallFloatToSmallFloatHashAsIntegerInReg:scratch: */ +static NoDbgRegParms sqInt +genConvertSmallFloatToSmallFloatHashAsIntegerInRegscratch(sqInt reg, sqInt scratch) +{ + sqInt quickConstant; + + assert(((((usqInt)((smallFloatTag()))) >> 1) - (smallIntegerTag())) == (smallIntegerTag())); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, 1, reg); + gAndCqRR(1U << ((numTagBits()) - 1), reg, scratch); + + /* SubR:R: */ + genoperandoperand(SubRR, scratch, reg); + quickConstant = (((usqInt)((smallFloatTag()))) >> 1) - (smallIntegerTag()); + + /* begin SubCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, quickConstant, reg); + quickConstant = 0x3F - ((numTagBits()) - 1); + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, quickConstant, scratch); + + /* OrR:R: */ + genoperandoperand(OrRR, scratch, reg); + return 0; +} + + +/* Convert the SmallInteger in reg to a Character, assuming + the SmallInteger's value is a valid character. */ + + /* CogObjectRepresentationFor64BitSpur>>#genConvertSmallIntegerToCharacterInReg: */ +static NoDbgRegParms void +genConvertSmallIntegerToCharacterInReg(sqInt reg) +{ + sqInt quickConstant; + + quickConstant = (characterTag()) - (smallIntegerTag()); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, quickConstant, reg); +} + + /* CogObjectRepresentationFor64BitSpur>>#genConvertSmallIntegerToIntegerInReg: */ +static NoDbgRegParms sqInt +genConvertSmallIntegerToIntegerInReg(sqInt reg) +{ + /* ArithmeticShiftRightCq:R: */ + genoperandoperand(ArithmeticShiftRightCqR, numTagBits(), reg); + return 0; +} + + +/* The arguments are in an array in Arg1Reg. Its size is in sizeReg. + Load Arg0Reg and Arg1Reg with the first two slots, as appropriate. + Since objects always have at least one slot it is safe to load arg0 + without checking. + But the array could be at the end of memory so we must check that it has + two slots before it is safe to access the second slot. */ + + /* CogObjectRepresentationFor64BitSpur>>#genFetchRegArgsForPerformWithArguments: */ +static NoDbgRegParms sqInt +genFetchRegArgsForPerformWithArguments(sqInt sizeReg) +{ + AbstractInstruction *skip; + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BaseHeaderSize, Arg1Reg, Arg0Reg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 2, sizeReg); + skip = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BaseHeaderSize + BytesPerWord, Arg1Reg, Arg1Reg); + jmpTarget(skip, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genFloatArithmetic:preOpCheck:boxed: */ +static NoDbgRegParms sqInt +genFloatArithmeticpreOpCheckboxed(sqInt arithmeticOperator, AbstractInstruction *(*preOpCheckOrNil)(int rcvrReg, int argReg), sqInt rcvrBoxed) +{ + AbstractInstruction *doOp; + AbstractInstruction *jumpFailCheck; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpNotBoxedFloat; + AbstractInstruction *jumpNotSmallFloat; + AbstractInstruction *jumpNotSmallInteger; + + jumpFailCheck = ((AbstractInstruction *) 0); + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + if (rcvrBoxed) { + genGetDoubleValueOfinto(ReceiverResultReg, DPFPReg0); + } + else { + genGetSmallFloatValueOfscratchinto(ReceiverResultReg, TempReg, DPFPReg0); + } + jumpNotSmallFloat = genJumpNotSmallFloat(Arg0Reg); + genGetSmallFloatValueOfscratchinto(Arg0Reg, TempReg, DPFPReg1); + doOp = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + if (preOpCheckOrNil) { + jumpFailCheck = preOpCheckOrNil(DPFPReg0, DPFPReg1); + } + genoperandoperand(arithmeticOperator, DPFPReg1, DPFPReg0); + genInstantiateFloatValueintoscratchRegscratchRegmayFail(DPFPReg0, ReceiverResultReg, ClassReg, TempReg, 0); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSmallFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpNotSmallInteger = genJumpNotSmallInteger(Arg0Reg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, Arg1Reg); + genConvertSmallIntegerToIntegerInReg(Arg1Reg); + + /* ConvertR:Rd: */ + genoperandoperand(ConvertRRd, Arg1Reg, DPFPReg1); + + /* Jump: */ + genoperand(Jump, ((sqInt)doOp)); + jmpTarget(jumpNotSmallInteger, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpImmediate = genJumpImmediate(Arg0Reg); + genGetCompactClassIndexNonImmOfinto(Arg0Reg, SendNumArgsReg); + genCmpClassFloatCompactIndexR(SendNumArgsReg); + jumpNotBoxedFloat = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genGetDoubleValueOfinto(Arg0Reg, DPFPReg1); + + /* Jump: */ + genoperand(Jump, ((sqInt)doOp)); + jmpTarget(jumpImmediate, jmpTarget(jumpNotBoxedFloat, jmpTarget(jumpNotSmallInteger, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + if (preOpCheckOrNil) { + jmpTarget(jumpFailCheck, ((AbstractInstruction *) (((jumpNotSmallInteger->operands))[0]))); + } + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genFloatComparison:orIntegerComparison:invert:boxed: */ +static NoDbgRegParms sqInt +genFloatComparisonorIntegerComparisoninvertboxed(AbstractInstruction *(*jumpFPOpcodeGenerator)(void *), sqInt jumpOpcode, sqInt invertComparison, sqInt rcvrBoxed) +{ + AbstractInstruction *compareFloat; + AbstractInstruction *jumpAmbiguous; + AbstractInstruction *jumpCond; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpNotBoxedFloat; + AbstractInstruction *jumpNotSmallFloat; + AbstractInstruction *jumpNotSmallInteger; + AbstractInstruction *jumpTrue; + AbstractInstruction *returnTrue; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + if (rcvrBoxed) { + genGetDoubleValueOfinto(ReceiverResultReg, DPFPReg0); + } + else { + genGetSmallFloatValueOfscratchinto(ReceiverResultReg, TempReg, DPFPReg0); + } + jumpNotSmallFloat = genJumpNotSmallFloat(Arg0Reg); + genGetSmallFloatValueOfscratchinto(Arg0Reg, TempReg, DPFPReg1); + compareFloat = (invertComparison + ? genoperandoperand(CmpRdRd, DPFPReg0, DPFPReg1) + : genoperandoperand(CmpRdRd, DPFPReg1, DPFPReg0)); + + /* May need to invert for NaNs */ + + /* FP jumps are a little weird */ + jumpCond = jumpFPOpcodeGenerator(0); + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpCond, (returnTrue = /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, trueObject(), ReceiverResultReg))); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSmallFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpNotSmallInteger = genJumpNotSmallInteger(Arg0Reg); + + /* Test for ambiguity, that is when floatRcvr == (double) intArg */ + genConvertSmallIntegerToIntegerInReg(Arg0Reg); + + /* ConvertR:Rd: */ + genoperandoperand(ConvertRRd, Arg0Reg, DPFPReg1); + + /* CmpRd:Rd: */ + genoperandoperand(CmpRdRd, DPFPReg0, DPFPReg1); + + /* If floatRcvr == (double) intArg then use compareInt(intArg,(int64) floatRcvr) + else use compareFloat(floatRcvr,(double) intArg) */ + jumpAmbiguous = gJumpFPEqual(0); + + /* Case of non ambiguity, use compareFloat(floatRcvr,(double) intArg) */ + + /* Jump: */ + genoperand(Jump, ((sqInt)compareFloat)); + + /* Case of ambiguity, use compareInt((int64) floatRcvr, intArg) */ + jmpTarget(jumpAmbiguous, genoperandoperand(ConvertRdR, DPFPReg0, ReceiverResultReg)); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, ReceiverResultReg); + jumpTrue = genConditionalBranchoperand(jumpOpcode, 0); + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpTrue, returnTrue); + + /* not a Small Float, nor Small Integer, check for Boxed Float argument */ + jmpTarget(jumpNotSmallInteger, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpImmediate = genJumpImmediate(Arg0Reg); + genGetCompactClassIndexNonImmOfinto(Arg0Reg, SendNumArgsReg); + genCmpClassFloatCompactIndexR(SendNumArgsReg); + jumpNotBoxedFloat = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genGetDoubleValueOfinto(Arg0Reg, DPFPReg1); + + /* Jump: */ + genoperand(Jump, ((sqInt)compareFloat)); + jmpTarget(jumpImmediate, jmpTarget(jumpNotBoxedFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + +/* Fetch the instance's identity hash into destReg, encoded as a + SmallInteger. + */ + + /* CogObjectRepresentationFor64BitSpur>>#genGetHashFieldNonImmOf:asSmallIntegerInto: */ +static NoDbgRegParms sqInt +genGetHashFieldNonImmOfasSmallIntegerInto(sqInt instReg, sqInt destReg) +{ + sqInt quickConstant; + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, instReg, destReg); + quickConstant = (identityHashFullWordShift()) - (numTagBits()); + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, quickConstant, destReg); + quickConstant = ((sqInt)((usqInt)((identityHashHalfWordMask())) << (numTagBits()))); + + /* begin AndCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, destReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, smallIntegerTag(), destReg); + + /* Shift and mask the field leaving room for the SmallInteger tag. */ + return 0; +} + + +/* Fetch the instance's identity hash into destReg, unencoded. */ + + /* CogObjectRepresentationFor64BitSpur>>#genGetHashFieldNonImmOf:into: */ +static NoDbgRegParms sqInt +genGetHashFieldNonImmOfinto(sqInt instReg, sqInt destReg) +{ + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, instReg, destReg); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, 32, destReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, identityHashHalfWordMask(), destReg); + return 0; +} + + +/* Extract the inline cache tag for the object in sourceReg into destReg. The + inline cache tag for a given object is the value loaded in inline caches + to distinguish + objects of different classes. In Spur this is either the tags for + immediates, or + the receiver's classIndex. Answer the label for the start of the sequence. */ + + /* CogObjectRepresentationFor64BitSpur>>#genGetInlineCacheClassTagFrom:into:forEntry: */ +static NoDbgRegParms AbstractInstruction * +genGetInlineCacheClassTagFromintoforEntry(sqInt sourceReg, sqInt destReg, sqInt forEntry) +{ + AbstractInstruction *entryLabel; + AbstractInstruction *jumpImm; + + if (forEntry) { + /* AlignmentNops: */ + genoperand(AlignmentNops, BytesPerWord); + } + entryLabel = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + gAndCqRR(tagMask(), sourceReg, destReg); + jumpImm = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Get least significant half of header word in destReg */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, sourceReg, destReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, classIndexMask(), destReg); + jmpTarget(jumpImm, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return entryLabel; +} + + +/* Get the size in byte-sized slots of the object in srcReg into destReg. + srcReg may equal destReg. + destReg <- numSlots << self shiftForWord - (fmt bitAnd: 7). + Assumes the object in srcReg has a byte format, i.e. 16 to 23 or 24 to 31 */ + + /* CogObjectRepresentationFor64BitSpur>>#genGetNumBytesOf:into: */ +static NoDbgRegParms sqInt +genGetNumBytesOfinto(sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *jmp; + + genGetRawSlotSizeOfNonImminto(srcReg, destReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, numSlotsMask(), destReg); + jmp = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + genGetOverflowSlotsOfinto(srcReg, destReg); + jmpTarget(jmp, genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), destReg)); + genGetBitsofFormatByteOfinto(7, srcReg, TempReg); + + /* Now: fmt bitAnd: 3 in TempReg */ + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, destReg); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genGetOverflowSlotsOf:into: */ +static NoDbgRegParms sqInt +genGetOverflowSlotsOfinto(sqInt srcReg, sqInt destReg) +{ + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, -BaseHeaderSize, srcReg, destReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, 8, destReg); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, 8, destReg); + return 0; +} + + +/* Convert the SmallFloat oop in ooppReg into the corresponding float value + in dpReg. + c.f. Spur64BitMemoryManager>>smallFloatBitsOf: */ + + /* CogObjectRepresentationFor64BitSpur>>#genGetSmallFloatValueOf:scratch:into: */ +static NoDbgRegParms sqInt +genGetSmallFloatValueOfscratchinto(sqInt oopReg, sqInt scratch, sqInt dpReg) +{ + AbstractInstruction *jumpSFZero; + sqInt quickConstant; + + /* MoveR:R: */ + genoperandoperand(MoveRR, oopReg, scratch); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, numTagBits(), scratch); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 1, scratch); + jumpSFZero = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + quickConstant = ((sqInt)((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, quickConstant, scratch); + jmpTarget(jumpSFZero, genoperandoperand(RotateRightCqR, 1, scratch)); + + /* begin MoveR:Rd: */ + assert(BytesPerWord == 8); + genoperandoperand(MoveRRd, scratch, dpReg); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genInstantiate64BitPositiveIntegerValue:into:scratchReg:mayFail: */ +static NoDbgRegParms AbstractInstruction * +genInstantiate64BitPositiveIntegerValueintoscratchRegmayFail(sqInt valueReg, sqInt resultReg, sqInt scratch, sqInt mayFail) +{ + AbstractInstruction *abstractInstruction; + sqInt allocSize; + AbstractInstruction *jumpFail; + AbstractInstruction *jumpOK; + usqLong newLPIHeader; + sqInt quickConstant; + + jumpFail = ((AbstractInstruction *) 0); + allocSize = BaseHeaderSize + BytesPerWord; + newLPIHeader = headerForSlotsformatclassIndex(1, firstByteFormat(), ClassLargePositiveIntegerCompactIndex); + if (mayFail) { + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), scratch); + quickConstant = (getScavengeThreshold()) - allocSize; + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, scratch); + jumpFail = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, scratch, resultReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, allocSize, scratch); + } + else { + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), resultReg); + quickConstant = (getScavengeThreshold()) - allocSize; + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, resultReg); + jumpOK = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceScheduleScavengeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpTarget(jumpOK, gAddCqRR(allocSize, resultReg, scratch)); + } + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, scratch, freeStartAddress()); + + /* begin genStoreHeader:intoNewInstance:using: */ + /* MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, newLPIHeader, scratch); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, scratch, 0, resultReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, valueReg, BaseHeaderSize, resultReg); + return (mayFail + ? jumpFail + : 0); +} + + /* CogObjectRepresentationFor64BitSpur>>#genInstantiate64BitSignedIntegerValue:into:scratchReg:mayFail: */ +static NoDbgRegParms AbstractInstruction * +genInstantiate64BitSignedIntegerValueintoscratchRegmayFail(sqInt valueReg, sqInt resultReg, sqInt scratch, sqInt mayFail) +{ + AbstractInstruction *abstractInstruction; + sqInt allocSize; + AbstractInstruction *jumpFail; + AbstractInstruction *jumpNeg; + AbstractInstruction *jumpOK; + usqLong newLNIHeader; + sqInt quickConstant; + + jumpFail = ((AbstractInstruction *) 0); + allocSize = BaseHeaderSize + BytesPerWord; + newLNIHeader = headerForSlotsformatclassIndex(1, firstByteFormat(), ClassLargeNegativeIntegerCompactIndex); + if (mayFail) { + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), scratch); + quickConstant = (getScavengeThreshold()) - allocSize; + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, scratch); + jumpFail = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, scratch, resultReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, allocSize, scratch); + } + else { + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), resultReg); + quickConstant = (getScavengeThreshold()) - allocSize; + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, resultReg); + jumpOK = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceScheduleScavengeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpTarget(jumpOK, gAddCqRR(allocSize, resultReg, scratch)); + } + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, scratch, freeStartAddress()); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, newLNIHeader, scratch); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, valueReg); + jumpNeg = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* We can avoid duplicating the large constant and a jump choosing between + the alternatives by incrementing the single constant if positive. The assert + checks that the hack works. Compact code is to be preferred because it is + an uncommon case; usually the value will fit in a SmallInteger. */ + assert((headerForSlotsformatclassIndex(0, 0, 1)) == 1); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ClassLargePositiveIntegerCompactIndex - ClassLargeNegativeIntegerCompactIndex, scratch); + jmpTarget(jumpNeg, genoperandoperandoperand(MoveRMwr, scratch, 0, resultReg)); + + /* The old less compact code was + cogit CmpCq: 0 R: valueReg. + jumpNeg := cogit JumpLess: 0. + cogit MoveCq: newLPIHeader R: scratch1. + jumpJoin := cogit Jump: 0. + jumpNeg jmpTarget: + (cogit MoveCq: newLNIHeader R: scratch1). + jumpJoin jmpTarget: + (cogit MoveR: scratch1 Mw: 0 r: resultReg). */ + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, valueReg, BaseHeaderSize, resultReg); + return (mayFail + ? jumpFail + : 0); +} + + +/* Allocate a new float instance with the value in the double-precision + register dpreg. + Do *not* use scratch2, which exists only for compatibility with V3. + If the allocation succeeds assign it to resultReg. If mayFail is false + always allocate. + If mayFail is true and the allocation fails do *not* update resultReg. + If mayFail is true answer the jump to take on failure, otherwise answer + nil. */ +/* Override to answer a SmallFloat64 if possible. */ + + /* CogObjectRepresentationFor64BitSpur>>#genInstantiateFloatValue:into:scratchReg:scratchReg:mayFail: */ +static NoDbgRegParms AbstractInstruction * +genInstantiateFloatValueintoscratchRegscratchRegmayFail(sqInt dpreg, sqInt resultReg, sqInt scratch1, sqInt scratch2, sqInt mayFail) +{ + AbstractInstruction *abstractInstruction; + usqIntptr_t allocSize; + AbstractInstruction *jumpFail; + AbstractInstruction *jumpMerge; + AbstractInstruction *jumpNotSF; + AbstractInstruction *jumpOK; + usqLong newFloatHeader; + sqInt quickConstant; + + jumpFail = ((AbstractInstruction *) 0); + + /* begin MoveRd:R: */ + assert(BytesPerWord == 8); + genoperandoperand(MoveRdR, dpreg, resultReg); + jumpNotSF = genJumpNotSmallFloatValueBitsscratch(resultReg, scratch1); + genConvertBitsToSmallFloatInscratch(resultReg, scratch1); + jumpMerge = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpNotSF, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + allocSize = BaseHeaderSize + (sizeof(double)); + if (mayFail) { + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), scratch1); + quickConstant = (getScavengeThreshold()) - allocSize; + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, scratch1); + jumpFail = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, scratch1, resultReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, allocSize, scratch1); + } + else { + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), resultReg); + quickConstant = (getScavengeThreshold()) - allocSize; + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, resultReg); + jumpOK = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceScheduleScavengeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpTarget(jumpOK, gAddCqRR(allocSize, resultReg, scratch1)); + } + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, scratch1, freeStartAddress()); + newFloatHeader = headerForSlotsformatclassIndex((sizeof(double)) / BytesPerWord, firstLongFormat(), ClassFloatCompactIndex); + + /* begin genStoreHeader:intoNewInstance:using: */ + /* MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, newFloatHeader, scratch1); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, scratch1, 0, resultReg); + + /* #MoveRd:M64:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRdM64r, dpreg, BaseHeaderSize, resultReg); + jumpFail = (mayFail + ? jumpFail + : ((AbstractInstruction *) null)); + jmpTarget(jumpMerge, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return jumpFail; +} + + +/* Generate a test for aRegister containing an integer value in the + SmallInteger range, and a jump if so, answering the jump. + c.f. Spur64BitMemoryManager>>isIntegerValue: */ + + /* CogObjectRepresentationFor64BitSpur>>#genJumpIsSmallIntegerValue:scratch: */ +static NoDbgRegParms AbstractInstruction * +genJumpIsSmallIntegerValuescratch(sqInt aRegister, sqInt scratchReg) +{ + sqInt quickConstant; + + gArithmeticShiftRightCqRR(0x3F - (numTagBits()), aRegister, scratchReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, scratchReg); + quickConstant = (1U << ((numTagBits()) + 1)) - 1; + + /* begin AndCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, scratchReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 1, scratchReg); + return genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); +} + + +/* Generate a compare and branch to test if aRegister contains other than a + Character. + */ + + /* CogObjectRepresentationFor64BitSpur>>#genJumpNotCharacter: */ +static NoDbgRegParms AbstractInstruction * +genJumpNotCharacter(sqInt reg) +{ + /* #TstCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, characterTag(), reg); + return genConditionalBranchoperand(JumpZero, ((sqInt)0)); +} + + +/* Generate a test to check that the integer register contains a floating + point value within the SmallFloat64 range, + and answer the jump. c.f. Spur64BitMemoryManager>>isSmallFloatValue: */ + + /* CogObjectRepresentationFor64BitSpur>>#genJumpNotSmallFloatValueBits:scratch: */ +static NoDbgRegParms AbstractInstruction * +genJumpNotSmallFloatValueBitsscratch(sqInt reg, sqInt exponent) +{ + AbstractInstruction *jumpFail; + AbstractInstruction *jumpMaxExponent; + AbstractInstruction *jumpMinExponent; + AbstractInstruction *jumpTest; + AbstractInstruction *jumpZeroMantissa; + sqInt quickConstant; + + gLogicalShiftLeftCqRR(1, reg, exponent); + quickConstant = (smallFloatMantissaBits()) + 1; + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, quickConstant, exponent); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, smallFloatExponentOffset(), exponent); + jumpMinExponent = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + quickConstant = 0xFF + (smallFloatExponentOffset()); + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, exponent); + jumpMaxExponent = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + jumpFail = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpMinExponent, gTstCqR((1ULL << (smallFloatMantissaBits())) - 1, reg)); + jumpZeroMantissa = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, smallFloatExponentOffset(), exponent); + jumpTest = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpZeroMantissa, genoperandoperand(CmpCqR, 0, exponent)); + jmpTarget(jumpTest, genConditionalBranchoperand(JumpNonZero, ((sqInt)jumpFail))); + jmpTarget(jumpMaxExponent, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return jumpFail; +} + + +/* Generate a compare and branch to test if aRegister contains other than a + SmallFloat. Answer the jump. */ + + /* CogObjectRepresentationFor64BitSpur>>#genJumpNotSmallFloat: */ +static NoDbgRegParms AbstractInstruction * +genJumpNotSmallFloat(sqInt reg) +{ + /* #TstCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, smallFloatTag(), reg); + return genConditionalBranchoperand(JumpZero, ((sqInt)0)); +} + + +/* Generate a test for aRegister containing an integer value outside the + SmallInteger range, and a jump if so, answering the jump. + c.f. Spur64BitMemoryManager>>isIntegerValue: */ + + /* CogObjectRepresentationFor64BitSpur>>#genJumpNotSmallIntegerValue:scratch: */ +static NoDbgRegParms AbstractInstruction * +genJumpNotSmallIntegerValuescratch(sqInt aRegister, sqInt scratchReg) +{ + sqInt quickConstant; + + gArithmeticShiftRightCqRR(0x3F - (numTagBits()), aRegister, scratchReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, scratchReg); + quickConstant = (1U << ((numTagBits()) + 1)) - 1; + + /* begin AndCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, scratchReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 1, scratchReg); + return genConditionalBranchoperand(JumpGreater, ((sqInt)0)); +} + + +/* Generate a compare and branch to test if aRegister contains other than a + SmallInteger. + */ + + /* CogObjectRepresentationFor64BitSpur>>#genJumpNotSmallInteger: */ +static NoDbgRegParms AbstractInstruction * +genJumpNotSmallInteger(sqInt reg) +{ + /* #TstCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, smallIntegerTag(), reg); + return genConditionalBranchoperand(JumpZero, ((sqInt)0)); +} + + +/* Generate a compare and branch to test if aRegister contains a + SmallInteger. Answer the jump, or UnimplementedOperation if this cannot be + done with + a single register. */ + + /* CogObjectRepresentationFor64BitSpur>>#genJumpSmallInteger: */ +static NoDbgRegParms AbstractInstruction * +genJumpSmallInteger(sqInt aRegister) +{ + /* #TstCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, smallIntegerTag(), aRegister); + return genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +} + + +/* Generate the code for primitives 61 & 165, at:put:/basicAt:put: & + integerAt:put:. If signedVersion is true + then generate signed accesses to the bits classes (a la 164 & 165). If + signedVersion is false, + generate unsigned accesses (a la 60, 61, 63 & 64). */ +/* c.f. StackInterpreter>>stSizeOf: SpurMemoryManager>>lengthOf:format: + fixedFieldsOf:format:length: + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveAtPutSigned: */ +static NoDbgRegParms sqInt +genPrimitiveAtPutSigned(sqInt signedVersion) +{ + sqInt formatReg; + AbstractInstruction *jump64BitArgIsImmediate; + AbstractInstruction *jump64BitsOutOfBounds; + AbstractInstruction *jumpArrayOutOfBounds; + AbstractInstruction *jumpBadIndex; + AbstractInstruction *jumpBytesOutOfBounds; + AbstractInstruction *jumpBytesOutOfRange; + AbstractInstruction *jumpDoubleWordsOutOfRange; + AbstractInstruction *jumpFixedFieldsOutOfBounds; + AbstractInstruction *jumpHasFixedFields; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpImmutable; + AbstractInstruction *jumpIsBytes; + AbstractInstruction *jumpIsCompiledMethod; + AbstractInstruction *jumpIsContext; + AbstractInstruction *jumpIsShorts; + AbstractInstruction *jumpIsWords; + AbstractInstruction *jumpNegative; + AbstractInstruction *jumpNonSmallIntegerValue; + AbstractInstruction *jumpNot64BitIndexable; + AbstractInstruction *jumpNot8ByteInteger; + AbstractInstruction *jumpNotIndexableBits; + AbstractInstruction *jumpNotIndexablePointers; + AbstractInstruction *jumpNotPointers; + AbstractInstruction *jumpShortsOutOfBounds; + AbstractInstruction *jumpShortsOutOfRange; + AbstractInstruction *jumpWordsOutOfBounds; + AbstractInstruction *jumpWordsOutOfRange; + AbstractInstruction *methodInBounds; + sqInt nSlotsOrBytesReg; + sqInt quickConstant; + AbstractInstruction *rejoin; + + jumpDoubleWordsOutOfRange = ((AbstractInstruction *) 0); + jumpNegative = ((AbstractInstruction *) 0); + nSlotsOrBytesReg = ClassReg; + + /* begin genLoadArgAtDepth:into: */ + assert(1 < (numRegArgs())); + assert(0 < (numRegArgs())); + jumpImmediate = genJumpImmediate(ReceiverResultReg); + jumpBadIndex = genJumpNotSmallInteger(Arg0Reg); + genConvertSmallIntegerToIntegerInReg(Arg0Reg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 1, Arg0Reg); +# if IMMUTABILITY + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), TempReg); + + /* begin genJumpBaseHeaderImmutable: */ + /* TstCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, immutableBitMask(), TempReg); + jumpImmutable = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +# else // IMMUTABILITY + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), NoReg); +# endif // IMMUTABILITY + + genGetNumSlotsOfinto(ReceiverResultReg, nSlotsOrBytesReg); + + /* dispatch on format in a combination of highest dynamic frequency order first and convenience. + 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, weakArrayFormat(), formatReg); + jumpNotPointers = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* optimistic store check; assume index in range (almost always is). */ + genStoreCheckReceiverRegvalueRegscratchReginFrame(ReceiverResultReg, Arg1Reg, TempReg, 0); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, arrayFormat(), formatReg); + jumpNotIndexablePointers = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + jumpHasFixedFields = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpArrayOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), Arg0Reg); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, Arg1Reg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpHasFixedFields, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genGetClassIndexOfNonImminto(ReceiverResultReg, formatReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ClassMethodContextCompactIndex, formatReg); + jumpIsContext = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* get # fixed fields in formatReg */ + genGetClassObjectOfClassIndexintoscratchReg(formatReg, Extra0Reg, TempReg); + genLoadSlotsourceRegdestReg(InstanceSpecificationIndex, Extra0Reg, formatReg); + genConvertSmallIntegerToIntegerInReg(formatReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, fixedFieldsOfClassFormatMask(), formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrBytesReg); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), formatReg); + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpFixedFieldsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* AddR:R: */ + genoperandoperand(AddRR, formatReg, Arg0Reg); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, Arg1Reg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotPointers, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpNonSmallIntegerValue = genJumpNotSmallInteger(Arg1Reg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), formatReg); + jumpIsBytes = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstShortFormat(), formatReg); + jumpIsShorts = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstLongFormat(), formatReg); + jumpIsWords = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, sixtyFourBitIndexableFormat(), formatReg); + jumpNotIndexableBits = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* fall through to 64-bit words */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, SendNumArgsReg); + genConvertSmallIntegerToIntegerInReg(SendNumArgsReg); + if (!signedVersion) { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, SendNumArgsReg); + jumpNegative = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + } + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + rejoin = genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jump64BitsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), Arg0Reg); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, SendNumArgsReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* Handle LargePositiveInteger (unsigned) or LargePositiveInteger and LargeNegativeInteger (signed) + for sixtyFourBitIndexableFormat */ + jmpTarget(jumpNonSmallIntegerValue, genoperandoperand(CmpCqR, sixtyFourBitIndexableFormat(), formatReg)); + jumpNot64BitIndexable = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + jump64BitArgIsImmediate = genJumpImmediate(Arg1Reg); + if (signedVersion) { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BaseHeaderSize, Arg1Reg, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, TempReg); + jumpDoubleWordsOutOfRange = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + } + + /* Test top bit of 64-bit word in large integer for range check. + Now check if the header is that of an 8 byte LargePositiveInteger */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, Arg1Reg, SendNumArgsReg); + quickConstant = headerForSlotsformatclassIndex(numSlotsMask(), formatMask(), classIndexMask()); + + /* begin AndCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, SendNumArgsReg); + quickConstant = headerForSlotsformatclassIndex(1, firstByteFormat(), ClassLargePositiveIntegerCompactIndex); + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, SendNumArgsReg); + jumpNot8ByteInteger = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BaseHeaderSize, Arg1Reg, SendNumArgsReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)rejoin)); + if (signedVersion) { + /* Now check if the header is that of an 8 byte LargeNegativeInteger */ + jmpTarget(jumpNot8ByteInteger, gCmpCqR(headerForSlotsformatclassIndex(1, firstByteFormat(), ClassLargeNegativeIntegerCompactIndex), SendNumArgsReg)); + jumpNot8ByteInteger = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BaseHeaderSize, Arg1Reg, TempReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, SendNumArgsReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, SendNumArgsReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)rejoin)); + } + if (signedVersion) { + jmpTarget(jumpIsWords, gArithmeticShiftRightCqRR(0x1F + (numTagBits()), Arg1Reg, TempReg)); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 1, TempReg); + } + else { + jmpTarget(jumpIsWords, genoperandoperand(CmpCqR, (((usqInt)0xFFFFFFFFU << 3) | 1), Arg1Reg)); + } + jumpWordsOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - 2, nSlotsOrBytesReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 4) - 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrBytesReg); + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpWordsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> ((shiftForWord()) - 1), Arg0Reg); + + /* MoveR:X32r:R: */ + genoperandoperandoperand(MoveRX32rR, TempReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + if (signedVersion) { + jmpTarget(jumpIsBytes, gArithmeticShiftRightCqRR(7 + (numTagBits()), Arg1Reg, TempReg)); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 1, TempReg); + } + else { + jmpTarget(jumpIsBytes, genoperandoperand(CmpCqR, (((usqInt)0xFF << 3) | 1), Arg1Reg)); + } + jumpBytesOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), nSlotsOrBytesReg); + gAndCqRR(BytesPerWord - 1, formatReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, nSlotsOrBytesReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpBytesOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), formatReg); + jumpIsCompiledMethod = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + methodInBounds = genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BaseHeaderSize, Arg0Reg); + + /* MoveR:Xbr:R: */ + genoperandoperandoperand(MoveRXbrR, TempReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + if (signedVersion) { + jmpTarget(jumpIsShorts, gArithmeticShiftRightCqRR(15 + (numTagBits()), Arg1Reg, TempReg)); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 1, TempReg); + } + else { + jmpTarget(jumpIsShorts, genoperandoperand(CmpCqR, (((usqInt)0xFFFF << 3) | 1), Arg1Reg)); + } + jumpShortsOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - 1, nSlotsOrBytesReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 2) - 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrBytesReg); + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpShortsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg0Reg, ReceiverResultReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg0Reg, ReceiverResultReg); + + /* #MoveR:M16:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRM16r, TempReg, BaseHeaderSize, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* Now check that the index is beyond the method's literals... */ + jmpTarget(jumpIsCompiledMethod, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + getLiteralCountOfplusOneinBytesintoscratch(ReceiverResultReg, 1, 1, nSlotsOrBytesReg, TempReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + + /* JumpBelow: */ + genConditionalBranchoperand(JumpBelow, ((sqInt)methodInBounds)); + jmpTarget(jumpNot8ByteInteger, jmpTarget(jump64BitArgIsImmediate, jmpTarget(jumpNot64BitIndexable, jmpTarget(jumpIsContext, jmpTarget(jumpNotIndexableBits, jmpTarget(jumpBytesOutOfRange, jmpTarget(jumpShortsOutOfRange, jmpTarget(jumpWordsOutOfRange, jmpTarget(jumpIsCompiledMethod, jmpTarget(jumpArrayOutOfBounds, jmpTarget(jumpBytesOutOfBounds, jmpTarget(jumpShortsOutOfBounds, jmpTarget(jumpWordsOutOfBounds, jmpTarget(jump64BitsOutOfBounds, jmpTarget(jumpNotIndexablePointers, jmpTarget(jumpFixedFieldsOutOfBounds, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))))))))))))))); + if (signedVersion) { + jmpTarget(jumpDoubleWordsOutOfRange, ((AbstractInstruction *) (((jumpIsContext->operands))[0]))); + } + else { + jmpTarget(jumpNegative, ((AbstractInstruction *) (((jumpIsContext->operands))[0]))); + } +# if IMMUTABILITY + jmpTarget(jumpImmutable, ((AbstractInstruction *) (((jumpIsContext->operands))[0]))); +# endif + + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, Arg0Reg); + genConvertIntegerToSmallIntegerInReg(Arg0Reg); + jmpTarget(jumpBadIndex, jmpTarget(jumpImmediate, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return 0; +} + + +/* Generate the code for primitives 60 & 164, at:/basicAt: & integerAt:. If + signedVersion is true + then generate signed accesses to the bits classes (a la 164 & 165). If + signedVersion is false, + generate unsigned accesses (a la 60, 61, 63 & 64). */ +/* c.f. StackInterpreter>>stSizeOf: SpurMemoryManager>>lengthOf:format: + fixedFieldsOf:format:length: + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveAtSigned: */ +static NoDbgRegParms sqInt +genPrimitiveAtSigned(sqInt signedVersion) +{ + AbstractInstruction *convertToIntAndReturn; + sqInt formatReg; + AbstractInstruction *jumpArrayOutOfBounds; + AbstractInstruction *jumpBadIndex; + AbstractInstruction *jumpBytesOutOfBounds; + AbstractInstruction *jumpFixedFieldsOutOfBounds; + AbstractInstruction *jumpHasFixedFields; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpIsArray; + AbstractInstruction *jumpIsBytes; + AbstractInstruction *jumpIsContext; + AbstractInstruction *jumpIsLongs; + AbstractInstruction *jumpIsMethod; + AbstractInstruction *jumpIsShorts; + AbstractInstruction *jumpIsWords; + AbstractInstruction *jumpLongsOutOfBounds; + AbstractInstruction *jumpNotIndexable; + AbstractInstruction *jumpNotSmallInteger; + AbstractInstruction *jumpShortsOutOfBounds; + AbstractInstruction *jumpWordsOutOfBounds; + AbstractInstruction *methodInBounds; + sqInt nSlotsOrElementsReg; + sqInt quickConstant; + + nSlotsOrElementsReg = ClassReg; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpImmediate = genJumpImmediate(ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, Arg1Reg); + jumpBadIndex = genJumpNotSmallInteger(Arg0Reg); + genConvertSmallIntegerToIntegerInReg(Arg1Reg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 1, Arg1Reg); + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), TempReg); + genGetNumSlotsOfinto(ReceiverResultReg, nSlotsOrElementsReg); + + /* dispatch on format in a combination of highest dynamic frequency order first and convenience. + 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), formatReg); + jumpIsBytes = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, arrayFormat(), formatReg); + jumpIsArray = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + jumpNotIndexable = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, weakArrayFormat(), formatReg); + jumpHasFixedFields = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstShortFormat(), formatReg); + jumpIsShorts = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstLongFormat(), formatReg); + jumpIsWords = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, sixtyFourBitIndexableFormat(), formatReg); + jumpIsLongs = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + jmpTarget(jumpNotIndexable, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpNotIndexable = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpIsArray, + + (/* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)), + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg))); + jumpArrayOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), Arg1Reg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, Arg1Reg, ReceiverResultReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsBytes, genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), nSlotsOrElementsReg)); + gAndCqRR(7, formatReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, nSlotsOrElementsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpBytesOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), formatReg); + jumpIsMethod = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + methodInBounds = genoperandoperand(AddCqR, BaseHeaderSize, Arg1Reg); + if (signedVersion) { + /* MoveXbr:R:R: */ + genoperandoperandoperand(MoveXbrRR, Arg1Reg, ReceiverResultReg, ReceiverResultReg); + } + else { + /* formatReg already contains a value <= 16r1f, so no need to zero it */ + + /* MoveXbr:R:R: */ + genoperandoperandoperand(MoveXbrRR, Arg1Reg, ReceiverResultReg, formatReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, formatReg, ReceiverResultReg); + } + if (signedVersion) { + /* SignExtend8R:R: */ + genoperandoperand(SignExtend8RR, ReceiverResultReg, ReceiverResultReg); + } + convertToIntAndReturn = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + genConvertIntegerToSmallIntegerInReg(ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsShorts, gLogicalShiftLeftCqR((shiftForWord()) - 1, nSlotsOrElementsReg)); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, 3, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrElementsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpShortsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg1Reg, ReceiverResultReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg1Reg, ReceiverResultReg); + + /* #MoveM16:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveM16rR, BaseHeaderSize, ReceiverResultReg, ReceiverResultReg); + if (signedVersion) { + /* SignExtend16R:R: */ + genoperandoperand(SignExtend16RR, ReceiverResultReg, ReceiverResultReg); + } + + /* Jump: */ + genoperand(Jump, ((sqInt)convertToIntAndReturn)); + jmpTarget(jumpIsWords, gLogicalShiftLeftCqR((shiftForWord()) - 2, nSlotsOrElementsReg)); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrElementsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpWordsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> ((shiftForWord()) - 1), Arg1Reg); + + /* MoveX32r:R:R: */ + genoperandoperandoperand(MoveX32rRR, Arg1Reg, ReceiverResultReg, TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, ReceiverResultReg); + if (signedVersion) { + /* SignExtend32R:R: */ + genoperandoperand(SignExtend32RR, ReceiverResultReg, ReceiverResultReg); + } + + /* Jump: */ + genoperand(Jump, ((sqInt)convertToIntAndReturn)); + jmpTarget(jumpHasFixedFields, genoperandoperand(AndCqR, classIndexMask(), TempReg)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, formatReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ClassMethodContextCompactIndex, TempReg); + jumpIsContext = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + genGetClassObjectOfClassIndexintoscratchReg(formatReg, Extra0Reg, TempReg); + genLoadSlotsourceRegdestReg(InstanceSpecificationIndex, Extra0Reg, formatReg); + genConvertSmallIntegerToIntegerInReg(formatReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, fixedFieldsOfClassFormatMask(), formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrElementsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpFixedFieldsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* index is (formatReg (fixed fields) + Arg1Reg (0-rel index)) * wordSize + baseHeaderSize */ + + /* AddR:R: */ + genoperandoperand(AddRR, formatReg, Arg1Reg); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), Arg1Reg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, Arg1Reg, ReceiverResultReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsLongs, + + (/* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)), + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg))); + jumpLongsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), Arg1Reg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, Arg1Reg, ReceiverResultReg, ClassReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, TempReg); + if (signedVersion) { + /* ArithmeticShiftRightCq:R: */ + genoperandoperand(ArithmeticShiftRightCqR, numSmallIntegerBits(), TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 1, TempReg); + jumpNotSmallInteger = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, ReceiverResultReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)convertToIntAndReturn)); + jmpTarget(jumpNotSmallInteger, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genInstantiate64BitSignedIntegerValueintoscratchRegmayFail(ClassReg, ReceiverResultReg, TempReg, 0); + } + else { + quickConstant = (numSmallIntegerBits()) - 1; + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, quickConstant, TempReg); + if (!(setsConditionCodesFor(lastOpcode(), JumpZero))) { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, TempReg); + } + jumpNotSmallInteger = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, ReceiverResultReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)convertToIntAndReturn)); + jmpTarget(jumpNotSmallInteger, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genInstantiate64BitPositiveIntegerValueintoscratchRegmayFail(ClassReg, ReceiverResultReg, TempReg, 0); + } + + /* c.f. Spur64BitMemoryManager>>#isIntegerValue: */ + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsMethod, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* Now check that the index is beyond the method's literals... */ + getLiteralCountOfplusOneinBytesintoscratch(ReceiverResultReg, 1, 1, nSlotsOrElementsReg, TempReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + + /* JumpBelow: */ + genConditionalBranchoperand(JumpBelow, ((sqInt)methodInBounds)); + jmpTarget(jumpLongsOutOfBounds, jmpTarget(jumpFixedFieldsOutOfBounds, jmpTarget(jumpArrayOutOfBounds, jmpTarget(jumpBytesOutOfBounds, jmpTarget(jumpShortsOutOfBounds, jmpTarget(jumpWordsOutOfBounds, jmpTarget(jumpNotIndexable, jmpTarget(jumpIsContext, jmpTarget(jumpBadIndex, jmpTarget(jumpImmediate, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))))))))); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveFloatEqual */ +static sqInt +genPrimitiveFloatEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPEqual, JumpZero, 0, 1) + : genPureFloatComparisoninvertboxed(gJumpFPEqual, 0, 1)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveFloatGreaterOrEqual */ +static sqInt +genPrimitiveFloatGreaterOrEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreaterOrEqual, JumpGreaterOrEqual, 0, 1) + : genPureFloatComparisoninvertboxed(gJumpFPGreaterOrEqual, 0, 1)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveFloatGreaterThan */ +static sqInt +genPrimitiveFloatGreaterThan(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreater, JumpGreater, 0, 1) + : genPureFloatComparisoninvertboxed(gJumpFPGreater, 0, 1)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveFloatLessOrEqual */ +static sqInt +genPrimitiveFloatLessOrEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreaterOrEqual, JumpLessOrEqual, 1, 1) + : genPureFloatComparisoninvertboxed(gJumpFPGreaterOrEqual, 1, 1)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveFloatLessThan */ +static sqInt +genPrimitiveFloatLessThan(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreater, JumpLess, 1, 1) + : genPureFloatComparisoninvertboxed(gJumpFPGreater, 1, 1)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveFloatNotEqual */ +static sqInt +genPrimitiveFloatNotEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPNotEqual, JumpNonZero, 0, 1) + : genPureFloatComparisoninvertboxed(gJumpFPNotEqual, 0, 1)); +} + + +/* Arguably we should fail for immediates, but so far no one has complained, + so... + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveIdentityHash */ +static sqInt +genPrimitiveIdentityHash(void) +{ + AbstractInstruction *abstractInstruction; + int callerSavedRegsToBeSaved; + sqInt callTargetSqInt; + AbstractInstruction *cascade0; + AbstractInstruction *jumpImm; + AbstractInstruction *jumpNotCharacter; + AbstractInstruction *jumpNotSet; + AbstractInstruction *lastInst; + int mustSaveSPReg; + sqInt reg; + int registersToBePushed; + AbstractInstruction *ret; + AbstractInstruction *self_in_CogX64Compiler; + sqInt spareCalleeSavedReg; + + /* uses TstCqR */ + jumpImm = genJumpImmediate(ReceiverResultReg); + genGetHashFieldNonImmOfasSmallIntegerInto(ReceiverResultReg, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ConstZero, TempReg); + jumpNotSet = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, ReceiverResultReg); + ret = /* genPrimReturn */ + (methodOrBlockNumArgs <= (numRegArgs()) + ? genoperand(RetN, 0) + : genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord)); + jmpTarget(jumpImm, gAndCqRR(tagMask(), ReceiverResultReg, TempReg)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, characterTag(), TempReg); + jumpNotCharacter = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genConvertCharacterToSmallIntegerInReg(ReceiverResultReg); + ret = /* genPrimReturn */ + (methodOrBlockNumArgs <= (numRegArgs()) + ? genoperand(RetN, 0) + : genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord)); + jmpTarget(jumpNotCharacter, genoperandoperand(CmpCqR, smallFloatTag(), TempReg)); + + /* JumpNonZero: */ + genConditionalBranchoperand(JumpNonZero, ((sqInt)ret)); + genConvertSmallFloatToSmallFloatHashAsIntegerInRegscratch(ReceiverResultReg, TempReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSet, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* For non-behaviors we can call ceNewHash to assign a fresh hash. + For behaviors we must fail into the interpreter primitive to enter the receiver into the class table. */ + if (!(primitiveIndex == 75)) { + return 0; + } + + /* begin compileInvokeCENewHashOf */ + /* begin saveRegistersInMask:around: */ + spareCalleeSavedReg = 0; + callerSavedRegsToBeSaved = CallerSavedRegisterMask & ((1U << VarBaseReg)); + registersToBePushed = callerSavedRegsToBeSaved; + reg = 0; + while (registersToBePushed != 0) { + if (((registersToBePushed & 1) != 0)) { + /* PushR: */ + genoperand(PushR, reg); + } + reg += 1; + registersToBePushed = ((sqInt)(registersToBePushed)) >> 1; + } + + /* If the backEnd doesn't use the native SP register in generated code, and the non-native + SP is not callee-saved, then it too must be saved and restored around the call. */ + mustSaveSPReg = 0 /* (usesNativeSPReg not) */; + if (mustSaveSPReg) { + spareCalleeSavedReg = availableRegisterOrNoneIn(((ABICalleeSavedRegisterMask | ((1U << SPReg))) - ((1U << SPReg)))); + assert(!((spareCalleeSavedReg == NoReg))); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SPReg, spareCalleeSavedReg); + } + self_in_CogX64Compiler = ((AbstractInstruction *) (backEnd)); + + /* begin genMarshallNArgs:arg:arg:arg:arg: */ + /* SubCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 32, RSP); + + /* WIN64 ABI always reserves shadow space on the stack for callee to save up to 4 register parameters */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, CArg0Reg); + + /* begin CallAnyFixedRT: */ + if (is32BitSignedImmediate(backEnd, maximumDistanceFromCodeZone(((usqInt)ceNewHashOf)))) { + callTargetSqInt = ((usqIntptr_t)ceNewHashOf); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, callTargetSqInt); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* #CallFullRT: #CallFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(CallFull, ((usqIntptr_t)ceNewHashOf)); + } + cascade0 = backEnd; + + /* begin genRemoveNArgsFromStack: */ + /* AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 32, RSP); + lastInst = genWriteCResultIntoReg(cascade0, ReceiverResultReg); + if (mustSaveSPReg) { + lastInst = genoperandoperand(MoveRR, spareCalleeSavedReg, SPReg); + } + while (reg > 0) { + reg -= 1; + if (((callerSavedRegsToBeSaved & (1ULL << reg)) != 0)) { + lastInst = genoperand(PopR, reg); + } + } + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + return UnfailingPrimitive; +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveImmediateAsInteger */ +static sqInt +genPrimitiveImmediateAsInteger(void) +{ + AbstractInstruction *jumpNotCharacter; + AbstractInstruction *ret; + + gAndCqRR(tagMask(), ReceiverResultReg, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, characterTag(), TempReg); + jumpNotCharacter = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genConvertCharacterToSmallIntegerInReg(ReceiverResultReg); + ret = /* genPrimReturn */ + (methodOrBlockNumArgs <= (numRegArgs()) + ? genoperand(RetN, 0) + : genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord)); + jmpTarget(jumpNotCharacter, genoperandoperand(CmpCqR, smallFloatTag(), TempReg)); + + /* JumpNonZero: */ + genConditionalBranchoperand(JumpNonZero, ((sqInt)ret)); + genConvertSmallFloatToSmallFloatHashAsIntegerInRegscratch(ReceiverResultReg, TempReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + return UnfailingPrimitive; +} + + +/* Implement primitiveNew for convenient cases: + - the receiver has a hash + - the receiver is fixed size (excluding ephemerons to save instructions & + miniscule time) + - single word header/num slots < numSlotsMask + - the result fits in eden (actually below scavengeThreshold) + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveNew */ +static sqInt +genPrimitiveNew(void) +{ + sqInt byteSizeReg; + AbstractInstruction *fillLoop; + sqInt fillReg; + sqInt headerReg; + sqInt instSpecReg; + AbstractInstruction *jumpHasSlots; + AbstractInstruction *jumpNoSpace; + AbstractInstruction *jumpTooBig; + AbstractInstruction *jumpUnhashed; + AbstractInstruction *jumpVariableOrEphemeron; + AbstractInstruction *skip; + + if (methodOrBlockNumArgs) { + return UnimplementedPrimitive; + } + + /* header will contain classIndex/class's hash & format & numSlots/fixed size and finally fill value (nilObject). */ + headerReg = (fillReg = SendNumArgsReg); + + /* inst spec will hold class's instance specification, then byte size and finally end of new object. */ + instSpecReg = (byteSizeReg = ClassReg); + + /* get freeStart as early as possible so as not to wait later... */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), Arg1Reg); + + /* get class's hash & fail if 0 */ + genGetHashFieldNonImmOfinto(ReceiverResultReg, headerReg); + jumpUnhashed = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* get class's format inst var for both inst spec (format field) and num fixed fields */ + genLoadSlotsourceRegdestReg(InstanceSpecificationIndex, ReceiverResultReg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, instSpecReg); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, fixedFieldsFieldWidth(), TempReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, formatMask(), TempReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, fixedFieldsOfClassFormatMask(), instSpecReg); + + /* fail if not fixed or if ephemeron (rare beasts so save the cycles) */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, nonIndexablePointerFormat(), TempReg); + jumpVariableOrEphemeron = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, numSlotsMask(), instSpecReg); + jumpTooBig = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* Add format to classIndex/format in header; the add in numSlots */ + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, instSpecReg, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, numSlotsFullShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* compute byte size; remember 0-sized objects still need 1 slot. */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, byteSizeReg); + jumpHasSlots = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, BaseHeaderSize * 2, byteSizeReg); + skip = genoperand(Jump, ((sqInt)0)); + + /* round up to allocationUnit */ + jmpTarget(jumpHasSlots, genoperandoperand(AddCqR, BaseHeaderSize / BytesPerWord, byteSizeReg)); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), byteSizeReg); + jmpTarget(skip, genoperandoperand(AddRR, Arg1Reg, byteSizeReg)); + + /* check if allocation fits (freeSize + byteSize < scavengeThreshold); scavengeThreshold is constant. */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, getScavengeThreshold(), byteSizeReg); + jumpNoSpace = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* write back new freeStart; get result. byteSizeReg holds new freeStart, the limit of the object */ + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, byteSizeReg, freeStartAddress()); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* write header */ + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, headerReg, 0, Arg1Reg); + + /* now fill */ + + /* #LoadEffectiveAddressMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(LoadEffectiveAddressMwrR, BaseHeaderSize, ReceiverResultReg, Arg1Reg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, nilObject(), fillReg); + fillLoop = genoperandoperandoperand(MoveRMwr, fillReg, 0, Arg1Reg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 8, Arg1Reg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, byteSizeReg); + + /* JumpAbove: */ + genConditionalBranchoperand(JumpAbove, ((sqInt)fillLoop)); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpUnhashed, jmpTarget(jumpVariableOrEphemeron, jmpTarget(jumpTooBig, jmpTarget(jumpNoSpace, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))); + return 0; +} + + +/* Implement primitiveNewWithArg for convenient cases: + - the receiver has a hash + - the receiver is variable and not compiled method + - single word header/num slots < numSlotsMask + - the result fits in eden + See superclass method for dynamic frequencies of formats. + For the moment we implement only arrayFormat, firstByteFormat & + firstLongFormat + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveNewWithArg */ +static sqInt +genPrimitiveNewWithArg(void) +{ + sqInt byteSizeReg; + AbstractInstruction *fillLoop; + sqInt fillReg; + sqInt headerReg; + sqInt instSpecReg; + AbstractInstruction *jumpArrayFormat; + AbstractInstruction *jumpArrayTooBig; + AbstractInstruction *jumpByteFormat; + AbstractInstruction *jumpBytePrepDone; + AbstractInstruction *jumpByteTooBig; + AbstractInstruction *jumpFailCuzFixed; + AbstractInstruction *jumpHasSlots; + AbstractInstruction *jumpLongPrepDone; + AbstractInstruction *jumpLongTooBig; + AbstractInstruction *jumpNElementsNonInt; + AbstractInstruction *jumpNoSpace; + AbstractInstruction *jumpUnhashed; + sqInt maxSlots; + sqInt quickConstant; + AbstractInstruction *skip; + + if (methodOrBlockNumArgs != 1) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* header will contain classIndex/class's hash & format & numSlots/fixed size */ + headerReg = SendNumArgsReg; + + /* Assume there's an available scratch register on 64-bit machines. This holds the saved numFixedFields and then the value to fill with */ + fillReg = Extra0Reg; + assert(fillReg > 0); + + /* inst spec will hold class's instance specification and then byte size */ + instSpecReg = (byteSizeReg = ClassReg); + + /* The max slots we'll allocate here are those for a single header */ + maxSlots = (numSlotsMask()) - 1; + + /* get freeStart as early as possible so as not to wait later... */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), Arg1Reg); + + /* get class's hash & fail if 0 */ + genGetHashFieldNonImmOfinto(ReceiverResultReg, headerReg); + jumpUnhashed = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* get index and fail if not a +ve integer */ + jumpNElementsNonInt = genJumpNotSmallInteger(Arg0Reg); + + /* get class's format inst var for inst spec (format field) */ + genLoadSlotsourceRegdestReg(InstanceSpecificationIndex, ReceiverResultReg, instSpecReg); + quickConstant = (fixedFieldsFieldWidth()) + (numSmallIntegerTagBits()); + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, quickConstant, instSpecReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, formatMask(), instSpecReg); + + /* Add format to classIndex/format header now */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, instSpecReg, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* get integer value of num fields in fillReg now */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, fillReg); + genConvertSmallIntegerToIntegerInReg(fillReg); + + /* dispatch on format, failing if not variable or if compiled method */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, arrayFormat(), instSpecReg); + jumpArrayFormat = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), instSpecReg); + jumpByteFormat = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstLongFormat(), instSpecReg); + jumpFailCuzFixed = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, (((usqInt)(maxSlots * 2) << 3) | 1), Arg0Reg); + jumpLongTooBig = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* save num elements/slot size to instSpecReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, fillReg, instSpecReg); + + /* compute odd bits and add into headerReg; oddBits := 2 - nElements bitAnd: 1 */ + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, BytesPerWord / 4, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, instSpecReg, TempReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 4) - 1, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* round up num elements to numSlots in instSpecReg */ + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, (BytesPerWord / 4) - 1, instSpecReg); + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, (shiftForWord()) - 2, instSpecReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, fillReg); + + /* go allocate */ + jumpLongPrepDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpByteFormat, genoperandoperand(CmpCqR, (((usqInt)(maxSlots * BytesPerWord) << 3) | 1), Arg0Reg)); + jumpByteTooBig = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* save num elements to instSpecReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, fillReg, instSpecReg); + + /* compute odd bits and add into headerReg; oddBits := 8 - nElements bitAnd: 7 */ + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, BytesPerWord, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, instSpecReg, TempReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, BytesPerWord - 1, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* round up num elements to numSlots in instSpecReg */ + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BytesPerWord - 1, instSpecReg); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, shiftForWord(), instSpecReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, fillReg); + + /* go allocate */ + jumpBytePrepDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpArrayFormat, genoperandoperand(CmpCqR, (((usqInt)maxSlots << 3) | 1), Arg0Reg)); + jumpArrayTooBig = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* save num elements/slot size to instSpecReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, fillReg, instSpecReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, nilObject(), fillReg); + + /* fall through to allocate */ + jmpTarget(jumpBytePrepDone, jmpTarget(jumpLongPrepDone, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + + /* store numSlots to headerReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, instSpecReg, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, numSlotsFullShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* compute byte size; remember 0-sized objects still need 1 slot. */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, byteSizeReg); + jumpHasSlots = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, BaseHeaderSize * 2, byteSizeReg); + skip = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpHasSlots, genoperandoperand(AddCqR, BaseHeaderSize / BytesPerWord, byteSizeReg)); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), byteSizeReg); + jmpTarget(skip, genoperandoperand(AddRR, Arg1Reg, byteSizeReg)); + + /* check if allocation fits */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, getScavengeThreshold(), byteSizeReg); + jumpNoSpace = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* get result, increment freeStart and write it back. Arg1Reg holds new freeStart, the limit of the object */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, byteSizeReg, freeStartAddress()); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, headerReg, 0, ReceiverResultReg); + + /* now fill */ + + /* #LoadEffectiveAddressMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(LoadEffectiveAddressMwrR, BaseHeaderSize, ReceiverResultReg, Arg1Reg); + fillLoop = genoperandoperandoperand(MoveRMwr, fillReg, 0, Arg1Reg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 8, Arg1Reg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, byteSizeReg); + + /* JumpAbove: */ + genConditionalBranchoperand(JumpAbove, ((sqInt)fillLoop)); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNoSpace, jmpTarget(jumpUnhashed, jmpTarget(jumpFailCuzFixed, jmpTarget(jumpArrayTooBig, jmpTarget(jumpByteTooBig, jmpTarget(jumpLongTooBig, jmpTarget(jumpNElementsNonInt, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))))))); + return 0; +} + + +/* Implement primitiveShallowCopy/primitiveClone for convenient cases: + - the receiver is not a context + - the receiver is not a compiled method + - the result fits in eden (actually below scavengeThreshold) */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveShallowCopy */ +static sqInt +genPrimitiveShallowCopy(void) +{ + AbstractInstruction *continuance; + AbstractInstruction *copyLoop; + sqInt formatReg; + AbstractInstruction *jumpEmpty; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpIsMethod; + AbstractInstruction *jumpNoSpace; + AbstractInstruction *jumpTooBig; + AbstractInstruction *jumpVariable; + sqInt ptrReg; + sqInt quickConstant; + sqInt resultReg; + sqInt slotsReg; + + jumpImmediate = genJumpImmediate(ReceiverResultReg); + resultReg = Arg0Reg; + slotsReg = Arg1Reg; + + /* get freeStart as early as possible so as not to wait later... */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), resultReg); + + /* formatReg := self formatOf: ReceiverResultReg */ + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (ptrReg = (formatReg = SendNumArgsReg)), NoReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), formatReg); + jumpIsMethod = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, indexablePointersFormat(), formatReg); + jumpVariable = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + continuance = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + genGetRawSlotSizeOfNonImminto(ReceiverResultReg, slotsReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, numSlotsMask(), slotsReg); + jumpTooBig = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, slotsReg); + jumpEmpty = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* compute byte size for slots */ + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BaseHeaderSize / BytesPerWord, slotsReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), slotsReg); + + /* AddR:R: */ + genoperandoperand(AddRR, resultReg, slotsReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, getScavengeThreshold(), slotsReg); + + /* check if allocation fits (freeSize + byteSize < scavengeThreshold); scavengeThreshold is constant. */ + jumpNoSpace = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, resultReg, ptrReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, slotsReg, freeStartAddress()); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, BytesPerWord * 2, slotsReg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, ReceiverResultReg, TempReg); + quickConstant = headerForSlotsformatclassIndex(numSlotsMask(), formatMask(), classIndexMask()); + + /* begin AndCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, 0, resultReg); + + /* write back new freeStart; get result. slotsReg holds new freeStart, the limit of the object + set up loop bounds + copy header, masking off irrelevant bits */ + copyLoop = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BytesPerWord, ReceiverResultReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BytesPerWord, ptrReg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, ReceiverResultReg, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, 0, ptrReg); + + /* begin CmpR:R: */ + assert(!((ptrReg == SPReg))); + genoperandoperand(CmpRR, ptrReg, slotsReg); + + /* JumpAboveOrEqual: */ + genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)copyLoop)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, resultReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* If the receiver is variable pointers, fail if its a context, otherwise continue */ + jmpTarget(jumpVariable, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genGetClassIndexOfNonImminto(ReceiverResultReg, ClassReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ClassMethodContextCompactIndex, ClassReg); + + /* JumpNonZero: */ + genConditionalBranchoperand(JumpNonZero, ((sqInt)continuance)); + jmpTarget(jumpImmediate, jmpTarget(jumpNoSpace, jmpTarget(jumpIsMethod, jmpTarget(jumpTooBig, jmpTarget(jumpEmpty, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))))); + return 0; +} + + +/* Generate the code for primitive 173, instVarAt:. Defer to + StackInterpreterPrimitives>>primitiveSlotAt for Contexts. + */ +/* c.f. StackInterpreter>>stSizeOf: SpurMemoryManager>>lengthOf:format: + fixedFieldsOf:format:length: + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSlotAt */ +static sqInt +genPrimitiveSlotAt(void) +{ + AbstractInstruction *convertToIntAndReturn; + sqInt formatReg; + AbstractInstruction *jumpBadIndex; + AbstractInstruction *jumpBytesOutOfBounds; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpIsBytes; + AbstractInstruction *jumpIsContext; + AbstractInstruction *jumpIsMethod; + AbstractInstruction *jumpIsShorts; + AbstractInstruction *jumpIsWords; + AbstractInstruction *jumpLongsOutOfBounds; + AbstractInstruction *jumpNonPointers; + AbstractInstruction *jumpPointersOutOfBounds; + AbstractInstruction *jumpShortsOutOfBounds; + AbstractInstruction *jumpToReturnLargeInteger; + AbstractInstruction *jumpWordsOutOfBounds; + sqInt nSlotsOrElementsReg; + sqInt quickConstant; + + nSlotsOrElementsReg = ClassReg; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpImmediate = genJumpImmediate(ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, Arg1Reg); + jumpBadIndex = genJumpNotSmallInteger(Arg0Reg); + genConvertSmallIntegerToIntegerInReg(Arg1Reg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 1, Arg1Reg); + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), TempReg); + genGetNumSlotsOfinto(ReceiverResultReg, nSlotsOrElementsReg); + + /* dispatch on format in a combination of highest dynamic frequency order first and convenience. + 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, sixtyFourBitIndexableFormat(), formatReg); + jumpNonPointers = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, classIndexMask(), TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ClassMethodContextCompactIndex, TempReg); + + /* Fail to StackInterpreterPrimitives>>primitiveSlotAt for Contexts */ + jumpIsContext = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpPointersOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* index is (formatReg Arg1Reg (0-rel index) * wordSize + baseHeaderSize */ + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), Arg1Reg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, Arg1Reg, ReceiverResultReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNonPointers, genoperandoperand(CmpCqR, firstByteFormat(), formatReg)); + jumpIsBytes = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstShortFormat(), formatReg); + jumpIsShorts = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstLongFormat(), formatReg); + jumpIsWords = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* fall through to objectMemory sixtyFourBitIndexableFormat */ + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpLongsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), Arg1Reg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, Arg1Reg, ReceiverResultReg, ClassReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, TempReg); + quickConstant = (numSmallIntegerBits()) - 1; + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, quickConstant, TempReg); + if (!(setsConditionCodesFor(lastOpcode(), JumpZero))) { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, TempReg); + } + jumpToReturnLargeInteger = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genConvertIntegerInRegtoSmallIntegerInReg(ClassReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpToReturnLargeInteger, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genInstantiate64BitPositiveIntegerValueintoscratchRegmayFail(ClassReg, ReceiverResultReg, TempReg, 0); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsBytes, genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), nSlotsOrElementsReg)); + gAndCqRR(7, formatReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, nSlotsOrElementsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpBytesOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), formatReg); + jumpIsMethod = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BaseHeaderSize, Arg1Reg); + + /* formatReg already contains a value <= 16r1f, so no need to zero it */ + + /* MoveXbr:R:R: */ + genoperandoperandoperand(MoveXbrRR, Arg1Reg, ReceiverResultReg, formatReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, formatReg, ReceiverResultReg); + convertToIntAndReturn = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + genConvertIntegerToSmallIntegerInReg(ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsShorts, gLogicalShiftLeftCqR((shiftForWord()) - 1, nSlotsOrElementsReg)); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, 3, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrElementsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpShortsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg1Reg, ReceiverResultReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg1Reg, ReceiverResultReg); + + /* #MoveM16:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveM16rR, BaseHeaderSize, ReceiverResultReg, ReceiverResultReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)convertToIntAndReturn)); + jmpTarget(jumpIsWords, gLogicalShiftLeftCqR((shiftForWord()) - 2, nSlotsOrElementsReg)); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrElementsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, nSlotsOrElementsReg); + jumpWordsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> ((shiftForWord()) - 1), Arg1Reg); + + /* MoveX32r:R:R: */ + genoperandoperandoperand(MoveX32rRR, Arg1Reg, ReceiverResultReg, TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, ReceiverResultReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)convertToIntAndReturn)); + jmpTarget(jumpLongsOutOfBounds, jmpTarget(jumpPointersOutOfBounds, jmpTarget(jumpBytesOutOfBounds, jmpTarget(jumpIsMethod, jmpTarget(jumpShortsOutOfBounds, jmpTarget(jumpWordsOutOfBounds, jmpTarget(jumpIsContext, jmpTarget(jumpBadIndex, jmpTarget(jumpImmediate, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))))))))); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatAdd */ +static sqInt +genPrimitiveSmallFloatAdd(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(AddRdRd, null, 0) + : genPureFloatArithmeticpreOpCheckboxed(AddRdRd, null, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatDivide */ +static sqInt +genPrimitiveSmallFloatDivide(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(DivRdRd, genDoubleFailIfZeroArgRcvrarg, 0) + : genPureFloatArithmeticpreOpCheckboxed(DivRdRd, genDoubleFailIfZeroArgRcvrarg, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatEqual */ +static sqInt +genPrimitiveSmallFloatEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPEqual, JumpZero, 0, 0) + : genPureFloatComparisoninvertboxed(gJumpFPEqual, 0, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatGreaterOrEqual */ +static sqInt +genPrimitiveSmallFloatGreaterOrEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreaterOrEqual, JumpGreaterOrEqual, 0, 0) + : genPureFloatComparisoninvertboxed(gJumpFPGreaterOrEqual, 0, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatGreaterThan */ +static sqInt +genPrimitiveSmallFloatGreaterThan(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreater, JumpGreater, 0, 0) + : genPureFloatComparisoninvertboxed(gJumpFPGreater, 0, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatLessOrEqual */ +static sqInt +genPrimitiveSmallFloatLessOrEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreaterOrEqual, JumpLessOrEqual, 1, 0) + : genPureFloatComparisoninvertboxed(gJumpFPGreaterOrEqual, 1, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatLessThan */ +static sqInt +genPrimitiveSmallFloatLessThan(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPGreater, JumpLess, 1, 0) + : genPureFloatComparisoninvertboxed(gJumpFPGreater, 1, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatMultiply */ +static sqInt +genPrimitiveSmallFloatMultiply(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(MulRdRd, null, 0) + : genPureFloatArithmeticpreOpCheckboxed(MulRdRd, null, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatNotEqual */ +static sqInt +genPrimitiveSmallFloatNotEqual(void) +{ + return (numericPrimsMixComparison() + ? genFloatComparisonorIntegerComparisoninvertboxed(gJumpFPNotEqual, JumpNonZero, 0, 0) + : genPureFloatComparisoninvertboxed(gJumpFPNotEqual, 0, 0)); +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatSquareRoot */ +static sqInt +genPrimitiveSmallFloatSquareRoot(void) +{ + AbstractInstruction *jumpNegative; + + genGetSmallFloatValueOfscratchinto(ReceiverResultReg, SendNumArgsReg, DPFPReg0); + + /* XorRd:Rd: */ + genoperandoperand(XorRdRd, DPFPReg1, DPFPReg1); + + /* CmpRd:Rd: */ + genoperandoperand(CmpRdRd, DPFPReg0, DPFPReg1); + + /* +0.0 is all zeros */ + jumpNegative = gJumpFPGreater(0); + + /* SqrtRd: */ + genoperand(SqrtRd, DPFPReg0); + genInstantiateFloatValueintoscratchRegscratchRegmayFail(DPFPReg0, ReceiverResultReg, ClassReg, TempReg, 0); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNegative, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveSmallFloatSubtract */ +static sqInt +genPrimitiveSmallFloatSubtract(void) +{ + return (numericPrimsMixArithmetic() + ? genFloatArithmeticpreOpCheckboxed(SubRdRd, null, 0) + : genPureFloatArithmeticpreOpCheckboxed(SubRdRd, null, 0)); +} + + +/* c.f. StackInterpreter>>stSizeOf: SpurMemoryManager>>lengthOf:format: + fixedFieldsOf:format:length: + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveStringAt */ +static sqInt +genPrimitiveStringAt(void) +{ + AbstractInstruction *done; + sqInt formatReg; + AbstractInstruction *jumpBadIndex; + AbstractInstruction *jumpBytesOutOfBounds; + AbstractInstruction *jumpIsBytes; + AbstractInstruction *jumpIsShorts; + AbstractInstruction *jumpNotIndexable; + AbstractInstruction *jumpShortsOutOfBounds; + AbstractInstruction *jumpWordsDone; + AbstractInstruction *jumpWordsOutOfBounds; + AbstractInstruction *jumpWordTooBig; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, Arg1Reg); + jumpBadIndex = genJumpNotSmallInteger(Arg0Reg); + genConvertSmallIntegerToIntegerInReg(Arg1Reg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 1, Arg1Reg); + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), NoReg); + genGetNumSlotsOfinto(ReceiverResultReg, ClassReg); + + /* dispatch on format in a combination of highest dynamic frequency order first and convenience. + 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), formatReg); + jumpIsBytes = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstShortFormat(), formatReg); + jumpIsShorts = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstLongFormat(), formatReg); + jumpNotIndexable = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* fall through to double words */ + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - 2, ClassReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 4) - 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, ClassReg); + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, ClassReg); + jumpWordsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> ((shiftForWord()) - 1), Arg1Reg); + + /* MoveX32r:R:R: */ + genoperandoperandoperand(MoveX32rRR, Arg1Reg, ReceiverResultReg, TempReg); + jumpWordTooBig = jumpNotCharacterUnsignedValueInRegister(TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, ReceiverResultReg); + jumpWordsDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpIsBytes, genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), ClassReg)); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, BytesPerWord - 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, ClassReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, ClassReg); + jumpBytesOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BaseHeaderSize, Arg1Reg); + + /* MoveXbr:R:R: */ + genoperandoperandoperand(MoveXbrRR, Arg1Reg, ReceiverResultReg, ReceiverResultReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, 0xFF, ReceiverResultReg); + jmpTarget(jumpWordsDone, (done = genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + genConvertIntegerToCharacterInReg(ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsShorts, gLogicalShiftLeftCqR((shiftForWord()) - 1, ClassReg)); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 2) - 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, ClassReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, ClassReg); + jumpShortsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg1Reg, ReceiverResultReg); + + /* #MoveM16:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveM16rR, BaseHeaderSize, ReceiverResultReg, ReceiverResultReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)done)); + jmpTarget(jumpWordTooBig, jmpTarget(jumpBytesOutOfBounds, jmpTarget(jumpShortsOutOfBounds, jmpTarget(jumpWordsOutOfBounds, jmpTarget(jumpNotIndexable, jmpTarget(jumpBadIndex, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))))); + return CompletePrimitive; +} + + +/* c.f. StackInterpreter>>stSizeOf: SpurMemoryManager>>lengthOf:format: + fixedFieldsOf:format:length: + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveStringAtPut */ +static sqInt +genPrimitiveStringAtPut(void) +{ + sqInt formatReg; + AbstractInstruction *jumpBadArg; + AbstractInstruction *jumpBadIndex; + AbstractInstruction *jumpBytesOutOfBounds; + AbstractInstruction *jumpBytesOutOfRange; + AbstractInstruction *jumpImmutable; + AbstractInstruction *jumpIsBytes; + AbstractInstruction *jumpIsCompiledMethod; + AbstractInstruction *jumpIsShorts; + AbstractInstruction *jumpNotString; + AbstractInstruction *jumpShortsOutOfBounds; + AbstractInstruction *jumpShortsOutOfRange; + AbstractInstruction *jumpWordsOutOfBounds; + AbstractInstruction *jumpWordsOutOfRange; + sqInt quickConstant; + + /* begin genLoadArgAtDepth:into: */ + assert(1 < (numRegArgs())); + assert(0 < (numRegArgs())); + jumpBadIndex = genJumpNotSmallInteger(Arg0Reg); + jumpBadArg = genJumpNotCharacter(Arg1Reg); + genConvertSmallIntegerToIntegerInReg(Arg0Reg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 1, Arg0Reg); +# if IMMUTABILITY + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), TempReg); + + /* begin genJumpBaseHeaderImmutable: */ + /* TstCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, immutableBitMask(), TempReg); + jumpImmutable = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +# else // IMMUTABILITY + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), NoReg); +# endif // IMMUTABILITY + + genGetNumSlotsOfinto(ReceiverResultReg, ClassReg); + + /* dispatch on format; words and/or bytes. + 0 to 8 = pointer objects, forwarders, reserved. + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstLongFormat(), formatReg); + jumpNotString = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), formatReg); + jumpIsCompiledMethod = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), formatReg); + jumpIsBytes = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstShortFormat(), formatReg); + jumpIsShorts = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* fall through to double words */ + quickConstant = characterObjectOf((1U << (numCharacterBits())) - 1); + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, Arg1Reg); + jumpWordsOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - 2, ClassReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 4) - 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, ClassReg); + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, ClassReg); + jumpWordsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertCharacterToCodeInReg(TempReg); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> ((shiftForWord()) - 1), Arg0Reg); + + /* MoveR:X32r:R: */ + genoperandoperandoperand(MoveRX32rR, TempReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsShorts, gCmpCqR(characterObjectOf(0xFFFF), Arg1Reg)); + jumpShortsOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - 1, ClassReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 2) - 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, ClassReg); + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, ClassReg); + jumpShortsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertCharacterToCodeInReg(TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg0Reg, ReceiverResultReg); + + /* #MoveR:M16:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRM16r, TempReg, BaseHeaderSize, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsBytes, gCmpCqR(characterObjectOf(0xFF), Arg1Reg)); + jumpBytesOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), ClassReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, BytesPerWord - 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, ClassReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, ClassReg); + jumpBytesOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertCharacterToCodeInReg(TempReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BaseHeaderSize, Arg0Reg); + + /* MoveR:Xbr:R: */ + genoperandoperandoperand(MoveRXbrR, TempReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotString, jmpTarget(jumpBytesOutOfRange, jmpTarget(jumpShortsOutOfRange, jmpTarget(jumpWordsOutOfRange, jmpTarget(jumpIsCompiledMethod, jmpTarget(jumpBytesOutOfBounds, jmpTarget(jumpShortsOutOfBounds, jmpTarget(jumpWordsOutOfBounds, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))))))); +# if IMMUTABILITY + jmpTarget(jumpImmutable, ((AbstractInstruction *) (((jumpNotString->operands))[0]))); +# endif + + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, Arg0Reg); + genConvertIntegerToSmallIntegerInReg(Arg0Reg); + jmpTarget(jumpBadArg, jmpTarget(jumpBadIndex, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + +/* Implement primitiveUninitializedNewWithArg for convenient cases: + - the receiver has a hash + - the receiver is variable and not compiled method + - the result fits in eden + See superclass method for dynamic frequencies of formats. + For the moment we implement only arrayFormat, firstByteFormat & + firstLongFormat + */ + + /* CogObjectRepresentationFor64BitSpur>>#genPrimitiveUninitializedNewWithArg */ +static sqInt +genPrimitiveUninitializedNewWithArg(void) +{ + sqInt byteSizeReg; + sqInt fillReg; + sqInt headerReg; + sqInt instSpecReg; + AbstractInstruction *jumpByteFormat; + AbstractInstruction *jumpByteTooBig; + AbstractInstruction *jumpDoubleByteFormat; + AbstractInstruction *jumpDoubleBytePrepDone; + AbstractInstruction *jumpDoubleWordFormat; + AbstractInstruction *jumpDoubleWordPrepDone; + AbstractInstruction *jumpDoubleWordTooBig; + AbstractInstruction *jumpFailCuzFixed; + AbstractInstruction *jumpHasSlots; + AbstractInstruction *jumpLongPrepDone; + AbstractInstruction *jumpLongTooBig; + AbstractInstruction *jumpNElementsNonInt; + AbstractInstruction *jumpNoSpace; + AbstractInstruction *jumpNoSpaceBigObjects; + AbstractInstruction *jumpOverflowHeader; + AbstractInstruction *jumpShortTooBig; + AbstractInstruction *jumpUnhashed; + sqInt maxSlots; + sqInt quickConstant; + AbstractInstruction *skip; + + if (methodOrBlockNumArgs != 1) { + return UnimplementedPrimitive; + } + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* header will contain classIndex/class's hash & format & numSlots/fixed size */ + headerReg = SendNumArgsReg; + + /* Assume there's an available scratch register on 64-bit machines. This holds the saved numFixedFields and then the value to fill with */ + fillReg = Extra0Reg; + assert(fillReg > 0); + + /* inst spec will hold class's instance specification and then byte size */ + instSpecReg = (byteSizeReg = ClassReg); + + /* Allow a max of 1 MB */ + maxSlots = maxSlotsForNewSpaceAlloc(); + + /* get freeStart as early as possible so as not to wait later... */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), Arg1Reg); + + /* get class's hash & fail if 0 */ + genGetHashFieldNonImmOfinto(ReceiverResultReg, headerReg); + jumpUnhashed = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* get index and fail if not a +ve integer */ + jumpNElementsNonInt = genJumpNotSmallInteger(Arg0Reg); + + /* get class's format inst var for inst spec (format field) */ + genLoadSlotsourceRegdestReg(InstanceSpecificationIndex, ReceiverResultReg, instSpecReg); + quickConstant = (fixedFieldsFieldWidth()) + (numSmallIntegerTagBits()); + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, quickConstant, instSpecReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, formatMask(), instSpecReg); + + /* Add format to classIndex/format header now */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, instSpecReg, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* get integer value of num fields in fillReg now */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, fillReg); + genConvertSmallIntegerToIntegerInReg(fillReg); + + /* dispatch on format, failing if not a byte format */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), instSpecReg); + jumpByteFormat = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstShortFormat(), instSpecReg); + jumpDoubleByteFormat = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, sixtyFourBitIndexableFormat(), instSpecReg); + jumpDoubleWordFormat = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstLongFormat(), instSpecReg); + jumpFailCuzFixed = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* allocates a 32-bit array */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, (((usqInt)(maxSlots * 2) << 3) | 1), Arg0Reg); + jumpLongTooBig = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* save num elements/slot size to instSpecReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, fillReg, instSpecReg); + + /* compute odd bits and add into headerReg; oddBits := 2 - nElements bitAnd: 1 */ + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, BytesPerWord / 4, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, instSpecReg, TempReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 4) - 1, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* round up num elements to numSlots in instSpecReg; (numElements / 2) ceiling */ + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, (BytesPerWord / 4) - 1, instSpecReg); + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, (shiftForWord()) - 2, instSpecReg); + + /* go allocate */ + /* allocates a 16-bit array */ + jumpLongPrepDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpDoubleByteFormat, genoperandoperand(CmpCqR, (((usqInt)(maxSlots * 4) << 3) | 1), Arg0Reg)); + jumpShortTooBig = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* save num elements to instSpecReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, fillReg, instSpecReg); + + /* compute odd bits and add into headerReg; oddBits := 4 - nElements bitAnd: 3 */ + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, BytesPerWord / 2, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, instSpecReg, TempReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 2) - 1, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* round up num elements to numSlots in instSpecReg; (numElements / 4) ceiling */ + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, (BytesPerWord / 2) - 1, instSpecReg); + + /* begin LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, (shiftForWord()) - 1, instSpecReg); + + /* go allocate */ + /* allocates a 64-bit array */ + jumpDoubleBytePrepDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpDoubleWordFormat, genoperandoperand(CmpCqR, (((usqInt)maxSlots << 3) | 1), Arg0Reg)); + jumpDoubleWordTooBig = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* save num elements to instSpecReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, fillReg, instSpecReg); + + /* go allocate */ + /* allocates a byte array */ + jumpDoubleWordPrepDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpByteFormat, genoperandoperand(CmpCqR, (((usqInt)(maxSlots * BytesPerWord) << 3) | 1), Arg0Reg)); + jumpByteTooBig = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* save num elements to instSpecReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, fillReg, instSpecReg); + + /* compute odd bits and add into headerReg; oddBits := 8 - nElements bitAnd: 7 */ + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, BytesPerWord, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, instSpecReg, TempReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, BytesPerWord - 1, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, formatShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* round up num elements to numSlots in instSpecReg; (numElements / 8) ceiling */ + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BytesPerWord - 1, instSpecReg); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, shiftForWord(), instSpecReg); + + /* fall through to allocate */ + jmpTarget(jumpDoubleWordPrepDone, jmpTarget(jumpDoubleBytePrepDone, jmpTarget(jumpLongPrepDone, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + + /* if numSlots >= 255 -> overflow header -> handle it */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, numSlotsMask(), instSpecReg); + jumpOverflowHeader = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* fallthrough: allocate objects with < 255 slots + store numSlots to headerReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, instSpecReg, TempReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, numSlotsFullShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* compute byte size; remember 0-sized objects still need 1 slot. */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, byteSizeReg); + jumpHasSlots = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, BaseHeaderSize * 2, byteSizeReg); + skip = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpHasSlots, genoperandoperand(AddCqR, BaseHeaderSize / BytesPerWord, byteSizeReg)); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), byteSizeReg); + jmpTarget(skip, genoperandoperand(AddRR, Arg1Reg, byteSizeReg)); + + /* check if allocation fits */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, getScavengeThreshold(), byteSizeReg); + jumpNoSpace = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* get result, increment freeStart and write it back. Arg1Reg holds new freeStart, the limit of the object */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, byteSizeReg, freeStartAddress()); + + /* write other half of header (numSlots/0 identityHash) */ + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, headerReg, 0, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* allocate object with up to 1 << 16 - 1 slots; numSlots >= 255 + store always 255 to headerReg */ + jmpTarget(jumpOverflowHeader, genoperandoperand(MoveCqR, 0xFF, TempReg)); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, numSlotsFullShift(), TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, TempReg, headerReg); + + /* calculate overflow header; Temp Reg can be resued as it already contains 255 as needed */ + + /* AddR:R: */ + genoperandoperand(AddRR, instSpecReg, TempReg); + + /* compute byte size */ + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, (BaseHeaderSize * 2) / BytesPerWord, byteSizeReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), byteSizeReg); + + /* check if allocation fits */ + + /* AddR:R: */ + genoperandoperand(AddRR, Arg1Reg, byteSizeReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, getScavengeThreshold(), byteSizeReg); + jumpNoSpaceBigObjects = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* get result, increment freeStart and write it back. Arg1Reg holds new freeStart, the limit of the object */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, byteSizeReg, freeStartAddress()); + + /* write overflow part */ + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, 0, ReceiverResultReg); + + /* write header */ + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, headerReg, BaseHeaderSize, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpShortTooBig, jmpTarget(jumpDoubleWordTooBig, jmpTarget(jumpNoSpaceBigObjects, jmpTarget(jumpNoSpace, jmpTarget(jumpUnhashed, jmpTarget(jumpFailCuzFixed, jmpTarget(jumpByteTooBig, jmpTarget(jumpLongTooBig, jmpTarget(jumpNElementsNonInt, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))))))))); + return 0; +} + + +/* In the Pure version, mixed arithmetic with SmallInteger is forbidden */ + + /* CogObjectRepresentationFor64BitSpur>>#genPureFloatArithmetic:preOpCheck:boxed: */ +static NoDbgRegParms sqInt +genPureFloatArithmeticpreOpCheckboxed(sqInt arithmeticOperator, AbstractInstruction *(*preOpCheckOrNil)(int rcvrReg, int argReg), sqInt rcvrBoxed) +{ + AbstractInstruction *doOp; + AbstractInstruction *jumpFailCheck; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpNotBoxedFloat; + AbstractInstruction *jumpNotSmallFloat; + + jumpFailCheck = ((AbstractInstruction *) 0); + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + if (rcvrBoxed) { + genGetDoubleValueOfinto(ReceiverResultReg, DPFPReg0); + } + else { + genGetSmallFloatValueOfscratchinto(ReceiverResultReg, TempReg, DPFPReg0); + } + jumpNotSmallFloat = genJumpNotSmallFloat(Arg0Reg); + genGetSmallFloatValueOfscratchinto(Arg0Reg, TempReg, DPFPReg1); + doOp = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + if (preOpCheckOrNil) { + jumpFailCheck = preOpCheckOrNil(DPFPReg0, DPFPReg1); + } + genoperandoperand(arithmeticOperator, DPFPReg1, DPFPReg0); + genInstantiateFloatValueintoscratchRegscratchRegmayFail(DPFPReg0, ReceiverResultReg, ClassReg, TempReg, 0); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSmallFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpImmediate = genJumpImmediate(Arg0Reg); + genGetCompactClassIndexNonImmOfinto(Arg0Reg, SendNumArgsReg); + genCmpClassFloatCompactIndexR(SendNumArgsReg); + jumpNotBoxedFloat = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genGetDoubleValueOfinto(Arg0Reg, DPFPReg1); + + /* Jump: */ + genoperand(Jump, ((sqInt)doOp)); + jmpTarget(jumpImmediate, jmpTarget(jumpNotBoxedFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + if (preOpCheckOrNil) { + jmpTarget(jumpFailCheck, ((AbstractInstruction *) (((jumpNotBoxedFloat->operands))[0]))); + } + return 0; +} + + +/* In the Pure version, mixed arithmetic with SmallInteger is forbidden */ + + /* CogObjectRepresentationFor64BitSpur>>#genPureFloatComparison:invert:boxed: */ +static NoDbgRegParms sqInt +genPureFloatComparisoninvertboxed(AbstractInstruction *(*jumpFPOpcodeGenerator)(void *), sqInt invertComparison, sqInt rcvrBoxed) +{ + AbstractInstruction *compareFloat; + AbstractInstruction *jumpCond; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpNotBoxedFloat; + AbstractInstruction *jumpNotSmallFloat; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + if (rcvrBoxed) { + genGetDoubleValueOfinto(ReceiverResultReg, DPFPReg0); + } + else { + genGetSmallFloatValueOfscratchinto(ReceiverResultReg, TempReg, DPFPReg0); + } + jumpNotSmallFloat = genJumpNotSmallFloat(Arg0Reg); + genGetSmallFloatValueOfscratchinto(Arg0Reg, TempReg, DPFPReg1); + compareFloat = (invertComparison + ? genoperandoperand(CmpRdRd, DPFPReg0, DPFPReg1) + : genoperandoperand(CmpRdRd, DPFPReg1, DPFPReg0)); + + /* May need to invert for NaNs */ + + /* FP jumps are a little weird */ + jumpCond = jumpFPOpcodeGenerator(0); + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpCond, /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, trueObject(), ReceiverResultReg)); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* not a Small Float, check for Boxed Float argument */ + jmpTarget(jumpNotSmallFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpImmediate = genJumpImmediate(Arg0Reg); + genGetCompactClassIndexNonImmOfinto(Arg0Reg, SendNumArgsReg); + genCmpClassFloatCompactIndexR(SendNumArgsReg); + jumpNotBoxedFloat = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genGetDoubleValueOfinto(Arg0Reg, DPFPReg1); + + /* Jump: */ + genoperand(Jump, ((sqInt)compareFloat)); + jmpTarget(jumpImmediate, jmpTarget(jumpNotBoxedFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + /* CogObjectRepresentationFor64BitSpur>>#genRemoveSmallIntegerTagsInScratchReg: */ +static NoDbgRegParms sqInt +genRemoveSmallIntegerTagsInScratchReg(sqInt scratchReg) +{ + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 1, scratchReg); + return 0; +} + + /* CogObjectRepresentationFor64BitSpur>>#genShiftAwaySmallIntegerTagsInScratchReg: */ +static NoDbgRegParms sqInt +genShiftAwaySmallIntegerTagsInScratchReg(sqInt scratchReg) +{ + /* ArithmeticShiftRightCq:R: */ + genoperandoperand(ArithmeticShiftRightCqR, numTagBits(), scratchReg); + return 0; +} + + +/* Stack looks like + return address */ + + /* CogObjectRepresentationFor64BitSpur>>#genSmallIntegerComparison:orDoubleComparison:invert: */ +static NoDbgRegParms sqInt +genSmallIntegerComparisonorDoubleComparisoninvert(sqInt jumpOpcode, AbstractInstruction * NoDbgRegParms (*jumpFPOpcodeGenerator)(void *), sqInt invertComparison) +{ + AbstractInstruction *compareIntFloat; + AbstractInstruction *jumpAmbiguous; + AbstractInstruction *jumpCond; + AbstractInstruction *jumpNotBoxedFloat; + AbstractInstruction *jumpNotFloatAtAll; + AbstractInstruction *jumpNotSmallFloat; + AbstractInstruction *jumpTrue; + sqInt r; + AbstractInstruction *returnTrue; + + r = genSmallIntegerComparison(jumpOpcode); + if (r < 0) { + return r; + } +# if defined(DPFPReg0) + jumpNotSmallFloat = genJumpNotSmallFloat(Arg0Reg); + genGetSmallFloatValueOfscratchinto(Arg0Reg, TempReg, DPFPReg1); + + /* Case of (int compare: float). Test for ambiguity, that is when (double) intRcvr == floatArg */ + compareIntFloat = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + genConvertSmallIntegerToIntegerInReg(ReceiverResultReg); + + /* ConvertR:Rd: */ + genoperandoperand(ConvertRRd, ReceiverResultReg, DPFPReg0); + + /* CmpRd:Rd: */ + genoperandoperand(CmpRdRd, DPFPReg0, DPFPReg1); + jumpAmbiguous = gJumpFPEqual(0); + + /* Case of non ambiguity, use compareFloat((double) intRcvr,floatArg) + May need to invert for NaNs */ + if (invertComparison) { + /* CmpRd:Rd: */ + genoperandoperand(CmpRdRd, DPFPReg0, DPFPReg1); + } + else { + /* CmpRd:Rd: */ + genoperandoperand(CmpRdRd, DPFPReg1, DPFPReg0); + } + + /* FP jumps are a little weird */ + jumpCond = jumpFPOpcodeGenerator(0); + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpCond, (returnTrue = /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, trueObject(), ReceiverResultReg))); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* Case of ambiguity, use compareInt(intRcvr , (int64) floatArg) */ + jmpTarget(jumpAmbiguous, genoperandoperand(ConvertRdR, DPFPReg1, Arg0Reg)); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, ReceiverResultReg); + jumpTrue = genConditionalBranchoperand(jumpOpcode, 0); + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpTrue, returnTrue); + + /* not a Small Float, check for Boxed Float argument */ + jmpTarget(jumpNotSmallFloat, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpNotFloatAtAll = genJumpImmediate(Arg0Reg); + genGetCompactClassIndexNonImmOfinto(Arg0Reg, SendNumArgsReg); + genCmpClassFloatCompactIndexR(SendNumArgsReg); + jumpNotBoxedFloat = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* It was a Boxed Float, so convert the receiver to double and perform the (int compare: float) operation */ + genGetDoubleValueOfinto(Arg0Reg, DPFPReg1); + + /* Jump: */ + genoperand(Jump, ((sqInt)compareIntFloat)); + + /* not a Float, just let the primitive fall thru failure */ + jmpTarget(jumpNotBoxedFloat, jmpTarget(jumpNotFloatAtAll, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); +# endif // defined(DPFPReg0) + + + /* Fall through on non-SmallInteger argument. Argument may be a Float : let us check or fail + check for Small Float argument */ + return CompletePrimitive; +} + + +/* Get the literal count of a CompiledMethod into headerReg, plus one if + requested. If inBytes is true, scale the count by the word size. Deal with + the possibility of + the method being cogged. */ + + /* CogObjectRepresentationFor64BitSpur>>#getLiteralCountOf:plusOne:inBytes:into:scratch: */ +static NoDbgRegParms sqInt +getLiteralCountOfplusOneinBytesintoscratch(sqInt methodReg, sqInt plusOne, sqInt inBytes, sqInt litCountReg, sqInt scratchReg) +{ + sqInt quickConstant; + + genGetMethodHeaderOfintoscratch(methodReg, litCountReg, scratchReg); + assert((1U << (numTagBits())) == BytesPerWord); + if (inBytes) { + quickConstant = ((sqInt)((usqInt)((alternateHeaderNumLiteralsMask())) << (numTagBits()))); + + /* begin AndCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, litCountReg); + } + else { + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, numTagBits(), litCountReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, alternateHeaderNumLiteralsMask(), litCountReg); + } + if (plusOne) { + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, (inBytes + ? LiteralStart * BytesPerWord + : LiteralStart), litCountReg); + } + return 0; +} + + +/* Answer the relevant inline cache tag for an instance. + c.f. getInlineCacheClassTagFrom:into: & inlineCacheTagForClass: */ + + /* CogObjectRepresentationFor64BitSpur>>#inlineCacheTagForInstance: */ +static NoDbgRegParms sqInt +inlineCacheTagForInstance(sqInt oop) +{ + return (isImmediate(oop) + ? oop & (tagMask()) + : classIndexOf(oop)); +} + + /* CogObjectRepresentationFor64BitSpur>>#log2BytesPerWord */ +static sqInt +log2BytesPerWord(void) +{ + return 3; +} + + +/* Generate the routine that converts selector indices into selector objects. + It is called from the send trampolines. + If the selector index is negative, convert it into a positive index into + the special selectors array and index that. Otherwise, index the current + method. The routine uses Extra0Reg & Extra1Reg, which are available, since + they are not live at point of send. */ + + /* CogObjectRepresentationFor64BitSpur>>#maybeGenerateSelectorIndexDereferenceRoutine */ +static void +maybeGenerateSelectorIndexDereferenceRoutine(void) +{ + sqInt byteOffset; + AbstractInstruction *jumpFullBlock; + AbstractInstruction *jumpNegative; + AbstractInstruction *jumpNotBlock; + sqInt offset; + + zeroOpcodeIndex(); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ClassReg); + jumpNegative = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, FoxMethod, FPReg, Extra0Reg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 2, ClassReg); + + /* #TstCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, MFMethodFlagIsBlockFlag, Extra0Reg); + + /* Change selector index to 1-relative, skipping the method header */ + jumpNotBlock = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* If in a block, need to find the home method... If using full blocks, need to test the cpicHasMNUCaseOrCMIsFullBlock bit */ + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, -(zoneAlignment()), Extra0Reg); + byteOffset = BaseHeaderSize + 1; + + /* #MoveMb:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMbrR, byteOffset, Extra0Reg, Extra1Reg); + + /* #TstCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, 16, Extra1Reg); + jumpFullBlock = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #MoveM16:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveM16rR, 0, Extra0Reg, Extra1Reg); + + /* SubR:R: */ + genoperandoperand(SubRR, Extra1Reg, Extra0Reg); + jmpTarget(jumpNotBlock, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jmpTarget(jumpFullBlock, ((AbstractInstruction *) (((jumpNotBlock->operands))[0]))); + + /* Now fetch the method object and index with the literal index to retrieve the selector */ + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, -(zoneAlignment()), Extra0Reg); + offset = offsetof(CogMethod, methodObject); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, Extra0Reg, Extra1Reg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, ClassReg, Extra1Reg, ClassReg); + + /* RetN: */ + genoperand(RetN, 0); + jmpTarget(jumpNegative, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* NegateR: */ + genoperand(NegateR, ClassReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, 1, ClassReg); + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, specialObjectsOopAddress(), Extra0Reg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 1, ClassReg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, (SpecialSelectors + 1) * BytesPerWord, Extra0Reg, Extra1Reg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, ClassReg, Extra1Reg, ClassReg); + + /* RetN: */ + genoperand(RetN, 0); + + /* Index, including header size */ + ceDereferenceSelectorIndex = methodZoneBase; + outputInstructionsForGeneratedRuntimeAt(ceDereferenceSelectorIndex); + recordGeneratedRunTimeaddress("ceDereferenceSelectorIndex", ceDereferenceSelectorIndex); + recordRunTimeObjectReferences(); +} + + /* CogObjectRepresentationFor64BitSpur>>#numSmallIntegerBits */ +static sqInt +numSmallIntegerBits(void) +{ + return 61; +} + + /* CogObjectRepresentationFor64BitSpur>>#numSmallIntegerTagBits */ +static sqInt +numSmallIntegerTagBits(void) +{ + return 3; +} + + +/* The three valid tag patterns are 1 (SmallInteger), 2 (Character) and 3 + (SmallFloat64). + */ + + /* CogObjectRepresentationFor64BitSpur>>#validInlineCacheTag: */ +static NoDbgRegParms sqInt +validInlineCacheTag(sqInt classIndexOrTagPattern) +{ + return ((classIndexOrTagPattern >= 1) + && (classIndexOrTagPattern <= 3)) + || ((classAtIndex(classIndexOrTagPattern))); +} + + /* CogObjectRepresentationForSpur>>#callStoreCheckTrampoline */ +static void +callStoreCheckTrampoline(void) +{ + AbstractInstruction *abstractInstruction; + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreCheckTrampoline); + (abstractInstruction->annotation = IsRelativeCall); +} + + /* CogObjectRepresentationForSpur>>#checkValidOopReference: */ +static NoDbgRegParms sqInt +checkValidOopReference(sqInt anOop) +{ + return (isImmediate(anOop)) + || ((heapMapAtWord(pointerForOop(anOop))) != 0); +} + + +/* If primIndex has an accessorDepth, or is the external call, + compile and answer the jump to a retry routine. */ + + /* CogObjectRepresentationForSpur>>#compileJumpToRetryOf:onPrimitiveFail: */ +static NoDbgRegParms AbstractInstruction * +compileJumpToRetryOfonPrimitiveFail(void (*primitiveRoutine)(void), sqInt primIndex) +{ + if ((accessorDepthForPrimitiveIndex(primIndex)) >= 0) { + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, primFailCodeAddress(), TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, TempReg); + return genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + } + if (PrimNumberExternalCall == primIndex) { + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, primFailCodeAddress(), TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, PrimErrNoMemory, TempReg); + return genConditionalBranchoperand(JumpZero, ((sqInt)0)); + } + return null; +} + + /* CogObjectRepresentationForSpur>>#couldBeObject: */ +static NoDbgRegParms sqInt +couldBeObject(sqInt literal) +{ + return (isNonImmediate(literal)) + && (oopisGreaterThanOrEqualTo(literal, startOfMemory())); +} + + +/* Create a trampoline to answer the active context that will + answer it if a frame is already married, and create it otherwise. + Assume numArgs is in SendNumArgsReg and ClassReg is free. */ + + /* CogObjectRepresentationForSpur>>#genActiveContextTrampolineLarge:inBlock:called: */ +static NoDbgRegParms usqInt +genActiveContextTrampolineLargeinBlockcalled(sqInt isLarge, sqInt isInBlock, char *aString) +{ + usqInt startAddress; + + startAddress = methodZoneBase; + zeroOpcodeIndex(); + genGetActiveContextLargeinBlock(isLarge, isInBlock); + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress(aString, startAddress); + recordRunTimeObjectReferences(); + return startAddress; +} + + +/* Check the remembered bit of the object in objReg; answer the jump taken if + the bit is already set. + Only need to fetch the byte containing it, which reduces the size of the + mask constant. + */ + + /* CogObjectRepresentationForSpur>>#genCheckRememberedBitOf:scratch: */ +static NoDbgRegParms AbstractInstruction * +genCheckRememberedBitOfscratch(sqInt objReg, sqInt scratchReg) +{ + sqInt mask; + sqInt rememberedBitByteOffset; + + rememberedBitByteOffset = (rememberedBitShift()) / 8; + mask = 1ULL << ((rememberedBitShift()) % 8); + + /* #MoveMb:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMbrR, rememberedBitByteOffset, objReg, scratchReg); + + /* #TstCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, mask, scratchReg); + return genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +} + + /* CogObjectRepresentationForSpur>>#genConvertCharacterToCodeInReg: */ +static NoDbgRegParms sqInt +genConvertCharacterToCodeInReg(sqInt reg) +{ + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, numTagBits(), reg); + return 0; +} + + /* CogObjectRepresentationForSpur>>#genConvertIntegerToCharacterInReg: */ +static NoDbgRegParms sqInt +genConvertIntegerToCharacterInReg(sqInt reg) +{ + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, numTagBits(), reg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, characterTag(), reg); + return 0; +} + + +/* Create a full closure with the given values. */ + + /* CogObjectRepresentationForSpur>>#genCreateFullClosure:numArgs:numCopied:ignoreContext:contextNumArgs:large:inBlock: */ +static NoDbgRegParms sqInt +genCreateFullClosurenumArgsnumCopiedignoreContextcontextNumArgslargeinBlock(sqInt compiledBlock, sqInt numArgs, sqInt numCopied, sqInt ignoreContext, sqInt contextNumArgs, sqInt contextIsLarge, sqInt contextIsBlock) +{ + AbstractInstruction *abstractInstruction; + usqInt byteSize; + usqLong header; + sqInt numSlots; + AbstractInstruction *skip; + + + /* First get thisContext into ReceiverResultReg and thence in ClassReg. */ + if (ignoreContext) { + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, nilObject(), ClassReg); + } + else { + genGetActiveContextNumArgslargeinBlock(contextNumArgs, contextIsLarge, contextIsBlock); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, ClassReg); + } + numSlots = FullClosureFirstCopiedValueIndex + numCopied; + byteSize = smallObjectBytesForSlots(numSlots); + header = headerForSlotsformatclassIndex(numSlots, indexablePointersFormat(), ClassFullBlockClosureCompactIndex); + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), ReceiverResultReg); + + /* begin genStoreHeader:intoNewInstance:using: */ + /* MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, header, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, 0, ReceiverResultReg); + + /* #LoadEffectiveAddressMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(LoadEffectiveAddressMwrR, byteSize, ReceiverResultReg, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, freeStartAddress()); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, getScavengeThreshold(), TempReg); + skip = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceScheduleScavengeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpTarget(skip, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ClassReg, (ClosureOuterContextIndex * BytesPerOop) + BaseHeaderSize, ReceiverResultReg); + + /* genMoveConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(compiledBlock)) + && ((oopisGreaterThan(compiledBlock, classTableRootObj())) + || (oopisLessThan(compiledBlock, nilObject())))) { + annotateobjRef(genoperandoperand(MoveCwR, compiledBlock, TempReg), compiledBlock); + } + else { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, compiledBlock, TempReg); + } + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, (ClosureStartPCIndex * BytesPerOop) + BaseHeaderSize, ReceiverResultReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, (((usqInt)numArgs << 3) | 1), TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, (ClosureNumArgsIndex * BytesPerOop) + BaseHeaderSize, ReceiverResultReg); + return 0; +} + + +/* Make sure that the oop in reg is not forwarded. + Use the fact that isForwardedObjectClassIndexPun is a power of two to save + an instruction. */ +/* maybe a fixup or an instruction */ +/* maybe a fixup or an instruction */ + + /* CogObjectRepresentationForSpur>>#genEnsureOopInRegNotForwarded:scratchReg:ifForwarder:ifNotForwarder: */ +static NoDbgRegParms sqInt +genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(sqInt reg, sqInt scratch, void *fwdJumpTarget, void *nonFwdJumpTargetOrZero) +{ + AbstractInstruction *finished; + AbstractInstruction *imm; + AbstractInstruction *ok; + sqInt quickConstant; + + assert(reg != scratch); + imm = genJumpImmediate(reg); + + /* notionally + self genGetClassIndexOfNonImm: reg into: scratch. + cogit CmpCq: objectMemory isForwardedObjectClassIndexPun R: TempReg. + but the following is an instruction shorter: */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, reg, scratch); + quickConstant = (classIndexMask()) - (isForwardedObjectClassIndexPun()); + + /* begin AndCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, scratch); + ok = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genLoadSlotsourceRegdestReg(0, reg, reg); + + /* Jump: */ + genoperand(Jump, ((sqInt)fwdJumpTarget)); + finished = (((usqInt)nonFwdJumpTargetOrZero) + ? nonFwdJumpTargetOrZero + : genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jmpTarget(imm, jmpTarget(ok, finished)); + return 0; +} + + +/* Make sure that the oop in reg is not forwarded, updating the slot in + objReg with the value. + */ + + /* CogObjectRepresentationForSpur>>#genEnsureOopInRegNotForwarded:scratchReg:updatingSlot:in: */ +static NoDbgRegParms sqInt +genEnsureOopInRegNotForwardedscratchRegupdatingSlotin(sqInt reg, sqInt scratch, sqInt index, sqInt objReg) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *imm; + AbstractInstruction *loop; + AbstractInstruction *ok; + sqInt quickConstant; + + + /* Open-code + self genEnsureOopInRegNotForwarded: reg + scratchReg: scratch + updatingMw: index * objectMemory wordSize + objectMemory baseHeaderSize + r: objReg. + to avoid calling the store check unless the receiver is forwarded. */ + assert((reg != scratch) + && (objReg != scratch)); + loop = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + imm = genJumpImmediate(reg); + + /* notionally + self genGetClassIndexOfNonImm: reg into: scratch. + cogit CmpCq: objectMemory isForwardedObjectClassIndexPun R: TempReg. + but the following is an instruction shorter: */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, reg, scratch); + quickConstant = (classIndexMask()) - (isForwardedObjectClassIndexPun()); + + /* begin AndCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, scratch); + ok = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genLoadSlotsourceRegdestReg(0, reg, reg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, reg, (index * BytesPerWord) + BaseHeaderSize, objReg); + + /* Check that we're meeting the contract of ceStoreCheckContextReceiverTrampoline. */ + assert((reg == Arg0Reg) + && ((scratch == TempReg) + && (objReg == ReceiverResultReg))); + + /* If a frame has been built then (if a link register is in use) the link register has been saved. + If not it must be saved and restored around the call since it will be used to return from the method. */ + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreCheckContextReceiverTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + + /* Jump: */ + genoperand(Jump, ((sqInt)loop)); + jmpTarget(ok, jmpTarget(imm, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return 0; +} + + +/* Create a trampoline to answer the active context that will answer it if a + frame is already married, + and create it otherwise. Assume numArgs is in SendNumArgsReg and ClassReg + is free. + These include machine code implementations of + CoInterpreter>>#marryFrame:SP: + */ + + /* CogObjectRepresentationForSpur>>#genGetActiveContextLarge:inBlock: */ +static NoDbgRegParms sqInt +genGetActiveContextLargeinBlock(sqInt isLarge, sqInt isInBlock) +{ + AbstractInstruction *abstractInstruction; + int callerSavedRegsToBeSaved; + AbstractInstruction *continuation; + AbstractInstruction *exit; + usqLong header; + AbstractInstruction *jumpNeedScavenge; + AbstractInstruction *jumpSingle; + AbstractInstruction *lastInst; + AbstractInstruction *loopHead; + int mustSaveSPReg; + sqInt offset; + sqInt reg; + int registersToBePushed; + int slotSize; + sqInt spareCalleeSavedReg; + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, FoxMethod, FPReg, ClassReg); + + /* #TstCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, MFMethodFlagHasContextFlag, ClassReg); + + /* jump if flag bit not set */ + jumpSingle = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* since the flag bit was set, get the context in the receiver reg and return */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, FoxThisContext, FPReg, ReceiverResultReg); + + /* RetN: */ + genoperand(RetN, 0); + jmpTarget(jumpSingle, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* OK, it doesn't exist; instantiate and initialize it, mimicking CoInterpreter>>#marryFrame:SP: + set the hasContext flag; See CoInterpreter class>>initializeFrameIndices */ + + /* #OrCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(OrCqR, MFMethodFlagHasContextFlag, ClassReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ClassReg, FoxMethod, FPReg); + + /* now get the home CogMethod into ClassReg and save for post-instantiation. */ + switch (isInBlock) { + case InFullBlock: + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 3, ClassReg); + break; + case InVanillaBlock: + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 3, ClassReg); + + /* #MoveM16:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveM16rR, 0, ClassReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, ClassReg); + break; + case 0: + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 1, ClassReg); + break; + default: + error("Case not found and no otherwise clause"); + } + + /* -3 is -(hasContext+isBlock) flags + -1 is hasContext flag + instantiate the context... */ + slotSize = (isLarge + ? LargeContextSlots + : SmallContextSlots); + header = headerForSlotsformatclassIndex(slotSize, indexablePointersFormat(), ClassMethodContextCompactIndex); + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), ReceiverResultReg); + + /* begin genStoreHeader:intoNewInstance:using: */ + /* MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, header, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, 0, ReceiverResultReg); + offset = smallObjectBytesForSlots(slotSize); + + /* begin LoadEffectiveAddressMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(LoadEffectiveAddressMwrR, offset, ReceiverResultReg, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, freeStartAddress()); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, getScavengeThreshold(), TempReg); + jumpNeedScavenge = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* Now initialize the fields of the context. See CoInterpreter>>marryFrame:SP: + sender gets frame pointer as a SmallInteger */ + continuation = genoperandoperandoperand(LoadEffectiveAddressMwrR, 1, FPReg, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, BaseHeaderSize + (SenderIndex * BytesPerOop), ReceiverResultReg); + + /* pc gets frame caller as a SmallInteger */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, FoxSavedFP, FPReg, TempReg); + genSetSmallIntegerTagsIn(TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, BaseHeaderSize + (InstructionPointerIndex * BytesPerOop), ReceiverResultReg); + + /* Set the method field, freeing up ClassReg again, and frame's context field, */ + offset = offsetof(CogMethod, methodObject); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, ClassReg, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, BaseHeaderSize + (MethodIndex * BytesPerWord), ReceiverResultReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ReceiverResultReg, FoxThisContext, FPReg); + + /* Now compute stack pointer; this is stackPointer (- 1 for return pc if a CISC) - framePointer - wordSize (1 each for saved pc, method, context, receiver) + 1 (1-relative) + numArgs + TPR note - the code here is actually doing + context stackPointer := ((((fp - sp) / wordSize) - [3|4]) + num args) asSmallInteger */ + gSubRRR(SPReg, FPReg, TempReg); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, log2BytesPerWord(), TempReg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 4, TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, SendNumArgsReg, TempReg); + + /* TempReg := FPReg - SPReg */ + genConvertIntegerToSmallIntegerInReg(TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, BaseHeaderSize + (StackPointerIndex * BytesPerOop), ReceiverResultReg); + + /* Set closureOrNil to either the stacked receiver or nil */ + if (isInBlock > 0) { + /* #LoadEffectiveAddressMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(LoadEffectiveAddressMwrR, 2, SendNumArgsReg, TempReg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, TempReg, FPReg, TempReg); + } + else { + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, nilObject(), TempReg); + } + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, BaseHeaderSize + (ClosureIndex * BytesPerOop), ReceiverResultReg); + + /* Set the receiver */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, FoxMFReceiver, FPReg, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, BaseHeaderSize + (ReceiverIndex * BytesPerOop), ReceiverResultReg); + + /* Now copy the arguments. This is tricky because of the shortage of registers,. ClassReg ranges + from 1 to numArgs (SendNumArgsReg), and from ReceiverIndex + 1 to ReceiverIndex + numArgs. + 1 to: numArgs do: + [:i| + temp := longAt(FPReg + ((SendNumArgs - i + 2) * wordSize)). +2 for saved pc and savedfp + longAtput(FPReg + FoxMFReceiver + (i * wordSize), temp)] + TPR note: this is a prime candidate for passing off to the backend to do at least faintly optimal code */ + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 1, ClassReg); + + /* begin CmpR:R: */ + assert(!(0 /* (SendNumArgsReg = SPReg) */)); + loopHead = genoperandoperand(CmpRR, SendNumArgsReg, ClassReg); + exit = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SendNumArgsReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, ClassReg, TempReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 2, TempReg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, TempReg, FPReg, TempReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ReceiverIndex + (BaseHeaderSize / BytesPerWord), ClassReg); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, TempReg, ClassReg, ReceiverResultReg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, (ReceiverIndex + (BaseHeaderSize / BytesPerWord)) - 1, ClassReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)loopHead)); + + /* +2 for saved fp and saved pc + Now convert ClassReg from frame index to context index + convert back adding 1 ;-) */ + jmpTarget(exit, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* Now compute number of fields to initialize to nil; set ClassReg to the destination index in the new context... + destination index is index of first non-arg temp, hence add SendNumArgs and header words to ReceiverIndex + 1 */ + gAddCqRR((ReceiverIndex + 1) + (BaseHeaderSize / BytesPerWord), SendNumArgsReg, ClassReg); + gSubRRR(SPReg, FPReg, SendNumArgsReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, nilObject(), TempReg); + + /* ...and loop filling the context's temp slots with nils. N.B. if CISC there is a retpc on the stack */ + loopHead = genoperandoperand(SubCqR, BytesPerWord, SendNumArgsReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, FoxSavedFP - FoxMFReceiver, SendNumArgsReg); + exit = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, TempReg, ClassReg, ReceiverResultReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, ClassReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)loopHead)); + jmpTarget(exit, genoperand(RetN, 0)); + jmpTarget(jumpNeedScavenge, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin saveRegistersInMask:around: */ + spareCalleeSavedReg = 0; + callerSavedRegsToBeSaved = CallerSavedRegisterMask & (((1U << ReceiverResultReg) | (1U << SendNumArgsReg)) | (1U << ClassReg)); + registersToBePushed = callerSavedRegsToBeSaved; + reg = 0; + while (registersToBePushed != 0) { + if (((registersToBePushed & 1) != 0)) { + /* PushR: */ + genoperand(PushR, reg); + } + reg += 1; + registersToBePushed = ((sqInt)(registersToBePushed)) >> 1; + } + + /* If the backEnd doesn't use the native SP register in generated code, and the non-native + SP is not callee-saved, then it too must be saved and restored around the call. */ + mustSaveSPReg = 0 /* (usesNativeSPReg not) */; + if (mustSaveSPReg) { + spareCalleeSavedReg = availableRegisterOrNoneIn(((ABICalleeSavedRegisterMask | ((1U << SPReg))) - ((1U << SPReg)))); + assert(!((spareCalleeSavedReg == NoReg))); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SPReg, spareCalleeSavedReg); + } + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceScheduleScavengeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + lastInst = abstractInstruction; + if (mustSaveSPReg) { + lastInst = genoperandoperand(MoveRR, spareCalleeSavedReg, SPReg); + } + while (reg > 0) { + reg -= 1; + if (((callerSavedRegsToBeSaved & (1ULL << reg)) != 0)) { + lastInst = genoperand(PopR, reg); + } + } + + /* Jump: */ + genoperand(Jump, ((sqInt)continuation)); + return 0; +} + + +/* Get the active context into ReceiverResultReg, creating it if necessary. */ + + /* CogObjectRepresentationForSpur>>#genGetActiveContextNumArgs:large:inBlock: */ +static NoDbgRegParms sqInt +genGetActiveContextNumArgslargeinBlock(sqInt numArgs, sqInt isLargeContext, sqInt isInBlock) +{ + AbstractInstruction *abstractInstruction; + sqInt routine; + + if (isLargeContext) { + switch (isInBlock) { + case 0: + routine = ceLargeActiveContextInMethodTrampoline; + break; + case InVanillaBlock: + routine = ceLargeActiveContextInBlockTrampoline; + break; + case InFullBlock: + routine = ceLargeActiveContextInFullBlockTrampoline; + break; + default: + error("Case not found and no otherwise clause"); + } + } + else { + switch (isInBlock) { + case 0: + routine = ceSmallActiveContextInMethodTrampoline; + break; + case InVanillaBlock: + routine = ceSmallActiveContextInBlockTrampoline; + break; + case InFullBlock: + routine = ceSmallActiveContextInFullBlockTrampoline; + break; + default: + error("Case not found and no otherwise clause"); + } + } + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, numArgs, SendNumArgsReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, routine); + (abstractInstruction->annotation = IsRelativeCall); + return 0; +} + + /* CogObjectRepresentationForSpur>>#genGetBits:ofFormatByteOf:into: */ +static NoDbgRegParms sqInt +genGetBitsofFormatByteOfinto(sqInt mask, sqInt sourceReg, sqInt destReg) +{ + /* #MoveMb:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMbrR, 3, sourceReg, destReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, mask, destReg); + return 0; +} + + +/* Fetch the instance's class index into destReg. */ + + /* CogObjectRepresentationForSpur>>#genGetClassIndexOfNonImm:into: */ +static NoDbgRegParms sqInt +genGetClassIndexOfNonImminto(sqInt sourceReg, sqInt destReg) +{ + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, sourceReg, destReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, classIndexMask(), destReg); + return 0; +} + + +/* Fetch the class object whose index is in instReg into destReg. + It is non-obvious, but the Cogit assumes loading a class does not involve + a runtime call, so do not call classAtIndex: */ + + /* CogObjectRepresentationForSpur>>#genGetClassObjectOfClassIndex:into:scratchReg: */ +static NoDbgRegParms sqInt +genGetClassObjectOfClassIndexintoscratchReg(sqInt instReg, sqInt destReg, sqInt scratchReg) +{ + assert(instReg != destReg); + assert(instReg != scratchReg); + assert(destReg != scratchReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, instReg, scratchReg); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, classTableMajorIndexShift(), scratchReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), scratchReg); + assert(!(shouldAnnotateObjectReference(classTableRootObj()))); + if (isWithinMwOffsetRange(backEnd, (classTableRootObj()) + BaseHeaderSize)) { + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, (classTableRootObj()) + BaseHeaderSize, scratchReg, destReg); + } + else { + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, classTableRootObj(), scratchReg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BaseHeaderSize, scratchReg, destReg); + } + + /* MoveR:R: */ + genoperandoperand(MoveRR, instReg, scratchReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, classTableMinorIndexMask(), scratchReg); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), scratchReg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, scratchReg, destReg, destReg); + return 0; +} + + +/* Fetch the instance's class into destReg. If the instance is not the + receiver and is forwarded, follow forwarding. */ + + /* CogObjectRepresentationForSpur>>#genGetClassObjectOf:into:scratchReg:mayBeAForwarder: */ +static NoDbgRegParms sqInt +genGetClassObjectOfintoscratchRegmayBeAForwarder(sqInt instReg, sqInt destReg, sqInt scratchReg, sqInt mayBeForwarder) +{ + AbstractInstruction *jumpIsImm; + AbstractInstruction *jumpNotForwarded; + AbstractInstruction *loop; + + if ((instReg == destReg) + || ((instReg == scratchReg) + || (destReg == scratchReg))) { + return BadRegisterSet; + } + loop = genoperandoperand(MoveRR, instReg, scratchReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, tagMask(), scratchReg); + jumpIsImm = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Get least significant half of header word in destReg */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, instReg, scratchReg); + + /* mask off class index */ + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, classIndexMask(), scratchReg); + if (mayBeForwarder) { + /* if it is forwarded... */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, isForwardedObjectClassIndexPun(), scratchReg); + jumpNotForwarded = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* ...follow the forwarding pointer and loop to fetch its classIndex */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BaseHeaderSize, instReg, instReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)loop)); + jmpTarget(jumpNotForwarded, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } + jmpTarget(jumpIsImm, genoperandoperand(MoveRR, scratchReg, destReg)); + if (scratchReg) { + genGetClassObjectOfClassIndexintoscratchReg(destReg, scratchReg, TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, scratchReg, destReg); + } + else { + /* PushR: */ + genoperand(PushR, instReg); + genGetClassObjectOfClassIndexintoscratchReg(destReg, instReg, TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, instReg, destReg); + + /* PopR: */ + genoperand(PopR, instReg); + } + return 0; +} + + /* CogObjectRepresentationForSpur>>#genGetClassTagOf:into:scratchReg: */ +static NoDbgRegParms AbstractInstruction * +genGetClassTagOfintoscratchReg(sqInt instReg, sqInt destReg, sqInt scratchReg) +{ + return genGetInlineCacheClassTagFromintoforEntry(instReg, destReg, 1); +} + + +/* Fetch the instance's class index into destReg. */ + + /* CogObjectRepresentationForSpur>>#genGetCompactClassIndexNonImmOf:into: */ +static NoDbgRegParms sqInt +genGetCompactClassIndexNonImmOfinto(sqInt instReg, sqInt destReg) +{ + return genGetClassIndexOfNonImminto(instReg, destReg); +} + + /* CogObjectRepresentationForSpur>>#genGetDoubleValueOf:into: */ +static NoDbgRegParms sqInt +genGetDoubleValueOfinto(sqInt srcReg, sqInt destFPReg) +{ + /* #MoveM64:r:Rd: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveM64rRd, BaseHeaderSize, srcReg, destFPReg); + return 0; +} + + +/* Get the format field of the object in srcReg into destReg. + srcReg may equal destReg. */ + + /* CogObjectRepresentationForSpur>>#genGetFormatOf:into: */ +static NoDbgRegParms sqInt +genGetFormatOfinto(sqInt srcReg, sqInt destReg) +{ + return genGetBitsofFormatByteOfinto(formatMask(), srcReg, destReg); +} + + +/* Get the format of the object in sourceReg into destReg. If + scratchRegOrNone is not NoReg, load at least the least significant 32-bits + (64-bits in 64-bits) of the + header word, which contains the format, into scratchRegOrNone. */ + + /* CogObjectRepresentationForSpur>>#genGetFormatOf:into:leastSignificantHalfOfBaseHeaderIntoScratch: */ +static NoDbgRegParms sqInt +genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(sqInt sourceReg, sqInt destReg, sqInt scratchRegOrNone) +{ + if (scratchRegOrNone == NoReg) { + /* #MoveMb:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMbrR, 3, sourceReg, destReg); + } + else { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, sourceReg, scratchRegOrNone); + gLogicalShiftRightCqRR(formatShift(), scratchRegOrNone, destReg); + } + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, formatMask(), destReg); + return 0; +} + + +/* Get the size in word-sized slots of the object in srcReg into destReg. + srcReg may equal destReg. */ + + /* CogObjectRepresentationForSpur>>#genGetNumSlotsOf:into: */ +static NoDbgRegParms sqInt +genGetNumSlotsOfinto(sqInt srcReg, sqInt destReg) +{ + AbstractInstruction *jmp; + + assert(srcReg != destReg); + genGetRawSlotSizeOfNonImminto(srcReg, destReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, numSlotsMask(), destReg); + jmp = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + genGetOverflowSlotsOfinto(srcReg, destReg); + jmpTarget(jmp, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + +/* The raw numSlots field is the most significant byte of the 64-bit header + word. MoveMbrR zero-extends. */ + + /* CogObjectRepresentationForSpur>>#genGetRawSlotSizeOfNonImm:into: */ +static NoDbgRegParms sqInt +genGetRawSlotSizeOfNonImminto(sqInt sourceReg, sqInt destReg) +{ + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, destReg); + + /* #MoveMb:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMbrR, 7, sourceReg, destReg); + return 0; +} + + /* CogObjectRepresentationForSpur>>#genJumpImmediate: */ +static NoDbgRegParms AbstractInstruction * +genJumpImmediate(sqInt aRegister) +{ + /* #TstCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, tagMask(), aRegister); + return genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +} + + /* CogObjectRepresentationForSpur>>#genJumpImmutable:scratchReg: */ +#if IMMUTABILITY +static NoDbgRegParms AbstractInstruction * +genJumpImmutablescratchReg(sqInt sourceReg, sqInt scratchReg) +{ + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, sourceReg, scratchReg); + + /* begin genJumpBaseHeaderImmutable: */ + /* TstCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, immutableBitMask(), scratchReg); + return genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +} +#endif /* IMMUTABILITY */ + + /* CogObjectRepresentationForSpur>>#genJumpMutable:scratchReg: */ +#if IMMUTABILITY +static NoDbgRegParms AbstractInstruction * +genJumpMutablescratchReg(sqInt sourceReg, sqInt scratchReg) +{ + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, sourceReg, scratchReg); + + /* begin genJumpBaseHeaderMutable: */ + /* TstCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, immutableBitMask(), scratchReg); + return genConditionalBranchoperand(JumpZero, ((sqInt)0)); +} +#endif /* IMMUTABILITY */ + + +/* Generate a call to code that allocates a new Array of size. + The Array should be initialized with nils iff initialize is true. + The size arg is passed in SendNumArgsReg, the result + must come back in ReceiverResultReg. */ + + /* CogObjectRepresentationForSpur>>#genNewArrayOfSize:initialized: */ +static NoDbgRegParms sqInt +genNewArrayOfSizeinitialized(sqInt size, sqInt initialize) +{ + AbstractInstruction *abstractInstruction; + usqLong header; + sqInt i; + sqInt offset; + AbstractInstruction *skip; + + assert(size < (numSlotsMask())); + header = headerForSlotsformatclassIndex(size, arrayFormat(), ClassArrayCompactIndex); + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), ReceiverResultReg); + + /* begin genStoreHeader:intoNewInstance:using: */ + /* MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, header, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, 0, ReceiverResultReg); + if (initialize + && (size > 0)) { + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, nilObject(), TempReg); + for (i = 0; i < size; i += 1) { + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, (i * BytesPerWord) + BaseHeaderSize, ReceiverResultReg); + } + } + offset = smallObjectBytesForSlots(size); + + /* begin LoadEffectiveAddressMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(LoadEffectiveAddressMwrR, offset, ReceiverResultReg, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, freeStartAddress()); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, getScavengeThreshold(), TempReg); + skip = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceScheduleScavengeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpTarget(skip, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + +/* Create a closure with the given startpc, numArgs and numCopied + within a context with ctxtNumArgs, large if isLargeCtxt that is in a + block if isInBlock. Do /not/ initialize the copied values. */ + + /* CogObjectRepresentationForSpur>>#genNoPopCreateClosureAt:numArgs:numCopied:contextNumArgs:large:inBlock: */ +static NoDbgRegParms sqInt +genNoPopCreateClosureAtnumArgsnumCopiedcontextNumArgslargeinBlock(sqInt bcpc, sqInt numArgs, sqInt numCopied, sqInt ctxtNumArgs, sqInt isLargeCtxt, sqInt isInBlock) +{ + AbstractInstruction *abstractInstruction; + usqInt byteSize; + usqLong header; + sqInt numSlots; + AbstractInstruction *skip; + + + /* First get thisContext into ReceiverResultRega and thence in ClassReg. */ + genGetActiveContextNumArgslargeinBlock(ctxtNumArgs, isLargeCtxt, isInBlock); + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, ClassReg); + numSlots = ClosureFirstCopiedValueIndex + numCopied; + byteSize = smallObjectBytesForSlots(numSlots); + header = headerForSlotsformatclassIndex(numSlots, indexablePointersFormat(), ClassBlockClosureCompactIndex); + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), ReceiverResultReg); + + /* begin genStoreHeader:intoNewInstance:using: */ + /* MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, header, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, 0, ReceiverResultReg); + + /* #LoadEffectiveAddressMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(LoadEffectiveAddressMwrR, byteSize, ReceiverResultReg, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, freeStartAddress()); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, getScavengeThreshold(), TempReg); + skip = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceScheduleScavengeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpTarget(skip, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ClassReg, (ClosureOuterContextIndex * BytesPerOop) + BaseHeaderSize, ReceiverResultReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, (((usqInt)bcpc << 3) | 1), TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, (ClosureStartPCIndex * BytesPerOop) + BaseHeaderSize, ReceiverResultReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, (((usqInt)numArgs << 3) | 1), TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, (ClosureNumArgsIndex * BytesPerOop) + BaseHeaderSize, ReceiverResultReg); + return 0; +} + + /* CogObjectRepresentationForSpur>>#genPrimitiveAsCharacter */ +static sqInt +genPrimitiveAsCharacter(void) +{ + AbstractInstruction *jumpNotInt; + AbstractInstruction *jumpOutOfRange; + sqInt reg; + + jumpNotInt = ((AbstractInstruction *) 0); + if (methodOrBlockNumArgs) { + if (methodOrBlockNumArgs > 1) { + return UnimplementedPrimitive; + } + reg = Arg0Reg; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpNotInt = genJumpNotSmallInteger(reg); + } + else { + reg = ReceiverResultReg; + } + + /* MoveR:R: */ + genoperandoperand(MoveRR, reg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + jumpOutOfRange = jumpNotCharacterUnsignedValueInRegister(TempReg); + genConvertSmallIntegerToCharacterInReg(reg); + if (reg != ReceiverResultReg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, reg, ReceiverResultReg); + } + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpOutOfRange, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + if (reg != ReceiverResultReg) { + jmpTarget(jumpNotInt, ((AbstractInstruction *) (((jumpOutOfRange->operands))[0]))); + } + return CompletePrimitive; +} + + /* CogObjectRepresentationForSpur>>#genPrimitiveAsFloat */ +static sqInt +genPrimitiveAsFloat(void) +{ + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* ConvertR:Rd: */ + genoperandoperand(ConvertRRd, TempReg, DPFPReg0); + genInstantiateFloatValueintoscratchRegscratchRegmayFail(DPFPReg0, ReceiverResultReg, ClassReg, TempReg, 0); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + return UnfailingPrimitive; +} + + +/* Generate primitive 60, at: with unsigned access for pure bits classes. */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveAt */ +static sqInt +genPrimitiveAt(void) +{ + return genPrimitiveAtSigned(0); +} + + +/* Generate primitive 61, at:put: with unsigned access for pure bits classes. */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveAtPut */ +static sqInt +genPrimitiveAtPut(void) +{ + return genPrimitiveAtPutSigned(0); +} + + /* CogObjectRepresentationForSpur>>#genPrimitiveFloatSquareRoot */ +static sqInt +genPrimitiveFloatSquareRoot(void) +{ + genGetDoubleValueOfinto(ReceiverResultReg, DPFPReg0); + + /* SqrtRd: */ + genoperand(SqrtRd, DPFPReg0); + genInstantiateFloatValueintoscratchRegscratchRegmayFail(DPFPReg0, ReceiverResultReg, ClassReg, TempReg, 0); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + return UnfailingPrimitive; +} + + /* CogObjectRepresentationForSpur>>#genPrimitiveIdenticalOrNotIf: */ +static NoDbgRegParms sqInt +genPrimitiveIdenticalOrNotIf(sqInt orNot) +{ + AbstractInstruction *comp; + AbstractInstruction *jumpCmp; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + assert(!(0 /* (Arg0Reg = SPReg) */)); + comp = genoperandoperand(CmpRR, Arg0Reg, ReceiverResultReg); + if (orNot) { + jumpCmp = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* genEnsureOopInRegNotForwarded:scratchReg:jumpBackTo: */ + genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(Arg0Reg, TempReg, comp, 0); + } + else { + jumpCmp = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + } + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, trueObject(), ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpCmp, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + if (!orNot) { + /* genEnsureOopInRegNotForwarded:scratchReg:jumpBackTo: */ + genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(Arg0Reg, TempReg, comp, 0); + } + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + return UnfailingPrimitive; +} + + +/* Generate primitive 164, at: with signed access for pure bits classes. */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveIntegerAt */ +static sqInt +genPrimitiveIntegerAt(void) +{ + return genPrimitiveAtSigned(1); +} + + +/* Generate primitive 165, at:put: with signed access for pure bits classes. */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveIntegerAtPut */ +static sqInt +genPrimitiveIntegerAtPut(void) +{ + return genPrimitiveAtPutSigned(1); +} + + +/* */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveMakePoint */ +static sqInt +genPrimitiveMakePoint(void) +{ + sqInt allocSize; + AbstractInstruction *jumpFail; + usqLong newPointHeader; + sqInt resultReg; + sqInt scratchReg; + + resultReg = ClassReg; + scratchReg = SendNumArgsReg; + + /* */ + allocSize = BaseHeaderSize + (BytesPerWord * 2); + newPointHeader = headerForSlotsformatclassIndex(2, nonIndexablePointerFormat(), ClassPointCompactIndex); + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, freeStartAddress(), resultReg); + + /* #LoadEffectiveAddressMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(LoadEffectiveAddressMwrR, allocSize, resultReg, scratchReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, getScavengeThreshold(), scratchReg); + jumpFail = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, scratchReg, freeStartAddress()); + + /* begin genStoreHeader:intoNewInstance:using: */ + /* MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, newPointHeader, scratchReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, scratchReg, 0, resultReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ReceiverResultReg, BaseHeaderSize, resultReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, Arg0Reg, BaseHeaderSize + BytesPerWord, resultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, resultReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpFail, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + /* CogObjectRepresentationForSpur>>#genPrimitiveObjectAt */ +static sqInt +genPrimitiveObjectAt(void) +{ + sqInt headerReg; + AbstractInstruction *jumpBadIndex; + AbstractInstruction *jumpBounds; + AbstractInstruction *jumpNotHeaderIndex; + sqInt quickConstant; + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + jumpBadIndex = genJumpNotSmallInteger(Arg0Reg); + genGetMethodHeaderOfintoscratch(ReceiverResultReg, (headerReg = Arg1Reg), TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, (((usqInt)1 << 3) | 1), Arg0Reg); + jumpNotHeaderIndex = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, headerReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotHeaderIndex, gAndCqR((((usqInt)(alternateHeaderNumLiteralsMask()) << 3) | 1), headerReg)); + quickConstant = ((((usqInt)1 << 3) | 1)) - (smallIntegerTag()); + + /* begin SubCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, quickConstant, Arg0Reg); + assert(!((headerReg == SPReg))); + genoperandoperand(CmpRR, headerReg, Arg0Reg); + jumpBounds = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + genConvertSmallIntegerToIntegerInReg(Arg0Reg); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), Arg0Reg); + + /* MoveXwr:R:R: */ + genoperandoperandoperand(MoveXwrRR, Arg0Reg, ReceiverResultReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpBounds, gAddCqR(((((usqInt)1 << 3) | 1)) - (smallIntegerTag()), Arg0Reg)); + jmpTarget(jumpBadIndex, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + +/* c.f. StackInterpreter>>stSizeOf: lengthOf:baseHeader:format: + fixedFieldsOf:format:length: + */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveSize */ +static sqInt +genPrimitiveSize(void) +{ + AbstractInstruction *jump32BitLongsDone; + AbstractInstruction *jump64BitLongsDone; + AbstractInstruction *jumpArrayDone; + AbstractInstruction *jumpBytesDone; + AbstractInstruction *jumpHasFixedFields; + AbstractInstruction *jumpImm; + AbstractInstruction *jumpIs32BitLongs; + AbstractInstruction *jumpIsBytes; + AbstractInstruction *jumpIsContext; + AbstractInstruction *jumpIsContext1; + AbstractInstruction *jumpIsShorts; + AbstractInstruction *jumpNotIndexable; + AbstractInstruction *jumpNotIndexable1; + AbstractInstruction *jumpShortsDone; + + jumpImm = genJumpImmediate(ReceiverResultReg); + + /* begin genGetSizeOf:into:formatReg:scratchReg:abortJumpsInto: */ + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, SendNumArgsReg, TempReg); + genGetNumSlotsOfinto(ReceiverResultReg, ClassReg); + + /* dispatch on format in a combination of highest dynamic frequency order first and convenience. + 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), SendNumArgsReg); + jumpIsBytes = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, arrayFormat(), SendNumArgsReg); + jumpArrayDone = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + jumpNotIndexable1 = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, weakArrayFormat(), SendNumArgsReg); + jumpHasFixedFields = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstShortFormat(), SendNumArgsReg); + jumpIsShorts = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstLongFormat(), SendNumArgsReg); + jumpIs32BitLongs = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, sixtyFourBitIndexableFormat(), SendNumArgsReg); + jump64BitLongsDone = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + jmpTarget(jumpNotIndexable1, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpNotIndexable1 = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpIsBytes, genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), ClassReg)); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, BytesPerWord - 1, SendNumArgsReg); + + /* SubR:R: */ + genoperandoperand(SubRR, SendNumArgsReg, ClassReg); + jumpBytesDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpIsShorts, gLogicalShiftLeftCqR((shiftForWord()) - 1, ClassReg)); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (((usqInt)(BytesPerWord)) >> 1) - 1, SendNumArgsReg); + + /* SubR:R: */ + genoperandoperand(SubRR, SendNumArgsReg, ClassReg); + jumpShortsDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpIs32BitLongs, gLogicalShiftLeftCqR((shiftForWord()) - 2, ClassReg)); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (((usqInt)(BytesPerWord)) >> 2) - 1, SendNumArgsReg); + + /* SubR:R: */ + genoperandoperand(SubRR, SendNumArgsReg, ClassReg); + jump32BitLongsDone = genoperand(Jump, ((sqInt)0)); + + /* formatReg contains fmt, now up for grabs. + destReg contains numSlots, precious. + sourceReg must be preserved */ + jmpTarget(jumpHasFixedFields, genoperandoperand(AndCqR, classIndexMask(), TempReg)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, SendNumArgsReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ClassMethodContextCompactIndex, TempReg); + jumpIsContext1 = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + genGetClassObjectOfClassIndexintoscratchReg(SendNumArgsReg, Extra0Reg, TempReg); + genLoadSlotsourceRegdestReg(InstanceSpecificationIndex, Extra0Reg, SendNumArgsReg); + genConvertSmallIntegerToIntegerInReg(SendNumArgsReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, fixedFieldsOfClassFormatMask(), SendNumArgsReg); + + /* SubR:R: */ + genoperandoperand(SubRR, SendNumArgsReg, ClassReg); + jmpTarget(jumpArrayDone, jmpTarget(jump64BitLongsDone, jmpTarget(jump32BitLongsDone, jmpTarget(jumpShortsDone, jmpTarget(jumpBytesDone, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))))); + jumpNotIndexable = jumpNotIndexable1; + jumpIsContext = jumpIsContext1; + genConvertIntegerInRegtoSmallIntegerInReg(ClassReg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpImm, jmpTarget(jumpNotIndexable, jmpTarget(jumpIsContext, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + return CompletePrimitive; +} + + +/* primitiveCompareWith: */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveStringCompareWith */ +static sqInt +genPrimitiveStringCompareWith(void) +{ + AbstractInstruction *instr; + AbstractInstruction *jump; + AbstractInstruction *jumpAbove; + AbstractInstruction *jumpIncorrectFormat1; + AbstractInstruction *jumpIncorrectFormat2; + AbstractInstruction *jumpIncorrectFormat3; + AbstractInstruction *jumpIncorrectFormat4; + AbstractInstruction *jumpMidFailure; + AbstractInstruction *jumpSuccess; + sqInt minSizeReg; + sqInt string1CharOrByteSizeReg; + sqInt string1Reg; + sqInt string2CharOrByteSizeReg; + sqInt string2Reg; + + + /* I redefine those name to ease program comprehension */ + string1Reg = ReceiverResultReg; + string2Reg = Arg0Reg; + string1CharOrByteSizeReg = Arg1Reg; + string2CharOrByteSizeReg = ClassReg; + minSizeReg = SendNumArgsReg; + + /* Load arguments in reg */ + + /* begin genLoadArgAtDepth:into: */ + assert(0 < (numRegArgs())); + + /* checks if string1 is a byteobject and get its size in bytes */ + genGetFormatOfinto(string1Reg, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), TempReg); + jumpIncorrectFormat1 = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), TempReg); + jumpIncorrectFormat2 = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + genGetNumSlotsOfinto(string1Reg, string1CharOrByteSizeReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), string1CharOrByteSizeReg); + gAndCqRR(BytesPerWord - 1, TempReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, string1CharOrByteSizeReg); + genGetFormatOfinto(string2Reg, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), TempReg); + jumpIncorrectFormat3 = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), TempReg); + jumpIncorrectFormat4 = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + genGetNumSlotsOfinto(string2Reg, string2CharOrByteSizeReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), string2CharOrByteSizeReg); + gAndCqRR(BytesPerWord - 1, TempReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, string2CharOrByteSizeReg); + + /* Type and number of arguments are correct + Compute the min */ + + /* begin CmpR:R: */ + assert(!((string1CharOrByteSizeReg == SPReg))); + genoperandoperand(CmpRR, string1CharOrByteSizeReg, string2CharOrByteSizeReg); + jumpAbove = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, string1CharOrByteSizeReg, minSizeReg); + jump = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpAbove, genoperandoperand(MoveRR, string2CharOrByteSizeReg, minSizeReg)); + jmpTarget(jump, genoperandoperand(CmpCqR, 0, minSizeReg)); + + /* if one of the string is empty, no need to go through the comparing loop */ + /* Compare the bytes */ + jumpSuccess = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, BaseHeaderSize, TempReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BaseHeaderSize, minSizeReg); + instr = genoperandoperandoperand(MoveXbrRR, TempReg, string1Reg, string1CharOrByteSizeReg); + + /* MoveXbr:R:R: */ + genoperandoperandoperand(MoveXbrRR, TempReg, string2Reg, string2CharOrByteSizeReg); + + /* SubR:R: */ + genoperandoperand(SubRR, string2CharOrByteSizeReg, string1CharOrByteSizeReg); + + /* the 2 compared characters are different, exit the loop */ + jumpMidFailure = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, TempReg); + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, minSizeReg); + + /* JumpNonZero: */ + genConditionalBranchoperand(JumpNonZero, ((sqInt)instr)); + + /* all bytes from 1 to minSize are equal */ + genGetNumBytesOfinto(string1Reg, string1CharOrByteSizeReg); + genGetNumBytesOfinto(string2Reg, string2CharOrByteSizeReg); + jmpTarget(jumpSuccess, genoperandoperand(SubRR, string2CharOrByteSizeReg, string1CharOrByteSizeReg)); + jmpTarget(jumpMidFailure, genoperandoperand(MoveRR, string1CharOrByteSizeReg, ReceiverResultReg)); + genConvertIntegerToSmallIntegerInReg(ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIncorrectFormat4, jmpTarget(jumpIncorrectFormat3, jmpTarget(jumpIncorrectFormat2, jmpTarget(jumpIncorrectFormat1, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))); + return CompletePrimitive; +} + + +/* replaceFrom: start to: stop with: replacement startingAt: repStart. + + The primitive in the JIT tries to deal with two pathological cases, copy + of arrays and byteStrings, + which often copies only a dozen of fields and where switching to the C + runtime cost a lot. + + Based on heuristics on the method class, I generate a quick array path + (typically for Array), + a quick byteString path (typically for ByteString, ByteArray and + LargeInteger) or no quick + path at all (Typically for Bitmap). + + The many tests to ensure that the primitive won't fail are not super + optimised (multiple reloading + or stack arguments in registers) but this is still good enough and worth + it since we're avoiding + the Smalltalk to C stack switch. The tight copying loops are optimised. + + It is possible to build a bigger version with the 2 different paths but I + (Clement) believe this + is too big machine code wise to be worth it. + */ + + /* CogObjectRepresentationForSpur>>#genPrimitiveStringReplace */ +static sqInt +genPrimitiveStringReplace(void) +{ + sqInt adjust; + sqInt arrayReg; + AbstractInstruction *instr; + AbstractInstruction *jmpAlreadyRemembered; + AbstractInstruction *jmpDestYoung; + AbstractInstruction *jumpEmpty; + AbstractInstruction *jumpImm; + AbstractInstruction *jumpImmutable; + AbstractInstruction *jumpIncorrectFormat1; + AbstractInstruction *jumpIncorrectFormat2; + AbstractInstruction *jumpIncorrectFormat3; + AbstractInstruction *jumpIncorrectFormat4; + AbstractInstruction *jumpNotSmi1; + AbstractInstruction *jumpNotSmi2; + AbstractInstruction *jumpNotSmi3; + AbstractInstruction *jumpOutOfBounds1; + AbstractInstruction *jumpOutOfBounds2; + AbstractInstruction *jumpOutOfBounds3; + AbstractInstruction *jumpOutOfBounds4; + sqInt offset; + sqInt replReg; + sqInt repStartReg; + sqInt result; + sqInt startReg; + sqInt stopReg; + + + /* Can I generate a quick path for this method ? */ + jumpOutOfBounds3 = ((AbstractInstruction *) 0); + jumpOutOfBounds4 = ((AbstractInstruction *) 0); + if (!((maybeMethodClassOfseemsToBeInstantiating(methodObj, arrayFormat())) + || (maybeMethodClassOfseemsToBeInstantiating(methodObj, firstByteFormat())))) { + return UnimplementedPrimitive; + } + + /* I redefine those name to ease program comprehension */ + arrayReg = ReceiverResultReg; + startReg = Arg0Reg; + stopReg = Arg1Reg; + replReg = ClassReg; + repStartReg = SendNumArgsReg; + + /* Load arguments in reg */ + + /* begin genStackArgAt:into: */ + offset = (1) * BytesPerWord; + genoperandoperandoperand(MoveMwrR, offset, SPReg, repStartReg); + + /* begin genStackArgAt:into: */ + offset = (2) * BytesPerWord; + genoperandoperandoperand(MoveMwrR, offset, SPReg, replReg); + + /* begin genStackArgAt:into: */ + offset = (3) * BytesPerWord; + genoperandoperandoperand(MoveMwrR, offset, SPReg, stopReg); + + /* begin genStackArgAt:into: */ + offset = (4) * BytesPerWord; + genoperandoperandoperand(MoveMwrR, offset, SPReg, startReg); + + /* start,stop,repStart Smis or fail the primitive */ + jumpNotSmi1 = genJumpNotSmallInteger(repStartReg); + jumpNotSmi2 = genJumpNotSmallInteger(stopReg); + jumpNotSmi3 = genJumpNotSmallInteger(startReg); + + /* repl non immediate or fail the primitive */ + jumpImm = genJumpImmediate(replReg); + + /* if start>stop primitive success */ + + /* begin CmpR:R: */ + assert(!((startReg == SPReg))); + genoperandoperand(CmpRR, startReg, stopReg); + jumpEmpty = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* If receiver immutable fail the primitive */ +# if IMMUTABILITY + jumpImmutable = genJumpImmutablescratchReg(ReceiverResultReg, TempReg); +# endif + + + /* 0 >= start, fail */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, (((usqInt)0 << 3) | 1), startReg); + jumpOutOfBounds1 = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + + /* 0 >= replStart, fail */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, (((usqInt)0 << 3) | 1), repStartReg); + jumpOutOfBounds2 = genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)0)); + + /* --- Pointer object version --- */ + if (maybeMethodClassOfseemsToBeInstantiating(methodObj, arrayFormat())) { + /* Are they both array format ? */ + genGetFormatOfinto(arrayReg, TempReg); + genGetFormatOfinto(replReg, startReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, arrayFormat(), startReg); + jumpIncorrectFormat1 = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, arrayFormat(), TempReg); + jumpIncorrectFormat2 = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Both objects are arrays, */ + genGetNumSlotsOfinto(arrayReg, TempReg); + genConvertSmallIntegerToIntegerInReg(stopReg); + + /* arr size < stop */ + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, stopReg); + jumpOutOfBounds3 = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + + /* rep size < repStart - start + stop */ + genGetNumSlotsOfinto(replReg, TempReg); + + /* begin genStackArgAt:into: */ + offset = (4) * BytesPerWord; + genoperandoperandoperand(MoveMwrR, offset, SPReg, startReg); + genConvertSmallIntegerToIntegerInReg(startReg); + genConvertSmallIntegerToIntegerInReg(repStartReg); + + /* SubR:R: */ + genoperandoperand(SubRR, startReg, stopReg); + + /* AddR:R: */ + genoperandoperand(AddRR, repStartReg, stopReg); + + /* stopReg: stop - start + repStart */ + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, stopReg); + jumpOutOfBounds4 = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + + /* Everything in bounds + PossibleRemembered object */ + + /* #MoveCw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveCwR, storeCheckBoundary(), TempReg); + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, arrayReg); + jmpDestYoung = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + jmpAlreadyRemembered = genCheckRememberedBitOfscratch(arrayReg, TempReg); + + /* saveAndRestoreLinkRegAround: */ + callStoreCheckTrampoline(); + jmpTarget(jmpDestYoung, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jmpTarget(jmpAlreadyRemembered, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* Copy the array */ + + /* begin genStackArgAt:into: */ + offset = (3) * BytesPerWord; + genoperandoperandoperand(MoveMwrR, offset, SPReg, stopReg); + genConvertSmallIntegerToIntegerInReg(stopReg); + + /* Shift replReg to read it with startReg offset */ + + /* SubR:R: */ + genoperandoperand(SubRR, startReg, repStartReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), repStartReg); + + /* AddR:R: */ + genoperandoperand(AddRR, repStartReg, replReg); + adjust = (((usqInt)(BaseHeaderSize)) >> (shiftForWord())) - 1; + if (adjust) { + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, adjust, startReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, adjust, stopReg); + } + + /* Tight copying loop */ + instr = genoperandoperandoperand(MoveXwrRR, startReg, replReg, TempReg); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, TempReg, startReg, arrayReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, startReg); + + /* begin CmpR:R: */ + assert(!((startReg == SPReg))); + genoperandoperand(CmpRR, startReg, stopReg); + + /* JumpAboveOrEqual: */ + genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)instr)); + jmpTarget(jumpEmpty, /* genPrimReturn */ + (methodOrBlockNumArgs <= (numRegArgs()) + ? genoperand(RetN, 0) + : genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord))); + + /* CANNOT REACH by falling though */ + jmpTarget(jumpIncorrectFormat1, jmpTarget(jumpIncorrectFormat2, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + } + + /* --- Byte object version --- */ + if (maybeMethodClassOfseemsToBeInstantiating(methodObj, firstByteFormat())) { + /* Are they both byte array format ? CompiledMethod excluded */ + genGetFormatOfinto(arrayReg, TempReg); + genGetFormatOfinto(replReg, repStartReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), repStartReg); + jumpIncorrectFormat1 = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), repStartReg); + jumpIncorrectFormat2 = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), TempReg); + jumpIncorrectFormat3 = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), TempReg); + jumpIncorrectFormat4 = genConditionalBranchoperand(JumpGreaterOrEqual, ((sqInt)0)); + + /* Both objects are byte arrays */ + genGetNumSlotsOfinto(arrayReg, startReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), startReg); + gAndCqRR(BytesPerWord - 1, TempReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, startReg); + genConvertSmallIntegerToIntegerInReg(stopReg); + + /* arr size < stop */ + + /* begin CmpR:R: */ + assert(!((startReg == SPReg))); + genoperandoperand(CmpRR, startReg, stopReg); + jumpOutOfBounds3 = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + + /* rep size < repStart - start + stop */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, repStartReg, TempReg); + + /* begin genStackArgAt:into: */ + offset = (1) * BytesPerWord; + genoperandoperandoperand(MoveMwrR, offset, SPReg, repStartReg); + + /* begin genStackArgAt:into: */ + offset = (4) * BytesPerWord; + genoperandoperandoperand(MoveMwrR, offset, SPReg, startReg); + genConvertSmallIntegerToIntegerInReg(startReg); + genConvertSmallIntegerToIntegerInReg(repStartReg); + + /* SubR:R: */ + genoperandoperand(SubRR, startReg, stopReg); + + /* AddR:R: */ + genoperandoperand(AddRR, repStartReg, stopReg); + genGetNumSlotsOfinto(replReg, startReg); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), startReg); + gAndCqRR(BytesPerWord - 1, TempReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, startReg); + + /* stopReg: stop - start + repStart */ + + /* begin CmpR:R: */ + assert(!((startReg == SPReg))); + genoperandoperand(CmpRR, startReg, stopReg); + jumpOutOfBounds4 = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + + /* Everything in bounds + Copy the bytes */ + + /* begin genStackArgAt:into: */ + offset = (4) * BytesPerWord; + genoperandoperandoperand(MoveMwrR, offset, SPReg, startReg); + genConvertSmallIntegerToIntegerInReg(startReg); + + /* begin genStackArgAt:into: */ + offset = (3) * BytesPerWord; + genoperandoperandoperand(MoveMwrR, offset, SPReg, stopReg); + genConvertSmallIntegerToIntegerInReg(stopReg); + + /* Shift replReg to read it with startReg offset */ + + /* SubR:R: */ + genoperandoperand(SubRR, startReg, repStartReg); + + /* AddR:R: */ + genoperandoperand(AddRR, repStartReg, replReg); + adjust = BaseHeaderSize - 1; + if (adjust) { + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, adjust, startReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, adjust, stopReg); + } + + /* Tight copying loop */ + instr = genoperandoperandoperand(MoveXbrRR, startReg, replReg, TempReg); + + /* MoveR:Xbr:R: */ + genoperandoperandoperand(MoveRXbrR, TempReg, startReg, arrayReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, startReg); + + /* begin CmpR:R: */ + assert(!((startReg == SPReg))); + genoperandoperand(CmpRR, startReg, stopReg); + + /* JumpAboveOrEqual: */ + genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)instr)); + jmpTarget(jumpEmpty, /* genPrimReturn */ + (methodOrBlockNumArgs <= (numRegArgs()) + ? genoperand(RetN, 0) + : genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord))); + + /* CANNOT REACH by falling though */ + jmpTarget(jumpIncorrectFormat4, jmpTarget(jumpIncorrectFormat3, jmpTarget(jumpIncorrectFormat2, jmpTarget(jumpIncorrectFormat1, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))); + } + if (((result = compileInterpreterPrimitive())) < 0) { + return result; + } + jmpTarget(jumpImm, jmpTarget(jumpNotSmi1, jmpTarget(jumpNotSmi2, jmpTarget(jumpNotSmi3, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))); + jmpTarget(jumpOutOfBounds1, jmpTarget(jumpOutOfBounds2, jmpTarget(jumpOutOfBounds3, jmpTarget(jumpOutOfBounds4, ((AbstractInstruction *) (((jumpImm->operands))[0])))))); +# if IMMUTABILITY + jmpTarget(jumpImmutable, ((AbstractInstruction *) (((jumpImm->operands))[0]))); +# endif + + return CompletePrimitive; +} + + /* CogObjectRepresentationForSpur>>#genSetSmallIntegerTagsIn: */ +static NoDbgRegParms sqInt +genSetSmallIntegerTagsIn(sqInt scratchReg) +{ + /* #OrCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(OrCqR, 1, scratchReg); + return 0; +} + + +/* Create a trampoline to store-check the update of the receiver in a + closure's outerContext in compileBlockFrameBuild:. */ + + /* CogObjectRepresentationForSpur>>#genStoreCheckContextReceiverTrampoline */ +static usqInt +genStoreCheckContextReceiverTrampoline(void) +{ + usqInt startAddress; + + startAddress = methodZoneBase; + zeroOpcodeIndex(); + genStoreCheckReceiverRegvalueRegscratchReginFrame(ReceiverResultReg, Arg0Reg, TempReg, 0); + + /* RetN: */ + genoperand(RetN, 0); + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress("ceStoreCheckContextReceiver", startAddress); + recordRunTimeObjectReferences(); + return startAddress; +} + + +/* Generate the code for a store check of valueReg into destReg. */ + + /* CogObjectRepresentationForSpur>>#genStoreCheckReceiverReg:valueReg:scratchReg:inFrame: */ +static NoDbgRegParms sqInt +genStoreCheckReceiverRegvalueRegscratchReginFrame(sqInt destReg, sqInt valueReg, sqInt scratchReg, sqInt inFrame) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *jmpAlreadyRemembered; + AbstractInstruction *jmpDestYoung; + AbstractInstruction *jmpImmediate; + AbstractInstruction *jmpSourceOld; + + + /* Is value stored an immediate? If so we're done */ + jmpAlreadyRemembered = ((AbstractInstruction *) 0); + jmpImmediate = genJumpImmediate(valueReg); + + /* Get the old/new boundary in scratchReg */ + + /* #MoveCw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveCwR, storeCheckBoundary(), scratchReg); + + /* Is target young? If so we're done */ + + /* begin CmpR:R: */ + assert(!((scratchReg == SPReg))); + genoperandoperand(CmpRR, scratchReg, destReg); + jmpDestYoung = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* Is value stored old? If so we're done. */ + + /* begin CmpR:R: */ + assert(!((scratchReg == SPReg))); + genoperandoperand(CmpRR, scratchReg, valueReg); + jmpSourceOld = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* value is young and target is old. + Need to remember this only if the remembered bit is not already set. */ + if (!CheckRememberedInTrampoline) { + jmpAlreadyRemembered = genCheckRememberedBitOfscratch(destReg, scratchReg); + } + + /* Remembered bit is not set. Call store check to insert dest into remembered table. */ + assert(destReg == ReceiverResultReg); + + /* begin evaluateTrampolineCallBlock:protectLinkRegIfNot: */ + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreCheckTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpTarget(jmpImmediate, jmpTarget(jmpDestYoung, jmpTarget(jmpSourceOld, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + if (!CheckRememberedInTrampoline) { + jmpTarget(jmpAlreadyRemembered, ((AbstractInstruction *) (((jmpSourceOld->operands))[0]))); + } + return 0; +} + + /* CogObjectRepresentationForSpur>>#genStoreSourceReg:slotIndex:destReg:scratchReg:inFrame:needsStoreCheck: */ +static NoDbgRegParms sqInt +genStoreSourceRegslotIndexdestRegscratchReginFrameneedsStoreCheck(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt inFrame, sqInt needsStoreCheck) +{ + AbstractInstruction *abstractInstruction; + + /* begin genTraceStores */ + if (traceStores > 0) { + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, TempReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceTraceStoreTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + } + + /* do the store */ + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, sourceReg, (index * BytesPerWord) + BaseHeaderSize, destReg); + + /* now the check. needStoreCheck is false if the JIT has figured out that the value stored does not need the check (immediate, nil, true, false) */ + if (needsStoreCheck) { + return genStoreCheckReceiverRegvalueRegscratchReginFrame(destReg, sourceReg, scratchReg, inFrame); + } + return 0; +} + + +/* This method is used for unchecked stores in objects after their creation + (typically, inlined creation of Array, closures and some temp vectors). + Currently there is no need to do the immutability check here + */ + + /* CogObjectRepresentationForSpur>>#genStoreSourceReg:slotIndex:intoNewObjectInDestReg: */ +static NoDbgRegParms sqInt +genStoreSourceRegslotIndexintoNewObjectInDestReg(sqInt sourceReg, sqInt index, sqInt destReg) +{ + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, sourceReg, (index * BytesPerWord) + BaseHeaderSize, destReg); + return 0; +} + + +/* Convention: + - RcvrResultReg holds the object mutated. + If immutability failure: + - TempReg holds the instance variable index mutated + if instVarIndex > numDedicatedStoreTrampoline + - ClassReg holds the value to store + Registers are not lived across this trampoline as the + immutability failure may need new stack frames. */ + + /* CogObjectRepresentationForSpur>>#genStoreTrampolineCalled:instVarIndex: */ +#if IMMUTABILITY +static NoDbgRegParms usqInt +genStoreTrampolineCalledinstVarIndex(char *trampolineName, sqInt instVarIndex) +{ + AbstractInstruction *jumpImmutable; + AbstractInstruction *jumpRC; + sqInt pushLinkReg; + sqInt quickConstant; + usqInt startAddress; + + startAddress = methodZoneBase; + zeroOpcodeIndex(); + if (CheckRememberedInTrampoline) { + /* begin genStoreTrampolineCheckingRememberedCalled:instVarIndex: */ + jumpImmutable = genJumpImmutablescratchReg(ReceiverResultReg, SendNumArgsReg); + + /* Store check + If on 64-bits and doing the remembered bit test here, we can combine the tests to fetch the header once. */ + quickConstant = 1U << (rememberedBitShift()); + + /* begin TstCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, quickConstant, SendNumArgsReg); + jumpRC = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* RetN: */ + genoperand(RetN, 0); + jmpTarget(jumpRC, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + compileTrampolineFornumArgsargargargargregsToSavepushLinkRegresultReg(remember, 1, ReceiverResultReg, null, null, null, 0 /* emptyRegisterMask */, 1, /* returnRegForStoreCheck */ + (((CallerSavedRegisterMask & ((1U << ReceiverResultReg))) != 0) + ? ReceiverResultReg + : ABIResultReg)); + jmpTarget(jumpImmutable, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + compileTrampolineFornumArgsargargargargregsToSavepushLinkRegresultReg(ceCannotAssignTowithIndexvalueToAssign, 3, ReceiverResultReg, (instVarIndex < (NumStoreTrampolines - 1) + ? (/* begin trampolineArgConstant: */ + assert(instVarIndex >= 0), + -2 - instVarIndex) + : TempReg), ClassReg, null, 0 /* emptyRegisterMask */, 1, NoReg); + } + else { + /* begin genStoreTrampolineNotCheckingRememberedCalled:instVarIndex: */ + genSmalltalkToCStackSwitch((pushLinkReg = 0 /* hasLinkRegister */)); + + /* SendNumArgsReg is mutated but we don't care as registers are not live across the trampoline. + There is no reason why registers cannot be saved over the remember: call, but since the + immutability check is a suspension point, registers cannot remain live. */ + jumpImmutable = genJumpImmutablescratchReg(ReceiverResultReg, SendNumArgsReg); + + /* Store check */ + compileCallFornumArgsargargargargresultRegregsToSave(remember, 1, ReceiverResultReg, null, null, null, /* returnRegForStoreCheck */ + (((CallerSavedRegisterMask & ((1U << ReceiverResultReg))) != 0) + ? ReceiverResultReg + : ABIResultReg), 0 /* emptyRegisterMask */); + genLoadStackPointers(backEnd); + genTrampolineReturn(pushLinkReg); + jmpTarget(jumpImmutable, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + compileCallFornumArgsargargargargresultRegregsToSave(ceCannotAssignTowithIndexvalueToAssign, 3, ReceiverResultReg, (instVarIndex < (NumStoreTrampolines - 1) + ? (/* begin trampolineArgConstant: */ + assert(instVarIndex >= 0), + -2 - instVarIndex) + : TempReg), ClassReg, null, NoReg, 0 /* emptyRegisterMask */); + genLoadStackPointers(backEnd); + genTrampolineReturn(pushLinkReg); + } + outputInstructionsForGeneratedRuntimeAt(startAddress); + recordGeneratedRunTimeaddress(trampolineName, startAddress); + recordRunTimeObjectReferences(); + return startAddress; +} +#endif /* IMMUTABILITY */ + + +/* Store check code is duplicated to use a single trampoline */ + + /* CogObjectRepresentationForSpur>>#genStoreWithImmutabilityAndStoreCheckSourceReg:slotIndex:destReg:scratchReg:needRestoreRcvr: */ +#if IMMUTABILITY +static NoDbgRegParms sqInt +genStoreWithImmutabilityAndStoreCheckSourceRegslotIndexdestRegscratchRegneedRestoreRcvr(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt needRestoreRcvr) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *immutableJump; + AbstractInstruction *jmpAlreadyRemembered; + AbstractInstruction *jmpDestYoung; + AbstractInstruction *jmpImmediate; + AbstractInstruction *jmpSourceOld; + + jmpAlreadyRemembered = ((AbstractInstruction *) 0); + immutableJump = genJumpImmutablescratchReg(destReg, scratchReg); + + /* begin genTraceStores */ + if (traceStores > 0) { + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, TempReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceTraceStoreTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + } + + /* do the store */ + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, sourceReg, (index * BytesPerWord) + BaseHeaderSize, destReg); + + /* store check */ + jmpImmediate = genJumpImmediate(sourceReg); + + /* Get the old/new boundary in scratchReg */ + + /* #MoveCw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveCwR, storeCheckBoundary(), scratchReg); + + /* Is target young? If so we're done */ + + /* begin CmpR:R: */ + assert(!((scratchReg == SPReg))); + genoperandoperand(CmpRR, scratchReg, destReg); + jmpDestYoung = genConditionalBranchoperand(JumpBelow, ((sqInt)0)); + + /* Is value stored old? If so we're done. */ + + /* begin CmpR:R: */ + assert(!((scratchReg == SPReg))); + genoperandoperand(CmpRR, scratchReg, sourceReg); + jmpSourceOld = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* value is young and target is old. + Need to remember this only if the remembered bit is not already set. */ + if (!CheckRememberedInTrampoline) { + jmpAlreadyRemembered = genCheckRememberedBitOfscratch(destReg, scratchReg); + } + + /* Set the inst var index for the benefit of the immutability check. The trampoline will + repeat the check to choose between the immutability violation and the store check. */ + jmpTarget(immutableJump, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin genStoreTrampolineCall: */ + assert(IMMUTABILITY); + if (index >= (NumStoreTrampolines - 1)) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, index, TempReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreTrampolines[NumStoreTrampolines - 1]); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreTrampolines[index]); + (abstractInstruction->annotation = IsRelativeCall); + } + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + if (needRestoreRcvr) { + genWriteCResultIntoReg(backEnd, ReceiverResultReg); + } + else { + if (((CallerSavedRegisterMask & ((1U << ReceiverResultReg))) != 0)) { + /* begin voidReceiverOptStatus */ + ((simSelf())->liveRegister = NoReg); + } + } + jmpTarget(jmpImmediate, jmpTarget(jmpDestYoung, jmpTarget(jmpSourceOld, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + if (!CheckRememberedInTrampoline) { + jmpTarget(jmpAlreadyRemembered, ((AbstractInstruction *) (((jmpSourceOld->operands))[0]))); + } + return 0; +} +#endif /* IMMUTABILITY */ + + +/* Gen an immutability check with no store check (e.g. assigning an immediate + literal) + */ +/* imm check has its own trampoline */ + + /* CogObjectRepresentationForSpur>>#genStoreWithImmutabilityButNoStoreCheckSourceReg:slotIndex:destReg:scratchReg:needRestoreRcvr: */ +#if IMMUTABILITY +static NoDbgRegParms sqInt +genStoreWithImmutabilityButNoStoreCheckSourceRegslotIndexdestRegscratchRegneedRestoreRcvr(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt needRestoreRcvr) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *immutabilityFailure; + AbstractInstruction *mutableJump; + + mutableJump = genJumpMutablescratchReg(destReg, scratchReg); + + /* begin genStoreTrampolineCall: */ + assert(IMMUTABILITY); + if (index >= (NumStoreTrampolines - 1)) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, index, TempReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreTrampolines[NumStoreTrampolines - 1]); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreTrampolines[index]); + (abstractInstruction->annotation = IsRelativeCall); + } + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + + /* begin voidReceiverOptStatus */ + ((simSelf())->liveRegister = NoReg); + if (needRestoreRcvr) { + /* begin putSelfInReceiverResultReg */ + storeToReg(simSelf(), ReceiverResultReg); + } + immutabilityFailure = genoperand(Jump, ((sqInt)0)); + jmpTarget(mutableJump, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin genTraceStores */ + if (traceStores > 0) { + /* MoveR:R: */ + genoperandoperand(MoveRR, ClassReg, TempReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceTraceStoreTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + } + + /* do the store */ + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, sourceReg, (index * BytesPerWord) + BaseHeaderSize, destReg); + jmpTarget(immutabilityFailure, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} +#endif /* IMMUTABILITY */ + + +/* We know there is a frame as immutability check requires a frame */ +/* needRestoreRcvr has to be true to keep RcvrResultReg live with the + receiver in it across the trampoline + */ +/* Trampoline convention... */ + + /* CogObjectRepresentationForSpur>>#genStoreWithImmutabilityCheckSourceReg:slotIndex:destReg:scratchReg:needsStoreCheck:needRestoreRcvr: */ +#if IMMUTABILITY +static NoDbgRegParms sqInt +genStoreWithImmutabilityCheckSourceRegslotIndexdestRegscratchRegneedsStoreCheckneedRestoreRcvr(sqInt sourceReg, sqInt index, sqInt destReg, sqInt scratchReg, sqInt needsStoreCheck, sqInt needRestoreRcvr) +{ + assert(destReg == ReceiverResultReg); + assert(scratchReg == TempReg); + assert(sourceReg == ClassReg); + + /* The two paths are very different as the trampoline is shared in 1 case so we split here */ + if (needsStoreCheck) { + genStoreWithImmutabilityAndStoreCheckSourceRegslotIndexdestRegscratchRegneedRestoreRcvr(sourceReg, index, destReg, scratchReg, needRestoreRcvr); + } + else { + genStoreWithImmutabilityButNoStoreCheckSourceRegslotIndexdestRegscratchRegneedRestoreRcvr(sourceReg, index, destReg, scratchReg, needRestoreRcvr); + } + return 0; +} +#endif /* IMMUTABILITY */ + + +/* Make sure SendNumArgsReg and ClassReg are available in addition to + ReceiverResultReg and TempReg in + genGetActiveContextNumArgs:large:inBlock:. + */ + + /* CogObjectRepresentationForSpur>>#getActiveContextAllocatesInMachineCode */ +static sqInt +getActiveContextAllocatesInMachineCode(void) +{ + return 1; +} + + +/* Since all cache tags in Spur are class indices none of + them are young or have to be updated in a scavenge. */ + + /* CogObjectRepresentationForSpur>>#inlineCacheTagIsYoung: */ +static NoDbgRegParms sqInt +inlineCacheTagIsYoung(sqInt cacheTag) +{ + return 0; +} + + /* CogObjectRepresentationForSpur>>#jumpNotCharacterUnsignedValueInRegister: */ +static NoDbgRegParms AbstractInstruction * +jumpNotCharacterUnsignedValueInRegister(sqInt reg) +{ + sqInt quickConstant; + + quickConstant = (1U << (numCharacterBits())) - 1; + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, reg); + return genConditionalBranchoperand(JumpAbove, ((sqInt)0)); +} + + +/* Mark and trace a literal in a machine code instruction preceding address + in cogMethodOrNil. + Answer if code was modified. */ + + /* CogObjectRepresentationForSpur>>#markAndTraceLiteral:in:atpc: */ +static NoDbgRegParms sqInt +markAndTraceLiteralinatpc(sqInt literal, CogMethod *cogMethodOrNil, usqInt address) +{ + sqInt objOop; + + if (!(couldBeObject(literal))) { + return 0; + } + assert(addressCouldBeObj(literal)); + if (!(isForwarded(literal))) { + markAndTrace(literal); + return 0; + } + + /* begin setCodeModified */ + codeModified = 1; + objOop = followForwarded(literal); + storeLiteralbeforeFollowingAddress(backEnd, objOop, address); + markAndTraceUpdatedLiteralin(objOop, cogMethodOrNil); + return 1; +} + + +/* Mark and trace a literal in a sqInt variable of cogMethod. */ + + /* CogObjectRepresentationForSpur>>#markAndTraceLiteral:in:at: */ +static NoDbgRegParms void +markAndTraceLiteralinat(sqInt literal, CogMethod *cogMethod, sqInt *address) +{ + sqInt objOop; + + if (!(couldBeObject(literal))) { + return; + } + assert(addressCouldBeObj(literal)); + if (!(isForwarded(literal))) { + markAndTrace(literal); + return; + } + objOop = followForwarded(literal); + address[0] = objOop; + markAndTraceUpdatedLiteralin(objOop, cogMethod); +} + + +/* Common code to mark a literal in cogMethod and add + the cogMethod to youngReferrers if the literal is young. */ + + /* CogObjectRepresentationForSpur>>#markAndTraceUpdatedLiteral:in: */ +static NoDbgRegParms void +markAndTraceUpdatedLiteralin(sqInt objOop, CogMethod *cogMethodOrNil) +{ + if (isNonImmediate(objOop)) { + if ((cogMethodOrNil) + && (isYoungObject(objOop))) { + ensureInYoungReferrers(cogMethodOrNil); + } + markAndTrace(objOop); + } +} + + +/* If primIndex has an accessorDepth and fails, or it is external and fails + with PrimErrNoMemory, + call ceCheckAndMaybeRetryPrimitive if so If ceCheck.... answers true, + retry the primitive. */ + + /* CogObjectRepresentationForSpur>>#maybeCompileRetryOf:onPrimitiveFail:flags:jump:continue: */ +static NoDbgRegParms sqInt +maybeCompileRetryOfonPrimitiveFailflagsjumpcontinue(void (*primitiveRoutine)(void), sqInt primIndex, sqInt flags, AbstractInstruction *jmp, AbstractInstruction *label) +{ + if (!jmp) { + return 0; + } + jmpTarget(jmp, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* newMethod must be set unless it already has been. */ + if (!(((flags & PrimCallNeedsNewMethod) != 0))) { + genLoadNewMethod(); + } + + /* primitiveFunctionPointer must be set */ + + /* #MoveCw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveCwR, ((sqInt)primitiveRoutine), TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, primitiveFunctionPointerAddress()); + compileCallFornumArgsargargargargresultRegregsToSave( + ceCheckAndMaybeRetryPrimitive, + 1, + (/* begin trampolineArgConstant: */ + assert(primIndex >= 0), + -2 - primIndex), + null, + null, + null, + TempReg, + 0 /* emptyRegisterMask */); + + /* Jump: */ + genoperand(Jump, ((sqInt)label)); + return 0; +} + + +/* Generate a shift of the register containing the class tag in a method + cache probe. + c.f. SpurMemoryManager>>methodCacheHashOf:with: */ + + /* CogObjectRepresentationForSpur>>#maybeShiftClassTagRegisterForMethodCacheProbe: */ +static NoDbgRegParms sqInt +maybeShiftClassTagRegisterForMethodCacheProbe(sqInt classTagReg) +{ + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, 2, classTagReg); + return 0; +} + + /* CogObjectRepresentationForSpur>>#numCharacterBits */ +static sqInt +numCharacterBits(void) +{ + return 30; +} + + /* CogObjectRepresentationForSpur>>#remapObject: */ +static NoDbgRegParms sqInt +remapObject(sqInt objOop) +{ + assert(addressCouldBeObj(objOop)); + return (shouldRemapObj(objOop) + ? remapObj(objOop) + : objOop); +} + + /* CogObjectRepresentationForSpur>>#remapOop: */ +static NoDbgRegParms sqInt +remapOop(sqInt objOop) +{ + return (shouldRemapOop(objOop) + ? remapObj(objOop) + : objOop); +} + + +/* Objects in newSpace or oldSpace except nil, true, false & + classTableRootObj need to be annotated. + */ + + /* CogObjectRepresentationForSpur>>#shouldAnnotateObjectReference: */ +static NoDbgRegParms sqInt +shouldAnnotateObjectReference(sqInt anOop) +{ + return (isNonImmediate(anOop)) + && ((oopisGreaterThan(anOop, classTableRootObj())) + || (oopisLessThan(anOop, nilObject()))); +} + + /* CogObjectRepresentationForSpur>>#slotOffsetOfInstVarIndex: */ +static NoDbgRegParms sqInt +slotOffsetOfInstVarIndex(sqInt index) +{ + return (index * BytesPerWord) + BaseHeaderSize; +} + + /* CogSimStackEntry>>#ensureSpilledAt:from: */ +static NoDbgRegParms SimStackEntry * +ensureSpilledAtfrom(SimStackEntry *self_in_CogSimStackEntry, sqInt baseOffset, sqInt baseRegister) +{ + AbstractInstruction *inst; + + if ((self_in_CogSimStackEntry->spilled)) { + if (((self_in_CogSimStackEntry->type)) == SSSpill) { + assert(((((self_in_CogSimStackEntry->offset)) == baseOffset) + && (((self_in_CogSimStackEntry->registerr)) == baseRegister)) + || (violatesEnsureSpilledSpillAssert())); + return self_in_CogSimStackEntry; + } + } + assert(((self_in_CogSimStackEntry->type)) != SSSpill); + traceSpill(self_in_CogSimStackEntry); + if (((self_in_CogSimStackEntry->type)) == SSConstant) { + inst = /* genPushConstant: */ + (/* shouldAnnotateObjectReference: */ + (isNonImmediate((self_in_CogSimStackEntry->constant))) + && ((oopisGreaterThan((self_in_CogSimStackEntry->constant), classTableRootObj())) + || (oopisLessThan((self_in_CogSimStackEntry->constant), nilObject()))) + ? annotateobjRef(genoperand(PushCw, (self_in_CogSimStackEntry->constant)), (self_in_CogSimStackEntry->constant)) + : genoperand(PushCq, (self_in_CogSimStackEntry->constant))); + } + else { + if (((self_in_CogSimStackEntry->type)) == SSBaseOffset) { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, (self_in_CogSimStackEntry->offset), (self_in_CogSimStackEntry->registerr), TempReg); + inst = genoperand(PushR, TempReg); + } + else { + assert(((self_in_CogSimStackEntry->type)) == SSRegister); + inst = genoperand(PushR, (self_in_CogSimStackEntry->registerr)); + } + (self_in_CogSimStackEntry->type) = SSSpill; + (self_in_CogSimStackEntry->offset) = baseOffset; + (self_in_CogSimStackEntry->registerr) = baseRegister; + } + (self_in_CogSimStackEntry->spilled) = 1; + return self_in_CogSimStackEntry; +} + + /* CogSimStackEntry>>#isSameEntryAs: */ +static NoDbgRegParms sqInt +isSameEntryAs(SimStackEntry *self_in_CogSimStackEntry, SimStackEntry *ssEntry) +{ + return (((self_in_CogSimStackEntry->type)) == ((ssEntry->type))) + && ((((((self_in_CogSimStackEntry->type)) == SSBaseOffset) + || (((self_in_CogSimStackEntry->type)) == SSSpill)) + && ((((self_in_CogSimStackEntry->offset)) == ((ssEntry->offset))) + && (((self_in_CogSimStackEntry->registerr)) == ((ssEntry->registerr))))) + || (((((self_in_CogSimStackEntry->type)) == SSRegister) + && (((self_in_CogSimStackEntry->registerr)) == ((ssEntry->registerr)))) + || ((((self_in_CogSimStackEntry->type)) == SSConstant) + && (((self_in_CogSimStackEntry->constant)) == ((ssEntry->constant)))))); +} + + +/* Receiver is not a forwarder, except in blocks with no inst var access. + For now we optimize only the case where receiver is accessed in a method. */ + + /* CogSimStackEntry>>#mayBeAForwarder */ +static NoDbgRegParms sqInt +mayBeAForwarder(SimStackEntry *self_in_CogSimStackEntry) +{ + if ((((self_in_CogSimStackEntry->type)) == SSRegister) + && (isNonForwarderReceiver((self_in_CogSimStackEntry->registerr)))) { + return 0; + } + return ((self_in_CogSimStackEntry->type)) != SSConstant; +} + + /* CogSimStackEntry>>#popToReg: */ +static NoDbgRegParms SimStackEntry * +popToReg(SimStackEntry *self_in_CogSimStackEntry, sqInt reg) +{ + if ((self_in_CogSimStackEntry->spilled)) { + /* PopR: */ + genoperand(PopR, reg); + } + else { + switch ((self_in_CogSimStackEntry->type)) { + case SSBaseOffset: + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, (self_in_CogSimStackEntry->offset), (self_in_CogSimStackEntry->registerr), reg); + break; + case SSConstant: + /* genMoveConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate((self_in_CogSimStackEntry->constant))) + && ((oopisGreaterThan((self_in_CogSimStackEntry->constant), classTableRootObj())) + || (oopisLessThan((self_in_CogSimStackEntry->constant), nilObject())))) { + annotateobjRef(genoperandoperand(MoveCwR, (self_in_CogSimStackEntry->constant), reg), (self_in_CogSimStackEntry->constant)); + } + else { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, (self_in_CogSimStackEntry->constant), reg); + } + break; + case SSRegister: + if (reg != ((self_in_CogSimStackEntry->registerr))) { + /* MoveR:R: */ + genoperandoperand(MoveRR, (self_in_CogSimStackEntry->registerr), reg); + } + else { + /* Label */ + genoperandoperand(Label, (labelCounter += 1), bytecodePC); + } + break; + default: + error("Case not found and no otherwise clause"); + } + } + return self_in_CogSimStackEntry; +} + + +/* Answer a bit mask for the receiver's register, if any. */ + + /* CogSimStackEntry>>#registerMask */ +static NoDbgRegParms sqInt +registerMask(SimStackEntry *self_in_CogSimStackEntry) +{ + return ((((self_in_CogSimStackEntry->type)) == SSBaseOffset) + || (((self_in_CogSimStackEntry->type)) == SSRegister) + ? ((((self_in_CogSimStackEntry->registerr)) < 0) ? (((usqInt)(1)) >> (-((self_in_CogSimStackEntry->registerr)))) : (1ULL << ((self_in_CogSimStackEntry->registerr)))) + : 0); +} + + /* CogSimStackEntry>>#registerMaskOrNone */ +static NoDbgRegParms sqInt +registerMaskOrNone(SimStackEntry *self_in_CogSimStackEntry) +{ + return (((self_in_CogSimStackEntry->type)) == SSRegister + ? ((((self_in_CogSimStackEntry->registerr)) < 0) ? (((usqInt)(1)) >> (-((self_in_CogSimStackEntry->registerr)))) : (1ULL << ((self_in_CogSimStackEntry->registerr)))) + : 0); +} + + /* CogSimStackEntry>>#registerOrNone */ +static NoDbgRegParms sqInt +registerOrNone(SimStackEntry *self_in_CogSimStackEntry) +{ + return (((self_in_CogSimStackEntry->type)) == SSRegister + ? (self_in_CogSimStackEntry->registerr) + : NoReg); +} + + /* CogSimStackEntry>>#storeToReg: */ +static NoDbgRegParms SimStackEntry * +storeToReg(SimStackEntry *self_in_CogSimStackEntry, sqInt reg) +{ + switch ((self_in_CogSimStackEntry->type)) { + case SSBaseOffset: + case SSSpill: + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, (self_in_CogSimStackEntry->offset), (self_in_CogSimStackEntry->registerr), reg); + break; + case SSConstant: + /* genMoveConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate((self_in_CogSimStackEntry->constant))) + && ((oopisGreaterThan((self_in_CogSimStackEntry->constant), classTableRootObj())) + || (oopisLessThan((self_in_CogSimStackEntry->constant), nilObject())))) { + annotateobjRef(genoperandoperand(MoveCwR, (self_in_CogSimStackEntry->constant), reg), (self_in_CogSimStackEntry->constant)); + } + else { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, (self_in_CogSimStackEntry->constant), reg); + } + break; + case SSRegister: + if (reg != ((self_in_CogSimStackEntry->registerr))) { + /* MoveR:R: */ + genoperandoperand(MoveRR, (self_in_CogSimStackEntry->registerr), reg); + } + else { + /* Label */ + genoperandoperand(Label, (labelCounter += 1), bytecodePC); + } + break; + default: + error("Case not found and no otherwise clause"); + } + return self_in_CogSimStackEntry; +} + + /* CogSSBytecodeFixup>>#isMergeFixup */ +static NoDbgRegParms int +isMergeFixup(BytecodeFixup *self_in_CogSSBytecodeFixup) +{ + return (((usqInt)((self_in_CogSSBytecodeFixup->targetInstruction)))) == NeedsMergeFixupFlag; +} + + +/* Answer an unused abstract register in the liveRegMask. + Subclasses with more registers can override to answer them. + N.B. Do /not/ allocate TempReg. */ +/* Answer an unused abstract register in the liveRegMask. + Subclasses with more registers can override to answer them. + N.B. Do /not/ allocate TempReg. */ + + /* CogX64Compiler>>#availableRegisterOrNoneFor: */ +static NoDbgRegParms sqInt +availableRegisterOrNoneFor(AbstractInstruction *self_in_CogX64Compiler, sqInt liveRegsMask) +{ + if (!(((liveRegsMask & ((1U << Extra5Reg))) != 0))) { + return Extra5Reg; + } + if (!(((liveRegsMask & ((1U << Extra4Reg))) != 0))) { + return Extra4Reg; + } + if (!(((liveRegsMask & ((1U << Extra3Reg))) != 0))) { + return Extra3Reg; + } + if (!(((liveRegsMask & ((1U << Extra2Reg))) != 0))) { + return Extra2Reg; + } + if (!(((liveRegsMask & ((1U << Extra1Reg))) != 0))) { + return Extra1Reg; + } + if (!(((liveRegsMask & ((1U << Extra0Reg))) != 0))) { + return Extra0Reg; + } + if (!(((liveRegsMask & ((1U << Arg1Reg))) != 0))) { + return Arg1Reg; + } + if (!(((liveRegsMask & ((1U << Arg0Reg))) != 0))) { + return Arg0Reg; + } + if (!(((liveRegsMask & ((1U << SendNumArgsReg))) != 0))) { + return SendNumArgsReg; + } + if (!(((liveRegsMask & ((1U << ClassReg))) != 0))) { + return ClassReg; + } + if (!(((liveRegsMask & ((1U << ReceiverResultReg))) != 0))) { + return ReceiverResultReg; + } + return NoReg; +} + + /* CogX64Compiler>>#callFullInstructionByteSize */ +static NoDbgRegParms sqInt +callFullInstructionByteSize(AbstractInstruction *self_in_CogX64Compiler) +{ + return 12; +} + + +/* Answer the address the full call immediately preceding + callSiteReturnAddress will jump to. + */ + + /* CogX64Compiler>>#callFullTargetFromReturnAddress: */ +static NoDbgRegParms sqInt +callFullTargetFromReturnAddress(AbstractInstruction *self_in_CogX64Compiler, sqInt callSiteReturnAddress) +{ + return unalignedLongAt((void *)((callSiteReturnAddress - 2) - 8)); +} + + /* CogX64Compiler>>#callInstructionByteSize */ +static NoDbgRegParms sqInt +callInstructionByteSize(AbstractInstruction *self_in_CogX64Compiler) +{ + return 5; +} + + +/* Answer the address the call immediately preceding callSiteReturnAddress + will jump to. + */ + + /* CogX64Compiler>>#callTargetFromReturnAddress: */ +static NoDbgRegParms usqInt +callTargetFromReturnAddress(AbstractInstruction *self_in_CogX64Compiler, sqInt callSiteReturnAddress) +{ + unsigned int callDistance; + + callDistance = literal32BeforeFollowingAddress(self_in_CogX64Compiler, callSiteReturnAddress); + return ((usqInt)(callSiteReturnAddress + (((int) callDistance)))); +} + + /* CogX64Compiler>>#cFloatResultToRd: */ +static NoDbgRegParms AbstractInstruction * +cFloatResultToRd(AbstractInstruction *self_in_CogX64Compiler, sqInt reg) +{ + if (XMM0L != reg) { + /* MoveRd:Rd: */ + genoperandoperand(MoveRdRd, XMM0L, reg); + } + return self_in_CogX64Compiler; +} + + /* CogX64Compiler>>#cFloatResultToRs: */ +static NoDbgRegParms AbstractInstruction * +cFloatResultToRs(AbstractInstruction *self_in_CogX64Compiler, sqInt reg) +{ + if (XMM0L != reg) { + /* MoveRs:Rs: */ + genoperandoperand(MoveRsRs, XMM0L, reg); + } + return self_in_CogX64Compiler; +} + + /* CogX64Compiler>>#cmpC32RTempByteSize */ +static NoDbgRegParms sqInt +cmpC32RTempByteSize(AbstractInstruction *self_in_CogX64Compiler) +{ + return 5; +} + + +/* Compute the maximum size for each opcode. This allows jump offsets to + be determined, provided that all backward branches are long branches. */ +/* N.B. The ^N forms are to get around the bytecode compiler's long branch + limits which are exceeded when each case jumps around the otherwise. */ + + /* CogX64Compiler>>#computeMaximumSize */ +static NoDbgRegParms sqInt +computeMaximumSize(AbstractInstruction *self_in_CogX64Compiler) +{ + switch ((self_in_CogX64Compiler->opcode)) { + case Label: + return 0; + + case AlignmentNops: + return (((self_in_CogX64Compiler->operands))[0]) - 1; + + case Fill32: + case IMULRR: + case BSR: + case SignExtend8RR: + case SignExtend16RR: + case ZeroExtend8RR: + case ZeroExtend16RR: + return 4; + + case Nop: + case REP: + case CLD: + case MOVSB: + case Stop: + return 1; + + case CDQ: + case CPUID: + case MOVSQ: + return 2; + + case IDIVR: + case CallR: + case JumpR: + case AddRR: + case AddcRR: + case AndRR: + case CmpRR: + case OrRR: + case XorRR: + case SubRR: + case SubbRR: + case NegateR: + case MoveRR: + case SignExtend32RR: + return 3; + + case XCHGRR: + return (((((self_in_CogX64Compiler->operands))[0]) == RAX) + || ((((self_in_CogX64Compiler->operands))[1]) == RAX) + ? 2 + : 3); + + case CallFull: + return 12; + + case Call: + case ClzRR: + case MoveRRd: + case MoveRdR: + case ConvertRRd: + case ConvertRdR: + return 5; + + case JumpFull: + resolveJumpTarget(self_in_CogX64Compiler); + return 12; + + case JumpLong: + case Jump: + resolveJumpTarget(self_in_CogX64Compiler); + return 5; + + case JumpZero: + case JumpNonZero: + case JumpNegative: + case JumpNonNegative: + case JumpOverflow: + case JumpNoOverflow: + case JumpCarry: + case JumpNoCarry: + case JumpLess: + case JumpGreaterOrEqual: + case JumpGreater: + case JumpLessOrEqual: + case JumpBelow: + case JumpAboveOrEqual: + case JumpAbove: + case JumpBelowOrEqual: + case JumpLongZero: + case JumpLongNonZero: + case JumpFPEqual: + case JumpFPNotEqual: + case JumpFPLess: + case JumpFPGreaterOrEqual: + case JumpFPGreater: + case JumpFPLessOrEqual: + case JumpFPOrdered: + case JumpFPUnordered: + resolveJumpTarget(self_in_CogX64Compiler); + return 6; + + case RetN: + return (((self_in_CogX64Compiler->operands))[0] + ? 3 + : 1); + + case AddCqR: + case AddcCqR: + case AndCqR: + case CmpCqR: + case OrCqR: + case SubCqR: + case SubbCqR: + case TstCqR: + case XorCqR: + /* begin computeSizeOfArithCqR */ + if (isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0])) { + return 4; + } + if (is32BitSignedImmediate(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0])) { + return (((self_in_CogX64Compiler->operands))[1] + ? 7 + : 6); + } + return 13; + + case AddCwR: + case AndCwR: + case CmpCwR: + case OrCwR: + case SubCwR: + case XorCwR: + return 13; + + case CmpC32R: + return ((((self_in_CogX64Compiler->operands))[1]) <= 7 + ? (((self_in_CogX64Compiler->operands))[1] + ? 6 + : 5) + : 7); + + case LoadEffectiveAddressMwrR: + return ((isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0]) + ? 4 + : 7)) + ((((((self_in_CogX64Compiler->operands))[1]) & 7) == RSP + ? 1 + : 0)); + + case LogicalShiftLeftCqR: + case LogicalShiftRightCqR: + case ArithmeticShiftRightCqR: + case RotateRightCqR: + case RotateLeftCqR: + return ((((self_in_CogX64Compiler->operands))[0]) == 1 + ? 3 + : 4); + + case LogicalShiftLeftRR: + case LogicalShiftRightRR: + case ArithmeticShiftRightRR: + return computeShiftRRSize(self_in_CogX64Compiler); + + case AddRdRd: + case CmpRdRd: + case SubRdRd: + case MulRdRd: + case DivRdRd: + case XorRdRd: + case AddRsRs: + case SubRsRs: + case MulRsRs: + case DivRsRs: + case MoveRdRd: + case MoveRsRs: + case ConvertRRs: + case ConvertRsR: + case ConvertRsRd: + case ConvertRdRs: + return 4 + ((((((self_in_CogX64Compiler->operands))[1]) > 7) + || ((((self_in_CogX64Compiler->operands))[0]) > 7) + ? 1 + : 0)); + + case SqrtRd: + case SqrtRs: + return 4 + (((((self_in_CogX64Compiler->operands))[0]) > 7 + ? 1 + : 0)); + + case CmpRsRs: + case XorRsRs: + return 3 + ((((((self_in_CogX64Compiler->operands))[1]) > 7) + || ((((self_in_CogX64Compiler->operands))[0]) > 7) + ? 1 + : 0)); + + case MoveCqR: + return (((self_in_CogX64Compiler->operands))[0] + ? (is32BitSignedImmediate(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0]) + ? 7 + : 10 /* (moveCwRByteSize - 1) */) + : 3); + + case MoveCwR: + return (/* inCurrentCompilation: */ + (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])))) + || ((((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0]))) == (methodLabel))) + || (/* addressIsInCurrentCompilation: */ + ((((usqInt)(((self_in_CogX64Compiler->operands))[0]))) >= ((methodLabel->address))) + && ((((usqInt)(((self_in_CogX64Compiler->operands))[0]))) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize))))) + ? 7 + : 11 /* moveCwRByteSize */); + + case MoveC32R: + return 7; + + case MoveAwR: + return (/* isAddressRelativeToVarBase: */ + ((((self_in_CogX64Compiler->operands))[0]) >= (varBaseAddress)) + && (((((self_in_CogX64Compiler->operands))[0]) - (varBaseAddress)) < (0x100000)) + ? (isQuick(self_in_CogX64Compiler, (((self_in_CogX64Compiler->operands))[0]) - (varBaseAddress)) + ? ((((((self_in_CogX64Compiler->operands))[0]) - (varBaseAddress)) == 0) + && (((((self_in_CogX64Compiler->operands))[1]) & 7) != RBP) + ? 3 + : 4) + : 7) + : (((self_in_CogX64Compiler->operands))[1] + ? (((((self_in_CogX64Compiler->operands))[1]) == RBP) + || ((((self_in_CogX64Compiler->operands))[1]) == RSP) + ? 12 + : 14) + : 10)); + + case MoveA32R: + return (((self_in_CogX64Compiler->operands))[1] + ? 13 + : 9); + + case MoveRAw: + return (/* isAddressRelativeToVarBase: */ + ((((self_in_CogX64Compiler->operands))[1]) >= (varBaseAddress)) + && (((((self_in_CogX64Compiler->operands))[1]) - (varBaseAddress)) < (0x100000)) + ? (isQuick(self_in_CogX64Compiler, (((self_in_CogX64Compiler->operands))[1]) - (varBaseAddress)) + ? ((((((self_in_CogX64Compiler->operands))[1]) - (varBaseAddress)) == 0) + && (((((self_in_CogX64Compiler->operands))[0]) & 7) != RBP) + ? 3 + : 4) + : 7) + : (((self_in_CogX64Compiler->operands))[0] + ? (((((self_in_CogX64Compiler->operands))[0]) == RBP) + || ((((self_in_CogX64Compiler->operands))[0]) == RSP) + ? 13 + : 14) + : 10)); + + case MoveRA32: + return (((self_in_CogX64Compiler->operands))[0] + ? 13 + : 9); + + case MoveAbR: + return (/* isAddressRelativeToVarBase: */ + ((((self_in_CogX64Compiler->operands))[0]) >= (varBaseAddress)) + && (((((self_in_CogX64Compiler->operands))[0]) - (varBaseAddress)) < (0x100000)) + ? (isQuick(self_in_CogX64Compiler, (((self_in_CogX64Compiler->operands))[0]) - (varBaseAddress)) + ? ((((((self_in_CogX64Compiler->operands))[0]) - (varBaseAddress)) == 0) + && (((((self_in_CogX64Compiler->operands))[1]) & 7) != RBP) + ? 3 + : 4) + : 7) + : (((self_in_CogX64Compiler->operands))[1] + ? 14 + : 10)); + + case MoveRAb: + return (/* isAddressRelativeToVarBase: */ + ((((self_in_CogX64Compiler->operands))[1]) >= (varBaseAddress)) + && (((((self_in_CogX64Compiler->operands))[1]) - (varBaseAddress)) < (0x100000)) + ? (isQuick(self_in_CogX64Compiler, (((self_in_CogX64Compiler->operands))[1]) - (varBaseAddress)) + ? ((((((self_in_CogX64Compiler->operands))[1]) - (varBaseAddress)) == 0) + && (((((self_in_CogX64Compiler->operands))[0]) & 7) != RBP) + ? 3 + : 4) + : 7) + : (((self_in_CogX64Compiler->operands))[0] + ? 14 + : 10)); + + case MoveRMwr: + assert(is32BitSignedImmediate(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[1])); + return ((isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[1]) + ? (((((self_in_CogX64Compiler->operands))[1]) == 0) + && (((((self_in_CogX64Compiler->operands))[2]) & 7) != RBP) + ? 3 + : 4) + : 7)) + ((((((self_in_CogX64Compiler->operands))[2]) & 7) == RSP + ? 1 + : 0)); + + case MoveRM32r: + return (((isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[1]) + ? (((((self_in_CogX64Compiler->operands))[1]) == 0) + && (((((self_in_CogX64Compiler->operands))[2]) & 6) != RSP) + ? 2 + : 3) + : 6)) + (((((((self_in_CogX64Compiler->operands))[2]) & 7) == RSP) + && ((((self_in_CogX64Compiler->operands))[1]) != 0) + ? 1 + : 0))) + ((((((self_in_CogX64Compiler->operands))[2]) > 7) + || ((((self_in_CogX64Compiler->operands))[0]) > 7) + ? 1 + : 0)); + + case MoveRsM32r: + case MoveRdM64r: + return (((isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[1]) + ? (((((self_in_CogX64Compiler->operands))[1]) == 0) + && (((((self_in_CogX64Compiler->operands))[2]) & 6) != RSP) + ? 4 + : 5) + : 8)) + (((((((self_in_CogX64Compiler->operands))[2]) & 7) == RSP) + && ((((self_in_CogX64Compiler->operands))[1]) != 0) + ? 1 + : 0))) + ((((((self_in_CogX64Compiler->operands))[2]) > 7) + || ((((self_in_CogX64Compiler->operands))[0]) > 7) + ? 1 + : 0)); + + case MoveMbrR: + case MoveM8rR: + case MoveMs8rR: + case MoveMwrR: + assert(is32BitSignedImmediate(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0])); + return ((isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0]) + ? (((((self_in_CogX64Compiler->operands))[0]) == 0) + && (((((self_in_CogX64Compiler->operands))[1]) & 7) != RBP) + ? 3 + : 4) + : 7)) + ((((((self_in_CogX64Compiler->operands))[1]) & 7) == RSP + ? 1 + : 0)); + + case MoveRMbr: + case MoveRM8r: + assert(is32BitSignedImmediate(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[1])); + return ((isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[1]) + ? (((((self_in_CogX64Compiler->operands))[1]) == 0) + && (((((self_in_CogX64Compiler->operands))[0]) & 7) != RBP) + ? 3 + : 4) + : 7)) + ((((((self_in_CogX64Compiler->operands))[2]) & 7) == RSP + ? 1 + : 0)); + + case MoveM16rR: + assert(is32BitSignedImmediate(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0])); + return ((isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0]) + ? (((((self_in_CogX64Compiler->operands))[0]) == 0) + && (((((self_in_CogX64Compiler->operands))[1]) & 7) != RBP) + ? 4 + : 5) + : 8)) + ((((((self_in_CogX64Compiler->operands))[1]) & 7) == RSP + ? 1 + : 0)); + + case MoveRM16r: + assert(is32BitSignedImmediate(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[1])); + return (((isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[1]) + ? 4 + : 7)) + ((((((self_in_CogX64Compiler->operands))[2]) & 7) == RSP + ? 1 + : 0))) + ((((((self_in_CogX64Compiler->operands))[0]) > 7) + || ((((self_in_CogX64Compiler->operands))[2]) > 7) + ? 1 + : 0)); + + case MoveM32rR: + return (((isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0]) + ? (((((self_in_CogX64Compiler->operands))[0]) == 0) + && (((((self_in_CogX64Compiler->operands))[1]) & 6) != RSP) + ? 2 + : 3) + : 6)) + (((((((self_in_CogX64Compiler->operands))[1]) & 7) == RSP) + && ((((self_in_CogX64Compiler->operands))[0]) != 0) + ? 1 + : 0))) + ((((((self_in_CogX64Compiler->operands))[1]) > 7) + || ((((self_in_CogX64Compiler->operands))[2]) > 7) + ? 1 + : 0)); + + case MoveM32rRs: + case MoveM64rRd: + return (((isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0]) + ? (((((self_in_CogX64Compiler->operands))[0]) == 0) + && (((((self_in_CogX64Compiler->operands))[1]) & 6) != RSP) + ? 4 + : 5) + : 8)) + (((((((self_in_CogX64Compiler->operands))[1]) & 7) == RSP) + && ((((self_in_CogX64Compiler->operands))[0]) != 0) + ? 1 + : 0))) + ((((((self_in_CogX64Compiler->operands))[1]) > 7) + || ((((self_in_CogX64Compiler->operands))[2]) > 7) + ? 1 + : 0)); + + case MoveXbrRR: + assert((((self_in_CogX64Compiler->operands))[0]) != RSP); + return (((((self_in_CogX64Compiler->operands))[1]) & 7) == RBP + ? 5 + : 4); + + case MoveRXbrR: + assert((((self_in_CogX64Compiler->operands))[1]) != RSP); + return ((((((self_in_CogX64Compiler->operands))[0]) > 3) + || (((((self_in_CogX64Compiler->operands))[1]) > 7) + || ((((self_in_CogX64Compiler->operands))[2]) > 7)) + ? 4 + : 3)) + ((((((self_in_CogX64Compiler->operands))[2]) & 7) == RBP + ? 1 + : 0)); + + case MoveXwrRR: + assert((((self_in_CogX64Compiler->operands))[0]) != RSP); + return (((((self_in_CogX64Compiler->operands))[1]) == RBP) + || ((((self_in_CogX64Compiler->operands))[1]) == R13) + ? 5 + : 4); + + case MoveRXwrR: + assert((((self_in_CogX64Compiler->operands))[1]) != RSP); + return (((((self_in_CogX64Compiler->operands))[2]) == RBP) + || ((((self_in_CogX64Compiler->operands))[2]) == R13) + ? 5 + : 4); + + case MoveX32rRR: + assert((((self_in_CogX64Compiler->operands))[0]) != RSP); + return ((((((self_in_CogX64Compiler->operands))[1]) == RBP) + || ((((self_in_CogX64Compiler->operands))[1]) == R13) + ? 7 + : 6)) + ((((((self_in_CogX64Compiler->operands))[0]) > 7) + || (((((self_in_CogX64Compiler->operands))[1]) > 7) + || ((((self_in_CogX64Compiler->operands))[2]) > 7)) + ? 1 + : 0)); + + case MoveRX32rR: + assert((((self_in_CogX64Compiler->operands))[1]) != RSP); + return ((((((self_in_CogX64Compiler->operands))[2]) == RBP) + || ((((self_in_CogX64Compiler->operands))[2]) == R13) + ? 4 + : 3)) + ((((((self_in_CogX64Compiler->operands))[0]) > 7) + || (((((self_in_CogX64Compiler->operands))[1]) > 7) + || ((((self_in_CogX64Compiler->operands))[2]) > 7)) + ? 1 + : 0)); + + case PopR: + case PushR: + return ((((self_in_CogX64Compiler->operands))[0]) < 8 + ? 1 + : 2); + + case PushCq: + return (isQuick(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0]) + ? 2 + : (is32BitSignedImmediate(self_in_CogX64Compiler, ((self_in_CogX64Compiler->operands))[0]) + ? 5 + : computeSizeOfPushCw(self_in_CogX64Compiler))); + + case PushCw: + return computeSizeOfPushCw(self_in_CogX64Compiler); + + case PrefetchAw: + return (/* isAddressRelativeToVarBase: */ + ((((self_in_CogX64Compiler->operands))[0]) >= (varBaseAddress)) + && (((((self_in_CogX64Compiler->operands))[0]) - (varBaseAddress)) < (0x100000)) + ? 7 + : 0); + + case ZeroExtend32RR: + return 2 + ((((((self_in_CogX64Compiler->operands))[1]) > 7) + || ((((self_in_CogX64Compiler->operands))[0]) > 7) + ? 1 + : 0)); + + case MovePerfCnt64RL: + return concretizeMovePerfCnt64RL(self_in_CogX64Compiler); + + default: + error("Case not found and no otherwise clause"); + } + + /* Noops & Pseudo Ops + Specific Arithmetic + Processor Specific Data Movement + Control + Arithmetic + Data Movement + N.B. Always inlined. + Conversion + it is easier to calculate the right value by concretizing. We concretize twice, but so what? */ + return 0; +} + + +/* On the x86 the only instructions that shift by the value of a + register require the shift count to be in %ecx. So we may + have to use swap instructions to get the count into ecx. */ + + /* CogX64Compiler>>#computeShiftRRSize */ +static NoDbgRegParms int +computeShiftRRSize(AbstractInstruction *self_in_CogX64Compiler) +{ + sqInt shiftCountReg; + + shiftCountReg = ((self_in_CogX64Compiler->operands))[0]; + return (shiftCountReg == RCX + ? 3 + : (shiftCountReg + ? (/* XCHG r1,r2 */ + + 9) + : (/* XCHG RAX,r2 */ + + 7))); +} + + /* CogX64Compiler>>#computeSizeOfPushCw */ +static NoDbgRegParms sqInt +computeSizeOfPushCw(AbstractInstruction *self_in_CogX64Compiler) +{ + return (/* inCurrentCompilation: */ + (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])))) + || ((((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0]))) == (methodLabel))) + || (/* addressIsInCurrentCompilation: */ + ((((usqInt)(((self_in_CogX64Compiler->operands))[0]))) >= ((methodLabel->address))) + && ((((usqInt)(((self_in_CogX64Compiler->operands))[0]))) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize))))) + ? 9 + : 12 /* pushCwByteSize */); +} + + +/* Will get inlined into concretizeAt: switch. */ + + /* CogX64Compiler>>#concretizeArithCqRWithRO:raxOpcode: */ +static NoDbgRegParms sqInt +concretizeArithCqRWithROraxOpcode(AbstractInstruction *self_in_CogX64Compiler, sqInt regOpcode, sqInt raxOpcode) +{ + sqInt reg; + sqInt value; + + value = ((self_in_CogX64Compiler->operands))[0]; + reg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + if (isQuick(self_in_CogX64Compiler, value)) { + ((self_in_CogX64Compiler->machineCode))[1] = 131; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, regOpcode)); + ((self_in_CogX64Compiler->machineCode))[3] = (value & 0xFF); + return 4; + } + if (is32BitSignedImmediate(self_in_CogX64Compiler, value)) { + if (!reg) { + ((self_in_CogX64Compiler->machineCode))[1] = raxOpcode; + ((self_in_CogX64Compiler->machineCode))[2] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 24) & 0xFF); + return 6; + } + ((self_in_CogX64Compiler->machineCode))[1] = 129; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, regOpcode)); + ((self_in_CogX64Compiler->machineCode))[3] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(value)) >> 24) & 0xFF); + return 7; + } + return concretizeArithCwR(self_in_CogX64Compiler, (raxOpcode == 61 + ? 57 + : raxOpcode - 2)); +} + + /* CogX64Compiler>>#concretizeFill32 */ +static NoDbgRegParms sqInt +concretizeFill32(AbstractInstruction *self_in_CogX64Compiler) +{ + usqIntptr_t word; + + word = ((self_in_CogX64Compiler->operands))[0]; + ((self_in_CogX64Compiler->machineCode))[0] = (word & 0xFF); + ((self_in_CogX64Compiler->machineCode))[1] = (((word) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[2] = (((word) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = (((word) >> 24) & 0xFF); + return 4; +} + + +/* Generate code for + 0x0: 50 pushq %rax + 0x1: 52 pushq %rdx + 0x2: 0f 31 rdtsc + 0x4: 48 c1 e2 20 shlq $0x20, %rdx + 0x8: 48 09 d0 orq %rdx, %rax + 0xb: 48 89 f8 movq %rdi, %rax + 0xe: 5a popq %rdx + 0xf: 58 popq %rax + et al */ + + /* CogX64Compiler>>#concretizeMovePerfCnt64RL */ +static NoDbgRegParms sqInt +concretizeMovePerfCnt64RL(AbstractInstruction *self_in_CogX64Compiler) +{ + sqInt liveRegisterMask; + sqInt offset; + sqInt reg; + + reg = ((self_in_CogX64Compiler->operands))[0]; + liveRegisterMask = ((self_in_CogX64Compiler->operands))[1]; + offset = 0; + if (((liveRegisterMask & ((1U << RAX))) != 0)) { + ((self_in_CogX64Compiler->machineCode))[0] = 80; + offset += 1; + } + if (((liveRegisterMask & ((1U << RDX))) != 0)) { + ((self_in_CogX64Compiler->machineCode))[offset] = 82; + offset += 1; + } + + /* too lazy to define the swap cases for the moment... */ + assert(!((reg == RDX))); + ((self_in_CogX64Compiler->machineCode))[offset] = 15; + ((self_in_CogX64Compiler->machineCode))[offset + 1] = 49; + ((self_in_CogX64Compiler->machineCode))[offset + 2] = (rexRxb(self_in_CogX64Compiler, 0, 0, RDX)); + ((self_in_CogX64Compiler->machineCode))[offset + 3] = 193; + ((self_in_CogX64Compiler->machineCode))[offset + 4] = (modRMRO(self_in_CogX64Compiler, ModReg, RDX, 4)); + ((self_in_CogX64Compiler->machineCode))[offset + 5] = 32; + ((self_in_CogX64Compiler->machineCode))[offset + 6] = (rexRxb(self_in_CogX64Compiler, RAX, 0, RDX)); + ((self_in_CogX64Compiler->machineCode))[offset + 7] = 11; + ((self_in_CogX64Compiler->machineCode))[offset + 8] = (modRMRO(self_in_CogX64Compiler, ModReg, RDX, RAX)); + + /* rdtsc + shlq $0x20, %rdx + orq %rax, %rdx (%rax := %rax bitOr: %rdx */ + offset += 9; + if (reg) { + ((self_in_CogX64Compiler->machineCode))[offset] = (rexRxb(self_in_CogX64Compiler, RAX, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[offset + 1] = 137; + ((self_in_CogX64Compiler->machineCode))[offset + 2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, RAX)); + + /* movq %rDEST, %rax */ + offset += 3; + } + if (((liveRegisterMask & ((1U << RDX))) != 0)) { + ((self_in_CogX64Compiler->machineCode))[offset] = 90; + offset += 1; + } + if (((liveRegisterMask & ((1U << RAX))) != 0)) { + ((self_in_CogX64Compiler->machineCode))[offset] = 88; + offset += 1; + } + return offset; +} + + /* CogX64Compiler>>#concretizeMoveRX32rR */ +static NoDbgRegParms sqInt +concretizeMoveRX32rR(AbstractInstruction *self_in_CogX64Compiler) +{ + sqInt base; + sqInt index; + sqInt offset; + sqInt src; + + src = ((self_in_CogX64Compiler->operands))[0]; + index = ((self_in_CogX64Compiler->operands))[1]; + base = ((self_in_CogX64Compiler->operands))[2]; + if ((index > 7) + || ((base > 7) + || (src > 7))) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 0, src, index, base)); + offset = 1; + } + else { + offset = 0; + } + if ((base & 7) != RBP) { + ((self_in_CogX64Compiler->machineCode))[offset] = 137; + ((self_in_CogX64Compiler->machineCode))[offset + 1] = (modRMRO(self_in_CogX64Compiler, ModRegInd, 4, src)); + ((self_in_CogX64Compiler->machineCode))[offset + 2] = (sib(self_in_CogX64Compiler, SIB4, index, base)); + return offset + 3; + } + ((self_in_CogX64Compiler->machineCode))[offset] = 137; + ((self_in_CogX64Compiler->machineCode))[offset + 1] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, 4, src)); + ((self_in_CogX64Compiler->machineCode))[offset + 2] = (sib(self_in_CogX64Compiler, SIB4, index, base)); + ((self_in_CogX64Compiler->machineCode))[offset + 3] = 0; + return offset + 4; +} + + +/* MoveX32rRR is expected to zero-extend, so explicitly zero the destination. */ + + /* CogX64Compiler>>#concretizeMoveX32rRR */ +static NoDbgRegParms sqInt +concretizeMoveX32rRR(AbstractInstruction *self_in_CogX64Compiler) +{ + sqInt base; + sqInt dest; + sqInt index; + sqInt offset; + + index = ((self_in_CogX64Compiler->operands))[0]; + base = ((self_in_CogX64Compiler->operands))[1]; + dest = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, dest, 0, dest)); + ((self_in_CogX64Compiler->machineCode))[1] = 49; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, dest, dest)); + if ((index > 7) + || ((base > 7) + || (dest > 7))) { + ((self_in_CogX64Compiler->machineCode))[3] = (rexwrxb(self_in_CogX64Compiler, 0, dest, index, base)); + offset = 1; + } + else { + offset = 0; + } + if ((base & 7) != RBP) { + ((self_in_CogX64Compiler->machineCode))[offset + 3] = 139; + ((self_in_CogX64Compiler->machineCode))[offset + 4] = (modRMRO(self_in_CogX64Compiler, ModRegInd, 4, dest)); + ((self_in_CogX64Compiler->machineCode))[offset + 5] = (sib(self_in_CogX64Compiler, SIB4, index, base)); + return offset + 6; + } + ((self_in_CogX64Compiler->machineCode))[offset + 3] = 139; + ((self_in_CogX64Compiler->machineCode))[offset + 4] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, 4, dest)); + ((self_in_CogX64Compiler->machineCode))[offset + 5] = (sib(self_in_CogX64Compiler, SIB4, index, base)); + ((self_in_CogX64Compiler->machineCode))[offset + 6] = 0; + return offset + 7; +} + + /* CogX64Compiler>>#concretizeOpRR: */ +static NoDbgRegParms sqInt +concretizeOpRR(AbstractInstruction *self_in_CogX64Compiler, sqInt x64opcode) +{ + sqInt regLHS; + sqInt regRHS; + + regLHS = ((self_in_CogX64Compiler->operands))[0]; + regRHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, regRHS, 0, regLHS)); + ((self_in_CogX64Compiler->machineCode))[1] = x64opcode; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, regLHS, regRHS)); + return 3; +} + + +/* We support only prefetches for addresses that are variables relative to + VarBase + */ + + /* CogX64Compiler>>#concretizePrefetchAw */ +static NoDbgRegParms sqInt +concretizePrefetchAw(AbstractInstruction *self_in_CogX64Compiler) +{ + sqInt offset; + sqInt operand; + + operand = ((self_in_CogX64Compiler->operands))[0]; + if (!(/* isAddressRelativeToVarBase: */ + (operand >= (varBaseAddress)) + && ((operand - (varBaseAddress)) < (0x100000)))) { + return 0; + } + offset = operand - (varBaseAddress); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = 24; + ((self_in_CogX64Compiler->machineCode))[2] = 147; + ((self_in_CogX64Compiler->machineCode))[3] = (offset & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offset)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offset)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = (((usqInt)(offset)) >> 24); + return 7; +} + + +/* CmpRR/MoveRR RHS LHS computes LHS - RHS, i.e. apparently reversed. You + have to think subtract. */ + + /* CogX64Compiler>>#concretizeReverseOpRR: */ +static NoDbgRegParms sqInt +concretizeReverseOpRR(AbstractInstruction *self_in_CogX64Compiler, sqInt x64opcode) +{ + sqInt regLHS; + sqInt regRHS; + + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, regRHS, 0, regLHS)); + ((self_in_CogX64Compiler->machineCode))[1] = x64opcode; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, regLHS, regRHS)); + return 3; +} + + /* CogX64Compiler>>#concretizeXCHGRR */ +static NoDbgRegParms sqInt +concretizeXCHGRR(AbstractInstruction *self_in_CogX64Compiler) +{ + sqInt r1; + sqInt r2; + + r1 = ((self_in_CogX64Compiler->operands))[0]; + r2 = ((self_in_CogX64Compiler->operands))[1]; + if (!r2) { + r2 = r1; + r1 = RAX; + } + if (!r1) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, r2)); + ((self_in_CogX64Compiler->machineCode))[1] = (144 + (r2 % 8)); + return 2; + } + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, r1, 0, r2)); + ((self_in_CogX64Compiler->machineCode))[1] = 135; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, r2, r1)); + return 3; +} + + +/* Do a throw-away compilation to get at the cpuid info and initialize + cpuidWord1 N.B. All of MSVC, gcc & clang have intrinsics for this, so if + you have the energy + by all means reimplement as an #if _MSC_VER...#elif __GNUC__ #else ... + saga. + */ + + /* CogX64Compiler>>#detectFeatures */ +static NoDbgRegParms AbstractInstruction * +detectFeatures(AbstractInstruction *self_in_CogX64Compiler) +{ + usqIntptr_t (*cpuid)(void); + usqIntptr_t fixupSize; + usqIntptr_t opcodeSize; + usqInt startAddress; + + startAddress = methodZoneBase; + + /* begin allocateOpcodes:bytecodes: */ + numAbstractOpcodes = 10; + opcodeSize = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupSize = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + abstractOpcodes = alloca(opcodeSize + fixupSize); + bzero(abstractOpcodes, opcodeSize + fixupSize); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeSize)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + cpuid = ((usqIntptr_t (*)(void)) startAddress); + + /* PushR: */ + genoperand(PushR, RDX); + + /* PushR: */ + genoperand(PushR, RCX); + + /* PushR: */ + genoperand(PushR, RBX); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0x80000001U, RAX); + gen(CPUID); + + /* MoveR:R: */ + genoperandoperand(MoveRR, RCX, RAX); + + /* PopR: */ + genoperand(PopR, RBX); + + /* PopR: */ + genoperand(PopR, RCX); + + /* PopR: */ + genoperand(PopR, RDX); + + /* RetN: */ + genoperand(RetN, 0); + outputInstructionsForGeneratedRuntimeAt(startAddress); + + /* begin resetMethodZoneBase: */ + methodZoneBase = startAddress; + setCpuidWord1(self_in_CogX64Compiler, cpuid()); + return self_in_CogX64Compiler; +} + + +/* Attempt to generate concrete machine code for the instruction at address. + This is the inner dispatch of concretizeAt: actualAddress which exists + only to get around the branch size limits in the SqueakV3 (blue book + derived) bytecode set. */ + + /* CogX64Compiler>>#dispatchConcretize */ +static NoDbgRegParms sqInt +dispatchConcretize(AbstractInstruction *self_in_CogX64Compiler) +{ + sqInt addressOperand; + sqInt base; + sqInt baseReg; + AbstractInstruction *dependentChain; + sqInt dest; + sqInt destReg; + sqInt distance; + sqInt i; + sqInt index; + AbstractInstruction *jumpTarget; + sqInt maskReg; + int offset; + sqInt offset1; + sqInt offsetSqInt; + sqInt operand; + sqInt reg; + sqInt regLHS; + sqInt regRHS; + sqInt regToShift; + sqInt save0; + sqInt save1; + sqInt savedSize; + sqInt shiftCountReg; + sqInt skip; + sqInt src; + sqInt srcReg; + sqInt value; + sqInt valueSqInt; + usqIntptr_t word; + + if (((self_in_CogX64Compiler->opcode)) >= CPUID) { + return dispatchConcretizeProcessorSpecific(self_in_CogX64Compiler); + } + switch ((self_in_CogX64Compiler->opcode)) { + case Label: + /* begin concretizeLabel */ + dependentChain = (self_in_CogX64Compiler->dependent); + while (!(!dependentChain)) { + /* begin updateLabel: */ + assert((((dependentChain->opcode)) == MoveCwR) + || (((dependentChain->opcode)) == PushCw)); + ((dependentChain->operands))[0] = (((self_in_CogX64Compiler->address)) + (((self_in_CogX64Compiler->operands))[1])); + dependentChain = (dependentChain->dependent); + } + return 0; + + case AlignmentNops: + /* begin concretizeAlignmentNops */ + for (i = 0; i < ((self_in_CogX64Compiler->machineCodeSize)); i += 1) { + ((self_in_CogX64Compiler->machineCode))[i] = 144; + } + return (self_in_CogX64Compiler->machineCodeSize); + + case Fill32: + /* begin concretizeFill32 */ + word = ((self_in_CogX64Compiler->operands))[0]; + ((self_in_CogX64Compiler->machineCode))[0] = (word & 0xFF); + ((self_in_CogX64Compiler->machineCode))[1] = (((word) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[2] = (((word) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = (((word) >> 24) & 0xFF); + return 4; + + case Nop: + /* begin concretizeNop */ + ((self_in_CogX64Compiler->machineCode))[0] = 144; + return 1; + + case Call: + /* begin concretizeCall */ + assert((((self_in_CogX64Compiler->operands))[0]) != 0); + offset = (((int) (((self_in_CogX64Compiler->operands))[0]))) - (((int) (((self_in_CogX64Compiler->address)) + 5))); + ((self_in_CogX64Compiler->machineCode))[0] = 232; + ((self_in_CogX64Compiler->machineCode))[1] = (offset & 0xFF); + ((self_in_CogX64Compiler->machineCode))[2] = ((((usqInt)(offset)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offset)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offset)) >> 24) & 0xFF); + return 5; + + case CallR: + /* begin concretizeCallR */ + reg = ((self_in_CogX64Compiler->operands))[0]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = 0xFF; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 2)); + return 3; + + case CallFull: + /* begin concretizeCallFull */ + operand = ((self_in_CogX64Compiler->operands))[0]; + ((self_in_CogX64Compiler->machineCode))[0] = 72; + ((self_in_CogX64Compiler->machineCode))[1] = 184; + ((self_in_CogX64Compiler->machineCode))[2] = (operand & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(operand)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(operand)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(operand)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(operand)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(operand)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8] = ((((usqInt)(operand)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[9] = ((((usqInt)(operand)) >> 56) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[10] = 0xFF; + ((self_in_CogX64Compiler->machineCode))[11] = (modRMRO(self_in_CogX64Compiler, ModReg, RAX, 2)); + return 12; + + case JumpR: + /* begin concretizeJumpR */ + reg = ((self_in_CogX64Compiler->operands))[0]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = 0xFF; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 4)); + return 3; + + case JumpFull: + /* begin concretizeJumpFull */ + operand = ((self_in_CogX64Compiler->operands))[0]; + ((self_in_CogX64Compiler->machineCode))[0] = 72; + ((self_in_CogX64Compiler->machineCode))[1] = 184; + ((self_in_CogX64Compiler->machineCode))[2] = (operand & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(operand)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(operand)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(operand)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(operand)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(operand)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8] = ((((usqInt)(operand)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[9] = ((((usqInt)(operand)) >> 56) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[10] = 0xFF; + ((self_in_CogX64Compiler->machineCode))[11] = (modRMRO(self_in_CogX64Compiler, ModReg, RAX, 4)); + return 12; + + case JumpLong: + /* begin concretizeJumpLong */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset = ((int) ((((char *)jumpTarget)) - (((char *)(((self_in_CogX64Compiler->address)) + 5))))); + ((self_in_CogX64Compiler->machineCode))[0] = 233; + ((self_in_CogX64Compiler->machineCode))[1] = (offset & 0xFF); + ((self_in_CogX64Compiler->machineCode))[2] = ((((usqInt)(offset)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offset)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offset)) >> 24) & 0xFF); + return 5; + + case JumpLongZero: + case JumpZero: + case JumpFPEqual: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (116); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (132); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpLongNonZero: + case JumpNonZero: + case JumpFPNotEqual: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (117); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (133); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case Jump: + /* begin concretizeJump */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset = ((int) ((((char *)jumpTarget)) - (((char *)(((self_in_CogX64Compiler->address)) + 2))))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset))) { + ((self_in_CogX64Compiler->machineCode))[0] = 235; + ((self_in_CogX64Compiler->machineCode))[1] = (offset & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + offset = ((int) ((((char *)jumpTarget)) - (((char *)(((self_in_CogX64Compiler->address)) + 5))))); + ((self_in_CogX64Compiler->machineCode))[0] = 233; + ((self_in_CogX64Compiler->machineCode))[1] = (offset & 0xFF); + ((self_in_CogX64Compiler->machineCode))[2] = ((((usqInt)(offset)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offset)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offset)) >> 24) & 0xFF); + return 5; + + case JumpNegative: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (120); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (136); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpNonNegative: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (121); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (137); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpOverflow: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (112); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (128); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpNoOverflow: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (113); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (129); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpCarry: + case JumpBelow: + case JumpFPLess: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (114); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (130); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpNoCarry: + case JumpAboveOrEqual: + case JumpFPGreaterOrEqual: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (115); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (131); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpLess: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (0x7C); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (140); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpGreaterOrEqual: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (125); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (141); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpGreater: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (0x7F); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (143); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpLessOrEqual: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (0x7E); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (142); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpAbove: + case JumpFPGreater: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (119); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (135); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpBelowOrEqual: + case JumpFPLessOrEqual: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (118); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (134); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpFPOrdered: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (123); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (139); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case JumpFPUnordered: + /* begin concretizeConditionalJump: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offset1 = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 2))); + if (((self_in_CogX64Compiler->machineCodeSize) + ? ((self_in_CogX64Compiler->machineCodeSize)) == 2 + : isQuick(self_in_CogX64Compiler, offset1))) { + ((self_in_CogX64Compiler->machineCode))[0] = (122); + ((self_in_CogX64Compiler->machineCode))[1] = (offset1 & 0xFF); + return 2; + } + + /* size not determined because no sizeJump pass; generating initial trampolines */ + + /* begin concretizeConditionalJumpLong: */ + /* begin computeJumpTargetOffsetPlus: */ + /* begin jumpTargetAddress */ + jumpTarget = ((AbstractInstruction *) (((self_in_CogX64Compiler->operands))[0])); + assertSaneJumpTarget(jumpTarget); + if (/* isAnInstruction: */ + (addressIsInInstructions(jumpTarget)) + || (jumpTarget == (methodLabel))) { + jumpTarget = ((AbstractInstruction *) ((jumpTarget->address))); + } + assert(jumpTarget != 0); + offsetSqInt = (((sqLong) jumpTarget)) - (((sqLong) (((self_in_CogX64Compiler->address)) + 6))); + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = (138); + ((self_in_CogX64Compiler->machineCode))[2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 6; + + case RetN: + /* begin concretizeRetN */ + offsetSqInt = ((self_in_CogX64Compiler->operands))[0]; + if (!offsetSqInt) { + ((self_in_CogX64Compiler->machineCode))[0] = 195; + return 1; + } + ((self_in_CogX64Compiler->machineCode))[0] = 194; + ((self_in_CogX64Compiler->machineCode))[1] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[2] = (((usqInt)(offsetSqInt)) >> 8); + return 3; + + case Stop: + /* begin concretizeStop */ + ((self_in_CogX64Compiler->machineCode))[0] = 204; + return 1; + + case AddCqR: + return concretizeArithCqRWithROraxOpcode(self_in_CogX64Compiler, 0, 5); + + case AddcCqR: + return concretizeArithCqRWithROraxOpcode(self_in_CogX64Compiler, 2, 20); + + case AddCwR: + return concretizeArithCwR(self_in_CogX64Compiler, 3); + + case AddRR: + return concretizeOpRR(self_in_CogX64Compiler, 3); + + case AddRsRs: + /* begin concretizeSEEOpRsRs: */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 88; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 4; + + case AddRdRd: + /* begin concretizeSEE2OpRdRd: */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 242; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 88; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 4; + + case AndCqR: + return concretizeArithCqRWithROraxOpcode(self_in_CogX64Compiler, 4, 37); + + case AndCwR: + return concretizeArithCwR(self_in_CogX64Compiler, 35); + + case AndRR: + return concretizeOpRR(self_in_CogX64Compiler, 35); + + case TstCqR: + /* begin concretizeTstCqR */ + value = ((self_in_CogX64Compiler->operands))[0]; + reg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + if (isQuick(self_in_CogX64Compiler, value)) { + ((self_in_CogX64Compiler->machineCode))[1] = 246; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 0)); + ((self_in_CogX64Compiler->machineCode))[3] = (value & 0xFF); + return 4; + } + if (is32BitSignedImmediate(self_in_CogX64Compiler, value)) { + if (!reg) { + ((self_in_CogX64Compiler->machineCode))[1] = 169; + ((self_in_CogX64Compiler->machineCode))[2] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 24) & 0xFF); + return 6; + } + ((self_in_CogX64Compiler->machineCode))[1] = 247; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 0)); + ((self_in_CogX64Compiler->machineCode))[3] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(value)) >> 24) & 0xFF); + return 7; + } + return concretizeArithCwR(self_in_CogX64Compiler, 133); + + case CmpCqR: + return concretizeArithCqRWithROraxOpcode(self_in_CogX64Compiler, 7, 61); + + case CmpCwR: + return concretizeArithCwR(self_in_CogX64Compiler, 57); + + case CmpC32R: + /* begin concretizeCmpC32R */ + value = ((self_in_CogX64Compiler->operands))[0]; + reg = ((self_in_CogX64Compiler->operands))[1]; + if (reg) { + if (reg > 7) { + ((self_in_CogX64Compiler->machineCode))[0] = 65; + skip = 2; + } + else { + skip = 1; + } + ((self_in_CogX64Compiler->machineCode))[skip - 1] = 129; + ((self_in_CogX64Compiler->machineCode))[skip] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 7)); + } + else { + ((self_in_CogX64Compiler->machineCode))[0] = 61; + skip = 0; + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 2] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 3] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = ((((usqInt)(value)) >> 24) & 0xFF); + return 5 + skip; + + case CmpRR: + return concretizeReverseOpRR(self_in_CogX64Compiler, 57); + + case CmpRdRd: + /* begin concretizeCmpRdRd */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 102; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 46; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 4; + + case CmpRsRs: + /* begin concretizeCmpRsRs */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 46; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 3; + + case DivRdRd: + /* begin concretizeSEE2OpRdRd: */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 242; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 94; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 4; + + case DivRsRs: + /* begin concretizeSEEOpRsRs: */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 94; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 4; + + case MulRdRd: + /* begin concretizeSEE2OpRdRd: */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 242; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 89; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 4; + + case MulRsRs: + /* begin concretizeSEEOpRsRs: */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 89; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 4; + + case OrCqR: + return concretizeArithCqRWithROraxOpcode(self_in_CogX64Compiler, 1, 13); + + case OrCwR: + return concretizeArithCwR(self_in_CogX64Compiler, 11); + + case OrRR: + return concretizeOpRR(self_in_CogX64Compiler, 11); + + case SubCqR: + return concretizeArithCqRWithROraxOpcode(self_in_CogX64Compiler, 5, 45); + + case SubbCqR: + return concretizeArithCqRWithROraxOpcode(self_in_CogX64Compiler, 3, 29); + + case SubCwR: + return concretizeArithCwR(self_in_CogX64Compiler, 43); + + case SubRR: + return concretizeOpRR(self_in_CogX64Compiler, 43); + + case SubRdRd: + /* begin concretizeSEE2OpRdRd: */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 242; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 92; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 4; + + case SubRsRs: + /* begin concretizeSEEOpRsRs: */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 92; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 4; + + case SqrtRd: + /* begin concretizeSqrtRd */ + reg = ((self_in_CogX64Compiler->operands))[0]; + ((self_in_CogX64Compiler->machineCode))[0] = 242; + if (reg <= 7) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, reg, 0, reg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 81; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, reg)); + return skip + 4; + + case SqrtRs: + /* begin concretizeSqrtRs */ + reg = ((self_in_CogX64Compiler->operands))[0]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + if (reg <= 7) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, reg, 0, reg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 81; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, reg)); + return skip + 4; + + case XorCqR: + return concretizeArithCqRWithROraxOpcode(self_in_CogX64Compiler, 6, 35); + + case XorCwR: + return concretizeArithCwR(self_in_CogX64Compiler, 51); + + case XorRR: + return concretizeOpRR(self_in_CogX64Compiler, 51); + + case XorRdRd: + /* begin concretizeXorRdRd */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 102; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 87; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 4; + + case XorRsRs: + /* begin concretizeXorRsRs */ + regRHS = ((self_in_CogX64Compiler->operands))[0]; + regLHS = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 15; + if ((regLHS <= 7) + && (regRHS <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, regLHS, 0, regRHS)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 87; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (modRMRO(self_in_CogX64Compiler, ModReg, regRHS, regLHS)); + return skip + 3; + + case NegateR: + /* begin concretizeNegateR */ + reg = ((self_in_CogX64Compiler->operands))[0]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = 247; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 3)); + return 3; + + case LoadEffectiveAddressMwrR: + /* begin concretizeLoadEffectiveAddressMwrR */ + offsetSqInt = ((self_in_CogX64Compiler->operands))[0]; + srcReg = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 141; + if ((srcReg != RSP) + && (srcReg != R12)) { + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + return 4; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 7; + } + + /* ESP/R12: */ + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + return 5; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 8; + + case RotateLeftCqR: + /* begin concretizeShiftCqRegOpcode: */ + distance = ((self_in_CogX64Compiler->operands))[0]; + assert(((distance >= 1) && (distance <= 0x3F))); + reg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + if (distance == 1) { + ((self_in_CogX64Compiler->machineCode))[1] = 209; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 0)); + return 3; + } + ((self_in_CogX64Compiler->machineCode))[1] = 193; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 0)); + ((self_in_CogX64Compiler->machineCode))[3] = distance; + return 4; + + case RotateRightCqR: + /* begin concretizeShiftCqRegOpcode: */ + distance = ((self_in_CogX64Compiler->operands))[0]; + assert(((distance >= 1) && (distance <= 0x3F))); + reg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + if (distance == 1) { + ((self_in_CogX64Compiler->machineCode))[1] = 209; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 1)); + return 3; + } + ((self_in_CogX64Compiler->machineCode))[1] = 193; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 1)); + ((self_in_CogX64Compiler->machineCode))[3] = distance; + return 4; + + case ArithmeticShiftRightCqR: + /* begin concretizeShiftCqRegOpcode: */ + distance = ((self_in_CogX64Compiler->operands))[0]; + assert(((distance >= 1) && (distance <= 0x3F))); + reg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + if (distance == 1) { + ((self_in_CogX64Compiler->machineCode))[1] = 209; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 7)); + return 3; + } + ((self_in_CogX64Compiler->machineCode))[1] = 193; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 7)); + ((self_in_CogX64Compiler->machineCode))[3] = distance; + return 4; + + case LogicalShiftRightCqR: + /* begin concretizeShiftCqRegOpcode: */ + distance = ((self_in_CogX64Compiler->operands))[0]; + assert(((distance >= 1) && (distance <= 0x3F))); + reg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + if (distance == 1) { + ((self_in_CogX64Compiler->machineCode))[1] = 209; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 5)); + return 3; + } + ((self_in_CogX64Compiler->machineCode))[1] = 193; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 5)); + ((self_in_CogX64Compiler->machineCode))[3] = distance; + return 4; + + case LogicalShiftLeftCqR: + /* begin concretizeShiftCqRegOpcode: */ + distance = ((self_in_CogX64Compiler->operands))[0]; + assert(((distance >= 1) && (distance <= 0x3F))); + reg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + if (distance == 1) { + ((self_in_CogX64Compiler->machineCode))[1] = 209; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 4)); + return 3; + } + ((self_in_CogX64Compiler->machineCode))[1] = 193; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 4)); + ((self_in_CogX64Compiler->machineCode))[3] = distance; + return 4; + + case ArithmeticShiftRightRR: + /* begin concretizeShiftRegRegOpcode: */ + shiftCountReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + if (shiftCountReg == RCX) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, destReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 211; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, destReg, 7)); + return 3; + } + regToShift = (destReg == shiftCountReg + ? RCX + : (destReg == RCX + ? shiftCountReg + : destReg)); + if (!shiftCountReg) { + ((self_in_CogX64Compiler->machineCode))[0] = 72; + ((self_in_CogX64Compiler->machineCode))[1] = (144 + RCX); + ((self_in_CogX64Compiler->machineCode))[2] = (rexRxb(self_in_CogX64Compiler, 0, 0, regToShift)); + ((self_in_CogX64Compiler->machineCode))[3] = 211; + ((self_in_CogX64Compiler->machineCode))[4] = (modRMRO(self_in_CogX64Compiler, ModReg, regToShift, 7)); + ((self_in_CogX64Compiler->machineCode))[5] = 72; + ((self_in_CogX64Compiler->machineCode))[6] = (144 + RCX); + return 7; + } + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, shiftCountReg, 0, RCX)); + ((self_in_CogX64Compiler->machineCode))[1] = 135; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, RCX, shiftCountReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (rexRxb(self_in_CogX64Compiler, 0, 0, regToShift)); + ((self_in_CogX64Compiler->machineCode))[4] = 211; + ((self_in_CogX64Compiler->machineCode))[5] = (modRMRO(self_in_CogX64Compiler, ModReg, regToShift, 7)); + ((self_in_CogX64Compiler->machineCode))[6] = (rexRxb(self_in_CogX64Compiler, shiftCountReg, 0, RCX)); + ((self_in_CogX64Compiler->machineCode))[7] = 135; + ((self_in_CogX64Compiler->machineCode))[8] = (modRMRO(self_in_CogX64Compiler, ModReg, RCX, shiftCountReg)); + + /* XCHG R?X,RCX + SAR RCX,R!X + XCHG R?X,RCX */ + return 9; + + case LogicalShiftLeftRR: + /* begin concretizeShiftRegRegOpcode: */ + shiftCountReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + if (shiftCountReg == RCX) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, destReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 211; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, destReg, 4)); + return 3; + } + regToShift = (destReg == shiftCountReg + ? RCX + : (destReg == RCX + ? shiftCountReg + : destReg)); + if (!shiftCountReg) { + ((self_in_CogX64Compiler->machineCode))[0] = 72; + ((self_in_CogX64Compiler->machineCode))[1] = (144 + RCX); + ((self_in_CogX64Compiler->machineCode))[2] = (rexRxb(self_in_CogX64Compiler, 0, 0, regToShift)); + ((self_in_CogX64Compiler->machineCode))[3] = 211; + ((self_in_CogX64Compiler->machineCode))[4] = (modRMRO(self_in_CogX64Compiler, ModReg, regToShift, 4)); + ((self_in_CogX64Compiler->machineCode))[5] = 72; + ((self_in_CogX64Compiler->machineCode))[6] = (144 + RCX); + return 7; + } + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, shiftCountReg, 0, RCX)); + ((self_in_CogX64Compiler->machineCode))[1] = 135; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, RCX, shiftCountReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (rexRxb(self_in_CogX64Compiler, 0, 0, regToShift)); + ((self_in_CogX64Compiler->machineCode))[4] = 211; + ((self_in_CogX64Compiler->machineCode))[5] = (modRMRO(self_in_CogX64Compiler, ModReg, regToShift, 4)); + ((self_in_CogX64Compiler->machineCode))[6] = (rexRxb(self_in_CogX64Compiler, shiftCountReg, 0, RCX)); + ((self_in_CogX64Compiler->machineCode))[7] = 135; + ((self_in_CogX64Compiler->machineCode))[8] = (modRMRO(self_in_CogX64Compiler, ModReg, RCX, shiftCountReg)); + + /* XCHG R?X,RCX + SAR RCX,R!X + XCHG R?X,RCX */ + return 9; + + case ClzRR: + /* begin concretizeClzRR */ + assert(hasLZCNTInstructions(self_in_CogX64Compiler)); + maskReg = ((self_in_CogX64Compiler->operands))[0]; + dest = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + ((self_in_CogX64Compiler->machineCode))[1] = (rexwrxb(self_in_CogX64Compiler, 1, dest, 0, maskReg)); + ((self_in_CogX64Compiler->machineCode))[2] = 15; + ((self_in_CogX64Compiler->machineCode))[3] = 189; + ((self_in_CogX64Compiler->machineCode))[4] = (modRMRO(self_in_CogX64Compiler, ModReg, maskReg, dest)); + return 5; + + case MoveCqR: + /* begin concretizeMoveCqR */ + value = ((self_in_CogX64Compiler->operands))[0]; + reg = ((self_in_CogX64Compiler->operands))[1]; + if (is32BitSignedImmediate(self_in_CogX64Compiler, value)) { + if (!value) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, reg, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = 49; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, reg)); + return 3; + } + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = 199; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 0)); + ((self_in_CogX64Compiler->machineCode))[3] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(value)) >> 24) & 0xFF); + return 7; + } + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = (184 + (reg & 7)); + ((self_in_CogX64Compiler->machineCode))[2] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(value)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(value)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8] = ((((usqInt)(value)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[9] = ((((usqInt)(value)) >> 56) & 0xFF); + return 10; + + case MoveCwR: + /* begin concretizeMoveCwR */ + value = ((self_in_CogX64Compiler->operands))[0]; + reg = ((self_in_CogX64Compiler->operands))[1]; + if (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) value))) + || ((((AbstractInstruction *) value)) == (methodLabel))) { + value = ((((AbstractInstruction *) value))->address); + } + if (/* addressIsInCurrentCompilation: */ + ((((usqInt)value)) >= ((methodLabel->address))) + && ((((usqInt)value)) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize))))) { + offsetSqInt = value - (((self_in_CogX64Compiler->address)) + 7); + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, reg, 0, 0)); + ((self_in_CogX64Compiler->machineCode))[1] = 141; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, 5, reg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + + /* LoadEffectiveAddress */ + return 7; + } + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = (184 + (reg & 7)); + ((self_in_CogX64Compiler->machineCode))[2] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(value)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(value)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8] = ((((usqInt)(value)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[9] = ((((usqInt)(value)) >> 56) & 0xFF); + + /* Add a nop to disambiguate between MoveCwR/PushCw and ArithCwR, which ends with a (self mod: ModReg RM: 0 RO: 0) */ + ((self_in_CogX64Compiler->machineCode))[10] = 144; + return 11; + + case MoveC32R: + /* begin concretizeMoveC32R */ + value = ((self_in_CogX64Compiler->operands))[0]; + reg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = 199; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, reg, 0)); + ((self_in_CogX64Compiler->machineCode))[3] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(value)) >> 24) & 0xFF); + return 7; + + case MoveRR: + return concretizeReverseOpRR(self_in_CogX64Compiler, 137); + + case MoveAwR: + /* begin concretizeMoveAwR */ + addressOperand = ((self_in_CogX64Compiler->operands))[0]; + if (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) addressOperand))) + || ((((AbstractInstruction *) addressOperand)) == (methodLabel))) { + addressOperand = ((((AbstractInstruction *) addressOperand))->address); + } + if (/* isAddressRelativeToVarBase: */ + (addressOperand >= (varBaseAddress)) + && ((addressOperand - (varBaseAddress)) < (0x100000))) { + save0 = ((self_in_CogX64Compiler->operands))[0]; + save1 = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->operands))[0] = (addressOperand - (varBaseAddress)); + ((self_in_CogX64Compiler->operands))[1] = RBX; + ((self_in_CogX64Compiler->operands))[2] = save1; + + /* begin concretizeMoveMwrR */ + offsetSqInt = ((self_in_CogX64Compiler->operands))[0]; + srcReg = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 139; + if ((srcReg != RSP) + && (srcReg != R12)) { + if ((offsetSqInt == 0) + && ((srcReg != RBP) + && (srcReg != R13))) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + savedSize = 3; + goto l1; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + savedSize = 4; + goto l1; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + savedSize = 7; + goto l1; + } + + /* RSP: */ + if (!offsetSqInt) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + savedSize = 4; + goto l1; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + savedSize = 5; + goto l1; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + savedSize = 8; + /* end concretizeMoveMwrR */ +l1: + ((self_in_CogX64Compiler->operands))[0] = save0; + ((self_in_CogX64Compiler->operands))[1] = save1; + ((self_in_CogX64Compiler->operands))[2] = 0; + return savedSize; + } + reg = ((self_in_CogX64Compiler->operands))[1]; + + /* If fetching RAX, fetch directly, otherwise, because of instruction encoding limitations, the register + _must_ be fetched through RAX. If reg = RBP or RSP simply fetch directly, otherwise swap RAX with + the register before and after the fetch through RAX. We avoid swapping before hand with RBP + and RSP because setting RSP to whatever the contents of RAX is can cause disastrous results if + an interrupt is delivered immediately after that point. See mail threads beginning with + http://lists.squeakfoundation.org/pipermail/vm-dev/2019-September/031428.html + http://lists.squeakfoundation.org/pipermail/vm-dev/2019-October/031499.html */ + if ((reg == RAX) + || ((reg == RBP) + || (reg == RSP))) { + offset1 = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = (144 + (reg % 8)); + offset1 = 2; + } + ((self_in_CogX64Compiler->machineCode))[0 + offset1] = 72; + ((self_in_CogX64Compiler->machineCode))[1 + offset1] = 161; + ((self_in_CogX64Compiler->machineCode))[2 + offset1] = (addressOperand & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3 + offset1] = ((((usqInt)(addressOperand)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4 + offset1] = ((((usqInt)(addressOperand)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5 + offset1] = ((((usqInt)(addressOperand)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6 + offset1] = ((((usqInt)(addressOperand)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7 + offset1] = ((((usqInt)(addressOperand)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8 + offset1] = ((((usqInt)(addressOperand)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[9 + offset1] = ((((usqInt)(addressOperand)) >> 56) & 0xFF); + if (!reg) { + return 10; + } + + /* Now effect the assignment via xchg, which saves a byte over a move */ + if ((reg == RBP) + || (reg == RSP)) { + ((self_in_CogX64Compiler->machineCode))[10] = (rexRxb(self_in_CogX64Compiler, RAX, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[11] = (144 + (reg % 8)); + return 12; + } + ((self_in_CogX64Compiler->machineCode))[12] = (((self_in_CogX64Compiler->machineCode))[0]); + ((self_in_CogX64Compiler->machineCode))[13] = (((self_in_CogX64Compiler->machineCode))[1]); + return 14; + + case MoveA32R: + /* begin concretizeMoveA32R */ + addressOperand = ((self_in_CogX64Compiler->operands))[0]; + if (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) addressOperand))) + || ((((AbstractInstruction *) addressOperand)) == (methodLabel))) { + addressOperand = ((((AbstractInstruction *) addressOperand))->address); + } + reg = ((self_in_CogX64Compiler->operands))[1]; + if (reg) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = (144 + (reg % 8)); + offsetSqInt = 2; + } + else { + offsetSqInt = 0; + } + ((self_in_CogX64Compiler->machineCode))[0 + offsetSqInt] = 161; + ((self_in_CogX64Compiler->machineCode))[1 + offsetSqInt] = (addressOperand & 0xFF); + ((self_in_CogX64Compiler->machineCode))[2 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 56) & 0xFF); + if (!reg) { + return 9; + } + ((self_in_CogX64Compiler->machineCode))[11] = (((self_in_CogX64Compiler->machineCode))[0]); + ((self_in_CogX64Compiler->machineCode))[12] = (((self_in_CogX64Compiler->machineCode))[1]); + return 13; + + case MoveRAw: + /* begin concretizeMoveRAw */ + reg = ((self_in_CogX64Compiler->operands))[0]; + addressOperand = ((self_in_CogX64Compiler->operands))[1]; + if (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) addressOperand))) + || ((((AbstractInstruction *) addressOperand)) == (methodLabel))) { + addressOperand = ((((AbstractInstruction *) addressOperand))->address); + } + if (/* isAddressRelativeToVarBase: */ + (addressOperand >= (varBaseAddress)) + && ((addressOperand - (varBaseAddress)) < (0x100000))) { + save1 = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->operands))[1] = (addressOperand - (varBaseAddress)); + ((self_in_CogX64Compiler->operands))[2] = RBX; + + /* begin concretizeMoveRMwr */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + offsetSqInt = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, srcReg, 0, destReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 137; + if ((destReg != RSP) + && (destReg != R12)) { + if ((offsetSqInt == 0) + && ((destReg != RBP) + && (destReg != R13))) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, destReg, srcReg)); + savedSize = 3; + goto l2; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + savedSize = 4; + goto l2; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + savedSize = 7; + goto l2; + } + + /* RSP: */ + if (!offsetSqInt) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + savedSize = 4; + goto l2; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + savedSize = 5; + goto l2; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + savedSize = 8; + /* end concretizeMoveRMwr */ +l2: + ((self_in_CogX64Compiler->operands))[1] = save1; + ((self_in_CogX64Compiler->operands))[2] = 0; + return savedSize; + } + + /* If storing RAX, store directly, otherwise, because of instruction encoding limitations, the register + _must_ be stored through RAX. If reg = RBP or RSP simply store directly, otherwise swap RAX with + the register before and after the store through RAX. We avoid swapping before hand with RBP + and RSP because setting RSP to whatever the contents of RAX is can cause disastrous results if + an interrupt is delivered immediately after that point. See mail threads beginning with + http://lists.squeakfoundation.org/pipermail/vm-dev/2019-September/031428.html + http://lists.squeakfoundation.org/pipermail/vm-dev/2019-October/031499.html */ + if ((reg == RAX) + || ((reg == RBP) + || (reg == RSP))) { + offset1 = 0; + } + else { + if ((reg == RBP) + || (reg == RSP)) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, reg, 0, RAX)); + ((self_in_CogX64Compiler->machineCode))[1] = 137; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, RAX, reg)); + offset1 = 3; + } + else { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, RAX, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = (144 + (reg % 8)); + offset1 = 2; + } + } + ((self_in_CogX64Compiler->machineCode))[0 + offset1] = 72; + ((self_in_CogX64Compiler->machineCode))[1 + offset1] = 163; + ((self_in_CogX64Compiler->machineCode))[2 + offset1] = (addressOperand & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3 + offset1] = ((((usqInt)(addressOperand)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4 + offset1] = ((((usqInt)(addressOperand)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5 + offset1] = ((((usqInt)(addressOperand)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6 + offset1] = ((((usqInt)(addressOperand)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7 + offset1] = ((((usqInt)(addressOperand)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8 + offset1] = ((((usqInt)(addressOperand)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[9 + offset1] = ((((usqInt)(addressOperand)) >> 56) & 0xFF); + if (!reg) { + return 10; + } + if ((reg == RBP) + || (reg == RSP)) { + return 13; + } + + /* Now effect the assignment via xchg, which restores RAX */ + ((self_in_CogX64Compiler->machineCode))[12] = (((self_in_CogX64Compiler->machineCode))[0]); + ((self_in_CogX64Compiler->machineCode))[13] = (((self_in_CogX64Compiler->machineCode))[1]); + return 14; + + case MoveRA32: + /* begin concretizeMoveRA32 */ + reg = ((self_in_CogX64Compiler->operands))[0]; + addressOperand = ((self_in_CogX64Compiler->operands))[1]; + if (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) addressOperand))) + || ((((AbstractInstruction *) addressOperand)) == (methodLabel))) { + addressOperand = ((((AbstractInstruction *) addressOperand))->address); + } + if (reg) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = (144 + (reg % 8)); + offsetSqInt = 2; + } + else { + offsetSqInt = 0; + } + ((self_in_CogX64Compiler->machineCode))[0 + offsetSqInt] = 163; + ((self_in_CogX64Compiler->machineCode))[1 + offsetSqInt] = (addressOperand & 0xFF); + ((self_in_CogX64Compiler->machineCode))[2 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8 + offsetSqInt] = ((((usqInt)(addressOperand)) >> 56) & 0xFF); + if (!reg) { + return 9; + } + ((self_in_CogX64Compiler->machineCode))[11] = (((self_in_CogX64Compiler->machineCode))[0]); + ((self_in_CogX64Compiler->machineCode))[12] = (((self_in_CogX64Compiler->machineCode))[1]); + return 13; + + case MoveAbR: + /* begin concretizeMoveAbR */ + addressOperand = ((self_in_CogX64Compiler->operands))[0]; + if (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) addressOperand))) + || ((((AbstractInstruction *) addressOperand)) == (methodLabel))) { + addressOperand = ((((AbstractInstruction *) addressOperand))->address); + } + if (/* isAddressRelativeToVarBase: */ + (addressOperand >= (varBaseAddress)) + && ((addressOperand - (varBaseAddress)) < (0x100000))) { + save0 = ((self_in_CogX64Compiler->operands))[0]; + save1 = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->operands))[0] = (addressOperand - (varBaseAddress)); + ((self_in_CogX64Compiler->operands))[1] = RBX; + ((self_in_CogX64Compiler->operands))[2] = save1; + + /* begin concretizeMoveMbrR */ + offsetSqInt = ((self_in_CogX64Compiler->operands))[0]; + srcReg = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 138; + if ((srcReg != RSP) + && (srcReg != R12)) { + if ((offsetSqInt == 0) + && ((srcReg != RBP) + && (srcReg != R13))) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + savedSize = 3; + goto l3; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + savedSize = 4; + goto l3; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + savedSize = 7; + goto l3; + } + + /* RSP: */ + if (!offsetSqInt) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + savedSize = 4; + goto l3; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + savedSize = 5; + goto l3; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + savedSize = 8; + /* end concretizeMoveMbrR */ +l3: + ((self_in_CogX64Compiler->operands))[0] = save0; + ((self_in_CogX64Compiler->operands))[1] = save1; + ((self_in_CogX64Compiler->operands))[2] = 0; + return savedSize; + } + reg = ((self_in_CogX64Compiler->operands))[1]; + if (reg) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = (144 + (reg % 8)); + offset1 = 2; + } + else { + offset1 = 0; + } + ((self_in_CogX64Compiler->machineCode))[0 + offset1] = 72; + ((self_in_CogX64Compiler->machineCode))[1 + offset1] = 160; + ((self_in_CogX64Compiler->machineCode))[2 + offset1] = (addressOperand & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3 + offset1] = ((((usqInt)(addressOperand)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4 + offset1] = ((((usqInt)(addressOperand)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5 + offset1] = ((((usqInt)(addressOperand)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6 + offset1] = ((((usqInt)(addressOperand)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7 + offset1] = ((((usqInt)(addressOperand)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8 + offset1] = ((((usqInt)(addressOperand)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[9 + offset1] = ((((usqInt)(addressOperand)) >> 56) & 0xFF); + if (!reg) { + return 10; + } + ((self_in_CogX64Compiler->machineCode))[12] = (((self_in_CogX64Compiler->machineCode))[0]); + ((self_in_CogX64Compiler->machineCode))[13] = (((self_in_CogX64Compiler->machineCode))[1]); + return 14; + + case MoveRAb: + /* begin concretizeMoveRAb */ + reg = ((self_in_CogX64Compiler->operands))[0]; + addressOperand = ((self_in_CogX64Compiler->operands))[1]; + if (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) addressOperand))) + || ((((AbstractInstruction *) addressOperand)) == (methodLabel))) { + addressOperand = ((((AbstractInstruction *) addressOperand))->address); + } + if (/* isAddressRelativeToVarBase: */ + (addressOperand >= (varBaseAddress)) + && ((addressOperand - (varBaseAddress)) < (0x100000))) { + save1 = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->operands))[1] = (addressOperand - (varBaseAddress)); + ((self_in_CogX64Compiler->operands))[2] = RBX; + + /* begin concretizeMoveRMbr */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + offsetSqInt = ((self_in_CogX64Compiler->operands))[1]; + baseReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, srcReg, 0, baseReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 136; + if ((baseReg != RSP) + && (baseReg != R12)) { + if ((offsetSqInt == 0) + && ((baseReg != RBP) + && (baseReg != R13))) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, baseReg, srcReg)); + savedSize = 3; + goto l4; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, baseReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + savedSize = 4; + goto l4; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, baseReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + savedSize = 7; + goto l4; + } + + /* RSP: */ + if (!offsetSqInt) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, baseReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, baseReg)); + savedSize = 4; + goto l4; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, baseReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, baseReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + savedSize = 5; + goto l4; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, baseReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, baseReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + savedSize = 8; + /* end concretizeMoveRMbr */ +l4: + ((self_in_CogX64Compiler->operands))[1] = save1; + ((self_in_CogX64Compiler->operands))[2] = 0; + return savedSize; + } + if (reg) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, reg)); + ((self_in_CogX64Compiler->machineCode))[1] = (144 + (reg % 8)); + offset1 = 2; + } + else { + offset1 = 0; + } + ((self_in_CogX64Compiler->machineCode))[0 + offset1] = 72; + ((self_in_CogX64Compiler->machineCode))[1 + offset1] = 162; + ((self_in_CogX64Compiler->machineCode))[2 + offset1] = (addressOperand & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3 + offset1] = ((((usqInt)(addressOperand)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4 + offset1] = ((((usqInt)(addressOperand)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5 + offset1] = ((((usqInt)(addressOperand)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6 + offset1] = ((((usqInt)(addressOperand)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7 + offset1] = ((((usqInt)(addressOperand)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8 + offset1] = ((((usqInt)(addressOperand)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[9 + offset1] = ((((usqInt)(addressOperand)) >> 56) & 0xFF); + if (!reg) { + return 10; + } + ((self_in_CogX64Compiler->machineCode))[12] = (((self_in_CogX64Compiler->machineCode))[0]); + ((self_in_CogX64Compiler->machineCode))[13] = (((self_in_CogX64Compiler->machineCode))[1]); + return 14; + + case MoveMbrR: + case MoveM8rR: + /* begin concretizeMoveMbrR */ + offsetSqInt = ((self_in_CogX64Compiler->operands))[0]; + srcReg = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 138; + if ((srcReg != RSP) + && (srcReg != R12)) { + if ((offsetSqInt == 0) + && ((srcReg != RBP) + && (srcReg != R13))) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + return 3; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + return 4; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 7; + } + + /* RSP: */ + if (!offsetSqInt) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + return 4; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + return 5; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 8; + + case MoveRMbr: + case MoveRM8r: + /* begin concretizeMoveRMbr */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + offsetSqInt = ((self_in_CogX64Compiler->operands))[1]; + baseReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, srcReg, 0, baseReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 136; + if ((baseReg != RSP) + && (baseReg != R12)) { + if ((offsetSqInt == 0) + && ((baseReg != RBP) + && (baseReg != R13))) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, baseReg, srcReg)); + return 3; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, baseReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + return 4; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, baseReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 7; + } + + /* RSP: */ + if (!offsetSqInt) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, baseReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, baseReg)); + return 4; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, baseReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, baseReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + return 5; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, baseReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, baseReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 8; + + case MoveM16rR: + /* begin concretizeMoveM16rR */ + offsetSqInt = ((self_in_CogX64Compiler->operands))[0]; + srcReg = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 15; + ((self_in_CogX64Compiler->machineCode))[2] = 183; + if ((srcReg != RSP) + && (srcReg != R12)) { + if ((offsetSqInt == 0) + && ((srcReg != RBP) + && (srcReg != R13))) { + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + return 4; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + return 5; + } + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 8; + } + + /* RSP & R12: */ + if ((offsetSqInt == 0) + && ((srcReg != RBP) + && (srcReg != R13))) { + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + return 5; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[5] = (offsetSqInt & 0xFF); + return 6; + } + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[5] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 9; + + case MoveRM16r: + /* begin concretizeMoveRM16r */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + offsetSqInt = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = 102; + if ((srcReg > 7) + || (destReg > 7)) { + ((self_in_CogX64Compiler->machineCode))[1] = (rexwrxb(self_in_CogX64Compiler, 0, srcReg, 0, destReg)); + skip = 1; + } + else { + skip = 0; + } + if ((destReg & 7) != RSP) { + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 137; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (offsetSqInt & 0xFF); + return skip + 4; + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 137; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return skip + 7; + } + + /* RSP: */ + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 137; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (offsetSqInt & 0xFF); + return skip + 5; + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 137; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return skip + 8; + + case MoveM32rR: + /* begin concretizeMoveM32rR */ + offsetSqInt = ((self_in_CogX64Compiler->operands))[0]; + srcReg = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + skip = 1; + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 0, destReg, 0, srcReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip] = 139; + if (!offsetSqInt) { + if ((srcReg & 6) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + return skip + 2; + } + if ((srcReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + return skip + 3; + } + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + if ((srcReg & 7) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (offsetSqInt & 0xFF); + return skip + 3; + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (offsetSqInt & 0xFF); + return skip + 4; + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + if ((srcReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + skip += 1; + } + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return skip + 6; + + case MoveM32rRs: + /* begin concretizeMoveM32rRs */ + offsetSqInt = ((self_in_CogX64Compiler->operands))[0]; + srcReg = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = 102; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, destReg, 0, srcReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 110; + if (!offsetSqInt) { + if ((srcReg & 6) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + return skip + 4; + } + if ((srcReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + return skip + 5; + } + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + if ((srcReg & 7) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (offsetSqInt & 0xFF); + return skip + 5; + } + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = (offsetSqInt & 0xFF); + return skip + 6; + } + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + if ((srcReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + skip += 1; + } + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return skip + 8; + + case MoveM64rRd: + /* begin concretizeMoveM64rRd */ + offsetSqInt = ((self_in_CogX64Compiler->operands))[0]; + srcReg = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, destReg, 0, srcReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 0x7E; + if (!offsetSqInt) { + if ((srcReg & 6) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + return skip + 4; + } + if ((srcReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + return skip + 5; + } + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + if ((srcReg & 7) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (offsetSqInt & 0xFF); + return skip + 5; + } + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = (offsetSqInt & 0xFF); + return skip + 6; + } + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + if ((srcReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + skip += 1; + } + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return skip + 8; + + case MoveMwrR: + /* begin concretizeMoveMwrR */ + offsetSqInt = ((self_in_CogX64Compiler->operands))[0]; + srcReg = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 139; + if ((srcReg != RSP) + && (srcReg != R12)) { + if ((offsetSqInt == 0) + && ((srcReg != RBP) + && (srcReg != R13))) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + return 3; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + return 4; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 7; + } + + /* RSP: */ + if (!offsetSqInt) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + return 4; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + return 5; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, srcReg, destReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, srcReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 8; + + case MoveXbrRR: + /* begin concretizeMoveXbrRR */ + index = ((self_in_CogX64Compiler->operands))[0]; + base = ((self_in_CogX64Compiler->operands))[1]; + dest = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, dest, index, base)); + ((self_in_CogX64Compiler->machineCode))[1] = 138; + if ((base != RBP) + && (base != R13)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, 4, dest)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, index, base)); + return 4; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, 4, dest)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, index, base)); + ((self_in_CogX64Compiler->machineCode))[4] = 0; + return 5; + + case MoveRXbrR: + /* begin concretizeMoveRXbrR */ + src = ((self_in_CogX64Compiler->operands))[0]; + index = ((self_in_CogX64Compiler->operands))[1]; + base = ((self_in_CogX64Compiler->operands))[2]; + offsetSqInt = 0; + if ((src > 3) + || ((base > 7) + || (index > 7))) { + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, src, index, base)); + offsetSqInt = 1; + } + ((self_in_CogX64Compiler->machineCode))[0 + offsetSqInt] = 136; + if ((base & 7) != RBP) { + ((self_in_CogX64Compiler->machineCode))[1 + offsetSqInt] = (modRMRO(self_in_CogX64Compiler, ModRegInd, 4, src)); + ((self_in_CogX64Compiler->machineCode))[2 + offsetSqInt] = (sib(self_in_CogX64Compiler, SIB1, index, base)); + return 3 + offsetSqInt; + } + + /* RBP,R13 */ + ((self_in_CogX64Compiler->machineCode))[1 + offsetSqInt] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, 4, src)); + ((self_in_CogX64Compiler->machineCode))[2 + offsetSqInt] = (sib(self_in_CogX64Compiler, SIB1, index, base)); + ((self_in_CogX64Compiler->machineCode))[3 + offsetSqInt] = 0; + return 4 + offsetSqInt; + + case MoveXwrRR: + /* begin concretizeMoveXwrRR */ + index = ((self_in_CogX64Compiler->operands))[0]; + base = ((self_in_CogX64Compiler->operands))[1]; + dest = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, dest, index, base)); + if ((base != RBP) + && (base != R13)) { + ((self_in_CogX64Compiler->machineCode))[1] = 139; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, 4, dest)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB8, index, base)); + return 4; + } + ((self_in_CogX64Compiler->machineCode))[1] = 139; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, 4, dest)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB8, index, base)); + ((self_in_CogX64Compiler->machineCode))[4] = 0; + return 5; + + case MoveRXwrR: + /* begin concretizeMoveRXwrR */ + src = ((self_in_CogX64Compiler->operands))[0]; + index = ((self_in_CogX64Compiler->operands))[1]; + base = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, src, index, base)); + if ((base != RBP) + && (base != R13)) { + ((self_in_CogX64Compiler->machineCode))[1] = 137; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, 4, src)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB8, index, base)); + return 4; + } + ((self_in_CogX64Compiler->machineCode))[1] = 137; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, 4, src)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB8, index, base)); + ((self_in_CogX64Compiler->machineCode))[4] = 0; + return 5; + + case MoveX32rRR: + return concretizeMoveX32rRR(self_in_CogX64Compiler); + + case MoveRX32rR: + return concretizeMoveRX32rR(self_in_CogX64Compiler); + + case MoveRMwr: + /* begin concretizeMoveRMwr */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + offsetSqInt = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, srcReg, 0, destReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 137; + if ((destReg != RSP) + && (destReg != R12)) { + if ((offsetSqInt == 0) + && ((destReg != RBP) + && (destReg != R13))) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, destReg, srcReg)); + return 3; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + return 4; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 7; + } + + /* RSP: */ + if (!offsetSqInt) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + return 4; + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + return 5; + } + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + ((self_in_CogX64Compiler->machineCode))[4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return 8; + + case MoveRM32r: + /* begin concretizeMoveRM32r */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + offsetSqInt = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + skip = 1; + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 0, srcReg, 0, destReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip] = 137; + if (!offsetSqInt) { + if ((destReg & 6) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModRegInd, destReg, srcReg)); + return skip + 2; + } + if ((destReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModRegInd, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + return skip + 3; + } + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + if ((destReg & 7) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (offsetSqInt & 0xFF); + return skip + 3; + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (offsetSqInt & 0xFF); + return skip + 4; + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, destReg, srcReg)); + if ((destReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + skip += 1; + } + ((self_in_CogX64Compiler->machineCode))[skip + 2] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 3] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return skip + 6; + + case MoveRsM32r: + /* begin concretizeMoveRsM32r */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + offsetSqInt = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = 102; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, srcReg, 0, destReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 0x7E; + if (!offsetSqInt) { + if ((destReg & 6) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegInd, destReg, srcReg)); + return skip + 4; + } + if ((destReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegInd, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + return skip + 5; + } + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + if ((destReg & 7) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (offsetSqInt & 0xFF); + return skip + 5; + } + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = (offsetSqInt & 0xFF); + return skip + 6; + } + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, destReg, srcReg)); + if ((destReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + skip += 1; + } + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return skip + 8; + + case MoveRdM64r: + /* begin concretizeMoveRdM64r */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + offsetSqInt = ((self_in_CogX64Compiler->operands))[1]; + destReg = ((self_in_CogX64Compiler->operands))[2]; + ((self_in_CogX64Compiler->machineCode))[0] = 102; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, srcReg, 0, destReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 214; + if (!offsetSqInt) { + if ((destReg & 6) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegInd, destReg, srcReg)); + return skip + 4; + } + if ((destReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegInd, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + return skip + 5; + } + } + if (isQuick(self_in_CogX64Compiler, offsetSqInt)) { + if ((destReg & 7) != RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (offsetSqInt & 0xFF); + return skip + 5; + } + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp8, destReg, srcReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = (offsetSqInt & 0xFF); + return skip + 6; + } + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModRegRegDisp32, destReg, srcReg)); + if ((destReg & 7) == RSP) { + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (sib(self_in_CogX64Compiler, SIB1, 4, destReg)); + skip += 1; + } + ((self_in_CogX64Compiler->machineCode))[skip + 4] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 5] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 6] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[skip + 7] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + return skip + 8; + + case MoveRdR: + /* begin concretizeMoveRdR */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 102; + ((self_in_CogX64Compiler->machineCode))[1] = (rexRxb(self_in_CogX64Compiler, srcReg, 0, destReg)); + ((self_in_CogX64Compiler->machineCode))[2] = 15; + ((self_in_CogX64Compiler->machineCode))[3] = 0x7E; + ((self_in_CogX64Compiler->machineCode))[4] = (modRMRO(self_in_CogX64Compiler, ModReg, destReg, srcReg)); + return 5; + + case MoveRRd: + /* begin concretizeMoveRRd */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 102; + ((self_in_CogX64Compiler->machineCode))[1] = (rexRxb(self_in_CogX64Compiler, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[2] = 15; + ((self_in_CogX64Compiler->machineCode))[3] = 110; + ((self_in_CogX64Compiler->machineCode))[4] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return 5; + + case MoveRdRd: + /* begin concretizeMoveRdRd */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 242; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, srcReg, 0, destReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 17; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, destReg, srcReg)); + return skip + 4; + + case MoveRsRs: + /* begin concretizeMoveRsRs */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, srcReg, 0, destReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 17; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, destReg, srcReg)); + return skip + 4; + + case PopR: + /* begin concretizePopR */ + reg = ((self_in_CogX64Compiler->operands))[0]; + if (reg < 8) { + ((self_in_CogX64Compiler->machineCode))[0] = (88 + reg); + return 1; + } + ((self_in_CogX64Compiler->machineCode))[0] = 65; + ((self_in_CogX64Compiler->machineCode))[1] = (88 + (reg - 8)); + return 2; + + case PushR: + /* begin concretizePushR */ + reg = ((self_in_CogX64Compiler->operands))[0]; + if (reg < 8) { + ((self_in_CogX64Compiler->machineCode))[0] = (80 + reg); + return 1; + } + ((self_in_CogX64Compiler->machineCode))[0] = 65; + ((self_in_CogX64Compiler->machineCode))[1] = (80 + (reg - 8)); + return 2; + + case PushCq: + /* begin concretizePushCq */ + value = ((self_in_CogX64Compiler->operands))[0]; + if (isQuick(self_in_CogX64Compiler, value)) { + ((self_in_CogX64Compiler->machineCode))[0] = 106; + ((self_in_CogX64Compiler->machineCode))[1] = (value & 0xFF); + return 2; + } + if (is32BitSignedImmediate(self_in_CogX64Compiler, value)) { + ((self_in_CogX64Compiler->machineCode))[0] = 104; + ((self_in_CogX64Compiler->machineCode))[1] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[2] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 24) & 0xFF); + return 5; + } + + /* begin concretizePushCw */ + valueSqInt = ((self_in_CogX64Compiler->operands))[0]; + if (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) valueSqInt))) + || ((((AbstractInstruction *) valueSqInt)) == (methodLabel))) { + valueSqInt = ((((AbstractInstruction *) valueSqInt))->address); + } + if (/* addressIsInCurrentCompilation: */ + ((((usqInt)valueSqInt)) >= ((methodLabel->address))) + && ((((usqInt)valueSqInt)) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize))))) { + offsetSqInt = valueSqInt - (((self_in_CogX64Compiler->address)) + 7); + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, RISCTempReg, 0, 0)); + ((self_in_CogX64Compiler->machineCode))[1] = 141; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, 5, RISCTempReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = 65; + ((self_in_CogX64Compiler->machineCode))[8] = (72 + RISCTempReg); + + /* LoadEffectiveAddress */ + return 9; + } + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, RISCTempReg, 0, RISCTempReg)); + ((self_in_CogX64Compiler->machineCode))[1] = (184 + (RISCTempReg & 7)); + ((self_in_CogX64Compiler->machineCode))[2] = (valueSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(valueSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(valueSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(valueSqInt)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(valueSqInt)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(valueSqInt)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8] = ((((usqInt)(valueSqInt)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[9] = ((((usqInt)(valueSqInt)) >> 56) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[10] = 65; + ((self_in_CogX64Compiler->machineCode))[11] = (72 + RISCTempReg); + assert((((self_in_CogX64Compiler->machineCode))[11]) < 144); + return 12; + + case PushCw: + /* begin concretizePushCw */ + value = ((self_in_CogX64Compiler->operands))[0]; + if (/* isAnInstruction: */ + (addressIsInInstructions(((AbstractInstruction *) value))) + || ((((AbstractInstruction *) value)) == (methodLabel))) { + value = ((((AbstractInstruction *) value))->address); + } + if (/* addressIsInCurrentCompilation: */ + ((((usqInt)value)) >= ((methodLabel->address))) + && ((((usqInt)value)) < ((((youngReferrers) < (((methodLabel->address)) + MaxMethodSize)) ? (youngReferrers) : (((methodLabel->address)) + MaxMethodSize))))) { + offsetSqInt = value - (((self_in_CogX64Compiler->address)) + 7); + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, RISCTempReg, 0, 0)); + ((self_in_CogX64Compiler->machineCode))[1] = 141; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModRegInd, 5, RISCTempReg)); + ((self_in_CogX64Compiler->machineCode))[3] = (offsetSqInt & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(offsetSqInt)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(offsetSqInt)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(offsetSqInt)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = 65; + ((self_in_CogX64Compiler->machineCode))[8] = (72 + RISCTempReg); + + /* LoadEffectiveAddress */ + return 9; + } + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, RISCTempReg, 0, RISCTempReg)); + ((self_in_CogX64Compiler->machineCode))[1] = (184 + (RISCTempReg & 7)); + ((self_in_CogX64Compiler->machineCode))[2] = (value & 0xFF); + ((self_in_CogX64Compiler->machineCode))[3] = ((((usqInt)(value)) >> 8) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[4] = ((((usqInt)(value)) >> 16) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[5] = ((((usqInt)(value)) >> 24) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[6] = ((((usqInt)(value)) >> 32) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[7] = ((((usqInt)(value)) >> 40) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[8] = ((((usqInt)(value)) >> 48) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[9] = ((((usqInt)(value)) >> 56) & 0xFF); + ((self_in_CogX64Compiler->machineCode))[10] = 65; + ((self_in_CogX64Compiler->machineCode))[11] = (72 + RISCTempReg); + assert((((self_in_CogX64Compiler->machineCode))[11]) < 144); + return 12; + + case PrefetchAw: + return concretizePrefetchAw(self_in_CogX64Compiler); + + case ConvertRRd: + /* begin concretizeConvertRRd */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 242; + ((self_in_CogX64Compiler->machineCode))[1] = (rexRxb(self_in_CogX64Compiler, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[2] = 15; + ((self_in_CogX64Compiler->machineCode))[3] = 42; + ((self_in_CogX64Compiler->machineCode))[4] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return 5; + + case ConvertRdR: + /* begin concretizeConvertRdR */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 242; + ((self_in_CogX64Compiler->machineCode))[1] = (rexRxb(self_in_CogX64Compiler, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[2] = 15; + ((self_in_CogX64Compiler->machineCode))[3] = 45; + ((self_in_CogX64Compiler->machineCode))[4] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return 5; + + case ConvertRRs: + /* begin concretizeConvertRRs */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, destReg, 0, srcReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 42; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return skip + 4; + + case ConvertRsR: + /* begin concretizeConvertRsR */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, destReg, 0, srcReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 45; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return skip + 4; + + case ConvertRsRd: + /* begin concretizeConvertRsRd */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 243; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, destReg, 0, srcReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 90; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return skip + 4; + + case ConvertRdRs: + /* begin concretizeConvertRdRs */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = 242; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + ((self_in_CogX64Compiler->machineCode))[(skip = 1)] = (rexwrxb(self_in_CogX64Compiler, 0, destReg, 0, srcReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip + 1] = 15; + ((self_in_CogX64Compiler->machineCode))[skip + 2] = 90; + ((self_in_CogX64Compiler->machineCode))[skip + 3] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return skip + 4; + + case SignExtend8RR: + /* begin concretizeSignExtend8RR */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 1, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 15; + ((self_in_CogX64Compiler->machineCode))[2] = 190; + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return 4; + + case SignExtend16RR: + /* begin concretizeSignExtend16RR */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 1, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 15; + ((self_in_CogX64Compiler->machineCode))[2] = 191; + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return 4; + + case SignExtend32RR: + /* begin concretizeSignExtend32RR */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 1, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 99; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return 3; + + case ZeroExtend8RR: + /* begin concretizeZeroExtend8RR */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 1, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 15; + ((self_in_CogX64Compiler->machineCode))[2] = 182; + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return 4; + + case ZeroExtend16RR: + /* begin concretizeZeroExtend16RR */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 1, destReg, 0, srcReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 15; + ((self_in_CogX64Compiler->machineCode))[2] = 183; + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return 4; + + case ZeroExtend32RR: + /* begin concretizeZeroExtend32RR */ + srcReg = ((self_in_CogX64Compiler->operands))[0]; + destReg = ((self_in_CogX64Compiler->operands))[1]; + if ((srcReg <= 7) + && (destReg <= 7)) { + skip = 0; + } + else { + skip = 1; + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 0, destReg, 0, srcReg)); + } + ((self_in_CogX64Compiler->machineCode))[skip] = 139; + ((self_in_CogX64Compiler->machineCode))[skip + 1] = (modRMRO(self_in_CogX64Compiler, ModReg, srcReg, destReg)); + return skip + 2; + + case MovePerfCnt64RL: + return concretizeMovePerfCnt64RL(self_in_CogX64Compiler); + + default: + error("Case not found and no otherwise clause"); + } + + /* Noops & Pseudo Ops + Control + Table B-1 Intel 64 and IA-32 Architectures Software Developer's Manual Volume 1: Basic Architecture + Arithmetic + Data Movement + Conversion + miscellaneous */ + return 0; +} + + +/* Attempt to generate concrete machine code for the instruction at address. + This is part of the inner dispatch of concretizeAt: actualAddress which + exists only + to get around the number of literals limits in the SqueakV3 (blue book + derived) bytecode set. */ + + /* CogX64Compiler>>#dispatchConcretizeProcessorSpecific */ +static NoDbgRegParms sqInt +dispatchConcretizeProcessorSpecific(AbstractInstruction *self_in_CogX64Compiler) +{ + sqInt dest; + sqInt maskReg; + sqInt reg1; + sqInt reg2; + sqInt regDivisor; + + switch ((self_in_CogX64Compiler->opcode)) { + case CPUID: + /* begin concretizeCPUID */ + ((self_in_CogX64Compiler->machineCode))[0] = 15; + ((self_in_CogX64Compiler->machineCode))[1] = 162; + return 2; + + case CDQ: + /* begin concretizeCDQ */ + ((self_in_CogX64Compiler->machineCode))[0] = 72; + ((self_in_CogX64Compiler->machineCode))[1] = 153; + return 2; + + case IDIVR: + /* begin concretizeIDIVR */ + regDivisor = ((self_in_CogX64Compiler->operands))[0]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, 0, 0, regDivisor)); + ((self_in_CogX64Compiler->machineCode))[1] = 247; + ((self_in_CogX64Compiler->machineCode))[2] = (modRMRO(self_in_CogX64Compiler, ModReg, regDivisor, 7)); + return 3; + + case IMULRR: + /* begin concretizeMulRR */ + reg1 = ((self_in_CogX64Compiler->operands))[0]; + reg2 = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexRxb(self_in_CogX64Compiler, reg2, 0, reg1)); + ((self_in_CogX64Compiler->machineCode))[1] = 15; + ((self_in_CogX64Compiler->machineCode))[2] = 175; + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModReg, reg1, reg2)); + return 4; + + case XCHGRR: + return concretizeXCHGRR(self_in_CogX64Compiler); + + case REP: + /* begin concretizeREP */ + ((self_in_CogX64Compiler->machineCode))[0] = 243; + return 1; + + case CLD: + /* begin concretizeCLD */ + ((self_in_CogX64Compiler->machineCode))[0] = 0xFC; + return 1; + + case MOVSB: + /* begin concretizeMOVSB */ + ((self_in_CogX64Compiler->machineCode))[0] = 164; + return 1; + + case MOVSQ: + /* begin concretizeMOVSQ */ + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 1, 0, 0, 0)); + ((self_in_CogX64Compiler->machineCode))[1] = 165; + return 2; + + case BSR: + /* begin concretizeBSR */ + maskReg = ((self_in_CogX64Compiler->operands))[0]; + dest = ((self_in_CogX64Compiler->operands))[1]; + ((self_in_CogX64Compiler->machineCode))[0] = (rexwrxb(self_in_CogX64Compiler, 1, dest, 0, maskReg)); + ((self_in_CogX64Compiler->machineCode))[1] = 15; + ((self_in_CogX64Compiler->machineCode))[2] = 189; + ((self_in_CogX64Compiler->machineCode))[3] = (modRMRO(self_in_CogX64Compiler, ModReg, maskReg, dest)); + return 4; + + default: + error("Case not found and no otherwise clause"); + } + + /* Specific Arithmetic + Specific Data Movement */ + return 0; +} + + /* CogX64Compiler>>#fullCallsAreRelative */ +static NoDbgRegParms sqInt +fullCallsAreRelative(AbstractInstruction *self_in_CogX64Compiler) +{ + return 0; +} + + /* CogX64Compiler>>#genDivR:R:Quo:Rem: */ +static NoDbgRegParms AbstractInstruction * +genDivRRQuoRem(AbstractInstruction *self_in_CogX64Compiler, sqInt abstractRegDivisor, sqInt abstractRegDividend, sqInt abstractRegQuotient, sqInt abstractRegRemainder) +{ + sqInt rDividend; + sqInt rDivisor; + sqInt reg; + sqInt rQuotient; + sqInt rRemainder; + sqInt rUnused; + sqInt saveRestoreEAX; + sqInt saveRestoreEDX; + sqInt saveRestoreExchanged; + + assert(abstractRegDividend != abstractRegDivisor); + assert(abstractRegQuotient != abstractRegRemainder); + rDividend = abstractRegDividend; + rDivisor = abstractRegDivisor; + rQuotient = abstractRegQuotient; + rRemainder = abstractRegRemainder; + + /* IDIV r does a signed divide of RDX:RAX by r, RAX := Quotient, RDX := Remainder. + Since we must sign extend the dividend into RDX we must substitute another register if RDX is an input. */ + if ((rDividend == RDX) + || (rDivisor == RDX)) { + /* Slang, sigh... */ + rUnused = RAX; + while (rUnused <= RDI) { + if ((rUnused != RSP) + && ((rUnused != RBP) + && ((rUnused != RDX) + && ((rUnused != rDividend) + && ((rUnused != rDivisor) + && ((rUnused != rQuotient) + && (rUnused != rRemainder))))))) { + /* PushR: */ + genoperand(PushR, rUnused); + + /* MoveR:R: */ + genoperandoperand(MoveRR, RDX, rUnused); + if (rDividend == RDX) { + genDivRRQuoRem(self_in_CogX64Compiler, rDivisor, rUnused, rQuotient, rRemainder); + } + else { + genDivRRQuoRem(self_in_CogX64Compiler, rUnused, rDividend, rQuotient, rRemainder); + } + + /* PopR: */ + genoperand(PopR, rUnused); + return self_in_CogX64Compiler; + } + rUnused += 1; + } + error("couldn't find unused register in genDivR:R:Quo:Rem:"); + } + + /* If either output does not include RAX or RDX we must save and restore RAX and/or RDX. */ + if ((saveRestoreEAX = (rQuotient != RAX) + && (rRemainder != RAX))) { + /* PushR: */ + genoperand(PushR, RAX); + } + if ((saveRestoreEDX = (rQuotient != RDX) + && (rRemainder != RDX))) { + /* PushR: */ + genoperand(PushR, RDX); + } + saveRestoreExchanged = -1; + if (rDividend) { + if (rDivisor) { + /* MoveR:R: */ + genoperandoperand(MoveRR, rDividend, RAX); + } + else { + if (((rDividend != rQuotient) + && (rDividend != rRemainder)) + && ((rDividend != RDX) + || (!saveRestoreEDX))) { + reg = (saveRestoreExchanged = rDividend); + + /* begin PushR: */ + genoperand(PushR, reg); + } + genoperandoperand(XCHGRR, rDivisor, rDividend); + } + } + + /* CDQ sign-extends RAX into RDX as required for IDIV */ + gen(CDQ); + genoperand(IDIVR, (rDivisor + ? rDivisor + : rDividend)); + + /* Must not overwrite result while juggling */ + if ((rQuotient == RDX) + && (rRemainder == RAX)) { + genoperandoperand(XCHGRR, rQuotient, rRemainder); + } + else { + if (rQuotient == RDX) { + if (rRemainder != RDX) { + /* MoveR:R: */ + genoperandoperand(MoveRR, RDX, rRemainder); + } + if (rQuotient) { + /* MoveR:R: */ + genoperandoperand(MoveRR, RAX, rQuotient); + } + } + else { + if (rQuotient) { + /* MoveR:R: */ + genoperandoperand(MoveRR, RAX, rQuotient); + } + if (rRemainder != RDX) { + /* MoveR:R: */ + genoperandoperand(MoveRR, RDX, rRemainder); + } + } + } + if (saveRestoreExchanged >= 0) { + /* PopR: */ + genoperand(PopR, saveRestoreExchanged); + } + if (saveRestoreEDX) { + /* PopR: */ + genoperand(PopR, RDX); + } + if (saveRestoreEAX) { + /* PopR: */ + genoperand(PopR, RAX); + } + return self_in_CogX64Compiler; +} + + +/* Get the abstract registers for ECX, EDI and ESI */ + + /* CogX64Compiler>>#genMemCopy:to:constantSize: */ +static NoDbgRegParms AbstractInstruction * +genMemCopytoconstantSize(AbstractInstruction *self_in_CogX64Compiler, sqInt originalSourceReg, sqInt originalDestReg, sqInt size) +{ + sqInt countReg; + sqInt destReg; + AbstractInstruction *inst; + sqInt numbytes; + sqInt numwords; + sqInt sourceReg; + + sourceReg = RSI; + destReg = RDI; + countReg = RCX; + + /* Put the source in ESI and the dest in EDI */ + inst = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + if (originalSourceReg != sourceReg) { + if (originalDestReg == sourceReg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, originalDestReg, TempReg); + } + + /* MoveR:R: */ + genoperandoperand(MoveRR, originalSourceReg, sourceReg); + } + if (originalDestReg != destReg) { + if (originalDestReg == sourceReg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, destReg); + } + else { + /* MoveR:R: */ + genoperandoperand(MoveRR, originalDestReg, destReg); + } + } + + /* Store the count and clear the direction */ + gen(CLD); + + /* First copy the bytes */ + numbytes = size & 7; + if (numbytes > 0) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, numbytes, countReg); + gen(REP); + gen(MOVSB); + } + + /* Now copy the qwords */ + numwords = size / 8; + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, numwords, countReg); + gen(REP); + gen(MOVSQ); + return self_in_CogX64Compiler; +} + + +/* Get the abstract registers for ECX, EDI and ESI */ + + /* CogX64Compiler>>#genMemCopy:to:size: */ +static NoDbgRegParms AbstractInstruction * +genMemCopytosize(AbstractInstruction *self_in_CogX64Compiler, sqInt originalSourceReg, sqInt originalDestReg, sqInt originalSize) +{ + sqInt countReg; + sqInt destReg; + AbstractInstruction *inst; + sqInt size; + sqInt sourceReg; + sqInt spilledSize; + + sourceReg = RSI; + destReg = RDI; + countReg = RCX; + + /* TODO: Avoid spilling */ + spilledSize = 0; + if ((originalSize == sourceReg) + || (originalSize == destReg)) { + /* PushR: */ + genoperand(PushR, originalSize); + spilledSize = 1; + } + + /* Put the source in ESI and the dest in EDI */ + inst = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + if (originalSourceReg != sourceReg) { + if (originalDestReg == sourceReg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, originalDestReg, TempReg); + } + + /* MoveR:R: */ + genoperandoperand(MoveRR, originalSourceReg, sourceReg); + } + if (originalDestReg != destReg) { + if (originalDestReg == sourceReg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, destReg); + } + else { + /* MoveR:R: */ + genoperandoperand(MoveRR, originalDestReg, destReg); + } + } + + /* Put the original size register in somewhere different than ECX */ + if (spilledSize) { + /* PopR: */ + genoperand(PopR, TempReg); + size = TempReg; + } + else { + if (originalSize == countReg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, originalSize, TempReg); + size = TempReg; + } + else { + size = originalSize; + } + } + gen(CLD); + + /* First copy the bytes */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, size, countReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, 7, countReg); + gen(REP); + gen(MOVSB); + + /* Now copy the words */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, size, countReg); + + /* LogicalShiftRightCq:R: */ + genoperandoperand(LogicalShiftRightCqR, 3, countReg); + gen(REP); + gen(MOVSQ); + return self_in_CogX64Compiler; +} + + /* CogX64Compiler>>#genMulR:R: */ +static NoDbgRegParms AbstractInstruction * +genMulRR(AbstractInstruction *self_in_CogX64Compiler, sqInt regSource, sqInt regDest) +{ + return genoperandoperand(IMULRR, regSource, regDest); +} + + +/* Ensure that the register args are pushed before the outer and + inner retpcs at an entry miss for arity <= self numRegArgs. The + outer retpc is that of a call at a send site. The inner is the call + from a method or PIC abort/miss to the trampoline. */ +/* This won't be as clumsy on a RISC. But putting the receiver and + args above the return address means the CoInterpreter has a + single machine-code frame format which saves us a lot of work. */ +/* Iff there are register args convert + base -> outerRetpc (send site retpc) + sp -> innerRetpc (PIC abort/miss retpc) + to + base -> receiver + (arg0) + (arg1) + outerRetpc + sp -> innerRetpc (PIC abort/miss retpc) */ + + /* CogX64Compiler>>#genPushRegisterArgsForAbortMissNumArgs: */ +static NoDbgRegParms AbstractInstruction * +genPushRegisterArgsForAbortMissNumArgs(AbstractInstruction *self_in_CogX64Compiler, sqInt numArgs) +{ + if (numArgs <= (numRegArgs())) { + assert((numRegArgs()) <= 2); + if (!numArgs) { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, SPReg, TempReg); + + /* PushR: */ + genoperand(PushR, TempReg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord * 2, SPReg, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, BytesPerWord, SPReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ReceiverResultReg, 2 * BytesPerWord, SPReg); + return self_in_CogX64Compiler; + } + if (numArgs == 1) { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord, SPReg, TempReg); + + /* PushR: */ + genoperand(PushR, TempReg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord, SPReg, TempReg); + + /* PushR: */ + genoperand(PushR, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ReceiverResultReg, 3 * BytesPerWord, SPReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, Arg0Reg, 2 * BytesPerWord, SPReg); + return self_in_CogX64Compiler; + } + if (numArgs == 2) { + /* PushR: */ + genoperand(PushR, Arg1Reg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord * 2, SPReg, TempReg); + + /* PushR: */ + genoperand(PushR, TempReg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord * 2, SPReg, TempReg); + + /* PushR: */ + genoperand(PushR, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ReceiverResultReg, 4 * BytesPerWord, SPReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, Arg0Reg, 3 * BytesPerWord, SPReg); + return self_in_CogX64Compiler; + } + } + return self_in_CogX64Compiler; +} + + +/* Ensure that the register args are pushed before the retpc for arity <= + self numRegArgs. This + isn't as clumsy on a RISC. But putting the receiver and args above the + return address + means the CoInterpreter has a single machine-code frame format which saves + us a lot of work. + N.B. Take great care to /not/ smash TempReg, which is used in directed + send marshalling. + We could use XCHG to swap the ReceiverResultReg and top-of-stack return + address, pushing the + the ret pc (now in ReceiverResultReg) later, but XCHG is very slow. We can + use SendNumArgsReg + because it is only live in sends of arity >= (NumSendTrampolines - 1). */ + + /* CogX64Compiler>>#genPushRegisterArgsForNumArgs:scratchReg: */ +static NoDbgRegParms AbstractInstruction * +genPushRegisterArgsForNumArgsscratchReg(AbstractInstruction *self_in_CogX64Compiler, sqInt numArgs, sqInt scratchReg) +{ + assert((numRegArgs()) < (NumSendTrampolines - 1)); + if (numArgs <= (numRegArgs())) { + assert((numRegArgs()) <= 2); + + /* these two variants show the same performance on Intel Core i7, but the second one may be shorter. */ + + /* a.k.a. + cogit gen: XCHGMwrR operand: 0 operand: SPReg operand: ReceiverResultReg. + but XCHG is slow. */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, SPReg, scratchReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ReceiverResultReg, 0, SPReg); + if (numArgs > 0) { + /* PushR: */ + genoperand(PushR, Arg0Reg); + if (numArgs > 1) { + /* PushR: */ + genoperand(PushR, Arg1Reg); + } + } + + /* PushR: */ + genoperand(PushR, scratchReg); + } + return self_in_CogX64Compiler; +} + + +/* This is a no-op on x64 SysV since the ABI passes up to 6 args in registers + and trampolines currently observe a limit of 4. + But the WIN64 ABI always reserves shadow space for saving up to 4 + parameter registers (even if less than 4 args). + */ + + /* CogX64Compiler>>#genRemoveNArgsFromStack: */ +static NoDbgRegParms sqInt +genRemoveNArgsFromStack(AbstractInstruction *self_in_CogX64Compiler, sqInt n) +{ + assert(n <= 4); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 32, RSP); + return 0; +} + + +/* Restore the registers in regMask as saved by genSaveRegs:. */ + + /* CogX64Compiler>>#genRestoreRegs: */ +static NoDbgRegParms sqInt +genRestoreRegs(AbstractInstruction *self_in_CogX64Compiler, sqInt regMask) +{ + sqInt reg; + + assert(!((((regMask & (registerMaskForand(RSP, RBP))) != 0)))); + for (reg = RAX; reg <= R15; reg += 1) { + if (((regMask & (((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)))) != 0)) { + /* PopR: */ + genoperand(PopR, reg); + } + } + return 0; +} + + +/* Save the registers in regMask for a call into the C run-time from a + trampoline. + */ + + /* CogX64Compiler>>#genSaveRegs: */ +static NoDbgRegParms sqInt +genSaveRegs(AbstractInstruction *self_in_CogX64Compiler, sqInt regMask) +{ + sqInt reg; + + assert(!((((regMask & (registerMaskForand(RSP, RBP))) != 0)))); + for (reg = R15; reg >= RAX; reg += -1) { + if (((regMask & (((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)))) != 0)) { + /* PushR: */ + genoperand(PushR, reg); + } + } + return 0; +} + + /* CogX64Compiler>>#genSubstituteReturnAddress: */ +static NoDbgRegParms AbstractInstruction * +genSubstituteReturnAddress(AbstractInstruction *self_in_CogX64Compiler, sqInt retpc) +{ + return genoperand(PushCw, retpc); +} + + /* CogX64Compiler>>#genSwapR:R:Scratch: */ +static NoDbgRegParms AbstractInstruction * +genSwapRRScratch(AbstractInstruction *self_in_CogX64Compiler, sqInt regA, sqInt regB, sqInt regTmp) +{ + return genoperandoperand(XCHGRR, regA, regB); +} + + +/* Answer if we support LZCNT */ + + /* CogX64Compiler>>#hasLZCNTInstructions */ +static NoDbgRegParms int +hasLZCNTInstructions(AbstractInstruction *self_in_CogX64Compiler) +{ + return (((cpuidWord1(self_in_CogX64Compiler)) & (32)) != 0); +} + + +/* Answer if the processor has a dedicated callee-saved register to point to + the base of commonly-accessed variables. */ + + /* CogX64Compiler>>#hasVarBaseRegister */ +static NoDbgRegParms sqInt +hasVarBaseRegister(AbstractInstruction *self_in_CogX64Compiler) +{ + return 1; +} + + +/* Answer the instruction size at pc. This is used in method disassembly + to decode the jumps in block dispatch to discover where block methods + occur within a larger method. This is very far from a full decode. */ + + /* CogX64Compiler>>#instructionSizeAt: */ +static NoDbgRegParms sqInt +instructionSizeAt(AbstractInstruction *self_in_CogX64Compiler, sqInt pc) +{ + sqInt op; + + op = byteAt((void *)(pc)); + if ((op & 0xF8) == 72) { + return 1 + (instructionSizeAt(self_in_CogX64Compiler, pc + 1)); + } + switch (op) { + case 15: + return twoByteInstructionSizeAt(self_in_CogX64Compiler, pc); + + case 61: + case 233: + return 5; + + case 112: + case 113: + case 114: + case 115: + case 116: + case 117: + case 118: + case 119: + case 120: + case 121: + case 122: + case 123: + case 0x7C: + case 125: + case 0x7E: + case 0x7F: + case 137: + case 235: + return 2; + + case 131: + return sizeImmediateGroup1at(self_in_CogX64Compiler, op, pc); + + case 139: + return sizeHasModrmat(self_in_CogX64Compiler, op, pc); + + case 144: + case 204: + return 1; + + default: + error("Case not found and no otherwise clause"); + } + return 0; +} + + +/* Top 32 bits all the same as the bottom 32 bits' sign bit implies we can + use a sign-extended 4 byte offset. + */ + + /* CogX64Compiler>>#is32BitSignedImmediate: */ +static NoDbgRegParms int +is32BitSignedImmediate(AbstractInstruction *self_in_CogX64Compiler, sqInt a64BitUnsignedOperand) +{ + return (((int) a64BitUnsignedOperand)) == (((sqLong) a64BitUnsignedOperand)); +} + + +/* Assuming mcpc is a send return pc answer if the instruction before it is a + call (not a CallFull). + */ + + /* CogX64Compiler>>#isCallPrecedingReturnPC: */ +static NoDbgRegParms int +isCallPrecedingReturnPC(AbstractInstruction *self_in_CogX64Compiler, sqInt mcpc) +{ + return (byteAt((void *)(mcpc - 5))) == 232; +} + + /* CogX64Compiler>>#isJumpAt: */ +static NoDbgRegParms sqInt +isJumpAt(AbstractInstruction *self_in_CogX64Compiler, sqInt pc) +{ + sqInt op; + + op = byteAt((void *)(pc)); + return (((op >= 112) && (op <= 0x7F))) + || ((op == 233) + || ((op == 235) + || (((op == 15) + && ((((byteAt((void *)(pc + 1))) >= 128) && ((byteAt((void *)(pc + 1))) <= 143)))) + || ((op == 72) + && (((byteAt((void *)(pc + 1))) == 161) + && (((byteAt((void *)(pc + 10))) == 0xFF) + && ((byteAt((void *)(pc + 11))) == 224))))))); +} + + /* CogX64Compiler>>#isQuick: */ +static NoDbgRegParms int +isQuick(AbstractInstruction *self_in_CogX64Compiler, usqIntptr_t operand) +{ + return (((((sqLong) operand)) >= -128) && ((((sqLong) operand)) <= 0x7F)); +} + + +/* Answer if an address can be accessed using the offset in a MoveMw:r:R: or + similar instruction. + We assume this is true for 32-bit processors and expect 64-bit processors + to answer false + for values in the interpreter or the object memory. Restrict our use of + offsets to reference + addresses within the method zone, rather than checking for a 32-bit + offset, so as to keep the + simulator and real VM in sync. */ + + /* CogX64Compiler>>#isWithinMwOffsetRange: */ +static NoDbgRegParms sqInt +isWithinMwOffsetRange(AbstractInstruction *self_in_CogX64Compiler, sqInt anAddress) +{ + return /* addressIsInCodeZone: */ + ((((usqInt)anAddress)) >= codeBase) + && ((((usqInt)anAddress)) < limitAddress); +} + + +/* Set the target of a jump instruction. These all have the target in the + first operand. */ +/* Set the target of a jump instruction. These all have the target in the + first operand. + Override to cope with JumpFPNotEqual where because of IEEE NaN conformance + and the behaviour of COMISD/UCOMISD we generate two jumps to the same + target. */ + + /* CogX64Compiler>>#jmpTarget: */ +static NoDbgRegParms AbstractInstruction * +jmpTarget(AbstractInstruction *self_in_CogX64Compiler, AbstractInstruction *anAbstractInstruction) +{ + AbstractInstruction *aDependent; + + aDependent = (self_in_CogX64Compiler->dependent); + while (aDependent) { + jmpTarget(aDependent, anAbstractInstruction); + aDependent = (aDependent->dependent); + } + + /* check for inadvertent smashing of already-set jmpTargets; development only */ + ((self_in_CogX64Compiler->operands))[0] = (((usqInt)anAbstractInstruction)); + return anAbstractInstruction; +} + + +/* Branch/Call ranges. Jump[Cond] can be generated as short as possible. + Call/Jump[Cond]Long must be generated + in the same number of bytes irrespective of displacement since their + targets may be updated, but they need only + span 16Mb, the maximum size of the code zone. This allows e.g. ARM to use + single-word call and jump instructions + for most calls and jumps. CallFull/JumpFull must also be generated in the + same number of bytes irrespective of + displacement for the same reason, but they must be able to span the full + (32-bit or 64-bit) address space because + they are used to call code in the C runtime, which may be distant from the + code zone + */ + + /* CogX64Compiler>>#jumpLongByteSize */ +static NoDbgRegParms sqInt +jumpLongByteSize(AbstractInstruction *self_in_CogX64Compiler) +{ + return 5; +} + + /* CogX64Compiler>>#jumpLongConditionalByteSize */ +static NoDbgRegParms sqInt +jumpLongConditionalByteSize(AbstractInstruction *self_in_CogX64Compiler) +{ + return 6; +} + + +/* Answer the target address for the long jump immediately preceding mcpc */ + + /* CogX64Compiler>>#jumpLongTargetBeforeFollowingAddress: */ +static NoDbgRegParms usqInt +jumpLongTargetBeforeFollowingAddress(AbstractInstruction *self_in_CogX64Compiler, sqInt mcpc) +{ + return callTargetFromReturnAddress(self_in_CogX64Compiler, mcpc); +} + + /* CogX64Compiler>>#jumpTargetPCAt: */ +static NoDbgRegParms usqInt +jumpTargetPCAt(AbstractInstruction *self_in_CogX64Compiler, sqInt pc) +{ + sqInt byte; + sqInt offset; + sqInt size; + + size = instructionSizeAt(self_in_CogX64Compiler, pc); + if (size == 2) { + byte = byteAt((void *)(pc + 1)); + offset = (byte & 128 + ? byte - 0x100 + : byte); + } + else { + byte = byteAt((void *)((pc + size) - 1)); + offset = (byte & 128 + ? byte - 0x100 + : byte); + offset = ((((usqInt)(offset) << 8))) + (byteAt((void *)((pc + size) - 2))); + offset = ((((usqInt)(offset) << 8))) + (byteAt((void *)((pc + size) - 3))); + offset = ((((usqInt)(offset) << 8))) + (byteAt((void *)((pc + size) - 4))); + } + return (pc + size) + offset; +} + + +/* Answer the delta from the stack pointer after a call to the stack pointer + immediately prior to the call. This is used to compute the stack pointer + immediately prior to a call from within a leaf routine, which in turn is + used to capture the c stack pointer to use in trampolines back into the C + run-time. */ + + /* CogX64Compiler>>#leafCallStackPointerDelta */ +static NoDbgRegParms sqInt +leafCallStackPointerDelta(AbstractInstruction *self_in_CogX64Compiler) +{ + return 8; +} + + +/* Answer the byte size of a MoveCwR opcode's corresponding machine code + when the argument is a PIC. This is for the self-reference at the end of a + closed PIC: leaq 0xffffffffffffff2b(%rip), %rcx : 48 8D 0D 2B FF FF FF */ + + /* CogX64Compiler>>#loadPICLiteralByteSize */ +static NoDbgRegParms sqInt +loadPICLiteralByteSize(AbstractInstruction *self_in_CogX64Compiler) +{ + return 7; +} + + /* CogX64Compiler>>#machineCodeAt: */ +static NoDbgRegParms sqInt +machineCodeAt(AbstractInstruction *self_in_CogX64Compiler, sqInt anOffset) +{ + return ((self_in_CogX64Compiler->machineCode))[anOffset]; +} + + +/* Answer the maximum number of bytes of machine code generated for any + abstract instruction. + e.g. xchg %rdx, %rax; movq $0x12345678ABCDEF0, %(rax); xchg %rdx, %rax => + 48 92 48 A3 F0 DE BC 9A 78 56 34 12 48 92 + */ + + /* CogX64Compiler>>#machineCodeBytes */ +static NoDbgRegParms sqInt +machineCodeBytes(AbstractInstruction *self_in_CogX64Compiler) +{ + return 14; +} + + +/* See ModR/M byte & opcode syntax + In addition to the notation shown above in 'Mnemonic Syntax' on page 43, + the following notation indicates the size and type of operands in the + syntax of an instruction opcode: + /digit Indicates that the ModRM byte specifies only one register or memory + (r/m) operand. + The digit is specified by the ModRM reg field and is used as an + instruction-opcode extension. + Valid digit values range from 0 to 7. + /r Indicates that the ModRM byte specifies both a register operand and a + reg/mem (register or memory) operand. */ + + /* CogX64Compiler>>#mod:RM:RO: */ +static NoDbgRegParms usqInt +modRMRO(AbstractInstruction *self_in_CogX64Compiler, sqInt mod, sqInt regMode, sqInt regOpcode) +{ + return (((((usqInt)(mod) << 6))) + (((regOpcode & 7) << 3))) + (regMode & 7); +} + + /* CogX64Compiler>>#numIntRegArgs */ +static NoDbgRegParms sqInt +numIntRegArgs(AbstractInstruction *self_in_CogX64Compiler) +{ + return 4; +} + + /* CogX64Compiler>>#padIfPossibleWithStopsFrom:to: */ +static NoDbgRegParms AbstractInstruction * +padIfPossibleWithStopsFromto(AbstractInstruction *self_in_CogX64Compiler, sqInt startAddr, sqInt endAddr) +{ + stopsFromto(self_in_CogX64Compiler, startAddr, endAddr); + return self_in_CogX64Compiler; +} + + +/* On Intel the performance counter appears to be a nanosecond clock, + at least on intel Core etc. So presume a 1GHz processor */ + + /* CogX64Compiler>>#performanceCounterTicksPerMicrosecond */ +static NoDbgRegParms sqInt +performanceCounterTicksPerMicrosecond(AbstractInstruction *self_in_CogX64Compiler) +{ + return 1000; +} + + /* CogX64Compiler>>#relocateCallBeforeReturnPC:by: */ +static NoDbgRegParms AbstractInstruction * +relocateCallBeforeReturnPCby(AbstractInstruction *self_in_CogX64Compiler, sqInt retpc, sqInt delta) +{ + sqInt distance; + + if (delta) { + distance = ((((((usqInt)((byteAt((void *)(retpc - 1)))) << 24))) + ((((usqInt)((byteAt((void *)(retpc - 2)))) << 16)))) + ((((usqInt)((byteAt((void *)(retpc - 3)))) << 8)))) + (byteAt((void *)(retpc - 4))); + distance += delta; + byteAtput((void *)(retpc - 1),(((usqInt)(distance)) >> 24) & 0xFF); + byteAtput((void *)(retpc - 2),(((usqInt)(distance)) >> 16) & 0xFF); + byteAtput((void *)(retpc - 3),(((usqInt)(distance)) >> 8) & 0xFF); + byteAtput((void *)(retpc - 4),distance & 0xFF); + } + return self_in_CogX64Compiler; +} + + +/* We generate the method address using pc-relative addressing. + Simply check that rip-relative addressing is being used. c.f. + concretizeMoveCwR */ + + /* CogX64Compiler>>#relocateMethodReferenceBeforeAddress:by: */ +static NoDbgRegParms AbstractInstruction * +relocateMethodReferenceBeforeAddressby(AbstractInstruction *self_in_CogX64Compiler, sqInt pc, sqInt delta) +{ + assert((((byteAt((void *)(pc - 6))) == 141) + && (((byteAt((void *)(pc - 5))) | (modRMRO(self_in_CogX64Compiler, 0, 0, 7))) == (modRMRO(self_in_CogX64Compiler, ModRegInd, 5, 7)))) + || (((byteAt((void *)(pc - 8))) == 141) + && (((byteAt((void *)(pc - 7))) | (modRMRO(self_in_CogX64Compiler, 0, 0, 7))) == (modRMRO(self_in_CogX64Compiler, ModRegInd, 5, 7))))); + return self_in_CogX64Compiler; +} + + +/* Rewrite a call instruction to call a different target. This variant is + used to link PICs + in ceSendMiss et al, and to rewrite cached primitive calls. Answer the + extent of + the code change which is used to compute the range of the icache to flush. */ +/* self cCode: '' + inSmalltalk: [cogit disassembleFrom: callSiteReturnAddress - 10 to: + callSiteReturnAddress - 1]. */ + + /* CogX64Compiler>>#rewriteCallAt:target: */ +static NoDbgRegParms sqInt +rewriteCallAttarget(AbstractInstruction *self_in_CogX64Compiler, usqInt callSiteReturnAddress, usqInt callTargetAddress) +{ + usqInt callDistance; + + callDistance = ((usqInt) (int)(callTargetAddress - callSiteReturnAddress)); + byteAtput((void *)(callSiteReturnAddress - 1),((callDistance) >> 24) & 0xFF); + byteAtput((void *)(callSiteReturnAddress - 2),((callDistance) >> 16) & 0xFF); + byteAtput((void *)(callSiteReturnAddress - 3),((callDistance) >> 8) & 0xFF); + byteAtput((void *)(callSiteReturnAddress - 4),callDistance & 0xFF); + assert((callTargetFromReturnAddress(self_in_CogX64Compiler, callSiteReturnAddress)) == callTargetAddress); + + /* self cCode: '' + inSmalltalk: [cogit disassembleFrom: callSiteReturnAddress - 10 to: callSiteReturnAddress - 1]. */ + return 5; +} + + +/* Rewrite the short jump instruction to jump to a new cpic case target. */ +/* prevent type inference for avoiding warning on abs */ + + /* CogX64Compiler>>#rewriteCPICJumpAt:target: */ +static NoDbgRegParms AbstractInstruction * +rewriteCPICJumpAttarget(AbstractInstruction *self_in_CogX64Compiler, usqInt addressFollowingJump, usqInt jumpTargetAddr) +{ + sqInt callDistance; + + callDistance = jumpTargetAddr - addressFollowingJump; + assert((SQABS(callDistance)) < 128); + byteAtput((void *)(addressFollowingJump - 1),callDistance & 0xFF); + return self_in_CogX64Compiler; +} + + +/* Rewrite an inline cache to call a different target for a new tag. This + variant is used + to link unlinked sends in ceSend:to:numArgs: et al. Answer the extent of + the code + change which is used to compute the range of the icache to flush. + N.B. On 64-bit platforms the inline cache tag is only 32-bits wide, hence + this code + is identical to that for the IA32. */ +/* self cCode: '' + inSmalltalk: [cogit disassembleFrom: callSiteReturnAddress - 12 to: + callSiteReturnAddress - 1]. */ + + /* CogX64Compiler>>#rewriteInlineCacheAt:tag:target: */ +static NoDbgRegParms sqInt +rewriteInlineCacheAttagtarget(AbstractInstruction *self_in_CogX64Compiler, usqInt callSiteReturnAddress, sqInt cacheTag, usqInt callTargetAddress) +{ + usqInt callDistance; + + callDistance = ((usqInt) (int)(callTargetAddress - callSiteReturnAddress)); + byteAtput((void *)(callSiteReturnAddress - 1),((callDistance) >> 24) & 0xFF); + byteAtput((void *)(callSiteReturnAddress - 2),((callDistance) >> 16) & 0xFF); + byteAtput((void *)(callSiteReturnAddress - 3),((callDistance) >> 8) & 0xFF); + byteAtput((void *)(callSiteReturnAddress - 4),callDistance & 0xFF); + byteAtput((void *)(callSiteReturnAddress - 6),(((usqInt)(cacheTag)) >> 24) & 0xFF); + byteAtput((void *)(callSiteReturnAddress - 7),(((usqInt)(cacheTag)) >> 16) & 0xFF); + byteAtput((void *)(callSiteReturnAddress - 8),(((usqInt)(cacheTag)) >> 8) & 0xFF); + byteAtput((void *)(callSiteReturnAddress - 9),cacheTag & 0xFF); + assert((callTargetFromReturnAddress(self_in_CogX64Compiler, callSiteReturnAddress)) == callTargetAddress); + + /* self cCode: '' + inSmalltalk: [cogit disassembleFrom: callSiteReturnAddress - 12 to: callSiteReturnAddress - 1]. */ + return 12; +} + + +/* Rewrite an inline cache with a new tag. This variant is used + by the garbage collector. */ + + /* CogX64Compiler>>#rewriteInlineCacheTag:at: */ +static NoDbgRegParms AbstractInstruction * +rewriteInlineCacheTagat(AbstractInstruction *self_in_CogX64Compiler, sqInt cacheTag, sqInt callSiteReturnAddress) +{ + unalignedLong32Atput((void *)(callSiteReturnAddress - 9),cacheTag); + return self_in_CogX64Compiler; +} + + +/* <0-15> */ +/* <0-15> */ +/* <0-15> */ + + /* CogX64Compiler>>#rexR:x:b: */ +static NoDbgRegParms usqInt +rexRxb(AbstractInstruction *self_in_CogX64Compiler, sqInt reg, sqInt sibReg, sqInt fieldReg) +{ + return rexwrxb(self_in_CogX64Compiler, 1, reg, sibReg, fieldReg); +} + + +/* */ +/* <0-15> */ +/* <0-15> */ +/* <0-15> */ +/* Given width64, the R register, sib register, and modrm/sib/reg field + register, answer the correctly encoded REX prefix byte. + See AMD64 Architecture Programmer's Manual Volume 3: General-Purpose and + System Instructions, Table 1-11 */ + + /* CogX64Compiler>>#rexw:r:x:b: */ +static NoDbgRegParms usqInt +rexwrxb(AbstractInstruction *self_in_CogX64Compiler, sqInt width64, sqInt reg, sqInt sibReg, sqInt fieldReg) +{ + usqInt regBits; + + regBits = ((((reg & 8)) >> 1) + (((sibReg & 8)) >> 2)) + (((usqInt)(fieldReg)) >> 3); + return ((width64 + ? 72 + : 64)) + regBits; +} + + +/* to save Slang from having to be a real compiler (it can't inline switches + that return) + */ +/* Answer if the receiver's opcode sets the condition codes correctly for the + given conditional jump opcode. + */ + + /* CogX64Compiler>>#setsConditionCodesFor: */ +static NoDbgRegParms sqInt +setsConditionCodesFor(AbstractInstruction *self_in_CogX64Compiler, sqInt aConditionalJumpOpcode) +{ + switch ((self_in_CogX64Compiler->opcode)) { + case ArithmeticShiftRightCqR: + case ArithmeticShiftRightRR: + case LogicalShiftLeftCqR: + case LogicalShiftLeftRR: + return ((aConditionalJumpOpcode >= JumpZero) && (aConditionalJumpOpcode <= JumpNonNegative)); + + case LogicalShiftRightCqR: + return 0; + + case XorRR: + return 1; + + case ClzRR: + return (aConditionalJumpOpcode == JumpZero) + || ((aConditionalJumpOpcode == JumpNonZero) + || ((aConditionalJumpOpcode == JumpCarry) + || (aConditionalJumpOpcode == JumpNoCarry))); + + default: + haltmsg("unhandled opcode in setsConditionCodesFor:"); + return 0; + } + return 0; +} + + /* CogX64Compiler>>#sizeHasModrm:at: */ +static NoDbgRegParms int +sizeHasModrmat(AbstractInstruction *self_in_CogX64Compiler, sqInt op, sqInt pc) +{ + sqInt mod; + sqInt modrm; + usqInt rm; + usqInt ro; + + modrm = byteAt((void *)(pc + 1)); + mod = ((usqInt)(modrm)) >> 6; + ro = (((usqInt)(modrm)) >> 3) & 7; + rm = modrm & 7; + if (mod == 3) { + return 2; + } + if (rm != 4) { + switch (mod) { + case 0: + return (rm == 5 + ? 6 + : 3); + + case 1: + return 3; + + case 2: + return 6; + + default: + error("Case not found and no otherwise clause"); + } + } + + /* no SIB byte */ + haltmsg("fall through in sizeHasModrm:at:"); + return 0; +} + + +/* see [1] p A-7, p A-13 */ + + /* CogX64Compiler>>#sizeImmediateGroup1:at: */ +static NoDbgRegParms int +sizeImmediateGroup1at(AbstractInstruction *self_in_CogX64Compiler, sqInt op, sqInt pc) +{ + sqInt mod; + sqInt modrm; + usqInt rm; + usqInt ro; + + modrm = byteAt((void *)(pc + 1)); + mod = ((usqInt)(modrm)) >> 6; + ro = (((usqInt)(modrm)) >> 3) & 7; + rm = modrm & 7; + switch (ro) { + case 7: + return (op == 129 + ? 6 + : 3); + + default: + error("Case not found and no otherwise clause"); + } + return 0; +} + + /* CogX64Compiler>>#stopsFrom:to: */ +static NoDbgRegParms AbstractInstruction * +stopsFromto(AbstractInstruction *self_in_CogX64Compiler, sqInt startAddr, sqInt endAddr) +{ + memset(((void *)startAddr), 204 /* stop */, (endAddr - startAddr) + 1); + return self_in_CogX64Compiler; +} + + +/* Rewrite the 32-bit literal in the instruction immediately preceding + followingAddress. + */ + + /* CogX64Compiler>>#storeLiteral32:beforeFollowingAddress: */ +static NoDbgRegParms AbstractInstruction * +storeLiteral32beforeFollowingAddress(AbstractInstruction *self_in_CogX64Compiler, sqInt literal, sqInt followingAddress) +{ + byteAtput((void *)(followingAddress - 1),(((usqInt)(literal)) >> 24) & 0xFF); + byteAtput((void *)(followingAddress - 2),(((usqInt)(literal)) >> 16) & 0xFF); + byteAtput((void *)(followingAddress - 3),(((usqInt)(literal)) >> 8) & 0xFF); + byteAtput((void *)(followingAddress - 4),literal & 0xFF); + return self_in_CogX64Compiler; +} + + /* CogX64Compiler>>#s:i:b: */ +static NoDbgRegParms usqInt +sib(AbstractInstruction *self_in_CogX64Compiler, sqInt scale, sqInt indexReg, sqInt baseReg) +{ + return (((((usqInt)(scale) << 6))) + (((indexReg & 7) << 3))) + (baseReg & 7); +} + + /* CogX64Compiler>>#twoByteInstructionSizeAt: */ +static NoDbgRegParms int +twoByteInstructionSizeAt(AbstractInstruction *self_in_CogX64Compiler, sqInt pc) +{ + sqInt op; + + op = byteAt((void *)(pc + 1)); + switch (op & 240) { + case 128: + /* long conditional jumps */ + return 6; + + default: + error("Case not found and no otherwise clause"); + } + return 0; +} + + +/* Answer if Call and JumpLong are relative and hence need to take the + caller's relocation delta into account during code compaction, rather than + just the + callee's delta. */ + + /* CogX64Compiler>>#zoneCallsAreRelative */ +static NoDbgRegParms sqInt +zoneCallsAreRelative(AbstractInstruction *self_in_CogX64Compiler) +{ + return 1; +} + + +/* Generate the run-time entries at the base of the native code zone and + update the base. + */ + + /* LiveTypingCogit>>#generateRunTimeTrampolines */ +static sqInt +generateRunTimeTrampolines(void) +{ + ceCollectInstVarType = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectInstVarTypeForatof, "ceCollectInstVarType", 3, ReceiverResultReg, Extra0Reg, TempReg, null, CallerSavedRegisterMask, 1, NoReg, 0); + + /* NOTE: the 2-arg trampolines stage their index in Extra1Reg, not Extra0Reg: the value is in + TempReg = CArg0Reg on ARMv8, and genMarshallNArgs relocates that value into Extra0Reg, which + would clobber an index kept there. (The 3-arg ceCollectInstVarType is safe with the index in + Extra0Reg because there its value is relocated into Extra1Reg instead.) */ + ceCollectTempVarType = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectTempVarTypeAtof, "ceCollectTempVarType", 2, Extra1Reg, TempReg, null, null, CallerSavedRegisterMask, 1, NoReg, 0); + ceCollectRemoteTempVarType = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectRemoteTempVarTypeAtof, "ceCollectRemoteTempVarType", 2, Extra1Reg, TempReg, null, null, CallerSavedRegisterMask, 1, NoReg, 0); + ceCollectArgumentsTypeFrameful = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectArgumentsTypeForFramefulActivation, "ceCollectArgsTypeFrameful", 0, null, null, null, null, 0 /* emptyRegisterMask */, 1, NoReg, 0); + ceCollectArgumentsTypeFramelessSpilled = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectArgumentsTypeForFramlessSpilledActivationOf, "ceCollectArgsTypeFramelessSpilled", 1, TempReg, null, null, null, (1U << ReceiverResultReg), 1, NoReg, 0); + ceCollectArgumentsTypeFramelessUnspilled1Args = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectArgumentsTypeForFramlessUnspilledActivationWithArgof, "ceCollectArgumentsTypeFramelessUnspilled1Args", 2, Arg0Reg, TempReg, null, null, ((1U << ReceiverResultReg) | (1U << Arg0Reg)) | (1U << Arg1Reg), 1, NoReg, 0); + ceCollectArgumentsTypeFramelessUnspilled2Args = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectArgumentsTypeForFramlessUnspilledActivationWithArgargof, "ceCollectArgumentsTypeFramelessUnspilled2Args", 3, Arg0Reg, Arg1Reg, TempReg, null, ((1U << ReceiverResultReg) | (1U << Arg0Reg)) | (1U << Arg1Reg), 1, NoReg, 0); + ceCollectReturnType = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectReturnTypeForof, "ceCollectReturnType", 2, TempReg, ReceiverResultReg, null, null, (1U << ReceiverResultReg), 1, NoReg, 0); + ceSendMustBeBooleanAddFalseTrampoline = genMustBeBooleanTrampolineForcalled(falseObject(), "ceSendMustBeBooleanAddFalseTrampoline"); + ceSendMustBeBooleanAddTrueTrampoline = genMustBeBooleanTrampolineForcalled(trueObject(), "ceSendMustBeBooleanAddTrueTrampoline"); + + /* begin genNonLocalReturnTrampoline */ + zeroOpcodeIndex(); + + /* write the return address to the coInterpreter instructionPointerAddress; + following the CallRT to this CISCs will have pushed it on the stack, so pop it first; RISCs will have it in + their link register so just write it directly. */ + + /* PopR: */ + genoperand(PopR, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, instructionPointerAddress()); + ceNonLocalReturnTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceNonLocalReturn, "ceNonLocalReturnTrampoline", 1, ReceiverResultReg, null, null, null, 0 /* emptyRegisterMask */, 0, NoReg, 1); + + /* begin genCheckForInterruptsTrampoline */ + zeroOpcodeIndex(); + + /* if we have a link register we will assume that it does not get automatically pushed onto the stack + and thus there is no need to pop it before saving to instructionPointerAddress */ + + /* PopR: */ + genoperand(PopR, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, instructionPointerAddress()); + ceCheckForInterruptTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCheckForInterrupt, "ceCheckForInterruptTrampoline", 0, null, null, null, null, 0 /* emptyRegisterMask */, 0, NoReg, 1); + + /* Neither of the context inst var access trampolines save registers. Their operation could cause + arbitrary update of stack frames, so the assumption is that callers flush the stack before calling + the context inst var access trampolines, and that everything except the result is dead afterwards. */ + ceFetchContextInstVarTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceContextinstVar, "ceFetchContextInstVarTrampoline", 2, ReceiverResultReg, SendNumArgsReg, null, null, 0 /* emptyRegisterMask */, 1, SendNumArgsReg, 0); + + /* to keep ReceiverResultReg live. */ + ceStoreContextInstVarTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceContextinstVarvalue, "ceStoreContextInstVarTrampoline", 3, ReceiverResultReg, SendNumArgsReg, ClassReg, null, 0 /* emptyRegisterMask */, 1, ReceiverResultReg, 0); + ceCannotResumeTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCannotResume, "ceCannotResumeTrampoline", 0, null, null, null, null, 0 /* emptyRegisterMask */, 1, NoReg, 0); + + /* ceInvokeInterpreter is an optimization and a work-around. Historically we used setjmp/longjmp to reenter the + interpreter at the current C stack base. The C stack base is set at start-up and on each callback enter and + callback return. The interpreter must be invoked whenever a non-machine-code method must be run. That might + be when invoking an interpreter method from one of the send linking routines (ceSend:...), or on continuing from + an evaluation primitive such as primitiveExecuteMethod. The problem here is that such primitives could have + been invoked by the interpreter or by machine code. So some form of non-local jump is required. But at least as + early as MSVC Community 2017, the Microshaft longjmp performs stack unwinding which gets hoplessly confused + (bless its little heart) by any stack switch between machine code and C stack, and raises a spurious + Stack cookie instrumentation code detected a stack-based buffer overrun + error from the bowels of gs_report.c _GSHandlerCheck. + Since the CoInterpreter maintains the base of the C stack in CFramePointer & CStackPointer, it is straight-forward + for us to simply call interpret after doing the switch to the C stack, avoiding the stack unwind issue altogether. */ + ceInvokeInterpret = genInvokeInterpretTrampoline(); + + /* These two are unusual; they are reached by return instructions. */ + ceReturnToInterpreterTrampoline = genReturnToInterpreterTrampoline(); + ceBaseFrameReturnTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceBaseFrameReturn, "ceBaseFrameReturnTrampoline", 1, ReceiverResultReg, null, null, null, 0 /* emptyRegisterMask */, 0, NoReg, 0); + return null; +} + + /* LiveTypingCogit>>#genMoveCurrentMethodOopToRegister: */ +static NoDbgRegParms void +genMoveCurrentMethodOopToRegister(sqInt reg) +{ + addDependent(methodLabel, annotateAbsolutePCRef(genoperandoperand(MoveCwR, ((sqInt)methodLabel), reg))); +} + + /* LiveTypingCogit>>#genStorePop:ReceiverVariable:needsStoreCheck:needsImmutabilityCheck: */ +static NoDbgRegParms sqInt +genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(sqInt popBoolean, sqInt slotIndex, sqInt needsStoreCheck, sqInt needsImmCheck) +{ + AbstractInstruction *abstractInstruction; + sqInt i; + sqInt needsImmCheckSqInt; + sqInt needsStoreCheckSqInt; + sqInt topReg; + + ensureReceiverResultRegContainsSelf(); + storeToReg(ssTop(), TempReg); + + /* #MoveCw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveCwR, slotIndex, Extra0Reg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectInstVarType); + (abstractInstruction->annotation = IsRelativeCall); + ssFlushUpThroughReceiverVariable(slotIndex); + preserveTopInClassRegIfIsReceiverResultReg(); + ensureReceiverResultRegContainsSelf(); + + /* In two path compilation the receiver is young AND mutable, hence no store check nor immutability check is needed */ + needsStoreCheckSqInt = (!useTwoPaths) + && (needsStoreCheck); + needsImmCheckSqInt = needsImmCheck + && (!useTwoPaths); + + /* begin genGenericStorePop:slotIndex:destReg:needsStoreCheck:needsRestoreRcvr:needsImmutabilityCheck: */ +# if IMMUTABILITY + if (needsImmCheckSqInt) { + /* begin ssAllocateRequiredReg:upThrough: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << ClassReg), simStackPtr - 1, simNativeStackPtr); + ssStoreAndReplacePoptoReg(popBoolean, ClassReg); + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + return genStoreWithImmutabilityCheckSourceRegslotIndexdestRegscratchRegneedsStoreCheckneedRestoreRcvr(ClassReg, slotIndex, ReceiverResultReg, TempReg, needsStoreCheckSqInt, 1); + } +# endif // IMMUTABILITY + + topReg = allocateRegForStackEntryAtnotConflictingWith(0, (1U << ReceiverResultReg)); + ssStorePoptoReg(popBoolean, topReg); + return genStoreSourceRegslotIndexdestRegscratchReginFrameneedsStoreCheck(topReg, slotIndex, ReceiverResultReg, TempReg, needsFrame, needsStoreCheckSqInt); +} + + +/* The only reason we assert needsFrame here is that in a frameless method + ReceiverResultReg must and does contain only self, but the ceStoreCheck + trampoline expects the target of the store to be in ReceiverResultReg. So + in a frameless method we would have a conflict between the receiver and + the temote temp store, unless we we smart enough to realise that + ReceiverResultReg was unused after the literal variable store, unlikely + given that methods return self by default. */ +/* Stage the trampoline arguments so they survive the ARM C-argument + marshaller. + The value goes in TempReg and the slot index in Extra1Reg. + TempReg = CArg0Reg on ARMv8, + so when genMarshallNArgs relocates the + value (the arg that aliases CArg0Reg) it moves it into + Extra0Reg; + staging the index in Extra1Reg (NOT Extra0Reg) keeps the index from being + clobbered + by that relocation, which would otherwise pass the value as + both tempVarIndex and assignedObject. + */ + + /* LiveTypingCogit>>#genStorePop:RemoteTemp:At:needsStoreCheck: */ +static NoDbgRegParms sqInt +genStorePopRemoteTempAtneedsStoreCheck(sqInt popBoolean, sqInt slotIndex, sqInt remoteTempIndex, sqInt needsStoreCheck) +{ + AbstractInstruction *abstractInstruction; + sqInt offset; + sqInt topReg; + + storeToReg(ssTop(), TempReg); + + /* #MoveCw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveCwR, slotIndex, Extra1Reg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectRemoteTempVarType); + (abstractInstruction->annotation = IsRelativeCall); + assert(needsFrame); + + /* N.B. No need to check the stack for references because we generate code for + remote temp loads that stores the result in a register, deferring only the register push. */ + + /* begin ssAllocateRequiredReg: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << ReceiverResultReg), simStackPtr, simNativeStackPtr); + voidReceiverResultRegContainsSelf(); + offset = frameOffsetOfTemporary(remoteTempIndex); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, FPReg, ReceiverResultReg); + + /* begin genGenericStorePop:slotIndex:destReg:needsStoreCheck:needsRestoreRcvr:needsImmutabilityCheck: */ +# if IMMUTABILITY +# endif + + topReg = allocateRegForStackEntryAtnotConflictingWith(0, (1U << ReceiverResultReg)); + ssStorePoptoReg(popBoolean, topReg); + return genStoreSourceRegslotIndexdestRegscratchReginFrameneedsStoreCheck(topReg, slotIndex, ReceiverResultReg, TempReg, needsFrame, needsStoreCheck); +} + + +/* Stage the trampoline arguments so they survive the ARM C-argument + marshaller. + The value goes in TempReg and the temp index in Extra1Reg. + TempReg = CArg0Reg on ARMv8, + so when genMarshallNArgs relocates the + value (the arg that aliases CArg0Reg) it moves it into + Extra0Reg; + staging the index in Extra1Reg (NOT Extra0Reg) keeps the index from being + clobbered + by that relocation, which would otherwise pass the value as + both tempVarIndex and assignedObject. + */ + + /* LiveTypingCogit>>#genStorePop:TemporaryVariable: */ +static NoDbgRegParms sqInt +genStorePopTemporaryVariable(sqInt popBoolean, sqInt tempIndex) +{ + AbstractInstruction *abstractInstruction; + sqInt offset; + sqInt reg; + + if (!(inBlock > 0)) { + storeToReg(ssTop(), TempReg); + + /* #MoveCw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveCwR, tempIndex, Extra1Reg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectTempVarType); + (abstractInstruction->annotation = IsRelativeCall); + } + ssFlushUpThroughTemporaryVariable(tempIndex); + reg = ssStorePoptoPreferredReg(popBoolean, TempReg); + offset = frameOffsetOfTemporary(tempIndex); + + /* begin MoveR:Mw:r: */ + /* gen:operand:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, reg, offset, FPReg); + ((simStackAt(tempIndex + 1))->bcptr = bytecodePC); + return 0; +} + + +/* Generate a method return from within a method or a block. + Frameless method activation looks like + CISCs (x86): + receiver + args + sp-> ret pc. + RISCs (ARM): + receiver + args + ret pc in LR. + A fully framed activation is described in CoInterpreter + class>initializeFrameIndices. Return pops receiver and arguments off the + stack. Callee pushes the result. */ +/* Assumes return object to be in ReceiverResultReg already. + Collect the + method's return type before returning. + On architectures with a link + register (ARM), a frameless method holds the caller's + return address in + LinkReg and returns through it (see super's frameless RetN:), but the + + trampoline call is a branch-with-link that clobbers LinkReg. Save and + restore LinkReg + around the call so the return address survives. On CISC + (return address on the stack) + the call is stack-balanced and no + protection is needed. + */ + + /* LiveTypingCogit>>#genUpArrowReturn */ +static sqInt +genUpArrowReturn(void) +{ + AbstractInstruction *abstractInstruction; + sqInt i; + sqInt offset; + + if (!(primitiveIndex > 0)) { + genMoveCurrentMethodOopToRegister(TempReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectReturnType); + (abstractInstruction->annotation = IsRelativeCall); + } + + /* can't fall through */ + deadCode = 1; + if (inBlock > 0) { + assert(needsFrame); + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceNonLocalReturnTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + return 0; + } + if ( +# if IMMUTABILITY + needsFrame + && (!useTwoPaths) +# else + needsFrame +# endif + ) { + + /* MoveR:R: */ + genoperandoperand(MoveRR, FPReg, SPReg); + + /* PopR: */ + genoperand(PopR, FPReg); + + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + else { + offset = ((methodOrBlockNumArgs > (numRegArgs())) + || (regArgsHaveBeenPushed) + ? (methodOrBlockNumArgs + 1) * BytesPerWord + : 0); + + /* begin RetN: */ + genoperand(RetN, offset); + } + return 0; +} + + /* LiveTypingCogit>>#initSimStackForFramefulMethod: */ +static NoDbgRegParms void +initSimStackForFramefulMethod(sqInt startpc) +{ + AbstractInstruction *abstractInstruction; + SimStackEntry *cascade0; + SimStackEntry *desc; + sqInt i; + + /* N.B. Includes num args */ + simStackPtr = methodOrBlockNumTemps; + simSpillBase = methodOrBlockNumTemps + 1; + cascade0 = simSelf(); + (cascade0->type = SSBaseOffset); + (cascade0->spilled = 1); + (cascade0->registerr = FPReg); + (cascade0->offset = FoxMFReceiver); + (cascade0->liveRegister = NoReg); + + /* args */ + for (i = 1; i <= methodOrBlockNumArgs; i += 1) { + desc = simStackAt(i); + (desc->type = SSBaseOffset); + (desc->spilled = 1); + (desc->registerr = FPReg); + (desc->offset = FoxCallerSavedIP + (((methodOrBlockNumArgs - i) + 1) * BytesPerWord)); + (desc->bcptr = startpc); + } + + /* temps */ + for (i = (methodOrBlockNumArgs + 1); i <= simStackPtr; i += 1) { + desc = simStackAt(i); + (desc->type = SSBaseOffset); + (desc->spilled = 1); + (desc->registerr = FPReg); + (desc->offset = FoxMFReceiver - ((i - methodOrBlockNumArgs) * BytesPerWord)); + (desc->bcptr = startpc); + } + if ((inBlock > 0) + || (methodOrBlockNumArgs == 0)) { + return; + } + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectArgumentsTypeFrameful); + (abstractInstruction->annotation = IsRelativeCall); +} + + +/* Collect argument types on entry to a frameless method. Like + genUpArrowReturn, the + trampoline call is a branch-with-link that + clobbers LinkReg, and a frameless activation + holds the caller's return + address in LinkReg, so protect it with saveAndRestoreLinkRegAround: + (a + no-op on CISC). The spilled-args target reads the arguments at + stackPointer + 2*wordSize, + an offset that already accounts for this + protection push plus the trampoline's own + genSmalltalkToCStackSwitch + push, so no interpreter-side change is needed. + */ + + /* LiveTypingCogit>>#initSimStackForFramelessMethod: */ +static NoDbgRegParms void +initSimStackForFramelessMethod(sqInt startpc) +{ + AbstractInstruction *abstractInstruction; + SimStackEntry *cascade0; + SimStackEntry *desc; + sqInt i; + + cascade0 = simSelf(); + (cascade0->type = SSRegister); + (cascade0->spilled = 0); + (cascade0->registerr = ReceiverResultReg); + (cascade0->liveRegister = ReceiverResultReg); + assert(methodOrBlockNumTemps == methodOrBlockNumArgs); + assert((numRegArgs()) <= 2); + if (((methodOrBlockNumArgs >= 1) && (methodOrBlockNumArgs <= (numRegArgs())))) { + desc = simStackAt(1); + (desc->type = SSRegister); + (desc->spilled = 0); + (desc->registerr = Arg0Reg); + (desc->bcptr = startpc); + if (methodOrBlockNumArgs > 1) { + desc = simStackAt(2); + (desc->type = SSRegister); + (desc->spilled = 0); + (desc->registerr = Arg1Reg); + (desc->bcptr = startpc); + } + } + else { + for (i = 1; i <= methodOrBlockNumArgs; i += 1) { + desc = simStackAt(i); + (desc->type = SSBaseOffset); + (desc->registerr = SPReg); + (desc->spilled = 1); + (desc->offset = ((methodOrBlockNumArgs + 1) - i) * BytesPerWord); + (desc->bcptr = startpc); + } + } + simStackPtr = methodOrBlockNumArgs; + simSpillBase = methodOrBlockNumArgs + 1; + if ((inBlock > 0) + || (methodOrBlockNumArgs == 0)) { + return; + } + genMoveCurrentMethodOopToRegister(TempReg); + + /* saveAndRestoreLinkRegAround: */ + switch (methodOrBlockNumArgs) { + case 1: + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectArgumentsTypeFramelessUnspilled1Args); + (abstractInstruction->annotation = IsRelativeCall); + break; + case 2: + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectArgumentsTypeFramelessUnspilled2Args); + (abstractInstruction->annotation = IsRelativeCall); + break; + default: + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectArgumentsTypeFramelessSpilled); + (abstractInstruction->annotation = IsRelativeCall); + } +} + + +/* Do the store check. Answer the argument for the benefit of the code + generator; ReceiverResultReg may be caller-saved and hence smashed by this + call. Answering + it allows the code generator to reload ReceiverResultReg cheaply. + In Spur the only thing we leave to the run-time is adding the receiver to + the remembered set and setting its isRemembered bit. */ + + /* LiveTypingCogObjectRepresentationFor64BitSpur>>#generateObjectRepresentationTrampolines */ +static void +generateObjectRepresentationTrampolines(void) +{ + sqInt instVarIndex; + AbstractInstruction *jumpSC; + + ceCollectTypesForPrimitiveSlotAtPut = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCollectTypesForPrimitiveSlotAtputof, "ceCollectTypesForPrimitiveSlotAtPut", 3, Arg0Reg, Arg1Reg, ReceiverResultReg, null, CallerSavedRegisterMask, 1, NoReg, 0); +# if IMMUTABILITY + for (instVarIndex = 0; instVarIndex < NumStoreTrampolines; instVarIndex += 1) { + ceStoreTrampolines[instVarIndex] = (genStoreTrampolineCalledinstVarIndex(trampolineNamenumArgslimit("ceStoreTrampoline", instVarIndex, NumStoreTrampolines - 2), instVarIndex)); + } +# endif // IMMUTABILITY + + + /* begin genStoreCheckTrampoline */ + if (CheckRememberedInTrampoline) { + zeroOpcodeIndex(); + jumpSC = genCheckRememberedBitOfscratch(ReceiverResultReg, ABIResultReg); + assert(((jumpSC->opcode)) == JumpNonZero); + (jumpSC->opcode = JumpZero); + + /* RetN: */ + genoperand(RetN, 0); + jmpTarget(jumpSC, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } + ceStoreCheckTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(remember, "ceStoreCheckTrampoline", 1, ReceiverResultReg, null, null, null, ((CallerSavedRegisterMask | ((1U << ReceiverResultReg))) - ((1U << ReceiverResultReg))), 1, /* returnRegForStoreCheck */ + (((CallerSavedRegisterMask & ((1U << ReceiverResultReg))) != 0) + ? ReceiverResultReg + : ABIResultReg), CheckRememberedInTrampoline); + ceStoreCheckContextReceiverTrampoline = genStoreCheckContextReceiverTrampoline(); + ceScheduleScavengeTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceScheduleScavenge, "ceScheduleScavengeTrampoline", 0, null, null, null, null, CallerSavedRegisterMask, 1, NoReg, 0); + ceSmallActiveContextInMethodTrampoline = genActiveContextTrampolineLargeinBlockcalled(0, 0, "ceSmallMethodContext"); + ceSmallActiveContextInBlockTrampoline = genActiveContextTrampolineLargeinBlockcalled(0, InVanillaBlock, "ceSmallBlockContext"); + ceSmallActiveContextInFullBlockTrampoline = genActiveContextTrampolineLargeinBlockcalled(0, InFullBlock, "ceSmallFullBlockContext"); + ceLargeActiveContextInMethodTrampoline = genActiveContextTrampolineLargeinBlockcalled(1, 0, "ceLargeMethodContext"); + ceLargeActiveContextInBlockTrampoline = genActiveContextTrampolineLargeinBlockcalled(1, InVanillaBlock, "ceLargeBlockContext"); + ceLargeActiveContextInFullBlockTrampoline = genActiveContextTrampolineLargeinBlockcalled(1, InFullBlock, "ceLargeFullBlockContext"); +} + + +/* Generate the code for primitive 174, instVarAt:put:. Defer to + StackInterpreterPrimitives>>primitiveSlotAtPut for Contexts. + */ +/* c.f. StackInterpreter>>stSizeOf: SpurMemoryManager>>lengthOf:format: + fixedFieldsOf:format:length: + */ +/* Assumes arguments to be in Arg0Reg and Arg1Reg, and receiver to be in + ReceiverResultReg. + Collect the assigned slot value's type before + performing the primitive store. + The at:put:/instVarAt:put: primitive is + compiled into a frameless method, which on + architectures with a link + register (ARM) keeps the caller's return address in LinkReg and + returns + through it. The ceCollectTypesForPrimitiveSlotAtPut trampoline call is a + + branch-with-link that clobbers LinkReg, so protect it with + saveAndRestoreLinkRegAround: + (a no-op on CISC, where the return address + is on the stack). Without this the frameless + primitive returns into + itself instead of its caller, corrupting the stack. + */ + + /* LiveTypingCogObjectRepresentationFor64BitSpur>>#genPrimitiveSlotAtPut */ +static sqInt +genPrimitiveSlotAtPut(void) +{ + AbstractInstruction *abstractInstruction; + sqInt formatReg; + AbstractInstruction *jump64BitArgIsImmediate; + AbstractInstruction *jump64BitsOutOfBounds; + AbstractInstruction *jumpBadIndex; + AbstractInstruction *jumpBytesOutOfBounds; + AbstractInstruction *jumpBytesOutOfRange; + AbstractInstruction *jumpImmediate; + AbstractInstruction *jumpImmutable; + AbstractInstruction *jumpIsBytes; + AbstractInstruction *jumpIsCompiledMethod; + AbstractInstruction *jumpIsContext; + AbstractInstruction *jumpIsShorts; + AbstractInstruction *jumpIsWords; + AbstractInstruction *jumpNonSmallIntegerValue; + AbstractInstruction *jumpNot64BitIndexable; + AbstractInstruction *jumpNot8ByteInteger; + AbstractInstruction *jumpNotIndexableBits; + AbstractInstruction *jumpNotPointers; + AbstractInstruction *jumpPointersOutOfBounds; + AbstractInstruction *jumpShortsOutOfBounds; + AbstractInstruction *jumpShortsOutOfRange; + AbstractInstruction *jumpWordsOutOfBounds; + AbstractInstruction *jumpWordsOutOfRange; + sqInt nSlotsOrBytesReg; + sqInt quickConstant; + AbstractInstruction *rejoin; + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceCollectTypesForPrimitiveSlotAtPut); + (abstractInstruction->annotation = IsRelativeCall); + nSlotsOrBytesReg = Extra0Reg; + + /* begin genLoadArgAtDepth:into: */ + assert(1 < (numRegArgs())); + assert(0 < (numRegArgs())); + jumpImmediate = genJumpImmediate(ReceiverResultReg); + jumpBadIndex = genJumpNotSmallInteger(Arg0Reg); + genConvertSmallIntegerToIntegerInReg(Arg0Reg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 1, Arg0Reg); +# if IMMUTABILITY + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), TempReg); + + /* begin genJumpBaseHeaderImmutable: */ + /* TstCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(TstCqR, immutableBitMask(), TempReg); + jumpImmutable = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); +# else // IMMUTABILITY + genGetFormatOfintoleastSignificantHalfOfBaseHeaderIntoScratch(ReceiverResultReg, (formatReg = SendNumArgsReg), NoReg); +# endif // IMMUTABILITY + + genGetNumSlotsOfinto(ReceiverResultReg, nSlotsOrBytesReg); + + /* dispatch on format in a combination of highest dynamic frequency order first and convenience. + 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, weakArrayFormat(), formatReg); + jumpNotPointers = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + genGetClassIndexOfNonImminto(ReceiverResultReg, ClassReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ClassMethodContextCompactIndex, ClassReg); + + /* Fail to StackInterpreterPrimitives>>primitiveSlotAtPut for Context */ + /* optimistic store check; assume index in range (almost always is). */ + jumpIsContext = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + genStoreCheckReceiverRegvalueRegscratchReginFrame(ReceiverResultReg, Arg1Reg, TempReg, 0); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpPointersOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), Arg0Reg); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, Arg1Reg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotPointers, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpNonSmallIntegerValue = genJumpNotSmallInteger(Arg1Reg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstByteFormat(), formatReg); + jumpIsBytes = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstShortFormat(), formatReg); + jumpIsShorts = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstLongFormat(), formatReg); + jumpIsWords = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, sixtyFourBitIndexableFormat(), formatReg); + jumpNotIndexableBits = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* fall through to 64-bit words */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, SendNumArgsReg); + genConvertSmallIntegerToIntegerInReg(SendNumArgsReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + rejoin = genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jump64BitsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> (shiftForWord()), Arg0Reg); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, SendNumArgsReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + + /* Handle LargePositiveInteger (unsigned) or LargePositiveInteger and LargeNegativeInteger (signed) + for sixtyFourBitIndexableFormat */ + jmpTarget(jumpNonSmallIntegerValue, genoperandoperand(CmpCqR, sixtyFourBitIndexableFormat(), formatReg)); + jumpNot64BitIndexable = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + jump64BitArgIsImmediate = genJumpImmediate(Arg1Reg); + + /* Now check if the header is that of an 8 byte LargePositiveInteger */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, Arg1Reg, SendNumArgsReg); + quickConstant = headerForSlotsformatclassIndex(numSlotsMask(), formatMask(), classIndexMask()); + + /* begin AndCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, quickConstant, SendNumArgsReg); + quickConstant = headerForSlotsformatclassIndex(1, firstByteFormat(), ClassLargePositiveIntegerCompactIndex); + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, SendNumArgsReg); + jumpNot8ByteInteger = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BaseHeaderSize, Arg1Reg, SendNumArgsReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)rejoin)); + jmpTarget(jumpIsWords, genoperandoperand(CmpCqR, (((usqInt)0xFFFFFFFFU << 3) | 1), Arg1Reg)); + jumpWordsOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - 2, nSlotsOrBytesReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 4) - 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrBytesReg); + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpWordsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, ((usqInt)(BaseHeaderSize)) >> ((shiftForWord()) - 1), Arg0Reg); + + /* MoveR:X32r:R: */ + genoperandoperandoperand(MoveRX32rR, TempReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsShorts, genoperandoperand(CmpCqR, (((usqInt)0xFFFF << 3) | 1), Arg1Reg)); + jumpShortsOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - 1, nSlotsOrBytesReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, (BytesPerWord / 2) - 1, formatReg); + + /* SubR:R: */ + genoperandoperand(SubRR, formatReg, nSlotsOrBytesReg); + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpShortsOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg0Reg, ReceiverResultReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg0Reg, ReceiverResultReg); + + /* #MoveR:M16:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRM16r, TempReg, BaseHeaderSize, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpIsBytes, genoperandoperand(CmpCqR, (((usqInt)0xFF << 3) | 1), Arg1Reg)); + jumpBytesOutOfRange = genConditionalBranchoperand(JumpAbove, ((sqInt)0)); + + /* LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, shiftForWord(), nSlotsOrBytesReg); + gAndCqRR(BytesPerWord - 1, formatReg, TempReg); + + /* SubR:R: */ + genoperandoperand(SubRR, TempReg, nSlotsOrBytesReg); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, nSlotsOrBytesReg); + jumpBytesOutOfBounds = genConditionalBranchoperand(JumpBelowOrEqual, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), formatReg); + jumpIsCompiledMethod = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, TempReg); + genConvertSmallIntegerToIntegerInReg(TempReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BaseHeaderSize, Arg0Reg); + + /* MoveR:Xbr:R: */ + genoperandoperandoperand(MoveRXbrR, TempReg, Arg0Reg, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNot8ByteInteger, jmpTarget(jump64BitArgIsImmediate, jmpTarget(jumpNot64BitIndexable, jmpTarget(jumpIsContext, jmpTarget(jumpNotIndexableBits, jmpTarget(jumpBytesOutOfRange, jmpTarget(jumpShortsOutOfRange, jmpTarget(jumpWordsOutOfRange, jmpTarget(jumpIsCompiledMethod, jmpTarget(jumpPointersOutOfBounds, jmpTarget(jumpBytesOutOfBounds, jmpTarget(jumpShortsOutOfBounds, jmpTarget(jumpWordsOutOfBounds, jmpTarget(jump64BitsOutOfBounds, genoperandoperand(Label, (labelCounter += 1), bytecodePC))))))))))))))); +# if IMMUTABILITY + jmpTarget(jumpImmutable, ((AbstractInstruction *) (((jumpIsContext->operands))[0]))); +# endif + + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 1, Arg0Reg); + genConvertIntegerToSmallIntegerInReg(Arg0Reg); + jmpTarget(jumpBadIndex, jmpTarget(jumpImmediate, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return 0; +} + + +/* Answer the Cogit's ABI as a string, if it adheres to a specific one. + Otherwise answer nil + */ + + /* SimpleStackBasedCogit>>#abiName */ +#if VMInvestigations +char * +abiName(void) +{ + return +# if defined(ABI) + ABI +# else + null +# endif + ; +} +#endif /* VMInvestigations */ + + /* SimpleStackBasedCogit>>#cogMethodHasExternalPrim: */ +sqInt +cogMethodHasExternalPrim(CogMethod *aCogMethod) +{ + sqInt primIndex; + + primIndex = primitiveIndexOfMethodheader((aCogMethod->methodObject), (aCogMethod->methodHeader)); + return (primIndex == PrimNumberExternalCall) + || (primIndex == PrimNumberFFICall); +} + + /* SimpleStackBasedCogit>>#cogMethodHasMachineCodePrim: */ +sqInt +cogMethodHasMachineCodePrim(CogMethod *aCogMethod) +{ + sqInt primIndex; + + primIndex = primitiveIndexOfMethodheader((aCogMethod->methodObject), (aCogMethod->methodHeader)); + return (((primIndex >= 1) && (primIndex <= MaxCompiledPrimitiveIndex))) + && ((((primitiveGeneratorTable[primIndex]).primitiveGenerator))); +} + + +/* Populate the last element of the three element array data with a sequence + of relative pc/value/object triples for the object & code references + embedded in the CogMethod. + If detailCPICs is true, include the list of targets as an array for sites + linked to closed PICs. + */ + + /* SimpleStackBasedCogit>>#cogMethodImports:into:detailCPICs: */ +#if VMInvestigations +sqInt +cogMethodImportsintodetailCPICs(CogMethod *cogMethod, sqInt data, sqInt detailCPICs) +{ + char *abort; + sqInt annotation; + sqInt importCount; + sqInt importsArray; + usqInt importsPtr[3]; + sqInt map; + sqInt mapByte; + usqInt mcpc; + void (*pfp)(void); + sqInt result; + + importCount = 0; + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = countCogMethodDataImportspccount(annotation, ((char *) mcpc), (&importCount)); + if (result) { + goto l1; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + /* end mapFor:performUntil:arg: */ +l1: + importCount = (importCount + 2) + 1; + + /* for methodObject & selector + for the abort */ + if ((((primitiveIndex = primitiveIndexOf((cogMethod->methodObject)))) > 0) + && ((!(isQuickPrimitiveIndex(primitiveIndex))) + && (((pfp = functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto((cogMethod->methodObject), primitiveIndex, null))) != 0))) { + importCount += 1; + } + else { + primitiveIndex = 0; + } + if ((importsArray = instantiateClassindexableSize(classArray(), importCount * 3))) { + abort = ((char *)(callTargetFromReturnAddress(backEnd, (((usqInt)cogMethod)) + missOffset))); + assert((codeEntryFor(abort)) == abort); + storePointerUncheckedofObjectwithValue(2, data, importsArray); + storePointerUncheckedofObjectwithValue(0, importsArray, (((usqInt)(offsetof(CogMethod, methodObject)) << 3) | 1)); + storePointerUncheckedofObjectwithValue(1, importsArray, positive64BitIntegerFor((cogMethod->methodObject))); + storePointerUncheckedofObjectwithValue(2, importsArray, (cogMethod->methodObject)); + storePointerUncheckedofObjectwithValue(3, importsArray, (((usqInt)(offsetof(CogMethod, selector)) << 3) | 1)); + storePointerUncheckedofObjectwithValue(4, importsArray, positive64BitIntegerFor((cogMethod->selector))); + storePointerUncheckedofObjectwithValue(5, importsArray, (cogMethod->selector)); + storePointerUncheckedofObjectwithValue(6, importsArray, (((usqInt)missOffset << 3) | 1)); + storePointerUncheckedofObjectwithValue(7, importsArray, positive64BitIntegerFor(callTargetFromReturnAddress(backEnd, (((usqInt)cogMethod)) + missOffset))); + storePointerUncheckedofObjectwithValue(8, importsArray, stringForCString(codeEntryNameFor(abort))); + + /* the first and second entries are the methodObject and selector in the CogMethod header + the third entry in imports is the abort call */ + if (primitiveIndex > 0) { + storePointerUncheckedofObjectwithValue(9, importsArray, (((usqInt)-1 << 3) | 1)); + storePointerUncheckedofObjectwithValue(10, importsArray, positive64BitIntegerFor(((usqInt)pfp))); + storePointerUncheckedofObjectwithValue(11, importsArray, stringForCString("primitiveFunction")); + importsPtr[0] = (((usqInt) ((((usqInt *) (firstIndexableField(importsArray)))) + 12))); + } + else { + importsPtr[0] = (((usqInt) ((((usqInt *) (firstIndexableField(importsArray)))) + 9))); + } + + /* pass both the next place to store in imports (importsPtr at: 0) and the imports array (importsPtr at: 1) to the store enumerator */ + importsPtr[1] = importsArray; + importsPtr[2] = detailCPICs; + enumeratingCogMethod = cogMethod; + + /* N.B. answers error code from storeCogMethodDataImports:pc:pointer: if any */ + + /* begin mapFor:performUntil:arg: */ + mapByte = 0; + mcpc = /* firstMappedPCFor: */ + ((((cogMethod->cmType)) >= CMMethod) + && ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) + ? (((usqInt)cogMethod)) + cbNoSwitchEntryOffset + : (((usqInt)cogMethod)) + cmNoCheckEntryOffset); + map = ((((usqInt)cogMethod)) + ((cogMethod->blockSize))) - 1; + enumeratingCogMethod = cogMethod; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + mcpc += (mapByte & DisplacementMask); + + /* If this is an IsSendCall annotation, peek ahead for an IsAnnotationExtension, and consume it. */ + if ((((annotation = ((usqInt)(mapByte)) >> AnnotationShift)) == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + result = storeCogMethodDataImportspcpointer(annotation, ((char *) mcpc), (&importsPtr)); + if (result) { + return result; + } + } + else { + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + return 0; + } + else { + return PrimErrNoMemory; + } +} +#endif /* VMInvestigations */ + + +/* Compile the jump instruction(s) at the end of the method that dispatch to + each block body. + */ + + /* SimpleStackBasedCogit>>#compileBlockDispatch */ +static sqInt +compileBlockDispatch(void) +{ + AbstractInstruction *jumpSkip; + + assert(blockCount > 0); + + /* Generate the special block entry for primitiveClosureValueNoContextSwitch + that signals a context-switch is not possible by zeroing SendNumArgsReg. + Even though conceptually this isn't needed if no blocks build a frame it has + to be present so that primitiveClosureValueNoContextSwitch can call it. */ + blockEntryNoContextSwitch = genoperandoperand(MoveCqR, 0, SendNumArgsReg); + + /* Now generate a binary search through start pcs to jump to blocks. 1 block is a special case. */ + jumpSkip = genoperand(Jump, ((sqInt)0)); + + /* Set OK to context switch flag to non-zero. */ + blockEntryLabel = genoperandoperand(MoveRR, ReceiverResultReg, SendNumArgsReg); + jmpTarget(jumpSkip, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + if (blockCount > 1) { + genLoadSlotsourceRegdestReg(ClosureStartPCIndex, ReceiverResultReg, TempReg); + } + compileBlockDispatchFromto(0, blockCount - 1); + return 0; +} + + +/* After pushing the temporaries but before the stack limit check a primitive + method needs to fetch the error code, if any. If the primitive has failed, + call the trampoline + that will assign it to the last temp. */ + + /* SimpleStackBasedCogit>>#compileGetErrorCode */ +static void +compileGetErrorCode(void) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *jmpNoError; + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, primFailCodeAddress(), TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, TempReg); + jmpNoError = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + addDependent(methodLabel, annotateAbsolutePCRef(genoperandoperand(MoveCwR, ((sqInt)methodLabel), ClassReg))); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceReapAndResetErrorCodeTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpTarget(jmpNoError, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); +} + + /* SimpleStackBasedCogit>>#compileInterpreterPrimitive */ +static sqInt +compileInterpreterPrimitive(void) +{ + sqInt flags; + void (*primitiveRoutine)(void); + + flags = 0; + primitiveRoutine = functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(methodObj, primitiveIndex, (&flags)); + assert((((flags & PrimCallCollectsProfileSamples) != 0) + ? ((flags & PrimCallNeedsNewMethod) != 0) + : 1)); + if (((flags & PrimCallOnSmalltalkStack) != 0)) { + return compileOnStackExternalPrimitiveflags(primitiveRoutine, flags); + } + return compileInterpreterPrimitiveflags(primitiveRoutine, flags); +} + + +/* Compile a call to an interpreter primitive. Call the C routine with the + usual stack-switching dance, test the primFailCode and then either + return on success or continue to the method body. */ + + /* SimpleStackBasedCogit>>#compileInterpreterPrimitive:flags: */ +static NoDbgRegParms sqInt +compileInterpreterPrimitiveflags(void (*primitiveRoutine)(void), sqInt flags) +{ + AbstractInstruction *continueAfterRetry; + AbstractInstruction *jmpOnFailure; + AbstractInstruction *jmpRetryOrNil; + sqInt record; + + assert(!((registerisInMask(VarBaseReg, ABICallerSavedRegisterMask)))); + + /* Save processor fp, sp and return pc in the interpreter's frame stack and instruction pointers */ + genExternalizePointersForPrimitiveCall(); + + /* Switch to the C stack. */ + + /* begin genLoadCStackPointersForPrimCall */ + if (cFramePointerInUse) { + genLoadCStackPointers(backEnd); + } + else { + genLoadCStackPointer(backEnd); + } + + /* Old old full prim trace is in VMMaker-eem.550 and prior. + Old simpler full prim trace is in VMMaker-eem.2969 and prior. */ + if ((record = recordPrimTraceForMethod(methodObj))) { + genFastPrimTraceUsingandsetNewMethod(ClassReg, SendNumArgsReg, ((flags & PrimCallNeedsNewMethod) != 0)); + } + + /* Clear the primFailCode and set argumentCount */ + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, primFailCodeAddress()); + if (methodOrBlockNumArgs) { + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, methodOrBlockNumArgs, TempReg); + } + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, argumentCountAddress()); + + /* If required, set newMethod */ + if ((!record) + && (((flags & PrimCallNeedsNewMethod) != 0))) { + genLoadNewMethod(); + } + + /* Invoke the primitive. If the primitive (potentially) contains a call-back then its code + may disappear and consequently we cannot return here, since here may evaporate. + Instead sideways-call the routine, substituting cePrimReturnEnterCogCode[Profiling] + as the return address, so the call always returns there. + To save complications also use this path when profiling, since profiling slows down + execution by much more than this sideways call and it means we have but one + version of the profiling sample code to worry about. */ + + /* #PrefetchAw: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(PrefetchAw, primFailCodeAddress()); + if (((flags & (PrimCallMayEndureCodeCompaction + PrimCallCollectsProfileSamples)) != 0)) { + /* The ceActivateFailingPrimitiveMethod: machinery can't handle framelessness. */ + needsFrame = 1; + + /* begin genMarshallNArgs:arg:arg:arg:arg: */ + /* SubCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 32, RSP); + + /* WIN64 ABI always reserves shadow space on the stack for callee to save up to 4 register parameters */ + + /* #genSubstituteReturnAddress: #PushCw: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(PushCw, (((flags & PrimCallCollectsProfileSamples) != 0) + ? cePrimReturnEnterCogCodeProfiling + : cePrimReturnEnterCogCode)); + + /* #JumpFullRT: #JumpFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(JumpFull, ((sqInt)(((usqInt)primitiveRoutine)))); + return 0; + } + + /* Call the C primitive routine. */ + + /* begin genMarshallNArgs:arg:arg:arg:arg: */ + /* SubCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 32, RSP); + + /* WIN64 ABI always reserves shadow space on the stack for callee to save up to 4 register parameters */ + + /* #CallFullRT: #CallFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(CallFull, ((usqInt)primitiveRoutine)); + + /* begin genRemoveNArgsFromStack: */ + /* AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 32, RSP); + jmpRetryOrNil = compileJumpToRetryOfonPrimitiveFail(primitiveRoutine, primitiveIndex); + + /* placing the test here attributes the tick to the primitive plus any checkForAndFollowForwardedPrimitiveState + scanning, but attributes all of a failing primitive to the current method (in ceStackOverflow: on frame build). */ + continueAfterRetry = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* Switch back to the Smalltalk stack. Stack better be in either of these two states: + success: stackPointer -> result (was receiver) + arg1 + ... + argN + return pc + failure: receiver + arg1 + ... + stackPointer -> argN + return pc */ + genLoadStackPointersForPrimCall(backEnd, ClassReg); + + /* genLoadStackPointersForPrimCall: leaves the stack in these states: + NoLinkRegister LinkRegister + success: result (was receiver) stackPointer -> result (was receiver) + stackPointer -> arg1 arg1 + ... ... + argN argN + return pc + failure: receiver receiver + arg1 arg1 + ... ... + argN stackPointer -> argN + stackPointer -> return pc + which corresponds to the stack on entry after pushRegisterArgs. + In either case we can write the instructionPointer to top of stack or load it into the LinkRegister to reestablish the return pc. */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, instructionPointerAddress(), ClassReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ClassReg, 0, SPReg); + + /* Test primitive failure */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, primFailCodeAddress(), TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, TempReg); + jmpOnFailure = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Fetch result from stack */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord, SPReg, ReceiverResultReg); + + /* RetN: */ + genoperand(RetN, BytesPerWord); + maybeCompileRetryOfonPrimitiveFailflagsjumpcontinue(primitiveRoutine, primitiveIndex, flags, jmpRetryOrNil, continueAfterRetry); + + /* Jump to restore of receiver reg and proceed to frame build for failure. */ + jmpTarget(jmpOnFailure, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* Restore receiver reg from stack. If on RISCs ret pc is in LinkReg, if on CISCs ret pc is on stack. */ + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord * (methodOrBlockNumArgs + 1), SPReg, ReceiverResultReg); + return 0; +} + + +/* Compile a fast call of a C primitive using the current stack page, + avoiding the stack switch except on failure. + This convention still uses stackPointer and argumentCount to access + operands. Push all operands to the stack, + assign stackPointer, argumentCount, and zero primFailCode. Make the call + (saving a LinkReg if required). + Test for failure and return. On failure, if there is an accessor depth, + assign framePointer and newMethod, + do the stack switch, call checkForAndFollowForwardedPrimitiveState, and + loop back if forwarders are found. + Fall through to frame build. + + Things get messy when profiling. Because of the potential retry, and the + need to take the profile sample after a + retry if it occurs, two separate samplings must be done, one immediately + before returning on success (potentially + after retry), and one immediately before falling through to failure. + However, on failure there is no need to take + a sample immediately. Provided the stack limit is smashed then a sample + will be taken on frame build. */ + + /* SimpleStackBasedCogit>>#compileOnStackExternalPrimitive:flags: */ +static NoDbgRegParms sqInt +compileOnStackExternalPrimitiveflags(void (*primitiveRoutine)(void), sqInt flags) +{ + AbstractInstruction *abstractInstruction; + sqInt calleeSavedRegisterMask; + sqInt callTargetSqInt; + AbstractInstruction *continueAfterProfileSampleOnSuccess; + AbstractInstruction *jmpRetryOrFail; + AbstractInstruction *jumpToTakeSampleOnSuccess; + sqInt reg; + sqInt retpcOffset; + AbstractInstruction *retry; + AbstractInstruction *skip; + sqInt spRegSaveRegister; + + jumpToTakeSampleOnSuccess = ((AbstractInstruction *) 0); + assert(((flags & PrimCallOnSmalltalkStack) != 0)); + assert(!((registerisInMask(VarBaseReg, ABICallerSavedRegisterMask)))); + if (recordFastCCallPrimTraceForMethod(methodObj)) { + genFastPrimTraceUsingandsetNewMethod(ClassReg, SendNumArgsReg, 0); + } + + /* Clear the primFailCode and set argumentCount */ + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, primFailCodeAddress()); + if (methodOrBlockNumArgs) { + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, methodOrBlockNumArgs, TempReg); + } + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, argumentCountAddress()); + genExternalizeStackPointerForFastPrimitiveCall(); + + /* If profiling also write the framePointer and instructionPointer so that + ceTakeProfileSample: can activate the profiling process immediately. */ + if (((flags & PrimCallCollectsProfileSamples) != 0)) { + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, FPReg, framePointerAddress()); + if (regArgsHaveBeenPushed) { + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, SendNumArgsReg, instructionPointerAddress()); + } + else { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, SPReg, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, instructionPointerAddress()); + } + } + + /* We may need to save LinkReg and/or SPReg, and given the stack machinations + it is much easier to save them in callee saved registers than on the stack itself. */ + calleeSavedRegisterMask = ((ABICalleeSavedRegisterMask | ((1U << ClassReg))) - ((1U << ClassReg))); + spRegSaveRegister = NoReg; + retry = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + if (((flags & PrimCallOnSmalltalkStackAlign2x) != 0)) { + gAndCqRR((-1 - (((BytesPerWord * 2) - 1))), SPReg, NativeSPReg); + } + else { + } + + /* begin genMarshallNArgs:arg:arg:arg:arg: */ + /* SubCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 32, RSP); + + /* WIN64 ABI always reserves shadow space on the stack for callee to save up to 4 register parameters */ + + /* If the primitive is in the interpreter then its address won't change relative to the code zone over time, + whereas if it is in a plugin its address could change if the module is un/re/over/loaded. + So if in the interpreter and in range use a normal call instruction. */ + if (((!(flags & PrimCallIsExternalCall))) + && (is32BitSignedImmediate(backEnd, maximumDistanceFromCodeZone(((usqInt)primitiveRoutine))))) { + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ((usqInt)primitiveRoutine)); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* #CallFullRT: #CallFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(CallFull, ((usqInt)primitiveRoutine)); + } + + /* begin genRemoveNArgsFromStack: */ + /* AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 32, RSP); + + /* Remember to restore the native stack pointer to point to the C stack, + otherwise the Smalltalk frames will get overwritten on an interrupt. */ + + /* test primFailCode and jump to failure sequence if non-zero */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, primFailCodeAddress(), TempReg); + if (spRegSaveRegister != NoReg) { + /* MoveR:R: */ + genoperandoperand(MoveRR, spRegSaveRegister, SPReg); + } + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, TempReg); + jmpRetryOrFail = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* If profiling, check for a sample now. The alternative is to have two checks and two calls, + which would include the cost of checkForAndFollowForwardedPrimitiveState in the sample. + But that's excessive given that retry failures are very rare. */ + if (((flags & PrimCallCollectsProfileSamples) != 0)) { + /* begin genCheckForProfileTimerTick: */ + reg = RAX; + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, nextProfileTickAddress(), Arg1Reg); + gMovePerfCnt64RL(reg, (0)); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, reg); + jumpToTakeSampleOnSuccess = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + } + + /* At this point the primitive has cut back stackPointer to point to the result. */ + continueAfterProfileSampleOnSuccess = genoperandoperand(MoveAwR, stackPointerAddress(), TempReg); + + /* get result and restore retpc */ + + /* The original retpc is (argumentCount + 1) words below stackPointer. */ + retpcOffset = -((methodOrBlockNumArgs + 1) * BytesPerWord); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, retpcOffset, TempReg, ClassReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, SPReg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, TempReg, ReceiverResultReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ClassReg, 0, TempReg); + + /* RetN: */ + genoperand(RetN, 0); + if (((flags & PrimCallCollectsProfileSamples) != 0)) { + jmpTarget(jumpToTakeSampleOnSuccess, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genLoadNewMethod(); + genTakeProfileSample(continueAfterProfileSampleOnSuccess); + } + + /* primitive failure. if there is an accessor depth, scan and retry on failure (but what if failing for out of memory?). + Retry by jumping back to the retry label. */ + jmpTarget(jmpRetryOrFail, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + if ((accessorDepthForPrimitiveMethod(methodObj)) >= 0) { + /* Given that following primitive state to the accessor depth is recursive, we're asking for + trouble if we run the fixup on the Smalltalk stack page. Run it on the full C stack instead. + This won't be a performance issue since primitive failure should be very rare. + Note that this cannot loop on failure because after the first retry the forwarders will have + been followed and so checkForAndFollowForwardedPrimitiveState will answer false on a + second failure. */ + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, FPReg, framePointerAddress()); + + /* #MoveCw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveCwR, ((usqInt)primitiveRoutine), TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, primitiveFunctionPointerAddress()); + genLoadNewMethod(); + + /* begin genLoadCStackPointersForPrimCall */ + if (cFramePointerInUse) { + genLoadCStackPointers(backEnd); + } + else { + genLoadCStackPointer(backEnd); + } + + /* begin genMarshallNArgs:arg:arg:arg:arg: */ + /* SubCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 32, RSP); + + /* WIN64 ABI always reserves shadow space on the stack for callee to save up to 4 register parameters */ + + /* begin CallAnyFixedRT: */ + if (is32BitSignedImmediate(backEnd, maximumDistanceFromCodeZone(((usqInt)checkForAndFollowForwardedPrimitiveState)))) { + callTargetSqInt = ((usqIntptr_t)checkForAndFollowForwardedPrimitiveState); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, callTargetSqInt); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* #CallFullRT: #CallFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(CallFull, ((usqIntptr_t)checkForAndFollowForwardedPrimitiveState)); + } + genLoadStackPointersForPrimCall(backEnd, ClassReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ABIResultReg); + skip = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, primFailCodeAddress()); + + /* Jump: */ + genoperand(Jump, ((sqInt)retry)); + jmpTarget(skip, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } + else { + if (((flags & PrimCallOnSmalltalkStackAlign2x) != 0)) { + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, stackPointerAddress(), TempReg); + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, BytesPerWord, TempReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, TempReg, SPReg); + } + } + + /* must reload SPReg to undo any alignment change, + Remember to restore the native stack pointer to point to the C stack, + otherwise the Smalltalk frames will get overwritten on an interrupt. */ + + /* If profiling, check for a sample now, in this case for failure. Avoid taking the sample. If the sample should + be taken, smash the stack limit and allow the subsequent post frame build stack check to do the sampling. */ + if (((flags & PrimCallCollectsProfileSamples) != 0)) { + /* begin genCheckForProfileTimerTick: */ + reg = RAX; + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, nextProfileTickAddress(), Arg1Reg); + gMovePerfCnt64RL(reg, (0)); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, reg); + skip = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* Invert the branch to jump if a sample should /not/ be taken. */ + (skip->opcode = inverseBranchFor((skip->opcode))); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, -1, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, stackLimitAddress()); + jmpTarget(skip, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } + + /* The LinkRegister now contains the return address either of the primitive call or of checkForAndFollowForwardedPrimitiveState. + It must be restored to the return address of the send invoking this primitive method. */ + + /* Finally remember to reload ReceiverResultReg if required. Even if + arguments have been pushed, the prolog sequence assumes it is live. */ + if (((ABICallerSavedRegisterMask & ((1U << ReceiverResultReg))) != 0)) { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, (methodOrBlockNumArgs + 1) * BytesPerWord, SPReg, ReceiverResultReg); + } + + /* continue to frame build... */ + return 0; +} + + +/* Compile one method cache probe in an OpenPIC's lookup of selector. + Answer the jump taken if the selector probe fails. + The class tag of the receiver must be in SendNumArgsReg. ClassReg and + TempReg are used as scratch registers. + On a hit, the offset of the entry is in ClassReg. */ + + /* SimpleStackBasedCogit>>#compileOpenPICMethodCacheProbeFor:withShift:baseRegOrNone: */ +static NoDbgRegParms AbstractInstruction * +compileOpenPICMethodCacheProbeForwithShiftbaseRegOrNone(sqInt selector, sqInt shift, sqInt baseRegOrNone) +{ + AbstractInstruction *jumpSelectorMiss; + sqInt offset; + + /* MoveR:R: */ + genoperandoperand(MoveRR, SendNumArgsReg, ClassReg); + maybeShiftClassTagRegisterForMethodCacheProbe(ClassReg); + annotateobjRef(genoperandoperand(XorCwR, selector, ClassReg), selector); + assert(shift <= (shiftForWord())); + + /* Need to shift the hash right by shift to form the probe, and then shift the probe left by shiftForWord to form the index. + So shift left by shiftForWord - shift and and with the shifted mask. */ + if (shift < (shiftForWord())) { + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - shift, ClassReg); + } + genoperandoperand(AndCqR, ((sqInt)((usqInt)(MethodCacheMask) << (shiftForWord()))), ClassReg); + if (baseRegOrNone == NoReg) { + offset = (methodCacheAddress()) + ((((usqInt)(MethodCacheSelector) << (shiftForWord())))); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, ClassReg, TempReg); + } + else { + /* AddR:R: */ + genoperandoperand(AddRR, baseRegOrNone, ClassReg); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, ((sqInt)((usqInt)(MethodCacheSelector) << (shiftForWord()))), ClassReg, TempReg); + } + annotateobjRef(genoperandoperand(CmpCwR, selector, TempReg), selector); + jumpSelectorMiss = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + if (baseRegOrNone == NoReg) { + offset = (methodCacheAddress()) + ((((usqInt)(MethodCacheClass) << (shiftForWord())))); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, ClassReg, TempReg); + } + else { + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, ((sqInt)((usqInt)(MethodCacheClass) << (shiftForWord()))), ClassReg, TempReg); + } + assert(!(0 /* (SendNumArgsReg = SPReg) */)); + genoperandoperand(CmpRR, SendNumArgsReg, TempReg); + return jumpSelectorMiss; +} + + +/* Compile the code for an open PIC. Perform a probe of the first-level + method lookup cache followed by a call of ceSendFromInLineCacheMiss: if + the probe fails. */ + + /* SimpleStackBasedCogit>>#compileOpenPIC:numArgs: */ +static NoDbgRegParms void +compileOpenPICnumArgs(sqInt selector, sqInt numArgs) +{ + sqInt cacheBaseReg; + AbstractInstruction *itsAHit; + AbstractInstruction *jumpBCMethod; + AbstractInstruction *jumpClassMiss; + AbstractInstruction *jumpSelectorMiss; + sqInt offset; + sqInt quickConstant; + sqInt reg; + + /* begin preenMethodLabel */ + /* setLabelOffset: */ + ((methodLabel->operands))[1] = 0; + compilePICAbort(numArgs); + entry = genGetClassTagOfintoscratchReg(ReceiverResultReg, SendNumArgsReg, TempReg); + cacheBaseReg = NoReg; + if (!(/* #isWithinMwOffsetRange: #addressIsInCodeZone: */ + ((((usqInt)(methodCacheAddress()))) >= codeBase) + && ((((usqInt)(methodCacheAddress()))) < limitAddress))) { + quickConstant = methodCacheAddress(); + reg = (cacheBaseReg = Extra0Reg); + + /* begin MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, quickConstant, reg); + } + + /* Do first of three probes. See CoInterpreter>>lookupInMethodCacheSel:classTag: */ + jumpSelectorMiss = compileOpenPICMethodCacheProbeForwithShiftbaseRegOrNone(selector, 0, cacheBaseReg); + jumpClassMiss = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Fetch the method. The interpret trampoline requires the bytecoded method in SendNumArgsReg */ + offset = (cacheBaseReg == NoReg + ? (methodCacheAddress()) + ((((usqInt)(MethodCacheMethod) << (shiftForWord())))) + : ((sqInt)((usqInt)(MethodCacheMethod) << (shiftForWord())))); + + /* begin MoveMw:r:R: */ + itsAHit = genoperandoperandoperand(MoveMwrR, offset, ClassReg, SendNumArgsReg); + + /* If the method is compiled jump to its unchecked entry-point, otherwise interpret it. */ + genLoadSlotsourceRegdestReg(HeaderIndex, SendNumArgsReg, ClassReg); + jumpBCMethod = genJumpImmediate(ClassReg); + jmpTarget(jumpBCMethod, picInterpretAbort); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, cmNoCheckEntryOffset, ClassReg); + + /* JumpR: */ + genoperand(JumpR, ClassReg); + + /* First probe missed. Do second of three probes. Shift hash right one and retry. */ + jmpTarget(jumpSelectorMiss, jmpTarget(jumpClassMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + jumpSelectorMiss = compileOpenPICMethodCacheProbeForwithShiftbaseRegOrNone(selector, 1, cacheBaseReg); + + /* JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)itsAHit)); + + /* Second probe missed. Do last probe. Shift hash right two and retry. */ + jmpTarget(jumpSelectorMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpSelectorMiss = compileOpenPICMethodCacheProbeForwithShiftbaseRegOrNone(selector, 2, cacheBaseReg); + + /* JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)itsAHit)); + + /* Last probe missed. Call ceSendFromInLineCacheMiss: to do the full lookup. */ + jmpTarget(jumpSelectorMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + if ((numRegArgs()) > 0) { + genPushRegisterArgsForNumArgsscratchReg(backEnd, numArgs, SendNumArgsReg); + } + genSmalltalkToCStackSwitch(1); + addDependent(methodLabel, annotateAbsolutePCRef(genoperandoperand(MoveCwR, ((sqInt)methodLabel), SendNumArgsReg))); + compileCallFornumArgsargargargargresultRegregsToSave(ceSendFromInLineCacheMiss, 1, SendNumArgsReg, null, null, null, NoReg, 0 /* emptyRegisterMask */); +} + + +/* Compile one method cache probe in a perform: primitive's lookup of + selector. Answer the jump taken if the selector probe fails. */ + + /* SimpleStackBasedCogit>>#compilePerformMethodCacheProbeFor:withShift:baseRegOrNone: */ +static NoDbgRegParms AbstractInstruction * +compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(sqInt selectorReg, sqInt shift, sqInt baseRegOrNone) +{ + AbstractInstruction *jumpSelectorMiss; + sqInt offset; + + /* MoveR:R: */ + genoperandoperand(MoveRR, SendNumArgsReg, ClassReg); + maybeShiftClassTagRegisterForMethodCacheProbe(ClassReg); + + /* XorR:R: */ + genoperandoperand(XorRR, selectorReg, ClassReg); + assert(shift <= (shiftForWord())); + + /* Need to shift the hash right by shift to form the probe, and then shift the probe left by shiftForWord to form the index. + So shift left by shiftForWord - shift and and with the shifted mask. */ + if (shift < (shiftForWord())) { + /* begin LogicalShiftLeftCq:R: */ + genoperandoperand(LogicalShiftLeftCqR, (shiftForWord()) - shift, ClassReg); + } + genoperandoperand(AndCqR, ((sqInt)((usqInt)(MethodCacheMask) << (shiftForWord()))), ClassReg); + if (baseRegOrNone == NoReg) { + offset = (methodCacheAddress()) + ((((usqInt)(MethodCacheSelector) << (shiftForWord())))); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, ClassReg, TempReg); + } + else { + /* AddR:R: */ + genoperandoperand(AddRR, baseRegOrNone, ClassReg); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, ((sqInt)((usqInt)(MethodCacheSelector) << (shiftForWord()))), ClassReg, TempReg); + } + assert(!((selectorReg == SPReg))); + genoperandoperand(CmpRR, selectorReg, TempReg); + jumpSelectorMiss = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + if (baseRegOrNone == NoReg) { + offset = (methodCacheAddress()) + ((((usqInt)(MethodCacheClass) << (shiftForWord())))); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, ClassReg, TempReg); + } + else { + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, ((sqInt)((usqInt)(MethodCacheClass) << (shiftForWord()))), ClassReg, TempReg); + } + assert(!(0 /* (SendNumArgsReg = SPReg) */)); + genoperandoperand(CmpRR, SendNumArgsReg, TempReg); + return jumpSelectorMiss; +} + + +/* Compile a primitive. If possible, performance-critical primitives will + be generated by their own routines (primitiveGenerator). Otherwise, + if there is a primitive at all, we call the C routine with the usual + stack-switching dance, test the primFailCode and then either return + on success or continue to the method body. */ + + /* SimpleStackBasedCogit>>#compilePrimitive */ +static sqInt +compilePrimitive(void) +{ + sqInt code; + sqInt flags; + AbstractInstruction *jumpNotSmallInteger; + sqInt opcodeIndexAtPrimitive; + PrimitiveDescriptor *primitiveDescriptor; + void (*primitiveRoutine)(void); + sqInt reg; + + flags = 0; + primitiveDescriptor = ((PrimitiveDescriptor *) 0); + if (!primitiveIndex) { + return 0; + } + + /* If a descriptor specifies an argument count (by numArgs >= 0) then it must match + for the generated code to be correct. For example for speed many primitives use + ResultReceiverReg instead of accessing the stack, so the receiver better be at + numArgs down the stack. Use the interpreter version if not. */ + if ((((primitiveDescriptor = primitiveGeneratorOrNil()))) + && ((((primitiveDescriptor->primitiveGenerator))) + && ((((primitiveDescriptor->primNumArgs)) < 0) + || (((primitiveDescriptor->primNumArgs)) == methodOrBlockNumArgs)))) { + /* Note opcodeIndex so that any arg load instructions + for unimplemented primitives can be discarded. */ + opcodeIndexAtPrimitive = opcodeIndex; + code = ((primitiveDescriptor->primitiveGenerator))(); + if ((code < 0) + && (code != UnimplementedPrimitive)) { + return code; + } + + /* Generator failed, so no point continuing... + If the primitive can never fail then there is nothing more that needs to be done. */ + if (code == UnfailingPrimitive) { + return 0; + } + + /* If the machine code version handles all cases the only reason to call the interpreter + primitive is to reap the primitive error code. Don't bother if it isn't used. */ + if ((code == CompletePrimitive) + && (!(/* methodUsesPrimitiveErrorCode:header: */ + ((primitiveIndexOfMethodheader(methodObj, methodHeader)) > 0) + && ((longStoreBytecodeForHeader(methodHeader)) == (fetchByteofObject((startPCOfMethodHeader(methodHeader)) + (sizeOfCallPrimitiveBytecode(methodHeader)), methodObj)))))) { + return 0; + } + + /* Discard any arg load code generated by the primitive generator. */ + if (code == UnimplementedPrimitive) { + opcodeIndex = opcodeIndexAtPrimitive; + } + } + primitiveRoutine = functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(methodObj, primitiveIndex, (&flags)); + if ((primitiveRoutine == 0) + || (primitiveRoutine == (((void (*)(void)) primitiveFail)))) { + return genFastPrimFail(); + } + + /* no primitive */ + if ((primitiveRoutine == (((void (*)(void)) primitiveHighResClock))) + && (methodOrBlockNumArgs == 0)) { + /* begin genPrimitiveHighResClock64 */ + reg = RAX; + assert(registerisNotInMask(reg, registerMaskForand(ReceiverResultReg, Arg1Reg))); + gMovePerfCnt64RL(reg, (0)); + gLogicalShiftRightCqRR((numSmallIntegerBits()) - 1, reg, Arg1Reg); + if (!(setsConditionCodesFor(lastOpcode(), JumpZero))) { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, Arg1Reg); + } + jumpNotSmallInteger = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genConvertIntegerInRegtoSmallIntegerInReg(reg, ReceiverResultReg); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + jmpTarget(jumpNotSmallInteger, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genInstantiate64BitPositiveIntegerValueintoscratchRegmayFail(reg, ReceiverResultReg, Arg1Reg, 0); + + /* genPrimReturn */ + if (methodOrBlockNumArgs <= (numRegArgs())) { + /* RetN: */ + genoperand(RetN, 0); + } + else { + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + } + code = UnfailingPrimitive; + if (code != UnimplementedPrimitive) { + return code; + } + } + if (((flags & PrimCallOnSmalltalkStack) != 0)) { + return compileOnStackExternalPrimitiveflags(primitiveRoutine, flags); + } + return compileInterpreterPrimitiveflags(primitiveRoutine, flags); +} + + /* SimpleStackBasedCogit>>#extendedPushBytecode */ +static sqInt +extendedPushBytecode(void) +{ + usqInt variableIndex; + usqInt variableType; + + variableType = (((usqInt)(byte1)) >> 6) & 3; + variableIndex = byte1 & 0x3F; + if (!variableType) { + return genPushReceiverVariable(variableIndex); + } + if (variableType == 1) { + return genPushTemporaryVariable(variableIndex); + } + if (variableType == 2) { + return genPushLiteralIndex(variableIndex); + } + return genPushLiteralVariable(variableIndex); +} + + /* SimpleStackBasedCogit>>#extendedStoreAndPopBytecode */ +static sqInt +extendedStoreAndPopBytecode(void) +{ + AbstractInstruction *abstractInstruction; + usqInt variableIndex; + usqInt variableType; + + variableType = (((usqInt)(byte1)) >> 6) & 3; + variableIndex = byte1 & 0x3F; + if (!variableType) { + return genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(1, variableIndex, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } + if (variableType == 1) { + genStorePopTemporaryVariable(1, variableIndex); + + /* needs a fake map entry if Immutability is ON... */ +# if IMMUTABILITY + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); +# endif // IMMUTABILITY + + return 0; + } + if (variableType == 3) { + return genStorePopLiteralVariableneedsStoreCheckneedsImmutabilityCheck(1, variableIndex, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } + return EncounteredUnknownBytecode; +} + + /* SimpleStackBasedCogit>>#extendedStoreBytecode */ +static sqInt +extendedStoreBytecode(void) +{ + AbstractInstruction *abstractInstruction; + usqInt variableIndex; + usqInt variableType; + + variableType = (((usqInt)(byte1)) >> 6) & 3; + variableIndex = byte1 & 0x3F; + if (!variableType) { + return genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(0, variableIndex, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } + if (variableType == 1) { + genStorePopTemporaryVariable(0, variableIndex); + + /* needs a fake map entry if Immutability is ON... */ +# if IMMUTABILITY + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); +# endif // IMMUTABILITY + + return 0; + } + if (variableType == 3) { + return genStorePopLiteralVariableneedsStoreCheckneedsImmutabilityCheck(0, variableIndex, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } + return EncounteredUnknownBytecode; +} + + /* SimpleStackBasedCogit>>#frameOffsetOfTemporary: */ +static NoDbgRegParms sqInt +frameOffsetOfTemporary(sqInt index) +{ + return /* frameOffsetOfTemporary:numArgs: */ + (index < methodOrBlockNumArgs + ? FoxCallerSavedIP + ((methodOrBlockNumArgs - index) * BytesPerWord) + : (FoxMFReceiver - BytesPerWord) + ((methodOrBlockNumArgs - index) * BytesPerWord)); +} + + +/* Implemented with SistaCogit only */ + + /* SimpleStackBasedCogit>>#genCallMappedInlinedPrimitive */ +static sqInt +genCallMappedInlinedPrimitive(void) +{ + return EncounteredUnknownBytecode; +} + + /* SimpleStackBasedCogit>>#genDoubleFailIfZeroArgRcvr:arg: */ +static NoDbgRegParms AbstractInstruction * +genDoubleFailIfZeroArgRcvrarg(int rcvrReg, int argReg) +{ + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, TempReg); + + /* ConvertR:Rd: */ + genoperandoperand(ConvertRRd, TempReg, DPFPReg2); + + /* CmpRd:Rd: */ + genoperandoperand(CmpRdRd, DPFPReg2, argReg); + return gJumpFPEqual(0); +} + + +/* Can use any of the first 32 literals for the selector and pass up to 7 + arguments. + */ + + /* SimpleStackBasedCogit>>#genExtendedSendBytecode */ +static sqInt +genExtendedSendBytecode(void) +{ + return genSendnumArgs(byte1 & 0x1F, ((usqInt)(byte1)) >> 5); +} + + /* SimpleStackBasedCogit>>#genExtendedSuperBytecode */ +static sqInt +genExtendedSuperBytecode(void) +{ + return genSendSupernumArgs(byte1 & 0x1F, ((usqInt)(byte1)) >> 5); +} + + +/* 244 11110100 i i i i i i i i Pop and Jump 0n False i i i i i i i i (+ + Extend B * 256, where Extend B >= 0) + */ + + /* SimpleStackBasedCogit>>#genExtJumpIfFalse */ +static sqInt +genExtJumpIfFalse(void) +{ + sqInt distance; + sqInt target; + + distance = byte1 + ((((usqInt)(extB) << 8))); + assert(distance == (v4LongForwardBranchDistance(generatorAt(byte0), bytecodePC, ((extA != 0 + ? 1 + : 0)) + ((extB != 0 + ? 1 + : 0)), methodObj))); + extB = 0; + numExtB = 0; + target = (distance + 2) + bytecodePC; + return genJumpIfto(falseObject(), target); +} + + +/* 243 11110011 i i i i i i i i Pop and Jump 0n True i i i i i i i i (+ + Extend B * 256, where Extend B >= 0) + */ + + /* SimpleStackBasedCogit>>#genExtJumpIfTrue */ +static sqInt +genExtJumpIfTrue(void) +{ + sqInt distance; + sqInt target; + + distance = byte1 + ((((usqInt)(extB) << 8))); + assert(distance == (v4LongForwardBranchDistance(generatorAt(byte0), bytecodePC, ((extA != 0 + ? 1 + : 0)) + ((extB != 0 + ? 1 + : 0)), methodObj))); + extB = 0; + numExtB = 0; + target = (distance + 2) + bytecodePC; + return genJumpIfto(trueObject(), target); +} + + +/* NewspeakV4: 221 11011101 Nop */ +/* SistaV1: 91 01011011' Nop */ + + /* SimpleStackBasedCogit>>#genExtNopBytecode */ +static sqInt +genExtNopBytecode(void) +{ + extA = (numExtB = (extB = 0)); + return 0; +} + + +/* SistaV1: 233 11101001 iiiiiiii Push Character #iiiiiiii (+ Extend A * + 256) + */ + + /* SimpleStackBasedCogit>>#genExtPushCharacterBytecode */ +static sqInt +genExtPushCharacterBytecode(void) +{ + sqInt value; + + value = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + return ssPushConstant(characterObjectOf(value)); +} + + +/* NewsqueakV4: 229 11100101 iiiiiiii Push Integer #iiiiiiii (+ Extend B * + 256, where bbbbbbbb = sddddddd, e.g. -32768 = i=0, a=0, s=1) + SistaV1: 232 11101000 iiiiiiii Push Integer #iiiiiiii (+ Extend B * 256, + where bbbbbbbb = sddddddd, e.g. -32768 = i=0, a=0, s=1) + */ + + /* SimpleStackBasedCogit>>#genExtPushIntegerBytecode */ +static sqInt +genExtPushIntegerBytecode(void) +{ + sqInt value; + + value = byte1 + ((((usqInt)(extB) << 8))); + extB = 0; + numExtB = 0; + return ssPushConstant((((usqInt)value << 3) | 1)); +} + + +/* 228 11100100 i i i i i i i i Push Literal #iiiiiiii (+ Extend A * 256) */ + + /* SimpleStackBasedCogit>>#genExtPushLiteralBytecode */ +static sqInt +genExtPushLiteralBytecode(void) +{ + sqInt index; + + index = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + return genPushLiteralIndex(index); +} + + +/* 227 11100011 i i i i i i i i Push Literal Variable #iiiiiiii (+ Extend A + * 256) + */ + + /* SimpleStackBasedCogit>>#genExtPushLiteralVariableBytecode */ +static sqInt +genExtPushLiteralVariableBytecode(void) +{ + sqInt index; + + index = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + return genPushLiteralVariable(index); +} + + +/* SistaV1: * 82 01010010 Push thisContext, (then Extend B = 1 => push + thisProcess) + */ + + /* SimpleStackBasedCogit>>#genExtPushPseudoVariable */ +static sqInt +genExtPushPseudoVariable(void) +{ + sqInt ext; + + ext = extB; + extB = 0; + numExtB = 0; + switch (ext) { + case 0: + return genPushActiveContextBytecode(); + + default: + return EncounteredUnknownBytecode; + } + return 0; +} + + +/* 226 11100010 i i i i i i i i Push Receiver Variable #iiiiiiii (+ Extend A + * 256) + */ + + /* SimpleStackBasedCogit>>#genExtPushReceiverVariableBytecode */ +static sqInt +genExtPushReceiverVariableBytecode(void) +{ + sqInt index; + + index = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + return ((mclassCouldBeContext()) + && (isReadMediatedContextInstVarIndex(index)) + ? genPushMaybeContextReceiverVariable(index) + : genPushReceiverVariable(index)); +} + + +/* 238 11101110 i i i i i j j j Send Literal Selector #iiiii (+ Extend A * + 32) with jjj (+ Extend B * 8) Arguments + */ + + /* SimpleStackBasedCogit>>#genExtSendBytecode */ +static sqInt +genExtSendBytecode(void) +{ + sqInt litIndex; + usqInt nArgs; + + litIndex = (((usqInt)(byte1)) >> 3) + ((((usqInt)(extA) << 5))); + extA = 0; + nArgs = (byte1 & 7) + ((((usqInt)(extB) << 3))); + extB = 0; + numExtB = 0; + return genSendnumArgs(litIndex, nArgs); +} + + +/* 239 11101111 i i i i i j j j Send To Superclass Literal Selector #iiiii + (+ Extend A * 32) with jjj (+ Extend B * 8) Arguments + */ + + /* SimpleStackBasedCogit>>#genExtSendSuperBytecode */ +static sqInt +genExtSendSuperBytecode(void) +{ + int isDirected; + sqInt litIndex; + usqInt nArgs; + + if ((isDirected = extB >= 64)) { + extB = extB & 0x3F; + } + litIndex = (((usqInt)(byte1)) >> 3) + ((((usqInt)(extA) << 5))); + extA = 0; + nArgs = (byte1 & 7) + ((((usqInt)(extB) << 3))); + extB = 0; + numExtB = 0; + return (isDirected + ? genSendDirectedSupernumArgs(litIndex, nArgs) + : genSendSupernumArgs(litIndex, nArgs)); +} + + +/* 236 11101100 i i i i i i i i Pop and Store Literal Variable #iiiiiiii (+ + Extend A * 256) + */ + + /* SimpleStackBasedCogit>>#genExtStoreAndPopLiteralVariableBytecode */ +static sqInt +genExtStoreAndPopLiteralVariableBytecode(void) +{ + sqInt index; + + index = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + return genStorePopLiteralVariableneedsStoreCheckneedsImmutabilityCheck(1, index, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); +} + + +/* 235 11101011 i i i i i i i i Pop and Store Receiver Variable #iiiiiii (+ + Extend A * 256) + */ + + /* SimpleStackBasedCogit>>#genExtStoreAndPopReceiverVariableBytecode */ +static sqInt +genExtStoreAndPopReceiverVariableBytecode(void) +{ + sqInt index; + + index = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + if ((mclassCouldBeContext()) + && (isWriteMediatedContextInstVarIndex(index))) { + return genStorePopMaybeContextReceiverVariableneedsStoreCheckneedsImmutabilityCheck(1, index, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } + else { + return genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(1, index, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } +} + + +/* 233 11101001 i i i i i i i i Store Literal Variable #iiiiiiii (+ Extend A + * 256) + */ + + /* SimpleStackBasedCogit>>#genExtStoreLiteralVariableBytecode */ +static sqInt +genExtStoreLiteralVariableBytecode(void) +{ + sqInt index; + + index = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + return genStorePopLiteralVariableneedsStoreCheckneedsImmutabilityCheck(0, index, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); +} + + +/* 232 11101000 i i i i i i i i Store Receiver Variable #iiiiiii (+ Extend A + * 256) + */ + + /* SimpleStackBasedCogit>>#genExtStoreReceiverVariableBytecode */ +static sqInt +genExtStoreReceiverVariableBytecode(void) +{ + sqInt index; + + index = byte1 + ((((usqInt)(extA) << 8))); + extA = 0; + if ((mclassCouldBeContext()) + && (isWriteMediatedContextInstVarIndex(index))) { + return genStorePopMaybeContextReceiverVariableneedsStoreCheckneedsImmutabilityCheck(0, index, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } + else { + return genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(0, index, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } +} + + +/* 242 11110010 i i i i i i i i Jump i i i i i i i i (+ Extend B * 256, + where bbbbbbbb = sddddddd, e.g. -32768 = i=0, a=0, s=1) + */ + + /* SimpleStackBasedCogit>>#genExtUnconditionalJump */ +static sqInt +genExtUnconditionalJump(void) +{ + AbstractInstruction *abstractInstruction; + sqInt distance; + sqInt target; + + distance = byte1 + ((((usqInt)(extB) << 8))); + assert(distance == (v4LongBranchDistance(generatorAt(byte0), bytecodePC, ((extA != 0 + ? 1 + : 0)) + ((extB != 0 + ? 1 + : 0)), methodObj))); + extB = 0; + numExtB = 0; + target = (distance + 2) + bytecodePC; + if (distance < 0) { + return genJumpBackTo(target); + } + genJumpTo(target); + + /* The bytecode must be mapped since it can be either forward or backward, and + backwards branches must be mapped. So if forward, we need to map. */ + abstractInstruction = lastOpcode(); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + return 0; +} + + /* SimpleStackBasedCogit>>#genFastPrimFail */ +static sqInt +genFastPrimFail(void) +{ + primitiveIndex = 0; + return UnfailingPrimitive; +} + + +/* Support for compileInterpreterPrimitive. Generate inline code + so as to record the primitive trace as fast as possible. If setNewMethod + as a side-effect write the method to newMethod. */ + + /* SimpleStackBasedCogit>>#genFastPrimTraceUsing:and:setNewMethod: */ +static NoDbgRegParms void +genFastPrimTraceUsingandsetNewMethod(sqInt r1, sqInt r2, sqInt setNewMethod) +{ + sqInt offset; + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, r2); + + /* #MoveAb:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAbR, primTraceLogIndexAddress(), r2); + + /* #LoadEffectiveAddressMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(LoadEffectiveAddressMwrR, 1, r2, r1); + + /* #MoveR:Ab: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAb, r1, primTraceLogIndexAddress()); + addDependent(methodLabel, annotateAbsolutePCRef(genoperandoperand(MoveCwR, ((sqInt)methodLabel), r1))); + offset = offsetof(CogMethod, methodObject); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, r1, TempReg); + + /* #MoveCw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveCwR, ((sqInt)(primTraceLogAddress())), r1); + + /* MoveR:Xwr:R: */ + genoperandoperandoperand(MoveRXwrR, TempReg, r2, r1); + if (setNewMethod) { + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, newMethodAddress()); + + /* begin maybeGenZeroLRPStopUsecs */ +# if LRPCheck + if (checkingLongRunningPrimitives()) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, longRunningPrimitiveStopUsecsAddress()); + } +# endif // LRPCheck + + } +} + + /* SimpleStackBasedCogit>>#genLoadNewMethod */ +static void +genLoadNewMethod(void) +{ + sqInt offset; + + addDependent(methodLabel, annotateAbsolutePCRef(genoperandoperand(MoveCwR, ((sqInt)methodLabel), ClassReg))); + offset = offsetof(CogMethod, methodObject); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, ClassReg, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, newMethodAddress()); +# if LRPCheck + if (checkingLongRunningPrimitives()) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, longRunningPrimitiveStopUsecsAddress()); + } +# endif // LRPCheck +} + + /* SimpleStackBasedCogit>>#genLongJumpIfFalse */ +static sqInt +genLongJumpIfFalse(void) +{ + sqInt distance; + sqInt target; + + distance = v3LongForwardBranchDistance(generatorAt(byte0), bytecodePC, 0, methodObj); + target = (distance + 2) + bytecodePC; + return genJumpIfto(falseObject(), target); +} + + /* SimpleStackBasedCogit>>#genLongJumpIfTrue */ +static sqInt +genLongJumpIfTrue(void) +{ + sqInt distance; + sqInt target; + + distance = v3LongForwardBranchDistance(generatorAt(byte0), bytecodePC, 0, methodObj); + target = (distance + 2) + bytecodePC; + return genJumpIfto(trueObject(), target); +} + + +/* 230 11100110 i i i i i i i i Push Temporary Variable #iiiiiiii */ + + /* SimpleStackBasedCogit>>#genLongPushTemporaryVariableBytecode */ +static sqInt +genLongPushTemporaryVariableBytecode(void) +{ + return genPushTemporaryVariable(byte1); +} + + +/* 237 11101101 i i i i i i i i Pop and Store Temporary Variable #iiiiiiii */ + + /* SimpleStackBasedCogit>>#genLongStoreAndPopTemporaryVariableBytecode */ +static sqInt +genLongStoreAndPopTemporaryVariableBytecode(void) +{ + return genStorePopTemporaryVariable(1, byte1); +} + + +/* 234 11101010 i i i i i i i i Store Temporary Variable #iiiiiiii */ + + /* SimpleStackBasedCogit>>#genLongStoreTemporaryVariableBytecode */ +static sqInt +genLongStoreTemporaryVariableBytecode(void) +{ + return genStorePopTemporaryVariable(0, byte1); +} + + /* SimpleStackBasedCogit>>#genLongUnconditionalBackwardJump */ +static sqInt +genLongUnconditionalBackwardJump(void) +{ + sqInt distance; + + distance = v3LongBranchDistance(generatorAt(byte0), bytecodePC, 0, methodObj); + assert(distance < 0); + return genJumpBackTo((distance + 2) + bytecodePC); +} + + /* SimpleStackBasedCogit>>#genLongUnconditionalForwardJump */ +static sqInt +genLongUnconditionalForwardJump(void) +{ + sqInt distance; + sqInt targetpc; + + distance = v3LongBranchDistance(generatorAt(byte0), bytecodePC, 0, methodObj); + assert(distance >= 0); + targetpc = (distance + 2) + bytecodePC; + return genJumpTo(targetpc); +} + + +/* Compile the code for a probe of the first-level method cache for a perform + primitive. The selector is assumed to be in Arg0Reg. Defer to + adjustArgumentsForPerform: to + adjust the arguments before the jump to the method. */ +/* N.B. Can't assume TempReg already contains the tag because a method can + of course be invoked via the unchecked entry-point, e.g. as does perform:. */ + + /* SimpleStackBasedCogit>>#genLookupForPerformNumArgs: */ +static NoDbgRegParms sqInt +genLookupForPerformNumArgs(sqInt numArgs) +{ + sqInt cacheBaseReg; + AbstractInstruction *itsAHit; + AbstractInstruction *jumpBadNumArgs; + AbstractInstruction *jumpClassMiss; + AbstractInstruction *jumpInterpret; + AbstractInstruction *jumpSelectorMiss; + sqInt offset; + sqInt quickConstant; + sqInt reg; + + genGetInlineCacheClassTagFromintoforEntry(ReceiverResultReg, SendNumArgsReg, 0); + cacheBaseReg = NoReg; + if (!(/* #isWithinMwOffsetRange: #addressIsInCodeZone: */ + ((((usqInt)(methodCacheAddress()))) >= codeBase) + && ((((usqInt)(methodCacheAddress()))) < limitAddress))) { + quickConstant = methodCacheAddress(); + reg = (cacheBaseReg = Extra0Reg); + + /* begin MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, quickConstant, reg); + } + + /* Do first of three probes. See CoInterpreter>>lookupInMethodCacheSel:classTag: */ + jumpSelectorMiss = compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(Arg0Reg, 0, cacheBaseReg); + jumpClassMiss = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Fetch the method, and check if it is cogged. */ + offset = (cacheBaseReg == NoReg + ? (methodCacheAddress()) + ((((usqInt)(MethodCacheMethod) << (shiftForWord())))) + : ((sqInt)((usqInt)(MethodCacheMethod) << (shiftForWord())))); + + /* begin MoveMw:r:R: */ + itsAHit = genoperandoperandoperand(MoveMwrR, offset, ClassReg, SendNumArgsReg); + + /* If the method is not compiled fall back on the interpreter primitive. */ + genLoadSlotsourceRegdestReg(HeaderIndex, SendNumArgsReg, ClassReg); + jumpInterpret = genJumpImmediate(ClassReg); + + /* check the argument count; if it's wrong fall back on the interpreter primitive. */ + + /* begin genLoadcmNumArgsOf:into: */ + /* MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, SendNumArgsReg); + offset = sizeof(((((CogMethod *) 0))->objectHeader)); + + /* begin MoveMb:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMbrR, offset, ClassReg, SendNumArgsReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, numArgs - 1, SendNumArgsReg); + jumpBadNumArgs = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Adjust arguments and jump to the method's unchecked entry-point. */ + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, cmNoCheckEntryOffset, ClassReg); + adjustArgumentsForPerform(numArgs); + + /* JumpR: */ + genoperand(JumpR, ClassReg); + + /* First probe missed. Do second of three probes. Shift hash right one and retry. */ + jmpTarget(jumpSelectorMiss, jmpTarget(jumpClassMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + jumpSelectorMiss = compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(Arg0Reg, 1, cacheBaseReg); + + /* JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)itsAHit)); + + /* Second probe missed. Do last probe. Shift hash right two and retry. */ + jmpTarget(jumpSelectorMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpSelectorMiss = compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(Arg0Reg, 2, cacheBaseReg); + + /* JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)itsAHit)); + + /* Last probe missed/not jitted/bad num args. Caller will generate the call to fall back on the interpreter primitive. */ + jmpTarget(jumpSelectorMiss, jmpTarget(jumpInterpret, jmpTarget(jumpBadNumArgs, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + return 0; +} + + /* SimpleStackBasedCogit>>#genMustBeBooleanTrampolineFor:called: */ +static NoDbgRegParms usqInt +genMustBeBooleanTrampolineForcalled(sqInt boolean, char *trampolineName) +{ + zeroOpcodeIndex(); + + /* If the objectRepresentation does want true & false to be mobile then we need to record these addresses. */ + assert(!(shouldAnnotateObjectReference(boolean))); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, boolean, TempReg); + return genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceSendMustBeBoolean, trampolineName, 1, TempReg, null, null, null, 0 /* emptyRegisterMask */, 1, NoReg, 1); +} + + +/* Implement 28-bit hashMultiply for SmallInteger and LargePositiveInteger + receivers. + */ + + /* SimpleStackBasedCogit>>#genPrimitiveHashMultiply */ +static sqInt +genPrimitiveHashMultiply(void) +{ + AbstractInstruction *jmpFailImm; + AbstractInstruction *jmpFailNotPositiveLargeInt; + + if (mclassIsSmallInteger()) { + genConvertSmallIntegerToIntegerInReg(ReceiverResultReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, HashMultiplyConstant, TempReg); + + /* MulR:R: */ + genMulRR(backEnd, TempReg, ReceiverResultReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, HashMultiplyMask, ReceiverResultReg); + genConvertIntegerToSmallIntegerInReg(ReceiverResultReg); + + /* RetN: */ + genoperand(RetN, 0); + return CompletePrimitive; + } + + /* This check is necessary. LargeNegativeInteger is a subclass of LargePositiveInteger in Squeak, Pharo, Cuis, et al. */ + jmpFailImm = genJumpImmediate(ReceiverResultReg); + genGetCompactClassIndexNonImmOfinto(ReceiverResultReg, ClassReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ClassLargePositiveIntegerCompactIndex, ClassReg); + jmpFailNotPositiveLargeInt = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genLoadSlotsourceRegdestReg(0, ReceiverResultReg, ReceiverResultReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, HashMultiplyConstant, TempReg); + + /* MulR:R: */ + genMulRR(backEnd, TempReg, ReceiverResultReg); + + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, HashMultiplyMask, ReceiverResultReg); + genConvertIntegerToSmallIntegerInReg(ReceiverResultReg); + + /* RetN: */ + genoperand(RetN, 0); + jmpTarget(jmpFailImm, jmpTarget(jmpFailNotPositiveLargeInt, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return CompletePrimitive; +} + + +/* Generate the substitute return code for an external or FFI primitive call. + On success simply return, extracting numArgs from newMethod. + On primitive failure call ceActivateFailingPrimitiveMethod: newMethod. */ + + /* SimpleStackBasedCogit>>#genPrimReturnEnterCogCodeEnilopmart: */ +static NoDbgRegParms void +genPrimReturnEnterCogCodeEnilopmart(sqInt profiling) +{ + AbstractInstruction *abstractInstruction; + sqInt callTargetSqInt; + AbstractInstruction *continuePostSample; + AbstractInstruction *jmpFail; + AbstractInstruction *jmpSample; + int newMethodReg; + sqInt reg; + + continuePostSample = ((AbstractInstruction *) 0); + jmpSample = ((AbstractInstruction *) 0); + zeroOpcodeIndex(); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, varBaseAddress, VarBaseReg); + if (profiling) { + /* begin genCheckForProfileTimerTick: */ + reg = RAX; + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, nextProfileTickAddress(), Arg1Reg); + gMovePerfCnt64RL(reg, (0)); + + /* begin CmpR:R: */ + assert(!(0 /* (Arg1Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg1Reg, reg); + jmpSample = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + continuePostSample = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + } + + /* Test primitive failure */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, primFailCodeAddress(), TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, TempReg); + jmpFail = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Switch back to the Smalltalk stack. Stack better be in either of these two states: + success: stackPointer -> result (was receiver) + arg1 + ... + argN + return pc + failure: receiver + arg1 + ... + stackPointer -> argN + return pc + We push the instructionPointer to reestablish the return pc in the success case, + but leave it to ceActivateFailingPrimitiveMethod: to do so in the failure case. */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, instructionPointerAddress(), ClassReg); + genLoadStackPointers(backEnd); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, SPReg, ReceiverResultReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, ClassReg, 0, SPReg); + + /* RetN: */ + genoperand(RetN, 0); + jmpTarget(jmpFail, genoperandoperand(MoveAwR, newMethodAddress(), SendNumArgsReg)); + + /* Reload sp with CStackPointer; easier than popping args of checkProfileTick. */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, cStackPointerAddress(), SPReg); + compileCallFornumArgsargargargargresultRegregsToSave(ceActivateFailingPrimitiveMethod, 1, SendNumArgsReg, null, null, null, NoReg, 0 /* emptyRegisterMask */); + + /* On Spur ceActivateFailingPrimitiveMethod: may retry the primitive and return if successful. + So continue by returning to the caller. + Switch back to the Smalltalk stack. Stack should be in this state: + success: stackPointer -> result (was receiver) + arg1 + ... + argN + return pc + We can push the instructionPointer or load it into the LinkRegister to reestablish the return pc */ + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, instructionPointerAddress(), ClassReg); + genLoadStackPointers(backEnd); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord, SPReg, ReceiverResultReg); + + /* PushR: */ + genoperand(PushR, ClassReg); + + /* RetN: */ + genoperand(RetN, BytesPerWord); + if (profiling) { + jmpTarget(jmpSample, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* Test the register(s) containing nextProfileTick's value and jump to continue if not + greater than zero. See genCheckForProfileTimerTick: for the register assignments. */ + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, Arg1Reg); + + /* JumpLessOrEqual: */ + genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)continuePostSample)); + newMethodReg = CArg0Reg; + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, newMethodAddress(), newMethodReg); + + /* begin genMarshallNArgs:arg:arg:arg:arg: */ + /* SubCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 32, RSP); + + /* WIN64 ABI always reserves shadow space on the stack for callee to save up to 4 register parameters */ + + /* begin CallAnyFixedRT: */ + if (is32BitSignedImmediate(backEnd, maximumDistanceFromCodeZone(((usqInt)ceTakeProfileSample)))) { + callTargetSqInt = ((usqIntptr_t)ceTakeProfileSample); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, callTargetSqInt); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* #CallFullRT: #CallFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(CallFull, ((usqIntptr_t)ceTakeProfileSample)); + } + + /* begin genRemoveNArgsFromStack: */ + /* AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, 32, RSP); + + /* Jump: */ + genoperand(Jump, ((sqInt)continuePostSample)); + } +} + + /* SimpleStackBasedCogit>>#genPushConstantFalseBytecode */ +static sqInt +genPushConstantFalseBytecode(void) +{ + return ssPushConstant(falseObject()); +} + + /* SimpleStackBasedCogit>>#genPushConstantNilBytecode */ +static sqInt +genPushConstantNilBytecode(void) +{ + return ssPushConstant(nilObject()); +} + + +/* 79 01001111 Push 1 */ + + /* SimpleStackBasedCogit>>#genPushConstantOneBytecode */ +static sqInt +genPushConstantOneBytecode(void) +{ + return ssPushConstant((((usqInt)1 << 3) | 1)); +} + + /* SimpleStackBasedCogit>>#genPushConstantTrueBytecode */ +static sqInt +genPushConstantTrueBytecode(void) +{ + return ssPushConstant(trueObject()); +} + + +/* 78 01001110 Push 0 */ + + /* SimpleStackBasedCogit>>#genPushConstantZeroBytecode */ +static sqInt +genPushConstantZeroBytecode(void) +{ + return ssPushConstant((((usqInt)0 << 3) | 1)); +} + + /* SimpleStackBasedCogit>>#genPushLiteralConstantBytecode */ +static sqInt +genPushLiteralConstantBytecode(void) +{ + return genPushLiteralIndex(byte0 & 0x1F); +} + + +/* 16-31 0001 i i i i Push Literal Variable #iiii */ + + /* SimpleStackBasedCogit>>#genPushLiteralVariable16CasesBytecode */ +static sqInt +genPushLiteralVariable16CasesBytecode(void) +{ + return genPushLiteralVariable(byte0 & 15); +} + + /* SimpleStackBasedCogit>>#genPushLiteralVariableBytecode */ +static sqInt +genPushLiteralVariableBytecode(void) +{ + return genPushLiteralVariable(byte0 & 0x1F); +} + + /* SimpleStackBasedCogit>>#genPushQuickIntegerConstantBytecode */ +static sqInt +genPushQuickIntegerConstantBytecode(void) +{ + return ssPushConstant((((usqInt)(byte0 - 117) << 3) | 1)); +} + + /* SimpleStackBasedCogit>>#genPushReceiverVariableBytecode */ +static sqInt +genPushReceiverVariableBytecode(void) +{ + return genPushReceiverVariable(byte0 & 15); +} + + /* SimpleStackBasedCogit>>#genPushTemporaryVariableBytecode */ +static sqInt +genPushTemporaryVariableBytecode(void) +{ + return genPushTemporaryVariable(byte0 & 15); +} + + +/* because selected by CoInterpreter>>quickPrimitiveGeneratorFor: */ + + /* SimpleStackBasedCogit>>#genQuickReturnConst */ +sqInt +genQuickReturnConst(void) +{ + sqInt constant; + + constant = quickPrimitiveConstantFor(primitiveIndex); + + /* genMoveConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(constant)) + && ((oopisGreaterThan(constant, classTableRootObj())) + || (oopisLessThan(constant, nilObject())))) { + annotateobjRef(genoperandoperand(MoveCwR, constant, ReceiverResultReg), constant); + } + else { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, constant, ReceiverResultReg); + } + genUpArrowReturn(); + return UnfailingPrimitive; +} + + +/* because selected by CoInterpreter>>quickPrimitiveGeneratorFor: */ + + /* SimpleStackBasedCogit>>#genQuickReturnInstVar */ +sqInt +genQuickReturnInstVar(void) +{ + sqInt index; + + index = quickPrimitiveInstVarIndexFor(primitiveIndex); + genLoadSlotsourceRegdestReg(index, ReceiverResultReg, ReceiverResultReg); + genUpArrowReturn(); + return UnfailingPrimitive; +} + + +/* because selected by CoInterpreter>>quickPrimitiveGeneratorFor: */ + + /* SimpleStackBasedCogit>>#genQuickReturnSelf */ +sqInt +genQuickReturnSelf(void) +{ + genUpArrowReturn(); + return UnfailingPrimitive; +} + + /* SimpleStackBasedCogit>>#genReturnFalse */ +static sqInt +genReturnFalse(void) +{ + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, falseObject(), ReceiverResultReg); + return genUpArrowReturn(); +} + + /* SimpleStackBasedCogit>>#genReturnNil */ +static sqInt +genReturnNil(void) +{ + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, nilObject(), ReceiverResultReg); + return genUpArrowReturn(); +} + + /* SimpleStackBasedCogit>>#genReturnNilFromBlock */ +static sqInt +genReturnNilFromBlock(void) +{ + assert(inBlock > 0); + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, nilObject(), ReceiverResultReg); + return genBlockReturn(); +} + + /* SimpleStackBasedCogit>>#genReturnTrue */ +static sqInt +genReturnTrue(void) +{ + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, trueObject(), ReceiverResultReg); + return genUpArrowReturn(); +} + + +/* Can use any of the first 64 literals for the selector and pass up to 3 + arguments. + */ + + /* SimpleStackBasedCogit>>#genSecondExtendedSendBytecode */ +static sqInt +genSecondExtendedSendBytecode(void) +{ + return genSendnumArgs(byte1 & 0x3F, ((usqInt)(byte1)) >> 6); +} + + /* SimpleStackBasedCogit>>#genSendLiteralSelector0ArgsBytecode */ +static sqInt +genSendLiteralSelector0ArgsBytecode(void) +{ + return genSendnumArgs(byte0 & 15, 0); +} + + /* SimpleStackBasedCogit>>#genSendLiteralSelector1ArgBytecode */ +static sqInt +genSendLiteralSelector1ArgBytecode(void) +{ + return genSendnumArgs(byte0 & 15, 1); +} + + /* SimpleStackBasedCogit>>#genSendLiteralSelector2ArgsBytecode */ +static sqInt +genSendLiteralSelector2ArgsBytecode(void) +{ + return genSendnumArgs(byte0 & 15, 2); +} + + /* SimpleStackBasedCogit>>#genShortJumpIfFalse */ +static sqInt +genShortJumpIfFalse(void) +{ + sqInt distance; + sqInt target; + + distance = v3ShortForwardBranchDistance(generatorAt(byte0), bytecodePC, 0, methodObj); + target = (distance + 1) + bytecodePC; + return genJumpIfto(falseObject(), target); +} + + /* SimpleStackBasedCogit>>#genShortJumpIfTrue */ +static sqInt +genShortJumpIfTrue(void) +{ + sqInt distance; + sqInt target; + + distance = v3ShortForwardBranchDistance(generatorAt(byte0), bytecodePC, 0, methodObj); + target = (distance + 1) + bytecodePC; + return genJumpIfto(trueObject(), target); +} + + /* SimpleStackBasedCogit>>#genShortUnconditionalJump */ +static sqInt +genShortUnconditionalJump(void) +{ + sqInt distance; + sqInt target; + + distance = v3ShortForwardBranchDistance(generatorAt(byte0), bytecodePC, 0, methodObj); + target = (distance + 1) + bytecodePC; + return genJumpTo(target); +} + + /* SimpleStackBasedCogit>>#genSpecialSelectorEqualsEquals */ +static sqInt +genSpecialSelectorEqualsEquals(void) +{ + return genInlinedIdenticalOrNotIf(0); +} + + /* SimpleStackBasedCogit>>#genSpecialSelectorNotEqualsEquals */ +static sqInt +genSpecialSelectorNotEqualsEquals(void) +{ + return genInlinedIdenticalOrNotIf(1); +} + + /* SimpleStackBasedCogit>>#genSpecialSelectorSend */ +static sqInt +genSpecialSelectorSend(void) +{ + sqInt index; + sqInt numArgs; + + index = byte0 - (/* firstSpecialSelectorBytecodeOffset */ + (bytecodeSetOffset == 0x100 + ? AltFirstSpecialSelector + 0x100 + : FirstSpecialSelector)); + numArgs = specialSelectorNumArgs(index); + return genSendnumArgs((-index) - 1, numArgs); +} + + /* SimpleStackBasedCogit>>#genStoreAndPopReceiverVariableBytecode */ +static sqInt +genStoreAndPopReceiverVariableBytecode(void) +{ + return genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(1, byte0 & 7, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); +} + + /* SimpleStackBasedCogit>>#genStoreAndPopRemoteTempLongBytecode */ +static sqInt +genStoreAndPopRemoteTempLongBytecode(void) +{ + return genStorePopRemoteTempAtneedsStoreCheck(1, byte1, byte2, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject())))))); +} + + /* SimpleStackBasedCogit>>#genStoreAndPopTemporaryVariableBytecode */ +static sqInt +genStoreAndPopTemporaryVariableBytecode(void) +{ + return genStorePopTemporaryVariable(1, byte0 & 7); +} + + /* SimpleStackBasedCogit>>#genStoreRemoteTempLongBytecode */ +static sqInt +genStoreRemoteTempLongBytecode(void) +{ + return genStorePopRemoteTempAtneedsStoreCheck(0, byte1, byte2, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject())))))); +} + + +/* Test if nextProfileTick is non-zero and if so call ceTakeProfileSample on + the C stack and jump to continueLabel. If nextProfileTick is not greater + than zero + jump to continueLabel without calling. */ +/* Test the register(s) containing nextProfileTick's value and jump to + continue if not + greater than zero. See genCheckForProfileTimerTick: for the register + assignments. + */ + + /* SimpleStackBasedCogit>>#genTakeProfileSample: */ +static NoDbgRegParms void +genTakeProfileSample(AbstractInstruction *continueLabel) +{ + AbstractInstruction *abstractInstruction; + sqInt callTargetSqInt; + sqInt delta; + sqInt numRegsPushed; + usqInt regMaskCopy; + sqInt wordsPushedModAlignment; + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, Arg1Reg); + + /* JumpLessOrEqual: */ + genConditionalBranchoperand(JumpLessOrEqual, ((sqInt)continueLabel)); + + /* in 32-bits if the top 32-bits is negative the value is negative */ + + /* begin genLoadCStackPointersForPrimCall */ + if (cFramePointerInUse) { + genLoadCStackPointers(backEnd); + } + else { + genLoadCStackPointer(backEnd); + } + + /* begin genAlignCStackGivenRegistersToBeSaved:numArgs:wordAlignment: */ + regMaskCopy = ((usqInt)0); + numRegsPushed = 0; + while (regMaskCopy != 0) { + numRegsPushed += regMaskCopy & 1; + regMaskCopy = ((regMaskCopy) >> 1); + } + if ((numRegsPushed == 0)) { + goto l1; + } + wordsPushedModAlignment = (numRegsPushed + ((((1 - (numIntRegArgs(backEnd))) < 0) ? 0 : (1 - (numIntRegArgs(backEnd)))))) % (cStackAlignment / BytesPerWord); + if (wordsPushedModAlignment) { + delta = (cStackAlignment / BytesPerWord) - wordsPushedModAlignment; + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, delta * BytesPerWord, SPReg); + } + /* end genAlignCStackGivenRegistersToBeSaved:numArgs:wordAlignment: */ +l1: + + /* begin genMarshallNArgs:arg:arg:arg:arg: */ + /* SubCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, 32, RSP); + + /* WIN64 ABI always reserves shadow space on the stack for callee to save up to 4 register parameters */ + + /* begin CallAnyFixedRT: */ + if (is32BitSignedImmediate(backEnd, maximumDistanceFromCodeZone(((usqInt)ceTakeProfileSample)))) { + callTargetSqInt = ((usqIntptr_t)ceTakeProfileSample); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, callTargetSqInt); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* #CallFullRT: #CallFull: #gen:literal: #checkLiteral:forInstruction: */ + genoperand(CallFull, ((usqIntptr_t)ceTakeProfileSample)); + } + + /* no need to remove stacked args since the stack pointers are immediately reloaded with the Smalltalk stack pointers. */ + genLoadStackPointersForPrimCall(backEnd, ClassReg); + + /* Jump: */ + genoperand(Jump, ((sqInt)continueLabel)); +} + + +/* SistaV1: * 217 Trap */ + + /* SimpleStackBasedCogit>>#genUnconditionalTrapBytecode */ +static sqInt +genUnconditionalTrapBytecode(void) +{ + return EncounteredUnknownBytecode; +} + + +/* Answer the Cogit's ISA as a string */ + + /* SimpleStackBasedCogit>>#isaName */ +#if VMInvestigations +char * +isaName(void) +{ + return ISA; +} +#endif /* VMInvestigations */ + + +/* Collect the branch and send data for cogMethod, storing it into arrayObj. */ + + /* SimpleStackBasedCogit>>#mapPCDataFor:into: */ +sqInt +mapPCDataForinto(CogMethod *cogMethod, sqInt arrayObj) +{ + sqInt aMethodObj; + sqInt annotation; + sqInt bcpc; + sqInt bsOffset; + sqInt byte; + CogBlockMethod *cogMethod1; + BytecodeDescriptor *descriptor; + sqInt distance; + sqInt endbcpc; + usqInt errCode; + CogMethod *homeMethod; + sqInt isBackwardBranch; + sqInt isInBlock; + sqInt latestContinuation; + sqInt map; + sqInt mapByte; + usqInt mcpc; + sqInt nExts; + sqInt nextBcpc; + sqInt result; + sqInt startbcpc; + sqInt targetPC; + + introspectionDataIndex = 0; + introspectionData = arrayObj; + if (!((cogMethod->stackCheckOffset))) { + assert(introspectionDataIndex == 0); + if ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) { + storePointerUncheckedofObjectwithValue(0, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(1, introspectionData, (((usqInt)cbNoSwitchEntryOffset << 3) | 1)); + storePointerUncheckedofObjectwithValue(2, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(3, introspectionData, (((usqInt)cbEntryOffset << 3) | 1)); + } + else { + storePointerUncheckedofObjectwithValue(0, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(1, introspectionData, (((usqInt)cmEntryOffset << 3) | 1)); + storePointerUncheckedofObjectwithValue(2, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(3, introspectionData, (((usqInt)cmNoCheckEntryOffset << 3) | 1)); + } + return 4; + } + cogMethod1 = ((CogBlockMethod *) cogMethod); + startbcpc = startPCOfMethod((cogMethod->methodObject)); + + /* begin mapFor:bcpc:performUntil:arg: */ + descriptor = ((BytecodeDescriptor *) 0); + latestContinuation = 0; + mapByte = 0; + nextBcpc = 0; + assert(((cogMethod1->stackCheckOffset)) > 0); + mcpc = (((usqInt)cogMethod1)) + ((cogMethod1->stackCheckOffset)); + + /* The stack check maps to the start of the first bytecode, + the first bytecode being effectively after frame build. */ + result = pcDataForAnnotationMcpcBcpcMethod(null, 0 + ((((usqInt)(HasBytecodePC) << 1))), ((char *) mcpc), startbcpc, ((void *)cogMethod)); + if (result) { + errCode = result; + goto l1; + } + bcpc = startbcpc; + + /* In both CMMethod and CMBlock cases find the start of the map and + skip forward to the bytecode pc map entry for the stack check. */ + if (((cogMethod1->cmType)) >= CMMethod) { + isInBlock = (cogMethod1->cpicHasMNUCaseOrCMIsFullBlock); + homeMethod = ((CogMethod *) cogMethod1); + assert(startbcpc == (startPCOfMethodHeader((homeMethod->methodHeader)))); + map = ((((usqInt)homeMethod)) + ((homeMethod->blockSize))) - 1; + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert((annotation == IsAbsPCReference) + || ((annotation == IsObjectReference) + || ((annotation == IsRelativeCall) + || (annotation == IsDisplacementX2N)))); + latestContinuation = startbcpc; + aMethodObj = (homeMethod->methodObject); + endbcpc = (numBytesOf(aMethodObj)) - 1; + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + + /* If the method has a primitive, skip it and the error code store, if any; + Logically. these come before the stack check and so must be ignored. */ + bcpc += deltaToSkipPrimAndErrorStoreInheader(aMethodObj, (homeMethod->methodHeader)); + } + else { + isInBlock = 1; + assert(bcpc == ((cogMethod1->startpc))); + homeMethod = cmHomeMethod(cogMethod1); + map = findMapLocationForMcpcinMethod((((usqInt)cogMethod1)) + (sizeof(CogBlockMethod)), homeMethod); + assert(map != 0); + annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift; + assert(((((usqInt)(annotation)) >> AnnotationShift) == HasBytecodePC) + || ((((usqInt)(annotation)) >> AnnotationShift) == IsDisplacementX2N)); + + /* fiducial */ + while (((annotation = ((usqInt)((byteAt((void *)(map))))) >> AnnotationShift)) != HasBytecodePC) { + map -= 1; + } + + /* skip fiducial; i.e. the map entry for the pc immediately following the method header. */ + map -= 1; + aMethodObj = (homeMethod->methodObject); + bcpc = startbcpc - (/* blockCreationBytecodeSizeForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? AltBlockCreationBytecodeSize + : BlockCreationBytecodeSize)); + bsOffset = /* bytecodeSetOffsetForHeader: */ + (headerIndicatesAlternateBytecodeSet((homeMethod->methodHeader)) + ? 0x100 + : 0); + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + endbcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, -1, aMethodObj) + : 0)); + bcpc = startbcpc; + } + nExts = 0; + enumeratingCogMethod = homeMethod; + + /* Now skip up through the bytecode pc map entry for the stack check. */ + while ((((usqInt)((byteAt((void *)(map))))) >> AnnotationShift) != HasBytecodePC) { + map -= 1; + } + map -= 1; + while (((mapByte = byteAt((void *)(map)))) != MapEnd) { + if (mapByte >= FirstAnnotation) { + annotation = ((usqInt)(mapByte)) >> AnnotationShift; + mcpc += (mapByte & DisplacementMask); + if (annotation >= HasBytecodePC) { + if ((annotation == IsSendCall) + && ((((usqInt)(((mapByte = byteAt((void *)(map - 1)))))) >> AnnotationShift) == IsAnnotationExtension)) { + annotation += mapByte & DisplacementMask; + map -= 1; + } + while (1) { + byte = (fetchByteofObject(bcpc, aMethodObj)) + bsOffset; + descriptor = generatorAt(byte); + if (isInBlock) { + if (bcpc >= endbcpc) { + errCode = 0; + goto l1; + } + } + else { + if (((descriptor->isReturn)) + && (bcpc >= latestContinuation)) { + errCode = 0; + goto l1; + } + if ((isBranch(descriptor)) + || ((descriptor->isBlockCreation))) { + /* begin latestContinuationPCFor:at:exts:in: */ + distance = ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj); + targetPC = (bcpc + ((descriptor->numBytes))) + (((distance < 0) ? 0 : distance)); + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + } + } + nextBcpc = (bcpc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj) + : 0)); + if (((descriptor->isMapped)) + || (isInBlock + && ((descriptor->isMappedInBlock)))) break; + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + isBackwardBranch = (isBranch(descriptor)) + && ((/* begin isBackwardBranch:at:exts:in: */ + assert(((descriptor->spanFunction))), + (((descriptor->spanFunction))(descriptor, bcpc, nExts, aMethodObj)) < 0)); + result = pcDataForAnnotationMcpcBcpcMethod(descriptor, (isBackwardBranch + ? ((((usqInt)(annotation) << 1))) + 1 + : ((sqInt)((usqInt)(annotation) << 1))), ((char *) mcpc), (isBackwardBranch + ? bcpc - (2 * nExts) + : bcpc), ((void *)cogMethod)); + if (result) { + errCode = result; + goto l1; + } + bcpc = nextBcpc; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + } + else { + assert(((((usqInt)(mapByte)) >> AnnotationShift) == IsDisplacementX2N) + || ((((usqInt)(mapByte)) >> AnnotationShift) == IsAnnotationExtension)); + if (mapByte < ((((usqInt)(IsAnnotationExtension) << AnnotationShift)))) { + mcpc += ((((usqInt)((mapByte - DisplacementX2N)) << AnnotationShift))); + } + } + map -= 1; + } + + /* defensive; we exit on bcpc */ + errCode = 0; + /* end mapFor:bcpc:performUntil:arg: */ +l1: + if (errCode) { + assert(errCode == PrimErrNoMemory); + return -1; + } + if ((cogMethod->blockEntryOffset)) { + errCode = blockDispatchTargetsForperformarg(cogMethod, pcDataForBlockEntryMethod, ((sqInt)cogMethod)); + if (errCode) { + assert(errCode == PrimErrNoMemory); + return -1; + } + } + return introspectionDataIndex; +} + + /* SimpleStackBasedCogit>>#numSpecialSelectors */ +static sqInt +numSpecialSelectors(void) +{ + return (bytecodeSetOffset == 0x100 + ? AltNumSpecialSelectors + : NumSpecialSelectors); +} + + +/* Collect the branch and send data for the block method starting at + blockEntryMcpc, storing it into picData. + */ + + /* SimpleStackBasedCogit>>#pcDataForBlockEntry:Method: */ +static NoDbgRegParms usqInt +pcDataForBlockEntryMethod(sqInt blockEntryMcpc, sqInt cogMethod) +{ + storePointerUncheckedofObjectwithValue(introspectionDataIndex, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 1, introspectionData, (((usqInt)(blockEntryMcpc - blockNoContextSwitchOffset) << 3) | 1)); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 2, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 3, introspectionData, (((usqInt)blockEntryMcpc << 3) | 1)); + introspectionDataIndex += 4; + return 0; +} + + /* SimpleStackBasedCogit>>#pcDataFor:Annotation:Mcpc:Bcpc:Method: */ +static NoDbgRegParms sqInt +pcDataForAnnotationMcpcBcpcMethod(BytecodeDescriptor *descriptor, sqInt isBackwardBranchAndAnnotation, char *mcpc, sqInt bcpc, void *cogMethodArg) +{ + sqInt actualBcpc; + sqInt actualMcpc; + + if (!descriptor) { + assert(introspectionDataIndex == 0); + if (((((CogMethod *) cogMethodArg))->cpicHasMNUCaseOrCMIsFullBlock)) { + storePointerUncheckedofObjectwithValue(introspectionDataIndex, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 1, introspectionData, (((usqInt)cbNoSwitchEntryOffset << 3) | 1)); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 2, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 3, introspectionData, (((usqInt)cbEntryOffset << 3) | 1)); + } + else { + storePointerUncheckedofObjectwithValue(introspectionDataIndex, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 1, introspectionData, (((usqInt)cmEntryOffset << 3) | 1)); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 2, introspectionData, nilObject()); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 3, introspectionData, (((usqInt)cmNoCheckEntryOffset << 3) | 1)); + } + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 4, introspectionData, (((usqInt)(bcpc + 1) << 3) | 1)); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 5, introspectionData, (((usqInt)(((((CogMethod *) cogMethodArg))->stackCheckOffset)) << 3) | 1)); + introspectionDataIndex += 6; + return 0; + } + + /* this is the stackCheck offset */ + if ((((usqInt)(isBackwardBranchAndAnnotation)) >> 1) >= HasBytecodePC) { + actualBcpc = (((isBackwardBranchAndAnnotation & 1) != 0) + ? bcpc + 1 + : (bcpc + ((descriptor->numBytes))) + 1); + actualMcpc = (((usqInt)mcpc)) - (((usqInt)cogMethodArg)); + storePointerUncheckedofObjectwithValue(introspectionDataIndex, introspectionData, (((usqInt)actualBcpc << 3) | 1)); + storePointerUncheckedofObjectwithValue(introspectionDataIndex + 1, introspectionData, (((usqInt)actualMcpc << 3) | 1)); + introspectionDataIndex += 2; + } + return 0; +} + + +/* If there is a generator for the current primitive then answer it; + otherwise answer nil. */ + + /* SimpleStackBasedCogit>>#primitiveGeneratorOrNil */ +static PrimitiveDescriptor * +primitiveGeneratorOrNil(void) +{ + PrimitiveDescriptor *primitiveDescriptor; + + if (isQuickPrimitiveIndex(primitiveIndex)) { + /* an unused one */ + primitiveDescriptor = (&(primitiveGeneratorTable[0])); + (primitiveDescriptor->primitiveGenerator = quickPrimitiveGeneratorFor(primitiveIndex)); + return primitiveDescriptor; + } + if (((primitiveIndex >= 1) && (primitiveIndex <= MaxCompiledPrimitiveIndex))) { + /* for debugging, allow excluding specific primitives */ + return (&(primitiveGeneratorTable[primitiveIndex])); + } + return null; +} + + /* SimpleStackBasedCogit>>#register:isInMask: */ +static NoDbgRegParms int +registerisInMask(sqInt reg, sqInt mask) +{ + return ((mask & (((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)))) != 0); +} + + /* SimpleStackBasedCogit>>#register:isNotInMask: */ +static NoDbgRegParms int +registerisNotInMask(sqInt reg, sqInt mask) +{ + return (!(mask & (((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg))))); +} + + +/* Add a blockStart for an embedded block. For a binary tree walk block + dispatch blocks must be compiled in pc/depth-first order but are scanned + in breadth-first + order, so do an insertion sort (which of course is really a bubble sort + because we + have to move everything higher to make room). */ + + /* StackToRegisterMappingCogit>>#addBlockStartAt:numArgs:numCopied:span: */ +static NoDbgRegParms BlockStart * +addBlockStartAtnumArgsnumCopiedspan(sqInt bytecodepc, sqInt numArgs, sqInt numCopied, sqInt span) +{ + BlockStart *blockStart; + sqInt i; + sqInt j; + + + /* Transcript ensureCr; nextPutAll: 'addBlockStartAt: '; print: bytecodepc; cr; flush. */ + if (blockCount > 0) { + i = blockCount - 1; + while (1) { + blockStart = (&(blockStarts[i])); + + /* check for repeat addition during recompilation due to initialNil miscount. */ + if (((blockStart->startpc)) == bytecodepc) { + return blockStart; + } + if (!((((blockStart->startpc)) > bytecodepc) + && (i > 0))) break; + i -= 1; + } + for (j = blockCount; j >= (i + 1); j += -1) { + blockStarts[j] = (blockStarts[j - 1]); + } + blockStart = (&(blockStarts[i + 1])); + } + else { + blockStart = (&(blockStarts[blockCount])); + } + blockCount += 1; + (blockStart->startpc = bytecodepc); + (blockStart->numArgs = numArgs); + (blockStart->numCopied = numCopied); + (blockStart->numInitialNils = 0); + (blockStart->stackCheckLabel = null); + (blockStart->hasInstVarRef = 0); + (blockStart->span = span); + return blockStart; +} + + +/* e.g. Receiver Receiver or Receiver Receiver (RISC) + Selector/Arg0 => Arg1 Selector/Arg0 => Arg1 + Arg1 Arg2 Arg1 Arg2 + Arg2 Arg3 Arg2 sp-> Arg3 + Arg3 sp-> retpc sp-> Arg3 + sp-> retpc */ +/* Generate code to adjust the possibly stacked arguments immediately + before jumping to a method looked up by a perform primitive. */ + + /* StackToRegisterMappingCogit>>#adjustArgumentsForPerform: */ +static NoDbgRegParms void +adjustArgumentsForPerform(sqInt numArgs) +{ + sqInt index; + + assert((numRegArgs()) <= 2); + assert(numArgs >= 1); + if (numArgs <= (numRegArgs())) { + if (numArgs == 2) { + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg1Reg, Arg0Reg); + } + return; + } + + /* If the arity is one more than the max numRegArgs, the receiver and all arguments have to be removed from the stack. */ + if (((numRegArgs()) + 1) == numArgs) { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, 0, SPReg, TempReg); + if ((numRegArgs()) == 2) { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord, SPReg, Arg1Reg); + + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord * 2, SPReg, Arg0Reg); + } + else { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, BytesPerWord, SPReg, Arg0Reg); + } + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, (numArgs + 1) * BytesPerWord, SPReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, 0, SPReg); + return; + } + + /* e.g. Receiver Receiver + Selector/Arg0 => Arg1 + Arg1 Arg2 + Arg2 sp-> Arg3 + sp-> Arg3 */ + for (index = (numArgs - 1); index >= 1; index += -1) { + /* #MoveMw:r:R: #gen:quickConstant:operand:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, index * BytesPerWord, SPReg, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, (index + 1) * BytesPerWord, SPReg); + } + + /* PopR: */ + genoperand(PopR, TempReg); + + /* #MoveR:Mw:r: #gen:operand:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveRMwr, TempReg, 0, SPReg); +} + + +/* If the stack entry is already in a register not conflicting with regMask, + answers it, + else allocate a new register not conflicting with reg mask + */ + + /* StackToRegisterMappingCogit>>#allocateRegForStackEntryAt:notConflictingWith: */ +static NoDbgRegParms sqInt +allocateRegForStackEntryAtnotConflictingWith(sqInt index, sqInt regMask) +{ + sqInt mask; + SimStackEntry *stackEntry; + + /* begin ssValue: */ + stackEntry = simStackAt(simStackPtr - index); + mask = registerMaskOrNone(stackEntry); + if ((mask != 0) + && ((!(mask & regMask)))) { + /* When one does pushDup on a SSRegister + followed by an operation reusing the register + but mutating the value of the register, then the value that was + dup is now refering to the register with a mutated value which + is incorrect. This problem is serious as it can happen also with + multiple unsafe instructions (no dup involved) + This is not a problem if the operation reusing the register is not + mutating the value of if all the dup values are used in the same + operation. + => I think we should introduce allocateRegMutatingStackEntryAt: + notConflictingWith:upThrough:, used by operations mutating the reg + value and flushing partially the stack if somewhere between simSpill + and the ssEntries used by the operation someone uses also the same + register. */ + return registerOrNone(stackEntry); + } + return allocateRegNotConflictingWith(regMask); +} + + +/* if there's a free register, use it */ + + /* StackToRegisterMappingCogit>>#allocateRegNotConflictingWith: */ +static NoDbgRegParms sqInt +allocateRegNotConflictingWith(sqInt regMask) +{ + sqInt reg; + + reg = availableRegisterOrNoneFor(backEnd, (liveRegisters()) | regMask); + if (reg == NoReg) { + reg = freeAnyRegNotConflictingWith(regMask); + } + + /* No free register, choose one that does not conflict with regMask */ + if (reg == ReceiverResultReg) { + voidReceiverResultRegContainsSelf(); + } + + /* If we've allocated RcvrResultReg, it's not live anymore */ + return reg; +} + + /* StackToRegisterMappingCogit>>#anyReferencesToRegister:inTopNItems: */ +static NoDbgRegParms sqInt +anyReferencesToRegisterinTopNItems(sqInt reg, sqInt n) +{ + sqInt i; + sqInt regMask; + + regMask = ((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)); + for (i = simStackPtr; i >= ((simStackPtr - n) + 1); i += -1) { + if ((((registerMask(simStackAt(i))) & regMask) != 0)) { + return 1; + } + } + return 0; +} + + +/* This is a static version of ceCallCogCodePopReceiverArg0Regs + for break-pointing when debugging in C. */ +/* This exists only for break-pointing. */ + + /* StackToRegisterMappingCogit>>#callCogCodePopReceiverArg0Regs */ +void +callCogCodePopReceiverArg0Regs(void) +{ + realCECallCogCodePopReceiverArg0Regs(); +} + + +/* This is a static version of ceCallCogCodePopReceiverArg1Arg0Regs + for break-pointing when debugging in C. */ +/* This exists only for break-pointing. */ + + /* StackToRegisterMappingCogit>>#callCogCodePopReceiverArg1Arg0Regs */ +void +callCogCodePopReceiverArg1Arg0Regs(void) +{ + realCECallCogCodePopReceiverArg1Arg0Regs(); +} + + /* StackToRegisterMappingCogit>>#compileBlockBodies */ +static sqInt +compileBlockBodies(void) +{ + BlockStart *blockStart; + sqInt compiledBlocksCount; + sqInt initialCounterIndex; + sqInt initialOpcodeIndex; + sqInt initialStackPtr; + sqInt (* const pushNilSizeFunction)(sqInt,sqInt) = squeakV3orSistaV1PushNilSizenumInitialNils; + sqInt result; + sqInt savedNeedsFrame; + sqInt savedNumArgs; + sqInt savedNumTemps; + + initialStackPtr = 0; + assert(blockCount > 0); + + /* scanBlock: in compileBlockEntry: sets both of these appropriately for each block. */ + savedNeedsFrame = needsFrame; + savedNumArgs = methodOrBlockNumArgs; + savedNumTemps = methodOrBlockNumTemps; + inBlock = InVanillaBlock; + compiledBlocksCount = 0; + while (compiledBlocksCount < blockCount) { + compilationPass = 1; + blockStart = blockStartAt(compiledBlocksCount); + if (((result = scanBlock(blockStart))) < 0) { + return result; + } + initialOpcodeIndex = opcodeIndex; + + /* for SistaCogit */ + initialCounterIndex = 0 /* maybeCounterIndex */; + while (1) { + compileBlockEntry(blockStart); + initialStackPtr = simStackPtr; + if (((result = compileBytecodesFromthrough(((blockStart->startpc)) + (pushNilSizeFunction(methodObj, ((blockStart->numInitialNils)))), (((blockStart->startpc)) + ((blockStart->span))) - 1))) < 0) { + return result; + } + + /* If the final simStackPtr is less than the initial simStackPtr then scanBlock: over- + estimated the number of initial nils (because it assumed one or more pushNils to + produce an operand were pushNils to initialize temps. This is very rare, so + compensate by checking, adjusting numInitialNils and recompiling the block body. + N.B. No need to reinitialize the literalsManager because it answers existing literals. */ + if (initialStackPtr == simStackPtr) break; + assert((initialStackPtr > simStackPtr) + || (deadCode)); + + /* for asserts */ + compilationPass += 1; + (blockStart->numInitialNils = (((blockStart->numInitialNils)) + simStackPtr) - initialStackPtr); + (((blockStart->fakeHeader))->dependent = null); + reinitializeFixupsFromthrough(((blockStart->startpc)) + ((blockStart->numInitialNils)), (((blockStart->startpc)) + ((blockStart->span))) - 1); + bzero(abstractOpcodes + initialOpcodeIndex, + (opcodeIndex - initialOpcodeIndex) * sizeof(AbstractInstruction)); + opcodeIndex = initialOpcodeIndex; + } + compiledBlocksCount += 1; + } + needsFrame = savedNeedsFrame; + methodOrBlockNumArgs = savedNumArgs; + methodOrBlockNumTemps = savedNumTemps; + return 0; +} + + +/* Build a frame for a block activation. See CoInterpreter + class>>initializeFrameIndices. closure (in ReceiverResultReg) + arg0 + ... + argN + caller's saved ip/this stackPage (for a base frame) + fp-> saved fp + method + context (uninitialized?) + receiver + first temp + ... + sp-> Nth temp + Avoid use of SendNumArgsReg which is the flag determining whether + context switch is allowed on stack-overflow. */ +/* Build a frame for a block activation. See CoInterpreter + class>>initializeFrameIndices. Override to push the register receiver and + register arguments, if any, and to correctly + initialize the explicitly nilled/pushed temp entries (they are /not/ of + type constant nil). */ + + /* StackToRegisterMappingCogit>>#compileBlockFrameBuild: */ +static NoDbgRegParms void +compileBlockFrameBuild(BlockStart *blockStart) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *cascade0; + sqInt i; + sqInt ign; + + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + + /* PushR: */ + genoperand(PushR, FPReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SPReg, FPReg); + + /* Think of ClassReg as ClosureReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, ClassReg); + + /* The block method field must have its MFMethodFlagIsBlockFlag bit set. + We arrange this using a labelOffset. A hack, but it works. */ + cascade0 = (blockStart->fakeHeader); + addDependent(cascade0, annotateAbsolutePCRef(genoperand(PushCw, ((sqInt)((blockStart->fakeHeader)))))); + + /* setLabelOffset: */ + ((cascade0->operands))[1] = MFMethodFlagIsBlockFlag; + + /* method */ + + /* genPushConstant: */ + genoperand(PushCq, nilObject()); + + /* Fetch home receiver from outer context. closure is on stack and initially in ReceiverResultReg. + It is safe to use Arg0Reg because reg args are pushed by the value primitives if there are any. */ + if ((blockStart->hasInstVarRef)) { + genLoadSlotsourceRegdestReg(ClosureOuterContextIndex, ClassReg, ReceiverResultReg); + genLoadSlotsourceRegdestReg(ReceiverIndex, ReceiverResultReg, Arg0Reg); + genEnsureOopInRegNotForwardedscratchRegupdatingSlotin(Arg0Reg, TempReg, ReceiverIndex, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ReceiverResultReg); + } + else { + genLoadSlotsourceRegdestReg(ClosureOuterContextIndex, ClassReg, Arg0Reg); + genLoadSlotsourceRegdestReg(ReceiverIndex, Arg0Reg, ReceiverResultReg); + } + + /* Use ReceiverResultReg for Context to agree with store check trampoline */ + + /* PushR: */ + genoperand(PushR, ReceiverResultReg); + for (i = 0; i < ((blockStart->numCopied)); i += 1) { + genLoadSlotsourceRegdestReg(i + ClosureFirstCopiedValueIndex, ClassReg, TempReg); + + /* PushR: */ + genoperand(PushR, TempReg); + } + (blockStart->stackCheckLabel = compileStackOverflowCheck(1)); + methodOrBlockNumTemps = (((blockStart->numArgs)) + ((blockStart->numCopied))) + ((blockStart->numInitialNils)); + initSimStackForFramefulMethod((blockStart->startpc)); + if (((blockStart->numInitialNils)) > 0) { + if (((blockStart->numInitialNils)) > 1) { + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, nilObject(), TempReg); + for (ign = 1; ign <= ((blockStart->numInitialNils)); ign += 1) { + /* PushR: */ + genoperand(PushR, TempReg); + } + } + else { + /* genPushConstant: */ + genoperand(PushCq, nilObject()); + } + } +} + + +/* Make sure ReceiverResultReg holds the receiver, loaded from the closure, + which is what is initially in ReceiverResultReg. We must annotate the + first instruction in vanilla blocks so that + findMethodForStartBcpc:inHomeMethod: can function. We need two annotations + because the first is a fiducial. */ +/* Make sure ReceiverResultReg holds the receiver, loaded from + the closure, which is what is initially in ReceiverResultReg */ + + /* StackToRegisterMappingCogit>>#compileBlockFramelessEntry: */ +static NoDbgRegParms void +compileBlockFramelessEntry(BlockStart *blockStart) +{ + AbstractInstruction *abstractInstruction; + + methodOrBlockNumTemps = (((blockStart->numArgs)) + ((blockStart->numCopied))) + ((blockStart->numInitialNils)); + initSimStackForFramelessBlock((blockStart->startpc)); + if ((blockStart->entryLabel)) { + abstractInstruction = (blockStart->entryLabel); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + } + if ((blockStart->hasInstVarRef)) { + genLoadSlotsourceRegdestReg(ClosureOuterContextIndex, ReceiverResultReg, ReceiverResultReg); + genLoadSlotsourceRegdestReg(ReceiverIndex, ReceiverResultReg, Arg0Reg); + genEnsureOopInRegNotForwardedscratchRegupdatingSlotin(Arg0Reg, TempReg, ReceiverIndex, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ReceiverResultReg); + } + else { + genLoadSlotsourceRegdestReg(ClosureOuterContextIndex, ReceiverResultReg, TempReg); + genLoadSlotsourceRegdestReg(ReceiverIndex, TempReg, ReceiverResultReg); + } +} + + +/* Loop over bytecodes, dispatching to the generator for each bytecode, + handling fixups in due course. + */ + + /* StackToRegisterMappingCogit>>#compileBytecodesFrom:through: */ +static NoDbgRegParms sqInt +compileBytecodesFromthrough(sqInt start, sqInt end) +{ + BytecodeDescriptor *descriptor; + BytecodeFixup *fixup; + sqInt nExts; + sqInt nextOpcodeIndex; + sqInt result; + + traceSimStack(); + bytecodePC = start; + nExts = (result = 0); + descriptor = null; + deadCode = 0; + while (1) { + maybeHaltIfDebugPC(); + mergeWithFixupIfRequired((fixup = fixupAt(bytecodePC))); + descriptor = loadBytesAndGetDescriptor(bytecodePC); + nextOpcodeIndex = opcodeIndex; + result = (deadCode + ? mapDeadDescriptorIfNeeded(descriptor) + : ((descriptor->generator))()); + if (!result) { + /* begin assertExtsAreConsumed: */ + if (!((descriptor->isExtension))) { + assert((extA == 0) + && ((extB == 0) + && (numExtB == 0))); + } + } + traceDescriptor(descriptor); + traceSimStack(); + + /* begin patchFixupTargetIfNeeded:nextOpcodeIndex: */ + if ((((((usqInt)((fixup->targetInstruction)))) >= NeedsNonMergeFixupFlag) && ((((usqInt)((fixup->targetInstruction)))) <= NeedsMergeFixupFlag))) { + /* There is a fixup for this bytecode. It must point to the first generated + instruction for this bytecode. If there isn't one we need to add a label. */ + if (opcodeIndex == nextOpcodeIndex) { + /* Label */ + genoperandoperand(Label, (labelCounter += 1), bytecodePC); + } + (fixup->targetInstruction = abstractInstructionAt(nextOpcodeIndex)); + } + bytecodePC = (bytecodePC + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, bytecodePC, nExts, methodObj) + : 0)); + if (!((result == 0) + && (bytecodePC <= end))) break; + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } + + /* begin checkEnoughOpcodes */ + if (opcodeIndex > numAbstractOpcodes) { + error("Cog JIT internal error. Too many abstract opcodes. Num opcodes heuristic is too optimistic."); + } + return result; +} + + /* StackToRegisterMappingCogit>>#compileCogFullBlockMethod: */ +static NoDbgRegParms CogMethod * +compileCogFullBlockMethod(sqInt numCopied) +{ + usqIntptr_t allocBytes; + usqIntptr_t fixupBytes; + sqInt numBlocks; + sqInt numBytecodes; + sqInt numCleanBlocks; + usqIntptr_t opcodeBytes; + sqInt result; + + methodOrBlockNumTemps = tempCountOf(methodObj); + setHasMovableLiteral(0); + setHasYoungReferent(isYoungObject(methodObj)); + methodOrBlockNumArgs = argumentCountOf(methodObj); + inBlock = InFullBlock; + maxLitIndex = -1; + assert((primitiveIndexOf(methodObj)) == 0); + initialPC = startPCOfMethod(methodObj); + + /* initial estimate. Actual endPC is determined in scanMethod. */ + endPC = numBytesOf(methodObj); + numBytecodes = (endPC - initialPC) + 1; + primitiveIndex = 0; + + /* begin allocateOpcodes:bytecodes:ifFail: */ + numAbstractOpcodes = (numBytecodes + 10) * 10 /* estimateOfAbstractOpcodesPerBytecodes */; + opcodeBytes = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupBytes = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + allocBytes = opcodeBytes + fixupBytes; + + /* Document the fact that the MaxStackAllocSize ensures that the number of abstract + opcodes fits in a 16 bit integer (e.g. CogBytecodeFixup's instructionIndex). */ + assert((((sizeof(CogAbstractInstruction)) + (sizeof(CogBytecodeFixup))) * 0xC000) > MaxStackAllocSize); + if (allocBytes > MaxStackAllocSize) { + return ((CogMethod *) MethodTooBig); + } + abstractOpcodes = alloca(allocBytes); + bzero(abstractOpcodes, allocBytes); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeBytes)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + /* end allocateOpcodes:bytecodes:ifFail: */ +l1: + if (((numBlocks = scanMethod())) < 0) { + return ((CogMethod *) numBlocks); + } + assert(numBlocks == 0); + numCleanBlocks = scanForCleanBlocks(); + assert(numCleanBlocks == 0); + allocateBlockStarts(numBlocks + numCleanBlocks); + blockCount = 0; + if (numCleanBlocks > 0) { + addCleanBlockStarts(); + } + + /* Inaccurate error code, but it'll do. This will likely never fail. */ + blockEntryLabel = null; + (methodLabel->dependent = null); + if (((result = compileEntireFullBlockMethod(numCopied))) < 0) { + return ((CogMethod *) result); + } + return generateCogFullBlock(); +} + + /* StackToRegisterMappingCogit>>#compileCogMethod: */ +static NoDbgRegParms CogMethod * +compileCogMethod(sqInt selector) +{ + usqIntptr_t allocBytes; + int extra; + usqIntptr_t fixupBytes; + sqInt methodFoundInvalidPostScanRV; + sqInt numBlocks; + sqInt numBytecodes; + sqInt numCleanBlocks; + usqIntptr_t opcodeBytes; + sqInt result; + + methodOrBlockNumTemps = tempCountOf(methodObj); + setHasMovableLiteral(0); + setHasYoungReferent((isYoungObject(methodObj)) + || (isYoung(selector))); + methodOrBlockNumArgs = argumentCountOf(methodObj); + inBlock = 0; + maxLitIndex = -1; + extra = ((((primitiveIndex = primitiveIndexOf(methodObj))) > 0) + && (!(isQuickPrimitiveIndex(primitiveIndex))) + ? 30 + : 10); + initialPC = startPCOfMethod(methodObj); + + /* initial estimate. Actual endPC is determined in scanMethod. */ + endPC = (isQuickPrimitiveIndex(primitiveIndex) + ? initialPC - 1 + : numBytesOf(methodObj)); + numBytecodes = (endPC - initialPC) + 1; + + /* begin allocateOpcodes:bytecodes:ifFail: */ + numAbstractOpcodes = (numBytecodes + extra) * 10 /* estimateOfAbstractOpcodesPerBytecodes */; + opcodeBytes = (sizeof(CogAbstractInstruction)) * numAbstractOpcodes; + fixupBytes = (sizeof(CogBytecodeFixup)) * numAbstractOpcodes; + allocBytes = opcodeBytes + fixupBytes; + + /* Document the fact that the MaxStackAllocSize ensures that the number of abstract + opcodes fits in a 16 bit integer (e.g. CogBytecodeFixup's instructionIndex). */ + assert((((sizeof(CogAbstractInstruction)) + (sizeof(CogBytecodeFixup))) * 0xC000) > MaxStackAllocSize); + if (allocBytes > MaxStackAllocSize) { + return ((CogMethod *) MethodTooBig); + } + abstractOpcodes = alloca(allocBytes); + bzero(abstractOpcodes, allocBytes); + fixups = ((void *)((((usqInt)abstractOpcodes)) + opcodeBytes)); + + /* begin zeroOpcodeIndexForNewOpcodes */ + opcodeIndex = 0; + labelCounter = 0; + /* end allocateOpcodes:bytecodes:ifFail: */ +l1: + if (((numBlocks = scanMethod())) < 0) { + return ((CogMethod *) numBlocks); + } + numCleanBlocks = scanForCleanBlocks(); + + /* begin methodFoundInvalidPostScan */ + if (!needsFrame) { + methodFoundInvalidPostScanRV = methodOrBlockNumTemps > methodOrBlockNumArgs; + goto l2; + } + methodFoundInvalidPostScanRV = 0; + /* end methodFoundInvalidPostScan */ +l2: + if (methodFoundInvalidPostScanRV) { + return ((CogMethod *) ShouldNotJIT); + } + allocateBlockStarts(numBlocks + numCleanBlocks); + blockCount = 0; + if (numCleanBlocks > 0) { + addCleanBlockStarts(); + } + + /* Inaccurate error code, but it'll do. This will likely never fail. */ + blockEntryLabel = null; + (methodLabel->dependent = null); + if (((result = compileEntireMethod())) < 0) { + return ((CogMethod *) result); + } + return generateCogMethod(selector); +} + + +/* Compile the abstract instructions for the entire method, including blocks. */ +/* Compile the abstract instructions for the entire method, including blocks. */ + + /* StackToRegisterMappingCogit>>#compileEntireMethod */ +static sqInt +compileEntireMethod(void) +{ + sqInt result; + + regArgsHaveBeenPushed = 0; + + /* begin preenMethodLabel */ + /* setLabelOffset: */ + ((methodLabel->operands))[1] = 0; + compileAbort(); + compileEntry(); + if (((result = compilePrimitive())) < 0) { + return result; + } + compileFrameBuild(); + if (((result = compileMethodBody())) < 0) { + return result; + } + if (!blockCount) { + return 0; + } + if (((result = compileBlockBodies())) < 0) { + return result; + } + return compileBlockDispatch(); +} + + +/* Build a frame for a CogMethod activation. See CoInterpreter + class>>initializeFrameIndices. receiver (in ReceiverResultReg) + arg0 + ... + argN + caller's saved ip/this stackPage (for a base frame) + fp-> saved fp + method + context (uninitialized?) + receiver + first temp + ... + sp-> Nth temp + If there is a primitive and an error code the Nth temp is the error code. + Ensure SendNumArgsReg is set early on (incidentally to nilObj) because + it is the flag determining whether context switch is allowed on + stack-overflow. */ +/* Build a frame for a CogMethod activation. See CoInterpreter + class>>initializeFrameIndices. Override to push the register receiver and + register arguments, if any. */ + + /* StackToRegisterMappingCogit>>#compileFrameBuild */ +static void +compileFrameBuild(void) +{ + sqInt i; + sqInt toDoLimit; + + +# if IMMUTABILITY + if (useTwoPaths) { + compileTwoPathFrameBuild(); + return; + } +# endif + + if (!needsFrame) { + if (useTwoPaths) { + compileTwoPathFramelessInit(); + } + initSimStackForFramelessMethod(initialPC); + return; + } + assert(!(useTwoPaths)); + genPushRegisterArgs(); + if (!needsFrame) { + return; + } + + /* PushR: */ + genoperand(PushR, FPReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SPReg, FPReg); + addDependent(methodLabel, annotateAbsolutePCRef(genoperand(PushCw, ((sqInt)methodLabel)))); + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, nilObject(), SendNumArgsReg); + + /* PushR: */ + genoperand(PushR, SendNumArgsReg); + + /* PushR: */ + genoperand(PushR, ReceiverResultReg); + toDoLimit = temporaryCountOfMethodHeader(methodHeader); + for (i = (methodOrBlockNumArgs + 1); i <= toDoLimit; i += 1) { + /* PushR: */ + genoperand(PushR, SendNumArgsReg); + } + if (/* methodUsesPrimitiveErrorCode:header: */ + ((primitiveIndexOfMethodheader(methodObj, methodHeader)) > 0) + && ((longStoreBytecodeForHeader(methodHeader)) == (fetchByteofObject((startPCOfMethodHeader(methodHeader)) + (sizeOfCallPrimitiveBytecode(methodHeader)), methodObj)))) { + compileGetErrorCode(); + } + stackCheckLabel = compileStackOverflowCheck(canContextSwitchIfActivatingheader(methodObj, methodHeader)); + initSimStackForFramefulMethod(initialPC); +} + + +/* Make sure ReceiverResultReg holds the receiver, loaded from the closure, + which is what is initially in ReceiverResultReg. */ +/* Make sure ReceiverResultReg holds the receiver, loaded from + the closure, which is what is initially in ReceiverResultReg */ + + /* StackToRegisterMappingCogit>>#compileFullBlockFramelessEntry: */ +static NoDbgRegParms void +compileFullBlockFramelessEntry(sqInt numCopied) +{ + initSimStackForFramelessBlock(initialPC); + genLoadSlotsourceRegdestReg(FullClosureReceiverIndex, ReceiverResultReg, Arg0Reg); + genEnsureOopInRegNotForwardedscratchRegupdatingSlotin(Arg0Reg, TempReg, FullClosureReceiverIndex, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ReceiverResultReg); +} + + +/* Build a frame for a block activation. See CoInterpreter + class>>initializeFrameIndices. closure (in ReceiverResultReg) + arg0 + ... + argN + caller's saved ip/this stackPage (for a base frame) + fp-> saved fp + method + context (uninitialized?) + receiver + first temp + ... + sp-> Nth temp + Avoid use of SendNumArgsReg which is the flag determining whether + context switch is allowed on stack-overflow. */ + + /* StackToRegisterMappingCogit>>#compileFullBlockMethodFrameBuild: */ +static NoDbgRegParms void +compileFullBlockMethodFrameBuild(sqInt numCopied) +{ + sqInt i; + sqInt toDoLimit; + + if (useTwoPaths) { + /* method with only inst var store, we compile only slow path for now */ + useTwoPaths = 0; +# if IMMUTABILITY + needsFrame = 1; +# endif + + } + if (!needsFrame) { + /* it is OK for numCopied to be non-zero provided that the block does not actually use the copied values. + There are some blocks like this, e.g. that simply reference copied values to mark them as used for Slang. + See e.g. CroquetPlugin>>#primitiveGatherEntropy which contains the block [bufPtr. bufSize. false], + which the bytecode compiler optimizes to [false]. */ + compileFullBlockFramelessEntry(numCopied); + initSimStackForFramelessBlock(initialPC); + return; + } + if (!needsFrame) { + return; + } + + /* PushR: */ + genoperand(PushR, FPReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SPReg, FPReg); + + /* Think of ClassReg as ClosureReg */ + + /* MoveR:R: */ + genoperandoperand(MoveRR, ReceiverResultReg, ClassReg); + + /* The block method field must have its MFMethodFlagIsBlockFlag bit set. + We arrange this using a labelOffset. A hack, but it works. */ + addDependent(methodLabel, annotateAbsolutePCRef(genoperand(PushCw, ((sqInt)methodLabel)))); + + /* setLabelOffset: */ + ((methodLabel->operands))[1] = MFMethodFlagIsBlockFlag; + + /* method */ + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, nilObject(), SendNumArgsReg); + + /* PushR: */ + genoperand(PushR, SendNumArgsReg); + genLoadSlotsourceRegdestReg(FullClosureReceiverIndex, ClassReg, Arg0Reg); + genEnsureOopInRegNotForwardedscratchRegupdatingSlotin(Arg0Reg, TempReg, FullClosureReceiverIndex, ReceiverResultReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, Arg0Reg, ReceiverResultReg); + + /* PushR: */ + genoperand(PushR, ReceiverResultReg); + for (i = 0; i < numCopied; i += 1) { + genLoadSlotsourceRegdestReg(i + FullClosureFirstCopiedValueIndex, ClassReg, TempReg); + + /* PushR: */ + genoperand(PushR, TempReg); + } + + /* Push slots for temps */ + toDoLimit = temporaryCountOfMethodHeader(methodHeader); + for (i = ((methodOrBlockNumArgs + numCopied) + 1); i <= toDoLimit; i += 1) { + /* PushR: */ + genoperand(PushR, SendNumArgsReg); + } + stackCheckLabel = compileStackOverflowCheck(1); + initSimStackForFramefulMethod(initialPC); +} + + +/* Build a frame for a CogMethod activation. See CoInterpreter + class>>initializeFrameIndices. receiver (in ReceiverResultReg) + arg0 + ... + argN + caller's saved ip/this stackPage (for a base frame) + fp-> saved fp + method + context (uninitialized?) + receiver + first temp + ... + sp-> Nth temp + If there is a primitive and an error code the Nth temp is the error code. + Ensure SendNumArgsReg is set early on (incidentally to nilObj) because + it is the flag determining whether context switch is allowed on + stack-overflow. */ +/* We are in a method where the frame is needed *only* for instance variable + store, typically a setter method. + This case has 20% overhead with Immutability compared to setter without + immutability because of the stack + frame creation. We compile two path, one where the object is immutable, + one where it isn't. At the beginning + of the frame build, we take one path or the other depending on the + receiver mutability. + + Note: this specific case happens only where there are only instance + variabel stores. We could do something + similar for literal variable stores, but we don't as it's too uncommon. + */ + + /* StackToRegisterMappingCogit>>#compileTwoPathFrameBuild */ +#if IMMUTABILITY +static void +compileTwoPathFrameBuild(void) +{ + sqInt i; + AbstractInstruction *jumpImmutable; + AbstractInstruction *jumpOld; + sqInt toDoLimit; + + assert(useTwoPaths); + assert(blockCount == 0); + jumpImmutable = genJumpImmutablescratchReg(ReceiverResultReg, TempReg); + + /* begin genJumpInOldSpace: */ + /* CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, storeCheckBoundary(), ReceiverResultReg); + jumpOld = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* first path. The receiver is mutable */ + assert(!needsFrame); + initSimStackForFramelessMethod(initialPC); + + /* begin compileMethodBody */ + if (endPC < initialPC) { + goto l1; + } + compileBytecodesFromthrough(initialPC + (deltaToSkipPrimAndErrorStoreInheader(methodObj, methodHeader)), endPC); + /* end compileMethodBody */ +l1: + + /* second path. The receiver is mutable */ + + /* reset because it impacts inst var store compilation */ + useTwoPaths = 0; + needsFrame = 1; + jmpTarget(jumpOld, jmpTarget(jumpImmutable, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + genPushRegisterArgs(); + if (!needsFrame) { + return; + } + + /* PushR: */ + genoperand(PushR, FPReg); + + /* MoveR:R: */ + genoperandoperand(MoveRR, SPReg, FPReg); + addDependent(methodLabel, annotateAbsolutePCRef(genoperand(PushCw, ((sqInt)methodLabel)))); + + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, nilObject(), SendNumArgsReg); + + /* PushR: */ + genoperand(PushR, SendNumArgsReg); + + /* PushR: */ + genoperand(PushR, ReceiverResultReg); + toDoLimit = temporaryCountOfMethodHeader(methodHeader); + for (i = (methodOrBlockNumArgs + 1); i <= toDoLimit; i += 1) { + /* PushR: */ + genoperand(PushR, SendNumArgsReg); + } + if (/* methodUsesPrimitiveErrorCode:header: */ + ((primitiveIndexOfMethodheader(methodObj, methodHeader)) > 0) + && ((longStoreBytecodeForHeader(methodHeader)) == (fetchByteofObject((startPCOfMethodHeader(methodHeader)) + (sizeOfCallPrimitiveBytecode(methodHeader)), methodObj)))) { + compileGetErrorCode(); + } + stackCheckLabel = compileStackOverflowCheck(canContextSwitchIfActivatingheader(methodObj, methodHeader)); + initSimStackForFramefulMethod(initialPC); +} +#endif /* IMMUTABILITY */ + + +/* We are in a frameless method with at least two inst var stores. We compile + two paths, + one where the object is in new space, and one where it isn't. At the + beginning + of the method, we take one path or the other depending on the receiver + being in newSpace. + */ + + /* StackToRegisterMappingCogit>>#compileTwoPathFramelessInit */ +static void +compileTwoPathFramelessInit(void) +{ + AbstractInstruction *jumpOld; + + assert(!(IMMUTABILITY)); + assert(!(needsFrame)); + assert(useTwoPaths); + + /* begin genJumpInOldSpace: */ + /* CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, storeCheckBoundary(), ReceiverResultReg); + jumpOld = genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)0)); + + /* first path. The receiver is young */ + initSimStackForFramelessMethod(initialPC); + + /* begin compileMethodBody */ + if (endPC < initialPC) { + goto l1; + } + compileBytecodesFromthrough(initialPC + (deltaToSkipPrimAndErrorStoreInheader(methodObj, methodHeader)), endPC); + /* end compileMethodBody */ +l1: + + /* second path. The receiver is old */ + + /* reset because it impacts inst var store compilation */ + useTwoPaths = 0; + jmpTarget(jumpOld, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); +} + + /* StackToRegisterMappingCogit>>#cPICMissTrampolineFor: */ +static NoDbgRegParms sqInt +cPICMissTrampolineFor(sqInt numArgs) +{ + return picMissTrampolines[((numArgs < ((numRegArgs()) + 1)) ? numArgs : ((numRegArgs()) + 1))]; +} + + +/* Replaces the Blue Book double-extended send [132], in which the first byte + was wasted on 8 bits of argument count. + Here we use 3 bits for the operation sub-type (opType), and the remaining + 5 bits for argument count where needed. + The last byte give access to 256 instVars or literals. + See also secondExtendedSendBytecode + */ + + /* StackToRegisterMappingCogit>>#doubleExtendedDoAnythingBytecode */ +static sqInt +doubleExtendedDoAnythingBytecode(void) +{ + AbstractInstruction *abstractInstruction; + sqInt opType; + + opType = ((usqInt)(byte1)) >> 5; + if (!opType) { + return genSendnumArgs(byte2, byte1 & 0x1F); + } + if (opType == 1) { + return genSendSupernumArgs(byte2, byte1 & 0x1F); + } + + /* We need a map entry for this bytecode for correct parsing. + The sends will get an IsSend entry anyway. The other cases need a fake one. */ + switch (opType) { + case 2: + if (isReadMediatedContextInstVarIndex(byte2)) { + genPushMaybeContextReceiverVariable(byte2); + } + else { + genPushReceiverVariable(byte2); + + /* begin annotateInstructionForBytecode */ + abstractInstruction = (prevInstIsPCAnnotated() + ? gen(Nop) + : genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + return 0; + } + break; + case 3: + genPushLiteralIndex(byte2); + + /* begin annotateInstructionForBytecode */ + abstractInstruction = (prevInstIsPCAnnotated() + ? gen(Nop) + : genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + return 0; + + case 4: + genPushLiteralVariable(byte2); + break; + case 7: + /* genStorePop:LiteralVariable: */ + genStorePopLiteralVariableneedsStoreCheckneedsImmutabilityCheck(0, byte2, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); +# if IMMUTABILITY + /* genStorePop:LiteralVariable: annotates; don't annotate twice */ + return 0; +# endif + + + default: + if (isWriteMediatedContextInstVarIndex(byte2)) { + /* genStorePop:MaybeContextReceiverVariable: */ + genStorePopMaybeContextReceiverVariableneedsStoreCheckneedsImmutabilityCheck(opType == 6, byte2, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } + else { + /* genStorePop:ReceiverVariable: */ + genStorePopReceiverVariableneedsStoreCheckneedsImmutabilityCheck(opType == 6, byte2, /* ssTopNeedsStoreCheck */ + ((((ssTop())->type)) != SSConstant) + || ((isNonImmediate(((ssTop())->constant))) + && (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject()))))), 1); + } +# if IMMUTABILITY + /* genStorePop:...ReceiverVariable: annotate; don't annotate twice */ + return 0; +# endif + +; + } + + /* 5 & 6 + We need a map entry for this bytecode for correct parsing (if the method builds a frame). */ + assert(needsFrame); + + /* genPushMaybeContextInstVar, pushLitVar, store & storePop all generate code */ + assert(!(prevInstIsPCAnnotated())); + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + return 0; +} + + /* StackToRegisterMappingCogit>>#duplicateTopBytecode */ +static sqInt +duplicateTopBytecode(void) +{ + SimStackEntry desc; + + /* begin ssTopDescriptor */ + desc = simStack[simStackPtr]; + return ssPushDesc(desc); +} + + +/* Make sure there's a flagged fixup at the target pc in fixups. + Initially a fixup's target is just a flag. Later on it is replaced with a + proper instruction. */ + + /* StackToRegisterMappingCogit>>#ensureFixupAt: */ +static NoDbgRegParms BytecodeFixup * +ensureFixupAt(sqInt targetPC) +{ + BytecodeFixup *fixup; + + /* begin fixupAt: */ + fixup = fixupAtIndex(targetPC - initialPC); + traceFixupmerge(fixup, 1); + if ((((usqInt)((fixup->targetInstruction)))) <= NeedsNonMergeFixupFlag) { + /* begin becomeMergeFixup */ + (fixup->targetInstruction) = ((AbstractInstruction *) NeedsMergeFixupFlag); + (fixup->simStackPtr = simStackPtr); + } + else { + if ((fixup->isTargetOfBackwardBranch)) { + (fixup->simStackPtr = simStackPtr); + } + else { + assert(((fixup->simStackPtr)) == simStackPtr); + } + } + + /* convert a non-merge into a merge */ + return fixup; +} + + +/* Make sure there's a flagged fixup at the target pc in fixups. + Initially a fixup's target is just a flag. Later on it is replaced with a + proper instruction. */ + + /* StackToRegisterMappingCogit>>#ensureNonMergeFixupAt: */ +static NoDbgRegParms BytecodeFixup * +ensureNonMergeFixupAt(sqInt targetPC) +{ + BytecodeFixup *fixup; + + /* begin fixupAt: */ + fixup = fixupAtIndex(targetPC - initialPC); + traceFixupmerge(fixup, 1); + if (!((fixup->targetInstruction))) { + /* begin becomeNonMergeFixup */ + (fixup->targetInstruction) = ((AbstractInstruction *) NeedsNonMergeFixupFlag); + } + return fixup; +} + + /* StackToRegisterMappingCogit>>#ensureReceiverResultRegContainsSelf */ +static void +ensureReceiverResultRegContainsSelf(void) +{ + if (needsFrame) { + if (!((((simSelf())->liveRegister)) == ReceiverResultReg)) { + /* begin ssAllocateRequiredReg: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << ReceiverResultReg), simStackPtr, simNativeStackPtr); + storeToReg(simSelf(), ReceiverResultReg); + ((simSelf())->liveRegister = ReceiverResultReg); + } + } + else { + assert(((((simSelf())->type)) == SSRegister) + && (((((simSelf())->registerr)) == ReceiverResultReg) + && (receiverIsInReceiverResultReg()))); + } +} + + /* StackToRegisterMappingCogit>>#evaluate:at: */ +static NoDbgRegParms void +evaluateat(BytecodeDescriptor *descriptor, sqInt pc) +{ + byte0 = fetchByteofObject(pc, methodObj); + assert(descriptor == (generatorAt(bytecodeSetOffset + byte0))); + + /* begin loadSubsequentBytesForDescriptor:at: */ + if (((descriptor->numBytes)) > 1) { + byte1 = fetchByteofObject(pc + 1, methodObj); + if (((descriptor->numBytes)) > 2) { + byte2 = fetchByteofObject(pc + 2, methodObj); + if (((descriptor->numBytes)) > 3) { + byte3 = fetchByteofObject(pc + 3, methodObj); + if (((descriptor->numBytes)) > 4) { + notYetImplemented(); + } + } + } + } + ((descriptor->generator))(); +} + + +/* Attempt to follow a branch to a pc. Handle branches to unconditional jumps + and branches to push: aBoolean; conditional branch pairs. If the branch + cannot be + followed answer targetBytecodePC. It is not possible to follow jumps to + conditional branches because the stack changes depth. That following is + left to the genJumpIf:to: + clients. */ + + /* StackToRegisterMappingCogit>>#eventualTargetOf: */ +static NoDbgRegParms sqInt +eventualTargetOf(sqInt targetBytecodePC) +{ + sqInt cond; + sqInt currentTarget; + BytecodeDescriptor *descriptor; + sqInt nExts; + sqInt nextPC; + sqInt span; + + cond = 0; + descriptor = ((BytecodeDescriptor *) 0); + nextPC = (currentTarget = targetBytecodePC); + while (1) { + nExts = 0; + while (1) { + /* begin generatorForPC: */ + descriptor = generatorAt(bytecodeSetOffset + (fetchByteofObject(nextPC, methodObj))); + if ((descriptor->isReturn)) { + return currentTarget; + } + if (!((descriptor->isExtension))) break; + nExts += 1; + nextPC += (descriptor->numBytes); + } + if (/* isUnconditionalBranch */ + (isBranch(descriptor)) + && (!(((descriptor->isBranchTrue)) + || ((descriptor->isBranchFalse))))) { + span = ((descriptor->spanFunction))(descriptor, nextPC, nExts, methodObj); + if (span < 0) { + return currentTarget; + } + + /* Do *not* follow backward branches; these are interrupt points and should not be elided. */ + nextPC = (nextPC + ((descriptor->numBytes))) + span; + } + else { + if (((descriptor->generator)) == genPushConstantTrueBytecode) { + cond = 1; + } + else { + if (((descriptor->generator)) == genPushConstantFalseBytecode) { + cond = 0; + } + else { + return currentTarget; + } + } + + /* Don't step into loops across a pushTrue; jump:if: boundary, so as not to confuse stack depth fixup. */ + if (((fixupAt(nextPC))->isTargetOfBackwardBranch)) { + return currentTarget; + } + nextPC = eventualTargetOf(nextPC + ((descriptor->numBytes))); + nExts = 0; + while (1) { + /* begin generatorForPC: */ + descriptor = generatorAt(bytecodeSetOffset + (fetchByteofObject(nextPC, methodObj))); + if ((descriptor->isReturn)) { + return currentTarget; + } + if (!((descriptor->isExtension))) break; + nExts += 1; + nextPC += (descriptor->numBytes); + } + if (!(isBranch(descriptor))) { + return currentTarget; + } + if (/* isUnconditionalBranch */ + (isBranch(descriptor)) + && (!(((descriptor->isBranchTrue)) + || ((descriptor->isBranchFalse))))) { + return currentTarget; + } + nextPC = (cond == ((descriptor->isBranchTrue)) + ? (nextPC + ((descriptor->numBytes))) + (((descriptor->spanFunction))(descriptor, nextPC, nExts, methodObj)) + : nextPC + ((descriptor->numBytes))); + } + currentTarget = nextPC; + } + return 0; +} + + +/* Spill the closest register on stack not conflicting with regMask. + Assertion Failure if regMask has already all the registers */ + + /* StackToRegisterMappingCogit>>#freeAnyRegNotConflictingWith: */ +static NoDbgRegParms sqInt +freeAnyRegNotConflictingWith(sqInt regMask) +{ + SimStackEntry *desc; + sqInt index; + sqInt reg; + + assert(needsFrame); + reg = NoReg; + index = ((simSpillBase < 0) ? 0 : simSpillBase); + while ((reg == NoReg) + && (index < simStackPtr)) { + desc = simStackAt(index); + if (((desc->type)) == SSRegister) { + if (!(((regMask & (((((desc->registerr)) < 0) ? (((usqInt)(1)) >> (-((desc->registerr)))) : (1ULL << ((desc->registerr)))))) != 0))) { + reg = (desc->registerr); + } + } + index += 1; + } + assert(!((reg == NoReg))); + + /* begin ssAllocateRequiredReg: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)), simStackPtr, simNativeStackPtr); + return reg; +} + + +/* Return from block, assuming result already loaded into ReceiverResultReg. */ +/* Return from block, assuming result already loaded into ReceiverResultReg. */ + + /* StackToRegisterMappingCogit>>#genBlockReturn */ +static sqInt +genBlockReturn(void) +{ + if (needsFrame) { + /* MoveR:R: */ + genoperandoperand(MoveRR, FPReg, SPReg); + + /* PopR: */ + genoperand(PopR, FPReg); + } + + /* RetN: */ + genoperand(RetN, (methodOrBlockNumArgs + 1) * BytesPerWord); + + /* can't fall through */ + deadCode = 1; + return 0; +} + + +/* Generate special versions of the ceCallCogCodePopReceiverAndClassRegs + enilopmart that also pop register args from the stack to undo the pushing + of register args in the abort/miss trampolines. */ + + /* StackToRegisterMappingCogit>>#genCallPICEnilopmartNumArgs: */ +static NoDbgRegParms void +(*genCallPICEnilopmartNumArgs(sqInt numArgs))(void) +{ + sqInt endAddress; + usqInt enilopmart; + sqInt size; + + zeroOpcodeIndex(); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, varBaseAddress, VarBaseReg); + genLoadStackPointers(backEnd); + + /* PopR: */ + genoperand(PopR, ClassReg); + + /* PopR: */ + genoperand(PopR, TempReg); + + /* PopR: */ + genoperand(PopR, SendNumArgsReg); + if (numArgs > 0) { + if (numArgs > 1) { + /* PopR: */ + genoperand(PopR, Arg1Reg); + assert((numRegArgs()) == 2); + } + + /* PopR: */ + genoperand(PopR, Arg0Reg); + } + + /* PopR: */ + genoperand(PopR, ReceiverResultReg); + + /* PushR: */ + genoperand(PushR, SendNumArgsReg); + + /* JumpR: */ + genoperand(JumpR, TempReg); + computeMaximumSizes(); + size = generateInstructionsAt(methodZoneBase); + endAddress = outputInstructionsAt(methodZoneBase); + assert((methodZoneBase + size) == endAddress); + enilopmart = methodZoneBase; + methodZoneBase = alignUptoRoutineBoundary(endAddress); + stopsFromto(backEnd, endAddress, methodZoneBase - 1); + recordGeneratedRunTimeaddress(trampolineNamenumRegArgs("ceCallPIC", numArgs), enilopmart); + return ((void (*)(void)) enilopmart); +} + + +/* SistaV1: ** 248 (2) 11111000 iiiiiiii mssjjjjj Call Primitive #iiiiiiii + + (jjjjj * 256) + m=1 means inlined primitive, no hard return after execution. + ss defines the unsafe operation set used to encode the operations. + (ss = 0 means sista unsafe operations, ss = 01 means lowcode operations, + other numbers are as yet used). + See SistaCogit genCallPrimitiveBytecode, EncoderForSistaV1's class comment + and StackInterpreter>>#callPrimitiveBytecode for more information. */ + + /* StackToRegisterMappingCogit>>#genCallPrimitiveBytecode */ +static sqInt +genCallPrimitiveBytecode(void) +{ + usqInt prim; + usqInt primSet; + + if (byte2 < 128) { + return (bytecodePC == initialPC + ? 0 + : EncounteredUnknownBytecode); + } + prim = ((((usqInt)((byte2 - 128)) << 8))) + byte1; + primSet = ((prim) >> 13) & 3; + prim = prim & 0x1FFF; + return EncounteredUnknownBytecode; +} + + +/* Override to push the register receiver and register arguments, if any. */ + + /* StackToRegisterMappingCogit>>#genExternalizePointersForPrimitiveCall */ +static sqInt +genExternalizePointersForPrimitiveCall(void) +{ + genPushRegisterArgs(); + + /* PopR: */ + genoperand(PopR, TempReg); + + /* #MoveR:Aw: #gen:operand:literal: #checkLiteral:forInstruction: */ + genoperandoperand(MoveRAw, TempReg, instructionPointerAddress()); + + /* Set coInterpreter stackPointer to the topmost argument, skipping the return address. */ + return genSaveStackPointers(backEnd); +} + + +/* Override to push the register receiver and register arguments, if any. */ + + /* StackToRegisterMappingCogit>>#genExternalizeStackPointerForFastPrimitiveCall */ +static AbstractInstruction * +genExternalizeStackPointerForFastPrimitiveCall(void) +{ + genPushRegisterArgs(); + gAddCqRR(BytesPerWord, SPReg, TempReg); + return genoperandoperand(MoveRAw, TempReg, stackPointerAddress()); +} + + +/* Block compilation. At this point in the method create the block. Note its + start and defer generating code for it until after the method and any + other preceding + blocks. The block's actual code will be compiled later. */ +/* 253 11111101 eei i i kkk jjjjjjjj Push Closure Num Copied iii (+ Ext A + // 16 * 8) Num Args kkk (+ Ext A \\ 16 * 8) BlockSize jjjjjjjj (+ Ext B * + 256). ee = num extensions + */ + + /* StackToRegisterMappingCogit>>#genExtPushClosureBytecode */ +static sqInt +genExtPushClosureBytecode(void) +{ + sqInt i; + usqInt numArgs; + usqInt numCopied; + sqInt reg; + sqInt startpc; + + assert(needsFrame); + startpc = bytecodePC + (((generatorAt(byte0))->numBytes)); + addBlockStartAtnumArgsnumCopiedspan(startpc, (numArgs = (byte1 & 7) + ((extA % 16) * 8)), (numCopied = ((((usqInt)(byte1)) >> 3) & 7) + ((extA / 16) * 8)), byte2 + ((((usqInt)(extB) << 8)))); + + /* 0 relative */ + extA = (numExtB = (extB = 0)); + + /* begin genInlineClosure:numArgs:numCopied: */ + assert(getActiveContextAllocatesInMachineCode()); + voidReceiverResultRegContainsSelf(); + + /* begin ssAllocateCallReg:and:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(CallerSavedRegisterMask | (((1U << ReceiverResultReg) | (1U << SendNumArgsReg)) | (1U << ClassReg)), simStackPtr, simNativeStackPtr); + genNoPopCreateClosureAtnumArgsnumCopiedcontextNumArgslargeinBlock(startpc + 1, numArgs, numCopied, methodOrBlockNumArgs, methodNeedsLargeContext(methodObj), inBlock); + + /* 1 relative */ + for (i = 1; i <= numCopied; i += 1) { + reg = ssStorePoptoPreferredReg(1, TempReg); + genStoreSourceRegslotIndexintoNewObjectInDestReg(reg, (ClosureFirstCopiedValueIndex + numCopied) - i, ReceiverResultReg); + } + ssPushRegister(ReceiverResultReg); + return 0; +} + + +/* Full Block creation compilation. The block's actual code will be compiled + separatedly. + */ +/* * 255 11111111 xxxxxxxx siyyyyyy push Closure Compiled block literal + index xxxxxxxx (+ Extend A * 256) numCopied yyyyyy receiverOnStack: s = 1 + ignoreOuterContext: i = 1 + */ + + /* StackToRegisterMappingCogit>>#genExtPushFullClosureBytecode */ +static sqInt +genExtPushFullClosureBytecode(void) +{ + sqInt compiledBlock; + sqInt i; + int ignoreContext; + sqInt numCopied; + int receiverIsOnStack; + sqInt reg; + SimStackEntry *selfOfStoreToReg; + + assert(needsFrame); + compiledBlock = getLiteral(byte1 + ((((usqInt)(extA) << 8)))); + extA = 0; + numCopied = byte2 & (0x3F); + receiverIsOnStack = ((byte2 & (128)) != 0); + ignoreContext = ((byte2 & (64)) != 0); + voidReceiverResultRegContainsSelf(); + + /* begin ssAllocateCallReg:and:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(CallerSavedRegisterMask | (((1U << ReceiverResultReg) | (1U << SendNumArgsReg)) | (1U << ClassReg)), simStackPtr, simNativeStackPtr); + genCreateFullClosurenumArgsnumCopiedignoreContextcontextNumArgslargeinBlock(compiledBlock, argumentCountOf(compiledBlock), numCopied, ignoreContext, methodOrBlockNumArgs, methodNeedsLargeContext(methodObj), inBlock); + + /* Closure in ReceiverResultReg */ + for (i = 1; i <= numCopied; i += 1) { + reg = ssStorePoptoPreferredReg(1, TempReg); + genStoreSourceRegslotIndexintoNewObjectInDestReg(reg, (FullClosureFirstCopiedValueIndex + numCopied) - i, ReceiverResultReg); + } + if (receiverIsOnStack) { + reg = ssStorePoptoPreferredReg(1, TempReg); + } + else { + selfOfStoreToReg = simSelf(); + storeToReg(selfOfStoreToReg, (reg = TempReg)); + } + genStoreSourceRegslotIndexintoNewObjectInDestReg(reg, FullClosureReceiverIndex, ReceiverResultReg); + ssPushRegister(ReceiverResultReg); + return 0; +} + + +/* Enilopmarts transfer control from C into machine code (backwards + trampolines). + */ +/* Enilopmarts transfer control from C into machine code (backwards + trampolines). Override to add version for generic and PIC-specific entry + with reg args. */ + + /* StackToRegisterMappingCogit>>#generateEnilopmarts */ +static void +generateEnilopmarts(void) +{ + +# if Debug + /* begin genEnilopmartFor:forCall:called: */ + realCEEnterCogCodePopReceiverReg = genEnilopmartForandandforCallcalled(ReceiverResultReg, NoReg, NoReg, 0, "realCEEnterCogCodePopReceiverReg"); + ceEnterCogCodePopReceiverReg = enterCogCodePopReceiver; + + /* begin genEnilopmartFor:forCall:called: */ + realCECallCogCodePopReceiverReg = genEnilopmartForandandforCallcalled(ReceiverResultReg, NoReg, NoReg, 1, "realCECallCogCodePopReceiverReg"); + ceCallCogCodePopReceiverReg = callCogCodePopReceiver; + + /* begin genEnilopmartFor:and:forCall:called: */ + realCECallCogCodePopReceiverAndClassRegs = genEnilopmartForandandforCallcalled(ReceiverResultReg, ClassReg, NoReg, 1, "realCECallCogCodePopReceiverAndClassRegs"); + ceCallCogCodePopReceiverAndClassRegs = callCogCodePopReceiverAndClassRegs; +# else // Debug + /* begin genEnilopmartFor:forCall:called: */ + ceEnterCogCodePopReceiverReg = genEnilopmartForandandforCallcalled(ReceiverResultReg, NoReg, NoReg, 0, "ceEnterCogCodePopReceiverReg"); + ceCallCogCodePopReceiverReg = genEnilopmartForandandforCallcalled(ReceiverResultReg, NoReg, NoReg, 1, "ceCallCogCodePopReceiverReg"); + ceCallCogCodePopReceiverAndClassRegs = genEnilopmartForandandforCallcalled(ReceiverResultReg, ClassReg, NoReg, 1, "ceCallCogCodePopReceiverAndClassRegs"); +# endif // Debug + + genPrimReturnEnterCogCodeEnilopmart(0); + cePrimReturnEnterCogCode = methodZoneBase; + outputInstructionsForGeneratedRuntimeAt(cePrimReturnEnterCogCode); + recordGeneratedRunTimeaddress("cePrimReturnEnterCogCode", cePrimReturnEnterCogCode); + genPrimReturnEnterCogCodeEnilopmart(1); + cePrimReturnEnterCogCodeProfiling = methodZoneBase; + outputInstructionsForGeneratedRuntimeAt(cePrimReturnEnterCogCodeProfiling); + recordGeneratedRunTimeaddress("cePrimReturnEnterCogCodeProfiling", cePrimReturnEnterCogCodeProfiling); +# if Debug + /* begin genEnilopmartFor:and:forCall:called: */ + realCECallCogCodePopReceiverArg0Regs = genEnilopmartForandandforCallcalled(ReceiverResultReg, Arg0Reg, NoReg, 1, "realCECallCogCodePopReceiverArg0Regs"); + ceCallCogCodePopReceiverArg0Regs = callCogCodePopReceiverArg0Regs; + realCECallCogCodePopReceiverArg1Arg0Regs = genEnilopmartForandandforCallcalled(ReceiverResultReg, Arg0Reg, Arg1Reg, 1, "realCECallCogCodePopReceiverArg1Arg0Regs"); + ceCallCogCodePopReceiverArg1Arg0Regs = callCogCodePopReceiverArg1Arg0Regs; +# else // Debug + /* begin genEnilopmartFor:and:forCall:called: */ + ceCallCogCodePopReceiverArg0Regs = genEnilopmartForandandforCallcalled(ReceiverResultReg, Arg0Reg, NoReg, 1, "ceCallCogCodePopReceiverArg0Regs"); + ceCallCogCodePopReceiverArg1Arg0Regs = genEnilopmartForandandforCallcalled(ReceiverResultReg, Arg0Reg, Arg1Reg, 1, "ceCallCogCodePopReceiverArg1Arg0Regs"); +# endif // Debug + + + /* These are special versions of the ceCallCogCodePopReceiverAndClassRegs enilopmart that also + pop register args from the stack to undo the pushing of register args in the abort/miss trampolines. */ + ceCall0ArgsPIC = genCallPICEnilopmartNumArgs(0); + if ((numRegArgs()) >= 1) { + ceCall1ArgsPIC = genCallPICEnilopmartNumArgs(1); + if ((numRegArgs()) >= 2) { + ceCall2ArgsPIC = genCallPICEnilopmartNumArgs(2); + assert((numRegArgs()) == 2); + } + } +} + + +/* Size pc-dependent instructions and assign eventual addresses to all + instructions. Answer the size of the code. + Compute forward branches based on virtual address (abstract code starts at + 0), assuming that any branches branched over are long. + Compute backward branches based on actual address. + Reuse the fixups array to record the pc-dependent instructions that need + to have + their code generation postponed until after the others. + + Override to add handling for null branches (branches to the immediately + following instruction) occasioned by StackToRegisterMapping's following of + jumps. */ + + /* StackToRegisterMappingCogit>>#generateInstructionsAt: */ +static NoDbgRegParms sqInt +generateInstructionsAt(sqInt eventualAbsoluteAddress) +{ + sqInt absoluteAddress; + AbstractInstruction *abstractInstruction; + BytecodeFixup *fixup; + sqInt i; + sqInt j; + sqInt pcDependentIndex; + + absoluteAddress = eventualAbsoluteAddress; + pcDependentIndex = 0; + for (i = 0; i < opcodeIndex; i += 1) { + maybeBreakGeneratingInstructionWithIndex(i); + abstractInstruction = abstractInstructionAt(i); + + /* N.B. if you want to break in resizing, break here, note the instruction index, back up to the + sender, restart, and step into computeMaximumSizes, breaking at this instruction's index. */ + maybeBreakGeneratingFromto(absoluteAddress, absoluteAddress + ((abstractInstruction->maxSize))); + if (isPCDependent(abstractInstruction)) { + sizePCDependentInstructionAt(abstractInstruction, absoluteAddress); + if ((isJump(abstractInstruction)) + && ((((i + 1) < opcodeIndex) + && ((((AbstractInstruction *) (((abstractInstruction->operands))[0]))) == (abstractInstructionAt(i + 1)))) + || (((i + 2) < opcodeIndex) + && (((((AbstractInstruction *) (((abstractInstruction->operands))[0]))) == (abstractInstructionAt(i + 2))) + && ((((abstractInstructionAt(i + 1))->opcode)) == Nop))))) { + (abstractInstruction->opcode = Nop); + concretizeAt(abstractInstruction, absoluteAddress); + } + else { + fixup = fixupAtIndex(pcDependentIndex); + pcDependentIndex += 1; + (fixup->instructionIndex = i); + } + absoluteAddress += (abstractInstruction->machineCodeSize); + } + else { + absoluteAddress = concretizeAt(abstractInstruction, absoluteAddress); + + /* N.B. if you want to break in resizing, break here, note the instruction index, back up to the + sender, restart, and step into computeMaximumSizes, breaking at this instruction's index. */ + assert(((abstractInstruction->machineCodeSize)) == ((abstractInstruction->maxSize))); + } + } + for (j = 0; j < pcDependentIndex; j += 1) { + fixup = fixupAtIndex(j); + maybeBreakGeneratingInstructionWithIndex((fixup->instructionIndex)); + abstractInstruction = abstractInstructionAt((fixup->instructionIndex)); + maybeBreakGeneratingFromto((abstractInstruction->address), (((abstractInstruction->address)) + ((abstractInstruction->maxSize))) - 1); + concretizeAt(abstractInstruction, (abstractInstruction->address)); + } + return absoluteAddress - eventualAbsoluteAddress; +} + + +/* Generate the run-time entries for the various method and PIC entry misses + and aborts. + Read the class-side method trampolines for documentation on the various + trampolines + */ + + /* StackToRegisterMappingCogit>>#generateMissAbortTrampolines */ +static void +generateMissAbortTrampolines(void) +{ + sqInt numArgs; + sqInt toDoLimit; + sqInt toDoLimit1; + sqInt toDoLimit2; + + toDoLimit = (numRegArgs()) + 1; + for (numArgs = 0; numArgs <= toDoLimit; numArgs += 1) { + methodAbortTrampolines[numArgs] = (genMethodAbortTrampolineFor(numArgs)); + } + toDoLimit1 = (numRegArgs()) + 1; + for (numArgs = 0; numArgs <= toDoLimit1; numArgs += 1) { + picAbortTrampolines[numArgs] = (genPICAbortTrampolineFor(numArgs)); + } + toDoLimit2 = (numRegArgs()) + 1; + for (numArgs = 0; numArgs <= toDoLimit2; numArgs += 1) { + picMissTrampolines[numArgs] = (genPICMissTrampolineFor(numArgs)); + } + ceReapAndResetErrorCodeTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceReapAndResetErrorCodeFor, "ceReapAndResetErrorCodeTrampoline", 1, ClassReg, null, null, null, 0 /* emptyRegisterMask */, 1, NoReg, 0); +} + + +/* Override to generate code to push the register arg(s) for <= numRegArg + arity sends. + */ + + /* StackToRegisterMappingCogit>>#generateSendTrampolines */ +static void +generateSendTrampolines(void) +{ + sqInt numArgs; + + for (numArgs = 0; numArgs < NumSendTrampolines; numArgs += 1) { + ordinarySendTrampolines[numArgs] = (genSendTrampolineFornumArgscalledargargargarg( + ceSendsupertonumArgs, + numArgs, + trampolineNamenumArgs("ceSend", numArgs), + ClassReg, + (/* begin trampolineArgConstant: */ + assert(0 >= 0), + -2 - 0), + ReceiverResultReg, + /* numArgsOrSendNumArgsReg: */ + (numArgs <= (NumSendTrampolines - 2) + ? (/* begin trampolineArgConstant: */ + assert(numArgs >= 0), + -2 - numArgs) + : SendNumArgsReg))); + } + + /* Generate these in the middle so they are within [firstSend, lastSend]. */ + for (numArgs = 0; numArgs < NumSendTrampolines; numArgs += 1) { + directedSuperSendTrampolines[numArgs] = (genSendTrampolineFornumArgscalledargargargarg(ceSendabovetonumArgs, numArgs, trampolineNamenumArgs("ceDirectedSuperSend", numArgs), ClassReg, TempReg, ReceiverResultReg, /* numArgsOrSendNumArgsReg: */ + (numArgs <= (NumSendTrampolines - 2) + ? (/* begin trampolineArgConstant: */ + assert(numArgs >= 0), + -2 - numArgs) + : SendNumArgsReg))); + directedSuperBindingSendTrampolines[numArgs] = (genSendTrampolineFornumArgscalledargargargarg(ceSendaboveClassBindingtonumArgs, numArgs, trampolineNamenumArgs("ceDirectedSuperBindingSend", numArgs), ClassReg, TempReg, ReceiverResultReg, /* numArgsOrSendNumArgsReg: */ + (numArgs <= (NumSendTrampolines - 2) + ? (/* begin trampolineArgConstant: */ + assert(numArgs >= 0), + -2 - numArgs) + : SendNumArgsReg))); + } + for (numArgs = 0; numArgs < NumSendTrampolines; numArgs += 1) { + superSendTrampolines[numArgs] = (genSendTrampolineFornumArgscalledargargargarg( + ceSendsupertonumArgs, + numArgs, + trampolineNamenumArgs("ceSuperSend", numArgs), + ClassReg, + (/* begin trampolineArgConstant: */ + assert(1 >= 0), + -2 - 1), + ReceiverResultReg, + /* numArgsOrSendNumArgsReg: */ + (numArgs <= (NumSendTrampolines - 2) + ? (/* begin trampolineArgConstant: */ + assert(numArgs >= 0), + -2 - numArgs) + : SendNumArgsReg))); + } + firstSend = ordinarySendTrampolines[0]; + lastSend = superSendTrampolines[NumSendTrampolines - 1]; +} + + +/* Generate trampolines for tracing. In the simulator we can save a lot of + time and avoid noise instructions in the lastNInstructions log by + short-cutting these + trampolines, but we need them in the real vm. */ + + /* StackToRegisterMappingCogit>>#generateTracingTrampolines */ +static void +generateTracingTrampolines(void) +{ + ceTraceLinkedSendTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceTraceLinkedSend, "ceTraceLinkedSendTrampoline", 1, ReceiverResultReg, null, null, null, CallerSavedRegisterMask, 1, NoReg, 0); + ceTraceBlockActivationTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceTraceBlockActivation, "ceTraceBlockActivationTrampoline", 0, null, null, null, null, CallerSavedRegisterMask, 1, NoReg, 0); + ceTraceStoreTrampoline = genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceTraceStoreOfinto, "ceTraceStoreTrampoline", 2, TempReg, ReceiverResultReg, null, null, CallerSavedRegisterMask, 1, NoReg, 0); +} + + /* StackToRegisterMappingCogit>>#genForwardersInlinedIdenticalOrNotIf: */ +static NoDbgRegParms sqInt +genForwardersInlinedIdenticalOrNotIf(sqInt orNot) +{ + sqInt argConstant; + sqInt argNeedsReg; + sqInt argReg; + sqInt argRegSqInt; + BytecodeDescriptor *branchDescriptor; + BytecodeDescriptor *branchDescriptor1; + BytecodeFixup *fixup; + sqInt i; + void *jumpTarget; + AbstractInstruction *label; + sqInt nExts; + sqInt nextPC; + sqInt nextPCSqInt; + sqInt postBranchPC; + sqInt postBranchPCSqInt; + BytecodeDescriptor *primDescriptor; + sqInt rcvrConstant; + sqInt rcvrNeedsReg; + sqInt rcvrReg; + sqInt rcvrRegSqInt; + sqInt reg; + sqInt rNextSqInt; + sqInt rTopSqInt; + BytecodeFixup *self_in_CogBytecodeFixup; + SimStackEntry *simStackEntry; + sqInt targetBytecodePC; + sqInt targetBytecodePCSqInt; + sqInt topRegistersMask; + sqInt unforwardArg; + sqInt unforwardRcvr; + + unforwardRcvr = mayBeAForwarder(ssValue(1)); + unforwardArg = mayBeAForwarder(ssTop()); + if ((!unforwardRcvr) + && (!unforwardArg)) { + return genVanillaInlinedIdenticalOrNotIf(orNot); + } + assert(unforwardArg + || (unforwardRcvr)); + + /* We use reg for non annotable constants to avoid duplicating objRef. */ + + /* begin ssValue: */ + simStackEntry = simStackAt(simStackPtr - 1); + rcvrConstant = (((simStackEntry->type)) == SSConstant) + && ((isImmediate((simStackEntry->constant))) + || (!(/* shouldAnnotateObjectReference: */ + (isNonImmediate((simStackEntry->constant))) + && ((oopisGreaterThan((simStackEntry->constant), classTableRootObj())) + || (oopisLessThan((simStackEntry->constant), nilObject())))))); + + /* begin ssTop */ + simStackEntry = simStackAt(simStackPtr); + argConstant = (((simStackEntry->type)) == SSConstant) + && ((isImmediate((simStackEntry->constant))) + || (!(/* shouldAnnotateObjectReference: */ + (isNonImmediate((simStackEntry->constant))) + && ((oopisGreaterThan((simStackEntry->constant), classTableRootObj())) + || (oopisLessThan((simStackEntry->constant), nilObject())))))); + + /* begin extractMaybeBranchDescriptorInto: */ + branchDescriptor1 = ((BytecodeDescriptor *) 0); + primDescriptor = generatorAt(byte0); + nextPCSqInt = bytecodePC + ((primDescriptor->numBytes)); + nExts = 0; + while (1) { + while (1) { + /* begin generatorForPC: */ + branchDescriptor1 = generatorAt(bytecodeSetOffset + (fetchByteofObject(nextPCSqInt, methodObj))); + if (!((branchDescriptor1->isExtension))) break; + nExts += 1; + nextPCSqInt += (branchDescriptor1->numBytes); + } + if (!(/* isUnconditionalBranch */ + (isBranch(branchDescriptor1)) + && (!(((branchDescriptor1->isBranchTrue)) + || ((branchDescriptor1->isBranchFalse)))))) break; + nextPCSqInt = eventualTargetOf((nextPCSqInt + ((branchDescriptor1->numBytes))) + (((branchDescriptor1->spanFunction))(branchDescriptor1, nextPCSqInt, nExts, methodObj))); + } + targetBytecodePCSqInt = (postBranchPCSqInt = 0); + if (((branchDescriptor1->isBranchTrue)) + || ((branchDescriptor1->isBranchFalse))) { + targetBytecodePCSqInt = eventualTargetOf((nextPCSqInt + ((branchDescriptor1->numBytes))) + (((branchDescriptor1->spanFunction))(branchDescriptor1, nextPCSqInt, nExts, methodObj))); + postBranchPCSqInt = eventualTargetOf(nextPCSqInt + ((branchDescriptor1->numBytes))); + } + else { + nextPCSqInt = bytecodePC + ((primDescriptor->numBytes)); + } + branchDescriptor = branchDescriptor1; + nextPC = nextPCSqInt; + postBranchPC = postBranchPCSqInt; + targetBytecodePC = targetBytecodePCSqInt; + argNeedsReg = !argConstant; + rcvrNeedsReg = !rcvrConstant; + + /* begin allocateEqualsEqualsRegistersArgNeedsReg:rcvrNeedsReg:into: */ + assert(argNeedsReg + || (rcvrNeedsReg)); + argRegSqInt = (rcvrRegSqInt = NoReg); + if (argNeedsReg) { + if (rcvrNeedsReg) { + /* begin allocateRegForStackTopTwoEntriesInto: */ + topRegistersMask = 0; + rTopSqInt = (rNextSqInt = NoReg); + if ((registerOrNone(ssTop())) != NoReg) { + rTopSqInt = registerOrNone(ssTop()); + } + if ((registerOrNone(ssValue(1))) != NoReg) { + reg = (rNextSqInt = registerOrNone(ssValue(1))); + + /* begin registerMaskFor: */ + topRegistersMask = ((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)); + } + if (rTopSqInt == NoReg) { + rTopSqInt = allocateRegNotConflictingWith(topRegistersMask); + } + if (rNextSqInt == NoReg) { + rNextSqInt = allocateRegNotConflictingWith(((rTopSqInt < 0) ? (((usqInt)(1)) >> (-rTopSqInt)) : (1ULL << rTopSqInt))); + } + assert(!(((rTopSqInt == NoReg) + || (rNextSqInt == NoReg)))); + argRegSqInt = rTopSqInt; + rcvrRegSqInt = rNextSqInt; + popToReg(ssTop(), argRegSqInt); + popToReg(ssValue(1), rcvrRegSqInt); + } + else { + argRegSqInt = allocateRegForStackEntryAtnotConflictingWith(0, 0); + popToReg(ssTop(), argRegSqInt); + + /* If the receiver is a spilled constant we need to pop it from the stack. */ + if (((ssValue(1))->spilled)) { + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BytesPerWord, SPReg); + } + } + } + else { + assert(rcvrNeedsReg); + assert(!((((ssTop())->spilled)))); + rcvrRegSqInt = allocateRegForStackEntryAtnotConflictingWith(1, 0); + popToReg(ssValue(1), rcvrRegSqInt); + } + assert(!((argNeedsReg + && (argRegSqInt == NoReg)))); + assert(!((rcvrNeedsReg + && (rcvrRegSqInt == NoReg)))); + rcvrReg = rcvrRegSqInt; + argReg = argRegSqInt; + + /* If not followed by a branch, resolve to true or false. */ + if (!(((branchDescriptor->isBranchTrue)) + || ((branchDescriptor->isBranchFalse)))) { + return genIdenticalNoBranchArgIsConstantrcvrIsConstantargRegrcvrRegorNotIf(argConstant, rcvrConstant, argReg, rcvrReg, orNot); + } + + /* If branching the stack must be flushed for the merge */ + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= (simStackPtr - 2)) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < (simStackPtr - 2)) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : (simStackPtr - 2))); i <= (simStackPtr - 2); i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = (simStackPtr - 2) + 1; + } + label = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin genCmpArgIsConstant:rcvrIsConstant:argReg:rcvrReg: */ + assert((argReg != NoReg) + || (rcvrReg != NoReg)); + if (argConstant) { + /* genCmpConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject())))) { + annotateobjRef(genoperandoperand(CmpCwR, ((ssTop())->constant), rcvrReg), ((ssTop())->constant)); + } + else { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ((ssTop())->constant), rcvrReg); + } + } + else { + if (rcvrConstant) { + /* genCmpConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssValue(1))->constant))) + && ((oopisGreaterThan(((ssValue(1))->constant), classTableRootObj())) + || (oopisLessThan(((ssValue(1))->constant), nilObject())))) { + annotateobjRef(genoperandoperand(CmpCwR, ((ssValue(1))->constant), argReg), ((ssValue(1))->constant)); + } + else { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ((ssValue(1))->constant), argReg); + } + } + else { + /* begin CmpR:R: */ + assert(!((argReg == SPReg))); + genoperandoperand(CmpRR, argReg, rcvrReg); + } + } + ssPop(2); + + /* Since there is a following conditional jump bytecode (unless there is deadCode), + define non-merge fixups and leave the cond bytecode to set the mergeness. */ + + /* begin fixupAt: */ + self_in_CogBytecodeFixup = fixupAtIndex(nextPC - initialPC); + if ((self_in_CogBytecodeFixup->targetInstruction)) { + assert(!(deadCode)); + } + else { + deadCode = 1; + ensureFixupAt(targetBytecodePC); + ensureFixupAt(postBranchPC); + } + if (orNot == ((branchDescriptor->isBranchTrue))) { + fixup = ensureNonMergeFixupAt(targetBytecodePC); + jumpTarget = ensureNonMergeFixupAt(postBranchPC); + + /* begin JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)jumpTarget)); + } + else { + fixup = ensureNonMergeFixupAt(postBranchPC); + jumpTarget = ensureNonMergeFixupAt(targetBytecodePC); + + /* begin JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)jumpTarget)); + } + + /* orNot is true for ~~ + a == b ifTrue: ... or a ~~ b ifFalse: ... jump on equal to target pc + a == b ifFalse: ... or a ~~ b ifTrue: ... jump on equal to post-branch pc + The forwarders checks need to jump back to the comparison (label) if a forwarder is found, else + jump forward either to the next forwarder check or to the postBranch or branch target (fixup). */ + if (unforwardArg + && (unforwardRcvr)) { + /* genEnsureOopInRegNotForwarded:scratchReg:jumpBackTo: */ + genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(argReg, TempReg, label, 0); + } + genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder((unforwardRcvr + ? rcvrReg + : argReg), TempReg, label, fixup); + + /* Not reached, execution flow has jumped to fixup */ + if (!deadCode) { + ssPushConstant(trueObject()); + } + return 0; +} + + +/* Generates the machine code for #== in the case where the instruction is + not followed by a branch + */ + + /* StackToRegisterMappingCogit>>#genIdenticalNoBranchArgIsConstant:rcvrIsConstant:argReg:rcvrReg:orNotIf: */ +static NoDbgRegParms sqInt +genIdenticalNoBranchArgIsConstantrcvrIsConstantargRegrcvrRegorNotIf(sqInt argIsConstant, sqInt rcvrIsConstant, sqInt argReg, sqInt rcvrRegOrNone, sqInt orNot) +{ + AbstractInstruction *jumpEqual; + AbstractInstruction *jumpNotEqual; + AbstractInstruction *label; + sqInt resultReg; + + label = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin genCmpArgIsConstant:rcvrIsConstant:argReg:rcvrReg: */ + assert((argReg != NoReg) + || (rcvrRegOrNone != NoReg)); + if (argIsConstant) { + /* genCmpConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject())))) { + annotateobjRef(genoperandoperand(CmpCwR, ((ssTop())->constant), rcvrRegOrNone), ((ssTop())->constant)); + } + else { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ((ssTop())->constant), rcvrRegOrNone); + } + } + else { + if (rcvrIsConstant) { + /* genCmpConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssValue(1))->constant))) + && ((oopisGreaterThan(((ssValue(1))->constant), classTableRootObj())) + || (oopisLessThan(((ssValue(1))->constant), nilObject())))) { + annotateobjRef(genoperandoperand(CmpCwR, ((ssValue(1))->constant), argReg), ((ssValue(1))->constant)); + } + else { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ((ssValue(1))->constant), argReg); + } + } + else { + /* begin CmpR:R: */ + assert(!((argReg == SPReg))); + genoperandoperand(CmpRR, argReg, rcvrRegOrNone); + } + } + ssPop(2); + resultReg = (rcvrRegOrNone == NoReg + ? argReg + : rcvrRegOrNone); + jumpEqual = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + if (!argIsConstant) { + /* genEnsureOopInRegNotForwarded:scratchReg:jumpBackTo: */ + genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(argReg, TempReg, label, 0); + } + if (!rcvrIsConstant) { + /* genEnsureOopInRegNotForwarded:scratchReg:jumpBackTo: */ + genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(rcvrRegOrNone, TempReg, label, 0); + } + if (orNot) { + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, trueObject(), resultReg); + } + else { + /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, falseObject(), resultReg); + } + jumpNotEqual = genoperand(Jump, ((sqInt)0)); + jmpTarget(jumpEqual, (orNot + ? /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, falseObject(), resultReg) + : /* genMoveConstant:R: */ + genoperandoperand(MoveCqR, trueObject(), resultReg))); + jmpTarget(jumpNotEqual, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + ssPushRegister(resultReg); + return 0; +} + + +/* Decompose code generation for #== into a common constant-folding version, + followed by a double dispatch through the objectRepresentation to a + version that doesn't deal with forwarders and a version that does. */ + + /* StackToRegisterMappingCogit>>#genInlinedIdenticalOrNotIf: */ +static NoDbgRegParms sqInt +genInlinedIdenticalOrNotIf(sqInt orNot) +{ + BytecodeDescriptor *primDescriptor; + sqInt result; + + primDescriptor = generatorAt(byte0); + if ((isUnannotatableConstant(ssTop())) + && (isUnannotatableConstant(ssValue(1)))) { + assert(!((primDescriptor->isMapped))); + if ((orNot + ? (((ssTop())->constant)) != (((ssValue(1))->constant)) + : (((ssTop())->constant)) == (((ssValue(1))->constant)))) { + result = trueObject(); + } + else { + result = falseObject(); + } + ssPop(2); + return ssPushConstant(result); + } + return genForwardersInlinedIdenticalOrNotIf(orNot); +} + + /* StackToRegisterMappingCogit>>#genJumpBackTo: */ +static NoDbgRegParms sqInt +genJumpBackTo(sqInt targetBytecodePC) +{ + AbstractInstruction *abstractInstruction; + sqInt i; + void *jumpTarget; + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + + /* can't fall through */ + deadCode = 1; + + /* #MoveAw:R: #gen:literal:operand: #checkLiteral:forInstruction: */ + genoperandoperand(MoveAwR, stackLimitAddress(), TempReg); + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, SPReg); + + /* begin fixupAt: */ + jumpTarget = fixupAtIndex(targetBytecodePC - initialPC); + genConditionalBranchoperand(JumpAboveOrEqual, ((sqInt)jumpTarget)); + abstractInstruction = genoperand(Call, ceCheckForInterruptTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + + /* begin fixupAt: */ + jumpTarget = fixupAtIndex(targetBytecodePC - initialPC); + genoperand(Jump, ((sqInt)jumpTarget)); + return 0; +} + + /* StackToRegisterMappingCogit>>#genJumpIf:to: */ +static NoDbgRegParms sqInt +genJumpIfto(sqInt boolean, sqInt targetBytecodePC) +{ + AbstractInstruction *abstractInstruction; + SimStackEntry *desc; + sqInt eventualTarget; + BytecodeFixup *fixup; + sqInt i; + void *jumpTarget; + AbstractInstruction *ok; + sqInt quickConstant; + + eventualTarget = eventualTargetOf(targetBytecodePC); + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= (simStackPtr - 1)) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < (simStackPtr - 1)) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : (simStackPtr - 1))); i < simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = (simStackPtr - 1) + 1; + } + + /* begin ssTop */ + desc = simStackAt(simStackPtr); + ssPop(1); + if (/* stackEntryIsBoolean: */ + (((desc->type)) == SSConstant) + && ((((desc->constant)) == (trueObject())) + || (((desc->constant)) == (falseObject())))) { + /* Must arrange there's a fixup at the target whether it is jumped to or + not so that the simStackPtr can be kept correct. */ + fixup = ensureFixupAt(eventualTarget); + + /* Must annotate the bytecode for correct pc mapping. */ + abstractInstruction = (((desc->constant)) == boolean + ? genoperand(Jump, ((sqInt)fixup)) + : (prevInstIsPCAnnotated() + ? gen(Nop) + : genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + extA = 0; + return 0; + } + popToReg(desc, TempReg); + + /* Cunning trick by LPD. If true and false are contiguous subtract the smaller. + Correct result is either 0 or the distance between them. If result is not 0 or + their distance send mustBeBoolean. */ + assert((objectAfter(falseObject())) == (trueObject())); + + /* genSubConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(boolean)) + && ((oopisGreaterThan(boolean, classTableRootObj())) + || (oopisLessThan(boolean, nilObject())))) { + annotateobjRef(genoperandoperand(SubCwR, boolean, TempReg), TempReg); + } + else { + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, boolean, TempReg); + } + jumpTarget = ensureFixupAt(eventualTarget); + + /* begin JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)jumpTarget)); + if (((extA & 1) != 0)) { + extA = 0; + abstractInstruction = lastOpcode(); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + return 0; + } + extA = 0; + quickConstant = (boolean == (falseObject()) + ? (trueObject()) - (falseObject()) + : (falseObject()) - (trueObject())); + + /* begin CmpCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, quickConstant, TempReg); + ok = genConditionalBranchoperand(JumpZero, ((sqInt)0)); + genCallMustBeBooleanFor(boolean); + jmpTarget(ok, annotateBytecode(genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + return 0; +} + + /* StackToRegisterMappingCogit>>#genJumpTo: */ +static NoDbgRegParms sqInt +genJumpTo(sqInt targetBytecodePC) +{ + sqInt eventualTarget; + BytecodeFixup *fixup; + BytecodeDescriptor *generator; + sqInt i; + + eventualTarget = eventualTargetOf(targetBytecodePC); + if ((eventualTarget > bytecodePC) + && ((/* stackTopIsBoolean */ + (simStackPtr >= methodOrBlockNumArgs) + && (stackEntryIsBoolean(ssTop()))) + && (isConditionalBranch(generator = generatorForPC(eventualTarget))))) { + eventualTarget = (eventualTarget + ((generator->numBytes))) + ((((generator->isBranchTrue)) == ((((ssTop())->constant)) == (trueObject())) + ? ((generator->spanFunction))(generator, eventualTarget, 0, methodObj) + : 0)); + ssPop(1); + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + fixup = ensureFixupAt(eventualTarget); + ssPop(-1); + } + else { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + fixup = ensureFixupAt(eventualTarget); + } + + /* can't fall through */ + deadCode = 1; + + /* Jump: */ + genoperand(Jump, ((sqInt)fixup)); + return 0; +} + + /* StackToRegisterMappingCogit>>#genMarshalledSend:numArgs:sendTable: */ +static NoDbgRegParms sqInt +genMarshalledSendnumArgssendTable(sqInt selectorIndex, sqInt numArgs, sqInt *sendTable) +{ + sqInt annotation; + AbstractInstruction *instruction; + + assert(needsFrame); + + /* begin annotationForSendTable: */ + if (sendTable == ordinarySendTrampolines) { + annotation = IsSendCall; + goto l1; + } + if (sendTable == directedSuperSendTrampolines) { + annotation = IsDirectedSuperSend; + goto l1; + } + if (sendTable == directedSuperBindingSendTrampolines) { + annotation = IsDirectedSuperBindingSend; + goto l1; + } + assert(sendTable == superSendTrampolines); + annotation = IsSuperSend; + /* end annotationForSendTable: */ +l1: + + /* Deal with stale super sends; see SpurMemoryManager's class comment. */ + if (/* annotationIsForUncheckedEntryPoint: */ + (annotation == IsSuperSend) + || (((annotation >= IsDirectedSuperSend) && (annotation <= IsDirectedSuperBindingSend)))) { + /* begin genEnsureOopInRegNotForwarded:scratchReg: */ + instruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + genEnsureOopInRegNotForwardedscratchRegifForwarderifNotForwarder(ReceiverResultReg, TempReg, instruction, 0); + } + + /* 0 through (NumSendTrampolines - 2) numArgs sends have the arg count implciti in the trampoline. + The last send trampoline (NumSendTrampolines - 1) passes numArgs in SendNumArgsReg. */ + if (numArgs >= (NumSendTrampolines - 1)) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, numArgs, SendNumArgsReg); + } + if (((annotation >= IsDirectedSuperSend) && (annotation <= IsDirectedSuperBindingSend))) { + /* genMoveConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(tempOop)) + && ((oopisGreaterThan(tempOop, classTableRootObj())) + || (oopisLessThan(tempOop, nilObject())))) { + annotateobjRef(genoperandoperand(MoveCwR, tempOop, TempReg), tempOop); + } + else { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, tempOop, TempReg); + } + } + genLoadInlineCacheWithSelector(selectorIndex); + ((genoperand(Call, sendTable[((numArgs < (NumSendTrampolines - 1)) ? numArgs : (NumSendTrampolines - 1))]))->annotation = annotation); + + /* begin voidReceiverOptStatus */ + ((simSelf())->liveRegister = NoReg); + return ssPushRegister(ReceiverResultReg); +} + + +/* Generate the abort for a method. This abort performs either a call of + ceSICMiss: to handle a single-in-line cache miss or a call of + ceStackOverflow: to handle a + stack overflow. It distinguishes the two by testing ResultReceiverReg. If + the register is zero then this is a stack-overflow because a) the receiver + has already + been pushed and so can be set to zero before calling the abort, and b) the + receiver must always contain an object (and hence be non-zero) on SIC + miss. */ + + /* StackToRegisterMappingCogit>>#genMethodAbortTrampolineFor: */ +static NoDbgRegParms usqInt +genMethodAbortTrampolineFor(sqInt numArgs) +{ + AbstractInstruction *jumpSICMiss; + + zeroOpcodeIndex(); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, 0, ReceiverResultReg); + jumpSICMiss = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* The abort sequence has pushed the LinkReg a second time - because a stack + overflow can only happen after building a frame, which pushes LinkReg anyway, and + we still need to push LinkReg in case we get to this routine from a sendMissAbort. + (On ARM there is a simpler way; use two separate abort calls since all instructions are 32-bits + but on x86 the zero receiver reg, call methodAbort sequence is smaller; we may fix this one day). + Overwrite that duplicate with the right one - the return address for the call to the abort trampoline. + The only reason it matters is an assert in ceStackOverflow: uses it */ + compileTrampolineFornumArgsargargargargregsToSavepushLinkRegresultReg(ceStackOverflow, 1, SendNumArgsReg, null, null, null, 0 /* emptyRegisterMask */, 0, NoReg); + + /* The LinkReg has already been set above. */ + jmpTarget(jumpSICMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genPushRegisterArgsForAbortMissNumArgs(backEnd, numArgs); + return genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceSICMiss, trampolineNamenumRegArgs("ceMethodAbort", numArgs), 1, ReceiverResultReg, null, null, null, 0 /* emptyRegisterMask */, 0, NoReg, 1); +} + + +/* Generate the abort for a PIC. This abort performs either a call of + ceInterpretMethodFromPIC:receiver: to handle invoking an uncogged + target or a call of ceMNUFromPICMNUMethod:receiver: to handle an + MNU dispatch in a closed PIC. It distinguishes the two by testing + ClassReg. If the register is zero then this is an MNU. */ + + /* StackToRegisterMappingCogit>>#genPICAbortTrampolineFor: */ +static NoDbgRegParms usqInt +genPICAbortTrampolineFor(sqInt numArgs) +{ + zeroOpcodeIndex(); + genPushRegisterArgsForAbortMissNumArgs(backEnd, numArgs); + return genInnerPICAbortTrampoline(trampolineNamenumRegArgs("cePICAbort", numArgs)); +} + + /* StackToRegisterMappingCogit>>#genPICMissTrampolineFor: */ +static NoDbgRegParms usqInt +genPICMissTrampolineFor(sqInt numArgs) +{ + usqInt startAddress; + + startAddress = methodZoneBase; + zeroOpcodeIndex(); + + /* N.B. a closed PIC jumps to the miss routine, not calls it, so there is only one retpc on the stack. */ + genPushRegisterArgsForNumArgsscratchReg(backEnd, numArgs, SendNumArgsReg); + genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(ceCPICMissreceiver, trampolineNamenumRegArgs("cePICMiss", numArgs), 2, ClassReg, ReceiverResultReg, null, null, 0 /* emptyRegisterMask */, 1, NoReg, 1); + return startAddress; +} + + /* StackToRegisterMappingCogit>>#genPopStackBytecode */ +static sqInt +genPopStackBytecode(void) +{ + if (((ssTop())->spilled)) { + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BytesPerWord, SPReg); + } + ssPop(1); + return 0; +} + + +/* Check the argument count. Fail if wrong. + Get the method from the outerContext and see if it is cogged. If so, jump + to the + block entry or the no-context-switch entry, as appropriate, and we're + done. If not, + invoke the interpreter primitive. */ +/* Check the argument count. Fail if wrong. + Get the method from the outerContext and see if it is cogged. If so, jump + to the + block entry or the no-context-switch entry, as appropriate, and we're + done. If not, + invoke the interpreter primitive. + Override to push the register args first. */ + + /* StackToRegisterMappingCogit>>#genPrimitiveClosureValue */ +static sqInt +genPrimitiveClosureValue(void) +{ + AbstractInstruction *jumpBCMethod; + AbstractInstruction *jumpFail1; + AbstractInstruction *jumpFail2; + AbstractInstruction *jumpFail3; + AbstractInstruction *jumpFail4; + AbstractInstruction *jumpFailNArgs; + sqInt offset; + void (*primitiveRoutine)(void); + sqInt result; + + genPushRegisterArgs(); + genLoadSlotsourceRegdestReg(ClosureNumArgsIndex, ReceiverResultReg, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, (((usqInt)methodOrBlockNumArgs << 3) | 1), TempReg); + jumpFailNArgs = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + genLoadSlotsourceRegdestReg(ClosureOuterContextIndex, ReceiverResultReg, ClassReg); + jumpFail1 = genJumpImmediate(ClassReg); + genGetCompactClassIndexNonImmOfinto(ClassReg, TempReg); + genCmpClassMethodContextCompactIndexR(TempReg); + jumpFail2 = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* We defer unforwarding the receiver to the prologue; scanning blocks + for inst var refs and only unforwarding if the block refers to inst vars. */ + genLoadSlotsourceRegdestReg(MethodIndex, ClassReg, SendNumArgsReg); + jumpFail3 = genJumpImmediate(SendNumArgsReg); + genGetFormatOfinto(SendNumArgsReg, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), TempReg); + jumpFail4 = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + genLoadSlotsourceRegdestReg(HeaderIndex, SendNumArgsReg, ClassReg); + jumpBCMethod = genJumpImmediate(ClassReg); + offset = offsetof(CogMethod, blockEntryOffset); + + /* begin MoveM16:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveM16rR, offset, ClassReg, TempReg); + + /* AddR:R: */ + genoperandoperand(AddRR, ClassReg, TempReg); + primitiveRoutine = functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(methodObj, primitiveIndex, null); + if (primitiveRoutine == primitiveClosureValueNoContextSwitch) { + if (!blockNoContextSwitchOffset) { + return NotFullyInitialized; + } + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, blockNoContextSwitchOffset, TempReg); + } + + /* JumpR: */ + genoperand(JumpR, TempReg); + jmpTarget(jumpBCMethod, jmpTarget(jumpFail1, jmpTarget(jumpFail2, jmpTarget(jumpFail3, jmpTarget(jumpFail4, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))))); + if (((result = compileInterpreterPrimitiveflags(primitiveRoutine, primitivePropertyFlagsprimIndex(methodObj, primitiveIndex)))) < 0) { + return result; + } + jmpTarget(jumpFailNArgs, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + +/* Check the argument count. Fail if wrong. + Get the method from the outerContext and see if it is cogged. If so, jump + to the + block entry or the no-context-switch entry, as appropriate, and we're + done. If not, + invoke the interpreter primitive. */ +/* Override to push the register args first. */ + + /* StackToRegisterMappingCogit>>#genPrimitiveFullClosureValue */ +static sqInt +genPrimitiveFullClosureValue(void) +{ + AbstractInstruction *jumpBCMethod; + AbstractInstruction *jumpFail4; + AbstractInstruction *jumpFailImmediateMethod; + AbstractInstruction *jumpFailNArgs; + void (*primitiveRoutine)(void); + sqInt quickConstant; + sqInt result; + + genPushRegisterArgs(); + genLoadSlotsourceRegdestReg(ClosureNumArgsIndex, ReceiverResultReg, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, (((usqInt)methodOrBlockNumArgs << 3) | 1), TempReg); + jumpFailNArgs = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* We defer unforwarding the receiver to the prologue; scanning blocks + for inst var refs and only unforwarding if the block refers to inst vars. */ + genLoadSlotsourceRegdestReg(FullClosureCompiledBlockIndex, ReceiverResultReg, SendNumArgsReg); + jumpFailImmediateMethod = genJumpImmediate(SendNumArgsReg); + genGetFormatOfinto(SendNumArgsReg, TempReg); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, firstCompiledMethodFormat(), TempReg); + jumpFail4 = genConditionalBranchoperand(JumpLess, ((sqInt)0)); + genLoadSlotsourceRegdestReg(HeaderIndex, SendNumArgsReg, ClassReg); + jumpBCMethod = genJumpImmediate(ClassReg); + primitiveRoutine = functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(methodObj, primitiveIndex, null); + quickConstant = (primitiveRoutine == primitiveFullClosureValueNoContextSwitch + ? fullBlockNoContextSwitchEntryOffset() + : fullBlockEntryOffset()); + + /* begin AddCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, quickConstant, ClassReg); + + /* JumpR: */ + genoperand(JumpR, ClassReg); + jmpTarget(jumpBCMethod, jmpTarget(jumpFailImmediateMethod, jmpTarget(jumpFail4, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))); + if (((result = compileInterpreterPrimitiveflags(primitiveRoutine, primitivePropertyFlagsprimIndex(methodObj, primitiveIndex)))) < 0) { + return result; + } + jmpTarget(jumpFailNArgs, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return CompletePrimitive; +} + + +/* Generate an in-line perform primitive. The lookup code requires the + selector to be in Arg0Reg. + adjustArgumentsForPerform: adjusts the arguments once + genLookupForPerformNumArgs: has generated the code for the lookup. */ + + /* StackToRegisterMappingCogit>>#genPrimitivePerform */ +static sqInt +genPrimitivePerform(void) +{ + sqInt offset; + + if (methodOrBlockNumArgs > (numRegArgs())) { + offset = (methodOrBlockNumArgs) * BytesPerWord; + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, SPReg, Arg0Reg); + } + return genLookupForPerformNumArgs(methodOrBlockNumArgs); +} + + +/* Generate an in-line perform:withArguments: primitive. The lookup code + requires the selector to be in Arg0Reg + and the array to be in Arg1Reg. The primitive will only handle cases 0 to + numRegArgs. Is it worth it you ask? + Here are arguemnt count requencies for a short run of Croquet/Virtend + which show that even for V3, with only + one rtegister arg, it very much is (but we wimp out on V3 cuz of the + complexity of checking the Array): + [0] = 253743 52.5% + [1] = 116117 24%/76.5% + [2] = 99876 20.7%/97.2% + [3] = 12837 2.7%/99.9% + [4] = 209 + [5] = 84 + [6] = 2 + [7] = 313 + [8] = 0 + [9] = 0 + [10] = 0 + [11] = 1 + [12] = 0 + [13] = 0 + [14] = 0 + [15] = 0 */ + + /* StackToRegisterMappingCogit>>#genPrimitivePerformWithArguments */ +static sqInt +genPrimitivePerformWithArguments(void) +{ + sqInt cacheBaseReg; + AbstractInstruction *itsAHit; + AbstractInstruction *jumpBadNumArgs1; + AbstractInstruction *jumpBadNumArgs2; + AbstractInstruction *jumpClassMiss; + AbstractInstruction *jumpImmArray; + AbstractInstruction *jumpInterpret; + AbstractInstruction *jumpSelectorMiss; + sqInt offset; + sqInt quickConstant; + sqInt reg; + + /* begin genLookupForPerformWithArguments */ + jumpImmArray = genJumpImmediate(Arg1Reg); + genGetInlineCacheClassTagFromintoforEntry(ReceiverResultReg, SendNumArgsReg, 0); + cacheBaseReg = NoReg; + if (!(/* #isWithinMwOffsetRange: #addressIsInCodeZone: */ + ((((usqInt)(methodCacheAddress()))) >= codeBase) + && ((((usqInt)(methodCacheAddress()))) < limitAddress))) { + quickConstant = methodCacheAddress(); + reg = (cacheBaseReg = Extra0Reg); + + /* begin MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, quickConstant, reg); + } + + /* Do first of three probes. See CoInterpreter>>lookupInMethodCacheSel:classTag: */ + jumpSelectorMiss = compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(Arg0Reg, 0, cacheBaseReg); + jumpClassMiss = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* Fetch the method, and check if it is cogged. */ + offset = (cacheBaseReg == NoReg + ? (methodCacheAddress()) + ((((usqInt)(MethodCacheMethod) << (shiftForWord())))) + : ((sqInt)((usqInt)(MethodCacheMethod) << (shiftForWord())))); + + /* begin MoveMw:r:R: */ + itsAHit = genoperandoperandoperand(MoveMwrR, offset, ClassReg, SendNumArgsReg); + + /* If the method is not compiled fall back on the interpreter primitive. */ + genLoadSlotsourceRegdestReg(HeaderIndex, SendNumArgsReg, ClassReg); + jumpInterpret = genJumpImmediate(ClassReg); + + /* check the argument count; if it's wrong fall back on the interpreter primitive. */ + + /* begin genLoadcmNumArgsOf:into: */ + /* MoveCq:R: */ + /* gen:quickConstant:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, 0, SendNumArgsReg); + offset = sizeof(((((CogMethod *) 0))->objectHeader)); + + /* begin MoveMb:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMbrR, offset, ClassReg, SendNumArgsReg); + genGetRawSlotSizeOfNonImminto(Arg1Reg, TempReg); + + /* begin CmpR:R: */ + assert(!(0 /* (TempReg = SPReg) */)); + genoperandoperand(CmpRR, TempReg, SendNumArgsReg); + jumpBadNumArgs1 = genConditionalBranchoperand(JumpNonZero, ((sqInt)0)); + + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, numRegArgs(), SendNumArgsReg); + jumpBadNumArgs2 = genConditionalBranchoperand(JumpGreater, ((sqInt)0)); + + /* Fetch arguments and jump to the method's unchecked entry-point. */ + genFetchRegArgsForPerformWithArguments(TempReg); + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, cmNoCheckEntryOffset, ClassReg); + + /* JumpR: */ + genoperand(JumpR, ClassReg); + + /* First probe missed. Do second of three probes. Shift hash right one and retry. */ + jmpTarget(jumpSelectorMiss, jmpTarget(jumpClassMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC))); + jumpSelectorMiss = compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(Arg0Reg, 1, cacheBaseReg); + + /* JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)itsAHit)); + + /* Second probe missed. Do last probe. Shift hash right two and retry. */ + jmpTarget(jumpSelectorMiss, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + jumpSelectorMiss = compilePerformMethodCacheProbeForwithShiftbaseRegOrNone(Arg0Reg, 2, cacheBaseReg); + + /* JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)itsAHit)); + + /* Last probe missed/not jitted/bad num args. Caller will generate the call to fall back on the interpreter primitive. */ + jmpTarget(jumpImmArray, jmpTarget(jumpSelectorMiss, jmpTarget(jumpInterpret, jmpTarget(jumpBadNumArgs1, jmpTarget(jumpBadNumArgs2, genoperandoperand(Label, (labelCounter += 1), bytecodePC)))))); + return 0; +} + + /* StackToRegisterMappingCogit>>#genPushActiveContextBytecode */ +static sqInt +genPushActiveContextBytecode(void) +{ + assert(needsFrame); + voidReceiverResultRegContainsSelf(); + + /* begin ssAllocateCallReg:and:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(CallerSavedRegisterMask | (((1U << ReceiverResultReg) | (1U << SendNumArgsReg)) | (1U << ClassReg)), simStackPtr, simNativeStackPtr); + genGetActiveContextNumArgslargeinBlock(methodOrBlockNumArgs, methodNeedsLargeContext(methodObj), inBlock); + return ssPushRegister(ReceiverResultReg); +} + + +/* Block compilation. At this point in the method create the block. Note its + start and defer generating code for it until after the method and any + other preceding + blocks. The block's actual code will be compiled later. */ +/* 143 10001111 llllkkkk jjjjjjjj iiiiiiii Push Closure Num Copied llll Num + Args kkkk BlockSize jjjjjjjjiiiiiiii */ + + /* StackToRegisterMappingCogit>>#genPushClosureCopyCopiedValuesBytecode */ +static sqInt +genPushClosureCopyCopiedValuesBytecode(void) +{ + sqInt i; + usqInt numArgs; + sqInt numCopied; + sqInt reg; + sqInt startpc; + + assert(needsFrame); + startpc = bytecodePC + (((generatorAt(byte0))->numBytes)); + addBlockStartAtnumArgsnumCopiedspan(startpc, (numArgs = byte1 & 15), (numCopied = ((usqInt)(byte1)) >> 4), ((((usqInt)(byte2) << 8))) + byte3); + + /* 0 relative */ + + /* begin genInlineClosure:numArgs:numCopied: */ + assert(getActiveContextAllocatesInMachineCode()); + voidReceiverResultRegContainsSelf(); + + /* begin ssAllocateCallReg:and:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(CallerSavedRegisterMask | (((1U << ReceiverResultReg) | (1U << SendNumArgsReg)) | (1U << ClassReg)), simStackPtr, simNativeStackPtr); + genNoPopCreateClosureAtnumArgsnumCopiedcontextNumArgslargeinBlock(startpc + 1, numArgs, numCopied, methodOrBlockNumArgs, methodNeedsLargeContext(methodObj), inBlock); + + /* 1 relative */ + for (i = 1; i <= numCopied; i += 1) { + reg = ssStorePoptoPreferredReg(1, TempReg); + genStoreSourceRegslotIndexintoNewObjectInDestReg(reg, (ClosureFirstCopiedValueIndex + numCopied) - i, ReceiverResultReg); + } + ssPushRegister(ReceiverResultReg); + return 0; +} + + +/* */ +/* Override to avoid the BytecodeSetHasDirectedSuperSend check, which is + unnecessary here given the simulation stack. */ + + /* StackToRegisterMappingCogit>>#genPushLiteralIndex: */ +static NoDbgRegParms sqInt +genPushLiteralIndex(sqInt literalIndex) +{ + sqInt literal; + + literal = getLiteral(literalIndex); + return ssPushConstant(literal); +} + + /* StackToRegisterMappingCogit>>#genPushLiteralVariable: */ +static NoDbgRegParms sqInt +genPushLiteralVariable(sqInt literalIndex) +{ + sqInt association; + sqInt bcpc; + BytecodeDescriptor *descriptor1; + sqInt eA; + sqInt eB; + sqInt freeReg; + sqInt savedB0; + sqInt savedB1; + sqInt savedB2; + sqInt savedB3; + sqInt savedEA; + sqInt savedEB; + sqInt savedNEB; + + association = getLiteral(literalIndex); + + /* If followed by a directed super send bytecode, avoid generating any code yet. + The association will be passed to the directed send trampoline in a register + and fully dereferenced only when first linked. It will be ignored in later sends. */ + assert(!(directedSendUsesBinding)); + + /* begin nextDescriptorExtensionsAndNextPCInto: */ + descriptor1 = generatorAt(byte0); + savedB0 = byte0; + savedB1 = byte1; + savedB2 = byte2; + savedB3 = byte3; + savedEA = extA; + savedEB = extB; + savedNEB = numExtB; + bcpc = bytecodePC + ((descriptor1->numBytes)); + do { + if (bcpc > endPC) { + goto l1; + } + descriptor1 = loadBytesAndGetDescriptor(bcpc); + if (!((descriptor1->isExtension))) { + eA = extA; + eB = extB; + extA = savedEA; + extB = savedEB; + numExtB = savedNEB; + byte0 = savedB0; + byte1 = savedB1; + byte2 = savedB2; + byte3 = savedB3; + if (/* isDirectedSuper:extA:extB: */ + (descriptor1) + && ((((descriptor1->generator)) == genExtSendSuperBytecode) + && (eB >= 64))) { + ssPushConstant(association); + directedSendUsesBinding = 1; + return 0; + } + goto l1; + } + ((descriptor1->generator))(); + bcpc += (descriptor1->numBytes); + } while(1); + /* end nextDescriptorExtensionsAndNextPCInto: */ +l1: + freeReg = allocateRegNotConflictingWith(0); + + /* N.B. Do _not_ use ReceiverResultReg to avoid overwriting receiver in assignment in frameless methods. + So far descriptors are not rich enough to describe the entire dereference so generate the register + load but don't push the result. There is an order-of-evaluation issue if we defer the dereference. */ + + /* genMoveConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(association)) + && ((oopisGreaterThan(association, classTableRootObj())) + || (oopisLessThan(association, nilObject())))) { + annotateobjRef(genoperandoperand(MoveCwR, association, TempReg), association); + } + else { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, association, TempReg); + } + genLoadSlotsourceRegdestReg(ValueIndex, TempReg, freeReg); + ssPushRegister(freeReg); + return 0; +} + + /* StackToRegisterMappingCogit>>#genPushMaybeContextReceiverVariable: */ +static NoDbgRegParms sqInt +genPushMaybeContextReceiverVariable(sqInt slotIndex) +{ + AbstractInstruction *abstractInstruction; + AbstractInstruction *jmpDone; + AbstractInstruction *jmpSingle; + + /* begin ssAllocateCallReg:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(CallerSavedRegisterMask | (((1U << ReceiverResultReg)) | ((1U << SendNumArgsReg))), simStackPtr, simNativeStackPtr); + ensureReceiverResultRegContainsSelf(); + + /* begin genPushMaybeContextSlotIndex: */ + assert(needsFrame); + if (((CallerSavedRegisterMask & ((1U << ReceiverResultReg))) != 0)) { + /* We have no way of reloading ReceiverResultReg since we need the inst var value as the result. */ + voidReceiverResultRegContainsSelf(); + } + + /* See CoInterpreter>>contextInstructionPointer:frame: for an explanation + of the instruction pointer slot handling. */ + if (slotIndex == InstructionPointerIndex) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, slotIndex, SendNumArgsReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceFetchContextInstVarTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + return ssPushRegister(SendNumArgsReg); + } + genLoadSlotsourceRegdestReg(SenderIndex, ReceiverResultReg, TempReg); + jmpSingle = genJumpNotSmallInteger(TempReg); + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, slotIndex, SendNumArgsReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceFetchContextInstVarTrampoline); + (abstractInstruction->annotation = IsRelativeCall); + jmpDone = genoperand(Jump, ((sqInt)0)); + jmpTarget(jmpSingle, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + genLoadSlotsourceRegdestReg(slotIndex, ReceiverResultReg, SendNumArgsReg); + jmpTarget(jmpDone, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return ssPushRegister(SendNumArgsReg); +} + + /* StackToRegisterMappingCogit>>#genPushNewArrayBytecode */ +static sqInt +genPushNewArrayBytecode(void) +{ + sqInt i; + sqInt iSqInt; + int popValues; + usqInt size; + + assert(needsFrame); + voidReceiverResultRegContainsSelf(); + if ((popValues = byte1 > 0x7F)) { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (iSqInt = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); iSqInt <= simStackPtr; iSqInt += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(iSqInt), frameOffsetOfTemporary(iSqInt - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + } + else { + /* begin ssAllocateCallReg:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(CallerSavedRegisterMask | (((1U << SendNumArgsReg)) | ((1U << ReceiverResultReg))), simStackPtr, simNativeStackPtr); + } + size = byte1 & 0x7F; + if (!popValues) { + if (tryCollapseTempVectorInitializationOfSize(size)) { + return 0; + } + } + genNewArrayOfSizeinitialized(size, !popValues); + if (popValues) { + for (i = (size - 1); i >= 0; i += -1) { + /* PopR: */ + genoperand(PopR, TempReg); + genStoreSourceRegslotIndexintoNewObjectInDestReg(TempReg, i, ReceiverResultReg); + } + ssPop(size); + } + return ssPushRegister(ReceiverResultReg); +} + + /* StackToRegisterMappingCogit>>#genPushReceiverBytecode */ +static sqInt +genPushReceiverBytecode(void) +{ + if ((((simSelf())->liveRegister)) == ReceiverResultReg) { + return ssPushRegister(ReceiverResultReg); + } + return ssPushDesc(ssSelfDescriptor()); +} + + /* StackToRegisterMappingCogit>>#genPushReceiverVariable: */ +static NoDbgRegParms sqInt +genPushReceiverVariable(sqInt index) +{ + ensureReceiverResultRegContainsSelf(); + return ssPushBaseoffset(ReceiverResultReg, slotOffsetOfInstVarIndex(index)); +} + + +/* Ensure that the register args are pushed before the retpc for methods with + arity <= self numRegArgs. + */ +/* This isn't as clumsy on a RISC. But putting the receiver and + args above the return address means the CoInterpreter has a + single machine-code frame format which saves us a lot of work. */ + + /* StackToRegisterMappingCogit>>#genPushRegisterArgs */ +static void +genPushRegisterArgs(void) +{ + if (!(regArgsHaveBeenPushed + || (methodOrBlockNumArgs > (numRegArgs())))) { + genPushRegisterArgsForNumArgsscratchReg(backEnd, methodOrBlockNumArgs, SendNumArgsReg); + regArgsHaveBeenPushed = 1; + } +} + + /* StackToRegisterMappingCogit>>#genPushRemoteTempLongBytecode */ +static sqInt +genPushRemoteTempLongBytecode(void) +{ + sqInt offset; + sqInt remoteTempReg; + sqInt tempVectReg; + + tempVectReg = allocateRegNotConflictingWith(0); + offset = frameOffsetOfTemporary(byte2); + + /* begin MoveMw:r:R: */ + /* gen:quickConstant:operand:operand: */ + /* checkQuickConstant:forInstruction: */ + genoperandoperandoperand(MoveMwrR, offset, FPReg, tempVectReg); + remoteTempReg = availableRegisterOrNoneFor(backEnd, (liveRegisters()) | (((tempVectReg < 0) ? (((usqInt)(1)) >> (-tempVectReg)) : (1ULL << tempVectReg)))); + if (remoteTempReg == NoReg) { + remoteTempReg = tempVectReg; + } + genLoadSlotsourceRegdestReg(byte1, tempVectReg, remoteTempReg); + return ssPushRegister(remoteTempReg); +} + + +/* If a frameless method (not a block), only argument temps can be accessed. + This is assured by the use of needsFrameIfMod16GENumArgs: in pushTemp. */ + + /* StackToRegisterMappingCogit>>#genPushTemporaryVariable: */ +static NoDbgRegParms sqInt +genPushTemporaryVariable(sqInt index) +{ + assert((inBlock > 0) + || (needsFrame + || (index < methodOrBlockNumArgs))); + return ssPushDesc(simStack[index + 1]); +} + + +/* In a frameless method ReceiverResultReg already contains self. + In a frameful method, ReceiverResultReg /may/ contain self. */ + + /* StackToRegisterMappingCogit>>#genReturnReceiver */ +static sqInt +genReturnReceiver(void) +{ + if (needsFrame) { + if (!((((simSelf())->liveRegister)) == ReceiverResultReg)) { + /* begin putSelfInReceiverResultReg */ + storeToReg(simSelf(), ReceiverResultReg); + } + } + return genUpArrowReturn(); +} + + /* StackToRegisterMappingCogit>>#genReturnTopFromBlock */ +static sqInt +genReturnTopFromBlock(void) +{ + assert(inBlock > 0); + popToReg(ssTop(), ReceiverResultReg); + ssPop(1); + return genBlockReturn(); +} + + /* StackToRegisterMappingCogit>>#genReturnTopFromMethod */ +static sqInt +genReturnTopFromMethod(void) +{ + popToReg(ssTop(), ReceiverResultReg); + ssPop(1); + return genUpArrowReturn(); +} + + /* StackToRegisterMappingCogit>>#genSendDirectedSuper:numArgs: */ +static NoDbgRegParms sqInt +genSendDirectedSupernumArgs(sqInt selectorIndex, sqInt numArgs) +{ + sqInt result; + + assert((((ssTop())->type)) == SSConstant); + tempOop = ((ssTop())->constant); + ssPop(1); + marshallSendArguments(numArgs); + result = genMarshalledSendnumArgssendTable(selectorIndex, numArgs, (directedSendUsesBinding + ? directedSuperBindingSendTrampolines + : directedSuperSendTrampolines)); + directedSendUsesBinding = 0; + return result; +} + + /* StackToRegisterMappingCogit>>#genSendSuper:numArgs: */ +static NoDbgRegParms sqInt +genSendSupernumArgs(sqInt selectorIndex, sqInt numArgs) +{ + marshallSendArguments(numArgs); + return genMarshalledSendnumArgssendTable(selectorIndex, numArgs, superSendTrampolines); +} + + +/* Generate a trampoline with four arguments. + Hack: a negative value indicates an abstract register, a non-negative + value indicates a constant. */ + + /* StackToRegisterMappingCogit>>#genSendTrampolineFor:numArgs:called:arg:arg:arg:arg: */ +static NoDbgRegParms usqInt +genSendTrampolineFornumArgscalledargargargarg(void *aRoutine, sqInt numArgs, char *aString, sqInt regOrConst0, sqInt regOrConst1, sqInt regOrConst2, sqInt regOrConst3) +{ + sqInt routine; + usqInt startAddress; + + startAddress = methodZoneBase; + zeroOpcodeIndex(); + genPushRegisterArgsForNumArgsscratchReg(backEnd, numArgs, SendNumArgsReg); + + /* If we're on a 64-bit platform we use 32-bit inline caches. These don't have room for a selector oop, only + a selector index. If so, call the selectorIndexDereferenceRoutine to convert the index into a selector oop. */ + if ((routine = ceDereferenceSelectorIndex)) { + /* Explicitly save LinkReg via ExtraReg2; it's presumably faster than pushing/popping */ + + /* Call: */ + genoperand(Call, routine); + } + genTrampolineForcallednumArgsargargargargregsToSavepushLinkRegresultRegappendOpcodes(aRoutine, aString, 4, regOrConst0, regOrConst1, regOrConst2, regOrConst3, 0 /* emptyRegisterMask */, 1, NoReg, 1); + return startAddress; +} + + /* StackToRegisterMappingCogit>>#genSend:numArgs: */ +static NoDbgRegParms sqInt +genSendnumArgs(sqInt selectorIndex, sqInt numArgs) +{ + marshallSendArguments(numArgs); + return genMarshalledSendnumArgssendTable(selectorIndex, numArgs, ordinarySendTrampolines); +} + + /* StackToRegisterMappingCogit>>#genSpecialSelectorArithmetic */ +static sqInt +genSpecialSelectorArithmetic(void) +{ + AbstractInstruction *abstractInstruction; + sqInt argInt; + int argIsConst; + sqInt argIsInt; + sqInt i; + sqInt index; + AbstractInstruction *jumpContinue; + AbstractInstruction *jumpNotSmallInts; + BytecodeDescriptor *primDescriptor; + sqInt rcvrInt; + int rcvrIsConst; + sqInt rcvrIsInt; + sqInt result; + SimStackEntry *selfOfIsSameEntryAs; + + primDescriptor = generatorAt(byte0); + argIsInt = ((argIsConst = (((ssTop())->type)) == SSConstant)) + && ((((((argInt = ((ssTop())->constant)))) & 7) == 1)); + rcvrIsInt = (((rcvrIsConst = (((ssValue(1))->type)) == SSConstant)) + && ((((((rcvrInt = ((ssValue(1))->constant)))) & 7) == 1))) + || ((mclassIsSmallInteger()) + && (((selfOfIsSameEntryAs = ssValue(1)), + isSameEntryAs(selfOfIsSameEntryAs, simSelf())))); + if (argIsInt + && (rcvrIsInt + && (rcvrIsConst))) { + rcvrInt = (rcvrInt >> 3); + argInt = (argInt >> 3); + switch ((primDescriptor->opcode)) { + case AddRR: + result = rcvrInt + argInt; + break; + case SubRR: + result = rcvrInt - argInt; + break; + case AndRR: + result = rcvrInt & argInt; + break; + case OrRR: + result = rcvrInt | argInt; + break; + default: + error("Case not found and no otherwise clause"); + } + if (isIntegerValue(result)) { + /* Must annotate the bytecode for correct pc mapping. */ + ssPop(2); + return ssPushAnnotatedConstant((((usqInt)result << 3) | 1)); + } + return genSpecialSelectorSend(); + } + + /* If there's any constant involved other than a SmallInteger don't attempt to inline. */ + if ((rcvrIsConst + && (!rcvrIsInt)) + || (argIsConst + && (!argIsInt))) { + return genSpecialSelectorSend(); + } + + /* If we know nothing about the types then better not to inline as the inline cache and + primitive code is not terribly slow so wasting time on duplicating tag tests is pointless. */ + if (!(argIsInt + || (rcvrIsInt))) { + return genSpecialSelectorSend(); + } + if (argIsInt) { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= (simStackPtr - 2)) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < (simStackPtr - 2)) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : (simStackPtr - 2))); i <= (simStackPtr - 2); i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = (simStackPtr - 2) + 1; + } + popToReg(ssValue(1), ReceiverResultReg); + ssPop(2); + } + else { + marshallSendArguments(1); + } + jumpNotSmallInts = (rcvrIsInt + && (argIsInt) + ? ((AbstractInstruction *) null) + : (argIsInt + ? genJumpNotSmallInteger(ReceiverResultReg) + : (rcvrIsInt + ? genJumpNotSmallInteger(Arg0Reg) + : genJumpNotSmallIntegersInandscratch(ReceiverResultReg, Arg0Reg, TempReg)))); + switch ((primDescriptor->opcode)) { + case AddRR: + if (argIsInt) { + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, argInt - ConstZero, ReceiverResultReg); + jumpContinue = genConditionalBranchoperand(JumpNoOverflow, ((sqInt)0)); + + /* overflow; must undo the damage before continuing */ + + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, argInt - ConstZero, ReceiverResultReg); + } + else { + genRemoveSmallIntegerTagsInScratchReg(ReceiverResultReg); + + /* AddR:R: */ + genoperandoperand(AddRR, Arg0Reg, ReceiverResultReg); + jumpContinue = genConditionalBranchoperand(JumpNoOverflow, ((sqInt)0)); + + /* overflow; must undo the damage before continuing */ + if (rcvrIsInt + && (rcvrIsConst)) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, rcvrInt, ReceiverResultReg); + } + else { + /* SubR:R: */ + genoperandoperand(SubRR, Arg0Reg, ReceiverResultReg); + genSetSmallIntegerTagsIn(ReceiverResultReg); + } + } + break; + case SubRR: + if (argIsInt) { + /* #SubCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(SubCqR, argInt - ConstZero, ReceiverResultReg); + jumpContinue = genConditionalBranchoperand(JumpNoOverflow, ((sqInt)0)); + + /* overflow; must undo the damage before continuing */ + + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, argInt - ConstZero, ReceiverResultReg); + } + else { + genRemoveSmallIntegerTagsInScratchReg(Arg0Reg); + + /* SubR:R: */ + genoperandoperand(SubRR, Arg0Reg, ReceiverResultReg); + jumpContinue = genConditionalBranchoperand(JumpNoOverflow, ((sqInt)0)); + + /* overflow; must undo the damage before continuing */ + + /* AddR:R: */ + genoperandoperand(AddRR, Arg0Reg, ReceiverResultReg); + genSetSmallIntegerTagsIn(Arg0Reg); + } + break; + case AndRR: + if (argIsInt) { + /* #AndCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AndCqR, argInt, ReceiverResultReg); + } + else { + /* AndR:R: */ + genoperandoperand(AndRR, Arg0Reg, ReceiverResultReg); + } + jumpContinue = (jumpNotSmallInts + ? genoperand(Jump, ((sqInt)0)) + : ((AbstractInstruction *) null)); + break; + case OrRR: + if (argIsInt) { + /* #OrCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(OrCqR, argInt, ReceiverResultReg); + } + else { + /* OrR:R: */ + genoperandoperand(OrRR, Arg0Reg, ReceiverResultReg); + } + jumpContinue = (jumpNotSmallInts + ? genoperand(Jump, ((sqInt)0)) + : ((AbstractInstruction *) null)); + break; + default: + error("Case not found and no otherwise clause"); + } + if (jumpNotSmallInts) { + jmpTarget(jumpNotSmallInts, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } + else { + if (!jumpContinue) { + /* begin annotateInstructionForBytecode */ + abstractInstruction = (prevInstIsPCAnnotated() + ? gen(Nop) + : genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + ssPushRegister(ReceiverResultReg); + return 0; + } + } + if (argIsInt) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, argInt, Arg0Reg); + } + index = byte0 - (/* firstSpecialSelectorBytecodeOffset */ + (bytecodeSetOffset == 0x100 + ? AltFirstSpecialSelector + 0x100 + : FirstSpecialSelector)); + genMarshalledSendnumArgssendTable((-index) - 1, 1, ordinarySendTrampolines); + jmpTarget(jumpContinue, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + return 0; +} + + /* StackToRegisterMappingCogit>>#genSpecialSelectorClass */ +static sqInt +genSpecialSelectorClass(void) +{ + sqInt requiredReg1; + sqInt topReg; + + topReg = registerOrNone(ssTop()); + ssPop(1); + if ((topReg == NoReg) + || (topReg == ClassReg)) { + requiredReg1 = (topReg = SendNumArgsReg); + + /* begin ssAllocateRequiredReg:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((((requiredReg1 < 0) ? (((usqInt)(1)) >> (-requiredReg1)) : (1ULL << requiredReg1))) | ((1U << ClassReg)), simStackPtr, simNativeStackPtr); + } + else { + /* begin ssAllocateRequiredReg: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << ClassReg), simStackPtr, simNativeStackPtr); + } + ssPush(1); + popToReg(ssTop(), topReg); + genGetClassObjectOfintoscratchRegmayBeAForwarder(topReg, ClassReg, TempReg, mayBeAForwarder(ssTop())); + ssPop(1); + return ssPushRegister(ClassReg); +} + + /* StackToRegisterMappingCogit>>#genSpecialSelectorComparison */ +static sqInt +genSpecialSelectorComparison(void) +{ + AbstractInstruction *abstractInstruction; + sqInt argInt; + sqInt argIsIntConst; + BytecodeDescriptor *branchDescriptor; + BytecodeDescriptor *branchDescriptor1; + sqInt i; + sqInt index; + sqInt inlineCAB; + AbstractInstruction *jumpNotSmallInts; + void *jumpTarget; + sqInt nExts; + sqInt nextPC; + sqInt nextPCSqInt; + sqInt postBranchPC; + sqInt postBranchPCSqInt; + BytecodeDescriptor *primDescriptor; + BytecodeDescriptor *primDescriptor1; + int rcvrIsConst; + sqInt rcvrIsInt; + SimStackEntry *selfOfIsSameEntryAs; + sqInt targetBytecodePC; + sqInt targetPC; + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= (simStackPtr - 2)) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < (simStackPtr - 2)) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : (simStackPtr - 2))); i <= (simStackPtr - 2); i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = (simStackPtr - 2) + 1; + } + primDescriptor = generatorAt(byte0); + argIsIntConst = ((((ssTop())->type)) == SSConstant) + && ((((((argInt = ((ssTop())->constant)))) & 7) == 1)); + rcvrIsInt = (((rcvrIsConst = (((ssValue(1))->type)) == SSConstant)) + && (((((((ssValue(1))->constant))) & 7) == 1))) + || ((mclassIsSmallInteger()) + && (((selfOfIsSameEntryAs = ssValue(1)), + isSameEntryAs(selfOfIsSameEntryAs, simSelf())))); + if (argIsIntConst + && (rcvrIsInt + && (rcvrIsConst))) { + return genStaticallyResolvedSpecialSelectorComparison(); + } + + /* begin extractMaybeBranchDescriptorInto: */ + branchDescriptor1 = ((BytecodeDescriptor *) 0); + primDescriptor1 = generatorAt(byte0); + nextPCSqInt = bytecodePC + ((primDescriptor1->numBytes)); + nExts = 0; + while (1) { + while (1) { + /* begin generatorForPC: */ + branchDescriptor1 = generatorAt(bytecodeSetOffset + (fetchByteofObject(nextPCSqInt, methodObj))); + if (!((branchDescriptor1->isExtension))) break; + nExts += 1; + nextPCSqInt += (branchDescriptor1->numBytes); + } + if (!(/* isUnconditionalBranch */ + (isBranch(branchDescriptor1)) + && (!(((branchDescriptor1->isBranchTrue)) + || ((branchDescriptor1->isBranchFalse)))))) break; + nextPCSqInt = eventualTargetOf((nextPCSqInt + ((branchDescriptor1->numBytes))) + (((branchDescriptor1->spanFunction))(branchDescriptor1, nextPCSqInt, nExts, methodObj))); + } + targetBytecodePC = (postBranchPCSqInt = 0); + if (((branchDescriptor1->isBranchTrue)) + || ((branchDescriptor1->isBranchFalse))) { + targetBytecodePC = eventualTargetOf((nextPCSqInt + ((branchDescriptor1->numBytes))) + (((branchDescriptor1->spanFunction))(branchDescriptor1, nextPCSqInt, nExts, methodObj))); + postBranchPCSqInt = eventualTargetOf(nextPCSqInt + ((branchDescriptor1->numBytes))); + } + else { + nextPCSqInt = bytecodePC + ((primDescriptor1->numBytes)); + } + branchDescriptor = branchDescriptor1; + nextPC = nextPCSqInt; + postBranchPC = postBranchPCSqInt; + targetPC = targetBytecodePC; + + /* Only interested in inlining if followed by a conditional branch. */ + inlineCAB = ((branchDescriptor->isBranchTrue)) + || ((branchDescriptor->isBranchFalse)); + + /* Further, only interested in inlining = and ~= if there's a SmallInteger constant involved. + The relational operators successfully statically predict SmallIntegers; the equality operators do not. */ + if (inlineCAB + && ((((primDescriptor->opcode)) == JumpZero) + || (((primDescriptor->opcode)) == JumpNonZero))) { + inlineCAB = argIsIntConst + || (rcvrIsInt); + } + if (!inlineCAB) { + return genSpecialSelectorSend(); + } + if (argIsIntConst) { + popToReg(ssValue(1), ReceiverResultReg); + ssPop(2); + } + else { + marshallSendArguments(1); + } + jumpNotSmallInts = (rcvrIsInt + && (argIsIntConst) + ? ((AbstractInstruction *) null) + : (argIsIntConst + ? genJumpNotSmallInteger(ReceiverResultReg) + : (rcvrIsInt + ? genJumpNotSmallInteger(Arg0Reg) + : genJumpNotSmallIntegersInandscratch(ReceiverResultReg, Arg0Reg, TempReg)))); + if (argIsIntConst) { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, argInt, ReceiverResultReg); + } + else { + /* begin CmpR:R: */ + assert(!(0 /* (Arg0Reg = SPReg) */)); + genoperandoperand(CmpRR, Arg0Reg, ReceiverResultReg); + } + + /* Cmp is weird/backwards so invert the comparison. Further since there is a following conditional + jump bytecode define non-merge fixups and leave the cond bytecode to set the mergeness. */ + genConditionalBranchoperand(((branchDescriptor->isBranchTrue) + ? (primDescriptor->opcode) + : inverseBranchFor((primDescriptor->opcode))), ((usqInt)(ensureNonMergeFixupAt(targetPC)))); + jumpTarget = ensureNonMergeFixupAt(postBranchPC); + + /* begin Jump: */ + genoperand(Jump, ((sqInt)jumpTarget)); + if (!jumpNotSmallInts) { + /* begin annotateInstructionForBytecode */ + abstractInstruction = (prevInstIsPCAnnotated() + ? gen(Nop) + : genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + ensureFixupAt(postBranchPC); + ensureFixupAt(targetPC); + deadCode = 1; + return 0; + } + jmpTarget(jumpNotSmallInts, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + if (argIsIntConst) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, argInt, Arg0Reg); + } + index = byte0 - (/* firstSpecialSelectorBytecodeOffset */ + (bytecodeSetOffset == 0x100 + ? AltFirstSpecialSelector + 0x100 + : FirstSpecialSelector)); + return genMarshalledSendnumArgssendTable((-index) - 1, 1, ordinarySendTrampolines); +} + + +/* Assumes both operands are ints */ + + /* StackToRegisterMappingCogit>>#genStaticallyResolvedSpecialSelectorComparison */ +static sqInt +genStaticallyResolvedSpecialSelectorComparison(void) +{ + sqInt argInt; + BytecodeDescriptor *primDescriptor; + sqInt rcvrInt; + int result; + + primDescriptor = generatorAt(byte0); + argInt = ((ssTop())->constant); + rcvrInt = ((ssValue(1))->constant); + + /* In Simulator ints are unsigned... */ + switch ((primDescriptor->opcode)) { + case JumpLess: + result = rcvrInt < argInt; + break; + case JumpLessOrEqual: + result = rcvrInt <= argInt; + break; + case JumpGreater: + result = rcvrInt > argInt; + break; + case JumpGreaterOrEqual: + result = rcvrInt >= argInt; + break; + case JumpZero: + result = rcvrInt == argInt; + break; + case JumpNonZero: + result = rcvrInt != argInt; + break; + default: + error("Case not found and no otherwise clause"); + } + + /* Must annotate the bytecode for correct pc mapping. */ + ssPop(2); + return ssPushAnnotatedConstant((result + ? trueObject() + : falseObject())); +} + + +/* We need a frame because the association has to be in ReceiverResultReg for + the various trampolines + and ReceiverResultReg holds only the receiver in frameless methods. + */ + + /* StackToRegisterMappingCogit>>#genStorePop:LiteralVariable:needsStoreCheck:needsImmutabilityCheck: */ +static NoDbgRegParms sqInt +genStorePopLiteralVariableneedsStoreCheckneedsImmutabilityCheck(sqInt popBoolean, sqInt litVarIndex, sqInt needsStoreCheck, sqInt needsImmCheck) +{ + sqInt association; + sqInt i; + sqInt topReg; + + assert(needsFrame); + + /* begin genLoadLiteralVariable:in: */ + association = getLiteral(litVarIndex); + voidReceiverResultRegContainsSelf(); + + /* begin ssAllocateRequiredReg: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << ReceiverResultReg), simStackPtr, simNativeStackPtr); + + /* genMoveConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(association)) + && ((oopisGreaterThan(association, classTableRootObj())) + || (oopisLessThan(association, nilObject())))) { + annotateobjRef(genoperandoperand(MoveCwR, association, ReceiverResultReg), association); + } + else { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, association, ReceiverResultReg); + } + + /* begin genGenericStorePop:slotIndex:destReg:needsStoreCheck:needsRestoreRcvr:needsImmutabilityCheck: */ +# if IMMUTABILITY + if (needsImmCheck) { + /* begin ssAllocateRequiredReg:upThrough: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << ClassReg), simStackPtr - 1, simNativeStackPtr); + ssStoreAndReplacePoptoReg(popBoolean, ClassReg); + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + return genStoreWithImmutabilityCheckSourceRegslotIndexdestRegscratchRegneedsStoreCheckneedRestoreRcvr(ClassReg, ValueIndex, ReceiverResultReg, TempReg, needsStoreCheck, 0); + } +# endif // IMMUTABILITY + + topReg = allocateRegForStackEntryAtnotConflictingWith(0, (1U << ReceiverResultReg)); + ssStorePoptoReg(popBoolean, topReg); + return genStoreSourceRegslotIndexdestRegscratchReginFrameneedsStoreCheck(topReg, ValueIndex, ReceiverResultReg, TempReg, needsFrame, needsStoreCheck); +} + + +/* The reason we need a frame here is that assigning to an inst var of a + context may + involve wholesale reorganization of stack pages, and the only way to + preserve the + execution state of an activation in that case is if it has a frame. */ + + /* StackToRegisterMappingCogit>>#genStorePop:MaybeContextReceiverVariable:needsStoreCheck:needsImmutabilityCheck: */ +static NoDbgRegParms sqInt +genStorePopMaybeContextReceiverVariableneedsStoreCheckneedsImmutabilityCheck(sqInt popBoolean, sqInt slotIndex, sqInt needsStoreCheck, sqInt needsImmCheck) +{ + AbstractInstruction *abstractInstruction; + sqInt i; + AbstractInstruction *immutabilityFailure; + AbstractInstruction *mutableJump; + + assert(needsFrame); + ssFlushUpThroughReceiverVariable(slotIndex); + preserveTopInClassRegIfIsReceiverResultReg(); + ensureReceiverResultRegContainsSelf(); + + /* begin genGenericStorePop:MaybeContextSlotIndex:needsStoreCheck:needsRestoreRcvr:needsImmutabilityCheck: */ + immutabilityFailure = ((AbstractInstruction *) 0); + assert(needsFrame); +# if IMMUTABILITY + if (needsImmCheck) { + mutableJump = genJumpMutablescratchReg(ReceiverResultReg, TempReg); + + /* begin genStoreTrampolineCall: */ + assert(IMMUTABILITY); + if (slotIndex >= (NumStoreTrampolines - 1)) { + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, slotIndex, TempReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreTrampolines[NumStoreTrampolines - 1]); + (abstractInstruction->annotation = IsRelativeCall); + } + else { + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreTrampolines[slotIndex]); + (abstractInstruction->annotation = IsRelativeCall); + } + abstractInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + + /* begin putSelfInReceiverResultReg */ + storeToReg(simSelf(), ReceiverResultReg); + immutabilityFailure = genoperand(Jump, ((sqInt)0)); + jmpTarget(mutableJump, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } +# endif // IMMUTABILITY + + ssPop(1); + + /* begin ssAllocateCallReg:and: */ + ssAllocateRequiredRegMaskupThroughupThroughNative(CallerSavedRegisterMask | (((1U << ClassReg)) | ((1U << SendNumArgsReg))), simStackPtr, simNativeStackPtr); + ssPush(1); + genLoadSlotsourceRegdestReg(SenderIndex, ReceiverResultReg, TempReg); + ssStoreAndReplacePoptoReg(popBoolean, ClassReg); + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + + /* #MoveCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(MoveCqR, slotIndex, SendNumArgsReg); + + /* begin CallRT: */ + abstractInstruction = genoperand(Call, ceStoreContextInstVarTrampoline); + (abstractInstruction->annotation = IsRelativeCall); +# if IMMUTABILITY + if (needsImmCheck) { + jmpTarget(immutabilityFailure, genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + } +# endif + + return 0; +} + + /* StackToRegisterMappingCogit>>#genVanillaInlinedIdenticalOrNotIf: */ +static NoDbgRegParms sqInt +genVanillaInlinedIdenticalOrNotIf(sqInt orNot) +{ + int argIsConstant; + sqInt argNeedsReg; + sqInt argReg; + sqInt argRegSqInt; + BytecodeDescriptor *branchDescriptor; + BytecodeDescriptor *branchDescriptor1; + sqInt i; + void *jumpTarget; + sqInt nExts; + sqInt nextPC; + sqInt nextPCSqInt; + sqInt postBranchPC; + sqInt postBranchPCSqInt; + BytecodeDescriptor *primDescriptor; + sqInt rcvrIsConstant; + sqInt rcvrNeedsReg; + sqInt rcvrReg; + sqInt rcvrRegSqInt; + sqInt reg; + sqInt rNextSqInt; + sqInt rTopSqInt; + BytecodeFixup *self_in_CogBytecodeFixup; + sqInt targetBytecodePC; + sqInt targetPC; + sqInt topRegistersMask; + + /* begin extractMaybeBranchDescriptorInto: */ + branchDescriptor1 = ((BytecodeDescriptor *) 0); + primDescriptor = generatorAt(byte0); + nextPCSqInt = bytecodePC + ((primDescriptor->numBytes)); + nExts = 0; + while (1) { + while (1) { + /* begin generatorForPC: */ + branchDescriptor1 = generatorAt(bytecodeSetOffset + (fetchByteofObject(nextPCSqInt, methodObj))); + if (!((branchDescriptor1->isExtension))) break; + nExts += 1; + nextPCSqInt += (branchDescriptor1->numBytes); + } + if (!(/* isUnconditionalBranch */ + (isBranch(branchDescriptor1)) + && (!(((branchDescriptor1->isBranchTrue)) + || ((branchDescriptor1->isBranchFalse)))))) break; + nextPCSqInt = eventualTargetOf((nextPCSqInt + ((branchDescriptor1->numBytes))) + (((branchDescriptor1->spanFunction))(branchDescriptor1, nextPCSqInt, nExts, methodObj))); + } + targetBytecodePC = (postBranchPCSqInt = 0); + if (((branchDescriptor1->isBranchTrue)) + || ((branchDescriptor1->isBranchFalse))) { + targetBytecodePC = eventualTargetOf((nextPCSqInt + ((branchDescriptor1->numBytes))) + (((branchDescriptor1->spanFunction))(branchDescriptor1, nextPCSqInt, nExts, methodObj))); + postBranchPCSqInt = eventualTargetOf(nextPCSqInt + ((branchDescriptor1->numBytes))); + } + else { + nextPCSqInt = bytecodePC + ((primDescriptor->numBytes)); + } + branchDescriptor = branchDescriptor1; + nextPC = nextPCSqInt; + postBranchPC = postBranchPCSqInt; + targetPC = targetBytecodePC; + argIsConstant = (((ssTop())->type)) == SSConstant; + + /* They can't be both constants to use correct machine opcodes. + However annotable constants can't be resolved statically, hence we need to careful. */ + rcvrIsConstant = (!argIsConstant) + && ((((ssValue(1))->type)) == SSConstant); + argNeedsReg = !argIsConstant; + rcvrNeedsReg = !rcvrIsConstant; + + /* begin allocateEqualsEqualsRegistersArgNeedsReg:rcvrNeedsReg:into: */ + assert(argNeedsReg + || (rcvrNeedsReg)); + argRegSqInt = (rcvrRegSqInt = NoReg); + if (argNeedsReg) { + if (rcvrNeedsReg) { + /* begin allocateRegForStackTopTwoEntriesInto: */ + topRegistersMask = 0; + rTopSqInt = (rNextSqInt = NoReg); + if ((registerOrNone(ssTop())) != NoReg) { + rTopSqInt = registerOrNone(ssTop()); + } + if ((registerOrNone(ssValue(1))) != NoReg) { + reg = (rNextSqInt = registerOrNone(ssValue(1))); + + /* begin registerMaskFor: */ + topRegistersMask = ((reg < 0) ? (((usqInt)(1)) >> (-reg)) : (1ULL << reg)); + } + if (rTopSqInt == NoReg) { + rTopSqInt = allocateRegNotConflictingWith(topRegistersMask); + } + if (rNextSqInt == NoReg) { + rNextSqInt = allocateRegNotConflictingWith(((rTopSqInt < 0) ? (((usqInt)(1)) >> (-rTopSqInt)) : (1ULL << rTopSqInt))); + } + assert(!(((rTopSqInt == NoReg) + || (rNextSqInt == NoReg)))); + argRegSqInt = rTopSqInt; + rcvrRegSqInt = rNextSqInt; + popToReg(ssTop(), argRegSqInt); + popToReg(ssValue(1), rcvrRegSqInt); + } + else { + argRegSqInt = allocateRegForStackEntryAtnotConflictingWith(0, 0); + popToReg(ssTop(), argRegSqInt); + + /* If the receiver is a spilled constant we need to pop it from the stack. */ + if (((ssValue(1))->spilled)) { + /* #AddCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(AddCqR, BytesPerWord, SPReg); + } + } + } + else { + assert(rcvrNeedsReg); + assert(!((((ssTop())->spilled)))); + rcvrRegSqInt = allocateRegForStackEntryAtnotConflictingWith(1, 0); + popToReg(ssValue(1), rcvrRegSqInt); + } + assert(!((argNeedsReg + && (argRegSqInt == NoReg)))); + assert(!((rcvrNeedsReg + && (rcvrRegSqInt == NoReg)))); + rcvrReg = rcvrRegSqInt; + argReg = argRegSqInt; + + /* If not followed by a branch, resolve to true or false. */ + if (!(((branchDescriptor->isBranchTrue)) + || ((branchDescriptor->isBranchFalse)))) { + return genIdenticalNoBranchArgIsConstantrcvrIsConstantargRegrcvrRegorNotIf(argIsConstant, rcvrIsConstant, argReg, rcvrReg, orNot); + } + + /* If branching the stack must be flushed for the merge */ + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= (simStackPtr - 2)) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < (simStackPtr - 2)) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : (simStackPtr - 2))); i <= (simStackPtr - 2); i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = (simStackPtr - 2) + 1; + } + + /* begin genCmpArgIsConstant:rcvrIsConstant:argReg:rcvrReg: */ + assert((argReg != NoReg) + || (rcvrReg != NoReg)); + if (argIsConstant) { + /* genCmpConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssTop())->constant))) + && ((oopisGreaterThan(((ssTop())->constant), classTableRootObj())) + || (oopisLessThan(((ssTop())->constant), nilObject())))) { + annotateobjRef(genoperandoperand(CmpCwR, ((ssTop())->constant), rcvrReg), ((ssTop())->constant)); + } + else { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ((ssTop())->constant), rcvrReg); + } + } + else { + if (rcvrIsConstant) { + /* genCmpConstant:R: */ + if (/* shouldAnnotateObjectReference: */ + (isNonImmediate(((ssValue(1))->constant))) + && ((oopisGreaterThan(((ssValue(1))->constant), classTableRootObj())) + || (oopisLessThan(((ssValue(1))->constant), nilObject())))) { + annotateobjRef(genoperandoperand(CmpCwR, ((ssValue(1))->constant), argReg), ((ssValue(1))->constant)); + } + else { + /* #CmpCq:R: #gen:quickConstant:operand: #checkQuickConstant:forInstruction: */ + genoperandoperand(CmpCqR, ((ssValue(1))->constant), argReg); + } + } + else { + /* begin CmpR:R: */ + assert(!((argReg == SPReg))); + genoperandoperand(CmpRR, argReg, rcvrReg); + } + } + ssPop(2); + + /* Further since there is a following conditional jump bytecode, define + non-merge fixups and leave the cond bytecode to set the mergeness. */ + + /* begin fixupAt: */ + self_in_CogBytecodeFixup = fixupAtIndex(nextPC - initialPC); + if ((self_in_CogBytecodeFixup->targetInstruction)) { + assert(!(deadCode)); + } + else { + deadCode = 1; + ensureFixupAt(targetPC); + ensureFixupAt(postBranchPC); + } + if (orNot == ((branchDescriptor->isBranchTrue))) { + ensureNonMergeFixupAt(targetPC); + jumpTarget = ensureNonMergeFixupAt(postBranchPC); + + /* begin JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)jumpTarget)); + jumpTarget = ensureNonMergeFixupAt(targetPC); + + /* begin Jump: */ + genoperand(Jump, ((sqInt)jumpTarget)); + } + else { + jumpTarget = ensureNonMergeFixupAt(targetPC); + + /* begin JumpZero: */ + genConditionalBranchoperand(JumpZero, ((sqInt)jumpTarget)); + jumpTarget = ensureNonMergeFixupAt(postBranchPC); + + /* begin Jump: */ + genoperand(Jump, ((sqInt)jumpTarget)); + } + + /* orNot is true for ~~ + a == b ifTrue: ... or a ~~ b ifFalse: ... jump on equal to target pc + a == b ifFalse: ... or a ~~ b ifTrue: ... jump on equal to post-branch pc + Not reached, execution flow has jumped to fixup */ + if (!deadCode) { + ssPushConstant(trueObject()); + } + return 0; +} + + +/* The register receiver (the closure itself) and args are pushed by the + closure value primitive(s) + and hence a frameless block has all arguments and copied values pushed to + the stack. However, + the method receiver (self) is put in the ReceiverResultReg by the block + entry. + */ + + /* StackToRegisterMappingCogit>>#initSimStackForFramelessBlock: */ +static NoDbgRegParms void +initSimStackForFramelessBlock(sqInt startpc) +{ + SimStackEntry *cascade0; + SimStackEntry *desc; + sqInt i; + + cascade0 = simSelf(); + (cascade0->type = SSRegister); + (cascade0->spilled = 0); + (cascade0->registerr = ReceiverResultReg); + (cascade0->liveRegister = ReceiverResultReg); + assert(methodOrBlockNumTemps >= methodOrBlockNumArgs); + for (i = 1; i <= methodOrBlockNumTemps; i += 1) { + desc = simStackAt(i); + (desc->type = SSBaseOffset); + (desc->spilled = 1); + (desc->registerr = SPReg); + (desc->offset = ((methodOrBlockNumArgs + 1) - i) * BytesPerWord); + (desc->bcptr = startpc); + } + + /* N.B. Includes num args */ + simStackPtr = methodOrBlockNumTemps; + simSpillBase = methodOrBlockNumTemps + 1; +} + + +/* Do not inline (inBlock access) */ + + /* StackToRegisterMappingCogit>>#isNonForwarderReceiver: */ +static NoDbgRegParms sqInt +isNonForwarderReceiver(sqInt reg) +{ + return ((((simSelf())->liveRegister)) == ReceiverResultReg) + && ((inBlock == 0) + && (reg == ReceiverResultReg)); +} + + /* StackToRegisterMappingCogit>>#liveRegisters */ +static sqInt +liveRegisters(void) +{ + sqInt i; + sqInt regsSet; + + if (needsFrame) { + regsSet = 0; + } + else { + regsSet = (1U << ReceiverResultReg); + if ((methodOrBlockNumArgs <= (numRegArgs())) + && (methodOrBlockNumArgs > 0)) { + regsSet = regsSet | ((1U << Arg0Reg)); + if (((numRegArgs()) > 1) + && (methodOrBlockNumArgs > 1)) { + regsSet = regsSet | ((1U << Arg1Reg)); + } + } + } + for (i = (((simSpillBase < 0) ? 0 : simSpillBase)); i <= simStackPtr; i += 1) { + regsSet = regsSet | (registerMask(simStackAt(i))); + } + return regsSet; +} + + +/* insert nops for dead code that is mapped so that bc + to mc mapping is not many to one */ + + /* StackToRegisterMappingCogit>>#mapDeadDescriptorIfNeeded: */ +static NoDbgRegParms sqInt +mapDeadDescriptorIfNeeded(BytecodeDescriptor *descriptor) +{ + AbstractInstruction *abstractInstruction; + + if (((descriptor->isMapped)) + || ((inBlock > 0) + && ((descriptor->isMappedInBlock)))) { + abstractInstruction = gen(Nop); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + } + return 0; +} + + +/* Spill everything on the simulated stack that needs spilling (that below + receiver and arguments). + Marshall receiver and arguments to stack and/or registers depending on arg + count. If the args don't fit in registers push receiver and args (spill + everything), but still assign + the receiver to ReceiverResultReg. */ + + /* StackToRegisterMappingCogit>>#marshallSendArguments: */ +static NoDbgRegParms void +marshallSendArguments(sqInt numArgs) +{ + sqInt anyRefs; + SimStackEntry *cascade0; + sqInt i; + sqInt numSpilled; + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= ((simStackPtr - numArgs) - 1)) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < ((simStackPtr - numArgs) - 1)) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : ((simStackPtr - numArgs) - 1))); i < (simStackPtr - numArgs); i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = ((simStackPtr - numArgs) - 1) + 1; + } + if (numArgs > (numRegArgs())) { + numSpilled = numberOfSpillsInTopNItems(numArgs + 1); + anyRefs = anyReferencesToRegisterinTopNItems(ReceiverResultReg, numArgs + 1); + if ((numSpilled > 0) + || (anyRefs)) { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + storeToReg(simStackAt(simStackPtr - numArgs), ReceiverResultReg); + } + else { + cascade0 = simStackAt(simStackPtr - numArgs); + storeToReg(cascade0, ReceiverResultReg); + (cascade0->type = SSRegister); + (cascade0->registerr = ReceiverResultReg); + + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + } + } + else { + if (numArgs > 0) { + if (((numRegArgs()) > 1) + && (numArgs > 1)) { + /* begin ssAllocateRequiredReg:upThrough: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << Arg0Reg), simStackPtr - 2, simNativeStackPtr); + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << Arg1Reg), simStackPtr - 1, simNativeStackPtr); + } + else { + /* begin ssAllocateRequiredReg:upThrough: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << Arg0Reg), simStackPtr - 1, simNativeStackPtr); + } + } + if (((numRegArgs()) > 1) + && (numArgs > 1)) { + popToReg(simStackAt(simStackPtr), Arg1Reg); + } + if (numArgs > 0) { + popToReg(simStackAt((simStackPtr - numArgs) + 1), Arg0Reg); + } + popToReg(simStackAt(simStackPtr - numArgs), ReceiverResultReg); + } + + /* If there are no spills and no references to ReceiverResultReg + the fetch of ReceiverResultReg from the stack can be avoided + by assigning directly to ReceiverResultReg and pushing it. + Move the args to the register arguments, being careful to do + so last to first so e.g. previous contents don't get overwritten. + Also check for any arg registers in use by other args. */ + ssPop(numArgs + 1); +} + + +/* For assert checking; or rather for avoiding assert fails when dealing with + the hack for block temps in the SqueakV3PlusClosures bytecode set. + */ + + /* StackToRegisterMappingCogit>>#maybeCompilingFirstPassOfBlockWithInitialPushNil */ +static sqInt +maybeCompilingFirstPassOfBlockWithInitialPushNil(void) +{ + return (inBlock == InVanillaBlock) + && ((methodOrBlockNumTemps > methodOrBlockNumArgs) + && (compilationPass == 1)); +} + + +/* If this bytecode has a fixup, some kind of merge needs to be done. There + are 4 cases: + 1) the bytecode has no fixup (fixup isNotAFixup) + do nothing + 2) the bytecode has a non merge fixup + the fixup has needsNonMergeFixup. + The code generating non merge fixup (currently only special selector code) + is responsible + for the merge so no need to do it. + We set deadCode to false as the instruction can be reached from jumps. + 3) the bytecode has a merge fixup, but execution flow *cannot* fall + through to the merge point. + the fixup has needsMergeFixup and deadCode = true. + ignores the current simStack as it does not mean anything + restores the simStack to the state the jumps to the merge point expects it + to be. + 4) the bytecode has a merge fixup and execution flow *can* fall through to + the merge point. + the fixup has needsMergeFixup and deadCode = false. + flushes the stack to the stack pointer so the fall through execution path + simStack is + in the state the merge point expects it to be. + restores the simStack to the state the jumps to the merge point expects it + to be. + + In addition, if this is a backjump merge point, we patch the fixup to hold + the current simStackPtr + for later assertions. */ + + /* StackToRegisterMappingCogit>>#mergeWithFixupIfRequired: */ +static NoDbgRegParms sqInt +mergeWithFixupIfRequired(BytecodeFixup *fixup) +{ + SimStackEntry *cascade0; + sqInt i; + + /* begin assertCorrectSimStackPtr */ + assert((simSpillBase >= methodOrBlockNumTemps) + || ((maybeCompilingFirstPassOfBlockWithInitialPushNil()) + && (simSpillBase > methodOrBlockNumArgs))); + if (needsFrame + && (simSpillBase > 0)) { + assert(((((simStackAt(simSpillBase - 1))->spilled)) == 1) + || ((maybeCompilingFirstPassOfBlockWithInitialPushNil()) + && (simSpillBase > methodOrBlockNumArgs))); + assert((simSpillBase > simStackPtr) + || ((((simStackAt(simSpillBase))->spilled)) == 0)); + } + + /* case 1 */ + if (!((fixup->targetInstruction))) { + return 0; + } + + /* case 2 */ + if ((((usqInt)((fixup->targetInstruction)))) == NeedsNonMergeFixupFlag) { + deadCode = 0; + return 0; + } + + /* cases 3 and 4 */ + assert(isMergeFixup(fixup)); + traceMerge(fixup); + if (deadCode) { + assert((((fixup->simStackPtr)) >= methodOrBlockNumTemps) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil())); + simStackPtr = (fixup->simStackPtr); + } + else { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= simStackPtr) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < simStackPtr) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : simStackPtr)); i <= simStackPtr; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = simStackPtr + 1; + } + } + + /* case 3 + Would like to assert fixup simStackPtr >= methodOrBlockNumTemps + but can't because of the initialNils hack. + case 4 + cases 3 and 4 */ + deadCode = 0; + if ((fixup->isTargetOfBackwardBranch)) { + (fixup->simStackPtr = simStackPtr); + } + (fixup->targetInstruction = genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + assert(simStackPtr == ((fixup->simStackPtr))); + + /* begin restoreSimStackAtMergePoint: */ + /* begin voidReceiverOptStatus */ + ((simSelf())->liveRegister = NoReg); + for (i = (methodOrBlockNumTemps + 1); i <= simStackPtr; i += 1) { + cascade0 = simStackAt(i); + (cascade0->type = SSSpill); + (cascade0->offset = FoxMFReceiver - ((i - methodOrBlockNumArgs) * BytesPerOop)); + (cascade0->registerr = FPReg); + (cascade0->spilled = 1); + } + simSpillBase = simStackPtr + 1; + return 0; +} + + /* StackToRegisterMappingCogit>>#methodAbortTrampolineFor: */ +static NoDbgRegParms sqInt +methodAbortTrampolineFor(sqInt numArgs) +{ + return methodAbortTrampolines[((numArgs < ((numRegArgs()) + 1)) ? numArgs : ((numRegArgs()) + 1))]; +} + + /* StackToRegisterMappingCogit>>#needsFrameIfMod16GENumArgs: */ +static NoDbgRegParms sqInt +needsFrameIfMod16GENumArgs(sqInt stackDelta) +{ + return (byte0 % 16) >= methodOrBlockNumArgs; +} + + +/* As of August 2013, the code generator can't deal with spills in frameless + methods (the + issue is to do with the stack offset to get at an argument, which is + changed when there's a spill). + In e.g. TextColor>>#dominates: other ^other class == self class the second + send of class + needs also rto allocate a register that the first one used, but the first + one's register can't be + spilled. So avoid this by only allowing class to be sent if the stack + contains a single element. */ + + /* StackToRegisterMappingCogit>>#needsFrameIfStackGreaterThanOne: */ +static NoDbgRegParms sqInt +needsFrameIfStackGreaterThanOne(sqInt stackDelta) +{ + return stackDelta > 1; +} + + /* StackToRegisterMappingCogit>>#numberOfSpillsInTopNItems: */ +static NoDbgRegParms sqInt +numberOfSpillsInTopNItems(sqInt n) +{ + sqInt i; + + for (i = simStackPtr; i >= ((simStackPtr - n) + 1); i += -1) { + if ((((simStackAt(i))->type)) == SSSpill) { + return n - (simStackPtr - i); + } + } + return 0; +} + + /* StackToRegisterMappingCogit>>#picAbortTrampolineFor: */ +static NoDbgRegParms sqInt +picAbortTrampolineFor(sqInt numArgs) +{ + return picAbortTrampolines[((numArgs < ((numRegArgs()) + 1)) ? numArgs : ((numRegArgs()) + 1))]; +} + + +/* Try and keep the register contents for ReceiverResultReg if it is top of + stack + */ + + /* StackToRegisterMappingCogit>>#preserveTopInClassRegIfIsReceiverResultReg */ +static void +preserveTopInClassRegIfIsReceiverResultReg(void) +{ + if (((((ssTop())->type)) == SSRegister) + && ((((ssTop())->registerr)) == ReceiverResultReg)) { + /* begin ssAllocateRequiredReg: */ + ssAllocateRequiredRegMaskupThroughupThroughNative((1U << ClassReg), simStackPtr, simNativeStackPtr); + storeToReg(ssTop(), ClassReg); + ((ssTop())->registerr = ClassReg); + } +} + + /* StackToRegisterMappingCogit>>#prevInstIsPCAnnotated */ +static sqInt +prevInstIsPCAnnotated(void) +{ + sqInt prevIndex; + AbstractInstruction *prevInst; + + if (!(opcodeIndex > 0)) { + return 0; + } + prevIndex = opcodeIndex - 1; + while (1) { + if (prevIndex <= 0) { + return 0; + } + prevInst = abstractInstructionAt(prevIndex); + if (((!((prevInst->annotation)) + ? 0 + : (prevInst->annotation))) >= HasBytecodePC) { + return 1; + } + if (!(((prevInst->opcode)) == Label)) break; + prevIndex -= 1; + } + return 0; +} + + +/* Used to mark ReceiverResultReg as dead or not containing simSelf. + Used when the simStack has already been flushed, e.g. for sends. */ + + /* StackToRegisterMappingCogit>>#receiverIsInReceiverResultReg */ +static int +receiverIsInReceiverResultReg(void) +{ + return (((simSelf())->liveRegister)) == ReceiverResultReg; +} + + +/* When a block must be recompiled due to overestimating the + numInitialNils fixups must be restored, which means rescannning + since backward branches need their targets initialized. */ + + /* StackToRegisterMappingCogit>>#reinitializeFixupsFrom:through: */ +static NoDbgRegParms void +reinitializeFixupsFromthrough(sqInt start, sqInt end) +{ + BytecodeDescriptor *descriptor; + sqInt distance; + BytecodeFixup *fixup; + sqInt nExts; + sqInt pc; + BytecodeFixup *self_in_CogSSBytecodeFixup; + sqInt targetPC; + + pc = start; + nExts = 0; + while (pc <= end) { + /* begin fixupAt: */ + self_in_CogSSBytecodeFixup = fixupAtIndex(pc - initialPC); + (self_in_CogSSBytecodeFixup->targetInstruction) = 0; + (self_in_CogSSBytecodeFixup->simStackPtr) = 0; + byte0 = (fetchByteofObject(pc, methodObj)) + bytecodeSetOffset; + descriptor = generatorAt(byte0); + if ((isBranch(descriptor)) + && ((/* begin isBackwardBranch:at:exts:in: */ + assert(((descriptor->spanFunction))), + (((descriptor->spanFunction))(descriptor, pc, nExts, methodObj)) < 0))) { + distance = ((descriptor->spanFunction))(descriptor, pc, nExts, methodObj); + targetPC = (pc + ((descriptor->numBytes))) + distance; + + /* begin initializeFixupAt: */ + /* begin fixupAt: */ + fixup = fixupAtIndex(targetPC - initialPC); + (fixup->targetInstruction) = ((AbstractInstruction *) NeedsMergeFixupFlag); + + /* begin setIsBackwardBranchFixup */ + (fixup->isTargetOfBackwardBranch) = 1; + } + if ((descriptor->isBlockCreation)) { + distance = ((descriptor->spanFunction))(descriptor, pc, nExts, methodObj); + pc = (pc + ((descriptor->numBytes))) + distance; + } + else { + pc += (descriptor->numBytes); + } + nExts = ((descriptor->isExtension) + ? nExts + 1 + : 0); + } +} + + +/* Scan the block to determine if the block needs a frame or not */ + + /* StackToRegisterMappingCogit>>#scanBlock: */ +static NoDbgRegParms sqInt +scanBlock(BlockStart *blockStart) +{ + BytecodeDescriptor *descriptor; + sqInt end; + sqInt framelessStackDelta; + sqInt nExts; + sqInt numPushNils; + sqInt (* const numPushNilsFunction)(struct _BytecodeDescriptor *,sqInt,sqInt,sqInt) = squeakV3orSistaV1NumPushNils; + sqInt pc; + sqInt pushingNils; + + needsFrame = 0; + prevBCDescriptor = null; + methodOrBlockNumArgs = (blockStart->numArgs); + inBlock = InVanillaBlock; + pc = (blockStart->startpc); + end = ((blockStart->startpc)) + ((blockStart->span)); + framelessStackDelta = (nExts = (extA = (numExtB = (extB = 0)))); + pushingNils = 1; + while (pc < end) { + descriptor = loadBytesAndGetDescriptor(pc); + if ((descriptor->isExtension)) { + ((descriptor->generator))(); + } + if (!needsFrame) { + if ((!((descriptor->needsFrameFunction))) + || (((descriptor->needsFrameFunction))(framelessStackDelta))) { + needsFrame = 1; + } + else { + framelessStackDelta += (descriptor->stackDelta); + } + } + + /* begin maybeNoteDescriptor:blockStart: */ + if ((descriptor->isInstVarRef)) { + (blockStart->hasInstVarRef = 1); + } + if (pushingNils + && (!((descriptor->isExtension)))) { + /* Count the initial number of pushed nils acting as temp initializers. We can't tell + whether an initial pushNil is an operand reference or a temp initializer, except + when the pushNil is a jump target (has a fixup), which never happens: + self systemNavigation browseAllSelect: + [:m| | ebc | + (ebc := m embeddedBlockClosures + select: [:ea| ea decompile statements first isMessage] + thenCollect: [:ea| ea decompile statements first selector]) notEmpty + and: [(#(whileTrue whileFalse whileTrue: whileFalse:) intersection: ebc) notEmpty]] + or if the bytecode set has a push multiple nils bytecode. We simply count initial nils. + Rarely we may end up over-estimating. We will correct by checking the stack depth + at the end of the block in compileBlockBodies. */ + if (((numPushNils = numPushNilsFunction(descriptor, pc, nExts, methodObj))) > 0) { + assert(((descriptor->numBytes)) == 1); + (blockStart->numInitialNils = ((blockStart->numInitialNils)) + numPushNils); + } + else { + pushingNils = 0; + } + } + pc = (pc + ((descriptor->numBytes))) + (((descriptor->isBlockCreation) + ? ((descriptor->spanFunction))(descriptor, pc, nExts, methodObj) + : 0)); + if ((descriptor->isExtension)) { + nExts += 1; + } + else { + nExts = (extA = (numExtB = (extB = 0))); + } + prevBCDescriptor = descriptor; + } + + /* It would be nice of this wasn't necessary but alas we need to do the eager + scan for frameless methods so that we don't end up popping too much off + the simulated stack, e.g. for pushNil; returnTopFromBlock methods. */ + if (!needsFrame) { + assert((framelessStackDelta >= 0) + && (((blockStart->numInitialNils)) >= framelessStackDelta)); + (blockStart->numInitialNils = ((blockStart->numInitialNils)) - framelessStackDelta); + } + return 0; +} + + +/* Scan the method (and all embedded blocks) to determine + - what the last bytecode is; extra bytes at the end of a method are used + to encode things like source pointers or temp names + - if the method needs a frame or not + - what are the targets of any backward branches. + - how many blocks it creates + Answer the block count or on error a negative error code */ + + /* StackToRegisterMappingCogit>>#scanMethod */ +static sqInt +scanMethod(void) +{ + BytecodeDescriptor *descriptor; + sqInt distance; + BytecodeFixup *fixup; + sqInt framelessStackDelta; + sqInt latestContinuation; + sqInt nExts; + sqInt numBlocks; + sqInt pc; + sqInt seenInstVarStore; + sqInt targetPC; + + needsFrame = (useTwoPaths = (seenInstVarStore = 0)); + prevBCDescriptor = null; + if ((primitiveIndex > 0) + && (isQuickPrimitiveIndex(primitiveIndex))) { + return 0; + } + pc = (latestContinuation = initialPC); + numBlocks = (framelessStackDelta = (nExts = (extA = (numExtB = (extB = 0))))); + while (pc <= endPC) { + descriptor = loadBytesAndGetDescriptor(pc); + if ((descriptor->isExtension)) { + if (((descriptor->opcode)) == Nop) { + return EncounteredUnknownBytecode; + } + + /* unknown bytecode tag; see Cogit class>>#generatorTableFrom: */ + ((descriptor->generator))(); + } + if (((descriptor->isReturn)) + && (pc >= latestContinuation)) { + endPC = pc; + } + if (!needsFrame) { + if ((!((descriptor->needsFrameFunction))) + || (((descriptor->needsFrameFunction))(framelessStackDelta))) { + /* With immutability we win simply by avoiding a frame build if the receiver is young and not immutable. */ +# if IMMUTABILITY + if ((descriptor->is1ByteInstVarStore)) { + useTwoPaths = 1; + } + else { + needsFrame = 1; + useTwoPaths = 0; + } +# else // IMMUTABILITY + needsFrame = 1; + useTwoPaths = 0; +# endif + + } + else { + framelessStackDelta += (descriptor->stackDelta); + + /* Without immutability we win if there are two or more stores and the receiver is new. */ +# if IMMUTABILITY +# else + if ((descriptor->is1ByteInstVarStore)) { + if (seenInstVarStore) { + useTwoPaths = 1; + } + else { + seenInstVarStore = 1; + } + } +# endif // IMMUTABILITY + + } + } + if (isBranch(descriptor)) { + distance = ((descriptor->spanFunction))(descriptor, pc, nExts, methodObj); + targetPC = (pc + ((descriptor->numBytes))) + distance; + + /* begin isBackwardBranch:at:exts:in: */ + assert(((descriptor->spanFunction))); + if ((((descriptor->spanFunction))(descriptor, pc, nExts, methodObj)) < 0) { + /* begin initializeFixupAt: */ + /* begin fixupAt: */ + fixup = fixupAtIndex(targetPC - initialPC); + (fixup->targetInstruction) = ((AbstractInstruction *) NeedsMergeFixupFlag); + + /* begin setIsBackwardBranchFixup */ + (fixup->isTargetOfBackwardBranch) = 1; + } + else { + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + } + } + if ((descriptor->isBlockCreation)) { + numBlocks += 1; + distance = ((descriptor->spanFunction))(descriptor, pc, nExts, methodObj); + targetPC = (pc + ((descriptor->numBytes))) + distance; + latestContinuation = ((latestContinuation < targetPC) ? targetPC : latestContinuation); + } + pc += (descriptor->numBytes); + nExts = ((descriptor->isExtension) + ? nExts + 1 + : (extA = (numExtB = (extB = 0)))); + prevBCDescriptor = descriptor; + } + return numBlocks; +} + + /* StackToRegisterMappingCogit>>#squeakV3orSistaV1PushNilSize:numInitialNils: */ +static NoDbgRegParms sqInt +squeakV3orSistaV1PushNilSizenumInitialNils(sqInt aMethodObj, sqInt numInitialNils) +{ + return (numInitialNils); +} + + /* StackToRegisterMappingCogit>>#squeakV3orSistaV1:Num:Push:Nils: */ +static NoDbgRegParms sqInt +squeakV3orSistaV1NumPushNils(BytecodeDescriptor *descriptor, sqInt pc, sqInt nExts, sqInt aMethodObj) +{ + return (/* v3:Num:Push:Nils: */ + (((descriptor->generator)) == genPushConstantNilBytecode + ? 1 + : 0)); +} + + /* StackToRegisterMappingCogit>>#ssAllocateRequiredRegMask:upThrough:upThroughNative: */ +static NoDbgRegParms void +ssAllocateRequiredRegMaskupThroughupThroughNative(sqInt requiredRegsMask, sqInt stackPtr, sqInt nativeStackPtr) +{ + sqInt i; + sqInt iSqInt; + sqInt lastRequired; + sqInt lastRequiredNative; + sqInt liveRegs; + + lastRequired = -1; + lastRequiredNative = -1; + + /* compute live regs while noting the last occurrence of required regs. + If these are not free we must spill from simSpillBase to last occurrence. + Note we are conservative here; we could allocate FPReg in frameless methods. */ + liveRegs = (1U << FPReg) | (1U << SPReg); + for (i = (((simSpillBase < 0) ? 0 : simSpillBase)); i <= stackPtr; i += 1) { + liveRegs = liveRegs | (registerMask(simStackAt(i))); + if ((((registerMask(simStackAt(i))) & requiredRegsMask) != 0)) { + lastRequired = i; + } + } + + /* If any of requiredRegsMask are live we must spill. */ + if (((liveRegs & requiredRegsMask) != 0)) { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= lastRequired) { + for (iSqInt = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < lastRequired) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : lastRequired)); iSqInt <= lastRequired; iSqInt += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(iSqInt), frameOffsetOfTemporary(iSqInt - 1), FPReg); + } + simSpillBase = lastRequired + 1; + } + assert(!(((((liveRegisters()) & requiredRegsMask) != 0)))); + } +} + + +/* Any occurrences on the stack of the value being stored (which is the top + of stack) + must be flushed, and hence any values colder than them stack. */ + + /* StackToRegisterMappingCogit>>#ssFlushUpThroughReceiverVariable: */ +static NoDbgRegParms void +ssFlushUpThroughReceiverVariable(sqInt slotIndex) +{ + SimStackEntry *desc; + sqInt i; + sqInt index; + + + /* begin ssFlushUpThrough: */ + assert(simSpillBase >= 0); + for (index = (simStackPtr - 1); index >= simSpillBase; index += -1) { + if (((desc = simStackAt(index)), + (((desc->type)) == SSBaseOffset) + && ((((desc->registerr)) == ReceiverResultReg) + && (((desc->offset)) == (slotOffsetOfInstVarIndex(slotIndex)))))) { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= index) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < index) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : index)); i <= index; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = index + 1; + } + goto l1; + } + } + /* end ssFlushUpThrough: */ +l1:; +} + + +/* Any occurrences on the stack of the value being stored (which is the top + of stack) + must be flushed, and hence any values colder than them stack. */ + + /* StackToRegisterMappingCogit>>#ssFlushUpThroughTemporaryVariable: */ +static NoDbgRegParms void +ssFlushUpThroughTemporaryVariable(sqInt tempIndex) +{ + SimStackEntry *desc; + sqInt i; + sqInt index; + sqInt offset; + + offset = ((simStackAt(tempIndex + 1))->offset); + assert(offset == (frameOffsetOfTemporary(tempIndex))); + + /* begin ssFlushUpThrough: */ + assert(simSpillBase >= 0); + for (index = (simStackPtr - 1); index >= simSpillBase; index += -1) { + if (((desc = simStackAt(index)), + (((desc->type)) == SSBaseOffset) + && ((((desc->registerr)) == FPReg) + && (((desc->offset)) == offset)))) { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= index) { + for (i = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < index) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : index)); i <= index; i += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(i), frameOffsetOfTemporary(i - 1), FPReg); + } + simSpillBase = index + 1; + } + goto l1; + } + } + /* end ssFlushUpThrough: */ +l1:; +} + + /* StackToRegisterMappingCogit>>#ssPop: */ +static NoDbgRegParms void +ssPop(sqInt n) +{ + sqInt i; + sqInt toDoLimit; + + assert(((simStackPtr - n) >= methodOrBlockNumTemps) + || (((!needsFrame) + && ((simStackPtr - n) >= 0)) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil()))); + simStackPtr -= n; + + /* begin updateSimSpillBase */ + assert(((simSpillBase > methodOrBlockNumTemps) + && (simStackPtr >= methodOrBlockNumTemps)) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil())); + if (simSpillBase > simStackPtr) { + simSpillBase = simStackPtr + 1; + while (((simSpillBase - 1) > methodOrBlockNumTemps) + && (!(((simStackAt(simSpillBase - 1))->spilled)))) { + simSpillBase -= 1; + } + } + else { + while ((((simStackAt(simSpillBase))->spilled)) + && (simSpillBase <= simStackPtr)) { + simSpillBase += 1; + } + } + toDoLimit = (((simSpillBase - 1) < simStackPtr) ? (simSpillBase - 1) : simStackPtr); + for (i = (methodOrBlockNumTemps + 1); i <= toDoLimit; i += 1) { + assert((((simStackAt(i))->spilled)) == 1); + } + assert((simSpillBase > simStackPtr) + || ((((simStackAt(simSpillBase))->spilled)) == 0)); +} + + /* StackToRegisterMappingCogit>>#ssPushAnnotatedConstant: */ +static NoDbgRegParms sqInt +ssPushAnnotatedConstant(sqInt literal) +{ + AbstractInstruction *abstractInstruction; + + ssPushConstant(literal); + + /* begin annotateInstructionForBytecode */ + abstractInstruction = (prevInstIsPCAnnotated() + ? gen(Nop) + : genoperandoperand(Label, (labelCounter += 1), bytecodePC)); + + /* begin annotateBytecode: */ + (abstractInstruction->annotation = HasBytecodePC); + return 0; +} + + /* StackToRegisterMappingCogit>>#ssPushBase:offset: */ +static NoDbgRegParms sqInt +ssPushBaseoffset(sqInt reg, sqInt offset) +{ + SimStackEntry *cascade0; + sqInt i; + sqInt toDoLimit; + + ssPush(1); + + /* begin ssTop */ + cascade0 = simStackAt(simStackPtr); + (cascade0->type = SSBaseOffset); + (cascade0->spilled = 0); + (cascade0->registerr = reg); + (cascade0->offset = offset); + (cascade0->bcptr = bytecodePC); + + /* begin updateSimSpillBase */ + assert(((simSpillBase > methodOrBlockNumTemps) + && (simStackPtr >= methodOrBlockNumTemps)) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil())); + if (simSpillBase > simStackPtr) { + simSpillBase = simStackPtr + 1; + while (((simSpillBase - 1) > methodOrBlockNumTemps) + && (!(((simStackAt(simSpillBase - 1))->spilled)))) { + simSpillBase -= 1; + } + } + else { + while ((((simStackAt(simSpillBase))->spilled)) + && (simSpillBase <= simStackPtr)) { + simSpillBase += 1; + } + } + toDoLimit = (((simSpillBase - 1) < simStackPtr) ? (simSpillBase - 1) : simStackPtr); + for (i = (methodOrBlockNumTemps + 1); i <= toDoLimit; i += 1) { + assert((((simStackAt(i))->spilled)) == 1); + } + assert((simSpillBase > simStackPtr) + || ((((simStackAt(simSpillBase))->spilled)) == 0)); + return 0; +} + + /* StackToRegisterMappingCogit>>#ssPushConstant: */ +static NoDbgRegParms sqInt +ssPushConstant(sqInt literal) +{ + SimStackEntry *cascade0; + sqInt i; + sqInt toDoLimit; + + ssPush(1); + + /* begin ssTop */ + cascade0 = simStackAt(simStackPtr); + (cascade0->type = SSConstant); + (cascade0->spilled = 0); + (cascade0->constant = literal); + (cascade0->bcptr = bytecodePC); + + /* begin updateSimSpillBase */ + assert(((simSpillBase > methodOrBlockNumTemps) + && (simStackPtr >= methodOrBlockNumTemps)) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil())); + if (simSpillBase > simStackPtr) { + simSpillBase = simStackPtr + 1; + while (((simSpillBase - 1) > methodOrBlockNumTemps) + && (!(((simStackAt(simSpillBase - 1))->spilled)))) { + simSpillBase -= 1; + } + } + else { + while ((((simStackAt(simSpillBase))->spilled)) + && (simSpillBase <= simStackPtr)) { + simSpillBase += 1; + } + } + toDoLimit = (((simSpillBase - 1) < simStackPtr) ? (simSpillBase - 1) : simStackPtr); + for (i = (methodOrBlockNumTemps + 1); i <= toDoLimit; i += 1) { + assert((((simStackAt(i))->spilled)) == 1); + } + assert((simSpillBase > simStackPtr) + || ((((simStackAt(simSpillBase))->spilled)) == 0)); + return 0; +} + + /* StackToRegisterMappingCogit>>#ssPushDesc: */ +static NoDbgRegParms sqInt +ssPushDesc(SimStackEntry simStackEntry) +{ + sqInt i; + sqInt toDoLimit; + + if (((simStackEntry.type)) == SSSpill) { + (simStackEntry.type = SSBaseOffset); + } + (simStackEntry.spilled = 0); + (simStackEntry.bcptr = bytecodePC); + simStack[(simStackPtr += 1)] = simStackEntry; + + /* begin updateSimSpillBase */ + assert(((simSpillBase > methodOrBlockNumTemps) + && (simStackPtr >= methodOrBlockNumTemps)) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil())); + if (simSpillBase > simStackPtr) { + simSpillBase = simStackPtr + 1; + while (((simSpillBase - 1) > methodOrBlockNumTemps) + && (!(((simStackAt(simSpillBase - 1))->spilled)))) { + simSpillBase -= 1; + } + } + else { + while ((((simStackAt(simSpillBase))->spilled)) + && (simSpillBase <= simStackPtr)) { + simSpillBase += 1; + } + } + toDoLimit = (((simSpillBase - 1) < simStackPtr) ? (simSpillBase - 1) : simStackPtr); + for (i = (methodOrBlockNumTemps + 1); i <= toDoLimit; i += 1) { + assert((((simStackAt(i))->spilled)) == 1); + } + assert((simSpillBase > simStackPtr) + || ((((simStackAt(simSpillBase))->spilled)) == 0)); + return 0; +} + + /* StackToRegisterMappingCogit>>#ssPushRegister: */ +static NoDbgRegParms sqInt +ssPushRegister(sqInt reg) +{ + SimStackEntry *cascade0; + sqInt i; + sqInt toDoLimit; + + ssPush(1); + + /* begin ssTop */ + cascade0 = simStackAt(simStackPtr); + (cascade0->type = SSRegister); + (cascade0->spilled = 0); + (cascade0->registerr = reg); + (cascade0->bcptr = bytecodePC); + + /* begin updateSimSpillBase */ + assert(((simSpillBase > methodOrBlockNumTemps) + && (simStackPtr >= methodOrBlockNumTemps)) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil())); + if (simSpillBase > simStackPtr) { + simSpillBase = simStackPtr + 1; + while (((simSpillBase - 1) > methodOrBlockNumTemps) + && (!(((simStackAt(simSpillBase - 1))->spilled)))) { + simSpillBase -= 1; + } + } + else { + while ((((simStackAt(simSpillBase))->spilled)) + && (simSpillBase <= simStackPtr)) { + simSpillBase += 1; + } + } + toDoLimit = (((simSpillBase - 1) < simStackPtr) ? (simSpillBase - 1) : simStackPtr); + for (i = (methodOrBlockNumTemps + 1); i <= toDoLimit; i += 1) { + assert((((simStackAt(i))->spilled)) == 1); + } + assert((simSpillBase > simStackPtr) + || ((((simStackAt(simSpillBase))->spilled)) == 0)); + return 0; +} + + /* StackToRegisterMappingCogit>>#ssPush: */ +static NoDbgRegParms void +ssPush(sqInt n) +{ + simStackPtr += n; +} + + /* StackToRegisterMappingCogit>>#ssSelfDescriptor */ +static SimStackEntry +ssSelfDescriptor(void) +{ + return simStack[0]; +} + + +/* In addition to ssStorePop:toReg:, if this is a store and not + a popInto I change the simulated stack to use the register + for the top value */ + + /* StackToRegisterMappingCogit>>#ssStoreAndReplacePop:toReg: */ +static NoDbgRegParms void +ssStoreAndReplacePoptoReg(sqInt popBoolean, sqInt reg) +{ + char topSpilled; + + topSpilled = ((ssTop())->spilled); + ssStorePoptoReg(popBoolean + || (topSpilled), reg); + if (!popBoolean) { + if (!topSpilled) { + ssPop(1); + } + ssPushRegister(reg); + } +} + + +/* Store or pop the top simulated stack entry to a register. + Use preferredReg if the entry is not itself a register. + Answer the actual register the result ends up in. */ + + /* StackToRegisterMappingCogit>>#ssStorePop:toPreferredReg: */ +static NoDbgRegParms sqInt +ssStorePoptoPreferredReg(sqInt popBoolean, sqInt preferredReg) +{ + sqInt actualReg; + + actualReg = preferredReg; + if ((((ssTop())->type)) == SSRegister) { + assert(!(((ssTop())->spilled))); + actualReg = ((ssTop())->registerr); + } + ssStorePoptoReg(popBoolean, actualReg); + return actualReg; +} + + +/* Store or pop the top simulated stack entry to a register. + N.B.: popToReg: and storeToReg: does not generate anything if + it moves a register to the same register. */ + + /* StackToRegisterMappingCogit>>#ssStorePop:toReg: */ +static NoDbgRegParms void +ssStorePoptoReg(sqInt popBoolean, sqInt reg) +{ + if (popBoolean) { + popToReg(ssTop(), reg); + ssPop(1); + } + else { + storeToReg(ssTop(), reg); + } +} + + /* StackToRegisterMappingCogit>>#ssTop */ +static SimStackEntry * +ssTop(void) +{ + return simStackAt(simStackPtr); +} + + /* StackToRegisterMappingCogit>>#ssValue: */ +static NoDbgRegParms SimStackEntry * +ssValue(sqInt n) +{ + return simStackAt(simStackPtr - n); +} + + /* StackToRegisterMappingCogit>>#stackEntryIsBoolean: */ +static NoDbgRegParms sqInt +stackEntryIsBoolean(SimStackEntry *simStackEntry) +{ + return (((simStackEntry->type)) == SSConstant) + && ((((simStackEntry->constant)) == (trueObject())) + || (((simStackEntry->constant)) == (falseObject()))); +} + + +/* Answer if the stack is valid up to, but not including, simSpillBase. */ + + /* StackToRegisterMappingCogit>>#tempsValidAndVolatileEntriesSpilled */ +static sqInt +tempsValidAndVolatileEntriesSpilled(void) +{ + sqInt culprit; + sqInt i; + + culprit = 0; + for (i = 1; i <= methodOrBlockNumTemps; i += 1) { + if (!(((((simStackAt(i))->type)) == SSBaseOffset) + || (maybeCompilingFirstPassOfBlockWithInitialPushNil()))) { + if (!culprit) { + culprit = i; + } + return 0; + } + } + for (i = (methodOrBlockNumTemps + 1); i < simSpillBase; i += 1) { + if (!(((simStackAt(i))->spilled))) { + if (!culprit) { + culprit = i; + } + return 0; + } + } + return 1; +} + + +/* If the sequence of bytecodes is + push: (Array new: 1) + popIntoTemp: tempIndex + pushConstant: const or pushTemp: n + popIntoTemp: 0 inVectorAt: tempIndex + collapse this into + tempAt: tempIndex put: {const or temp} + and answer true, otherwise answer false. + One might think that we should look for a sequence of more than + one pushes and pops but this is extremely rare. + Exclude pushRcvr: n to avoid potential complications with context inst + vars. */ + + /* StackToRegisterMappingCogit>>#tryCollapseTempVectorInitializationOfSize: */ +static NoDbgRegParms sqInt +tryCollapseTempVectorInitializationOfSize(sqInt slots) +{ + sqInt pc; + BytecodeDescriptor *pushArrayDesc; + BytecodeDescriptor *pushValueDesc; + sqInt reg; + sqInt remoteTempIndex; + BytecodeDescriptor *storeArrayDesc; + BytecodeDescriptor *storeValueDesc; + usqInt tempIndex; + + if (slots != 1) { + return 0; + } + + /* begin generatorForPC: */ + pushArrayDesc = generatorAt(bytecodeSetOffset + (fetchByteofObject(bytecodePC, methodObj))); + assert(((pushArrayDesc->generator)) == genPushNewArrayBytecode); + pc = bytecodePC + ((pushArrayDesc->numBytes)); + + /* begin generatorForPC: */ + storeArrayDesc = generatorAt(bytecodeSetOffset + (fetchByteofObject(pc, methodObj))); + if (((storeArrayDesc->generator)) == genStoreAndPopTemporaryVariableBytecode) { + tempIndex = (fetchByteofObject(bytecodePC + ((pushArrayDesc->numBytes)), methodObj)) & 7; + } + else { + if (!(((storeArrayDesc->generator)) == genLongStoreAndPopTemporaryVariableBytecode)) { + return 0; + } + tempIndex = fetchByteofObject((bytecodePC + ((pushArrayDesc->numBytes))) + 1, methodObj); + } + pc = (bytecodePC + ((pushArrayDesc->numBytes))) + ((storeArrayDesc->numBytes)); + + /* begin generatorForPC: */ + pushValueDesc = generatorAt(bytecodeSetOffset + (fetchByteofObject(pc, methodObj))); + if (!((((pushValueDesc->generator)) == genPushLiteralConstantBytecode) + || ((((pushValueDesc->generator)) == genPushQuickIntegerConstantBytecode) + || (((pushValueDesc->generator)) == genPushTemporaryVariableBytecode)))) { + return 0; + } + pc = ((bytecodePC + ((pushArrayDesc->numBytes))) + ((storeArrayDesc->numBytes))) + ((pushValueDesc->numBytes)); + + /* begin generatorForPC: */ + storeValueDesc = generatorAt(bytecodeSetOffset + (fetchByteofObject(pc, methodObj))); + remoteTempIndex = fetchByteofObject((((bytecodePC + ((pushArrayDesc->numBytes))) + ((storeArrayDesc->numBytes))) + ((pushValueDesc->numBytes))) + 2, methodObj); + if (!((((storeValueDesc->generator)) == genStoreAndPopRemoteTempLongBytecode) + && (tempIndex == remoteTempIndex))) { + return 0; + } + genNewArrayOfSizeinitialized(1, 0); + evaluateat(pushValueDesc, (bytecodePC + ((pushArrayDesc->numBytes))) + ((storeArrayDesc->numBytes))); + reg = ssStorePoptoPreferredReg(1, TempReg); + genStoreSourceRegslotIndexintoNewObjectInDestReg(reg, 0, ReceiverResultReg); + ssPushRegister(ReceiverResultReg); + evaluateat(storeArrayDesc, bytecodePC + ((pushArrayDesc->numBytes))); + bytecodePC = ((bytecodePC + ((storeArrayDesc->numBytes))) + ((pushValueDesc->numBytes))) + ((storeValueDesc->numBytes)); + + /* + pushArrayDesc numBytes this gets added by nextBytecodePCFor:at:exts:in: */ + return 1; +} + + /* StackToRegisterMappingCogit>>#violatesEnsureSpilledSpillAssert */ +static sqInt +violatesEnsureSpilledSpillAssert(void) +{ + return 1; +} + + +/* Used when ReceiverResultReg is allocated for other than simSelf, and + there may be references to ReceiverResultReg which need to be spilled. */ + + /* StackToRegisterMappingCogit>>#voidReceiverResultRegContainsSelf */ +static void +voidReceiverResultRegContainsSelf(void) +{ + sqInt i; + sqInt iSqInt; + sqInt spillIndex; + + /* begin voidReceiverOptStatus */ + ((simSelf())->liveRegister = NoReg); + spillIndex = 0; + for (i = ((((methodOrBlockNumTemps + 1) < simSpillBase) ? simSpillBase : (methodOrBlockNumTemps + 1))); i <= simStackPtr; i += 1) { + if ((registerOrNone(simStackAt(i))) == ReceiverResultReg) { + spillIndex = i; + } + } + if (spillIndex > 0) { + /* begin ssFlushTo: */ + assert(tempsValidAndVolatileEntriesSpilled()); + if (simSpillBase <= spillIndex) { + for (iSqInt = (((((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) < spillIndex) ? ((((((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) < simStackPtr) ? (((simSpillBase < (methodOrBlockNumTemps + 1)) ? (methodOrBlockNumTemps + 1) : simSpillBase)) : simStackPtr)) : spillIndex)); iSqInt <= spillIndex; iSqInt += 1) { + assert(needsFrame); + ensureSpilledAtfrom(simStackAt(iSqInt), frameOffsetOfTemporary(iSqInt - 1), FPReg); + } + simSpillBase = spillIndex + 1; + } + } +} diff --git a/src/spur64.cog.livetyping/cogmethod.h b/src/spur64.cog.livetyping/cogmethod.h new file mode 100644 index 0000000000..ec20d3c0dc --- /dev/null +++ b/src/spur64.cog.livetyping/cogmethod.h @@ -0,0 +1,39 @@ +/* Automatically generated by + CCodeGenerator * VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba + (Cog-eem.511, Compiler-ct.535) + */ + +typedef struct { + unsigned short homeOffset; + unsigned short startpc; + unsigned int padToWord; + unsigned cmNumArgs : 8; + unsigned cmType : 3; + unsigned cmRefersToYoung : 1; + unsigned cpicHasMNUCaseOrCMIsFullBlock : 1; + unsigned cmUsageCount : 3; + unsigned cmUsesPenultimateLit : 1; + unsigned cbUsesInstVars : 1; + unsigned cmHasMovableLiteral : 1; + unsigned cmUnusedFlag : 1; + unsigned stackCheckOffset : 12; + } CogBlockMethod; + +typedef struct { + sqLong objectHeader; + unsigned cmNumArgs : 8; + unsigned cmType : 3; + unsigned cmRefersToYoung : 1; + unsigned cpicHasMNUCaseOrCMIsFullBlock : 1; + unsigned cmUsageCount : 3; + unsigned cmUsesPenultimateLit : 1; + unsigned cbUsesInstVars : 1; + unsigned cmHasMovableLiteral : 1; + unsigned cmUnusedFlag : 1; + unsigned stackCheckOffset : 12; + unsigned short blockSize; + unsigned short blockEntryOffset; + sqInt methodObject; + sqInt methodHeader; + sqInt selector; + } CogMethod; diff --git a/src/spur64.cog.livetyping/cointerp.c b/src/spur64.cog.livetyping/cointerp.c new file mode 100644 index 0000000000..fbe60becf9 --- /dev/null +++ b/src/spur64.cog.livetyping/cointerp.c @@ -0,0 +1,88351 @@ +/* Automatically generated by + CCodeGeneratorGlobalStructure * VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba + (Cog-eem.511, Compiler-ct.535) + from + LiveTypingCoInterpreter * VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba + */ +static char __buildInfo[] = "LiveTypingCoInterpreter * VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba " __DATE__ ; +char *__interpBuildInfo = __buildInfo; + + + +#if USE_GLOBAL_STRUCT +# define SQ_USE_GLOBAL_STRUCT 1 +#else +# define SQ_USE_GLOBAL_STRUCT 0 +#endif +#if USE_GLOBAL_STRUCT_REG +# define SQ_USE_GLOBAL_STRUCT_REG 1 +#else +# define SQ_USE_GLOBAL_STRUCT_REG 0 +#endif + +#include "sq.h" +#include /* for printf */ +#include /* for e.g. alloca */ +#include +#include /* for wint_t */ +#include "vmCallback.h" +#include "sqMemoryFence.h" +#include "sqImageFileAccess.h" +#include "sqSetjmpShim.h" +#include "dispdbg.h" +#include "sqCogStackAlignment.h" +#include "cogmethod.h" +#include "cointerp.h" +#include "cogit.h" + + +/* StackInterpreter class>>preambleCCode */ +/* Disable Intel compiler inlining of warning which is used for breakpoints */ +#pragma auto_inline(off) +sqInt warnpid, erroronwarn; +EXPORT(void) +warning(const char *s) { /* Print an error message but don't necessarily exit. */ + if (erroronwarn) error(s); + if (warnpid) + printf("\n%s pid %ld\n", s, (long)warnpid); + else + printf("\n%s\n", s); +} +EXPORT(void) +warningat(const char *s, int l) { /* ditto with line number. */ + /* use alloca to call warning so one does not have to remember to set multiple breakpoints... */ + char *sl = alloca(strlen(s) + 16); + sprintf(sl, "%s %d", s, l); + warning(sl); +} +EXPORT(void) +warninginat(const char *s, const char *f, int l) { /* ditto with function name & line number. */ + /* use alloca to call warning so one does not have to remember to set multiple breakpoints... */ + char *sl = alloca(strlen(s) + strlen(f) + 17); + sprintf(sl, "%s %s %d", s, f, l); + warning(sl); +} +#pragma auto_inline(on) + +/* end StackInterpreter class>>preambleCCode */ + + +/*** Constants ***/ +#define ActiveProcessIndex 1 +#define AlternateHeaderHasPrimFlag 0x80000 +#define AlternateHeaderNumLiteralsMask 0x7FFF +#define AltLongStoreBytecode 245 +#define BecameActiveClassFlag 8 +#define BecameCompiledMethodFlag 2 +#define BecameJittedCompiledMethodFlag 16 +#define BecamePointerObjectFlag 1 +#define CacheProbeMax 3 +#define ClassAlien 52 +#define ClassArray 7 +#define ClassArrayCompactIndex 51 +#define ClassBitmap 4 +#define ClassBitmapCompactIndex 53 +#define ClassBlockClosure 36 +#define ClassBlockClosureCompactIndex 37 +#define ClassByteArray 26 +#define ClassByteString 6 +#define ClassByteStringCompactIndex 52 +#define ClassCharacter 19 +#define ClassDoubleByteArray 31 +#define ClassDoubleWordArray 33 +#define ClassExternalAddress 43 +#define ClassExternalData 45 +#define ClassExternalFunction 46 +#define ClassExternalLibrary 47 +#define ClassExternalStructure 44 +#define ClassFloat 9 +#define ClassFloatCompactIndex 34 +#define ClassFullBlockClosureCompactIndex 38 +#define ClassLargeNegativeInteger 42 +#define ClassLargeNegativeIntegerCompactIndex 32 +#define ClassLargePositiveInteger 13 +#define ClassLargePositiveIntegerCompactIndex 33 +#define ClassMessage 15 +#define ClassMessageCompactIndex 35 +#define ClassMethodContext 10 +#define ClassMethodContextCompactIndex 36 +#define ClassPoint 12 +#define ClassPointCompactIndex 54 +#define ClassSemaphore 18 +#define ClassSmallInteger 5 +#define ClassUnsafeAlien 54 +#define ClassWordArray 32 +#define ClosureFirstCopiedValueIndex 3 +#define ClosureIndex 4 +#define ClosureNumArgsIndex 2 +#define ClosureOuterContextIndex 0 +#define ClosureStartPCIndex 1 +#define CMBlock 4 +#define CMClosedPIC 2 +#define CMMethod 5 +#define CMMethodFlaggedForBecome 6 +#define CMOpenPIC 3 +#define ConstMinusOne 0xFFFFFFFFFFFFFFF9ULL +#define ConstOne 9 +#define ConstTwo 17 +#define ConstZero 1 +#define CrossedX 258 +#define CSEnterCriticalSection 2 +#define CSExitCriticalSection 4 +#define CSFromInterpreter 0 +#define CSFromMachineCode 1 +#define CSResume 6 +#define CSSetPriority 16 +#define CSSuspend 10 +#define CSSynchronousSignal 8 +#define CSWait 12 +#define CSYield 14 +#define CtxtTempFrameStart 6 +#define DisownVMForFFICallFlag 8 +#define DisownVMFromCallbackFlag 16 +#define EncounteredUnknownBytecode -6 +#define EndOfRun 0x101 +#define ExcessSignalsIndex 2 +#define ExternalCallLiteralFlagsIndex 2 +#define ExternalCallLiteralFunctionNameIndex 1 +#define ExternalCallLiteralModuleNameIndex 0 +#define ExternalCallLiteralTargetFunctionIndex 3 +#define ExternalObjectsArray 38 +#define ExtraRootsSize 64 +#define FalseObject 1 +#define FirstLinkIndex 0 +#define FoxCallerSavedIP 8 +#define FoxIFReceiver -40 +#define FoxIFrameFlags -24 +#define FoxIFSavedIP -32 +#define FoxMethod -8 +#define FoxMFReceiver -24 +#define FoxSavedFP 0 +#define FoxThisContext -16 +#define FullClosureCompiledBlockIndex 1 +#define FullClosureFirstCopiedValueIndex 4 +#define FullClosureReceiverIndex 3 +#define GCCheckFreeSpace 32 +#define GCCheckPrimCall 128 +#define GCCheckShorten 64 +#define GCModeBecome 8 +#define GCModeFull 1 +#define GCModeImageSegment 16 +#define GCModeNewSpace 2 +#define HasBeenReturnedFromMCPC -1 +#define HasBeenReturnedFromMCPCOop 0xFFFFFFFFFFFFFFF9ULL +#define HashMultiplyConstant 1664525 +#define HeaderIndex 0 +#define IFrameSlots 7 +#define InstanceSpecificationIndex 2 +#define InstructionPointerIndex 1 +#define InstVarTypesArraysIndex 5 +#define KeyIndex 0 +#define LargeContextBit 0x100000 +#define LargeContextSlots 62 +#define LastLinkIndex 1 +#define LiteralStart 1 +#define LongStoreBytecode 129 +#define MarkOnTenure 5 +#define MarkStackRootIndex 0x1000 +#define MaxExternalPrimitiveTableSize 4096 +#define MaxLiteralCountForCompile 60 +#define MaxNegativeErrorCode -8 +#define MaxPrimitiveIndex 583 +#define MaxQuickPrimitiveIndex 519 +#define MaxRTRefCount 7 +#define MessageArgumentsIndex 1 +#define MessageLookupClassIndex 2 +#define MessageSelectorIndex 0 +#define MethodArrayIndex 1 +#define MethodCacheClass 2 +#define MethodCacheEntries 0x400 +#define MethodCacheEntrySize 4 +#define MethodCacheMask 0xFFC +#define MethodCacheMethod 3 +#define MethodCachePrimFunction 4 +#define MethodCacheSelector 1 +#define MethodCacheSize 4096 +#define MethodDictionaryIndex 1 +#define MethodHeaderArgCountShift 27 +#define MethodHeaderFlagBitPosition 31 +#define MethodHeaderTempCountShift 21 +#define MethodIndex 3 +#define MethodTooBig -4 +#define MFMethodFlagHasContextFlag 1 +#define MFMethodFlagIsBlockFlag 2 +#define MFMethodFlagsMask 0x7 +#define MFMethodMask -0x8 +#define MFrameSlots 5 +#define MillisecondClockMask 0x1FFFFFFF +#define MinBackwardJumpCountForCompile 40 +#define MournQueueRootIndex 4098 +#define MultipleBytecodeSetsBitmask 0x200 +#define MyListIndex 3 +#define NextLinkIndex 0 +#define NilObject 0 +#define NotFullyInitialized -1 +#define ObjStackFixedSlots 4 +#define ObjStackFreex 2 +#define ObjStackLimit 0xFF8 +#define ObjStackMyx 1 +#define ObjStackNextx 3 +#define ObjStackPageSlots 0xFFC +#define ObjStackTopx 0 +#define OldBecameNewFlag 4 +#define PrimCallCollectsProfileSamples 16 +#define PrimCallIsExternalCall 32 +#define PrimCallNeedsNewMethod 4 +#define PrimCallOnSmalltalkStack 1 +#define PrimCallOnSmalltalkStackAlign2x 2 +#define PrimitiveErrorTableIndex 51 +#define PrimNumberDoExternalCall 218 +#define PrimNumberDoPrimitive 118 +#define PrimNumberExternalCall 117 +#define PrimNumberFFICall 120 +#define PrimNumberHandlerMarker 199 +#define PrimNumberNoContextSwitchMarker 123 +#define PrimNumberUnwindMarker 198 +#define PrimNumberVMParameter 254 +#define PrimTraceLogSize 256 +#define PriorityIndex 2 +#define ProcessListsIndex 0 +#define ProcessSignalingLowSpace 22 +#define ReceiverIndex 5 +#define RemapBufferSize 25 +#define RememberedSetRootIndex 4099 +#define RemoteTempVarTypesArraysIndex 4 +#define ReturnToInterpreter 1 +#define ReturnTypesArrayIndex 3 +#define ScavengeInProgress 1 +#define SchedulerAssociation 3 +#define SelectorAboutToReturn 48 +#define SelectorAttemptToAssign 50 +#define SelectorCannotInterpret 34 +#define SelectorCannotReturn 21 +#define SelectorDoesNotUnderstand 20 +#define SelectorInvokeCallback 53 +#define SelectorMustBeBoolean 25 +#define SelectorRunWithIn 49 +#define SelectorStart 2 +#define SelectorUnknownBytecode 57 +#define SenderIndex 0 +#define ShouldNotJIT -8 +#define SlidingCompactionInProgress 2 +#define SmallContextSlots 22 +#define SpecialSelectors 23 +#define SpurPrimitiveAccessorDepthShift 8 +#define SpurPrimitiveFlagsMask 0xFF +#define StackPageReachedButUntraced 1 +#define StackPageTraced 2 +#define StackPageTraceInvalid -1 +#define StackPageUnreached 0 +#define StackPointerIndex 2 +#define SuperclassIndex 0 +#define SuspendedContextIndex 1 +#define TempVarTypesArraysIndex 2 +#define TenureByAge 1 +#define TenureByClass 2 +#define TenureToShrinkRT 3 +#define TheDisplay 14 +#define TheFinalizationSemaphore 41 +#define TheInterruptSemaphore 30 +#define TheLowSpaceSemaphore 17 +#define TheTimerSemaphore 29 +#define TopHashBit 0x200000 +#define TopOopBit 0x8000000000000000ULL +#define TraceBlockActivation 2 +#define TraceBlockCreation 3 +#define TraceBufferSize 768 +#define TraceCodeCompaction 6 +#define TraceContextSwitch 1 +#define TraceFullGC 5 +#define TraceIncrementalGC 4 +#if !defined(TraceLog) /* Allow this to be overridden on the compiler command line */ +# define TraceLog 0 +#endif +#define TracePrimitiveFailure 10 +#define TracePrimitiveRetry 11 +#define TraceSend 0 +#define TraceStackOverflow 9 +#define TraceTypeShift 8 +#define TraceVMCallback 7 +#define TraceVMCallbackReturn 8 +#define TrueObject 2 +#define ValidatedClassDoubleByteArrayFlag 1 +#define ValidatedClassDoubleWordArrayFlag 4 +#define ValidatedClassWordArrayFlag 2 +#define ValueIndex 1 +#define WeaklingStackRootIndex 0x1001 +#define XIndex 0 +#define YIndex 1 + +typedef struct { + usqInt sEdenBytes; + usqInt sPastBytes; + usqInt sRememberedSetSize; + usqInt sRememberedSetRedZone; + usqInt sRememberedSetLimit; + usqInt sStatTenures; + usqInt tTenureCriterion; + usqInt tTenureThreshold; + usqInt tRefCountToShrinkRT; + usqInt eSurvivorBytes; + usqInt eRememberedSetSize; + usqInt eStatTenures; + } SpurScavengeLogRecord; + + +typedef struct { + usqInt segStart; + usqInt segSize; + sqInt swizzle; + usqInt containsPinned; + usqInt savedSegSize; + usqInt lastFreeObject; + } SpurSegmentInfo; + + +typedef struct { + usqInt start; + usqInt limit; + } SpurNewSpaceSpace; + + +typedef struct { + usqInt start; + usqInt limit; + usqInt top; + } SpurContiguousObjStack; + + +typedef struct _StackPage { + char *stackLimit; + char *headSP; + char *headFP; + char *baseFP; + char *baseAddress; + char *realStackLimit; + char *lastAddress; + int trace; + int padToWord; + struct _StackPage *nextPage; + struct _StackPage *prevPage; + } StackPage; + +#define CogStackPage StackPage + + + +/*** Function Prototypes ***/ + + +#if !PRODUCTION && defined(PlatformNoDbgRegParms) +# define NoDbgRegParms PlatformNoDbgRegParms +#endif + +#if !defined(NoDbgRegParms) +# define NoDbgRegParms /*empty*/ +#endif + + + +#if !defined(NeverInline) +# define NeverInline /*empty*/ +#endif + +extern sqInt interpret(void); +static NoDbgRegParms CogMethod * cmHomeMethod(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMBlock(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMClosedPIC(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMMethodEtAl(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMMethodFlaggedForBecome(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMOpenPIC(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms sqInt addressIsInPage(StackPage *self_in_CogStackPage, char *address); +static NoDbgRegParms int isFree(StackPage *self_in_CogStackPage); +static NoDbgRegParms sqInt freeStackPageNoAssert(StackPage *aPage); +static NoDbgRegParms void freeStackPage(StackPage *aPage); +static NoDbgRegParms sqInt markStackPageMostRecentlyUsed(StackPage *page); +static NoDbgRegParms sqInt markStackPageNextMostRecentlyUsed(StackPage *page); +static StackPage * newStackPage(void); +static sqInt pageListIsWellFormed(void); +static NoDbgRegParms StackPage * stackPageAt(sqInt index); +static NoDbgRegParms StackPage * stackPageFor(void *pointer); +static double statAverageLivePagesWhenMapping(void); +extern int vmIsInitialized(void); +extern sqInt accessorDepthForPrimitiveIndex(sqInt primIndex); +static NoDbgRegParms void activateNewCogMethodinInterpreter(CogMethod *cogMethod, sqInt inInterpreter); +static NoDbgRegParms void addNewMethodToCache(sqInt classObj); +extern usqInt argumentCountAddress(void); +static NoDbgRegParms CogMethod * asCogHomeMethod(CogBlockMethod *aCogMethod); +static NoDbgRegParms void assertValidExecutionPointersimbarline(usqInt lip, char *lifp, char *lisp, sqInt inInterpreter, sqInt ln); +static NoDbgRegParms sqInt assertValidStackedInstructionPointersInline(StackPage *aStackPage, sqInt ln); +static NoDbgRegParms void assertValidStackedInstructionPointers(sqInt ln); +static NoDbgRegParms void attemptToSwitchToMachineCode(sqInt bcpc); +static NoDbgRegParms void backupContexttoBlockingSendTo(sqInt suspendedContext, sqInt conditionVariable); +extern void callForCogCompiledCodeCompaction(void); +static NoDbgRegParms void callRegisterArgCogMethodatreceiver(CogMethod *cogMethod, sqInt entryOffset, sqInt rcvr); +extern void ceActivateFailingPrimitiveMethod(sqInt aPrimitiveMethod); +extern sqInt ceBaseFrameReturn(sqInt returnValue); +#if IMMUTABILITY +extern sqInt ceCannotAssignTowithIndexvalueToAssign(sqInt immutableObject, sqInt index, sqInt valueToAssign); +#endif /* IMMUTABILITY */ +extern sqInt ceCannotResume(void); +extern void ceCheckAndMaybeRetryPrimitive(sqInt primIndex); +extern void ceCheckForInterrupt(void); +extern sqInt ceContextinstVar(sqInt maybeContext, sqInt slotIndex); +extern sqInt ceContextinstVarvalue(sqInt maybeMarriedContext, sqInt slotIndex, sqInt anOop); +extern sqInt ceInterpretMethodFromPICreceiver(sqInt aMethodObj, sqInt rcvr); +extern sqInt ceMNUFromPICMNUMethodreceiver(sqInt aMethodObj, sqInt rcvr); +extern sqInt ceNewHashOf(sqInt anObject); +extern sqInt ceNonLocalReturn(sqInt returnValue); +extern void ceReapAndResetErrorCodeFor(CogMethod *cogMethod); +static NoDbgRegParms sqInt ceSendAborttonumArgs(sqInt selector, sqInt rcvr, sqInt numArgs); +extern sqInt ceSendFromInLineCacheMiss(CogMethod *cogMethodOrPIC); +extern sqInt ceSendMustBeBooleanTointerpretingAtDelta(sqInt aNonBooleanObject, sqInt jumpSize); +extern sqInt ceSendMustBeBoolean(sqInt anObject); +extern void ceSendaboveClassBindingtonumArgs(sqInt selector, sqInt methodClassBinding, sqInt rcvr, sqInt numArgs); +extern sqInt ceSendabovetonumArgs(sqInt selector, sqInt methodClass, sqInt rcvr, sqInt numArgs); +extern sqInt ceSendsupertonumArgs(sqInt selector, sqInt superNormalBar, sqInt rcvr, sqInt numArgs); +extern void ceStackOverflow(sqInt contextSwitchIfNotNil); +extern void ceTakeProfileSample(void); +extern void ceTraceBlockActivation(void); +extern void ceTraceLinkedSend(sqInt theReceiver); +extern void ceTraceStoreOfinto(sqInt aValue, sqInt anObject); +extern usqInt cFramePointerAddress(void); +extern void checkAssertsEnabledInCoInterpreter(void); +static NoDbgRegParms sqInt checkCodeIntegrity(sqInt gcModes); +extern sqInt checkForAndFollowForwardedPrimitiveState(void); +extern sqInt checkIfCFramePointerInUse(void); +#if LRPCheck +extern int checkingLongRunningPrimitives(void); +#endif /* LRPCheck */ +static NoDbgRegParms sqInt checkOkayFields(sqInt oop); +static NeverInline void checkProfileTickPostPrimitive(void); +static NoDbgRegParms sqInt cogMethodHasRealNonProfilingPrimitive(CogMethod *cogMethod); +extern sqInt cogMethodHasTooManyLiterals(CogMethod *aCogMethod); +extern CogMethod * cogMethodOf(sqInt aMethodOop); +static void commenceCogCompiledCodeCompaction(void); +extern void compilationBreakpointFor(sqInt selectorOop); +static NoDbgRegParms sqInt contextInstructionPointerframe(sqInt theIP, char *theFP); +static NoDbgRegParms void convertFrametoInterpreterFrame(char *theFP, sqInt pcDelta); +extern usqInt cReturnAddressAddress(void); +extern usqInt cStackPointerAddress(void); +static NoDbgRegParms sqInt deferStackLimitSmashAroundwith(void (*functionSymbol)(sqInt), sqInt arg); +static NoDbgRegParms sqInt divorceAllFramesSuchThat(sqInt (*criterion)(char *fp)); +extern void dumpStackAlignmentInfo(void); +static NoDbgRegParms void ensureAllContextsHaveBytecodePCsIf(sqInt (*criterion)(sqInt methodOop)); +static NoDbgRegParms void ensureContextHasBytecodePC(sqInt aContext); +static NoDbgRegParms void ensureContextIsExecutionSafeAfterAssignToStackPointer(sqInt aContext); +static sqInt enterSmalltalkExecutiveImplementation(void); +extern void executeCogMethodfromLinkedSendWithReceiver(CogMethod *cogMethod, sqInt rcvr); +static NoDbgRegParms void executeCogMethodfromUnlinkedSendWithReceiver(CogMethod *cogMethod, sqInt rcvr); +extern void executeCogPICfromLinkedSendWithReceiverandCacheTag(CogMethod *cogPIC, sqInt rcvr, sqInt cacheTag); +static sqInt executeNewMethod(void); +static sqInt executeNewMethodJitting(void); +static NoDbgRegParms sqInt externalInstVarofContext(sqInt offset, sqInt aContext); +static NoDbgRegParms void findNewMethodInClassTag(sqInt classTagArg); +static NoDbgRegParms void flagMethodAsInterpreted(sqInt aMethod); +static void flushExternalPrimitives(void); +static NoDbgRegParms void flushMethodsWithMachineCodePrimitivesAndContinueAnswering(sqInt result); +static void followForwardedFieldsInCurrentMethod(void); +static void followForwardingPointersOfReceiverAndTemporariesInStackZone(void); +static void followForwardingPointersOfReceiversInStackZone(void); +extern void forceInterruptCheckFromHeartbeat(void); +static NoDbgRegParms sqInt frameCallerContext(char *theFP); +static NoDbgRegParms int frameHasContext(char *theFP); +static NoDbgRegParms int frameIsBlockActivation(char *theFP); +static NoDbgRegParms sqInt frameMethodObject(char *theFP); +static NoDbgRegParms usqInt frameNumArgs(char *theFP); +extern usqInt framePointerAddress(void); +static NoDbgRegParms void framePrintDescription(sqInt it); +static NoDbgRegParms sqInt frameReceiver(char *theFP); +extern void (*functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(sqInt methodObj, sqInt primitiveIndex, sqInt *flagsPtr))(void); +extern sqInt getCurrentBytecode(void); +static NoDbgRegParms sqInt handleForwardedSendFaultForReceiverstackDelta(sqInt forwardedReceiver, sqInt stackDelta); +static NoDbgRegParms sqInt handleMNUInMachineCodeToclassForMessage(sqInt selectorIndex, sqInt rcvr, sqInt classForMessage); +static NoDbgRegParms int iframeIsBlockActivation(char *theFP); +static NoDbgRegParms sqInt iframeReceiver(char *theFP); +static NoDbgRegParms usqInt iframeSavedIP(char *theFP); +extern void ifValidWriteBackStackPointersSaveTo(void *theCFP, void *theCSP, char **savedFPP, char **savedSPP); +extern usqInt instructionPointerAddress(void); +static NoDbgRegParms sqInt instructionPointerForFramecurrentFPcurrentIP(char *spouseFP, char *currentFP, sqInt instrPtr); +static NeverInline sqInt interpreterAllocationReserveBytes(void); +static sqInt interpretMethodFromMachineCode(void); +static NoDbgRegParms int isCogMethodReferenceNoAssert(sqInt methodHeader); +extern int isCogMethodReference(sqInt methodHeader); +static NoDbgRegParms sqInt isExternalMethodInPlugin(sqInt methodObj); +extern sqInt isFullBlockMethod(sqInt aMethodObj); +static NoDbgRegParms sqInt isMachineCodeFrameForCogMethodFlaggedForBecome(char *theFP); +static NoDbgRegParms sqInt isMachineCodeFrameForCogMethodWithMachineCodePrim(char *theFP); +static NoDbgRegParms sqInt isMachineCodeFrameForCogMethodWithTooManyLiterals(char *theFP); +static NoDbgRegParms sqInt isMachineCodeFrameForExternalPrimitiveMethod(char *theFP); +static NoDbgRegParms sqInt isMachineCodeFrameForRealNonProfilingPrimitive(char *theFP); +static NoDbgRegParms int isMachineCodeFrame(char *theFP); +static NoDbgRegParms sqInt isRealNonProfilingPrimitiveMethod(sqInt methodObj); +static NoDbgRegParms sqInt isRealNonProfilingPrimitiveMethodprimIndex(sqInt methodObj, sqInt primitiveIndex); +static NoDbgRegParms sqInt isWidowedContextDuringGC(sqInt aOnceMarriedContext); +static NoDbgRegParms sqInt isWidowedContext(sqInt aOnceMarriedContext); +static NoDbgRegParms sqInt ismethodAssignmentToContextWithMachineCodePC(sqInt fieldIndex, sqInt anOop); +#if LRPCheck +extern usqInt longRunningPrimitiveStopUsecsAddress(void); +#endif /* LRPCheck */ +extern sqInt lookupMNUreceiver(sqInt selector, sqInt rcvr); +extern sqInt lookupOrdinaryreceiver(sqInt selector, sqInt rcvr); +static NoDbgRegParms StackPage * makeBaseFrameFor(sqInt aContext); +static NeverInline void mapStackPages(void); +extern void markActiveMethodsAndReferents(void); +static NoDbgRegParms void markAndTraceStackPage(StackPage *thePage); +static NoDbgRegParms void markCogMethodsAndReferentsOnPage(StackPage *thePage); +static NoDbgRegParms usqInt marryFrameSP(char *theFP, char *theSP); +extern sqInt maxLookupNoMNUErrorCode(void); +extern usqInt methodCacheAddress(void); +extern int methodHasCogMethod(sqInt aMethodOop); +static NoDbgRegParms sqInt methodHasMachineCodePrimitiveMethod(sqInt methodObj); +static NoDbgRegParms NeverInline sqInt methodHasPrimitiveInPrimTracePlugin(sqInt aMethodObj); +static NoDbgRegParms sqInt methodHasTooManyLiterals(sqInt methodObj); +#if VMInvestigations +extern int methodHeaderHasPrimitive(sqInt methodHeader); +#endif /* VMInvestigations */ +extern int methodNeedsLargeContext(sqInt methodObj); +extern sqInt methodShouldBeCogged(sqInt aMethodObj); +static NoDbgRegParms int methodWithHeaderShouldBeCogged(sqInt methodHeader); +static NoDbgRegParms CogBlockMethod * mframeCogMethod(char *theFP); +extern CogMethod * mframeHomeMethodExport(void); +static NoDbgRegParms CogMethod * mframeHomeMethod(char *theFP); +static NoDbgRegParms int mframeIsBlockActivation(char *theFP); +static NoDbgRegParms sqInt mframeReceiver(char *theFP); +static NeverInline sqInt minimumUnusedHeadroom(void); +static NoDbgRegParms sqInt mnuMethodOrNilFor(sqInt rcvr); +static NoDbgRegParms char * moveFramesInthroughtoPage(StackPage *oldPage, char *theFP, StackPage *newPage); +static NoDbgRegParms sqInt mustMapMachineCodePCcontext(sqInt theIP, sqInt aOnceMarriedContext); +extern usqInt newMethodAddress(void); +extern usqInt nextProfileTickAddress(void); +extern sqInt noAssertHeaderOf(sqInt methodPointer); +extern sqInt positive32BitIntegerFor(unsigned int integerValue); +extern unsigned int positive32BitValueOf(sqInt oop); +extern sqInt positive64BitIntegerFor(usqLong integerValue); +extern usqLong positive64BitValueOf(sqInt oop); +static NoDbgRegParms void postGCAction(sqInt gcModeArg); +static NoDbgRegParms void preGCAction(sqInt gcModeArg); +extern usqInt primFailCodeAddress(void); +static NoDbgRegParms void primitiveBecomeReturn(sqInt ec); +static void primitiveExecuteMethod(void); +static void primitiveExecuteMethodArgsArray(void); +extern sqInt primitivePropertyFlagsprimIndex(sqInt methodObj, sqInt primIndex); +extern void * primTraceLogAddress(void); +extern usqInt primTraceLogIndexAddress(void); +extern void printCogMethod(CogMethod *cogMethod); +static NoDbgRegParms void printFrameFlagsForFP(char *theFP); +static NoDbgRegParms void printFrameMethodFor(char *theFP); +static NoDbgRegParms void printFrameThingatextra(char *name, char *address, sqInt extraValue); +extern sqInt printFrameWithSP(char *theFP, char *theSP); +extern void printMethodCacheFor(sqInt thing); +extern sqInt quickPrimitiveConstantFor(sqInt aQuickPrimitiveIndex); +extern sqInt (*quickPrimitiveGeneratorFor(sqInt aQuickPrimitiveIndex))(void); +extern sqInt quickPrimitiveInstVarIndexFor(sqInt primIndex); +extern sqInt rawHeaderOf(sqInt methodPointer); +extern void rawHeaderOfput(sqInt methodOop, sqInt cogMethodOrMethodHeader); +extern size_t readImageFromFileHeapSizeStartingAt(sqImageFile f, usqInt desiredHeapSize, squeakFileOffsetType imageOffset); +extern sqInt recordFastCCallPrimTraceForMethod(sqInt aMethodObj); +extern sqInt recordPrimTraceForMethod(sqInt aMethodObj); +static NeverInline void reloadPrimitiveCalloutPointer(void); +extern void reportMinimumUnusedHeadroom(void); +extern void reportMinimumUnusedHeadroomOn(FILE *aStdioStream); +static NoDbgRegParms sqInt returnToExecutivepostContextSwitch(sqInt inInterpreter, sqInt switchedContext); +static NoDbgRegParms sqInt returntoExecutive(sqInt returnValue, sqInt inInterpreter); +extern sqInt sendInvokeCallbackContext(VMCallbackContext *vmCallbackContext); +static NoDbgRegParms sqInt shortPrintFrame(char *theFP); +extern sqInt signed32BitIntegerFor(sqInt integerValue); +extern int signed32BitValueOf(sqInt oop); +extern sqInt signed64BitIntegerFor(sqLong integerValue); +extern sqLong signed64BitValueOf(sqInt oop); +extern sqInt specialSelectorNumArgs(sqInt index); +extern usqInt stackLimitAddress(void); +static sqInt stackLimitOffset(void); +static sqInt stackPageHeadroom(void); +extern usqInt stackPointerAddress(void); +extern usqInt startPCOfMethodHeader(sqInt aCompiledMethodHeader); +extern sqInt startPCOrNilOfLiteralin(sqInt lit, sqInt aMethodObj); +static NoDbgRegParms void tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFromtoreturnValue(sqInt contextToReturnFrom, sqInt contextToReturnTo, sqInt returnValue); +static NoDbgRegParms sqInt temporaryin(sqInt offset, char *theFP); +static NoDbgRegParms sqInt temporaryinput(sqInt offset, char *theFP, sqInt valueOop); +static NoDbgRegParms sqInt transferTofrom(sqInt newProcOrNil, sqInt sourceCode); +extern void updateStackZoneReferencesToCompiledCodePreCompaction(void); +static NoDbgRegParms void updateStateOfSpouseContextForFrameWithSP(char *theFP, char *theSP); +extern sqInt validInstructionPointerinMethodframePointer(usqInt instrPointer, usqInt aMethod, char *fp); +static NoDbgRegParms sqInt validStackPageBaseFrame(StackPage *aPage); +static NoDbgRegParms sqInt voidVMStateForSnapshotFlushingExternalPrimitivesIf(sqInt flushExtPrims); +extern char * whereIs(sqInt anOop); +static NoDbgRegParms NeverInline void widowOrForceToBytecodePC(sqInt ctxt); +static NoDbgRegParms int frameIsMarked(sqInt theFPInt); +#if VMInvestigations +EXPORT(int) primitiveAllMethodsCompiledToMachineCode(void); +#endif /* VMInvestigations */ +#if VMInvestigations +EXPORT(void) primitiveBenchmarkFollowForwardersInStackZone(void); +#endif /* VMInvestigations */ +#if VMInvestigations +EXPORT(void) primitiveBenchmarkFollowForwardersOfReceiverAndTemporariesInStackZone(void); +#endif /* VMInvestigations */ +#if VMInvestigations +EXPORT(sqInt) primitiveBenchmarkJITReceiver(void); +#endif /* VMInvestigations */ +#if VMInvestigations +EXPORT(void) primitiveBenchmarkScavenge(void); +#endif /* VMInvestigations */ +static void primitiveClosureCopyWithCopiedValues(void); +#if VMInvestigations +EXPORT(sqInt) primitiveCogitMethod(void); +#endif /* VMInvestigations */ +#if VMInvestigations +EXPORT(sqInt) primitiveCogStaticSymbols(void); +#endif /* VMInvestigations */ +static void primitiveCollectCogCodeConstituents(void); +#if VMInvestigations +EXPORT(void) primitiveContextXray(void); +#endif /* VMInvestigations */ +static void primitiveEnterCriticalSection(void); +static void primitiveExitCriticalSection(void); +EXPORT(void) primitiveExternalCallNoOp(void); +EXPORT(void) primitiveFastCNoOp(void); +EXPORT(void) primitiveFastCNoOpAlignedForFloats(void); +static void primitiveFlushCacheByMethod(void); +static void primitiveFlushCacheBySelector(void); +extern usqInt primitiveFunctionPointerAddress(void); +#if LRPCheck +EXPORT(sqInt) primitiveLongRunningPrimitiveSemaphore(void); +#endif /* LRPCheck */ +#if VMInvestigations +EXPORT(sqInt) primitiveMethodIsCogged(void); +#endif /* VMInvestigations */ +EXPORT(sqInt) primitiveMethodPCData(void); +static void primitiveMethodXray(void); +EXPORT(void) primitiveMinimumUnusedHeadroom(void); +static void primitiveObjectAt(void); +static void primitiveObjectAtPut(void); +EXPORT(sqInt) primitiveProfileSemaphore(void); +static void primitiveResume(void); +static void primitiveSetPriority(void); +static void primitiveSignal(void); +static void primitiveSnapshot(void); +static void primitiveSnapshotEmbedded(void); +static void primitiveSuspend(void); +static void primitiveSuspendBackingUpV1(void); +EXPORT(void) primitiveSuspendBackingUpV2(void); +static void primitiveTerminateTo(void); +static void primitiveUnloadModule(void); +static void primitiveVoidVMState(void); +static void primitiveVoidVMStateForMethod(void); +static void primitiveWait(void); +static void primitiveYield(void); +static void unmarkAllFrames(void); +static NoDbgRegParms void initializeStacknumSlotspageSize(char *theStackPages, sqInt stackSlots, sqInt slotsPerPage); +static NoDbgRegParms sqInt pageIndexFor(void *pointer); +static NoDbgRegParms char * whereIsMaybeStackThing(sqInt anOop); +extern char * cStringOrNullFor(sqInt oop); +extern sqInt failed(void); +extern sqInt identityHashOf(sqInt anOop); +static NoDbgRegParms sqInt isInstanceOfClassArray(sqInt oop); +static NoDbgRegParms sqInt isNegativeIntegerValueOf(sqInt oop); +extern sqInt isPositiveMachineIntegerObject(sqInt oop); +static NoDbgRegParms sqInt magnitude64BitIntegerForneg(usqLong magnitude, sqInt isNegative); +static NoDbgRegParms usqLong magnitude64BitValueOf(sqInt oop); +static NoDbgRegParms usqIntptr_t positiveMachineIntegerValueOfObj(sqInt oop); +extern usqIntptr_t positiveMachineIntegerValueOf(sqInt oop); +static void primitiveAdd(void); +EXPORT(void) primitiveAddLargeIntegers(void); +static void primitiveAdoptInstance(void); +EXPORT(void) primitiveAllInstances(void); +EXPORT(void) primitiveAllObjects(void); +static void primitiveArctan(void); +static void primitiveArrayBecome(void); +static void primitiveArrayBecomeOneWay(void); +static void primitiveArrayBecomeOneWayCopyHashArg(void); +static void primitiveArrayBecomeOneWayNoCopyHash(void); +static void primitiveAsCharacter(void); +static void primitiveAsFloat(void); +static void primitiveAt(void); +static void primitiveAtPut(void); +static void primitiveBeCursor(void); +static void primitiveBeDisplay(void); +static void primitiveBeep(void); +static void primitiveBehaviorHash(void); +static void primitiveBitAnd(void); +EXPORT(void) primitiveBitAndLargeIntegers(void); +static void primitiveBitOr(void); +EXPORT(void) primitiveBitOrLargeIntegers(void); +static void primitiveBitShift(void); +EXPORT(void) primitiveBitShiftLargeIntegers(void); +static void primitiveBitXor(void); +EXPORT(void) primitiveBitXorLargeIntegers(void); +EXPORT(sqInt) primitiveBytecodeSetsAvailable(void); +static void primitiveBytesLeft(void); +static void primitiveCalloutToFFI(void); +static void primitiveChangeClass(void); +static void primitiveClass(void); +static void primitiveClearVMProfile(void); +static void primitiveClipboardText(void); +EXPORT(sqInt) primitiveClockLogAddresses(void); +static void primitiveClosureValue(void); +extern void primitiveClosureValueNoContextSwitch(void); +static void primitiveClosureValueWithArgs(void); +static void primitiveCoarseLocalMicrosecondClock(void); +static void primitiveCoarseUTCMicrosecondClock(void); +EXPORT(sqInt) primitiveCompareBytes(void); +EXPORT(void) primitiveCompareWith(void); +static void primitiveConstantFill(void); +static void primitiveControlVMProfiling(void); +static void primitiveCopyObject(void); +static void primitiveCosine(void); +EXPORT(sqInt) primitiveCrashVM(void); +EXPORT(sqInt) primitiveDisablePowerManager(void); +static void primitiveDiv(void); +static void primitiveDivide(void); +EXPORT(void) primitiveDivideLargeIntegers(void); +EXPORT(void) primitiveDivLargeIntegers(void); +static void primitiveEqual(void); +EXPORT(void) primitiveEqualLargeIntegers(void); +static void primitiveExitToDebugger(void); +static void primitiveExp(void); +static void primitiveExponent(void); +extern sqInt primitiveFail(void); +extern sqInt primitiveFailForOSError(sqLong osErrorCode); +extern sqInt primitiveFailFor(sqInt reasonCode); +extern sqInt primitiveFailForwithSecondary(sqInt reasonCode, sqLong extraErrorCode); +extern sqInt primitiveFailureCode(void); +static void primitiveFetchNextMourner(void); +static void primitiveFloatAdd(void); +static void primitiveFloatArrayAt(void); +static void primitiveFloatArrayAtPut(void); +static void primitiveFloatAt(void); +static void primitiveFloatAtPut(void); +static void primitiveFloatDivide(void); +static void primitiveFloatEqual(void); +static void primitiveFloatGreaterOrEqual(void); +static void primitiveFloatGreaterThan(void); +static void primitiveFloatLessOrEqual(void); +static void primitiveFloatLessThan(void); +static void primitiveFloatMultiply(void); +static void primitiveFloatNotEqual(void); +static void primitiveFloatSubtract(void); +static void primitiveFlushCache(void); +static void primitiveFlushExternalPrimitives(void); +static void primitiveForceDisplayUpdate(void); +static void primitiveFormPrint(void); +static void primitiveFractionalPart(void); +static void primitiveFullClosureValue(void); +extern void primitiveFullClosureValueNoContextSwitch(void); +static void primitiveFullClosureValueWithArgs(void); +static void primitiveGetAttribute(void); +EXPORT(sqInt) primitiveGetenv(void); +#if IMMUTABILITY +static void primitiveGetImmutability(void); +#else +# define primitiveGetImmutability (void (*)(void))0 +#endif /* IMMUTABILITY */ +EXPORT(sqInt) primitiveGetLogDirectory(void); +static void primitiveGetNextEvent(void); +static void primitiveGreaterOrEqual(void); +EXPORT(void) primitiveGreaterOrEqualLargeIntegers(void); +static void primitiveGreaterThan(void); +EXPORT(void) primitiveGreaterThanLargeIntegers(void); +static void primitiveGrowMemoryByAtLeast(void); +static void primitiveHashMultiply(void); +EXPORT(sqInt) primitiveHeartbeatFrequency(void); +static void primitiveHighBit(void); +EXPORT(sqInt) primitiveHighResClock(void); +static void primitiveIdentical(void); +static void primitiveIdentityHash(void); +EXPORT(sqInt) primitiveImageFormatVersion(void); +static void primitiveImageName(void); +static void primitiveImmediateAsInteger(void); +static void primitiveInputSemaphore(void); +static void primitiveInputWord(void); +static void primitiveIntegerAt(void); +static void primitiveIntegerAtPut(void); +EXPORT(sqInt) primitiveInterruptChecksPerMSec(void); +static void primitiveInterruptSemaphore(void); +EXPORT(sqInt) primitiveIsBigEnder(void); +static void primitiveIsPinned(void); +static void primitiveLessOrEqual(void); +EXPORT(void) primitiveLessOrEqualLargeIntegers(void); +static void primitiveLessThan(void); +EXPORT(void) primitiveLessThanLargeIntegers(void); +static void primitiveListBuiltinModule(void); +static void primitiveListExternalModule(void); +static void primitiveLoadImageSegment(void); +static void primitiveLocalMicrosecondClock(void); +static void primitiveLogN(void); +static void primitiveLowSpaceSemaphore(void); +static void primitiveMakePoint(void); +static void primitiveMaxIdentityHash(void); +static void primitiveMillisecondClock(void); +EXPORT(sqInt) primitiveMillisecondClockMask(void); +static void primitiveMod(void); +EXPORT(void) primitiveModLargeIntegers(void); +EXPORT(sqInt) primitiveMultipleBytecodeSetsActive(void); +static void primitiveMultiply(void); +EXPORT(void) primitiveMultiplyLargeIntegers(void); +static void primitiveNew(void); +static void primitiveNewMethod(void); +static void primitiveNewWithArg(void); +static void primitiveNextInstance(void); +static void primitiveNextObject(void); +static void primitiveNoop(void); +static void primitiveNotEqual(void); +EXPORT(void) primitiveNotEqualLargeIntegers(void); +static void primitiveNotIdentical(void); +static void primitiveObjectsAccessibleFromRoots(void); +EXPORT(sqInt) primitivePathToUsing(void); +static void primitivePerformInSuperclass(void); +static void primitivePerformWithArgs(void); +static void primitivePinnedNew(void); +static void primitivePinnedNewWithArg(void); +EXPORT(sqInt) primitiveProfilePrimitive(void); +EXPORT(sqInt) primitiveProfileSample(void); +EXPORT(sqInt) primitiveProfileStart(void); +static void primitiveQuit(void); +static void primitiveQuo(void); +EXPORT(void) primitiveQuoLargeIntegers(void); +static void primitiveRelinquishProcessor(void); +EXPORT(void) primitiveRemLargeIntegers(void); +static void primitiveScanCharacters(void); +EXPORT(sqInt) primitiveScreenDepth(void); +EXPORT(sqInt) primitiveScreenScaleFactor(void); +static void primitiveScreenSize(void); +static void primitiveSecondsClock(void); +static void primitiveSetDisplayMode(void); +static void primitiveSetFullScreen(void); +#if IMMUTABILITY +static void primitiveSetImmutability(void); +#else +# define primitiveSetImmutability (void (*)(void))0 +#endif /* IMMUTABILITY */ +static void primitiveSetInterruptKey(void); +EXPORT(sqInt) primitiveSetLogDirectory(void); +static void primitiveSetOrHasIdentityHash(void); +static void primitiveShortAt(void); +static void primitiveShortAtPut(void); +static void primitiveShowDisplayRect(void); +static void primitiveSignalAtBytesLeft(void); +static void primitiveSine(void); +static void primitiveSize(void); +static void primitiveSizeInBytes(void); +static void primitiveSizeInBytesOfInstance(void); +static void primitiveSmallFloatAdd(void); +static void primitiveSmallFloatArctan(void); +static void primitiveSmallFloatCosine(void); +static void primitiveSmallFloatDivide(void); +static void primitiveSmallFloatEqual(void); +static void primitiveSmallFloatExp(void); +static void primitiveSmallFloatExponent(void); +static void primitiveSmallFloatFractionalPart(void); +static void primitiveSmallFloatGreaterOrEqual(void); +static void primitiveSmallFloatGreaterThan(void); +static void primitiveSmallFloatLessOrEqual(void); +static void primitiveSmallFloatLessThan(void); +static void primitiveSmallFloatLogN(void); +static void primitiveSmallFloatMultiply(void); +static void primitiveSmallFloatNotEqual(void); +static void primitiveSmallFloatSine(void); +static void primitiveSmallFloatSquareRoot(void); +static void primitiveSmallFloatSubtract(void); +static void primitiveSmallFloatTimesTwoPower(void); +static void primitiveSmallFloatTruncated(void); +static void primitiveSomeInstance(void); +static void primitiveSomeObject(void); +static void primitiveSpecialObjectsOop(void); +static void primitiveSquareRoot(void); +static void primitiveStoreImageSegment(void); +static void primitiveStringAt(void); +static void primitiveStringAtPut(void); +static void primitiveStringReplace(void); +static void primitiveSubtract(void); +EXPORT(void) primitiveSubtractLargeIntegers(void); +static void primitiveTestAndSetOwnershipOfCriticalSection(void); +static void primitiveTestDisplayDepth(void); +#if TestingPrimitives +EXPORT(sqInt) primitiveTestShortenIndexableSize(void); +#endif /* TestingPrimitives */ +static void primitiveTimesTwoPower(void); +static void primitiveTruncated(void); +static void primitiveUninitializedNewWithArg(void); +static void primitiveUninitializedPinnedNewWithArg(void); +static void primitiveUpdateTimezone(void); +static void primitiveUTCMicrosecondClock(void); +static void primitiveUtcAndTimezoneOffset(void); +EXPORT(sqInt) primitiveUtcWithOffset(void); +static void primitiveVMPath(void); +static void primitiveVMProfileSamplesInto(void); +extern int signalNoResume(sqInt aSemaphore); +static NoDbgRegParms sqIntptr_t signedMachineIntegerValueOfObj(sqInt oop); +extern sqIntptr_t signedMachineIntegerValueOf(sqInt oop); +extern usqInt sizeOfAlienData(sqInt oop); +extern void * startOfAlienData(sqInt oop); +extern sqInt success(sqInt successBoolean); +extern void ceCollectArgumentsTypeForFramefulActivation(void); +extern void ceCollectArgumentsTypeForFramlessSpilledActivationOf(CogMethod *cogMethod); +extern void ceCollectArgumentsTypeForFramlessUnspilledActivationWithArgargof(sqInt arg0, sqInt arg1, CogMethod *cogMethod); +extern void ceCollectArgumentsTypeForFramlessUnspilledActivationWithArgof(sqInt arg, CogMethod *cogMethod); +extern void ceCollectInstVarTypeForatof(sqInt rcvr, sqInt instVarIndex, sqInt assignedObject); +extern void ceCollectRemoteTempVarTypeAtof(sqInt tempVarIndex, sqInt assignedObject); +extern void ceCollectReturnTypeForof(CogMethod *cogMethod, sqInt returnObject); +extern void ceCollectTempVarTypeAtof(sqInt tempVarIndex, sqInt assignedObject); +extern void ceCollectTypesForPrimitiveSlotAtputof(sqInt slotIndex, sqInt assignedObject, sqInt rcvr); +static void primitiveSlotAtPut(void); +static NoDbgRegParms sqInt becomeEffectFlagsFor(sqInt objOop); +extern void ceScheduleScavenge(void); +extern sqInt classSmallFloat(void); +static void clearLeakMapAndMapAccessibleObjects(void); +extern void ensureNoForwardedLiteralsIn(sqInt aMethodObj); +static NoDbgRegParms sqInt followForwardedObjectFieldstoDepth(sqInt objOop, sqInt depth); +extern usqInt freeStartAddress(void); +extern usqInt getScavengeThreshold(void); +static NoDbgRegParms sqLong headerWhileForwardingOf(sqInt aCompiledMethodObjOop); +extern sqInt isBytes(sqInt oop); +extern sqInt isForwardedClassIndex(sqInt maybeClassIndex); +extern int isImmediateClass(sqInt classObj); +extern sqInt isReallyYoungObject(sqInt objOop); +extern sqInt methodHeaderOf(sqInt methodObj); +extern usqInt needGCFlagAddress(void); +static NoDbgRegParms void printCantBeObjecton(sqInt oop, FILE *aStream); +extern usqInt specialObjectsOopAddress(void); +extern sqInt withoutForwardingOnandwithsendToCogit(sqInt obj1, sqInt obj2, sqInt aBool, sqInt (*selector)(sqInt,sqInt,sqInt)); +static NoDbgRegParms usqInt addressAfter(sqInt objOop); +static NoDbgRegParms sqInt allocateSlotsForPinningInOldSpacebytesformatclassIndex(sqInt numSlots, usqInt totalBytes, sqInt formatField, sqInt classIndex); +extern sqInt byteSwapped(sqInt w); +static NoDbgRegParms usqInt bytesInBody(sqInt objOop); +static int defaultEdenBytes(void); +extern sqInt fetchClassTagOf(sqInt oop); +extern sqInt floatObjectOf(double aFloat); +static NoDbgRegParms sqLong floatValueBitsOf(sqInt floatOop); +extern double floatValueOf(sqInt oop); +static NoDbgRegParms void hackSlimBridgeToat(sqInt objOop, sqInt startAddress); +extern int headerIndicatesAlternateBytecodeSet(sqInt methodHeader); +static NoDbgRegParms sqInt initFreeChunkWithBytesat(usqLong numBytes, sqInt address); +static NoDbgRegParms void initSegmentBridgeWithBytesat(usqLong numBytes, sqInt address); +extern sqInt instantiateClassindexableSize(sqInt classObj, usqInt nElements); +extern sqInt integerObjectOf(sqInt value); +extern sqInt integerValueOf(sqInt oop); +static NoDbgRegParms int isFloatInstance(sqInt oop); +static NoDbgRegParms int isImmediateFloat(sqInt oop); +extern int isIntegerObject(sqInt oop); +extern int isIntegerValue(sqInt intValue); +static NoDbgRegParms int isLilliputianSize(sqInt chunkBytes); +static NoDbgRegParms int isSmallFloatValueBits(usqLong rawFloatBits); +static NoDbgRegParms int isSmallFloatValue(double aFloat); +static NoDbgRegParms int isWordsNonImm(sqInt objOop); +static NoDbgRegParms sqInt lengthOfformat(sqInt objOop, sqInt fmt); +static NoDbgRegParms double loadFloatOrIntFrom(sqInt floatOrIntOop); +static sqInt maxSlotsForAlloc(void); +extern double noFailFloatValueOf(sqInt aFloatOop); +static sqInt numFreeLists(void); +static NoDbgRegParms sqInt objectAfterMaybeSlimBridgelimit(sqInt objOop, sqInt limit); +static NoDbgRegParms double smallFloatValueOf(sqInt oop); +extern usqInt smallObjectBytesForSlots(sqInt numSlots); +static sqInt wordIndexableFormat(void); +static sqInt wordSizeClassIndexPun(void); +static NoDbgRegParms NeverInline void addToEphemeronList(sqInt ephemeronCorpse); +static NoDbgRegParms NeverInline void addToWeakList(sqInt weakCorpse); +static sqInt allNewSpaceObjectsHaveZeroRTRefCount(void); +static sqInt allWeakSurvivorsOnWeakList(void); +static NeverInline void computeRefCountToShrinkRT(void); +static NoDbgRegParms sqInt copyAndForwardMourner(sqInt mourner); +static NoDbgRegParms sqInt copyAndForward(sqInt survivor); +static NoDbgRegParms NeverInline sqInt copyToOldSpacebytesformat(sqInt survivor, sqInt bytesInObject, sqInt formatOfSurvivor); +extern usqInt corpseForCorpseOffset(sqInt corpseOffset); +static NoDbgRegParms usqInt firstCorpse(sqInt headOfCorpseList); +static void growRememberedSet(void); +static NoDbgRegParms sqInt isInRememberedSet(sqInt objOop); +static NoDbgRegParms sqInt isMaybeOldScavengeSurvivor(sqInt oop); +static NoDbgRegParms sqInt isScavengeSurvivor(sqInt oop); +static NoDbgRegParms sqInt isYetToBeScavenged(sqInt oop); +static usqInt newSpaceCapacity(void); +static NoDbgRegParms sqInt nextCorpseOrNil(sqInt corpse); +extern void openScavengeLog(void); +extern void printEphemeronList(void); +extern void printRememberedSet(void); +static sqInt processEphemerons(void); +static void processWeaklings(void); +static NoDbgRegParms sqInt processWeakSurvivor(sqInt weakObj); +extern sqInt remember(sqInt objOop); +static NoDbgRegParms void scavengeFutureSurvivorSpaceStartingAt(sqInt initialAddress); +static sqInt scavengeInactiveEphemeronsInRememberedSet(void); +static sqInt scavengeInactiveEphemeronsOnEphemeronList(void); +static sqInt scavengeLoop(void); +static NoDbgRegParms sqInt scavengeReferentsOfPointers(sqInt referrer); +static NoDbgRegParms sqInt scavengeReferentsOf(sqInt referrer); +static NoDbgRegParms void scavengeRememberedSetStartingAt(sqInt n); +static float scavengerTenuringThreshold(void); +static sqInt unfiredEphemeronsAtEndOfRememberedSet(void); +static sqInt validRememberedSetPartitionPostSIEIRS(void); +static sqInt wholeEphemeronListIsFirable(void); +static NeverInline void writeScavengeLog(void); +static NoDbgRegParms sqInt accessibleObjectAfter(sqInt objOop); +static NoDbgRegParms NeverInline sqInt activeAndDeferredScan(sqInt anEphemeron); +static NoDbgRegParms void addFreeSubTree(sqInt freeTree); +extern sqInt addGCRoot(sqInt *varLoc); +static NoDbgRegParms sqInt addressCouldBeClassObj(sqInt maybeClassObj); +extern sqInt addressCouldBeObj(sqInt address); +extern sqInt addressCouldBeOop(sqInt address); +static NoDbgRegParms sqInt addToFreeListbytes(sqInt freeChunk, sqInt chunkBytes); +static NoDbgRegParms sqInt allInstancesOf(sqInt aClass); +static sqInt allObjects(void); +static sqInt allObjectsUnmarked(void); +static sqInt allOldMarkedWeakObjectsOnWeaklingStack(void); +static sqInt allocateLargestFreeChunk(void); +static NoDbgRegParms sqInt allocateOldSpaceChunkOfBytes(usqInt chunkBytes); +extern sqInt allocatePinnedSlots(sqInt nSlots); +static NoDbgRegParms sqInt allocateSlotsInOldSpacebytesformatclassIndex(sqInt numSlots, usqInt totalBytes, sqInt formatField, sqInt classIndex); +static sqInt allocationUnit(void); +static NoDbgRegParms sqInt allStrongSlotsOfWeaklingAreMarked(sqInt aWeakling); +static sqInt allUnscannedEphemeronsAreActive(void); +static NoDbgRegParms NeverInline sqInt assertInnerValidFreeObject(sqInt objOop); +extern sqInt becomewith(sqInt array1, sqInt array2); +static NoDbgRegParms sqInt becomewithtwoWaycopyHash(sqInt array1, sqInt array2, sqInt twoWayFlag, sqInt copyHashFlag); +extern void beRootIfOld(sqInt oop); +static sqInt bitsSetInFreeSpaceMaskForAllFreeLists(void); +static sqInt bridgeSize(void); +static NoDbgRegParms sqInt byteFormatForNumBytes(sqInt numBytes); +static sqInt byteFormatMask(void); +extern sqInt byteSizeOf(sqInt oop); +extern unsigned char bytesPerElement(sqInt oop); +static NoDbgRegParms sqInt changeClassOfto(sqInt rcvr, sqInt argClass); +extern sqInt characterObjectOf(sqInt characterCode); +extern usqInt characterValueOf(sqInt oop); +static NoDbgRegParms sqInt cheapAddressCouldBeInHeap(sqInt address); +extern sqInt checkedLongAt(sqInt byteAddress); +static sqInt checkHeapFreeSpaceIntegrity(void); +static NoDbgRegParms sqInt checkHeapIntegrityclassIndicesShouldBeValid(sqInt excludeUnmarkedObjs, sqInt classIndicesShouldBeValid); +extern sqInt checkOkayOop(usqInt oop); +extern sqInt checkOopHasOkayClass(usqInt obj); +static NoDbgRegParms sqInt checkOopIntegritynamed(sqInt obj, char *name); +static NoDbgRegParms sqInt checkOopIntegritynamedindex(sqInt obj, char *name, sqInt i); +extern sqInt classAlien(void); +extern sqInt classArray(void); +extern sqInt classAtIndex(sqInt classIndex); +static NoDbgRegParms sqInt classAtIndexput(sqInt classIndex, sqInt objOop); +extern sqInt classBitmap(void); +extern sqInt classByteArray(void); +extern sqInt classCharacter(void); +extern sqInt classDoubleByteArray(void); +extern sqInt classDoubleWordArray(void); +extern sqInt classExternalAddress(void); +extern sqInt classExternalData(void); +extern sqInt classExternalFunction(void); +extern sqInt classExternalLibrary(void); +extern sqInt classExternalStructure(void); +extern sqInt classFloat(void); +extern sqInt classFloat32Array(void); +extern sqInt classFloat64Array(void); +static NoDbgRegParms sqInt classForClassTag(sqInt classIndex); +static NoDbgRegParms sqInt classFormatFromInstFormat(sqInt instFormat); +static sqInt classIndexFieldWidth(void); +extern sqInt classIndexOf(sqInt objOop); +extern sqInt classLargeNegativeInteger(void); +extern sqInt classLargePositiveInteger(void); +extern sqInt classOrNilAtIndex(sqInt classIndex); +extern sqInt classPoint(void); +extern sqInt classSemaphore(void); +extern sqInt classSmallInteger(void); +extern sqInt classString(void); +extern sqInt classTableMinorIndexMask(void); +extern sqInt classTablePageSize(void); +extern sqInt classTableRootObj(void); +static sqInt classTableRootSlots(void); +extern sqInt classTagForClass(sqInt classObj); +extern sqInt classUnsafeAlien(void); +extern sqInt classWordArray(void); +static void clearLeakMapAndMapAccessibleFreeSpace(void); +static NoDbgRegParms sqInt cloneInOldSpaceforPinning(sqInt objOop, sqInt forPinning); +extern sqInt cloneObject(sqInt objOop); +static NoDbgRegParms sqInt copyObjtoAddrstopAtsavedFirstFieldsindex(sqInt objOop, sqInt segAddr, sqInt endSeg, sqInt savedFirstFields, sqInt i); +extern void countMarkedAndUnmarkdObjects(sqInt printFlags); +static usqLong currentAllocatedBytes(void); +static NoDbgRegParms void doScavenge(sqInt tenuringCriterion); +static NoDbgRegParms sqInt doShortentoIndexableSize(sqInt objOop, sqInt indexableSize); +extern usqInt eeInstantiateClassIndexformatnumSlots(sqInt knownClassIndex, sqInt objFormat, sqInt numSlots); +extern NeverInline void eek(void); +static NoDbgRegParms void enableObjectEnumerationFrom(sqInt initialObject); +static NoDbgRegParms sqInt ensureBehaviorHash(sqInt aBehavior); +static NoDbgRegParms sqInt ensureRoomOnObjStackAt(sqInt objStackRootIndex); +static NoDbgRegParms sqInt enterIntoClassTable(sqInt aBehavior); +static NoDbgRegParms sqInt existInstancesInNewSpaceOf(sqInt classObj); +static NoDbgRegParms void expungeFromClassTable(sqInt aBehavior); +extern sqInt falseObject(void); +extern sqInt fetchByteofObject(sqInt byteIndex, sqInt objOop); +static NoDbgRegParms NeverInline sqInt fetchClassOfNonImm(sqInt objOop); +extern sqInt fetchClassOf(sqInt oop); +static NoDbgRegParms sqInt fetchClassTagOfNonImm(sqInt obj); +extern sqLong fetchIntegerofObject(sqInt fieldIndex, sqInt objOop); +extern int fetchLong32ofObject(sqInt fieldIndex, sqInt oop); +static NoDbgRegParms sqInt fetchPointerofFreeChunk(sqInt fieldIndex, sqInt objOop); +extern sqInt fetchPointerofObject(sqInt fieldIndex, sqInt objOop); +extern sqInt findClassNamed(char *aString); +static sqInt findLargestFreeChunk(void); +extern void findStringBeginningWith(char *aCString); +extern void findString(char *aCString); +static NoDbgRegParms void fireEphemeron(sqInt ephemeron); +static sqInt firstAccessibleObject(void); +static NoDbgRegParms void * firstFixedFieldOfMaybeImmediate(sqInt oop); +extern void * firstFixedField(sqInt objOop); +extern void * firstIndexableField(sqInt objOop); +extern sqInt fixedFieldsOfClassFormatMask(void); +extern sqInt fixedFieldsOfClassFormat(sqInt classFormat); +static NoDbgRegParms sqInt fixedFieldsOfformatlength(sqInt objOop, sqInt fmt, sqInt wordLength); +static NoDbgRegParms NeverInline sqInt fixFollowedFieldofObjectwithInitialValue(sqInt fieldIndex, sqInt anObject, sqInt initialValue); +static NoDbgRegParms sqInt followedKeyOfEphemeron(sqInt objOop); +static NoDbgRegParms sqInt followFieldofObject(sqInt fieldIndex, sqInt anObject); +extern sqInt followForwarded(sqInt objOop); +static NoDbgRegParms sqInt followMaybeForwarded(sqInt objOop); +static NoDbgRegParms sqInt followObjFieldofObject(sqInt fieldIndex, sqInt anObject); +extern sqInt formatOfClass(sqInt classPointer); +static NoDbgRegParms sqInt formatOf(sqInt objOop); +static NoDbgRegParms sqInt forwardersIn(sqInt anObject); +static sqInt freeChunkNextIndex(void); +static sqInt freeChunkParentIndex(void); +static sqInt freeChunkPrevIndex(void); +static NoDbgRegParms sqInt freeChunkWithBytesat(sqInt bytes, sqInt address); +extern sqInt freeObject(sqInt objOop); +extern NeverInline usqLong fullGC(void); +static float getHeapGrowthToSizeGCRatio(void); +static NoDbgRegParms sqInt goodContextSize(sqInt oop); +static NoDbgRegParms usqInt growOldSpaceByAtLeast(sqInt minAmmount); +static NoDbgRegParms sqInt hasPointerFieldsNonImm(sqInt oop); +extern usqLong headerForSlotsformatclassIndex(sqInt numSlots, sqInt formatField, sqInt classIndex); +static NoDbgRegParms usqLong headerForSlotshashformatclassIndex(sqInt numSlots, sqInt hash, sqInt formatField, sqInt classIndex); +static sqInt hiddenRootSlots(void); +static sqInt hiddenRootsObject(void); +static sqInt imageSegmentVersion(void); +#if IMMUTABILITY +extern sqInt immutableBitMask(void); +#endif /* IMMUTABILITY */ +extern sqInt indexOfin(sqInt anElement, sqInt anObject); +static NoDbgRegParms void inFreeTreeReplacewith(sqInt treeNode, sqInt newNode); +static NoDbgRegParms sqInt initialInstanceOf(sqInt classObj); +static NeverInline void initializeNewSpaceVariables(void); +static NoDbgRegParms void initializeObjectMemory(sqInt bytesToShift); +static void initializeUnscannedEphemerons(void); +extern void inOrderPrintFreeTreeprintList(sqInt freeChunk, sqInt printNextList); +static NoDbgRegParms NeverInline void inPlaceBecomeandcopyHashFlag(sqInt obj1, sqInt obj2, sqInt copyHashFlag); +extern sqInt instanceSizeOf(sqInt classObj); +static NoDbgRegParms sqInt instantiateClass(sqInt classObj); +extern sqInt instSpecOfClassFormat(sqInt classFormat); +static NoDbgRegParms sqInt instSpecOfClass(sqInt classPointer); +static NoDbgRegParms void invalidCompactClassError(const char *className); +extern sqInt isArray(sqInt oop); +static NoDbgRegParms int isBridgeOrEnumerableObjectNoAssert(sqInt objOop); +extern int isCharacterObject(sqInt oop); +extern int isCharacterValue(sqInt anInteger); +static NoDbgRegParms int isCompiledMethodFormat(sqInt format); +static NoDbgRegParms int isCompiledMethodHeader(sqInt objHeader); +extern int isCompiledMethod(sqInt objOop); +static NoDbgRegParms int isContextHeader(sqInt aHeader); +static NoDbgRegParms int isContextNonImm(sqInt oop); +static NoDbgRegParms sqInt isContext(sqInt oop); +static NoDbgRegParms int isCopiedIntoSegment(sqInt anObjectInTheHeap); +static NoDbgRegParms sqInt isEmptyObjStack(sqInt objStack); +static NoDbgRegParms sqInt isEnumerableObjectNoAssert(sqInt objOop); +static NoDbgRegParms int isEphemeronFormat(sqInt format); +static NoDbgRegParms int isEphemeron(sqInt objOop); +extern sqInt isFixedSizePointerFormat(sqInt format); +static NoDbgRegParms int isForwardedClassTag(sqInt classIndex); +extern int isForwarded(sqInt objOop); +static NoDbgRegParms int isFreeObject(sqInt objOop); +static NoDbgRegParms sqInt isFreeOop(sqInt oop); +extern int isImmediate(sqInt oop); +static NoDbgRegParms sqInt isInClassTable(sqInt objOop); +extern sqInt isIndexable(sqInt objOop); +static NoDbgRegParms sqInt isInEden(sqInt objOop); +static NoDbgRegParms sqInt isInFutureSpace(sqInt address); +extern sqInt isInMemory(sqInt address); +static NoDbgRegParms sqInt isInNewSpace(sqInt objOop); +extern sqInt isInOldSpace(sqInt address); +static NoDbgRegParms sqInt isInPastSpace(sqInt address); +static NoDbgRegParms int isLargeFreeObject(sqInt objOop); +extern sqInt isLong64s(sqInt oop); +extern int isMarked(sqInt objOop); +static NoDbgRegParms sqInt isMaybeFiredEphemeron(sqInt objOop); +extern int isNonImmediate(sqInt oop); +static NoDbgRegParms int isObjEphemeron(sqInt objOop); +static NoDbgRegParms sqInt isObjImmutable(sqInt anOop); +static NoDbgRegParms int isOldObject(sqInt objOop); +extern sqInt isOopCompiledMethod(sqInt oop); +extern sqInt isOopForwarded(sqInt oop); +extern sqInt isOopImmutable(sqInt oop); +extern sqInt isOopMutable(sqInt oop); +extern int isPinned(sqInt objOop); +extern sqInt isPointers(sqInt oop); +static NoDbgRegParms sqInt isPureBitsFormat(sqInt format); +static NoDbgRegParms sqInt isPureBitsNonImm(sqInt objOop); +static NoDbgRegParms int isRemembered(sqInt objOop); +static NoDbgRegParms int isSegmentBridge(sqInt objOop); +static NoDbgRegParms sqInt isSemaphoreOop(sqInt anOop); +extern sqInt isShorts(sqInt oop); +static NoDbgRegParms int isUnambiguouslyForwarder(sqInt objOop); +extern int isUnmarked(sqInt objOop); +extern sqInt isValidClassTag(sqInt classIndex); +static NoDbgRegParms sqInt isValidObjStackAt(sqInt objStackRootIndex); +static NoDbgRegParms sqInt isValidObjStackPagemyIndex(sqInt objStackPage, sqInt myx); +static NoDbgRegParms sqInt isValidObjStackPagemyIndexfirstPage(sqInt objStackPage, sqInt myx, sqInt isFirstPage); +static NoDbgRegParms sqInt isValidObjStack(sqInt objStack); +static NoDbgRegParms int isWeakNonImm(sqInt objOop); +extern sqInt isWeak(sqInt oop); +static NoDbgRegParms sqInt isWordsOrBytesNonImm(sqInt objOop); +extern sqInt isWordsOrBytes(sqInt oop); +extern sqInt isWordsOrShorts(sqInt oop); +extern sqInt isWords(sqInt oop); +extern int isYoungObject(sqInt objOop); +extern sqInt isYoung(sqInt oop); +static NoDbgRegParms sqInt isinstanceOfcompactClassIndex(sqInt oop, sqInt classOop, sqInt compactClassIndex); +extern sqInt isonObjStack(sqInt oop, sqInt objStack); +static NoDbgRegParms sqInt keyOfEphemeron(sqInt objOop); +static NoDbgRegParms sqInt keyOfMaybeFiredEphemeron(sqInt objOop); +static NoDbgRegParms sqInt knownClassAtIndex(sqInt classIndex); +static sqInt lastPointerFormat(void); +static NoDbgRegParms sqInt lastPointerOfWhileSwizzling(sqInt objOop); +static NoDbgRegParms sqInt lastPointerOf(sqInt objOop); +extern int leakCheckFullGC(void); +extern int leakCheckNewSpaceGC(void); +static NoDbgRegParms sqInt lengthOfMaybeImmediate(sqInt oop); +static NoDbgRegParms sqInt lengthOf(sqInt objOop); +extern usqInt literalCountOfMethodHeader(sqInt header); +extern usqInt literalCountOf(sqInt methodPointer); +static NoDbgRegParms NeverInline sqInt loadImageSegmentFromoutPointers(sqInt segmentWordArray, sqInt outPointerArray); +extern void longPrintInstancesOf(sqInt aClassOop); +extern void longPrintInstancesWithClassIndex(sqInt classIndex); +extern void longPrintReferencesTo(sqInt anOop); +static void mapExtraRoots(void); +static NeverInline void mapMournQueue(void); +static NoDbgRegParms sqInt mapOopsFromtooutPointersoutHashes(sqInt segStart, sqInt segAddr, sqInt outPointerArray, sqInt savedOutHashes); +static NoDbgRegParms void markAndTraceClassOf(sqInt objOop); +static NoDbgRegParms void markAndTraceObjStackandContents(sqInt stackOrNil, sqInt markAndTraceContents); +extern NeverInline void markAndTrace(sqInt objOop); +static sqInt markInactiveEphemerons(void); +static NoDbgRegParms NeverInline void markObjects(sqInt objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged); +static void markWeaklingsAndMarkAndFireEphemerons(void); +extern sqInt maxIdentityHash(void); +extern sqInt maxSlotsForNewSpaceAlloc(void); +extern sqInt maybeMethodClassOfseemsToBeInstantiating(sqInt methodObj, sqInt format); +extern sqInt minSlotsForShortening(void); +static NoDbgRegParms usqInt newHashBitsOf(sqInt objOop); +static sqInt newSpaceIsEmpty(void); +extern sqInt nilObject(void); +static NeverInline void nilUnmarkedWeaklingSlots(void); +static NoDbgRegParms sqInt noCheckClassAtIndex(sqInt classIndex); +static NoDbgRegParms sqInt noCheckPushonObjStack(sqInt objOop, sqInt objStack); +static NoDbgRegParms sqInt noFixupFollowFieldofObject(sqInt fieldIndex, sqInt anObject); +static NoDbgRegParms NeverInline sqInt noInlineAllocateSlotsformatclassIndex(sqInt numSlots, sqInt formatField, sqInt classIndex); +static int noUnscannedEphemerons(void); +static NoDbgRegParms sqInt numBytesOfBitsformat(sqInt objOop, sqInt format); +static NoDbgRegParms sqInt numBytesOfBytes(sqInt objOop); +extern sqInt numBytesOf(sqInt objOop); +static NoDbgRegParms usqInt numPointerSlotsOf(sqInt objOop); +static NoDbgRegParms sqInt numSlotsForBytes(sqInt numBytes); +static NoDbgRegParms usqInt numSlotsOfAny(sqInt objOop); +static NoDbgRegParms usqInt numSlotsOfIndexablePointerObj(sqInt objOop); +extern usqInt numSlotsOf(sqInt objOop); +static NoDbgRegParms sqInt numStrongSlotsOfInephemeral(sqInt objOop); +static NoDbgRegParms sqInt numStrongSlotsOfWeakling(sqInt objOop); +static sqInt numUnscannedEphemerons(void); +extern sqInt objCouldBeClassObj(sqInt objOop); +extern sqInt objectAfter(sqInt objOop); +extern sqInt objectBefore(sqInt objOop); +static NoDbgRegParms sqInt objectInPastSpaceBefore(sqInt objOop); +static NoDbgRegParms sqInt objectStartingAt(sqInt address); +static NoDbgRegParms sqInt objectsAccessibleFromRoots(sqInt arrayOfRootsArg); +static NoDbgRegParms NeverInline sqInt objectsReachableFromRoots(sqInt arrayOfRoots); +static NoDbgRegParms sqInt okayOop(sqInt signedOop); +extern sqInt oldSpaceObjectAfter(sqInt objOop); +static NoDbgRegParms NeverInline void outOfPlaceBecomeandcopyHashFlag(sqInt obj1, sqInt obj2, sqInt copyHashFlag); +extern sqInt pinObject(sqInt objOop); +static NoDbgRegParms sqInt popObjStack(sqInt objStack); +extern sqInt popRemappableOop(void); +static NoDbgRegParms void postBecomeScanClassTable(sqInt effectsFlags); +extern sqInt primitiveErrorTable(void); +extern void printActivationsOf(sqInt aMethodObj); +extern void printBogons(void); +extern void printContextReferencesTo(sqInt anOop); +extern void printEntity(sqInt oop); +extern void printEphemerons(void); +extern void printForwarders(void); +static NoDbgRegParms void printForwarderon(sqInt oop, FILE *aStream); +extern void printFreeChunks(void); +extern void printFreeChunk(sqInt freeChunk); +static NoDbgRegParms void printFreeChunkprintAsTreeNode(sqInt freeChunk, sqInt printAsTreeNode); +extern void printFreeListHeads(void); +extern sqInt printFreeList(sqInt chunkOrIndex); +static NoDbgRegParms void printFreeObjecton(sqInt oop, FILE *aStream); +extern void printFreeTree(void); +static NoDbgRegParms void printFreeTreeChunk(sqInt chunkOrZero); +extern void printHeaderOf(sqInt objOop); +static NoDbgRegParms void printImmediateObjecton(sqInt oop, FILE *aStream); +extern void printInstancesOf(sqInt aClassOop); +extern void printInstancesWithClassIndex(sqInt classIndex); +extern void printInvalidClassTableEntries(void); +#if LLDB +extern void printMarkedOops(void); +#endif /* LLDB */ +extern void printMethodImplementorsOf(sqInt anOop); +extern void printMethodReferencesTo(sqInt anOop); +extern void printMournQueue(void); +extern void printNewObjectsInObjStack(sqInt objStack); +static NoDbgRegParms void printNonPointerDataOfon(sqInt oop, FILE *aStream); +extern void printObjectsFromto(sqInt startAddress, sqInt endAddress); +extern void printObjectsWithHash(sqInt hash); +extern void printObjStackAndContents(sqInt objStack); +static NoDbgRegParms sqInt printObjStackPagemyIndexpageTypeprintContents(sqInt objStackPage, sqInt myx, sqInt pageType, sqInt printContents); +static NoDbgRegParms void printObjStackPagemyIndextag(sqInt objStackPage, sqInt myx, char *pageType); +extern void printObjStack(sqInt objStack); +extern NeverInline void printOopsExcept(sqInt (*function)(sqInt)); +extern void printOopsFromto(sqInt startAddress, sqInt endAddress); +extern NeverInline void printOopsSuchThat(sqInt (*function)(sqInt)); +extern void printReferencesTo(sqInt anOop); +static NoDbgRegParms void printStringDataOfon(sqInt oop, FILE *aStream); +#if LLDB +extern void printUnmarkedOops(void); +#endif /* LLDB */ +extern void printWeaklings(void); +static NoDbgRegParms sqInt pushOnUnscannedEphemeronsStack(sqInt anEphemeron); +extern void pushRemappableOop(sqInt oop); +static NoDbgRegParms void queueMourner(sqInt anEphemeronOrWeakArray); +extern int rawHashBitsOf(sqInt objOop); +extern sqInt receiverTagBitsForMethod(sqInt aMethodObj); +static void refireQueuedEphemeronsPostSnapshot(void); +static NoDbgRegParms sqInt relocateObjStackForPlanningCompactorandContents(sqInt objStack, sqInt relocateContents); +extern sqInt removeGCRoot(sqInt *varLoc); +static NoDbgRegParms sqInt returnrestoringObjectsInsavedFirstFieldsandsavedHashes(sqInt errCode, sqInt reachableObjectsArray, sqInt savedFirstFields, sqInt outPointersArray, sqInt savedHashes); +static NoDbgRegParms sqInt returnrestoringObjectsInupTosavedFirstFields(sqInt errCode, sqInt reachableObjectsArray, sqInt limitOrTag, sqInt savedFirstFields); +static NoDbgRegParms NeverInline void reverseBytesIn32BitWordsIn(sqInt segmentWordArray); +static NoDbgRegParms void runLeakCheckerForFreeSpaceignoring(sqInt gcModes, sqInt anOopOrNil); +static NoDbgRegParms void runLeakCheckerFor(sqInt gcModes); +static NoDbgRegParms void runLeakCheckerForexcludeUnmarkedObjsclassIndicesShouldBeValid(sqInt gcModes, sqInt excludeUnmarkedObjs, sqInt classIndicesShouldBeValid); +static NoDbgRegParms void safePrintStringOf(sqInt oop); +static NoDbgRegParms void scavengingGCTenuringIf(sqInt tenuringCriterion); +static NoDbgRegParms NeverInline sqInt setHeapBasememoryLimitendOfMemory(sqInt baseOfHeap, sqInt memLimit, sqInt memEnd); +static NoDbgRegParms void setIsPinnedOfto(sqInt objOop, sqInt aBoolean); +static NoDbgRegParms void setIsRememberedOfto(sqInt objOop, sqInt aBoolean); +extern sqInt shortentoIndexableSize(sqInt objOop, sqInt indexableSize); +extern void shortPrintObjectsFromto(sqInt startAddress, sqInt endAddress); +extern sqInt shouldRemapOop(sqInt oop); +static NoDbgRegParms sqInt sizeOfObjStack(sqInt objStack); +static int slidingCompactionInProgress(void); +extern sqInt slotSizeOf(sqInt oop); +extern sqInt splObj(sqInt index); +static NoDbgRegParms usqInt startOfObject(sqInt objOop); +extern sqInt statNumGCs(void); +extern usqInt storeCheckBoundary(void); +static NoDbgRegParms sqInt storeImageSegmentIntooutPointersroots(sqInt segmentWordArrayArg, sqInt outPointersArrayArg, sqInt arrayOfRootsArg); +extern void storeIntegerofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqLong integerValue); +extern int storeLong32ofObjectwithValue(sqInt fieldIndex, sqInt obj, sqInt valueWord); +extern sqInt storePointerUncheckedofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqInt valuePointer); +extern sqInt storePointerofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqInt valuePointer); +extern sqInt stringForCString(const char *aCString); +static NoDbgRegParms sqInt swizzleObjStackAt(sqInt objStackRootIndex); +extern void tenuringIncrementalGC(void); +static NoDbgRegParms sqInt topOfObjStack(sqInt objStack); +extern sqInt topRemappableOop(void); +static sqInt totalFreeListBytes(void); +extern sqInt trueObject(void); +static void unfireQueuedEphemeronsForSnapshot(void); +static NoDbgRegParms sqInt unlinkFreeChunkchunkBytes(sqInt freeChunk, sqInt chunkBytes); +static NoDbgRegParms NeverInline sqInt unlinkLilliputianChunkindex(sqInt freeChunk, sqInt index); +static NoDbgRegParms void unlinkSolitaryFreeTreeNode(sqInt freeTreeNode); +extern sqInt unpinObject(sqInt objOop); +static sqInt unscannedEphemeronsContainsOnlyActiveEphemerons(void); +static sqInt unscannedEphemeronsContainsOnlyEphemerons(void); +static NoDbgRegParms void updateListStartingAt(sqInt freeNode); +static sqInt validClassTableHashes(void); +static sqInt validClassTableRootPages(void); +extern int validFreeTree(void); +static NoDbgRegParms sqInt validFreeTreeChunk(sqInt chunk); +static NoDbgRegParms const char * validFreeTreeChunkparent(sqInt chunk, sqInt parent); +static sqInt validObjStacks(void); +static NoDbgRegParms sqInt validPostBecomeArrayContents(sqInt anArray); +static NoDbgRegParms sqInt validStorePointerArgs(sqInt fieldIndex, sqInt objOop, sqInt valuePointer); +static NoDbgRegParms sqInt validStorePointerUncheckedArgs(sqInt fieldIndex, sqInt objOop, sqInt valuePointer); +extern sqInt vmEndianness(void); +static NoDbgRegParms char * whereIsMaybeHeapThing(sqInt anOop); +static NeverInline void compact(void); +static NeverInline sqInt copyAndUnmarkMobileObjects(void); +static NoDbgRegParms NeverInline void copyAndUnmark(sqInt firstPass); +static void endCompaction(void); +static NoDbgRegParms void freeFromupTonextObject(usqInt initialToFinger, usqInt limit, sqInt nextObject); +static NoDbgRegParms sqInt isMobile(sqInt obj); +static NoDbgRegParms sqInt isPostMobile(sqInt obj); +static NeverInline sqInt planCompactSavingForwarders(void); +static NoDbgRegParms void reinitializeScanFrom(sqInt initialObject); +extern sqInt remapObj(sqInt objOop); +static sqInt savedFirstFieldsSpaceWasAllocated(void); +static NoDbgRegParms sqInt scanForFirstFreeAndFirstMobileObjectFrom(sqInt initialObject); +extern sqInt shouldRemapObj(sqInt objOop); +static void unmarkObjectsFromFirstFreeObject(void); +static NeverInline void updatePointers(void); +static NoDbgRegParms void updatePointersInsavedFirstFieldPointer(sqInt obj, sqInt firstFieldPtr); +static NoDbgRegParms sqInt useSegmentForSavedFirstFieldsSpace(sqInt spaceEstimate); +static NoDbgRegParms sqInt validRelocationPlanInPass(sqInt onePass); +static NoDbgRegParms usqInt segLimit(SpurSegmentInfo *self_in_SpurSegmentInfo); +static NoDbgRegParms SpurSegmentInfo * addSegmentOfSize(sqInt ammount); +static NoDbgRegParms void adjustSegmentSwizzlesBy(sqInt firstSegmentShift); +static sqInt allBridgesMarked(void); +static void allocateOrExtendSegmentInfos(void); +static NoDbgRegParms sqInt bridgeAt(sqInt segIndex); +static NoDbgRegParms sqInt bridgeFor(SpurSegmentInfo *aSegment); +static NoDbgRegParms void bridgeFromto(SpurSegmentInfo *aSegment, SpurSegmentInfo *nextSegmentOrNil); +static void checkSegments(void); +static void collapseSegmentsPostSwizzle(void); +static NoDbgRegParms void * firstGapOfSizeAtLeast(sqInt size); +static NoDbgRegParms sqInt isEmptySegment(SpurSegmentInfo *seg); +static NoDbgRegParms sqInt isInSegments(usqInt address); +static NoDbgRegParms sqInt isValidSegmentBridge(sqInt objOop); +static NoDbgRegParms SpurSegmentInfo * nextNonEmptySegmentAfter(sqInt i); +static NeverInline void prepareForSnapshot(void); +static NoDbgRegParms sqInt readHeapFromImageFiledataBytes(sqImageFile f, sqInt numBytes); +static void restorePostSnapshot(void); +extern SpurSegmentInfo * segmentContainingObj(sqInt objOop); +static sqInt segmentOverlap(void); +static NoDbgRegParms int shrinkObjectMemory(usqInt delta); +static NoDbgRegParms sqInt swizzleObj(sqInt objOop); +static usqInt totalBytesInSegments(void); +static NoDbgRegParms sqInt writeImageSegmentsToFile(sqImageFile aBinaryStream); +static NoDbgRegParms sqInt writeSegmentnextSegmenttoFile(SpurSegmentInfo *segment, SpurSegmentInfo *nextSegment, sqImageFile aBinaryStream); +static NoDbgRegParms sqInt accessorDepthForExternalPrimitiveMethod(sqInt methodObj); +extern sqInt accessorDepthForPrimitiveMethod(sqInt aMethodObj); +extern sqInt activeProcess(void); +extern void addIdleUsecs(sqInt idleUsecs); +static NoDbgRegParms void addLastLinktoList(sqInt proc, sqInt aList); +static char * allOnesAsCharStar(void); +extern sqInt argumentCountOfClosure(sqInt closurePointer); +extern usqInt argumentCountOfMethodHeader(sqInt header); +extern usqInt argumentCountOf(sqInt methodPointer); +extern void * arrayValueOf(sqInt arrayOop); +static NoDbgRegParms sqInt asciiOfCharacter(sqInt characterObj); +extern void assertValidExecutionPointers(usqInt lip, char *lifp, char *lisp); +static NoDbgRegParms void assertValidStackLimits(sqInt ln); +extern sqInt booleanValueOf(sqInt obj); +extern sqInt callbackEnter(sqInt *callbackID); +extern sqInt callbackLeave(sqInt cbID); +extern sqInt canContextSwitchIfActivatingheader(sqInt theMethod, sqInt methodHeader); +extern sqInt characterForAscii(sqInt ascii); +extern sqInt checkAllAccessibleObjectsOkay(void); +#if LRPCheck +static NeverInline sqInt checkDeliveryOfLongRunningPrimitiveSignal(void); +#endif /* LRPCheck */ +extern sqInt checkedIntegerValueOf(sqInt intOop); +static NoDbgRegParms sqInt checkForEventsMayContextSwitch(sqInt mayContextSwitch); +extern sqInt checkImageHeaderFromBytesAndSize(char *bytes, sqInt totalSize); +static NoDbgRegParms sqInt checkImageVersionFromstartingAtassignRawVersion(sqImageFile f, squeakFileOffsetType imageOffset, sqInt *rawVersionPtr); +static sqInt checkInterpreterIntegrity(void); +static NoDbgRegParms sqInt checkIsStillMarriedContextcurrentFP(sqInt aContext, char *currentFP); +static sqInt checkLogIntegrity(void); +extern sqInt checkOkayInterpreterObjects(sqInt writeBack); +static NoDbgRegParms sqInt checkOkayStackPage(StackPage *thePage); +static NoDbgRegParms sqInt checkOkayStackZone(sqInt writeBack); +static sqInt checkStackIntegrity(void); +static NoDbgRegParms sqInt checkStackPointerForMaybeMarriedContext(sqInt aContext); +static NoDbgRegParms sqInt checkStackPointerIndexForFrame(char *theFP); +static NoDbgRegParms sqInt classNameOfIs(sqInt aClass, char *className); +extern void clearTraceLog(void); +static NoDbgRegParms sqInt contexthasValidInversePCMappingOfin(sqInt aContext, sqInt theIP, char *theFP); +extern sqInt copiedValueCountOfClosure(sqInt closureObj); +extern sqInt copyBits(void); +extern sqInt copyBitsFromtoat(sqInt x0, sqInt x1, sqInt y); +static NoDbgRegParms NeverInline sqInt couldBeProcess(sqInt oop); +static NoDbgRegParms void createActualMessageTo(sqInt lookupClass); +static sqInt defaultNumStackPages(void); +#if SEND_PRINTING +extern void disableSendPrinting(void); +#endif /* SEND_PRINTING */ +extern sqInt disownVM(sqInt flags); +static sqInt divorceAllFrames(void); +static NoDbgRegParms void divorceFramesIn(StackPage *aStackPage); +static NoDbgRegParms sqInt doPrimitiveDivby(sqInt rcvr, sqInt arg); +static NoDbgRegParms sqInt doPrimitiveModby(sqInt rcvr, sqInt arg); +extern sqInt doSignalSemaphoreWithIndex(sqInt index); +extern struct VirtualMachine * * dummyReferToProxy(void); +extern void dumpPrimTraceLog(void); +extern void dumpPrimTraceLogOn(FILE *aStdioStream); +extern void dumpTraceLog(void); +extern NeverInline void eekcr(void); +#if SEND_PRINTING +extern void enableSendPrinting(void); +#endif /* SEND_PRINTING */ +static NoDbgRegParms sqInt ensureCallerContext(char *theFP); +static NoDbgRegParms void ensureImageFormatIsUpToDate(sqInt swapBytes); +static NoDbgRegParms char * establishFrameForContextToReturnTo(sqInt contextToReturnTo); +static NoDbgRegParms void externalDivorceFrameandContext(char *theFP, sqInt ctxt); +static NoDbgRegParms char * externalEnsureIsBaseFrame(char *aFramePtr); +static NoDbgRegParms sqInt externalInstVarofContextput(sqInt index, sqInt maybeMarriedContext, sqInt anOop); +static sqInt externalQuickPrimitiveResponse(void); +static void failUnbalancedPrimitive(void); +extern void * fetchArrayofObject(sqInt fieldIndex, sqInt objectPointer); +extern double fetchFloatofObject(sqInt fieldIndex, sqInt objectPointer); +static NoDbgRegParms sqInt fetchPointerofMarriedContext(sqInt offset, sqInt aContext); +static NoDbgRegParms sqInt fetchStackPointerOf(sqInt aContext); +extern int fileTimesInUTC(void); +static NoDbgRegParms sqInt findClassContainingMethodstartingAt(sqInt meth, sqInt classObj); +extern sqInt findClassOfMethodforReceiver(sqInt meth, sqInt rcvr); +static NoDbgRegParms char * findFrameAboveinPage(char *theFP, StackPage *thePage); +static sqInt findHighestPriority(void); +static NoDbgRegParms sqInt findHomeForContext(sqInt aContext); +static NoDbgRegParms sqInt findMethodWithPrimitiveFromContextUpToContext(sqInt primitive, sqInt senderContext, sqInt homeContext); +extern sqInt findSelectorOfMethod(sqInt aMethodOop); +static NoDbgRegParms char * findSPOfon(char *theFP, StackPage *thePage); +static NoDbgRegParms sqInt findUnwindThroughContext(sqInt homeContext); +extern void flush(void); +static NoDbgRegParms sqInt flushExternalPrimitiveOf(sqInt methodObj); +static NoDbgRegParms void flushMethodCacheForMethod(sqInt oldMethod); +static NoDbgRegParms void followForwardedFrameContentsstackPointer(char *theFP, char *theSP); +extern sqInt forceInterruptCheck(void); +static NoDbgRegParms char * frameCallerSP(char *theFP); +static NoDbgRegParms sqInt frameContext(char *theFP); +static NoDbgRegParms char * frameOfMarriedContext(sqInt aContext); +static NoDbgRegParms sqInt frameStackedReceiverOffset(char *theFP); +static NoDbgRegParms sqInt frameStackedReceivernumArgs(char *theFP, sqInt numArgs); +static void freeUntracedStackPages(void); +extern sqInt fullDisplayUpdate(void); +static NoDbgRegParms void (*functionPointerForinClass(sqInt primIdx, sqInt theClass))(void); +static sqInt getCogVMFeatureFlags(void); +static sqInt getErrorObjectFromPrimFailCode(void); +extern int getFullScreenFlag(void); +extern sqInt getInterruptKeycode(void); +extern sqInt getInterruptPending(void); +extern usqLong getNextWakeupUsecs(void); +extern sqInt getSavedWindowSize(void); +static NoDbgRegParms usqInt getShortFromFileswap(sqImageFile aFile, sqInt swapFlag); +static sqInt getSnapshotScreenSize(void); +extern sqInt * getStackPointer(void); +extern sqInt getThisSessionID(void); +extern FILE * getTranscript(void); +static NoDbgRegParms sqInt handleForwardedSelectorFaultFor(sqInt selectorOop); +static NoDbgRegParms sqInt handleForwardedSendFaultForTag(sqInt classTag); +static NoDbgRegParms sqInt handleSpecialSelectorSendFaultForfpsp(sqInt obj, char *theFP, char *theSP); +static NeverInline void handleStackOverflow(void); +static NoDbgRegParms sqInt handleStackOverflowOrEventAllowContextSwitch(sqInt mayContextSwitch); +extern sqInt highBit(usqInt anUnsignedValue); +extern sqInt homeMethodOf(sqInt aMethodOop); +static NoDbgRegParms sqInt ifCurrentStackPageHasValidHeadPointers(StackPage *thePage); +static NoDbgRegParms usqInt iframeMethod(char *theFP); +extern sqInt includesBehaviorThatOf(sqInt aClass, sqInt aSuperclass); +extern void initStackPagesAndContinueIntowith(void (*continuation)(void *), void *argument); +extern void ioFilenamefromStringofLengthresolveAliases(char *aCharBuffer, char *aFilenameString, sqInt filenameLength, sqInt aBoolean); +static NoDbgRegParms int isBaseFrame(char *theFP); +extern sqInt isBooleanObject(sqInt oop); +static NoDbgRegParms int isEmptyList(sqInt aLinkedList); +extern int isFloatObject(sqInt oop); +static NoDbgRegParms sqInt isFrameonPage(char *aFrame, StackPage *aPage); +extern sqInt isKindOfInteger(sqInt oop); +extern sqInt isLargeIntegerObject(sqInt oop); +extern sqInt isLargeNegativeIntegerObject(sqInt oop); +extern sqInt isLargePositiveIntegerObject(sqInt oop); +static NoDbgRegParms sqInt isLinkedExternalPrimitive(sqInt methodObj); +static NoDbgRegParms sqInt isLiveContext(sqInt oop); +static NoDbgRegParms int isMarriedOrWidowedContext(sqInt aContext); +static NoDbgRegParms sqInt isMetaPrimitiveIndex(sqInt primIndex); +static NoDbgRegParms sqInt isNullExternalPrimitiveCall(sqInt aMethodObj); +static int isPrimitiveFunctionPointerAnIndex(void); +extern int isQuickPrimitiveIndex(sqInt anInteger); +extern int isReadMediatedContextInstVarIndex(sqInt index); +static NoDbgRegParms int isSingleContext(sqInt aContext); +static NoDbgRegParms sqInt isStillMarriedContext(sqInt aContext); +static NoDbgRegParms int isVanillaBlockClosure(sqInt aClosure); +static NoDbgRegParms sqInt isWidowedContextNoConvert(sqInt aOnceMarriedContext); +extern int isWriteMediatedContextInstVarIndex(sqInt index); +extern sqInt isKindOfClass(sqInt oop, sqInt aClass); +extern sqInt isKindOf(sqInt oop, char *className); +extern sqInt isMemberOf(sqInt oop, char *className); +static NoDbgRegParms sqInt lengthOfNameOfClass(sqInt classOop); +extern sqInt literalofMethod(sqInt offset, sqInt methodPointer); +extern sqInt loadBitBltFrom(sqInt bb); +extern void loadInitialContext(void); +extern void longPrintOop(sqInt oop); +extern sqInt longStoreBytecodeForHeader(sqInt methodHeader); +static NoDbgRegParms sqInt lookupInMethodCacheSelclassTag(sqInt selector, sqInt classTag); +static NoDbgRegParms sqInt lookupMethodInClass(sqInt class); +static NoDbgRegParms sqInt lookupMNUInClass(sqInt class); +static NoDbgRegParms sqInt lookupOrdinaryNoMNUEtcInClass(sqInt class); +extern sqInt lookupSelectorinClass(sqInt selector, sqInt class); +static NoDbgRegParms void makeContextSnapshotSafe(sqInt ctxt); +extern usqInt makePointwithxValueyValue(sqInt xValue, sqInt yValue); +static void mapInterpreterOops(void); +static void mapPrimTraceLog(void); +static void mapTraceLog(void); +static void markAndTracePrimTraceLog(void); +static NoDbgRegParms sqInt markAndTraceStackPages(sqInt fullGCFlag); +static void markAndTraceTraceLog(void); +static void markAndTraceUntracedReachableStackPages(void); +static NoDbgRegParms sqInt marriedContextpointsTostackDeltaForCurrentFrame(sqInt spouseContext, sqInt anOop, sqInt stackDeltaForCurrentFrame); +static NoDbgRegParms void marryContextInNewStackPageAndInitializeInterpreterRegisters(sqInt aContext); +static NoDbgRegParms sqInt maybeLeakCheckExternalPrimCall(sqInt aMethodObj); +extern sqInt maybeSelectorOfMethod(sqInt methodObj); +extern sqInt methodArgumentCount(void); +extern sqInt methodClassAssociationOf(sqInt methodPointer); +extern sqInt methodClassOf(sqInt methodPointer); +extern sqInt methodPrimitiveIndex(void); +extern sqInt methodReturnBool(sqInt boolean); +extern sqInt methodReturnFloat(double aFloat); +extern sqInt methodReturnInteger(sqInt integer); +extern sqInt methodReturnReceiver(void); +extern NeverInline sqInt methodReturnString(const char *aCString); +extern sqInt methodReturnValue(sqInt oop); +extern int methodUsesAlternateBytecodeSet(sqInt aMethodObj); +static NoDbgRegParms sqInt methodUsesPrimitiveErrorCode(sqInt aMethodObj); +EXPORT(void) moduleUnloaded(char *aModuleName); +static NoDbgRegParms char * nameOfClass(sqInt classOop); +static NoDbgRegParms char * nameOfClasslengthInto(sqInt classOop, sqInt *lengthPtr); +static NoDbgRegParms sqInt noMarkedContextsOnPage(StackPage *thePage); +static NoDbgRegParms sqInt numSlotsOfMarriedContext(sqInt aContext); +static sqInt numStkPages(void); +static NoDbgRegParms sqInt objectequalsString(sqInt anOop, char *aCString); +extern sqInt ownVM(sqInt disownResult); +extern usqInt pcPreviousToinSqueakV3PlusClosuresOrSistaV1Method(sqInt theIP, sqInt aMethod); +static NoDbgRegParms sqInt penultimateLiteralOf(sqInt aMethodOop); +extern sqInt popStack(void); +extern sqInt pop(sqInt nItems); +extern void popthenPush(sqInt nItems, sqInt oop); +static sqInt postGCUpdateDisplayBits(void); +EXPORT(void) primitiveEventProcessingControl(void); +extern void primitiveFailForFFIExceptionat(usqLong exceptionCode, usqInt pc); +static NoDbgRegParms int primitiveFloatEqualtoArg(sqInt rcvrOop, sqInt argOop); +static NoDbgRegParms int primitiveFloatGreaterOrEqualtoArg(sqInt rcvrOop, sqInt argOop); +static NoDbgRegParms int primitiveFloatGreaterthanArg(sqInt rcvrOop, sqInt argOop); +static NoDbgRegParms int primitiveFloatLessOrEqualtoArg(sqInt rcvrOop, sqInt argOop); +static NoDbgRegParms int primitiveFloatLessthanArg(sqInt rcvrOop, sqInt argOop); +extern sqInt primitiveIndexOfMethodheader(sqInt theMethod, sqInt methodHeader); +extern sqInt primitiveIndexOf(sqInt methodPointer); +extern usqInt primitiveMethod(void); +static NoDbgRegParms void printActivationNameForSelectorstartClass(sqInt aSelector, sqInt startClass); +static NoDbgRegParms void printActivationNameForreceiverisBlockfirstTemporary(sqInt aMethod, sqInt anObject, sqInt isBlock, sqInt maybeMessage); +extern void printAllStacks(void); +extern void printAllStacksOn(FILE *aStdioStream); +extern void printCallStack(void); +static NoDbgRegParms sqInt printCallStackFP(char *theFP); +extern sqInt printCallStackOf(sqInt aContextOrProcessOrFrame); +static NoDbgRegParms sqInt printCallStackOfcurrentFP(sqInt aContext, char *currFP); +extern void printCallStackOn(FILE *aStdioStream); +static NoDbgRegParms sqInt printContextCallStackOf(sqInt aContext); +extern void printContext(sqInt aContext); +static NoDbgRegParms void printDecodeMethodHeaderOop(sqInt methodHeaderOop); +extern void printExternalHeadFrame(void); +static NoDbgRegParms sqInt printFrameAndCallersSPshort(char *theFP, char *theSP, sqInt printShort); +static NoDbgRegParms void printFrameOopat(char *name, char *address); +static NoDbgRegParms void printFrameOopindexat(char *name, sqInt idx, char *address); +extern void printFramesInPage(StackPage *thePage); +extern void printFramesOnStackPageListInUse(void); +static NoDbgRegParms void printFrameThingandFrameat(char *name, char *theFP, char *address); +static NoDbgRegParms void printFrameThingatextraString(char *name, char *address, char *extraStringOrNil); +extern sqInt printFrame(char *theFP); +static NoDbgRegParms sqInt printHexPtrnp(void *p); +extern void printHex(usqInt n); +extern void printLikelyImplementorsOfSelector(sqInt selector); +static NoDbgRegParms void printLogEntryAt(sqInt i); +extern void printMethodCache(void); +extern void printMethodDictionaryOf(sqInt behavior); +extern void printMethodDictionary(sqInt dictionary); +static NoDbgRegParms void printNameOfClasscount(sqInt classOop, sqInt cnt); +static NoDbgRegParms void printNum(sqInt n); +static NoDbgRegParms void printOopShortInner(sqInt oop); +extern void printOop(sqInt oop); +static NoDbgRegParms sqInt printPrimLogEntryAthasParameter(sqInt i, sqInt hasParameter); +extern void printProcessStack(sqInt aProcess); +extern sqInt printProcsOnList(sqInt procList); +extern sqInt printStackCallStackOf(sqInt aContextOrProcessOrFrame); +extern void printStackPageList(void); +extern void printStackPageListInUse(void); +extern void printStackPages(void); +extern void printStackPagesInUse(void); +static NoDbgRegParms void printStackPageuseCount(StackPage *page, sqInt n); +extern void printStackReferencesTo(sqInt oop); +static NoDbgRegParms void printStringOf(sqInt oop); +extern void print(char *s); +extern sqInt processListForProcess(sqInt aProcess); +extern sqInt processOSErrInstVarOffset(void); +extern void pushBool(sqInt trueOrFalse); +static NoDbgRegParms sqInt pushedReceiverOrClosureOfFrame(char *theFP); +extern void pushFloat(double f); +extern sqInt pushInteger(sqInt integerValue); +extern void push(sqInt object); +static NoDbgRegParms void putLongtoFile(sqInt aLong, sqImageFile aFile); +static NoDbgRegParms void putShorttoFile(short aShort, sqImageFile aFile); +static NoDbgRegParms void putToSleepyieldingIf(sqInt aProcess, sqInt yieldImplicitly); +static NoDbgRegParms void putWord32toFile(int aWord32, sqImageFile aFile); +static NoDbgRegParms sqInt quickFetchIntegerofObject(sqInt fieldIndex, sqInt objectPointer); +extern sqInt readableFormat(sqInt imageVersion); +static NoDbgRegParms NeverInline void reapAndResetErrorCodeToheader(char *theSP, sqInt methodHeader); +extern sqInt reestablishContextPriorToCallback(sqInt callbackContext); +static NoDbgRegParms sqInt removeFirstLinkOfList(sqInt aList); +static NoDbgRegParms sqInt removeProcessfromList(sqInt aProcess, sqInt aList); +static NoDbgRegParms sqInt resumepreemptedYieldingIffrom(sqInt aProcess, sqInt yieldImplicitly, sqInt sourceCode); +static sqInt retryPrimitiveOnFailure(void); +extern sqInt returnAsThroughCallbackContext(sqInt returnTypeOop, VMCallbackContext *vmCallbackContext, sqInt callbackMethodContext); +static NoDbgRegParms void reverseDisplayFromto(sqInt startIndex, sqInt endIndex); +static NoDbgRegParms void rewriteMethodCacheEntryForExternalPrimitiveToFunction(void (*localPrimAddress)()); +static NoDbgRegParms sqInt safeMethodClassOf(sqInt methodPointer); +static NoDbgRegParms sqInt saneFunctionPointerForFailureOfPrimIndex(sqInt primIndex); +extern sqInt schedulerPointer(void); +extern void setBreakMNUSelector(char *aString); +extern void setBreakSelector(char *aString); +extern void setFullScreenFlag(sqInt aBoolean); +extern void * setInterruptCheckChain(void (*aFunction)()); +extern void setInterruptKeycode(sqInt value); +extern void setInterruptPending(sqInt value); +extern void setNextWakeupUsecs(usqLong value); +extern void setSavedWindowSize(sqInt value); +static void setSignalLowSpaceFlagAndSaveProcess(void); +static NoDbgRegParms void setTraceFlagOnContextsFramesPageIfNeeded(sqInt aContext); +static NoDbgRegParms sqInt shortPrintContext(sqInt aContext); +static NoDbgRegParms sqInt shortPrintFrameAndCallers(char *theFP); +extern void shortPrintFramesInPage(StackPage *thePage); +extern void shortPrintFramesOnStackPageListInUse(void); +extern void shortPrintFrameAndNCallers(char *theFP, sqInt n); +static NoDbgRegParms void shortPrintOop(sqInt oop); +static NoDbgRegParms sqInt shortReversePrintFrameAndCallers(char *aFramePointer); +extern sqInt showDisplayBitsLeftTopRightBottom(sqInt aForm, sqInt l, sqInt t, sqInt r, sqInt b); +static sqInt signalExternalSemaphores(void); +extern sqInt sizeOfCallPrimitiveBytecode(sqInt methodHeader); +extern sqInt sizeOfLongStoreTempBytecode(sqInt methodHeader); +extern sqInt sizeOfSTArrayFromCPrimitive(void *cPtr); +static int slowPrimitiveResponse(void); +static NoDbgRegParms sqInt snapshot(sqInt embedded); +extern sqInt specialSelector(sqInt index); +static NoDbgRegParms void spurPostBecomeAction(sqInt theBecomeEffectsFlags); +extern double stackFloatValue(sqInt offset); +extern sqInt stackIntegerValue(sqInt offset); +static sqInt stackLimitBytes(void); +extern sqInt stackMutableObjectValue(sqInt offset); +extern sqInt stackObjectValue(sqInt offset); +static sqInt stackPageByteSize(void); +static NoDbgRegParms sqInt stackPointerForMaybeMarriedContext(sqInt aContext); +static NoDbgRegParms sqInt stackPointerIndexForFrame(char *theFP); +extern usqIntptr_t stackPositiveMachineIntegerValue(sqInt offset); +extern sqIntptr_t stackSignedMachineIntegerValue(sqInt offset); +extern sqInt stackTop(void); +extern sqInt stackValue(sqInt offset); +extern usqInt startPCOfMethod(sqInt aCompiledMethod); +extern sqInt stObjectat(sqInt array, sqInt index); +extern sqInt stObjectatput(sqInt array, sqInt index, sqInt value); +static NoDbgRegParms char * storeSenderOfFramewithValue(char *theFP, sqInt anOop); +extern sqInt stSizeOf(sqInt oop); +extern sqInt superclassOf(sqInt classPointer); +static NoDbgRegParms sqInt synchronousSignal(sqInt aSemaphore); +extern usqInt tempCountOf(sqInt methodPointer); +extern usqInt temporaryCountOfMethodHeader(sqInt header); +extern sqInt ultimateLiteralOf(sqInt aMethodOop); +static NoDbgRegParms sqInt unfollowFirstLiteralOfMaybeCalloutMethodprimitiveIndex(sqInt methodObj, sqInt primIndex); +static NoDbgRegParms sqInt updateDisplayLeftTopRightBottom(sqInt l, sqInt t, sqInt r, sqInt b); +static NoDbgRegParms sqInt validBCPCinMethod(sqInt thePC, usqInt aMethod); +static NoDbgRegParms sqInt validInstructionPointerinFrame(usqInt anInstrPointer, char *fp); +static sqInt validStackPageBaseFrames(void); +#if LRPCheck +static NoDbgRegParms NeverInline void voidLongRunningPrimitive(char *reason); +#endif /* LRPCheck */ +static sqInt wakeHighestPriority(void); +static NeverInline sqInt writeImageFileIO(void); +static NoDbgRegParms usqInt cloneContext(sqInt aContext); +static NoDbgRegParms sqInt fieldOrSenderFPofContext(sqInt index, sqInt contextObj); +static NoDbgRegParms sqInt fieldofFrame(sqInt index, char *theFP); +static NoDbgRegParms sqInt isAppropriateForCopyObject(sqInt oop); +static NoDbgRegParms void (*linkExternalCallerrInto(sqInt externalCallLiteral, sqInt *failPtr))(void); +static void primitiveClone(void); +static void primitiveContextAt(void); +static void primitiveContextAtPut(void); +static void primitiveContextPreserveTemps(void); +static void primitiveContextSize(void); +static void primitiveDeferDisplayUpdates(void); +static void primitiveDoNamedPrimitiveWithArgs(void); +static void primitiveDoPrimitiveWithArgs(void); +static void primitiveExternalCall(void); +static void primitiveFindHandlerContext(void); +static void primitiveFindNextUnwindContext(void); +static void primitiveFullGC(void); +static void primitiveGetSetFFIExceptionHandling(void); +static void primitiveIncrementalGC(void); +static void primitiveInstVarAt(void); +static void primitiveInstVarAtPut(void); +static void primitiveInvokeObjectAsMethod(void); +#if LRPCheck +EXPORT(void) primitiveLongRunningPrimitive(void); +#endif /* LRPCheck */ +static void primitiveObjectPointsTo(void); +static void primitivePerform(void); +static void primitivePin(void); +EXPORT(void) primitiveSetGCSemaphore(void); +static void primitiveSignalAtMilliseconds(void); +static void primitiveSignalAtUTCMicroseconds(void); +static void primitiveSlotAt(void); +static void primitiveStoreStackp(void); +static void primitiveVMParameter(void); +EXPORT(void) primitiveVoidReceiver(void); +static NoDbgRegParms void pruneStackstackp(sqInt stack, sqInt stackp); +static void unmarkAfterPathTo(void); + +/*** Variables ***/ +#if SQ_USE_GLOBAL_STRUCT +# define _iss /* define in-struct static as void */ +static struct foo { +#else +# define _iss static +#endif +_iss char * stackLimit; +_iss char * stackPointer; +_iss char * framePointer; +_iss volatile usqIntptr_t CStackPointer; +_iss volatile usqIntptr_t CFramePointer; +_iss volatile usqIntptr_t CReturnAddress; +_iss usqInt scavengeThreshold; +_iss usqInt freeStart; +_iss sqInt needGCFlag; +_iss sqInt specialObjectsOop; +_iss sqInt primFailCode; +_iss unsigned char primTraceLogIndex; +_iss usqInt newMethod; +_iss usqInt instructionPointer; +_iss sqInt argumentCount; +_iss sqLong nextProfileTick; +_iss sqInt primTraceLog[256]; +_iss sqInt nilObj; +_iss StackPage * stackPage; +_iss usqInt oldSpaceStart; +_iss usqInt method; +_iss sqInt bytecodeSetSelector; +_iss FILE * transcript; +_iss usqInt endOfMemory; +_iss sqInt messageSelector; +_iss usqInt pastSpaceStart; +_iss usqInt newSpaceStart; +_iss sqInt trueObj; +_iss StackPage * pages; +_iss sqInt falseObj; +_iss usqInt totalFreeOldSpace; +_iss sqInt classTableFirstPage; +_iss sqInt hiddenRootsObj; +_iss SpurSegmentInfo * segments; +_iss sqInt markStack; +_iss sqInt numSegments; +_iss sqInt weaklingStack; +_iss sqInt traceLogIndex; +_iss sqInt * freeLists; +_iss sqInt rememberedSetSize; +_iss sqInt numClassTablePages; +_iss sqInt * rememberedSet; +_iss sqInt futureSurvivorStart; +_iss sqInt tempOop; +_iss usqInt firstFreeObject; +_iss usqInt freeListsMask; +_iss StackPage * mostRecentlyUsedPage; +_iss usqInt lastHash; +_iss sqInt mournQueue; +_iss char * stackBasePlus1; +_iss sqInt numRememberedEphemerons; +_iss sqInt numStackPages; +_iss usqInt totalHeapSizeIncludingBridges; +_iss sqInt classTableIndex; +_iss usqInt lastMobileObject; +_iss sqInt profileProcess; +_iss sqInt profileSemaphore; +_iss sqInt profileMethod; +_iss sqInt remapBufferCount; +_iss sqInt becomeEffectsFlags; +#if LRPCheck +_iss sqInt longRunningPrimitiveCheckSemaphore; +#endif +_iss sqInt mobileStart; +_iss sqInt performWithArgumentsRecursionGuard; +_iss sqInt growHeadroom; +_iss sqInt lkupClass; +_iss sqInt tenureCriterion; +_iss sqInt tenureThreshold; +_iss sqInt ephemeronList; +_iss sqInt gcMode; +_iss sqInt lastCoggableInterpretedBlockMethod; +_iss sqInt lastUncoggableInterpretedBlockMethod; +_iss char * objStackInvalidBecause; +_iss sqInt classNameIndex; +_iss sqInt gcPhaseInProgress; +#if LRPCheck +_iss sqInt longRunningPrimitiveCheckMethod; +#endif +_iss sqInt extraRootCount; +_iss sqInt highestRunnableProcessPriority; +_iss sqInt invalidObjStackPage; +_iss sqInt lastMethodCacheProbeWrite; +_iss sqInt metaAccessorDepth; +_iss usqInt objectAfterLastMobileObject; +_iss sqInt tempOop2; +_iss sqInt cogCompiledCodeCompactionCalledFor; +_iss usqInt freeOldSpaceStart; +_iss sqInt previousRememberedSetSize; +_iss sqInt validatedIntegerClassFlags; +_iss usqInt firstMobileObject; +_iss sqInt numSegInfos; +_iss sqInt savedFirstFieldsSpaceNotInOldSpace; +_iss usqLong statGCEndUsecs; +_iss sqInt bytesPerPage; +_iss usqInt cogCodeSize; +#if LRPCheck +_iss usqLong longRunningPrimitiveStopUsecs; +#endif +_iss usqLong nextWakeupUsecs; +_iss sqInt shrinkThreshold; +_iss sqInt statTenures; +_iss sqInt thisClassIndex; +#if LRPCheck +_iss usqLong longRunningPrimitiveStartUsecs; +#endif +_iss sqInt metaclassNumSlots; +_iss sqInt pendingFinalizationSignals; +_iss usqLong statCheckForEvents; +_iss sqInt statSurvivorCount; +_iss sqInt weakList; +_iss sqInt firstFieldOfRememberedSet; +_iss usqLong gcStartUsecs; +_iss sqInt marking; +_iss sqLong oldSpaceUsePriorToScavenge; +_iss sqInt statMaxAllocSegmentTime; +_iss sqInt statScavenges; +_iss sqInt statShrinkMemory; +_iss sqInt firstSegmentSize; +_iss sqInt multipleBytecodeSetsActive; +_iss sqInt rememberedSetLimit; +_iss FILE * scavengeLog; +_iss sqInt statFullGCs; +_iss sqInt biasForGC; +_iss sqInt deferProfileCheckForVNCS; +_iss usqInt lowSpaceThreshold; +_iss sqInt rememberedSetRedZone; +_iss sqLong secondaryErrorCode; +_iss usqLong statAllocatedBytes; +_iss sqInt statCodeCompactionCount; +_iss usqLong statCodeCompactionUsecs; +_iss usqLong statCompactionUsecs; +_iss usqLong statFullGCUsecs; +_iss sqInt statGrowMemory; +_iss usqLong statMarkUsecs; +_iss sqInt statNumMaps; +_iss usqLong statScavengeGCUsecs; +_iss usqLong statSweepUsecs; +_iss sqInt the2ndUnknownShort; +_iss sqInt anomaly; +_iss sqInt edenBytes; +_iss sqInt externalPrimitiveTableFirstFreeIndex; +_iss sqInt ffiCalloutVMHandle; +_iss sqInt freeSpaceCheckOopToIgnore; +_iss usqInt heapSizeAtPreviousGC; +_iss sqInt interruptKeycode; +_iss sqInt interruptPending; +#if LRPCheck +_iss sqInt longRunningPrimitiveCheckSequenceNumber; +#endif +_iss sqInt methodDictLinearSearchLimit; +_iss sqInt oldImageBaseAddress; +_iss sqInt savedWindowSize; +_iss usqLong statForceInterruptCheck; +_iss usqLong statIOProcessEvents; +_iss usqLong statIdleUsecs; +_iss sqInt statMarkCount; +_iss sqInt statMaxPageCountWhenMapping; +_iss sqInt statPageCountWhenMappingSum; +_iss usqLong statProcessSwitch; +_iss sqInt statRootTableCount; +_iss usqLong statStackOverflow; +_iss usqLong statStackPageDivorce; +_iss sqInt canSwizzle; +_iss sqInt classLinkedListClassTag; +_iss sqInt deferSmash; +_iss sqInt deferredSmash; +_iss sqInt extraFramesToMoveOnOverflow; +_iss sqInt globalSessionID; +#if LRPCheck +_iss sqInt longRunningPrimitiveSignalUndelivered; +#endif +_iss sqInt maxExtSemTabSizeSet; +_iss sqInt primCalloutIsExternal; +_iss sqInt refCountToShrinkRT; +_iss sqInt signalLowSpace; +_iss sqInt statCompactPassCount; +_iss usqLong statIGCDeltaUsecs; +_iss sqInt statPendingFinalizationSignals; +_iss sqInt statRootTableOverflows; +_iss usqLong statSGCDeltaUsecs; +_iss sqInt bogon; +_iss sqInt classByteArrayCompactIndex; +_iss usqInt exceptionPC; +_iss sqInt gcSemaphoreIndex; +_iss usqLong gcSweepEndUsecs; +#if LRPCheck +_iss usqLong longRunningPrimitiveGCUsecs; +#endif +_iss usqLong nextPollUsecs; +_iss sqInt overflowLimit; +_iss StackPage * overflowedPage; +_iss sqInt printSends; +_iss sqInt tenuringClassIndex; +_iss sqInt statIncrGCs; +_iss sqIntptr_t methodCache[MethodCacheSize + 1 /* 4097 */]; +_iss sqInt traceLog[TraceBufferSize /* 768 */]; +_iss sqInt remapBuffer[RemapBufferSize + 1 /* 26 */]; +_iss sqInt *extraRoots[ExtraRootsSize + 1 /* 65 */]; +_iss usqLong byteCount; +_iss sqInt typesCollector; +_iss SpurContiguousObjStack unscannedEphemerons; +_iss SpurContiguousObjStack savedFirstFieldsSpace; +_iss SpurNewSpaceSpace pastSpace; +_iss SpurScavengeLogRecord scavengeLogRecord; +_iss SpurNewSpaceSpace futureSpace; +_iss SpurNewSpaceSpace eden; +_iss float heapGrowthToSizeGCRatio; +_iss double tenuringProportion; +#undef _iss +#if SQ_USE_GLOBAL_STRUCT + } fum; +# if SQ_USE_GLOBAL_STRUCT_REG +# define DECL_MAYBE_SQ_GLOBAL_STRUCT /* using a global reg pointer */ +# define DECL_MAYBE_VOLATILE_SQ_GLOBAL_STRUCT /* using a global reg pointer */ +# else +# define DECL_MAYBE_SQ_GLOBAL_STRUCT register struct foo * foo = &fum; +# define DECL_MAYBE_VOLATILE_SQ_GLOBAL_STRUCT volatile register struct foo * foo = &fum; +# endif +# define GIV(interpreterInstVar) (foo->interpreterInstVar) +#else +# define DECL_MAYBE_SQ_GLOBAL_STRUCT /* oh, no mr bill! */ +# define DECL_MAYBE_VOLATILE_SQ_GLOBAL_STRUCT /* oh no, mr bill! */ +# define GIV(interpreterInstVar) interpreterInstVar +#endif +#if SQ_USE_GLOBAL_STRUCT +# if SQ_USE_GLOBAL_STRUCT_REG +# define fooxstr(s) foostr(s) +# define foostr(s) #s +register struct foo * foo asm(fooxstr(USE_GLOBAL_STRUCT_REG)); +# else +static struct foo * foo = &fum; +# endif +#endif +static void (*primitiveFunctionPointer)(); +static void *primitiveCalloutPointer = (void *)-1; +static usqInt (* const pcPreviousToFunction)(sqInt,sqInt) = pcPreviousToinSqueakV3PlusClosuresOrSistaV1Method; +static void (*interruptCheckChain)(void) = 0; +static int (*sHEAFn)() = 0; +static void (*primitiveTable[MaxPrimitiveIndex + 2 /* 585 */])(void) = { + /* 0 */ (void (*)(void))0, + /* 1 */ primitiveAdd, + /* 2 */ primitiveSubtract, + /* 3 */ primitiveLessThan, + /* 4 */ primitiveGreaterThan, + /* 5 */ primitiveLessOrEqual, + /* 6 */ primitiveGreaterOrEqual, + /* 7 */ primitiveEqual, + /* 8 */ primitiveNotEqual, + /* 9 */ primitiveMultiply, + /* 10 */ primitiveDivide, + /* 11 */ primitiveMod, + /* 12 */ primitiveDiv, + /* 13 */ primitiveQuo, + /* 14 */ primitiveBitAnd, + /* 15 */ primitiveBitOr, + /* 16 */ primitiveBitXor, + /* 17 */ primitiveBitShift, + /* 18 */ primitiveMakePoint, + /* 19 */ (void (*)(void))0, + /* 20 */ primitiveRemLargeIntegers, + /* 21 */ primitiveAddLargeIntegers, + /* 22 */ primitiveSubtractLargeIntegers, + /* 23 */ primitiveLessThanLargeIntegers, + /* 24 */ primitiveGreaterThanLargeIntegers, + /* 25 */ primitiveLessOrEqualLargeIntegers, + /* 26 */ primitiveGreaterOrEqualLargeIntegers, + /* 27 */ primitiveEqualLargeIntegers, + /* 28 */ primitiveNotEqualLargeIntegers, + /* 29 */ primitiveMultiplyLargeIntegers, + /* 30 */ primitiveDivideLargeIntegers, + /* 31 */ primitiveModLargeIntegers, + /* 32 */ primitiveDivLargeIntegers, + /* 33 */ primitiveQuoLargeIntegers, + /* 34 */ primitiveBitAndLargeIntegers, + /* 35 */ primitiveBitOrLargeIntegers, + /* 36 */ primitiveBitXorLargeIntegers, + /* 37 */ primitiveBitShiftLargeIntegers, + /* 38 */ primitiveFloatAt, + /* 39 */ primitiveFloatAtPut, + /* 40 */ primitiveAsFloat, + /* 41 */ primitiveFloatAdd, + /* 42 */ primitiveFloatSubtract, + /* 43 */ primitiveFloatLessThan, + /* 44 */ primitiveFloatGreaterThan, + /* 45 */ primitiveFloatLessOrEqual, + /* 46 */ primitiveFloatGreaterOrEqual, + /* 47 */ primitiveFloatEqual, + /* 48 */ primitiveFloatNotEqual, + /* 49 */ primitiveFloatMultiply, + /* 50 */ primitiveFloatDivide, + /* 51 */ primitiveTruncated, + /* 52 */ primitiveFractionalPart, + /* 53 */ primitiveExponent, + /* 54 */ primitiveTimesTwoPower, + /* 55 */ primitiveSquareRoot, + /* 56 */ primitiveSine, + /* 57 */ primitiveArctan, + /* 58 */ primitiveLogN, + /* 59 */ primitiveExp, + /* 60 */ primitiveAt, + /* 61 */ primitiveAtPut, + /* 62 */ primitiveSize, + /* 63 */ primitiveStringAt, + /* 64 */ primitiveStringAtPut, + /* 65 */ (void (*)(void))0, + /* 66 */ (void (*)(void))0, + /* 67 */ (void (*)(void))0, + /* 68 */ primitiveObjectAt, + /* 69 */ primitiveObjectAtPut, + /* 70 */ primitiveNew, + /* 71 */ primitiveNewWithArg, + /* 72 */ primitiveArrayBecomeOneWay, + /* 73 */ primitiveInstVarAt, + /* 74 */ primitiveInstVarAtPut, + /* 75 */ primitiveIdentityHash, + /* 76 */ primitiveStoreStackp, + /* 77 */ primitiveSomeInstance, + /* 78 */ primitiveNextInstance, + /* 79 */ primitiveNewMethod, + /* 80 */ (void (*)(void))0, + /* 81 */ (void (*)(void))0, + /* 82 */ (void (*)(void))0, + /* 83 */ primitivePerform, + /* 84 */ primitivePerformWithArgs, + /* 85 */ primitiveSignal, + /* 86 */ primitiveWait, + /* 87 */ primitiveResume, + /* 88 */ primitiveSuspend, + /* 89 */ primitiveFlushCache, + /* 90 */ (void (*)(void))0, + /* 91 */ primitiveTestDisplayDepth, + /* 92 */ primitiveSetDisplayMode, + /* 93 */ primitiveInputSemaphore, + /* 94 */ primitiveGetNextEvent, + /* 95 */ primitiveInputWord, + /* 96 */ primitiveObjectsAccessibleFromRoots, + /* 97 */ primitiveSnapshot, + /* 98 */ primitiveStoreImageSegment, + /* 99 */ primitiveLoadImageSegment, + /* 100 */ primitivePerformInSuperclass, + /* 101 */ primitiveBeCursor, + /* 102 */ primitiveBeDisplay, + /* 103 */ primitiveScanCharacters, + /* 104 */ (void (*)(void))0, + /* 105 */ primitiveStringReplace, + /* 106 */ primitiveScreenSize, + /* 107 */ (void (*)(void))0, + /* 108 */ (void (*)(void))0, + /* 109 */ (void (*)(void))0, + /* 110 */ primitiveIdentical, + /* 111 */ primitiveClass, + /* 112 */ primitiveBytesLeft, + /* 113 */ primitiveQuit, + /* 114 */ primitiveExitToDebugger, + /* 115 */ primitiveChangeClass, + /* 116 */ primitiveFlushCacheByMethod, + /* 117 */ primitiveExternalCall, + /* 118 */ primitiveDoPrimitiveWithArgs, + /* 119 */ primitiveFlushCacheBySelector, + /* 120 */ primitiveCalloutToFFI, + /* 121 */ primitiveImageName, + /* 122 */ primitiveNoop, + /* 123 */ (void (*)(void))0, + /* 124 */ primitiveLowSpaceSemaphore, + /* 125 */ primitiveSignalAtBytesLeft, + /* 126 */ primitiveDeferDisplayUpdates, + /* 127 */ primitiveShowDisplayRect, + /* 128 */ primitiveArrayBecome, + /* 129 */ primitiveSpecialObjectsOop, + /* 130 */ primitiveFullGC, + /* 131 */ primitiveIncrementalGC, + /* 132 */ primitiveObjectPointsTo, + /* 133 */ primitiveSetInterruptKey, + /* 134 */ primitiveInterruptSemaphore, + /* 135 */ primitiveMillisecondClock, + /* 136 */ primitiveSignalAtMilliseconds, + /* 137 */ primitiveSecondsClock, + /* 138 */ primitiveSomeObject, + /* 139 */ primitiveNextObject, + /* 140 */ primitiveBeep, + /* 141 */ primitiveClipboardText, + /* 142 */ primitiveVMPath, + /* 143 */ primitiveShortAt, + /* 144 */ primitiveShortAtPut, + /* 145 */ primitiveConstantFill, + /* 146 */ (void (*)(void))0, + /* 147 */ (void (*)(void))0, + /* 148 */ primitiveClone, + /* 149 */ primitiveGetAttribute, + /* 150 */ primitiveCosine, + /* 151 */ (void (*)(void))0, + /* 152 */ (void (*)(void))0, + /* 153 */ (void (*)(void))0, + /* 154 */ (void (*)(void))0, + /* 155 */ (void (*)(void))0, + /* 156 */ (void (*)(void))0, + /* 157 */ (void (*)(void))0, + /* 158 */ primitiveCompareWith, + /* 159 */ primitiveHashMultiply, + /* 160 */ primitiveAdoptInstance, + /* 161 */ primitiveSetOrHasIdentityHash, + /* 162 */ (void (*)(void))0, + /* 163 */ primitiveGetImmutability, + /* 164 */ primitiveSetImmutability, + /* 165 */ primitiveIntegerAt, + /* 166 */ primitiveIntegerAtPut, + /* 167 */ primitiveYield, + /* 168 */ primitiveCopyObject, + /* 169 */ primitiveNotIdentical, + /* 170 */ primitiveAsCharacter, + /* 171 */ primitiveImmediateAsInteger, + /* 172 */ primitiveFetchNextMourner, + /* 173 */ primitiveSlotAt, + /* 174 */ primitiveSlotAtPut, + /* 175 */ primitiveBehaviorHash, + /* 176 */ primitiveMaxIdentityHash, + /* 177 */ primitiveAllInstances, + /* 178 */ primitiveAllObjects, + /* 179 */ (void (*)(void))0, + /* 180 */ primitiveGrowMemoryByAtLeast, + /* 181 */ primitiveSizeInBytesOfInstance, + /* 182 */ primitiveSizeInBytes, + /* 183 */ primitiveIsPinned, + /* 184 */ primitivePin, + /* 185 */ primitiveExitCriticalSection, + /* 186 */ primitiveEnterCriticalSection, + /* 187 */ primitiveTestAndSetOwnershipOfCriticalSection, + /* 188 */ primitiveExecuteMethodArgsArray, + /* 189 */ primitiveExecuteMethod, + /* 190 */ (void (*)(void))0, + /* 191 */ (void (*)(void))0, + /* 192 */ (void (*)(void))0, + /* 193 */ (void (*)(void))0, + /* 194 */ (void (*)(void))0, + /* 195 */ primitiveFindNextUnwindContext, + /* 196 */ primitiveTerminateTo, + /* 197 */ primitiveFindHandlerContext, + /* 198 */ (void (*)(void))0, + /* 199 */ (void (*)(void))0, + /* 200 */ primitiveClosureCopyWithCopiedValues, + /* 201 */ primitiveClosureValue, + /* 202 */ primitiveClosureValue, + /* 203 */ primitiveClosureValue, + /* 204 */ primitiveClosureValue, + /* 205 */ primitiveClosureValue, + /* 206 */ primitiveClosureValueWithArgs, + /* 207 */ primitiveFullClosureValue, + /* 208 */ primitiveFullClosureValueWithArgs, + /* 209 */ primitiveFullClosureValueNoContextSwitch, + /* 210 */ primitiveContextAt, + /* 211 */ primitiveContextAtPut, + /* 212 */ primitiveContextSize, + /* 213 */ primitiveContextPreserveTemps, + /* 214 */ primitiveVoidVMState, + /* 215 */ primitiveVoidVMStateForMethod, + /* 216 */ primitiveMethodXray, + /* 217 */ (void (*)(void))0, + /* 218 */ primitiveDoNamedPrimitiveWithArgs, + /* 219 */ (void (*)(void))0, + /* 220 */ primitiveGetSetFFIExceptionHandling, + /* 221 */ primitiveClosureValueNoContextSwitch, + /* 222 */ primitiveClosureValueNoContextSwitch, + /* 223 */ (void (*)(void))0, + /* 224 */ (void (*)(void))0, + /* 225 */ (void (*)(void))0, + /* 226 */ (void (*)(void))0, + /* 227 */ (void (*)(void))0, + /* 228 */ (void (*)(void))0, + /* 229 */ (void (*)(void))0, + /* 230 */ primitiveRelinquishProcessor, + /* 231 */ primitiveForceDisplayUpdate, + /* 232 */ primitiveFormPrint, + /* 233 */ primitiveSetFullScreen, + /* 234 */ (void (*)(void))0, + /* 235 */ (void (*)(void))0, + /* 236 */ (void (*)(void))0, + /* 237 */ (void (*)(void))0, + /* 238 */ primitiveFloatArrayAt, + /* 239 */ primitiveFloatArrayAtPut, + /* 240 */ primitiveUTCMicrosecondClock, + /* 241 */ primitiveLocalMicrosecondClock, + /* 242 */ primitiveSignalAtUTCMicroseconds, + /* 243 */ primitiveUpdateTimezone, + /* 244 */ primitiveUtcAndTimezoneOffset, + /* 245 */ primitiveCoarseUTCMicrosecondClock, + /* 246 */ primitiveCoarseLocalMicrosecondClock, + /* 247 */ primitiveSnapshotEmbedded, + /* 248 */ primitiveArrayBecomeOneWayNoCopyHash, + /* 249 */ primitiveArrayBecomeOneWayCopyHashArg, + /* 250 */ primitiveClearVMProfile, + /* 251 */ primitiveControlVMProfiling, + /* 252 */ primitiveVMProfileSamplesInto, + /* 253 */ primitiveCollectCogCodeConstituents, + /* 254 */ primitiveVMParameter, + /* 255 */ (void (*)(void))0, + /* 256 */ (void (*)(void))256, + /* 257 */ (void (*)(void))257, + /* 258 */ (void (*)(void))258, + /* 259 */ (void (*)(void))259, + /* 260 */ (void (*)(void))260, + /* 261 */ (void (*)(void))261, + /* 262 */ (void (*)(void))262, + /* 263 */ (void (*)(void))263, + /* 264 */ (void (*)(void))264, + /* 265 */ (void (*)(void))265, + /* 266 */ (void (*)(void))266, + /* 267 */ (void (*)(void))267, + /* 268 */ (void (*)(void))268, + /* 269 */ (void (*)(void))269, + /* 270 */ (void (*)(void))270, + /* 271 */ (void (*)(void))271, + /* 272 */ (void (*)(void))272, + /* 273 */ (void (*)(void))273, + /* 274 */ (void (*)(void))274, + /* 275 */ (void (*)(void))275, + /* 276 */ (void (*)(void))276, + /* 277 */ (void (*)(void))277, + /* 278 */ (void (*)(void))278, + /* 279 */ (void (*)(void))279, + /* 280 */ (void (*)(void))280, + /* 281 */ (void (*)(void))281, + /* 282 */ (void (*)(void))282, + /* 283 */ (void (*)(void))283, + /* 284 */ (void (*)(void))284, + /* 285 */ (void (*)(void))285, + /* 286 */ (void (*)(void))286, + /* 287 */ (void (*)(void))287, + /* 288 */ (void (*)(void))288, + /* 289 */ (void (*)(void))289, + /* 290 */ (void (*)(void))290, + /* 291 */ (void (*)(void))291, + /* 292 */ (void (*)(void))292, + /* 293 */ (void (*)(void))293, + /* 294 */ (void (*)(void))294, + /* 295 */ (void (*)(void))295, + /* 296 */ (void (*)(void))296, + /* 297 */ (void (*)(void))297, + /* 298 */ (void (*)(void))298, + /* 299 */ (void (*)(void))299, + /* 300 */ (void (*)(void))300, + /* 301 */ (void (*)(void))301, + /* 302 */ (void (*)(void))302, + /* 303 */ (void (*)(void))303, + /* 304 */ (void (*)(void))304, + /* 305 */ (void (*)(void))305, + /* 306 */ (void (*)(void))306, + /* 307 */ (void (*)(void))307, + /* 308 */ (void (*)(void))308, + /* 309 */ (void (*)(void))309, + /* 310 */ (void (*)(void))310, + /* 311 */ (void (*)(void))311, + /* 312 */ (void (*)(void))312, + /* 313 */ (void (*)(void))313, + /* 314 */ (void (*)(void))314, + /* 315 */ (void (*)(void))315, + /* 316 */ (void (*)(void))316, + /* 317 */ (void (*)(void))317, + /* 318 */ (void (*)(void))318, + /* 319 */ (void (*)(void))319, + /* 320 */ (void (*)(void))320, + /* 321 */ (void (*)(void))321, + /* 322 */ (void (*)(void))322, + /* 323 */ (void (*)(void))323, + /* 324 */ (void (*)(void))324, + /* 325 */ (void (*)(void))325, + /* 326 */ (void (*)(void))326, + /* 327 */ (void (*)(void))327, + /* 328 */ (void (*)(void))328, + /* 329 */ (void (*)(void))329, + /* 330 */ (void (*)(void))330, + /* 331 */ (void (*)(void))331, + /* 332 */ (void (*)(void))332, + /* 333 */ (void (*)(void))333, + /* 334 */ (void (*)(void))334, + /* 335 */ (void (*)(void))335, + /* 336 */ (void (*)(void))336, + /* 337 */ (void (*)(void))337, + /* 338 */ (void (*)(void))338, + /* 339 */ (void (*)(void))339, + /* 340 */ (void (*)(void))340, + /* 341 */ (void (*)(void))341, + /* 342 */ (void (*)(void))342, + /* 343 */ (void (*)(void))343, + /* 344 */ (void (*)(void))344, + /* 345 */ (void (*)(void))345, + /* 346 */ (void (*)(void))346, + /* 347 */ (void (*)(void))347, + /* 348 */ (void (*)(void))348, + /* 349 */ (void (*)(void))349, + /* 350 */ (void (*)(void))350, + /* 351 */ (void (*)(void))351, + /* 352 */ (void (*)(void))352, + /* 353 */ (void (*)(void))353, + /* 354 */ (void (*)(void))354, + /* 355 */ (void (*)(void))355, + /* 356 */ (void (*)(void))356, + /* 357 */ (void (*)(void))357, + /* 358 */ (void (*)(void))358, + /* 359 */ (void (*)(void))359, + /* 360 */ (void (*)(void))360, + /* 361 */ (void (*)(void))361, + /* 362 */ (void (*)(void))362, + /* 363 */ (void (*)(void))363, + /* 364 */ (void (*)(void))364, + /* 365 */ (void (*)(void))365, + /* 366 */ (void (*)(void))366, + /* 367 */ (void (*)(void))367, + /* 368 */ (void (*)(void))368, + /* 369 */ (void (*)(void))369, + /* 370 */ (void (*)(void))370, + /* 371 */ (void (*)(void))371, + /* 372 */ (void (*)(void))372, + /* 373 */ (void (*)(void))373, + /* 374 */ (void (*)(void))374, + /* 375 */ (void (*)(void))375, + /* 376 */ (void (*)(void))376, + /* 377 */ (void (*)(void))377, + /* 378 */ (void (*)(void))378, + /* 379 */ (void (*)(void))379, + /* 380 */ (void (*)(void))380, + /* 381 */ (void (*)(void))381, + /* 382 */ (void (*)(void))382, + /* 383 */ (void (*)(void))383, + /* 384 */ (void (*)(void))384, + /* 385 */ (void (*)(void))385, + /* 386 */ (void (*)(void))386, + /* 387 */ (void (*)(void))387, + /* 388 */ (void (*)(void))388, + /* 389 */ (void (*)(void))389, + /* 390 */ (void (*)(void))390, + /* 391 */ (void (*)(void))391, + /* 392 */ (void (*)(void))392, + /* 393 */ (void (*)(void))393, + /* 394 */ (void (*)(void))394, + /* 395 */ (void (*)(void))395, + /* 396 */ (void (*)(void))396, + /* 397 */ (void (*)(void))397, + /* 398 */ (void (*)(void))398, + /* 399 */ (void (*)(void))399, + /* 400 */ (void (*)(void))400, + /* 401 */ (void (*)(void))401, + /* 402 */ (void (*)(void))402, + /* 403 */ (void (*)(void))403, + /* 404 */ (void (*)(void))404, + /* 405 */ (void (*)(void))405, + /* 406 */ (void (*)(void))406, + /* 407 */ (void (*)(void))407, + /* 408 */ (void (*)(void))408, + /* 409 */ (void (*)(void))409, + /* 410 */ (void (*)(void))410, + /* 411 */ (void (*)(void))411, + /* 412 */ (void (*)(void))412, + /* 413 */ (void (*)(void))413, + /* 414 */ (void (*)(void))414, + /* 415 */ (void (*)(void))415, + /* 416 */ (void (*)(void))416, + /* 417 */ (void (*)(void))417, + /* 418 */ (void (*)(void))418, + /* 419 */ (void (*)(void))419, + /* 420 */ (void (*)(void))420, + /* 421 */ (void (*)(void))421, + /* 422 */ (void (*)(void))422, + /* 423 */ (void (*)(void))423, + /* 424 */ (void (*)(void))424, + /* 425 */ (void (*)(void))425, + /* 426 */ (void (*)(void))426, + /* 427 */ (void (*)(void))427, + /* 428 */ (void (*)(void))428, + /* 429 */ (void (*)(void))429, + /* 430 */ (void (*)(void))430, + /* 431 */ (void (*)(void))431, + /* 432 */ (void (*)(void))432, + /* 433 */ (void (*)(void))433, + /* 434 */ (void (*)(void))434, + /* 435 */ (void (*)(void))435, + /* 436 */ (void (*)(void))436, + /* 437 */ (void (*)(void))437, + /* 438 */ (void (*)(void))438, + /* 439 */ (void (*)(void))439, + /* 440 */ (void (*)(void))440, + /* 441 */ (void (*)(void))441, + /* 442 */ (void (*)(void))442, + /* 443 */ (void (*)(void))443, + /* 444 */ (void (*)(void))444, + /* 445 */ (void (*)(void))445, + /* 446 */ (void (*)(void))446, + /* 447 */ (void (*)(void))447, + /* 448 */ (void (*)(void))448, + /* 449 */ (void (*)(void))449, + /* 450 */ (void (*)(void))450, + /* 451 */ (void (*)(void))451, + /* 452 */ (void (*)(void))452, + /* 453 */ (void (*)(void))453, + /* 454 */ (void (*)(void))454, + /* 455 */ (void (*)(void))455, + /* 456 */ (void (*)(void))456, + /* 457 */ (void (*)(void))457, + /* 458 */ (void (*)(void))458, + /* 459 */ (void (*)(void))459, + /* 460 */ (void (*)(void))460, + /* 461 */ (void (*)(void))461, + /* 462 */ (void (*)(void))462, + /* 463 */ (void (*)(void))463, + /* 464 */ (void (*)(void))464, + /* 465 */ (void (*)(void))465, + /* 466 */ (void (*)(void))466, + /* 467 */ (void (*)(void))467, + /* 468 */ (void (*)(void))468, + /* 469 */ (void (*)(void))469, + /* 470 */ (void (*)(void))470, + /* 471 */ (void (*)(void))471, + /* 472 */ (void (*)(void))472, + /* 473 */ (void (*)(void))473, + /* 474 */ (void (*)(void))474, + /* 475 */ (void (*)(void))475, + /* 476 */ (void (*)(void))476, + /* 477 */ (void (*)(void))477, + /* 478 */ (void (*)(void))478, + /* 479 */ (void (*)(void))479, + /* 480 */ (void (*)(void))480, + /* 481 */ (void (*)(void))481, + /* 482 */ (void (*)(void))482, + /* 483 */ (void (*)(void))483, + /* 484 */ (void (*)(void))484, + /* 485 */ (void (*)(void))485, + /* 486 */ (void (*)(void))486, + /* 487 */ (void (*)(void))487, + /* 488 */ (void (*)(void))488, + /* 489 */ (void (*)(void))489, + /* 490 */ (void (*)(void))490, + /* 491 */ (void (*)(void))491, + /* 492 */ (void (*)(void))492, + /* 493 */ (void (*)(void))493, + /* 494 */ (void (*)(void))494, + /* 495 */ (void (*)(void))495, + /* 496 */ (void (*)(void))496, + /* 497 */ (void (*)(void))497, + /* 498 */ (void (*)(void))498, + /* 499 */ (void (*)(void))499, + /* 500 */ (void (*)(void))500, + /* 501 */ (void (*)(void))501, + /* 502 */ (void (*)(void))502, + /* 503 */ (void (*)(void))503, + /* 504 */ (void (*)(void))504, + /* 505 */ (void (*)(void))505, + /* 506 */ (void (*)(void))506, + /* 507 */ (void (*)(void))507, + /* 508 */ (void (*)(void))508, + /* 509 */ (void (*)(void))509, + /* 510 */ (void (*)(void))510, + /* 511 */ (void (*)(void))511, + /* 512 */ (void (*)(void))512, + /* 513 */ (void (*)(void))513, + /* 514 */ (void (*)(void))514, + /* 515 */ (void (*)(void))515, + /* 516 */ (void (*)(void))516, + /* 517 */ (void (*)(void))517, + /* 518 */ (void (*)(void))518, + /* 519 */ (void (*)(void))519, + /* 520 */ (void (*)(void))0, + /* 521 */ (void (*)(void))0, + /* 522 */ (void (*)(void))0, + /* 523 */ (void (*)(void))0, + /* 524 */ (void (*)(void))0, + /* 525 */ (void (*)(void))0, + /* 526 */ (void (*)(void))0, + /* 527 */ (void (*)(void))0, + /* 528 */ (void (*)(void))0, + /* 529 */ (void (*)(void))0, + /* 530 */ (void (*)(void))0, + /* 531 */ (void (*)(void))0, + /* 532 */ (void (*)(void))0, + /* 533 */ (void (*)(void))0, + /* 534 */ (void (*)(void))0, + /* 535 */ (void (*)(void))0, + /* 536 */ (void (*)(void))0, + /* 537 */ (void (*)(void))0, + /* 538 */ (void (*)(void))0, + /* 539 */ (void (*)(void))0, + /* 540 */ (void (*)(void))0, + /* 541 */ primitiveSmallFloatAdd, + /* 542 */ primitiveSmallFloatSubtract, + /* 543 */ primitiveSmallFloatLessThan, + /* 544 */ primitiveSmallFloatGreaterThan, + /* 545 */ primitiveSmallFloatLessOrEqual, + /* 546 */ primitiveSmallFloatGreaterOrEqual, + /* 547 */ primitiveSmallFloatEqual, + /* 548 */ primitiveSmallFloatNotEqual, + /* 549 */ primitiveSmallFloatMultiply, + /* 550 */ primitiveSmallFloatDivide, + /* 551 */ primitiveSmallFloatTruncated, + /* 552 */ primitiveSmallFloatFractionalPart, + /* 553 */ primitiveSmallFloatExponent, + /* 554 */ primitiveSmallFloatTimesTwoPower, + /* 555 */ primitiveSmallFloatSquareRoot, + /* 556 */ primitiveSmallFloatSine, + /* 557 */ primitiveSmallFloatArctan, + /* 558 */ primitiveSmallFloatLogN, + /* 559 */ primitiveSmallFloatExp, + /* 560 */ primitiveSmallFloatCosine, + /* 561 */ (void (*)(void))0, + /* 562 */ (void (*)(void))0, + /* 563 */ (void (*)(void))0, + /* 564 */ (void (*)(void))0, + /* 565 */ (void (*)(void))0, + /* 566 */ (void (*)(void))0, + /* 567 */ primitiveSetPriority, + /* 568 */ primitiveSuspendBackingUpV1, + /* 569 */ (void (*)(void))0, + /* 570 */ primitiveFlushExternalPrimitives, + /* 571 */ primitiveUnloadModule, + /* 572 */ primitiveListBuiltinModule, + /* 573 */ primitiveListExternalModule, + /* 574 */ (void (*)(void))0, + /* 575 */ primitiveHighBit, + /* 576 */ (void (*)(void))0, + /* 577 */ (void (*)(void))0, + /* 578 */ primitiveSuspendBackingUpV2, + /* 579 */ (void (*)(void))0, + /* 580 */ primitivePinnedNew, + /* 581 */ primitivePinnedNewWithArg, + /* 582 */ primitiveUninitializedNewWithArg, + /* 583 */ primitiveUninitializedPinnedNewWithArg, + 0 }; +static void (*externalPrimitiveTable[MaxExternalPrimitiveTableSize + 1 /* 4097 */])(void); +static signed short primitiveMetadataTable[MaxPrimitiveIndex + 2 /* 585 */] = { +// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 +/*0*/ -256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-256, +/*20*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x101, 0x101, +/*40*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 3, 3, 3, 3, +/*60*/ 1, 1, 0, 1, 1,-256,-256,-256, 0x100, 0x100, 0, 0, 0, 268, 0x100, 0, 0x100, 0, +/*78*/ 0, 0, +/*80*/ -256,-256,-256, 4, 4, 0, 0x100, 0, 0x200,-256,-256, 0, 0, 0, 0x100,-256, 0,-256, +/*98*/ 0, 0, +/*100*/ 260, 0x200, 0x200, 0x200,-256, 513,-256,-256,-256,-256, 0, 0x100, 0, 0,-256, +/*115*/ 0x100, 0, 12, 260, 0, +/*120*/ 524, 0x100,-256,-256, 1, 0, 0, 0, 0,-255,-256, 0, 0, 0, 0,-256, 0,-256,-256, +/*139*/ 0, +/*140*/ -256, 0x100,-256, 0x101, 0x101, 0x101,-256,-256, 12, 0, 3,-256,-256,-256,-256, +/*155*/ -256,-256,-256, 0x100, 0x100, +/*160*/ 0, 0,-256, 1, 1, 0x100, 0x100,-256, 0x101, 0, 0, 0,-256, 268, 0x100, 0,-256, +/*177*/ 0,-256,-256, +/*180*/ 0, 1, 1, 1, 0x100, 0x100, 0x100, 0x100, 0x200, 0x100,-256,-256,-256,-256,-256, +/*195*/ 0x100, 0x200, 0,-256,-256, +/*200*/ -256, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x100, 0x100, 0x100, 0x100, 0x100, +/*212*/ 0x100, 0,-256, 0, 0,-256, 772,-256, +/*220*/ 0, 0x200, 0x200,-256,-256,-256,-256,-256,-256,-256, 0,-256, 0x200, 0,-256,-256, +/*236*/ -256,-256,-253,-253, +/*240*/ -256,-256, 0,-256, 0x100,-256,-256,-256, 0, 0,-256, 0, 0x100, 0, 0,-256,-256, +/*257*/ -256,-256,-256, +/*260*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*276*/ -256,-256,-256,-256, +/*280*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*296*/ -256,-256,-256,-256, +/*300*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*316*/ -256,-256,-256,-256, +/*320*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*336*/ -256,-256,-256,-256, +/*340*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*356*/ -256,-256,-256,-256, +/*360*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*376*/ -256,-256,-256,-256, +/*380*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*396*/ -256,-256,-256,-256, +/*400*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*416*/ -256,-256,-256,-256, +/*420*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*436*/ -256,-256,-256,-256, +/*440*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*456*/ -256,-256,-256,-256, +/*460*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*476*/ -256,-256,-256,-256, +/*480*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*496*/ -256,-256,-256,-256, +/*500*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*516*/ -256,-256,-256,-256, +/*520*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*536*/ -256,-256,-256,-256, +/*540*/ -256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 1, 3, 3, 3, 3, 3, 3, +/*560*/ 3,-256,-256,-256,-256,-256,-256, 0x100, 0x200,-256,-244, 0x100, 0, 0,-256, 1, +/*576*/ -256,-256, 0x200,-256, +/*580*/ 0, 0, 0, 0 + }; +static char * traceSources[] = { + "?", "enterCritical", "exitCritical", "resume", "syncsig", "suspend", "wait", + "yield", "priority" + }; +sqInt imageHeaderFlags; +sqInt checkForLeaks; +sqInt maxLiteralCountForCompile = MaxLiteralCountForCompile /* 60 */; +sqInt debugCallbackPath; +sqInt breakLookupClassTag; +char * primTracePluginName; +sqInt breakSelectorLength = MinSmallInteger; +void * displayBits; +sqInt desiredEdenBytes; +sqInt desiredNumStackPages; +sqInt extraVMMemory; +sqInt eventTraceMask; +usqInt maxOldSpaceSize; +sqInt desiredCogCodeSize; +char * breakSelector; +sqInt ffiExceptionResponse; +usqInt heapBase; +sqInt inIOProcessEvents; +int (*showSurfaceFn)(sqIntptr_t, int, int, int, int); +sqInt deferDisplayUpdates; +struct VirtualMachine* interpreterProxy; +sqInt debugCallbackInvokes; +sqInt debugCallbackReturns; +sqInt suppressHeartbeatFlag; +sqInt cannotDeferDisplayUpdates; +sqInt checkedPluginName; +const char *interpreterVersion = "Open Smalltalk Cog[Spur] VM [LiveTypingCoInterpreter * VMMaker.oscog-eem.3776]"; +const char * leakCheckFlagsMeanings[] = { + "1: check full GC", "2: check new space GC", "4: check incremental GC", "8: check become", + "16: check image segment", "32: check free space", "64: check shorten", "128: check prim call", + null + }; +sqInt minBackwardJumpCountForCompile = MinBackwardJumpCountForCompile /* 40 */; +int displayWidth; +int displayDepth; +int displayHeight; +char expensiveAsserts = 0; +volatile int sendTrace; + + +/*** Macros ***/ +#define cPICNumCases stackCheckOffset +#define cPICNumCasesHack hack hack hack i.e. the getter macro does all the work +#define nextOpenPIC methodObject +#define nextOpenPICHack hack hack hack i.e. the getter macro does all the work +#define mostRecentlyUsedPage() GIV(mostRecentlyUsedPage) +#define stackPageAtpages(index,pages) ((pages) + (index)) +#define statMaxPageCountWhenMapping() GIV(statMaxPageCountWhenMapping) +#define compilationBreakpointclassTagisMNUCase(sel, classTag, isMNU) do { \ + if (numBytesOf(sel) == (isMNU ? -breakSelectorLength : breakSelectorLength) \ + && !strncmp((char *)((sel) + BaseHeaderSize), breakSelector, (isMNU ? -breakSelectorLength : breakSelectorLength))) { \ + suppressHeartbeatFlag = 1; \ + compilationBreakpointFor(sel); \ + } \ +} while (0) +#define compilationBreakpointisMNUCase(sel, isMNU) do { \ + if (numBytesOf(sel) == (isMNU ? -breakSelectorLength : breakSelectorLength) \ + && !strncmp((char *)((sel) + BaseHeaderSize), breakSelector, (isMNU ? -breakSelectorLength : breakSelectorLength))) { \ + suppressHeartbeatFlag = 1; \ + compilationBreakpointFor(sel); \ + } \ +} while (0) +#define flagInterpretedMethods() (imageHeaderFlags & 8) +#define getDesiredCogCodeSize() integerObjectOf(desiredCogCodeSize) +#define heapBase() heapBase +#define pageIndexForstackBasePlus1bytesPerPage(pointer,stkBasePlus1,pageByteSize) (((char *)(pointer) - (stkBasePlus1)) / (pageByteSize)) +#define nullHeaderForMachineCodeMethod() 0x8000000A000035LL +#define startOfMemory() heapBase +#define numTagBits() 3 +#define shiftForWord() 3 +#define smallFloatExponentBits() 8 +#define smallFloatExponentOffset() 896 +#define smallFloatMantissaBits() 52 +#define smallFloatTag() 4 +#define tagMask() 0x7 +#define wordSize() 8 +#define arrayClassIndexPun() 16 +#define arrayFormat() 2 +#define characterTag() 2 +#define classIndexMask() 0x3FFFFF +#define classIsItselfClassIndexPun() 31 +#define classTableMajorIndexShift() 10 +#define ephemeronFormat() 5 +#define extraAlignedBitByteShift() 6 +#define extraAlignedBitShift() 22 +#define firstByteFormat() 16 +#define firstClassIndexPun() 16 +#define firstCompiledMethodFormat() 24 +#define firstLongFormat() 10 +#define firstShortFormat() 12 +#define fixedFieldsFieldWidth() 16 +#define formatFieldByteOffset() 3 +#define formatFieldWidthShift() 5 +#define formatMask() 0x1F +#define formatShift() 24 +#define forwardedFormat() 7 +#define greyBitByteShift() 6 +#define greyBitShift() 54 +#define identityHashFullWordShift() 32 +#define identityHashHalfWordMask() 0x3FFFFF +#define immutableBitByteShift() 7 +#define immutableBitShift() 23 +#define immutableExtraBitsByteOffset() 2 +#define indexablePointersFormat() 3 +#define isForwardedObjectClassIndexPun() 8 +#define isFreeObjectClassIndexPun() 0 +#define lastClassIndexPun() 31 +#define markBitsByteOffset() 6 +#define markedBitByteShift() 7 +#define markedBitFullShift() 55 +#define markedBitHalfShift() 23 +#define nonIndexablePointerFormat() 1 +#define numSlotsFieldByteOffset() 7 +#define numSlotsFullShift() 56 +#define numSlotsHalfShift() 24 +#define numSlotsMask() 0xFF +#define pinnedBitByteShift() 6 +#define pinnedBitShift() 30 +#define rememberedBitByteShift() 5 +#define rememberedBitShift() 29 +#define rootTableCapacity() GIV(rememberedSetLimit) +#define rootTableCount() GIV(rememberedSetSize) +#define segmentBridgePun() 3 +#define sixtyFourBitIndexableFormat() 9 +#define sixtyFourBitLongsClassIndexPun() 19 +#define smallIntegerTag() 1 +#define thirtyTwoBitLongsClassIndexPun() 18 +#define unusedBitByteShift() 7 +#define unusedBitShift() 31 +#define weakArrayClassIndexPun() 17 +#define weakArrayFormat() 4 +#define alternateHeaderNumLiteralsMask() 0x7FFF +#define cr() fputc('\n',transcript) +#define dispatchFunctionPointer(aFunctionPointer) (aFunctionPointer)() +#define enterSmalltalkExecutive() enterSmalltalkExecutiveImplementation() +#define initialEnterSmalltalkExecutive() enterSmalltalkExecutiveImplementation() +#define numericPrimsMixArithmetic() (imageHeaderFlags & 0x100) +#define numericPrimsMixComparison() (imageHeaderFlags & 0x800) +#define primTraceLogIndex(aValue) (GIV(primTraceLogIndex) = (aValue)) +#define printChar(aCharacter) fputc(aCharacter,transcript) +#define remoteIsInstVarAccess() 128 +#define stackPageFrameBytes() (256 * BytesPerWord) +#define allocatype(numElements, elementType) alloca((numElements)*sizeof(elementType)) +#define numElementsIn(anArray) (sizeof(anArray)/sizeof(anArray[0])) +#define oopisGreaterThanOrEqualTo(anOop,otherOop) ((usqInt)(anOop) >= (usqInt)(otherOop)) +#define oopisGreaterThanOrEqualToandLessThanOrEqualTo(anOop,baseOop,limitOop) ((usqInt)(anOop) >= (usqInt)(baseOop) && (usqInt)(anOop) <= (usqInt)(limitOop)) +#define oopisGreaterThanOrEqualToandLessThan(anOop,baseOop,limitOop) ((usqInt)(anOop) >= (usqInt)(baseOop) && (usqInt)(anOop) < (usqInt)(limitOop)) +#define oopisGreaterThan(anOop,otherOop) ((usqInt)(anOop) > (usqInt)(otherOop)) +#define oopisGreaterThanandLessThan(anOop,baseOop,limitOop) ((usqInt)(anOop) > (usqInt)(baseOop) && (usqInt)(anOop) < (usqInt)(limitOop)) +#define oopisLessThanOrEqualTo(anOop,otherOop) ((usqInt)(anOop) <= (usqInt)(otherOop)) +#define oopisLessThan(anOop,otherOop) ((usqInt)(anOop) < (usqInt)(otherOop)) + + +/*** Methods ***/ + + +/* This is the main interpreter loop. + In a pure interpreter it loops forever, fetching and executing bytecodes. + With the Cogit JIT executing code as well, the interpreter is reentered + from machine code + whenever the machine code wants to interpret a method instead of executing + its machine + code. Entry into the interpreter is done via a ''jump call'' in machine + code that uses + CFramePointer and CStackPointer to find the base of the C stack (set in + CoInterpreter>> enterSmalltalkExecutiveImplementation) and substitutes + CReturnAddress as the return + address in the code so it always appears that interpret has been called + from CoInterpreter>>enterSmalltalkExecutiveImplementation, which may be + important to, + for example, C exception handling inside the VM. + + When running in the context of a browser plugin VM the interpreter must + return control + to the browser periodically. This should done only when the state of the + currently running + Squeak thread is safely stored in the object heap. Since this is the case + at the moment + that a check for interrupts is performed, that is when we return to the + browser if it is time + to do so. Interrupt checks happen quite frequently. */ +/* If stacklimit is zero then the stack pages have not been initialized. */ + + /* CoInterpreter>>#interpret */ +sqInt +interpret(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + sqInt currentBytecode; + sqInt extA; + sqInt extB; + sqInt header; + sqInt lkupClassTag; + char * localFP; + char * localIP; + sqInt localReturnValue; + char * localSP; + sqInt methodHeader; + sqInt numExtB; + sqInt stackPageBytes; + usqIntptr_t stackZoneBytes; + void *theStackMemory; + + if (!GIV(stackLimit)) { + /* begin initStackPagesAndInterpret */ + /* begin ensureInitializeStackZone */ + if (!GIV(pages)) { + stackPageBytes = stackPageByteSize(); + stackZoneBytes = (GIV(numStackPages) * ((sizeof(CogStackPage)) + (stackPageByteSize()))) + BytesPerWord; + theStackMemory = alloca(stackZoneBytes); + memset(theStackMemory, 0, stackZoneBytes); + initializeStacknumSlotspageSize(theStackMemory, stackZoneBytes / BytesPerWord, stackPageBytes / BytesPerWord); + } + + /* Once the stack pages are initialized we can continue to bootstrap the system. */ + loadInitialContext(); + + /* We're ready for the heartbeat (poll interrupt) */ + ioInitHeartbeat(); + initialEnterSmalltalkExecutive(); + return null; + } + assertCStackWellAligned(); + + /* An unchecked write is probably faster, so instead of + CReturnAddress ifNil: + [CReturnAddress := self cCoerceSimple: self getReturnAddress to: #usqIntptr_t] + we have simply */ + assert((!GIV(CReturnAddress)) + || (GIV(CReturnAddress) == (((usqIntptr_t) (getReturnAddress()))))); + GIV(CReturnAddress) = ((usqIntptr_t) (getReturnAddress())); + + /* record entry time when running as a browser plug-in */ + browserPluginInitialiseIfNeeded(); + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + assert(!((GIV(instructionPointer) == (ceReturnToInterpreterPC())))); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 1, __LINE__); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + + /* begin initExtensions */ + extA = (numExtB = (extB = 0)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + while (1) { + /* begin dispatchBytecode */ + bytecodeDispatchDebugHook(); + + VM_LABEL(bytecodeDispatch); + switch (currentBytecode) { + case 0: // pushReceiverVariableBytecode + case 256: // 0 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize))); + } + break; + case 1: // pushReceiverVariableBytecode + case 257: // 1 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode1); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 8 /* (1 << shiftForWord) */))); + } + break; + case 2: // pushReceiverVariableBytecode + case 258: // 2 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode2); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 16 /* (2 << shiftForWord) */))); + } + break; + case 3: // pushReceiverVariableBytecode + case 259: // 3 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode3); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 24 /* (3 << shiftForWord) */))); + } + break; + case 4: // pushReceiverVariableBytecode + case 260: // 4 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode4); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 32 /* (4 << shiftForWord) */))); + } + break; + case 5: // pushReceiverVariableBytecode + case 261: // 5 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode5); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 40 /* (5 << shiftForWord) */))); + } + break; + case 6: // pushReceiverVariableBytecode + case 262: // 6 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode6); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 48 /* (6 << shiftForWord) */))); + } + break; + case 7: // pushReceiverVariableBytecode + case 263: // 7 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode7); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 56 /* (7 << shiftForWord) */))); + } + break; + case 8: // pushReceiverVariableBytecode + case 264: // 8 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode8); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 64 /* (8 << shiftForWord) */))); + } + break; + case 9: // pushReceiverVariableBytecode + case 265: // 9 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode9); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 72 /* (9 << shiftForWord) */))); + } + break; + case 10: // pushReceiverVariableBytecode + case 266: // 10 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode10); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 80 /* (10 << shiftForWord) */))); + } + break; + case 11: // pushReceiverVariableBytecode + case 267: // 11 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode11); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 88 /* (11 << shiftForWord) */))); + } + break; + case 12: // pushReceiverVariableBytecode + case 268: // 12 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode12); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 96 /* (12 << shiftForWord) */))); + } + break; + case 13: // pushReceiverVariableBytecode + case 269: // 13 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode13); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 104 /* (13 << shiftForWord) */))); + } + break; + case 14: // pushReceiverVariableBytecode + case 270: // 14 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode14); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 112 /* (14 << shiftForWord) */))); + } + break; + case 15: // pushReceiverVariableBytecode + case 271: // 15 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode15); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 120 /* (15 << shiftForWord) */))); + } + break; + case 16: // pushTemporaryVariableBytecode + case 320: // 64 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (0 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + (frameNumArgs * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + (frameNumArgs * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 17: // pushTemporaryVariableBytecode + case 321: // 65 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode1); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (1 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 1) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 1) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 18: // pushTemporaryVariableBytecode + case 322: // 66 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode2); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (2 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 2) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 2) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 19: // pushTemporaryVariableBytecode + case 323: // 67 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode3); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (3 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 3) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 3) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 20: // pushTemporaryVariableBytecode + case 324: // 68 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode4); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (4 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 4) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 4) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 21: // pushTemporaryVariableBytecode + case 325: // 69 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode5); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (5 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 5) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 5) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 22: // pushTemporaryVariableBytecode + case 326: // 70 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode6); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (6 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 6) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 6) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 23: // pushTemporaryVariableBytecode + case 327: // 71 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode7); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (7 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 7) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 7) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 24: // pushTemporaryVariableBytecode + case 328: // 72 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode8); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (8 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 8) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 8) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 25: // pushTemporaryVariableBytecode + case 329: // 73 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode9); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (9 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 9) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 9) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 26: // pushTemporaryVariableBytecode + case 330: // 74 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode10); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (10 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 10) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 10) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 27: // pushTemporaryVariableBytecode + case 331: // 75 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode11); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (11 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 11) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 11) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 28: // pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode12); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (12 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 12) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 12) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 29: // pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode13); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (13 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 13) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 13) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 30: // pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode14); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (14 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 14) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 14) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 31: // pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode15); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (15 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 15) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 15) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 32: // pushLiteralConstantBytecode + case 288: // 32 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 8 /* ((0 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 33: // pushLiteralConstantBytecode + case 289: // 33 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode1); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 16 /* ((1 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 34: // pushLiteralConstantBytecode + case 290: // 34 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode2); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 24 /* ((2 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 35: // pushLiteralConstantBytecode + case 291: // 35 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode3); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 32 /* ((3 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 36: // pushLiteralConstantBytecode + case 292: // 36 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode4); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 40 /* ((4 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 37: // pushLiteralConstantBytecode + case 293: // 37 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode5); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 48 /* ((5 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 38: // pushLiteralConstantBytecode + case 294: // 38 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode6); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 56 /* ((6 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 39: // pushLiteralConstantBytecode + case 295: // 39 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode7); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 64 /* ((7 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 40: // pushLiteralConstantBytecode + case 296: // 40 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode8); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 72 /* ((8 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 41: // pushLiteralConstantBytecode + case 297: // 41 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode9); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 80 /* ((9 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 42: // pushLiteralConstantBytecode + case 298: // 42 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode10); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 88 /* ((10 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 43: // pushLiteralConstantBytecode + case 299: // 43 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode11); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 96 /* ((11 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 44: // pushLiteralConstantBytecode + case 300: // 44 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode12); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 104 /* ((12 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 45: // pushLiteralConstantBytecode + case 301: // 45 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode13); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 112 /* ((13 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 46: // pushLiteralConstantBytecode + case 302: // 46 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode14); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 120 /* ((14 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 47: // pushLiteralConstantBytecode + case 303: // 47 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode15); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 128 /* ((15 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 48: // pushLiteralConstantBytecode + case 304: // 48 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode16); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 136 /* ((16 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 49: // pushLiteralConstantBytecode + case 305: // 49 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode17); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 144 /* ((17 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 50: // pushLiteralConstantBytecode + case 306: // 50 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode18); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 152 /* ((18 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 51: // pushLiteralConstantBytecode + case 307: // 51 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode19); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 160 /* ((19 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 52: // pushLiteralConstantBytecode + case 308: // 52 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode20); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 168 /* ((20 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 53: // pushLiteralConstantBytecode + case 309: // 53 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode21); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 176 /* ((21 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 54: // pushLiteralConstantBytecode + case 310: // 54 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode22); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 184 /* ((22 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 55: // pushLiteralConstantBytecode + case 311: // 55 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode23); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 192 /* ((23 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 56: // pushLiteralConstantBytecode + case 312: // 56 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode24); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 200 /* ((24 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 57: // pushLiteralConstantBytecode + case 313: // 57 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode25); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 208 /* ((25 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 58: // pushLiteralConstantBytecode + case 314: // 58 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode26); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 216 /* ((26 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 59: // pushLiteralConstantBytecode + case 315: // 59 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode27); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 224 /* ((27 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 60: // pushLiteralConstantBytecode + case 316: // 60 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode28); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 232 /* ((28 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 61: // pushLiteralConstantBytecode + case 317: // 61 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode29); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 240 /* ((29 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 62: // pushLiteralConstantBytecode + case 318: // 62 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode30); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 0xF8 /* ((30 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 63: // pushLiteralConstantBytecode + case 319: // 63 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode31); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 0x100 /* ((31 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 64: // pushLiteralVariableBytecode + case 272: // 16 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 8 /* ((0 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(1 /* (0 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 65: // pushLiteralVariableBytecode + case 273: // 17 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode1); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 16 /* ((1 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(2 /* (1 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 66: // pushLiteralVariableBytecode + case 274: // 18 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode2); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 24 /* ((2 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(3 /* (2 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 67: // pushLiteralVariableBytecode + case 275: // 19 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode3); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 32 /* ((3 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(4 /* (3 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 68: // pushLiteralVariableBytecode + case 276: // 20 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode4); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 40 /* ((4 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(5 /* (4 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 69: // pushLiteralVariableBytecode + case 277: // 21 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode5); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 48 /* ((5 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(6 /* (5 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 70: // pushLiteralVariableBytecode + case 278: // 22 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode6); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 56 /* ((6 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(7 /* (6 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 71: // pushLiteralVariableBytecode + case 279: // 23 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode7); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 64 /* ((7 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(8 /* (7 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 72: // pushLiteralVariableBytecode + case 280: // 24 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode8); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 72 /* ((8 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(9 /* (8 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 73: // pushLiteralVariableBytecode + case 281: // 25 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode9); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 80 /* ((9 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(10 /* (9 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 74: // pushLiteralVariableBytecode + case 282: // 26 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode10); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 88 /* ((10 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(11 /* (10 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 75: // pushLiteralVariableBytecode + case 283: // 27 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode11); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 96 /* ((11 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(12 /* (11 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 76: // pushLiteralVariableBytecode + case 284: // 28 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode12); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 104 /* ((12 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(13 /* (12 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 77: // pushLiteralVariableBytecode + case 285: // 29 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode13); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 112 /* ((13 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(14 /* (13 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 78: // pushLiteralVariableBytecode + case 286: // 30 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode14); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 120 /* ((14 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(15 /* (14 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 79: // pushLiteralVariableBytecode + case 287: // 31 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode15); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 128 /* ((15 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(16 /* (15 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 80: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode16); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 136 /* ((16 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(17 /* (16 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 81: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode17); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 144 /* ((17 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(18 /* (17 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 82: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode18); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 152 /* ((18 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(19 /* (18 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 83: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode19); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 160 /* ((19 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(20 /* (19 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 84: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode20); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 168 /* ((20 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(21 /* (20 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 85: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode21); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 176 /* ((21 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(22 /* (21 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 86: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode22); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 184 /* ((22 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(23 /* (22 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 87: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode23); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 192 /* ((23 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(24 /* (23 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 88: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode24); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 200 /* ((24 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(25 /* (24 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 89: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode25); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 208 /* ((25 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(26 /* (25 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 90: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode26); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 216 /* ((26 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(27 /* (26 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 91: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode27); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 224 /* ((27 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(28 /* (27 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 92: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode28); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 232 /* ((28 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(29 /* (28 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 93: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode29); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 240 /* ((29 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(30 /* (29 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 94: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode30); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 0xF8 /* ((30 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(0x1F /* (30 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 95: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode31); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 0x100 /* ((31 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(32 /* (31 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 96: // storeAndPopReceiverVariableBytecode + case 97: // storeAndPopReceiverVariableBytecode + case 98: // storeAndPopReceiverVariableBytecode + case 99: // storeAndPopReceiverVariableBytecode + case 100: // storeAndPopReceiverVariableBytecode + case 101: // storeAndPopReceiverVariableBytecode + case 102: // storeAndPopReceiverVariableBytecode + case 103: // storeAndPopReceiverVariableBytecode + case 456: // 200 storeAndPopReceiverVariableBytecode + case 457: // 201 storeAndPopReceiverVariableBytecode + case 458: // 202 storeAndPopReceiverVariableBytecode + case 459: // 203 storeAndPopReceiverVariableBytecode + case 460: // 204 storeAndPopReceiverVariableBytecode + case 461: // 205 storeAndPopReceiverVariableBytecode + case 462: // 206 storeAndPopReceiverVariableBytecode + case 463: // 207 storeAndPopReceiverVariableBytecode + { + sqInt fmt; + sqInt index; + usqInt instVarIndex; + sqInt instVarTypesArrays; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt rcvr; + sqInt rcvrClass; + sqInt tagBits; + sqInt top; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + VM_LABEL(storeAndPopReceiverVariableBytecode); + /* begin collectInstanceVariableTypeFor:at:of: */ + rcvrClass = /* fetchClassOf: */ + ((tagBits = (longAt(localFP + FoxIFReceiver)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(localFP + FoxIFReceiver))); + instVarTypesArrays = /* #instVarTypesArraysOf: #safeFollowObjField:ofObject: */ + (InstVarTypesArraysIndex < (lengthOf(rcvrClass)) + ? followObjFieldofObject(InstVarTypesArraysIndex, rcvrClass) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (instVarTypesArrays) + && (isInstanceOfClassArray(instVarTypesArrays)))) { + goto l3; + } + typesArray = /* safeFollowObjField:ofObject: */ + ((currentBytecode & 7) < (lengthOf(instVarTypesArrays)) + ? followObjFieldofObject(currentBytecode & 7, instVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l4; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l4; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l4; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l4; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l4: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2: + /* end collectTypeOf:in:at: */ +l3: + rcvr = longAt(localFP + FoxIFReceiver); + top = longAt(localSP); + instVarIndex = currentBytecode & 7; + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; +# if IMMUTABILITY + assert(!((isImmediate(rcvr)))); + if ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),rcvr); + longAtput((localSP -= BytesPerOop),top); + longAtput((localSP -= BytesPerOop),(((instVarIndex + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l1; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(instVarIndex, rcvr, top)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(top & (tagMask())))) + && (oopisLessThan(top, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((instVarIndex << (shiftForWord())))),top); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l1: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 104: // storeAndPopTemporaryVariableBytecode + case 464: // 208 storeAndPopTemporaryVariableBytecode + { + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + VM_LABEL(storeAndPopTemporaryVariableBytecode); + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 3)) != 0))) { + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l152; + } + typesArray = /* safeFollowObjField:ofObject: */ + (0 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(0, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l154; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << 3 /* shiftForWord */))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l153; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << 3 /* shiftForWord */)) - (fmt & 7); + goto l153; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << 2 /* (shiftForWord - 1) */)) - (fmt & 3); + goto l153; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << 1 /* (shiftForWord - 2) */)) - (fmt & 1); + goto l153; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l153; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l153: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l154; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */)))),objectClass); + goto l154; + } + } + /* end collectTypeOf:in: */ +l154: + /* end collectTypeOf:in:at: */ +l152:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (0 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + (frameNumArgs * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + (frameNumArgs * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + break; + case 105: // storeAndPopTemporaryVariableBytecode + case 465: // 209 storeAndPopTemporaryVariableBytecode + { + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + VM_LABEL(storeAndPopTemporaryVariableBytecode1); + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 3)) != 0))) { + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l155; + } + typesArray = /* safeFollowObjField:ofObject: */ + (1 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(1, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l157; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << 3 /* shiftForWord */))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l156; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << 3 /* shiftForWord */)) - (fmt & 7); + goto l156; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << 2 /* (shiftForWord - 1) */)) - (fmt & 3); + goto l156; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << 1 /* (shiftForWord - 2) */)) - (fmt & 1); + goto l156; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l156; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l156: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l157; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */)))),objectClass); + goto l157; + } + } + /* end collectTypeOf:in: */ +l157: + /* end collectTypeOf:in:at: */ +l155:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (1 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 1) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 1) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + break; + case 106: // storeAndPopTemporaryVariableBytecode + case 466: // 210 storeAndPopTemporaryVariableBytecode + { + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + VM_LABEL(storeAndPopTemporaryVariableBytecode2); + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 3)) != 0))) { + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l158; + } + typesArray = /* safeFollowObjField:ofObject: */ + (2 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(2, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l160; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << 3 /* shiftForWord */))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l159; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << 3 /* shiftForWord */)) - (fmt & 7); + goto l159; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << 2 /* (shiftForWord - 1) */)) - (fmt & 3); + goto l159; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << 1 /* (shiftForWord - 2) */)) - (fmt & 1); + goto l159; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l159; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l159: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l160; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */)))),objectClass); + goto l160; + } + } + /* end collectTypeOf:in: */ +l160: + /* end collectTypeOf:in:at: */ +l158:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (2 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 2) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 2) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + break; + case 107: // storeAndPopTemporaryVariableBytecode + case 467: // 211 storeAndPopTemporaryVariableBytecode + { + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + VM_LABEL(storeAndPopTemporaryVariableBytecode3); + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 3)) != 0))) { + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l161; + } + typesArray = /* safeFollowObjField:ofObject: */ + (3 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(3, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l163; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << 3 /* shiftForWord */))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l162; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << 3 /* shiftForWord */)) - (fmt & 7); + goto l162; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << 2 /* (shiftForWord - 1) */)) - (fmt & 3); + goto l162; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << 1 /* (shiftForWord - 2) */)) - (fmt & 1); + goto l162; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l162; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l162: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l163; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */)))),objectClass); + goto l163; + } + } + /* end collectTypeOf:in: */ +l163: + /* end collectTypeOf:in:at: */ +l161:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (3 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 3) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 3) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + break; + case 108: // storeAndPopTemporaryVariableBytecode + case 468: // 212 storeAndPopTemporaryVariableBytecode + { + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + VM_LABEL(storeAndPopTemporaryVariableBytecode4); + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 3)) != 0))) { + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l164; + } + typesArray = /* safeFollowObjField:ofObject: */ + (4 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(4, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l166; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << 3 /* shiftForWord */))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l165; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << 3 /* shiftForWord */)) - (fmt & 7); + goto l165; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << 2 /* (shiftForWord - 1) */)) - (fmt & 3); + goto l165; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << 1 /* (shiftForWord - 2) */)) - (fmt & 1); + goto l165; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l165; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l165: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l166; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */)))),objectClass); + goto l166; + } + } + /* end collectTypeOf:in: */ +l166: + /* end collectTypeOf:in:at: */ +l164:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (4 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 4) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 4) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + break; + case 109: // storeAndPopTemporaryVariableBytecode + case 469: // 213 storeAndPopTemporaryVariableBytecode + { + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + VM_LABEL(storeAndPopTemporaryVariableBytecode5); + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 3)) != 0))) { + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l167; + } + typesArray = /* safeFollowObjField:ofObject: */ + (5 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(5, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l169; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << 3 /* shiftForWord */))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l168; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << 3 /* shiftForWord */)) - (fmt & 7); + goto l168; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << 2 /* (shiftForWord - 1) */)) - (fmt & 3); + goto l168; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << 1 /* (shiftForWord - 2) */)) - (fmt & 1); + goto l168; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l168; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l168: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l169; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */)))),objectClass); + goto l169; + } + } + /* end collectTypeOf:in: */ +l169: + /* end collectTypeOf:in:at: */ +l167:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (5 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 5) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 5) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + break; + case 110: // storeAndPopTemporaryVariableBytecode + case 470: // 214 storeAndPopTemporaryVariableBytecode + { + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + VM_LABEL(storeAndPopTemporaryVariableBytecode6); + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 3)) != 0))) { + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l170; + } + typesArray = /* safeFollowObjField:ofObject: */ + (6 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(6, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l172; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << 3 /* shiftForWord */))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l171; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << 3 /* shiftForWord */)) - (fmt & 7); + goto l171; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << 2 /* (shiftForWord - 1) */)) - (fmt & 3); + goto l171; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << 1 /* (shiftForWord - 2) */)) - (fmt & 1); + goto l171; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l171; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l171: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l172; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */)))),objectClass); + goto l172; + } + } + /* end collectTypeOf:in: */ +l172: + /* end collectTypeOf:in:at: */ +l170:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (6 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 6) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 6) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + break; + case 111: // storeAndPopTemporaryVariableBytecode + case 471: // 215 storeAndPopTemporaryVariableBytecode + { + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + VM_LABEL(storeAndPopTemporaryVariableBytecode7); + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 3)) != 0))) { + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l173; + } + typesArray = /* safeFollowObjField:ofObject: */ + (7 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(7, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l175; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << 3 /* shiftForWord */))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l174; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << 3 /* shiftForWord */)) - (fmt & 7); + goto l174; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << 2 /* (shiftForWord - 1) */)) - (fmt & 3); + goto l174; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << 1 /* (shiftForWord - 2) */)) - (fmt & 1); + goto l174; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l174; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l174: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l175; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */)))),objectClass); + goto l175; + } + } + /* end collectTypeOf:in: */ +l175: + /* end collectTypeOf:in:at: */ +l173:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (7 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 7) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 7) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + break; + case 112: // pushReceiverBytecode + case 332: // 76 pushReceiverBytecode + { + VM_LABEL(pushReceiverBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt(localFP + FoxIFReceiver)); + } + break; + case 113: // pushConstantTrueBytecode + case 333: // 77 pushConstantTrueBytecode + { + VM_LABEL(pushConstantTrueBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),GIV(trueObj)); + } + break; + case 114: // pushConstantFalseBytecode + case 334: // 78 pushConstantFalseBytecode + { + VM_LABEL(pushConstantFalseBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),GIV(falseObj)); + } + break; + case 115: // pushConstantNilBytecode + case 335: // 79 pushConstantNilBytecode + { + VM_LABEL(pushConstantNilBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),GIV(nilObj)); + } + break; + case 116: // pushConstantMinusOneBytecode + { + VM_LABEL(pushConstantMinusOneBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),ConstMinusOne); + } + break; + case 117: // pushConstantZeroBytecode + case 336: // 80 pushConstantZeroBytecode + { + VM_LABEL(pushConstantZeroBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),ConstZero); + } + break; + case 118: // pushConstantOneBytecode + case 337: // 81 pushConstantOneBytecode + { + VM_LABEL(pushConstantOneBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),ConstOne); + } + break; + case 119: // pushConstantTwoBytecode + { + VM_LABEL(pushConstantTwoBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),ConstTwo); + } + break; + case 120: // returnReceiver + { + VM_LABEL(returnReceiver); + localReturnValue = longAt(localFP + FoxIFReceiver); + + /* goto commonReturn */ + } + + commonReturn: + /* commonReturn */ + { + sqInt additionalMethodState; + sqInt aMethodObj; + char *callerFP; + sqInt closure; + sqInt contextToReturnTo; + sqInt currentCtx; + sqInt fmt; + sqInt frameMethod; + char *frameToReturnTo; + sqInt header; + sqInt home; + sqInt index; + sqInt methodHeader; + StackPage *newPage; + sqInt nextCntx; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt ourContext; + sqInt returnTypesArray; + sqInt selectorOrProperties; + sqInt senderOop; + sqInt tagBits; + char *theFP; + char *theFP1; + StackPage *thePage; + StackPage *thePage1; + sqInt typeAtIndex; + sqInt typesArraySize; + sqInt unwindContextOrNilOrZero; + + VM_LABEL(commonReturn); + frameToReturnTo = ((char *) 0); + theFP = ((char *) 0); + + /* begin collectReturnTypeForFrame:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + returnTypesArray = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (ReturnTypesArrayIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(ReturnTypesArrayIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (returnTypesArray) + && (isInstanceOfClassArray(returnTypesArray)))) { + goto l177; + } + objectClass = /* fetchClassOf: */ + ((tagBits = localReturnValue & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(localReturnValue)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(returnTypesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(returnTypesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(returnTypesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l181; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l181; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l181; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l181; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l181; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l181: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((returnTypesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, returnTypesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l177; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, returnTypesArray, objectClass)); + assert(isNonImmediate(returnTypesArray)); + if (oopisGreaterThanOrEqualTo(returnTypesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(returnTypesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(returnTypesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((returnTypesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l177; + } + } + /* end collectTypeOf:in: */ +l177: + if (!(byteAt((localFP + FoxIFrameFlags) + 3))) { + goto commonCallerReturn; + goto l176; + } + + /* Update the current page's headFrame pointers to enable the search for unwind protects below + to identify widowed contexts correctly. */ + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* Since this is a block activation the closure is on the stack above any args and the frame. */ + closure = longAt(localFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(localFP))->cmNumArgs) + : byteAt((localFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))); + + /* avoid compiler warning */ + /* Walk the closure's lexical chain to find the context or frame to return from (home). + If home is missing (Sista closures) then throw cannotReturn rather than crash. */ + home = null; + while (closure != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + home = longAt((void *)((closure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + assert(isNonImmediate(home)); + if ((!((longAt((void *)(home))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + home = fixFollowedFieldofObjectwithInitialValue(ClosureOuterContextIndex, closure, home); + } + if (!(/* isContext: */ + ((!(home & (tagMask())))) + && (((longAt((void *)(home))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* begin internalCannotReturn: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l179; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l179: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),ourContext); + longAtput((localSP -= BytesPerOop),localReturnValue); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))); + GIV(argumentCount) = 1; + goto normalSend; + goto l176; + } + + /* begin followObjField:ofObject: */ + closure = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord())))))); + assert(isNonImmediate(closure)); + if ((!((longAt((void *)(closure))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + closure = fixFollowedFieldofObjectwithInitialValue(ClosureIndex, home, closure); + } + } + + /* home is to be returned from provided there is no unwind-protect activation between + this frame and home's sender. Search for an unwind. findUnwindThroughContext: + will answer either the context for an unwind-protect activation or nilObj if the sender + cannot be found or 0 if no unwind is found but the sender is. */ + unwindContextOrNilOrZero = findUnwindThroughContext(home); + if (unwindContextOrNilOrZero == GIV(nilObj)) { + /* error: can't find home on chain; cannot return */ + + /* begin internalCannotReturn: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l182; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l182: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),ourContext); + longAtput((localSP -= BytesPerOop),localReturnValue); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))); + GIV(argumentCount) = 1; + goto normalSend; + goto l176; + } + if (unwindContextOrNilOrZero) { + /* begin internalAboutToReturn:through: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l183; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l183: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),ourContext); + longAtput((localSP -= BytesPerOop),localReturnValue); + longAtput((localSP -= BytesPerOop),unwindContextOrNilOrZero); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAboutToReturn) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l176; + } + + /* Now we know home is on the sender chain. + We could be returning to either a context or a frame. Find out which. */ + contextToReturnTo = null; + if (((((longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert(checkIsStillMarriedContextcurrentFP(home, localFP)); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (longAt(theFP + FoxSavedFP)) { + frameToReturnTo = ((char *)(longAt(theFP + FoxSavedFP))); + } + else { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + contextToReturnTo = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(contextToReturnTo)); + assert((contextToReturnTo == (nilObject())) + || (isContext(followMaybeForwarded(contextToReturnTo)))); + } + } + else { + contextToReturnTo = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + if ((/* isContext: */ + ((!(contextToReturnTo & (tagMask())))) + && (((longAt((void *)(contextToReturnTo))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + assert(checkIsStillMarriedContextcurrentFP(contextToReturnTo, localFP)); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + frameToReturnTo = ((char *)(senderOop - (smallIntegerTag()))); + contextToReturnTo = null; + } + } + + /* If returning to a context we must make a frame for it unless it is dead. */ + if (contextToReturnTo) { + frameToReturnTo = establishFrameForContextToReturnTo(contextToReturnTo); + if (!frameToReturnTo) { + /* begin internalCannotReturn: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l178; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l178: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),ourContext); + longAtput((localSP -= BytesPerOop),localReturnValue); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))); + GIV(argumentCount) = 1; + goto normalSend; + goto l176; + } + } + + /* Now we have a frame to return to. If it is on a different page we must free intervening pages and + nil out intervening contexts. We must free intervening stack pages because if we leave the pages + to be divorced then their contexts will be divorced with intact senders and instruction pointers. This + code is similar to primitiveTerminateTo. We must move any frames on intervening pages above the + frame linked to because these may be in use, e.g. via co-routining (see baseFrameReturn). */ + assert(pageListIsWellFormed()); + + /* begin stackPageFor: */ + newPage = stackPageAtpages(pageIndexFor(frameToReturnTo), GIV(pages)); + if (newPage != GIV(stackPage)) { + theFP1 = (GIV(stackPage)->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP1)); + thePage1 = stackPageAtpages(pageIndexFor(theFP1), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + freeStackPage(GIV(stackPage)); + while (1) { + assert(isContext(currentCtx)); + if ((((((longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && ((stackPageFor((theFP = frameOfMarriedContext(currentCtx)))) == newPage)) break; + if (((((longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (theFP != ((thePage->headFP))) { + /* Since we've just deallocated a page we know that newStackPage won't deallocate an existing one. */ + moveFramesInthroughtoPage(thePage, findFrameAboveinPage(theFP, thePage), newStackPage()); + } + theFP1 = (thePage->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP1)); + thePage1 = stackPageAtpages(pageIndexFor(theFP1), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + freeStackPage(thePage); + } + else { + nextCntx = longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + + /* begin markContextAsDead: */ + assert(isContext(currentCtx)); + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(SenderIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + currentCtx = nextCntx; + } + } + + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + localSP = (GIV(stackPage)->headSP); + localFP = (GIV(stackPage)->headFP); + } + + /* Two cases. Returning to the top frame on a new page or an interior frame on the current page. + The top frame has its instruction pointer on top of stack. An interior frame has its instruction pointer + in the caller frame. We need to peel back any frames on the page until we get to the correct frame. */ + if (localFP == frameToReturnTo) { + localIP = ((char *)(longAt(localSP))); + } + else { + do { + callerFP = localFP; + localFP = ((char *)(longAt(localFP + FoxSavedFP))); + } while(localFP != frameToReturnTo); + localIP = ((char *)(longAt(callerFP + FoxCallerSavedIP))); + localSP = ((/* begin frameCallerSP: */ + assert(!(isBaseFrame(callerFP))), +(callerFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(callerFP))->cmNumArgs) + : byteAt((callerFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)) - BytesPerWord; + } + + /* pop the saved IP, push the return value and continue. */ + + /* begin maybeReturnToMachineCodeFrame */ + if ((((usqInt)localIP)) < (startOfMemory())) { + if ((((usqInt)localIP)) != (ceReturnToInterpreterPC())) { + /* localIP in the cog method zone indicates a return to machine code. */ + + /* begin returnToMachineCodeFrame */ + assertCStackWellAligned(); + assert((((usqInt)localIP)) < (startOfMemory())); + assert(isMachineCodeFrame(localFP)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 0, __LINE__); + assert(isPCWithinMethodZone(((usqInt)localIP))); + + /* internalStackTopPut: */ + longAtput(localSP,((usqInt)localIP)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),localReturnValue); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + ceEnterCogCodePopReceiverReg(); + goto l180; + } + localIP = ((char *)(((usqInt)(longAt(localFP + FoxIFSavedIP))))); + } + /* end maybeReturnToMachineCodeFrame */ +l180: + aMethodObj = longAt(localFP + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* internalStackTopPut: */ + longAtput(localSP,localReturnValue); + } + /* end case */ +l176:; + break; + case 121: // returnTrue + case 345: // 89 returnTrue + { + VM_LABEL(returnTrue); + localReturnValue = GIV(trueObj); + goto commonReturn; + } + break; + case 122: // returnFalse + case 346: // 90 returnFalse + { + VM_LABEL(returnFalse); + localReturnValue = GIV(falseObj); + goto commonReturn; + } + break; + case 123: // returnNil + case 347: // 91 returnNil + { + VM_LABEL(returnNil); + localReturnValue = GIV(nilObj); + goto commonReturn; + } + break; + case 124: // returnTopFromMethod + case 348: // 92 returnTopFromMethod + { + VM_LABEL(returnTopFromMethod); + localReturnValue = longAt(localSP); + goto commonReturn; + } + break; + case 125: // returnTopFromBlock + { + VM_LABEL(returnTopFromBlock); + localReturnValue = longAt(localSP); + + /* goto commonCallerReturn */ + } + + commonCallerReturn: + /* commonCallerReturn */ + { + sqInt aMethodObj; + char *callerFP; + char *callersFPOrNull; + sqInt contextToReturnFrom; + sqInt contextToReturnTo; + char *fp; + char *frameAbove; + sqInt header; + sqInt methodHeader; + StackPage *newPage; + sqInt retToContext; + sqInt senderOop; + char *theFP; + StackPage *thePage; + StackPage *thePage1; + char *theSP; + + VM_LABEL(commonCallerReturn); + + /* TODO: Store/restore the nativeSP more properly, when it exists */ + callersFPOrNull = ((char *)(longAt(localFP + FoxSavedFP))); + if (!callersFPOrNull) { + assert(localFP == ((GIV(stackPage)->baseFP))); + + /* begin baseFrameReturn */ + /* begin frameCallerContext: */ + assert(isBaseFrame(localFP)); + thePage1 = stackPageAtpages(pageIndexFor(localFP), GIV(pages)); + contextToReturnTo = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(contextToReturnTo)); + assert((contextToReturnTo == (nilObject())) + || (isContext(followMaybeForwarded(contextToReturnTo)))); + + /* The stack page is effectively free now, so free it. We must free it to be + correct in determining if contextToReturnTo is still married, and in case + makeBaseFrameFor: cogs a method, which may cause a code compaction, + in which case the frame must be free to avoid the relocation machinery + tracing the dead frame. Since freeing now temporarily violates the page-list + ordering invariant, use the assert-free version. */ + freeStackPageNoAssert(GIV(stackPage)); + retToContext = /* isContext: */ + ((!(contextToReturnTo & (tagMask())))) + && (((longAt((void *)(contextToReturnTo))) & (classIndexMask())) == ClassMethodContextCompactIndex); + if (retToContext + && (/* isStillMarriedContext: */ + (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(contextToReturnTo))))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (theFP == ((thePage->headFP))) { + theSP = (thePage->headSP); + } + else { + /* Returning to some interior frame, presumably because of a sender assignment. + Move the frames above to another page (they may be in use, e.g. via coroutining). + Make the interior frame the top frame. */ + + /* begin findFrameAbove:inPage: */ + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == theFP) { + frameAbove = 0; + goto l185; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == theFP) { + frameAbove = fp; + goto l185; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + frameAbove = 0; + /* end findFrameAbove:inPage: */ +l185: + + /* Since we've just deallocated a page we know that newStackPage won't deallocate an existing one. */ + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l186; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l186: + assert(newPage == GIV(stackPage)); + moveFramesInthroughtoPage(thePage, frameAbove, newPage); + markStackPageMostRecentlyUsed(newPage); + theFP = (thePage->headFP); + theSP = (thePage->headSP); + } + } + else { + if (!(retToContext + && (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1)))) { + contextToReturnFrom = longAt(((GIV(stackPage)->baseAddress)) - BytesPerWord); + tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFromtoreturnValue(contextToReturnFrom, contextToReturnTo, localReturnValue); + + /* begin externalCannotReturn:from: */ + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),contextToReturnFrom); + longAtput((GIV(stackPointer) -= BytesPerWord),localReturnValue); + + /* Both ceBaseFrameReturnTrampoline & ceNonLocalReturnTrampoline pop + their caller's return pc into instructionPointer. In this uncommon case restore + it, since a send's call pushes the instructionPointer (after the arguments). */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + ((void) (ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), contextToReturnFrom, 1))); + goto l184; + } + + /* We must void the instructionPointer to stop it being updated if makeBaseFrameFor: + cogs a method, which may cause a code compaction. */ + GIV(instructionPointer) = 0; + thePage = makeBaseFrameFor(contextToReturnTo); + theFP = (thePage->headFP); + theSP = (thePage->headSP); + } + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + assert((stackPageFor(theFP)) == GIV(stackPage)); + localSP = theSP; + localFP = theFP; + localIP = ((char *)(longAt(localSP))); + if ((((usqInt)localIP)) < (startOfMemory())) { + if ((((usqInt)localIP)) != (ceReturnToInterpreterPC())) { + /* localIP in the cog method zone indicates a return to machine code. */ + + /* begin returnToMachineCodeFrame */ + assertCStackWellAligned(); + assert((((usqInt)localIP)) < (startOfMemory())); + assert(isMachineCodeFrame(localFP)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 0, __LINE__); + assert(isPCWithinMethodZone(((usqInt)localIP))); + + /* internalStackTopPut: */ + longAtput(localSP,((usqInt)localIP)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),localReturnValue); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + ceEnterCogCodePopReceiverReg(); + goto l184; + } + localIP = ((char *)(((usqInt)(longAt(localFP + FoxIFSavedIP))))); + } + assert(checkIsStillMarriedContextcurrentFP(contextToReturnTo, localFP)); + aMethodObj = longAt(localFP + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* internalStackTopPut: */ + longAtput(localSP,localReturnValue); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l184; + } + + /* baseFrame */ + localIP = ((char *)(longAt(localFP + FoxCallerSavedIP))); + localSP = localFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(localFP))->cmNumArgs) + : byteAt((localFP + FoxIFrameFlags) + 1)))) << (shiftForWord()))))); + localFP = callersFPOrNull; + if ((((usqInt)localIP)) < (startOfMemory())) { + if ((((usqInt)localIP)) != (ceReturnToInterpreterPC())) { + /* localIP in the cog method zone indicates a return to machine code. */ + + /* begin returnToMachineCodeFrame */ + assertCStackWellAligned(); + assert((((usqInt)localIP)) < (startOfMemory())); + assert(isMachineCodeFrame(localFP)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 0, __LINE__); + assert(isPCWithinMethodZone(((usqInt)localIP))); + + /* internalStackTopPut: */ + longAtput(localSP,((usqInt)localIP)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),localReturnValue); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + ceEnterCogCodePopReceiverReg(); + goto l184; + } + localIP = ((char *)(((usqInt)(longAt(localFP + FoxIFSavedIP))))); + } + aMethodObj = longAt(localFP + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* internalStackTopPut: */ + longAtput(localSP,localReturnValue); + } + /* end case */ +l184:; + break; + case 126: // unknownBytecode + case 127: // unknownBytecode + + respondToUnknownBytecode: + /* respondToUnknownBytecode */ + { + usqInt numSlots; + sqInt ourContext; + + VM_LABEL(respondToUnknownBytecode); + GIV(messageSelector) = /* maybeSplObj: */ + (SelectorUnknownBytecode < ((/* begin numSlotsOf: */ + assert((classIndexOf(GIV(specialObjectsOop))) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(GIV(specialObjectsOop) + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(GIV(specialObjectsOop) - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) + ? longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorUnknownBytecode) << (shiftForWord())))))) + : 0); + if ((!GIV(messageSelector)) + || (GIV(messageSelector) == GIV(nilObj))) { + error("Unknown bytecode"); + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l187; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l187: + + /* undo fetch of bytecode so that context's pc is pointing to the unknown bytecode. */ + localIP -= 1; + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),ourContext); + GIV(argumentCount) = 0; + goto normalSend; + } + break; + case 128: // extendedPushBytecode + { + sqInt descriptor; + usqInt frameNumArgs; + sqInt litVar; + sqInt object; + usqInt variableIndex; + usqInt variableType; + + VM_LABEL(extendedPushBytecode); + descriptor = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + variableType = (((usqInt)(descriptor)) >> 6) & 3; + variableIndex = descriptor & 0x3F; + if (!variableType) { + /* begin pushReceiverVariable: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + ((variableIndex << (shiftForWord())))))); + goto l8; + } + if (variableType == 1) { + /* begin pushTemporaryVariable: */ + object = /* itemporary:in: */ + (variableIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - variableIndex) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - variableIndex) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + goto l8; + } + if (variableType == 2) { + /* begin pushLiteralConstant: */ + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + (((variableIndex + LiteralStart) << (shiftForWord()))))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + goto l8; + } + if (variableType == 3) { + /* begin pushLiteralVariable: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + (((variableIndex + LiteralStart) << (shiftForWord()))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(variableIndex + LiteralStart, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))))); + goto l8; + } + } + /* end case */ +l8:; + break; + case 129: // extendedStoreBytecode + { + sqInt additionalMethodState; + sqInt descriptor; + sqInt descriptor1; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt index; + sqInt instVarTypesArrays; + sqInt litVar; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt objOop; + sqInt rcvrClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + sqInt value; + usqInt variableIndex; + + VM_LABEL(extendedStoreBytecode); + /* begin extendedStoreBytecodePop: */ + descriptor = byteAt(localIP + 1); + switch ((((usqInt)(descriptor)) >> 6) & 3) { + case 0: + /* begin collectInstanceVariableTypeFor:at:of: */ + rcvrClass = /* fetchClassOf: */ + ((tagBits = (longAt(localFP + FoxIFReceiver)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(localFP + FoxIFReceiver))); + instVarTypesArrays = /* #instVarTypesArraysOf: #safeFollowObjField:ofObject: */ + (InstVarTypesArraysIndex < (lengthOf(rcvrClass)) + ? followObjFieldofObject(InstVarTypesArraysIndex, rcvrClass) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (instVarTypesArrays) + && (isInstanceOfClassArray(instVarTypesArrays)))) { + goto l14; + } + typesArray = /* safeFollowObjField:ofObject: */ + ((descriptor & 0x3F) < (lengthOf(instVarTypesArrays)) + ? followObjFieldofObject(descriptor & 0x3F, instVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l13; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l9; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l9; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l9; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l9; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l9; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l9: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l13; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l13; + } + } + /* end collectTypeOf:in: */ +l13: + /* end collectTypeOf:in:at: */ +l14:; + break; + case 1: + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 3)) != 0))) { + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l11; + } + typesArray = /* safeFollowObjField:ofObject: */ + ((descriptor & 0x3F) < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(descriptor & 0x3F, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l10; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l15; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l15; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l15; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l15; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l15; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l15: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l10; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l10; + } + } + /* end collectTypeOf:in: */ +l10: + /* end collectTypeOf:in:at: */ +l11:; + } + break; + default: +; + } + descriptor1 = byteAt(++localIP); + variableIndex = descriptor1 & 0x3F; + value = longAt(localSP); + switch ((((usqInt)(descriptor1)) >> 6) & 3) { + case 0: + objOop = longAt(localFP + FoxIFReceiver); + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(objOop)))); + if ((((usqInt)((byteAt((void *)(objOop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),objOop); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((variableIndex + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l12; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, objOop, value)); + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + ((variableIndex << (shiftForWord())))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l12:; + break; + case 1: + /* itemporary:in:put: */ + if (variableIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - variableIndex) * BytesPerWord),value); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - variableIndex) * BytesPerWord),value); + } + break; + case 2: + error("illegal store"); + break; + case 3: + /* begin storeLiteralVariable:withValue: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + (((variableIndex + LiteralStart) << (shiftForWord()))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(variableIndex + LiteralStart, GIV(method), litVar); + } + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(litVar)))); + if ((((usqInt)((byteAt((void *)(litVar + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),litVar); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((usqInt)2 /* (ValueIndex + 1) */ << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l16; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ValueIndex, litVar, value)); + assert(isNonImmediate(litVar)); + if (oopisGreaterThanOrEqualTo(litVar, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(litVar + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(litVar); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l16:; + break; + default: + error("Case not found and no otherwise clause"); + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 130: // extendedStoreAndPopBytecode + { + sqInt additionalMethodState; + sqInt descriptor; + sqInt descriptor1; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt index; + sqInt instVarTypesArrays; + sqInt litVar; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt objOop; + sqInt rcvrClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + sqInt value; + usqInt variableIndex; + + VM_LABEL(extendedStoreAndPopBytecode); + /* begin extendedStoreBytecodePop: */ + descriptor = byteAt(localIP + 1); + switch ((((usqInt)(descriptor)) >> 6) & 3) { + case 0: + /* begin collectInstanceVariableTypeFor:at:of: */ + rcvrClass = /* fetchClassOf: */ + ((tagBits = (longAt(localFP + FoxIFReceiver)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(localFP + FoxIFReceiver))); + instVarTypesArrays = /* #instVarTypesArraysOf: #safeFollowObjField:ofObject: */ + (InstVarTypesArraysIndex < (lengthOf(rcvrClass)) + ? followObjFieldofObject(InstVarTypesArraysIndex, rcvrClass) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (instVarTypesArrays) + && (isInstanceOfClassArray(instVarTypesArrays)))) { + goto l22; + } + typesArray = /* safeFollowObjField:ofObject: */ + ((descriptor & 0x3F) < (lengthOf(instVarTypesArrays)) + ? followObjFieldofObject(descriptor & 0x3F, instVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l21; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l17; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l17; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l17; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l17; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l17; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l17: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l21; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l21; + } + } + /* end collectTypeOf:in: */ +l21: + /* end collectTypeOf:in:at: */ +l22:; + break; + case 1: + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 3)) != 0))) { + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l19; + } + typesArray = /* safeFollowObjField:ofObject: */ + ((descriptor & 0x3F) < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(descriptor & 0x3F, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l18; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l23; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l23; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l23; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l23; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l23; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l23: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l18; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l18; + } + } + /* end collectTypeOf:in: */ +l18: + /* end collectTypeOf:in:at: */ +l19:; + } + break; + default: +; + } + descriptor1 = byteAt(++localIP); + variableIndex = descriptor1 & 0x3F; + value = longAt(localSP); + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + switch ((((usqInt)(descriptor1)) >> 6) & 3) { + case 0: + objOop = longAt(localFP + FoxIFReceiver); + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(objOop)))); + if ((((usqInt)((byteAt((void *)(objOop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),objOop); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((variableIndex + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l20; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, objOop, value)); + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + ((variableIndex << (shiftForWord())))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l20:; + break; + case 1: + /* itemporary:in:put: */ + if (variableIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - variableIndex) * BytesPerWord),value); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - variableIndex) * BytesPerWord),value); + } + break; + case 2: + error("illegal store"); + break; + case 3: + /* begin storeLiteralVariable:withValue: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + (((variableIndex + LiteralStart) << (shiftForWord()))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(variableIndex + LiteralStart, GIV(method), litVar); + } + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(litVar)))); + if ((((usqInt)((byteAt((void *)(litVar + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),litVar); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((usqInt)2 /* (ValueIndex + 1) */ << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l24; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ValueIndex, litVar, value)); + assert(isNonImmediate(litVar)); + if (oopisGreaterThanOrEqualTo(litVar, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(litVar + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(litVar); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l24:; + break; + default: + error("Case not found and no otherwise clause"); + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 131: // singleExtendedSendBytecode + { + sqInt descriptor; + + VM_LABEL(singleExtendedSendBytecode); + descriptor = byteAt(++localIP); + + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((descriptor & 0x1F) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = ((usqInt)(descriptor)) >> 5; + + /* goto normalSend */ + } + + normalSend: + /* normalSend */ + { + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(normalSend); + rcvr = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + lkupClassTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + assert(lkupClassTag != (nilObject())); + + commonSendOrdinary: + /* commonSendOrdinary */ + { + sqInt additionalMethodState; + sqInt arg; + sqInt argIndex; + sqInt classTablePage; + CogMethod *cogMethod; + sqInt fieldIndex; + sqInt fmt; + sqInt hash; + sqInt i; + sqInt index; + sqInt localPrimIndex; + sqInt maxNumArgs; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt nArgs; + usqInt numSlots; + usqInt numSlotsUsqInt; + usqInt numTemps; + sqInt object; + sqInt objectClass; + usqInt probe; + sqInt rcvr; + char *savedFramePointer; + char *savedStackPointer; + sqInt selectorOrProperties; + int succeeded; + sqInt switched; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt types; + sqInt typesArraySize; + sqInt wordSize; + + VM_LABEL(commonSendOrdinary); + rcvr = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + if (recordSendTrace()) { + /* begin recordTrace:source:thing:extra: */ + if (TraceLog) { + GIV(traceLog)[GIV(traceLogIndex)] = 0 /* (TraceSend + CSFromInterpreter) */; + GIV(traceLog)[GIV(traceLogIndex) + 1] = (classForClassTag(lkupClassTag)); + GIV(traceLog)[GIV(traceLogIndex) + 2] = GIV(messageSelector); + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + if (printOnTrace()) { + printActivationNameForSelectorstartClass(GIV(messageSelector), classForClassTag(lkupClassTag)); + cr(); + } + } + + /* begin internalFindNewMethodOrdinary */ + /* begin inlineLookupInMethodCacheSel:classTag: */ + /* shift drops two low-order zeros from addresses */ + hash = GIV(messageSelector) ^ ((((usqInt)(lkupClassTag) << 2))); + + /* first probe */ + probe = hash & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == GIV(messageSelector)) + && ((GIV(methodCache)[probe + MethodCacheClass]) == lkupClassTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + goto l195; + } + + /* second probe */ + probe = (((usqInt)(hash)) >> 1) & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == GIV(messageSelector)) + && ((GIV(methodCache)[probe + MethodCacheClass]) == lkupClassTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + goto l195; + } + probe = (((usqInt)(hash)) >> 2) & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == GIV(messageSelector)) + && ((GIV(methodCache)[probe + MethodCacheClass]) == lkupClassTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + goto l195; + } + goto l192; +l195: + + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + goto l189; +l192: + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + if ((/* isOopForwarded: */ + ((!(GIV(messageSelector) & (tagMask())))) + && ((!((longAt((void *)(GIV(messageSelector)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + || (lkupClassTag == (isForwardedObjectClassIndexPun()))) { + if (/* isOopForwarded: */ + ((!(GIV(messageSelector) & (tagMask())))) + && ((!((longAt((void *)(GIV(messageSelector)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + GIV(messageSelector) = handleForwardedSelectorFaultFor(GIV(messageSelector)); + } + if (lkupClassTag == (isForwardedObjectClassIndexPun())) { + lkupClassTag = handleForwardedSendFaultForTag(lkupClassTag); + } + if (lookupInMethodCacheSelclassTag(GIV(messageSelector), lkupClassTag)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + goto l190; + } + } + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((lkupClassTag >= 0) + && ((lkupClassTag <= (tagMask())) + || ((lkupClassTag >= (arrayClassIndexPun())) + && (lkupClassTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(lkupClassTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + GIV(lkupClass) = null; + goto l196; + } + fieldIndex = lkupClassTag & ((1U << (classTableMajorIndexShift())) - 1); + GIV(lkupClass) = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l196: + assert((GIV(lkupClass)) + && (addressCouldBeClassObj(GIV(lkupClass)))); + lookupMethodInClass(GIV(lkupClass)); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + addNewMethodToCache(GIV(lkupClass)); +l189: + /* end internalFindNewMethodOrdinary */ +l190: + + /* begin internalExecuteNewMethod */ + if (primitiveFunctionPointer) { + if ((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex) { + /* begin internalQuickPrimitiveResponse */ + assert(isPrimitiveFunctionPointerAnIndex()); + localPrimIndex = ((sqInt) primitiveFunctionPointer); + assert((localPrimIndex > 0xFF) + && (localPrimIndex < 520)); + + /* Quick return inst vars */ + if (localPrimIndex >= 264) { + longAtput(localSP,longAt((void *)(((longAt(localSP)) + BaseHeaderSize) + ((((usqInt)((localPrimIndex - 264)) << (shiftForWord()))))))); + goto l194; + } + + /* Quick return constants */ + if (localPrimIndex == 0x100) { + goto l194; + } + if (localPrimIndex == 0x101) { + /* internalStackTopPut: */ + longAtput(localSP,GIV(trueObj)); + goto l194; + } + if (localPrimIndex == 258) { + /* internalStackTopPut: */ + longAtput(localSP,GIV(falseObj)); + goto l194; + } + if (localPrimIndex == 259) { + /* internalStackTopPut: */ + longAtput(localSP,GIV(nilObj)); + goto l194; + } + + /* internalStackTopPut: */ + longAtput(localSP,(((usqInt)(localPrimIndex - 261) << 3) | 1)); + goto l194; + } + + /* slowPrimitiveResponse may of course context-switch. If so we must reenter the + new process appropriately, returning only if we've found an interpreter frame. */ + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin slowPrimitiveResponse */ + assert(!(isOopForwarded(stackValue(GIV(argumentCount))))); + assert((GIV(remapBufferCount)) == 0); + nArgs = GIV(argumentCount); + savedStackPointer = GIV(stackPointer); + savedFramePointer = GIV(framePointer); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if (/* recordPrimTraceForMethod: */ + (recordPrimTrace()) + && ((primTracePluginName + ? methodHasPrimitiveInPrimTracePlugin(GIV(newMethod)) + : 1))) { + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = GIV(newMethod); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + dispatchFunctionPointer(primitiveFunctionPointer); + assert(maybeLeakCheckExternalPrimCall(GIV(newMethod))); + + /* begin maybeRetryPrimitiveOnFailure */ + if (GIV(primFailCode)) { + retryPrimitiveOnFailure(); + } + if ((!GIV(primFailCode)) + && ((GIV(framePointer) == savedFramePointer) + && (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))))) { + if (GIV(stackPointer) != (savedStackPointer + (nArgs * BytesPerWord))) { + /* This is necessary but insufficient; the result may still have been written to the stack. + At least we'll know something is wrong. */ + failUnbalancedPrimitive(); + GIV(stackPointer) = savedStackPointer; + } + } + + /* Don't fail if primitive has done something radical, e.g. perform: + If we are profiling, take accurate primitive measures */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + succeeded = !GIV(primFailCode); + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (succeeded) { + returntoExecutive(popStack(), 1); + browserPluginReturnIfNeeded(); + goto l194; + } + } + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* if not primitive, or primitive failed, activate the method */ + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if (!((!(methodHeader & (smallIntegerTag()))))) { + /* begin internalActivateNewMethod */ + /* begin collectArgumentsTypeFor:fromStackAt:count: */ + /* begin tempVarTypesArraysOf: */ + /* begin additionalMethodStateOf:at: */ + /* begin additionalMethodStateOf: */ + selectorOrProperties = penultimateLiteralOf(GIV(newMethod)); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l188; + } + wordSize = BytesPerWord; + maxNumArgs = (((lengthOf(tempVarTypesArrays)) < GIV(argumentCount)) ? (lengthOf(tempVarTypesArrays)) : GIV(argumentCount)); + for (argIndex = 0; argIndex < maxNumArgs; argIndex += 1) { + /* begin followObjField:ofObject: */ + types = longAt((void *)((tempVarTypesArrays + BaseHeaderSize) + ((((usqInt)(argIndex) << (shiftForWord())))))); + assert(isNonImmediate(types)); + if ((!((longAt((void *)(types))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + types = fixFollowedFieldofObjectwithInitialValue(argIndex, tempVarTypesArrays, types); + } + arg = longAt((void *)(localSP + (argIndex * wordSize))); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (types) + && (isInstanceOfClassArray(types)))) { + goto l193; + } + objectClass = /* fetchClassOf: */ + ((tagBits = arg & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(arg)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(types + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(types + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(types - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l191; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l191; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l191; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l191; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l191; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l191: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, types, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l193; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, types, objectClass)); + assert(isNonImmediate(types)); + if (oopisGreaterThanOrEqualTo(types, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(types + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(types); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l193; + } + } + /* end collectTypeOf:in: */ +l193:; + } + /* end collectArgumentsTypeFor:fromStackAt:count: */ +l188: + methodHeaderSqInt = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(!((isCogMethodReferenceNoAssert(methodHeaderSqInt)))); + numTemps = (((usqInt)(methodHeaderSqInt)) >> MethodHeaderTempCountShift) & 0x3F; + assert(GIV(argumentCount) == (argumentCountOfMethodHeader(methodHeaderSqInt))); + + /* could new rcvr be set at point of send? */ + rcvr = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + assert(!(isOopForwarded(rcvr))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),((usqInt)localIP)); + longAtput((localSP -= BytesPerOop),((usqInt)localFP)); + localFP = localSP; + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeaderSqInt); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeaderSqInt)) < 0 + ? 0x100 + : 0); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((((usqInt)(methodHeaderSqInt)) >> MethodHeaderArgCountShift) & 15) << ((BytesPerWord * 8) - 8))))) + : ((1 + ((((((usqInt)(methodHeaderSqInt)) >> MethodHeaderArgCountShift) & 15) << 8))))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + longAtput((localSP -= BytesPerOop),0); + longAtput((localSP -= BytesPerOop),rcvr); + + /* Initialize temps... */ + for (i = (GIV(argumentCount) + 1); i <= numTemps; i += 1) { + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(nilObj)); + } + + /* -1 to account for pre-increment in fetchNextBytecode */ + localIP = (((char *)(((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeaderSqInt) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeaderSqInt >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize))) - 1; + if (((methodHeaderSqInt & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + localIP += 3 /* sizeOfCallPrimitiveBytecode: */; + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(localSP, methodHeaderSqInt); + } + } + assert((frameNumArgs(localFP)) == GIV(argumentCount)); + assert(!(frameIsBlockActivation(localFP))); + assert(!(frameHasContext(localFP))); + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc). */ + if (localSP < GIV(stackLimit)) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + switched = handleStackOverflowOrEventAllowContextSwitch(canContextSwitchIfActivatingheader(GIV(newMethod), methodHeaderSqInt)); + returnToExecutivepostContextSwitch(1, switched); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + } + goto l194; + } + + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(localFP))); + longAtput(localFP + FoxIFSavedIP,((sqInt)localIP)); + GIV(instructionPointer) = ceReturnToInterpreterPC(); + + /* begin externalizeFPandSP */ + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* This may context switch and hence return... */ + activateNewCogMethodinInterpreter(((CogMethod *) methodHeader), 1); + + /* Hence this si reachable... */ + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + /* end internalExecuteNewMethod */ +l194: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + } + break; + case 132: // doubleExtendedDoAnythingBytecode + { + sqInt byte2; + sqInt byte3; + sqInt callerContextOrNil; + char *callerFP; + sqInt litVar; + sqInt obj; + sqInt object; + int onCurrentPage; + sqInt opType; + sqInt senderOop; + char *spouseFP; + char *theFP; + StackPage *thePage; + sqInt top; + sqInt value; + + VM_LABEL(doubleExtendedDoAnythingBytecode); + byte2 = byteAt(++localIP); + byte3 = byteAt(++localIP); + opType = ((usqInt)(byte2)) >> 5; + if (!opType) { + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((byte3 + LiteralStart)) << (shiftForWord())))))); + GIV(argumentCount) = byte2 & 0x1F; + goto normalSend; + goto l25; + } + if (opType == 1) { + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((byte3 + LiteralStart)) << (shiftForWord())))))); + GIV(argumentCount) = byte2 & 0x1F; + goto superclassSend; + goto l25; + } + if (opType == 2) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + obj = longAt(localFP + FoxIFReceiver); + if ((byte3 <= StackPointerIndex) + && (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin instVar:ofContext: */ + assert(byte3 < MethodIndex); + assert(isContext(obj)); + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + if (!(((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + value = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord())))))); + if ((byte3 == InstructionPointerIndex) + && (((((value) & 7) == 1)) + && ((((int) value)) < 0))) { + /* begin internalMustMapMachineCodePC:context: */ + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + value = mustMapMachineCodePCcontext((value >> 3), obj); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + } + object = value; + goto l28; + } + if (isWidowedContext(obj)) { + object = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord())))))); + goto l28; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!byte3) { + /* begin ensureCallerContext: */ + callerFP = ((char *)(longAt(spouseFP + FoxSavedFP))); + if (!callerFP) { + /* begin frameCallerContext: */ + assert(isBaseFrame(spouseFP)); + thePage = stackPageAtpages(pageIndexFor(spouseFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + object = callerContextOrNil; + goto l28; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP))); + object = longAt(callerFP + FoxThisContext); + goto l28; + } + object = marryFrameSP( + callerFP, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(spouseFP))), + (spouseFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(spouseFP))->cmNumArgs) + : byteAt((spouseFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + goto l28; + } + if (byte3 == StackPointerIndex) { + assert((ReceiverIndex + (stackPointerIndexForFrame(spouseFP))) < (lengthOf(obj))); + object = (((usqInt)(stackPointerIndexForFrame(spouseFP)) << 3) | 1); + goto l28; + } + if (byte3 == InstructionPointerIndex) { + object = instructionPointerForFramecurrentFPcurrentIP(spouseFP, localFP, oopForPointer(localIP)); + goto l28; + } + error("bad index"); + object = 0; + /* end instVar:ofContext: */ +l28: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + else { + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord()))))))); + } + goto l25; + } + if (opType == 3) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((byte3 + LiteralStart)) << (shiftForWord())))))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + goto l25; + } + if (opType == 4) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((byte3 + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(byte3 + LiteralStart, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))))); + goto l25; + } + top = longAt(localSP); + if (opType == 7) { + /* begin storeLiteralVariable:withValue: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((byte3 + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(byte3 + LiteralStart, GIV(method), litVar); + } + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(litVar)))); + if ((((usqInt)((byteAt((void *)(litVar + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),litVar); + longAtput((localSP -= BytesPerOop),top); + longAtput((localSP -= BytesPerOop),(((usqInt)2 /* (ValueIndex + 1) */ << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l27; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ValueIndex, litVar, top)); + assert(isNonImmediate(litVar)); + if (oopisGreaterThanOrEqualTo(litVar, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(top & (tagMask())))) + && (oopisLessThan(top, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(litVar + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(litVar); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))),top); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l27: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l25; + } + + /* opType = 5 is store; opType = 6 is storePop */ + if (opType == 6) { + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + obj = longAt(localFP + FoxIFReceiver); + if ((byte3 <= ReceiverIndex) + && ((((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + || (ismethodAssignmentToContextWithMachineCodePC(byte3, top))))) { + /* begin instVar:ofContext:put: */ + assert(isMarriedOrWidowedContext(obj)); + assert(!((isObjImmutable(obj)))); + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(obj))))) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(byte3, obj, top)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(top & (tagMask())))) + && (oopisLessThan(top, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord()))))),top); + if (byte3 == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + goto l26; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!byte3) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + onCurrentPage = thePage == GIV(stackPage); + storeSenderOfFramewithValue(theFP, top); + if (onCurrentPage) { + localFP = (GIV(stackPage)->headFP); + localSP = (GIV(stackPage)->headSP); + } + else { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + goto l26; + } + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + externalDivorceFrameandContext(theFP, obj); + if (ismethodAssignmentToContextWithMachineCodePC(byte3, obj)) { + ensureContextHasBytecodePC(obj); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(byte3, obj, top)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(top & (tagMask())))) + && (oopisLessThan(top, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord()))))),top); + if (byte3 == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + + /* Assigning various fields can force a divorce which can change the stackPage. */ + markStackPageMostRecentlyUsed(GIV(stackPage)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 1, __LINE__); + /* end instVar:ofContext:put: */ +l26:; + } + else { + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(obj)))); + if ((((usqInt)((byteAt((void *)(obj + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),obj); + longAtput((localSP -= BytesPerOop),top); + longAtput((localSP -= BytesPerOop),(((usqInt)(byte3 + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l29; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(byte3, obj, top)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(top & (tagMask())))) + && (oopisLessThan(top, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord()))))),top); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l29:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + /* end case */ +l25:; + break; + case 133: // singleExtendedSuperBytecode + { + sqInt descriptor; + + VM_LABEL(singleExtendedSuperBytecode); + descriptor = byteAt(++localIP); + + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((descriptor & 0x1F) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = ((usqInt)(descriptor)) >> 5; + + /* goto superclassSend */ + } + + superclassSend: + /* superclassSend */ + { + sqInt aValue; + sqInt classPointer; + sqInt err; + int hash; + sqInt literal; + sqInt objOop; + sqInt offset; + sqInt referent; + sqInt superclass; + + VM_LABEL(superclassSend); + /* begin methodClassOf: */ + offset = (literalCountOf(GIV(method))) - 1; + literal = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = fixFollowedFieldofObjectwithInitialValue(offset + LiteralStart, GIV(method), literal); + } + classPointer = ((literal != GIV(nilObj)) + && (/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + ? (assert((numSlotsOf(literal)) > ValueIndex), + followFieldofObject(ValueIndex, literal)) + : GIV(nilObj)); + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + superclass = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(superclass)); + if ((!((longAt((void *)(superclass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + superclass = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, superclass); + } + + /* classTagForClass: uses ensureBehaviorHash: */ + + /* begin classTagForClass: */ + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(superclass)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + lkupClassTag = ((hash = (long32At((void *)(superclass + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(superclass) + ? ((err = enterIntoClassTable(superclass)) + ? -err + : (long32At((void *)(superclass + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + + /* To maintain the invariant that all receivers are unforwarded we need an explicit + read barrier in the super send cases. Even though we always follow receivers + on become e.g. super doSomethingWith: (self become: other) forwards the receiver + self pushed on the stack. */ + + /* begin ensureReceiverUnforwarded */ + if (/* isOopForwarded: */ + ((!((longAt(localSP + (GIV(argumentCount) * BytesPerOop))) & (tagMask())))) + && ((!((longAt((void *)(longAt(localSP + (GIV(argumentCount) * BytesPerOop))))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + referent = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + aValue = referent; + + /* begin internalStackValue:put: */ + longAtput(localSP + (GIV(argumentCount) * BytesPerOop),aValue); + } + assert(lkupClassTag != (nilObject())); + goto commonSendOrdinary; + } + break; + case 134: // secondExtendedSendBytecode + { + sqInt descriptor; + + VM_LABEL(secondExtendedSendBytecode); + descriptor = byteAt(++localIP); + + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((descriptor & 0x3F) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = ((usqInt)(descriptor)) >> 6; + goto normalSend; + } + break; + case 135: // popStackBytecode + case 472: // 216 popStackBytecode + { + VM_LABEL(popStackBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + localSP += 1 * BytesPerOop; + } + break; + case 136: // duplicateTopBytecode + case 339: // 83 duplicateTopBytecode + { + sqInt object; + + VM_LABEL(duplicateTopBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = longAt(localSP); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 137: // pushActiveContextBytecode + { + sqInt ourContext; + + VM_LABEL(pushActiveContextBytecode); + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l30; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l30: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),ourContext); + } + break; + case 138: // pushNewArrayBytecode + case 487: // 231 pushNewArrayBytecode + { + usqInt array; + sqInt i; + usqInt newObj; + usqInt numBytes; + int popValues; + sqInt size; + + VM_LABEL(pushNewArrayBytecode); + size = byteAt(++localIP); + if ((popValues = size >= 128)) { + size -= 128; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((size >= 0) + && ((knownClassAtIndex(ClassArrayCompactIndex)) != GIV(nilObj))); + assert((arrayFormat()) == (instSpecOfClass(knownClassAtIndex(ClassArrayCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(size < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((size < 1 + ? 8 /* allocationUnit */ + : size * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + array = 0; + goto l31; + } + } + long64Atput((void *)(newObj),((((((usqLong) size)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + GIV(freeStart) += numBytes; + array = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l31: + + /* Can't use NewObjectMemory>>#eeInstantiateSmallClassIndex:format:numSlots: + cuz it assumes <= 252 bytes whereras this may allocate 127 * 4 + headerSize bytes. */ + if (popValues) { + for (i = 0; i < size; i += 1) { + /* Assume: have just allocated a new Array; it must be young. Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(i, array, longAt((void *)(localSP + (((size - i) - 1) * BytesPerOop))))); + longAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),longAt((void *)(localSP + (((size - i) - 1) * BytesPerOop)))); + } + + /* begin internalPop: */ + localSP += size * BytesPerOop; + } + else { + for (i = 0; i < size; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(i, array, GIV(nilObj))); + longAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),array); + } + break; + case 139: // callPrimitiveBytecode + case 504: // 248 callPrimitiveBytecode + { + sqInt header; + sqInt headerSqInt; + + VM_LABEL(callPrimitiveBytecode); + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + headerSqInt = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + if ((((header & AlternateHeaderHasPrimFlag) != 0)) + && ((((sqInt)localIP)) == (((((usqInt)(pointerForOop(GIV(method))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize))) { + localIP = (localIP + 3 /* sizeOfCallPrimitiveBytecode: */) - 1; + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l32; + } + goto respondToUnknownBytecode; + } + /* end case */ +l32:; + break; + case 140: // pushRemoteTempLongBytecode + case 507: // 251 pushRemoteTempLongBytecode + { + usqInt frameNumArgs; + sqInt remoteTempIndex; + sqInt tempVector; + sqInt tempVectorIndex; + + VM_LABEL(pushRemoteTempLongBytecode); + remoteTempIndex = byteAt(++localIP); + tempVectorIndex = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + tempVector = /* itemporary:in: */ + (tempVectorIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - tempVectorIndex) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - tempVectorIndex) * BytesPerWord))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((tempVector + BaseHeaderSize) + ((((usqInt)(remoteTempIndex) << (shiftForWord()))))))); + } + break; + case 141: // storeRemoteTempLongBytecode + case 508: // 252 storeRemoteTempLongBytecode + { + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt indexSqInt; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt remoteTempIndex; + sqInt remoteTempTypesArrays; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVector; + sqInt tempVectorIndex; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + sqInt valuePointer; + + VM_LABEL(storeRemoteTempLongBytecode); + remoteTempIndex = byteAt(++localIP); + tempVectorIndex = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + remoteTempTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (RemoteTempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(RemoteTempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (remoteTempTypesArrays) + && (isInstanceOfClassArray(remoteTempTypesArrays)))) { + goto l35; + } + typesArray = /* safeFollowObjField:ofObject: */ + (remoteTempIndex < (lengthOf(remoteTempTypesArrays)) + ? followObjFieldofObject(remoteTempIndex, remoteTempTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l34; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l33; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l33; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l33; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l33; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l33; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l33: + for (indexSqInt = 0; indexSqInt < typesArraySize; indexSqInt += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(indexSqInt) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(indexSqInt, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l34; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(indexSqInt, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(indexSqInt) << (shiftForWord()))))),objectClass); + goto l34; + } + } + /* end collectTypeOf:in: */ +l34: + /* end collectTypeOf:in:at: */ +l35: + + /* begin temporaryVector:in: */ + tempVector = /* itemporary:in: */ + (tempVectorIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - tempVectorIndex) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - tempVectorIndex) * BytesPerWord))); + valuePointer = longAt(localSP); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(remoteTempIndex, tempVector, valuePointer)); + assert(isNonImmediate(tempVector)); + if (oopisGreaterThanOrEqualTo(tempVector, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(tempVector + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(tempVector); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((tempVector + BaseHeaderSize) + ((((usqInt)(remoteTempIndex) << (shiftForWord()))))),valuePointer); + } + break; + case 142: // storeAndPopRemoteTempLongBytecode + case 509: // 253 storeAndPopRemoteTempLongBytecode + { + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt indexSqInt; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt remoteTempIndex; + sqInt remoteTempTypesArrays; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVector; + sqInt tempVectorIndex; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + sqInt valuePointer; + + VM_LABEL(storeAndPopRemoteTempLongBytecode); + /* begin storeRemoteTempLongBytecode */ + remoteTempIndex = byteAt(++localIP); + tempVectorIndex = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + remoteTempTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (RemoteTempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(RemoteTempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (remoteTempTypesArrays) + && (isInstanceOfClassArray(remoteTempTypesArrays)))) { + goto l36; + } + typesArray = /* safeFollowObjField:ofObject: */ + (remoteTempIndex < (lengthOf(remoteTempTypesArrays)) + ? followObjFieldofObject(remoteTempIndex, remoteTempTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l38; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l37; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l37; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l37; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l37; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l37; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l37: + for (indexSqInt = 0; indexSqInt < typesArraySize; indexSqInt += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(indexSqInt) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(indexSqInt, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l38; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(indexSqInt, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(indexSqInt) << (shiftForWord()))))),objectClass); + goto l38; + } + } + /* end collectTypeOf:in: */ +l38: + /* end collectTypeOf:in:at: */ +l36: + + /* begin temporaryVector:in: */ + tempVector = /* itemporary:in: */ + (tempVectorIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - tempVectorIndex) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - tempVectorIndex) * BytesPerWord))); + valuePointer = longAt(localSP); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(remoteTempIndex, tempVector, valuePointer)); + assert(isNonImmediate(tempVector)); + if (oopisGreaterThanOrEqualTo(tempVector, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(tempVector + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(tempVector); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((tempVector + BaseHeaderSize) + ((((usqInt)(remoteTempIndex) << (shiftForWord()))))),valuePointer); + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + break; + case 143: // pushClosureCopyCopiedValuesBytecode + { + sqInt blockSize; + sqInt context; + sqInt i; + sqInt initialIP; + usqInt newClosure; + usqInt newObj; + usqInt numArgs; + sqInt numArgsNumCopied; + usqInt numBytes; + sqInt numCopied; + sqInt numSlots; + + VM_LABEL(pushClosureCopyCopiedValuesBytecode); + numArgsNumCopied = byteAt(++localIP); + numArgs = numArgsNumCopied & 15; + numCopied = (((usqInt)(numArgsNumCopied)) >> 4); + + /* Split blockSize := (self fetchByte * 256) + self fetchByte. into two because evaluation order in C is undefined. */ + blockSize = ((sqInt)((usqInt)((byteAt(++localIP))) << 8)); + blockSize += byteAt(++localIP); + + /* begin pushClosureNumArgs:copiedValues:blockSize: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + context = longAt(localFP + FoxThisContext); + goto l39; + } + context = marryFrameSP(localFP, localSP + (numCopied * BytesPerOop)); + /* end ensureFrameIsMarried:SP: */ +l39: + initialIP = ((((usqInt)localIP)) + 2) - (GIV(method) + BaseHeaderSize); + + /* begin closureIn:numArgs:instructionPointer:numCopiedValues: */ + numSlots = ClosureFirstCopiedValueIndex + numCopied; + assert((numSlots >= 0) + && ((knownClassAtIndex(ClassBlockClosureCompactIndex)) != GIV(nilObj))); + assert((indexablePointersFormat()) == (instSpecOfClass(knownClassAtIndex(ClassBlockClosureCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newClosure = 0; + goto l40; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((indexablePointersFormat())) << (formatShift()))))) + ClassBlockClosureCompactIndex); + GIV(freeStart) += numBytes; + newClosure = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l40: + + /* Assume: have just allocated a new closure; it must be young. Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureOuterContextIndex, newClosure, context)); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord()))))),context); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureStartPCIndex, newClosure, (((usqInt)initialIP << 3) | 1))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))),(((usqInt)initialIP << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureNumArgsIndex, newClosure, ((numArgs << 3) | 1))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord()))))),((numArgs << 3) | 1)); + if (numCopied > 0) { + for (i = 0; i < numCopied; i += 1) { + /* Assume: have just allocated a new BlockClosure; it must be young. + Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(i + ClosureFirstCopiedValueIndex, newClosure, longAt((void *)(localSP + (((numCopied - i) - 1) * BytesPerOop))))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)((i + ClosureFirstCopiedValueIndex)) << (shiftForWord()))))),longAt((void *)(localSP + (((numCopied - i) - 1) * BytesPerOop)))); + } + + /* begin internalPop: */ + localSP += numCopied * BytesPerOop; + } + localIP += blockSize; + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),newClosure); + } + break; + case 144: // shortUnconditionalJump + case 432: // 176 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump); + offset = 1; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + break; + case 145: // shortUnconditionalJump + case 433: // 177 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump1); + offset = 2; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + break; + case 146: // shortUnconditionalJump + case 434: // 178 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump2); + offset = 3; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + break; + case 147: // shortUnconditionalJump + case 435: // 179 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump3); + offset = 4; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + break; + case 148: // shortUnconditionalJump + case 436: // 180 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump4); + offset = 5; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + break; + case 149: // shortUnconditionalJump + case 437: // 181 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump5); + offset = 6; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + break; + case 150: // shortUnconditionalJump + case 438: // 182 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump6); + offset = 7; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + break; + case 151: // shortUnconditionalJump + case 439: // 183 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump7); + offset = 8; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + break; + case 152: // shortConditionalJumpFalse + case 153: // shortConditionalJumpFalse + case 154: // shortConditionalJumpFalse + case 155: // shortConditionalJumpFalse + case 156: // shortConditionalJumpFalse + case 157: // shortConditionalJumpFalse + case 158: // shortConditionalJumpFalse + case 159: // shortConditionalJumpFalse + case 448: // 192 shortConditionalJumpFalse + case 449: // 193 shortConditionalJumpFalse + case 450: // 194 shortConditionalJumpFalse + case 451: // 195 shortConditionalJumpFalse + case 452: // 196 shortConditionalJumpFalse + case 453: // 197 shortConditionalJumpFalse + case 454: // 198 shortConditionalJumpFalse + case 455: // 199 shortConditionalJumpFalse + { + sqInt boolean; + sqInt offset; + + VM_LABEL(shortConditionalJumpFalse); + offset = (currentBytecode & 7) + 1; + + /* begin jumplfFalseBy: */ + boolean = longAt(localSP); + if (boolean == GIV(falseObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(trueObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l41; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfFalseBy: */ +l41:; + } + break; + case 160: // longUnconditionalJump + case 161: // longUnconditionalJump + case 162: // longUnconditionalJump + case 163: // longUnconditionalJump + case 164: // longUnconditionalJump + case 165: // longUnconditionalJump + case 166: // longUnconditionalJump + case 167: // longUnconditionalJump + { + sqInt backwardJumpCountByte; + sqInt header; + sqInt methodHeader; + sqInt offset; + sqInt switched; + + VM_LABEL(longUnconditionalJump); + offset = (((currentBytecode & 7) - 4) * 0x100) + (byteAt(++localIP)); + localIP += offset; + + /* begin ifBackwardsCheckForEvents: */ + if (offset >= 0) { + goto l42; + } + if (localSP < GIV(stackLimit)) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + switched = checkForEventsMayContextSwitch(1); + returnToExecutivepostContextSwitch(1, switched); + browserPluginReturnIfNeeded(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (switched) { + goto l42; + } + } + + /* We use the least significant byte of the flags word (which is marked as an immediate) and + subtract two each time to avoid disturbing the least significant tag bit. Since the byte is + initialized to 1 (on frame build), on first decrement it will become -1. Trip when it reaches 1 again. */ + backwardJumpCountByte = byteAt(localFP + ((VMBIGENDIAN + ? (FoxIFrameFlags + BytesPerWord) - 1 + : FoxIFrameFlags))); + if (((backwardJumpCountByte -= 2)) == 1) { + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + attemptToSwitchToMachineCode(((((oopForPointer(localIP)) - offset) - GIV(method)) - BaseHeaderSize) - 1); + } + + /* can't cog method; avoid asking to cog it again for the longest possible time. */ + backwardJumpCountByte = 0x7F; + } + else { + if (backwardJumpCountByte == -1) { + assert(minBackwardJumpCountForCompile <= 128); + backwardJumpCountByte = ((((usqInt)((minBackwardJumpCountForCompile - 1)) << 1))) + 1; + } + } + + /* begin iframeBackwardBranchByte:put: */ + byteAtput(localFP + ((VMBIGENDIAN + ? (FoxIFrameFlags + BytesPerWord) - 1 + : FoxIFrameFlags)),backwardJumpCountByte); + /* end ifBackwardsCheckForEvents: */ +l42: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 168: // longJumpIfTrue + case 169: // longJumpIfTrue + case 170: // longJumpIfTrue + case 171: // longJumpIfTrue + { + sqInt boolean; + sqInt offset; + + VM_LABEL(longJumpIfTrue); + offset = ((currentBytecode & 3) * 0x100) + (byteAt(++localIP)); + + /* begin jumplfTrueBy: */ + boolean = longAt(localSP); + if (boolean == GIV(trueObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(falseObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l43; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfTrueBy: */ +l43:; + } + break; + case 172: // longJumpIfFalse + case 173: // longJumpIfFalse + case 174: // longJumpIfFalse + case 175: // longJumpIfFalse + { + sqInt boolean; + sqInt offset; + + VM_LABEL(longJumpIfFalse); + offset = ((currentBytecode & 3) * 0x100) + (byteAt(++localIP)); + + /* begin jumplfFalseBy: */ + boolean = longAt(localSP); + if (boolean == GIV(falseObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(trueObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l44; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfFalseBy: */ +l44:; + } + break; + case 176: // bytecodePrimAdd + case 352: // 96 bytecodePrimAdd + { + sqInt arg; + double argDouble; + usqLong bits; + sqInt rcvr; + double rcvrDouble; + sqInt result; + double resultDouble; + char *sp; + sqInt tagBits; + double value; + + VM_LABEL(bytecodePrimAdd); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + result = ((rcvr >> 3)) + ((arg >> 3)); + if ((((((usqInt)(result)) >> 60) + 1) & 15) <= 1) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),(((usqInt)result << 3) | 1)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l45; + } + } + else { + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin primitiveFloatAdd:toArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvr & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvrDouble = value; + goto l46; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvrDouble = ((double) ((rcvr >> 3)) ); + goto l46; + } + } + else { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvr + BaseHeaderSize, resultDouble); + rcvrDouble = resultDouble; + goto l46; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvrDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l46: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = arg & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(arg)); + bits = ((((usqInt)arg))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + argDouble = value; + goto l47; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + argDouble = ((double) ((arg >> 3)) ); + goto l47; + } + } + else { + if (((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(arg + BaseHeaderSize, resultDouble); + argDouble = resultDouble; + goto l47; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + argDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l47: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvrDouble + argDouble)); + GIV(stackPointer) = sp; + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l45; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (0U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l45:; + break; + case 177: // bytecodePrimSubtract + case 353: // 97 bytecodePrimSubtract + { + sqInt arg; + double argDouble; + usqLong bits; + sqInt rcvr; + double rcvrDouble; + sqInt result; + double resultDouble; + char *sp; + sqInt tagBits; + double value; + + VM_LABEL(bytecodePrimSubtract); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + result = ((rcvr >> 3)) - ((arg >> 3)); + if ((((((usqInt)(result)) >> 60) + 1) & 15) <= 1) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),(((usqInt)result << 3) | 1)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l48; + } + } + else { + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin primitiveFloatSubtract:fromArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvr & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvrDouble = value; + goto l49; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvrDouble = ((double) ((rcvr >> 3)) ); + goto l49; + } + } + else { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvr + BaseHeaderSize, resultDouble); + rcvrDouble = resultDouble; + goto l49; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvrDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l49: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = arg & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(arg)); + bits = ((((usqInt)arg))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + argDouble = value; + goto l50; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + argDouble = ((double) ((arg >> 3)) ); + goto l50; + } + } + else { + if (((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(arg + BaseHeaderSize, resultDouble); + argDouble = resultDouble; + goto l50; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + argDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l50: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvrDouble - argDouble)); + GIV(stackPointer) = sp; + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l48; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (2U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l48:; + break; + case 178: // bytecodePrimLessThan + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimLessThan); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheat: */ + if (rcvr < arg) { + /* goto booleanCheatTrue */ + } + else { + goto booleanCheatFalse; + } + goto l51; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatLessthanArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l51; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (4U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l51: + + booleanCheatTrue: + /* booleanCheatTrue */ + { + sqInt bytecode; + sqInt offset; + + VM_LABEL(booleanCheatTrue); + /* assume next bytecode is jumpIfFalse (99%) */ + bytecode = byteAt(++localIP); + + /* begin internalPop: */ + localSP += 2 * BytesPerOop; + if ((bytecode < 173) + && (bytecode > 151)) { + if (bytecode < 160) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l197; + } + + /* short jumpIfFalse 152 - 159 */ + if (bytecode == 172) { + /* fetchByte */ + byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l197; + } + + /* long jumpIfFalse */ + if (bytecode > 167) { + offset = ((((usqInt)((bytecode - 168)) << 8))) + (byteAt(++localIP)); + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l197; + } + } + + /* not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result */ + currentBytecode = bytecode + GIV(bytecodeSetSelector); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(trueObj)); + } + /* end case */ +l197:; + break; + case 179: // bytecodePrimGreaterThan + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimGreaterThan); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheat: */ + if (rcvr > arg) { + goto booleanCheatTrue; + } + else { + /* goto booleanCheatFalse */ + } + goto l52; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatGreaterthanArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l52; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (6U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l52: + + booleanCheatFalse: + /* booleanCheatFalse */ + { + sqInt bytecode; + sqInt offset; + + VM_LABEL(booleanCheatFalse); + /* assume next bytecode is jumpIfFalse (99%) */ + bytecode = byteAt(++localIP); + + /* begin internalPop: */ + localSP += 2 * BytesPerOop; + if ((bytecode < 160) + && (bytecode > 151)) { + /* begin jump: */ + localIP = (localIP + (bytecode - 151)) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l198; + } + + /* short jumpIfFalse */ + if (bytecode == 172) { + offset = byteAt(++localIP); + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l198; + } + + /* long jumpIfFalse + not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result */ + currentBytecode = bytecode + GIV(bytecodeSetSelector); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(falseObj)); + } + /* end case */ +l198:; + break; + case 180: // bytecodePrimLessOrEqual + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimLessOrEqual); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheat: */ + if (rcvr <= arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l53; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatLessOrEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l53; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (8U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l53:; + break; + case 181: // bytecodePrimGreaterOrEqual + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimGreaterOrEqual); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheat: */ + if (rcvr >= arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l54; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatGreaterOrEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l54; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (10U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l54:; + break; + case 182: // bytecodePrimEqual + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimEqual); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* begin booleanCheat: */ + if (rcvr == arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l55; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l55; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (12U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l55:; + break; + case 183: // bytecodePrimNotEqual + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimNotEqual); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* begin booleanCheat: */ + if (rcvr != arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l56; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatFalse; + } + else { + goto booleanCheatTrue; + } + goto l56; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (14U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l56:; + break; + case 184: // bytecodePrimMultiply + case 360: // 104 bytecodePrimMultiply + { + sqInt arg; + double argDouble; + usqLong bits; + sqInt oop; + sqInt overflow; + sqInt rcvr; + double rcvrDouble; + sqInt result; + double resultDouble; + char *sp; + sqInt tagBits; + double value; + + VM_LABEL(bytecodePrimMultiply); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + rcvr = (rcvr >> 3); + arg = (arg >> 3); + overflow = (rcvr > 0 + ? (arg > 0 + ? rcvr > ((MaxSmallInteger) / arg) + : arg < ((MinSmallInteger) / rcvr)) + : (arg > 0 + ? rcvr < ((MinSmallInteger) / arg) + : (rcvr < 0) + && (arg < ((MaxSmallInteger) / rcvr)))); + if (!overflow) { + result = rcvr * arg; + oop = (((usqInt)result << 3) | 1); + + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),oop); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l57; + } + } + else { + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin primitiveFloatMultiply:byArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvr & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvrDouble = value; + goto l58; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvrDouble = ((double) ((rcvr >> 3)) ); + goto l58; + } + } + else { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvr + BaseHeaderSize, resultDouble); + rcvrDouble = resultDouble; + goto l58; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvrDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l58: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = arg & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(arg)); + bits = ((((usqInt)arg))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + argDouble = value; + goto l59; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + argDouble = ((double) ((arg >> 3)) ); + goto l59; + } + } + else { + if (((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(arg + BaseHeaderSize, resultDouble); + argDouble = resultDouble; + goto l59; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + argDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l59: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvrDouble * argDouble)); + GIV(stackPointer) = sp; + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l57; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (16U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l57:; + break; + case 185: // bytecodePrimDivide + case 361: // 105 bytecodePrimDivide + { + sqInt arg; + double argDouble; + usqLong bits; + sqInt rcvr; + double rcvrDouble; + sqInt result; + double resultDouble; + char *sp; + sqInt tagBits; + double value; + + VM_LABEL(bytecodePrimDivide); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + rcvr = (rcvr >> 3); + arg = (arg >> 3); + if ((arg != 0) + && ((rcvr % arg) == 0)) { + result = rcvr / arg; + + /* generates C / operation */ + if ((((((usqInt)(result)) >> 60) + 1) & 15) <= 1) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),(((usqInt)result << 3) | 1)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l60; + } + } + } + else { + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin primitiveFloatDivide:byArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvr & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvrDouble = value; + goto l61; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvrDouble = ((double) ((rcvr >> 3)) ); + goto l61; + } + } + else { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvr + BaseHeaderSize, resultDouble); + rcvrDouble = resultDouble; + goto l61; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvrDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l61: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = arg & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(arg)); + bits = ((((usqInt)arg))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + argDouble = value; + goto l62; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + argDouble = ((double) ((arg >> 3)) ); + goto l62; + } + } + else { + if (((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(arg + BaseHeaderSize, resultDouble); + argDouble = resultDouble; + goto l62; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + argDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l62: + + /* begin success: */ + if (!(argDouble != 0.0)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvrDouble / argDouble)); + GIV(stackPointer) = sp; + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l60; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (18U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l60:; + break; + case 186: // bytecodePrimMod + case 362: // 106 bytecodePrimMod + { + sqInt mod; + + VM_LABEL(bytecodePrimMod); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + mod = doPrimitiveModby(longAt(localSP + (1 * BytesPerOop)), longAt(localSP)); + if (!GIV(primFailCode)) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),(((usqInt)mod << 3) | 1)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l63; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (20U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l63:; + break; + case 187: // bytecodePrimMakePoint + case 363: // 107 bytecodePrimMakePoint + { + sqInt arg; + usqInt newObj; + usqInt numBytes; + usqInt pt; + sqInt rcvr; + + VM_LABEL(bytecodePrimMakePoint); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((/* isFloatOrInt: */ + (((rcvr & (tagMask())) != 0) + ? !(((rcvr & (characterTag())) != 0)) + : ((longAt((void *)(rcvr))) & (classIndexMask())) == ClassFloatCompactIndex)) + && (/* isFloatOrInt: */ + (((arg & (tagMask())) != 0) + ? !(((arg & (characterTag())) != 0)) + : ((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex))) { + /* begin eeInstantiatePoint */ + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert(((YIndex + 1) >= 0) + && ((knownClassAtIndex(ClassPointCompactIndex)) != GIV(nilObj))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(knownClassAtIndex(ClassPointCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert((YIndex + 1) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (((YIndex + 1) < 1 + ? 8 /* allocationUnit */ + : (YIndex + 1) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + pt = 0; + goto l65; + } + } + long64Atput((void *)(newObj),((((((usqLong) (YIndex + 1))) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + ClassPointCompactIndex); + GIV(freeStart) += numBytes; + pt = newObj; + /* end eeInstantiatePoint */ +l65: + + /* No need to check since new object is always new. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pt)) + && (!(isForwarded(pt)))); + assert(validStorePointerUncheckedArgs(XIndex, pt, rcvr)); + longAtput((void *)((pt + BaseHeaderSize) + ((((usqInt)(XIndex) << (shiftForWord()))))),rcvr); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pt)) + && (!(isForwarded(pt)))); + assert(validStorePointerUncheckedArgs(YIndex, pt, arg)); + longAtput((void *)((pt + BaseHeaderSize) + ((((usqInt)(YIndex) << (shiftForWord()))))),arg); + + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),pt); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l64; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (22U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l64:; + break; + case 188: // bytecodePrimBitShift + case 364: // 108 bytecodePrimBitShift + { + sqInt integerArgument; + sqInt integerReceiver; + sqInt shifted; + + VM_LABEL(bytecodePrimBitShift); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin primitiveBitShift */ + integerArgument = longAt(GIV(stackPointer)); + if (!((((integerArgument) & 7) == 1))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + goto l67; + } + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + integerReceiver = signed64BitValueOf(integerReceiver); + if (!GIV(primFailCode)) { + if (((integerArgument = (integerArgument >> 3))) >= 0) { + if (!(integerArgument <= 61 /* numSmallIntegerBits */)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + goto l67; + } + shifted = ((sqInt)((usqInt)(integerReceiver) << integerArgument)); + if (!(integerReceiver == ((shifted) >> integerArgument))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + goto l67; + } + } + else { + if (!(integerArgument >= (-61 /* numSmallIntegerBits */))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + goto l67; + } + shifted = (integerReceiver) >> (0 - integerArgument); + } + + /* Left shift -- must fail bits would be lost + Right shift -- OK to lose bits */ + shifted = ((((((usqInt)(shifted)) >> 60) + 1) & 15) <= 1 + ? (((usqInt)shifted << 3) | 1) + : signed64BitIntegerFor(shifted)); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),shifted); + } + /* end primitiveBitShift */ +l67: + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l66; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (24U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l66:; + break; + case 189: // bytecodePrimDiv + case 365: // 109 bytecodePrimDiv + { + sqInt quotient; + + VM_LABEL(bytecodePrimDiv); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + quotient = doPrimitiveDivby(longAt(localSP + (1 * BytesPerOop)), longAt(localSP)); + if (!GIV(primFailCode)) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),(((usqInt)quotient << 3) | 1)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l68; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (26U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l68:; + break; + case 190: // bytecodePrimBitAnd + case 366: // 110 bytecodePrimBitAnd + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimBitAnd); + arg = longAt(localSP); + rcvr = longAt(localSP + (1 * BytesPerOop)); + if (((((arg) & 7) == 1)) + && ((((rcvr) & 7) == 1))) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),arg & rcvr); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l69; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + primitiveBitAnd(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l69; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (28U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l69:; + break; + case 191: // bytecodePrimBitOr + case 367: // 111 bytecodePrimBitOr + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimBitOr); + arg = longAt(localSP); + rcvr = longAt(localSP + (1 * BytesPerOop)); + if (((((arg) & 7) == 1)) + && ((((rcvr) & 7) == 1))) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),arg | rcvr); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l70; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + primitiveBitOr(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l70; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (30U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l70:; + break; + case 192: // bytecodePrimAt + case 368: // 112 bytecodePrimAt + { + VM_LABEL(bytecodePrimAt); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (32U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + break; + case 193: // bytecodePrimAtPut + case 369: // 113 bytecodePrimAtPut + { + VM_LABEL(bytecodePrimAtPut); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (34U << (shiftForWord())))); + GIV(argumentCount) = 2; + goto normalSend; + } + break; + case 194: // bytecodePrimSize + case 370: // 114 bytecodePrimSize + { + sqInt ccIndex; + sqInt fmt; + sqInt isArray; + sqInt isString; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt sz; + + VM_LABEL(bytecodePrimSize); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(localSP); + + /* Shortcut the mega-lookup for ByteString and Array, the two big consumers of cycles + here. Both of these have compact class indices and neither has any added fields. */ + + /* begin isInstanceOfClassByteString: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((rcvr & (tagMask())) != 0)) { + isString = 0; + goto l72; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isString = ClassByteStringCompactIndex == ccIndex; + /* end isInstanceOfClassByteString: */ +l72: + if (isString) { + /* begin lengthOf: */ + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + sz = numSlots; + goto l73; + } + if (fmt >= (firstByteFormat())) { + sz = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l73; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + sz = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l73; + } + if (fmt >= (firstLongFormat())) { + sz = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l73; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + sz = numSlots; + goto l73; + } + + /* fmt = self forwardedFormat */ + sz = 0; + /* end lengthOf: */ +l73: + + /* internalStackTopPut: */ + longAtput(localSP,(((usqInt)sz << 3) | 1)); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l71; + } + + /* begin isInstanceOfClassArray: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((rcvr & (tagMask())) != 0)) { + isArray = 0; + goto l74; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isArray = ClassArrayCompactIndex == ccIndex; + /* end isInstanceOfClassArray: */ +l74: + if (isArray) { + /* begin lengthOf: */ + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + sz = numSlots; + goto l75; + } + if (fmt >= (firstByteFormat())) { + sz = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l75; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + sz = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l75; + } + if (fmt >= (firstLongFormat())) { + sz = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l75; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + sz = numSlots; + goto l75; + } + + /* fmt = self forwardedFormat */ + sz = 0; + /* end lengthOf: */ +l75: + + /* internalStackTopPut: */ + longAtput(localSP,(((usqInt)sz << 3) | 1)); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l71; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (36U << (shiftForWord())))); + GIV(argumentCount) = 0; + goto normalSend; + } + /* end case */ +l71:; + break; + case 195: // bytecodePrimNext + case 371: // 115 bytecodePrimNext + { + VM_LABEL(bytecodePrimNext); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (38U << (shiftForWord())))); + GIV(argumentCount) = ((fetchPointerofObject(39, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))))) >> 3); + goto normalSend; + } + break; + case 196: // bytecodePrimNextPut + case 372: // 116 bytecodePrimNextPut + { + VM_LABEL(bytecodePrimNextPut); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (40U << (shiftForWord())))); + GIV(argumentCount) = ((fetchPointerofObject(41, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))))) >> 3); + goto normalSend; + } + break; + case 197: // bytecodePrimAtEnd + case 373: // 117 bytecodePrimAtEnd + { + VM_LABEL(bytecodePrimAtEnd); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (42U << (shiftForWord())))); + GIV(argumentCount) = ((fetchPointerofObject(43, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))))) >> 3); + goto normalSend; + } + break; + case 198: // bytecodePrimIdentical + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimIdentical); + rcvr = longAt(localSP + (1 * BytesPerOop)); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = handleSpecialSelectorSendFaultForfpsp(rcvr, localFP, localSP); + } + arg = longAt(localSP); + if (/* isOopForwarded: */ + ((!(arg & (tagMask())))) + && ((!((longAt((void *)(arg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + arg = handleSpecialSelectorSendFaultForfpsp(arg, localFP, localSP); + } + + /* begin booleanCheat: */ + if (rcvr == arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + } + break; + case 199: // bytecodePrimClass + case 375: // 119 bytecodePrimClass + { + sqInt aValue; + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(bytecodePrimClass); + rcvr = longAt(localSP); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = handleSpecialSelectorSendFaultForfpsp(rcvr, localFP, localSP); + } + aValue = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)); + + /* begin internalStackTopPut: */ + longAtput(localSP,aValue); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 200: // bytecodePrimNotIdentical + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimNotIdentical); + rcvr = longAt(localSP + (1 * BytesPerOop)); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = handleSpecialSelectorSendFaultForfpsp(rcvr, localFP, localSP); + } + arg = longAt(localSP); + if (/* isOopForwarded: */ + ((!(arg & (tagMask())))) + && ((!((longAt((void *)(arg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + arg = handleSpecialSelectorSendFaultForfpsp(arg, localFP, localSP); + } + + /* begin booleanCheat: */ + if (rcvr != arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + } + break; + case 201: // bytecodePrimValue + case 377: // 121 bytecodePrimValue + { + sqInt ccIndex; + sqInt isBlock; + sqInt rcvr; + + VM_LABEL(bytecodePrimValue); + rcvr = longAt(localSP); + GIV(argumentCount) = 0; + + /* begin isInstanceOfClassBlockClosure: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((rcvr & (tagMask())) != 0)) { + isBlock = 0; + goto l77; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isBlock = ClassBlockClosureCompactIndex == ccIndex; + /* end isInstanceOfClassBlockClosure: */ +l77: + if (isBlock) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + primitiveClosureValue(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l76; + } + GIV(primFailCode) = 0; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (50U << (shiftForWord())))); + goto normalSend; + } + /* end case */ +l76:; + break; + case 202: // bytecodePrimValueWithArg + case 378: // 122 bytecodePrimValueWithArg + { + sqInt ccIndex; + sqInt isBlock; + sqInt rcvr; + + VM_LABEL(bytecodePrimValueWithArg); + rcvr = longAt(localSP + (1 * BytesPerOop)); + GIV(argumentCount) = 1; + + /* begin isInstanceOfClassBlockClosure: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((rcvr & (tagMask())) != 0)) { + isBlock = 0; + goto l79; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isBlock = ClassBlockClosureCompactIndex == ccIndex; + /* end isInstanceOfClassBlockClosure: */ +l79: + if (isBlock) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + primitiveClosureValue(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l78; + } + GIV(primFailCode) = 0; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (52U << (shiftForWord())))); + goto normalSend; + } + /* end case */ +l78:; + break; + case 203: // bytecodePrimDo + case 379: // 123 bytecodePrimDo + { + VM_LABEL(bytecodePrimDo); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (54U << (shiftForWord())))); + GIV(argumentCount) = ((fetchPointerofObject(55, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))))) >> 3); + goto normalSend; + } + break; + case 204: // bytecodePrimNew + case 380: // 124 bytecodePrimNew + { + VM_LABEL(bytecodePrimNew); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (56U << (shiftForWord())))); + GIV(argumentCount) = 0; + goto normalSend; + } + break; + case 205: // bytecodePrimNewWithArg + case 381: // 125 bytecodePrimNewWithArg + { + VM_LABEL(bytecodePrimNewWithArg); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (58U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + break; + case 206: // bytecodePrimPointX + case 382: // 126 bytecodePrimPointX + { + sqInt ccIndex; + sqInt isClassOfNonImmequalTocompactClassIndexRV; + sqInt rcvr; + + VM_LABEL(bytecodePrimPointX); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(localSP); + if ((!(rcvr & (tagMask())))) { + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isClassOfNonImmequalTocompactClassIndexRV = ClassPointCompactIndex == ccIndex; + if (isClassOfNonImmequalTocompactClassIndexRV) { + longAtput(localSP,longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(XIndex) << (shiftForWord()))))))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l80; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (60U << (shiftForWord())))); + GIV(argumentCount) = 0; + goto normalSend; + } + /* end case */ +l80:; + break; + case 207: // bytecodePrimPointY + case 383: // 127 bytecodePrimPointY + { + sqInt ccIndex; + sqInt isClassOfNonImmequalTocompactClassIndexRV; + sqInt rcvr; + + VM_LABEL(bytecodePrimPointY); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(localSP); + if ((!(rcvr & (tagMask())))) { + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isClassOfNonImmequalTocompactClassIndexRV = ClassPointCompactIndex == ccIndex; + if (isClassOfNonImmequalTocompactClassIndexRV) { + longAtput(localSP,longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(YIndex) << (shiftForWord()))))))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l81; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (0x3EU << (shiftForWord())))); + GIV(argumentCount) = 0; + goto normalSend; + } + /* end case */ +l81:; + break; + case 208: // sendLiteralSelector0ArgsBytecode + case 209: // sendLiteralSelector0ArgsBytecode + case 210: // sendLiteralSelector0ArgsBytecode + case 211: // sendLiteralSelector0ArgsBytecode + case 212: // sendLiteralSelector0ArgsBytecode + case 213: // sendLiteralSelector0ArgsBytecode + case 214: // sendLiteralSelector0ArgsBytecode + case 215: // sendLiteralSelector0ArgsBytecode + case 216: // sendLiteralSelector0ArgsBytecode + case 217: // sendLiteralSelector0ArgsBytecode + case 218: // sendLiteralSelector0ArgsBytecode + case 219: // sendLiteralSelector0ArgsBytecode + case 220: // sendLiteralSelector0ArgsBytecode + case 221: // sendLiteralSelector0ArgsBytecode + case 222: // sendLiteralSelector0ArgsBytecode + case 223: // sendLiteralSelector0ArgsBytecode + case 384: // 128 sendLiteralSelector0ArgsBytecode + case 385: // 129 sendLiteralSelector0ArgsBytecode + case 386: // 130 sendLiteralSelector0ArgsBytecode + case 387: // 131 sendLiteralSelector0ArgsBytecode + case 388: // 132 sendLiteralSelector0ArgsBytecode + case 389: // 133 sendLiteralSelector0ArgsBytecode + case 390: // 134 sendLiteralSelector0ArgsBytecode + case 391: // 135 sendLiteralSelector0ArgsBytecode + case 392: // 136 sendLiteralSelector0ArgsBytecode + case 393: // 137 sendLiteralSelector0ArgsBytecode + case 394: // 138 sendLiteralSelector0ArgsBytecode + case 395: // 139 sendLiteralSelector0ArgsBytecode + case 396: // 140 sendLiteralSelector0ArgsBytecode + case 397: // 141 sendLiteralSelector0ArgsBytecode + case 398: // 142 sendLiteralSelector0ArgsBytecode + case 399: // 143 sendLiteralSelector0ArgsBytecode + { + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(sendLiteralSelector0ArgsBytecode); + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((currentBytecode & 15) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = 0; + rcvr = longAt(localSP); + lkupClassTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + assert(lkupClassTag != (nilObject())); + goto commonSendOrdinary; + } + break; + case 224: // sendLiteralSelector1ArgBytecode + case 225: // sendLiteralSelector1ArgBytecode + case 226: // sendLiteralSelector1ArgBytecode + case 227: // sendLiteralSelector1ArgBytecode + case 228: // sendLiteralSelector1ArgBytecode + case 229: // sendLiteralSelector1ArgBytecode + case 230: // sendLiteralSelector1ArgBytecode + case 231: // sendLiteralSelector1ArgBytecode + case 232: // sendLiteralSelector1ArgBytecode + case 233: // sendLiteralSelector1ArgBytecode + case 234: // sendLiteralSelector1ArgBytecode + case 235: // sendLiteralSelector1ArgBytecode + case 236: // sendLiteralSelector1ArgBytecode + case 237: // sendLiteralSelector1ArgBytecode + case 238: // sendLiteralSelector1ArgBytecode + case 239: // sendLiteralSelector1ArgBytecode + case 400: // 144 sendLiteralSelector1ArgBytecode + case 401: // 145 sendLiteralSelector1ArgBytecode + case 402: // 146 sendLiteralSelector1ArgBytecode + case 403: // 147 sendLiteralSelector1ArgBytecode + case 404: // 148 sendLiteralSelector1ArgBytecode + case 405: // 149 sendLiteralSelector1ArgBytecode + case 406: // 150 sendLiteralSelector1ArgBytecode + case 407: // 151 sendLiteralSelector1ArgBytecode + case 408: // 152 sendLiteralSelector1ArgBytecode + case 409: // 153 sendLiteralSelector1ArgBytecode + case 410: // 154 sendLiteralSelector1ArgBytecode + case 411: // 155 sendLiteralSelector1ArgBytecode + case 412: // 156 sendLiteralSelector1ArgBytecode + case 413: // 157 sendLiteralSelector1ArgBytecode + case 414: // 158 sendLiteralSelector1ArgBytecode + case 415: // 159 sendLiteralSelector1ArgBytecode + { + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(sendLiteralSelector1ArgBytecode); + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((currentBytecode & 15) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = 1; + rcvr = longAt(localSP + (1 * BytesPerOop)); + lkupClassTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + assert(lkupClassTag != (nilObject())); + goto commonSendOrdinary; + } + break; + case 240: // sendLiteralSelector2ArgsBytecode + case 241: // sendLiteralSelector2ArgsBytecode + case 242: // sendLiteralSelector2ArgsBytecode + case 243: // sendLiteralSelector2ArgsBytecode + case 244: // sendLiteralSelector2ArgsBytecode + case 245: // sendLiteralSelector2ArgsBytecode + case 246: // sendLiteralSelector2ArgsBytecode + case 247: // sendLiteralSelector2ArgsBytecode + case 248: // sendLiteralSelector2ArgsBytecode + case 249: // sendLiteralSelector2ArgsBytecode + case 250: // sendLiteralSelector2ArgsBytecode + case 251: // sendLiteralSelector2ArgsBytecode + case 252: // sendLiteralSelector2ArgsBytecode + case 253: // sendLiteralSelector2ArgsBytecode + case 254: // sendLiteralSelector2ArgsBytecode + case 255: // sendLiteralSelector2ArgsBytecode + case 416: // 160 sendLiteralSelector2ArgsBytecode + case 417: // 161 sendLiteralSelector2ArgsBytecode + case 418: // 162 sendLiteralSelector2ArgsBytecode + case 419: // 163 sendLiteralSelector2ArgsBytecode + case 420: // 164 sendLiteralSelector2ArgsBytecode + case 421: // 165 sendLiteralSelector2ArgsBytecode + case 422: // 166 sendLiteralSelector2ArgsBytecode + case 423: // 167 sendLiteralSelector2ArgsBytecode + case 424: // 168 sendLiteralSelector2ArgsBytecode + case 425: // 169 sendLiteralSelector2ArgsBytecode + case 426: // 170 sendLiteralSelector2ArgsBytecode + case 427: // 171 sendLiteralSelector2ArgsBytecode + case 428: // 172 sendLiteralSelector2ArgsBytecode + case 429: // 173 sendLiteralSelector2ArgsBytecode + case 430: // 174 sendLiteralSelector2ArgsBytecode + case 431: // 175 sendLiteralSelector2ArgsBytecode + { + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(sendLiteralSelector2ArgsBytecode); + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((currentBytecode & 15) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = 2; + rcvr = longAt(localSP + (2 * BytesPerOop)); + lkupClassTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + assert(lkupClassTag != (nilObject())); + goto commonSendOrdinary; + } + break; + case 338: // 82 extPushPseudoVariable + { + sqInt objOop; + sqInt theThingToPush; + + VM_LABEL(extPushPseudoVariable); + switch (extB) { + case 0: + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + theThingToPush = longAt(localFP + FoxThisContext); + goto l82; + } + theThingToPush = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l82:; + break; + case 1: + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + theThingToPush = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + break; + default: + goto respondToUnknownBytecode; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),theThingToPush); + extB = 0; + numExtB = 0; + } + break; + case 340: // 84 unknownBytecode + case 341: // 85 unknownBytecode + case 342: // 86 unknownBytecode + case 343: // 87 unknownBytecode + case 473: // 217 unconditionalTrapBytecode + case 474: // 218 unknownBytecode + case 475: // 219 unknownBytecode + case 476: // 220 unknownBytecode + case 477: // 221 unknownBytecode + case 478: // 222 unknownBytecode + case 479: // 223 unknownBytecode + case 486: // 230 unknownBytecode + case 502: // 246 unknownBytecode + case 503: // 247 unknownBytecode + case 510: // 254 unknownBytecode + case 511: // 255 unknownBytecode + { + VM_LABEL(unknownBytecode); + goto respondToUnknownBytecode; + } + break; + case 344: // 88 returnReceiver + { + VM_LABEL(returnReceiver1); + localReturnValue = longAt(localFP + FoxIFReceiver); + goto commonReturn; + } + break; + case 349: // 93 returnNilFromBlock + { + VM_LABEL(returnNilFromBlock); + localReturnValue = GIV(nilObj); + goto commonCallerReturn; + } + break; + case 350: // 94 returnTopFromBlock + { + VM_LABEL(returnTopFromBlock1); + localReturnValue = longAt(localSP); + goto commonCallerReturn; + } + break; + case 351: // 95 extNopBytecode + { + VM_LABEL(extNopBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + numExtB = (extA = (extB = 0)); + } + break; + case 354: // 98 bytecodePrimLessThanSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimLessThanSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheatSistaV1: */ + if (rcvr < arg) { + /* goto booleanCheatTrueSistaV1 */ + } + else { + goto booleanCheatFalseSistaV1; + } + goto l89; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatLessthanArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l89; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (4U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l89: + + booleanCheatTrueSistaV1: + /* booleanCheatTrueSistaV1 */ + { + sqInt bytecode; + sqInt offset; + + VM_LABEL(booleanCheatTrueSistaV1); + /* assume next bytecode is jumpIfFalse (99%) */ + bytecode = byteAt(++localIP); + + /* begin internalPop: */ + localSP += 2 * BytesPerOop; + if (bytecode >= 192) { + if (bytecode <= 199) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l199; + } + + /* short jumpIfFalse 192 - 199 */ + if (bytecode == 239) { + /* fetchByte */ + byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l199; + } + + /* long jumpIfFalse */ + if (bytecode == 238) { + offset = byteAt(++localIP); + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l199; + } + } + + /* not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result */ + currentBytecode = bytecode + GIV(bytecodeSetSelector); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(trueObj)); + } + /* end case */ +l199:; + break; + case 355: // 99 bytecodePrimGreaterThanSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimGreaterThanSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheatSistaV1: */ + if (rcvr > arg) { + goto booleanCheatTrueSistaV1; + } + else { + /* goto booleanCheatFalseSistaV1 */ + } + goto l90; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatGreaterthanArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l90; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (6U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l90: + + booleanCheatFalseSistaV1: + /* booleanCheatFalseSistaV1 */ + { + sqInt bytecode; + sqInt offset; + + VM_LABEL(booleanCheatFalseSistaV1); + /* assume next bytecode is jumpIfFalse (99%) */ + bytecode = byteAt(++localIP); + + /* begin internalPop: */ + localSP += 2 * BytesPerOop; + if ((bytecode < 199) + && (bytecode > 191)) { + /* begin jump: */ + localIP = (localIP + (bytecode - 191)) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l200; + } + + /* short jumpIfFalse */ + if (bytecode == 239) { + offset = byteAt(++localIP); + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l200; + } + + /* long jumpIfFalse + not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result */ + currentBytecode = bytecode + GIV(bytecodeSetSelector); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(falseObj)); + } + /* end case */ +l200:; + break; + case 356: // 100 bytecodePrimLessOrEqualSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimLessOrEqualSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheatSistaV1: */ + if (rcvr <= arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l91; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatLessOrEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l91; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (8U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l91:; + break; + case 357: // 101 bytecodePrimGreaterOrEqualSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimGreaterOrEqualSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheatSistaV1: */ + if (rcvr >= arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l92; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatGreaterOrEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l92; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (10U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l92:; + break; + case 358: // 102 bytecodePrimEqualSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimEqualSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* begin booleanCheatSistaV1: */ + if (rcvr == arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l93; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l93; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (12U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l93:; + break; + case 359: // 103 bytecodePrimNotEqualSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimNotEqualSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* begin booleanCheatSistaV1: */ + if (rcvr != arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l94; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatFalseSistaV1; + } + else { + goto booleanCheatTrueSistaV1; + } + goto l94; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (14U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l94:; + break; + case 374: // 118 bytecodePrimIdenticalSistaV1 + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimIdenticalSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = handleSpecialSelectorSendFaultForfpsp(rcvr, localFP, localSP); + } + if (/* isOopForwarded: */ + ((!(arg & (tagMask())))) + && ((!((longAt((void *)(arg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + arg = handleSpecialSelectorSendFaultForfpsp(arg, localFP, localSP); + } + + /* begin booleanCheatSistaV1: */ + if (rcvr == arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + } + break; + case 376: // 120 bytecodePrimNotIdenticalSistaV1 + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimNotIdenticalSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = handleSpecialSelectorSendFaultForfpsp(rcvr, localFP, localSP); + } + if (/* isOopForwarded: */ + ((!(arg & (tagMask())))) + && ((!((longAt((void *)(arg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + arg = handleSpecialSelectorSendFaultForfpsp(arg, localFP, localSP); + } + + /* begin booleanCheatSistaV1: */ + if (rcvr != arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + } + break; + case 440: // 184 shortConditionalJumpTrue + case 441: // 185 shortConditionalJumpTrue + case 442: // 186 shortConditionalJumpTrue + case 443: // 187 shortConditionalJumpTrue + case 444: // 188 shortConditionalJumpTrue + case 445: // 189 shortConditionalJumpTrue + case 446: // 190 shortConditionalJumpTrue + case 447: // 191 shortConditionalJumpTrue + { + sqInt boolean; + sqInt offset; + + VM_LABEL(shortConditionalJumpTrue); + offset = (currentBytecode & 7) + 1; + + /* begin jumplfTrueBy: */ + boolean = longAt(localSP); + if (boolean == GIV(trueObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(falseObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l120; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfTrueBy: */ +l120:; + } + break; + case 480: // 224 extABytecode + { + VM_LABEL(extABytecode); + extA = (((((usqInt)(extA) << 8)))) + (byteAt(++localIP)); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 481: // 225 extBBytecode + { + sqInt byte; + + VM_LABEL(extBBytecode); + byte = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + extB = ((numExtB == 0) + && (byte > 0x7F) + ? byte - 0x100 + : (((((usqInt)(extB) << 8)))) + byte); + numExtB += 1; + } + break; + case 482: // 226 extPushReceiverVariableBytecode + { + sqInt callerContextOrNil; + char *callerFP; + sqInt index; + sqInt obj; + sqInt object; + sqInt senderOop; + char *spouseFP; + StackPage *thePage; + sqInt value; + + VM_LABEL(extPushReceiverVariableBytecode); + index = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + extA = 0; + + /* begin pushMaybeContextReceiverVariable: */ + obj = longAt(localFP + FoxIFReceiver); + if ((index <= StackPointerIndex) + && (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin instVar:ofContext: */ + assert(index < MethodIndex); + assert(isContext(obj)); + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + if (!(((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + value = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if ((index == InstructionPointerIndex) + && (((((value) & 7) == 1)) + && ((((int) value)) < 0))) { + /* begin internalMustMapMachineCodePC:context: */ + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + value = mustMapMachineCodePCcontext((value >> 3), obj); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + } + object = value; + goto l129; + } + if (isWidowedContext(obj)) { + object = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + goto l129; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!index) { + /* begin ensureCallerContext: */ + callerFP = ((char *)(longAt(spouseFP + FoxSavedFP))); + if (!callerFP) { + /* begin frameCallerContext: */ + assert(isBaseFrame(spouseFP)); + thePage = stackPageAtpages(pageIndexFor(spouseFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + object = callerContextOrNil; + goto l129; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP))); + object = longAt(callerFP + FoxThisContext); + goto l129; + } + object = marryFrameSP( + callerFP, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(spouseFP))), + (spouseFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(spouseFP))->cmNumArgs) + : byteAt((spouseFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + goto l129; + } + if (index == StackPointerIndex) { + assert((ReceiverIndex + (stackPointerIndexForFrame(spouseFP))) < (lengthOf(obj))); + object = (((usqInt)(stackPointerIndexForFrame(spouseFP)) << 3) | 1); + goto l129; + } + if (index == InstructionPointerIndex) { + object = instructionPointerForFramecurrentFPcurrentIP(spouseFP, localFP, oopForPointer(localIP)); + goto l129; + } + error("bad index"); + object = 0; + /* end instVar:ofContext: */ +l129: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + else { + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))))); + } + } + break; + case 483: // 227 extPushLiteralVariableBytecode + { + sqInt index; + sqInt litVar; + + VM_LABEL(extPushLiteralVariableBytecode); + index = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + extA = 0; + + /* begin pushLiteralVariable: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((index + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(index + LiteralStart, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))))); + } + break; + case 484: // 228 extPushLiteralBytecode + { + sqInt index; + sqInt object; + + VM_LABEL(extPushLiteralBytecode); + index = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + extA = 0; + + /* begin pushLiteralConstant: */ + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((index + LiteralStart)) << (shiftForWord())))))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 485: // 229 longPushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt index; + + VM_LABEL(longPushTemporaryVariableBytecode); + index = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),/* itemporary:in: */ + (index < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - index) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - index) * BytesPerWord)))); + } + break; + case 488: // 232 extPushIntegerBytecode + { + sqInt value; + + VM_LABEL(extPushIntegerBytecode); + value = (byteAt(++localIP)) + ((((usqInt)(extB) << 8))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + extB = 0; + numExtB = 0; + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),(((usqInt)value << 3) | 1)); + } + break; + case 489: // 233 extPushCharacterBytecode + { + sqInt object; + sqInt value; + + VM_LABEL(extPushCharacterBytecode); + value = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = ((((usqInt)(value) << (numTagBits())))) + (characterTag()); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + extA = 0; + } + break; + case 490: // 234 extSendBytecode + { + sqInt byte; + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(extSendBytecode); + byte = byteAt(++localIP); + + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((((((usqInt)(byte)) >> 3) + ((((usqInt)(extA) << 5)))) + LiteralStart)) << (shiftForWord())))))); + extA = 0; + GIV(argumentCount) = (byte & 7) + ((((usqInt)(extB) << 3))); + extB = 0; + numExtB = 0; + rcvr = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + lkupClassTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + goto commonSendOrdinary; + } + break; + case 491: // 235 extSendSuperBytecode + { + sqInt byte; + + VM_LABEL(extSendSuperBytecode); + byte = byteAt(++localIP); + + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((((((usqInt)(byte)) >> 3) + ((((usqInt)(extA) << 5)))) + LiteralStart)) << (shiftForWord())))))); + extA = 0; + if (extB >= 64) { + GIV(argumentCount) = (byte & 7) + ((((usqInt)((extB - 64)) << 3))); + extB = 0; + numExtB = 0; + + /* goto directedSuperclassSend */ + goto l131; + } + GIV(argumentCount) = (byte & 7) + ((((usqInt)(extB) << 3))); + extB = 0; + numExtB = 0; + goto superclassSend; + } + /* end case */ +l131: + + directedSuperclassSend: + /* directedSuperclassSend */ + { + sqInt aValue; + sqInt class; + sqInt err; + int hash; + sqInt objOop; + sqInt referent; + sqInt superclass; + + VM_LABEL(directedSuperclassSend); + /* begin internalPopStack */ + class = longAt(localSP); + localSP += BytesPerOop; + if ((!((longAt((void *)(class))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(class)); + referent = longAt((void *)((class + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + class = referent; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + superclass = longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(superclass)); + if ((!((longAt((void *)(superclass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + superclass = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, class, superclass); + } + + /* classTagForClass: uses ensureBehaviorHash: */ + + /* begin classTagForClass: */ + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(superclass)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + lkupClassTag = ((hash = (long32At((void *)(superclass + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(superclass) + ? ((err = enterIntoClassTable(superclass)) + ? -err + : (long32At((void *)(superclass + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + + /* To maintain the invariant that all receivers are unforwarded we need an explicit + read barrier in the super send cases. Even though we always follow receivers + on become e.g. super doSomethingWith: (self become: other) forwards the receiver + self pushed on the stack. */ + + /* begin ensureReceiverUnforwarded */ + if (/* isOopForwarded: */ + ((!((longAt(localSP + (GIV(argumentCount) * BytesPerOop))) & (tagMask())))) + && ((!((longAt((void *)(longAt(localSP + (GIV(argumentCount) * BytesPerOop))))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + referent = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + aValue = referent; + + /* begin internalStackValue:put: */ + longAtput(localSP + (GIV(argumentCount) * BytesPerOop),aValue); + } + assert(lkupClassTag != (nilObject())); + goto commonSendOrdinary; + } + break; + case 492: // 236 callMappedInlinedPrimitive + { + VM_LABEL(callMappedInlinedPrimitive); + localIP -= 2; + goto respondToUnknownBytecode; + } + break; + case 493: // 237 extUnconditionalJump + { + sqInt backwardJumpCountByte; + sqInt bcpcDelta; + sqInt byte; + sqInt header; + sqInt methodHeader; + sqInt offset; + sqInt switched; + + VM_LABEL(extUnconditionalJump); + byte = byteAt(++localIP); + offset = byte + ((((usqInt)(extB) << 8))); + bcpcDelta = (offset < 0 + ? numExtB * 2 + : 0); + extB = 0; + numExtB = 0; + localIP += offset; + + /* begin ifBackwardsCheckForEvents: */ + if ((offset + bcpcDelta) >= 0) { + goto l132; + } + if (localSP < GIV(stackLimit)) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + switched = checkForEventsMayContextSwitch(1); + returnToExecutivepostContextSwitch(1, switched); + browserPluginReturnIfNeeded(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (switched) { + goto l132; + } + } + + /* We use the least significant byte of the flags word (which is marked as an immediate) and + subtract two each time to avoid disturbing the least significant tag bit. Since the byte is + initialized to 1 (on frame build), on first decrement it will become -1. Trip when it reaches 1 again. */ + backwardJumpCountByte = byteAt(localFP + ((VMBIGENDIAN + ? (FoxIFrameFlags + BytesPerWord) - 1 + : FoxIFrameFlags))); + if (((backwardJumpCountByte -= 2)) == 1) { + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + attemptToSwitchToMachineCode(((((oopForPointer(localIP)) - (offset + bcpcDelta)) - GIV(method)) - BaseHeaderSize) - 1); + } + + /* can't cog method; avoid asking to cog it again for the longest possible time. */ + backwardJumpCountByte = 0x7F; + } + else { + if (backwardJumpCountByte == -1) { + assert(minBackwardJumpCountForCompile <= 128); + backwardJumpCountByte = ((((usqInt)((minBackwardJumpCountForCompile - 1)) << 1))) + 1; + } + } + + /* begin iframeBackwardBranchByte:put: */ + byteAtput(localFP + ((VMBIGENDIAN + ? (FoxIFrameFlags + BytesPerWord) - 1 + : FoxIFrameFlags)),backwardJumpCountByte); + /* end ifBackwardsCheckForEvents: */ +l132: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 494: // 238 extJumpIfTrue + { + sqInt boolean; + sqInt byte; + sqInt offset; + + VM_LABEL(extJumpIfTrue); + byte = byteAt(++localIP); + offset = byte + ((((usqInt)(extB) << 8))); + numExtB = (extB = (extA = 0)); + + /* begin jumplfTrueBy: */ + boolean = longAt(localSP); + if (boolean == GIV(trueObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(falseObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l133; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfTrueBy: */ +l133:; + } + break; + case 495: // 239 extJumpIfFalse + { + sqInt boolean; + sqInt byte; + sqInt offset; + + VM_LABEL(extJumpIfFalse); + byte = byteAt(++localIP); + offset = byte + ((((usqInt)(extB) << 8))); + numExtB = (extB = (extA = 0)); + + /* begin jumplfFalseBy: */ + boolean = longAt(localSP); + if (boolean == GIV(falseObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(trueObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l134; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfFalseBy: */ +l134:; + } + break; + case 496: // 240 extStoreAndPopReceiverVariableBytecode + { + sqInt obj; + int onCurrentPage; + sqInt senderOop; + char *theFP; + StackPage *thePage; + sqInt value; + sqInt variableIndex; + + VM_LABEL(extStoreAndPopReceiverVariableBytecode); + variableIndex = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + extA = 0; + value = longAt(localSP); + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + obj = longAt(localFP + FoxIFReceiver); + if ((variableIndex <= ReceiverIndex) + && ((((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + || (ismethodAssignmentToContextWithMachineCodePC(variableIndex, value))))) { + /* begin instVar:ofContext:put: */ + assert(isMarriedOrWidowedContext(obj)); + assert(!((isObjImmutable(obj)))); + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(obj))))) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, value)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),value); + if (variableIndex == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + goto l135; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!variableIndex) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + onCurrentPage = thePage == GIV(stackPage); + storeSenderOfFramewithValue(theFP, value); + if (onCurrentPage) { + localFP = (GIV(stackPage)->headFP); + localSP = (GIV(stackPage)->headSP); + } + else { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + goto l135; + } + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + externalDivorceFrameandContext(theFP, obj); + if (ismethodAssignmentToContextWithMachineCodePC(variableIndex, obj)) { + ensureContextHasBytecodePC(obj); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, value)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),value); + if (variableIndex == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + + /* Assigning various fields can force a divorce which can change the stackPage. */ + markStackPageMostRecentlyUsed(GIV(stackPage)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 1, __LINE__); + /* end instVar:ofContext:put: */ +l135:; + } + else { + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(obj)))); + if ((((usqInt)((byteAt((void *)(obj + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),obj); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((usqInt)(variableIndex + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l136; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, value)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l136:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 497: // 241 extStoreAndPopLiteralVariableBytecode + { + sqInt litVar; + sqInt value; + sqInt variableIndex; + + VM_LABEL(extStoreAndPopLiteralVariableBytecode); + variableIndex = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + value = longAt(localSP); + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + extA = 0; + + /* begin storeLiteralVariable:withValue: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((variableIndex + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(variableIndex + LiteralStart, GIV(method), litVar); + } + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(litVar)))); + if ((((usqInt)((byteAt((void *)(litVar + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),litVar); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((usqInt)2 /* (ValueIndex + 1) */ << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l137; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ValueIndex, litVar, value)); + assert(isNonImmediate(litVar)); + if (oopisGreaterThanOrEqualTo(litVar, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(litVar + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(litVar); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l137: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 498: // 242 longStoreAndPopTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt index; + + VM_LABEL(longStoreAndPopTemporaryVariableBytecode); + /* begin longStoreTemporaryVariableBytecode */ + index = byteAt(++localIP); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (index < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - index) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - index) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + break; + case 499: // 243 extStoreReceiverVariableBytecode + { + sqInt anObject; + sqInt obj; + int onCurrentPage; + sqInt senderOop; + char *theFP; + StackPage *thePage; + sqInt variableIndex; + + VM_LABEL(extStoreReceiverVariableBytecode); + variableIndex = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + extA = 0; + anObject = longAt(localSP); + + /* begin storeMaybeContextReceiverVariable:withValue: */ + obj = longAt(localFP + FoxIFReceiver); + if ((variableIndex <= ReceiverIndex) + && ((((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + || (ismethodAssignmentToContextWithMachineCodePC(variableIndex, anObject))))) { + /* begin instVar:ofContext:put: */ + assert(isMarriedOrWidowedContext(obj)); + assert(!((isObjImmutable(obj)))); + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(obj))))) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, anObject)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anObject & (tagMask())))) + && (oopisLessThan(anObject, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),anObject); + if (variableIndex == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + goto l138; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!variableIndex) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + onCurrentPage = thePage == GIV(stackPage); + storeSenderOfFramewithValue(theFP, anObject); + if (onCurrentPage) { + localFP = (GIV(stackPage)->headFP); + localSP = (GIV(stackPage)->headSP); + } + else { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + goto l138; + } + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + externalDivorceFrameandContext(theFP, obj); + if (ismethodAssignmentToContextWithMachineCodePC(variableIndex, obj)) { + ensureContextHasBytecodePC(obj); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, anObject)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anObject & (tagMask())))) + && (oopisLessThan(anObject, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),anObject); + if (variableIndex == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + + /* Assigning various fields can force a divorce which can change the stackPage. */ + markStackPageMostRecentlyUsed(GIV(stackPage)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 1, __LINE__); + /* end instVar:ofContext:put: */ +l138:; + } + else { + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(obj)))); + if ((((usqInt)((byteAt((void *)(obj + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),obj); + longAtput((localSP -= BytesPerOop),anObject); + longAtput((localSP -= BytesPerOop),(((usqInt)(variableIndex + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l139; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, anObject)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anObject & (tagMask())))) + && (oopisLessThan(anObject, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),anObject); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l139:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 500: // 244 extStoreLiteralVariableBytecode + { + sqInt anObject; + sqInt litVar; + sqInt variableIndex; + + VM_LABEL(extStoreLiteralVariableBytecode); + variableIndex = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + extA = 0; + anObject = longAt(localSP); + + /* begin storeLiteralVariable:withValue: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((variableIndex + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(variableIndex + LiteralStart, GIV(method), litVar); + } + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(litVar)))); + if ((((usqInt)((byteAt((void *)(litVar + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),litVar); + longAtput((localSP -= BytesPerOop),anObject); + longAtput((localSP -= BytesPerOop),(((usqInt)2 /* (ValueIndex + 1) */ << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l140; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ValueIndex, litVar, anObject)); + assert(isNonImmediate(litVar)); + if (oopisGreaterThanOrEqualTo(litVar, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anObject & (tagMask())))) + && (oopisLessThan(anObject, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(litVar + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(litVar); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))),anObject); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l140: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 501: // 245 longStoreTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt index; + + VM_LABEL(longStoreTemporaryVariableBytecode); + index = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (index < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - index) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - index) * BytesPerWord),longAt(localSP)); + } + } + break; + case 505: // 249 extPushFullClosureBytecode + { + sqInt byte; + sqInt compiledBlock; + sqInt compiledBlockLiteralIndex; + sqInt context; + sqInt header; + sqInt i; + int ignoreContext; + usqInt newClosure; + usqInt newObj; + usqInt numArgs; + usqInt numBytes; + sqInt numCopied; + sqInt numCopiedSqInt; + sqInt numSlots; + int receiverIsOnStack; + sqInt startIndex; + + VM_LABEL(extPushFullClosureBytecode); + compiledBlockLiteralIndex = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + extA = 0; + + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + compiledBlock = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((compiledBlockLiteralIndex + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(compiledBlock)); + if ((!((longAt((void *)(compiledBlock))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + compiledBlock = fixFollowedFieldofObjectwithInitialValue(compiledBlockLiteralIndex + LiteralStart, GIV(method), compiledBlock); + } + assert(isOopCompiledMethod(compiledBlock)); + + /* begin argumentCountOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(compiledBlock)); + header = longAt((void *)((compiledBlock + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numArgs = (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; + byte = byteAt(++localIP); + numCopied = byte & (0x3F); + receiverIsOnStack = ((byte & (128)) != 0); + ignoreContext = ((byte & (64)) != 0); + + /* begin pushFullClosureNumArgs:copiedValues:compiledBlock:receiverIsOnStack:ignoreContext: */ + if (ignoreContext) { + context = GIV(nilObj); + } + else { + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + context = longAt(localFP + FoxThisContext); + goto l142; + } + context = marryFrameSP(localFP, localSP + (numCopied * BytesPerOop)); + /* end ensureFrameIsMarried:SP: */ +l142:; + } + + /* begin fullClosureIn:numArgs:numCopiedValues:compiledBlock: */ + numSlots = FullClosureFirstCopiedValueIndex + numCopied; + assert((numSlots >= 0) + && ((knownClassAtIndex(ClassFullBlockClosureCompactIndex)) != GIV(nilObj))); + assert((indexablePointersFormat()) == (instSpecOfClass(knownClassAtIndex(ClassFullBlockClosureCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newClosure = 0; + goto l143; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((indexablePointersFormat())) << (formatShift()))))) + ClassFullBlockClosureCompactIndex); + GIV(freeStart) += numBytes; + newClosure = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l143: + + /* Assume: have just allocated a new closure; it must be young. Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureOuterContextIndex, newClosure, context)); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord()))))),context); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(FullClosureCompiledBlockIndex, newClosure, compiledBlock)); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(FullClosureCompiledBlockIndex) << (shiftForWord()))))),compiledBlock); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureNumArgsIndex, newClosure, ((numArgs << 3) | 1))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord()))))),((numArgs << 3) | 1)); + if (receiverIsOnStack) { + startIndex = FullClosureReceiverIndex; + numCopiedSqInt = numCopied + 1; + } + else { + startIndex = FullClosureFirstCopiedValueIndex; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(FullClosureReceiverIndex, newClosure, longAt(localFP + FoxIFReceiver))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(FullClosureReceiverIndex) << (shiftForWord()))))),longAt(localFP + FoxIFReceiver)); + numCopiedSqInt = numCopied; + } + if (numCopiedSqInt > 0) { + for (i = 0; i < numCopiedSqInt; i += 1) { + /* Assume: have just allocated a new BlockClosure; it must be young. + Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(i + startIndex, newClosure, longAt((void *)(localSP + (((numCopiedSqInt - i) - 1) * BytesPerOop))))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)((i + startIndex)) << (shiftForWord()))))),longAt((void *)(localSP + (((numCopiedSqInt - i) - 1) * BytesPerOop)))); + } + + /* begin internalPop: */ + localSP += numCopiedSqInt * BytesPerOop; + } + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),newClosure); + } + break; + case 506: // 250 extPushClosureBytecode + { + sqInt blockSize; + sqInt byte; + sqInt context; + sqInt i; + sqInt initialIP; + usqInt newClosure; + usqInt newObj; + usqInt numArgs; + usqInt numBytes; + usqInt numCopied; + sqInt numSlots; + + VM_LABEL(extPushClosureBytecode); + byte = byteAt(++localIP); + numArgs = (byte & 7) + ((extA % 16) * 8); + numCopied = ((((usqInt)(byte)) >> 3) & 7) + ((extA / 16) * 8); + extA = 0; + blockSize = (byteAt(++localIP)) + ((((usqInt)(extB) << 8))); + numExtB = (extB = 0); + + /* begin pushClosureNumArgs:copiedValues:blockSize: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + context = longAt(localFP + FoxThisContext); + goto l144; + } + context = marryFrameSP(localFP, localSP + (numCopied * BytesPerOop)); + /* end ensureFrameIsMarried:SP: */ +l144: + initialIP = ((((usqInt)localIP)) + 2) - (GIV(method) + BaseHeaderSize); + + /* begin closureIn:numArgs:instructionPointer:numCopiedValues: */ + numSlots = ClosureFirstCopiedValueIndex + numCopied; + assert((numSlots >= 0) + && ((knownClassAtIndex(ClassBlockClosureCompactIndex)) != GIV(nilObj))); + assert((indexablePointersFormat()) == (instSpecOfClass(knownClassAtIndex(ClassBlockClosureCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newClosure = 0; + goto l145; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((indexablePointersFormat())) << (formatShift()))))) + ClassBlockClosureCompactIndex); + GIV(freeStart) += numBytes; + newClosure = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l145: + + /* Assume: have just allocated a new closure; it must be young. Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureOuterContextIndex, newClosure, context)); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord()))))),context); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureStartPCIndex, newClosure, (((usqInt)initialIP << 3) | 1))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))),(((usqInt)initialIP << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureNumArgsIndex, newClosure, ((numArgs << 3) | 1))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord()))))),((numArgs << 3) | 1)); + if (numCopied > 0) { + for (i = 0; i < numCopied; i += 1) { + /* Assume: have just allocated a new BlockClosure; it must be young. + Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(i + ClosureFirstCopiedValueIndex, newClosure, longAt((void *)(localSP + (((numCopied - i) - 1) * BytesPerOop))))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)((i + ClosureFirstCopiedValueIndex)) << (shiftForWord()))))),longAt((void *)(localSP + (((numCopied - i) - 1) * BytesPerOop)))); + } + + /* begin internalPop: */ + localSP += numCopied * BytesPerOop; + } + localIP += blockSize; + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),newClosure); + } + break; + } + } + + /* undo the pre-increment of IP (in fetchByte in fetchNextBytecode) before returning */ + localIP -= 1; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + return null; +} + + /* CogBlockMethod>>#cmHomeMethod */ +static NoDbgRegParms CogMethod * +cmHomeMethod(CogBlockMethod *self_in_CogBlockMethod) +{ + return (((self_in_CogBlockMethod->cmType)) >= CMMethod + ? ((CogMethod *) self_in_CogBlockMethod) + : (assert(CMBlock == (self_in_CogBlockMethod->cmType)), + ((CogMethod *) ((((usqInt)self_in_CogBlockMethod)) - ((self_in_CogBlockMethod->homeOffset)))))); +} + + /* CogBlockMethod>>#isCMBlock */ +static NoDbgRegParms int +isCMBlock(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMBlock; +} + + /* CogBlockMethod>>#isCMClosedPIC */ +static NoDbgRegParms int +isCMClosedPIC(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMClosedPIC; +} + + /* CogBlockMethod>>#isCMMethodEtAl */ +static NoDbgRegParms int +isCMMethodEtAl(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) >= CMMethod; +} + + /* CogBlockMethod>>#isCMMethodFlaggedForBecome */ +static NoDbgRegParms int +isCMMethodFlaggedForBecome(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMMethodFlaggedForBecome; +} + + /* CogBlockMethod>>#isCMOpenPIC */ +static NoDbgRegParms int +isCMOpenPIC(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMOpenPIC; +} + + +/* For assert-checking */ + + /* CogStackPage>>#addressIsInPage: */ +static NoDbgRegParms sqInt +addressIsInPage(StackPage *self_in_CogStackPage, char *address) +{ + return (((self_in_CogStackPage->lastAddress)) < address) + && (address < ((self_in_CogStackPage->baseAddress))); +} + + /* CogStackPage>>#isFree */ +static NoDbgRegParms int +isFree(StackPage *self_in_CogStackPage) +{ + return ((self_in_CogStackPage->baseFP)) == 0; +} + + +/* */ +/* MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#freeStackPageNoAssert: */ +static NoDbgRegParms sqInt +freeStackPageNoAssert(StackPage *aPage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *prev; + StackPage *self_in_CogStackPage; + + (aPage->baseFP = 0); + if (aPage == GIV(mostRecentlyUsedPage)) { + GIV(mostRecentlyUsedPage) = (GIV(mostRecentlyUsedPage)->prevPage); + return null; + } + self_in_CogStackPage = (prev = (aPage->prevPage)); + if (!((self_in_CogStackPage->baseFP))) { + return null; + } + (prev->nextPage = (aPage->nextPage)); + (((aPage->nextPage))->prevPage = prev); + (aPage->nextPage = (GIV(mostRecentlyUsedPage)->nextPage)); + (((GIV(mostRecentlyUsedPage)->nextPage))->prevPage = aPage); + (aPage->prevPage = GIV(mostRecentlyUsedPage)); + (GIV(mostRecentlyUsedPage)->nextPage = aPage); + return 0; +} + + +/* */ +/* MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#freeStackPage: */ +static NoDbgRegParms void +freeStackPage(StackPage *aPage) +{ + freeStackPageNoAssert(aPage); + assert(pageListIsWellFormed()); +} + + +/* */ +/* MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#markStackPageMostRecentlyUsed: */ +static NoDbgRegParms sqInt +markStackPageMostRecentlyUsed(StackPage *page) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (page == GIV(mostRecentlyUsedPage)) { + return null; + } + + /* Common case; making new page most recently used. */ + if (((page->prevPage)) == GIV(mostRecentlyUsedPage)) { + GIV(mostRecentlyUsedPage) = page; + assert(pageListIsWellFormed()); + return null; + } + (((page->prevPage))->nextPage = (page->nextPage)); + (((page->nextPage))->prevPage = (page->prevPage)); + (((GIV(mostRecentlyUsedPage)->nextPage))->prevPage = page); + (page->prevPage = GIV(mostRecentlyUsedPage)); + (page->nextPage = (GIV(mostRecentlyUsedPage)->nextPage)); + (GIV(mostRecentlyUsedPage)->nextPage = page); + GIV(mostRecentlyUsedPage) = page; + assert(pageListIsWellFormed()); + return 0; +} + + +/* */ +/* This method is used to move a page to a position in the list such that it + cannot be deallocated when a new page is allocated, without changing the + most recently + used page. There must be at least 3 pages in the system. So making the + page the MRU's prevPage is sufficient to ensure it won't be deallocated. */ +/* MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#markStackPageNextMostRecentlyUsed: */ +static NoDbgRegParms sqInt +markStackPageNextMostRecentlyUsed(StackPage *page) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(page != GIV(mostRecentlyUsedPage)); + if (((page->nextPage)) == GIV(mostRecentlyUsedPage)) { + return null; + } + (((page->prevPage))->nextPage = (page->nextPage)); + (((page->nextPage))->prevPage = (page->prevPage)); + (((GIV(mostRecentlyUsedPage)->prevPage))->nextPage = page); + (page->prevPage = (GIV(mostRecentlyUsedPage)->prevPage)); + (page->nextPage = GIV(mostRecentlyUsedPage)); + (GIV(mostRecentlyUsedPage)->prevPage = page); + assert(pageListIsWellFormed()); + return 0; +} + + +/* MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#newStackPage */ +static StackPage * +newStackPage(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *lruOrFree; + + lruOrFree = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((lruOrFree->baseFP))) { + return lruOrFree; + } + divorceFramesIn(lruOrFree); + return lruOrFree; +} + + +/* Answer if the stack page list is well-formed. + MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#pageListIsWellFormed */ +static sqInt +pageListIsWellFormed(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt count; + sqInt limit; + sqInt ok; + StackPage *page; + + ok = 1; + page = (GIV(mostRecentlyUsedPage)->nextPage); + count = 1; + limit = GIV(numStackPages) * 2; + while ((((page->baseFP)) == 0) + && ((page != GIV(mostRecentlyUsedPage)) + && (count <= limit))) { + if (!(asserta(((((page->nextPage))->prevPage)) == page))) { + ok = 0; + } + page = (page->nextPage); + count += 1; + } + while ((page != GIV(mostRecentlyUsedPage)) + && (count <= limit)) { + if (!(asserta(((((page->nextPage))->prevPage)) == page))) { + ok = 0; + } + if (asserta(!(isFree(page)))) { + if (!(asserta((addressIsInPage(page, (page->baseFP))) + && (addressIsInPage(page, (page->headSP)))))) { + ok = 0; + } + } + else { + ok = 0; + } + page = (page->nextPage); + count += 1; + } + if (!(asserta(count == (numStkPages())))) { + ok = 0; + } + return ok; +} + + +/* Answer the page for a page index. + N.B. This is a zero-relative index. */ + + /* CogStackPages>>#stackPageAt: */ +static NoDbgRegParms StackPage * +stackPageAt(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return stackPageAtpages(index, GIV(pages)); +} + + +/* */ + + /* CogStackPages>>#stackPageFor: */ +static NoDbgRegParms StackPage * +stackPageFor(void *pointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return stackPageAtpages(pageIndexFor(pointer), GIV(pages)); +} + + /* CogStackPages>>#statAverageLivePagesWhenMapping */ +static double +statAverageLivePagesWhenMapping(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (GIV(statNumMaps) + ? (((double) GIV(statPageCountWhenMappingSum) )) / GIV(statNumMaps) + : 0.0); +} + + +/* Initialization is completed by initializing the stack zone on entry to the + interpreter. Answer when this has been done. This allows e.g. the crash + dump processing to + be avoided until the system has been initialized, easing debugging of new + ports as they fail in early system initialization attempts. */ + + /* CogStackPages>>#vmIsInitialized */ +int +vmIsInitialized(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return !!GIV(pages); +} + + /* CoInterpreter>>#accessorDepthForPrimitiveIndex: */ +sqInt +accessorDepthForPrimitiveIndex(sqInt primIndex) +{ + return ((sqInt)((primitiveMetadataTable[primIndex]))) >> SpurPrimitiveAccessorDepthShift; +} + + +/* Activate newMethod when newMethod has been cogged, i.e. create a + machine-code frame and (re)enter machine-code. + */ + + /* CoInterpreter>>#activateNewCogMethod:inInterpreter: */ +static NoDbgRegParms void +activateNewCogMethodinInterpreter(CogMethod *cogMethod, sqInt inInterpreter) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int i; + sqInt methodHeader; + usqInt numTemps; + sqInt object; + sqInt rcvr; + char *sp; + sqInt switched; + + assert((rawHeaderOf(GIV(newMethod))) == (((sqInt)cogMethod))); + methodHeader = (cogMethod->methodHeader); + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (((cogMethod->cmNumArgs)) * BytesPerWord)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + if (!((cogMethod->stackCheckOffset))) { + /* frameless method; nothing to activate... */ + if ((numRegArgs()) > 0) { + if (((cogMethod->cmNumArgs)) <= (numRegArgs())) { + callRegisterArgCogMethodatreceiver(cogMethod, noCheckEntryOffset(), rcvr); + } + } + + /* dont use and: so as to get Slang to inline cogit numRegArgs > 0 */ + object = (((usqInt)cogMethod)) + (noCheckEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + ceCallCogCodePopReceiverReg(); + error("should not be reached"); + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),((usqInt)cogMethod)); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + for (i = (((cogMethod->cmNumArgs)) + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + if ((((methodHeader & AlternateHeaderHasPrimFlag) != 0)) + && (GIV(primFailCode) != 0)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeader); + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc). */ + if (GIV(stackPointer) >= GIV(stackLimit)) { + assert(((cogMethod->stackCheckOffset)) > (noCheckEntryOffset())); + object = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + ceEnterCogCodePopReceiverReg(); + error("should not be reached"); + } + GIV(instructionPointer) = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + switched = handleStackOverflowOrEventAllowContextSwitch(canContextSwitchIfActivatingheader(GIV(newMethod), methodHeader)); + returnToExecutivepostContextSwitch(inInterpreter, switched); +} + + +/* Add the given entry to the method cache. + The policy is as follows: + Look for an empty entry anywhere in the reprobe chain. + If found, install the new entry there. + If not found, then install the new entry at the first probe position + and delete the entries in the rest of the reprobe chain. + This has two useful purposes: + If there is active contention over the first slot, the second + or third will likely be free for reentry after ejection. + Also, flushing is good when reprobe chains are getting full. */ +/* Override to refuse to cache other than compiled methods. + This protects open PICs against having to test for compiled methods. */ + + /* CoInterpreter>>#addNewMethodToCache: */ +static NoDbgRegParms void +addNewMethodToCache(sqInt classObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTag; + sqInt err; + usqInt firstBytecode; + sqInt hash; + int hashInt; + sqInt header; + sqInt methodHeader; + sqInt p; + sqInt primitiveIndex; + usqInt probe; + + if (!(/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + primitiveFunctionPointer = primitiveInvokeObjectAsMethod; + return; + } + + /* begin classTagForClass: */ + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classTag = ((hashInt = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hashInt + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + + /* begin methodCacheHashOf:with: */ + hash = GIV(messageSelector) ^ ((((usqInt)(classTag) << 2))); + if (/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) { + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]))); + } + else { + assert(!((isNonImmediate(GIV(newMethod))) + && (isForwarded(GIV(newMethod))))); + primitiveFunctionPointer = primitiveInvokeObjectAsMethod; + } + for (p = 0; p < CacheProbeMax; p += 1) { + probe = (((usqInt)(hash)) >> p) & MethodCacheMask; + if (!(GIV(methodCache)[probe + MethodCacheSelector])) { + /* Found an empty entry -- use it */ + GIV(methodCache)[probe + MethodCacheSelector] = GIV(messageSelector); + GIV(methodCache)[probe + MethodCacheClass] = (classTagForClass(classObj)); + GIV(methodCache)[probe + MethodCacheMethod] = GIV(newMethod); + GIV(methodCache)[probe + MethodCachePrimFunction] = (((sqIntptr_t) primitiveFunctionPointer)); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = probe; + return; + } + } + + /* OK, we failed to find an entry -- install at the first slot... */ + + /* first probe */ + probe = hash & MethodCacheMask; + GIV(methodCache)[probe + MethodCacheSelector] = GIV(messageSelector); + GIV(methodCache)[probe + MethodCacheClass] = (classTagForClass(classObj)); + GIV(methodCache)[probe + MethodCacheMethod] = GIV(newMethod); + GIV(methodCache)[probe + MethodCachePrimFunction] = (((sqIntptr_t) primitiveFunctionPointer)); + + /* this for primitiveExternalMethod */ + /* ...and zap the following entries */ + GIV(lastMethodCacheProbeWrite) = probe; + for (p = 1; p < CacheProbeMax; p += 1) { + probe = (((usqInt)(hash)) >> p) & MethodCacheMask; + GIV(methodCache)[probe + MethodCacheSelector] = 0; + } +} + + /* CoInterpreter>>#argumentCountAddress */ +usqInt +argumentCountAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(argumentCount)))); +} + + +/* Coerce either a CMMethod or a CMBlock to the home CMMethod */ + + /* CoInterpreter>>#asCogHomeMethod: */ +static NoDbgRegParms CogMethod * +asCogHomeMethod(CogBlockMethod *aCogMethod) +{ + return (((aCogMethod->cmType)) >= CMMethod + ? ((CogMethod *) aCogMethod) + : /* cmHomeMethod */ + (((aCogMethod->cmType)) >= CMMethod + ? ((CogMethod *) aCogMethod) + : (assert(CMBlock == (aCogMethod->cmType)), + ((CogMethod *) ((((usqInt)aCogMethod)) - ((aCogMethod->homeOffset))))))); +} + + /* CoInterpreter>>#assertValidExecutionPointe:r:s:imbar:line: */ +static NoDbgRegParms void +assertValidExecutionPointersimbarline(usqInt lip, char *lifp, char *lisp, sqInt inInterpreter, sqInt ln) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cogMethod; + sqInt methodField; + sqInt methodFieldSqInt; + usqInt theIP; + + assertl(GIV(stackPage) == (GIV(mostRecentlyUsedPage)), ln); + assertl(addressIsInPage(GIV(stackPage), lifp), ln); + assert(deferStackLimitSmashAroundwith(assertValidStackLimits, ln)); + assertl(lisp < lifp, ln); + assertl(lifp > lisp, ln); + assertl(lisp >= (((GIV(stackPage)->realStackLimit)) - (stackLimitOffset())), ln); + assertl(((lifp - lisp) / BytesPerOop) < LargeContextSlots, ln); + methodField = longAt(lifp + FoxMethod); + if (inInterpreter) { + assertl(!(isMachineCodeFrame(lifp)), ln); + assert(lisp <= (lifp + FoxIFReceiver)); + assertl(GIV(method) == methodField, ln); + assertl((methodUsesAlternateBytecodeSet(GIV(method))) == (GIV(bytecodeSetSelector) == 0x100), ln); + if (assertal(cheapAddressCouldBeInHeap(methodField), ln)) { + theIP = (lip == (ceReturnToInterpreterPC()) + ? ((usqInt)(longAt(lifp + FoxIFSavedIP))) + : lip); + assertl((theIP >= (methodField + (lastPointerOf(methodField)))) + && (theIP < (((methodField + (numBytesOfBytes(methodField))) + BaseHeaderSize) - 1)), ln); + } + assertl((iframeIsBlockActivation(lifp)) + || ((pushedReceiverOrClosureOfFrame(lifp)) == (iframeReceiver(lifp))), ln); + } + else { + assertl(isMachineCodeFrame(lifp), ln); + assert(lisp <= (lifp + FoxMFReceiver)); + if ((assertal((((usqInt)methodField)) >= (minCogMethodAddress()), ln)) + && (assertal((((usqInt)methodField)) < (maxCogMethodAddress()), ln))) { + /* begin mframeHomeMethod: */ + methodFieldSqInt = longAt(lifp + FoxMethod); + if (methodFieldSqInt & MFMethodFlagIsBlockFlag) { + cogMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodFieldSqInt & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodFieldSqInt & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodFieldSqInt & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodFieldSqInt & MFMethodMask))))) - (((((CogBlockMethod *) (methodFieldSqInt & MFMethodMask)))->homeOffset)))))); + goto l1; + } + cogMethod = ((CogMethod *) (methodFieldSqInt & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + assertl((lip > (methodField + ((mframeIsBlockActivation(lifp) + ? sizeof(CogBlockMethod) + : sizeof(CogMethod))))) + && (lip < (methodField + ((cogMethod->blockSize)))), ln); + } + assertl((mframeIsBlockActivation(lifp)) + || ((pushedReceiverOrClosureOfFrame(lifp)) == (mframeReceiver(lifp))), ln); + } + if (!(longAt(lifp + FoxSavedFP))) { + assertl(frameHasContext(lifp), ln); + assertl((frameContext(lifp)) == (longAt(((GIV(stackPage)->baseAddress)) - BytesPerWord)), ln); + } +} + + +/* Check that the stacked instruction pointers in the given page are correct. + Checks the interpreter sender/machine code callee contract. */ + + /* CoInterpreter>>#assertValidStackedInstructionPointersIn:line: */ +static NoDbgRegParms sqInt +assertValidStackedInstructionPointersInline(StackPage *aStackPage, sqInt ln) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt methodField; + usqInt methodObj; + sqInt prevFrameWasCogged; + char *theFP; + usqInt theIP; + CogMethod *theMethod; + + callerFP = ((char *) 0); + if (!(assertal(!(isFree(aStackPage)), ln))) { + return 0; + } + prevFrameWasCogged = 0; + + /* The top of stack of an inactive page is always the instructionPointer. + The top of stack of the active page may be the instructionPointer if it has been pushed, + which is indicated by a 0 instructionPointer. */ + if ((GIV(stackPage) == aStackPage) + && (GIV(instructionPointer) != 0)) { + theIP = GIV(instructionPointer); + theFP = GIV(framePointer); + } + else { + theIP = ((usqInt)(longAt((aStackPage->headSP)))); + theFP = (aStackPage->headFP); + if (GIV(stackPage) == aStackPage) { + assertl(GIV(framePointer) == theFP, ln); + } + } + while (1) { + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* begin mframeHomeMethod: */ + methodField = longAt(theFP + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + theMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + goto l1; + } + theMethod = ((CogMethod *) (methodField & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + assertl((theIP == (ceCannotResumePC())) + || ((theIP >= (((usqInt)theMethod))) + && (theIP < ((((usqInt)theMethod)) + ((theMethod->blockSize))))), ln); + prevFrameWasCogged = 1; + } + else { + methodObj = longAt(theFP + FoxMethod); + if (prevFrameWasCogged) { + assertl(theIP == (ceReturnToInterpreterPC()), ln); + } + if (theIP == (ceReturnToInterpreterPC())) { + theIP = ((usqInt)(longAt(theFP + FoxIFSavedIP))); + } + assertl((theIP >= (methodObj + (lastPointerOf(methodObj)))) + && (theIP < (((methodObj + (numBytesOfBytes(methodObj))) + BaseHeaderSize) - 1)), ln); + prevFrameWasCogged = 0; + } + + /* assert-check the interpreter frame. */ + theIP = ((usqInt)(longAt(theFP + FoxCallerSavedIP))); + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theFP = callerFP; + } + assertl(theIP == (ceBaseFrameReturnPC()), ln); + return 1; +} + + +/* Check that the stacked instruction pointers in all pages are correct. + Checks the interpreter sender/machine code callee contract. + Written so it will be optimized away if not in an assert VM. */ + + /* CoInterpreter>>#assertValidStackedInstructionPointers: */ +static NoDbgRegParms void +assertValidStackedInstructionPointers(sqInt ln) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + StackPage *thePage; + + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + assertl(assertValidStackedInstructionPointersInline(thePage, ln), ln); + } + } +} + + +/* Attempt to convert the current interpreted activation into a machine code + activation, and if this is popssible, jump into machine code. bcpc is the + 0-relative pc of the backward branch bytecode (not any preceding + extension). */ + + /* CoInterpreter>>#attemptToSwitchToMachineCode: */ +static NoDbgRegParms void +attemptToSwitchToMachineCode(sqInt bcpc) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt closure; + sqInt cls; + CogMethod *cogMethod; + CogBlockMethod *cogMethod1; + CogMethod *homeMethod; + sqInt methodField; + sqInt methodHeader; + char *p; + usqInt pc; + usqInt startBcpc; + char *theFP; + + if (!(methodHasCogMethod(GIV(method)))) { + if (((byteAt((GIV(framePointer) + FoxIFrameFlags) + 3)) != 0) + && (!(isVanillaBlockClosure((cls = frameStackedReceivernumArgs(GIV(framePointer), /* frameNumArgs: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(GIV(framePointer)))->cmNumArgs) + : byteAt((GIV(framePointer) + FoxIFrameFlags) + 1)))))))) { + /* Compiled block / full closure */ + cogFullBlockMethodnumCopied(GIV(method), (numPointerSlotsOf(cls)) - FullClosureFirstCopiedValueIndex); + } + else { + /* Compiled method */ + cogselector(GIV(method), GIV(nilObj)); + } + } + if (methodHasCogMethod(GIV(method))) { + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + + /* begin convertToMachineCodeFrame:bcpc: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + + /* Update the return pc, perhaps saving it in the caller's iframeSavedIP. */ + if (longAt(GIV(framePointer) + FoxSavedFP)) { + if (!((((usqInt)(longAt((((char *)(longAt(GIV(framePointer) + FoxSavedFP)))) + FoxMethod)))) < (startOfMemory()))) { + theFP = ((char *)(longAt(GIV(framePointer) + FoxSavedFP))); + + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(theFP))); + longAtput(theFP + FoxIFSavedIP,longAt(GIV(framePointer) + FoxCallerSavedIP)); + longAtput(GIV(framePointer) + FoxCallerSavedIP,ceReturnToInterpreterPC()); + } + } + else { + longAtput(GIV(framePointer) + FoxCallerSavedIP,ceBaseFrameReturnPC()); + } + + /* Compute the cog method field */ + if (byteAt((GIV(framePointer) + FoxIFrameFlags) + 3)) { + closure = longAt(GIV(framePointer) + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(GIV(framePointer)))->cmNumArgs) + : byteAt((GIV(framePointer) + FoxIFrameFlags) + 1)))) << (shiftForWord())))))); + + /* begin isVanillaBlockClosure: */ + assert((isPointers(closure)) + && ((numSlotsOf(closure)) >= ClosureFirstCopiedValueIndex)); + if (((((longAt((void *)((closure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1)) { + startBcpc = (((longAt((void *)((closure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))))) >> 3)) - 1; + cogMethod1 = findMethodForStartBcpcinHomeMethod(startBcpc, cogMethod); + } + else { + startBcpc = (((/* begin literalCountOfMethodHeader: */ + assert((((((cogMethod->methodHeader))) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((((cogMethod->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop; + cogMethod1 = ((CogBlockMethod *) cogMethod); + } + methodField = (((sqInt)cogMethod1)) + MFMethodFlagIsBlockFlag; + } + else { + startBcpc = (((/* begin literalCountOfMethodHeader: */ + assert((((((cogMethod->methodHeader))) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((((cogMethod->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop; + cogMethod1 = ((CogBlockMethod *) cogMethod); + methodField = ((sqInt)cogMethod); + } + + /* compute the pc before converting the frame to help with debugging. */ + pc = mcPCForBackwardBranchstartBcpcin(bcpc, startBcpc, cogMethod1); + assert(pc > ((((usqInt)cogMethod1)) + (noCheckEntryOffset()))); + assert(bcpc == (bytecodePCForstartBcpcin(pc, startBcpc, cogMethod1))); + + /* now convert to a machine code frame */ + longAtput(GIV(framePointer) + FoxMethod,methodField + ((byteAt((GIV(framePointer) + FoxIFrameFlags) + 2) + ? MFMethodFlagHasContextFlag + : 0))); + for (p = (GIV(framePointer) + FoxIFReceiver); p >= GIV(stackPointer); p += (-BytesPerWord)) { + longAtput((p + FoxMFReceiver) - FoxIFReceiver,longAt(p)); + } + GIV(stackPointer) = (GIV(stackPointer) + FoxMFReceiver) - FoxIFReceiver; + + /* begin assertValidMachineCodeFrame: */ + assert(isMachineCodeFrame(GIV(framePointer))); + cogMethod1 = ((CogBlockMethod *) ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodMask)); + homeMethod = /* asCogHomeMethod: */ + (((cogMethod1->cmType)) >= CMMethod + ? ((CogMethod *) cogMethod1) + : /* cmHomeMethod */ + (((cogMethod1->cmType)) >= CMMethod + ? ((CogMethod *) cogMethod1) + : (assert(CMBlock == (cogMethod1->cmType)), + ((CogMethod *) ((((usqInt)cogMethod1)) - ((cogMethod1->homeOffset))))))); + assert((methodFor(cogMethod1)) == homeMethod); + assert((pc > (((sqInt)cogMethod1))) + && (pc < ((((sqInt)homeMethod)) + ((homeMethod->blockSize))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),pc); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + ceEnterCogCodePopReceiverReg(); + } +} + + +/* Support for primitiveSuspend. + Assume suspendedContext is that of a process waiting on a condition + variable. Backup the PC of suspendedContext to the send that entered the + wait state. + primitiveEnterCriticalSection pushes false for blocked waiters. false must + be replaced by the condition variable. */ +/* The suspendedContext is in one of six states, hence six cases: + 1 & 2: single context with bytecode pc or single context with machine code + pc 3 & 4: married to machine code frame in embedded (v3) block or machine + code frame in method/full block + 5 & 6: married to interpreter frame with saved ip or interpreter frame + with pushed pc */ + + /* CoInterpreter>>#backupContext:toBlockingSendTo: */ +static NoDbgRegParms void +backupContexttoBlockingSendTo(sqInt suspendedContext, sqInt conditionVariable) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogBlockMethod *cogMethod; + sqInt mcpc; + sqInt pc; + sqInt senderOop; + sqInt sp; + usqInt startBcpc; + char *theFP; + sqInt theIP; + sqInt theMethod; + usqInt theNewIP; + StackPage *thePage; + + assert(isContext(suspendedContext)); + theMethod = longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* cases 1 & 2 */ + if ((!((longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))) & (tagMask())))) { + pc = longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + sp = longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + assert(((((pc) & 7) == 1)) + && (((pc >> 3)) != 0)); + assert(((((sp) & 7) == 1)) + && (((sp >> 3)) > 0)); + + /* case 2 */ + if (((pc = (pc >> 3))) < 0) { + pc = ((mustMapMachineCodePCcontext(pc, suspendedContext)) >> 3); + } + + /* context with machine code pc; must map... */ + theIP = ((theMethod + BaseHeaderSize) + pc) - 1; + theNewIP = pcPreviousToFunction(theIP, theMethod); + assert((theNewIP < theIP) + && ((theIP - theNewIP) <= 3)); + pc = ((theNewIP - theMethod) - BaseHeaderSize) + 1; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(suspendedContext)) + && (!(isForwarded(suspendedContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, suspendedContext, (((usqInt)pc << 3) | 1))); + longAtput((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),(((usqInt)pc << 3) | 1)); + + /* implicitly converts to 0 relative */ + sp = ((sp >> 3)) + ReceiverIndex; + assert(((fetchPointerofObject(sp, suspendedContext)) == (falseObject())) + || ((fetchPointerofObject(sp, suspendedContext)) == conditionVariable)); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(sp, suspendedContext, conditionVariable)); + assert(isNonImmediate(suspendedContext)); + if (oopisGreaterThanOrEqualTo(suspendedContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(conditionVariable & (tagMask())))) + && (oopisLessThan(conditionVariable, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(suspendedContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(suspendedContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(sp) << (shiftForWord()))))),conditionVariable); + return; + } + assert(isMarriedOrWidowedContext(suspendedContext)); + assert(!((isWidowedContextNoConvert(suspendedContext)))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(!((thePage == GIV(stackPage)))); + assert(theFP == ((thePage->headFP))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* a machine code pc... it must be converted... */ + mcpc = longAt((thePage->headSP)); + + /* begin mframeCogMethod: */ + cogMethod = ((CogBlockMethod *) ((longAt(theFP + FoxMethod)) & MFMethodMask)); + if (((cogMethod->cmType)) == CMBlock) { + assert((frameStackedReceivernumArgs(theFP, (cogMethod->cmNumArgs))) == (fetchPointerofObject(ClosureIndex, suspendedContext))); + + /* begin startPCOfClosure: */ + startBcpc = (((longAt((void *)(((longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))))) >> 3)) - 1; + } + else { + startBcpc = ((literalCountOf(theMethod)) + LiteralStart) * BytesPerOop; + } + theIP = bytecodePCForstartBcpcin(mcpc, startBcpc, cogMethod); + theIP = (theIP + theMethod) + BaseHeaderSize; + theNewIP = pcPreviousToFunction(theIP, theMethod); + assert((theNewIP < theIP) + && ((theIP - theNewIP) <= 3)); + convertFrametoInterpreterFrame(theFP, theIP - theNewIP); + } + else { + theIP = longAt((thePage->headSP)); + if (theIP == (ceReturnToInterpreterPC())) { + /* fetchByte uses pre-increment; must + 1 to point at correct bytecode... */ + theIP = (((usqInt)(longAt(theFP + FoxIFSavedIP)))) + 1; + theNewIP = pcPreviousToFunction(theIP, theMethod); + assert((theNewIP < theIP) + && ((theIP - theNewIP) <= 3)); + + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(theFP))); + longAtput(theFP + FoxIFSavedIP,theNewIP - 1); + } + else { + /* fetchByte uses pre-increment; must + 1 to point at correct bytecode... */ + theIP += 1; + assert(validInstructionPointerinMethodframePointer(theIP, theMethod, theFP)); + theNewIP = pcPreviousToFunction(theIP, theMethod); + assert((theNewIP < theIP) + && ((theIP - theNewIP) <= 3)); + longAtput((thePage->headSP),theNewIP - 1); + } + } + assert(((longAt(((thePage->headSP)) + BytesPerWord)) == (falseObject())) + || ((longAt(((thePage->headSP)) + BytesPerWord)) == conditionVariable)); + longAtput(((thePage->headSP)) + BytesPerWord,conditionVariable); +} + + /* CoInterpreter>>#callForCogCompiledCodeCompaction */ +void +callForCogCompiledCodeCompaction(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(cogCompiledCodeCompactionCalledFor) = 1; + forceInterruptCheck(); +} + + +/* convert + rcvr base + arg(s) + retpc <- sp + to + retpc base + entrypc + rcvr + arg(s) <- sp + and then enter at either the checked or the unchecked entry-point. */ + + /* CoInterpreter>>#callRegisterArgCogMethod:at:receiver: */ +static NoDbgRegParms void +callRegisterArgCogMethodatreceiver(CogMethod *cogMethod, sqInt entryOffset, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + char *sp; + + assert(((numRegArgs()) > 0) + && (((numRegArgs()) <= 2) + && (((cogMethod->cmNumArgs)) <= (numRegArgs())))); + if (((cogMethod->cmNumArgs)) == 2) { + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (3 * BytesPerWord),longAt(GIV(stackPointer))); + object = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (1 * BytesPerWord),longAt(GIV(stackPointer) + (3 * BytesPerWord))); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (2 * BytesPerWord),rcvr); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (3 * BytesPerWord),(((sqInt)cogMethod)) + entryOffset); + ceCallCogCodePopReceiverArg1Arg0Regs(); + } + if (((cogMethod->cmNumArgs)) == 1) { + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (2 * BytesPerWord),longAt(GIV(stackPointer))); + object = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (1 * BytesPerWord),rcvr); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (2 * BytesPerWord),(((sqInt)cogMethod)) + entryOffset); + ceCallCogCodePopReceiverArg0Regs(); + } + assert(((cogMethod->cmNumArgs)) == 0); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (1 * BytesPerWord),longAt(GIV(stackPointer))); + + /* stackValue:put: */ + longAtput(GIV(stackPointer),(((sqInt)cogMethod)) + entryOffset); + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),rcvr); + GIV(stackPointer) = sp; + ceCallCogCodePopReceiverReg(); +} + + +/* An external call or FFI primitive has failed. Build the frame and + activate as appropriate. Enter either the interpreter or machine + code depending on whether aPrimitiveMethod has been or is still + cogged. Note that we could always interpret but want the efficiency + of executing machine code if it is available. */ + + /* CoInterpreter>>#ceActivateFailingPrimitiveMethod: */ +void +ceActivateFailingPrimitiveMethod(sqInt aPrimitiveMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt arg; + sqInt argIndex; + sqInt fmt; + sqInt header; + usqInt i; + sqInt index; + int inInterpreter; + sqInt maxNumArgs; + sqInt methodHeader; + sqInt methodHeaderSqInt; + usqInt numArgs; + usqInt numSlots; + usqInt numSlotsUsqInt; + usqInt numTemps; + sqInt object; + sqInt objectClass; + sqInt rcvr; + sqInt result; + sqInt selectorOrProperties; + sqInt switched; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt types; + sqInt typesArraySize; + sqInt wordSize; + + assert(GIV(primFailCode) != 0); + assert(GIV(newMethod) == aPrimitiveMethod); + + /* If we're on Spur, retry the primitive, if appropriate, returning if successful after retry. */ + + /* ugh; annoying. Most users want the simulated address for the primitive (e.g. compilation in the Cogit). + But here we want the object for the primitive. So we choose to side-step the simulator's implementation + rather than change the other clients to do the mapFunctionToAddress: the simulator's version does. */ + primitiveFunctionPointer = functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(aPrimitiveMethod, (primitiveIndexOf(aPrimitiveMethod)), null); + retryPrimitiveOnFailure(); + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + if (!GIV(primFailCode)) { + result = longAt(GIV(stackPointer)); + assert(isPCWithinMethodZone(GIV(instructionPointer))); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(instructionPointer)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),result); + ceEnterCogCodePopReceiverReg(); + } + methodHeader = longAt((void *)((aPrimitiveMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + activateNewCogMethodinInterpreter(((CogMethod *) methodHeader), 0); + } + else { + /* begin activateNewMethod */ + /* begin collectArgumentsTypeFor:fromStackAt:count: */ + /* begin tempVarTypesArraysOf: */ + /* begin additionalMethodStateOf:at: */ + /* begin additionalMethodStateOf: */ + selectorOrProperties = penultimateLiteralOf(GIV(newMethod)); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l3; + } + wordSize = BytesPerWord; + maxNumArgs = (((lengthOf(tempVarTypesArrays)) < GIV(argumentCount)) ? (lengthOf(tempVarTypesArrays)) : GIV(argumentCount)); + for (argIndex = 0; argIndex < maxNumArgs; argIndex += 1) { + /* begin followObjField:ofObject: */ + types = longAt((void *)((tempVarTypesArrays + BaseHeaderSize) + ((((usqInt)(argIndex) << (shiftForWord())))))); + assert(isNonImmediate(types)); + if ((!((longAt((void *)(types))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + types = fixFollowedFieldofObjectwithInitialValue(argIndex, tempVarTypesArrays, types); + } + arg = longAt((void *)(GIV(stackPointer) + (argIndex * wordSize))); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (types) + && (isInstanceOfClassArray(types)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = arg & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(arg)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(types + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(types + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(types - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, types, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, types, objectClass)); + assert(isNonImmediate(types)); + if (oopisGreaterThanOrEqualTo(types, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(types + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(types); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2:; + } + /* end collectArgumentsTypeFor:fromStackAt:count: */ +l3: + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeaderSqInt = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeaderSqInt)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeaderSqInt)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(!(isOopForwarded(rcvr))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeaderSqInt); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeaderSqInt)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeaderSqInt) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeaderSqInt >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + if (((methodHeaderSqInt & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeaderSqInt); + } + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc). */ + switched = 1; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(canContextSwitchIfActivatingheader(GIV(newMethod), methodHeaderSqInt)); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + } +} + + +/* Return across a page boundary. The context to return to (which may be + married) is stored in the first word of the stack. We get here when a + return instruction jumps + to the ceBaseFrameReturn: address that is the return pc for base frames. A + consequence of this is that the current frame is no longer valid since an + interrupt may have overwritten + its state as soon as the stack pointer has been cut-back beyond the return + pc. So to have + a context to send the cannotReturn: message to we also store the base + frame's context + in the second word of the stack page. */ + + /* CoInterpreter>>#ceBaseFrameReturn: */ +sqInt +ceBaseFrameReturn(sqInt returnValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + char *callerFP; + sqInt contextToReturnFrom; + sqInt contextToReturnTo; + char *fp; + char *frameAbove; + sqInt header; + sqInt isAContext; + sqInt methodHeader; + StackPage *newPage; + sqInt senderOop; + char *sp; + StackPage *thePage; + + assert((stackPageFor(GIV(stackPointer))) == GIV(stackPage)); + assert((GIV(mostRecentlyUsedPage)) == GIV(stackPage)); + assertCStackWellAligned(); + assert(GIV(framePointer) == 0); + assert(GIV(stackPointer) <= (((GIV(stackPage)->baseAddress)) - BytesPerWord)); + assert((((GIV(stackPage)->baseFP)) + (2 * BytesPerWord)) < ((GIV(stackPage)->baseAddress))); + + /* We would like to use the following assert but we can't since the stack pointer will be above the + base frame pointer in the base frame return and hence the 0 a base frame pointer points at could + be overwritten which will cause the isBaseFrame assert in frameCallerContext: to fail. + self assert: (self frameCallerContext: stackPage baseFP) = (stackPages longAt: stackPage baseAddress). */ + assert((addressCouldBeObj(longAt(((GIV(stackPage)->baseAddress)) - BytesPerWord))) + && (isContext(longAt(((GIV(stackPage)->baseAddress)) - BytesPerWord)))); + contextToReturnTo = longAt((GIV(stackPage)->baseAddress)); + assert(addressCouldBeObj(contextToReturnTo)); + + /* The stack page is effectively free now, so free it. We must free it to be + correct in determining if contextToReturnTo is still married, and in case + makeBaseFrameFor: cogs a method, which may cause a code compaction, + in which case the frame must be free to avoid the relocation machinery + tracing the dead frame. Since freeing now temporarily violates the page-list + ordering invariant, use the assert-free version. */ + freeStackPageNoAssert(GIV(stackPage)); + isAContext = /* isContext: */ + ((!(contextToReturnTo & (tagMask())))) + && (((longAt((void *)(contextToReturnTo))) & (classIndexMask())) == ClassMethodContextCompactIndex); + if (isAContext + && (/* isStillMarriedContext: */ + (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(contextToReturnTo))))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + GIV(framePointer) = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(GIV(framePointer)), GIV(pages)); + if (GIV(framePointer) == ((thePage->headFP))) { + GIV(stackPointer) = (thePage->headSP); + } + else { + /* Returning to some interior frame, presumably because of a sender assignment. + Move the frames above to another page (they may be in use, e.g. via coroutining). + Make the interior frame the top frame. */ + + /* begin findFrameAbove:inPage: */ + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == GIV(framePointer)) { + frameAbove = 0; + goto l1; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == GIV(framePointer)) { + frameAbove = fp; + goto l1; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + frameAbove = 0; + /* end findFrameAbove:inPage: */ +l1: + + /* Since we've just deallocated a page we know that newStackPage won't deallocate an existing one. */ + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l2; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l2: + assert(newPage == GIV(stackPage)); + moveFramesInthroughtoPage(thePage, frameAbove, newPage); + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + } + } + else { + if (!(isAContext + && (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1)))) { + contextToReturnFrom = longAt(((GIV(stackPage)->baseAddress)) - BytesPerWord); + tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFromtoreturnValue(contextToReturnFrom, contextToReturnTo, returnValue); + + /* begin externalCannotReturn:from: */ + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),contextToReturnFrom); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + + /* Both ceBaseFrameReturnTrampoline & ceNonLocalReturnTrampoline pop + their caller's return pc into instructionPointer. In this uncommon case restore + it, since a send's call pushes the instructionPointer (after the arguments). */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), contextToReturnFrom, 1); + } + + /* void the instructionPointer to stop it being incorrectly updated in a code + compaction in makeBaseFrameFor:. */ + GIV(instructionPointer) = 0; + thePage = makeBaseFrameFor(contextToReturnTo); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + } + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + assert((stackPageFor(GIV(framePointer))) == GIV(stackPage)); + if ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())) { + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + ceEnterCogCodePopReceiverReg(); + } + GIV(instructionPointer) = longAt(GIV(stackPointer)); + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* stackTopPut: */ + longAtput(GIV(stackPointer),returnValue); + assert(checkIsStillMarriedContextcurrentFP(contextToReturnTo, GIV(framePointer))); + ceInvokeInterpret(); + + /* NOTREACHED */ + return null; +} + + +/* index is unboxed and 0-based. The call-back expects 1-based value (to + perform the operation with instVarAt:put: + */ + + /* CoInterpreter>>#ceCannotAssignTo:withIndex:valueToAssign: */ +#if IMMUTABILITY +sqInt +ceCannotAssignTowithIndexvalueToAssign(sqInt immutableObject, sqInt index, sqInt valueToAssign) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + sqInt top; + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),immutableObject); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),valueToAssign); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),(((usqInt)(index + 1) << 3) | 1)); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))), immutableObject, 2); +} +#endif /* IMMUTABILITY */ + + +/* A context that has been returned from, or otherwise has an invalid pc has + been reentered. + Until we have a cannotResume: selector, simply resend cannotReturn:. */ + + /* CoInterpreter>>#ceCannotResume */ +sqInt +ceCannotResume(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + sqInt resultOop; + + assert(isMachineCodeFrame(GIV(framePointer))); + assert(frameHasContext(GIV(framePointer))); + resultOop = longAt(GIV(stackPointer)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt(GIV(framePointer) + FoxThisContext)); + longAtput((GIV(stackPointer) -= BytesPerWord),resultOop); + + /* Make sure the happy couple remain returned from. */ + object = ceCannotResumePC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), longAt(GIV(framePointer) + FoxThisContext), 1); +} + + +/* Log failure and then retry if there's an accessorDepth or failure due to + no memory. + */ + + /* CoInterpreter>>#ceCheckAndMaybeRetryPrimitive: */ +void +ceCheckAndMaybeRetryPrimitive(sqInt primIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodSelectorOrImmediate; + sqInt retried; + sqInt tagBits; + + if ((recordPrimTrace()) + && (!primTracePluginName)) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveFailure << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + aMethodSelectorOrImmediate = /* fetchClassOf: */ + ((tagBits = (longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord))) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)))); + + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = aMethodSelectorOrImmediate; + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + retried = retryPrimitiveOnFailure(); + if (retried + && (recordPrimTrace())) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveRetry << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } +} + + /* CoInterpreter>>#ceCheckForInterrupt */ +void +ceCheckForInterrupt(void) +{ + sqInt switched; + + switched = checkForEventsMayContextSwitch(1); + returnToExecutivepostContextSwitch(0, switched); +} + + /* CoInterpreter>>#ceContext:instVar: */ +sqInt +ceContextinstVar(sqInt maybeContext, sqInt slotIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt result; + sqInt top; + + if (((longAt((void *)(maybeContext))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + result = externalInstVarofContext(slotIndex, maybeContext); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + else { + result = longAt((void *)((maybeContext + BaseHeaderSize) + ((((usqInt)(slotIndex) << (shiftForWord())))))); + } + return result; +} + + +/* genStorePop:MaybeContextReceiverVariable: filters out unmarried contexts + but not arbitrary objects in subclasses. It answers maybeMarriedContext so + that the StackToRegisterMappingCogit can keep ReceiverResultReg live. */ + + /* CoInterpreter>>#ceContext:instVar:value: */ +sqInt +ceContextinstVarvalue(sqInt maybeMarriedContext, sqInt slotIndex, sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt top; + + if ((((longAt((void *)(maybeMarriedContext))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (((((longAt((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + externalInstVarofContextput(slotIndex, maybeMarriedContext, anOop); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + else { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(slotIndex, maybeMarriedContext, anOop)); + assert(isNonImmediate(maybeMarriedContext)); + if (oopisGreaterThanOrEqualTo(maybeMarriedContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anOop & (tagMask())))) + && (oopisLessThan(anOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(maybeMarriedContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(maybeMarriedContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(slotIndex) << (shiftForWord()))))),anOop); + } + return maybeMarriedContext; +} + + /* CoInterpreter>>#ceInterpretMethodFromPIC:receiver: */ +sqInt +ceInterpretMethodFromPICreceiver(sqInt aMethodObj, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + CogMethod *pic; + sqInt primitiveIndex; + sqInt top; + + + /* pop off inner return and locate open or closed PIC */ + pic = ((CogMethod *) ((popStack()) - (interpretOffset()))); + assert((isCMOpenPIC(((CogBlockMethod *) pic))) + || (isCMClosedPIC(((CogBlockMethod *) pic)))); + + /* If found from an open PIC then it must be an uncogged method and, since it's been found + in the method cache, should be cogged if possible. If found from a closed PIC then at the + time the closed PIC was created the method was uncoggable, either because there was + no space, it had too many literals or it contained an illegal bytecode). So don't try and cog + it, but subsequently it may have been cogged via another path. If the method is, or ends up + cogged, jump to machine code, otherwise interpret. */ + if (((pic->cmType)) == CMOpenPIC) { + assert(!(methodHasCogMethod(aMethodObj))); + + /* begin methodShouldBeCogged: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + goto l1; + } + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(aMethodObj); + } + goto l2; +l1: + cogselector(aMethodObj, (pic->selector)); +l2:; + } + if (methodHasCogMethod(aMethodObj)) { + executeCogMethodfromUnlinkedSendWithReceiver(cogMethodOf(aMethodObj), rcvr); + } + GIV(messageSelector) = (pic->selector); + GIV(newMethod) = aMethodObj; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (aMethodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]))); + GIV(argumentCount) = (pic->cmNumArgs); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + return interpretMethodFromMachineCode(); +} + + /* CoInterpreter>>#ceMNUFromPICMNUMethod:receiver: */ +sqInt +ceMNUFromPICMNUMethodreceiver(sqInt aMethodObj, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cPIC; + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + sqInt primitiveIndex; + sqInt selector; + sqInt tagBits; + sqInt top; + + assert(addressCouldBeOop(rcvr)); + assert((aMethodObj == 0) + || ((addressCouldBeObj(aMethodObj)) + && (isOopCompiledMethod(aMethodObj)))); + selector = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(selector), lengthOfMaybeImmediate(selector), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + cPIC = ((CogMethod *) ((popStack()) - (mnuOffset()))); + assert((isCMClosedPIC(((CogBlockMethod *) cPIC))) + || (isCMOpenPIC(((CogBlockMethod *) cPIC)))); + GIV(argumentCount) = (cPIC->cmNumArgs); + GIV(messageSelector) = (cPIC->selector); + if (aMethodObj) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + createActualMessageTo(/* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr))); + if (/* maybeMethodHasCogMethod: */ + ((!(aMethodObj & (tagMask())))) + && ((((byteAt((void *)(aMethodObj + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + executeCogMethodfromUnlinkedSendWithReceiver(cogMethodOf(aMethodObj), rcvr); + + /* NOTREACHED */ + assert(0); + } + GIV(newMethod) = aMethodObj; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (aMethodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]))); + return interpretMethodFromMachineCode(); + } + + /* handleMNU:InMachineCodeTo:classForMessage: assumes lkupClass is set, since every other use is + after a lookupMethodNoMNUEtcInClass: call, which sets lkupClass. Here we must set it manually. + Global variables. Bah! */ + handleMNUInMachineCodeToclassForMessage(SelectorDoesNotUnderstand, rcvr, (GIV(lkupClass) = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)))); + + /* NOTREACHED */ + assert(0); + return 0; +} + + +/* Called from machine code to set an object's identity hash to a new hash. + N.B. may be called from machine code on the Smalltalk stack so this should + not call deeply. */ +/* We know anObject has not a hash yet (or this entry-point would not be + called. Sets the hash, then answers it as a smallinteger */ + + /* CoInterpreter>>#ceNewHashOf: */ +sqInt +ceNewHashOf(sqInt anObject) +{ + assert((isNonImmediate(anObject)) + && ((rawHashBitsOf(anObject)) == 0)); + return (((newHashBitsOf(anObject)) << 3) | 1); +} + + /* CoInterpreter>>#ceNonLocalReturn: */ +sqInt +ceNonLocalReturn(sqInt returnValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt closure; + sqInt contextToReturnTo; + sqInt currentCtx; + char *frameToReturnTo; + sqInt home; + StackPage *newPage; + sqInt nextCntx; + sqInt ourContext; + sqInt ourContextSqInt; + sqInt senderOop; + char *sp; + char *theFP; + char *theFP1; + StackPage *thePage; + StackPage *thePage1; + sqInt top; + sqInt unwindContextOrNilOrZero; + + + /* self shortPrintFrameAndCallers: framePointer. + self printOop: returnValue. + self halt. */ + frameToReturnTo = ((char *) 0); + theFP = ((char *) 0); + assert(isMachineCodeFrame(GIV(framePointer))); + assert(frameIsBlockActivation(GIV(framePointer))); + + /* Update the current page's headFrame pointers to enable the search for unwind protects below + to identify widowed contexts correctly. */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* Since this is a block activation the closure is on the stack above any args and the frame. */ + closure = longAt(GIV(framePointer) + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(GIV(framePointer)))->cmNumArgs) + : byteAt((GIV(framePointer) + FoxIFrameFlags) + 1)))) << (shiftForWord())))))); + + /* avoid compiler warning */ + /* Walk the closure's lexical chain to find the context or frame to return from (home). */ + home = null; + while (closure != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + home = longAt((void *)((closure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + assert(isNonImmediate(home)); + if ((!((longAt((void *)(home))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + home = fixFollowedFieldofObjectwithInitialValue(ClosureOuterContextIndex, closure, home); + } + if (!(/* isContext: */ + ((!(home & (tagMask())))) + && (((longAt((void *)(home))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* error: can't find home on chain; cannot return */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + ourContext = longAt(GIV(framePointer) + FoxThisContext); + goto l1; + } + ourContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l1: + + /* begin externalCannotReturn:from: */ + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),ourContext); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + + /* Both ceBaseFrameReturnTrampoline & ceNonLocalReturnTrampoline pop + their caller's return pc into instructionPointer. In this uncommon case restore + it, since a send's call pushes the instructionPointer (after the arguments). */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), ourContext, 1); + } + + /* begin followObjField:ofObject: */ + closure = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord())))))); + assert(isNonImmediate(closure)); + if ((!((longAt((void *)(closure))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + closure = fixFollowedFieldofObjectwithInitialValue(ClosureIndex, home, closure); + } + } + + /* home is to be returned from provided there is no unwind-protect activation between + this frame and home's sender. Search for an unwind. findUnwindThroughContext: + will answer either the context for an unwind-protect activation or nilObj if the sender + cannot be found or 0 if no unwind is found but the sender is. */ + unwindContextOrNilOrZero = findUnwindThroughContext(home); + if (unwindContextOrNilOrZero == GIV(nilObj)) { + /* error: can't find home on chain; cannot return */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + ourContext = longAt(GIV(framePointer) + FoxThisContext); + goto l2; + } + ourContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l2: + + /* begin externalCannotReturn:from: */ + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),ourContext); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + + /* Both ceBaseFrameReturnTrampoline & ceNonLocalReturnTrampoline pop + their caller's return pc into instructionPointer. In this uncommon case restore + it, since a send's call pushes the instructionPointer (after the arguments). */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), ourContext, 1); + } + if (unwindContextOrNilOrZero) { + /* begin externalAboutToReturn:through: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + ourContextSqInt = longAt(GIV(framePointer) + FoxThisContext); + goto l3; + } + ourContextSqInt = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l3: + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),ourContextSqInt); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),unwindContextOrNilOrZero); + + /* The ceNonLocalReturnTrampoline pops its caller's return pc into instructionPointer. + In this uncommon case restore it, since a send's call pushes the instructionPointer (after the arguments). */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAboutToReturn) << (shiftForWord())))))), ourContextSqInt, 2); + } + + /* Now we know home is on the sender chain. + We could be returning to either a context or a frame. Find out which. */ + contextToReturnTo = null; + if (((((longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert(checkIsStillMarriedContextcurrentFP(home, GIV(framePointer))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (longAt(theFP + FoxSavedFP)) { + frameToReturnTo = ((char *)(longAt(theFP + FoxSavedFP))); + } + else { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + contextToReturnTo = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(contextToReturnTo)); + assert((contextToReturnTo == (nilObject())) + || (isContext(followMaybeForwarded(contextToReturnTo)))); + } + } + else { + contextToReturnTo = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + if ((/* isContext: */ + ((!(contextToReturnTo & (tagMask())))) + && (((longAt((void *)(contextToReturnTo))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + assert(checkIsStillMarriedContextcurrentFP(contextToReturnTo, GIV(framePointer))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + frameToReturnTo = ((char *)(senderOop - (smallIntegerTag()))); + contextToReturnTo = null; + } + } + + /* If returning to a context we must make a frame for it unless it is dead. */ + if (contextToReturnTo) { + frameToReturnTo = establishFrameForContextToReturnTo(contextToReturnTo); + if (!frameToReturnTo) { + /* error: home's sender is dead; cannot return */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + ourContext = longAt(GIV(framePointer) + FoxThisContext); + goto l4; + } + ourContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l4: + + /* begin externalCannotReturn:from: */ + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),ourContext); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + + /* Both ceBaseFrameReturnTrampoline & ceNonLocalReturnTrampoline pop + their caller's return pc into instructionPointer. In this uncommon case restore + it, since a send's call pushes the instructionPointer (after the arguments). */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), ourContext, 1); + } + } + + /* Now we have a frame to return to. If it is on a different page we must + free intervening pages and nil out intervening contexts. We must free + intervening stack pages because if we leave the pages to be divorced + then their contexts will be divorced with intact senders and instruction + pointers. This code is similar to primitiveTerminateTo. */ + assert(pageListIsWellFormed()); + + /* begin stackPageFor: */ + newPage = stackPageAtpages(pageIndexFor(frameToReturnTo), GIV(pages)); + if (newPage != GIV(stackPage)) { + theFP1 = (GIV(stackPage)->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP1)); + thePage1 = stackPageAtpages(pageIndexFor(theFP1), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + assert(isContext(currentCtx)); + freeStackPage(GIV(stackPage)); + while (1) { + assert(isContext(currentCtx)); + if ((((((longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && ((stackPageFor((theFP = frameOfMarriedContext(currentCtx)))) == newPage)) break; + if (((((longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + theFP1 = (thePage->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP1)); + thePage1 = stackPageAtpages(pageIndexFor(theFP1), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + freeStackPage(thePage); + } + else { + nextCntx = longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + + /* begin markContextAsDead: */ + assert(isContext(currentCtx)); + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(SenderIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + currentCtx = nextCntx; + } + } + + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (newPage->headSP); + GIV(framePointer) = (newPage->headFP); + } + + /* Two cases. Returning to the top frame or an interior frame. The + top frame has its instruction pointer on top of stack. An interior + frame has its instruction pointer in the caller frame. We need to + peel back any frames on the page until we get to the correct frame. */ + if (GIV(framePointer) == frameToReturnTo) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + } + else { + do { + callerFP = GIV(framePointer); + GIV(framePointer) = ((char *)(longAt(GIV(framePointer) + FoxSavedFP))); + } while(GIV(framePointer) != frameToReturnTo); + GIV(instructionPointer) = longAt(callerFP + FoxCallerSavedIP); + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(callerFP))); + GIV(stackPointer) = (callerFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(callerFP))->cmNumArgs) + : byteAt((callerFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + return returntoExecutive(returnValue, 0); +} + + /* CoInterpreter>>#ceReapAndResetErrorCodeFor: */ +void +ceReapAndResetErrorCodeFor(CogMethod *cogMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(GIV(primFailCode) != 0); + GIV(newMethod) = (cogMethod->methodObject); + reapAndResetErrorCodeToheader(GIV(stackPointer) + BytesPerWord, (cogMethod->methodHeader)); +} + + +/* Entry-point for an abort send in a CogMethod (aboutToReturn:through:, + cannotReturn: et al). + Try and dispatch the send, but the send may turn into an MNU in which case + defer to + handleMNUInMachineCodeTo:... which will dispatch the MNU. + + Continue execution via either executeMethod or + interpretMethodFromMachineCode: depending on whether the target method is + cogged or not. */ + + /* CoInterpreter>>#ceSendAbort:to:numArgs: */ +static NoDbgRegParms sqInt +ceSendAborttonumArgs(sqInt selector, sqInt rcvr, sqInt numArgs) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classObj; + sqInt classTablePage; + sqInt classTag; + CogMethod *cogMethod; + sqInt errSelIdx; + sqInt fieldIndex; + sqInt methodHeader; + sqInt tagBits; + sqInt top; + + + /* self printExternalHeadFrame */ + /* self printStringOf: selector */ + assertCStackWellAligned(); + assert(addressCouldBeOop(rcvr)); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(selector), lengthOfMaybeImmediate(selector), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + GIV(argumentCount) = numArgs; + classTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + if (lookupInMethodCacheSelclassTag(selector, classTag)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, selector); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), selector); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + GIV(messageSelector) = selector; + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((classTag >= 0) + && ((classTag <= (tagMask())) + || ((classTag >= (arrayClassIndexPun())) + && (classTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classObj = null; + goto l1; + } + fieldIndex = classTag & ((1U << (classTableMajorIndexShift())) - 1); + classObj = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l1: + if ((errSelIdx = lookupOrdinaryNoMNUEtcInClass(classObj))) { + handleMNUInMachineCodeToclassForMessage(errSelIdx, rcvr, classObj); + + /* NOTREACHED */ + assert(0); + } + } + + /* check for coggability because method is in the cache */ + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (/* maybeMethodHasCogMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + executeNewMethod(); + assert(0); + } + return interpretMethodFromMachineCode(); +} + + +/* Send from an Open PIC when the first-level method lookup probe has failed, + or to continue when PIC creation has failed (e.g. because we're out of + code space), + or when a send has failed due to a forwarded receiver. */ +/* self printFrame: stackPage headFP WithSP: stackPage headSP */ +/* self printStringOf: selector */ + + /* CoInterpreter>>#ceSendFromInLineCacheMiss: */ +sqInt +ceSendFromInLineCacheMiss(CogMethod *cogMethodOrPIC) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classObj; + sqInt classTablePage; + sqInt classTag; + CogMethod *cogMethod; + sqInt errSelIdx; + sqInt fieldIndex; + sqInt methodHeader; + sqInt rcvr; + sqInt selector; + sqInt tagBits; + sqInt top; + + GIV(argumentCount) = (cogMethodOrPIC->cmNumArgs); + + /* skip return pc */ + rcvr = longAt(GIV(stackPointer) + ((((cogMethodOrPIC->cmNumArgs)) + 1) * BytesPerWord)); + assert(addressCouldBeOop(rcvr)); + classTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + + /* would like to assert this but must also allow for an interpretable method in the cache. */ + if (lookupInMethodCacheSelclassTag((cogMethodOrPIC->selector), classTag)) { + selector = (cogMethodOrPIC->selector); + + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, selector); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), selector); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + if (/* isOopForwarded: */ + ((!(((cogMethodOrPIC->selector)) & (tagMask())))) + && ((!((longAt((void *)((cogMethodOrPIC->selector)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + handleForwardedSelectorFaultFor((cogMethodOrPIC->selector)); + return ceSendFromInLineCacheMiss(cogMethodOrPIC); + } + if (classTag == (isForwardedObjectClassIndexPun())) { + handleForwardedSendFaultForReceiverstackDelta(rcvr, 1); + + /* skip return pc */ + return ceSendFromInLineCacheMiss(cogMethodOrPIC); + } + GIV(messageSelector) = (cogMethodOrPIC->selector); + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((classTag >= 0) + && ((classTag <= (tagMask())) + || ((classTag >= (arrayClassIndexPun())) + && (classTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classObj = null; + goto l1; + } + fieldIndex = classTag & ((1U << (classTableMajorIndexShift())) - 1); + classObj = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l1: + if ((errSelIdx = lookupOrdinaryNoMNUEtcInClass(classObj))) { + handleMNUInMachineCodeToclassForMessage(errSelIdx, rcvr, classObj); + + /* NOTREACHED */ + assert(0); + } + } + + /* check for coggability because method is in the cache */ + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (/* maybeMethodHasCogMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + executeNewMethod(); + assert(0); + } + return interpretMethodFromMachineCode(); +} + + +/* For RegisterAllocatingCogit we want the pc following a conditional branch + not to be reachable, so + we don't have to generate code to reload registers. But notionally the pc + following a conditional + branch is reached when continuing from a mustBeBoolean error. Instead of + supporting this in the + JIT, simply convert to an interpreter frame, backup the pc to the branch, + reenter the interpreter + and hence retry the mustBeBoolean send therein. N.B. We could do this for + immutability violations + too, but immutability is used in actual applications and so should be + performant, whereas + mustBeBoolean errors are extremely rare and so we choose brevity over + performance in this case. */ + + /* CoInterpreter>>#ceSendMustBeBooleanTo:interpretingAtDelta: */ +sqInt +ceSendMustBeBooleanTointerpretingAtDelta(sqInt aNonBooleanObject, sqInt jumpSize) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + sqInt top; + + assert(addressCouldBeOop(aNonBooleanObject)); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + convertFrametoInterpreterFrame(GIV(framePointer), jumpSize); + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),aNonBooleanObject); + GIV(stackPointer) = sp; + + /* and now reenter the interpreter... */ + ceInvokeInterpret(); + + /* NOTREACHED */ + return null; +} + + /* CoInterpreter>>#ceSendMustBeBoolean: */ +sqInt +ceSendMustBeBoolean(sqInt anObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + sqInt top; + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),anObject); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))), anObject, 0); +} + + +/* Entry-point for an unlinked directed super send in a CogMethod. Smalltalk + stack looks like + receiver + args + head sp -> sender return pc + methodClassBinding is an association whose value is the class above which + to start the lookup. */ + + /* CoInterpreter>>#ceSend:aboveClassBinding:to:numArgs: */ +void +ceSendaboveClassBindingtonumArgs(sqInt selector, sqInt methodClassBinding, sqInt rcvr, sqInt numArgs) +{ + ceSendabovetonumArgs(selector, fetchPointerofObject(ValueIndex, /* followMaybeForwarded: */ + (/* isOopForwarded: */ + ((!(methodClassBinding & (tagMask())))) + && ((!((longAt((void *)(methodClassBinding))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + ? followForwarded(methodClassBinding) + : methodClassBinding)), rcvr, numArgs); +} + + +/* Entry-point for an unlinked directed super send in a CogMethod. Smalltalk + stack looks like + receiver + args + head sp -> sender return pc + methodClass is the class above which to start the lookup. + + If an MNU then defer to handleMNUInMachineCodeTo:... which will dispatch + the MNU and + may choose to allocate a closed PIC with a fast MNU dispatch for this + send. Otherwise + attempt to link the send site as efficiently as possible. All link + attempts may fail; e.g. + because we're out of code memory. + + Continue execution via either executeMethod or + interpretMethodFromMachineCode: depending on whether the target method is + cogged or not. */ + + /* CoInterpreter>>#ceSend:above:to:numArgs: */ +sqInt +ceSendabovetonumArgs(sqInt selector, sqInt methodClass, sqInt rcvr, sqInt numArgs) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classObj; + sqInt classObjSqInt; + sqInt classPointer; + sqInt classTablePage; + sqInt classTag; + CogMethod *cogMethod; + CogMethod *cogMethod1; + sqInt err; + sqInt errSelIdx; + sqInt fieldIndex; + int hash; + sqInt methodHeader; + CogMethod *newCogMethod; + sqInt objOop; + sqInt tagBits; + sqInt top; + + + /* self printExternalHeadFrame */ + /* self printStringOf: selector */ + assertCStackWellAligned(); + assert(addressCouldBeOop(rcvr)); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(selector), lengthOfMaybeImmediate(selector), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + classPointer = /* followMaybeForwarded: */ + (/* isOopForwarded: */ + ((!(methodClass & (tagMask())))) + && ((!((longAt((void *)(methodClass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + ? followForwarded(methodClass) + : methodClass); + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, objOop); + } + classObjSqInt = objOop; + + /* begin classTagForClass: */ + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObjSqInt)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classTag = ((hash = (long32At((void *)(classObjSqInt + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObjSqInt) + ? ((err = enterIntoClassTable(classObjSqInt)) + ? -err + : (long32At((void *)(classObjSqInt + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + GIV(argumentCount) = numArgs; + if (lookupInMethodCacheSelclassTag(selector, classTag)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod1 = ((CogMethod *) methodHeader); + if (((cogMethod1->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod1, selector); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), selector); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + assert(!((isForwardedClassTag(classTag)))); + if (/* isOopForwarded: */ + ((!(selector & (tagMask())))) + && ((!((longAt((void *)(selector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + return ceSendabovetonumArgs(handleForwardedSelectorFaultFor(selector), methodClass, rcvr, numArgs); + } + GIV(messageSelector) = selector; + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((classTag >= 0) + && ((classTag <= (tagMask())) + || ((classTag >= (arrayClassIndexPun())) + && (classTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classObj = null; + goto l1; + } + fieldIndex = classTag & ((1U << (classTableMajorIndexShift())) - 1); + classObj = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l1: + if ((errSelIdx = lookupOrdinaryNoMNUEtcInClass(classObj))) { + if ((errSelIdx == SelectorDoesNotUnderstand) + && ((((usqInt)((cogMethod = cogMNUPICSelectorreceivermethodOperandnumArgs(GIV(messageSelector), rcvr, mnuMethodOrNilFor(rcvr), GIV(argumentCount)))))) > (minCogMethodAddress()))) { + linkSendAtintooffsetreceiver(longAt(GIV(stackPointer)), mframeHomeMethod(GIV(framePointer)), cogMethod, noCheckEntryOffset(), rcvr); + } + handleMNUInMachineCodeToclassForMessage(errSelIdx, rcvr, classObj); + assert(0); + } + } + + /* check for coggability because method is in the cache + Method found and has a cog method. Attempt to link to it. The receiver's class may be young. + We must not link to an Open PIC since they perform normal sends. */ + if (/* maybeMethodHasCogMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, selector); + } + else { + /* Deal with anonymous accessors, e.g. in Newspeak. The cogMethod may not have the + correct selector. If not, try and compile a new method with the correct selector. */ + if (((cogMethod->selector)) != selector) { + if ((newCogMethod = cogselector(GIV(newMethod), selector))) { + cogMethod = newCogMethod; + } + } + } + if (((cogMethod->selector)) == selector) { + linkSendAtintooffsetreceiver(longAt(GIV(stackPointer)), mframeHomeMethod(GIV(framePointer)), cogMethod, noCheckEntryOffset(), rcvr); + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + executeNewMethod(); + assert(0); + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + return interpretMethodFromMachineCode(); +} + + +/* Entry-point for an unlinked send in a CogMethod. Smalltalk stack looks + like receiver + args + head sp -> sender return pc + + If an MNU then defer to handleMNUInMachineCodeTo:... which will dispatch + the MNU and + may choose to allocate a closed PIC with a fast MNU dispatch for this + send. Otherwise + attempt to link the send site as efficiently as possible. All link + attempts may fail; e.g. + because we're out of code memory. + + Continue execution via either executeMethod or + interpretMethodFromMachineCode: depending on whether the target method is + cogged or not. */ + + /* CoInterpreter>>#ceSend:super:to:numArgs: */ +sqInt +ceSendsupertonumArgs(sqInt selector, sqInt superNormalBar, sqInt rcvr, sqInt numArgs) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classObj; + sqInt classObjSqInt; + sqInt classPointer; + sqInt classTablePage; + sqInt classTag; + CogMethod *cogMethod; + CogMethod *cogMethod1; + sqInt err; + sqInt errSelIdx; + sqInt fieldIndex; + int hash; + sqInt literal; + sqInt methodHeader; + sqInt methodPointer; + CogMethod *newCogMethod; + sqInt objOop; + sqInt offset; + sqInt tagBits; + sqInt top; + + + /* self printExternalHeadFrame */ + /* self printStringOf: selector */ + assertCStackWellAligned(); + assert(addressCouldBeOop(rcvr)); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(selector), lengthOfMaybeImmediate(selector), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + if (superNormalBar) { + methodPointer = /* frameMethodObject: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(GIV(framePointer)))->methodObject) + : longAt(GIV(framePointer) + FoxMethod)); + + /* begin methodClassOf: */ + offset = (literalCountOf(methodPointer)) - 1; + literal = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = fixFollowedFieldofObjectwithInitialValue(offset + LiteralStart, methodPointer, literal); + } + classPointer = ((literal != GIV(nilObj)) + && (/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + ? (assert((numSlotsOf(literal)) > ValueIndex), + followFieldofObject(ValueIndex, literal)) + : GIV(nilObj)); + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, objOop); + } + classObjSqInt = objOop; + + /* begin classTagForClass: */ + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObjSqInt)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classTag = ((hash = (long32At((void *)(classObjSqInt + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObjSqInt) + ? ((err = enterIntoClassTable(classObjSqInt)) + ? -err + : (long32At((void *)(classObjSqInt + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + } + else { + classTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + } + GIV(argumentCount) = numArgs; + if (lookupInMethodCacheSelclassTag(selector, classTag)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod1 = ((CogMethod *) methodHeader); + if (((cogMethod1->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod1, selector); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), selector); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + if (/* isOopForwarded: */ + ((!(selector & (tagMask())))) + && ((!((longAt((void *)(selector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + return ceSendsupertonumArgs(handleForwardedSelectorFaultFor(selector), superNormalBar, rcvr, numArgs); + } + if (classTag == (isForwardedObjectClassIndexPun())) { + assert(superNormalBar == 0); + return ceSendsupertonumArgs(selector, superNormalBar, handleForwardedSendFaultForReceiverstackDelta(rcvr, 1), numArgs); + } + GIV(messageSelector) = selector; + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((classTag >= 0) + && ((classTag <= (tagMask())) + || ((classTag >= (arrayClassIndexPun())) + && (classTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classObj = null; + goto l1; + } + fieldIndex = classTag & ((1U << (classTableMajorIndexShift())) - 1); + classObj = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l1: + if ((errSelIdx = lookupOrdinaryNoMNUEtcInClass(classObj))) { + if ((errSelIdx == SelectorDoesNotUnderstand) + && ((((usqInt)((cogMethod = cogMNUPICSelectorreceivermethodOperandnumArgs(GIV(messageSelector), rcvr, mnuMethodOrNilFor(rcvr), GIV(argumentCount)))))) > (minCogMethodAddress()))) { + linkSendAtintooffsetreceiver(longAt(GIV(stackPointer)), mframeHomeMethod(GIV(framePointer)), cogMethod, (superNormalBar + ? noCheckEntryOffset() + : entryOffset()), rcvr); + } + handleMNUInMachineCodeToclassForMessage(errSelIdx, rcvr, classObj); + assert(0); + } + + /* Methods towards the roots of the class hierarchy are shared by many subclasses. + It is likely to be profitable to cog these methods but becAuse they're being sent to + instances of different classes they may not be found in the cache. Luckily we have + lastMethodCacheProbeWrite to track recent writes to the cache and hence can detect + when a method is used more than once but via different classes. */ + if ((GIV(methodCache)[GIV(lastMethodCacheProbeWrite) + MethodCacheMethod]) == GIV(newMethod)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod1 = ((CogMethod *) methodHeader); + if (((cogMethod1->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod1, selector); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), selector); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + } + + /* check for coggability because method is in the cache + Method found and has a cog method. Attempt to link to it. The receiver's class may be young. + If the Cogit can't store young classes in inline caches we can link to an open PIC instead. */ + if (/* maybeMethodHasCogMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, selector); + } + else { + /* Deal with anonymous accessors, e.g. in Newspeak. The cogMethod may not have the + correct selector. If not, try and compile a new method with the correct selector. */ + if (((cogMethod->selector)) != selector) { + if ((newCogMethod = cogselector(GIV(newMethod), selector))) { + cogMethod = newCogMethod; + } + } + } + if (((cogMethod->selector)) == selector) { + linkSendAtintooffsetreceiver(longAt(GIV(stackPointer)), mframeHomeMethod(GIV(framePointer)), cogMethod, (superNormalBar + ? noCheckEntryOffset() + : entryOffset()), rcvr); + } + else { + if (!superNormalBar) { + patchToOpenPICFornumArgsreceiver(selector, numArgs, rcvr); + } + } + + /* If patchToOpenPICFor:.. returns we're out of code memory */ + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + executeNewMethod(); + assert(0); + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + return interpretMethodFromMachineCode(); +} + + +/* If contextSwitchIfNotNil is nil we can't context switch. + contextSwitchIfNotNil is set to nil by + - the special primitiveClosureValueNoContextSwitch entry-point in block + dispatch - the stack check in methods with primitive 198. + In a normal method contextSwitchIfNotNil will be the method (see e.g. + SimpleStackBasedCogit>>compileFrameBuild). In a block it will be the + closure (see e.g. SimpleStackBasedCogit>>compileMethodBody). */ + + /* CoInterpreter>>#ceStackOverflow: */ +void +ceStackOverflow(sqInt contextSwitchIfNotNil) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ceMethodAbortRetAddr; + CogBlockMethod *cogMethod; + sqInt switched; + + /* begin popStack */ + ceMethodAbortRetAddr = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* begin mframeCogMethod: */ + cogMethod = ((CogBlockMethod *) ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodMask)); + assert((ceMethodAbortRetAddr - (abortOffset())) == (((usqInt)(asCogHomeMethod(cogMethod))))); + GIV(instructionPointer) = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + GIV(method) = (GIV(newMethod) = (GIV(messageSelector) = GIV(nilObj))); + switched = handleStackOverflowOrEventAllowContextSwitch(contextSwitchIfNotNil != 0); + returnToExecutivepostContextSwitch(0, switched); + error("should not be reached"); +} + + +/* The high resolution timer has been found to be greater than + nextProfileTick after calling a primitive. + Now take a sample iff nextProfileTick has not been reset. c.f. + checkProfileTickPostPrimitive:. If the + primitive succeeds then signal the profile semaphore (activate the + profiling process) immediately + so that the sample is attributable to the current sender. */ + + /* CoInterpreter>>#ceTakeProfileSample */ +void +ceTakeProfileSample(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + + /* avoid calling assert in real VM cuz this may run on Smalltalk stack. */ + if (!GIV(profileProcess)) { + if (GIV(nextProfileTick) > 0) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + GIV(profileProcess) = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + if (!GIV(primFailCode)) { + GIV(profileMethod) = GIV(newMethod); + + /* Zero nextProfileTick because signalling the semaphore is when the VM effectively delivers the sample. */ + + /* begin zeroNextProfileTick */ + GIV(nextProfileTick) = 0; + if (synchronousSignal(GIV(profileSemaphore))) { + returnToExecutivepostContextSwitch(0, 1); + assert(!(1)); + } + else { + /* we reach here when there is a higher priority process than the profile process active, e.g. the timer process */ + } + } + } + else { + GIV(profileMethod) = null; + } + } + + /* if not nil a sample has already been taken. + force an interrupt check, but do not use forceInterruptCheck (which may + call interruptCheckChain) so that this can be run on the Smalltalk stack + from compileOnStackExternalPrimitive:flags: */ + GIV(stackLimit) = ((char *) (((usqInt) -1))); +} + + /* CoInterpreter>>#ceTraceBlockActivation */ +void +ceTraceBlockActivation(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt thingOne; + + if (recordBlockTrace()) { + thingOne = ((mframeHomeMethod(GIV(framePointer)))->methodObject); + + /* begin recordTrace:source:thing:extra: */ + if (TraceLog) { + GIV(traceLog)[GIV(traceLogIndex)] = ((((((usqInt)(TraceBlockActivation) << TraceTypeShift)))) + CSFromMachineCode); + GIV(traceLog)[GIV(traceLogIndex) + 1] = thingOne; + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + if (printOnTrace()) { + printActivationNameForreceiverisBlockfirstTemporary(((mframeHomeMethod(GIV(framePointer)))->methodObject), /* frameReceiver: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? longAt(GIV(framePointer) + FoxMFReceiver) + : longAt(GIV(framePointer) + FoxIFReceiver)), 1, null); + cr(); + } + } +} + + /* CoInterpreter>>#ceTraceLinkedSend: */ +void +ceTraceLinkedSend(sqInt theReceiver) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cogMethod; + sqInt selector; + sqInt tagBits; + sqInt thingOne; + sqInt thingTwo; + + cogMethod = ((CogMethod *) ((longAt(GIV(stackPointer))) - (traceLinkedSendOffset()))); + if (((cogMethod->cmNumArgs)) > (numRegArgs())) { + } + else { + } + GIV(stackPointer) + BytesPerWord; + + /* stackValue: */ + longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* empty maybeCheckStackDepth:sp:pc: */ + + /* cogit recordSendTrace ifTrue: is implicit; wouldn't compile the call otherwise. */ + thingOne = /* fetchClassOf: */ + ((tagBits = theReceiver & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(theReceiver)); + thingTwo = (cogMethod->selector); + + /* begin recordTrace:source:thing:extra: */ + if (TraceLog) { + GIV(traceLog)[GIV(traceLogIndex)] = 1 /* (TraceSend + CSFromMachineCode) */; + GIV(traceLog)[GIV(traceLogIndex) + 1] = thingOne; + GIV(traceLog)[GIV(traceLogIndex) + 2] = thingTwo; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + if (printOnTrace()) { + printActivationNameForreceiverisBlockfirstTemporary((cogMethod->methodObject), theReceiver, 0, null); + cr(); + } + selector = (cogMethod->selector); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(selector), lengthOfMaybeImmediate(selector), /* fetchClassTagOf: */ + ((tagBits = theReceiver & (tagMask())) + ? tagBits + : (longAt((void *)(theReceiver))) & (classIndexMask()))); +} + + +/* For assertion checking. */ + + /* CoInterpreter>>#ceTraceStoreOf:into: */ +void +ceTraceStoreOfinto(sqInt aValue, sqInt anObject) +{ + assert((isImmediate(aValue)) + || (addressCouldBeObj(aValue))); + assert(addressCouldBeObj(anObject)); +} + + /* CoInterpreter>>#cFramePointerAddress */ +usqInt +cFramePointerAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(CFramePointer)))); +} + + /* CoInterpreter>>#checkAssertsEnabledInCoInterpreter */ +void +checkAssertsEnabledInCoInterpreter(void) +{ + sqInt assertsAreEnabledInCoInterpreter; + + assertsAreEnabledInCoInterpreter = 0; + assert(assertsAreEnabledInCoInterpreter); +} + + +/* Perform an integrity/leak check using the heapMap. Assume + clearLeakMapAndMapAccessibleObjects has set a bit at each + object's header. Check that all object references in machine + code are valid. Answer if all checks pass. */ + + /* CoInterpreter>>#checkCodeIntegrity: */ +static NoDbgRegParms sqInt +checkCodeIntegrity(sqInt gcModes) +{ + return checkIntegrityOfObjectReferencesInCode(gcModes); +} + + +/* In Spur a primitive may fail due to encountering a forwarder. On failure, + check the accessorDepth for the + primitive and if non-negative scan the args to the depth, following any + forwarders. Answer if any are found + so the prim can be retried. The primitive index is derived from newMethod. + + See + http://www.mirandabanda.org/cogblog/2014/02/08/primitives-and-the-partial-read-barrier/ + and SpurMemoryManager's class comment. */ +/* Override to log and export to Cogit */ + + /* CoInterpreter>>#checkForAndFollowForwardedPrimitiveState */ +sqInt +checkForAndFollowForwardedPrimitiveState(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt accessorDepth; + sqInt aMethodSelectorOrImmediate; + usqInt firstBytecode; + sqInt found; + sqInt header; + sqInt index; + sqInt methodHeader; + sqInt oop; + sqInt primIndex; + sqInt referent; + sqInt scannedStackFrame; + sqInt tagBits; + + if ((recordPrimTrace()) + && (!primTracePluginName)) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveFailure << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + aMethodSelectorOrImmediate = /* fetchClassOf: */ + ((tagBits = (longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord))) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)))); + + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = aMethodSelectorOrImmediate; + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + assert(failed()); + found = 0; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIndex = 0; + } + assert((GIV(argumentCount) == (argumentCountOf(GIV(newMethod)))) + || (isMetaPrimitiveIndex(primIndex))); + + /* First things first; make sure the metadata has been followed before it is accessed to derive accessorDepth... */ + if (((/* isCalloutPrimitiveIndex: */ + (primIndex == PrimNumberExternalCall) + || (primIndex == PrimNumberFFICall)) + || (primIndex == PrimNumberDoExternalCall)) + && (unfollowFirstLiteralOfMaybeCalloutMethodprimitiveIndex(GIV(newMethod), primIndex))) { + found = 1; + } + + /* If the primitive is one of the meta primitives PrimNumberDoPrimitive or PrimNumberDoExternalCall, then + metaAccessorDepth will have been set to nil at the start of the primitive, and to the accessor depth of the + called primitive (or external call) immediately before dispatch. Hence if primIndex is that of a meta primitive + then if metaAccessorDepth is -2, the accessor depth is that of the meta primitive, and if > -2, then + metaAccessorDepth is the accessor depth of the primitive (or external call). Similarly, if the primitive is + primitiveExternalCall then the accessor depth is that of primitiveExternalCall until primitiveFunctionPointer + is assigned, at which point the accessor depth is taken from the slot in newMethod's first literal. */ + accessorDepth = ((/* isMetaPrimitiveIndex: */ + (primIndex == PrimNumberDoPrimitive) + || (primIndex == PrimNumberDoExternalCall)) + && (GIV(metaAccessorDepth) > -2) + ? GIV(metaAccessorDepth) + : accessorDepthForPrimitiveMethod(GIV(newMethod))); + assert(((accessorDepth >= -1) && (accessorDepth <= 5))); + if (accessorDepth >= 0) { + scannedStackFrame = 0; + for (index = 0; index <= GIV(argumentCount); index += 1) { + oop = longAt((void *)(GIV(stackPointer) + (index * BytesPerWord))); + if ((!(oop & (tagMask())))) { + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + assert(index < GIV(argumentCount)); + found = 1; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + + /* stackValue:put: */ + longAtput((void *)(GIV(stackPointer) + (index * BytesPerWord)),oop); + if (!scannedStackFrame) { + scannedStackFrame = 1; + + /* Avoid repeated primitive failures by following all state in the current stack frame. */ + followForwardedFrameContentsstackPointer(GIV(framePointer), GIV(stackPointer) + ((GIV(argumentCount) + 1) * BytesPerWord)); + } + } + if ((accessorDepth > 0) + && ((/* hasPointerFields: */ + ((!(oop & (tagMask())))) + && (hasPointerFieldsNonImm(oop))) + && (followForwardedObjectFieldstoDepth(oop, accessorDepth)))) { + found = 1; + } + } + } + } + if (found + && (recordPrimTrace())) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveRetry << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + return found; +} + + +/* Necessary because we very much want CStackPointer and CFramePointer to be + static/private and grouped + with other interpreter variables which will hence be accessed via + VarBaseReg on platforms that have one. + */ + + /* CoInterpreter>>#checkIfCFramePointerInUse */ +sqInt +checkIfCFramePointerInUse(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return isCFramePointerInUse((&GIV(CFramePointer)), (&GIV(CStackPointer))); +} + + /* CoInterpreter>>#checkingLongRunningPrimitives */ +#if LRPCheck +int +checkingLongRunningPrimitives(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(longRunningPrimitiveCheckSemaphore); +} +#endif /* LRPCheck */ + + +/* Check if the argument is an ok object. + If this is a pointers object, check that its fields are all okay oops. */ + + /* CoInterpreter>>#checkOkayFields: */ +static NoDbgRegParms sqInt +checkOkayFields(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fieldOop; + sqInt hasYoung; + sqInt i; + sqInt iSqInt; + + if ((oop == null) + || (oop == 0)) { + return 1; + } + if ((((oop) & 7) == 1)) { + return 1; + } + if (!(checkOkayOop(oop))) { + return 0; + } + if (!(checkOopHasOkayClass(oop))) { + return 0; + } + if (!((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + || (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return 1; + } + hasYoung = 0 /* (hasSpurMemoryManagerAPI not) */; + i = (numPointerSlotsOf(oop)) - 1; + while (i >= 0) { + fieldOop = longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(fieldOop & (smallIntegerTag())))) { + if ((i == 0) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) { + if (!(methodFor(((void *)fieldOop)))) { + fprintf(GIV(transcript), + "method %p has an invalid cog method reference\n", + ((void *)oop)); + return 0; + } + } + else { + hasYoung = hasYoung + || (/* isYoung: */ + ((!(fieldOop & (tagMask())))) + && (oopisLessThan(fieldOop, GIV(oldSpaceStart)))); + if (!(checkOkayOop(fieldOop))) { + return 0; + } + if (!(checkOopHasOkayClass(fieldOop))) { + return 0; + } + } + } + i -= 1; + } + if (hasYoung) { + /* begin checkOkayYoungReferrer: */ + if (oopisLessThan(oop, GIV(oldSpaceStart))) { + return 1; + } + if (!((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + fprintf(GIV(transcript), + "remembered bit is not set in %p\n", + ((void *)oop)); + return 0; + } + + /* begin isInRememberedSet: */ + for (iSqInt = 0; iSqInt < GIV(rememberedSetSize); iSqInt += 1) { + if ((GIV(rememberedSet)[iSqInt]) == oop) { + return 1; + } + } + fprintf(GIV(transcript), + "%p has remembered bit set but is not in remembered set\n", + ((void *)oop)); + return 0; + } + return 1; +} + + +/* Check if the profile timer has expired during a primitive and if so take a + sample. If the primitive has failed sample the profileMethod as nil. */ +/* If continuing from primitive[Full]ClosureValueNoContextSwitch defer until + the next check + */ + + /* CoInterpreter>>#checkProfileTickPostPrimitive */ +static NeverInline void +checkProfileTickPostPrimitive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + + if (GIV(deferProfileCheckForVNCS)) { + GIV(deferProfileCheckForVNCS) = 0; + } + else { + if ((GIV(nextProfileTick) > 0) + && ((ioHighResClock()) >= GIV(nextProfileTick))) { + if (!GIV(profileProcess)) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + GIV(profileProcess) = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + if (GIV(primFailCode)) { + GIV(profileMethod) = null; + } + else { + GIV(profileMethod) = GIV(newMethod); + + /* begin zeroNextProfileTick */ + GIV(nextProfileTick) = 0; + if (synchronousSignal(GIV(profileSemaphore))) { + returnToExecutivepostContextSwitch(0, 1); + assert(!(1)); + } + else { + /* we reach here when there is a higher priority process than the profile process active, e.g. the timer process */ + } + } + forceInterruptCheck(); + } + } + } +} + + /* CoInterpreter>>#cogMethodHasRealNonProfilingPrimitive: */ +static NoDbgRegParms sqInt +cogMethodHasRealNonProfilingPrimitive(CogMethod *cogMethod) +{ + return (((cogMethod->cmType)) >= CMMethod) + && (isRealNonProfilingPrimitiveMethod((cogMethod->methodObject))); +} + + /* CoInterpreter>>#cogMethodHasTooManyLiterals: */ +sqInt +cogMethodHasTooManyLiterals(CogMethod *aCogMethod) +{ + return !(((/* begin literalCountOfMethodHeader: */ + assert((((((aCogMethod->methodHeader))) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((((aCogMethod->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile); +} + + /* CoInterpreter>>#cogMethodOf: */ +CogMethod * +cogMethodOf(sqInt aMethodOop) +{ + sqInt methodHeader; + + methodHeader = longAt((void *)((aMethodOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + return ((CogMethod *) methodHeader); +} + + /* CoInterpreter>>#commenceCogCompiledCodeCompaction */ +static void +commenceCogCompiledCodeCompaction(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong startTime; + sqInt top; + + GIV(cogCompiledCodeCompactionCalledFor) = 0; + if (recordEventTrace()) { + /* begin recordTrace: */ + /* begin recordTrace:source:thing:extra: */ + if (TraceLog) { + GIV(traceLog)[GIV(traceLogIndex)] = ((((((usqInt)(TraceCodeCompaction) << TraceTypeShift))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = 0; + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + } + if (recordPrimTrace()) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TraceCodeCompaction << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + startTime = ioUTCMicrosecondsNow(); + + /* This can be called in a number of circumstances. The instructionPointer + may contain a native pc that must be relocated. There may already be a + pushed instructionPointer on stack. Clients ensure that instructionPointer + is 0 if it should not be pushed and/or relocated. Pushing twice is a mistake + because only the top one will be relocated. */ + if (GIV(instructionPointer)) { + /* better not have already been pushed */ + assert((((usqInt)(stackTop()))) != GIV(instructionPointer)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + (GIV(stackPage)->headSP = GIV(stackPointer)); + } + assertValidStackedInstructionPointers(__LINE__); + compactCogCompiledCode(); + + /* begin nilUncoggableMethods */ + GIV(lastCoggableInterpretedBlockMethod) = (GIV(lastUncoggableInterpretedBlockMethod) = null); + if (GIV(instructionPointer)) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin externalWriteBackHeadStackPointer */ + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + (GIV(stackPage)->headSP = GIV(stackPointer)); + } + assertValidStackedInstructionPointers(__LINE__); + GIV(statCodeCompactionCount) += 1; + GIV(statCodeCompactionUsecs) += (ioUTCMicrosecondsNow()) - startTime; + if (checkForLeaks) { + clearLeakMapAndMapAccessibleObjects(); + asserta(checkCodeIntegrity(0)); + } +} + + /* CoInterpreter>>#compilationBreakpointFor: */ +void +compilationBreakpointFor(sqInt selectorOop) +{ + suppressHeartbeatFlag = 1; + warning("compilation send break (heartbeat suppressed)"); +} + + +/* Answer a value to store in the InstructionPointer index of a context + object for theIP and theFP. + Mapping native pcs to bytecode pcs is quite expensive, requiring a search + through the method + map. We mitigate this cost by deferring mapping until we really have to, + which is when a context's + instruction pointer is accessed by Smalltalk code (either direct inst var + access or through the + instVarAt: primitive). But to defer mapping we have to be able to + distinguish machine code from + bytecode pcs, which we do by using negative values for machine code pcs. + So if the frame is a + machine code one answer the negation of the offset in the cog method. + + As a whorish performance hack we also include the block method offset in + the pc of a block. + The least significant 16 bits are the native pc and the most significant + 14 bits are the block + start, in block alignment units. So when mapping back we can find the + start of the block. + + See mustMapMachineCodePC:context: for the code that does the actual + mapping. */ + + /* CoInterpreter>>#contextInstructionPointer:frame: */ +static NoDbgRegParms sqInt +contextInstructionPointerframe(sqInt theIP, char *theFP) +{ + sqInt blockOffset; + CogBlockMethod *cogMethod; + CogMethod *homeMethod; + + assert(validInstructionPointerinFrame(theIP, theFP)); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* begin mframeCogMethod: */ + cogMethod = ((CogBlockMethod *) ((longAt(theFP + FoxMethod)) & MFMethodMask)); + if (theIP == (ceCannotResumePC())) { + return HasBeenReturnedFromMCPCOop; + } + if (((cogMethod->cmType)) >= CMMethod) { + return (((usqInt)((((sqInt)cogMethod)) - theIP) << 3) | 1); + } + + /* cmHomeMethod */ + if (((cogMethod->cmType)) >= CMMethod) { + homeMethod = ((CogMethod *) cogMethod); + } + else { + assert(CMBlock == (cogMethod->cmType)); + homeMethod = ((CogMethod *) ((((usqInt)cogMethod)) - ((cogMethod->homeOffset)))); + } + blockOffset = ((((sqInt)homeMethod)) - (((sqInt)cogMethod))) / (blockAlignment()); + return (((usqInt)((((((usqInt)(blockOffset) << 16)))) | (((((sqInt)cogMethod)) - theIP) & 0xFFFF)) << 3) | 1); + } + return (((usqInt)(((((theIP == (ceReturnToInterpreterPC()) + ? ((usqInt)(longAt(theFP + FoxIFSavedIP))) + : theIP)) - (longAt(theFP + FoxMethod))) - BaseHeaderSize) + 2) << 3) | 1); +} + + +/* Convert the given machine code frame to an interpreter frame. Back up the + pc by + pcDelta. Support for backing up the pc in primitiveSuspend for processes + waiting on condition variables. Support for mustBeBoolean in the + RegisterAllocatingCogit and + for cloneContext: in shallowCopy when a code compaction is caused by + machine code + to bytecode pc mapping. */ + + /* CoInterpreter>>#convertFrame:toInterpreterFrame: */ +static NoDbgRegParms void +convertFrametoInterpreterFrame(char *theFP, sqInt pcDelta) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogBlockMethod *cogMethod; + CogMethod *homeMethod; + sqInt methodHeader; + sqInt methodObj; + sqInt oop; + char *p; + usqInt startBcpc; + usqInt theIP; + char *theNewSP; + StackPage *thePage; + char *theSP; + + thePage = ((StackPage *) 0); + assert(isMachineCodeFrame(theFP)); + if (theFP == GIV(framePointer)) { + theIP = GIV(instructionPointer); + theSP = GIV(stackPointer); + } + else { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(theFP == ((thePage->headFP))); + theSP = (thePage->headSP); + theIP = longAt(theSP); + } + + /* begin mframeCogMethod: */ + cogMethod = ((CogBlockMethod *) ((longAt(theFP + FoxMethod)) & MFMethodMask)); + if ((((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0) + && (!((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)))) { + /* cmHomeMethod */ + if (((cogMethod->cmType)) >= CMMethod) { + homeMethod = ((CogMethod *) cogMethod); + } + else { + assert(CMBlock == (cogMethod->cmType)); + homeMethod = ((CogMethod *) ((((usqInt)cogMethod)) - ((cogMethod->homeOffset)))); + } + methodHeader = (homeMethod->methodHeader); + methodObj = (homeMethod->methodObject); + startBcpc = (cogMethod->startpc); + } + else { + methodHeader = ((((CogMethod *) cogMethod))->methodHeader); + methodObj = ((((CogMethod *) cogMethod))->methodObject); + startBcpc = (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop; + } + + /* Map the machine code instructionPointer to the interpreter instructionPointer of the branch. */ + theIP = bytecodePCForstartBcpcin(theIP, startBcpc, cogMethod); + + /* fetchByte uses pre-increment, so pre decrement */ + theIP = (((methodObj + BaseHeaderSize) + theIP) - pcDelta) - 1; + assert(validInstructionPointerinMethodframePointer(theIP, methodObj, theFP)); + + /* Make space for the two extra fields in an interpreter frame */ + for (p = theSP; p <= (theFP + FoxMFReceiver); p += BytesPerWord) { + oop = longAt(p); + longAtput((p - BytesPerWord) - BytesPerWord,longAt(p)); + } + + /* Fill in the fields */ + longAtput(theFP + FoxIFrameFlags,/* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(((cogMethod->cmNumArgs))) << ((BytesPerWord * 8) - 8))))) + (((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag + ? 1ULL << ((BytesPerWord * 8) - 16) + : 0))) + (((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag + ? 1ULL << ((BytesPerWord * 8) - 24) + : 0)) + : ((1 + ((((usqInt)(((cogMethod->cmNumArgs))) << 8)))) + (((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag + ? 0x10000 + : 0))) + (((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag + ? 0x1000000 + : 0)))); + longAtput(theFP + FoxIFSavedIP,theIP); + longAtput(theFP + FoxMethod,methodObj); + if (theFP == GIV(framePointer)) { + GIV(stackPointer) = (GIV(stackPointer) - BytesPerWord) - BytesPerWord; + GIV(instructionPointer) = theIP; + + /* begin setMethod:methodHeader: */ + GIV(method) = methodObj; + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + } + else { + theNewSP = (theSP - BytesPerWord) - BytesPerWord; + (thePage->headSP = theNewSP); + longAtput(theNewSP,ceReturnToInterpreterPC()); + } +} + + /* CoInterpreter>>#cReturnAddressAddress */ +usqInt +cReturnAddressAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(CReturnAddress)))); +} + + /* CoInterpreter>>#cStackPointerAddress */ +usqInt +cStackPointerAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(CStackPointer)))); +} + + +/* Defer smashes of the stackLimit around the call of functionSymbol (for + assert checks) + */ + + /* CoInterpreter>>#deferStackLimitSmashAround:with: */ +static NoDbgRegParms sqInt +deferStackLimitSmashAroundwith(void (*functionSymbol)(sqInt), sqInt arg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(deferSmash) = 1; + sqLowLevelMFence(); + functionSymbol(arg); + GIV(deferSmash) = 0; + sqLowLevelMFence(); + if (GIV(deferredSmash)) { + GIV(deferredSmash) = 0; + sqLowLevelMFence(); + forceInterruptCheck(); + } + return 1; +} + + +/* Divorce all frames that satisfy criterion and answer the current + activeContext. + */ + + /* CoInterpreter>>#divorceAllFramesSuchThat: */ +static NoDbgRegParms sqInt +divorceAllFramesSuchThat(sqInt (*criterion)(char *fp)) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + StackPage *aPage; + char *calleeFP; + sqInt divorcedSome; + sqInt i; + sqInt object; + sqInt theContext; + char *theFP; + char *theSP; + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l1; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l1: + + /* begin ensurePushedInstructionPointer */ + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + do { + if (GIV(stackPage)) { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + + /* This is needed for the assert in externalDivorceFrame:andContext: + Slang can't currently cope with the lack of the variable here. + Something to do with the preceding statement. Take it out + and the code is good. leave it in and we get do { ... } while(l1:) */ + + /* begin divorceSomeFramesIf: */ + divorcedSome = 0; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + aPage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(aPage))) { + /* this to avoid assert in externalDivorceFrame:andContext: */ + markStackPageMostRecentlyUsed(GIV(stackPage)); + + /* begin divorceAFrameIf:in: */ + calleeFP = ((char *) 0); + theFP = (aPage->headFP); + theSP = (aPage->headSP); + + /* theSP points at hottest item on frame's stack */ + theSP += BytesPerWord; + while (1) { + if (criterion(theFP)) { + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + theContext = longAt(theFP + FoxThisContext); + goto l4; + } + theContext = marryFrameSP(theFP, theSP); + /* end ensureFrameIsMarried:SP: */ +l4: + externalDivorceFrameandContext(theFP, theContext); + if (theFP == GIV(framePointer)) { + GIV(framePointer) = 0; + } + goto l2; + } + calleeFP = theFP; + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!(theFP != 0)) break; + /* theSP points at stacked hottest item on frame's stack */ + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(calleeFP))); + theSP = (calleeFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(calleeFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(calleeFP))->cmNumArgs) + : byteAt((calleeFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + goto l3; +l2: + divorcedSome = 1; +l3:; + } + } + } while(divorcedSome); + return activeContext; +} + + /* CoInterpreter>>#dumpStackAlignmentInfo */ +void +dumpStackAlignmentInfo(void) +{ + printf("STACK_ALIGN_BYTES:%d STACK_ALIGN_MASK:%d STACK_FP_ALIGNMENT:%d STACK_SP_ALIGNMENT:%d\n", + ((int) STACK_ALIGN_BYTES), + ((int) STACK_ALIGN_MASK), + ((int) STACK_FP_ALIGNMENT), + ((int) STACK_SP_ALIGNMENT)); +} + + +/* Map all native pcs to bytecoded pcs in all contexts whose method matches + criterion. + */ + + /* CoInterpreter>>#ensureAllContextsHaveBytecodePCsIf: */ +static NoDbgRegParms void +ensureAllContextsHaveBytecodePCsIf(sqInt (*criterion)(sqInt methodOop)) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt pc; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + pc = 0; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (((((((pc = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))))))) & 7) == 1)) + && ((pc < 0) + && (criterion((followObjFieldofObject(MethodIndex, obj))))))) { + widowOrForceToBytecodePC(obj); + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } +} + + +/* Make sure the context has a byetcode pc. Can only be used on single + contexts. + */ + + /* CoInterpreter>>#ensureContextHasBytecodePC: */ +static NoDbgRegParms void +ensureContextHasBytecodePC(sqInt aContext) +{ + sqInt pc; + + assert(!(isMarriedOrWidowedContext(aContext))); + pc = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + if (((((pc) & 7) == 1)) + && (((pc = (pc >> 3))) < 0)) { + pc = mustMapMachineCodePCcontext(pc, aContext); + assert(validBCPCinMethod((pc >> 3), fetchPointerofObject(MethodIndex, aContext))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aContext)) + && (!(isForwarded(aContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, aContext, pc)); + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),pc); + } +} + + +/* Safety to give the JIT lattitude in calling convention. Conceptually, + returning a value to a context involves pushing that value onto the stack. + This is used + in Squeak methods such as ContextPart>>jump + jump + | top | + thisContext sender push: nil. + stackp = 0 ifTrue: [self stepToSendOrReturn]. + stackp = 0 ifTrue: [self push: nil]. + top := self pop. + thisContext privSender: self. + ^top + Here jump may pop the value of a temporary variable off the stack which + will, conceptually and, in the interpreter, actually, get pushed back on + return. But + if the JIT is mapping the stack to registers disaster may ensue since the + value may not get pushed to the stack and code may access an invalid value + (e.g. a pc). + + The solution is to fall back on the interpreter. If the stack pointer is + changed we + also ensure the pc is a bytecode pc (+ive) which will cause + makeBaseFrameFor: to create an interpreter frame if the context is + executed again. */ + + /* CoInterpreter>>#ensureContextIsExecutionSafeAfterAssignToStackPointer: */ +static NoDbgRegParms void +ensureContextIsExecutionSafeAfterAssignToStackPointer(sqInt aContext) +{ + ensureContextHasBytecodePC(aContext); +} + + +/* Main entry-point into the interpreter at each execution level, where an + execution level is either the start of execution or reentry for a + callback. Capture the C stack + pointers so that calls from machine-code into the C run-time occur at this + level. This is the actual implementation, separated from + enterSmalltalkExecutive so the + simulator can wrap it in an exception handler and hence simulate the + Cogit's jump + back into C code on interpreting; see ceInvokeInterpret. + + Conceptually, an invocation of interpret exists at each level of execution + from the + initial invocation through each callback. Entry to each execution level is + through this function. It captures the C stack & frame pointers for this + level of execution + and then either invokes machine code or interpret, depending on whether + the current frame (the effective entry-point into Smalltalk execution) is + a machine code + or interpreted frame. In addition, interpret captures the return address + of its caller + (this function). The Cogit then uses the captured C stack pointers and + return address to invoke interpret as if it had been called from this + function. */ + + /* CoInterpreter>>#enterSmalltalkExecutiveImplementation */ +static sqInt +enterSmalltalkExecutiveImplementation(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assertCStackWellAligned(); + ceCaptureCStackPointers(); + assertSavedCStackPointersWellAligned(); + if ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())) { + returnToExecutivepostContextSwitch(0, 1); + } + interpret(); + return 0; +} + + +/* Execute a CogMethod from a linked send. The receiver, + arguments and return address are on the Smalltalk stack. First + push the entry-point and finally the register argument(s). Then write + back the frame pointers and call the routine that will pop off the + register argument(s) and jump to the entry by executing a return + instruction. + In the simple jit only the receiver gets passed in registers, so only the + receiver gets pushed. */ + + /* CoInterpreter>>#executeCogMethod:fromLinkedSendWithReceiver: */ +void +executeCogMethodfromLinkedSendWithReceiver(CogMethod *cogMethod, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + char *sp; + + assertCStackWellAligned(); + assert(isMachineCodeFrame(GIV(framePointer))); + assertValidExecutionPointersimbarline(longAt(GIV(stackPointer)), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + if ((numRegArgs()) > 0) { + if (((cogMethod->cmNumArgs)) <= (numRegArgs())) { + callRegisterArgCogMethodatreceiver(cogMethod, entryOffset(), rcvr); + } + } + + /* dont use and: so as to get Slang to inline cogit numRegArgs > 0 */ + object = (((sqInt)cogMethod)) + (entryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((sp = GIV(stackPointer) - BytesPerWord),rcvr); + GIV(stackPointer) = sp; + ceCallCogCodePopReceiverReg(); +} + + +/* Execute a CogMethod from an unlinked send. The receiver, + arguments and return address are on the Smalltalk stack. First + push the entry-point and finally the register argument(s). Then write + back the frame pointers and call the routine that will pop off the + register argument(s) and jump to the entry by executing a return + instruction. + In the simple jit only the receiver gets passed in registers, so only the + receiver gets pushed. */ + + /* CoInterpreter>>#executeCogMethod:fromUnlinkedSendWithReceiver: */ +static NoDbgRegParms void +executeCogMethodfromUnlinkedSendWithReceiver(CogMethod *cogMethod, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + char *sp; + + assertCStackWellAligned(); + assert(isMachineCodeFrame(GIV(framePointer))); + assertValidExecutionPointersimbarline(longAt(GIV(stackPointer)), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + if ((numRegArgs()) > 0) { + if (((cogMethod->cmNumArgs)) <= (numRegArgs())) { + callRegisterArgCogMethodatreceiver(cogMethod, noCheckEntryOffset(), rcvr); + } + } + + /* dont use and: so as to get Slang to inline cogit numRegArgs > 0 */ + object = (((sqInt)cogMethod)) + (noCheckEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((sp = GIV(stackPointer) - BytesPerWord),rcvr); + GIV(stackPointer) = sp; + ceCallCogCodePopReceiverReg(); +} + + +/* Execute a closed PIC from a linked send, to redispatch based on the rcvr. + The receiver, arguments and return address are on the Smalltalk stack. + First push the entry-point and finally the register argument(s). Then + write back the frame pointers and call the routine that will pop off the + register argument(s) and jump to the entry by executing a return + instruction. + In the simple jit only the receiver gets passed in registers, so only the + receiver gets pushed. */ + + /* CoInterpreter>>#executeCogPIC:fromLinkedSendWithReceiver:andCacheTag: */ +void +executeCogPICfromLinkedSendWithReceiverandCacheTag(CogMethod *cogPIC, sqInt rcvr, sqInt cacheTag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + char *sp; + + assertCStackWellAligned(); + assert(isMachineCodeFrame(GIV(framePointer))); + assertValidExecutionPointersimbarline(longAt(GIV(stackPointer)), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + object = (((sqInt)cogPIC)) + (entryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + if ((numRegArgs()) > 0) { + if (((cogPIC->cmNumArgs)) <= (numRegArgs())) { + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),cacheTag); + GIV(stackPointer) = sp; + switch ((cogPIC->cmNumArgs)) { + case 0: + ceCall0ArgsPIC(); + break; + case 1: + ceCall1ArgsPIC(); + break; + case 2: + ceCall2ArgsPIC(); + break; + default: +; + } + error("not reached"); + } + } + + /* dont use and: so as to get Slang to inline cogit numRegArgs > 0 */ + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),rcvr); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),cacheTag); + GIV(stackPointer) = sp; + ceCallCogCodePopReceiverAndClassRegs(); +} + + +/* Execute newMethod - either primitiveFunctionPointer must be set directly + (i.e. from primitiveExecuteMethod et al), or it would have been set + probing the method cache (i.e. primitivePerform et al). */ + + /* CoInterpreter>>#executeNewMethod */ +static sqInt +executeNewMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt arg; + sqInt argIndex; + sqInt fmt; + sqInt header; + usqInt i; + sqInt index; + int inInterpreter; + int inInterpreterInt; + sqInt maxNumArgs; + sqInt methodHeader; + sqInt methodHeaderSqInt; + usqInt numArgs; + usqInt numSlots; + usqInt numSlotsUsqInt; + usqInt numTemps; + sqInt object; + sqInt objectClass; + sqInt rcvr; + sqInt selectorOrProperties; + sqInt switched; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt types; + sqInt typesArraySize; + sqInt wordSize; + + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + if (primitiveFunctionPointer) { + if ((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex) { + externalQuickPrimitiveResponse(); + returntoExecutive(popStack(), inInterpreter); + return null; + } + + /* slowPrimitiveResponse may of course context-switch. If so we must reenter the + new process appopriately, returning only if we've reached here directly from the + interpreter and have found an interpreter frame. The instructionPointer tells us + from whence we came. */ + if (slowPrimitiveResponse()) { + returntoExecutive(popStack(), inInterpreter); + return null; + } + } + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* if not primitive, or primitive failed, activate the method */ + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,((sqInt)GIV(instructionPointer))); + GIV(instructionPointer) = ceReturnToInterpreterPC(); + } + activateNewCogMethodinInterpreter(((CogMethod *) methodHeader), inInterpreter); + } + else { + /* begin activateNewMethod */ + /* begin collectArgumentsTypeFor:fromStackAt:count: */ + /* begin tempVarTypesArraysOf: */ + /* begin additionalMethodStateOf:at: */ + /* begin additionalMethodStateOf: */ + selectorOrProperties = penultimateLiteralOf(GIV(newMethod)); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l3; + } + wordSize = BytesPerWord; + maxNumArgs = (((lengthOf(tempVarTypesArrays)) < GIV(argumentCount)) ? (lengthOf(tempVarTypesArrays)) : GIV(argumentCount)); + for (argIndex = 0; argIndex < maxNumArgs; argIndex += 1) { + /* begin followObjField:ofObject: */ + types = longAt((void *)((tempVarTypesArrays + BaseHeaderSize) + ((((usqInt)(argIndex) << (shiftForWord())))))); + assert(isNonImmediate(types)); + if ((!((longAt((void *)(types))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + types = fixFollowedFieldofObjectwithInitialValue(argIndex, tempVarTypesArrays, types); + } + arg = longAt((void *)(GIV(stackPointer) + (argIndex * wordSize))); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (types) + && (isInstanceOfClassArray(types)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = arg & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(arg)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(types + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(types + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(types - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, types, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, types, objectClass)); + assert(isNonImmediate(types)); + if (oopisGreaterThanOrEqualTo(types, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(types + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(types); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2:; + } + /* end collectArgumentsTypeFor:fromStackAt:count: */ +l3: + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeaderSqInt = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeaderSqInt)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeaderSqInt)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(!(isOopForwarded(rcvr))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreterInt = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeaderSqInt); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeaderSqInt)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeaderSqInt) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeaderSqInt >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + if (((methodHeaderSqInt & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeaderSqInt); + } + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc). */ + switched = 1; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(canContextSwitchIfActivatingheader(GIV(newMethod), methodHeaderSqInt)); + } + returnToExecutivepostContextSwitch(inInterpreterInt, switched); + } + return 0; +} + + +/* Execute newMethod - either primitiveFunctionPointer must be set directly + (i.e. from primitiveExecuteMethod et al), or it would have been set + probing the method cache (i.e. primitivePerform et al). + Eagerly compile it if appropriate so that doits are fast. */ + + /* CoInterpreter>>#executeNewMethodJitting */ +static sqInt +executeNewMethodJitting(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt arg; + sqInt argIndex; + sqInt fmt; + sqInt header; + usqInt i; + sqInt index; + int inInterpreter; + int inInterpreterInt; + sqInt maxNumArgs; + sqInt methodHeader; + sqInt methodHeaderSqInt; + usqInt numArgs; + usqInt numSlots; + usqInt numSlotsUsqInt; + usqInt numTemps; + sqInt object; + sqInt objectClass; + sqInt rcvr; + sqInt selectorOrProperties; + sqInt switched; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt types; + sqInt typesArraySize; + sqInt wordSize; + + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + if (primitiveFunctionPointer) { + if ((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex) { + externalQuickPrimitiveResponse(); + returntoExecutive(popStack(), inInterpreter); + return null; + } + + /* slowPrimitiveResponse may of course context-switch. If so we must reenter the + new process appopriately, returning only if we've reached here directly from the + interpreter and have found an interpreter frame. The instructionPointer tells us + from whence we came. */ + if (slowPrimitiveResponse()) { + returntoExecutive(popStack(), inInterpreter); + return null; + } + } + + /* Eagerly compile it if appropriate so that doits are fast. */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if (!((!(methodHeader & (smallIntegerTag()))))) { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(nilObj)); + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + + /* if not primitive, or primitive failed, activate the method */ + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,((sqInt)GIV(instructionPointer))); + GIV(instructionPointer) = ceReturnToInterpreterPC(); + } + activateNewCogMethodinInterpreter(((CogMethod *) methodHeader), inInterpreter); + } + else { + /* begin activateNewMethod */ + /* begin collectArgumentsTypeFor:fromStackAt:count: */ + /* begin tempVarTypesArraysOf: */ + /* begin additionalMethodStateOf:at: */ + /* begin additionalMethodStateOf: */ + selectorOrProperties = penultimateLiteralOf(GIV(newMethod)); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l3; + } + wordSize = BytesPerWord; + maxNumArgs = (((lengthOf(tempVarTypesArrays)) < GIV(argumentCount)) ? (lengthOf(tempVarTypesArrays)) : GIV(argumentCount)); + for (argIndex = 0; argIndex < maxNumArgs; argIndex += 1) { + /* begin followObjField:ofObject: */ + types = longAt((void *)((tempVarTypesArrays + BaseHeaderSize) + ((((usqInt)(argIndex) << (shiftForWord())))))); + assert(isNonImmediate(types)); + if ((!((longAt((void *)(types))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + types = fixFollowedFieldofObjectwithInitialValue(argIndex, tempVarTypesArrays, types); + } + arg = longAt((void *)(GIV(stackPointer) + (argIndex * wordSize))); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (types) + && (isInstanceOfClassArray(types)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = arg & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(arg)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(types + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(types + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(types - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, types, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, types, objectClass)); + assert(isNonImmediate(types)); + if (oopisGreaterThanOrEqualTo(types, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(types + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(types); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2:; + } + /* end collectArgumentsTypeFor:fromStackAt:count: */ +l3: + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeaderSqInt = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeaderSqInt)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeaderSqInt)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(!(isOopForwarded(rcvr))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreterInt = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeaderSqInt); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeaderSqInt)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeaderSqInt) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeaderSqInt >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + if (((methodHeaderSqInt & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeaderSqInt); + } + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc). */ + switched = 1; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(canContextSwitchIfActivatingheader(GIV(newMethod), methodHeaderSqInt)); + } + returnToExecutivepostContextSwitch(inInterpreterInt, switched); + } + return 0; +} + + +/* Fetch an instance variable from a maybe married context. + If the context is still married compute the value of the + relevant inst var from the spouse frame's state. + + If the context is single but has a negative instruction pointer + recognise that the instruction pointer is actually into machine + code and convert it to the corresponding bytecode pc. */ + + /* CoInterpreter>>#externalInstVar:ofContext: */ +static NoDbgRegParms sqInt +externalInstVarofContext(sqInt offset, sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt value; + + assert(isContext(aContext)); + assert(offset <= (ReceiverIndex + (checkStackPointerForMaybeMarriedContext(aContext)))); + + /* method, closureOrNil & receiver need no special handling; only + sender, pc & stackp have to be computed for married contexts. */ + if (!(offset <= StackPointerIndex)) { + return longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(offset) << (shiftForWord())))))); + } + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))) { + return fetchPointerofMarriedContext(offset, aContext); + } + value = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(offset) << (shiftForWord())))))); + + /* Why not update the slot to refer to the mapped pc? We're damned if we do, and damned if + we don't. If it is and the context is live and is returned to, then the pc will be mapped back + to machine code. If we don't then the mapping will be repeated on every access. What we + need are stats that show whether or not a read here predicts one or other outcome with any + certainty. For now, we keep it as it has been for a long time and do not update the slot. */ + if ((offset == InstructionPointerIndex) + && (((((value) & 7) == 1)) + && ((((int) value)) < 0))) { + return mustMapMachineCodePCcontext((value >> 3), aContext); + } + return value; +} + + +/* Find the compiled method to be run when the current messageSelector is + looked up in the + class/classIndex classTagArg, setting the values of newMethod and + primitiveFunctionPointer. + */ + + /* CoInterpreter>>#findNewMethodInClassTag: */ +static NoDbgRegParms void +findNewMethodInClassTag(sqInt classTagArg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt classTag; + CogMethod *cogMethod; + sqInt fieldIndex; + sqInt methodHeader; + + if (lookupInMethodCacheSelclassTag(GIV(messageSelector), classTagArg)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + /* entry was not found in the cache; perhaps soemthing was forwarded. */ + classTag = classTagArg; + if ((/* isOopForwarded: */ + ((!(GIV(messageSelector) & (tagMask())))) + && ((!((longAt((void *)(GIV(messageSelector)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + || (classTag == (isForwardedObjectClassIndexPun()))) { + if (/* isOopForwarded: */ + ((!(GIV(messageSelector) & (tagMask())))) + && ((!((longAt((void *)(GIV(messageSelector)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + GIV(messageSelector) = handleForwardedSelectorFaultFor(GIV(messageSelector)); + } + if (classTag == (isForwardedObjectClassIndexPun())) { + classTag = handleForwardedSendFaultForTag(classTag); + } + if (lookupInMethodCacheSelclassTag(GIV(messageSelector), classTag)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + return; + } + } + + /* entry was not found in the cache; look it up the hard way */ + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((classTag >= 0) + && ((classTag <= (tagMask())) + || ((classTag >= (arrayClassIndexPun())) + && (classTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + GIV(lkupClass) = null; + goto l1; + } + fieldIndex = classTag & ((1U << (classTableMajorIndexShift())) - 1); + GIV(lkupClass) = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l1: + lookupMethodInClass(GIV(lkupClass)); + addNewMethodToCache(GIV(lkupClass)); + } +} + + +/* The flag bit can be used to flag methods that are interpreted, if it has + been requested + from the image header flags. */ + + /* CoInterpreter>>#flagMethodAsInterpreted: */ +static NoDbgRegParms void +flagMethodAsInterpreted(sqInt aMethod) +{ + sqInt rawHeader; + sqInt realHeader; + + assert(flagInterpretedMethods()); + rawHeader = longAt((void *)((aMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + realHeader = ((!(rawHeader & (smallIntegerTag()))) + ? ((((CogMethod *) rawHeader))->methodHeader) + : rawHeader); + realHeader = realHeader | ((((usqInt)(1U << MethodHeaderFlagBitPosition) << 3) | 1)); + + /* begin isCogMethodReference: */ + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + ((((CogMethod *) rawHeader))->methodHeader = realHeader); + } + else { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aMethod)) + && (!(isForwarded(aMethod)))); + assert(validStorePointerUncheckedArgs(0, aMethod, realHeader)); + longAtput((void *)((aMethod + BaseHeaderSize) + (0U << (shiftForWord()))),realHeader); + } +} + + +/* Flush the references to external functions from plugin primitives. + Then continue execution answering self. + This will force a reload of those primitives when accessed next. + Note: We must flush the method cache here also, so that any failed + primitives are looked up again. + Override to ensure that any and all activations of an external method + have a bytecode pc so that if code generation changes (e.g. a primitive + method is used, unloaded, and the reloaded primitive is marked with + the FastCPrimitiveFlag) stale machine code pcs have been eliminated. + THIS MUST BE INVOKED IN THE CONTEXT OF A PRIMITIVE. */ + + /* CoInterpreter>>#flushExternalPrimitives */ +static void +flushExternalPrimitives(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt senderOop; + sqInt startObject; + char *theFP; + char *theFrame; + StackPage *thePage; + sqInt top; + + activeContext = divorceAllFramesSuchThat(isMachineCodeFrameForExternalPrimitiveMethod); + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (((byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + flushExternalPrimitiveOf(obj); + } + else { + if (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin mapToBytecodePCIfActivationOfExternalMethod: */ + if (isExternalMethodInPlugin(longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))))) { + if (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (isWidowedContext(obj)) { + goto l1; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + externalDivorceFrameandContext(theFP, obj); + } + } + ensureContextHasBytecodePC(obj); + } + /* end mapToBytecodePCIfActivationOfExternalMethod: */ +l1:; + } + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2: + assert(oopisGreaterThan(obj, prevObj)); + } + unlinkSendsToMethodsSuchThatAndFreeIf(cogMethodHasExternalPrim, 1); + + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + + /* begin flushExternalPrimitiveTable */ + memset(externalPrimitiveTable, 0, MaxExternalPrimitiveTableSize * (sizeof(externalPrimitiveTable[0]))); + GIV(externalPrimitiveTableFirstFreeIndex) = 0; + + /* If flushing led to divorce continue in the interpreter. */ + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + ceInvokeInterpret(); + } + + /* If not, work out where we are and continue */ + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + assert(((thePage->headFP)) == theFrame); + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* Arrange that any and all cog methods with machine code primitives can be + and are discarded. + Hence scan contexts and map their PCs to bytecode PCs if required, and + scan frames, divorcing + the frames of activations if required. Then continue execution answering + result. THIS MUST BE + INVOKED IN THE CONTEXT OF A PRIMITIVE. It exists to support + vmParameterAt:put:. */ + + /* CoInterpreter>>#flushMethodsWithMachineCodePrimitivesAndContinueAnswering: */ +static NoDbgRegParms void +flushMethodsWithMachineCodePrimitivesAndContinueAnswering(sqInt result) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt senderOop; + char *sp; + char *theFrame; + StackPage *thePage; + sqInt top; + + activeContext = divorceAllFramesSuchThat(isMachineCodeFrameForCogMethodWithMachineCodePrim); + ensureAllContextsHaveBytecodePCsIf(methodHasMachineCodePrimitiveMethod); + unlinkSendsToMethodsSuchThatAndFreeIf(cogMethodHasMachineCodePrim, 1); + + /* If the top frame was divorced then continue in the interpreter. */ + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* pop bogus machine-code instructionPointer, arguments and receiver */ + + /* begin pop:thenPush: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 2) - 1) * BytesPerWord)),result); + GIV(stackPointer) = sp; + ceInvokeInterpret(); + } + + /* If not, work out where we are and continue */ + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + assert(((thePage->headFP)) == theFrame); + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + + /* begin pop:thenPush: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),result); + GIV(stackPointer) = sp; +} + + /* CoInterpreter>>#followForwardedFieldsInCurrentMethod */ +static void +followForwardedFieldsInCurrentMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cogMethod; + sqInt methodField; + + if ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())) { + /* begin mframeHomeMethod: */ + methodField = longAt(GIV(framePointer) + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + cogMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + goto l1; + } + cogMethod = ((CogMethod *) (methodField & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + followForwardedObjectFieldstoDepth((cogMethod->methodObject), 0); + followForwardedLiteralsIn(cogMethod); + } + else { + followForwardedObjectFieldstoDepth(GIV(method), 0); + } +} + + +/* A more thorough version of followForwardingPointersInStackZone that also + follows all temporaries (but not stack contents after the temps). + This allows removal of the TempVectReadBarrier in the IGC */ + + /* CoInterpreter>>#followForwardingPointersOfReceiverAndTemporariesInStackZone */ +static void +followForwardingPointersOfReceiverAndTemporariesInStackZone(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + char *frameRcvrOffset; + sqInt header; + sqInt i; + sqInt methodHeader; + sqInt newOop; + sqInt offset; + usqInt oop; + sqInt referent; + char *theFP; + usqInt theIPPtr; + StackPage *thePage; + char *theSP; + + callerFP = ((char *) 0); + if (!GIV(stackPage)) { + assert((!(GIV(mostRecentlyUsedPage))) + || (isFree(GIV(mostRecentlyUsedPage)))); + return; + } + + /* the system must be snapshotting; nothing to do... */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((thePage->baseFP)) { + assert(ifCurrentStackPageHasValidHeadPointers(thePage)); + theFP = (thePage->headFP); + + /* Skip the instruction pointer on top of stack of inactive pages. */ + theIPPtr = (!(thePage == GIV(stackPage)) + ? ((usqInt)((thePage->headSP))) + : 0); + while (1) { + assert(addressIsInPage(thePage, theFP)); + assert((!theIPPtr) + || (addressIsInPage(thePage, ((void *)theIPPtr)))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + oop = longAt(theFP + FoxMFReceiver); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxMFReceiver,followForwarded(oop)); + } + assert(!(isForwarded(((mframeHomeMethod(theFP))->methodObject)))); + frameRcvrOffset = theFP + FoxMFReceiver; + methodHeader = ((mframeHomeMethod(theFP))->methodHeader); + } + else { + oop = longAt(theFP + FoxIFReceiver); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxIFReceiver,followForwarded(oop)); + } + oop = longAt(theFP + FoxMethod); + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + newOop = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(newOop & (tagMask())))) + && ((!((longAt((void *)(newOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + newOop = longAt((void *)((newOop + BaseHeaderSize) + (0U << (shiftForWord())))); + } + offset = newOop - oop; + if ((theIPPtr) + && ((longAt((void *)(theIPPtr))) > oop)) { + longAtput((void *)(theIPPtr),(longAt((void *)(theIPPtr))) + offset); + } + longAtput(theFP + FoxIFSavedIP,(longAt(theFP + FoxIFSavedIP)) + offset); + longAtput(theFP + FoxMethod,(oop = newOop)); + } + frameRcvrOffset = theFP + FoxIFReceiver; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(oop)); + header = longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + } + theSP = frameRcvrOffset - (((((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F) * BytesPerWord); + while (theSP <= frameRcvrOffset) { + oop = longAt(theSP); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + longAtput(theSP,oop); + } + theSP += BytesPerWord; + } + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) + && ((!((longAt((void *)(longAt(theFP + FoxThisContext)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxThisContext,followForwarded(longAt(theFP + FoxThisContext))); + } + offset = (FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))); + oop = longAt(theFP + offset); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + offset,followForwarded(oop)); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theIPPtr = ((usqInt)(theFP + FoxCallerSavedIP)); + theFP = callerFP; + } + + /* And finally follow the saved context and the caller context. */ + theSP = ((thePage->baseAddress)) - BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + longAtput(theSP,followForwarded(oop)); + } + theSP += BytesPerWord; + } + } + } +} + + +/* Spur's become: is lazy, turning the becommed object into a forwarding + object to the other. + The read-barrier is minimised by arranging that forwarding pointers will + fail a method cache + probe, since notionally objects' internals are accessed only via sending + messages to them, + the exception is primitives that access the internals of the non-receiver + argument(s). + To avoid a read barrier on bytecode, literal and inst var fetch and + non-local return, we scan + the receivers (including the stacked receiver for non-local return) and + method references + in the stack zone and follow any forwarded ones. This is of course way + cheaper than + scanning all of memory as in the old become. + + Override to handle machine code frames */ + + /* CoInterpreter>>#followForwardingPointersOfReceiversInStackZone */ +static void +followForwardingPointersOfReceiversInStackZone(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt i; + sqInt newOop; + sqInt offset; + usqInt oop; + char *theFP; + usqInt theIPPtr; + StackPage *thePage; + char *theSP; + + callerFP = ((char *) 0); + if (!GIV(stackPage)) { + assert((!(GIV(mostRecentlyUsedPage))) + || (isFree(GIV(mostRecentlyUsedPage)))); + return; + } + + /* the system must be snapshotting; nothing to do... */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((thePage->baseFP)) { + assert(ifCurrentStackPageHasValidHeadPointers(thePage)); + theFP = (thePage->headFP); + + /* Skip the instruction pointer on top of stack of inactive pages. */ + theIPPtr = (!(thePage == GIV(stackPage)) + ? ((usqInt)((thePage->headSP))) + : 0); + while (1) { + assert(addressIsInPage(thePage, theFP)); + assert((!theIPPtr) + || (addressIsInPage(thePage, ((void *)theIPPtr)))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + oop = longAt(theFP + FoxMFReceiver); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxMFReceiver,followForwarded(oop)); + } + assert(!(isForwarded(((mframeHomeMethod(theFP))->methodObject)))); + } + else { + oop = longAt(theFP + FoxIFReceiver); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxIFReceiver,followForwarded(oop)); + } + oop = longAt(theFP + FoxMethod); + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + newOop = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(newOop & (tagMask())))) + && ((!((longAt((void *)(newOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + newOop = longAt((void *)((newOop + BaseHeaderSize) + (0U << (shiftForWord())))); + } + offset = newOop - oop; + if ((theIPPtr) + && ((longAt((void *)(theIPPtr))) > oop)) { + longAtput((void *)(theIPPtr),(longAt((void *)(theIPPtr))) + offset); + } + longAtput(theFP + FoxIFSavedIP,(longAt(theFP + FoxIFSavedIP)) + offset); + longAtput(theFP + FoxMethod,(oop = newOop)); + } + } + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) + && ((!((longAt((void *)(longAt(theFP + FoxThisContext)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxThisContext,followForwarded(longAt(theFP + FoxThisContext))); + } + offset = (FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))); + oop = longAt(theFP + offset); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + offset,followForwarded(oop)); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theIPPtr = ((usqInt)(theFP + FoxCallerSavedIP)); + theFP = callerFP; + } + + /* And finally follow the saved context and the caller context. */ + theSP = ((thePage->baseAddress)) - BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + longAtput(theSP,followForwarded(oop)); + } + theSP += BytesPerWord; + } + } + } +} + + +/* Force an interrupt check ASAP. This version is the + entry-point to forceInterruptCheck for the heartbeat + timer to allow for repeatable debugging. */ + + /* CoInterpreter>>#forceInterruptCheckFromHeartbeat */ +void +forceInterruptCheckFromHeartbeat(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!suppressHeartbeatFlag) { + /* begin checkForLongRunningPrimitive */ +# if LRPCheck + if (!GIV(longRunningPrimitiveCheckSemaphore)) { + goto l1; + } + if ((GIV(longRunningPrimitiveStartUsecs) > 0) + && ((GIV(longRunningPrimitiveCheckMethod) == GIV(newMethod)) + && (GIV(longRunningPrimitiveCheckSequenceNumber) == GIV(statCheckForEvents)))) { + GIV(longRunningPrimitiveStopUsecs) = ioUTCMicroseconds(); + assert(GIV(longRunningPrimitiveStopUsecs) > GIV(longRunningPrimitiveStartUsecs)); + goto l1; + } + + /* See traceProfileState & mapProfileState. */ + if (!GIV(longRunningPrimitiveStopUsecs)) { + GIV(longRunningPrimitiveCheckSequenceNumber) = GIV(statCheckForEvents); + GIV(longRunningPrimitiveCheckMethod) = GIV(newMethod); + GIV(longRunningPrimitiveStartUsecs) = ioUTCMicroseconds(); + sqLowLevelMFence(); + } +# endif // LRPCheck + + /* end checkForLongRunningPrimitive */ +l1: + sqLowLevelMFence(); + if (GIV(deferSmash)) { + GIV(deferredSmash) = 1; + sqLowLevelMFence(); + } + else { + forceInterruptCheck(); + } + } +} + + +/* In the StackInterpreter the saved ip field of a base frame holds the + base frame's caller context. But in the Cog VM the first word on the + stack holds the base frame's caller context, which is immediately + above the stacked receiver. */ + + /* CoInterpreter>>#frameCallerContext: */ +static NoDbgRegParms sqInt +frameCallerContext(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + StackPage *thePage; + + assert(isBaseFrame(theFP)); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + return callerContextOrNil; +} + + /* CoInterpreter>>#frameHasContext: */ +static NoDbgRegParms int +frameHasContext(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0); +} + + +/* */ + + /* CoInterpreter>>#frameIsBlockActivation: */ +static NoDbgRegParms int +frameIsBlockActivation(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0); +} + + /* CoInterpreter>>#frameMethodObject: */ +static NoDbgRegParms sqInt +frameMethodObject(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); +} + + +/* See encodeFrameFieldHasContext:numArgs: */ + + /* CoInterpreter>>#frameNumArgs: */ +static NoDbgRegParms usqInt +frameNumArgs(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)); +} + + /* CoInterpreter>>#framePointerAddress */ +usqInt +framePointerAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(framePointer)))); +} + + /* CoInterpreter>>#framePrintDescription: */ +static NoDbgRegParms void +framePrintDescription(sqInt it) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!it) { + return; + } + if (it == GIV(nilObj)) { + fprintf(GIV(transcript), "=nil"); + return; + } + if (it == (ceReturnToInterpreterPC())) { + fprintf(GIV(transcript), "=ceReturnToInterpreterPC"); + return; + } + if (it == (ceBaseFrameReturnPC())) { + fprintf(GIV(transcript), "=ceBaseFrameReturnPC"); + return; + } + if (it == (ceCannotResumePC())) { + fprintf(GIV(transcript), "=ceCannotResumePC"); + return; + } + fprintf(GIV(transcript), + "=%" PRIdSQINT "", + it); +} + + /* CoInterpreter>>#frameReceiver: */ +static NoDbgRegParms sqInt +frameReceiver(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? longAt(theFP + FoxMFReceiver) + : longAt(theFP + FoxIFReceiver)); +} + + /* CoInterpreter>>#functionPointerForCompiledMethod:primitiveIndex:primitivePropertyFlagsInto: */ +void +(*functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(sqInt methodObj, sqInt primitiveIndex, sqInt *flagsPtr))(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt flags; + void (*functionPointer)(void); + sqInt header; + sqInt headerSqInt; + sqInt index; + sqInt lit; + sqInt litSqInt; + sqInt metadata; + usqInt metadataFlags; + sqInt primIdx; + sqInt targetFunctionIndex; + + flags = 0; + if (flagsPtr) { + flagsPtr[0] = ((flags = primitivePropertyFlagsprimIndex(methodObj, primitiveIndex))); + } + if (primitiveIndex == PrimNumberFFICall) { + /* begin functionForPrimitiveCallout */ + if ((((sqInt)primitiveCalloutPointer)) == -1) { + reloadPrimitiveCalloutPointer(); + } + if (flagsPtr) { + if (GIV(primCalloutIsExternal)) { + flagsPtr[0] = (flags | PrimCallIsExternalCall); + } + } + return primitiveCalloutPointer; + } + if (primitiveIndex == PrimNumberExternalCall) { + /* begin attemptToLinkExternalPrimitive: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + headerSqInt = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* primitiveIndexOfMethod:header: */ + if (((header & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((header >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIdx = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIdx = 0; + } + lit = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + assert((primIdx == PrimNumberExternalCall) + && (((literalCountOfMethodHeader(header)) > 0) + && ((isArray(lit)) + && ((numSlotsOf(lit)) == 4)))); + targetFunctionIndex = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord())))))); + assert((((targetFunctionIndex) & 7) == 1)); + if (!((targetFunctionIndex >> 3))) { + linkExternalCallerrInto(lit, null); + } + if (flagsPtr) { + /* N.B. We only support the FastCPrimitiveFlag on Spur because Spur will not run a GC to + satisfy an allocation in a primitive. The V3 ObjectMemory will and hence the depth of + stack needed in a V3 primitive is probably too large to safely execute on a stack page. */ + metadata = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFlagsIndex) << (shiftForWord())))))); + if ((((metadata) & 7) == 1)) { + metadataFlags = ((metadata >> 3)) & SpurPrimitiveFlagsMask; + + /* combine the specific external primitive's flags with the base flags. + Hence e.g. if profiling is in effect (as indicated by PrimCallCollectsProfileSamples + in the base flags) it remains in effect after combining the specific flags. */ + flags = flags | metadataFlags; + } + if (numBytesOf(longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralModuleNameIndex) << (shiftForWord())))))))) { + flags = flags | PrimCallIsExternalCall; + } + flagsPtr[0] = flags; + } + + /* begin functionForPrimitiveExternalCall: */ + if (!((literalCountOf(methodObj)) > 0)) { + return primitiveExternalCall; + } + + /* Check if it's an array of length 4 */ + litSqInt = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(litSqInt & (tagMask())))) + && (((byteAt((void *)(litSqInt + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((lengthOf(litSqInt)) == 4))) { + return primitiveExternalCall; + } + index = longAt((void *)((litSqInt + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord())))))); + if (!(((((index) & 7) == 1)) + && ((((index = (index >> 3))) > 0) + && (index <= MaxExternalPrimitiveTableSize)))) { + return primitiveExternalCall; + } + functionPointer = externalPrimitiveTable[index - 1]; + if (!functionPointer) { + return primitiveExternalCall; + } + return functionPointer; + } + + /* begin functionPointerFor:inClass: */ + return ((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]); +} + + +/* currentBytecode will be private to the main dispatch loop in the generated + code. This method allows the currentBytecode to be retrieved from global + variables. Override to answer -1 if we're not in an interpreter frame. */ + + /* CoInterpreter>>#getCurrentBytecode */ +sqInt +getCurrentBytecode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)GIV(framePointer))) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)GIV(framePointer))) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)GIV(framePointer))) <= (((usqInt)GIV(pages)))))))) + && (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) + ? byteAt((void *)(GIV(instructionPointer))) + : -1); +} + + +/* Handle a send fault that may be due to a send to a forwarded object. + Unforward the receiver on the stack and answer it. */ + + /* CoInterpreter>>#handleForwardedSendFaultForReceiver:stackDelta: */ +static NoDbgRegParms sqInt +handleForwardedSendFaultForReceiverstackDelta(sqInt forwardedReceiver, sqInt stackDelta) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt rcvr; + sqInt rcvrStackIndex; + + + /* should *not* be a super send, so the receiver should be forwarded. */ + assert(isOopForwarded(forwardedReceiver)); + rcvrStackIndex = GIV(argumentCount) + stackDelta; + assert((stackValue(rcvrStackIndex)) == forwardedReceiver); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(forwardedReceiver)); + rcvr = longAt((void *)((forwardedReceiver + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord())))); + } + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (rcvrStackIndex * BytesPerWord),rcvr); + followForwardedFrameContentsstackPointer(GIV(framePointer), GIV(stackPointer) + ((rcvrStackIndex + 1) * BytesPerWord)); + if (isPointers(/* frameReceiver: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? longAt(GIV(framePointer) + FoxMFReceiver) + : longAt(GIV(framePointer) + FoxIFReceiver)))) { + followForwardedObjectFieldstoDepth(/* frameReceiver: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? longAt(GIV(framePointer) + FoxMFReceiver) + : longAt(GIV(framePointer) + FoxIFReceiver)), 0); + } + followForwardedFieldsInCurrentMethod(); + return rcvr; +} + + +/* A message send from either an open PIC or an unlinked send has not been + understood. Create a message and execute the relevant resulting MNU + method. messageSelector is an implicit argument (yuck). */ + + /* CoInterpreter>>#handleMNU:InMachineCodeTo:classForMessage: */ +static NoDbgRegParms sqInt +handleMNUInMachineCodeToclassForMessage(sqInt selectorIndex, sqInt rcvr, sqInt classForMessage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classForThisMessage; + CogMethod *cogMethod; + sqInt errSelIdx; + sqInt methodHeader; + sqInt top; + + assert(addressCouldBeOop(rcvr)); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + createActualMessageTo(classForMessage); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(selectorIndex) << (shiftForWord())))))); + if (lookupInMethodCacheSelclassTag(GIV(messageSelector), classTagForClass(GIV(lkupClass)))) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + errSelIdx = lookupMNUInClass((classForThisMessage = GIV(lkupClass))); + if (errSelIdx) { + if (selectorIndex == SelectorDoesNotUnderstand) { + error("Recursive not understood error encountered"); + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return handleMNUInMachineCodeToclassForMessage(errSelIdx, rcvr, classForThisMessage); + } + } + + /* check for coggability because method is in the cache */ + if (/* maybeMethodHasCogMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + executeCogMethodfromUnlinkedSendWithReceiver(cogMethodOf(GIV(newMethod)), rcvr); + + /* NOTREACHED */ + assert(0); + } + return interpretMethodFromMachineCode(); +} + + +/* */ + + /* CoInterpreter>>#iframeIsBlockActivation: */ +static NoDbgRegParms int +iframeIsBlockActivation(char *theFP) +{ + return (byteAt((theFP + FoxIFrameFlags) + 3)) != 0; +} + + /* CoInterpreter>>#iframeReceiver: */ +static NoDbgRegParms sqInt +iframeReceiver(char *theFP) +{ + return longAt(theFP + FoxIFReceiver); +} + + /* CoInterpreter>>#iframeSavedIP: */ +static NoDbgRegParms usqInt +iframeSavedIP(char *theFP) +{ + return ((usqInt)(longAt(theFP + FoxIFSavedIP))); +} + + +/* This is for low-level error reporting. If either of the C stack pointers + are pointing into the stack zone then write them back to framePointer + and/or stackPointer so that the stack backtrace will be up to date. Write + their original values through savedFPP & savedSPP if non-null. */ + + /* CoInterpreter>>#ifValidWriteBackStack:Pointers:Save:To: */ +void +ifValidWriteBackStackPointersSaveTo(void *theCFP, void *theCSP, char **savedFPP, char **savedSPP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (savedFPP) { + savedFPP[0] = GIV(framePointer); + } + if (savedSPP) { + savedSPP[0] = GIV(stackPointer); + } + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theCFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theCFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theCFP)) <= (((usqInt)GIV(pages)))))))) { + GIV(framePointer) = theCFP; + } + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theCSP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theCSP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theCSP)) <= (((usqInt)GIV(pages)))))))) { + GIV(stackPointer) = theCSP; + } +} + + /* CoInterpreter>>#instructionPointerAddress */ +usqInt +instructionPointerAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(instructionPointer)))); +} + + +/* Answer the bytecode pc object (i.e. SmallInteger) for an active frame. The + bytecode pc is derived from the frame's pc. If the frame is the top frame + on the current stack + the frame pc is whatever the current instruction pointer is. If the frame + is the top + frame on some other stack the frame pc is the value on top of stack. + Otherwise the + frame pc is the saved pc of the frame above. Once the frame pc is found it + must be + mapped to a bytecode pc. */ + + /* CoInterpreter>>#instructionPointerForFrame:currentFP:currentIP: */ +static NoDbgRegParms sqInt +instructionPointerForFramecurrentFPcurrentIP(char *spouseFP, char *currentFP, sqInt instrPtr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + char *fp; + char *theFPAbove; + sqInt theIP; + StackPage *thePage; + sqInt value; + + if (spouseFP == currentFP) { + theIP = instrPtr; + } + else { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(spouseFP), GIV(pages)); + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == spouseFP) { + theFPAbove = 0; + goto l1; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == spouseFP) { + theFPAbove = fp; + goto l1; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + theFPAbove = 0; + /* end findFrameAbove:inPage: */ +l1: + theIP = (theFPAbove + ? longAt(theFPAbove + FoxCallerSavedIP) + : longAt((thePage->headSP))); + } + value = contextInstructionPointerframe(theIP, spouseFP); + return ((((int) value)) < 0 + ? mustMapMachineCodePCcontext((value >> 3), longAt(spouseFP + FoxThisContext)) + : value); +} + + +/* At a rough approximation we may need to allocate up to a couple + of page's worth of contexts when switching stack pages, assigning + to senders, etc. But the snapshot primitive voids all stack pages. + So a safe margin is the size of a large context times the maximum + number of frames per page times the number of pages. */ + + /* CoInterpreter>>#interpreterAllocationReserveBytes */ +static NeverInline sqInt +interpreterAllocationReserveBytes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt maxFramesPerPage; + sqInt maxUsedBytesPerPage; + + maxUsedBytesPerPage = (stackPageFrameBytes()) + (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord); + maxFramesPerPage = (maxUsedBytesPerPage / BytesPerWord) / MFrameSlots; + return ((maxFramesPerPage * LargeContextSlots) * BytesPerOop) * GIV(numStackPages); +} + + +/* Execute a method interpretively from machine code. We assume (require) + that newMethod + messageSelector, primitiveFunctionPointer and argumentCount have been set + in the caller. + Once evaluated either continue in the interpreter via a jongjmp or in + machine code via an + enilopmart (a form of longjmp - a stinking rose by any other name). */ + + /* CoInterpreter>>#interpretMethodFromMachineCode */ +static sqInt +interpretMethodFromMachineCode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt arg; + sqInt argIndex; + sqInt fmt; + sqInt header; + usqInt i; + sqInt index; + int inInterpreter; + sqInt maxNumArgs; + sqInt methodHeader; + sqInt nArgs; + usqInt numArgs; + usqInt numSlots; + usqInt numSlotsUsqInt; + usqInt numTemps; + sqInt object; + sqInt objectClass; + sqInt rcvr; + char *savedFramePointer; + char *savedStackPointer; + sqInt selectorOrProperties; + sqInt switched; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt types; + sqInt typesArraySize; + sqInt wordSize; + + assertCStackWellAligned(); + assert(validInstructionPointerinFrame(GIV(instructionPointer), GIV(framePointer))); + if (primitiveFunctionPointer) { + if (primitiveFunctionPointer == primitiveInvokeObjectAsMethod) { + assert(!(isOopCompiledMethod(GIV(newMethod)))); + } + else { + assert((isOopCompiledMethod(GIV(newMethod))) + && ((primitiveIndexOf(GIV(newMethod))) != 0)); + } + + /* Invoke an interpreter primitive (because the method is to be interpreted or has not yet been + compiled). This is very similar to invoking an interpreter primitive from a compiled primitive + (see e.g. SimpleStackBasedCogit>>compileInterpreterPrimitive:). Cut back the stack pointer + (done above) to skip the return address and invoke the function. On return if it has succeeded + simply continue otherwise restore the stackPointer, collect the pc and interpret. Note that + frame building primitives such as primitiveClosureValue, primitiveEvaluateMethod et al will not + return but will instead jump into either machine code or longjmp back to the interpreter. + Assign stackPage headFP so we can tell if the primitive built a frame. We can't simply save + the framePointer since e.g. assignment to contexts (via primitiveInstVarAt:put:) can change the + framePointer. But context assignments will change both the framePointer and stackPage headFP. */ + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + if ((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex) { + externalQuickPrimitiveResponse(); + GIV(primFailCode) = 0; + } + else { + /* begin slowPrimitiveResponse */ + assert(!(isOopForwarded(stackValue(GIV(argumentCount))))); + assert((GIV(remapBufferCount)) == 0); + nArgs = GIV(argumentCount); + savedStackPointer = GIV(stackPointer); + savedFramePointer = GIV(framePointer); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if (/* recordPrimTraceForMethod: */ + (recordPrimTrace()) + && ((primTracePluginName + ? methodHasPrimitiveInPrimTracePlugin(GIV(newMethod)) + : 1))) { + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = GIV(newMethod); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + dispatchFunctionPointer(primitiveFunctionPointer); + assert(maybeLeakCheckExternalPrimCall(GIV(newMethod))); + + /* begin maybeRetryPrimitiveOnFailure */ + if (GIV(primFailCode)) { + retryPrimitiveOnFailure(); + } + if ((!GIV(primFailCode)) + && ((GIV(framePointer) == savedFramePointer) + && (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))))) { + if (GIV(stackPointer) != (savedStackPointer + (nArgs * BytesPerWord))) { + /* This is necessary but insufficient; the result may still have been written to the stack. + At least we'll know something is wrong. */ + failUnbalancedPrimitive(); + GIV(stackPointer) = savedStackPointer; + } + } + + /* Don't fail if primitive has done something radical, e.g. perform: + If we are profiling, take accurate primitive measures */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + + /* successful */ + !GIV(primFailCode); + } + if (!GIV(primFailCode)) { + returntoExecutive(popStack(), 0); + } + } + else { + assert((isOopCompiledMethod(GIV(newMethod))) + && (((primitiveIndexOf(GIV(newMethod))) == 0) + || (((functionPointerForinClass(primitiveIndexOf(GIV(newMethod)), nilObject())) == 0) + || (isNullExternalPrimitiveCall(GIV(newMethod)))))); + } + + /* if not primitive, or primitive failed, activate the method and reenter the interpreter */ + + /* begin activateNewMethod */ + /* begin collectArgumentsTypeFor:fromStackAt:count: */ + /* begin tempVarTypesArraysOf: */ + /* begin additionalMethodStateOf:at: */ + /* begin additionalMethodStateOf: */ + selectorOrProperties = penultimateLiteralOf(GIV(newMethod)); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l3; + } + wordSize = BytesPerWord; + maxNumArgs = (((lengthOf(tempVarTypesArrays)) < GIV(argumentCount)) ? (lengthOf(tempVarTypesArrays)) : GIV(argumentCount)); + for (argIndex = 0; argIndex < maxNumArgs; argIndex += 1) { + /* begin followObjField:ofObject: */ + types = longAt((void *)((tempVarTypesArrays + BaseHeaderSize) + ((((usqInt)(argIndex) << (shiftForWord())))))); + assert(isNonImmediate(types)); + if ((!((longAt((void *)(types))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + types = fixFollowedFieldofObjectwithInitialValue(argIndex, tempVarTypesArrays, types); + } + arg = longAt((void *)(GIV(stackPointer) + (argIndex * wordSize))); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (types) + && (isInstanceOfClassArray(types)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = arg & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(arg)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(types + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(types + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(types - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, types, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, types, objectClass)); + assert(isNonImmediate(types)); + if (oopisGreaterThanOrEqualTo(types, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(types + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(types); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2:; + } + /* end collectArgumentsTypeFor:fromStackAt:count: */ +l3: + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeader)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(!(isOopForwarded(rcvr))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeader); + } + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc). */ + switched = 1; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(canContextSwitchIfActivatingheader(GIV(newMethod), methodHeader)); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + ceInvokeInterpret(); + + /* NOTREACHED */ + return null; +} + + +/* This version exists for asserts */ + + /* CoInterpreter>>#isCogMethodReferenceNoAssert: */ +static NoDbgRegParms int +isCogMethodReferenceNoAssert(sqInt methodHeader) +{ + return (!(methodHeader & (smallIntegerTag()))); +} + + /* CoInterpreter>>#isCogMethodReference: */ +int +isCogMethodReference(sqInt methodHeader) +{ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + return (!(methodHeader & (smallIntegerTag()))); +} + + /* CoInterpreter>>#isExternalMethodInPlugin: */ +static NoDbgRegParms sqInt +isExternalMethodInPlugin(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt headerSqInt; + sqInt literal; + usqInt numSlots; + sqInt primIdx; + + literal = 0; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + headerSqInt = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* primitiveIndexOfMethod:header: */ + if (((header & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((header >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIdx = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIdx = 0; + } + return (primIdx == PrimNumberExternalCall) + && ((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) > 0) + && ((isArray((literal = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord()))))))) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(literal)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(literal + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(literal - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4) + && ((isBytes(longAt((void *)((literal + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralModuleNameIndex) << (shiftForWord())))))))) + && (!((numBytesOf(longAt((void *)((literal + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralModuleNameIndex) << (shiftForWord())))))))) == 0)))))); +} + + +/* Answer if the argument is a full block method or not. Full block methods + (as introduced by the SistaV1BytecodeSet) are used to implement block + closures and have a reference to their enclosing block or method as their + last literal. Compiled methods have a class association as their last + literal. */ + + /* CoInterpreter>>#isFullBlockMethod: */ +sqInt +isFullBlockMethod(sqInt aMethodObj) +{ + sqInt oop; + + oop = ultimateLiteralOf(aMethodObj); + + /* begin isOopCompiledMethod: */ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())); +} + + /* CoInterpreter>>#isMachineCodeFrameForCogMethodFlaggedForBecome: */ +static NoDbgRegParms sqInt +isMachineCodeFrameForCogMethodFlaggedForBecome(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && (isCMMethodFlaggedForBecome(((CogBlockMethod *) (mframeHomeMethod(theFP))))); +} + + /* CoInterpreter>>#isMachineCodeFrameForCogMethodWithMachineCodePrim: */ +static NoDbgRegParms sqInt +isMachineCodeFrameForCogMethodWithMachineCodePrim(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && (cogMethodHasMachineCodePrim(mframeHomeMethod(theFP))); +} + + /* CoInterpreter>>#isMachineCodeFrameForCogMethodWithTooManyLiterals: */ +static NoDbgRegParms sqInt +isMachineCodeFrameForCogMethodWithTooManyLiterals(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && (!(((/* begin literalCountOfMethodHeader: */ + assert(((((((mframeHomeMethod(theFP))->methodHeader))) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + (((((mframeHomeMethod(theFP))->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile)); +} + + /* CoInterpreter>>#isMachineCodeFrameForExternalPrimitiveMethod: */ +static NoDbgRegParms sqInt +isMachineCodeFrameForExternalPrimitiveMethod(char *theFP) +{ + CogMethod *homeMethod; + + homeMethod = ((CogMethod *) 0); + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && ((cogMethodHasMachineCodePrim((homeMethod = mframeHomeMethod(theFP)))) + && (isExternalMethodInPlugin((homeMethod->methodObject)))); +} + + /* CoInterpreter>>#isMachineCodeFrameForRealNonProfilingPrimitive: */ +static NoDbgRegParms sqInt +isMachineCodeFrameForRealNonProfilingPrimitive(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt firstLiteral; + sqInt functionName; + sqInt header; + CogMethod *homeMethod; + sqInt methodField; + sqInt methodHeader; + sqInt methodObj; + usqInt numSlots; + void (*pf)(void); + sqInt primitiveIndex; + + if (!((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()))) { + return 0; + } + + /* begin mframeHomeMethod: */ + methodField = longAt(theFP + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + homeMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + goto l1; + } + homeMethod = ((CogMethod *) (methodField & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + methodObj = (homeMethod->methodObject); + + /* begin isRealNonProfilingPrimitiveMethod: */ + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin isRealNonProfilingPrimitiveMethod:primIndex: */ + if (!(((primitiveIndex >= 1) && (primitiveIndex <= MaxPrimitiveIndex)))) { + return 0; + } + + /* Filter-out the profiling primitives themselves. */ + if (primitiveIndex == PrimNumberExternalCall) { + firstLiteral = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(firstLiteral & (tagMask())))) + && (((byteAt((void *)(firstLiteral + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(firstLiteral)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(firstLiteral + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(firstLiteral - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4))) { + return 1; + } + functionName = longAt((void *)((firstLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFunctionNameIndex) << (shiftForWord())))))); + return ((numBytesOf(functionName)) < (21)) + || (0 != (strncmp("primitiveProfile", ((char *) (firstIndexableField(functionName))), 16))); + } + + /* Answer true if there's a real primitive here, excepting the ValueNoContextSwitch primitives, + because a sample can't be taken in them, as doing so could cause a context switch. */ + pf = primitiveTable[primitiveIndex]; + return (pf != (((void *)0))) + && ((pf != (((void (*)(void)) primitiveFail))) + && ((pf != primitiveClosureValueNoContextSwitch) + && (pf != primitiveFullClosureValueNoContextSwitch))); +} + + /* CoInterpreter>>#isMachineCodeFrame: */ +static NoDbgRegParms int +isMachineCodeFrame(char *theFP) +{ + return (((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()); +} + + +/* Answer if the methodObj's primitive is one that should be profiled. + Filter-out the profiling primitives themselves. */ + + /* CoInterpreter>>#isRealNonProfilingPrimitiveMethod: */ +static NoDbgRegParms sqInt +isRealNonProfilingPrimitiveMethod(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt firstLiteral; + sqInt functionName; + sqInt header; + sqInt methodHeader; + usqInt numSlots; + void (*pf)(void); + sqInt primitiveIndex; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin isRealNonProfilingPrimitiveMethod:primIndex: */ + if (!(((primitiveIndex >= 1) && (primitiveIndex <= MaxPrimitiveIndex)))) { + return 0; + } + + /* Filter-out the profiling primitives themselves. */ + if (primitiveIndex == PrimNumberExternalCall) { + firstLiteral = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(firstLiteral & (tagMask())))) + && (((byteAt((void *)(firstLiteral + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(firstLiteral)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(firstLiteral + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(firstLiteral - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4))) { + return 1; + } + functionName = longAt((void *)((firstLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFunctionNameIndex) << (shiftForWord())))))); + return ((numBytesOf(functionName)) < (21)) + || (0 != (strncmp("primitiveProfile", ((char *) (firstIndexableField(functionName))), 16))); + } + + /* Answer true if there's a real primitive here, excepting the ValueNoContextSwitch primitives, + because a sample can't be taken in them, as doing so could cause a context switch. */ + pf = primitiveTable[primitiveIndex]; + return (pf != (((void *)0))) + && ((pf != (((void (*)(void)) primitiveFail))) + && ((pf != primitiveClosureValueNoContextSwitch) + && (pf != primitiveFullClosureValueNoContextSwitch))); +} + + +/* Answer if the methodObj's primitive is one that should be profiled. + In particular answer false for the no-context-switch value primitives and + the profiling primitives themselves. */ + + /* CoInterpreter>>#isRealNonProfilingPrimitiveMethod:primIndex: */ +static NoDbgRegParms sqInt +isRealNonProfilingPrimitiveMethodprimIndex(sqInt methodObj, sqInt primitiveIndex) +{ + sqInt firstLiteral; + sqInt functionName; + usqInt numSlots; + void (*pf)(void); + + if (!(((primitiveIndex >= 1) && (primitiveIndex <= MaxPrimitiveIndex)))) { + return 0; + } + + /* Filter-out the profiling primitives themselves. */ + if (primitiveIndex == PrimNumberExternalCall) { + firstLiteral = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(firstLiteral & (tagMask())))) + && (((byteAt((void *)(firstLiteral + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(firstLiteral)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(firstLiteral + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(firstLiteral - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4))) { + return 1; + } + functionName = longAt((void *)((firstLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFunctionNameIndex) << (shiftForWord())))))); + return ((numBytesOf(functionName)) < (21)) + || (0 != (strncmp("primitiveProfile", ((char *) (firstIndexableField(functionName))), 16))); + } + + /* Answer true if there's a real primitive here, excepting the ValueNoContextSwitch primitives, + because a sample can't be taken in them, as doing so could cause a context switch. */ + pf = primitiveTable[primitiveIndex]; + return (pf != (((void *)0))) + && ((pf != (((void (*)(void)) primitiveFail))) + && ((pf != primitiveClosureValueNoContextSwitch) + && (pf != primitiveFullClosureValueNoContextSwitch))); +} + + +/* See if the argument is married to a live frame or not. i.e. see if there + is a matching + frame whose frameContext field is aOnceMarriedContext, or a forwarder to + it. If aOnceMarriedContext is not married to a live frame, turn it into a + bereaved single context. + This version is for use during scavenging when stack references may be + forwarded. Following what appear to be references to forwarded objects on + the stack is dangerous; + an instruction ponter may be correctly aligned and may point to bytes that + just happen + to look like a forwarder. So it is only safe to follow fields that we know + are frameContext + fields; hence the stack page is walked to check that aOnceMarriedContext + is pointing to + a live frame. This only has to happen during scavenging because after a + become: all + frameContext fields have been followed and so there is no need to follow + forwarders. */ +/* Override to check gcMode is correct. See comment in superclass, and in + superclass's isWidowedContext:. + */ + + /* CoInterpreter>>#isWidowedContextDuringGC: */ +static NoDbgRegParms sqInt +isWidowedContextDuringGC(sqInt aOnceMarriedContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anInteger; + char *maybeFrame; + sqInt maybeFrameCtxt; + sqInt referent; + sqInt senderOop; + char *shouldBeFrameCallerField; + StackPage *thePage; + + assert((GIV(gcMode) == GCModeNewSpace) + || ((GIV(gcMode) == GCModeFull) + || (GIV(gcMode) == GCModeImageSegment))); + assert((isContext(aOnceMarriedContext)) + && (isMarriedOrWidowedContext(aOnceMarriedContext))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + maybeFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(maybeFrame), GIV(pages)); + if (!((isFree(thePage)) + || (maybeFrame < ((thePage->headFP))))) { + /* The frame pointer is within the bounds of a live page. + Now check if it matches a frame. */ + anInteger = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + + /* begin withoutSmallIntegerTags: */ + assert((((anInteger) & 7) == 1)); + shouldBeFrameCallerField = ((char *)(anInteger - (smallIntegerTag()))); + if (((((char *)(longAt(maybeFrame + FoxSavedFP)))) == shouldBeFrameCallerField) + && (/* frameHasContext: */ + ((((usqInt)(longAt(maybeFrame + FoxMethod)))) < (startOfMemory()) + ? ((longAt(maybeFrame + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((maybeFrame + FoxIFrameFlags) + 2)) != 0))) { + maybeFrameCtxt = longAt(maybeFrame + FoxThisContext); + + /* On Spur we need to follow the context to check for a match, but since the VM is + only speculating about maybeFrame being a frame, and only speculating about + maybeContext being a context, we need to be sure before we can safely follow. */ + if ((isFrameonPage(maybeFrame, thePage)) + && ((!((longAt((void *)(maybeFrameCtxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(maybeFrameCtxt)); + referent = longAt((void *)((maybeFrameCtxt + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + maybeFrameCtxt = referent; + + /* begin setFrameContext:to: */ + longAtput(maybeFrame + FoxThisContext,maybeFrameCtxt); + } + if (maybeFrameCtxt == aOnceMarriedContext) { + return 0; + } + } + } + + /* It is out of range or doesn't match the frame's context. + It is widowed. Time to wear black. */ + + /* begin markContextAsDead: */ + assert(isContext(aOnceMarriedContext)); + assert((isNonImmediate(aOnceMarriedContext)) + && (!(isForwarded(aOnceMarriedContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, aOnceMarriedContext, GIV(nilObj))); + longAtput((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aOnceMarriedContext)) + && (!(isForwarded(aOnceMarriedContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, aOnceMarriedContext, GIV(nilObj))); + longAtput((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + return 1; +} + + +/* Answer if the argument is married to a live frame or not. + If it is not, turn it into a bereaved single context. This version is safe + for use + only when no frameContext fields may be forwarded (as maybe the case + when scavenging). Post become: all frameContext fields are followed, and + hence normally no following of frameContext fields is necessary. But + during a scavenge one must use isWidowedContextDuringGC:. */ +/* Override to check gcMode is correct. See comment in superclass. */ + + /* CoInterpreter>>#isWidowedContext: */ +static NoDbgRegParms sqInt +isWidowedContext(sqInt aOnceMarriedContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anInteger; + sqInt senderOop; + char *shouldBeFrameCallerField; + char *theFrame; + StackPage *thePage; + sqInt widowed; + + assert(GIV(gcMode) != GCModeNewSpace); + + /* begin isWidowedContextNoConvert: */ + assert((isContext(aOnceMarriedContext)) + && (isMarriedOrWidowedContext(aOnceMarriedContext))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + if (!((isFree(thePage)) + || (theFrame < ((thePage->headFP))))) { + /* The frame pointer is within the bounds of a live page. + Now check if it matches a frame. */ + anInteger = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + + /* begin withoutSmallIntegerTags: */ + assert((((anInteger) & 7) == 1)); + shouldBeFrameCallerField = ((char *)(anInteger - (smallIntegerTag()))); + if (((((char *)(longAt(theFrame + FoxSavedFP)))) == shouldBeFrameCallerField) + && (/* frameHasContext: */ + ((((usqInt)(longAt(theFrame + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFrame + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFrame + FoxIFrameFlags) + 2)) != 0))) { + assert(!(((isFrameonPage(theFrame, thePage)) + && (isForwarded(frameContext(theFrame)))))); + if ((longAt(theFrame + FoxThisContext)) == aOnceMarriedContext) { + widowed = 0; + goto l1; + } + } + } + widowed = 1; + /* end isWidowedContextNoConvert: */ +l1: + if (!widowed) { + return 0; + } + + /* begin markContextAsDead: */ + assert(isContext(aOnceMarriedContext)); + assert((isNonImmediate(aOnceMarriedContext)) + && (!(isForwarded(aOnceMarriedContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, aOnceMarriedContext, GIV(nilObj))); + longAtput((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aOnceMarriedContext)) + && (!(isForwarded(aOnceMarriedContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, aOnceMarriedContext, GIV(nilObj))); + longAtput((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + return 1; +} + + +/* If the method is assigned, any machine code pc must be mapped to a + bytecode one + before the method is changed. */ + + /* CoInterpreter>>#is:methodAssignmentToContextWithMachineCodePC: */ +static NoDbgRegParms sqInt +ismethodAssignmentToContextWithMachineCodePC(sqInt fieldIndex, sqInt anOop) +{ + sqInt thePC; + + thePC = 0; + return (fieldIndex == MethodIndex) + && (((((int) ((thePC = longAt((void *)((anOop + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))))) < 0) + && ((((thePC) & 7) == 1))); +} + + /* CoInterpreter>>#longRunningPrimitiveStopUsecsAddress */ +#if LRPCheck +usqInt +longRunningPrimitiveStopUsecsAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(longRunningPrimitiveStopUsecs)))); +} +#endif /* LRPCheck */ + + +/* Lookup selector in rcvr, without doing MNU processing, and answer either a + method or an error code if the message was not understood. Used to + populate closed PICs. */ +/* self printFrame: stackPage headFP WithSP: stackPage headSP */ +/* self printStringOf: selector */ + + /* CoInterpreter>>#lookupMNU:receiver: */ +sqInt +lookupMNUreceiver(sqInt selector, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTag; + sqInt erridx; + sqInt inCache; + sqInt tagBits; + + classTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + inCache = lookupInMethodCacheSelclassTag(selector, classTag); + if (!inCache) { + GIV(messageSelector) = selector; + erridx = lookupMNUInClass(classForClassTag(classTag)); + if (erridx) { + assert(erridx <= (maxLookupNoMNUErrorCode())); + return erridx; + } + } + return GIV(newMethod); +} + + +/* Lookup selector in rcvr, without doing MNU processing, and answer either a + method or an error code if the message was not understood. Used to + populate closed PICs. */ +/* self printFrame: stackPage headFP WithSP: stackPage headSP */ +/* self printStringOf: selector */ + + /* CoInterpreter>>#lookupOrdinary:receiver: */ +sqInt +lookupOrdinaryreceiver(sqInt selector, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTag; + sqInt erridx; + sqInt tagBits; + + classTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + if (!(lookupInMethodCacheSelclassTag(selector, classTag))) { + GIV(messageSelector) = selector; + if ((erridx = lookupOrdinaryNoMNUEtcInClass(classForClassTag(classTag)))) { + assert(erridx <= (maxLookupNoMNUErrorCode())); + return erridx; + } + } + return GIV(newMethod); +} + + +/* */ +/* Marry aContext with the base frame of a new stack page. Build the base + frame to reflect the context's state. Answer the new page. Override to + hold the caller context in a different place, In the StackInterpreter we + use the caller saved ip, but in the Cog VM caller saved ip is the + ceBaseReturn: trampoline. Simply hold the caller context in the first word + of the stack. */ + + /* CoInterpreter>>#makeBaseFrameFor: */ +static NoDbgRegParms StackPage * +makeBaseFrameFor(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cogMethod; + CogMethod *cogMethod1; + sqInt header; + sqInt headerSqInt; + sqInt i; + sqInt maybeClosure; + usqInt numArgs; + usqInt numSlots; + sqInt oop; + StackPage *page; + char *pointer; + sqInt rawHeader; + sqInt rcvr; + sqInt stackPtrIndex; + sqInt theIP; + sqInt theMethod; + sqInt valuePointer; + sqInt yetToCompact; + + + /* theIP must be typed as signed because it is assigned ceCannotResumePC and so maybe implicitly typed as unsigned. */ + assert(isContext(aContext)); + assert(isSingleContext(aContext)); + assert(goodContextSize(aContext)); + theIP = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + theIP = ((((theIP) & 7) == 1) + ? (theIP >> 3) + : HasBeenReturnedFromMCPC); + + /* begin followObjField:ofObject: */ + theMethod = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + assert(isNonImmediate(theMethod)); + if ((!((longAt((void *)(theMethod))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + theMethod = fixFollowedFieldofObjectwithInitialValue(MethodIndex, aContext, theMethod); + } + + /* begin newStackPage */ + page = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((page->baseFP))) { + goto l1; + } + divorceFramesIn(page); + /* end newStackPage */ +l1: + + /* first word on stack is caller context of base frame */ + longAtput((pointer = (page->baseAddress)),followObjFieldofObject(SenderIndex, aContext)); + + /* second word is the context itself; needed for cannotReturn processing; see ceBaseReturn:. */ + longAtput((pointer -= BytesPerWord),aContext); + + /* begin followField:ofObject: */ + rcvr = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = fixFollowedFieldofObjectwithInitialValue(ReceiverIndex, aContext, rcvr); + } + + /* If the frame is a closure activation then the closure should be on the stack in + the pushed receiver position (closures receive the value[:value:] messages). + Otherwise it should be the receiver proper. */ + maybeClosure = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord())))))); + if (maybeClosure != GIV(nilObj)) { + if ((!((longAt((void *)(maybeClosure))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + maybeClosure = fixFollowedFieldofObjectwithInitialValue(ClosureIndex, aContext, maybeClosure); + } + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + longAtput((pointer -= BytesPerWord),maybeClosure); + } + else { + /* begin methodHeaderOf: */ + assert(isCompiledMethod(theMethod)); + headerSqInt = longAt((void *)((theMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + numArgs = (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; + + /* If this is a synthetic context its IP could be pointing at the CallPrimitive opcode. If so, skip it. */ + if ((((header & AlternateHeaderHasPrimFlag) != 0)) + && (theIP == (1 + ((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop)))) { + theIP += 3 /* sizeOfCallPrimitiveBytecode: */; + } + longAtput((pointer -= BytesPerWord),rcvr); + } + + /* Put the arguments on the stack */ + for (i = 1; i <= numArgs; i += 1) { + longAtput((pointer -= BytesPerWord),longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))))); + } + + /* saved caller ip is base return trampoline */ + longAtput((pointer -= BytesPerWord),ceBaseFrameReturnPC()); + + /* base frame's saved fp is null */ + longAtput((pointer -= BytesPerWord),0); + + /* N.B. Don't set the baseFP, which marks the page as in use, until after + ensureMethodIsCogged: and/or instructionPointer:forContext:frame:. These + can cause a compiled code compaction which, if marked as in use, will + examine this partially initialized page and crash. */ + (page->headFP = pointer); + + /* Create either a machine code frame or an interpreter frame based on the pc. If the pc is -ve + it is a machine code pc and so we produce a machine code frame. If +ve an interpreter frame. + N.B. Do *not* change this to try and map from a bytecode pc to a machine code frame under + any circumstances. See ensureContextIsExecutionSafeAfterAssignToStackPointer: */ + if (theIP < 0) { + /* Since we would have to generate a machine-code method to be able to map + the native pc anyway we should create a native method and native frame. */ + + /* begin ensureMethodIsCogged:maybeClosure: */ + rawHeader = longAt((void *)((theMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) rawHeader); + goto l2; + } + yetToCompact = 1; + while (1) { + if ((maybeClosure != GIV(nilObj)) + && (!((/* begin isVanillaBlockClosure: */ + assert((isPointers(maybeClosure)) + && ((numSlotsOf(maybeClosure)) >= ClosureFirstCopiedValueIndex)), + ((((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1))))) { + cogMethod1 = cogFullBlockMethodnumCopied( + theMethod, + (/* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(maybeClosure)))), + ((/* begin numSlotsOf: */ + assert((classIndexOf(maybeClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(maybeClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(maybeClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex)); + } + else { + cogMethod1 = cogselector(theMethod, GIV(nilObj)); + } + if (!((cogMethod1 == null) + && (GIV(cogCompiledCodeCompactionCalledFor) + && (yetToCompact)))) break; + yetToCompact = 0; + commenceCogCompiledCodeCompaction(); + } + if (!(asserta(cogMethod1 != null))) { + error("could not compile method that should have been compiled"); + } + cogMethod = cogMethod1; + /* end ensureMethodIsCogged:maybeClosure: */ +l2: + theMethod = ((sqInt)cogMethod); + if (maybeClosure != GIV(nilObj)) { + /* begin isVanillaBlockClosure: */ + assert((isPointers(maybeClosure)) + && ((numSlotsOf(maybeClosure)) >= ClosureFirstCopiedValueIndex)); + if (((((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* If the pc is the special HasBeenReturnedFromMCPC pc set the pc + appropriately so that the frame stays in the cannotReturn: state. */ + if (theIP == HasBeenReturnedFromMCPC) { + theMethod = ((sqInt)(findMethodForStartBcpcinHomeMethod((((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))))) >> 3)) - 1, ((CogMethod *) theMethod)))); + if (!theMethod) { + error("cannot find machine code block matching closure's startpc"); + } + theIP = ceCannotResumePC(); + } + else { + assert((((theIP) >> 16)) < -1); + theMethod -= (((theIP) >> 16)) * (blockAlignment()); + theIP = theMethod - (((short)theIP)); + } + } + else { + assert((((theIP) >> 16)) >= -1); + + /* If the pc is the special HasBeenReturnedFromMCPC pc set the pc + appropriately so that the frame stays in the cannotReturn: state. */ + theIP = (theIP == HasBeenReturnedFromMCPC + ? ceCannotResumePC() + : (((sqInt)theMethod)) - theIP); + } + longAtput((pointer -= BytesPerWord),(theMethod + MFMethodFlagHasContextFlag) + MFMethodFlagIsBlockFlag); + } + else { + assert((((theIP) >> 16)) >= -1); + + /* If the pc is the special HasBeenReturnedFromMCPC pc set the pc + appropriately so that the frame stays in the cannotReturn: state. */ + theIP = (theIP == HasBeenReturnedFromMCPC + ? ceCannotResumePC() + : (((sqInt)theMethod)) - theIP); + longAtput((pointer -= BytesPerWord),theMethod + MFMethodFlagHasContextFlag); + } + longAtput((pointer -= BytesPerWord),aContext); + } + else { + longAtput((pointer -= BytesPerWord),theMethod); + longAtput((pointer -= BytesPerWord),aContext); + longAtput((pointer -= BytesPerWord),/* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8)))) + (1ULL << ((BytesPerWord * 8) - 16))) + ((maybeClosure != GIV(nilObj) + ? 1ULL << ((BytesPerWord * 8) - 24) + : 0)) + : ((1 + ((numArgs << 8))) + (0x10000)) + ((maybeClosure != GIV(nilObj) + ? 0x1000000 + : 0)))); + longAtput((pointer -= BytesPerWord),0); + + /* begin iframeInstructionPointerForIndex:method: */ + assert(((theIP >= (((LiteralStart + (literalCountOf(theMethod))) * BytesPerOop) + 1)) && (theIP <= (lengthOf(theMethod))))); + theIP = ((theMethod + theIP) + BaseHeaderSize) - 2; + } + (page->baseFP = (page->headFP)); + assert(frameHasContext((page->baseFP))); + assert((frameNumArgs((page->baseFP))) == numArgs); + longAtput((pointer -= BytesPerWord),rcvr); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + stackPtrIndex = (oop >> 3); + assert((ReceiverIndex + stackPtrIndex) < (lengthOf(aContext))); + for (i = (numArgs + 1); i <= stackPtrIndex; i += 1) { + longAtput((pointer -= BytesPerWord),longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))))); + + /* nil the slot in the context so that it doesn't inadvertently hang onto some collectable object. + Thanks to Ryan Macnak for identifying this bug */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aContext)) + && (!(isForwarded(aContext)))); + assert(validStorePointerUncheckedArgs(ReceiverIndex + i, aContext, GIV(nilObj))); + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))),GIV(nilObj)); + } + + /* top of stack is the instruction pointer */ + longAtput((pointer -= BytesPerWord),theIP); + (page->headSP = pointer); + assert(contexthasValidInversePCMappingOfin(aContext, theIP, (page->baseFP))); + + /* Mark context as married by setting its sender to the frame pointer plus SmallInteger + tags and the InstructionPointer to the saved fp (which ensures correct alignment + w.r.t. the frame when we check for validity) plus SmallInteger tags. */ + /* begin withSmallIntegerTags: */ + assert(((oopForPointer((page->baseFP))) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer((page->baseFP))) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aContext)) + && (!(isForwarded(aContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, aContext, valuePointer)); + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),valuePointer); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(0)) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(0)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aContext)) + && (!(isForwarded(aContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, aContext, valuePointer)); + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointer); + assert(((((fetchPointerofObject(SenderIndex, aContext))) & 7) == 1)); + assert((frameOfMarriedContext(aContext)) == ((page->baseFP))); + assert(validStackPageBaseFrame(page)); + return page; +} + + /* CoInterpreter>>#mapStackPages */ +static NeverInline void +mapStackPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + char *frameRcvrOffset; + sqInt i; + sqInt numLivePages; + sqInt oop; + char *theFP; + sqInt theIP; + char *theIPPtr; + StackPage *thePage; + char *theSP; + + callerFP = ((char *) 0); + theIP = 0; + numLivePages = 0; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((thePage->baseFP)) { + assert(ifCurrentStackPageHasValidHeadPointers(thePage)); + numLivePages += 1; + theSP = (thePage->headSP); + theFP = (thePage->headFP); + + /* Skip the instruction pointer on top of stack of inactive pages. */ + if (thePage == GIV(stackPage)) { + theIPPtr = (((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + || ((((usqInt)(longAt(theFP + FoxIFSavedIP)))) == 0) + ? 0 + : theFP + FoxIFSavedIP); + } + else { + theIPPtr = theSP; + theSP += BytesPerWord; + } + while (1) { + assert(addressIsInPage(thePage, theFP)); + assert(addressIsInPage(thePage, theSP)); + assert((theIPPtr == 0) + || (addressIsInPage(thePage, theIPPtr))); + frameRcvrOffset = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + while (theSP <= frameRcvrOffset) { + oop = longAt(theSP); + if (/* shouldRemapOop: */ + ((!(oop & (tagMask())))) + && (shouldRemapObj(oop))) { + longAtput(theSP,remapObj(oop)); + } + theSP += BytesPerWord; + } + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + if (shouldRemapObj(longAt(theFP + FoxThisContext))) { + longAtput(theFP + FoxThisContext,remapObj(longAt(theFP + FoxThisContext))); + } + + /* With SqueakV3 objectMemory or SpurPlanningCompactor can't assert since object body is yet to move. */ + if (!((GIV(gcPhaseInProgress) == SlidingCompactionInProgress) + || (GIV(gcPhaseInProgress) == ScavengeInProgress))) { + assert((isMarriedOrWidowedContext(frameContext(theFP))) + && ((frameOfMarriedContext(frameContext(theFP))) == theFP)); + } + } + if (!((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()))) { + if (shouldRemapObj(longAt(theFP + FoxMethod))) { + if (theIPPtr) { + theIP = longAt(theIPPtr); + if (theIP == (ceReturnToInterpreterPC())) { + assert((iframeSavedIP(theFP)) > (iframeMethod(theFP))); + theIPPtr = theFP + FoxIFSavedIP; + theIP = longAt(theIPPtr); + } + else { + assert(theIP > (iframeMethod(theFP))); + } + theIP -= longAt(theFP + FoxMethod); + } + longAtput(theFP + FoxMethod,remapObj(longAt(theFP + FoxMethod))); + if (theIPPtr) { + longAtput(theIPPtr,theIP + (longAt(theFP + FoxMethod))); + } + } + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theSP = ((theIPPtr = theFP + FoxCallerSavedIP)) + BytesPerWord; + theFP = callerFP; + } + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if (/* shouldRemapOop: */ + ((!(oop & (tagMask())))) + && (shouldRemapObj(oop))) { + longAtput(theSP,remapObj(oop)); + } + theSP += BytesPerWord; + } + } + } + + /* begin recordLivePagesOnMapping: */ + GIV(statNumMaps) += 1; + GIV(statPageCountWhenMappingSum) += numLivePages; + GIV(statMaxPageCountWhenMapping) = ((GIV(statMaxPageCountWhenMapping) < numLivePages) ? numLivePages : GIV(statMaxPageCountWhenMapping)); +} + + +/* If instructionPointer is referring to machine code, as it will be if a + primitive is in progress + (see isCodeCompactingPrimitiveIndex:) it may refer to a method, and if so + that method + must be retained. */ + + /* CoInterpreter>>#markActiveMethodsAndReferents */ +void +markActiveMethodsAndReferents(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + CogMethod *primCogMethod; + StackPage *thePage; + + if (GIV(instructionPointer)) { + if ((primCogMethod = cogMethodContaining(GIV(instructionPointer)))) { + markMethodAndReferents(((CogBlockMethod *) primCogMethod)); + } + } + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + markCogMethodsAndReferentsOnPage(thePage); + } + } +} + + /* CoInterpreter>>#markAndTraceStackPage: */ +static NoDbgRegParms void +markAndTraceStackPage(StackPage *thePage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogBlockMethod *aCogMethod; + char *callerFP; + char *frameRcvrOffset; + CogMethod *homeMethod; + sqInt oop; + sqInt referent; + char *theFP; + char *theSP; + + callerFP = ((char *) 0); + assert(!(isFree(thePage))); + assert(ifCurrentStackPageHasValidHeadPointers(thePage)); + assert(((thePage->trace)) != StackPageTraced); + (thePage->trace = StackPageTraced); + theSP = (thePage->headSP); + theFP = (thePage->headFP); + + /* Skip the instruction pointer on top of stack of inactive pages. */ + if (!(thePage == GIV(stackPage))) { + theSP += BytesPerWord; + } + while (1) { + frameRcvrOffset = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + while (theSP <= frameRcvrOffset) { + oop = longAt(theSP); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + longAtput(theSP,oop); + } + if (!(((oop & (tagMask())) != 0))) { + markAndTrace(oop); + } + theSP += BytesPerWord; + } + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + markAndTrace(frameContext(theFP)); + } + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* begin mframeCogMethod: */ + aCogMethod = ((CogBlockMethod *) ((longAt(theFP + FoxMethod)) & MFMethodMask)); + homeMethod = /* asCogHomeMethod: */ + (((aCogMethod->cmType)) >= CMMethod + ? ((CogMethod *) aCogMethod) + : /* cmHomeMethod */ + (((aCogMethod->cmType)) >= CMMethod + ? ((CogMethod *) aCogMethod) + : (assert(CMBlock == (aCogMethod->cmType)), + ((CogMethod *) ((((usqInt)aCogMethod)) - ((aCogMethod->homeOffset))))))); + markAndTrace((homeMethod->methodObject)); + } + else { + markAndTrace(iframeMethod(theFP)); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + theFP = callerFP; + } + + /* caller ip is ceBaseReturnPC */ + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + longAtput(theSP,oop); + } + if (!(((oop & (tagMask())) != 0))) { + markAndTrace(oop); + } + theSP += BytesPerWord; + } +} + + /* CoInterpreter>>#markCogMethodsAndReferentsOnPage: */ +static NoDbgRegParms void +markCogMethodsAndReferentsOnPage(StackPage *thePage) +{ + char *callerFP; + char *theFP; + + callerFP = ((char *) 0); + assert(!(isFree(thePage))); + assert(ifCurrentStackPageHasValidHeadPointers(thePage)); + theFP = (thePage->headFP); + while (1) { + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + markMethodAndReferents(mframeCogMethod(theFP)); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theFP = callerFP; + } +} + + +/* Marry an unmarried frame. This means creating a spouse context + initialized with a subset of the frame's state that references the frame. + For the default closure implementation we do not need to copy temps. + + This method is important enough for performance to be worth streamlining. + + Override to set the ``has context'' flag appropriately for both machine + code and + interpreter frames and to streamline the machine code/interpreter + differences. + */ + + /* CoInterpreter>>#marryFrame:SP: */ +static NoDbgRegParms usqInt +marryFrameSP(char *theFP, char *theSP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt closureOrNil; + CogMethod *cogMethod; + sqInt header; + sqInt headerSqInt; + sqInt i; + sqInt methodFieldOrObj; + usqInt newObj; + usqInt numArgs; + usqInt numBytes; + sqInt numSlots; + sqInt numStack; + sqInt rcvr; + usqInt theContext; + sqInt valuePointer; + + assert(!((frameHasContext(theFP)))); + assert(!((isBaseFrame(theFP)))); + assert(addressCouldBeOop(longAt(theSP))); + + /* Decide how much of the stack to preserve in widowed contexts. Preserving too much state will potentially + hold onto garbage. Holding onto too little may mean that a dead context isn't informative enough in a + debugging situation. But the fundamental issue is that the temporary values captured when the frame is + married are likely stale, so if the temporary variable values are important they need to be updated later; + see primitiveContextPreserveTemps. So when marrying we capture the arguments and set the stack pointer + to accomodate (at least *) the temporaries, but the temporaries are all set to nil. In machine code contexts + are created via code generated by genGetActiveContextLarge:inBlock: which must agree perfectly with + this method. + * the V3PlusClosures bytecode set has to use pushNil bytecodes to establish slots for temporary variables + in block contexts, but a pushNil may be ambiguous with oushing a receiver, or even argument, e.g. + [nil == nil]. Instead of sweating the issue of using an accurate temporary count, we simply set the stack + pointer to its current value and set all slots following the arguments to nil. */ + methodFieldOrObj = longAt(theFP + FoxMethod); + if ((((usqInt)methodFieldOrObj)) < (startOfMemory())) { + longAtput(theFP + FoxMethod,methodFieldOrObj | MFMethodFlagHasContextFlag); + cogMethod = ((CogMethod *) (methodFieldOrObj & MFMethodMask)); + numArgs = (cogMethod->cmNumArgs); + if (((cogMethod->cmType)) >= CMMethod) { + closureOrNil = ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock) + ? longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((numArgs << (shiftForWord()))))) + : GIV(nilObj)); + } + else { + assert(isCMBlock(((CogBlockMethod *) cogMethod))); + + /* cmHomeMethod */ + if ((((((CogBlockMethod *) cogMethod))->cmType)) >= CMMethod) { + cogMethod = ((CogMethod *) (((CogBlockMethod *) cogMethod))); + } + else { + assert(CMBlock == ((((CogBlockMethod *) cogMethod))->cmType)); + cogMethod = ((CogMethod *) ((((usqInt)(((CogBlockMethod *) cogMethod)))) - (((((CogBlockMethod *) cogMethod))->homeOffset)))); + } + closureOrNil = longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((numArgs << (shiftForWord()))))); + } + methodFieldOrObj = (cogMethod->methodObject); + header = (cogMethod->methodHeader); + rcvr = longAt(theFP + FoxMFReceiver); + } + else { + /* begin setIFrameHasContext: */ + byteAtput((theFP + FoxIFrameFlags) + 2,1); + assert(isCompiledMethod(methodFieldOrObj)); + headerSqInt = longAt((void *)((methodFieldOrObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + if (byteAt((theFP + FoxIFrameFlags) + 3)) { + numArgs = byteAt((theFP + FoxIFrameFlags) + 1); + closureOrNil = longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((numArgs << (shiftForWord()))))); + } + else { + numArgs = (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; + closureOrNil = GIV(nilObj); + } + rcvr = longAt(theFP + FoxIFReceiver); + } + + /* inline (self isMachineCodeFrame: theFP) */ + numStack = (((usqInt)(((theFP + FoxMFReceiver) - theSP))) >> (shiftForWord())) + numArgs; + numSlots = (header & LargeContextBit + ? LargeContextSlots + : SmallContextSlots); + + /* begin eeInstantiateMethodContextSlots: */ + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + theContext = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((indexablePointersFormat())) << (formatShift()))))) + ClassMethodContextCompactIndex); + GIV(freeStart) += numBytes; + theContext = newObj; + /* end eeInstantiateMethodContextSlots: */ +l1: + + /* begin setFrameContext:to: */ + longAtput(theFP + FoxThisContext,theContext); + + /* Mark context as married by setting its sender to the frame pointer plus SmallInteger tags and the InstructionPointer + to the saved fp (which ensures correct alignment w.r.t. the frame when we check for validity) */ + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(theFP)) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(theFP)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, theContext, valuePointer)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),valuePointer); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(((char *)(longAt(theFP + FoxSavedFP))))) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(((char *)(longAt(theFP + FoxSavedFP))))) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, theContext, valuePointer)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(StackPointerIndex, theContext, (((usqInt)numStack << 3) | 1))); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord()))))),(((usqInt)numStack << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(MethodIndex, theContext, methodFieldOrObj)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord()))))),methodFieldOrObj); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(ClosureIndex, theContext, closureOrNil)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord()))))),closureOrNil); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(ReceiverIndex, theContext, rcvr)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord()))))),rcvr); + for (i = 1; i <= numArgs; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(ReceiverIndex + i, theContext, longAt((void *)((theFP + FoxCallerSavedIP) + ((numArgs - (i - 1)) * BytesPerWord))))); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))),longAt((void *)((theFP + FoxCallerSavedIP) + ((numArgs - (i - 1)) * BytesPerWord)))); + } + for (i = (numArgs + 1); i <= numStack; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(ReceiverIndex + i, theContext, GIV(nilObj))); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))),GIV(nilObj)); + } + assert(frameHasContext(theFP)); + assert(frameOfMarriedContext(theContext) == theFP); + assert((numStack + ReceiverIndex) < (lengthOf(theContext))); + return theContext; +} + + /* CoInterpreter>>#maxLookupNoMNUErrorCode */ +sqInt +maxLookupNoMNUErrorCode(void) +{ + return ((SelectorCannotInterpret < SelectorDoesNotUnderstand) ? SelectorDoesNotUnderstand : SelectorCannotInterpret); +} + + /* CoInterpreter>>#methodCacheAddress */ +usqInt +methodCacheAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)GIV(methodCache)); +} + + /* CoInterpreter>>#methodHasCogMethod: */ +int +methodHasCogMethod(sqInt aMethodOop) +{ + sqInt methodHeader; + + assert(isNonImmediate(aMethodOop)); + methodHeader = longAt((void *)((aMethodOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + return (!(methodHeader & (smallIntegerTag()))); +} + + /* CoInterpreter>>#methodHasMachineCodePrimitiveMethod: */ +static NoDbgRegParms sqInt +methodHasMachineCodePrimitiveMethod(sqInt methodObj) +{ + sqInt methodHeader; + + methodHeader = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + return ((/* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))), + /* isNonIntegerObject: */ + (!(methodHeader & (smallIntegerTag()))))) + && (cogMethodHasMachineCodePrim(((CogMethod *) methodHeader))); +} + + /* CoInterpreter>>#methodHasPrimitiveInPrimTracePlugin: */ +static NoDbgRegParms NeverInline sqInt +methodHasPrimitiveInPrimTracePlugin(sqInt aMethodObj) +{ + sqInt len; + sqInt lit; + usqInt numSlots; + sqInt ok; + sqInt pluginName; + + if (!(((primitiveIndexOf(aMethodObj)) == PrimNumberExternalCall) + && ((literalCountOf(aMethodObj)) > 0))) { + return 0; + } + lit = longAt((void *)((aMethodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(lit & (tagMask())))) + && (((byteAt((void *)(lit + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(lit)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(lit + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(lit - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4))) { + return 0; + } + assert(primTracePluginName); + pluginName = longAt((void *)((lit + BaseHeaderSize) + (0U << (shiftForWord())))); + if (!(/* isBytes: */ + ((!(pluginName & (tagMask())))) + && (((byteAt((void *)(pluginName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + return 0; + } + + /* begin object:equalsString: */ + len = strlen(primTracePluginName); + ok = /* object:equalsString:ofSize: */ + (/* isBytes: */ + ((!(pluginName & (tagMask())))) + && (((byteAt((void *)(pluginName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((!(((byteAt((void *)(pluginName + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((numBytesOfBytes(pluginName)) == len) + && ((strncmp(primTracePluginName, firstIndexableField(pluginName), len)) == 0))); + return ok; +} + + /* CoInterpreter>>#methodHasTooManyLiterals: */ +static NoDbgRegParms sqInt +methodHasTooManyLiterals(sqInt methodObj) +{ + return !(methodWithHeaderShouldBeCogged(methodHeaderOf(methodObj))); +} + + +/* Note: We now have 10 bits of primitive index, but they are in two places + for temporary backward compatibility. The time to unpack is negligible, + since the derived primitive function pointer is stored in the method + cache. */ + + /* CoInterpreter>>#methodHeaderHasPrimitive: */ +#if VMInvestigations +int +methodHeaderHasPrimitive(sqInt methodHeader) +{ + return ((methodHeader & AlternateHeaderHasPrimFlag) != 0); +} +#endif /* VMInvestigations */ + + /* CoInterpreter>>#methodNeedsLargeContext: */ +int +methodNeedsLargeContext(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + sqInt methodHeader; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin methodHeaderIndicatesLargeFrame: */ + return (methodHeader & LargeContextBit) != 0; +} + + /* CoInterpreter>>#methodShouldBeCogged: */ +sqInt +methodShouldBeCogged(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + sqInt methodHeader; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + return 1; + } + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(aMethodObj); + } + return 0; +} + + +/* At the moment jit any method with less than N literals, where N defaults + to 60. + See e.g. SimpleStackBasedCogit class>>initialize. + In my dev image eem 2/22/2009 13:39 + (30 to: 100 by: 5) collect: + [:n| n -> (SystemNavigation default allSelect: [:m| m numLiterals > n]) + size] #(30->1681 35->1150 40->765 45->523 50->389 55->289 60->206 + 65->151 70->124 75->99 80->73 85->63 90->54 95->42 100->38). + And running the CogVMSimulator with flagging of interpreted methods turned + on reveals + the following sizes of interpreted methods. + | sizes | + sizes := Bag new. + SystemNavigation default allSelect: [:m| m flag ifTrue: [sizes add: m + numLiterals]. false]. + sizes sortedElements asArray + #( 40->4 41->1 42->2 44->1 45->3 46->1 47->2 48->1 + 50->2 51->1 53->1 55->1 56->1 + 87->1 108->1 171->1) + literalCountOfHeader: does not include the header word. */ + + /* CoInterpreter>>#methodWithHeaderShouldBeCogged: */ +static NoDbgRegParms int +methodWithHeaderShouldBeCogged(sqInt methodHeader) +{ + return ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile; +} + + +/* Answer the Cog method for a machine code frame. This may be + either a full CogMethod or merely a CogBlockMethod rump header. */ + + /* CoInterpreter>>#mframeCogMethod: */ +static NoDbgRegParms CogBlockMethod * +mframeCogMethod(char *theFP) +{ + return ((CogBlockMethod *) ((longAt(theFP + FoxMethod)) & MFMethodMask)); +} + + /* CoInterpreter>>#mframeHomeMethodExport */ +CogMethod * +mframeHomeMethodExport(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt methodField; + + /* begin mframeHomeMethod: */ + methodField = longAt(GIV(framePointer) + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + return /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + } + return ((CogMethod *) (methodField & MFMethodMask)); +} + + +/* Answer the home method for a machine code frame. From a block frame we + find the home method through the block's homeOffset field which is the + delta to it. + In both cases we need to strip the isBlock and isContext flags from the + method field. */ + + /* CoInterpreter>>#mframeHomeMethod: */ +static NoDbgRegParms CogMethod * +mframeHomeMethod(char *theFP) +{ + sqInt methodField; + + methodField = longAt(theFP + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + return /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + } + return ((CogMethod *) (methodField & MFMethodMask)); +} + + +/* */ + + /* CoInterpreter>>#mframeIsBlockActivation: */ +static NoDbgRegParms int +mframeIsBlockActivation(char *theFP) +{ + return ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0; +} + + /* CoInterpreter>>#mframeReceiver: */ +static NoDbgRegParms sqInt +mframeReceiver(char *theFP) +{ + return longAt(theFP + FoxMFReceiver); +} + + +/* Traverse all stack pages looking for non-zero bytes in the headroom part + of each page. + Answer the minimum size of unused headroom (zero bytes) in the pages. This + is for + checking that there is enough headroom allocated in stack pages. */ + + /* CoInterpreter>>#minimumUnusedHeadroom */ +static NeverInline sqInt +minimumUnusedHeadroom(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt minUnused; + char *p; + StackPage *page; + sqInt unused; + + if (!(GIV(pages))) { + return -1; + } + minUnused = (((stackPageAt(0))->baseAddress)) - (((stackPageAt(0))->lastAddress)); + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + page = stackPageAtpages(i, GIV(pages)); + p = (page->lastAddress); + do { + p += BytesPerWord; + } while(((longAt(p)) == 0) + && (p <= ((page->baseAddress)))); + unused = (p - BytesPerWord) - ((page->lastAddress)); + if (unused < minUnused) { + minUnused = unused; + } + } + return minUnused; +} + + +/* Lookup the doesNotUnderstand: selector in the class of the argument rcvr. + Answer either the matching method (cogged if appropriate), or nil, if not + found. + */ + + /* CoInterpreter>>#mnuMethodOrNilFor: */ +static NoDbgRegParms sqInt +mnuMethodOrNilFor(sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentClass; + sqInt dictionary; + usqInt index; + usqInt length; + sqInt mask; + sqInt methodArray; + sqInt methodHeader; + sqInt mnuMethod; + sqInt mnuSelector; + sqInt nextSelector; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt tagBits; + sqInt wrapAround; + + assert(!((isOopForwarded(rcvr)))); + currentClass = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)); + mnuSelector = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))); + while (currentClass != GIV(nilObj)) { + dictionary = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + if (dictionary == GIV(nilObj)) { + return null; + } + + /* begin lookupMethodFor:InDictionary: */ + /* begin numSlotsOf: */ + assert((classIndexOf(dictionary)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(dictionary + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(dictionary - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + mask = (length - SelectorStart) - 1; + index = SelectorStart + (mask & ((((mnuSelector & (tagMask())) != 0) + ? (mnuSelector >> 3) + : (long32At((void *)(mnuSelector + 4))) & (identityHashHalfWordMask())))); + + /* It is assumed that there are some nils in this dictionary, and search will + stop when one is encountered. However, if there are no nils, then wrapAround + will be detected the second time the loop gets to the end of the table. */ + wrapAround = 0; + while (1) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((index << (shiftForWord()))))); + if (nextSelector == GIV(nilObj)) { + mnuMethod = null; + goto l1; + } + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == mnuSelector) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOop = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(index - SelectorStart, methodArray, objOop); + } + mnuMethod = objOop; + goto l1; + } + index += 1; + if (index == length) { + if (wrapAround) { + mnuMethod = null; + goto l1; + } + wrapAround = 1; + index = SelectorStart; + } + } + mnuMethod = null; + /* end lookupMethodFor:InDictionary: */ +l1: + if (mnuMethod) { + methodHeader = longAt((void *)((mnuMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + if ((!((/* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))), + /* isNonIntegerObject: */ + (!(methodHeader & (smallIntegerTag())))))) + && (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile)) { + cogselector(mnuMethod, mnuSelector); + } + return mnuMethod; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOopSqInt = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOopSqInt)); + if ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOopSqInt); + } + currentClass = objOopSqInt; + } + return null; +} + + +/* Move frames from the hot end of oldPage through to theFP to newPage. + This has the effect of making theFP a base frame which can be stored into. + Answer theFP's new location. */ + + /* CoInterpreter>>#moveFramesIn:through:toPage: */ +static NoDbgRegParms char * +moveFramesInthroughtoPage(StackPage *oldPage, char *theFP, StackPage *newPage) +{ + char *callerFP; + usqInt callerIP; + sqInt delta; + char *fpInNewPage; + char *newFP; + char *newSP; + char *offsetCallerFP; + char *source; + sqInt stackedReceiverOffset; + sqInt theContext; + char *theFP1; + char *theSP; + sqInt valuePointer; + sqInt valuePointerSqInt; + + + /* A base frame must have a context for cannotReturn: processing. */ + assert(!(isBaseFrame(theFP))); + assert(validStackPageBaseFrames()); + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + assert(frameHasContext(callerFP)); + assert(isContext(frameContext(callerFP))); + theSP = theFP + (((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? FoxMFReceiver + : FoxIFReceiver)); + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + theContext = longAt(theFP + FoxThisContext); + goto l1; + } + theContext = marryFrameSP(theFP, theSP); + /* end ensureFrameIsMarried:SP: */ +l1: + longAtput((newSP = (newPage->baseAddress)),longAt(callerFP + FoxThisContext)); + longAtput((newSP -= BytesPerWord),theContext); + stackedReceiverOffset = (FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))); + + /* First move the data, leaving room for the caller and base frame contexts. We will fix up frame pointers later. */ + for (source = (theFP + stackedReceiverOffset); source >= ((oldPage->headSP)); source += (-BytesPerWord)) { + newSP -= BytesPerWord; + longAtput(newSP,longAt(source)); + } + + /* newSP = oldSP + delta => delta = newSP - oldSP */ + delta = newSP - ((oldPage->headSP)); + newFP = (((newPage->baseAddress)) - stackedReceiverOffset) - (2 * BytesPerWord); + theFP1 = ((oldPage->headFP)) + delta; + + /* begin setHeadFP:andSP:inPage: */ + assert(newSP < theFP1); + assert((newSP < ((newPage->baseAddress))) + && (newSP > (((newPage->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((theFP1 < ((newPage->baseAddress))) + && (theFP1 > (((newPage->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (newPage->headFP = theFP1); + (newPage->headSP = newSP); + (newPage->baseFP = newFP); + callerIP = longAt(theFP + FoxCallerSavedIP); + if ((((usqInt)callerIP)) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(callerFP))); + longAtput(callerFP + FoxIFSavedIP,callerIP); + callerIP = ceReturnToInterpreterPC(); + } + longAtput(theFP + stackedReceiverOffset,callerIP); + assert((callerFP < ((oldPage->baseAddress))) + && (callerFP > (((oldPage->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (oldPage->headFP = callerFP); + (oldPage->headSP = theFP + stackedReceiverOffset); + + /* Mark the new base frame in the new page */ + longAtput(newFP + FoxCallerSavedIP,ceBaseFrameReturnPC()); + longAtput(newFP + FoxSavedFP,0); + + /* Now relocate frame pointers, updating married contexts to refer to their moved spouse frames. */ + fpInNewPage = (newPage->headFP); + do { + offsetCallerFP = ((char *)(longAt(fpInNewPage + FoxSavedFP))); + if (offsetCallerFP) { + offsetCallerFP += delta; + } + longAtput(fpInNewPage + FoxSavedFP,oopForPointer(offsetCallerFP)); + if (/* frameHasContext: */ + ((((usqInt)(longAt(fpInNewPage + FoxMethod)))) < (startOfMemory()) + ? ((longAt(fpInNewPage + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((fpInNewPage + FoxIFrameFlags) + 2)) != 0)) { + theContext = longAt(fpInNewPage + FoxThisContext); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(fpInNewPage)) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(fpInNewPage)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, theContext, valuePointer)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),valuePointer); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(offsetCallerFP)) & (BytesPerWord - 1)) == 0); + valuePointerSqInt = (oopForPointer(offsetCallerFP)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, theContext, valuePointerSqInt)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointerSqInt); + } + fpInNewPage = offsetCallerFP; + } while(fpInNewPage != 0); + assert(validStackPageBaseFrames()); + return newFP; +} + + +/* Map the native pc theIP into a bytecode pc integer object and answer it. + See contextInstructionPointer:frame: for the explanation. */ + + /* CoInterpreter>>#mustMapMachineCodePC:context: */ +static NoDbgRegParms sqInt +mustMapMachineCodePCcontext(sqInt theIP, sqInt aOnceMarriedContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bcpc; + CogMethod *cogMethod; + CogMethod *cogMethod1; + CogBlockMethod *cogMethodForIP; + sqInt maybeClosure; + sqInt mcpc; + sqInt methodObj; + usqInt numSlots; + sqInt rawHeader; + usqInt startBcpc; + sqInt yetToCompact; + + if (theIP == HasBeenReturnedFromMCPC) { + return GIV(nilObj); + } + maybeClosure = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord())))))); + methodObj = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + if ((maybeClosure != GIV(nilObj)) + && ((/* begin isVanillaBlockClosure: */ + assert((isPointers(maybeClosure)) + && ((numSlotsOf(maybeClosure)) >= ClosureFirstCopiedValueIndex)), + ((((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1)))) { + assert((((theIP) >> 16)) < -1); + startBcpc = (((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))))) >> 3)) - 1; + } + else { + assert((((theIP) >> 16)) == -1); + startBcpc = ((literalCountOf(methodObj)) + LiteralStart) * BytesPerOop; + } + + /* begin ensureMethodIsCogged:maybeClosure: */ + rawHeader = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) rawHeader); + goto l2; + } + yetToCompact = 1; + while (1) { + if ((maybeClosure != GIV(nilObj)) + && (!((/* begin isVanillaBlockClosure: */ + assert((isPointers(maybeClosure)) + && ((numSlotsOf(maybeClosure)) >= ClosureFirstCopiedValueIndex)), + ((((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1))))) { + cogMethod1 = cogFullBlockMethodnumCopied( + methodObj, + (/* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(maybeClosure)))), + ((/* begin numSlotsOf: */ + assert((classIndexOf(maybeClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(maybeClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(maybeClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex)); + } + else { + cogMethod1 = cogselector(methodObj, GIV(nilObj)); + } + if (!((cogMethod1 == null) + && (GIV(cogCompiledCodeCompactionCalledFor) + && (yetToCompact)))) break; + yetToCompact = 0; + commenceCogCompiledCodeCompaction(); + } + if (!(asserta(cogMethod1 != null))) { + error("could not compile method that should have been compiled"); + } + cogMethod = cogMethod1; + /* end ensureMethodIsCogged:maybeClosure: */ +l2: + + /* begin bytecodePCFor:cogMethod:startBcpc: */ + assert(theIP < 0); + if ((((theIP) >> 16)) < -1) { + cogMethodForIP = ((CogBlockMethod *) ((((sqInt)cogMethod)) - ((((theIP) >> 16)) * (blockAlignment())))); + assert(isCMBlock(cogMethodForIP)); + assert((cmHomeMethod(cogMethodForIP)) == cogMethod); + mcpc = (((sqInt)cogMethodForIP)) - (((short)theIP)); + } + else { + cogMethodForIP = ((CogBlockMethod *) cogMethod); + assert(isCMMethodEtAl(cogMethodForIP)); + mcpc = (((sqInt)cogMethod)) - theIP; + + /* map any pcs in primitive code (i.e. return addresses for interpreter primitive calls) to the initial pc */ + if ((((usqInt)mcpc)) < ((cogMethod->stackCheckOffset))) { + bcpc = startBcpc; + goto l1; + } + } + + /* See contextInstructionPointer:frame: */ + assert(((mcpc >= (((sqInt)cogMethod))) && (mcpc <= ((((sqInt)cogMethod)) + ((cogMethod->blockSize)))))); + bcpc = bytecodePCForstartBcpcin(mcpc, startBcpc, cogMethodForIP); + /* end bytecodePCFor:cogMethod:startBcpc: */ +l1: + assert(bcpc >= (startPCOfMethod(methodObj))); + + /* If there's a CallPrimitive we need to skip it. */ + if ((bcpc == startBcpc) + && ((maybeClosure == GIV(nilObj)) + && (((((cogMethod->methodHeader)) & AlternateHeaderHasPrimFlag) != 0)))) { + bcpc += 3 /* sizeOfCallPrimitiveBytecode: */; + } + return (((usqInt)(bcpc + 1) << 3) | 1); +} + + /* CoInterpreter>>#newMethodAddress */ +usqInt +newMethodAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(newMethod)))); +} + + +/* N.B. nextProfileTick is 64-bits */ + + /* CoInterpreter>>#nextProfileTickAddress */ +usqInt +nextProfileTickAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(nextProfileTick)))); +} + + /* CoInterpreter>>#noAssertHeaderOf: */ +sqInt +noAssertHeaderOf(sqInt methodPointer) +{ + sqInt methodHeader; + + methodHeader = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + return ((!(methodHeader & (smallIntegerTag()))) + ? ((((CogMethod *) methodHeader))->methodHeader) + : methodHeader); +} + + +/* integerValue is interpreted as POSITIVE, e.g. as the result of Bitmap>at:. + N.B. Returning in each arm separately enables Slang inlining. + /Don't/ return the ifTrue:ifFalse: unless Slang inlining of conditionals + is fixed. */ + + /* CoInterpreter>>#positive32BitIntegerFor: */ +sqInt +positive32BitIntegerFor(unsigned int integerValue) +{ + return (((usqInt)((((usqInt)integerValue)) & 0xFFFFFFFFU) << 3) | 1); +} + + +/* Convert the given object into an integer value. + The object may be either a positive SmallInteger or a four-byte + LargePositiveInteger. + */ + + /* CoInterpreter>>#positive32BitValueOf: */ +unsigned int +positive32BitValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt value64; + + if ((((oop) & 7) == 1)) { + value64 = (oop >> 3); + if ((value64 < 0) + || ((((unsigned int) value64)) != value64)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + value64 = 0; + } + return value64; + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; +} + + +/* Answer a Large Positive Integer object for the given integer value. N.B. + will *not* cause a GC. */ + + /* CoInterpreter>>#positive64BitIntegerFor: */ +sqInt +positive64BitIntegerFor(usqLong integerValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newLargeInteger; + usqInt newObj; + usqInt numBytes; + sqInt numSlots; + sqInt objFormat; + sqInt sz; + + if (integerValue <= (MaxSmallInteger)) { + return ((integerValue << 3) | 1); + } + sz = 8; + objFormat = (firstByteFormat()) + ((8 - sz) & (BytesPerWord - 1)); + numSlots = 8 / BytesPerOop; + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((numSlots >= 0) + && ((knownClassAtIndex(ClassLargePositiveIntegerCompactIndex)) != GIV(nilObj))); + assert(((objFormat < (firstByteFormat()) + ? objFormat + : objFormat & (byteFormatMask()))) == (instSpecOfClass(knownClassAtIndex(ClassLargePositiveIntegerCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newLargeInteger = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + ClassLargePositiveIntegerCompactIndex); + GIV(freeStart) += numBytes; + newLargeInteger = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((newLargeInteger + BaseHeaderSize)),SQ_SWAP_8_BYTES_IF_BIGENDIAN(integerValue)); + return newLargeInteger; +} + + +/* Convert the given object into an integer value. + The object may be either a positive SmallInteger or up to an eight-byte + LargePositiveInteger. + */ + + /* CoInterpreter>>#positive64BitValueOf: */ +usqLong +positive64BitValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + sqInt fmt; + usqInt numSlots; + int ok; + sqInt smallIntValue; + sqInt sz; + + if ((((oop) & 7) == 1)) { + smallIntValue = (oop >> 3); + if (smallIntValue < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return smallIntValue; + } + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (!ok) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + sz = ((((/* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (sz > (sizeof(sqLong))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + if (sz > 4) { + return SQ_SWAP_8_BYTES_IF_BIGENDIAN((long64At((void *)((oop + BaseHeaderSize))))); + } + return ((unsigned int) (SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((oop + BaseHeaderSize))))))); +} + + +/* Signal the gc semaphore, and inform the display subsystem if the display + bitmap has moved. + */ +/* Attempt to shrink free memory, signal the gc semaphore and let the Cogit + do its post GC thang + */ + + /* CoInterpreter>>#postGCAction: */ +static NoDbgRegParms void +postGCAction(sqInt gcModeArg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(gcModeArg == GIV(gcMode)); + signalSemaphoreWithIndex(GIV(gcSemaphoreIndex)); + postGCUpdateDisplayBits(); + cogitPostGCAction(gcModeArg); + + /* begin nilUncoggableMethods */ + GIV(lastCoggableInterpretedBlockMethod) = (GIV(lastUncoggableInterpretedBlockMethod) = null); + GIV(gcMode) = 0; +} + + +/* Need to write back the frame pointers unless all pages are free (as in + snapshot) + */ +/* Need to write back the frame pointers unless all pages are free (as in + snapshot). Need to set gcMode var (to avoid passing the flag through a lot + of the updating code) */ + + /* CoInterpreter>>#preGCAction: */ +static NoDbgRegParms void +preGCAction(sqInt gcModeArg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aTraceCode; + int traceType; + + if (GIV(stackPage)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + if (recordPrimTrace()) { + aTraceCode = (gcModeArg == GCModeFull + ? TraceFullGC + : TraceIncrementalGC); + + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)aTraceCode << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + if (gcModeArg == GCModeFull) { + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + } + GIV(gcMode) = gcModeArg; + if (recordEventTrace()) { + traceType = (gcModeArg == GCModeFull + ? TraceFullGC + : TraceIncrementalGC); + + /* begin recordTrace: */ + /* begin recordTrace:source:thing:extra: */ + if (TraceLog) { + GIV(traceLog)[GIV(traceLogIndex)] = ((((((usqInt)(traceType) << TraceTypeShift))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = 0; + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + } +} + + /* CoInterpreter>>#primFailCodeAddress */ +usqInt +primFailCodeAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(primFailCode)))); +} + + +/* Return from a become primitive after invoking + become:with:twoWay:copyHash:. If succeeding and the top frame has been + divorced then continue in the interpreter. + See the comment in primitiveArrayBecome. */ + + /* CoInterpreter>>#primitiveBecomeReturn: */ +static NoDbgRegParms void +primitiveBecomeReturn(sqInt ec) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContextOrNil; + sqInt activeProc; + sqInt objOop; + + if (ec) { + /* primitiveFailFor: */ + GIV(primFailCode) = ec; + } + else { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + activeContextOrNil = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if (!GIV(framePointer)) { + assert(isContext(activeContextOrNil)); + + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContextOrNil); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(activeProc)) + && (!(isForwarded(activeProc)))); + assert(validStorePointerUncheckedArgs(SuspendedContextIndex, activeProc, GIV(nilObj))); + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* pop bogus machine-code instructionPointer and any primitive arguments */ + + /* begin pop: */ + GIV(stackPointer) += (GIV(argumentCount) + 1) * BytesPerWord; + ceInvokeInterpret(); + } + + /* If the top frame was divorced then continue in the interpreter. + If not, check where we are and continue... */ + if (!(activeContextOrNil == GIV(nilObj))) { + /* divorcing should not have disturbed the topFrame if it wasn;t divorced */ + assert(isContext(activeContextOrNil)); + assert(GIV(framePointer) == (frameOfMarriedContext(activeContextOrNil))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(activeProc)) + && (!(isForwarded(activeProc)))); + assert(validStorePointerUncheckedArgs(SuspendedContextIndex, activeProc, GIV(nilObj))); + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),GIV(nilObj)); + } + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } +} + + +/* receiver, args, then method are on top of stack. Execute method against + receiver and args. + Set primitiveFunctionPointer because no cache lookup has been done for the + method, and + hence primitiveFunctionPointer is stale. */ + + /* CoInterpreter>>#primitiveExecuteMethod */ +static void +primitiveExecuteMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodArgument; + sqInt methodHeader; + sqInt primitiveIndex; + sqInt top; + + methodArgument = longAt(GIV(stackPointer)); + if (!(/* isOopCompiledMethod: */ + ((!(methodArgument & (tagMask())))) + && (((byteAt((void *)(methodArgument + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (!((GIV(argumentCount) - 1) == (argumentCountOf(methodArgument)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(newMethod) = top; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]))); + GIV(argumentCount) -= 1; + executeNewMethodJitting(); + + /* Recursive xeq affects primErrorCode */ + + /* begin initPrimCall */ + GIV(primFailCode) = 0; +} + + +/* receiver, argsArray, then method are on top of stack. Execute method + against receiver and args. Allow for up to two extra arguments (e.g. for + mirror primitives). + Set primitiveFunctionPointer because no cache lookup has been done for the + method, and hence primitiveFunctionPointer is stale. */ + + /* CoInterpreter>>#primitiveExecuteMethodArgsArray */ +static void +primitiveExecuteMethodArgsArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt argCnt; + sqInt argumentArray; + usqInt firstBytecode; + sqInt header; + sqInt i; + sqInt methodArgument; + sqInt methodHeader; + usqInt numSlots; + sqInt primitiveIndex; + sqInt rcvr; + + methodArgument = longAt(GIV(stackPointer)); + argumentArray = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((/* isOopCompiledMethod: */ + ((!(methodArgument & (tagMask())))) + && (((byteAt((void *)(methodArgument + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* begin argumentCountOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodArgument)); + header = longAt((void *)((methodArgument + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + argCnt = (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; + if (!(argCnt == ((/* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + if (GIV(argumentCount) > 2) { + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if ((GIV(argumentCount) > 4) + || (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord),rcvr); + } + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + for (i = 0; i < argCnt; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))))); + } + GIV(newMethod) = methodArgument; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]))); + GIV(argumentCount) = argCnt; + executeNewMethodJitting(); + + /* Recursive xeq affects primErrorCode */ + + /* begin initPrimCall */ + GIV(primFailCode) = 0; +} + + +/* Answer any special requirements of the given primitive */ + + /* CoInterpreter>>#primitivePropertyFlags:primIndex: */ +sqInt +primitivePropertyFlagsprimIndex(sqInt methodObj, sqInt primIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int baseFlags; + + /* begin primitivePropertyFlagsForSpur:primIndex: */ + baseFlags = 0; + if (GIV(profileSemaphore)) { + if (isRealNonProfilingPrimitiveMethodprimIndex(methodObj, primIndex)) { + baseFlags = PrimCallCollectsProfileSamples + PrimCallNeedsNewMethod; + } + } +# if LRPCheck + if (GIV(longRunningPrimitiveCheckSemaphore)) { + baseFlags = baseFlags | PrimCallNeedsNewMethod; + } +# endif + + + /* Because one primitive number is used for all three vmParameter primtiives + the metadata can't be attached to the method implementation. */ + if ((primIndex == PrimNumberVMParameter) + && ((argumentCountOf(methodObj)) == 1)) { + return (baseFlags + PrimCallOnSmalltalkStack) + PrimCallOnSmalltalkStackAlign2x; + } + + /* vmParameterAt: */ + return baseFlags | ((primitiveMetadataTable[primIndex]) & SpurPrimitiveFlagsMask); +} + + /* CoInterpreter>>#primTraceLogAddress */ +void * +primTraceLogAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(primTraceLog); +} + + +/* N.B. primTraceLogIndex is 8-bits */ + + /* CoInterpreter>>#primTraceLogIndexAddress */ +usqInt +primTraceLogIndexAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(primTraceLogIndex)))); +} + + /* CoInterpreter>>#printCogMethod: */ +void +printCogMethod(CogMethod *cogMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + usqInt firstBytecode; + sqInt primitive; + sqInt s; + + address = ((sqInt)cogMethod); + printHex(address); + print(" <-> "); + printHex(address + ((cogMethod->blockSize))); + if (((cogMethod->cmType)) >= CMMethod) { + print(": method: "); + printHex((cogMethod->methodObject)); + + /* primitiveIndexOfMethod:header: */ + if (((((cogMethod->methodHeader)) & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (((cogMethod->methodObject)) + ((LiteralStart + (((((cogMethod->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitive = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitive = 0; + } + if (primitive) { + print(" prim "); + printNum(primitive); + } + if (addressCouldBeObj((cogMethod->methodObject))) { + if ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) { + print(" [full]"); + } + else { + if (addressCouldBeObj(methodClassOf((cogMethod->methodObject)))) { + /* begin space */ + printChar(' '); + printNameOfClasscount(methodClassOf((cogMethod->methodObject)), 2); + } + } + } + } + if (((cogMethod->cmType)) == CMBlock) { + print(": block home: "); + printHex(((usqInt)(/* cmHomeMethod */ + ((((((CogBlockMethod *) cogMethod))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) cogMethod))) + : (assert(CMBlock == ((((CogBlockMethod *) cogMethod))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) cogMethod)))) - (((((CogBlockMethod *) cogMethod))->homeOffset))))))))); + } + if (((cogMethod->cmType)) == CMClosedPIC) { + print(": Closed PIC N: "); + printHex((cogMethod->cPICNumCases)); + } + if (((cogMethod->cmType)) == CMOpenPIC) { + print(": Open PIC "); + } + print(" selector: "); + printHex((cogMethod->selector)); + if (((cogMethod->selector)) == GIV(nilObj)) { + if ((((cogMethod->cmType)) >= CMMethod) + && (((s = maybeSelectorOfMethod((cogMethod->methodObject)))))) { + print(" (nil: "); + printStringOf(s); + print(")"); + } + else { + print(" (nil)"); + } + } + else { + /* begin space */ + printChar(' '); + printStringOf((cogMethod->selector)); + } + cr(); +} + + /* CoInterpreter>>#printFrameFlagsForFP: */ +static NoDbgRegParms void +printFrameFlagsForFP(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *address; + usqInt it; + + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + address = theFP + FoxMethod; + it = (longAt(address)) & 7; + } + else { + address = theFP + FoxIFrameFlags; + it = longAt(address); + } + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%3sfrm flags: %p", + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? "mc" + : "int"), + ((void *)it)); + if (it) { + fprintf(GIV(transcript), + "=%d", + ((int) it)); + } + fprintf(GIV(transcript), + " numArgs: %d %sContext %sBlock\n", + ((int) (/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))), + (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0) + ? "is" + : "no"), + (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0) + ? "is" + : "not")); +} + + /* CoInterpreter>>#printFrameMethodFor: */ +static NoDbgRegParms void +printFrameMethodFor(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *address; + sqInt it; + + address = theFP + FoxMethod; + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + " method: %16p\t" /* pst: */, + ((void *)it)); + if (((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && (((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0)) { + fprintf(GIV(transcript), + "hm: %p\t", + ((void *)(mframeHomeMethod(theFP)))); + } + shortPrintOop(/* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod))); +} + + /* CoInterpreter>>#printFrameThing:at:extra: */ +static NoDbgRegParms void +printFrameThingatextra(char *name, char *address, sqInt extraValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt it; + + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%12s: %16p" /* pst: */, + name, + ((void *)it)); + framePrintDescription(it); + fprintf(GIV(transcript), + " %" PRIdSQINT "\n", + extraValue); +} + + +/* useful for VM debugging */ + + /* CoInterpreter>>#printFrame:WithSP: */ +sqInt +printFrameWithSP(char *theFP, char *theSP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *addr; + CogBlockMethod *cogMethod; + CogMethod *homeMethod; + usqInt i; + usqInt index; + usqInt numArgs; + usqInt numSlots; + usqInt numTemps; + char *rcvrAddress; + sqInt rcvrOrClosure; + usqInt theMethod; + sqInt theMethodEnd; + char *toDoLimit; + sqInt topThing; + + if (!(/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + fprintf(GIV(transcript), + "%p is not in the stack zone?!\n", + theFP); + return null; + } + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + cogMethod = mframeCogMethod(theFP); + homeMethod = mframeHomeMethod(theFP); + theMethod = ((usqInt)homeMethod); + theMethodEnd = (((sqInt)homeMethod)) + ((homeMethod->blockSize)); + numArgs = (cogMethod->cmNumArgs); + numTemps = (((usqInt)(((homeMethod->methodHeader)))) >> MethodHeaderTempCountShift) & 0x3F; + } + else { + theMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + theMethodEnd = theMethod + (((((/* begin numSlotsOf: */ + assert((classIndexOf(theMethod)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(theMethod + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(theMethod - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) + BaseHeaderSize); + numArgs = byteAt((theFP + FoxIFrameFlags) + 1); + numTemps = tempCountOf(theMethod); + } + if (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0)) { + /* No BlockLocalTempCounter in the Cogit's C code, so quick hack is to use numCopied + numArgs */ + rcvrOrClosure = longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))); + if (((!(rcvrOrClosure & (tagMask())))) + && ((addressCouldBeObj(rcvrOrClosure)) + && ((fetchClassOfNonImm(rcvrOrClosure)) == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassBlockClosure) << (shiftForWord())))))))))) { + numTemps = numArgs + (stSizeOf(rcvrOrClosure)); + } + else { + numTemps = numArgs; + } + } + shortPrintFrame(theFP); + rcvrAddress = theFP + ((FoxCallerSavedIP + BytesPerWord) + ((numArgs << (shiftForWord())))); + if (longAt(theFP + FoxSavedFP)) { + } + else { + rcvrAddress + (2 * BytesPerWord); + + /* empty frameRange:to: */ + printFrameOopat("(caller ctxt", rcvrAddress + (2 * BytesPerWord)); + printFrameOopat("(saved ctxt", rcvrAddress + (1 * BytesPerWord)); + } + printFrameOopat("rcvr/clsr", rcvrAddress); + for (i = numArgs; i >= 1; i += -1) { + printFrameOopindexat("arg", numArgs - i, (theFP + FoxCallerSavedIP) + (i * BytesPerWord)); + } + printFrameThingatextraString("caller ip", theFP + FoxCallerSavedIP, ((longAt(theFP + FoxCallerSavedIP)) == (ceReturnToInterpreterPC()) + ? "ceReturnToInterpreter" + : 0)); + + /* begin printFrameThing:at: */ + printFrameThingatextraString("saved fp", theFP + FoxSavedFP, ((char *)null)); + printFrameMethodFor(theFP); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + printFrameFlagsForFP(theFP); + } + printFrameOopat("context", theFP + FoxThisContext); + if (!((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()))) { + printFrameFlagsForFP(theFP); + } + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + rcvrAddress = theFP + FoxMFReceiver; + } + else { + printFrameThingatextra("saved ip", theFP + FoxIFSavedIP, (((usqInt)(longAt(theFP + FoxIFSavedIP))) + ? (((((usqInt)(longAt(theFP + FoxIFSavedIP)))) - theMethod) + 2) - BaseHeaderSize + : 0)); + rcvrAddress = theFP + FoxIFReceiver; + } + printFrameOopat("receiver", rcvrAddress); + topThing = longAt(theSP); + if ((oopisGreaterThanOrEqualToandLessThan(topThing, theMethod, theMethodEnd)) + || ((topThing == (ceReturnToInterpreterPC())) + || ((topThing == (ceBaseFrameReturnPC())) + || (topThing == (ceCannotResumePC()))))) { + toDoLimit = theSP + BytesPerWord; + for (addr = (rcvrAddress - BytesPerWord); addr >= toDoLimit; addr += (-BytesPerWord)) { + index = ((rcvrAddress - addr) / BytesPerWord) + numArgs; + if (index <= numTemps) { + printFrameOopindexat("temp", index - 1, addr); + } + else { + printFrameOopat((/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0) + ? "temp/stck" + : "stck"), addr); + } + } + printFrameThingatextra("frame ip", theSP, (topThing > theMethod + ? ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? topThing - theMethod + : ((topThing - theMethod) + 2) - BaseHeaderSize) + : topThing)); + } + else { + for (addr = (rcvrAddress - BytesPerWord); addr >= theSP; addr += (-BytesPerWord)) { + index = ((rcvrAddress - addr) / BytesPerWord) + numArgs; + if (index <= numTemps) { + printFrameOopindexat("temp", index - 1, addr); + } + else { + printFrameOopat((/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0) + ? "temp/stck" + : "stck"), addr); + } + } + } + return 0; +} + + +/* useful for VM debugging */ + + /* CoInterpreter>>#printMethodCacheFor: */ +void +printMethodCacheFor(sqInt thing) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqIntptr_t c; + int i; + sqIntptr_t m; + sqInt n; + sqIntptr_t p; + sqIntptr_t s; + + n = 0; + for (i = 0; i < MethodCacheSize; i += MethodCacheEntrySize) { + s = GIV(methodCache)[i + MethodCacheSelector]; + c = GIV(methodCache)[i + MethodCacheClass]; + m = GIV(methodCache)[i + MethodCacheMethod]; + p = GIV(methodCache)[i + MethodCachePrimFunction]; + if (((thing == -1) + || ((s == thing) + || ((c == thing) + || ((p == thing) + || ((m == thing) + || ((addressCouldBeObj(m)) + && ((/* maybeMethodHasCogMethod: */ + ((!(m & (tagMask())))) + && ((((byteAt((void *)(m + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((m + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) + && ((((sqInt)(cogMethodOf(m)))) == thing)))))))) + && ((/* addressCouldBeOop: */ + (((s & (tagMask())) != 0)) + || (addressCouldBeObj(s))) + && ((c != 0) + && ((addressCouldBeClassObj(c)) + || (addressCouldBeClassObj(classForClassTag(c))))))) { + n += 1; + fprintf(GIV(transcript), + "%d %x\n\t", + ((int) i), + ((int) i)); + if (((byteAt((void *)(s + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())) { + fprintf(GIV(transcript), + "%p %.*s\n", + ((void *)s), + ((int) (numBytesOfBytes(s))), + ((char *) (firstIndexableField(s)))); + } + else { + shortPrintOop(s); + } + + /* begin tab */ + printChar('\t'); + if (addressCouldBeClassObj(c)) { + shortPrintOop(c); + } + else { + printNum(c); + + /* begin space */ + printChar(' '); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)c)); + printChar(' '); + shortPrintOop(classForClassTag(c)); + } + + /* begin tab */ + printChar('\t'); + shortPrintOop(m); + + /* begin tab */ + printChar('\t'); + if (p > 0x400) { + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)p)); + } + else { + printNum(p); + } + cr(); + } + } + if (n > 1) { + printNum(n); + cr(); + } +} + + /* CoInterpreter>>#quickPrimitiveConstantFor: */ +sqInt +quickPrimitiveConstantFor(sqInt aQuickPrimitiveIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + switch (aQuickPrimitiveIndex) { + case 0x101: + return GIV(trueObj); + + case 258: + return GIV(falseObj); + + case 259: + return GIV(nilObj); + + case 260: + return ConstMinusOne; + + case 261: + return ConstZero; + + case 262: + return ConstOne; + + case 263: + return ConstTwo; + + default: + error("Case not found and no otherwise clause"); + } + return 0; +} + + /* CoInterpreter>>#quickPrimitiveGeneratorFor: */ +sqInt +(*quickPrimitiveGeneratorFor(sqInt aQuickPrimitiveIndex))(void) +{ + switch (aQuickPrimitiveIndex) { + case 0x100: + return genQuickReturnSelf; + + case 0x101: + case 258: + case 259: + case 260: + case 261: + case 262: + case 263: + return genQuickReturnConst; + + default: + return genQuickReturnInstVar; + } + return 0; +} + + /* CoInterpreter>>#quickPrimitiveInstVarIndexFor: */ +sqInt +quickPrimitiveInstVarIndexFor(sqInt primIndex) +{ + return primIndex - 264; +} + + /* CoInterpreter>>#rawHeaderOf: */ +sqInt +rawHeaderOf(sqInt methodPointer) +{ + return longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); +} + + +/* Since methods may be updated while forwarding during become, make the + assert accomodate this. + */ + + /* CoInterpreter>>#rawHeaderOf:put: */ +void +rawHeaderOfput(sqInt methodOop, sqInt cogMethodOrMethodHeader) +{ + assert(isCompiledMethodHeader(headerWhileForwardingOf(methodOop))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(methodOop)) + && (!(isForwarded(methodOop)))); + assert(validStorePointerUncheckedArgs(HeaderIndex, methodOop, cogMethodOrMethodHeader)); + longAtput((void *)((methodOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))),cogMethodOrMethodHeader); +} + + +/* Read an image from the given file stream, allocating an amount of memory + to its object heap. + + V3: desiredHeapSize is the total size of the heap. Fail if the image has + an unknown format or + requires more than the specified amount of memory. + + Spur: desiredHeapSize is ignored; this routine will attempt to provide at + least extraVMMemory's + ammount of free space after the image is loaded, taking any free space in + the image into account. + extraVMMemory is stored in the image header and is accessible as + vmParameterAt: 23. If + extraVMMemory is 0, the value defaults to the default grow headroom. Fail + if the image has an + unknown format or if sufficient memory cannot be allocated. + + Details: This method detects when the image was stored on a machine with + the opposite byte + ordering from this machine and swaps the bytes automatically. Furthermore, + it allows the header + information to start 512 bytes into the file, since some file transfer + programs for the Macintosh + apparently prepend a Mac-specific header of this size. Note that this same + 512 bytes of prefix + area could also be used to store an exec command on Unix systems, allowing + one to launch + Smalltalk by invoking the image name as a command. */ + + /* CoInterpreter>>#readImageFromFile:HeapSize:StartingAt: */ +size_t +readImageFromFileHeapSizeStartingAt(sqImageFile f, usqInt desiredHeapSize, squeakFileOffsetType imageOffset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt allocationReserve; + sqInt anObject; + sqInt bit; + sqInt bytes; + sqInt bytesRead; + sqInt bytesToShift; + sqInt classArrayClass; + sqInt classArrayObj; + sqInt cogCodeBase; + size_t dataSize; + sqInt firstSegSize; + sqInt freeOldSpaceInImage; + usqInt hdrCogCodeSize; + sqInt hdrEdenBytes; + usqInt hdrMaxExtSemTabSize; + usqInt hdrNumStackPages; + sqInt headerSize; + squeakFileOffsetType headerStart; + sqInt headroom; + sqInt headroomSqInt; + usqInt heapSize; + usqInt heapSizeUsqInt; + sqInt i; + usqInt mem; + usqInt minimumMemory; + usqInt numSlots; + sqInt objOop; + sqInt oop; + sqInt rawVersion; + sqInt seed; + int swapBytes; + sqInt toDoLimit; + sqInt toDoLimit1; + sqInt version; + int w; + unsigned short w1; + sqInt wSqInt; + + rawVersion = 0; + + /* stdout is not available at compile time. this is the earliest available point. */ + GIV(transcript) = stdout; + + /* guess Metaclass instSize */ + GIV(metaclassNumSlots) = 6; + + /* guess (Class instVarIndexFor: 'name' ifAbsent: []) - 1 */ + GIV(classNameIndex) = 6; + version = checkImageVersionFromstartingAtassignRawVersion(f, imageOffset, (&rawVersion)); + if (!version) { + /* begin bailOutOfImageLoad: */ + fprintf(stderr, + "This ver %d vm cannot read ver %" PRIdSQINT " image file %s\n", + ((int) 68021 /* imageFormatVersion */), + rawVersion, + getImageName()); + } + swapBytes = rawVersion != version; + GIV(multipleBytecodeSetsActive) = ((version & MultipleBytecodeSetsBitmask) != 0); + + /* record header start position */ + headerStart = (sqImageFilePosition(f)) - 4; + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + headerSize = (swapBytes + ? SQ_SWAP_4_BYTES(w) + : w); + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + dataSize = ((sqInt) ((swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt))); + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + GIV(oldImageBaseAddress) = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + anObject = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin specialObjectsOop: */ + GIV(specialObjectsOop) = anObject; + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + seed = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin lastHash: */ + if (!((GIV(lastHash) = seed & (identityHashHalfWordMask())))) { + while (((GIV(lastHash) = (((usqInt) (ioUTCMicrosecondsNow()))) & (identityHashHalfWordMask()))) == 0) { + } + } + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + GIV(savedWindowSize) = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + imageHeaderFlags = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + extraVMMemory = (swapBytes + ? SQ_SWAP_4_BYTES(w) + : w); + + /* begin getShortFromFile:swap: */ + w1 = 0; + sqImageFileRead((&w1), sizeof(unsigned short), 1, f); + hdrNumStackPages = (swapBytes + ? ((((usqInt)(w1)) >> 8) & 0xFF) | (((w1 & 0xFF) << 8)) + : w1); + + /* 4 stack pages is small. Should be able to run with as few as + three. 4 should be comfortable but slow. 8 is a reasonable + default. Can be changed via vmParameterAt: 43 put: n. + Can be set as a preference (Info.plist, VM.ini, command line etc). + If desiredNumStackPages is already non-zero then it has been + set as a preference. Ignore (but preserve) the header's default. */ + GIV(numStackPages) = (desiredNumStackPages + ? desiredNumStackPages + : (hdrNumStackPages + ? hdrNumStackPages + : defaultNumStackPages())); + desiredNumStackPages = hdrNumStackPages; + + /* This slot holds the size of the native method zone in 1k units. (pad to word boundary). */ + hdrCogCodeSize = (getShortFromFileswap(f, swapBytes)) * 0x400; + + /* set for vmParameter 47 */ + GIV(cogCodeSize) = (desiredCogCodeSize + ? desiredCogCodeSize + : (hdrCogCodeSize + ? (desiredCogCodeSize = hdrCogCodeSize) + : defaultCogCodeSize())); + if (GIV(cogCodeSize) > (maxCogCodeSize())) { + GIV(cogCodeSize) = maxCogCodeSize(); + } + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + hdrEdenBytes = (swapBytes + ? SQ_SWAP_4_BYTES(w) + : w); + bytes = (desiredEdenBytes + ? desiredEdenBytes + : (hdrEdenBytes + ? hdrEdenBytes + : defaultEdenBytes())); + + /* begin edenBytes: */ + GIV(edenBytes) = bytes; + desiredEdenBytes = hdrEdenBytes; + + /* begin getShortFromFile:swap: */ + w1 = 0; + sqImageFileRead((&w1), sizeof(unsigned short), 1, f); + hdrMaxExtSemTabSize = (swapBytes + ? ((((usqInt)(w1)) >> 8) & 0xFF) | (((w1 & 0xFF) << 8)) + : w1); + if (hdrMaxExtSemTabSize) { + /* begin setMaxExtSemSizeTo: */ + GIV(maxExtSemTabSizeSet) = 1; + ioSetMaxExtSemTableSize(hdrMaxExtSemTabSize); + } + + /* begin getShortFromFile:swap: */ + w1 = 0; + sqImageFileRead((&w1), sizeof(unsigned short), 1, f); + GIV(the2ndUnknownShort) = (swapBytes + ? ((((usqInt)(w1)) >> 8) & 0xFF) | (((w1 & 0xFF) << 8)) + : w1); + + /* Set maxLiteralCountForCompile unless it has already been set on the command line. */ + if (maxLiteralCountForCompile < 0) { + maxLiteralCountForCompile = (GIV(the2ndUnknownShort) + ? GIV(the2ndUnknownShort) + : MaxLiteralCountForCompile); + } + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + firstSegSize = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin firstSegmentSize: */ + GIV(firstSegmentSize) = firstSegSize; + + /* compare memory requirements with availability */ + allocationReserve = interpreterAllocationReserveBytes(); + minimumMemory = ((GIV(cogCodeSize) + dataSize) + GIV(edenBytes)) + allocationReserve; + + /* no need to include the stackZone; this is alloca'ed + Compute how much space is needed for the initial heap allocation. + no need to include the stackZone; this is alloca'ed. + no need to include the JIT code zone size; this is allocated separately. */ + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + freeOldSpaceInImage = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin initialHeadroom:givenFreeOldSpaceInImage: */ + headroomSqInt = (extraVMMemory + ? extraVMMemory + : (!(GIV(growHeadroom)) + ? 0x1000000 + : GIV(growHeadroom))); + if (freeOldSpaceInImage >= headroomSqInt) { + headroom = 0; + goto l1; + } + if (freeOldSpaceInImage >= ((headroomSqInt * 7) / 8)) { + headroom = headroomSqInt / 8; + goto l1; + } + if (freeOldSpaceInImage >= ((headroomSqInt * 3) / 4)) { + headroom = headroomSqInt / 4; + goto l1; + } + if (freeOldSpaceInImage >= ((headroomSqInt * 5) / 8)) { + headroom = (headroomSqInt * 3) / 8; + goto l1; + } + if (freeOldSpaceInImage >= (headroomSqInt / 2)) { + headroom = headroomSqInt / 2; + goto l1; + } + headroom = headroomSqInt; + /* end initialHeadroom:givenFreeOldSpaceInImage: */ +l1: + heapSizeUsqInt = ((dataSize + headroom) + GIV(edenBytes)) + ((headroom > allocationReserve + ? 0 + : allocationReserve)); + + /* begin roundUpHeapSize: */ + bit = (((highBit(heapSizeUsqInt)) - 1) * 3) / 4; + heapSize = (((heapSizeUsqInt & ((1ULL << bit) - 1)) != 0) + ? (((heapSizeUsqInt | ((1ULL << bit) - 1)) - ((1ULL << bit) - 1))) + (1ULL << bit) + : heapSizeUsqInt); + + /* allocateJITMemory will assign the actual size allocated, which is rounded up to a page boundary. */ + cogCodeBase = ((sqInt)(allocateJITMemory((&GIV(cogCodeSize))))); + + /* allocate a contiguous block of memory for the Squeak heap and ancilliary data structures */ + if ((mem = ((usqInt)(allocateMemoryMinimumImageFileHeaderSize(heapSize, minimumMemory, f, headerSize))))) { + /* cannot clash with the variable memory still in use in NewCoObjectMemory and superclasses */ + setHeapBasememoryLimitendOfMemory((heapBase = mem), mem + heapSize, mem + dataSize); + } + else { + insufficientMemoryAvailableError(); + } + + /* position file after the header */ + sqImageFileSeek(f, headerStart + headerSize); + + /* read in the image in bulk, then swap the bytes if necessary */ + bytesRead = readHeapFromImageFiledataBytes(f, dataSize); + if (bytesRead != dataSize) { + unableToReadImageError(); + } + ensureImageFormatIsUpToDate(swapBytes); + + /* compute difference between old and new memory base addresses */ + bytesToShift = GIV(oldSpaceStart) - GIV(oldImageBaseAddress); + + /* begin initializeInterpreter: */ + interpreterProxy = sqGetInterpreterProxy(); + (&interpreterProxy); + initializeObjectMemory(bytesToShift); + + /* begin checkAssumedCompactClasses */ + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassArrayCompactIndex >= 1) && (ClassArrayCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassArrayCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("Array"); + } + + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassLargeNegativeInteger) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassLargeNegativeIntegerCompactIndex >= 1) && (ClassLargeNegativeIntegerCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassLargeNegativeIntegerCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("LargeNegativeInteger"); + } + + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassLargePositiveInteger) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassLargePositiveIntegerCompactIndex >= 1) && (ClassLargePositiveIntegerCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassLargePositiveIntegerCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("LargePositiveInteger"); + } + + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassFloat) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassFloatCompactIndex >= 1) && (ClassFloatCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassFloatCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("Float"); + } + + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassBlockClosure) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassBlockClosureCompactIndex >= 1) && (ClassBlockClosureCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassBlockClosureCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("BlockClosure"); + } + + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassMethodContext) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassMethodContextCompactIndex >= 1) && (ClassMethodContextCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassMethodContextCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("MethodContext"); + } + objOop = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteArray) << (shiftForWord())))))); + + /* begin compactIndexOfClass: */ + assert((rawHashBitsOf(objOop)) != 0); + GIV(classByteArrayCompactIndex) = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()); + + /* begin initializeExtraClassInstVarIndices */ + classArrayObj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))); + classArrayClass = fetchClassOfNonImm(classArrayObj); + + /* begin numSlotsOf: */ + assert((classIndexOf(classArrayClass)) > (isForwardedObjectClassIndexPun())); + + /* determine actual Metaclass instSize */ + GIV(metaclassNumSlots) = (((numSlots = byteAt((void *)(classArrayClass + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classArrayClass - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + + /* default */ + GIV(thisClassIndex) = 5; + /* begin numSlotsOf: */ + assert((classIndexOf(classArrayClass)) > (isForwardedObjectClassIndexPun())); + if (((numSlots = byteAt((void *)(classArrayClass + (numSlotsFieldByteOffset()))))) == (numSlotsMask())) { + toDoLimit = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classArrayClass - BaseHeaderSize)))) << 8)))))) >> 8; + } + else { + toDoLimit = numSlots; + } + for (i = (InstanceSpecificationIndex + 1); i <= toDoLimit; i += 1) { + if ((longAt((void *)((classArrayClass + BaseHeaderSize) + ((((usqInt)((i - 1)) << (shiftForWord()))))))) == classArrayObj) { + GIV(thisClassIndex) = i - 1; + } + } + + /* default */ + GIV(classNameIndex) = 6; + /* begin numSlotsOf: */ + assert((classIndexOf(classArrayObj)) > (isForwardedObjectClassIndexPun())); + if (((numSlots = byteAt((void *)(classArrayObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask())) { + toDoLimit1 = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classArrayObj - BaseHeaderSize)))) << 8)))))) >> 8; + } + else { + toDoLimit1 = numSlots; + } + for (i = (InstanceSpecificationIndex + 1); i <= toDoLimit1; i += 1) { + oop = longAt((void *)((classArrayObj + BaseHeaderSize) + ((((usqInt)((i - 1)) << (shiftForWord())))))); + if (objectequalsString(oop, "Array")) { + GIV(classNameIndex) = i - 1; + } + } + GIV(method) = (GIV(newMethod) = GIV(nilObj)); + GIV(methodDictLinearSearchLimit) = 8; + + /* begin initialCleanup */ + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + + /* begin flushExternalPrimitiveTable */ + memset(externalPrimitiveTable, 0, MaxExternalPrimitiveTableSize * (sizeof(externalPrimitiveTable[0]))); + GIV(externalPrimitiveTableFirstFreeIndex) = 0; + + /* cmd-. as used for Mac but no other OS */ + GIV(interruptKeycode) = ((0x800)) + (((sqInt)'.')); + while (GIV(globalSessionID) == 0) { + GIV(globalSessionID) = ((time(NULL)) + (ioMSecs())) & 0x7FFFFFFF; + } + GIV(metaAccessorDepth) = -2; + sHEAFn = ioLoadFunctionFrom("secHasEnvironmentAccess", "SecurityPlugin"); + initializeCodeZoneFromupTo(cogCodeBase, cogCodeBase + GIV(cogCodeSize)); + return dataSize; +} + + +/* This is a little elaborate. The primTraceLog is only useful if it is not + full of noise. + To reduce noise when debugging a specific plugin we allow a plugin name to + be specified and will only generate the primTraceLog code for primitives + in that plugin. */ + + /* CoInterpreter>>#recordFastCCallPrimTraceForMethod: */ +sqInt +recordFastCCallPrimTraceForMethod(sqInt aMethodObj) +{ + return (recordFastCCallPrimTrace()) + && ((primTracePluginName + ? methodHasPrimitiveInPrimTracePlugin(aMethodObj) + : 1)); +} + + +/* This is a little elaborate. The primTraceLog is only useful if it is not + full of noise. + To reduce noise when debugging a specific plugin we allow a plugin name to + be specified and will only generate the primTraceLog code for primitives + in that plugin. */ + + /* CoInterpreter>>#recordPrimTraceForMethod: */ +sqInt +recordPrimTraceForMethod(sqInt aMethodObj) +{ + return (recordPrimTrace()) + && ((primTracePluginName + ? methodHasPrimitiveInPrimTracePlugin(aMethodObj) + : 1)); +} + + +/* noop in the StackInterpreter */ + + /* CoInterpreter>>#reloadPrimitiveCalloutPointer */ +static NeverInline void +reloadPrimitiveCalloutPointer(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + char *moduleName; + + primitiveCalloutPointer = ioLoadFunctionFrom("primitiveCallout", "SqueakFFIPrims"); + + /* Find out whether SqueakFFIPrims is internal or external */ + GIV(primCalloutIsExternal) = 1; + index = 1; + do { + if ((moduleName = ioListBuiltinModule(index))) { + if (!(strcmp(moduleName, "SqueakFFIPrims"))) { + GIV(primCalloutIsExternal) = 0; + return; + } + } + else { + return; + } + index += 1; + } while(1); +} + + +/* Report the stack page size and minimum unused headroom to stdout. */ + + /* CoInterpreter>>#reportMinimumUnusedHeadroom */ +void +reportMinimumUnusedHeadroom(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fprintf(GIV(transcript), + "stack page bytes %" PRIdSQINT " available headroom %" PRIdSQINT " minimum unused headroom %" PRIdSQINT "\n", + stackPageByteSize(), + ((stackPageByteSize()) - ((((stackPageFrameBytes()) < (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))) ? (stackPageFrameBytes()) : (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))))) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord), + minimumUnusedHeadroom()); +} + + +/* Report the stack page size and minimum unused headroom to a stream. */ + + /* CoInterpreter>>#reportMinimumUnusedHeadroomOn: */ +void +reportMinimumUnusedHeadroomOn(FILE *aStdioStream) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + FILE *savedTranscript; + + /* begin withRedirectedOutputTo:do: */ + savedTranscript = GIV(transcript); + GIV(transcript) = (aStdioStream + ? aStdioStream + : stdout); + + /* begin reportMinimumUnusedHeadroom */ + fprintf(GIV(transcript), + "stack page bytes %" PRIdSQINT " available headroom %" PRIdSQINT " minimum unused headroom %" PRIdSQINT "\n", + stackPageByteSize(), + ((stackPageByteSize()) - ((((stackPageFrameBytes()) < (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))) ? (stackPageFrameBytes()) : (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))))) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord), + minimumUnusedHeadroom()); + GIV(transcript) = savedTranscript; +} + + +/* Return to the current frame, either by entering machine code, or + longjmp-ing back to the + interpreter or simply returning, depending on where we are. To know + whether to return or + enter machine code we have to know from whence we came. We could have come + from the interpreter, either directly or via a machine code primitive. We + could have come from + machine code. The instructionPointer tells us where from. If it is above + startOfMemory we're + in the interpreter. If it is below, then we are in machine-code unless it + is ceReturnToInterpreterPC, + in which case we're in a machine-code primitive called from the + interpreter. */ + + /* CoInterpreter>>#returnToExecutive:postContextSwitch: */ +static NoDbgRegParms sqInt +returnToExecutivepostContextSwitch(sqInt inInterpreter, sqInt switchedContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + CogBlockMethod *cogMethod; + sqInt fullyInInterpreter; + sqInt header; + sqInt methodHeader; + sqInt retValue; + + assertCStackWellAligned(); + if ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())) { + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + + /* If returning after a context switch then a result may have to be popped from the stack. + If the process is suspended at a send then the result of the primitive in which the + process was suspended is still on the stack and must be popped into ReceiverResultReg. + If not, nothing should be popped and ReceiverResultReg gets the receiver. */ + if (switchedContext) { + /* begin mframeCogMethod: */ + cogMethod = ((CogBlockMethod *) ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodMask)); + assert((GIV(instructionPointer) > (minCogMethodAddress())) + && (GIV(instructionPointer) < (maxCogMethodAddress()))); + if ((GIV(instructionPointer) != ((((sqInt)cogMethod)) + ((cogMethod->stackCheckOffset)))) + && (isSendReturnPC(GIV(instructionPointer)))) { + assert(addressCouldBeOop(stackTop())); + + /* begin popStack */ + retValue = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + } + else { + retValue = longAt(GIV(framePointer) + FoxMFReceiver); + } + } + else { + retValue = longAt(GIV(framePointer) + FoxMFReceiver); + } + assert(isPCWithinMethodZone(GIV(instructionPointer))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),retValue); + ceEnterCogCodePopReceiverReg(); + } + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + fullyInInterpreter = inInterpreter; + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + fullyInInterpreter = 0; + } + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 1, __LINE__); + if (!fullyInInterpreter) { + ceInvokeInterpret(); + } + return null; +} + + +/* We have made a context switch, either when interpreting or from machine + code. Effectively return to the current frame, either by entering machine + code, or + longjmp-ing back to the interpreter or simply returning, depending on + where we are. */ + + /* CoInterpreter>>#return:toExecutive: */ +static NoDbgRegParms sqInt +returntoExecutive(sqInt returnValue, sqInt inInterpreter) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + sqInt header; + sqInt methodHeader; + char *sp; + + assertCStackWellAligned(); + if ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())) { + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + assert(isPCWithinMethodZone(GIV(instructionPointer))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + ceEnterCogCodePopReceiverReg(); + } + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 1, __LINE__); + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + if (inInterpreter) { + return null; + } + ceInvokeInterpret(); + + /* NOTREACHED */ + return null; +} + + +/* Send the calllback message to Alien class with the supplied arg(s). Use + either the 1 arg + invokeCallbackContext: or the 4 arg invokeCallback:stack:registers:jmpbuf: + message, depending on what selector is installed in the + specialObjectsArray. Note that if invoking the + legacy invokeCallback:stack:registers:jmpbuf: we pass the + vmCallbackContext as the jmpbuf + argument (see reestablishContextPriorToCallback:). The arguments are raw C + addresses and + are converted to integer objects on the way. sendInvokeCallbackContext: & + returnAs:ThroughCallback:Context: along with ownVM: and disownVM: conspire + to save and + restore newMethod, argumentCount and primitiveFunctionPointer around a + callback. The VM depends on argumentCount being correct to cut-back the + correct number of + arguments on primitive return. Since this is an implicit send we need to + log it explicitly. + The return side is done via a primitive so that gets logged normally. */ +/* Override check stack alignment. */ + + /* CoInterpreter>>#sendInvokeCallbackContext: */ +sqInt +sendInvokeCallbackContext(VMCallbackContext *vmCallbackContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTag; + CogMethod *cogMethod; + usqInt i; + usqInt initialIP; + sqInt methodHeader; + usqInt numArgs; + usqInt numTemps; + sqInt object; + sqInt rcvr; + sqInt thunkp; + + assertCStackWellAligned(); + if (recordPrimTrace()) { + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorInvokeCallback) << (shiftForWord()))))))); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + thunkp = ((sqInt) ((vmCallbackContext->thunkp))); + + /* begin recordInvokeCallback: */ + /* begin recordTrace:source:thing:extra: */ + if (TraceLog) { + GIV(traceLog)[GIV(traceLogIndex)] = ((((((usqInt)(TraceVMCallback) << TraceTypeShift))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = (((sqInt)thunkp)); + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + + /* begin fetchClassTagOfNonImm: */ + classTag = (longAt((void *)(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord()))))))))) & (classIndexMask()); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorInvokeCallback) << (shiftForWord())))))); + if (!(lookupInMethodCacheSelclassTag(GIV(messageSelector), classTag))) { + if (lookupOrdinaryNoMNUEtcInClass(classForClassTag(classTag))) { + return 0; + } + } + assert(((debugCallbackInvokes += 1)) > 0); + + /* self assert: debugCallbackInvokes < 3802. */ + + /* begin saveCStackStateForCallbackContext: */ + (vmCallbackContext->savedCStackPointer = ((void *)GIV(CStackPointer))); + (vmCallbackContext->savedCFramePointer = ((void *)GIV(CFramePointer))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord()))))))); + if ((argumentCountOf(GIV(newMethod))) == 4) { + object = positive64BitIntegerFor(((usqInt)((vmCallbackContext->thunkp)))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + object = positive64BitIntegerFor(((usqInt)((vmCallbackContext->stackp)))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + object = positive64BitIntegerFor(((usqInt)((vmCallbackContext->intregargsp)))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + object = positive64BitIntegerFor(((usqInt)vmCallbackContext)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + + /* begin justActivateNewMethod: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + cogMethod = null; + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + methodHeader = (cogMethod->methodHeader); + } + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeader)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(addressCouldBeOop(rcvr)); + assert(!(isOopForwarded(rcvr))); + if (cogMethod) { + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + } + GIV(instructionPointer) = ceReturnToInterpreterPC(); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + initialIP = ((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize; + if (cogMethod) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)cogMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + GIV(instructionPointer) = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + GIV(instructionPointer) = initialIP - 1; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + if (!cogMethod) { + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + } + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeader); + } + } + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + + /* begin checkForStackOverflow */ + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (GIV(stackPointer) < ((GIV(stackPage)->realStackLimit))) { + handleStackOverflow(); + } + assert((frameReceiver(GIV(framePointer))) == (splObj(ClassAlien))); + + /* begin enterSmalltalkExecutiveFromCallback */ + enterSmalltalkExecutive(); + + /* not reached */ + return 1; +} + + /* CoInterpreter>>#shortPrintFrame: */ +static NoDbgRegParms sqInt +shortPrintFrame(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + sqInt mthd; + sqInt rcvr; + + if (!(/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "invalid frame pointer"); + cr(); + return null; + } + rcvr = /* frameReceiver: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? longAt(theFP + FoxMFReceiver) + : longAt(theFP + FoxIFReceiver)); + mthd = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + printHexPtrnp(theFP); + + /* begin space */ + printChar(' '); + printChar(((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? 'M' + : 'I')); + + /* begin space */ + printChar(' '); + printActivationNameForreceiverisBlockfirstTemporary(mthd, rcvr, /* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0), /* temporary:in: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (0 < ((frameNumArgs = ((mframeCogMethod(theFP))->cmNumArgs))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgs) * BytesPerWord)) + : longAt(((theFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs) * BytesPerWord))) + : /* itemporary:in: */ + (0 < ((frameNumArgsUsqInt = byteAt((theFP + FoxIFrameFlags) + 1))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt) * BytesPerWord)) + : longAt(((theFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt) * BytesPerWord))))); + + /* begin space */ + printChar(' '); + shortPrintOop(rcvr); + return 0; +} + + +/* Answer a full 32 bit integer object for the given integer value. + N.B. Returning in each arm separately enables Slang inlining. + /Don't/ return the ifTrue:ifFalse: unless Slang inlining of conditionals + is fixed. */ + + /* CoInterpreter>>#signed32BitIntegerFor: */ +sqInt +signed32BitIntegerFor(sqInt integerValue) +{ + return (((usqInt)(((int) integerValue)) << 3) | 1); +} + + +/* Convert the given object into an integer value. + The object may be either a SmallInteger or a four-byte LargeInteger. */ + + /* CoInterpreter>>#signed32BitValueOf: */ +int +signed32BitValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt value64; + + if ((((oop) & 7) == 1)) { + value64 = (oop >> 3); + if ((((int) value64)) != value64) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + value64 = 0; + } + return value64; + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; +} + + +/* Answer a Large Integer object for the given integer value. N.B. will *not* + cause a GC. */ + + /* CoInterpreter>>#signed64BitIntegerFor: */ +sqInt +signed64BitIntegerFor(sqLong integerValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt largeClass; + usqLong magnitude; + usqInt newLargeInteger; + usqInt newObj; + usqInt numBytes; + sqInt numSlots; + sqInt objFormat; + sqInt sz; + + if (integerValue < 0) { + if (integerValue >= (MinSmallInteger)) { + return (((usqInt)(((sqInt)integerValue)) << 3) | 1); + } + largeClass = ClassLargeNegativeIntegerCompactIndex; + magnitude = 0 - (((usqLong) integerValue)); + } + else { + if (integerValue <= (MaxSmallInteger)) { + return (((usqInt)(((sqInt)integerValue)) << 3) | 1); + } + largeClass = ClassLargePositiveIntegerCompactIndex; + magnitude = integerValue; + } + sz = 8; + objFormat = (firstByteFormat()) + ((8 - sz) & (BytesPerWord - 1)); + numSlots = (sz + 3) / BytesPerOop; + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((numSlots >= 0) + && ((largeClass != 0) + && ((knownClassAtIndex(largeClass)) != GIV(nilObj)))); + assert(((objFormat < (firstByteFormat()) + ? objFormat + : objFormat & (byteFormatMask()))) == (instSpecOfClass(knownClassAtIndex(largeClass)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newLargeInteger = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + largeClass); + GIV(freeStart) += numBytes; + newLargeInteger = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + if (sz > 4) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((newLargeInteger + BaseHeaderSize)),SQ_SWAP_8_BYTES_IF_BIGENDIAN(magnitude)); + } + else { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((newLargeInteger + BaseHeaderSize)),SQ_SWAP_4_BYTES_IF_BIGENDIAN(magnitude)); + } + return newLargeInteger; +} + + +/* Convert the given object into an integer value. + The object may be either a positive SmallInteger or a eight-byte + LargeInteger. + */ + + /* CoInterpreter>>#signed64BitValueOf: */ +sqLong +signed64BitValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + sqInt fmt; + usqLong magnitude; + sqInt negative; + usqInt numSlots; + int ok; + sqInt sz; + + if ((((oop) & 7) == 1)) { + return ((sqLong) ((oop >> 3))); + } + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (ok) { + negative = 0; + } + else { + negative = 1; + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargeNegativeIntegerCompactIndex == ccIndex; + if (!ok) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + sz = ((((/* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (sz > 4) { + magnitude = SQ_SWAP_8_BYTES_IF_BIGENDIAN((long64At((void *)((oop + BaseHeaderSize))))); + if ((sz > (sizeof(sqLong))) + || ((negative + ? magnitude > 0x8000000000000000ULL + : magnitude >= 0x8000000000000000ULL))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + } + else { + magnitude = ((unsigned int) (SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((oop + BaseHeaderSize))))))); + } + return (negative + ? 0 - magnitude + : magnitude); +} + + +/* */ + + /* CoInterpreter>>#specialSelectorNumArgs: */ +sqInt +specialSelectorNumArgs(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((fetchPointerofObject((index * 2) + 1, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))))) >> 3); +} + + /* CoInterpreter>>#stackLimitAddress */ +usqInt +stackLimitAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(stackLimit)))); +} + + +/* Answer the amount of slots needed to fit a new frame at the point the + stack limit is checked. A frame looks like this at the point the stack + limit is checked: + stacked receiver/closure + arg0 + ... + argN + caller's method ip/base frame's sender context + fp-> saved fp + method + context (uninitialized?) + method header fields (interpreter only) + saved method ip (uninitialized?; interpreter only) + receiver + first temp + ... + sp-> Nth temp + So the amount of headroom is + the maximum number of arguments + 1 (for stacked receiver and arguments) + + the frame size + + the max number of temps. + Since a method's number of temps includes its arguments the actual offset + is: */ + + /* CoInterpreter>>#stackLimitOffset */ +static sqInt +stackLimitOffset(void) +{ + return ((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord; +} + + +/* Return a minimum amount of headroom for each stack page (in bytes). + In the interpreter we don't actually need any headroom. In a JIT the stack + has to have room for interrupt handlers which will run on the stack. + Defer to the platform for this one. */ + + /* CoInterpreter>>#stackPageHeadroom */ +static sqInt +stackPageHeadroom(void) +{ + return osCogStackPageHeadroom(); +} + + /* CoInterpreter>>#stackPointerAddress */ +usqInt +stackPointerAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(stackPointer)))); +} + + +/* Zero-relative version of CompiledMethod>>startpc. */ + + /* CoInterpreter>>#startPCOfMethodHeader: */ +usqInt +startPCOfMethodHeader(sqInt aCompiledMethodHeader) +{ + return (((/* begin literalCountOfMethodHeader: */ + assert((((aCompiledMethodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((aCompiledMethodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop; +} + + +/* Answer the startPC of lit if it is a (clean) block in aMethodObj, + otherwise answer nil. + */ + + /* CoInterpreter>>#startPCOrNilOfLiteral:in: */ +sqInt +startPCOrNilOfLiteralin(sqInt lit, sqInt aMethodObj) +{ + usqInt numSlots; + sqInt oop; + sqInt outerContext; + + if (!(((!(lit & (tagMask())))) + && ((((byteAt((void *)(lit + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat())) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(lit)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(lit + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(lit - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= ClosureFirstCopiedValueIndex)))) { + return null; + } + outerContext = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + if (!(/* isContext: */ + ((!(outerContext & (tagMask())))) + && (((longAt((void *)(outerContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + return null; + } + if (aMethodObj != (longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))))) { + return null; + } + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + return (oop >> 3); +} + + +/* Handle the cannot return response for a base frame return to an invalid + context. Build a new base frame for the context in the cannot resume state + ready for the + send of cannotReturn:. + + Since we have returned from the base frame of the page the context is + effectively widowed. + But its sender needs to be contextToReturnTo, and its pc needs to be the + HasBeenReturnedFromMCPC marker. So bereave it (as a side-effect of + isWidowedContext:), assign contextToReturnTo to + sender, and rebuild its frame, which will have the ceCannotResumePC as its + pc. Finally push + returnValue and set instructionPointer to ceCannotResumePC in preparation + for the send. */ + + /* CoInterpreter>>#tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFrom:to:returnValue: */ +static NoDbgRegParms void +tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFromtoreturnValue(sqInt contextToReturnFrom, sqInt contextToReturnTo, sqInt returnValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *newPage; + + assert((GIV(stackPage)) + && (isFree(GIV(stackPage)))); + isWidowedContext(contextToReturnFrom); + assert(!(isMarriedOrWidowedContext(contextToReturnFrom))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SenderIndex, contextToReturnFrom, contextToReturnTo)); + assert(isNonImmediate(contextToReturnFrom)); + if (oopisGreaterThanOrEqualTo(contextToReturnFrom, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(contextToReturnTo & (tagMask())))) + && (oopisLessThan(contextToReturnTo, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(contextToReturnFrom + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(contextToReturnFrom); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((contextToReturnFrom + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),contextToReturnTo); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(InstructionPointerIndex, contextToReturnFrom, HasBeenReturnedFromMCPCOop)); + assert(isNonImmediate(contextToReturnFrom)); + if (oopisGreaterThanOrEqualTo(contextToReturnFrom, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(HasBeenReturnedFromMCPCOop & (tagMask())))) + && (oopisLessThan(HasBeenReturnedFromMCPCOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(contextToReturnFrom + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(contextToReturnFrom); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((contextToReturnFrom + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),HasBeenReturnedFromMCPCOop); + + /* void the instructionPointer to stop it being incorrectly updated in a code + compaction in makeBaseFrameFor:. */ + GIV(instructionPointer) = 0; + newPage = makeBaseFrameFor(contextToReturnFrom); + assert(GIV(stackPage) == newPage); + + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (newPage->headSP); + GIV(framePointer) = (newPage->headFP); + assert((stackTop()) == (ceCannotResumePC())); + + /* overwrite the ceSendCannotResumePC on the stack. If ever re-executed + the returnValue will be taken from top-of-stack by ceCannotResume. */ + + /* stackTopPut: */ + longAtput(GIV(stackPointer),returnValue); + + /* Assign it to instructionPointer as externalCannotReturn:from: pushes it. */ + GIV(instructionPointer) = ceCannotResumePC(); +} + + +/* See StackInterpreter class>>initializeFrameIndices */ + + /* CoInterpreter>>#temporary:in: */ +static NoDbgRegParms sqInt +temporaryin(sqInt offset, char *theFP) +{ + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (offset < ((frameNumArgs = ((mframeCogMethod(theFP))->cmNumArgs))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgs - offset) * BytesPerWord)) + : longAt(((theFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs - offset) * BytesPerWord))) + : /* itemporary:in: */ + (offset < ((frameNumArgsUsqInt = byteAt((theFP + FoxIFrameFlags) + 1))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt - offset) * BytesPerWord)) + : longAt(((theFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt - offset) * BytesPerWord)))); +} + + /* CoInterpreter>>#temporary:in:put: */ +static NoDbgRegParms sqInt +temporaryinput(sqInt offset, char *theFP, sqInt valueOop) +{ + usqInt frameNumArgs; + unsigned char frameNumArgs1; + + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? longAtput((offset < ((frameNumArgs1 = ((mframeCogMethod(theFP))->cmNumArgs))) + ? (theFP + FoxCallerSavedIP) + ((frameNumArgs1 - offset) * BytesPerWord) + : ((theFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs1 - offset) * BytesPerWord)),valueOop) + : /* itemporary:in:put: */ + (offset < ((frameNumArgs = byteAt((theFP + FoxIFrameFlags) + 1))) + ? longAtput((theFP + FoxCallerSavedIP) + ((frameNumArgs - offset) * BytesPerWord),valueOop) + : longAtput(((theFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - offset) * BytesPerWord),valueOop))); +} + + +/* Record a process to be awoken on the next interpreter cycle. + Note: It is currently a fatal VM error if there is no runnable process + (indicated by newProcOrNil being nil), but the right thing to do is to + enter a wait state until the next event/expired delay/available input, + etc. Reimplement to cope with possible code compaction in + makeBaseFrameFor:. */ + + /* CoInterpreter>>#transferTo:from: */ +static NoDbgRegParms sqInt +transferTofrom(sqInt newProcOrNil, sqInt sourceCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt aMethodObj; + sqInt header; + usqInt ip; + StackPage *lastUsedPage; + sqInt methodHeader; + sqInt newContext; + StackPage *newPage; + sqInt oldProc; + char *ptr; + sqInt sched; + sqInt senderOop; + char *theFrame; + StackPage *thePage; + char *toDoLimit; + char *toDoLimit1; + sqInt top; + + GIV(statProcessSwitch) += 1; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + ip = (GIV(instructionPointer) >= (startOfMemory()) + ? GIV(instructionPointer) + 1 + : GIV(instructionPointer)); + + /* begin assertValidExternalFrameWithIP: */ + assertValidExecutionPointersimbarline(ip, GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + toDoLimit1 = /* frameReceiverLocation: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? GIV(framePointer) + FoxMFReceiver + : GIV(framePointer) + FoxIFReceiver); + for (ptr = (GIV(stackPointer) + BytesPerWord); ptr <= toDoLimit1; ptr += BytesPerWord) { + assert(addressCouldBeOop(longAt(ptr))); + } + + /* skip pushed instructionPointer */ + assert(isOopCompiledMethod(frameMethodObject(GIV(framePointer)))); + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(!((isForwarded(frameContext(GIV(framePointer)))))); + } + toDoLimit = GIV(framePointer) + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(GIV(framePointer)))->cmNumArgs) + : byteAt((GIV(framePointer) + FoxIFrameFlags) + 1)))) << (shiftForWord()))))); + for (ptr = ((GIV(framePointer) + FoxCallerSavedIP) + BytesPerWord); ptr <= toDoLimit; ptr += BytesPerWord) { + assert(addressCouldBeOop(longAt(ptr))); + } + + /* ensureMethodIsCogged: in makeBaseFrameFor: in + externalSetStackPageAndPointersForSuspendedContextOfProcess: + below may do a code compaction. Nil instructionPointer to avoid it getting pushed twice. */ + GIV(instructionPointer) = 0; + sched = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + oldProc = longAt((void *)((sched + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin recordContextSwitchFrom:to:in: */ + if ((recordEventTrace()) + && (!primTracePluginName)) { + /* begin recordTrace:source:thing:extra: */ + if (TraceLog) { + GIV(traceLog)[GIV(traceLogIndex)] = ((((((usqInt)(TraceContextSwitch) << TraceTypeShift)))) + sourceCode); + GIV(traceLog)[GIV(traceLogIndex) + 1] = oldProc; + GIV(traceLog)[GIV(traceLogIndex) + 2] = newProcOrNil; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l1; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer) + BytesPerWord); + /* end ensureFrameIsMarried:SP: */ +l1: + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, oldProc, activeContext)); + assert(isNonImmediate(oldProc)); + if (oopisGreaterThanOrEqualTo(oldProc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeContext & (tagMask())))) + && (oopisLessThan(activeContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(oldProc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(oldProc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((oldProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),activeContext); + if (!newProcOrNil) { + error("scheduler could not find a runnable process"); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ActiveProcessIndex, sched, newProcOrNil)); + assert(isNonImmediate(sched)); + if (oopisGreaterThanOrEqualTo(sched, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(newProcOrNil & (tagMask())))) + && (oopisLessThan(newProcOrNil, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(sched + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(sched); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((sched + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord()))))),newProcOrNil); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newProcOrNil)) + && (!(isForwarded(newProcOrNil)))); + assert(validStorePointerUncheckedArgs(MyListIndex, newProcOrNil, GIV(nilObj))); + longAtput((void *)((newProcOrNil + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin externalSetStackPageAndPointersForSuspendedContextOfProcess: */ + newContext = longAt((void *)((newProcOrNil + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + assert(isContext(newContext)); + if (((((longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert(checkIsStillMarriedContextcurrentFP(newContext, GIV(framePointer))); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newProcOrNil)) + && (!(isForwarded(newProcOrNil)))); + assert(validStorePointerUncheckedArgs(SuspendedContextIndex, newProcOrNil, GIV(nilObj))); + longAtput((void *)((newProcOrNil + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),GIV(nilObj)); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(newContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + if (theFrame != ((thePage->headFP))) { + /* explicit assignment of suspendedContext can cause switch to interior frame. */ + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l3; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l3: + moveFramesInthroughtoPage(thePage, findFrameAboveinPage(theFrame, thePage), newPage); + + /* begin markStackPageLeastMostRecentlyUsed: */ + assert(newPage == ((GIV(mostRecentlyUsedPage)->nextPage))); + lastUsedPage = (newPage->nextPage); + while (((lastUsedPage->baseFP)) == 0) { + lastUsedPage = (lastUsedPage->nextPage); + } + if (((lastUsedPage->nextPage)) == newPage) { + goto l2; + } + (((newPage->prevPage))->nextPage = (newPage->nextPage)); + (((newPage->nextPage))->prevPage = (newPage->prevPage)); + (((lastUsedPage->prevPage))->nextPage = newPage); + (newPage->prevPage = (lastUsedPage->prevPage)); + (newPage->nextPage = lastUsedPage); + (lastUsedPage->prevPage = newPage); + assert(pageListIsWellFormed()); + /* end markStackPageLeastMostRecentlyUsed: */ +l2:; + } + assert(((thePage->headFP)) == theFrame); + } + else { + thePage = makeBaseFrameFor(newContext); + theFrame = (thePage->baseFP); + } + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + return 0; +} + + +/* Go through all frames in the stack zone and mark their methods + so that compaction does not free any methods that are in use. */ +/* If instructionPointer is referring to machine code, as it will be if a + primitive is in progress + (see isCodeCompactingPrimitiveIndex:) it must be updated if it is + referring to a moved + method. */ + + /* CoInterpreter>>#updateStackZoneReferencesToCompiledCodePreCompaction */ +void +updateStackZoneReferencesToCompiledCodePreCompaction(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt i; + CogMethod *primCogMethod; + usqInt theFlags; + char *theFP; + usqInt theIP; + char *theIPPtr; + CogMethod *theMethod; + sqInt theMethodField; + StackPage *thePage; + + if (GIV(instructionPointer)) { + if ((primCogMethod = cogMethodContaining(GIV(instructionPointer)))) { + GIV(instructionPointer) += (primCogMethod->objectHeader); + } + } + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + /* begin updateStackZoneReferencesToCompiledCodePreCompactionOnPage: */ + callerFP = ((char *) 0); + theIPPtr = (thePage->headSP); + theFP = (thePage->headFP); + while (1) { + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + theMethodField = longAt(theFP + FoxMethod); + theFlags = theMethodField & MFMethodFlagsMask; + theMethod = ((CogMethod *) (theMethodField - theFlags)); + if (((theMethod->cmType)) == CMBlock) { + /* cmHomeMethod */ + if ((((((CogBlockMethod *) (theMethodField - theFlags)))->cmType)) >= CMMethod) { + theMethod = ((CogMethod *) (((CogBlockMethod *) (theMethodField - theFlags)))); + } + else { + assert(CMBlock == ((((CogBlockMethod *) (theMethodField - theFlags)))->cmType)); + theMethod = ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (theMethodField - theFlags))))) - (((((CogBlockMethod *) (theMethodField - theFlags)))->homeOffset)))); + } + } + theIP = ((usqInt)(longAt(theIPPtr))); + if ((theIP != (ceCannotResumePC())) + && (asserta((theIP >= (((usqInt)theMethod))) + && (theIP < ((((usqInt)theMethod)) + ((theMethod->blockSize))))))) { + longAtput(theIPPtr,theIP + ((theMethod->objectHeader))); + } + longAtput(theFP + FoxMethod,theMethodField + ((theMethod->objectHeader))); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theIPPtr = theFP + FoxCallerSavedIP; + theFP = callerFP; + } + } + } +} + + +/* Update the frame's spouse context with the frame's current state except + for the + sender and instruction pointer, which are used to mark the context as + married, and the receiver and method, which are already set. */ + + /* CoInterpreter>>#updateStateOfSpouseContextForFrame:WithSP: */ +static NoDbgRegParms void +updateStateOfSpouseContextForFrameWithSP(char *theFP, char *theSP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *argsPointer; + sqInt fieldIndex; + sqInt i; + char *pointer; + usqInt tempIndex; + sqInt theContext; + sqInt valuePointer; + + assert(frameHasContext(theFP)); + theContext = longAt(theFP + FoxThisContext); + assert(isContext(theContext)); + assert(frameReceiver(theFP) == noFixupFollowFieldofObject(ReceiverIndex, theContext)); + assert(frameMethodObject(theFP) == noFixupFollowFieldofObject(MethodIndex, theContext)); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + tempIndex = ((mframeCogMethod(theFP))->cmNumArgs); + pointer = (theFP + FoxMFReceiver) - BytesPerWord; + } + else { + tempIndex = byteAt((theFP + FoxIFrameFlags) + 1); + pointer = (theFP + FoxIFReceiver) - BytesPerWord; + } + + /* update the arguments. this would appear not to be strictly necessary, but is for two reasons. + First, the fact that arguments are read-only is only as convention in the Smalltalk compiler; + other languages may choose to modify arguments. + Second, the Squeak runUntilErrorOrReturnFrom: nightmare pops the stack top, which may, in + certain circumstances, be the last argument, and hence the last argument may not have been + stored into the context. */ + argsPointer = theFP + ((FoxCallerSavedIP + BytesPerWord) + ((tempIndex << (shiftForWord())))); + for (i = 1; i <= tempIndex; i += 1) { + argsPointer -= BytesPerWord; + assert(addressCouldBeOop(longAt(argsPointer))); + fieldIndex = ReceiverIndex + i; + valuePointer = longAt(argsPointer); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, theContext, valuePointer)); + assert(isNonImmediate(theContext)); + if (oopisGreaterThanOrEqualTo(theContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(theContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(theContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); + } + + /* now update the non-argument stack contents. */ + while (pointer >= theSP) { + assert(addressCouldBeOop(longAt(pointer))); + tempIndex += 1; + fieldIndex = ReceiverIndex + tempIndex; + valuePointer = longAt(pointer); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, theContext, valuePointer)); + assert(isNonImmediate(theContext)); + if (oopisGreaterThanOrEqualTo(theContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(theContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(theContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); + pointer -= BytesPerWord; + } + assert((ReceiverIndex + tempIndex) < (lengthOf(theContext))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(StackPointerIndex, theContext, ((tempIndex << 3) | 1))); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord()))))),((tempIndex << 3) | 1)); +} + + +/* Note that we accept anInstrPointer pointing to a callPrimitiveBytecode + at the start of a method that contains a primitive. This because methods + like Context(Part)>>reset have to be updated to skip the callPrimtiive + bytecode otherwise. */ +/* -1 for pre-increment in fetchNextBytecode */ + + /* CoInterpreter>>#validInstructionPointer:inMethod:framePointer: */ +sqInt +validInstructionPointerinMethodframePointer(usqInt instrPointer, usqInt aMethod, char *fp) +{ + CogMethod *cogMethod; + sqInt header; + usqInt theInstrPointer; + + if (instrPointer == (ceCannotResumePC())) { + return (((usqInt)(longAt(fp + FoxMethod)))) < (startOfMemory()); + } + if (instrPointer == (ceReturnToInterpreterPC())) { + if ((((usqInt)(longAt(fp + FoxMethod)))) < (startOfMemory())) { + return 0; + } + theInstrPointer = ((usqInt)(longAt(fp + FoxIFSavedIP))); + } + else { + theInstrPointer = instrPointer; + header = longAt((void *)((aMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + if (((/* begin isCogMethodReference: */ + assert(((((header) & 7) == 1)) + || (((((usqInt)header)) < (startOfMemory())) + && ((((usqInt)header)) >= (minCogMethodAddress())))), + /* isNonIntegerObject: */ + (!(header & (smallIntegerTag()))))) + && (theInstrPointer < (startOfMemory()))) { + cogMethod = ((CogMethod *) header); + return (theInstrPointer >= (header + (sizeof(CogMethod)))) + && (theInstrPointer < (header + ((cogMethod->blockSize)))); + } + } + return (theInstrPointer >= (((aMethod + (lastPointerOf(aMethod))) + BytesPerOop) - 1)) + && (theInstrPointer < (((aMethod + (numBytesOfBytes(aMethod))) + BaseHeaderSize) - 1)); +} + + +/* Check that the base frame in the stack page has a valid sender and saved + context. + */ + + /* CoInterpreter>>#validStackPageBaseFrame: */ +static NoDbgRegParms sqInt +validStackPageBaseFrame(StackPage *aPage) +{ + sqInt savedThisContext; + sqInt senderContextOrNil; + + senderContextOrNil = longAt((aPage->baseAddress)); + savedThisContext = longAt(((aPage->baseAddress)) - BytesPerWord); + if (!(asserta(((((aPage->baseFP)) + (frameStackedReceiverOffset((aPage->baseFP)))) + (2 * BytesPerWord)) == ((aPage->baseAddress))))) { + return 0; + } + if (!(asserta(addressCouldBeObj(senderContextOrNil)))) { + return 0; + } + if (!(asserta(addressCouldBeObj(savedThisContext)))) { + return 0; + } + if (!(asserta((senderContextOrNil == (nilObject())) + || (isContext(senderContextOrNil))))) { + return 0; + } + if (!(asserta(isContext(savedThisContext)))) { + return 0; + } + if (!(asserta((frameCallerContext((aPage->baseFP))) == senderContextOrNil))) { + return 0; + } + if (!(asserta((frameContext((aPage->baseFP))) == savedThisContext))) { + return 0; + } + return 1; +} + + +/* Make sure that all VM state that affects the heap contents is voided so + that the heap is + ready to be snapshotted. If flushExtPrims is true, flush references to + external primitives in methods. Answer the activeContext that should be + stored in the snapshot. */ +/* Make sure that all VM state that affects the heap contents is voided so + that the heap is + ready to be snapshotted. If flushExtPrims is true, flush references to + external primitives in methods. Answer the activeContext that should be + stored in the snapshot. */ + + /* CoInterpreter>>#voidVMStateForSnapshotFlushingExternalPrimitivesIf: */ +static NoDbgRegParms sqInt +voidVMStateForSnapshotFlushingExternalPrimitivesIf(sqInt flushExtPrims) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt address; + sqInt classIndex; + sqInt fmt; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* in case of code compactions. */ + GIV(instructionPointer) = 0; + activeContext = divorceAllFrames(); + + /* begin bereaveAllMarriedContextsForSnapshotFlushingExternalPrimitivesIf: */ + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + fmt = (byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask()); + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + makeContextSnapshotSafe(objSqInt); + } + + /* Clean out external functions from compiled methods */ + if (flushExtPrims + && (fmt >= (firstCompiledMethodFormat()))) { + flushExternalPrimitiveOf(objSqInt); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } + voidCogCompiledCode(); + return activeContext; +} + + +/* useful for VM debugging */ + + /* CoInterpreter>>#whereIs: */ +char * +whereIs(sqInt anOop) +{ + char *somewhere; + char *where; + + if ((somewhere = whereIsMaybeCodeThing(anOop))) { + return somewhere; + } + if ((where = whereIsMaybeHeapThing(anOop))) { + return where; + } + if ((where = whereIsMaybeStackThing(anOop))) { + return where; + } + return " is no where obvious"; +} + + +/* Either widow the context or map its pc to a bytecode one. + Used to implement primitiveVoidVMStateForMethod. */ +/* for debugging & saving space */ + + /* CoInterpreter>>#widowOrForceToBytecodePC: */ +static NoDbgRegParms NeverInline void +widowOrForceToBytecodePC(sqInt ctxt) +{ + if (((((longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (!(isWidowedContext(ctxt))) { + assert(!((isMachineCodeFrame(frameOfMarriedContext(ctxt))))); + } + } + else { + ensureContextHasBytecodePC(ctxt); + } +} + + /* CoInterpreterPrimitives>>#frameIsMarked: */ +static NoDbgRegParms int +frameIsMarked(sqInt theFPInt) +{ + sqInt methodField; + + methodField = longAt((void *)(theFPInt + FoxMethod)); + return ((((usqInt)methodField)) < (startOfMemory()) + ? (methodField & 4) != 0 + : ((longAt((void *)(theFPInt + FoxIFrameFlags))) & 2) != 0); +} + + /* CoInterpreterPrimitives>>#primitiveAllMethodsCompiledToMachineCode */ +#if VMInvestigations +EXPORT(int) +primitiveAllMethodsCompiledToMachineCode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayObj; + sqInt delta; + sqInt maxNumEntries; + sqInt nEntries; + + if (GIV(argumentCount)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + maxNumEntries = (numMethods()) + (16 /* (allocationUnit * 2) */ / BytesPerOop); + arrayObj = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), maxNumEntries); + if (!arrayObj) { + return -1; + } + nEntries = methodsCompiledToMachineCodeInto(arrayObj); + if (nEntries < maxNumEntries) { + /* begin shorten:toIndexableSize: */ + assert((nEntries >= 0) + && (nEntries < (lengthOf(arrayObj)))); + delta = doShortentoIndexableSize(arrayObj, nEntries); + assert((lengthOf(followMaybeForwarded(arrayObj))) == nEntries); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),arrayObj); + return 0; +} +#endif /* VMInvestigations */ + + /* CoInterpreterPrimitives>>#primitiveBenchmarkFollowForwardersInStackZone */ +#if VMInvestigations +EXPORT(void) +primitiveBenchmarkFollowForwardersInStackZone(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + usqLong then; + + /* begin primitiveReturnTimeTakenFor: */ + then = ioHighResClock(); + followForwardingPointersOfReceiversInStackZone(); + oop = positive64BitIntegerFor((ioHighResClock()) - then); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} +#endif /* VMInvestigations */ + + /* CoInterpreterPrimitives>>#primitiveBenchmarkFollowForwardersOfReceiverAndTemporariesInStackZone */ +#if VMInvestigations +EXPORT(void) +primitiveBenchmarkFollowForwardersOfReceiverAndTemporariesInStackZone(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + usqLong then; + + /* begin primitiveReturnTimeTakenFor: */ + then = ioHighResClock(); + followForwardingPointersOfReceiverAndTemporariesInStackZone(); + oop = positive64BitIntegerFor((ioHighResClock()) - then); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} +#endif /* VMInvestigations */ + + /* CoInterpreterPrimitives>>#primitiveBenchmarkJITReceiver */ +#if VMInvestigations +EXPORT(sqInt) +primitiveBenchmarkJITReceiver(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cloneMethod; + sqInt oop; + sqInt receiverMethod; + usqLong then; + + receiverMethod = longAt(GIV(stackPointer)); + if (!(/* isOopCompiledMethod: */ + ((!(receiverMethod & (tagMask())))) + && (((byteAt((void *)(receiverMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return (GIV(primFailCode) = PrimErrBadReceiver); + } + if (isFullBlockMethod(receiverMethod)) { + return (GIV(primFailCode) = PrimErrUnsupported); + } + cloneMethod = cloneObject(receiverMethod); + if (!cloneMethod) { + return (GIV(primFailCode) = PrimErrNoMemory); + } + + /* begin primitiveReturnTimeTakenFor: */ + then = ioHighResClock(); + if (!(cogselector(cloneMethod, maybeSelectorOfMethod(cloneMethod)))) { + return (GIV(primFailCode) = PrimErrOperationFailed); + } + freeCogMethod(cogMethodOf(cloneMethod)); + oop = positive64BitIntegerFor((ioHighResClock()) - then); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return 0; +} +#endif /* VMInvestigations */ + + /* CoInterpreterPrimitives>>#primitiveBenchmarkScavenge */ +#if VMInvestigations +EXPORT(void) +primitiveBenchmarkScavenge(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + usqLong then; + + /* begin primitiveReturnTimeTakenFor: */ + then = ioHighResClock(); + scavengingGCTenuringIf(TenureByAge); + (((usqInt)(((((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes())) < 0) ? 0 : ((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes())))) << 3) | 1); + oop = positive64BitIntegerFor((ioHighResClock()) - then); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} +#endif /* VMInvestigations */ + + +/* This is optional old obsolete stuff tedious to implement in Cog because of + the need to map from machine code pc to bytecode pc. So for now just fail. */ + + /* CoInterpreterPrimitives>>#primitiveClosureCopyWithCopiedValues */ +static void +primitiveClosureCopyWithCopiedValues(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } +} + + +/* Answer an Array of: + - the address of the CogMethod of the receiver, + - the CogMethod of the receiver as a ByteArray, and + - a sequence of relative pc/value/object triples for the imported + references embedded in the CogMethod. + Fail on V3. Fail if the receiver is not a cogged method. Fail if there is + insufficient memory for the result. + If there is an argument it is a boolean that if true includes more + detailed Closed PIC information. */ + + /* CoInterpreterPrimitives>>#primitiveCogitMethod */ +#if VMInvestigations +EXPORT(sqInt) +primitiveCogitMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt code; + CogMethod *cogMethod; + sqInt cogMethodData; + usqInt data; + sqInt detailCPICs; + sqInt methodHeader; + sqInt methodReceiver; + usqInt newObj; + usqInt numBytes; + sqInt valuePointer; + + if (GIV(argumentCount) > 0) { + if (GIV(argumentCount) > 1) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + if (!((detailCPICs = (longAt(GIV(stackPointer))) == GIV(trueObj)))) { + if (!((longAt(GIV(stackPointer))) == GIV(falseObj))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + } + } + else { + detailCPICs = 0; + } + methodReceiver = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + if (!((/* isOopCompiledMethod: */ + ((!(methodReceiver & (tagMask())))) + && (((byteAt((void *)(methodReceiver + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (methodHasCogMethod(methodReceiver)))) { + return (GIV(primFailCode) = PrimErrBadReceiver); + } + + /* begin eeInstantiateAnySmallClassIndex:format:numSlots: */ + assert((classAtIndex(ClassArrayCompactIndex)) != GIV(nilObj)); + assert((arrayFormat()) == (instSpecOfClass(classAtIndex(ClassArrayCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (3 * BytesPerOop); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + data = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) 3)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + GIV(freeStart) += numBytes; + data = newObj; + /* end eeInstantiateAnySmallClassIndex:format:numSlots: */ +l1: + + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((methodReceiver + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + valuePointer = positive64BitIntegerFor(((usqInt)cogMethod)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(data)) + && (!(isForwarded(data)))); + assert(validStorePointerUncheckedArgs(0, data, valuePointer)); + longAtput((void *)((data + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + if ((cogMethodData = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteArray) << (shiftForWord())))))), (cogMethod->blockSize)))) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(data)) + && (!(isForwarded(data)))); + assert(validStorePointerUncheckedArgs(1, data, cogMethodData)); + longAtput((void *)((data + BaseHeaderSize) + (1U << (shiftForWord()))),cogMethodData); + memcpy(firstIndexableField(cogMethodData), cogMethod, (cogMethod->blockSize)); + } + else { + return (GIV(primFailCode) = PrimErrNoMemory); + } + code = cogMethodImportsintodetailCPICs(cogMethod, data, detailCPICs); + if (code) { + return (GIV(primFailCode) = code); + } + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),data); + return 0; +} +#endif /* VMInvestigations */ + + +/* Answer an array of string,value pairs for all static symbols needed for + deferred disassembly. + */ + + /* CoInterpreterPrimitives>>#primitiveCogStaticSymbols */ +#if VMInvestigations +EXPORT(sqInt) +primitiveCogStaticSymbols(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *abiNameOrNil; + sqInt i; + sqInt symbols; + sqInt valuePointer; + + symbols = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 54); + valuePointer = stringForCString("wordSize"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(0, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(1, symbols, (((usqInt)BytesPerWord << 3) | 1))); + longAtput((void *)((symbols + BaseHeaderSize) + (1U << (shiftForWord()))),(((usqInt)BytesPerWord << 3) | 1)); + valuePointer = stringForCString("ISA"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(2, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (2U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString(isaName()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(3, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (3U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("ABI"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(4, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (4U << (shiftForWord()))),valuePointer); + valuePointer = ((abiNameOrNil = abiName()) + ? stringForCString(abiNameOrNil) + : GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(5, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (5U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("MULTIPLEBYTECODESETS"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(6, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (6U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(7, symbols, GIV(trueObj))); + longAtput((void *)((symbols + BaseHeaderSize) + (7U << (shiftForWord()))),GIV(trueObj)); + valuePointer = stringForCString("varBaseAddress"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(8, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (8U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(computeGoodVarBaseAddress()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(9, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (9U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("argumentCount"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(10, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (10U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(argumentCount))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(11, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (11U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("CFramePointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(12, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (12U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(CFramePointer))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(13, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (13U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("CStackPointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(14, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (14U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(CStackPointer))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(15, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (15U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("framePointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(16, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (16U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(framePointer))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(17, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (17U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("instructionPointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(18, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (18U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(instructionPointer))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(19, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (19U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("interpret"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(20, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (20U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)interpret)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(21, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (21U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("methodCache"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(22, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (22U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)GIV(methodCache))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(23, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (23U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("newMethod"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(24, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (24U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(newMethod))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(25, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (25U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("nextProfileTick"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(26, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (26U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(nextProfileTick))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(27, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (27U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("primFailCode"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(28, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (28U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(primFailCode))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(29, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (29U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("primitiveFunctionPointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(30, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (30U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&primitiveFunctionPointer)))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(0x1F, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (0x1FU << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("primTraceLog"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(32, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (32U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)GIV(primTraceLog))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(33, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (33U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("primTraceLogIndex"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(34, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (34U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(primTraceLogIndex))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(35, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (35U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("stackLimit"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(36, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (36U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(stackLimit))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(37, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (37U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("stackPointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(38, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (38U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(stackPointer))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(39, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (39U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("nil"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(40, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (40U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(41, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (41U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("false"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(42, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (42U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(falseObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(43, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (43U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("true"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(44, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (44U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(trueObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(45, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (45U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("classTableRootObj"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(46, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (46U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(hiddenRootsObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(47, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (47U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("methodZoneBase"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(48, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (48U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(getMethodZoneBase()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(49, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (49U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("checkForAndFollowForwardedPrimitiveState"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(50, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (50U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)checkForAndFollowForwardedPrimitiveState)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(51, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (51U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("ceCheckAndMaybeRetryPrimitive:"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(52, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (52U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)ceCheckAndMaybeRetryPrimitive)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(53, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (53U << (shiftForWord()))),valuePointer); + + /* these are needed because they are not annotated and hence not identified as exports by primitiveCogitMethod + this is needed to distinguish linked/unlinked sends + these two show up in Spur primitives, but maybe absolute calls, which won't show up in exports */ + for (i = 2; i <= 53; i += 1) { + if (!(longAt((void *)((symbols + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + return (GIV(primFailCode) = PrimErrNoMemory); + } + } + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),symbols); + return 0; +} +#endif /* VMInvestigations */ + + +/* Answer the contents of the code zone as an array of pair-wise element, + address in ascending + address order. Answer a string for a runtime routine or abstract label + (beginning, end, etc), + a CompiledMethod for a CMMethod, or a selector (presumably a Symbol) for a + PIC. If there is an argument and it is true, then collect inner + information about the CogMethod. */ + + /* CoInterpreterPrimitives>>#primitiveCollectCogCodeConstituents */ +static void +primitiveCollectCogCodeConstituents(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt constituents; + sqInt withDetails; + + if (GIV(argumentCount)) { + withDetails = longAt(GIV(stackPointer)); + if (!((withDetails == GIV(trueObj)) + || (withDetails == GIV(falseObj)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + withDetails = withDetails == GIV(trueObj); + } + else { + withDetails = 0; + } + constituents = cogCodeConstituents(withDetails); + if (!constituents) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + return; + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),constituents); +} + + +/* Lift the veil from a context and answer an integer describing its interior + state. Used for e.g. VM tests so they can verify they're testing what they + think they're testing. + 0 implies a vanilla heap context. + Bit 0 = is or was married to a frame + Bit 1 = is still married to a frame + Bit 2 = frame is executing machine code + Bit 3 = has machine code pc (as opposed to nil or a bytecode pc) + Bit 4 = method is currently compiled to machine code */ + + /* CoInterpreterPrimitives>>#primitiveContextXray */ +#if VMInvestigations +EXPORT(void) +primitiveContextXray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt context; + sqInt flags; + sqInt pc; + sqInt senderOop; + char *theFP; + sqInt theMethod; + + context = longAt(GIV(stackPointer)); + pc = longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + if (((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (checkIsStillMarriedContextcurrentFP(context, GIV(framePointer))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + flags = 7; + } + else { + flags = 3; + } + } + else { + flags = 1; + } + } + else { + flags = 0; + } + if (((((pc) & 7) == 1)) + && (((pc >> 3)) < 0)) { + flags = flags | 8; + } + theMethod = longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + if (/* maybeMethodHasCogMethod: */ + ((!(theMethod & (tagMask())))) + && ((((byteAt((void *)(theMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((theMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + flags = flags | 16; + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),(((usqInt)flags << 3) | 1)); +} +#endif /* VMInvestigations */ + + +/* Attempt to enter a CriticalSection/Mutex. If not owned, set the owner to + the current + process and answer false. If owned by the current process answer true. + Otherwise suspend the process. Answer if the receiver is owned by the + current process. + For simulation, if there is an argument it is taken to be the effective + activeProcess (see Process>>effectiveProcess). */ + + /* CoInterpreterPrimitives>>#primitiveEnterCriticalSection */ +static void +primitiveEnterCriticalSection(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + sqInt criticalSection; + int inInterpreter; + sqInt objOop; + sqInt owningProcess; + sqInt owningProcessIndex; + + if (GIV(argumentCount) > 0) { + /* rcvr */ + criticalSection = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + activeProc = longAt(GIV(stackPointer)); + if (/* isOopForwarded: */ + ((!(activeProc & (tagMask())))) + && ((!((longAt((void *)(activeProc))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + } + else { + /* rcvr */ + criticalSection = longAt(GIV(stackPointer)); + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + } + + /* CriticalSections are laid out like Semaphores */ + owningProcessIndex = ExcessSignalsIndex; + owningProcess = longAt((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord())))))); + if (owningProcess == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(owningProcessIndex, criticalSection, activeProc)); + assert(isNonImmediate(criticalSection)); + if (oopisGreaterThanOrEqualTo(criticalSection, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeProc & (tagMask())))) + && (oopisLessThan(activeProc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(criticalSection + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(criticalSection); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord()))))),activeProc); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(falseObj)); + return; + } + if (owningProcess == activeProc) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(trueObj)); + return; + } + + /* Arrange to answer false (unowned) when the process is resumed. */ + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(falseObj)); + + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + addLastLinktoList(activeProc, criticalSection); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSEnterCriticalSection); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); +} + + +/* Exit the critical section. + This may change the active process as a result. */ + + /* CoInterpreterPrimitives>>#primitiveExitCriticalSection */ +static void +primitiveExitCriticalSection(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt criticalSection; + int inInterpreter; + sqInt owningProcess; + sqInt owningProcessIndex; + + /* rcvr */ + criticalSection = longAt(GIV(stackPointer)); + + /* CriticalSections are laid out like Semaphores */ + owningProcessIndex = ExcessSignalsIndex; + + /* begin isEmptyList: */ + assert(!(isForwarded(criticalSection))); + if ((longAt((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(criticalSection)) + && (!(isForwarded(criticalSection)))); + assert(validStorePointerUncheckedArgs(owningProcessIndex, criticalSection, GIV(nilObj))); + longAtput((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + owningProcess = removeFirstLinkOfList(criticalSection); + + /* store check unnecessary because aSemaphore referred to owningProcess + via its FirstLinkIndex slot before owningProcess was removed. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(criticalSection)) + && (!(isForwarded(criticalSection)))); + assert(validStorePointerUncheckedArgs(owningProcessIndex, criticalSection, owningProcess)); + longAtput((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord()))))),owningProcess); + + /* Note that resume: isn't fair; it won't suspend the active process. + For fairness we must do the equivalent of a primitiveYield, but that + may break old code, so we stick with unfair resume:. */ + if (resumepreemptedYieldingIffrom(owningProcess, (!(imageHeaderFlags & 16)), CSExitCriticalSection)) { + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } + } +} + + +/* No-op for measuring performance of primitiveExternalCall/#117 linkage */ + + /* CoInterpreterPrimitives>>#primitiveExternalCallNoOp */ +EXPORT(void) +primitiveExternalCallNoOp(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* No-op for measuring performance of FastCPrimitiveFlag linkage */ + + /* CoInterpreterPrimitives>>#primitiveFastCNoOp */ +EXPORT(void) +primitiveFastCNoOp(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* No-op for measuring performance of + FastCPrimitiveFlag+FastCPrimitiveAlignForFloatsFlag linkage + */ + + /* CoInterpreterPrimitives>>#primitiveFastCNoOpAlignedForFloats */ +EXPORT(void) +primitiveFastCNoOpAlignedForFloats(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* The receiver is a compiledMethod. Clear all entries in the method lookup + cache that refer to this method, presumably because it has been redefined, + overridden or removed. + */ +/* The receiver is a compiledMethod. Clear all entries in the method lookup + cache that + refer to this method, presumably because it has been redefined, overridden + or removed. + Override to flush appropriate machine code caches also. */ + + /* CoInterpreterPrimitives>>#primitiveFlushCacheByMethod */ +static void +primitiveFlushCacheByMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + flushMethodCacheForMethod(longAt(GIV(stackPointer))); + unlinkSendsToandFreeIf(longAt(GIV(stackPointer)), 0); +} + + +/* The receiver is a message selector. Clear all entries in the method lookup + cache with this selector, presumably because an associated method has been + redefined. Override to also flush machine code caches. */ + + /* CoInterpreterPrimitives>>#primitiveFlushCacheBySelector */ +static void +primitiveFlushCacheBySelector(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt probe; + sqInt selector; + + selector = longAt(GIV(stackPointer)); + probe = 0; + for (i = 1; i <= MethodCacheEntries; i += 1) { + if ((GIV(methodCache)[probe + MethodCacheSelector]) == selector) { + GIV(methodCache)[probe + MethodCacheSelector] = 0; + } + probe += MethodCacheEntrySize; + } + unlinkSendsOfisMNUSelector(selector, selector == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))))); +} + + /* CoInterpreterPrimitives>>#primitiveFunctionPointerAddress */ +usqInt +primitiveFunctionPointerAddress(void) +{ + return ((usqInt)((&primitiveFunctionPointer))); +} + + +/* Primitive. Install the semaphore to be used for collecting long-running + primitives, + or nil if no semaphore should be used. */ + + /* CoInterpreterPrimitives>>#primitiveLongRunningPrimitiveSemaphore */ +#if LRPCheck +EXPORT(sqInt) +primitiveLongRunningPrimitiveSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + int flushState; + sqInt sema; + + if (GIV(argumentCount) != 1) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + sema = longAt(GIV(stackPointer)); + if (sema == GIV(nilObj)) { + flushState = GIV(longRunningPrimitiveCheckSemaphore); + GIV(longRunningPrimitiveCheckSemaphore) = null; + } + else { + flushState = !GIV(longRunningPrimitiveCheckSemaphore); + if (!(/* isSemaphoreOop: */ + ((!(sema & (tagMask())))) + && (((longAt((void *)(sema))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + GIV(longRunningPrimitiveCheckSemaphore) = sema; + } + + /* If we've switched checking on or off we must void machine code + (and machine code pcs in contexts) since we will start or stop setting + newMethod in machine code primitive invocations, and so generate + slightly different code from here on in. */ + if (flushState) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + activeContext = voidVMStateForSnapshotFlushingExternalPrimitivesIf(0); + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + assert((((stackValue(0)) == (nilObject())) + && (!GIV(longRunningPrimitiveCheckSemaphore))) + || (((stackValue(0)) == GIV(longRunningPrimitiveCheckSemaphore)) + && (isSemaphoreOop(sema)))); + } + voidLongRunningPrimitive("install"); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + if (flushState) { + ceInvokeInterpret(); + } + return 0; +} +#endif /* LRPCheck */ + + +/* Answer if a method is currently jitted. */ + + /* CoInterpreterPrimitives>>#primitiveMethodIsCogged */ +#if VMInvestigations +EXPORT(sqInt) +primitiveMethodIsCogged(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodOop; + sqInt boolean; + sqInt methodHeader; + char *sp; + + if (GIV(argumentCount)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + if (!(/* isOopCompiledMethod: */ + ((!((longAt(GIV(stackPointer))) & (tagMask())))) + && (((byteAt((void *)((longAt(GIV(stackPointer))) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return (GIV(primFailCode) = PrimErrBadReceiver); + } + aMethodOop = longAt(GIV(stackPointer)); + + /* begin methodHasCogMethod: */ + assert(isNonImmediate(aMethodOop)); + methodHeader = longAt((void *)((aMethodOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + boolean = (!(methodHeader & (smallIntegerTag()))); + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (boolean + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return 0; +} +#endif /* VMInvestigations */ + + /* CoInterpreterPrimitives>>#primitiveMethodPCData */ +EXPORT(sqInt) +primitiveMethodPCData(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cm; + CogMethod *cogMethod; + sqInt data; + sqInt dataSqInt; + sqInt delta; + sqInt methodHeader; + sqInt methodReceiver; + sqInt nEntries; + sqInt nSlots; + + if (GIV(argumentCount)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + methodReceiver = longAt(GIV(stackPointer)); + data = 0; + if (methodHasCogMethod(methodReceiver)) { + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((methodReceiver + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + + /* begin pcDataFor: */ + cm = (cogMethod->methodObject); + nSlots = (((byteSizeOf(cm)) - ((((/* begin literalCountOfMethodHeader: */ + assert((((((cogMethod->methodHeader))) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((((cogMethod->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop)) * 2) + (16 /* (allocationUnit * 2) */ / BytesPerOop); + dataSqInt = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), nSlots); + if (!dataSqInt) { + data = -1; + goto l1; + } + nEntries = mapPCDataForinto(cogMethod, dataSqInt); + if (!nEntries) { + data = 0; + goto l1; + } + if (nEntries < nSlots) { + /* begin shorten:toIndexableSize: */ + assert((nEntries >= 0) + && (nEntries < (lengthOf(dataSqInt)))); + delta = doShortentoIndexableSize(dataSqInt, nEntries); + assert((lengthOf(followMaybeForwarded(dataSqInt))) == nEntries); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + } + data = dataSqInt; + /* end pcDataFor: */ +l1: + if (data == -1) { + return (GIV(primFailCode) = PrimErrNoMemory); + } + } + if (!data) { + data = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 0); + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),data); + return 0; +} + + +/* Lift the veil from a method and answer an integer describing the interior + state of its machine code. + Used for e.g. VM tests so they can verify they're testing what they think + they're testing. + 0 implies a vanilla method. + Bit 0 = method might be compiled to machine code + Bit 1 = method is currently compiled to machine code + Bit 2 = is compiled frameless. + Bit 3 = method refers to young object. + Bit 4 = method too big to be jitted (more than 64k of code, or needs more + than 1.5Mb of stack space to compile) + Bit 5 = method contains unknown/unjittable bytecode + Bit 6 = method should not be jitted because it contains a primitive not to + be called from machine code (unused) */ + + /* CoInterpreterPrimitives>>#primitiveMethodXray */ +static void +primitiveMethodXray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt alreadyCogged; + CogMethod *cogMethod; + int flags; + sqInt header; + sqInt methodHeader; + sqInt methodObj; + + methodObj = longAt(GIV(stackPointer)); + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + alreadyCogged = /* maybeMethodHasCogMethod: */ + ((!((longAt(GIV(stackPointer))) & (tagMask())))) + && ((((byteAt((void *)((longAt(GIV(stackPointer))) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))))))); + flags = 1; + if (!alreadyCogged) { + cogMethod = cogselector(longAt(GIV(stackPointer)), GIV(nilObj)); + if ((cogMethod == null) + && (GIV(cogCompiledCodeCompactionCalledFor))) { + commenceCogCompiledCodeCompaction(); + cogMethod = cogselector(longAt(GIV(stackPointer)), GIV(nilObj)); + } + switch (((sqInt)cogMethod)) { + case MethodTooBig: + flags = 17; + break; + case EncounteredUnknownBytecode: + flags = 33; + break; + case ShouldNotJIT: + flags = 65; + break; + default: + assert(!(((((((sqInt)cogMethod)) >= MaxNegativeErrorCode) && ((((sqInt)cogMethod)) <= NotFullyInitialized))))); + } + } + if ((flags == 1) + && (/* maybeMethodHasCogMethod: */ + ((!((longAt(GIV(stackPointer))) & (tagMask())))) + && ((((byteAt((void *)((longAt(GIV(stackPointer))) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))))))))) { + /* begin cogMethodOf: */ + methodHeader = longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + flags = ((cogMethod->stackCheckOffset) + ? 3 + : 7); + if ((cogMethod->cmRefersToYoung)) { + flags += 8; + } + if (!alreadyCogged) { + freeCogMethod(cogMethod); + } + } + } + else { + flags = 0; + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),(((usqInt)flags << 3) | 1)); +} + + /* CoInterpreterPrimitives>>#primitiveMinimumUnusedHeadroom */ +EXPORT(void) +primitiveMinimumUnusedHeadroom(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integer; + + integer = minimumUnusedHeadroom(); + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); +} + + +/* Defined for CompiledMethods only */ + + /* CoInterpreterPrimitives>>#primitiveObjectAt */ +static void +primitiveObjectAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt integerPointer; + sqInt oop; + sqInt rawHeader; + sqInt realHeader; + sqInt thisReceiver; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + index = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + index = 0; + } + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + thisReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rawHeader = longAt((void *)((thisReceiver + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + realHeader = ((!(rawHeader & (smallIntegerTag()))) + ? ((((CogMethod *) rawHeader))->methodHeader) + : rawHeader); + if (!((index > 0) + && (index <= (((/* begin literalCountOfMethodHeader: */ + assert((((realHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((realHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + oop = (index == 1 + ? realHeader + : longAt((void *)((thisReceiver + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oop); +} + + +/* Store a literal into a CompiledMethod at the given index. Defined for + CompiledMethods only. + We assume that if the user is using this on active code then they will use + primitiveVoidVMStateForMethod to discard the machine code as required. */ + + /* CoInterpreterPrimitives>>#primitiveObjectAtPut */ +static void +primitiveObjectAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fieldIndex; + sqInt index; + sqInt newValue; + sqInt rawHeader; + sqInt realHeader; + sqInt thisReceiver; + + newValue = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((!(index & (smallIntegerTag())))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + index = (index >> 3); + thisReceiver = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(thisReceiver + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + return; + } + rawHeader = longAt((void *)((thisReceiver + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + realHeader = ((!(rawHeader & (smallIntegerTag()))) + ? ((((CogMethod *) rawHeader))->methodHeader) + : rawHeader); + if (!((index > 0) + && (index <= (((/* begin literalCountOfMethodHeader: */ + assert((((realHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((realHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (index == 1) { + if (((!(newValue & (smallIntegerTag())))) + || (((/* begin literalCountOfMethodHeader: */ + assert((((newValue) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((newValue >> 3)) & AlternateHeaderNumLiteralsMask)) != ((assert((((realHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((realHeader >> 3)) & AlternateHeaderNumLiteralsMask)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* begin isCogMethodReference: */ + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + ((((CogMethod *) rawHeader))->methodHeader = newValue); + } + else { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(thisReceiver)) + && (!(isForwarded(thisReceiver)))); + assert(validStorePointerUncheckedArgs(0, thisReceiver, newValue)); + longAtput((void *)((thisReceiver + BaseHeaderSize) + (0U << (shiftForWord()))),newValue); + } + } + else { + fieldIndex = index - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, thisReceiver, newValue)); + assert(isNonImmediate(thisReceiver)); + if (oopisGreaterThanOrEqualTo(thisReceiver, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(newValue & (tagMask())))) + && (oopisLessThan(newValue, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(thisReceiver + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(thisReceiver); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((thisReceiver + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),newValue); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),newValue); +} + + +/* Primitive. Install the semaphore to be used for profiling, + or nil if no semaphore should be used. + See also primitiveProfileStart. */ + + /* CoInterpreterPrimitives>>#primitiveProfileSemaphore */ +EXPORT(sqInt) +primitiveProfileSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt address; + sqInt classIndex; + int flushState; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt sema; + sqInt senderOop; + sqInt startObject; + char *theFP; + char *theFrame; + StackPage *thePage; + sqInt top; + + sema = longAt(GIV(stackPointer)); + if (sema == GIV(nilObj)) { + flushState = GIV(profileSemaphore); + GIV(profileSemaphore) = null; + + /* begin zeroNextProfileTick */ + GIV(nextProfileTick) = 0; + } + else { + flushState = !GIV(profileSemaphore); + if (!(/* isSemaphoreOop: */ + ((!(sema & (tagMask())))) + && (((longAt((void *)(sema))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + GIV(profileSemaphore) = sema; + } + GIV(profileProcess) = (GIV(profileMethod) = null); + + /* If we've switched profiling on or off we must void machine code + (and machine code pcs in contexts) since we will start or stop + testing the profile clock in machine code primitive invocations, + and so generate slightly different code from here on in. */ + if (flushState) { + /* begin mapActivationsOfRealPrimitivesToBytecodePCsAndFreeCoggedPrimitiveMethods */ + activeContext = divorceAllFramesSuchThat(isMachineCodeFrameForRealNonProfilingPrimitive); + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin mapToBytecodePCIfActivationOfRealNonProfilingPrimitive: */ + assert(isContextNonImm(obj)); + if (isRealNonProfilingPrimitiveMethod(longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))))) { + if (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (isWidowedContext(obj)) { + goto l2; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + externalDivorceFrameandContext(theFP, obj); + } + } + ensureContextHasBytecodePC(obj); + } + /* end mapToBytecodePCIfActivationOfRealNonProfilingPrimitive: */ +l2:; + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + unlinkSendsToMethodsSuchThatAndFreeIf(cogMethodHasRealNonProfilingPrimitive, 1); + + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + + /* If flushing led to divorce continue in the interpreter. */ + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + ceInvokeInterpret(); + } + + /* If not, work out where we are and continue */ + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + assert(((thePage->headFP)) == theFrame); + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + else { + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + return 0; +} + + +/* Put this process on the scheduler's lists thus allowing it to proceed next + time there is + a chance for processes of it's priority level. It must go to the back of + its run queue so + as not to preempt any already running processes at this level. If the + process's priority + is higher than the current process, preempt the current process. */ + + /* CoInterpreterPrimitives>>#primitiveResume */ +static void +primitiveResume(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + int inInterpreter; + sqInt proc; + + /* rcvr */ + /* Alas in Spur we need a read barrier */ + proc = longAt(GIV(stackPointer)); + + /* begin followObjField:ofObject: */ + ctxt = longAt((void *)((proc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + assert(isNonImmediate(ctxt)); + if ((!((longAt((void *)(ctxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + ctxt = fixFollowedFieldofObjectwithInitialValue(SuspendedContextIndex, proc, ctxt); + } + if (!(/* isContext: */ + ((!(ctxt & (tagMask())))) + && (((longAt((void *)(ctxt))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* We're about to switch process, either to an interpreted frame or a + machine code frame. To know whether to return or enter machine code + we have to know from whence we came. We could have come from the + interpreter, either directly or via a machine code primitive. We could have + come from machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + if (resumepreemptedYieldingIffrom(proc, (!(imageHeaderFlags & 16)), CSResume)) { + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } +} + + +/* Assign the priority of the receiver based on the argument, performing any + necessary rescheduling. + There are three main cases: + the active process + a quiesent/runnable process + a suspended/waiting process + There are scheduling implications only for the first two cases (the two + forms of runnable processes). */ + + /* CoInterpreterPrimitives>>#primitiveSetPriority */ +static void +primitiveSetPriority(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activePriority; + sqInt existingPriority; + int inInterpreter; + sqInt newPriority; + usqInt numSlots; + sqInt oop; + sqInt processList; + sqInt processLists; + sqInt receiverProcess; + sqInt scheduler; + sqInt theActiveProcess; + + scheduler = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + processLists = longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + receiverProcess = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + existingPriority = longAt((void *)((receiverProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + + /* Setting the priority when it is nil is simply instance initialization (typically from Process class>>#forContext:priority:). + If priority has any other non-integer value then there is something wrong and trhe primitive should fail. */ + if (!(((((existingPriority) & 7) == 1)) + || (existingPriority == GIV(nilObj)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + newPriority = longAt(GIV(stackPointer)); + if (!(((((newPriority) & 7) == 1)) + && (((((newPriority = (newPriority >> 3))) >= 1) && (newPriority <= ((/* begin numSlotsOf: */ + assert((classIndexOf(processLists)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(processLists + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(processLists - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + theActiveProcess = longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((theActiveProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + activePriority = (oop >> 3); + + /* assign priority for all cases. return receiver for all cases */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(receiverProcess)) + && (!(isForwarded(receiverProcess)))); + assert(validStorePointerUncheckedArgs(PriorityIndex, receiverProcess, (((usqInt)newPriority << 3) | 1))); + longAtput((void *)((receiverProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord()))))),(((usqInt)newPriority << 3) | 1)); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + + /* Setting the priority when it is nil is simply instance initialization */ + if (!((((existingPriority) & 7) == 1))) { + assert(nilObject() == existingPriority); + return; + } + existingPriority = (existingPriority >> 3); + + /* begin highestRunnablePriorityIsAtLeast: */ + if (GIV(highestRunnableProcessPriority)) { + GIV(highestRunnableProcessPriority) = ((GIV(highestRunnableProcessPriority) < newPriority) ? newPriority : GIV(highestRunnableProcessPriority)); + } + + /* We may switch process, either to an interpreted frame or a machine code frame. To + know whether to return or enter machine code we have to know from whence we came. + We could have come from the interpreter, either directly or via a machine code primitive. + We could have come from machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + + /* If setting the priority of the active process then setting the priority + higher or same is a noop, but setting it lower is like yielding. + But to avoid putting the process to sleep (creating its context etc) + first check if it is still the runnable process on lowering its priority. */ + if (theActiveProcess == receiverProcess) { + if ((newPriority < activePriority) + && ((findHighestPriority()) > newPriority)) { + processList = longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((newPriority - 1)) << (shiftForWord())))))); + addLastLinktoList(theActiveProcess, processList); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSSetPriority); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } + return; + } + + /* If receiverProcess's list is the quiesent list at its priority then it is quiescent/runnable, + otherwise receiverProcess is either suspended (myList = nil) or waiting on a condition + variable. If receiverProcess is quiescent then if setting the priority to less or equal to + the active priority then this is a noop, but if setting higher then effectively the active + process yields to the receiverProcess. */ + if (((longAt((void *)((receiverProcess + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))))) == (longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((existingPriority - 1)) << (shiftForWord())))))))) + && (newPriority > activePriority)) { + processList = longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((activePriority - 1)) << (shiftForWord())))))); + addLastLinktoList(theActiveProcess, processList); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSSetPriority); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } + return; +} + + +/* Synchronously signal the semaphore. + This may change the active process as a result. */ +/* We may be about to switch process, either to an interpreted frame or a + machine code frame. To know whether to return or enter machine code + we have to know from whence we came. We could have come from the + interpreter, either directly or via a machine code primitive. We could + have come from machine code. The instructionPointer tells us where from: */ + + /* CoInterpreterPrimitives>>#primitiveSignal */ +static void +primitiveSignal(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int inInterpreter; + + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + if (synchronousSignal(longAt(GIV(stackPointer)))) { + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } +} + + +/* Save a normal snapshot under the same name as it was loaded + unless it has been renamed by the last primitiveImageName. + Note that when executed this primitive answers false, but when the + resulting image is run afresh, the primitive answers true. + + Override to jump to the interpreter because the machine code zone is now + void. + */ + + /* CoInterpreterPrimitives>>#primitiveSnapshot */ +static void +primitiveSnapshot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + snapshot(0); + if ((longAt(GIV(framePointer) + FoxMethod)) == GIV(newMethod)) { + /* snapshot: has reached the end and built a frame. + In the JIT we need to back-up the pc before reentering the interpreter. */ + GIV(instructionPointer) -= 1; + } + ceInvokeInterpret(); +} + + +/* Save an embedded snapshot. + Note that when executed this primitive answers false, but when the + resulting image is run afresh, the primitive answers true. + + Override to jump to the interpreter because the machine code zone is now + void. + */ + + /* CoInterpreterPrimitives>>#primitiveSnapshotEmbedded */ +static void +primitiveSnapshotEmbedded(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + snapshot(1); + if ((longAt(GIV(framePointer) + FoxMethod)) == GIV(newMethod)) { + /* snapshot: has reached the end and built a frame. + In the JIT we need to back-up the pc before reentering the interpreter. */ + GIV(instructionPointer) -= 1; + } + ceInvokeInterpret(); +} + + +/* Primitive #88. Suspend the receiver, aProcess, such that it can be + executed again + by sending #resume. If the given process is not the active process, take + it off + its corresponding list. The primitive returns the list the receiver was + previously on. + c.f. primitiveSuspendBackingUpV1,#568 & primitiveSuspendBackingUpV2,#578 */ + + /* CoInterpreterPrimitives>>#primitiveSuspend */ +static void +primitiveSuspend(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int inInterpreter; + sqInt myContext; + sqInt myList; + usqInt numSlots; + sqInt ok; + sqInt process; + + process = longAt(GIV(stackPointer)); + if (process == (activeProcess())) { + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(nilObj)); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSSuspend); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + return; + } + myList = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord())))))); + myContext = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if (!((/* isPointers: */ + ((!(myList & (tagMask())))) + && (((byteAt((void *)(myList + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(myList)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(myList + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(myList - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > LastLinkIndex) + && ((((longAt((void *)(myContext))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((/* begin isResumableContext: */ + assert(isContext(myContext)), + ((((longAt((void *)((myContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + ok = removeProcessfromList(process, myList); + if (!ok) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrOperationFailed; + return; + } + + /* stackTopPut: */ + longAtput(GIV(stackPointer),myList); +} + + +/* Primitive #568. Suspend the receiver, aProcess, such that it can be + executed again + by sending #resume. If the given process is not the active process, take + it off + its corresponding list. If the list was not its run queue assume it was on + some condition variable (Semaphore, Mutex) and back up its pc to the send + that invoked the wait state the process entered. Hence when the process + resumes it will reenter the wait state. Answer the list the receiver was + previously on, + unless it was the activeProcess, in which case answer nil. + c.f. primitiveSuspend,#88 & primitiveSuspendBackingUpV2,#578 */ + + /* CoInterpreterPrimitives>>#primitiveSuspendBackingUpV1 */ +static void +primitiveSuspendBackingUpV1(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int inInterpreter; + sqInt myContext; + sqInt myList; + usqInt numSlots; + sqInt ok; + sqInt process; + + process = longAt(GIV(stackPointer)); + if (process == (activeProcess())) { + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(nilObj)); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSSuspend); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + return; + } + myList = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord())))))); + myContext = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if (!((/* isPointers: */ + ((!(myList & (tagMask())))) + && (((byteAt((void *)(myList + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(myList)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(myList + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(myList - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > LastLinkIndex) + && ((((longAt((void *)(myContext))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((/* begin isResumableContext: */ + assert(isContext(myContext)), + ((((longAt((void *)((myContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + ok = removeProcessfromList(process, myList); + if (!ok) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrOperationFailed; + return; + } + if (((longAt((void *)(myList))) & (classIndexMask())) != GIV(classLinkedListClassTag)) { + backupContexttoBlockingSendTo(myContext, myList); + } + + /* stackTopPut: */ + longAtput(GIV(stackPointer),myList); +} + + +/* Primitive #578. Suspend the receiver, aProcess, such that it can be + executed again + by sending #resume. If the given process is not the active process, take + it off + its corresponding list. If the list was not its run queue assume it was on + some condition variable (Semaphore, Mutex) and back up its pc to the send + that invoked the wait state the process entered. Hence when the process + resumes it will reenter the wait state. Answer the list the receiver was + previously on iff + it was not active and not blocked, otherwise answer nil. + c.f. primitiveSuspend,#88 & primitiveSuspendBackingUpV1,#568, + which always answer the list the process was on, even if blocked. */ + + /* CoInterpreterPrimitives>>#primitiveSuspendBackingUpV2 */ +EXPORT(void) +primitiveSuspendBackingUpV2(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int inInterpreter; + sqInt myContext; + sqInt myList; + usqInt numSlots; + sqInt ok; + sqInt process; + + process = longAt(GIV(stackPointer)); + if (process == (activeProcess())) { + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(nilObj)); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSSuspend); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + return; + } + myList = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord())))))); + myContext = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if (!((/* isPointers: */ + ((!(myList & (tagMask())))) + && (((byteAt((void *)(myList + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(myList)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(myList + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(myList - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > LastLinkIndex) + && ((((longAt((void *)(myContext))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((/* begin isResumableContext: */ + assert(isContext(myContext)), + ((((longAt((void *)((myContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + ok = removeProcessfromList(process, myList); + if (!ok) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrOperationFailed; + return; + } + if (((longAt((void *)(myList))) & (classIndexMask())) != GIV(classLinkedListClassTag)) { + backupContexttoBlockingSendTo(myContext, myList); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(nilObj)); + } + else { + /* stackTopPut: */ + longAtput(GIV(stackPointer),myList); + } +} + + +/* Primitive. Terminate up the context stack from the receiver up to but not + including the argument, if previousContext is on my Context stack. Make + previousContext my + sender. This prim has to shadow the code in ContextPart>terminateTo: to be + correct. + Override to ensure the caller's saved ip is correct, i.e. if an + interpreter frame it may + have to move to iframeSavedIP. */ + + /* CoInterpreterPrimitives>>#primitiveTerminateTo */ +static void +primitiveTerminateTo(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aContextOrNil; + char *callerFP; + char *callerFP1; + char *contextsFP; + usqInt contextsIP; + char *contextsSP; + sqInt currentCtx; + char *fp; + char *fp1; + char *frameAbove; + sqInt handlerOrNilOrZero; + char *newFP; + char *newSP; + sqInt nextCntx; + StackPage *pageToStopOn; + sqInt senderOop; + sqInt senderOopSqInt; + char *source; + sqInt stackedReceiverOffset; + char *theFP; + char *theFP1; + StackPage *thePage; + StackPage *thePage1; + sqInt thisCtx; + sqInt valuePointer; + + contextsFP = ((char *) 0); + aContextOrNil = longAt(GIV(stackPointer)); + if (!((aContextOrNil == GIV(nilObj)) + || (/* isContext: */ + ((!(aContextOrNil & (tagMask())))) + && (((longAt((void *)(aContextOrNil))) & (classIndexMask())) == ClassMethodContextCompactIndex)))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + thisCtx = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (thisCtx == aContextOrNil) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* If we're searching for aContextOrNil it might be on a stack page. Helps to know + if we can free a whole page or not, or if we can short-cut the termination. */ + if ((aContextOrNil != GIV(nilObj)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContextOrNil))))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + contextsFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + pageToStopOn = stackPageAtpages(pageIndexFor(contextsFP), GIV(pages)); + } + else { + pageToStopOn = 0; + } + + /* if thisCtx is married ensure it is a base frame. Then we can assign its sender. */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(thisCtx)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* Optimize terminating thisContext. Move its frame down to be next to + aContextOrNil's frame. Common in the exception system and so helps to be fast. */ + if ((theFP == GIV(framePointer)) + && (pageToStopOn == GIV(stackPage))) { + assertValidStackedInstructionPointersInline(GIV(stackPage), __LINE__); + if ((((char *)(longAt(theFP + FoxSavedFP)))) != contextsFP) { + stackedReceiverOffset = (FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))); + + /* begin findFrameAbove:inPage: */ + callerFP = ((char *) 0); + fp = (pageToStopOn->headFP); + if (fp == contextsFP) { + frameAbove = 0; + goto l1; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == contextsFP) { + frameAbove = fp; + goto l1; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + frameAbove = 0; + /* end findFrameAbove:inPage: */ +l1: + contextsIP = longAt(frameAbove + FoxCallerSavedIP); + assert((((((usqInt)contextsIP)) >= (startOfMemory())) + || (contextsIP == (ceReturnToInterpreterPC()))) == (!(isMachineCodeFrame(contextsFP)))); + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(frameAbove))); + newSP = (frameAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(frameAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(frameAbove))->cmNumArgs) + : byteAt((frameAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + newFP = (newSP - stackedReceiverOffset) - BytesPerWord; + for (source = (theFP + stackedReceiverOffset); source >= GIV(stackPointer); source += (-BytesPerWord)) { + newSP -= BytesPerWord; + longAtput(newSP,longAt(source)); + } + longAtput(newFP + FoxSavedFP,((usqInt)contextsFP)); + + /* Ensure contract between machine-code callee and interpreter caller frames is preserved. + Return pc needs to be ceReturnToInterpreterPC. */ + if (((((usqInt)(longAt(newFP + FoxMethod)))) < (startOfMemory())) + && (contextsIP >= (startOfMemory()))) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(contextsFP))); + longAtput(contextsFP + FoxIFSavedIP,contextsIP); + contextsIP = ceReturnToInterpreterPC(); + } + longAtput(newFP + FoxCallerSavedIP,contextsIP); + assert(isContext(thisCtx)); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(newFP)) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(newFP)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(thisCtx)) + && (!(isForwarded(thisCtx)))); + assert(validStorePointerUncheckedArgs(SenderIndex, thisCtx, valuePointer)); + longAtput((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),valuePointer); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(contextsFP)) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(contextsFP)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(thisCtx)) + && (!(isForwarded(thisCtx)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, thisCtx, valuePointer)); + longAtput((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointer); + GIV(framePointer) = newFP; + GIV(stackPointer) = newSP; + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + assertValidStackedInstructionPointersInline(GIV(stackPage), __LINE__); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + return; + } + assertValidStackedInstructionPointers(__LINE__); + + /* May cause a GC!! */ + theFP = externalEnsureIsBaseFrame(theFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + + /* May also reclaim aContextOrNil's page, hence... */ + if ((aContextOrNil != GIV(nilObj)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContextOrNil))))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + contextsFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + pageToStopOn = stackPageAtpages(pageIndexFor(contextsFP), GIV(pages)); + } + else { + pageToStopOn = 0; + } + } + else { + currentCtx = longAt((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + } + assertValidStackedInstructionPointers(__LINE__); + + /* begin context:hasSender: */ + handlerOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(-1, thisCtx, aContextOrNil); + if (!handlerOrNilOrZero) { + /* Need to walk the stack freeing stack pages and nilling contexts. */ + while (!((currentCtx == aContextOrNil) + || (currentCtx == GIV(nilObj)))) { + assert(isContext(currentCtx)); + if (((((longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + + /* If externalEnsureIsBaseFrame: above has moved thisContext to its own stack + then we will always terminate to a frame on a different page. But if we are + terminating some other context to a context somewhere on the current page + we must save the active frames above that context. Things will look e.g. like this: + thisCtx 499383332 s MethodContext(ContextPart)>resume: + 499380484 s BlockClosure>ensure: + 499377320 s MethodContext(ContextPart)>handleSignal: + 499373760 s MethodContext(ContextPart)>handleSignal: + 499372772 s MessageNotUnderstood(Exception)>signal + 499369068 s CodeSimulationTests(Object)>doesNotUnderstand: absentMethod + 499368708 s [] in CodeSimulationTests>testDNU + (sender is 0xbffc2480 I CodeSimulationTests>runSimulated:) + ------------ + framePointer 0xbffc234c M MethodContext(ContextPart)>doPrimitive:method:receiver:args: + 0xbffc2378 M MethodContext(ContextPart)>tryPrimitiveFor:receiver:args: + 0xbffc23ac M MethodContext(ContextPart)>send:to:with:super: + 0xbffc23e4 M MethodContext(ContextPart)>send:super:numArgs: + 0xbffc2418 M MethodContext(InstructionStream)>interpretNextInstructionFor: + 0xbffc2434 M MethodContext(ContextPart)>step + 0xbffc2458 I MethodContext(ContextPart)>runSimulated:contextAtEachStep: + ------------ + (499368708's sender) 0xbffc2480 I CodeSimulationTests>runSimulated: + 0xbffc249c M CodeSimulationTests>testDNU + 0xbffc24bc I CodeSimulationTests(TestCase)>performTest + 0xbffc24dc I [] in CodeSimulationTests(TestCase)>runCase + aContextOrNil 0xbffc24fc M BlockClosure>ensure: + 0xbffc2520 I CodeSimulationTests(TestCase)>runCase + 0xbffc253c M [] in TestResult>runCase: + When we find this case we move the frames above to a new page by making the + frame above currentCtx a base frame, i.e. making 0xbffc2458 in the above example + a base frame. But in this iteration of the loop we don't move down a frame i.e. currentCtx + doesn't change on this iteration. */ + if (thePage == GIV(stackPage)) { + /* begin findFrameAbove:inPage: */ + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == theFP) { + frameAbove = 0; + goto l2; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == theFP) { + frameAbove = fp; + goto l2; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + frameAbove = 0; + /* end findFrameAbove:inPage: */ +l2: + assert(frameAbove != 0); + + /* May cause a GC!! May also reclaim aContextOrNil's page, hence... */ + frameAbove = externalEnsureIsBaseFrame(frameAbove); + if ((aContextOrNil != GIV(nilObj)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContextOrNil))))) { + /* begin frameOfMarriedContext: */ + senderOopSqInt = longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOopSqInt) & 7) == 1)); + contextsFP = ((char *)(senderOopSqInt - (smallIntegerTag()))); + + /* begin stackPageFor: */ + pageToStopOn = stackPageAtpages(pageIndexFor(contextsFP), GIV(pages)); + } + else { + pageToStopOn = 0; + } + } + else { + if (thePage == pageToStopOn) { + /* We're here. Cut back the stack to aContextOrNil's frame, + push its instructionPointer if it's not already a head frame, + and we're done. */ + + /* begin findFrameAbove:inPage: */ + callerFP1 = ((char *) 0); + fp1 = (thePage->headFP); + if (fp1 == contextsFP) { + frameAbove = 0; + goto l3; + } + while (((callerFP1 = ((char *)(longAt(fp1 + FoxSavedFP))))) != 0) { + if (callerFP1 == contextsFP) { + frameAbove = fp1; + goto l3; + } + fp1 = callerFP1; + } + error("did not find theFP in stack page"); + frameAbove = 0; + /* end findFrameAbove:inPage: */ +l3: + if (frameAbove) { + contextsSP = ((/* begin frameCallerSP: */ + assert(!(isBaseFrame(frameAbove))), +(frameAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(frameAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(frameAbove))->cmNumArgs) + : byteAt((frameAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)) - BytesPerWord; + longAtput(contextsSP,longAt(frameAbove + FoxCallerSavedIP)); + + /* begin setHeadFP:andSP:inPage: */ + assert(contextsSP < contextsFP); + assert((contextsSP < ((thePage->baseAddress))) + && (contextsSP > (((thePage->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((contextsFP < ((thePage->baseAddress))) + && (contextsFP > (((thePage->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (thePage->headFP = contextsFP); + (thePage->headSP = contextsSP); + } + currentCtx = aContextOrNil; + } + else { + /* We can free the entire page without further ado. */ + theFP1 = (thePage->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP1)); + thePage1 = stackPageAtpages(pageIndexFor(theFP1), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + + /* for a short time invariant is violated; assert follows */ + freeStackPageNoAssert(thePage); + } + } + } + else { + nextCntx = longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + + /* begin markContextAsDead: */ + assert(isContext(currentCtx)); + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(SenderIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + currentCtx = nextCntx; + } + } + } + assert(pageListIsWellFormed()); + if (((((longAt((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert(checkIsStillMarriedContextcurrentFP(thisCtx, GIV(framePointer))); + assert(isBaseFrame(frameOfMarriedContext(thisCtx))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin frameCallerContext:put: */ + assert((aContextOrNil == (nilObject())) + || (isContext(aContextOrNil))); + assert(isBaseFrame(theFP)); + assert(((theFP + (frameStackedReceiverOffset(theFP))) + (2 * BytesPerWord)) == (((stackPageFor(theFP))->baseAddress))); + assert((longAt((theFP + (frameStackedReceiverOffset(theFP))) + BytesPerWord)) == (frameContext(theFP))); + longAtput((theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + (2 * BytesPerWord),aContextOrNil); + } + else { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SenderIndex, thisCtx, aContextOrNil)); + assert(isNonImmediate(thisCtx)); + if (oopisGreaterThanOrEqualTo(thisCtx, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(aContextOrNil & (tagMask())))) + && (oopisLessThan(aContextOrNil, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(thisCtx + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(thisCtx); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),aContextOrNil); + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + assertValidStackedInstructionPointers(__LINE__); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); +} + + +/* Primitive. Unload the module with the given name. + Reloading of the module will happen *later* automatically, when a + function from it is called. This is forced by invalidating all external + primitive methods and activations in flushExternalPrimitives. + N.B. since this is most likely a development time activity we don't care + about performance. */ + + /* CoInterpreterPrimitives>>#primitiveUnloadModule */ +static void +primitiveUnloadModule(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt moduleLength; + sqInt moduleName; + usqInt numSlots; + + moduleName = longAt(GIV(stackPointer)); + if (!(/* isBytes: */ + ((!(moduleName & (tagMask())))) + && (((byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + moduleLength = ((((/* begin numSlotsOf: */ + assert((classIndexOf(moduleName)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(moduleName + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(moduleName - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (!(ioUnloadModuleOfLength(oopForPointer(firstIndexableField(moduleName)), moduleLength))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + if (/* object:equalsString:ofSize: */ + (/* isBytes: */ + ((!(moduleName & (tagMask())))) + && (((byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((!(((byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((numBytesOfBytes(moduleName)) == moduleLength) + && ((strncmp("SqueakFFIPrims", firstIndexableField(moduleName), moduleLength)) == 0)))) { + primitiveCalloutPointer = ((void *) -1); + } + + /* N.B. flushExternalPrimitives continues. Do *not* do anything after flushExternalPrimitives in the CoInterpreter */ + forceInterruptCheck(); + flushExternalPrimitives(); +} + + +/* Void all internal VM state in the stack and machine code zones + + Override to jump to the interpreter because the machine code zone is now + void. + */ + + /* CoInterpreterPrimitives>>#primitiveVoidVMState */ +static void +primitiveVoidVMState(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + activeContext = voidVMStateForSnapshotFlushingExternalPrimitivesIf(0); + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + ceInvokeInterpret(); +} + + +/* The receiver (or first argument) must be a compiledMethod. The optional + (or second) argument must be a + boolean. Clear all VM state associated with the method, including any + machine code, or machine code pcs + in context objects. If the optional boolean argument is false do not scan + the heap looking for contexts. */ + + /* CoInterpreterPrimitives>>#primitiveVoidVMStateForMethod */ +static void +primitiveVoidVMStateForMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt address; + StackPage *aPage; + char *calleeFP; + sqInt classIndex; + CogMethod *cogMethod; + sqInt divorcedSome; + sqInt followingWord; + usqInt followingWordAddress; + int hasCogMethod; + sqInt i; + sqInt methodHeader; + sqInt methodObj; + usqInt numSlots; + sqInt obj; + sqInt object; + sqInt prevObj; + sqInt prevPrevObj; + sqInt scanHeapForContexts; + sqInt senderOop; + sqInt startObject; + sqInt theContext; + char *theFP; + char *theFrame; + StackPage *thePage; + char *theSP; + sqInt top; + + /* See comment ''One might think...'' below */ + /* In Smalltalk allow both aMethod voidCogVMState and aMethod voidCogVMStateScanningContextsIf: aBoolean */ + scanHeapForContexts = 1; + if (GIV(argumentCount)) { + methodObj = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin booleanValueOf: */ + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + scanHeapForContexts = 1; + goto l1; + } + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + scanHeapForContexts = 0; + goto l1; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + scanHeapForContexts = null; + /* end booleanValueOf: */ +l1: + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + else { + methodObj = longAt(GIV(stackPointer)); + } + if (GIV(argumentCount) > 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + + /* In the NewspeakVM we allow VMMirror voidStateFor: method scanningIf: aBoolean as well as the Smalltalk forms. */ + flushMethodCacheForMethod(methodObj); + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l2; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l2: + + /* begin ensurePushedInstructionPointer */ + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if ((hasCogMethod = methodHasCogMethod(methodObj))) { + /* begin divorceMachineCodeFramesWithMethod: */ + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + do { + if (GIV(stackPage)) { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + + /* This is needed for the assert in externalDivorceFrame:andContext: + Slang can't currently cope with the lack of the variable here. + Something to do with the preceding statement. Take it out + and the code is good. leave it in and we get do { ... } while(l1:) */ + + /* begin divorceSomeMachineCodeFramesWithMethod: */ + divorcedSome = 0; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + aPage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(aPage))) { + /* this to avoid assert in externalDivorceFrame:andContext: */ + markStackPageMostRecentlyUsed(GIV(stackPage)); + + /* begin divorceAMachineCodeFrameWithCogMethod:in: */ + calleeFP = ((char *) 0); + theFP = (aPage->headFP); + theSP = (aPage->headSP); + + /* theSP points at hottest item on frame's stack */ + theSP += BytesPerWord; + while (1) { + if (((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && (cogMethod == (mframeHomeMethod(theFP)))) { + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + theContext = longAt(theFP + FoxThisContext); + goto l6; + } + theContext = marryFrameSP(theFP, theSP); + /* end ensureFrameIsMarried:SP: */ +l6: + externalDivorceFrameandContext(theFP, theContext); + goto l4; + } + calleeFP = theFP; + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!(theFP != 0)) break; + /* theSP points at stacked hottest item on frame's stack */ + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(calleeFP))); + theSP = (calleeFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(calleeFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(calleeFP))->cmNumArgs) + : byteAt((calleeFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + goto l5; +l4: + divorcedSome = 1; +l5:; + } + } + } while(divorcedSome); + } + + /* One might think (as this author did) that the heap scan is unnecessary if the method does not + have a cog method. But it could be the case that the code zone has recently been reclaimed + and so not having a cog method is no indication that it didn't have a cog method some time in + the recent past, and that there are indeed still contexts with machine code pcs out there. The + only steps that can be avoided are divorcing frames in the stack zone, and scanning to unlink and + free if there isn't a cog method, unless we are told otherwise. */ + if (scanHeapForContexts) { + /* begin ensureAllContextsWithMethodHaveBytecodePCs: */ + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord()))))))) == methodObj)) { + widowOrForceToBytecodePC(obj); + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l3: + assert(oopisGreaterThan(obj, prevObj)); + } + } + if (hasCogMethod) { + unlinkSendsToandFreeIf(methodObj, 1); + } + + /* If flushing led to divorce continue in the interpreter. */ + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + assert((methodObj == (stackTop())) + || ((GIV(argumentCount) > 0) + && (methodObj == (stackValue(1))))); + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + ceInvokeInterpret(); + } + + /* If not, work out where we are and continue */ + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + assert(((thePage->headFP)) == theFrame); + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + assert((methodObj == (stackTop())) + || ((GIV(argumentCount) > 0) + && (methodObj == (stackValue(1))))); + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + /* CoInterpreterPrimitives>>#primitiveWait */ +static void +primitiveWait(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + sqLong excessSignals; + int inInterpreter; + sqInt objOop; + sqInt sema; + + /* rcvr */ + sema = longAt(GIV(stackPointer)); + excessSignals = fetchIntegerofObject(ExcessSignalsIndex, sema); + if (excessSignals > 0) { + storeIntegerofObjectwithValue(ExcessSignalsIndex, sema, excessSignals - 1); + } + else { + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + addLastLinktoList(activeProc, sema); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSEnterCriticalSection); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } +} + + +/* Primitively do the equivalent of Process>yield, avoiding the overhead of a + fork and a wait in the standard implementation. + Get the Processor object (1), from it get the activeProcess (2) and hence + the activeProcess's priority (3), and the array of + all runnable processes (5). Via the priority get the list of quiescent + processes at the active priority (6) (implicitly we know + there are no quiescent processes at higher priorities than the + activeProcess which is always one of the highest priority + runnable processes). */ +/* 1 */ + + /* CoInterpreterPrimitives>>#primitiveYield */ +static void +primitiveYield(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + int inInterpreter; + sqInt oop; + sqInt priority; + sqInt processList; + sqInt processLists; + sqInt scheduler; + + scheduler = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* 2 */ + activeProc = longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* 3 */ + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + priority = (oop >> 3); + + /* 4 */ + processLists = longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + + /* 5 */ + processList = longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((priority - 1)) << (shiftForWord())))))); + + /* If the list of quiescent processes at this priority is empty there are no processes to yield to. + If the list is not empty, activate the first one, and add the active process to the end of the list. */ + + /* begin isEmptyList: */ + assert(!(isForwarded(processList))); + if ((longAt((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj)) { + return; + } + + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + addLastLinktoList(activeProc, processList); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSYield); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); +} + + /* CoInterpreterPrimitives>>#unmarkAllFrames */ +static void +unmarkAllFrames(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt flags; + sqInt i; + sqInt methodField; + char *theFP; + StackPage *thePage; + + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + theFP = (thePage->headFP); + do { + methodField = longAt(theFP + FoxMethod); + if ((((usqInt)methodField)) < (startOfMemory())) { + if (methodField & 4) { + longAtput(theFP + FoxMethod,methodField - 4); + } + } + else { + flags = longAt(theFP + FoxIFrameFlags); + if (flags & 2) { + longAtput(theFP + FoxIFrameFlags,flags - 2); + } + } + } while(((theFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0); + } + } +} + + +/* Initialize the stack pages. In the C VM theStackPages will be alloca'ed + memory to hold the + stack pages on the C stack. In the simulator they are housed in the memory + between the + cogMethodZone and the heap. */ + + /* CoInterpreterStackPages>>#initializeStack:numSlots:pageSize: */ +static NoDbgRegParms void +initializeStacknumSlotspageSize(char *theStackPages, sqInt stackSlots, sqInt slotsPerPage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt count; + sqInt index; + sqInt numPages; + StackPage *page; + char *pageStructBase; + void *pointer; + usqIntptr_t structStackPageSize; + sqInt theIndex; + + structStackPageSize = sizeof(CogStackPage); + GIV(bytesPerPage) = slotsPerPage * BytesPerWord; + numPages = GIV(numStackPages); + + /* Because stack pages grow down baseAddress is at the top of a stack page and so to avoid + subtracting BytesPerWord from baseAddress and lastAddress in the init loop below we simply + push the stackPage array up one word to avoid the overlap. This word is extraStackBytes. */ + pageStructBase = (theStackPages + (numPages * GIV(bytesPerPage))) + BytesPerWord; + GIV(pages) = ((StackPage *) pageStructBase); + + /* make sure there's enough headroom */ + assert((((stackPageByteSize()) - (stackLimitBytes())) - (stackLimitOffset())) >= (stackPageHeadroom())); + for (index = 0; index < numPages; index += 1) { + /* begin stackPageAt: */ + page = stackPageAtpages(index, GIV(pages)); + (page->lastAddress = theStackPages + (index * GIV(bytesPerPage))); + (page->baseAddress = ((page->lastAddress)) + GIV(bytesPerPage)); + (page->stackLimit = ((page->baseAddress)) - ((((stackPageFrameBytes()) < (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))) ? (stackPageFrameBytes()) : (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))))); + (page->realStackLimit = (page->stackLimit)); + (page->baseFP = 0); + (page->nextPage = stackPageAt((index == (numPages - 1) + ? 0 + : index + 1))); + (page->prevPage = stackPageAt((index + ? index - 1 + : numPages - 1))); + } + + /* Now compute stackBasePlus1 so that the pageIndexFor: call maps all addresses from + aPage baseAddress to aBase limitAddress + 1 to the same index (stacks grow down) */ + GIV(stackBasePlus1) = (((char *) theStackPages)) + 1; + + /* The overflow limit is the amount of stack to retain when moving frames from an overflowing + stack to reduce thrashing. See stackOverflowOrEvent:mayContextSwitch: */ + + /* begin stackPageAt: */ + page = stackPageAtpages(0, GIV(pages)); + GIV(overflowLimit) = ((((page->baseAddress)) - ((page->realStackLimit))) * 3) / 5; + for (index = 0; index < numPages; index += 1) { + /* begin stackPageAt: */ + page = stackPageAtpages(index, GIV(pages)); + assert((pageIndexFor((page->baseAddress))) == index); + assert((pageIndexFor(((page->baseAddress)) - ((slotsPerPage - 1) * BytesPerWord))) == index); + assert((stackPageFor((page->baseAddress))) == page); + assert((stackPageFor((page->stackLimit))) == page); + + /* begin initializePageTraceToInvalid: */ + (page->trace = StackPageTraceInvalid); + } + GIV(mostRecentlyUsedPage) = stackPageAtpages(0, GIV(pages)); + page = GIV(mostRecentlyUsedPage); + count = 0; + do { + count += 1; + pointer = (page->baseAddress); + + /* begin pageIndexFor: */ +# if !NDEBUG + assert((((((char *) pointer)) >= (GIV(stackBasePlus1) - 1)) && ((((char *) pointer)) <= (((char *) GIV(pages)))))); +# endif // !NDEBUG + + + /* this to avoid an annoying warning: left operand of comma operator has no effect */ + theIndex = pageIndexForstackBasePlus1bytesPerPage(pointer, GIV(stackBasePlus1), GIV(bytesPerPage)); + assert((stackPageAt(theIndex)) == page); + assert((pageIndexFor((page->baseAddress))) == theIndex); + assert((pageIndexFor((page->stackLimit))) == theIndex); + assert((pageIndexFor(((page->lastAddress)) + 1)) == theIndex); + } while(((page = (page->nextPage))) != GIV(mostRecentlyUsedPage)); + assert(count == numPages); + assert(pageListIsWellFormed()); +} + + +/* */ +/* Answer the page index for a pointer into stack memory, i.e. the index + for the page the address is in. N.B. This is a zero-relative index. */ + + /* CoInterpreterStackPages>>#pageIndexFor: */ +static NoDbgRegParms sqInt +pageIndexFor(void *pointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + +# if !NDEBUG + assert((((((char *) pointer)) >= (GIV(stackBasePlus1) - 1)) && ((((char *) pointer)) <= (((char *) GIV(pages)))))); +# endif + + + /* this to avoid an annoying warning: left operand of comma operator has no effect */ + return pageIndexForstackBasePlus1bytesPerPage(pointer, GIV(stackBasePlus1), GIV(bytesPerPage)); +} + + +/* If anOop is an address within the stack zone answer a string stating that, + otherwise answer nil. + */ + + /* CoInterpreterStackPages>>#whereIsMaybeStackThing: */ +static NoDbgRegParms char * +whereIsMaybeStackThing(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (oopisGreaterThanOrEqualToandLessThan(anOop, GIV(stackBasePlus1) - 1, GIV(pages))) { + return " is in the stack zone"; + } + return null; +} + + +/* Answer either a malloced string with the null-terminated contents of oop + if oop is a string, + or the null pointer if oop is nil, or fail. It is the client's + responsibility to free the string later. */ + + /* InterpreterPrimitives>>#cStringOrNullFor: */ +char * +cStringOrNullFor(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + char *cString; + sqInt fmt; + sqInt isString; + sqInt len; + usqInt numSlots; + usqInt numSlotsUsqInt; + + /* begin isInstanceOfClassByteString: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((oop & (tagMask())) != 0)) { + isString = 0; + goto l2; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + isString = ClassByteStringCompactIndex == ccIndex; + /* end isInstanceOfClassByteString: */ +l2: + if (!isString) { + if (oop != GIV(nilObj)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + return 0; + } + + /* begin lengthOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + len = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + len = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + len = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + len = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + len = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + len = 0; + /* end lengthOf: */ +l1: + if (!len) { + return 0; + } + cString = malloc(len + 1); + if (!cString) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoCMemory; + return 0; + } + memcpy(cString, firstIndexableField(oop), len); + cString[len] = 0; + return cString; +} + + +/* In C, non-zero is true, so avoid computation by simply answering + primFailCode in the C version. + */ + + /* InterpreterPrimitives>>#failed */ +sqInt +failed(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(primFailCode); +} + + +/* Answer the identity hash of an object, assigning a hash if it doesn't have + one. On Spur refuse to assign a hash to something that looks like a + behavior. + */ + + /* InterpreterPrimitives>>#identityHashOf: */ +sqInt +identityHashOf(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt hash; + usqInt hashUsqInt; + + if (((anOop & (tagMask())) != 0)) { + /* begin immediateAsInteger:ifFail: */ + if ((((anOop) & 7) == 1)) { + return ((usqInt) ((anOop >> 3))); + } + if (((anOop & (characterTag())) != 0)) { + return ((((usqInt)anOop))) >> (numTagBits()); + } + if (((anOop & (smallFloatTag())) != 0)) { + /* begin rotatedFloatBitsOf: */ + assert(isImmediateFloat(anOop)); + return (((((usqInt)anOop))) >> ((numTagBits()) + 1)) + ((((anOop & (1U << (numTagBits()))) != 0) + ? ((((usqInt)-1)) << ((64 - (numTagBits())) - 1)) + : 0)); + } + return ((usqInt) -1); + } + if ((((long32At((void *)(anOop + 4))) & (identityHashHalfWordMask())) == 0) + && (objCouldBeClassObj(anOop))) { + return -1; + } + + /* begin hashBitsOf: */ + hash = (long32At((void *)(anOop + 4))) & (identityHashHalfWordMask()); + if (!hash) { + /* cb 1/19/2017 18:34: + would like to assert + self assert: (coInterpreter addressCouldBeClassObj: objOop) not + but instance-specific behaviors that are instances of themselves may + fail this test. + eem 12/28/2021 11:52 I'm not sure this is the issue. The issue is only validating objOop as a behavior. */ + + /* begin newHashBitsOf: */ + /* begin newObjectHash */ + assert(!((GIV(lastHash) == 0))); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 2)); + GIV(lastHash) = GIV(lastHash) ^ ((((GIV(lastHash) << 7))) & (identityHashHalfWordMask())); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 3)); + hashUsqInt = GIV(lastHash); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(anOop + 4),((((long32At((void *)(anOop + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hashUsqInt & (identityHashHalfWordMask()))); + hash = hashUsqInt & (identityHashHalfWordMask()); + } + return hash; +} + + +/* N.B. Because Slang always inlines is:instanceOf:compactClassIndex: + (because is:instanceOf:compactClassIndex: has an inline: pragma) the + phrase (objectMemory splObj: ClassArray) is expanded in-place and + is _not_ evaluated if oop has a non-zero CompactClassIndex. */ + + /* InterpreterPrimitives>>#isInstanceOfClassArray: */ +static NoDbgRegParms sqInt +isInstanceOfClassArray(sqInt oop) +{ + sqInt ccIndex; + + /* begin is:instanceOf:compactClassIndex: */ + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + return ClassArrayCompactIndex == ccIndex; +} + + +/* Answer true if integer object is negative. + Fail if object pointed by oop i not an integer. */ + + /* InterpreterPrimitives>>#isNegativeIntegerValueOf: */ +static NoDbgRegParms sqInt +isNegativeIntegerValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + int ok; + sqInt smallInt; + + if ((((oop) & 7) == 1)) { + smallInt = (oop >> 3); + return smallInt < 0; + } + if ((!(oop & (tagMask())))) { + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (ok) { + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargeNegativeIntegerCompactIndex == ccIndex; + if (ok) { + return 1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; +} + + +/* Answer if oop is a value of an integer in address range, i.e up to the + size of a machine word. + The object may be either a positive SmallInteger or a LargePositiveInteger + of size <= word size. + */ + + /* InterpreterPrimitives>>#isPositiveMachineIntegerObject: */ +sqInt +isPositiveMachineIntegerObject(sqInt oop) +{ + sqInt ccIndex; + int ok; + + if ((((oop) & 7) == 1)) { + return ((oop >> 3)) >= 0; + } + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + return ok + && ((numBytesOfBytes(oop)) <= (sizeof(usqIntptr_t))); +} + + +/* Return a Large Integer object for the given integer magnitude and sign */ + + /* InterpreterPrimitives>>#magnitude64BitIntegerFor:neg: */ +static NoDbgRegParms sqInt +magnitude64BitIntegerForneg(usqLong magnitude, sqInt isNegative) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int isSmall; + int largeClassIndex; + usqInt newLargeInteger; + usqInt newObj; + usqInt numBytesUsqInt; + sqInt numSlots; + sqInt objFormat; + sqInt smallVal; + sqInt sz; + + isSmall = (isNegative + ? magnitude <= ((MaxSmallInteger) + 1) + : magnitude <= (MaxSmallInteger)); + if (isSmall) { + smallVal = ((sqInt) magnitude); + if (isNegative) { + smallVal = 0 - smallVal; + } + return (((usqInt)smallVal << 3) | 1); + } + largeClassIndex = (isNegative + ? ClassLargeNegativeIntegerCompactIndex + : ClassLargePositiveIntegerCompactIndex); + sz = 8; + objFormat = (firstByteFormat()) + ((8 - sz) & (BytesPerWord - 1)); + + /* begin eeInstantiateSmallClassIndex:format:numBytes: */ + assert((sz >= 0) + && ((largeClassIndex != 0) + && ((knownClassAtIndex(largeClassIndex)) != GIV(nilObj)))); + assert(((objFormat < (firstByteFormat()) + ? objFormat + : objFormat & (byteFormatMask()))) == (instSpecOfClass(knownClassAtIndex(largeClassIndex)))); + numSlots = ((sz + BytesPerWord) - 1) / BytesPerWord; + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytesUsqInt = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytesUsqInt % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytesUsqInt) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytesUsqInt) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newLargeInteger = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + largeClassIndex); + GIV(freeStart) += numBytesUsqInt; + newLargeInteger = newObj; + /* end eeInstantiateSmallClassIndex:format:numBytes: */ +l1: + if (sz > 4) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((newLargeInteger + BaseHeaderSize)),SQ_SWAP_8_BYTES_IF_BIGENDIAN(magnitude)); + } + else { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((newLargeInteger + BaseHeaderSize)),SQ_SWAP_4_BYTES_IF_BIGENDIAN(magnitude)); + } + return newLargeInteger; +} + + +/* Convert the given object into an integer value. + The object may be either a positive SmallInteger or up to an eight-byte + LargeInteger. + */ + + /* InterpreterPrimitives>>#magnitude64BitValueOf: */ +static NoDbgRegParms usqLong +magnitude64BitValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + sqInt fmt; + usqInt numSlots; + int ok; + sqInt smallIntValue; + sqInt sz; + + if ((((oop) & 7) == 1)) { + smallIntValue = (oop >> 3); + if (smallIntValue < 0) { + smallIntValue = 0 - smallIntValue; + } + return smallIntValue; + } + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (!ok) { + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargeNegativeIntegerCompactIndex == ccIndex; + if (!ok) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + sz = ((((/* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (sz > (sizeof(sqLong))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + if (sz > 4) { + return SQ_SWAP_8_BYTES_IF_BIGENDIAN((long64At((void *)((oop + BaseHeaderSize))))); + } + return ((unsigned int) (SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((oop + BaseHeaderSize))))))); +} + + +/* Answer a value of an integer in address range, i.e up to the size of a + machine word. + The object is known not to be a SmallIntege. It is hoped to be a + LargePositiveInteger of size <= word size. + */ + + /* InterpreterPrimitives>>#positiveMachineIntegerValueOfObj: */ +static NoDbgRegParms usqIntptr_t +positiveMachineIntegerValueOfObj(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bs; + sqInt ccIndex; + sqInt fmt; + sqInt isClassOfNonImmequalTocompactClassIndexRV; + usqInt numSlots; + + assert(!(((((oop) & 7) == 1)))); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + isClassOfNonImmequalTocompactClassIndexRV = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (!isClassOfNonImmequalTocompactClassIndexRV) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + bs = ((((/* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (bs > (sizeof(usqIntptr_t))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + if (((sizeof(usqIntptr_t)) == 8) + && (bs > 4)) { + return SQ_SWAP_8_BYTES_IF_BIGENDIAN((long64At((void *)((oop + BaseHeaderSize))))); + } + return ((unsigned int) (SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((oop + BaseHeaderSize))))))); +} + + +/* Answer a value of an integer in address range, i.e up to the size of a + machine word. + The object may be either a positive SmallInteger or a LargePositiveInteger + of size <= word size. + */ +/* some important callers such as primitiveNewWithArg, so inline the common + case + */ + + /* InterpreterPrimitives>>#positiveMachineIntegerValueOf: */ +usqIntptr_t +positiveMachineIntegerValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt value; + + if ((((oop) & 7) == 1)) { + value = (oop >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return value; + } + + /* don't inline the rare case */ + return positiveMachineIntegerValueOfObj(oop); +} + + /* InterpreterPrimitives>>#primitiveAdd */ +static void +primitiveAdd(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerResult; + + integerResult = (stackIntegerValue(1)) + (stackIntegerValue(0)); + + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)(integerResult)) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerResult << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveAddLargeIntegers */ +EXPORT(void) +primitiveAddLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + sqInt resultIsNegative; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (GIV(primFailCode)) { + return; + } + if (aIsNegative == bIsNegative) { + /* Protect against overflow */ + if (a > (0xFFFFFFFFFFFFFFFFULL - b)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + result = a + b; + resultIsNegative = aIsNegative; + } + else { + if (a >= b) { + result = a - b; + resultIsNegative = aIsNegative; + } + else { + result = b - a; + resultIsNegative = bIsNegative; + } + } + oopResult = magnitude64BitIntegerForneg(result, resultIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Primitive. Change the class of the argument to make it an instance of the + receiver given that the format of the receiver matches the format of the + argument's class. + Fail if receiver or argument are SmallIntegers, or the receiver is an + instance of a + compact class and the argument isn't, or when the argument's class is + compact and + the receiver isn't, or when the format of the receiver is different from + the format of + the argument's class, or when the arguments class is fixed and the + receiver's size + differs from the size that an instance of the argument's class should + have. + */ + + /* InterpreterPrimitives>>#primitiveAdoptInstance */ +static void +primitiveAdoptInstance(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt err; + sqInt rcvr; + + arg = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((arg & (tagMask())) != 0)) + || ((GIV(argumentCount) > 1) + && ((((rcvr & (tagMask())) != 0)) + || (!(objCouldBeClassObj(rcvr)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + err = changeClassOfto(arg, rcvr); + if (err) { + /* changeClassOf:to: answers errors as if rcvr (the class) is an argument... */ + if (err == PrimErrBadReceiver) { + err = PrimErrBadArgument; + } + else { + if (err == PrimErrBadArgument) { + err = PrimErrBadReceiver; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = err; + } + else { + /* Flush at cache because rcvr's class has changed. */ + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + return; +} + + +/* Answer an array of all instances of the receiver that exist + when the primitive is called, excluding any that may be + garbage collected as a side effect of allocating the result array. */ + + /* InterpreterPrimitives>>#primitiveAllInstances */ +EXPORT(void) +primitiveAllInstances(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt delta; + sqInt result; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + result = allInstancesOf(longAt(GIV(stackPointer))); + if ((((result) & 7) == 1)) { + /* begin growToAccomodateContainerWithNumSlots: */ + delta = (BaseHeaderSize * 2) + (((result >> 3)) * BytesPerOop); + growOldSpaceByAtLeast(((GIV(growHeadroom) < delta) ? delta : GIV(growHeadroom))); + result = allInstancesOf(longAt(GIV(stackPointer))); + if ((((result) & 7) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + return; + } + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); +} + + +/* Answer an array of all objects that exist when the primitive + is called, excluding those that may be garbage collected as + a side effect of allocating the result array. */ + + /* InterpreterPrimitives>>#primitiveAllObjects */ +EXPORT(void) +primitiveAllObjects(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt delta; + sqInt result; + + result = allObjects(); + if ((((result) & 7) == 1)) { + /* begin growToAccomodateContainerWithNumSlots: */ + delta = (BaseHeaderSize * 2) + (((result >> 3)) * BytesPerOop); + growOldSpaceByAtLeast(((GIV(growHeadroom) < delta) ? delta : GIV(growHeadroom))); + result = allObjects(); + if ((((result) & 7) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + return; + } + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); +} + + +/* Computes arctan of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveArctan */ +static void +primitiveArctan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(atan(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + +/* Invoke the two-way become primitive. + We must at least flush the method cache here, to eliminate stale + references to mutated classes and/or selectors. + + If in the CoInterpreter, we must deal with jitted methods being becommed. + In the conception of the abstract VM, + unless a CompiledMethod is becommed to one with equivalent bytecode + contexts referring to the becommed + method will likely fetch invalid bytecodes on resumption. The + responsibility for validity here lies with the user of + the become primitive, not the VM. So one could imagine checking for + methods becomming equivalent methods + and updating Cog methods to refer to their becommed duals. But that + requires machinery to compare two + compiled methods to check if their code is equivalent. A much simpler + approach, which also gets the VM to fail + in a less confusing place if it is going to fail because the programmer + has not ensured compiled code validity + across become, is to simply divorce all frames and map context pcs back to + bytecode pcs when becomming + jitted methods. However, mapping native pcs in contexts whose methods are + flagged for become will not ensure + that pcs are mapped reliably. Consider this arc: a context on a method + that has been jitted exists with a native pc. + The method is unjitted to make room for other methods to be jitted. The + method is becommed. So the scan is only + effective for methods in the jit. Slowing down become so that a rare case + may fail more comprehensibly, when its + going to fail anyway, is a waste of effort. So we do divorce frames (since + machine code frames refer to CogMethods, + not methods), but don't scan the entire heap looking for native pcs in + contexts. Hence the primitive must be + prepared to have its calling frame divorced. We store the context for the + top frame in activeProcess. + BTW, as of late '22/early '23 this is only done for Spur. + See preBecomeAction: and postBecomeAction: */ + + /* InterpreterPrimitives>>#primitiveArrayBecome */ +static void +primitiveArrayBecome(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt ec; + sqInt rcvr; + + arg = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + ec = becomewithtwoWaycopyHash(rcvr, arg, 1, 0); + primitiveBecomeReturn(ec); +} + + +/* Invoke the one-way become primitive. + See the comment in primitiveArrayBecome for handling pervasive effects on + method cacheing and jitting. */ + + /* InterpreterPrimitives>>#primitiveArrayBecomeOneWay */ +static void +primitiveArrayBecomeOneWay(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt ec; + sqInt rcvr; + + arg = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + ec = becomewithtwoWaycopyHash(rcvr, arg, 0, 1); + primitiveBecomeReturn(ec); +} + + +/* Similar to primitiveArrayBecomeOneWay but accepts a third argument + deciding whether to + copy the receiver's elements identity hashes over the argument's elements + identity hashes. + See the comment in primitiveArrayBecome for handling pervasive effects on + method cacheing and jitting. + */ + + /* InterpreterPrimitives>>#primitiveArrayBecomeOneWayCopyHashArg */ +static void +primitiveArrayBecomeOneWayCopyHashArg(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt copyHashFlag; + sqInt ec; + + copyHashFlag = 0; + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + copyHashFlag = 1; + } + else { + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + copyHashFlag = 0; + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + ec = becomewithtwoWaycopyHash(longAt(GIV(stackPointer) + (2 * BytesPerWord)), longAt(GIV(stackPointer) + (1 * BytesPerWord)), 0, copyHashFlag); + primitiveBecomeReturn(ec); +} + + +/* Similar to primitiveArrayBecomeOneWay but does /not/ copy the receiver's + elements identity hashes over the argument's elements identity hashes. + See the comment in primitiveArrayBecome for handling pervasive effects on + method cacheing and jitting. */ + + /* InterpreterPrimitives>>#primitiveArrayBecomeOneWayNoCopyHash */ +static void +primitiveArrayBecomeOneWayNoCopyHash(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt ec; + sqInt rcvr; + + arg = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + ec = becomewithtwoWaycopyHash(rcvr, arg, 0, 0); + primitiveBecomeReturn(ec); +} + + /* InterpreterPrimitives>>#primitiveAsCharacter */ +static void +primitiveAsCharacter(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt characterCode; + sqInt characterObject; + + characterCode = longAt(GIV(stackPointer)); + if (!(((((characterCode) & 7) == 1)) + && (((characterCode = (characterCode >> 3)), + /* isInRangeCharacterCode: */ + ((characterCode >= 0) && (characterCode <= (0x3FFFFFFF))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = (GIV(argumentCount) + ? PrimErrBadArgument + : PrimErrBadReceiver); + return; + } + characterObject = ((((usqInt)(characterCode) << (numTagBits())))) + (characterTag()); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),characterObject); +} + + +/* N.B. This will answer inexact results for integers with > 53 bits of + magnitude. + */ + + /* InterpreterPrimitives>>#primitiveAsFloat */ +static void +primitiveAsFloat(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt rcvr; + char *sp; + + rcvr = longAt(GIV(stackPointer)); + assert((((rcvr) & 7) == 1)); + + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer)),floatObjectOf(((double) ((rcvr >> 3)) ))); + GIV(stackPointer) = sp; +} + + /* InterpreterPrimitives>>#primitiveAt */ +static void +primitiveAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt result; + sqInt sp1; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + + /* begin commonAt: */ + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + index = longAt(GIV(stackPointer)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l5; + } + + /* No need to test for large positive integers here. No object has 1g elements */ + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 1) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l5; + } + index = (index >> 3); + + /* begin stObject:at: */ + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + sp1 = stackPointerIndexForFrame(frameOfMarriedContext(rcvr)); + assert((ReceiverIndex + ((sp1 >> 3))) < (lengthOf(rcvr))); + stSize = sp1; + goto l3; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l3; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(rcvr))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l3: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))))) { + result = temporaryin(index - 1, frameOfMarriedContext(rcvr)); + goto l4; + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(rcvr)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + result = (((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + (index - 1)))) << 3) | 1); + goto l4; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + result = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << (shiftForWord())))))); + goto l4; + } + if (fmt >= (firstByteFormat())) { + result = (((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + ((index + fixedFields) - 1)))) << 3) | 1); + goto l4; + } + if (fmt >= (firstShortFormat())) { + result = (((usqInt)(((unsigned short) (shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))))))) << 3) | 1); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + result = positive64BitIntegerFor(long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))))); + goto l4; + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + result = (((usqInt)((((usqInt)(long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + goto l4; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + result = 0; + /* end stObject:at: */ +l4: + if (!GIV(primFailCode)) { + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); + } + /* end commonAt: */ +l5:; +} + + /* InterpreterPrimitives>>#primitiveAtPut */ +static void +primitiveAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fieldIndex; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt signedValueToStore; + sqInt signedValueToStoreSqInt; + sqInt sp; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + usqLong unsigned64BitValueToStore; + unsigned int unsignedValueToStore; + sqInt value; + + /* begin commonAtPut: */ + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + value = longAt(GIV(stackPointer)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l11; + } + + /* No need to test for large positive integers here. No object has 1g elements */ + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 2) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l11; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l11; + } + index = (index >> 3); + + /* begin stObject:at:put: */ + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l7; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l7; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l7; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l7; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l7; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l7: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(rcvr)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(rcvr))); + stSize = sp; + goto l9; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l9; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(rcvr))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l9: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))))) { + temporaryinput(index - 1, frameOfMarriedContext(rcvr), value); + goto l10; + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(rcvr)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + if (((((value) & 7) == 1)) + && ((((usqInt)((signedValueToStore = (value >> 3))))) <= 0xFF)) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + (index - 1)),signedValueToStore); + goto l10; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l10; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l6; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l6; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l6: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:storing:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + fieldIndex = (index + fixedFields) - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, rcvr, value)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),value); + goto l8; + } + if (fmt >= (firstByteFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l8; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l8; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + ((index + fixedFields) - 1)),signedValueToStoreSqInt); + goto l8; + } + if (fmt >= (firstShortFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l8; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l8; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))),signedValueToStoreSqInt); + goto l8; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l8; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l8: + goto l10; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + /* end stObject:at:put: */ +l10: + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + } + /* end commonAtPut: */ +l11:; +} + + +/* Set the cursor to the given shape. The Mac only supports 16x16 pixel + cursors. Cursor offsets are handled by Smalltalk. + */ + + /* InterpreterPrimitives>>#primitiveBeCursor */ +static void +primitiveBeCursor(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cursorBitsObj; + sqInt cursorObj; + sqInt depth; + sqInt extentX; + sqInt extentY; + sqInt maskBitsObj; + sqInt maskObj; + usqInt numSlots; + sqInt offsetObj; + sqInt offsetX; + sqInt offsetY; + + maskBitsObj = 0; + if ((((usqInt)GIV(argumentCount))) > 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + cursorObj = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + if (!((/* isPointers: */ + ((!(cursorObj & (tagMask())))) + && (((byteAt((void *)(cursorObj + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(cursorObj)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(cursorObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(cursorObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= 5))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + cursorBitsObj = longAt((void *)((cursorObj + BaseHeaderSize) + (0U << (shiftForWord())))); + offsetObj = longAt((void *)((cursorObj + BaseHeaderSize) + (4U << (shiftForWord())))); + if (!(((((((extentX = longAt((void *)((cursorObj + BaseHeaderSize) + (1U << (shiftForWord()))))))) & 7) == 1)) + && (((((((extentY = longAt((void *)((cursorObj + BaseHeaderSize) + (2U << (shiftForWord()))))))) & 7) == 1)) + && (((((((depth = longAt((void *)((cursorObj + BaseHeaderSize) + (3U << (shiftForWord()))))))) & 7) == 1)) + && ((/* isPointers: */ + ((!(offsetObj & (tagMask())))) + && (((byteAt((void *)(offsetObj + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(offsetObj)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(offsetObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(offsetObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= 2) + && (((((((offsetX = longAt((void *)((offsetObj + BaseHeaderSize) + (0U << (shiftForWord()))))))) & 7) == 1)) + && ((((((offsetY = longAt((void *)((offsetObj + BaseHeaderSize) + (1U << (shiftForWord()))))))) & 7) == 1))))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + offsetX = (offsetX >> 3); + offsetY = (offsetY >> 3); + extentX = (extentX >> 3); + extentY = (extentY >> 3); + depth = (depth >> 3); + if (!GIV(argumentCount)) { + if (depth == 32) { + if (!((extentX > 0) + && ((extentY > 0) + && ((((offsetX >= (-extentX)) && (offsetX <= 0))) + && ((((offsetY >= (-extentY)) && (offsetY <= 0))) + && ((/* isWords: */ + ((!(cursorBitsObj & (tagMask())))) + && (((((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1))))) + && ((lengthOfformat(cursorBitsObj, firstLongFormat())) == (extentX * extentY)))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* This should pass (objectMemory firstIndexableField: cursorBitsObj type: #'unsigned int *'), but the platform sources are venerable and expect sqInts */ + ioSetCursorARGB(((sqInt) (firstIndexableField(cursorBitsObj))), extentX, extentY, offsetX, offsetY); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return; + } + + /* Support arbitrary-sized 32 bit ARGB forms --bf 3/1/2007 23:51 */ + if (!((extentX == 16) + && ((extentY == 16) + && ((((offsetX >= -16) && (offsetX <= 0))) + && ((((offsetY >= -16) && (offsetY <= 0))) + && ((/* isWords: */ + ((!(cursorBitsObj & (tagMask())))) + && (((((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1))))) + && ((lengthOfformat(cursorBitsObj, firstLongFormat())) == 16))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* This should pass (objectMemory firstIndexableField: cursorBitsObj type: #'unsigned int *'), but the platform sources are venerable and expect sqInts */ + ioSetCursor(((sqInt) (firstIndexableField(cursorBitsObj))), offsetX, offsetY); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return; + } + if (!((extentX == 16) + && ((extentY == 16) + && ((((offsetX >= -16) && (offsetX <= 0))) + && ((((offsetY >= -16) && (offsetY <= 0))) + && ((/* isWords: */ + ((!(cursorBitsObj & (tagMask())))) + && (((((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1))))) + && ((lengthOfformat(cursorBitsObj, firstLongFormat())) == 16))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + maskObj = longAt(GIV(stackPointer)); + if (!((/* isPointers: */ + ((!(maskObj & (tagMask())))) + && (((byteAt((void *)(maskObj + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(maskObj)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(maskObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(maskObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= 5) + && (((longAt((void *)((maskObj + BaseHeaderSize) + (1U << (shiftForWord()))))) == ((((usqInt)16 << 3) | 1))) + && (((longAt((void *)((maskObj + BaseHeaderSize) + (2U << (shiftForWord()))))) == ((((usqInt)16 << 3) | 1))) + && (((longAt((void *)((maskObj + BaseHeaderSize) + (3U << (shiftForWord()))))) == ((((usqInt)1 << 3) | 1))) + && ((isWords((maskBitsObj = longAt((void *)((maskObj + BaseHeaderSize) + (0U << (shiftForWord()))))))) + && ((lengthOfformat(maskBitsObj, firstLongFormat())) == 16)))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* This should pass (objectMemory firstIndexableField: cursorBitsObj type: #'unsigned int *'), but the platform sources are venerable and expect sqInts */ + ioSetCursorWithMask(((sqInt) (firstIndexableField(cursorBitsObj))), ((sqInt) (firstIndexableField(maskBitsObj))), offsetX, offsetY); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* Record the system Display object in the specialObjectsTable, + and if possible pin the display bitmap. Further, invoke ioBeDisplay + to alow the VM to record the location, width heigth & depth of the bitmap. */ + + /* InterpreterPrimitives>>#primitiveBeDisplay */ +static void +primitiveBeDisplay(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bitsOop; + void *bitsOrHandle; + sqInt depthOop; + sqInt heightOop; + sqInt pinnedBits; + sqInt rcvr; + sqInt widthOop; + + rcvr = longAt(GIV(stackPointer)); + if (!((/* isPointers: */ + ((!(rcvr & (tagMask())))) + && (((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && (((lengthOf(rcvr)) >= 4) + && (((bitsOop = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord()))))), + ((/* isWordsOrBytes: */ + ((!(bitsOop & (tagMask())))) + && (isWordsOrBytesNonImm(bitsOop))) + || ((((bitsOop) & 7) == 1))) + && (((((((widthOop = longAt((void *)((rcvr + BaseHeaderSize) + (1U << (shiftForWord()))))))) & 7) == 1)) + && (((((((heightOop = longAt((void *)((rcvr + BaseHeaderSize) + (2U << (shiftForWord()))))))) & 7) == 1)) + && ((((((depthOop = longAt((void *)((rcvr + BaseHeaderSize) + (3U << (shiftForWord()))))))) & 7) == 1))))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheDisplay, GIV(specialObjectsOop), rcvr)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(rcvr & (tagMask())))) + && (oopisLessThan(rcvr, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheDisplay) << (shiftForWord()))))),rcvr); + if (((!(bitsOop & (tagMask())))) + && (!(((byteAt((void *)(bitsOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0))) { + /* Answers 0 if memory required to pin but not enough memory available. */ + pinnedBits = pinObject(bitsOop); + if (pinnedBits) { + bitsOop = pinnedBits; + } + } + bitsOrHandle = ((!(bitsOop & (tagMask()))) + ? firstIndexableField(bitsOop) + : ((void *)bitsOop)); + + /* begin ioBeDisplay:width:height:depth: */ + displayBits = bitsOrHandle; + displayWidth = (widthOop >> 3); + displayHeight = (heightOop >> 3); + displayDepth = (depthOop >> 3); + ioNoteDisplayChangedwidthheightdepth(bitsOrHandle, (widthOop >> 3), (heightOop >> 3), (depthOop >> 3)); +} + + +/* make the basic beep noise */ + + /* InterpreterPrimitives>>#primitiveBeep */ +static void +primitiveBeep(void) +{ + ioBeep(); +} + + +/* For the mirror prims check that the class obj is actually a valid class. + No need to check if it's the receiver since the method exists only on + Behavior. + */ + + /* InterpreterPrimitives>>#primitiveBehaviorHash */ +static void +primitiveBehaviorHash(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt err; + int hash; + sqInt hashOrError; + sqInt shouldBeClassObj; + + shouldBeClassObj = longAt(GIV(stackPointer)); + if (GIV(argumentCount) > 0) { + if ((((shouldBeClassObj & (tagMask())) != 0)) + || (((!((longAt((void *)(shouldBeClassObj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + || (!(objCouldBeClassObj(shouldBeClassObj))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + assert((isNonImmediate(shouldBeClassObj)) + && (addressCouldBeClassObj(shouldBeClassObj))); + + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(shouldBeClassObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + hashOrError = ((hash = (long32At((void *)(shouldBeClassObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(shouldBeClassObj) + ? ((err = enterIntoClassTable(shouldBeClassObj)) + ? -err + : (long32At((void *)(shouldBeClassObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (hashOrError < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -hashOrError; + return; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)hashOrError << 3) | 1)); +} + + +/* Note no short-cut for SmallIntegers. Either the inline interpreter + bytecode or the JIT primitive will handle this case. */ + + /* InterpreterPrimitives>>#primitiveBitAnd */ +static void +primitiveBitAnd(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt integerArgumentValue; + usqInt integerReceiverValue; + sqInt oop; + sqInt value; + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArgumentValue = ((usqIntptr_t) null); + goto l1; + } + integerArgumentValue = ((usqIntptr_t) value); + goto l1; + } + + /* don't inline the rare case */ + integerArgumentValue = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin positiveMachineIntegerValueOf: */ + if ((((oop) & 7) == 1)) { + value = (oop >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerReceiverValue = ((usqIntptr_t) null); + goto l2; + } + integerReceiverValue = ((usqIntptr_t) value); + goto l2; + } + + /* don't inline the rare case */ + integerReceiverValue = positiveMachineIntegerValueOfObj(oop); + /* end positiveMachineIntegerValueOf: */ +l2: + if (!GIV(primFailCode)) { + oop = positive64BitIntegerFor(integerArgumentValue & integerReceiverValue); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oop); + } +} + + +/* Primitive logical operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveBitAndLargeIntegers */ +EXPORT(void) +primitiveBitAndLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong integerArg; + usqLong integerRcvr; + sqInt oopResult; + + integerArg = positive64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = positive64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (GIV(primFailCode)) { + return; + } + oopResult = positive64BitIntegerFor(integerRcvr & integerArg); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Note no short-cut for SmallIntegers. Either the inline interpreter + bytecode or the JIT primitive will handle this case. */ + + /* InterpreterPrimitives>>#primitiveBitOr */ +static void +primitiveBitOr(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt integerArgumentValue; + usqInt integerReceiverValue; + sqInt oop; + sqInt value; + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArgumentValue = ((usqIntptr_t) null); + goto l1; + } + integerArgumentValue = ((usqIntptr_t) value); + goto l1; + } + + /* don't inline the rare case */ + integerArgumentValue = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin positiveMachineIntegerValueOf: */ + if ((((oop) & 7) == 1)) { + value = (oop >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerReceiverValue = ((usqIntptr_t) null); + goto l2; + } + integerReceiverValue = ((usqIntptr_t) value); + goto l2; + } + + /* don't inline the rare case */ + integerReceiverValue = positiveMachineIntegerValueOfObj(oop); + /* end positiveMachineIntegerValueOf: */ +l2: + if (!GIV(primFailCode)) { + oop = positive64BitIntegerFor(integerArgumentValue | integerReceiverValue); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oop); + } +} + + +/* Primitive logical operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveBitOrLargeIntegers */ +EXPORT(void) +primitiveBitOrLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong integerArg; + usqLong integerRcvr; + sqInt oopResult; + + integerArg = positive64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = positive64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (GIV(primFailCode)) { + return; + } + oopResult = positive64BitIntegerFor(integerRcvr | integerArg); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Perform a bitShift. In 32-bits deal only with non-negative 32-bit + integers. In 64-bits deal with signed 64-bit quantities (max (2^63)-1). */ + + /* InterpreterPrimitives>>#primitiveBitShift */ +static void +primitiveBitShift(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + sqInt shifted; + + integerArgument = longAt(GIV(stackPointer)); + if (!((((integerArgument) & 7) == 1))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + integerReceiver = signed64BitValueOf(integerReceiver); + if (!GIV(primFailCode)) { + if (((integerArgument = (integerArgument >> 3))) >= 0) { + if (!(integerArgument <= 61 /* numSmallIntegerBits */)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + shifted = ((sqInt)((usqInt)(integerReceiver) << integerArgument)); + if (!(integerReceiver == ((shifted) >> integerArgument))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + } + else { + if (!(integerArgument >= (-61 /* numSmallIntegerBits */))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + shifted = (integerReceiver) >> (0 - integerArgument); + } + + /* Left shift -- must fail bits would be lost + Right shift -- OK to lose bits */ + shifted = ((((((usqInt)(shifted)) >> 60) + 1) & 15) <= 1 + ? (((usqInt)shifted << 3) | 1) + : signed64BitIntegerFor(shifted)); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),shifted); + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveBitShiftLargeIntegers */ +EXPORT(void) +primitiveBitShiftLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + sqInt integerPointer; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + sqInt shift; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + shift = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + shift = 0; + } + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + a = magnitude64BitValueOf(oopRcvr); + if (GIV(primFailCode)) { + return; + } + if (shift >= 0) { + /* Protect against overflow */ + + /* This is to avoid undue (usqInt) cast */ + result = 0xFFFFFFFFFFFFFFFFULL; + if ((shift >= 64) + || (a > ((result) >> shift))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + result = (a << shift); + } + else { + shift = 0 - shift; + if (shift >= 64) { + result = 0; + } + else { + result = (a) >> shift; + } + + /* Fake 2 complement for negative values */ + if (aIsNegative + && (((result << shift)) != a)) { + result += 1; + } + } + oopResult = magnitude64BitIntegerForneg(result, aIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + /* InterpreterPrimitives>>#primitiveBitXor */ +static void +primitiveBitXor(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + usqInt integerArgumentValue; + sqInt integerReceiver; + usqInt integerReceiverValue; + sqInt oop; + sqInt value; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((((integerArgument) & 7) == 1)) + && ((((integerReceiver) & 7) == 1))) { + oop = (integerArgument ^ integerReceiver) + (smallIntegerTag()); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oop); + } + else { + /* begin positiveMachineIntegerValueOf: */ + if ((((integerArgument) & 7) == 1)) { + value = (integerArgument >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArgumentValue = ((usqIntptr_t) null); + goto l1; + } + integerArgumentValue = ((usqIntptr_t) value); + goto l1; + } + + /* don't inline the rare case */ + integerArgumentValue = positiveMachineIntegerValueOfObj(integerArgument); + /* end positiveMachineIntegerValueOf: */ +l1: + + /* begin positiveMachineIntegerValueOf: */ + if ((((integerReceiver) & 7) == 1)) { + value = (integerReceiver >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerReceiverValue = ((usqIntptr_t) null); + goto l2; + } + integerReceiverValue = ((usqIntptr_t) value); + goto l2; + } + + /* don't inline the rare case */ + integerReceiverValue = positiveMachineIntegerValueOfObj(integerReceiver); + /* end positiveMachineIntegerValueOf: */ +l2: + if (!GIV(primFailCode)) { + oop = positive64BitIntegerFor(integerArgumentValue ^ integerReceiverValue); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oop); + } + } +} + + +/* Primitive logical operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveBitXorLargeIntegers */ +EXPORT(void) +primitiveBitXorLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong integerArg; + usqLong integerRcvr; + sqInt oopResult; + + integerArg = positive64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = positive64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (GIV(primFailCode)) { + return; + } + oopResult = positive64BitIntegerFor(integerRcvr ^ integerArg); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Answer the encoder names for the supported bytecode sets. */ + + /* InterpreterPrimitives>>#primitiveBytecodeSetsAvailable */ +EXPORT(sqInt) +primitiveBytecodeSetsAvailable(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt encoderNames; + usqInt newObj; + usqInt numBytes; + sqInt valuePointer; + + if (GIV(argumentCount) > 0) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((knownClassAtIndex(ClassArrayCompactIndex)) != GIV(nilObj)); + assert((arrayFormat()) == (instSpecOfClass(knownClassAtIndex(ClassArrayCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (2 * BytesPerOop); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + encoderNames = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) 2)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + GIV(freeStart) += numBytes; + encoderNames = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + valuePointer = stringForCString("EncoderForV3PlusClosures"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(encoderNames)) + && (!(isForwarded(encoderNames)))); + assert(validStorePointerUncheckedArgs(0, encoderNames, valuePointer)); + longAtput((void *)((encoderNames + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("EncoderForSistaV1"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(encoderNames)) + && (!(isForwarded(encoderNames)))); + assert(validStorePointerUncheckedArgs(1, encoderNames, valuePointer)); + longAtput((void *)((encoderNames + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),encoderNames); + return 0; +} + + +/* Answer bytes available at this moment. For more meaningful + results, calls to this primitive should be precedeed by a full + or incremental garbage collection. */ + + /* InterpreterPrimitives>>#primitiveBytesLeft */ +static void +primitiveBytesLeft(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aBool; + sqInt chunk; + sqInt freeChunk; + sqInt i; + sqInt integerVal; + + if (!GIV(argumentCount)) { + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),((GIV(totalFreeOldSpace) << 3) | 1)); + return; + } + + /* old behavior - just return the size of free memory */ + if (GIV(argumentCount) == 1) { + /* Spur behavior; if argument is nil answer size of largest free chunk in oldSpace. */ + if ((longAt(GIV(stackPointer))) == GIV(nilObj)) { + /* begin sizeOfLargestFreeChunk */ + freeChunk = findLargestFreeChunk(); + if (!freeChunk) { + for (i = 0x3F; i >= 1; i += -1) { + if ((chunk = GIV(freeLists)[i])) { + integerVal = bytesInBody(chunk); + goto l1; + } + } + integerVal = 0; + goto l1; + } + integerVal = bytesInBody(freeChunk); + /* end sizeOfLargestFreeChunk */ +l1: + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerVal << 3) | 1)); + return; + } + + /* new behaviour -including or excluding swap space depending on aBool */ + + /* begin booleanValueOf: */ + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + aBool = 1; + goto l2; + } + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + aBool = 0; + goto l2; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + aBool = null; + /* end booleanValueOf: */ +l2: + if (!GIV(primFailCode)) { + integerVal = ((((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes())) < 0) ? 0 : ((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes()))); + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerVal << 3) | 1)); + return; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; +} + + +/* Perform a function call to a foreign function. + Only invoked from method containing explicit external call spec. + Due to this we use the pluggable prim mechanism explicitly here + (the first literal of any FFI spec'ed method is an ExternalFunction + and not an array as used in the pluggable primitive mechanism). */ +/* Manually copied from primitiveCalloutAccessorDepth in the + ThreadedFFIPlugins... + */ +/* since call may invoke a callback */ +/* since call signature is in first literal... */ + + /* InterpreterPrimitives>>#primitiveCalloutToFFI */ +static void +primitiveCalloutToFFI(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*primitiveCallout)(void); + + /* begin functionForPrimitiveCallout */ + if ((((sqInt)primitiveCalloutPointer)) == -1) { + reloadPrimitiveCalloutPointer(); + } + primitiveCallout = ((void (*)(void)) primitiveCalloutPointer); + if (primitiveCallout) { + dispatchFunctionPointer(primitiveCallout); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive. Change the class of the receiver into the class of the argument + given that + the format of the receiver matches the format of the argument's class. + Fail if the + receiver or argument are SmallIntegers, or the receiver is an instance of + a compact + class and the argument isn't, or when the argument's class is compact and + the receiver + isn't, or when the format of the receiver is different from the format of + the argument's + class, or when the arguments class is fixed and the receiver's size + differs from the size + that an instance of the argument's class should have. */ + + /* InterpreterPrimitives>>#primitiveChangeClass */ +static void +primitiveChangeClass(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt argClass; + sqInt err; + sqInt oop; + sqInt rcvr; + + /* begin stackObjectValue: */ + oop = longAt(GIV(stackPointer)); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = null; + goto l1; + } + arg = oop; + /* end stackObjectValue: */ +l1: + + /* begin stackObjectValue: */ + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = null; + goto l2; + } + rcvr = oop; + /* end stackObjectValue: */ +l2: + if (GIV(primFailCode)) { + return; + } + argClass = fetchClassOfNonImm(arg); + err = changeClassOfto(rcvr, argClass); + if (err) { + /* primitiveFailFor: */ + GIV(primFailCode) = err; + } + else { + /* Flush at cache because rcvr's class has changed. */ + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + return; +} + + /* InterpreterPrimitives>>#primitiveClass */ +static void +primitiveClass(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt instance; + sqInt oop; + sqInt tagBits; + + instance = longAt(GIV(stackPointer)); + if ((GIV(argumentCount) > 0) + && (/* isOopForwarded: */ + ((!(instance & (tagMask())))) + && ((!((longAt((void *)(instance))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + else { + oop = /* fetchClassOf: */ + ((tagBits = instance & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(instance)); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + } +} + + +/* Primitive. Void the VM profile histograms. */ + + /* InterpreterPrimitives>>#primitiveClearVMProfile */ +static void +primitiveClearVMProfile(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + ioClearProfile(); + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* When called with a single string argument, post the string to + the clipboard. When called with zero arguments, return a + string containing the current clipboard contents. */ + + /* InterpreterPrimitives>>#primitiveClipboardText */ +static void +primitiveClipboardText(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + usqInt numSlots; + sqInt s; + sqInt sz; + + if (GIV(argumentCount) == 1) { + s = longAt(GIV(stackPointer)); + if (!(/* isBytes: */ + ((!(s & (tagMask())))) + && (((byteAt((void *)(s + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + if (!GIV(primFailCode)) { + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(s + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + sz = ((((/* begin numSlotsOf: */ + assert((classIndexOf(s)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(s + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(s - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + clipboardWriteFromAt(sz, s + BaseHeaderSize, 0); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + } + else { + sz = clipboardSize(); + + /* begin allocateBytes:classIndex: */ + assert(addressCouldBeClassObj(classAtIndex(ClassByteStringCompactIndex))); + assert((instSpecOfClass(classAtIndex(ClassByteStringCompactIndex))) == (firstByteFormat())); + s = noInlineAllocateSlotsformatclassIndex(((sz + BytesPerOop) - 1) / BytesPerOop, byteFormatForNumBytes(sz), ClassByteStringCompactIndex); + if (!s) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + clipboardReadIntoAt(sz, s + BaseHeaderSize, 0); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),s); + } +} + + +/* Take a boolean which if true turns or keeps clock logging on. Answer an + array supplying + the size of the clock logs, the address of the usecs log, the index in it, + the address of the + msecs log, and the index into it. */ + + /* InterpreterPrimitives>>#primitiveClockLogAddresses */ +EXPORT(sqInt) +primitiveClockLogAddresses(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt midx; + void *msecs; + sqInt result; + sqInt runInNOut; + sqInt uidx; + void *usecs; + sqInt v1; + sqInt v2; + + + /* bypass type inference which would deduce int */ + midx = 0; + msecs = ((void *) 0); + uidx = 0; + usecs = ((void *) 0); + if (GIV(argumentCount) != 1) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + runInNOut = (longAt(GIV(stackPointer))) == GIV(trueObj); + ioGetClockLogSizeUsecsIdxMsecsIdx((&runInNOut), (&usecs), (&uidx), (&msecs), (&midx)); + result = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 5); + if (!result) { + return (GIV(primFailCode) = PrimErrNoMemory); + } + +#if SPURVM + v1 = positive64BitIntegerFor(((usqInt)usecs)); + v2 = positive64BitIntegerFor(((usqInt)msecs)); +#else /* SPURVM */ + pushRemappableOop(result); + v1 = positive64BitIntegerFor(((usqInt)usecs)); + pushRemappableOop(v1); + v2 = positive64BitIntegerFor(((usqInt)msecs)); + v1 = popRemappableOop(); + result = popRemappableOop(); +#endif /* SPURVM */ + if (GIV(primFailCode)) { + return (GIV(primFailCode) = PrimErrNoMemory); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(result)) + && (!(isForwarded(result)))); + assert(validStorePointerUncheckedArgs(0, result, (((usqInt)runInNOut << 3) | 1))); + longAtput((void *)((result + BaseHeaderSize) + (0U << (shiftForWord()))),(((usqInt)runInNOut << 3) | 1)); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(1, result, v1)); + assert(isNonImmediate(result)); + if (oopisGreaterThanOrEqualTo(result, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(v1 & (tagMask())))) + && (oopisLessThan(v1, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(result + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(result); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((result + BaseHeaderSize) + (1U << (shiftForWord()))),v1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(result)) + && (!(isForwarded(result)))); + assert(validStorePointerUncheckedArgs(2, result, (((usqInt)uidx << 3) | 1))); + longAtput((void *)((result + BaseHeaderSize) + (2U << (shiftForWord()))),(((usqInt)uidx << 3) | 1)); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(3, result, v2)); + assert(isNonImmediate(result)); + if (oopisGreaterThanOrEqualTo(result, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(v2 & (tagMask())))) + && (oopisLessThan(v2, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(result + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(result); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((result + BaseHeaderSize) + (3U << (shiftForWord()))),v2); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(result)) + && (!(isForwarded(result)))); + assert(validStorePointerUncheckedArgs(4, result, (((usqInt)midx << 3) | 1))); + longAtput((void *)((result + BaseHeaderSize) + (4U << (shiftForWord()))),(((usqInt)midx << 3) | 1)); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); + return 0; +} + + /* InterpreterPrimitives>>#primitiveClosureValue */ +static void +primitiveClosureValue(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt blockClosure; + sqInt closureIP; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt i; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt outerContext; + sqInt switched; + + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(GIV(argumentCount) == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Somewhat paranoiac checks we need while debugging that we may be able to discard + in a robust system. */ + outerContext = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + if (!(/* isContext: */ + ((!(outerContext & (tagMask())))) + && (((longAt((void *)(outerContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* Check if the closure's method is actually a CompiledMethod. */ + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin activateNewClosure:outer:method:numArgs:mayContextSwitch: */ + assert(isContext(outerContext)); + assert(isVanillaBlockClosure(blockClosure)); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - ClosureFirstCopiedValueIndex; + assert(closureMethod == (fetchPointerofObject(MethodIndex, outerContext))); + assert(isOopCompiledMethod(closureMethod)); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + ((cogMethod->blockEntryOffset)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogselector(closureMethod, GIV(nilObj)); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + ((cogMethod->blockEntryOffset)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(ReceiverIndex, outerContext, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + ClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* The initial instructions in the block nil-out remaining temps. + the instruction pointer is a pointer variable equal to + method oop + ip + BaseHeaderSize + -1 for 0-based addressing of fetchByte + -1 because it gets incremented BEFORE fetching currentByte */ + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + closureIP = (oop >> 3); + GIV(instructionPointer) = ((closureMethod + closureIP) + BaseHeaderSize) - 2; + + /* begin setMethod:methodHeader: */ + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(1); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewClosure:outer:method:numArgs:mayContextSwitch: */ +l1:; +} + + +/* An exact clone of primitiveClosureValue except that this version will not + check for interrupts on stack overflow. It may invoke the garbage + collector but will not switch processes. See + checkForInterruptsMayContextSwitch: */ + + /* InterpreterPrimitives>>#primitiveClosureValueNoContextSwitch */ +void +primitiveClosureValueNoContextSwitch(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt blockClosure; + sqInt closureIP; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt i; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt outerContext; + sqInt switched; + + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(GIV(argumentCount) == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Somewhat paranoiac checks we need while debugging that we may be able to discard + in a robust system. */ + outerContext = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + if (!(/* isContext: */ + ((!(outerContext & (tagMask())))) + && (((longAt((void *)(outerContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* Check if the closure's method is actually a CompiledMethod. */ + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + GIV(deferProfileCheckForVNCS) = 1; + + /* begin activateNewClosure:outer:method:numArgs:mayContextSwitch: */ + assert(isContext(outerContext)); + assert(isVanillaBlockClosure(blockClosure)); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - ClosureFirstCopiedValueIndex; + assert(closureMethod == (fetchPointerofObject(MethodIndex, outerContext))); + assert(isOopCompiledMethod(closureMethod)); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (((cogMethod->blockEntryOffset)) - (noContextSwitchBlockEntryOffset())); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogselector(closureMethod, GIV(nilObj)); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (((cogMethod->blockEntryOffset)) - (noContextSwitchBlockEntryOffset())); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(ReceiverIndex, outerContext, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + ClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* The initial instructions in the block nil-out remaining temps. + the instruction pointer is a pointer variable equal to + method oop + ip + BaseHeaderSize + -1 for 0-based addressing of fetchByte + -1 because it gets incremented BEFORE fetching currentByte */ + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + closureIP = (oop >> 3); + GIV(instructionPointer) = ((closureMethod + closureIP) + BaseHeaderSize) - 2; + + /* begin setMethod:methodHeader: */ + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(0); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewClosure:outer:method:numArgs:mayContextSwitch: */ +l1:; +} + + /* InterpreterPrimitives>>#primitiveClosureValueWithArgs */ +static void +primitiveClosureValueWithArgs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt argumentArray; + usqInt arraySize; + sqInt blockClosure; + sqInt closureIP; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt i; + sqInt index; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt outerContext; + sqInt switched; + sqInt top; + + argumentArray = longAt(GIV(stackPointer)); + if (!(/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Check for enough space in thisContext to push all args */ + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (!(arraySize <= (LargeContextSlots - CtxtTempFrameStart))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(arraySize == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Somewhat paranoiac checks we need while debugging that we may be able to discard + in a robust system. */ + outerContext = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + if (!(/* isContext: */ + ((!(outerContext & (tagMask())))) + && (((longAt((void *)(outerContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* Check if the closure's method is actually a CompiledMethod. */ + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* Copy the arguments to the stack, and activate */ + index = 1; + while (index <= numArgs) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + index += 1; + } + + /* begin activateNewClosure:outer:method:numArgs:mayContextSwitch: */ + assert(isContext(outerContext)); + assert(isVanillaBlockClosure(blockClosure)); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - ClosureFirstCopiedValueIndex; + assert(closureMethod == (fetchPointerofObject(MethodIndex, outerContext))); + assert(isOopCompiledMethod(closureMethod)); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + ((cogMethod->blockEntryOffset)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogselector(closureMethod, GIV(nilObj)); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + ((cogMethod->blockEntryOffset)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(ReceiverIndex, outerContext, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + ClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* The initial instructions in the block nil-out remaining temps. + the instruction pointer is a pointer variable equal to + method oop + ip + BaseHeaderSize + -1 for 0-based addressing of fetchByte + -1 because it gets incremented BEFORE fetching currentByte */ + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + closureIP = (oop >> 3); + GIV(instructionPointer) = ((closureMethod + closureIP) + BaseHeaderSize) - 2; + + /* begin setMethod:methodHeader: */ + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(1); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewClosure:outer:method:numArgs:mayContextSwitch: */ +l1:; +} + + +/* Return the value of the microsecond clock in the local timezone, as + updated by the heartbeat, as an integer. + This is the number of microseconds since the Smalltalk epoch, 1901/1/1 + 12:00am. The microsecond clock is at least 60 bits wide which means it'll + get to around August + 38435 before it wraps around. Be sure to put it on your calendar. The + coarse clock is + updated by the heartbeat thread and as such is much cheaper than + primitiveUTCMicrosecondClock, which always entails a system call. */ + + /* InterpreterPrimitives>>#primitiveCoarseLocalMicrosecondClock */ +static void +primitiveCoarseLocalMicrosecondClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = positive64BitIntegerFor(ioLocalMicroseconds()); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Return the value of the microsecond clock as updated by the heartbeat as + an integer. + This is the number of microseconds since the Smalltalk epoch, 1901/1/1 + 12:00am. The microsecond clock is at least 60 bits wide which means it'll + get to around August + 38435 before it wraps around. Be sure to put it on your calendar. The + coarse clock is + updated by the heartbeat thread and as such is much cheaper than + primitiveUTCMicrosecondClock, which always entails a system call. */ + + /* InterpreterPrimitives>>#primitiveCoarseUTCMicrosecondClock */ +static void +primitiveCoarseUTCMicrosecondClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = positive64BitIntegerFor(ioUTCMicroseconds()); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Primitive. Compare two byte-indexed objects for equality */ + + /* InterpreterPrimitives>>#primitiveCompareBytes */ +EXPORT(sqInt) +primitiveCompareBytes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg1; + sqInt arg2; + sqInt fmt; + sqInt i; + sqInt len; + usqInt numSlots; + + arg1 = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + arg2 = longAt(GIV(stackPointer)); + + /* Quick identity test */ + if (arg1 == arg2) { + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(trueObj)); + return 0; + } + if (!((/* isBytes: */ + ((!(arg1 & (tagMask())))) + && (((byteAt((void *)(arg1 + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && (/* isBytes: */ + ((!(arg2 & (tagMask())))) + && (((byteAt((void *)(arg2 + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(arg1 + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + len = ((((/* begin numSlotsOf: */ + assert((classIndexOf(arg1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(arg1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(arg1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (!(len == (numBytesOfBytes(arg2)))) { + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(falseObj)); + return 0; + } + for (i = 0; i < len; i += 1) { + if (!((byteAt((void *)((arg1 + BaseHeaderSize) + i))) == (byteAt((void *)((arg2 + BaseHeaderSize) + i))))) { + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(falseObj)); + return 0; + } + } + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(trueObj)); + return 0; +} + + +/* primitiveCompareWith: string2 [collated: order] */ +/* 1 - fetch the parameters from the stack */ + + /* InterpreterPrimitives>>#primitiveCompareWith */ +EXPORT(void) +primitiveCompareWith(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt c1; + sqInt c2; + sqInt fmt; + sqInt i; + sqInt min; + usqInt numSlots; + sqInt order; + sqInt result; + sqInt string1; + sqInt string2; + sqInt strLength1; + sqInt strLength2; + + order = 0; + if (!(GIV(argumentCount) == 1)) { + if (GIV(argumentCount) != 2) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + order = longAt(GIV(stackPointer)); + if (!((/* isBytes: */ + ((!(order & (tagMask())))) + && (((byteAt((void *)(order + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((numBytesOfBytes(order)) == 0x100))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + string1 = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* 2 - check their types - all parameters are ByteObject */ + string2 = longAt(GIV(stackPointer) + ((GIV(argumentCount) - 1) * BytesPerWord)); + if (!((/* isBytes: */ + ((!(string1 & (tagMask())))) + && (((byteAt((void *)(string1 + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && (/* isBytes: */ + ((!(string2 & (tagMask())))) + && (((byteAt((void *)(string2 + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* 3 - compare the strings */ + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(string1 + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + strLength1 = ((((/* begin numSlotsOf: */ + assert((classIndexOf(string1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(string1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(string1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(string2 + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + strLength2 = ((((/* begin numSlotsOf: */ + assert((classIndexOf(string2)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(string2 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(string2 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (order) { + /* begin rawCompare:length:with:length:accessBlock: */ + min = ((strLength1 < strLength2) ? strLength1 : strLength2); + for (i = 0; i < min; i += 1) { + c1 = byteAt((void *)((order + BaseHeaderSize) + ((byteAt((void *)((string1 + BaseHeaderSize) + i))) + 1))); + c2 = byteAt((void *)((order + BaseHeaderSize) + ((byteAt((void *)((string2 + BaseHeaderSize) + i))) + 1))); + if (!(c1 == c2)) { + result = c1 - c2; + goto l2; + } + } + result = strLength1 - strLength2; + /* end rawCompare:length:with:length:accessBlock: */ +l2:; + } + else { + /* begin rawCompare:length:with:length:accessBlock: */ + min = ((strLength1 < strLength2) ? strLength1 : strLength2); + for (i = 0; i < min; i += 1) { + c1 = byteAt((void *)((string1 + BaseHeaderSize) + i)); + c2 = byteAt((void *)((string2 + BaseHeaderSize) + i)); + if (!(c1 == c2)) { + result = c1 - c2; + goto l1; + } + } + result = strLength1 - strLength2; + /* end rawCompare:length:with:length:accessBlock: */ +l1:; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)result << 3) | 1)); +} + + +/* Fill the receiver, which must be an indexable non-pointer + object, with the given integer value. */ + + /* InterpreterPrimitives>>#primitiveConstantFill */ +static void +primitiveConstantFill(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt end; + usqLong fillValue; + sqInt format; + usqInt i; + sqInt oddBytes; + sqInt rcvr; + + /* begin primitiveConstantFillSpur */ + if (GIV(argumentCount) != 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + goto l1; + } + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(((!(rcvr & (tagMask())))) + && (((format = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()))) >= (sixtyFourBitIndexableFormat())))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l1; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l1; + } + fillValue = positive64BitValueOf(longAt(GIV(stackPointer))); + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + if (format >= (firstShortFormat())) { + if (format >= (firstByteFormat())) { + if ((fillValue > 0xFF) + || (format >= (firstCompiledMethodFormat()))) { + /* primitiveFailFor: */ + GIV(primFailCode) = (fillValue > 0xFF + ? PrimErrBadArgument + : PrimErrBadReceiver); + goto l1; + } + fillValue = ((fillValue + ((fillValue << 8))) + ((fillValue << 16))) + ((fillValue << 24)); + oddBytes = format & 7; + } + else { + if (fillValue > 0xFFFF) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + fillValue += (fillValue << 16); + oddBytes = ((format & 3) << 1); + } + fillValue += (fillValue << 32); + } + else { + if (format == (sixtyFourBitIndexableFormat())) { + oddBytes = 0; + } + else { + if (fillValue > 0xFFFFFFFFU) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + fillValue += (fillValue << 32); + oddBytes = ((format & 1) << 2); + } + } + end = addressAfter(rcvr); + i = rcvr + BaseHeaderSize; + while (i < end) { + long64Atput((void *)(i),fillValue); + i += 8; + } + + /* now ensure trailing bytes are zero */ + if (oddBytes > 0) { + fillValue = (fillValue) >> (8 * oddBytes); + long64Atput((void *)(i - 8),fillValue); + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + /* end primitiveConstantFillSpur */ +l1:; +} + + +/* Primitive. Start or stop the VM profiler. The first argument is a boolean + to switch profiling on or off. The second argument is an integer or nil. + If an integer it determines the maximum number of samples in the VM's + sample buffer. Answer the current number of samples in the buffer. */ + + /* InterpreterPrimitives>>#primitiveControlVMProfiling */ +static void +primitiveControlVMProfiling(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bufferSize; + sqInt numSamples; + sqInt onOffBar; + + if (GIV(argumentCount) != 2) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + if (((onOffBar = longAt(GIV(stackPointer) + (1 * BytesPerWord)))) == GIV(trueObj)) { + onOffBar = 1; + } + else { + if (onOffBar == GIV(falseObj)) { + onOffBar = 0; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + } + if (((bufferSize = longAt(GIV(stackPointer)))) == GIV(nilObj)) { + bufferSize = 0; + } + else { + if (!(((((bufferSize) & 7) == 1)) + && (((bufferSize = (bufferSize >> 3))) > 0))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + } + numSamples = ioControlNewProfile(onOffBar,bufferSize); + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),(((usqInt)numSamples << 3) | 1)); +} + + +/* Primitive. Copy the state of the receiver from the argument. + Fail if receiver and argument are of a different class. + Fail if the receiver or argument are contexts (because of context-to-stack + mapping). Fail if receiver and argument have different lengths (for + indexable objects). + Fail if the objects are not in a fit state to be copied (e.g. married + contexts and Cogged methods) */ + + /* InterpreterPrimitives>>#primitiveCopyObject */ +static void +primitiveCopyObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt fmt; + sqInt i; + usqInt length; + usqInt numBytes; + usqInt numSlots; + sqInt rcvr; + sqInt valuePointer; + + if (!(GIV(argumentCount) >= 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + arg = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if (((arg & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (((longAt((void *)(rcvr))) & (classIndexMask())) != ((longAt((void *)(arg))) & (classIndexMask()))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (isWordsOrBytesNonImm(rcvr)) { + /* begin numBytesOf: */ + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + length = numBytes - (fmt & 7); + goto l1; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + length = ((sqInt) numBytes); + goto l1; + } + if (fmt >= (firstShortFormat())) { + length = numBytes - (((fmt & 3) << 1)); + goto l1; + } + + /* fmt >= self firstLongFormat */ + length = numBytes - (((fmt & 1) << 2)); + /* end numBytesOf: */ +l1: + if (!((((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask())) == ((byteAt((void *)(arg + (formatFieldByteOffset())))) & (formatMask()))) + && (length == (numBytesOf(arg))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + memcpy(((void *)(rcvr + BaseHeaderSize)), ((void *)(arg + BaseHeaderSize)), length); + } + else { + if (!(isAppropriateForCopyObject(rcvr))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (!((isAppropriateForCopyObject(arg)) + && (length == (lengthOf(arg))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + for (i = 0; i < length; i += 1) { + valuePointer = longAt((void *)((arg + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(i, rcvr, valuePointer)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + } + + /* Note: The above could be faster for young receivers but I don't think it'll matter */ + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* Computes cosine of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveCosine */ +static void +primitiveCosine(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(cos(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + +/* Crash the VM by indirecting through a null pointer. If the sole argument + is true crash in this thread, and if it is false crash in a new thread. If + the argument is an integer use the method that implies. + bit 0 = thread to crash in; 1 => this thread + bit 1 = crash method; 0 => indirect through null pointer; 1 => call exit */ + + /* InterpreterPrimitives>>#primitiveCrashVM */ +EXPORT(sqInt) +primitiveCrashVM(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt crashInThisThread; + + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + crashInThisThread = ((longAt(GIV(stackPointer))) >> 3); + } + else { + /* begin booleanValueOf: */ + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + crashInThisThread = 1; + goto l1; + } + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + crashInThisThread = 0; + goto l1; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + crashInThisThread = null; + /* end booleanValueOf: */ +l1:; + } + if (GIV(primFailCode) + || (GIV(argumentCount) != 1)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + crashInThisOrAnotherThread(crashInThisThread); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + return 0; +} + + +/* Pass in a non-negative value to disable the architectures powermanager if + any, zero to enable. This is a named (not numbered) primitive in the null + module (ie the VM) + */ + + /* InterpreterPrimitives>>#primitiveDisablePowerManager */ +EXPORT(sqInt) +primitiveDisablePowerManager(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integer; + sqInt integerPointer; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + integer = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integer = 0; + } + if (!GIV(primFailCode)) { + ioDisablePowerManager(integer); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + return 0; +} + + /* InterpreterPrimitives>>#primitiveDiv */ +static void +primitiveDiv(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt quotient; + + quotient = doPrimitiveDivby(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)(quotient)) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)quotient << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } +} + + /* InterpreterPrimitives>>#primitiveDivide */ +static void +primitiveDivide(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerPointer; + sqInt integerReceiver; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + integerReceiver = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerReceiver = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + integerArgument = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArgument = 0; + } + if ((integerArgument != 0) + && ((integerReceiver % integerArgument) == 0)) { + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)((integerReceiver / integerArgument))) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)(integerReceiver / integerArgument) << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveDivideLargeIntegers */ +EXPORT(void) +primitiveDivideLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (GIV(primFailCode)) { + return; + } + + /* check for exact division */ + if (!((b != 0) + && ((a % b) == 0))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + result = a / b; + oopResult = magnitude64BitIntegerForneg(result, aIsNegative != bIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveDivLargeIntegers */ +EXPORT(void) +primitiveDivLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong rem; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (!b) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return; + } + result = a / b; + if (a) { + if (!(bIsNegative == aIsNegative)) { + /* Round toward negative infinity */ + rem = a % b; + if (rem) { + /* This can not overflow, because b > 1, otherwise rem = 0 */ + result += 1; + } + } + } + oopResult = magnitude64BitIntegerForneg(result, bIsNegative != aIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + /* InterpreterPrimitives>>#primitiveEqual */ +static void +primitiveEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + int result; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver == integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + result = (signed64BitValueOf(integerReceiver)) == (signed64BitValueOf(integerArgument)); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (result + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveEqualLargeIntegers */ +EXPORT(void) +primitiveEqualLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr == integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveExitToDebugger */ +static void +primitiveExitToDebugger(void) +{ + error("Exit to debugger at user request"); +} + + +/* Computes E raised to the receiver power. + Receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveExp */ +static void +primitiveExp(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(exp(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + +/* Exponent part of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveExponent */ +static void +primitiveExponent(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + double doubleValue; + int pwr; + sqInt rcvr; + + pwr = 0; + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + + /* rcvr = frac * 2^pwr, where frac is in [0.5..1.0) */ + frexp(doubleValue, (&pwr)); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),(((usqInt)(pwr - 1) << 3) | 1)); +} + + +/* Set general (unspecified) primitive failure. Don't overwrite an error code + that has already been set. */ +/* Use returnTypeC: #sqInt because that's the way it is defined in sq.h. + Use no explicit return so that Slang doesn't fail an inlining type-check + when a primitive with return type void uses ^self primitiveFail to exit. */ + + /* InterpreterPrimitives>>#primitiveFail */ +sqInt +primitiveFail(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; +} + + +/* Set PrimErrOSError primitive failure and associated osErrorCode. */ + + /* InterpreterPrimitives>>#primitiveFailForOSError: */ +sqInt +primitiveFailForOSError(sqLong osErrorCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(secondaryErrorCode) = osErrorCode; + return (GIV(primFailCode) = PrimErrOSError); +} + + +/* Set specific primitive failure. + N.B. primitiveFailFor: PrimNoErr is expected to clear the primFailCode. */ + + /* InterpreterPrimitives>>#primitiveFailFor: */ +sqInt +primitiveFailFor(sqInt reasonCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (GIV(primFailCode) = reasonCode); +} + + +/* Set primFailCode primitive failure and associated secondaryErrorCode. */ + + /* InterpreterPrimitives>>#primitiveFailFor:withSecondary: */ +sqInt +primitiveFailForwithSecondary(sqInt reasonCode, sqLong extraErrorCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(secondaryErrorCode) = extraErrorCode; + return (GIV(primFailCode) = reasonCode); +} + + /* InterpreterPrimitives>>#primitiveFailureCode */ +sqInt +primitiveFailureCode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(primFailCode); +} + + /* InterpreterPrimitives>>#primitiveFetchNextMourner */ +static void +primitiveFetchNextMourner(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt mourner; + + mourner = /* dequeueMourner */ + (GIV(mournQueue) != GIV(nilObj) + ? (assert(isValidObjStack(GIV(mournQueue))), + popObjStack(GIV(mournQueue))) + : 0); + if (mourner) { + assert(isValidObjStack(GIV(mournQueue))); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),mourner); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNotFound; + } +} + + /* InterpreterPrimitives>>#primitiveFloatAdd */ +static void +primitiveFloatAdd(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + sqInt argOop; + usqLong bits; + double rcvr; + sqInt rcvrOop; + double result; + char *sp; + sqInt tagBits; + double value; + + rcvrOop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + argOop = longAt(GIV(stackPointer)); + + /* begin primitiveFloatAdd:toArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l2: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr + arg)); + GIV(stackPointer) = sp; + } + return; +} + + +/* Index the receiver, which must be an indexable non-pointer object, and + yield a float. + */ + + /* InterpreterPrimitives>>#primitiveFloatArrayAt */ +static void +primitiveFloatArrayAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + float aFloat; + sqLong doubleBits; + sqInt fmt; + sqInt index; + usqInt newFloatObj; + usqInt newObj; + usqInt numBytes; + sqInt numSlots; + sqInt numSlotsSqInt; + sqInt oop; + usqLong rawFloatBits; + sqInt rcvr; + usqLong rot; + + /* begin primitiveSpurFloatArrayAt */ + index = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l3; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l3; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + doubleBits = long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3))))); + + /* begin floatObjectOfBits: */ + if (isSmallFloatValueBits(doubleBits)) { + rawFloatBits = doubleBits; + + /* begin smallFloatObjectOfBits: */ + assert(isSmallFloatValueBits(rawFloatBits)); + rot = (((rawFloatBits) >> 0x3F) & 1) + ((rawFloatBits << 1)); + if (rot > 1) { + rot -= (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + assert(rot > 0); + } + + /* a.k.a. ~= +/-0.0 */ + oop = ((rot << (numTagBits()))) + (smallFloatTag()); + goto l2; + } + numSlotsSqInt = (sizeof(double)) / BytesPerOop; + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((numSlotsSqInt >= 0) + && ((knownClassAtIndex(ClassFloatCompactIndex)) != GIV(nilObj))); + assert((firstLongFormat()) == (instSpecOfClass(knownClassAtIndex(ClassFloatCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlotsSqInt < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlotsSqInt < 1 + ? 8 /* allocationUnit */ + : numSlotsSqInt * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newFloatObj = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlotsSqInt)) << (numSlotsFullShift()))) + ((((usqInt)((firstLongFormat())) << (formatShift()))))) + ClassFloatCompactIndex); + GIV(freeStart) += numBytes; + newFloatObj = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((newFloatObj + BaseHeaderSize)),doubleBits); + oop = newFloatObj; + /* end floatObjectOfBits: */ +l2: + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + goto l3; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l3; + } + if ((fmt >= (firstLongFormat())) + && (fmt <= ((firstLongFormat()) + 1))) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + /* begin fetchFloat32:ofObject: */ + aFloat = singleFloatAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2))))); + oop = floatObjectOf(aFloat); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + goto l3; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l3; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + /* end primitiveSpurFloatArrayAt */ +l3:; +} + + +/* Index the receiver, which must be an indexable non-pointer object, and + store a float. + */ + + /* InterpreterPrimitives>>#primitiveFloatArrayAtPut */ +static void +primitiveFloatArrayAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + float aFloat; + usqLong bits; + sqInt fmt; + sqInt index; + sqInt numSlots; + sqInt rcvr; + double result; + sqInt tagBits; + sqLong value; + sqInt valueOop; + + /* begin primitiveSpurFloatArrayAtPut */ + valueOop = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (!((/* isFloatInstance: */ + ((tagBits = valueOop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(valueOop))) & (classIndexMask())) == ClassFloatCompactIndex)) + && ((((index) & 7) == 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l1; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l1; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + value = floatValueBitsOf(valueOop); + + /* begin storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3)))),value); + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + + /* N.B. Currently we simply truncate to 32-bits, which matches the behavior of the FloatArrayPlugin. + Maybe we should validate and range check. */ + if ((fmt >= (firstLongFormat())) + && (fmt <= ((firstLongFormat()) + 1))) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(valueOop)); + if (valueOop & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(valueOop)); + bits = ((((usqInt)valueOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&result), (&bits), sizeof(result)); + } + else { + fetchFloatAtinto(valueOop + BaseHeaderSize, result); + } + aFloat = result; + + /* begin storeFloat32:ofObject:withValue: */ + singleFloatAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2)))),aFloat); + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + /* end primitiveSpurFloatArrayAtPut */ +l1:; +} + + +/* Provide platform-independent access to 32-bit words comprising + a Float. Map index 1 onto the most significant word and index 2 + onto the least significant word. */ + + /* InterpreterPrimitives>>#primitiveFloatAt */ +static void +primitiveFloatAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + sqInt fieldIndex; + sqInt index; + unsigned int integerValue; + sqInt rcvr; + usqInt result; + + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + index = longAt(GIV(stackPointer)); + if (index == ConstOne) { + fieldIndex = (VMBIGENDIAN + ? 0 + : 1); + + /* begin fetchLong32:ofFloatObject: */ + if (!(((rcvr & (smallFloatTag())) != 0))) { + integerValue = long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2))))); + goto l1; + } + + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + integerValue = ((unsigned int) ((fieldIndex + ? (bits) >> 32 + : bits & 0xFFFFFFFFU))); + /* end fetchLong32:ofFloatObject: */ +l1: + + /* begin positive32BitIntegerFor: */ + result = (((usqInt)((((usqInt)integerValue)) & 0xFFFFFFFFU) << 3) | 1); + longAtput((GIV(stackPointer) += 1 * BytesPerWord),result); + return; + } + if (index == ConstTwo) { + fieldIndex = (VMBIGENDIAN + ? 1 + : 0); + + /* begin fetchLong32:ofFloatObject: */ + if (!(((rcvr & (smallFloatTag())) != 0))) { + integerValue = long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2))))); + goto l2; + } + + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + integerValue = ((unsigned int) ((fieldIndex + ? (bits) >> 32 + : bits & 0xFFFFFFFFU))); + /* end fetchLong32:ofFloatObject: */ +l2: + + /* begin positive32BitIntegerFor: */ + result = (((usqInt)((((usqInt)integerValue)) & 0xFFFFFFFFU) << 3) | 1); + longAtput((GIV(stackPointer) += 1 * BytesPerWord),result); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = ((((index) & 7) == 1) + ? PrimErrBadIndex + : PrimErrBadArgument); +} + + +/* Provide platform-independent access to 32-bit words comprising + a Float. Map index 1 onto the most significant word and index 2 + onto the least significant word. */ + + /* InterpreterPrimitives>>#primitiveFloatAtPut */ +static void +primitiveFloatAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt oopToStore; + sqInt rcvr; + usqInt valueToStore; + + oopToStore = longAt(GIV(stackPointer)); + valueToStore = positive32BitValueOf(oopToStore); + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((rcvr & (smallFloatTag())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + return; + } + if (index == ConstOne) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((VMBIGENDIAN + ? 0 + : 1))) << 2)))),valueToStore); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),oopToStore); + return; + } + if (index == ConstTwo) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((VMBIGENDIAN + ? 1 + : 0))) << 2)))),valueToStore); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),oopToStore); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = ((((index) & 7) == 1) + ? PrimErrBadIndex + : PrimErrBadArgument); +} + + /* InterpreterPrimitives>>#primitiveFloatDivide */ +static void +primitiveFloatDivide(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + sqInt argOop; + usqLong bits; + double rcvr; + sqInt rcvrOop; + double result; + char *sp; + sqInt tagBits; + double value; + + rcvrOop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + argOop = longAt(GIV(stackPointer)); + + /* begin primitiveFloatDivide:byArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l2: + + /* begin success: */ + if (!(arg != 0.0)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr / arg)); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatEqual */ +static void +primitiveFloatEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatEqualtoArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatGreaterOrEqual */ +static void +primitiveFloatGreaterOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatGreaterOrEqualtoArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatGreaterThan */ +static void +primitiveFloatGreaterThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatGreaterthanArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatLessOrEqual */ +static void +primitiveFloatLessOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatLessOrEqualtoArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatLessThan */ +static void +primitiveFloatLessThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatLessthanArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatMultiply */ +static void +primitiveFloatMultiply(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + sqInt argOop; + usqLong bits; + double rcvr; + sqInt rcvrOop; + double result; + char *sp; + sqInt tagBits; + double value; + + rcvrOop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + argOop = longAt(GIV(stackPointer)); + + /* begin primitiveFloatMultiply:byArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l2: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr * arg)); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatNotEqual */ +static void +primitiveFloatNotEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatEqualtoArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(falseObj) + : GIV(trueObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatSubtract */ +static void +primitiveFloatSubtract(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + sqInt argOop; + usqLong bits; + double rcvr; + sqInt rcvrOop; + double result; + char *sp; + sqInt tagBits; + double value; + + rcvrOop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + argOop = longAt(GIV(stackPointer)); + + /* begin primitiveFloatSubtract:fromArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l2: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr - arg)); + GIV(stackPointer) = sp; + } + return; +} + + +/* Clear the method lookup cache. This must be done after every programming + change. + */ + + /* InterpreterPrimitives>>#primitiveFlushCache */ +static void +primitiveFlushCache(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); +} + + +/* Primitive. Flush all the existing external primitives in the image thus + forcing a reload on next invocation. + */ + + /* InterpreterPrimitives>>#primitiveFlushExternalPrimitives */ +static void +primitiveFlushExternalPrimitives(void) +{ + flushExternalPrimitives(); +} + + +/* On some platforms, this primitive forces enqueued display updates to be + processed immediately. On others, it does nothing. + */ + + /* InterpreterPrimitives>>#primitiveForceDisplayUpdate */ +static void +primitiveForceDisplayUpdate(void) +{ + ioForceDisplayUpdate(); +} + + +/* On platforms that support it, this primitive prints the receiver, assumed + to be a Form, to the default printer. + */ + + /* InterpreterPrimitives>>#primitiveFormPrint */ +static void +primitiveFormPrint(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bitsArray; + sqInt bitsArraySize; + sqLong depth; + sqInt fmt; + sqLong h; + double hScale; + sqInt landscapeFlag; + usqInt numBytes; + usqInt numSlots; + sqLong pixelsPerWord; + sqInt rcvr; + sqInt successBoolean; + double vScale; + sqLong w; + sqLong wordsPerLine; + + bitsArray = 0; + depth = 0; + h = 0; + w = 0; + + /* begin booleanValueOf: */ + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + landscapeFlag = 1; + goto l1; + } + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + landscapeFlag = 0; + goto l1; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + landscapeFlag = null; + /* end booleanValueOf: */ +l1: + vScale = floatValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + hScale = floatValueOf(longAt(GIV(stackPointer) + (2 * BytesPerWord))); + rcvr = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + if (!((/* isPointers: */ + ((!(rcvr & (tagMask())))) + && (((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((lengthOf(rcvr)) >= 4))) { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + bitsArray = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord())))); + w = fetchIntegerofObject(1, rcvr); + h = fetchIntegerofObject(2, rcvr); + depth = fetchIntegerofObject(3, rcvr); + if (!((w > 0) + && (h > 0))) { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + pixelsPerWord = 32 / depth; + wordsPerLine = (w + (pixelsPerWord - 1)) / pixelsPerWord; + if (/* isWordsOrBytes: */ + ((!(bitsArray & (tagMask())))) + && (isWordsOrBytesNonImm(bitsArray))) { + /* begin numBytesOf: */ + fmt = (byteAt((void *)(bitsArray + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(bitsArray)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(bitsArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(bitsArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + bitsArraySize = numBytes - (fmt & 7); + goto l2; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + bitsArraySize = numBytes; + goto l2; + } + if (fmt >= (firstShortFormat())) { + bitsArraySize = numBytes - (((fmt & 3) << 1)); + goto l2; + } + + /* fmt >= self firstLongFormat */ + bitsArraySize = numBytes - (((fmt & 1) << 2)); + /* end numBytesOf: */ +l2: + + /* begin success: */ + if (!(bitsArraySize == ((wordsPerLine * h) * 4))) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + if (!GIV(primFailCode)) { + successBoolean = ioFormPrint(bitsArray + BaseHeaderSize, w, h, depth, hScale, vScale, landscapeFlag); + + /* begin success: */ + if (!successBoolean) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + } +} + + +/* Fractional part of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveFractionalPart */ +static void +primitiveFractionalPart(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + double trunc; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(modf(doubleValue, (&trunc))); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveFullClosureValue */ +static void +primitiveFullClosureValue(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt blockClosure; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt header; + sqInt i; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + usqInt numTemps; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt switched; + + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(GIV(argumentCount) == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureCompiledBlockIndex) << (shiftForWord())))))); + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin activateNewFullClosure:method:numArgs:mayContextSwitch: */ + assert(closureMethod == (fetchPointerofObject(FullClosureCompiledBlockIndex, blockClosure))); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(blockClosure)))); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex; + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogFullBlockMethodnumCopied(closureMethod, numCopied); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(FullClosureReceiverIndex, blockClosure, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + FullClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(closureMethod)); + header = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + for (i = ((numArgs + numCopied) + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(closureMethod)))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + assert((((usqInt)closureMethod)) >= (startOfMemory())); + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeaderSqInt = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeaderSqInt)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(1); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewFullClosure:method:numArgs:mayContextSwitch: */ +l1:; +} + + +/* An exact clone of primitiveFullClosureValue except that this version will + not check for interrupts on stack overflow. It may invoke the garbage + collector but will not switch processes. See + checkForInterruptsMayContextSwitch: */ + + /* InterpreterPrimitives>>#primitiveFullClosureValueNoContextSwitch */ +void +primitiveFullClosureValueNoContextSwitch(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt blockClosure; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt header; + sqInt i; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + usqInt numTemps; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt switched; + + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(GIV(argumentCount) == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureCompiledBlockIndex) << (shiftForWord())))))); + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + GIV(deferProfileCheckForVNCS) = 1; + + /* begin activateNewFullClosure:method:numArgs:mayContextSwitch: */ + assert(closureMethod == (fetchPointerofObject(FullClosureCompiledBlockIndex, blockClosure))); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockNoContextSwitchEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(blockClosure)))); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex; + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogFullBlockMethodnumCopied(closureMethod, numCopied); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockNoContextSwitchEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(FullClosureReceiverIndex, blockClosure, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + FullClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(closureMethod)); + header = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + for (i = ((numArgs + numCopied) + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(closureMethod)))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + assert((((usqInt)closureMethod)) >= (startOfMemory())); + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeaderSqInt = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeaderSqInt)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(0); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewFullClosure:method:numArgs:mayContextSwitch: */ +l1:; +} + + /* InterpreterPrimitives>>#primitiveFullClosureValueWithArgs */ +static void +primitiveFullClosureValueWithArgs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt argumentArray; + usqInt arraySize; + sqInt blockClosure; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt header; + sqInt i; + sqInt index; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + usqInt numTemps; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt switched; + sqInt top; + + argumentArray = longAt(GIV(stackPointer)); + if (!(/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Check for enough space in thisContext to push all args */ + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (!(arraySize <= (LargeContextSlots - CtxtTempFrameStart))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(arraySize == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureCompiledBlockIndex) << (shiftForWord())))))); + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* Copy the arguments to the stack, and activate */ + index = 1; + while (index <= numArgs) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + index += 1; + } + + /* begin activateNewFullClosure:method:numArgs:mayContextSwitch: */ + assert(closureMethod == (fetchPointerofObject(FullClosureCompiledBlockIndex, blockClosure))); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(blockClosure)))); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex; + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogFullBlockMethodnumCopied(closureMethod, numCopied); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(FullClosureReceiverIndex, blockClosure, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + FullClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(closureMethod)); + header = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + for (i = ((numArgs + numCopied) + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(closureMethod)))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + assert((((usqInt)closureMethod)) >= (startOfMemory())); + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeaderSqInt = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeaderSqInt)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(1); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewFullClosure:method:numArgs:mayContextSwitch: */ +l1:; +} + + +/* Fetch the system attribute with the given integer ID. The result is a + string, or nil if the attribute is not defined. + */ + + /* InterpreterPrimitives>>#primitiveGetAttribute */ +static void +primitiveGetAttribute(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt attr; + const char *attributeString; + + attr = longAt(GIV(stackPointer)); + if (!((((attr) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if ((attributeString = getAttributeString((attr >> 3)))) { + methodReturnString(attributeString); + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); + } +} + + +/* Access to environment variables via getenv. No putenv or setenv as yet. */ + + /* InterpreterPrimitives>>#primitiveGetenv */ +EXPORT(sqInt) +primitiveGetenv(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *dstPtr; + sqInt fmt; + char *key; + usqInt numBytes; + usqInt numSlots; + sqInt obj; + sqInt sz; + char *val; + + if (sHEAFn) { + if (!(sHEAFn())) { + return (GIV(primFailCode) = PrimErrInappropriate); + } + } + + /* secHasEnvironmentAccess */ + + /* begin stackEphemeralStringValue: */ + obj = longAt(GIV(stackPointer)); + if (!(/* isBytes: */ + ((!(obj & (tagMask())))) + && (((byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + key = null; + goto l2; + } + + /* begin byteSizeOf: */ + if (((obj & (tagMask())) != 0)) { + sz = 0; + goto l1; + } + + /* begin numBytesOf: */ + fmt = (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + sz = numBytes - (fmt & 7); + goto l1; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + sz = numBytes; + goto l1; + } + if (fmt >= (firstShortFormat())) { + sz = numBytes - (((fmt & 3) << 1)); + goto l1; + } + + /* fmt >= self firstLongFormat */ + sz = numBytes - (((fmt & 1) << 2)); + /* end byteSizeOf: */ +l1: + dstPtr = alloca(sz + 1); + if (!dstPtr) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoCMemory; + key = null; + goto l2; + } + memcpy(dstPtr, ((char *) (firstIndexableField(obj))), sz); + dstPtr[sz] = 0; + key = dstPtr; + /* end stackEphemeralStringValue: */ +l2: + if (key) { + if ((val = getenv(key))) { + methodReturnString(val); + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); + } + } + return 0; +} + + /* InterpreterPrimitives>>#primitiveGetImmutability */ +#if IMMUTABILITY +static void +primitiveGetImmutability(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt boolean; + sqInt rcvr; + char *sp; + + rcvr = longAt(GIV(stackPointer)); + if ((GIV(argumentCount) > 0) + && (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + boolean = /* isOopImmutable: */ + (((rcvr & (tagMask())) != 0)) + || (((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0); + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (boolean + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; +} +#endif /* IMMUTABILITY */ + + +/* Primitive. Answer the VM's current log directory */ + + /* InterpreterPrimitives>>#primitiveGetLogDirectory */ +EXPORT(sqInt) +primitiveGetLogDirectory(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + char *ptr; + sqInt stringOop; + sqInt sz; + + ptr = ioGetLogDirectory(); + if (!ptr) { + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + sz = strlen(ptr); + stringOop = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord())))))), sz); + for (i = 0; i < sz; i += 1) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((stringOop + BaseHeaderSize) + i),ptr[i]); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),stringOop); + return 0; +} + + +/* Primitive. Return the next input event from the VM event queue. */ + + /* InterpreterPrimitives>>#primitiveGetNextEvent */ +static void +primitiveGetNextEvent(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqIntptr_t eventTypeIs; + static char * eventTypeNames[] = { + "None", "Mouse", "Keyboard", "DragDropFiles", "Menu", "Window", "Complex", "MouseWheel", + "Plugin" + }; + sqIntptr_t evtBuf[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + sqInt i; + sqIntptr_t value; + + arg = longAt(GIV(stackPointer)); + if (!((/* isArray: */ + ((!(arg & (tagMask())))) + && (((byteAt((void *)(arg + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((slotSizeOf(arg)) == 8))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + ioGetNextEvent(((sqInputEvent*) evtBuf)); + if (GIV(primFailCode)) { + return; + } + if ((eventTraceMask != 0) + && (((eventTraceMask & ((((evtBuf[0]) < 0) ? (((usqInt)(1)) >> (-(evtBuf[0]))) : (1ULL << (evtBuf[0]))))) != 0))) { + fprintf(stderr, + "Event%s/%" PRIdSQINT " @ %" PRIuSQINT "\t\t%" PRIdSQINT "/%" PRIxSQINT " %" PRIdSQINT "/%" PRIxSQINT "\n\t%" PRIdSQINT "/%" PRIxSQINT " %" PRIdSQINT "/%" PRIxSQINT "\t %" PRIdSQINT "/%" PRIxSQINT " %p\n", + ((((evtBuf[0]) >= 0) && ((evtBuf[0]) <= 8)) + ? eventTypeNames[evtBuf[0]] + : "?"), + evtBuf[0], + evtBuf[1], + evtBuf[2], + evtBuf[2], + evtBuf[3], + evtBuf[3], + evtBuf[4], + evtBuf[4], + evtBuf[5], + evtBuf[5], + evtBuf[6], + evtBuf[6], + ((void *)(evtBuf[7]))); + } + + /* Event type */ + eventTypeIs = evtBuf[0]; + storeIntegerofObjectwithValue(0, arg, evtBuf[0]); + if (GIV(primFailCode)) { + return; + } + if (eventTypeIs == 6) { + for (i = 1; i <= 7; i += 1) { + value = evtBuf[i]; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(i, arg, value)); + assert(isNonImmediate(arg)); + if (oopisGreaterThanOrEqualTo(arg, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(arg + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(arg); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((arg + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),value); + } + } + else { + storeIntegerofObjectwithValue(1, arg, (evtBuf[1]) & MillisecondClockMask); + if (GIV(primFailCode)) { + return; + } + for (i = 2; i <= 7; i += 1) { + value = evtBuf[i]; + if ((((((usqIntptr_t)(value)) >> 60) + 1) & 15) <= 1) { + storeIntegerofObjectwithValue(i, arg, value); + } + else { + value = positive64BitIntegerFor(value); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(i, arg, value)); + assert(isNonImmediate(arg)); + if (oopisGreaterThanOrEqualTo(arg, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(arg + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(arg); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((arg + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),value); + } + } + } + + /* Event is Complex, assume evtBuf is populated correctly and return + Event time stamp */ + if (!GIV(primFailCode)) { + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } +} + + /* InterpreterPrimitives>>#primitiveGreaterOrEqual */ +static void +primitiveGreaterOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver >= integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveGreaterOrEqualLargeIntegers */ +EXPORT(void) +primitiveGreaterOrEqualLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr >= integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveGreaterThan */ +static void +primitiveGreaterThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver > integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveGreaterThanLargeIntegers */ +EXPORT(void) +primitiveGreaterThanLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr > integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveGrowMemoryByAtLeast */ +static void +primitiveGrowMemoryByAtLeast(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ammount; + usqInt segSize; + + ammount = longAt(GIV(stackPointer)); + if (!((((ammount) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if ((segSize = growOldSpaceByAtLeast((ammount >> 3)))) { + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),((segSize << 3) | 1)); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + } +} + + +/* Implement 28-bit hashMultiply for SmallInteger and LargePositiveInteger + receivers. + */ + + /* InterpreterPrimitives>>#primitiveHashMultiply */ +static void +primitiveHashMultiply(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + sqInt ok; + sqInt value; + + value = longAt(GIV(stackPointer)); + if ((((value) & 7) == 1)) { + value = (value >> 3); + } + else { + /* begin is:instanceOf:compactClassIndex: */ + if (((value & (tagMask())) != 0)) { + ok = 0; + goto l1; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(value))); + ccIndex = (longAt((void *)(value))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + /* end is:instanceOf:compactClassIndex: */ +l1: + if (!ok) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + value = SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((value + BaseHeaderSize))))); + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),(((((((usqInt) value)) * HashMultiplyConstant) & 0xFFFFFFF) << 3) | 1)); +} + + +/* Answer the heartbeat frequency in beats per second. If the argument is + true, reset the frequency measure. */ + + /* InterpreterPrimitives>>#primitiveHeartbeatFrequency */ +EXPORT(sqInt) +primitiveHeartbeatFrequency(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + unsigned int integerValue; + sqInt oop; + sqInt reset; + + reset = (GIV(argumentCount) == 1) + && ((longAt(GIV(stackPointer))) == GIV(trueObj)); + integerValue = ((unsigned int) (ioHeartbeatFrequency(reset))); + + /* begin positive32BitIntegerFor: */ + oop = (((usqInt)((((usqInt)integerValue)) & 0xFFFFFFFFU) << 3) | 1); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return 0; +} + + /* InterpreterPrimitives>>#primitiveHighBit */ +static void +primitiveHighBit(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + unsigned long highestBitZeroBased; + sqInt integerReceiverOop; + sqInt leadingZeroCount; + + integerReceiverOop = longAt(GIV(stackPointer)); + + /* Convert the receiver Oop to use a single tag bit */ + integerReceiverOop = ((integerReceiverOop) >> 2 /* (numSmallIntegerTagBits - 1) */) | 1; + if (integerReceiverOop < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } +# if defined(__GNUC__) + /* Note: in gcc, result is undefined if input is zero (for compatibility with BSR fallback when no CLZ instruction available). + but input is never zero because we pass the oop with tag bits set, so we are safe */ + leadingZeroCount = __builtin_clzll(integerReceiverOop); + assert(!((leadingZeroCount == 0))); + + /* Nice bit trick: 1-based high-bit is (32 - clz) - 1 to account for tag bit. + This is like two-complement - clz - 1 on 5 bits, or in other words a bit-invert operation clz ^16r1F */ + + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),(((usqInt)(leadingZeroCount ^ ((BytesPerWord * 8) - 1)) << 3) | 1)); +# elif (defined(_MSC_VER)) || (defined(__ICC)) + /* In MSVC, _lzcnt and _lzcnt64 builtins do not fallback to BSR when not supported by CPU + Instead of messing with __cpuid() we always use the BSR intrinsic + Setting this variable is useless, but VMMaker will generate an automatic initialization at a worse place if this isn't initialized explicitly. */ + highestBitZeroBased = 0; + + /* We do not even test the return value, because integerReceiverOop is never zero */ + _BitScanReverse64((&highestBitZeroBased), integerReceiverOop); + + /* thanks to the tag bit, the +1 operation for getting 1-based rank is not necessary */ + + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),((highestBitZeroBased << 3) | 1)); +# else // defined(__GNUC__) || (defined(_MSC_VER)) || (defined(__ICC)) + /* not gcc/clang, nor MSVC/ICC, you have to implement if your compiler provides useful builtins */ + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } +# endif // defined(__GNUC__) || (defined(_MSC_VER)) || (defined(__ICC)) +} + + +/* Return the value of the high resolution clock if this system has any. + The exact frequency of the high res clock is undefined specifically so + that we can use + processor dependent instructions (like RDTSC). The only use for the high + res clock is for + profiling where we can allocate time based on sub-msec resolution of the + high res clock. + If no high-resolution counter is available, the platform should return + zero. ar 6/22/2007 */ + + /* InterpreterPrimitives>>#primitiveHighResClock */ +EXPORT(sqInt) +primitiveHighResClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = positive64BitIntegerFor(ioHighResClock()); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return 0; +} + + +/* is the receiver/first argument the same object as the (last) argument?. + pop argumentCount because this can be used as a mirror primitive. */ + + /* InterpreterPrimitives>>#primitiveIdentical */ +static void +primitiveIdentical(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt otherObject; + char *sp; + sqInt thisObject; + + thisObject = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + otherObject = longAt(GIV(stackPointer)); + if ((/* isOopForwarded: */ + ((!(otherObject & (tagMask())))) + && ((!((longAt((void *)(otherObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + || ((GIV(argumentCount) > 1) + && (/* isOopForwarded: */ + ((!(thisObject & (tagMask())))) + && ((!((longAt((void *)(thisObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (thisObject == otherObject + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveIdentityHash */ +static void +primitiveIdentityHash(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt hash; + usqInt hashUsqInt; + sqInt integer; + sqInt thisReceiver; + + thisReceiver = longAt(GIV(stackPointer)); + if ((((thisReceiver & (tagMask())) != 0)) + || ((GIV(argumentCount) > 0) + && ((!((longAt((void *)(thisReceiver))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = (GIV(argumentCount) > 0 + ? PrimErrBadArgument + : PrimErrBadReceiver); + return; + } + + /* fail if forwarded; the retry machinery will fix things up. */ + + /* begin hashBitsOf: */ + hash = (long32At((void *)(thisReceiver + 4))) & (identityHashHalfWordMask()); + if (!hash) { + /* cb 1/19/2017 18:34: + would like to assert + self assert: (coInterpreter addressCouldBeClassObj: objOop) not + but instance-specific behaviors that are instances of themselves may + fail this test. + eem 12/28/2021 11:52 I'm not sure this is the issue. The issue is only validating objOop as a behavior. */ + + /* begin newHashBitsOf: */ + /* begin newObjectHash */ + assert(!((GIV(lastHash) == 0))); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 2)); + GIV(lastHash) = GIV(lastHash) ^ ((((GIV(lastHash) << 7))) & (identityHashHalfWordMask())); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 3)); + hashUsqInt = GIV(lastHash); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(thisReceiver + 4),((((long32At((void *)(thisReceiver + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hashUsqInt & (identityHashHalfWordMask()))); + hash = hashUsqInt & (identityHashHalfWordMask()); + } + integer = hash; + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); +} + + +/* Answer an integer identifying the type of image. The image version number + may identify the format of the image (e.g. 32 or 64-bit word size) or + specific requirements + of the image (e.g. block closure support required). + + This is a named (not numbered) primitive in the null module (ie the VM) + */ + + /* InterpreterPrimitives>>#primitiveImageFormatVersion */ +EXPORT(sqInt) +primitiveImageFormatVersion(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = (((usqInt)((((usqInt)(/* imageFormatVersionForSnapshot */ + (GIV(multipleBytecodeSetsActive) + ? 68533 /* (imageFormatVersion bitOr: MultipleBytecodeSetsBitmask) */ + : 68021 /* imageFormatVersion */)))) & 0xFFFFFFFFU) << 3) | 1); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); + return 0; +} + + +/* When called with a single string argument, record the string as the + current image file name. + When called with zero arguments, return a string containing the current + image file name. + */ + + /* InterpreterPrimitives>>#primitiveImageName */ +static void +primitiveImageName(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + sqInt isString; + sqInt okToRename; + sqInt s; + void *sCRIfn; + sqInt sz; + + if (GIV(argumentCount) == 1) { + s = longAt(GIV(stackPointer)); + + /* begin isInstanceOfClassByteString: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((s & (tagMask())) != 0)) { + isString = 0; + goto l1; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(s))); + ccIndex = (longAt((void *)(s))) & (classIndexMask()); + isString = ClassByteStringCompactIndex == ccIndex; + /* end isInstanceOfClassByteString: */ +l1: + if (!isString) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* If the security plugin can be loaded, use it to check for rename permission. + If not, assume it's ok */ + sCRIfn = ioLoadFunctionFrom("secCanRenameImage", "SecurityPlugin"); + if (sCRIfn) { + okToRename = ((sqInt (*)(void))sCRIfn)(); + if (!okToRename) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + } + imageNamePutLength(s + BaseHeaderSize, numBytesOf(s)); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + return; + } + sz = imageNameSize(); + s = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord())))))), sz); + imageNameGetLength(s + BaseHeaderSize, sz); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),s); +} + + +/* For a Smalllnteger, answer itself. + For a Character, answer its code as an unsigned integer. + For a SmallFloat, answer the signed, but unadjusted bit pattern (so as to + keep the result a SmallInteger). + This is a good value for an immediate's hash. */ + + /* InterpreterPrimitives>>#primitiveImmediateAsInteger */ +static void +primitiveImmediateAsInteger(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + usqInt value; + + value = 0; + oop = longAt(GIV(stackPointer)); + + /* begin immediateAsInteger:ifFail: */ + if ((((oop) & 7) == 1)) { + value = (oop >> 3); + goto l1; + } + if (((oop & (characterTag())) != 0)) { + value = ((((usqInt)oop))) >> (numTagBits()); + goto l1; + } + if (((oop & (smallFloatTag())) != 0)) { + /* begin rotatedFloatBitsOf: */ + assert(isImmediateFloat(oop)); + value = (((((usqInt)oop))) >> ((numTagBits()) + 1)) + ((((oop & (1U << (numTagBits()))) != 0) + ? ((((usqInt)-1)) << ((64 - (numTagBits())) - 1)) + : 0)); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + /* end immediateAsInteger:ifFail: */ +l1: + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),((value << 3) | 1)); +} + + +/* Register the input semaphore. If the argument is not a + Semaphore, unregister the current input semaphore. */ + + /* InterpreterPrimitives>>#primitiveInputSemaphore */ +static void +primitiveInputSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + + arg = longAt(GIV(stackPointer)); + if ((((arg) & 7) == 1)) { + /* If arg is integer, then use it as an index + into the external objects array and install it + as the new event semaphore */ + ioSetInputSemaphore((arg >> 3)); + if (!GIV(primFailCode)) { + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + return; + } + + /* old code for compatibility */ + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; +} + + +/* Return an integer indicating the reason for the most recent input + interrupt. + */ + + /* InterpreterPrimitives>>#primitiveInputWord */ +static void +primitiveInputWord(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),(((usqInt)0 << 3) | 1)); +} + + /* InterpreterPrimitives>>#primitiveIntegerAt */ +static void +primitiveIntegerAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classPointer; + sqInt fmt; + sqInt fmtSqInt; + sqInt index; + sqInt numFixed; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt rcvr; + sqInt value; + + /* begin primitiveSpurIntegerAt */ + index = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l1; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + goto l1; + } + + /* begin numBytesOfBytes: */ + fmtSqInt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert(fmtSqInt >= (firstByteFormat())); + numSlots = ((((/* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) << (shiftForWord()))) - (fmtSqInt & 7); + if ((((usqInt)index)) < numSlots) { + value = byteAt((void *)((rcvr + BaseHeaderSize) + index)); + if (value > 0x7F) { + value -= 0x100; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)value << 3) | 1)); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt >= (firstShortFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 1; + if ((((usqInt)index)) < numSlots) { + value = shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 1))))); + if (value > 0x7FFF) { + value -= 0x10000; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)value << 3) | 1)); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + oop = signed64BitIntegerFor(long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3)))))); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt >= (firstLongFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + value = long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2))))); + if (value > 0x7FFFFFFF) { + value -= 0x100000000LL; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)value << 3) | 1)); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if ((fmt <= 5 /* lastPointerFormat */) + && (/* isIndexableFormat: */ + (fmt >= (arrayFormat())) + && ((fmt <= (weakArrayFormat())) + || (fmt >= (sixtyFourBitIndexableFormat()))))) { + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt == (arrayFormat())) { + if ((((usqInt)index)) < numSlots) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))))); + goto l1; + } + } + + /* begin numFixedSlotsOf: */ + classPointer = fetchClassOfNonImm(rcvr); + classFormat = ((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + numFixed = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + if ((((index + 1) >= numFixed) && ((index + 1) <= numSlots))) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index + numFixed)) << (shiftForWord()))))))); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + /* end primitiveSpurIntegerAt */ +l1:; +} + + /* InterpreterPrimitives>>#primitiveIntegerAtPut */ +static void +primitiveIntegerAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classPointer; + sqInt fieldIndex; + sqInt fmt; + sqInt fmtSqInt; + sqInt index; + sqInt numFixed; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqLong value; + sqInt valueOop; + + /* begin primitiveSpurIntegerAtPut */ + valueOop = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + value = signed64BitValueOf(valueOop); + if (!((!GIV(primFailCode)) + && ((((index) & 7) == 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l1; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l1; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + goto l1; + } + if (!((((usqInt)((((value) >> 7)) + 1))) <= 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + + /* begin numBytesOfBytes: */ + fmtSqInt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert(fmtSqInt >= (firstByteFormat())); + numSlots = ((((/* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) << (shiftForWord()))) - (fmtSqInt & 7); + if ((((usqInt)index)) < numSlots) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + index),value); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt >= (firstShortFormat())) { + if (!((((usqInt)((((value) >> 15)) + 1))) <= 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 1; + if ((((usqInt)index)) < numSlots) { + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 1)))),value); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + /* No range check on value in this case because signed64BitValueOf: performed it above. */ + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3)))),value); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt >= (firstLongFormat())) { + if (!((((usqInt)((((value) >> 0x1F)) + 1))) <= 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2)))),value); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if ((fmt <= 5 /* lastPointerFormat */) + && (/* isIndexableFormat: */ + (fmt >= (arrayFormat())) + && ((fmt <= (weakArrayFormat())) + || (fmt >= (sixtyFourBitIndexableFormat()))))) { + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt == (arrayFormat())) { + if ((((usqInt)index)) < numSlots) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, rcvr, valueOop)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valueOop & (tagMask())))) + && (oopisLessThan(valueOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),valueOop); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + } + + /* begin numFixedSlotsOf: */ + classPointer = fetchClassOfNonImm(rcvr); + classFormat = ((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + numFixed = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + if ((((index + 1) >= numFixed) && ((index + 1) <= numSlots))) { + fieldIndex = index + numFixed; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, rcvr, valueOop)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valueOop & (tagMask())))) + && (oopisLessThan(valueOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valueOop); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + /* end primitiveSpurIntegerAtPut */ +l1:; +} + + +/* Primitive. Answer the number of interrupt checks per milliseconds that we + execute on this machine. This can be used to adjust the sub-msecs profiler + to check (roughly) + n times per millisecond. + */ + + /* InterpreterPrimitives>>#primitiveInterruptChecksPerMSec */ +EXPORT(sqInt) +primitiveInterruptChecksPerMSec(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + if (GIV(argumentCount)) { + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + oop = (((usqInt)(ioHeartbeatMilliseconds()) << 3) | 1); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); + return 0; +} + + +/* Register the user interrupt semaphore. If the argument is + not a Semaphore, unregister the current interrupt + semaphore. */ + + /* InterpreterPrimitives>>#primitiveInterruptSemaphore */ +static void +primitiveInterruptSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + + arg = longAt(GIV(stackPointer)); + if ((arg == GIV(nilObj)) + || (/* isSemaphoreOop: */ + ((!(arg & (tagMask())))) + && (((longAt((void *)(arg))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))))) { + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheInterruptSemaphore, GIV(specialObjectsOop), arg)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(arg & (tagMask())))) + && (oopisLessThan(arg, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheInterruptSemaphore) << (shiftForWord()))))),arg); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } +} + + +/* Answer if running on a big endian machine. */ + + /* InterpreterPrimitives>>#primitiveIsBigEnder */ +EXPORT(sqInt) +primitiveIsBigEnder(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop:thenPushBool: */ + longAtput(GIV(stackPointer),GIV(falseObj)); + return 0; +} + + +/* Answer if the receiver is pinned, i.e. immobile. */ + + /* InterpreterPrimitives>>#primitiveIsPinned */ +static void +primitiveIsPinned(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt obj; + char *sp; + sqInt trueOrFalse; + + obj = longAt(GIV(stackPointer)); + if ((((obj & (tagMask())) != 0)) + || ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + trueOrFalse = ((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0; + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (trueOrFalse + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; +} + + /* InterpreterPrimitives>>#primitiveLessOrEqual */ +static void +primitiveLessOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver <= integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveLessOrEqualLargeIntegers */ +EXPORT(void) +primitiveLessOrEqualLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr <= integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveLessThan */ +static void +primitiveLessThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver < integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveLessThanLargeIntegers */ +EXPORT(void) +primitiveLessThanLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr < integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + +/* Primitive. Return the n-th builtin module name. */ + + /* InterpreterPrimitives>>#primitiveListBuiltinModule */ +static void +primitiveListBuiltinModule(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt integerPointer; + char *moduleName; + + if (!(GIV(argumentCount) == 1)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + index = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + index = 0; + } + if (GIV(primFailCode) + || (index <= 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + if ((moduleName = ioListBuiltinModule(index))) { + methodReturnString(moduleName); + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); + } +} + + +/* Primitive. Answer the n-th loaded external module name. */ + + /* InterpreterPrimitives>>#primitiveListExternalModule */ +static void +primitiveListExternalModule(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt integerPointer; + char *moduleName; + + if (!(GIV(argumentCount) == 1)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + index = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + index = 0; + } + if (GIV(primFailCode) + || (index <= 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + if ((moduleName = ioListLoadedModule(index))) { + methodReturnString(moduleName); + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); + } +} + + +/* This primitive is called from Smalltalk as... + loadSegmentFrom: aWordArray outPointers: anArray. + + This primitive will load a binary image segment created by + primitiveStoreImageSegment. It expects the outPointer array to be of the + proper size, and the wordArray to be well + formed. It will return as its value the original array of roots, and the + erstwhile segmentWordArray will have been truncated to a size of one word, + i.e. retaining the version + stamp. If this primitive should fail, the segmentWordArray will, sadly, + have been reduced to + an unrecognizable and unusable jumble. But what more could you have done + with it anyway? + [How about saving it so the system functions as primitives are intended? + eem 5/9/2017 16:31] + + In Spur, if the primitive succeeds, the segmentWordArray is also becomed + into the array of loaded + objects, to allow fixing up of loaded objects directly without nextObject, + which Spur doesn't support. */ + + /* InterpreterPrimitives>>#primitiveLoadImageSegment */ +static void +primitiveLoadImageSegment(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt outPointerArray; + sqInt result; + sqInt segmentWordArray; + + outPointerArray = longAt(GIV(stackPointer)); + segmentWordArray = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* Essential type checks */ + if (!((/* isArray: */ + ((!(outPointerArray & (tagMask())))) + && (((byteAt((void *)(outPointerArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (/* isWords: */ + ((!(segmentWordArray & (tagMask())))) + && (((((byteAt((void *)(segmentWordArray + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(segmentWordArray + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1))))))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Must be indexable pointers + Must be indexable words + the engine returns the roots array which was first in the segment, or an error code on failure. */ + result = loadImageSegmentFromoutPointers(segmentWordArray, outPointerArray); + if (oopisGreaterThan(result, segmentWordArray)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),result); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = result; + } +} + + +/* Return the value of the microsecond clock in the local timezone as an + integer. This is the number of microseconds since the Smalltalk epoch, + 1901/1/1 12:00am. + The microsecond clock is at least 60 bits wide which means it'll get to + around August + 38435 before it wraps around. Be sure to put it on your calendar. This + primitive accesses the time as answered by the OS. */ + + /* InterpreterPrimitives>>#primitiveLocalMicrosecondClock */ +static void +primitiveLocalMicrosecondClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = positive64BitIntegerFor(ioLocalMicrosecondsNow()); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Natural log of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveLogN */ +static void +primitiveLogN(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(log(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + +/* Register the low-space semaphore. If the argument is not a + Semaphore, unregister the current low-space Semaphore. */ + + /* InterpreterPrimitives>>#primitiveLowSpaceSemaphore */ +static void +primitiveLowSpaceSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + + arg = longAt(GIV(stackPointer)); + if ((arg == GIV(nilObj)) + || (/* isSemaphoreOop: */ + ((!(arg & (tagMask())))) + && (((longAt((void *)(arg))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))))) { + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheLowSpaceSemaphore, GIV(specialObjectsOop), arg)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(arg & (tagMask())))) + && (oopisLessThan(arg, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheLowSpaceSemaphore) << (shiftForWord()))))),arg); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } +} + + /* InterpreterPrimitives>>#primitiveMakePoint */ +static void +primitiveMakePoint(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + usqInt newObj; + usqInt numBytes; + usqInt pt; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + arg = longAt(GIV(stackPointer)); + if (!(/* isFloatOrInt: */ + (((arg & (tagMask())) != 0) + ? !(((arg & (characterTag())) != 0)) + : ((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* begin eeInstantiatePoint */ + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert(((YIndex + 1) >= 0) + && ((knownClassAtIndex(ClassPointCompactIndex)) != GIV(nilObj))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(knownClassAtIndex(ClassPointCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert((YIndex + 1) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (((YIndex + 1) < 1 + ? 8 /* allocationUnit */ + : (YIndex + 1) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + pt = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) (YIndex + 1))) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + ClassPointCompactIndex); + GIV(freeStart) += numBytes; + pt = newObj; + /* end eeInstantiatePoint */ +l1: + + /* No need to check since new object is always new. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pt)) + && (!(isForwarded(pt)))); + assert(validStorePointerUncheckedArgs(XIndex, pt, rcvr)); + longAtput((void *)((pt + BaseHeaderSize) + ((((usqInt)(XIndex) << (shiftForWord()))))),rcvr); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pt)) + && (!(isForwarded(pt)))); + assert(validStorePointerUncheckedArgs(YIndex, pt, arg)); + longAtput((void *)((pt + BaseHeaderSize) + ((((usqInt)(YIndex) << (shiftForWord()))))),arg); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),pt); +} + + /* InterpreterPrimitives>>#primitiveMaxIdentityHash */ +static void +primitiveMaxIdentityHash(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),(((usqInt)(identityHashHalfWordMask()) << 3) | 1)); +} + + +/* Return the value of the millisecond clock as an integer. Note that the + millisecond clock wraps around periodically. On some platforms it can wrap + daily. The range is limited to SmallInteger maxVal / 2 to allow delays of + up to that length without overflowing a SmallInteger. + */ + + /* InterpreterPrimitives>>#primitiveMillisecondClock */ +static void +primitiveMillisecondClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = ((((ioMSecs()) & MillisecondClockMask) << 3) | 1); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Provide access to the millisecond clock mask to support calculation + of durations based on the millisecond clock value. */ + + /* InterpreterPrimitives>>#primitiveMillisecondClockMask */ +EXPORT(sqInt) +primitiveMillisecondClockMask(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),(((usqInt)MillisecondClockMask << 3) | 1)); + return 0; +} + + /* InterpreterPrimitives>>#primitiveMod */ +static void +primitiveMod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt mod; + + mod = doPrimitiveModby(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)(mod)) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)mod << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveModLargeIntegers */ +EXPORT(void) +primitiveModLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (!b) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return; + } + result = a % b; + + /* Handle remainder of same sign as argument */ + if (result) { + if (!(bIsNegative == aIsNegative)) { + result = b - result; + } + } + oopResult = magnitude64BitIntegerForneg(result, bIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Given one boolean parameter, set multipleBytecodeSetsActive to inform + the VM that alternate bytecode sets such as SistaV1 are now in use and + that the image format number should be updated accordingly. With zero + parameters, answer the current value of multipleBytecodeSetsActive. */ + + /* InterpreterPrimitives>>#primitiveMultipleBytecodeSetsActive */ +EXPORT(sqInt) +primitiveMultipleBytecodeSetsActive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + if (GIV(argumentCount) > 1) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + if (GIV(argumentCount) == 1) { + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + GIV(multipleBytecodeSetsActive) = 1; + } + else { + if (!((longAt(GIV(stackPointer))) == GIV(falseObj))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + GIV(multipleBytecodeSetsActive) = 0; + } + } + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (GIV(multipleBytecodeSetsActive) + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return 0; +} + + /* InterpreterPrimitives>>#primitiveMultiply */ +static void +primitiveMultiply(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArg; + sqInt integerPointer; + sqInt integerRcvr; + sqInt integerResult; + sqInt overflow; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + integerRcvr = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerRcvr = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + integerArg = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArg = 0; + } + if (!GIV(primFailCode)) { + overflow = (integerRcvr > 0 + ? (integerArg > 0 + ? integerRcvr > ((MaxSmallInteger) / integerArg) + : integerArg < ((MinSmallInteger) / integerRcvr)) + : (integerArg > 0 + ? integerRcvr < ((MinSmallInteger) / integerArg) + : (integerRcvr < 0) + && (integerArg < ((MaxSmallInteger) / integerRcvr)))); + if (overflow) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + else { + integerResult = integerRcvr * integerArg; + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerResult << 3) | 1)); + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveMultiplyLargeIntegers */ +EXPORT(void) +primitiveMultiplyLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (GIV(primFailCode)) { + return; + } + + /* check for overflow */ + if ((a > 1) + && ((b > 1) + && (a > (0xFFFFFFFFFFFFFFFFULL / b)))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + result = a * b; + oopResult = magnitude64BitIntegerForneg(result, aIsNegative != bIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + /* InterpreterPrimitives>>#primitiveNew */ +static void +primitiveNew(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt obj; + sqInt reasonCode; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + + /* Allocate a new fixed-size instance. Fail if the allocation would leave + less than lowSpaceThreshold bytes free. This *will not* cause a GC :-) */ + if ((obj = instantiateClass(longAt(GIV(stackPointer))))) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + reasonCode = (isFixedSizePointerFormat((((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + /* InterpreterPrimitives>>#primitiveNewMethod */ +static void +primitiveNewMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bytecodeCount; + sqInt class; + sqInt classFormat; + sqInt classIndex; + sqInt err; + int hash; + sqInt header; + sqInt i; + sqInt instSpec; + usqInt literalCount; + sqInt newObj; + usqInt newObjUsqInt; + usqInt numBytes; + usqInt numSlots; + usqInt p; + sqInt reasonCode; + usqInt size; + sqInt theMethod; + usqInt toDoLimit; + + header = longAt(GIV(stackPointer)); + bytecodeCount = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(((((header) & 7) == 1)) + && (((((bytecodeCount) & 7) == 1)) + && (((bytecodeCount = (bytecodeCount >> 3))) >= 0)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + class = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + literalCount = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + size = ((literalCount + LiteralStart) * BytesPerOop) + bytecodeCount; + + /* begin instantiateCompiledMethodClass:indexableSize: */ + classFormat = ((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + if (instSpec != (firstCompiledMethodFormat())) { + theMethod = null; + goto l2; + } + numSlots = (size + 7) / 8; + instSpec += (8 - size) & 7; + + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(class)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(class + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(class) + ? ((err = enterIntoClassTable(class)) + ? -err + : (long32At((void *)(class + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + theMethod = null; + goto l2; + } + if (numSlots > ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (numSlots > (0x10000000000LL)) { + theMethod = null; + goto l2; + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpec, classIndex); + } + else { + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if (((numSlots) >> 56) > 0) { + newObj = null; + goto l1; + } + newObjUsqInt = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObjUsqInt = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, instSpec, classIndex); + goto l1; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObjUsqInt),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObjUsqInt),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObjUsqInt % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newObj = newObjUsqInt; + /* end allocateSlots:format:classIndex: */ +l1:; + } + if (newObj) { + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + theMethod = newObj; + /* end instantiateCompiledMethodClass:indexableSize: */ +l2: + if (!theMethod) { + reasonCode = (isCompiledMethodFormat((((usqInt)((((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + return; + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theMethod)) + && (!(isForwarded(theMethod)))); + assert(validStorePointerUncheckedArgs(HeaderIndex, theMethod, header)); + longAtput((void *)((theMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))),header); + for (i = 1; i <= literalCount; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theMethod)) + && (!(isForwarded(theMethod)))); + assert(validStorePointerUncheckedArgs(i, theMethod, GIV(nilObj))); + longAtput((void *)((theMethod + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),theMethod); +} + + +/* Allocate a new indexable instance. Fail if the allocation would leave less + than lowSpaceThreshold bytes free. May cause a GC. + */ + + /* InterpreterPrimitives>>#primitiveNewWithArg */ +static void +primitiveNewWithArg(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt instSpec; + sqInt obj; + sqInt reasonCode; + usqIntptr_t size; + sqInt value; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + size = null; + goto l1; + } + size = value; + goto l1; + } + + /* don't inline the rare case */ + size = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* positiveMachineIntegerValueOf: succeeds only for non-negative integers. */ + if ((obj = instantiateClassindexableSize(longAt(GIV(stackPointer) + (1 * BytesPerWord)), size))) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + instSpec = (((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer) + (1 * BytesPerWord))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + reasonCode = ((/* isIndexableFormat: */ + (instSpec >= (arrayFormat())) + && ((instSpec <= (weakArrayFormat())) + || (instSpec >= (sixtyFourBitIndexableFormat())))) + && (!(instSpec >= (firstCompiledMethodFormat()))) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + /* InterpreterPrimitives>>#primitiveNextInstance */ +static void +primitiveNextInstance(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt actualObj; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt object; + sqInt subsequentObject; + + object = longAt(GIV(stackPointer)); + if (!(((object & (tagMask())) != 0))) { + /* begin instanceAfter: */ + actualObj = object; + classIndex = (longAt((void *)(object))) & (classIndexMask()); + + /* begin isYoungObject: */ + assert(isNonImmediate(object)); + if (oopisLessThan(object, GIV(oldSpaceStart))) { + if (oopisGreaterThanOrEqualToandLessThan(object, ((GIV(eden)).start), GIV(freeStart))) { + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(actualObj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + actualObj = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + actualObj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + if (!(oopisLessThan(actualObj, GIV(freeStart)))) break; + if (classIndex == ((longAt((void *)(actualObj))) & (classIndexMask()))) { + subsequentObject = actualObj; + goto l4; + } + } + actualObj = (oopisGreaterThan(GIV(pastSpaceStart), ((GIV(pastSpace)).start)) + ? objectStartingAt(((GIV(pastSpace)).start)) + : GIV(nilObj)); + } + if (oopisGreaterThanOrEqualToandLessThan(actualObj, ((GIV(pastSpace)).start), GIV(pastSpaceStart))) { + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(actualObj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + actualObj = GIV(pastSpaceStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + actualObj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l2:; + if (!(oopisLessThan(actualObj, GIV(pastSpaceStart)))) break; + if (classIndex == ((longAt((void *)(actualObj))) & (classIndexMask()))) { + subsequentObject = actualObj; + goto l4; + } + } + actualObj = GIV(nilObj); + } + } + + /* it almost never is because usually this is preceded by a send of initialInstanceOf: */ + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(actualObj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + actualObj = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + actualObj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3:; + if (!(oopisLessThan(actualObj, GIV(endOfMemory)))) break; + if (classIndex == ((longAt((void *)(actualObj))) & (classIndexMask()))) { + subsequentObject = actualObj; + goto l4; + } + } + subsequentObject = null; + /* end instanceAfter: */ +l4: + if (subsequentObject) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),subsequentObject); + return; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } +} + + +/* Return the object following the receiver in the heap. Return the + SmallInteger zero when there are no more objects. + */ + + /* InterpreterPrimitives>>#primitiveNextObject */ +static void +primitiveNextObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt instance; + + if ((((longAt(GIV(stackPointer))) & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if ((instance = accessibleObjectAfter(longAt(GIV(stackPointer))))) { + assert(isInMemory(instance)); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),instance); + } + else { + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)0 << 3) | 1)); + } +} + + +/* A placeholder for primitives that haven't been implemented or are being + withdrawn gradually. Just absorbs any arguments and returns the receiver. + */ + + /* InterpreterPrimitives>>#primitiveNoop */ +static void +primitiveNoop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + /* InterpreterPrimitives>>#primitiveNotEqual */ +static void +primitiveNotEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + int result; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver != integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + result = (signedMachineIntegerValueOf(integerReceiver)) != (signedMachineIntegerValueOf(integerArgument)); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (result + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveNotEqualLargeIntegers */ +EXPORT(void) +primitiveNotEqualLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr != integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + +/* is the receiver/first argument not the same object as the (last) + argument?. pop argumentCount because this can be used as a mirror + primitive. + */ +/* is the receiver/first argument the same object as the (last) argument?. + pop argumentCount because this can be used as a mirror primitive. */ + + /* InterpreterPrimitives>>#primitiveNotIdentical */ +static void +primitiveNotIdentical(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt otherObject; + char *sp; + sqInt thisObject; + + thisObject = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + otherObject = longAt(GIV(stackPointer)); + if ((/* isOopForwarded: */ + ((!(otherObject & (tagMask())))) + && ((!((longAt((void *)(otherObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + || ((GIV(argumentCount) > 1) + && (/* isOopForwarded: */ + ((!(thisObject & (tagMask())))) + && ((!((longAt((void *)(thisObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (thisObject != otherObject + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + +/* This primitive is called from Squeak as... + arrayOfRoots uniquelyAccessibleObjects */ +/* This primitive answers an array of the receiver and every object in its + proper tree of subParts (ie, that is not refered to from anywhere else + outside the tree). + */ +/* This primitive could be used to implement the primitiveStoreImageSegment + segment, thanks to a suggestion from Igor Stassenko. Currently it is + used only to debug that primitive. */ + + /* InterpreterPrimitives>>#primitiveObjectsAccessibleFromRoots */ +static void +primitiveObjectsAccessibleFromRoots(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayOfRoots; + sqInt result; + + arrayOfRoots = longAt(GIV(stackPointer)); + + /* Essential type checks */ + if (!(/* isArray: */ + ((!(arrayOfRoots & (tagMask())))) + && (((byteAt((void *)(arrayOfRoots + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Must be indexable pointers */ + result = objectsAccessibleFromRoots(arrayOfRoots); + if (((((result) & 7) == 1)) + && (((result >> 3)) == PrimErrNoMemory)) { + fullGC(); + arrayOfRoots = longAt(GIV(stackPointer)); + result = objectsAccessibleFromRoots(arrayOfRoots); + } + if ((((result) & 7) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = (result >> 3); + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); + } +} + + +/* primitivePathTo: anObject using: stack followWeak: boolean + Answer a path to anObject from the root that does not pass through + the current context */ + + /* InterpreterPrimitives>>#primitivePathToUsing */ +EXPORT(sqInt) +primitivePathToUsing(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt current; + sqInt err; + sqInt fmt; + usqInt freeStartAtStart; + sqInt header; + sqInt headerSqInt; + sqInt i; + sqInt index; + sqInt iSqInt; + sqInt methodField; + sqInt next; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt path; + sqInt sp; + sqInt stack; + sqInt stackp; + sqInt stackSize; + sqInt toDoLimit; + sqInt toDoLimitSqInt; + + err = 0; + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (!(GIV(argumentCount) >= 2)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + if (!(((longAt(GIV(stackPointer))) == GIV(trueObj)) + || ((longAt(GIV(stackPointer))) == GIV(falseObj)))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + stack = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin pathTo:using:followWeak: */ + if (!(/* isArray: */ + ((!(stack & (tagMask())))) + && (((byteAt((void *)(stack + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + err = PrimErrBadArgument; + goto l5; + } + assert(allObjectsUnmarked()); + + /* check no allocations during search */ + freeStartAtStart = GIV(freeStart); + beRootIfOld(stack); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(stack + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(stack + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(stack - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + stackSize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + stackSize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + stackSize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + stackSize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + stackSize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + stackSize = 0; + /* end lengthOf: */ +l1: + + /* begin mark: */ + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(stack))); + byteAtput((void *)(stack + (markBitsByteOffset())),(byteAt((void *)(stack + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* no need. the current context is not reachable from the active process (suspendedContext is nil) + objectMemory mark: self activeProcess. */ + current = GIV(specialObjectsOop); + + /* begin mark: */ + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(current))); + byteAtput((void *)(current + (markBitsByteOffset())),(byteAt((void *)(current + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(current + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(current + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(current - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + index = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + index = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + index = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + index = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + index = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + index = 0; + /* end lengthOf: */ +l2: + stackp = 0; + while (1) { + while (((index -= 1)) >= -1) { + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)current)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)current)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)current)) <= (((usqInt)GIV(pages)))))))) { + next = (index >= 0 + ? fieldofFrame(index, ((char *) current)) + : GIV(nilObj)); + } + else { + if (index >= 0) { + next = (((longAt((void *)(current))) & (classIndexMask())) == ClassMethodContextCompactIndex + ? fieldOrSenderFPofContext(index, current) + : longAt((void *)((current + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))))); + } + else { + next = fetchClassOfNonImm(current); + } + } + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)next)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)next)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)next)) <= (((usqInt)GIV(pages)))))))) { + assert(isFrameonPage(((char *) next), stackPageFor(((char *) next)))); + } + else { + if (next >= heapBase) { + assert(checkOkayOop(next)); + } + } + if (next == (longAt(GIV(stackPointer) + (2 * BytesPerWord)))) { + assert(freeStartAtStart == (GIV(freeStart))); + unmarkAfterPathTo(); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(stackp, stack, current)); + assert(isNonImmediate(stack)); + if (oopisGreaterThanOrEqualTo(stack, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(current & (tagMask())))) + && (oopisLessThan(current, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(stack + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(stack); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(stackp) << (shiftForWord()))))),current); + pruneStackstackp(stack, stackp); + err = 0; + goto l5; + } + if (((!(next & (smallIntegerTag())))) + && ((/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)next)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)next)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)next)) <= (((usqInt)GIV(pages))))))) + ? !(frameIsMarked(next)) + : (next >= heapBase) + && ((!(((byteAt((void *)(next + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) + && (((/* isPointers: */ + ((!(next & (tagMask())))) + && (((byteAt((void *)(next + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + || (((byteAt((void *)(next + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((longAt(GIV(stackPointer))) == GIV(trueObj)) + || (!(isWeakNonImm(next))))))))) { + if ((stackp + 2) > stackSize) { + assert(freeStartAtStart == (GIV(freeStart))); + unmarkAfterPathTo(); + + /* begin nilFieldsOf: */ + toDoLimit = ((assert((classIndexOf(stack)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(stack + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(stack - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(i, stack, GIV(nilObj))); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } + err = PrimErrBadIndex; + goto l5; + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(stackp, stack, current)); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(stackp) << (shiftForWord()))))),current); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(stackp + 1, stack, (((usqInt)index << 3) | 1))); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)((stackp + 1)) << (shiftForWord()))))),(((usqInt)index << 3) | 1)); + stackp += 2; + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)(((char *) next)))) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)(((char *) next)))) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)(((char *) next)))) <= (((usqInt)GIV(pages)))))))) { + /* begin markFrame: */ + methodField = longAt((void *)(next + FoxMethod)); + if ((((usqInt)methodField)) < (startOfMemory())) { + longAtput((void *)(next + FoxMethod),methodField | 4); + } + else { + longAtput((void *)(next + FoxIFrameFlags),(longAt((void *)(next + FoxIFrameFlags))) | 2); + } + index = CtxtTempFrameStart + (stackPointerIndexForFrame(((char *) next))); + } + else { + /* begin mark: */ + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(next))); + byteAtput((void *)(next + (markBitsByteOffset())),(byteAt((void *)(next + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(next + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(next))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((next + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l3; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(next))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l3: + index = ((usqInt) (CtxtTempFrameStart + contextSize)); + goto l4; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(next)) > (isForwardedObjectClassIndexPun())); + index = (((numSlotsUsqInt = byteAt((void *)(next + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(next - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + goto l4; + } + if (fmt == (forwardedFormat())) { + index = 1; + goto l4; + } + if (fmt < (firstCompiledMethodFormat())) { + index = 0; + goto l4; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(next)); + headerSqInt = longAt((void *)((next + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + index = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l4:; + } + current = next; + } + } + if (current == GIV(specialObjectsOop)) { + assert(freeStartAtStart == (GIV(freeStart))); + unmarkAfterPathTo(); + + /* begin nilFieldsOf: */ + toDoLimitSqInt = ((assert((classIndexOf(stack)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(stack + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(stack - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) - 1; + for (iSqInt = 0; iSqInt <= toDoLimitSqInt; iSqInt += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(iSqInt, stack, GIV(nilObj))); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord()))))),GIV(nilObj)); + } + err = PrimErrNotFound; + goto l5; + } + index = ((longAt((void *)((stack + BaseHeaderSize) + ((((usqInt)((stackp - 1)) << (shiftForWord()))))))) >> 3); + current = longAt((void *)((stack + BaseHeaderSize) + ((((usqInt)((stackp - 2)) << (shiftForWord())))))); + stackp -= 2; + } + /* end pathTo:using:followWeak: */ +l5: + if (err) { + return (GIV(primFailCode) = err); + } + path = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),path); + return 0; +} + + /* InterpreterPrimitives>>#primitivePerformInSuperclass */ +static void +primitivePerformInSuperclass(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt argumentArray; + usqInt arraySize; + sqInt currentClass; + sqInt delta; + sqInt index; + sqInt lookupClass; + usqInt numSlots; + sqInt objOop; + sqInt offset; + sqInt performArgCount; + sqInt rcvr; + usqInt savedNewMethod; + sqInt selector; + sqInt tagBits; + sqInt top; + + rcvr = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + lookupClass = longAt(GIV(stackPointer)); + if (GIV(argumentCount) != 3) { + if (GIV(argumentCount) != 4) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + } + + /* e.g. object:perform:withArguments:inClass: */ + currentClass = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)); + while (currentClass != lookupClass) { + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOop); + } + currentClass = objOop; + if (currentClass == GIV(nilObj)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + selector = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + argumentArray = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin primitiveObject:perform:withArguments:lookedUpIn: */ + if (!(/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + + /* Check if number of arguments is reasonable; MaxNumArgs isn't available + so just use LargeContextSize */ + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (arraySize > (LargeContextSlots - CtxtTempFrameStart)) { + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + goto l1; + } + performArgCount = GIV(argumentCount); + + /* Recursion check: + | a | + a := Array with: #perform:withArguments: with: nil. + a at: 2 put: a. + a perform: a first withArguments: a. + | a | + a := Array with: #perform:withArguments: with: (Array with: #perform:withArguments: with: nil). + a last at: 2 put: a. + a perform: a first withArguments: a */ + if (!GIV(performWithArgumentsRecursionGuard)) { + GIV(performWithArgumentsRecursionGuard) = (lookupClass + ? longAt(GIV(stackPointer) + (1 * BytesPerWord)) + : longAt(GIV(stackPointer))); + } + + /* Push newMethod to save it in case of failure, + then push the actual receiver and the args in the array. */ + savedNewMethod = GIV(newMethod); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* Copy the arguments to the stack, in case of MNU, and lookup */ + for (index = 1; index <= arraySize; index += 1) { + arg = longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord())))))); + if (arg == GIV(performWithArgumentsRecursionGuard)) { + GIV(performWithArgumentsRecursionGuard) = null; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),arg); + } + if (!GIV(performWithArgumentsRecursionGuard)) { + /* begin pop: */ + GIV(stackPointer) += (2 + arraySize) * BytesPerWord; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l1; + } + GIV(argumentCount) = arraySize; + GIV(messageSelector) = selector; + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + if (printOnTrace()) { + printActivationNameForSelectorstartClass(GIV(messageSelector), (lookupClass + ? lookupClass + : /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)))); + cr(); + } + findNewMethodInClassTag((lookupClass + ? classTagForClass(lookupClass) + : /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())))); + + /* Only test CompiledMethods for argument count - any other objects playacting as CMs will have to take their chances */ + if ((/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && ((argumentCountOf(GIV(newMethod))) != GIV(argumentCount))) { + /* Restore the state by popping the array entries, the actual receiver and the saved + newMethod, leaving the selector and array, and fail. N.B. If an MNU has happened + then argumentCount will match newMethod, so this code will not be reached. + These asserts check that an MNU has not occurred if the argumentCount doesn't match the newMethod. */ + assert((stackTop()) == ((arraySize == 0 + ? rcvr + : fetchPointerofObject(arraySize - 1, argumentArray)))); + assert(GIV(argumentCount) == arraySize); + + /* begin pop: */ + GIV(stackPointer) += (arraySize + 1) * BytesPerWord; + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(newMethod) = top; + + /* Must reset primitiveFunctionPointer for checkForAndFollowForwardedPrimitiveState */ + GIV(argumentCount) = performArgCount; + primitiveFunctionPointer = (lookupClass + ? primitivePerformInSuperclass + : primitivePerformWithArgs); + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + goto l1; + } + + /* Cannot fail this primitive from here-on. Slide the actual receiver and arguments down + to replace the perform arguments and saved newMethod and then execute the new + method. Use argumentCount not arraySize because an MNU may have changed it. */ + + /* +2 = receiver + saved newMethod */ + delta = BytesPerWord * (performArgCount + 2); + for (offset = (GIV(argumentCount) * BytesPerWord); offset >= 0; offset += (-BytesPerWord)) { + longAtput((GIV(stackPointer) + offset) + delta,longAt(GIV(stackPointer) + offset)); + } + + /* Part of the recursion guard above. To ensure we fail in the original method, restore + newMethod, and don't do the activateNewMethod implicit in executeNewMethod. */ + if ((primitiveFunctionPointer == primitivePerformWithArgs) + || (primitiveFunctionPointer == primitivePerformInSuperclass)) { + /* This should of course be a tail call, which could be done via setjmp/longjmp. + But this is vanity code. After all how often is a recursive invocation of + primitivePerformWithArgs et al made? */ + GIV(newMethod) = savedNewMethod; + GIV(argumentCount) = performArgCount; + + /* begin pop: */ + GIV(stackPointer) += (performArgCount + 2) * BytesPerWord; + + /* prevent inlining... */ + slowPrimitiveResponse(); + GIV(performWithArgumentsRecursionGuard) = null; + goto l1; + } + GIV(performWithArgumentsRecursionGuard) = null; + + /* begin pop: */ + GIV(stackPointer) += (performArgCount + 2) * BytesPerWord; + + /* prevent inlining... */ + executeNewMethod(); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + /* end primitiveObject:perform:withArguments:lookedUpIn: */ +l1:; +} + + /* InterpreterPrimitives>>#primitivePerformWithArgs */ +static void +primitivePerformWithArgs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt actualReceiver; + sqInt arg; + sqInt argumentArray; + usqInt arraySize; + sqInt delta; + sqInt index; + usqInt numSlots; + sqInt offset; + sqInt performArgCount; + usqInt savedNewMethod; + sqInt selector; + sqInt tagBits; + sqInt top; + + actualReceiver = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + selector = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + argumentArray = longAt(GIV(stackPointer)); + + /* begin primitiveObject:perform:withArguments:lookedUpIn: */ + if (!(/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + + /* Check if number of arguments is reasonable; MaxNumArgs isn't available + so just use LargeContextSize */ + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (arraySize > (LargeContextSlots - CtxtTempFrameStart)) { + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + goto l1; + } + performArgCount = GIV(argumentCount); + + /* Recursion check: + | a | + a := Array with: #perform:withArguments: with: nil. + a at: 2 put: a. + a perform: a first withArguments: a. + | a | + a := Array with: #perform:withArguments: with: (Array with: #perform:withArguments: with: nil). + a last at: 2 put: a. + a perform: a first withArguments: a */ + if (!GIV(performWithArgumentsRecursionGuard)) { + GIV(performWithArgumentsRecursionGuard) = longAt(GIV(stackPointer)); + } + + /* Push newMethod to save it in case of failure, + then push the actual receiver and the args in the array. */ + savedNewMethod = GIV(newMethod); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),actualReceiver); + + /* Copy the arguments to the stack, in case of MNU, and lookup */ + for (index = 1; index <= arraySize; index += 1) { + arg = longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord())))))); + if (arg == GIV(performWithArgumentsRecursionGuard)) { + GIV(performWithArgumentsRecursionGuard) = null; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),arg); + } + if (!GIV(performWithArgumentsRecursionGuard)) { + /* begin pop: */ + GIV(stackPointer) += (2 + arraySize) * BytesPerWord; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l1; + } + GIV(argumentCount) = arraySize; + GIV(messageSelector) = selector; + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), /* fetchClassTagOf: */ + ((tagBits = actualReceiver & (tagMask())) + ? tagBits + : (longAt((void *)(actualReceiver))) & (classIndexMask()))); + if (printOnTrace()) { + printActivationNameForSelectorstartClass(GIV(messageSelector), /* fetchClassOf: */ + ((tagBits = actualReceiver & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(actualReceiver))); + cr(); + } + findNewMethodInClassTag(/* fetchClassTagOf: */ + ((tagBits = actualReceiver & (tagMask())) + ? tagBits + : (longAt((void *)(actualReceiver))) & (classIndexMask()))); + + /* Only test CompiledMethods for argument count - any other objects playacting as CMs will have to take their chances */ + if ((/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && ((argumentCountOf(GIV(newMethod))) != GIV(argumentCount))) { + /* Restore the state by popping the array entries, the actual receiver and the saved + newMethod, leaving the selector and array, and fail. N.B. If an MNU has happened + then argumentCount will match newMethod, so this code will not be reached. + These asserts check that an MNU has not occurred if the argumentCount doesn't match the newMethod. */ + assert((stackTop()) == ((arraySize == 0 + ? actualReceiver + : fetchPointerofObject(arraySize - 1, argumentArray)))); + assert(GIV(argumentCount) == arraySize); + + /* begin pop: */ + GIV(stackPointer) += (arraySize + 1) * BytesPerWord; + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(newMethod) = top; + + /* Must reset primitiveFunctionPointer for checkForAndFollowForwardedPrimitiveState */ + GIV(argumentCount) = performArgCount; + primitiveFunctionPointer = primitivePerformWithArgs; + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + goto l1; + } + + /* Cannot fail this primitive from here-on. Slide the actual receiver and arguments down + to replace the perform arguments and saved newMethod and then execute the new + method. Use argumentCount not arraySize because an MNU may have changed it. */ + + /* +2 = receiver + saved newMethod */ + delta = BytesPerWord * (performArgCount + 2); + for (offset = (GIV(argumentCount) * BytesPerWord); offset >= 0; offset += (-BytesPerWord)) { + longAtput((GIV(stackPointer) + offset) + delta,longAt(GIV(stackPointer) + offset)); + } + + /* Part of the recursion guard above. To ensure we fail in the original method, restore + newMethod, and don't do the activateNewMethod implicit in executeNewMethod. */ + if ((primitiveFunctionPointer == primitivePerformWithArgs) + || (primitiveFunctionPointer == primitivePerformInSuperclass)) { + /* This should of course be a tail call, which could be done via setjmp/longjmp. + But this is vanity code. After all how often is a recursive invocation of + primitivePerformWithArgs et al made? */ + GIV(newMethod) = savedNewMethod; + GIV(argumentCount) = performArgCount; + + /* begin pop: */ + GIV(stackPointer) += (performArgCount + 2) * BytesPerWord; + + /* prevent inlining... */ + slowPrimitiveResponse(); + GIV(performWithArgumentsRecursionGuard) = null; + goto l1; + } + GIV(performWithArgumentsRecursionGuard) = null; + + /* begin pop: */ + GIV(stackPointer) += (performArgCount + 2) * BytesPerWord; + + /* prevent inlining... */ + executeNewMethod(); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + /* end primitiveObject:perform:withArguments:lookedUpIn: */ +l1:; +} + + /* InterpreterPrimitives>>#primitivePinnedNew */ +static void +primitivePinnedNew(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt classObj; + sqInt err; + int hash; + sqInt instSpec; + sqInt newObj; + sqInt numSlots; + sqInt obj; + usqInt p; + sqInt reasonCode; + usqInt toDoLimit; + + + /* For the mirror prims check that the class obj is actually a valid class. + Allocate a new fixed-size instance. Fail if the allocation would leave + less than lowSpaceThreshold bytes free. This *will not* cause a GC :-) */ + classObj = longAt(GIV(stackPointer)); + + /* begin inOldSpaceInstantiatePinnedClass: */ + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + if (!(/* isFixedSizePointerFormat: */ + (instSpec <= (nonIndexablePointerFormat())) + || (instSpec == (ephemeronFormat())))) { + obj = null; + goto l1; + } + + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + obj = null; + goto l1; + } + numSlots = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((((usqInt)(numSlots) << (shiftForWord())))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpec, classIndex); + if (newObj) { + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),GIV(nilObj)); + } + } + obj = newObj; + /* end inOldSpaceInstantiatePinnedClass: */ +l1: + if (obj) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + reasonCode = (isFixedSizePointerFormat((((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + +/* Allocate a new pinned indexable instance. Fail if the allocation would + leave less than lowSpaceThreshold bytes free. + */ + + /* InterpreterPrimitives>>#primitivePinnedNewWithArg */ +static void +primitivePinnedNewWithArg(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt classObj; + sqInt err; + sqInt fillValue; + int hash; + sqInt instSpec; + sqInt instSpecSqInt; + sqInt newObj; + usqInt numSlots; + sqInt obj; + usqInt p; + sqInt reasonCode; + usqIntptr_t size; + usqInt toDoLimit; + sqInt value; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + size = null; + goto l1; + } + size = value; + goto l1; + } + + /* don't inline the rare case */ + size = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* positiveMachineIntegerValueOf: succeeds only for non-negative integers. */ + classObj = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin inOldSpaceInstantiatePinnedClass:indexableSize: */ + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpecSqInt = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + fillValue = 0; + switch (instSpecSqInt) { + case arrayFormat(): + numSlots = size; + fillValue = GIV(nilObj); + break; + case indexablePointersFormat(): + case weakArrayFormat(): + numSlots = (classFormat & ((1U << (fixedFieldsFieldWidth())) - 1)) + size; + fillValue = GIV(nilObj); + break; + case sixtyFourBitIndexableFormat(): + numSlots = size; + break; + case firstLongFormat(): + if ((classIndex == ClassFloatCompactIndex) + && (size != 2)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + obj = null; + goto l2; + } + numSlots = (size + 1) / 2; + instSpecSqInt += size & 1; + break; + case firstShortFormat(): + numSlots = (size + 3) / 4; + instSpecSqInt += (4 - size) & 3; + break; + case firstByteFormat(): + numSlots = (size + 7) / 8; + instSpecSqInt += (8 - size) & 7; + break; + default: + if ((size != 0) + || (instSpecSqInt > 5 /* lastPointerFormat */)) { + obj = null; + goto l2; + } + numSlots = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + fillValue = GIV(nilObj); + } + + /* non-indexable + Some Squeak images include funky fixed subclasses of abstract variable + superclasses. e.g. DirectoryEntry as a subclass of ArrayedCollection. + The (Threaded)FFIPlugin expects to be able to instantiate ExternalData via + this method. + Hence allow fixed classes to be instantiated here iff nElements = 0. */ + if (!classIndex) { + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + obj = null; + goto l2; + } + } + if (numSlots > (0x10000000000LL)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + obj = null; + goto l2; + } + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpecSqInt, classIndex); + if (newObj) { + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),fillValue); + } + } + obj = newObj; + /* end inOldSpaceInstantiatePinnedClass:indexableSize: */ +l2: + if (obj) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + instSpec = (((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer) + (1 * BytesPerWord))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + reasonCode = ((/* isIndexableFormat: */ + (instSpec >= (arrayFormat())) + && ((instSpec <= (weakArrayFormat())) + || (instSpec >= (sixtyFourBitIndexableFormat())))) + && (!(instSpec >= (firstCompiledMethodFormat()))) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + +/* Primitive. Answer the last primitive method sampled by the profiler. */ + + /* InterpreterPrimitives>>#primitiveProfilePrimitive */ +EXPORT(sqInt) +primitiveProfilePrimitive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = (!(GIV(profileMethod)) + ? GIV(nilObj) + : GIV(profileMethod)); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return 0; +} + + +/* Primitive. Answer the last sample taken by the profiler, or nil if the + profiler isn't active. + See also primitiveProfileStart. + */ + + /* InterpreterPrimitives>>#primitiveProfileSample */ +EXPORT(sqInt) +primitiveProfileSample(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = (!(GIV(profileProcess)) + ? GIV(nilObj) + : GIV(profileProcess)); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return 0; +} + + +/* Primitive. Begin profiling execution by using the high-resolution clock + instead of a time-based process + (which is limited to timing resolution and triggers off the same signal + that many of the processes being + profiled trigger off leading to consistently wrong results). The argument + is the number of ticks of the + high-resolution clock to elapse before taking a sample. The sample is + stored in the profileProcess and + profileMethod iVars which can be retrieved via primitiveProfileSample and + primitiveProfilePrimitive. So also void the sample iVars. Once a sample is + taken, the semaphore installed via + primitiveProfileSemaphore is signalled. If the argument is less or equal + to zero, profiling is disabled. */ + + /* InterpreterPrimitives>>#primitiveProfileStart */ +EXPORT(sqInt) +primitiveProfileStart(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt deltaTicks; + + deltaTicks = longAt(GIV(stackPointer)); + if ((((deltaTicks) & 7) == 1)) { + GIV(deferProfileCheckForVNCS) = 0; + deltaTicks = (deltaTicks >> 3); + GIV(nextProfileTick) = (deltaTicks > 0 + ? (ioHighResClock()) + deltaTicks + : 0); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return 0; + } + return (GIV(primFailCode) = PrimErrBadArgument); +} + + /* InterpreterPrimitives>>#primitiveQuit */ +static void +primitiveQuit(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + ioExitWithErrorCode((GIV(argumentCount) == 1 + ? ((longAt(GIV(stackPointer))) >> 3) + : 0)); +} + + +/* Rounds negative results towards zero. */ + + /* InterpreterPrimitives>>#primitiveQuo */ +static void +primitiveQuo(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArg; + sqInt integerPointer; + sqInt integerRcvr; + sqInt integerResult; + + integerResult = 0; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + integerRcvr = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerRcvr = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + integerArg = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArg = 0; + } + + /* begin success: */ + if (!integerArg) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + integerResult = /* quot:ient: */ + (integerRcvr > 0 + ? (integerArg > 0 + ? integerRcvr / integerArg + : 0 - (integerRcvr / (0 - integerArg))) + : (integerArg > 0 + ? 0 - ((0 - integerRcvr) / integerArg) + : (0 - integerRcvr) / (0 - integerArg))); + } + + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)(integerResult)) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerResult << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveQuoLargeIntegers */ +EXPORT(void) +primitiveQuoLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (!b) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return; + } + result = a / b; + oopResult = magnitude64BitIntegerForneg(result, bIsNegative != aIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Relinquish the processor for up to the given number of microseconds. The + exact behavior of this primitive is platform dependent. + */ + + /* InterpreterPrimitives>>#primitiveRelinquishProcessor */ +static void +primitiveRelinquishProcessor(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerPointer; + sqInt microSecs; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + microSecs = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + microSecs = 0; + } + if (!GIV(primFailCode)) { + if (!GIV(nextProfileTick)) { + ioRelinquishProcessorForMicroseconds(microSecs); + + /* In simulation we allow ioRelinquishProcessorForMicroseconds: to fail so that + we can arrange that the simulator responds to input events promply. This + *DOES NOT HAPPEN* in the real vm. */ + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveRemLargeIntegers */ +EXPORT(void) +primitiveRemLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (!b) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return; + } + result = a % b; + oopResult = magnitude64BitIntegerForneg(result, aIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* The character scanner primitive + primScanCharactersFrom: startIndex to: stopIndex in: sourceString rightX: + rightX stopConditions: stops kern: kernDelta + This is the inner loop of measurement and scanning for text display. March + through sourceString from startIndex to stopIndex. + If any character is flagged with a non-nil entry in stops, then return the + corresponding value. Determine width of each character + from xTable, indexed by map. If destX would exceed rightX, then return + stops at: 258. Advance destX by the width of the + character. If stopIndex has been reached, then return stops at: 257. + + Receiver inst vars: + destX horizontal position for next character (distance from left of + composition area) + lastIndex the Integer index of next character to be processed in the + sourceString argument + xTable an array mapping character code to glyph x coordinate in a form + of glyphs + map an array mapping character code to glyph position. + + Failure codes: + PrimErrBadArgument one of the indices is not a SmallInteger, or the sting + argument is not a byte string, or the stops array is too small. + PrimErrBadReceiver the receiver has less than four inst vars or any of + scanDestX & scanLastIndex are not SmallIntegers, or the scanXTable or + scanMap are not arrays of the right size + PrimErrBadIndex either a glyph index in scanMap or a start index in + scanXTable are not SmallIntegers + PrimErrLimitExceeded arithmetic on scanDestX has wrapped around to become + negative + */ + + /* InterpreterPrimitives>>#primitiveScanCharacters */ +static void +primitiveScanCharacters(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ascii; + sqInt glyphIndex; + sqInt kernDelta; + sqInt maxGlyph; + sqInt nextDestX; + sqInt rcvr; + sqInt scanDestX; + sqInt scanLastIndex; + sqInt scanMap; + sqInt scanRightX; + sqInt scanStartIndex; + sqInt scanStopIndex; + sqInt scanXTable; + sqInt sourceString; + sqInt sourceX; + sqInt sourceX2; + char *sp1; + sqInt stopReason; + sqInt stops; + + if (GIV(argumentCount) != 6) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + + /* Load the receiver and arguments */ + kernDelta = longAt(GIV(stackPointer)); + stops = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + scanRightX = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + sourceString = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + scanStopIndex = longAt(GIV(stackPointer) + (4 * BytesPerWord)); + scanStartIndex = longAt(GIV(stackPointer) + (5 * BytesPerWord)); + rcvr = longAt(GIV(stackPointer) + (6 * BytesPerWord)); + if (!(((((kernDelta) & 7) == 1)) + && (((((scanRightX) & 7) == 1)) + && (((((scanStopIndex) & 7) == 1)) + && ((((scanStartIndex) & 7) == 1)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + kernDelta = (kernDelta >> 3); + scanRightX = (scanRightX >> 3); + scanStopIndex = (scanStopIndex >> 3); + scanStartIndex = (scanStartIndex >> 3); + + /* check argument type and range and rcvr */ + if (!((/* isArray: */ + ((!(stops & (tagMask())))) + && (((byteAt((void *)(stops + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((slotSizeOf(stops)) >= 258) + && ((/* isBytes: */ + ((!(sourceString & (tagMask())))) + && (((byteAt((void *)(sourceString + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((scanStartIndex > 0) + && ((scanStopIndex > 0) + && (scanStopIndex <= (byteSizeOf(sourceString))))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (!((/* isPointers: */ + ((!(rcvr & (tagMask())))) + && (((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((slotSizeOf(rcvr)) >= 4))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* Check required rcvr instVars */ + scanDestX = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord())))); + scanLastIndex = longAt((void *)((rcvr + BaseHeaderSize) + (1U << (shiftForWord())))); + scanXTable = longAt((void *)((rcvr + BaseHeaderSize) + (2U << (shiftForWord())))); + scanMap = longAt((void *)((rcvr + BaseHeaderSize) + (3U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(scanXTable & (tagMask())))) + && (((byteAt((void *)(scanXTable + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((/* isArray: */ + ((!(scanMap & (tagMask())))) + && (((byteAt((void *)(scanMap + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((slotSizeOf(scanMap)) == 0x100) + && (((((scanDestX) & 7) == 1)) + && ((((scanLastIndex) & 7) == 1))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if (((scanDestX = (scanDestX >> 3))) < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + scanLastIndex = (scanLastIndex >> 3); + maxGlyph = (slotSizeOf(scanXTable)) - 2; + + /* Okay, here we go. We have eliminated nearly all failure + conditions, to optimize the inner fetches. */ + scanLastIndex = scanStartIndex; + while (scanLastIndex <= scanStopIndex) { + /* Known to be okay since scanStartIndex > 0 and scanStopIndex <= sourceString size */ + ascii = byteAt((void *)((sourceString + BaseHeaderSize) + (scanLastIndex - 1))); + + /* Known to be okay since stops size >= 258 */ + if (!(((stopReason = longAt((void *)((stops + BaseHeaderSize) + ((((usqInt)(ascii) << (shiftForWord())))))))) == GIV(nilObj))) { + if (!(scanDestX >= 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrLimitExceeded; + return; + } + storeIntegerofObjectwithValue(0, rcvr, scanDestX); + storeIntegerofObjectwithValue(1, rcvr, scanLastIndex); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),stopReason); + return; + } + + /* Store everything back and get out of here since some stop condition needs to be checked + Known to be okay since scanMap size = 256 */ + glyphIndex = longAt((void *)((scanMap + BaseHeaderSize) + ((((usqInt)(ascii) << (shiftForWord())))))); + + /* fail if the glyphIndex is out of range */ + if (!(((((glyphIndex) & 7) == 1)) + && ((((glyphIndex = (glyphIndex >> 3))) >= 0) + && (glyphIndex <= maxGlyph)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + sourceX = longAt((void *)((scanXTable + BaseHeaderSize) + ((((usqInt)(glyphIndex) << (shiftForWord())))))); + sourceX2 = longAt((void *)((scanXTable + BaseHeaderSize) + ((((usqInt)((glyphIndex + 1)) << (shiftForWord())))))); + + /* Above may fail if non-integer entries in scanXTable */ + if (!(((((sourceX) & 7) == 1)) + && ((((sourceX2) & 7) == 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + nextDestX = (scanDestX + ((sourceX2 >> 3))) - ((sourceX >> 3)); + if (nextDestX > scanRightX) { + /* Store everything back and get out of here since we got to the right edge */ + storeIntegerofObjectwithValue(0, rcvr, scanDestX); + storeIntegerofObjectwithValue(1, rcvr, scanLastIndex); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((sp1 = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),longAt((void *)((stops + BaseHeaderSize) + ((((usqInt)((CrossedX - 1)) << (shiftForWord()))))))); + GIV(stackPointer) = sp1; + return; + } + if (!((nextDestX >= 0) + && (((scanDestX = nextDestX + kernDelta)) >= 0))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrLimitExceeded; + return; + } + scanLastIndex += 1; + } + storeIntegerofObjectwithValue(0, rcvr, scanDestX); + storeIntegerofObjectwithValue(1, rcvr, scanStopIndex); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),longAt((void *)((stops + BaseHeaderSize) + ((((usqInt)((EndOfRun - 1)) << (shiftForWord()))))))); +} + + +/* Return a SmallInteger indicating the current depth of the OS screen. + Negative values are used to imply LSB type pixel format an there is some + support in the VM for handling either MSB or LSB + */ + + /* InterpreterPrimitives>>#primitiveScreenDepth */ +EXPORT(sqInt) +primitiveScreenDepth(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt depth; + + depth = ioScreenDepth(); + if (GIV(primFailCode)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),(((usqInt)depth << 3) | 1)); + return 0; +} + + +/* Answer a float indicating the current scale factor for pixels of the + Smalltalk window. + */ + + /* InterpreterPrimitives>>#primitiveScreenScaleFactor */ +EXPORT(sqInt) +primitiveScreenScaleFactor(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double factor; + char *sp; + + factor = ioScreenScaleFactor(); + if (!GIV(primFailCode)) { + /* begin methodReturnFloat: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),floatObjectOf(factor)); + GIV(stackPointer) = sp; + } + return 0; +} + + +/* Answer a point indicating the current size of the Smalltalk window. + Currently there is a limit of 65535 in each direction because the + point is encoded into a single 32bit value in the image header. + This might well become a problem one day */ + + /* InterpreterPrimitives>>#primitiveScreenSize */ +static void +primitiveScreenSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newObj; + usqInt numBytes; + sqInt oop; + usqInt pointResult; + sqInt pointWord; + sqInt xValue; + sqInt yValue; + + pointWord = ioScreenSize(); + xValue = (((usqInt)(pointWord)) >> 16) & 0xFFFF; + yValue = pointWord & 0xFFFF; + + /* begin makePointwithxValue:yValue: */ + /* begin eeInstantiatePoint */ + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert(((YIndex + 1) >= 0) + && ((knownClassAtIndex(ClassPointCompactIndex)) != GIV(nilObj))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(knownClassAtIndex(ClassPointCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert((YIndex + 1) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (((YIndex + 1) < 1 + ? 8 /* allocationUnit */ + : (YIndex + 1) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + pointResult = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) (YIndex + 1))) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + ClassPointCompactIndex); + GIV(freeStart) += numBytes; + pointResult = newObj; + /* end eeInstantiatePoint */ +l1: + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pointResult)) + && (!(isForwarded(pointResult)))); + assert(validStorePointerUncheckedArgs(XIndex, pointResult, (((usqInt)xValue << 3) | 1))); + longAtput((void *)((pointResult + BaseHeaderSize) + ((((usqInt)(XIndex) << (shiftForWord()))))),(((usqInt)xValue << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pointResult)) + && (!(isForwarded(pointResult)))); + assert(validStorePointerUncheckedArgs(YIndex, pointResult, (((usqInt)yValue << 3) | 1))); + longAtput((void *)((pointResult + BaseHeaderSize) + ((((usqInt)(YIndex) << (shiftForWord()))))),(((usqInt)yValue << 3) | 1)); + oop = pointResult; + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Return the number of seconds since January 1, 1901 as an integer. */ + + /* InterpreterPrimitives>>#primitiveSecondsClock */ +static void +primitiveSecondsClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = (((usqInt)((((usqInt)(ioSecondsNow()))) & 0xFFFFFFFFU) << 3) | 1); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Ask the GUI to set the requested display mode. + See DisplayScreen class depth:width:height:fullscreen: */ + + /* InterpreterPrimitives>>#primitiveSetDisplayMode */ +static void +primitiveSetDisplayMode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt d; + sqInt fsFlag; + sqInt h; + sqInt integerPointer; + sqInt okay; + char *sp; + sqInt w; + + /* begin booleanValueOf: */ + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + fsFlag = 1; + goto l1; + } + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + fsFlag = 0; + goto l1; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + fsFlag = null; + /* end booleanValueOf: */ +l1: + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + h = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + h = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + w = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + w = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + d = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + d = 0; + } + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + okay = ioSetDisplayMode(w, h, d, fsFlag); + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrOperationFailed; + return; + } + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (okay + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; +} + + +/* On platforms that support it, set full-screen mode to the value of the + boolean argument. + */ + + /* InterpreterPrimitives>>#primitiveSetFullScreen */ +static void +primitiveSetFullScreen(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt argOop; + + argOop = longAt(GIV(stackPointer)); + if (argOop == GIV(trueObj)) { + ioSetFullScreen(1); + } + else { + if (argOop == GIV(falseObj)) { + ioSetFullScreen(0); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + if (!GIV(primFailCode)) { + /* begin setFullScreenFlag: */ + imageHeaderFlags = (argOop == GIV(trueObj) + ? imageHeaderFlags | 1 + : ((imageHeaderFlags | 1) - 1)); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } +} + + /* InterpreterPrimitives>>#primitiveSetImmutability */ +#if IMMUTABILITY +static void +primitiveSetImmutability(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt processLists; + sqInt rcvr; + sqInt scheduler; + char *sp; + sqInt wasImmutable; + + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if ((GIV(argumentCount) > 1) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* fail if forwarded; the retry machinery will fix things up. */ + wasImmutable = +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ; + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + /* begin canBeImmutable: */ + assert(isNonImmediate(rcvr)); + + /* For now we fail the primitive for contexts to we ensure there are no immutable contexts. + Later we can consider having immutable contexts and send cannotReturn callback + when returning to an immutable context. That would mean that setting a context + to immutable would require a divorce and returns to immutable context are + necessarily across stack pages */ + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + goto l1; + } + + /* Weak structures can't be immutable */ + + /* begin isEphemeron: */ + assert(isNonImmediate(rcvr)); + format = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + if (format == (ephemeronFormat())) { + goto l1; + } + + /* begin isWeakNonImm: */ + format = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + if (format == (weakArrayFormat())) { + goto l1; + } + + /* No clue what is going on for semaphores so they can't be immutable */ + if (((longAt((void *)(rcvr))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))) { + goto l1; + } + + /* Simple version of process management: we forbid Process and LinkedList instances to be immutable + as well as the Processor and the array of activeProcess */ + + /* begin fetchPointer:ofObject: */ + scheduler = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + processLists = longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + if (rcvr == scheduler) { + goto l1; + } + if (rcvr == processLists) { + goto l1; + } + + /* Is it a linkedList ? */ + if ((classIndexOf(longAt((void *)((processLists + BaseHeaderSize) + (1U << (shiftForWord())))))) == ((longAt((void *)(rcvr))) & (classIndexMask()))) { + goto l1; + } + + /* is it a Process ? */ + if ((classIndexOf(longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))))) == ((longAt((void *)(rcvr))) & (classIndexMask()))) { + goto l1; + } + + /* The rest of the code is relative to process management: the Processor (the active + process scheduler) can't be immutable, as well as all the objects relative to Process management + scheduler := self fetchPointer: ValueIndex ofObject: (self splObj: SchedulerAssociation). + processLists := objectMemory fetchPointer: ProcessListsIndex ofObject: scheduler. + ((objectMemory formatOf: oop) = objectMemory nonIndexablePointerFormat) + ifFalse: + [ (objectMemory isArrayNonImm: oop) ifFalse: [^ true]. + ^ (oop = processLists) not ]. + (objectMemory numSlotsOf: oop) >= 2 ifFalse: [^ true]. + + is the oop the scheduler itself ? + oop = scheduler ifTrue: [^ false]. + 1 to: (objectMemory numSlotsOf: processLists) do: [ :i | + + is the oop one of the linked lists ? + (list := processLists at: i) = oop ifTrue: [^ false]. + + is the oop one of the runnable process ? + first := objectMemory fetchPointer: FirstLinkIndex ofObject: list. + first = objectMemory nilObject ifFalse: + [ last := objectMemory fetchPointer: LastLinkIndex ofObject: list. + link := first. + [ link = last ] whileFalse: + [ link = oop ifTrue: [^ false]. + link := objectMemory fetchPointer: NextLinkIndex ofObject: link. ] ] ]. */ + goto l2; +l1: + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + return; +l2: + + /* begin setIsImmutableOf:to: */ + longAtput((void *)(rcvr),(longAt((void *)(rcvr))) | (1U << (immutableBitShift()))); + } + else { + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + /* begin setIsImmutableOf:to: */ + longAtput((void *)(rcvr),(longAt((void *)(rcvr))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (wasImmutable + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; +} +#endif /* IMMUTABILITY */ + + +/* Set the user interrupt keycode. The keycode is an integer whose encoding + is described in the comment for primitiveKbdNext. + */ + + /* InterpreterPrimitives>>#primitiveSetInterruptKey */ +static void +primitiveSetInterruptKey(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerPointer; + sqInt keycode; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + keycode = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + keycode = 0; + } + if (!GIV(primFailCode)) { + /* begin setInterruptKeycode: */ + GIV(interruptKeycode) = keycode; + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } +} + + +/* Primitive. Set the VM's log directory */ + + /* InterpreterPrimitives>>#primitiveSetLogDirectory */ +EXPORT(sqInt) +primitiveSetLogDirectory(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + usqInt numBytes; + usqInt numSlots; + sqInt stringOop; + sqInt sz; + + stringOop = longAt(GIV(stackPointer)); + if (!(/* isBytes: */ + ((!(stringOop & (tagMask())))) + && (((byteAt((void *)(stringOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + + /* begin byteSizeOf: */ + if (((stringOop & (tagMask())) != 0)) { + sz = 0; + goto l1; + } + + /* begin numBytesOf: */ + fmt = (byteAt((void *)(stringOop + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(stringOop)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(stringOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(stringOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + sz = numBytes - (fmt & 7); + goto l1; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + sz = numBytes; + goto l1; + } + if (fmt >= (firstShortFormat())) { + sz = numBytes - (((fmt & 3) << 1)); + goto l1; + } + + /* fmt >= self firstLongFormat */ + sz = numBytes - (((fmt & 1) << 2)); + /* end byteSizeOf: */ +l1: + ioSetLogDirectoryOfSize(firstIndexableField(stringOop), sz); + if (!GIV(primFailCode)) { + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + return 0; +} + + /* InterpreterPrimitives>>#primitiveSetOrHasIdentityHash */ +static void +primitiveSetOrHasIdentityHash(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt hasHash; + sqInt hash; + sqInt isReceiverAClass; + sqInt lastArg; + int oldHash; + char *sp; + sqInt thisReceiver; + + if (!GIV(argumentCount)) { + hasHash = ((!((longAt(GIV(stackPointer))) & (tagMask())))) + && ((long32At((void *)((longAt(GIV(stackPointer))) + 4))) & (identityHashHalfWordMask())); + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (hasHash + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return; + } + isReceiverAClass = 0; + if (GIV(argumentCount) == 2) { + lastArg = longAt(GIV(stackPointer)); + if ((((lastArg) & 7) == 1)) { + hash = lastArg; + thisReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + } + else { + thisReceiver = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + hash = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (lastArg == GIV(trueObj)) { + isReceiverAClass = 1; + } + else { + if (!(lastArg == GIV(falseObj))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + } + } + else { + thisReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + hash = longAt(GIV(stackPointer)); + } + + /* anObject primitiveSetIdentityHashTo: hash */ + if (!(((((hash) & 7) == 1)) + && ((/* if written naturally, clang warns: unsequenced modification and access to 'hash' */ + (hash = (hash >> 3)), + (hash & (identityHashHalfWordMask())) == hash)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (!((!(thisReceiver & (tagMask()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = (thisReceiver == (longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord))) + ? PrimErrBadReceiver + : PrimErrBadArgument); + return; + } + oldHash = (long32At((void *)(thisReceiver + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(thisReceiver + 4),((((long32At((void *)(thisReceiver + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + if (isReceiverAClass) { + classAtIndexput(hash, thisReceiver); + + /* this figures out if the index is ambiguous and fixes all the instances if needed */ + allInstancesOf(thisReceiver); + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)oldHash << 3) | 1)); +} + + +/* Treat the receiver, which can be indexible by either bytes or words, as + an array of signed 16-bit values. Answer the contents of the given index. + Note that the index specifies the i-th 16-bit entry, not the i-th byte or + word. */ + + /* InterpreterPrimitives>>#primitiveShortAt */ +static void +primitiveShortAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt rcvr; + sqInt value; + + index = longAt(GIV(stackPointer)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(/* isWordsOrBytes: */ + ((!(rcvr & (tagMask())))) + && (isWordsOrBytesNonImm(rcvr)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + return; + } + index = (index >> 3); + if (!((index >= 1) + && (index <= (((usqInt)((numBytesOf(rcvr)))) >> 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + value = shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 1))))); + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)value << 3) | 1)); +} + + +/* Treat the receiver, which can be indexible by either bytes or words, as an + array of signed 16-bit values. Set the contents of the given index to the + given value. + Note that the index specifies the i-th 16-bit entry, not the i-th byte or + word. + */ + + /* InterpreterPrimitives>>#primitiveShortAtPut */ +static void +primitiveShortAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt rcvr; + sqInt value; + + value = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(((((value) & 7) == 1)) + && (((((index) & 7) == 1)) + && (((value = (value >> 3)), + (value >= -32768) + && (value <= 0x7FFF)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (!(/* isWordsOrBytes: */ + ((!(rcvr & (tagMask())))) + && (isWordsOrBytesNonImm(rcvr)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + return; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + return; + } + index = (index >> 3); + if (!((index >= 1) + && (index <= (((usqInt)((numBytesOf(rcvr)))) >> 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 1)))),value); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),(((usqInt)value << 3) | 1)); +} + + +/* Force the given rectangular section of the Display to be + copied to the screen. */ + + /* InterpreterPrimitives>>#primitiveShowDisplayRect */ +static void +primitiveShowDisplayRect(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bottom; + sqInt integerPointer; + sqInt left; + sqInt right; + sqInt top; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + bottom = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + bottom = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + top = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + top = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + right = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + right = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + left = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + left = 0; + } + if (!GIV(primFailCode)) { + updateDisplayLeftTopRightBottom(left, top, right, bottom); + ioForceDisplayUpdate(); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } +} + + +/* Set the low-water mark for free space. When the free space falls + below this level, the new and new: primitives fail and system attempts + to allocate space (e.g., to create a method context) cause the low-space + semaphore (if one is registered) to be signalled. */ + + /* InterpreterPrimitives>>#primitiveSignalAtBytesLeft */ +static void +primitiveSignalAtBytesLeft(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bytes; + + bytes = longAt(GIV(stackPointer)); + if (((((bytes) & 7) == 1)) + && (((bytes = (bytes >> 3))) >= 0)) { + /* begin lowSpaceThreshold: */ + GIV(lowSpaceThreshold) = bytes; + + /* N.B. The threshold > 0 guard eliminates a warning when + self lowSpaceThreshold: 0 + is inlined into setSignalLowSpaceFlagAndSaveProcess */ + if ((bytes > 0) + && (GIV(totalFreeOldSpace) < bytes)) { + growOldSpaceByAtLeast(bytes - GIV(totalFreeOldSpace)); + } + assert(GIV(totalFreeOldSpace) >= GIV(lowSpaceThreshold)); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } +} + + +/* Computes sine of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveSine */ +static void +primitiveSine(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(sin(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSize */ +static void +primitiveSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt totalLength; + + fmt = 0; + rcvr = longAt(GIV(stackPointer)); + if ((((rcvr & (tagMask())) != 0)) + || (((hdr = long64At((void *)(rcvr))), + (((fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()))) < (arrayFormat())) + || (fmt == (forwardedFormat()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* Integers are not indexable + And if Spur, this is a forwarder. */ + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + primitiveContextSize(); + return; + } + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)(totalLength - fixedFields) << 3) | 1)); +} + + /* InterpreterPrimitives>>#primitiveSizeInBytes */ +static void +primitiveSizeInBytes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong byteSize; + sqInt oop; + + byteSize = /* totalByteSizeOf: */ + ((((longAt(GIV(stackPointer))) & (tagMask())) != 0) + ? 0 + : bytesInBody(longAt(GIV(stackPointer)))); + oop = positive64BitIntegerFor(byteSize); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} + + +/* Answer the byte size of an instance of the receiver. If num args > 0 + then the last argument is a variable size and the size answered is the + size of an instance of the receiver with that many indexable elements. */ + + /* InterpreterPrimitives>>#primitiveSizeInBytesOfInstance */ +static void +primitiveSizeInBytesOfInstance(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt byteSize; + sqInt classFormat; + int err; + sqInt instSpec; + usqInt numSlots; + sqInt numSlotsSqInt; + sqInt oop; + + if (GIV(argumentCount) > 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + + /* Support VMMirror>>byteSizeOfInstanceOf:WithIndexableVariables: */ + err = -1; + if (GIV(argumentCount) >= 1) { + if (!(((((longAt(GIV(stackPointer)))) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* begin byteSizeOfInstanceOf:withIndexableSlots:errInto: */ + classFormat = ((longAt((void *)(((longAt(GIV(stackPointer) + (1 * BytesPerWord))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + switch (instSpec) { + case arrayFormat(): + numSlots = ((longAt(GIV(stackPointer))) >> 3); + break; + case indexablePointersFormat(): + case weakArrayFormat(): + numSlots = (classFormat & ((1U << (fixedFieldsFieldWidth())) - 1)) + (((longAt(GIV(stackPointer))) >> 3)); + break; + case sixtyFourBitIndexableFormat(): + numSlots = ((longAt(GIV(stackPointer))) >> 3); + break; + case firstLongFormat(): + numSlots = ((((longAt(GIV(stackPointer))) >> 3)) + 1) / 2; + break; + case firstShortFormat(): + numSlots = ((((longAt(GIV(stackPointer))) >> 3)) + 3) / 4; + break; + case firstByteFormat(): + case firstCompiledMethodFormat(): + numSlots = ((((longAt(GIV(stackPointer))) >> 3)) + (BytesPerOop - 1)) / BytesPerOop; + break; + default: + byteSize = (err = PrimErrBadReceiver); + goto l1; + } + if (numSlots >= (((((usqInt)1)) << ((BytesPerOop * 8) - 3 /* logBytesPerOop */)))) { + byteSize = (err = ((((longAt(GIV(stackPointer))) >> 3)) < 0 + ? PrimErrBadArgument + : PrimErrLimitExceeded)); + goto l1; + } + byteSize = /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize); + /* end byteSizeOfInstanceOf:withIndexableSlots:errInto: */ +l1: + if (err >= 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = err; + return; + } + oop = positive64BitIntegerFor(byteSize); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return; + } + + /* begin byteSizeOfInstanceOf:errInto: */ + classFormat = ((longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + if (!(/* isFixedSizePointerFormat: */ + (instSpec <= (nonIndexablePointerFormat())) + || (instSpec == (ephemeronFormat())))) { + byteSize = (err = PrimErrBadReceiver); + goto l2; + } + numSlotsSqInt = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + byteSize = /* objectBytesForSlots: */ + (numSlotsSqInt + ? ((((usqInt)(numSlotsSqInt) << (shiftForWord())))) + ((numSlotsSqInt >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize); + /* end byteSizeOfInstanceOf:errInto: */ +l2: + if (err >= 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = err; + return; + } + + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),((byteSize << 3) | 1)); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatAdd */ +static void +primitiveSmallFloatAdd(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr + arg)); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatArctan */ +static void +primitiveSmallFloatArctan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + sqInt oop; + double rcvr; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + aValue = floatObjectOf(atan(rcvr)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatCosine */ +static void +primitiveSmallFloatCosine(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + sqInt oop; + double rcvr; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + aValue = floatObjectOf(cos(rcvr)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatDivide */ +static void +primitiveSmallFloatDivide(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + if (arg == 0.0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr / arg)); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatEqual */ +static void +primitiveSmallFloatEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) == intArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr == arg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + +/* Computes E raised to the receiver power. + Since SmallFloats cannot represent NaNs there's no need to special case. */ + + /* InterpreterPrimitives>>#primitiveSmallFloatExp */ +static void +primitiveSmallFloatExp(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + + aValue = floatObjectOf(exp(smallFloatValueOf(longAt(GIV(stackPointer))))); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + +/* Answer the exponent part of this float. */ + + /* InterpreterPrimitives>>#primitiveSmallFloatExponent */ +static void +primitiveSmallFloatExponent(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqInt exp; + + aValue = (((usqInt)(((/* begin exponentOfSmallFloat: */ + assert(((longAt(GIV(stackPointer))) & (tagMask())) == (smallFloatTag())), +((((usqInt)(longAt(GIV(stackPointer))))) <= 15 + ? 0 + : ((exp = ((((usqInt)(longAt(GIV(stackPointer)))))) >> (((numTagBits()) + (smallFloatMantissaBits())) + 1)), + (exp + (smallFloatExponentOffset())) - 0x3FE)))) - 1) << 3) | 1); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatFractionalPart */ +static void +primitiveSmallFloatFractionalPart(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double frac; + sqInt oop; + double rcvr; + double trunc; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + frac = modf(rcvr, (&trunc)); + aValue = floatObjectOf(frac); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatGreaterOrEqual */ +static void +primitiveSmallFloatGreaterOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) >= intArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr >= arg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatGreaterThan */ +static void +primitiveSmallFloatGreaterThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) > intArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr > arg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatLessOrEqual */ +static void +primitiveSmallFloatLessOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) <= intArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr <= arg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatLessThan */ +static void +primitiveSmallFloatLessThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) < intArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr < arg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + +/* Natural log. */ + + /* InterpreterPrimitives>>#primitiveSmallFloatLogN */ +static void +primitiveSmallFloatLogN(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + sqInt oop; + double rcvr; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + aValue = floatObjectOf(log(rcvr)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatMultiply */ +static void +primitiveSmallFloatMultiply(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr * arg)); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatNotEqual */ +static void +primitiveSmallFloatNotEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) == intArg + ? GIV(falseObj) + : GIV(trueObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr == arg + ? GIV(falseObj) + : GIV(trueObj))); + GIV(stackPointer) = sp; + } + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatSine */ +static void +primitiveSmallFloatSine(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + sqInt oop; + double rcvr; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + aValue = floatObjectOf(sin(rcvr)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatSquareRoot */ +static void +primitiveSmallFloatSquareRoot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + sqInt oop; + double rcvr; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + if (rcvr >= 0.0) { + aValue = floatObjectOf(sqrt(rcvr)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatSubtract */ +static void +primitiveSmallFloatSubtract(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr - arg)); + GIV(stackPointer) = sp; + } +} + + +/* Multiply the receiver by the power of the argument. */ + + /* InterpreterPrimitives>>#primitiveSmallFloatTimesTwoPower */ +static void +primitiveSmallFloatTimesTwoPower(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt rcvr; + sqInt result; + sqInt twiceMaxExponent; + + arg = longAt(GIV(stackPointer)); + if (!((((arg) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* N.B. SmallFloats are finite. NaN and Infinity overflow into boxed floats. + This is doing range checking work that is done in ldexp, but we include + it explicitly to exemplify bit manipulation of SmallFloats. */ + + /* begin isSmallFloatZero: */ + assert(isImmediateFloat(rcvr)); + if ((((usqInt)rcvr)) <= ((1U << (numTagBits())) + (smallFloatTag()))) { + result = rcvr; + } + else { + arg = (arg >> 3); + twiceMaxExponent = 0x1000; + if (arg < (-twiceMaxExponent)) { + /* begin mapSignedSmallFloatToSignedSmallFloatZero: */ + assert(isImmediateFloat(rcvr)); + result = rcvr & ((1U << (numTagBits())) + (tagMask())); + } + else { + /* clip arg to at most int range; ldexp's last arg is of type int */ + if (arg > twiceMaxExponent) { + arg = twiceMaxExponent; + } + result = floatObjectOf(ldexp(smallFloatValueOf(rcvr), ((int) arg))); + } + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),result); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatTruncated */ +static void +primitiveSmallFloatTruncated(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + sqInt oop; + double rcvr; + double trunc; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + modf(rcvr, (&trunc)); + if ((((trunc >= (((double) (MinSmallInteger) ))) && (trunc <= (((double) (MaxSmallInteger) ))))) + && ((((((usqInt)((((sqInt)trunc)))) >> 60) + 1) & 15) <= 1)) { + /* stackTopPut: */ + longAtput(GIV(stackPointer),(((usqInt)(((sqInt)trunc)) << 3) | 1)); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + /* InterpreterPrimitives>>#primitiveSomeInstance */ +static void +primitiveSomeInstance(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt instance; + + class = longAt(GIV(stackPointer)); + + /* For the mirror prims check that the class obj is actually a valid class. */ + instance = initialInstanceOf(class); + if (instance) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),instance); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Return the first object in the heap. */ + + /* InterpreterPrimitives>>#primitiveSomeObject */ +static void +primitiveSomeObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = firstAccessibleObject(); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} + + +/* Return the oop of the SpecialObjectsArray. */ + + /* InterpreterPrimitives>>#primitiveSpecialObjectsOop */ +static void +primitiveSpecialObjectsOop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),GIV(specialObjectsOop)); +} + + +/* Computes square root of float receiver; receiver *must* be a float + instance. + */ + + /* InterpreterPrimitives>>#primitiveSquareRoot */ +static void +primitiveSquareRoot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + if (doubleValue >= 0.0) { + aValue = floatObjectOf(sqrt(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* This primitive is called from Squeak as... + storeSegmentFor: arrayOfRoots into: aWordArray outPointers: + anArray. + */ +/* This primitive will store a binary image segment (in the same format as + the Squeak image file) of the receiver and every object in its proper tree + of subParts (ie, that is not refered to from anywhere else outside the + tree). All pointers from within the tree to objects outside the tree will + be copied into the array of outpointers. In their place in the image + segment will be an oop equal to the offset in the outPointer array (the + first would be 4). but with the high bit set. + */ +/* The primitive expects the array and wordArray to be more than adequately + long. In this case it returns normally, and truncates the two arrays to + exactly the right size. To simplify truncation, both incoming arrays are + required to be whatever the objectMemory considers long objects. If either + array is too small, the primitive will fail, but in no other case. + + During operation of the primitive, it is necessary to convert from both + internal and external oops to their mapped values. To make this fast, the + headers of the original objects in question are replaced by the mapped + values (and this is noted by adding the forbidden XX header type). Tables + are kept of both kinds of oops, as well as of the original headers for + restoration. + To be specific, there are two similar two-part tables, the outpointer + array, and one in the upper fifth of the segmentWordArray. Each grows oops + from the bottom up, and preserved headers from halfway up. + + In case of either success or failure, the headers must be restored. In the + event of primitive failure, the table of outpointers must also be nilled + out (since the garbage in the high half will not have been discarded. + */ + + /* InterpreterPrimitives>>#primitiveStoreImageSegment */ +static void +primitiveStoreImageSegment(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayOfRoots; + sqInt ecode; + sqInt outPointerArray; + sqInt segmentWordArray; + + outPointerArray = longAt(GIV(stackPointer)); + segmentWordArray = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + arrayOfRoots = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + + /* Essential type checks */ + if (!((/* isArray: */ + ((!(arrayOfRoots & (tagMask())))) + && (((byteAt((void *)(arrayOfRoots + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((/* isArray: */ + ((!(outPointerArray & (tagMask())))) + && (((byteAt((void *)(outPointerArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (/* isWords: */ + ((!(segmentWordArray & (tagMask())))) + && (((((byteAt((void *)(segmentWordArray + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(segmentWordArray + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1)))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* Must be indexable pointers + Must be indexable words */ + ecode = storeImageSegmentIntooutPointersroots(segmentWordArray, outPointerArray, arrayOfRoots); + if (ecode == PrimErrNeedCompaction) { + fullGC(); + outPointerArray = longAt(GIV(stackPointer)); + segmentWordArray = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + arrayOfRoots = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + ecode = storeImageSegmentIntooutPointersroots(segmentWordArray, outPointerArray, arrayOfRoots); + } + if (ecode) { + /* primitiveFailFor: */ + GIV(primFailCode) = ecode; + } + else { + /* begin pop: */ + GIV(stackPointer) += 3 * BytesPerWord; + } +} + + /* InterpreterPrimitives>>#primitiveStringAt */ +static void +primitiveStringAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt result; + sqInt sp1; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + + /* begin commonAt: */ + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + index = longAt(GIV(stackPointer)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l5; + } + + /* No need to test for large positive integers here. No object has 1g elements */ + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 1) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l5; + } + index = (index >> 3); + + /* begin stObject:at: */ + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + sp1 = stackPointerIndexForFrame(frameOfMarriedContext(rcvr)); + assert((ReceiverIndex + ((sp1 >> 3))) < (lengthOf(rcvr))); + stSize = sp1; + goto l3; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l3; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(rcvr))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l3: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))))) { + result = temporaryin(index - 1, frameOfMarriedContext(rcvr)); + goto l4; + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(rcvr)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + result = (((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + (index - 1)))) << 3) | 1); + goto l4; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + result = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << (shiftForWord())))))); + goto l4; + } + if (fmt >= (firstByteFormat())) { + result = (((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + ((index + fixedFields) - 1)))) << 3) | 1); + goto l4; + } + if (fmt >= (firstShortFormat())) { + result = (((usqInt)(((unsigned short) (shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))))))) << 3) | 1); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + result = positive64BitIntegerFor(long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))))); + goto l4; + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + result = (((usqInt)((((usqInt)(long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + goto l4; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + result = 0; + /* end stObject:at: */ +l4: + if (!GIV(primFailCode)) { + if (!(((result >= 0) && (result <= (0x3FFFFFFF))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l5; + } + result = ((((usqInt)(((result >> 3))) << (numTagBits())))) + (characterTag()); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); + } + /* end commonAt: */ +l5:; +} + + /* InterpreterPrimitives>>#primitiveStringAtPut */ +static void +primitiveStringAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fieldIndex; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt signedValueToStore; + sqInt signedValueToStoreSqInt; + sqInt sp; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + usqLong unsigned64BitValueToStore; + unsigned int unsignedValueToStore; + sqInt value; + sqInt valueSqInt; + + /* begin commonAtPut: */ + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + value = longAt(GIV(stackPointer)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l11; + } + + /* No need to test for large positive integers here. No object has 1g elements */ + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 2) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l11; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l11; + } + index = (index >> 3); + valueSqInt = asciiOfCharacter(value); + + /* begin stObject:at:put: */ + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l1: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(rcvr)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(rcvr))); + stSize = sp; + goto l2; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l2; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(rcvr))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l2: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))))) { + temporaryinput(index - 1, frameOfMarriedContext(rcvr), valueSqInt); + goto l5; + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(rcvr)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + if (((((valueSqInt) & 7) == 1)) + && ((((usqInt)((signedValueToStore = (valueSqInt >> 3))))) <= 0xFF)) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + (index - 1)),signedValueToStore); + goto l5; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l5; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l3; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l3; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l3: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:storing:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + fieldIndex = (index + fixedFields) - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, rcvr, valueSqInt)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valueSqInt & (tagMask())))) + && (oopisLessThan(valueSqInt, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valueSqInt); + goto l4; + } + if (fmt >= (firstByteFormat())) { + if (!((((valueSqInt) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStoreSqInt = (valueSqInt >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + ((index + fixedFields) - 1)),signedValueToStoreSqInt); + goto l4; + } + if (fmt >= (firstShortFormat())) { + if (!((((valueSqInt) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStoreSqInt = (valueSqInt >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))),signedValueToStoreSqInt); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(valueSqInt); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l4; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(valueSqInt); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l4: + goto l5; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + /* end stObject:at:put: */ +l5: + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + } + /* end commonAtPut: */ +l11:; +} + + +/* primReplaceFrom: start to: stop with: replacement startingAt: + repStart + */ + + /* InterpreterPrimitives>>#primitiveStringReplace */ +static void +primitiveStringReplace(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt array; + sqInt arrayFmt; + sqInt arrayInstSize; + usqInt arrayLength; + sqInt class; + sqInt i; + sqInt mustRemember; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt repl; + sqInt replFmt; + sqInt replInstSize; + usqInt replLength; + sqInt replStart; + sqInt srcDelta; + sqInt start; + sqInt stop; + sqInt valuePointer; + + /* begin primitiveSpurStringReplace */ + array = longAt(GIV(stackPointer) + (4 * BytesPerWord)); + start = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + stop = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + repl = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + replStart = longAt(GIV(stackPointer)); + if (((!(start & (smallIntegerTag())))) + || (((!(stop & (smallIntegerTag())))) + || (((!(replStart & (smallIntegerTag())))) + || (((repl & (tagMask())) != 0))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l7; + } + + /* can happen in LgInt copy */ + start = (start >> 3); + stop = (stop >> 3); + replStart = (replStart >> 3); + if ((stop >= start) + && ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(array + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + )) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l7; + } + arrayFmt = (byteAt((void *)(array + (formatFieldByteOffset())))) & (formatMask()); + replFmt = (byteAt((void *)(repl + (formatFieldByteOffset())))) & (formatMask()); + + /* N.B. In the below start - 1 to: stop - 1 do:, Slang is intelligent enough to use < instead of <= so avoiding the stop - 1. */ + if (arrayFmt <= 5 /* lastPointerFormat */) { + /* Array formats must be the same; but for copying, weak arrays are equivalent to arrays. */ + if (arrayFmt == (weakArrayFormat())) { + arrayFmt = arrayFormat(); + } + if (replFmt == (weakArrayFormat())) { + replFmt = arrayFormat(); + } + if (arrayFmt != replFmt) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l7; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(array)) > (isForwardedObjectClassIndexPun())); + arrayLength = (((numSlots = byteAt((void *)(array + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + + /* begin fixedFieldsOf:format:length: */ + if ((arrayFmt >= (sixtyFourBitIndexableFormat())) + || (arrayFmt == (arrayFormat()))) { + arrayInstSize = 0; + goto l5; + } + if (arrayFmt < (arrayFormat())) { + arrayInstSize = arrayLength; + goto l5; + } + class = fetchClassOfNonImm(array); + arrayInstSize = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l5: + + /* begin numSlotsOf: */ + assert((classIndexOf(repl)) > (isForwardedObjectClassIndexPun())); + replLength = (((numSlots = byteAt((void *)(repl + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(repl - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + + /* begin fixedFieldsOf:format:length: */ + if ((replFmt >= (sixtyFourBitIndexableFormat())) + || (replFmt == (arrayFormat()))) { + replInstSize = 0; + goto l6; + } + if (replFmt < (arrayFormat())) { + replInstSize = replLength; + goto l6; + } + class = fetchClassOfNonImm(repl); + replInstSize = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l6: + if (!((start >= 1) + && (((start - 1) <= stop) + && (((stop + arrayInstSize) <= arrayLength) + && ((replStart >= 1) + && ((((stop - start) + replStart) + replInstSize) <= replLength)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l7; + } + start += arrayInstSize; + stop += arrayInstSize; + srcDelta = (replStart + replInstSize) - start; + + /* begin isOldObject: */ + assert(isNonImmediate(array)); + if (oopisGreaterThanOrEqualTo(array, GIV(oldSpaceStart))) { + mustRemember = 0; + for (i = (start - 1); i < stop; i += 1) { + oop = longAt((void *)((repl + BaseHeaderSize) + ((((usqInt)((srcDelta + i)) << (shiftForWord())))))); + if (/* isYoung: */ + ((!(oop & (tagMask())))) + && (oopisLessThan(oop, GIV(oldSpaceStart)))) { + mustRemember = 1; + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(i, array, oop)); + longAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),oop); + } + if (mustRemember) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(array + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(array); + } + } + } + else { + for (i = (start - 1); i < stop; i += 1) { + valuePointer = longAt((void *)((repl + BaseHeaderSize) + ((((usqInt)((srcDelta + i)) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(i, array, valuePointer)); + longAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + } + + /* We might consider comparing stop - start to some value here and using forceInterruptCheck */ + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + goto l7; + } + + /* Non-pointer array formats must match */ + + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(array + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (arrayFmt <= (ephemeronFormat())) { + arrayLength = ((sqInt) numSlots); + goto l3; + } + if (arrayFmt >= (firstByteFormat())) { + arrayLength = ((numSlots << (shiftForWord()))) - (arrayFmt & 7); + goto l3; + } + + /* bytes, including CompiledMethod */ + if (arrayFmt >= (firstShortFormat())) { + arrayLength = ((numSlots << ((shiftForWord()) - 1))) - (arrayFmt & 3); + goto l3; + } + if (arrayFmt >= (firstLongFormat())) { + arrayLength = ((numSlots << ((shiftForWord()) - 2))) - (arrayFmt & 1); + goto l3; + } + if (arrayFmt == (sixtyFourBitIndexableFormat())) { + arrayLength = ((sqInt) numSlots); + goto l3; + } + + /* fmt = self forwardedFormat */ + arrayLength = 0; + /* end lengthOf:format: */ +l3: + + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(repl + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(repl - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (replFmt <= (ephemeronFormat())) { + replLength = ((sqInt) numSlots); + goto l4; + } + if (replFmt >= (firstByteFormat())) { + replLength = ((numSlots << (shiftForWord()))) - (replFmt & 7); + goto l4; + } + + /* bytes, including CompiledMethod */ + if (replFmt >= (firstShortFormat())) { + replLength = ((numSlots << ((shiftForWord()) - 1))) - (replFmt & 3); + goto l4; + } + if (replFmt >= (firstLongFormat())) { + replLength = ((numSlots << ((shiftForWord()) - 2))) - (replFmt & 1); + goto l4; + } + if (replFmt == (sixtyFourBitIndexableFormat())) { + replLength = ((sqInt) numSlots); + goto l4; + } + + /* fmt = self forwardedFormat */ + replLength = 0; + /* end lengthOf:format: */ +l4: + + /* begin classFormatFromInstFormat: */ + if (arrayFmt >= (firstByteFormat())) { + arrayFmt = arrayFmt & -8; + goto l1; + } + + /* this is likely the common case */ + if (arrayFmt <= (sixtyFourBitIndexableFormat())) { + goto l1; + } + if (arrayFmt < (firstShortFormat())) { + arrayFmt = arrayFmt & -2; + goto l1; + } + arrayFmt = arrayFmt & -4; + /* end classFormatFromInstFormat: */ +l1: + + /* begin classFormatFromInstFormat: */ + if (replFmt >= (firstByteFormat())) { + replFmt = replFmt & -8; + goto l2; + } + + /* this is likely the common case */ + if (replFmt <= (sixtyFourBitIndexableFormat())) { + goto l2; + } + if (replFmt < (firstShortFormat())) { + replFmt = replFmt & -2; + goto l2; + } + replFmt = replFmt & -4; + /* end classFormatFromInstFormat: */ +l2: + if (!((arrayFmt == replFmt) + && ((arrayFmt >= (sixtyFourBitIndexableFormat())) + && (arrayFmt < (firstCompiledMethodFormat()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l7; + } + if (!((start >= 1) + && (((start - 1) <= stop) + && ((stop <= arrayLength) + && ((replStart >= 1) + && (((stop - start) + replStart) <= replLength)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l7; + } + srcDelta = replStart - start; + if (arrayFmt >= (firstShortFormat())) { + if (arrayFmt >= (firstByteFormat())) { + for (i = (start - 1); i < stop; i += 1) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((array + BaseHeaderSize) + i),byteAt((void *)((repl + BaseHeaderSize) + (srcDelta + i)))); + } + } + else { + for (i = (start - 1); i < stop; i += 1) { + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << 1)))),shortAt((void *)((repl + BaseHeaderSize) + ((((usqInt)((srcDelta + i)) << 1)))))); + } + } + } + else { + if (arrayFmt >= (firstLongFormat())) { + for (i = (start - 1); i < stop; i += 1) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << 2)))),long32At((void *)((repl + BaseHeaderSize) + ((((usqInt)((srcDelta + i)) << 2)))))); + } + } + else { + for (i = (start - 1); i < stop; i += 1) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << 3)))),long64At((void *)((repl + BaseHeaderSize) + ((((usqInt)((srcDelta + i)) << 3)))))); + } + } + } + + /* 8 & 16-bit word type objects + 32 & 64-bit word type objects + We might consider comparing stop - start to some value here and using forceInterruptCheck */ + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + /* end primitiveSpurStringReplace */ +l7:; +} + + /* InterpreterPrimitives>>#primitiveSubtract */ +static void +primitiveSubtract(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerResult; + + integerResult = (stackIntegerValue(1)) - (stackIntegerValue(0)); + + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)(integerResult)) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerResult << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveSubtractLargeIntegers */ +EXPORT(void) +primitiveSubtractLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + sqInt resultIsNegative; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (GIV(primFailCode)) { + return; + } + if (aIsNegative != bIsNegative) { + /* Protect against overflow */ + if (a > (0xFFFFFFFFFFFFFFFFULL - b)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + result = a + b; + resultIsNegative = aIsNegative; + } + else { + if (a >= b) { + result = a - b; + resultIsNegative = aIsNegative; + } + else { + result = b - a; + resultIsNegative = !aIsNegative; + } + } + oopResult = magnitude64BitIntegerForneg(result, resultIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Attempt to test-and-set the ownership of the critical section. If not + owned, set the owner to the current process and answer false. If owned by + the current process answer true. If owned by some other process answer + nil. For simulation if there is an argument it is taken to be the + effective activeProcess + (see Process>>effectiveProcess). */ + + /* InterpreterPrimitives>>#primitiveTestAndSetOwnershipOfCriticalSection */ +static void +primitiveTestAndSetOwnershipOfCriticalSection(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + sqInt criticalSection; + sqInt objOop; + sqInt owningProcess; + sqInt owningProcessIndex; + + if (GIV(argumentCount) > 0) { + /* rcvr */ + criticalSection = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + activeProc = longAt(GIV(stackPointer)); + if (/* isOopForwarded: */ + ((!(activeProc & (tagMask())))) + && ((!((longAt((void *)(activeProc))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + } + else { + /* rcvr */ + criticalSection = longAt(GIV(stackPointer)); + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + } + + /* CriticalSections are laid out like Semaphores */ + owningProcessIndex = ExcessSignalsIndex; + owningProcess = longAt((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord())))))); + if (owningProcess == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(owningProcessIndex, criticalSection, activeProc)); + assert(isNonImmediate(criticalSection)); + if (oopisGreaterThanOrEqualTo(criticalSection, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeProc & (tagMask())))) + && (oopisLessThan(activeProc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(criticalSection + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(criticalSection); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord()))))),activeProc); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(falseObj)); + return; + } + if (owningProcess == activeProc) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(trueObj)); + return; + } + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); +} + + +/* Return true if the host OS does support the given display depth. */ + + /* InterpreterPrimitives>>#primitiveTestDisplayDepth */ +static void +primitiveTestDisplayDepth(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bitsPerPixel; + sqInt integerPointer; + sqInt okay; + char *sp; + + okay = 0; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + bitsPerPixel = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + bitsPerPixel = 0; + } + if (!GIV(primFailCode)) { + okay = ioHasDisplayDepth(bitsPerPixel); + } + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (okay + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + +/* Given an object with indexable pointer fields, reduce the size of the + indexable fields + to the requested size. Answer the number of bytes freed, or zero if the + object cannot + be shortened. + */ + + /* InterpreterPrimitives>>#primitiveTestShortenIndexableSize */ +#if TestingPrimitives +EXPORT(sqInt) +primitiveTestShortenIndexableSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt array; + sqInt delta; + sqInt indexableSize; + sqInt integer; + sqInt newSizeOop; + usqInt numSlots; + + newSizeOop = longAt(GIV(stackPointer)); + array = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(((((newSizeOop) & 7) == 1)) + && (((((newSizeOop >> 3)) >= 0) && (((newSizeOop >> 3)) <= ((stSizeOf(array)) - 1)))))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + if (!(/* canShorten: */ + ((!(array & (tagMask())))) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(array)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(array + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > 0) + && ((((byteAt((void *)(array + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())) + || (((((byteAt((void *)(array + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(array + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1)))))))) { + return (GIV(primFailCode) = PrimErrBadReceiver); + } + indexableSize = (newSizeOop >> 3); + + /* begin shorten:toIndexableSize: */ + assert((indexableSize >= 0) + && (indexableSize < (lengthOf(array)))); + delta = doShortentoIndexableSize(array, indexableSize); + assert((lengthOf(followMaybeForwarded(array))) == indexableSize); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + integer = delta; + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); + return 0; +} +#endif /* TestingPrimitives */ + + +/* Multiply the receiver by the power of the argument. + Receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveTimesTwoPower */ +static void +primitiveTimesTwoPower(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aFloatOop; + sqInt arg; + usqLong bits; + double rcvr; + double result; + char *sp; + sqInt twiceMaxExponent; + + arg = longAt(GIV(stackPointer)); + if (!((((arg) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + arg = (arg >> 3); + + /* clip arg to at most int range; ldexp's last arg is of type int */ + twiceMaxExponent = 0x1000; + if (arg < (-twiceMaxExponent)) { + arg = -twiceMaxExponent; + } + else { + if (arg > twiceMaxExponent) { + arg = twiceMaxExponent; + } + } + aFloatOop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(aFloatOop)); + if (aFloatOop & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(aFloatOop)); + bits = ((((usqInt)aFloatOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + } + else { + fetchFloatAtinto(aFloatOop + BaseHeaderSize, rcvr); + } + result = ldexp(rcvr, ((int) arg)); + + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(result)); + GIV(stackPointer) = sp; +} + + +/* Integral part of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveTruncated */ +static void +primitiveTruncated(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + double doubleValue; + sqInt rcvr; + double trunc; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + modf(doubleValue, (&trunc)); + if (((trunc >= (((double) (MinSmallInteger) ))) && (trunc <= (((double) (MaxSmallInteger) ))))) { + /* stackTopPut: */ + longAtput(GIV(stackPointer),(((usqInt)(((sqInt)trunc)) << 3) | 1)); + return; + } + + /* make Nicolas Cellier's otherwise recursive BoxedFloat64>>truncated nice 2/7/2025 07:34 work on 32-bits. */ + + /* a.k.a. trunc abs <= Float maxExactInteger asFloat */ + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } +} + + +/* Allocate a new indexable instance. Fail if the allocation would leave less + than lowSpaceThreshold bytes free. May cause a GC. + */ + + /* InterpreterPrimitives>>#primitiveUninitializedNewWithArg */ +static void +primitiveUninitializedNewWithArg(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt classObj; + sqInt err; + int hash; + sqInt instSpec; + sqInt instSpecSqInt; + sqInt newObj; + usqInt newObjUsqInt; + usqInt numBytes; + usqInt numSlots; + sqInt obj; + sqInt reasonCode; + usqIntptr_t size; + sqInt value; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + size = null; + goto l1; + } + size = value; + goto l1; + } + + /* don't inline the rare case */ + size = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* positiveMachineIntegerValueOf: succeeds only for non-negative integers. */ + classObj = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin instantiateUninitializedClass:indexableSize: */ + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpecSqInt = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + assert(isPureBitsFormat(instSpecSqInt)); + switch (instSpecSqInt) { + case sixtyFourBitIndexableFormat(): + numSlots = size; + break; + case firstLongFormat(): + if ((classIndex == ClassFloatCompactIndex) + && (size != 2)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + obj = null; + goto l3; + } + numSlots = (size + 1) / 2; + instSpecSqInt += size & 1; + break; + case firstShortFormat(): + numSlots = (size + 3) / 4; + instSpecSqInt += (4 - size) & 3; + break; + case firstByteFormat(): + numSlots = (size + 7) / 8; + instSpecSqInt += (8 - size) & 7; + break; + default: + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + obj = null; + goto l3; + } + + /* not bits indexable */ + if (!classIndex) { + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + obj = null; + goto l3; + } + } + if (numSlots > ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (numSlots > (0x10000000000LL)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + obj = null; + goto l3; + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpecSqInt, classIndex); + } + else { + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if (((numSlots) >> 56) > 0) { + newObj = null; + goto l2; + } + newObjUsqInt = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObjUsqInt = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, instSpecSqInt, classIndex); + goto l2; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObjUsqInt),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(instSpecSqInt) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObjUsqInt),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(instSpecSqInt) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObjUsqInt % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newObj = newObjUsqInt; + /* end allocateSlots:format:classIndex: */ +l2:; + } + obj = newObj; + /* end instantiateUninitializedClass:indexableSize: */ +l3: + if (obj) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + instSpec = (((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer) + (1 * BytesPerWord))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + reasonCode = (/* isPureBitsFormat: */ + (instSpec >= (sixtyFourBitIndexableFormat())) + && (instSpec < (firstCompiledMethodFormat())) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + +/* Allocate a new pinned indexable instance with junk contents, saving the + time for initializing with 0. Fail if the receiver us not a pure bits + class. Fail + if the allocation would leave less than lowSpaceThreshold bytes free. */ + + /* InterpreterPrimitives>>#primitiveUninitializedPinnedNewWithArg */ +static void +primitiveUninitializedPinnedNewWithArg(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt classObj; + sqInt err; + int hash; + sqInt instSpec; + sqInt instSpecSqInt; + sqInt newObj; + usqInt numSlots; + sqInt obj; + sqInt reasonCode; + usqIntptr_t size; + sqInt value; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + size = null; + goto l1; + } + size = value; + goto l1; + } + + /* don't inline the rare case */ + size = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* positiveMachineIntegerValueOf: succeeds only for non-negative integers. */ + classObj = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin inOldSpaceInstantiateUninitializedPinnedClass:indexableSize: */ + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpecSqInt = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + switch (instSpecSqInt) { + case sixtyFourBitIndexableFormat(): + numSlots = size; + break; + case firstLongFormat(): + if ((classIndex == ClassFloatCompactIndex) + && (size != 2)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + obj = null; + goto l2; + } + numSlots = (size + 1) / 2; + instSpecSqInt += size & 1; + break; + case firstShortFormat(): + numSlots = (size + 3) / 4; + instSpecSqInt += (4 - size) & 3; + break; + case firstByteFormat(): + numSlots = (size + 7) / 8; + instSpecSqInt += (8 - size) & 7; + break; + default: + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + obj = null; + goto l2; + } + + /* non-indexable and/or pointers */ + if (!classIndex) { + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + obj = null; + goto l2; + } + } + if (numSlots > (0x10000000000LL)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + obj = null; + goto l2; + } + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpecSqInt, classIndex); + obj = newObj; + /* end inOldSpaceInstantiateUninitializedPinnedClass:indexableSize: */ +l2: + if (obj) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + instSpec = (((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer) + (1 * BytesPerWord))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + reasonCode = (/* isPureBitsFormat: */ + (instSpec >= (sixtyFourBitIndexableFormat())) + && (instSpec < (firstCompiledMethodFormat())) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + +/* Update the VMs notion of the current timezone. The VM sets its notion + of the timezone once at start-up. If one wants the VM to keep its notion + up-to-date arrange to invoke this primitive periodically. */ + + /* InterpreterPrimitives>>#primitiveUpdateTimezone */ +static void +primitiveUpdateTimezone(void) +{ + ioUpdateVMTimezone(); +} + + +/* Return the value of the microsecond clock in UTC as an integer. + This is the number of microseconds since the Smalltalk epoch, 1901/1/1 + 12:00am. The microsecond clock is at least 60 bits wide which means it'll + get to around August + 38435 before it wraps around. Be sure to put it on your calendar. This + primitive accesses the time as answered by the OS. */ + + /* InterpreterPrimitives>>#primitiveUTCMicrosecondClock */ +static void +primitiveUTCMicrosecondClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = positive64BitIntegerFor(ioUTCMicrosecondsNow()); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Answer an array with UTC microseconds since the Smalltalk epoch and the + current seconds offset from GMT in the local time zone. Any pointer object + with two or more slots) may be supplied as a parameter. */ + + /* InterpreterPrimitives>>#primitiveUtcAndTimezoneOffset */ +static void +primitiveUtcAndTimezoneOffset(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt resultArray; + sqInt valuePointer; + + if (GIV(argumentCount) > 0) { + if (GIV(argumentCount) > 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + resultArray = longAt(GIV(stackPointer)); + if (!((/* isPointers: */ + ((!(resultArray & (tagMask())))) + && (((byteAt((void *)(resultArray + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((lengthOf(resultArray)) >= 2))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + else { + resultArray = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 2); + } + + /* N.B. No pushRemappableOop:/popRemappableOop in Cog because positive64BitIntegerFor: et al use + eeInstantiate... allocators which are guaranteed not to do a GC. */ + valuePointer = (((usqInt)(ioLocalSecondsOffset()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultArray)) + && (!(isForwarded(resultArray)))); + assert(validStorePointerUncheckedArgs(1, resultArray, valuePointer)); + longAtput((void *)((resultArray + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(ioUTCMicroseconds()); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(0, resultArray, valuePointer)); + assert(isNonImmediate(resultArray)); + if (oopisGreaterThanOrEqualTo(resultArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(resultArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(resultArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((resultArray + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),resultArray); +} + + +/* Answer an array with UTC microseconds since the Posix epoch and the + current seconds offset from GMT in the local time zone. An empty two + element array (or any object with two or more slots) may be supplied + as a parameter. + This is a named (not numbered) primitive in the null module (ie the VM) */ + + /* InterpreterPrimitives>>#primitiveUtcWithOffset */ +EXPORT(sqInt) +primitiveUtcWithOffset(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + static usqLong epochDelta = 2177452800000000ULL; + sqInt resultArray; + sqInt valuePointer; + + if (GIV(argumentCount) > 0) { + if (GIV(argumentCount) > 1) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + resultArray = longAt(GIV(stackPointer)); + if (!((/* isPointers: */ + ((!(resultArray & (tagMask())))) + && (((byteAt((void *)(resultArray + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((lengthOf(resultArray)) >= 2))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + } + else { + resultArray = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 2); + } + + /* N.B. No pushRemappableOop:/popRemappableOop in Cog because positive64BitIntegerFor: et al use + eeInstantiate... allocators which are guaranteed not to do a GC. */ + valuePointer = (((usqInt)(ioLocalSecondsOffset()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultArray)) + && (!(isForwarded(resultArray)))); + assert(validStorePointerUncheckedArgs(1, resultArray, valuePointer)); + longAtput((void *)((resultArray + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor((ioUTCMicrosecondsNow()) - epochDelta); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(0, resultArray, valuePointer)); + assert(isNonImmediate(resultArray)); + if (oopisGreaterThanOrEqualTo(resultArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(resultArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(resultArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((resultArray + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),resultArray); + return 0; +} + + +/* Return a string containing the path name of VM's directory. */ + + /* InterpreterPrimitives>>#primitiveVMPath */ +static void +primitiveVMPath(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt s; + sqInt sz; + + sz = vmPathSize(); + s = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord())))))), sz); + vmPathGetLength(s + BaseHeaderSize, sz); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),s); +} + + +/* Primitive. + 0 args: Answer whether the VM Profiler is running or not. + 1 arg: Copy the sample data into the supplied argument, which must be a + Bitmap of suitable size. Answer the number of samples copied into the + buffer. */ + + /* InterpreterPrimitives>>#primitiveVMProfileSamplesInto */ +static void +primitiveVMProfileSamplesInto(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + long bufferSize; + sqInt numSamples; + sqInt running; + sqInt sampleBuffer; + char *sp; + + bufferSize = 0; + running = 0; + ioNewProfileStatus((&running), (&bufferSize)); + if (!GIV(argumentCount)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer)),/* booleanObjectOf: */ + (running + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return; + } + if (!(GIV(argumentCount) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + sampleBuffer = longAt(GIV(stackPointer)); + if (!(((!(sampleBuffer & (tagMask())))) + && ((isPureBitsNonImm(sampleBuffer)) + && ((numBytesOf(sampleBuffer)) >= (bufferSize * BytesPerWord))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + numSamples = ioNewProfileSamplesInto(pointerForOop(sampleBuffer + BaseHeaderSize)); + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)numSamples << 3) | 1)); +} + + +/* Signal the given semaphore from within the interpreter. Used to serialize + callbacks. + */ + + /* InterpreterPrimitives>>#signalNoResume: */ +int +signalNoResume(sqInt aSemaphore) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int empty; + + /* begin isEmptyList: */ + assert(!(isForwarded(aSemaphore))); + empty = (longAt((void *)((aSemaphore + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj); + if (!empty) { + putToSleepyieldingIf(removeFirstLinkOfList(aSemaphore), 1); + } + return empty; +} + + +/* Answer a signed value of an integer up to the size of a machine word. + The object is not an immediate and is hoped to be a LargeInteger of size + <= word size. + */ + + /* InterpreterPrimitives>>#signedMachineIntegerValueOfObj: */ +static NoDbgRegParms sqIntptr_t +signedMachineIntegerValueOfObj(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bs; + sqInt ccIndex; + sqInt fmt; + sqInt isClassOfNonImmequalTocompactClassIndexRV; + usqIntptr_t limit; + usqIntptr_t magnitude; + sqInt negative; + usqInt numBytes; + usqInt numSlots; + usqIntptr_t value; + + assert(!(((((oop) & 7) == 1)))); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + isClassOfNonImmequalTocompactClassIndexRV = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (isClassOfNonImmequalTocompactClassIndexRV) { + negative = 0; + } + else { + negative = 1; + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + isClassOfNonImmequalTocompactClassIndexRV = ClassLargeNegativeIntegerCompactIndex == ccIndex; + if (!isClassOfNonImmequalTocompactClassIndexRV) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + } + + /* begin numBytesOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + bs = numBytes - (fmt & 7); + goto l1; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + bs = numBytes; + goto l1; + } + if (fmt >= (firstShortFormat())) { + bs = numBytes - (((fmt & 3) << 1)); + goto l1; + } + + /* fmt >= self firstLongFormat */ + bs = numBytes - (((fmt & 1) << 2)); + /* end numBytesOf: */ +l1: + if (bs > (sizeof(usqIntptr_t))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + if (((sizeof(sqIntptr_t)) == 8) + && (bs > 4)) { + magnitude = SQ_SWAP_8_BYTES_IF_BIGENDIAN((long64At((void *)((oop + BaseHeaderSize))))); + } + else { + magnitude = ((unsigned int) (SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((oop + BaseHeaderSize))))))); + } + limit = ((((usqIntptr_t)1)) << (((sizeof(sqIntptr_t)) * 8) - 1)); + if ((negative + ? magnitude > limit + : magnitude >= limit)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + if (negative) { + value = 0 - magnitude; + } + else { + value = magnitude; + } + return value; +} + + +/* Answer a signed value of an integer up to the size of a machine word. + The object may be either a positive SmallInteger or a LargeInteger of size + <= word size. + */ +/* inline the common case... */ + + /* InterpreterPrimitives>>#signedMachineIntegerValueOf: */ +sqIntptr_t +signedMachineIntegerValueOf(sqInt oop) +{ + if ((((oop) & 7) == 1)) { + return (oop >> 3); + } + return signedMachineIntegerValueOfObj(oop); +} + + +/* Answer the start of the Alien's data or fail if oop is not an Alien. */ + + /* InterpreterPrimitives>>#sizeOfAlienData: */ +usqInt +sizeOfAlienData(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt oopClass; + sqInt tagBits; + + /* begin is:KindOfClass: */ + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while ((oopClass != GIV(nilObj)) + && ((/* isPointers: */ + ((!(oopClass & (tagMask())))) + && (((byteAt((void *)(oopClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOfAny(oopClass)) > InstanceSpecificationIndex))) { + if (oopClass == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord())))))))) { + goto l1; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return 0; +l1: + return SQABS(longAt((void *)(oop + BaseHeaderSize))); +} + + +/* Answer the start of the Alien's data or fail if oop is not an Alien. */ + + /* InterpreterPrimitives>>#startOfAlienData: */ +void * +startOfAlienData(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt oopClass; + sqInt tagBits; + + /* begin is:KindOfClass: */ + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while ((oopClass != GIV(nilObj)) + && ((/* isPointers: */ + ((!(oopClass & (tagMask())))) + && (((byteAt((void *)(oopClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOfAny(oopClass)) > InstanceSpecificationIndex))) { + if (oopClass == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord())))))))) { + goto l1; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return 0; +l1: + return ((void *) (((longAt((void *)(oop + BaseHeaderSize))) > 0 + ? (oop + BaseHeaderSize) + BytesPerOop + : longAt((void *)((oop + BaseHeaderSize) + BytesPerOop))))); +} + + +/* Set the state of the primitive failure code/success flag, iff + successBoolean is false. If primFailCode is non-zero a primitive has + failed. If primFailCode + is greater than one then its value indicates the reason for failure. */ +/* Use returnTypeC: #sqInt because that's the way it is defined in sq.h. + Use no explicit return so that Slang doesn't fail an inlining type-check + when a primitive with return type void uses ^self success: false to exit. */ + + /* InterpreterPrimitives>>#success: */ +sqInt +success(sqInt successBoolean) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!successBoolean) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + return 0; +} + + /* LiveTypingCoInterpreter>>#ceCollectArgumentsTypeForFramefulActivation */ +void +ceCollectArgumentsTypeForFramefulActivation(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt arg; + sqInt argIndex; + sqInt fmt; + CogMethod *frameMethod; + sqInt index; + char *lastArgPointer; + sqInt maxNumArgs; + sqInt methodField; + sqInt methodObject; + sqInt numArgs; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt types; + sqInt typesArraySize; + sqInt wordSize; + + /* begin mframeHomeMethod: */ + methodField = longAt(GIV(framePointer) + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + frameMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + goto l1; + } + frameMethod = ((CogMethod *) (methodField & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + lastArgPointer = (GIV(framePointer) + FoxCallerSavedIP) + BytesPerWord; + methodObject = (frameMethod->methodObject); + numArgs = (frameMethod->cmNumArgs); + + /* begin collectArgumentsTypeFor:fromStackAt:count: */ + /* begin tempVarTypesArraysOf: */ + /* begin additionalMethodStateOf:at: */ + /* begin additionalMethodStateOf: */ + selectorOrProperties = penultimateLiteralOf(methodObject); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l4; + } + wordSize = BytesPerWord; + maxNumArgs = (((lengthOf(tempVarTypesArrays)) < numArgs) ? (lengthOf(tempVarTypesArrays)) : numArgs); + for (argIndex = 0; argIndex < maxNumArgs; argIndex += 1) { + /* begin followObjField:ofObject: */ + types = longAt((void *)((tempVarTypesArrays + BaseHeaderSize) + ((((usqInt)(argIndex) << (shiftForWord())))))); + assert(isNonImmediate(types)); + if ((!((longAt((void *)(types))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + types = fixFollowedFieldofObjectwithInitialValue(argIndex, tempVarTypesArrays, types); + } + arg = longAt((void *)(lastArgPointer + (argIndex * wordSize))); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (types) + && (isInstanceOfClassArray(types)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = arg & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(arg)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(types + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(types + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(types - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l3; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l3; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l3; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l3; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l3; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l3: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, types, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, types, objectClass)); + assert(isNonImmediate(types)); + if (oopisGreaterThanOrEqualTo(types, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(types + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(types); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2:; + } + /* end collectArgumentsTypeFor:fromStackAt:count: */ +l4:; +} + + /* LiveTypingCoInterpreter>>#ceCollectArgumentsTypeForFramlessSpilledActivationOf: */ +void +ceCollectArgumentsTypeForFramlessSpilledActivationOf(CogMethod *cogMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt arg; + sqInt argIndex; + sqInt fmt; + sqInt index; + char *lastArgPointer; + sqInt maxNumArgs; + sqInt methodObject; + sqInt numArgs; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt types; + sqInt typesArraySize; + sqInt wordSize; + + lastArgPointer = GIV(stackPointer) + (2 * BytesPerWord); + methodObject = (cogMethod->methodObject); + numArgs = (cogMethod->cmNumArgs); + + /* begin collectArgumentsTypeFor:fromStackAt:count: */ + /* begin tempVarTypesArraysOf: */ + /* begin additionalMethodStateOf:at: */ + /* begin additionalMethodStateOf: */ + selectorOrProperties = penultimateLiteralOf(methodObject); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l3; + } + wordSize = BytesPerWord; + maxNumArgs = (((lengthOf(tempVarTypesArrays)) < numArgs) ? (lengthOf(tempVarTypesArrays)) : numArgs); + for (argIndex = 0; argIndex < maxNumArgs; argIndex += 1) { + /* begin followObjField:ofObject: */ + types = longAt((void *)((tempVarTypesArrays + BaseHeaderSize) + ((((usqInt)(argIndex) << (shiftForWord())))))); + assert(isNonImmediate(types)); + if ((!((longAt((void *)(types))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + types = fixFollowedFieldofObjectwithInitialValue(argIndex, tempVarTypesArrays, types); + } + arg = longAt((void *)(lastArgPointer + (argIndex * wordSize))); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (types) + && (isInstanceOfClassArray(types)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = arg & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(arg)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(types + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(types + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(types - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, types, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, types, objectClass)); + assert(isNonImmediate(types)); + if (oopisGreaterThanOrEqualTo(types, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(types + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(types); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2:; + } + /* end collectArgumentsTypeFor:fromStackAt:count: */ +l3:; +} + + /* LiveTypingCoInterpreter>>#ceCollectArgumentsTypeForFramlessUnspilledActivationWithArg:arg:of: */ +void +ceCollectArgumentsTypeForFramlessUnspilledActivationWithArgargof(sqInt arg0, sqInt arg1, CogMethod *cogMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt fmt; + sqInt index; + sqInt methodObject; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + methodObject = (cogMethod->methodObject); + + /* begin collectArgumentsTypeFor:arg:arg: */ + /* begin tempVarTypesArraysOf: */ + /* begin additionalMethodStateOf:at: */ + /* begin additionalMethodStateOf: */ + selectorOrProperties = penultimateLiteralOf(methodObject); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l5; + } + typesArray = /* safeFollowObjField:ofObject: */ + (1 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(1, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = arg0 & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(arg0)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2: + typesArray = /* safeFollowObjField:ofObject: */ + (0 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(0, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l4; + } + objectClass = /* fetchClassOf: */ + ((tagBits = arg1 & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(arg1)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l3; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l3; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l3; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l3; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l3; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l3: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l4; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l4; + } + } + /* end collectTypeOf:in: */ +l4: + /* end collectArgumentsTypeFor:arg:arg: */ +l5:; +} + + /* LiveTypingCoInterpreter>>#ceCollectArgumentsTypeForFramlessUnspilledActivationWithArg:of: */ +void +ceCollectArgumentsTypeForFramlessUnspilledActivationWithArgof(sqInt arg, CogMethod *cogMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt fmt; + sqInt index; + sqInt methodObject; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + methodObject = (cogMethod->methodObject); + + /* begin collectArgumentsTypeFor:arg: */ + /* begin tempVarTypesArraysOf: */ + /* begin additionalMethodStateOf:at: */ + /* begin additionalMethodStateOf: */ + selectorOrProperties = penultimateLiteralOf(methodObject); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l3; + } + typesArray = /* safeFollowObjField:ofObject: */ + (0 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(0, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = arg & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(arg)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2: + /* end collectArgumentsTypeFor:arg: */ +l3:; +} + + /* LiveTypingCoInterpreter>>#ceCollectInstVarTypeFor:at:of: */ +void +ceCollectInstVarTypeForatof(sqInt rcvr, sqInt instVarIndex, sqInt assignedObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt index; + sqInt instVarTypesArrays; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt rcvrClass; + sqInt tagBits; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + /* begin collectInstanceVariableTypeFor:at:of: */ + rcvrClass = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)); + instVarTypesArrays = /* #instVarTypesArraysOf: #safeFollowObjField:ofObject: */ + (InstVarTypesArraysIndex < (lengthOf(rcvrClass)) + ? followObjFieldofObject(InstVarTypesArraysIndex, rcvrClass) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (instVarTypesArrays) + && (isInstanceOfClassArray(instVarTypesArrays)))) { + goto l3; + } + typesArray = /* safeFollowObjField:ofObject: */ + (instVarIndex < (lengthOf(instVarTypesArrays)) + ? followObjFieldofObject(instVarIndex, instVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = assignedObject & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(assignedObject)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2: + /* end collectTypeOf:in:at: */ +l3:; +} + + /* LiveTypingCoInterpreter>>#ceCollectRemoteTempVarTypeAt:of: */ +void +ceCollectRemoteTempVarTypeAtof(sqInt tempVarIndex, sqInt assignedObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt remoteTempTypesArrays; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + /* begin collectRemoteTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(GIV(framePointer)))->methodObject) + : longAt(GIV(framePointer) + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + remoteTempTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (RemoteTempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(RemoteTempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (remoteTempTypesArrays) + && (isInstanceOfClassArray(remoteTempTypesArrays)))) { + goto l3; + } + typesArray = /* safeFollowObjField:ofObject: */ + (tempVarIndex < (lengthOf(remoteTempTypesArrays)) + ? followObjFieldofObject(tempVarIndex, remoteTempTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = assignedObject & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(assignedObject)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2: + /* end collectTypeOf:in:at: */ +l3:; +} + + /* LiveTypingCoInterpreter>>#ceCollectReturnTypeFor:of: */ +void +ceCollectReturnTypeForof(CogMethod *cogMethod, sqInt returnObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt fmt; + sqInt index; + sqInt methodObject; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt returnTypesArray; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt typeAtIndex; + sqInt typesArraySize; + + methodObject = (cogMethod->methodObject); + + /* begin collectReturnTypeForMethod:of: */ + /* begin returnTypesArrayOf: */ + /* begin additionalMethodStateOf:at: */ + /* begin additionalMethodStateOf: */ + selectorOrProperties = penultimateLiteralOf(methodObject); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + returnTypesArray = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (ReturnTypesArrayIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(ReturnTypesArrayIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (returnTypesArray) + && (isInstanceOfClassArray(returnTypesArray)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = returnObject & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(returnObject)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(returnTypesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(returnTypesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(returnTypesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((returnTypesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, returnTypesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, returnTypesArray, objectClass)); + assert(isNonImmediate(returnTypesArray)); + if (oopisGreaterThanOrEqualTo(returnTypesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(returnTypesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(returnTypesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((returnTypesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2:; +} + + /* LiveTypingCoInterpreter>>#ceCollectTempVarTypeAt:of: */ +void +ceCollectTempVarTypeAtof(sqInt tempVarIndex, sqInt assignedObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(GIV(framePointer)))->methodObject) + : longAt(GIV(framePointer) + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l3; + } + typesArray = /* safeFollowObjField:ofObject: */ + (tempVarIndex < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(tempVarIndex, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = assignedObject & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(assignedObject)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2: + /* end collectTypeOf:in:at: */ +l3:; +} + + /* LiveTypingCoInterpreter>>#ceCollectTypesForPrimitiveSlotAt:put:of: */ +void +ceCollectTypesForPrimitiveSlotAtputof(sqInt slotIndex, sqInt assignedObject, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt index; + sqInt instVarIndex; + sqInt instVarTypesArrays; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt rcvrClass; + sqInt tagBits; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + if (!((((slotIndex) & 7) == 1))) { + return; + } + + /* Slot indices are 1-relative */ + instVarIndex = ((slotIndex >> 3)) - 1; + if (instVarIndex < 0) { + return; + } + + /* begin collectInstanceVariableTypeFor:at:of: */ + rcvrClass = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)); + instVarTypesArrays = /* #instVarTypesArraysOf: #safeFollowObjField:ofObject: */ + (InstVarTypesArraysIndex < (lengthOf(rcvrClass)) + ? followObjFieldofObject(InstVarTypesArraysIndex, rcvrClass) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (instVarTypesArrays) + && (isInstanceOfClassArray(instVarTypesArrays)))) { + goto l3; + } + typesArray = /* safeFollowObjField:ofObject: */ + (instVarIndex < (lengthOf(instVarTypesArrays)) + ? followObjFieldofObject(instVarIndex, instVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = assignedObject & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(assignedObject)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2: + /* end collectTypeOf:in:at: */ +l3:; +} + + /* LiveTypingCoInterpreter>>#primitiveSlotAtPut */ +static void +primitiveSlotAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt assignedObject; + sqInt fmt; + sqInt fmtSqInt; + sqInt index; + sqInt instVarIndex; + sqInt instVarTypesArrays; + sqInt newValue; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt rcvr; + sqInt rcvrClass; + sqInt rcvrSqInt; + sqInt result; + sqInt slotIndex; + sqInt tagBits; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + usqIntptr_t value; + sqInt valueSqInt; + + assignedObject = longAt(GIV(stackPointer)); + slotIndex = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + + /* begin superPrimitiveSlotAtPut */ + newValue = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rcvrSqInt = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (!((((index) & 7) == 1))) { + result = (GIV(primFailCode) = PrimErrBadArgument); + goto l2; + } + if (((rcvrSqInt & (tagMask())) != 0)) { + result = (GIV(primFailCode) = PrimErrBadReceiver); + goto l2; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvrSqInt + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + ) { + result = (GIV(primFailCode) = PrimErrNoModification); + goto l2; + } + fmt = (byteAt((void *)(rcvrSqInt + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(rcvrSqInt)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(rcvrSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvrSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if ((((usqInt)index)) < numSlots) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(rcvrSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + externalInstVarofContextput(index, rcvrSqInt, newValue); + } + else { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, rcvrSqInt, newValue)); + assert(isNonImmediate(rcvrSqInt)); + if (oopisGreaterThanOrEqualTo(rcvrSqInt, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(newValue & (tagMask())))) + && (oopisLessThan(newValue, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvrSqInt + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvrSqInt); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvrSqInt + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),newValue); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); + result = 0; + goto l2; + } + result = (GIV(primFailCode) = PrimErrBadIndex); + goto l2; + } + + /* begin positiveMachineIntegerValueOf: */ + if ((((newValue) & 7) == 1)) { + valueSqInt = (newValue >> 3); + if (valueSqInt < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + value = null; + goto l1; + } + value = valueSqInt; + goto l1; + } + + /* don't inline the rare case */ + value = positiveMachineIntegerValueOfObj(newValue); + /* end positiveMachineIntegerValueOf: */ +l1: + if (GIV(primFailCode)) { + GIV(primFailCode) = PrimErrBadArgument; + result = 0; + goto l2; + } + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + result = (GIV(primFailCode) = PrimErrUnsupported); + goto l2; + } + if ((((usqInt)value)) > 0xFF) { + result = (GIV(primFailCode) = PrimErrBadArgument); + goto l2; + } + + /* begin numBytesOfBytes: */ + fmtSqInt = (byteAt((void *)(rcvrSqInt + (formatFieldByteOffset())))) & (formatMask()); + assert(fmtSqInt >= (firstByteFormat())); + numSlots = ((((/* begin numSlotsOf: */ + assert((classIndexOf(rcvrSqInt)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(rcvrSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvrSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) << (shiftForWord()))) - (fmtSqInt & 7); + if ((((usqInt)index)) < numSlots) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvrSqInt + BaseHeaderSize) + index),value); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); + result = 0; + goto l2; + } + result = (GIV(primFailCode) = PrimErrBadIndex); + goto l2; + } + if (fmt >= (firstShortFormat())) { + if ((((usqInt)value)) > 0xFFFF) { + result = (GIV(primFailCode) = PrimErrBadArgument); + goto l2; + } + numSlots = ((usqInt)((numBytesOf(rcvrSqInt)))) >> 1; + if ((((usqInt)index)) < numSlots) { + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvrSqInt + BaseHeaderSize) + ((((usqInt)(index) << 1)))),value); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); + result = 0; + goto l2; + } + result = (GIV(primFailCode) = PrimErrBadIndex); + goto l2; + } + if (fmt >= (firstLongFormat())) { + if ((((usqInt)value)) > 0xFFFFFFFFU) { + result = (GIV(primFailCode) = PrimErrBadArgument); + goto l2; + } + numSlots = ((usqInt)((numBytesOf(rcvrSqInt)))) >> 2; + if ((((usqInt)index)) < numSlots) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvrSqInt + BaseHeaderSize) + ((((usqInt)(index) << 2)))),value); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); + result = 0; + goto l2; + } + result = (GIV(primFailCode) = PrimErrBadIndex); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvrSqInt)))) >> 3; + if ((((usqInt)index)) < numSlots) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvrSqInt + BaseHeaderSize) + ((((usqInt)(index) << 3)))),value); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); + result = 0; + goto l2; + } + result = (GIV(primFailCode) = PrimErrBadIndex); + goto l2; + } + result = (GIV(primFailCode) = PrimErrBadReceiver); + /* end superPrimitiveSlotAtPut */ +l2: + if (!result) { + /* Slot indices are 1-relative */ + instVarIndex = ((slotIndex >> 3)) - 1; + + /* begin collectInstanceVariableTypeFor:at:of: */ + rcvrClass = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)); + instVarTypesArrays = /* #instVarTypesArraysOf: #safeFollowObjField:ofObject: */ + (InstVarTypesArraysIndex < (lengthOf(rcvrClass)) + ? followObjFieldofObject(InstVarTypesArraysIndex, rcvrClass) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (instVarTypesArrays) + && (isInstanceOfClassArray(instVarTypesArrays)))) { + goto l3; + } + typesArray = /* safeFollowObjField:ofObject: */ + (instVarIndex < (lengthOf(instVarTypesArrays)) + ? followObjFieldofObject(instVarIndex, instVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l5; + } + objectClass = /* fetchClassOf: */ + ((tagBits = assignedObject & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(assignedObject)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l4; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l4; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l4; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l4; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l4: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l5; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l5; + } + } + /* end collectTypeOf:in: */ +l5: + /* end collectTypeOf:in:at: */ +l3:; + } + return; +} + + +/* Answer the appropriate become effect flags for objOop, or 0 if none. + The effect flags determine how much work is done after the become in + following forwarding pointers, + voiding method caches, etc. Flag jitted methods because these will have to + be freed after divorcing + frames and mapping native pcs to bytecode pcs in contexts using them. + See the comment in InterpreterPrimitives>>#primitiveArrayBecome */ + + /* Spur64BitCoMemoryManager>>#becomeEffectFlagsFor: */ +static NoDbgRegParms sqInt +becomeEffectFlagsFor(sqInt objOop) +{ + int hash; + sqInt methodHeader; + + hash = 0; + if (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) { + if ((((hash = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()))) != 0) + && ((classAtIndex(hash)) == objOop)) { + return BecamePointerObjectFlag + BecameActiveClassFlag; + } + return BecamePointerObjectFlag; + } + if (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + methodHeader = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + flagCogMethodForBecome(((CogMethod *) methodHeader)); + return BecameCompiledMethodFlag + BecameJittedCompiledMethodFlag; + } + return BecameCompiledMethodFlag; + } + return 0; +} + + +/* The check against scavengeThreshold in + genInstantiate64BitSignedIntegerValue:* et al compares + freeStart against scavengeThreshold minus the allocation size. Hence the + assert must compensate here. + */ + + /* Spur64BitCoMemoryManager>>#ceScheduleScavenge */ +void +ceScheduleScavenge(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(((GIV(freeStart) + BaseHeaderSize) + (sizeof(double))) >= GIV(scavengeThreshold)); + + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); +} + + /* Spur64BitCoMemoryManager>>#classSmallFloat */ +sqInt +classSmallFloat(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)((smallFloatTag())) << (shiftForWord())))))); +} + + +/* Perform an integrity/leak check using the heapMap. Set a bit at each + object's header. */ +/* Perform an integrity/leak check using the heapMap. Set a bit at each + object's header. + Override to set a bit at each Cog method */ + + /* Spur64BitCoMemoryManager>>#clearLeakMapAndMapAccessibleObjects */ +static void +clearLeakMapAndMapAccessibleObjects(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + clearHeapMap(); + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + heapMapAtWordPut(pointerForOop(obj), 1); + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + addCogMethodsToHeapMap(); +} + + +/* Ensure there are no forwarded literals in the argument. */ + + /* Spur64BitCoMemoryManager>>#ensureNoForwardedLiteralsIn: */ +void +ensureNoForwardedLiteralsIn(sqInt aMethodObj) +{ + followForwardedObjectFieldstoDepth(aMethodObj, 0); +} + + +/* Follow pointers in the object to depth. + Answer if any forwarders were found. + How to avoid cyclic structures?? A temporary mark bit? eem 6/22/2020 no + need since depth is always finite. */ + + /* Spur64BitCoMemoryManager>>#followForwardedObjectFields:toDepth: */ +static NoDbgRegParms sqInt +followForwardedObjectFieldstoDepth(sqInt objOop, sqInt depth) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt fmt; + sqInt found; + sqInt header; + sqInt headerSqInt; + sqInt i; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt referent; + sqInt sp; + + found = 0; + assert((isPointers(objOop)) + || (isOopCompiledMethod(objOop))); + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin numPointerSlotsOf:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + numSlots = CtxtTempFrameStart + contextSize; + goto l2; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + goto l2; + } + if (fmt == (forwardedFormat())) { + numSlots = 1; + goto l2; + } + if (fmt < (firstCompiledMethodFormat())) { + numSlots = 0; + goto l2; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf:format: */ +l2: + + /* It is essential to skip the first field of a method because it may be a + reference to a Cog method in the method zone, not a real object at all. */ + for (i = ((fmt >= (firstCompiledMethodFormat()) + ? 1 + : 0)); i < numSlots; i += 1) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(oop & (tagMask())))) { + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + found = 1; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(i, objOop, oop)); + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(oop & (tagMask())))) + && (oopisLessThan(oop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),oop); + } + if ((depth > 0) + && ((/* hasPointerFields: */ + ((!(oop & (tagMask())))) + && (hasPointerFieldsNonImm(oop))) + && (followForwardedObjectFieldstoDepth(oop, depth - 1)))) { + found = 1; + } + } + } + return found; +} + + /* Spur64BitCoMemoryManager>>#freeStartAddress */ +usqInt +freeStartAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(freeStart)))); +} + + /* Spur64BitCoMemoryManager>>#getScavengeThreshold */ +usqInt +getScavengeThreshold(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(scavengeThreshold); +} + + +/* Answer the header of the argument even though + it may have its header word in a forwarding block + (which shouldn't happen with Spur). */ + + /* Spur64BitCoMemoryManager>>#headerWhileForwardingOf: */ +static NoDbgRegParms sqLong +headerWhileForwardingOf(sqInt aCompiledMethodObjOop) +{ + assert(!(isForwarded(aCompiledMethodObjOop))); + return long64At((void *)(aCompiledMethodObjOop)); +} + + +/* Answer true if the argument contains indexable bytes. See comment in + formatOf: + */ +/* Note: Includes CompiledMethods. */ + + /* Spur64BitCoMemoryManager>>#isBytes: */ +sqInt +isBytes(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())); +} + + +/* A lenient tester of forwarded class indices for inline cache management in + the Cogit. + */ + + /* Spur64BitCoMemoryManager>>#isForwardedClassIndex: */ +sqInt +isForwardedClassIndex(sqInt maybeClassIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt entry; + sqInt fieldIndex; + + if ((((usqInt)maybeClassIndex)) >= (1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift())))) { + return 0; + } + fieldIndex = ((usqInt)(maybeClassIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return 0; + } + fieldIndex = maybeClassIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + entry = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + return (!((longAt((void *)(entry))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))); +} + + /* Spur64BitCoMemoryManager>>#isImmediateClass: */ +int +isImmediateClass(sqInt classObj) +{ + return ((((usqInt)((((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) == (forwardedFormat()); +} + + +/* Answer if obj is young. Require that obj is non-immediate. Override to + filter-out Cog methods + */ + + /* Spur64BitCoMemoryManager>>#isReallyYoungObject: */ +sqInt +isReallyYoungObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(isNonImmediate(objOop)); + return (oopisLessThan(objOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(objOop, GIV(newSpaceStart))); +} + + +/* Answer the method header of a CompiledMethod object. + If the method has been cogged then the header is a pointer to + the CogMethod and the real header will be stored in the CogMethod. */ + + /* Spur64BitCoMemoryManager>>#methodHeaderOf: */ +sqInt +methodHeaderOf(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + + assert(isCompiledMethod(methodObj)); + header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + return ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); +} + + /* Spur64BitCoMemoryManager>>#needGCFlagAddress */ +usqInt +needGCFlagAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(needGCFlag)))); +} + + /* Spur64BitCoMemoryManager>>#printCantBeObject:on: */ +static NoDbgRegParms void +printCantBeObjecton(sqInt oop, FILE *aStream) +{ + char *where; + + fprintf(aStream, + "%p%s\n", + ((void *)oop), + ((where = whereIsMaybeCodeThing(oop)) + ? where + : (oop & 7 /* (allocationUnit - 1) */ + ? " is misaligned" + : whereIs(oop)))); +} + + /* Spur64BitCoMemoryManager>>#specialObjectsOopAddress */ +usqInt +specialObjectsOopAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(specialObjectsOop)))); +} + + +/* For the purposes of become: send selector to the cogit with obj1, obj2 + and aBool and answer the result. Undo forwarding for the selector. */ + + /* Spur64BitCoMemoryManager>>#withoutForwardingOn:and:with:sendToCogit: */ +sqInt +withoutForwardingOnandwithsendToCogit(sqInt obj1, sqInt obj2, sqInt aBool, sqInt (*selector)(sqInt,sqInt,sqInt)) +{ + sqInt targetA; + sqInt targetB; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj1)); + targetA = longAt((void *)((obj1 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(targetA & (tagMask())))) + && ((!((longAt((void *)(targetA))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + targetA = longAt((void *)((targetA + BaseHeaderSize) + (0U << (shiftForWord())))); + } + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj2)); + targetB = longAt((void *)((obj2 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(targetB & (tagMask())))) + && ((!((longAt((void *)(targetB))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + targetB = longAt((void *)((targetB + BaseHeaderSize) + (0U << (shiftForWord())))); + } + return selector(targetA, targetB, aBool); +} + + +/* Answer the address immediately following an object. */ + + /* Spur64BitMemoryManager>>#addressAfter: */ +static NoDbgRegParms usqInt +addressAfter(sqInt objOop) +{ + usqInt numSlots; + + numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + if (!numSlots) { + return (objOop + 8 /* allocationUnit */) + BaseHeaderSize; + } + if (numSlots == (numSlotsMask())) { + numSlots = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8; + } + return (objOop + BaseHeaderSize) + (((numSlots << (shiftForWord())))); +} + + +/* Answer the oop of a chunk of space in oldSpace with numSlots slots. Try + and allocate in a segment that already includes pinned objects. The header + of the + result will have been filled-in but not the contents. */ + + /* Spur64BitMemoryManager>>#allocateSlotsForPinningInOldSpace:bytes:format:classIndex: */ +static NoDbgRegParms sqInt +allocateSlotsForPinningInOldSpacebytesformatclassIndex(sqInt numSlots, usqInt totalBytes, sqInt formatField, sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt acceptedChunk; + sqInt acceptedNode; + sqInt child; + usqInt childBytes; + sqInt chunk; + usqInt index; + usqInt initialIndex; + sqInt lilliputian; + sqInt lilliputianSqInt; + sqInt next; + sqInt nextFreeChunk; + sqInt nextSqInt; + sqInt node; + sqInt prev; + + /* begin allocateOldSpaceChunkOfBytes:suchThat: */ + next = 0; + prev = 0; + + /* be optimistic (& don't wait for the write) */ + GIV(totalFreeOldSpace) -= totalBytes; + initialIndex = totalBytes / 8 /* allocationUnit */; + if ((initialIndex < 64 /* numFreeLists */) + && ((1ULL << initialIndex) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << initialIndex)) != 0)) { + if ((node = GIV(freeLists)[initialIndex])) { + prev = 0; + while (node != 0) { + assert(node == (startOfObject(node))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(node)); + next = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + if (((segmentContainingObj(node))->containsPinned)) { + if (prev) { + /* begin setNextFreeChunkOf:withValue:chunkBytes: */ + /* begin isLilliputianSize: */ + assert(totalBytes >= (BaseHeaderSize + (allocationUnit()))); + lilliputianSqInt = totalBytes == (BaseHeaderSize + 8 /* allocationUnit */); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((next == 0) + || (isFreeObject(next))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),next); + if ((next != 0) + && (!lilliputianSqInt)) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + } + } + else { + /* begin unlinkFreeChunk:atIndex:chunkBytes: */ + /* begin isLilliputianSize: */ + assert(totalBytes >= (BaseHeaderSize + (allocationUnit()))); + lilliputian = totalBytes == (BaseHeaderSize + 8 /* allocationUnit */); + + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(node)) == (initialIndex * (allocationUnit()))) + && ((initialIndex > 1) + && ((startOfObject(node)) == node))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[initialIndex] = ((nextSqInt = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))))); + if ((!lilliputian) + && (nextSqInt != 0)) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextSqInt)); + longAtput((void *)((nextSqInt + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + } + chunk = node; + goto l1; + } + prev = node; + node = next; + } + } + else { + GIV(freeListsMask) -= 1ULL << initialIndex; + } + } + + /* first search for free chunks of a multiple of chunkBytes in size */ + index = initialIndex; + while ((((index += initialIndex)) < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << index)) != 0)) { + if ((node = GIV(freeLists)[index])) { + prev = 0; + while (node != 0) { + assert(node == (startOfObject(node))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(node)); + next = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + if (((segmentContainingObj(node))->containsPinned)) { + if (prev) { + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((next == 0) + || (isFreeObject(next))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),next); + if (next) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + } + } + else { + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(node)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(node)) == node))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((nextSqInt = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))))); + if (nextSqInt) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextSqInt)); + longAtput((void *)((nextSqInt + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + } + freeChunkWithBytesat((index * 8 /* allocationUnit */) - totalBytes, (/* startOfObject: */ + ((byteAt((void *)(node + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? node - BaseHeaderSize + : node)) + totalBytes); + chunk = node; + goto l1; + } + prev = node; + node = next; + } + } + else { + GIV(freeListsMask) -= 1ULL << index; + } + } + } + + /* now get desperate and use the first that'll fit. + Note that because the minimum free size is 16 bytes (2 * allocationUnit), to + leave room for the forwarding pointer/next free link, we can only break chunks + that are at least 16 bytes larger, hence start at initialIndex + 2. */ + index = initialIndex + 1; + while ((((index += 1)) < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << index)) != 0)) { + if ((node = GIV(freeLists)[index])) { + prev = 0; + while (node != 0) { + assert(node == (startOfObject(node))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(node)); + next = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + if (((segmentContainingObj(node))->containsPinned)) { + if (prev) { + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((next == 0) + || (isFreeObject(next))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),next); + if (next) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + } + } + else { + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(node)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(node)) == node))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((nextSqInt = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))))); + if (nextSqInt) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextSqInt)); + longAtput((void *)((nextSqInt + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + } + freeChunkWithBytesat((index * 8 /* allocationUnit */) - totalBytes, (/* startOfObject: */ + ((byteAt((void *)(node + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? node - BaseHeaderSize + : node)) + totalBytes); + chunk = node; + goto l1; + } + prev = node; + node = next; + } + } + else { + GIV(freeListsMask) -= 1ULL << index; + } + } + } + } + + /* Large chunk, or no space on small free lists. Search the large chunk list. + Large chunk list organized as a tree, each node of which is a list of chunks + of the same size. Beneath the node are smaller and larger blocks. + When the search ends parent should hold the smallest chunk at least as + large as chunkBytes, or 0 if none. acceptedChunk and acceptedNode save + us from having to back-up when the acceptanceBlock filters-out all nodes + of the right size, but there are nodes of the wrong size it does accept. */ + child = GIV(freeLists)[0]; + node = (acceptedChunk = (acceptedNode = 0)); + while (child != 0) { + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(child)); + childBytes = bytesInBody(child); + if (childBytes == totalBytes) { + node = child; + while (1) { + prev = node; + node = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + if (!(node != 0)) break; + if (((segmentContainingObj(node))->containsPinned)) { + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(node)); + nextFreeChunk = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + } + chunk = /* startOfObject: */ + ((byteAt((void *)(node + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? node - BaseHeaderSize + : node); + goto l1; + } + } + if (((segmentContainingObj(child))->containsPinned)) { + next = longAt((void *)((child + BaseHeaderSize) + (0U << (shiftForWord())))); + if (next) { + inFreeTreeReplacewith(child, next); + } + else { + unlinkSolitaryFreeTreeNode(child); + } + + /* no list; remove the interior node + list; replace node with it */ + chunk = /* startOfObject: */ + ((byteAt((void *)(child + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? child - BaseHeaderSize + : child); + goto l1; + } + } + + /* size match; try to remove from list at node. */ + if (child) { + /* Note that because the minimum free size is 16 bytes (2 * allocationUnit), to + leave room for the forwarding pointer/next free link, we can only break chunks + that are at least 16 bytes larger, hence reject chunks < 2 * allocationUnit larger. */ + if (childBytes <= (totalBytes + 8 /* allocationUnit */)) { + child = longAt((void *)((child + BaseHeaderSize) + (4U << (shiftForWord())))); + } + else { + node = child; + child = longAt((void *)((node + BaseHeaderSize) + (3U << (shiftForWord())))); + if (!acceptedNode) { + acceptedChunk = node; + + /* first search the list. */ + do { + acceptedChunk = longAt((void *)((acceptedChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + if ((acceptedChunk != 0) + && (((segmentContainingObj(acceptedChunk))->containsPinned))) { + acceptedNode = node; + } + } while((acceptedChunk != 0) + && (acceptedNode == 0)); + + /* nothing on the list; will the node do? This prefers + acceptable nodes higher up the tree over acceptable + list elements further down, but we haven't got all day... */ + if ((acceptedNode == 0) + && (((segmentContainingObj(node))->containsPinned))) { + acceptedNode = node; + + /* break out of loop now we have an acceptedNode */ + child = 0; + } + } + } + } + } + if (acceptedNode) { + if (acceptedChunk) { + assert((bytesInBody(acceptedChunk)) >= (totalBytes + (allocationUnit()))); + while (1) { + next = longAt((void *)((acceptedNode + BaseHeaderSize) + (0U << (shiftForWord())))); + if (!(next != acceptedChunk)) break; + acceptedNode = next; + } + nextFreeChunk = longAt((void *)((acceptedChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(acceptedNode)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((acceptedNode + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((acceptedNode == 0) + || (isFreeObject(acceptedNode))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),acceptedNode); + } + freeChunkWithBytesat((bytesInBody(acceptedChunk)) - totalBytes, (/* startOfObject: */ + ((byteAt((void *)(acceptedChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? acceptedChunk - BaseHeaderSize + : acceptedChunk)) + totalBytes); + chunk = /* startOfObject: */ + ((byteAt((void *)(acceptedChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? acceptedChunk - BaseHeaderSize + : acceptedChunk); + goto l1; + } + next = longAt((void *)((acceptedNode + BaseHeaderSize) + (0U << (shiftForWord())))); + if (next) { + inFreeTreeReplacewith(acceptedNode, next); + } + else { + unlinkSolitaryFreeTreeNode(acceptedNode); + } + + /* no list; remove the interior node + list; replace node with it */ + assert((bytesInBody(acceptedNode)) >= (totalBytes + (allocationUnit()))); + freeChunkWithBytesat((bytesInBody(acceptedNode)) - totalBytes, (/* startOfObject: */ + ((byteAt((void *)(acceptedNode + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? acceptedNode - BaseHeaderSize + : acceptedNode)) + totalBytes); + chunk = /* startOfObject: */ + ((byteAt((void *)(acceptedNode + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? acceptedNode - BaseHeaderSize + : acceptedNode); + goto l1; + } + + /* optimism was unfounded */ + GIV(totalFreeOldSpace) += totalBytes; + chunk = null; + /* end allocateOldSpaceChunkOfBytes:suchThat: */ +l1: + if (!chunk) { + chunk = allocateOldSpaceChunkOfBytes(totalBytes); + if (!chunk) { + return null; + } + ((segmentContainingObj(chunk))->containsPinned = 1); + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(chunk),numSlots + ((((usqInt)((numSlotsMask())) << (numSlotsFullShift()))))); + longAtput((void *)(chunk + BaseHeaderSize),(((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex) | (1U << (pinnedBitShift()))); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return chunk + BaseHeaderSize; + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + longAtput((void *)(chunk),(((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex) | (1U << (pinnedBitShift()))); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return chunk; +} + + +/* Answer the given integer with its bytes in the reverse order. */ + + /* Spur64BitMemoryManager>>#byteSwapped: */ +sqInt +byteSwapped(sqInt w) +{ + return SQ_SWAP_8_BYTES(w); +} + + +/* Answer the total number of bytes in an object including header and + possible overflow size header. + */ + + /* Spur64BitMemoryManager>>#bytesInBody: */ +static NoDbgRegParms usqInt +bytesInBody(sqInt objOop) +{ + usqLong header; + usqLong headerNumSlots; + usqInt numSlots; + + header = longAt((void *)(objOop)); + headerNumSlots = (header) >> (numSlotsFullShift()); + numSlots = (headerNumSlots == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : (headerNumSlots + ? headerNumSlots + : 1)); + return ((numSlots << (shiftForWord()))) + ((headerNumSlots == (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)); +} + + +/* Answer the default amount of memory to allocate for the eden space. + The actual value can be set via vmParameterAt: and/or a preference in the + ini file. + The shootout tests seem to plateau at 5 or 6Mb. + + Originally, both the 32-bit and 64-bit versions used the same 4Mb default. + Measuring the simulator on image start-up, the 64-bit system's eden at the + same point in start-up + (the first copyBits) is only 8% larger in bytes because it allocates 26% + fewer objects. + Some 21% of the objects in the 32-bit version's eden are large integers + and floats that + are representable as 64-bit immediates. + + But when running benchmarks such as the computer language shootout's + binary trees, + using the same amount of memory for the 64-bit system causes a significant + slow-down and a lot of compactions. So we now use 4Mb for 32-bits and 7Mb + for 64-bits. */ + + /* Spur64BitMemoryManager>>#defaultEdenBytes */ +static int +defaultEdenBytes(void) +{ + return 0x700000; +} + + +/* Answer the tag used in lookup caches for a receiver. This is the + receiver's classIndex. */ + + /* Spur64BitMemoryManager>>#fetchClassTagOf: */ +sqInt +fetchClassTagOf(sqInt oop) +{ + sqInt tagBits; + + return ((tagBits = oop & (tagMask())) + ? tagBits + : (longAt((void *)(oop))) & (classIndexMask())); +} + + /* Spur64BitMemoryManager>>#floatObjectOf: */ +sqInt +floatObjectOf(double aFloat) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newFloatObj; + usqInt newObj; + usqInt numBytes; + sqInt numSlots; + usqLong rawFloat; + usqLong rot; + + if (isSmallFloatValue(aFloat)) { + /* begin smallFloatObjectOf: */ + assert(isSmallFloatValue(aFloat)); + memcpy((&rawFloat), (&aFloat), sizeof(rawFloat)); + + /* begin smallFloatObjectOfBits: */ + assert(isSmallFloatValueBits(rawFloat)); + rot = (((rawFloat) >> 0x3F) & 1) + ((rawFloat << 1)); + if (rot > 1) { + rot -= (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + assert(rot > 0); + } + + /* a.k.a. ~= +/-0.0 */ + return ((rot << (numTagBits()))) + (smallFloatTag()); + } + numSlots = (sizeof(double)) / BytesPerOop; + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((numSlots >= 0) + && ((knownClassAtIndex(ClassFloatCompactIndex)) != GIV(nilObj))); + assert((firstLongFormat()) == (instSpecOfClass(knownClassAtIndex(ClassFloatCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newFloatObj = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((firstLongFormat())) << (formatShift()))))) + ClassFloatCompactIndex); + GIV(freeStart) += numBytes; + newFloatObj = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + storeFloatAtfrom(newFloatObj + BaseHeaderSize, aFloat); + return newFloatObj; +} + + +/* Answer the 64-bit value of the argument as raw bits. */ + + /* Spur64BitMemoryManager>>#floatValueBitsOf: */ +static NoDbgRegParms sqLong +floatValueBitsOf(sqInt floatOop) +{ + usqLong rot; + + assert(isFloatInstance(floatOop)); + if (floatOop & (tagMask())) { + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOop)); + rot = ((((usqInt)floatOop))) >> (numTagBits()); + if (rot > 1) { + rot += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + rot = ((rot << 0x3F)) + (((((usqInt)rot))) >> 1); + return rot; + } + return long64At((void *)((floatOop + BaseHeaderSize))); +} + + +/* Answer the C double precision floating point value of the argument, + or fail if it is not a Float, and answer 0. + Note: May be called by translated primitive code. */ + + /* Spur64BitMemoryManager>>#floatValueOf: */ +double +floatValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + double result; + sqInt tagBits; + double value; + + if ((tagBits = oop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + return value; + } + } + else { + if (((longAt((void *)(oop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(oop + BaseHeaderSize, result); + return result; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0.0; +} + + +/* This is a horrible hack for getting to the first object in eden if + pastSpace is almost full. + If there is only one (64-bit) word at the end of pastSpace there is no + room for a full + bridge, but there is room for this hack. */ + + /* Spur64BitMemoryManager>>#hackSlimBridgeTo:at: */ +static NoDbgRegParms void +hackSlimBridgeToat(sqInt objOop, sqInt startAddress) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(oopisLessThan(startAddress, objOop)); + assert(oopisLessThanOrEqualTo(objOop, GIV(oldSpaceStart))); + assert(((startAddress + (allocationUnit())) == objOop) + || (((startAddress + (allocationUnit())) + (allocationUnit())) == objOop)); + longAtput((void *)(startAddress),((startAddress + 8 /* allocationUnit */) == objOop + ? ((sqInt)((usqInt)((numSlotsMask())) << (numSlotsFullShift()))) + : ((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + 1)); +} + + +/* A negative header selects the alternate bytecode set. */ + + /* Spur64BitMemoryManager>>#headerIndicatesAlternateBytecodeSet: */ +int +headerIndicatesAlternateBytecodeSet(sqInt methodHeader) +{ + return (((sqLong) methodHeader)) < 0; +} + + +/* must have room for a header (single or double) plus the next free pointer */ + + /* Spur64BitMemoryManager>>#initFreeChunkWithBytes:at: */ +static NoDbgRegParms sqInt +initFreeChunkWithBytesat(usqLong numBytes, sqInt address) +{ + sqLong numSlots; + + assert(((numBytes % (allocationUnit())) == 0) + && (numBytes >= (BaseHeaderSize + BytesPerOop))); + + /* double header */ + if (numBytes >= (((((usqInt)((numSlotsMask())) << (shiftForWord())))) + BaseHeaderSize)) { + numSlots = ((usqLong)(((numBytes - BaseHeaderSize) - BaseHeaderSize))) >> (shiftForWord()); + longAtput((void *)(address),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(address + 8),((sqInt)((usqInt)((numSlotsMask())) << (numSlotsFullShift())))); + return address + 8; + } + + /* single header */ + numSlots = ((usqLong)((numBytes - BaseHeaderSize))) >> (shiftForWord()); + assert(numSlots < (numSlotsMask())); + longAtput((void *)(address),((sqLong)((usqLong)(numSlots) << (numSlotsFullShift())))); + return address; +} + + +/* Must have room for a double header or a short object with the forwarding + slot (16 bytes either way). + */ + + /* Spur64BitMemoryManager>>#initSegmentBridgeWithBytes:at: */ +static NoDbgRegParms void +initSegmentBridgeWithBytesat(usqLong numBytes, sqInt address) +{ + sqLong numSlots; + + assert(((numBytes % (allocationUnit())) == 0) + && (numBytes >= (BaseHeaderSize + BaseHeaderSize))); + numSlots = ((usqLong)(((numBytes - BaseHeaderSize) - BaseHeaderSize))) >> (shiftForWord()); + if (numSlots) { + longAtput((void *)(address),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(address + BaseHeaderSize),(((((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + (1U << (pinnedBitShift()))) + (1ULL << (markedBitFullShift()))) + ((((usqInt)((sixtyFourBitIndexableFormat())) << (formatShift()))))) + (segmentBridgePun())); + } + else { + longAtput((void *)(address),(((1U << (pinnedBitShift())) + (1ULL << (markedBitFullShift()))) + ((((usqInt)((sixtyFourBitIndexableFormat())) << (formatShift()))))) + (segmentBridgePun())); + } +} + + +/* Allocate an instance of a variable class, excepting CompiledMethod. */ + + /* Spur64BitMemoryManager>>#instantiateClass:indexableSize: */ +sqInt +instantiateClassindexableSize(sqInt classObj, usqInt nElements) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt err; + sqInt fillValue; + int hash; + sqInt instSpec; + sqInt newObj; + usqInt newObjUsqInt; + usqInt numBytes; + usqInt numSlots; + usqInt p; + usqInt toDoLimit; + + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + fillValue = 0; + switch (instSpec) { + case arrayFormat(): + numSlots = nElements; + fillValue = GIV(nilObj); + break; + case indexablePointersFormat(): + case weakArrayFormat(): + numSlots = (classFormat & ((1U << (fixedFieldsFieldWidth())) - 1)) + nElements; + fillValue = GIV(nilObj); + break; + case sixtyFourBitIndexableFormat(): + numSlots = nElements; + break; + case firstLongFormat(): + if ((classIndex == ClassFloatCompactIndex) + && (nElements != 2)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return null; + } + numSlots = (nElements + 1) / 2; + instSpec += nElements & 1; + break; + case firstShortFormat(): + numSlots = (nElements + 3) / 4; + instSpec += (4 - nElements) & 3; + break; + case firstByteFormat(): + numSlots = (nElements + 7) / 8; + instSpec += (8 - nElements) & 7; + break; + default: + if ((nElements != 0) + || (instSpec > 5 /* lastPointerFormat */)) { + return null; + } + numSlots = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + fillValue = GIV(nilObj); + } + + /* non-indexable + Some Squeak images include funky fixed subclasses of abstract variable + superclasses. e.g. DirectoryEntry as a subclass of ArrayedCollection. + The (Threaded)FFIPlugin expects to be able to instantiate ExternalData via + this method. + Hence allow fixed classes to be instantiated here iff nElements = 0. */ + if (!classIndex) { + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + return null; + } + } + if (numSlots > ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (numSlots > (0x10000000000LL)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return null; + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpec, classIndex); + } + else { + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if (((numSlots) >> 56) > 0) { + newObj = null; + goto l1; + } + newObjUsqInt = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObjUsqInt = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, instSpec, classIndex); + goto l1; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObjUsqInt),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObjUsqInt),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObjUsqInt % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newObj = newObjUsqInt; + /* end allocateSlots:format:classIndex: */ +l1:; + } + if (newObj) { + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),fillValue); + } + } + return newObj; +} + + +/* Convert the integer value, assumed to be in SmallInteger range, into a + tagged SmallInteger object. + In C, use a shift and an add to set the tag bit. + In Smalltalk we have to work harder because the simulator works with + strictly positive bit patterns. */ + + /* Spur64BitMemoryManager>>#integerObjectOf: */ +sqInt +integerObjectOf(sqInt value) +{ + return (((((usqInt)value)) << (numTagBits()))) + 1; +} + + +/* Translator produces 'oop >> 3' */ + + /* Spur64BitMemoryManager>>#integerValueOf: */ +sqInt +integerValueOf(sqInt oop) +{ + return (((((usqInt)(oop)) >> 0x3F)) == 1 + ? ((((((-(numTagBits())) < 0) ? (((usqInt)(oop)) >> (-(-(numTagBits())))) : ((((usqInt)(oop) << (-(numTagBits()))))))) & 0x1FFFFFFFFFFFFFFFLL) - 0x1FFFFFFFFFFFFFFFLL) - 1 + : (((-(numTagBits())) < 0) ? (((usqInt)(oop)) >> (-(-(numTagBits())))) : (((sqInt)((usqInt)(oop) << (-(numTagBits()))))))); +} + + /* Spur64BitMemoryManager>>#isFloatInstance: */ +static NoDbgRegParms int +isFloatInstance(sqInt oop) +{ + sqInt tagBits; + + return ((tagBits = oop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(oop))) & (classIndexMask())) == ClassFloatCompactIndex); +} + + /* Spur64BitMemoryManager>>#isImmediateFloat: */ +static NoDbgRegParms int +isImmediateFloat(sqInt oop) +{ + return ((oop & (smallFloatTag())) != 0); +} + + /* Spur64BitMemoryManager>>#isIntegerObject: */ +int +isIntegerObject(sqInt oop) +{ + return ((oop & (smallIntegerTag())) != 0); +} + + +/* Answer if the given value can be represented as a Smalltalk integer value. + In 64-bits we use a 3 bit tag which leaves 61 bits for 2's complement + signed integers. In C, use a shift add and mask to test if the top 4 bits + are all the same. + Since 16rFFFFFFFFFFFFFFFF >> 60 = 16rF the computation intValue >> 60 + 1 + bitAnd: 16rF + maps in-range -ve values to 0 and in-range +ve values to 1. */ + + /* Spur64BitMemoryManager>>#isIntegerValue: */ +int +isIntegerValue(sqInt intValue) +{ + return (((((usqInt)(intValue)) >> 60) + 1) & 15) <= 1; +} + + +/* To have a prev pointer, which follows the next pointer, we need at least + two slots. + */ + + /* Spur64BitMemoryManager>>#isLilliputianSize: */ +static NoDbgRegParms int +isLilliputianSize(sqInt chunkBytes) +{ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + return chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */); +} + + /* Spur64BitMemoryManager>>#isSmallFloatValueBits: */ +static NoDbgRegParms int +isSmallFloatValueBits(usqLong rawFloatBits) +{ + usqInt exponent; + + exponent = ((rawFloatBits) >> (smallFloatMantissaBits())) & 0x7FF; + return (exponent > (smallFloatExponentOffset()) + ? exponent <= (0xFF + (smallFloatExponentOffset())) + : (rawFloatBits & ((1ULL << (smallFloatMantissaBits())) - 1) + ? exponent == (smallFloatExponentOffset()) + : exponent == 0)); +} + + /* Spur64BitMemoryManager>>#isSmallFloatValue: */ +static NoDbgRegParms int +isSmallFloatValue(double aFloat) +{ + usqInt exponent; + usqLong rawFloat; + + memcpy((&rawFloat), (&aFloat), sizeof(rawFloat)); + exponent = ((rawFloat) >> (smallFloatMantissaBits())) & 0x7FF; + return (exponent > (smallFloatExponentOffset()) + ? exponent <= (0xFF + (smallFloatExponentOffset())) + : (rawFloat & ((1ULL << (smallFloatMantissaBits())) - 1) + ? exponent == (smallFloatExponentOffset()) + : exponent == 0)); +} + + +/* Answer if the argument contains only indexable words (no oops). See + comment in formatOf: + */ + + /* Spur64BitMemoryManager>>#isWordsNonImm: */ +static NoDbgRegParms int +isWordsNonImm(sqInt objOop) +{ + return ((((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1))); +} + + +/* Answer the number of indexable units in the given object. + For a CompiledMethod, the size of the method header (in bytes) + should be subtracted from the result of this method. */ + + /* Spur64BitMemoryManager>>#lengthOf:format: */ +static NoDbgRegParms sqInt +lengthOfformat(sqInt objOop, sqInt fmt) +{ + usqInt numSlots; + usqInt numSlotsUsqInt; + + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + +/* If floatOrInt is an integer and we enable mixed arithmetic in primitives, + then convert it to a C double float and return it. + If it is a Float, then load its value and return it. + Otherwise fail -- ie return with primErrorCode non-zero. */ + + /* Spur64BitMemoryManager>>#loadFloatOrIntFrom: */ +static NoDbgRegParms double +loadFloatOrIntFrom(sqInt floatOrIntOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + double result; + sqInt tagBits; + double value; + + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + return value; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + return ((double) ((floatOrIntOop >> 3)) ); + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + return result; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0.0; +} + + +/* Answer the maximum number of slots we are willing to attempt to allocate + in an object. + Must fit in 56-bits; c.f. bytesInObject: */ + + /* Spur64BitMemoryManager>>#maxSlotsForAlloc */ +static sqInt +maxSlotsForAlloc(void) +{ + return 0x10000000000LL; +} + + +/* Answer the C double precision floating point value of the argument, + which *must* be something for which self isFloatInstance: answers true. + Note: May be called by translated primitive code. */ + + /* Spur64BitMemoryManager>>#noFailFloatValueOf: */ +double +noFailFloatValueOf(sqInt aFloatOop) +{ + usqLong bits; + double result; + + assert(isFloatInstance(aFloatOop)); + if (aFloatOop & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(aFloatOop)); + bits = ((((usqInt)aFloatOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&result), (&bits), sizeof(result)); + } + else { + fetchFloatAtinto(aFloatOop + BaseHeaderSize, result); + } + return result; +} + + +/* Answer the number of free lists. We use freeListsMask, a bitmap, to avoid + reading empty list heads. This should fit in a machine word to end up in a + register during free chunk allocation. */ + + /* Spur64BitMemoryManager>>#numFreeLists */ +static sqInt +numFreeLists(void) +{ + return 64; +} + + +/* Object parsing. + 1. all objects have at least a word following the header, for a forwarding + pointer. 2. objects with an overflow size have a preceding word with a + saturated numSlots. If the word + following an object doesn't have a saturated numSlots field it must be a + single-header object. + If the word following does have a saturated numSlots it must be the + overflow size word. + + This variation on objectAfter:limit: allows for a single (64-bit) word + bridge which may be needed + to bridge from an almost full pastSpace to eden. It is only used in the + flat enumerators that use + startAddressForBridgedHeapEnumeration and enumerate over pastSpace, eden + and oldSpace + in that order. Note that the order for allObjects, and allInstances + enumerates over oldSpace first. + + This hack is cheap. It increases the size of the objectAfter code, but + saves two extra copies of + the inner loop, since the inner loop now enumerates over all of pastSpace, + eden and oldSpace. + The test for a slim bridge is only performed if applied to an overflow + header, and typically only + 1 in 400 objects have overflow headers in 32-bits, 1 in 500 in 64-bits. + The complication is that + image segment loading evaporates the word array by setting the overflow + slots to 1, and this + is ambiguous with a slimbridge. The resolution is that if the segmentArray + has an overflow header, + and is in new space, then its slot size can be zeroed and its overflow + header changed to a slimbridge. + + At some point we should allow slimbridges (slivers?) throughout object + memory, and use them to + provide object alignment by slimbridges (slivers?) padding up to the + following (aligned) object. + */ + + /* Spur64BitMemoryManager>>#objectAfterMaybeSlimBridge:limit: */ +static NoDbgRegParms sqInt +objectAfterMaybeSlimBridgelimit(sqInt objOop, sqInt limit) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, limit)) { + return limit; + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); +} + + +/* Answer the C double precision floating point value of the argument, a + SmallFloat. See section 61-bit Immediate Floats in the SpurMemoryManager + class comment. + msb lsb + Decode: [8expsubset][52mantissa][1s][3tags] + shift away tags: [ 000 ][8expsubset][52mantissa][1s] + add exponent offset: [ 11 exponent ][52mantissa][1s] + rot sign: [1s][ 11 exponent ][52mantissa] */ + + /* Spur64BitMemoryManager>>#smallFloatValueOf: */ +static NoDbgRegParms double +smallFloatValueOf(sqInt oop) +{ + usqLong bits; + double value; + + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + return value; +} + + +/* Answer the total number of bytes in an object without an overflow header, + including header bytes. + */ + + /* Spur64BitMemoryManager>>#smallObjectBytesForSlots: */ +usqInt +smallObjectBytesForSlots(sqInt numSlots) +{ + return BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); +} + + /* Spur64BitMemoryManager>>#wordIndexableFormat */ +static sqInt +wordIndexableFormat(void) +{ + return sixtyFourBitIndexableFormat(); +} + + /* Spur64BitMemoryManager>>#wordSizeClassIndexPun */ +static sqInt +wordSizeClassIndexPun(void) +{ + return sixtyFourBitLongsClassIndexPun(); +} + + +/* ephemeronCorpse is the corpse of an ephemeron that was copied and + forwarded. Later on its surviving copy must be scanned to nil weak + references. Thread the corpse onto the weakList. Later, the weakList can + be followed, and + the forwarding pointer followed to locate the survivor. */ +/* Should be too infrequent to lower icache density of copyAndForward: */ + + /* SpurGenerationScavenger>>#addToEphemeronList: */ +static NoDbgRegParms NeverInline void +addToEphemeronList(sqInt ephemeronCorpse) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ephemeronListOffset; + + assert(isYetToBeScavenged(keyOfEphemeron(followForwarded(ephemeronCorpse)))); + ephemeronListOffset = (!(GIV(ephemeronList)) + ? 0 + : GIV(ephemeronList)); + + /* begin setCorpseOffsetOf:to: */ + assert(isYoung(ephemeronCorpse)); + assert(isForwarded(ephemeronCorpse)); + long64Atput((void *)(ephemeronCorpse),headerForSlotshashformatclassIndex(byteAt((void *)(ephemeronCorpse + (numSlotsFieldByteOffset()))), ((usqInt)(ephemeronListOffset)) >> 8, ephemeronListOffset & 0xFF, isForwardedObjectClassIndexPun())); + GIV(ephemeronList) = (((usqInt)((ephemeronCorpse - GIV(newSpaceStart)))) >> 3 /* shiftForAllocationUnit */) + 1; + assert((firstCorpse(GIV(ephemeronList))) == ephemeronCorpse); +} + + +/* weakCorpse is the corpse of a weak array that was copied and forwarded. + Later on its surviving copy must be scanned to nil weak references. + Thread the corpse onto the weakList. Later, the weakList can be followed, + and the forwarding pointer followed to locate the survivor. */ +/* Should be too infrequent to lower icache density of copyAndForward: */ + + /* SpurGenerationScavenger>>#addToWeakList: */ +static NoDbgRegParms NeverInline void +addToWeakList(sqInt weakCorpse) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt weakListOffset; + + weakListOffset = (!(GIV(weakList)) + ? 0 + : GIV(weakList)); + + /* begin setCorpseOffsetOf:to: */ + assert(isYoung(weakCorpse)); + assert(isForwarded(weakCorpse)); + long64Atput((void *)(weakCorpse),headerForSlotshashformatclassIndex(byteAt((void *)(weakCorpse + (numSlotsFieldByteOffset()))), ((usqInt)(weakListOffset)) >> 8, weakListOffset & 0xFF, isForwardedObjectClassIndexPun())); + GIV(weakList) = (((usqInt)((weakCorpse - GIV(newSpaceStart)))) >> 3 /* shiftForAllocationUnit */) + 1; + assert((firstCorpse(GIV(weakList))) == weakCorpse); +} + + /* SpurGenerationScavenger>>#allNewSpaceObjectsHaveZeroRTRefCount */ +static sqInt +allNewSpaceObjectsHaveZeroRTRefCount(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + /* begin allNewSpaceObjectsDo: */ + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l2; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOopSqInt, GIV(freeStart))) { + assert(isBridgeOrEnumerableObjectNoAssert(objOopSqInt)); + if ((((usqInt)((byteAt((void *)(objOopSqInt + (formatFieldByteOffset())))))) >> (rememberedBitByteShift())) > 0) { + return 0; + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOopSqInt = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + /* end allNewSpaceEntitiesDo: */ +l2: + return 1; +} + + /* SpurGenerationScavenger>>#allWeakSurvivorsOnWeakList */ +static sqInt +allWeakSurvivorsOnWeakList(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + usqInt corpse; + sqInt followingWord; + usqInt followingWordAddress; + sqInt format; + sqInt limit; + sqInt listOffset; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + + /* begin allFutureSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + address = (GIV(futureSpace).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + limit = GIV(futureSurvivorStart); + while (oopisLessThan(objOop, limit)) { + /* begin isWeakNonImm: */ + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (format == (weakArrayFormat())) { + /* begin is:onWeaklingList: */ + corpse = ((((usqInt)((GIV(weakList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + while (corpse) { + if (objOop == (followForwarded(corpse))) { + goto l1; + } + + /* begin nextCorpseOrNil: */ + assert(isYoung(corpse)); + listOffset = ((((usqInt)(((long32At((void *)(corpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(corpse + (formatFieldByteOffset())))); + corpse = ((sqInt) ((listOffset + ? ((((usqInt)((listOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0))); + } + return 0; + return 0; +l1:; + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, limit)) { + objOop = limit; + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l2:; + } + return 1; +} + + +/* Some time in every scavenger's life there may come a time when someone + writes code that stresses + the remembered table. One might conclude that if the remembered table is + full, then the right thing + to do is simply to tenure everything, emptying the remembered table. But + in some circumstances this + can be counter-productive, and result in the same situation arising soon + after tenuring everything. + Instead, we can try and selectively prune the remembered table, tenuring + only those objects that + are referenced by many objects in the remembered table. That's what this + algorithm does. It + reference counts young objects referenced from the remembered set, and + then sets a threshold + used to tenure objects oft referenced from the remembered set, thereby + allowing the remembered + set to shrink, while not tenuring everything. + + Once in a network monitoring application in a galaxy not dissimilar from + the one this code inhabits, + a tree of nodes referring to large integers was in precisely this + situation. The nodes were old, and + the integers were in new space. Some of the nodes referred to shared + numbers, some their own + unique numbers. The numbers were updated frequently. Were new space simply + tenured when the + remembered table was full, the remembered table would soon fill up as new + numbers were computed. + Only by selectively pruning the remembered table of nodes that shared + data, was a balance achieved + whereby the remembered table population was kept small, and tenuring rates + were low. */ + + /* SpurGenerationScavenger>>#computeRefCountToShrinkRT */ +static NeverInline void +computeRefCountToShrinkRT(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt count; + sqInt elephant; + sqInt entirePopulation; + int i; + sqInt iSqInt; + sqInt j; + long population[MaxRTRefCount + 1]; + sqInt refCount; + sqInt referent; + sqInt referentSqInt; + sqInt toDoLimit; + + memset(population, 0, (sizeof(long)) * (MaxRTRefCount + 1)); + assert(allNewSpaceObjectsHaveZeroRTRefCount()); + + /* begin referenceCountRememberedReferents: */ + for (iSqInt = 0; iSqInt < GIV(rememberedSetSize); iSqInt += 1) { + elephant = GIV(rememberedSet)[iSqInt]; + if ((!((longAt((void *)(elephant))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(elephant)); + referentSqInt = longAt((void *)((elephant + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + elephant = referentSqInt; + if (((elephant & (tagMask())) != 0)) { + elephant = GIV(nilObj); + } + + /* take care if elephant forwarded to an immediate */ + GIV(rememberedSet)[iSqInt] = elephant; + } + toDoLimit = (numPointerSlotsOf(elephant)) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + referent = longAt((void *)((elephant + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (/* isReallyYoung: */ + ((!(referent & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)), + (oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))))) { + refCount = ((usqInt)((byteAt((void *)(referent + (formatFieldByteOffset())))))) >> (rememberedBitByteShift()); + if (refCount < MaxRTRefCount) { + if (refCount > 0) { + population[refCount] = ((population[refCount]) - 1); + } + refCount += 1; + + /* begin rtRefCountOf:put: */ + assert(isYoungObject(referent)); + byteAtput((void *)(referent + (formatFieldByteOffset())),((byteAt((void *)(referent + (formatFieldByteOffset())))) & (formatMask())) + ((((usqInt)(refCount) << (rememberedBitByteShift()))))); + population[refCount] = ((population[refCount]) + 1); + } + } + } + } + + /* begin setRefCountToShrinkRT: */ + assert((population[0]) == 0); + entirePopulation = 0; + for (j = 1; j <= MaxRTRefCount; j += 1) { + entirePopulation += population[j]; + } + count = 0; + i = MaxRTRefCount + 1; + while ((count < (entirePopulation / 2)) + && (((i -= 1)) >= 0)) { + count += population[i]; + } + GIV(refCountToShrinkRT) = ((i < 0) ? 0 : i); +} + + +/* A special version of copyAndForward: for objects in the mournQueue. If + we're in the good times tenuring regime then copy to futureSpace, + otherwise tenure. + Also, don't repeat any of the ephemeron processing. */ + + /* SpurGenerationScavenger>>#copyAndForwardMourner: */ +static NoDbgRegParms sqInt +copyAndForwardMourner(sqInt mourner) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytesInObj; + sqInt format; + sqInt newLocation; + sqInt newStart; + sqInt objOop; + usqInt startOfSurvivor; + int tenure; + + assert((isInEden(mourner)) + || (isInPastSpace(mourner))); + + /* cog methods should be excluded. */ + bytesInObj = bytesInBody(mourner); + format = (byteAt((void *)(mourner + (formatFieldByteOffset())))) & (formatMask()); + + /* Allow Slang to inline. */ + tenure = !((GIV(tenureCriterion) == TenureByAge) + && (GIV(tenureThreshold) == 0)); + if (tenure + || ((GIV(futureSurvivorStart) + bytesInObj) > ((GIV(futureSpace).limit)))) { + newLocation = copyToOldSpacebytesformat(mourner, bytesInObj, format); + } + else { + /* begin copyToFutureSpace:bytes: */ + /* we hope writes are cheap... */ + GIV(statSurvivorCount) += 1; + assert((GIV(futureSurvivorStart) + bytesInObj) <= ((GIV(futureSpace).limit))); + startOfSurvivor = /* startOfObject: */ + ((byteAt((void *)(mourner + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? mourner - BaseHeaderSize + : mourner); + newStart = GIV(futureSurvivorStart); + GIV(futureSurvivorStart) += bytesInObj; + memcpy(((void *)newStart), ((void *)startOfSurvivor), bytesInObj); + if (GIV(tenureCriterion) == TenureToShrinkRT) { + objOop = newStart + (mourner - startOfSurvivor); + + /* begin rtRefCountOf:put: */ + assert(isYoungObject(objOop)); + byteAtput((void *)(objOop + (formatFieldByteOffset())),((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) + (0U << (rememberedBitByteShift()))); + } + newLocation = newStart + (mourner - startOfSurvivor); + } + + /* begin forwardSurvivor:to: */ + assert(isInNewSpace(mourner)); + assert((isInFutureSpace(newLocation)) + || (isInOldSpace(newLocation))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(mourner)) + && (!(isForwarded(mourner)))); + assert(validStorePointerUncheckedArgs(0, mourner, newLocation)); + longAtput((void *)((mourner + BaseHeaderSize) + (0U << (shiftForWord()))),newLocation); + + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(mourner),((longAt((void *)(mourner))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); + return newLocation; +} + + +/* copyAndForward: survivor copies a survivor object either to + futureSurvivorSpace or, if it is to be promoted, to oldSpace. + It leaves a forwarding pointer behind. If the object is weak + then corpse is threaded onto the weakList for later treatment. */ + + /* SpurGenerationScavenger>>#copyAndForward: */ +static NoDbgRegParms sqInt +copyAndForward(sqInt survivor) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytesInObj; + sqInt format; + sqInt newLocation; + sqInt newStart; + sqInt objOop; + usqInt startOfSurvivor; + + assert((isInEden(survivor)) + || (isInPastSpace(survivor))); + + /* cog methods should be excluded. */ + bytesInObj = bytesInBody(survivor); + format = (byteAt((void *)(survivor + (formatFieldByteOffset())))) & (formatMask()); + if (((GIV(futureSurvivorStart) + bytesInObj) > ((GIV(futureSpace).limit))) + || (/* shouldBeTenured: */ + (GIV(tenureCriterion) == TenureByAge + ? oopisLessThan(survivor, GIV(tenureThreshold)) + : (GIV(tenureCriterion) == TenureToShrinkRT + ? (((usqInt)((byteAt((void *)(survivor + (formatFieldByteOffset())))))) >> (rememberedBitByteShift())) >= GIV(refCountToShrinkRT) + : (GIV(tenureCriterion) == TenureByClass + ? ((longAt((void *)(survivor))) & (classIndexMask())) == GIV(tenuringClassIndex) + : 0))))) { + newLocation = copyToOldSpacebytesformat(survivor, bytesInObj, format); + } + else { + /* begin copyToFutureSpace:bytes: */ + /* we hope writes are cheap... */ + GIV(statSurvivorCount) += 1; + assert((GIV(futureSurvivorStart) + bytesInObj) <= ((GIV(futureSpace).limit))); + startOfSurvivor = /* startOfObject: */ + ((byteAt((void *)(survivor + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? survivor - BaseHeaderSize + : survivor); + newStart = GIV(futureSurvivorStart); + GIV(futureSurvivorStart) += bytesInObj; + memcpy(((void *)newStart), ((void *)startOfSurvivor), bytesInObj); + if (GIV(tenureCriterion) == TenureToShrinkRT) { + objOop = newStart + (survivor - startOfSurvivor); + + /* begin rtRefCountOf:put: */ + assert(isYoungObject(objOop)); + byteAtput((void *)(objOop + (formatFieldByteOffset())),((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) + (0U << (rememberedBitByteShift()))); + } + newLocation = newStart + (survivor - startOfSurvivor); + } + + /* begin forwardSurvivor:to: */ + assert(isInNewSpace(survivor)); + assert((isInFutureSpace(newLocation)) + || (isInOldSpace(newLocation))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(survivor)) + && (!(isForwarded(survivor)))); + assert(validStorePointerUncheckedArgs(0, survivor, newLocation)); + longAtput((void *)((survivor + BaseHeaderSize) + (0U << (shiftForWord()))),newLocation); + + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(survivor),((longAt((void *)(survivor))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); + + /* if weak or ephemeron add to the relevant list for subsequent scanning. */ + if (((format >= (weakArrayFormat())) && (format <= (ephemeronFormat())))) { + if (format == (weakArrayFormat())) { + addToWeakList(survivor); + } + else { + assert(isEphemeronFormat(format)); + if (isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(newLocation)) + && (isObjEphemeron(newLocation))), + /* fetchPointer:ofObject: */ + longAt((void *)((newLocation + BaseHeaderSize) + (0U << (shiftForWord()))))))) { + addToEphemeronList(survivor); + } + } + } + return newLocation; +} + + +/* Copy survivor to oldSpace. Answer the new oop of the object. */ +/* Should be too infrequent to lower icache density of copyAndForward: */ + + /* SpurGenerationScavenger>>#copyToOldSpace:bytes:format: */ +static NoDbgRegParms NeverInline sqInt +copyToOldSpacebytesformat(sqInt survivor, sqInt bytesInObject, sqInt formatOfSurvivor) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt field; + sqInt newOop; + sqInt newStart; + sqInt nTenures; + sqInt p; + usqInt startOfSurvivor; + sqInt toDoLimit; + + assert((formatOfSurvivor == (formatOf(survivor))) + && (((!(isMarked(survivor))) + || (GIV(tenureCriterion) == MarkOnTenure)) + && ((GIV(tenureCriterion) == TenureToShrinkRT) + || ((!(isPinned(survivor))) + && (!(isRemembered(survivor))))))); + nTenures = GIV(statTenures); + startOfSurvivor = /* startOfObject: */ + ((byteAt((void *)(survivor + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? survivor - BaseHeaderSize + : survivor); + newStart = allocateOldSpaceChunkOfBytes(bytesInObject); + if (!newStart) { + growOldSpaceByAtLeast(0); + newStart = allocateOldSpaceChunkOfBytes(bytesInObject); + if (!newStart) { + error("out of memory"); + } + } + + /* manager checkFreeSpace. */ + memcpy(((void *)newStart), ((void *)startOfSurvivor), bytesInObject); + newOop = newStart + (survivor - startOfSurvivor); + if (GIV(tenureCriterion) >= (((TenureToShrinkRT < MarkOnTenure) ? TenureToShrinkRT : MarkOnTenure))) { + if (GIV(tenureCriterion) == TenureToShrinkRT) { + /* begin rtRefCountOf:put: */ + assert(isYoungObject(newOop)); + byteAtput((void *)(newOop + (formatFieldByteOffset())),((byteAt((void *)(newOop + (formatFieldByteOffset())))) & (formatMask())) + (0U << (rememberedBitByteShift()))); + } + if (GIV(tenureCriterion) == MarkOnTenure) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(newOop))); + byteAtput((void *)(newOop + (markBitsByteOffset())),(byteAt((void *)(newOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + } + } + GIV(statTenures) = nTenures + 1; + if (/* isAnyPointerFormat: */ + (formatOfSurvivor <= 5 /* lastPointerFormat */) + || (formatOfSurvivor >= (firstCompiledMethodFormat()))) { + /* A very quick and dirty scan to find young referents. If we misidentify bytes + in a CompiledMethod as young we don't care; it's unlikely, and a subsequent + scan of the rt will filter the object out. But it's good to filter here because + otherwise an attempt to shrink the RT may simply fill it up with new objects, + and here the data is likely in the cache. */ + toDoLimit = (bytesInObject - (survivor - startOfSurvivor)) - BytesPerWord; + for (p = BaseHeaderSize; p <= toDoLimit; p += BytesPerWord) { + field = longAt((void *)(survivor + p)); + if (/* isReallyYoung: */ + ((!(field & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(field)), + (oopisLessThan(field, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(field, GIV(newSpaceStart)))))) { + remember(newOop); + return newOop; + } + } + } + return newOop; +} + + +/* Use the identityHash and format-containing byte to construct a 30 bit + offset through non-future newSpace and use this to implement lists for + weak array + and ephemeron processing. 30 bits of 8 byte allocationUnits units is 2 ^ + 33 bytes, or 8Gb, big enough for newSpace for a good few years yet. */ +/* debugging */ + + /* SpurGenerationScavenger>>#corpseForCorpseOffset: */ +usqInt +corpseForCorpseOffset(sqInt corpseOffset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((((usqInt)((corpseOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); +} + + /* SpurGenerationScavenger>>#firstCorpse: */ +static NoDbgRegParms usqInt +firstCorpse(sqInt headOfCorpseList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((((usqInt)((headOfCorpseList - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); +} + + /* SpurGenerationScavenger>>#growRememberedSet */ +static void +growRememberedSet(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt *base; + sqInt fudge; + sqInt i; + sqInt newObj; + sqInt nSlots; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt obj; + usqInt p; + usqInt toDoLimit; + + + /* Don't ruin locality in remember: */ + obj = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))); + + /* begin numSlotsOf: */ + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + assert(numSlots >= 0x400); + nSlots = numSlots * 2; + + /* begin allocatePinnedSlots: */ + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(nSlots, /* objectBytesForSlots: */ + (nSlots + ? ((((usqInt)(nSlots) << (shiftForWord())))) + ((nSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (newObj) { + assert(isPinned(newObj)); + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + if (!newObj) { + nSlots = numSlots + 0x400; + + /* begin allocatePinnedSlots: */ + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(nSlots, /* objectBytesForSlots: */ + (nSlots + ? ((((usqInt)(nSlots) << (shiftForWord())))) + ((nSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (newObj) { + assert(isPinned(newObj)); + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + if (!newObj) { + if (!(growOldSpaceByAtLeast((numSlots + 0x400) * BytesPerOop))) { + error("could not grow remembered set"); + } + nSlots = numSlots + 0x400; + + /* begin allocatePinnedSlots: */ + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(nSlots, /* objectBytesForSlots: */ + (nSlots + ? ((((usqInt)(nSlots) << (shiftForWord())))) + ((nSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (newObj) { + assert(isPinned(newObj)); + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + } + } + + /* begin rememberedSetObj: */ + assert(isOldObject(newObj)); + assert((isNonImmediate(GIV(hiddenRootsObj))) + && (!(isForwarded(GIV(hiddenRootsObj))))); + assert(validStorePointerUncheckedArgs(RememberedSetRootIndex, GIV(hiddenRootsObj), newObj)); + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord()))))),newObj); + base = firstIndexableField(newObj); + for (i = 0; i < GIV(rememberedSetSize); i += 1) { + base[i] = (GIV(rememberedSet)[i]); + } + + /* if growing in the middle of a GC, need to preserve marked status. */ + if ((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(newObj))); + byteAtput((void *)(newObj + (markBitsByteOffset())),(byteAt((void *)(newObj + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(obj))); + byteAtput((void *)(obj + (markBitsByteOffset())),(byteAt((void *)(obj + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + freeObject(obj); + GIV(rememberedSet) = base; + + /* begin numSlotsOf: */ + assert((classIndexOf(newObj)) > (isForwardedObjectClassIndexPun())); + GIV(rememberedSetLimit) = (((numSlotsUsqInt = byteAt((void *)(newObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(newObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + + /* begin setRememberedSetRedZone */ + fudge = ((((GIV(eden).limit)) - ((GIV(eden).start))) / BytesPerWord) / 0x400; + GIV(rememberedSetRedZone) = ((((GIV(rememberedSetLimit) * 3) / 4) < fudge) ? fudge : ((GIV(rememberedSetLimit) * 3) / 4)); +} + + /* SpurGenerationScavenger>>#isInRememberedSet: */ +static NoDbgRegParms sqInt +isInRememberedSet(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + for (i = 0; i < GIV(rememberedSetSize); i += 1) { + if ((GIV(rememberedSet)[i]) == objOop) { + return 1; + } + } + return 0; +} + + +/* Answer whether the oop has survived a scavenge. This version is + for processing weak survivors and must cope with the scavenge in + freeUnmarkedObjectsAndSortAndCoalesceFreeSpaceForPigCompact. */ + + /* SpurGenerationScavenger>>#isMaybeOldScavengeSurvivor: */ +static NoDbgRegParms sqInt +isMaybeOldScavengeSurvivor(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt target; + + if (((oop & (tagMask())) != 0)) { + return 1; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + target = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(target & (tagMask())))) + && ((!((longAt((void *)(target))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + target = longAt((void *)((target + BaseHeaderSize) + (0U << (shiftForWord())))); + } + if (((oop & (tagMask())) != 0)) { + return 1; + } + } + else { + target = oop; + } + return (oopisGreaterThanOrEqualTo(target, GIV(oldSpaceStart)) + ? (GIV(tenureCriterion) != MarkOnTenure) + || (((byteAt((void *)(target + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + : oopisGreaterThanOrEqualToandLessThan(target, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))); +} + + +/* Answer whether the oop has survived a scavenge. This is equivalent to + | target | + (manager isImmediate: oop) ifTrue: + [^true]. + target := (manager isForwarded: oop) + ifTrue: [manager followForwarded: oop] + ifFalse: [oop]. + ^((manager isInEden: target) + or: [(manager isInPastSpace: target)]) not */ + + /* SpurGenerationScavenger>>#isScavengeSurvivor: */ +static NoDbgRegParms sqInt +isScavengeSurvivor(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt target; + + if (((oop & (tagMask())) != 0)) { + return 1; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + target = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(target & (tagMask())))) + && ((!((longAt((void *)(target))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + target = longAt((void *)((target + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + else { + target = oop; + } + return (!(/* isReallyYoung: */ + ((!(target & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(target)), + (oopisLessThan(target, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(target, GIV(newSpaceStart))))))) + || (oopisGreaterThanOrEqualToandLessThan(target, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))); +} + + +/* Answer whether the oop is in eden or pastSpace and yet-to-be scavenged. */ + + /* SpurGenerationScavenger>>#isYetToBeScavenged: */ +static NoDbgRegParms sqInt +isYetToBeScavenged(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt target; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + target = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(target & (tagMask())))) + && ((!((longAt((void *)(target))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + target = longAt((void *)((target + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + else { + target = oop; + } + return (/* isReallyYoung: */ + ((!(target & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(target)), + (oopisLessThan(target, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(target, GIV(newSpaceStart)))))) + && (!(oopisGreaterThanOrEqualToandLessThan(target, ((GIV(futureSpace)).start), GIV(futureSurvivorStart)))); +} + + /* SpurGenerationScavenger>>#newSpaceCapacity */ +static usqInt +newSpaceCapacity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((GIV(eden).limit)) - (((((GIV(futureSpace).start)) < ((GIV(pastSpace).start))) ? ((GIV(futureSpace).start)) : ((GIV(pastSpace).start)))); +} + + +/* corpse is the corpse of a weak array that has been added to the weakList. + Answer the next object on the list, or nil if none. */ + + /* SpurGenerationScavenger>>#nextCorpseOrNil: */ +static NoDbgRegParms sqInt +nextCorpseOrNil(sqInt corpse) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt listOffset; + + assert(isYoung(corpse)); + listOffset = ((((usqInt)(((long32At((void *)(corpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(corpse + (formatFieldByteOffset())))); + return (listOffset + ? ((((usqInt)((listOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0); +} + + /* SpurGenerationScavenger>>#openScavengeLog */ +void +openScavengeLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(scavengeLog) = fopen("scavenge.log", "a+"); +} + + +/* Print the objects on the ephemeronList, indicating active or inactive. */ + + /* SpurGenerationScavenger>>#printEphemeronList */ +void +printEphemeronList(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt corpse; + sqInt ephemeron; + + corpse = ((((usqInt)((GIV(ephemeronList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + if (!corpse) { + print("empty"); + cr(); + return; + } + while (corpse) { + ephemeron = followForwarded(corpse); + fprintf(GIV(transcript), + "%x -> %p %s\n", + ((int) corpse), + ((void *)ephemeron), + (isYetToBeScavenged(keyOfMaybeFiredEphemeron(ephemeron)) + ? "active" + : "inactive")); + corpse = nextCorpseOrNil(corpse); + } +} + + +/* Print the objects in the remembered set. */ +/* useful for debugging */ + + /* SpurGenerationScavenger>>#printRememberedSet */ +void +printRememberedSet(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + for (i = 0; i < GIV(rememberedSetSize); i += 1) { + printNum(i); + + /* begin space */ + printChar(' '); + shortPrintOop(GIV(rememberedSet)[i]); + } +} + + +/* There are ephemerons to be scavenged, and maybe some to be fired. We can't + fire any + ephemerons until we have reached the fixed point of scavenging all + inactive ephemerons, + and removing them from the remembered set and/or the list. Once at that + fixed point the + rememberedTable has only unfired ephemerons in its numRememberedEphemerons + partition, and the ephemeronList is either empty or contains only firable + ephemerons. At this fixed point + fire the remaining ephemerons and then scavenge the keys of the ephemerons + in the + remembered set and the contents of the ephemeronList. Answer if any were + fired. + Since more that one ephemeron can refer to the same key, and firability + for a key is + determined by that key not (yet) having survived the scavenge, we must + first fire + ephemerons before scavenging their keys, since the first ephemeron that + scavenges its key will make all other ephemerons on that key appear to be + unfirable. + Read the class comment for a more in-depth description of the algorithm. */ + + /* SpurGenerationScavenger>>#processEphemerons */ +static sqInt +processEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt corpseOffset; + sqInt ephemeron; + usqInt ephemeronCorpse; + sqInt firedIRS; + sqInt firedOEL; + sqInt foundNewReferent; + sqInt i; + sqInt key; + sqInt listOffset; + usqInt numSlots; + sqInt offsetToNextCorpse; + sqInt referent; + sqInt referentSqInt; + sqInt scavengedUnfirable; + sqInt toDoLimit; + + scavengedUnfirable = 0; + if (scavengeInactiveEphemeronsInRememberedSet()) { + scavengedUnfirable = 1; + } + assert(validRememberedSetPartitionPostSIEIRS()); + if ((scavengeInactiveEphemeronsOnEphemeronList()) + || (scavengedUnfirable)) { + return 0; + } + assert(wholeEphemeronListIsFirable()); + + /* begin fireEphemeronsInRememberedSet */ + assert(!((unfiredEphemeronsAtEndOfRememberedSet()))); + if (!GIV(numRememberedEphemerons)) { + firedIRS = 0; + goto l1; + } + i = 0; + while (i < GIV(numRememberedEphemerons)) { + ephemeron = GIV(rememberedSet)[i]; + assert(isEphemeron(ephemeron)); + + /* begin keyOfEphemeron: */ + assert((isNonImmediate(ephemeron)) + && (isObjEphemeron(ephemeron))); + key = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isYetToBeScavenged(key)); + fireEphemeron(ephemeron); + i += 1; + } + + /* If any ephemerons were fired then processEphemerons is expected to + scavenge them in scavengeUnfiredEphemeronsInRememberedSet */ + firedIRS = 1; + /* end fireEphemeronsInRememberedSet */ +l1: + + /* begin fireEphemeronsOnEphemeronList */ + if (!GIV(ephemeronList)) { + firedOEL = 0; + goto l2; + } + ephemeronCorpse = ((((usqInt)((GIV(ephemeronList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + while (ephemeronCorpse) { + assert((isYoung(ephemeronCorpse)) + && (isForwarded(ephemeronCorpse))); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(ephemeronCorpse)); + ephemeron = longAt((void *)((ephemeronCorpse + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(ephemeron & (tagMask())))) + && ((!((longAt((void *)(ephemeron))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ephemeron = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + } + + /* begin keyOfMaybeFiredEphemeron: */ + assert(isMaybeFiredEphemeron(ephemeron)); + key = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isYetToBeScavenged(key)); + fireEphemeron(ephemeron); + + /* begin nextCorpseOrNil: */ + assert(isYoung(ephemeronCorpse)); + listOffset = ((((usqInt)(((long32At((void *)(ephemeronCorpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(ephemeronCorpse + (formatFieldByteOffset())))); + ephemeronCorpse = ((sqInt) ((listOffset + ? ((((usqInt)((listOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0))); + } + firedOEL = 1; + /* end fireEphemeronsOnEphemeronList */ +l2: + if (firedIRS + || (GIV(numRememberedEphemerons) > 0)) { + /* begin scavengeEphemeronsInRememberedSet */ + assert(!((unfiredEphemeronsAtEndOfRememberedSet()))); + i = 0; + while (i < GIV(numRememberedEphemerons)) { + ephemeron = GIV(rememberedSet)[i]; + assert((isEphemeron(ephemeron)) + || (isonObjStack(ephemeron, GIV(mournQueue)))); + if (scavengeReferentsOfPointers(ephemeron)) { + i += 1; + } + else { + setIsRememberedOfto(ephemeron, 0); + + /* remove from set by overwriting with next-to-be scanned */ + GIV(numRememberedEphemerons) -= 1; + GIV(previousRememberedSetSize) -= 1; + GIV(rememberedSetSize) -= 1; + + /* First overwrite with last firable ephemeron (could be a noop if this is the last one). + Then overwrite last firable entry with next unscanned rememberedSet entry (could also be a noop). + Then overwrite next unscanned entry with last unscanned rememberedSet entry (could also be a noop). */ + GIV(rememberedSet)[i] = (GIV(rememberedSet)[GIV(numRememberedEphemerons)]); + GIV(rememberedSet)[GIV(numRememberedEphemerons)] = (GIV(rememberedSet)[GIV(previousRememberedSetSize)]); + GIV(rememberedSet)[GIV(previousRememberedSetSize)] = (GIV(rememberedSet)[GIV(rememberedSetSize)]); + } + } + + /* no more firable ephemerons in this cycle. + scavengeRememberedSetStartingAt: may find new ones. */ + GIV(numRememberedEphemerons) = 0; + } + + /* begin scavengeEphemeronsOnEphemeronList */ + if (!((corpseOffset = GIV(ephemeronList)))) { + goto l3; + } + GIV(ephemeronList) = null; + while (corpseOffset != 0) { + ephemeronCorpse = ((((usqInt)((corpseOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + assert(isForwarded(ephemeronCorpse)); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(ephemeronCorpse)); + ephemeron = longAt((void *)((ephemeronCorpse + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(ephemeron & (tagMask())))) + && ((!((longAt((void *)(ephemeron))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ephemeron = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + } + offsetToNextCorpse = ((((usqInt)(((long32At((void *)(ephemeronCorpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(ephemeronCorpse + (formatFieldByteOffset())))); + + /* begin scavengeReferentsOfPointers: */ + foundNewReferent = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(ephemeron)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(ephemeron + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(ephemeron - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((ephemeron + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((ephemeron + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + corpseOffset = offsetToNextCorpse; + } + /* end scavengeEphemeronsOnEphemeronList */ +l3: + assert(GIV(numRememberedEphemerons) == 0); + assert(!GIV(ephemeronList)); + return firedIRS + || (firedOEL); +} + + +/* Go through the remembered set and the weak list, nilling references to + any objects that didn't survive the scavenge. Read the class comment + for a more in-depth description of the algorithm. */ + + /* SpurGenerationScavenger>>#processWeaklings */ +static void +processWeaklings(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt i; + sqInt listOffset; + sqInt rootObj; + usqInt weakCorpse; + sqInt weakObj; + + assert(allWeakSurvivorsOnWeakList()); + i = 0; + while (i < GIV(rememberedSetSize)) { + rootObj = GIV(rememberedSet)[i]; + + /* begin isWeakNonImm: */ + format = (byteAt((void *)(rootObj + (formatFieldByteOffset())))) & (formatMask()); + if (format == (weakArrayFormat())) { + /* If no more referents, remove by overwriting with the last element in the set. */ + if (processWeakSurvivor(rootObj)) { + i += 1; + } + else { + setIsRememberedOfto(rootObj, 0); + if ((i + 1) < GIV(rememberedSetSize)) { + GIV(rememberedSet)[i] = (GIV(rememberedSet)[GIV(rememberedSetSize) - 1]); + } + GIV(rememberedSetSize) -= 1; + } + } + else { + i += 1; + } + } + if (GIV(weakList)) { + weakCorpse = ((((usqInt)((GIV(weakList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + while (weakCorpse) { + assert(isForwarded(weakCorpse)); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(weakCorpse)); + weakObj = longAt((void *)((weakCorpse + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(weakObj & (tagMask())))) + && ((!((longAt((void *)(weakObj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + weakObj = longAt((void *)((weakObj + BaseHeaderSize) + (0U << (shiftForWord())))); + } + + /* weakObj may have been tenured... */ + if ((processWeakSurvivor(weakObj)) + && (((/* begin isOldObject: */ + assert(isNonImmediate(weakObj)), + oopisGreaterThanOrEqualTo(weakObj, GIV(oldSpaceStart)))) + && (!(((byteAt((void *)(weakObj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0)))) { + remember(weakObj); + } + + /* begin nextCorpseOrNil: */ + assert(isYoung(weakCorpse)); + listOffset = ((((usqInt)(((long32At((void *)(weakCorpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(weakCorpse + (formatFieldByteOffset())))); + weakCorpse = ((sqInt) ((listOffset + ? ((((usqInt)((listOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0))); + } + GIV(weakList) = null; + } +} + + +/* Process a weak survivor on the weakList. Those of its fields + which have not survived the scavenge should be nilled, and if any + are, the coInterpreter should be informed via fireFinalization:. + Answer if the weakObj has any young referents. */ + + /* SpurGenerationScavenger>>#processWeakSurvivor: */ +static NoDbgRegParms sqInt +processWeakSurvivor(sqInt weakObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classPointer; + sqInt hasYoungReferents; + sqInt i; + usqInt numSlots; + sqInt numStrongSlots; + sqInt referent; + sqInt referentSqInt; + sqInt toDoLimit; + sqInt weakObjShouldMourn; + + weakObjShouldMourn = (hasYoungReferents = 0); + + /* N.B. generateToByDoLimitExpression:negative:on: guards against (unsigned)0 - 1 going +ve */ + + /* begin numFixedSlotsOf: */ + classPointer = fetchClassOfNonImm(weakObj); + classFormat = ((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + numStrongSlots = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + for (i = 0; i < numStrongSlots; i += 1) { + referent = longAt((void *)((weakObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(referent & (tagMask())))) + && ((/* begin isYoungObject: */ + assert(isNonImmediate(referent)), + oopisLessThan(referent, GIV(oldSpaceStart))))) { + hasYoungReferents = 1; + } + } + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(weakObj)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(weakObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(weakObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = numStrongSlots; i <= toDoLimit; i += 1) { + referent = longAt((void *)((weakObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* Referent could be forwarded due to scavenging or a become:, don't assume. */ + if ((!(referent & (tagMask())))) { + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + + /* weakObj is either young or already in remembered table; no need to check */ + assert((isReallyYoungObject(weakObj)) + || (isRemembered(weakObj))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(weakObj)) + && (!(isForwarded(weakObj)))); + assert(validStorePointerUncheckedArgs(i, weakObj, referent)); + longAtput((void *)((weakObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + if (isMaybeOldScavengeSurvivor(referent)) { + /* begin isYoungObject: */ + assert(isNonImmediate(referent)); + if (oopisLessThan(referent, GIV(oldSpaceStart))) { + hasYoungReferents = 1; + } + } + else { + weakObjShouldMourn = 1; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(weakObj)) + && (!(isForwarded(weakObj)))); + assert(validStorePointerUncheckedArgs(i, weakObj, GIV(nilObj))); + longAtput((void *)((weakObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } + } + } + if (weakObjShouldMourn) { + /* begin fireFinalization: */ + if (((imageHeaderFlags & 64) != 0)) { + queueMourner(weakObj); + } + + /* begin signalFinalization: */ + forceInterruptCheck(); + GIV(pendingFinalizationSignals) += 1; + } + return hasYoungReferents; +} + + +/* Add the argument to the remembered set and set its isRemembered bit to + true. Answer the argument for the benefit of the Cogit. */ + + /* SpurGenerationScavenger>>#remember: */ +sqInt +remember(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(isNonImmediate(objOop)); + assert(!((isYoungObject(objOop)))); + assert(!((isRemembered(objOop)))); + assert(!((isInRememberedSet(objOop)))); + setIsRememberedOfto(objOop, 1); + if (GIV(rememberedSetSize) >= GIV(rememberedSetLimit)) { + growRememberedSet(); + } + GIV(rememberedSet)[GIV(rememberedSetSize)] = objOop; + if (((GIV(rememberedSetSize) += 1)) >= GIV(rememberedSetRedZone)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + return objOop; +} + + +/* scavengeFutureSurvivorSpaceStartingAt: does a depth-first traversal of the + new objects starting at the one at initialAddress in futureSurvivorSpace. */ + + /* SpurGenerationScavenger>>#scavengeFutureSurvivorSpaceStartingAt: */ +static NoDbgRegParms void +scavengeFutureSurvivorSpaceStartingAt(sqInt initialAddress) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + sqInt obj; + usqInt ptr; + + ptr = initialAddress; + while (ptr < GIV(futureSurvivorStart)) { + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(ptr + (numSlotsFieldByteOffset()))); + obj = (numSlots == (numSlotsMask()) + ? ptr + BaseHeaderSize + : ptr); + ptr = addressAfter(obj); + ((void) (scavengeReferentsOf(obj))); + } +} + + +/* There are ephemerons in the remembered set. Scavenge all with + scavenged/old keys, + leaving only firable/triggerable ephemerons. Answer if any + unfirable/inactive ephemerons + were scavenged. Answering false implies there are only firable ephemerons + (if any) within + the 0 to numRememberedEphemerons - 1 range. + Continue scavenging until at the fixed point of only having firable + ephemerons with still-to-be-scavenged keys in the numRememberedEphemerons + partition. */ + + /* SpurGenerationScavenger>>#scavengeInactiveEphemeronsInRememberedSet */ +static sqInt +scavengeInactiveEphemeronsInRememberedSet(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ephemeron; + sqInt i; + sqInt key; + sqInt numRememberedAtStartOfScan; + sqInt scavengedInactive; + + scavengedInactive = 0; + do { + numRememberedAtStartOfScan = GIV(numRememberedEphemerons); + i = 0; + while (i < GIV(numRememberedEphemerons)) { + ephemeron = GIV(rememberedSet)[i]; + assert((isEphemeron(ephemeron)) + || (isonObjStack(ephemeron, GIV(mournQueue)))); + + /* begin keyOfEphemeron: */ + assert((isNonImmediate(ephemeron)) + && (isObjEphemeron(ephemeron))); + key = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + if (isYetToBeScavenged(key)) { + i += 1; + } + else { + scavengedInactive = 1; + if (scavengeReferentsOfPointers(ephemeron)) { + if (i < (GIV(numRememberedEphemerons) - 1)) { + GIV(rememberedSet)[i] = (GIV(rememberedSet)[GIV(numRememberedEphemerons) - 1]); + GIV(rememberedSet)[GIV(numRememberedEphemerons) - 1] = ephemeron; + } + GIV(numRememberedEphemerons) -= 1; + } + else { + GIV(numRememberedEphemerons) -= 1; + GIV(previousRememberedSetSize) -= 1; + GIV(rememberedSetSize) -= 1; + + /* First overwrite with last firable ephemeron (could be a noop if this is the last one). + Then overwrite last firable entry with next unscanned rememberedSet entry (could also be a noop). + Then overwrite next unscanned entry with last unscanned rememberedSet entry (could also be a noop). */ + GIV(rememberedSet)[i] = (GIV(rememberedSet)[GIV(numRememberedEphemerons)]); + GIV(rememberedSet)[GIV(numRememberedEphemerons)] = (GIV(rememberedSet)[GIV(previousRememberedSetSize)]); + GIV(rememberedSet)[GIV(previousRememberedSetSize)] = (GIV(rememberedSet)[GIV(rememberedSetSize)]); + } + } + } + } while(!(GIV(numRememberedEphemerons) == numRememberedAtStartOfScan)); + + /* rememberedSet contains numRememberedEphemerons firable ephemerons in the rememberedSet. + These will be fired in processEphemerons fireEphemeronsInRememberedSet */ + return scavengedInactive; +} + + +/* Scavenge all untriggerable ephemerons on the ephemeronList and remove them + there-from. Answer if any were scavenged. */ + + /* SpurGenerationScavenger>>#scavengeInactiveEphemeronsOnEphemeronList */ +static sqInt +scavengeInactiveEphemeronsOnEphemeronList(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anyScavenged; + sqInt ephemeron; + usqInt ephemeronCorpse; + sqInt foundNewReferent; + sqInt i; + sqInt key; + sqInt nextCorpseOffset; + usqInt numSlots; + usqInt previousCorpse; + sqInt referent; + sqInt referentSqInt; + sqInt toDoLimit; + + if (!GIV(ephemeronList)) { + return 0; + } + anyScavenged = 0; + previousCorpse = null; + + /* In figuring out the termination condition I believe it is sufficient to consider four cases: + ephemeronList starts with + two firable corpses + two unfirable corpses + a firable corpse followed by an unfirable corpse + an unfirable corpse followed by a firable corpse */ + ephemeronCorpse = ((((usqInt)((GIV(ephemeronList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + while (ephemeronCorpse) { + assert((isYoung(ephemeronCorpse)) + && (isForwarded(ephemeronCorpse))); + nextCorpseOffset = ((((usqInt)(((long32At((void *)(ephemeronCorpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(ephemeronCorpse + (formatFieldByteOffset())))); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(ephemeronCorpse)); + ephemeron = longAt((void *)((ephemeronCorpse + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(ephemeron & (tagMask())))) + && ((!((longAt((void *)(ephemeron))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ephemeron = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + } + + /* begin keyOfMaybeFiredEphemeron: */ + assert(isMaybeFiredEphemeron(ephemeron)); + key = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + if (isYetToBeScavenged(key)) { + previousCorpse = ephemeronCorpse; + } + else { + anyScavenged = 1; + + /* begin scavengeReferentsOfPointers: */ + foundNewReferent = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(ephemeron)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(ephemeron + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(ephemeron - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((ephemeron + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((ephemeron + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + if (previousCorpse) { + /* begin setCorpseOffsetOf:to: */ + assert(isYoung(previousCorpse)); + assert(isForwarded(previousCorpse)); + long64Atput((void *)(previousCorpse),headerForSlotshashformatclassIndex(byteAt((void *)(previousCorpse + (numSlotsFieldByteOffset()))), ((usqInt)(nextCorpseOffset)) >> 8, nextCorpseOffset & 0xFF, isForwardedObjectClassIndexPun())); + } + else { + GIV(ephemeronList) = (nextCorpseOffset + ? nextCorpseOffset + : 0); + } + } + ephemeronCorpse = (nextCorpseOffset + ? ((((usqInt)((nextCorpseOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0); + } + if (previousCorpse) { + /* begin setCorpseOffsetOf:to: */ + assert(isYoung(previousCorpse)); + assert(isForwarded(previousCorpse)); + long64Atput((void *)(previousCorpse),headerForSlotshashformatclassIndex(byteAt((void *)(previousCorpse + (numSlotsFieldByteOffset()))), 0, 0, isForwardedObjectClassIndexPun())); + } + return anyScavenged; +} + + +/* This is the inner loop of the main routine, scavenge. It first scavenges + the new objects immediately + reachable from old ones and the stack. Then it scavenges those that are + transitively reachable. If any + results in a tenure, the tenuree gets remembered, and it first scavenges + objects adjacent to the tenure, + then scavenges the ones reachable from the tenuree. This loop continues + until no more reachable + objects are left. At that point, pastSurvivorSpace is exchanged with + futureSurvivorSpace. + Notice that each pointer in a live object is inspected once and only once. + The previousRememberedSetSize + and previousFutureSurvivorSpaceLimit variables ensure that no object is + scanned twice, as well as + detecting closure. If this were not true, some pointers might get + forwarded twice. + + An extension of the algorithm presented in David's original paper is to + handle weak arrays and ephemerons. + Weak arrays should not have their weak referents scavenged unless there + are strong references to them. + Ephemerons should fire if their key is not reachable other than from + ephemerons and weak arrays. + Handle this by maintaining a list for weak arrays and a list for + ephemerons, which allow scavenging these + objects once all other objects in new space have been scavenged, hence + allowing the scavenger to + detect which referents in new space of weak arrays are dead and of + ephemeron keys are only live due to + ephemerons. Read the class comment for a more in-depth description of the + algorithm. + Answer how many passes occurred. This is to use the value of p[ass which + is valuable for debugging. */ +/* debugging */ + + /* SpurGenerationScavenger>>#scavengeLoop */ +static sqInt +scavengeLoop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firedEphemerons; + sqInt firstTime; + sqInt i; + sqInt mournQueueSize; + sqInt newSize; + sqInt pass; + sqInt previousFutureSurvivorStart; + StackPage *thePage; + + mournQueueSize = 0; + assert(GIV(futureSurvivorStart) == ((GIV(futureSpace).start))); + GIV(weakList) = (GIV(ephemeronList) = null); + GIV(numRememberedEphemerons) = 0; + firstTime = 1; + GIV(previousRememberedSetSize) = 0; + previousFutureSurvivorStart = GIV(futureSurvivorStart); + firedEphemerons = 0; + pass = 0; + + /* begin initStackPageGC */ + if (GIV(stackPage)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + (thePage->trace = StackPageUnreached); + } + while (1) { + pass += 1; + scavengeRememberedSetStartingAt(GIV(previousRememberedSetSize)); + GIV(previousRememberedSetSize) = GIV(rememberedSetSize); + if (firstTime) { + mapInterpreterOops(); + mapMournQueue(); + mapExtraRoots(); + mournQueueSize = sizeOfObjStack(GIV(mournQueue)); + firstTime = 0; + } + + /* if nothing more copied and forwarded (or remembered by mapInterpreterOops et al) + to scavenge, and no ephemerons to process, the scavenge is done. */ + if ((GIV(previousRememberedSetSize) == GIV(rememberedSetSize)) + && ((previousFutureSurvivorStart == GIV(futureSurvivorStart)) + && ((!firedEphemerons) + && (!GIV(ephemeronList))))) { + assert(GIV(numRememberedEphemerons) == 0); + return pass; + } + scavengeFutureSurvivorSpaceStartingAt(previousFutureSurvivorStart); + previousFutureSurvivorStart = GIV(futureSurvivorStart); + + /* all reachable objects in this cycle have been promoted to futureSpace. + ephemerons can now be processed. */ + firedEphemerons = processEphemerons(); + if (((newSize = sizeOfObjStack(GIV(mournQueue)))) > mournQueueSize) { + mapMournQueue(); + mournQueueSize = newSize; + } + } + return pass; +} + + +/* Scavenge the referents of a normal pointer object. + Answer if it still refers to young objects. */ + + /* SpurGenerationScavenger>>#scavengeReferentsOfPointers: */ +static NoDbgRegParms sqInt +scavengeReferentsOfPointers(sqInt referrer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt foundNewReferent; + sqInt i; + usqInt numSlots; + sqInt referent; + sqInt referentSqInt; + sqInt toDoLimit; + + foundNewReferent = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(referrer)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(referrer + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(referrer - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + return foundNewReferent; +} + + +/* scavengeReferentsOf: referrer inspects relevant pointers in referrer. If + any are new objects, it has them moved to FutureSurvivorSpace, and + answers truth. If there are no new referents, it answers falsity. To + handle weak arrays, if the referrer is weak only scavenge strong slots and + answer true so that it won't be removed from the remembered set until + later. To handle ephemerons, only scavenge other slots if the key slot has + already been scavenged. */ +/* forwarding objects should be followed by callers, + unless the forwarder is a root in the remembered table. */ + + /* SpurGenerationScavenger>>#scavengeReferentsOf: */ +static NoDbgRegParms sqInt +scavengeReferentsOf(sqInt referrer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt foundNewReferent; + sqInt i; + usqInt numSlots; + sqInt referent; + sqInt referentSqInt; + sqInt toDoLimit; + + assert((!(isForwarded(referrer))) + || (isRemembered(referrer))); + assert((isInFutureSpace(referrer)) + || (isInOldSpace(referrer))); + switch ((byteAt((void *)(referrer + (formatFieldByteOffset())))) & (formatMask())) { + case nonIndexablePointerFormat(): + case arrayFormat(): + /* begin scavengeReferentsOfPointers: */ + foundNewReferent = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(referrer)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(referrer + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(referrer - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + return foundNewReferent; + + case indexablePointersFormat(): + /* begin scavengeReferentsOfIndexablePointers: */ + foundNewReferent = 0; + toDoLimit = (numSlotsOfIndexablePointerObj(referrer)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + return foundNewReferent; + + case weakArrayFormat(): + /* begin scavengeReferentsOfWeakling: */ + toDoLimit = (numStrongSlotsOfWeakling(referrer)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (!(oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart)))) { + referent = copyAndForward(referent); + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + + /* Answer true to keep any weak objects in the remembered set until later. */ + return 1; + + case ephemeronFormat(): + /* Scavenge the referents of an ephemeron. If the key + has not yet been scavenged, don't scavenge it yet. */ + if (isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(referrer)) + && (isObjEphemeron(referrer))), + /* fetchPointer:ofObject: */ + longAt((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord()))))))) { + return 1; + } + + /* begin scavengeReferentsOfPointers: */ + foundNewReferent = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(referrer)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(referrer + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(referrer - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + return foundNewReferent; + + case forwardedFormat(): + /* begin scavengeReferentOfForwarder: */ + foundNewReferent = 0; + referent = longAt((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord())))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord()))),referent); + } + return foundNewReferent; + + case firstCompiledMethodFormat(): + case (firstCompiledMethodFormat()) + 1: + case (firstCompiledMethodFormat()) + 2: + case (firstCompiledMethodFormat()) + 3: + case (firstCompiledMethodFormat()) + 4: + case (firstCompiledMethodFormat()) + 5: + case (firstCompiledMethodFormat()) + 6: + case (firstCompiledMethodFormat()) + 7: + /* begin scavengeReferentsOfCompiledCode: */ + foundNewReferent = 0; + toDoLimit = literalCountOf(referrer); + for (i = 1; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + return foundNewReferent; + + default: + assert(((numSlotsOf(referrer)) == 0) + || (isWordsOrBytesNonImm(referrer))); + } + return 0; +} + + +/* scavengeRememberedSetStartingAt: n traverses objects in the remembered + set starting at the nth one. If the object does not refer to any new + objects, it + is removed from the set. Otherwise, its new referents are scavenged. Defer + scavenging ephemerons until after a complete scavenge has been performed, + so that triggered ephemerons can be fired. Move them to the front of the + set and count them in numRememberedEphemerons for later scanning. */ + + /* SpurGenerationScavenger>>#scavengeRememberedSetStartingAt: */ +static NoDbgRegParms void +scavengeRememberedSetStartingAt(sqInt n) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt destIndex; + sqInt referrer; + sqInt sourceIndex; + + sourceIndex = (destIndex = n); + while (sourceIndex < GIV(rememberedSetSize)) { + referrer = GIV(rememberedSet)[sourceIndex]; + + /* Any potential firing ephemerons should not be scanned yet. + Move any to the front of the set to save time in later scanning. */ + if ((isEphemeron(referrer)) + && (isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(referrer)) + && (isObjEphemeron(referrer))), + /* fetchPointer:ofObject: */ + longAt((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord())))))))) { + assert(destIndex >= GIV(numRememberedEphemerons)); + GIV(rememberedSet)[destIndex] = (GIV(rememberedSet)[GIV(numRememberedEphemerons)]); + GIV(rememberedSet)[GIV(numRememberedEphemerons)] = referrer; + GIV(numRememberedEphemerons) += 1; + destIndex += 1; + } + else { + if (scavengeReferentsOf(referrer)) { + GIV(rememberedSet)[destIndex] = referrer; + destIndex += 1; + } + else { + setIsRememberedOfto(referrer, 0); + } + } + sourceIndex += 1; + } + GIV(rememberedSetSize) = destIndex; + assert(!((unfiredEphemeronsAtEndOfRememberedSet()))); +} + + +/* (Slang flattens so need unique selectors) */ + + /* SpurGenerationScavenger>>#scavengerTenuringThreshold */ +static float +scavengerTenuringThreshold(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (GIV(tenureThreshold) >= ((GIV(pastSpace).start)) + ? (((double) (GIV(tenureThreshold) - ((GIV(pastSpace).start))) )) / (((GIV(pastSpace).limit)) - ((GIV(pastSpace).start))) + : 0); +} + + +/* For assert checking only. */ + + /* SpurGenerationScavenger>>#unfiredEphemeronsAtEndOfRememberedSet */ +static sqInt +unfiredEphemeronsAtEndOfRememberedSet(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt i; + sqInt referrer; + + for (i = GIV(numRememberedEphemerons); i < GIV(rememberedSetSize); i += 1) { + referrer = GIV(rememberedSet)[i]; + + /* begin isEphemeron: */ + assert(isNonImmediate(referrer)); + format = (byteAt((void *)(referrer + (formatFieldByteOffset())))) & (formatMask()); + if (format == (ephemeronFormat())) { + if (isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(referrer)) + && (isObjEphemeron(referrer))), + /* fetchPointer:ofObject: */ + longAt((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord()))))))) { + return 1; + } + } + } + return 0; +} + + +/* Answer if the remembered table is laid out correctly after + scavengeInactiveEphemeronsInRememberedSet. This implies that up to + numRememberedEphemerons are only unfired ephemerons, and no unfired + ephemerons beyond. + */ + + /* SpurGenerationScavenger>>#validRememberedSetPartitionPostSIEIRS */ +static sqInt +validRememberedSetPartitionPostSIEIRS(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt i; + sqInt referrer; + + for (i = 0; i < GIV(rememberedSetSize); i += 1) { + referrer = GIV(rememberedSet)[i]; + + /* begin isEphemeron: */ + assert(isNonImmediate(referrer)); + format = (byteAt((void *)(referrer + (formatFieldByteOffset())))) & (formatMask()); + if (format == (ephemeronFormat())) { + if ((isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(referrer)) + && (isObjEphemeron(referrer))), + /* fetchPointer:ofObject: */ + longAt((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord())))))) + ? i >= GIV(numRememberedEphemerons) + : i < GIV(numRememberedEphemerons))) { + return 0; + } + } + } + return 1; +} + + +/* Answer if everything on the ephemeronList is a firable ephemeron */ + + /* SpurGenerationScavenger>>#wholeEphemeronListIsFirable */ +static sqInt +wholeEphemeronListIsFirable(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ephemeron; + usqInt ephemeronCorpse; + sqInt listOffset; + + if (!GIV(ephemeronList)) { + return 1; + } + ephemeronCorpse = ((((usqInt)((GIV(ephemeronList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + while (ephemeronCorpse) { + if (!((/* isYoung: */ + ((!(ephemeronCorpse & (tagMask())))) + && (oopisLessThan(ephemeronCorpse, GIV(oldSpaceStart)))) + && ((!((longAt((void *)(ephemeronCorpse))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + return 0; + } + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(ephemeronCorpse)); + ephemeron = longAt((void *)((ephemeronCorpse + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(ephemeron & (tagMask())))) + && ((!((longAt((void *)(ephemeron))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ephemeron = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + } + if (!((isEphemeron(ephemeron)) + && (isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(ephemeron)) + && (isObjEphemeron(ephemeron))), + /* fetchPointer:ofObject: */ + longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord()))))))))) { + return 0; + } + + /* begin nextCorpseOrNil: */ + assert(isYoung(ephemeronCorpse)); + listOffset = ((((usqInt)(((long32At((void *)(ephemeronCorpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(ephemeronCorpse + (formatFieldByteOffset())))); + ephemeronCorpse = ((sqInt) ((listOffset + ? ((((usqInt)((listOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0))); + } + return 1; +} + + +/* Output the entire record. */ + + /* SpurGenerationScavenger>>#writeScavengeLog */ +static NeverInline void +writeScavengeLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + static char *policyNames[] = {"", "by age", "by class", "to shrink rt", "don't tenure", "mark on tenure"}; + + /* log data collected by logStartScavenge */ + fprintf(GIV(scavengeLog), + "scavenge %" PRIdSQINT " eden bytes: 0x%" PRIxSQINT "/%" PRIdSQINT " past bytes: 0x%" PRIxSQINT "/%" PRIdSQINT "\n\trem set: %" PRIdSQINT " redzone: %" PRIdSQINT " size: %" PRIdSQINT "\n", + GIV(statScavenges), + (GIV(scavengeLogRecord).sEdenBytes), + (GIV(scavengeLogRecord).sEdenBytes), + (GIV(scavengeLogRecord).sPastBytes), + (GIV(scavengeLogRecord).sPastBytes), + (GIV(scavengeLogRecord).sRememberedSetSize), + (GIV(scavengeLogRecord).sRememberedSetRedZone), + (GIV(scavengeLogRecord).sRememberedSetLimit)); + + /* log data collected by logTenuringPolicy */ + fprintf(GIV(scavengeLog), + (((GIV(scavengeLogRecord).tTenureCriterion)) == TenureToShrinkRT + ? "\ttenure below 0x%" PRIxSQINT "/%" PRIdSQINT " %s refct %" PRIdSQINT "\n" + : "\ttenure below 0x%" PRIxSQINT "/%" PRIdSQINT " %s\n"), + (GIV(scavengeLogRecord).tTenureThreshold), + (GIV(scavengeLogRecord).tTenureThreshold), + policyNames[(GIV(scavengeLogRecord).tTenureCriterion)], + (GIV(scavengeLogRecord).tRefCountToShrinkRT)); + + /* log data collected by logEndScavenge */ + fprintf(GIV(scavengeLog), + "\tsurvivor bytes: 0x%" PRIxSQINT "/%" PRIdSQINT " rem set: %" PRIdSQINT " tenured: %" PRIdSQINT " usecs: %" PRIdSQINT "\n", + (GIV(scavengeLogRecord).eSurvivorBytes), + (GIV(scavengeLogRecord).eSurvivorBytes), + (GIV(scavengeLogRecord).eRememberedSetSize), + ((GIV(scavengeLogRecord).eStatTenures)) - ((GIV(scavengeLogRecord).sStatTenures)), + GIV(statSGCDeltaUsecs)); + fflush(GIV(scavengeLog)); +} + + +/* Answer the accessible object following the given object or + free chunk in the heap. Return nil when heap is exhausted. + This is for primitiveNextObject subsequent to primitiveSomeObject. + It also tries to handle more general use by ordering objects as + eden + past + old + but this is tricky becaus ethe order in memory is + past + eden + old */ + + /* SpurMemoryManager>>#accessibleObjectAfter: */ +static NoDbgRegParms sqInt +accessibleObjectAfter(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objAfter; + + objAfter = objOop; + if (oopisLessThan(objAfter, GIV(nilObj))) { + assert((isInEden(objOop)) + || (isInPastSpace(objOop))); + if (oopisGreaterThan(objAfter, GIV(pastSpaceStart))) { + /* Obj is in eden. Answer next normal object in eden, if there is one. */ + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objAfter); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objAfter = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objAfter = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + if (!(oopisLessThan(objAfter, GIV(freeStart)))) break; + if (((longAt((void *)(objAfter))) & (classIndexMask())) > (lastClassIndexPun())) { + return objAfter; + } + } + + /* There wasn't a next object in eden. If past space is empty answer nilObj. */ + if (GIV(pastSpaceStart) <= (((GIV(pastSpace)).start))) { + return GIV(nilObj); + } + + /* If the first object in pastSpace is OK, answer it, otherwise fall through to enumerate past space. */ + address = ((GIV(pastSpace)).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objAfter = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + if (((longAt((void *)(objAfter))) & (classIndexMask())) > (lastClassIndexPun())) { + return objAfter; + } + } + + /* Either objOop was in pastSpace, or enumeration exhaused eden, so enumerate past space. */ + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objAfter); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + objAfter = GIV(pastSpaceStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objAfter = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l2:; + if (!(oopisLessThan(objAfter, GIV(pastSpaceStart)))) break; + if (((longAt((void *)(objAfter))) & (classIndexMask())) > (lastClassIndexPun())) { + return objAfter; + } + } + return GIV(nilObj); + } + + /* object in new space */ + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objAfter); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objAfter = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + objAfter = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3: + if (objAfter == GIV(endOfMemory)) { + return null; + } + if (((longAt((void *)(objAfter))) & (classIndexMask())) > (lastClassIndexPun())) { + return objAfter; + } + } + return 0; +} + + +/* Answer whether an ephemeron is active (has an unmarked + key) and was pushed on the unscanned ephemerons stack. */ + + /* SpurMemoryManager>>#activeAndDeferredScan: */ +static NoDbgRegParms NeverInline sqInt +activeAndDeferredScan(sqInt anEphemeron) +{ + sqInt key; + + key = 0; + assert(isEphemeron(anEphemeron)); + if ((isImmediate((/* begin keyOfEphemeron: */ + assert((isNonImmediate(anEphemeron)) + && (isObjEphemeron(anEphemeron))), + (key = longAt((void *)((anEphemeron + BaseHeaderSize) + (0U << (shiftForWord())))))))) + || (((byteAt((void *)(key + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) { + return 0; + } + return pushOnUnscannedEphemeronsStack(anEphemeron); +} + + +/* Add a freeChunk sub tree back into the large free chunk tree. + This is for allocateOldSpaceChunkOf[Exactly]Bytes:[suchThat:]. */ +/* N.B. *can't* use numSlotsOfAny: because of rounding up of odd slots + and/or step in size at 1032 bytes in 32-bits or 2048 bytes in 64-bits. */ + + /* SpurMemoryManager>>#addFreeSubTree: */ +static NoDbgRegParms void +addFreeSubTree(sqInt freeTree) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytesInArg; + usqInt bytesInNode; + sqInt subNode; + sqInt treeNode; + + assert(isFreeObject(freeTree)); + bytesInArg = bytesInBody(freeTree); + assert(bytesInArg >= ((numFreeLists()) * (allocationUnit()))); + treeNode = GIV(freeLists)[0]; + assert(treeNode != 0); + while (1) { + bytesInNode = bytesInBody(treeNode); + + /* check for overlap; could write this as self oop: (self objectAfter: freeChunk) isLessThanOrEqualTo: child... + but that relies on headers being correct, etc. So keep it clumsy... */ + assert((oopisLessThanOrEqualTo((freeTree + bytesInArg) - BaseHeaderSize, treeNode)) + || (oopisGreaterThanOrEqualTo(freeTree, (treeNode + bytesInNode) - BaseHeaderSize))); + assert(bytesInNode >= ((numFreeLists()) * (allocationUnit()))); + assert(bytesInArg != bytesInNode); + if (bytesInNode > bytesInArg) { + subNode = longAt((void *)((treeNode + BaseHeaderSize) + (3U << (shiftForWord())))); + if (!subNode) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(treeNode)); + assert((freeTree == 0) + || (isFreeObject(freeTree))); + longAtput((void *)((treeNode + BaseHeaderSize) + (3U << (shiftForWord()))),freeTree); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeTree)); + assert((treeNode == 0) + || (isFreeObject(treeNode))); + longAtput((void *)((freeTree + BaseHeaderSize) + (2U << (shiftForWord()))),treeNode); + return; + } + } + else { + subNode = longAt((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + if (!subNode) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(treeNode)); + assert((freeTree == 0) + || (isFreeObject(freeTree))); + longAtput((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord()))),freeTree); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeTree)); + assert((treeNode == 0) + || (isFreeObject(treeNode))); + longAtput((void *)((freeTree + BaseHeaderSize) + (2U << (shiftForWord()))),treeNode); + return; + } + } + treeNode = subNode; + } +} + + +/* Add the given variable location to the extra roots table. */ + + /* SpurMemoryManager>>#addGCRoot: */ +sqInt +addGCRoot(sqInt *varLoc) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (GIV(extraRootCount) >= ExtraRootsSize) { + return 0; + } + GIV(extraRoots)[(GIV(extraRootCount) += 1)] = varLoc; + return 1; +} + + +/* Answer if maybeClassObj looks like a class object */ + + /* SpurMemoryManager>>#addressCouldBeClassObj: */ +static NoDbgRegParms sqInt +addressCouldBeClassObj(sqInt maybeClassObj) +{ + return (addressCouldBeObj(maybeClassObj)) + && (objCouldBeClassObj(maybeClassObj)); +} + + /* SpurMemoryManager>>#addressCouldBeObj: */ +sqInt +addressCouldBeObj(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((address & (BaseHeaderSize - 1)) == 0) + && ((/* Inlined isInOldSpace: for speed, because it also segregates the newSpace objects */ + + (oopisGreaterThanOrEqualTo(address, GIV(oldSpaceStart)) + ? oopisLessThan(address, GIV(endOfMemory)) + : (oopisGreaterThanOrEqualToandLessThan(address, ((GIV(eden)).start), GIV(freeStart))) + || ((oopisGreaterThanOrEqualToandLessThan(address, ((GIV(pastSpace)).start), GIV(pastSpaceStart))) + || ((GIV(gcPhaseInProgress) == ScavengeInProgress) + && (oopisGreaterThanOrEqualToandLessThan(address, ((GIV(futureSpace)).start), GIV(futureSurvivorStart)))))))); +} + + +/* Answer if address appears to be that of either an immediate or an object. + For code disassembly and assertions. */ + + /* SpurMemoryManager>>#addressCouldBeOop: */ +sqInt +addressCouldBeOop(sqInt address) +{ + return (((address & (tagMask())) != 0)) + || (addressCouldBeObj(address)); +} + + +/* Add freeChunk to the relevant freeList. + For the benefit of sortedFreeObject:, if freeChunk is large, answer the + treeNode it + is added to, if it is added to the next list of a freeTreeNode, otherwise + answer 0. */ +/* coInterpreter transcript ensureCr. coInterpreter print: 'freeing '. self + printFreeChunk: freeChunk. + */ + + /* SpurMemoryManager>>#addToFreeList:bytes: */ +static NoDbgRegParms sqInt +addToFreeListbytes(sqInt freeChunk, sqInt chunkBytes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt child; + usqInt childBytes; + sqInt index; + sqInt lilliputian; + sqInt nextFreeChunk; + sqInt parent; + + assert(isFreeObject(freeChunk)); + assert(chunkBytes == (bytesInBody(freeChunk))); + + /* Too slow to be enabled byt default but useful to debug Selective... + self deny: (compactor isSegmentBeingCompacted: (segmentManager segmentContainingObj: freeChunk)). */ + index = chunkBytes / 8 /* allocationUnit */; + if (index < 64 /* numFreeLists */) { + nextFreeChunk = GIV(freeLists)[index]; + + /* begin setNextFreeChunkOf:withValue:chunkBytes: */ + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + lilliputian = chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if ((nextFreeChunk != 0) + && (!lilliputian)) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((freeChunk == 0) + || (isFreeObject(freeChunk))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),freeChunk); + } + + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + if (!(chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */))) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + GIV(freeLists)[index] = freeChunk; + GIV(freeListsMask) = GIV(freeListsMask) | (1ULL << index); + return 0; + } + + /* begin addToFreeTree:bytes: */ + /* begin initFreeTreeChunk:bytes: */ + assert(isFreeObject(freeChunk)); + assert(chunkBytes == (bytesInBody(freeChunk))); + assert(chunkBytes >= ((numFreeLists()) * (allocationUnit()))); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord()))),0); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),0); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (2U << (shiftForWord()))),0); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (3U << (shiftForWord()))),0); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (4U << (shiftForWord()))),0); + + /* Large chunk list organized as a tree, each node of which is a list of chunks of the same size. + Beneath the node are smaller and larger blocks. */ + parent = 0; + child = GIV(freeLists)[0]; + while (child != 0) { + childBytes = bytesInBody(child); + + /* check for overlap; could write this as self oop: (self objectAfter: freeChunk) isLessThanOrEqualTo: child... + but that relies on headers being correct, etc. So keep it clumsy... */ + assert((oopisLessThanOrEqualTo((freeChunk + chunkBytes) - BaseHeaderSize, child)) + || (oopisGreaterThanOrEqualTo(freeChunk, (child + childBytes) - BaseHeaderSize))); + if (childBytes == chunkBytes) { + nextFreeChunk = longAt((void *)((child + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((freeChunk == 0) + || (isFreeObject(freeChunk))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),freeChunk); + } + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(child)); + assert((freeChunk == 0) + || (isFreeObject(freeChunk))); + longAtput((void *)((child + BaseHeaderSize) + (0U << (shiftForWord()))),freeChunk); + if (freeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + assert((child == 0) + || (isFreeObject(child))); + longAtput((void *)((freeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),child); + } + return child; + } + + /* size match; add to list at node. + walk down the tree */ + parent = child; + child = longAt((void *)((child + BaseHeaderSize) + ((((usqInt)(((childBytes > chunkBytes + ? 3 /* freeChunkSmallerIndex */ + : 4 /* freeChunkLargerIndex */))) << (shiftForWord())))))); + } + if (!parent) { + assert((GIV(freeLists)[0]) == 0); + GIV(freeLists)[0] = freeChunk; + GIV(freeListsMask) = GIV(freeListsMask) | 1; + return 0; + } + assert(((GIV(freeListsMask) & 1) != 0)); + + /* insert in tree */ + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + assert((parent == 0) + || (isFreeObject(parent))); + longAtput((void *)((freeChunk + BaseHeaderSize) + (2U << (shiftForWord()))),parent); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(parent)); + assert((freeChunk == 0) + || (isFreeObject(freeChunk))); + longAtput((void *)((parent + BaseHeaderSize) + ((((usqInt)(((childBytes > chunkBytes + ? 3 /* freeChunkSmallerIndex */ + : 4 /* freeChunkLargerIndex */))) << (shiftForWord()))))),freeChunk); + return 0; +} + + +/* Attempt to answer an array of all objects, excluding those that may + be garbage collected as a side effect of allocating the result array. + If no memory is available answer the number of instances as a + SmallInteger. Since objects are at least 16 bytes big, and the largest + SmallInteger covers + 1/4 of the address space, the count can never overflow. */ + + /* SpurMemoryManager>>#allInstancesOf: */ +static NoDbgRegParms sqInt +allInstancesOf(sqInt aClass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt actualIndex; + usqInt bytes; + int classIndex; + sqInt classOrNil; + sqInt count; + sqInt countSqInt; + int expectedIndex; + int expectedIndexInt; + sqInt followingWord; + usqInt followingWordAddress; + sqInt freeChunk; + sqInt i; + sqInt index; + sqInt iSqInt; + sqInt j; + usqInt limit; + usqInt newObj; + usqInt numBytes; + usqInt numSlots; + sqInt objOop; + sqInt page; + sqInt prevObj; + sqInt prevPrevObj; + sqInt ptr; + sqInt ptrSqInt; + sqInt smallObj; + usqInt start; + usqInt startUsqInt; + sqInt toDoLimit; + sqInt valuePointer; + + classIndex = (long32At((void *)(aClass + 4))) & (identityHashHalfWordMask()); + if (!classIndex) { + /* begin allocateSlots:format:classIndex: */ + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + 8 /* allocationUnit */; + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (0 <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + freeChunk = allocateSlotsInOldSpacebytesformatclassIndex(0, numBytes, arrayFormat(), ClassArrayCompactIndex); + goto l1; + } + longAtput((void *)(newObj),((((((usqLong) 0)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + freeChunk = newObj; + /* end allocateSlots:format:classIndex: */ +l1: + return freeChunk; + } + + /* N.B. Does /not/ update totalFreeOldSpace */ + freeChunk = allocateLargestFreeChunk(); + start = freeChunk + BaseHeaderSize; + limit = addressAfter(freeChunk); + + /* begin isClassAtUniqueIndex: */ + expectedIndex = (long32At((void *)(aClass + 4))) & (identityHashHalfWordMask()); + for (iSqInt = 0; iSqInt < GIV(numClassTablePages); iSqInt += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNil = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNil != GIV(nilObj)) { + index = ((((usqInt)(iSqInt) << (classTableMajorIndexShift())))) + j; + if ((classOrNil == aClass) + && ((index != expectedIndex) + && (index > (lastClassIndexPun())))) { + goto l4; + } + } + } + } + + /* begin uniqueIndex:allInstancesInto:limit:resultsInto: */ + countSqInt = 0; + ptrSqInt = start; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + + /* continue enumerating even if no room so as to unmark all objects. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + countSqInt += 1; + if (ptrSqInt < limit) { + longAtput((void *)(ptrSqInt),objOop); + ptrSqInt += BytesPerOop; + } + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l10; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l10:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + startUsqInt = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (startUsqInt > GIV(freeStart)) { + goto l9; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(startUsqInt + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? startUsqInt + BaseHeaderSize + : startUsqInt); + while (oopisLessThan(objOop, GIV(freeStart))) { + /* continue enumerating even if no room so as to unmark all objects. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + countSqInt += 1; + if (ptrSqInt < limit) { + longAtput((void *)(ptrSqInt),objOop); + ptrSqInt += BytesPerOop; + } + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l8; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l8:; + } + /* end allNewSpaceEntitiesDo: */ +l9: + count = countSqInt; + ptr = ptrSqInt; + goto l3; +l4: + + /* begin ambiguousClass:allInstancesInto:limit:resultsInto: */ + countSqInt = 0; + ptrSqInt = start; + expectedIndex = (long32At((void *)(aClass + 4))) & (identityHashHalfWordMask()); + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + + /* continue enumerating even if no room so as to unmark all objects and/or normalize class indices. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + actualIndex = (longAt((void *)(objOop))) & (classIndexMask()); + if ((classOrNilAtIndex(actualIndex)) == aClass) { + if (actualIndex != expectedIndex) { + /* begin setClassIndexOf:to: */ + assert(((expectedIndex >= 0) && (expectedIndex <= (classIndexMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(classIndexMask()))) + expectedIndex); + } + countSqInt += 1; + if (ptrSqInt < limit) { + longAtput((void *)(ptrSqInt),objOop); + ptrSqInt += BytesPerOop; + } + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l7; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l7:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + startUsqInt = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (startUsqInt > GIV(freeStart)) { + goto l6; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(startUsqInt + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? startUsqInt + BaseHeaderSize + : startUsqInt); + while (oopisLessThan(objOop, GIV(freeStart))) { + /* continue enumerating even if no room so as to unmark all objects and/or normalize class indices. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + actualIndex = (longAt((void *)(objOop))) & (classIndexMask()); + if ((classOrNilAtIndex(actualIndex)) == aClass) { + if (actualIndex != expectedIndex) { + /* begin setClassIndexOf:to: */ + assert(((expectedIndex >= 0) && (expectedIndex <= (classIndexMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(classIndexMask()))) + expectedIndex); + } + countSqInt += 1; + if (ptrSqInt < limit) { + longAtput((void *)(ptrSqInt),objOop); + ptrSqInt += BytesPerOop; + } + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l5; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l5:; + } + /* end allNewSpaceEntitiesDo: */ +l6: + + /* begin purgeDuplicateClassTableEntriesFor: */ + expectedIndexInt = (long32At((void *)(aClass + 4))) & (identityHashHalfWordMask()); + for (iSqInt = 0; iSqInt < GIV(numClassTablePages); iSqInt += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNil = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNil != GIV(nilObj)) { + index = ((((usqInt)(iSqInt) << (classTableMajorIndexShift())))) + j; + if ((classOrNil == aClass) + && ((index != expectedIndexInt) + && (index > (lastClassIndexPun())))) { + classAtIndexput(index, GIV(nilObj)); + if (index < GIV(classTableIndex)) { + GIV(classTableIndex) = index; + } + } + } + } + } + + /* classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + assert(GIV(classTableIndex) >= (1U << (classTableMajorIndexShift()))); + count = countSqInt; + ptr = ptrSqInt; +l3: + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + if ((count > ((ptr - start) / BytesPerOop)) + || ((limit != ptr) + && ((limit - ptr) <= 8 /* allocationUnit */))) { + freeChunkWithBytesat(bytesInBody(freeChunk), /* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk)); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return (((usqInt)count << 3) | 1); + } + + /* not enough room + can't split a single word */ + if (count < (numSlotsMask())) { + /* begin allocateSlots:format:classIndex: */ + if (count >= (numSlotsMask())) { + if ((((usqInt)(count)) >> 56) > 0) { + smallObj = null; + goto l2; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (count * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((count < 1 + ? 8 /* allocationUnit */ + : count * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (count <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + smallObj = allocateSlotsInOldSpacebytesformatclassIndex(count, numBytes, arrayFormat(), ClassArrayCompactIndex); + goto l2; + } + if (count >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + count); + longAtput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + } + else { + longAtput((void *)(newObj),((((((usqLong) count)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + smallObj = newObj; + /* end allocateSlots:format:classIndex: */ +l2: + for (i = 0; i < count; i += 1) { + valuePointer = longAt((void *)((freeChunk + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(smallObj)) + && (!(isForwarded(smallObj)))); + assert(validStorePointerUncheckedArgs(i, smallObj, valuePointer)); + longAtput((void *)((smallObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + freeChunkWithBytesat(bytesInBody(freeChunk), /* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk)); + beRootIfOld(smallObj); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return smallObj; + } + bytes = (BaseHeaderSize + BaseHeaderSize) + (count * BytesPerOop); + start = /* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk); + freeChunkWithBytesat((limit - start) - bytes, start + bytes); + GIV(totalFreeOldSpace) -= bytes; + + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(freeChunk - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56))) + count); + + /* begin set:classIndexTo:formatTo: */ + assert(((ClassArrayCompactIndex >= 0) && (ClassArrayCompactIndex <= (classIndexMask())))); + assert((((arrayFormat()) >= 0) && ((arrayFormat()) <= (formatMask())))); + longAtput((void *)(freeChunk),((longAt((void *)(freeChunk))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + (ClassArrayCompactIndex + ((((usqInt)((arrayFormat())) << (formatShift())))))); + + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(freeChunk + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(freeChunk); + } + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + runLeakCheckerFor(GCModeFull); + return freeChunk; +} + + +/* Attempt to answer an array of all objects, excluding those that may + be garbage collected as a side effect of allocating the result array. + If no memory is available answer the number of objects as a SmallInteger. + Since objects are at least 16 bytes big, and the largest SmallInteger + covers 1/4 of the address space, the count can never overflow. */ + + /* SpurMemoryManager>>#allObjects */ +static sqInt +allObjects(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytes; + sqInt count; + sqInt followingWord; + usqInt followingWordAddress; + sqInt freeChunk; + usqInt limit; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + sqInt ptr; + usqInt start; + usqInt startUsqInt; + + + /* N.B. Does /not/ update totalFreeOldSpace */ + freeChunk = allocateLargestFreeChunk(); + ptr = (start = freeChunk + BaseHeaderSize); + limit = addressAfter(freeChunk); + count = 0; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + + /* continue enumerating even if no room so as to unmark all objects. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + count += 1; + if (ptr < limit) { + longAtput((void *)(ptr),objOop); + ptr += BytesPerOop; + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + startUsqInt = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (startUsqInt > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(startUsqInt + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? startUsqInt + BaseHeaderSize + : startUsqInt); + while (oopisLessThan(objOop, GIV(freeStart))) { + /* continue enumerating even if no room so as to unmark all objects. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + count += 1; + if (ptr < limit) { + longAtput((void *)(ptr),objOop); + ptr += BytesPerOop; + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3: + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + assert(count >= (numSlotsMask())); + if ((count > ((ptr - start) / BytesPerOop)) + || ((limit != ptr) + && ((limit - ptr) <= 8 /* allocationUnit */))) { + freeChunkWithBytesat(bytesInBody(freeChunk), /* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk)); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return (((usqInt)count << 3) | 1); + } + + /* not enough room + can't split a single word */ + bytes = (BaseHeaderSize + BaseHeaderSize) + (count * BytesPerOop); + start = /* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk); + freeChunkWithBytesat((limit - start) - bytes, start + bytes); + GIV(totalFreeOldSpace) -= bytes; + + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(freeChunk - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56))) + count); + + /* begin set:classIndexTo:formatTo: */ + assert(((ClassArrayCompactIndex >= 0) && (ClassArrayCompactIndex <= (classIndexMask())))); + assert((((arrayFormat()) >= 0) && ((arrayFormat()) <= (formatMask())))); + longAtput((void *)(freeChunk),((longAt((void *)(freeChunk))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + (ClassArrayCompactIndex + ((((usqInt)((arrayFormat())) << (formatShift())))))); + + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(freeChunk + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(freeChunk); + } + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + runLeakCheckerFor(GCModeFull); + return freeChunk; +} + + /* SpurMemoryManager>>#allObjectsUnmarked */ +static sqInt +allObjectsUnmarked(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + GIV(bogon) = obj; + return 0; + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + return 1; +} + + /* SpurMemoryManager>>#allOldMarkedWeakObjectsOnWeaklingStack */ +static sqInt +allOldMarkedWeakObjectsOnWeaklingStack(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if ((isWeakNonImm(objOop)) + && (((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) { + if (!(isonObjStack(objOop, GIV(weaklingStack)))) { + return 0; + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + return 1; +} + + +/* Answer the largest free chunk in the free lists. */ +/* would like to use ifNotNil: but the ^next inside the ^blah ifNotNil: + confused Slang + */ + + /* SpurMemoryManager>>#allocateLargestFreeChunk */ +static sqInt +allocateLargestFreeChunk(void) +{ + sqInt freeChunk; + sqInt next; + sqInt nextFreeChunk; + + freeChunk = findLargestFreeChunk(); + if (!freeChunk) { + return null; + } + + /* This will be the node, not a list element. Answer a list element in preference. */ + next = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + if (next) { + assert((bytesInBody(freeChunk)) >= (numFreeLists())); + nextFreeChunk = longAt((void *)((next + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((freeChunk == 0) + || (isFreeObject(freeChunk))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),freeChunk); + } + return next; + } + unlinkSolitaryFreeTreeNode(freeChunk); + return freeChunk; +} + + +/* Answer a chunk of oldSpace from the free lists, if available, + otherwise answer nil. Break up a larger chunk if one of the + exact size does not exist. N.B. the chunk is simply a pointer, it + has no valid header. The caller *must* fill in the header correctly. */ +/* for debugging: */ +/* totalFreeOldSpace := self totalFreeListBytes */ + + /* SpurMemoryManager>>#allocateOldSpaceChunkOfBytes: */ +static NoDbgRegParms sqInt +allocateOldSpaceChunkOfBytes(usqInt chunkBytes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt child; + usqInt childBytes; + sqInt chunk; + usqInt index; + usqInt initialIndex; + sqInt lilliputian; + sqInt next; + sqInt nextFreeChunk; + usqInt nodeBytes; + sqInt parent; + + nodeBytes = 0; + + /* be optimistic (& don't wait for the write) */ + GIV(totalFreeOldSpace) -= chunkBytes; + initialIndex = chunkBytes / 8 /* allocationUnit */; + if ((initialIndex < 64 /* numFreeLists */) + && ((1ULL << initialIndex) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << initialIndex)) != 0)) { + if ((chunk = GIV(freeLists)[initialIndex])) { + assert(chunk == (startOfObject(chunk))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(chunk)); + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + lilliputian = chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */); + + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(chunk)) == (initialIndex * (allocationUnit()))) + && ((initialIndex > 1) + && ((startOfObject(chunk)) == chunk))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[initialIndex] = ((next = longAt((void *)((chunk + BaseHeaderSize) + (0U << (shiftForWord())))))); + if ((!lilliputian) + && (next != 0)) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + return chunk; + } + GIV(freeListsMask) -= 1ULL << initialIndex; + } + + /* first search for free chunks of a multiple of chunkBytes in size */ + index = initialIndex; + while ((((index += index)) < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << index)) != 0)) { + if ((chunk = GIV(freeLists)[index])) { + assert(chunk == (startOfObject(chunk))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(chunk)); + assert(((bytesInBody(chunk)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(chunk)) == chunk))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((next = longAt((void *)((chunk + BaseHeaderSize) + (0U << (shiftForWord())))))); + if (next) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + assert((bytesInBody(chunk)) == (index * (allocationUnit()))); + freeChunkWithBytesat((index * 8 /* allocationUnit */) - chunkBytes, (/* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk)) + chunkBytes); + return chunk; + } + GIV(freeListsMask) -= 1ULL << index; + } + } + + /* now get desperate and use the first that'll fit. + Note that because the minimum free size is 16 bytes (2 * allocationUnit), to + leave room for the forwarding pointer/next free link, we can only break chunks + that are at least 16 bytes larger, hence start at initialIndex + 2. */ + index = initialIndex + 1; + while ((((index += 1)) < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << index)) != 0)) { + if ((chunk = GIV(freeLists)[index])) { + assert(chunk == (startOfObject(chunk))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(chunk)); + assert(((bytesInBody(chunk)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(chunk)) == chunk))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((next = longAt((void *)((chunk + BaseHeaderSize) + (0U << (shiftForWord())))))); + if (next) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + assert((bytesInBody(chunk)) == (index * (allocationUnit()))); + freeChunkWithBytesat((index * 8 /* allocationUnit */) - chunkBytes, (/* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk)) + chunkBytes); + return chunk; + } + GIV(freeListsMask) -= 1ULL << index; + } + } + } + + /* Large chunk, or no space on small free lists. Search the large chunk list. + Large chunk list organized as a tree, each node of which is a list of chunks + of the same size. Beneath the node are smaller and larger blocks. + When the search ends parent should hold the smallest chunk at least as + large as chunkBytes, or 0 if none. */ + parent = 0; + child = GIV(freeLists)[0]; + while (child != 0) { + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(child)); + childBytes = bytesInBody(child); + if (childBytes == chunkBytes) { + chunk = longAt((void *)((child + BaseHeaderSize) + (0U << (shiftForWord())))); + if (chunk) { + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(chunk)); + nextFreeChunk = longAt((void *)((chunk + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(child)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((child + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((child == 0) + || (isFreeObject(child))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),child); + } + return /* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk); + } + nodeBytes = childBytes; + parent = child; + + /* break out of loop to remove interior node */ + child = 0; + } + else { + if (childBytes <= (chunkBytes + 8 /* allocationUnit */)) { + child = longAt((void *)((child + BaseHeaderSize) + (4U << (shiftForWord())))); + } + else { + /* parent will be smallest node >= chunkBytes + allocationUnit */ + parent = child; + nodeBytes = childBytes; + child = longAt((void *)((child + BaseHeaderSize) + (3U << (shiftForWord())))); + } + } + } + if (!parent) { + /* optimism was unfounded */ + GIV(totalFreeOldSpace) += chunkBytes; + return null; + } + + /* self printFreeChunk: parent */ + assert((nodeBytes == chunkBytes) + || (nodeBytes >= (chunkBytes + (2 * (allocationUnit()))))); + assert((bytesInBody(parent)) == nodeBytes); + + /* attempt to remove from list */ + chunk = longAt((void *)((parent + BaseHeaderSize) + (0U << (shiftForWord())))); + if (chunk) { + assert((chunkBytes == nodeBytes) + || ((chunkBytes + (allocationUnit())) < nodeBytes)); + nextFreeChunk = longAt((void *)((chunk + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(parent)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((parent + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((parent == 0) + || (isFreeObject(parent))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),parent); + } + if (chunkBytes != nodeBytes) { + freeChunkWithBytesat(nodeBytes - chunkBytes, (/* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk)) + chunkBytes); + } + return /* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk); + } + + /* no list; remove the interior node */ + chunk = parent; + unlinkSolitaryFreeTreeNode(chunk); + + /* if there's space left over, add the fragment back. */ + if (chunkBytes != nodeBytes) { + freeChunkWithBytesat(nodeBytes - chunkBytes, (/* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk)) + chunkBytes); + } + return /* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk); +} + + /* SpurMemoryManager>>#allocatePinnedSlots: */ +sqInt +allocatePinnedSlots(sqInt nSlots) +{ + sqInt obj; + usqInt p; + usqInt toDoLimit; + + obj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(nSlots, /* objectBytesForSlots: */ + (nSlots + ? ((((usqInt)(nSlots) << (shiftForWord())))) + ((nSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (obj) { + assert(isPinned(obj)); + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((obj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1, addressAfter(obj))); + toDoLimit = ((usqInt)(((obj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(obj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + return obj; +} + + +/* Answer the oop of a chunk of space in oldSpace with numSlots slots. The + header will have been filled-in but not the contents. If no memory is + available answer nil. */ + + /* SpurMemoryManager>>#allocateSlotsInOldSpace:bytes:format:classIndex: */ +static NoDbgRegParms sqInt +allocateSlotsInOldSpacebytesformatclassIndex(sqInt numSlots, usqInt totalBytes, sqInt formatField, sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt chunk; + + if ((chunk = allocateOldSpaceChunkOfBytes(totalBytes))) { + if (numSlots >= (numSlotsMask())) { + long64Atput((void *)(chunk),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + long64Atput((void *)(chunk + BaseHeaderSize),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex); + + /* begin checkFreeSpace:ignoring: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, chunk + BaseHeaderSize); + } + return chunk + BaseHeaderSize; + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + long64Atput((void *)(chunk),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex); + + /* begin checkFreeSpace:ignoring: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, chunk); + } + return chunk; + } + return 0; +} + + +/* All objects are a multiple of 8 bytes in length */ + + /* SpurMemoryManager>>#allocationUnit */ +static sqInt +allocationUnit(void) +{ + return 8; +} + + +/* N.B. generateToByDoLimitExpression:negative:on: guards against (unsigned)0 + - 1 going +ve + */ + + /* SpurMemoryManager>>#allStrongSlotsOfWeaklingAreMarked: */ +static NoDbgRegParms sqInt +allStrongSlotsOfWeaklingAreMarked(sqInt aWeakling) +{ + sqInt i; + sqInt referent; + sqInt toDoLimit; + + toDoLimit = (numStrongSlotsOfWeakling(aWeakling)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((aWeakling + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + if (!((byteAt((void *)(referent + (markBitsByteOffset())))) & (1U << (markedBitByteShift())))) { + return 0; + } + } + } + return 1; +} + + /* SpurMemoryManager>>#allUnscannedEphemeronsAreActive */ +static sqInt +allUnscannedEphemeronsAreActive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt key; + usqInt p; + + for (p = ((GIV(unscannedEphemerons).start)); p <= ((GIV(unscannedEphemerons).top)); p += BytesPerOop) { + /* begin keyOfMaybeFiredEphemeron: */ + assert(isMaybeFiredEphemeron(longAt((void *)(p)))); + key = longAt((void *)(((longAt((void *)(p))) + BaseHeaderSize) + (0U << (shiftForWord())))); + if ((((key & (tagMask())) != 0)) + || (((byteAt((void *)(key + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) { + return 0; + } + } + return 1; +} + + +/* we don't want to inline so we can nest that in an assertion with the + return true so the production VM does not generate any code here, while in + simulation, the code breaks on the assertion we want to. + */ + + /* SpurMemoryManager>>#assertInnerValidFreeObject: */ +static NoDbgRegParms NeverInline sqInt +assertInnerValidFreeObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cameFrom; + sqInt chunk; + sqInt chunkBytes; + usqInt index; + sqInt largeChild; + sqInt smallChild; + sqInt treeNode; + + assert(oopisLessThanOrEqualTo(addressAfter(objOop), GIV(endOfMemory))); + chunk = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert((chunk == 0) + || (isFreeOop(chunk))); + chunkBytes = bytesInBody(objOop); + + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + if (!(chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */))) { + /* double linkedlist assertions */ + chunk = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (chunk) { + assert(isFreeOop(chunk)); + assert(objOop == (fetchPointerofFreeChunk(freeChunkPrevIndex(), chunk))); + } + chunk = longAt((void *)((objOop + BaseHeaderSize) + (1U << (shiftForWord())))); + index = (bytesInBody(objOop)) / 8 /* allocationUnit */; + if ((index < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + if ((GIV(freeLists)[index]) == objOop) { + assert(chunk == 0); + } + } + else { + /* begin freeTreeNodesDo: */ + treeNode = GIV(freeLists)[0]; + if (!treeNode) { + goto l1; + } + cameFrom = -1; + do { + assert((bytesInBody(treeNode)) >= ((numFreeLists()) * (allocationUnit()))); + smallChild = longAt((void *)((treeNode + BaseHeaderSize) + (3U << (shiftForWord())))); + largeChild = longAt((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + assert((smallChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), smallChild)))); + assert((largeChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), largeChild)))); + + /* apply if the node has no children, or it has no large children and we're + returning from the small child, or we're returning from the large child. */ + if (((smallChild == 0) + && (largeChild == 0)) + || ((largeChild + ? cameFrom == largeChild + : cameFrom == smallChild))) { + if (treeNode == objOop) { + assert(chunk == 0); + } + + /* and since we've applied we must move on up */ + cameFrom = treeNode; + treeNode = longAt((void *)((treeNode + BaseHeaderSize) + (2U << (shiftForWord())))); + } + else { + if ((smallChild != 0) + && (cameFrom != smallChild)) { + treeNode = smallChild; + } + else { + assert(largeChild != 0); + treeNode = largeChild; + } + cameFrom = -1; + } + } while(treeNode != 0); + /* end freeTreeNodesDo: */ +l1:; + } + if (chunk) { + assert(isFreeOop(chunk)); + assert(objOop == (fetchPointerofFreeChunk(freeChunkNextIndex(), chunk))); + } + } + if ((bytesInBody(objOop)) >= 0x200 /* (numFreeLists * #allocationUnit) */) { + /* Tree assertions */ + chunk = longAt((void *)((objOop + BaseHeaderSize) + (2U << (shiftForWord())))); + assert((chunk == 0) + || ((isFreeOop(chunk)) + && (isLargeFreeObject(chunk)))); + chunk = longAt((void *)((objOop + BaseHeaderSize) + (3U << (shiftForWord())))); + assert((chunk == 0) + || ((isFreeOop(chunk)) + && (isLargeFreeObject(chunk)))); + chunk = longAt((void *)((objOop + BaseHeaderSize) + (4U << (shiftForWord())))); + assert((chunk == 0) + || ((isFreeOop(chunk)) + && (isLargeFreeObject(chunk)))); + } + return 1; +} + + /* SpurMemoryManager>>#become:with: */ +sqInt +becomewith(sqInt array1, sqInt array2) +{ + return becomewithtwoWaycopyHash(array1, array2, 1, 1); +} + + +/* All references to each object in array1 are swapped with all references to + the corresponding object in array2. That is, all pointers to one object + are replaced + with with pointers to the other. The arguments must be arrays of the same + length. + Answers PrimNoErr if the primitive succeeds, otherwise a relevant error + code. + */ +/* Implementation: Uses lazy forwarding to defer updating references until + message send. + */ + + /* SpurMemoryManager>>#become:with:twoWay:copyHash: */ +static NoDbgRegParms sqInt +becomewithtwoWaycopyHash(sqInt array1, sqInt array2, sqInt twoWayFlag, sqInt copyHashFlag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt contextSize; + sqInt ec; + sqInt fieldOffset; + sqInt fmt; + sqInt hash; + sqInt header; + sqInt headerSqInt; + sqInt i; + sqInt newObj1; + sqInt newObj2; + usqInt numSlots; + usqInt numSlotsUsqInt; + usqLong o1ClassIndex; + sqLong o1Header; + usqLong o2ClassIndex; + sqLong o2Header; + sqInt obj1; + sqInt obj2; + sqInt objOop; + sqInt objOopSqInt; + sqInt oop1; + sqInt oop2; + sqInt referent; + sqInt referentSqInt; + sqInt size; + sqInt sp; + sqInt toDoLimit; + sqInt top; + + assert(GIV(becomeEffectsFlags) == 0); + runLeakCheckerFor(GCModeBecome); + if (!(/* isArray: */ + ((!(array1 & (tagMask())))) + && (((byteAt((void *)(array1 + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + return PrimErrBadReceiver; + } + if (!((/* isArray: */ + ((!(array2 & (tagMask())))) + && (((byteAt((void *)(array2 + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(array1)) > (isForwardedObjectClassIndexPun())), + (((numSlotsUsqInt = byteAt((void *)(array1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) == ((/* begin numSlotsOf: */ + assert((classIndexOf(array2)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(array2 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array2 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))))) { + return PrimErrBadArgument; + } + + /* begin containsOnlyValidBecomeObjects:and:twoWay:copyHash: */ + /* begin lastPointerOf: */ + fmt = (byteAt((void *)(array1 + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt != (forwardedFormat())); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(array1))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(array1))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + fieldOffset = (((CtxtTempFrameStart - 1) + contextSize) * BytesPerOop) + BaseHeaderSize; + goto l2; + } + fieldOffset = ((((/* begin numSlotsOf: */ + assert((classIndexOf(array1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(array1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1) * BytesPerOop) + BaseHeaderSize; + goto l2; + } + if (fmt < (firstCompiledMethodFormat())) { + fieldOffset = 0; + goto l2; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(array1)); + headerSqInt = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + fieldOffset = (((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) - 1) * BytesPerOop) + BaseHeaderSize; + /* end lastPointerOf: */ +l2: + size = 0; + + /* array1 is known to be the same size as array2 */ + while (fieldOffset >= BaseHeaderSize) { + oop1 = longAt((void *)(array1 + fieldOffset)); + if (/* isOopForwarded: */ + ((!(oop1 & (tagMask())))) + && ((!((longAt((void *)(oop1))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop1)); + referent = longAt((void *)((oop1 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop1 = referent; + longAtput((void *)(array1 + fieldOffset),oop1); + } + + /* begin ifOopInvalidForBecome:errorCodeInto: */ + if (((oop1 & (tagMask())) != 0)) { + ec = PrimErrInappropriate; + goto l5; + } + if ((byteAt((void *)(oop1 + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + ec = PrimErrObjectIsPinned; + goto l5; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(oop1 + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + ec = PrimErrNoModification; + goto l5; + } + /* end ifOopInvalidForBecome:errorCodeInto: */ +l3: + oop2 = longAt((void *)(array2 + fieldOffset)); + if (/* isOopForwarded: */ + ((!(oop2 & (tagMask())))) + && ((!((longAt((void *)(oop2))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop2)); + referentSqInt = longAt((void *)((oop2 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop2 = referentSqInt; + longAtput((void *)(array2 + fieldOffset),oop2); + } + if (oop1 != oop2) { + if (twoWayFlag) { + /* begin ifOopInvalidForBecome:errorCodeInto: */ + if (((oop2 & (tagMask())) != 0)) { + ec = PrimErrInappropriate; + goto l5; + } + if ((byteAt((void *)(oop2 + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + ec = PrimErrObjectIsPinned; + goto l5; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(oop2 + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + ) { + ec = PrimErrNoModification; + goto l5; + } + /* end ifOopInvalidForBecome:errorCodeInto: */ +l4: + size = (size + (bytesInBody(oop1))) + (bytesInBody(oop2)); + GIV(becomeEffectsFlags) = (GIV(becomeEffectsFlags) | (becomeEffectFlagsFor(oop1))) | (becomeEffectFlagsFor(oop2)); + } + else { + if (copyHashFlag) { + if (((oop2 & (tagMask())) != 0)) { + ec = PrimErrInappropriate; + goto l5; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(oop2 + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + ) { + ec = PrimErrNoModification; + goto l5; + } + } + GIV(becomeEffectsFlags) = GIV(becomeEffectsFlags) | (becomeEffectFlagsFor(oop1)); + } + } + fieldOffset -= BytesPerOop; + } + if (size >= (GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart)))) { + ec = PrimErrNoMemory; + goto l5; + } + ec = 0; + /* end containsOnlyValidBecomeObjects:and:twoWay:copyHash: */ +l5: + if (ec) { + /* begin zeroBecomeEffectFlagsOnFailure */ + if (((GIV(becomeEffectsFlags) & BecameJittedCompiledMethodFlag) != 0)) { + unflagBecomeFlaggedMethods(); + } + GIV(becomeEffectsFlags) = 0; + return ec; + } + + /* begin preBecomeAction: */ + if (GIV(stackPage)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + + /* see the comment in InterpreterPrimitives>>primitiveArrayBecome */ + if (((GIV(becomeEffectsFlags) & BecameJittedCompiledMethodFlag) != 0)) { + /* begin insulateFramesAndContextsFromCogMethodsFlaggedForBecome */ + activeContext = divorceAllFramesSuchThat(isMachineCodeFrameForCogMethodFlaggedForBecome); + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + assert((GIV(framePointer) != 0) == (isStillMarriedContext(activeContext))); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + objOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, objOop, activeContext)); + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeContext & (tagMask())))) + && (oopisLessThan(activeContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),activeContext); + } + } + + /* Need to set gcMode var (to avoid passing the flag through a lot of the updating code) */ + GIV(gcMode) = GCModeBecome; + if (twoWayFlag) { + /* begin innerBecomeObjectsIn:and:copyHash: */ + toDoLimit = ((assert((classIndexOf(array1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(array1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + /* At first blush it would appear unnecessary to use followField: here since + the validation in become:with:twoWay:copyHash: follows forwarders. But + there's nothing to ensure all elements of each array are unique and don't + appear in the other array. So the enumeration could encounter an object + already becommed earlier in the same enumeration. */ + + /* begin followField:ofObject: */ + obj1 = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(obj1 & (tagMask())))) + && ((!((longAt((void *)(obj1))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + obj1 = fixFollowedFieldofObjectwithInitialValue(i, array1, obj1); + } + + /* begin followField:ofObject: */ + obj2 = longAt((void *)((array2 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(obj2 & (tagMask())))) + && ((!((longAt((void *)(obj2))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + obj2 = fixFollowedFieldofObjectwithInitialValue(i, array2, obj2); + } + if (obj1 != obj2) { + /* begin doBecome:and:copyHash: */ + o1Header = long64At((void *)(obj1)); + o1ClassIndex = (((usqInt)(obj1)) >> (identityHashFullWordShift())) & (identityHashHalfWordMask()); + if ((o1ClassIndex != 0) + && ((classAtIndex(o1ClassIndex)) != obj1)) { + o1ClassIndex = 0; + } + o2Header = long64At((void *)(obj2)); + o2ClassIndex = (((usqInt)(obj2)) >> (identityHashFullWordShift())) & (identityHashHalfWordMask()); + if ((o2ClassIndex != 0) + && ((classAtIndex(o2ClassIndex)) != obj2)) { + o2ClassIndex = 0; + } + + /* Refuse to do an in-place become on classes since their being + forwarded is used in the flush method cache implementations. + Refuse to do an in-place become on compiled code because + doing so breaks the unmapping of methods from cog methods. */ + if ((((/* begin numSlotsOf: */ + assert((classIndexOf(obj1)) > (isForwardedObjectClassIndexPun())), + (((numSlotsUsqInt = byteAt((void *)(obj1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) == ((/* begin numSlotsOf: */ + assert((classIndexOf(obj2)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(obj2 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj2 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))) + && (((o1ClassIndex + o2ClassIndex) == 0) + && ((!(isCompiledMethodFormat((((usqLong)(o1Header)) >> (formatShift())) & (formatMask())))) + && (!(isCompiledMethodFormat((((usqLong)(o2Header)) >> (formatShift())) & (formatMask()))))))) { + inPlaceBecomeandcopyHashFlag(obj1, obj2, copyHashFlag); + goto l6; + } + outOfPlaceBecomeandcopyHashFlag(obj1, obj2, copyHashFlag); + + /* if copyHashFlag then nothing changes, since hashes were also swapped. */ + if (copyHashFlag) { + goto l6; + } + + /* if copyHash is false then the classTable entries must be updated. + We leave the following until postBecomeScanClassTable:, but must + swap the forwarders if two active classes have been becommed, + and assign hashes if not. */ + if (o1ClassIndex) { + if (o2ClassIndex) { + classAtIndexput(o1ClassIndex, obj2); + classAtIndexput(o2ClassIndex, obj1); + } + else { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj1)); + newObj2 = longAt((void *)((obj1 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(newObj2 & (tagMask())))) + && ((!((longAt((void *)(newObj2))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + newObj2 = longAt((void *)((newObj2 + BaseHeaderSize) + (0U << (shiftForWord())))); + } + assert((rawHashBitsOf(newObj2)) == 0); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(newObj2 + 4),((((long32At((void *)(newObj2 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (o1ClassIndex & (identityHashHalfWordMask()))); + } + } + else { + if (o2ClassIndex) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj2)); + newObj1 = longAt((void *)((obj2 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(newObj1 & (tagMask())))) + && ((!((longAt((void *)(newObj1))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + newObj1 = longAt((void *)((newObj1 + BaseHeaderSize) + (0U << (shiftForWord())))); + } + assert((rawHashBitsOf(newObj1)) == 0); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(newObj1 + 4),((((long32At((void *)(newObj1 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (o2ClassIndex & (identityHashHalfWordMask()))); + } + } + /* end doBecome:and:copyHash: */ +l6: + objOop = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(i, array1, objOop); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((array2 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(i, array2, objOopSqInt); + } + } + } + } + else { + /* begin innerBecomeObjectsIn:to:copyHash: */ + toDoLimit = ((assert((classIndexOf(array1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(array1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + /* At first blush it would appear unnecessary to use followField: here since + the validation in become:with:twoWay:copyHash: follows forwarders. But + there's nothing to ensure all elements of each array is unique and doesn't + appear in the other array. So the enumeration could encounter an object + already becommed earlier in the same enumeration. */ + + /* begin followField:ofObject: */ + obj1 = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(obj1 & (tagMask())))) + && ((!((longAt((void *)(obj1))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + obj1 = fixFollowedFieldofObjectwithInitialValue(i, array1, obj1); + } + + /* begin followField:ofObject: */ + obj2 = longAt((void *)((array2 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(obj2 & (tagMask())))) + && ((!((longAt((void *)(obj2))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + obj2 = fixFollowedFieldofObjectwithInitialValue(i, array2, obj2); + } + if (obj1 != obj2) { + /* begin doBecome:to:copyHash: */ + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(obj1),((longAt((void *)(obj1))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(obj1),(longAt((void *)(obj1))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(obj1)); + assert(!(isOopForwarded(obj2))); + + /* begin isOldObject: */ + assert(isNonImmediate(obj1)); + if (oopisGreaterThanOrEqualTo(obj1, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj2 & (tagMask())))) + && (oopisLessThan(obj2, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj1 + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj1); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj1 + BaseHeaderSize) + (0U << (shiftForWord()))),obj2); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(obj1 + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(obj1 + (numSlotsFieldByteOffset())),1); + } + if (copyHashFlag) { + hash = (long32At((void *)(obj1 + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(obj2 + 4),((((long32At((void *)(obj2 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + } + if (((/* begin isOldObject: */ + assert(isNonImmediate(obj1)), + oopisGreaterThanOrEqualTo(obj1, GIV(oldSpaceStart)))) + && (/* isYoung: */ + ((!(obj2 & (tagMask())))) + && (oopisLessThan(obj2, GIV(oldSpaceStart))))) { + GIV(becomeEffectsFlags) = GIV(becomeEffectsFlags) | OldBecameNewFlag; + } + assert(!((isOopForwarded(obj2)))); + + /* begin followField:ofObject: */ + objOop = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(i, array1, objOop); + } + assert(!(isOopForwarded(obj2))); + } + } + } + + /* begin followSpecialObjectsOop */ + if ((!((longAt((void *)(GIV(specialObjectsOop)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + GIV(validatedIntegerClassFlags) = 0; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(GIV(specialObjectsOop))); + referent = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + GIV(specialObjectsOop) = referent; + } + followForwardedObjectFieldstoDepth(GIV(specialObjectsOop), 0); + + /* N.B. perform coInterpreter's postBecomeAction: *before* postBecomeScanClassTable: + to allow the coInterpreter to void method cache entries by spotting classIndices that + refer to forwarded objects. postBecomeScanClassTable: follows forwarders in the table. */ + + /* begin postBecomeAction: */ + spurPostBecomeAction(GIV(becomeEffectsFlags)); + if (((GIV(becomeEffectsFlags) & BecameJittedCompiledMethodFlag) != 0)) { + freeBecomeFlaggedMethods(); + } + followMovableLiteralsAndUpdateYoungReferrers(); + + /* begin nilUncoggableMethods */ + GIV(lastCoggableInterpretedBlockMethod) = (GIV(lastUncoggableInterpretedBlockMethod) = null); + assert(kosherYoungReferrers()); + GIV(gcMode) = 0; + postBecomeScanClassTable(GIV(becomeEffectsFlags)); + if (twoWayFlag) { + assert(validPostBecomeArrayContents(array1)); + assert(validPostBecomeArrayContents(array2)); + } + else { + assert(validPostBecomeArrayContents(array2)); + } + assert(validClassTableHashes()); + runLeakCheckerFor(GCModeBecome); + + /* begin zeroBecomeEffectFlagsOnSuccess */ + GIV(becomeEffectsFlags) = 0; + return PrimNoErr; +} + + +/* If this object is old, mark it as a root (because a new object + may be stored into it). */ + + /* SpurMemoryManager>>#beRootIfOld: */ +void +beRootIfOld(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin isOldObject: */ + assert(isNonImmediate(oop)); + if (oopisGreaterThanOrEqualTo(oop, GIV(oldSpaceStart))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(oop); + } + } +} + + /* SpurMemoryManager>>#bitsSetInFreeSpaceMaskForAllFreeLists */ +static sqInt +bitsSetInFreeSpaceMaskForAllFreeLists(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + if (((GIV(freeLists)[i]) != 0) + && ((!((1ULL << i) & GIV(freeListsMask))))) { + return 0; + } + } + return 1; +} + + /* SpurMemoryManager>>#bridgeSize */ +static sqInt +bridgeSize(void) +{ + return 2 * BaseHeaderSize; +} + + /* SpurMemoryManager>>#byteFormatForNumBytes: */ +static NoDbgRegParms sqInt +byteFormatForNumBytes(sqInt numBytes) +{ + return (firstByteFormat()) + ((8 - numBytes) & (BytesPerWord - 1)); +} + + /* SpurMemoryManager>>#byteFormatMask */ +static sqInt +byteFormatMask(void) +{ + return 24; +} + + /* SpurMemoryManager>>#byteSizeOf: */ +sqInt +byteSizeOf(sqInt oop) +{ + sqInt fmt; + usqInt numBytes; + usqInt numSlots; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin numBytesOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + return numBytes - (fmt & 7); + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + return numBytes; + } + if (fmt >= (firstShortFormat())) { + return numBytes - (((fmt & 3) << 1)); + } + + /* fmt >= self firstLongFormat */ + return numBytes - (((fmt & 1) << 2)); +} + + +/* Answer the basic element size for the receiver. Answer 0 for immediates + and CompiledCode + (element size could be wordSize for literals or 1 for bytes, so its + indeterminable). Answer + wordSize for pointer objects. Otherwise answer the actual element size of + a bits container. */ + + /* SpurMemoryManager>>#bytesPerElement: */ +unsigned char +bytesPerElement(sqInt oop) +{ + unsigned char byteSizes[16] = + { wordSize(), wordSize(), wordSize(), wordSize(), + wordSize(), wordSize(), wordSize(), 0, /* forwarder */ wordSize(), + 8, + 4, 4, + 2, 2, 2, 2 }; + sqInt fmt; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + return 0; + } + return 1; + } + return byteSizes[fmt]; +} + + +/* Attempt to change the class of the receiver to the argument given that the + format of the receiver matches the format of the argument. If successful, + answer 0, otherwise answer an error code indicating the reason for + failure. + Fail if the format of the receiver is incompatible with the format of the + argument, or if the argument is a fixed class and the receiver's size + differs from the size + that an instance of the argument should have. */ + + /* SpurMemoryManager>>#changeClassOf:to: */ +static NoDbgRegParms sqInt +changeClassOfto(sqInt rcvr, sqInt argClass) +{ + sqInt classFormat; + sqInt classIndex; + sqInt fixedFields; + sqInt fmt; + sqInt instBytes; + sqInt instFormat; + usqInt instSlots; + sqInt newFormat; + usqInt numBytes; + usqInt numSlots; + + instBytes = 0; + instSlots = 0; + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + return PrimErrNoModification; + } + classFormat = ((longAt((void *)((argClass + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + fixedFields = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + classFormat = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + instFormat = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + + /* Fail for inability to access classIndex before making contexts snapshot-safe. */ + if (((classIndex = ensureBehaviorHash(argClass))) < 0) { + return -classIndex; + } + + /* Now check the instance for compatibility and compute odd bits if necessary. */ + if (classFormat <= 5 /* lastPointerFormat */) { + if (instFormat > 5 /* lastPointerFormat */) { + return PrimErrInappropriate; + } + if ((((/* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? (instSlots = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8) + : (instSlots = numSlots)))) < fixedFields) + || ((instSlots > fixedFields) + && (/* isFixedSizePointerFormat: */ + (classFormat <= (nonIndexablePointerFormat())) + || (classFormat == (ephemeronFormat()))))) { + return PrimErrBadReceiver; + } + if ((instFormat == (indexablePointersFormat())) + && (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + makeContextSnapshotSafe(rcvr); + } + newFormat = classFormat; + } + else { + /* Fail if the class's format is somehow invalid. */ + if (classFormat != (classFormatFromInstFormat(classFormat))) { + return PrimErrBadArgument; + } + + /* begin numBytesOf: */ + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + instBytes = numBytes - (fmt & 7); + goto l1; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + instBytes = numBytes; + goto l1; + } + if (fmt >= (firstShortFormat())) { + instBytes = numBytes - (((fmt & 3) << 1)); + goto l1; + } + + /* fmt >= self firstLongFormat */ + instBytes = numBytes - (((fmt & 1) << 2)); + /* end numBytesOf: */ +l1: + switch (classFormat) { + case sixtyFourBitIndexableFormat(): + if (((instBytes & 7) != 0)) { + return PrimErrBadReceiver; + } + newFormat = classFormat; + break; + case firstLongFormat(): + if (((instBytes & 3) != 0)) { + return PrimErrBadReceiver; + } + newFormat = classFormat + ((2 - (((usqInt)(instBytes)) >> 2)) & 1); + break; + case firstShortFormat(): + if (((instBytes & 1) != 0)) { + return PrimErrBadReceiver; + } + newFormat = classFormat + ((4 - (((usqInt)(instBytes)) >> 1)) & 3); + break; + case firstByteFormat(): + newFormat = classFormat + ((8 - instBytes) & 7); + break; + case firstCompiledMethodFormat(): + if (classFormat != (firstCompiledMethodFormat())) { + return PrimErrInappropriate; + } + newFormat = instFormat; + break; + default: + return PrimErrInappropriate; + } + } + + /* begin set:classIndexTo:formatTo: */ + assert(((classIndex >= 0) && (classIndex <= (classIndexMask())))); + assert(((newFormat >= 0) && (newFormat <= (formatMask())))); + longAtput((void *)(rcvr),((longAt((void *)(rcvr))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + (classIndex + ((((usqInt)(newFormat) << (formatShift())))))); + assert((numBytesOf(rcvr)) == ((classFormat <= (lastPointerFormat()) + ? instSlots * BytesPerOop + : instBytes))); + + /* ok */ + return 0; +} + + /* SpurMemoryManager>>#characterObjectOf: */ +sqInt +characterObjectOf(sqInt characterCode) +{ + return ((((usqInt)(characterCode) << (numTagBits())))) + (characterTag()); +} + + +/* Immediate characters are unsigned */ + + /* SpurMemoryManager>>#characterValueOf: */ +usqInt +characterValueOf(sqInt oop) +{ + return ((((usqInt)oop))) >> (numTagBits()); +} + + /* SpurMemoryManager>>#cheapAddressCouldBeInHeap: */ +static NoDbgRegParms sqInt +cheapAddressCouldBeInHeap(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((address & (BytesPerWord - 1)) == 0) + && ((oopisGreaterThanOrEqualTo(address, GIV(newSpaceStart))) + && (oopisLessThan(address, GIV(endOfMemory)))); +} + + +/* Assumes zero-based array indexing. */ + + /* SpurMemoryManager>>#checkedLongAt: */ +sqInt +checkedLongAt(sqInt byteAddress) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!(isInMemory(byteAddress))) { + warning("checkedLongAt bad address"); + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + return longAt((void *)(byteAddress)); +} + + +/* Perform an integrity/leak check using the heapMap. Assume + clearLeakMapAndMapAccessibleFreeSpace has set a bit at each free chunk's + header. Scan all objects in the heap checking that no pointer points + to a free chunk and that all free chunks that refer to others refer to + marked chunks. Answer if all checks pass. */ + + /* SpurMemoryManager>>#checkHeapFreeSpaceIntegrity */ +static sqInt +checkHeapFreeSpaceIntegrity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt chunkBytes; + sqInt fi; + sqInt fieldOop; + sqInt followingWord; + usqInt followingWordAddress; + sqInt i; + usqInt numSlots; + sqInt objOop; + sqInt ok; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + sqInt toDoLimit; + sqInt toDoLimit1; + usqInt total; + + ok = 1; + total = 0; + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + if (GIV(freeLists)[i]) { + if (!(heapMapAtWord(pointerForOop(GIV(freeLists)[i])))) { + fprintf(GIV(transcript), + "leak in free list %d to non-free %p\n", + ((int) i), + ((void *)(GIV(freeLists)[i]))); + ok = 0; + } + } + } + + /* Excuse the duplication but performance is at a premium and we avoid + some tests by splitting the newSpace and oldSpace enumerations. */ + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l2; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + fprintf(GIV(transcript), + "young object %p is free\n", + ((void *)objOop)); + ok = 0; + } + else { + if (objOop != GIV(freeSpaceCheckOopToIgnore)) { + toDoLimit = (numPointerSlotsOf(objOop)) - 1; + for (fi = 0; fi <= toDoLimit; fi += 1) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + if ((!(fieldOop & (tagMask())))) { + if (heapMapAtWord(pointerForOop(fieldOop))) { + fprintf(GIV(transcript), + "object leak in %p @ %d = %p is free\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + /* end allNewSpaceEntitiesDo: */ +l2: + + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + if (!(heapMapAtWord(pointerForOop(objOop)))) { + fprintf(GIV(transcript), + "leak in free chunk %p is unmapped?! \n", + ((void *)objOop)); + ok = 0; + } + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) == 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ 0 = %p is unmapped\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + chunkBytes = bytesInBody(objOop); + + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + if (!(chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */))) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (1U << (shiftForWord())))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) == 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ 0 = %p is unmapped\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + if ((bytesInBody(objOop)) >= 0x200 /* (numFreeLists * #allocationUnit) */) { + for (fi = 2 /* freeChunkParentIndex */; fi <= 4 /* freeChunkLargerIndex */; fi += 1) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) == 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ %d = %p is unmapped\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + } + total += bytesInBody(objOop); + } + else { + if (objOop != GIV(freeSpaceCheckOopToIgnore)) { + toDoLimit1 = (numPointerSlotsOf(objOop)) - 1; + for (fi = 0; fi <= toDoLimit1; fi += 1) { + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + assert(fi == 0); + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + } + else { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + } + + /* We keep #fetchPointer:ofObject: API here for assertions */ + if ((!(fieldOop & (tagMask())))) { + if (heapMapAtWord(pointerForOop(fieldOop))) { + fprintf(GIV(transcript), + "object leak in %p @ %d = %p is free\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3:; + } + if (total != GIV(totalFreeOldSpace)) { + fprintf(GIV(transcript), + "incorrect totalFreeOldSpace; expected %d found %d\n", + ((int) GIV(totalFreeOldSpace)), + ((int) total)); + ok = 0; + } + return ok; +} + + +/* Perform an integrity/leak check using the heapMap. Assume + clearLeakMapAndMapAccessibleObjects has set a bit at each (non-free) + object's header. Scan all objects in the heap checking that every + pointer points to a header. Scan the rememberedSet, remapBuffer and + extraRootTable checking + that every entry is a pointer to a header. Check that the number of roots + is correct and that all + rememberedSet entries have their isRemembered: flag set. Answer if all + checks pass. */ + + /* SpurMemoryManager>>#checkHeapIntegrity:classIndicesShouldBeValid: */ +static NoDbgRegParms sqInt +checkHeapIntegrityclassIndicesShouldBeValid(sqInt excludeUnmarkedObjs, sqInt classIndicesShouldBeValid) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt chunkBytes; + sqInt classIndex; + sqInt classIndexSqInt; + sqInt classOop; + sqInt classTablePage; + sqInt containsYoung; + sqInt fi; + sqInt fieldIndex; + sqInt fieldOop; + sqInt followingWord; + usqInt followingWordAddress; + sqInt freeListOop; + sqInt i; + sqInt iSqInt; + sqInt numOnThisPage; + sqInt numRememberedObjectsInHeap; + usqInt numSlots; + sqInt obj; + sqInt objOop; + sqInt objStackPage; + sqInt ok; + sqInt prevObj; + sqInt prevPrevObj; + sqInt ri; + usqInt start; + sqInt toDoLimit; + sqInt toDoLimit1; + + ok = 1; + numRememberedObjectsInHeap = 0; + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + if ((freeListOop = GIV(freeLists)[i])) { + if (oopisGreaterThanOrEqualToandLessThan(freeListOop, GIV(oldSpaceStart), GIV(endOfMemory))) { + if (heapMapAtWord(pointerForOop(freeListOop))) { + fprintf(GIV(transcript), + "leak in free list %d to non-free %p\n", + ((int) i), + ((void *)freeListOop)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "leak in free list %d to out-of-bounds %p\n", + ((int) i), + ((void *)freeListOop)); + ok = 0; + } + } + } + + /* Excuse the duplication but performance is at a premium and we avoid + some tests by splitting the newSpace and oldSpace enumerations. */ + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l6; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + fprintf(GIV(transcript), + "young object %p is free\n", + ((void *)objOop)); + ok = 0; + } + else { + if (!((!(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) + && (excludeUnmarkedObjs))) { + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) { + fprintf(GIV(transcript), + "young object %p is remembered\n", + ((void *)objOop)); + ok = 0; + } + } + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isInHeapBounds: */ + (oopisGreaterThanOrEqualTo(fieldOop, (!(minCogMethodAddress()) + ? GIV(nilObj) + : minCogMethodAddress()))) + && (oopisLessThan(fieldOop, GIV(endOfMemory)))) { + if (!(heapMapAtWord(pointerForOop(fieldOop)))) { + fprintf(GIV(transcript), + "object leak in forwarder %p to unmapped %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "object leak in forwarder %p to out-of-bounds %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + else { + classIndexSqInt = (classIndex = (longAt((void *)(objOop))) & (classIndexMask())); + + /* begin classOrNilAtIndex: */ + assert((classIndexSqInt <= (tagMask())) + || (classIndexSqInt >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndexSqInt)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classOop = GIV(nilObj); + goto l1; + } + fieldIndex = classIndexSqInt & ((1U << (classTableMajorIndexShift())) - 1); + classOop = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classOrNilAtIndex: */ +l1: + if (classIndicesShouldBeValid + && ((classOop == GIV(nilObj)) + && (!(((longAt((void *)(objOop))) & (classIndexMask())) <= (lastClassIndexPun()))))) { + fprintf(GIV(transcript), + "object leak in %p invalid class index %p -> nilObj\n", + ((void *)objOop), + ((void *)classIndex)); + ok = 0; + } + toDoLimit = (numPointerSlotsOf(objOop)) - 1; + for (fi = 0; fi <= toDoLimit; fi += 1) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + if ((!(fieldOop & (tagMask())))) { + if (/* isInHeapBounds: */ + (oopisGreaterThanOrEqualTo(fieldOop, (!(minCogMethodAddress()) + ? GIV(nilObj) + : minCogMethodAddress()))) + && (oopisLessThan(fieldOop, GIV(endOfMemory)))) { + if (!(heapMapAtWord(pointerForOop(fieldOop)))) { + fprintf(GIV(transcript), + "object leak in %p @ %d = %p\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "object leak in %p to out-of-bounds %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l5; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l5:; + } + /* end allNewSpaceEntitiesDo: */ +l6: + + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + if (heapMapAtWord(pointerForOop(objOop))) { + fprintf(GIV(transcript), + "leak in free chunk %p is mapped?! \n", + ((void *)objOop)); + ok = 0; + } + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) != 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ 0 = %p is mapped\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + chunkBytes = bytesInBody(objOop); + + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + if (!(chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */))) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (1U << (shiftForWord())))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) != 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ 1 = %p is mapped\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + if ((bytesInBody(objOop)) >= 0x200 /* (numFreeLists * #allocationUnit) */) { + for (fi = 2 /* freeChunkParentIndex */; fi <= 4 /* freeChunkLargerIndex */; fi += 1) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) != 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ %d = %p is mapped\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + } + } + else { + if (!(excludeUnmarkedObjs + && (!(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)))) { + containsYoung = 0; + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) { + numRememberedObjectsInHeap += 1; + + /* begin isInRememberedSet: */ + for (iSqInt = 0; iSqInt < GIV(rememberedSetSize); iSqInt += 1) { + if ((GIV(rememberedSet)[iSqInt]) == objOop) { + goto l2; + } + } + fprintf(GIV(transcript), + "remembered object %p is not in remembered table\n", + ((void *)objOop)); + ok = 0; +l2:; + } + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isInHeapBounds: */ + (oopisGreaterThanOrEqualTo(fieldOop, (!(minCogMethodAddress()) + ? GIV(nilObj) + : minCogMethodAddress()))) + && (oopisLessThan(fieldOop, GIV(endOfMemory)))) { + if (!(heapMapAtWord(pointerForOop(fieldOop)))) { + fprintf(GIV(transcript), + "object leak in forwarder %p to unmapped %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "object leak in forwarder %p to out-of-bounds %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + if (/* isReallyYoung: */ + ((!(fieldOop & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(fieldOop)), + (oopisLessThan(fieldOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(fieldOop, GIV(newSpaceStart)))))) { + containsYoung = 1; + } + } + else { + classIndexSqInt = (classIndex = (longAt((void *)(objOop))) & (classIndexMask())); + + /* begin classOrNilAtIndex: */ + assert((classIndexSqInt <= (tagMask())) + || (classIndexSqInt >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndexSqInt)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classOop = GIV(nilObj); + goto l3; + } + fieldIndex = classIndexSqInt & ((1U << (classTableMajorIndexShift())) - 1); + classOop = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classOrNilAtIndex: */ +l3: + if (classIndicesShouldBeValid + && ((classOop == GIV(nilObj)) + && (classIndex > (lastClassIndexPun())))) { + fprintf(GIV(transcript), + "object leak in %p invalid class index %p -> nilObj\n", + ((void *)objOop), + ((void *)classIndex)); + ok = 0; + } + toDoLimit1 = (numPointerSlotsOf(objOop)) - 1; + for (fi = 0; fi <= toDoLimit1; fi += 1) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + if ((!(fieldOop & (tagMask())))) { + if (/* isInHeapBounds: */ + (oopisGreaterThanOrEqualTo(fieldOop, (!(minCogMethodAddress()) + ? GIV(nilObj) + : minCogMethodAddress()))) + && (oopisLessThan(fieldOop, GIV(endOfMemory)))) { + if (!(heapMapAtWord(pointerForOop(fieldOop)))) { + fprintf(GIV(transcript), + "object leak in %p @ %d = %p\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "object leak in %p to out-of-bounds %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + + /* don't be misled by CogMethods; they appear to be young, but they're not */ + if (/* isReallyYoung: */ + ((!(fieldOop & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(fieldOop)), + (oopisLessThan(fieldOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(fieldOop, GIV(newSpaceStart)))))) { + containsYoung = 1; + } + } + } + } + if (containsYoung) { + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + fprintf(GIV(transcript), + "unremembered object %p contains young oop(s)\n", + ((void *)objOop)); + ok = 0; + } + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l7; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l7:; + } + if (numRememberedObjectsInHeap != GIV(rememberedSetSize)) { + fprintf(GIV(transcript), + "root count mismatch. #heap roots %d; #roots %d\n", + ((int) numRememberedObjectsInHeap), + ((int) GIV(rememberedSetSize))); + + /* But the system copes with overflow... */ + } + + /* begin rememberedSetWithIndexDo: */ + for (iSqInt = 0; iSqInt < GIV(rememberedSetSize); iSqInt += 1) { + obj = GIV(rememberedSet)[iSqInt]; + if (obj & (BytesPerWord - 1)) { + fprintf(GIV(transcript), + "misaligned oop in remembered set @ %d = %p\n", + ((int) iSqInt), + ((void *)obj)); + ok = 0; + } + else { + if (heapMapAtWord(pointerForOop(obj))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + fprintf(GIV(transcript), + "non-root in remembered set @ %d = %p\n", + ((int) iSqInt), + ((void *)obj)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "object leak in remembered set @ %d = %p\n", + ((int) iSqInt), + ((void *)obj)); + ok = 0; + } + } + } + + /* begin objStack:do: */ + if (GIV(mournQueue) == GIV(nilObj)) { + goto l4; + } + eassert(isValidObjStack(GIV(mournQueue))); + objStackPage = GIV(mournQueue); + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + obj = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (obj & (BytesPerWord - 1)) { + fprintf(GIV(transcript), + "misaligned oop in mournQueue @ %d in %p = %p\n", + ((int) iSqInt), + ((void *)objStackPage), + ((void *)obj)); + ok = 0; + } + else { + if (!(excludeUnmarkedObjs + && (!(((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)))) { + if (!(heapMapAtWord(pointerForOop(obj)))) { + fprintf(GIV(transcript), + "object leak in mournQueue @ %d in %p = %p\n", + ((int) iSqInt), + ((void *)objStackPage), + ((void *)obj)); + ok = 0; + } + } + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l4: + for (ri = 1; ri <= GIV(remapBufferCount); ri += 1) { + obj = GIV(remapBuffer)[ri]; + if (obj & (BytesPerWord - 1)) { + fprintf(GIV(transcript), + "misaligned remapRoot @ %d = %p\n", + ((int) ri), + ((void *)obj)); + ok = 0; + } + else { + if (!(heapMapAtWord(pointerForOop(obj)))) { + fprintf(GIV(transcript), + "object leak in remapRoots @ %d = %p\n", + ((int) ri), + ((void *)obj)); + ok = 0; + } + } + } + for (ri = 1; ri <= GIV(extraRootCount); ri += 1) { + obj = (GIV(extraRoots)[ri])[0]; + if (obj & (BytesPerWord - 1)) { + fprintf(GIV(transcript), + "misaligned extraRoot @ %d => %p\n", + ((int) ri), + ((void *)obj)); + ok = 0; + } + else { + if (!(heapMapAtWord(pointerForOop(obj)))) { + fprintf(GIV(transcript), + "object leak in extraRoots @ %d => %p\n", + ((int) ri), + ((void *)obj)); + ok = 0; + } + } + } + return ok; +} + + +/* Verify that the given oop is legitimate. Check address, header, and size + but not class. + Answer true if OK. Otherwise print reason and answer false. */ +/* useful for debugging */ + + /* SpurMemoryManager>>#checkOkayOop: */ +sqInt +checkOkayOop(usqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt fmt; + usqLong unusedBits; + sqInt unusedBitsInYoungObjects; + + + /* address and size checks */ + if (((oop & (tagMask())) != 0)) { + return 1; + } + if (!(addressCouldBeObj(oop))) { + fprintf(GIV(transcript), + "oop %p is not a valid address", + ((void *)oop)); + return 0; + } + if (!(oopisLessThanOrEqualTo(addressAfter(oop), GIV(endOfMemory)))) { + fprintf(GIV(transcript), + "oop %p size would make it extend beyond the end of memory", + ((void *)oop)); + return 0; + } + + /* header type checks */ + if (!(((classIndex = (longAt((void *)(oop))) & (classIndexMask()))) >= (firstClassIndexPun()))) { + fprintf(GIV(transcript), + "oop %p is a free chunk, or bridge, not an object", + ((void *)oop)); + return 0; + } + if (((byteAt((void *)(oop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) + && ((byteAt((void *)((oop - BaseHeaderSize) + (numSlotsFieldByteOffset())))) != (numSlotsMask()))) { + fprintf(GIV(transcript), + "oop %p header has overflow header word, but overflow word does not have a saturated numSlots field", + ((void *)oop)); + return 0; + } + + /* format check */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if ((fmt == 6) || (fmt == 8)) { + fprintf(GIV(transcript), + "oop %p has an unknown format type", + ((void *)oop)); + return 0; + } + if ((fmt == (forwardedFormat())) != (classIndex == (isForwardedObjectClassIndexPun()))) { + fprintf(GIV(transcript), + "oop %p has mis-matched format/classIndex fields; only one of them is the isForwarded value", + ((void *)oop)); + return 0; + } + + /* specific header bit checks */ + unusedBits = (0x400000) | (0x40000000000000LL); + if ((long64At((void *)(oop))) & unusedBits) { + fprintf(GIV(transcript), + "oop %p has some unused header bits set; should be zero", + ((void *)oop)); + return 0; + } + unusedBitsInYoungObjects = (1U << (unusedBitShift())) | ((1U << (pinnedBitShift())) | (1U << (rememberedBitShift()))); + if ((longAt((void *)(oop))) & unusedBitsInYoungObjects) { + fprintf(GIV(transcript), + "oop %p has some header bits unused in young objects set; should be zero", + ((void *)oop)); + return 0; + } + return 1; +} + + +/* Attempt to verify that the given obj has a reasonable behavior. The class + must be a + valid, non-integer oop and must not be nilObj. It must be a pointers + object with three + or more fields. Finally, the instance specification field of the behavior + must match that + of the instance. If OK answer true. If not, print reason and answer false. */ +/* useful for debugging */ + + /* SpurMemoryManager>>#checkOopHasOkayClass: */ +sqInt +checkOopHasOkayClass(usqInt obj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + usqInt objClass; + sqInt objFormat; + + if (!(checkOkayOop(obj))) { + return 0; + } + objClass = ((usqInt) (fetchClassOfNonImm(obj))); + if (((objClass & (tagMask())) != 0)) { + fprintf(GIV(transcript), + "obj %p an immediate is not a valid class or behavior\n", + ((void *)obj)); + return 0; + } + if (!(okayOop(objClass))) { + fprintf(GIV(transcript), + "obj %p class obj is not ok\n", + ((void *)obj)); + return 0; + } + if (!((((byteAt((void *)(objClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(objClass)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(objClass + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objClass - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= 3))) { + fprintf(GIV(transcript), + "obj %p a class (behavior) must be a pointers object of size >= 3\n", + ((void *)obj)); + return 0; + } + objFormat = (/* isBytes: */ + ((!(obj & (tagMask())))) + && (((byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())) + ? ((((byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask())) | 7) - 7) + : (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask())); + if (((((usqInt)((((longAt((void *)((objClass + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) != objFormat) { + fprintf(GIV(transcript), + "obj %p and its class (behavior) formats differ\n", + ((void *)obj)); + return 0; + } + return 1; +} + + /* SpurMemoryManager>>#checkOopIntegrity:named: */ +static NoDbgRegParms sqInt +checkOopIntegritynamed(sqInt obj, char *name) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((oopisLessThan(obj, GIV(endOfMemory))) + && ((heapMapAtWord(pointerForOop(obj))) != 0)) { + return 1; + } + fprintf(GIV(transcript), + "%s leak %p\n", + name, + ((void *)obj)); + return 0; +} + + /* SpurMemoryManager>>#checkOopIntegrity:named:index: */ +static NoDbgRegParms sqInt +checkOopIntegritynamedindex(sqInt obj, char *name, sqInt i) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((oopisLessThan(obj, GIV(endOfMemory))) + && ((heapMapAtWord(pointerForOop(obj))) != 0)) { + return 1; + } + fprintf(GIV(transcript), + "%s leak @ %d = %p\n", + name, + ((int) i), + ((void *)obj)); + return 0; +} + + /* SpurMemoryManager>>#classAlien */ +sqInt +classAlien(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classArray */ +sqInt +classArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classAtIndex: */ +sqInt +classAtIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt fieldIndex; + + assert((classIndex >= 0) + && ((classIndex <= (tagMask())) + || ((classIndex >= (arrayClassIndexPun())) + && (classIndex <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return null; + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + +/* for become & GC of classes */ + + /* SpurMemoryManager>>#classAtIndex:put: */ +static NoDbgRegParms sqInt +classAtIndexput(sqInt classIndex, sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt fieldIndex; + + assert((classIndex <= (tagMask())) + || (classIndex >= (arrayClassIndexPun()))); + assert((objOop == GIV(nilObj)) + || ((((rawHashBitsOf(objOop)) == classIndex) + || ((classAtIndex(rawHashBitsOf(objOop))) == objOop)) + && (objCouldBeClassObj(objOop)))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + error("attempt to add class to empty page"); + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, classTablePage, objOop)); + assert(isNonImmediate(classTablePage)); + if (oopisGreaterThanOrEqualTo(classTablePage, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objOop & (tagMask())))) + && (oopisLessThan(objOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(classTablePage + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(classTablePage); + } + } + } + + /* most stores into young objects */ + return longAtput((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),objOop); +} + + /* SpurMemoryManager>>#classBitmap */ +sqInt +classBitmap(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassBitmap) << (shiftForWord())))))); +} + + +/* a.k.a. self fetchPointer: ClassByteArrayCompactIndex ofObject: + classTableFirstPage + */ + + /* SpurMemoryManager>>#classByteArray */ +sqInt +classByteArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteArray) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classCharacter */ +sqInt +classCharacter(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassCharacter) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classDoubleByteArray */ +sqInt +classDoubleByteArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOop; + int hash; + + /* begin accessIntegerClass:withValidationFlag: */ + hash = 0; + classOop = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassDoubleByteArray) << (shiftForWord())))))); + if (((GIV(validatedIntegerClassFlags) & ValidatedClassDoubleByteArrayFlag) != 0)) { + return classOop; + } + if ((((classOop & (tagMask())) != 0)) + || ((((hash = (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask()))) == 0) + || ((classAtIndex(hash)) != classOop))) { + return GIV(nilObj); + } + GIV(validatedIntegerClassFlags) = GIV(validatedIntegerClassFlags) | ValidatedClassDoubleByteArrayFlag; + return classOop; +} + + /* SpurMemoryManager>>#classDoubleWordArray */ +sqInt +classDoubleWordArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOop; + int hash; + + /* begin accessIntegerClass:withValidationFlag: */ + hash = 0; + classOop = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassDoubleWordArray) << (shiftForWord())))))); + if (((GIV(validatedIntegerClassFlags) & ValidatedClassDoubleWordArrayFlag) != 0)) { + return classOop; + } + if ((((classOop & (tagMask())) != 0)) + || ((((hash = (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask()))) == 0) + || ((classAtIndex(hash)) != classOop))) { + return GIV(nilObj); + } + GIV(validatedIntegerClassFlags) = GIV(validatedIntegerClassFlags) | ValidatedClassDoubleWordArrayFlag; + return classOop; +} + + /* SpurMemoryManager>>#classExternalAddress */ +sqInt +classExternalAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassExternalAddress) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classExternalData */ +sqInt +classExternalData(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassExternalData) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classExternalFunction */ +sqInt +classExternalFunction(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassExternalFunction) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classExternalLibrary */ +sqInt +classExternalLibrary(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassExternalLibrary) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classExternalStructure */ +sqInt +classExternalStructure(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassExternalStructure) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classFloat */ +sqInt +classFloat(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassFloat) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classFloat32Array */ +sqInt +classFloat32Array(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(nilObj); +} + + /* SpurMemoryManager>>#classFloat64Array */ +sqInt +classFloat64Array(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(nilObj); +} + + +/* No need to check this; classAtIndex: has a stricter assert: + self assert: classIndex ~= self isForwardedObjectClassIndexPun. */ + + /* SpurMemoryManager>>#classForClassTag: */ +static NoDbgRegParms sqInt +classForClassTag(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt fieldIndex; + + /* begin classAtIndex: */ + assert((classIndex >= 0) + && ((classIndex <= (tagMask())) + || ((classIndex >= (arrayClassIndexPun())) + && (classIndex <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return null; + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + +/* 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState + et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + + /* SpurMemoryManager>>#classFormatFromInstFormat: */ +static NoDbgRegParms sqInt +classFormatFromInstFormat(sqInt instFormat) +{ + if (instFormat >= (firstByteFormat())) { + return instFormat & -8; + } + + /* this is likely the common case */ + if (instFormat <= (sixtyFourBitIndexableFormat())) { + return instFormat; + } + if (instFormat < (firstShortFormat())) { + return instFormat & -2; + } + return instFormat & -4; +} + + +/* 22-bit class mask => ~ 4M classes */ + + /* SpurMemoryManager>>#classIndexFieldWidth */ +static sqInt +classIndexFieldWidth(void) +{ + return 22; +} + + +/* Answer objOop's classIndex from the object header. + A note on performance: + This routine uses longAt:, which does a 32-bit load on the 32-bit system, + and a 64-bit load + on the 64-bit system. Since the only 64-bit implementation(s) is/are + little-endian, and + since all of UNUSED,isPinned,isRemembered,format,isImmutable and + classIndex fit within + the least significant 32-bits we could use long32At: to access these, in + the hope that the + 32-bit access will be quicker on 64-bits by virtue of fetching half the + bits. But experiments + show that doing this does not produce any increase; at least any signal is + lost in the noise. */ + + /* SpurMemoryManager>>#classIndexOf: */ +sqInt +classIndexOf(sqInt objOop) +{ + return (longAt((void *)(objOop))) & (classIndexMask()); +} + + /* SpurMemoryManager>>#classLargeNegativeInteger */ +sqInt +classLargeNegativeInteger(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin knownClassAtIndex: */ + assert(((ClassLargeNegativeIntegerCompactIndex >= 1) && (ClassLargeNegativeIntegerCompactIndex <= (classTablePageSize())))); + return longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassLargeNegativeIntegerCompactIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classLargePositiveInteger */ +sqInt +classLargePositiveInteger(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin knownClassAtIndex: */ + assert(((ClassLargePositiveIntegerCompactIndex >= 1) && (ClassLargePositiveIntegerCompactIndex <= (classTablePageSize())))); + return longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassLargePositiveIntegerCompactIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classOrNilAtIndex: */ +sqInt +classOrNilAtIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt fieldIndex; + + assert((classIndex <= (tagMask())) + || (classIndex >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return GIV(nilObj); + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classPoint */ +sqInt +classPoint(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassPoint) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classSemaphore */ +sqInt +classSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classSmallInteger */ +sqInt +classSmallInteger(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSmallInteger) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classString */ +sqInt +classString(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord())))))); +} + + +/* 1024 entries per page (2^10); 22 bit classIndex implies 2^12 pages */ +/* self basicNew classTableMinorIndexMask */ + + /* SpurMemoryManager>>#classTableMinorIndexMask */ +sqInt +classTableMinorIndexMask(void) +{ + return (1U << (classTableMajorIndexShift())) - 1; +} + + +/* 1024 entries per page (2^10); 22 bit classIndex implies 2^12 pages */ +/* self basicNew classTablePageSize */ + + /* SpurMemoryManager>>#classTablePageSize */ +sqInt +classTablePageSize(void) +{ + return 1U << (classTableMajorIndexShift()); +} + + +/* For Cogit & bootstrap */ + + /* SpurMemoryManager>>#classTableRootObj */ +sqInt +classTableRootObj(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(hiddenRootsObj); +} + + +/* Answer the number of slots for class table pages in the hidden root + object. + */ + + /* SpurMemoryManager>>#classTableRootSlots */ +static sqInt +classTableRootSlots(void) +{ + return 1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift())); +} + + +/* Answer the classObj's identityHash to use as a tag in the first-level + method lookup cache. + */ + + /* SpurMemoryManager>>#classTagForClass: */ +sqInt +classTagForClass(sqInt classObj) +{ + sqInt err; + int hash; + + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + return ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); +} + + /* SpurMemoryManager>>#classUnsafeAlien */ +sqInt +classUnsafeAlien(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassUnsafeAlien) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classWordArray */ +sqInt +classWordArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOop; + int hash; + + /* begin accessIntegerClass:withValidationFlag: */ + hash = 0; + classOop = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassWordArray) << (shiftForWord())))))); + if (((GIV(validatedIntegerClassFlags) & ValidatedClassWordArrayFlag) != 0)) { + return classOop; + } + if ((((classOop & (tagMask())) != 0)) + || ((((hash = (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask()))) == 0) + || ((classAtIndex(hash)) != classOop))) { + return GIV(nilObj); + } + GIV(validatedIntegerClassFlags) = GIV(validatedIntegerClassFlags) | ValidatedClassWordArrayFlag; + return classOop; +} + + +/* Perform an integrity/leak check using the heapMap. Set a bit at each free + chunk's header. */ + + /* SpurMemoryManager>>#clearLeakMapAndMapAccessibleFreeSpace */ +static void +clearLeakMapAndMapAccessibleFreeSpace(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + + clearHeapMap(); + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + heapMapAtWordPut(pointerForOop(objOopSqInt), 1); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } +} + + /* SpurMemoryManager>>#cloneInOldSpace:forPinning: */ +static NoDbgRegParms sqInt +cloneInOldSpaceforPinning(sqInt objOop, sqInt forPinning) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cogMethodOrMethodHeader; + sqInt fmt; + int hash; + sqInt hasYoung; + sqInt i; + sqInt newObj; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt rawHeader; + sqInt referent; + sqInt valuePointer; + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (forPinning) { + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), fmt, (longAt((void *)(objOop))) & (classIndexMask())); + } + else { + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), fmt, (longAt((void *)(objOop))) & (classIndexMask())); + } + if (!newObj) { + return 0; + } + if (fmt <= 5 /* lastPointerFormat */) { + hasYoung = 0; + for (i = 0; i < numSlots; i += 1) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + } + if (((!(oop & (tagMask())))) + && ((/* begin isYoungObject: */ + assert(isNonImmediate(oop)), + oopisLessThan(oop, GIV(oldSpaceStart))))) { + hasYoung = 1; + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(i, newObj, oop)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),oop); + } + if (hasYoung) { + remember(newObj); + } + } + else { + for (i = 0; i < numSlots; i += 1) { + valuePointer = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(i, newObj, valuePointer)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + if (fmt >= (firstCompiledMethodFormat())) { + /* begin maybeFixClonedCompiledMethod: */ + assert(isOopCompiledMethod(newObj)); + rawHeader = longAt((void *)((newObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + cogMethodOrMethodHeader = ((((CogMethod *) rawHeader))->methodHeader); + + /* begin rawHeaderOf:put: */ + assert(isCompiledMethodHeader(headerWhileForwardingOf(newObj))); + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(HeaderIndex, newObj, cogMethodOrMethodHeader)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))),cogMethodOrMethodHeader); + } + if (((/* begin isYoungObject: */ + assert(isNonImmediate(objOop)), + oopisLessThan(objOop, GIV(oldSpaceStart)))) + || (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0)) { + remember(newObj); + } + } + } + if ((hash = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()))) { + /* begin setHashBitsOf:to: */ + long32Atput((void *)(newObj + 4),((((long32At((void *)(newObj + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(objOop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* begin setIsImmutableOf:to: */ + longAtput((void *)(newObj),(longAt((void *)(newObj))) | (1U << (immutableBitShift()))); + } + return newObj; +} + + /* SpurMemoryManager>>#cloneObject: */ +sqInt +cloneObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt cogMethodOrMethodHeader; + sqInt fmt; + sqInt hasYoung; + sqInt i; + sqInt newObj; + usqInt newObjUsqInt; + usqInt numBytes; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt rawHeader; + sqInt referent; + sqInt valuePointer; + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (numSlots > ((1U << (fixedFieldsFieldWidth())) - 1)) { + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + + /* begin allocateSlotsInOldSpace:format:classIndex: */ + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), fmt, classIndex); + } + else { + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if (((numSlots) >> 56) > 0) { + newObj = null; + goto l1; + } + newObjUsqInt = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObjUsqInt = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, fmt, classIndex); + goto l1; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObjUsqInt),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(fmt) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObjUsqInt),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(fmt) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObjUsqInt % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newObj = newObjUsqInt; + /* end allocateSlots:format:classIndex: */ +l1:; + } + if (newObj) { + if (fmt <= 5 /* lastPointerFormat */) { + hasYoung = 0; + for (i = 0; i < numSlots; i += 1) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(oop & (tagMask())))) { + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + } + if (((!(oop & (tagMask())))) + && ((/* begin isYoungObject: */ + assert(isNonImmediate(oop)), + oopisLessThan(oop, GIV(oldSpaceStart))))) { + hasYoung = 1; + } + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(i, newObj, oop)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),oop); + } + if (hasYoung + && (!((/* begin isYoungObject: */ + assert(isNonImmediate(newObj)), + oopisLessThan(newObj, GIV(oldSpaceStart)))))) { + remember(newObj); + } + } + else { + for (i = 0; i < numSlots; i += 1) { + valuePointer = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(i, newObj, valuePointer)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + if (fmt >= (firstCompiledMethodFormat())) { + /* begin maybeFixClonedCompiledMethod: */ + assert(isOopCompiledMethod(newObj)); + rawHeader = longAt((void *)((newObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + cogMethodOrMethodHeader = ((((CogMethod *) rawHeader))->methodHeader); + + /* begin rawHeaderOf:put: */ + assert(isCompiledMethodHeader(headerWhileForwardingOf(newObj))); + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(HeaderIndex, newObj, cogMethodOrMethodHeader)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))),cogMethodOrMethodHeader); + } + if (((/* begin isOldObject: */ + assert(isNonImmediate(newObj)), + oopisGreaterThanOrEqualTo(newObj, GIV(oldSpaceStart)))) + && (((/* begin isYoungObject: */ + assert(isNonImmediate(objOop)), + oopisLessThan(objOop, GIV(oldSpaceStart)))) + || (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0))) { + remember(newObj); + } + } + } + } + return newObj; +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. + Copy objOop into the segment beginning at segAddr, and forward it to the + copy, saving its first field in savedFirstField, and setting its marked + bit to indicate it has + been copied. If it is a class in the class table, set the copy's hash to 0 + for reassignment + on load, and mark it as a class by setting its isRemembered bit. + Answer the next segmentAddr if successful. Answer an appropriate error + code if not */ +/* Copy the object... */ + + /* SpurMemoryManager>>#copyObj:toAddr:stopAt:savedFirstFields:index: */ +static NoDbgRegParms sqInt +copyObjtoAddrstopAtsavedFirstFieldsindex(sqInt objOop, sqInt segAddr, sqInt endSeg, sqInt savedFirstFields, sqInt i) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bodySize; + sqInt contextSize; + sqInt copy; + sqInt fmt; + int hash; + sqInt header; + sqInt headerSqInt; + sqInt iSqInt; + sqInt methodHeader; + usqInt numLiterals; + usqInt numMediatedSlots; + usqInt numSlots; + sqInt oop; + sqInt sp; + sqInt toDoLimit; + sqInt valuePointer; + + assert(!((isCopiedIntoSegment(objOop)))); + bodySize = bytesInBody(objOop); + if (oopisGreaterThanOrEqualTo(segAddr + bodySize, endSeg)) { + return PrimErrWritePastObject; + } + memcpy(((void *)segAddr), ((void *)(/* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop))), bodySize); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(segAddr + (numSlotsFieldByteOffset()))); + copy = (numSlots == (numSlotsMask()) + ? segAddr + BaseHeaderSize + : segAddr); + + /* Clear remembered, mark bits of all headers copied into the segment (except classes) */ + setIsRememberedOfto(copy, 0); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(copy))); + byteAtput((void *)(copy + (markBitsByteOffset())),(byteAt((void *)(copy + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + + /* Make any objects with hidden dynamic state (contexts, methods) look like normal objects. */ + + /* begin ifAProxy:updateCopy: */ + if (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + if (/* isStillMarriedContext: */ + (((((longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(objOop)))) { + /* begin numSlotsOfMarriedContext: */ + contextSize = stackPointerIndexForFrame(frameOfMarriedContext(objOop)); + assert((CtxtTempFrameStart + contextSize) <= (numSlotsOf(objOop))); + numMediatedSlots = CtxtTempFrameStart + contextSize; + for (iSqInt = 0; iSqInt < numMediatedSlots; iSqInt += 1) { + oop = fetchPointerofMarriedContext(iSqInt, objOop); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(copy)) + && (!(isForwarded(copy)))); + assert(validStorePointerUncheckedArgs(iSqInt, copy, oop)); + longAtput((void *)((copy + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord()))))),oop); + } + } + else { + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + numMediatedSlots = CtxtTempFrameStart + contextSize; + goto l2; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numMediatedSlots = (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l2; + } + if (fmt == (forwardedFormat())) { + numMediatedSlots = 1; + goto l2; + } + if (fmt < (firstCompiledMethodFormat())) { + numMediatedSlots = 0; + goto l2; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numMediatedSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l2:; + } + + /* And make sure to nil the slots beyond the top of stack... */ + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (iSqInt = numMediatedSlots; iSqInt <= toDoLimit; iSqInt += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(copy)) + && (!(isForwarded(copy)))); + assert(validStorePointerUncheckedArgs(iSqInt, copy, GIV(nilObj))); + longAtput((void *)((copy + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord()))))),GIV(nilObj)); + } + } + if (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + methodHeader = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(copy)) + && (!(isForwarded(copy)))); + assert(validStorePointerUncheckedArgs(HeaderIndex, copy, ((((CogMethod *) methodHeader))->methodHeader))); + longAtput((void *)((copy + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))),((((CogMethod *) methodHeader))->methodHeader)); + } + } + + /* If the object is a class, zero its identityHash (which is its classIndex) and set its + isRemembered bit. It will be assigned a new hash and entered into the table on load. */ + hash = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()); + if ((hash > (lastClassIndexPun())) + && ((classOrNilAtIndex(hash)) == objOop)) { + /* begin setHashBitsOf:to: */ + long32Atput((void *)(copy + 4),((((long32At((void *)(copy + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (0)); + setIsRememberedOfto(copy, 1); + } + + /* Now forward the object to its copy in the segment. */ + valuePointer = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(savedFirstFields)) + && (!(isForwarded(savedFirstFields)))); + assert(validStorePointerUncheckedArgs(i, savedFirstFields, valuePointer)); + longAtput((void *)((savedFirstFields + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(0, objOop, copy)); + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),copy); + + /* begin markAsCopiedIntoSegment: */ + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* Answer the new end of segment */ + return segAddr + bodySize; +} + + +/* print the count of marked and unmarked objects. + In addition if 1 is set in printFlags, short-print marked objects, + and/or if 2 is set, short-print unmarked obejcts. */ +/* useful for debugging */ + + /* SpurMemoryManager>>#countMarkedAndUnmarkdObjects: */ +void +countMarkedAndUnmarkdObjects(sqInt printFlags) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt nm; + sqInt nu; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + nm = (nu = 0); + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + nm += 1; + if (((printFlags & 1) != 0)) { + shortPrintOop(obj); + } + } + else { + nu += 1; + if (((printFlags & 2) != 0)) { + shortPrintOop(obj); + } + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "n marked: "); + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)nm)); + cr(); + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "n unmarked: "); + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)nu)); + cr(); +} + + +/* Compute the current allocated bytes since last set. + This is the cumulative total in statAllocatedBytes plus the allocation + since the last scavenge. */ +/* Slang infers the type of the difference between two unsigned variables as + signed. In this case we want it to be unsigned. */ + + /* SpurMemoryManager>>#currentAllocatedBytes */ +static usqLong +currentAllocatedBytes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt use; + + use = ((/* begin totalOldSpaceCapacity */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + return (GIV(statAllocatedBytes) + (GIV(freeStart) - (((GIV(eden)).start)))) + (use - GIV(oldSpaceUsePriorToScavenge)); +} + + +/* The inner shell for scavenge, abstrascted out so globalGarbageCollect can + use it. + */ + + /* SpurMemoryManager>>#doScavenge: */ +static NoDbgRegParms void +doScavenge(sqInt tenuringCriterion) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + float fractionSurvived; + sqInt previousFSStart; + SpurNewSpaceSpace temp; + usqInt use; + + /* begin doAllocationAccountingForScavenge */ + /* begin currentAllocatedBytes */ + use = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + GIV(statAllocatedBytes) = (GIV(statAllocatedBytes) + (GIV(freeStart) - (((GIV(eden)).start)))) + (use - GIV(oldSpaceUsePriorToScavenge)); + GIV(gcPhaseInProgress) = ScavengeInProgress; + + /* begin scavenge: */ + GIV(statSurvivorCount) = 0; + GIV(tenureCriterion) = tenuringCriterion; + + /* begin logStartScavenge */ + (GIV(scavengeLogRecord).sEdenBytes = GIV(freeStart) - ((GIV(eden).start))); + (GIV(scavengeLogRecord).sPastBytes = GIV(pastSpaceStart) - ((GIV(pastSpace).start))); + (GIV(scavengeLogRecord).sRememberedSetSize = GIV(rememberedSetSize)); + (GIV(scavengeLogRecord).sRememberedSetRedZone = GIV(rememberedSetRedZone)); + (GIV(scavengeLogRecord).sRememberedSetLimit = GIV(rememberedSetLimit)); + (GIV(scavengeLogRecord).sStatTenures = GIV(statTenures)); + + /* begin strategizeToLimitRememberedTable */ + if ((GIV(tenureCriterion) == TenureByAge) + && (GIV(rememberedSetSize) >= GIV(rememberedSetRedZone))) { + GIV(tenureCriterion) = TenureToShrinkRT; + computeRefCountToShrinkRT(); + } + + /* begin logTenuringPolicy */ + (GIV(scavengeLogRecord).tTenureCriterion = GIV(tenureCriterion)); + (GIV(scavengeLogRecord).tTenureThreshold = ((GIV(tenureCriterion) == TenureByAge) + && (GIV(tenureThreshold) > ((GIV(pastSpace).start))) + ? GIV(tenureThreshold) - ((GIV(pastSpace).start)) + : 0)); + (GIV(scavengeLogRecord).tRefCountToShrinkRT = GIV(refCountToShrinkRT)); + ((void) (scavengeLoop())); + processWeaklings(); + + /* begin computeTenuringThreshold */ + fractionSurvived = (((GIV(futureSpace).limit)) == ((GIV(futureSpace).start)) + ? 0.0 + : (((double) (GIV(futureSurvivorStart) - ((GIV(futureSpace).start))) )) / (((GIV(futureSpace).limit)) - ((GIV(futureSpace).start)))); + GIV(tenureThreshold) = (fractionSurvived > 0.9 + ? (round(((((GIV(pastSpace).limit)) - ((GIV(pastSpace).start))) * (1.0 - GIV(tenuringProportion))))) + ((GIV(pastSpace).start)) + : 0); + + /* begin exchangeSurvivorSpaces */ + temp = GIV(pastSpace); + GIV(pastSpace) = GIV(futureSpace); + GIV(futureSpace) = temp; + + /* begin logEndScavenge */ + (GIV(scavengeLogRecord).eSurvivorBytes = GIV(futureSurvivorStart) - ((GIV(pastSpace).start))); + (GIV(scavengeLogRecord).eRememberedSetSize = GIV(rememberedSetSize)); + (GIV(scavengeLogRecord).eStatTenures = GIV(statTenures)); + + /* begin initFutureSpaceStart */ + previousFSStart = GIV(futureSurvivorStart); + GIV(futureSurvivorStart) = (GIV(futureSpace).start); + GIV(pastSpaceStart) = previousFSStart; + assert(oopisGreaterThanOrEqualToandLessThanOrEqualTo(GIV(pastSpaceStart), ((GIV(pastSpace)).start), ((GIV(pastSpace)).limit))); + GIV(freeStart) = ((GIV(eden)).start); + GIV(gcPhaseInProgress) = 0; + + /* begin resetAllocationAccountingAfterGC */ + GIV(oldSpaceUsePriorToScavenge) = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); +} + + +/* Reduce the number of indexable fields in objOop, a pointer object, to + nSlots. If in oldSpace, + convert the unused residual to a free chunk. If in eden, cut back + freeStart if possible. + Without changes to numSlotsForShortening:toIndexableSize: this only works + for arrayFormat, longFormat, and on 64-bits sixtyFourBitIndexableFormat, + objects. Answer the number of bytes returned to free memory, which may be + zero if no change + was possible. */ + + /* SpurMemoryManager>>#doShorten:toIndexableSize: */ +static NoDbgRegParms sqInt +doShortentoIndexableSize(sqInt objOop, sqInt indexableSize) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytesBefore; + sqInt classIndex; + sqInt copy; + sqInt delta; + usqInt followingAddress; + sqInt format; + sqInt formatSqInt; + sqInt freeChunk; + sqInt i; + sqInt instFormat; + usqInt newObj; + usqInt numBytes; + sqInt numSlots; + sqInt valuePointer; + + /* for assert checking */ + followingAddress = addressAfter(objOop); + assert(oopisLessThanOrEqualTo(followingAddress, GIV(endOfMemory))); + + /* numSlotsForShortening:toIndexableSize: */ + switch ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) { + case arrayFormat(): + case sixtyFourBitIndexableFormat(): + numSlots = indexableSize; + break; + case firstLongFormat(): + case (firstLongFormat()) + 1: + numSlots = ((indexableSize * 4) + (BytesPerWord - 1)) / BytesPerWord; + break; + default: + assertf("invalid format for shortening"); + numSlots = 0; + } + bytesBefore = bytesInBody(objOop); + delta = bytesBefore - (/* objectBytesForSlots: */ + (numSlots + ? ((((usqInt)(numSlots) << (shiftForWord())))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize)); + + /* Since the system rounds objects up to 64-bits, losing a 32-bit + slot may not actually change the bytes occupied by the object. */ + if (!delta) { + if ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) { + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(objOop - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56))) + numSlots); + } + else { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),numSlots); + } + + /* begin updateFormatOfShortenedObject:to: */ + if (((((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) <= ((firstLongFormat()) + 1)))) { + formatSqInt = (firstLongFormat()) + (indexableSize & 1); + + /* begin setFormatOf:to: */ + assert(((formatSqInt >= 0) && (formatSqInt <= (formatMask())))); + byteAtput((void *)(objOop + (formatFieldByteOffset())),((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + formatSqInt); + } + return 0; + } + + /* Currently we can't have one word gaps in oldSpace but we can in newSpace. So only create + a copy and forward if objOop is in old space. If in newSpace we'll create a slimbridge below. */ + if ((delta <= 8 /* allocationUnit */) + && ((/* begin isOldObject: */ + assert(isNonImmediate(objOop)), + oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))))) { + /* begin normalisedFormatFor:indexableSize: */ + instFormat = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (instFormat >= (firstByteFormat())) { + formatSqInt = instFormat & -8; + goto l1; + } + + /* this is likely the common case */ + if (instFormat <= (sixtyFourBitIndexableFormat())) { + formatSqInt = instFormat; + goto l1; + } + if (instFormat < (firstShortFormat())) { + formatSqInt = instFormat & -2; + goto l1; + } + formatSqInt = instFormat & -4; + /* end classFormatFromInstFormat: */ +l1: + format = ((formatSqInt == (firstLongFormat())) + && (((indexableSize & 1) != 0)) + ? formatSqInt + 1 + : formatSqInt); + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if ((((usqInt)(numSlots)) >> 56) > 0) { + copy = null; + goto l2; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + copy = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, format, classIndex); + goto l2; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(format) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(format) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + copy = newObj; + /* end allocateSlots:format:classIndex: */ +l2: + if (!copy) { + error("shorten:toIndexableSize: attempted to shorten by allocationUnit and failed to allocate space for copy!"); + } + if (/* isPureBitsFormat: */ + (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat()))) { + memcpy(firstIndexableField(copy), firstIndexableField(objOop), numBytesOfBitsformat(copy, format)); + } + else { + for (i = 0; i < numSlots; i += 1) { + valuePointer = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(copy)) + && (!(isForwarded(copy)))); + assert(validStorePointerUncheckedArgs(i, copy, valuePointer)); + longAtput((void *)((copy + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + } + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) { + remember(copy); + } + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(objOop),(longAt((void *)(objOop))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(objOop)); + assert(!(isOopForwarded(copy))); + + /* begin isOldObject: */ + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(copy & (tagMask())))) + && (oopisLessThan(copy, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),copy); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),1); + } + return 0; + } + if ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) { + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(objOop - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56))) + numSlots); + + /* Setting an overflow slot count to 0 or 1 in newSpace creates a slimbridge. + So we must also change the normal slot count so that the object no + longer has the overflow header word, which has become the slimbridge. */ + if (numSlots <= 1) { + if (oopisLessThan(objOop, GIV(oldSpaceStart))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),numSlots); + hackSlimBridgeToat(objOop, objOop - 8 /* allocationUnit */); + } + } + } + else { + assert(numSlots < (numSlotsMask())); + + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),numSlots); + } + + /* {self addressAfter: objOop. delta. (self addressAfter: objOop) + delta. followingAddress} collect: #hex */ + delta = followingAddress - (addressAfter(objOop)); + assert((delta >= (allocationUnit())) + && ((delta % (allocationUnit())) == 0)); + if (delta == 8 /* allocationUnit */) { + assert(!((isOldObject(objOop)))); + delta = 0; + if (followingAddress == GIV(freeStart)) { + GIV(freeStart) = addressAfter(objOop); + } + else { + hackSlimBridgeToat(followingAddress, addressAfter(objOop)); + } + } + else { + freeChunk = initFreeChunkWithBytesat(delta, addressAfter(objOop)); + assert((objectAfter(objOop)) == freeChunk); + assert((addressAfter(freeChunk)) == followingAddress); + if (oopisGreaterThanOrEqualToandLessThan(objOop, GIV(oldSpaceStart), GIV(endOfMemory))) { + GIV(totalFreeOldSpace) += delta; + addToFreeListbytes(freeChunk, delta); + } + else { + delta = 0; + + /* begin setClassIndexOf:to: */ + assert((((sixtyFourBitLongsClassIndexPun()) >= 0) && ((sixtyFourBitLongsClassIndexPun()) <= (classIndexMask())))); + longAtput((void *)(freeChunk),((longAt((void *)(freeChunk))) & (~(usqIntptr_t)(classIndexMask()))) + (sixtyFourBitLongsClassIndexPun())); + + /* begin setFormatOf:to: */ + assert((((firstLongFormat()) >= 0) && ((firstLongFormat()) <= (formatMask())))); + byteAtput((void *)(freeChunk + (formatFieldByteOffset())),((byteAt((void *)(freeChunk + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (firstLongFormat())); + } + } + + /* begin normalisedFormatFor:indexableSize: */ + instFormat = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (instFormat >= (firstByteFormat())) { + formatSqInt = instFormat & -8; + goto l3; + } + + /* this is likely the common case */ + if (instFormat <= (sixtyFourBitIndexableFormat())) { + formatSqInt = instFormat; + goto l3; + } + if (instFormat < (firstShortFormat())) { + formatSqInt = instFormat & -2; + goto l3; + } + formatSqInt = instFormat & -4; + /* end classFormatFromInstFormat: */ +l3: + formatSqInt = ((formatSqInt == (firstLongFormat())) + && (((indexableSize & 1) != 0)) + ? formatSqInt + 1 + : formatSqInt); + + /* begin setFormatOf:to: */ + assert(((formatSqInt >= 0) && (formatSqInt <= (formatMask())))); + byteAtput((void *)(objOop + (formatFieldByteOffset())),((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + formatSqInt); + return delta; +} + + +/* Instantiate an instance of a compact class. ee stands for execution engine + and implies that this allocation will *NOT* cause a GC. N.B. the + instantiated object + IS NOT FILLED and must be completed before returning it to Smalltalk. + Since this + call is used in routines that do just that we are safe. Break this rule + and die in GC. + Result is guaranteed to be young. */ + + /* SpurMemoryManager>>#eeInstantiateClassIndex:format:numSlots: */ +usqInt +eeInstantiateClassIndexformatnumSlots(sqInt knownClassIndex, sqInt objFormat, sqInt numSlots) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newObj; + usqInt numBytes; + + assert((numSlots >= 0) + && ((knownClassIndex != 0) + && ((knownClassAtIndex(knownClassIndex)) != GIV(nilObj)))); + assert(((objFormat < (firstByteFormat()) + ? objFormat + : objFormat & (byteFormatMask()))) == (instSpecOfClass(knownClassAtIndex(knownClassIndex)))); + + /* begin allocateNewSpaceSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if (numSlots > 0xFFFFFFFFU) { + return null; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateNewSpaceSlots:format:classIndex:"); + return 0; + } + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),numSlots); + longAtput((void *)(GIV(freeStart) + 4),((sqInt)((usqInt)((numSlotsMask())) << (numSlotsHalfShift())))); + long64Atput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + knownClassIndex); + } + else { + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + knownClassIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + return newObj; +} + + +/* For breakpointing the end of a leak check print message */ + + /* SpurMemoryManager>>#eek */ +NeverInline void +eek(void) +{ +} + + +/* We use bridges to stitch segments together to make it appear that the heap + is one contiguous space. + Bridges at the end of oldSpace segments are maintained. Bridges at the end + of pastSpace and eden + are temporary, and are established here, depending on the current sizes of + pastSpace end eden. + + N.B. this introduces complications. Either or both pastSpace and eden may + be empty, so the + bridge from pastSpace may skip eden. pastSpace may be full, so there may + be no bridge at + the end of pastSpace. Most difficult, pastSpace could be one 64-bit word + short of full, but normal + bridges are two word objects. To make this work we introduce a hack, + objectAfterMaybeSlimBridge:limit:, which uses a fake overflow slot count + to get to the start of the next object, which is either one or two + words away, depending on whether the first object in eden has a normal or + an overflow header. */ + + /* SpurMemoryManager>>#enableObjectEnumerationFrom: */ +static NoDbgRegParms void +enableObjectEnumerationFrom(sqInt initialObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (oopisLessThan(initialObject, GIV(oldSpaceStart))) { + if (GIV(freeStart) > (((GIV(eden)).start))) { + /* begin bridgeEdenAndOldSpace */ + initSegmentBridgeWithBytesat(GIV(oldSpaceStart) - GIV(freeStart), GIV(freeStart)); + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + } + else { + /* begin bridgePastSpaceAndOldSpace */ + initSegmentBridgeWithBytesat(GIV(oldSpaceStart) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } +} + + /* SpurMemoryManager>>#ensureBehaviorHash: */ +static NoDbgRegParms sqInt +ensureBehaviorHash(sqInt aBehavior) +{ + sqInt err; + int hash; + + assert(addressCouldBeClassObj(aBehavior)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + return ((hash = (long32At((void *)(aBehavior + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(aBehavior) + ? ((err = enterIntoClassTable(aBehavior)) + ? -err + : (long32At((void *)(aBehavior + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); +} + + +/* An obj stack is a stack of objects stored in a hidden root slot, such as + the markStack or the ephemeronQueue. It is a linked list of segments, + with the hot end at the head of the list. It is a word object. The stack + pointer is in ObjStackTopx and 0 means empty. The list goes through + ObjStackNextx. We don't want to shrink objStacks, since they're used + in GC and its good to keep their memory around. So unused pages + created by popping emptying pages are kept on the ObjStackFreex list. */ + + /* SpurMemoryManager>>#ensureRoomOnObjStackAt: */ +static NoDbgRegParms sqInt +ensureRoomOnObjStackAt(sqInt objStackRootIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt freeOrNewPage; + sqInt stackOrNil; + + stackOrNil = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord())))))); + if ((stackOrNil == GIV(nilObj)) + || ((longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) >= ObjStackLimit)) { + freeOrNewPage = (stackOrNil == GIV(nilObj) + ? 0 + : longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))))); + if (freeOrNewPage) { + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(stackOrNil)) == (wordIndexableFormat())); + longAtput((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),0); + assert((!GIV(marking)) + || (isMarked(freeOrNewPage))); + } + else { + freeOrNewPage = allocateSlotsInOldSpacebytesformatclassIndex(ObjStackPageSlots, ((((usqInt)(ObjStackPageSlots) << (shiftForWord())))) + (BaseHeaderSize + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (!freeOrNewPage) { + /* Allocate a new segment and retry. This is very uncommon. But it happened to me (Clement). */ + growOldSpaceByAtLeast(ObjStackPageSlots * BytesPerOop); + freeOrNewPage = allocateSlotsInOldSpacebytesformatclassIndex(ObjStackPageSlots, ((((usqInt)(ObjStackPageSlots) << (shiftForWord())))) + (BaseHeaderSize + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (!freeOrNewPage) { + error("no memory to allocate or extend obj stack"); + } + } + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(freeOrNewPage)) == (wordIndexableFormat())); + longAtput((void *)((freeOrNewPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),0); + if (GIV(marking)) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(freeOrNewPage))); + byteAtput((void *)(freeOrNewPage + (markBitsByteOffset())),(byteAt((void *)(freeOrNewPage + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + } + } + + /* the free page list is always on the new page. */ + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(freeOrNewPage)) == (wordIndexableFormat())); + longAtput((void *)((freeOrNewPage + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord()))))),objStackRootIndex); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(freeOrNewPage)) == (wordIndexableFormat())); + longAtput((void *)((freeOrNewPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord()))))),(stackOrNil == GIV(nilObj) + ? 0 + : stackOrNil)); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(freeOrNewPage)) == (wordIndexableFormat())); + longAtput((void *)((freeOrNewPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))),0); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(objStackRootIndex, GIV(hiddenRootsObj), freeOrNewPage)); + assert(isNonImmediate(GIV(hiddenRootsObj))); + if (oopisGreaterThanOrEqualTo(GIV(hiddenRootsObj), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(freeOrNewPage & (tagMask())))) + && (oopisLessThan(freeOrNewPage, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(hiddenRootsObj) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(hiddenRootsObj)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord()))))),freeOrNewPage); + assert(isValidObjStackAt(objStackRootIndex)); + + /* Added a new page; now update and answer the relevant cached first page. */ + + /* begin updateRootOfObjStackAt:with: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(objStackRootIndex, GIV(hiddenRootsObj), freeOrNewPage)); + assert(isNonImmediate(GIV(hiddenRootsObj))); + if (oopisGreaterThanOrEqualTo(GIV(hiddenRootsObj), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(freeOrNewPage & (tagMask())))) + && (oopisLessThan(freeOrNewPage, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(hiddenRootsObj) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(hiddenRootsObj)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord()))))),freeOrNewPage); + switch (objStackRootIndex) { + case MarkStackRootIndex: + GIV(markStack) = freeOrNewPage; + break; + case WeaklingStackRootIndex: + GIV(weaklingStack) = freeOrNewPage; + break; + case MournQueueRootIndex: + GIV(mournQueue) = freeOrNewPage; + break; + default: + error("Case not found and no otherwise clause"); + } + stackOrNil = freeOrNewPage; + } + assert(isValidObjStackAt(objStackRootIndex)); + return stackOrNil; +} + + +/* Enter aBehavior into the class table and answer 0. Otherwise answer a + primitive failure code. */ + + /* SpurMemoryManager>>#enterIntoClassTable: */ +static NoDbgRegParms sqInt +enterIntoClassTable(sqInt aBehavior) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt initialMajorIndex; + sqInt majorIndex; + sqInt minorIndex; + usqInt p; + sqInt page; + sqInt toDoLimit; + usqInt toDoLimitUsqInt; + + majorIndex = ((usqInt)(GIV(classTableIndex))) >> (classTableMajorIndexShift()); + initialMajorIndex = majorIndex; + + /* classTableIndex should never index the first page; it's reserved for known classes */ + assert(initialMajorIndex > 0); + minorIndex = GIV(classTableIndex) & ((1U << (classTableMajorIndexShift())) - 1); + while (1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(majorIndex) << (shiftForWord())))))); + if (page == GIV(nilObj)) { + /* begin allocateSlotsInOldSpace:format:classIndex: */ + page = allocateSlotsInOldSpacebytesformatclassIndex(1U << (classTableMajorIndexShift()), /* objectBytesForSlots: */ + (1U << (classTableMajorIndexShift()) + ? ((((usqInt)((1U << (classTableMajorIndexShift()))) << (shiftForWord())))) + (((1U << (classTableMajorIndexShift())) >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), arrayFormat(), arrayClassIndexPun()); + if (!page) { + return PrimErrNoMemory; + } + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((page + BaseHeaderSize) + ((1U << (classTableMajorIndexShift())) * BytesPerOop)) - 1, addressAfter(page))); + toDoLimitUsqInt = ((usqInt)(((page + BaseHeaderSize) + ((1U << (classTableMajorIndexShift())) * BytesPerOop)) - 1)); + for (p = (((usqInt)(page + BaseHeaderSize))); p <= toDoLimitUsqInt; p += 8 /* allocationUnit */) { + longAtput((void *)(p),GIV(nilObj)); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(majorIndex, GIV(hiddenRootsObj), page)); + assert(isNonImmediate(GIV(hiddenRootsObj))); + if (oopisGreaterThanOrEqualTo(GIV(hiddenRootsObj), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(page & (tagMask())))) + && (oopisLessThan(page, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(hiddenRootsObj) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(hiddenRootsObj)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(majorIndex) << (shiftForWord()))))),page); + GIV(numClassTablePages) += 1; + minorIndex = 0; + } + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (i = minorIndex; i <= toDoLimit; i += 1) { + if ((longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))))) == GIV(nilObj)) { + GIV(classTableIndex) = ((((usqInt)(majorIndex) << (classTableMajorIndexShift())))) + i; + + /* classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + assert(GIV(classTableIndex) >= (1U << (classTableMajorIndexShift()))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(i, page, aBehavior)); + assert(isNonImmediate(page)); + if (oopisGreaterThanOrEqualTo(page, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(aBehavior & (tagMask())))) + && (oopisLessThan(aBehavior, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(page + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(page); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((page + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),aBehavior); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(aBehavior + 4),((((long32At((void *)(aBehavior + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (GIV(classTableIndex) & (identityHashHalfWordMask()))); + assert((classAtIndex(rawHashBitsOf(aBehavior))) == aBehavior); + return 0; + } + } + minorIndex = 0; + + /* classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + majorIndex = ((((majorIndex + 1) & ((1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift()))) - 1)) < 1) ? 1 : ((majorIndex + 1) & ((1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift()))) - 1))); + if (majorIndex == initialMajorIndex) { + return PrimErrLimitExceeded; + } + } + return 0; +} + + /* SpurMemoryManager>>#existInstancesInNewSpaceOf: */ +static NoDbgRegParms sqInt +existInstancesInNewSpaceOf(sqInt classObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + + /* begin allNewSpaceObjectsDo: */ + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l2; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOopSqInt, GIV(freeStart))) { + assert(isBridgeOrEnumerableObjectNoAssert(objOopSqInt)); + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == classIndex) { + return 1; + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOopSqInt = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + /* end allNewSpaceEntitiesDo: */ +l2: + return 0; +} + + +/* Remove aBehavior from the class table. */ + + /* SpurMemoryManager>>#expungeFromClassTable: */ +static NoDbgRegParms void +expungeFromClassTable(sqInt aBehavior) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int classIndex; + sqInt classTablePage; + sqInt majorIndex; + int minorIndex; + + assert(isInClassTable(aBehavior)); + classIndex = (long32At((void *)(aBehavior + 4))) & (identityHashHalfWordMask()); + majorIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + minorIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(majorIndex) << (shiftForWord())))))); + assert(classTablePage != GIV(classTableFirstPage)); + assert((numSlotsOf(classTablePage)) == (classTablePageSize())); + assert((fetchPointerofObject(minorIndex, classTablePage)) == aBehavior); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(classTablePage)) + && (!(isForwarded(classTablePage)))); + assert(validStorePointerUncheckedArgs(minorIndex, classTablePage, GIV(nilObj))); + longAtput((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(minorIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* If the removed class is before the classTableIndex, set the + classTableIndex to point to the empty slot so as to reuse it asap. */ + if (classIndex < GIV(classTableIndex)) { + GIV(classTableIndex) = classIndex; + } + + /* classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + assert(GIV(classTableIndex) >= (1U << (classTableMajorIndexShift()))); +} + + /* SpurMemoryManager>>#falseObject */ +sqInt +falseObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(falseObj); +} + + /* SpurMemoryManager>>#fetchByte:ofObject: */ +sqInt +fetchByteofObject(sqInt byteIndex, sqInt objOop) +{ + return byteAt((void *)((objOop + BaseHeaderSize) + byteIndex)); +} + + /* SpurMemoryManager>>#fetchClassOfNonImm: */ +static NoDbgRegParms NeverInline sqInt +fetchClassOfNonImm(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt classTablePage; + sqInt fieldIndex; + + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + if (classIndex <= (classIsItselfClassIndexPun())) { + if (classIndex == (classIsItselfClassIndexPun())) { + return objOop; + } + + /* Answer nil to avoid the assert failure in classOrNilAtIndex: */ + if (classIndex == (isForwardedObjectClassIndexPun())) { + return GIV(nilObj); + } + } + assert(classIndex >= (arrayClassIndexPun())); + + /* begin classOrNilAtIndex: */ + assert((classIndex <= (tagMask())) + || (classIndex >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return GIV(nilObj); + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#fetchClassOf: */ +sqInt +fetchClassOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt tagBits; + + return ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); +} + + +/* In Spur an object's classIndex is the tag in all method caches. */ + + /* SpurMemoryManager>>#fetchClassTagOfNonImm: */ +static NoDbgRegParms sqInt +fetchClassTagOfNonImm(sqInt obj) +{ + return (longAt((void *)(obj))) & (classIndexMask()); +} + + /* SpurMemoryManager>>#fetchInteger:ofObject: */ +sqLong +fetchIntegerofObject(sqInt fieldIndex, sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt oop; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + return ((((oop) & 7) == 1) + ? (oop >> 3) + : (GIV(primFailCode) = PrimErrInappropriate)); + } + return (fmt < (firstShortFormat()) + ? (fmt < (firstLongFormat()) + ? long64At((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 3))))) + : long32At((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2)))))) + : (fmt < (firstCompiledMethodFormat()) + ? (fmt < (firstByteFormat()) + ? byteAt((void *)((objOop + BaseHeaderSize) + fieldIndex)) + : shortAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 1)))))) + : (GIV(primFailCode) = PrimErrUnsupported))); +} + + +/* index by 32-bit units, and return a 32-bit value. Intended to replace + fetchWord:ofObject: + */ + + /* SpurMemoryManager>>#fetchLong32:ofObject: */ +int +fetchLong32ofObject(sqInt fieldIndex, sqInt oop) +{ + return long32At((void *)((oop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2))))); +} + + /* SpurMemoryManager>>#fetchPointer:ofFreeChunk: */ +static NoDbgRegParms sqInt +fetchPointerofFreeChunk(sqInt fieldIndex, sqInt objOop) +{ + return longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#fetchPointer:ofObject: */ +sqInt +fetchPointerofObject(sqInt fieldIndex, sqInt objOop) +{ + return longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#findClassNamed: */ +sqInt +findClassNamed(char *aString) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOrNil; + sqInt i; + sqInt ignored; + sqInt j; + sqInt page; + sqInt toDoLimit; + + /* begin classTableEntriesDo: */ + for (i = 0; i < GIV(numClassTablePages); i += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNil = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNil != GIV(nilObj)) { + ignored = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + if (classNameOfIs(classOrNil, aString)) { + return classOrNil; + } + } + } + } + return null; +} + + +/* Answer, but do not remove, the largest free chunk in the free lists. */ + + /* SpurMemoryManager>>#findLargestFreeChunk */ +static sqInt +findLargestFreeChunk(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt childNode; + sqInt treeNode; + + childNode = 0; + treeNode = GIV(freeLists)[0]; + if (!treeNode) { + return null; + } + while (1) { + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(treeNode)); + assert((bytesInBody(treeNode)) >= ((numFreeLists()) * (allocationUnit()))); + childNode = longAt((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + if (!(childNode != 0)) break; + treeNode = childNode; + } + return treeNode; +} + + +/* Print the oops of all string-like things that start with the same + characters as aCString + */ +/* useful for debugging */ + + /* SpurMemoryManager>>#findStringBeginningWith: */ +void +findStringBeginningWith(char *aCString) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aCStringStrlen; + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + aCStringStrlen = strlen(aCString); + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (/* object:beginsWithString:ofSize: */ + (/* isBytes: */ + ((!(objSqInt & (tagMask())))) + && (((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((!(((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((numBytesOfBytes(objSqInt)) >= aCStringStrlen) + && ((strncmp(aCString, firstIndexableField(objSqInt), aCStringStrlen)) == 0)))) { + printHex(objSqInt); + + /* begin space */ + printChar(' '); + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)(lengthOf(objSqInt)))); + printChar(' '); + printOopShortInner(objSqInt); + cr(); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + +/* Print the oops of all string-like things that have the same characters as + aCString + */ + + /* SpurMemoryManager>>#findString: */ +void +findString(char *aCString) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aCStringStrlen; + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + aCStringStrlen = strlen(aCString); + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (/* object:equalsString:ofSize: */ + (/* isBytes: */ + ((!(objSqInt & (tagMask())))) + && (((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((!(((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((numBytesOfBytes(objSqInt)) == aCStringStrlen) + && ((strncmp(aCString, firstIndexableField(objSqInt), aCStringStrlen)) == 0)))) { + printHex(objSqInt); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + cr(); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + /* SpurMemoryManager>>#fireEphemeron: */ +static NoDbgRegParms void +fireEphemeron(sqInt ephemeron) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + + assert(isNonImmediate(ephemeron)); + format = (byteAt((void *)(ephemeron + (formatFieldByteOffset())))) & (formatMask()); + if (format == (ephemeronFormat())) { + queueMourner(ephemeron); + + /* begin setEphemeronToFired: */ + assert((isNonImmediate(ephemeron)) + && (isEphemeron(ephemeron))); + + /* begin setFormatOf:to: */ + assert((((nonIndexablePointerFormat()) >= 0) && ((nonIndexablePointerFormat()) <= (formatMask())))); + byteAtput((void *)(ephemeron + (formatFieldByteOffset())),((byteAt((void *)(ephemeron + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (nonIndexablePointerFormat())); + + /* begin signalFinalization: */ + forceInterruptCheck(); + GIV(pendingFinalizationSignals) += 1; + } + else { + assert((format == (nonIndexablePointerFormat())) + && (isonObjStack(ephemeron, GIV(mournQueue)))); + } +} + + /* SpurMemoryManager>>#firstAccessibleObject */ +static sqInt +firstAccessibleObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt savedTenuringThreshold; + + assert(GIV(nilObj) == GIV(oldSpaceStart)); + + /* flush newSpace to settle the enumeration. */ + + /* begin flushNewSpace */ + savedTenuringThreshold = GIV(tenureThreshold); + GIV(tenureThreshold) = GIV(oldSpaceStart); + scavengingGCTenuringIf(TenureByAge); + + /* begin setRawTenuringThreshold: */ + GIV(tenureThreshold) = savedTenuringThreshold; + assert((GIV(rememberedSetSize)) == 0); + assert(GIV(pastSpaceStart) == (((GIV(pastSpace)).start))); + assert(GIV(freeStart) == (((GIV(eden)).start))); + return GIV(nilObj); +} + + +/* for the message send breakpoint; selectors can be immediates. */ + + /* SpurMemoryManager>>#firstFixedFieldOfMaybeImmediate: */ +static NoDbgRegParms void * +firstFixedFieldOfMaybeImmediate(sqInt oop) +{ + return (((oop & (tagMask())) != 0) + ? ((void *)oop) + : pointerForOop(oop + BaseHeaderSize)); +} + + /* SpurMemoryManager>>#firstFixedField: */ +void * +firstFixedField(sqInt objOop) +{ + return pointerForOop(objOop + BaseHeaderSize); +} + + +/* NOTE: overridden in various simulator subclasses to add coercion to + CArray, so please duplicate any changes. + There are only two important cases, both for objects with named inst vars, + i.e. formats 2,3 & 5. + The first indexable field for formats 2 & 5 is the slot count (by + convention, even though that's off the end + of the object). For 3 we must go to the class. */ + + /* SpurMemoryManager>>#firstIndexableField: */ +void * +firstIndexableField(sqInt objOop) +{ + sqInt classFormat; + sqInt classPointer; + sqInt fmt; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= (weakArrayFormat())) { + if (fmt == (arrayFormat())) { + return pointerForOop(objOop + BaseHeaderSize); + } + + /* array starts at 0. */ + if (fmt >= (indexablePointersFormat())) { + classPointer = fetchClassOfNonImm(objOop); + + /* begin formatOfClass: */ + classFormat = ((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + return pointerForOop((objOop + BaseHeaderSize) + ((((usqInt)((classFormat & ((1U << (fixedFieldsFieldWidth())) - 1))) << (shiftForWord()))))); + } + + /* indexable with inst vars; need to delve into the class format word + otherwise not indexable */ + return 0; + } + + /* All bit objects, and indeed CompiledMethod, though this is a no-no, start at 0 */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat()))) { + return pointerForOop(objOop + BaseHeaderSize); + } + + /* otherwise not indexable */ + return 0; +} + + /* SpurMemoryManager>>#fixedFieldsOfClassFormatMask */ +sqInt +fixedFieldsOfClassFormatMask(void) +{ + return (1U << (fixedFieldsFieldWidth())) - 1; +} + + /* SpurMemoryManager>>#fixedFieldsOfClassFormat: */ +sqInt +fixedFieldsOfClassFormat(sqInt classFormat) +{ + return classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); +} + + /* SpurMemoryManager>>#fixedFieldsOf:format:length: */ +static NoDbgRegParms sqInt +fixedFieldsOfformatlength(sqInt objOop, sqInt fmt, sqInt wordLength) +{ + sqInt class; + + + /* N.B. written to fall through to fetchClassOfNonImm: et al for forwarders + so as to trigger an assert fail. */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + return 0; + } + if (fmt < (arrayFormat())) { + return wordLength; + } + class = fetchClassOfNonImm(objOop); + return (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); +} + + +/* Private helper for followField:ofObject: to avoid code duplication for + rare case. + */ + + /* SpurMemoryManager>>#fixFollowedField:ofObject:withInitialValue: */ +static NoDbgRegParms NeverInline sqInt +fixFollowedFieldofObjectwithInitialValue(sqInt fieldIndex, sqInt anObject, sqInt initialValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + + assert(isOopForwarded(initialValue)); + + /* inlined followForwarded: for speed (one less test) */ + objOop = initialValue; + do { + objOop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + } while(/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, anObject, objOop)); + assert(isNonImmediate(anObject)); + if (oopisGreaterThanOrEqualTo(anObject, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objOop & (tagMask())))) + && (oopisLessThan(objOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(anObject + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(anObject); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((anObject + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),objOop); + return objOop; +} + + +/* Answer the object the ephemeron guards. This is its first element. */ + + /* SpurMemoryManager>>#followedKeyOfEphemeron: */ +static NoDbgRegParms sqInt +followedKeyOfEphemeron(sqInt objOop) +{ + sqInt oop; + + assert((isNonImmediate(objOop)) + && (isEphemeron(objOop))); + + /* begin followOopField:ofObject: */ + oop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + oop = fixFollowedFieldofObjectwithInitialValue(0, objOop, oop); + } + return oop; +} + + +/* Make sure the oop at fieldIndex in anObject is not forwarded (follow the + forwarder there-in if so). Answer the (possibly followed) oop at + fieldIndex. */ + + /* SpurMemoryManager>>#followField:ofObject: */ +static NoDbgRegParms sqInt +followFieldofObject(sqInt fieldIndex, sqInt anObject) +{ + sqInt objOop; + + objOop = longAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(fieldIndex, anObject, objOop); + } + return objOop; +} + + +/* Follow a forwarding pointer. This must be a loop because we cannot prevent + forwarders to + forwarders being created by lazy become. Consider the following example by + Igor Stasenko: + array := { a. b. c }. + - array at: 1 points to &a. array at: 2 points to &b. array at: 3 points + to &c + a becomeForward: b + - array at: 1 still points to &a. array at: 2 still points to &b. array + at: 3 still points to &c + b becomeForward: c. + - array at: 1 still points to &a. array at: 2 still points to &b. array + at: 3 still points to &c + - when accessing array first one has to follow a forwarding chain: + &a -> &b -> c */ + + /* SpurMemoryManager>>#followForwarded: */ +sqInt +followForwarded(sqInt objOop) +{ + sqInt referent; + + assert(isUnambiguouslyForwarder(objOop)); + referent = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + return referent; +} + + /* SpurMemoryManager>>#followMaybeForwarded: */ +static NoDbgRegParms sqInt +followMaybeForwarded(sqInt objOop) +{ + return (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + ? followForwarded(objOop) + : objOop); +} + + +/* Make sure the obj at fieldIndex in anObject is not forwarded (follow the + forwarder there-in if so). Answer the (possibly followed) obj at + fieldIndex. */ + + /* SpurMemoryManager>>#followObjField:ofObject: */ +static NoDbgRegParms sqInt +followObjFieldofObject(sqInt fieldIndex, sqInt anObject) +{ + sqInt objOop; + + objOop = longAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(fieldIndex, anObject, objOop); + } + return objOop; +} + + /* SpurMemoryManager>>#formatOfClass: */ +sqInt +formatOfClass(sqInt classPointer) +{ + return ((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); +} + + +/* 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState + et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable (lsb = # of unused 32-bit fields, 11 unused in 32 + bits) 12 - 15 16-bit indexable (ls2b = # of unused 16-bit fields, 14 & 15 + unused in 32-bits) + 16 - 23 byte indexable (ls3b = # of unused 8-bit fields, 20, 21, 22, 23 + unused in 32-bits) + 24 - 31 compiled method (ls3b = # of unused 8-bit fields, 20, 21, 22, 23 + unused in 32-bits) */ +/* A note on performance. Since the format field is, by design, aligned on a + byte boundary + in the fourth byte of the header (see headerForSlots:format:classIndex:) + it is accessed + as below. But it used to be accessed via + ^(self longAt: objOop) >> self formatShift bitAnd: self formatMask + This potentially involves more operations (a shift) and larger literals, + In practice clang + optimizes the long form to the byte form on x86_64, showing it is to be + preferred on at + least one architecture. On arm64 for example both forms take two + instructions. */ + + /* SpurMemoryManager>>#formatOf: */ +static NoDbgRegParms sqInt +formatOf(sqInt objOop) +{ + return (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); +} + + +/* Answer if anObject is itself forwarded, or is a pointer object containing + any references to forwarded objects. + */ + + /* SpurMemoryManager>>#forwardersIn: */ +static NoDbgRegParms sqInt +forwardersIn(sqInt anObject) +{ + sqInt i; + sqInt oop; + sqInt toDoLimit; + + if ((!((longAt((void *)(anObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + return 1; + } + toDoLimit = (numPointerSlotsOf(anObject)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + oop = longAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + return 1; + } + } + return 0; +} + + +/* for linking objecs on each free list, or, during pigCompact, doubly- + linking the free objects in address order using the xor link hack. */ + + /* SpurMemoryManager>>#freeChunkNextIndex */ +static sqInt +freeChunkNextIndex(void) +{ + return 0; +} + + +/* for organizing the tree of large free chunks. */ + + /* SpurMemoryManager>>#freeChunkParentIndex */ +static sqInt +freeChunkParentIndex(void) +{ + return 2; +} + + +/* For linking objecs on each free list, doubly-linking the free objects. + Free chunks of size 1 do not have a prev index. */ + + /* SpurMemoryManager>>#freeChunkPrevIndex */ +static sqInt +freeChunkPrevIndex(void) +{ + return 1; +} + + /* SpurMemoryManager>>#freeChunkWithBytes:at: */ +static NoDbgRegParms sqInt +freeChunkWithBytesat(sqInt bytes, sqInt address) +{ + sqInt freeChunk; + + assert(isInOldSpace(address)); + assert((segmentContainingObj(address)) == (segmentContainingObj(address + bytes))); + freeChunk = initFreeChunkWithBytesat(bytes, address); + addToFreeListbytes(freeChunk, bytes); + assert(freeChunk == (objectStartingAt(address))); + return freeChunk; +} + + +/* Free an object in oldSpace. Coalesce if possible to reduce fragmentation. */ + + /* SpurMemoryManager>>#freeObject: */ +sqInt +freeObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytes; + usqInt chunkBytes; + sqInt index; + sqInt next; + usqInt numSlots; + usqInt start; + + assert(isInOldSpace(objOop)); + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) { + /* begin forgetObject: */ + assert(GIV(rememberedSetSize) > 0); + assert(isRemembered(objOop)); + setIsRememberedOfto(objOop, 0); + if (!(objOop == (GIV(rememberedSet)[GIV(rememberedSetSize) - 1]))) { + index = 0; + while (index < GIV(rememberedSetSize)) { + if (objOop == (GIV(rememberedSet)[index])) { + GIV(rememberedSet)[index] = (GIV(rememberedSet)[GIV(rememberedSetSize) - 1]); + index = GIV(rememberedSetSize); + } + else { + index += 1; + } + } + } + GIV(rememberedSetSize) -= 1; + assert(GIV(rememberedSetSize) >= 0); + } + bytes = bytesInBody(objOop); + start = /* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)((start + bytes) + (numSlotsFieldByteOffset()))); + next = (numSlots == (numSlotsMask()) + ? (start + bytes) + BaseHeaderSize + : start + bytes); + if (((longAt((void *)(next))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + /* begin detachFreeObject: */ + chunkBytes = bytesInBody(next); + GIV(totalFreeOldSpace) -= chunkBytes; + unlinkFreeChunkchunkBytes(next, chunkBytes); + bytes += bytesInBody(next); + } + GIV(totalFreeOldSpace) += bytes; + return freeChunkWithBytesat(bytes, start); +} + + +/* Perform a full eager compacting GC. Answer the size of the largest free + chunk. + */ +/* for profiling */ + + /* SpurMemoryManager>>#fullGC */ +NeverInline usqLong +fullGC(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + usqLong compactionStartUsecs; + usqLong gcMarkEndUsecs; + sqInt i; + sqInt index; + usqInt numSlots; + sqInt obj; + sqInt objOop; + + GIV(needGCFlag) = 0; + GIV(gcStartUsecs) = ioUTCMicrosecondsNow(); + GIV(statMarkCount) = 0; + preGCAction(GCModeFull); + + /* begin globalGarbageCollect */ + assert(validObjStacks()); + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + + /* Mark objects /before/ scavenging, to empty the rememberedTable of unmarked roots. */ + markObjects(1); + gcMarkEndUsecs = ioUTCMicrosecondsNow(); + + /* begin forgetUnmarkedRememberedObjects */ + index = 0; + while (index < GIV(rememberedSetSize)) { + obj = GIV(rememberedSet)[index]; + if ((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + index += 1; + } + else { + setIsRememberedOfto(obj, 0); + GIV(rememberedSetSize) -= 1; + GIV(rememberedSet)[index] = (GIV(rememberedSet)[GIV(rememberedSetSize)]); + } + } + assert(GIV(rememberedSetSize) >= 0); + + /* begin setGCMode: */ + GIV(gcMode) = GCModeNewSpace; + doScavenge(MarkOnTenure); + + /* begin setGCMode: */ + GIV(gcMode) = GCModeFull; + + /* Mid-way the leak check must be more lenient. Unmarked classes will have been + expunged from the table, but unmarked instances will not yet have been reclaimed. */ + runLeakCheckerForexcludeUnmarkedObjsclassIndicesShouldBeValid(GCModeFull, 1, 1); + compactionStartUsecs = ioUTCMicrosecondsNow(); + + /* begin prepareForGlobalSweep */ + for (i = 0; i < GIV(numSegments); i += 1) { + ((GIV(segments)[i]).containsPinned = 0); + } + + /* begin bridgeAt: */ + /* begin bridgeFor: */ + address = (((((&(GIV(segments)[GIV(numSegments) - 1])))->segSize)) + ((((&(GIV(segments)[GIV(numSegments) - 1])))->segStart))) - (2 * BaseHeaderSize); + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + compact(); + + /* begin attemptToShrink */ + if ((GIV(totalFreeOldSpace) > GIV(shrinkThreshold)) + && ((GIV(totalFreeOldSpace) > GIV(growHeadroom)) + && (shrinkObjectMemory((((GIV(totalFreeOldSpace) - GIV(growHeadroom)) < GIV(growHeadroom)) ? GIV(growHeadroom) : (GIV(totalFreeOldSpace) - GIV(growHeadroom))))))) { + GIV(statShrinkMemory) += 1; + } + + /* begin setHeapSizeAtPreviousGC */ + GIV(heapSizeAtPreviousGC) = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + + /* begin resetAllocationAccountingAfterGC */ + GIV(oldSpaceUsePriorToScavenge) = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + assert(validObjStacks()); + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + assert(allObjectsUnmarked()); + runLeakCheckerFor(GCModeFull); + postGCAction(GCModeFull); + GIV(statGCEndUsecs) = ioUTCMicrosecondsNow(); + + /* begin updateFullGCStats */ + GIV(statFullGCs) += 1; + GIV(statFullGCUsecs) += GIV(statGCEndUsecs) - GIV(gcStartUsecs); + GIV(statCompactionUsecs) += GIV(statGCEndUsecs) - compactionStartUsecs; + if (GIV(gcSweepEndUsecs)) { + GIV(statSweepUsecs) += GIV(gcSweepEndUsecs) - compactionStartUsecs; + } + GIV(statMarkUsecs) += gcMarkEndUsecs - GIV(gcStartUsecs); + return (GIV(freeLists)[0] + ? bytesInBody(findLargestFreeChunk()) + : 0); +} + + /* SpurMemoryManager>>#getHeapGrowthToSizeGCRatio */ +static float +getHeapGrowthToSizeGCRatio(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(heapGrowthToSizeGCRatio); +} + + /* SpurMemoryManager>>#goodContextSize: */ +static NoDbgRegParms sqInt +goodContextSize(sqInt oop) +{ + usqInt numSlots; + usqInt numSlotsUsqInt; + + /* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + return (numSlots == SmallContextSlots) + || (numSlots == LargeContextSlots); +} + + +/* Attempt to grow memory by at least minAmmount. + Answer the size of the new segment, or nil if the attempt failed. */ + + /* SpurMemoryManager>>#growOldSpaceByAtLeast: */ +static NoDbgRegParms usqInt +growOldSpaceByAtLeast(sqInt minAmmount) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt ammount; + sqInt bytes; + sqInt headroom; + sqInt interval; + SpurSegmentInfo *segInfo; + sqInt start; + usqInt total; + + + /* statGrowMemory counts attempts, not successes. */ + /* we need to include overhead for a new object header plus the segment bridge. */ + GIV(statGrowMemory) += 1; + ammount = minAmmount + ((BaseHeaderSize * 2) + (2 * BaseHeaderSize)); + + /* round up to the nearest power of two. */ + ammount = 1ULL << (highBit(ammount - 1)); + + /* and grow by at least growHeadroom. */ + ammount = ((ammount < GIV(growHeadroom)) ? GIV(growHeadroom) : ammount); + + /* Now apply the maxOldSpaceSize limit, if one is in effect. */ + if (maxOldSpaceSize > 0) { + /* begin totalOldSpaceSize */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)); + total = GIV(totalHeapSizeIncludingBridges); + if (total >= maxOldSpaceSize) { + GIV(needGCFlag) = 1; + return null; + } + headroom = maxOldSpaceSize - total; + if (headroom < ammount) { + if (headroom < (minAmmount + ((BaseHeaderSize * 2) + (2 * BaseHeaderSize)))) { + GIV(needGCFlag) = 1; + return null; + } + ammount = headroom; + } + } + start = ioUTCMicrosecondsNow(); + if ((segInfo = addSegmentOfSize(ammount))) { + /* begin assimilateNewSegment: */ + if ((((segInfo->segSize)) + ((segInfo->segStart))) >= GIV(endOfMemory)) { + GIV(freeOldSpaceStart) = (GIV(endOfMemory) = (((segInfo->segSize)) + ((segInfo->segStart))) - (2 * BaseHeaderSize)); + } + + /* and add the new free chunk to the free list; done here + instead of in assimilateNewSegment: for the assert */ + bytes = ((segInfo->segSize)) - (2 * BaseHeaderSize); + address = (segInfo->segStart); + + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += bytes; + freeChunkWithBytesat(bytes, address); + assert((addressAfter(objectStartingAt((segInfo->segStart)))) == ((segLimit(segInfo)) - (bridgeSize()))); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCCheckFreeSpace)) == (GCCheckFreeSpace | GCCheckFreeSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + checkSegments(); + interval = (ioUTCMicrosecondsNow()) - start; + if (interval > GIV(statMaxAllocSegmentTime)) { + GIV(statMaxAllocSegmentTime) = interval; + } + return (segInfo->segSize); + } + GIV(needGCFlag) = 1; + return null; +} + + /* SpurMemoryManager>>#hasPointerFieldsNonImm: */ +static NoDbgRegParms sqInt +hasPointerFieldsNonImm(sqInt oop) +{ + sqInt format; + + format = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isAnyPointerFormat: */ + return (format <= 5 /* lastPointerFormat */) + || (format >= (firstCompiledMethodFormat())); +} + + +/* The header format in LSB is + MSB: | 8: numSlots | (on a byte boundary) + | 2 bits | (msb,lsb = {isMarked,isGrey}) + | 22: identityHash | (on a word boundary) + | 3 bits | (msb <-> lsb = {UNUSED,isPinned,isRemembered} + | 5: format | (on a byte boundary) + | 2 bits | (msb,lsb = {isImmutable,isExtraAligned}) + | 22: classIndex | (on a word boundary) : LSB + The 7 bit flags are used (or reserved) for + isExtraAligned (bit 22) + isImmutable (bit 23) + isRemembered (bit 29) + isPinned (bit 30) + UNUSED (bit 31) + isGrey (bit 54) + isMarked (bit 55) + The three bit field { UNUSED, isPinned, isRemembered } is for bits that + are never + set in young objects (forcing uses of the UNUSED bit to be for oldSPace + objects only). + This allows the remembered table to be pruned when full by using these + bits as a + reference count of newSpace objects from the remembered table. Objects + with a + high count should be tenured to prune the remembered table. */ + + /* SpurMemoryManager>>#headerForSlots:format:classIndex: */ +usqLong +headerForSlotsformatclassIndex(sqInt numSlots, sqInt formatField, sqInt classIndex) +{ + return ((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex; +} + + +/* The header format in LSB is + MSB: | 8: numSlots | (on a byte boundary) + | 2 bits | (msb,lsb = {isMarked,isGrey}) + | 22: identityHash | (on a word boundary) + | 3 bits | (msb <-> lsb = {UNUSED,isPinned,isRemembered} + | 5: format | (on a byte boundary) + | 2 bits | (msb,lsb = {isImmutable,isExtraAligned}) + | 22: classIndex | (on a word boundary) : LSB + The 7 bit flags are used (or reserved) for + isExtraAligned (bit 22) + isImmutable (bit 23) + isRemembered (bit 29) + isPinned (bit 30) + UNUSED (bit 31) + isGrey (bit 54) + isMarked (bit 55) + The three bit field { UNUSED, isPinned, isRemembered } is for bits that + are never + set in young objects (forcing uses of the UNUSED bit to be for oldSPace + objects only). + This allows the remembered table to be pruned when full by using these + bits as a + reference count of newSpace objects from the remembered table. Objects + with a + high count should be tenured to prune the remembered table. */ + + /* SpurMemoryManager>>#headerForSlots:hash:format:classIndex: */ +static NoDbgRegParms usqLong +headerForSlotshashformatclassIndex(sqInt numSlots, sqInt hash, sqInt formatField, sqInt classIndex) +{ + return (((((((usqLong) numSlots)) << (numSlotsFullShift()))) + (((((usqLong) hash)) << (identityHashFullWordShift())))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex; +} + + +/* Answer the number of extra root slots in the root of the hidden root + object. + */ + + /* SpurMemoryManager>>#hiddenRootSlots */ +static sqInt +hiddenRootSlots(void) +{ + return 8; +} + + /* SpurMemoryManager>>#hiddenRootsObject */ +static sqInt +hiddenRootsObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(hiddenRootsObj); +} + + +/* a more complex version that tells both the word reversal and the + endianness of the machine + it came from. Low half of word is e.g. 6521. Top byte is top byte of + #doesNotUnderstand: on + this machine. ($d on the Mac or $s on the PC) */ + + /* SpurMemoryManager>>#imageSegmentVersion */ +static sqInt +imageSegmentVersion(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int wholeWord; + + /* first data word, 'does' */ + wholeWord = long32At((void *)((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord()))))))) + BaseHeaderSize)); + return 68021 /* imageFormatVersion */ | (wholeWord & 0xFF000000U); +} + + +/* mask the immutable bit in the base header word */ + + /* SpurMemoryManager>>#immutableBitMask */ +#if IMMUTABILITY +sqInt +immutableBitMask(void) +{ + return 1U << (immutableBitShift()); +} +#endif /* IMMUTABILITY */ + + /* SpurMemoryManager>>#indexOf:in: */ +sqInt +indexOfin(sqInt anElement, sqInt anObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt fmtSqInt; + sqInt i; + usqInt numSlots; + usqInt numSlotsUsqInt; + + fmt = (byteAt((void *)(anObject + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(anObject)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(anObject + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(anObject - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + for (i = 0; i <= numSlots; i += 1) { + if (anElement == (longAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + return i; + } + } + } + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + return (GIV(primFailCode) = PrimErrUnsupported); + } + + /* begin numBytesOfBytes: */ + fmtSqInt = (byteAt((void *)(anObject + (formatFieldByteOffset())))) & (formatMask()); + assert(fmtSqInt >= (firstByteFormat())); + numSlots = ((((/* begin numSlotsOf: */ + assert((classIndexOf(anObject)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(anObject + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(anObject - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) << (shiftForWord()))) - (fmtSqInt & 7); + for (i = 0; i <= numSlots; i += 1) { + if (anElement == (byteAt((void *)((anObject + BaseHeaderSize) + i)))) { + return i; + } + } + } + if (fmt >= (firstShortFormat())) { + numSlots = ((usqInt)((numBytesOf(anObject)))) >> 1; + for (i = 0; i <= numSlots; i += 1) { + if (anElement == (((unsigned short) (shortAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(i) << 1))))))))) { + return i; + } + } + } + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(anObject)))) >> 3; + for (i = 0; i <= numSlots; i += 1) { + if (anElement == (long64At((void *)((anObject + BaseHeaderSize) + ((((usqInt)(i) << 3))))))) { + return i; + } + } + } + if (fmt >= (firstLongFormat())) { + numSlots = ((usqInt)((numBytesOf(anObject)))) >> 2; + for (i = 0; i <= numSlots; i += 1) { + if (anElement == (long32At((void *)((anObject + BaseHeaderSize) + ((((usqInt)(i) << 2))))))) { + return i; + } + } + } + return -1; +} + + +/* Part of reorderReversedTreeList:. Switch treeNode with newNode in + the tree, but do nothing to the list linked through freeChunkNextIndex. */ + + /* SpurMemoryManager>>#inFreeTreeReplace:with: */ +static NoDbgRegParms void +inFreeTreeReplacewith(sqInt treeNode, sqInt newNode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fieldIndex; + sqInt i; + sqInt relative; + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(newNode)); + longAtput((void *)((newNode + BaseHeaderSize) + (1U << (shiftForWord()))),0); + + /* copy parent, smaller, larger */ + for (i = 2 /* freeChunkParentIndex */; i <= 4 /* freeChunkLargerIndex */; i += 1) { + relative = longAt((void *)((treeNode + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (i == 2 /* freeChunkParentIndex */) { + if (relative) { + fieldIndex = (treeNode == (longAt((void *)((relative + BaseHeaderSize) + (3U << (shiftForWord()))))) + ? 3 /* freeChunkSmallerIndex */ + : 4 /* freeChunkLargerIndex */); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(relative)); + assert((newNode == 0) + || (isFreeObject(newNode))); + longAtput((void *)((relative + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),newNode); + } + else { + assert((GIV(freeLists)[0]) == treeNode); + GIV(freeLists)[0] = newNode; + } + } + else { + if (relative) { + assert((fetchPointerofFreeChunk(freeChunkParentIndex(), relative)) == treeNode); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(relative)); + assert((newNode == 0) + || (isFreeObject(newNode))); + longAtput((void *)((relative + BaseHeaderSize) + (2U << (shiftForWord()))),newNode); + } + } + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(newNode)); + assert((relative == 0) + || (isFreeObject(relative))); + longAtput((void *)((newNode + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),relative); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(treeNode)); + longAtput((void *)((treeNode + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),0); + } +} + + /* SpurMemoryManager>>#initialInstanceOf: */ +static NoDbgRegParms sqInt +initialInstanceOf(sqInt classObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int classIndex; + int classIndexInt; + sqInt classIndexSqInt; + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + if (classIndex) { + /* flush instances in newSpace to settle the enumeration. */ + + /* begin flushNewSpaceInstancesOf: */ + classIndexInt = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + if (!classIndexInt) { + goto l1; + } + + /* no instances; nothing to do */ + + /* begin tenuringClassIndex: */ + GIV(tenuringClassIndex) = classIndexInt; + scavengingGCTenuringIf(TenureByClass); + assert(!(existInstancesInNewSpaceOf(classObj))); + /* end flushNewSpaceInstancesOf: */ +l1: + + /* scan old space for the first one found. */ + + /* begin allOldSpaceObjectsDo: */ + /* begin allOldSpaceObjectsFrom:do: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndexSqInt = (longAt((void *)(objOopSqInt))) & (classIndexMask()); + assert((classIndexSqInt == (segmentBridgePun())) + || ((classIndexSqInt == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objOopSqInt))) != 0) + && (classIndexSqInt < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndexSqInt >= (isForwardedObjectClassIndexPun())) { + if (classIndex == ((longAt((void *)(objOopSqInt))) & (classIndexMask()))) { + return objOopSqInt; + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l2:; + } + } + return null; +} + + /* SpurMemoryManager>>#initializeNewSpaceVariables */ +static NeverInline void +initializeNewSpaceVariables(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(freeStart) = ((GIV(eden)).start); + GIV(pastSpaceStart) = ((GIV(pastSpace)).start); + GIV(scavengeThreshold) = ((((GIV(eden)).limit)) - ((((GIV(eden).limit)) - ((GIV(eden).start))) / 64)) - (interpreterAllocationReserveBytes()); + GIV(newSpaceStart) = (((((GIV(pastSpace)).start)) < (((GIV(futureSpace)).start))) ? (((GIV(pastSpace)).start)) : (((GIV(futureSpace)).start))); + assert(GIV(newSpaceStart) < (((GIV(eden)).start))); +} + + +/* Initialize object memory variables at startup time. Assume endOfMemory at + al are + initialised by the image-reading code via + setHeapBase:memoryLimit:endOfMemory:. endOfMemory is assumed to point to + the end of the last object in the image. + Assume: image reader also initializes the following variables: + specialObjectsOop + lastHash */ +/* Catch mis-initializations leading to bad translations to C */ + + /* SpurMemoryManager>>#initializeObjectMemory: */ +static NoDbgRegParms void +initializeObjectMemory(sqInt bytesToShift) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anOop; + sqInt cameFrom; + usqInt chunkBytes; + sqInt classIndex; + sqInt classTableRoot; + sqInt field; + sqInt fieldAddr; + sqInt fieldOop; + sqInt firstObj; + sqInt followingWord; + usqInt followingWordAddress; + sqInt freeChunk; + sqInt freeListObj; + usqInt freeOldStart; + sqInt fudge; + sqInt i; + sqInt index; + sqInt j; + sqInt largeChild; + usqInt limit; + sqInt min; + sqInt nilObjPreSwizzle; + usqInt numSlots; + sqInt obj; + usqInt p; + sqInt page; + sqInt smallChild; + usqInt startOfFreeOldSpace; + usqInt toDoLimit; + sqInt treeNode; + sqInt valuePointer; + + assert(BaseHeaderSize == BaseHeaderSize); + assert((((sqInt)((maxSlotsForAlloc()) * BytesPerWord))) > 0); + initSegmentBridgeWithBytesat(2 * BaseHeaderSize, GIV(endOfMemory) - (2 * BaseHeaderSize)); + adjustSegmentSwizzlesBy(bytesToShift); + + /* image may be at a different address; adjust oops for new location */ + + /* begin adjustAllOopsBy: */ + assert(newSpaceIsEmpty()); + numSlots = byteAt((void *)(GIV(oldSpaceStart) + (numSlotsFieldByteOffset()))); + firstObj = (numSlots == (numSlotsMask()) + ? GIV(oldSpaceStart) + BaseHeaderSize + : GIV(oldSpaceStart)); + classTableRoot = oldSpaceObjectAfter(oldSpaceObjectAfter(oldSpaceObjectAfter(oldSpaceObjectAfter(firstObj)))); + nilObjPreSwizzle = GIV(oldSpaceStart) - bytesToShift; + + /* begin numSlotsOf: */ + assert((classIndexOf(classTableRoot)) > (isForwardedObjectClassIndexPun())); + GIV(numClassTablePages) = (((numSlots = byteAt((void *)(classTableRoot + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classTableRoot - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + assert(GIV(numClassTablePages) == ((classTableRootSlots()) + (hiddenRootSlots()))); + for (i = 2; i < GIV(numClassTablePages); i += 1) { + if ((longAt((void *)((classTableRoot + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))))) == nilObjPreSwizzle) { + GIV(numClassTablePages) = i; + goto l4; + } + } + /* end countNumClassPagesPreSwizzle: */ +l4: + if ((bytesToShift != 0) + || (GIV(numSegments) > 1)) { + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(GIV(oldSpaceStart) + (numSlotsFieldByteOffset()))); + obj = (numSlots == (numSlotsMask()) + ? GIV(oldSpaceStart) + BaseHeaderSize + : GIV(oldSpaceStart)); + while (oopisLessThan(obj, GIV(freeOldSpaceStart))) { + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + /* begin swizzleFieldsOfObject: */ + fieldAddr = obj + (lastPointerOfWhileSwizzling(obj)); + while (oopisGreaterThanOrEqualTo(fieldAddr, obj + BaseHeaderSize)) { + fieldOop = longAt((void *)(fieldAddr)); + if ((!(fieldOop & (tagMask())))) { + longAtput((void *)(fieldAddr),swizzleObj(fieldOop)); + } + fieldAddr -= BytesPerOop; + } + } + else { + if (classIndex == (isFreeObjectClassIndexPun())) { + /* begin swizzleFieldsOfFreeChunk: */ + field = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + if (field) { + valuePointer = swizzleObj(field); + + /* begin storeUncheckedNoAssert:ofAny:withValue: */ + longAtput((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + } + chunkBytes = bytesInBody(obj); + + /* The prevPointer is not guaranteed to be valid in older images. + updateListStartingAt: via updateFreeLists does restore the prev pointer + in all small free lists, so simply avoid swizzling it now. */ + if (chunkBytes >= 0x200 /* (numFreeLists * #allocationUnit) */) { + for (index = 2 /* freeChunkParentIndex */; index <= 4 /* freeChunkLargerIndex */; index += 1) { + field = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (field) { + valuePointer = swizzleObj(field); + + /* begin storeUncheckedNoAssert:ofAny:withValue: */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),valuePointer); + } + } + } + } + } + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3:; + } + } + GIV(specialObjectsOop) = swizzleObj(GIV(specialObjectsOop)); + + /* heavily used special objects */ + GIV(nilObj) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(NilObject) << (shiftForWord())))))); + GIV(falseObj) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(FalseObject) << (shiftForWord())))))); + GIV(trueObj) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TrueObject) << (shiftForWord())))))); + + /* In Cog we insist that nil, true & false are next to each other (Cogit generates tighter + conditional branch code as a result). In addition, Spur places the free lists and + class table root page immediately following them. */ + assert(GIV(nilObj) == GIV(oldSpaceStart)); + assert(GIV(falseObj) == (oldSpaceObjectAfter(GIV(nilObj)))); + assert(GIV(trueObj) == (oldSpaceObjectAfter(GIV(falseObj)))); + freeListObj = oldSpaceObjectAfter(GIV(trueObj)); + anOop = oldSpaceObjectAfter(freeListObj); + + /* begin setHiddenRootsObj: */ + GIV(hiddenRootsObj) = anOop; + assert(validClassTableRootPages()); + GIV(classTableFirstPage) = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(((numSlotsOf(GIV(classTableFirstPage))) - 1) == (classTableMinorIndexMask())); + + /* Hack fix. A bug in markAndTraceClassOf: caused the class of the first class table page + to be changed from its pun. This can be restored manually, but we do it here too. */ + if (((longAt((void *)(GIV(classTableFirstPage)))) & (classIndexMask())) != (arrayClassIndexPun())) { + /* begin setClassIndexOf:to: */ + assert((((arrayClassIndexPun()) >= 0) && ((arrayClassIndexPun()) <= (classIndexMask())))); + longAtput((void *)(GIV(classTableFirstPage)),((longAt((void *)(GIV(classTableFirstPage)))) & (~(usqIntptr_t)(classIndexMask()))) + (arrayClassIndexPun())); + } + GIV(numClassTablePages) = 1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift())); + + /* If loading an image, set the classTableIndex to the first unused slot in the class table after the first page. + Set numClassTablePages to the number of used pages. + Set classTableIndex to point at the first unused entry. First set it to the max as a sentinel. */ + GIV(classTableIndex) = ((sqInt)((usqInt)(GIV(numClassTablePages)) << (classTableMajorIndexShift()))); + for (i = 1; i < GIV(numClassTablePages); i += 1) { + if (((page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) == GIV(nilObj)) { + if ((((usqInt)(GIV(classTableIndex))) >> (classTableMajorIndexShift())) > i) { + GIV(classTableIndex) = ((sqInt)((usqInt)(((((i - 1) < 1) ? 1 : (i - 1)))) << (classTableMajorIndexShift()))); + } + GIV(numClassTablePages) = i; + assert((classOrNilAtIndex(GIV(classTableIndex))) == GIV(nilObj)); + goto l1; + } + else { + if ((((usqInt)(GIV(classTableIndex))) >> (classTableMajorIndexShift())) > i) { + j = 0; + while (j < (1U << (classTableMajorIndexShift()))) { + if ((longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord()))))))) == GIV(nilObj)) { + GIV(classTableIndex) = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + j = 1U << (classTableMajorIndexShift()); + } + j += 1; + } + } + } + } + + /* no unused slots; set it to the start of the second page. */ + if ((((usqInt)(GIV(classTableIndex))) >> (classTableMajorIndexShift())) >= GIV(numClassTablePages)) { + GIV(classTableIndex) = 1U << (classTableMajorIndexShift()); + } + assert((classOrNilAtIndex(GIV(classTableIndex))) == GIV(nilObj)); + /* end setHiddenRootsObj: */ +l1: + GIV(markStack) = swizzleObjStackAt(MarkStackRootIndex); + GIV(weaklingStack) = swizzleObjStackAt(WeaklingStackRootIndex); + GIV(mournQueue) = swizzleObjStackAt(MournQueueRootIndex); + assert(validObjStacks()); + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + + /* begin initializeFreeSpacePostLoad: */ + assert((numSlotsOf(freeListObj)) == (numFreeLists())); + assert((formatOf(freeListObj)) == (wordIndexableFormat())); + GIV(freeLists) = firstIndexableField(freeListObj); + GIV(freeListsMask) = 0; + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + if (GIV(freeLists)[i]) { + GIV(freeListsMask) = GIV(freeListsMask) | (1ULL << i); + GIV(freeLists)[i] = (swizzleObj(GIV(freeLists)[i])); + } + } + collapseSegmentsPostSwizzle(); + + /* begin updateFreeLists */ + min = 3; + for (i = min; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + updateListStartingAt(GIV(freeLists)[i]); + } + + /* Large chunks */ + + /* begin freeTreeNodesDo: */ + treeNode = GIV(freeLists)[0]; + if (!treeNode) { + goto l2; + } + cameFrom = -1; + do { + assert((bytesInBody(treeNode)) >= ((numFreeLists()) * (allocationUnit()))); + smallChild = longAt((void *)((treeNode + BaseHeaderSize) + (3U << (shiftForWord())))); + largeChild = longAt((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + assert((smallChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), smallChild)))); + assert((largeChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), largeChild)))); + + /* apply if the node has no children, or it has no large children and we're + returning from the small child, or we're returning from the large child. */ + if (((smallChild == 0) + && (largeChild == 0)) + || ((largeChild + ? cameFrom == largeChild + : cameFrom == smallChild))) { + updateListStartingAt(treeNode); + + /* and since we've applied we must move on up */ + cameFrom = treeNode; + treeNode = longAt((void *)((treeNode + BaseHeaderSize) + (2U << (shiftForWord())))); + } + else { + if ((smallChild != 0) + && (cameFrom != smallChild)) { + treeNode = smallChild; + } + else { + assert(largeChild != 0); + treeNode = largeChild; + } + cameFrom = -1; + } + } while(treeNode != 0); + /* end freeTreeNodesDo: */ +l2: + + /* begin computeFreeSpacePostSwizzle */ + GIV(totalFreeOldSpace) = totalFreeListBytes(); + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + startOfFreeOldSpace = GIV(freeOldSpaceStart); + + /* begin initializeOldSpaceFirstFree: */ + limit = GIV(endOfMemory) - (2 * BaseHeaderSize); + if (limit > startOfFreeOldSpace) { + GIV(totalFreeOldSpace) += limit - startOfFreeOldSpace; + freeOldStart = startOfFreeOldSpace; + while ((limit - freeOldStart) >= (0x100000000LL)) { + freeChunk = freeChunkWithBytesat(0x100000000LL, freeOldStart); + freeOldStart += 0x100000000LL; + assert(freeOldStart == (addressAfter(freeChunk))); + } + if (freeOldStart < limit) { + freeChunk = freeChunkWithBytesat(limit - freeOldStart, freeOldStart); + assert((addressAfter(freeChunk)) == limit); + } + } + GIV(endOfMemory) -= 2 * BaseHeaderSize; + GIV(freeOldSpaceStart) = GIV(endOfMemory); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCCheckFreeSpace)) == (GCCheckFreeSpace | GCCheckFreeSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + initializeNewSpaceVariables(); + + /* begin initializeRememberedSet */ + obj = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))); + if (obj == GIV(nilObj)) { + /* begin allocatePinnedSlots: */ + obj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(0x400, (1024U << (shiftForWord())) + (BaseHeaderSize + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (obj) { + assert(isPinned(obj)); + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((obj + BaseHeaderSize) + (0x400 * BytesPerOop)) - 1, addressAfter(obj))); + toDoLimit = ((usqInt)(((obj + BaseHeaderSize) + (0x400 * BytesPerOop)) - 1)); + for (p = (((usqInt)(obj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + + /* begin rememberedSetObj: */ + assert(isOldObject(obj)); + assert((isNonImmediate(GIV(hiddenRootsObj))) + && (!(isForwarded(GIV(hiddenRootsObj))))); + assert(validStorePointerUncheckedArgs(RememberedSetRootIndex, GIV(hiddenRootsObj), obj)); + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord()))))),obj); + } + else { + /* begin setFormatOf:to: */ + assert((((sixtyFourBitIndexableFormat()) >= 0) && ((sixtyFourBitIndexableFormat()) <= (formatMask())))); + byteAtput((void *)(obj + (formatFieldByteOffset())),((byteAt((void *)(obj + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (sixtyFourBitIndexableFormat())); + } + + /* The Spur32to64BitBootstrap failed to set the type of rememberedSetObj to 64-bit indexability. + This is unimportant except for simulation; rememberedSet is declared as sqInt *, but to have + firstIndexableField: below answer a suitable type the format must be wordIndexableFormat. */ + assert((formatOf(obj)) == (wordIndexableFormat())); + assert(isPinned(obj)); + GIV(rememberedSet) = firstIndexableField(obj); + GIV(rememberedSetSize) = 0; + + /* begin numSlotsOf: */ + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())); + GIV(rememberedSetLimit) = (((numSlots = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + + /* begin setRememberedSetRedZone */ + fudge = ((((GIV(eden).limit)) - ((GIV(eden).start))) / BytesPerWord) / 0x400; + GIV(rememberedSetRedZone) = ((((GIV(rememberedSetLimit) * 3) / 4) < fudge) ? fudge : ((GIV(rememberedSetLimit) * 3) / 4)); + checkSegments(); + + /* begin biasForGC */ + GIV(biasForGC) = 1; + + /* These defaults should depend on machine size; e.g. too small on a powerful laptop, too big on a Pi. */ + + /* headroom when growing */ + GIV(growHeadroom) = 0x1000000; + + /* free space before shrinking */ + GIV(shrinkThreshold) = 0x2000000; + GIV(heapSizeAtPreviousGC) = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + + /* begin resetAllocationAccountingAfterGC */ + GIV(oldSpaceUsePriorToScavenge) = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + + /* By default GC after scavenge if heap has grown by a third since the last GC */ + GIV(heapGrowthToSizeGCRatio) = 0.333333; +} + + +/* Initialize unscannedEphemerons to use the largest unused new space, + which ever is the larger. We haven't yet scavenged, so eden may be + almost full, but futureSpace is known to be empty. N.B. we *cannot* + use free old space because it may be used to grow the markStack. */ + + /* SpurMemoryManager>>#initializeUnscannedEphemerons */ +static void +initializeUnscannedEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt sizeOfFutureSpace; + sqInt sizeOfUnusedEden; + + sizeOfUnusedEden = (((GIV(eden)).limit)) - GIV(freeStart); + sizeOfFutureSpace = (((GIV(futureSpace)).limit)) - (((GIV(futureSpace)).start)); + if (sizeOfUnusedEden > sizeOfFutureSpace) { + (GIV(unscannedEphemerons).start = GIV(freeStart)); + (GIV(unscannedEphemerons).limit = ((GIV(eden)).limit)); + } + else { + (GIV(unscannedEphemerons).start = ((GIV(futureSpace)).start)); + (GIV(unscannedEphemerons).limit = ((GIV(futureSpace)).limit)); + } + (GIV(unscannedEphemerons).top = ((GIV(unscannedEphemerons).start)) - BytesPerOop); + assert(numUnscannedEphemerons() == 0); +} + + +/* print free chunks in freeTree in order. */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#inOrderPrintFreeTree:printList: */ +void +inOrderPrintFreeTreeprintList(sqInt freeChunk, sqInt printNextList) +{ + sqInt next; + + if ((next = longAt((void *)((freeChunk + BaseHeaderSize) + (3U << (shiftForWord())))))) { + inOrderPrintFreeTreeprintList(next, printNextList); + } + printFreeChunkprintAsTreeNode(freeChunk, 1); + if (printNextList) { + next = freeChunk; + while (((next = longAt((void *)((next + BaseHeaderSize) + (0U << (shiftForWord())))))) != 0) { + /* begin tab */ + printChar('\t'); + printFreeChunkprintAsTreeNode(next, 0); + } + } + if ((next = longAt((void *)((freeChunk + BaseHeaderSize) + (4U << (shiftForWord())))))) { + inOrderPrintFreeTreeprintList(next, printNextList); + } +} + + +/* in an effort to fix a compiler bug with two-way become post r3427 */ +/* Do become in place by swapping object contents. */ + + /* SpurMemoryManager>>#inPlaceBecome:and:copyHashFlag: */ +static NoDbgRegParms NeverInline void +inPlaceBecomeandcopyHashFlag(sqInt obj1, sqInt obj2, sqInt copyHashFlag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqLong hashBits; + sqLong header1; + sqLong header2; + sqInt i; + usqInt numSlots; + sqInt o1HasYoung; + sqInt o2HasYoung; + sqLong remembered; + sqInt temp1; + sqInt temp2; + sqInt toDoLimit; + + assert((numSlotsOf(obj1)) == (numSlotsOf(obj2))); + assert(((rawHashBitsOf(obj1)) == 0) + || ((classOrNilAtIndex(rawHashBitsOf(obj1))) != obj1)); + assert(((rawHashBitsOf(obj2)) == 0) + || ((classOrNilAtIndex(rawHashBitsOf(obj2))) != obj2)); + + /* swap headers, but swapping headers swaps remembered bits and hashes; + remembered bits must be unswapped and hashes may be unswapped if + copyHash is false. */ + + /* begin cleverSwapHeaders:and:copyHashFlag: */ + header1 = long64At((void *)(obj1)); + header2 = long64At((void *)(obj2)); + remembered = (header1 ^ header2) & (1U << (rememberedBitShift())); + if (remembered) { + header1 = header1 ^ remembered; + header2 = header2 ^ remembered; + } + + /* swapping headers swaps hash; if not copyHashFlag then unswap hash */ + if (!copyHashFlag) { + hashBits = (header1 ^ header2) & 0x3FFFFF00000000LL /* identityHashFullWordMask */; + if (hashBits) { + header1 = header1 ^ hashBits; + header2 = header2 ^ hashBits; + } + } + long64Atput((void *)(obj1),header2); + long64Atput((void *)(obj2),header1); + o1HasYoung = (o2HasYoung = 0); + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(obj1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(obj1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + temp1 = longAt((void *)((obj1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + temp2 = longAt((void *)((obj2 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(obj1)) + && (!(isForwarded(obj1)))); + assert(validStorePointerUncheckedArgs(i, obj1, temp2)); + longAtput((void *)((obj1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),temp2); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(obj2)) + && (!(isForwarded(obj2)))); + assert(validStorePointerUncheckedArgs(i, obj2, temp1)); + longAtput((void *)((obj2 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),temp1); + if (/* isYoung: */ + ((!(temp2 & (tagMask())))) + && (oopisLessThan(temp2, GIV(oldSpaceStart)))) { + o1HasYoung = 1; + } + if (/* isYoung: */ + ((!(temp1 & (tagMask())))) + && (oopisLessThan(temp1, GIV(oldSpaceStart)))) { + o2HasYoung = 1; + } + } + if (o1HasYoung + && ((/* begin isOldObject: */ + assert(isNonImmediate(obj1)), + oopisGreaterThanOrEqualTo(obj1, GIV(oldSpaceStart))))) { + fmt = (byteAt((void *)(obj1 + (formatFieldByteOffset())))) & (formatMask()); + if (!(/* isPureBitsFormat: */ + (fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat())))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj1 + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj1); + } + } + } + if (o2HasYoung + && ((/* begin isOldObject: */ + assert(isNonImmediate(obj2)), + oopisGreaterThanOrEqualTo(obj2, GIV(oldSpaceStart))))) { + fmt = (byteAt((void *)(obj2 + (formatFieldByteOffset())))) & (formatMask()); + if (!(/* isPureBitsFormat: */ + (fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat())))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj2 + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj2); + } + } + } +} + + +/* Answer the number of slots in a class. For example the instanceSizeOf: + ClassPoint is 2, for the x & y slots. The instance size of non-pointer + classes is 0. */ + + /* SpurMemoryManager>>#instanceSizeOf: */ +sqInt +instanceSizeOf(sqInt classObj) +{ + assert(addressCouldBeClassObj(classObj)); + return (((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); +} + + /* SpurMemoryManager>>#instantiateClass: */ +static NoDbgRegParms sqInt +instantiateClass(sqInt classObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt err; + int hash; + sqInt instSpec; + sqInt newObj; + usqInt newObjUsqInt; + usqInt numBytes; + sqInt numSlots; + usqInt p; + usqInt toDoLimit; + + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + if (!(/* isFixedSizePointerFormat: */ + (instSpec <= (nonIndexablePointerFormat())) + || (instSpec == (ephemeronFormat())))) { + return null; + } + + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + return null; + } + numSlots = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if ((((usqInt)(numSlots)) >> 56) > 0) { + newObj = null; + goto l1; + } + newObjUsqInt = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObjUsqInt = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, instSpec, classIndex); + goto l1; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObjUsqInt),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObjUsqInt),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObjUsqInt % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newObj = newObjUsqInt; + /* end allocateSlots:format:classIndex: */ +l1: + if (newObj) { + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),GIV(nilObj)); + } + } + return newObj; +} + + /* SpurMemoryManager>>#instSpecOfClassFormat: */ +sqInt +instSpecOfClassFormat(sqInt classFormat) +{ + return (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); +} + + +/* This field in a class's format inst var corresponds to the 5-bit format + field stored in every object header + */ + + /* SpurMemoryManager>>#instSpecOfClass: */ +static NoDbgRegParms sqInt +instSpecOfClass(sqInt classPointer) +{ + return (((usqInt)((((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); +} + + /* SpurMemoryManager>>#invalidCompactClassError: */ +static NoDbgRegParms void +invalidCompactClassError(const char *className) +{ + printf("\nClass %s does not have the required class index\n", className); + exit(-1); +} + + +/* Answer true if this is an indexable object with pointer elements, e.g., an + array + */ + + /* SpurMemoryManager>>#isArray: */ +sqInt +isArray(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())); +} + + +/* Answer if objOop should be included in an allObjects...Do: enumeration. + This is for assert-checking only. */ + + /* SpurMemoryManager>>#isBridgeOrEnumerableObjectNoAssert: */ +static NoDbgRegParms int +isBridgeOrEnumerableObjectNoAssert(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + return (classIndex >= (isForwardedObjectClassIndexPun()) + ? classIndex < (GIV(numClassTablePages) * (1U << (classTableMajorIndexShift()))) + : classIndex == (segmentBridgePun())); +} + + /* SpurMemoryManager>>#isCharacterObject: */ +int +isCharacterObject(sqInt oop) +{ + return ((oop & (characterTag())) != 0); +} + + /* SpurMemoryManager>>#isCharacterValue: */ +int +isCharacterValue(sqInt anInteger) +{ + return ((anInteger >= 0) && (anInteger <= (0x3FFFFFFF))); +} + + /* SpurMemoryManager>>#isCompiledMethodFormat: */ +static NoDbgRegParms int +isCompiledMethodFormat(sqInt format) +{ + return format >= (firstCompiledMethodFormat()); +} + + +/* Answer whether the argument header has compiled method format */ + + /* SpurMemoryManager>>#isCompiledMethodHeader: */ +static NoDbgRegParms int +isCompiledMethodHeader(sqInt objHeader) +{ + return ((((usqInt)(objHeader)) >> (formatShift())) & (formatMask())) >= (firstCompiledMethodFormat()); +} + + +/* Answer whether the argument object is of compiled method format */ + + /* SpurMemoryManager>>#isCompiledMethod: */ +int +isCompiledMethod(sqInt objOop) +{ + return ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()); +} + + /* SpurMemoryManager>>#isContextHeader: */ +static NoDbgRegParms int +isContextHeader(sqInt aHeader) +{ + return (aHeader & (classIndexMask())) == ClassMethodContextCompactIndex; +} + + /* SpurMemoryManager>>#isContextNonImm: */ +static NoDbgRegParms int +isContextNonImm(sqInt oop) +{ + return ((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex; +} + + /* SpurMemoryManager>>#isContext: */ +static NoDbgRegParms sqInt +isContext(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex); +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. */ + + /* SpurMemoryManager>>#isCopiedIntoSegment: */ +static NoDbgRegParms int +isCopiedIntoSegment(sqInt anObjectInTheHeap) +{ + return ((byteAt((void *)(anObjectInTheHeap + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0; +} + + /* SpurMemoryManager>>#isEmptyObjStack: */ +static NoDbgRegParms sqInt +isEmptyObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (objStack == GIV(nilObj)) { + return 1; + } + eassert(isValidObjStack(objStack)); + return (0 == (longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))))) + && (0 == (longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))); +} + + +/* Answer if objOop should be included in an allObjects...Do: enumeration. + This is for assert-checking only. */ + + /* SpurMemoryManager>>#isEnumerableObjectNoAssert: */ +static NoDbgRegParms sqInt +isEnumerableObjectNoAssert(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + return (classIndex >= (isForwardedObjectClassIndexPun())) + && (classIndex < (GIV(numClassTablePages) * (1U << (classTableMajorIndexShift())))); +} + + /* SpurMemoryManager>>#isEphemeronFormat: */ +static NoDbgRegParms int +isEphemeronFormat(sqInt format) +{ + return format == (ephemeronFormat()); +} + + /* SpurMemoryManager>>#isEphemeron: */ +static NoDbgRegParms int +isEphemeron(sqInt objOop) +{ + sqInt format; + + assert(isNonImmediate(objOop)); + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isEphemeronFormat: */ + return format == (ephemeronFormat()); +} + + /* SpurMemoryManager>>#isFixedSizePointerFormat: */ +sqInt +isFixedSizePointerFormat(sqInt format) +{ + return (format <= (nonIndexablePointerFormat())) + || (format == (ephemeronFormat())); +} + + /* SpurMemoryManager>>#isForwardedClassTag: */ +static NoDbgRegParms int +isForwardedClassTag(sqInt classIndex) +{ + return classIndex == (isForwardedObjectClassIndexPun()); +} + + +/* Answer if objOop is that if a forwarder. Take advantage of + isForwardedObjectClassIndexPun being a power of two to generate a more + efficient test than the straight-forward + (self classIndexOf: objOop) = self isForwardedObjectClassIndexPun + at the cost of this being ambiguous with free chunks. So either never + apply this to free chunks + or guard with (self isFreeObject: foo) not. So far the idiom has been to + guard with isFreeObject: */ +/* self assert: (self isFreeObject: objOop) not. */ + + /* SpurMemoryManager>>#isForwarded: */ +int +isForwarded(sqInt objOop) +{ + return (!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))); +} + + /* SpurMemoryManager>>#isFreeObject: */ +static NoDbgRegParms int +isFreeObject(sqInt objOop) +{ + return ((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun()); +} + + /* SpurMemoryManager>>#isFreeOop: */ +static NoDbgRegParms sqInt +isFreeOop(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())); +} + + /* SpurMemoryManager>>#isImmediate: */ +int +isImmediate(sqInt oop) +{ + return ((oop & (tagMask())) != 0); +} + + /* SpurMemoryManager>>#isInClassTable: */ +static NoDbgRegParms sqInt +isInClassTable(sqInt objOop) +{ + int hash; + + hash = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()); + return (hash != 0) + && ((classAtIndex(hash)) == objOop); +} + + /* SpurMemoryManager>>#isIndexable: */ +sqInt +isIndexable(sqInt objOop) +{ + sqInt fmt; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + return /* isIndexableFormat: */ + (fmt >= (arrayFormat())) + && ((fmt <= (weakArrayFormat())) + || (fmt >= (sixtyFourBitIndexableFormat()))); +} + + /* SpurMemoryManager>>#isInEden: */ +static NoDbgRegParms sqInt +isInEden(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return oopisGreaterThanOrEqualToandLessThan(objOop, ((GIV(eden)).start), GIV(freeStart)); +} + + /* SpurMemoryManager>>#isInFutureSpace: */ +static NoDbgRegParms sqInt +isInFutureSpace(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return oopisGreaterThanOrEqualToandLessThan(address, ((GIV(futureSpace)).start), GIV(futureSurvivorStart)); +} + + +/* Answer if the given address is in ST object memory. */ + + /* SpurMemoryManager>>#isInMemory: */ +sqInt +isInMemory(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt addressUsqInt; + sqInt i; + + if (/* isInNewSpace: */ + (oopisLessThan(address, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(address, GIV(newSpaceStart)))) { + return (oopisGreaterThanOrEqualToandLessThan(address, ((GIV(eden)).start), GIV(freeStart))) + || ((oopisGreaterThanOrEqualToandLessThan(address, ((GIV(pastSpace)).start), GIV(pastSpaceStart))) + || ((GIV(gcPhaseInProgress) == ScavengeInProgress) + && (oopisGreaterThanOrEqualToandLessThan(address, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))))); + } + addressUsqInt = address; + + /* begin isInSegments: */ + for (i = 0; i < GIV(numSegments); i += 1) { + if (addressUsqInt < (((GIV(segments)[i]).segStart))) { + return 0; + } + if (addressUsqInt < ((((GIV(segments)[i]).segSize)) + (((GIV(segments)[i]).segStart)))) { + return 1; + } + } + return 0; +} + + /* SpurMemoryManager>>#isInNewSpace: */ +static NoDbgRegParms sqInt +isInNewSpace(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (oopisLessThan(objOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(objOop, GIV(newSpaceStart))); +} + + /* SpurMemoryManager>>#isInOldSpace: */ +sqInt +isInOldSpace(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return oopisGreaterThanOrEqualToandLessThan(address, GIV(oldSpaceStart), GIV(endOfMemory)); +} + + /* SpurMemoryManager>>#isInPastSpace: */ +static NoDbgRegParms sqInt +isInPastSpace(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return oopisGreaterThanOrEqualToandLessThan(address, ((GIV(pastSpace)).start), GIV(pastSpaceStart)); +} + + /* SpurMemoryManager>>#isLargeFreeObject: */ +static NoDbgRegParms int +isLargeFreeObject(sqInt objOop) +{ + return (bytesInBody(objOop)) >= 0x200 /* (numFreeLists * #allocationUnit) */; +} + + +/* Answer if the argument contains only indexable 64-bit double words (no + oops). See comment in formatOf: + */ + + /* SpurMemoryManager>>#isLong64s: */ +sqInt +isLong64s(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) == (sixtyFourBitIndexableFormat())); +} + + /* SpurMemoryManager>>#isMarked: */ +int +isMarked(sqInt objOop) +{ + return ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0; +} + + +/* Answer if objOop is an ephemeron that may or may not have been fired. + Unfired ephemerons have ephemeronFormat as their format. Fired ephemerons + have their format set to nonIndexablePointerFormat, so to disambiguate, + check the class is an ephemeron class in this case. */ + + /* SpurMemoryManager>>#isMaybeFiredEphemeron: */ +static NoDbgRegParms sqInt +isMaybeFiredEphemeron(sqInt objOop) +{ + sqInt format; + + if (((objOop & (tagMask())) != 0)) { + return 0; + } + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + return (format == (ephemeronFormat())) + || ((format == (nonIndexablePointerFormat())) + && ((instSpecOfClass(fetchClassOfNonImm(objOop))) == (ephemeronFormat()))); +} + + /* SpurMemoryManager>>#isNonImmediate: */ +int +isNonImmediate(sqInt oop) +{ + return (!(oop & (tagMask()))); +} + + /* SpurMemoryManager>>#isObjEphemeron: */ +static NoDbgRegParms int +isObjEphemeron(sqInt objOop) +{ + sqInt format; + + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isEphemeronFormat: */ + return format == (ephemeronFormat()); +} + + /* SpurMemoryManager>>#isObjImmutable: */ +static NoDbgRegParms sqInt +isObjImmutable(sqInt anOop) +{ + return +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(anOop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ; +} + + +/* Answer if obj is old. Require that obj is non-immediate. */ + + /* SpurMemoryManager>>#isOldObject: */ +static NoDbgRegParms int +isOldObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(isNonImmediate(objOop)); + return oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart)); +} + + +/* Answer whether the oop is an object of compiled method format */ + + /* SpurMemoryManager>>#isOopCompiledMethod: */ +sqInt +isOopCompiledMethod(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())); +} + + /* SpurMemoryManager>>#isOopForwarded: */ +sqInt +isOopForwarded(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))); +} + + /* SpurMemoryManager>>#isOopImmutable: */ +sqInt +isOopImmutable(sqInt oop) +{ + return (((oop & (tagMask())) != 0)) + || (((((usqInt)((byteAt((void *)(oop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0); +} + + /* SpurMemoryManager>>#isOopMutable: */ +sqInt +isOopMutable(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (!(((((usqInt)((byteAt((void *)(oop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0)); +} + + /* SpurMemoryManager>>#isPinned: */ +int +isPinned(sqInt objOop) +{ + return ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0; +} + + +/* Answer if the argument has only fields that can hold oops. See comment in + formatOf: + */ + + /* SpurMemoryManager>>#isPointers: */ +sqInt +isPointers(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */); +} + + +/* the inverse of isAnyPointerFormat: */ + + /* SpurMemoryManager>>#isPureBitsFormat: */ +static NoDbgRegParms sqInt +isPureBitsFormat(sqInt format) +{ + return (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat())); +} + + +/* Answer if the argument contains only indexable words (no oops). See + comment in formatOf: + */ + + /* SpurMemoryManager>>#isPureBitsNonImm: */ +static NoDbgRegParms sqInt +isPureBitsNonImm(sqInt objOop) +{ + sqInt format; + + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isPureBitsFormat: */ + return (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat())); +} + + /* SpurMemoryManager>>#isRemembered: */ +static NoDbgRegParms int +isRemembered(sqInt objOop) +{ + return ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0; +} + + +/* Maybe this should be in SpurSegmentManager only */ + + /* SpurMemoryManager>>#isSegmentBridge: */ +static NoDbgRegParms int +isSegmentBridge(sqInt objOop) +{ + return ((longAt((void *)(objOop))) & (classIndexMask())) == (segmentBridgePun()); +} + + /* SpurMemoryManager>>#isSemaphoreOop: */ +static NoDbgRegParms sqInt +isSemaphoreOop(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((!(anOop & (tagMask())))) + && (((longAt((void *)(anOop))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))); +} + + +/* Answer if the argument contains only indexable 16-bit half words (no + oops). See comment in formatOf: + */ + + /* SpurMemoryManager>>#isShorts: */ +sqInt +isShorts(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstShortFormat())) && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= ((firstByteFormat()) - 1)))); +} + + +/* This version is private to SpurMemoryManager (for asserts, etc). It does + not take advantage of the power-of-two optimization in isForwarded:. */ + + /* SpurMemoryManager>>#isUnambiguouslyForwarder: */ +static NoDbgRegParms int +isUnambiguouslyForwarder(sqInt objOop) +{ + return ((longAt((void *)(objOop))) & (classIndexMask())) == (isForwardedObjectClassIndexPun()); +} + + +/* For debugging using printOopsSuchThat: */ + + /* SpurMemoryManager>>#isUnmarked: */ +int +isUnmarked(sqInt objOop) +{ + return !(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0); +} + + /* SpurMemoryManager>>#isValidClassTag: */ +sqInt +isValidClassTag(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOrNil; + sqInt classTablePage; + sqInt fieldIndex; + + assert(((classIndex >= 0) && (classIndex <= ((1U << (classIndexFieldWidth())) - 1)))); + if (((classIndex >= ((tagMask()) + 1)) && (classIndex <= (lastClassIndexPun())))) { + return 0; + } + + /* begin classOrNilAtIndex: */ + assert((classIndex <= (tagMask())) + || (classIndex >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classOrNil = GIV(nilObj); + goto l1; + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + classOrNil = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classOrNilAtIndex: */ +l1: + return (classOrNil != GIV(nilObj)) + && (((long32At((void *)(classOrNil + 4))) & (identityHashHalfWordMask())) == classIndex); +} + + +/* Answer if the obj stack at objStackRootIndex is valid. */ + + /* SpurMemoryManager>>#isValidObjStackAt: */ +static NoDbgRegParms sqInt +isValidObjStackAt(sqInt objStackRootIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt stackOrNil; + + stackOrNil = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord())))))); + return (stackOrNil == GIV(nilObj)) + || (isValidObjStackPagemyIndexfirstPage(stackOrNil, objStackRootIndex, 1)); +} + + +/* Just check the page itself. */ + + /* SpurMemoryManager>>#isValidObjStackPage:myIndex: */ +static NoDbgRegParms sqInt +isValidObjStackPagemyIndex(sqInt objStackPage, sqInt myx) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!(((longAt((void *)(objStackPage))) & (classIndexMask())) == (sixtyFourBitLongsClassIndexPun()))) { + GIV(objStackInvalidBecause) = "wrong class index"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (!(((byteAt((void *)(objStackPage + (formatFieldByteOffset())))) & (formatMask())) == (sixtyFourBitIndexableFormat()))) { + GIV(objStackInvalidBecause) = "wrong format"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (!((numSlotsOfAny(objStackPage)) == ObjStackPageSlots)) { + GIV(objStackInvalidBecause) = "wrong num slots"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (!(myx == (longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord()))))))))) { + GIV(objStackInvalidBecause) = "wrong myx"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (GIV(marking) + && (!(((byteAt((void *)(objStackPage + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) { + GIV(objStackInvalidBecause) = "marking but page is unmarked"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + return 1; +} + + +/* Answer if the obj stack at stackRootIndex is valid. */ + + /* SpurMemoryManager>>#isValidObjStackPage:myIndex:firstPage: */ +static NoDbgRegParms sqInt +isValidObjStackPagemyIndexfirstPage(sqInt objStackPage, sqInt myx, sqInt isFirstPage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt freeOrNextPage; + sqInt index; + char *ns; + sqInt page; + + if (!(isValidObjStackPagemyIndex(objStackPage, myx))) { + return 0; + } + freeOrNextPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + while (freeOrNextPage != 0) { + if (!isFirstPage) { + GIV(objStackInvalidBecause) = "free page on other than first page"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (freeOrNextPage == (longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))) { + GIV(objStackInvalidBecause) = "free page = next page"; + GIV(invalidObjStackPage) = freeOrNextPage; + return 0; + } + if (!(isValidObjStackPagemyIndex(freeOrNextPage, myx))) { + /* begin stretch:cat: */ + ns = malloc(((strlen(GIV(objStackInvalidBecause))) + (14)) + 2); + strcpy(ns, GIV(objStackInvalidBecause)); + GIV(objStackInvalidBecause) = ((char *) (strcat(ns, ", on next page"))); + return 0; + } + page = longAt((void *)((freeOrNextPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + if ((page == freeOrNextPage) + || (page == objStackPage)) { + GIV(objStackInvalidBecause) = "circularity in free page list"; + GIV(invalidObjStackPage) = page; + return 0; + } + freeOrNextPage = page; + } + if (isFirstPage) { + if (!(((myx >= (1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift())))) && (myx <= (((1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift()))) + 8 /* hiddenRootSlots */) - 1))))) { + GIV(objStackInvalidBecause) = "myx out of range"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (!((longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(myx) << (shiftForWord()))))))) == objStackPage)) { + GIV(objStackInvalidBecause) = "firstPage is not root"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + } + index = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + if (!(((index >= 0) && (index <= ObjStackLimit)))) { + GIV(objStackInvalidBecause) = "bad topx"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + freeOrNextPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + if (!freeOrNextPage) { + return 1; + } + if (freeOrNextPage == objStackPage) { + GIV(objStackInvalidBecause) = "circularity in objStack page list"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + return isValidObjStackPagemyIndexfirstPage(freeOrNextPage, myx, 0); +} + + +/* Answer if the obj stack at objStackRootIndex is valid. */ + + /* SpurMemoryManager>>#isValidObjStack: */ +static NoDbgRegParms sqInt +isValidObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!((addressCouldBeObj(objStack)) + && ((numSlotsOfAny(objStack)) == ObjStackPageSlots))) { + GIV(objStackInvalidBecause) = "first page not obj or wrong size"; + GIV(invalidObjStackPage) = objStack; + return 0; + } + return isValidObjStackPagemyIndexfirstPage(objStack, longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))), 1); +} + + /* SpurMemoryManager>>#isWeakNonImm: */ +static NoDbgRegParms int +isWeakNonImm(sqInt objOop) +{ + sqInt format; + + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isWeakFormat: */ + return format == (weakArrayFormat()); +} + + +/* Answer if the argument has only weak fields that can hold oops. See + comment in formatOf: + */ + + /* SpurMemoryManager>>#isWeak: */ +sqInt +isWeak(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (isWeakNonImm(oop)); +} + + +/* Answer if the contains only indexable words or bytes (no oops). See + comment in formatOf: + */ +/* Note: Excludes CompiledMethods. */ + + /* SpurMemoryManager>>#isWordsOrBytesNonImm: */ +static NoDbgRegParms sqInt +isWordsOrBytesNonImm(sqInt objOop) +{ + sqInt format; + + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isPureBitsFormat: */ + return (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat())); +} + + +/* Answer if the contains only indexable words or bytes (no oops). See + comment in formatOf: + */ +/* Note: Excludes CompiledMethods. */ + + /* SpurMemoryManager>>#isWordsOrBytes: */ +sqInt +isWordsOrBytes(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (isWordsOrBytesNonImm(oop)); +} + + +/* Answer if the argument contains only indexable 16-bit half words or 32-bit + indexable words (no oops). + See comment in formatOf: */ + + /* SpurMemoryManager>>#isWordsOrShorts: */ +sqInt +isWordsOrShorts(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= ((firstByteFormat()) - 1)))); +} + + +/* Answer if the argument contains only indexable words (no oops). See + comment in formatOf: + */ + + /* SpurMemoryManager>>#isWords: */ +sqInt +isWords(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1)))); +} + + +/* Answer if obj is young. Require that obj is non-immediate. */ + + /* SpurMemoryManager>>#isYoungObject: */ +int +isYoungObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(isNonImmediate(objOop)); + return oopisLessThan(objOop, GIV(oldSpaceStart)); +} + + +/* Answer if oop is young. */ + + /* SpurMemoryManager>>#isYoung: */ +sqInt +isYoung(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((!(oop & (tagMask())))) + && (oopisLessThan(oop, GIV(oldSpaceStart))); +} + + +/* Answer if oop is an instance of the given class. If the class has a + (non-zero) compactClassIndex use that to speed up the check. N.B. Inlining + should result in classOop not being accessed if oop's compact class index + and compactClassIndex are non-zero. */ + + /* SpurMemoryManager>>#is:instanceOf:compactClassIndex: */ +static NoDbgRegParms sqInt +isinstanceOfcompactClassIndex(sqInt oop, sqInt classOop, sqInt compactClassIndex) +{ + sqInt ccIndex; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + if (compactClassIndex) { + return compactClassIndex == ccIndex; + } + else { + return classOop == (classAtIndex(ccIndex)); + } +} + + +/* useful for debugging... */ + + /* SpurMemoryManager>>#is:onObjStack: */ +sqInt +isonObjStack(sqInt oop, sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt nextPage; + + if (objStack == GIV(nilObj)) { + return 0; + } + assert((numSlotsOfAny(objStack)) == ObjStackPageSlots); + + /* There are four fixed slots in an obj stack, and a Topx of 0 indicates empty, so + if there were 6 slots in an oop stack, full would be 2, and the last 0-rel index is 5. */ + index = (longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) + ObjStackNextx; + while (index >= ObjStackFixedSlots) { + if (oop == (longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))))) { + return 1; + } + index -= 1; + } + nextPage = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + if (nextPage) { + if (isonObjStack(oop, nextPage)) { + return 1; + } + } + return 0; +} + + +/* Answer the object the ephemeron guards. This is its first element. */ + + /* SpurMemoryManager>>#keyOfEphemeron: */ +static NoDbgRegParms sqInt +keyOfEphemeron(sqInt objOop) +{ + assert((isNonImmediate(objOop)) + && (isObjEphemeron(objOop))); + return longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); +} + + +/* Answer the object the ephemeron guards. This is its first element. */ + + /* SpurMemoryManager>>#keyOfMaybeFiredEphemeron: */ +static NoDbgRegParms sqInt +keyOfMaybeFiredEphemeron(sqInt objOop) +{ + assert(isMaybeFiredEphemeron(objOop)); + return longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); +} + + /* SpurMemoryManager>>#knownClassAtIndex: */ +static NoDbgRegParms sqInt +knownClassAtIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(((classIndex >= 1) && (classIndex <= (classTablePageSize())))); + return longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(classIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#lastPointerFormat */ +static sqInt +lastPointerFormat(void) +{ + return 5; +} + + +/* Answer the byte offset of the last pointer field of the given object. + Works with CompiledMethods, as well as ordinary objects. + Does not examine the stack pointer of contexts to be sure to swizzle + the nils that fill contexts on snapshot. + It is invariant that on image load no object contains a forwarding + pointer, and the image contains no forwarders (see class comment). */ + + /* SpurMemoryManager>>#lastPointerOfWhileSwizzling: */ +static NoDbgRegParms sqInt +lastPointerOfWhileSwizzling(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt header; + sqInt headerSqInt; + usqInt numSlots; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt != (forwardedFormat())); + if (fmt <= 5 /* lastPointerFormat */) { + return ((((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1) * BytesPerOop) + BaseHeaderSize; + } + if (fmt < (firstCompiledMethodFormat())) { + return 0; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + return (((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) - 1) * BytesPerOop) + BaseHeaderSize; +} + + +/* Answer the byte offset of the last pointer field of the given object. + Works with CompiledMethods, as well as ordinary objects. */ + + /* SpurMemoryManager>>#lastPointerOf: */ +static NoDbgRegParms sqInt +lastPointerOf(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt fmt; + sqInt header; + sqInt headerSqInt; + usqInt numSlots; + sqInt sp; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt != (forwardedFormat())); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + return (((CtxtTempFrameStart - 1) + contextSize) * BytesPerOop) + BaseHeaderSize; + } + return ((((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1) * BytesPerOop) + BaseHeaderSize; + } + if (fmt < (firstCompiledMethodFormat())) { + return 0; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + return (((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) - 1) * BytesPerOop) + BaseHeaderSize; +} + + /* SpurMemoryManager>>#leakCheckFullGC */ +int +leakCheckFullGC(void) +{ + return ((checkForLeaks & GCModeFull) != 0); +} + + /* SpurMemoryManager>>#leakCheckNewSpaceGC */ +int +leakCheckNewSpaceGC(void) +{ + return ((checkForLeaks & GCModeNewSpace) != 0); +} + + +/* for the message send breakpoint; selectors can be immediates. */ + + /* SpurMemoryManager>>#lengthOfMaybeImmediate: */ +static NoDbgRegParms sqInt +lengthOfMaybeImmediate(sqInt oop) +{ + sqInt fmt; + usqInt numSlots; + usqInt numSlotsUsqInt; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin lengthOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + +/* Answer the number of indexable units in the given object. + For a CompiledMethod, the size of the method header (in bytes) should + be subtracted from the result. */ + + /* SpurMemoryManager>>#lengthOf: */ +static NoDbgRegParms sqInt +lengthOf(sqInt objOop) +{ + sqInt fmt; + usqInt numSlots; + usqInt numSlotsUsqInt; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + /* SpurMemoryManager>>#literalCountOfMethodHeader: */ +usqInt +literalCountOfMethodHeader(sqInt header) +{ + assert((((header) & 7) == 1)); + return ((header >> 3)) & AlternateHeaderNumLiteralsMask; +} + + /* SpurMemoryManager>>#literalCountOf: */ +usqInt +literalCountOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + return ((header >> 3)) & AlternateHeaderNumLiteralsMask; +} + + +/* This primitive is called from Smalltalk as... + loadSegmentFrom: aWordArray outPointers: anArray. */ +/* This primitive will load a binary image segment created by + primitiveStoreImageSegment. It expects the outPointer array to be of the + proper size, and the wordArray to be well formed. + It will return as its value the original array of roots, and the + segmentWordArray will become an + array of the loaded objects. If this primitive should fail, the + segmentWordArray will, sadly, have + been reduced to an unrecognizable and unusable jumble. But what more could + you have done + with it anyway? + + The primitive, if it succeeds, also becomes the segmentWordArray into the + array of loaded objects. + This allows fixing up of loaded objects directly, without nextObject, + which Spur doesn't support. */ + + /* SpurMemoryManager>>#loadImageSegmentFrom:outPointers: */ +static NoDbgRegParms NeverInline sqInt +loadImageSegmentFromoutPointers(sqInt segmentWordArray, sqInt outPointerArray) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt classOop; + sqInt classRef; + sqInt clone; + sqInt err; + sqInt errorCode; + sqInt errorCodeSqInt; + sqInt fillIdx; + sqInt followingWord; + usqInt followingWordAddress; + usqInt followingWordAddressUsqInt; + sqInt followingWordSqInt; + int hash; + sqInt i; + sqInt loadedObjectsArray; + usqInt mappedOop; + sqInt numLoadedObjects; + usqInt numOutPointers; + sqInt numSegObjs; + usqInt numSlots; + sqInt numSlotsSqInt; + sqInt objOop; + sqInt oldClone; + sqInt oop; + usqInt oopUsqInt; + sqInt scanClassTable; + usqInt segmentLimit; + sqInt segmentStart; + sqInt segVersion; + sqInt toDoLimit; + + /* begin numSlotsOf: */ + assert((classIndexOf(segmentWordArray)) > (isForwardedObjectClassIndexPun())); + segmentLimit = (((numSlots = byteAt((void *)(segmentWordArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(segmentWordArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if ((/* objectBytesForSlots: */ + (segmentLimit + ? ((segmentLimit << (shiftForWord()))) + ((segmentLimit >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize)) < (8 /* allocationUnit */ + BaseHeaderSize)) { + return PrimErrBadArgument; + } + + /* version info + one object header + Verify format. If the format is wrong, word-swap (since ImageSegment data are 32-bit longs). + If it is still wrong, undo the damage and fail. */ + segVersion = longAt((void *)(segmentWordArray + BaseHeaderSize)); + if (!(((((segVersion & 0xFFFFFF) | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */)) { + reverseBytesIn32BitWordsIn(segmentWordArray); + segVersion = longAt((void *)(segmentWordArray + BaseHeaderSize)); + if (!(((((segVersion & 0xFFFFFF) | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */)) { + reverseBytesIn32BitWordsIn(segmentWordArray); + return PrimErrBadArgument; + } + } + + /* low 3 bytes */ + segmentStart = (segmentWordArray + BaseHeaderSize) + 8 /* allocationUnit */; + segmentLimit = ((segmentLimit * BytesPerOop) + segmentWordArray) + BaseHeaderSize; + + /* Notionally reverse the Byte type objects if the data is from opposite endian machine. + Test top byte. $d on the Mac or $s on the PC. Rest of word is equal. If Spur is ever + ported to big-endian machines then the segment may have to be byte/word swapped, + but so far it only runs on little-endian machines, so for now just fail if endianness is wrong. */ + if (((((usqInt)(segVersion)) >> 24) & 0xFF) != ((((usqInt)((imageSegmentVersion()))) >> 24) & 0xFF)) { + return PrimErrBadArgument; + } + + /* Reverse the byte-type objects once + Avoid having to remember by arranging that there are no young outPointers if segment is in old space. */ + + /* begin isOldObject: */ + assert(isNonImmediate(segmentWordArray)); + if (oopisGreaterThanOrEqualTo(segmentWordArray, GIV(oldSpaceStart))) { + /* begin ensureNoNewObjectsIn: */ + hash = 0; + scanClassTable = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(outPointerArray)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(outPointerArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointerArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + oop = longAt((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isYoung: */ + ((!(oop & (tagMask())))) + && (oopisLessThan(oop, GIV(oldSpaceStart)))) { + clone = cloneInOldSpaceforPinning(oop, 0); + if (!clone) { + errorCode = PrimErrNoMemory; + goto l6; + } + if ((((hash = (long32At((void *)(oop + 4))) & (identityHashHalfWordMask()))) != 0) + && ((classOrNilAtIndex(hash)) == oop)) { + scanClassTable = 1; + } + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(oop),((longAt((void *)(oop))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(oop),(longAt((void *)(oop))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(oop)); + assert(!(isOopForwarded(clone))); + + /* begin isOldObject: */ + assert(isNonImmediate(oop)); + if (oopisGreaterThanOrEqualTo(oop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(clone & (tagMask())))) + && (oopisLessThan(clone, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(oop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord()))),clone); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(oop + (numSlotsFieldByteOffset())),1); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(outPointerArray)) + && (!(isForwarded(outPointerArray)))); + assert(validStorePointerUncheckedArgs(i, outPointerArray, clone)); + longAtput((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),clone); + } + } + if (scanClassTable) { + postBecomeScanClassTable(BecamePointerObjectFlag); + } + errorCode = 0; + /* end ensureNoNewObjectsIn: */ +l6: + if (errorCode) { + return errorCode; + } + } + + /* scan through mapping oops and validating class references. Defer entering any + class objects into the class table and/or pinning objects until a second pass. */ + + /* begin mapOopsAndValidateClassRefsFrom:to:outPointers: */ + /* begin numSlotsOf: */ + assert((classIndexOf(outPointerArray)) > (isForwardedObjectClassIndexPun())); + numOutPointers = (((numSlots = byteAt((void *)(outPointerArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointerArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numSegObjs = 0; + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(segmentStart + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? segmentStart + BaseHeaderSize + : segmentStart); + while (oopisLessThan(objOop, segmentLimit)) { + numSegObjs += 1; + + /* No object in the segment should be marked. If is is something is wrong. */ + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + errorCode = PrimErrInappropriate; + goto l8; + } + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + + /* validate the class ref, but don't update it until any internal classes have been added to the class table. */ + if (((classIndex & TopHashBit) != 0)) { + classIndex -= TopHashBit; + if (classIndex >= numOutPointers) { + errorCode = PrimErrBadIndex; + goto l8; + } + mappedOop = longAt((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)(classIndex) << (shiftForWord())))))); + hash = (long32At((void *)(mappedOop + 4))) & (identityHashHalfWordMask()); + if (!((hash == 0) + || ((hash > (lastClassIndexPun())) + && ((classOrNilAtIndex(hash)) == mappedOop)))) { + errorCode = PrimErrInappropriate; + goto l8; + } + } + else { + if (((oopUsqInt = ((classIndex - (firstClassIndexPun())) * 8 /* allocationUnit */) + segmentStart)) >= segmentLimit) { + errorCode = PrimErrBadIndex; + goto l8; + } + if ((long32At((void *)(oopUsqInt + 4))) & (identityHashHalfWordMask())) { + errorCode = PrimErrInappropriate; + goto l8; + } + } + + /* The class is contained within the segment. */ + toDoLimit = (numPointerSlotsOf(objOop)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + oopUsqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(oopUsqInt & (tagMask())))) { + if (((oopUsqInt & TopOopBit) != 0)) { + if (((oopUsqInt = (oopUsqInt - TopOopBit) / BytesPerOop)) >= numOutPointers) { + errorCode = PrimErrBadIndex; + goto l8; + } + mappedOop = longAt((void *)((outPointerArray + BaseHeaderSize) + ((oopUsqInt << (shiftForWord()))))); + } + else { + if (oopUsqInt & 7 /* (allocationUnit - 1) */) { + errorCode = PrimErrInappropriate; + goto l8; + } + if (((mappedOop = oopUsqInt + segmentStart)) >= segmentLimit) { + errorCode = PrimErrBadIndex; + goto l8; + } + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(i, objOop, mappedOop)); + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),mappedOop); + } + } + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, segmentLimit)) { + objOop = segmentLimit; + goto l7; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l7:; + } + errorCode = -numSegObjs; + /* end mapOopsAndValidateClassRefsFrom:to:outPointers: */ +l8: + if (errorCode > 0) { + return errorCode; + } + numLoadedObjects = -errorCode; + loadedObjectsArray = noInlineAllocateSlotsformatclassIndex(numLoadedObjects, arrayFormat(), ClassArrayCompactIndex); + if (!loadedObjectsArray) { + growOldSpaceByAtLeast((BaseHeaderSize + BaseHeaderSize) + (numLoadedObjects * BytesPerOop)); + loadedObjectsArray = noInlineAllocateSlotsformatclassIndex(numLoadedObjects, arrayFormat(), ClassArrayCompactIndex); + if (!loadedObjectsArray) { + return PrimErrNoMemory; + } + } + + /* Scan for classes contained in the segment, entering them into the class table. + Classes are at the front, after the root array and have the remembered bit set. */ + + /* begin enterClassesIntoClassTableFrom:to: */ + objOop = objectAfter(objectStartingAt(segmentStart)); + while ((oopisLessThan(objOop, segmentLimit)) + && (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0)) { + setIsRememberedOfto(objOop, 0); + if ((errorCodeSqInt = enterIntoClassTable(objOop))) { + oop = objOop; + objOop = objectAfter(objectStartingAt(segmentStart)); + while (oopisLessThan(objOop, oop)) { + expungeFromClassTable(objOop); + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, segmentLimit)) { + objOop = segmentLimit; + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + errorCode = errorCodeSqInt; + goto l3; + } + + /* begin objectAfter:limit: */ + followingWordAddressUsqInt = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddressUsqInt, segmentLimit)) { + objOop = segmentLimit; + goto l2; + } + followingWordSqInt = longAt((void *)(followingWordAddressUsqInt)); + objOop = ((((usqInt)(followingWordSqInt)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddressUsqInt + BaseHeaderSize + : followingWordAddressUsqInt); + /* end objectAfter:limit: */ +l2:; + } + errorCode = 0; + /* end enterClassesIntoClassTableFrom:to: */ +l3: + if (errorCode) { + return errorCode; + } + + /* Make a final pass, assigning class indices and/or pinning pinned objects and collecting the loaded objects in loadedObjectsArray */ + + /* begin assignClassIndicesAndPinFrom:to:outPointers:filling: */ + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(segmentStart + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? segmentStart + BaseHeaderSize + : segmentStart); + fillIdx = 0; + while (oopisLessThan(objOop, segmentLimit)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(loadedObjectsArray)) + && (!(isForwarded(loadedObjectsArray)))); + assert(validStorePointerUncheckedArgs(fillIdx, loadedObjectsArray, objOop)); + longAtput((void *)((loadedObjectsArray + BaseHeaderSize) + ((((usqInt)(fillIdx) << (shiftForWord()))))),objOop); + fillIdx += 1; + + /* In the segment, class indices are offset indexes into the segment data, + or into outPointers. See mapOopsFrom:to:outPointers:outHashes: and + newOutPointer:at:in:hashes:. */ + classRef = (longAt((void *)(objOop))) & (classIndexMask()); + classOop = (((classRef & TopHashBit) != 0) + ? longAt((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)((classRef - TopHashBit)) << (shiftForWord())))))) + : ((classRef - (firstClassIndexPun())) * 8 /* allocationUnit */) + segmentStart); + classIndex = (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask()); + if (!classIndex) { + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classOop)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classOop) + ? ((err = enterIntoClassTable(classOop)) + ? -err + : (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + errorCode = -classIndex; + goto l5; + } + } + assert((classIndex > (lastClassIndexPun())) + && ((classOrNilAtIndex(classIndex)) == classOop)); + + /* begin setClassIndexOf:to: */ + assert(((classIndex >= 0) && (classIndex <= (classIndexMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(classIndexMask()))) + classIndex); + if ((/* isInNewSpace: */ + (oopisLessThan(objOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(objOop, GIV(newSpaceStart)))) + && (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)) { + oldClone = cloneInOldSpaceforPinning(objOop, 1); + if (oldClone) { + setIsPinnedOfto(oldClone, 1); + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(objOop),(longAt((void *)(objOop))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(objOop)); + assert(!(isOopForwarded(oldClone))); + + /* begin isOldObject: */ + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(oldClone & (tagMask())))) + && (oopisLessThan(oldClone, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),oldClone); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),1); + } + } + } + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, segmentLimit)) { + objOop = segmentLimit; + goto l4; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l4:; + } + errorCode = 0; + /* end assignClassIndicesAndPinFrom:to:outPointers:filling: */ +l5: + if (errorCode) { + return errorCode; + } + + /* Evaporate the container, leaving the newly loaded objects in place. */ + if ((byteAt((void *)(segmentWordArray + (numSlotsFieldByteOffset())))) == (numSlotsMask())) { + if (oopisLessThan(segmentWordArray, GIV(oldSpaceStart))) { + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(segmentWordArray - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56)))); + + /* rawNumSlotsOf:put: */ + byteAtput((void *)(segmentWordArray + (numSlotsFieldByteOffset())),0); + } + else { + numSlotsSqInt = 8 /* allocationUnit */ / BytesPerOop; + + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(segmentWordArray - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56))) + numSlotsSqInt); + } + } + else { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(segmentWordArray + (numSlotsFieldByteOffset())),8 /* allocationUnit */ / BytesPerOop); + } + + /* N.B. setting the overflow slots to 1 creates a slimbridge in eden, so we also need to delete the overflow slot count in the segment itself + Finally forward the segmentWordArray to the loadedObjectsArray */ + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(segmentWordArray),((longAt((void *)(segmentWordArray))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(segmentWordArray),(longAt((void *)(segmentWordArray))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(segmentWordArray)); + assert(!(isOopForwarded(loadedObjectsArray))); + + /* begin isOldObject: */ + assert(isNonImmediate(segmentWordArray)); + if (oopisGreaterThanOrEqualTo(segmentWordArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(loadedObjectsArray & (tagMask())))) + && (oopisLessThan(loadedObjectsArray, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(segmentWordArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(segmentWordArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((segmentWordArray + BaseHeaderSize) + (0U << (shiftForWord()))),loadedObjectsArray); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(segmentWordArray + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(segmentWordArray + (numSlotsFieldByteOffset())),1); + } + runLeakCheckerFor(GCModeImageSegment); + + /* Answer the first object in the segment because this is what primitiveLoadImageSegment tests for, + even though the real result is the segmentWordArray forwarded to the loadedObjectsArray. */ + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(segmentStart + (numSlotsFieldByteOffset()))); + return (numSlots == (numSlotsMask()) + ? segmentStart + BaseHeaderSize + : segmentStart); +} + + +/* Scan the heap printing the oops of any and all objects that are instances + of aClassOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#longPrintInstancesOf: */ +void +longPrintInstancesOf(sqInt aClassOop) +{ + int classIndex; + + classIndex = (long32At((void *)(aClassOop + 4))) & (identityHashHalfWordMask()); + if (classIndex != (isFreeObjectClassIndexPun())) { + longPrintInstancesWithClassIndex(classIndex); + } +} + + +/* Scan the heap printing any and all objects whose classIndex equals the + argument. + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#longPrintInstancesWithClassIndex: */ +void +longPrintInstancesWithClassIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + longPrintOop(objOop); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + longPrintOop(objOop); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3:; +} + + +/* Scan the heap long printing the oops of any and all objects that refer to + anOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#longPrintReferencesTo: */ +void +longPrintReferencesTo(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt i; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt prntObj; + sqInt startObject; + + prntObj = 0; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + || (((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) { + if (((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + i = (literalCountOf(objSqInt)) + LiteralStart; + } + else { + if (((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + i = CtxtTempFrameStart + (fetchStackPointerOf(objSqInt)); + } + else { + /* begin numSlotsOf: */ + assert((classIndexOf(objSqInt)) > (isForwardedObjectClassIndexPun())); + i = (((numSlots = byteAt((void *)(objSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + } + } + while (((i -= 1)) >= 0) { + if (anOop == (longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + fprintf(GIV(transcript), + "%p @ %d\n", + ((void *)objSqInt), + ((int) i)); + prntObj = 1; + i = 0; + } + } + if (prntObj) { + prntObj = 0; + longPrintOop(objSqInt); + } + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + /* SpurMemoryManager>>#mapExtraRoots */ +static void +mapExtraRoots(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt oop; + + if (shouldRemapObj(GIV(specialObjectsOop))) { + GIV(validatedIntegerClassFlags) = 0; + GIV(specialObjectsOop) = remapObj(GIV(specialObjectsOop)); + } + assert(GIV(remapBufferCount) == 0); + + /* 1 to: remapBufferCount do: + [:i | | oop | + oop := remapBufferCount at: i. + ((self isImmediate: oop) or: [self isFreeObject: oop]) ifFalse: + [(self shouldRemapObj: oop) ifTrue: + [remapBuffer at: i put: (self remapObj: oop)]]]. */ + for (i = 1; i <= GIV(extraRootCount); i += 1) { + oop = (GIV(extraRoots)[i])[0]; + if (!((((oop & (tagMask())) != 0)) + || (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())))) { + if (shouldRemapObj(oop)) { + (GIV(extraRoots)[i])[0] = (remapObj(oop)); + } + } + } +} + + /* SpurMemoryManager>>#mapMournQueue */ +static NeverInline void +mapMournQueue(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt iSqInt; + sqInt mourner; + sqInt numOnThisPage; + sqInt objStackPage; + sqInt referent; + + /* begin objStack:do: */ + if (GIV(mournQueue) == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(GIV(mournQueue))); + objStackPage = GIV(mournQueue); + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + mourner = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if ((!(mourner & (tagMask())))) { + if ((!((longAt((void *)(mourner))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(mourner)); + referent = longAt((void *)((mourner + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + mourner = referent; + } + if (!(isScavengeSurvivor(mourner))) { + mourner = copyAndForwardMourner(mourner); + } + + /* we could check for change but writes are cheap with write buffers... */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objStackPage)) + && (!(isForwarded(objStackPage)))); + assert(validStorePointerUncheckedArgs(iSqInt, objStackPage, mourner)); + longAtput((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord()))))),mourner); + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l1:; +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. + Now scan, adding out pointers to the outPointersArray; all objects in + arrayOfObjects have had their first fields set to point to their copies in + segmentWordArray. Answer + the outIndex if the scan succeded. Fail if outPointers is too small and + answer -1. + + As established by copyObj:toAddr:startAt:stopAt:savedFirstFields:index:, + the marked bit is set for all objects in the segment + the remembered bit is set for all classes in the segment. + + Class indices should be set as follows (see + assignClassIndicesAndPinFrom:to:outPointers:filling:) - class indices for + classes in the segment */ + + /* SpurMemoryManager>>#mapOopsFrom:to:outPointers:outHashes: */ +static NoDbgRegParms sqInt +mapOopsFromtooutPointersoutHashes(sqInt segStart, sqInt segAddr, sqInt outPointerArray, sqInt savedOutHashes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + int hash; + sqInt heapOop; + sqInt i; + usqInt numSlots; + sqInt numSlotsSqInt; + usqInt numSlotsUsqInt; + sqInt objIndex; + sqInt objOop; + sqInt oop; + sqInt outIndex; + usqInt p; + sqInt segIndex; + sqInt toDoLimit; + usqInt toDoLimitUsqInt; + sqInt valueWord; + sqInt valueWordSqInt; + + /* objIndex is for debugging; it mirrors indices in the sender's arrayOfObjects. */ + outIndex = (objIndex = 0); + /* begin numSlotsOf: */ + assert((classIndexOf(outPointerArray)) > (isForwardedObjectClassIndexPun())); + if (((numSlots = byteAt((void *)(outPointerArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask())) { + numSlotsSqInt = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointerArray - BaseHeaderSize)))) << 8)))))) >> 8; + } + else { + numSlotsSqInt = numSlots; + } + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((outPointerArray + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1, addressAfter(outPointerArray))); + toDoLimitUsqInt = ((usqInt)(((outPointerArray + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1)); + for (p = (((usqInt)(outPointerArray + BaseHeaderSize))); p <= toDoLimitUsqInt; p += 8 /* allocationUnit */) { + longAtput((void *)(p),GIV(nilObj)); + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(segStart + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? segStart + BaseHeaderSize + : segStart); + while (oopisLessThan(objOop, segAddr)) { + heapOop = fetchClassOfNonImm(objOop); + + /* Set the classIndex of the instance. This is a segment offset (segAddr - segStart / allocationUnit) for instances of + classes within the segment, and an outPointer index (index in outPointers + TopHashBit) for classes outside the segment. */ + if ((byteAt((void *)(heapOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + oop = longAt((void *)((heapOop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(oopisGreaterThanOrEqualToandLessThan(oop, segStart, segAddr)); + segIndex = ((oop - segStart) / 8 /* allocationUnit */) + (firstClassIndexPun()); + if (((segIndex & TopHashBit) != 0)) { + return -1; + } + } + else { + hash = (long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask()); + if (/* is:outPointerClassHashFor:in:limit: */ + (((hash & TopHashBit) != 0)) + && (((hash - TopHashBit) <= outIndex) + && (heapOop == (longAt((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)((hash - TopHashBit)) << (shiftForWord())))))))))) { + segIndex = hash; + } + else { + /* begin newOutPointer:at:in:hashes: */ + if (outIndex >= ((/* begin numSlotsOf: */ + assert((classIndexOf(outPointerArray)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(outPointerArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointerArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))) { + /* no room in outPointers; fail */ + outIndex = 0; + goto l2; + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(outIndex, outPointerArray, heapOop)); + assert(isNonImmediate(outPointerArray)); + if (oopisGreaterThanOrEqualTo(outPointerArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(heapOop & (tagMask())))) + && (oopisLessThan(heapOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(outPointerArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(outPointerArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)(outIndex) << (shiftForWord()))))),heapOop); + valueWord = (long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask()); + + /* begin storeLong32:ofObject:withValue: */ + long32Atput((void *)((savedOutHashes + BaseHeaderSize) + ((((usqInt)(outIndex) << 2)))),valueWord); + long32Atput((void *)(heapOop + 4),((((long32At((void *)(heapOop + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + ((outIndex + TopHashBit) & (identityHashHalfWordMask()))); + outIndex += 1; + /* end newOutPointer:at:in:hashes: */ +l2: + if (!outIndex) { + return -1; + } + + /* no room in outPointers; fail */ + segIndex = (long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask()); + } + + /* oop is a new outPointer; allocate its oop */ + assert(((segIndex & TopHashBit) != 0)); + } + + /* oop is a class in the segment; storeImageSegmentInto:outPointers:roots: established offset is within range. + oop is an outPointer; locate or allocate its oop */ + + /* begin setClassIndexOf:to: */ + assert(((segIndex >= 0) && (segIndex <= (classIndexMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(classIndexMask()))) + segIndex); + toDoLimit = (numPointerSlotsOf(objOop)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + heapOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(heapOop & (tagMask())))) { + if ((byteAt((void *)(heapOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + oop = longAt((void *)((heapOop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(oopisGreaterThanOrEqualToandLessThan(oop, segStart, segAddr)); + oop -= segStart; + } + else { + hash = (long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask()); + if (/* is:outPointerClassHashFor:in:limit: */ + (((hash & TopHashBit) != 0)) + && (((hash - TopHashBit) <= outIndex) + && (heapOop == (longAt((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)((hash - TopHashBit)) << (shiftForWord())))))))))) { + oop = ((hash - TopHashBit) * BytesPerOop) + TopOopBit; + } + else { + /* begin newOutPointer:at:in:hashes: */ + if (outIndex >= ((/* begin numSlotsOf: */ + assert((classIndexOf(outPointerArray)) > (isForwardedObjectClassIndexPun())), + (((numSlotsUsqInt = byteAt((void *)(outPointerArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointerArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt)))) { + /* no room in outPointers; fail */ + outIndex = 0; + goto l3; + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(outIndex, outPointerArray, heapOop)); + assert(isNonImmediate(outPointerArray)); + if (oopisGreaterThanOrEqualTo(outPointerArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(heapOop & (tagMask())))) + && (oopisLessThan(heapOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(outPointerArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(outPointerArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)(outIndex) << (shiftForWord()))))),heapOop); + valueWordSqInt = (long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask()); + + /* begin storeLong32:ofObject:withValue: */ + long32Atput((void *)((savedOutHashes + BaseHeaderSize) + ((((usqInt)(outIndex) << 2)))),valueWordSqInt); + long32Atput((void *)(heapOop + 4),((((long32At((void *)(heapOop + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + ((outIndex + TopHashBit) & (identityHashHalfWordMask()))); + outIndex += 1; + /* end newOutPointer:at:in:hashes: */ +l3: + if (!outIndex) { + return -1; + } + + /* no room in outPointers; fail */ + assert((((rawHashBitsOf(heapOop)) & TopHashBit) != 0)); + oop = ((((long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask())) - TopHashBit) * BytesPerOop) + TopOopBit; + } + } + + /* oop is an object in the segment. + oop is an outPointer; locate or allocate its oop */ + + /* storeUncheckedNoAssert:ofAny:withValue: */ + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),oop); + } + } + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, segAddr)) { + objOop = segAddr; + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1: + objIndex += 1; + } + return outIndex; +} + + +/* Ensure the class of the argument is marked, pushing it on the markStack if + not already marked. + And for one-way become, which can create duplicate entries in the class + table, make sure + objOop's classIndex refers to the classObj's actual classIndex. + Note that this is recursive, but the metaclass chain should terminate + quickly. + */ + + /* SpurMemoryManager>>#markAndTraceClassOf: */ +static NoDbgRegParms void +markAndTraceClassOf(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt classObj; + sqInt classTablePage; + sqInt fieldIndex; + sqInt objStack; + int realClassIndex; + + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + + /* begin classOrNilAtIndex: */ + assert((classIndex <= (tagMask())) + || (classIndex >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classObj = GIV(nilObj); + goto l1; + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + classObj = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classOrNilAtIndex: */ +l1: + assert(objCouldBeClassObj(classObj)); + realClassIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + if ((classIndex != realClassIndex) + && (classIndex > (lastClassIndexPun()))) { + /* begin setClassIndexOf:to: */ + assert(((realClassIndex >= 0) && (realClassIndex <= (classIndexMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(classIndexMask()))) + realClassIndex); + } + if (!((byteAt((void *)(classObj + (markBitsByteOffset())))) & (1U << (markedBitByteShift())))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(classObj))); + byteAtput((void *)(classObj + (markBitsByteOffset())),(byteAt((void *)(classObj + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + markAndTraceClassOf(classObj); + objStack = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(classObj)); + if (((classObj & (tagMask())) != 0)) { + assert(objStack == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack)) + ? fetchPointerofObject(ObjStackNextx, objStack) + : objStack)))); + } + else { + assert(!((objStack == GIV(markStack)) + && (isWeakNonImm(classObj)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack != GIV(weaklingStack)) + || (isWeakNonImm(classObj))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(classObj, objStack); + } +} + + +/* An obj stack is a stack of objects stored in a hidden root slot, such + as the markStack or the ephemeronQueue. It is a linked list of + segments, with the hot end at the head of the list. It is a word object. + The stack pointer is in ObjStackTopx and 0 means empty. */ + + /* SpurMemoryManager>>#markAndTraceObjStack:andContents: */ +static NoDbgRegParms void +markAndTraceObjStackandContents(sqInt stackOrNil, sqInt markAndTraceContents) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt field; + sqInt index; + + if (stackOrNil == GIV(nilObj)) { + return; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(stackOrNil))); + byteAtput((void *)(stackOrNil + (markBitsByteOffset())),(byteAt((void *)(stackOrNil + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + field = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + if (field) { + markAndTraceObjStackandContents(field, markAndTraceContents); + } + field = stackOrNil; + while (1) { + field = longAt((void *)((field + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + if (!(field != 0)) break; + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(field))); + byteAtput((void *)(field + (markBitsByteOffset())),(byteAt((void *)(field + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + } + if (!markAndTraceContents) { + return; + } + + /* There are four fixed slots in an obj stack, and a Topx of 0 indicates empty, so + if there were 6 slots in an oop stack, full would be 2, and the last 0-rel index is 5. */ + index = (longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) + ObjStackNextx; + while (index >= ObjStackFixedSlots) { + /* begin followObjField:ofObject: */ + field = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(field)); + if ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + field = fixFollowedFieldofObjectwithInitialValue(index, stackOrNil, field); + } + if (!(((field & (tagMask())) != 0))) { + markAndTrace(field); + } + index -= 1; + } +} + + +/* Mark the argument, and all objects reachable from it, and any remaining + objects on the mark stack. Follow forwarding pointers in the scan. */ +/* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. + The assert for this is in markAndShouldScan: */ + + /* SpurMemoryManager>>#markAndTrace: */ +NeverInline void +markAndTrace(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt field; + sqInt fmt; + sqInt format; + sqInt formatSqInt; + sqInt header; + sqInt headerSqInt; + sqInt index; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt numStrongSlots; + sqInt objOop1; + sqInt objOopSqInt; + sqInt objOopSqIntSqInt; + sqInt objStack; + sqInt objStack1; + sqInt objStackSqInt; + sqInt objToScan; + sqInt scanLargeObject; + sqInt sp; + + /* begin markAndShouldScan: */ + if (((objOop & (tagMask())) != 0)) { + return; + } + + /* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. */ + assert(!(isForwarded(objOop))); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + return; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (/* isPureBitsFormat: */ + (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat()))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + markAndTraceClassOf(objOop); + } + return; + } + + /* avoid pushing non-pointer objects on the markStack. + Avoid tracing classes of non-objects on the heap, e.g. IRC caches, Sista counters. */ + if (format == (weakArrayFormat())) { + objStack = GIV(weaklingStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objOop)); + if (((objOop & (tagMask())) != 0)) { + assert(objStack == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack)) + ? fetchPointerofObject(ObjStackNextx, objStack) + : objStack)))); + } + else { + assert(!((objStack == GIV(markStack)) + && (isWeakNonImm(objOop)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack != GIV(weaklingStack)) + || (isWeakNonImm(objOop))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objOop, objStack); + return; + } + + /* push weaklings on the weakling stack to scan later */ + if ((format == (ephemeronFormat())) + && (activeAndDeferredScan(objOop))) { + return; + } + + /* Now scan the object, and any remaining objects on the mark stack. */ + + /* begin markLoopFrom: */ + numStrongSlots = 0; + objToScan = objOop; + + /* To avoid overflowing the mark stack when we encounter large objects, we + push the obj, then its numStrongSlots, and then index the object from the stack. */ + do { + if (((objToScan & (tagMask())) != 0)) { + scanLargeObject = 1; + } + else { + /* begin numStrongSlotsOfInephemeral: */ + fmt = (byteAt((void *)(objToScan + (formatFieldByteOffset())))) & (formatMask()); + assert((fmt != (ephemeronFormat())) + || ((isImmediate(keyOfEphemeron(objToScan))) + || (isMarked(keyOfEphemeron(objToScan))))); + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(objToScan)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objToScan + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objToScan - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (arrayFormat())) { + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (indexablePointersFormat())) { + if (((longAt((void *)(objToScan))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + setTraceFlagOnContextsFramesPageIfNeeded(objToScan); + + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l3; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objToScan))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l3: + numStrongSlots = CtxtTempFrameStart + contextSize; + goto l4; + } + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (weakArrayFormat())) { + objOopSqIntSqInt = fetchClassOfNonImm(objToScan); + numStrongSlots = (((longAt((void *)((objOopSqIntSqInt + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + goto l4; + } + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (forwardedFormat())) { + numStrongSlots = 1; + goto l4; + } + if (fmt < (firstCompiledMethodFormat())) { + numStrongSlots = 0; + goto l4; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objToScan)); + headerSqInt = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numStrongSlots = numLiterals + LiteralStart; + /* end numStrongSlotsOfInephemeral: */ +l4: + scanLargeObject = numStrongSlots > 64 /* traceImmediatelySlotLimit */; + } + if (scanLargeObject) { + if (((objToScan & (tagMask())) != 0)) { + index = (objToScan >> 3); + objToScan = topOfObjStack(GIV(markStack)); + } + else { + index = numStrongSlots; + markAndTraceClassOf(objToScan); + } + while (index > 0) { + index -= 1; + field = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if ((!(field & (tagMask())))) { + if ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + field = fixFollowedFieldofObjectwithInitialValue(index, objToScan, field); + } + + /* fixFollowedField: is /not/ inlined */ + + /* begin markAndShouldScan: */ + if (((field & (tagMask())) != 0)) { + goto l1; + } + + /* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. */ + assert(!(isForwarded(field))); + if ((byteAt((void *)(field + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + goto l1; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(field))); + byteAtput((void *)(field + (markBitsByteOffset())),(byteAt((void *)(field + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + format = (byteAt((void *)(field + (formatFieldByteOffset())))) & (formatMask()); + if (/* isPureBitsFormat: */ + (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat()))) { + if (((longAt((void *)(field))) & (classIndexMask())) > (lastClassIndexPun())) { + markAndTraceClassOf(field); + } + goto l1; + } + + /* avoid pushing non-pointer objects on the markStack. + Avoid tracing classes of non-objects on the heap, e.g. IRC caches, Sista counters. */ + if (format == (weakArrayFormat())) { + objStack1 = GIV(weaklingStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + goto l1; + } + + /* push weaklings on the weakling stack to scan later */ + if ((format == (ephemeronFormat())) + && (activeAndDeferredScan(field))) { + goto l1; + } + if (index > 0) { + if ((topOfObjStack(GIV(markStack))) != objToScan) { + objStack = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objToScan)); + if (((objToScan & (tagMask())) != 0)) { + assert(objStack == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack)) + ? fetchPointerofObject(ObjStackNextx, objStack) + : objStack)))); + } + else { + assert(!((objStack == GIV(markStack)) + && (isWeakNonImm(objToScan)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack != GIV(weaklingStack)) + || (isWeakNonImm(objToScan))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objToScan, objStack); + } + objOopSqInt = (((usqInt)index << 3) | 1); + objStackSqInt = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objOopSqInt)); + if (((objOopSqInt & (tagMask())) != 0)) { + assert(objStackSqInt == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStackSqInt)) + ? fetchPointerofObject(ObjStackNextx, objStackSqInt) + : objStackSqInt)))); + } + else { + assert(!((objStackSqInt == GIV(markStack)) + && (isWeakNonImm(objOopSqInt)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStackSqInt != GIV(weaklingStack)) + || (isWeakNonImm(objOopSqInt))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objOopSqInt, objStackSqInt); + } + objToScan = field; + index = -1; +l1:; + } + } + if (index >= 0) { + objToScan = popObjStack(GIV(markStack)); + if (objToScan == objOop) { + objToScan = popObjStack(GIV(markStack)); + } + } + } + else { + index = numStrongSlots; + markAndTraceClassOf(objToScan); + while (index > 0) { + index -= 1; + field = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if ((!(field & (tagMask())))) { + if ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + field = fixFollowedFieldofObjectwithInitialValue(index, objToScan, field); + } + + /* fixFollowedField: is /not/ inlined */ + + /* begin markAndShouldScan: */ + if (((field & (tagMask())) != 0)) { + goto l2; + } + + /* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. */ + assert(!(isForwarded(field))); + if ((byteAt((void *)(field + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + goto l2; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(field))); + byteAtput((void *)(field + (markBitsByteOffset())),(byteAt((void *)(field + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + formatSqInt = (byteAt((void *)(field + (formatFieldByteOffset())))) & (formatMask()); + if (/* isPureBitsFormat: */ + (formatSqInt >= (sixtyFourBitIndexableFormat())) + && (formatSqInt < (firstCompiledMethodFormat()))) { + if (((longAt((void *)(field))) & (classIndexMask())) > (lastClassIndexPun())) { + markAndTraceClassOf(field); + } + goto l2; + } + + /* avoid pushing non-pointer objects on the markStack. + Avoid tracing classes of non-objects on the heap, e.g. IRC caches, Sista counters. */ + if (formatSqInt == (weakArrayFormat())) { + objStack1 = GIV(weaklingStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + goto l2; + } + + /* push weaklings on the weakling stack to scan later */ + if ((formatSqInt == (ephemeronFormat())) + && (activeAndDeferredScan(field))) { + goto l2; + } + objStack1 = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + if (((byteAt((void *)(field + (numSlotsFieldByteOffset())))) > 64 /* traceImmediatelySlotLimit */) + && (((numStrongSlots = numStrongSlotsOfInephemeral(field))) > 64 /* traceImmediatelySlotLimit */)) { + objOop1 = (((usqInt)numStrongSlots << 3) | 1); + objStack1 = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objOop1)); + if (((objOop1 & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(objOop1)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(objOop1))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objOop1, objStack1); + } +l2:; + } + } + objToScan = popObjStack(GIV(markStack)); + } + + /* scanning a large object. scan until hitting an unmarked object, then switch to it, if any. + scanning a small object. scan, marking, pushing unmarked referents, then switch to the top of the stack. */ + } while(objToScan); +} + + +/* Go through the unscanned ephemerons, marking the inactive ones, and + removing them from the unscanned ephemerons. Answer if any inactive + ones were found. We cannot fire the ephemerons until all are found to + be active since scan-marking an inactive ephemeron later in the set may + render a previously-observed active ephemeron as inactive. */ + + /* SpurMemoryManager>>#markInactiveEphemerons */ +static sqInt +markInactiveEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt ephemeron; + sqInt field; + sqInt fmt; + sqInt format; + sqInt formatSqInt; + sqInt foundInactive; + sqInt header; + sqInt headerSqInt; + sqInt index; + sqInt key; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt numStrongSlots; + sqInt objOop; + sqInt objOop1; + sqInt objOopSqInt; + sqInt objOopSqIntSqInt; + sqInt objStack; + sqInt objStack1; + sqInt objStackSqInt; + sqInt objToScan; + usqInt ptr; + sqInt scanLargeObject; + sqInt sp; + + foundInactive = 0; + ptr = (GIV(unscannedEphemerons).start); + while (ptr <= ((GIV(unscannedEphemerons).top))) { + objOop = (ephemeron = longAt((void *)(ptr))); + + /* begin followedKeyOfEphemeron: */ + assert((isNonImmediate(objOop)) + && (isEphemeron(objOop))); + + /* begin followOopField:ofObject: */ + key = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isOopForwarded: */ + ((!(key & (tagMask())))) + && ((!((longAt((void *)(key))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + key = fixFollowedFieldofObjectwithInitialValue(0, objOop, key); + } + if ((((key & (tagMask())) != 0)) + || (((byteAt((void *)(key + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) { + foundInactive = 1; + + /* Now remove the inactive ephemeron from the set, and trace it. + Tracing it may add more ephemerons to the set. */ + if (((GIV(unscannedEphemerons).top)) > ptr) { + longAtput((void *)(ptr),longAt((void *)((GIV(unscannedEphemerons).top)))); + } + (GIV(unscannedEphemerons).top = ((GIV(unscannedEphemerons).top)) - BytesPerOop); + + /* To get markAndTrace: to trace the ephemeron we need to clear the mark bit. */ + + /* begin markLoopFrom: */ + numStrongSlots = 0; + objToScan = ephemeron; + + /* To avoid overflowing the mark stack when we encounter large objects, we + push the obj, then its numStrongSlots, and then index the object from the stack. */ + do { + if (((objToScan & (tagMask())) != 0)) { + scanLargeObject = 1; + } + else { + /* begin numStrongSlotsOfInephemeral: */ + fmt = (byteAt((void *)(objToScan + (formatFieldByteOffset())))) & (formatMask()); + assert((fmt != (ephemeronFormat())) + || ((isImmediate(keyOfEphemeron(objToScan))) + || (isMarked(keyOfEphemeron(objToScan))))); + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(objToScan)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objToScan + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objToScan - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (arrayFormat())) { + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (indexablePointersFormat())) { + if (((longAt((void *)(objToScan))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + setTraceFlagOnContextsFramesPageIfNeeded(objToScan); + + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l3; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objToScan))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l3: + numStrongSlots = CtxtTempFrameStart + contextSize; + goto l4; + } + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (weakArrayFormat())) { + objOopSqIntSqInt = fetchClassOfNonImm(objToScan); + numStrongSlots = (((longAt((void *)((objOopSqIntSqInt + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + goto l4; + } + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (forwardedFormat())) { + numStrongSlots = 1; + goto l4; + } + if (fmt < (firstCompiledMethodFormat())) { + numStrongSlots = 0; + goto l4; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objToScan)); + headerSqInt = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numStrongSlots = numLiterals + LiteralStart; + /* end numStrongSlotsOfInephemeral: */ +l4: + scanLargeObject = numStrongSlots > 64 /* traceImmediatelySlotLimit */; + } + if (scanLargeObject) { + if (((objToScan & (tagMask())) != 0)) { + index = (objToScan >> 3); + objToScan = topOfObjStack(GIV(markStack)); + } + else { + index = numStrongSlots; + markAndTraceClassOf(objToScan); + } + while (index > 0) { + index -= 1; + field = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if ((!(field & (tagMask())))) { + if ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + field = fixFollowedFieldofObjectwithInitialValue(index, objToScan, field); + } + + /* fixFollowedField: is /not/ inlined */ + + /* begin markAndShouldScan: */ + if (((field & (tagMask())) != 0)) { + goto l1; + } + + /* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. */ + assert(!(isForwarded(field))); + if ((byteAt((void *)(field + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + goto l1; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(field))); + byteAtput((void *)(field + (markBitsByteOffset())),(byteAt((void *)(field + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + format = (byteAt((void *)(field + (formatFieldByteOffset())))) & (formatMask()); + if (/* isPureBitsFormat: */ + (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat()))) { + if (((longAt((void *)(field))) & (classIndexMask())) > (lastClassIndexPun())) { + markAndTraceClassOf(field); + } + goto l1; + } + + /* avoid pushing non-pointer objects on the markStack. + Avoid tracing classes of non-objects on the heap, e.g. IRC caches, Sista counters. */ + if (format == (weakArrayFormat())) { + objStack1 = GIV(weaklingStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + goto l1; + } + + /* push weaklings on the weakling stack to scan later */ + if ((format == (ephemeronFormat())) + && (activeAndDeferredScan(field))) { + goto l1; + } + if (index > 0) { + if ((topOfObjStack(GIV(markStack))) != objToScan) { + objStack = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objToScan)); + if (((objToScan & (tagMask())) != 0)) { + assert(objStack == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack)) + ? fetchPointerofObject(ObjStackNextx, objStack) + : objStack)))); + } + else { + assert(!((objStack == GIV(markStack)) + && (isWeakNonImm(objToScan)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack != GIV(weaklingStack)) + || (isWeakNonImm(objToScan))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objToScan, objStack); + } + objOopSqInt = (((usqInt)index << 3) | 1); + objStackSqInt = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objOopSqInt)); + if (((objOopSqInt & (tagMask())) != 0)) { + assert(objStackSqInt == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStackSqInt)) + ? fetchPointerofObject(ObjStackNextx, objStackSqInt) + : objStackSqInt)))); + } + else { + assert(!((objStackSqInt == GIV(markStack)) + && (isWeakNonImm(objOopSqInt)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStackSqInt != GIV(weaklingStack)) + || (isWeakNonImm(objOopSqInt))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objOopSqInt, objStackSqInt); + } + objToScan = field; + index = -1; +l1:; + } + } + if (index >= 0) { + objToScan = popObjStack(GIV(markStack)); + if (objToScan == ephemeron) { + objToScan = popObjStack(GIV(markStack)); + } + } + } + else { + index = numStrongSlots; + markAndTraceClassOf(objToScan); + while (index > 0) { + index -= 1; + field = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if ((!(field & (tagMask())))) { + if ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + field = fixFollowedFieldofObjectwithInitialValue(index, objToScan, field); + } + + /* fixFollowedField: is /not/ inlined */ + + /* begin markAndShouldScan: */ + if (((field & (tagMask())) != 0)) { + goto l2; + } + + /* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. */ + assert(!(isForwarded(field))); + if ((byteAt((void *)(field + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + goto l2; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(field))); + byteAtput((void *)(field + (markBitsByteOffset())),(byteAt((void *)(field + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + formatSqInt = (byteAt((void *)(field + (formatFieldByteOffset())))) & (formatMask()); + if (/* isPureBitsFormat: */ + (formatSqInt >= (sixtyFourBitIndexableFormat())) + && (formatSqInt < (firstCompiledMethodFormat()))) { + if (((longAt((void *)(field))) & (classIndexMask())) > (lastClassIndexPun())) { + markAndTraceClassOf(field); + } + goto l2; + } + + /* avoid pushing non-pointer objects on the markStack. + Avoid tracing classes of non-objects on the heap, e.g. IRC caches, Sista counters. */ + if (formatSqInt == (weakArrayFormat())) { + objStack1 = GIV(weaklingStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + goto l2; + } + + /* push weaklings on the weakling stack to scan later */ + if ((formatSqInt == (ephemeronFormat())) + && (activeAndDeferredScan(field))) { + goto l2; + } + objStack1 = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + if (((byteAt((void *)(field + (numSlotsFieldByteOffset())))) > 64 /* traceImmediatelySlotLimit */) + && (((numStrongSlots = numStrongSlotsOfInephemeral(field))) > 64 /* traceImmediatelySlotLimit */)) { + objOop1 = (((usqInt)numStrongSlots << 3) | 1); + objStack1 = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objOop1)); + if (((objOop1 & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(objOop1)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(objOop1))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objOop1, objStack1); + } +l2:; + } + } + objToScan = popObjStack(GIV(markStack)); + } + + /* scanning a large object. scan until hitting an unmarked object, then switch to it, if any. + scanning a small object. scan, marking, pushing unmarked referents, then switch to the top of the stack. */ + } while(objToScan); + } + else { + ptr += BytesPerOop; + } + } + return foundInactive; +} + + +/* for profiling */ +/* Mark all accessible objects. + objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged is true if all + objects are unmarked and/or if unmarked classes shoud be removed from the + class table. */ +/* If the incremental collector is running mark bits may be set; stop it and + clear them if necessary. + */ + + /* SpurMemoryManager>>#markObjects: */ +static NoDbgRegParms NeverInline void +markObjects(sqInt objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt classOrNil; + sqInt classTablePage; + sqInt format; + sqInt i; + sqInt j; + sqInt objOop; + sqInt oop; + sqInt result; + StackPage *thePage; + sqInt toDoLimit; + + runLeakCheckerFor(GCModeFull); + + /* begin shutDownGlobalIncrementalGC: */ + if (objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged) { + assert(allObjectsUnmarked()); + } + initializeUnscannedEphemerons(); + + /* begin initializeMarkStack */ + ensureRoomOnObjStackAt(MarkStackRootIndex); + ensureRoomOnObjStackAt(WeaklingStackRootIndex); + GIV(marking) = 1; + + /* begin markAccessibleObjectsAndFireEphemerons */ + assert(GIV(marking)); + assert(validClassTableRootPages()); + assert(allBridgesMarked()); + + /* for debugging markAndTrace: set (MarkStackRecord := OrderedCollection new) + This must come first to enable stack page reclamation. It clears + the trace flags on stack pages and so must precede any marking. + Otherwise it will clear the trace flags of reached pages. */ + + /* begin initStackPageGC */ + if (GIV(stackPage)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + (thePage->trace = StackPageUnreached); + } + + /* begin markAndTraceHiddenRoots */ + markAndTraceObjStackandContents(GIV(markStack), 0); + markAndTraceObjStackandContents(GIV(weaklingStack), 0); + markAndTraceObjStackandContents(GIV(mournQueue), 1); + objOop = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + /* begin freeListsObj */ + assert((firstIndexableField(oldSpaceObjectAfter(GIV(trueObj)))) == GIV(freeLists)); + objOop = oldSpaceObjectAfter(GIV(trueObj)); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* begin isWeakNonImm: */ + format = (byteAt((void *)(GIV(classTableFirstPage) + (formatFieldByteOffset())))) & (formatMask()); + if (format == (weakArrayFormat())) { + markAndTrace(GIV(hiddenRootsObj)); + goto l1; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(GIV(hiddenRootsObj)))); + byteAtput((void *)(GIV(hiddenRootsObj) + (markBitsByteOffset())),(byteAt((void *)(GIV(hiddenRootsObj) + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + markAndTrace(GIV(classTableFirstPage)); + for (i = 1; i < GIV(numClassTablePages); i += 1) { + objOop = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + } + /* end markAndTraceHiddenRoots */ +l1: + + /* begin markAndTraceExtraRoots */ + assert(GIV(remapBufferCount) == 0); + + /* 1 to: remapBufferCount do: + [:i| + oop := remapBuffer at: i. + ((self isImmediate: oop) or: [self isFreeObject: oop]) ifFalse: + [self markAndTrace: oop]]. */ + for (i = 1; i <= GIV(extraRootCount); i += 1) { + oop = (GIV(extraRoots)[i])[0]; + if (!((((oop & (tagMask())) != 0)) + || (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())))) { + markAndTrace(oop); + } + } + assert(validClassTableRootPages()); + + /* begin markAndTraceInterpreterOops: */ + markAndTraceStackPages(1); + markAndTraceTraceLog(); + markAndTracePrimTraceLog(); + markAndTrace(GIV(specialObjectsOop)); + if (!(((GIV(newMethod) & (tagMask())) != 0))) { + markAndTrace(GIV(newMethod)); + } + + /* begin traceProfileState */ + /* begin followForwardingPointersInProfileState */ + /* begin profileStateDoUpdating: */ + if (GIV(profileProcess)) { + if ((result = ((!((longAt((void *)(GIV(profileProcess)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileProcess)) + : 0))) { + GIV(profileProcess) = result; + } + } + if (GIV(profileMethod)) { + if ((result = ((!((longAt((void *)(GIV(profileMethod)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileMethod)) + : 0))) { + GIV(profileMethod) = result; + } + } + if (GIV(profileSemaphore)) { + if ((result = ((!((longAt((void *)(GIV(profileSemaphore)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileSemaphore)) + : 0))) { + GIV(profileSemaphore) = result; + } + } + + /* begin profileStateDo: */ + if (GIV(profileProcess)) { + markAndTrace(GIV(profileProcess)); + } + if (GIV(profileMethod)) { + markAndTrace(GIV(profileMethod)); + } + if (GIV(profileSemaphore)) { + markAndTrace(GIV(profileSemaphore)); + } +# if LRPCheck + /* The longRunningPrimitiveCheckMethod (LRPCM) is sampled in an interrupt. Be very careful with it. + If longRunningPrimitiveCheckSequenceNumber (LRPCSN) = statCheckForEvents then LRPCM has + been recenty sampled, but it must be newMethod and we don't need to trace it twice. If LRPCSN + ~= statCheckForEvents then LRPCM must be some extant object and needs to be traced. */ + sqLowLevelMFence(); + if ((GIV(longRunningPrimitiveCheckMethod) != null) + && (GIV(longRunningPrimitiveCheckSequenceNumber) != GIV(statCheckForEvents))) { + if ((!((longAt((void *)(GIV(longRunningPrimitiveCheckMethod)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + GIV(longRunningPrimitiveCheckMethod) = followForwarded(GIV(longRunningPrimitiveCheckMethod)); + } + markAndTrace(GIV(longRunningPrimitiveCheckMethod)); + } + if (GIV(longRunningPrimitiveCheckSemaphore)) { + if ((!((longAt((void *)(GIV(longRunningPrimitiveCheckSemaphore)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + GIV(longRunningPrimitiveCheckSemaphore) = followForwarded(GIV(longRunningPrimitiveCheckSemaphore)); + } + markAndTrace(GIV(longRunningPrimitiveCheckSemaphore)); + } +# endif // LRPCheck + + if (GIV(tempOop)) { + markAndTrace(GIV(tempOop)); + } + if (GIV(tempOop2)) { + markAndTrace(GIV(tempOop2)); + } + + /* V3 memory manager support */ + for (i = 1; i <= GIV(remapBufferCount); i += 1) { + oop = GIV(remapBuffer)[i]; + if (!(((oop & (tagMask())) != 0))) { + markAndTrace(oop); + } + } + assert(validObjStacks()); + markWeaklingsAndMarkAndFireEphemerons(); + assert(validObjStacks()); + + /* begin expungeDuplicateAndUnmarkedClasses: */ + for (i = 1; i < GIV(numClassTablePages); i += 1) { + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNil = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + classIndex = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + assert((classOrNil == GIV(nilObj)) + || (addressCouldBeClassObj(classOrNil))); + + /* only remove a class if it is at a duplicate entry or it is unmarked and we're expunging unmarked classes. */ + if (classOrNil == GIV(nilObj)) { + if (classIndex < GIV(classTableIndex)) { + GIV(classTableIndex) = classIndex; + } + } + else { + if ((objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged + && (!(((byteAt((void *)(classOrNil + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) + || (((long32At((void *)(classOrNil + 4))) & (identityHashHalfWordMask())) != classIndex)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(classTablePage)) + && (!(isForwarded(classTablePage)))); + assert(validStorePointerUncheckedArgs(j, classTablePage, GIV(nilObj))); + longAtput((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord()))))),GIV(nilObj)); + + /* but if it is marked, it should still be in the table at its correct index. */ + assert((objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged + && (!(isMarked(classOrNil)))) + || ((classAtIndex(rawHashBitsOf(classOrNil))) == classOrNil)); + + /* If the removed class is before the classTableIndex, set the + classTableIndex to point to the empty slot so as to reuse it asap. */ + if (classIndex < GIV(classTableIndex)) { + GIV(classTableIndex) = classIndex; + } + } + } + } + } + + /* Avoid expunging the puns by not scanning the 0th page. + classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + assert(GIV(classTableIndex) >= (1U << (classTableMajorIndexShift()))); + nilUnmarkedWeaklingSlots(); + GIV(marking) = 0; +} + + +/* After the initial scan-mark is complete ephemerons can be processed. + Weaklings have accumulated on the weaklingStack, but more may be + uncovered during ephemeron processing. So trace the strong slots + of the weaklings, and as ephemerons are processed ensure any newly + reached weaklings are also traced. */ + + /* SpurMemoryManager>>#markWeaklingsAndMarkAndFireEphemerons */ +static void +markWeaklingsAndMarkAndFireEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ephemeron; + sqInt ephemeronSqInt; + sqInt field; + sqInt i; + sqInt iSqInt; + sqInt key; + sqInt keySqInt; + sqInt numOnThisPage; + sqInt numToEnumerate; + sqInt numToEnumerateOnThisPage; + sqInt numTracedWeaklings; + sqInt numUnscannedBeforeMarkAndFire; + sqInt objStackPage; + usqInt p; + sqInt size; + sqInt toDoLimit; + sqInt topIndex; + sqInt weakling; + + numTracedWeaklings = 0; + while (1) { + assert(unscannedEphemeronsContainsOnlyEphemerons()); + markAndTraceUntracedReachableStackPages(); + markAndTraceMachineCodeOfMarkedMethods(); + + /* Make sure all reached weaklings have their strong slots traced before firing ephemerons... */ + do { + /* begin markAndTraceWeaklingsFrom: */ + /* begin objStack:from:do: */ + eassert(isValidObjStack(GIV(weaklingStack))); + size = longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + objStackPage = longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + while (objStackPage != 0) { + size += ObjStackLimit; + assert((fetchPointerofObject(ObjStackTopx, objStackPage)) == ObjStackLimit); + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + numToEnumerate = size - numTracedWeaklings; + objStackPage = GIV(weaklingStack); + while (numToEnumerate > 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + numToEnumerateOnThisPage = ((numToEnumerate < numOnThisPage) ? numToEnumerate : numOnThisPage); + topIndex = (numOnThisPage + ObjStackFixedSlots) - 1; + for (iSqInt = topIndex; iSqInt >= ((topIndex - numToEnumerateOnThisPage) + 1); iSqInt += -1) { + assert(isWeak(fetchPointerofObject(iSqInt, objStackPage))); + weakling = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + assert(!((isForwarded(weakling)))); + markAndTraceClassOf(weakling); + + /* N.B. generateToByDoLimitExpression:negative:on: guards against (unsigned)0 - 1 going +ve */ + toDoLimit = (numStrongSlotsOfWeakling(weakling)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + /* begin followOopField:ofObject: */ + field = longAt((void *)((weakling + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(field & (tagMask())))) + && ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + field = fixFollowedFieldofObjectwithInitialValue(i, weakling, field); + } + if (!((((field & (tagMask())) != 0)) + || (((byteAt((void *)(field + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) { + markAndTrace(field); + } + } + } + numToEnumerate -= numToEnumerateOnThisPage; + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + numTracedWeaklings = size; + } while((sizeOfObjStack(GIV(weaklingStack))) > numTracedWeaklings); + if (((GIV(unscannedEphemerons).top)) < ((GIV(unscannedEphemerons).start))) { + markAndTraceUntracedReachableStackPages(); + markAndTraceMachineCodeOfMarkedMethods(); + freeUntracedStackPages(); + freeUnmarkedMachineCode(); + if (((GIV(unscannedEphemerons).top)) < ((GIV(unscannedEphemerons).start))) { + return; + } + } + assert(unscannedEphemeronsContainsOnlyEphemerons()); + while (markInactiveEphemerons()) { + } + assert(unscannedEphemeronsContainsOnlyActiveEphemerons()); + numUnscannedBeforeMarkAndFire = ((((sqInt) (((GIV(unscannedEphemerons).top)) - ((GIV(unscannedEphemerons).start))))) / BytesPerOop) + 1; + + /* begin fireAllUnscannedEphemerons */ + key = 0; + assert(allUnscannedEphemeronsAreActive()); + for (p = ((GIV(unscannedEphemerons).start)); p <= ((GIV(unscannedEphemerons).top)); p += BytesPerOop) { + ephemeron = longAt((void *)(p)); + if ((isEphemeron(ephemeron)) + && ((isNonImmediate((/* begin keyOfEphemeron: */ + assert((isNonImmediate(ephemeron)) + && (isObjEphemeron(ephemeron))), + (key = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))))))) + && (!(((byteAt((void *)(key + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)))) { + fireEphemeron(ephemeron); + } + } + + /* begin markAllUnscannedEphemerons */ + assert(allUnscannedEphemeronsAreActive()); + while (((GIV(unscannedEphemerons).top)) >= ((GIV(unscannedEphemerons).start))) { + ephemeronSqInt = longAt((void *)((GIV(unscannedEphemerons).start))); + longAtput((void *)((GIV(unscannedEphemerons).start)),longAt((void *)((GIV(unscannedEphemerons).top)))); + (GIV(unscannedEphemerons).top = ((GIV(unscannedEphemerons).top)) - BytesPerOop); + + /* begin followedKeyOfMaybeFiredEphemeron: */ + assert(isMaybeFiredEphemeron(ephemeronSqInt)); + keySqInt = longAt((void *)((ephemeronSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isOopForwarded: */ + ((!(keySqInt & (tagMask())))) + && ((!((longAt((void *)(keySqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + keySqInt = fixFollowedFieldofObjectwithInitialValue(0, ephemeronSqInt, keySqInt); + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(ephemeronSqInt))); + byteAtput((void *)(ephemeronSqInt + (markBitsByteOffset())),(byteAt((void *)(ephemeronSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + markAndTrace(keySqInt); + markAndTrace(ephemeronSqInt); + } + if (numUnscannedBeforeMarkAndFire == (((((sqInt) (((GIV(unscannedEphemerons).top)) - ((GIV(unscannedEphemerons).start))))) / BytesPerOop) + 1)) { + initializeUnscannedEphemerons(); + } + } +} + + /* SpurMemoryManager>>#maxIdentityHash */ +sqInt +maxIdentityHash(void) +{ + return identityHashHalfWordMask(); +} + + +/* Almost entirely arbitrary, but we dont want 1Mb bitmaps allocated in eden. + But this choice means no check for numSlots > maxSlotsForNewSpaceAlloc + for non-variable allocations. */ + + /* SpurMemoryManager>>#maxSlotsForNewSpaceAlloc */ +sqInt +maxSlotsForNewSpaceAlloc(void) +{ + return (1U << (fixedFieldsFieldWidth())) - 1; +} + + +/* Answers if the code is installed in a class instantiating objects with the + format. Used in primitive + generation to make a quick path based on where the method is installed. + This method cannot + be used as a guarantee as there can be false positive, it's just a + heuristic. Tries to interpret the last literal of the method as a behavior + (more than 3 fields, 3rd field a Smi). + If it can be interpreted as a behavior, answers if instSpec matches the + format, else answers false. */ + + /* SpurMemoryManager>>#maybeMethodClassOf:seemsToBeInstantiating: */ +sqInt +maybeMethodClassOfseemsToBeInstantiating(sqInt methodObj, sqInt format) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt instSpec; + sqInt literal; + sqInt maybeClassObj; + sqInt maybeFormat; + sqInt offset; + + /* begin methodClassOf: */ + offset = (literalCountOf(methodObj)) - 1; + literal = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = fixFollowedFieldofObjectwithInitialValue(offset + LiteralStart, methodObj, literal); + } + maybeClassObj = ((literal != GIV(nilObj)) + && (/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + ? (assert((numSlotsOf(literal)) > ValueIndex), + followFieldofObject(ValueIndex, literal)) + : GIV(nilObj)); + if (!(((byteAt((void *)(maybeClassObj + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) { + return 0; + } + if (!((numSlotsOfAny(maybeClassObj)) > InstanceSpecificationIndex)) { + return 0; + } + maybeFormat = longAt((void *)((maybeClassObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord())))))); + if (!((((maybeFormat) & 7) == 1))) { + return 0; + } + instSpec = (((usqInt)(((maybeFormat >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + return instSpec == format; +} + + +/* Answer the minimum number of additional slots to allocate in an object to + always be able to shorten it. + This is enough slots to allocate a minimum-sized object. */ + + /* SpurMemoryManager>>#minSlotsForShortening */ +sqInt +minSlotsForShortening(void) +{ + return 16 /* (allocationUnit * 2) */ / BytesPerOop; +} + + +/* N.B. may be called from machine code on the Smalltalk stack so this should + not call deeply. + */ + + /* SpurMemoryManager>>#newHashBitsOf: */ +static NoDbgRegParms usqInt +newHashBitsOf(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt hash; + + /* begin newObjectHash */ + assert(!((GIV(lastHash) == 0))); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 2)); + GIV(lastHash) = GIV(lastHash) ^ ((((GIV(lastHash) << 7))) & (identityHashHalfWordMask())); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 3)); + hash = GIV(lastHash); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(objOop + 4),((((long32At((void *)(objOop + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + return hash & (identityHashHalfWordMask()); +} + + /* SpurMemoryManager>>#newSpaceIsEmpty */ +static sqInt +newSpaceIsEmpty(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (GIV(freeStart) == (((GIV(eden)).start))) + && (GIV(pastSpaceStart) == (((GIV(pastSpace)).start))); +} + + /* SpurMemoryManager>>#nilObject */ +sqInt +nilObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(nilObj); +} + + +/* Nil the unmarked slots in the weaklings on the + weakling stack, finalizing those that lost references. + Finally, empty the weaklingStack. */ +/* for profiling */ + + /* SpurMemoryManager>>#nilUnmarkedWeaklingSlots */ +static NeverInline void +nilUnmarkedWeaklingSlots(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anyUnmarked; + sqInt i; + sqInt iSqInt; + sqInt nextNextPage; + sqInt nextPage; + sqInt numOnThisPage; + usqInt numSlots; + sqInt numToEnumerate; + sqInt numToEnumerateOnThisPage; + sqInt objStackPage; + sqInt referent; + sqInt size; + sqInt toDoLimit; + sqInt topIndex; + sqInt weakling; + + eassert(allOldMarkedWeakObjectsOnWeaklingStack()); + if (GIV(weaklingStack) == GIV(nilObj)) { + return; + } + + /* begin objStack:from:do: */ + eassert(isValidObjStack(GIV(weaklingStack))); + size = longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + objStackPage = longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + while (objStackPage != 0) { + size += ObjStackLimit; + assert((fetchPointerofObject(ObjStackTopx, objStackPage)) == ObjStackLimit); + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + numToEnumerate = size; + objStackPage = GIV(weaklingStack); + while (numToEnumerate > 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + numToEnumerateOnThisPage = ((numToEnumerate < numOnThisPage) ? numToEnumerate : numOnThisPage); + topIndex = (numOnThisPage + ObjStackFixedSlots) - 1; + for (i = topIndex; i >= ((topIndex - numToEnumerateOnThisPage) + 1); i += -1) { + assert(isWeak(fetchPointerofObject(i, objStackPage))); + weakling = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin nilUnmarkedWeaklingSlotsIn: */ + anyUnmarked = 0; + assert(allStrongSlotsOfWeaklingAreMarked(weakling)); + + /* N.B. generateToByDoLimitExpression:negative:on: guards against (unsigned)0 - 1 going +ve */ + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(weakling)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(weakling + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(weakling - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (iSqInt = (numStrongSlotsOfWeakling(weakling)); iSqInt <= toDoLimit; iSqInt += 1) { + referent = longAt((void *)((weakling + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + if (((longAt((void *)(referent))) & (classIndexMask())) == (isForwardedObjectClassIndexPun())) { + referent = fixFollowedFieldofObjectwithInitialValue(iSqInt, weakling, referent); + } + if (!((((referent & (tagMask())) != 0)) + || (((byteAt((void *)(referent + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(weakling)) + && (!(isForwarded(weakling)))); + assert(validStorePointerUncheckedArgs(iSqInt, weakling, GIV(nilObj))); + longAtput((void *)((weakling + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord()))))),GIV(nilObj)); + anyUnmarked = 1; + } + } + } + if (anyUnmarked) { + /* fireFinalization: could grow the mournQueue and if so, + additional pages must be marked to avoid being GC'ed. */ + assert(GIV(marking)); + + /* begin fireFinalization: */ + if (((imageHeaderFlags & 64) != 0)) { + queueMourner(weakling); + } + + /* begin signalFinalization: */ + forceInterruptCheck(); + GIV(pendingFinalizationSignals) += 1; + } + } + numToEnumerate -= numToEnumerateOnThisPage; + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + + /* begin emptyObjStack: */ + if (GIV(weaklingStack) == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(GIV(weaklingStack))); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(GIV(weaklingStack))) == (wordIndexableFormat())); + longAtput((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))),0); + nextPage = longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + while (nextPage != 0) { + nextNextPage = longAt((void *)((nextPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(nextPage)) == (wordIndexableFormat())); + longAtput((void *)((nextPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))))); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(nextPage)) == (wordIndexableFormat())); + longAtput((void *)((nextPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord()))))),0); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(GIV(weaklingStack))) == (wordIndexableFormat())); + longAtput((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),nextPage); + nextPage = nextNextPage; + } + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(GIV(weaklingStack))) == (wordIndexableFormat())); + longAtput((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord()))))),0); + eassert(isValidObjStack(GIV(weaklingStack))); + /* end emptyObjStack: */ +l1:; +} + + /* SpurMemoryManager>>#noCheckClassAtIndex: */ +static NoDbgRegParms sqInt +noCheckClassAtIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt fieldIndex; + + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return null; + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + +/* Push an element on an objStack. Split from push:onObjStack: for testing. */ + + /* SpurMemoryManager>>#noCheckPush:onObjStack: */ +static NoDbgRegParms sqInt +noCheckPushonObjStack(sqInt objOop, sqInt objStack) +{ + sqInt topx; + + eassert(isValidObjStack(objStack)); + + /* for debugging markAndTrace: set (MarkStackRecord := OrderedCollection new) */ + topx = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + if (topx >= ObjStackLimit) { + noCheckPushonObjStack(objOop, ensureRoomOnObjStackAt(longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))))); + } + else { + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(objStack)) == (wordIndexableFormat())); + longAtput((void *)((objStack + BaseHeaderSize) + ((((usqInt)((ObjStackFixedSlots + topx)) << (shiftForWord()))))),objOop); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(objStack)) == (wordIndexableFormat())); + longAtput((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))),topx + 1); + } + return objOop; +} + + +/* Make sure the oop at fieldIndex in anObject is not forwarded (follow the + forwarder there-in if so). Answer the (possibly followed) oop at + fieldIndex. */ + + /* SpurMemoryManager>>#noFixupFollowField:ofObject: */ +static NoDbgRegParms sqInt +noFixupFollowFieldofObject(sqInt fieldIndex, sqInt anObject) +{ + sqInt objOop; + sqInt referent; + + objOop = longAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + referent = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + objOop = referent; + } + return objOop; +} + + +/* Allocate an object with numSlots space. If there is room beneath + scavengeThreshold allocate in newSpace, otherwise alocate in oldSpace. If + there is not room in newSpace + and a scavenge is not already scheduled, schedule a scavenge. */ + + /* SpurMemoryManager>>#noInlineAllocateSlots:format:classIndex: */ +static NoDbgRegParms NeverInline sqInt +noInlineAllocateSlotsformatclassIndex(sqInt numSlots, sqInt formatField, sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newObj; + usqInt numBytes; + + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if ((((usqInt)(numSlots)) >> 56) > 0) { + return null; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + return allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, formatField, classIndex); + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + return newObj; +} + + /* SpurMemoryManager>>#noUnscannedEphemerons */ +static int +noUnscannedEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((GIV(unscannedEphemerons).top)) < ((GIV(unscannedEphemerons).start)); +} + + +/* Answer the number of indexable bytes in the given non-immediate pure bits + object with format. + */ + + /* SpurMemoryManager>>#numBytesOfBits:format: */ +static NoDbgRegParms sqInt +numBytesOfBitsformat(sqInt objOop, sqInt format) +{ + usqInt numBytes; + usqInt numSlots; + + assert((isPureBitsFormat(format)) + && ((formatOf(objOop)) == format)); + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (format < (firstShortFormat())) { + return (format <= (sixtyFourBitIndexableFormat()) + ? numBytes + : numBytes - (((format & 1) << 2))); + } + return (format < (firstByteFormat()) + ? numBytes - (((format & 3) << 1)) + : numBytes - (format & 7)); +} + + +/* Answer the number of indexable bytes in the given non-immediate + byte-indexable object. + */ + + /* SpurMemoryManager>>#numBytesOfBytes: */ +static NoDbgRegParms sqInt +numBytesOfBytes(sqInt objOop) +{ + sqInt fmt; + usqInt numSlots; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + return ((((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); +} + + +/* Answer the number of indexable bytes in the given non-immediate object. + Does not adjust the size of contexts by stackPointer. */ + + /* SpurMemoryManager>>#numBytesOf: */ +sqInt +numBytesOf(sqInt objOop) +{ + sqInt fmt; + usqInt numBytes; + usqInt numSlots; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + return numBytes - (fmt & 7); + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + return numBytes; + } + if (fmt >= (firstShortFormat())) { + return numBytes - (((fmt & 3) << 1)); + } + + /* fmt >= self firstLongFormat */ + return numBytes - (((fmt & 1) << 2)); +} + + +/* Answer the number of pointer fields in the given object. + Works with CompiledMethods, as well as ordinary objects. */ + + /* SpurMemoryManager>>#numPointerSlotsOf: */ +static NoDbgRegParms usqInt +numPointerSlotsOf(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt fmt; + sqInt header; + sqInt headerSqInt; + usqInt numLiterals; + usqInt numSlots; + sqInt sp; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin numPointerSlotsOf:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + return CtxtTempFrameStart + contextSize; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + return (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + } + if (fmt == (forwardedFormat())) { + return 1; + } + if (fmt < (firstCompiledMethodFormat())) { + return 0; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + return numLiterals + LiteralStart; +} + + /* SpurMemoryManager>>#numSlotsForBytes: */ +static NoDbgRegParms sqInt +numSlotsForBytes(sqInt numBytes) +{ + return (numBytes + (BytesPerWord - 1)) / BytesPerWord; +} + + +/* A private internal version of numSlotsOf: that is happy to be applied to + free or forwarded objects. + */ + + /* SpurMemoryManager>>#numSlotsOfAny: */ +static NoDbgRegParms usqInt +numSlotsOfAny(sqInt objOop) +{ + usqInt numSlots; + + numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + return (numSlots == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); +} + + +/* Answer the number of strong pointer fields in the given indexable pointer + object. + */ + + /* SpurMemoryManager>>#numSlotsOfIndexablePointerObj: */ +static NoDbgRegParms usqInt +numSlotsOfIndexablePointerObj(sqInt objOop) +{ + usqInt numSlots; + + assert((formatOf(objOop)) == (indexablePointersFormat())); + if (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + return CtxtTempFrameStart + (fetchStackPointerOf(objOop)); + } + else { + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + return (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + } +} + + +/* numSlotsOf: should not be applied to free or forwarded objects. */ + + /* SpurMemoryManager>>#numSlotsOf: */ +usqInt +numSlotsOf(sqInt objOop) +{ + usqInt numSlots; + + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + return (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); +} + + +/* Answer the number of strong pointer fields in the given object, + which is expected not to be an active ephemeron. + Works with CompiledMethods as well as ordinary objects. */ + + /* SpurMemoryManager>>#numStrongSlotsOfInephemeral: */ +static NoDbgRegParms sqInt +numStrongSlotsOfInephemeral(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt fmt; + sqInt header; + sqInt headerSqInt; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objOopSqInt; + sqInt sp; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + assert((fmt != (ephemeronFormat())) + || ((isImmediate(keyOfEphemeron(objOop))) + || (isMarked(keyOfEphemeron(objOop))))); + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (arrayFormat())) { + return numSlots; + } + if (fmt == (indexablePointersFormat())) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + setTraceFlagOnContextsFramesPageIfNeeded(objOop); + + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + return CtxtTempFrameStart + contextSize; + } + return numSlots; + } + if (fmt == (weakArrayFormat())) { + objOopSqInt = fetchClassOfNonImm(objOop); + + /* begin fixedFieldsOfClass: */ + return (((longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + } + return numSlots; + } + if (fmt == (forwardedFormat())) { + return 1; + } + if (fmt < (firstCompiledMethodFormat())) { + return 0; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + return numLiterals + LiteralStart; +} + + +/* Answer the number of strong pointer fields in the given weakling. */ + + /* SpurMemoryManager>>#numStrongSlotsOfWeakling: */ +static NoDbgRegParms sqInt +numStrongSlotsOfWeakling(sqInt objOop) +{ + sqInt objOopSqInt; + + assert((formatOf(objOop)) == (weakArrayFormat())); + objOopSqInt = fetchClassOfNonImm(objOop); + + /* begin fixedFieldsOfClass: */ + return (((longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); +} + + /* SpurMemoryManager>>#numUnscannedEphemerons */ +static sqInt +numUnscannedEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((((sqInt) (((GIV(unscannedEphemerons).top)) - ((GIV(unscannedEphemerons).start))))) / BytesPerOop) + 1; +} + + +/* Answer if objOop looks like a class object. WIth Spur be lenient if the + object doesn't + yet have a hash (i.e. is not yet in the classTable), and accept forwarding + pointers. + */ + + /* SpurMemoryManager>>#objCouldBeClassObj: */ +sqInt +objCouldBeClassObj(sqInt objOop) +{ + sqInt fieldOop; + + return (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + && (((numSlotsOfAny(objOop)) > InstanceSpecificationIndex) + && (((fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord()))))))), + ((addressCouldBeObj(fieldOop)) + && ((((byteAt((void *)(fieldOop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + || ((/* isOopForwarded: */ + ((!(fieldOop & (tagMask())))) + && ((!((longAt((void *)(fieldOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + && (isPointers(followForwarded(fieldOop)))))) + && (((fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord()))))))), + ((addressCouldBeObj(fieldOop)) + && ((((byteAt((void *)(fieldOop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + || ((/* isOopForwarded: */ + ((!(fieldOop & (tagMask())))) + && ((!((longAt((void *)(fieldOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + && (isPointers(followForwarded(fieldOop)))))) + && (((((longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord())))))))) & 7) == 1))))))); +} + + +/* Object parsing. + 1. all objects have at least a word following the header, for a forwarding + pointer. 2. objects with an overflow size have a preceeing word with a + saturated slotSize. If the word following + an object doesn't have a saturated size field it must be a single-header + object. If the word following + does have a saturated slotSize it must be the overflow size word. */ + + /* SpurMemoryManager>>#objectAfter: */ +sqInt +objectAfter(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + + if (oopisLessThan(objOop, GIV(oldSpaceStart))) { + if (oopisGreaterThanOrEqualToandLessThan(objOop, ((GIV(eden)).start), GIV(freeStart))) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + return GIV(freeStart); + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + } + if (oopisGreaterThanOrEqualToandLessThan(objOop, ((GIV(pastSpace)).start), GIV(pastSpaceStart))) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + return GIV(pastSpaceStart); + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + } + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(futureSurvivorStart))) { + return GIV(futureSurvivorStart); + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + } + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + return GIV(endOfMemory); + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); +} + + +/* useful for debugging */ + + /* SpurMemoryManager>>#objectBefore: */ +sqInt +objectBefore(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOopSqInt; + sqInt prev; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + prev = null; + if (oopisLessThan(objOop, GIV(oldSpaceStart))) { + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l2; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOopSqInt, GIV(freeStart))) { + if (oopisGreaterThanOrEqualTo(objOopSqInt, objOop)) { + return prev; + } + prev = objOopSqInt; + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOopSqInt = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + /* end allNewSpaceEntitiesDo: */ +l2: + return prev; + } + + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + if (oopisGreaterThanOrEqualTo(objOopSqInt, objOop)) { + return prev; + } + prev = objOopSqInt; + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3:; + } + return prev; +} + + +/* For assertions only... This ends the recursion in setting up the bridged + enumerations of new space that need objectBefore: for assertion checking. */ + + /* SpurMemoryManager>>#objectInPastSpaceBefore: */ +static NoDbgRegParms sqInt +objectInPastSpaceBefore(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt prev; + + if (GIV(pastSpaceStart) <= (((GIV(pastSpace)).start))) { + return null; + } + prev = null; + address = ((GIV(pastSpace)).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + obj = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + while (oopisLessThan(obj, GIV(pastSpaceStart))) { + if (oopisGreaterThanOrEqualTo(obj, objOop)) { + return prev; + } + prev = obj; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + obj = GIV(pastSpaceStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + return prev; +} + + +/* For enumerating objects find the header of the first object in a space. + If the object starts with an overflow size field it will start at the next + allocationUnit. c.f. numSlotsOf: */ + + /* SpurMemoryManager>>#objectStartingAt: */ +static NoDbgRegParms sqInt +objectStartingAt(sqInt address) +{ + usqInt numSlots; + + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + return (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); +} + + +/* This primitive is called from Squeak as... + arrayOfRoots uniquelyAccessibleObjects + + This primitive answers an array of the receiver and every object in its + proper tree of subParts (ie, that is not refered to from anywhere else + outside the tree). + + The primitive can fail for the following reasons with the specified + failure codes: + PrimErrNoMemory: additional allocations failed + */ + + /* SpurMemoryManager>>#objectsAccessibleFromRoots: */ +static NoDbgRegParms sqInt +objectsAccessibleFromRoots(sqInt arrayOfRootsArg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayOfObjects; + sqInt arrayOfRoots; + + runLeakCheckerFor(GCModeImageSegment); + + /* First scavenge to collect any new space garbage that refers to the graph. */ + + /* begin scavengingGC */ + scavengingGCTenuringIf(TenureByAge); + arrayOfRoots = /* updatePostScavenge: */ + ((!((longAt((void *)(arrayOfRootsArg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(arrayOfRootsArg) + : arrayOfRootsArg); + + /* Now compute the transitive closure, collecting the sequence of objects to be stored in the arrayOfObjects array. + Included in arrayOfObjects are the arrayOfRoots and all its contents. All objects have been unmarked. */ + arrayOfObjects = objectsReachableFromRoots(arrayOfRoots); + if (!arrayOfObjects) { + return (((usqInt)PrimErrNoMemory << 3) | 1); + } + + /* If objectsReachableFromRoots: answers an integer there is not enough continuous free space in which to allocate the + reachable objects. If there is sufficient free space then answer an error code to prompt a compacting GC and a retry. */ + if ((((arrayOfObjects) & 7) == 1)) { + if ((GIV(totalFreeOldSpace) - 8 /* allocationUnit */) >= ((arrayOfObjects >> 3))) { + return (((usqInt)PrimErrNeedCompaction << 3) | 1); + } + return (((usqInt)PrimErrNoMemory << 3) | 1); + } + assert(allObjectsUnmarked()); + assert(!((forwardersIn(arrayOfObjects)))); + return arrayOfObjects; +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. + Answer an Array of all the objects only reachable from the argument, an + Array of root objects, + starting with arrayOfRoots. If there is no space, answer a SmallInteger + whose value is the + number of slots required. This is used to collect the objects to include + in an image segment + on Spur, separate from creating the segment, hence simplifying the + implementation. Thanks to Igor Stasenko for this idea. */ + + /* SpurMemoryManager>>#objectsReachableFromRoots: */ +static NoDbgRegParms NeverInline sqInt +objectsReachableFromRoots(sqInt arrayOfRoots) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt count; + sqInt delta; + sqInt followingWord; + usqInt followingWordAddress; + usqInt freeChunk; + sqInt i; + sqInt iSqInt; + usqInt limit; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt oop; + sqInt oopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + usqInt ptr; + sqInt referent; + usqInt start; + usqInt startUsqInt; + sqInt toDoLimit; + sqInt toDoLimit1; + sqInt toDoLimitSqInt; + + + /* & hence start & ptr are too; limit is also because of addressAfter: */ + assert(isArray(arrayOfRoots)); + + /* Mark all objects except those only reachable from the arrayOfRoots by marking + each object in arrayOfRoots and then marking all reachable objects (from the + system roots). This leaves unmarked only objects reachable from the arrayOfRoots. + N.B. A side-effect of the marking is that all forwarders in arrayOfRoots will be followed. */ + assert(allObjectsUnmarked()); + + /* begin setGCMode: */ + GIV(gcMode) = GCModeImageSegment; + assert(!(isFreeObject(arrayOfRoots))); + byteAtput((void *)(arrayOfRoots + (markBitsByteOffset())),(byteAt((void *)(arrayOfRoots + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + toDoLimitSqInt = ((/* begin numSlotsOf: */ + assert((classIndexOf(arrayOfRoots)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(arrayOfRoots + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(arrayOfRoots - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (iSqInt = 0; iSqInt <= toDoLimitSqInt; iSqInt += 1) { + /* begin followField:ofObject: */ + oopSqInt = longAt((void *)((arrayOfRoots + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(oopSqInt & (tagMask())))) + && ((!((longAt((void *)(oopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + oopSqInt = fixFollowedFieldofObjectwithInitialValue(iSqInt, arrayOfRoots, oopSqInt); + } + if ((!(oopSqInt & (tagMask())))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oopSqInt))); + byteAtput((void *)(oopSqInt + (markBitsByteOffset())),(byteAt((void *)(oopSqInt + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + } + } + markObjects(0); + + /* After the mark phase all unreachable weak slots will have been nilled + and all active ephemerons fired. */ + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + assert(noUnscannedEphemerons()); + + /* Now unmark the roots before collecting the transitive closure of unmarked objects accessible from the roots. */ + + /* begin unmarkObjectsIn: */ + toDoLimitSqInt = ((assert((classIndexOf(arrayOfRoots)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(arrayOfRoots + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(arrayOfRoots - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (iSqInt = 0; iSqInt <= toDoLimitSqInt; iSqInt += 1) { + /* begin followField:ofObject: */ + oopSqInt = longAt((void *)((arrayOfRoots + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(oopSqInt & (tagMask())))) + && ((!((longAt((void *)(oopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + oopSqInt = fixFollowedFieldofObjectwithInitialValue(iSqInt, arrayOfRoots, oopSqInt); + } + if ((!(oopSqInt & (tagMask())))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oopSqInt))); + byteAtput((void *)(oopSqInt + (markBitsByteOffset())),(byteAt((void *)(oopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + + /* Use the largest free chunk to answer the result. */ + + /* N.B. Does /not/ update totalFreeOldSpace */ + freeChunk = allocateLargestFreeChunk(); + + /* but must update so that growth in the markStack does not cause assert fails. */ + GIV(totalFreeOldSpace) -= bytesInBody(freeChunk); + ptr = (start = freeChunk + BaseHeaderSize); + limit = addressAfter(freeChunk); + count = 0; + + /* First put the arrayOfRoots; order is important. */ + noCheckPushonObjStack(arrayOfRoots, GIV(markStack)); + + /* Now collect the roots and the transitive closure of unmarked objects from them. */ + while (!(isEmptyObjStack(GIV(markStack)))) { + objOop = popObjStack(GIV(markStack)); + assert(isMarked(objOop)); + count += 1; + if (ptr < limit) { + longAtput((void *)(ptr),objOop); + ptr += BytesPerOop; + } + oop = fetchClassOfNonImm(objOop); + if (!((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (markedBitByteShift())))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + noCheckPushonObjStack(oop, GIV(markStack)); + } + if ((((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(objOop))))) { + toDoLimit = (numSlotsOfMarriedContext(objOop)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + oop = fetchPointerofMarriedContext(i, objOop); + if (!((((oop & (tagMask())) != 0)) + || (((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + noCheckPushonObjStack(oop, GIV(markStack)); + } + } + } + else { + toDoLimit1 = (numPointerSlotsOf(objOop)) - 1; + for (i = 0; i <= toDoLimit1; i += 1) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (!((((oop & (tagMask())) != 0)) + || (((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + noCheckPushonObjStack(oop, GIV(markStack)); + } + } + } + } + + /* begin unmarkAllObjects */ + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + if ((byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) > (lastClassIndexPun())) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + else { + if (!(((longAt((void *)(objOopSqInt))) & (classIndexMask())) == (segmentBridgePun()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + startUsqInt = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (startUsqInt > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(startUsqInt + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? startUsqInt + BaseHeaderSize + : startUsqInt); + while (oopisLessThan(objOopSqInt, GIV(freeStart))) { + if ((byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) > (lastClassIndexPun())) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + else { + if (!(((longAt((void *)(objOopSqInt))) & (classIndexMask())) == (segmentBridgePun()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOopSqInt = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3: + + /* Now try and allocate the result */ + if ((count > ((ptr - start) / BytesPerOop)) + || ((limit != ptr) + && ((limit - ptr) <= 8 /* allocationUnit */))) { + freeObject(freeChunk); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeImageSegment)) == (GCCheckFreeSpace | GCModeImageSegment))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + + /* begin setGCMode: */ + GIV(gcMode) = 0; + return (((usqInt)count << 3) | 1); + } + + /* not enough room + can't split a single word + There's room; set the format, & classIndex and shorten. */ + + /* begin setFormatOf:to: */ + assert((((arrayFormat()) >= 0) && ((arrayFormat()) <= (formatMask())))); + byteAtput((void *)(freeChunk + (formatFieldByteOffset())),((byteAt((void *)(freeChunk + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (arrayFormat())); + + /* begin setClassIndexOf:to: */ + assert(((ClassArrayCompactIndex >= 0) && (ClassArrayCompactIndex <= (classIndexMask())))); + longAtput((void *)(freeChunk),((longAt((void *)(freeChunk))) & (~(usqIntptr_t)(classIndexMask()))) + ClassArrayCompactIndex); + + /* begin shorten:toIndexableSize: */ + assert((count >= 0) + && (count < (lengthOf(freeChunk)))); + delta = doShortentoIndexableSize(freeChunk, count); + assert((lengthOf(followMaybeForwarded(freeChunk))) == count); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + if ((!((longAt((void *)(freeChunk))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(freeChunk)); + referent = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + freeChunk = referent; + } + + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(freeChunk + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(freeChunk); + } + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeImageSegment)) == (GCCheckFreeSpace | GCModeImageSegment))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + runLeakCheckerFor(GCModeImageSegment); + + /* begin setGCMode: */ + GIV(gcMode) = 0; + return freeChunk; +} + + +/* Verify that the given oop is legitimate. Check address, header, and size + but not class. + */ + + /* SpurMemoryManager>>#okayOop: */ +static NoDbgRegParms sqInt +okayOop(sqInt signedOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt fmt; + usqInt oop; + usqLong unusedBits; + sqInt unusedBitsInYoungObjects; + + oop = ((usqInt) signedOop); + + /* address and size checks */ + if (((oop & (tagMask())) != 0)) { + return 1; + } + if (!(addressCouldBeObj(oop))) { + error("oop is not a valid address"); + return 0; + } + if (!(oopisLessThanOrEqualTo(addressAfter(oop), GIV(endOfMemory)))) { + error("oop size would make it extend beyond the end of memory"); + return 0; + } + + /* header type checks */ + if (!(((classIndex = (longAt((void *)(oop))) & (classIndexMask()))) >= (firstClassIndexPun()))) { + error("oop is a free chunk, or bridge, not an object"); + return 0; + } + if (((byteAt((void *)(oop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) + && ((byteAt((void *)((oop - BaseHeaderSize) + (numSlotsFieldByteOffset())))) != (numSlotsMask()))) { + error("oop header has overflow header word, but overflow word does not have a saturated numSlots field"); + return 0; + } + + /* format check */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if ((fmt == 6) || (fmt == 8)) { + error("oop has an unknown format type"); + return 0; + } + if ((fmt == (forwardedFormat())) != (classIndex == (isForwardedObjectClassIndexPun()))) { + error("oop has mis-matched format/classIndex fields; only one of them is the isForwarded value"); + return 0; + } + + /* specific header bit checks */ + unusedBits = (0x400000) | (0x40000000000000LL); + if ((long64At((void *)(oop))) & unusedBits) { + error("some unused header bits are set; should be zero"); + return 0; + } + unusedBitsInYoungObjects = ((1ULL << (greyBitShift())) | (1U << (pinnedBitShift()))) | (1U << (rememberedBitShift())); + if ((longAt((void *)(oop))) & unusedBitsInYoungObjects) { + error("some header bits unused in young objects are set; should be zero"); + return 0; + } + return 1; +} + + +/* useful for debugging */ +/* Object parsing. + 1. all objects have at least a word following the header, for a forwarding + pointer. 2. objects with an overflow size have a preceeing word with a + saturated slotSize. If the word following + an object doesn't have a saturated size field it must be a single-header + object. If the word following + does have a saturated slotSize it must be the overflow size word. */ + + /* SpurMemoryManager>>#oldSpaceObjectAfter: */ +sqInt +oldSpaceObjectAfter(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + return GIV(endOfMemory); + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); +} + + +/* in an effort to fix a compiler bug with two-way become post r3427 */ +/* Allocate two new objects, n1 & n2. Copy the contents appropriately. + Convert obj1 and obj2 + into forwarding objects pointing to n2 and n1 respectively. No need to + check if cloneObject: + succeeds because an earlier pass over objects ensured that there is enough + memory. + */ + + /* SpurMemoryManager>>#outOfPlaceBecome:and:copyHashFlag: */ +static NoDbgRegParms NeverInline void +outOfPlaceBecomeandcopyHashFlag(sqInt obj1, sqInt obj2, sqInt copyHashFlag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt clone1; + sqInt clone2; + sqInt hash; + + clone1 = (((longAt((void *)(obj1))) & (classIndexMask())) == ClassMethodContextCompactIndex + ? cloneContext(obj1) + : cloneObject(obj1)); + clone2 = (((longAt((void *)(obj2))) & (classIndexMask())) == ClassMethodContextCompactIndex + ? cloneContext(obj2) + : cloneObject(obj2)); + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(obj1 + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* begin setIsImmutableOf:to: */ + longAtput((void *)(clone1),(longAt((void *)(clone1))) | (1U << (immutableBitShift()))); + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(obj2 + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* begin setIsImmutableOf:to: */ + longAtput((void *)(clone2),(longAt((void *)(clone2))) | (1U << (immutableBitShift()))); + } + if (copyHashFlag) { + hash = (long32At((void *)(obj1 + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(clone1 + 4),((((long32At((void *)(clone1 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + hash = (long32At((void *)(obj2 + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(clone2 + 4),((((long32At((void *)(clone2 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + } + else { + hash = (long32At((void *)(obj2 + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(clone1 + 4),((((long32At((void *)(clone1 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + hash = (long32At((void *)(obj1 + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(clone2 + 4),((((long32At((void *)(clone2 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + } + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(obj1),((longAt((void *)(obj1))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(obj1),(longAt((void *)(obj1))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(obj1)); + assert(!(isOopForwarded(clone2))); + + /* begin isOldObject: */ + assert(isNonImmediate(obj1)); + if (oopisGreaterThanOrEqualTo(obj1, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(clone2 & (tagMask())))) + && (oopisLessThan(clone2, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj1 + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj1); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj1 + BaseHeaderSize) + (0U << (shiftForWord()))),clone2); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(obj1 + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(obj1 + (numSlotsFieldByteOffset())),1); + } + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(obj2),((longAt((void *)(obj2))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(obj2),(longAt((void *)(obj2))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(obj2)); + assert(!(isOopForwarded(clone1))); + + /* begin isOldObject: */ + assert(isNonImmediate(obj2)); + if (oopisGreaterThanOrEqualTo(obj2, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(clone1 & (tagMask())))) + && (oopisLessThan(clone1, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj2 + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj2); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj2 + BaseHeaderSize) + (0U << (shiftForWord()))),clone1); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(obj2 + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(obj2 + (numSlotsFieldByteOffset())),1); + } + if ((((/* begin isYoungObject: */ + assert(isNonImmediate(obj1)), + oopisLessThan(obj1, GIV(oldSpaceStart)))) != ((/* begin isYoungObject: */ + assert(isNonImmediate(clone2)), + oopisLessThan(clone2, GIV(oldSpaceStart))))) + || (((/* begin isYoungObject: */ + assert(isNonImmediate(obj2)), + oopisLessThan(obj2, GIV(oldSpaceStart)))) != ((/* begin isYoungObject: */ + assert(isNonImmediate(clone1)), + oopisLessThan(clone1, GIV(oldSpaceStart)))))) { + GIV(becomeEffectsFlags) = GIV(becomeEffectsFlags) | OldBecameNewFlag; + } +} + + +/* Attempt to pin objOop, which must not be immediate. + If the attempt succeeds answer objOop's (possibly moved) oop. + If the attempt fails, which can only occur if there is no memory, answer + 0. */ + + /* SpurMemoryManager>>#pinObject: */ +sqInt +pinObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt oldClone; + sqInt referent; + SpurSegmentInfo *seg; + + assert(isNonImmediate(objOop)); + assert(!((isForwarded(objOop)))); + + /* We choose to clone to keep pinned objects together to reduce fragmentation, + if the object is not too large, assuming that pinning is rare and that fragmentation is a bad thing. + Too large is defined as over 1mb. The size of a 640x480x4 bitmap is 1228800. */ + + /* begin isOldObject: */ + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if ((numBytesOf(objOop)) > (0x100000)) { + setIsPinnedOfto(objOop, 1); + return objOop; + } + seg = segmentContainingObj(objOop); + if ((seg->containsPinned)) { + setIsPinnedOfto(objOop, 1); + return objOop; + } + + /* begin someSegmentContainsPinned */ + for (i = 0; i < GIV(numSegments); i += 1) { + if (((GIV(segments)[i]).containsPinned)) { + goto l1; + } + } + setIsPinnedOfto(objOop, 1); + (seg->containsPinned = 1); + return objOop; +l1:; + } + oldClone = cloneInOldSpaceforPinning(objOop, 1); + if (oldClone) { + GIV(becomeEffectsFlags) = becomeEffectFlagsFor(objOop); + setIsPinnedOfto(oldClone, 1); + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(objOop),(longAt((void *)(objOop))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(objOop)); + assert(!(isOopForwarded(oldClone))); + + /* begin isOldObject: */ + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(oldClone & (tagMask())))) + && (oopisLessThan(oldClone, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),oldClone); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),1); + } + + /* begin followSpecialObjectsOop */ + if ((!((longAt((void *)(GIV(specialObjectsOop)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + GIV(validatedIntegerClassFlags) = 0; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(GIV(specialObjectsOop))); + referent = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + GIV(specialObjectsOop) = referent; + } + followForwardedObjectFieldstoDepth(GIV(specialObjectsOop), 0); + + /* begin postBecomeAction: */ + spurPostBecomeAction(GIV(becomeEffectsFlags)); + if (((GIV(becomeEffectsFlags) & BecameJittedCompiledMethodFlag) != 0)) { + freeBecomeFlaggedMethods(); + } + followMovableLiteralsAndUpdateYoungReferrers(); + + /* begin nilUncoggableMethods */ + GIV(lastCoggableInterpretedBlockMethod) = (GIV(lastUncoggableInterpretedBlockMethod) = null); + assert(kosherYoungReferrers()); + GIV(gcMode) = 0; + postBecomeScanClassTable(GIV(becomeEffectsFlags)); + GIV(becomeEffectsFlags) = 0; + } + return oldClone; +} + + /* SpurMemoryManager>>#popObjStack: */ +static NoDbgRegParms sqInt +popObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt myx; + sqInt nextPage; + sqInt top; + sqInt topx; + + eassert(isValidObjStack(objStack)); + topx = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + if (!topx) { + assert((fetchPointerofObject(ObjStackNextx, objStack)) == 0); + + /* for debugging markAndTrace: set (MarkStackRecord := OrderedCollection new) */ + return null; + } + topx -= 1; + top = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)((topx + ObjStackFixedSlots)) << (shiftForWord())))))); + assert(addressCouldBeOop(top)); + + /* for debugging markAndTrace: set (MarkStackRecord := OrderedCollection new) */ + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(objStack)) == (wordIndexableFormat())); + longAtput((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))),topx); + if ((topx == 0) + && (((nextPage = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))) != 0)) { + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(nextPage)) == (wordIndexableFormat())); + longAtput((void *)((nextPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),objStack); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(objStack)) == (wordIndexableFormat())); + longAtput((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord()))))),0); + myx = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))); + + /* begin updateRootOfObjStackAt:with: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(myx, GIV(hiddenRootsObj), nextPage)); + assert(isNonImmediate(GIV(hiddenRootsObj))); + if (oopisGreaterThanOrEqualTo(GIV(hiddenRootsObj), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(nextPage & (tagMask())))) + && (oopisLessThan(nextPage, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(hiddenRootsObj) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(hiddenRootsObj)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(myx) << (shiftForWord()))))),nextPage); + switch (myx) { + case MarkStackRootIndex: + GIV(markStack) = nextPage; + break; + case WeaklingStackRootIndex: + GIV(weaklingStack) = nextPage; + break; + case MournQueueRootIndex: + GIV(mournQueue) = nextPage; + break; + default: + error("Case not found and no otherwise clause"); + } + eassert(isValidObjStack(nextPage)); + } + else { + eassert(isValidObjStack(objStack)); + } + return top; +} + + +/* Pop and return the possibly remapped object from the remap buffer. + We support this excessence for compatibility with ObjectMemory. + Spur doesn't GC during allocation. */ + + /* SpurMemoryManager>>#popRemappableOop */ +sqInt +popRemappableOop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = GIV(remapBuffer)[GIV(remapBufferCount)]; + GIV(remapBufferCount) -= 1; + return oop; +} + + +/* Scan the class table post-become (iff an active class object was becommed) + to ensure no + forwarding pointers, and no unhashed classes exist in the class table. + + Note that one-way become can cause duplications in the class table. + So if pointer objects have been becommed, scan all pages in the class + table and eliminate ay entries that have a zero hash. + doBecome:and:copyHash: has + already ensured that any becomed class has been stored at the right index. */ + + /* SpurMemoryManager>>#postBecomeScanClassTable: */ +static NoDbgRegParms void +postBecomeScanClassTable(sqInt effectsFlags) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOrNil; + sqInt i; + sqInt j; + usqInt numSlots; + sqInt page; + sqInt referent; + sqInt toDoLimit; + + assert(validClassTableRootPages()); + if (!(((effectsFlags & BecamePointerObjectFlag) != 0))) { + return; + } + for (i = 0; i < GIV(numClassTablePages); i += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + assert(!(isForwarded(page))); + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(page)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(page + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(page - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNil = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNil != GIV(nilObj)) { + if ((!((longAt((void *)(classOrNil))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(classOrNil)); + referent = longAt((void *)((classOrNil + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + classOrNil = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(j, page, classOrNil)); + assert(isNonImmediate(page)); + if (oopisGreaterThanOrEqualTo(page, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(classOrNil & (tagMask())))) + && (oopisLessThan(classOrNil, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(page + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(page); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord()))))),classOrNil); + } + if (!((long32At((void *)(classOrNil + 4))) & (identityHashHalfWordMask()))) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(page)) + && (!(isForwarded(page)))); + assert(validStorePointerUncheckedArgs(j, page, GIV(nilObj))); + longAtput((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord()))))),GIV(nilObj)); + + /* If the removed class is before the classTableIndex, set the + classTableIndex to point to the empty slot so as to reuse it asap. */ + if ((((((usqInt)(i) << (classTableMajorIndexShift())))) + j) < GIV(classTableIndex)) { + GIV(classTableIndex) = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + } + } + } + } + } + + /* classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + assert(GIV(classTableIndex) >= (1U << (classTableMajorIndexShift()))); +} + + /* SpurMemoryManager>>#primitiveErrorTable */ +sqInt +primitiveErrorTable(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(PrimitiveErrorTableIndex) << (shiftForWord())))))); +} + + +/* Scan the heap printing the oops of any and all contexts that refer to + anOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printActivationsOf: */ +void +printActivationsOf(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (aMethodObj == (longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord()))))))))) { + printHex(objSqInt); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + print(" pc "); + printHex(longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))))); + cr(); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printBogons */ +void +printBogons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin printTheBogons: */ + print("bogon "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)GIV(bogon))); + cr(); + print("anomaly "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)GIV(anomaly))); + cr(); +} + + +/* Scan the heap printing the oops of any and all contexts that refer to + anOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printContextReferencesTo: */ +void +printContextReferencesTo(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt i; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + i = CtxtTempFrameStart + (fetchStackPointerOf(objSqInt)); + while (((i -= 1)) >= 0) { + if (anOop == (longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + printHex(objSqInt); + print(" @ "); + printNum(i); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + print(" pc "); + printHex(longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))))); + cr(); + i = 0; + } + } + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printEntity: */ +void +printEntity(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt n; + sqInt printFlags; + + printFlags = 0; + printHex(oop); + + /* begin space */ + printChar(' '); + if (!(addressCouldBeObj(oop))) { + print((((oop & (tagMask())) != 0) + ? "immediate" + : "unknown")); + return; + } + print((((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun()) + ? "free" + : (((longAt((void *)(oop))) & (classIndexMask())) == (segmentBridgePun()) + ? "bridge" + : ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? "forwarder" + : (((longAt((void *)(oop))) & (classIndexMask())) <= (lastClassIndexPun()) + ? ((printFlags = 1), + "pun/obj stack") + : ((printFlags = 1), + "object")))))); + + /* begin space */ + printChar(' '); + n = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + + /* begin printHexnpnp: */ + fprintf(GIV(transcript), + "%" PRIxSQINT "", + n); + print("/"); + n = bytesInBody(oop); + + /* begin printHexnpnp: */ + fprintf(GIV(transcript), + "%" PRIxSQINT "", + n); + print("/"); + printNum(bytesInBody(oop)); + if (printFlags) { + /* begin space */ + printChar(' '); + print((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 15 + ? "f:0" + : "f:")); + n = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin printHexnpnp: */ + fprintf(GIV(transcript), + "%" PRIxSQINT "", + n); + print(((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (greyBitByteShift())) + ? " g" + : " .")); + print(((((usqInt)((byteAt((void *)(oop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1 + ? "i" + : ".")); + print(((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (markedBitByteShift())) + ? "m" + : ".")); + print(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift())) + ? "p" + : ".")); + print(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())) + ? "r" + : ".")); + } + cr(); +} + + +/* Actually print all objects that have ephemeron format. */ + + /* SpurMemoryManager>>#printEphemerons */ +void +printEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt format; + sqInt nSoFar; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + nSoFar = 0; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + format = (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask()); + if (format == (ephemeronFormat())) { + printf("0x%" PRIxSQINT "", obj); + if (((nSoFar += 1)) >= 4) { + nSoFar = 0; + cr(); + } + else { + /* begin space */ + printChar(' '); + } + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + if (nSoFar > 0) { + cr(); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printForwarders */ +void +printForwarders(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == (isForwardedObjectClassIndexPun())) { + printHex(objOopSqInt); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOopSqInt, GIV(freeStart))) { + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == (isForwardedObjectClassIndexPun())) { + printHex(objOopSqInt); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOopSqInt = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3:; +} + + /* SpurMemoryManager>>#printForwarder:on: */ +static NoDbgRegParms void +printForwarderon(sqInt oop, FILE *aStream) +{ + fprintf(aStream, + "%p is a forwarded hdr%d slot size %" PRIuSQINT " object to %p\n", + ((void *)oop), + ((byteAt((void *)(oop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? 16 + : 8), + numSlotsOfAny(oop), + ((void *)(followForwarded(oop)))); +} + + +/* This version goes through memory, printing all free chunks. + Other versions go through the free lists. This one will show + all free chunks even if the free lists are corrupt. */ +/* useful for debugging */ + + /* SpurMemoryManager>>#printFreeChunks */ +void +printFreeChunks(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + sqInt seenNewFreeChunk; + usqInt start; + + seenNewFreeChunk = 0; + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l2; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + if (!seenNewFreeChunk) { + print("NewSpace CONTAINS FREE OBJECT(S)!!"); + cr(); + seenNewFreeChunk = 1; + } + printFreeChunk(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + /* end allNewSpaceEntitiesDo: */ +l2: + + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + printFreeChunk(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3:; + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printFreeChunk: */ +void +printFreeChunk(sqInt freeChunk) +{ + printFreeChunkprintAsTreeNode(freeChunk, 1); +} + + /* SpurMemoryManager>>#printFreeChunk:printAsTreeNode: */ +static NoDbgRegParms void +printFreeChunkprintAsTreeNode(sqInt freeChunk, sqInt printAsTreeNode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt n; + usqInt numBytes; + + numBytes = bytesInBody(freeChunk); + print("freeChunk "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)freeChunk)); + if (printAsTreeNode) { + print((freeChunk == (GIV(freeLists)[0]) + ? " + " + : " - ")); + n = addressAfter(freeChunk); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + } + print(" bytes "); + printNum(numBytes); + print(" next "); + n = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + if (!(isLilliputianSize(numBytes))) { + print(" prev "); + n = longAt((void *)((freeChunk + BaseHeaderSize) + (1U << (shiftForWord())))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + } + if ((numBytes >= 0x200 /* (numFreeLists * #allocationUnit) */) + && (printAsTreeNode)) { + print(" ^ "); + n = longAt((void *)((freeChunk + BaseHeaderSize) + (2U << (shiftForWord())))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + print(" < "); + n = longAt((void *)((freeChunk + BaseHeaderSize) + (3U << (shiftForWord())))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + print(" > "); + n = longAt((void *)((freeChunk + BaseHeaderSize) + (4U << (shiftForWord())))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + } + cr(); +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printFreeListHeads */ +void +printFreeListHeads(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt expectedMask; + sqInt i; + + expectedMask = 0; + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + printHex(GIV(freeLists)[i]); + if (GIV(freeLists)[i]) { + expectedMask += 1ULL << i; + } + if ((i + 1) % (4)) { + print(" "); + } + else { + cr(); + } + } + cr(); + print("mask: "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)GIV(freeListsMask))); + print(" expected: "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)expectedMask)); + cr(); +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printFreeList: */ +sqInt +printFreeList(sqInt chunkOrIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt freeChunk; + + if ((chunkOrIndex >= 0) + && (chunkOrIndex < 64 /* numFreeLists */)) { + return printFreeList(GIV(freeLists)[chunkOrIndex]); + } + freeChunk = chunkOrIndex; + while (freeChunk != 0) { + printFreeChunk(freeChunk); + freeChunk = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + } + return 0; +} + + /* SpurMemoryManager>>#printFreeObject:on: */ +static NoDbgRegParms void +printFreeObjecton(sqInt oop, FILE *aStream) +{ + fprintf(aStream, + "%p is a free chunk of size %" PRIdSQINT " 0th field:%p\n", + ((void *)oop), + bytesInBody(oop), + ((void *)(longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord()))))))); +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printFreeTree */ +void +printFreeTree(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + printFreeTreeChunk(GIV(freeLists)[0]); +} + + +/* Slang is blind-sided by the inlining of printFreeTreeChunk: into + printFreeTree. + */ + + /* SpurMemoryManager>>#printFreeTreeChunk: */ +static NoDbgRegParms void +printFreeTreeChunk(sqInt chunkOrZero) +{ + if (chunkOrZero > 0) { + printFreeTreeChunk(longAt((void *)((chunkOrZero + BaseHeaderSize) + (3U << (shiftForWord()))))); + printFreeChunk(chunkOrZero); + printFreeTreeChunk(longAt((void *)((chunkOrZero + BaseHeaderSize) + (4U << (shiftForWord()))))); + } +} + + +/* useful for VM debugging */ +/* N.B. No safety bounds checks!! We need to look e.g. at corpses. */ + + /* SpurMemoryManager>>#printHeaderOf: */ +void +printHeaderOf(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt n; + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)objOop)); + if ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) { + print(" hdr16 slotf "); + n = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + print(" slotc "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)(((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8))); + + /* begin space */ + printChar(' '); + } + else { + print(" hdr8 slots "); + n = numSlotsOfAny(objOop); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + } + printChar(' '); + printChar(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift())) + ? 'M' + : 'm')); + printChar(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (greyBitByteShift())) + ? 'G' + : 'g')); + printChar(((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift())) + ? 'P' + : 'p')); + printChar(((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())) + ? 'R' + : 'r')); + printChar(((((usqInt)((byteAt((void *)(objOop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1 + ? 'I' + : 'i')); + print(" hash "); + n = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + print(" fmt "); + n = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + print(" cidx "); + n = (longAt((void *)(objOop))) & (classIndexMask()); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + cr(); +} + + /* SpurMemoryManager>>#printImmediateObject:on: */ +static NoDbgRegParms void +printImmediateObjecton(sqInt oop, FILE *aStream) +{ + assert(isImmediate(oop)); + if ((((oop) & 7) == 1)) { + fprintf(aStream, + "%p=%" PRIdSQINT "\n", + ((void *)oop), + ((sqInt)((oop >> 3)))); + } + if (((oop & (characterTag())) != 0)) { + fprintf(aStream, + "%p=$%ld ($%lc)\n", + ((void *)oop), + ((long)(((((usqInt)oop))) >> (numTagBits()))), + ((wint_t) (((((usqInt)oop))) >> (numTagBits())))); + } + if (((oop & (smallFloatTag())) != 0)) { + fprintf(aStream, + "%p=%g\n", + ((void *)oop), + floatValueOf(oop)); + } +} + + +/* Scan the heap printing the oops of any and all objects that are instances + of aClassOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printInstancesOf: */ +void +printInstancesOf(sqInt aClassOop) +{ + int classIndex; + + classIndex = (long32At((void *)(aClassOop + 4))) & (identityHashHalfWordMask()); + if (classIndex != (isFreeObjectClassIndexPun())) { + printInstancesWithClassIndex(classIndex); + } +} + + +/* Scan the heap printing the oops of any and all objects whose classIndex + equals the argument. + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printInstancesWithClassIndex: */ +void +printInstancesWithClassIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + printHex(objOop); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + printHex(objOop); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3:; +} + + +/* Print the objects in the classTable that have bad hashes. */ +/* useful for debugging */ + + /* SpurMemoryManager>>#printInvalidClassTableEntries */ +void +printInvalidClassTableEntries(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOrNilSqInt; + int hash; + sqInt i; + sqInt index; + sqInt j; + sqInt page; + sqInt toDoLimit; + + hash = 0; + if (!(validClassTableRootPages())) { + print("class table invalid; cannot print"); + cr(); + return; + } + + /* begin classTableEntriesDo: */ + for (i = 0; i < GIV(numClassTablePages); i += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNilSqInt = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNilSqInt != GIV(nilObj)) { + index = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + if (((!((longAt((void *)(classOrNilSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + || ((((hash = (long32At((void *)(classOrNilSqInt + 4))) & (identityHashHalfWordMask()))) == 0) + || ((noCheckClassAtIndex(hash)) != classOrNilSqInt))) { + print("entry "); + printHex(index); + print(" oop "); + printHex(classOrNilSqInt); + print(" hash "); + printHex(hash); + print(" => "); + printHex(classAtIndex(hash)); + cr(); + } + } + } + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printMarkedOops */ +#if LLDB +void +printMarkedOops(void) +{ + printOopsSuchThat(isMarked); +} +#endif /* LLDB */ + + +/* Scan the heap printing the oops of any and all methods that implement + anOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printMethodImplementorsOf: */ +void +printMethodImplementorsOf(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && ((maybeSelectorOfMethod(objSqInt)) == anOop)) { + printHex(objSqInt); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + cr(); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + +/* Scan the heap printing the oops of any and all methods that refer to anOop */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printMethodReferencesTo: */ +void +printMethodReferencesTo(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt i; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + i = ((literalCountOf(objSqInt)) + LiteralStart) - 1; + while (((i -= 1)) >= 0) { + if (anOop == (longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + printHex(objSqInt); + print(" @ "); + printNum(i); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + cr(); + i = 0; + } + } + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + /* SpurMemoryManager>>#printMournQueue */ +void +printMournQueue(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt iSqInt; + sqInt mourner; + sqInt nSoFar; + sqInt numOnThisPage; + sqInt objStackPage; + + nSoFar = 0; + + /* begin objStack:do: */ + if (GIV(mournQueue) == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(GIV(mournQueue))); + objStackPage = GIV(mournQueue); + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + mourner = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + format = (((mourner & (tagMask())) != 0) + ? forwardedFormat() + : (byteAt((void *)(mourner + (formatFieldByteOffset())))) & (formatMask())); + fprintf(GIV(transcript), + "%p%s", + ((void *)mourner), + (format == (weakArrayFormat()) + ? " weak " + : (format == (ephemeronFormat()) + ? " eph?!" + : (format == (nonIndexablePointerFormat()) + ? " fired" + : " ???? ")))); + if (((nSoFar += 1)) >= 4) { + nSoFar = 0; + cr(); + } + else { + /* begin space */ + printChar(' '); + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l1: + if (nSoFar > 0) { + cr(); + } +} + + +/* useful for debugging */ + + /* SpurMemoryManager>>#printNewObjectsInObjStack: */ +void +printNewObjectsInObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt iSqInt; + sqInt n; + sqInt numOnThisPage; + sqInt obj; + sqInt objStackPage; + + n = 0; + + /* begin objStack:do: */ + if (objStack == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(objStack)); + objStackPage = objStack; + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + obj = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (/* isInNewSpace: */ + (oopisLessThan(obj, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(obj, GIV(newSpaceStart)))) { + /* begin space */ + printChar(' '); + printHex(obj); + if (!(((n += 1)) % 8)) { + cr(); + } + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l1: + if (n % 8) { + cr(); + } + fprintf(GIV(transcript), + "total %" PRIdSQINT "\n", + n); +} + + /* SpurMemoryManager>>#printNonPointerDataOf:on: */ +static NoDbgRegParms void +printNonPointerDataOfon(sqInt oop, FILE *aStream) +{ + sqInt elementsPerLine; + sqInt format; + sqInt index; + sqInt lastIndex; + usqInt numSlots; + usqInt numSlotsUsqInt; + + format = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert(((format >= (sixtyFourBitIndexableFormat())) && (format <= ((firstCompiledMethodFormat()) - 1)))); + + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (format <= (ephemeronFormat())) { + lastIndex = numSlots; + goto l1; + } + if (format >= (firstByteFormat())) { + lastIndex = ((numSlots << (shiftForWord()))) - (format & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (format >= (firstShortFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 1))) - (format & 3); + goto l1; + } + if (format >= (firstLongFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 2))) - (format & 1); + goto l1; + } + if (format == (sixtyFourBitIndexableFormat())) { + lastIndex = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + lastIndex = 0; + /* end lengthOf:format: */ +l1: + if (!lastIndex) { + return; + } + if (format == (sixtyFourBitIndexableFormat())) { + lastIndex = ((32 < lastIndex) ? 32 : lastIndex); + + /* 0x/16r0123456789ABCDEF x 4 = 76/80 */ + elementsPerLine = 4; + for (index = 1; index <= lastIndex; index += 1) { + fprintf(aStream, + "%18" PRIXSQINT "%c", + ((usqLong) (long64At((void *)((oop + BaseHeaderSize) + ((((usqInt)((index - 1)) << 3))))))), + (((index % elementsPerLine) == 0) + || (index == lastIndex) + ? '\n' + : ' ')); + } + return; + } + if (format < (firstShortFormat())) { + lastIndex = ((64 < lastIndex) ? 64 : lastIndex); + + /* 0x/16r12345678 x 8 = 80/88 */ + elementsPerLine = 8; + for (index = 1; index <= lastIndex; index += 1) { + fprintf(aStream, + "%10p%c", + ((void *)(((usqInt)(long32At((void *)((oop + BaseHeaderSize) + ((((usqInt)((index - 1)) << 2))))))))), + (((index % elementsPerLine) == 0) + || (index == lastIndex) + ? '\n' + : ' ')); + } + return; + } + if (format < (firstByteFormat())) { + lastIndex = ((128 < lastIndex) ? 128 : lastIndex); + + /* 0x/16r1234 x 10 = 70/80 */ + elementsPerLine = 10; + for (index = 1; index <= lastIndex; index += 1) { + fprintf(aStream, + "%6p%c", + ((void *)(shortAt((void *)((oop + BaseHeaderSize) + ((((usqInt)((index - 1)) << 1))))))), + (((index % elementsPerLine) == 0) + || (index == lastIndex) + ? '\n' + : ' ')); + } + return; + } + lastIndex = ((0x100 < lastIndex) ? 0x100 : lastIndex); + + /* 0x/16r12 x 16 = 80/96 */ + elementsPerLine = 16; + for (index = 1; index <= lastIndex; index += 1) { + fprintf(aStream, + "%4p%c", + ((void *)(byteAt((void *)((oop + BaseHeaderSize) + (index - 1))))), + (((index % elementsPerLine) == 0) + || (index == lastIndex) + ? '\n' + : ' ')); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printObjectsFrom:to: */ +void +printObjectsFromto(sqInt startAddress, sqInt endAddress) +{ + sqInt oop; + + oop = objectBefore(startAddress); + oop = (oop + ? ((objectAfter(oop)) == startAddress + ? startAddress + : oop) + : startAddress); + while (oopisLessThan(oop, endAddress)) { + if (!((((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) + || (((longAt((void *)(oop))) & (classIndexMask())) == (segmentBridgePun())))) { + printOop(oop); + } + oop = objectAfter(oop); + } +} + + +/* Scan the heap printing the oops of any and all objects whose hash equals + the argument. + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printObjectsWithHash: */ +void +printObjectsWithHash(sqInt hash) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((long32At((void *)(objOop + 4))) & (identityHashHalfWordMask())) == hash) { + shortPrintOop(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((long32At((void *)(objOop + 4))) & (identityHashHalfWordMask())) == hash) { + shortPrintOop(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3:; +} + + /* SpurMemoryManager>>#printObjStackAndContents: */ +void +printObjStackAndContents(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt total; + + /* begin printObjStack:printContents: */ + if (objStack == GIV(nilObj)) { + print("nil"); + cr(); + } + else { + total = printObjStackPagemyIndexpageTypeprintContents(objStack, longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))), ObjStackMyx, 1); + fprintf(GIV(transcript), + "total %" PRIdSQINT "\n", + total); + } +} + + +/* Print the page and all pages accessible from it. Answer the total number + of objects on all non free pages. + */ + + /* SpurMemoryManager>>#printObjStackPage:myIndex:pageType:printContents: */ +static NoDbgRegParms sqInt +printObjStackPagemyIndexpageTypeprintContents(sqInt objStackPage, sqInt myx, sqInt pageType, sqInt printContents) +{ + sqInt freeOrNextPage; + sqInt index; + int isFirstPage; + int isFreePage; + int isNextPage; + sqInt n; + sqInt page; + sqInt totalContents; + + isFirstPage = pageType == ObjStackMyx; + isNextPage = pageType == ObjStackNextx; + isFreePage = pageType == ObjStackFreex; + printObjStackPagemyIndextag(objStackPage, myx, (isFirstPage + ? "head" + : (isFreePage + ? "free" + : "next"))); + if (isFirstPage + || (isNextPage)) { + /* begin tab */ + printChar('\t'); + print("topx: "); + printNum(longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))); + print(" next: "); + printHex(longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord()))))))); + if (isFirstPage) { + print(" free: "); + printHex(longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))))); + } + cr(); + } + if (isFirstPage) { + freeOrNextPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + while (freeOrNextPage != 0) { + printObjStackPagemyIndexpageTypeprintContents(freeOrNextPage, myx, ObjStackFreex, 0); + page = longAt((void *)((freeOrNextPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + if ((page == freeOrNextPage) + || (page == objStackPage)) { + print("circularity in free page list!!"); + cr(); + page = 0; + } + freeOrNextPage = page; + } + } + freeOrNextPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + totalContents = (freeOrNextPage + ? printObjStackPagemyIndexpageTypeprintContents(freeOrNextPage, myx, ObjStackNextx, printContents) + : 0); + if (printContents) { + n = 0; + index = (longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) + ObjStackNextx; + while (index >= ObjStackFixedSlots) { + /* begin space */ + printChar(' '); + printHex(longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))))); + index -= 1; + if (((n += 1)) >= 8) { + cr(); + n = 0; + } + } + if (((longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) + ObjStackNextx) >= ObjStackFixedSlots) { + cr(); + } + } + return (isFreePage + ? 0 + : totalContents + (longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))))); +} + + /* SpurMemoryManager>>#printObjStackPage:myIndex:tag: */ +static NoDbgRegParms void +printObjStackPagemyIndextag(sqInt objStackPage, sqInt myx, char *pageType) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fprintf(GIV(transcript), + "%s %p cx %d (%d) fmt %d (%d) sz %d (%d) myx: %d (%d%s\n", + pageType, + ((void *)objStackPage), + ((int) ((longAt((void *)(objStackPage))) & (classIndexMask()))), + ((int) (sixtyFourBitLongsClassIndexPun())), + ((int) ((byteAt((void *)(objStackPage + (formatFieldByteOffset())))) & (formatMask()))), + ((int) (sixtyFourBitIndexableFormat())), + ((int) (numSlotsOfAny(objStackPage))), + ObjStackPageSlots, + ((int) (longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))))), + ((int) myx), + ((byteAt((void *)(objStackPage + (markBitsByteOffset())))) & (1U << (markedBitByteShift())) + ? ") mkd" + : ") unmkd")); +} + + +/* useful for debugging */ + + /* SpurMemoryManager>>#printObjStack: */ +void +printObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt total; + + /* begin printObjStack:printContents: */ + if (objStack == GIV(nilObj)) { + print("nil"); + cr(); + } + else { + total = printObjStackPagemyIndexpageTypeprintContents(objStack, longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))), ObjStackMyx, 0); + fprintf(GIV(transcript), + "total %" PRIdSQINT "\n", + total); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printOopsExcept: */ +NeverInline void +printOopsExcept(sqInt (*function)(sqInt)) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt n; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + n = 0; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (!(function(objOop))) { + n += 1; + printEntity(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (!(function(objOop))) { + n += 1; + printEntity(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3: + if (n > 4) { + printNum(n); + print(" objects"); + cr(); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printOopsFrom:to: */ +void +printOopsFromto(sqInt startAddress, sqInt endAddress) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firstNonEntity; + sqInt inEmptySpace; + sqInt lastNonEntity; + sqInt limit; + sqInt oop; + + firstNonEntity = 0; + oop = objectBefore(startAddress); + limit = (((((usqIntptr_t)endAddress)) < GIV(endOfMemory)) ? (((usqIntptr_t)endAddress)) : GIV(endOfMemory)); + oop = (oop + ? ((objectAfter(oop)) == startAddress + ? startAddress + : oop) + : startAddress); + inEmptySpace = 0; + while (oopisLessThan(oop, limit)) { + printEntity(oop); + while (1) { + oop = objectAfter(oop); + if (!((long64At((void *)(oop))) == 0)) break; + if (!inEmptySpace) { + inEmptySpace = 1; + firstNonEntity = oop; + } + lastNonEntity = oop; + } + if (inEmptySpace) { + inEmptySpace = 0; + print("skipped empty space from "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)firstNonEntity)); + print(" to "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)lastNonEntity)); + cr(); + oop = objectStartingAt(oop); + } + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printOopsSuchThat: */ +NeverInline void +printOopsSuchThat(sqInt (*function)(sqInt)) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt n; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + n = 0; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (function(objOop)) { + n += 1; + printEntity(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (function(objOop)) { + n += 1; + printEntity(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3: + if (n > 4) { + printNum(n); + print(" objects"); + cr(); + } +} + + +/* Scan the heap printing the oops of any and all objects that refer to anOop */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printReferencesTo: */ +void +printReferencesTo(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt contextSize; + sqInt fmt; + sqInt followingWord; + usqInt followingWordAddress; + sqInt header; + sqInt headerSqInt; + sqInt i; + usqInt numLiterals; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt sp; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l2; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objSqInt))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l2: + i = ((usqInt) (CtxtTempFrameStart + contextSize)); + goto l3; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objSqInt)) > (isForwardedObjectClassIndexPun())); + i = (((numSlots = byteAt((void *)(objSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l3; + } + if (fmt == (forwardedFormat())) { + i = 1; + goto l3; + } + if (fmt < (firstCompiledMethodFormat())) { + i = 0; + goto l3; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objSqInt)); + headerSqInt = longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + i = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l3: + while (((i -= 1)) >= 0) { + if (anOop == (longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + printHex(objSqInt); + print(" @ "); + printNum(i); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + cr(); + i = 0; + } + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } + if (isonObjStack(anOop, GIV(markStack))) { + print("on markStack"); + cr(); + } + if (isonObjStack(anOop, GIV(weaklingStack))) { + print("on weaklingStack"); + cr(); + } + if (isonObjStack(anOop, GIV(mournQueue))) { + print("in mournQueue"); + cr(); + } + if ((((byteAt((void *)(anOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0) + && (isInRememberedSet(anOop))) { + print("in rememberedSet"); + cr(); + } +} + + /* SpurMemoryManager>>#printStringDataOf:on: */ +static NoDbgRegParms void +printStringDataOfon(sqInt oop, FILE *aStream) +{ + char *buffer; + sqInt byte; + sqInt i; + sqInt limit; + sqInt n; + unsigned int *wideBuffer; + int word; + + if (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())) { + buffer = ((char *) (alloca(0x400))); + n = (i = 0); + limit = (((numBytesOfBytes(oop)) < 0x100) ? (numBytesOfBytes(oop)) : 0x100); + while (n < limit) { + byte = byteAt((void *)((oop + BaseHeaderSize) + i)); + i += 1; + if ((byte < 32) + && (byte != 9)) { + buffer[n] = '<'; + n += 1; + if ((byte == 10) + || (byte == 13)) { + if (byte == 10) { + buffer[n] = 'L'; + buffer[n + 1] = 'F'; + } + else { + buffer[n] = 'C'; + buffer[n + 1] = 'R'; + } + n += 2; + } + else { + if (byte >= 10) { + buffer[n] = ((byte / 10) + (((sqInt)'0'))); + n += 1; + } + buffer[n] = ((byte % 10) + (((sqInt)'0'))); + n += 1; + } + buffer[n] = '>'; + n += 1; + } + else { + buffer[n] = byte; + n += 1; + } + } + fprintf(aStream, + "%.*s%s\n", + ((int) n), + buffer, + ((numBytesOfBytes(oop)) > limit + ? "..." + : "")); + } + else { + assert(isWordsNonImm(oop)); + wideBuffer = ((unsigned int *) (alloca(0x1000))); + n = (i = 0); + limit = (((lengthOf(oop)) < 0x100) ? (lengthOf(oop)) : 0x100); + while (i < limit) { + word = long32At((void *)((oop + BaseHeaderSize) + ((((usqInt)(i) << 2))))); + i += 1; + if ((word < 32) + && (word != 9)) { + wideBuffer[n] = '<'; + n += 1; + if ((word == 10) + || (word == 13)) { + if (word == 10) { + wideBuffer[n] = 'L'; + wideBuffer[n + 1] = 'F'; + } + else { + wideBuffer[n] = 'C'; + wideBuffer[n + 1] = 'R'; + } + n += 2; + } + else { + if (word >= 10) { + wideBuffer[n] = ((word / 10) + (((sqInt)'0'))); + n += 1; + } + wideBuffer[n] = ((word % 10) + (((sqInt)'0'))); + n += 1; + } + wideBuffer[n] = '>'; + n += 1; + } + else { + wideBuffer[n] = word; + n += 1; + } + } + fwprintf(aStream, + L"%.*ls%s\n", + ((int) n), + wideBuffer, + ((lengthOf(oop)) > limit + ? "..." + : "")); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printUnmarkedOops */ +#if LLDB +void +printUnmarkedOops(void) +{ + printOopsExcept(isMarked); +} +#endif /* LLDB */ + + +/* Print all objects that have weak array format. */ + + /* SpurMemoryManager>>#printWeaklings */ +void +printWeaklings(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt format; + sqInt nSoFar; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + nSoFar = 0; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + format = (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask()); + if (format == (weakArrayFormat())) { + printf("0x%" PRIxSQINT "", obj); + if (((nSoFar += 1)) >= 4) { + nSoFar = 0; + cr(); + } + else { + /* begin space */ + printChar(' '); + } + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + if (nSoFar > 0) { + cr(); + } +} + + +/* Attempt to push anEphemeron on the unscanned ephemerons stack and answer + if the attempt succeeded. Note that the ephemeron stack overflowing isn't + a disaster; it simply means treating the ephemeron as strong in this GC + cycle. */ + + /* SpurMemoryManager>>#pushOnUnscannedEphemeronsStack: */ +static NoDbgRegParms sqInt +pushOnUnscannedEphemeronsStack(sqInt anEphemeron) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(isEphemeron(anEphemeron)); + if (((GIV(unscannedEphemerons).top)) >= ((GIV(unscannedEphemerons).limit))) { + return 0; + } + longAtput((void *)((GIV(unscannedEphemerons).top = ((GIV(unscannedEphemerons).top)) + BytesPerOop)),anEphemeron); + return 1; +} + + +/* Record the given object in a the remap buffer. Objects in this buffer are + remapped when a compaction occurs. This facility is used by the + interpreter to ensure that + objects in temporary variables are properly remapped. + We support this excessence for compatibility with ObjectMemory. + Spur doesn't GC during allocation. */ + + /* SpurMemoryManager>>#pushRemappableOop: */ +void +pushRemappableOop(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(addressCouldBeOop(oop)); + GIV(remapBuffer)[(GIV(remapBufferCount) += 1)] = oop; + if (!(GIV(remapBufferCount) <= RemapBufferSize)) { + error("remapBuffer overflow"); + } +} + + +/* Add the ephemeron or weak array to the queue. */ +/* If an ephemeron it shouldn't be added twice. */ + + /* SpurMemoryManager>>#queueMourner: */ +static NoDbgRegParms void +queueMourner(sqInt anEphemeronOrWeakArray) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt objStack; + + assert(!((((formatOf(anEphemeronOrWeakArray)) != (weakArrayFormat())) + && (isonObjStack(anEphemeronOrWeakArray, GIV(mournQueue)))))); + + /* it should be a weak array or an ephemeron */ + assert((isNonImmediate(anEphemeronOrWeakArray)) + && (((formatOf(anEphemeronOrWeakArray)) == (ephemeronFormat())) + || ((formatOf(anEphemeronOrWeakArray)) == (weakArrayFormat())))); + ensureRoomOnObjStackAt(MournQueueRootIndex); + + /* There is no point queueing weak arrays more than once. Note that it should be impossible + for ephemerons to be enqueued more than once since they are turned into non-ephemerons + in the relevant sender. */ + + /* begin isEphemeron: */ + assert(isNonImmediate(anEphemeronOrWeakArray)); + format = (byteAt((void *)(anEphemeronOrWeakArray + (formatFieldByteOffset())))) & (formatMask()); + if (!(format == (ephemeronFormat()))) { + if (isonObjStack(anEphemeronOrWeakArray, GIV(mournQueue))) { + return; + } + } + objStack = GIV(mournQueue); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(anEphemeronOrWeakArray)); + if (((anEphemeronOrWeakArray & (tagMask())) != 0)) { + assert(objStack == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack)) + ? fetchPointerofObject(ObjStackNextx, objStack) + : objStack)))); + } + else { + assert(!((objStack == GIV(markStack)) + && (isWeakNonImm(anEphemeronOrWeakArray)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack != GIV(weaklingStack)) + || (isWeakNonImm(anEphemeronOrWeakArray))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(anEphemeronOrWeakArray, objStack); +} + + /* SpurMemoryManager>>#rawHashBitsOf: */ +int +rawHashBitsOf(sqInt objOop) +{ + return (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()); +} + + +/* Answer the tag bits for the receiver based on the method's methodClass, if + any. These bits are extended with a bit that says that the method may have + a Context receiver, i.e. + mclass is Context or its superclasses. The absence of this bit is used to + avoid expensive store checks, etc. */ + + /* SpurMemoryManager>>#receiverTagBitsForMethod: */ +sqInt +receiverTagBitsForMethod(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classContextOrSuperclass; + sqInt literal; + sqInt methodClassOrNil; + sqInt objOop; + sqInt offset; + + /* begin methodClassOf: */ + offset = (literalCountOf(aMethodObj)) - 1; + literal = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = fixFollowedFieldofObjectwithInitialValue(offset + LiteralStart, aMethodObj, literal); + } + methodClassOrNil = ((literal != GIV(nilObj)) + && (/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + ? (assert((numSlotsOf(literal)) > ValueIndex), + followFieldofObject(ValueIndex, literal)) + : GIV(nilObj)); + if (methodClassOrNil == GIV(nilObj)) { + return (1U << (numTagBits())); + } + + /* If we don't know the methodClass be pessimal + Mixins don't necessarily have a format inst var; filter out non-integer format. */ + if (((((usqInt)((((longAt((void *)((methodClassOrNil + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) == (forwardedFormat())) { + /* begin receiverTagBitsForImmediateMethodClass: */ + if (methodClassOrNil == (longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)((smallIntegerTag())) << (shiftForWord())))))))) { + return smallIntegerTag(); + } + if (methodClassOrNil == (longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)((characterTag())) << (shiftForWord())))))))) { + return characterTag(); + } + assert(methodClassOrNil == (fetchPointerofObject(smallFloatTag(), GIV(classTableFirstPage)))); + return smallFloatTag(); + } + + /* Now check if mclass is one of Context's superclasses... */ + + /* begin knownClassAtIndex: */ + assert(((ClassMethodContextCompactIndex >= 1) && (ClassMethodContextCompactIndex <= (classTablePageSize())))); + classContextOrSuperclass = longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassMethodContextCompactIndex) << (shiftForWord())))))); + do { + if (classContextOrSuperclass == methodClassOrNil) { + return (1U << (numTagBits())); + } + + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classContextOrSuperclass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classContextOrSuperclass, objOop); + } + classContextOrSuperclass = objOop; + } while(classContextOrSuperclass != GIV(nilObj)); + return 0; +} + + +/* Ephemerons in the mourn queue will have been fired (had their + ephemeron-ness turned off), + but the ephemeron queue is not saved in the snapshot. So the snapshotted + ephemerons need to be unfired so they're still unqueued ephemerons in the + loaded image. + This must be undone post snapshot. */ + + /* SpurMemoryManager>>#refireQueuedEphemeronsPostSnapshot */ +static void +refireQueuedEphemeronsPostSnapshot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt iSqInt; + sqInt mourner; + sqInt numOnThisPage; + sqInt objStackPage; + + /* begin objStack:do: */ + if (GIV(mournQueue) == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(GIV(mournQueue))); + objStackPage = GIV(mournQueue); + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + mourner = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (((!(mourner & (tagMask())))) + && (((byteAt((void *)(mourner + (formatFieldByteOffset())))) & (formatMask())) == (ephemeronFormat()))) { + /* begin setFormatOf:to: */ + assert((((nonIndexablePointerFormat()) >= 0) && ((nonIndexablePointerFormat()) <= (formatMask())))); + byteAtput((void *)(mourner + (formatFieldByteOffset())),((byteAt((void *)(mourner + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (nonIndexablePointerFormat())); + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l1:; +} + + +/* Relocate all objStack pages that comprise objStack, including contents if + requested. + */ + + /* SpurMemoryManager>>#relocateObjStackForPlanningCompactor:andContents: */ +static NoDbgRegParms sqInt +relocateObjStackForPlanningCompactorandContents(sqInt objStack, sqInt relocateContents) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt finishIndex; + sqInt freeList; + sqInt fwd; + sqInt i; + sqInt next; + sqInt oop; + sqInt relocated; + sqInt result; + sqInt stackOrNil; + + next = 0; + if (objStack == GIV(nilObj)) { + return objStack; + } + assert((relocateContents + ? (rawHashBitsOf(objStack)) < ObjStackLimit + : (rawHashBitsOf(objStack)) == 0)); + stackOrNil = objStack; + freeList = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + while (1) { + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + + /* There are four fixed slots in an obj stack, and a Topx of 0 indicates empty, so + if there were 5 slots in an oop stack, full would be 2, and the last 0-rel index is 4. + Hence the last index is topx + fixed slots - 1, or topx + ObjStackNextx. The first + two slots, Topx and Myx are not object references. */ + next = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + finishIndex = ObjStackNextx + ((relocateContents + ? (long32At((void *)(stackOrNil + 4))) & (identityHashHalfWordMask()) + : 0)); + + /* begin relocateObjectsInHeapEntity:from:to: */ + for (i = ObjStackFreex; i <= finishIndex; i += 1) { + oop = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert(isMarked(oop)); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stackOrNil)) + && (!(isForwarded(stackOrNil)))); + assert(validStorePointerUncheckedArgs(i, stackOrNil, fwd)); + longAtput((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + relocated = (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(stackOrNil, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(stackOrNil + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)) + ? longAt((void *)((stackOrNil + BaseHeaderSize) + (0U << (shiftForWord())))) + : stackOrNil); + if (stackOrNil == objStack) { + result = relocated; + } + if (!(next != 0)) break; + stackOrNil = next; + } + while (freeList != 0) { + assert((numSlotsOfAny(freeList)) == ObjStackPageSlots); + next = longAt((void *)((freeList + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + + /* begin relocateObjectsInHeapEntity:from:to: */ + for (i = ObjStackFreex; i <= ObjStackFreex; i += 1) { + oop = longAt((void *)((freeList + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert(isMarked(oop)); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(freeList)) + && (!(isForwarded(freeList)))); + assert(validStorePointerUncheckedArgs(i, freeList, fwd)); + longAtput((void *)((freeList + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + if (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(freeList, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(freeList + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0))) { + /* fetchPointer:ofObject: */ + longAt((void *)((freeList + BaseHeaderSize) + (0U << (shiftForWord())))); + } + else { + } + freeList = next; + } + return relocated; +} + + +/* Remove the given variable location to the extra roots table. */ + + /* SpurMemoryManager>>#removeGCRoot: */ +sqInt +removeGCRoot(sqInt *varLoc) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int i; + + for (i = 1; i <= GIV(extraRootCount); i++) { + if (varLoc == (GIV(extraRoots)[i])) { + GIV(extraRoots)[i] = (GIV(extraRoots)[GIV(extraRootCount)]); + GIV(extraRootCount) -= 1; + return 1; + } + } + return 0; +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. */ + + /* SpurMemoryManager>>#return:restoringObjectsIn:savedFirstFields:and:savedHashes: */ +static NoDbgRegParms sqInt +returnrestoringObjectsInsavedFirstFieldsandsavedHashes(sqInt errCode, sqInt reachableObjectsArray, sqInt savedFirstFields, sqInt outPointersArray, sqInt savedHashes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int hash; + sqInt i; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt toDoLimit; + sqInt valuePointer; + + /* begin restoreObjectsIn:upTo:savedFirstFields: */ + /* begin numSlotsOf: */ + assert((classIndexOf(reachableObjectsArray)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(reachableObjectsArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(reachableObjectsArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + for (i = 0; i < numSlots; i += 1) { + oop = longAt((void *)((reachableObjectsArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + valuePointer = longAt((void *)((savedFirstFields + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(oop)) + && (!(isForwarded(oop)))); + assert(validStorePointerUncheckedArgs(0, oop, valuePointer)); + longAtput((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + if (oopisGreaterThanOrEqualToandLessThan(savedFirstFields, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedFirstFields); + } + + /* begin restoreObjectsIn:savedHashes: */ + toDoLimit = ((assert((classIndexOf(outPointersArray)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(outPointersArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointersArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + if (((hash = long32At((void *)((savedHashes + BaseHeaderSize) + ((((usqInt)(i) << 2))))))) > (identityHashHalfWordMask())) { + if (oopisGreaterThanOrEqualToandLessThan(savedHashes, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedHashes); + } + goto l1; + } + oop = longAt((void *)((outPointersArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(oop + 4),((((long32At((void *)(oop + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + if (oopisGreaterThanOrEqualToandLessThan(savedHashes, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedHashes); + } + /* end restoreObjectsIn:savedHashes: */ +l1: + freeObject(reachableObjectsArray); + runLeakCheckerFor(GCModeImageSegment); + assert(allObjectsUnmarked()); + return errCode; +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. */ + + /* SpurMemoryManager>>#return:restoringObjectsIn:upTo:savedFirstFields: */ +static NoDbgRegParms sqInt +returnrestoringObjectsInupTosavedFirstFields(sqInt errCode, sqInt reachableObjectsArray, sqInt limitOrTag, sqInt savedFirstFields) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt valuePointer; + + /* begin restoreObjectsIn:upTo:savedFirstFields: */ + numSlots = (limitOrTag == -1 + ? (/* begin numSlotsOf: */ + assert((classIndexOf(reachableObjectsArray)) > (isForwardedObjectClassIndexPun())), + (((numSlotsUsqInt = byteAt((void *)(reachableObjectsArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(reachableObjectsArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt)) + : limitOrTag); + for (i = 0; i < numSlots; i += 1) { + oop = longAt((void *)((reachableObjectsArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + valuePointer = longAt((void *)((savedFirstFields + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(oop)) + && (!(isForwarded(oop)))); + assert(validStorePointerUncheckedArgs(0, oop, valuePointer)); + longAtput((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + if (oopisGreaterThanOrEqualToandLessThan(savedFirstFields, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedFirstFields); + } + freeObject(reachableObjectsArray); + runLeakCheckerFor(GCModeImageSegment); + assert(allObjectsUnmarked()); + return errCode; +} + + +/* This exists to get around a compiler bug in Apple LLVM version 7.0.0 + (clang-700.1.76) that was avoiding the second comparison of segVersion + after the first byte swap. */ + + /* SpurMemoryManager>>#reverseBytesIn32BitWordsIn: */ +static NoDbgRegParms NeverInline void +reverseBytesIn32BitWordsIn(sqInt segmentWordArray) +{ + sqInt addr; + sqInt stopAddr; + + stopAddr = addressAfter(segmentWordArray); + + /* begin reverseBytesIn32BitWordsFrom:to: */ + addr = segmentWordArray + BaseHeaderSize; + while (oopisLessThan(addr, stopAddr)) { + long32Atput((void *)(addr),SQ_SWAP_4_BYTES((long32At((void *)(addr))))); + addr += 4; + } +} + + +/* Check free space integrity by setting bits in the map corresponding to all + free space objects + and checking tat no pointer field refers to a free object. anOopOrNil is + provided to filter-out + the as-yet-to-be initialized object in + primitiveShallowCopy/primitiveClone. + */ + + /* SpurMemoryManager>>#runLeakCheckerForFreeSpace:ignoring: */ +static NoDbgRegParms void +runLeakCheckerForFreeSpaceignoring(sqInt gcModes, sqInt anOopOrNil) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (((gcModes & GCCheckFreeSpace) != 0)) { + reverseDisplayFromto(16, 19); + clearLeakMapAndMapAccessibleFreeSpace(); + GIV(freeSpaceCheckOopToIgnore) = anOopOrNil; + asserta(checkHeapFreeSpaceIntegrity()); + GIV(freeSpaceCheckOopToIgnore) = null; + } +} + + /* SpurMemoryManager>>#runLeakCheckerFor: */ +static NoDbgRegParms void +runLeakCheckerFor(sqInt gcModes) +{ + /* begin inLineRunLeakCheckerFor:excludeUnmarkedObjs:classIndicesShouldBeValid: */ + if (((gcModes & checkForLeaks) != 0)) { + if (((gcModes & GCModeFull) != 0)) { + reverseDisplayFromto(0, 7); + } + else { + reverseDisplayFromto(8, 15); + } + clearLeakMapAndMapAccessibleObjects(); + asserta(checkHeapIntegrityclassIndicesShouldBeValid(0, 1)); + asserta((checkInterpreterIntegrity()) == 0); + asserta(checkStackIntegrity()); + asserta(checkCodeIntegrity(gcModes)); + if (((gcModes & GCCheckFreeSpace) != 0)) { + clearLeakMapAndMapAccessibleFreeSpace(); + asserta(checkHeapFreeSpaceIntegrity()); + } + } + return; +} + + /* SpurMemoryManager>>#runLeakCheckerFor:excludeUnmarkedObjs:classIndicesShouldBeValid: */ +static NoDbgRegParms void +runLeakCheckerForexcludeUnmarkedObjsclassIndicesShouldBeValid(sqInt gcModes, sqInt excludeUnmarkedObjs, sqInt classIndicesShouldBeValid) +{ + /* begin inLineRunLeakCheckerFor:excludeUnmarkedObjs:classIndicesShouldBeValid: */ + if (((gcModes & checkForLeaks) != 0)) { + if (((gcModes & GCModeFull) != 0)) { + reverseDisplayFromto(0, 7); + } + else { + reverseDisplayFromto(8, 15); + } + clearLeakMapAndMapAccessibleObjects(); + asserta(checkHeapIntegrityclassIndicesShouldBeValid(excludeUnmarkedObjs, classIndicesShouldBeValid)); + asserta((checkInterpreterIntegrity()) == 0); + asserta(checkStackIntegrity()); + asserta(checkCodeIntegrity(gcModes)); + if (((gcModes & GCCheckFreeSpace) != 0)) { + clearLeakMapAndMapAccessibleFreeSpace(); + asserta(checkHeapFreeSpaceIntegrity()); + } + } +} + + /* SpurMemoryManager>>#safePrintStringOf: */ +static NoDbgRegParms void +safePrintStringOf(sqInt oop) +{ + printStringOf(/* followMaybeForwarded: */ + (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + ? followForwarded(oop) + : oop)); + return; +} + + +/* Run the scavenger. */ + + /* SpurMemoryManager>>#scavengingGCTenuringIf: */ +static NoDbgRegParms void +scavengingGCTenuringIf(sqInt tenuringCriterion) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt n; + sqInt probe; + + assert(GIV(remapBufferCount) == 0); + if (!(asserta(((((GIV(eden)).limit)) - GIV(freeStart)) > (interpreterAllocationReserveBytes())))) { + /* begin tab */ + printChar('\t'); + n = (((GIV(eden)).limit)) - GIV(freeStart); + + /* begin printNum: */ + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)n)); + printChar(' '); + n = interpreterAllocationReserveBytes(); + + /* begin printNum: */ + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)n)); + printChar(' '); + n = (interpreterAllocationReserveBytes()) - ((((GIV(eden)).limit)) - GIV(freeStart)); + + /* begin printNum: */ + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)n)); + cr(); + } + + /* begin checkMemoryMap */ + assert(isYoungObject(GIV(newSpaceStart))); + assert(!(isOldObject(GIV(newSpaceStart)))); + assert(isYoungObject(GIV(oldSpaceStart) - BytesPerWord)); + assert(!(isOldObject(GIV(oldSpaceStart) - BytesPerWord))); + assert(!(isYoungObject(GIV(oldSpaceStart)))); + assert(isOldObject(GIV(oldSpaceStart))); + assert(!(isYoungObject(GIV(endOfMemory)))); + assert(isOldObject(GIV(endOfMemory))); + + /* we would like the following to be true, but we either choose one boundary check for + cogMethods vs objects (isMachineCodeFrame: et al) or one boundary check for + copyAndForward:. We can't have both, and the former is likely the highest dynamic + frequency. */ + assert(!(isOldObject(minCogMethodAddress()))); + assert(!(isOldObject(maxCogMethodAddress()))); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + runLeakCheckerFor(GCModeNewSpace); + preGCAction(GCModeNewSpace); + + /* begin flushMethodCacheFrom:to: */ + probe = 0; + for (i = 1; i <= MethodCacheEntries; i += 1) { + if (GIV(methodCache)[probe + MethodCacheSelector]) { + if (((oopisGreaterThanOrEqualTo(GIV(methodCache)[probe + MethodCacheSelector], GIV(newSpaceStart))) + && (oopisLessThan(GIV(methodCache)[probe + MethodCacheSelector], GIV(oldSpaceStart)))) + || ((oopisGreaterThanOrEqualTo(GIV(methodCache)[probe + MethodCacheMethod], GIV(newSpaceStart))) + && (oopisLessThan(GIV(methodCache)[probe + MethodCacheMethod], GIV(oldSpaceStart))))) { + GIV(methodCache)[probe + MethodCacheSelector] = 0; + } + } + probe += MethodCacheEntrySize; + } + + /* would prefer this to be in mapInterpreterOops, but + compatibility with ObjectMemory dictates it goes here. */ + GIV(needGCFlag) = 0; + GIV(gcStartUsecs) = ioUTCMicrosecondsNow(); + doScavenge(tenuringCriterion); + GIV(statScavenges) += 1; + GIV(statGCEndUsecs) = ioUTCMicrosecondsNow(); + GIV(statSGCDeltaUsecs) = GIV(statGCEndUsecs) - GIV(gcStartUsecs); + GIV(statScavengeGCUsecs) += GIV(statSGCDeltaUsecs); + GIV(statRootTableCount) = GIV(rememberedSetSize); + + /* begin logScavenge */ + if (GIV(scavengeLog)) { + writeScavengeLog(); + } + postGCAction(GCModeNewSpace); + runLeakCheckerFor(GCModeNewSpace); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } +} + + +/* Set the dimensions of the heap, answering the start of oldSpace. edenBytes + holds the desired ``size of eden'' + which is actually the total size of new space minus the reserve. edenBytes + is then divided up between eden + and the two survivor spaces, where each survivor space is a + scavengerDenominator (one seventh) of the total. */ +/* Transcript + cr; nextPutAll: 'heapBase: '; print: baseOfHeap; nextPut: $/; nextPutAll: + baseOfHeap hex; + nextPutAll: ' memLimit '; print: memLimit; nextPut: $/; nextPutAll: + memLimit hex; + nextPutAll: ' memEnd '; print: memEnd; nextPut: $/; nextPutAll: memEnd + hex; cr; flush. */ +/* This is more than a little counter-intuitive. Eden must include + interpreterAllocationReserveBytes. + */ + + /* SpurMemoryManager>>#setHeapBase:memoryLimit:endOfMemory: */ +static NoDbgRegParms NeverInline sqInt +setHeapBasememoryLimitendOfMemory(sqInt baseOfHeap, sqInt memLimit, sqInt memEnd) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt actualEdenBytes; + sqInt previousFSStart; + sqInt reserve; + sqInt survivorBytes; + sqInt totalBytes; + + reserve = interpreterAllocationReserveBytes(); + GIV(newSpaceStart) = baseOfHeap; + GIV(oldSpaceStart) = (baseOfHeap + GIV(edenBytes)) + reserve; + totalBytes = GIV(oldSpaceStart) - GIV(newSpaceStart); + + /* begin newSpaceStart:newSpaceBytes:survivorBytes: */ + survivorBytes = ((((GIV(oldSpaceStart) - GIV(newSpaceStart)) - reserve) / 7 /* scavengerDenominator */) & ~7); + actualEdenBytes = (((totalBytes - survivorBytes) - survivorBytes) & ~7); + assert((((totalBytes - actualEdenBytes) - survivorBytes) - survivorBytes) < (allocationUnit())); + if (((survivorBytes * 2) + actualEdenBytes) > ((0x200000000LL))) { + error("requested new space size exceeds 8Gb limit imposed by ephemeron/weak implementation."); + } + + /* for tenuring we require older objects below younger objects. since allocation + grows up this means that the survivor spaces must precede eden. */ + (GIV(pastSpace).start = GIV(newSpaceStart)); + (GIV(pastSpace).limit = GIV(newSpaceStart) + survivorBytes); + (GIV(futureSpace).start = (GIV(pastSpace).limit)); + (GIV(futureSpace).limit = ((GIV(pastSpace).limit)) + survivorBytes); + (GIV(eden).start = (GIV(futureSpace).limit)); + (GIV(eden).limit = GIV(newSpaceStart) + totalBytes); + assert((((GIV(futureSpace)).limit)) <= (GIV(newSpaceStart) + totalBytes)); + assert((((((GIV(eden)).start)) % (allocationUnit())) + ((((GIV(eden)).limit)) % (allocationUnit()))) == 0); + assert((((((GIV(pastSpace)).start)) % (allocationUnit())) + ((((GIV(pastSpace)).limit)) % (allocationUnit()))) == 0); + assert((((((GIV(futureSpace)).start)) % (allocationUnit())) + ((((GIV(futureSpace)).limit)) % (allocationUnit()))) == 0); + + /* begin initFutureSpaceStart */ + previousFSStart = GIV(futureSurvivorStart); + GIV(futureSurvivorStart) = (GIV(futureSpace).start); + GIV(tenuringProportion) = 0.9; + GIV(freeStart) = ((GIV(eden)).start); + GIV(pastSpaceStart) = ((GIV(pastSpace)).start); + GIV(freeOldSpaceStart) = memEnd; + GIV(endOfMemory) = memLimit; + return baseOfHeap; +} + + /* SpurMemoryManager>>#setIsPinnedOf:to: */ +static NoDbgRegParms void +setIsPinnedOfto(sqInt objOop, sqInt aBoolean) +{ + assert((isOldObject(objOop)) + && (!(isFreeObject(objOop)))); + byteAtput((void *)(objOop + (formatFieldByteOffset())),(aBoolean + ? (byteAt((void *)(objOop + (formatFieldByteOffset())))) | (1U << (pinnedBitByteShift())) + : (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (0xFF - (1U << (pinnedBitByteShift()))))); +} + + /* SpurMemoryManager>>#setIsRememberedOf:to: */ +static NoDbgRegParms void +setIsRememberedOfto(sqInt objOop, sqInt aBoolean) +{ + assert(!((isFreeObject(objOop)))); + byteAtput((void *)(objOop + (formatFieldByteOffset())),(aBoolean + ? (byteAt((void *)(objOop + (formatFieldByteOffset())))) | (1U << (rememberedBitByteShift())) + : (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (0xFF - (1U << (rememberedBitByteShift()))))); +} + + +/* Reduce the number of indexable fields in objOop, an arrayFormat or + longFormat object, to nSlots. + Convert the unused residual to a free chunk (if in oldSpace). + Answer the number of bytes returned to free memory, which may be zero. */ + + /* SpurMemoryManager>>#shorten:toIndexableSize: */ +sqInt +shortentoIndexableSize(sqInt objOop, sqInt indexableSize) +{ + sqInt delta; + + assert((indexableSize >= 0) + && (indexableSize < (lengthOf(objOop)))); + delta = doShortentoIndexableSize(objOop, indexableSize); + assert((lengthOf(followMaybeForwarded(objOop))) == indexableSize); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + return delta; +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#shortPrintObjectsFrom:to: */ +void +shortPrintObjectsFromto(sqInt startAddress, sqInt endAddress) +{ + sqInt oop; + + oop = objectBefore(startAddress); + oop = (oop + ? ((objectAfter(oop)) == startAddress + ? startAddress + : oop) + : startAddress); + while (oopisLessThan(oop, endAddress)) { + if (!(((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun()))) { + shortPrintOop(oop); + } + oop = objectAfter(oop); + } +} + + +/* Answer if the oop should be scavenged.. The method is called + shouldRemapOop: for compatibility with ObjectMemory. */ + + /* SpurMemoryManager>>#shouldRemapOop: */ +sqInt +shouldRemapOop(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (shouldRemapObj(oop)); +} + + /* SpurMemoryManager>>#sizeOfObjStack: */ +static NoDbgRegParms sqInt +sizeOfObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objStackPage; + sqInt total; + + if (objStack == GIV(nilObj)) { + return 0; + } + total = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + objStackPage = objStack; + while (1) { + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + if (!(objStackPage != 0)) break; + total += ObjStackLimit; + assert((fetchPointerofObject(ObjStackTopx, objStackPage)) == ObjStackLimit); + } + return total; +} + + /* SpurMemoryManager>>#slidingCompactionInProgress */ +static int +slidingCompactionInProgress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(gcPhaseInProgress) == SlidingCompactionInProgress; +} + + +/* *DO NOT CONFUSE THIS WITH numSlotsOf:. + This is an ObjectMemory compatibility method with questionable semantics. + Answers the number of slots in the receiver. + If the receiver is a byte object, return the number of bytes. + If the receiver is a short object, return the number of shorts. + If the receiver is a word object, return the number of words. + If the receiver is a double word object, return the number of double + words. Otherwise return the number of pointers. */ + + /* SpurMemoryManager>>#slotSizeOf: */ +sqInt +slotSizeOf(sqInt oop) +{ + sqInt fmt; + usqInt numSlots; + usqInt numSlotsUsqInt; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin lengthOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + +/* Return one of the objects in the specialObjectsArray */ + + /* SpurMemoryManager>>#splObj: */ +sqInt +splObj(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); +} + + +/* Answer the start of objOop, which is either the address of the overflow + size word, or objOop itself, depending on the size of the object. This may + be applied to any kind of object, normal, forwarders or free chunks. */ + + /* SpurMemoryManager>>#startOfObject: */ +static NoDbgRegParms usqInt +startOfObject(sqInt objOop) +{ + return ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); +} + + +/* Part of InterpreterProxy's 1.14 API */ + + /* SpurMemoryManager>>#statNumGCs */ +sqInt +statNumGCs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (GIV(statScavenges) + GIV(statIncrGCs)) + GIV(statFullGCs); +} + + +/* A renaming for the Cogit, which can't make sense of GIV(oldSpaceStart) */ + + /* SpurMemoryManager>>#storeCheckBoundary */ +usqInt +storeCheckBoundary(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(oldSpaceStart); +} + + +/* This primitive is called from Squeak as... + storeSegmentFor: arrayOfRoots into: aWordArray outPointers: + anArray. + This primitive will store a binary image segment (in the same format as + objects in the heap) of the + set of objects in arrayOfObjects. All pointers from within the set to + objects outside the set will be + copied into the array of outPointers. In their place in the image segment + will be an oop equal to the + offset in the outPointer array (the first would be 8), but with the high + bit set. + + Since Spur has a class table the load primitive must insert classes that + have instances into the + class table. This primitive marks such classes using the isRemembered bit, + which isn't meaningful + as a remembered bit in the segment. + + The primitive expects the segmentWordArray and outPointerArray to be more + than adequately long. + In this case it returns normally, and truncates the two arrays to exactly + the right size. + + The primitive can fail for the following reasons with the specified + failure codes: + PrimErrGenericFailure: the segmentWordArray is too small for the version + stamp PrimErrWritePastObject: the segmentWordArray is too small to + contain the reachable objects + PrimErrBadIndex: the outPointerArray is too small + PrimErrNoMemory: there is insufficient free space to store the array + answered by objectsReachableFromRoots:, + or the savedFirstFields and savedOutHashes arrays. + PrimErrNeedCompaction: a GC is needed to make room for the array answered + by objectsReachableFromRoots: + PrimErrLimitExceeded: there is no room in the hash field to store out + pointer indices or class references, + or the outPointerArray is larger than the max value of the hash field. + PrimErrNoModification: the segmentWordArrayArg or outPointerArrayArg are + immutable PrimErrObjectIsPinned: the segmentWordArrayArg or + outPointerArrayArg are pinned */ + + /* SpurMemoryManager>>#storeImageSegmentInto:outPointers:roots: */ +static NoDbgRegParms sqInt +storeImageSegmentIntooutPointersroots(sqInt segmentWordArrayArg, sqInt outPointersArrayArg, sqInt arrayOfRootsArg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayOfObjects; + sqInt arrayOfRoots; + sqInt delta; + usqInt endSeg; + int hash; + sqInt here; + sqInt i; + sqInt indexableSize; + sqInt newSegAddrOrError; + sqInt numClassesInSegment; + usqInt numSlots; + sqInt numSlotsSqInt; + sqInt objOop; + sqInt objOopSqInt; + sqInt outIndex; + sqInt outPointersArray; + usqInt p; + sqInt savedFirstFields; + sqInt savedOutHashes; + usqInt segAddr; + sqInt segmentWordArray; + usqInt segStart; + sqInt tempObjOop; + sqInt there; + sqInt toDoLimit; + sqInt toDoLimitSqInt; + usqInt toDoLimitUsqInt; + + if (( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(segmentWordArrayArg + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + ) + || ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(outPointersArrayArg + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + )) { + return PrimErrNoModification; + } + + /* Since segmentWordArrayArg & outPointersArrayArg may get shortened, they can't be pinned. */ + if ((((byteAt((void *)(segmentWordArrayArg + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0) + || (((byteAt((void *)(outPointersArrayArg + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)) { + return PrimErrObjectIsPinned; + } + if (((/* begin numSlotsOf: */ + assert((classIndexOf(outPointersArrayArg)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(outPointersArrayArg + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointersArrayArg - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > (identityHashHalfWordMask())) { + return PrimErrLimitExceeded; + } + runLeakCheckerFor(GCModeImageSegment); + + /* First scavenge to collect any new space garbage that refers to the graph. */ + + /* begin scavengingGC */ + scavengingGCTenuringIf(TenureByAge); + segmentWordArray = /* updatePostScavenge: */ + ((!((longAt((void *)(segmentWordArrayArg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(segmentWordArrayArg) + : segmentWordArrayArg); + outPointersArray = /* updatePostScavenge: */ + ((!((longAt((void *)(outPointersArrayArg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(outPointersArrayArg) + : outPointersArrayArg); + arrayOfRoots = /* updatePostScavenge: */ + ((!((longAt((void *)(arrayOfRootsArg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(arrayOfRootsArg) + : arrayOfRootsArg); + assert(!((forwardersIn(outPointersArray)))); + assert(!((forwardersIn(arrayOfRoots)))); + + /* Now compute the transitive closure, collecting the sequence of objects to be stored in the arrayOfObjects array. + Included in arrayOfObjects are the arrayOfRoots and all its contents. All objects have been unmarked. */ + arrayOfObjects = objectsReachableFromRoots(arrayOfRoots); + if (!arrayOfObjects) { + return PrimErrNoMemory; + } + + /* If objectsReachableFromRoots: answers an integer there is not enough continuous free space in which to allocate the + reachable objects. If there is sufficient free space then answer an error code to prompt a compacting GC and a retry. */ + if ((((arrayOfObjects) & 7) == 1)) { + if ((GIV(totalFreeOldSpace) - 8 /* allocationUnit */) >= ((arrayOfObjects >> 3))) { + return PrimErrNeedCompaction; + } + return PrimErrNoMemory; + } + assert(allObjectsUnmarked()); + assert(!((forwardersIn(arrayOfObjects)))); + runLeakCheckerFor(GCModeImageSegment); + + /* Both to expand the max size of segment and to reduce the length of the + load-time pass that adds classes to the class table, move classes to the + front of arrayOfObjects, leaving the root array as the first element. */ + + /* begin moveClassesForwardsIn: */ + /* if > 0, this is the index of the first non-class past the first element. */ + numClassesInSegment = (there = 0); + toDoLimitSqInt = ((/* begin numSlotsOf: */ + assert((classIndexOf(arrayOfObjects)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(arrayOfObjects + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(arrayOfObjects - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (here = 1; here <= toDoLimitSqInt; here += 1) { + objOopSqInt = longAt((void *)((arrayOfObjects + BaseHeaderSize) + ((((usqInt)(here) << (shiftForWord())))))); + hash = (long32At((void *)(objOopSqInt + 4))) & (identityHashHalfWordMask()); + if ((hash > (lastClassIndexPun())) + && ((classOrNilAtIndex(hash)) == objOopSqInt)) { + numClassesInSegment += 1; + if (there > 0) { + tempObjOop = longAt((void *)((arrayOfObjects + BaseHeaderSize) + ((((usqInt)(there) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(arrayOfObjects)) + && (!(isForwarded(arrayOfObjects)))); + assert(validStorePointerUncheckedArgs(there, arrayOfObjects, objOopSqInt)); + longAtput((void *)((arrayOfObjects + BaseHeaderSize) + ((((usqInt)(there) << (shiftForWord()))))),objOopSqInt); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(arrayOfObjects)) + && (!(isForwarded(arrayOfObjects)))); + assert(validStorePointerUncheckedArgs(here, arrayOfObjects, tempObjOop)); + longAtput((void *)((arrayOfObjects + BaseHeaderSize) + ((((usqInt)(here) << (shiftForWord()))))),tempObjOop); + there += 1; + } + } + else { + if (!there) { + there = here; + } + } + } + + /* The scheme is to copy the objects into segmentWordArray, and then map the oops in segmentWordArray. + Therefore the primitive needs to both map efficiently originals to copies in segmentWordArray and + be able to undo any side-effects if the primitive fails because either segmentWordArray or outPointersArray + is too small. The mapping is done by having the objects to be stored in arrayOfObjects refer to their mapped + locations through their first field, just like a forwarding pointer, but without becoming a forwarder, saving their + first field in savedFirstFields, and the objects in outPointersArray pointing to their locations in the outPointersArray + through their identityHashes, saved in savedOutHashes. + Since arrayOfObjects and its savedFirstFields, and outPointersArray and its saved hashes, can be enumerated + side-by-side, the hashes can be restored to the originals. So the first field of the heap object corresponding to + an object in arrayOfObjects is set to its location in segmentWordArray, and the hash of an object in outPointersArray + is set to its index in outPointersArray plus the top hash bit. Classes in arrayOfObjects have their marked bit set. + Oops in objects in segmentWordArray are therefore mapped by accessing the original oop, and following its first + field. Class indices in segmentWordArray are mapped by fetching the original class, and testing its marked bit. + If marked, the first field is followed to access the class copy in the segment. Out pointers (objects and classes, + which are unmarked), the object's identityHash is set (eek!!) to its index in the outPointersArray. So savedOutHashes + parallels the outPointersArray. The saved hash array is initialized with an out-of-range hash value so that the first + unused entry can be identified. */ + savedFirstFields = noInlineAllocateSlotsformatclassIndex(numSlotsOf(arrayOfObjects), wordIndexableFormat(), wordSizeClassIndexPun()); + savedOutHashes = noInlineAllocateSlotsformatclassIndex(numSlotsForBytes((numSlotsOf(outPointersArray)) * 4), firstLongFormat(), thirtyTwoBitLongsClassIndexPun()); + if ((!savedFirstFields) + || (!savedOutHashes)) { + freeObject(arrayOfObjects); + if (oopisGreaterThanOrEqualToandLessThan(savedFirstFields, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedFirstFields); + } + if (oopisGreaterThanOrEqualToandLessThan(savedOutHashes, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedOutHashes); + } + return PrimErrNoMemory; + } + /* begin numSlotsOf: */ + assert((classIndexOf(savedFirstFields)) > (isForwardedObjectClassIndexPun())); + if (((numSlots = byteAt((void *)(savedFirstFields + (numSlotsFieldByteOffset()))))) == (numSlotsMask())) { + numSlotsSqInt = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(savedFirstFields - BaseHeaderSize)))) << 8)))))) >> 8; + } + else { + numSlotsSqInt = numSlots; + } + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((savedFirstFields + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1, addressAfter(savedFirstFields))); + toDoLimitUsqInt = ((usqInt)(((savedFirstFields + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1)); + for (p = (((usqInt)(savedFirstFields + BaseHeaderSize))); p <= toDoLimitUsqInt; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + /* begin numSlotsOf: */ + assert((classIndexOf(savedOutHashes)) > (isForwardedObjectClassIndexPun())); + if (((numSlots = byteAt((void *)(savedOutHashes + (numSlotsFieldByteOffset()))))) == (numSlotsMask())) { + numSlotsSqInt = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(savedOutHashes - BaseHeaderSize)))) << 8)))))) >> 8; + } + else { + numSlotsSqInt = numSlots; + } + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((savedOutHashes + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1, addressAfter(savedOutHashes))); + toDoLimitUsqInt = ((usqInt)(((savedOutHashes + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1)); + for (p = (((usqInt)(savedOutHashes + BaseHeaderSize))); p <= toDoLimitUsqInt; p += 8 /* allocationUnit */) { + longAtput((void *)(p),((((usqInt)(((identityHashHalfWordMask()) + 1)) << 32))) + ((identityHashHalfWordMask()) + 1)); + } + segAddr = segmentWordArray + BaseHeaderSize; + endSeg = addressAfter(segmentWordArray); + + /* Write a version number for byte order and version check. */ + if (segAddr >= endSeg) { + return PrimErrGenericFailure; + } + long32Atput((void *)(segAddr),imageSegmentVersion()); + long32Atput((void *)(segAddr + 4),imageSegmentVersion()); + segStart = (segAddr += 8 /* allocationUnit */); + assert(arrayOfRoots == (fetchPointerofObject(0, arrayOfObjects))); + + /* Copy all reachable objects to the segment, setting the marked bit for all objects (clones) in the segment, + and the remembered bit for all classes (clones) in the segment. */ + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(arrayOfObjects)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(arrayOfObjects + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(arrayOfObjects - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + /* Check that classes in the segment are addressable. Since the top bit of the hash field is used to tag + classes external to the segment, the segment offset must not inadvertently set this bit. This limit still + allows for a million or more classes. */ + if ((i == numClassesInSegment) + && ((((segAddr - segStart) / 8 /* allocationUnit */) + (lastClassIndexPun())) >= TopHashBit)) { + return returnrestoringObjectsInupTosavedFirstFields(PrimErrLimitExceeded, arrayOfObjects, i, savedFirstFields); + } + objOop = longAt((void *)((arrayOfObjects + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + assert(!(((isImmediate(objOop)) + || (isForwarded(objOop))))); + newSegAddrOrError = copyObjtoAddrstopAtsavedFirstFieldsindex(objOop, segAddr, endSeg, savedFirstFields, i); + if (oopisLessThan(newSegAddrOrError, segStart)) { + return returnrestoringObjectsInupTosavedFirstFields(newSegAddrOrError, arrayOfObjects, i, savedFirstFields); + } + segAddr = newSegAddrOrError; + } + + /* Check that it can be safely shortened. */ + if ((endSeg != segAddr) + && ((endSeg - segAddr) < (BaseHeaderSize + BytesPerOop))) { + return returnrestoringObjectsInupTosavedFirstFields(PrimErrWritePastObject, arrayOfObjects, -1, savedFirstFields); + } + + /* Now scan, adding out pointers to the outPointersArray; all objects in arrayOfObjects + have their first field pointing to the corresponding copy in segmentWordArray. */ + if (((outIndex = mapOopsFromtooutPointersoutHashes(segStart, segAddr, outPointersArray, savedOutHashes))) < 0) { + return returnrestoringObjectsInsavedFirstFieldsandsavedHashes(PrimErrBadIndex, arrayOfObjects, savedFirstFields, outPointersArray, savedOutHashes); + } + + /* no room in outPointers; fail + We're done. Shorten the results, restore hashes and return. */ + indexableSize = (segAddr - (segmentWordArray + BaseHeaderSize)) / 4; + + /* begin shorten:toIndexableSize: */ + assert((indexableSize >= 0) + && (indexableSize < (lengthOf(segmentWordArray)))); + delta = doShortentoIndexableSize(segmentWordArray, indexableSize); + assert((lengthOf(followMaybeForwarded(segmentWordArray))) == indexableSize); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + + /* begin shorten:toIndexableSize: */ + assert((outIndex >= 0) + && (outIndex < (lengthOf(outPointersArray)))); + delta = doShortentoIndexableSize(outPointersArray, outIndex); + assert((lengthOf(followMaybeForwarded(outPointersArray))) == outIndex); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + return returnrestoringObjectsInsavedFirstFieldsandsavedHashes(PrimNoErr, arrayOfObjects, savedFirstFields, outPointersArray, savedOutHashes); +} + + /* SpurMemoryManager>>#storeInteger:ofObject:withValue: */ +void +storeIntegerofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqLong integerValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((((((usqLong)(integerValue)) >> 60) + 1) & 15) <= 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(fieldIndex, objOop, (((usqInt)integerValue << 3) | 1))); + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),(((usqInt)integerValue << 3) | 1)); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + } + } + else { + if (fmt < (firstShortFormat())) { + if (fmt < (firstLongFormat())) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 3)))),integerValue); + } + else { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2)))),integerValue); + } + } + else { + if (fmt < (firstCompiledMethodFormat())) { + if (fmt < (firstByteFormat())) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((objOop + BaseHeaderSize) + fieldIndex),integerValue); + } + else { + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 1)))),integerValue); + } + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + } + } + } +} + + /* SpurMemoryManager>>#storeLong32:ofObject:withValue: */ +int +storeLong32ofObjectwithValue(sqInt fieldIndex, sqInt obj, sqInt valueWord) +{ + return long32Atput((void *)((obj + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2)))),valueWord); +} + + /* SpurMemoryManager>>#storePointerUnchecked:ofObject:withValue: */ +sqInt +storePointerUncheckedofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqInt valuePointer) +{ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(fieldIndex, objOop, valuePointer)); + return longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); +} + + +/* Note must check here for stores of young objects into old ones. */ +/* See SistaCogit */ + + /* SpurMemoryManager>>#storePointer:ofObject:withValue: */ +sqInt +storePointerofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqInt valuePointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(validStorePointerArgs(fieldIndex, objOop, valuePointer)); + + /* begin isOldObject: */ + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + return longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); +} + + +/* Answer a new String copied from a null-terminated C string, + or nil if out of memory. */ + + /* SpurMemoryManager>>#stringForCString: */ +sqInt +stringForCString(const char *aCString) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt formatField; + sqInt len; + usqInt newObj; + sqInt newString; + usqInt numBytes; + sqInt numSlots; + + len = strlen(aCString); + numSlots = (len + (BytesPerWord - 1)) / BytesPerWord; + formatField = (firstByteFormat()) + ((8 - len) & (BytesPerWord - 1)); + + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if ((((usqInt)(numSlots)) >> 56) > 0) { + newString = null; + goto l1; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newString = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, formatField, ClassByteStringCompactIndex); + goto l1; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + ClassByteStringCompactIndex); + } + else { + longAtput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + ClassByteStringCompactIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newString = newObj; + /* end allocateSlots:format:classIndex: */ +l1: + if (newString) { + strncpy(((char *) (newString + BaseHeaderSize)), aCString, len); + } + return newString; +} + + +/* On load, swizzle the pointers in an obj stack. Answer the obj stack's oop. */ + + /* SpurMemoryManager>>#swizzleObjStackAt: */ +static NoDbgRegParms sqInt +swizzleObjStackAt(sqInt objStackRootIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt field; + sqInt firstPage; + sqInt index; + sqInt page; + sqInt stackOrNil; + + firstPage = (stackOrNil = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord()))))))); + if (stackOrNil == GIV(nilObj)) { + return stackOrNil; + } + do { + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + assert((fetchPointerofObject(ObjStackMyx, stackOrNil)) == objStackRootIndex); + + /* There are four fixed slots in an obj stack, and a Topx of 0 indicates empty, so + if there were 5 slots in an oop stack, full would be 2, and the last 0-rel index is 4. + Hence the last index is topx + fixed slots - 1, or topx + ObjStackNextx */ + index = (longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) + ObjStackNextx; + + /* swizzle fields including ObjStackNextx, excluding ObjStackFreex and leave field containing the next link. */ + do { + field = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (!((field == 0) + || (((field & (tagMask())) != 0)))) { + field = swizzleObj(field); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(stackOrNil)) == (wordIndexableFormat())); + longAtput((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),field); + } + } while(((index -= 1)) >= ObjStackNextx); + } while(((stackOrNil = field)) != 0); + if ((stackOrNil = longAt((void *)((firstPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))))) { + page = firstPage; + do { + stackOrNil = swizzleObj(stackOrNil); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(page)) == (wordIndexableFormat())); + longAtput((void *)((page + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),stackOrNil); + page = stackOrNil; + } while(((stackOrNil = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))))) != 0); + } + assert(isValidObjStackAt(objStackRootIndex)); + return longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord())))))); +} + + +/* Do an incremental GC that tenures all surviving young objects to old + space. The selector tenuringIncrementalGC refers to the V3 incremental + collector, which is the collector for young objects, equivalent in + function to the scavenger. + The Spur incremental collector is called the global incremental garbage + collector or globalIGC for short. + */ + + /* SpurMemoryManager>>#tenuringIncrementalGC */ +void +tenuringIncrementalGC(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt savedTenuringThreshold; + + /* begin flushNewSpace */ + savedTenuringThreshold = GIV(tenureThreshold); + GIV(tenureThreshold) = GIV(oldSpaceStart); + scavengingGCTenuringIf(TenureByAge); + + /* begin setRawTenuringThreshold: */ + GIV(tenureThreshold) = savedTenuringThreshold; + assert((GIV(rememberedSetSize)) == 0); + assert(GIV(pastSpaceStart) == (((GIV(pastSpace)).start))); + assert(GIV(freeStart) == (((GIV(eden)).start))); +} + + +/* This assert is tricky. push:onObjStack: may call topOfObjStack: just after + pushing an + empty page on the stack, and will ask if the second page is valid. */ + + /* SpurMemoryManager>>#topOfObjStack: */ +static NoDbgRegParms sqInt +topOfObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt topx; + + eassert(isValidObjStackPagemyIndexfirstPage(objStack, fetchPointerofObject(ObjStackMyx, objStack), objStack == (fetchPointerofObject(fetchPointerofObject(ObjStackMyx, objStack), GIV(hiddenRootsObj))))); + topx = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + if (!topx) { + assert((fetchPointerofObject(ObjStackNextx, objStack)) == 0); + return null; + } + return longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(((topx + ObjStackFixedSlots) - 1)) << (shiftForWord())))))); +} + + +/* Answers the top of the remappable oop stack. Useful when writing loops. + We support this excessence for compatibility with ObjectMemory. + Spur doesn't GC during allocation. */ + + /* SpurMemoryManager>>#topRemappableOop */ +sqInt +topRemappableOop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(remapBuffer)[GIV(remapBufferCount)]; +} + + +/* This method both computes the actual number of free bytes by traversing + all free objects + on the free lists/tree, and checks that the tree is valid. It is used + mainly by checkFreeSpace. */ + + /* SpurMemoryManager>>#totalFreeListBytes */ +static sqInt +totalFreeListBytes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bytesInChunk; + sqInt cameFrom; + sqInt i; + sqInt largeChild; + sqInt listNode; + sqInt nextNode; + sqInt smallChild; + sqInt totalFreeBytes; + sqInt treeNodeSqInt; + + totalFreeBytes = 0; + for (i = 1; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + bytesInChunk = i * 8 /* allocationUnit */; + listNode = GIV(freeLists)[i]; + while (listNode != 0) { + totalFreeBytes += bytesInChunk; + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(listNode)); + assert(bytesInChunk == (bytesInBody(listNode))); + nextNode = longAt((void *)((listNode + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(nextNode != listNode); + listNode = nextNode; + } + } + + /* begin freeTreeNodesDo: */ + treeNodeSqInt = GIV(freeLists)[0]; + if (!treeNodeSqInt) { + goto l1; + } + cameFrom = -1; + do { + assert((bytesInBody(treeNodeSqInt)) >= ((numFreeLists()) * (allocationUnit()))); + smallChild = longAt((void *)((treeNodeSqInt + BaseHeaderSize) + (3U << (shiftForWord())))); + largeChild = longAt((void *)((treeNodeSqInt + BaseHeaderSize) + (4U << (shiftForWord())))); + assert((smallChild == 0) + || (treeNodeSqInt == (fetchPointerofFreeChunk(freeChunkParentIndex(), smallChild)))); + assert((largeChild == 0) + || (treeNodeSqInt == (fetchPointerofFreeChunk(freeChunkParentIndex(), largeChild)))); + + /* apply if the node has no children, or it has no large children and we're + returning from the small child, or we're returning from the large child. */ + if (((smallChild == 0) + && (largeChild == 0)) + || ((largeChild + ? cameFrom == largeChild + : cameFrom == smallChild))) { + bytesInChunk = bytesInBody(treeNodeSqInt); + assert((bytesInChunk / (allocationUnit())) >= (numFreeLists())); + listNode = treeNodeSqInt; + while (listNode != 0) { + /* self printFreeChunk: listNode */ + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(listNode)); + assert((listNode == treeNodeSqInt) + || ((fetchPointerofFreeChunk(freeChunkParentIndex(), listNode)) == 0)); + totalFreeBytes += bytesInChunk; + assert(bytesInChunk == (bytesInBody(listNode))); + nextNode = longAt((void *)((listNode + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(nextNode != listNode); + listNode = nextNode; + } + + /* and since we've applied we must move on up */ + cameFrom = treeNodeSqInt; + treeNodeSqInt = longAt((void *)((treeNodeSqInt + BaseHeaderSize) + (2U << (shiftForWord())))); + } + else { + if ((smallChild != 0) + && (cameFrom != smallChild)) { + treeNodeSqInt = smallChild; + } + else { + assert(largeChild != 0); + treeNodeSqInt = largeChild; + } + cameFrom = -1; + } + } while(treeNodeSqInt != 0); + /* end freeTreeNodesDo: */ +l1: + return totalFreeBytes; +} + + /* SpurMemoryManager>>#trueObject */ +sqInt +trueObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(trueObj); +} + + +/* Ephemerons in the mourn queue will have been fired (had their + ephemeron-ness turned off), + but the ephemeron queue is not saved in the snapshot. So the snapshotted + ephemerons need to be unfired so they're still unqueued ephemerons in the + loaded image. */ + + /* SpurMemoryManager>>#unfireQueuedEphemeronsForSnapshot */ +static void +unfireQueuedEphemeronsForSnapshot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt iSqInt; + sqInt mourner; + sqInt numOnThisPage; + sqInt objStackPage; + + /* begin objStack:do: */ + if (GIV(mournQueue) == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(GIV(mournQueue))); + objStackPage = GIV(mournQueue); + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + mourner = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (((!(mourner & (tagMask())))) + && (((byteAt((void *)(mourner + (formatFieldByteOffset())))) & (formatMask())) == (nonIndexablePointerFormat()))) { + /* begin setFormatOf:to: */ + assert((((ephemeronFormat()) >= 0) && ((ephemeronFormat()) <= (formatMask())))); + byteAtput((void *)(mourner + (formatFieldByteOffset())),((byteAt((void *)(mourner + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (ephemeronFormat())); + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l1:; +} + + +/* Unlink a free object from the free lists. Do not alter totalFreeOldSpace. + Used for coalescing. + */ + + /* SpurMemoryManager>>#unlinkFreeChunk:chunkBytes: */ +static NoDbgRegParms sqInt +unlinkFreeChunkchunkBytes(sqInt freeChunk, sqInt chunkBytes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt lilliputian; + sqInt next; + sqInt nextFreeChunk; + sqInt nextSqInt; + sqInt prev; + + index = chunkBytes / 8 /* allocationUnit */; + + /* Pathological 64 bits case - size 1 - single linked list */ + + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + if (chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */)) { + return unlinkLilliputianChunkindex(freeChunk, index); + } + prev = longAt((void *)((freeChunk + BaseHeaderSize) + (1U << (shiftForWord())))); + + /* Has prev element: update double linked list */ + if (prev) { + nextFreeChunk = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:chunkBytes: */ + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + lilliputian = chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if ((nextFreeChunk != 0) + && (!lilliputian)) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + } + return freeChunk; + } + + /* Is the beginning of a list + Small chunk */ + if ((index < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(freeChunk)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(freeChunk)) == freeChunk))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((nextSqInt = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))))); + if (nextSqInt) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextSqInt)); + longAtput((void *)((nextSqInt + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + return freeChunk; + } + + /* Large chunk */ + next = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + if (next) { + inFreeTreeReplacewith(freeChunk, next); + } + else { + unlinkSolitaryFreeTreeNode(freeChunk); + } + + /* no list; remove the interior node + list; replace node with it */ + return freeChunk; +} + + /* SpurMemoryManager>>#unlinkLilliputianChunk:index: */ +static NoDbgRegParms NeverInline sqInt +unlinkLilliputianChunkindex(sqInt freeChunk, sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt next; + sqInt nextSqInt; + sqInt node; + sqInt prev; + + + /* for profiling */ + node = GIV(freeLists)[index]; + prev = 0; + while (node != 0) { + assert(node == (startOfObject(node))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(node)); + next = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + if (node == freeChunk) { + if (prev) { + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((next == 0) + || (isFreeObject(next))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),next); + } + else { + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(freeChunk)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(freeChunk)) == freeChunk))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((nextSqInt = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))))); + } + return freeChunk; + } + prev = node; + node = next; + } + error("freeChunk not found in lilliputian chunk free list"); + return 0; +} + + +/* Unlink a freeTreeNode. Assumes the node has no list (null next link). */ + + /* SpurMemoryManager>>#unlinkSolitaryFreeTreeNode: */ +static NoDbgRegParms void +unlinkSolitaryFreeTreeNode(sqInt freeTreeNode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fieldIndex; + sqInt larger; + sqInt parent; + sqInt smaller; + + assert((fetchPointerofFreeChunk(freeChunkNextIndex(), freeTreeNode)) == 0); + + /* case 1. interior node has one child, P = parent, N = node, S = subtree (mirrored for large vs small) + ___ ___ + | P | | P | + _/_ _/_ + | N | => | S | + _/_ + | S | + case 2: interior node has two children, , P = parent, N = node, L = smaller, left subtree, R = larger, right subtree. + add the left subtree to the bottom left of the right subtree (mirrored for large vs small) + ___ ___ + | P | | P | + _/_ _/_ + | N | => | R | + _/_ _\_ _/_ + | L | | R | | L | */ + smaller = longAt((void *)((freeTreeNode + BaseHeaderSize) + (3U << (shiftForWord())))); + larger = longAt((void *)((freeTreeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + parent = longAt((void *)((freeTreeNode + BaseHeaderSize) + (2U << (shiftForWord())))); + if (parent) { + if (smaller) { + fieldIndex = (freeTreeNode == (longAt((void *)((parent + BaseHeaderSize) + (3U << (shiftForWord()))))) + ? 3 /* freeChunkSmallerIndex */ + : 4 /* freeChunkLargerIndex */); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(parent)); + assert((smaller == 0) + || (isFreeObject(smaller))); + longAtput((void *)((parent + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),smaller); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(smaller)); + assert((parent == 0) + || (isFreeObject(parent))); + longAtput((void *)((smaller + BaseHeaderSize) + (2U << (shiftForWord()))),parent); + if (larger) { + addFreeSubTree(larger); + } + } + else { + fieldIndex = (freeTreeNode == (longAt((void *)((parent + BaseHeaderSize) + (3U << (shiftForWord()))))) + ? 3 /* freeChunkSmallerIndex */ + : 4 /* freeChunkLargerIndex */); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(parent)); + assert((larger == 0) + || (isFreeObject(larger))); + longAtput((void *)((parent + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),larger); + if (larger) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(larger)); + assert((parent == 0) + || (isFreeObject(parent))); + longAtput((void *)((larger + BaseHeaderSize) + (2U << (shiftForWord()))),parent); + } + } + } + else { + if (smaller) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(smaller)); + longAtput((void *)((smaller + BaseHeaderSize) + (2U << (shiftForWord()))),0); + GIV(freeLists)[0] = smaller; + if (larger) { + addFreeSubTree(larger); + } + } + else { + if (larger) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(larger)); + longAtput((void *)((larger + BaseHeaderSize) + (2U << (shiftForWord()))),0); + } + GIV(freeLists)[0] = larger; + } + } +} + + /* SpurMemoryManager>>#unpinObject: */ +sqInt +unpinObject(sqInt objOop) +{ + assert(isNonImmediate(objOop)); + setIsPinnedOfto(objOop, 0); + return 0; +} + + /* SpurMemoryManager>>#unscannedEphemeronsContainsOnlyActiveEphemerons */ +static sqInt +unscannedEphemeronsContainsOnlyActiveEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt entry; + usqInt p; + + for (p = ((GIV(unscannedEphemerons).start)); p <= ((GIV(unscannedEphemerons).top)); p += BytesPerOop) { + entry = longAt((void *)(p)); + if (!(((!(entry & (tagMask())))) + && ((isEphemeron(entry)) + && (!(isMarked(followedKeyOfEphemeron(entry))))))) { + return 0; + } + } + return 1; +} + + /* SpurMemoryManager>>#unscannedEphemeronsContainsOnlyEphemerons */ +static sqInt +unscannedEphemeronsContainsOnlyEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt entry; + usqInt p; + + for (p = ((GIV(unscannedEphemerons).start)); p <= ((GIV(unscannedEphemerons).top)); p += BytesPerOop) { + entry = longAt((void *)(p)); + if (!(((!(entry & (tagMask())))) + && (isEphemeron(entry)))) { + return 0; + } + } + return 1; +} + + /* SpurMemoryManager>>#updateListStartingAt: */ +static NoDbgRegParms void +updateListStartingAt(sqInt freeNode) +{ + sqInt obj; + sqInt prev; + + obj = 0; + if (!freeNode) { + return; + } + assert(!((isLilliputianSize(bytesInBody(freeNode))))); + prev = freeNode; + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + longAtput((void *)((prev + BaseHeaderSize) + (1U << (shiftForWord()))),0); + while (1) { + obj = longAt((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord())))); + if (!(obj != 0)) break; + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(obj)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((obj + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + prev = obj; + } +} + + +/* Check the hashes of classes in the table. The tricky thing here is that + classes may be duplicated + in the table. So each entry must be in the table at its hash, even if it + is elsewhere in the table. */ + + /* SpurMemoryManager>>#validClassTableHashes */ +static sqInt +validClassTableHashes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOrNilSqInt; + int hash; + sqInt i; + sqInt ignored; + sqInt j; + sqInt page; + sqInt toDoLimit; + + if (!(validClassTableRootPages())) { + return 0; + } + + /* begin classTableEntriesDo: */ + for (i = 0; i < GIV(numClassTablePages); i += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNilSqInt = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNilSqInt != GIV(nilObj)) { + ignored = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + if ((!((longAt((void *)(classOrNilSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + return 0; + } + hash = (long32At((void *)(classOrNilSqInt + 4))) & (identityHashHalfWordMask()); + if (!hash) { + return 0; + } + if ((noCheckClassAtIndex(hash)) != classOrNilSqInt) { + return 0; + } + } + } + } + return 1; +} + + +/* Answer if hiddenRootsObj is of the right size with the + expected contents, and if numClassTablePages is correct. */ + + /* SpurMemoryManager>>#validClassTableRootPages */ +static sqInt +validClassTableRootPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + usqInt numSlots; + sqInt obj; + sqInt toDoLimit; + + if (!(((/* begin numSlotsOf: */ + assert((classIndexOf(GIV(hiddenRootsObj))) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(GIV(hiddenRootsObj) + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(GIV(hiddenRootsObj) - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == ((1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift()))) + 8 /* hiddenRootSlots */))) { + return 0; + } + + /* is it in range? */ + if (!((GIV(numClassTablePages) > 1) + && (GIV(numClassTablePages) <= (1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift())))))) { + return 0; + } + + /* are all pages the right size? */ + for (i = 0; i < GIV(numClassTablePages); i += 1) { + obj = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (!((addressCouldBeObj(obj)) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == (1U << (classTableMajorIndexShift()))))) { + return 0; + } + } + + /* are all entries beyond numClassTablePages nil? */ + toDoLimit = (1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift()))) - 1; + for (i = GIV(numClassTablePages); i <= toDoLimit; i += 1) { + if ((longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))))) != GIV(nilObj)) { + return 0; + } + } + return 1; +} + + +/* useful for debugging */ + + /* SpurMemoryManager>>#validFreeTree */ +int +validFreeTree(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return !(validFreeTreeChunkparent(GIV(freeLists)[0], 0)); +} + + /* SpurMemoryManager>>#validFreeTreeChunk: */ +static NoDbgRegParms sqInt +validFreeTreeChunk(sqInt chunk) +{ + if (!(segmentContainingObj(chunk))) { + return 0; + } + return !(validFreeTreeChunkparent(chunk, longAt((void *)((chunk + BaseHeaderSize) + (2U << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#validFreeTreeChunk:parent: */ +static NoDbgRegParms const char * +validFreeTreeChunkparent(sqInt chunk, sqInt parent) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + const char *reason; + + if (!chunk) { + return null; + } + if (!(/* addressCouldBeOldObj: */ + ((chunk & (BaseHeaderSize - 1)) == 0) + && (oopisGreaterThanOrEqualToandLessThan(chunk, GIV(oldSpaceStart), GIV(endOfMemory))))) { + return "not in old space"; + } + if (((bytesInBody(chunk)) / 8 /* allocationUnit */) < 64 /* numFreeLists */) { + return "too small"; + } + if (parent != (longAt((void *)((chunk + BaseHeaderSize) + (2U << (shiftForWord())))))) { + return "bad parent"; + } + if ((segmentContainingObj(chunk)) != (segmentContainingObj(addressAfter(chunk)))) { + return "not in one segment"; + } + if ((reason = validFreeTreeChunkparent(longAt((void *)((chunk + BaseHeaderSize) + (3U << (shiftForWord())))), chunk))) { + return reason; + } + if ((reason = validFreeTreeChunkparent(longAt((void *)((chunk + BaseHeaderSize) + (4U << (shiftForWord())))), chunk))) { + return reason; + } + return null; +} + + /* SpurMemoryManager>>#validObjStacks */ +static sqInt +validObjStacks(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((GIV(markStack) == GIV(nilObj)) + || (isValidObjStack(GIV(markStack)))) + && (((GIV(weaklingStack) == GIV(nilObj)) + || (isValidObjStack(GIV(weaklingStack)))) + && ((GIV(mournQueue) == GIV(nilObj)) + || (isValidObjStack(GIV(mournQueue))))); +} + + +/* This is a hook for the CoMemoryManagers to check for valid compiled code. + It is a noop here. */ + + /* SpurMemoryManager>>#validPostBecomeArrayContents: */ +static NoDbgRegParms sqInt +validPostBecomeArrayContents(sqInt anArray) +{ + return 1; +} + + /* SpurMemoryManager>>#validStorePointerArgs:_:_: */ +static NoDbgRegParms sqInt +validStorePointerArgs(sqInt fieldIndex, sqInt objOop, sqInt valuePointer) +{ + usqInt numSlots; + + return (fieldIndex >= 0) + && ((fieldIndex < ((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))) + && (!((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))); +} + + /* SpurMemoryManager>>#validStorePointerUncheckedArgs:_:_: */ +static NoDbgRegParms sqInt +validStorePointerUncheckedArgs(sqInt fieldIndex, sqInt objOop, sqInt valuePointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + + return (fieldIndex >= 0) + && (((fieldIndex == 0) + || (fieldIndex < ((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))))) + && ((((valuePointer & (tagMask())) != 0)) + || ((/* isInHeapBounds: */ + (oopisGreaterThanOrEqualTo(valuePointer, (!(minCogMethodAddress()) + ? GIV(nilObj) + : minCogMethodAddress()))) + && (oopisLessThan(valuePointer, GIV(endOfMemory)))) + || (((fieldIndex == 0) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 0) + || (GIV(gcPhaseInProgress) == SlidingCompactionInProgress))) + || ((((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (sixtyFourBitIndexableFormat())) + && ((((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) < (firstCompiledMethodFormat())) + || (fieldIndex > (literalCountOf(objOop))))))))); +} + + +/* 1/true = big, 0/false = little. + Answer false for little endian, true for big endian (should of course be + vmIsBigEndian; sigh) */ + + /* SpurMemoryManager>>#vmEndianness */ +sqInt +vmEndianness(void) +{ + return VMBIGENDIAN; +} + + /* SpurMemoryManager>>#whereIsMaybeHeapThing: */ +static NoDbgRegParms char * +whereIsMaybeHeapThing(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (/* isInNewSpace: */ + (oopisLessThan(anOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(anOop, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(anOop, ((GIV(eden)).start), GIV(freeStart))) { + return " is in eden"; + } + if (oopisGreaterThanOrEqualToandLessThan(anOop, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + return " is in future space"; + } + if (oopisGreaterThanOrEqualToandLessThan(anOop, ((GIV(pastSpace)).start), GIV(pastSpaceStart))) { + return " is in past space"; + } + return " is in new space"; + } + if (oopisGreaterThanOrEqualToandLessThan(anOop, GIV(oldSpaceStart), GIV(endOfMemory))) { + if (segmentContainingObj(anOop)) { + return " is in old space"; + } + return " is between old space segments"; + } + return null; +} + + +/* Sweep all of old space, sliding unpinned marked objects down over free and + unmarked objects. + Let the segmentManager mark which segments contain pinned objects via + notePinned:. + */ + + /* SpurPlanningCompactor>>#compact */ +static NeverInline void +compact(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt finalPass; + sqInt firstPass; + sqInt followingWord; + usqInt followingWordAddress; + sqInt highestSuitableFreeBlock; + sqInt i; + sqInt largestFreeChunk; + sqInt largestFreeChunkSqInt; + usqInt numSlots; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt sizeOfEden; + sqInt spaceEstimate; + sqInt stackOrNil; + + + /* for profiling */ + /* begin initializeScanCheckingForFullyCompactedHeap */ + GIV(firstMobileObject) = (GIV(lastMobileObject) = (GIV(objectAfterLastMobileObject) = null)); + reinitializeScanFrom(GIV(hiddenRootsObj)); + if (!GIV(firstFreeObject)) { + error("uncompactable heap; no unmarked objects found"); + } + if (GIV(firstMobileObject) >= GIV(endOfMemory)) { + /* begin unmarkObjectsInFullyCompactedHeap */ + /* begin unmarkInitialImmobileObjects */ + /* begin allOldSpaceObjectsFrom:do: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objOopSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objOopSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (oopisGreaterThanOrEqualTo(objOopSqInt, GIV(firstMobileObject))) { + goto l2; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l4; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l4:; + } + /* end unmarkInitialImmobileObjects */ +l2: + + /* begin unmarkSurvivingObjectsForCompact */ + /* begin allPastSpaceObjectsDo: */ + /* begin allPastSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + address = ((GIV(pastSpace)).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + while (oopisLessThan(objOopSqInt, GIV(pastSpaceStart))) { + assert(isEnumerableObjectNoAssert(objOopSqInt)); + if ((byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + objOopSqInt = GIV(pastSpaceStart); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l3:; + } + return; + } + + /* begin initializeCompaction */ + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + + /* begin selectSavedFirstFieldsSpace */ + spaceEstimate = (GIV(endOfMemory) - GIV(nilObj)) / 40; + sizeOfEden = (((GIV(eden)).limit)) - (((GIV(eden)).start)); + if (spaceEstimate > sizeOfEden) { + /* begin findHighestSuitableFreeBlock: */ + if ((largestFreeChunk = findLargestFreeChunk())) { + if (((bytesInBody(largestFreeChunk)) >= spaceEstimate) + && ((((usqInt)largestFreeChunk)) > (((usqInt)(GIV(endOfMemory) - GIV(totalFreeOldSpace)))))) { + highestSuitableFreeBlock = largestFreeChunk; + goto l8; + } + } + highestSuitableFreeBlock = null; + /* end findHighestSuitableFreeBlock: */ +l8: + if (highestSuitableFreeBlock) { + if ((spaceEstimate > ((/* begin sizeOfFree: */ + assert(isFreeObject(highestSuitableFreeBlock)), + bytesInBody(highestSuitableFreeBlock)))) + && (useSegmentForSavedFirstFieldsSpace(spaceEstimate))) { + goto l5; + } + if (((/* begin sizeOfFree: */ + assert(isFreeObject(highestSuitableFreeBlock)), + bytesInBody(highestSuitableFreeBlock))) > sizeOfEden) { + /* begin useFreeChunkForSavedFirstFieldsSpace: */ + assert(validFreeTreeChunk(highestSuitableFreeBlock)); + (GIV(savedFirstFieldsSpace).start = highestSuitableFreeBlock + (4 /* freeChunkLargerIndex */ * BytesPerOop)); + (GIV(savedFirstFieldsSpace).limit = addressAfter(highestSuitableFreeBlock)); + GIV(savedFirstFieldsSpaceNotInOldSpace) = 0; + assert(!((savedFirstFieldsSpaceWasAllocated()))); + goto l5; + } + } + if (useSegmentForSavedFirstFieldsSpace(spaceEstimate)) { + goto l5; + } + } + + /* begin useEdenForSavedFirstFieldsSpace */ + (GIV(savedFirstFieldsSpace).start = ((GIV(eden)).start)); + (GIV(savedFirstFieldsSpace).limit = ((GIV(eden)).limit)); + GIV(savedFirstFieldsSpaceNotInOldSpace) = 1; + assert(!((savedFirstFieldsSpaceWasAllocated()))); + /* end selectSavedFirstFieldsSpace */ +l5: + + /* begin unpinRememberedSet */ + /* begin fetchPointer:ofObject: */ + GIV(firstFieldOfRememberedSet) = longAt((void *)(((longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord()))))))) + BaseHeaderSize) + (0U << (shiftForWord())))); + setIsPinnedOfto(longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))), 0); + + /* begin resetFreeListHeads */ + GIV(freeListsMask) = 0; + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + GIV(freeLists)[i] = 0; + } + + /* begin prepareObjStacksForPlanningCompactor */ + /* begin prepareObjStackForPlanningCompactor: */ + if (GIV(markStack) == GIV(nilObj)) { + goto l7; + } + stackOrNil = GIV(markStack); + do { + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(stackOrNil + 4),((((long32At((void *)(stackOrNil + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + ((longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) & (identityHashHalfWordMask()))); + } while(((stackOrNil = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))) != 0); + /* end prepareObjStackForPlanningCompactor: */ +l7: + if (GIV(weaklingStack) == GIV(nilObj)) { + goto l9; + } + stackOrNil = GIV(weaklingStack); + do { + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(stackOrNil + 4),((((long32At((void *)(stackOrNil + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + ((longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) & (identityHashHalfWordMask()))); + } while(((stackOrNil = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))) != 0); + /* end prepareObjStackForPlanningCompactor: */ +l9: + if (GIV(mournQueue) == GIV(nilObj)) { + goto l6; + } + stackOrNil = GIV(mournQueue); + do { + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(stackOrNil + 4),((((long32At((void *)(stackOrNil + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + ((longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) & (identityHashHalfWordMask()))); + } while(((stackOrNil = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))) != 0); + /* end prepareObjStackForPlanningCompactor: */ +l6: + GIV(totalFreeOldSpace) = 0; + GIV(gcPhaseInProgress) = SlidingCompactionInProgress; + (GIV(savedFirstFieldsSpace).top = ((GIV(savedFirstFieldsSpace).start)) - BytesPerOop); + firstPass = 1; + while (1) { + finalPass = planCompactSavingForwarders(); + assert((validRelocationPlanInPass(finalPass)) == 0); + updatePointers(); + copyAndUnmark(firstPass); + + /* Would like to check here, but can't if multi-pass. */ + + /* Currently we do only a single pass if a normal GC, assuming that a pass will + always compact plenty of space. But we should perhaps check this assumption + by looking at the large free tree and seeing that the ratio of the largest free + chunk to the total ammount of free space is high. */ + if (finalPass + || (GIV(biasForGC))) break; + firstPass = 0; + reinitializeScanFrom(GIV(firstFreeObject)); + if (!(GIV(firstMobileObject) > GIV(firstFreeObject))) { + /* We get here when firstFreeObject is the last object in memory. + firstMobileObject may be random value from previous compaction + in this case, and heap is fully compacted. Abort compaction. + We set firstFreeObject to firstMobileObject for unmarking. */ + GIV(firstFreeObject) = GIV(firstMobileObject); + unmarkObjectsFromFirstFreeObject(); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + endCompaction(); + return; + } + + /* begin updateSavedFirstFieldsSpaceIfNecessary */ + if ((/* savedFirstFieldsSpaceInFreeChunk */ + (!GIV(savedFirstFieldsSpaceNotInOldSpace)) + && (oopisGreaterThan((GIV(savedFirstFieldsSpace).start), GIV(nilObj)))) + && (((GIV(savedFirstFieldsSpace).start)) < GIV(lastMobileObject))) { + spaceEstimate = ((GIV(savedFirstFieldsSpace).limit)) - ((GIV(savedFirstFieldsSpace).start)); + + /* begin findHighestSuitableFreeBlock: */ + if ((largestFreeChunkSqInt = findLargestFreeChunk())) { + if (((bytesInBody(largestFreeChunkSqInt)) >= spaceEstimate) + && ((((usqInt)largestFreeChunkSqInt)) > (((usqInt)(GIV(endOfMemory) - GIV(totalFreeOldSpace)))))) { + largestFreeChunk = largestFreeChunkSqInt; + goto l1; + } + } + largestFreeChunk = null; + /* end findHighestSuitableFreeBlock: */ +l1: + if (largestFreeChunk) { + /* begin useFreeChunkForSavedFirstFieldsSpace: */ + assert(validFreeTreeChunk(largestFreeChunk)); + (GIV(savedFirstFieldsSpace).start = largestFreeChunk + (4 /* freeChunkLargerIndex */ * BytesPerOop)); + (GIV(savedFirstFieldsSpace).limit = addressAfter(largestFreeChunk)); + GIV(savedFirstFieldsSpaceNotInOldSpace) = 0; + assert(!((savedFirstFieldsSpaceWasAllocated()))); + } + else { + /* begin useEdenForSavedFirstFieldsSpace */ + (GIV(savedFirstFieldsSpace).start = ((GIV(eden)).start)); + (GIV(savedFirstFieldsSpace).limit = ((GIV(eden)).limit)); + GIV(savedFirstFieldsSpaceNotInOldSpace) = 1; + assert(!((savedFirstFieldsSpaceWasAllocated()))); + } + } + (GIV(savedFirstFieldsSpace).top = ((GIV(savedFirstFieldsSpace).start)) - BytesPerOop); + } + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + endCompaction(); +} + + +/* Sweep the mobile portion of the heap, moving objects to their eventual + locations, and clearing their marked bits. + Remember to update the savedFirstFields of pointer objects, as these have + been forwarded. + Answer if the end of the heap was reached (savedFirstFieldsSpace has not + overflowed). + The enumerations in planCompactSavingForwarders, + updatePointersInMobileObjects and copyAndUnmarkMobileObjects + match. We could implement them as a single enumeration method taking + several block arguments, but arguably that + would make understanding an already tricky algorithm more difficult. + Instead we tolerate the duplication and encourage + the reader to diff the three methods to see where they diverge (e.g. via + Cmd-shift-C). */ + + /* SpurPlanningCompactor>>#copyAndUnmarkMobileObjects */ +static NeverInline sqInt +copyAndUnmarkMobileObjects(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt availableSpace; + usqInt bytes; + sqInt destObj; + sqInt finalObject; + sqInt firstField; + sqInt followingWord; + usqInt followingWordAddress; + usqInt limit; + usqInt next; + usqInt nextBytes; + sqInt nextObj; + usqInt numSlots; + usqInt obj; + usqInt objBytes; + sqInt objOop; + usqInt previousPin; + sqInt prevObj; + sqInt prevPrevObj; + SpurSegmentInfo *seg; + usqInt start; + usqInt startOfPreviousPin; + usqInt toFinger; + usqInt top; + + availableSpace = 0; + previousPin = 0; + assert(!((isMarked(GIV(firstFreeObject))))); + toFinger = /* startOfObject: */ + ((byteAt((void *)(GIV(firstFreeObject) + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? GIV(firstFreeObject) - BaseHeaderSize + : GIV(firstFreeObject)); + top = (GIV(savedFirstFieldsSpace).start); + startOfPreviousPin = 0; + finalObject = (!(GIV(lastMobileObject)) + ? GIV(nilObj) + : GIV(lastMobileObject)); + + /* begin allOldSpaceEntitiesForCompactingFrom:to:do: */ + assert(isOldObject(GIV(firstFreeObject))); + assert(oopisLessThanOrEqualTo(finalObject, GIV(endOfMemory))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstFreeObject); + limit = (oopisLessThan(finalObject, GIV(endOfMemory)) + ? addressAfter(finalObject) + : GIV(endOfMemory)); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, limit))) break; + assert((long64At((void *)(objOop))) != 0); + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + nextObj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + nextObj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1: + assert((previousPin == null + ? toFinger <= (startOfObject(objOop)) + : (isMarked(previousPin)) + && (toFinger <= startOfPreviousPin))); + assert(GIV(savedFirstFieldsSpaceNotInOldSpace) + || (toFinger < top)); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + if (!previousPin) { + previousPin = objOop; + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + } + } + else { + bytes = bytesInBody(objOop); + while ((toFinger <= startOfPreviousPin) + && ((bytes != ((availableSpace = startOfPreviousPin - toFinger))) + && ((bytes + (16)) > availableSpace))) { + /* The object does not fit in the space between toFinger and previousPin. + Move toFinger up to point at the first unmarked or mobile object after + previousPin, or, if previousPin is contiguous with o, to the start of this + object. Update previousPin to be the next pinned object above toFInger + and below this object, or nil if no such pinned object exists. + Any unfillable gaps between adjacent pinned objects will be freed. */ + if (availableSpace > 0) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += availableSpace; + freeChunkWithBytesat(availableSpace, toFinger); + } + do { + assert((isMarked(previousPin)) + && (isPinned(previousPin))); + + /* begin unmarkPinned: */ + if (((longAt((void *)(previousPin))) & (classIndexMask())) == (segmentBridgePun())) { + assert(isMarked(previousPin)); + } + else { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(previousPin))); + byteAtput((void *)(previousPin + (markBitsByteOffset())),(byteAt((void *)(previousPin + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + + /* begin notePinned: */ + assert(isPinned(previousPin)); + assert(!((isSegmentBridge(previousPin)))); + seg = segmentContainingObj(previousPin); + (seg->containsPinned = 1); + } + toFinger = addressAfter(previousPin); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(toFinger + (numSlotsFieldByteOffset()))); + previousPin = (numSlots == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + } while((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && ((((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0) + && (previousPin < objOop))); + + /* Now previousPin is either equal to o or mobile. + Move it to the next pinned object below o */ + while (!((previousPin >= objOop) + || ((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && (((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + previousPin = oldSpaceObjectAfter(previousPin); + } + if (previousPin >= objOop) { + previousPin = null; + startOfPreviousPin = 0; + } + else { + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(previousPin + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? previousPin - BaseHeaderSize + : previousPin); + } + } + firstField = longAt((void *)(top)); + + /* begin copyAndUnmarkObject:to:bytes:firstField: */ + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + destObj = (numSlots == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + start = /* startOfObject:given: */ + (numSlots == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + + /* memmove must be used since the ranges may overlap. */ + memmove(((void *)toFinger), ((void *)start), bytes); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(destObj)) + && (!(isForwarded(destObj)))); + assert(validStorePointerUncheckedArgs(0, destObj, firstField)); + longAtput((void *)((destObj + BaseHeaderSize) + (0U << (shiftForWord()))),firstField); + toFinger += bytes; + if (((top += BytesPerOop)) >= ((GIV(savedFirstFieldsSpace).limit))) { + assert(((GIV(savedFirstFieldsSpace).top)) == (top - BytesPerOop)); + assert(nextObj == GIV(objectAfterLastMobileObject)); + if (!previousPin) { + previousPin = nextObj; + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(nextObj + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? nextObj - BaseHeaderSize + : nextObj); + } + + /* Create a free object for firstFreeObject to be set to on the next pass, but + do not link it into the free tree as it will be written over in that next pass. */ + if (toFinger < startOfPreviousPin) { + GIV(firstFreeObject) = initFreeChunkWithBytesat(startOfPreviousPin - toFinger, toFinger); + } + else { + GIV(firstFreeObject) = previousPin; + } + return 0; + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + objOop = nextObj; + } + freeFromupTonextObject(toFinger, GIV(endOfMemory), (!(previousPin) + ? (!(GIV(objectAfterLastMobileObject)) + ? objectAfter(GIV(firstFreeObject)) + : GIV(objectAfterLastMobileObject)) + : previousPin)); + + /* begin coalesceFrom: */ + next = 0; + if (toFinger >= GIV(endOfMemory)) { + goto l2; + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(toFinger + (numSlotsFieldByteOffset()))); + obj = (numSlots == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + while (1) { + next = oldSpaceObjectAfter(obj); + if (!(next < GIV(endOfMemory))) break; + if ((((longAt((void *)(obj))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) + && (((longAt((void *)(next))) & (classIndexMask())) == (isFreeObjectClassIndexPun()))) { + objBytes = bytesInBody(obj); + nextBytes = bytesInBody(next); + unlinkFreeChunkchunkBytes(obj, objBytes); + unlinkFreeChunkchunkBytes(next, nextBytes); + obj = freeChunkWithBytesat(objBytes + nextBytes, /* startOfObject: */ + ((byteAt((void *)(obj + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? obj - BaseHeaderSize + : obj)); + } + else { + obj = next; + } + } + /* end coalesceFrom: */ +l2: + return 1; +} + + +/* Sweep the heap, unmarking all objects and moving mobile objects to their + correct positions, + restoring their savedFirstFields. */ + + /* SpurPlanningCompactor>>#copyAndUnmark: */ +static NoDbgRegParms NeverInline void +copyAndUnmark(sqInt firstPass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt finalPass; + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + + if (firstPass) { + /* begin unmarkInitialImmobileObjects */ + /* begin allOldSpaceObjectsFrom:do: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objOopSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objOopSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (oopisGreaterThanOrEqualTo(objOopSqInt, GIV(firstMobileObject))) { + goto l2; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + /* end unmarkInitialImmobileObjects */ +l2:; + } + finalPass = copyAndUnmarkMobileObjects(); + if ((GIV(lastMobileObject)) + && ((!finalPass) + && (GIV(biasForGC)))) { + unmarkObjectsFromFirstFreeObject(); + } +} + + /* SpurPlanningCompactor>>#endCompaction */ +static void +endCompaction(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + + /* begin unmarkSurvivingObjectsForCompact */ + /* begin allPastSpaceObjectsDo: */ + /* begin allPastSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + address = ((GIV(pastSpace)).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + while (oopisLessThan(objOopSqInt, GIV(pastSpaceStart))) { + assert(isEnumerableObjectNoAssert(objOopSqInt)); + if ((byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + objOopSqInt = GIV(pastSpaceStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + + /* begin endSlidingCompaction */ + GIV(gcPhaseInProgress) = 0; + if (GIV(rememberedSetSize) > 0) { + objOop = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(0, objOop, GIV(firstFieldOfRememberedSet))); + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),GIV(firstFieldOfRememberedSet)); + } + setIsPinnedOfto(longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))), 1); + + /* begin relocateRememberedSet */ + GIV(rememberedSet) = firstIndexableField(longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord()))))))); + if (/* savedFirstFieldsSpaceWasAllocated */ + GIV(savedFirstFieldsSpaceNotInOldSpace) + && (oopisGreaterThan((GIV(savedFirstFieldsSpace).start), GIV(nilObj)))) { + sqDeallocateMemorySegmentAtOfSize(((void *)((GIV(savedFirstFieldsSpace).start))), ((GIV(savedFirstFieldsSpace).limit)) - ((GIV(savedFirstFieldsSpace).start))); + } +} + + +/* Free from toFinger up to limit, dealing with possible intervening pinned + objects. + */ + + /* SpurPlanningCompactor>>#freeFrom:upTo:nextObject: */ +static NoDbgRegParms void +freeFromupTonextObject(usqInt initialToFinger, usqInt limit, sqInt nextObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + sqInt obj; + usqInt objStart; + SpurSegmentInfo *seg; + usqInt toFinger; + + toFinger = initialToFinger; + objStart = /* startOfObject: */ + ((byteAt((void *)(nextObject + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? nextObject - BaseHeaderSize + : nextObject); + if (toFinger < objStart) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += objStart - toFinger; + freeChunkWithBytesat(objStart - toFinger, toFinger); + } + toFinger = objStart; + while (objStart < limit) { + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(objStart + (numSlotsFieldByteOffset()))); + obj = (numSlots == (numSlotsMask()) + ? objStart + BaseHeaderSize + : objStart); + if ((((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && (((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)) { + /* begin unmarkPinned: */ + if (((longAt((void *)(obj))) & (classIndexMask())) == (segmentBridgePun())) { + assert(isMarked(obj)); + } + else { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(obj))); + byteAtput((void *)(obj + (markBitsByteOffset())),(byteAt((void *)(obj + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + + /* begin notePinned: */ + assert(isPinned(obj)); + assert(!((isSegmentBridge(obj)))); + seg = segmentContainingObj(obj); + (seg->containsPinned = 1); + } + if (toFinger < objStart) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += objStart - toFinger; + freeChunkWithBytesat(objStart - toFinger, toFinger); + } + toFinger = (objStart = addressAfter(obj)); + } + else { + objStart = addressAfter(obj); + } + } + if (limit > toFinger) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += limit - toFinger; + freeChunkWithBytesat(limit - toFinger, toFinger); + } +} + + /* SpurPlanningCompactor>>#isMobile: */ +static NoDbgRegParms sqInt +isMobile(sqInt obj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (oopisGreaterThanOrEqualToandLessThanOrEqualTo(obj, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)); +} + + +/* For asserts */ + + /* SpurPlanningCompactor>>#isPostMobile: */ +static NoDbgRegParms sqInt +isPostMobile(sqInt obj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return oopisGreaterThanOrEqualToandLessThanOrEqualTo(obj, GIV(mobileStart), GIV(lastMobileObject)); +} + + +/* Sweep the heap from firstFreeObject forwarding marked objects to where + they can be moved to, saving their forwarding pointer in + savedFirstFieldsSpace. Continue until either the end of the heap is + reached or savedFirstFieldsSpace is full. + Answer if the end of the heap was reached (savedFirstFieldsSpace has not + overflowed). + The enumerations in planCompactSavingForwarders, + updatePointersInMobileObjects and copyAndUnmarkMobileObjects + match. We could implement them as a single enumeration method taking + several block arguments, but arguably that + would make understanding an already tricky algorithm more difficult. + Instead we tolerate the duplication and encourage + the reader to diff the three methods to see where they diverge (e.g. via + Cmd-shift-C). */ + + /* SpurPlanningCompactor>>#planCompactSavingForwarders */ +static NeverInline sqInt +planCompactSavingForwarders(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt availableSpace; + usqInt bytes; + sqInt eventualLocation; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + usqInt previousPin; + sqInt prevObj; + sqInt prevPrevObj; + usqInt startOfPreviousPin; + usqInt toFinger; + usqInt top; + + availableSpace = 0; + previousPin = 0; + assert(!((isMarked(GIV(firstFreeObject))))); + toFinger = /* startOfObject: */ + ((byteAt((void *)(GIV(firstFreeObject) + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? GIV(firstFreeObject) - BaseHeaderSize + : GIV(firstFreeObject)); + top = (GIV(savedFirstFieldsSpace).start); + startOfPreviousPin = 0; + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(firstFreeObject))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstFreeObject); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + assert((previousPin == null + ? toFinger <= (startOfObject(objOop)) + : (isMarked(previousPin)) + && (toFinger <= startOfPreviousPin))); + assert(GIV(savedFirstFieldsSpaceNotInOldSpace) + || (toFinger < top)); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + if (!previousPin) { + previousPin = objOop; + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + } + } + else { + bytes = bytesInBody(objOop); + while ((toFinger <= startOfPreviousPin) + && ((bytes != ((availableSpace = startOfPreviousPin - toFinger))) + && ((bytes + (16)) > availableSpace))) { + /* The object does not fit in the space between toFinger and previousPin. + Move toFinger up to point at the first unmarked or mobile object after + previousPin, or, if previousPin is contiguous with o, to the start of this + object. Update previousPin to be the next pinned object above toFInger + and below this object, or nil if no such pinned object exists. + Any unfillable gaps between adjacent pinned objects will be freed. */ + do { + toFinger = addressAfter(previousPin); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(toFinger + (numSlotsFieldByteOffset()))); + previousPin = (numSlots == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + } while((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && ((((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0) + && (previousPin < objOop))); + + /* Now previousPin is either equal to o or mobile. + Move it to the next pinned object below o */ + while (!((previousPin >= objOop) + || ((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && (((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + previousPin = oldSpaceObjectAfter(previousPin); + } + if (previousPin >= objOop) { + previousPin = null; + startOfPreviousPin = 0; + } + else { + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(previousPin + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? previousPin - BaseHeaderSize + : previousPin); + } + } + + /* begin forwardMobileObject:to:savedFirstFieldPtr: */ + GIV(lastMobileObject) = objOop; + eventualLocation = ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + longAtput((void *)(top),longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(0, objOop, eventualLocation)); + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),eventualLocation); + toFinger += bytes; + if (((top += BytesPerOop)) >= ((GIV(savedFirstFieldsSpace).limit))) { + (GIV(savedFirstFieldsSpace).top = top - BytesPerOop); + GIV(objectAfterLastMobileObject) = oldSpaceObjectAfter(GIV(lastMobileObject)); + return 0; + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* If the heap is already fully compacted there will be no lastMobileObject... */ + if (GIV(lastMobileObject)) { + (GIV(savedFirstFieldsSpace).top = top - BytesPerOop); + GIV(objectAfterLastMobileObject) = oldSpaceObjectAfter(GIV(lastMobileObject)); + } + return 1; +} + + +/* Search for firstFreeObject and firstMobileObject from initialObject, which + is the + hiddenRootsObject on the first pass, and the objectAfterLastMobileObject + on subsequent passes). */ + + /* SpurPlanningCompactor>>#reinitializeScanFrom: */ +static NoDbgRegParms void +reinitializeScanFrom(sqInt initialObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + + GIV(firstFreeObject) = scanForFirstFreeAndFirstMobileObjectFrom(initialObject); + if (GIV(firstFreeObject)) { + GIV(mobileStart) = /* startOfObject: */ + ((byteAt((void *)(GIV(firstFreeObject) + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? GIV(firstFreeObject) - BaseHeaderSize + : GIV(firstFreeObject)); + } + if (GIV(objectAfterLastMobileObject)) { + /* begin allOldSpaceEntitiesFrom:to:do: */ + assert((isNonImmediate(GIV(firstFreeObject))) + && (isInSegments(GIV(firstFreeObject)))); + assert((isNonImmediate(GIV(objectAfterLastMobileObject))) + && (isInSegments(GIV(objectAfterLastMobileObject)))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstFreeObject); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThanOrEqualTo(objOop, GIV(objectAfterLastMobileObject)))) break; + assert((long64At((void *)(objOop))) != 0); + if (!((((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0) + || ((!(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) + || (GIV(objectAfterLastMobileObject) == objOop)))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + GIV(firstMobileObject) = GIV(objectAfterLastMobileObject); + } +} + + +/* Scavenge or simply follow objOop. Answer the new location of objOop. + The send should have been guarded by a send of shouldRemapOop:. + The method is called remapObj: for compatibility with ObjectMemory. */ + + /* SpurPlanningCompactor>>#remapObj: */ +sqInt +remapObj(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt resolvedObj; + + /* begin slidingCompactionRemapObj: */ + assert(shouldRemapOop(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + resolvedObj = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(resolvedObj & (tagMask())))) + && ((!((longAt((void *)(resolvedObj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + resolvedObj = longAt((void *)((resolvedObj + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + else { + assert(!((isInFutureSpace(objOop)))); + resolvedObj = objOop; + } + if (GIV(gcPhaseInProgress) > 0) { + if (GIV(gcPhaseInProgress) == ScavengeInProgress) { + if ((/* isReallyYoung: */ + ((!(resolvedObj & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(resolvedObj)), + (oopisLessThan(resolvedObj, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(resolvedObj, GIV(newSpaceStart)))))) + && (!(oopisGreaterThanOrEqualToandLessThan(resolvedObj, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))))) { + return copyAndForward(resolvedObj); + } + } + else { + assert(slidingCompactionInProgress()); + if (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(objOop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0))) { + return longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + } + return resolvedObj; +} + + /* SpurPlanningCompactor>>#savedFirstFieldsSpaceWasAllocated */ +static sqInt +savedFirstFieldsSpaceWasAllocated(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(savedFirstFieldsSpaceNotInOldSpace) + && (oopisGreaterThan((GIV(savedFirstFieldsSpace).start), GIV(nilObj))); +} + + +/* Scan from initialObject, setting firstMobileObject to the first marked + object after the first free object found, or endOfMemory if none is found. + Answer the first free object found, or nil if none. */ + + /* SpurPlanningCompactor>>#scanForFirstFreeAndFirstMobileObjectFrom: */ +static NoDbgRegParms sqInt +scanForFirstFreeAndFirstMobileObjectFrom(sqInt initialObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firstFree; + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + + firstFree = 0; + GIV(firstMobileObject) = GIV(endOfMemory); + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(initialObject)); + prevPrevObj = (prevObj = null); + objOop = initialObject; + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (firstFree) { + GIV(firstMobileObject) = objOop; + return firstFree; + } + } + else { + if (!firstFree) { + firstFree = objOop; + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + return firstFree; +} + + +/* Answer if the obj should be scavenged, or simply followed. Sent via the + compactor from shouldRemapObj:. We test for being already scavenged + because mapStackPages + via mapInterpreterOops may be applied twice in the context of a global GC + where a + scavenge, followed by a scan-mark-free, and final compaction passes may + result in + scavenged fields being visited twice. */ + + /* SpurPlanningCompactor>>#shouldRemapObj: */ +sqInt +shouldRemapObj(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return /* slidingCompactionShouldRemapObj: */ + ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + || ((GIV(gcPhaseInProgress) > 0) + && ((GIV(gcPhaseInProgress) == ScavengeInProgress + ? ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(objOop)), + (oopisLessThan(objOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(objOop, GIV(newSpaceStart))))) + && (!(oopisGreaterThanOrEqualToandLessThan(objOop, ((GIV(futureSpace)).start), GIV(futureSurvivorStart)))) + : /* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(objOop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0))))); +} + + +/* Sweep the final immobile heap, freeing and coalescing unmarked and free + objects, and unmarking all marked objects up to the end of memory. */ + + /* SpurPlanningCompactor>>#unmarkObjectsFromFirstFreeObject */ +static void +unmarkObjectsFromFirstFreeObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt freeBytes; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + SpurSegmentInfo *seg; + usqInt startOfFree; + + startOfFree = 0; + freeBytes = 0; + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(firstFreeObject))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstFreeObject); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (startOfFree) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += freeBytes; + freeChunkWithBytesat(freeBytes, startOfFree); + startOfFree = null; + freeBytes = 0; + } + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + /* begin unmarkPinned: */ + if (((longAt((void *)(objOop))) & (classIndexMask())) == (segmentBridgePun())) { + assert(isMarked(objOop)); + } + else { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + + /* begin notePinned: */ + assert(isPinned(objOop)); + assert(!((isSegmentBridge(objOop)))); + seg = segmentContainingObj(objOop); + (seg->containsPinned = 1); + } + } + else { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + else { + if (!startOfFree) { + startOfFree = /* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + } + freeBytes += bytesInBody(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + if (startOfFree) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += freeBytes; + freeChunkWithBytesat(freeBytes, startOfFree); + } +} + + +/* Sweep the heap, updating all objects to their eventual locations. + Remember to update the savedFirstFields of pointer objects, as these have + been forwarded. */ + + /* SpurPlanningCompactor>>#updatePointers */ +static NeverInline void +updatePointers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt availableSpace; + usqInt bytes; + sqInt classIndex; + sqInt contextSize; + sqInt fmt; + sqInt followingWord; + usqInt followingWordAddress; + sqInt fwd; + sqInt header; + sqInt headerSqInt; + sqInt heapEntity; + sqInt i; + usqInt numLiterals; + usqInt numPointerSlots; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objOop; + sqInt objOopSqInt; + sqInt onePass; + sqInt oop; + usqInt previousPin; + sqInt prevObj; + sqInt prevPrevObj; + sqInt sp; + usqInt startOfPreviousPin; + usqInt toFinger; + usqInt top; + + if (!(GIV(lastMobileObject))) { + return; + } + assert((startOfObject(GIV(firstFreeObject))) == GIV(mobileStart)); + mapInterpreterOops(); + mapExtraRoots(); + + /* begin updatePointersInManagerHeapEntities */ + /* begin relocateObjStacksForPlanningCompactor */ + GIV(markStack) = relocateObjStackForPlanningCompactorandContents(GIV(markStack), 0); + GIV(weaklingStack) = relocateObjStackForPlanningCompactorandContents(GIV(weaklingStack), 0); + GIV(mournQueue) = relocateObjStackForPlanningCompactorandContents(GIV(mournQueue), 1); + if ((GIV(rememberedSetSize) > 0) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(GIV(firstFieldOfRememberedSet), GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(GIV(firstFieldOfRememberedSet) + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + GIV(firstFieldOfRememberedSet) = longAt((void *)((GIV(firstFieldOfRememberedSet) + BaseHeaderSize) + (0U << (shiftForWord())))); + } + heapEntity = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))); + + /* begin relocateObjectsInHeapEntity:from:to: */ + for (i = 1; i < GIV(rememberedSetSize); i += 1) { + oop = longAt((void *)((heapEntity + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert(isMarked(oop)); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(heapEntity)) + && (!(isForwarded(heapEntity)))); + assert(validStorePointerUncheckedArgs(i, heapEntity, fwd)); + longAtput((void *)((heapEntity + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + if (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(heapEntity, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(heapEntity + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0))) { + /* fetchPointer:ofObject: */ + longAt((void *)((heapEntity + BaseHeaderSize) + (0U << (shiftForWord())))); + } + else { + } + + /* begin updatePointersInSurvivingObjects */ + /* begin allPastSpaceObjectsDo: */ + /* begin allPastSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + address = ((GIV(pastSpace)).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + while (oopisLessThan(objOopSqInt, GIV(pastSpaceStart))) { + assert(isEnumerableObjectNoAssert(objOopSqInt)); + + /* begin updatePointersIn: */ + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objOopSqInt + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l3; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOopSqInt))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l3: + numPointerSlots = CtxtTempFrameStart + contextSize; + goto l2; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOopSqInt)) > (isForwardedObjectClassIndexPun())); + numPointerSlots = (((numSlots = byteAt((void *)(objOopSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOopSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l2; + } + if (fmt == (forwardedFormat())) { + numPointerSlots = 1; + goto l2; + } + if (fmt < (firstCompiledMethodFormat())) { + numPointerSlots = 0; + goto l2; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOopSqInt)); + headerSqInt = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numPointerSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l2: + for (i = 0; i < numPointerSlots; i += 1) { + oop = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert((isMarked(oop)) + || (objOopSqInt == (hiddenRootsObject()))); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOopSqInt)) + && (!(isForwarded(objOopSqInt)))); + assert(validStorePointerUncheckedArgs(i, objOopSqInt, fwd)); + longAtput((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + objOopSqInt = GIV(pastSpaceStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + + /* begin updatePointersInInitialImmobileObjects */ + /* begin allOldSpaceObjectsFrom:do: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objOopSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objOopSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (oopisGreaterThanOrEqualTo(objOopSqInt, GIV(firstFreeObject))) { + goto l7; + } + + /* would like to assert this, but it isn't true if more than one pass: self assert: (manager isMarked: o). */ + + /* begin updatePointersIn: */ + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objOopSqInt + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l4; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOopSqInt))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l4: + numPointerSlots = CtxtTempFrameStart + contextSize; + goto l6; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOopSqInt)) > (isForwardedObjectClassIndexPun())); + numPointerSlots = (((numSlots = byteAt((void *)(objOopSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOopSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l6; + } + if (fmt == (forwardedFormat())) { + numPointerSlots = 1; + goto l6; + } + if (fmt < (firstCompiledMethodFormat())) { + numPointerSlots = 0; + goto l6; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOopSqInt)); + headerSqInt = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numPointerSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l6: + for (i = 0; i < numPointerSlots; i += 1) { + oop = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert((isMarked(oop)) + || (objOopSqInt == (hiddenRootsObject()))); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOopSqInt)) + && (!(isForwarded(objOopSqInt)))); + assert(validStorePointerUncheckedArgs(i, objOopSqInt, fwd)); + longAtput((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l5; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l5:; + } + /* end updatePointersInInitialImmobileObjects */ +l7: + + /* begin updatePointersInMobileObjects */ + availableSpace = 0; + previousPin = 0; + assert(!((isMarked(GIV(firstFreeObject))))); + toFinger = /* startOfObject: */ + ((byteAt((void *)(GIV(firstFreeObject) + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? GIV(firstFreeObject) - BaseHeaderSize + : GIV(firstFreeObject)); + top = (GIV(savedFirstFieldsSpace).start); + startOfPreviousPin = 0; + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(firstFreeObject))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstFreeObject); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + assert((previousPin == null + ? toFinger <= (startOfObject(objOop)) + : (isMarked(previousPin)) + && (toFinger <= startOfPreviousPin))); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + if (!previousPin) { + previousPin = objOop; + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + } + + /* begin updatePointersIn: */ + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l8; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l8: + numPointerSlots = CtxtTempFrameStart + contextSize; + goto l10; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numPointerSlots = (((numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + goto l10; + } + if (fmt == (forwardedFormat())) { + numPointerSlots = 1; + goto l10; + } + if (fmt < (firstCompiledMethodFormat())) { + numPointerSlots = 0; + goto l10; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numPointerSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l10: + for (i = 0; i < numPointerSlots; i += 1) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert((isMarked(oop)) + || (objOop == (hiddenRootsObject()))); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(i, objOop, fwd)); + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + } + else { + bytes = bytesInBody(objOop); + while ((toFinger <= startOfPreviousPin) + && ((bytes != ((availableSpace = startOfPreviousPin - toFinger))) + && ((bytes + (16)) > availableSpace))) { + /* The object does not fit in the space between toFinger and previousPin. + Move toFinger up to point at the first unmarked or mobile object after + previousPin, or, if previousPin is contiguous with o, to the start of this + object. Update previousPin to be the next pinned object above toFInger + and below this object, or nil if no such pinned object exists. + Any unfillable gaps between adjacent pinned objects will be freed. */ + do { + toFinger = addressAfter(previousPin); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(toFinger + (numSlotsFieldByteOffset()))); + previousPin = (numSlots == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + } while((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && ((((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0) + && (previousPin < objOop))); + + /* Now previousPin is either equal to o or mobile. + Move it to the next pinned object below o */ + while (!((previousPin >= objOop) + || ((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && (((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + previousPin = oldSpaceObjectAfter(previousPin); + } + if (previousPin >= objOop) { + previousPin = null; + startOfPreviousPin = 0; + } + else { + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(previousPin + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? previousPin - BaseHeaderSize + : previousPin); + } + } + updatePointersInsavedFirstFieldPointer(objOop, top); + toFinger += bytes; + if (((top += BytesPerOop)) >= ((GIV(savedFirstFieldsSpace).limit))) { + assert(((GIV(savedFirstFieldsSpace).top)) == (top - BytesPerOop)); + onePass = 0; + goto l11; + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l9; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l9:; + } + assert(((GIV(savedFirstFieldsSpace).top)) == (top - BytesPerOop)); + onePass = 1; + /* end updatePointersInMobileObjects */ +l11: + if (!onePass) { + /* begin updatePointersInObjectsOverflowingSavedFirstFieldsSpace */ + /* begin allOldSpaceObjectsFrom:do: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(objectAfterLastMobileObject))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(objectAfterLastMobileObject); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objOopSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objOopSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + /* begin updatePointersIn: */ + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objOopSqInt + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l12; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOopSqInt))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l12: + numPointerSlots = CtxtTempFrameStart + contextSize; + goto l14; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOopSqInt)) > (isForwardedObjectClassIndexPun())); + numPointerSlots = (((numSlots = byteAt((void *)(objOopSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOopSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l14; + } + if (fmt == (forwardedFormat())) { + numPointerSlots = 1; + goto l14; + } + if (fmt < (firstCompiledMethodFormat())) { + numPointerSlots = 0; + goto l14; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOopSqInt)); + headerSqInt = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numPointerSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l14: + for (i = 0; i < numPointerSlots; i += 1) { + oop = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert((isMarked(oop)) + || (objOopSqInt == (hiddenRootsObject()))); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOopSqInt)) + && (!(isForwarded(objOopSqInt)))); + assert(validStorePointerUncheckedArgs(i, objOopSqInt, fwd)); + longAtput((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l13; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l13:; + } + } +} + + +/* Sweep the pointer fields in obj, updating all references to mobile objects + to their eventual locations. + firstFieldPtr is supplied for mobile objects so that the saved first field + can be updated, and so that + the first field of a compiled method (which is its header, or reference to + a CogMethod holding its header) + can be retrieved. */ + + /* SpurPlanningCompactor>>#updatePointersIn:savedFirstFieldPointer: */ +static NoDbgRegParms void +updatePointersInsavedFirstFieldPointer(sqInt obj, sqInt firstFieldPtr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt field; + sqInt fmt; + sqInt fwd; + sqInt header; + sqInt i; + usqInt numLiterals; + sqInt numPointerSlots; + usqInt numSlots; + sqInt oop; + sqInt sp; + + fmt = (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask()); + + /* begin numPointerSlotsWhileCompactingOf:withFormat:savedFirstFieldPointer: */ + assert((firstFieldPtr) + && (isMobile(obj))); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l2; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(obj))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l2: + numPointerSlots = ((usqInt) (CtxtTempFrameStart + contextSize)); + goto l3; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())); + numPointerSlots = (((numSlots = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l3; + } + assert(!((fmt == (forwardedFormat())))); + if (fmt < (firstCompiledMethodFormat())) { + numPointerSlots = 0; + goto l3; + } + field = longAt((void *)(firstFieldPtr)); + + /* begin methodHeaderFromSavedFirstField: */ + if ((((field) & 7) == 1)) { + header = field; + goto l1; + } + assert((isNonImmediate(field)) + && (field < GIV(newSpaceStart))); + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) field))->objectHeader)); + header = ((((CogMethod *) field))->methodHeader); + /* end methodHeaderFromSavedFirstField: */ +l1: + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numPointerSlots = numLiterals + LiteralStart; + /* end numPointerSlotsWhileCompactingOf:withFormat:savedFirstFieldPointer: */ +l3: + if ((fmt <= 5 /* lastPointerFormat */) + && (numPointerSlots > 0)) { + oop = longAt((void *)(firstFieldPtr)); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert(isMarked(oop)); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + longAtput((void *)(firstFieldPtr),fwd); + } + } + + /* excludes CompiledMethod + Relocate the saved first field; Note that CompiledMethods can be excluded since their + first field is either a SmallInteger or a reference to a CogMethod outside of oldSpace. */ + for (i = 1; i < numPointerSlots; i += 1) { + oop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert((isMarked(oop)) + || (obj == (hiddenRootsObject()))); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(obj)) + && (!(isForwarded(obj)))); + assert(validStorePointerUncheckedArgs(i, obj, fwd)); + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } +} + + +/* Attempt to allocate a memory segment large enough to hold the + savedFirstFieldsSpace. Invoked when neither eden nor a large free chunk + are found to be big enough for the job. */ + + /* SpurPlanningCompactor>>#useSegmentForSavedFirstFieldsSpace: */ +static NoDbgRegParms sqInt +useSegmentForSavedFirstFieldsSpace(sqInt spaceEstimate) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt allocatedSize; + sqInt roundedSize; + void *segAddress; + + allocatedSize = 0; + roundedSize = ((spaceEstimate + 0x3FF) / 0x400) * 0x400; + if ((segAddress = sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto(roundedSize, firstGapOfSizeAtLeast(roundedSize), (&allocatedSize)))) { + (GIV(savedFirstFieldsSpace).start = ((usqIntptr_t)segAddress)); + (GIV(savedFirstFieldsSpace).limit = (((usqIntptr_t)segAddress)) + allocatedSize); + GIV(savedFirstFieldsSpaceNotInOldSpace) = 1; + assert(savedFirstFieldsSpaceWasAllocated()); + return 1; + } + + /* sent to the manager so that the simulator can increase memory to simulate a new segment */ + return 0; +} + + +/* Answer 0 if all the mobile objects from firstMobileObject to + lastMobileObject have sane forwarding addresses, and that + savedFirstFieldsSpace is of + matching capacity. Otherwise answer an error code identifying the anomaly. */ + + /* SpurPlanningCompactor>>#validRelocationPlanInPass: */ +static NoDbgRegParms sqInt +validRelocationPlanInPass(sqInt onePass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt destination; + sqInt followingWord; + usqInt followingWordAddress; + sqInt nMobiles; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt toFinger; + + nMobiles = 0; + toFinger = GIV(mobileStart); + GIV(anomaly) = null; + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(firstMobileObject))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstMobileObject); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift())))) { + nMobiles += 1; + destination = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (!(destination >= toFinger)) { + GIV(anomaly) = objOop; + return 1; + } + toFinger += bytesInBody(objOop); + if (oopisGreaterThan(objOop, GIV(lastMobileObject))) { + GIV(anomaly) = objOop; + return 2; + } + if (objOop == GIV(lastMobileObject)) { + return ((((((GIV(savedFirstFieldsSpace).top)) + BytesPerOop) - ((GIV(savedFirstFieldsSpace).start))) / BytesPerOop) == nMobiles + ? 0 + : 3); + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* N.B. written this way so that if there are no mobiles the expression evaluates to 0 in Smalltalk /and/ in C unsigned arithmetic. */ + return ((((((GIV(savedFirstFieldsSpace).top)) + BytesPerOop) - ((GIV(savedFirstFieldsSpace).start))) / BytesPerOop) == nMobiles + ? 0 + : 4); +} + + /* SpurSegmentInfo>>#segLimit */ +static NoDbgRegParms usqInt +segLimit(SpurSegmentInfo *self_in_SpurSegmentInfo) +{ + return ((self_in_SpurSegmentInfo->segSize)) + ((self_in_SpurSegmentInfo->segStart)); +} + + /* SpurSegmentManager>>#addSegmentOfSize: */ +static NoDbgRegParms SpurSegmentInfo * +addSegmentOfSize(sqInt ammount) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt allocatedSize; + sqInt i; + sqInt idx; + sqInt lastSegIndex; + SpurSegmentInfo *newSeg; + sqInt newSegIndex; + void *segAddress; + sqInt segAddressSqInt; + sqInt segIndex; + + newSegIndex = 0; + allocatedSize = 0; + if ((segAddress = sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto(ammount, firstGapOfSizeAtLeast(ammount), (&allocatedSize)))) { + segAddressSqInt = ((usqIntptr_t)segAddress); + + /* begin insertSegmentFor: */ + assert(segAddressSqInt >= (segLimit(&GIV(segments)[0]))); + if (GIV(numSegments) == GIV(numSegInfos)) { + allocateOrExtendSegmentInfos(); + } + assert(GIV(numSegments) < GIV(numSegInfos)); + segIndex = (lastSegIndex = GIV(numSegments) - 1); + GIV(numSegments) += 1; + while (1) { + if (segAddressSqInt >= ((((GIV(segments)[segIndex]).segSize)) + (((GIV(segments)[segIndex]).segStart)))) { + segIndex += 1; + for (idx = lastSegIndex; idx >= segIndex; idx += -1) { + GIV(segments)[idx + 1] = (GIV(segments)[idx]); + } + newSegIndex = segIndex; + goto l1; + } + segIndex -= 1; + } + /* end insertSegmentFor: */ +l1: + + /* Simulation insertion code duplicates entries if newSegIndex ~= numSegments - 1 */ + newSeg = (&(GIV(segments)[newSegIndex])); + (newSeg->segStart = ((usqIntptr_t)segAddress)); + (newSeg->segSize = allocatedSize); + (newSeg->swizzle = 0); + assert(!(segmentOverlap())); + bridgeFromto((&(GIV(segments)[newSegIndex - 1])), newSeg); + bridgeFromto(newSeg, (!(newSegIndex == (GIV(numSegments) - 1)) + ? (&(GIV(segments)[newSegIndex + 1])) + : 0)); + GIV(totalHeapSizeIncludingBridges) += allocatedSize; + + /* test isInMemory: */ + for (i = 0; i < GIV(numSegments); i += 1) { + assert(isInSegments(((GIV(segments)[i]).segStart))); + assert(isInSegments((segLimit(&GIV(segments)[i])) - BytesPerWord)); + assert((!(isInSegments(segLimit(&GIV(segments)[i])))) + || ((i < (GIV(numSegments) - 1)) + && ((segLimit(&GIV(segments)[i])) == (((GIV(segments)[i + 1]).segStart))))); + assert((!(isInSegments((((GIV(segments)[i]).segStart)) - BytesPerWord))) + || ((i > 0) + && ((segLimit(&GIV(segments)[i - 1])) == (((GIV(segments)[i]).segStart))))); + } + return newSeg; + } + + /* sent to the manager so that the simulator can increase memory to simulate a new segment */ + return null; +} + + +/* Adjust swizzles by firstSegmentShift. Also computes segStarts as + they were in the image when it was written, so that oops' segments + can be determined and hence oops correctly swizzled. */ + + /* SpurSegmentManager>>#adjustSegmentSwizzlesBy: */ +static NoDbgRegParms void +adjustSegmentSwizzlesBy(sqInt firstSegmentShift) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt oldBaseAddr; + SpurSegmentInfo *segInfo; + + oldBaseAddr = GIV(oldSpaceStart) - firstSegmentShift; + for (i = 0; i < GIV(numSegments); i += 1) { + segInfo = (&(GIV(segments)[i])); + (segInfo->segStart = ((segInfo->segStart)) + oldBaseAddr); + (segInfo->swizzle = ((segInfo->swizzle)) - oldBaseAddr); + } + GIV(canSwizzle) = 1; +} + + /* SpurSegmentManager>>#allBridgesMarked */ +static sqInt +allBridgesMarked(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt bridgeObj; + sqInt i; + usqInt numSlots; + + for (i = 0; i < GIV(numSegments); i += 1) { + /* begin bridgeAt: */ + /* begin bridgeFor: */ + address = (((((&(GIV(segments)[i])))->segSize)) + ((((&(GIV(segments)[i])))->segStart))) - (2 * BaseHeaderSize); + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + bridgeObj = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + assert(isValidSegmentBridge(bridgeObj)); + if (!((byteAt((void *)(bridgeObj + (markBitsByteOffset())))) & (1U << (markedBitByteShift())))) { + return 0; + } + } + return 1; +} + + +/* Increase the number of allocated segInfos by 16. */ + + /* SpurSegmentManager>>#allocateOrExtendSegmentInfos */ +static void +allocateOrExtendSegmentInfos(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt newNumSegs; + + if (!GIV(numSegInfos)) { + GIV(numSegInfos) = 16; + GIV(segments) = calloc(GIV(numSegInfos), sizeof(SpurSegmentInfo)); + return; + } + newNumSegs = GIV(numSegInfos) + 16; + GIV(segments) = realloc(GIV(segments), newNumSegs * (sizeof(SpurSegmentInfo))); + if (!GIV(segments)) { + error("out of memory; cannot allocate more segments"); + } + memset(GIV(segments) + GIV(numSegInfos), 0, (newNumSegs - GIV(numSegInfos)) * (sizeof(SpurSegmentInfo))); + GIV(numSegInfos) = newNumSegs; +} + + /* SpurSegmentManager>>#bridgeAt: */ +static NoDbgRegParms sqInt +bridgeAt(sqInt segIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + usqInt numSlots; + + /* begin bridgeFor: */ + address = (((((&(GIV(segments)[segIndex])))->segSize)) + ((((&(GIV(segments)[segIndex])))->segStart))) - (2 * BaseHeaderSize); + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + return (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); +} + + /* SpurSegmentManager>>#bridgeFor: */ +static NoDbgRegParms sqInt +bridgeFor(SpurSegmentInfo *aSegment) +{ + sqInt address; + usqInt numSlots; + + address = (((aSegment->segSize)) + ((aSegment->segStart))) - (2 * BaseHeaderSize); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + return (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); +} + + +/* Create a bridge from aSegment to the next segment, + or create a terminating bridge if there is no next segment. */ + + /* SpurSegmentManager>>#bridgeFrom:to: */ +static NoDbgRegParms void +bridgeFromto(SpurSegmentInfo *aSegment, SpurSegmentInfo *nextSegmentOrNil) +{ + sqInt bridgeSpan; + sqInt clifton; + usqInt segEnd; + + segEnd = ((aSegment->segSize)) + ((aSegment->segStart)); + + /* clifton is where the Avon bridge begins... */ + clifton = segEnd - (2 * BaseHeaderSize); + bridgeSpan = (nextSegmentOrNil + ? (((nextSegmentOrNil->segStart)) - segEnd) + (2 * BaseHeaderSize) + : 2 * BaseHeaderSize); + assert(bridgeSpan >= 0); + initSegmentBridgeWithBytesat(bridgeSpan, clifton); + + /* the revised bridge should get us to the new segment */ + assert((addressAfter(objectStartingAt(clifton))) == ((nextSegmentOrNil == null + ? segLimit(aSegment) + : (nextSegmentOrNil->segStart)))); +} + + /* SpurSegmentManager>>#checkSegments */ +static void +checkSegments(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + assert(GIV(numSegments) >= 1); + for (i = 0; i < GIV(numSegments); i += 1) { + assert(addressCouldBeObj(((GIV(segments)[i]).segStart))); + assert(isValidSegmentBridge(bridgeAt(i))); + } + assert(((segLimit(&GIV(segments)[GIV(numSegments) - 1])) - (bridgeSize())) == (GIV(endOfMemory))); +} + + +/* The image has been loaded, old segments reconstructed, and the heap + swizzled into a single contiguous segment. Collapse the segments into one. */ + + /* SpurSegmentManager>>#collapseSegmentsPostSwizzle */ +static void +collapseSegmentsPostSwizzle(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + SpurSegmentInfo *cascade0; + + GIV(canSwizzle) = 0; + GIV(numSegments) = 1; + + /* begin computeTotalHeapSizeIncludingBridges */ + GIV(totalHeapSizeIncludingBridges) = GIV(endOfMemory) - GIV(oldSpaceStart); + cascade0 = (&(GIV(segments)[0])); + (cascade0->segStart = GIV(oldSpaceStart)); + (cascade0->segSize = GIV(totalHeapSizeIncludingBridges)); + assert(isSegmentBridge(bridgeAt(0))); + assert((numSlotsOfAny(bridgeAt(0))) == 0); +} + + +/* Answer the segment limit of the first segment followed by a gap of at + least size bytes. + */ + + /* SpurSegmentManager>>#firstGapOfSizeAtLeast: */ +static NoDbgRegParms void * +firstGapOfSizeAtLeast(sqInt size) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt bridge; + sqInt i; + usqInt numSlots; + + for (i = 0; i <= (GIV(numSegments) - 2); i += 1) { + /* begin bridgeAt: */ + /* begin bridgeFor: */ + address = (((((&(GIV(segments)[i])))->segSize)) + ((((&(GIV(segments)[i])))->segStart))) - (2 * BaseHeaderSize); + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + bridge = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + if (((bytesInBody(bridge)) - (2 * BaseHeaderSize)) >= size) { + return ((void *)((((GIV(segments)[i]).segSize)) + (((GIV(segments)[i]).segStart)))); + } + } + return ((void *)((((GIV(segments)[GIV(numSegments) - 1]).segSize)) + (((GIV(segments)[GIV(numSegments) - 1]).segStart)))); +} + + /* SpurSegmentManager>>#isEmptySegment: */ +static NoDbgRegParms sqInt +isEmptySegment(SpurSegmentInfo *seg) +{ + sqInt address; + sqInt firstObj; + usqInt numSlots; + + address = (seg->segStart); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + firstObj = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + return (((longAt((void *)(firstObj))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) + && ((addressAfter(firstObj)) == ((((seg->segSize)) + ((seg->segStart))) - (2 * BaseHeaderSize))); +} + + /* SpurSegmentManager>>#isInSegments: */ +static NoDbgRegParms sqInt +isInSegments(usqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + for (i = 0; i < GIV(numSegments); i += 1) { + if (address < (((GIV(segments)[i]).segStart))) { + return 0; + } + if (address < ((((GIV(segments)[i]).segSize)) + (((GIV(segments)[i]).segStart)))) { + return 1; + } + } + return 0; +} + + +/* bridges bridge the gaps between segments. They are the last object in each + segment. + */ + + /* SpurSegmentManager>>#isValidSegmentBridge: */ +static NoDbgRegParms sqInt +isValidSegmentBridge(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((addressCouldBeObj(objOop)) + || (objOop == GIV(endOfMemory))) + && ((((longAt((void *)(objOop))) & (classIndexMask())) == (segmentBridgePun())) + && (((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) + || ((numSlotsOfAny(objOop)) == 0))); +} + + +/* Answer the the next non-empty segment or nil. The size of a segment + includes that of its bridge. A segment containing just a free object and a + bridge will still + have a size of manager bridgeSize after shortening it in + prepareForSnapshot. + */ + + /* SpurSegmentManager>>#nextNonEmptySegmentAfter: */ +static NoDbgRegParms SpurSegmentInfo * +nextNonEmptySegmentAfter(sqInt i) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt nextx; + + nextx = i; + while (1) { + if (((nextx += 1)) >= GIV(numSegments)) { + return null; + } + if ((((GIV(segments)[nextx]).segSize)) > (2 * BaseHeaderSize)) { + return (&(GIV(segments)[nextx])); + } + } + return 0; +} + + +/* shorten all segments by any trailing free space. */ + + /* SpurSegmentManager>>#prepareForSnapshot */ +static NeverInline void +prepareForSnapshot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cameFrom; + SpurSegmentInfo *cascade0; + usqInt chunkBytes; + sqInt followingWord; + usqInt followingWordAddress; + usqInt freeChunk; + sqInt i; + sqInt largeChild; + sqInt newEndOfMemory; + sqInt next; + sqInt node; + SpurSegmentInfo *seg; + SpurSegmentInfo *selfOfSegSize; + sqInt smallChild; + sqInt treeNode; + + checkSegments(); + for (i = 0; i < GIV(numSegments); i += 1) { + cascade0 = (&(GIV(segments)[i])); + (cascade0->savedSegSize = ((GIV(segments)[i]).segSize)); + (cascade0->lastFreeObject = null); + } + + /* Ideally finding the lastFreeObject of each segment would be + done in some linear pass through the heap. But for now KISS. */ + + /* begin freeTreeNodesDo: */ + treeNode = GIV(freeLists)[0]; + if (!treeNode) { + goto l2; + } + cameFrom = -1; + do { + assert((bytesInBody(treeNode)) >= ((numFreeLists()) * (allocationUnit()))); + smallChild = longAt((void *)((treeNode + BaseHeaderSize) + (3U << (shiftForWord())))); + largeChild = longAt((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + assert((smallChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), smallChild)))); + assert((largeChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), largeChild)))); + + /* apply if the node has no children, or it has no large children and we're + returning from the small child, or we're returning from the large child. */ + if (((smallChild == 0) + && (largeChild == 0)) + || ((largeChild + ? cameFrom == largeChild + : cameFrom == smallChild))) { + node = treeNode; + while (node != 0) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(node); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + next = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + next = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1: + if (((longAt((void *)(next))) & (classIndexMask())) == (segmentBridgePun())) { + seg = segmentContainingObj(node); + (seg->lastFreeObject = node); + node = 0; + } + else { + node = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + + /* and since we've applied we must move on up */ + cameFrom = treeNode; + treeNode = longAt((void *)((treeNode + BaseHeaderSize) + (2U << (shiftForWord())))); + } + else { + if ((smallChild != 0) + && (cameFrom != smallChild)) { + treeNode = smallChild; + } + else { + assert(largeChild != 0); + treeNode = largeChild; + } + cameFrom = -1; + } + } while(treeNode != 0); + /* end freeTreeNodesDo: */ +l2: + for (i = 0; i < GIV(numSegments); i += 1) { + if ((freeChunk = ((GIV(segments)[i]).lastFreeObject))) { + /* begin detachFreeObject: */ + chunkBytes = bytesInBody(freeChunk); + GIV(totalFreeOldSpace) -= chunkBytes; + unlinkFreeChunkchunkBytes(freeChunk, chunkBytes); + selfOfSegSize = (&(GIV(segments)[i])); + (selfOfSegSize->segSize = ((/* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk)) + (2 * BaseHeaderSize)) - (((GIV(segments)[i]).segStart))); + bridgeFromto((&(GIV(segments)[i])), (i < (GIV(numSegments) - 1) + ? (&(GIV(segments)[i + 1])) + : 0)); + } + } + + /* perhaps this should read + manager setEndOfMemory: 0; assimilateNewSegment: (segments at: numSegments - 1) */ + newEndOfMemory = ((((GIV(segments)[GIV(numSegments) - 1]).segSize)) + (((GIV(segments)[GIV(numSegments) - 1]).segStart))) - (2 * BaseHeaderSize); + + /* begin setEndOfMemory: */ + GIV(endOfMemory) = newEndOfMemory; + if (GIV(freeOldSpaceStart) > newEndOfMemory) { + GIV(freeOldSpaceStart) = newEndOfMemory; + } +} + + +/* Read numBytes of image data from f into memory at memoryBaseForImageRead. + Answer the number of bytes written. In addition, read each segment, build + up the + segment info for swizzling, while eliminating the bridge objects at the + end of each + segment that specify the distance to and the size of the subsequent + segment. */ + + /* SpurSegmentManager>>#readHeapFromImageFile:dataBytes: */ +static NoDbgRegParms sqInt +readHeapFromImageFiledataBytes(sqImageFile f, sqInt numBytes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bridge; + sqInt bridgehead; + usqInt bridgeSpan; + sqInt bytesRead; + usqInt newBase; + usqInt nextSegmentSize; + sqInt oldBase; + SpurSegmentInfo *segInfo; + sqInt totalBytesRead; + + allocateOrExtendSegmentInfos(); + + /* segment sizes include the two-header-word bridge at the end of each segment. */ + GIV(numSegments) = (totalBytesRead = 0); + + /* N.B. still must be adjusted by oldBaseAddr. */ + oldBase = 0; + newBase = GIV(oldSpaceStart); + nextSegmentSize = GIV(firstSegmentSize); + bridgehead = (GIV(firstSegmentSize) + GIV(oldSpaceStart)) - (2 * BaseHeaderSize); + while (1) { + segInfo = (&(GIV(segments)[GIV(numSegments)])); + (segInfo->segStart = oldBase); + (segInfo->segSize = nextSegmentSize); + (segInfo->swizzle = newBase - oldBase); + bytesRead = sqImageFileRead(pointerForOop(newBase), sizeof(char), nextSegmentSize, f); + if (bytesRead > 0) { + totalBytesRead += bytesRead; + } + if (bytesRead != nextSegmentSize) { + return totalBytesRead; + } + if (((GIV(numSegments) += 1)) >= GIV(numSegInfos)) { + allocateOrExtendSegmentInfos(); + } + bridge = bridgehead + BaseHeaderSize; + bridgeSpan = (byteAt((void *)(bridgehead + (numSlotsFieldByteOffset()))) + ? BytesPerOop * (((((usqInt)(((sqInt)((usqInt)((longAt((void *)(bridge - BaseHeaderSize)))) << 8)))))) >> 8) + : 0); + oldBase = (oldBase + nextSegmentSize) + bridgeSpan; + newBase = (newBase + nextSegmentSize) - (2 * BaseHeaderSize); + nextSegmentSize = ((usqInt)(long64At((void *)(bridge)))); + if (!(nextSegmentSize != 0)) break; + bridgehead = (bridgehead - (2 * BaseHeaderSize)) + nextSegmentSize; + } + + /* newBase should point just past the last bridge. all others should have been eliminated. */ + assert((newBase - (GIV(oldSpaceStart))) == (totalBytesRead - (GIV(numSegments) * (bridgeSize())))); + + /* set freeOldSpaceStart now for adjustAllOopsBy: */ + + /* begin setFreeOldSpaceStart: */ + GIV(freeOldSpaceStart) = newBase; + + /* we're done. nil firstSegmentSize for a subsequent snapshot. */ + GIV(firstSegmentSize) = null; + return totalBytesRead; +} + + +/* Restore all shortened segments to their proper size, re-freeing the + trailing space. + */ + + /* SpurSegmentManager>>#restorePostSnapshot */ +static void +restorePostSnapshot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt bytes; + usqInt freeChunk; + sqInt i; + sqInt newEndOfMemory; + SpurSegmentInfo *seg; + + + /* Set endOfMemory first, to avoid assert fails in freeChunkWithBytes:at:. */ + seg = (&(GIV(segments)[GIV(numSegments) - 1])); + if ((seg->lastFreeObject)) { + newEndOfMemory = (((seg->savedSegSize)) + ((seg->segStart))) - (2 * BaseHeaderSize); + + /* begin setEndOfMemory: */ + GIV(endOfMemory) = newEndOfMemory; + if (GIV(freeOldSpaceStart) > newEndOfMemory) { + GIV(freeOldSpaceStart) = newEndOfMemory; + } + } + else { + assert((GIV(endOfMemory)) == ((segLimit(seg)) - (bridgeSize()))); + } + for (i = (GIV(numSegments) - 1); i >= 0; i += -1) { + seg = (&(GIV(segments)[i])); + if ((freeChunk = (seg->lastFreeObject))) { + address = (((seg->segSize)) + ((seg->segStart))) - (2 * BaseHeaderSize); + (seg->segSize = (seg->savedSegSize)); + bridgeFromto(seg, (i < (GIV(numSegments) - 1) + ? (&(GIV(segments)[i + 1])) + : 0)); + bytes = ((((seg->segSize)) + ((seg->segStart))) - address) - (2 * BaseHeaderSize); + + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += bytes; + freeChunkWithBytesat(bytes, address); + } + } + checkSegments(); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } +} + + +/* Answer the segment containing an object. This is mostly for assert + checking, but + variations on the incremental GC may use it in anger. Binary search is (of + course) marginally slower than linear search for a single segment (e.g. in + a 720k object heap, + 67.1ms vs 61.3ms, or 9.5% slower to derive the segment containing every + old space + entity), but usefully faster for many segments (e.g. 92.7ms vs 116ms, or + 20% faster + in the same heap extended with enough large arrays to require 11 segments; + and this + is pessimal; there are fewer objects at high addresses since the large + arrays are there). */ + + /* SpurSegmentManager>>#segmentContainingObj: */ +SpurSegmentInfo * +segmentContainingObj(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt high; + sqInt low; + sqInt mid; + SpurSegmentInfo *seg; + + low = 0; + mid = GIV(numSegments) / 2; + high = GIV(numSegments) - 1; + do { + seg = (&(GIV(segments)[mid])); + if (oopisGreaterThanOrEqualTo(objOop, (seg->segStart))) { + if (mid == high) { + return (oopisLessThan(objOop, ((seg->segSize)) + ((seg->segStart))) + ? seg + : 0); + } + else { + low = mid; + mid = ((mid + high) + 1) / 2; + } + } + else { + high = mid - 1; + mid = (low + mid) / 2; + } + } while(low <= high); + return null; +} + + +/* Answers true if a segment overlaps with another one. */ + + /* SpurSegmentManager>>#segmentOverlap */ +static sqInt +segmentOverlap(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt endi; + usqInt endj; + sqInt i; + sqInt j; + usqInt starti; + usqInt startj; + + for (i = 0; i < GIV(numSegments); i += 1) { + starti = ((GIV(segments)[i]).segStart); + endi = (((GIV(segments)[i]).segSize)) + (((GIV(segments)[i]).segStart)); + for (j = 0; j < GIV(numSegments); j += 1) { + startj = ((GIV(segments)[j]).segStart); + endj = (((GIV(segments)[j]).segSize)) + (((GIV(segments)[j]).segStart)); + if (!(i == j)) { + if (!((starti < startj) + || (starti >= endj))) { + return 1; + } + if (!((endi <= startj) + || (endi >= endj))) { + return 1; + } + } + } + } + return 0; +} + + +/* Answer if any shrinkage was achieved. */ + + /* SpurSegmentManager>>#shrinkObjectMemory: */ +static NoDbgRegParms int +shrinkObjectMemory(usqInt delta) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + usqInt chunkBytes; + usqInt currentEnd; + sqInt deltaSqInt; + SpurSegmentInfo *emptySeg; + sqInt freeChunk; + sqInt i; + sqInt iSqInt; + sqInt j; + sqInt k; + usqInt numSlots; + SpurSegmentInfo *seg; + SpurSegmentInfo *segInfo; + usqInt shrinkage; + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCCheckFreeSpace)) == (GCCheckFreeSpace | GCCheckFreeSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + shrinkage = delta; + while (1) { + /* begin findEmptySegNearestInSizeTo: */ + emptySeg = null; + deltaSqInt = shrinkage; + for (k = 0; k < GIV(numSegments); k += 1) { + seg = (&(GIV(segments)[k])); + if (isEmptySegment(seg)) { + if (emptySeg) { + if ((shrinkage >= (((seg->segSize)) * 0.75)) + && ((SQABS(((sqInt) (((seg->segSize)) - shrinkage)))) < deltaSqInt)) { + emptySeg = seg; + deltaSqInt = SQABS(((sqInt) (((seg->segSize)) - shrinkage))); + } + } + else { + emptySeg = seg; + } + } + } + if ((!emptySeg) + || (((emptySeg->segSize)) > shrinkage)) { + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCCheckFreeSpace)) == (GCCheckFreeSpace | GCCheckFreeSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return shrinkage < delta; + } + shrinkage -= (emptySeg->segSize); + address = (emptySeg->segStart); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + freeChunk = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin detachFreeObject: */ + chunkBytes = bytesInBody(freeChunk); + GIV(totalFreeOldSpace) -= chunkBytes; + unlinkFreeChunkchunkBytes(freeChunk, chunkBytes); + + /* begin removeSegment: */ + i = 0; + for (iSqInt = 0; iSqInt < GIV(numSegments); iSqInt += 1) { + if (((emptySeg->segStart)) == (((GIV(segments)[iSqInt]).segStart))) { + i = iSqInt; + goto l1; + } + } + error("segment not found"); + /* end indexOfSegment: */ +l1: + assert(i > 0); + GIV(totalHeapSizeIncludingBridges) -= (emptySeg->segSize); + sqDeallocateMemorySegmentAtOfSize(((void *)((emptySeg->segStart))), (emptySeg->segSize)); + for (j = i; j <= (GIV(numSegments) - 2); j += 1) { + GIV(segments)[j] = (GIV(segments)[j + 1]); + } + GIV(numSegments) -= 1; + bridgeFromto((&(GIV(segments)[i - 1])), (i <= (GIV(numSegments) - 1) + ? (&(GIV(segments)[i])) + : 0)); + segInfo = (&(GIV(segments)[GIV(numSegments) - 1])); + + /* begin setLastSegment: */ + currentEnd = (((segInfo->segSize)) + ((segInfo->segStart))) - (2 * BaseHeaderSize); + if (currentEnd <= GIV(endOfMemory)) { + GIV(endOfMemory) = currentEnd; + if (GIV(freeOldSpaceStart) > currentEnd) { + GIV(freeOldSpaceStart) = currentEnd; + } + } + } + return 0; +} + + /* SpurSegmentManager>>#swizzleObj: */ +static NoDbgRegParms sqInt +swizzleObj(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + assert(GIV(canSwizzle)); + for (i = (GIV(numSegments) - 1); i >= 1; i += -1) { + if (objOop >= (((GIV(segments)[i]).segStart))) { + return objOop + (((GIV(segments)[i]).swizzle)); + } + } + return objOop + (((GIV(segments)[0]).swizzle)); +} + + +/* This ``slow'' count is for asserts only. */ + + /* SpurSegmentManager>>#totalBytesInSegments */ +static usqInt +totalBytesInSegments(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + usqInt total; + + total = 0; + for (i = 0; i < GIV(numSegments); i += 1) { + total += ((GIV(segments)[i]).segSize); + } + return total; +} + + /* SpurSegmentManager>>#writeImageSegmentsToFile: */ +static NoDbgRegParms sqInt +writeImageSegmentsToFile(sqImageFile aBinaryStream) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt total; + + assert(((GIV(endOfMemory)) == (segLimit(&GIV(segments)[GIV(numSegments) - 1]))) + || (((GIV(endOfMemory)) + (bridgeSize())) == (segLimit(&GIV(segments)[GIV(numSegments) - 1])))); + if (GIV(firstSegmentSize)) { + assert(GIV(firstSegmentSize) == (((GIV(segments)[0]).segSize))); + } + assert((((GIV(segments)[0]).segSize)) > 0); + total = 0; + for (i = 0; i < GIV(numSegments); i += 1) { + if ((((GIV(segments)[i]).segSize)) > (2 * BaseHeaderSize)) { + total += writeSegmentnextSegmenttoFile((&(GIV(segments)[i])), nextNonEmptySegmentAfter(i), aBinaryStream); + } + } + return total; +} + + +/* Write the segment contents, the size of and the distance to the next + segment to aBinaryStream. + */ + + /* SpurSegmentManager>>#writeSegment:nextSegment:toFile: */ +static NoDbgRegParms sqInt +writeSegmentnextSegmenttoFile(SpurSegmentInfo *segment, SpurSegmentInfo *nextSegment, sqImageFile aBinaryStream) +{ + usqLong firstSavedBridgeWord; + sqInt nWritten; + sqInt pier1; + sqInt pier2; + usqLong secondSavedBridgeWord; + + pier1 = (((segment->segSize)) + ((segment->segStart))) - (2 * BaseHeaderSize); + pier2 = pier1 + BaseHeaderSize; + assert(isValidSegmentBridge(bridgeFor(segment))); + assert((startOfObject(bridgeFor(segment))) == pier1); + + /* Temporarily change the bridge to bridge to the next non-empty segment. + The first double word of the bridge includes the bridge size in slots, and + hence specifies the distance to the next segment. The following double + word is replaced by the size of the next segment, or 0 if there isn't one. */ + firstSavedBridgeWord = long64At((void *)(pier1)); + secondSavedBridgeWord = long64At((void *)(pier2)); + bridgeFromto(segment, nextSegment); + long64Atput((void *)(pier2),(nextSegment + ? (nextSegment->segSize) + : 0)); + nWritten = sqImageFileWrite(((void *)((segment->segStart))), 1, (segment->segSize), aBinaryStream); + long64Atput((void *)(pier1),firstSavedBridgeWord); + long64Atput((void *)(pier2),secondSavedBridgeWord); + return nWritten; +} + + /* StackInterpreter>>#accessorDepthForExternalPrimitiveMethod: */ +static NoDbgRegParms sqInt +accessorDepthForExternalPrimitiveMethod(sqInt methodObj) +{ + sqInt flags; + sqInt lit; + + assert(isLinkedExternalPrimitive(methodObj)); + lit = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + flags = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFlagsIndex) << (shiftForWord())))))); + return (((flags >> 3))) >> SpurPrimitiveAccessorDepthShift; +} + + /* StackInterpreter>>#accessorDepthForPrimitiveMethod: */ +sqInt +accessorDepthForPrimitiveMethod(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + sqInt primIndex; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (aMethodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIndex = 0; + } + return ((primIndex == PrimNumberExternalCall) + && (isLinkedExternalPrimitive(aMethodObj)) + ? accessorDepthForExternalPrimitiveMethod(aMethodObj) + : ((sqInt)((primitiveMetadataTable[primIndex]))) >> SpurPrimitiveAccessorDepthShift); +} + + +/* Answer the current activeProcess. */ +/* Now used for FFI error reaping */ + + /* StackInterpreter>>#activeProcess */ +sqInt +activeProcess(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); +} + + +/* The various poll/select calls in the VM should attempt to tally the + ammount of time spent at idle here, so as to render the uptime value + meaningful. + */ + + /* StackInterpreter>>#addIdleUsecs: */ +void +addIdleUsecs(sqInt idleUsecs) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(statIdleUsecs) += idleUsecs; +} + + +/* Add the given process to the end of the given linked list + and set the backpointer of process to its new list. */ + + /* StackInterpreter>>#addLastLink:toList: */ +static NoDbgRegParms void +addLastLinktoList(sqInt proc, sqInt aList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt lastLink; + + assert(!((isForwarded(proc)))); + assert(!((isForwarded(aList)))); + assert((fetchPointerofObject(NextLinkIndex, proc)) == (nilObject())); + + /* begin isEmptyList: */ + assert(!(isForwarded(aList))); + if ((longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(FirstLinkIndex, aList, proc)); + assert(isNonImmediate(aList)); + if (oopisGreaterThanOrEqualTo(aList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(proc & (tagMask())))) + && (oopisLessThan(proc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))),proc); + } + else { + lastLink = longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord())))))); + assert(lastLink != proc); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(NextLinkIndex, lastLink, proc)); + assert(isNonImmediate(lastLink)); + if (oopisGreaterThanOrEqualTo(lastLink, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(proc & (tagMask())))) + && (oopisLessThan(proc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(lastLink + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(lastLink); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((lastLink + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord()))))),proc); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(LastLinkIndex, aList, proc)); + assert(isNonImmediate(aList)); + if (oopisGreaterThanOrEqualTo(aList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(proc & (tagMask())))) + && (oopisLessThan(proc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord()))))),proc); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(MyListIndex, proc, aList)); + assert(isNonImmediate(proc)); + if (oopisGreaterThanOrEqualTo(proc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(aList & (tagMask())))) + && (oopisLessThan(aList, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(proc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(proc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((proc + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),aList); +} + + /* StackInterpreter>>#allOnesAsCharStar */ +static char * +allOnesAsCharStar(void) +{ + return ((char *) (((usqInt) -1))); +} + + +/* for Cogit */ + + /* StackInterpreter>>#argumentCountOfClosure: */ +sqInt +argumentCountOfClosure(sqInt closurePointer) +{ + sqInt oop; + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((closurePointer + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + return (oop >> 3); +} + + /* StackInterpreter>>#argumentCountOfMethodHeader: */ +usqInt +argumentCountOfMethodHeader(sqInt header) +{ + return (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; +} + + /* StackInterpreter>>#argumentCountOf: */ +usqInt +argumentCountOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin argumentCountOfMethodHeader: */ + return (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; +} + + +/* Return the address of first indexable field of resulting array object, or + fail if + the instance variable does not contain an indexable bytes or words object. */ +/* Note: May be called by translated primitive code. */ + + /* StackInterpreter>>#arrayValueOf: */ +void * +arrayValueOf(sqInt arrayOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (/* isWordsOrBytes: */ + ((!(arrayOop & (tagMask())))) + && (isWordsOrBytesNonImm(arrayOop))) { + return ((void *) (pointerForOop(arrayOop + BaseHeaderSize))); + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; +} + + +/* Returns an integer object */ + + /* StackInterpreter>>#asciiOfCharacter: */ +static NoDbgRegParms sqInt +asciiOfCharacter(sqInt characterObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (((characterObj & (characterTag())) != 0)) { + return characterObj - ((characterTag()) - (smallIntegerTag())); + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return ConstZero; +} + + /* StackInterpreter>>#assertValidExecutionPointe:r:s: */ +void +assertValidExecutionPointers(usqInt lip, char *lifp, char *lisp) +{ + assertValidExecutionPointersimbarline(lip, lifp, lisp, !((((usqInt)(longAt(lifp + FoxMethod)))) < (startOfMemory())), __LINE__); +} + + +/* Order in the stackLimit checks is important because stackLimit is smashed + by interrupts. So always check for unsmashed value first to avoid race + condition. + */ + + /* StackInterpreter>>#assertValidStackLimits: */ +static NoDbgRegParms void +assertValidStackLimits(sqInt ln) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assertl((GIV(stackLimit) == ((GIV(stackPage)->realStackLimit))) + || (GIV(stackLimit) == (allOnesAsCharStar())), ln); + assertl((((GIV(stackPage)->stackLimit)) == ((GIV(stackPage)->realStackLimit))) + || (((GIV(stackPage)->stackLimit)) == (allOnesAsCharStar())), ln); +} + + +/* convert true and false (Smalltalk) to true or false(C) */ + + /* StackInterpreter>>#booleanValueOf: */ +sqInt +booleanValueOf(sqInt obj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (obj == GIV(trueObj)) { + return 1; + } + if (obj == GIV(falseObj)) { + return 0; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; +} + + +/* Re-enter the interpreter to execute a (non-Alien) callback. */ + + /* StackInterpreter>>#callbackEnter: */ +sqInt +callbackEnter(sqInt *callbackID) +{ + warning("callbackEnter: is obsolete"); + return 0; +} + + +/* Leave from a previous callback */ + + /* StackInterpreter>>#callbackLeave: */ +sqInt +callbackLeave(sqInt cbID) +{ + warning("callbackLeave: is obsolete"); + return 0; +} + + +/* Context switch should not be allowed on every method activation. In + particular the + implementation of ensure: and ifCurtailed: depends on there being no + suspension point + on failing primitive 198 (primitiveMarkUnwindMethod, + primitiveMarkUnwindMethod). slowPrimitiveResponse used to state + ``N.B. This means there is no suspension point on primitive failure + which methods such as ensure: and ifCurtailed: rely on.'' + Rather than prevent context switch on all primitives but the ones we + really need + to be suspension points (primitiveSignal et al) we choose to allow context + switch for all but primitiveMarkUnwindMethod and + PrimNumberNoContextSwitch. */ + + /* StackInterpreter>>#canContextSwitchIfActivating:header: */ +sqInt +canContextSwitchIfActivatingheader(sqInt theMethod, sqInt methodHeader) +{ + usqInt firstBytecode; + sqInt primitiveIndex; + + /* primitiveIndexOfMethod:header: */ + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (theMethod + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + return (primitiveIndex != PrimNumberUnwindMarker) + && (primitiveIndex != PrimNumberNoContextSwitchMarker); +} + + +/* Above ObjectMemory, arg must lie in range 0-255! */ + + /* StackInterpreter>>#characterForAscii: */ +sqInt +characterForAscii(sqInt ascii) +{ + return ((((usqInt)(ascii) << (numTagBits())))) + (characterTag()); +} + + +/* Ensure that all accessible objects in the heap are okay. */ +/* useful for VM debugging */ + + /* StackInterpreter>>#checkAllAccessibleObjectsOkay */ +sqInt +checkAllAccessibleObjectsOkay(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt ok; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + ok = 1; + + /* begin allObjectsDoSafely: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + if (((longAt((void *)(obj))) & (classIndexMask())) > (lastClassIndexPun())) { + ok = ok && (checkOkayFields(obj)); + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + return ok; +} + + +/* Check for a hit of the longRunningPrimitive probe and if so attempt to + signal the + longRunningPrimitiveCheckSemaphore. Answer if a process switch occurred as + a result. */ + + /* StackInterpreter>>#checkDeliveryOfLongRunningPrimitiveSignal */ +#if LRPCheck +static NeverInline sqInt +checkDeliveryOfLongRunningPrimitiveSignal(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((GIV(longRunningPrimitiveStopUsecs) > GIV(longRunningPrimitiveStartUsecs)) + && ((GIV(longRunningPrimitiveCheckSemaphore)) + && (GIV(longRunningPrimitiveSignalUndelivered)))) { + GIV(longRunningPrimitiveSignalUndelivered) = 0; + GIV(longRunningPrimitiveGCUsecs) = ((GIV(gcStartUsecs) < GIV(longRunningPrimitiveStopUsecs)) + && (GIV(statGCEndUsecs) > GIV(longRunningPrimitiveStartUsecs)) + ? GIV(statGCEndUsecs) - GIV(gcStartUsecs) + : 0); + + /* Signal the LRP check semaphore if it is present */ + return synchronousSignal(GIV(longRunningPrimitiveCheckSemaphore)); + } + + /* a hit + but not yet delivered */ + return 0; +} +#endif /* LRPCheck */ + + +/* Note: May be called by translated primitive code. */ + + /* StackInterpreter>>#checkedIntegerValueOf: */ +sqInt +checkedIntegerValueOf(sqInt intOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((((intOop) & 7) == 1)) { + return (intOop >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } +} + + +/* Check for possible interrupts and handle one if necessary. + Answer if a context switch has occurred. */ + + /* StackInterpreter>>#checkForEventsMayContextSwitch: */ +static NoDbgRegParms sqInt +checkForEventsMayContextSwitch(sqInt mayContextSwitch) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt heapSizePostGC; + usqLong now; + sqInt nowSqInt; + sqInt objOop; + sqInt sema; + sqInt sufficientSpaceAfterGCRV; + sqInt switched; + + GIV(statCheckForEvents) += 1; + + /* restore the stackLimit if it has been smashed. */ + + /* begin restoreStackLimit */ + (GIV(stackPage)->stackLimit = (GIV(stackPage)->realStackLimit)); + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + + /* Allow the platform to do anything it needs to do synchronously. */ + ioSynchronousCheckForEvents(); + + /* begin checkCogCompiledCodeCompactionCalledFor */ + if (GIV(cogCompiledCodeCompactionCalledFor)) { + commenceCogCompiledCodeCompaction(); + } + if (GIV(needGCFlag)) { + /* sufficientSpaceAfterGC: runs the incremental GC and + then, if not enough space is available, the fullGC. */ + + /* begin sufficientSpaceAfterGC: */ + scavengingGCTenuringIf(TenureByAge); + heapSizePostGC = ((/* begin totalOldSpaceCapacity */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + if (((((double) (heapSizePostGC - GIV(heapSizeAtPreviousGC)) )) / GIV(heapSizeAtPreviousGC)) >= GIV(heapGrowthToSizeGCRatio)) { + fullGC(); + } + else { + if (GIV(totalFreeOldSpace) > (GIV(shrinkThreshold) * 2)) { + /* begin attemptToShrink */ + if ((GIV(totalFreeOldSpace) > GIV(shrinkThreshold)) + && ((GIV(totalFreeOldSpace) > GIV(growHeadroom)) + && (shrinkObjectMemory((((GIV(totalFreeOldSpace) - GIV(growHeadroom)) < GIV(growHeadroom)) ? GIV(growHeadroom) : (GIV(totalFreeOldSpace) - GIV(growHeadroom))))))) { + GIV(statShrinkMemory) += 1; + } + sufficientSpaceAfterGCRV = 1; + goto l1; + } + } + + /* Also attempt to shrink if there is plenty of free space and no need to GC */ + while ((GIV(totalFreeOldSpace) < GIV(growHeadroom)) + && ((growOldSpaceByAtLeast(0)))) { + if (GIV(totalFreeOldSpace) >= GIV(growHeadroom)) { + sufficientSpaceAfterGCRV = 1; + goto l1; + } + } + + /* Answer false if space is low */ + sufficientSpaceAfterGCRV = GIV(lowSpaceThreshold) <= GIV(totalFreeOldSpace); + /* end sufficientSpaceAfterGC: */ +l1: + if (!sufficientSpaceAfterGCRV) { + setSignalLowSpaceFlagAndSaveProcess(); + } + } + if (!mayContextSwitch) { + return 0; + } + + /* begin checkProfileTickIfSwitched */ + if ((GIV(nextProfileTick) <= 0) + || ((ioHighResClock()) < GIV(nextProfileTick))) { + switched = 0; + goto l2; + } + + /* Take a sample (if not already done so) for the profiler. */ + if (!GIV(profileProcess)) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + GIV(profileProcess) = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + } + GIV(profileMethod) = null; + + /* Zero nextProfileTick because signalling the semaphore is when the VM effectively delivers the sample. */ + + /* begin zeroNextProfileTick */ + GIV(nextProfileTick) = 0; + switched = synchronousSignal(GIV(profileSemaphore)); + /* end checkProfileTickIfSwitched */ +l2: +# if LRPCheck + if (checkDeliveryOfLongRunningPrimitiveSignal()) { + switched = 1; + } +# endif + + if (GIV(signalLowSpace)) { + /* signalLowSpace: */ + GIV(signalLowSpace) = 0; + sema = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheLowSpaceSemaphore) << (shiftForWord())))))); + if ((sema != GIV(nilObj)) + && (synchronousSignal(sema))) { + switched = 1; + } + } + nowSqInt = (now = ioUTCMicroseconds()); + + /* begin checkInvokeIOProcessEvents: */ + if (nowSqInt >= GIV(nextPollUsecs)) { + GIV(statIOProcessEvents) += 1; + ioProcessEvents(); + + /* msecs to wait before next call to ioProcessEvents. Note that strictly + speaking we might need to update 'now' at this point since + ioProcessEvents could take a very long time on some platforms */ + GIV(nextPollUsecs) = nowSqInt + 20000; + } + if (GIV(interruptPending)) { + GIV(interruptPending) = 0; + + /* reset interrupt flag */ + sema = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheInterruptSemaphore) << (shiftForWord())))))); + if ((sema != GIV(nilObj)) + && (synchronousSignal(sema))) { + switched = 1; + } + } + if (GIV(nextWakeupUsecs)) { + if (now >= GIV(nextWakeupUsecs)) { + GIV(nextWakeupUsecs) = 0; + + /* set timer interrupt to 0 for 'no timer' */ + sema = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord())))))); + if ((sema != GIV(nilObj)) + && (synchronousSignal(sema))) { + switched = 1; + } + } + } + + /* signal any pending finalizations */ + if (GIV(pendingFinalizationSignals) > 0) { + GIV(pendingFinalizationSignals) = 0; + sema = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheFinalizationSemaphore) << (shiftForWord())))))); + if ((sema != GIV(nilObj)) + && (synchronousSignal(sema))) { + switched = 1; + } + } + + /* signal all semaphores in semaphoresToSignal */ + if (signalExternalSemaphores()) { + switched = 1; + } + return switched; +} + + +/* Support for embedded images. Check that the first few bytes of a potential + header and answer if it + looks like something the VM can load, + The method checks the first three fields of the header (magic, header size + & data size) & the total size. + The magic number should be correct. + The header size should be correct. + The size of the data should be at least as long as the headerSize plus the + data size in the header */ +/* Need at least headerSize bytes; no point going further if not... */ + + /* StackInterpreter>>#checkImageHeaderFromBytes:AndSize: */ +sqInt +checkImageHeaderFromBytesAndSize(char *bytes, sqInt totalSize) +{ + sqInt dataSize; + sqInt headerSize; + int version; + + if (totalSize < (BytesPerWord * 16)) { + return 0; + } + version = long32At(bytes); + headerSize = long32At(bytes + 4); + dataSize = longAt(bytes + 8); + if (!((((version | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */)) { + if (!(((((SQ_SWAP_4_BYTES(version)) | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */)) { + return 0; + } + headerSize = SQ_SWAP_8_BYTES(headerSize); + dataSize = SQ_SWAP_8_BYTES(dataSize); + } + return (headerSize == (BytesPerWord * 16)) + && (totalSize >= (headerSize + dataSize)); +} + + +/* Read and verify the image file version number and answer it. + Assign through rawVersionPtr the unswapped version number. The caller can + then infer if the given image file needs to be byte-swapped by seeing if + the returned value + equals tha assigned through rawVersionPtr. + 0 is answered if no valid version could be found. */ + + /* StackInterpreter>>#checkImageVersionFrom:startingAt:assignRawVersion: */ +static NoDbgRegParms sqInt +checkImageVersionFromstartingAtassignRawVersion(sqImageFile f, squeakFileOffsetType imageOffset, sqInt *rawVersionPtr) +{ + sqInt version; + int w; + + rawVersionPtr[0] = 0; + + /* check the version number */ + sqImageFileSeek(f, imageOffset); + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + version = w; + rawVersionPtr[0] = version; + if ((((version | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */) { + return version; + } + + /* try with bytes reversed */ + sqImageFileSeek(f, imageOffset); + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + version = SQ_SWAP_4_BYTES(w); + if (!(/* isLikelyImageVersion: */ + (((rawVersionPtr[0]) / 100) == 65) + || (((rawVersionPtr[0]) / 1000) == 68))) { + rawVersionPtr[0] = version; + } + if ((((version | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */) { + return version; + } + + /* Note: The following is only meaningful if not reading an embedded image */ + if (!imageOffset) { + sqImageFileSeek(f, 0x200); + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + version = w; + if (!(/* isLikelyImageVersion: */ + (((rawVersionPtr[0]) / 100) == 65) + || (((rawVersionPtr[0]) / 1000) == 68))) { + rawVersionPtr[0] = version; + } + if ((((version | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */) { + return version; + } + + /* try skipping the first 512 bytes with bytes reversed */ + sqImageFileSeek(f, 0x200); + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + version = SQ_SWAP_4_BYTES(w); + if (!(/* isLikelyImageVersion: */ + (((rawVersionPtr[0]) / 100) == 65) + || (((rawVersionPtr[0]) / 1000) == 68))) { + rawVersionPtr[0] = version; + } + if ((((version | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */) { + return version; + } + } + + /* try skipping the first 512 bytes (prepended by certain Mac file transfer utilities) */ + if (!(/* isLikelyImageVersion: */ + (((rawVersionPtr[0]) / 100) == 65) + || (((rawVersionPtr[0]) / 1000) == 68))) { + rawVersionPtr[0] = version; + } + return 0; +} + + +/* Perform an integrity/leak check using the heapMap. Assume + clearLeakMapAndMapAccessibleObjects has set a bit at each + object's header. Check that all oops in the interpreter's state + points to a header. Answer 0 if all checks pass. */ + + /* StackInterpreter>>#checkInterpreterIntegrity */ +static sqInt +checkInterpreterIntegrity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt flags; + + flags = 0; + if (!(checkOopIntegritynamed(GIV(specialObjectsOop), "specialObjectsOop"))) { + flags = 1; + } + + /* No longer check messageSelector; it is ephemeral, not living beyond message lookup. + (objectMemory isNonImmediate: messageSelector) ifTrue: + [(objectMemory checkOopIntegrity: messageSelector named: 'messageSelector') ifFalse: + [flags := flags + N]]. */ + if (!(checkOopIntegritynamed(GIV(newMethod), "newMethod"))) { + flags += 2; + } + + /* No longer check lkupClass; it is ephemeral, not living beyond message lookup. + (objectMemory checkOopIntegrity: lkupClass named: 'lkupClass') ifFalse: + [flags := flags + N]. */ + if (GIV(profileProcess)) { + if (!(checkOopIntegritynamed(GIV(profileProcess), "profileProcess"))) { + flags += 4; + } + } + if (GIV(profileMethod)) { + if (!(checkOopIntegritynamed(GIV(profileMethod), "profileMethod"))) { + flags += 8; + } + } + if (GIV(profileSemaphore)) { + if (!(checkOopIntegritynamed(GIV(profileSemaphore), "profileSemaphore"))) { + flags += 16; + } + } + if (GIV(tempOop)) { + if (!(checkOopIntegritynamed(GIV(tempOop), "tempOop"))) { + flags += 32; + } + } + if (GIV(tempOop2)) { + if (!(checkOopIntegritynamed(GIV(tempOop2), "tempOop2"))) { + flags += 64; + } + } + if (!(checkLogIntegrity())) { + flags += 128; + } + return flags; +} + + +/* Another version of isWidowedContext: for debugging. + This will not bereave a widowed context. */ + + /* StackInterpreter>>#checkIsStillMarriedContext:currentFP: */ +static NoDbgRegParms sqInt +checkIsStillMarriedContextcurrentFP(sqInt aContext, char *currentFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *limitFP; + char *maybeFP; + sqInt maybeFrameCtxt; + sqInt referent; + sqInt senderOop; + StackPage *thePage; + + if (!((/* isContext: */ + ((!(aContext & (tagMask())))) + && (((longAt((void *)(aContext))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)))) { + return 0; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + maybeFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(maybeFP), GIV(pages)); + limitFP = ((thePage == GIV(stackPage)) + && (currentFP) + ? currentFP + : (thePage->headFP)); + if (!((maybeFP >= limitFP) + && (((!((((sqInt)(((char *)(longAt(maybeFP + FoxSavedFP)))))) & (tagMask())))) + && ((((/* begin withSmallIntegerTags: */ + assert(((oopForPointer(((char *)(longAt(maybeFP + FoxSavedFP))))) & (BytesPerWord - 1)) == 0), + (oopForPointer(((char *)(longAt(maybeFP + FoxSavedFP))))) + (smallIntegerTag()))) == (longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) + && (/* frameHasContext: */ + ((((usqInt)(longAt(maybeFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(maybeFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((maybeFP + FoxIFrameFlags) + 2)) != 0)))))) { + return 0; + } + maybeFrameCtxt = longAt(maybeFP + FoxThisContext); + + /* On Spur we need to follow the context to check for a match, but since the VM is + only speculating about maybeFrame being a frame, and only speculating about + maybeContext being a context, we need to be sure before we can safely follow. */ + if ((!(isFree(thePage))) + && ((isFrameonPage(maybeFP, thePage)) + && ((!((longAt((void *)(maybeFrameCtxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(maybeFrameCtxt)); + referent = longAt((void *)((maybeFrameCtxt + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + maybeFrameCtxt = referent; + } + return maybeFrameCtxt == aContext; +} + + +/* Check the log for leaks. The trace log is a circular buffer of pairs of + entries. If there is an entry at traceLogIndex - 3 \\ TraceBufferSize it + has entries. If + there is something at traceLogIndex it has wrapped. */ + + /* StackInterpreter>>#checkLogIntegrity */ +static sqInt +checkLogIntegrity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt limit; + sqInt ok; + sqInt oop; + sqInt remainder; + + limit = /* safe:mod: */ + (((remainder = (GIV(traceLogIndex) - 3) % TraceBufferSize)) < 0 + ? remainder + TraceBufferSize + : remainder); + if (!(GIV(traceLog)[limit])) { + return 1; + } + if (GIV(traceLog)[GIV(traceLogIndex)]) { + limit = TraceBufferSize - 3; + } + ok = 1; + for (i = 0; i <= limit; i += 3) { + oop = GIV(traceLog)[i]; + if (!(((oop & (tagMask())) != 0))) { + if (!(checkOopIntegritynamedindex(oop, "traceLog", i))) { + ok = 0; + } + } + oop = GIV(traceLog)[i + 1]; + if (!(((oop & (tagMask())) != 0))) { + if (!(checkOopIntegritynamedindex(oop, "traceLog", i + 1))) { + ok = 0; + } + } + } + return ok; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#checkOkayInterpreterObjects: */ +sqInt +checkOkayInterpreterObjects(sqInt writeBack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int i; + sqInt ok; + sqInt oop; + sqIntptr_t oopOrZero; + + ok = 1; + ok = ok && (checkOkayFields(GIV(nilObj))); + ok = ok && (checkOkayFields(GIV(falseObj))); + ok = ok && (checkOkayFields(GIV(trueObj))); + ok = ok && (checkOkayFields(GIV(specialObjectsOop))); + ok = ok && (checkOkayFields(GIV(messageSelector))); + ok = ok && (checkOkayFields(GIV(newMethod))); + ok = ok && (checkOkayFields(GIV(lkupClass))); + for (i = 0; i < MethodCacheEntries; i += MethodCacheEntrySize) { + oopOrZero = GIV(methodCache)[i + MethodCacheSelector]; + if (oopOrZero) { + ok = ok && (checkOkayFields(GIV(methodCache)[i + MethodCacheSelector])); + ok = ok && (checkOkayFields(GIV(methodCache)[i + MethodCacheMethod])); + } + } + for (i = 1; i <= GIV(remapBufferCount); i += 1) { + oop = GIV(remapBuffer)[i]; + if (!(((oop & (tagMask())) != 0))) { + ok = ok && (checkOkayFields(oop)); + } + } + ok = ok && (checkOkayStackZone(writeBack)); + return ok; +} + + /* StackInterpreter>>#checkOkayStackPage: */ +static NoDbgRegParms sqInt +checkOkayStackPage(StackPage *thePage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + char *frameRcvrOffset; + sqInt ok; + sqInt oop; + char *theFP; + char *theSP; + + callerFP = ((char *) 0); + theSP = (thePage->headSP); + theFP = (thePage->headFP); + ok = 1; + + /* Skip the instruction pointer on top of stack of inactive pages. */ + if (!(thePage == GIV(stackPage))) { + theSP += BytesPerWord; + } + while (1) { + frameRcvrOffset = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + while (theSP <= frameRcvrOffset) { + oop = longAt(theSP); + if (!((((oop & (tagMask())) != 0)) + || (checkOkayFields(oop)))) { + ok = 0; + } + theSP += BytesPerWord; + } + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + if (!(checkOkayFields(longAt(theFP + FoxThisContext)))) { + ok = 0; + } + } + if (!(checkOkayFields(/* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod))))) { + ok = 0; + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + theFP = callerFP; + } + + /* caller ip is frameCallerContext in a base frame */ + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if (!((((oop & (tagMask())) != 0)) + || (checkOkayFields(oop)))) { + ok = 0; + } + theSP += BytesPerWord; + } + return ok; +} + + +/* Check that all objects in the stack zone are okay */ + + /* StackInterpreter>>#checkOkayStackZone: */ +static NoDbgRegParms sqInt +checkOkayStackZone(sqInt writeBack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt ok; + StackPage *thePage; + + if (writeBack) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + ok = 1; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + ok = ok && (checkOkayStackPage(thePage)); + } + } + return ok; +} + + +/* Perform an integrity/leak check using the heapMap. Assume + clearLeakMapAndMapAccesibleObjects has set a bit at each + object's header. Scan all objects accessible from the stack + checking that every pointer points to a header. Answer if no + dangling pointers were detected. */ + + /* StackInterpreter>>#checkStackIntegrity */ +static sqInt +checkStackIntegrity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + CogMethod *cogMethod; + char *frameRcvrOffset; + sqInt i; + sqInt methodField; + sqInt ok; + usqInt oop; + char *theFP; + StackPage *thePage; + char *theSP; + + callerFP = ((char *) 0); + ok = 1; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + if (thePage == GIV(stackPage)) { + theSP = GIV(stackPointer); + theFP = GIV(framePointer); + } + else { + theSP = (thePage->headSP); + theFP = (thePage->headFP); + } + + /* Skip the instruction pointer on top of stack of inactive pages. */ + if (!(thePage == GIV(stackPage))) { + theSP += BytesPerWord; + } + while (1) { + frameRcvrOffset = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + while (theSP <= frameRcvrOffset) { + oop = longAt(theSP); + if (((!(oop & (tagMask())))) + && ((heapMapAtWord(pointerForOop(oop))) == 0)) { + printFrameThingandFrameat("object leak in frame temp", theFP, theSP); + ok = 0; + } + theSP += BytesPerWord; + } + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + oop = longAt(theFP + FoxThisContext); + if ((((oop & (tagMask())) != 0)) + || ((heapMapAtWord(pointerForOop(oop))) == 0)) { + printFrameThingandFrameat("object leak in frame ctxt", theFP, theFP + FoxThisContext); + ok = 0; + } + if (!(/* isContext: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + printFrameThingandFrameat("frame ctxt should be context", theFP, theFP + FoxThisContext); + ok = 0; + } + if (!((/* isContext: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (((((longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)))) { + printFrameThingandFrameat("frame ctxt should be married", theFP, theFP + FoxThisContext); + ok = 0; + } + if (!((/* isContext: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && ((frameOfMarriedContext(oop)) == theFP))) { + printFrameThingandFrameat("frame ctxt should be married to this frame ", theFP, theFP + FoxThisContext); + ok = 0; + } + } + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* begin mframeHomeMethod: */ + methodField = longAt(theFP + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + cogMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + goto l1; + } + cogMethod = ((CogMethod *) (methodField & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + if (!(heapMapAtWord(cogMethod))) { + printFrameThingandFrameat("object leak in mframe mthd", theFP, theFP + FoxMethod); + ok = 0; + } + } + else { + oop = longAt(theFP + FoxMethod); + if ((((oop & (tagMask())) != 0)) + || ((heapMapAtWord(pointerForOop(oop))) == 0)) { + printFrameThingandFrameat("object leak in iframe mthd", theFP, theFP + FoxMethod); + ok = 0; + } + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + theFP = callerFP; + } + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if (((!(oop & (tagMask())))) + && ((heapMapAtWord(pointerForOop(oop))) == 0)) { + printFrameThingandFrameat("object leak in frame arg", theFP, theSP); + ok = 0; + } + theSP += BytesPerWord; + } + } + } + return ok; +} + + +/* Version of stackPointerForMaybeMarriedContext: with no side-effects (does + not widow). + Used for assertion checking. Safe only in external primitives + (framePointer valid). + Answer the stackPointer of a Context. */ + + /* StackInterpreter>>#checkStackPointerForMaybeMarriedContext: */ +static NoDbgRegParms sqInt +checkStackPointerForMaybeMarriedContext(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + sqInt methodPointer; + sqInt sp; + sqInt spSqInt; + + if (checkIsStillMarriedContextcurrentFP(aContext, GIV(framePointer))) { + sp = checkStackPointerIndexForFrame(frameOfMarriedContext(aContext)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(aContext))); + return sp; + } + if (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + methodPointer = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* begin argumentCountOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin argumentCountOfMethodHeader: */ + return (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; + } + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + return 0; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + return (spSqInt >> 3); +} + + +/* Version of stackPointerIndexForFrame: that does not depend on writing back + head frame pointers. + Used for assertion checking. Safe only in external primitives + (framePointer valid). + Answer the 0-based index rel to the given frame. + (This is what stackPointer used to be before conversion to pointer) */ +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#checkStackPointerIndexForFrame: */ +static NoDbgRegParms sqInt +checkStackPointerIndexForFrame(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *aFrame; + char *prevFrame; + char *startFrame; + StackPage *thePage; + char *theSP; + + if (theFP == GIV(framePointer)) { + return /* stackPointerIndexForFrame:WithSP: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (((usqInt)(((theFP + FoxMFReceiver) - GIV(stackPointer)))) >> (shiftForWord())) + (((mframeCogMethod(theFP))->cmNumArgs)) + : (((usqInt)(((theFP + FoxIFReceiver) - GIV(stackPointer)))) >> (shiftForWord())) + (byteAt((theFP + FoxIFrameFlags) + 1))); + } + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + startFrame = (thePage == GIV(stackPage) + ? GIV(framePointer) + : (thePage->headFP)); + + /* begin findSPOrNilOf:on:startingFrom: */ + prevFrame = ((char *) 0); + if (startFrame == theFP) { + if (((thePage->headSP)) >= startFrame) { + /* If the SP is invalid return the pointer to the receiver field. */ + theSP = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + goto l1; + } + + /* Skip the instruction pointer on top of stack of inactive pages. */ + theSP = (thePage == GIV(stackPage) + ? (thePage->headSP) + : ((thePage->headSP)) + BytesPerWord); + goto l1; + } + aFrame = startFrame; + while (1) { + prevFrame = aFrame; + aFrame = ((char *)(longAt(aFrame + FoxSavedFP))); + if (!(aFrame != 0)) break; + if (theFP == aFrame) { + /* begin frameCallerSP: */ + assert(!(isBaseFrame(prevFrame))); + theSP = (prevFrame + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(prevFrame + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(prevFrame))->cmNumArgs) + : byteAt((prevFrame + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + goto l1; + } + } + theSP = null; + /* end findSPOrNilOf:on:startingFrom: */ +l1: + if (!theSP) { + return -1; + } + return /* stackPointerIndexForFrame:WithSP: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (((usqInt)(((theFP + FoxMFReceiver) - theSP))) >> (shiftForWord())) + (((mframeCogMethod(theFP))->cmNumArgs)) + : (((usqInt)(((theFP + FoxIFReceiver) - theSP))) >> (shiftForWord())) + (byteAt((theFP + FoxIFrameFlags) + 1))); +} + + +/* Check if aClass's name is className */ + + /* StackInterpreter>>#classNameOf:Is: */ +static NoDbgRegParms sqInt +classNameOfIs(sqInt aClass, char *className) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt i; + sqInt length; + sqInt name; + usqInt numSlots; + char *srcName; + + if (((/* begin numSlotsOf: */ + assert((classIndexOf(aClass)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(aClass + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aClass - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) <= GIV(classNameIndex)) { + return 0; + } + name = longAt((void *)((aClass + BaseHeaderSize) + ((((usqInt)(GIV(classNameIndex)) << (shiftForWord())))))); + if (!(/* isBytes: */ + ((!(name & (tagMask())))) + && (((byteAt((void *)(name + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + return 0; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(name + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + length = ((((/* begin numSlotsOf: */ + assert((classIndexOf(name)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(name + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(name - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + srcName = ((char *) (arrayValueOf(name))); + for (i = 0; i < length; i += 1) { + if (!((srcName[i]) == (className[i]))) { + return 0; + } + } + + /* Check if className really ends at this point */ + return (className[length]) == 0; +} + + /* StackInterpreter>>#clearTraceLog */ +void +clearTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + GIV(traceLogIndex) = 0; + for (i = 0; i < TraceBufferSize; i += 1) { + GIV(traceLog)[i] = 0; + } +} + + +/* For asserts. Check that theIP maps back correctly to the context's pc. + The CallPrimitive bytecode presents a complication. */ + + /* StackInterpreter>>#context:hasValidInversePCMappingOf:in: */ +static NoDbgRegParms sqInt +contexthasValidInversePCMappingOfin(sqInt aContext, sqInt theIP, char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt encodedip; + sqInt header; + sqInt methodHeader; + sqInt methodObj; + sqInt pc; + + pc = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + encodedip = contextInstructionPointerframe(theIP, theFP); + return (pc == encodedip) + || (((methodObj = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord()))))))), + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)), + (header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))))), + (methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader)))), + (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) + && ((((encodedip >> 3)) - ((pc >> 3))) == 3 /* sizeOfCallPrimitiveBytecode: */))); +} + + +/* for Cogit */ + + /* StackInterpreter>>#copiedValueCountOfClosure: */ +sqInt +copiedValueCountOfClosure(sqInt closureObj) +{ + usqInt numSlots; + + return (((((longAt((void *)((closureObj + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1) + ? (/* begin copiedValueCountOfVanillaClosure: */ + assert(isVanillaBlockClosure(closureObj)), + ((/* begin numSlotsOf: */ + assert((classIndexOf(closureObj)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(closureObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(closureObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - ClosureFirstCopiedValueIndex) + : (/* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(closureObj)))), + ((/* begin numSlotsOf: */ + assert((classIndexOf(closureObj)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(closureObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(closureObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex)); +} + + +/* This entry point needs to be implemented for the interpreter proxy. + Since BitBlt is now a plugin we need to look up BitBltPlugin:=copyBits + and call it. This entire mechanism should eventually go away and be + replaced with a dynamic lookup from BitBltPlugin itself but for backward + compatibility this stub is provided */ + + /* StackInterpreter>>#copyBits */ +sqInt +copyBits(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void *fn; + + fn = ioLoadFunctionFrom("copyBits", "BitBltPlugin"); + if (!fn) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return ((sqInt (*)(void))fn)(); +} + + +/* This entry point needs to be implemented for the interpreter proxy. + Since BitBlt is now a plugin we need to look up + BitBltPlugin:=copyBitsFrom:to:at: and call it. This entire mechanism + should eventually go away and be + replaced with a dynamic lookup from BitBltPlugin itself but for backward + compatibility this stub is provided + */ + + /* StackInterpreter>>#copyBitsFrom:to:at: */ +sqInt +copyBitsFromtoat(sqInt x0, sqInt x1, sqInt y) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void *fn; + + fn = ioLoadFunctionFrom("copyBitsFromtoat", "BitBltPlugin"); + if (!fn) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return ((sqInt (*)(sqInt, sqInt, sqInt))fn)(x0, x1, y); +} + + /* StackInterpreter>>#couldBeProcess: */ +static NoDbgRegParms NeverInline sqInt +couldBeProcess(sqInt oop) +{ + return (addressCouldBeObj(oop)) + && ((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + && ((!(((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (((lengthOf(oop)) > MyListIndex) + && (isContext(longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))))))))); +} + + +/* Bundle up the selector, arguments and lookupClass into a Message object. + In the process it pops the arguments off the stack, and pushes the message + object. + This can then be presented as the argument of e.g. #doesNotUnderstand: */ + + /* StackInterpreter>>#createActualMessageTo: */ +static NoDbgRegParms void +createActualMessageTo(sqInt lookupClass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt argumentArray; + sqInt i; + usqInt message; + usqInt newObj; + usqInt numBytes; + + + /* This is a useful break-point */ + assert((isImmediate(GIV(messageSelector))) + || (addressCouldBeObj(GIV(messageSelector)))); + + /* begin mnuBreakpoint:receiver: */ + mnuBreakpointreceiver(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), null); + assert((GIV(argumentCount) >= 0) + && ((knownClassAtIndex(ClassArrayCompactIndex)) != GIV(nilObj))); + assert((arrayFormat()) == (instSpecOfClass(knownClassAtIndex(ClassArrayCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(GIV(argumentCount) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((GIV(argumentCount) < 1 + ? 8 /* allocationUnit */ + : GIV(argumentCount) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + argumentArray = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) GIV(argumentCount))) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + GIV(freeStart) += numBytes; + argumentArray = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert(((MessageLookupClassIndex + 1) >= 0) + && ((knownClassAtIndex(ClassMessageCompactIndex)) != GIV(nilObj))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(knownClassAtIndex(ClassMessageCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert((MessageLookupClassIndex + 1) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (((MessageLookupClassIndex + 1) < 1 + ? 8 /* allocationUnit */ + : (MessageLookupClassIndex + 1) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + message = 0; + goto l2; + } + } + long64Atput((void *)(newObj),((((((usqLong) (MessageLookupClassIndex + 1))) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + ClassMessageCompactIndex); + GIV(freeStart) += numBytes; + message = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l2: + + /* Since the array is new can use unchecked stores. */ + for (i = ((GIV(argumentCount) - 1) * BytesPerOop); i >= 0; i += (-BytesPerOop)) { + longAtput((void *)((argumentArray + BaseHeaderSize) + i),popStack()); + } + + /* Since message is new can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(message)) + && (!(isForwarded(message)))); + assert(validStorePointerUncheckedArgs(MessageSelectorIndex, message, GIV(messageSelector))); + longAtput((void *)((message + BaseHeaderSize) + ((((usqInt)(MessageSelectorIndex) << (shiftForWord()))))),GIV(messageSelector)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(message)) + && (!(isForwarded(message)))); + assert(validStorePointerUncheckedArgs(MessageArgumentsIndex, message, argumentArray)); + longAtput((void *)((message + BaseHeaderSize) + ((((usqInt)(MessageArgumentsIndex) << (shiftForWord()))))),argumentArray); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(message)) + && (!(isForwarded(message)))); + assert(validStorePointerUncheckedArgs(MessageLookupClassIndex, message, lookupClass)); + longAtput((void *)((message + BaseHeaderSize) + ((((usqInt)(MessageLookupClassIndex) << (shiftForWord()))))),lookupClass); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),message); + GIV(argumentCount) = 1; +} + + +/* Return the default number of stack pages allocate at startup. + This V3 default suits Qwaq Forums (specifically general rendering). + The Spur default reflects tuning for GC performance ast Cadence. + It is probably a bit high for normal use but QF is profligate with + processes. The actual value can be set via vmParameterAt: and/or a + preference in the ini file. */ + + /* StackInterpreter>>#defaultNumStackPages */ +static sqInt +defaultNumStackPages(void) +{ + return 50; +} + + /* StackInterpreter>>#disableSendPrinting */ +#if SEND_PRINTING +void +disableSendPrinting(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(printSends) = 0; +} +#endif /* SEND_PRINTING */ + + +/* Release the VM to other threads and answer the current thread's index. + Currently valid flags for the non-threaded VM are: + DisownVMForFFICallFlag - informs the VM that it is entering an FFI call + DisownVMFromCallbackFlag - informs the VM that it is reentering an FFI + call + This is the entry-point for plugins and primitives that wish to release + the VM while + performing some operation that may potentially block, and for callbacks + returning back to some blocking operation. While this exists for the + threaded FFI VM we use + it to reset newMethod and the argumentCount after a callback. + This routine returns a negative result on error; see CoInterpreter>>ownVM: + and the clients in platforms/Cross/plugins/IA32ABI/*.c */ + + /* StackInterpreter>>#disownVM: */ +sqInt +disownVM(sqInt flags) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt top; + + assert(GIV(primFailCode) == 0); + assert(flags != 0); + + /* In the single-threaded VM here, we treat flags directly as vmHandle. See MTVM. + We are either in disowning after a callback (see thunkEntry) or before an FFI callout. */ + assert((flags == DisownVMFromCallbackFlag) + || ((flags == (DisownVMFromCallbackFlag + 1)) + || (flags == DisownVMForFFICallFlag))); + + /* If DisownVMForFFICall this is from the FFI plugin and we're making a callout; remember the fact. */ + if (((flags & DisownVMForFFICallFlag) != 0)) { + assert((isOopCompiledMethod(GIV(newMethod))) + && ((argumentCountOf(GIV(newMethod))) == GIV(argumentCount))); + return (GIV(ffiCalloutVMHandle) = flags); + } + + /* Otherwise this is a callback return; restore argumentCount and newMethod as per the ownVM: on callback. */ + if (((flags & DisownVMFromCallbackFlag) != 0)) { + GIV(argumentCount) = ((popStack()) >> 3); + assert(((GIV(argumentCount) >= 0) && (GIV(argumentCount) <= (argumentCountOfMethodHeader(-1))))); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(newMethod) = top; + assert((isOopCompiledMethod(GIV(newMethod))) + && ((argumentCountOf(GIV(newMethod))) == GIV(argumentCount))); + return 0; + } + return flags; +} + + /* StackInterpreter>>#divorceAllFrames */ +static sqInt +divorceAllFrames(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + StackPage *aPage; + sqInt i; + + if (GIV(stackPage)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l1; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer) + BytesPerWord); + /* end ensureFrameIsMarried:SP: */ +l1: + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + aPage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(aPage))) { + divorceFramesIn(aPage); + } + } + + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + return activeContext; +} + + /* StackInterpreter>>#divorceFramesIn: */ +static NoDbgRegParms void +divorceFramesIn(StackPage *aStackPage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt calleeContext; + char *calleeFP; + sqInt callerContextOrNil; + sqInt theContext; + char *theFP; + sqInt theIP; + StackPage *thePage; + char *theSP; + sqInt valuePointer; + + calleeFP = ((char *) 0); + GIV(statStackPageDivorce) += 1; + theFP = (aStackPage->headFP); + theSP = (aStackPage->headSP); + theIP = longAt(theSP); + + /* theSP points at hottest item on frame's stack */ + theSP += BytesPerWord; + calleeContext = null; + while (1) { + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + theContext = longAt(theFP + FoxThisContext); + goto l1; + } + theContext = marryFrameSP(theFP, theSP); + /* end ensureFrameIsMarried:SP: */ +l1: + updateStateOfSpouseContextForFrameWithSP(theFP, theSP); + valuePointer = contextInstructionPointerframe(theIP, theFP); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, theContext, valuePointer)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointer); + assert((frameReceiver(theFP)) == (followFieldofObject(ReceiverIndex, theContext))); + if (calleeContext) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SenderIndex, calleeContext, theContext)); + assert(isNonImmediate(calleeContext)); + if (oopisGreaterThanOrEqualTo(calleeContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(theContext & (tagMask())))) + && (oopisLessThan(theContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(calleeContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(calleeContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((calleeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),theContext); + } + calleeContext = theContext; + calleeFP = theFP; + theIP = longAt(theFP + FoxCallerSavedIP); + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!(theFP != 0)) break; + /* theSP points at stacked hottest item on frame's stack */ + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(calleeFP))); + theSP = (calleeFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(calleeFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(calleeFP))->cmNumArgs) + : byteAt((calleeFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + + /* begin frameCallerContext: */ + assert(isBaseFrame(calleeFP)); + thePage = stackPageAtpages(pageIndexFor(calleeFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + valuePointer = callerContextOrNil; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SenderIndex, theContext, valuePointer)); + assert(isNonImmediate(theContext)); + if (oopisGreaterThanOrEqualTo(theContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(theContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(theContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),valuePointer); + + /* The page is now free; mark it so. */ + (aStackPage->baseFP = 0); +} + + +/* Rounds negative results towards negative infinity, rather than zero. */ + + /* StackInterpreter>>#doPrimitiveDiv:by: */ +static NoDbgRegParms sqInt +doPrimitiveDivby(sqInt rcvr, sqInt arg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArg; + sqInt integerRcvr; + sqInt posArg; + sqInt posRcvr; + sqInt result; + + integerArg = 0; + integerRcvr = 0; + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + integerRcvr = (rcvr >> 3); + integerArg = (arg >> 3); + + /* begin success: */ + if (!integerArg) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return 1; + } + if (integerRcvr > 0) { + if (integerArg > 0) { + result = integerRcvr / integerArg; + } + else { + /* round negative result toward negative infinity */ + posArg = 0 - integerArg; + result = 0 - ((integerRcvr + (posArg - 1)) / posArg); + } + } + else { + posRcvr = 0 - integerRcvr; + if (integerArg > 0) { + /* round negative result toward negative infinity */ + result = 0 - ((posRcvr + (integerArg - 1)) / integerArg); + } + else { + posArg = 0 - integerArg; + result = posRcvr / posArg; + } + } + + /* begin success: */ + if (!((((((usqInt)(result)) >> 60) + 1) & 15) <= 1)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + return result; +} + + /* StackInterpreter>>#doPrimitiveMod:by: */ +static NoDbgRegParms sqInt +doPrimitiveModby(sqInt rcvr, sqInt arg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArg; + sqInt integerRcvr; + sqInt integerResult; + + integerArg = 0; + integerRcvr = 0; + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + integerRcvr = (rcvr >> 3); + integerArg = (arg >> 3); + + /* begin success: */ + if (!integerArg) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return 1; + } + integerResult = integerRcvr % integerArg; + + /* ensure that the result has the same sign as the integerArg */ + if (integerArg < 0) { + if (integerResult > 0) { + integerResult += integerArg; + } + } + else { + if (integerResult < 0) { + integerResult += integerArg; + } + } + + /* begin success: */ + if (!((((((usqInt)(integerResult)) >> 60) + 1) & 15) <= 1)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + return integerResult; +} + + +/* Signal the external semaphore with the given index. Answer if a context + switch occurs as a result. Do not bounds check. This has been done in the + caller. */ + + /* StackInterpreter>>#doSignalSemaphoreWithIndex: */ +sqInt +doSignalSemaphoreWithIndex(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt sema; + sqInt xArray; + + xArray = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ExternalObjectsArray) << (shiftForWord())))))); + assert(isArray(xArray)); + + /* Note: semaphore indices are 1-based */ + sema = longAt((void *)((xArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord())))))); + assert(!(isOopForwarded(sema))); + return (/* isSemaphoreOop: */ + ((!(sema & (tagMask())))) + && (((longAt((void *)(sema))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord())))))))))) + && (synchronousSignal(sema)); +} + + +/* Presumably this exists to squash C compiler warnings about unused + variables... + */ + + /* StackInterpreter>>#dummyReferToProxy */ +struct VirtualMachine * * +dummyReferToProxy(void) +{ + return (&interpreterProxy); +} + + +/* The prim trace log is a circular buffer of entries. If there is + an entry at primTraceLogIndex \\ PrimTraceLogSize it has entries. + If there is something at primTraceLogIndex it has wrapped. */ + + /* StackInterpreter>>#dumpPrimTraceLog */ +void +dumpPrimTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt hasParameter; + sqInt i; + sqInt remainder; + + if (!(GIV(primTraceLog)[/* safe:mod: */ + (((remainder = (GIV(primTraceLogIndex) - 1) % PrimTraceLogSize)) < 0 + ? remainder + PrimTraceLogSize + : remainder)])) { + return; + } + hasParameter = 0; + if (GIV(primTraceLog)[GIV(primTraceLogIndex)]) { + for (i = GIV(primTraceLogIndex); i < PrimTraceLogSize; i += 1) { + hasParameter = printPrimLogEntryAthasParameter(i, hasParameter); + } + } + for (i = 0; i < GIV(primTraceLogIndex); i += 1) { + hasParameter = printPrimLogEntryAthasParameter(i, hasParameter); + } +} + + +/* Print the prim trace log on a specific output stream. */ +/* essential for writing crash.dmp; use export: not api, so it will be + accessible on win32 and won't be written to cointerp.h + */ + + /* StackInterpreter>>#dumpPrimTraceLogOn: */ +void +dumpPrimTraceLogOn(FILE *aStdioStream) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + FILE *savedTranscript; + + /* begin withRedirectedOutputTo:do: */ + savedTranscript = GIV(transcript); + GIV(transcript) = (aStdioStream + ? aStdioStream + : stdout); + dumpPrimTraceLog(); + GIV(transcript) = savedTranscript; +} + + +/* The trace log is a circular buffer of pairs of entries. If there is + an entry at traceLogIndex - 3 \\ TraceBufferSize it has entries. + If there is something at traceLogIndex it has wrapped. */ + + /* StackInterpreter>>#dumpTraceLog */ +void +dumpTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt remainder; + + if (!(GIV(traceLog)[/* safe:mod: */ + (((remainder = (GIV(traceLogIndex) - 3) % TraceBufferSize)) < 0 + ? remainder + TraceBufferSize + : remainder)])) { + return; + } + if (GIV(traceLog)[GIV(traceLogIndex)]) { + for (i = GIV(traceLogIndex); i <= (TraceBufferSize - 3); i += 3) { + printLogEntryAt(i); + } + } + for (i = 0; i <= (GIV(traceLogIndex) - 3); i += 3) { + printLogEntryAt(i); + } +} + + +/* For marking the end of a leak check print message */ + + /* StackInterpreter>>#eekcr */ +NeverInline void +eekcr(void) +{ + cr(); +} + + /* StackInterpreter>>#enableSendPrinting */ +#if SEND_PRINTING +void +enableSendPrinting(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(printSends) = 1; +} +#endif /* SEND_PRINTING */ + + +/* Answer the caller context for a frame. If the frame has a caller + frame that doesn't have a context, then marry the caller frame. */ + + /* StackInterpreter>>#ensureCallerContext: */ +static NoDbgRegParms sqInt +ensureCallerContext(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + char *callerFP; + StackPage *thePage; + + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!callerFP) { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + return callerContextOrNil; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP))); + return longAt(callerFP + FoxThisContext); + } + return marryFrameSP( + callerFP, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFP))), + (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); +} + + +/* Ensure the image data has been updated to suit the current VM. */ + + /* StackInterpreter>>#ensureImageFormatIsUpToDate: */ +static NoDbgRegParms void +ensureImageFormatIsUpToDate(sqInt swapBytes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt addr; + sqInt address; + sqInt classIndex; + sqInt fmt; + sqInt followingWord; + usqInt followingWordAddress; + sqInt methodHeader; + usqInt numSlots; + sqInt obj; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + sqInt stopAddr; + int swapFloatWords; + sqInt temp; + int tempInt; + sqInt wordAddr; + + if (swapBytes) { + /* begin reverseBytesInImage */ + /* begin reverseBytesInMemory */ + /* begin reverseBytesFrom:to: */ + addr = GIV(oldSpaceStart); + while (oopisLessThan(addr, GIV(endOfMemory))) { + longAtput((void *)(addr),SQ_SWAP_8_BYTES((longAt((void *)(addr))))); + addr += BytesPerWord; + } + + /* Second, return the bytes of bytes-type objects to their + orginal order, and perform any other format conversions. */ + + /* begin updateObjectsPostByteSwap */ + swapFloatWords = VMBIGENDIAN != ((!(imageHeaderFlags & 2))); + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + fmt = (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask()); + if (fmt >= (firstByteFormat())) { + wordAddr = obj + BaseHeaderSize; + if (fmt >= (firstCompiledMethodFormat())) { + methodHeader = longAt((void *)(obj + BaseHeaderSize)); + wordAddr += (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop; + } + + /* compiled method; start after methodHeader and literals */ + stopAddr = obj + (((((/* begin numSlotsOf: */ + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) + BaseHeaderSize); + + /* begin reverseBytesFrom:to: */ + addr = wordAddr; + while (oopisLessThan(addr, stopAddr)) { + longAtput((void *)(addr),SQ_SWAP_8_BYTES((longAt((void *)(addr))))); + addr += BytesPerWord; + } + } + + /* oop contains bytes */ + if (fmt == (firstLongFormat())) { + if (swapFloatWords + && (((longAt((void *)(obj))) & (classIndexMask())) == ClassFloatCompactIndex)) { + temp = longAt((void *)(obj + BaseHeaderSize)); + longAtput((void *)(obj + BaseHeaderSize),longAt((void *)((obj + BaseHeaderSize) + 4))); + longAtput((void *)((obj + BaseHeaderSize) + 4),temp); + } + else { + } + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + if (swapFloatWords) { + /* setImageFloatsBigEndian: */ + imageHeaderFlags = ((imageHeaderFlags | 2) - 2); + } + } + else { + /* begin convertFloatsToPlatformOrder */ + if (VMBIGENDIAN == ((!(imageHeaderFlags & 2)))) { + goto l3; + } + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassFloatCompactIndex) { + tempInt = long32At((void *)(objSqInt + BaseHeaderSize)); + long32Atput((void *)(objSqInt + BaseHeaderSize),long32At((void *)((objSqInt + BaseHeaderSize) + 4))); + long32Atput((void *)((objSqInt + BaseHeaderSize) + 4),tempInt); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2: + assert(oopisGreaterThan(objSqInt, prevObj)); + } + + /* setImageFloatsBigEndian: */ + imageHeaderFlags = ((imageHeaderFlags | 2) - 2); + /* end convertFloatsToPlatformOrder */ +l3:; + } +} + + /* StackInterpreter>>#establishFrameForContextToReturnTo: */ +static NoDbgRegParms char * +establishFrameForContextToReturnTo(sqInt contextToReturnTo) +{ + sqInt senderOop; + StackPage *thePage; + + if (!(/* isContext: */ + ((!(contextToReturnTo & (tagMask())))) + && (((longAt((void *)(contextToReturnTo))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + return 0; + } + if (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (isWidowedContext(contextToReturnTo)) { + /* error: home's sender is dead; cannot return */ + return 0; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + return ((char *)(senderOop - (smallIntegerTag()))); + } + if (!(((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1))) { + return 0; + } + thePage = makeBaseFrameFor(contextToReturnTo); + markStackPageMostRecentlyUsed(thePage); + return (thePage->baseFP); +} + + +/* Divorce a single frame and its context. If it is not the top frame of a + stack this means splitting its stack. */ +/* stackPage needs to have current head pointers to avoid confusion. */ + + /* StackInterpreter>>#externalDivorceFrame:andContext: */ +static NoDbgRegParms void +externalDivorceFrameandContext(char *theFP, sqInt ctxt) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + sqInt callerCtx; + char *callerFP; + char *callerFP1; + sqInt callerIP; + char *callerSP; + char *frameAbove; + StackPage *newPage; + int onCurrent; + sqInt theIP; + StackPage *thePage; + StackPage *thePage1; + char *theSP; + sqInt valuePointer; + + assert((!GIV(stackPage)) + || (GIV(stackPage) == (GIV(mostRecentlyUsedPage)))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (!((onCurrent = thePage == GIV(stackPage)))) { + markStackPageNextMostRecentlyUsed(thePage); + } + theSP = findSPOfon(theFP, thePage); + updateStateOfSpouseContextForFrameWithSP(theFP, theSP); + + /* begin ensureCallerContext: */ + callerFP1 = ((char *)(longAt(theFP + FoxSavedFP))); + if (!callerFP1) { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerContextOrNil = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + callerCtx = callerContextOrNil; + goto l2; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP1 + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP1 + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP1 + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP1))); + callerCtx = longAt(callerFP1 + FoxThisContext); + goto l2; + } + callerCtx = marryFrameSP( + callerFP1, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFP))), + (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + /* end ensureCallerContext: */ +l2: + if ((frameAbove = findFrameAboveinPage(theFP, thePage))) { + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l1; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l1: + theIP = longAt(frameAbove + FoxCallerSavedIP); + frameAbove = moveFramesInthroughtoPage(thePage, frameAbove, newPage); + if (onCurrent) { + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (newPage->headSP); + GIV(framePointer) = (newPage->headFP); + } + else { + markStackPageMostRecentlyUsed(newPage); + } + assert((frameCallerContext(frameAbove)) == ctxt); + } + else { + theIP = longAt((thePage->headSP)); + } + + /* If we're divorcing the top frame we can simply peel it off. + othewise move all frames above to a new stack and then peel the frame off. */ + valuePointer = contextInstructionPointerframe(theIP, theFP); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, ctxt, valuePointer)); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointer); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SenderIndex, ctxt, callerCtx)); + assert(isNonImmediate(ctxt)); + if (oopisGreaterThanOrEqualTo(ctxt, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(callerCtx & (tagMask())))) + && (oopisLessThan(callerCtx, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(ctxt + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(ctxt); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),callerCtx); + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (callerFP) { + callerIP = longAt(theFP + FoxCallerSavedIP); + callerSP = ((/* begin frameCallerSP: */ + assert(!(isBaseFrame(theFP))), +(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)) - BytesPerWord; + longAtput(callerSP,callerIP); + + /* begin setHeadFP:andSP:inPage: */ + assert(callerSP < callerFP); + assert((callerSP < ((thePage->baseAddress))) + && (callerSP > (((thePage->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((callerFP < ((thePage->baseAddress))) + && (callerFP > (((thePage->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (thePage->headFP = callerFP); + (thePage->headSP = callerSP); + } + else { + freeStackPage(thePage); + } +} + + +/* Ensure aFramePtr is a base frame. Then we can assign its sender. + Answer the possibly moved location of the frame. */ + + /* StackInterpreter>>#externalEnsureIsBaseFrame: */ +static NoDbgRegParms char * +externalEnsureIsBaseFrame(char *aFramePtr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int onCurrent; + char *theFP; + StackPage *thePage; + + if (!(longAt(aFramePtr + FoxSavedFP))) { + return aFramePtr; + } + theFP = aFramePtr; + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + onCurrent = thePage == GIV(stackPage); + + /* Storing the frame's sender with its caller's context + has the side effect of making theFP a base frame. */ + theFP = storeSenderOfFramewithValue(theFP, ensureCallerContext(theFP)); + if (onCurrent) { + assert(GIV(stackPage) != thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (GIV(stackPage)->headSP); + GIV(framePointer) = (GIV(stackPage)->headFP); + } + else { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + assert(pageListIsWellFormed()); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + return theFP; +} + + /* StackInterpreter>>#externalInstVar:ofContext:put: */ +static NoDbgRegParms sqInt +externalInstVarofContextput(sqInt index, sqInt maybeMarriedContext, sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int onCurrentPage; + sqInt senderOop; + char *theFP; + StackPage *thePage; + + assert(isContext(maybeMarriedContext)); + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* Assign the field of a married context. */ + assert(!((isObjImmutable(maybeMarriedContext)))); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(maybeMarriedContext))))) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, maybeMarriedContext, anOop)); + assert(isNonImmediate(maybeMarriedContext)); + if (oopisGreaterThanOrEqualTo(maybeMarriedContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anOop & (tagMask())))) + && (oopisLessThan(anOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(maybeMarriedContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(maybeMarriedContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),anOop); + if (index == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(maybeMarriedContext); + } + return null; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + onCurrentPage = thePage == GIV(stackPage); + if (index) { + externalDivorceFrameandContext(theFP, maybeMarriedContext); + if (ismethodAssignmentToContextWithMachineCodePC(index, maybeMarriedContext)) { + ensureContextHasBytecodePC(maybeMarriedContext); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, maybeMarriedContext, anOop)); + assert(isNonImmediate(maybeMarriedContext)); + if (oopisGreaterThanOrEqualTo(maybeMarriedContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anOop & (tagMask())))) + && (oopisLessThan(anOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(maybeMarriedContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(maybeMarriedContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),anOop); + if (index == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(maybeMarriedContext); + } + } + else { + storeSenderOfFramewithValue(theFP, anOop); + } + if (onCurrentPage) { + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (GIV(stackPage)->headSP); + GIV(framePointer) = (GIV(stackPage)->headFP); + } + else { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(pageListIsWellFormed()); + assert(validStackPageBaseFrames()); + return 0; +} + + +/* Invoke a quick primitive. + Called under the assumption that primFunctionPtr has been preloaded */ + + /* StackInterpreter>>#externalQuickPrimitiveResponse */ +static sqInt +externalQuickPrimitiveResponse(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt localPrimIndex; + sqInt oop; + + assert(isPrimitiveFunctionPointerAnIndex()); + localPrimIndex = ((sqInt) primitiveFunctionPointer); + assert((localPrimIndex > 0xFF) + && (localPrimIndex < 520)); + + /* Quick return inst vars */ + if (localPrimIndex >= 264) { + oop = longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)((localPrimIndex - 264)) << (shiftForWord())))))); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); + return 1; + } + + /* Quick return constants */ + if (localPrimIndex == 0x100) { + return 1; + } + if (localPrimIndex == 0x101) { + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),GIV(trueObj)); + return 1; + } + if (localPrimIndex == 258) { + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),GIV(falseObj)); + return 1; + } + if (localPrimIndex == 259) { + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),GIV(nilObj)); + return 1; + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),(((usqInt)(localPrimIndex - 261) << 3) | 1)); + return 1; +} + + +/* not inlined for breakpoint value... */ + + /* StackInterpreter>>#failUnbalancedPrimitive */ +static void +failUnbalancedPrimitive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; +} + + +/* Fetch the instance variable at the given index of the given object. Return + the address of first indexable field of resulting array object, or fail if + the instance variable does not contain an indexable bytes or words object. + */ +/* Note: May be called by translated primitive code. */ + + /* StackInterpreter>>#fetchArray:ofObject: */ +void * +fetchArrayofObject(sqInt fieldIndex, sqInt objectPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayOop; + + arrayOop = longAt((void *)((objectPointer + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + + /* begin arrayValueOf: */ + if (/* isWordsOrBytes: */ + ((!(arrayOop & (tagMask())))) + && (isWordsOrBytesNonImm(arrayOop))) { + return ((void *) (pointerForOop(arrayOop + BaseHeaderSize))); + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; +} + + +/* Fetch the instance variable at the given index of the given object. Return + the C double precision floating point value of that instance variable, or + fail if it is not a Float. + */ +/* Note: May be called by translated primitive code. */ + + /* StackInterpreter>>#fetchFloat:ofObject: */ +double +fetchFloatofObject(sqInt fieldIndex, sqInt objectPointer) +{ + sqInt floatOop; + + floatOop = longAt((void *)((objectPointer + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + return floatValueOf(floatOop); +} + + +/* Fetch a slot from a married context. Compute the value + of the relevant inst var from the spouse frame's state. + + This method assumes frame pointers have been written back. */ + + /* StackInterpreter>>#fetchPointer:ofMarriedContext: */ +static NoDbgRegParms sqInt +fetchPointerofMarriedContext(sqInt offset, sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + char *callerFP; + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + sqInt senderOop; + char *spouseFP; + StackPage *thePage; + + assert(isContext(aContext)); + assert((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer))); + assert(checkIsStillMarriedContextcurrentFP(aContext, GIV(framePointer))); + + /* method, closureOrNil & receiver need no special handling; only + sender, pc & stackp have to be computed for married contexts. */ + if (offset <= ReceiverIndex) { + if (!(offset <= StackPointerIndex)) { + return longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(offset) << (shiftForWord())))))); + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!offset) { + /* begin ensureCallerContext: */ + callerFP = ((char *)(longAt(spouseFP + FoxSavedFP))); + if (!callerFP) { + /* begin frameCallerContext: */ + assert(isBaseFrame(spouseFP)); + thePage = stackPageAtpages(pageIndexFor(spouseFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + return callerContextOrNil; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP))); + return longAt(callerFP + FoxThisContext); + } + return marryFrameSP( + callerFP, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(spouseFP))), + (spouseFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(spouseFP))->cmNumArgs) + : byteAt((spouseFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + } + if (offset == StackPointerIndex) { + return (((usqInt)(stackPointerIndexForFrame(spouseFP)) << 3) | 1); + } + if (offset == InstructionPointerIndex) { + return instructionPointerForFramecurrentFPcurrentIP(spouseFP, GIV(framePointer), GIV(instructionPointer)); + } + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + return ((((offset - ReceiverIndex) >= 1) && ((offset - ReceiverIndex) <= (stackPointerIndexForFrame(spouseFP)))) + ? /* temporary:in: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? ((offset - (ReceiverIndex + 1)) < ((frameNumArgs = ((mframeCogMethod(spouseFP))->cmNumArgs))) + ? longAt((spouseFP + FoxCallerSavedIP) + ((frameNumArgs - (offset - (ReceiverIndex + 1))) * BytesPerWord)) + : longAt(((spouseFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs - (offset - (ReceiverIndex + 1))) * BytesPerWord))) + : /* itemporary:in: */ + ((offset - (ReceiverIndex + 1)) < ((frameNumArgsUsqInt = byteAt((spouseFP + FoxIFrameFlags) + 1))) + ? longAt((spouseFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt - (offset - (ReceiverIndex + 1))) * BytesPerWord)) + : longAt(((spouseFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt - (offset - (ReceiverIndex + 1))) * BytesPerWord)))) + : GIV(nilObj)); +} + + +/* Return the stackPointer of a Context or BlockContext. + Does not deal with married contexts. Use only for debug + printing or object tracing functions. To obtain an accurate + stack pointer use stackPointerForMaybeMarriedContext: */ + + /* StackInterpreter>>#fetchStackPointerOf: */ +static NoDbgRegParms sqInt +fetchStackPointerOf(sqInt aContext) +{ + sqInt sp; + + sp = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + return 0; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(aContext))); + return (sp >> 3); +} + + +/* Answer if file times, including those from the FilePlugin and + FileAttributesPlugin, should be answered in UTC seconds from the epoch + rather than local seconds. */ + + /* StackInterpreter>>#fileTimesInUTC */ +int +fileTimesInUTC(void) +{ + return ((imageHeaderFlags & 0x200) != 0); +} + + /* StackInterpreter>>#findClassContainingMethod:startingAt: */ +static NoDbgRegParms sqInt +findClassContainingMethodstartingAt(sqInt meth, sqInt classObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classDict; + usqInt classDictSize; + sqInt currClass; + sqInt i; + sqInt methodArray; + usqInt numSlots; + sqInt objOop; + sqInt referent; + sqInt referentSqInt; + + if (/* isOopForwarded: */ + ((!(classObj & (tagMask())))) + && ((!((longAt((void *)(classObj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(classObj)); + currClass = longAt((void *)((classObj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(currClass & (tagMask())))) + && ((!((longAt((void *)(currClass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + currClass = longAt((void *)((currClass + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + else { + currClass = classObj; + } + do { + assert(!(isForwarded(currClass))); + if (!(addressCouldBeClassObj(currClass))) { + return GIV(nilObj); + } + + /* begin noFixupFollowField:ofObject: */ + classDict = longAt((void *)((currClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(classDict & (tagMask())))) + && ((!((longAt((void *)(classDict))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(classDict)); + referent = longAt((void *)((classDict + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + classDict = referent; + } + assert(!(isForwarded(classDict))); + + /* begin numSlotsOf: */ + assert((classIndexOf(classDict)) > (isForwardedObjectClassIndexPun())); + classDictSize = (((numSlots = byteAt((void *)(classDict + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classDict - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (classDictSize > MethodArrayIndex) { + /* begin noFixupFollowField:ofObject: */ + methodArray = longAt((void *)((classDict + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(methodArray & (tagMask())))) + && ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(methodArray)); + referentSqInt = longAt((void *)((methodArray + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + methodArray = referentSqInt; + } + assert(!(isForwarded(methodArray))); + i = 0; + while (i < (classDictSize - SelectorStart)) { + if (meth == (noFixupFollowFieldofObject(i, methodArray))) { + return currClass; + } + i += 1; + } + } + + /* begin noFixupSuperclassOf: */ + /* begin noFixupFollowField:ofObject: */ + objOop = longAt((void *)((currClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + referentSqInt = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + objOop = referentSqInt; + } + currClass = objOop; + } while(!(currClass == GIV(nilObj))); + return currClass; +} + + /* StackInterpreter>>#findClassOfMethod:forReceiver: */ +sqInt +findClassOfMethodforReceiver(sqInt meth, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt rclass; + sqInt tagBits; + + if ((/* addressCouldBeOop: */ + (((rcvr & (tagMask())) != 0)) + || (addressCouldBeObj(rcvr))) + && (!(/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))))) { + rclass = findClassContainingMethodstartingAt(meth, /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr))); + if (rclass != GIV(nilObj)) { + return rclass; + } + } + if (!((addressCouldBeObj(meth)) + && (((byteAt((void *)(meth + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return GIV(nilObj); + } + return findClassContainingMethodstartingAt(meth, safeMethodClassOf(meth)); +} + + +/* Answer the frame above theFP (adjacent frame nearest head end). + If theFP is the head frame answer 0. */ + + /* StackInterpreter>>#findFrameAbove:inPage: */ +static NoDbgRegParms char * +findFrameAboveinPage(char *theFP, StackPage *thePage) +{ + char *callerFP; + char *fp; + + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == theFP) { + return 0; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == theFP) { + return fp; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + return 0; +} + + +/* Answer the highest priority of runnable process without altering any + lists. Do *not* update highestRunnableProcessPriority. + Answer 0 if no runnable process can be found. */ + + /* StackInterpreter>>#findHighestPriority */ +static sqInt +findHighestPriority(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + sqInt objOop; + sqInt p; + sqInt processList; + sqInt schedLists; + + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + schedLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + p = (GIV(highestRunnableProcessPriority) + ? GIV(highestRunnableProcessPriority) + : (/* begin numSlotsOf: */ + assert((classIndexOf(schedLists)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(schedLists + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(schedLists - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))); + while (((p -= 1)) >= 0) { + processList = longAt((void *)((schedLists + BaseHeaderSize) + ((((usqInt)(p) << (shiftForWord())))))); + + /* begin isEmptyList: */ + assert(!(isForwarded(processList))); + if (!((longAt((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj))) { + return p + 1; + } + } + return 0; +} + + /* StackInterpreter>>#findHomeForContext: */ +static NoDbgRegParms sqInt +findHomeForContext(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt closureOrNil; + + if (!(/* isContext: */ + ((!(aContext & (tagMask())))) + && (((longAt((void *)(aContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + return null; + } + closureOrNil = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord())))))); + assert((isPointers(closureOrNil)) + && ((closureOrNil == (nilObject())) + || ((numSlotsOf(closureOrNil)) >= ClosureFirstCopiedValueIndex))); + if ((((closureOrNil & (tagMask())) != 0)) + || (closureOrNil == GIV(nilObj))) { + return aContext; + } + + /* robustness in the presence of bugs */ + if ((((longAt((void *)(closureOrNil))) & (classIndexMask())) != ClassBlockClosureCompactIndex) + && (((longAt((void *)(closureOrNil))) & (classIndexMask())) != ClassFullBlockClosureCompactIndex)) { + return null; + } + return findHomeForContext(longAt((void *)((closureOrNil + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord()))))))); +} + + +/* See findUnwindThroughContext:. Alas this is mutually recursive with + findMethodWithPrimitive:FromFP:SP:ThroughContext: instead of iterative. + We're doing the simplest thing that could possibly work. Niceties can + wait. */ +/* Being mutually-recursive with findMethodWithPrimitive:FromFP:UpToContext: + gives the author's type inference algorithm headaches. Wimp out by + declaring the return type. + */ + + /* StackInterpreter>>#findMethodWithPrimitive:FromContext:UpToContext: */ +static NoDbgRegParms sqInt +findMethodWithPrimitiveFromContextUpToContext(sqInt primitive, sqInt senderContext, sqInt homeContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt senderContextSqInt; + sqInt senderOop; + char *startFP; + sqInt theContext; + char *theFP; + char *theFPAbove; + sqInt theMethod; + sqInt theMethodSqInt; + StackPage *thePage; + char *theSP; + + assert((senderContext == (nilObject())) + || (isContext(senderContext))); + assert((homeContext == (nilObject())) + || (isContext(homeContext))); + theContext = senderContext; + while (1) { + if (theContext == GIV(nilObj)) { + return theContext; + } + if (((((longAt((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) break; + if (theContext == homeContext) { + return 0; + } + if (!((primitive == 0) + || ((longAt((void *)((theContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord()))))))) != GIV(nilObj)))) { + /* begin followObjField:ofObject: */ + theMethod = longAt((void *)((theContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + assert(isNonImmediate(theMethod)); + if ((!((longAt((void *)(theMethod))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + theMethod = fixFollowedFieldofObjectwithInitialValue(MethodIndex, theContext, theMethod); + } + if ((primitiveIndexOf(theMethod)) == primitive) { + return theContext; + } + } + + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SenderIndex, theContext, objOop); + } + theContext = objOop; + } + if (isWidowedContext(theContext)) { + return GIV(nilObj); + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + startFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin findMethodWithPrimitive:FromFP:UpToContext: */ + theFP = startFP; + theFPAbove = startFP; + do { + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) + && (homeContext == (longAt(theFP + FoxThisContext)))) { + return 0; + } + if (!((primitive == 0) + || (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0)))) { + theMethodSqInt = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + if ((primitiveIndexOf(theMethodSqInt)) == primitive) { + if (theFP == theFPAbove) { + theSP = findSPOfon(theFP, stackPageFor(theFP)); + } + else { + /* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFPAbove))); + theSP = (theFPAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFPAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFPAbove))->cmNumArgs) + : byteAt((theFPAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + return longAt(theFP + FoxThisContext); + } + return marryFrameSP(theFP, theSP); + } + } + theFPAbove = theFP; + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + } while(theFP != 0); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFPAbove)); + thePage = stackPageAtpages(pageIndexFor(theFPAbove), GIV(pages)); + senderContextSqInt = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(senderContextSqInt)); + assert((senderContextSqInt == (nilObject())) + || (isContext(followMaybeForwarded(senderContextSqInt)))); + if (!(/* isContext: */ + ((!(senderContextSqInt & (tagMask())))) + && (((longAt((void *)(senderContextSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + return GIV(nilObj); + } + return findMethodWithPrimitiveFromContextUpToContext(primitive, senderContextSqInt, homeContext); +} + + /* StackInterpreter>>#findSelectorOfMethod: */ +sqInt +findSelectorOfMethod(sqInt aMethodOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classDict; + usqInt classDictSize; + sqInt classObj; + sqInt homeMethod; + sqInt i; + sqInt methodArray; + usqInt numSlots; + sqInt objOop; + sqInt referent; + sqInt selector; + + if (!(addressCouldBeObj(aMethodOop))) { + return GIV(nilObj); + } + if ((!((longAt((void *)(aMethodOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + return findSelectorOfMethod(followForwarded(aMethodOop)); + } + if (!(/* isOopCompiledMethod: */ + ((!(aMethodOop & (tagMask())))) + && (((byteAt((void *)(aMethodOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return GIV(nilObj); + } + homeMethod = homeMethodOf(aMethodOop); + if ((selector = maybeSelectorOfMethod(homeMethod))) { + return selector; + } + classObj = safeMethodClassOf(homeMethod); + if (addressCouldBeClassObj(classObj)) { + /* begin noFixupFollowField:ofObject: */ + classDict = longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(classDict & (tagMask())))) + && ((!((longAt((void *)(classDict))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(classDict)); + referent = longAt((void *)((classDict + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + classDict = referent; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(classDict)) > (isForwardedObjectClassIndexPun())); + classDictSize = (((numSlots = byteAt((void *)(classDict + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classDict - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (classDictSize > MethodArrayIndex) { + /* begin noFixupFollowField:ofObject: */ + methodArray = longAt((void *)((classDict + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(methodArray & (tagMask())))) + && ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(methodArray)); + referent = longAt((void *)((methodArray + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + methodArray = referent; + } + i = 0; + while (i < (classDictSize - SelectorStart)) { + if (homeMethod == (noFixupFollowFieldofObject(i, methodArray))) { + /* begin noFixupFollowField:ofObject: */ + objOop = longAt((void *)((classDict + BaseHeaderSize) + ((((usqInt)((i + SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + referent = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + objOop = referent; + } + return objOop; + } + i += 1; + } + } + } + return GIV(nilObj); +} + + +/* Search for the stack pointer for theFP. This points to the hottest item on + the frame's stack. + DO NOT CALL THIS WITH theFP == localFP OR theFP == framePointer! */ + + /* StackInterpreter>>#findSPOf:on: */ +static NoDbgRegParms char * +findSPOfon(char *theFP, StackPage *thePage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *aFrame; + char *prevFrame; + char *startFrame; + char *theSP; + + assert(!((isFree(thePage)))); + startFrame = (thePage->headFP); + + /* begin findSPOrNilOf:on:startingFrom: */ + prevFrame = ((char *) 0); + if (startFrame == theFP) { + if (((thePage->headSP)) >= startFrame) { + /* If the SP is invalid return the pointer to the receiver field. */ + theSP = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + goto l1; + } + + /* Skip the instruction pointer on top of stack of inactive pages. */ + theSP = (thePage == GIV(stackPage) + ? (thePage->headSP) + : ((thePage->headSP)) + BytesPerWord); + goto l1; + } + aFrame = startFrame; + while (1) { + prevFrame = aFrame; + aFrame = ((char *)(longAt(aFrame + FoxSavedFP))); + if (!(aFrame != 0)) break; + if (theFP == aFrame) { + /* begin frameCallerSP: */ + assert(!(isBaseFrame(prevFrame))); + theSP = (prevFrame + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(prevFrame + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(prevFrame))->cmNumArgs) + : byteAt((prevFrame + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + goto l1; + } + } + theSP = null; + /* end findSPOrNilOf:on:startingFrom: */ +l1: + if (theSP) { + return theSP; + } + error("did not find theFP in stack page"); + return 0; +} + + +/* Search for either an unwind-protect (activation of method with primitive + 198, PrimNumberUnwindMarker) + or homeContext along the sender chain, which ever is found first. Return + values: 0 home context was found on sender chain with no intervening + unwind-protects nilObj home context could not be found => cannotReturn + context the context of an intervening unwind-protect implies home context + was found */ +/* Almost always (98%) the home is on the same page, in which case we know it + will be found. + */ + + /* StackInterpreter>>#findUnwindThroughContext: */ +static NoDbgRegParms sqInt +findUnwindThroughContext(sqInt homeContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxtOrNilOrZero; + sqInt onSamePage; + sqInt senderContext; + char *theFP; + char *theFPAbove; + sqInt theMethod; + sqInt theMethodSqInt; + StackPage *thePage; + char *theSP; + + onSamePage = (/* isStillMarriedContext: */ + (((((longAt((void *)((homeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(homeContext)))) + && ((pageIndexFor(GIV(framePointer))) == (pageIndexFor(frameOfMarriedContext(homeContext)))); + + /* Since nothing changes we don't need to internalize. */ + + /* begin findMethodWithPrimitive:FromFP:UpToContext: */ + theFP = GIV(framePointer); + theFPAbove = GIV(framePointer); + do { + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) + && (homeContext == (longAt(theFP + FoxThisContext)))) { + ctxtOrNilOrZero = 0; + goto l1; + } + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0))) { + theMethodSqInt = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + if ((primitiveIndexOf(theMethodSqInt)) == PrimNumberUnwindMarker) { + if (theFP == theFPAbove) { + theSP = findSPOfon(theFP, stackPageFor(theFP)); + } + else { + /* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFPAbove))); + theSP = (theFPAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFPAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFPAbove))->cmNumArgs) + : byteAt((theFPAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + ctxtOrNilOrZero = longAt(theFP + FoxThisContext); + goto l1; + } + ctxtOrNilOrZero = marryFrameSP(theFP, theSP); + goto l1; + } + } + theFPAbove = theFP; + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + } while(theFP != 0); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFPAbove)); + thePage = stackPageAtpages(pageIndexFor(theFPAbove), GIV(pages)); + senderContext = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(senderContext)); + assert((senderContext == (nilObject())) + || (isContext(followMaybeForwarded(senderContext)))); + if (!(/* isContext: */ + ((!(senderContext & (tagMask())))) + && (((longAt((void *)(senderContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + ctxtOrNilOrZero = GIV(nilObj); + goto l1; + } + ctxtOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(PrimNumberUnwindMarker, senderContext, homeContext); + /* end findMethodWithPrimitive:FromFP:UpToContext: */ +l1: + assert(!((onSamePage + && (ctxtOrNilOrZero == (nilObject()))))); + if (!ctxtOrNilOrZero) { + theMethod = longAt((void *)((homeContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + if ((primitiveIndexOf(theMethod)) == PrimNumberUnwindMarker) { + return homeContext; + } + return 0; + } + + /* If an unwind was found, can the home context be found also? No need to look if on the same page. + No need to look if cannot return (ctxtOrNilOrZero = objectMemory nilObject) */ + if (!(onSamePage + || (ctxtOrNilOrZero == GIV(nilObj)))) { + if ((findMethodWithPrimitiveFromContextUpToContext(0, ctxtOrNilOrZero, homeContext)) == GIV(nilObj)) { + return GIV(nilObj); + } + } + return ctxtOrNilOrZero; +} + + /* StackInterpreter>>#flush */ +void +flush(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fflush(GIV(transcript)); +} + + +/* methodObj is a CompiledMethod. If it contains an external primitive, + flush the function address and session ID of the CM. Answer the prim + index for the benefit of subclass overrides. */ + + /* StackInterpreter>>#flushExternalPrimitiveOf: */ +static NoDbgRegParms sqInt +flushExternalPrimitiveOf(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt headerSqInt; + sqInt lit; + usqInt numSlots; + sqInt primIdx; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + headerSqInt = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* primitiveIndexOfMethod:header: */ + if (((header & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((header >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIdx = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIdx = 0; + } + if ((primIdx == PrimNumberExternalCall) + && (((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) > 0)) { + lit = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if ((/* isArray: */ + ((!(lit & (tagMask())))) + && (((byteAt((void *)(lit + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(lit)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(lit + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(lit - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(lit)) + && (!(isForwarded(lit)))); + assert(validStorePointerUncheckedArgs(2, lit, ConstZero)); + longAtput((void *)((lit + BaseHeaderSize) + (2U << (shiftForWord()))),ConstZero); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(lit)) + && (!(isForwarded(lit)))); + assert(validStorePointerUncheckedArgs(3, lit, ConstZero)); + longAtput((void *)((lit + BaseHeaderSize) + (3U << (shiftForWord()))),ConstZero); + } + } + + /* If not, something's broken */ + return primIdx; +} + + +/* Flush the method caches of references to oldMethod. */ + + /* StackInterpreter>>#flushMethodCacheForMethod: */ +static NoDbgRegParms void +flushMethodCacheForMethod(sqInt oldMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt probe; + + probe = 0; + for (i = 1; i <= MethodCacheEntries; i += 1) { + if ((GIV(methodCache)[probe + MethodCacheMethod]) == oldMethod) { + GIV(methodCache)[probe + MethodCacheSelector] = 0; + } + probe += MethodCacheEntrySize; + } + flushExternalPrimitiveOf(oldMethod); +} + + +/* follow pointers in the current stack frame up to theSP. */ + + /* StackInterpreter>>#followForwardedFrameContents:stackPointer: */ +static NoDbgRegParms void +followForwardedFrameContentsstackPointer(char *theFP, char *theSP) +{ + sqInt oop; + char *ptr; + sqInt referent; + char *toDoLimit; + char *toDoLimit1; + + toDoLimit1 = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + for (ptr = theSP; ptr <= toDoLimit1; ptr += BytesPerWord) { + oop = longAt(ptr); + if (((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(ptr,followForwarded(oop)); + } + } + toDoLimit = theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord()))))); + for (ptr = ((theFP + FoxCallerSavedIP) + BytesPerWord); ptr <= toDoLimit; ptr += BytesPerWord) { + oop = longAt(ptr); + if (((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + longAtput(ptr,oop); + } + } + + /* If we're in a block then also follow fields in the stacked closure as it may be activated again. */ + if (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0)) { + assert(oop == (longAt(theFP + (frameStackedReceiverOffset(theFP))))); + followForwardedObjectFieldstoDepth(oop, 0); + } + assert(!(isForwarded(frameMethodObject(theFP)))); + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(!(isForwarded(frameContext(theFP)))); + } +} + + +/* Force an interrupt check ASAP. + Must set the stack page's limit before stackLimit to avoid + a race condition if this is called from an interrupt handler. */ + + /* StackInterpreter>>#forceInterruptCheck */ +sqInt +forceInterruptCheck(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*iccFunc)(); + StackPage *thePage; + + + /* Do _not_ set stackLimit until the stack system has been initialized. + stackLimit is the initialization flag for the stack system. */ + if (!GIV(stackLimit)) { + return null; + } + thePage = GIV(stackPage); + if ((thePage) + && (thePage != 0)) { + (thePage->stackLimit = ((char *) (((usqInt) -1)))); + } + GIV(stackLimit) = ((char *) (((usqInt) -1))); + sqLowLevelMFence(); + + /* There is a race condition if we test the function and then dereference + it a second time to call it. This is called from interrupt code but at the + same time other code could be clearing the interruptCheckChain via + setInterruptCheckChain:. */ + if (((iccFunc = interruptCheckChain))) { + iccFunc(); + } + GIV(statForceInterruptCheck) += 1; + return 0; +} + + +/* Answer the SP of the caller provided theFP is not a base frame. + This points to the hottest item on the caller frame's stack. */ + + /* StackInterpreter>>#frameCallerSP: */ +static NoDbgRegParms char * +frameCallerSP(char *theFP) +{ + assert(!(isBaseFrame(theFP))); + return (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; +} + + /* StackInterpreter>>#frameContext: */ +static NoDbgRegParms sqInt +frameContext(char *theFP) +{ + return longAt(theFP + FoxThisContext); +} + + /* StackInterpreter>>#frameOfMarriedContext: */ +static NoDbgRegParms char * +frameOfMarriedContext(sqInt aContext) +{ + sqInt senderOop; + + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + + /* begin withoutSmallIntegerTags: */ + assert((((senderOop) & 7) == 1)); + return ((char *)(senderOop - (smallIntegerTag()))); +} + + +/* Answer the offset in bytes from the the frame pointer to its stacked + receiver. The receiver of a message send or the closure of a block + activation is + always on the stack above any arguments and the frame itself. See the + diagram in StackInterpreter class>>initializeFrameIndices. */ + + /* StackInterpreter>>#frameStackedReceiverOffset: */ +static NoDbgRegParms sqInt +frameStackedReceiverOffset(char *theFP) +{ + return (FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))); +} + + +/* Answer the stacked receiver given the frame's argument count. + The receiver of a message send or the closure of a block activation is + always on the stack above any arguments and the frame itself. See the + diagram in StackInterpreter class>>initializeFrameIndices. */ + + /* StackInterpreter>>#frameStackedReceiver:numArgs: */ +static NoDbgRegParms sqInt +frameStackedReceivernumArgs(char *theFP, sqInt numArgs) +{ + return longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)(numArgs) << (shiftForWord())))))); +} + + +/* Free any untraced stack pages. */ + + /* StackInterpreter>>#freeUntracedStackPages */ +static void +freeUntracedStackPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + StackPage *thePage; + + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((!(isFree(thePage))) + && (((thePage->trace)) == StackPageUnreached)) { + assert(noMarkedContextsOnPage(thePage)); + freeStackPage(thePage); + } + assert(((thePage->trace = StackPageTraceInvalid)) != 0); + } +} + + +/* Repaint the entire smalltalk screen, ignoring the affected rectangle. Used + in some platform's code when the Smalltalk window is brought to the front + or uncovered. + */ + + /* StackInterpreter>>#fullDisplayUpdate */ +sqInt +fullDisplayUpdate(void) +{ + updateDisplayLeftTopRightBottom(0, 0, displayWidth, displayHeight); + ioForceDisplayUpdate(); + return 0; +} + + +/* Find an actual function pointer for this primitiveIndex. This is an + opportunity to specialise the prim for the relevant class (format for + example). Default for now is simply the entry in the base primitiveTable. */ + + /* StackInterpreter>>#functionPointerFor:inClass: */ +static NoDbgRegParms void +(*functionPointerForinClass(sqInt primIdx, sqInt theClass))(void) +{ + return ((((usqInt)primIdx)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primIdx]); +} + + +/* Answer an array of flags indicating various optional features of the Cog + VM. If the bit is set then... + Bit 0: supports two bytecode sets (MULTIPLEBYTECODESETS) + Bit 1: supports immutablity (IMMUTABILITY) + Bit 2: suffers from a UNIX setitimer signal-based heartbeat + Bit 3: the VM provides cross-platform bit-identical floating point + Bit 4: the VM can catch exceptions in FFI calls and answer them as + primitive failures + Bit 5: the VM has suspend primitives 568 & 578 which back up a process to + before the wait if it was waiting on a condition variable + Bit 6: the VM is a threading VM */ + + /* StackInterpreter>>#getCogVMFeatureFlags */ +static sqInt +getCogVMFeatureFlags(void) +{ + return (((usqInt)((((((1 + ( +#if IMMUTABILITY + (IMMUTABILITY + ? 2 + : 0) +#else + 0 +#endif + )) + ( +#if ITIMER_HEARTBEAT + 4 +#else + 0 +#endif + )) + ( +#if BIT_IDENTICAL_FLOATING_POINT + 8 +#else + 0 +#endif + )) + ((ioCanCatchFFIExceptions() + ? 16 + : 0))) + 32)) << 3) | 1); +} + + +/* Answer the errorCode object to supply to a failing primitive method that + accepts one. + If there is a primitive error table and the primFailCode is a valid index + there-in answer + the corresponding entry in the table, otherwise simply answer the code as + an integer. + */ + + /* StackInterpreter>>#getErrorObjectFromPrimFailCode */ +static sqInt +getErrorObjectFromPrimFailCode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt clone; + sqInt errObj; + sqInt i; + usqInt newObj; + usqInt numBytes; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt table; + sqInt valuePointer; + + if (GIV(primFailCode) > 0) { + table = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(PrimitiveErrorTableIndex) << (shiftForWord())))))); + if (GIV(primFailCode) <= ((/* begin numSlotsOf: */ + assert((classIndexOf(table)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(table + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(table - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))) { + /* begin followField:ofObject: */ + errObj = longAt((void *)((table + BaseHeaderSize) + ((((usqInt)((GIV(primFailCode) - 1)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(errObj & (tagMask())))) + && ((!((longAt((void *)(errObj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + errObj = fixFollowedFieldofObjectwithInitialValue(GIV(primFailCode) - 1, table, errObj); + } + + /* If there's a clonable object in the table at that index, + answer a clone of the error object with the second slot set to the value of secondaryErrorCode. */ + if (((byteAt((void *)(errObj + (formatFieldByteOffset())))) & (formatMask())) == (nonIndexablePointerFormat())) { + /* begin cloneErrorObj: */ + /* begin numSlotsOf: */ + assert((classIndexOf(errObj)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(errObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(errObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + classIndex = (longAt((void *)(errObj))) & (classIndexMask()); + + /* begin eeInstantiateAnySmallClassIndex:format:numSlots: */ + assert((numSlots >= 0) + && ((classIndex != 0) + && ((classAtIndex(classIndex)) != GIV(nilObj)))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(classAtIndex(classIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + clone = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + classIndex); + GIV(freeStart) += numBytes; + clone = newObj; + /* end eeInstantiateAnySmallClassIndex:format:numSlots: */ +l1: + for (i = 0; i < numSlots; i += 1) { + valuePointer = longAt((void *)((errObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(clone)) + && (!(isForwarded(clone)))); + assert(validStorePointerUncheckedArgs(i, clone, valuePointer)); + longAtput((void *)((clone + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + if ((numSlots > 2) + && (GIV(primFailCode) == PrimErrFFIException)) { + valuePointer = positive64BitIntegerFor(((usqLong) GIV(secondaryErrorCode))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(clone)) + && (!(isForwarded(clone)))); + assert(validStorePointerUncheckedArgs(1, clone, valuePointer)); + longAtput((void *)((clone + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(exceptionPC)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(clone)) + && (!(isForwarded(clone)))); + assert(validStorePointerUncheckedArgs(2, clone, valuePointer)); + longAtput((void *)((clone + BaseHeaderSize) + (2U << (shiftForWord()))),valuePointer); + } + else { + valuePointer = signed64BitIntegerFor(GIV(secondaryErrorCode)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(clone)) + && (!(isForwarded(clone)))); + assert(validStorePointerUncheckedArgs(1, clone, valuePointer)); + longAtput((void *)((clone + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + } + errObj = clone; + } + return errObj; + } + } + return (((usqInt)GIV(primFailCode) << 3) | 1); +} + + +/* If bit 0 of the imageHeaderFlags is set then the system should use the + full screen. + */ + + /* StackInterpreter>>#getFullScreenFlag */ +int +getFullScreenFlag(void) +{ + return ((imageHeaderFlags & 1) != 0); +} + + /* StackInterpreter>>#getInterruptKeycode */ +sqInt +getInterruptKeycode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(interruptKeycode); +} + + /* StackInterpreter>>#getInterruptPending */ +sqInt +getInterruptPending(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(interruptPending); +} + + /* StackInterpreter>>#getNextWakeupUsecs */ +usqLong +getNextWakeupUsecs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(nextWakeupUsecs); +} + + /* StackInterpreter>>#getSavedWindowSize */ +sqInt +getSavedWindowSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(savedWindowSize); +} + + +/* Answer the next 16 bit word read from aFile, byte-swapped according to the + swapFlag. + */ + + /* StackInterpreter>>#getShortFromFile:swap: */ +static NoDbgRegParms usqInt +getShortFromFileswap(sqImageFile aFile, sqInt swapFlag) +{ + unsigned short w; + + w = 0; + sqImageFileRead((&w), sizeof(unsigned short), 1, aFile); + return (swapFlag + ? ((((usqInt)(w)) >> 8) & 0xFF) | (((w & 0xFF) << 8)) + : w); +} + + +/* Answer the screen size to write to the snapshot. + If the actual screen size is zero then write the savedWindowSize instead. */ + + /* StackInterpreter>>#getSnapshotScreenSize */ +static sqInt +getSnapshotScreenSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt screenSize; + + screenSize = ioScreenSize(); + if (((screenSize & 0xFFFF) == 0) + || ((((usqInt)(screenSize)) >> 16) == 0)) { + screenSize = GIV(savedWindowSize); + } + return screenSize; +} + + +/* For Alien FFI */ + + /* StackInterpreter>>#getStackPointer */ +sqInt * +getStackPointer(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((sqInt *) GIV(stackPointer)); +} + + +/* return the global session ID value */ + + /* StackInterpreter>>#getThisSessionID */ +sqInt +getThisSessionID(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(globalSessionID); +} + + /* StackInterpreter>>#getTranscript */ +FILE * +getTranscript(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(transcript); +} + + +/* Handle a send fault that is due to a send using a forwarded selector. + Unforward the selector and follow the current method and special + selectors array to unforward the source of the forwarded selector. */ + + /* StackInterpreter>>#handleForwardedSelectorFaultFor: */ +static NoDbgRegParms sqInt +handleForwardedSelectorFaultFor(sqInt selectorOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt referent; + + assert(isOopForwarded(selectorOop)); + followForwardedFieldsInCurrentMethod(); + followForwardedObjectFieldstoDepth(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))), 0); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(selectorOop)); + referent = longAt((void *)((selectorOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + return referent; +} + + +/* Handle a send fault that may be due to a send to a forwarded object. + Unforward the receiver on the stack and answer its actual class. */ + + /* StackInterpreter>>#handleForwardedSendFaultForTag: */ +static NoDbgRegParms sqInt +handleForwardedSendFaultForTag(sqInt classTag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt rcvr; + sqInt referent; + sqInt tagBits; + + assert(isForwardedClassTag(classTag)); + rcvr = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* should *not* be a super send, so the receiver should be forwarded. */ + assert(isOopForwarded(rcvr)); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(rcvr)); + referent = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + rcvr = referent; + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord),rcvr); + followForwardedFrameContentsstackPointer(GIV(framePointer), GIV(stackPointer) + ((GIV(argumentCount) + 1) * BytesPerWord)); + if (isPointers(/* frameReceiver: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? longAt(GIV(framePointer) + FoxMFReceiver) + : longAt(GIV(framePointer) + FoxIFReceiver)))) { + followForwardedObjectFieldstoDepth(/* frameReceiver: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? longAt(GIV(framePointer) + FoxMFReceiver) + : longAt(GIV(framePointer) + FoxIFReceiver)), 0); + } + return /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); +} + + +/* Handle a special send fault that may be due to a special selector + send accessing a forwarded object. obj is forwarded. + Unforward stack contents and and inst vars and answer obj's target. */ + + /* StackInterpreter>>#handleSpecialSelectorSendFaultFor:fp:sp: */ +static NoDbgRegParms sqInt +handleSpecialSelectorSendFaultForfpsp(sqInt obj, char *theFP, char *theSP) +{ + sqInt rcvr; + sqInt referent; + + assert(isOopForwarded(obj)); + followForwardedFrameContentsstackPointer(theFP, theSP); + rcvr = /* frameReceiver: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? longAt(theFP + FoxMFReceiver) + : longAt(theFP + FoxIFReceiver)); + if (/* isPointers: */ + ((!(rcvr & (tagMask())))) + && (((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) { + followForwardedObjectFieldstoDepth(rcvr, 0); + } + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + return referent; +} + + +/* Check for stack overflow, moving frames to another stack if so. + This should *only* be sent from checkForStackOverflow. */ + + /* StackInterpreter>>#handleStackOverflow */ +static NeverInline void +handleStackOverflow(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + char *callerFP; + char *callerFP1; + StackPage *newPage; + sqInt overflowCount; + char *overflowLimitAddress; + sqInt sourceCode; + char *theFP; + StackPage *thePage; + + callerFP = ((char *) 0); + assert(GIV(stackPointer) < ((GIV(stackPage)->realStackLimit))); + + /* begin traceStackOverflow */ + if (recordOverflowTrace()) { + sourceCode = ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? CSFromMachineCode + : CSFromInterpreter); + + /* begin recordTrace:source:thing:extra: */ + if (TraceLog) { + GIV(traceLog)[GIV(traceLogIndex)] = ((((((usqInt)(TraceStackOverflow) << TraceTypeShift)))) + sourceCode); + GIV(traceLog)[GIV(traceLogIndex) + 1] = 0; + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + } + if ((recordPrimTrace()) + && (!primTracePluginName)) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TraceStackOverflow << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + GIV(statStackOverflow) += 1; + + /* The stack has overflowed this page. If the system is executing some recursive algorithm, + e.g. fibonacci, then the system could thrash overflowing the stack if the call soon returns + back to the current page. To avoid thrashing, since overflow is quite slow, we can move + more than one frame. The idea is to record which page has overflowed, and the first + time it overflows move one frame, the second time two frames, and so on. We move no + more frames than would leave the page half occupied. */ + theFP = GIV(framePointer); + if (GIV(stackPage) == GIV(overflowedPage)) { + overflowLimitAddress = ((GIV(stackPage)->baseAddress)) - GIV(overflowLimit); + overflowCount = (GIV(extraFramesToMoveOnOverflow) += 1); + while ((((overflowCount -= 1)) >= 0) + && ((((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) < overflowLimitAddress) + && (!((longAt(callerFP + FoxSavedFP)) == 0)))) { + theFP = callerFP; + } + } + else { + GIV(overflowedPage) = GIV(stackPage); + GIV(extraFramesToMoveOnOverflow) = 0; + } + + /* begin ensureCallerContext: */ + callerFP1 = ((char *)(longAt(theFP + FoxSavedFP))); + if (!callerFP1) { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + goto l2; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP1 + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP1 + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP1 + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP1))); + + /* frameContext: */ + goto l2; + } + marryFrameSP( + callerFP1, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFP))), + (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + /* end ensureCallerContext: */ +l2: + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l1; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l1: + moveFramesInthroughtoPage(GIV(stackPage), theFP, newPage); + + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (newPage->headSP); + GIV(framePointer) = (newPage->headFP); + assert(validInstructionPointerinFrame(GIV(instructionPointer) + 1, GIV(framePointer))); + assert((!(frameHasContext(GIV(framePointer)))) + || (isContext(frameContext(GIV(framePointer))))); +} + + +/* The stackPointer is below the stackLimit. This is either because of a + stack overflow or the setting of stackLimit to indicate a possible + interrupt. Check for stackOverflow and interrupts anddeal with each + appropriately. Answer if a context switch occurred. */ + + /* StackInterpreter>>#handleStackOverflowOrEventAllowContextSwitch: */ +static NoDbgRegParms sqInt +handleStackOverflowOrEventAllowContextSwitch(sqInt mayContextSwitch) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin checkForStackOverflow */ + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (GIV(stackPointer) < ((GIV(stackPage)->realStackLimit))) { + handleStackOverflow(); + } + + /* If the stackLimit differs from the realStackLimit then the stackLimit + has been set to indicate an event or interrupt that needs servicing. */ + if (GIV(stackLimit) == ((GIV(stackPage)->realStackLimit))) { + return 0; + } + return checkForEventsMayContextSwitch(mayContextSwitch); +} + + +/* This is a C implementation needed by ioSetMaxExtSemTableSize + and e.g. stackPageByteSize. */ + + /* StackInterpreter>>#highBit: */ +sqInt +highBit(usqInt anUnsignedValue) +{ + sqInt bitNo; + usqInt shifted; + + + /* so it shows up in senders... */ + shifted = anUnsignedValue; + bitNo = 0; +# if BytesPerWord > 4 + if (!(shifted < (0x100000000LL))) { + shifted = (shifted) >> 32; + bitNo += 32; + } +# endif + + if (!(shifted < (0x10000))) { + shifted = (shifted) >> 16; + bitNo += 16; + } + if (!(shifted < (0x100))) { + shifted = (shifted) >> 8; + bitNo += 8; + } + if (!(shifted < (16))) { + shifted = (shifted) >> 4; + bitNo += 4; + } + if (!(shifted < (4))) { + shifted = (shifted) >> 2; + bitNo += 2; + } + if (!(shifted < (2))) { + shifted = (shifted) >> 1; + bitNo += 1; + } + + /* shifted 0 or 1 now */ + return bitNo + shifted; +} + + +/* With the full block implementation a CompiledBlock's method is found along + the chain through the last literal. See CompiledBlock>>#method. Answer + the home mehtod, or nilObj if it cannot be found. */ + + /* StackInterpreter>>#homeMethodOf: */ +sqInt +homeMethodOf(sqInt aMethodOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt lastLiteral; + sqInt methodChain; + + assert(isOopCompiledMethod(aMethodOop)); + methodChain = aMethodOop; + while (1) { + lastLiteral = ultimateLiteralOf(methodChain); + if (!(/* isOopCompiledMethod: */ + ((!(lastLiteral & (tagMask())))) + && (((byteAt((void *)(lastLiteral + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return methodChain; + } + if (!(/* isOopCompiledMethod: */ + ((!(lastLiteral & (tagMask())))) + && (((byteAt((void *)(lastLiteral + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return GIV(nilObj); + } + methodChain = lastLiteral; + } + return 0; +} + + +/* If thePage is the stackPage and the stackPointer and/or the framePointer + are pointing within it, + answer if thePage's heapSP and headFP are equal to the stackPointer and + framePointer respectively. + */ + + /* StackInterpreter>>#ifCurrentStackPageHasValidHeadPointers: */ +static NoDbgRegParms sqInt +ifCurrentStackPageHasValidHeadPointers(StackPage *thePage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (thePage == GIV(stackPage)) { + if (/* addressIsInPage: */ + (((thePage->lastAddress)) < GIV(framePointer)) + && (GIV(framePointer) < ((thePage->baseAddress)))) { + if (((thePage->headFP)) != GIV(framePointer)) { + return 0; + } + } + if (/* addressIsInPage: */ + (((thePage->lastAddress)) < GIV(stackPointer)) + && (GIV(stackPointer) < ((thePage->baseAddress)))) { + if (((thePage->headSP)) != GIV(stackPointer)) { + return 0; + } + } + } + return 1; +} + + /* StackInterpreter>>#iframeMethod: */ +static NoDbgRegParms usqInt +iframeMethod(char *theFP) +{ + return longAt(theFP + FoxMethod); +} + + +/* Return the equivalent of + aClass includesBehavior: aSuperclass. + Note: written for efficiency and better inlining (only 1 temp) */ + + /* StackInterpreter>>#includesBehavior:ThatOf: */ +sqInt +includesBehaviorThatOf(sqInt aClass, sqInt aSuperclass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt theClass; + + if (aSuperclass == GIV(nilObj)) { + return 0; + } + theClass = aClass; + while (1) { + if (theClass == aSuperclass) { + return 1; + } + if (!(theClass != GIV(nilObj))) break; + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((theClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, theClass, objOop); + } + theClass = objOop; + } + return 0; +} + + +/* Initialize the stack pages and then invoke continuation. Use alloca'ed + memory so that + when we have a JIT its stack pointer will be on the native stack since + alloca allocates + memory on the stack. Certain thread systems use the native stack pointer + as the + frame ID so putting the stack anywhere else can confuse the thread system. + + This path is for the threaded VM where we may want to allocate the stack + zone on + the stack of a thread other than the main VM thread. */ + + /* StackInterpreter>>#initStackPagesAndContinueInto:with: */ +void +initStackPagesAndContinueIntowith(void (*continuation)(void *), void *argument) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt stackPageBytes; + usqIntptr_t stackZoneBytes; + void *theStackMemory; + + /* begin ensureInitializeStackZone */ + if (!GIV(pages)) { + stackPageBytes = stackPageByteSize(); + stackZoneBytes = (GIV(numStackPages) * ((sizeof(CogStackPage)) + (stackPageByteSize()))) + BytesPerWord; + theStackMemory = alloca(stackZoneBytes); + memset(theStackMemory, 0, stackZoneBytes); + initializeStacknumSlotspageSize(theStackMemory, stackZoneBytes / BytesPerWord, stackPageBytes / BytesPerWord); + } + continuation(argument); +} + + +/* the vm has to convert aFilenameString via any canonicalization and + char-mapping and put the result in aCharBuffer. + Note the resolveAliases flag - this is an awful artefact of OSX and Apples + demented alias handling. When opening a file, the flag must be true, when + closing or renaming it must be false. Sigh. + */ + + /* StackInterpreter>>#ioFilename:fromString:ofLength:resolveAliases: */ +void +ioFilenamefromStringofLengthresolveAliases(char *aCharBuffer, char *aFilenameString, sqInt filenameLength, sqInt aBoolean) +{ + sqGetFilenameFromString(aCharBuffer, aFilenameString, filenameLength, aBoolean); +} + + +/* A base frame (first frame in a stack page) is so marked by having a null + saved fp. + */ + + /* StackInterpreter>>#isBaseFrame: */ +static NoDbgRegParms int +isBaseFrame(char *theFP) +{ + return (longAt(theFP + FoxSavedFP)) == 0; +} + + /* StackInterpreter>>#isBooleanObject: */ +sqInt +isBooleanObject(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (oop == GIV(trueObj)) + || (oop == GIV(falseObj)); +} + + /* StackInterpreter>>#isEmptyList: */ +static NoDbgRegParms int +isEmptyList(sqInt aLinkedList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(!(isForwarded(aLinkedList))); + return (longAt((void *)((aLinkedList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj); +} + + /* StackInterpreter>>#isFloatObject: */ +int +isFloatObject(sqInt oop) +{ + sqInt tagBits; + + return /* isFloatInstance: */ + ((tagBits = oop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(oop))) & (classIndexMask())) == ClassFloatCompactIndex); +} + + /* StackInterpreter>>#isFrame:onPage: */ +static NoDbgRegParms sqInt +isFrameonPage(char *aFrame, StackPage *aPage) +{ + char *prevFP; + char *theFP; + + assert(!((isFree(aPage)))); + + /* Walk the static chain making sure progress is being made, + and in the right direction, looking for a match with aFrame. */ + theFP = (aPage->headFP); + prevFP = theFP - BytesPerWord; + while (1) { + if (theFP == aFrame) { + return 1; + } + if (!((theFP > prevFP) + && (theFP < ((aPage->baseFP))))) break; + prevFP = theFP; + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + } + return 0; +} + + +/* Answer true if the oop is kind of Integer (Small or Large). */ + + /* StackInterpreter>>#isKindOfInteger: */ +sqInt +isKindOfInteger(sqInt oop) +{ + return ((((oop) & 7) == 1)) + || (/* isLargeIntegerInstance: */ + ((!(oop & (tagMask())))) + && ((((usqInt)(((longAt((void *)(oop))) & (classIndexMask())) - ClassLargeNegativeIntegerCompactIndex))) <= 1)); +} + + /* StackInterpreter>>#isLargeIntegerObject: */ +sqInt +isLargeIntegerObject(sqInt oop) +{ + return /* isLargeIntegerInstance: */ + ((!(oop & (tagMask())))) + && ((((usqInt)(((longAt((void *)(oop))) & (classIndexMask())) - ClassLargeNegativeIntegerCompactIndex))) <= 1); +} + + /* StackInterpreter>>#isLargeNegativeIntegerObject: */ +sqInt +isLargeNegativeIntegerObject(sqInt oop) +{ + return /* isInstanceOfClassLargeNegativeInteger: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassLargeNegativeIntegerCompactIndex); +} + + /* StackInterpreter>>#isLargePositiveIntegerObject: */ +sqInt +isLargePositiveIntegerObject(sqInt oop) +{ + return /* isInstanceOfClassLargePositiveInteger: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassLargePositiveIntegerCompactIndex); +} + + /* StackInterpreter>>#isLinkedExternalPrimitive: */ +static NoDbgRegParms sqInt +isLinkedExternalPrimitive(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt headerSqInt; + sqInt literal; + usqInt numSlots; + sqInt primIdx; + sqInt targetFunctionIndex; + + literal = 0; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + headerSqInt = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* primitiveIndexOfMethod:header: */ + if (((header & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((header >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIdx = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIdx = 0; + } + return (primIdx == PrimNumberExternalCall) + && ((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) > 0) + && ((isArray((literal = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord()))))))) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(literal)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(literal + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(literal - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4) + && (((targetFunctionIndex = longAt((void *)((literal + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord()))))))), + ((((targetFunctionIndex) & 7) == 1)) + && (((targetFunctionIndex >> 3)) > 0)))))); +} + + +/* Answer if the argument, which can be any object, is a live context. */ + + /* StackInterpreter>>#isLiveContext: */ +static NoDbgRegParms sqInt +isLiveContext(sqInt oop) +{ + assert(!((isOopForwarded(oop)))); + if (!(/* isContext: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + return 0; + } + if ((!((longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))) & (tagMask())))) { + return ((((longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1); + } + return !(isWidowedContext(oop)); +} + + /* StackInterpreter>>#isMarriedOrWidowedContext: */ +static NoDbgRegParms int +isMarriedOrWidowedContext(sqInt aContext) +{ + return ((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1); +} + + +/* This virtual machine provides two primitives that executes arbitrary + primitives, one + for indexed primitivces and one for named primitives. These meta + primitives are used + in the debugger to execute primitives while simulating execution. Spur + needs to know + the accessor depth for a primitive so that failures due to forwarders can + be fixed up + and retried. This method identifies such meta primitives so that + metaAccessorDepth can be substituted when appropriate. */ + + /* StackInterpreter>>#isMetaPrimitiveIndex: */ +static NoDbgRegParms sqInt +isMetaPrimitiveIndex(sqInt primIndex) +{ + return (primIndex == PrimNumberDoPrimitive) + || (primIndex == PrimNumberDoExternalCall); +} + + +/* Answer if the method is an external primitive call (prim 117) with a null + external primitive. + This is just for an assert in the CoInterpreter. */ + + /* StackInterpreter>>#isNullExternalPrimitiveCall: */ +static NoDbgRegParms sqInt +isNullExternalPrimitiveCall(sqInt aMethodObj) +{ + sqInt lit; + usqInt numSlots; + + if (!(((primitiveIndexOf(aMethodObj)) == PrimNumberExternalCall) + && ((literalCountOf(aMethodObj)) > 0))) { + return 0; + } + lit = longAt((void *)((aMethodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + return (/* isArray: */ + ((!(lit & (tagMask())))) + && (((byteAt((void *)(lit + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(lit)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(lit + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(lit - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4) + && (((longAt((void *)((lit + BaseHeaderSize) + (3U << (shiftForWord()))))) == ConstZero) + || ((longAt((void *)((lit + BaseHeaderSize) + (3U << (shiftForWord()))))) == ConstMinusOne))); +} + + +/* We save slots in the method cache by using the primitiveFunctionPointer + to hold either a function pointer or the index of a quick primitive. Since + quick primitive indices are small they can't be confused with function + addresses. */ + + /* StackInterpreter>>#isPrimitiveFunctionPointerAnIndex */ +static int +isPrimitiveFunctionPointerAnIndex(void) +{ + return (((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex; +} + + /* StackInterpreter>>#isQuickPrimitiveIndex: */ +int +isQuickPrimitiveIndex(sqInt anInteger) +{ + return ((anInteger >= 0x100) && (anInteger <= 519)); +} + + +/* Reading the sender, instructionPointer and stackPointer inst vars of a + context must take + account of potentially married contexts and fetch the state from the + frame. method, + closureOrNil and receiver can safely be fetched from the context without + checking. + */ + + /* StackInterpreter>>#isReadMediatedContextInstVarIndex: */ +int +isReadMediatedContextInstVarIndex(sqInt index) +{ + return index <= StackPointerIndex; +} + + /* StackInterpreter>>#isSingleContext: */ +static NoDbgRegParms int +isSingleContext(sqInt aContext) +{ + return (!((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))) & (tagMask()))); +} + + +/* Answer if aContext is married or widowed and still married. + If a context is widowed then turn it into a single dead context. */ + + /* StackInterpreter>>#isStillMarriedContext: */ +static NoDbgRegParms sqInt +isStillMarriedContext(sqInt aContext) +{ + return (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext))); +} + + +/* Answer if aClosure is a vanilla BlockClosure as in the first Cog release, + BlockClosure laid out as + 'outerContext, startpc, numArgs. FullBlockClosure is laid out as + outerContext, method, numArgs, receiver. + So either answer true if we're not supporting FullBlockClosure, or test + the startpc/method field. */ + + /* StackInterpreter>>#isVanillaBlockClosure: */ +static NoDbgRegParms int +isVanillaBlockClosure(sqInt aClosure) +{ + assert((isPointers(aClosure)) + && ((numSlotsOf(aClosure)) >= ClosureFirstCopiedValueIndex)); + return ((((longAt((void *)((aClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1); +} + + +/* Answer if the argument is married to a live frame or not. + This method is safe for use only when no frameContext fields may be + forwarded (as maybe the case when scavenging). Post become: all + frameContext fields are followed, and hence normally no following of + frameContext fields is necessary. */ +/* i.e. inline into isWidowedContext: */ + + /* StackInterpreter>>#isWidowedContextNoConvert: */ +static NoDbgRegParms sqInt +isWidowedContextNoConvert(sqInt aOnceMarriedContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anInteger; + sqInt senderOop; + char *shouldBeFrameCallerField; + char *theFrame; + StackPage *thePage; + + assert((isContext(aOnceMarriedContext)) + && (isMarriedOrWidowedContext(aOnceMarriedContext))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + if (!((isFree(thePage)) + || (theFrame < ((thePage->headFP))))) { + /* The frame pointer is within the bounds of a live page. + Now check if it matches a frame. */ + anInteger = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + + /* begin withoutSmallIntegerTags: */ + assert((((anInteger) & 7) == 1)); + shouldBeFrameCallerField = ((char *)(anInteger - (smallIntegerTag()))); + if (((((char *)(longAt(theFrame + FoxSavedFP)))) == shouldBeFrameCallerField) + && (/* frameHasContext: */ + ((((usqInt)(longAt(theFrame + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFrame + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFrame + FoxIFrameFlags) + 2)) != 0))) { + assert(!(((isFrameonPage(theFrame, thePage)) + && (isForwarded(frameContext(theFrame)))))); + if ((longAt(theFrame + FoxThisContext)) == aOnceMarriedContext) { + return 0; + } + } + } + return 1; +} + + +/* Wrining any inst vars of a context must take account of potentially + married contexts + and set the state in the frame. Inst vars in subclasses don't need + mediation; subclasses + can't marry. */ + + /* StackInterpreter>>#isWriteMediatedContextInstVarIndex: */ +int +isWriteMediatedContextInstVarIndex(sqInt index) +{ + return index <= ReceiverIndex; +} + + +/* Support for external primitives. */ + + /* StackInterpreter>>#is:KindOfClass: */ +sqInt +isKindOfClass(sqInt oop, sqInt aClass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt oopClass; + sqInt tagBits; + + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while ((oopClass != GIV(nilObj)) + && ((/* isPointers: */ + ((!(oopClass & (tagMask())))) + && (((byteAt((void *)(oopClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOfAny(oopClass)) > InstanceSpecificationIndex))) { + if (oopClass == aClass) { + return 1; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + return 0; +} + + +/* Support for external primitives. */ + + /* StackInterpreter>>#is:KindOf: */ +sqInt +isKindOf(sqInt oop, char *className) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt oopClass; + sqInt tagBits; + + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while (!(oopClass == GIV(nilObj))) { + if (classNameOfIs(oopClass, className)) { + return 1; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + return 0; +} + + +/* Support for external primitives */ + + /* StackInterpreter>>#is:MemberOf: */ +sqInt +isMemberOf(sqInt oop, char *className) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oopClass; + sqInt tagBits; + + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + return classNameOfIs(oopClass, className); +} + + /* StackInterpreter>>#lengthOfNameOfClass: */ +static NoDbgRegParms sqInt +lengthOfNameOfClass(sqInt classOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + usqInt numSlots; + usqInt numSlots1; + usqInt numSlotsUsqInt; + sqInt objOop; + + /* begin numSlotsOf: */ + assert((classIndexOf(classOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(classOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (numSlots == GIV(metaclassNumSlots)) { + return lengthOfNameOfClass(longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord()))))))); + } + if (numSlots <= GIV(classNameIndex)) { + return 0; + } + objOop = longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(classNameIndex)) << (shiftForWord())))))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + numSlots1 = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots1; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots1 << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots1 << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots1 << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots1; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + /* StackInterpreter>>#literal:ofMethod: */ +sqInt +literalofMethod(sqInt offset, sqInt methodPointer) +{ + return longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); +} + + +/* This entry point needs to be implemented for the interpreter proxy. + Since BitBlt is now a plugin we need to look up + BitBltPlugin:=loadBitBltFrom and call it. This entire mechanism should + eventually go away and be + replaced with a dynamic lookup from BitBltPlugin itself but for backward + compatibility this stub is provided + */ + + /* StackInterpreter>>#loadBitBltFrom: */ +sqInt +loadBitBltFrom(sqInt bb) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void *fn; + + fn = ioLoadFunctionFrom("loadBitBltFrom", "BitBltPlugin"); + if (!fn) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return ((sqInt (*)(sqInt))fn)(bb); +} + + /* StackInterpreter>>#loadInitialContext */ +void +loadInitialContext(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt activeProc; + sqInt obj; + sqInt objOop; + + runLeakCheckerFor(GCModeFull); + + /* primitiveSuspend needs to know the class of LinkedList */ + + /* begin getClassTagOfLinkedList */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + objOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + obj = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + GIV(classLinkedListClassTag) = (longAt((void *)(obj))) & (classIndexMask()); + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + activeContext = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#longPrintOop: */ +void +longPrintOop(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aClass; + int byte; + char *className; + sqInt classPointer; + sqInt cls; + CogMethod *cogMethod; + sqInt column; + sqInt contextSize; + sqInt fieldOop; + sqInt fmt; + sqInt fmtSqInt; + sqInt header; + sqInt headerSqInt; + sqInt i; + usqInt index; + sqInt lastIndex; + usqInt lastPointer; + sqInt length; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objOop; + sqInt oopClass; + sqInt sp; + sqInt tagBits; + char *where; + + length = 0; + if (((oop & (tagMask())) != 0)) { + printImmediateObjecton(oop, GIV(transcript)); + return; + } + if (!(addressCouldBeObj(oop))) { + /* begin printCantBeObject:on: */ + fprintf(GIV(transcript), + "%p%s\n", + ((void *)oop), + ((where = whereIsMaybeCodeThing(oop)) + ? where + : (oop & 7 /* (allocationUnit - 1) */ + ? " is misaligned" + : whereIs(oop)))); + return; + } + if (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + printFreeObjecton(oop, GIV(transcript)); + return; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + printForwarderon(oop, GIV(transcript)); + return; + } + if ((cls = fetchClassOfNonImm(oop))) { + className = nameOfClasslengthInto(cls, (&length)); + fprintf(GIV(transcript), + "%p: a(n) %.*s", + ((void *)oop), + ((int) length), + className); + fprintf(GIV(transcript), + "(%" PRIxSQINT "=>%p)", + (longAt((void *)(oop))) & (classIndexMask()), + ((void *)cls)); + } + else { + fprintf(GIV(transcript), + "%p has a nil class!!\n", + ((void *)oop)); + } + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + fprintf(GIV(transcript), + " format %" PRIxSQINT "", + fmt); + if (fmt > 5 /* lastPointerFormat */) { + fprintf(GIV(transcript), + " nbytes %" PRIdSQINT "", + numBytesOf(oop)); + } + else { + if (/* isIndexableFormat: */ + (fmt >= (arrayFormat())) + && ((fmt <= (weakArrayFormat())) + || (fmt >= (sixtyFourBitIndexableFormat())))) { + /* begin lengthOf: */ + fmtSqInt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmtSqInt <= (ephemeronFormat())) { + length = numSlots; + goto l2; + } + if (fmtSqInt >= (firstByteFormat())) { + length = ((numSlots << (shiftForWord()))) - (fmtSqInt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmtSqInt >= (firstShortFormat())) { + length = ((numSlots << ((shiftForWord()) - 1))) - (fmtSqInt & 3); + goto l2; + } + if (fmtSqInt >= (firstLongFormat())) { + length = ((numSlots << ((shiftForWord()) - 2))) - (fmtSqInt & 1); + goto l2; + } + if (fmtSqInt == (sixtyFourBitIndexableFormat())) { + length = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + length = 0; + /* end lengthOf: */ +l2: + fprintf(GIV(transcript), + " size %" PRIdSQINT "", + length - (fixedFieldsOfformatlength(oop, fmt, length))); + } + } + + /* begin printHeaderTypeOf:on: */ + fprintf(GIV(transcript), + " hdr%d %c%c%c%c%c", + ((byteAt((void *)(oop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? 16 + : 8), + ((((usqInt)((byteAt((void *)(oop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1 + ? 'i' + : '.'), + ((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())) + ? 'r' + : '.'), + ((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift())) + ? 'p' + : '.'), + ((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (markedBitByteShift())) + ? 'm' + : '.'), + ((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (greyBitByteShift())) + ? 'g' + : '.')); + fprintf(GIV(transcript), + " hash %x\n", + (long32At((void *)(oop + 4))) & (identityHashHalfWordMask())); + if (/* isPureBitsFormat: */ + (fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat()))) { + /* This will answer false if splObj: ClassAlien is nilObject */ + + /* begin is:KindOfClass: */ + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while ((oopClass != GIV(nilObj)) + && ((/* isPointers: */ + ((!(oopClass & (tagMask())))) + && (((byteAt((void *)(oopClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOfAny(oopClass)) > InstanceSpecificationIndex))) { + if (oopClass == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord())))))))) { + fprintf(GIV(transcript), + " datasize %" PRIdSQINT " %s @ %p\n", + longAt((void *)(oop + BaseHeaderSize)), + ((longAt((void *)(oop + BaseHeaderSize))) < 0 + ? "indirect" + : (longAt((void *)(oop + BaseHeaderSize)) + ? "direct" + : "pointer")), + startOfAlienData(oop)); + return; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + classPointer = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord())))))); + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, objOop); + } + aClass = objOop; + + /* begin is:KindOfClass: */ + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while ((oopClass != GIV(nilObj)) + && ((/* isPointers: */ + ((!(oopClass & (tagMask())))) + && (((byteAt((void *)(oopClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOfAny(oopClass)) > InstanceSpecificationIndex))) { + if (oopClass == aClass) { + printStringDataOfon(oop, GIV(transcript)); + return; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + printNonPointerDataOfon(oop, GIV(transcript)); + return; + } + + /* begin numPointerSlotsOf: */ + fmtSqInt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if (fmtSqInt <= 5 /* lastPointerFormat */) { + if ((fmtSqInt == (indexablePointersFormat())) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l3; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(oop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l3: + lastPointer = CtxtTempFrameStart + contextSize; + goto l4; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())); + lastPointer = (((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l4; + } + if (fmtSqInt == (forwardedFormat())) { + lastPointer = 1; + goto l4; + } + if (fmtSqInt < (firstCompiledMethodFormat())) { + lastPointer = 0; + goto l4; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(oop)); + headerSqInt = longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + lastPointer = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l4: + lastIndex = ((0x100 < lastPointer) ? 0x100 : lastPointer); + if (lastIndex > 0) { + for (i = 1; i <= lastIndex; i += 1) { + fieldOop = longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)((i - 1)) << (shiftForWord())))))); + + /* begin space */ + printChar(' '); + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)(i - 1))); + printChar(' '); + printHex(fieldOop); + + /* begin space */ + printChar(' '); + if ((i == 1) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) { + /* begin printMethodHeaderOop: */ + /* begin isCogMethodReference: */ + assert(((((fieldOop) & 7) == 1)) + || (((((usqInt)fieldOop)) < (startOfMemory())) + && ((((usqInt)fieldOop)) >= (minCogMethodAddress())))); + if ((!(fieldOop & (smallIntegerTag())))) { + if ((cogMethod = methodFor(((void *)fieldOop)))) { + printHex(fieldOop); + + /* begin space */ + printChar(' '); + printDecodeMethodHeaderOop((cogMethod->methodHeader)); + goto l1; + } + } + printDecodeMethodHeaderOop(fieldOop); + /* end printMethodHeaderOop: */ +l1:; + } + else { + printOopShortInner(fieldOop); + } + cr(); + } + } + if (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + lastPointer = (((literalCountOf(oop)) + LiteralStart) * BytesPerOop) + 1; + + /* begin lengthOf: */ + fmtSqInt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmtSqInt <= (ephemeronFormat())) { + lastIndex = numSlots; + goto l5; + } + if (fmtSqInt >= (firstByteFormat())) { + lastIndex = ((numSlots << (shiftForWord()))) - (fmtSqInt & 7); + goto l5; + } + + /* bytes, including CompiledMethod */ + if (fmtSqInt >= (firstShortFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 1))) - (fmtSqInt & 3); + goto l5; + } + if (fmtSqInt >= (firstLongFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 2))) - (fmtSqInt & 1); + goto l5; + } + if (fmtSqInt == (sixtyFourBitIndexableFormat())) { + lastIndex = numSlots; + goto l5; + } + + /* fmt = self forwardedFormat */ + lastIndex = 0; + /* end lengthOf: */ +l5: + if ((lastIndex - lastPointer) > 0x100) { + lastIndex = lastPointer + 0x100; + } + column = 1; + for (index = lastPointer; index <= lastIndex; index += 1) { + if (column == 1) { + fprintf(GIV(transcript), + "%10p ", + ((void *)(((oop + BaseHeaderSize) + index) - 1))); + } + byte = ((int) (byteAt((void *)((oop + BaseHeaderSize) + (index - 1))))); + fprintf(GIV(transcript), + "%02x/%-+3d%c", + byte, + byte, + (column == 8 + ? '\n' + : ' ')); + if (((column += 1)) > 8) { + column = 1; + } + } + if ((lengthOf(oop)) > lastIndex) { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "..."); + } + if (((column >= 2) && (column <= 7))) { + cr(); + } + } + else { + if (lastPointer > lastIndex) { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "..."); + cr(); + } + } +} + + +/* Answer the relevant long store temp bytecode, which indicates it has a + primitive error code. + */ +/* 234 11101010 i i i i i i i i Store Temporary Variable #iiiiiiii */ +/* 129 10000001 jjkkkkkk Store (Receiver Variable, Temporary Location, + Illegal, Literal Variable) [jj] #kkkkkk */ + + /* StackInterpreter>>#longStoreBytecodeForHeader: */ +sqInt +longStoreBytecodeForHeader(sqInt methodHeader) +{ + return ((((sqLong) methodHeader)) < 0 + ? AltLongStoreBytecode + : LongStoreBytecode); +} + + +/* This method implements a simple method lookup cache. If an entry for the + given selector and classTag is found in the cache, set the values of + 'newMethod' and + 'primitiveFunctionPointer' and answer true. Otherwise, answer false. */ + + /* StackInterpreter>>#lookupInMethodCacheSel:classTag: */ +static NoDbgRegParms sqInt +lookupInMethodCacheSelclassTag(sqInt selector, sqInt classTag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt hash; + usqInt probe; + + /* begin inlineLookupInMethodCacheSel:classTag: */ + /* shift drops two low-order zeros from addresses */ + hash = selector ^ ((((usqInt)(classTag) << 2))); + + /* first probe */ + probe = hash & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == selector) + && ((GIV(methodCache)[probe + MethodCacheClass]) == classTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + return 1; + } + + /* second probe */ + probe = (((usqInt)(hash)) >> 1) & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == selector) + && ((GIV(methodCache)[probe + MethodCacheClass]) == classTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + return 1; + } + probe = (((usqInt)(hash)) >> 2) & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == selector) + && ((GIV(methodCache)[probe + MethodCacheClass]) == classTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + return 1; + } + return 0; +} + + /* StackInterpreter>>#lookupMethodInClass: */ +static NoDbgRegParms sqInt +lookupMethodInClass(sqInt class) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentClass; + sqInt dictionary; + sqInt found; + sqInt index; + usqInt length; + sqInt mask; + sqInt methodArray; + sqInt nextSelector; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt tagBits; + sqInt wrapAround; + + assert(addressCouldBeClassObj(class)); + + /* begin lookupBreakFor: */ + if ((breakSelectorLength <= 0) + && (/* shouldBreakForLookupIn:given: */ + (breakLookupClassTag) + && ((class == breakLookupClassTag) + || (((long32At((void *)(class + 4))) & (identityHashHalfWordMask())) == breakLookupClassTag)))) { + warning("lookup class send break (heartbeat suppressed)"); + } + currentClass = class; + while (currentClass != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + dictionary = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + assert(isNonImmediate(dictionary)); + if ((!((longAt((void *)(dictionary))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + dictionary = fixFollowedFieldofObjectwithInitialValue(MethodDictionaryIndex, currentClass, dictionary); + } + if (dictionary == GIV(nilObj)) { + /* MethodDict pointer is nil (hopefully due a swapped out stub) + -- raise exception #cannotInterpret:. */ + createActualMessageTo(class); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotInterpret) << (shiftForWord())))))); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), /* fetchClassTagOf: */ + ((tagBits = 0) + ? tagBits + : (longAt((void *)(null))) & (classIndexMask()))); + return lookupMethodInClass(superclassOf(currentClass)); + } + + /* begin lookupMethodInDictionary: */ + /* begin numSlotsOf: */ + assert((classIndexOf(dictionary)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(dictionary + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(dictionary - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + mask = (length - SelectorStart) - 1; + + /* Use linear search on small dictionaries; its cheaper. + Also the limit can be set to force linear search of all dictionaries, which supports the + booting of images that need rehashing (e.g. because a tracer has generated an image + with different hashes but hasn't rehashed it yet.) */ + if (mask <= GIV(methodDictLinearSearchLimit)) { + index = 0; + while (index <= mask) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)((index + SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOop = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(index, methodArray, objOop); + } + GIV(newMethod) = objOop; + found = 1; + goto l1; + } + index += 1; + } + found = 0; + goto l1; + } + index = SelectorStart + (mask & ((((GIV(messageSelector) & (tagMask())) != 0) + ? (GIV(messageSelector) >> 3) + : (long32At((void *)(GIV(messageSelector) + 4))) & (identityHashHalfWordMask())))); + + /* It is assumed that there are some nils in this dictionary, and search will + stop when one is encountered. However, if there are no nils, then wrapAround + will be detected the second time the loop gets to the end of the table. */ + wrapAround = 0; + while (1) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (nextSelector == GIV(nilObj)) { + found = 0; + goto l1; + } + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOop = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(index - SelectorStart, methodArray, objOop); + } + GIV(newMethod) = objOop; + found = 1; + goto l1; + } + index += 1; + if (index == length) { + if (wrapAround) { + found = 0; + goto l1; + } + wrapAround = 1; + index = SelectorStart; + } + } + found = 0; + /* end lookupMethodInDictionary: */ +l1: + if (found) { + return currentClass; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOopSqInt = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOopSqInt)); + if ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOopSqInt); + } + currentClass = objOopSqInt; + } + + /* Could not find #doesNotUnderstand: -- unrecoverable error. */ + if (GIV(messageSelector) == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))))) { + error("Recursive not understood error encountered"); + } + + /* Cound not find a normal message -- raise exception #doesNotUnderstand: */ + createActualMessageTo(class); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))); + sendBreakpointclassTag(GIV(messageSelector) + BaseHeaderSize, lengthOf(GIV(messageSelector)), (long32At((void *)(class + 4))) & (identityHashHalfWordMask())); + return lookupMethodInClass(class); +} + + +/* Lookup messageSelector in class. Answer 0 on success. Answer the splObj: + index for the error selector to use on failure rather than performing MNU + processing etc. */ + + /* StackInterpreter>>#lookupMNUInClass: */ +static NoDbgRegParms sqInt +lookupMNUInClass(sqInt class) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentClass; + sqInt dictionary; + sqInt found; + sqInt index; + usqInt length; + sqInt mask; + sqInt methodArray; + sqInt nextSelector; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt wrapAround; + + /* begin lookupBreakFor: */ + if ((breakSelectorLength <= 0) + && (/* shouldBreakForLookupIn:given: */ + (breakLookupClassTag) + && ((class == breakLookupClassTag) + || (((long32At((void *)(class + 4))) & (identityHashHalfWordMask())) == breakLookupClassTag)))) { + warning("lookup class send break (heartbeat suppressed)"); + } + currentClass = class; + while (currentClass != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + dictionary = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + assert(isNonImmediate(dictionary)); + if ((!((longAt((void *)(dictionary))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + dictionary = fixFollowedFieldofObjectwithInitialValue(MethodDictionaryIndex, currentClass, dictionary); + } + if (dictionary == GIV(nilObj)) { + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOop); + } + GIV(lkupClass) = objOop; + return SelectorCannotInterpret; + } + + /* begin lookupMethodInDictionary: */ + /* begin numSlotsOf: */ + assert((classIndexOf(dictionary)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(dictionary + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(dictionary - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + mask = (length - SelectorStart) - 1; + + /* Use linear search on small dictionaries; its cheaper. + Also the limit can be set to force linear search of all dictionaries, which supports the + booting of images that need rehashing (e.g. because a tracer has generated an image + with different hashes but hasn't rehashed it yet.) */ + if (mask <= GIV(methodDictLinearSearchLimit)) { + index = 0; + while (index <= mask) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)((index + SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(index, methodArray, objOopSqInt); + } + GIV(newMethod) = objOopSqInt; + found = 1; + goto l1; + } + index += 1; + } + found = 0; + goto l1; + } + index = SelectorStart + (mask & ((((GIV(messageSelector) & (tagMask())) != 0) + ? (GIV(messageSelector) >> 3) + : (long32At((void *)(GIV(messageSelector) + 4))) & (identityHashHalfWordMask())))); + + /* It is assumed that there are some nils in this dictionary, and search will + stop when one is encountered. However, if there are no nils, then wrapAround + will be detected the second time the loop gets to the end of the table. */ + wrapAround = 0; + while (1) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (nextSelector == GIV(nilObj)) { + found = 0; + goto l1; + } + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(index - SelectorStart, methodArray, objOopSqInt); + } + GIV(newMethod) = objOopSqInt; + found = 1; + goto l1; + } + index += 1; + if (index == length) { + if (wrapAround) { + found = 0; + goto l1; + } + wrapAround = 1; + index = SelectorStart; + } + } + found = 0; + /* end lookupMethodInDictionary: */ +l1: + if (found) { + addNewMethodToCache(class); + return 0; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOopSqInt = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOopSqInt)); + if ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOopSqInt); + } + currentClass = objOopSqInt; + } + GIV(lkupClass) = class; + return SelectorDoesNotUnderstand; +} + + +/* Lookup messageSelector in class. Answer 0 on success. Answer the splObj: + index for the error selector to use on failure rather than performing MNU + processing etc. */ + + /* StackInterpreter>>#lookupOrdinaryNoMNUEtcInClass: */ +static NoDbgRegParms sqInt +lookupOrdinaryNoMNUEtcInClass(sqInt class) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentClass; + sqInt dictionary; + sqInt found; + sqInt index; + usqInt length; + sqInt mask; + sqInt methodArray; + sqInt nextSelector; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt wrapAround; + + /* begin lookupBreakFor: */ + if ((breakSelectorLength <= 0) + && (/* shouldBreakForLookupIn:given: */ + (breakLookupClassTag) + && ((class == breakLookupClassTag) + || (((long32At((void *)(class + 4))) & (identityHashHalfWordMask())) == breakLookupClassTag)))) { + warning("lookup class send break (heartbeat suppressed)"); + } + currentClass = class; + while (currentClass != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + dictionary = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + assert(isNonImmediate(dictionary)); + if ((!((longAt((void *)(dictionary))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + dictionary = fixFollowedFieldofObjectwithInitialValue(MethodDictionaryIndex, currentClass, dictionary); + } + if (dictionary == GIV(nilObj)) { + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOop); + } + GIV(lkupClass) = objOop; + return SelectorCannotInterpret; + } + + /* begin lookupMethodInDictionary: */ + /* begin numSlotsOf: */ + assert((classIndexOf(dictionary)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(dictionary + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(dictionary - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + mask = (length - SelectorStart) - 1; + + /* Use linear search on small dictionaries; its cheaper. + Also the limit can be set to force linear search of all dictionaries, which supports the + booting of images that need rehashing (e.g. because a tracer has generated an image + with different hashes but hasn't rehashed it yet.) */ + if (mask <= GIV(methodDictLinearSearchLimit)) { + index = 0; + while (index <= mask) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)((index + SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(index, methodArray, objOopSqInt); + } + GIV(newMethod) = objOopSqInt; + found = 1; + goto l1; + } + index += 1; + } + found = 0; + goto l1; + } + index = SelectorStart + (mask & ((((GIV(messageSelector) & (tagMask())) != 0) + ? (GIV(messageSelector) >> 3) + : (long32At((void *)(GIV(messageSelector) + 4))) & (identityHashHalfWordMask())))); + + /* It is assumed that there are some nils in this dictionary, and search will + stop when one is encountered. However, if there are no nils, then wrapAround + will be detected the second time the loop gets to the end of the table. */ + wrapAround = 0; + while (1) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (nextSelector == GIV(nilObj)) { + found = 0; + goto l1; + } + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(index - SelectorStart, methodArray, objOopSqInt); + } + GIV(newMethod) = objOopSqInt; + found = 1; + goto l1; + } + index += 1; + if (index == length) { + if (wrapAround) { + found = 0; + goto l1; + } + wrapAround = 1; + index = SelectorStart; + } + } + found = 0; + /* end lookupMethodInDictionary: */ +l1: + if (found) { + addNewMethodToCache(class); + return 0; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOopSqInt = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOopSqInt)); + if ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOopSqInt); + } + currentClass = objOopSqInt; + } + GIV(lkupClass) = class; + return SelectorDoesNotUnderstand; +} + + +/* Lookup selector in class. Answer the method or nil. This is a debugging + routine. It does /not/ side-effect lookupClass or newMethod. */ + + /* StackInterpreter>>#lookupSelector:inClass: */ +sqInt +lookupSelectorinClass(sqInt selector, sqInt class) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentClass; + sqInt dictionary; + usqInt index; + usqInt length; + sqInt mask; + sqInt meth; + sqInt methodArray; + sqInt nextSelector; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt wrapAround; + + currentClass = class; + while (currentClass != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + dictionary = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + assert(isNonImmediate(dictionary)); + if ((!((longAt((void *)(dictionary))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + dictionary = fixFollowedFieldofObjectwithInitialValue(MethodDictionaryIndex, currentClass, dictionary); + } + if (dictionary == GIV(nilObj)) { + return null; + } + + /* begin lookupMethodFor:InDictionary: */ + /* begin numSlotsOf: */ + assert((classIndexOf(dictionary)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(dictionary + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(dictionary - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + mask = (length - SelectorStart) - 1; + index = SelectorStart + (mask & ((((selector & (tagMask())) != 0) + ? (selector >> 3) + : (long32At((void *)(selector + 4))) & (identityHashHalfWordMask())))); + + /* It is assumed that there are some nils in this dictionary, and search will + stop when one is encountered. However, if there are no nils, then wrapAround + will be detected the second time the loop gets to the end of the table. */ + wrapAround = 0; + while (1) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((index << (shiftForWord()))))); + if (nextSelector == GIV(nilObj)) { + meth = null; + goto l1; + } + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == selector) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(index - SelectorStart, methodArray, objOopSqInt); + } + meth = objOopSqInt; + goto l1; + } + index += 1; + if (index == length) { + if (wrapAround) { + meth = null; + goto l1; + } + wrapAround = 1; + index = SelectorStart; + } + } + meth = null; + /* end lookupMethodFor:InDictionary: */ +l1: + if (meth) { + return meth; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOop); + } + currentClass = objOop; + } + return null; +} + + +/* Convert married contexts to widowed contexts, and in the CoInterpreter, + map any machine code pcs to bytecode pcs, and then nil all slots beyond + top of stack, so that the context can be resumed on any VM. */ + + /* StackInterpreter>>#makeContextSnapshotSafe: */ +static NoDbgRegParms void +makeContextSnapshotSafe(sqInt ctxt) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + usqInt numSlots; + sqInt toDoLimit; + sqInt toDoStart; + + assert(isContext(ctxt)); + + /* The stack pages have already been discarded. Any remaining married contexts are actually widows. */ + if (((((longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin markContextAsDead: */ + assert(isContext(ctxt)); + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs(SenderIndex, ctxt, GIV(nilObj))); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, ctxt, GIV(nilObj))); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + ensureContextHasBytecodePC(ctxt); + } + + /* Fill slots beyond top of stack with nil */ + toDoStart = (fetchStackPointerOf(ctxt)) + CtxtTempFrameStart; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(ctxt)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(ctxt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(ctxt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = toDoStart; i <= toDoLimit; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs(i, ctxt, GIV(nilObj))); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } +} + + +/* make a Point xValue@yValue. + We know both will be integers so no value nor root checking is needed */ + + /* StackInterpreter>>#makePointwithxValue:yValue: */ +usqInt +makePointwithxValueyValue(sqInt xValue, sqInt yValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newObj; + usqInt numBytes; + usqInt pointResult; + + /* begin eeInstantiatePoint */ + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert(((YIndex + 1) >= 0) + && ((knownClassAtIndex(ClassPointCompactIndex)) != GIV(nilObj))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(knownClassAtIndex(ClassPointCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert((YIndex + 1) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (((YIndex + 1) < 1 + ? 8 /* allocationUnit */ + : (YIndex + 1) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + pointResult = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) (YIndex + 1))) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + ClassPointCompactIndex); + GIV(freeStart) += numBytes; + pointResult = newObj; + /* end eeInstantiatePoint */ +l1: + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pointResult)) + && (!(isForwarded(pointResult)))); + assert(validStorePointerUncheckedArgs(XIndex, pointResult, (((usqInt)xValue << 3) | 1))); + longAtput((void *)((pointResult + BaseHeaderSize) + ((((usqInt)(XIndex) << (shiftForWord()))))),(((usqInt)xValue << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pointResult)) + && (!(isForwarded(pointResult)))); + assert(validStorePointerUncheckedArgs(YIndex, pointResult, (((usqInt)yValue << 3) | 1))); + longAtput((void *)((pointResult + BaseHeaderSize) + ((((usqInt)(YIndex) << (shiftForWord()))))),(((usqInt)yValue << 3) | 1)); + return pointResult; +} + + +/* Map all oops in the interpreter's state to their new values + during garbage collection or a become: operation. */ +/* Assume: All traced variables contain valid oops. */ + + /* StackInterpreter>>#mapInterpreterOops */ +static void +mapInterpreterOops(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ipdelta; + sqInt result; + + mapStackPages(); + + /* begin mapMachineCode: */ + mapObjectReferencesInMachineCode(GIV(gcMode)); + mapPrimTraceLog(); + mapTraceLog(); + + /* begin mapVMRegisters */ + ipdelta = 0; + if (shouldRemapObj(GIV(method))) { + ipdelta = (/* method:includesAddress: */ + (GIV(instructionPointer) > GIV(method)) + && (GIV(instructionPointer) < (GIV(method) + ((((numSlotsOfAny(GIV(method))) << (shiftForWord()))) + BaseHeaderSize))) + ? GIV(instructionPointer) - GIV(method) + : 0); + GIV(method) = remapObj(GIV(method)); + if (ipdelta) { + GIV(instructionPointer) = GIV(method) + ipdelta; + } + } + if (/* shouldRemapOop: */ + ((!(GIV(newMethod) & (tagMask())))) + && (shouldRemapObj(GIV(newMethod)))) { + ipdelta = ((!ipdelta) + && (/* method:includesAddress: */ + (GIV(instructionPointer) > GIV(newMethod)) + && (GIV(instructionPointer) < (GIV(newMethod) + ((((numSlotsOfAny(GIV(newMethod))) << (shiftForWord()))) + BaseHeaderSize)))) + ? GIV(instructionPointer) - GIV(newMethod) + : 0); + + /* don't relocate twice!! */ + GIV(newMethod) = remapObj(GIV(newMethod)); + if (ipdelta) { + GIV(instructionPointer) = GIV(newMethod) + ipdelta; + } + } + + /* begin mapProfileState */ + /* begin profileStateDoUpdating: */ + if (GIV(profileProcess)) { + if ((result = (shouldRemapObj(GIV(profileProcess)) + ? remapObj(GIV(profileProcess)) + : 0))) { + GIV(profileProcess) = result; + } + } + if (GIV(profileMethod)) { + if ((result = (shouldRemapObj(GIV(profileMethod)) + ? remapObj(GIV(profileMethod)) + : 0))) { + GIV(profileMethod) = result; + } + } + if (GIV(profileSemaphore)) { + if ((result = (shouldRemapObj(GIV(profileSemaphore)) + ? remapObj(GIV(profileSemaphore)) + : 0))) { + GIV(profileSemaphore) = result; + } + } +# if LRPCheck + /* The longRunningPrimitiveCheckMethod (LRPCM) is sampled in an interrupt. Be very careful with it. + If longRunningPrimitiveCheckSequenceNumber (LRPCSN) = statCheckForEvents then LRPCM has + been recenty sampled and could be mapped or not, but it must be newMethod and we can simply + copy newMethod. If LRPCSN ~= statCheckForEvents then LRPCM must be some extant object and + needs to be remapped. */ + sqLowLevelMFence(); + if (GIV(longRunningPrimitiveCheckMethod)) { + if (GIV(longRunningPrimitiveCheckSequenceNumber) == GIV(statCheckForEvents)) { + GIV(longRunningPrimitiveCheckMethod) = GIV(newMethod); + } + else { + if (shouldRemapObj(GIV(longRunningPrimitiveCheckMethod))) { + GIV(longRunningPrimitiveCheckMethod) = remapObj(GIV(longRunningPrimitiveCheckMethod)); + } + } + sqLowLevelMFence(); + } + if (GIV(longRunningPrimitiveCheckSemaphore)) { + if (shouldRemapObj(GIV(longRunningPrimitiveCheckSemaphore))) { + GIV(longRunningPrimitiveCheckSemaphore) = remapObj(GIV(longRunningPrimitiveCheckSemaphore)); + } + } +# endif // LRPCheck + + if ((GIV(tempOop) != 0) + && (/* shouldRemapOop: */ + ((!(GIV(tempOop) & (tagMask())))) + && (shouldRemapObj(GIV(tempOop))))) { + GIV(tempOop) = remapObj(GIV(tempOop)); + } + if ((GIV(tempOop2) != 0) + && (/* shouldRemapOop: */ + ((!(GIV(tempOop2) & (tagMask())))) + && (shouldRemapObj(GIV(tempOop2))))) { + GIV(tempOop2) = remapObj(GIV(tempOop2)); + } +} + + +/* The prim trace log is a circular buffer of objects. If there is + an entry at primTraceLogIndex - 1 \\ PrimTraceBufferSize it has entries. + If there is something at primTraceLogIndex it has wrapped. */ + + /* StackInterpreter>>#mapPrimTraceLog */ +static void +mapPrimTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt entryOop; + sqInt i; + sqInt remainder; + + if (!(GIV(primTraceLog)[/* safe:mod: */ + (((remainder = (GIV(primTraceLogIndex) - 1) % PrimTraceLogSize)) < 0 + ? remainder + PrimTraceLogSize + : remainder)])) { + return; + } + if (GIV(primTraceLog)[GIV(primTraceLogIndex)]) { + for (i = GIV(primTraceLogIndex); i < PrimTraceLogSize; i += 1) { + entryOop = GIV(primTraceLog)[i]; + if ((entryOop != 0) + && (/* shouldRemapOop: */ + ((!(entryOop & (tagMask())))) + && (shouldRemapObj(entryOop)))) { + GIV(primTraceLog)[i] = (remapObj(entryOop)); + } + } + } + for (i = 0; i < GIV(primTraceLogIndex); i += 1) { + entryOop = GIV(primTraceLog)[i]; + if ((entryOop != 0) + && (/* shouldRemapOop: */ + ((!(entryOop & (tagMask())))) + && (shouldRemapObj(entryOop)))) { + GIV(primTraceLog)[i] = (remapObj(entryOop)); + } + } +} + + +/* The trace log is a circular buffer of pairs of entries. If there is + an entry at traceLogIndex - 3 \\ TraceBufferSize it has entries. + If there is something at traceLogIndex it has wrapped. */ + + /* StackInterpreter>>#mapTraceLog */ +static void +mapTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt limit; + sqInt oop; + sqInt remainder; + sqInt traceType; + + oop = 0; + limit = /* safe:mod: */ + (((remainder = (GIV(traceLogIndex) - 3) % TraceBufferSize)) < 0 + ? remainder + TraceBufferSize + : remainder); + if (!(GIV(traceLog)[limit])) { + return; + } + if (GIV(traceLog)[GIV(traceLogIndex)]) { + limit = TraceBufferSize - 3; + } + for (i = 0; i <= limit; i += 3) { + traceType = ((usqInt)((GIV(traceLog)[i]))) >> TraceTypeShift; + if ((traceType <= TraceBlockCreation) + && ((((oop = GIV(traceLog)[i + 1])) != 0) + && (/* shouldRemapOop: */ + ((!(oop & (tagMask())))) + && (shouldRemapObj(oop))))) { + GIV(traceLog)[i + 1] = (remapObj(oop)); + } + if ((/* extraIsObjectForTraceType: */ + (traceType == TraceSend) + || (traceType == TraceContextSwitch)) + && ((((oop = GIV(traceLog)[i + 2])) != 0) + && (/* shouldRemapOop: */ + ((!(oop & (tagMask())))) + && (shouldRemapObj(oop))))) { + GIV(traceLog)[i + 2] = (remapObj(oop)); + } + } +} + + +/* The prim trace log is a circular buffer of objects. If there is + an entry at primTraceLogIndex - 1 \\ PrimTraceBufferSize it has entries. + If there is something at primTraceLogIndex it has wrapped. */ + + /* StackInterpreter>>#markAndTracePrimTraceLog */ +static void +markAndTracePrimTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt entryOop; + sqInt i; + sqInt remainder; + + if (!(GIV(primTraceLog)[/* safe:mod: */ + (((remainder = (GIV(primTraceLogIndex) - 1) % PrimTraceLogSize)) < 0 + ? remainder + PrimTraceLogSize + : remainder)])) { + return; + } + if (GIV(primTraceLog)[GIV(primTraceLogIndex)]) { + for (i = GIV(primTraceLogIndex); i < PrimTraceLogSize; i += 1) { + entryOop = GIV(primTraceLog)[i]; + if ((entryOop != 0) + && ((!(entryOop & (tagMask()))))) { + markAndTrace(entryOop); + } + } + } + for (i = 0; i < GIV(primTraceLogIndex); i += 1) { + entryOop = GIV(primTraceLog)[i]; + if ((entryOop != 0) + && ((!(entryOop & (tagMask()))))) { + markAndTrace(entryOop); + } + } +} + + +/* GC of pages. Throwing away all stack pages on full GC is simple but + dangerous because it causes us to allocate lots of contexts immediately + before a GC. + Reclaiming pages whose top context is not referenced is poor because it + would take N incrementalGCs to reclaim N unused pages. Only the page whose + top context is not referred to by the bottom context of any other page + would be + reclaimed. Not until the next GC would the page whose top contect is the + previously reclaimed page's base frame's bottom context be reclaimed. + + Better is to not mark stack pages until their contexts are encountered. We + can eagerly trace the active page and the page reachable from its bottom + context if any, and so on. Other pages can be marked when we encounter a + married context. */ + + /* StackInterpreter>>#markAndTraceStackPages: */ +static NoDbgRegParms sqInt +markAndTraceStackPages(sqInt fullGCFlag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt context; + sqInt i; + void *pointer; + sqInt senderOop; + char *theFP; + StackPage *thePage; + StackPage *thePage1; + + + /* On an incremental GC simply consider all non-free stack pages to be roots. */ + if (!fullGCFlag) { + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + markAndTraceStackPage(thePage); + } + } + return null; + } + + /* On a full GC only eagerly trace pages referenced from + the base of the active page, i.e. on the active stack. */ + if (!GIV(stackPage)) { + return null; + } + thePage = GIV(stackPage); + do { + markAndTraceStackPage(thePage); + theFP = (thePage->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + context = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(context)); + assert((context == (nilObject())) + || (isContext(followMaybeForwarded(context)))); + if ((/* isContext: */ + ((!(context & (tagMask())))) + && (((longAt((void *)(context))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && ((((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(context)))))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + pointer = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(pointer), GIV(pages)); + assert(!(isFree(thePage))); + } + } while(((thePage->trace)) < StackPageTraced); + return 0; +} + + +/* The trace log is a circular buffer of pairs of entries. If there is an + entry at + traceLogIndex - 3 \\ TraceBufferSize it has entries. If there is something + at traceLogIndex it has wrapped. */ + + /* StackInterpreter>>#markAndTraceTraceLog */ +static void +markAndTraceTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt limit; + sqInt oop; + sqInt remainder; + sqInt traceType; + + limit = /* safe:mod: */ + (((remainder = (GIV(traceLogIndex) - 3) % TraceBufferSize)) < 0 + ? remainder + TraceBufferSize + : remainder); + if (!(GIV(traceLog)[limit])) { + return; + } + if (GIV(traceLog)[GIV(traceLogIndex)]) { + limit = TraceBufferSize - 3; + } + for (i = 0; i <= limit; i += 3) { + traceType = ((usqInt)((GIV(traceLog)[i]))) >> TraceTypeShift; + if (traceType <= TraceBlockCreation) { + oop = GIV(traceLog)[i + 1]; + if ((oop != 0) + && ((!(oop & (tagMask()))))) { + markAndTrace(oop); + } + } + if (/* extraIsObjectForTraceType: */ + (traceType == TraceSend) + || (traceType == TraceContextSwitch)) { + oop = GIV(traceLog)[i + 2]; + if ((oop != 0) + && ((!(oop & (tagMask()))))) { + markAndTrace(oop); + } + } + } +} + + +/* Trace any untraced pages */ + + /* StackInterpreter>>#markAndTraceUntracedReachableStackPages */ +static void +markAndTraceUntracedReachableStackPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt foundToBeTracedPage; + sqInt i; + StackPage *thePage; + + do { + foundToBeTracedPage = 0; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((!(isFree(thePage))) + && (((thePage->trace)) == StackPageReachedButUntraced)) { + foundToBeTracedPage = 1; + markAndTraceStackPage(thePage); + } + } + } while(foundToBeTracedPage); +} + + +/* This is a helper for primitiveObjectPointsTo so it *does not* check the + frameContext field because that is an implicit self-reference not present + in the state . + */ + + /* StackInterpreter>>#marriedContext:pointsTo:stackDeltaForCurrentFrame: */ +static NoDbgRegParms sqInt +marriedContextpointsTostackDeltaForCurrentFrame(sqInt spouseContext, sqInt anOop, sqInt stackDeltaForCurrentFrame) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *rcvrOffset; + sqInt senderOop; + char *theFP; + StackPage *thePage; + char *theSP; + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((spouseContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (theFP == GIV(framePointer)) { + theSP = GIV(stackPointer) + (stackDeltaForCurrentFrame * BytesPerWord); + } + else { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + theSP = findSPOfon(theFP, thePage); + } + if ((((anOop) & 7) == 1)) { + if ((anOop == ((((usqInt)(/* stackPointerIndexForFrame:WithSP: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (((usqInt)(((theFP + FoxMFReceiver) - theSP))) >> (shiftForWord())) + (((mframeCogMethod(theFP))->cmNumArgs)) + : (((usqInt)(((theFP + FoxIFReceiver) - theSP))) >> (shiftForWord())) + (byteAt((theFP + FoxIFrameFlags) + 1)))) << 3) | 1))) + || (anOop == (externalInstVarofContext(InstructionPointerIndex, spouseContext)))) { + return 1; + } + } + else { + if (anOop == (/* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)))) { + return 1; + } + if (longAt(theFP + FoxSavedFP)) { + if ((/* frameHasContext: */ + ((((usqInt)(longAt((((char *)(longAt(theFP + FoxSavedFP)))) + FoxMethod)))) < (startOfMemory()) + ? ((longAt((((char *)(longAt(theFP + FoxSavedFP)))) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt(((((char *)(longAt(theFP + FoxSavedFP)))) + FoxIFrameFlags) + 2)) != 0)) + && (anOop == (longAt((((char *)(longAt(theFP + FoxSavedFP)))) + FoxThisContext)))) { + return 1; + } + } + else { + if (anOop == (frameCallerContext(theFP))) { + return 1; + } + } + } + + /* Check stack and instruction pointer fields. + Check method and sender fields, avoiding unnecessarily reifying sender context. + Now check receiver, temps and stack contents */ + rcvrOffset = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + while (theSP <= rcvrOffset) { + if (anOop == (longAt(theSP))) { + return 1; + } + theSP += BytesPerWord; + } + + /* Finally check stacked receiver (closure field or duplicate of receiver) and arguments */ + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + rcvrOffset = theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord()))))); + while (theSP <= rcvrOffset) { + if (anOop == (longAt(theSP))) { + return 1; + } + theSP += BytesPerWord; + } + return 0; +} + + +/* Establish aContext at the base of a new stackPage, make the stackPage the + active one and set-up the interreter registers. This is used to boot the + system and bring it back after a snapshot. */ + + /* StackInterpreter>>#marryContextInNewStackPageAndInitializeInterpreterRegisters: */ +static NoDbgRegParms void +marryContextInNewStackPageAndInitializeInterpreterRegisters(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + sqInt header; + sqInt methodHeader; + StackPage *newPage; + sqInt top; + + assert(!GIV(stackPage)); + newPage = makeBaseFrameFor(aContext); + + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (newPage->headSP); + GIV(framePointer) = (newPage->headFP); + aMethodObj = longAt(((GIV(stackPage)->headFP)) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; +} + + +/* Assert-only check for leaks after external prim calls if checkForLeaks + includes the GCCheckPrimCall flag. + This is ionly really useful from a low-level C debugger, hence no + accessors for checkedPluginName. + */ + + /* StackInterpreter>>#maybeLeakCheckExternalPrimCall: */ +static NoDbgRegParms sqInt +maybeLeakCheckExternalPrimCall(sqInt aMethodObj) +{ + if ((((checkForLeaks & GCCheckPrimCall) != 0)) + && (((primitiveIndexOf(aMethodObj)) == PrimNumberExternalCall) + && (checkedPluginName == (fetchPointerofObject(0, longAt((void *)((aMethodObj + BaseHeaderSize) + (1U << (shiftForWord()))))))))) { + runLeakCheckerFor(GCCheckPrimCall); + } + return 1; +} + + +/* Answer the selector of a method, assuming its penultimate literal is + either a symbol or a pointer object whose first slot references the method + and whose second slot is a symbol (i.e. an AdditionalMethodState). If a + Symbol can't be found answer nil. This isn't satisfactory, as it puts a + lot of information + into the VM, but it is needed for adequate crash debugging at Cadence. + With full blocks as of 9/2016 the last literal of a CompiledBlock is a + back pointer + to the enclosing block or compiled method. */ + + /* StackInterpreter>>#maybeSelectorOfMethod: */ +sqInt +maybeSelectorOfMethod(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt maybeSelector; + usqInt numSlots; + sqInt penultimateLiteral; + sqInt ultimateLiteral; + + assert(isOopCompiledMethod(methodObj)); + ultimateLiteral = ultimateLiteralOf(methodObj); + if (/* isOopCompiledMethod: */ + ((!(ultimateLiteral & (tagMask())))) + && (((byteAt((void *)(ultimateLiteral + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) { + return maybeSelectorOfMethod(ultimateLiteral); + } + penultimateLiteral = penultimateLiteralOf(methodObj); + if (/* isWordsOrBytes: */ + ((!(penultimateLiteral & (tagMask())))) + && (isWordsOrBytesNonImm(penultimateLiteral))) { + return (((longAt((void *)(penultimateLiteral))) & (classIndexMask())) == (fetchClassTagOfNonImm(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))))) + ? penultimateLiteral + : 0); + } + return ((/* isPointers: */ + ((!(penultimateLiteral & (tagMask())))) + && (((byteAt((void *)(penultimateLiteral + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(penultimateLiteral)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(penultimateLiteral + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(penultimateLiteral - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= 2) + && (((longAt((void *)((penultimateLiteral + BaseHeaderSize) + (0U << (shiftForWord()))))) == methodObj) + && (((maybeSelector = longAt((void *)((penultimateLiteral + BaseHeaderSize) + (1U << (shiftForWord()))))), + (/* isWordsOrBytes: */ + ((!(maybeSelector & (tagMask())))) + && (isWordsOrBytesNonImm(maybeSelector))) + && (((longAt((void *)(maybeSelector))) & (classIndexMask())) == (fetchClassTagOfNonImm(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord()))))))))))))) + ? maybeSelector + : 0); +} + + /* StackInterpreter>>#methodArgumentCount */ +sqInt +methodArgumentCount(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(argumentCount); +} + + /* StackInterpreter>>#methodClassAssociationOf: */ +sqInt +methodClassAssociationOf(sqInt methodPointer) +{ + sqInt offset; + + offset = (literalCountOf(methodPointer)) - 1; + + /* begin literal:ofMethod: */ + return longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); +} + + +/* Answer the method class of a method which is the value of an Association + in the last literal, + or answer nil if there isn't one. + Using a read barrier here simplifies the become implementation and costs + very little + because the class index and ValueIndex of the association almost certainly + share a cache line. */ + + /* StackInterpreter>>#methodClassOf: */ +sqInt +methodClassOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt literal; + sqInt offset; + + offset = (literalCountOf(methodPointer)) - 1; + + /* begin followLiteral:ofMethod: */ + /* begin followField:ofObject: */ + literal = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = fixFollowedFieldofObjectwithInitialValue(offset + LiteralStart, methodPointer, literal); + } + return ((literal != GIV(nilObj)) + && (/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + ? (assert((numSlotsOf(literal)) > ValueIndex), + followFieldofObject(ValueIndex, literal)) + : GIV(nilObj)); +} + + /* StackInterpreter>>#methodPrimitiveIndex */ +sqInt +methodPrimitiveIndex(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + + if (!((addressCouldBeObj(GIV(newMethod))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return -1; + } + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin primitiveIndexOfMethod:header: */ + return (((methodHeader & AlternateHeaderHasPrimFlag) != 0) + ? ((firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize), + (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8)))) + : 0); +} + + +/* Sets the return value for a method. In the CoInterpreter we replace the + cumbersome primResult machinery. */ + + /* StackInterpreter>>#methodReturnBool: */ +sqInt +methodReturnBool(sqInt boolean) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + assert(!((failed()))); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (boolean + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return 0; +} + + +/* Sets the return value for a method. */ + + /* StackInterpreter>>#methodReturnFloat: */ +sqInt +methodReturnFloat(double aFloat) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + assert(!((failed()))); + + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),floatObjectOf(aFloat)); + GIV(stackPointer) = sp; + return 0; +} + + +/* Sets the return value for a method. In the CoInterpreter we replace the + cumbersome primResult machinery. */ + + /* StackInterpreter>>#methodReturnInteger: */ +sqInt +methodReturnInteger(sqInt integer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + assert(!((failed()))); + + /* begin pop:thenPushInteger: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),(((usqInt)integer << 3) | 1)); + GIV(stackPointer) = sp; + return 0; +} + + +/* Sets the return value for a method */ + + /* StackInterpreter>>#methodReturnReceiver */ +sqInt +methodReturnReceiver(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(!((failed()))); + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return 0; +} + + +/* Attempt to answer a ByteString for a given C string as the result of a + primitive. + */ + + /* StackInterpreter>>#methodReturnString: */ +NeverInline sqInt +methodReturnString(const char *aCString) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt result; + + assert(!((failed()))); + if (aCString) { + if ((result = stringForCString(aCString))) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); + } + else { + GIV(primFailCode) = PrimErrNoMemory; + } + } + else { + GIV(primFailCode) = PrimErrOperationFailed; + } + return 0; +} + + +/* Sets the return value for a method. In the CoInterpreter we replace the + cumbersome primResult machinery. */ + + /* StackInterpreter>>#methodReturnValue: */ +sqInt +methodReturnValue(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + assert(!((failed()))); + + /* begin pop:thenPush: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),oop); + GIV(stackPointer) = sp; + return 0; +} + + +/* A negative header selects the alternate bytecode set. */ + + /* StackInterpreter>>#methodUsesAlternateBytecodeSet: */ +int +methodUsesAlternateBytecodeSet(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + sqInt methodHeader; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin headerIndicatesAlternateBytecodeSet: */ + return (((sqLong) methodHeader)) < 0; +} + + +/* Answer if aMethodObj contains a primitive and uses the primitive error + code. + */ + + /* StackInterpreter>>#methodUsesPrimitiveErrorCode: */ +static NoDbgRegParms sqInt +methodUsesPrimitiveErrorCode(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + return ((/* primitiveIndexOfMethod:header: */ + (((methodHeader & AlternateHeaderHasPrimFlag) != 0) + ? ((firstBytecode = (aMethodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize), + (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8)))) + : 0)) > 0) + && ((/* longStoreBytecodeForHeader: */ + ((((sqLong) methodHeader)) < 0 + ? AltLongStoreBytecode + : LongStoreBytecode)) == (fetchByteofObject(((((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop) + 3 /* sizeOfCallPrimitiveBytecode: */, aMethodObj))); +} + + +/* The module with the given name was just unloaded. + Make sure we have no dangling references. */ + + /* StackInterpreter>>#moduleUnloaded: */ +EXPORT(void) +moduleUnloaded(char *aModuleName) +{ + if (!(strcmp(aModuleName, "SurfacePlugin"))) { + /* Surface plugin went away. Should never happen. But then, who knows */ + showSurfaceFn = 0; + } +} + + +/* Brain-damaged nameOfClass: for C VM. Does *not* answer Foo class for + metaclasses. Use e.g. classIsMeta: to avoid being fooled. */ + + /* StackInterpreter>>#nameOfClass: */ +static NoDbgRegParms char * +nameOfClass(sqInt classOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt maybeNameOop; + sqInt maybeThisClassOop; + usqInt numSlots; + usqInt numSlotsUsqInt; + + /* begin numSlotsOf: */ + assert((classIndexOf(classOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(classOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (numSlots == GIV(metaclassNumSlots)) { + maybeThisClassOop = longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord())))))); + if (addressCouldBeClassObj(maybeThisClassOop)) { + return nameOfClass(longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord()))))))); + } + return "bad class"; + } + if (!((numSlots > GIV(classNameIndex)) + && (((maybeNameOop = longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(classNameIndex)) << (shiftForWord()))))))), + /* isBytes: */ + ((!(maybeNameOop & (tagMask())))) + && (((byteAt((void *)(maybeNameOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))))) { + return "bad class"; + } + return firstIndexableField(maybeNameOop); +} + + +/* Brain-damaged nameOfClass: for C VM. Does *not* answer Foo class for + metaclasses. Use e.g. classIsMeta: to avoid being fooled. */ + + /* StackInterpreter>>#nameOfClass:lengthInto: */ +static NoDbgRegParms char * +nameOfClasslengthInto(sqInt classOop, sqInt *lengthPtr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt maybeNameOop; + sqInt maybeThisClassOop; + usqInt numSlots; + usqInt numSlotsUsqInt; + + /* begin numSlotsOf: */ + assert((classIndexOf(classOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(classOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (numSlots == GIV(metaclassNumSlots)) { + maybeThisClassOop = longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord())))))); + if (addressCouldBeClassObj(maybeThisClassOop)) { + return nameOfClasslengthInto(longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord())))))), lengthPtr); + } + lengthPtr[0] = 9; + return "bad class"; + } + if ((numSlots > GIV(classNameIndex)) + && (((maybeNameOop = longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(classNameIndex)) << (shiftForWord()))))))), + /* isBytes: */ + ((!(maybeNameOop & (tagMask())))) + && (((byteAt((void *)(maybeNameOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))))) { + lengthPtr[0] = (numBytesOfBytes(maybeNameOop)); + return firstIndexableField(maybeNameOop); + } + lengthPtr[0] = 9; + return "bad class"; +} + + +/* Answer true if there are no marked contexts on thePage. */ + + /* StackInterpreter>>#noMarkedContextsOnPage: */ +static NoDbgRegParms sqInt +noMarkedContextsOnPage(StackPage *thePage) +{ + char *theFP; + + assert(!(isFree(thePage))); + theFP = (thePage->headFP); + do { + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + if ((byteAt((void *)((longAt(theFP + FoxThisContext)) + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + return 0; + } + } + } while(((theFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0); + return 1; +} + + +/* Answer the number of effective pointer fields in the given context. */ + + /* StackInterpreter>>#numSlotsOfMarriedContext: */ +static NoDbgRegParms sqInt +numSlotsOfMarriedContext(sqInt aContext) +{ + sqInt contextSize; + + contextSize = stackPointerIndexForFrame(frameOfMarriedContext(aContext)); + assert((CtxtTempFrameStart + contextSize) <= (numSlotsOf(aContext))); + return CtxtTempFrameStart + contextSize; +} + + /* StackInterpreter>>#numStkPages */ +static sqInt +numStkPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(numStackPages); +} + + /* StackInterpreter>>#object:equalsString: */ +static NoDbgRegParms sqInt +objectequalsString(sqInt anOop, char *aCString) +{ + sqInt len; + + len = strlen(aCString); + return /* object:equalsString:ofSize: */ + (/* isBytes: */ + ((!(anOop & (tagMask())))) + && (((byteAt((void *)(anOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((!(((byteAt((void *)(anOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((numBytesOfBytes(anOop)) == len) + && ((strncmp(aCString, firstIndexableField(anOop), len)) == 0))); +} + + +/* This is the entry-point for plugins and primitives that wish to reacquire + the VM after having + released it via disownVM or callbacks that want to acquire it without + knowing their ownership + status. While this exists for the threaded FFI VM we use it to reset + newMethod and the + argumentCount after a callback. + + Answer 0 if the current thread is known to the VM (and on return owns the + VM). Answer 1 if the current thread is unknown to the VM and takes + ownership. Answer -1 if the current thread is unknown to the VM and fails + to take ownership. */ + + /* StackInterpreter>>#ownVM: */ +sqInt +ownVM(sqInt disownResult) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert((isOopCompiledMethod(GIV(newMethod))) + && ((argumentCountOf(GIV(newMethod))) == GIV(argumentCount))); + + /* From callbacks (see thunkEntry), we don't know our ownerhip. Stash newMethod + and argumentCount on the stack. */ + if (!disownResult) { + assert(GIV(primFailCode) == 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),(((usqInt)GIV(argumentCount) << 3) | 1)); + return 1; + } + if (((disownResult & DisownVMForFFICallFlag) != 0)) { + GIV(ffiCalloutVMHandle) = 0; + } + return 0; +} + + /* StackInterpreter>>#pcPreviousTo:inSqueakV3PlusClosuresOrSistaV1Method: */ +usqInt +pcPreviousToinSqueakV3PlusClosuresOrSistaV1Method(sqInt theIP, sqInt aMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt byte; + static unsigned char extensionSizes[] = { + 2, 2, 2, 2, 3, 2, 2, 1, 1, 1, 2, 3, 3, 3, 3, 4 + }; + sqInt header; + sqInt methodHeader; + usqInt previousPC; + usqInt thisIP; + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethod)); + header = longAt((void *)((aMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + /* begin pcPreviousTo:inSistaV1Method: */ + thisIP = ((((literalCountOf(aMethod)) + LiteralStart) * BytesPerOop) + aMethod) + BaseHeaderSize; + assert(oopisLessThan(thisIP, theIP)); + while (oopisLessThan(thisIP, theIP)) { + previousPC = thisIP; + do { + byte = byteAt((void *)(thisIP)); + thisIP += /* sizeOfSistaV1Bytecode: */ + (byte < 224 + ? 1 + : (byte < 0xF8 + ? 2 + : 3)); + } while(((byte >= 224) && (byte <= 225))); + } + return previousPC; + } + + /* begin pcPreviousTo:inSqueakV3PlusClosuresMethod: */ + thisIP = ((((literalCountOf(aMethod)) + LiteralStart) * BytesPerOop) + aMethod) + BaseHeaderSize; + assert(oopisLessThan(thisIP, theIP)); + while (oopisLessThan(thisIP, theIP)) { + previousPC = thisIP; + byte = byteAt((void *)(thisIP)); + thisIP += (byte <= 125 + ? 1 + : (byte >= 176 + ? 1 + : (byte >= 160 + ? 2 + : (byte >= 144 + ? 1 + : (byte >= 128 + ? extensionSizes[byte - 128] + : (assertf("illegal bytecode in sizeOfSqueakV3Bytecode:"), + 1)))))); + } + return previousPC; +} + + /* StackInterpreter>>#penultimateLiteralOf: */ +static NoDbgRegParms sqInt +penultimateLiteralOf(sqInt aMethodOop) +{ + sqInt offset; + + assert(isOopCompiledMethod(aMethodOop)); + offset = (literalCountOf(aMethodOop)) - 2; + + /* begin literal:ofMethod: */ + return longAt((void *)((aMethodOop + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#popStack */ +sqInt +popStack(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt top; + + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + return top; +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#pop: */ +sqInt +pop(sqInt nItems) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(stackPointer) += nItems * BytesPerWord; + return null; +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#pop:thenPush: */ +void +popthenPush(sqInt nItems, sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + longAtput((sp = GIV(stackPointer) + ((nItems - 1) * BytesPerWord)),oop); + GIV(stackPointer) = sp; +} + + +/* Update the displayBits after a GC may have moved it. + Answer if the displayBits appear valid. The wrinkle here is that the + displayBits could be a surface handle. */ + + /* StackInterpreter>>#postGCUpdateDisplayBits */ +static sqInt +postGCUpdateDisplayBits(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void *bitsNow; + sqInt bitsOop; + sqInt displayObj; + + displayObj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheDisplay) << (shiftForWord())))))); + if (!((/* isPointers: */ + ((!(displayObj & (tagMask())))) + && (((byteAt((void *)(displayObj + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((lengthOf(displayObj)) >= 4))) { + return 0; + } + + /* begin followOopField:ofObject: */ + bitsOop = longAt((void *)((displayObj + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isOopForwarded: */ + ((!(bitsOop & (tagMask())))) + && ((!((longAt((void *)(bitsOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + bitsOop = fixFollowedFieldofObjectwithInitialValue(0, displayObj, bitsOop); + } + if ((bitsOop == GIV(nilObj)) + || ((((bitsOop) & 7) == 1))) { + return 1; + } + + /* it ain't yet set + It's a surface; our work here is done... */ + assert((addressCouldBeObj(bitsOop)) + && (isWordsOrBytes(bitsOop))); + if (!((byteAt((void *)(bitsOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift())))) { + if (GIV(stackPage)) { + pinObject(bitsOop); + + /* begin followOopField:ofObject: */ + bitsOop = longAt((void *)((displayObj + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isOopForwarded: */ + ((!(bitsOop & (tagMask())))) + && ((!((longAt((void *)(bitsOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + bitsOop = fixFollowedFieldofObjectwithInitialValue(0, displayObj, bitsOop); + } + } + + /* If stackPage is nil we're snapshotting and now is not the time to pin. */ + bitsNow = firstIndexableField(bitsOop); + if (displayBits != bitsNow) { + displayBits = bitsNow; + ioNoteDisplayChangedwidthheightdepth(displayBits, displayWidth, displayHeight, displayDepth); + } + } + return 1; +} + + +/* With 0 args answers whether ioProcessEvents is enabled and being called. + With 1 arg expects a boolean which will enable ioProcessEvents if true and + disable it if false, answering its previous state. */ + + /* StackInterpreter>>#primitiveEventProcessingControl */ +EXPORT(void) +primitiveEventProcessingControl(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int enabled; + char *sp; + + enabled = inIOProcessEvents >= 0; + if (!GIV(argumentCount)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer)),/* booleanObjectOf: */ + (enabled + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return; + } + if (GIV(argumentCount) == 1) { + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + if (inIOProcessEvents < 0) { + inIOProcessEvents = 0; + } + } + else { + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + inIOProcessEvents = -1; + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (enabled + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; +} + + +/* Set PrimErrFFIException primitive failure and associated exceptionCode + (a.k.a. + secondaryErrorCode) and exceptionPC. Under control of the + ffiExceptionResponse flag, + if in a primitive with an error code and ffiCalloutVMHandle indicates + we're in an FFI call, + then fail the primitive. + ffiExceptionResponse < 0 never fail (i.e. always crash) + ffiExceptionResponse = 0 fail if method has a primitive error code + (default) ffiExceptionResponse > 0 always fail */ + + /* StackInterpreter>>#primitiveFailForFFIException:at: */ +void +primitiveFailForFFIExceptionat(usqLong exceptionCode, usqInt pc) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cogMethod; + usqInt i; + usqInt initialIP; + sqInt methodHeader; + usqInt numArgs; + usqInt numTemps; + sqInt object; + sqInt rcvr; + + if ((GIV(ffiCalloutVMHandle) == 0) + || (ffiExceptionResponse < 0)) { + return; + } + + /* i.e. not in an FFI call + i.e. never fail */ + GIV(secondaryErrorCode) = ((sqLong) exceptionCode); + GIV(exceptionPC) = pc; + GIV(primFailCode) = PrimErrFFIException; + if ((ffiExceptionResponse > 0) + || ((/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (methodUsesPrimitiveErrorCode(GIV(newMethod))))) { + ownVM(GIV(ffiCalloutVMHandle)); + + /* begin activateFailingPrimitiveMethod */ + assert(GIV(primFailCode) != 0); + assert(addressCouldBeObj(GIV(newMethod))); + assert(isCompiledMethod(GIV(newMethod))); + assert((primitiveIndexOf(GIV(newMethod))) != 0); + + /* begin justActivateNewMethod: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + cogMethod = null; + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + methodHeader = (cogMethod->methodHeader); + cogMethod = null; + } + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeader)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(addressCouldBeOop(rcvr)); + assert(!(isOopForwarded(rcvr))); + if (cogMethod) { + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + } + GIV(instructionPointer) = ceReturnToInterpreterPC(); + } + cogMethod = null; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + initialIP = ((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize; + if (cogMethod) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)cogMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + GIV(instructionPointer) = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + GIV(instructionPointer) = initialIP - 1; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + if (!cogMethod) { + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + } + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeader); + } + } + ceInvokeInterpret(); + + /* NOTREACHED */ + } +} + + /* StackInterpreter>>#primitiveFloatEqual:toArg: */ +static NoDbgRegParms int +primitiveFloatEqualtoArg(sqInt rcvrOop, sqInt argOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + double rcvr; + double result; + sqInt tagBits; + double value; + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l2: + if (rcvr == arg) { + if ((((argOop) & 7) == 1)) { + return (((sqInt)rcvr)) == ((argOop >> 3)); + } + + /* Resolve case of ambiguity so as to have comparison of exact values */ + if ((((rcvrOop) & 7) == 1)) { + /* Same when used from bytecodePrim... + note that rcvr and arg cannot be both integer (case is already handled) */ + return ((rcvrOop >> 3)) == (((sqInt)arg)); + } + } + return rcvr == arg; +} + + /* StackInterpreter>>#primitiveFloatGreaterOrEqual:toArg: */ +static NoDbgRegParms int +primitiveFloatGreaterOrEqualtoArg(sqInt rcvrOop, sqInt argOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + double rcvr; + double result; + sqInt tagBits; + double value; + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l2: + if (rcvr == arg) { + if ((((argOop) & 7) == 1)) { + return (((sqInt)rcvr)) >= ((argOop >> 3)); + } + + /* Resolve case of ambiguity so as to have comparison of exact values */ + if ((((rcvrOop) & 7) == 1)) { + /* Same when used from bytecodePrim... + note that rcvr and arg cannot be both integer (case is already handled) */ + return ((rcvrOop >> 3)) >= (((sqInt)arg)); + } + } + return rcvr >= arg; +} + + /* StackInterpreter>>#primitiveFloatGreater:thanArg: */ +static NoDbgRegParms int +primitiveFloatGreaterthanArg(sqInt rcvrOop, sqInt argOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + double rcvr; + double result; + sqInt tagBits; + double value; + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l2: + if (rcvr == arg) { + if ((((argOop) & 7) == 1)) { + return (((sqInt)rcvr)) > ((argOop >> 3)); + } + + /* Resolve case of ambiguity so as to have comparison of exact values */ + if ((((rcvrOop) & 7) == 1)) { + /* Same when used from bytecodePrim... + note that rcvr and arg cannot be both integer (case is already handled) */ + return ((rcvrOop >> 3)) > (((sqInt)arg)); + } + } + return rcvr > arg; +} + + /* StackInterpreter>>#primitiveFloatLessOrEqual:toArg: */ +static NoDbgRegParms int +primitiveFloatLessOrEqualtoArg(sqInt rcvrOop, sqInt argOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + double rcvr; + double result; + sqInt tagBits; + double value; + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l2: + if (rcvr == arg) { + if ((((argOop) & 7) == 1)) { + return (((sqInt)rcvr)) <= ((argOop >> 3)); + } + + /* Resolve case of ambiguity so as to have comparison of exact values */ + if ((((rcvrOop) & 7) == 1)) { + /* Same when used from bytecodePrim... + note that rcvr and arg cannot be both integer (case is already handled) */ + return ((rcvrOop >> 3)) <= (((sqInt)arg)); + } + } + return rcvr <= arg; +} + + /* StackInterpreter>>#primitiveFloatLess:thanArg: */ +static NoDbgRegParms int +primitiveFloatLessthanArg(sqInt rcvrOop, sqInt argOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + double rcvr; + double result; + sqInt tagBits; + double value; + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l2: + if (rcvr == arg) { + if ((((argOop) & 7) == 1)) { + return (((sqInt)rcvr)) < ((argOop >> 3)); + } + + /* Resolve case of ambiguity so as to have comparison of exact values */ + if ((((rcvrOop) & 7) == 1)) { + /* Same when used from bytecodePrim... + note that rcvr and arg cannot be both integer (case is already handled) */ + return ((rcvrOop >> 3)) < (((sqInt)arg)); + } + } + return rcvr < arg; +} + + +/* Note: With the Squeak V3 format we now have 10 bits of primitive index, + but they are + in two places for temporary backward compatibility. The time to unpack is + negligible, since the derived primitive function pointer is stored in the + method cache. With the + Spur format we assume a 3-byte CallPrimitive with a little-endian 16-bit + primitive index. */ + + /* StackInterpreter>>#primitiveIndexOfMethod:header: */ +sqInt +primitiveIndexOfMethodheader(sqInt theMethod, sqInt methodHeader) +{ + usqInt firstBytecode; + + return (((methodHeader & AlternateHeaderHasPrimFlag) != 0) + ? ((firstBytecode = (theMethod + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize), + (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8)))) + : 0); +} + + /* StackInterpreter>>#primitiveIndexOf: */ +sqInt +primitiveIndexOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin primitiveIndexOfMethod:header: */ + return (((methodHeader & AlternateHeaderHasPrimFlag) != 0) + ? ((firstBytecode = (methodPointer + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize), + (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8)))) + : 0); +} + + +/* Return the method an external primitive was defined in */ + + /* StackInterpreter>>#primitiveMethod */ +usqInt +primitiveMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(newMethod); +} + + /* StackInterpreter>>#printActivationNameForSelector:startClass: */ +static NoDbgRegParms void +printActivationNameForSelectorstartClass(sqInt aSelector, sqInt startClass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classDict; + usqInt classDictSize; + sqInt currClass; + sqInt i; + sqInt methClass; + usqInt numSlots; + sqInt objOop; + + if (addressCouldBeObj(startClass)) { + /* begin findClassForSelector:lookupClass:do: */ + currClass = startClass; + do { + classDict = longAt((void *)((currClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + + /* begin numSlotsOf: */ + assert((classIndexOf(classDict)) > (isForwardedObjectClassIndexPun())); + classDictSize = (((numSlots = byteAt((void *)(classDict + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classDict - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + i = SelectorStart; + while (i < classDictSize) { + if (aSelector == (longAt((void *)((classDict + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + methClass = currClass; + goto l1; + } + i += 1; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((currClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currClass, objOop); + } + currClass = objOop; + } while(!(currClass == GIV(nilObj))); + methClass = null; + /* end findClassForSelector:lookupClass:do: */ +l1: + if ((!methClass) + || (startClass == methClass)) { + printNameOfClasscount(methClass, 5); + printChar('>'); + if (!methClass) { + printStringOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord()))))))); + print(" "); + } + } + else { + printNameOfClasscount(startClass, 5); + printChar('('); + printNameOfClasscount(methClass, 5); + printChar(')'); + printChar('>'); + } + } + else { + print("INVALID CLASS"); + } + if (/* addressCouldBeOop: */ + (((aSelector & (tagMask())) != 0)) + || (addressCouldBeObj(aSelector))) { + if (/* isBytes: */ + ((!(aSelector & (tagMask())))) + && (((byteAt((void *)(aSelector + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) { + printStringOf(aSelector); + } + else { + /* begin printOopShort: */ + printOopShortInner(aSelector); + fflush(GIV(transcript)); + } + } + else { + print("INVALID SELECTOR"); + } +} + + /* StackInterpreter>>#printActivationNameFor:receiver:isBlock:firstTemporary: */ +static NoDbgRegParms void +printActivationNameForreceiverisBlockfirstTemporary(sqInt aMethod, sqInt anObject, sqInt isBlock, sqInt maybeMessage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classObj; + sqInt homeMethod; + sqInt methClass; + sqInt methodSel; + sqInt tagBits; + + if (isBlock) { + print("[] in "); + homeMethod = homeMethodOf(aMethod); + } + else { + homeMethod = aMethod; + } + methClass = findClassOfMethodforReceiver(homeMethod, anObject); + methodSel = findSelectorOfMethod(homeMethod); + if ((/* addressCouldBeOop: */ + (((anObject & (tagMask())) != 0)) + || (addressCouldBeObj(anObject))) + && ((!(/* isOopForwarded: */ + ((!(anObject & (tagMask())))) + && ((!((longAt((void *)(anObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) + && (addressCouldBeClassObj((classObj = /* fetchClassOf: */ + ((tagBits = anObject & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(anObject))))))) { + printNameOfClasscount(classObj, 5); + if (!((classObj == methClass) + || ((!methClass) + || (methClass == GIV(nilObj))))) { + printChar('('); + printNameOfClasscount(methClass, 5); + printChar(')'); + } + } + else { + print("INVALID RECEIVER"); + } + printChar('>'); + if (/* addressCouldBeOop: */ + (((methodSel & (tagMask())) != 0)) + || (addressCouldBeObj(methodSel))) { + if (methodSel == GIV(nilObj)) { + print("(nil)"); + } + else { + if (((methodSel & (tagMask())) != 0)) { + printChar('('); + printHex(methodSel); + printChar(')'); + } + else { + printStringOf(methodSel); + } + } + } + else { + print("INVALID SELECTOR = "); + printHex(methodSel); + } + if ((methodSel == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))))) + && ((addressCouldBeObj(maybeMessage)) + && ((fetchClassOfNonImm(maybeMessage)) == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassMessage) << (shiftForWord())))))))))) { + /* print arg message selector */ + methodSel = longAt((void *)((maybeMessage + BaseHeaderSize) + ((((usqInt)(MessageSelectorIndex) << (shiftForWord())))))); + + /* begin space */ + printChar(' '); + printStringOf(methodSel); + } +} + + +/* Print all the stacks of all running processes, including those that are + currently suspended. + */ +/* useful for VM debugging */ + + /* StackInterpreter>>#printAllStacks */ +void +printAllStacks(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classPointer; + sqInt followingWord; + usqInt followingWordAddress; + sqInt linkedListClass; + sqInt minProcessInstSize; + sqInt myList; + sqInt myListClass; + usqInt numSlots; + sqInt objOop; + sqInt objSqInt; + usqInt p; + sqInt prevObj; + sqInt prevPrevObj; + sqInt pri; + sqInt proc; + sqInt processClass; + sqInt processList; + sqInt schedLists; + sqInt startObject; + sqInt tagBits; + + /* may not be an instance of process. may in exceptional circumstances be nilObject */ + proc = activeProcess(); + printNameOfClasscount(/* fetchClassOf: */ + ((tagBits = proc & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(proc)), 5); + + /* begin space */ + printChar(' '); + printHex(proc); + print(" priority "); + printNum(quickFetchIntegerofObject(PriorityIndex, proc)); + cr(); + if (GIV(framePointer)) { + printCallStack(); + } + else { + printProcessStack(proc); + } + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + schedLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + linkedListClass = null; + + /* then the runnable processes */ + p = (GIV(highestRunnableProcessPriority) + ? GIV(highestRunnableProcessPriority) + : numSlotsOf(schedLists)); + for (pri = (p - 1); pri >= 0; pri += -1) { + processList = longAt((void *)((schedLists + BaseHeaderSize) + ((((usqInt)(pri) << (shiftForWord())))))); + if (!(isEmptyList(processList))) { + if (proc == GIV(nilObj)) { + proc = longAt((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + } + cr(); + print("processes at priority "); + printNum(pri + 1); + printProcsOnList(processList); + } + if (!linkedListClass) { + linkedListClass = fetchClassOfNonImm(processList); + } + } + if (!linkedListClass) { + classPointer = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord())))))); + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + linkedListClass = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(linkedListClass)); + if ((!((longAt((void *)(linkedListClass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + linkedListClass = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, linkedListClass); + } + } + if (proc == GIV(nilObj)) { + cr(); + print("Cannot find a runnable process. Cannot therefore determine class Process. Cannot therefore print suspended processes"); + return; + } + cr(); + print("suspended processes"); + + /* Find the root of the Process hierarchy. It is the class, or superclass, + of a process, that has inst size at least large enough to include myList */ + processClass = (!(proc == GIV(nilObj)) + ? /* fetchClassOf: */ + ((tagBits = proc & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(proc)) + : 0); + minProcessInstSize = MyListIndex + 1; + while ((instanceSizeOf(superclassOf(processClass))) >= minProcessInstSize) { + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((processClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, processClass, objOop); + } + processClass = objOop; + } + minProcessInstSize = instanceSizeOf(processClass); + + /* look for all subInstances of process that have a context as a suspendedContext and are on a list other than a LinkedList */ + + /* begin allObjectsDoSafely: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + if (((longAt((void *)(objSqInt))) & (classIndexMask())) > (lastClassIndexPun())) { + if ((((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + && (((numSlotsOf(objSqInt)) >= minProcessInstSize) + && ((isContext(longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))))) + && (isKindOfClass(objSqInt, processClass))))) { + /* Is the process waiting on some delaying list? This will be a subclass of LinkedList. + If so, assume it is blocked on the list. */ + myList = longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord())))))); + if ((myList != GIV(nilObj)) + && ((((myListClass = fetchClassOfNonImm(myList))) != linkedListClass) + && (isKindOfClass(myList, linkedListClass)))) { + printProcessStack(objSqInt); + } + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + +/* Print all the stacks of all running processes, including those that are + currently suspended. + */ +/* useful for VM debugging */ + + /* StackInterpreter>>#printAllStacksOn: */ +void +printAllStacksOn(FILE *aStdioStream) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + FILE *savedTranscript; + + /* begin withRedirectedOutputTo:do: */ + savedTranscript = GIV(transcript); + GIV(transcript) = (aStdioStream + ? aStdioStream + : stdout); + printAllStacks(); + GIV(transcript) = savedTranscript; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printCallStack */ +void +printCallStack(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *fp; + + if ((fp = GIV(framePointer))) { + printCallStackFP(fp); + } + else { + printCallStackOf(fetchPointerofObject(SuspendedContextIndex, activeProcess())); + } +} + + /* StackInterpreter>>#printCallStackFP: */ +static NoDbgRegParms sqInt +printCallStackFP(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt context; + + context = shortReversePrintFrameAndCallers(theFP); + while (!(context == GIV(nilObj))) { + if (((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (!(checkIsStillMarriedContextcurrentFP(context, GIV(framePointer)))) { + shortPrintContext(context); + return null; + } + context = shortReversePrintFrameAndCallers(frameOfMarriedContext(context)); + } + else { + context = printContextCallStackOf(context); + } + } + return 0; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printCallStackOf: */ +sqInt +printCallStackOf(sqInt aContextOrProcessOrFrame) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt context; + + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)aContextOrProcessOrFrame)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)aContextOrProcessOrFrame)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)aContextOrProcessOrFrame)) <= (((usqInt)GIV(pages)))))))) { + return printCallStackFP(((char *)aContextOrProcessOrFrame)); + } + if (aContextOrProcessOrFrame == (activeProcess())) { + return printCallStackOf(((sqInt)GIV(framePointer))); + } + if (couldBeProcess(aContextOrProcessOrFrame)) { + return printCallStackOf(longAt((void *)((aContextOrProcessOrFrame + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))))); + } + context = aContextOrProcessOrFrame; + while (!(context == GIV(nilObj))) { + if (((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (!(checkIsStillMarriedContextcurrentFP(context, GIV(framePointer)))) { + shortPrintContext(context); + return null; + } + context = shortReversePrintFrameAndCallers(frameOfMarriedContext(context)); + } + else { + context = printContextCallStackOf(context); + } + } + return 0; +} + + /* StackInterpreter>>#printCallStackOf:currentFP: */ +static NoDbgRegParms sqInt +printCallStackOfcurrentFP(sqInt aContext, char *currFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + sqInt senderOop; + char *theFP; + StackPage *thePage; + + ctxt = aContext; + while (!(ctxt == GIV(nilObj))) { + if (((((longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (checkIsStillMarriedContextcurrentFP(ctxt, currFP)) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (isFree(thePage)) { + fprintf(GIV(transcript), + "%p is on a free page?!\n", + theFP); + return null; + } + shortPrintFrameAndCallers(theFP); + theFP = (thePage->baseFP); + ctxt = frameCallerContext(theFP); + if ((!((longAt((void *)(ctxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + ctxt = followForwarded(ctxt); + } + } + else { + fprintf(GIV(transcript), + "widowed caller frame %p\n", + theFP); + return null; + } + } + else { + shortPrintContext(ctxt); + ctxt = longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + } + } + return 0; +} + + +/* Print the call stack on a specific output stream. */ +/* useful for VM debugging */ + + /* StackInterpreter>>#printCallStackOn: */ +void +printCallStackOn(FILE *aStdioStream) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + FILE *savedTranscript; + + /* begin withRedirectedOutputTo:do: */ + savedTranscript = GIV(transcript); + GIV(transcript) = (aStdioStream + ? aStdioStream + : stdout); + printCallStack(); + GIV(transcript) = savedTranscript; +} + + +/* Print the call stack of aContext until it links to a frame. */ + + /* StackInterpreter>>#printContextCallStackOf: */ +static NoDbgRegParms sqInt +printContextCallStackOf(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + + ctxt = aContext; + while (!((ctxt == GIV(nilObj)) + || (((((longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)))) { + shortPrintContext(ctxt); + ctxt = longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + } + return ctxt; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printContext: */ +void +printContext(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt ip; + sqInt meth; + sqInt sender; + sqInt sp; + + shortPrintContext(aContext); + sender = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + ip = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + if ((((sender) & 7) == 1)) { + if (checkIsStillMarriedContextcurrentFP(aContext, GIV(framePointer))) { + print("married (assuming framePointer valid)"); + cr(); + } + else { + print("widowed (assuming framePointer valid)"); + cr(); + } + print("sender "); + printNum(sender); + print(" ("); + printHexPtrnp( + (/* begin withoutSmallIntegerTags: */ + assert((((sender) & 7) == 1)), + ((char *)(sender - (smallIntegerTag()))))); + printChar(')'); + cr(); + print("ip "); + printNum(ip); + print(" ("); + printHexPtrnp( + (/* begin withoutSmallIntegerTags: */ + assert((((ip) & 7) == 1)), + ((char *)(ip - (smallIntegerTag()))))); + printChar(')'); + cr(); + } + else { + print("sender "); + shortPrintOop(sender); + print("ip "); + if (ip == GIV(nilObj)) { + shortPrintOop(ip); + } + else { + printNum(ip); + print(" ("); + printNum((ip >> 3)); + + /* begin space */ + printChar(' '); + printHex((ip >> 3)); + printChar(')'); + cr(); + } + } + sp = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + print("sp "); + printNum((sp >> 3)); + print(" ("); + printHex(sp); + printChar(')'); + cr(); + print("method "); + + /* begin printMethodFieldForPrintContext: */ + meth = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + fprintf(GIV(transcript), + "%p: ", + ((void *)meth)); + printOopShortInner(meth); + if (methodHasCogMethod(meth)) { + fprintf(GIV(transcript), + " (%p)", + cogMethodOf(meth)); + } + cr(); + print("closure "); + shortPrintOop(longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord()))))))); + print("receiver "); + shortPrintOop(longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord()))))))); + sp = (sp >> 3); + sp = ((sp < ((lengthOf(aContext)) - ReceiverIndex)) ? sp : ((lengthOf(aContext)) - ReceiverIndex)); + for (i = 1; i <= sp; i += 1) { + print(" "); + printNum(i); + + /* begin space */ + printChar(' '); + shortPrintOop(longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))))); + } +} + + /* StackInterpreter>>#printDecodeMethodHeaderOop: */ +static NoDbgRegParms void +printDecodeMethodHeaderOop(sqInt methodHeaderOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin printOopShort: */ + printOopShortInner(methodHeaderOop); + fflush(GIV(transcript)); + if (((methodHeaderOop & AlternateHeaderHasPrimFlag) != 0)) { + print(" hasPrim"); + } + if (methodHeaderOop & LargeContextBit) { + print(" largeFrame"); + } + if (((methodHeaderOop >> 3)) < 0) { + print(" altSet"); + } + print(" nLits "); + printNum( + (/* begin literalCountOfMethodHeader: */ + assert((((methodHeaderOop) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeaderOop >> 3)) & AlternateHeaderNumLiteralsMask)); + print(" nArgs "); + printNum((((usqInt)(methodHeaderOop)) >> MethodHeaderArgCountShift) & 15); + print(" nTemps "); + printNum((((usqInt)(methodHeaderOop)) >> MethodHeaderTempCountShift) & 0x3F); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printExternalHeadFrame */ +void +printExternalHeadFrame(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + printFrameWithSP(GIV(framePointer), GIV(stackPointer)); +} + + /* StackInterpreter>>#printFrameAndCallers:SP:short: */ +static NoDbgRegParms sqInt +printFrameAndCallersSPshort(char *theFP, char *theSP, sqInt printShort) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!(/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + return null; + } + if (longAt(theFP + FoxSavedFP)) { + printFrameAndCallersSPshort(((char *)(longAt(theFP + FoxSavedFP))), frameCallerSP(theFP), printShort); + } + if (printShort) { + shortPrintFrame(theFP); + return null; + } + cr(); + printFrameWithSP(theFP, theSP); + return 0; +} + + /* StackInterpreter>>#printFrameOop:at: */ +static NoDbgRegParms void +printFrameOopat(char *name, char *address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt it; + + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%12s: %16p\t=" /* pst: */, + name, + ((void *)it)); + printOopShortInner(it); + cr(); +} + + /* StackInterpreter>>#printFrameOop:index:at: */ +static NoDbgRegParms void +printFrameOopindexat(char *name, sqInt idx, char *address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt it; + + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%s%10s%" PRIdSQINT ": %16p\t" /* pst: */, + (idx > 9 + ? "" + : " "), + name, + idx, + ((void *)it)); + printOopShortInner(it); + cr(); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printFramesInPage: */ +void +printFramesInPage(StackPage *thePage) +{ + printFrameAndCallersSPshort((thePage->headFP), (thePage->headSP), 0); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printFramesOnStackPageListInUse */ +void +printFramesOnStackPageListInUse(void) +{ + StackPage *page; + + page = GIV(mostRecentlyUsedPage); + do { + if (!(isFree(page))) { + print("page "); + printHexPtrnp(page); + cr(); + printFramesInPage(page); + cr(); + } + } while(((page = (page->prevPage))) != (GIV(mostRecentlyUsedPage))); +} + + /* StackInterpreter>>#printFrameThing:andFrame:at: */ +static NoDbgRegParms void +printFrameThingandFrameat(char *name, char *theFP, char *address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt it; + + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%12s: %16p" /* pst: */, + name, + ((void *)it)); + if (it) { + if (it == GIV(nilObj)) { + fprintf(GIV(transcript), "=nil"); + } + else { + fprintf(GIV(transcript), + "=%" PRIdSQINT "", + it); + } + } + fprintf(GIV(transcript), + " frame: %p\n", + theFP); +} + + /* StackInterpreter>>#printFrameThing:at:extraString: */ +static NoDbgRegParms void +printFrameThingatextraString(char *name, char *address, char *extraStringOrNil) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt it; + + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%12s: %16p" /* pst: */, + name, + ((void *)it)); + framePrintDescription(it); + if (extraStringOrNil) { + fprintf(GIV(transcript), + "%s", + extraStringOrNil); + } + cr(); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printFrame: */ +sqInt +printFrame(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *aFrame; + char *frameAbove; + char *prevFrame; + char *startFrame; + StackPage *thePage; + char *theSP; + + if (!(/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + if ((addressCouldBeObj(((sqInt)theFP))) + && ((isInMemory(((sqInt)theFP))) + && ((((longAt((void *)(((sqInt)theFP)))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (checkIsStillMarriedContextcurrentFP(((sqInt)theFP), GIV(framePointer)))))) { + return printFrame(frameOfMarriedContext(((sqInt)theFP))); + } + fprintf(GIV(transcript), + "%p is not in the stack zone?!\n", + theFP); + return null; + } + frameAbove = null; + if (theFP == GIV(framePointer)) { + theSP = GIV(stackPointer); + } + else { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (isFree(thePage)) { + fprintf(GIV(transcript), + "%p is on a free page?!\n", + theFP); + return null; + } + if ((thePage != GIV(stackPage)) + && (theFP == ((thePage->headFP)))) { + theSP = (thePage->headSP); + } + else { + startFrame = ((thePage == GIV(stackPage)) + && (((GIV(framePointer) >= ((thePage->realStackLimit))) && (GIV(framePointer) <= ((thePage->baseAddress))))) + ? GIV(framePointer) + : (thePage->headFP)); + + /* begin safeFindFrameAbove:on:startingFrom: */ + prevFrame = ((char *) 0); + assert(!(isFree(thePage))); + if (startFrame == theFP) { + frameAbove = null; + goto l1; + } + aFrame = startFrame; + while (1) { + prevFrame = aFrame; + aFrame = ((char *)(longAt(aFrame + FoxSavedFP))); + if (!(aFrame != 0)) break; + if (theFP == aFrame) { + frameAbove = prevFrame; + goto l1; + } + } + frameAbove = null; + /* end safeFindFrameAbove:on:startingFrom: */ +l1: + theSP = (frameAbove + ? frameCallerSP(frameAbove) + : ((char *) null)); + } + } + if (!theSP) { + fprintf(GIV(transcript), "could not find sp; using bogus value\n"); + theSP = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + } + printFrameWithSP(theFP, theSP); + if (frameAbove) { + /* begin printFrameThing:at: */ + printFrameThingatextraString("frame pc", frameAbove + FoxCallerSavedIP, ((char *)null)); + } + return 0; +} + + +/* Print p in hex, unpadded, in the form 0x1234 (C)/16r1234 (here) */ + + /* StackInterpreter>>#printHexPtrnp: */ +static NoDbgRegParms sqInt +printHexPtrnp(void *p) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return fprintf(GIV(transcript), + "%p", + p); +} + + +/* Print n in hex, in the form ' 0x1234', padded to a width of 10 characters + in 32-bits ('0x' + 8 nibbles) or 18 characters in 64-bits ('0x' + 16 + nibbles). In the simulator use 16r as the prefix, padding to 11 characters + in 32-bits + or 19 characters in 64-bits. */ + + /* StackInterpreter>>#printHex: */ +void +printHex(usqInt n) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fprintf(GIV(transcript), + "%16p" /* pst: */, + ((void *)n)); +} + + +/* Print all methods whose penultimate literal is either selector, + or an object whose first inst var is the method and whose + second is selector (e.g. an AdditionalMethodState). */ +/* useful for VM debugging */ + + /* StackInterpreter>>#printLikelyImplementorsOfSelector: */ +void +printLikelyImplementorsOfSelector(sqInt selector) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt methodClassAssociation; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && ((maybeSelectorOfMethod(objSqInt)) == selector)) { + /* try and print the key of the method class association (the name of the implementing class) */ + methodClassAssociation = methodClassAssociationOf(objSqInt); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)objSqInt)); + + /* begin space */ + printChar(' '); + printOopShortInner(((/* isPointers: */ + ((!(methodClassAssociation & (tagMask())))) + && (((byteAt((void *)(methodClassAssociation + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOf(methodClassAssociation)) >= 2) + ? longAt((void *)((methodClassAssociation + BaseHeaderSize) + (0U << (shiftForWord())))) + : methodClassAssociation)); + cr(); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + /* StackInterpreter>>#printLogEntryAt: */ +static NoDbgRegParms void +printLogEntryAt(sqInt i) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classMethodOrProcess; + sqInt selectorOrExtra; + sqInt source; + sqInt traceType; + sqInt traceTypeAndSource; + + traceTypeAndSource = GIV(traceLog)[i]; + traceType = ((usqInt)(traceTypeAndSource)) >> TraceTypeShift; + source = traceTypeAndSource & (((1U << TraceTypeShift)) - 1); + + /* a.k.a. thingOne */ + classMethodOrProcess = GIV(traceLog)[i + 1]; + + /* a.k.a. thingTwo */ + selectorOrExtra = GIV(traceLog)[i + 2]; + switch (traceType) { + case TraceSend: + /* begin printSendLogEntry:source:selector: */ + print((((source & CSFromMachineCode) != 0) + ? "m " + : "i ")); + printNameOfClasscount(classMethodOrProcess, 5); + print(">>"); + printStringOf(selectorOrExtra); + break; + case TraceStackOverflow: + print("stack overflow"); + break; + case TraceContextSwitch: + fprintf(GIV(transcript), + "context switch from %p to %p", + ((void *)classMethodOrProcess), + ((void *)selectorOrExtra)); + break; + case TraceBlockActivation: + fprintf(GIV(transcript), + " [] in %p", + ((void *)classMethodOrProcess)); + break; + case TraceBlockCreation: + fprintf(GIV(transcript), + "create [] %p", + ((void *)classMethodOrProcess)); + break; + case TraceIncrementalGC: + print("incrementalGC"); + break; + case TraceFullGC: + print("fullGC"); + break; + case TraceCodeCompaction: + print("compactCode"); + break; + case TraceVMCallback: + fprintf(GIV(transcript), + "callback %p", + ((void *)classMethodOrProcess)); + break; + case TraceVMCallbackReturn: + fprintf(GIV(transcript), + "callback return %p type %d", + ((void *)classMethodOrProcess), + ((int) source)); + break; + default: + print("UNKNOWN EVENT"); + } + if ((((traceType >= TraceStackOverflow) && (traceType <= TraceVMCallbackReturn))) + && (source > 1)) { + /* begin space */ + printChar(' '); + print(traceSources[((usqInt)(source)) >> 1]); + } + cr(); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printMethodCache */ +void +printMethodCache(void) +{ + printMethodCacheFor(-1); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printMethodDictionaryOf: */ +void +printMethodDictionaryOf(sqInt behavior) +{ + printMethodDictionary(longAt((void *)((behavior + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord()))))))); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printMethodDictionary: */ +void +printMethodDictionary(sqInt dictionary) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt meth; + sqInt methodArray; + sqInt selector; + sqInt toDoLimit; + + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + toDoLimit = (numSlotsOf(dictionary)) - 1; + for (index = SelectorStart; index <= toDoLimit; index += 1) { + selector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (selector != GIV(nilObj)) { + meth = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + printOopShortInner(selector); + print(" => "); + printOopShortInner(meth); + print(" ("); + printHex(selector); + print(" => "); + printHex(meth); + print(")\n"); + } + } +} + + +/* Details: The count argument is used to avoid a possible infinite recursion + if classOop is a corrupted object. + */ + + /* StackInterpreter>>#printNameOfClass:count: */ +static NoDbgRegParms void +printNameOfClasscount(sqInt classOop, sqInt cnt) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + + if (!GIV(classNameIndex)) { + print("??nil cnidx??"); + return; + } + if ((!classOop) + || ((classOop == 0) + || (cnt <= 0))) { + print("bad class"); + return; + } + numSlots = numSlotsOf(classOop); + if ((numSlots == GIV(metaclassNumSlots)) + && (GIV(metaclassNumSlots) > GIV(thisClassIndex))) { + printNameOfClasscount(longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord())))))), cnt - 1); + print(" class"); + } + else { + if (numSlots <= GIV(classNameIndex)) { + print("bad class"); + } + else { + printStringOf(longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(classNameIndex)) << (shiftForWord()))))))); + } + } +} + + /* StackInterpreter>>#printNum: */ +static NoDbgRegParms void +printNum(sqInt n) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)n)); +} + + /* StackInterpreter>>#printOopShortInner: */ +static NoDbgRegParms void +printOopShortInner(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + sqInt classLookupKey; + sqInt classOop; + double f; + char *name; + sqInt nameLen; + sqInt objOop; + double result; + sqInt tagBits; + sqInt target; + + if (((oop & (tagMask())) != 0)) { + if (((oop & (characterTag())) != 0)) { + fprintf(GIV(transcript), + "$%c(%" PRIxSQINT ")", + ((int) (((((usqInt)oop))) >> (numTagBits()))), + ((((usqInt)oop))) >> (numTagBits())); + return; + } + if ((((oop) & 7) == 1)) { + fprintf(GIV(transcript), + "%" PRIdSQINT "(0x%" PRIxSQINT ")", + (oop >> 3), + (oop >> 3)); + return; + } + if (((oop & (smallFloatTag())) != 0)) { + fprintf(GIV(transcript), + "%g(0x%" PRIxSQINT ")", + noFailFloatValueOf(oop), + oop); + return; + } + fprintf(GIV(transcript), + "unknown immediate %p", + ((void *)oop)); + return; + } + if (!(addressCouldBeObj(oop))) { + print((oop & 7 /* (allocationUnit - 1) */ + ? " is misaligned" + : whereIs(oop))); + return; + } + if (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + print(" is a free chunk"); + return; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + target = followForwarded(oop); + fprintf(GIV(transcript), + " is a forwarder to %p", + ((void *)target)); + return; + } + if (/* isFloatInstance: */ + ((tagBits = oop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(oop))) & (classIndexMask())) == ClassFloatCompactIndex)) { + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(oop)); + if (oop & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&result), (&bits), sizeof(result)); + } + else { + fetchFloatAtinto(oop + BaseHeaderSize, result); + } + f = result; + + /* begin printFloat: */ + fprintf(GIV(transcript), + "%g", + f); + return; + } + classOop = fetchClassOfNonImm(oop); + if (!(addressCouldBeObj(classOop))) { + print("a ??"); + return; + } + if ((numSlotsOf(classOop)) == GIV(metaclassNumSlots)) { + printNameOfClasscount(oop, 5); + return; + } + if (oop == GIV(nilObj)) { + print("nil"); + return; + } + if (oop == GIV(trueObj)) { + print("true"); + return; + } + if (oop == GIV(falseObj)) { + print("false"); + return; + } + nameLen = lengthOfNameOfClass(classOop); + if (!nameLen) { + print("a ??"); + return; + } + name = nameOfClass(classOop); + if ((/* isBytes: */ + ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && (nameLen >= 6)) { + if (!(strncmp((name + nameLen) - 6, "String", 6))) { + printChar('\''); + printStringOf(oop); + printChar('\''); + return; + } + + /* strncmp is weird */ + if (!(strncmp((name + nameLen) - 6, "Symbol", 6))) { + printChar('#'); + printStringOf(oop); + return; + } + } + if ((nameLen == 9) + && ((strncmp(name, "Character", 9)) == 0)) { + printChar('$'); + printChar(((longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord()))))) >> 3)); + return; + } + fprintf(GIV(transcript), + "a(n) %.*s", + ((int) nameLen), + name); + + /* Try to spot association-like things; they're all subclasses of LookupKey */ + if ((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + && (((instanceSizeOf(classOop)) == (ValueIndex + 1)) + && (isBytes(longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(KeyIndex) << (shiftForWord())))))))))) { + classLookupKey = fetchClassOfNonImm(splObj(SchedulerAssociation)); + while (1) { + if (classLookupKey == GIV(nilObj)) { + return; + } + if ((instanceSizeOf(classLookupKey)) == (KeyIndex + 1)) break; + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classLookupKey + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classLookupKey, objOop); + } + classLookupKey = objOop; + } + if (includesBehaviorThatOf(classOop, classLookupKey)) { + /* begin space */ + printChar(' '); + printOopShortInner(longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(KeyIndex) << (shiftForWord()))))))); + print(" -> "); + fprintf(GIV(transcript), + "%p", + ((void *)(longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))))))); + } + } +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printOop: */ +void +printOop(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt byte; + sqInt bytecodesPerLine; + char *className; + sqInt cls; + sqInt column; + sqInt fmt; + sqInt fmtSqInt; + sqInt index; + sqInt lastIndex; + sqInt length; + usqInt numSlots; + usqInt numSlotsUsqInt; + usqInt startIP; + sqInt tagBits; + + length = 0; + if (((oop & (tagMask())) != 0)) { + printImmediateObjecton(oop, GIV(transcript)); + return; + } + if (!(addressCouldBeObj(oop))) { + printCantBeObjecton(oop, GIV(transcript)); + return; + } + if (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + printFreeObjecton(oop, GIV(transcript)); + return; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + printForwarderon(oop, GIV(transcript)); + return; + } + if (!((cls = fetchClassOfNonImm(oop)))) { + fprintf(GIV(transcript), + "%p has a nil class!!\n", + ((void *)oop)); + return; + } + className = nameOfClasslengthInto(cls, (&length)); + fprintf(GIV(transcript), + "%p: a(n) %.*s", + ((void *)oop), + ((int) length), + className); + if (/* isFloatInstance: */ + ((tagBits = oop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(oop))) & (classIndexMask())) == ClassFloatCompactIndex)) { + fprintf(GIV(transcript), + "\n%g\n", + noFailFloatValueOf(oop)); + return; + } + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt > 5 /* lastPointerFormat */) { + fprintf(GIV(transcript), + " nbytes %" PRIdSQINT "", + numBytesOf(oop)); + } + cr(); + if (/* isPureBitsFormat: */ + (fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat()))) { + /* This will answer false if splObj: ClassAlien is nilObject */ + if (isKindOfClass(oop, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord())))))))) { + fprintf(GIV(transcript), + " datasize %" PRIdSQINT " %s @ %p\n", + longAt((void *)(oop + BaseHeaderSize)), + ((longAt((void *)(oop + BaseHeaderSize))) < 0 + ? "indirect" + : (longAt((void *)(oop + BaseHeaderSize)) + ? "direct" + : "pointer")), + startOfAlienData(oop)); + return; + } + if (isKindOfClass(oop, superclassOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord()))))))))) { + printStringDataOfon(oop, GIV(transcript)); + return; + } + printNonPointerDataOfon(oop, GIV(transcript)); + return; + } + startIP = (fmt >= (firstCompiledMethodFormat()) + ? (((literalCountOf(oop)) + LiteralStart) * BytesPerOop) / BytesPerWord + : numSlotsOf(oop)); + lastIndex = ((0x100 < startIP) ? 0x100 : startIP); + if (lastIndex > 0) { + for (index = 1; index <= lastIndex; index += 1) { + printHex(longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + + /* begin space */ + printChar(' '); + if (!(index % 5 /* elementsPerPrintOopLine */)) { + cr(); + } + } + if (lastIndex % 5 /* elementsPerPrintOopLine */) { + cr(); + } + } + if (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + startIP = (((literalCountOf(oop)) + LiteralStart) * BytesPerOop) + 1; + + /* begin lengthOf: */ + fmtSqInt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmtSqInt <= (ephemeronFormat())) { + lastIndex = numSlots; + goto l1; + } + if (fmtSqInt >= (firstByteFormat())) { + lastIndex = ((numSlots << (shiftForWord()))) - (fmtSqInt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmtSqInt >= (firstShortFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 1))) - (fmtSqInt & 3); + goto l1; + } + if (fmtSqInt >= (firstLongFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 2))) - (fmtSqInt & 1); + goto l1; + } + if (fmtSqInt == (sixtyFourBitIndexableFormat())) { + lastIndex = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + lastIndex = 0; + /* end lengthOf: */ +l1: + if ((lastIndex - startIP) > 0x100) { + lastIndex = startIP + 0x100; + } + bytecodesPerLine = 8; + column = 1; + for (index = startIP; index <= lastIndex; index += 1) { + if (column == 1) { + fprintf(GIV(transcript), + "%10p", + ((void *)(((oop + BaseHeaderSize) + index) - 1))); + } + byte = byteAt((void *)((oop + BaseHeaderSize) + (index - 1))); + fprintf(GIV(transcript), + " %02x/%-+3d", + ((int) byte), + ((int) byte)); + column += 1; + if (column > bytecodesPerLine) { + column = 1; + cr(); + } + } + if ((lengthOf(oop)) > lastIndex) { + print("..."); + } + if (!(column == 1)) { + cr(); + } + } + else { + if (startIP > 64) { + print("..."); + cr(); + } + } +} + + +/* print the entry and answer if it takes a parameter (as the following + entry) + */ + + /* StackInterpreter>>#printPrimLogEntryAt:hasParameter: */ +static NoDbgRegParms sqInt +printPrimLogEntryAthasParameter(sqInt i, sqInt hasParameter) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *className; + sqInt entryOop; + sqInt length; + sqInt methodClass; + sqInt methodSel; + sqInt value; + + length = 0; + entryOop = GIV(primTraceLog)[i]; + if (hasParameter) { + if (addressCouldBeObj(entryOop)) { + className = nameOfClasslengthInto(entryOop, (&length)); + } + else { + className = "bad class"; + length = 9; + } + fprintf(GIV(transcript), + "%.*s\n", + ((int) length), + className); + return 0; + } + if (((entryOop & (tagMask())) != 0)) { + value = (entryOop >> 3); + if (value == TraceIncrementalGC) { + fprintf(GIV(transcript), "**IncrementalGC**\n"); + } + else { + if (value == TraceFullGC) { + fprintf(GIV(transcript), "**FullGC**\n"); + } + else { + if (value == TraceCodeCompaction) { + fprintf(GIV(transcript), "**CompactCode**\n"); + } + else { + if (value == TraceStackOverflow) { + fprintf(GIV(transcript), "**StackOverflow**\n"); + } + else { + if (value == TracePrimitiveFailure) { + fprintf(GIV(transcript), "**PrimitiveFailure** "); + return 1; + } + else { + if (value == TracePrimitiveRetry) { + fprintf(GIV(transcript), "**PrimitiveRetry**\n"); + } + else { + fprintf(GIV(transcript), "???\n"); + } + } + } + } + } + } + return 0; + } + if (addressCouldBeObj(entryOop)) { + if (((byteAt((void *)(entryOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + className = "???"; + length = 3; + methodClass = safeMethodClassOf(entryOop); + if (methodClass != GIV(nilObj)) { + className = nameOfClasslengthInto(methodClass, (&length)); + } + methodSel = findSelectorOfMethod(entryOop); + if (methodSel == GIV(nilObj)) { + fprintf(GIV(transcript), + "%.*s>>(selector not found)\n", + ((int) length), + className); + } + else { + fprintf(GIV(transcript), + "%.*s>>#%.*s\n", + ((int) length), + className, + ((int) (numBytesOfBytes(methodSel))), + ((char *) (firstIndexableField(methodSel)))); + } + } + else { + safePrintStringOf(entryOop); + cr(); + } + } + else { + fprintf(GIV(transcript), + "%" PRIdSQINT "!!!\n", + i); + } + return 0; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printProcessStack: */ +void +printProcessStack(sqInt aProcess) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctx; + sqInt tagBits; + + cr(); + printNameOfClasscount(/* fetchClassOf: */ + ((tagBits = aProcess & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(aProcess)), 5); + + /* begin space */ + printChar(' '); + printHex(aProcess); + print(" priority "); + printNum(quickFetchIntegerofObject(PriorityIndex, aProcess)); + cr(); + ctx = followFieldofObject(SuspendedContextIndex, aProcess); + if (!(ctx == GIV(nilObj))) { + printCallStackOfcurrentFP(ctx, GIV(framePointer)); + } +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printProcsOnList: */ +sqInt +printProcsOnList(sqInt procList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firstProc; + sqInt proc; + + proc = (firstProc = followFieldofObject(FirstLinkIndex, procList)); + while (!(proc == GIV(nilObj))) { + printProcessStack(proc); + proc = followFieldofObject(NextLinkIndex, proc); + if (proc == firstProc) { + warning("circular process list!!"); + return null; + } + } + return 0; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackCallStackOf: */ +sqInt +printStackCallStackOf(sqInt aContextOrProcessOrFrame) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt context; + sqInt senderOop; + char *theFP; + + if (addressCouldBeObj(aContextOrProcessOrFrame)) { + if ((((longAt((void *)(aContextOrProcessOrFrame))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (checkIsStillMarriedContextcurrentFP(aContextOrProcessOrFrame, null))) { + return printStackCallStackOf(((sqInt)(frameOfMarriedContext(aContextOrProcessOrFrame)))); + } + if (aContextOrProcessOrFrame == (activeProcess())) { + return printStackCallStackOf(((sqInt)GIV(framePointer))); + } + if (couldBeProcess(aContextOrProcessOrFrame)) { + return printCallStackOf(longAt((void *)((aContextOrProcessOrFrame + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))))); + } + return null; + } + theFP = ((void *)aContextOrProcessOrFrame); + while (1) { + context = shortReversePrintFrameAndCallers(theFP); + if (!((((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && ((/* begin frameOfMarriedContext: */ + (senderOop = longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))), + assert((((senderOop) & 7) == 1)), + (theFP = ((char *)(senderOop - (smallIntegerTag())))), + checkIsStillMarriedContextcurrentFP(context, theFP))))) { + return null; + } + } + return 0; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackPageList */ +void +printStackPageList(void) +{ + StackPage *page; + + page = GIV(mostRecentlyUsedPage); + do { + /* begin printStackPage: */ + printStackPageuseCount(page, -1); + cr(); + } while(((page = (page->prevPage))) != (GIV(mostRecentlyUsedPage))); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackPageListInUse */ +void +printStackPageListInUse(void) +{ + sqInt n; + StackPage *page; + + page = GIV(mostRecentlyUsedPage); + n = 0; + do { + if (!(isFree(page))) { + printStackPageuseCount(page, (n += 1)); + cr(); + } + } while(((page = (page->prevPage))) != (GIV(mostRecentlyUsedPage))); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackPages */ +void +printStackPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + StackPage *page; + + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + page = stackPageAtpages(i, GIV(pages)); + printStackPageuseCount(page, -1); + cr(); + } +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackPagesInUse */ +void +printStackPagesInUse(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt n; + + n = 0; + for (i = 0; i < GIV(numStackPages); i += 1) { + if (!(isFree(stackPageAt(i)))) { + printStackPageuseCount(stackPageAt(i), (n += 1)); + cr(); + } + } +} + + /* StackInterpreter>>#printStackPage:useCount: */ +static NoDbgRegParms void +printStackPageuseCount(StackPage *page, sqInt n) +{ + print("page "); + printHexPtrnp(page); + print(" ("); + printNum(pageIndexFor((page->realStackLimit))); + if (n >= 0) { + print(","); + printNum(n); + } + print(") (trace: "); + printNum((page->trace)); + printChar(')'); + if (isFree(page)) { + print(" (free)"); + } + if (page == (GIV(mostRecentlyUsedPage))) { + print(" (MRU)"); + } + if (((page->prevPage)) == (GIV(mostRecentlyUsedPage))) { + print(" (LRU)"); + } + cr(); + + /* begin tab */ + printChar('\t'); + print("ba: "); + printHexPtrnp((page->baseAddress)); + print(" - sl: "); + printHexPtrnp((page->realStackLimit)); + print(" - sl-so: "); + printHexPtrnp(((page->realStackLimit)) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)); + print(" - la:"); + printHexPtrnp((page->lastAddress)); + if (!(isFree(page))) { + cr(); + + /* begin tab */ + printChar('\t'); + print("baseFP "); + printHexPtrnp((page->baseFP)); + + /* cr; */ + + /* begin tab */ + printChar('\t'); + print("headFP "); + printHexPtrnp((page->headFP)); + + /* cr; */ + + /* begin tab */ + printChar('\t'); + print("headSP "); + printHexPtrnp((page->headSP)); + } + cr(); + + /* begin tab */ + printChar('\t'); + print("prev "); + printHexPtrnp(page->prevPage); + print(" ("); + printNum(pageIndexFor((((page->prevPage))->realStackLimit))); + printChar(')'); + + /* begin tab */ + printChar('\t'); + print("next "); + printHexPtrnp(page->nextPage); + print(" ("); + printNum(pageIndexFor((((page->nextPage))->realStackLimit))); + printChar(')'); + cr(); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackReferencesTo: */ +void +printStackReferencesTo(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt i; + char *theFP; + StackPage *thePage; + char *theSP; + + callerFP = ((char *) 0); + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((thePage->baseFP)) { + theSP = (thePage->headSP); + theFP = (thePage->headFP); + + /* Skip the instruction pointer on top of stack of inactive pages. */ + if (!(thePage == GIV(stackPage))) { + theSP += BytesPerWord; + } + while (1) { + while (theSP <= (/* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver))) { + if (oop == (longAt(theSP))) { + fprintf(GIV(transcript), + "FP:%p @ %p\n", + theFP, + theSP); + } + theSP += BytesPerWord; + } + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) + && (oop == (longAt(theFP + FoxThisContext)))) { + fprintf(GIV(transcript), + "FP:%p CTXT\n", + theFP); + } + if (oop == (longAt(theFP + FoxMethod))) { + fprintf(GIV(transcript), + "FP:%p MTHD\n", + theFP); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + theFP = callerFP; + } + + /* a.k.a. FoxCallerContext */ + theSP = theFP + FoxCallerSavedIP; + while (theSP <= ((thePage->baseAddress))) { + if (oop == (longAt(theSP))) { + fprintf(GIV(transcript), + "FP:%p @ %p\n", + theFP, + theSP); + } + theSP += BytesPerWord; + } + } + } +} + + /* StackInterpreter>>#printStringOf: */ +static NoDbgRegParms void +printStringOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cnt; + sqInt code; + sqInt fmt; + sqInt i; + sqInt len; + sqInt max; + + if (((oop & (tagMask())) != 0)) { + return; + } + if (!(addressCouldBeObj(oop))) { + return; + } + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt < (firstByteFormat())) { + return; + } + cnt = ((((max = 128)) < ((len = lengthOf(oop)))) ? ((max = 128)) : ((len = lengthOf(oop)))); + i = 0; + if ((isinstanceOfcompactClassIndex(oop, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteArray) << (shiftForWord())))))), GIV(classByteArrayCompactIndex))) + || (/* isLargeIntegerInstance: */ + ((!(oop & (tagMask())))) + && ((((usqInt)(((longAt((void *)(oop))) & (classIndexMask())) - ClassLargeNegativeIntegerCompactIndex))) <= 1))) { + while (i < cnt) { + printHex(byteAt((void *)((oop + BaseHeaderSize) + i))); + i += 1; + } + } + else { + while (i < cnt) { + code = byteAt((void *)((oop + BaseHeaderSize) + i)); + switch (code) { + case 10: + print(""); + break; + case 13: + print(""); + break; + default: + printChar(code); + } + i += 1; + } + } + if (len > max) { + print("..."); + } + + /* begin flush */ + fflush(GIV(transcript)); +} + + /* StackInterpreter>>#print: */ +void +print(char *s) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fprintf(GIV(transcript), + "%s", + s); +} + + +/* Answer the list aProcess belongs to when runnable but not the active + process + */ +/* Useful for debugging!! */ + + /* StackInterpreter>>#processListForProcess: */ +sqInt +processListForProcess(sqInt aProcess) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt oop; + sqInt priority; + sqInt processLists; + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + priority = (oop >> 3); + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + processLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + return longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((priority - 1)) << (shiftForWord())))))); +} + + +/* Answer the zero-relative offset of Process' 'osErr inst var, or -1 if + absent. osErr will be set with the + platform's error after any FFI call (e.g. errno on unix or GetLastError, + WSAGetLastError et al on Win32) + which allows collecting the error without the potential for preemption + between call and error fetch, + which could result in fetching an invalid error code. */ + + /* StackInterpreter>>#processOSErrInstVarOffset */ +sqInt +processOSErrInstVarOffset(void) +{ + return (((imageHeaderFlags & 32) != 0) + ? (((imageHeaderFlags & 4) != 0) + ? 5 + : 4) + : -1); +} + + /* StackInterpreter>>#pushBool: */ +void +pushBool(sqInt trueOrFalse) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + + object = /* booleanObjectOf: */ + (trueOrFalse + ? GIV(trueObj) + : GIV(falseObj)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); +} + + +/* The receiver of a message send or the closure of a block activation is + always on the stack above any arguments and the frame itself. See the + diagram in StackInterpreter class>>initializeFrameIndices. */ + + /* StackInterpreter>>#pushedReceiverOrClosureOfFrame: */ +static NoDbgRegParms sqInt +pushedReceiverOrClosureOfFrame(char *theFP) +{ + /* begin frameStackedReceiver:numArgs: */ + return longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))); +} + + /* StackInterpreter>>#pushFloat: */ +void +pushFloat(double f) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + + object = floatObjectOf(f); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); +} + + /* StackInterpreter>>#pushInteger: */ +sqInt +pushInteger(sqInt integerValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),(((usqInt)integerValue << 3) | 1)); + GIV(stackPointer) = sp; + return null; +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#push: */ +void +push(sqInt object) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + longAtput((sp = GIV(stackPointer) - BytesPerWord),object); + GIV(stackPointer) = sp; +} + + +/* Append aLong to aFile in this platform's 'natural' byte order. aLong is + either 32 or 64 bits, + depending on ObjectMemory. (Bytes will be swapped, if necessary, when the + image is read + on a different platform.) Set successFlag to false if the write fails. */ + + /* StackInterpreter>>#putLong:toFile: */ +static NoDbgRegParms void +putLongtoFile(sqInt aLong, sqImageFile aFile) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objectsWritten; + + objectsWritten = sqImageFileWrite((&aLong), sizeof(sqInt), 1, aFile); + + /* begin success: */ + if (!(objectsWritten == 1)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Append the 16-bit aShort to aFile in this platform's 'natural' byte order. + (Bytes will be swapped, if necessary, when the image is read on a + different platform.) Set successFlag to false if the write fails. */ + + /* StackInterpreter>>#putShort:toFile: */ +static NoDbgRegParms void +putShorttoFile(short aShort, sqImageFile aFile) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objectsWritten; + + objectsWritten = sqImageFileWrite((&aShort), sizeof(short), 1, aFile); + + /* begin success: */ + if (!(objectsWritten == 1)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Save the given process on the scheduler process list for its priority, + adding to the back if yieldImplicitly or to the front if not + yieldImplicitly. + */ + + /* StackInterpreter>>#putToSleep:yieldingIf: */ +static NoDbgRegParms void +putToSleepyieldingIf(sqInt aProcess, sqInt yieldImplicitly) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firstLink; + sqInt objOop; + sqInt oop; + sqInt priority; + sqInt processList; + sqInt processLists; + + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + priority = (oop >> 3); + if ((GIV(highestRunnableProcessPriority) != 0) + && (priority > GIV(highestRunnableProcessPriority))) { + GIV(highestRunnableProcessPriority) = priority; + } + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + processLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + processList = longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((priority - 1)) << (shiftForWord())))))); + if (yieldImplicitly) { + addLastLinktoList(aProcess, processList); + } + else { + /* begin addFirstLink:toList: */ + assert(!(isForwarded(processList))); + assert((fetchPointerofObject(NextLinkIndex, aProcess)) == (nilObject())); + firstLink = longAt((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + assert(firstLink != aProcess); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(FirstLinkIndex, processList, aProcess)); + assert(isNonImmediate(processList)); + if (oopisGreaterThanOrEqualTo(processList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(aProcess & (tagMask())))) + && (oopisLessThan(aProcess, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(processList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(processList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))),aProcess); + if (firstLink == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(LastLinkIndex, processList, aProcess)); + assert(isNonImmediate(processList)); + if (oopisGreaterThanOrEqualTo(processList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(aProcess & (tagMask())))) + && (oopisLessThan(aProcess, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(processList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(processList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((processList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord()))))),aProcess); + } + else { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(NextLinkIndex, aProcess, firstLink)); + assert(isNonImmediate(aProcess)); + if (oopisGreaterThanOrEqualTo(aProcess, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(firstLink & (tagMask())))) + && (oopisLessThan(firstLink, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aProcess + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aProcess); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord()))))),firstLink); + } + + /* a.k.a. (self isEmptyList: aList) */ + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(MyListIndex, aProcess, processList)); + assert(isNonImmediate(aProcess)); + if (oopisGreaterThanOrEqualTo(aProcess, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(processList & (tagMask())))) + && (oopisLessThan(processList, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aProcess + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aProcess); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),processList); + } +} + + +/* Append aWord32 to aFile in this platform's 'natural' byte order. aWord32 + is 32 bits, + depending on ObjectMemory. (Bytes will be swapped, if necessary, when the + image is read + on a different platform.) Set successFlag to false if the write fails. */ + + /* StackInterpreter>>#putWord32:toFile: */ +static NoDbgRegParms void +putWord32toFile(int aWord32, sqImageFile aFile) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objectsWritten; + + objectsWritten = sqImageFileWrite((&aWord32), 4, 1, aFile); + + /* begin success: */ + if (!(objectsWritten == 1)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Return the integer value of the field without verifying that it is an + integer value! For use in time-critical places where the integer-ness of + the field can be guaranteed. + */ + + /* StackInterpreter>>#quickFetchInteger:ofObject: */ +static NoDbgRegParms sqInt +quickFetchIntegerofObject(sqInt fieldIndex, sqInt objectPointer) +{ + sqInt oop; + + oop = longAt((void *)((objectPointer + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + return (oop >> 3); +} + + +/* Anwer if images of the given format are readable by this interpreter. + Allows a virtual machine to accept selected older image formats. */ + + /* StackInterpreter>>#readableFormat: */ +sqInt +readableFormat(sqInt imageVersion) +{ + return (((imageVersion | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */; +} + + +/* Assuming the primFailCode is non-zero, check if the method consumes the + error code + and if so, assign it through theSP. Then zero the primFailCode. This is + infrequent code, + so keep it out of the common path. */ + + /* StackInterpreter>>#reapAndResetErrorCodeTo:header: */ +static NoDbgRegParms NeverInline void +reapAndResetErrorCodeToheader(char *theSP, sqInt methodHeader) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt initialPC; + + assert(GIV(primFailCode) != 0); + initialPC = (((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) + 3 /* sizeOfCallPrimitiveBytecode: */; + if ((byteAt((void *)(initialPC))) == (/* longStoreBytecodeForHeader: */ + ((((sqLong) methodHeader)) < 0 + ? AltLongStoreBytecode + : LongStoreBytecode))) { + longAtput(theSP,getErrorObjectFromPrimFailCode()); + } + GIV(primFailCode) = 0; +} + + +/* callbackContext is an activation of + invokeCallback:[stack:registers:jmpbuf:]. Its sender is the VM's state + prior to the callback. Reestablish that state, + and mark calloutContext as dead. */ + + /* StackInterpreter>>#reestablishContextPriorToCallback: */ +sqInt +reestablishContextPriorToCallback(sqInt callbackContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt calloutContext; + sqInt senderOop; + char *theFP; + StackPage *thePage; + sqInt top; + + if (!(isLiveContext(callbackContext))) { + return 0; + } + calloutContext = externalInstVarofContext(SenderIndex, callbackContext); + if (!(isLiveContext(calloutContext))) { + return 0; + } + + /* We're about to leave this stack page; must save the current frame's instructionPointer. */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* Mark callbackContext as dead; the common case is that it is the current frame. + We go the extra mile for the debugger. */ + if ((!((longAt((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))) & (tagMask())))) { + /* begin markContextAsDead: */ + assert(isContext(callbackContext)); + assert((isNonImmediate(callbackContext)) + && (!(isForwarded(callbackContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, callbackContext, GIV(nilObj))); + longAtput((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(callbackContext)) + && (!(isForwarded(callbackContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, callbackContext, GIV(nilObj))); + longAtput((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (GIV(framePointer) == theFP) { + if (longAt(theFP + FoxSavedFP)) { + GIV(instructionPointer) = longAt(GIV(framePointer) + FoxCallerSavedIP); + GIV(stackPointer) = (GIV(framePointer) + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(GIV(framePointer)))->cmNumArgs) + : byteAt((GIV(framePointer) + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + GIV(framePointer) = ((char *)(longAt(GIV(framePointer) + FoxSavedFP))); + return 1; + } + else { + freeStackPage(GIV(stackPage)); + } + } + else { + externalDivorceFrameandContext(theFP, callbackContext); + + /* begin markContextAsDead: */ + assert(isContext(callbackContext)); + assert((isNonImmediate(callbackContext)) + && (!(isForwarded(callbackContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, callbackContext, GIV(nilObj))); + longAtput((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(callbackContext)) + && (!(isForwarded(callbackContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, callbackContext, GIV(nilObj))); + longAtput((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + } + } + + /* Make the calloutContext the active frame. The case where calloutContext + is immediately below callbackContext on the same page is handled above. */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((calloutContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(calloutContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((calloutContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + + /* findSPOf:on: points to the word beneath the instructionPointer, but + there is no instructionPointer on the top frame of the current page. */ + assert(thePage != GIV(stackPage)); + GIV(stackPointer) = (findSPOfon(theFP, thePage)) - BytesPerWord; + GIV(framePointer) = theFP; + assert(GIV(stackPointer) < GIV(framePointer)); + } + else { + thePage = makeBaseFrameFor(calloutContext); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + return 1; +} + + +/* Remove the first process from the given linked list. */ + + /* StackInterpreter>>#removeFirstLinkOfList: */ +static NoDbgRegParms sqInt +removeFirstLinkOfList(sqInt aList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt first; + sqInt last; + sqInt next; + + assert(!((isForwarded(aList)))); + first = longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + last = longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord())))))); + assert(!((isForwarded(first)))); + assert(!((isForwarded(last)))); + if (first == last) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aList)) + && (!(isForwarded(aList)))); + assert(validStorePointerUncheckedArgs(FirstLinkIndex, aList, GIV(nilObj))); + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aList)) + && (!(isForwarded(aList)))); + assert(validStorePointerUncheckedArgs(LastLinkIndex, aList, GIV(nilObj))); + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + next = longAt((void *)((first + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(FirstLinkIndex, aList, next)); + assert(isNonImmediate(aList)); + if (oopisGreaterThanOrEqualTo(aList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(next & (tagMask())))) + && (oopisLessThan(next, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))),next); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(first)) + && (!(isForwarded(first)))); + assert(validStorePointerUncheckedArgs(NextLinkIndex, first, GIV(nilObj))); + longAtput((void *)((first + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord()))))),GIV(nilObj)); + return first; +} + + +/* Attempt to remove a process from a linked list. Answer if the attempt + succeeded. + */ + + /* StackInterpreter>>#removeProcess:fromList: */ +static NoDbgRegParms sqInt +removeProcessfromList(sqInt aProcess, sqInt aList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firstLink; + sqInt lastLink; + sqInt nextLink; + usqInt numSlots; + sqInt tempLink; + + nextLink = 0; + assert(!((isForwarded(aProcess)))); + assert(!((isForwarded(aList)))); + + /* any process on the list could have been becomed, so use a read barrier... */ + + /* begin followObjField:ofObject: */ + firstLink = longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(firstLink)); + if ((!((longAt((void *)(firstLink))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + firstLink = fixFollowedFieldofObjectwithInitialValue(FirstLinkIndex, aList, firstLink); + } + + /* begin followObjField:ofObject: */ + lastLink = longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(lastLink)); + if ((!((longAt((void *)(lastLink))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + lastLink = fixFollowedFieldofObjectwithInitialValue(LastLinkIndex, aList, lastLink); + } + if (aProcess == firstLink) { + /* begin followObjField:ofObject: */ + nextLink = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(nextLink)); + if ((!((longAt((void *)(nextLink))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + nextLink = fixFollowedFieldofObjectwithInitialValue(NextLinkIndex, aProcess, nextLink); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(FirstLinkIndex, aList, nextLink)); + assert(isNonImmediate(aList)); + if (oopisGreaterThanOrEqualTo(aList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(nextLink & (tagMask())))) + && (oopisLessThan(nextLink, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))),nextLink); + if (aProcess == lastLink) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aList)) + && (!(isForwarded(aList)))); + assert(validStorePointerUncheckedArgs(LastLinkIndex, aList, GIV(nilObj))); + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord()))))),GIV(nilObj)); + } + } + else { + tempLink = firstLink; + + /* fail if any link doesn't look like a process... */ + while (1) { + if (!((/* isPointers: */ + ((!(tempLink & (tagMask())))) + && (((byteAt((void *)(tempLink + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(tempLink)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(tempLink + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(tempLink - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > MyListIndex))) { + return 0; + } + + /* begin followObjField:ofObject: */ + nextLink = longAt((void *)((tempLink + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(nextLink)); + if ((!((longAt((void *)(nextLink))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + nextLink = fixFollowedFieldofObjectwithInitialValue(NextLinkIndex, tempLink, nextLink); + } + if (nextLink == aProcess) break; + tempLink = nextLink; + } + nextLink = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(NextLinkIndex, tempLink, nextLink)); + assert(isNonImmediate(tempLink)); + if (oopisGreaterThanOrEqualTo(tempLink, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(nextLink & (tagMask())))) + && (oopisLessThan(nextLink, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(tempLink + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(tempLink); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((tempLink + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord()))))),nextLink); + if (aProcess == lastLink) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(LastLinkIndex, aList, tempLink)); + assert(isNonImmediate(aList)); + if (oopisGreaterThanOrEqualTo(aList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(tempLink & (tagMask())))) + && (oopisLessThan(tempLink, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord()))))),tempLink); + } + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aProcess)) + && (!(isForwarded(aProcess)))); + assert(validStorePointerUncheckedArgs(NextLinkIndex, aProcess, GIV(nilObj))); + longAtput((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aProcess)) + && (!(isForwarded(aProcess)))); + assert(validStorePointerUncheckedArgs(MyListIndex, aProcess, GIV(nilObj))); + longAtput((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),GIV(nilObj)); + return 1; +} + + +/* Make aProcess runnable and if its priority is higher than that of the + current process, preempt the current process. Answer if the current + process was preempted. If the current process was preempted then if + yieldImplicitly add the current process to the back of its run queue, + causing an implicit yiled to other processes on the run queue, otherwise + add the current process to the front of its run queue, hence not yielding. + Blue book behaviour is to yield implicitly but is arguably incorrect. */ + + /* StackInterpreter>>#resume:preemptedYieldingIf:from: */ +static NoDbgRegParms sqInt +resumepreemptedYieldingIffrom(sqInt aProcess, sqInt yieldImplicitly, sqInt sourceCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activePriority; + sqInt activeProc; + sqInt newPriority; + sqInt objOop; + sqInt oop; + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + activePriority = (oop >> 3); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + newPriority = (oop >> 3); + if (newPriority <= activePriority) { + putToSleepyieldingIf(aProcess, 1); + return 0; + } + putToSleepyieldingIf(activeProc, yieldImplicitly); + transferTofrom(aProcess, sourceCode); + return 1; +} + + +/* In Spur two cases of primitive failure are handled specially. A primitive + may fail due to validation + encountering a forwarder. On failure, check the accessorDepth for the + primitive and if non-negative + scan the args to the depth, following any forwarders. Retry the primitive + if any are found. Hence + lazily and transparently following forwarders on primitive failure. + Additionally a primitive might fail + due to an allocation failing. Retry if external primitives have failed + with PrimErrNoMemory after running + first the scavenger and then on a subsequent failure, the global + mark-sweep collector. Hence lazily + and transparently GC on memory exhaustion. */ + + /* StackInterpreter>>#retryPrimitiveOnFailure */ +static sqInt +retryPrimitiveOnFailure(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt accessorDepth; + sqInt aMethodSelectorOrImmediate; + sqInt canRetry; + usqInt firstBytecode; + sqInt followDone; + sqInt gcDone; + sqInt header; + sqInt index; + sqInt methodHeader; + sqInt oop; + sqInt primIndex; + sqInt primitiveIndex; + sqInt referent; + sqInt retried; + sqInt retry; + sqInt scannedStackFrame; + sqInt tagBits; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + assert(saneFunctionPointerForFailureOfPrimIndex(primitiveIndex)); + gcDone = 0; + followDone = (canRetry = (retried = 0)); + while (1) { + retry = 0; + if (GIV(primFailCode) == PrimErrNoMemory) { + if (((gcDone += 1)) == 1) { + canRetry = primitiveIndex == PrimNumberExternalCall; + } + if (canRetry) { + if (gcDone == 1) { + /* begin scavengingGC */ + scavengingGCTenuringIf(TenureByAge); + } + if (gcDone == 2) { + fullGC(); + } + retry = gcDone <= 2; + } + } + else { + if (!followDone) { + followDone = 1; + + /* begin checkForAndFollowForwardedPrimitiveState */ + if ((recordPrimTrace()) + && (!primTracePluginName)) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveFailure << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + aMethodSelectorOrImmediate = /* fetchClassOf: */ + ((tagBits = (longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord))) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)))); + + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = aMethodSelectorOrImmediate; + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + assert(failed()); + retry = 0; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIndex = 0; + } + assert((GIV(argumentCount) == (argumentCountOf(GIV(newMethod)))) + || (isMetaPrimitiveIndex(primIndex))); + + /* First things first; make sure the metadata has been followed before it is accessed to derive accessorDepth... */ + if (((/* isCalloutPrimitiveIndex: */ + (primIndex == PrimNumberExternalCall) + || (primIndex == PrimNumberFFICall)) + || (primIndex == PrimNumberDoExternalCall)) + && (unfollowFirstLiteralOfMaybeCalloutMethodprimitiveIndex(GIV(newMethod), primIndex))) { + retry = 1; + } + + /* If the primitive is one of the meta primitives PrimNumberDoPrimitive or PrimNumberDoExternalCall, then + metaAccessorDepth will have been set to nil at the start of the primitive, and to the accessor depth of the + called primitive (or external call) immediately before dispatch. Hence if primIndex is that of a meta primitive + then if metaAccessorDepth is -2, the accessor depth is that of the meta primitive, and if > -2, then + metaAccessorDepth is the accessor depth of the primitive (or external call). Similarly, if the primitive is + primitiveExternalCall then the accessor depth is that of primitiveExternalCall until primitiveFunctionPointer + is assigned, at which point the accessor depth is taken from the slot in newMethod's first literal. */ + accessorDepth = ((/* isMetaPrimitiveIndex: */ + (primIndex == PrimNumberDoPrimitive) + || (primIndex == PrimNumberDoExternalCall)) + && (GIV(metaAccessorDepth) > -2) + ? GIV(metaAccessorDepth) + : accessorDepthForPrimitiveMethod(GIV(newMethod))); + assert(((accessorDepth >= -1) && (accessorDepth <= 5))); + if (accessorDepth >= 0) { + scannedStackFrame = 0; + for (index = 0; index <= GIV(argumentCount); index += 1) { + oop = longAt((void *)(GIV(stackPointer) + (index * BytesPerWord))); + if ((!(oop & (tagMask())))) { + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + assert(index < GIV(argumentCount)); + retry = 1; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + + /* stackValue:put: */ + longAtput((void *)(GIV(stackPointer) + (index * BytesPerWord)),oop); + if (!scannedStackFrame) { + scannedStackFrame = 1; + + /* Avoid repeated primitive failures by following all state in the current stack frame. */ + followForwardedFrameContentsstackPointer(GIV(framePointer), GIV(stackPointer) + ((GIV(argumentCount) + 1) * BytesPerWord)); + } + } + if ((accessorDepth > 0) + && ((/* hasPointerFields: */ + ((!(oop & (tagMask())))) + && (hasPointerFieldsNonImm(oop))) + && (followForwardedObjectFieldstoDepth(oop, accessorDepth)))) { + retry = 1; + } + } + } + } + if (retry + && (recordPrimTrace())) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveRetry << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + } + } + if (!(retry)) break; + assert(GIV(primFailCode) != 0); + retried = 1; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + dispatchFunctionPointer(primitiveFunctionPointer); + } + return retried; +} + + +/* callbackMethodContext is an activation of + invokeCallback:[stack:registers:jmpbuf:]. Its sender is the VM's state + prior to the callback. Reestablish that state (via longjmp), + and mark callbackMethodContext as dead. */ + + /* StackInterpreter>>#returnAs:ThroughCallback:Context: */ +sqInt +returnAsThroughCallbackContext(sqInt returnTypeOop, VMCallbackContext *vmCallbackContext, sqInt callbackMethodContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + sqInt calloutMethodContext; + sqInt cFramePointer; + sqInt cStackPointer; + sqInt header; + sqInt methodHeader; + sqInt senderOop; + char *theFP; + StackPage *thePage; + sqInt thunkp; + sqInt top; + + assert(GIV(primFailCode) == 0); + assert((((returnTypeOop) & 7) == 1)); + assert(!(isImmediate(((sqInt)vmCallbackContext)))); + assert((addressCouldBeObj(callbackMethodContext)) + && (isContext(callbackMethodContext))); + assert(((debugCallbackPath = 0)) == 0); + if (!(((((returnTypeOop) & 7) == 1)) + && (isLiveContext(callbackMethodContext)))) { + assert(((debugCallbackPath = 1)) == 1); + return 0; + } + calloutMethodContext = externalInstVarofContext(SenderIndex, callbackMethodContext); + if (!(isLiveContext(calloutMethodContext))) { + assert(((debugCallbackPath = 2)) == 2); + return 0; + } + assert(((debugCallbackReturns += 1)) > 0); + thunkp = ((sqInt) ((vmCallbackContext->thunkp))); + + /* begin recordCallbackReturn:thunkp: */ + /* begin recordTrace:source:thing:extra: */ + if (TraceLog) { + GIV(traceLog)[GIV(traceLogIndex)] = ((((((usqInt)(TraceVMCallbackReturn) << TraceTypeShift)))) + ((returnTypeOop >> 3))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = (((sqInt)thunkp)); + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + + /* We're about to leave this stack page; must save the current frame's instructionPointer. */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* Mark callbackMethodContext as dead; the common case is that it is the current frame. + We go the extra mile for the debugger. */ + if ((!((longAt((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))) & (tagMask())))) { + assert(((debugCallbackPath = debugCallbackPath | 4)) > 0); + + /* begin markContextAsDead: */ + assert(isContext(callbackMethodContext)); + assert((isNonImmediate(callbackMethodContext)) + && (!(isForwarded(callbackMethodContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, callbackMethodContext, GIV(nilObj))); + longAtput((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(callbackMethodContext)) + && (!(isForwarded(callbackMethodContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, callbackMethodContext, GIV(nilObj))); + longAtput((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + assert(((debugCallbackPath = debugCallbackPath | 8)) > 0); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + assert((frameReceiver(theFP)) == (splObj(ClassAlien))); + if (GIV(framePointer) == theFP) { + assert(((debugCallbackPath = debugCallbackPath | 16)) > 0); + if (longAt(theFP + FoxSavedFP)) { + assert(((debugCallbackPath = debugCallbackPath | 32)) > 0); + GIV(instructionPointer) = longAt(theFP + FoxCallerSavedIP); + GIV(stackPointer) = (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + GIV(framePointer) = ((char *)(longAt(theFP + FoxSavedFP))); + aMethodObj = /* frameMethodObject: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(GIV(framePointer)))->methodObject) + : longAt(GIV(framePointer) + FoxMethod)); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* begin restoreCStackStateForCallbackContext: */ + cFramePointer = ((sqInt) ((vmCallbackContext->savedCFramePointer))); + cStackPointer = ((sqInt) ((vmCallbackContext->savedCStackPointer))); + + /* begin setCFramePointer:setCStackPointer: */ + assert(!(((!cFramePointer) + || (!cStackPointer)))); + GIV(CStackPointer) = cStackPointer; + GIV(CFramePointer) = cFramePointer; + assertSavedCStackPointersWellAligned(); + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + + /* N.B. siglongjmp is defines as _longjmp on non-win32 platforms. + This matches the use of _setjmp in ia32abicc.c. */ + _longjmp((vmCallbackContext->trampoline), (returnTypeOop >> 3)); + return 1; + } + + /* calloutMethodContext is immediately below on the same page. Make it current. */ + freeStackPage(GIV(stackPage)); + } + else { + assert(((debugCallbackPath = debugCallbackPath | 64)) > 0); + externalDivorceFrameandContext(theFP, callbackMethodContext); + + /* begin markContextAsDead: */ + assert(isContext(callbackMethodContext)); + assert((isNonImmediate(callbackMethodContext)) + && (!(isForwarded(callbackMethodContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, callbackMethodContext, GIV(nilObj))); + longAtput((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(callbackMethodContext)) + && (!(isForwarded(callbackMethodContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, callbackMethodContext, GIV(nilObj))); + longAtput((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + } + } + + /* Make the calloutMethodContext the active frame. The case where calloutMethodContext + is immediately below callbackMethodContext on the same page is handled above. */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((calloutMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(calloutMethodContext)))) { + assert(((debugCallbackPath = debugCallbackPath | 128)) > 0); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((calloutMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + + /* findSPOf:on: points to the word beneath the instructionPointer, but + there is no instructionPointer on the top frame of the current page. */ + assert(thePage != GIV(stackPage)); + GIV(stackPointer) = (((thePage->headFP)) == theFP + ? (thePage->headSP) + : (findSPOfon(theFP, thePage)) - BytesPerWord); + GIV(framePointer) = theFP; + assert(GIV(stackPointer) < GIV(framePointer)); + } + else { + assert(((debugCallbackPath = debugCallbackPath | 0x100)) > 0); + thePage = makeBaseFrameFor(calloutMethodContext); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + aMethodObj = longAt((void *)((calloutMethodContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin restoreCStackStateForCallbackContext: */ + cFramePointer = ((sqInt) ((vmCallbackContext->savedCFramePointer))); + cStackPointer = ((sqInt) ((vmCallbackContext->savedCStackPointer))); + + /* begin setCFramePointer:setCStackPointer: */ + assert(!(((!cFramePointer) + || (!cStackPointer)))); + GIV(CStackPointer) = cStackPointer; + GIV(CFramePointer) = cFramePointer; + assertSavedCStackPointersWellAligned(); + + /* N.B. siglongjmp is defined as _longjmp on non-win32 platforms. + This matches the use of _setjmp in ia32abicc.c. */ + _longjmp((vmCallbackContext->trampoline), (returnTypeOop >> 3)); + + /* NOTREACHED */ + return 1; +} + + +/* Reverse the given range of Display pixels, rounded to whole word boundary. + Used to give feedback during VM activities such as garbage collection when + debugging. It is assumed that the given word range falls entirely within + the first line of the Display. */ + + /* StackInterpreter>>#reverseDisplayFrom:to: */ +static NoDbgRegParms void +reverseDisplayFromto(sqInt startIndex, sqInt endIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt primFailCodeValue; + sqInt ptr; + unsigned int reversed; + sqInt toDoLimit; + sqInt wordEndIndex; + sqInt wordStartIndex; + + if (!(postGCUpdateDisplayBits())) { + return; + } + if ((displayBits == 0) + || (((((((sqInt)displayBits)) & (tagMask())) != 0)) + || (displayDepth <= 0))) { + return; + } + wordStartIndex = ((((startIndex < 0) ? 0 : startIndex)) * displayDepth) / 32; + wordEndIndex = ((((endIndex < displayWidth) ? endIndex : displayWidth)) * displayDepth) / 32; + toDoLimit = (((sqInt)displayBits)) + (wordEndIndex * 4); + for (ptr = ((((sqInt)displayBits)) + (wordStartIndex * 4)); ptr <= toDoLimit; ptr += 4) { + reversed = (long32At((void *)(ptr))) ^ 0xFFFFFFFFU; + long32Atput((void *)(ptr),reversed); + } + primFailCodeValue = GIV(primFailCode); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + updateDisplayLeftTopRightBottom(0, 0, displayWidth, 1); + ioForceDisplayUpdate(); + GIV(primFailCode) = primFailCodeValue; +} + + +/* Rewrite an existing entry in the method cache with a new primitive + function address. + Used by primitiveExternalCall to make direct calls to found external + prims, or quickly + fail not found external prims. */ + + /* StackInterpreter>>#rewriteMethodCacheEntryForExternalPrimitiveToFunction: */ +static NoDbgRegParms void +rewriteMethodCacheEntryForExternalPrimitiveToFunction(void (*localPrimAddress)()) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((GIV(methodCache)[GIV(lastMethodCacheProbeWrite) + MethodCacheMethod]) == GIV(newMethod)) { + GIV(methodCache)[GIV(lastMethodCacheProbeWrite) + MethodCachePrimFunction] = (((sqIntptr_t) localPrimAddress)); + } +} + + +/* Safe version of methodClassOf: that deals with malformed compiled methods, + etc, and does not fixup forwarding pointers.. */ + + /* StackInterpreter>>#safeMethodClassOf: */ +static NoDbgRegParms sqInt +safeMethodClassOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt literal; + sqInt maybeClass; + usqInt numSlots; + sqInt offset; + + offset = (literalCountOf(methodPointer)) - 1; + + /* begin literal:ofMethod: */ + literal = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = followForwarded(literal); + } + if (!((/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(literal)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(literal + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(literal - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > ValueIndex))) { + return GIV(nilObj); + } + maybeClass = longAt((void *)((literal + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(maybeClass & (tagMask())))) + && ((!((longAt((void *)(maybeClass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + maybeClass = followForwarded(maybeClass); + } + return maybeClass; +} + + +/* This is an assert function used to ensure consistency between the + primitiveFunctionPointer and the primitive index when a primitive fails in + Spur. Since Spur automagically retries + failing primitives we want to know that the primitiveFunctionPointer is + actually valid. This isn't + always possible for the `indirect'' primitives (calling a plugin + primitive, calling the ffi, using the + receiver:tryPrimitive:withArgs: primitive evaluator in the simulator. We + do the best we can. */ + + /* StackInterpreter>>#saneFunctionPointerForFailureOfPrimIndex: */ +static NoDbgRegParms sqInt +saneFunctionPointerForFailureOfPrimIndex(sqInt primIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*basePrimitive)(void); + + /* begin functionPointerFor:inClass: */ + basePrimitive = ((((usqInt)primIndex)) > MaxPrimitiveIndex + ? ((void (*)(void)) 0) + : primitiveTable[primIndex]); + return (primitiveFunctionPointer == basePrimitive) + || (((/* isCalloutPrimitiveIndex: */ + (primIndex == PrimNumberExternalCall) + || (primIndex == PrimNumberFFICall)) + && (!((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex))) + || ((/* isMetaPrimitiveIndex: */ + (primIndex == PrimNumberDoPrimitive) + || (primIndex == PrimNumberDoExternalCall)) + && (GIV(metaAccessorDepth) > -2))); +} + + +/* Useful for debugging!! */ + + /* StackInterpreter>>#schedulerPointer */ +sqInt +schedulerPointer(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin fetchPointer:ofObject: */ + return longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#setBreakMNUSelector: */ +void +setBreakMNUSelector(char *aString) +{ + if ((breakSelector = aString)) { + breakSelectorLength = -(strlen(aString)); + } + else { + breakSelectorLength = MinSmallInteger; + } +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#setBreakSelector: */ +void +setBreakSelector(char *aString) +{ + if ((breakSelector = aString)) { + breakSelectorLength = strlen(aString); + } + else { + breakSelectorLength = MinSmallInteger; + } +} + + +/* If bit 0 of the imageHeaderFlags is set then the system should use the + full screen. + */ + + /* StackInterpreter>>#setFullScreenFlag: */ +void +setFullScreenFlag(sqInt aBoolean) +{ + imageHeaderFlags = (aBoolean + ? imageHeaderFlags | 1 + : ((imageHeaderFlags | 1) - 1)); +} + + /* StackInterpreter>>#setInterruptCheckChain: */ +void * +setInterruptCheckChain(void (*aFunction)()) +{ + void (*prevFunction)(); + + prevFunction = interruptCheckChain; + interruptCheckChain = aFunction; + return prevFunction; +} + + /* StackInterpreter>>#setInterruptKeycode: */ +void +setInterruptKeycode(sqInt value) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(interruptKeycode) = value; +} + + +/* This is invoked frrom the platform GUI event subsystem when the user + inputs a a char code matching interruptKeycode. Set the value and then + forceInterruptCheck so as to call the interruptCheckChain function if set. + It's important to set interruptPending *before* calling + forceInterruptCheck so that functions on the interruptCheckChain can check + interruptPending. */ + + /* StackInterpreter>>#setInterruptPending: */ +void +setInterruptPending(sqInt value) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(interruptPending) = value; + forceInterruptCheck(); +} + + /* StackInterpreter>>#setNextWakeupUsecs: */ +void +setNextWakeupUsecs(usqLong value) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(nextWakeupUsecs) = value; +} + + /* StackInterpreter>>#setSavedWindowSize: */ +void +setSavedWindowSize(sqInt value) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(savedWindowSize) = value; +} + + +/* The low space semaphore is about to be signaled. Set the signalLowSpace + flag, and force an interrupt check. Save the currently active process in + the special + objects array so that the low space handler will be able to determine the + process that first triggered a low space condition. The image's low space + handler is expected + to nil out the special objects array slot when it handles the low space + condition. */ + + /* StackInterpreter>>#setSignalLowSpaceFlagAndSaveProcess */ +static void +setSignalLowSpaceFlagAndSaveProcess(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + sqInt lastSavedProcess; + sqInt objOop; + + /* signalLowSpace: */ + GIV(signalLowSpace) = 1; + + /* begin lowSpaceThreshold: */ + GIV(lowSpaceThreshold) = 0; + + /* N.B. The threshold > 0 guard eliminates a warning when + self lowSpaceThreshold: 0 + is inlined into setSignalLowSpaceFlagAndSaveProcess */ + assert(GIV(totalFreeOldSpace) >= GIV(lowSpaceThreshold)); + lastSavedProcess = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ProcessSignalingLowSpace) << (shiftForWord())))))); + if (lastSavedProcess == GIV(nilObj)) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ProcessSignalingLowSpace, GIV(specialObjectsOop), activeProc)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeProc & (tagMask())))) + && (oopisLessThan(activeProc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ProcessSignalingLowSpace) << (shiftForWord()))))),activeProc); + } + forceInterruptCheck(); +} + + /* StackInterpreter>>#setTraceFlagOnContextsFramesPageIfNeeded: */ +static NoDbgRegParms void +setTraceFlagOnContextsFramesPageIfNeeded(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void *pointer; + sqInt senderOop; + StackPage *thePage; + + if (/* isStillMarriedContextDuringGC: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContextDuringGC(aContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + pointer = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(pointer), GIV(pages)); + assert(((((thePage->trace)) >= StackPageUnreached) && (((thePage->trace)) <= StackPageTraced))); + if (!((thePage->trace))) { + (thePage->trace = StackPageReachedButUntraced); + } + } +} + + /* StackInterpreter>>#shortPrintContext: */ +static NoDbgRegParms sqInt +shortPrintContext(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt home; + sqInt rcvr; + sqInt senderOop; + char *theFP; + + if (!(/* isContext: */ + ((!(aContext & (tagMask())))) + && (((longAt((void *)(aContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + printHex(aContext); + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " is not a context"); + cr(); + return null; + } + printHex(aContext); + if (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (checkIsStillMarriedContextcurrentFP(aContext, GIV(framePointer))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " M ("); + } + else { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " I ("); + } + printHex(((usqIntptr_t)theFP)); + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + ") "); + } + else { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " w "); + } + } + else { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " s "); + } + if ((home = findHomeForContext(aContext))) { + rcvr = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord())))))); + printActivationNameForreceiverisBlockfirstTemporary(longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))), rcvr, home != aContext, longAt((void *)((home + BaseHeaderSize) + ((((usqInt)((0 + CtxtTempFrameStart)) << (shiftForWord()))))))); + + /* begin space */ + printChar(' '); + shortPrintOop(rcvr); + } + else { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " BOGUS CONTEXT (can't determine home)"); + cr(); + } + return 0; +} + + /* StackInterpreter>>#shortPrintFrameAndCallers: */ +static NoDbgRegParms sqInt +shortPrintFrameAndCallers(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!(/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + return null; + } + shortPrintFrame(theFP); + if (longAt(theFP + FoxSavedFP)) { + shortPrintFrameAndCallers(((char *)(longAt(theFP + FoxSavedFP)))); + } + return 0; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#shortPrintFramesInPage: */ +void +shortPrintFramesInPage(StackPage *thePage) +{ + printFrameAndCallersSPshort((thePage->headFP), (thePage->headSP), 1); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#shortPrintFramesOnStackPageListInUse */ +void +shortPrintFramesOnStackPageListInUse(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *page; + + page = GIV(mostRecentlyUsedPage); + do { + if (!(isFree(page))) { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "page "); + printHexPtrnp(page); + cr(); + shortPrintFramesInPage(page); + cr(); + } + } while(((page = (page->prevPage))) != (GIV(mostRecentlyUsedPage))); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#shortPrintFrame:AndNCallers: */ +void +shortPrintFrameAndNCallers(char *theFP, sqInt n) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((n != 0) + && (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + shortPrintFrame(theFP); + shortPrintFrameAndNCallers(((char *)(longAt(theFP + FoxSavedFP))), n - 1); + } +} + + /* StackInterpreter>>#shortPrintOop: */ +static NoDbgRegParms void +shortPrintOop(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *className; + sqInt length; + char *where; + + length = 0; + if (((oop & (tagMask())) != 0)) { + printImmediateObjecton(oop, GIV(transcript)); + return; + } + if (!(addressCouldBeObj(oop))) { + /* begin printCantBeObject:on: */ + fprintf(GIV(transcript), + "%p%s\n", + ((void *)oop), + ((where = whereIsMaybeCodeThing(oop)) + ? where + : (oop & 7 /* (allocationUnit - 1) */ + ? " is misaligned" + : whereIs(oop)))); + return; + } + if (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + printFreeObjecton(oop, GIV(transcript)); + return; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + printForwarderon(oop, GIV(transcript)); + return; + } + className = nameOfClasslengthInto(fetchClassOfNonImm(oop), (&length)); + fprintf(GIV(transcript), + "%p: a(n) %.*s\n", + ((void *)oop), + ((int) length), + className); +} + + /* StackInterpreter>>#shortReversePrintFrameAndCallers: */ +static NoDbgRegParms sqInt +shortReversePrintFrameAndCallers(char *aFramePointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt caller; + char *callerFP; + char *theFP; + StackPage *thePage; + + callerFP = ((char *) 0); + if (!aFramePointer) { + return GIV(nilObj); + } + theFP = aFramePointer; + while (1) { + shortPrintFrame(theFP); + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!(callerFP != 0)) break; + theFP = callerFP; + } + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + caller = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(caller)); + assert((caller == (nilObject())) + || (isContext(followMaybeForwarded(caller)))); + if ((!((longAt((void *)(caller))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + caller = followForwarded(caller); + } + return caller; +} + + +/* Repaint the portion of the Smalltalk screen bounded by the affected + rectangle. Used to synchronize the screen after a Bitblt to the Smalltalk + Display object. + */ + + /* StackInterpreter>>#showDisplayBits:Left:Top:Right:Bottom: */ +sqInt +showDisplayBitsLeftTopRightBottom(sqInt aForm, sqInt l, sqInt t, sqInt r, sqInt b) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (deferDisplayUpdates + || (aForm != (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheDisplay) << (shiftForWord()))))))))) { + return null; + } + updateDisplayLeftTopRightBottom(l, t, r, b); + return 0; +} + + +/* Signal all requested semaphores. Answer if a context switch has occurred. */ + + /* StackInterpreter>>#signalExternalSemaphores */ +static sqInt +signalExternalSemaphores(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + sqInt xArray; + + xArray = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ExternalObjectsArray) << (shiftForWord())))))); + return doSignalExternalSemaphores( + (/* begin numSlotsOf: */ + assert((classIndexOf(xArray)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(xArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(xArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))); +} + + +/* Answer the size of the CallPrimitive bytecode that may be used to store a + method's primitive. + */ +/* NewsqueakV4: 249 11111001 iiiiiiii jjjjjjjj Call Primitive #iiiiiiii + + (jjjjjjjj * 256) + */ +/* SistaV1: 248 11111000 iiiiiiii mssjjjjj Call Primitive #iiiiiiii + ( + jjjjj * 256) */ +/* V3+Closures: 139 11101111 iiiiiiii jjjjjjjj Call Primitive #iiiiiiii + + (jjjjjjjj * 256) + */ + + /* StackInterpreter>>#sizeOfCallPrimitiveBytecode: */ +sqInt +sizeOfCallPrimitiveBytecode(sqInt methodHeader) +{ + return 3; +} + + +/* 234 11101010 i i i i i i i i Store Temporary Variable #iiiiiiii */ +/* 129 10000001 jjkkkkkk Store (Receiver Variable, Temporary Location, + Illegal, Literal Variable) [jj] #kkkkkk */ + + /* StackInterpreter>>#sizeOfLongStoreTempBytecode: */ +sqInt +sizeOfLongStoreTempBytecode(sqInt methodHeader) +{ + return 2; +} + + +/* Return the number of indexable fields of the given object. This method is + to be called from an automatically generated C primitive. The argument is + assumed to be a pointer to the first indexable field of a words or bytes + object; the object header starts 4 bytes before that. + */ +/* Note: Only called by translated primitive code. */ + + /* StackInterpreter>>#sizeOfSTArrayFromCPrimitive: */ +sqInt +sizeOfSTArrayFromCPrimitive(void *cPtr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + + oop = (oopForPointer(cPtr)) - BaseHeaderSize; + if (!(/* isWordsOrBytes: */ + ((!(oop & (tagMask())))) + && (isWordsOrBytesNonImm(oop)))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin lengthOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + +/* Invoke a normal (non-quick) primitive. + Called under the assumption that primFunctionPointer has been preloaded. */ + + /* StackInterpreter>>#slowPrimitiveResponse */ +static int +slowPrimitiveResponse(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt nArgs; + char *savedFramePointer; + char *savedStackPointer; + + assert(!(isOopForwarded(stackValue(GIV(argumentCount))))); + assert((GIV(remapBufferCount)) == 0); + nArgs = GIV(argumentCount); + savedStackPointer = GIV(stackPointer); + savedFramePointer = GIV(framePointer); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if (/* recordPrimTraceForMethod: */ + (recordPrimTrace()) + && ((primTracePluginName + ? methodHasPrimitiveInPrimTracePlugin(GIV(newMethod)) + : 1))) { + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = GIV(newMethod); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + dispatchFunctionPointer(primitiveFunctionPointer); + assert(maybeLeakCheckExternalPrimCall(GIV(newMethod))); + + /* begin maybeRetryPrimitiveOnFailure */ + if (GIV(primFailCode)) { + retryPrimitiveOnFailure(); + } + if ((!GIV(primFailCode)) + && ((GIV(framePointer) == savedFramePointer) + && (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))))) { + if (GIV(stackPointer) != (savedStackPointer + (nArgs * BytesPerWord))) { + /* This is necessary but insufficient; the result may still have been written to the stack. + At least we'll know something is wrong. */ + failUnbalancedPrimitive(); + GIV(stackPointer) = savedStackPointer; + } + } + + /* Don't fail if primitive has done something radical, e.g. perform: + If we are profiling, take accurate primitive measures */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + return !GIV(primFailCode); +} + + +/* update state of active context */ + + /* StackInterpreter>>#snapshot: */ +static NoDbgRegParms sqInt +snapshot(sqInt embedded) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt activeProc; + CogMethod *cogMethod; + usqInt i; + usqInt initialIP; + sqInt methodHeader; + usqInt numArgs; + usqInt numTemps; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt rcvr; + sqInt rcvrSqInt; + sqInt savedTenuringThreshold; + void *setMacType; + sqInt stackIndex; + + + /* For now the stack munging below doesn't deal with more than one argument. + It can, and should. */ + rcvr = 0; + if (GIV(argumentCount)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + + /* Need to convert all frames into contexts since the snapshot file only holds objects. */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + activeContext = voidVMStateForSnapshotFlushingExternalPrimitivesIf(1); + + /* update state of active process */ + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, activeProc, activeContext)); + assert(isNonImmediate(activeProc)); + if (oopisGreaterThanOrEqualTo(activeProc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeContext & (tagMask())))) + && (oopisLessThan(activeContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(activeProc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(activeProc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),activeContext); + GIV(tempOop) = activeContext; + + /* begin garbageCollectForSnapshot */ + /* begin flushNewSpace */ + savedTenuringThreshold = GIV(tenureThreshold); + GIV(tenureThreshold) = GIV(oldSpaceStart); + scavengingGCTenuringIf(TenureByAge); + + /* begin setRawTenuringThreshold: */ + GIV(tenureThreshold) = savedTenuringThreshold; + assert((GIV(rememberedSetSize)) == 0); + assert(GIV(pastSpaceStart) == (((GIV(pastSpace)).start))); + assert(GIV(freeStart) == (((GIV(eden)).start))); + + /* begin biasForSnapshot */ + GIV(biasForGC) = 0; + fullGC(); + + /* begin biasForGC */ + GIV(biasForGC) = 1; + unfireQueuedEphemeronsForSnapshot(); + prepareForSnapshot(); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + + /* Nothing moves from here on so it is safe to grab the activeContext again. */ + activeContext = GIV(tempOop); + GIV(tempOop) = 0; + if (!GIV(primFailCode)) { + /* Without contexts or stacks simulate + rcvr := self popStack. ''pop rcvr'' + self push: trueObj. + to arrange that the snapshot resumes with true. N.B. stackIndex is one-relative. */ + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + stackIndex = (oop >> 3); + rcvr = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(((stackIndex + CtxtTempFrameStart) - 1)) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(activeContext)) + && (!(isForwarded(activeContext)))); + assert(validStorePointerUncheckedArgs((stackIndex + CtxtTempFrameStart) - 1, activeContext, GIV(trueObj))); + longAtput((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(((stackIndex + CtxtTempFrameStart) - 1)) << (shiftForWord()))))),GIV(trueObj)); + + /* now attempt to write the snapshot file */ + writeImageFileIO(); + if ((!GIV(primFailCode)) + && (!embedded)) { + /* set Mac file type and creator; this is a noop on other platforms */ + setMacType = ioLoadFunctionFrom("setMacFileTypeAndCreator", "FilePlugin"); + if (setMacType) { + ((sqInt (*)(char *, char *, char *))setMacType)(imageName, "STim", "FAST"); + } + } + + /* Without contexts or stacks simulate + self pop: 1 */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(activeContext)) + && (!(isForwarded(activeContext)))); + assert(validStorePointerUncheckedArgs(StackPointerIndex, activeContext, (((usqInt)(stackIndex - 1) << 3) | 1))); + longAtput((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord()))))),(((usqInt)(stackIndex - 1) << 3) | 1)); + } + + /* begin postSnapshot */ + restorePostSnapshot(); + refireQueuedEphemeronsPostSnapshot(); + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + if (GIV(primFailCode)) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + cogMethod = null; + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + methodHeader = (cogMethod->methodHeader); + cogMethod = null; + } + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeader)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvrSqInt = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(addressCouldBeOop(rcvrSqInt)); + assert(!(isOopForwarded(rcvrSqInt))); + if (cogMethod) { + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + } + GIV(instructionPointer) = ceReturnToInterpreterPC(); + } + cogMethod = null; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + initialIP = ((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize; + if (cogMethod) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)cogMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + GIV(instructionPointer) = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + GIV(instructionPointer) = initialIP - 1; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),rcvrSqInt); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + if (!cogMethod) { + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + } + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeader); + } + } + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(falseObj)); + } + return 0; +} + + /* StackInterpreter>>#specialSelector: */ +sqInt +specialSelector(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin fetchPointer:ofObject: */ + return longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)((index * 2)) << (shiftForWord())))))); +} + + +/* Insulate the stack zone from the effects of a become. + All receivers must be unfollowed for two reasons: + 1. inst var access is direct with no read barrier + 2. super sends (always to the receiver) have no class check and so don't + trap for forwarded receivers. This is an issue for primitives that assume + their receiver + is valid and don't validate. + Super sends require an explicit check to ensure receivers in super sends + are unforwarded. + e.g. super doSomethingWith: (self become: other) forwards the receiver + self pushed on the + stack. So we could avoid following non-pointer receivers. But this is too + tricky, Instead, we + always follow receivers. + Methods must be unfollowed since bytecode access is direct with no read + barrier. But this only needs to be done if the becomeEffectsFlags indicate + that a + CompiledMethod was becommed. + The scheduler state must be followed, but only if the becomeEffectsFlags + indicate that a pointer object was becommed. */ +/* For VM profiling */ + + /* StackInterpreter>>#spurPostBecomeAction: */ +static NoDbgRegParms void +spurPostBecomeAction(sqInt theBecomeEffectsFlags) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqIntptr_t c; + sqInt first; + int i; + sqInt ipdelta; + sqInt iSqInt; + sqInt last; + sqInt list; + sqIntptr_t m; + sqInt next; + usqInt numSlots; + sqInt obj; + sqInt procLists; + sqInt referent; + sqInt result; + sqIntptr_t s; + sqInt sched; + sqInt schedAssoc; + sqInt toDoLimit; + sqInt xArray; + + if (theBecomeEffectsFlags) { + if (((theBecomeEffectsFlags & BecameActiveClassFlag) != 0)) { + /* begin flushBecommedClassesInMethodCache */ + for (i = 0; i < MethodCacheSize; i += MethodCacheEntrySize) { + c = GIV(methodCache)[i + MethodCacheClass]; + s = GIV(methodCache)[i + MethodCacheSelector]; + if ((c != 0) + && ((s != 0) + && (isForwarded(classOrNilAtIndex(c))))) { + GIV(methodCache)[i + MethodCacheClass] = 0; + GIV(methodCache)[i + MethodCacheSelector] = 0; + } + } + unlinkSendsLinkedForInvalidClasses(); + } + if (((theBecomeEffectsFlags & BecamePointerObjectFlag) != 0)) { + /* begin followForwardingPointersInScheduler */ + schedAssoc = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord())))))); + + /* the GC follows pointers in the special objects array for us. */ + assert(!(isForwarded(schedAssoc))); + + /* Make sure the active process has been followed. */ + followForwardedObjectFieldstoDepth(schedAssoc, 1); + sched = longAt((void *)((schedAssoc + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin followObjField:ofObject: */ + procLists = longAt((void *)((sched + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + assert(isNonImmediate(procLists)); + if ((!((longAt((void *)(procLists))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + procLists = fixFollowedFieldofObjectwithInitialValue(ProcessListsIndex, sched, procLists); + } + + /* Follow all links in the process list to ensure the lists are valid. */ + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(procLists)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(procLists + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(procLists - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (iSqInt = 0; iSqInt <= toDoLimit; iSqInt += 1) { + /* begin followObjField:ofObject: */ + list = longAt((void *)((procLists + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + assert(isNonImmediate(list)); + if ((!((longAt((void *)(list))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + list = fixFollowedFieldofObjectwithInitialValue(iSqInt, procLists, list); + } + + /* begin followObjField:ofObject: */ + first = longAt((void *)((list + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(first)); + if ((!((longAt((void *)(first))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + first = fixFollowedFieldofObjectwithInitialValue(FirstLinkIndex, list, first); + } + + /* begin followObjField:ofObject: */ + last = longAt((void *)((list + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(last)); + if ((!((longAt((void *)(last))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + last = fixFollowedFieldofObjectwithInitialValue(LastLinkIndex, list, last); + } + while (first != last) { + /* begin followObjField:ofObject: */ + next = longAt((void *)((first + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(next)); + if ((!((longAt((void *)(next))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + next = fixFollowedFieldofObjectwithInitialValue(NextLinkIndex, first, next); + } + first = next; + } + } + + /* begin followForwardingPointersInSpecialObjectsArray */ + /* begin followSemaphoreIn:at: */ + obj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheLowSpaceSemaphore) << (shiftForWord())))))); + if ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + obj = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheLowSpaceSemaphore, GIV(specialObjectsOop), obj)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheLowSpaceSemaphore) << (shiftForWord()))))),obj); + } + + /* begin followSemaphoreIn:at: */ + obj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheInterruptSemaphore) << (shiftForWord())))))); + if ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + obj = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheInterruptSemaphore, GIV(specialObjectsOop), obj)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheInterruptSemaphore) << (shiftForWord()))))),obj); + } + + /* begin followSemaphoreIn:at: */ + obj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord())))))); + if ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + obj = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheTimerSemaphore, GIV(specialObjectsOop), obj)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord()))))),obj); + } + + /* begin followSemaphoreIn:at: */ + obj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheFinalizationSemaphore) << (shiftForWord())))))); + if ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + obj = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheFinalizationSemaphore, GIV(specialObjectsOop), obj)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheFinalizationSemaphore) << (shiftForWord()))))),obj); + } + xArray = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ExternalObjectsArray) << (shiftForWord())))))); + if ((!((longAt((void *)(xArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(xArray)); + referent = longAt((void *)((xArray + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + xArray = referent; + + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ExternalObjectsArray, GIV(specialObjectsOop), xArray)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(xArray & (tagMask())))) + && (oopisLessThan(xArray, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ExternalObjectsArray) << (shiftForWord()))))),xArray); + } + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(xArray)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(xArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(xArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (ipdelta = 0; ipdelta <= toDoLimit; ipdelta += 1) { + /* begin followSemaphoreIn:at: */ + obj = longAt((void *)((xArray + BaseHeaderSize) + ((((usqInt)(ipdelta) << (shiftForWord())))))); + if ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + obj = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ipdelta, xArray, obj)); + assert(isNonImmediate(xArray)); + if (oopisGreaterThanOrEqualTo(xArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(xArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(xArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((xArray + BaseHeaderSize) + ((((usqInt)(ipdelta) << (shiftForWord()))))),obj); + } + } + } + if (((theBecomeEffectsFlags & (BecamePointerObjectFlag + BecameCompiledMethodFlag)) != 0)) { + /* begin followForwardingPointersInProfileState */ + /* begin profileStateDoUpdating: */ + if (GIV(profileProcess)) { + if ((result = ((!((longAt((void *)(GIV(profileProcess)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileProcess)) + : 0))) { + GIV(profileProcess) = result; + } + } + if (GIV(profileMethod)) { + if ((result = ((!((longAt((void *)(GIV(profileMethod)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileMethod)) + : 0))) { + GIV(profileMethod) = result; + } + } + if (GIV(profileSemaphore)) { + if ((result = ((!((longAt((void *)(GIV(profileSemaphore)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileSemaphore)) + : 0))) { + GIV(profileSemaphore) = result; + } + } + if (((theBecomeEffectsFlags & BecameCompiledMethodFlag) != 0)) { + /* begin followForwardedMethodsInMethodCache */ + for (i = 0; i < MethodCacheSize; i += MethodCacheEntrySize) { + c = GIV(methodCache)[i + MethodCacheClass]; + s = GIV(methodCache)[i + MethodCacheSelector]; + m = GIV(methodCache)[i + MethodCacheMethod]; + if ((c != 0) + && ((s != 0) + && ((m != 0) + && (/* isOopForwarded: */ + ((!(m & (tagMask())))) + && ((!((longAt((void *)(m))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(m)); + referent = longAt((void *)((m + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + m = referent; + GIV(methodCache)[i + MethodCacheMethod] = m; + } + } + + /* begin followMethodNewMethodAndInstructionPointer */ + ipdelta = 0; + if ((!((longAt((void *)(GIV(method)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + ipdelta = (/* method:includesAddress: */ + (GIV(instructionPointer) > GIV(method)) + && (GIV(instructionPointer) < (GIV(method) + ((((numSlotsOfAny(GIV(method))) << (shiftForWord()))) + BaseHeaderSize))) + ? GIV(instructionPointer) - GIV(method) + : 0); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(GIV(method))); + referent = longAt((void *)((GIV(method) + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + GIV(method) = referent; + if (ipdelta) { + GIV(instructionPointer) = GIV(method) + ipdelta; + } + } + if (/* isOopForwarded: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((!((longAt((void *)(GIV(newMethod)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ipdelta = ((!ipdelta) + && (/* method:includesAddress: */ + (GIV(instructionPointer) > GIV(newMethod)) + && (GIV(instructionPointer) < (GIV(newMethod) + ((((numSlotsOfAny(GIV(newMethod))) << (shiftForWord()))) + BaseHeaderSize)))) + ? GIV(instructionPointer) - GIV(newMethod) + : 0); + + /* don't relocate twice!! */ + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(GIV(newMethod))); + referent = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + GIV(newMethod) = referent; + } + + /* maybe oop due to object-as-method */ + if (ipdelta) { + GIV(instructionPointer) = GIV(newMethod) + ipdelta; + } + } + } + } + followForwardingPointersOfReceiversInStackZone(); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackFloatValue: */ +double +stackFloatValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return floatValueOf(longAt(GIV(stackPointer) + (offset * BytesPerWord))); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackIntegerValue: */ +sqInt +stackIntegerValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerPointer; + + integerPointer = longAt(GIV(stackPointer) + (offset * BytesPerWord)); + + /* begin checkedIntegerValueOf: */ + if ((((integerPointer) & 7) == 1)) { + return (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } +} + + +/* Answer the actual stackLimit offset in a page. Since stackPageByteSize may + have chosen to shrink a page + this may be less than stackPageFrameBytes, but it should be no more than + stackPageFrameBytes. + */ + + /* StackInterpreter>>#stackLimitBytes */ +static sqInt +stackLimitBytes(void) +{ + return (((stackPageFrameBytes()) < (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))) ? (stackPageFrameBytes()) : (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))); +} + + +/* Ensures that the given object is a real object, not a SmallInteger. */ +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackMutableObjectValue: */ +sqInt +stackMutableObjectValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = longAt(GIV(stackPointer) + (offset * BytesPerWord)); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(oop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + } + return oop; +} + + +/* Ensures that the given object is a real object, not a SmallInteger. */ +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackObjectValue: */ +sqInt +stackObjectValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = longAt(GIV(stackPointer) + (offset * BytesPerWord)); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return oop; +} + + +/* Answer a page size that is a power-of-two and contains a useful number of + frames. Room for 256 slots for frames gives around 40 frames a page which + is a + good compromise between overflow rate and latency in divorcing a page. */ + + /* StackInterpreter>>#stackPageByteSize */ +static sqInt +stackPageByteSize(void) +{ + sqInt largeSize; + sqInt pageBytes; + sqInt smallSize; + + pageBytes = ((stackPageFrameBytes()) + (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) + (osCogStackPageHeadroom()); + if (!(pageBytes & (pageBytes - 1))) { + return pageBytes; + } + + /* = 0 => a power of two + round up or round down; that is the question. If rounding down reduces + the size by no more than 1/8th round down, otherwise roundup. */ + largeSize = 1ULL << (highBit(pageBytes)); + smallSize = 1ULL << ((highBit(pageBytes)) - 1); + assert((largeSize > pageBytes) + && (pageBytes > smallSize)); + return ((pageBytes - smallSize) <= (smallSize / 8) + ? smallSize + : largeSize); +} + + +/* Return the stackPointer of a Context or BlockContext. */ + + /* StackInterpreter>>#stackPointerForMaybeMarriedContext: */ +static NoDbgRegParms sqInt +stackPointerForMaybeMarriedContext(sqInt aContext) +{ + sqInt sp; + sqInt spSqInt; + + if (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(aContext)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(aContext))); + return sp; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + return 0; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + return (spSqInt >> 3); +} + + +/* Return the 0-based index rel to the given frame. + (This is what stackPointer used to be before conversion to pointer) */ +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackPointerIndexForFrame: */ +static NoDbgRegParms sqInt +stackPointerIndexForFrame(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *thePage; + char *theSP; + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + theSP = findSPOfon(theFP, thePage); + return /* stackPointerIndexForFrame:WithSP: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (((usqInt)(((theFP + FoxMFReceiver) - theSP))) >> (shiftForWord())) + (((mframeCogMethod(theFP))->cmNumArgs)) + : (((usqInt)(((theFP + FoxIFReceiver) - theSP))) >> (shiftForWord())) + (byteAt((theFP + FoxIFrameFlags) + 1))); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackPositiveMachineIntegerValue: */ +usqIntptr_t +stackPositiveMachineIntegerValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerPointer; + sqInt value; + + integerPointer = longAt(GIV(stackPointer) + (offset * BytesPerWord)); + + /* begin positiveMachineIntegerValueOf: */ + if ((((integerPointer) & 7) == 1)) { + value = (integerPointer >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return value; + } + + /* don't inline the rare case */ + return positiveMachineIntegerValueOfObj(integerPointer); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackSignedMachineIntegerValue: */ +sqIntptr_t +stackSignedMachineIntegerValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerPointer; + + integerPointer = longAt(GIV(stackPointer) + (offset * BytesPerWord)); + + /* begin signedMachineIntegerValueOf: */ + if ((((integerPointer) & 7) == 1)) { + return (integerPointer >> 3); + } + return signedMachineIntegerValueOfObj(integerPointer); +} + + /* StackInterpreter>>#stackTop */ +sqInt +stackTop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt(GIV(stackPointer)); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackValue: */ +sqInt +stackValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt(GIV(stackPointer) + (offset * BytesPerWord)); +} + + +/* Zero-relative version of CompiledMethod>>startpc. */ + + /* StackInterpreter>>#startPCOfMethod: */ +usqInt +startPCOfMethod(sqInt aCompiledMethod) +{ + return ((literalCountOf(aCompiledMethod)) + LiteralStart) * BytesPerOop; +} + + +/* Return what ST would return for at: index. */ + + /* StackInterpreter>>#stObject:at: */ +sqInt +stObjectat(sqInt array, sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt sp; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + + hdr = long64At((void *)(array)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(array + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(array)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(array)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(array))); + stSize = sp; + goto l3; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l3; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(array))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l3: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(array)))))) { + return temporaryin(index - 1, frameOfMarriedContext(array)); + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(array)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + return (((usqInt)(byteAt((void *)((array + BaseHeaderSize) + (index - 1)))) << 3) | 1); + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(array); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + return longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << (shiftForWord())))))); + } + if (fmt >= (firstByteFormat())) { + return (((usqInt)(byteAt((void *)((array + BaseHeaderSize) + ((index + fixedFields) - 1)))) << 3) | 1); + } + if (fmt >= (firstShortFormat())) { + return (((usqInt)(((unsigned short) (shortAt((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))))))) << 3) | 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return positive64BitIntegerFor(long64At((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))))); + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + return (((usqInt)((((usqInt)(long32At((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + return 0; +} + + +/* Do what ST would return for at: index put: value. */ + + /* StackInterpreter>>#stObject:at:put: */ +sqInt +stObjectatput(sqInt array, sqInt index, sqInt value) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fieldIndex; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt signedValueToStore; + sqInt signedValueToStoreSqInt; + sqInt sp; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + usqLong unsigned64BitValueToStore; + unsigned int unsignedValueToStore; + + hdr = long64At((void *)(array)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(array + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l1: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(array)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(array)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(array))); + stSize = sp; + goto l2; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l2; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(array))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l2: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(array)))))) { + return temporaryinput(index - 1, frameOfMarriedContext(array), value); + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(array)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + if (((((value) & 7) == 1)) + && ((((usqInt)((signedValueToStore = (value >> 3))))) <= 0xFF)) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((array + BaseHeaderSize) + (index - 1)),signedValueToStore); + return value; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return value; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l3; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l3; + } + class = fetchClassOfNonImm(array); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l3: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:storing:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + fieldIndex = (index + fixedFields) - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, array, value)); + assert(isNonImmediate(array)); + if (oopisGreaterThanOrEqualTo(array, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(array + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(array); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),value); + goto l4; + } + if (fmt >= (firstByteFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((array + BaseHeaderSize) + ((index + fixedFields) - 1)),signedValueToStoreSqInt); + goto l4; + } + if (fmt >= (firstShortFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))),signedValueToStoreSqInt); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l4; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l4: + return value; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + return value; +} + + +/* Set the sender of a frame. If the frame is a base frame then this is + trivial; merely store into the FoxCallerSavedIP/FoxCallerContext field. If + not, then + split the stack at the frame, moving the frame and those hotter than it to + a new stack page. In the new stack page the frame will be the base frame + and storing trivial. Answer the possibly changed location of theFP. */ + + /* StackInterpreter>>#storeSenderOfFrame:withValue: */ +static NoDbgRegParms char * +storeSenderOfFramewithValue(char *theFP, sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + char *callerFP; + StackPage *newPage; + int onCurrentPage; + char *theMovedFP; + StackPage *thePage; + StackPage *thePage1; + + if (!(longAt(theFP + FoxSavedFP))) { + /* begin frameCallerContext:put: */ + assert((anOop == (nilObject())) + || (isContext(anOop))); + assert(isBaseFrame(theFP)); + assert(((theFP + (frameStackedReceiverOffset(theFP))) + (2 * BytesPerWord)) == (((stackPageFor(theFP))->baseAddress))); + assert((longAt((theFP + (frameStackedReceiverOffset(theFP))) + BytesPerWord)) == (frameContext(theFP))); + longAtput((theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + (2 * BytesPerWord),anOop); + return theFP; + } + + /* begin ensureCallerContext: */ + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!callerFP) { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerContextOrNil = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + goto l2; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP))); + + /* frameContext: */ + goto l2; + } + marryFrameSP( + callerFP, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFP))), + (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + /* end ensureCallerContext: */ +l2: + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + onCurrentPage = thePage == GIV(stackPage); + if (!onCurrentPage) { + /* Make sure the frame's page isn't divorced when a new page is allocated. */ + markStackPageNextMostRecentlyUsed(thePage); + } + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l1; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l1: + theMovedFP = moveFramesInthroughtoPage(thePage, theFP, newPage); + if (onCurrentPage) { + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + } + else { + markStackPageMostRecentlyUsed(newPage); + } + assert(isBaseFrame(theMovedFP)); + + /* begin frameCallerContext:put: */ + assert((anOop == (nilObject())) + || (isContext(anOop))); + assert(isBaseFrame(theMovedFP)); + assert(((theMovedFP + (frameStackedReceiverOffset(theMovedFP))) + (2 * BytesPerWord)) == (((stackPageFor(theMovedFP))->baseAddress))); + assert((longAt((theMovedFP + (frameStackedReceiverOffset(theMovedFP))) + BytesPerWord)) == (frameContext(theMovedFP))); + longAtput((theMovedFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theMovedFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theMovedFP))->cmNumArgs) + : byteAt((theMovedFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + (2 * BytesPerWord),anOop); + return theMovedFP; +} + + +/* Answer the number of indexable fields in the given object. (i.e., what + Smalltalk would return for size). + */ +/* Note: Assume oop is not an immediate! */ + + /* StackInterpreter>>#stSizeOf: */ +sqInt +stSizeOf(sqInt oop) +{ + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt totalLength; + + hdr = long64At((void *)(oop)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l1: + if (/* isPureBitsFormat: */ + (fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat()))) { + return totalLength; + } + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l2; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l2; + } + class = fetchClassOfNonImm(oop); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l2: + if (fmt == (indexablePointersFormat())) { + assert(!(isContextHeader(hdr))); + } + return totalLength - fixedFields; +} + + +/* Using a read barrier here simplifies the become implementation and costs + very little because the class index and superclass almost certainly share + a cache line. */ + + /* StackInterpreter>>#superclassOf: */ +sqInt +superclassOf(sqInt classPointer) +{ + sqInt objOop; + + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, objOop); + } + return objOop; +} + + +/* Signal the given semaphore from within the interpreter. + Answer if the current process was preempted. */ + + /* StackInterpreter>>#synchronousSignal: */ +static NoDbgRegParms sqInt +synchronousSignal(sqInt aSemaphore) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + sqLong excessSignals; + sqInt proc; + sqInt referent; + + assert((addressCouldBeObj(aSemaphore)) + && ((!(isForwarded(aSemaphore))) + && ((numSlotsOf(aSemaphore)) > ExcessSignalsIndex))); + + /* begin isEmptyList: */ + assert(!(isForwarded(aSemaphore))); + if ((longAt((void *)((aSemaphore + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj)) { + /* no process is waiting on this semaphore */ + excessSignals = fetchIntegerofObject(ExcessSignalsIndex, aSemaphore); + storeIntegerofObjectwithValue(ExcessSignalsIndex, aSemaphore, excessSignals + 1); + return 0; + } + + /* begin ensureSemaphoreUnforwardedThroughContext: */ + proc = longAt((void *)((aSemaphore + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + if ((!((longAt((void *)(proc))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + followForwardedObjectFieldstoDepth(aSemaphore, 1); + proc = longAt((void *)((aSemaphore + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + } + assert(!((isForwarded(proc)))); + ctxt = longAt((void *)((proc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if ((!((longAt((void *)(ctxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(ctxt)); + referent = longAt((void *)((ctxt + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + ctxt = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, proc, ctxt)); + assert(isNonImmediate(proc)); + if (oopisGreaterThanOrEqualTo(proc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(ctxt & (tagMask())))) + && (oopisLessThan(ctxt, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(proc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(proc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((proc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),ctxt); + } + return resumepreemptedYieldingIffrom(removeFirstLinkOfList(aSemaphore), (!(imageHeaderFlags & 16)), CSSynchronousSignal); +} + + /* StackInterpreter>>#tempCountOf: */ +usqInt +tempCountOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin temporaryCountOfMethodHeader: */ + return (((usqInt)(header)) >> MethodHeaderTempCountShift) & 0x3F; +} + + /* StackInterpreter>>#temporaryCountOfMethodHeader: */ +usqInt +temporaryCountOfMethodHeader(sqInt header) +{ + return (((usqInt)(header)) >> MethodHeaderTempCountShift) & 0x3F; +} + + /* StackInterpreter>>#ultimateLiteralOf: */ +sqInt +ultimateLiteralOf(sqInt aMethodOop) +{ + sqInt offset; + + assert(isOopCompiledMethod(aMethodOop)); + offset = (literalCountOf(aMethodOop)) - 1; + + /* begin literal:ofMethod: */ + return longAt((void *)((aMethodOop + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); +} + + +/* Follow the first literal of either a primitiveCallout or + primitiveExternalCall primitive method. This + will be an ExternalFunction for primitiveCallout or a four element Array + for primitiveExternalCall. + This is here to avoid following all the literals in a method, which would + be slow. Remember + forwarders are unlikely, so we only want to follow what is necessary, and + for an FFI call or + external primitive only the first literal is salient. */ + + /* StackInterpreter>>#unfollowFirstLiteralOfMaybeCalloutMethod:primitiveIndex: */ +static NoDbgRegParms sqInt +unfollowFirstLiteralOfMaybeCalloutMethodprimitiveIndex(sqInt methodObj, sqInt primIndex) +{ + sqInt firstLiteral; + sqInt found; + + found = 0; + + /* inlined self literal: 0 ofMethod: methodObj for clarity... */ + firstLiteral = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(LiteralStart) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(firstLiteral & (tagMask())))) + && ((!((longAt((void *)(firstLiteral))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + found = 1; + firstLiteral = fixFollowedFieldofObjectwithInitialValue(LiteralStart, methodObj, firstLiteral); + } + if (followForwardedObjectFieldstoDepth(firstLiteral, (primIndex == PrimNumberFFICall + ? (((sqInt)((primitiveMetadataTable[PrimNumberFFICall]))) >> SpurPrimitiveAccessorDepthShift) - 1 + : 0))) { + found = 1; + } + return found; +} + + +/* Repaint the portion of the Smalltalk screen bounded by the affected + rectangle. Used to synchronize the screen after a Bitblt to the Smalltalk + Display object. */ + + /* StackInterpreter>>#updateDisplayLeft:Top:Right:Bottom: */ +static NoDbgRegParms sqInt +updateDisplayLeftTopRightBottom(sqInt l, sqInt t, sqInt r, sqInt b) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bottom; + sqInt left; + sqInt right; + sqInt surfaceHandle; + sqInt top; + + left = ((l < 0) ? 0 : l); + right = ((r < displayWidth) ? r : displayWidth); + top = ((t < 0) ? 0 : t); + bottom = ((b < displayHeight) ? b : displayHeight); + if (!((left <= right) + && (top <= bottom))) { + return null; + } + if (((((((sqInt)displayBits))) & 7) == 1)) { + surfaceHandle = ((((sqInt)displayBits)) >> 3); + if (!showSurfaceFn) { + showSurfaceFn = ioLoadFunctionFrom("ioShowSurface", "SurfacePlugin"); + if (!showSurfaceFn) { + return (GIV(primFailCode) = PrimErrNotFound); + } + } + showSurfaceFn(surfaceHandle, left, top, right - left, bottom - top); + } + else { + assert(isNonImmediate(((sqInt)displayBits))); + ioShowDisplay(((sqInt)displayBits), displayWidth, displayHeight, displayDepth, left, right, top, bottom); + } + return 0; +} + + +/* Note that we accept anInstrPointer pointing to a callPrimitiveBytecode + at the start of a method that contains a primitive. This because methods + like Context(Part)>>reset have to be updated to skip the callPrimtiive + bytecode otherwise. */ + + /* StackInterpreter>>#validBCPC:inMethod: */ +static NoDbgRegParms sqInt +validBCPCinMethod(sqInt thePC, usqInt aMethod) +{ + return (thePC >= (((literalCountOf(aMethod)) + LiteralStart) * BytesPerOop)) + && (thePC < (numBytesOfBytes(aMethod))); +} + + +/* Note that we accept anInstrPointer pointing to a callPrimitiveBytecode + at the start of a method that contains a primitive. This because methods + like Context(Part)>>reset have to be updated to skip the callPrimtiive + bytecode otherwise. */ + + /* StackInterpreter>>#validInstructionPointer:inFrame: */ +static NoDbgRegParms sqInt +validInstructionPointerinFrame(usqInt anInstrPointer, char *fp) +{ + return validInstructionPointerinMethodframePointer(anInstrPointer, /* frameMethodObject: */ + ((((usqInt)(longAt(fp + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(fp))->methodObject) + : longAt(fp + FoxMethod)), fp); +} + + +/* Check that the base frames in all in-use stack pages have a sender and a + saved context. + */ + + /* StackInterpreter>>#validStackPageBaseFrames */ +static sqInt +validStackPageBaseFrames(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *aPage; + sqInt i; + + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + aPage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(aPage))) { + if (!(validStackPageBaseFrame(aPage))) { + return 0; + } + } + } + return 1; +} + + +/* Void the state associated with the long-running primitive check. + This is done when a new semaphore is installed or when it appears + that is longRunningPrimitiveCheckMethod is invalid, e.g. because it + has eben sampled in the middle of a GC. */ + + /* StackInterpreter>>#voidLongRunningPrimitive: */ +#if LRPCheck +static NoDbgRegParms NeverInline void +voidLongRunningPrimitive(char *reason) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(longRunningPrimitiveCheckMethod) = null; + GIV(longRunningPrimitiveStartUsecs) = (GIV(longRunningPrimitiveStopUsecs) = 0); + GIV(longRunningPrimitiveSignalUndelivered) = 1; + sqLowLevelMFence(); +} +#endif /* LRPCheck */ + + +/* Find the highest priority process that is ready to run and + remove it from its list. + To save time looking at many empty lists before finding a + runnable process the VM maintains a variable holding the + highest priority runnable process. If this variable is 0 then the + VM does not know the highest priority and must search all lists. + Answer nil if no process can be found. */ + + /* StackInterpreter>>#wakeHighestPriority */ +static sqInt +wakeHighestPriority(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + usqInt numSlots; + sqInt objOop; + sqInt p; + sqInt proc; + sqInt processList; + sqInt schedLists; + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + schedLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + p = (GIV(highestRunnableProcessPriority) + ? GIV(highestRunnableProcessPriority) + : (/* begin numSlotsOf: */ + assert((classIndexOf(schedLists)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(schedLists + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(schedLists - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))); + while (((p -= 1)) >= 0) { + processList = longAt((void *)((schedLists + BaseHeaderSize) + ((((usqInt)(p) << (shiftForWord())))))); + while (1) { + /* begin isEmptyList: */ + assert(!(isForwarded(processList))); + if ((longAt((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj)) break; + /* Only answer processes with a runnable suspendedContext. + Discard those that aren't; the VM would crash otherwise. */ + proc = removeFirstLinkOfList(processList); + ctxt = longAt((void *)((proc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if (isLiveContext(ctxt)) { + GIV(highestRunnableProcessPriority) = p + 1; + return proc; + } + warning("evicted zombie process from run queue"); + } + } + return null; +} + + +/* Write the image header and heap contents to imageFile for snapshot. c.f. + writeImageFileIOSimulation. The game below is to maintain 64-bit alignment + for all putLong:toFile: occurrences. */ + + /* StackInterpreter>>#writeImageFileIO */ +static NeverInline sqInt +writeImageFileIO(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bytesWritten; + sqImageFile f; + sqInt headerSize; + squeakFileOffsetType headerStart; + sqInt i; + sqInt imageBytes; + extern char imageName[]; + sqInt okToWrite; + void *sCWIfn; + + /* If the security plugin can be loaded, use it to check for write permission. + If not, assume it's ok */ + sCWIfn = ioLoadFunctionFrom("secCanWriteImage", "SecurityPlugin"); + if (sCWIfn) { + okToWrite = ((sqInt (*)(void))sCWIfn)(); + if (!okToWrite) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + } + + /* local constants */ + headerStart = 0; + + /* 64 or 128; header size in bytes; do not change! */ + headerSize = BytesPerWord * 16; + f = sqImageFileOpen(imageName, "wb"); + if (invalidSqImageFile(f)) { + return (GIV(primFailCode) = PrimErrOperationFailed); + } + + /* could not open the image file for writing */ + + /* begin imageSizeToWrite */ + assert(newSpaceIsEmpty()); + imageBytes = 0; + for (i = 0; i < GIV(numSegments); i += 1) { + if ((((GIV(segments)[i]).segSize)) > (2 * BaseHeaderSize)) { + imageBytes += ((GIV(segments)[i]).segSize); + } + } + headerStart = sqImageFileStartLocation(f, imageName, headerSize + imageBytes); + /* Note: on Unix systems one could put an exec command here, padded to 512 bytes */; + + /* position file to start of header */ + sqImageFileSeek(f, headerStart); + putWord32toFile(/* imageFormatVersionForSnapshot */ + (GIV(multipleBytecodeSetsActive) + ? 68533 /* (imageFormatVersion bitOr: MultipleBytecodeSetsBitmask) */ + : 68021 /* imageFormatVersion */), f); + putWord32toFile(headerSize, f); + putLongtoFile(imageBytes, f); + putLongtoFile(GIV(oldSpaceStart), f); + putLongtoFile(GIV(specialObjectsOop), f); + putLongtoFile(GIV(lastHash), f); + putLongtoFile(getSnapshotScreenSize(), f); + putLongtoFile(imageHeaderFlags, f); + putWord32toFile(extraVMMemory, f); + putShorttoFile(desiredNumStackPages, f); + putShorttoFile((desiredCogCodeSize + 0x3FF) / 0x400, f); + putWord32toFile(desiredEdenBytes, f); + putShorttoFile((GIV(maxExtSemTabSizeSet) + ? ioGetMaxExtSemTableSize() + : 0), f); + putShorttoFile(GIV(the2ndUnknownShort), f); + putLongtoFile(((GIV(segments)[0]).segSize), f); + putLongtoFile(GIV(totalFreeOldSpace), f); + putLongtoFile(0, f); + putLongtoFile(0, f); + putLongtoFile(0, f); + putLongtoFile(0, f); + putLongtoFile(0, f); + assert((headerStart + headerSize) == (sqImageFilePosition(f))); + + /* position file after the header */ + sqImageFileSeek(f, headerStart + headerSize); + if (GIV(primFailCode)) { + sqImageFileClose(f); + return null; + } + + /* file write or seek failure + write the image data */ + bytesWritten = writeImageSegmentsToFile(f); + + /* begin success: */ + if (!(bytesWritten == imageBytes)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + sqImageFileClose(f); + return 0; +} + + /* StackInterpreterPrimitives>>#cloneContext: */ +static NoDbgRegParms usqInt +cloneContext(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt cloned; + sqInt fieldIndex; + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + sqInt i; + usqInt newObj; + usqInt numBytes; + usqInt numSlots; + sqInt senderOop; + sqInt sp; + char *spouseFP; + usqInt sz; + sqInt valuePointer; + + /* begin numSlotsOf: */ + assert((classIndexOf(aContext)) > (isForwardedObjectClassIndexPun())); + sz = (((numSlots = byteAt((void *)(aContext + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aContext - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + + /* begin eeInstantiateMethodContextSlots: */ + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(sz < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((sz < 1 + ? 8 /* allocationUnit */ + : sz * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + cloned = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) sz)) << (numSlotsFullShift()))) + ((((usqInt)((indexablePointersFormat())) << (formatShift()))))) + ClassMethodContextCompactIndex); + GIV(freeStart) += numBytes; + cloned = newObj; + /* end eeInstantiateMethodContextSlots: */ +l1: + assert(!((cloned == 0))); + for (i = 0; i <= StackPointerIndex; i += 1) { + valuePointer = externalInstVarofContext(i, aContext); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(cloned)) + && (!(isForwarded(cloned)))); + assert(validStorePointerUncheckedArgs(i, cloned, valuePointer)); + longAtput((void *)((cloned + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + for (i = MethodIndex; i <= ReceiverIndex; i += 1) { + valuePointer = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(cloned)) + && (!(isForwarded(cloned)))); + assert(validStorePointerUncheckedArgs(i, cloned, valuePointer)); + longAtput((void *)((cloned + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + if (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + sp = (stackPointerIndexForFrame(spouseFP)) - 1; + for (i = 0; i <= sp; i += 1) { + fieldIndex = i + CtxtTempFrameStart; + valuePointer = /* temporary:in: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? (i < ((frameNumArgs = ((mframeCogMethod(spouseFP))->cmNumArgs))) + ? longAt((void *)((spouseFP + FoxCallerSavedIP) + ((frameNumArgs - i) * BytesPerWord))) + : longAt((void *)(((spouseFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs - i) * BytesPerWord)))) + : /* itemporary:in: */ + (i < ((frameNumArgsUsqInt = byteAt((spouseFP + FoxIFrameFlags) + 1))) + ? longAt((void *)((spouseFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt - i) * BytesPerWord))) + : longAt((void *)(((spouseFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt - i) * BytesPerWord))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(cloned)) + && (!(isForwarded(cloned)))); + assert(validStorePointerUncheckedArgs(fieldIndex, cloned, valuePointer)); + longAtput((void *)((cloned + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); + } + } + else { + sp = (fetchStackPointerOf(aContext)) - 1; + for (i = 0; i <= sp; i += 1) { + fieldIndex = i + CtxtTempFrameStart; + valuePointer = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)((i + CtxtTempFrameStart)) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(cloned)) + && (!(isForwarded(cloned)))); + assert(validStorePointerUncheckedArgs(fieldIndex, cloned, valuePointer)); + longAtput((void *)((cloned + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); + } + } + return cloned; +} + + +/* Arrange to answer naked frame pointers for unmarried + senders to avoid reifying contexts in the search. */ + + /* StackInterpreterPrimitives>>#fieldOrSenderFP:ofContext: */ +static NoDbgRegParms sqInt +fieldOrSenderFPofContext(sqInt index, sqInt contextObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt senderOop; + char *spouseFP; + sqInt tempIndex; + + tempIndex = index - CtxtTempFrameStart; + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((contextObj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(contextObj))))) { + if (tempIndex >= (fetchStackPointerOf(contextObj))) { + return GIV(nilObj); + } + else { + return longAt((void *)((contextObj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + } + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextObj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (tempIndex >= (stackPointerIndexForFrame(spouseFP))) { + return GIV(nilObj); + } + return fieldofFrame(index, spouseFP); +} + + +/* Arrange to answer naked frame pointers for unmarried + senders to avoid reifying contexts in the search. */ + + /* StackInterpreterPrimitives>>#field:ofFrame: */ +static NoDbgRegParms sqInt +fieldofFrame(sqInt index, char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + + switch (index) { + case SenderIndex: + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + return (callerFP + ? (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0) + ? (assert(checkIsStillMarriedContextcurrentFP(frameContext(callerFP), null)), + /* frameContext: */ + longAt(callerFP + FoxThisContext)) + : ((sqInt)callerFP)) + : frameCallerContext(theFP)); + + case StackPointerIndex: + case InstructionPointerIndex: + return ConstZero; + + case MethodIndex: + return /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + + case ClosureIndex: + return (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0) + ? frameStackedReceivernumArgs(theFP, /* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1))) + : GIV(nilObj)); + + case ReceiverIndex: + return /* frameReceiver: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? longAt(theFP + FoxMFReceiver) + : longAt(theFP + FoxIFReceiver)); + + default: + assert((((index - CtxtTempFrameStart) >= 0) && ((index - CtxtTempFrameStart) <= (stackPointerIndexForFrame(theFP))))); + return /* temporary:in: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((index - CtxtTempFrameStart) < ((frameNumArgs = ((mframeCogMethod(theFP))->cmNumArgs))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgs - (index - CtxtTempFrameStart)) * BytesPerWord)) + : longAt(((theFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs - (index - CtxtTempFrameStart)) * BytesPerWord))) + : /* itemporary:in: */ + ((index - CtxtTempFrameStart) < ((frameNumArgsUsqInt = byteAt((theFP + FoxIFrameFlags) + 1))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt - (index - CtxtTempFrameStart)) * BytesPerWord)) + : longAt(((theFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt - (index - CtxtTempFrameStart)) * BytesPerWord)))); + } + return 0; +} + + /* StackInterpreterPrimitives>>#isAppropriateForCopyObject: */ +static NoDbgRegParms sqInt +isAppropriateForCopyObject(sqInt oop) +{ + if (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) { + return 0; + } + if (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + return !(/* isStillMarriedContext: */ + (((((longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(oop)))); + } + + /* Note there is no version in CoInterpreterPrimitives such as + (objectMemory isCompiledMethod: oop) ifTrue: + [^(self methodHasCogMethod: oop) not]. + because isPointersNonImm: excludes compiled methods and the + copy loop in primitiveCopyObject cannot handle compiled methods. */ + return 1; +} + + +/* The function has not been loaded yet. Fetch module and function name. */ + + /* StackInterpreterPrimitives>>#linkExternalCall:errInto: */ +static NoDbgRegParms void +(*linkExternalCallerrInto(sqInt externalCallLiteral, sqInt *failPtr))(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*addr)(); + sqInt fmt; + void *functionAddress; + sqInt functionLength; + sqInt functionName; + sqInt i; + sqInt index; + sqInt metadata; + sqInt moduleLength; + sqInt moduleName; + usqInt numSlots; + + metadata = 0; + moduleName = longAt((void *)((externalCallLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralModuleNameIndex) << (shiftForWord())))))); + if (moduleName == GIV(nilObj)) { + moduleLength = 0; + } + else { + if (!(/* isBytes: */ + ((!(moduleName & (tagMask())))) + && (((byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + if (failPtr) { + failPtr[0] = PrimErrBadMethod; + } + return 0; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + moduleLength = ((((/* begin numSlotsOf: */ + assert((classIndexOf(moduleName)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(moduleName + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(moduleName - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + } + functionName = longAt((void *)((externalCallLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFunctionNameIndex) << (shiftForWord())))))); + if (!(/* isBytes: */ + ((!(functionName & (tagMask())))) + && (((byteAt((void *)(functionName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + if (failPtr) { + failPtr[0] = PrimErrBadMethod; + } + return 0; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(functionName + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + functionLength = ((((/* begin numSlotsOf: */ + assert((classIndexOf(functionName)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(functionName + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(functionName - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + + /* Spur needs metadata for the primitive, which is stored in the third slot of the literal. */ + addr = ioLoadExternalFunctionOfLengthFromModuleOfLengthMetadataInto(functionName + BaseHeaderSize, functionLength, moduleName + BaseHeaderSize, moduleLength, (&metadata)); + if (addr) { + assert(((((metadata) >> SpurPrimitiveAccessorDepthShift) >= -1) && (((metadata) >> SpurPrimitiveAccessorDepthShift) <= 5))); + functionAddress = ((void *) addr); + + /* begin addToExternalPrimitiveTable: */ + for (i = GIV(externalPrimitiveTableFirstFreeIndex); i < MaxExternalPrimitiveTableSize; i += 1) { + if (!(externalPrimitiveTable[i])) { + externalPrimitiveTable[i] = functionAddress; + index = (GIV(externalPrimitiveTableFirstFreeIndex) = i + 1); + goto l1; + } + } + + /* if no space left, return zero so it'll looked up again */ + index = 0; + /* end addToExternalPrimitiveTable: */ +l1: + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(externalCallLiteral)) + && (!(isForwarded(externalCallLiteral)))); + assert(validStorePointerUncheckedArgs(ExternalCallLiteralFlagsIndex, externalCallLiteral, (((usqInt)metadata << 3) | 1))); + longAtput((void *)((externalCallLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFlagsIndex) << (shiftForWord()))))),(((usqInt)metadata << 3) | 1)); + } + else { + index = -1; + } + + /* Store the index (or -1 if failure) back in the literal */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(externalCallLiteral)) + && (!(isForwarded(externalCallLiteral)))); + assert(validStorePointerUncheckedArgs(ExternalCallLiteralTargetFunctionIndex, externalCallLiteral, (((usqInt)index << 3) | 1))); + longAtput((void *)((externalCallLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord()))))),(((usqInt)index << 3) | 1)); + + /* If the function has been successfully loaded cache it */ + rewriteMethodCacheEntryForExternalPrimitiveToFunction((index >= 0 + ? addr + : 0)); + return addr; +} + + +/* Return a shallow copy of the receiver. + Special-case non-single contexts (because of context-to-stack mapping). + Can't fail for contexts cuz of image context instantiation code (sigh). */ +/* because of cloneContext: below */ + + /* StackInterpreterPrimitives>>#primitiveClone */ +static void +primitiveClone(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newCopy; + sqInt rcvr; + + newCopy = 0; + rcvr = longAt(GIV(stackPointer)); + if (((rcvr & (tagMask())) != 0)) { + newCopy = rcvr; + } + else { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + newCopy = cloneContext(rcvr); + } + else { + if ((GIV(argumentCount) == 0) + || (!((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + newCopy = cloneObject(rcvr); + } + } + if (!newCopy) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + return; + } + } + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newCopy); +} + + +/* Special version of primitiveAt for accessing contexts. + Written to be varargs for use from mirror primitives. */ + + /* StackInterpreterPrimitives>>#primitiveContextAt */ +static void +primitiveContextAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aContext; + sqInt class; + sqInt fixedFields; + sqInt fixedFieldsSqInt; + usqLong fmt; + usqLong fmtUsqLong; + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + sqLong hdr; + sqLong hdrSqLong; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt senderOop; + sqInt sp; + char *spouseFP; + sqInt spSqInt; + sqInt stSize; + sqInt stSizeSqInt; + sqInt totalLength; + sqInt totalLengthSqInt; + sqInt value; + + index = longAt(GIV(stackPointer)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + index = (index >> 3); + aContext = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* Duplicating much of stObject:at:put: here allows stObject:at:put: to omit tests for contexts. */ + hdr = long64At((void *)(aContext)); + if (!((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stObject:at: */ + hdrSqLong = long64At((void *)(aContext)); + fmtUsqLong = (((usqLong)(hdrSqLong)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(aContext + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aContext - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmtUsqLong <= (ephemeronFormat())) { + totalLengthSqInt = numSlots; + goto l7; + } + if (fmtUsqLong >= (firstByteFormat())) { + totalLengthSqInt = ((numSlots << (shiftForWord()))) - (fmtUsqLong & 7); + goto l7; + } + + /* bytes, including CompiledMethod */ + if (fmtUsqLong >= (firstShortFormat())) { + totalLengthSqInt = ((numSlots << ((shiftForWord()) - 1))) - (fmtUsqLong & 3); + goto l7; + } + if (fmtUsqLong >= (firstLongFormat())) { + totalLengthSqInt = ((numSlots << ((shiftForWord()) - 2))) - (fmtUsqLong & 1); + goto l7; + } + if (fmtUsqLong == (sixtyFourBitIndexableFormat())) { + totalLengthSqInt = numSlots; + goto l7; + } + + /* fmt = self forwardedFormat */ + totalLengthSqInt = 0; + /* end lengthOf:baseHeader:format: */ +l7: + if ((fmtUsqLong == (indexablePointersFormat())) + && ((hdrSqLong & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(aContext)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(aContext))); + stSizeSqInt = sp; + goto l5; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSizeSqInt = 0; + goto l5; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + stSizeSqInt = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l5: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSizeSqInt)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))))) { + value = temporaryin(index - 1, frameOfMarriedContext(aContext)); + goto l8; + } + } + if (fmtUsqLong >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(aContext)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLengthSqInt))) { + value = (((usqInt)(byteAt((void *)((aContext + BaseHeaderSize) + (index - 1)))) << 3) | 1); + goto l8; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmtUsqLong >= (sixtyFourBitIndexableFormat())) + || (fmtUsqLong == (arrayFormat()))) { + fixedFieldsSqInt = 0; + goto l6; + } + if (fmtUsqLong < (arrayFormat())) { + fixedFieldsSqInt = totalLengthSqInt; + goto l6; + } + class = fetchClassOfNonImm(aContext); + fixedFieldsSqInt = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l6: + stSizeSqInt = totalLengthSqInt - fixedFieldsSqInt; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSizeSqInt))) { + /* begin subscript:with:format: */ + if (fmtUsqLong <= 5 /* lastPointerFormat */) { + value = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << (shiftForWord())))))); + goto l8; + } + if (fmtUsqLong >= (firstByteFormat())) { + value = (((usqInt)(byteAt((void *)((aContext + BaseHeaderSize) + ((index + fixedFieldsSqInt) - 1)))) << 3) | 1); + goto l8; + } + if (fmtUsqLong >= (firstShortFormat())) { + value = (((usqInt)(((unsigned short) (shortAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 1)))))))) << 3) | 1); + goto l8; + } + if (fmtUsqLong == (sixtyFourBitIndexableFormat())) { + value = positive64BitIntegerFor(long64At((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 3)))))); + goto l8; + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + value = (((usqInt)((((usqInt)(long32At((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + goto l8; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmtUsqLong <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + value = 0; + /* end stObject:at: */ +l8: + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + } + return; + } + + /* might be an instance of a subclass */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext))))) { + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(aContext + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aContext - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l4; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l4; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l4; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l4; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l4: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l3; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l3; + } + class = fetchClassOfNonImm(aContext); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l3: + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l1; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + stSize = (spSqInt >> 3); + /* end fetchStackPointerOf: */ +l1: + if (!(((index >= 1) && (index <= stSize)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* begin subscript:with:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + value = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << (shiftForWord())))))); + goto l2; + } + if (fmt >= (firstByteFormat())) { + value = (((usqInt)(byteAt((void *)((aContext + BaseHeaderSize) + ((index + fixedFields) - 1)))) << 3) | 1); + goto l2; + } + if (fmt >= (firstShortFormat())) { + value = (((usqInt)(((unsigned short) (shortAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))))))) << 3) | 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + value = positive64BitIntegerFor(long64At((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))))); + goto l2; + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + value = (((usqInt)((((usqInt)(long32At((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + /* end subscript:with:format: */ +l2: + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + return; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!(((index >= 1) && (index <= (stackPointerIndexForFrame(spouseFP)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + value = /* temporary:in: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? ((index - 1) < ((frameNumArgs = ((mframeCogMethod(spouseFP))->cmNumArgs))) + ? longAt((spouseFP + FoxCallerSavedIP) + ((frameNumArgs - (index - 1)) * BytesPerWord)) + : longAt(((spouseFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs - (index - 1)) * BytesPerWord))) + : /* itemporary:in: */ + ((index - 1) < ((frameNumArgsUsqInt = byteAt((spouseFP + FoxIFrameFlags) + 1))) + ? longAt((spouseFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt - (index - 1)) * BytesPerWord)) + : longAt(((spouseFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt - (index - 1)) * BytesPerWord)))); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); +} + + +/* Special version of primitiveAtPut for accessing contexts. + Written to be varargs for use from mirror primitives. */ + + /* StackInterpreterPrimitives>>#primitiveContextAtPut */ +static void +primitiveContextAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aContext; + sqInt class; + sqInt fieldIndex; + sqInt fixedFields; + sqInt fixedFieldsSqInt; + usqLong fmt; + usqLong fmtUsqLong; + usqInt frameNumArgs; + unsigned char frameNumArgs1; + sqLong hdr; + sqLong hdrSqLong; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt senderOop; + sqInt signedValueToStore; + sqInt signedValueToStoreSqInt; + sqInt sp; + char *spouseFP; + sqInt spSqInt; + sqInt stSize; + sqInt stSizeSqInt; + sqInt totalLength; + sqInt totalLengthSqInt; + usqLong unsigned64BitValueToStore; + unsigned int unsignedValueToStore; + sqInt value; + + value = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aContext = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* Duplicating much of stObject:at:put: here allows stObject:at:put: to omit tests for contexts. */ + hdr = long64At((void *)(aContext)); + index = (index >> 3); + if (!((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stObject:at:put: */ + hdrSqLong = long64At((void *)(aContext)); + fmtUsqLong = (((usqLong)(hdrSqLong)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(aContext + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aContext - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmtUsqLong <= (ephemeronFormat())) { + totalLengthSqInt = numSlots; + goto l6; + } + if (fmtUsqLong >= (firstByteFormat())) { + totalLengthSqInt = ((numSlots << (shiftForWord()))) - (fmtUsqLong & 7); + goto l6; + } + + /* bytes, including CompiledMethod */ + if (fmtUsqLong >= (firstShortFormat())) { + totalLengthSqInt = ((numSlots << ((shiftForWord()) - 1))) - (fmtUsqLong & 3); + goto l6; + } + if (fmtUsqLong >= (firstLongFormat())) { + totalLengthSqInt = ((numSlots << ((shiftForWord()) - 2))) - (fmtUsqLong & 1); + goto l6; + } + if (fmtUsqLong == (sixtyFourBitIndexableFormat())) { + totalLengthSqInt = numSlots; + goto l6; + } + + /* fmt = self forwardedFormat */ + totalLengthSqInt = 0; + /* end lengthOf:baseHeader:format: */ +l6: + if ((fmtUsqLong == (indexablePointersFormat())) + && ((hdrSqLong & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(aContext)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(aContext))); + stSizeSqInt = sp; + goto l8; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSizeSqInt = 0; + goto l8; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + stSizeSqInt = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l8: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSizeSqInt)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))))) { + temporaryinput(index - 1, frameOfMarriedContext(aContext), value); + goto l9; + } + } + if (fmtUsqLong >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(aContext)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLengthSqInt))) { + if (((((value) & 7) == 1)) + && ((((usqInt)((signedValueToStore = (value >> 3))))) <= 0xFF)) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((aContext + BaseHeaderSize) + (index - 1)),signedValueToStore); + goto l9; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l9; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmtUsqLong >= (sixtyFourBitIndexableFormat())) + || (fmtUsqLong == (arrayFormat()))) { + fixedFieldsSqInt = 0; + goto l5; + } + if (fmtUsqLong < (arrayFormat())) { + fixedFieldsSqInt = totalLengthSqInt; + goto l5; + } + class = fetchClassOfNonImm(aContext); + fixedFieldsSqInt = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l5: + stSizeSqInt = totalLengthSqInt - fixedFieldsSqInt; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSizeSqInt))) { + /* begin subscript:with:storing:format: */ + if (fmtUsqLong <= 5 /* lastPointerFormat */) { + fieldIndex = (index + fixedFieldsSqInt) - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, aContext, value)); + assert(isNonImmediate(aContext)); + if (oopisGreaterThanOrEqualTo(aContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),value); + goto l7; + } + if (fmtUsqLong >= (firstByteFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l7; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l7; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((aContext + BaseHeaderSize) + ((index + fixedFieldsSqInt) - 1)),signedValueToStoreSqInt); + goto l7; + } + if (fmtUsqLong >= (firstShortFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l7; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l7; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 1)))),signedValueToStoreSqInt); + goto l7; + } + if (fmtUsqLong == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l7; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l7: + goto l9; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmtUsqLong <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + /* end stObject:at:put: */ +l9: + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + } + return; + } + + /* might be an instance of a subclass */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext))))) { + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(aContext + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aContext - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l3; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l3; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l3; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l3; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l3; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l3: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l2; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l2; + } + class = fetchClassOfNonImm(aContext); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l2: + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l1; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + stSize = (spSqInt >> 3); + /* end fetchStackPointerOf: */ +l1: + if (!(((index >= 1) && (index <= stSize)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* begin subscript:with:storing:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + fieldIndex = (index + fixedFields) - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, aContext, value)); + assert(isNonImmediate(aContext)); + if (oopisGreaterThanOrEqualTo(aContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),value); + goto l4; + } + if (fmt >= (firstByteFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStore = (value >> 3); + if (!((signedValueToStore >= 0) + && (signedValueToStore <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((aContext + BaseHeaderSize) + ((index + fixedFields) - 1)),signedValueToStore); + goto l4; + } + if (fmt >= (firstShortFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStore = (value >> 3); + if (!((signedValueToStore >= 0) + && (signedValueToStore <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))),signedValueToStore); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l4; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l4: + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + return; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!(((index >= 1) && (index <= (stackPointerIndexForFrame(spouseFP)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* temporary:in:put: */ + if ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory())) { + /* mtemporary:in:put: */ + longAtput(((index - 1) < ((frameNumArgs1 = ((mframeCogMethod(spouseFP))->cmNumArgs))) + ? (spouseFP + FoxCallerSavedIP) + ((frameNumArgs1 - (index - 1)) * BytesPerWord) + : ((spouseFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs1 - (index - 1)) * BytesPerWord)),value); + } + else { + /* itemporary:in:put: */ + if ((index - 1) < ((frameNumArgs = byteAt((spouseFP + FoxIFrameFlags) + 1)))) { + longAtput((spouseFP + FoxCallerSavedIP) + ((frameNumArgs - (index - 1)) * BytesPerWord),value); + } + else { + longAtput(((spouseFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - (index - 1)) * BytesPerWord),value); + } + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); +} + + +/* The core design of the opensmalltalk-vm's closure implementation has as + its rationale + breaking the dependence of a block on the temporaries of any outer + context, principally + the home context. The scheme is explained here: + + http://www.mirandabanda.org/cogblog/2009/01/14/under-cover-contexts-and-the-big-frame-up + Hence when a frame is married the VM *does not* copy temporary variables + into the context + object, copying only arguments (if any). See marryFrame:SP: and + genGetActiveContextLarge:inBlock:. Note that if a married frame and + context are divorced (which occurs when an inst var of a context is + assigned to or when the stack page on which the frame resides is reused + for newer frames) then + the full frame contents are written to the context, bringing it fully + up-to-date so that it becomes + the implementation of the relevant method/block activation, replacing the + frame. + Except in extraordinary circumstances there is no utility in accessing the + temporary variables of a + widowed (returned-from) context. The frame and context are married as soon + as thisContext is + referenced (either explicitly, or by creating a closure, or accessing the + sender of another context). + At this point there is no guarantee that temp vars have their final value, + and so there is no point + preserving them because their values at the time of marriage will very + likely be stale by the time + the context is widowed. N.B. One could imagine having the VM write the + values of frame + temporaries back to the context if it existed, but such a scheme would be + unacceptably slow. + + While I (eem 4/23/2026) have yet to see an example where temps should be + preserved, this + mechanism is provided just in case. It must be invoked after the last + modification of a temporary. + For example, the assignments to the completionStatus temps in + BlockClosure>>#ensure: and + BlockClosure>>#ifCurtailed: (as of 11/18/2025) are accessed from *while + the contexts are still active* + from Context>>#finishIncompleteUnwindsUpTo: and hence if married the VM + will access the temps + from the frame, not the context, and hence up-to-date values will be + answered. + There is a complication. Only methods and full blocks have an easily + accessible temporary count. + The original SqueakV3PlusClosures BlockClosure uses explicit pushNil + bytecodes to establish + block-local temps, and machinery such as + StackToRegisterMappingCogit>>#scanBlock: and + BlockLocalTempCounter to compute the number of temps. Hence this primitive + is actually two, + one that takes no arguments, accesses the temp count from the receiver's + method if possible, + failing if not with the error code PrimErrUnsupported, and one that takes + the temp count as its + argument. */ + + /* StackInterpreterPrimitives>>#primitiveContextPreserveTemps */ +static void +primitiveContextPreserveTemps(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt closureRcvr; + sqInt header; + sqInt methodPointer; + usqInt numArgs; + sqInt numNonArgTemps; + usqInt numTemps; + sqInt receiverCtxt; + sqInt senderOop; + char *theFP; + char *theSP; + + receiverCtxt = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + assert(isContext(receiverCtxt)); + + /* this is required for isWidowedContext: et al to function... */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* If the receiver is a single (heap) context then temps are already preserved. In normal use the + receiver would be married (e.g. as in executing thisContext preserveTemps where it would be + the current frame. But the execution simulation machinery could send it to a heap context. */ + if (!(((((longAt((void *)((receiverCtxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return; + } + + /* If the receiver is widowed then it's too late... */ + if (isWidowedContext(receiverCtxt)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + return; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((receiverCtxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + numArgs = /* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)); + if (GIV(argumentCount)) { + assert((methodArgumentCount()) == 1); + numTemps = longAt(GIV(stackPointer)); + if (!(((((numTemps) & 7) == 1)) + && (((((numTemps = (numTemps >> 3))) >= 0) && (numTemps <= ((LargeContextSlots - ReceiverIndex) - 1)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + else { + /* is this a normal method activation and/or a full block closure activation or not? + If not, fail and allow the image to compute the temp count. */ + if (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0)) { + closureRcvr = longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((numArgs << (shiftForWord()))))); + + /* begin isVanillaBlockClosure: */ + assert((isPointers(closureRcvr)) + && ((numSlotsOf(closureRcvr)) >= ClosureFirstCopiedValueIndex)); + if (((((longAt((void *)((closureRcvr + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + } + + /* N.B. the temp count includes the argument count */ + methodPointer = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + + /* begin tempCountOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(header)) >> MethodHeaderTempCountShift) & 0x3F; + } + numNonArgTemps = numTemps - numArgs; + theSP = (/* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver)) - (numNonArgTemps * BytesPerWord); + updateStateOfSpouseContextForFrameWithSP(theFP, theSP); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* Special version of primitiveSize for accessing contexts. + Written to be varargs for use from mirror primitives. */ + + /* StackInterpreterPrimitives>>#primitiveContextSize */ +static void +primitiveContextSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt sp1; + sqInt spSqInt; + sqInt sz; + sqInt totalLength; + + rcvr = longAt(GIV(stackPointer)); + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + if ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + sp1 = stackPointerIndexForFrame(frameOfMarriedContext(rcvr)); + assert((ReceiverIndex + ((sp1 >> 3))) < (lengthOf(rcvr))); + sz = sp1; + goto l3; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + sz = 0; + goto l3; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(rcvr))); + sz = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l3:; + } + else { + sz = totalLength - fixedFields; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)sz << 3) | 1)); +} + + +/* Set or clear the flag that controls whether modifications of + the Display object are propagated to the underlying + platform's screen. */ + + /* StackInterpreterPrimitives>>#primitiveDeferDisplayUpdates */ +static void +primitiveDeferDisplayUpdates(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt flag; + + if (cannotDeferDisplayUpdates) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + flag = longAt(GIV(stackPointer)); + if (flag == GIV(trueObj)) { + deferDisplayUpdates = 1; + } + else { + if (flag == GIV(falseObj)) { + deferDisplayUpdates = 0; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; +} + + +/* Simulate an primitiveExternalCall invocation (e.g. for the Debugger). Do + not cache anything. + e.g. ContextPart>>tryNamedPrimitiveIn: aCompiledMethod for: aReceiver + withArgs: arguments */ + + /* StackInterpreterPrimitives>>#primitiveDoNamedPrimitiveWithArgs */ +static void +primitiveDoNamedPrimitiveWithArgs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*addr)(void); + sqInt argumentArray; + usqInt arraySize; + sqInt ccIndex; + usqInt firstBytecode; + sqInt fmt; + sqInt functionLength; + sqInt functionName; + sqInt header; + sqInt index; + sqInt isArray; + sqInt methodArg; + sqInt methodHeader; + sqInt moduleLength; + sqInt moduleName; + usqInt newObj; + usqInt numBytes; + usqInt numSlots; + sqInt primRcvr; + sqInt spec; + sqInt successBoolean; + sqInt top; + sqInt valuePointer; + + GIV(metaAccessorDepth) = -2; + + /* See checkForAndFollowForwardedPrimitiveState */ + argumentArray = longAt(GIV(stackPointer)); + methodArg = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (!((/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (/* isOopCompiledMethod: */ + ((!(methodArg & (tagMask())))) + && (((byteAt((void *)(methodArg + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -2; + return; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (!(arraySize <= (LargeContextSlots - CtxtTempFrameStart))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -2; + return; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodArg)); + header = longAt((void *)((methodArg + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (!(((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) > 2)) { + /* primitiveFailFor: */ + GIV(primFailCode) = -3; + return; + } + spec = longAt((void *)((methodArg + BaseHeaderSize) + (1U << (shiftForWord())))); + + /* first literal */ + + /* begin isInstanceOfClassArray: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((spec & (tagMask())) != 0)) { + isArray = 0; + goto l2; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(spec))); + ccIndex = (longAt((void *)(spec))) & (classIndexMask()); + isArray = ClassArrayCompactIndex == ccIndex; + /* end isInstanceOfClassArray: */ +l2: + if (!(isArray + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(spec)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(spec + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(spec - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4) + && ((/* primitiveIndexOfMethod:header: */ + (((methodHeader & AlternateHeaderHasPrimFlag) != 0) + ? ((firstBytecode = (methodArg + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize), + (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8)))) + : 0)) == PrimNumberExternalCall)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -3; + return; + } + if (!(((((usqInt)(methodHeader)) >> MethodHeaderArgCountShift) & 15) == arraySize)) { + /* primitiveFailFor: */ + GIV(primFailCode) = -2; + return; + } + moduleName = longAt((void *)((spec + BaseHeaderSize) + (0U << (shiftForWord())))); + if (moduleName == GIV(nilObj)) { + moduleLength = 0; + } + else { + successBoolean = /* isBytes: */ + ((!(moduleName & (tagMask())))) + && (((byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())); + + /* begin success: */ + if (!successBoolean) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + moduleLength = ((((/* begin numSlotsOf: */ + assert((classIndexOf(moduleName)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(moduleName + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(moduleName - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + } + functionName = longAt((void *)((spec + BaseHeaderSize) + (1U << (shiftForWord())))); + successBoolean = /* isBytes: */ + ((!(functionName & (tagMask())))) + && (((byteAt((void *)(functionName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())); + + /* begin success: */ + if (!successBoolean) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(functionName + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + functionLength = ((((/* begin numSlotsOf: */ + assert((classIndexOf(functionName)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(functionName + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(functionName - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = -3; + return; + } + addr = ioLoadExternalFunctionOfLengthFromModuleOfLengthMetadataInto(functionName + BaseHeaderSize, functionLength, moduleName + BaseHeaderSize, moduleLength, (&GIV(metaAccessorDepth))); + + /* N.B. the accessor depth is the second byte of the primitive's metadata; + the first byte is various flags (currently l.s.b. = use fast C linkage). */ + GIV(metaAccessorDepth) = (addr + ? (((usqInt)(GIV(metaAccessorDepth))) >> 8) + : -2); + if (!addr) { + /* primitiveFailFor: */ + GIV(primFailCode) = -1; + return; + } + + /* begin eeInstantiateClassIndex:format:numSlots: */ + assert((knownClassAtIndex(ClassArrayCompactIndex)) != GIV(nilObj)); + assert((arrayFormat()) == (instSpecOfClass(knownClassAtIndex(ClassArrayCompactIndex)))); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (4 * BytesPerOop); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateNewSpaceSlots:format:classIndex:"); + GIV(tempOop) = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) 4)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + GIV(tempOop) = newObj; + /* end eeInstantiateClassIndex:format:numSlots: */ +l1: + + /* begin popStack */ + valuePointer = (argumentArray = longAt(GIV(stackPointer))); + GIV(stackPointer) += BytesPerWord; + valuePointer = (argumentArray); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(tempOop))) + && (!(isForwarded(GIV(tempOop))))); + assert(validStorePointerUncheckedArgs(0, GIV(tempOop), valuePointer)); + longAtput((void *)((GIV(tempOop) + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin popStack */ + valuePointer = (primRcvr = longAt(GIV(stackPointer))); + GIV(stackPointer) += BytesPerWord; + valuePointer = (primRcvr); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(tempOop))) + && (!(isForwarded(GIV(tempOop))))); + assert(validStorePointerUncheckedArgs(1, GIV(tempOop), valuePointer)); + longAtput((void *)((GIV(tempOop) + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + valuePointer = top; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(tempOop))) + && (!(isForwarded(GIV(tempOop))))); + assert(validStorePointerUncheckedArgs(2, GIV(tempOop), valuePointer)); + longAtput((void *)((GIV(tempOop) + BaseHeaderSize) + (2U << (shiftForWord()))),valuePointer); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + valuePointer = top; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(tempOop))) + && (!(isForwarded(GIV(tempOop))))); + assert(validStorePointerUncheckedArgs(3, GIV(tempOop), valuePointer)); + longAtput((void *)((GIV(tempOop) + BaseHeaderSize) + (3U << (shiftForWord()))),valuePointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),primRcvr); + GIV(argumentCount) = arraySize; + for (index = 1; index <= arraySize; index += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + } + primitiveFunctionPointer = addr; + dispatchFunctionPointer(addr); + if (GIV(primFailCode)) { + /* begin pop: */ + GIV(stackPointer) += (arraySize + 1) * BytesPerWord; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((GIV(tempOop) + BaseHeaderSize) + (3U << (shiftForWord()))))); + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((GIV(tempOop) + BaseHeaderSize) + (2U << (shiftForWord()))))); + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((GIV(tempOop) + BaseHeaderSize) + (1U << (shiftForWord()))))); + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((GIV(tempOop) + BaseHeaderSize) + (0U << (shiftForWord()))))); + GIV(argumentCount) = 3; + + /* Must reset primitiveFunctionPointer for checkForAndFollowForwardedPrimitiveState */ + primitiveFunctionPointer = primitiveDoNamedPrimitiveWithArgs; + + /* Hack. A nil prim error code (primErrorCode = 1) is interpreted by the image + as meaning this primitive is not implemented. So to pass back nil as an error + code we use -1 to indicate generic failure. */ + if (GIV(primFailCode) == 1) { + GIV(primFailCode) = -1; + } + } +} + + +/* Implement either ProtoObject>>tryPrimitive: primIndex withArgs: argArray + or Context>>receiver: anObject tryPrimitive: primIndex withArgs: argArray. + If this primitive fails, arrange that its error code is a negative + integer, to + distinguish between this failing and the primitive it invokes failing. */ + + /* StackInterpreterPrimitives>>#primitiveDoPrimitiveWithArgs */ +static void +primitiveDoPrimitiveWithArgs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt argumentArray; + usqInt arraySize; + sqInt index; + usqInt numSlots; + sqInt primIdx; + sqInt rcvr; + sqInt referent; + sqInt savedNumArgs; + + GIV(metaAccessorDepth) = -2; + + /* See checkForAndFollowForwardedPrimitiveState */ + if (!(((GIV(argumentCount) >= 2) && (GIV(argumentCount) <= 3)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -PrimErrUnsupported; + return; + } + argumentArray = longAt(GIV(stackPointer)); + primIdx = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((((primIdx) & 7) == 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -PrimErrBadArgument; + return; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (!(arraySize <= (LargeContextSlots - CtxtTempFrameStart))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -PrimErrLimitExceeded; + return; + } + primIdx = (primIdx >> 3); + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primIdx)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primIdx]))); + if (!primitiveFunctionPointer) { + primitiveFunctionPointer = primitiveDoPrimitiveWithArgs; + + /* primitiveFailFor: */ + GIV(primFailCode) = -PrimErrBadIndex; + return; + } + + /* Pop primIndex and argArray, then push args in place... */ + if (((savedNumArgs = GIV(argumentCount))) == 3) { + /* actual receiver */ + GIV(tempOop2) = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + + /* receiver for primitive */ + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(rcvr)); + referent = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + rcvr = referent; + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 3 * BytesPerWord),rcvr); + } + else { + /* begin pop: */ + GIV(stackPointer) += 2 * BytesPerWord; + } + + /* ...and receiver if the three arg form */ + GIV(argumentCount) = arraySize; + index = 1; + while (index <= arraySize) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + index += 1; + } + if ((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex) { + externalQuickPrimitiveResponse(); + GIV(tempOop2) = 0; + return; + } + + /* We use tempOop instead of pushRemappableOop:/popRemappableOop here because in + the Cogit primitiveEnterCriticalSection, primitiveSignal, primitiveResume et al longjmp back + to either the interpreter or machine code, depending on the process activated. So if we're + executing one of these primitives, control won't actually return here and the matching + popRemappableOop: wouldn't occur, potentially overflowing the remap buffer. + Note that while recursion could occur (nil tryPrimitive: 118 withArgs: #(118 #(110 #()))) + it counts as shooting oneself in the foot. */ + + /* prim might alloc/gc */ + /* Run the primitive (sets primFailCode) */ + GIV(tempOop) = argumentArray; + GIV(metaAccessorDepth) = ((sqInt)((primitiveMetadataTable[primIdx]))) >> SpurPrimitiveAccessorDepthShift; + + /* See checkForAndFollowForwardedPrimitiveState */ + if (!(slowPrimitiveResponse())) { + if (!GIV(tempOop)) { + return; + } + + /* the primitive failed in a recursive invocation. can't fix things with no value... */ + + /* begin pop: */ + GIV(stackPointer) += arraySize * BytesPerWord; + if (savedNumArgs == 3) { + rcvr = longAt(GIV(stackPointer)); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(tempOop2)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + } + longAtput((GIV(stackPointer) -= BytesPerWord),(((usqInt)primIdx << 3) | 1)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(tempOop)); + primitiveFunctionPointer = primitiveDoPrimitiveWithArgs; + GIV(argumentCount) = savedNumArgs; + } + + /* If primitive failed, then restore state for failure code */ + GIV(tempOop) = (GIV(tempOop2) = 0); +} + + +/* Call an external primitive. External primitive methods first literals are + an array of + * The module name (String | Symbol) + * The function name (String | Symbol) + * The session ID (SmallInteger) [OBSOLETE], or in Spur, the metadata + (accessorDepth and flags; Integer)) + * The function index (Integer) in the externalPrimitiveTable + For fast interpreter dispatch in subsequent invocations the + primitiveFunctionPointer in the method cache is rewritten, either to the + function itself, or to zero if the external + function is not found. This allows for fast responses as long as the + method stays in + the cache. The cache rewrite relies on lastMethodCacheProbeWrite which is + set in + addNewMethodToCache:. + Now that the VM flushes function addresses from its tables, the session ID + is obsolete, + but it is kept for backward compatibility. Also, a failed lookup is + reported specially. If a + method has been looked up and not been found, the function address is + stored as -1 + (i.e., the SmallInteger -1 to distinguish from 16rFFFFFFFF which may be + returned from + lookup), and the primitive fails with PrimErrNotFound. + */ +/* because the primitive accesses newMethod's first literal, which is checked + for explicitly in checkForAndFollowForwardedPrimitiveState + */ +/* since call may invoke a callback */ +/* since external primitive linkage is in first literal... */ +/* Check for it being a method for primitiveDoPrimitiveWithArgs. + Fetch the first literal of the method; check its an Array of length 4. + Look at the function index in case it has been loaded before */ + + /* StackInterpreterPrimitives>>#primitiveExternalCall */ +static void +primitiveExternalCall(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*addr)(void); + sqInt index; + sqInt lit; + usqInt numSlots; + sqInt reasonCode; + + if (!((/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((literalCountOf(GIV(newMethod))) > 0) + && (((lit = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + (1U << (shiftForWord()))))), + (/* isArray: */ + ((!(lit & (tagMask())))) + && (((byteAt((void *)(lit + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(lit)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(lit + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(lit - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4) + && (((index = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord()))))))), + (((index) & 7) == 1))))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadMethod; + return; + } + index = (index >> 3); + + /* Check if we have already looked up the function and failed. */ + if (index < 0) { + /* Function address was not found in this session, + Void the primitive function. */ + rewriteMethodCacheEntryForExternalPrimitiveToFunction(0); + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNotFound; + return; + } + + /* Try to call the function directly */ + if ((index > 0) + && (index <= MaxExternalPrimitiveTableSize)) { + addr = externalPrimitiveTable[index - 1]; + if (addr) { + rewriteMethodCacheEntryForExternalPrimitiveToFunction(addr); + + /* begin callExternalPrimitive: */ + primitiveFunctionPointer = addr; + dispatchFunctionPointer(addr); + + /* begin maybeRetryPrimitiveOnFailure */ + if (GIV(primFailCode)) { + retryPrimitiveOnFailure(); + } + return; + } + + /* if we get here, then an index to the external prim was + kept on the ST side although the underlying prim + table was already flushed */ + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNamedInternal; + return; + } + + /* Clean up session id/metadata and external primitive index */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(lit)) + && (!(isForwarded(lit)))); + assert(validStorePointerUncheckedArgs(ExternalCallLiteralFlagsIndex, lit, ConstZero)); + longAtput((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFlagsIndex) << (shiftForWord()))))),ConstZero); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(lit)) + && (!(isForwarded(lit)))); + assert(validStorePointerUncheckedArgs(ExternalCallLiteralTargetFunctionIndex, lit, ConstZero)); + longAtput((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord()))))),ConstZero); + + /* The function has not been loaded yet. Attempt to link it, cache it, and call it. */ + addr = linkExternalCallerrInto(lit, (&GIV(primFailCode))); + if (!addr) { + assert((fetchPointerofObject(ExternalCallLiteralFlagsIndex, lit)) == ConstZero); + reasonCode = (GIV(primFailCode) + ? GIV(primFailCode) + : PrimErrNotFound); + GIV(primFailCode) = reasonCode; + return; + } + + /* begin callExternalPrimitive: */ + primitiveFunctionPointer = addr; + dispatchFunctionPointer(addr); + + /* begin maybeRetryPrimitiveOnFailure */ + if (GIV(primFailCode)) { + retryPrimitiveOnFailure(); + } +} + + +/* Primitive. Search up the context stack for the next method context marked + for exception handling starting at the receiver. Return nil if none found */ + + /* StackInterpreterPrimitives>>#primitiveFindHandlerContext */ +static void +primitiveFindHandlerContext(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt handlerOrNilOrZero; + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + handlerOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(PrimNumberHandlerMarker, longAt(GIV(stackPointer)), GIV(nilObj)); + if (!handlerOrNilOrZero) { + handlerOrNilOrZero = GIV(nilObj); + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),handlerOrNilOrZero); +} + + +/* Primitive. Search up the context stack for the next method context marked + for unwind + handling from the receiver up to but not including the argument. Return + nil if none found. + */ + + /* StackInterpreterPrimitives>>#primitiveFindNextUnwindContext */ +static void +primitiveFindNextUnwindContext(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt calleeContext; + sqInt handlerOrNilOrZero; + sqInt senderContext; + sqInt senderOop; + sqInt startContext; + sqInt stopContext; + char *theFP; + char *theFP1; + char *theFPAbove; + sqInt theMethod; + StackPage *thePage; + char *theSP; + + stopContext = longAt(GIV(stackPointer)); + calleeContext = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((stopContext == GIV(nilObj)) + || (/* isContext: */ + ((!(stopContext & (tagMask())))) + && (((longAt((void *)(stopContext))) & (classIndexMask())) == ClassMethodContextCompactIndex)))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* The following should never be true, but developing full blocks, early in September + 2016 we were seeing invalid invocations of this primitive.. Hence the assert: */ + assert(stopContext != calleeContext); + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((calleeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(calleeContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((calleeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (longAt(theFP + FoxSavedFP)) { + /* begin findMethodWithPrimitive:FromFP:UpToContext: */ + theFP1 = ((char *)(longAt(theFP + FoxSavedFP))); + theFPAbove = ((char *)(longAt(theFP + FoxSavedFP))); + do { + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP1 + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP1 + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP1 + FoxIFrameFlags) + 2)) != 0)) + && (stopContext == (longAt(theFP1 + FoxThisContext)))) { + handlerOrNilOrZero = 0; + goto l1; + } + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP1 + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP1 + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP1 + FoxIFrameFlags) + 3)) != 0))) { + theMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP1 + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP1))->methodObject) + : longAt(theFP1 + FoxMethod)); + if ((primitiveIndexOf(theMethod)) == PrimNumberUnwindMarker) { + if (theFP1 == theFPAbove) { + theSP = findSPOfon(theFP1, stackPageFor(theFP1)); + } + else { + /* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFPAbove))); + theSP = (theFPAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFPAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFPAbove))->cmNumArgs) + : byteAt((theFPAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP1 + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP1 + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP1 + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP1))); + handlerOrNilOrZero = longAt(theFP1 + FoxThisContext); + goto l1; + } + handlerOrNilOrZero = marryFrameSP(theFP1, theSP); + goto l1; + } + } + theFPAbove = theFP1; + theFP1 = ((char *)(longAt(theFP1 + FoxSavedFP))); + } while(theFP1 != 0); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFPAbove)); + thePage = stackPageAtpages(pageIndexFor(theFPAbove), GIV(pages)); + senderContext = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(senderContext)); + assert((senderContext == (nilObject())) + || (isContext(followMaybeForwarded(senderContext)))); + if (!(/* isContext: */ + ((!(senderContext & (tagMask())))) + && (((longAt((void *)(senderContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + handlerOrNilOrZero = GIV(nilObj); + goto l1; + } + handlerOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(PrimNumberUnwindMarker, senderContext, stopContext); + /* end findMethodWithPrimitive:FromFP:UpToContext: */ +l1:; + } + else { + handlerOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(PrimNumberUnwindMarker, frameCallerContext(theFP), stopContext); + } + } + else { + startContext = longAt((void *)((calleeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + if (/* isContext: */ + ((!(startContext & (tagMask())))) + && (((longAt((void *)(startContext))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + handlerOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(PrimNumberUnwindMarker, startContext, stopContext); + } + else { + handlerOrNilOrZero = 0; + } + } + if (!handlerOrNilOrZero) { + handlerOrNilOrZero = GIV(nilObj); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),handlerOrNilOrZero); +} + + +/* Do a full garbage collection. In SqueakV3ObjectMemory, answer the number + of bytes available (including swap space if dynamic memory management is + supported). In Spur, answer the size of the largest free chunk. */ +/* Do a full garbage collection. In SqueakV3ObjectMemory, answer the number + of bytes available (including swap space if dynamic memory management is + supported). In Spur, answer the size of the largest free chunk. */ + + /* StackInterpreterPrimitives>>#primitiveFullGC */ +static void +primitiveFullGC(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integer; + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + integer = fullGC(); + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); +} + + /* StackInterpreterPrimitives>>#primitiveGetSetFFIExceptionHandling */ +static void +primitiveGetSetFFIExceptionHandling(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt integer; + + if (!GIV(argumentCount)) { + integer = (ioCanCatchFFIExceptions() + ? ffiExceptionResponse + : -1); + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); + return; + } + if (!(ioCanCatchFFIExceptions())) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + if (!(GIV(argumentCount) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + arg = longAt(GIV(stackPointer)); + if (!(((((arg) & 7) == 1)) + && (((((arg = (arg >> 3))) >= -1) && (arg <= 1))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + ffiExceptionResponse = arg; + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* Do a quick, incremental garbage collection and return the number of bytes + immediately available. + (Note: more space may be made available by doing a full garbage + collection. + */ +/* Do a quick, incremental garbage collection and return the number of bytes + immediately available. + (Note: more space may be made available by doing a full garbage + collection. + */ + + /* StackInterpreterPrimitives>>#primitiveIncrementalGC */ +static void +primitiveIncrementalGC(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerVal; + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* begin scavengingGC */ + scavengingGCTenuringIf(TenureByAge); + integerVal = ((((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes())) < 0) ? 0 : ((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes()))); + + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),(((usqInt)integerVal << 3) | 1)); +} + + +/* because of externalInstVar:ofContext: below */ + + /* StackInterpreterPrimitives>>#primitiveInstVarAt */ +static void +primitiveInstVarAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt totalLength; + sqInt value; + + index = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 1) + && (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + return; + } + index = (index >> 3); + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l3; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l3; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l3; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l3; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l3; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l3: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l2; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l2; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l2: + if (!((index >= 1) + && (index <= fixedFields))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + value = externalInstVarofContext(index - 1, rcvr); + } + else { + /* begin subscript:with:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + value = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord())))))); + goto l1; + } + if (fmt >= (firstByteFormat())) { + value = (((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + (index - 1)))) << 3) | 1); + goto l1; + } + if (fmt >= (firstShortFormat())) { + value = (((usqInt)(((unsigned short) (shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 1)))))))) << 3) | 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + value = positive64BitIntegerFor(long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 3)))))); + goto l1; + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + value = (((usqInt)((((usqInt)(long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + /* end subscript:with:format: */ +l1:; + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); +} + + /* StackInterpreterPrimitives>>#primitiveInstVarAtPut */ +static void +primitiveInstVarAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fieldIndex; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + sqInt newValue; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt signedValueToStore; + sqInt totalLength; + usqLong unsigned64BitValueToStore; + unsigned int unsignedValueToStore; + + newValue = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 2) + && (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + return; + } + index = (index >> 3); + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + if (!((index >= 1) + && (index <= fixedFields))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + externalInstVarofContextput(index - 1, rcvr, newValue); + } + else { + /* begin subscript:with:storing:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + fieldIndex = index - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, rcvr, newValue)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(newValue & (tagMask())))) + && (oopisLessThan(newValue, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),newValue); + goto l3; + } + if (fmt >= (firstByteFormat())) { + if (!((((newValue) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l3; + } + signedValueToStore = (newValue >> 3); + if (!((signedValueToStore >= 0) + && (signedValueToStore <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l3; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + (index - 1)),signedValueToStore); + goto l3; + } + if (fmt >= (firstShortFormat())) { + if (!((((newValue) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l3; + } + signedValueToStore = (newValue >> 3); + if (!((signedValueToStore >= 0) + && (signedValueToStore <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l3; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 1)))),signedValueToStore); + goto l3; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(newValue); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l3; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(newValue); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l3:; + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); +} + + +/* Primitive. 'Invoke' an object like a function, sending the special message + run: originalSelector with: arguments in: aReceiver. + */ + + /* StackInterpreterPrimitives>>#primitiveInvokeObjectAsMethod */ +static void +primitiveInvokeObjectAsMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt lookupClassTag; + usqInt newObj; + usqInt numBytes; + sqInt runArgs; + sqInt runReceiver; + sqInt tagBits; + sqInt top; + sqInt valuePointer; + + /* begin eeInstantiateClassIndex:format:numSlots: */ + assert((GIV(argumentCount) >= 0) + && ((knownClassAtIndex(ClassArrayCompactIndex)) != GIV(nilObj))); + assert((arrayFormat()) == (instSpecOfClass(knownClassAtIndex(ClassArrayCompactIndex)))); + + /* begin allocateNewSpaceSlots:format:classIndex: */ + if (GIV(argumentCount) >= (numSlotsMask())) { + if (GIV(argumentCount) > 0xFFFFFFFFU) { + runArgs = ((usqInt) null); + goto l1; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (GIV(argumentCount) * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((GIV(argumentCount) < 1 + ? 8 /* allocationUnit */ + : GIV(argumentCount) * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateNewSpaceSlots:format:classIndex:"); + runArgs = 0; + goto l1; + } + } + if (GIV(argumentCount) >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),GIV(argumentCount)); + longAtput((void *)(GIV(freeStart) + 4),((sqInt)((usqInt)((numSlotsMask())) << (numSlotsHalfShift())))); + long64Atput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + } + else { + long64Atput((void *)(newObj),((((((usqLong) GIV(argumentCount))) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + runArgs = newObj; + /* end eeInstantiateClassIndex:format:numSlots: */ +l1: + for (i = (GIV(argumentCount) - 1); i >= 0; i += -1) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + valuePointer = top; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(runArgs)) + && (!(isForwarded(runArgs)))); + assert(validStorePointerUncheckedArgs(i, runArgs, valuePointer)); + longAtput((void *)((runArgs + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + + /* begin popStack */ + runReceiver = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* setup send of newMethod run: originalSelector with: runArgs in: runReceiver */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(messageSelector)); + + /* original selector */ + longAtput((GIV(stackPointer) -= BytesPerWord),runArgs); + longAtput((GIV(stackPointer) -= BytesPerWord),runReceiver); + + /* stack is clean here */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorRunWithIn) << (shiftForWord())))))); + GIV(argumentCount) = 3; + lookupClassTag = /* fetchClassTagOf: */ + ((tagBits = GIV(newMethod) & (tagMask())) + ? tagBits + : (longAt((void *)(GIV(newMethod)))) & (classIndexMask())); + findNewMethodInClassTag(lookupClassTag); + executeNewMethod(); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; +} + + +/* Primitive. Answer an Array with the current long-running primitive method + identified by + the heartbeat, the minimum number of milliseconds it was active for, and + the milliseconds + of GC activity there-in, or nil if none. */ + + /* StackInterpreterPrimitives>>#primitiveLongRunningPrimitive */ +#if LRPCheck +EXPORT(void) +primitiveLongRunningPrimitive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong gcms; + sqInt lrpcm; + sqLong primms; + sqInt result; + + lrpcm = 0; + sqLowLevelMFence(); + + /* Since the longRunningPrimitiveCheckMethod is sampled at + interrupt time be careful to validate it before returning it. */ + if ((GIV(longRunningPrimitiveStopUsecs) > GIV(longRunningPrimitiveStartUsecs)) + && ((((lrpcm = GIV(longRunningPrimitiveCheckMethod)))) + && ((addressCouldBeObj(lrpcm)) + && (((byteAt((void *)(lrpcm + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))))) { + result = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 3); + primms = ((GIV(longRunningPrimitiveStopUsecs) - GIV(longRunningPrimitiveStartUsecs)) + 500) / 1000; + gcms = (GIV(longRunningPrimitiveGCUsecs) + 500) / 1000; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(0, result, lrpcm)); + assert(isNonImmediate(result)); + if (oopisGreaterThanOrEqualTo(result, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(lrpcm & (tagMask())))) + && (oopisLessThan(lrpcm, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(result + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(result); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((result + BaseHeaderSize) + (0U << (shiftForWord()))),lrpcm); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(result)) + && (!(isForwarded(result)))); + assert(validStorePointerUncheckedArgs(1, result, (((usqInt)primms << 3) | 1))); + longAtput((void *)((result + BaseHeaderSize) + (1U << (shiftForWord()))),(((usqInt)primms << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(result)) + && (!(isForwarded(result)))); + assert(validStorePointerUncheckedArgs(2, result, ((gcms << 3) | 1))); + longAtput((void *)((result + BaseHeaderSize) + (2U << (shiftForWord()))),((gcms << 3) | 1)); + } + else { + result = GIV(nilObj); + } + + /* a hit */ + voidLongRunningPrimitive("get"); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); +} +#endif /* LRPCheck */ + + +/* This primitive is assumed to be fast (see e.g. + MethodDictionary>>includesKey:) so make it so. + N.B. Works correctly for cogged methods too. */ + + /* StackInterpreterPrimitives>>#primitiveObjectPointsTo */ +static void +primitiveObjectPointsTo(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong fmt; + sqLong header; + sqInt headerSqInt; + sqInt i; + sqInt methodHeader; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + char *sp; + sqInt thang; + sqInt trueOrFalse; + + thang = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((rcvr & (tagMask())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),GIV(falseObj)); + return; + } + + /* Inlined version of lastPointerOf: for speed in determining if rcvr is a context. */ + header = long64At((void *)(rcvr)); + fmt = (((usqLong)(header)) >> (formatShift())) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && ((header & (classIndexMask())) == ClassMethodContextCompactIndex)) { + if (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + trueOrFalse = marriedContextpointsTostackDeltaForCurrentFrame(rcvr, thang, 2); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (trueOrFalse + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return; + } + } + + /* contexts end at the stack pointer */ + numSlots = CtxtTempFrameStart + (fetchStackPointerOf(rcvr)); + } + else { + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + } + } + else { + if (fmt < (firstCompiledMethodFormat())) { + /* begin pop:thenPushBool: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),GIV(falseObj)); + return; + } + + /* no pointers + CompiledMethod: contains both pointers and bytes: */ + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(rcvr)); + headerSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + if (methodHeader == thang) { + /* begin pop:thenPushBool: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),GIV(trueObj)); + return; + } + numSlots = ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart; + } + assert((((numSlots - 1) * BytesPerOop) + BaseHeaderSize) == (lastPointerOf(rcvr))); + for (i = BaseHeaderSize; i <= (((numSlots - 1) * BytesPerOop) + BaseHeaderSize); i += BytesPerOop) { + if ((longAt((void *)(rcvr + i))) == thang) { + /* begin pop:thenPushBool: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),GIV(trueObj)); + return; + } + } + + /* begin pop:thenPushBool: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),GIV(falseObj)); +} + + /* StackInterpreterPrimitives>>#primitivePerform */ +static void +primitivePerform(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt lookupClassTag; + sqInt newReceiver; + usqInt performMethod; + sqInt tagBits; + + performMethod = GIV(newMethod); + GIV(messageSelector) = longAt(GIV(stackPointer) + ((GIV(argumentCount) - 1) * BytesPerWord)); + newReceiver = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* NOTE: the following lookup may fail and be converted to #doesNotUnderstand:, + so we must adjust argumentCount and slide args now, so that will work. + Slide arguments down over selector */ + GIV(argumentCount) -= 1; + for (i = GIV(argumentCount); i >= 1; i += -1) { + longAtput(GIV(stackPointer) + (i * BytesPerWord),longAt(GIV(stackPointer) + ((i - 1) * BytesPerWord))); + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + lookupClassTag = /* fetchClassTagOf: */ + ((tagBits = newReceiver & (tagMask())) + ? tagBits + : (longAt((void *)(newReceiver))) & (classIndexMask())); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), /* fetchClassTagOf: */ + ((tagBits = newReceiver & (tagMask())) + ? tagBits + : (longAt((void *)(newReceiver))) & (classIndexMask()))); + if (printOnTrace()) { + printActivationNameForSelectorstartClass(GIV(messageSelector), classForClassTag(lookupClassTag)); + cr(); + } + findNewMethodInClassTag(lookupClassTag); + + /* Only test CompiledMethods for argument count - other objects will have to take their chances */ + if (!((/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && ((argumentCountOf(GIV(newMethod))) == GIV(argumentCount)))) { + /* Slide the args back up (sigh) and re-insert the selector. */ + + /* begin unPop: */ + GIV(stackPointer) -= 1 * BytesPerWord; + for (i = 1; i <= GIV(argumentCount); i += 1) { + longAtput(GIV(stackPointer) + ((i - 1) * BytesPerWord),longAt(GIV(stackPointer) + (i * BytesPerWord))); + } + longAtput(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord),GIV(messageSelector)); + GIV(argumentCount) += 1; + GIV(newMethod) = performMethod; + + /* Must reset primitiveFunctionPointer for checkForAndFollowForwardedPrimitiveState */ + primitiveFunctionPointer = primitivePerform; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + executeNewMethod(); + + /* Recursive xeq affects primErrorCode */ + + /* begin initPrimCall */ + GIV(primFailCode) = 0; +} + + +/* Pin or unpin the receiver, i.e. make it immobile or mobile, based on the + argument. Answer whether the object was already pinned. N.B. pinning does + *not* prevent + an object from being garbage collected. */ + + /* StackInterpreterPrimitives>>#primitivePin */ +static void +primitivePin(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt boolean; + sqInt obj; + sqInt wasPinned; + + obj = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((obj & (tagMask())) != 0)) + || ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + boolean = longAt(GIV(stackPointer)); + if (!((boolean == GIV(falseObj)) + || (boolean == GIV(trueObj)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if ((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + wasPinned = GIV(trueObj); + if (boolean != wasPinned) { + setIsPinnedOfto(obj, 0); + } + } + else { + wasPinned = GIV(falseObj); + if (boolean == GIV(trueObj)) { + if ((/* isContext: */ + ((!(obj & (tagMask())))) + && (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(obj))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if (!(pinObject(obj))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + return; + } + } + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),wasPinned); +} + + +/* Primitive. Indicate the semaphore to be signalled for upon garbage + collection + */ + + /* StackInterpreterPrimitives>>#primitiveSetGCSemaphore */ +EXPORT(void) +primitiveSetGCSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt integerPointer; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + index = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + index = 0; + } + if (!GIV(primFailCode)) { + GIV(gcSemaphoreIndex) = index; + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } +} + + +/* Cause the time semaphore, if one has been registered, to be + signalled when the microsecond clock is greater than or equal to + the given tick value. A tick value of zero turns off timer interrupts. */ + + /* StackInterpreterPrimitives>>#primitiveSignalAtMilliseconds */ +static void +primitiveSignalAtMilliseconds(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong deltaMsecs; + sqLong limit; + sqLong msecs; + sqInt msecsObj; + sqInt sema; + + msecsObj = longAt(GIV(stackPointer)); + sema = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + msecs = positive32BitValueOf(msecsObj); + if (!GIV(primFailCode)) { + if (/* isSemaphoreOop: */ + ((!(sema & (tagMask())))) + && (((longAt((void *)(sema))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord())))))))))) { + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheTimerSemaphore, GIV(specialObjectsOop), sema)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(sema & (tagMask())))) + && (oopisLessThan(sema, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord()))))),sema); + deltaMsecs = msecs - ((ioMSecs()) & MillisecondClockMask); + limit = ((usqInt)(MillisecondClockMask)) >> 1; + + /* Handle a roll-over that could happen in between image invocation of ioMSecs and this invocation. + This will limit the maximum relative duration to MillisecondClockMask/2, about 3 days currently. + Every delay longer than that limit may lead to undefined behavior (shorten delay, or no delay at all) */ + if (deltaMsecs > limit) { + deltaMsecs -= MillisecondClockMask; + } + GIV(nextWakeupUsecs) = (deltaMsecs > 0 + ? (ioUTCMicroseconds()) + (deltaMsecs * 1000) + : ioUTCMicroseconds()); + + /* begin pop: */ + GIV(stackPointer) += 2 * BytesPerWord; + return; + } + if (sema == GIV(nilObj)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(specialObjectsOop))) + && (!(isForwarded(GIV(specialObjectsOop))))); + assert(validStorePointerUncheckedArgs(TheTimerSemaphore, GIV(specialObjectsOop), GIV(nilObj))); + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord()))))),GIV(nilObj)); + GIV(nextWakeupUsecs) = 0; + + /* begin pop: */ + GIV(stackPointer) += 2 * BytesPerWord; + return; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; +} + + +/* Cause the time semaphore, if one has been registered, to be + signalled when the microsecond clock is greater than or equal to + the given tick value. A tick value of zero turns off timer interrupts. */ + + /* StackInterpreterPrimitives>>#primitiveSignalAtUTCMicroseconds */ +static void +primitiveSignalAtUTCMicroseconds(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt sema; + usqLong usecs; + sqInt usecsObj; + + usecsObj = longAt(GIV(stackPointer)); + sema = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + usecs = positive64BitValueOf(usecsObj); + if (!GIV(primFailCode)) { + if (/* isSemaphoreOop: */ + ((!(sema & (tagMask())))) + && (((longAt((void *)(sema))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord())))))))))) { + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheTimerSemaphore, GIV(specialObjectsOop), sema)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(sema & (tagMask())))) + && (oopisLessThan(sema, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord()))))),sema); + GIV(nextWakeupUsecs) = usecs; + + /* begin pop: */ + GIV(stackPointer) += 2 * BytesPerWord; + return; + } + if (sema == GIV(nilObj)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(specialObjectsOop))) + && (!(isForwarded(GIV(specialObjectsOop))))); + assert(validStorePointerUncheckedArgs(TheTimerSemaphore, GIV(specialObjectsOop), GIV(nilObj))); + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord()))))),GIV(nilObj)); + GIV(nextWakeupUsecs) = 0; + + /* begin pop: */ + GIV(stackPointer) += 2 * BytesPerWord; + return; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; +} + + +/* Answer a slot in an object. This numbers all slots from 1, ignoring the + distinction between + named and indexed inst vars. In objects with both named and indexed inst + vars, the named + inst vars precede the indexed ones. In non-object indexed objects (objects + that contain + bits, not object references) this primitive answers the raw integral value + at each slot. + e.g. for Strings it answers the character code, not the Character object + at each slot. */ +/* because of externalInstVar:ofContext: below */ + + /* StackInterpreterPrimitives>>#primitiveSlotAt */ +static void +primitiveSlotAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt fmtSqInt; + sqInt index; + sqInt numLiveSlots; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt rcvr; + sqInt value; + + index = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if ((((usqInt)index)) < numSlots) { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + numLiveSlots = (stackPointerForMaybeMarriedContext(rcvr)) + CtxtTempFrameStart; + value = ((((usqInt)index)) < numLiveSlots + ? externalInstVarofContext(index, rcvr) + : GIV(nilObj)); + } + else { + value = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + + /* begin numBytesOfBytes: */ + fmtSqInt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert(fmtSqInt >= (firstByteFormat())); + numSlots = ((((/* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) << (shiftForWord()))) - (fmtSqInt & 7); + if ((((usqInt)index)) < numSlots) { + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + index))) << 3) | 1)); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt >= (firstShortFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 1; + if ((((usqInt)index)) < numSlots) { + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)(((unsigned short) (shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 1)))))))) << 3) | 1)); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + oop = positive64BitIntegerFor(long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3)))))); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt >= (firstLongFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)((((usqInt)(long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1)); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; +} + + +/* Atomic store into context stackPointer. + Also ensures that any newly accessible cells are initialized to nil */ + + /* StackInterpreterPrimitives>>#primitiveStoreStackp */ +static void +primitiveStoreStackp(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + sqInt i; + sqInt integerPointer; + sqInt newStackp; + usqInt numSlots; + int onCurrentPage; + sqInt senderOop; + sqInt sp; + sqInt stackp; + char *theFP; + StackPage *thePage; + + ctxt = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + newStackp = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + newStackp = 0; + } + if (!((!GIV(primFailCode)) + && (((newStackp >= 0) && (newStackp <= (((/* begin numSlotsOf: */ + assert((classIndexOf(ctxt)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(ctxt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(ctxt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - CtxtTempFrameStart)))))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(ctxt)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (((onCurrentPage = thePage == GIV(stackPage))) + && (theFP == GIV(framePointer))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + externalDivorceFrameandContext(theFP, ctxt); + if (onCurrentPage) { + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (GIV(stackPage)->headSP); + GIV(framePointer) = (GIV(stackPage)->headFP); + } + else { + assert(GIV(stackPage) == (stackPageFor(GIV(framePointer)))); + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + } + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + stackp = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(ctxt))); + stackp = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + + /* Nil any newly accessible cells */ + for (i = (stackp + 1); i <= newStackp; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs((i + CtxtTempFrameStart) - 1, ctxt, GIV(nilObj))); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(((i + CtxtTempFrameStart) - 1)) << (shiftForWord()))))),GIV(nilObj)); + } + + /* begin storeStackPointerValue:inContext: */ + assert((ReceiverIndex + newStackp) < (lengthOf(ctxt))); + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs(StackPointerIndex, ctxt, (((usqInt)newStackp << 3) | 1))); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord()))))),(((usqInt)newStackp << 3) | 1)); + ensureContextIsExecutionSafeAfterAssignToStackPointer(ctxt); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; +} + + +/* Behaviour depends on argument count: + 0 args: return an Array of VM parameter values; + 1 arg: return the indicated VM parameter; + 2 args: set the VM indicated parameter. + VM parameters are numbered as follows: + 1 end (v3)/size(Spur) of old-space (0-based, read-only) + 2 end (v3)/size(Spur) of young/new-space (read-only) + 3 end (v3)/size(Spur) of heap (read-only) + 4 nil (was allocationCount (read-only)) + 5 nil (was allocations between GCs (read-write) + 6 survivor count tenuring threshold (read-write) + 7 full GCs since startup (read-only) + 8 total milliseconds in full GCs since startup (read-only) + 9 incremental GCs (SqueakV3) or scavenges (Spur) since startup (read-only) + 10 total milliseconds in incremental GCs (SqueakV3) or scavenges (Spur) + since startup (read-only) + 11 tenures of surving objects since startup or reset (read-write) + 12-20 were specific to ikp's JITTER VM, now 12 16 open for use + 13 if started, the start time in utc microseconds of the high-priority + ticker 14 if started, the number of checkHighPriorityTickees calls + 15 if started, the number of tickee calls from checkHighPriorityTickees + 16 total microseconds at idle since start-up (if non-zero) + 17 fraction of the code zone to use (Sista only; used to control code zone + use to preserve sendAndBranchData on counter tripped callback) + 18 total milliseconds in compaction phase of full GC since start-up (Spur + only) 19 scavenge threshold, the effective size of eden. When eden fills + to the threshold a scavenge is scheduled. Newer Spur VMs only. + 20 utc microseconds at VM start-up (actually at time initialization, which + precedes image load). + 21 root/remembered table size (occupancy) (read-only) + 22 root table overflows since startup (read-only) + 23 bytes of extra memory to reserve for VM buffers, plugins, etc (stored + in image file header). + 24 memory threshold above which shrinking object memory (rw) + 25 memory headroom when growing object memory (rw) + 26 interruptChecksEveryNms - force an ioProcessEvents every N milliseconds + (rw) 27 number of times mark loop iterated for current IGC/FGC (read-only) + includes ALL marking + 28 number of times sweep loop iterated for current IGC/FGC (read-only) + 29 number of times make forward loop iterated for current IGC/FGC + (read-only) 30 number of times compact move loop iterated for current + IGC/FGC (read-only) + 31 number of grow memory requests (read-only) + 32 number of shrink memory requests (read-only) + 33 number of root table entries used for current IGC/FGC (read-only) + 34 Spur: bytes allocated in total since start-up or reset (read-write) + (Used to be number of allocations done before current IGC/FGC (read-only)) + 35 number of survivor objects after current IGC/FGC (read-only) + 36 millisecond clock when current IGC/FGC completed (read-only) + 37 number of marked objects for Roots of the world, not including Root + Table entries for current IGC/FGC (read-only) + 38 milliseconds taken by current IGC (read-only) + 39 Number of finalization signals for Weak Objects pending when current + IGC/FGC completed (read-only) + 40 BytesPerOop for this image + 41 imageFormatVersion for the VM + 42 number of stack pages in use + 43 desired number of stack pages (stored in image file header, max 65535) + 44 size of eden, in bytes + 45 desired size of eden, in bytes (stored in image file header) + 46 machine code zone size, in bytes (Cog only; otherwise nil) + 47 desired machine code zone size (stored in image file header; Cog only; + otherwise nil) + 48 various header flags. See getImageHeaderFlags. + 49 max size the image promises to grow the external semaphore table to (0 + sets to default, which is 256 as of writing) + 50 max literal count for JIT compile (stored in image file header; Cog + only; otherwise nil) + 51 nil; reserved for VM parameters that persist in the image (such as eden + above) 52 root/remembered table capacity + 53 number of segments (Spur only; otherwise nil) + 54 total size of free old space (Spur only, otherwise nil) + 55 ratio of growth and image size at or above which a GC will be performed + post scavenge + 56 number of process switches since startup (read-only) + 57 number of ioProcessEvents calls since startup (read-only) + 58 number of ForceInterruptCheck calls since startup (read-only) + 59 number of check event calls since startup (read-only) + 60 number of stack page overflows since startup (read-only) + 61 number of stack page divorces since startup (read-only) + 62 compiled code compactions since startup (read-only; Cog only; otherwise + nil) 63 total milliseconds in compiled code compactions since startup + (read-only; Cog only; otherwise nil) + 64 the number of methods that currently have jitted machine-code + 65 various VM feature flags; see getCogVMFeatureFlags + 66 the byte size of a stack page + 67 the max allowed size of old space (Spur only; nil otherwise; 0 implies + no limit except that of the underlying platform) + 68 the average number of live stack pages when scanned by GC (at + scavenge/gc/become et al) (read-write) + 69 the maximum number of live stack pages when scanned by GC (at + scavenge/gc/become et al) (read-write) + 70 the vmProxyMajorVersion (the interpreterProxy VM_MAJOR_VERSION) + 71 the vmProxyMinorVersion (the interpreterProxy VM_MINOR_VERSION) + 72 total milliseconds in full GCs Mark phase since startup (read-only) + 73 total milliseconds in full GCs Sweep phase since startup (read-only, + can be 0 depending on compactors) + 74 maximum pause time due to segment allocation + 75 whether the arithmetic primitives perform conversion in case of mixed + SmallInteger/Float (true) or fail (false) + 76 the minimum unused headroom in all stack pages; Cog VMs only + 77 whether the numweric comparison primitives perform conversion in case + of mixed SmallInteger/Float (true) or fail (false) + + Note: Thanks to Ian Piumarta for this primitive. */ + + /* StackInterpreterPrimitives>>#primitiveVMParameter */ +static void +primitiveVMParameter(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aBool; + sqInt activeContext; + double aProportion; + sqIntptr_t arg; + sqInt argOop; + sqLong delta; + sqInt index; + usqInt limit; + int mustFlush; + usqInt oldMixedArithmeticHeaderFlags; + sqInt oop; + sqInt paramsArraySize; + sqInt result; + sqInt resultSqInt; + sqInt senderOop; + sqInt tagBits; + char *theFrame; + StackPage *thePage; + sqInt top; + sqInt value; + sqInt valuePointer; + + paramsArraySize = 77; + if (!GIV(argumentCount)) { + /* begin primitiveAllVMParameters: */ + resultSqInt = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), paramsArraySize); + valuePointer = positive64BitIntegerFor( + (/* begin oldSpaceSize *//* begin totalOldSpaceSize */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), + GIV(totalHeapSizeIncludingBridges))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(0, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)((GIV(freeStart) - (((GIV(eden)).start))) + (GIV(pastSpaceStart) - (((GIV(pastSpace)).start)))) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(1, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor((newSpaceCapacity()) + ((/* begin totalOldSpaceSize */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges)))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(2, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (2U << (shiftForWord()))),valuePointer); + + /* objectMemory storePointerUnchecked: 3 ofObject: result withValue: objectMemory nilObject was allocationCount + objectMemory storePointerUnchecked: 4 ofObject: result withValue: objectMemory nilObject allocationsBetweenGCs */ + valuePointer = (((usqInt)(((sqInt)(((scavengerTenuringThreshold()) * (((GIV(pastSpace).limit)) - ((GIV(pastSpace).start)))) / (8 * BytesPerOop)))) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(5, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (5U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(6, resultSqInt, (((usqInt)GIV(statFullGCs) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (6U << (shiftForWord()))),(((usqInt)GIV(statFullGCs) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(7, resultSqInt, ((((GIV(statFullGCUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (7U << (shiftForWord()))),((((GIV(statFullGCUsecs) + 500) / 1000) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(8, resultSqInt, (((usqInt)GIV(statScavenges) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (8U << (shiftForWord()))),(((usqInt)GIV(statScavenges) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(9, resultSqInt, ((((GIV(statScavengeGCUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (9U << (shiftForWord()))),((((GIV(statScavengeGCUsecs) + 500) / 1000) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(10, resultSqInt, (((usqInt)GIV(statTenures) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (10U << (shiftForWord()))),(((usqInt)GIV(statTenures) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(11, resultSqInt, (((usqInt)eventTraceMask << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (11U << (shiftForWord()))),(((usqInt)eventTraceMask << 3) | 1)); + valuePointer = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickerStartUSecs()) +# else + ConstZero +# endif + ; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(12, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (12U << (shiftForWord()))),valuePointer); + valuePointer = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickerCount()) +# else + ConstZero +# endif + ; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(13, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (13U << (shiftForWord()))),valuePointer); + valuePointer = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickeeCallCount()) +# else + ConstZero +# endif + ; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(14, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (14U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statIdleUsecs)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(15, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (15U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(16, resultSqInt, ConstZero)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (16U << (shiftForWord()))),ConstZero); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(17, resultSqInt, ((((GIV(statCompactionUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (17U << (shiftForWord()))),((((GIV(statCompactionUsecs) + 500) / 1000) << 3) | 1)); + valuePointer = (((usqInt)(GIV(scavengeThreshold) - (((GIV(eden)).start))) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(18, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (18U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(ioUTCStartMicroseconds()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(19, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (19U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)(rootTableCount()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(20, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (20U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(21, resultSqInt, (((usqInt)GIV(statRootTableOverflows) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (21U << (shiftForWord()))),(((usqInt)GIV(statRootTableOverflows) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(22, resultSqInt, (((usqInt)extraVMMemory << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (22U << (shiftForWord()))),(((usqInt)extraVMMemory << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(23, resultSqInt, (((usqInt)GIV(shrinkThreshold) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (23U << (shiftForWord()))),(((usqInt)GIV(shrinkThreshold) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(24, resultSqInt, (((usqInt)GIV(growHeadroom) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (24U << (shiftForWord()))),(((usqInt)GIV(growHeadroom) << 3) | 1)); + valuePointer = (((usqInt)(ioHeartbeatMilliseconds()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(25, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (25U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(26, resultSqInt, (((usqInt)GIV(statMarkCount) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (26U << (shiftForWord()))),(((usqInt)GIV(statMarkCount) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(27, resultSqInt, (((usqInt)0 /* statSweepCount */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (27U << (shiftForWord()))),(((usqInt)0 /* statSweepCount */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(28, resultSqInt, (((usqInt)0 /* statMkFwdCount */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (28U << (shiftForWord()))),(((usqInt)0 /* statMkFwdCount */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(29, resultSqInt, (((usqInt)GIV(statCompactPassCount) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (29U << (shiftForWord()))),(((usqInt)GIV(statCompactPassCount) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(30, resultSqInt, (((usqInt)GIV(statGrowMemory) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (30U << (shiftForWord()))),(((usqInt)GIV(statGrowMemory) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(0x1F, resultSqInt, (((usqInt)GIV(statShrinkMemory) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (0x1FU << (shiftForWord()))),(((usqInt)GIV(statShrinkMemory) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(32, resultSqInt, (((usqInt)GIV(statRootTableCount) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (32U << (shiftForWord()))),(((usqInt)GIV(statRootTableCount) << 3) | 1)); + valuePointer = positive64BitIntegerFor(currentAllocatedBytes()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(33, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (33U << (shiftForWord()))),valuePointer); + + /* was statAllocationCount */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(34, resultSqInt, (((usqInt)GIV(statSurvivorCount) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (34U << (shiftForWord()))),(((usqInt)GIV(statSurvivorCount) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(35, resultSqInt, ((((GIV(statGCEndUsecs) / 1000) & MillisecondClockMask) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (35U << (shiftForWord()))),((((GIV(statGCEndUsecs) / 1000) & MillisecondClockMask) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(36, resultSqInt, (((usqInt)0 /* statSpecialMarkCount */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (36U << (shiftForWord()))),(((usqInt)0 /* statSpecialMarkCount */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(37, resultSqInt, ((((GIV(statIGCDeltaUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (37U << (shiftForWord()))),((((GIV(statIGCDeltaUsecs) + 500) / 1000) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(38, resultSqInt, (((usqInt)GIV(statPendingFinalizationSignals) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (38U << (shiftForWord()))),(((usqInt)GIV(statPendingFinalizationSignals) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(39, resultSqInt, (((usqInt)BytesPerWord << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (39U << (shiftForWord()))),(((usqInt)BytesPerWord << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(40, resultSqInt, (((usqInt)68021 /* imageFormatVersion */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (40U << (shiftForWord()))),(((usqInt)68021 /* imageFormatVersion */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(41, resultSqInt, (((usqInt)GIV(numStackPages) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (41U << (shiftForWord()))),(((usqInt)GIV(numStackPages) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(42, resultSqInt, (((usqInt)desiredNumStackPages << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (42U << (shiftForWord()))),(((usqInt)desiredNumStackPages << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(43, resultSqInt, (((usqInt)(((GIV(eden).limit)) - ((GIV(eden).start))) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (43U << (shiftForWord()))),(((usqInt)(((GIV(eden).limit)) - ((GIV(eden).start))) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(44, resultSqInt, (((usqInt)desiredEdenBytes << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (44U << (shiftForWord()))),(((usqInt)desiredEdenBytes << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(45, resultSqInt, ((GIV(cogCodeSize) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (45U << (shiftForWord()))),((GIV(cogCodeSize) << 3) | 1)); + valuePointer = getDesiredCogCodeSize(); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(46, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (46U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(47, resultSqInt, (((usqInt)(((usqInt)(imageHeaderFlags)) >> 2) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (47U << (shiftForWord()))),(((usqInt)(((usqInt)(imageHeaderFlags)) >> 2) << 3) | 1)); + valuePointer = (((usqInt)(ioGetMaxExtSemTableSize()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(48, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (48U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(49, resultSqInt, (((usqInt)maxLiteralCountForCompile << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (49U << (shiftForWord()))),(((usqInt)maxLiteralCountForCompile << 3) | 1)); + valuePointer = (((usqInt)(((GIV(oldImageBaseAddress) != GIV(oldSpaceStart) + ? 1 + : 0)) + ((sqImageFileIsEmbedded() + ? 2 + : 0))) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(50, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (50U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)(rootTableCapacity()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(51, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (51U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(52, resultSqInt, (((usqInt)GIV(numSegments) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (52U << (shiftForWord()))),(((usqInt)GIV(numSegments) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(53, resultSqInt, ((GIV(totalFreeOldSpace) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (53U << (shiftForWord()))),((GIV(totalFreeOldSpace) << 3) | 1)); + valuePointer = floatObjectOf(getHeapGrowthToSizeGCRatio()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(54, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (54U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statProcessSwitch)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(55, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (55U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statIOProcessEvents)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(56, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (56U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statForceInterruptCheck)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(57, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (57U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statCheckForEvents)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(58, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (58U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statStackOverflow)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(59, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (59U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statStackPageDivorce)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(60, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (60U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(61, resultSqInt, (((usqInt)GIV(statCodeCompactionCount) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (61U << (shiftForWord()))),(((usqInt)GIV(statCodeCompactionCount) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(0x3E, resultSqInt, ((((GIV(statCodeCompactionUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (0x3EU << (shiftForWord()))),((((GIV(statCodeCompactionUsecs) + 500) / 1000) << 3) | 1)); + valuePointer = (((usqInt)(numMethodsOfType(CMMethod)) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(0x3F, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (0x3FU << (shiftForWord()))),valuePointer); + valuePointer = getCogVMFeatureFlags(); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(64, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (64U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(65, resultSqInt, (((usqInt)GIV(bytesPerPage) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (65U << (shiftForWord()))),(((usqInt)GIV(bytesPerPage) << 3) | 1)); + valuePointer = positive64BitIntegerFor(maxOldSpaceSize); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(66, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (66U << (shiftForWord()))),valuePointer); + valuePointer = floatObjectOf(statAverageLivePagesWhenMapping()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(67, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (67U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)(GIV(statMaxPageCountWhenMapping)) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(68, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (68U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(69, resultSqInt, (((usqInt)VM_PROXY_MAJOR /* vmProxyMajorVersion */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (69U << (shiftForWord()))),(((usqInt)VM_PROXY_MAJOR /* vmProxyMajorVersion */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(70, resultSqInt, (((usqInt)VM_PROXY_MINOR /* vmProxyMinorVersion */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (70U << (shiftForWord()))),(((usqInt)VM_PROXY_MINOR /* vmProxyMinorVersion */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(71, resultSqInt, ((((GIV(statMarkUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (71U << (shiftForWord()))),((((GIV(statMarkUsecs) + 500) / 1000) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(72, resultSqInt, ((((GIV(statSweepUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (72U << (shiftForWord()))),((((GIV(statSweepUsecs) + 500) / 1000) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(73, resultSqInt, (((usqInt)((GIV(statMaxAllocSegmentTime) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (73U << (shiftForWord()))),(((usqInt)((GIV(statMaxAllocSegmentTime) + 500) / 1000) << 3) | 1)); + aBool = numericPrimsMixArithmetic(); + + /* begin booleanObjectOf: */ + valuePointer = (aBool + ? GIV(trueObj) + : GIV(falseObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(74, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (74U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)(minimumUnusedHeadroom()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(75, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (75U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)(numericPrimsMixComparison()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(76, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (76U << (shiftForWord()))),valuePointer); + beRootIfOld(resultSqInt); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),resultSqInt); + return; + } + if (GIV(argumentCount) > 2) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + + /* index read & checks */ + index = longAt(GIV(stackPointer) + (((GIV(argumentCount) == 1 + ? 0 + : 1)) * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + index = (index >> 3); + if ((index < 1) + || (index > paramsArraySize)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (GIV(argumentCount) == 1) { + /* begin primitiveGetVMParameter: */ + switch (index) { + case 1: + result = positive64BitIntegerFor( + (/* begin oldSpaceSize *//* begin totalOldSpaceSize */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), + GIV(totalHeapSizeIncludingBridges))); + goto l1; + break; + case 2: + result = (((usqInt)((GIV(freeStart) - (((GIV(eden)).start))) + (GIV(pastSpaceStart) - (((GIV(pastSpace)).start)))) << 3) | 1); + goto l1; + break; + case 3: + result = positive64BitIntegerFor((newSpaceCapacity()) + ((/* begin totalOldSpaceSize */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges)))); + goto l1; + break; + case 6: + result = (((usqInt)(((sqInt)(((scavengerTenuringThreshold()) * (((GIV(pastSpace).limit)) - ((GIV(pastSpace).start)))) / (8 * BytesPerOop)))) << 3) | 1); + goto l1; + break; + case 7: + result = (((usqInt)GIV(statFullGCs) << 3) | 1); + goto l1; + break; + case 8: + result = ((((GIV(statFullGCUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 9: + result = (((usqInt)GIV(statScavenges) << 3) | 1); + goto l1; + break; + case 10: + result = ((((GIV(statScavengeGCUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 11: + result = (((usqInt)GIV(statTenures) << 3) | 1); + goto l1; + break; + case 12: + result = (((usqInt)eventTraceMask << 3) | 1); + goto l1; + break; + case 13: + result = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickerStartUSecs()) +# else + ConstZero +# endif + ; + goto l1; + break; + case 14: + result = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickerCount()) +# else + ConstZero +# endif + ; + goto l1; + break; + case 15: + result = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickeeCallCount()) +# else + ConstZero +# endif + ; + goto l1; + break; + case 16: + result = positive64BitIntegerFor(GIV(statIdleUsecs)); + goto l1; + break; + case 17: + result = ConstZero; + goto l1; + break; + case 18: + result = ((((GIV(statCompactionUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 19: + result = (((usqInt)(GIV(scavengeThreshold) - (((GIV(eden)).start))) << 3) | 1); + goto l1; + break; + case 20: + result = positive64BitIntegerFor(ioUTCStartMicroseconds()); + goto l1; + break; + case 21: + result = (((usqInt)(rootTableCount()) << 3) | 1); + goto l1; + break; + case 22: + result = (((usqInt)GIV(statRootTableOverflows) << 3) | 1); + goto l1; + break; + case 23: + result = (((usqInt)extraVMMemory << 3) | 1); + goto l1; + break; + case 24: + result = (((usqInt)GIV(shrinkThreshold) << 3) | 1); + goto l1; + break; + case 25: + result = (((usqInt)GIV(growHeadroom) << 3) | 1); + goto l1; + break; + case 26: + result = (((usqInt)(ioHeartbeatMilliseconds()) << 3) | 1); + goto l1; + break; + case 27: + result = (((usqInt)GIV(statMarkCount) << 3) | 1); + goto l1; + break; + case 28: + result = (((usqInt)0 /* statSweepCount */ << 3) | 1); + goto l1; + break; + case 29: + result = (((usqInt)0 /* statMkFwdCount */ << 3) | 1); + goto l1; + break; + case 30: + result = (((usqInt)GIV(statCompactPassCount) << 3) | 1); + goto l1; + break; + case 0x1F: + result = (((usqInt)GIV(statGrowMemory) << 3) | 1); + goto l1; + break; + case 32: + result = (((usqInt)GIV(statShrinkMemory) << 3) | 1); + goto l1; + break; + case 33: + result = (((usqInt)GIV(statRootTableCount) << 3) | 1); + goto l1; + break; + case 34: + result = positive64BitIntegerFor(currentAllocatedBytes()); + goto l1; + break; + case 35: + result = (((usqInt)GIV(statSurvivorCount) << 3) | 1); + goto l1; + break; + case 36: + result = ((((GIV(statGCEndUsecs) / 1000) & MillisecondClockMask) << 3) | 1); + goto l1; + break; + case 37: + result = (((usqInt)0 /* statSpecialMarkCount */ << 3) | 1); + goto l1; + break; + case 38: + result = ((((GIV(statIGCDeltaUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 39: + result = (((usqInt)GIV(statPendingFinalizationSignals) << 3) | 1); + goto l1; + break; + case 40: + result = (((usqInt)BytesPerWord << 3) | 1); + goto l1; + break; + case 41: + result = (((usqInt)68021 /* imageFormatVersion */ << 3) | 1); + goto l1; + break; + case 42: + result = (((usqInt)GIV(numStackPages) << 3) | 1); + goto l1; + break; + case 43: + result = (((usqInt)desiredNumStackPages << 3) | 1); + goto l1; + break; + case 44: + result = (((usqInt)(((GIV(eden).limit)) - ((GIV(eden).start))) << 3) | 1); + goto l1; + break; + case 45: + result = (((usqInt)desiredEdenBytes << 3) | 1); + goto l1; + break; + case 46: + result = ((GIV(cogCodeSize) << 3) | 1); + goto l1; + break; + case 47: + result = getDesiredCogCodeSize(); + goto l1; + break; + case 48: + result = (((usqInt)(((usqInt)(imageHeaderFlags)) >> 2) << 3) | 1); + goto l1; + break; + case 49: + result = (((usqInt)(ioGetMaxExtSemTableSize()) << 3) | 1); + goto l1; + break; + case 50: + result = (((usqInt)maxLiteralCountForCompile << 3) | 1); + goto l1; + break; + case 51: + result = (((usqInt)(((GIV(oldImageBaseAddress) != GIV(oldSpaceStart) + ? 1 + : 0)) + ((sqImageFileIsEmbedded() + ? 2 + : 0))) << 3) | 1); + goto l1; + break; + case 52: + result = (((usqInt)(rootTableCapacity()) << 3) | 1); + goto l1; + break; + case 53: + result = (((usqInt)GIV(numSegments) << 3) | 1); + goto l1; + break; + case 54: + result = ((GIV(totalFreeOldSpace) << 3) | 1); + goto l1; + break; + case 55: + result = floatObjectOf(getHeapGrowthToSizeGCRatio()); + goto l1; + break; + case 56: + result = positive64BitIntegerFor(GIV(statProcessSwitch)); + goto l1; + break; + case 57: + result = positive64BitIntegerFor(GIV(statIOProcessEvents)); + goto l1; + break; + case 58: + result = positive64BitIntegerFor(GIV(statForceInterruptCheck)); + goto l1; + break; + case 59: + result = positive64BitIntegerFor(GIV(statCheckForEvents)); + goto l1; + break; + case 60: + result = positive64BitIntegerFor(GIV(statStackOverflow)); + goto l1; + break; + case 61: + result = positive64BitIntegerFor(GIV(statStackPageDivorce)); + goto l1; + break; + case 0x3E: + result = (((usqInt)GIV(statCodeCompactionCount) << 3) | 1); + goto l1; + break; + case 0x3F: + result = ((((GIV(statCodeCompactionUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 64: + result = (((usqInt)(numMethodsOfType(CMMethod)) << 3) | 1); + goto l1; + break; + case 65: + result = getCogVMFeatureFlags(); + goto l1; + break; + case 66: + result = (((usqInt)GIV(bytesPerPage) << 3) | 1); + goto l1; + break; + case 67: + result = positive64BitIntegerFor(maxOldSpaceSize); + goto l1; + break; + case 68: + result = floatObjectOf(statAverageLivePagesWhenMapping()); + goto l1; + break; + case 69: + result = (((usqInt)(GIV(statMaxPageCountWhenMapping)) << 3) | 1); + goto l1; + break; + case 70: + result = (((usqInt)VM_PROXY_MAJOR /* vmProxyMajorVersion */ << 3) | 1); + goto l1; + break; + case 71: + result = (((usqInt)VM_PROXY_MINOR /* vmProxyMinorVersion */ << 3) | 1); + goto l1; + break; + case 72: + result = ((((GIV(statMarkUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 73: + result = ((((GIV(statSweepUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 74: + result = (((usqInt)((GIV(statMaxAllocSegmentTime) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 75: + aBool = numericPrimsMixArithmetic(); + result = (aBool + ? GIV(trueObj) + : GIV(falseObj)); + goto l1; + break; + case 76: + result = (((usqInt)(minimumUnusedHeadroom()) << 3) | 1); + goto l1; + break; + case 77: + aBool = numericPrimsMixComparison(); + result = (aBool + ? GIV(trueObj) + : GIV(falseObj)); + goto l1; + break; + default: + result = null; + goto l1; + } + /* end primitiveGetVMParameter: */ +l1: + oop = (!(result) + ? GIV(nilObj) + : result); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return; + } + + /* read VM parameter; written this way to avoid branch limits in V3 bytecode set + written thus to enable Slang inlining of primitiveGetVMParameter: + write a VM parameter */ + argOop = longAt(GIV(stackPointer)); + + /* begin primitiveSetVMParameter:arg: */ + switch (index) { + case 17: + case 55: + case 68: + if (!((/* isFloatInstance: */ + ((tagBits = argOop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex)) + || ((((argOop) & 7) == 1)))) { + GIV(primFailCode) = PrimErrBadArgument; + } + break; + case 67: + /* begin positiveMachineIntegerValueOf: */ + if ((((argOop) & 7) == 1)) { + value = (argOop >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = ((usqIntptr_t) null); + goto l2; + } + arg = ((usqIntptr_t) value); + goto l2; + } + + /* don't inline the rare case */ + arg = positiveMachineIntegerValueOfObj(argOop); + /* end positiveMachineIntegerValueOf: */ +l2:; + break; + case 75: + case 77: + /* begin booleanValueOf: */ + if (argOop == GIV(trueObj)) { + arg = 1; + goto l3; + } + if (argOop == GIV(falseObj)) { + arg = 0; + goto l3; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = null; + /* end booleanValueOf: */ +l3:; + break; + default: + arg = (argOop >> 3); + } + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l6; + } + + /* assume failure, then set success for handled indices */ + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + switch (index) { + case 5: + break; + case 6: + resultSqInt = (((usqInt)(((sqInt)(((scavengerTenuringThreshold()) * (((GIV(pastSpace).limit)) - ((GIV(pastSpace).start)))) / (8 * BytesPerOop)))) << 3) | 1); + + /* begin tenuringThreshold: */ + if (arg < 0) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + aProportion = (((double) (arg * (8 * BytesPerOop)) )) / (((double) (((GIV(pastSpace).limit)) - ((GIV(pastSpace).start))) )); + + /* begin scavengerTenuringThreshold: */ + GIV(tenuringProportion) = aProportion; + GIV(tenureThreshold) = (aProportion == 0.0 + ? 0 + : (round(((((GIV(pastSpace).limit)) - ((GIV(pastSpace).start))) * (1.0 - aProportion)))) + ((GIV(pastSpace).start))); + GIV(primFailCode) = 0; + /* end tenuringThreshold: */ +l4:; + break; + case 12: + resultSqInt = (((usqInt)eventTraceMask << 3) | 1); + eventTraceMask = arg; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + break; + case 11: + if (arg >= 0) { + resultSqInt = (((usqInt)GIV(statTenures) << 3) | 1); + + /* begin statTenures: */ + GIV(statTenures) = arg; + GIV(primFailCode) = 0; + } + break; + case 17: + break; + case 23: + resultSqInt = (((usqInt)extraVMMemory << 3) | 1); + extraVMMemory = arg; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + break; + case 24: + if (arg > 0) { + resultSqInt = (((usqInt)GIV(shrinkThreshold) << 3) | 1); + + /* begin shrinkThreshold: */ + GIV(shrinkThreshold) = arg; + GIV(primFailCode) = 0; + } + break; + case 25: + if (arg > 0) { + resultSqInt = (((usqInt)GIV(growHeadroom) << 3) | 1); + + /* growHeadroom: */ + GIV(growHeadroom) = arg; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + } + break; + case 26: + if (arg >= 0) { + resultSqInt = (((usqInt)(ioHeartbeatMilliseconds()) << 3) | 1); + ioSetHeartbeatMilliseconds(arg); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + } + break; + case 34: + if (arg >= 0) { + resultSqInt = positive64BitIntegerFor(currentAllocatedBytes()); + + /* begin setCurrentAllocatedBytesTo: */ + delta = (currentAllocatedBytes()) - GIV(statAllocatedBytes); + GIV(statAllocatedBytes) = arg; + GIV(oldSpaceUsePriorToScavenge) += delta; + assert((currentAllocatedBytes()) == arg); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + } + break; + case 43: + if (((arg >= 0) && (arg <= 0xFFFF))) { + resultSqInt = (((usqInt)desiredNumStackPages << 3) | 1); + desiredNumStackPages = arg; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + } + break; + case 45: + if (arg >= 0) { + resultSqInt = (((usqInt)desiredEdenBytes << 3) | 1); + desiredEdenBytes = arg; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + } + break; + case 47: + if (((arg >= 0) && (arg <= (maxCogCodeSize())))) { + resultSqInt = (((usqInt)(getDesiredCogCodeSize()) << 3) | 1); + + /* begin setDesiredCogCodeSize: */ + desiredCogCodeSize = arg; + GIV(primFailCode) = 0; + } + break; + case 48: + if (arg >= 0) { + /* answer old value */ + resultSqInt = (((usqInt)(((usqInt)(imageHeaderFlags)) >> 2) << 3) | 1); + + /* must flush/recompile if either of these are changed */ + oldMixedArithmeticHeaderFlags = imageHeaderFlags & 0x900; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if ((((usqInt)arg)) > 0x3FF) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + goto l5; + } + imageHeaderFlags = (((imageHeaderFlags | ((0xFFC))) - ((0xFFC)))) | (((((usqIntptr_t)(arg) << 2)))); + /* end setImageHeaderFlags: */ +l5: + if ((GIV(primFailCode) == 0) + && (oldMixedArithmeticHeaderFlags != (imageHeaderFlags & 0x900))) { + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + flushMethodsWithMachineCodePrimitivesAndContinueAnswering(resultSqInt); + goto l6; + } + } + break; + case 49: + if (((arg >= 0) && (arg <= 0xFFFF))) { + resultSqInt = (((usqInt)(ioGetMaxExtSemTableSize()) << 3) | 1); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + GIV(maxExtSemTabSizeSet) = 1; + ioSetMaxExtSemTableSize(arg); + } + break; + case 50: + if (((arg >= 0) && (arg <= 0x7FFF))) { + resultSqInt = (((usqInt)((((usqInt)maxLiteralCountForCompile << 3) | 1)) << 3) | 1); + + /* begin setMaxLiteralCountForCompile: */ + maxLiteralCountForCompile = (GIV(the2ndUnknownShort) = arg); + GIV(primFailCode) = 0; + activeContext = divorceAllFramesSuchThat(isMachineCodeFrameForCogMethodWithTooManyLiterals); + ensureAllContextsHaveBytecodePCsIf(methodHasTooManyLiterals); + unlinkSendsToMethodsSuchThatAndFreeIf(cogMethodHasTooManyLiterals, 1); + + /* If the top frame was divorced then continue in the interpreter. */ + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* pop bogus machine-code instructionPointer, arguments and receiver */ + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 2) - 1) * BytesPerWord),resultSqInt); + ceInvokeInterpret(); + } + + /* If not, work out where we are and continue */ + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + assert(((thePage->headFP)) == theFrame); + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),resultSqInt); + goto l6; + } + break; + case 55: + resultSqInt = floatObjectOf(getHeapGrowthToSizeGCRatio()); + + /* begin setHeapGrowthToSizeGCRatio: */ + GIV(heapGrowthToSizeGCRatio) = loadFloatOrIntFrom(argOop); + GIV(primFailCode) = 0; + break; + case 67: + if (arg >= 0) { + resultSqInt = positive64BitIntegerFor(maxOldSpaceSize); + limit = arg; + + /* begin setMaxOldSpaceSize: */ + maxOldSpaceSize = limit; + GIV(primFailCode) = 0; + } + break; + case 68: + resultSqInt = floatObjectOf(statAverageLivePagesWhenMapping()); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if ((loadFloatOrIntFrom(argOop)) == 0.0) { + GIV(statPageCountWhenMappingSum) = (GIV(statNumMaps) = 0); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + break; + case 69: + if (arg >= 0) { + resultSqInt = (((usqInt)(GIV(statMaxPageCountWhenMapping)) << 3) | 1); + + /* begin statMaxPageCountWhenMapping: */ + GIV(statMaxPageCountWhenMapping) = arg; + GIV(primFailCode) = 0; + } + break; + case 74: + if (arg >= 0) { + resultSqInt = (((usqInt)((GIV(statMaxAllocSegmentTime) + 500) / 1000) << 3) | 1); + + /* begin statMaxAllocSegmentTime: */ + GIV(statMaxAllocSegmentTime) = arg; + GIV(primFailCode) = 0; + } + break; + case 75: + aBool = numericPrimsMixArithmetic(); + + /* begin booleanObjectOf: */ + resultSqInt = (aBool + ? GIV(trueObj) + : GIV(falseObj)); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + mustFlush = (numericPrimsMixArithmetic()) != arg; + + /* setPrimitiveDoMixedArithmetic: */ + imageHeaderFlags = (arg + ? imageHeaderFlags | 0x100 + : ((imageHeaderFlags | 0x100) - 0x100)); + if (mustFlush) { + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + flushMethodsWithMachineCodePrimitivesAndContinueAnswering(resultSqInt); + goto l6; + } + break; + case 77: + aBool = numericPrimsMixComparison(); + + /* begin booleanObjectOf: */ + resultSqInt = (aBool + ? GIV(trueObj) + : GIV(falseObj)); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + mustFlush = (numericPrimsMixComparison()) != arg; + + /* setPrimitiveDoMixedNumericComparison: */ + imageHeaderFlags = (arg + ? imageHeaderFlags | 0x800 + : ((imageHeaderFlags | 0x800) - 0x800)); + if (mustFlush) { + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + flushMethodsWithMachineCodePrimitivesAndContinueAnswering(resultSqInt); + goto l6; + } + break; + default: +; + } + + /* disable inlining... + disable inlining... + disable inlining... */ + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),resultSqInt); + } + /* end primitiveSetVMParameter:arg: */ +l6:; +} + + +/* Potentially crash the VM by voiding the receiver. A subsequent inst var + access in the caller's frame should indirect through a null pointer. */ + + /* StackInterpreterPrimitives>>#primitiveVoidReceiver */ +EXPORT(void) +primitiveVoidReceiver(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + longAtput(/* frameReceiverLocation: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? GIV(framePointer) + FoxMFReceiver + : GIV(framePointer) + FoxIFReceiver),0); +} + + +/* Prune the stack to contain only the path, removing stacked indices + and mapping frame pointers to contexts The issue here is that a + GC can occur during ensureFrameIsMarried:SP:, but frame pointers + are not valid objects. So first prune back to objects and framePointers + as integers, and then replace frame pointers as integers by contexts. */ + + /* StackInterpreterPrimitives>>#pruneStack:stackp: */ +static NoDbgRegParms void +pruneStackstackp(sqInt stack, sqInt stackp) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt finger; + char *fp; + sqInt i; + sqInt objOrFP; + sqInt oop; + char *theFP; + char *theFPAbove; + StackPage *thePage; + char *theSP; + sqInt theStack; + sqInt toDoLimit; + + finger = 1; + for (i = 2; i < stackp; i += 2) { + objOrFP = longAt((void *)((stack + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)(((char *) objOrFP)))) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)(((char *) objOrFP)))) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)(((char *) objOrFP)))) <= (((usqInt)GIV(pages)))))))) { + objOrFP += smallIntegerTag(); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(finger, stack, objOrFP)); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(finger) << (shiftForWord()))))),objOrFP); + finger += 1; + } + toDoLimit = (lengthOf(stack)) - 1; + for (i = finger; i <= toDoLimit; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(i, stack, GIV(nilObj))); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } + oop = (theStack = stack); + + /* begin pushRemappableOop: */ + assert(addressCouldBeOop(oop)); + GIV(remapBuffer)[(GIV(remapBufferCount) += 1)] = oop; + if (!(GIV(remapBufferCount) <= RemapBufferSize)) { + error("remapBuffer overflow"); + } + for (i = 1; i < finger; i += 1) { + objOrFP = longAt((void *)((theStack + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((((objOrFP) & 7) == 1)) { + /* begin withoutSmallIntegerTags: */ + assert((((objOrFP) & 7) == 1)); + theFP = ((char *)(objOrFP - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == theFP) { + theFPAbove = 0; + goto l1; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == theFP) { + theFPAbove = fp; + goto l1; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + theFPAbove = 0; + /* end findFrameAbove:inPage: */ +l1: + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(theFPAbove))); + theSP = (theFPAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFPAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFPAbove))->cmNumArgs) + : byteAt((theFPAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + objOrFP = longAt(theFP + FoxThisContext); + goto l2; + } + objOrFP = marryFrameSP(theFP, theSP); + /* end ensureFrameIsMarried:SP: */ +l2: + theStack = GIV(remapBuffer)[GIV(remapBufferCount)]; + + /* after a GC stack may no longer be a root. */ + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(finger, theStack, objOrFP)); + assert(isNonImmediate(theStack)); + if (oopisGreaterThanOrEqualTo(theStack, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objOrFP & (tagMask())))) + && (oopisLessThan(objOrFP, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(theStack + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(theStack); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((theStack + BaseHeaderSize) + ((((usqInt)(finger) << (shiftForWord()))))),objOrFP); + } + } + + /* begin popRemappableOop */ + oop = GIV(remapBuffer)[GIV(remapBufferCount)]; + GIV(remapBufferCount) -= 1; +} + + /* StackInterpreterPrimitives>>#unmarkAfterPathTo */ +static void +unmarkAfterPathTo(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + unmarkAllFrames(); + + /* begin unmarkAllObjects */ + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + else { + if (!(((longAt((void *)(objOop))) & (classIndexMask())) == (segmentBridgePun()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + else { + if (!(((longAt((void *)(objOop))) & (classIndexMask())) == (segmentBridgePun()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3:; +} + +/*** Exports ***/ + +static char _m[] = ""; +void* vm_exports[][3] = { + {(void*)_m, "moduleUnloaded", (void*)moduleUnloaded}, + {(void*)_m, "primitiveAddLargeIntegers\000\000\001", (void*)primitiveAddLargeIntegers}, + {(void*)_m, "primitiveAllInstances\000\000\000", (void*)primitiveAllInstances}, +#if VMInvestigations + {(void*)_m, "primitiveAllMethodsCompiledToMachineCode\000\377\000", (void*)primitiveAllMethodsCompiledToMachineCode}, +#endif /* VMInvestigations */ + {(void*)_m, "primitiveAllObjects\000\377\000", (void*)primitiveAllObjects}, +#if VMInvestigations + {(void*)_m, "primitiveBenchmarkFollowForwardersInStackZone\000\377\000", (void*)primitiveBenchmarkFollowForwardersInStackZone}, +#endif /* VMInvestigations */ +#if VMInvestigations + {(void*)_m, "primitiveBenchmarkFollowForwardersOfReceiverAndTemporariesInStackZone\000\377\000", (void*)primitiveBenchmarkFollowForwardersOfReceiverAndTemporariesInStackZone}, +#endif /* VMInvestigations */ +#if VMInvestigations + {(void*)_m, "primitiveBenchmarkJITReceiver\000\000\000", (void*)primitiveBenchmarkJITReceiver}, +#endif /* VMInvestigations */ +#if VMInvestigations + {(void*)_m, "primitiveBenchmarkScavenge\000\377\000", (void*)primitiveBenchmarkScavenge}, +#endif /* VMInvestigations */ + {(void*)_m, "primitiveBitAndLargeIntegers\000\000\001", (void*)primitiveBitAndLargeIntegers}, + {(void*)_m, "primitiveBitOrLargeIntegers\000\000\001", (void*)primitiveBitOrLargeIntegers}, + {(void*)_m, "primitiveBitShiftLargeIntegers\000\000\001", (void*)primitiveBitShiftLargeIntegers}, + {(void*)_m, "primitiveBitXorLargeIntegers\000\000\001", (void*)primitiveBitXorLargeIntegers}, + {(void*)_m, "primitiveBytecodeSetsAvailable\000\377\000", (void*)primitiveBytecodeSetsAvailable}, + {(void*)_m, "primitiveClockLogAddresses\000\000\000", (void*)primitiveClockLogAddresses}, +#if VMInvestigations + {(void*)_m, "primitiveCogitMethod\000\000\000", (void*)primitiveCogitMethod}, +#endif /* VMInvestigations */ +#if VMInvestigations + {(void*)_m, "primitiveCogStaticSymbols\000\377\000", (void*)primitiveCogStaticSymbols}, +#endif /* VMInvestigations */ + {(void*)_m, "primitiveCompareBytes\000\001\001", (void*)primitiveCompareBytes}, + {(void*)_m, "primitiveCompareWith\000\001\000", (void*)primitiveCompareWith}, +#if VMInvestigations + {(void*)_m, "primitiveContextXray\000\001\000", (void*)primitiveContextXray}, +#endif /* VMInvestigations */ + {(void*)_m, "primitiveCrashVM\000\000\000", (void*)primitiveCrashVM}, + {(void*)_m, "primitiveDisablePowerManager\000\000\000", (void*)primitiveDisablePowerManager}, + {(void*)_m, "primitiveDivideLargeIntegers\000\000\001", (void*)primitiveDivideLargeIntegers}, + {(void*)_m, "primitiveDivLargeIntegers\000\000\001", (void*)primitiveDivLargeIntegers}, + {(void*)_m, "primitiveEqualLargeIntegers\000\000\001", (void*)primitiveEqualLargeIntegers}, + {(void*)_m, "primitiveEventProcessingControl\000\000\000", (void*)primitiveEventProcessingControl}, + {(void*)_m, "primitiveExternalCallNoOp\000\377\000", (void*)primitiveExternalCallNoOp}, + {(void*)_m, "primitiveFastCNoOp\000\377\001", (void*)primitiveFastCNoOp}, + {(void*)_m, "primitiveFastCNoOpAlignedForFloats\000\377\003", (void*)primitiveFastCNoOpAlignedForFloats}, + {(void*)_m, "primitiveGetenv\000\000\000", (void*)primitiveGetenv}, + {(void*)_m, "primitiveGetLogDirectory\000\377\000", (void*)primitiveGetLogDirectory}, + {(void*)_m, "primitiveGreaterOrEqualLargeIntegers\000\000\001", (void*)primitiveGreaterOrEqualLargeIntegers}, + {(void*)_m, "primitiveGreaterThanLargeIntegers\000\000\001", (void*)primitiveGreaterThanLargeIntegers}, + {(void*)_m, "primitiveHeartbeatFrequency\000\000\000", (void*)primitiveHeartbeatFrequency}, + {(void*)_m, "primitiveHighResClock\000\377\001", (void*)primitiveHighResClock}, + {(void*)_m, "primitiveImageFormatVersion\000\377\000", (void*)primitiveImageFormatVersion}, + {(void*)_m, "primitiveInterruptChecksPerMSec\000\377\000", (void*)primitiveInterruptChecksPerMSec}, + {(void*)_m, "primitiveIsBigEnder\000\377\000", (void*)primitiveIsBigEnder}, + {(void*)_m, "primitiveLessOrEqualLargeIntegers\000\000\001", (void*)primitiveLessOrEqualLargeIntegers}, + {(void*)_m, "primitiveLessThanLargeIntegers\000\000\001", (void*)primitiveLessThanLargeIntegers}, +#if LRPCheck + {(void*)_m, "primitiveLongRunningPrimitive\000\377\000", (void*)primitiveLongRunningPrimitive}, +#endif /* LRPCheck */ +#if LRPCheck + {(void*)_m, "primitiveLongRunningPrimitiveSemaphore\000\000\000", (void*)primitiveLongRunningPrimitiveSemaphore}, +#endif /* LRPCheck */ +#if VMInvestigations + {(void*)_m, "primitiveMethodIsCogged\000\000\000", (void*)primitiveMethodIsCogged}, +#endif /* VMInvestigations */ + {(void*)_m, "primitiveMethodPCData\000\000\000", (void*)primitiveMethodPCData}, + {(void*)_m, "primitiveMillisecondClockMask\000\377\000", (void*)primitiveMillisecondClockMask}, + {(void*)_m, "primitiveMinimumUnusedHeadroom\000\377\000", (void*)primitiveMinimumUnusedHeadroom}, + {(void*)_m, "primitiveModLargeIntegers\000\000\001", (void*)primitiveModLargeIntegers}, + {(void*)_m, "primitiveMultipleBytecodeSetsActive\000\000\000", (void*)primitiveMultipleBytecodeSetsActive}, + {(void*)_m, "primitiveMultiplyLargeIntegers\000\000\001", (void*)primitiveMultiplyLargeIntegers}, + {(void*)_m, "primitiveNotEqualLargeIntegers\000\000\001", (void*)primitiveNotEqualLargeIntegers}, + {(void*)_m, "primitivePathToUsing\000\000\000", (void*)primitivePathToUsing}, + {(void*)_m, "primitiveProfilePrimitive\000\377\001", (void*)primitiveProfilePrimitive}, + {(void*)_m, "primitiveProfileSample\000\377\001", (void*)primitiveProfileSample}, + {(void*)_m, "primitiveProfileSemaphore\000\000\014", (void*)primitiveProfileSemaphore}, + {(void*)_m, "primitiveProfileStart\000\000\001", (void*)primitiveProfileStart}, + {(void*)_m, "primitiveQuoLargeIntegers\000\000\001", (void*)primitiveQuoLargeIntegers}, + {(void*)_m, "primitiveRemLargeIntegers\000\000\001", (void*)primitiveRemLargeIntegers}, + {(void*)_m, "primitiveScreenDepth\000\377\000", (void*)primitiveScreenDepth}, + {(void*)_m, "primitiveScreenScaleFactor\000\377\000", (void*)primitiveScreenScaleFactor}, + {(void*)_m, "primitiveSetGCSemaphore\000\000\000", (void*)primitiveSetGCSemaphore}, + {(void*)_m, "primitiveSetLogDirectory\000\001\000", (void*)primitiveSetLogDirectory}, + {(void*)_m, "primitiveSubtractLargeIntegers\000\000\001", (void*)primitiveSubtractLargeIntegers}, + {(void*)_m, "primitiveSuspendBackingUpV2\000\002\000", (void*)primitiveSuspendBackingUpV2}, +#if TestingPrimitives + {(void*)_m, "primitiveTestShortenIndexableSize\000\001\000", (void*)primitiveTestShortenIndexableSize}, +#endif /* TestingPrimitives */ + {(void*)_m, "primitiveUtcWithOffset\000\001\000", (void*)primitiveUtcWithOffset}, + {(void*)_m, "primitiveVoidReceiver\000\377\000", (void*)primitiveVoidReceiver}, + {NULL, NULL, NULL} +}; diff --git a/src/spur64.cog.livetyping/cointerp.h b/src/spur64.cog.livetyping/cointerp.h new file mode 100644 index 0000000000..3d5371c703 --- /dev/null +++ b/src/spur64.cog.livetyping/cointerp.h @@ -0,0 +1,558 @@ +/* Automatically generated by + CCodeGeneratorGlobalStructure * VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba + (Cog-eem.511, Compiler-ct.535) + */ + + +/*** Function Prototypes ***/ + + +#if !PRODUCTION && defined(PlatformNoDbgRegParms) +# define NoDbgRegParms PlatformNoDbgRegParms +#endif + +#if !defined(NoDbgRegParms) +# define NoDbgRegParms /*empty*/ +#endif + + + +#if !defined(NeverInline) +# define NeverInline /*empty*/ +#endif + +extern sqInt accessorDepthForPrimitiveIndex(sqInt primIndex); +extern usqInt argumentCountAddress(void); +extern void callForCogCompiledCodeCompaction(void); +extern void ceActivateFailingPrimitiveMethod(sqInt aPrimitiveMethod); +extern sqInt ceBaseFrameReturn(sqInt returnValue); +#if IMMUTABILITY +extern sqInt ceCannotAssignTowithIndexvalueToAssign(sqInt immutableObject, sqInt index, sqInt valueToAssign); +#endif /* IMMUTABILITY */ +extern sqInt ceCannotResume(void); +extern void ceCheckAndMaybeRetryPrimitive(sqInt primIndex); +extern void ceCheckForInterrupt(void); +extern sqInt ceContextinstVar(sqInt maybeContext, sqInt slotIndex); +extern sqInt ceContextinstVarvalue(sqInt maybeMarriedContext, sqInt slotIndex, sqInt anOop); +extern sqInt ceInterpretMethodFromPICreceiver(sqInt aMethodObj, sqInt rcvr); +extern sqInt ceMNUFromPICMNUMethodreceiver(sqInt aMethodObj, sqInt rcvr); +extern sqInt ceNewHashOf(sqInt anObject); +extern sqInt ceNonLocalReturn(sqInt returnValue); +extern void ceReapAndResetErrorCodeFor(CogMethod *cogMethod); +extern sqInt ceSendFromInLineCacheMiss(CogMethod *cogMethodOrPIC); +extern sqInt ceSendMustBeBooleanTointerpretingAtDelta(sqInt aNonBooleanObject, sqInt jumpSize); +extern sqInt ceSendMustBeBoolean(sqInt anObject); +extern void ceSendaboveClassBindingtonumArgs(sqInt selector, sqInt methodClassBinding, sqInt rcvr, sqInt numArgs); +extern sqInt ceSendabovetonumArgs(sqInt selector, sqInt methodClass, sqInt rcvr, sqInt numArgs); +extern sqInt ceSendsupertonumArgs(sqInt selector, sqInt superNormalBar, sqInt rcvr, sqInt numArgs); +extern void ceStackOverflow(sqInt contextSwitchIfNotNil); +extern void ceTakeProfileSample(void); +extern void ceTraceBlockActivation(void); +extern void ceTraceLinkedSend(sqInt theReceiver); +extern void ceTraceStoreOfinto(sqInt aValue, sqInt anObject); +extern usqInt cFramePointerAddress(void); +extern sqInt checkForAndFollowForwardedPrimitiveState(void); +extern sqInt checkIfCFramePointerInUse(void); +#if LRPCheck +extern int checkingLongRunningPrimitives(void); +#endif /* LRPCheck */ +extern CogMethod * cogMethodOf(sqInt aMethodOop); +extern void compilationBreakpointFor(sqInt selectorOop); +extern usqInt cReturnAddressAddress(void); +extern usqInt cStackPointerAddress(void); +extern void executeCogMethodfromLinkedSendWithReceiver(CogMethod *cogMethod, sqInt rcvr); +extern void executeCogPICfromLinkedSendWithReceiverandCacheTag(CogMethod *cogPIC, sqInt rcvr, sqInt cacheTag); +extern usqInt framePointerAddress(void); +extern void (*functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(sqInt methodObj, sqInt primitiveIndex, sqInt *flagsPtr))(void); +extern usqInt instructionPointerAddress(void); +extern sqInt interpret(void); +extern int isCogMethodReference(sqInt methodHeader); +extern sqInt isFullBlockMethod(sqInt aMethodObj); +#if LRPCheck +extern usqInt longRunningPrimitiveStopUsecsAddress(void); +#endif /* LRPCheck */ +extern sqInt lookupMNUreceiver(sqInt selector, sqInt rcvr); +extern sqInt lookupOrdinaryreceiver(sqInt selector, sqInt rcvr); +extern void markActiveMethodsAndReferents(void); +extern sqInt maxLookupNoMNUErrorCode(void); +extern usqInt methodCacheAddress(void); +extern int methodHasCogMethod(sqInt aMethodOop); +#if VMInvestigations +extern int methodHeaderHasPrimitive(sqInt methodHeader); +#endif /* VMInvestigations */ +extern int methodNeedsLargeContext(sqInt methodObj); +extern sqInt methodShouldBeCogged(sqInt aMethodObj); +extern CogMethod * mframeHomeMethodExport(void); +extern usqInt newMethodAddress(void); +extern usqInt nextProfileTickAddress(void); +extern sqInt noAssertHeaderOf(sqInt methodPointer); +extern sqInt positive32BitIntegerFor(unsigned int integerValue); +extern unsigned int positive32BitValueOf(sqInt oop); +extern sqInt positive64BitIntegerFor(usqLong integerValue); +extern usqLong positive64BitValueOf(sqInt oop); +extern usqInt primFailCodeAddress(void); +extern sqInt primitivePropertyFlagsprimIndex(sqInt methodObj, sqInt primIndex); +extern void * primTraceLogAddress(void); +extern usqInt primTraceLogIndexAddress(void); +extern void printCogMethod(CogMethod *cogMethod); +extern sqInt quickPrimitiveConstantFor(sqInt aQuickPrimitiveIndex); +extern sqInt (*quickPrimitiveGeneratorFor(sqInt aQuickPrimitiveIndex))(void); +extern sqInt quickPrimitiveInstVarIndexFor(sqInt primIndex); +extern sqInt rawHeaderOf(sqInt methodPointer); +extern void rawHeaderOfput(sqInt methodOop, sqInt cogMethodOrMethodHeader); +extern sqInt recordFastCCallPrimTraceForMethod(sqInt aMethodObj); +extern sqInt recordPrimTraceForMethod(sqInt aMethodObj); +extern sqInt signed32BitIntegerFor(sqInt integerValue); +extern int signed32BitValueOf(sqInt oop); +extern sqInt signed64BitIntegerFor(sqLong integerValue); +extern sqLong signed64BitValueOf(sqInt oop); +extern sqInt specialSelectorNumArgs(sqInt index); +extern usqInt stackLimitAddress(void); +extern usqInt stackPointerAddress(void); +extern usqInt startPCOfMethodHeader(sqInt aCompiledMethodHeader); +extern sqInt startPCOrNilOfLiteralin(sqInt lit, sqInt aMethodObj); +extern void updateStackZoneReferencesToCompiledCodePreCompaction(void); +extern usqInt primitiveFunctionPointerAddress(void); +extern void primitiveClosureValueNoContextSwitch(void); +extern sqInt primitiveFail(void); +extern void primitiveFullClosureValueNoContextSwitch(void); +EXPORT(sqInt) primitiveHighResClock(void); +extern void ceCollectArgumentsTypeForFramefulActivation(void); +extern void ceCollectArgumentsTypeForFramlessSpilledActivationOf(CogMethod *cogMethod); +extern void ceCollectArgumentsTypeForFramlessUnspilledActivationWithArgargof(sqInt arg0, sqInt arg1, CogMethod *cogMethod); +extern void ceCollectArgumentsTypeForFramlessUnspilledActivationWithArgof(sqInt arg, CogMethod *cogMethod); +extern void ceCollectInstVarTypeForatof(sqInt rcvr, sqInt instVarIndex, sqInt assignedObject); +extern void ceCollectRemoteTempVarTypeAtof(sqInt tempVarIndex, sqInt assignedObject); +extern void ceCollectReturnTypeForof(CogMethod *cogMethod, sqInt returnObject); +extern void ceCollectTempVarTypeAtof(sqInt tempVarIndex, sqInt assignedObject); +extern void ceCollectTypesForPrimitiveSlotAtputof(sqInt slotIndex, sqInt assignedObject, sqInt rcvr); +extern void ceScheduleScavenge(void); +extern sqInt classSmallFloat(void); +extern void ensureNoForwardedLiteralsIn(sqInt aMethodObj); +extern usqInt freeStartAddress(void); +extern usqInt getScavengeThreshold(void); +extern sqInt isBytes(sqInt oop); +extern sqInt isForwardedClassIndex(sqInt maybeClassIndex); +extern int isImmediateClass(sqInt classObj); +extern sqInt isReallyYoungObject(sqInt objOop); +extern sqInt methodHeaderOf(sqInt methodObj); +extern usqInt needGCFlagAddress(void); +extern usqInt specialObjectsOopAddress(void); +extern sqInt withoutForwardingOnandwithsendToCogit(sqInt obj1, sqInt obj2, sqInt aBool, sqInt (*selector)(sqInt,sqInt,sqInt)); +extern sqInt fetchClassTagOf(sqInt oop); +extern sqInt floatObjectOf(double aFloat); +extern double floatValueOf(sqInt oop); +extern int headerIndicatesAlternateBytecodeSet(sqInt methodHeader); +extern sqInt instantiateClassindexableSize(sqInt classObj, usqInt nElements); +extern int isIntegerValue(sqInt intValue); +extern double noFailFloatValueOf(sqInt aFloatOop); +extern usqInt smallObjectBytesForSlots(sqInt numSlots); +extern sqInt remember(sqInt objOop); +extern sqInt addressCouldBeObj(sqInt address); +extern sqInt addressCouldBeOop(sqInt address); +extern sqInt allocatePinnedSlots(sqInt nSlots); +extern void beRootIfOld(sqInt oop); +extern sqInt byteSizeOf(sqInt oop); +extern unsigned char bytesPerElement(sqInt oop); +extern sqInt characterObjectOf(sqInt characterCode); +extern sqInt classArray(void); +extern sqInt classAtIndex(sqInt classIndex); +extern sqInt classDoubleWordArray(void); +extern sqInt classIndexOf(sqInt objOop); +extern sqInt classOrNilAtIndex(sqInt classIndex); +extern sqInt classSmallInteger(void); +extern sqInt classTableMinorIndexMask(void); +extern sqInt classTablePageSize(void); +extern sqInt classTableRootObj(void); +extern sqInt classTagForClass(sqInt classObj); +extern sqInt classWordArray(void); +extern usqInt eeInstantiateClassIndexformatnumSlots(sqInt knownClassIndex, sqInt objFormat, sqInt numSlots); +extern NeverInline void eek(void); +extern sqInt falseObject(void); +extern sqInt fetchByteofObject(sqInt byteIndex, sqInt objOop); +extern int fetchLong32ofObject(sqInt fieldIndex, sqInt oop); +extern sqInt fetchPointerofObject(sqInt fieldIndex, sqInt objOop); +extern void * firstIndexableField(sqInt objOop); +extern sqInt fixedFieldsOfClassFormatMask(void); +extern sqInt fixedFieldsOfClassFormat(sqInt classFormat); +extern sqInt followForwarded(sqInt objOop); +extern sqInt formatOfClass(sqInt classPointer); +extern sqInt freeObject(sqInt objOop); +extern usqLong headerForSlotsformatclassIndex(sqInt numSlots, sqInt formatField, sqInt classIndex); +#if IMMUTABILITY +extern sqInt immutableBitMask(void); +#endif /* IMMUTABILITY */ +extern sqInt instSpecOfClassFormat(sqInt classFormat); +extern int isCharacterObject(sqInt oop); +extern int isCompiledMethod(sqInt objOop); +extern sqInt isFixedSizePointerFormat(sqInt format); +extern int isForwarded(sqInt objOop); +extern int isImmediate(sqInt oop); +extern sqInt isInOldSpace(sqInt address); +extern sqInt isLong64s(sqInt oop); +extern int isMarked(sqInt objOop); +extern int isNonImmediate(sqInt oop); +extern sqInt isOopCompiledMethod(sqInt oop); +extern sqInt isOopForwarded(sqInt oop); +extern sqInt isOopMutable(sqInt oop); +extern sqInt isShorts(sqInt oop); +extern sqInt isValidClassTag(sqInt classIndex); +extern int isYoungObject(sqInt objOop); +extern sqInt isYoung(sqInt oop); +extern int leakCheckFullGC(void); +extern int leakCheckNewSpaceGC(void); +extern usqInt literalCountOfMethodHeader(sqInt header); +extern usqInt literalCountOf(sqInt methodPointer); +extern NeverInline void markAndTrace(sqInt objOop); +extern sqInt maxIdentityHash(void); +extern sqInt maxSlotsForNewSpaceAlloc(void); +extern sqInt maybeMethodClassOfseemsToBeInstantiating(sqInt methodObj, sqInt format); +extern sqInt minSlotsForShortening(void); +extern sqInt nilObject(void); +extern sqInt numBytesOf(sqInt objOop); +extern usqInt numSlotsOf(sqInt objOop); +extern sqInt objCouldBeClassObj(sqInt objOop); +extern sqInt objectAfter(sqInt objOop); +extern sqInt popRemappableOop(void); +extern sqInt primitiveErrorTable(void); +extern void printObjStackAndContents(sqInt objStack); +extern void pushRemappableOop(sqInt oop); +extern int rawHashBitsOf(sqInt objOop); +extern sqInt receiverTagBitsForMethod(sqInt aMethodObj); +extern sqInt shortentoIndexableSize(sqInt objOop, sqInt indexableSize); +extern sqInt shouldRemapOop(sqInt oop); +extern sqInt splObj(sqInt index); +extern usqInt storeCheckBoundary(void); +extern int storeLong32ofObjectwithValue(sqInt fieldIndex, sqInt obj, sqInt valueWord); +extern sqInt storePointerUncheckedofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqInt valuePointer); +extern sqInt storePointerofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqInt valuePointer); +extern sqInt stringForCString(const char *aCString); +extern void tenuringIncrementalGC(void); +extern sqInt topRemappableOop(void); +extern sqInt trueObject(void); +extern sqInt remapObj(sqInt objOop); +extern sqInt shouldRemapObj(sqInt objOop); +extern sqInt accessorDepthForPrimitiveMethod(sqInt aMethodObj); +extern sqInt argumentCountOfClosure(sqInt closurePointer); +extern usqInt argumentCountOfMethodHeader(sqInt header); +extern usqInt argumentCountOf(sqInt methodPointer); +extern sqInt canContextSwitchIfActivatingheader(sqInt theMethod, sqInt methodHeader); +extern sqInt copiedValueCountOfClosure(sqInt closureObj); +#if SEND_PRINTING +extern void disableSendPrinting(void); +#endif /* SEND_PRINTING */ +extern void dumpTraceLog(void); +extern NeverInline void eekcr(void); +#if SEND_PRINTING +extern void enableSendPrinting(void); +#endif /* SEND_PRINTING */ +extern void flush(void); +extern sqInt getThisSessionID(void); +extern FILE * getTranscript(void); +extern sqInt highBit(usqInt anUnsignedValue); +extern int isQuickPrimitiveIndex(sqInt anInteger); +extern int isReadMediatedContextInstVarIndex(sqInt index); +extern int isWriteMediatedContextInstVarIndex(sqInt index); +extern sqInt literalofMethod(sqInt offset, sqInt methodPointer); +extern sqInt longStoreBytecodeForHeader(sqInt methodHeader); +extern sqInt lookupSelectorinClass(sqInt selector, sqInt class); +extern sqInt maybeSelectorOfMethod(sqInt methodObj); +extern sqInt methodClassAssociationOf(sqInt methodPointer); +extern sqInt methodClassOf(sqInt methodPointer); +extern int methodUsesAlternateBytecodeSet(sqInt aMethodObj); +extern sqInt popStack(void); +extern sqInt primitiveIndexOfMethodheader(sqInt theMethod, sqInt methodHeader); +extern sqInt primitiveIndexOf(sqInt methodPointer); +extern void printHex(usqInt n); +extern void print(char *s); +extern sqInt sizeOfCallPrimitiveBytecode(sqInt methodHeader); +extern sqInt sizeOfLongStoreTempBytecode(sqInt methodHeader); +extern sqInt specialSelector(sqInt index); +extern sqInt stackTop(void); +extern sqInt stackValue(sqInt offset); +extern usqInt startPCOfMethod(sqInt aCompiledMethod); +extern usqInt tempCountOf(sqInt methodPointer); +extern usqInt temporaryCountOfMethodHeader(sqInt header); + + +/*** Global Variables ***/ +extern char * breakSelector; +extern sqInt breakSelectorLength ; +extern usqInt heapBase; +extern sqInt imageHeaderFlags; +extern sqInt suppressHeartbeatFlag; + + +/*** Macros ***/ +#define compilationBreakpointclassTagisMNUCase(sel, classTag, isMNU) do { \ + if (numBytesOf(sel) == (isMNU ? -breakSelectorLength : breakSelectorLength) \ + && !strncmp((char *)((sel) + BaseHeaderSize), breakSelector, (isMNU ? -breakSelectorLength : breakSelectorLength))) { \ + suppressHeartbeatFlag = 1; \ + compilationBreakpointFor(sel); \ + } \ +} while (0) +#define compilationBreakpointisMNUCase(sel, isMNU) do { \ + if (numBytesOf(sel) == (isMNU ? -breakSelectorLength : breakSelectorLength) \ + && !strncmp((char *)((sel) + BaseHeaderSize), breakSelector, (isMNU ? -breakSelectorLength : breakSelectorLength))) { \ + suppressHeartbeatFlag = 1; \ + compilationBreakpointFor(sel); \ + } \ +} while (0) +#define nullHeaderForMachineCodeMethod() 0x8000000A000035LL +#define startOfMemory() heapBase +#define numTagBits() 3 +#define shiftForWord() 3 +#define smallFloatExponentBits() 8 +#define smallFloatExponentOffset() 896 +#define smallFloatMantissaBits() 52 +#define smallFloatTag() 4 +#define tagMask() 0x7 +#define arrayFormat() 2 +#define characterTag() 2 +#define classIndexMask() 0x3FFFFF +#define classTableMajorIndexShift() 10 +#define firstByteFormat() 16 +#define firstCompiledMethodFormat() 24 +#define firstLongFormat() 10 +#define firstShortFormat() 12 +#define fixedFieldsFieldWidth() 16 +#define formatFieldByteOffset() 3 +#define formatMask() 0x1F +#define formatShift() 24 +#define identityHashFullWordShift() 32 +#define identityHashHalfWordMask() 0x3FFFFF +#define indexablePointersFormat() 3 +#define isForwardedObjectClassIndexPun() 8 +#define nonIndexablePointerFormat() 1 +#define numSlotsFieldByteOffset() 7 +#define numSlotsFullShift() 56 +#define numSlotsHalfShift() 24 +#define numSlotsMask() 0xFF +#define rememberedBitByteShift() 5 +#define rememberedBitShift() 29 +#define sixtyFourBitIndexableFormat() 9 +#define smallIntegerTag() 1 +#define weakArrayFormat() 4 +#define alternateHeaderNumLiteralsMask() 0x7FFF +#define numericPrimsMixArithmetic() (imageHeaderFlags & 0x100) +#define numericPrimsMixComparison() (imageHeaderFlags & 0x800) +#define remoteIsInstVarAccess() 128 + + +/*** Constants ***/ +#define ActiveProcessIndex 1 +#define BytecodeSetHasDirectedSuperSend 1 +#define BytecodeSetHasExtensions 1 +#define CharacterTable null +#define CharacterValueIndex 0 +#define ClassAlien 52 +#define ClassArray 7 +#define ClassArrayCompactIndex 51 +#define ClassBitmap 4 +#define ClassBitmapCompactIndex 53 +#define ClassBlockClosure 36 +#define ClassBlockClosureCompactIndex 37 +#define ClassBlockContext 11 +#define ClassBlockContextCompactIndex 0 +#define ClassByteArray 26 +#define ClassByteArrayCompactIndex 50 +#define ClassByteString 6 +#define ClassByteStringCompactIndex 52 +#define ClassCharacter 19 +#define ClassDoubleByteArray 31 +#define ClassDoubleWordArray 33 +#define ClassExternalAddress 43 +#define ClassExternalData 45 +#define ClassExternalFunction 46 +#define ClassExternalLibrary 47 +#define ClassExternalStructure 44 +#define ClassFloat 9 +#define ClassFloatCompactIndex 34 +#define ClassFullBlockClosure 37 +#define ClassFullBlockClosureCompactIndex 38 +#define ClassLargeNegativeInteger 42 +#define ClassLargeNegativeIntegerCompactIndex 32 +#define ClassLargePositiveInteger 13 +#define ClassLargePositiveIntegerCompactIndex 33 +#define ClassMessage 15 +#define ClassMessageCompactIndex 35 +#define ClassMethodContext 10 +#define ClassMethodContextCompactIndex 36 +#define ClassPoint 12 +#define ClassPointCompactIndex 54 +#define ClassSemaphore 18 +#define ClassSmallInteger 5 +#define ClassUnsafeAlien 54 +#define ClassWeakFinalizer 55 +#define ClassWordArray 32 +#define ClosureFirstCopiedValueIndex 3 +#define ClosureIndex 4 +#define ClosureNumArgsIndex 2 +#define ClosureOuterContextIndex 0 +#define ClosureStartPCIndex 1 +#define CMBlock 4 +#define CMClosedPIC 2 +#define CMFree 1 +#define CMMaxUsageCount 7 +#define CMMethod 5 +#define CMMethodFlaggedForBecome 6 +#define CMOpenPIC 3 +#define CompactClasses 28 +#define CompletePrimitive 4 +#define ConstMinusOne 0xFFFFFFFFFFFFFFF9ULL +#define ConstOne 9 +#define ConstTwo 17 +#define ConstZero 1 +#define CtxtTempFrameStart 6 +#define DisownVMForFFICallFlag 8 +#define DisownVMFromCallbackFlag 16 +#define DoAssertionChecks (!PRODUCTION) +#define DoExpensiveAssertionChecks 0 +#define EncounteredUnknownBytecode -6 +#define ExcessSignalsIndex 2 +#define ExternalObjectsArray 38 +#define FalseObject 1 +#define FastCPrimitiveAlignForFloatsFlag 2 +#define FastCPrimitiveFlag 1 +#define FirstLinkIndex 0 +#define ForeignCallbackProcess 56 +#undef FoxCallerContext +#define FoxCallerSavedIP 8 +#undef FoxFrameFlags +#define FoxIFReceiver -40 +#define FoxIFrameFlags -24 +#define FoxIFSavedIP -32 +#define FoxMethod -8 +#define FoxMFReceiver -24 +#undef FoxReceiver +#define FoxSavedFP 0 +#define FoxThisContext -16 +#undef FrameSlots +#define FullClosureCompiledBlockIndex 1 +#define FullClosureFirstCopiedValueIndex 4 +#define FullClosureReceiverIndex 3 +#define GCCheckFreeSpace 32 +#define GCCheckPrimCall 128 +#define GCCheckShorten 64 +#define GCModeBecome 8 +#define GCModeFull 1 +#define GCModeImageSegment 16 +#define GCModeIncremental 4 +#define GCModeNewSpace 2 +#define HashMultiplyConstant 1664525 +#define HashMultiplyMask 0xFFFFFFF +#define HeaderIndex 0 +#define IFrameSlots 7 +#define InstanceSpecificationIndex 2 +#define InstructionPointerIndex 1 +#define InsufficientCodeSpace -2 +#define KeyIndex 0 +#define LargeContextBit 0x100000 +#define LargeContextSize bogus +#define LargeContextSlots 62 +#define LastLinkIndex 1 +#define LiteralStart 1 +#define LookupRuleDynamicSuper 0x101 +#define LookupRuleImplicit 0x100 +#define LookupRuleMNU 259 +#define LookupRuleOrdinary 258 +#define LookupRuleSelf 0 +#define LowcodeContextMark 60 +#define LowcodeNativeContextClass 61 +#define MaxLiteralCountForCompile 60 +#define MaxMethodSize 65535 +#define MaxNegativeErrorCode -8 +#define MaxNumArgs 15 +#define MaxStackCheckOffset 0xFFF +#define MessageArgumentsIndex 1 +#define MessageLookupClassIndex 2 +#define MessageSelectorIndex 0 +#define MethodArrayIndex 1 +#define MethodCacheClass 2 +#define MethodCacheEntries 0x400 +#define MethodCacheEntrySize 4 +#define MethodCacheMask 0xFFC +#define MethodCacheMethod 3 +#define MethodCachePrim null +#define MethodCachePrimFunction 4 +#define MethodCacheSelector 1 +#define MethodCacheSize 4096 +#define MethodDictionaryIndex 1 +#define MethodIndex 3 +#define MethodTooBig -4 +#define MFMethodFlagHasContextFlag 1 +#define MFMethodFlagIsBlockFlag 2 +#define MFMethodFlagsMask 0x7 +#define MFMethodMask -0x8 +#define MFrameSlots 5 +#define MyListIndex 3 +#define NewsqueakV4BytecodeSet 0 +#define NextLinkIndex 0 +#define NilObject 0 +#define NotFullyInitialized -1 +#define NSMethodCacheActualReceiver 7 +#define NSMethodCacheCallingMethod 3 +#define NSMethodCacheClassTag 2 +#define NSMethodCacheDepthOrLookupRule 4 +#define NSMethodCacheEntries 0x200 +#define NSMethodCacheEntrySize 8 +#define NSMethodCacheMask 0xFF8 +#define NSMethodCachePrimFunction 6 +#define NSMethodCacheSelector 1 +#define NSMethodCacheSize 4096 +#define NSMethodCacheTargetMethod 5 +#define OwnVMForFFIExceptionFlag 16 +#define PrimCallCollectsProfileSamples 16 +#define PrimCallIsExternalCall 32 +#define PrimCallMayEndureCodeCompaction 8 +#define PrimCallNeedsNewMethod 4 +#define PrimCallOnSmalltalkStack 1 +#define PrimCallOnSmalltalkStackAlign2x 2 +#define PrimitiveErrorTableIndex 51 +#define PrimNumberExternalCall 117 +#define PrimNumberFFICall 120 +#define PrimNumberHandlerMarker 199 +#define PrimNumberNoContextSwitchMarker 123 +#define PrimNumberUnwindMarker 198 +#define PriorityIndex 2 +#define ProcessInExternalCodeTag 40 +#define ProcessListsIndex 0 +#define ProcessSignalingLowSpace 22 +#define ReceiverIndex 5 +#define SchedulerAssociation 3 +#define SelectorAboutToReturn 48 +#define SelectorAttemptToAssign 50 +#define SelectorCannotInterpret 34 +#define SelectorCannotReturn 21 +#define SelectorCounterTripped 58 +#define SelectorDoesNotUnderstand 20 +#define SelectorInvokeCallback 53 +#define SelectorMustBeBoolean 25 +#define SelectorRunWithIn 49 +#define SelectorSistaTrap 59 +#define SelectorStart 2 +#define SelectorUnknownBytecode 57 +#define SenderIndex 0 +#define ShouldNotJIT -8 +#define SistaV1BytecodeSet 1 +#define SmallContextSize bogus +#define SmallContextSlots 22 +#define SpecialSelectors 23 +#define SqueakV3PlusClosuresBytecodeSet 1 +#define StackPointerIndex 2 +#define SuperclassIndex 0 +#define SuspendedContextIndex 1 +#define TheDisplay 14 +#define TheFinalizationSemaphore 41 +#define TheInputSemaphore null +#define TheInterruptSemaphore 30 +#define TheLowSpaceSemaphore 17 +#define TheTimerSemaphore 29 +#define TrueObject 2 +#define UnfailingPrimitive 3 +#define UnimplementedPrimitive -7 +#define ValueIndex 1 +#define XIndex 0 +#define YIndex 1 +#define YoungSelectorInPIC -5 + diff --git a/src/spur64.cog.livetyping/cointerpmt.c b/src/spur64.cog.livetyping/cointerpmt.c new file mode 100644 index 0000000000..f0dc47c4de --- /dev/null +++ b/src/spur64.cog.livetyping/cointerpmt.c @@ -0,0 +1,87672 @@ +/* Automatically generated by + CCodeGeneratorGlobalStructure VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba + (Cog-eem.511, Compiler-ct.535) + from + CoInterpreterMT VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba + */ +static char __buildInfo[] = "CoInterpreterMT VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba " __DATE__ ; +char *__interpBuildInfo = __buildInfo; + + + +#if USE_GLOBAL_STRUCT +# define SQ_USE_GLOBAL_STRUCT 1 +#else +# define SQ_USE_GLOBAL_STRUCT 0 +#endif +#if USE_GLOBAL_STRUCT_REG +# define SQ_USE_GLOBAL_STRUCT_REG 1 +#else +# define SQ_USE_GLOBAL_STRUCT_REG 0 +#endif + +#include "sq.h" +#include /* for printf */ +#include /* for e.g. alloca */ +#include +#include /* for wint_t */ +#include "vmCallback.h" +#include "sqMemoryFence.h" +#include "sqImageFileAccess.h" +#include "sqSetjmpShim.h" +#include "dispdbg.h" +#include "sqCogStackAlignment.h" +#include "cogmethod.h" +#include "cointerp.h" +#include "cogit.h" +#include "sqAtomicOps.h" +#include + + +/* StackInterpreter class>>preambleCCode */ +/* Disable Intel compiler inlining of warning which is used for breakpoints */ +#pragma auto_inline(off) +sqInt warnpid, erroronwarn; +EXPORT(void) +warning(const char *s) { /* Print an error message but don't necessarily exit. */ + if (erroronwarn) error(s); + if (warnpid) + printf("\n%s pid %ld\n", s, (long)warnpid); + else + printf("\n%s\n", s); +} +EXPORT(void) +warningat(const char *s, int l) { /* ditto with line number. */ + /* use alloca to call warning so one does not have to remember to set multiple breakpoints... */ + char *sl = alloca(strlen(s) + 16); + sprintf(sl, "%s %d", s, l); + warning(sl); +} +EXPORT(void) +warninginat(const char *s, const char *f, int l) { /* ditto with function name & line number. */ + /* use alloca to call warning so one does not have to remember to set multiple breakpoints... */ + char *sl = alloca(strlen(s) + strlen(f) + 17); + sprintf(sl, "%s %s %d", s, f, l); + warning(sl); +} +#pragma auto_inline(on) + +/* end StackInterpreter class>>preambleCCode */ + + +/*** Constants ***/ +#define ActiveProcessIndex 1 +#define AlternateHeaderHasPrimFlag 0x80000 +#define AlternateHeaderNumLiteralsMask 0x7FFF +#define AltLongStoreBytecode 245 +#define BecameActiveClassFlag 8 +#define BecameCompiledMethodFlag 2 +#define BecameJittedCompiledMethodFlag 16 +#define BecamePointerObjectFlag 1 +#define CacheProbeMax 3 +#define ClassAlien 52 +#define ClassArray 7 +#define ClassArrayCompactIndex 51 +#define ClassBitmap 4 +#define ClassBitmapCompactIndex 53 +#define ClassBlockClosure 36 +#define ClassBlockClosureCompactIndex 37 +#define ClassByteArray 26 +#define ClassByteString 6 +#define ClassByteStringCompactIndex 52 +#define ClassCharacter 19 +#define ClassDoubleByteArray 31 +#define ClassDoubleWordArray 33 +#define ClassExternalAddress 43 +#define ClassExternalData 45 +#define ClassExternalFunction 46 +#define ClassExternalLibrary 47 +#define ClassExternalStructure 44 +#define ClassFloat 9 +#define ClassFloatCompactIndex 34 +#define ClassFullBlockClosureCompactIndex 38 +#define ClassLargeNegativeInteger 42 +#define ClassLargeNegativeIntegerCompactIndex 32 +#define ClassLargePositiveInteger 13 +#define ClassLargePositiveIntegerCompactIndex 33 +#define ClassMessage 15 +#define ClassMessageCompactIndex 35 +#define ClassMethodContext 10 +#define ClassMethodContextCompactIndex 36 +#define ClassPoint 12 +#define ClassPointCompactIndex 54 +#define ClassSemaphore 18 +#define ClassSmallInteger 5 +#define ClassUnsafeAlien 54 +#define ClassWordArray 32 +#define ClosureFirstCopiedValueIndex 3 +#define ClosureIndex 4 +#define ClosureNumArgsIndex 2 +#define ClosureOuterContextIndex 0 +#define ClosureStartPCIndex 1 +#define CMBlock 4 +#define CMClosedPIC 2 +#define CMMethod 5 +#define CMMethodFlaggedForBecome 6 +#define CMOpenPIC 3 +#define ConstMinusOne 0xFFFFFFFFFFFFFFF9ULL +#define ConstOne 9 +#define ConstTwo 17 +#define ConstZero 1 +#define CrossedX 258 +#define CSCheckEvents 18 +#define CSEnterCriticalSection 2 +#define CSExitCriticalSection 4 +#define CSFromInterpreter 0 +#define CSFromMachineCode 1 +#define CSResume 6 +#define CSSetPriority 16 +#define CSSuspend 10 +#define CSSwitchIfNeccessary 28 +#define CSSynchronousSignal 8 +#define CSThreadBind 26 +#define CSTryToExecuteSmalltalk 30 +#define CSWait 12 +#define CSYield 14 +#define CTMAssignableOrInVM 3 +#define CTMInitializing 1 +#define CTMUnavailable 2 +#define CTMUninitialized 0 +#define CTMUnknownOwner -1 +#define CTMWantingOwnership 4 +#define CtxtTempFrameStart 6 +#define DisownVMForFFICallFlag 8 +#define DisownVMForProcessorRelinquishFlag 32 +#define DisownVMFromCallbackFlag 16 +#define EncounteredUnknownBytecode -6 +#define EndOfRun 0x101 +#define ExcessSignalsIndex 2 +#define ExternalCallLiteralFlagsIndex 2 +#define ExternalCallLiteralFunctionNameIndex 1 +#define ExternalCallLiteralModuleNameIndex 0 +#define ExternalCallLiteralTargetFunctionIndex 3 +#define ExternalObjectsArray 38 +#define ExtraRootsSize 64 +#define FalseObject 1 +#define FirstLinkIndex 0 +#define ForeignCallbackProcess 56 +#define FoxCallerSavedIP 8 +#define FoxIFReceiver -40 +#define FoxIFrameFlags -24 +#define FoxIFSavedIP -32 +#define FoxMethod -8 +#define FoxMFReceiver -24 +#define FoxSavedFP 0 +#define FoxThisContext -16 +#define FullClosureCompiledBlockIndex 1 +#define FullClosureFirstCopiedValueIndex 4 +#define FullClosureReceiverIndex 3 +#define GCCheckFreeSpace 32 +#define GCCheckPrimCall 128 +#define GCCheckShorten 64 +#define GCModeBecome 8 +#define GCModeFull 1 +#define GCModeImageSegment 16 +#define GCModeNewSpace 2 +#define HasBeenReturnedFromMCPC -1 +#define HasBeenReturnedFromMCPCOop 0xFFFFFFFFFFFFFFF9ULL +#define HashMultiplyConstant 1664525 +#define HeaderIndex 0 +#define IFrameSlots 7 +#define InstanceSpecificationIndex 2 +#define InstructionPointerIndex 1 +#define KeyIndex 0 +#define LargeContextBit 0x100000 +#define LargeContextSlots 62 +#define LastLinkIndex 1 +#define LiteralStart 1 +#define LongStoreBytecode 129 +#define MarkOnTenure 5 +#define MarkStackRootIndex 0x1000 +#define MaxExternalPrimitiveTableSize 4096 +#define MaxLiteralCountForCompile 60 +#define MaxNegativeErrorCode -8 +#define MaxPrimitiveIndex 583 +#define MaxQuickPrimitiveIndex 519 +#define MaxRTRefCount 7 +#define MessageArgumentsIndex 1 +#define MessageLookupClassIndex 2 +#define MessageSelectorIndex 0 +#define MethodArrayIndex 1 +#define MethodCacheClass 2 +#define MethodCacheEntries 0x400 +#define MethodCacheEntrySize 4 +#define MethodCacheMask 0xFFC +#define MethodCacheMethod 3 +#define MethodCachePrimFunction 4 +#define MethodCacheSelector 1 +#define MethodCacheSize 4096 +#define MethodDictionaryIndex 1 +#define MethodHeaderArgCountShift 27 +#define MethodHeaderFlagBitPosition 31 +#define MethodHeaderTempCountShift 21 +#define MethodIndex 3 +#define MethodTooBig -4 +#define MFMethodFlagHasContextFlag 1 +#define MFMethodFlagIsBlockFlag 2 +#define MFMethodFlagsMask 0x7 +#define MFMethodMask -0x8 +#define MFrameSlots 5 +#define MillisecondClockMask 0x1FFFFFFF +#define MinBackwardJumpCountForCompile 40 +#define MournQueueRootIndex 4098 +#define MultipleBytecodeSetsBitmask 0x200 +#define MyListIndex 3 +#define NextLinkIndex 0 +#define NilObject 0 +#define NotFullyInitialized -1 +#define ObjStackFixedSlots 4 +#define ObjStackFreex 2 +#define ObjStackLimit 0xFF8 +#define ObjStackMyx 1 +#define ObjStackNextx 3 +#define ObjStackPageSlots 0xFFC +#define ObjStackTopx 0 +#define OldBecameNewFlag 4 +#define OwnedVMFromForeignThreadFlag 2 +#define OwnerLogSize 65536 +#define OwnVMForFFIExceptionFlag 16 +#define PrimCallCollectsProfileSamples 16 +#define PrimCallIsExternalCall 32 +#define PrimCallMayEndureCodeCompaction 8 +#define PrimCallNeedsNewMethod 4 +#define PrimCallOnSmalltalkStack 1 +#define PrimCallOnSmalltalkStackAlign2x 2 +#define PrimitiveErrorTableIndex 51 +#define PrimNumberDoExternalCall 218 +#define PrimNumberDoPrimitive 118 +#define PrimNumberExternalCall 117 +#define PrimNumberFFICall 120 +#define PrimNumberHandlerMarker 199 +#define PrimNumberNoContextSwitchMarker 123 +#define PrimNumberRelinquishProcessor 230 +#define PrimNumberUnwindMarker 198 +#define PrimNumberVMParameter 254 +#define PrimTraceLogSize 256 +#define PriorityIndex 2 +#define ProcessListsIndex 0 +#define ProcessSignalingLowSpace 22 +#define ReceiverIndex 5 +#define RemapBufferSize 25 +#define RememberedSetRootIndex 4099 +#define ReturnToInterpreter 1 +#define ScavengeInProgress 1 +#define SchedulerAssociation 3 +#define SelectorAboutToReturn 48 +#define SelectorAttemptToAssign 50 +#define SelectorCannotInterpret 34 +#define SelectorCannotReturn 21 +#define SelectorDoesNotUnderstand 20 +#define SelectorInvokeCallback 53 +#define SelectorMustBeBoolean 25 +#define SelectorRunWithIn 49 +#define SelectorStart 2 +#define SelectorUnknownBytecode 57 +#define SenderIndex 0 +#define ShouldNotJIT -8 +#define SlidingCompactionInProgress 2 +#define SmallContextSlots 22 +#define SpecialSelectors 23 +#define SpurPrimitiveAccessorDepthShift 8 +#define SpurPrimitiveFlagsMask 0xFF +#define StackPageReachedButUntraced 1 +#define StackPageTraced 2 +#define StackPageTraceInvalid -1 +#define StackPageUnreached 0 +#define StackPointerIndex 2 +#define SuperclassIndex 0 +#define SuspendedContextIndex 1 +#define TenureByAge 1 +#define TenureByClass 2 +#define TenureToShrinkRT 3 +#define TheDisplay 14 +#define TheFinalizationSemaphore 41 +#define TheInterruptSemaphore 30 +#define TheLowSpaceSemaphore 17 +#define TheTimerSemaphore 29 +#define ThreadAffinityIndex 4 +#define ThreadIdShift 16 +#define TopHashBit 0x200000 +#define TopOopBit 0x8000000000000000ULL +#define TraceAttemptOwnVM 12 +#define TraceBlockActivation 2 +#define TraceBlockCreation 3 +#define TraceBufferSize 768 +#define TraceCodeCompaction 6 +#define TraceContextSwitch 1 +#define TraceDisownVM 14 +#define TraceFullGC 5 +#define TraceIncrementalGC 4 +#if !defined(TraceLog) /* Allow this to be overridden on the compiler command line */ +# define TraceLog 0 +#endif +#define TraceOwnerShift 16 +#define TraceOwnVM 13 +#define TracePreemptDisowningThread 16 +#define TracePrimitiveFailure 10 +#define TracePrimitiveRetry 11 +#define TraceSend 0 +#define TraceStackOverflow 9 +#define TraceThreadSwitch 15 +#define TraceTypeShift 8 +#define TraceVMCallback 7 +#define TraceVMCallbackReturn 8 +#define TrueObject 2 +#define ValidatedClassDoubleByteArrayFlag 1 +#define ValidatedClassDoubleWordArrayFlag 4 +#define ValidatedClassWordArrayFlag 2 +#define ValueIndex 1 +#define VMAlreadyOwnedHenceDoNotDisownFlag 1 +#define WeaklingStackRootIndex 0x1001 +#define XIndex 0 +#define YIndex 1 + +typedef struct { + usqInt sEdenBytes; + usqInt sPastBytes; + usqInt sRememberedSetSize; + usqInt sRememberedSetRedZone; + usqInt sRememberedSetLimit; + usqInt sStatTenures; + usqInt tTenureCriterion; + usqInt tTenureThreshold; + usqInt tRefCountToShrinkRT; + usqInt eSurvivorBytes; + usqInt eRememberedSetSize; + usqInt eStatTenures; + } SpurScavengeLogRecord; + + +typedef struct { + usqInt segStart; + usqInt segSize; + sqInt swizzle; + usqInt containsPinned; + usqInt savedSegSize; + usqInt lastFreeObject; + } SpurSegmentInfo; + + +typedef struct { + usqInt start; + usqInt limit; + } SpurNewSpaceSpace; + + +typedef struct { + usqInt start; + usqInt limit; + usqInt top; + } SpurContiguousObjStack; + + +typedef struct _StackPage { + char *stackLimit; + char *headSP; + char *headFP; + char *baseFP; + char *baseAddress; + char *realStackLimit; + char *lastAddress; + int trace; + int padToWord; + struct _StackPage *nextPage; + struct _StackPage *prevPage; + } StackPage; + +#define CogStackPage StackPage + + +typedef struct _DisownState { + sqInt newMethodOrNull; + usqIntptr_t cStackPointer; + usqIntptr_t cFramePointer; + unsigned char argumentCount; +#ifndef NDEBUG + unsigned char inMachineCode; +#endif +#ifndef NDEBUG + unsigned char stackPtrIndex; +#endif + struct _DisownState *nestedDisownStates; + } DisownState; + + +typedef struct { + sqInt newMethodOrNull; + usqIntptr_t cStackPointer; + usqIntptr_t cFramePointer; + unsigned char argumentCount; +#ifndef NDEBUG + unsigned char inMachineCode; +#endif +#ifndef NDEBUG + unsigned char stackPtrIndex; +#endif + struct _DisownState *nestedDisownStates; + sqInt index; + volatile atomic_int state; + sqInt priority; + sqInt boundProcess; + usqIntptr_t bindingPointKey; + sqInt affinityOnBind; + sqOSSemaphore osSemaphore; + sqOSThread osThread; + jmp_buf reenterThreadSchedulingLoop; + } CogVMThread; + + +typedef struct { + usqLong timestamp; + int vmOwner; + int successfulSwitch; + } CogVMOwnerLog; + + + +/*** Function Prototypes ***/ + + +#if !PRODUCTION && defined(PlatformNoDbgRegParms) +# define NoDbgRegParms PlatformNoDbgRegParms +#endif + +#if !defined(NoDbgRegParms) +# define NoDbgRegParms /*empty*/ +#endif + + + +#if !defined(NeverInline) +# define NeverInline /*empty*/ +#endif + +extern sqInt interpret(void); +static NoDbgRegParms CogMethod * cmHomeMethod(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMBlock(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMClosedPIC(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMMethodEtAl(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMMethodFlaggedForBecome(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMOpenPIC(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms sqInt addressIsInPage(StackPage *self_in_CogStackPage, char *address); +static NoDbgRegParms int isFree(StackPage *self_in_CogStackPage); +static NoDbgRegParms sqInt freeStackPageNoAssert(StackPage *aPage); +static NoDbgRegParms void freeStackPage(StackPage *aPage); +static NoDbgRegParms sqInt markStackPageMostRecentlyUsed(StackPage *page); +static NoDbgRegParms sqInt markStackPageNextMostRecentlyUsed(StackPage *page); +static StackPage * newStackPage(void); +static sqInt pageListIsWellFormed(void); +static NoDbgRegParms StackPage * stackPageAt(sqInt index); +static NoDbgRegParms StackPage * stackPageFor(void *pointer); +static double statAverageLivePagesWhenMapping(void); +extern int vmIsInitialized(void); +static NoDbgRegParms CogVMThread * affinedThreadOrNilForProcesscurrentAffinityoop(sqInt aProcess, sqInt currentAffinity, sqInt threadAffinityOop); +static CogVMThread * currentVMThread(void); +extern void dumpOwnerLog(void); +extern void dumpOwnerLogMax(sqInt maxEntries); +extern void dumpOwnerLogOn(FILE *aFile); +extern void dumpOwnerLogOnMax(FILE *aFile, sqInt maxEntries); +static CogVMThread * ensureWillingThread(void); +extern sqOSThread getVMOSThread(void); +static sqInt getVMOwner(void); +static NoDbgRegParms sqInt growThreadInfosToAtLeast(sqInt index); +static NoDbgRegParms sqInt saveOwnerSwitchTosuccessful(sqInt newOwner, sqInt aBoolean); +static NoDbgRegParms sqInt startThreadForThreadIndex(sqInt index); +static NoDbgRegParms sqInt startThreadForThreadInfo(CogVMThread *vmThread); +static sqInt startThreadSubsystem(void); +static NoDbgRegParms void startVMThread(CogVMThread *vmThread); +static NoDbgRegParms sqInt threadIndexisCompatibleWith(sqInt aThreadIndex, sqInt threadAffinity); +static CogVMThread * unusedThreadInfo(void); +extern sqInt vmIsOwned(void); +static int vmIsOwnedOrLocked(void); +static NoDbgRegParms sqInt vmOwnerIsCompatibleWith(sqInt processThreadId); +static NoDbgRegParms int vmOwnerIs(sqInt index); +static NoDbgRegParms CogVMThread * vmThreadAt(sqInt index); +static NoDbgRegParms void wakeVMThread(CogVMThread *vmThread); +static CogVMThread * willingVMThread(void); +static NoDbgRegParms CogVMThread * initializeThreadState(CogVMThread *self_in_CogVMThread); +static NoDbgRegParms sqInt vmThreadState(CogVMThread *self_in_CogVMThread); +extern sqInt accessorDepthForPrimitiveIndex(sqInt primIndex); +static NoDbgRegParms void activateNewCogMethodinInterpreter(CogMethod *cogMethod, sqInt inInterpreter); +static void activateNewMethod(void); +static NoDbgRegParms void addNewMethodToCache(sqInt classObj); +extern usqInt argumentCountAddress(void); +static NoDbgRegParms CogMethod * asCogHomeMethod(CogBlockMethod *aCogMethod); +static NoDbgRegParms void assertValidExecutionPointersimbarline(usqInt lip, char *lifp, char *lisp, sqInt inInterpreter, sqInt ln); +static NoDbgRegParms sqInt assertValidStackedInstructionPointersInline(StackPage *aStackPage, sqInt ln); +static NoDbgRegParms void assertValidStackedInstructionPointers(sqInt ln); +static NoDbgRegParms void attemptToSwitchToMachineCode(sqInt bcpc); +static NoDbgRegParms void backupContexttoBlockingSendTo(sqInt suspendedContext, sqInt conditionVariable); +extern void callForCogCompiledCodeCompaction(void); +static NoDbgRegParms void callRegisterArgCogMethodatreceiver(CogMethod *cogMethod, sqInt entryOffset, sqInt rcvr); +extern void ceActivateFailingPrimitiveMethod(sqInt aPrimitiveMethod); +extern sqInt ceBaseFrameReturn(sqInt returnValue); +#if IMMUTABILITY +extern sqInt ceCannotAssignTowithIndexvalueToAssign(sqInt immutableObject, sqInt index, sqInt valueToAssign); +#endif /* IMMUTABILITY */ +extern sqInt ceCannotResume(void); +extern void ceCheckAndMaybeRetryPrimitive(sqInt primIndex); +extern sqInt ceContextinstVar(sqInt maybeContext, sqInt slotIndex); +extern sqInt ceContextinstVarvalue(sqInt maybeMarriedContext, sqInt slotIndex, sqInt anOop); +extern sqInt ceInterpretMethodFromPICreceiver(sqInt aMethodObj, sqInt rcvr); +extern sqInt ceMNUFromPICMNUMethodreceiver(sqInt aMethodObj, sqInt rcvr); +extern sqInt ceNewHashOf(sqInt anObject); +extern sqInt ceNonLocalReturn(sqInt returnValue); +extern void ceReapAndResetErrorCodeFor(CogMethod *cogMethod); +static NoDbgRegParms sqInt ceSendAborttonumArgs(sqInt selector, sqInt rcvr, sqInt numArgs); +extern sqInt ceSendFromInLineCacheMiss(CogMethod *cogMethodOrPIC); +extern sqInt ceSendMustBeBooleanTointerpretingAtDelta(sqInt aNonBooleanObject, sqInt jumpSize); +extern sqInt ceSendMustBeBoolean(sqInt anObject); +extern void ceSendaboveClassBindingtonumArgs(sqInt selector, sqInt methodClassBinding, sqInt rcvr, sqInt numArgs); +extern sqInt ceSendabovetonumArgs(sqInt selector, sqInt methodClass, sqInt rcvr, sqInt numArgs); +extern sqInt ceSendsupertonumArgs(sqInt selector, sqInt superNormalBar, sqInt rcvr, sqInt numArgs); +extern void ceStackOverflow(sqInt contextSwitchIfNotNil); +extern void ceTakeProfileSample(void); +extern void ceTraceBlockActivation(void); +extern void ceTraceLinkedSend(sqInt theReceiver); +extern void ceTraceStoreOfinto(sqInt aValue, sqInt anObject); +extern usqInt cFramePointerAddress(void); +extern void checkAssertsEnabledInCoInterpreter(void); +static NoDbgRegParms sqInt checkCodeIntegrity(sqInt gcModes); +extern sqInt checkForAndFollowForwardedPrimitiveState(void); +extern sqInt checkIfCFramePointerInUse(void); +#if LRPCheck +extern int checkingLongRunningPrimitives(void); +#endif /* LRPCheck */ +static NoDbgRegParms sqInt checkOkayFields(sqInt oop); +static NeverInline void checkProfileTickPostPrimitive(void); +static NoDbgRegParms sqInt cogMethodHasRealNonProfilingPrimitive(CogMethod *cogMethod); +extern sqInt cogMethodHasTooManyLiterals(CogMethod *aCogMethod); +extern CogMethod * cogMethodOf(sqInt aMethodOop); +static void commenceCogCompiledCodeCompaction(void); +extern void compilationBreakpointFor(sqInt selectorOop); +static NoDbgRegParms sqInt contextInstructionPointerframe(sqInt theIP, char *theFP); +static NoDbgRegParms void convertFrametoInterpreterFrame(char *theFP, sqInt pcDelta); +extern usqInt cReturnAddressAddress(void); +extern usqInt cStackPointerAddress(void); +static NoDbgRegParms sqInt divorceAllFramesSuchThat(sqInt (*criterion)(char *fp)); +extern void dumpStackAlignmentInfo(void); +static NoDbgRegParms void ensureAllContextsHaveBytecodePCsIf(sqInt (*criterion)(sqInt methodOop)); +static NoDbgRegParms void ensureContextHasBytecodePC(sqInt aContext); +static NoDbgRegParms void ensureContextIsExecutionSafeAfterAssignToStackPointer(sqInt aContext); +extern void executeCogMethodfromLinkedSendWithReceiver(CogMethod *cogMethod, sqInt rcvr); +static NoDbgRegParms void executeCogMethodfromUnlinkedSendWithReceiver(CogMethod *cogMethod, sqInt rcvr); +extern void executeCogPICfromLinkedSendWithReceiverandCacheTag(CogMethod *cogPIC, sqInt rcvr, sqInt cacheTag); +static sqInt executeNewMethod(void); +static sqInt executeNewMethodJitting(void); +static NoDbgRegParms sqInt externalInstVarofContext(sqInt offset, sqInt aContext); +static NoDbgRegParms void findNewMethodInClassTag(sqInt classTagArg); +static NoDbgRegParms void flagMethodAsInterpreted(sqInt aMethod); +static void flushExternalPrimitives(void); +static NoDbgRegParms void flushMethodsWithMachineCodePrimitivesAndContinueAnswering(sqInt result); +static void followForwardedFieldsInCurrentMethod(void); +static void followForwardingPointersOfReceiverAndTemporariesInStackZone(void); +static void followForwardingPointersOfReceiversInStackZone(void); +static NoDbgRegParms sqInt frameCallerContext(char *theFP); +static NoDbgRegParms int frameHasContext(char *theFP); +static NoDbgRegParms int frameIsBlockActivation(char *theFP); +static NoDbgRegParms sqInt frameMethodObject(char *theFP); +static NoDbgRegParms usqInt frameNumArgs(char *theFP); +extern usqInt framePointerAddress(void); +static NoDbgRegParms void framePrintDescription(sqInt it); +static NoDbgRegParms sqInt frameReceiver(char *theFP); +extern void (*functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(sqInt methodObj, sqInt primitiveIndex, sqInt *flagsPtr))(void); +extern sqInt getCurrentBytecode(void); +static NoDbgRegParms sqInt handleForwardedSendFaultForReceiverstackDelta(sqInt forwardedReceiver, sqInt stackDelta); +static NoDbgRegParms sqInt handleMNUInMachineCodeToclassForMessage(sqInt selectorIndex, sqInt rcvr, sqInt classForMessage); +static NoDbgRegParms int iframeIsBlockActivation(char *theFP); +static NoDbgRegParms sqInt iframeReceiver(char *theFP); +static NoDbgRegParms usqInt iframeSavedIP(char *theFP); +extern void ifValidWriteBackStackPointersSaveTo(void *theCFP, void *theCSP, char **savedFPP, char **savedSPP); +extern usqInt instructionPointerAddress(void); +static NoDbgRegParms sqInt instructionPointerForFramecurrentFPcurrentIP(char *spouseFP, char *currentFP, sqInt instrPtr); +static NeverInline sqInt interpreterAllocationReserveBytes(void); +static sqInt interpretMethodFromMachineCode(void); +static NoDbgRegParms int isCogMethodReferenceNoAssert(sqInt methodHeader); +extern int isCogMethodReference(sqInt methodHeader); +static NoDbgRegParms sqInt isExternalMethodInPlugin(sqInt methodObj); +extern sqInt isFullBlockMethod(sqInt aMethodObj); +static NoDbgRegParms sqInt isMachineCodeFrameForCogMethodFlaggedForBecome(char *theFP); +static NoDbgRegParms sqInt isMachineCodeFrameForCogMethodWithMachineCodePrim(char *theFP); +static NoDbgRegParms sqInt isMachineCodeFrameForCogMethodWithTooManyLiterals(char *theFP); +static NoDbgRegParms sqInt isMachineCodeFrameForExternalPrimitiveMethod(char *theFP); +static NoDbgRegParms sqInt isMachineCodeFrameForRealNonProfilingPrimitive(char *theFP); +static NoDbgRegParms int isMachineCodeFrame(char *theFP); +static NoDbgRegParms sqInt isRealNonProfilingPrimitiveMethod(sqInt methodObj); +static NoDbgRegParms sqInt isRealNonProfilingPrimitiveMethodprimIndex(sqInt methodObj, sqInt primitiveIndex); +static NoDbgRegParms sqInt isWidowedContextDuringGC(sqInt aOnceMarriedContext); +static NoDbgRegParms sqInt isWidowedContext(sqInt aOnceMarriedContext); +static NoDbgRegParms sqInt ismethodAssignmentToContextWithMachineCodePC(sqInt fieldIndex, sqInt anOop); +#if LRPCheck +extern usqInt longRunningPrimitiveStopUsecsAddress(void); +#endif /* LRPCheck */ +extern sqInt lookupMNUreceiver(sqInt selector, sqInt rcvr); +extern sqInt lookupOrdinaryreceiver(sqInt selector, sqInt rcvr); +static NoDbgRegParms StackPage * makeBaseFrameFor(sqInt aContext); +static NeverInline void mapStackPages(void); +extern void markActiveMethodsAndReferents(void); +static NoDbgRegParms void markAndTraceStackPage(StackPage *thePage); +static NoDbgRegParms void markCogMethodsAndReferentsOnPage(StackPage *thePage); +static NoDbgRegParms usqInt marryFrameSP(char *theFP, char *theSP); +extern sqInt maxLookupNoMNUErrorCode(void); +extern usqInt methodCacheAddress(void); +extern int methodHasCogMethod(sqInt aMethodOop); +static NoDbgRegParms sqInt methodHasMachineCodePrimitiveMethod(sqInt methodObj); +static NoDbgRegParms NeverInline sqInt methodHasPrimitiveInPrimTracePlugin(sqInt aMethodObj); +static NoDbgRegParms sqInt methodHasTooManyLiterals(sqInt methodObj); +#if VMInvestigations +extern int methodHeaderHasPrimitive(sqInt methodHeader); +#endif /* VMInvestigations */ +extern int methodNeedsLargeContext(sqInt methodObj); +extern sqInt methodShouldBeCogged(sqInt aMethodObj); +static NoDbgRegParms int methodWithHeaderShouldBeCogged(sqInt methodHeader); +static NoDbgRegParms CogBlockMethod * mframeCogMethod(char *theFP); +extern CogMethod * mframeHomeMethodExport(void); +static NoDbgRegParms CogMethod * mframeHomeMethod(char *theFP); +static NoDbgRegParms int mframeIsBlockActivation(char *theFP); +static NoDbgRegParms sqInt mframeReceiver(char *theFP); +static NeverInline sqInt minimumUnusedHeadroom(void); +static NoDbgRegParms sqInt mnuMethodOrNilFor(sqInt rcvr); +static NoDbgRegParms char * moveFramesInthroughtoPage(StackPage *oldPage, char *theFP, StackPage *newPage); +static NoDbgRegParms sqInt mustMapMachineCodePCcontext(sqInt theIP, sqInt aOnceMarriedContext); +extern usqInt newMethodAddress(void); +extern usqInt nextProfileTickAddress(void); +extern sqInt noAssertHeaderOf(sqInt methodPointer); +extern sqInt positive32BitIntegerFor(unsigned int integerValue); +extern unsigned int positive32BitValueOf(sqInt oop); +extern sqInt positive64BitIntegerFor(usqLong integerValue); +extern usqLong positive64BitValueOf(sqInt oop); +static NoDbgRegParms void postGCAction(sqInt gcModeArg); +static NoDbgRegParms void preGCAction(sqInt gcModeArg); +extern usqInt primFailCodeAddress(void); +static NoDbgRegParms void primitiveBecomeReturn(sqInt ec); +static void primitiveExecuteMethod(void); +static void primitiveExecuteMethodArgsArray(void); +extern sqInt primitivePropertyFlagsprimIndex(sqInt methodObj, sqInt primIndex); +extern void * primTraceLogAddress(void); +extern usqInt primTraceLogIndexAddress(void); +extern void printCogMethod(CogMethod *cogMethod); +static NoDbgRegParms void printFrameFlagsForFP(char *theFP); +static NoDbgRegParms void printFrameMethodFor(char *theFP); +static NoDbgRegParms void printFrameThingatextra(char *name, char *address, sqInt extraValue); +extern sqInt printFrameWithSP(char *theFP, char *theSP); +extern void printMethodCacheFor(sqInt thing); +extern sqInt quickPrimitiveConstantFor(sqInt aQuickPrimitiveIndex); +extern sqInt (*quickPrimitiveGeneratorFor(sqInt aQuickPrimitiveIndex))(void); +extern sqInt quickPrimitiveInstVarIndexFor(sqInt primIndex); +extern sqInt rawHeaderOf(sqInt methodPointer); +extern void rawHeaderOfput(sqInt methodOop, sqInt cogMethodOrMethodHeader); +extern size_t readImageFromFileHeapSizeStartingAt(sqImageFile f, usqInt desiredHeapSize, squeakFileOffsetType imageOffset); +extern sqInt recordFastCCallPrimTraceForMethod(sqInt aMethodObj); +extern sqInt recordPrimTraceForMethod(sqInt aMethodObj); +static NeverInline void reloadPrimitiveCalloutPointer(void); +extern void reportMinimumUnusedHeadroom(void); +extern void reportMinimumUnusedHeadroomOn(FILE *aStdioStream); +static NoDbgRegParms sqInt returnToExecutivepostContextSwitch(sqInt inInterpreter, sqInt switchedContext); +static NoDbgRegParms sqInt returntoExecutive(sqInt returnValue, sqInt inInterpreter); +static NoDbgRegParms sqInt shortPrintFrame(char *theFP); +extern sqInt signed32BitIntegerFor(sqInt integerValue); +extern int signed32BitValueOf(sqInt oop); +extern sqInt signed64BitIntegerFor(sqLong integerValue); +extern sqLong signed64BitValueOf(sqInt oop); +extern sqInt specialSelectorNumArgs(sqInt index); +extern usqInt stackLimitAddress(void); +static sqInt stackLimitOffset(void); +static sqInt stackPageHeadroom(void); +extern usqInt stackPointerAddress(void); +static NoDbgRegParms sqInt stackPointerIndexForFrameWithSP(char *theFP, char *theSP); +extern usqInt startPCOfMethodHeader(sqInt aCompiledMethodHeader); +extern sqInt startPCOrNilOfLiteralin(sqInt lit, sqInt aMethodObj); +static NoDbgRegParms void tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFromtoreturnValue(sqInt contextToReturnFrom, sqInt contextToReturnTo, sqInt returnValue); +static NoDbgRegParms sqInt temporaryin(sqInt offset, char *theFP); +static NoDbgRegParms sqInt temporaryinput(sqInt offset, char *theFP, sqInt valueOop); +extern void updateStackZoneReferencesToCompiledCodePreCompaction(void); +static NoDbgRegParms void updateStateOfSpouseContextForFrameWithSP(char *theFP, char *theSP); +extern sqInt validInstructionPointerinMethodframePointer(usqInt instrPointer, usqInt aMethod, char *fp); +static NoDbgRegParms sqInt validStackPageBaseFrame(StackPage *aPage); +static NoDbgRegParms sqInt voidVMStateForSnapshotFlushingExternalPrimitivesIf(sqInt flushExtPrims); +extern char * whereIs(sqInt anOop); +static NoDbgRegParms NeverInline void widowOrForceToBytecodePC(sqInt ctxt); +extern void ceCheckForInterrupt(void); +static void cedeToHigherPriorityThreads(void); +static NoDbgRegParms sqInt checkForEventsMayContextSwitch(sqInt mayContextSwitch); +static void checkVMOwnershipFromHeartbeat(void); +static NoDbgRegParms sqInt deferStackLimitSmashAroundwith(void (*functionSymbol)(sqInt), sqInt arg); +extern sqInt disownVM(sqInt flags); +extern void dumpTraceLog(void); +static sqInt enterSmalltalkExecutiveImplementation(void); +extern void forceInterruptCheckFromHeartbeat(void); +static void initialEnterSmalltalkExecutive(void); +static NoDbgRegParms sqInt isBoundProcess(sqInt aProcess); +extern void loadInitialContext(void); +static void mapInterpreterOops(void); +static sqInt ownVMFromUnidentifiedThread(void); +extern sqInt ownVM(sqInt disownResult); +static void preemptDisowningThread(void); +extern void primitiveFailForFFIExceptionat(usqLong exceptionCode, usqInt pc); +static void primitiveForceDisplayUpdate(void); +EXPORT(void) primitiveGetOwnerLog(void); +static void primitiveProcessBindToThreadAffinity(void); +EXPORT(void) primitiveProcessBoundThreadId(void); +static void primitiveRelinquishProcessor(void); +static void primitiveShowDisplayRect(void); +EXPORT(void) primitiveVMCurrentThreadId(void); +extern void printAllStacks(void); +static NoDbgRegParms void printLogEntryAt(sqInt i); +extern void printThreads(void); +static NoDbgRegParms void returnToSchedulingLoopAndReleaseVMOrWakeThreadsource(CogVMThread *vmThread, sqInt source); +extern sqInt sendInvokeCallbackContext(VMCallbackContext *vmCallbackContext); +static NoDbgRegParms sqInt threadAffinityOfProcess(sqInt aProcess); +static NoDbgRegParms void threadSchedulingLoopImplementation(CogVMThread *vmThread); +static NoDbgRegParms void threadSwitchIfNecessaryfrom(sqInt newProc, sqInt sourceCode); +static NoDbgRegParms sqInt transferTofrom(sqInt newProcOrNil, sqInt sourceCode); +static NoDbgRegParms void waitingPriorityIsAtLeast(sqInt minPriority); +static NoDbgRegParms int frameIsMarked(sqInt theFPInt); +#if VMInvestigations +EXPORT(int) primitiveAllMethodsCompiledToMachineCode(void); +#endif /* VMInvestigations */ +#if VMInvestigations +EXPORT(void) primitiveBenchmarkFollowForwardersInStackZone(void); +#endif /* VMInvestigations */ +#if VMInvestigations +EXPORT(void) primitiveBenchmarkFollowForwardersOfReceiverAndTemporariesInStackZone(void); +#endif /* VMInvestigations */ +#if VMInvestigations +EXPORT(sqInt) primitiveBenchmarkJITReceiver(void); +#endif /* VMInvestigations */ +#if VMInvestigations +EXPORT(void) primitiveBenchmarkScavenge(void); +#endif /* VMInvestigations */ +static void primitiveClosureCopyWithCopiedValues(void); +#if VMInvestigations +EXPORT(sqInt) primitiveCogitMethod(void); +#endif /* VMInvestigations */ +#if VMInvestigations +EXPORT(sqInt) primitiveCogStaticSymbols(void); +#endif /* VMInvestigations */ +static void primitiveCollectCogCodeConstituents(void); +#if VMInvestigations +EXPORT(void) primitiveContextXray(void); +#endif /* VMInvestigations */ +static void primitiveEnterCriticalSection(void); +static void primitiveExitCriticalSection(void); +EXPORT(void) primitiveExternalCallNoOp(void); +EXPORT(void) primitiveFastCNoOp(void); +EXPORT(void) primitiveFastCNoOpAlignedForFloats(void); +static void primitiveFlushCacheByMethod(void); +static void primitiveFlushCacheBySelector(void); +extern usqInt primitiveFunctionPointerAddress(void); +#if LRPCheck +EXPORT(sqInt) primitiveLongRunningPrimitiveSemaphore(void); +#endif /* LRPCheck */ +#if VMInvestigations +EXPORT(sqInt) primitiveMethodIsCogged(void); +#endif /* VMInvestigations */ +EXPORT(sqInt) primitiveMethodPCData(void); +static void primitiveMethodXray(void); +EXPORT(void) primitiveMinimumUnusedHeadroom(void); +static void primitiveObjectAt(void); +static void primitiveObjectAtPut(void); +EXPORT(sqInt) primitiveProfileSemaphore(void); +static void primitiveResume(void); +static void primitiveSetPriority(void); +static void primitiveSignal(void); +static void primitiveSnapshot(void); +static void primitiveSnapshotEmbedded(void); +static void primitiveSuspend(void); +static void primitiveSuspendBackingUpV1(void); +EXPORT(void) primitiveSuspendBackingUpV2(void); +static void primitiveTerminateTo(void); +static void primitiveUnloadModule(void); +static void primitiveVoidVMState(void); +static void primitiveVoidVMStateForMethod(void); +static void primitiveWait(void); +static void primitiveYield(void); +static void unmarkAllFrames(void); +static NoDbgRegParms void initializeStacknumSlotspageSize(char *theStackPages, sqInt stackSlots, sqInt slotsPerPage); +static NoDbgRegParms sqInt pageIndexFor(void *pointer); +static NoDbgRegParms char * whereIsMaybeStackThing(sqInt anOop); +extern char * cStringOrNullFor(sqInt oop); +extern sqInt failed(void); +extern sqInt identityHashOf(sqInt anOop); +static NoDbgRegParms sqInt isNegativeIntegerValueOf(sqInt oop); +extern sqInt isPositiveMachineIntegerObject(sqInt oop); +static NoDbgRegParms sqInt magnitude64BitIntegerForneg(usqLong magnitude, sqInt isNegative); +static NoDbgRegParms usqLong magnitude64BitValueOf(sqInt oop); +static NoDbgRegParms usqIntptr_t positiveMachineIntegerValueOfObj(sqInt oop); +extern usqIntptr_t positiveMachineIntegerValueOf(sqInt oop); +static void primitiveAdd(void); +EXPORT(void) primitiveAddLargeIntegers(void); +static void primitiveAdoptInstance(void); +EXPORT(void) primitiveAllInstances(void); +EXPORT(void) primitiveAllObjects(void); +static void primitiveArctan(void); +static void primitiveArrayBecome(void); +static void primitiveArrayBecomeOneWay(void); +static void primitiveArrayBecomeOneWayCopyHashArg(void); +static void primitiveArrayBecomeOneWayNoCopyHash(void); +static void primitiveAsCharacter(void); +static void primitiveAsFloat(void); +static void primitiveAt(void); +static void primitiveAtPut(void); +static void primitiveBeCursor(void); +static void primitiveBeDisplay(void); +static void primitiveBeep(void); +static void primitiveBehaviorHash(void); +static void primitiveBitAnd(void); +EXPORT(void) primitiveBitAndLargeIntegers(void); +static void primitiveBitOr(void); +EXPORT(void) primitiveBitOrLargeIntegers(void); +static void primitiveBitShift(void); +EXPORT(void) primitiveBitShiftLargeIntegers(void); +static void primitiveBitXor(void); +EXPORT(void) primitiveBitXorLargeIntegers(void); +EXPORT(sqInt) primitiveBytecodeSetsAvailable(void); +static void primitiveBytesLeft(void); +static void primitiveCalloutToFFI(void); +static void primitiveChangeClass(void); +static void primitiveClass(void); +static void primitiveClearVMProfile(void); +static void primitiveClipboardText(void); +EXPORT(sqInt) primitiveClockLogAddresses(void); +static void primitiveClosureValue(void); +extern void primitiveClosureValueNoContextSwitch(void); +static void primitiveClosureValueWithArgs(void); +static void primitiveCoarseLocalMicrosecondClock(void); +static void primitiveCoarseUTCMicrosecondClock(void); +EXPORT(sqInt) primitiveCompareBytes(void); +EXPORT(void) primitiveCompareWith(void); +static void primitiveConstantFill(void); +static void primitiveControlVMProfiling(void); +static void primitiveCopyObject(void); +static void primitiveCosine(void); +EXPORT(sqInt) primitiveCrashVM(void); +EXPORT(sqInt) primitiveDisablePowerManager(void); +static void primitiveDiv(void); +static void primitiveDivide(void); +EXPORT(void) primitiveDivideLargeIntegers(void); +EXPORT(void) primitiveDivLargeIntegers(void); +static void primitiveEqual(void); +EXPORT(void) primitiveEqualLargeIntegers(void); +static void primitiveExitToDebugger(void); +static void primitiveExp(void); +static void primitiveExponent(void); +extern sqInt primitiveFail(void); +extern sqInt primitiveFailForOSError(sqLong osErrorCode); +extern sqInt primitiveFailFor(sqInt reasonCode); +extern sqInt primitiveFailForwithSecondary(sqInt reasonCode, sqLong extraErrorCode); +extern sqInt primitiveFailureCode(void); +static void primitiveFetchNextMourner(void); +static void primitiveFloatAdd(void); +static void primitiveFloatArrayAt(void); +static void primitiveFloatArrayAtPut(void); +static void primitiveFloatAt(void); +static void primitiveFloatAtPut(void); +static void primitiveFloatDivide(void); +static void primitiveFloatEqual(void); +static void primitiveFloatGreaterOrEqual(void); +static void primitiveFloatGreaterThan(void); +static void primitiveFloatLessOrEqual(void); +static void primitiveFloatLessThan(void); +static void primitiveFloatMultiply(void); +static void primitiveFloatNotEqual(void); +static void primitiveFloatSubtract(void); +static void primitiveFlushCache(void); +static void primitiveFlushExternalPrimitives(void); +static void primitiveFormPrint(void); +static void primitiveFractionalPart(void); +static void primitiveFullClosureValue(void); +extern void primitiveFullClosureValueNoContextSwitch(void); +static void primitiveFullClosureValueWithArgs(void); +static void primitiveGetAttribute(void); +EXPORT(sqInt) primitiveGetenv(void); +#if IMMUTABILITY +static void primitiveGetImmutability(void); +#else +# define primitiveGetImmutability (void (*)(void))0 +#endif /* IMMUTABILITY */ +EXPORT(sqInt) primitiveGetLogDirectory(void); +static void primitiveGetNextEvent(void); +static void primitiveGreaterOrEqual(void); +EXPORT(void) primitiveGreaterOrEqualLargeIntegers(void); +static void primitiveGreaterThan(void); +EXPORT(void) primitiveGreaterThanLargeIntegers(void); +static void primitiveGrowMemoryByAtLeast(void); +static void primitiveHashMultiply(void); +EXPORT(sqInt) primitiveHeartbeatFrequency(void); +static void primitiveHighBit(void); +EXPORT(sqInt) primitiveHighResClock(void); +static void primitiveIdentical(void); +static void primitiveIdentityHash(void); +EXPORT(sqInt) primitiveImageFormatVersion(void); +static void primitiveImageName(void); +static void primitiveImmediateAsInteger(void); +static void primitiveInputSemaphore(void); +static void primitiveInputWord(void); +static void primitiveIntegerAt(void); +static void primitiveIntegerAtPut(void); +EXPORT(sqInt) primitiveInterruptChecksPerMSec(void); +static void primitiveInterruptSemaphore(void); +EXPORT(sqInt) primitiveIsBigEnder(void); +static void primitiveIsPinned(void); +static void primitiveLessOrEqual(void); +EXPORT(void) primitiveLessOrEqualLargeIntegers(void); +static void primitiveLessThan(void); +EXPORT(void) primitiveLessThanLargeIntegers(void); +static void primitiveListBuiltinModule(void); +static void primitiveListExternalModule(void); +static void primitiveLoadImageSegment(void); +static void primitiveLocalMicrosecondClock(void); +static void primitiveLogN(void); +static void primitiveLowSpaceSemaphore(void); +static void primitiveMakePoint(void); +static void primitiveMaxIdentityHash(void); +static void primitiveMillisecondClock(void); +EXPORT(sqInt) primitiveMillisecondClockMask(void); +static void primitiveMod(void); +EXPORT(void) primitiveModLargeIntegers(void); +EXPORT(sqInt) primitiveMultipleBytecodeSetsActive(void); +static void primitiveMultiply(void); +EXPORT(void) primitiveMultiplyLargeIntegers(void); +static void primitiveNew(void); +static void primitiveNewMethod(void); +static void primitiveNewWithArg(void); +static void primitiveNextInstance(void); +static void primitiveNextObject(void); +static void primitiveNoop(void); +static void primitiveNotEqual(void); +EXPORT(void) primitiveNotEqualLargeIntegers(void); +static void primitiveNotIdentical(void); +static void primitiveObjectsAccessibleFromRoots(void); +EXPORT(sqInt) primitivePathToUsing(void); +static void primitivePerformInSuperclass(void); +static void primitivePerformWithArgs(void); +static void primitivePinnedNew(void); +static void primitivePinnedNewWithArg(void); +EXPORT(sqInt) primitiveProfilePrimitive(void); +EXPORT(sqInt) primitiveProfileSample(void); +EXPORT(sqInt) primitiveProfileStart(void); +static void primitiveQuit(void); +static void primitiveQuo(void); +EXPORT(void) primitiveQuoLargeIntegers(void); +EXPORT(void) primitiveRemLargeIntegers(void); +static void primitiveScanCharacters(void); +EXPORT(sqInt) primitiveScreenDepth(void); +EXPORT(sqInt) primitiveScreenScaleFactor(void); +static void primitiveScreenSize(void); +static void primitiveSecondsClock(void); +static void primitiveSetDisplayMode(void); +static void primitiveSetFullScreen(void); +#if IMMUTABILITY +static void primitiveSetImmutability(void); +#else +# define primitiveSetImmutability (void (*)(void))0 +#endif /* IMMUTABILITY */ +static void primitiveSetInterruptKey(void); +EXPORT(sqInt) primitiveSetLogDirectory(void); +static void primitiveSetOrHasIdentityHash(void); +static void primitiveShortAt(void); +static void primitiveShortAtPut(void); +static void primitiveSignalAtBytesLeft(void); +static void primitiveSine(void); +static void primitiveSize(void); +static void primitiveSizeInBytes(void); +static void primitiveSizeInBytesOfInstance(void); +static void primitiveSmallFloatAdd(void); +static void primitiveSmallFloatArctan(void); +static void primitiveSmallFloatCosine(void); +static void primitiveSmallFloatDivide(void); +static void primitiveSmallFloatEqual(void); +static void primitiveSmallFloatExp(void); +static void primitiveSmallFloatExponent(void); +static void primitiveSmallFloatFractionalPart(void); +static void primitiveSmallFloatGreaterOrEqual(void); +static void primitiveSmallFloatGreaterThan(void); +static void primitiveSmallFloatLessOrEqual(void); +static void primitiveSmallFloatLessThan(void); +static void primitiveSmallFloatLogN(void); +static void primitiveSmallFloatMultiply(void); +static void primitiveSmallFloatNotEqual(void); +static void primitiveSmallFloatSine(void); +static void primitiveSmallFloatSquareRoot(void); +static void primitiveSmallFloatSubtract(void); +static void primitiveSmallFloatTimesTwoPower(void); +static void primitiveSmallFloatTruncated(void); +static void primitiveSomeInstance(void); +static void primitiveSomeObject(void); +static void primitiveSpecialObjectsOop(void); +static void primitiveSquareRoot(void); +static void primitiveStoreImageSegment(void); +static void primitiveStringAt(void); +static void primitiveStringAtPut(void); +static void primitiveStringReplace(void); +static void primitiveSubtract(void); +EXPORT(void) primitiveSubtractLargeIntegers(void); +static void primitiveTestAndSetOwnershipOfCriticalSection(void); +static void primitiveTestDisplayDepth(void); +#if TestingPrimitives +EXPORT(sqInt) primitiveTestShortenIndexableSize(void); +#endif /* TestingPrimitives */ +static void primitiveTimesTwoPower(void); +static void primitiveTruncated(void); +static void primitiveUninitializedNewWithArg(void); +static void primitiveUninitializedPinnedNewWithArg(void); +static void primitiveUpdateTimezone(void); +static void primitiveUTCMicrosecondClock(void); +static void primitiveUtcAndTimezoneOffset(void); +EXPORT(sqInt) primitiveUtcWithOffset(void); +static void primitiveVMPath(void); +static void primitiveVMProfileSamplesInto(void); +extern int signalNoResume(sqInt aSemaphore); +static NoDbgRegParms sqIntptr_t signedMachineIntegerValueOfObj(sqInt oop); +extern sqIntptr_t signedMachineIntegerValueOf(sqInt oop); +extern usqInt sizeOfAlienData(sqInt oop); +extern void * startOfAlienData(sqInt oop); +static int successful(void); +extern sqInt success(sqInt successBoolean); +static NoDbgRegParms sqInt becomeEffectFlagsFor(sqInt objOop); +extern void ceScheduleScavenge(void); +extern sqInt classSmallFloat(void); +static void clearLeakMapAndMapAccessibleObjects(void); +extern void ensureNoForwardedLiteralsIn(sqInt aMethodObj); +static NoDbgRegParms sqInt followForwardedObjectFieldstoDepth(sqInt objOop, sqInt depth); +extern usqInt freeStartAddress(void); +extern usqInt getScavengeThreshold(void); +static NoDbgRegParms sqLong headerWhileForwardingOf(sqInt aCompiledMethodObjOop); +extern sqInt isBytes(sqInt oop); +extern sqInt isForwardedClassIndex(sqInt maybeClassIndex); +extern int isImmediateClass(sqInt classObj); +extern sqInt isReallyYoungObject(sqInt objOop); +extern sqInt methodHeaderOf(sqInt methodObj); +extern usqInt needGCFlagAddress(void); +static NoDbgRegParms void printCantBeObjecton(sqInt oop, FILE *aStream); +extern usqInt specialObjectsOopAddress(void); +extern sqInt withoutForwardingOnandwithsendToCogit(sqInt obj1, sqInt obj2, sqInt aBool, sqInt (*selector)(sqInt,sqInt,sqInt)); +static NoDbgRegParms usqInt addressAfter(sqInt objOop); +static NoDbgRegParms sqInt allocateSlotsForPinningInOldSpacebytesformatclassIndex(sqInt numSlots, usqInt totalBytes, sqInt formatField, sqInt classIndex); +extern sqInt byteSwapped(sqInt w); +static NoDbgRegParms usqInt bytesInBody(sqInt objOop); +static int defaultEdenBytes(void); +extern sqInt fetchClassTagOf(sqInt oop); +extern sqInt floatObjectOf(double aFloat); +static NoDbgRegParms sqLong floatValueBitsOf(sqInt floatOop); +extern double floatValueOf(sqInt oop); +static NoDbgRegParms void hackSlimBridgeToat(sqInt objOop, sqInt startAddress); +extern int headerIndicatesAlternateBytecodeSet(sqInt methodHeader); +static NoDbgRegParms sqInt initFreeChunkWithBytesat(usqLong numBytes, sqInt address); +static NoDbgRegParms void initSegmentBridgeWithBytesat(usqLong numBytes, sqInt address); +extern sqInt instantiateClassindexableSize(sqInt classObj, usqInt nElements); +extern sqInt integerObjectOf(sqInt value); +extern sqInt integerValueOf(sqInt oop); +static NoDbgRegParms int isFloatInstance(sqInt oop); +static NoDbgRegParms int isImmediateFloat(sqInt oop); +extern int isIntegerObject(sqInt oop); +extern int isIntegerValue(sqInt intValue); +static NoDbgRegParms int isLilliputianSize(sqInt chunkBytes); +static NoDbgRegParms int isSmallFloatValueBits(usqLong rawFloatBits); +static NoDbgRegParms int isSmallFloatValue(double aFloat); +static NoDbgRegParms int isWordsNonImm(sqInt objOop); +static NoDbgRegParms sqInt lengthOfformat(sqInt objOop, sqInt fmt); +static NoDbgRegParms double loadFloatOrIntFrom(sqInt floatOrIntOop); +static sqInt maxSlotsForAlloc(void); +extern double noFailFloatValueOf(sqInt aFloatOop); +static sqInt numFreeLists(void); +static NoDbgRegParms sqInt objectAfterMaybeSlimBridgelimit(sqInt objOop, sqInt limit); +static NoDbgRegParms double smallFloatValueOf(sqInt oop); +extern usqInt smallObjectBytesForSlots(sqInt numSlots); +static sqInt wordIndexableFormat(void); +static sqInt wordSizeClassIndexPun(void); +static NoDbgRegParms NeverInline void addToEphemeronList(sqInt ephemeronCorpse); +static NoDbgRegParms NeverInline void addToWeakList(sqInt weakCorpse); +static sqInt allNewSpaceObjectsHaveZeroRTRefCount(void); +static sqInt allWeakSurvivorsOnWeakList(void); +static NeverInline void computeRefCountToShrinkRT(void); +static NoDbgRegParms sqInt copyAndForwardMourner(sqInt mourner); +static NoDbgRegParms sqInt copyAndForward(sqInt survivor); +static NoDbgRegParms NeverInline sqInt copyToOldSpacebytesformat(sqInt survivor, sqInt bytesInObject, sqInt formatOfSurvivor); +extern usqInt corpseForCorpseOffset(sqInt corpseOffset); +static NoDbgRegParms usqInt firstCorpse(sqInt headOfCorpseList); +static void growRememberedSet(void); +static NoDbgRegParms sqInt isInRememberedSet(sqInt objOop); +static NoDbgRegParms sqInt isMaybeOldScavengeSurvivor(sqInt oop); +static NoDbgRegParms sqInt isScavengeSurvivor(sqInt oop); +static NoDbgRegParms sqInt isYetToBeScavenged(sqInt oop); +static usqInt newSpaceCapacity(void); +static NoDbgRegParms usqInt nextCorpseOrNil(sqInt corpse); +extern void openScavengeLog(void); +extern void printEphemeronList(void); +extern void printRememberedSet(void); +static sqInt processEphemerons(void); +static void processWeaklings(void); +static NoDbgRegParms sqInt processWeakSurvivor(sqInt weakObj); +extern sqInt remember(sqInt objOop); +static NoDbgRegParms void scavengeFutureSurvivorSpaceStartingAt(sqInt initialAddress); +static sqInt scavengeInactiveEphemeronsInRememberedSet(void); +static sqInt scavengeInactiveEphemeronsOnEphemeronList(void); +static sqInt scavengeLoop(void); +static NoDbgRegParms sqInt scavengeReferentsOfPointers(sqInt referrer); +static NoDbgRegParms sqInt scavengeReferentsOf(sqInt referrer); +static NoDbgRegParms void scavengeRememberedSetStartingAt(sqInt n); +static float scavengerTenuringThreshold(void); +static sqInt unfiredEphemeronsAtEndOfRememberedSet(void); +static sqInt validRememberedSetPartitionPostSIEIRS(void); +static sqInt wholeEphemeronListIsFirable(void); +static NeverInline void writeScavengeLog(void); +static NoDbgRegParms sqInt accessibleObjectAfter(sqInt objOop); +static NoDbgRegParms NeverInline sqInt activeAndDeferredScan(sqInt anEphemeron); +static NoDbgRegParms void addFreeSubTree(sqInt freeTree); +extern sqInt addGCRoot(sqInt *varLoc); +static NoDbgRegParms sqInt addressCouldBeClassObj(sqInt maybeClassObj); +extern sqInt addressCouldBeObj(sqInt address); +extern sqInt addressCouldBeOop(sqInt address); +static NoDbgRegParms sqInt addToFreeListbytes(sqInt freeChunk, sqInt chunkBytes); +static NoDbgRegParms sqInt allInstancesOf(sqInt aClass); +static sqInt allObjects(void); +static sqInt allObjectsUnmarked(void); +static sqInt allOldMarkedWeakObjectsOnWeaklingStack(void); +static sqInt allocateLargestFreeChunk(void); +static NoDbgRegParms sqInt allocateOldSpaceChunkOfBytes(usqInt chunkBytes); +extern sqInt allocatePinnedSlots(sqInt nSlots); +static NoDbgRegParms sqInt allocateSlotsInOldSpacebytesformatclassIndex(sqInt numSlots, usqInt totalBytes, sqInt formatField, sqInt classIndex); +static sqInt allocationUnit(void); +static NoDbgRegParms sqInt allStrongSlotsOfWeaklingAreMarked(sqInt aWeakling); +static sqInt allUnscannedEphemeronsAreActive(void); +static NoDbgRegParms NeverInline sqInt assertInnerValidFreeObject(sqInt objOop); +extern sqInt becomewith(sqInt array1, sqInt array2); +static NoDbgRegParms sqInt becomewithtwoWaycopyHash(sqInt array1, sqInt array2, sqInt twoWayFlag, sqInt copyHashFlag); +extern void beRootIfOld(sqInt oop); +static sqInt bitsSetInFreeSpaceMaskForAllFreeLists(void); +static sqInt bridgeSize(void); +static NoDbgRegParms sqInt byteFormatForNumBytes(sqInt numBytes); +static sqInt byteFormatMask(void); +extern sqInt byteSizeOf(sqInt oop); +extern unsigned char bytesPerElement(sqInt oop); +static NoDbgRegParms sqInt changeClassOfto(sqInt rcvr, sqInt argClass); +extern sqInt characterObjectOf(sqInt characterCode); +extern usqInt characterValueOf(sqInt oop); +static NoDbgRegParms sqInt cheapAddressCouldBeInHeap(sqInt address); +extern sqInt checkedLongAt(sqInt byteAddress); +static sqInt checkHeapFreeSpaceIntegrity(void); +static NoDbgRegParms sqInt checkHeapIntegrityclassIndicesShouldBeValid(sqInt excludeUnmarkedObjs, sqInt classIndicesShouldBeValid); +extern sqInt checkOkayOop(usqInt oop); +extern sqInt checkOopHasOkayClass(usqInt obj); +static NoDbgRegParms sqInt checkOopIntegritynamed(sqInt obj, char *name); +static NoDbgRegParms sqInt checkOopIntegritynamedindex(sqInt obj, char *name, sqInt i); +extern sqInt classAlien(void); +extern sqInt classArray(void); +extern sqInt classAtIndex(sqInt classIndex); +static NoDbgRegParms sqInt classAtIndexput(sqInt classIndex, sqInt objOop); +extern sqInt classBitmap(void); +extern sqInt classByteArray(void); +extern sqInt classCharacter(void); +extern sqInt classDoubleByteArray(void); +extern sqInt classDoubleWordArray(void); +extern sqInt classExternalAddress(void); +extern sqInt classExternalData(void); +extern sqInt classExternalFunction(void); +extern sqInt classExternalLibrary(void); +extern sqInt classExternalStructure(void); +extern sqInt classFloat(void); +extern sqInt classFloat32Array(void); +extern sqInt classFloat64Array(void); +static NoDbgRegParms sqInt classForClassTag(sqInt classIndex); +static NoDbgRegParms sqInt classFormatFromInstFormat(sqInt instFormat); +static sqInt classIndexFieldWidth(void); +extern sqInt classIndexOf(sqInt objOop); +extern sqInt classLargeNegativeInteger(void); +extern sqInt classLargePositiveInteger(void); +extern sqInt classOrNilAtIndex(sqInt classIndex); +extern sqInt classPoint(void); +extern sqInt classSemaphore(void); +extern sqInt classSmallInteger(void); +extern sqInt classString(void); +extern sqInt classTableMinorIndexMask(void); +extern sqInt classTablePageSize(void); +extern sqInt classTableRootObj(void); +static sqInt classTableRootSlots(void); +extern sqInt classTagForClass(sqInt classObj); +extern sqInt classUnsafeAlien(void); +extern sqInt classWordArray(void); +static void clearLeakMapAndMapAccessibleFreeSpace(void); +static NoDbgRegParms sqInt cloneInOldSpaceforPinning(sqInt objOop, sqInt forPinning); +extern sqInt cloneObject(sqInt objOop); +static NoDbgRegParms sqInt copyObjtoAddrstopAtsavedFirstFieldsindex(sqInt objOop, sqInt segAddr, sqInt endSeg, sqInt savedFirstFields, sqInt i); +extern void countMarkedAndUnmarkdObjects(sqInt printFlags); +static usqLong currentAllocatedBytes(void); +static NoDbgRegParms void doScavenge(sqInt tenuringCriterion); +static NoDbgRegParms sqInt doShortentoIndexableSize(sqInt objOop, sqInt indexableSize); +extern usqInt eeInstantiateClassIndexformatnumSlots(sqInt knownClassIndex, sqInt objFormat, sqInt numSlots); +extern NeverInline void eek(void); +static NoDbgRegParms void enableObjectEnumerationFrom(sqInt initialObject); +static NoDbgRegParms sqInt ensureBehaviorHash(sqInt aBehavior); +static NoDbgRegParms sqInt ensureRoomOnObjStackAt(sqInt objStackRootIndex); +static NoDbgRegParms sqInt enterIntoClassTable(sqInt aBehavior); +static NoDbgRegParms sqInt existInstancesInNewSpaceOf(sqInt classObj); +static NoDbgRegParms void expungeFromClassTable(sqInt aBehavior); +extern sqInt falseObject(void); +extern sqInt fetchByteofObject(sqInt byteIndex, sqInt objOop); +static NoDbgRegParms NeverInline sqInt fetchClassOfNonImm(sqInt objOop); +extern sqInt fetchClassOf(sqInt oop); +static NoDbgRegParms sqInt fetchClassTagOfNonImm(sqInt obj); +extern sqLong fetchIntegerofObject(sqInt fieldIndex, sqInt objOop); +extern int fetchLong32ofObject(sqInt fieldIndex, sqInt oop); +static NoDbgRegParms sqInt fetchPointerofFreeChunk(sqInt fieldIndex, sqInt objOop); +extern sqInt fetchPointerofObject(sqInt fieldIndex, sqInt objOop); +extern sqInt findClassNamed(char *aString); +static sqInt findLargestFreeChunk(void); +extern void findStringBeginningWith(char *aCString); +extern void findString(char *aCString); +static NoDbgRegParms void fireEphemeron(sqInt ephemeron); +static sqInt firstAccessibleObject(void); +static NoDbgRegParms void * firstFixedFieldOfMaybeImmediate(sqInt oop); +extern void * firstFixedField(sqInt objOop); +extern void * firstIndexableField(sqInt objOop); +extern sqInt fixedFieldsOfClassFormatMask(void); +extern sqInt fixedFieldsOfClassFormat(sqInt classFormat); +static NoDbgRegParms sqInt fixedFieldsOfformatlength(sqInt objOop, sqInt fmt, sqInt wordLength); +static NoDbgRegParms NeverInline sqInt fixFollowedFieldofObjectwithInitialValue(sqInt fieldIndex, sqInt anObject, sqInt initialValue); +static NoDbgRegParms sqInt followedKeyOfEphemeron(sqInt objOop); +static NoDbgRegParms sqInt followFieldofObject(sqInt fieldIndex, sqInt anObject); +extern sqInt followForwarded(sqInt objOop); +static NoDbgRegParms sqInt followMaybeForwarded(sqInt objOop); +static NoDbgRegParms sqInt followObjFieldofObject(sqInt fieldIndex, sqInt anObject); +extern sqInt formatOfClass(sqInt classPointer); +static NoDbgRegParms sqInt formatOf(sqInt objOop); +static NoDbgRegParms sqInt forwardersIn(sqInt anObject); +static sqInt freeChunkNextIndex(void); +static sqInt freeChunkParentIndex(void); +static sqInt freeChunkPrevIndex(void); +static NoDbgRegParms sqInt freeChunkWithBytesat(sqInt bytes, sqInt address); +extern sqInt freeObject(sqInt objOop); +extern NeverInline usqLong fullGC(void); +static float getHeapGrowthToSizeGCRatio(void); +static NoDbgRegParms sqInt goodContextSize(sqInt oop); +static NoDbgRegParms usqInt growOldSpaceByAtLeast(sqInt minAmmount); +static NoDbgRegParms sqInt hasPointerFieldsNonImm(sqInt oop); +extern usqLong headerForSlotsformatclassIndex(sqInt numSlots, sqInt formatField, sqInt classIndex); +static NoDbgRegParms usqLong headerForSlotshashformatclassIndex(sqInt numSlots, sqInt hash, sqInt formatField, sqInt classIndex); +static sqInt hiddenRootSlots(void); +static sqInt hiddenRootsObject(void); +static sqInt imageSegmentVersion(void); +#if IMMUTABILITY +extern sqInt immutableBitMask(void); +#endif /* IMMUTABILITY */ +extern sqInt indexOfin(sqInt anElement, sqInt anObject); +static NoDbgRegParms void inFreeTreeReplacewith(sqInt treeNode, sqInt newNode); +static NoDbgRegParms sqInt initialInstanceOf(sqInt classObj); +static NeverInline void initializeNewSpaceVariables(void); +static NoDbgRegParms void initializeObjectMemory(sqInt bytesToShift); +static void initializeUnscannedEphemerons(void); +extern void inOrderPrintFreeTreeprintList(sqInt freeChunk, sqInt printNextList); +static NoDbgRegParms NeverInline void inPlaceBecomeandcopyHashFlag(sqInt obj1, sqInt obj2, sqInt copyHashFlag); +extern sqInt instanceSizeOf(sqInt classObj); +static NoDbgRegParms sqInt instantiateClass(sqInt classObj); +extern sqInt instSpecOfClassFormat(sqInt classFormat); +static NoDbgRegParms sqInt instSpecOfClass(sqInt classPointer); +static NoDbgRegParms void invalidCompactClassError(const char *className); +extern sqInt isArray(sqInt oop); +static NoDbgRegParms int isBridgeOrEnumerableObjectNoAssert(sqInt objOop); +extern int isCharacterObject(sqInt oop); +extern int isCharacterValue(sqInt anInteger); +static NoDbgRegParms int isCompiledMethodFormat(sqInt format); +static NoDbgRegParms int isCompiledMethodHeader(sqInt objHeader); +extern int isCompiledMethod(sqInt objOop); +static NoDbgRegParms int isContextHeader(sqInt aHeader); +static NoDbgRegParms int isContextNonImm(sqInt oop); +static NoDbgRegParms sqInt isContext(sqInt oop); +static NoDbgRegParms int isCopiedIntoSegment(sqInt anObjectInTheHeap); +static NoDbgRegParms sqInt isEmptyObjStack(sqInt objStack); +static NoDbgRegParms sqInt isEnumerableObjectNoAssert(sqInt objOop); +static NoDbgRegParms int isEphemeronFormat(sqInt format); +static NoDbgRegParms int isEphemeron(sqInt objOop); +extern sqInt isFixedSizePointerFormat(sqInt format); +static NoDbgRegParms int isForwardedClassTag(sqInt classIndex); +extern int isForwarded(sqInt objOop); +static NoDbgRegParms int isFreeObject(sqInt objOop); +static NoDbgRegParms sqInt isFreeOop(sqInt oop); +extern int isImmediate(sqInt oop); +static NoDbgRegParms sqInt isInClassTable(sqInt objOop); +extern sqInt isIndexable(sqInt objOop); +static NoDbgRegParms sqInt isInEden(sqInt objOop); +static NoDbgRegParms sqInt isInFutureSpace(sqInt address); +extern sqInt isInMemory(sqInt address); +static NoDbgRegParms sqInt isInNewSpace(sqInt objOop); +extern sqInt isInOldSpace(sqInt address); +static NoDbgRegParms sqInt isInPastSpace(sqInt address); +static NoDbgRegParms int isLargeFreeObject(sqInt objOop); +extern sqInt isLong64s(sqInt oop); +extern int isMarked(sqInt objOop); +static NoDbgRegParms sqInt isMaybeFiredEphemeron(sqInt objOop); +extern int isNonImmediate(sqInt oop); +static NoDbgRegParms int isObjEphemeron(sqInt objOop); +static NoDbgRegParms sqInt isObjImmutable(sqInt anOop); +static NoDbgRegParms int isOldObject(sqInt objOop); +extern sqInt isOopCompiledMethod(sqInt oop); +extern sqInt isOopForwarded(sqInt oop); +extern sqInt isOopImmutable(sqInt oop); +extern sqInt isOopMutable(sqInt oop); +extern int isPinned(sqInt objOop); +extern sqInt isPointers(sqInt oop); +static NoDbgRegParms sqInt isPureBitsFormat(sqInt format); +static NoDbgRegParms sqInt isPureBitsNonImm(sqInt objOop); +static NoDbgRegParms int isRemembered(sqInt objOop); +static NoDbgRegParms int isSegmentBridge(sqInt objOop); +static NoDbgRegParms sqInt isSemaphoreOop(sqInt anOop); +extern sqInt isShorts(sqInt oop); +static NoDbgRegParms int isUnambiguouslyForwarder(sqInt objOop); +extern int isUnmarked(sqInt objOop); +extern sqInt isValidClassTag(sqInt classIndex); +static NoDbgRegParms sqInt isValidObjStackAt(sqInt objStackRootIndex); +static NoDbgRegParms sqInt isValidObjStackPagemyIndex(sqInt objStackPage, sqInt myx); +static NoDbgRegParms sqInt isValidObjStackPagemyIndexfirstPage(sqInt objStackPage, sqInt myx, sqInt isFirstPage); +static NoDbgRegParms sqInt isValidObjStack(sqInt objStack); +static NoDbgRegParms int isWeakNonImm(sqInt objOop); +extern sqInt isWeak(sqInt oop); +static NoDbgRegParms sqInt isWordsOrBytesNonImm(sqInt objOop); +extern sqInt isWordsOrBytes(sqInt oop); +extern sqInt isWordsOrShorts(sqInt oop); +extern sqInt isWords(sqInt oop); +extern int isYoungObject(sqInt objOop); +extern sqInt isYoung(sqInt oop); +static NoDbgRegParms sqInt isinstanceOfcompactClassIndex(sqInt oop, sqInt classOop, sqInt compactClassIndex); +extern sqInt isonObjStack(sqInt oop, sqInt objStack); +static NoDbgRegParms sqInt keyOfEphemeron(sqInt objOop); +static NoDbgRegParms sqInt keyOfMaybeFiredEphemeron(sqInt objOop); +static NoDbgRegParms sqInt knownClassAtIndex(sqInt classIndex); +static sqInt lastPointerFormat(void); +static NoDbgRegParms sqInt lastPointerOfWhileSwizzling(sqInt objOop); +static NoDbgRegParms sqInt lastPointerOf(sqInt objOop); +extern int leakCheckFullGC(void); +extern int leakCheckNewSpaceGC(void); +static NoDbgRegParms sqInt lengthOfMaybeImmediate(sqInt oop); +static NoDbgRegParms sqInt lengthOf(sqInt objOop); +extern usqInt literalCountOfMethodHeader(sqInt header); +extern usqInt literalCountOf(sqInt methodPointer); +static NoDbgRegParms NeverInline sqInt loadImageSegmentFromoutPointers(sqInt segmentWordArray, sqInt outPointerArray); +extern void longPrintInstancesOf(sqInt aClassOop); +extern void longPrintInstancesWithClassIndex(sqInt classIndex); +extern void longPrintReferencesTo(sqInt anOop); +static void mapExtraRoots(void); +static NeverInline void mapMournQueue(void); +static NoDbgRegParms sqInt mapOopsFromtooutPointersoutHashes(sqInt segStart, sqInt segAddr, sqInt outPointerArray, sqInt savedOutHashes); +static NoDbgRegParms void markAndTraceClassOf(sqInt objOop); +static NoDbgRegParms void markAndTraceObjStackandContents(sqInt stackOrNil, sqInt markAndTraceContents); +extern NeverInline void markAndTrace(sqInt objOop); +static sqInt markInactiveEphemerons(void); +static NoDbgRegParms NeverInline void markObjects(sqInt objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged); +static void markWeaklingsAndMarkAndFireEphemerons(void); +extern sqInt maxIdentityHash(void); +extern sqInt maxSlotsForNewSpaceAlloc(void); +extern sqInt maybeMethodClassOfseemsToBeInstantiating(sqInt methodObj, sqInt format); +extern sqInt minSlotsForShortening(void); +static NoDbgRegParms usqInt newHashBitsOf(sqInt objOop); +static sqInt newSpaceIsEmpty(void); +extern sqInt nilObject(void); +static NeverInline void nilUnmarkedWeaklingSlots(void); +static NoDbgRegParms sqInt noCheckClassAtIndex(sqInt classIndex); +static NoDbgRegParms sqInt noCheckPushonObjStack(sqInt objOop, sqInt objStack); +static NoDbgRegParms sqInt noFixupFollowFieldofObject(sqInt fieldIndex, sqInt anObject); +static NoDbgRegParms NeverInline sqInt noInlineAllocateSlotsformatclassIndex(sqInt numSlots, sqInt formatField, sqInt classIndex); +static int noUnscannedEphemerons(void); +static NoDbgRegParms sqInt numBytesOfBitsformat(sqInt objOop, sqInt format); +static NoDbgRegParms sqInt numBytesOfBytes(sqInt objOop); +extern sqInt numBytesOf(sqInt objOop); +static NoDbgRegParms usqInt numPointerSlotsOf(sqInt objOop); +static NoDbgRegParms sqInt numSlotsForBytes(sqInt numBytes); +static NoDbgRegParms usqInt numSlotsOfAny(sqInt objOop); +static NoDbgRegParms usqInt numSlotsOfIndexablePointerObj(sqInt objOop); +extern usqInt numSlotsOf(sqInt objOop); +static NoDbgRegParms sqInt numStrongSlotsOfInephemeral(sqInt objOop); +static NoDbgRegParms sqInt numStrongSlotsOfWeakling(sqInt objOop); +static sqInt numUnscannedEphemerons(void); +extern sqInt objCouldBeClassObj(sqInt objOop); +extern sqInt objectAfter(sqInt objOop); +extern sqInt objectBefore(sqInt objOop); +static NoDbgRegParms sqInt objectInPastSpaceBefore(sqInt objOop); +static NoDbgRegParms sqInt objectStartingAt(sqInt address); +static NoDbgRegParms sqInt objectsAccessibleFromRoots(sqInt arrayOfRootsArg); +static NoDbgRegParms NeverInline sqInt objectsReachableFromRoots(sqInt arrayOfRoots); +static NoDbgRegParms sqInt okayOop(sqInt signedOop); +extern sqInt oldSpaceObjectAfter(sqInt objOop); +static NoDbgRegParms NeverInline void outOfPlaceBecomeandcopyHashFlag(sqInt obj1, sqInt obj2, sqInt copyHashFlag); +extern sqInt pinObject(sqInt objOop); +static NoDbgRegParms sqInt popObjStack(sqInt objStack); +extern sqInt popRemappableOop(void); +static NoDbgRegParms void postBecomeScanClassTable(sqInt effectsFlags); +extern sqInt primitiveErrorTable(void); +extern void printActivationsOf(sqInt aMethodObj); +extern void printBogons(void); +extern void printContextReferencesTo(sqInt anOop); +extern void printEntity(sqInt oop); +extern void printEphemerons(void); +extern void printForwarders(void); +static NoDbgRegParms void printForwarderon(sqInt oop, FILE *aStream); +extern void printFreeChunks(void); +extern void printFreeChunk(sqInt freeChunk); +static NoDbgRegParms void printFreeChunkprintAsTreeNode(sqInt freeChunk, sqInt printAsTreeNode); +extern void printFreeListHeads(void); +extern sqInt printFreeList(sqInt chunkOrIndex); +static NoDbgRegParms void printFreeObjecton(sqInt oop, FILE *aStream); +extern void printFreeTree(void); +static NoDbgRegParms void printFreeTreeChunk(sqInt chunkOrZero); +extern void printHeaderOf(sqInt objOop); +static NoDbgRegParms void printImmediateObjecton(sqInt oop, FILE *aStream); +extern void printInstancesOf(sqInt aClassOop); +extern void printInstancesWithClassIndex(sqInt classIndex); +extern void printInvalidClassTableEntries(void); +#if LLDB +extern void printMarkedOops(void); +#endif /* LLDB */ +extern void printMethodImplementorsOf(sqInt anOop); +extern void printMethodReferencesTo(sqInt anOop); +extern void printMournQueue(void); +extern void printNewObjectsInObjStack(sqInt objStack); +static NoDbgRegParms void printNonPointerDataOfon(sqInt oop, FILE *aStream); +extern void printObjectsFromto(sqInt startAddress, sqInt endAddress); +extern void printObjectsWithHash(sqInt hash); +extern void printObjStackAndContents(sqInt objStack); +static NoDbgRegParms sqInt printObjStackPagemyIndexpageTypeprintContents(sqInt objStackPage, sqInt myx, sqInt pageType, sqInt printContents); +static NoDbgRegParms void printObjStackPagemyIndextag(sqInt objStackPage, sqInt myx, char *pageType); +extern void printObjStack(sqInt objStack); +extern NeverInline void printOopsExcept(sqInt (*function)(sqInt)); +extern void printOopsFromto(sqInt startAddress, sqInt endAddress); +extern NeverInline void printOopsSuchThat(sqInt (*function)(sqInt)); +extern void printReferencesTo(sqInt anOop); +static NoDbgRegParms void printStringDataOfon(sqInt oop, FILE *aStream); +#if LLDB +extern void printUnmarkedOops(void); +#endif /* LLDB */ +extern void printWeaklings(void); +static NoDbgRegParms sqInt pushOnUnscannedEphemeronsStack(sqInt anEphemeron); +extern void pushRemappableOop(sqInt oop); +static NoDbgRegParms void queueMourner(sqInt anEphemeronOrWeakArray); +extern int rawHashBitsOf(sqInt objOop); +extern sqInt receiverTagBitsForMethod(sqInt aMethodObj); +static void refireQueuedEphemeronsPostSnapshot(void); +static NoDbgRegParms sqInt relocateObjStackForPlanningCompactorandContents(sqInt objStack, sqInt relocateContents); +extern sqInt removeGCRoot(sqInt *varLoc); +static NoDbgRegParms sqInt returnrestoringObjectsInsavedFirstFieldsandsavedHashes(sqInt errCode, sqInt reachableObjectsArray, sqInt savedFirstFields, sqInt outPointersArray, sqInt savedHashes); +static NoDbgRegParms sqInt returnrestoringObjectsInupTosavedFirstFields(sqInt errCode, sqInt reachableObjectsArray, sqInt limitOrTag, sqInt savedFirstFields); +static NoDbgRegParms NeverInline void reverseBytesIn32BitWordsIn(sqInt segmentWordArray); +static NoDbgRegParms void runLeakCheckerForFreeSpaceignoring(sqInt gcModes, sqInt anOopOrNil); +static NoDbgRegParms void runLeakCheckerFor(sqInt gcModes); +static NoDbgRegParms void runLeakCheckerForexcludeUnmarkedObjsclassIndicesShouldBeValid(sqInt gcModes, sqInt excludeUnmarkedObjs, sqInt classIndicesShouldBeValid); +static NoDbgRegParms void safePrintStringOf(sqInt oop); +static NoDbgRegParms void scavengingGCTenuringIf(sqInt tenuringCriterion); +static NoDbgRegParms NeverInline sqInt setHeapBasememoryLimitendOfMemory(sqInt baseOfHeap, sqInt memLimit, sqInt memEnd); +static NoDbgRegParms void setIsPinnedOfto(sqInt objOop, sqInt aBoolean); +static NoDbgRegParms void setIsRememberedOfto(sqInt objOop, sqInt aBoolean); +extern sqInt shortentoIndexableSize(sqInt objOop, sqInt indexableSize); +extern void shortPrintObjectsFromto(sqInt startAddress, sqInt endAddress); +extern sqInt shouldRemapOop(sqInt oop); +static NoDbgRegParms sqInt sizeOfObjStack(sqInt objStack); +static int slidingCompactionInProgress(void); +extern sqInt slotSizeOf(sqInt oop); +extern sqInt splObj(sqInt index); +static NoDbgRegParms usqInt startOfObject(sqInt objOop); +extern sqInt statNumGCs(void); +extern usqInt storeCheckBoundary(void); +static NoDbgRegParms sqInt storeImageSegmentIntooutPointersroots(sqInt segmentWordArrayArg, sqInt outPointersArrayArg, sqInt arrayOfRootsArg); +extern void storeIntegerofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqLong integerValue); +extern int storeLong32ofObjectwithValue(sqInt fieldIndex, sqInt obj, sqInt valueWord); +extern sqInt storePointerUncheckedofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqInt valuePointer); +extern sqInt storePointerofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqInt valuePointer); +extern sqInt stringForCString(const char *aCString); +static NoDbgRegParms sqInt swizzleObjStackAt(sqInt objStackRootIndex); +extern void tenuringIncrementalGC(void); +static NoDbgRegParms sqInt topOfObjStack(sqInt objStack); +extern sqInt topRemappableOop(void); +static sqInt totalFreeListBytes(void); +extern sqInt trueObject(void); +static void unfireQueuedEphemeronsForSnapshot(void); +static NoDbgRegParms sqInt unlinkFreeChunkchunkBytes(sqInt freeChunk, sqInt chunkBytes); +static NoDbgRegParms NeverInline sqInt unlinkLilliputianChunkindex(sqInt freeChunk, sqInt index); +static NoDbgRegParms void unlinkSolitaryFreeTreeNode(sqInt freeTreeNode); +extern sqInt unpinObject(sqInt objOop); +static sqInt unscannedEphemeronsContainsOnlyActiveEphemerons(void); +static sqInt unscannedEphemeronsContainsOnlyEphemerons(void); +static NoDbgRegParms void updateListStartingAt(sqInt freeNode); +static sqInt validClassTableHashes(void); +static sqInt validClassTableRootPages(void); +extern int validFreeTree(void); +static NoDbgRegParms sqInt validFreeTreeChunk(sqInt chunk); +static NoDbgRegParms const char * validFreeTreeChunkparent(sqInt chunk, sqInt parent); +static sqInt validObjStacks(void); +static NoDbgRegParms sqInt validPostBecomeArrayContents(sqInt anArray); +static NoDbgRegParms sqInt validStorePointerArgs(sqInt fieldIndex, sqInt objOop, sqInt valuePointer); +static NoDbgRegParms sqInt validStorePointerUncheckedArgs(sqInt fieldIndex, sqInt objOop, sqInt valuePointer); +extern sqInt vmEndianness(void); +static NoDbgRegParms char * whereIsMaybeHeapThing(sqInt anOop); +static NeverInline void compact(void); +static NeverInline sqInt copyAndUnmarkMobileObjects(void); +static NoDbgRegParms NeverInline void copyAndUnmark(sqInt firstPass); +static void endCompaction(void); +static NoDbgRegParms void freeFromupTonextObject(usqInt initialToFinger, usqInt limit, sqInt nextObject); +static NoDbgRegParms sqInt isMobile(sqInt obj); +static NoDbgRegParms sqInt isPostMobile(sqInt obj); +static NeverInline sqInt planCompactSavingForwarders(void); +static NoDbgRegParms void reinitializeScanFrom(sqInt initialObject); +extern sqInt remapObj(sqInt objOop); +static sqInt savedFirstFieldsSpaceWasAllocated(void); +static NoDbgRegParms sqInt scanForFirstFreeAndFirstMobileObjectFrom(sqInt initialObject); +extern sqInt shouldRemapObj(sqInt objOop); +static void unmarkObjectsFromFirstFreeObject(void); +static NeverInline void updatePointers(void); +static NoDbgRegParms void updatePointersInsavedFirstFieldPointer(sqInt obj, sqInt firstFieldPtr); +static NoDbgRegParms sqInt useSegmentForSavedFirstFieldsSpace(sqInt spaceEstimate); +static NoDbgRegParms sqInt validRelocationPlanInPass(sqInt onePass); +static NoDbgRegParms usqInt segLimit(SpurSegmentInfo *self_in_SpurSegmentInfo); +static NoDbgRegParms SpurSegmentInfo * addSegmentOfSize(sqInt ammount); +static NoDbgRegParms void adjustSegmentSwizzlesBy(sqInt firstSegmentShift); +static sqInt allBridgesMarked(void); +static void allocateOrExtendSegmentInfos(void); +static NoDbgRegParms sqInt bridgeAt(sqInt segIndex); +static NoDbgRegParms sqInt bridgeFor(SpurSegmentInfo *aSegment); +static NoDbgRegParms void bridgeFromto(SpurSegmentInfo *aSegment, SpurSegmentInfo *nextSegmentOrNil); +static void checkSegments(void); +static void collapseSegmentsPostSwizzle(void); +static NoDbgRegParms void * firstGapOfSizeAtLeast(sqInt size); +static NoDbgRegParms sqInt isEmptySegment(SpurSegmentInfo *seg); +static NoDbgRegParms sqInt isInSegments(usqInt address); +static NoDbgRegParms sqInt isValidSegmentBridge(sqInt objOop); +static NoDbgRegParms SpurSegmentInfo * nextNonEmptySegmentAfter(sqInt i); +static NeverInline void prepareForSnapshot(void); +static NoDbgRegParms sqInt readHeapFromImageFiledataBytes(sqImageFile f, sqInt numBytes); +static void restorePostSnapshot(void); +extern SpurSegmentInfo * segmentContainingObj(sqInt objOop); +static sqInt segmentOverlap(void); +static NoDbgRegParms int shrinkObjectMemory(usqInt delta); +static NoDbgRegParms sqInt swizzleObj(sqInt objOop); +static usqInt totalBytesInSegments(void); +static NoDbgRegParms sqInt writeImageSegmentsToFile(sqImageFile aBinaryStream); +static NoDbgRegParms sqInt writeSegmentnextSegmenttoFile(SpurSegmentInfo *segment, SpurSegmentInfo *nextSegment, sqImageFile aBinaryStream); +static NoDbgRegParms sqInt accessorDepthForExternalPrimitiveMethod(sqInt methodObj); +extern sqInt accessorDepthForPrimitiveMethod(sqInt aMethodObj); +extern sqInt activeProcess(void); +extern void addIdleUsecs(sqInt idleUsecs); +static NoDbgRegParms void addLastLinktoList(sqInt proc, sqInt aList); +static char * allOnesAsCharStar(void); +extern sqInt argumentCountOfClosure(sqInt closurePointer); +extern usqInt argumentCountOfMethodHeader(sqInt header); +extern usqInt argumentCountOf(sqInt methodPointer); +extern void * arrayValueOf(sqInt arrayOop); +static NoDbgRegParms sqInt asciiOfCharacter(sqInt characterObj); +extern void assertValidExecutionPointers(usqInt lip, char *lifp, char *lisp); +static NoDbgRegParms void assertValidStackLimits(sqInt ln); +extern sqInt booleanValueOf(sqInt obj); +extern sqInt callbackEnter(sqInt *callbackID); +extern sqInt callbackLeave(sqInt cbID); +extern sqInt canContextSwitchIfActivatingheader(sqInt theMethod, sqInt methodHeader); +extern sqInt characterForAscii(sqInt ascii); +extern sqInt checkAllAccessibleObjectsOkay(void); +#if LRPCheck +static NeverInline sqInt checkDeliveryOfLongRunningPrimitiveSignal(void); +#endif /* LRPCheck */ +extern sqInt checkedIntegerValueOf(sqInt intOop); +extern sqInt checkImageHeaderFromBytesAndSize(char *bytes, sqInt totalSize); +static NoDbgRegParms sqInt checkImageVersionFromstartingAtassignRawVersion(sqImageFile f, squeakFileOffsetType imageOffset, sqInt *rawVersionPtr); +static sqInt checkInterpreterIntegrity(void); +static NoDbgRegParms sqInt checkIsStillMarriedContextcurrentFP(sqInt aContext, char *currentFP); +static sqInt checkLogIntegrity(void); +extern sqInt checkOkayInterpreterObjects(sqInt writeBack); +static NoDbgRegParms sqInt checkOkayStackPage(StackPage *thePage); +static NoDbgRegParms sqInt checkOkayStackZone(sqInt writeBack); +static sqInt checkStackIntegrity(void); +static NoDbgRegParms sqInt checkStackPointerForMaybeMarriedContext(sqInt aContext); +static NoDbgRegParms sqInt checkStackPointerIndexForFrame(char *theFP); +static NoDbgRegParms sqInt classNameOfIs(sqInt aClass, char *className); +extern void clearTraceLog(void); +static NoDbgRegParms sqInt contexthasValidInversePCMappingOfin(sqInt aContext, sqInt theIP, char *theFP); +extern sqInt copiedValueCountOfClosure(sqInt closureObj); +extern sqInt copyBits(void); +extern sqInt copyBitsFromtoat(sqInt x0, sqInt x1, sqInt y); +static NoDbgRegParms NeverInline sqInt couldBeProcess(sqInt oop); +static NoDbgRegParms void createActualMessageTo(sqInt lookupClass); +static sqInt defaultNumStackPages(void); +#if SEND_PRINTING +extern void disableSendPrinting(void); +#endif /* SEND_PRINTING */ +static sqInt divorceAllFrames(void); +static NoDbgRegParms void divorceFramesIn(StackPage *aStackPage); +static NoDbgRegParms sqInt doPrimitiveDivby(sqInt rcvr, sqInt arg); +static NoDbgRegParms sqInt doPrimitiveModby(sqInt rcvr, sqInt arg); +extern sqInt doSignalSemaphoreWithIndex(sqInt index); +extern struct VirtualMachine * * dummyReferToProxy(void); +extern void dumpPrimTraceLog(void); +extern void dumpPrimTraceLogOn(FILE *aStdioStream); +extern NeverInline void eekcr(void); +#if SEND_PRINTING +extern void enableSendPrinting(void); +#endif /* SEND_PRINTING */ +static NoDbgRegParms sqInt ensureCallerContext(char *theFP); +static NoDbgRegParms void ensureImageFormatIsUpToDate(sqInt swapBytes); +static NoDbgRegParms char * establishFrameForContextToReturnTo(sqInt contextToReturnTo); +static NoDbgRegParms void externalDivorceFrameandContext(char *theFP, sqInt ctxt); +static NoDbgRegParms char * externalEnsureIsBaseFrame(char *aFramePtr); +static NoDbgRegParms sqInt externalInstVarofContextput(sqInt index, sqInt maybeMarriedContext, sqInt anOop); +static sqInt externalQuickPrimitiveResponse(void); +static void failUnbalancedPrimitive(void); +extern void * fetchArrayofObject(sqInt fieldIndex, sqInt objectPointer); +extern double fetchFloatofObject(sqInt fieldIndex, sqInt objectPointer); +static NoDbgRegParms sqInt fetchPointerofMarriedContext(sqInt offset, sqInt aContext); +static NoDbgRegParms sqInt fetchStackPointerOf(sqInt aContext); +extern int fileTimesInUTC(void); +static NoDbgRegParms sqInt findClassContainingMethodstartingAt(sqInt meth, sqInt classObj); +extern sqInt findClassOfMethodforReceiver(sqInt meth, sqInt rcvr); +static NoDbgRegParms char * findFrameAboveinPage(char *theFP, StackPage *thePage); +static sqInt findHighestPriority(void); +static NoDbgRegParms sqInt findHomeForContext(sqInt aContext); +static NoDbgRegParms sqInt findMethodWithPrimitiveFromContextUpToContext(sqInt primitive, sqInt senderContext, sqInt homeContext); +extern sqInt findSelectorOfMethod(sqInt aMethodOop); +static NoDbgRegParms char * findSPOfon(char *theFP, StackPage *thePage); +static NoDbgRegParms sqInt findUnwindThroughContext(sqInt homeContext); +extern void flush(void); +static NoDbgRegParms sqInt flushExternalPrimitiveOf(sqInt methodObj); +static NoDbgRegParms void flushMethodCacheForMethod(sqInt oldMethod); +static NoDbgRegParms void followForwardedFrameContentsstackPointer(char *theFP, char *theSP); +extern sqInt forceInterruptCheck(void); +static NoDbgRegParms char * frameCallerSP(char *theFP); +static NoDbgRegParms sqInt frameContext(char *theFP); +static NoDbgRegParms char * frameOfMarriedContext(sqInt aContext); +static NoDbgRegParms sqInt frameStackedReceiverOffset(char *theFP); +static NoDbgRegParms sqInt frameStackedReceivernumArgs(char *theFP, sqInt numArgs); +static void freeUntracedStackPages(void); +extern sqInt fullDisplayUpdate(void); +static NoDbgRegParms void (*functionPointerForinClass(sqInt primIdx, sqInt theClass))(void); +static sqInt getCogVMFeatureFlags(void); +static sqInt getErrorObjectFromPrimFailCode(void); +extern int getFullScreenFlag(void); +extern sqInt getInterruptKeycode(void); +extern sqInt getInterruptPending(void); +extern usqLong getNextWakeupUsecs(void); +extern sqInt getSavedWindowSize(void); +static NoDbgRegParms usqInt getShortFromFileswap(sqImageFile aFile, sqInt swapFlag); +static sqInt getSnapshotScreenSize(void); +extern sqInt * getStackPointer(void); +extern sqInt getThisSessionID(void); +extern FILE * getTranscript(void); +static NoDbgRegParms sqInt handleForwardedSelectorFaultFor(sqInt selectorOop); +static NoDbgRegParms sqInt handleForwardedSendFaultForTag(sqInt classTag); +static NoDbgRegParms sqInt handleSpecialSelectorSendFaultForfpsp(sqInt obj, char *theFP, char *theSP); +static NeverInline void handleStackOverflow(void); +static NoDbgRegParms sqInt handleStackOverflowOrEventAllowContextSwitch(sqInt mayContextSwitch); +extern sqInt highBit(usqInt anUnsignedValue); +extern sqInt homeMethodOf(sqInt aMethodOop); +static NoDbgRegParms sqInt ifCurrentStackPageHasValidHeadPointers(StackPage *thePage); +static NoDbgRegParms usqInt iframeMethod(char *theFP); +extern sqInt includesBehaviorThatOf(sqInt aClass, sqInt aSuperclass); +extern void initStackPagesAndContinueIntowith(void (*continuation)(void *), void *argument); +extern void ioFilenamefromStringofLengthresolveAliases(char *aCharBuffer, char *aFilenameString, sqInt filenameLength, sqInt aBoolean); +static NoDbgRegParms int isBaseFrame(char *theFP); +extern sqInt isBooleanObject(sqInt oop); +static NoDbgRegParms sqInt isCalloutPrimitiveIndex(sqInt primIndex); +static NoDbgRegParms int isEmptyList(sqInt aLinkedList); +extern int isFloatObject(sqInt oop); +static NoDbgRegParms sqInt isFrameonPage(char *aFrame, StackPage *aPage); +extern sqInt isKindOfInteger(sqInt oop); +extern sqInt isLargeIntegerObject(sqInt oop); +extern sqInt isLargeNegativeIntegerObject(sqInt oop); +extern sqInt isLargePositiveIntegerObject(sqInt oop); +static NoDbgRegParms sqInt isLinkedExternalPrimitive(sqInt methodObj); +static NoDbgRegParms sqInt isLiveContext(sqInt oop); +static NoDbgRegParms int isMarriedOrWidowedContext(sqInt aContext); +static NoDbgRegParms sqInt isMetaPrimitiveIndex(sqInt primIndex); +static NoDbgRegParms sqInt isNullExternalPrimitiveCall(sqInt aMethodObj); +static int isPrimitiveFunctionPointerAnIndex(void); +extern int isQuickPrimitiveIndex(sqInt anInteger); +extern int isReadMediatedContextInstVarIndex(sqInt index); +static NoDbgRegParms int isSingleContext(sqInt aContext); +static NoDbgRegParms sqInt isStillMarriedContext(sqInt aContext); +static NoDbgRegParms int isVanillaBlockClosure(sqInt aClosure); +static NoDbgRegParms sqInt isWidowedContextNoConvert(sqInt aOnceMarriedContext); +extern int isWriteMediatedContextInstVarIndex(sqInt index); +extern sqInt isKindOfClass(sqInt oop, sqInt aClass); +extern sqInt isKindOf(sqInt oop, char *className); +extern sqInt isMemberOf(sqInt oop, char *className); +static NoDbgRegParms sqInt lengthOfNameOfClass(sqInt classOop); +extern sqInt literalofMethod(sqInt offset, sqInt methodPointer); +extern sqInt loadBitBltFrom(sqInt bb); +extern void longPrintOop(sqInt oop); +extern sqInt longStoreBytecodeForHeader(sqInt methodHeader); +static NoDbgRegParms sqInt lookupInMethodCacheSelclassTag(sqInt selector, sqInt classTag); +static NoDbgRegParms sqInt lookupMethodInClass(sqInt class); +static NoDbgRegParms sqInt lookupMNUInClass(sqInt class); +static NoDbgRegParms sqInt lookupOrdinaryNoMNUEtcInClass(sqInt class); +extern sqInt lookupSelectorinClass(sqInt selector, sqInt class); +static NoDbgRegParms void makeContextSnapshotSafe(sqInt ctxt); +extern usqInt makePointwithxValueyValue(sqInt xValue, sqInt yValue); +static void mapPrimTraceLog(void); +static void mapTraceLog(void); +static void markAndTracePrimTraceLog(void); +static NoDbgRegParms sqInt markAndTraceStackPages(sqInt fullGCFlag); +static void markAndTraceTraceLog(void); +static void markAndTraceUntracedReachableStackPages(void); +static NoDbgRegParms sqInt marriedContextpointsTostackDeltaForCurrentFrame(sqInt spouseContext, sqInt anOop, sqInt stackDeltaForCurrentFrame); +static NoDbgRegParms void marryContextInNewStackPageAndInitializeInterpreterRegisters(sqInt aContext); +static NoDbgRegParms sqInt maybeLeakCheckExternalPrimCall(sqInt aMethodObj); +extern sqInt maybeSelectorOfMethod(sqInt methodObj); +extern sqInt methodArgumentCount(void); +extern sqInt methodClassAssociationOf(sqInt methodPointer); +extern sqInt methodClassOf(sqInt methodPointer); +extern sqInt methodPrimitiveIndex(void); +extern sqInt methodReturnBool(sqInt boolean); +extern sqInt methodReturnFloat(double aFloat); +extern sqInt methodReturnInteger(sqInt integer); +extern sqInt methodReturnReceiver(void); +extern NeverInline sqInt methodReturnString(const char *aCString); +extern sqInt methodReturnValue(sqInt oop); +extern int methodUsesAlternateBytecodeSet(sqInt aMethodObj); +static NoDbgRegParms sqInt methodUsesPrimitiveErrorCode(sqInt aMethodObj); +EXPORT(void) moduleUnloaded(char *aModuleName); +static NoDbgRegParms char * nameOfClass(sqInt classOop); +static NoDbgRegParms char * nameOfClasslengthInto(sqInt classOop, sqInt *lengthPtr); +static NoDbgRegParms sqInt noMarkedContextsOnPage(StackPage *thePage); +static NoDbgRegParms sqInt numSlotsOfMarriedContext(sqInt aContext); +static sqInt numStkPages(void); +static NoDbgRegParms sqInt objectequalsString(sqInt anOop, char *aCString); +extern usqInt pcPreviousToinSqueakV3PlusClosuresOrSistaV1Method(sqInt theIP, sqInt aMethod); +static NoDbgRegParms sqInt penultimateLiteralOf(sqInt aMethodOop); +extern sqInt popStack(void); +extern sqInt pop(sqInt nItems); +extern void popthenPush(sqInt nItems, sqInt oop); +static sqInt postGCUpdateDisplayBits(void); +EXPORT(void) primitiveEventProcessingControl(void); +static NoDbgRegParms int primitiveFloatEqualtoArg(sqInt rcvrOop, sqInt argOop); +static NoDbgRegParms int primitiveFloatGreaterOrEqualtoArg(sqInt rcvrOop, sqInt argOop); +static NoDbgRegParms int primitiveFloatGreaterthanArg(sqInt rcvrOop, sqInt argOop); +static NoDbgRegParms int primitiveFloatLessOrEqualtoArg(sqInt rcvrOop, sqInt argOop); +static NoDbgRegParms int primitiveFloatLessthanArg(sqInt rcvrOop, sqInt argOop); +extern sqInt primitiveIndexOfMethodheader(sqInt theMethod, sqInt methodHeader); +extern sqInt primitiveIndexOf(sqInt methodPointer); +extern usqInt primitiveMethod(void); +static NoDbgRegParms void printActivationNameForSelectorstartClass(sqInt aSelector, sqInt startClass); +static NoDbgRegParms void printActivationNameForreceiverisBlockfirstTemporary(sqInt aMethod, sqInt anObject, sqInt isBlock, sqInt maybeMessage); +extern void printAllStacksOn(FILE *aStdioStream); +extern void printCallStack(void); +static NoDbgRegParms sqInt printCallStackFP(char *theFP); +extern sqInt printCallStackOf(sqInt aContextOrProcessOrFrame); +static NoDbgRegParms sqInt printCallStackOfcurrentFP(sqInt aContext, char *currFP); +extern void printCallStackOn(FILE *aStdioStream); +static NoDbgRegParms sqInt printContextCallStackOf(sqInt aContext); +extern void printContext(sqInt aContext); +static NoDbgRegParms void printDecodeMethodHeaderOop(sqInt methodHeaderOop); +extern void printExternalHeadFrame(void); +static NoDbgRegParms sqInt printFrameAndCallersSPshort(char *theFP, char *theSP, sqInt printShort); +static NoDbgRegParms void printFrameOopat(char *name, char *address); +static NoDbgRegParms void printFrameOopindexat(char *name, sqInt idx, char *address); +extern void printFramesInPage(StackPage *thePage); +extern void printFramesOnStackPageListInUse(void); +static NoDbgRegParms void printFrameThingandFrameat(char *name, char *theFP, char *address); +static NoDbgRegParms void printFrameThingatextraString(char *name, char *address, char *extraStringOrNil); +extern sqInt printFrame(char *theFP); +static NoDbgRegParms sqInt printHexPtrnp(void *p); +extern void printHex(usqInt n); +extern void printLikelyImplementorsOfSelector(sqInt selector); +extern void printMethodCache(void); +extern void printMethodDictionaryOf(sqInt behavior); +extern void printMethodDictionary(sqInt dictionary); +static NoDbgRegParms void printNameOfClasscount(sqInt classOop, sqInt cnt); +static NoDbgRegParms void printNum(sqInt n); +static NoDbgRegParms void printOopShortInner(sqInt oop); +extern void printOop(sqInt oop); +static NoDbgRegParms sqInt printPrimLogEntryAthasParameter(sqInt i, sqInt hasParameter); +extern void printProcessStack(sqInt aProcess); +extern sqInt printProcsOnList(sqInt procList); +extern sqInt printStackCallStackOf(sqInt aContextOrProcessOrFrame); +extern void printStackPageList(void); +extern void printStackPageListInUse(void); +extern void printStackPages(void); +extern void printStackPagesInUse(void); +static NoDbgRegParms void printStackPageuseCount(StackPage *page, sqInt n); +extern void printStackReferencesTo(sqInt oop); +static NoDbgRegParms void printStringOf(sqInt oop); +extern void print(char *s); +extern sqInt processListForProcess(sqInt aProcess); +extern sqInt processOSErrInstVarOffset(void); +extern void pushBool(sqInt trueOrFalse); +static NoDbgRegParms sqInt pushedReceiverOrClosureOfFrame(char *theFP); +extern void pushFloat(double f); +extern sqInt pushInteger(sqInt integerValue); +extern void push(sqInt object); +static NoDbgRegParms void putLongtoFile(sqInt aLong, sqImageFile aFile); +static NoDbgRegParms void putShorttoFile(short aShort, sqImageFile aFile); +static NoDbgRegParms void putToSleepyieldingIf(sqInt aProcess, sqInt yieldImplicitly); +static NoDbgRegParms void putWord32toFile(int aWord32, sqImageFile aFile); +static NoDbgRegParms sqInt quickFetchIntegerofObject(sqInt fieldIndex, sqInt objectPointer); +extern sqInt readableFormat(sqInt imageVersion); +static NoDbgRegParms NeverInline void reapAndResetErrorCodeToheader(char *theSP, sqInt methodHeader); +extern sqInt reestablishContextPriorToCallback(sqInt callbackContext); +static NoDbgRegParms sqInt removeFirstLinkOfList(sqInt aList); +static NoDbgRegParms sqInt removeProcessfromList(sqInt aProcess, sqInt aList); +static NoDbgRegParms sqInt resumepreemptedYieldingIffrom(sqInt aProcess, sqInt yieldImplicitly, sqInt sourceCode); +static sqInt retryPrimitiveOnFailure(void); +extern sqInt returnAsThroughCallbackContext(sqInt returnTypeOop, VMCallbackContext *vmCallbackContext, sqInt callbackMethodContext); +static NoDbgRegParms void reverseDisplayFromto(sqInt startIndex, sqInt endIndex); +static NoDbgRegParms void rewriteMethodCacheEntryForExternalPrimitiveToFunction(void (*localPrimAddress)()); +static NoDbgRegParms sqInt safeMethodClassOf(sqInt methodPointer); +static NoDbgRegParms sqInt saneFunctionPointerForFailureOfPrimIndex(sqInt primIndex); +extern sqInt schedulerPointer(void); +extern void setBreakMNUSelector(char *aString); +extern void setBreakSelector(char *aString); +extern void setFullScreenFlag(sqInt aBoolean); +extern void * setInterruptCheckChain(void (*aFunction)()); +extern void setInterruptKeycode(sqInt value); +extern void setInterruptPending(sqInt value); +extern void setNextWakeupUsecs(usqLong value); +extern void setSavedWindowSize(sqInt value); +static void setSignalLowSpaceFlagAndSaveProcess(void); +static NoDbgRegParms void setTraceFlagOnContextsFramesPageIfNeeded(sqInt aContext); +static NoDbgRegParms sqInt shortPrintContext(sqInt aContext); +static NoDbgRegParms sqInt shortPrintFrameAndCallers(char *theFP); +extern void shortPrintFramesInPage(StackPage *thePage); +extern void shortPrintFramesOnStackPageListInUse(void); +extern void shortPrintFrameAndNCallers(char *theFP, sqInt n); +static NoDbgRegParms void shortPrintOop(sqInt oop); +static NoDbgRegParms sqInt shortReversePrintFrameAndCallers(char *aFramePointer); +extern sqInt showDisplayBitsLeftTopRightBottom(sqInt aForm, sqInt l, sqInt t, sqInt r, sqInt b); +static sqInt signalExternalSemaphores(void); +extern sqInt sizeOfCallPrimitiveBytecode(sqInt methodHeader); +extern sqInt sizeOfLongStoreTempBytecode(sqInt methodHeader); +extern sqInt sizeOfSTArrayFromCPrimitive(void *cPtr); +static int slowPrimitiveResponse(void); +static NoDbgRegParms sqInt snapshot(sqInt embedded); +extern sqInt specialSelector(sqInt index); +static NoDbgRegParms void spurPostBecomeAction(sqInt theBecomeEffectsFlags); +extern double stackFloatValue(sqInt offset); +extern sqInt stackIntegerValue(sqInt offset); +static sqInt stackLimitBytes(void); +extern sqInt stackMutableObjectValue(sqInt offset); +extern sqInt stackObjectValue(sqInt offset); +static sqInt stackPageByteSize(void); +static NoDbgRegParms sqInt stackPointerForMaybeMarriedContext(sqInt aContext); +static NoDbgRegParms sqInt stackPointerIndexForFrame(char *theFP); +extern usqIntptr_t stackPositiveMachineIntegerValue(sqInt offset); +extern sqIntptr_t stackSignedMachineIntegerValue(sqInt offset); +extern sqInt stackTop(void); +extern sqInt stackValue(sqInt offset); +extern usqInt startPCOfMethod(sqInt aCompiledMethod); +extern sqInt stObjectat(sqInt array, sqInt index); +extern sqInt stObjectatput(sqInt array, sqInt index, sqInt value); +static NoDbgRegParms char * storeSenderOfFramewithValue(char *theFP, sqInt anOop); +extern sqInt stSizeOf(sqInt oop); +extern sqInt superclassOf(sqInt classPointer); +static NoDbgRegParms sqInt synchronousSignal(sqInt aSemaphore); +extern usqInt tempCountOf(sqInt methodPointer); +extern usqInt temporaryCountOfMethodHeader(sqInt header); +extern sqInt ultimateLiteralOf(sqInt aMethodOop); +static NoDbgRegParms sqInt unfollowFirstLiteralOfMaybeCalloutMethodprimitiveIndex(sqInt methodObj, sqInt primIndex); +static NoDbgRegParms sqInt updateDisplayLeftTopRightBottom(sqInt l, sqInt t, sqInt r, sqInt b); +static NoDbgRegParms sqInt validBCPCinMethod(sqInt thePC, usqInt aMethod); +static NoDbgRegParms sqInt validInstructionPointerinFrame(usqInt anInstrPointer, char *fp); +static sqInt validStackPageBaseFrames(void); +#if LRPCheck +static NoDbgRegParms NeverInline void voidLongRunningPrimitive(char *reason); +#endif /* LRPCheck */ +static sqInt wakeHighestPriority(void); +static NeverInline sqInt writeImageFileIO(void); +static NoDbgRegParms usqInt cloneContext(sqInt aContext); +static NoDbgRegParms sqInt fieldOrSenderFPofContext(sqInt index, sqInt contextObj); +static NoDbgRegParms sqInt fieldofFrame(sqInt index, char *theFP); +static NoDbgRegParms sqInt isAppropriateForCopyObject(sqInt oop); +static NoDbgRegParms void (*linkExternalCallerrInto(sqInt externalCallLiteral, sqInt *failPtr))(void); +static void primitiveClone(void); +static void primitiveContextAt(void); +static void primitiveContextAtPut(void); +static void primitiveContextPreserveTemps(void); +static void primitiveContextSize(void); +static void primitiveDeferDisplayUpdates(void); +static void primitiveDoNamedPrimitiveWithArgs(void); +static void primitiveDoPrimitiveWithArgs(void); +static void primitiveExternalCall(void); +static void primitiveFindHandlerContext(void); +static void primitiveFindNextUnwindContext(void); +static void primitiveFullGC(void); +static void primitiveGetSetFFIExceptionHandling(void); +static void primitiveIncrementalGC(void); +static void primitiveInstVarAt(void); +static void primitiveInstVarAtPut(void); +static void primitiveInvokeObjectAsMethod(void); +#if LRPCheck +EXPORT(void) primitiveLongRunningPrimitive(void); +#endif /* LRPCheck */ +static void primitiveObjectPointsTo(void); +static void primitivePerform(void); +static void primitivePin(void); +EXPORT(void) primitiveSetGCSemaphore(void); +static void primitiveSignalAtMilliseconds(void); +static void primitiveSignalAtUTCMicroseconds(void); +static void primitiveSlotAt(void); +static void primitiveSlotAtPut(void); +static void primitiveStoreStackp(void); +static void primitiveVMParameter(void); +EXPORT(void) primitiveVoidReceiver(void); +static NoDbgRegParms void pruneStackstackp(sqInt stack, sqInt stackp); +static void unmarkAfterPathTo(void); + +/*** Variables ***/ +#if SQ_USE_GLOBAL_STRUCT +# define _iss /* define in-struct static as void */ +static struct foo { +#else +# define _iss static +#endif +_iss char * stackLimit; +_iss char * stackPointer; +_iss char * framePointer; +_iss volatile usqIntptr_t CStackPointer; +_iss volatile usqIntptr_t CFramePointer; +_iss volatile usqIntptr_t CReturnAddress; +_iss usqInt scavengeThreshold; +_iss usqInt freeStart; +_iss sqInt needGCFlag; +_iss sqInt specialObjectsOop; +_iss sqInt primFailCode; +_iss unsigned char primTraceLogIndex; +_iss usqInt newMethod; +_iss usqInt instructionPointer; +_iss sqInt argumentCount; +_iss sqLong nextProfileTick; +_iss sqInt primTraceLog[256]; +_iss StackPage * stackPage; +_iss sqInt nilObj; +_iss usqInt oldSpaceStart; +_iss usqInt method; +_iss sqInt bytecodeSetSelector; +_iss FILE * transcript; +_iss usqInt endOfMemory; +_iss sqInt messageSelector; +_iss usqInt pastSpaceStart; +_iss usqInt newSpaceStart; +_iss StackPage * pages; +_iss sqInt trueObj; +_iss sqInt falseObj; +_iss usqInt totalFreeOldSpace; +_iss sqInt hiddenRootsObj; +_iss sqInt traceLogIndex; +_iss SpurSegmentInfo * segments; +_iss sqInt markStack; +_iss sqInt numSegments; +_iss sqInt weaklingStack; +_iss sqInt * freeLists; +_iss sqInt rememberedSetSize; +_iss sqInt numClassTablePages; +_iss sqInt classTableFirstPage; +_iss sqInt * rememberedSet; +_iss sqInt numThreads; +_iss sqInt futureSurvivorStart; +_iss StackPage * mostRecentlyUsedPage; +_iss sqInt tempOop; +_iss usqInt firstFreeObject; +_iss usqInt freeListsMask; +_iss usqInt lastHash; +_iss sqInt mournQueue; +_iss char * stackBasePlus1; +_iss sqInt disowningThreadIndex; +_iss sqInt numRememberedEphemerons; +_iss sqInt numStackPages; +_iss usqInt totalHeapSizeIncludingBridges; +_iss sqInt classTableIndex; +_iss usqInt lastMobileObject; +_iss CogVMOwnerLog * ownerLog; +_iss sqInt profileSemaphore; +_iss CogVMThread ** threads; +_iss sqInt profileProcess; +_iss sqInt profileMethod; +_iss sqInt remapBufferCount; +_iss sqInt becomeEffectsFlags; +#if LRPCheck +_iss sqInt longRunningPrimitiveCheckSemaphore; +#endif +_iss sqInt mobileStart; +_iss sqInt performWithArgumentsRecursionGuard; +_iss sqInt growHeadroom; +_iss sqInt lkupClass; +_iss sqInt tenureCriterion; +_iss sqInt tenureThreshold; +_iss sqInt ephemeronList; +_iss sqInt gcMode; +_iss sqInt lastCoggableInterpretedBlockMethod; +_iss sqInt lastUncoggableInterpretedBlockMethod; +_iss char * objStackInvalidBecause; +_iss sqInt classNameIndex; +_iss sqInt gcPhaseInProgress; +_iss sqInt highestRunnableProcessPriority; +#if LRPCheck +_iss sqInt longRunningPrimitiveCheckMethod; +#endif +_iss sqInt extraRootCount; +_iss sqInt invalidObjStackPage; +_iss sqInt lastMethodCacheProbeWrite; +_iss sqInt metaAccessorDepth; +_iss usqInt objectAfterLastMobileObject; +_iss sqInt tempOop2; +_iss sqInt cogCompiledCodeCompactionCalledFor; +_iss usqInt freeOldSpaceStart; +_iss sqInt previousRememberedSetSize; +_iss sqInt validatedIntegerClassFlags; +_iss usqInt firstMobileObject; +_iss sqInt numSegInfos; +_iss sqInt savedFirstFieldsSpaceNotInOldSpace; +_iss usqLong statGCEndUsecs; +_iss sqInt bytesPerPage; +_iss usqInt cogCodeSize; +#if LRPCheck +_iss usqLong longRunningPrimitiveStopUsecs; +#endif +_iss usqLong nextWakeupUsecs; +_iss sqInt relinquishing; +_iss sqInt shrinkThreshold; +_iss sqInt statTenures; +_iss sqInt thisClassIndex; +#if LRPCheck +_iss usqLong longRunningPrimitiveStartUsecs; +#endif +_iss sqInt metaclassNumSlots; +_iss sqInt pendingFinalizationSignals; +_iss usqLong statCheckForEvents; +_iss sqInt statSurvivorCount; +_iss sqInt weakList; +_iss sqInt disowningCStackPointer; +_iss sqInt firstFieldOfRememberedSet; +_iss usqLong gcStartUsecs; +_iss sqInt marking; +_iss sqLong oldSpaceUsePriorToScavenge; +_iss sqInt statMaxAllocSegmentTime; +_iss sqInt statScavenges; +_iss sqInt statShrinkMemory; +_iss sqInt firstSegmentSize; +_iss sqInt multipleBytecodeSetsActive; +_iss sqInt numThreadsIncrement; +_iss sqInt rememberedSetLimit; +_iss FILE * scavengeLog; +_iss sqInt statFullGCs; +_iss usqLong statProcessSwitch; +_iss sqInt biasForGC; +_iss sqInt deferProfileCheckForVNCS; +_iss sqInt foreignCallbackPriority; +_iss usqInt lowSpaceThreshold; +_iss sqInt memoryIsScarce; +_iss sqInt rememberedSetRedZone; +_iss sqLong secondaryErrorCode; +_iss usqLong statAllocatedBytes; +_iss sqInt statCodeCompactionCount; +_iss usqLong statCodeCompactionUsecs; +_iss usqLong statCompactionUsecs; +_iss usqLong statFullGCUsecs; +_iss sqInt statGrowMemory; +_iss usqLong statMarkUsecs; +_iss sqInt statNumMaps; +_iss usqLong statScavengeGCUsecs; +_iss usqLong statSweepUsecs; +_iss sqInt the2ndUnknownShort; +_iss sqInt anomaly; +_iss sqInt checkThreadActivation; +_iss sqInt deferThreadSwitch; +_iss sqInt edenBytes; +_iss sqInt externalPrimitiveTableFirstFreeIndex; +_iss sqInt freeSpaceCheckOopToIgnore; +_iss usqInt heapSizeAtPreviousGC; +_iss sqInt interruptKeycode; +_iss sqInt interruptPending; +#if LRPCheck +_iss sqInt longRunningPrimitiveCheckSequenceNumber; +#endif +_iss sqInt methodDictLinearSearchLimit; +_iss sqInt oldImageBaseAddress; +_iss sqInt ownerLogWrapped; +_iss sqInt savedWindowSize; +_iss usqLong startTimestamp; +_iss usqLong statForceInterruptCheck; +_iss usqLong statIOProcessEvents; +_iss usqLong statIdleUsecs; +_iss sqInt statMarkCount; +_iss sqInt statMaxPageCountWhenMapping; +_iss sqInt statPageCountWhenMappingSum; +_iss sqInt statRootTableCount; +_iss usqLong statStackOverflow; +_iss usqLong statStackPageDivorce; +_iss sqInt canSwizzle; +_iss sqInt classLinkedListClassTag; +_iss sqInt deferSmash; +_iss sqInt deferredSmash; +_iss sqInt disowningCFramePointer; +_iss sqInt extraFramesToMoveOnOverflow; +_iss sqInt globalSessionID; +#if LRPCheck +_iss sqInt longRunningPrimitiveSignalUndelivered; +#endif +_iss sqInt maxExtSemTabSizeSet; +_iss usqLong nextPollUsecs; +_iss sqInt primCalloutIsExternal; +_iss sqInt refCountToShrinkRT; +_iss sqInt signalLowSpace; +_iss sqInt statCompactPassCount; +_iss usqLong statIGCDeltaUsecs; +_iss sqInt statPendingFinalizationSignals; +_iss sqInt statRootTableOverflows; +_iss usqLong statSGCDeltaUsecs; +_iss sqInt bogon; +_iss sqInt classByteArrayCompactIndex; +_iss sqInt disowningInstructionPointer; +_iss usqInt exceptionPC; +_iss sqInt gcSemaphoreIndex; +_iss usqLong gcSweepEndUsecs; +#if LRPCheck +_iss usqLong longRunningPrimitiveGCUsecs; +#endif +_iss sqInt overflowLimit; +_iss StackPage * overflowedPage; +_iss sqInt printSends; +_iss sqInt tenuringClassIndex; +_iss sqInt statIncrGCs; +_iss sqIntptr_t methodCache[MethodCacheSize + 1 /* 4097 */]; +_iss sqInt traceLog[TraceBufferSize /* 768 */]; +_iss sqInt remapBuffer[RemapBufferSize + 1 /* 26 */]; +_iss sqInt *extraRoots[ExtraRootsSize + 1 /* 65 */]; +_iss usqLong byteCount; +_iss volatile atomic_int vmOwner; +_iss SpurContiguousObjStack unscannedEphemerons; +_iss SpurContiguousObjStack savedFirstFieldsSpace; +_iss SpurNewSpaceSpace pastSpace; +_iss SpurScavengeLogRecord scavengeLogRecord; +_iss SpurNewSpaceSpace futureSpace; +_iss SpurNewSpaceSpace eden; +_iss volatile atomic_int maxWaitingPriority; +_iss volatile atomic_int ownerLogIndex; +_iss float heapGrowthToSizeGCRatio; +_iss double tenuringProportion; +_iss sqOSThread vmOSThread; +#undef _iss +#if SQ_USE_GLOBAL_STRUCT + } fum; +# if SQ_USE_GLOBAL_STRUCT_REG +# define DECL_MAYBE_SQ_GLOBAL_STRUCT /* using a global reg pointer */ +# define DECL_MAYBE_VOLATILE_SQ_GLOBAL_STRUCT /* using a global reg pointer */ +# else +# define DECL_MAYBE_SQ_GLOBAL_STRUCT register struct foo * foo = &fum; +# define DECL_MAYBE_VOLATILE_SQ_GLOBAL_STRUCT volatile register struct foo * foo = &fum; +# endif +# define GIV(interpreterInstVar) (foo->interpreterInstVar) +#else +# define DECL_MAYBE_SQ_GLOBAL_STRUCT /* oh, no mr bill! */ +# define DECL_MAYBE_VOLATILE_SQ_GLOBAL_STRUCT /* oh no, mr bill! */ +# define GIV(interpreterInstVar) interpreterInstVar +#endif +#if SQ_USE_GLOBAL_STRUCT +# if SQ_USE_GLOBAL_STRUCT_REG +# define fooxstr(s) foostr(s) +# define foostr(s) #s +register struct foo * foo asm(fooxstr(USE_GLOBAL_STRUCT_REG)); +# else +static struct foo * foo = &fum; +# endif +#endif +static void (*primitiveFunctionPointer)(); +static void *primitiveCalloutPointer = (void *)-1; +static usqInt (* const pcPreviousToFunction)(sqInt,sqInt) = pcPreviousToinSqueakV3PlusClosuresOrSistaV1Method; +static void (*interruptCheckChain)(void) = 0; +static int (*sHEAFn)() = 0; +static void (*primitiveTable[MaxPrimitiveIndex + 2 /* 585 */])(void) = { + /* 0 */ (void (*)(void))0, + /* 1 */ primitiveAdd, + /* 2 */ primitiveSubtract, + /* 3 */ primitiveLessThan, + /* 4 */ primitiveGreaterThan, + /* 5 */ primitiveLessOrEqual, + /* 6 */ primitiveGreaterOrEqual, + /* 7 */ primitiveEqual, + /* 8 */ primitiveNotEqual, + /* 9 */ primitiveMultiply, + /* 10 */ primitiveDivide, + /* 11 */ primitiveMod, + /* 12 */ primitiveDiv, + /* 13 */ primitiveQuo, + /* 14 */ primitiveBitAnd, + /* 15 */ primitiveBitOr, + /* 16 */ primitiveBitXor, + /* 17 */ primitiveBitShift, + /* 18 */ primitiveMakePoint, + /* 19 */ (void (*)(void))0, + /* 20 */ primitiveRemLargeIntegers, + /* 21 */ primitiveAddLargeIntegers, + /* 22 */ primitiveSubtractLargeIntegers, + /* 23 */ primitiveLessThanLargeIntegers, + /* 24 */ primitiveGreaterThanLargeIntegers, + /* 25 */ primitiveLessOrEqualLargeIntegers, + /* 26 */ primitiveGreaterOrEqualLargeIntegers, + /* 27 */ primitiveEqualLargeIntegers, + /* 28 */ primitiveNotEqualLargeIntegers, + /* 29 */ primitiveMultiplyLargeIntegers, + /* 30 */ primitiveDivideLargeIntegers, + /* 31 */ primitiveModLargeIntegers, + /* 32 */ primitiveDivLargeIntegers, + /* 33 */ primitiveQuoLargeIntegers, + /* 34 */ primitiveBitAndLargeIntegers, + /* 35 */ primitiveBitOrLargeIntegers, + /* 36 */ primitiveBitXorLargeIntegers, + /* 37 */ primitiveBitShiftLargeIntegers, + /* 38 */ primitiveFloatAt, + /* 39 */ primitiveFloatAtPut, + /* 40 */ primitiveAsFloat, + /* 41 */ primitiveFloatAdd, + /* 42 */ primitiveFloatSubtract, + /* 43 */ primitiveFloatLessThan, + /* 44 */ primitiveFloatGreaterThan, + /* 45 */ primitiveFloatLessOrEqual, + /* 46 */ primitiveFloatGreaterOrEqual, + /* 47 */ primitiveFloatEqual, + /* 48 */ primitiveFloatNotEqual, + /* 49 */ primitiveFloatMultiply, + /* 50 */ primitiveFloatDivide, + /* 51 */ primitiveTruncated, + /* 52 */ primitiveFractionalPart, + /* 53 */ primitiveExponent, + /* 54 */ primitiveTimesTwoPower, + /* 55 */ primitiveSquareRoot, + /* 56 */ primitiveSine, + /* 57 */ primitiveArctan, + /* 58 */ primitiveLogN, + /* 59 */ primitiveExp, + /* 60 */ primitiveAt, + /* 61 */ primitiveAtPut, + /* 62 */ primitiveSize, + /* 63 */ primitiveStringAt, + /* 64 */ primitiveStringAtPut, + /* 65 */ (void (*)(void))0, + /* 66 */ (void (*)(void))0, + /* 67 */ (void (*)(void))0, + /* 68 */ primitiveObjectAt, + /* 69 */ primitiveObjectAtPut, + /* 70 */ primitiveNew, + /* 71 */ primitiveNewWithArg, + /* 72 */ primitiveArrayBecomeOneWay, + /* 73 */ primitiveInstVarAt, + /* 74 */ primitiveInstVarAtPut, + /* 75 */ primitiveIdentityHash, + /* 76 */ primitiveStoreStackp, + /* 77 */ primitiveSomeInstance, + /* 78 */ primitiveNextInstance, + /* 79 */ primitiveNewMethod, + /* 80 */ (void (*)(void))0, + /* 81 */ (void (*)(void))0, + /* 82 */ (void (*)(void))0, + /* 83 */ primitivePerform, + /* 84 */ primitivePerformWithArgs, + /* 85 */ primitiveSignal, + /* 86 */ primitiveWait, + /* 87 */ primitiveResume, + /* 88 */ primitiveSuspend, + /* 89 */ primitiveFlushCache, + /* 90 */ (void (*)(void))0, + /* 91 */ primitiveTestDisplayDepth, + /* 92 */ primitiveSetDisplayMode, + /* 93 */ primitiveInputSemaphore, + /* 94 */ primitiveGetNextEvent, + /* 95 */ primitiveInputWord, + /* 96 */ primitiveObjectsAccessibleFromRoots, + /* 97 */ primitiveSnapshot, + /* 98 */ primitiveStoreImageSegment, + /* 99 */ primitiveLoadImageSegment, + /* 100 */ primitivePerformInSuperclass, + /* 101 */ primitiveBeCursor, + /* 102 */ primitiveBeDisplay, + /* 103 */ primitiveScanCharacters, + /* 104 */ (void (*)(void))0, + /* 105 */ primitiveStringReplace, + /* 106 */ primitiveScreenSize, + /* 107 */ (void (*)(void))0, + /* 108 */ (void (*)(void))0, + /* 109 */ (void (*)(void))0, + /* 110 */ primitiveIdentical, + /* 111 */ primitiveClass, + /* 112 */ primitiveBytesLeft, + /* 113 */ primitiveQuit, + /* 114 */ primitiveExitToDebugger, + /* 115 */ primitiveChangeClass, + /* 116 */ primitiveFlushCacheByMethod, + /* 117 */ primitiveExternalCall, + /* 118 */ primitiveDoPrimitiveWithArgs, + /* 119 */ primitiveFlushCacheBySelector, + /* 120 */ primitiveCalloutToFFI, + /* 121 */ primitiveImageName, + /* 122 */ primitiveNoop, + /* 123 */ (void (*)(void))0, + /* 124 */ primitiveLowSpaceSemaphore, + /* 125 */ primitiveSignalAtBytesLeft, + /* 126 */ primitiveDeferDisplayUpdates, + /* 127 */ primitiveShowDisplayRect, + /* 128 */ primitiveArrayBecome, + /* 129 */ primitiveSpecialObjectsOop, + /* 130 */ primitiveFullGC, + /* 131 */ primitiveIncrementalGC, + /* 132 */ primitiveObjectPointsTo, + /* 133 */ primitiveSetInterruptKey, + /* 134 */ primitiveInterruptSemaphore, + /* 135 */ primitiveMillisecondClock, + /* 136 */ primitiveSignalAtMilliseconds, + /* 137 */ primitiveSecondsClock, + /* 138 */ primitiveSomeObject, + /* 139 */ primitiveNextObject, + /* 140 */ primitiveBeep, + /* 141 */ primitiveClipboardText, + /* 142 */ primitiveVMPath, + /* 143 */ primitiveShortAt, + /* 144 */ primitiveShortAtPut, + /* 145 */ primitiveConstantFill, + /* 146 */ (void (*)(void))0, + /* 147 */ (void (*)(void))0, + /* 148 */ primitiveClone, + /* 149 */ primitiveGetAttribute, + /* 150 */ primitiveCosine, + /* 151 */ (void (*)(void))0, + /* 152 */ (void (*)(void))0, + /* 153 */ (void (*)(void))0, + /* 154 */ (void (*)(void))0, + /* 155 */ (void (*)(void))0, + /* 156 */ (void (*)(void))0, + /* 157 */ (void (*)(void))0, + /* 158 */ primitiveCompareWith, + /* 159 */ primitiveHashMultiply, + /* 160 */ primitiveAdoptInstance, + /* 161 */ primitiveSetOrHasIdentityHash, + /* 162 */ (void (*)(void))0, + /* 163 */ primitiveGetImmutability, + /* 164 */ primitiveSetImmutability, + /* 165 */ primitiveIntegerAt, + /* 166 */ primitiveIntegerAtPut, + /* 167 */ primitiveYield, + /* 168 */ primitiveCopyObject, + /* 169 */ primitiveNotIdentical, + /* 170 */ primitiveAsCharacter, + /* 171 */ primitiveImmediateAsInteger, + /* 172 */ primitiveFetchNextMourner, + /* 173 */ primitiveSlotAt, + /* 174 */ primitiveSlotAtPut, + /* 175 */ primitiveBehaviorHash, + /* 176 */ primitiveMaxIdentityHash, + /* 177 */ primitiveAllInstances, + /* 178 */ primitiveAllObjects, + /* 179 */ (void (*)(void))0, + /* 180 */ primitiveGrowMemoryByAtLeast, + /* 181 */ primitiveSizeInBytesOfInstance, + /* 182 */ primitiveSizeInBytes, + /* 183 */ primitiveIsPinned, + /* 184 */ primitivePin, + /* 185 */ primitiveExitCriticalSection, + /* 186 */ primitiveEnterCriticalSection, + /* 187 */ primitiveTestAndSetOwnershipOfCriticalSection, + /* 188 */ primitiveExecuteMethodArgsArray, + /* 189 */ primitiveExecuteMethod, + /* 190 */ (void (*)(void))0, + /* 191 */ (void (*)(void))0, + /* 192 */ (void (*)(void))0, + /* 193 */ (void (*)(void))0, + /* 194 */ (void (*)(void))0, + /* 195 */ primitiveFindNextUnwindContext, + /* 196 */ primitiveTerminateTo, + /* 197 */ primitiveFindHandlerContext, + /* 198 */ (void (*)(void))0, + /* 199 */ (void (*)(void))0, + /* 200 */ primitiveClosureCopyWithCopiedValues, + /* 201 */ primitiveClosureValue, + /* 202 */ primitiveClosureValue, + /* 203 */ primitiveClosureValue, + /* 204 */ primitiveClosureValue, + /* 205 */ primitiveClosureValue, + /* 206 */ primitiveClosureValueWithArgs, + /* 207 */ primitiveFullClosureValue, + /* 208 */ primitiveFullClosureValueWithArgs, + /* 209 */ primitiveFullClosureValueNoContextSwitch, + /* 210 */ primitiveContextAt, + /* 211 */ primitiveContextAtPut, + /* 212 */ primitiveContextSize, + /* 213 */ primitiveContextPreserveTemps, + /* 214 */ primitiveVoidVMState, + /* 215 */ primitiveVoidVMStateForMethod, + /* 216 */ primitiveMethodXray, + /* 217 */ (void (*)(void))0, + /* 218 */ primitiveDoNamedPrimitiveWithArgs, + /* 219 */ (void (*)(void))0, + /* 220 */ primitiveGetSetFFIExceptionHandling, + /* 221 */ primitiveClosureValueNoContextSwitch, + /* 222 */ primitiveClosureValueNoContextSwitch, + /* 223 */ (void (*)(void))0, + /* 224 */ (void (*)(void))0, + /* 225 */ (void (*)(void))0, + /* 226 */ primitiveGetOwnerLog, + /* 227 */ primitiveVMCurrentThreadId, + /* 228 */ primitiveProcessBoundThreadId, + /* 229 */ primitiveProcessBindToThreadAffinity, + /* 230 */ primitiveRelinquishProcessor, + /* 231 */ primitiveForceDisplayUpdate, + /* 232 */ primitiveFormPrint, + /* 233 */ primitiveSetFullScreen, + /* 234 */ (void (*)(void))0, + /* 235 */ (void (*)(void))0, + /* 236 */ (void (*)(void))0, + /* 237 */ (void (*)(void))0, + /* 238 */ primitiveFloatArrayAt, + /* 239 */ primitiveFloatArrayAtPut, + /* 240 */ primitiveUTCMicrosecondClock, + /* 241 */ primitiveLocalMicrosecondClock, + /* 242 */ primitiveSignalAtUTCMicroseconds, + /* 243 */ primitiveUpdateTimezone, + /* 244 */ primitiveUtcAndTimezoneOffset, + /* 245 */ primitiveCoarseUTCMicrosecondClock, + /* 246 */ primitiveCoarseLocalMicrosecondClock, + /* 247 */ primitiveSnapshotEmbedded, + /* 248 */ primitiveArrayBecomeOneWayNoCopyHash, + /* 249 */ primitiveArrayBecomeOneWayCopyHashArg, + /* 250 */ primitiveClearVMProfile, + /* 251 */ primitiveControlVMProfiling, + /* 252 */ primitiveVMProfileSamplesInto, + /* 253 */ primitiveCollectCogCodeConstituents, + /* 254 */ primitiveVMParameter, + /* 255 */ (void (*)(void))0, + /* 256 */ (void (*)(void))256, + /* 257 */ (void (*)(void))257, + /* 258 */ (void (*)(void))258, + /* 259 */ (void (*)(void))259, + /* 260 */ (void (*)(void))260, + /* 261 */ (void (*)(void))261, + /* 262 */ (void (*)(void))262, + /* 263 */ (void (*)(void))263, + /* 264 */ (void (*)(void))264, + /* 265 */ (void (*)(void))265, + /* 266 */ (void (*)(void))266, + /* 267 */ (void (*)(void))267, + /* 268 */ (void (*)(void))268, + /* 269 */ (void (*)(void))269, + /* 270 */ (void (*)(void))270, + /* 271 */ (void (*)(void))271, + /* 272 */ (void (*)(void))272, + /* 273 */ (void (*)(void))273, + /* 274 */ (void (*)(void))274, + /* 275 */ (void (*)(void))275, + /* 276 */ (void (*)(void))276, + /* 277 */ (void (*)(void))277, + /* 278 */ (void (*)(void))278, + /* 279 */ (void (*)(void))279, + /* 280 */ (void (*)(void))280, + /* 281 */ (void (*)(void))281, + /* 282 */ (void (*)(void))282, + /* 283 */ (void (*)(void))283, + /* 284 */ (void (*)(void))284, + /* 285 */ (void (*)(void))285, + /* 286 */ (void (*)(void))286, + /* 287 */ (void (*)(void))287, + /* 288 */ (void (*)(void))288, + /* 289 */ (void (*)(void))289, + /* 290 */ (void (*)(void))290, + /* 291 */ (void (*)(void))291, + /* 292 */ (void (*)(void))292, + /* 293 */ (void (*)(void))293, + /* 294 */ (void (*)(void))294, + /* 295 */ (void (*)(void))295, + /* 296 */ (void (*)(void))296, + /* 297 */ (void (*)(void))297, + /* 298 */ (void (*)(void))298, + /* 299 */ (void (*)(void))299, + /* 300 */ (void (*)(void))300, + /* 301 */ (void (*)(void))301, + /* 302 */ (void (*)(void))302, + /* 303 */ (void (*)(void))303, + /* 304 */ (void (*)(void))304, + /* 305 */ (void (*)(void))305, + /* 306 */ (void (*)(void))306, + /* 307 */ (void (*)(void))307, + /* 308 */ (void (*)(void))308, + /* 309 */ (void (*)(void))309, + /* 310 */ (void (*)(void))310, + /* 311 */ (void (*)(void))311, + /* 312 */ (void (*)(void))312, + /* 313 */ (void (*)(void))313, + /* 314 */ (void (*)(void))314, + /* 315 */ (void (*)(void))315, + /* 316 */ (void (*)(void))316, + /* 317 */ (void (*)(void))317, + /* 318 */ (void (*)(void))318, + /* 319 */ (void (*)(void))319, + /* 320 */ (void (*)(void))320, + /* 321 */ (void (*)(void))321, + /* 322 */ (void (*)(void))322, + /* 323 */ (void (*)(void))323, + /* 324 */ (void (*)(void))324, + /* 325 */ (void (*)(void))325, + /* 326 */ (void (*)(void))326, + /* 327 */ (void (*)(void))327, + /* 328 */ (void (*)(void))328, + /* 329 */ (void (*)(void))329, + /* 330 */ (void (*)(void))330, + /* 331 */ (void (*)(void))331, + /* 332 */ (void (*)(void))332, + /* 333 */ (void (*)(void))333, + /* 334 */ (void (*)(void))334, + /* 335 */ (void (*)(void))335, + /* 336 */ (void (*)(void))336, + /* 337 */ (void (*)(void))337, + /* 338 */ (void (*)(void))338, + /* 339 */ (void (*)(void))339, + /* 340 */ (void (*)(void))340, + /* 341 */ (void (*)(void))341, + /* 342 */ (void (*)(void))342, + /* 343 */ (void (*)(void))343, + /* 344 */ (void (*)(void))344, + /* 345 */ (void (*)(void))345, + /* 346 */ (void (*)(void))346, + /* 347 */ (void (*)(void))347, + /* 348 */ (void (*)(void))348, + /* 349 */ (void (*)(void))349, + /* 350 */ (void (*)(void))350, + /* 351 */ (void (*)(void))351, + /* 352 */ (void (*)(void))352, + /* 353 */ (void (*)(void))353, + /* 354 */ (void (*)(void))354, + /* 355 */ (void (*)(void))355, + /* 356 */ (void (*)(void))356, + /* 357 */ (void (*)(void))357, + /* 358 */ (void (*)(void))358, + /* 359 */ (void (*)(void))359, + /* 360 */ (void (*)(void))360, + /* 361 */ (void (*)(void))361, + /* 362 */ (void (*)(void))362, + /* 363 */ (void (*)(void))363, + /* 364 */ (void (*)(void))364, + /* 365 */ (void (*)(void))365, + /* 366 */ (void (*)(void))366, + /* 367 */ (void (*)(void))367, + /* 368 */ (void (*)(void))368, + /* 369 */ (void (*)(void))369, + /* 370 */ (void (*)(void))370, + /* 371 */ (void (*)(void))371, + /* 372 */ (void (*)(void))372, + /* 373 */ (void (*)(void))373, + /* 374 */ (void (*)(void))374, + /* 375 */ (void (*)(void))375, + /* 376 */ (void (*)(void))376, + /* 377 */ (void (*)(void))377, + /* 378 */ (void (*)(void))378, + /* 379 */ (void (*)(void))379, + /* 380 */ (void (*)(void))380, + /* 381 */ (void (*)(void))381, + /* 382 */ (void (*)(void))382, + /* 383 */ (void (*)(void))383, + /* 384 */ (void (*)(void))384, + /* 385 */ (void (*)(void))385, + /* 386 */ (void (*)(void))386, + /* 387 */ (void (*)(void))387, + /* 388 */ (void (*)(void))388, + /* 389 */ (void (*)(void))389, + /* 390 */ (void (*)(void))390, + /* 391 */ (void (*)(void))391, + /* 392 */ (void (*)(void))392, + /* 393 */ (void (*)(void))393, + /* 394 */ (void (*)(void))394, + /* 395 */ (void (*)(void))395, + /* 396 */ (void (*)(void))396, + /* 397 */ (void (*)(void))397, + /* 398 */ (void (*)(void))398, + /* 399 */ (void (*)(void))399, + /* 400 */ (void (*)(void))400, + /* 401 */ (void (*)(void))401, + /* 402 */ (void (*)(void))402, + /* 403 */ (void (*)(void))403, + /* 404 */ (void (*)(void))404, + /* 405 */ (void (*)(void))405, + /* 406 */ (void (*)(void))406, + /* 407 */ (void (*)(void))407, + /* 408 */ (void (*)(void))408, + /* 409 */ (void (*)(void))409, + /* 410 */ (void (*)(void))410, + /* 411 */ (void (*)(void))411, + /* 412 */ (void (*)(void))412, + /* 413 */ (void (*)(void))413, + /* 414 */ (void (*)(void))414, + /* 415 */ (void (*)(void))415, + /* 416 */ (void (*)(void))416, + /* 417 */ (void (*)(void))417, + /* 418 */ (void (*)(void))418, + /* 419 */ (void (*)(void))419, + /* 420 */ (void (*)(void))420, + /* 421 */ (void (*)(void))421, + /* 422 */ (void (*)(void))422, + /* 423 */ (void (*)(void))423, + /* 424 */ (void (*)(void))424, + /* 425 */ (void (*)(void))425, + /* 426 */ (void (*)(void))426, + /* 427 */ (void (*)(void))427, + /* 428 */ (void (*)(void))428, + /* 429 */ (void (*)(void))429, + /* 430 */ (void (*)(void))430, + /* 431 */ (void (*)(void))431, + /* 432 */ (void (*)(void))432, + /* 433 */ (void (*)(void))433, + /* 434 */ (void (*)(void))434, + /* 435 */ (void (*)(void))435, + /* 436 */ (void (*)(void))436, + /* 437 */ (void (*)(void))437, + /* 438 */ (void (*)(void))438, + /* 439 */ (void (*)(void))439, + /* 440 */ (void (*)(void))440, + /* 441 */ (void (*)(void))441, + /* 442 */ (void (*)(void))442, + /* 443 */ (void (*)(void))443, + /* 444 */ (void (*)(void))444, + /* 445 */ (void (*)(void))445, + /* 446 */ (void (*)(void))446, + /* 447 */ (void (*)(void))447, + /* 448 */ (void (*)(void))448, + /* 449 */ (void (*)(void))449, + /* 450 */ (void (*)(void))450, + /* 451 */ (void (*)(void))451, + /* 452 */ (void (*)(void))452, + /* 453 */ (void (*)(void))453, + /* 454 */ (void (*)(void))454, + /* 455 */ (void (*)(void))455, + /* 456 */ (void (*)(void))456, + /* 457 */ (void (*)(void))457, + /* 458 */ (void (*)(void))458, + /* 459 */ (void (*)(void))459, + /* 460 */ (void (*)(void))460, + /* 461 */ (void (*)(void))461, + /* 462 */ (void (*)(void))462, + /* 463 */ (void (*)(void))463, + /* 464 */ (void (*)(void))464, + /* 465 */ (void (*)(void))465, + /* 466 */ (void (*)(void))466, + /* 467 */ (void (*)(void))467, + /* 468 */ (void (*)(void))468, + /* 469 */ (void (*)(void))469, + /* 470 */ (void (*)(void))470, + /* 471 */ (void (*)(void))471, + /* 472 */ (void (*)(void))472, + /* 473 */ (void (*)(void))473, + /* 474 */ (void (*)(void))474, + /* 475 */ (void (*)(void))475, + /* 476 */ (void (*)(void))476, + /* 477 */ (void (*)(void))477, + /* 478 */ (void (*)(void))478, + /* 479 */ (void (*)(void))479, + /* 480 */ (void (*)(void))480, + /* 481 */ (void (*)(void))481, + /* 482 */ (void (*)(void))482, + /* 483 */ (void (*)(void))483, + /* 484 */ (void (*)(void))484, + /* 485 */ (void (*)(void))485, + /* 486 */ (void (*)(void))486, + /* 487 */ (void (*)(void))487, + /* 488 */ (void (*)(void))488, + /* 489 */ (void (*)(void))489, + /* 490 */ (void (*)(void))490, + /* 491 */ (void (*)(void))491, + /* 492 */ (void (*)(void))492, + /* 493 */ (void (*)(void))493, + /* 494 */ (void (*)(void))494, + /* 495 */ (void (*)(void))495, + /* 496 */ (void (*)(void))496, + /* 497 */ (void (*)(void))497, + /* 498 */ (void (*)(void))498, + /* 499 */ (void (*)(void))499, + /* 500 */ (void (*)(void))500, + /* 501 */ (void (*)(void))501, + /* 502 */ (void (*)(void))502, + /* 503 */ (void (*)(void))503, + /* 504 */ (void (*)(void))504, + /* 505 */ (void (*)(void))505, + /* 506 */ (void (*)(void))506, + /* 507 */ (void (*)(void))507, + /* 508 */ (void (*)(void))508, + /* 509 */ (void (*)(void))509, + /* 510 */ (void (*)(void))510, + /* 511 */ (void (*)(void))511, + /* 512 */ (void (*)(void))512, + /* 513 */ (void (*)(void))513, + /* 514 */ (void (*)(void))514, + /* 515 */ (void (*)(void))515, + /* 516 */ (void (*)(void))516, + /* 517 */ (void (*)(void))517, + /* 518 */ (void (*)(void))518, + /* 519 */ (void (*)(void))519, + /* 520 */ (void (*)(void))0, + /* 521 */ (void (*)(void))0, + /* 522 */ (void (*)(void))0, + /* 523 */ (void (*)(void))0, + /* 524 */ (void (*)(void))0, + /* 525 */ (void (*)(void))0, + /* 526 */ (void (*)(void))0, + /* 527 */ (void (*)(void))0, + /* 528 */ (void (*)(void))0, + /* 529 */ (void (*)(void))0, + /* 530 */ (void (*)(void))0, + /* 531 */ (void (*)(void))0, + /* 532 */ (void (*)(void))0, + /* 533 */ (void (*)(void))0, + /* 534 */ (void (*)(void))0, + /* 535 */ (void (*)(void))0, + /* 536 */ (void (*)(void))0, + /* 537 */ (void (*)(void))0, + /* 538 */ (void (*)(void))0, + /* 539 */ (void (*)(void))0, + /* 540 */ (void (*)(void))0, + /* 541 */ primitiveSmallFloatAdd, + /* 542 */ primitiveSmallFloatSubtract, + /* 543 */ primitiveSmallFloatLessThan, + /* 544 */ primitiveSmallFloatGreaterThan, + /* 545 */ primitiveSmallFloatLessOrEqual, + /* 546 */ primitiveSmallFloatGreaterOrEqual, + /* 547 */ primitiveSmallFloatEqual, + /* 548 */ primitiveSmallFloatNotEqual, + /* 549 */ primitiveSmallFloatMultiply, + /* 550 */ primitiveSmallFloatDivide, + /* 551 */ primitiveSmallFloatTruncated, + /* 552 */ primitiveSmallFloatFractionalPart, + /* 553 */ primitiveSmallFloatExponent, + /* 554 */ primitiveSmallFloatTimesTwoPower, + /* 555 */ primitiveSmallFloatSquareRoot, + /* 556 */ primitiveSmallFloatSine, + /* 557 */ primitiveSmallFloatArctan, + /* 558 */ primitiveSmallFloatLogN, + /* 559 */ primitiveSmallFloatExp, + /* 560 */ primitiveSmallFloatCosine, + /* 561 */ (void (*)(void))0, + /* 562 */ (void (*)(void))0, + /* 563 */ (void (*)(void))0, + /* 564 */ (void (*)(void))0, + /* 565 */ (void (*)(void))0, + /* 566 */ (void (*)(void))0, + /* 567 */ primitiveSetPriority, + /* 568 */ primitiveSuspendBackingUpV1, + /* 569 */ (void (*)(void))0, + /* 570 */ primitiveFlushExternalPrimitives, + /* 571 */ primitiveUnloadModule, + /* 572 */ primitiveListBuiltinModule, + /* 573 */ primitiveListExternalModule, + /* 574 */ (void (*)(void))0, + /* 575 */ primitiveHighBit, + /* 576 */ (void (*)(void))0, + /* 577 */ (void (*)(void))0, + /* 578 */ primitiveSuspendBackingUpV2, + /* 579 */ (void (*)(void))0, + /* 580 */ primitivePinnedNew, + /* 581 */ primitivePinnedNewWithArg, + /* 582 */ primitiveUninitializedNewWithArg, + /* 583 */ primitiveUninitializedPinnedNewWithArg, + 0 }; +static void (*externalPrimitiveTable[MaxExternalPrimitiveTableSize + 1 /* 4097 */])(void); +static signed short primitiveMetadataTable[MaxPrimitiveIndex + 2 /* 585 */] = { +// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 +/*0*/ -256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-256, +/*20*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x101, 0x101, +/*40*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 3, 3, 3, 3, +/*60*/ 1, 1, 0, 1, 1,-256,-256,-256, 0x100, 0x100, 0, 0, 0, 268, 0x100, 0, 0x100, 0, +/*78*/ 0, 0, +/*80*/ -256,-256,-256, 4, 4, 0, 0x100, 0, 0x200,-256,-256, 0, 0, 0, 0x100,-256, 0,-256, +/*98*/ 0, 0, +/*100*/ 260, 0x200, 0x200, 0x200,-256, 513,-256,-256,-256,-256, 0, 0x100, 0, 0,-256, +/*115*/ 0x100, 0, 12, 260, 0, +/*120*/ 524, 0x100,-256,-256, 1, 0, 0, 0, 0,-255,-256, 0, 0, 0, 0,-256, 0,-256,-256, +/*139*/ 0, +/*140*/ -256, 0x100,-256, 0x101, 0x101, 0x101,-256,-256, 12, 0, 3,-256,-256,-256,-256, +/*155*/ -256,-256,-256, 0x100, 0x100, +/*160*/ 0, 0,-256, 1, 1, 0x100, 0x100,-256, 0x101, 0, 0, 0,-256, 268, 0x100, 0,-256, +/*177*/ 0,-256,-256, +/*180*/ 0, 1, 1, 1, 0x100, 0x100, 0x100, 0x100, 0x200, 0x100,-256,-256,-256,-256,-256, +/*195*/ 0x100, 0x200, 0,-256,-256, +/*200*/ -256, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x100, 0x100, 0x100, 0x100, 0x100, +/*212*/ 0x100, 0,-256, 0, 0,-256, 772,-256, +/*220*/ 0, 0x200, 0x200,-256,-256,-256, 0x100,-256, 0, 0x100, 0,-256, 0x200, 0,-256, +/*235*/ -256,-256,-256,-253,-253, +/*240*/ -256,-256, 0,-256, 0x100,-256,-256,-256, 0, 0,-256, 0, 0x100, 0, 0,-256,-256, +/*257*/ -256,-256,-256, +/*260*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*276*/ -256,-256,-256,-256, +/*280*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*296*/ -256,-256,-256,-256, +/*300*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*316*/ -256,-256,-256,-256, +/*320*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*336*/ -256,-256,-256,-256, +/*340*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*356*/ -256,-256,-256,-256, +/*360*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*376*/ -256,-256,-256,-256, +/*380*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*396*/ -256,-256,-256,-256, +/*400*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*416*/ -256,-256,-256,-256, +/*420*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*436*/ -256,-256,-256,-256, +/*440*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*456*/ -256,-256,-256,-256, +/*460*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*476*/ -256,-256,-256,-256, +/*480*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*496*/ -256,-256,-256,-256, +/*500*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*516*/ -256,-256,-256,-256, +/*520*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*536*/ -256,-256,-256,-256, +/*540*/ -256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 1, 3, 3, 3, 3, 3, 3, +/*560*/ 3,-256,-256,-256,-256,-256,-256, 0x100, 0x200,-256,-244, 0x100, 0, 0,-256, 1, +/*576*/ -256,-256, 0x200,-256, +/*580*/ 0, 0, 0, 0 + }; +static char * traceSources[] = { + "?", "enterCritical", "exitCritical", "resume", "syncsig", "suspend", "wait", + "yield", "priority", "eventcheck", "threadsched", "ownVM", "bindToThread", "switchIfNecessary", + "tryToExecuteSmalltalk" + }; +sqInt imageHeaderFlags; +sqInt checkForLeaks; +sqInt maxLiteralCountForCompile = MaxLiteralCountForCompile /* 60 */; +sqInt debugCallbackPath; +sqInt breakLookupClassTag; +char * primTracePluginName; +sqInt breakSelectorLength = MinSmallInteger; +void * displayBits; +sqInt desiredEdenBytes; +sqInt desiredNumStackPages; +sqInt extraVMMemory; +sqInt eventTraceMask; +usqInt maxOldSpaceSize; +sqInt desiredCogCodeSize; +char * breakSelector; +sqInt disownCount; +sqInt ffiExceptionResponse; +usqInt heapBase; +sqInt inIOProcessEvents; +int (*showSurfaceFn)(sqIntptr_t, int, int, int, int); +sqInt deferDisplayUpdates; +struct VirtualMachine* interpreterProxy; +sqInt debugCallbackInvokes; +sqInt debugCallbackReturns; +sqInt suppressHeartbeatFlag; +sqInt cannotDeferDisplayUpdates; +sqInt enableOwnerLog; +sqInt checkedPluginName; +const char *interpreterVersion = "Open Smalltalk Cog MT VM [CoInterpreterMT VMMaker.oscog-eem.3776]"; +const char * leakCheckFlagsMeanings[] = { + "1: check full GC", "2: check new space GC", "4: check incremental GC", "8: check become", + "16: check image segment", "32: check free space", "64: check shorten", "128: check prim call", + null + }; +sqInt minBackwardJumpCountForCompile = MinBackwardJumpCountForCompile /* 40 */; +int displayWidth; +int displayDepth; +int displayHeight; +char expensiveAsserts = 0; +volatile int sendTrace; + + +/*** Macros ***/ +#define cPICNumCases stackCheckOffset +#define cPICNumCasesHack hack hack hack i.e. the getter macro does all the work +#define nextOpenPIC methodObject +#define nextOpenPICHack hack hack hack i.e. the getter macro does all the work +#define mostRecentlyUsedPage() GIV(mostRecentlyUsedPage) +#define stackPageAtpages(index,pages) ((pages) + (index)) +#define statMaxPageCountWhenMapping() GIV(statMaxPageCountWhenMapping) +#define assertValidProcessorStackPointersForIndex(ignored) 0 +#define getNumThreads() GIV(numThreads) +#define ownerLog() GIV(ownerLog) +#define ownerLogIndex() GIV(ownerLogIndex) +#define compilationBreakpointclassTagisMNUCase(sel, classTag, isMNU) do { \ + if (numBytesOf(sel) == (isMNU ? -breakSelectorLength : breakSelectorLength) \ + && !strncmp((char *)((sel) + BaseHeaderSize), breakSelector, (isMNU ? -breakSelectorLength : breakSelectorLength))) { \ + suppressHeartbeatFlag = 1; \ + compilationBreakpointFor(sel); \ + } \ +} while (0) +#define compilationBreakpointisMNUCase(sel, isMNU) do { \ + if (numBytesOf(sel) == (isMNU ? -breakSelectorLength : breakSelectorLength) \ + && !strncmp((char *)((sel) + BaseHeaderSize), breakSelector, (isMNU ? -breakSelectorLength : breakSelectorLength))) { \ + suppressHeartbeatFlag = 1; \ + compilationBreakpointFor(sel); \ + } \ +} while (0) +#define flagInterpretedMethods() (imageHeaderFlags & 8) +#define getDesiredCogCodeSize() integerObjectOf(desiredCogCodeSize) +#define heapBase() heapBase +#define assertCStackPointersBelongToCurrentThread(ignored) 0 +#define assertCStackPointersBelongToDisowningThread() 0 +#define assertProcessorStackPointersBelongToCurrentThread(ignored) 0 +#define assertValidNewMethodPropertyFlags() 0 +#define disownResultFlags(disownResult) ((disownResult) >> ThreadIdShift) +#define enterSmalltalkExecutive() enterSmalltalkExecutiveImplementation() +#define threadSchedulingLoop(vmThread) threadSchedulingLoopImplementation(vmThread) +#define pageIndexForstackBasePlus1bytesPerPage(pointer,stkBasePlus1,pageByteSize) (((char *)(pointer) - (stkBasePlus1)) / (pageByteSize)) +#define nullHeaderForMachineCodeMethod() 0x8000000A000035LL +#define startOfMemory() heapBase +#define numTagBits() 3 +#define shiftForWord() 3 +#define smallFloatExponentBits() 8 +#define smallFloatExponentOffset() 896 +#define smallFloatMantissaBits() 52 +#define smallFloatTag() 4 +#define tagMask() 0x7 +#define wordSize() 8 +#define arrayClassIndexPun() 16 +#define arrayFormat() 2 +#define characterTag() 2 +#define classIndexMask() 0x3FFFFF +#define classIsItselfClassIndexPun() 31 +#define classTableMajorIndexShift() 10 +#define ephemeronFormat() 5 +#define extraAlignedBitByteShift() 6 +#define extraAlignedBitShift() 22 +#define firstByteFormat() 16 +#define firstClassIndexPun() 16 +#define firstCompiledMethodFormat() 24 +#define firstLongFormat() 10 +#define firstShortFormat() 12 +#define fixedFieldsFieldWidth() 16 +#define formatFieldByteOffset() 3 +#define formatFieldWidthShift() 5 +#define formatMask() 0x1F +#define formatShift() 24 +#define forwardedFormat() 7 +#define greyBitByteShift() 6 +#define greyBitShift() 54 +#define identityHashFullWordShift() 32 +#define identityHashHalfWordMask() 0x3FFFFF +#define immutableBitByteShift() 7 +#define immutableBitShift() 23 +#define immutableExtraBitsByteOffset() 2 +#define indexablePointersFormat() 3 +#define isForwardedObjectClassIndexPun() 8 +#define isFreeObjectClassIndexPun() 0 +#define lastClassIndexPun() 31 +#define markBitsByteOffset() 6 +#define markedBitByteShift() 7 +#define markedBitFullShift() 55 +#define markedBitHalfShift() 23 +#define nonIndexablePointerFormat() 1 +#define numSlotsFieldByteOffset() 7 +#define numSlotsFullShift() 56 +#define numSlotsHalfShift() 24 +#define numSlotsMask() 0xFF +#define pinnedBitByteShift() 6 +#define pinnedBitShift() 30 +#define rememberedBitByteShift() 5 +#define rememberedBitShift() 29 +#define rootTableCapacity() GIV(rememberedSetLimit) +#define rootTableCount() GIV(rememberedSetSize) +#define segmentBridgePun() 3 +#define sixtyFourBitIndexableFormat() 9 +#define sixtyFourBitLongsClassIndexPun() 19 +#define smallIntegerTag() 1 +#define thirtyTwoBitLongsClassIndexPun() 18 +#define unusedBitByteShift() 7 +#define unusedBitShift() 31 +#define weakArrayClassIndexPun() 17 +#define weakArrayFormat() 4 +#define alternateHeaderNumLiteralsMask() 0x7FFF +#define cr() fputc('\n',transcript) +#define dispatchFunctionPointer(aFunctionPointer) (aFunctionPointer)() +#define numericPrimsMixArithmetic() (imageHeaderFlags & 0x100) +#define numericPrimsMixComparison() (imageHeaderFlags & 0x800) +#define primTraceLogIndex(aValue) (GIV(primTraceLogIndex) = (aValue)) +#define printChar(aCharacter) fputc(aCharacter,transcript) +#define remoteIsInstVarAccess() 128 +#define stackPageFrameBytes() (256 * BytesPerWord) +#define allocatype(numElements, elementType) alloca((numElements)*sizeof(elementType)) +#define numElementsIn(anArray) (sizeof(anArray)/sizeof(anArray[0])) +#define oopisGreaterThanOrEqualTo(anOop,otherOop) ((usqInt)(anOop) >= (usqInt)(otherOop)) +#define oopisGreaterThanOrEqualToandLessThanOrEqualTo(anOop,baseOop,limitOop) ((usqInt)(anOop) >= (usqInt)(baseOop) && (usqInt)(anOop) <= (usqInt)(limitOop)) +#define oopisGreaterThanOrEqualToandLessThan(anOop,baseOop,limitOop) ((usqInt)(anOop) >= (usqInt)(baseOop) && (usqInt)(anOop) < (usqInt)(limitOop)) +#define oopisGreaterThan(anOop,otherOop) ((usqInt)(anOop) > (usqInt)(otherOop)) +#define oopisGreaterThanandLessThan(anOop,baseOop,limitOop) ((usqInt)(anOop) > (usqInt)(baseOop) && (usqInt)(anOop) < (usqInt)(limitOop)) +#define oopisLessThanOrEqualTo(anOop,otherOop) ((usqInt)(anOop) <= (usqInt)(otherOop)) +#define oopisLessThan(anOop,otherOop) ((usqInt)(anOop) < (usqInt)(otherOop)) + + +/*** Methods ***/ + + +/* This is the main interpreter loop. + In a pure interpreter it loops forever, fetching and executing bytecodes. + With the Cogit JIT executing code as well, the interpreter is reentered + from machine code + whenever the machine code wants to interpret a method instead of executing + its machine + code. Entry into the interpreter is done via a ''jump call'' in machine + code that uses + CFramePointer and CStackPointer to find the base of the C stack (set in + CoInterpreter>> enterSmalltalkExecutiveImplementation) and substitutes + CReturnAddress as the return + address in the code so it always appears that interpret has been called + from CoInterpreter>>enterSmalltalkExecutiveImplementation, which may be + important to, + for example, C exception handling inside the VM. + + When running in the context of a browser plugin VM the interpreter must + return control + to the browser periodically. This should done only when the state of the + currently running + Squeak thread is safely stored in the object heap. Since this is the case + at the moment + that a check for interrupts is performed, that is when we return to the + browser if it is time + to do so. Interrupt checks happen quite frequently. */ +/* If stacklimit is zero then the stack pages have not been initialized. */ +/* See superclass method comment. + Override to add a variable to cache getReturnAddress (cuz it's a macro) */ + + /* CoInterpreterMT>>#interpret */ +sqInt +interpret(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + sqInt currentBytecode; + sqInt extA; + sqInt extB; + sqInt header; + sqInt lkupClassTag; + char * localFP; + char * localIP; + sqInt localReturnValue; + char * localSP; + sqInt methodHeader; + sqInt numExtB; + sqInt stackPageBytes; + usqIntptr_t stackZoneBytes; + sqInt theRetAddr; + void *theStackMemory; + + +# if !NDEBUG + theRetAddr = getReturnAddress(); +# endif + + if (!GIV(stackLimit)) { + /* begin initStackPagesAndInterpret */ + /* begin ensureInitializeStackZone */ + if (!GIV(pages)) { + stackPageBytes = stackPageByteSize(); + stackZoneBytes = (GIV(numStackPages) * ((sizeof(CogStackPage)) + (stackPageByteSize()))) + BytesPerWord; + theStackMemory = alloca(stackZoneBytes); + memset(theStackMemory, 0, stackZoneBytes); + initializeStacknumSlotspageSize(theStackMemory, stackZoneBytes / BytesPerWord, stackPageBytes / BytesPerWord); + } + + /* Once the stack pages are initialized we can continue to bootstrap the system. */ + loadInitialContext(); + + /* We're ready for the heartbeat (poll interrupt) */ + ioInitHeartbeat(); + initialEnterSmalltalkExecutive(); + return null; + } + assertCStackWellAligned(); + + /* An unchecked write is probably faster, so instead of + CReturnAddress ifNil: + [CReturnAddress := self cCoerceSimple: self getReturnAddress to: #usqIntptr_t] + we have simply */ + assert((!GIV(CReturnAddress)) + || (GIV(CReturnAddress) == (((usqIntptr_t) (getReturnAddress()))))); + GIV(CReturnAddress) = ((usqIntptr_t) (getReturnAddress())); + + /* record entry time when running as a browser plug-in */ + browserPluginInitialiseIfNeeded(); + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + assert(!((GIV(instructionPointer) == (ceReturnToInterpreterPC())))); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 1, __LINE__); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + + /* begin initExtensions */ + extA = (numExtB = (extB = 0)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + while (1) { + /* begin dispatchBytecode */ + bytecodeDispatchDebugHook(); + + VM_LABEL(bytecodeDispatch); + switch (currentBytecode) { + case 0: // pushReceiverVariableBytecode + case 256: // 0 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize))); + } + break; + case 1: // pushReceiverVariableBytecode + case 257: // 1 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode1); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 8 /* (1 << shiftForWord) */))); + } + break; + case 2: // pushReceiverVariableBytecode + case 258: // 2 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode2); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 16 /* (2 << shiftForWord) */))); + } + break; + case 3: // pushReceiverVariableBytecode + case 259: // 3 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode3); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 24 /* (3 << shiftForWord) */))); + } + break; + case 4: // pushReceiverVariableBytecode + case 260: // 4 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode4); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 32 /* (4 << shiftForWord) */))); + } + break; + case 5: // pushReceiverVariableBytecode + case 261: // 5 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode5); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 40 /* (5 << shiftForWord) */))); + } + break; + case 6: // pushReceiverVariableBytecode + case 262: // 6 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode6); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 48 /* (6 << shiftForWord) */))); + } + break; + case 7: // pushReceiverVariableBytecode + case 263: // 7 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode7); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 56 /* (7 << shiftForWord) */))); + } + break; + case 8: // pushReceiverVariableBytecode + case 264: // 8 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode8); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 64 /* (8 << shiftForWord) */))); + } + break; + case 9: // pushReceiverVariableBytecode + case 265: // 9 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode9); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 72 /* (9 << shiftForWord) */))); + } + break; + case 10: // pushReceiverVariableBytecode + case 266: // 10 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode10); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 80 /* (10 << shiftForWord) */))); + } + break; + case 11: // pushReceiverVariableBytecode + case 267: // 11 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode11); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 88 /* (11 << shiftForWord) */))); + } + break; + case 12: // pushReceiverVariableBytecode + case 268: // 12 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode12); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 96 /* (12 << shiftForWord) */))); + } + break; + case 13: // pushReceiverVariableBytecode + case 269: // 13 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode13); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 104 /* (13 << shiftForWord) */))); + } + break; + case 14: // pushReceiverVariableBytecode + case 270: // 14 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode14); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 112 /* (14 << shiftForWord) */))); + } + break; + case 15: // pushReceiverVariableBytecode + case 271: // 15 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode15); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 120 /* (15 << shiftForWord) */))); + } + break; + case 16: // pushTemporaryVariableBytecode + case 320: // 64 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (0 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + (frameNumArgs * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + (frameNumArgs * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 17: // pushTemporaryVariableBytecode + case 321: // 65 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode1); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (1 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 1) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 1) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 18: // pushTemporaryVariableBytecode + case 322: // 66 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode2); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (2 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 2) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 2) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 19: // pushTemporaryVariableBytecode + case 323: // 67 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode3); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (3 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 3) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 3) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 20: // pushTemporaryVariableBytecode + case 324: // 68 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode4); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (4 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 4) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 4) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 21: // pushTemporaryVariableBytecode + case 325: // 69 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode5); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (5 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 5) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 5) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 22: // pushTemporaryVariableBytecode + case 326: // 70 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode6); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (6 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 6) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 6) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 23: // pushTemporaryVariableBytecode + case 327: // 71 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode7); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (7 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 7) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 7) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 24: // pushTemporaryVariableBytecode + case 328: // 72 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode8); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (8 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 8) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 8) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 25: // pushTemporaryVariableBytecode + case 329: // 73 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode9); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (9 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 9) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 9) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 26: // pushTemporaryVariableBytecode + case 330: // 74 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode10); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (10 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 10) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 10) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 27: // pushTemporaryVariableBytecode + case 331: // 75 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode11); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (11 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 11) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 11) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 28: // pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode12); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (12 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 12) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 12) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 29: // pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode13); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (13 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 13) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 13) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 30: // pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode14); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (14 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 14) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 14) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 31: // pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode15); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (15 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 15) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 15) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + break; + case 32: // pushLiteralConstantBytecode + case 288: // 32 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 8 /* ((0 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 33: // pushLiteralConstantBytecode + case 289: // 33 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode1); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 16 /* ((1 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 34: // pushLiteralConstantBytecode + case 290: // 34 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode2); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 24 /* ((2 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 35: // pushLiteralConstantBytecode + case 291: // 35 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode3); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 32 /* ((3 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 36: // pushLiteralConstantBytecode + case 292: // 36 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode4); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 40 /* ((4 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 37: // pushLiteralConstantBytecode + case 293: // 37 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode5); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 48 /* ((5 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 38: // pushLiteralConstantBytecode + case 294: // 38 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode6); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 56 /* ((6 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 39: // pushLiteralConstantBytecode + case 295: // 39 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode7); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 64 /* ((7 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 40: // pushLiteralConstantBytecode + case 296: // 40 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode8); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 72 /* ((8 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 41: // pushLiteralConstantBytecode + case 297: // 41 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode9); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 80 /* ((9 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 42: // pushLiteralConstantBytecode + case 298: // 42 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode10); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 88 /* ((10 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 43: // pushLiteralConstantBytecode + case 299: // 43 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode11); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 96 /* ((11 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 44: // pushLiteralConstantBytecode + case 300: // 44 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode12); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 104 /* ((12 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 45: // pushLiteralConstantBytecode + case 301: // 45 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode13); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 112 /* ((13 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 46: // pushLiteralConstantBytecode + case 302: // 46 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode14); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 120 /* ((14 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 47: // pushLiteralConstantBytecode + case 303: // 47 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode15); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 128 /* ((15 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 48: // pushLiteralConstantBytecode + case 304: // 48 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode16); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 136 /* ((16 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 49: // pushLiteralConstantBytecode + case 305: // 49 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode17); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 144 /* ((17 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 50: // pushLiteralConstantBytecode + case 306: // 50 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode18); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 152 /* ((18 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 51: // pushLiteralConstantBytecode + case 307: // 51 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode19); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 160 /* ((19 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 52: // pushLiteralConstantBytecode + case 308: // 52 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode20); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 168 /* ((20 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 53: // pushLiteralConstantBytecode + case 309: // 53 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode21); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 176 /* ((21 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 54: // pushLiteralConstantBytecode + case 310: // 54 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode22); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 184 /* ((22 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 55: // pushLiteralConstantBytecode + case 311: // 55 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode23); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 192 /* ((23 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 56: // pushLiteralConstantBytecode + case 312: // 56 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode24); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 200 /* ((24 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 57: // pushLiteralConstantBytecode + case 313: // 57 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode25); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 208 /* ((25 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 58: // pushLiteralConstantBytecode + case 314: // 58 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode26); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 216 /* ((26 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 59: // pushLiteralConstantBytecode + case 315: // 59 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode27); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 224 /* ((27 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 60: // pushLiteralConstantBytecode + case 316: // 60 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode28); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 232 /* ((28 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 61: // pushLiteralConstantBytecode + case 317: // 61 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode29); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 240 /* ((29 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 62: // pushLiteralConstantBytecode + case 318: // 62 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode30); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 0xF8 /* ((30 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 63: // pushLiteralConstantBytecode + case 319: // 63 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode31); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 0x100 /* ((31 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 64: // pushLiteralVariableBytecode + case 272: // 16 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 8 /* ((0 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(1 /* (0 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 65: // pushLiteralVariableBytecode + case 273: // 17 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode1); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 16 /* ((1 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(2 /* (1 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 66: // pushLiteralVariableBytecode + case 274: // 18 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode2); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 24 /* ((2 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(3 /* (2 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 67: // pushLiteralVariableBytecode + case 275: // 19 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode3); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 32 /* ((3 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(4 /* (3 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 68: // pushLiteralVariableBytecode + case 276: // 20 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode4); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 40 /* ((4 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(5 /* (4 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 69: // pushLiteralVariableBytecode + case 277: // 21 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode5); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 48 /* ((5 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(6 /* (5 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 70: // pushLiteralVariableBytecode + case 278: // 22 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode6); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 56 /* ((6 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(7 /* (6 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 71: // pushLiteralVariableBytecode + case 279: // 23 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode7); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 64 /* ((7 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(8 /* (7 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 72: // pushLiteralVariableBytecode + case 280: // 24 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode8); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 72 /* ((8 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(9 /* (8 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 73: // pushLiteralVariableBytecode + case 281: // 25 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode9); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 80 /* ((9 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(10 /* (9 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 74: // pushLiteralVariableBytecode + case 282: // 26 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode10); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 88 /* ((10 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(11 /* (10 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 75: // pushLiteralVariableBytecode + case 283: // 27 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode11); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 96 /* ((11 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(12 /* (11 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 76: // pushLiteralVariableBytecode + case 284: // 28 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode12); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 104 /* ((12 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(13 /* (12 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 77: // pushLiteralVariableBytecode + case 285: // 29 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode13); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 112 /* ((13 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(14 /* (13 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 78: // pushLiteralVariableBytecode + case 286: // 30 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode14); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 120 /* ((14 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(15 /* (14 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 79: // pushLiteralVariableBytecode + case 287: // 31 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode15); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 128 /* ((15 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(16 /* (15 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 80: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode16); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 136 /* ((16 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(17 /* (16 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 81: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode17); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 144 /* ((17 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(18 /* (17 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 82: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode18); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 152 /* ((18 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(19 /* (18 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 83: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode19); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 160 /* ((19 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(20 /* (19 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 84: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode20); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 168 /* ((20 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(21 /* (20 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 85: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode21); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 176 /* ((21 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(22 /* (21 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 86: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode22); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 184 /* ((22 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(23 /* (22 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 87: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode23); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 192 /* ((23 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(24 /* (23 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 88: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode24); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 200 /* ((24 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(25 /* (24 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 89: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode25); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 208 /* ((25 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(26 /* (25 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 90: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode26); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 216 /* ((26 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(27 /* (26 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 91: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode27); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 224 /* ((27 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(28 /* (27 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 92: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode28); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 232 /* ((28 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(29 /* (28 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 93: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode29); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 240 /* ((29 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(30 /* (29 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 94: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode30); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 0xF8 /* ((30 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(0x1F /* (30 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 95: // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode31); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 0x100 /* ((31 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(32 /* (31 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + break; + case 96: // storeAndPopReceiverVariableBytecode + case 97: // storeAndPopReceiverVariableBytecode + case 98: // storeAndPopReceiverVariableBytecode + case 99: // storeAndPopReceiverVariableBytecode + case 100: // storeAndPopReceiverVariableBytecode + case 101: // storeAndPopReceiverVariableBytecode + case 102: // storeAndPopReceiverVariableBytecode + case 103: // storeAndPopReceiverVariableBytecode + case 456: // 200 storeAndPopReceiverVariableBytecode + case 457: // 201 storeAndPopReceiverVariableBytecode + case 458: // 202 storeAndPopReceiverVariableBytecode + case 459: // 203 storeAndPopReceiverVariableBytecode + case 460: // 204 storeAndPopReceiverVariableBytecode + case 461: // 205 storeAndPopReceiverVariableBytecode + case 462: // 206 storeAndPopReceiverVariableBytecode + case 463: // 207 storeAndPopReceiverVariableBytecode + { + usqInt instVarIndex; + sqInt rcvr; + sqInt top; + + VM_LABEL(storeAndPopReceiverVariableBytecode); + rcvr = longAt(localFP + FoxIFReceiver); + top = longAt(localSP); + instVarIndex = currentBytecode & 7; + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; +# if IMMUTABILITY + assert(!((isImmediate(rcvr)))); + if ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),rcvr); + longAtput((localSP -= BytesPerOop),top); + longAtput((localSP -= BytesPerOop),(((instVarIndex + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l1; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(instVarIndex, rcvr, top)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(top & (tagMask())))) + && (oopisLessThan(top, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((instVarIndex << (shiftForWord())))),top); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l1: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 104: // storeAndPopTemporaryVariableBytecode + case 464: // 208 storeAndPopTemporaryVariableBytecode + { + usqInt frameNumArgs; + + VM_LABEL(storeAndPopTemporaryVariableBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (0 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + (frameNumArgs * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + (frameNumArgs * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + break; + case 105: // storeAndPopTemporaryVariableBytecode + case 465: // 209 storeAndPopTemporaryVariableBytecode + { + usqInt frameNumArgs; + + VM_LABEL(storeAndPopTemporaryVariableBytecode1); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (1 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 1) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 1) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + break; + case 106: // storeAndPopTemporaryVariableBytecode + case 466: // 210 storeAndPopTemporaryVariableBytecode + { + usqInt frameNumArgs; + + VM_LABEL(storeAndPopTemporaryVariableBytecode2); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (2 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 2) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 2) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + break; + case 107: // storeAndPopTemporaryVariableBytecode + case 467: // 211 storeAndPopTemporaryVariableBytecode + { + usqInt frameNumArgs; + + VM_LABEL(storeAndPopTemporaryVariableBytecode3); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (3 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 3) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 3) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + break; + case 108: // storeAndPopTemporaryVariableBytecode + case 468: // 212 storeAndPopTemporaryVariableBytecode + { + usqInt frameNumArgs; + + VM_LABEL(storeAndPopTemporaryVariableBytecode4); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (4 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 4) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 4) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + break; + case 109: // storeAndPopTemporaryVariableBytecode + case 469: // 213 storeAndPopTemporaryVariableBytecode + { + usqInt frameNumArgs; + + VM_LABEL(storeAndPopTemporaryVariableBytecode5); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (5 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 5) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 5) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + break; + case 110: // storeAndPopTemporaryVariableBytecode + case 470: // 214 storeAndPopTemporaryVariableBytecode + { + usqInt frameNumArgs; + + VM_LABEL(storeAndPopTemporaryVariableBytecode6); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (6 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 6) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 6) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + break; + case 111: // storeAndPopTemporaryVariableBytecode + case 471: // 215 storeAndPopTemporaryVariableBytecode + { + usqInt frameNumArgs; + + VM_LABEL(storeAndPopTemporaryVariableBytecode7); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (7 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 7) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 7) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + break; + case 112: // pushReceiverBytecode + case 332: // 76 pushReceiverBytecode + { + VM_LABEL(pushReceiverBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt(localFP + FoxIFReceiver)); + } + break; + case 113: // pushConstantTrueBytecode + case 333: // 77 pushConstantTrueBytecode + { + VM_LABEL(pushConstantTrueBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),GIV(trueObj)); + } + break; + case 114: // pushConstantFalseBytecode + case 334: // 78 pushConstantFalseBytecode + { + VM_LABEL(pushConstantFalseBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),GIV(falseObj)); + } + break; + case 115: // pushConstantNilBytecode + case 335: // 79 pushConstantNilBytecode + { + VM_LABEL(pushConstantNilBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),GIV(nilObj)); + } + break; + case 116: // pushConstantMinusOneBytecode + { + VM_LABEL(pushConstantMinusOneBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),ConstMinusOne); + } + break; + case 117: // pushConstantZeroBytecode + case 336: // 80 pushConstantZeroBytecode + { + VM_LABEL(pushConstantZeroBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),ConstZero); + } + break; + case 118: // pushConstantOneBytecode + case 337: // 81 pushConstantOneBytecode + { + VM_LABEL(pushConstantOneBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),ConstOne); + } + break; + case 119: // pushConstantTwoBytecode + { + VM_LABEL(pushConstantTwoBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),ConstTwo); + } + break; + case 120: // returnReceiver + { + VM_LABEL(returnReceiver); + localReturnValue = longAt(localFP + FoxIFReceiver); + + /* goto commonReturn */ + } + + commonReturn: + /* commonReturn */ + { + sqInt aMethodObj; + char *callerFP; + sqInt closure; + sqInt contextToReturnTo; + sqInt currentCtx; + char *frameToReturnTo; + sqInt header; + sqInt home; + sqInt methodHeader; + StackPage *newPage; + sqInt nextCntx; + sqInt ourContext; + sqInt senderOop; + char *theFP; + char *theFP1; + StackPage *thePage; + StackPage *thePage1; + sqInt unwindContextOrNilOrZero; + + VM_LABEL(commonReturn); + + /* If this is a method simply return to the sender/caller. */ + frameToReturnTo = ((char *) 0); + theFP = ((char *) 0); + if (!(byteAt((localFP + FoxIFrameFlags) + 3))) { + goto commonCallerReturn; + goto l116; + } + + /* Update the current page's headFrame pointers to enable the search for unwind protects below + to identify widowed contexts correctly. */ + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* Since this is a block activation the closure is on the stack above any args and the frame. */ + closure = longAt(localFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(localFP))->cmNumArgs) + : byteAt((localFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))); + + /* avoid compiler warning */ + /* Walk the closure's lexical chain to find the context or frame to return from (home). + If home is missing (Sista closures) then throw cannotReturn rather than crash. */ + home = null; + while (closure != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + home = longAt((void *)((closure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + assert(isNonImmediate(home)); + if ((!((longAt((void *)(home))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + home = fixFollowedFieldofObjectwithInitialValue(ClosureOuterContextIndex, closure, home); + } + if (!(/* isContext: */ + ((!(home & (tagMask())))) + && (((longAt((void *)(home))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* begin internalCannotReturn: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l118; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l118: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),ourContext); + longAtput((localSP -= BytesPerOop),localReturnValue); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))); + GIV(argumentCount) = 1; + goto normalSend; + goto l116; + } + + /* begin followObjField:ofObject: */ + closure = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord())))))); + assert(isNonImmediate(closure)); + if ((!((longAt((void *)(closure))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + closure = fixFollowedFieldofObjectwithInitialValue(ClosureIndex, home, closure); + } + } + + /* home is to be returned from provided there is no unwind-protect activation between + this frame and home's sender. Search for an unwind. findUnwindThroughContext: + will answer either the context for an unwind-protect activation or nilObj if the sender + cannot be found or 0 if no unwind is found but the sender is. */ + unwindContextOrNilOrZero = findUnwindThroughContext(home); + if (unwindContextOrNilOrZero == GIV(nilObj)) { + /* error: can't find home on chain; cannot return */ + + /* begin internalCannotReturn: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l120; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l120: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),ourContext); + longAtput((localSP -= BytesPerOop),localReturnValue); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))); + GIV(argumentCount) = 1; + goto normalSend; + goto l116; + } + if (unwindContextOrNilOrZero) { + /* begin internalAboutToReturn:through: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l121; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l121: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),ourContext); + longAtput((localSP -= BytesPerOop),localReturnValue); + longAtput((localSP -= BytesPerOop),unwindContextOrNilOrZero); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAboutToReturn) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l116; + } + + /* Now we know home is on the sender chain. + We could be returning to either a context or a frame. Find out which. */ + contextToReturnTo = null; + if (((((longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert(checkIsStillMarriedContextcurrentFP(home, localFP)); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (longAt(theFP + FoxSavedFP)) { + frameToReturnTo = ((char *)(longAt(theFP + FoxSavedFP))); + } + else { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + contextToReturnTo = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(contextToReturnTo)); + assert((contextToReturnTo == (nilObject())) + || (isContext(followMaybeForwarded(contextToReturnTo)))); + } + } + else { + contextToReturnTo = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + if ((/* isContext: */ + ((!(contextToReturnTo & (tagMask())))) + && (((longAt((void *)(contextToReturnTo))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + assert(checkIsStillMarriedContextcurrentFP(contextToReturnTo, localFP)); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + frameToReturnTo = ((char *)(senderOop - (smallIntegerTag()))); + contextToReturnTo = null; + } + } + + /* If returning to a context we must make a frame for it unless it is dead. */ + if (contextToReturnTo) { + frameToReturnTo = establishFrameForContextToReturnTo(contextToReturnTo); + if (!frameToReturnTo) { + /* begin internalCannotReturn: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l117; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l117: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),ourContext); + longAtput((localSP -= BytesPerOop),localReturnValue); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))); + GIV(argumentCount) = 1; + goto normalSend; + goto l116; + } + } + + /* Now we have a frame to return to. If it is on a different page we must free intervening pages and + nil out intervening contexts. We must free intervening stack pages because if we leave the pages + to be divorced then their contexts will be divorced with intact senders and instruction pointers. This + code is similar to primitiveTerminateTo. We must move any frames on intervening pages above the + frame linked to because these may be in use, e.g. via co-routining (see baseFrameReturn). */ + assert(pageListIsWellFormed()); + + /* begin stackPageFor: */ + newPage = stackPageAtpages(pageIndexFor(frameToReturnTo), GIV(pages)); + if (newPage != GIV(stackPage)) { + theFP1 = (GIV(stackPage)->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP1)); + thePage1 = stackPageAtpages(pageIndexFor(theFP1), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + freeStackPage(GIV(stackPage)); + while (1) { + assert(isContext(currentCtx)); + if ((((((longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && ((stackPageFor((theFP = frameOfMarriedContext(currentCtx)))) == newPage)) break; + if (((((longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (theFP != ((thePage->headFP))) { + /* Since we've just deallocated a page we know that newStackPage won't deallocate an existing one. */ + moveFramesInthroughtoPage(thePage, findFrameAboveinPage(theFP, thePage), newStackPage()); + } + theFP1 = (thePage->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP1)); + thePage1 = stackPageAtpages(pageIndexFor(theFP1), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + freeStackPage(thePage); + } + else { + nextCntx = longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + + /* begin markContextAsDead: */ + assert(isContext(currentCtx)); + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(SenderIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + currentCtx = nextCntx; + } + } + + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + localSP = (GIV(stackPage)->headSP); + localFP = (GIV(stackPage)->headFP); + } + + /* Two cases. Returning to the top frame on a new page or an interior frame on the current page. + The top frame has its instruction pointer on top of stack. An interior frame has its instruction pointer + in the caller frame. We need to peel back any frames on the page until we get to the correct frame. */ + if (localFP == frameToReturnTo) { + localIP = ((char *)(longAt(localSP))); + } + else { + do { + callerFP = localFP; + localFP = ((char *)(longAt(localFP + FoxSavedFP))); + } while(localFP != frameToReturnTo); + localIP = ((char *)(longAt(callerFP + FoxCallerSavedIP))); + localSP = ((/* begin frameCallerSP: */ + assert(!(isBaseFrame(callerFP))), +(callerFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(callerFP))->cmNumArgs) + : byteAt((callerFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)) - BytesPerWord; + } + + /* pop the saved IP, push the return value and continue. */ + + /* begin maybeReturnToMachineCodeFrame */ + if ((((usqInt)localIP)) < (startOfMemory())) { + if ((((usqInt)localIP)) != (ceReturnToInterpreterPC())) { + /* localIP in the cog method zone indicates a return to machine code. */ + + /* begin returnToMachineCodeFrame */ + assertCStackWellAligned(); + assert((((usqInt)localIP)) < (startOfMemory())); + assert(isMachineCodeFrame(localFP)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 0, __LINE__); + assert(isPCWithinMethodZone(((usqInt)localIP))); + + /* internalStackTopPut: */ + longAtput(localSP,((usqInt)localIP)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),localReturnValue); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + ceEnterCogCodePopReceiverReg(); + goto l119; + } + localIP = ((char *)(((usqInt)(longAt(localFP + FoxIFSavedIP))))); + } + /* end maybeReturnToMachineCodeFrame */ +l119: + aMethodObj = longAt(localFP + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* internalStackTopPut: */ + longAtput(localSP,localReturnValue); + } + /* end case */ +l116:; + break; + case 121: // returnTrue + case 345: // 89 returnTrue + { + VM_LABEL(returnTrue); + localReturnValue = GIV(trueObj); + goto commonReturn; + } + break; + case 122: // returnFalse + case 346: // 90 returnFalse + { + VM_LABEL(returnFalse); + localReturnValue = GIV(falseObj); + goto commonReturn; + } + break; + case 123: // returnNil + case 347: // 91 returnNil + { + VM_LABEL(returnNil); + localReturnValue = GIV(nilObj); + goto commonReturn; + } + break; + case 124: // returnTopFromMethod + case 348: // 92 returnTopFromMethod + { + VM_LABEL(returnTopFromMethod); + localReturnValue = longAt(localSP); + goto commonReturn; + } + break; + case 125: // returnTopFromBlock + { + VM_LABEL(returnTopFromBlock); + localReturnValue = longAt(localSP); + + /* goto commonCallerReturn */ + } + + commonCallerReturn: + /* commonCallerReturn */ + { + sqInt aMethodObj; + char *callerFP; + char *callersFPOrNull; + sqInt contextToReturnFrom; + sqInt contextToReturnTo; + char *fp; + char *frameAbove; + sqInt header; + sqInt methodHeader; + StackPage *newPage; + sqInt retToContext; + sqInt senderOop; + char *theFP; + StackPage *thePage; + StackPage *thePage1; + char *theSP; + + VM_LABEL(commonCallerReturn); + + /* TODO: Store/restore the nativeSP more properly, when it exists */ + callersFPOrNull = ((char *)(longAt(localFP + FoxSavedFP))); + if (!callersFPOrNull) { + assert(localFP == ((GIV(stackPage)->baseFP))); + + /* begin baseFrameReturn */ + /* begin frameCallerContext: */ + assert(isBaseFrame(localFP)); + thePage1 = stackPageAtpages(pageIndexFor(localFP), GIV(pages)); + contextToReturnTo = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(contextToReturnTo)); + assert((contextToReturnTo == (nilObject())) + || (isContext(followMaybeForwarded(contextToReturnTo)))); + + /* The stack page is effectively free now, so free it. We must free it to be + correct in determining if contextToReturnTo is still married, and in case + makeBaseFrameFor: cogs a method, which may cause a code compaction, + in which case the frame must be free to avoid the relocation machinery + tracing the dead frame. Since freeing now temporarily violates the page-list + ordering invariant, use the assert-free version. */ + freeStackPageNoAssert(GIV(stackPage)); + retToContext = /* isContext: */ + ((!(contextToReturnTo & (tagMask())))) + && (((longAt((void *)(contextToReturnTo))) & (classIndexMask())) == ClassMethodContextCompactIndex); + if (retToContext + && (/* isStillMarriedContext: */ + (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(contextToReturnTo))))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (theFP == ((thePage->headFP))) { + theSP = (thePage->headSP); + } + else { + /* Returning to some interior frame, presumably because of a sender assignment. + Move the frames above to another page (they may be in use, e.g. via coroutining). + Make the interior frame the top frame. */ + + /* begin findFrameAbove:inPage: */ + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == theFP) { + frameAbove = 0; + goto l123; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == theFP) { + frameAbove = fp; + goto l123; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + frameAbove = 0; + /* end findFrameAbove:inPage: */ +l123: + + /* Since we've just deallocated a page we know that newStackPage won't deallocate an existing one. */ + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l124; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l124: + assert(newPage == GIV(stackPage)); + moveFramesInthroughtoPage(thePage, frameAbove, newPage); + markStackPageMostRecentlyUsed(newPage); + theFP = (thePage->headFP); + theSP = (thePage->headSP); + } + } + else { + if (!(retToContext + && (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1)))) { + contextToReturnFrom = longAt(((GIV(stackPage)->baseAddress)) - BytesPerWord); + tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFromtoreturnValue(contextToReturnFrom, contextToReturnTo, localReturnValue); + + /* begin externalCannotReturn:from: */ + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),contextToReturnFrom); + longAtput((GIV(stackPointer) -= BytesPerWord),localReturnValue); + + /* Both ceBaseFrameReturnTrampoline & ceNonLocalReturnTrampoline pop + their caller's return pc into instructionPointer. In this uncommon case restore + it, since a send's call pushes the instructionPointer (after the arguments). */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + ((void) (ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), contextToReturnFrom, 1))); + goto l122; + } + + /* We must void the instructionPointer to stop it being updated if makeBaseFrameFor: + cogs a method, which may cause a code compaction. */ + GIV(instructionPointer) = 0; + thePage = makeBaseFrameFor(contextToReturnTo); + theFP = (thePage->headFP); + theSP = (thePage->headSP); + } + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + assert((stackPageFor(theFP)) == GIV(stackPage)); + localSP = theSP; + localFP = theFP; + localIP = ((char *)(longAt(localSP))); + if ((((usqInt)localIP)) < (startOfMemory())) { + if ((((usqInt)localIP)) != (ceReturnToInterpreterPC())) { + /* localIP in the cog method zone indicates a return to machine code. */ + + /* begin returnToMachineCodeFrame */ + assertCStackWellAligned(); + assert((((usqInt)localIP)) < (startOfMemory())); + assert(isMachineCodeFrame(localFP)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 0, __LINE__); + assert(isPCWithinMethodZone(((usqInt)localIP))); + + /* internalStackTopPut: */ + longAtput(localSP,((usqInt)localIP)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),localReturnValue); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + ceEnterCogCodePopReceiverReg(); + goto l122; + } + localIP = ((char *)(((usqInt)(longAt(localFP + FoxIFSavedIP))))); + } + assert(checkIsStillMarriedContextcurrentFP(contextToReturnTo, localFP)); + aMethodObj = longAt(localFP + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* internalStackTopPut: */ + longAtput(localSP,localReturnValue); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l122; + } + + /* baseFrame */ + localIP = ((char *)(longAt(localFP + FoxCallerSavedIP))); + localSP = localFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(localFP))->cmNumArgs) + : byteAt((localFP + FoxIFrameFlags) + 1)))) << (shiftForWord()))))); + localFP = callersFPOrNull; + if ((((usqInt)localIP)) < (startOfMemory())) { + if ((((usqInt)localIP)) != (ceReturnToInterpreterPC())) { + /* localIP in the cog method zone indicates a return to machine code. */ + + /* begin returnToMachineCodeFrame */ + assertCStackWellAligned(); + assert((((usqInt)localIP)) < (startOfMemory())); + assert(isMachineCodeFrame(localFP)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 0, __LINE__); + assert(isPCWithinMethodZone(((usqInt)localIP))); + + /* internalStackTopPut: */ + longAtput(localSP,((usqInt)localIP)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),localReturnValue); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + ceEnterCogCodePopReceiverReg(); + goto l122; + } + localIP = ((char *)(((usqInt)(longAt(localFP + FoxIFSavedIP))))); + } + aMethodObj = longAt(localFP + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* internalStackTopPut: */ + longAtput(localSP,localReturnValue); + } + /* end case */ +l122:; + break; + case 126: // unknownBytecode + case 127: // unknownBytecode + + respondToUnknownBytecode: + /* respondToUnknownBytecode */ + { + usqInt numSlots; + sqInt ourContext; + + VM_LABEL(respondToUnknownBytecode); + GIV(messageSelector) = /* maybeSplObj: */ + (SelectorUnknownBytecode < ((/* begin numSlotsOf: */ + assert((classIndexOf(GIV(specialObjectsOop))) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(GIV(specialObjectsOop) + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(GIV(specialObjectsOop) - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) + ? longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorUnknownBytecode) << (shiftForWord())))))) + : 0); + if ((!GIV(messageSelector)) + || (GIV(messageSelector) == GIV(nilObj))) { + error("Unknown bytecode"); + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l125; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l125: + + /* undo fetch of bytecode so that context's pc is pointing to the unknown bytecode. */ + localIP -= 1; + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),ourContext); + GIV(argumentCount) = 0; + goto normalSend; + } + break; + case 128: // extendedPushBytecode + { + sqInt descriptor; + usqInt frameNumArgs; + sqInt litVar; + sqInt object; + usqInt variableIndex; + usqInt variableType; + + VM_LABEL(extendedPushBytecode); + descriptor = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + variableType = (((usqInt)(descriptor)) >> 6) & 3; + variableIndex = descriptor & 0x3F; + if (!variableType) { + /* begin pushReceiverVariable: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + ((variableIndex << (shiftForWord())))))); + goto l2; + } + if (variableType == 1) { + /* begin pushTemporaryVariable: */ + object = /* itemporary:in: */ + (variableIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - variableIndex) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - variableIndex) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + goto l2; + } + if (variableType == 2) { + /* begin pushLiteralConstant: */ + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + (((variableIndex + LiteralStart) << (shiftForWord()))))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + goto l2; + } + if (variableType == 3) { + /* begin pushLiteralVariable: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + (((variableIndex + LiteralStart) << (shiftForWord()))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(variableIndex + LiteralStart, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))))); + goto l2; + } + } + /* end case */ +l2:; + break; + case 129: // extendedStoreBytecode + { + sqInt descriptor; + usqInt frameNumArgs; + sqInt litVar; + sqInt objOop; + sqInt value; + usqInt variableIndex; + + VM_LABEL(extendedStoreBytecode); + /* begin extendedStoreBytecodePop: */ + descriptor = byteAt(++localIP); + variableIndex = descriptor & 0x3F; + value = longAt(localSP); + switch ((((usqInt)(descriptor)) >> 6) & 3) { + case 0: + objOop = longAt(localFP + FoxIFReceiver); + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(objOop)))); + if ((((usqInt)((byteAt((void *)(objOop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),objOop); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((variableIndex + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l3; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, objOop, value)); + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + ((variableIndex << (shiftForWord())))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l3:; + break; + case 1: + /* itemporary:in:put: */ + if (variableIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - variableIndex) * BytesPerWord),value); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - variableIndex) * BytesPerWord),value); + } + break; + case 2: + error("illegal store"); + break; + case 3: + /* begin storeLiteralVariable:withValue: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + (((variableIndex + LiteralStart) << (shiftForWord()))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(variableIndex + LiteralStart, GIV(method), litVar); + } + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(litVar)))); + if ((((usqInt)((byteAt((void *)(litVar + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),litVar); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((usqInt)2 /* (ValueIndex + 1) */ << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l4; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ValueIndex, litVar, value)); + assert(isNonImmediate(litVar)); + if (oopisGreaterThanOrEqualTo(litVar, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(litVar + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(litVar); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l4:; + break; + default: + error("Case not found and no otherwise clause"); + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 130: // extendedStoreAndPopBytecode + { + sqInt descriptor; + usqInt frameNumArgs; + sqInt litVar; + sqInt objOop; + sqInt value; + usqInt variableIndex; + + VM_LABEL(extendedStoreAndPopBytecode); + /* begin extendedStoreBytecodePop: */ + descriptor = byteAt(++localIP); + variableIndex = descriptor & 0x3F; + value = longAt(localSP); + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + switch ((((usqInt)(descriptor)) >> 6) & 3) { + case 0: + objOop = longAt(localFP + FoxIFReceiver); + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(objOop)))); + if ((((usqInt)((byteAt((void *)(objOop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),objOop); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((variableIndex + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l5; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, objOop, value)); + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + ((variableIndex << (shiftForWord())))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l5:; + break; + case 1: + /* itemporary:in:put: */ + if (variableIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - variableIndex) * BytesPerWord),value); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - variableIndex) * BytesPerWord),value); + } + break; + case 2: + error("illegal store"); + break; + case 3: + /* begin storeLiteralVariable:withValue: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + (((variableIndex + LiteralStart) << (shiftForWord()))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(variableIndex + LiteralStart, GIV(method), litVar); + } + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(litVar)))); + if ((((usqInt)((byteAt((void *)(litVar + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),litVar); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((usqInt)2 /* (ValueIndex + 1) */ << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l6; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ValueIndex, litVar, value)); + assert(isNonImmediate(litVar)); + if (oopisGreaterThanOrEqualTo(litVar, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(litVar + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(litVar); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l6:; + break; + default: + error("Case not found and no otherwise clause"); + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 131: // singleExtendedSendBytecode + { + sqInt descriptor; + + VM_LABEL(singleExtendedSendBytecode); + descriptor = byteAt(++localIP); + + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((descriptor & 0x1F) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = ((usqInt)(descriptor)) >> 5; + + /* goto normalSend */ + } + + normalSend: + /* normalSend */ + { + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(normalSend); + rcvr = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + lkupClassTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + assert(lkupClassTag != (nilObject())); + + commonSendOrdinary: + /* commonSendOrdinary */ + { + sqInt classTablePage; + CogMethod *cogMethod; + sqInt fieldIndex; + sqInt hash; + sqInt i; + sqInt localPrimIndex; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt nArgs; + usqInt numTemps; + sqInt object; + usqInt probe; + sqInt rcvr; + char *savedFramePointer; + char *savedStackPointer; + int succeeded; + sqInt switched; + sqInt tagBits; + + VM_LABEL(commonSendOrdinary); + rcvr = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + if (recordSendTrace()) { + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + TraceSend) + CSFromInterpreter); + GIV(traceLog)[GIV(traceLogIndex) + 1] = (classForClassTag(lkupClassTag)); + GIV(traceLog)[GIV(traceLogIndex) + 2] = GIV(messageSelector); + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + if (printOnTrace()) { + printActivationNameForSelectorstartClass(GIV(messageSelector), classForClassTag(lkupClassTag)); + cr(); + } + } + + /* begin internalFindNewMethodOrdinary */ + /* begin inlineLookupInMethodCacheSel:classTag: */ + /* shift drops two low-order zeros from addresses */ + hash = GIV(messageSelector) ^ ((((usqInt)(lkupClassTag) << 2))); + + /* first probe */ + probe = hash & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == GIV(messageSelector)) + && ((GIV(methodCache)[probe + MethodCacheClass]) == lkupClassTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + goto l128; + } + + /* second probe */ + probe = (((usqInt)(hash)) >> 1) & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == GIV(messageSelector)) + && ((GIV(methodCache)[probe + MethodCacheClass]) == lkupClassTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + goto l128; + } + probe = (((usqInt)(hash)) >> 2) & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == GIV(messageSelector)) + && ((GIV(methodCache)[probe + MethodCacheClass]) == lkupClassTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + goto l128; + } + goto l127; +l128: + + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + goto l131; +l127: + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + if ((/* isOopForwarded: */ + ((!(GIV(messageSelector) & (tagMask())))) + && ((!((longAt((void *)(GIV(messageSelector)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + || (lkupClassTag == (isForwardedObjectClassIndexPun()))) { + if (/* isOopForwarded: */ + ((!(GIV(messageSelector) & (tagMask())))) + && ((!((longAt((void *)(GIV(messageSelector)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + GIV(messageSelector) = handleForwardedSelectorFaultFor(GIV(messageSelector)); + } + if (lkupClassTag == (isForwardedObjectClassIndexPun())) { + lkupClassTag = handleForwardedSendFaultForTag(lkupClassTag); + } + if (lookupInMethodCacheSelclassTag(GIV(messageSelector), lkupClassTag)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + goto l126; + } + } + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((lkupClassTag >= 0) + && ((lkupClassTag <= (tagMask())) + || ((lkupClassTag >= (arrayClassIndexPun())) + && (lkupClassTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(lkupClassTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + GIV(lkupClass) = null; + goto l130; + } + fieldIndex = lkupClassTag & ((1U << (classTableMajorIndexShift())) - 1); + GIV(lkupClass) = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l130: + assert((GIV(lkupClass)) + && (addressCouldBeClassObj(GIV(lkupClass)))); + lookupMethodInClass(GIV(lkupClass)); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + addNewMethodToCache(GIV(lkupClass)); +l131: + /* end internalFindNewMethodOrdinary */ +l126: + + /* begin internalExecuteNewMethod */ + if (primitiveFunctionPointer) { + if ((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex) { + /* begin internalQuickPrimitiveResponse */ + assert(isPrimitiveFunctionPointerAnIndex()); + localPrimIndex = ((sqInt) primitiveFunctionPointer); + assert((localPrimIndex > 0xFF) + && (localPrimIndex < 520)); + + /* Quick return inst vars */ + if (localPrimIndex >= 264) { + longAtput(localSP,longAt((void *)(((longAt(localSP)) + BaseHeaderSize) + ((((usqInt)((localPrimIndex - 264)) << (shiftForWord()))))))); + goto l129; + } + + /* Quick return constants */ + if (localPrimIndex == 0x100) { + goto l129; + } + if (localPrimIndex == 0x101) { + /* internalStackTopPut: */ + longAtput(localSP,GIV(trueObj)); + goto l129; + } + if (localPrimIndex == 258) { + /* internalStackTopPut: */ + longAtput(localSP,GIV(falseObj)); + goto l129; + } + if (localPrimIndex == 259) { + /* internalStackTopPut: */ + longAtput(localSP,GIV(nilObj)); + goto l129; + } + + /* internalStackTopPut: */ + longAtput(localSP,(((usqInt)(localPrimIndex - 261) << 3) | 1)); + goto l129; + } + + /* slowPrimitiveResponse may of course context-switch. If so we must reenter the + new process appropriately, returning only if we've found an interpreter frame. */ + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin slowPrimitiveResponse */ + assert(!(isOopForwarded(stackValue(GIV(argumentCount))))); + assert((GIV(remapBufferCount)) == 0); + nArgs = GIV(argumentCount); + savedStackPointer = GIV(stackPointer); + savedFramePointer = GIV(framePointer); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if (/* recordPrimTraceForMethod: */ + (recordPrimTrace()) + && ((primTracePluginName + ? methodHasPrimitiveInPrimTracePlugin(GIV(newMethod)) + : 1))) { + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = GIV(newMethod); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + dispatchFunctionPointer(primitiveFunctionPointer); + assert(maybeLeakCheckExternalPrimCall(GIV(newMethod))); + + /* begin maybeRetryPrimitiveOnFailure */ + if (GIV(primFailCode)) { + retryPrimitiveOnFailure(); + } + if ((!GIV(primFailCode)) + && ((GIV(framePointer) == savedFramePointer) + && (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))))) { + if (GIV(stackPointer) != (savedStackPointer + (nArgs * BytesPerWord))) { + /* This is necessary but insufficient; the result may still have been written to the stack. + At least we'll know something is wrong. */ + failUnbalancedPrimitive(); + GIV(stackPointer) = savedStackPointer; + } + } + + /* Don't fail if primitive has done something radical, e.g. perform: + If we are profiling, take accurate primitive measures */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + succeeded = !GIV(primFailCode); + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (succeeded) { + returntoExecutive(popStack(), 1); + browserPluginReturnIfNeeded(); + goto l129; + } + } + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* if not primitive, or primitive failed, activate the method */ + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if (!((!(methodHeader & (smallIntegerTag()))))) { + /* begin internalActivateNewMethod */ + methodHeaderSqInt = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(!((isCogMethodReferenceNoAssert(methodHeaderSqInt)))); + numTemps = (((usqInt)(methodHeaderSqInt)) >> MethodHeaderTempCountShift) & 0x3F; + assert(GIV(argumentCount) == (argumentCountOfMethodHeader(methodHeaderSqInt))); + + /* could new rcvr be set at point of send? */ + rcvr = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + assert(!(isOopForwarded(rcvr))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),((usqInt)localIP)); + longAtput((localSP -= BytesPerOop),((usqInt)localFP)); + localFP = localSP; + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeaderSqInt); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeaderSqInt)) < 0 + ? 0x100 + : 0); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((((usqInt)(methodHeaderSqInt)) >> MethodHeaderArgCountShift) & 15) << ((BytesPerWord * 8) - 8))))) + : ((1 + ((((((usqInt)(methodHeaderSqInt)) >> MethodHeaderArgCountShift) & 15) << 8))))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + longAtput((localSP -= BytesPerOop),0); + longAtput((localSP -= BytesPerOop),rcvr); + + /* Initialize temps... */ + for (i = (GIV(argumentCount) + 1); i <= numTemps; i += 1) { + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(nilObj)); + } + + /* -1 to account for pre-increment in fetchNextBytecode */ + localIP = (((char *)(((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeaderSqInt) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeaderSqInt >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize))) - 1; + if (((methodHeaderSqInt & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + localIP += 3 /* sizeOfCallPrimitiveBytecode: */; + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(localSP, methodHeaderSqInt); + } + } + assert((frameNumArgs(localFP)) == GIV(argumentCount)); + assert(!(frameIsBlockActivation(localFP))); + assert(!(frameHasContext(localFP))); + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc). */ + if (localSP < GIV(stackLimit)) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + switched = handleStackOverflowOrEventAllowContextSwitch(canContextSwitchIfActivatingheader(GIV(newMethod), methodHeaderSqInt)); + returnToExecutivepostContextSwitch(1, switched); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + } + goto l129; + } + + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(localFP))); + longAtput(localFP + FoxIFSavedIP,((sqInt)localIP)); + GIV(instructionPointer) = ceReturnToInterpreterPC(); + + /* begin externalizeFPandSP */ + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* This may context switch and hence return... */ + activateNewCogMethodinInterpreter(((CogMethod *) methodHeader), 1); + + /* Hence this si reachable... */ + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + /* end internalExecuteNewMethod */ +l129: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + } + break; + case 132: // doubleExtendedDoAnythingBytecode + { + sqInt byte2; + sqInt byte3; + sqInt callerContextOrNil; + char *callerFP; + sqInt litVar; + sqInt obj; + sqInt object; + int onCurrentPage; + sqInt opType; + sqInt senderOop; + char *spouseFP; + char *theFP; + StackPage *thePage; + sqInt top; + sqInt value; + + VM_LABEL(doubleExtendedDoAnythingBytecode); + byte2 = byteAt(++localIP); + byte3 = byteAt(++localIP); + opType = ((usqInt)(byte2)) >> 5; + if (!opType) { + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((byte3 + LiteralStart)) << (shiftForWord())))))); + GIV(argumentCount) = byte2 & 0x1F; + goto normalSend; + goto l7; + } + if (opType == 1) { + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((byte3 + LiteralStart)) << (shiftForWord())))))); + GIV(argumentCount) = byte2 & 0x1F; + goto superclassSend; + goto l7; + } + if (opType == 2) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + obj = longAt(localFP + FoxIFReceiver); + if ((byte3 <= StackPointerIndex) + && (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin instVar:ofContext: */ + assert(byte3 < MethodIndex); + assert(isContext(obj)); + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + if (!(((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + value = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord())))))); + if ((byte3 == InstructionPointerIndex) + && (((((value) & 7) == 1)) + && ((((int) value)) < 0))) { + /* begin internalMustMapMachineCodePC:context: */ + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + value = mustMapMachineCodePCcontext((value >> 3), obj); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + } + object = value; + goto l10; + } + if (isWidowedContext(obj)) { + object = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord())))))); + goto l10; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!byte3) { + /* begin ensureCallerContext: */ + callerFP = ((char *)(longAt(spouseFP + FoxSavedFP))); + if (!callerFP) { + /* begin frameCallerContext: */ + assert(isBaseFrame(spouseFP)); + thePage = stackPageAtpages(pageIndexFor(spouseFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + object = callerContextOrNil; + goto l10; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP))); + object = longAt(callerFP + FoxThisContext); + goto l10; + } + object = marryFrameSP( + callerFP, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(spouseFP))), + (spouseFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(spouseFP))->cmNumArgs) + : byteAt((spouseFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + goto l10; + } + if (byte3 == StackPointerIndex) { + assert((ReceiverIndex + (stackPointerIndexForFrame(spouseFP))) < (lengthOf(obj))); + object = (((usqInt)(stackPointerIndexForFrame(spouseFP)) << 3) | 1); + goto l10; + } + if (byte3 == InstructionPointerIndex) { + object = instructionPointerForFramecurrentFPcurrentIP(spouseFP, localFP, oopForPointer(localIP)); + goto l10; + } + error("bad index"); + object = 0; + /* end instVar:ofContext: */ +l10: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + else { + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord()))))))); + } + goto l7; + } + if (opType == 3) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((byte3 + LiteralStart)) << (shiftForWord())))))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + goto l7; + } + if (opType == 4) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((byte3 + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(byte3 + LiteralStart, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))))); + goto l7; + } + top = longAt(localSP); + if (opType == 7) { + /* begin storeLiteralVariable:withValue: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((byte3 + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(byte3 + LiteralStart, GIV(method), litVar); + } + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(litVar)))); + if ((((usqInt)((byteAt((void *)(litVar + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),litVar); + longAtput((localSP -= BytesPerOop),top); + longAtput((localSP -= BytesPerOop),(((usqInt)2 /* (ValueIndex + 1) */ << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l9; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ValueIndex, litVar, top)); + assert(isNonImmediate(litVar)); + if (oopisGreaterThanOrEqualTo(litVar, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(top & (tagMask())))) + && (oopisLessThan(top, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(litVar + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(litVar); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))),top); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l9: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l7; + } + + /* opType = 5 is store; opType = 6 is storePop */ + if (opType == 6) { + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + obj = longAt(localFP + FoxIFReceiver); + if ((byte3 <= ReceiverIndex) + && ((((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + || (ismethodAssignmentToContextWithMachineCodePC(byte3, top))))) { + /* begin instVar:ofContext:put: */ + assert(isMarriedOrWidowedContext(obj)); + assert(!((isObjImmutable(obj)))); + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(obj))))) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(byte3, obj, top)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(top & (tagMask())))) + && (oopisLessThan(top, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord()))))),top); + if (byte3 == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + goto l8; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!byte3) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + onCurrentPage = thePage == GIV(stackPage); + storeSenderOfFramewithValue(theFP, top); + if (onCurrentPage) { + localFP = (GIV(stackPage)->headFP); + localSP = (GIV(stackPage)->headSP); + } + else { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + goto l8; + } + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + externalDivorceFrameandContext(theFP, obj); + if (ismethodAssignmentToContextWithMachineCodePC(byte3, obj)) { + ensureContextHasBytecodePC(obj); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(byte3, obj, top)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(top & (tagMask())))) + && (oopisLessThan(top, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord()))))),top); + if (byte3 == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + + /* Assigning various fields can force a divorce which can change the stackPage. */ + markStackPageMostRecentlyUsed(GIV(stackPage)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 1, __LINE__); + /* end instVar:ofContext:put: */ +l8:; + } + else { + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(obj)))); + if ((((usqInt)((byteAt((void *)(obj + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),obj); + longAtput((localSP -= BytesPerOop),top); + longAtput((localSP -= BytesPerOop),(((usqInt)(byte3 + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l11; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(byte3, obj, top)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(top & (tagMask())))) + && (oopisLessThan(top, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord()))))),top); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l11:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + /* end case */ +l7:; + break; + case 133: // singleExtendedSuperBytecode + { + sqInt descriptor; + + VM_LABEL(singleExtendedSuperBytecode); + descriptor = byteAt(++localIP); + + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((descriptor & 0x1F) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = ((usqInt)(descriptor)) >> 5; + + /* goto superclassSend */ + } + + superclassSend: + /* superclassSend */ + { + sqInt aValue; + sqInt classPointer; + sqInt err; + int hash; + sqInt literal; + sqInt objOop; + sqInt offset; + sqInt referent; + sqInt superclass; + + VM_LABEL(superclassSend); + /* begin methodClassOf: */ + offset = (literalCountOf(GIV(method))) - 1; + literal = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = fixFollowedFieldofObjectwithInitialValue(offset + LiteralStart, GIV(method), literal); + } + classPointer = ((literal != GIV(nilObj)) + && (/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + ? (assert((numSlotsOf(literal)) > ValueIndex), + followFieldofObject(ValueIndex, literal)) + : GIV(nilObj)); + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + superclass = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(superclass)); + if ((!((longAt((void *)(superclass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + superclass = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, superclass); + } + + /* classTagForClass: uses ensureBehaviorHash: */ + + /* begin classTagForClass: */ + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(superclass)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + lkupClassTag = ((hash = (long32At((void *)(superclass + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(superclass) + ? ((err = enterIntoClassTable(superclass)) + ? -err + : (long32At((void *)(superclass + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + + /* To maintain the invariant that all receivers are unforwarded we need an explicit + read barrier in the super send cases. Even though we always follow receivers + on become e.g. super doSomethingWith: (self become: other) forwards the receiver + self pushed on the stack. */ + + /* begin ensureReceiverUnforwarded */ + if (/* isOopForwarded: */ + ((!((longAt(localSP + (GIV(argumentCount) * BytesPerOop))) & (tagMask())))) + && ((!((longAt((void *)(longAt(localSP + (GIV(argumentCount) * BytesPerOop))))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + referent = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + aValue = referent; + + /* begin internalStackValue:put: */ + longAtput(localSP + (GIV(argumentCount) * BytesPerOop),aValue); + } + assert(lkupClassTag != (nilObject())); + goto commonSendOrdinary; + } + break; + case 134: // secondExtendedSendBytecode + { + sqInt descriptor; + + VM_LABEL(secondExtendedSendBytecode); + descriptor = byteAt(++localIP); + + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((descriptor & 0x3F) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = ((usqInt)(descriptor)) >> 6; + goto normalSend; + } + break; + case 135: // popStackBytecode + case 472: // 216 popStackBytecode + { + VM_LABEL(popStackBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + localSP += 1 * BytesPerOop; + } + break; + case 136: // duplicateTopBytecode + case 339: // 83 duplicateTopBytecode + { + sqInt object; + + VM_LABEL(duplicateTopBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = longAt(localSP); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 137: // pushActiveContextBytecode + { + sqInt ourContext; + + VM_LABEL(pushActiveContextBytecode); + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l12; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l12: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),ourContext); + } + break; + case 138: // pushNewArrayBytecode + case 487: // 231 pushNewArrayBytecode + { + usqInt array; + sqInt i; + usqInt newObj; + usqInt numBytes; + int popValues; + sqInt size; + + VM_LABEL(pushNewArrayBytecode); + size = byteAt(++localIP); + if ((popValues = size >= 128)) { + size -= 128; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((size >= 0) + && ((knownClassAtIndex(ClassArrayCompactIndex)) != GIV(nilObj))); + assert((arrayFormat()) == (instSpecOfClass(knownClassAtIndex(ClassArrayCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(size < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((size < 1 + ? 8 /* allocationUnit */ + : size * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + array = 0; + goto l13; + } + } + long64Atput((void *)(newObj),((((((usqLong) size)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + GIV(freeStart) += numBytes; + array = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l13: + + /* Can't use NewObjectMemory>>#eeInstantiateSmallClassIndex:format:numSlots: + cuz it assumes <= 252 bytes whereras this may allocate 127 * 4 + headerSize bytes. */ + if (popValues) { + for (i = 0; i < size; i += 1) { + /* Assume: have just allocated a new Array; it must be young. Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(i, array, longAt((void *)(localSP + (((size - i) - 1) * BytesPerOop))))); + longAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),longAt((void *)(localSP + (((size - i) - 1) * BytesPerOop)))); + } + + /* begin internalPop: */ + localSP += size * BytesPerOop; + } + else { + for (i = 0; i < size; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(i, array, GIV(nilObj))); + longAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),array); + } + break; + case 139: // callPrimitiveBytecode + case 504: // 248 callPrimitiveBytecode + { + sqInt header; + sqInt headerSqInt; + + VM_LABEL(callPrimitiveBytecode); + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + headerSqInt = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + if ((((header & AlternateHeaderHasPrimFlag) != 0)) + && ((((sqInt)localIP)) == (((((usqInt)(pointerForOop(GIV(method))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize))) { + localIP = (localIP + 3 /* sizeOfCallPrimitiveBytecode: */) - 1; + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l14; + } + goto respondToUnknownBytecode; + } + /* end case */ +l14:; + break; + case 140: // pushRemoteTempLongBytecode + case 507: // 251 pushRemoteTempLongBytecode + { + usqInt frameNumArgs; + sqInt remoteTempIndex; + sqInt tempVector; + sqInt tempVectorIndex; + + VM_LABEL(pushRemoteTempLongBytecode); + remoteTempIndex = byteAt(++localIP); + tempVectorIndex = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + tempVector = /* itemporary:in: */ + (tempVectorIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - tempVectorIndex) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - tempVectorIndex) * BytesPerWord))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((tempVector + BaseHeaderSize) + ((((usqInt)(remoteTempIndex) << (shiftForWord()))))))); + } + break; + case 141: // storeRemoteTempLongBytecode + case 508: // 252 storeRemoteTempLongBytecode + { + usqInt frameNumArgs; + sqInt remoteTempIndex; + sqInt tempVector; + sqInt tempVectorIndex; + sqInt valuePointer; + + VM_LABEL(storeRemoteTempLongBytecode); + remoteTempIndex = byteAt(++localIP); + tempVectorIndex = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + tempVector = /* itemporary:in: */ + (tempVectorIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - tempVectorIndex) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - tempVectorIndex) * BytesPerWord))); + valuePointer = longAt(localSP); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(remoteTempIndex, tempVector, valuePointer)); + assert(isNonImmediate(tempVector)); + if (oopisGreaterThanOrEqualTo(tempVector, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(tempVector + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(tempVector); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((tempVector + BaseHeaderSize) + ((((usqInt)(remoteTempIndex) << (shiftForWord()))))),valuePointer); + } + break; + case 142: // storeAndPopRemoteTempLongBytecode + case 509: // 253 storeAndPopRemoteTempLongBytecode + { + usqInt frameNumArgs; + sqInt remoteTempIndex; + sqInt tempVector; + sqInt tempVectorIndex; + sqInt valuePointer; + + VM_LABEL(storeAndPopRemoteTempLongBytecode); + /* begin storeRemoteTempLongBytecode */ + remoteTempIndex = byteAt(++localIP); + tempVectorIndex = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + tempVector = /* itemporary:in: */ + (tempVectorIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - tempVectorIndex) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - tempVectorIndex) * BytesPerWord))); + valuePointer = longAt(localSP); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(remoteTempIndex, tempVector, valuePointer)); + assert(isNonImmediate(tempVector)); + if (oopisGreaterThanOrEqualTo(tempVector, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(tempVector + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(tempVector); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((tempVector + BaseHeaderSize) + ((((usqInt)(remoteTempIndex) << (shiftForWord()))))),valuePointer); + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + break; + case 143: // pushClosureCopyCopiedValuesBytecode + { + sqInt blockSize; + sqInt context; + sqInt i; + sqInt initialIP; + usqInt newClosure; + usqInt newObj; + usqInt numArgs; + sqInt numArgsNumCopied; + usqInt numBytes; + sqInt numCopied; + sqInt numSlots; + + VM_LABEL(pushClosureCopyCopiedValuesBytecode); + numArgsNumCopied = byteAt(++localIP); + numArgs = numArgsNumCopied & 15; + numCopied = (((usqInt)(numArgsNumCopied)) >> 4); + + /* Split blockSize := (self fetchByte * 256) + self fetchByte. into two because evaluation order in C is undefined. */ + blockSize = ((sqInt)((usqInt)((byteAt(++localIP))) << 8)); + blockSize += byteAt(++localIP); + + /* begin pushClosureNumArgs:copiedValues:blockSize: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + context = longAt(localFP + FoxThisContext); + goto l15; + } + context = marryFrameSP(localFP, localSP + (numCopied * BytesPerOop)); + /* end ensureFrameIsMarried:SP: */ +l15: + initialIP = ((((usqInt)localIP)) + 2) - (GIV(method) + BaseHeaderSize); + + /* begin closureIn:numArgs:instructionPointer:numCopiedValues: */ + numSlots = ClosureFirstCopiedValueIndex + numCopied; + assert((numSlots >= 0) + && ((knownClassAtIndex(ClassBlockClosureCompactIndex)) != GIV(nilObj))); + assert((indexablePointersFormat()) == (instSpecOfClass(knownClassAtIndex(ClassBlockClosureCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newClosure = 0; + goto l16; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((indexablePointersFormat())) << (formatShift()))))) + ClassBlockClosureCompactIndex); + GIV(freeStart) += numBytes; + newClosure = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l16: + + /* Assume: have just allocated a new closure; it must be young. Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureOuterContextIndex, newClosure, context)); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord()))))),context); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureStartPCIndex, newClosure, (((usqInt)initialIP << 3) | 1))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))),(((usqInt)initialIP << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureNumArgsIndex, newClosure, ((numArgs << 3) | 1))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord()))))),((numArgs << 3) | 1)); + if (numCopied > 0) { + for (i = 0; i < numCopied; i += 1) { + /* Assume: have just allocated a new BlockClosure; it must be young. + Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(i + ClosureFirstCopiedValueIndex, newClosure, longAt((void *)(localSP + (((numCopied - i) - 1) * BytesPerOop))))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)((i + ClosureFirstCopiedValueIndex)) << (shiftForWord()))))),longAt((void *)(localSP + (((numCopied - i) - 1) * BytesPerOop)))); + } + + /* begin internalPop: */ + localSP += numCopied * BytesPerOop; + } + localIP += blockSize; + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),newClosure); + } + break; + case 144: // shortUnconditionalJump + case 432: // 176 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump); + offset = 1; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + break; + case 145: // shortUnconditionalJump + case 433: // 177 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump1); + offset = 2; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + break; + case 146: // shortUnconditionalJump + case 434: // 178 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump2); + offset = 3; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + break; + case 147: // shortUnconditionalJump + case 435: // 179 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump3); + offset = 4; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + break; + case 148: // shortUnconditionalJump + case 436: // 180 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump4); + offset = 5; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + break; + case 149: // shortUnconditionalJump + case 437: // 181 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump5); + offset = 6; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + break; + case 150: // shortUnconditionalJump + case 438: // 182 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump6); + offset = 7; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + break; + case 151: // shortUnconditionalJump + case 439: // 183 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump7); + offset = 8; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + break; + case 152: // shortConditionalJumpFalse + case 153: // shortConditionalJumpFalse + case 154: // shortConditionalJumpFalse + case 155: // shortConditionalJumpFalse + case 156: // shortConditionalJumpFalse + case 157: // shortConditionalJumpFalse + case 158: // shortConditionalJumpFalse + case 159: // shortConditionalJumpFalse + case 448: // 192 shortConditionalJumpFalse + case 449: // 193 shortConditionalJumpFalse + case 450: // 194 shortConditionalJumpFalse + case 451: // 195 shortConditionalJumpFalse + case 452: // 196 shortConditionalJumpFalse + case 453: // 197 shortConditionalJumpFalse + case 454: // 198 shortConditionalJumpFalse + case 455: // 199 shortConditionalJumpFalse + { + sqInt boolean; + sqInt offset; + + VM_LABEL(shortConditionalJumpFalse); + offset = (currentBytecode & 7) + 1; + + /* begin jumplfFalseBy: */ + boolean = longAt(localSP); + if (boolean == GIV(falseObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(trueObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l17; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfFalseBy: */ +l17:; + } + break; + case 160: // longUnconditionalJump + case 161: // longUnconditionalJump + case 162: // longUnconditionalJump + case 163: // longUnconditionalJump + case 164: // longUnconditionalJump + case 165: // longUnconditionalJump + case 166: // longUnconditionalJump + case 167: // longUnconditionalJump + { + sqInt backwardJumpCountByte; + sqInt header; + sqInt methodHeader; + sqInt offset; + sqInt switched; + + VM_LABEL(longUnconditionalJump); + offset = (((currentBytecode & 7) - 4) * 0x100) + (byteAt(++localIP)); + localIP += offset; + + /* begin ifBackwardsCheckForEvents: */ + if (offset >= 0) { + goto l18; + } + if (localSP < GIV(stackLimit)) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + switched = checkForEventsMayContextSwitch(1); + returnToExecutivepostContextSwitch(1, switched); + browserPluginReturnIfNeeded(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (switched) { + goto l18; + } + } + + /* We use the least significant byte of the flags word (which is marked as an immediate) and + subtract two each time to avoid disturbing the least significant tag bit. Since the byte is + initialized to 1 (on frame build), on first decrement it will become -1. Trip when it reaches 1 again. */ + backwardJumpCountByte = byteAt(localFP + ((VMBIGENDIAN + ? (FoxIFrameFlags + BytesPerWord) - 1 + : FoxIFrameFlags))); + if (((backwardJumpCountByte -= 2)) == 1) { + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + attemptToSwitchToMachineCode(((((oopForPointer(localIP)) - offset) - GIV(method)) - BaseHeaderSize) - 1); + } + + /* can't cog method; avoid asking to cog it again for the longest possible time. */ + backwardJumpCountByte = 0x7F; + } + else { + if (backwardJumpCountByte == -1) { + assert(minBackwardJumpCountForCompile <= 128); + backwardJumpCountByte = ((((usqInt)((minBackwardJumpCountForCompile - 1)) << 1))) + 1; + } + } + + /* begin iframeBackwardBranchByte:put: */ + byteAtput(localFP + ((VMBIGENDIAN + ? (FoxIFrameFlags + BytesPerWord) - 1 + : FoxIFrameFlags)),backwardJumpCountByte); + /* end ifBackwardsCheckForEvents: */ +l18: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 168: // longJumpIfTrue + case 169: // longJumpIfTrue + case 170: // longJumpIfTrue + case 171: // longJumpIfTrue + { + sqInt boolean; + sqInt offset; + + VM_LABEL(longJumpIfTrue); + offset = ((currentBytecode & 3) * 0x100) + (byteAt(++localIP)); + + /* begin jumplfTrueBy: */ + boolean = longAt(localSP); + if (boolean == GIV(trueObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(falseObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l19; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfTrueBy: */ +l19:; + } + break; + case 172: // longJumpIfFalse + case 173: // longJumpIfFalse + case 174: // longJumpIfFalse + case 175: // longJumpIfFalse + { + sqInt boolean; + sqInt offset; + + VM_LABEL(longJumpIfFalse); + offset = ((currentBytecode & 3) * 0x100) + (byteAt(++localIP)); + + /* begin jumplfFalseBy: */ + boolean = longAt(localSP); + if (boolean == GIV(falseObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(trueObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l20; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfFalseBy: */ +l20:; + } + break; + case 176: // bytecodePrimAdd + case 352: // 96 bytecodePrimAdd + { + sqInt arg; + double argDouble; + usqLong bits; + sqInt rcvr; + double rcvrDouble; + sqInt result; + double resultDouble; + char *sp; + sqInt tagBits; + double value; + + VM_LABEL(bytecodePrimAdd); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + result = ((rcvr >> 3)) + ((arg >> 3)); + if ((((((usqInt)(result)) >> 60) + 1) & 15) <= 1) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),(((usqInt)result << 3) | 1)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l21; + } + } + else { + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin primitiveFloatAdd:toArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvr & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvrDouble = value; + goto l22; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvrDouble = ((double) ((rcvr >> 3)) ); + goto l22; + } + } + else { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvr + BaseHeaderSize, resultDouble); + rcvrDouble = resultDouble; + goto l22; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvrDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l22: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = arg & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(arg)); + bits = ((((usqInt)arg))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + argDouble = value; + goto l23; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + argDouble = ((double) ((arg >> 3)) ); + goto l23; + } + } + else { + if (((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(arg + BaseHeaderSize, resultDouble); + argDouble = resultDouble; + goto l23; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + argDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l23: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvrDouble + argDouble)); + GIV(stackPointer) = sp; + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l21; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (0U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l21:; + break; + case 177: // bytecodePrimSubtract + case 353: // 97 bytecodePrimSubtract + { + sqInt arg; + double argDouble; + usqLong bits; + sqInt rcvr; + double rcvrDouble; + sqInt result; + double resultDouble; + char *sp; + sqInt tagBits; + double value; + + VM_LABEL(bytecodePrimSubtract); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + result = ((rcvr >> 3)) - ((arg >> 3)); + if ((((((usqInt)(result)) >> 60) + 1) & 15) <= 1) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),(((usqInt)result << 3) | 1)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l24; + } + } + else { + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin primitiveFloatSubtract:fromArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvr & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvrDouble = value; + goto l25; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvrDouble = ((double) ((rcvr >> 3)) ); + goto l25; + } + } + else { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvr + BaseHeaderSize, resultDouble); + rcvrDouble = resultDouble; + goto l25; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvrDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l25: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = arg & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(arg)); + bits = ((((usqInt)arg))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + argDouble = value; + goto l26; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + argDouble = ((double) ((arg >> 3)) ); + goto l26; + } + } + else { + if (((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(arg + BaseHeaderSize, resultDouble); + argDouble = resultDouble; + goto l26; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + argDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l26: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvrDouble - argDouble)); + GIV(stackPointer) = sp; + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l24; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (2U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l24:; + break; + case 178: // bytecodePrimLessThan + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimLessThan); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheat: */ + if (rcvr < arg) { + /* goto booleanCheatTrue */ + } + else { + goto booleanCheatFalse; + } + goto l27; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatLessthanArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l27; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (4U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l27: + + booleanCheatTrue: + /* booleanCheatTrue */ + { + sqInt bytecode; + sqInt offset; + + VM_LABEL(booleanCheatTrue); + /* assume next bytecode is jumpIfFalse (99%) */ + bytecode = byteAt(++localIP); + + /* begin internalPop: */ + localSP += 2 * BytesPerOop; + if ((bytecode < 173) + && (bytecode > 151)) { + if (bytecode < 160) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l132; + } + + /* short jumpIfFalse 152 - 159 */ + if (bytecode == 172) { + /* fetchByte */ + byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l132; + } + + /* long jumpIfFalse */ + if (bytecode > 167) { + offset = ((((usqInt)((bytecode - 168)) << 8))) + (byteAt(++localIP)); + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l132; + } + } + + /* not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result */ + currentBytecode = bytecode + GIV(bytecodeSetSelector); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(trueObj)); + } + /* end case */ +l132:; + break; + case 179: // bytecodePrimGreaterThan + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimGreaterThan); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheat: */ + if (rcvr > arg) { + goto booleanCheatTrue; + } + else { + /* goto booleanCheatFalse */ + } + goto l28; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatGreaterthanArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l28; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (6U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l28: + + booleanCheatFalse: + /* booleanCheatFalse */ + { + sqInt bytecode; + sqInt offset; + + VM_LABEL(booleanCheatFalse); + /* assume next bytecode is jumpIfFalse (99%) */ + bytecode = byteAt(++localIP); + + /* begin internalPop: */ + localSP += 2 * BytesPerOop; + if ((bytecode < 160) + && (bytecode > 151)) { + /* begin jump: */ + localIP = (localIP + (bytecode - 151)) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l133; + } + + /* short jumpIfFalse */ + if (bytecode == 172) { + offset = byteAt(++localIP); + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l133; + } + + /* long jumpIfFalse + not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result */ + currentBytecode = bytecode + GIV(bytecodeSetSelector); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(falseObj)); + } + /* end case */ +l133:; + break; + case 180: // bytecodePrimLessOrEqual + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimLessOrEqual); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheat: */ + if (rcvr <= arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l29; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatLessOrEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l29; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (8U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l29:; + break; + case 181: // bytecodePrimGreaterOrEqual + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimGreaterOrEqual); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheat: */ + if (rcvr >= arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l30; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatGreaterOrEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l30; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (10U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l30:; + break; + case 182: // bytecodePrimEqual + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimEqual); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* begin booleanCheat: */ + if (rcvr == arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l31; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l31; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (12U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l31:; + break; + case 183: // bytecodePrimNotEqual + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimNotEqual); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* begin booleanCheat: */ + if (rcvr != arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l32; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatFalse; + } + else { + goto booleanCheatTrue; + } + goto l32; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (14U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l32:; + break; + case 184: // bytecodePrimMultiply + case 360: // 104 bytecodePrimMultiply + { + sqInt arg; + double argDouble; + usqLong bits; + sqInt oop; + sqInt overflow; + sqInt rcvr; + double rcvrDouble; + sqInt result; + double resultDouble; + char *sp; + sqInt tagBits; + double value; + + VM_LABEL(bytecodePrimMultiply); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + rcvr = (rcvr >> 3); + arg = (arg >> 3); + overflow = (rcvr > 0 + ? (arg > 0 + ? rcvr > ((MaxSmallInteger) / arg) + : arg < ((MinSmallInteger) / rcvr)) + : (arg > 0 + ? rcvr < ((MinSmallInteger) / arg) + : (rcvr < 0) + && (arg < ((MaxSmallInteger) / rcvr)))); + if (!overflow) { + result = rcvr * arg; + oop = (((usqInt)result << 3) | 1); + + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),oop); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l33; + } + } + else { + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin primitiveFloatMultiply:byArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvr & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvrDouble = value; + goto l34; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvrDouble = ((double) ((rcvr >> 3)) ); + goto l34; + } + } + else { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvr + BaseHeaderSize, resultDouble); + rcvrDouble = resultDouble; + goto l34; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvrDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l34: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = arg & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(arg)); + bits = ((((usqInt)arg))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + argDouble = value; + goto l35; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + argDouble = ((double) ((arg >> 3)) ); + goto l35; + } + } + else { + if (((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(arg + BaseHeaderSize, resultDouble); + argDouble = resultDouble; + goto l35; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + argDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l35: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvrDouble * argDouble)); + GIV(stackPointer) = sp; + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l33; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (16U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l33:; + break; + case 185: // bytecodePrimDivide + case 361: // 105 bytecodePrimDivide + { + sqInt arg; + double argDouble; + usqLong bits; + sqInt rcvr; + double rcvrDouble; + sqInt result; + double resultDouble; + char *sp; + sqInt tagBits; + double value; + + VM_LABEL(bytecodePrimDivide); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + rcvr = (rcvr >> 3); + arg = (arg >> 3); + if ((arg != 0) + && ((rcvr % arg) == 0)) { + result = rcvr / arg; + + /* generates C / operation */ + if ((((((usqInt)(result)) >> 60) + 1) & 15) <= 1) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),(((usqInt)result << 3) | 1)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l36; + } + } + } + else { + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin primitiveFloatDivide:byArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvr & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvrDouble = value; + goto l37; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvrDouble = ((double) ((rcvr >> 3)) ); + goto l37; + } + } + else { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvr + BaseHeaderSize, resultDouble); + rcvrDouble = resultDouble; + goto l37; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvrDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l37: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = arg & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(arg)); + bits = ((((usqInt)arg))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + argDouble = value; + goto l38; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + argDouble = ((double) ((arg >> 3)) ); + goto l38; + } + } + else { + if (((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(arg + BaseHeaderSize, resultDouble); + argDouble = resultDouble; + goto l38; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + argDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l38: + + /* begin success: */ + if (!(argDouble != 0.0)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvrDouble / argDouble)); + GIV(stackPointer) = sp; + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l36; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (18U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l36:; + break; + case 186: // bytecodePrimMod + case 362: // 106 bytecodePrimMod + { + sqInt mod; + + VM_LABEL(bytecodePrimMod); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + mod = doPrimitiveModby(longAt(localSP + (1 * BytesPerOop)), longAt(localSP)); + if (!GIV(primFailCode)) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),(((usqInt)mod << 3) | 1)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l39; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (20U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l39:; + break; + case 187: // bytecodePrimMakePoint + case 363: // 107 bytecodePrimMakePoint + { + sqInt arg; + usqInt newObj; + usqInt numBytes; + usqInt pt; + sqInt rcvr; + + VM_LABEL(bytecodePrimMakePoint); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((/* isFloatOrInt: */ + (((rcvr & (tagMask())) != 0) + ? !(((rcvr & (characterTag())) != 0)) + : ((longAt((void *)(rcvr))) & (classIndexMask())) == ClassFloatCompactIndex)) + && (/* isFloatOrInt: */ + (((arg & (tagMask())) != 0) + ? !(((arg & (characterTag())) != 0)) + : ((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex))) { + /* begin eeInstantiatePoint */ + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert(((YIndex + 1) >= 0) + && ((knownClassAtIndex(ClassPointCompactIndex)) != GIV(nilObj))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(knownClassAtIndex(ClassPointCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert((YIndex + 1) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (((YIndex + 1) < 1 + ? 8 /* allocationUnit */ + : (YIndex + 1) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + pt = 0; + goto l41; + } + } + long64Atput((void *)(newObj),((((((usqLong) (YIndex + 1))) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + ClassPointCompactIndex); + GIV(freeStart) += numBytes; + pt = newObj; + /* end eeInstantiatePoint */ +l41: + + /* No need to check since new object is always new. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pt)) + && (!(isForwarded(pt)))); + assert(validStorePointerUncheckedArgs(XIndex, pt, rcvr)); + longAtput((void *)((pt + BaseHeaderSize) + ((((usqInt)(XIndex) << (shiftForWord()))))),rcvr); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pt)) + && (!(isForwarded(pt)))); + assert(validStorePointerUncheckedArgs(YIndex, pt, arg)); + longAtput((void *)((pt + BaseHeaderSize) + ((((usqInt)(YIndex) << (shiftForWord()))))),arg); + + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),pt); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l40; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (22U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l40:; + break; + case 188: // bytecodePrimBitShift + case 364: // 108 bytecodePrimBitShift + { + sqInt integerArgument; + sqInt integerReceiver; + sqInt shifted; + + VM_LABEL(bytecodePrimBitShift); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin primitiveBitShift */ + integerArgument = longAt(GIV(stackPointer)); + if (!((((integerArgument) & 7) == 1))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + goto l43; + } + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + integerReceiver = signed64BitValueOf(integerReceiver); + if (!GIV(primFailCode)) { + if (((integerArgument = (integerArgument >> 3))) >= 0) { + if (!(integerArgument <= 61 /* numSmallIntegerBits */)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + goto l43; + } + shifted = ((sqInt)((usqInt)(integerReceiver) << integerArgument)); + if (!(integerReceiver == ((shifted) >> integerArgument))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + goto l43; + } + } + else { + if (!(integerArgument >= (-61 /* numSmallIntegerBits */))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + goto l43; + } + shifted = (integerReceiver) >> (0 - integerArgument); + } + + /* Left shift -- must fail bits would be lost + Right shift -- OK to lose bits */ + shifted = ((((((usqInt)(shifted)) >> 60) + 1) & 15) <= 1 + ? (((usqInt)shifted << 3) | 1) + : signed64BitIntegerFor(shifted)); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),shifted); + } + /* end primitiveBitShift */ +l43: + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l42; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (24U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l42:; + break; + case 189: // bytecodePrimDiv + case 365: // 109 bytecodePrimDiv + { + sqInt quotient; + + VM_LABEL(bytecodePrimDiv); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + quotient = doPrimitiveDivby(longAt(localSP + (1 * BytesPerOop)), longAt(localSP)); + if (!GIV(primFailCode)) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),(((usqInt)quotient << 3) | 1)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l44; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (26U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l44:; + break; + case 190: // bytecodePrimBitAnd + case 366: // 110 bytecodePrimBitAnd + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimBitAnd); + arg = longAt(localSP); + rcvr = longAt(localSP + (1 * BytesPerOop)); + if (((((arg) & 7) == 1)) + && ((((rcvr) & 7) == 1))) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),arg & rcvr); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l45; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + primitiveBitAnd(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l45; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (28U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l45:; + break; + case 191: // bytecodePrimBitOr + case 367: // 111 bytecodePrimBitOr + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimBitOr); + arg = longAt(localSP); + rcvr = longAt(localSP + (1 * BytesPerOop)); + if (((((arg) & 7) == 1)) + && ((((rcvr) & 7) == 1))) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),arg | rcvr); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l46; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + primitiveBitOr(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l46; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (30U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l46:; + break; + case 192: // bytecodePrimAt + case 368: // 112 bytecodePrimAt + { + VM_LABEL(bytecodePrimAt); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (32U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + break; + case 193: // bytecodePrimAtPut + case 369: // 113 bytecodePrimAtPut + { + VM_LABEL(bytecodePrimAtPut); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (34U << (shiftForWord())))); + GIV(argumentCount) = 2; + goto normalSend; + } + break; + case 194: // bytecodePrimSize + case 370: // 114 bytecodePrimSize + { + sqInt ccIndex; + sqInt fmt; + sqInt isArray; + sqInt isString; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt sz; + + VM_LABEL(bytecodePrimSize); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(localSP); + + /* Shortcut the mega-lookup for ByteString and Array, the two big consumers of cycles + here. Both of these have compact class indices and neither has any added fields. */ + + /* begin isInstanceOfClassByteString: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((rcvr & (tagMask())) != 0)) { + isString = 0; + goto l48; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isString = ClassByteStringCompactIndex == ccIndex; + /* end isInstanceOfClassByteString: */ +l48: + if (isString) { + /* begin lengthOf: */ + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + sz = numSlots; + goto l49; + } + if (fmt >= (firstByteFormat())) { + sz = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l49; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + sz = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l49; + } + if (fmt >= (firstLongFormat())) { + sz = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l49; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + sz = numSlots; + goto l49; + } + + /* fmt = self forwardedFormat */ + sz = 0; + /* end lengthOf: */ +l49: + + /* internalStackTopPut: */ + longAtput(localSP,(((usqInt)sz << 3) | 1)); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l47; + } + + /* begin isInstanceOfClassArray: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((rcvr & (tagMask())) != 0)) { + isArray = 0; + goto l50; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isArray = ClassArrayCompactIndex == ccIndex; + /* end isInstanceOfClassArray: */ +l50: + if (isArray) { + /* begin lengthOf: */ + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + sz = numSlots; + goto l51; + } + if (fmt >= (firstByteFormat())) { + sz = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l51; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + sz = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l51; + } + if (fmt >= (firstLongFormat())) { + sz = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l51; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + sz = numSlots; + goto l51; + } + + /* fmt = self forwardedFormat */ + sz = 0; + /* end lengthOf: */ +l51: + + /* internalStackTopPut: */ + longAtput(localSP,(((usqInt)sz << 3) | 1)); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l47; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (36U << (shiftForWord())))); + GIV(argumentCount) = 0; + goto normalSend; + } + /* end case */ +l47:; + break; + case 195: // bytecodePrimNext + case 371: // 115 bytecodePrimNext + { + VM_LABEL(bytecodePrimNext); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (38U << (shiftForWord())))); + GIV(argumentCount) = ((fetchPointerofObject(39, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))))) >> 3); + goto normalSend; + } + break; + case 196: // bytecodePrimNextPut + case 372: // 116 bytecodePrimNextPut + { + VM_LABEL(bytecodePrimNextPut); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (40U << (shiftForWord())))); + GIV(argumentCount) = ((fetchPointerofObject(41, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))))) >> 3); + goto normalSend; + } + break; + case 197: // bytecodePrimAtEnd + case 373: // 117 bytecodePrimAtEnd + { + VM_LABEL(bytecodePrimAtEnd); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (42U << (shiftForWord())))); + GIV(argumentCount) = ((fetchPointerofObject(43, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))))) >> 3); + goto normalSend; + } + break; + case 198: // bytecodePrimIdentical + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimIdentical); + rcvr = longAt(localSP + (1 * BytesPerOop)); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = handleSpecialSelectorSendFaultForfpsp(rcvr, localFP, localSP); + } + arg = longAt(localSP); + if (/* isOopForwarded: */ + ((!(arg & (tagMask())))) + && ((!((longAt((void *)(arg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + arg = handleSpecialSelectorSendFaultForfpsp(arg, localFP, localSP); + } + + /* begin booleanCheat: */ + if (rcvr == arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + } + break; + case 199: // bytecodePrimClass + case 375: // 119 bytecodePrimClass + { + sqInt aValue; + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(bytecodePrimClass); + rcvr = longAt(localSP); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = handleSpecialSelectorSendFaultForfpsp(rcvr, localFP, localSP); + } + aValue = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)); + + /* begin internalStackTopPut: */ + longAtput(localSP,aValue); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 200: // bytecodePrimNotIdentical + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimNotIdentical); + rcvr = longAt(localSP + (1 * BytesPerOop)); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = handleSpecialSelectorSendFaultForfpsp(rcvr, localFP, localSP); + } + arg = longAt(localSP); + if (/* isOopForwarded: */ + ((!(arg & (tagMask())))) + && ((!((longAt((void *)(arg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + arg = handleSpecialSelectorSendFaultForfpsp(arg, localFP, localSP); + } + + /* begin booleanCheat: */ + if (rcvr != arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + } + break; + case 201: // bytecodePrimValue + case 377: // 121 bytecodePrimValue + { + sqInt ccIndex; + sqInt isBlock; + sqInt rcvr; + + VM_LABEL(bytecodePrimValue); + rcvr = longAt(localSP); + GIV(argumentCount) = 0; + + /* begin isInstanceOfClassBlockClosure: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((rcvr & (tagMask())) != 0)) { + isBlock = 0; + goto l53; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isBlock = ClassBlockClosureCompactIndex == ccIndex; + /* end isInstanceOfClassBlockClosure: */ +l53: + if (isBlock) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + primitiveClosureValue(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l52; + } + GIV(primFailCode) = 0; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (50U << (shiftForWord())))); + goto normalSend; + } + /* end case */ +l52:; + break; + case 202: // bytecodePrimValueWithArg + case 378: // 122 bytecodePrimValueWithArg + { + sqInt ccIndex; + sqInt isBlock; + sqInt rcvr; + + VM_LABEL(bytecodePrimValueWithArg); + rcvr = longAt(localSP + (1 * BytesPerOop)); + GIV(argumentCount) = 1; + + /* begin isInstanceOfClassBlockClosure: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((rcvr & (tagMask())) != 0)) { + isBlock = 0; + goto l55; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isBlock = ClassBlockClosureCompactIndex == ccIndex; + /* end isInstanceOfClassBlockClosure: */ +l55: + if (isBlock) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + primitiveClosureValue(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l54; + } + GIV(primFailCode) = 0; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (52U << (shiftForWord())))); + goto normalSend; + } + /* end case */ +l54:; + break; + case 203: // bytecodePrimDo + case 379: // 123 bytecodePrimDo + { + VM_LABEL(bytecodePrimDo); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (54U << (shiftForWord())))); + GIV(argumentCount) = ((fetchPointerofObject(55, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))))) >> 3); + goto normalSend; + } + break; + case 204: // bytecodePrimNew + case 380: // 124 bytecodePrimNew + { + VM_LABEL(bytecodePrimNew); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (56U << (shiftForWord())))); + GIV(argumentCount) = 0; + goto normalSend; + } + break; + case 205: // bytecodePrimNewWithArg + case 381: // 125 bytecodePrimNewWithArg + { + VM_LABEL(bytecodePrimNewWithArg); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (58U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + break; + case 206: // bytecodePrimPointX + case 382: // 126 bytecodePrimPointX + { + sqInt ccIndex; + sqInt isClassOfNonImmequalTocompactClassIndexRV; + sqInt rcvr; + + VM_LABEL(bytecodePrimPointX); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(localSP); + if ((!(rcvr & (tagMask())))) { + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isClassOfNonImmequalTocompactClassIndexRV = ClassPointCompactIndex == ccIndex; + if (isClassOfNonImmequalTocompactClassIndexRV) { + longAtput(localSP,longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(XIndex) << (shiftForWord()))))))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l56; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (60U << (shiftForWord())))); + GIV(argumentCount) = 0; + goto normalSend; + } + /* end case */ +l56:; + break; + case 207: // bytecodePrimPointY + case 383: // 127 bytecodePrimPointY + { + sqInt ccIndex; + sqInt isClassOfNonImmequalTocompactClassIndexRV; + sqInt rcvr; + + VM_LABEL(bytecodePrimPointY); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(localSP); + if ((!(rcvr & (tagMask())))) { + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isClassOfNonImmequalTocompactClassIndexRV = ClassPointCompactIndex == ccIndex; + if (isClassOfNonImmequalTocompactClassIndexRV) { + longAtput(localSP,longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(YIndex) << (shiftForWord()))))))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l57; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (0x3EU << (shiftForWord())))); + GIV(argumentCount) = 0; + goto normalSend; + } + /* end case */ +l57:; + break; + case 208: // sendLiteralSelector0ArgsBytecode + case 209: // sendLiteralSelector0ArgsBytecode + case 210: // sendLiteralSelector0ArgsBytecode + case 211: // sendLiteralSelector0ArgsBytecode + case 212: // sendLiteralSelector0ArgsBytecode + case 213: // sendLiteralSelector0ArgsBytecode + case 214: // sendLiteralSelector0ArgsBytecode + case 215: // sendLiteralSelector0ArgsBytecode + case 216: // sendLiteralSelector0ArgsBytecode + case 217: // sendLiteralSelector0ArgsBytecode + case 218: // sendLiteralSelector0ArgsBytecode + case 219: // sendLiteralSelector0ArgsBytecode + case 220: // sendLiteralSelector0ArgsBytecode + case 221: // sendLiteralSelector0ArgsBytecode + case 222: // sendLiteralSelector0ArgsBytecode + case 223: // sendLiteralSelector0ArgsBytecode + case 384: // 128 sendLiteralSelector0ArgsBytecode + case 385: // 129 sendLiteralSelector0ArgsBytecode + case 386: // 130 sendLiteralSelector0ArgsBytecode + case 387: // 131 sendLiteralSelector0ArgsBytecode + case 388: // 132 sendLiteralSelector0ArgsBytecode + case 389: // 133 sendLiteralSelector0ArgsBytecode + case 390: // 134 sendLiteralSelector0ArgsBytecode + case 391: // 135 sendLiteralSelector0ArgsBytecode + case 392: // 136 sendLiteralSelector0ArgsBytecode + case 393: // 137 sendLiteralSelector0ArgsBytecode + case 394: // 138 sendLiteralSelector0ArgsBytecode + case 395: // 139 sendLiteralSelector0ArgsBytecode + case 396: // 140 sendLiteralSelector0ArgsBytecode + case 397: // 141 sendLiteralSelector0ArgsBytecode + case 398: // 142 sendLiteralSelector0ArgsBytecode + case 399: // 143 sendLiteralSelector0ArgsBytecode + { + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(sendLiteralSelector0ArgsBytecode); + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((currentBytecode & 15) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = 0; + rcvr = longAt(localSP); + lkupClassTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + assert(lkupClassTag != (nilObject())); + goto commonSendOrdinary; + } + break; + case 224: // sendLiteralSelector1ArgBytecode + case 225: // sendLiteralSelector1ArgBytecode + case 226: // sendLiteralSelector1ArgBytecode + case 227: // sendLiteralSelector1ArgBytecode + case 228: // sendLiteralSelector1ArgBytecode + case 229: // sendLiteralSelector1ArgBytecode + case 230: // sendLiteralSelector1ArgBytecode + case 231: // sendLiteralSelector1ArgBytecode + case 232: // sendLiteralSelector1ArgBytecode + case 233: // sendLiteralSelector1ArgBytecode + case 234: // sendLiteralSelector1ArgBytecode + case 235: // sendLiteralSelector1ArgBytecode + case 236: // sendLiteralSelector1ArgBytecode + case 237: // sendLiteralSelector1ArgBytecode + case 238: // sendLiteralSelector1ArgBytecode + case 239: // sendLiteralSelector1ArgBytecode + case 400: // 144 sendLiteralSelector1ArgBytecode + case 401: // 145 sendLiteralSelector1ArgBytecode + case 402: // 146 sendLiteralSelector1ArgBytecode + case 403: // 147 sendLiteralSelector1ArgBytecode + case 404: // 148 sendLiteralSelector1ArgBytecode + case 405: // 149 sendLiteralSelector1ArgBytecode + case 406: // 150 sendLiteralSelector1ArgBytecode + case 407: // 151 sendLiteralSelector1ArgBytecode + case 408: // 152 sendLiteralSelector1ArgBytecode + case 409: // 153 sendLiteralSelector1ArgBytecode + case 410: // 154 sendLiteralSelector1ArgBytecode + case 411: // 155 sendLiteralSelector1ArgBytecode + case 412: // 156 sendLiteralSelector1ArgBytecode + case 413: // 157 sendLiteralSelector1ArgBytecode + case 414: // 158 sendLiteralSelector1ArgBytecode + case 415: // 159 sendLiteralSelector1ArgBytecode + { + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(sendLiteralSelector1ArgBytecode); + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((currentBytecode & 15) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = 1; + rcvr = longAt(localSP + (1 * BytesPerOop)); + lkupClassTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + assert(lkupClassTag != (nilObject())); + goto commonSendOrdinary; + } + break; + case 240: // sendLiteralSelector2ArgsBytecode + case 241: // sendLiteralSelector2ArgsBytecode + case 242: // sendLiteralSelector2ArgsBytecode + case 243: // sendLiteralSelector2ArgsBytecode + case 244: // sendLiteralSelector2ArgsBytecode + case 245: // sendLiteralSelector2ArgsBytecode + case 246: // sendLiteralSelector2ArgsBytecode + case 247: // sendLiteralSelector2ArgsBytecode + case 248: // sendLiteralSelector2ArgsBytecode + case 249: // sendLiteralSelector2ArgsBytecode + case 250: // sendLiteralSelector2ArgsBytecode + case 251: // sendLiteralSelector2ArgsBytecode + case 252: // sendLiteralSelector2ArgsBytecode + case 253: // sendLiteralSelector2ArgsBytecode + case 254: // sendLiteralSelector2ArgsBytecode + case 255: // sendLiteralSelector2ArgsBytecode + case 416: // 160 sendLiteralSelector2ArgsBytecode + case 417: // 161 sendLiteralSelector2ArgsBytecode + case 418: // 162 sendLiteralSelector2ArgsBytecode + case 419: // 163 sendLiteralSelector2ArgsBytecode + case 420: // 164 sendLiteralSelector2ArgsBytecode + case 421: // 165 sendLiteralSelector2ArgsBytecode + case 422: // 166 sendLiteralSelector2ArgsBytecode + case 423: // 167 sendLiteralSelector2ArgsBytecode + case 424: // 168 sendLiteralSelector2ArgsBytecode + case 425: // 169 sendLiteralSelector2ArgsBytecode + case 426: // 170 sendLiteralSelector2ArgsBytecode + case 427: // 171 sendLiteralSelector2ArgsBytecode + case 428: // 172 sendLiteralSelector2ArgsBytecode + case 429: // 173 sendLiteralSelector2ArgsBytecode + case 430: // 174 sendLiteralSelector2ArgsBytecode + case 431: // 175 sendLiteralSelector2ArgsBytecode + { + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(sendLiteralSelector2ArgsBytecode); + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((currentBytecode & 15) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = 2; + rcvr = longAt(localSP + (2 * BytesPerOop)); + lkupClassTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + assert(lkupClassTag != (nilObject())); + goto commonSendOrdinary; + } + break; + case 338: // 82 extPushPseudoVariable + { + sqInt objOop; + sqInt theThingToPush; + + VM_LABEL(extPushPseudoVariable); + switch (extB) { + case 0: + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + theThingToPush = longAt(localFP + FoxThisContext); + goto l58; + } + theThingToPush = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l58:; + break; + case 1: + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + theThingToPush = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + break; + default: + goto respondToUnknownBytecode; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),theThingToPush); + extB = 0; + numExtB = 0; + } + break; + case 340: // 84 unknownBytecode + case 341: // 85 unknownBytecode + case 342: // 86 unknownBytecode + case 343: // 87 unknownBytecode + case 473: // 217 unconditionalTrapBytecode + case 474: // 218 unknownBytecode + case 475: // 219 unknownBytecode + case 476: // 220 unknownBytecode + case 477: // 221 unknownBytecode + case 478: // 222 unknownBytecode + case 479: // 223 unknownBytecode + case 486: // 230 unknownBytecode + case 502: // 246 unknownBytecode + case 503: // 247 unknownBytecode + case 510: // 254 unknownBytecode + case 511: // 255 unknownBytecode + { + VM_LABEL(unknownBytecode); + goto respondToUnknownBytecode; + } + break; + case 344: // 88 returnReceiver + { + VM_LABEL(returnReceiver1); + localReturnValue = longAt(localFP + FoxIFReceiver); + goto commonReturn; + } + break; + case 349: // 93 returnNilFromBlock + { + VM_LABEL(returnNilFromBlock); + localReturnValue = GIV(nilObj); + goto commonCallerReturn; + } + break; + case 350: // 94 returnTopFromBlock + { + VM_LABEL(returnTopFromBlock1); + localReturnValue = longAt(localSP); + goto commonCallerReturn; + } + break; + case 351: // 95 extNopBytecode + { + VM_LABEL(extNopBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + numExtB = (extA = (extB = 0)); + } + break; + case 354: // 98 bytecodePrimLessThanSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimLessThanSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheatSistaV1: */ + if (rcvr < arg) { + /* goto booleanCheatTrueSistaV1 */ + } + else { + goto booleanCheatFalseSistaV1; + } + goto l65; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatLessthanArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l65; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (4U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l65: + + booleanCheatTrueSistaV1: + /* booleanCheatTrueSistaV1 */ + { + sqInt bytecode; + sqInt offset; + + VM_LABEL(booleanCheatTrueSistaV1); + /* assume next bytecode is jumpIfFalse (99%) */ + bytecode = byteAt(++localIP); + + /* begin internalPop: */ + localSP += 2 * BytesPerOop; + if (bytecode >= 192) { + if (bytecode <= 199) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l134; + } + + /* short jumpIfFalse 192 - 199 */ + if (bytecode == 239) { + /* fetchByte */ + byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l134; + } + + /* long jumpIfFalse */ + if (bytecode == 238) { + offset = byteAt(++localIP); + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l134; + } + } + + /* not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result */ + currentBytecode = bytecode + GIV(bytecodeSetSelector); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(trueObj)); + } + /* end case */ +l134:; + break; + case 355: // 99 bytecodePrimGreaterThanSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimGreaterThanSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheatSistaV1: */ + if (rcvr > arg) { + goto booleanCheatTrueSistaV1; + } + else { + /* goto booleanCheatFalseSistaV1 */ + } + goto l66; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatGreaterthanArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l66; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (6U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l66: + + booleanCheatFalseSistaV1: + /* booleanCheatFalseSistaV1 */ + { + sqInt bytecode; + sqInt offset; + + VM_LABEL(booleanCheatFalseSistaV1); + /* assume next bytecode is jumpIfFalse (99%) */ + bytecode = byteAt(++localIP); + + /* begin internalPop: */ + localSP += 2 * BytesPerOop; + if ((bytecode < 199) + && (bytecode > 191)) { + /* begin jump: */ + localIP = (localIP + (bytecode - 191)) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l135; + } + + /* short jumpIfFalse */ + if (bytecode == 239) { + offset = byteAt(++localIP); + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l135; + } + + /* long jumpIfFalse + not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result */ + currentBytecode = bytecode + GIV(bytecodeSetSelector); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(falseObj)); + } + /* end case */ +l135:; + break; + case 356: // 100 bytecodePrimLessOrEqualSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimLessOrEqualSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheatSistaV1: */ + if (rcvr <= arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l67; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatLessOrEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l67; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (8U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l67:; + break; + case 357: // 101 bytecodePrimGreaterOrEqualSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimGreaterOrEqualSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheatSistaV1: */ + if (rcvr >= arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l68; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatGreaterOrEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l68; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (10U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l68:; + break; + case 358: // 102 bytecodePrimEqualSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimEqualSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* begin booleanCheatSistaV1: */ + if (rcvr == arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l69; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l69; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (12U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l69:; + break; + case 359: // 103 bytecodePrimNotEqualSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimNotEqualSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* begin booleanCheatSistaV1: */ + if (rcvr != arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l70; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatFalseSistaV1; + } + else { + goto booleanCheatTrueSistaV1; + } + goto l70; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (14U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l70:; + break; + case 374: // 118 bytecodePrimIdenticalSistaV1 + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimIdenticalSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = handleSpecialSelectorSendFaultForfpsp(rcvr, localFP, localSP); + } + if (/* isOopForwarded: */ + ((!(arg & (tagMask())))) + && ((!((longAt((void *)(arg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + arg = handleSpecialSelectorSendFaultForfpsp(arg, localFP, localSP); + } + + /* begin booleanCheatSistaV1: */ + if (rcvr == arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + } + break; + case 376: // 120 bytecodePrimNotIdenticalSistaV1 + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimNotIdenticalSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = handleSpecialSelectorSendFaultForfpsp(rcvr, localFP, localSP); + } + if (/* isOopForwarded: */ + ((!(arg & (tagMask())))) + && ((!((longAt((void *)(arg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + arg = handleSpecialSelectorSendFaultForfpsp(arg, localFP, localSP); + } + + /* begin booleanCheatSistaV1: */ + if (rcvr != arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + } + break; + case 440: // 184 shortConditionalJumpTrue + case 441: // 185 shortConditionalJumpTrue + case 442: // 186 shortConditionalJumpTrue + case 443: // 187 shortConditionalJumpTrue + case 444: // 188 shortConditionalJumpTrue + case 445: // 189 shortConditionalJumpTrue + case 446: // 190 shortConditionalJumpTrue + case 447: // 191 shortConditionalJumpTrue + { + sqInt boolean; + sqInt offset; + + VM_LABEL(shortConditionalJumpTrue); + offset = (currentBytecode & 7) + 1; + + /* begin jumplfTrueBy: */ + boolean = longAt(localSP); + if (boolean == GIV(trueObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(falseObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l96; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfTrueBy: */ +l96:; + } + break; + case 480: // 224 extABytecode + { + VM_LABEL(extABytecode); + extA = (((((usqInt)(extA) << 8)))) + (byteAt(++localIP)); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 481: // 225 extBBytecode + { + sqInt byte; + + VM_LABEL(extBBytecode); + byte = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + extB = ((numExtB == 0) + && (byte > 0x7F) + ? byte - 0x100 + : (((((usqInt)(extB) << 8)))) + byte); + numExtB += 1; + } + break; + case 482: // 226 extPushReceiverVariableBytecode + { + sqInt callerContextOrNil; + char *callerFP; + sqInt index; + sqInt obj; + sqInt object; + sqInt senderOop; + char *spouseFP; + StackPage *thePage; + sqInt value; + + VM_LABEL(extPushReceiverVariableBytecode); + index = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + extA = 0; + + /* begin pushMaybeContextReceiverVariable: */ + obj = longAt(localFP + FoxIFReceiver); + if ((index <= StackPointerIndex) + && (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin instVar:ofContext: */ + assert(index < MethodIndex); + assert(isContext(obj)); + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + if (!(((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + value = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if ((index == InstructionPointerIndex) + && (((((value) & 7) == 1)) + && ((((int) value)) < 0))) { + /* begin internalMustMapMachineCodePC:context: */ + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + value = mustMapMachineCodePCcontext((value >> 3), obj); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + } + object = value; + goto l99; + } + if (isWidowedContext(obj)) { + object = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + goto l99; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!index) { + /* begin ensureCallerContext: */ + callerFP = ((char *)(longAt(spouseFP + FoxSavedFP))); + if (!callerFP) { + /* begin frameCallerContext: */ + assert(isBaseFrame(spouseFP)); + thePage = stackPageAtpages(pageIndexFor(spouseFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + object = callerContextOrNil; + goto l99; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP))); + object = longAt(callerFP + FoxThisContext); + goto l99; + } + object = marryFrameSP( + callerFP, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(spouseFP))), + (spouseFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(spouseFP))->cmNumArgs) + : byteAt((spouseFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + goto l99; + } + if (index == StackPointerIndex) { + assert((ReceiverIndex + (stackPointerIndexForFrame(spouseFP))) < (lengthOf(obj))); + object = (((usqInt)(stackPointerIndexForFrame(spouseFP)) << 3) | 1); + goto l99; + } + if (index == InstructionPointerIndex) { + object = instructionPointerForFramecurrentFPcurrentIP(spouseFP, localFP, oopForPointer(localIP)); + goto l99; + } + error("bad index"); + object = 0; + /* end instVar:ofContext: */ +l99: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + else { + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))))); + } + } + break; + case 483: // 227 extPushLiteralVariableBytecode + { + sqInt index; + sqInt litVar; + + VM_LABEL(extPushLiteralVariableBytecode); + index = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + extA = 0; + + /* begin pushLiteralVariable: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((index + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(index + LiteralStart, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))))); + } + break; + case 484: // 228 extPushLiteralBytecode + { + sqInt index; + sqInt object; + + VM_LABEL(extPushLiteralBytecode); + index = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + extA = 0; + + /* begin pushLiteralConstant: */ + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((index + LiteralStart)) << (shiftForWord())))))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + break; + case 485: // 229 longPushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt index; + + VM_LABEL(longPushTemporaryVariableBytecode); + index = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),/* itemporary:in: */ + (index < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - index) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - index) * BytesPerWord)))); + } + break; + case 488: // 232 extPushIntegerBytecode + { + sqInt value; + + VM_LABEL(extPushIntegerBytecode); + value = (byteAt(++localIP)) + ((((usqInt)(extB) << 8))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + extB = 0; + numExtB = 0; + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),(((usqInt)value << 3) | 1)); + } + break; + case 489: // 233 extPushCharacterBytecode + { + sqInt object; + sqInt value; + + VM_LABEL(extPushCharacterBytecode); + value = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = ((((usqInt)(value) << (numTagBits())))) + (characterTag()); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + extA = 0; + } + break; + case 490: // 234 extSendBytecode + { + sqInt byte; + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(extSendBytecode); + byte = byteAt(++localIP); + + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((((((usqInt)(byte)) >> 3) + ((((usqInt)(extA) << 5)))) + LiteralStart)) << (shiftForWord())))))); + extA = 0; + GIV(argumentCount) = (byte & 7) + ((((usqInt)(extB) << 3))); + extB = 0; + numExtB = 0; + rcvr = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + lkupClassTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + goto commonSendOrdinary; + } + break; + case 491: // 235 extSendSuperBytecode + { + sqInt byte; + + VM_LABEL(extSendSuperBytecode); + byte = byteAt(++localIP); + + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((((((usqInt)(byte)) >> 3) + ((((usqInt)(extA) << 5)))) + LiteralStart)) << (shiftForWord())))))); + extA = 0; + if (extB >= 64) { + GIV(argumentCount) = (byte & 7) + ((((usqInt)((extB - 64)) << 3))); + extB = 0; + numExtB = 0; + + /* goto directedSuperclassSend */ + goto l101; + } + GIV(argumentCount) = (byte & 7) + ((((usqInt)(extB) << 3))); + extB = 0; + numExtB = 0; + goto superclassSend; + } + /* end case */ +l101: + + directedSuperclassSend: + /* directedSuperclassSend */ + { + sqInt aValue; + sqInt class; + sqInt err; + int hash; + sqInt objOop; + sqInt referent; + sqInt superclass; + + VM_LABEL(directedSuperclassSend); + /* begin internalPopStack */ + class = longAt(localSP); + localSP += BytesPerOop; + if ((!((longAt((void *)(class))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(class)); + referent = longAt((void *)((class + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + class = referent; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + superclass = longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(superclass)); + if ((!((longAt((void *)(superclass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + superclass = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, class, superclass); + } + + /* classTagForClass: uses ensureBehaviorHash: */ + + /* begin classTagForClass: */ + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(superclass)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + lkupClassTag = ((hash = (long32At((void *)(superclass + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(superclass) + ? ((err = enterIntoClassTable(superclass)) + ? -err + : (long32At((void *)(superclass + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + + /* To maintain the invariant that all receivers are unforwarded we need an explicit + read barrier in the super send cases. Even though we always follow receivers + on become e.g. super doSomethingWith: (self become: other) forwards the receiver + self pushed on the stack. */ + + /* begin ensureReceiverUnforwarded */ + if (/* isOopForwarded: */ + ((!((longAt(localSP + (GIV(argumentCount) * BytesPerOop))) & (tagMask())))) + && ((!((longAt((void *)(longAt(localSP + (GIV(argumentCount) * BytesPerOop))))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + referent = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + aValue = referent; + + /* begin internalStackValue:put: */ + longAtput(localSP + (GIV(argumentCount) * BytesPerOop),aValue); + } + assert(lkupClassTag != (nilObject())); + goto commonSendOrdinary; + } + break; + case 492: // 236 callMappedInlinedPrimitive + { + VM_LABEL(callMappedInlinedPrimitive); + localIP -= 2; + goto respondToUnknownBytecode; + } + break; + case 493: // 237 extUnconditionalJump + { + sqInt backwardJumpCountByte; + sqInt bcpcDelta; + sqInt byte; + sqInt header; + sqInt methodHeader; + sqInt offset; + sqInt switched; + + VM_LABEL(extUnconditionalJump); + byte = byteAt(++localIP); + offset = byte + ((((usqInt)(extB) << 8))); + bcpcDelta = (offset < 0 + ? numExtB * 2 + : 0); + extB = 0; + numExtB = 0; + localIP += offset; + + /* begin ifBackwardsCheckForEvents: */ + if ((offset + bcpcDelta) >= 0) { + goto l102; + } + if (localSP < GIV(stackLimit)) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + switched = checkForEventsMayContextSwitch(1); + returnToExecutivepostContextSwitch(1, switched); + browserPluginReturnIfNeeded(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (switched) { + goto l102; + } + } + + /* We use the least significant byte of the flags word (which is marked as an immediate) and + subtract two each time to avoid disturbing the least significant tag bit. Since the byte is + initialized to 1 (on frame build), on first decrement it will become -1. Trip when it reaches 1 again. */ + backwardJumpCountByte = byteAt(localFP + ((VMBIGENDIAN + ? (FoxIFrameFlags + BytesPerWord) - 1 + : FoxIFrameFlags))); + if (((backwardJumpCountByte -= 2)) == 1) { + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + attemptToSwitchToMachineCode(((((oopForPointer(localIP)) - (offset + bcpcDelta)) - GIV(method)) - BaseHeaderSize) - 1); + } + + /* can't cog method; avoid asking to cog it again for the longest possible time. */ + backwardJumpCountByte = 0x7F; + } + else { + if (backwardJumpCountByte == -1) { + assert(minBackwardJumpCountForCompile <= 128); + backwardJumpCountByte = ((((usqInt)((minBackwardJumpCountForCompile - 1)) << 1))) + 1; + } + } + + /* begin iframeBackwardBranchByte:put: */ + byteAtput(localFP + ((VMBIGENDIAN + ? (FoxIFrameFlags + BytesPerWord) - 1 + : FoxIFrameFlags)),backwardJumpCountByte); + /* end ifBackwardsCheckForEvents: */ +l102: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 494: // 238 extJumpIfTrue + { + sqInt boolean; + sqInt byte; + sqInt offset; + + VM_LABEL(extJumpIfTrue); + byte = byteAt(++localIP); + offset = byte + ((((usqInt)(extB) << 8))); + numExtB = (extB = (extA = 0)); + + /* begin jumplfTrueBy: */ + boolean = longAt(localSP); + if (boolean == GIV(trueObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(falseObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l103; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfTrueBy: */ +l103:; + } + break; + case 495: // 239 extJumpIfFalse + { + sqInt boolean; + sqInt byte; + sqInt offset; + + VM_LABEL(extJumpIfFalse); + byte = byteAt(++localIP); + offset = byte + ((((usqInt)(extB) << 8))); + numExtB = (extB = (extA = 0)); + + /* begin jumplfFalseBy: */ + boolean = longAt(localSP); + if (boolean == GIV(falseObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(trueObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l104; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfFalseBy: */ +l104:; + } + break; + case 496: // 240 extStoreAndPopReceiverVariableBytecode + { + sqInt obj; + int onCurrentPage; + sqInt senderOop; + char *theFP; + StackPage *thePage; + sqInt value; + sqInt variableIndex; + + VM_LABEL(extStoreAndPopReceiverVariableBytecode); + variableIndex = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + extA = 0; + value = longAt(localSP); + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + obj = longAt(localFP + FoxIFReceiver); + if ((variableIndex <= ReceiverIndex) + && ((((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + || (ismethodAssignmentToContextWithMachineCodePC(variableIndex, value))))) { + /* begin instVar:ofContext:put: */ + assert(isMarriedOrWidowedContext(obj)); + assert(!((isObjImmutable(obj)))); + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(obj))))) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, value)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),value); + if (variableIndex == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + goto l105; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!variableIndex) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + onCurrentPage = thePage == GIV(stackPage); + storeSenderOfFramewithValue(theFP, value); + if (onCurrentPage) { + localFP = (GIV(stackPage)->headFP); + localSP = (GIV(stackPage)->headSP); + } + else { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + goto l105; + } + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + externalDivorceFrameandContext(theFP, obj); + if (ismethodAssignmentToContextWithMachineCodePC(variableIndex, obj)) { + ensureContextHasBytecodePC(obj); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, value)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),value); + if (variableIndex == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + + /* Assigning various fields can force a divorce which can change the stackPage. */ + markStackPageMostRecentlyUsed(GIV(stackPage)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 1, __LINE__); + /* end instVar:ofContext:put: */ +l105:; + } + else { + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(obj)))); + if ((((usqInt)((byteAt((void *)(obj + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),obj); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((usqInt)(variableIndex + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l106; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, value)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l106:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 497: // 241 extStoreAndPopLiteralVariableBytecode + { + sqInt litVar; + sqInt value; + sqInt variableIndex; + + VM_LABEL(extStoreAndPopLiteralVariableBytecode); + variableIndex = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + value = longAt(localSP); + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + extA = 0; + + /* begin storeLiteralVariable:withValue: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((variableIndex + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(variableIndex + LiteralStart, GIV(method), litVar); + } + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(litVar)))); + if ((((usqInt)((byteAt((void *)(litVar + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),litVar); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((usqInt)2 /* (ValueIndex + 1) */ << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l107; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ValueIndex, litVar, value)); + assert(isNonImmediate(litVar)); + if (oopisGreaterThanOrEqualTo(litVar, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(litVar + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(litVar); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l107: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 498: // 242 longStoreAndPopTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt index; + + VM_LABEL(longStoreAndPopTemporaryVariableBytecode); + /* begin longStoreTemporaryVariableBytecode */ + index = byteAt(++localIP); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (index < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - index) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - index) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + break; + case 499: // 243 extStoreReceiverVariableBytecode + { + sqInt anObject; + sqInt obj; + int onCurrentPage; + sqInt senderOop; + char *theFP; + StackPage *thePage; + sqInt variableIndex; + + VM_LABEL(extStoreReceiverVariableBytecode); + variableIndex = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + extA = 0; + anObject = longAt(localSP); + + /* begin storeMaybeContextReceiverVariable:withValue: */ + obj = longAt(localFP + FoxIFReceiver); + if ((variableIndex <= ReceiverIndex) + && ((((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + || (ismethodAssignmentToContextWithMachineCodePC(variableIndex, anObject))))) { + /* begin instVar:ofContext:put: */ + assert(isMarriedOrWidowedContext(obj)); + assert(!((isObjImmutable(obj)))); + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(obj))))) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, anObject)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anObject & (tagMask())))) + && (oopisLessThan(anObject, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),anObject); + if (variableIndex == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + goto l108; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!variableIndex) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + onCurrentPage = thePage == GIV(stackPage); + storeSenderOfFramewithValue(theFP, anObject); + if (onCurrentPage) { + localFP = (GIV(stackPage)->headFP); + localSP = (GIV(stackPage)->headSP); + } + else { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + goto l108; + } + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + externalDivorceFrameandContext(theFP, obj); + if (ismethodAssignmentToContextWithMachineCodePC(variableIndex, obj)) { + ensureContextHasBytecodePC(obj); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, anObject)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anObject & (tagMask())))) + && (oopisLessThan(anObject, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),anObject); + if (variableIndex == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + + /* Assigning various fields can force a divorce which can change the stackPage. */ + markStackPageMostRecentlyUsed(GIV(stackPage)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 1, __LINE__); + /* end instVar:ofContext:put: */ +l108:; + } + else { + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(obj)))); + if ((((usqInt)((byteAt((void *)(obj + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),obj); + longAtput((localSP -= BytesPerOop),anObject); + longAtput((localSP -= BytesPerOop),(((usqInt)(variableIndex + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l109; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, anObject)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anObject & (tagMask())))) + && (oopisLessThan(anObject, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),anObject); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l109:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 500: // 244 extStoreLiteralVariableBytecode + { + sqInt anObject; + sqInt litVar; + sqInt variableIndex; + + VM_LABEL(extStoreLiteralVariableBytecode); + variableIndex = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + extA = 0; + anObject = longAt(localSP); + + /* begin storeLiteralVariable:withValue: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((variableIndex + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(variableIndex + LiteralStart, GIV(method), litVar); + } + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(litVar)))); + if ((((usqInt)((byteAt((void *)(litVar + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),litVar); + longAtput((localSP -= BytesPerOop),anObject); + longAtput((localSP -= BytesPerOop),(((usqInt)2 /* (ValueIndex + 1) */ << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l110; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ValueIndex, litVar, anObject)); + assert(isNonImmediate(litVar)); + if (oopisGreaterThanOrEqualTo(litVar, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anObject & (tagMask())))) + && (oopisLessThan(anObject, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(litVar + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(litVar); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))),anObject); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l110: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + break; + case 501: // 245 longStoreTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt index; + + VM_LABEL(longStoreTemporaryVariableBytecode); + index = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (index < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - index) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - index) * BytesPerWord),longAt(localSP)); + } + } + break; + case 505: // 249 extPushFullClosureBytecode + { + sqInt byte; + sqInt compiledBlock; + sqInt compiledBlockLiteralIndex; + sqInt context; + sqInt header; + sqInt i; + int ignoreContext; + usqInt newClosure; + usqInt newObj; + usqInt numArgs; + usqInt numBytes; + sqInt numCopied; + sqInt numCopiedSqInt; + sqInt numSlots; + int receiverIsOnStack; + sqInt startIndex; + + VM_LABEL(extPushFullClosureBytecode); + compiledBlockLiteralIndex = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + extA = 0; + + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + compiledBlock = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((compiledBlockLiteralIndex + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(compiledBlock)); + if ((!((longAt((void *)(compiledBlock))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + compiledBlock = fixFollowedFieldofObjectwithInitialValue(compiledBlockLiteralIndex + LiteralStart, GIV(method), compiledBlock); + } + assert(isOopCompiledMethod(compiledBlock)); + + /* begin argumentCountOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(compiledBlock)); + header = longAt((void *)((compiledBlock + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numArgs = (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; + byte = byteAt(++localIP); + numCopied = byte & (0x3F); + receiverIsOnStack = ((byte & (128)) != 0); + ignoreContext = ((byte & (64)) != 0); + + /* begin pushFullClosureNumArgs:copiedValues:compiledBlock:receiverIsOnStack:ignoreContext: */ + if (ignoreContext) { + context = GIV(nilObj); + } + else { + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + context = longAt(localFP + FoxThisContext); + goto l112; + } + context = marryFrameSP(localFP, localSP + (numCopied * BytesPerOop)); + /* end ensureFrameIsMarried:SP: */ +l112:; + } + + /* begin fullClosureIn:numArgs:numCopiedValues:compiledBlock: */ + numSlots = FullClosureFirstCopiedValueIndex + numCopied; + assert((numSlots >= 0) + && ((knownClassAtIndex(ClassFullBlockClosureCompactIndex)) != GIV(nilObj))); + assert((indexablePointersFormat()) == (instSpecOfClass(knownClassAtIndex(ClassFullBlockClosureCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newClosure = 0; + goto l113; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((indexablePointersFormat())) << (formatShift()))))) + ClassFullBlockClosureCompactIndex); + GIV(freeStart) += numBytes; + newClosure = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l113: + + /* Assume: have just allocated a new closure; it must be young. Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureOuterContextIndex, newClosure, context)); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord()))))),context); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(FullClosureCompiledBlockIndex, newClosure, compiledBlock)); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(FullClosureCompiledBlockIndex) << (shiftForWord()))))),compiledBlock); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureNumArgsIndex, newClosure, ((numArgs << 3) | 1))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord()))))),((numArgs << 3) | 1)); + if (receiverIsOnStack) { + startIndex = FullClosureReceiverIndex; + numCopiedSqInt = numCopied + 1; + } + else { + startIndex = FullClosureFirstCopiedValueIndex; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(FullClosureReceiverIndex, newClosure, longAt(localFP + FoxIFReceiver))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(FullClosureReceiverIndex) << (shiftForWord()))))),longAt(localFP + FoxIFReceiver)); + numCopiedSqInt = numCopied; + } + if (numCopiedSqInt > 0) { + for (i = 0; i < numCopiedSqInt; i += 1) { + /* Assume: have just allocated a new BlockClosure; it must be young. + Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(i + startIndex, newClosure, longAt((void *)(localSP + (((numCopiedSqInt - i) - 1) * BytesPerOop))))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)((i + startIndex)) << (shiftForWord()))))),longAt((void *)(localSP + (((numCopiedSqInt - i) - 1) * BytesPerOop)))); + } + + /* begin internalPop: */ + localSP += numCopiedSqInt * BytesPerOop; + } + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),newClosure); + } + break; + case 506: // 250 extPushClosureBytecode + { + sqInt blockSize; + sqInt byte; + sqInt context; + sqInt i; + sqInt initialIP; + usqInt newClosure; + usqInt newObj; + usqInt numArgs; + usqInt numBytes; + usqInt numCopied; + sqInt numSlots; + + VM_LABEL(extPushClosureBytecode); + byte = byteAt(++localIP); + numArgs = (byte & 7) + ((extA % 16) * 8); + numCopied = ((((usqInt)(byte)) >> 3) & 7) + ((extA / 16) * 8); + extA = 0; + blockSize = (byteAt(++localIP)) + ((((usqInt)(extB) << 8))); + numExtB = (extB = 0); + + /* begin pushClosureNumArgs:copiedValues:blockSize: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + context = longAt(localFP + FoxThisContext); + goto l114; + } + context = marryFrameSP(localFP, localSP + (numCopied * BytesPerOop)); + /* end ensureFrameIsMarried:SP: */ +l114: + initialIP = ((((usqInt)localIP)) + 2) - (GIV(method) + BaseHeaderSize); + + /* begin closureIn:numArgs:instructionPointer:numCopiedValues: */ + numSlots = ClosureFirstCopiedValueIndex + numCopied; + assert((numSlots >= 0) + && ((knownClassAtIndex(ClassBlockClosureCompactIndex)) != GIV(nilObj))); + assert((indexablePointersFormat()) == (instSpecOfClass(knownClassAtIndex(ClassBlockClosureCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newClosure = 0; + goto l115; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((indexablePointersFormat())) << (formatShift()))))) + ClassBlockClosureCompactIndex); + GIV(freeStart) += numBytes; + newClosure = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l115: + + /* Assume: have just allocated a new closure; it must be young. Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureOuterContextIndex, newClosure, context)); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord()))))),context); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureStartPCIndex, newClosure, (((usqInt)initialIP << 3) | 1))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))),(((usqInt)initialIP << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureNumArgsIndex, newClosure, ((numArgs << 3) | 1))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord()))))),((numArgs << 3) | 1)); + if (numCopied > 0) { + for (i = 0; i < numCopied; i += 1) { + /* Assume: have just allocated a new BlockClosure; it must be young. + Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(i + ClosureFirstCopiedValueIndex, newClosure, longAt((void *)(localSP + (((numCopied - i) - 1) * BytesPerOop))))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)((i + ClosureFirstCopiedValueIndex)) << (shiftForWord()))))),longAt((void *)(localSP + (((numCopied - i) - 1) * BytesPerOop)))); + } + + /* begin internalPop: */ + localSP += numCopied * BytesPerOop; + } + localIP += blockSize; + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),newClosure); + } + break; + } + } + + /* undo the pre-increment of IP (in fetchByte in fetchNextBytecode) before returning */ + localIP -= 1; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + return null; +} + + /* CogBlockMethod>>#cmHomeMethod */ +static NoDbgRegParms CogMethod * +cmHomeMethod(CogBlockMethod *self_in_CogBlockMethod) +{ + return (((self_in_CogBlockMethod->cmType)) >= CMMethod + ? ((CogMethod *) self_in_CogBlockMethod) + : (assert(CMBlock == (self_in_CogBlockMethod->cmType)), + ((CogMethod *) ((((usqInt)self_in_CogBlockMethod)) - ((self_in_CogBlockMethod->homeOffset)))))); +} + + /* CogBlockMethod>>#isCMBlock */ +static NoDbgRegParms int +isCMBlock(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMBlock; +} + + /* CogBlockMethod>>#isCMClosedPIC */ +static NoDbgRegParms int +isCMClosedPIC(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMClosedPIC; +} + + /* CogBlockMethod>>#isCMMethodEtAl */ +static NoDbgRegParms int +isCMMethodEtAl(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) >= CMMethod; +} + + /* CogBlockMethod>>#isCMMethodFlaggedForBecome */ +static NoDbgRegParms int +isCMMethodFlaggedForBecome(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMMethodFlaggedForBecome; +} + + /* CogBlockMethod>>#isCMOpenPIC */ +static NoDbgRegParms int +isCMOpenPIC(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMOpenPIC; +} + + +/* For assert-checking */ + + /* CogStackPage>>#addressIsInPage: */ +static NoDbgRegParms sqInt +addressIsInPage(StackPage *self_in_CogStackPage, char *address) +{ + return (((self_in_CogStackPage->lastAddress)) < address) + && (address < ((self_in_CogStackPage->baseAddress))); +} + + /* CogStackPage>>#isFree */ +static NoDbgRegParms int +isFree(StackPage *self_in_CogStackPage) +{ + return ((self_in_CogStackPage->baseFP)) == 0; +} + + +/* */ +/* MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#freeStackPageNoAssert: */ +static NoDbgRegParms sqInt +freeStackPageNoAssert(StackPage *aPage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *prev; + StackPage *self_in_CogStackPage; + + (aPage->baseFP = 0); + if (aPage == GIV(mostRecentlyUsedPage)) { + GIV(mostRecentlyUsedPage) = (GIV(mostRecentlyUsedPage)->prevPage); + return null; + } + self_in_CogStackPage = (prev = (aPage->prevPage)); + if (!((self_in_CogStackPage->baseFP))) { + return null; + } + (prev->nextPage = (aPage->nextPage)); + (((aPage->nextPage))->prevPage = prev); + (aPage->nextPage = (GIV(mostRecentlyUsedPage)->nextPage)); + (((GIV(mostRecentlyUsedPage)->nextPage))->prevPage = aPage); + (aPage->prevPage = GIV(mostRecentlyUsedPage)); + (GIV(mostRecentlyUsedPage)->nextPage = aPage); + return 0; +} + + +/* */ +/* MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#freeStackPage: */ +static NoDbgRegParms void +freeStackPage(StackPage *aPage) +{ + freeStackPageNoAssert(aPage); + assert(pageListIsWellFormed()); +} + + +/* */ +/* MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#markStackPageMostRecentlyUsed: */ +static NoDbgRegParms sqInt +markStackPageMostRecentlyUsed(StackPage *page) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (page == GIV(mostRecentlyUsedPage)) { + return null; + } + + /* Common case; making new page most recently used. */ + if (((page->prevPage)) == GIV(mostRecentlyUsedPage)) { + GIV(mostRecentlyUsedPage) = page; + assert(pageListIsWellFormed()); + return null; + } + (((page->prevPage))->nextPage = (page->nextPage)); + (((page->nextPage))->prevPage = (page->prevPage)); + (((GIV(mostRecentlyUsedPage)->nextPage))->prevPage = page); + (page->prevPage = GIV(mostRecentlyUsedPage)); + (page->nextPage = (GIV(mostRecentlyUsedPage)->nextPage)); + (GIV(mostRecentlyUsedPage)->nextPage = page); + GIV(mostRecentlyUsedPage) = page; + assert(pageListIsWellFormed()); + return 0; +} + + +/* */ +/* This method is used to move a page to a position in the list such that it + cannot be deallocated when a new page is allocated, without changing the + most recently + used page. There must be at least 3 pages in the system. So making the + page the MRU's prevPage is sufficient to ensure it won't be deallocated. */ +/* MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#markStackPageNextMostRecentlyUsed: */ +static NoDbgRegParms sqInt +markStackPageNextMostRecentlyUsed(StackPage *page) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(page != GIV(mostRecentlyUsedPage)); + if (((page->nextPage)) == GIV(mostRecentlyUsedPage)) { + return null; + } + (((page->prevPage))->nextPage = (page->nextPage)); + (((page->nextPage))->prevPage = (page->prevPage)); + (((GIV(mostRecentlyUsedPage)->prevPage))->nextPage = page); + (page->prevPage = (GIV(mostRecentlyUsedPage)->prevPage)); + (page->nextPage = GIV(mostRecentlyUsedPage)); + (GIV(mostRecentlyUsedPage)->prevPage = page); + assert(pageListIsWellFormed()); + return 0; +} + + +/* MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#newStackPage */ +static StackPage * +newStackPage(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *lruOrFree; + + lruOrFree = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((lruOrFree->baseFP))) { + return lruOrFree; + } + divorceFramesIn(lruOrFree); + return lruOrFree; +} + + +/* Answer if the stack page list is well-formed. + MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#pageListIsWellFormed */ +static sqInt +pageListIsWellFormed(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt count; + sqInt limit; + sqInt ok; + StackPage *page; + + ok = 1; + page = (GIV(mostRecentlyUsedPage)->nextPage); + count = 1; + limit = GIV(numStackPages) * 2; + while ((((page->baseFP)) == 0) + && ((page != GIV(mostRecentlyUsedPage)) + && (count <= limit))) { + if (!(asserta(((((page->nextPage))->prevPage)) == page))) { + ok = 0; + } + page = (page->nextPage); + count += 1; + } + while ((page != GIV(mostRecentlyUsedPage)) + && (count <= limit)) { + if (!(asserta(((((page->nextPage))->prevPage)) == page))) { + ok = 0; + } + if (asserta(!(isFree(page)))) { + if (!(asserta((addressIsInPage(page, (page->baseFP))) + && (addressIsInPage(page, (page->headSP)))))) { + ok = 0; + } + } + else { + ok = 0; + } + page = (page->nextPage); + count += 1; + } + if (!(asserta(count == (numStkPages())))) { + ok = 0; + } + return ok; +} + + +/* Answer the page for a page index. + N.B. This is a zero-relative index. */ + + /* CogStackPages>>#stackPageAt: */ +static NoDbgRegParms StackPage * +stackPageAt(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return stackPageAtpages(index, GIV(pages)); +} + + +/* */ + + /* CogStackPages>>#stackPageFor: */ +static NoDbgRegParms StackPage * +stackPageFor(void *pointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return stackPageAtpages(pageIndexFor(pointer), GIV(pages)); +} + + /* CogStackPages>>#statAverageLivePagesWhenMapping */ +static double +statAverageLivePagesWhenMapping(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (GIV(statNumMaps) + ? (((double) GIV(statPageCountWhenMappingSum) )) / GIV(statNumMaps) + : 0.0); +} + + +/* Initialization is completed by initializing the stack zone on entry to the + interpreter. Answer when this has been done. This allows e.g. the crash + dump processing to + be avoided until the system has been initialized, easing debugging of new + ports as they fail in early system initialization attempts. */ + + /* CogStackPages>>#vmIsInitialized */ +int +vmIsInitialized(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return !!GIV(pages); +} + + /* CogThreadManager>>#affinedThreadOrNilForProcess:currentAffinity:oop: */ +static NoDbgRegParms CogVMThread * +affinedThreadOrNilForProcesscurrentAffinityoop(sqInt aProcess, sqInt currentAffinity, sqInt threadAffinityOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + CogVMThread *vmThread; + + if (currentAffinity > 0) { + if ((((currentAffinity >= 1) && (currentAffinity <= GIV(numThreads)))) + && (aProcess == (((vmThread = vmThreadAt(currentAffinity))->boundProcess)))) { + assert(((vmThread->affinityOnBind)) == threadAffinityOop); + return vmThread; + } + return null; + } + for (i = 1; i <= GIV(numThreads); i += 1) { + if ((((vmThread = vmThreadAt(i))->boundProcess)) == aProcess) { + assert(((vmThread->affinityOnBind)) == threadAffinityOop); + return vmThread; + } + } + return null; +} + + /* CogThreadManager>>#currentVMThread */ +static CogVMThread * +currentVMThread(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + + index = atomic_load((&GIV(vmOwner))); + + /* begin vmThreadAt: */ + assert(((index >= 0) && (index <= GIV(numThreads)))); + return (index > 0 + ? GIV(threads)[index] + : 0); +} + + +/* Useful for debugging!! */ + + /* CogThreadManager>>#dumpOwnerLog */ +void +dumpOwnerLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + dumpOwnerLogOn(GIV(transcript)); +} + + +/* Useful for debugging!! */ + + /* CogThreadManager>>#dumpOwnerLogMax: */ +void +dumpOwnerLogMax(sqInt maxEntries) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + dumpOwnerLogOnMax(GIV(transcript), maxEntries); +} + + +/* Useful for debugging!! */ + + /* CogThreadManager>>#dumpOwnerLogOn: */ +void +dumpOwnerLogOn(FILE *aFile) +{ + dumpOwnerLogOnMax(aFile, OwnerLogSize); +} + + +/* Print the last at most maxElements entires of the owner log. + Useful for debugging!! */ + + /* CogThreadManager>>#dumpOwnerLogOn:Max: */ +void +dumpOwnerLogOnMax(FILE *aFile, sqInt maxEntries) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogVMOwnerLog *entry; + sqInt i; + sqInt index; + sqInt limit; + sqInt numWrappedToPrint; + + if (GIV(ownerLog)) { + index = atomic_load((&GIV(ownerLogIndex))); + if ((index == 0) + && ((((GIV(ownerLog)[index]).timestamp)) == 0)) { + fprintf(aFile, "owner log is empty\n"); + return; + } + assert(((index >= 0) && (index <= (OwnerLogSize - 1)))); + if (GIV(ownerLogWrapped)) { + numWrappedToPrint = (((maxEntries - (index + 1)) < 0) ? 0 : (maxEntries - (index + 1))); + limit = (OwnerLogSize - 1) - numWrappedToPrint; + for (i = (index + 1); i < OwnerLogSize; i += 1) { + if (i > limit) { + entry = (&(GIV(ownerLog)[i])); + fprintf(aFile, + "ts: %llu %d %s\n", + ((entry->timestamp)) - GIV(startTimestamp), + (entry->vmOwner), + ((entry->successfulSwitch) + ? "ok" + : "-")); + } + } + } + else { + numWrappedToPrint = 0; + } + limit = (index - maxEntries) - numWrappedToPrint; + for (i = 0; i <= index; i += 1) { + if (i >= limit) { + entry = (&(GIV(ownerLog)[i])); + fprintf(aFile, + "ts: %llu %d %s\n", + ((entry->timestamp)) - GIV(startTimestamp), + (entry->vmOwner), + ((entry->successfulSwitch) + ? "ok" + : "-")); + } + } + } + fprintf(aFile, + "vmOwner: %d\n", + GIV(vmOwner)); +} + + /* CogThreadManager>>#ensureWillingThread */ +static CogVMThread * +ensureWillingThread(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt indexSqInt; + sqInt newIndex; + CogVMThread *willingThread; + + willingThread = willingVMThread(); + if (willingThread) { + return willingThread; + } + for (index = 1; index <= GIV(numThreads); index += 1) { + if (!(vmThreadState(vmThreadAt(index)))) { + /* begin startThreadForThreadIndex: */ + assert(index > 0); + if (index > GIV(numThreads)) { + if (!(growThreadInfosToAtLeast(index))) { + goto l1; + } + } + startThreadForThreadInfo(vmThreadAt(index)); + /* end startThreadForThreadIndex: */ +l1: + + /* begin vmThreadAt: */ + assert(((index >= 0) && (index <= GIV(numThreads)))); + return (index > 0 + ? GIV(threads)[index] + : 0); + } + } + indexSqInt = (newIndex = GIV(numThreads) + 1); + + /* begin startThreadForThreadIndex: */ + assert(indexSqInt > 0); + if (indexSqInt > GIV(numThreads)) { + if (!(growThreadInfosToAtLeast(indexSqInt))) { + goto l2; + } + } + startThreadForThreadInfo(vmThreadAt(indexSqInt)); + /* end startThreadForThreadIndex: */ +l2: + + /* begin vmThreadAt: */ + assert(((newIndex >= 0) && (newIndex <= GIV(numThreads)))); + return (newIndex > 0 + ? GIV(threads)[newIndex] + : 0); +} + + +/* This is for signalSemaphoreWithIndex in Cross/vm/sqExternalSemaphores.c */ + + /* CogThreadManager>>#getVMOSThread */ +sqOSThread +getVMOSThread(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(vmOSThread); +} + + /* CogThreadManager>>#getVMOwner */ +static sqInt +getVMOwner(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return atomic_load((&GIV(vmOwner))); +} + + +/* Grow the thread infos to at least index in numThreadsIncrement quanta. */ + + /* CogThreadManager>>#growThreadInfosToAtLeast: */ +static NoDbgRegParms sqInt +growThreadInfosToAtLeast(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt j; + sqInt newNumThreads; + CogVMThread **newThreads; + sqInt nThreads; + CogVMThread *vmThread; + CogVMThread *vmThreads; + + if (GIV(memoryIsScarce)) { + return 0; + } + newNumThreads = (((index + GIV(numThreadsIncrement)) - 1) / GIV(numThreadsIncrement)) * GIV(numThreadsIncrement); + if (newNumThreads >= ((1U << ThreadIdShift) - 1)) { + return 0; + } + + /* Since 0 is not a valid index we allocate one extra CogVMThread and use 1-relative indices. */ + newThreads = realloc(GIV(threads), (newNumThreads + 1) * sizeof(CogVMThread *)); + if (!newThreads) { + GIV(memoryIsScarce) = 1; + return 0; + } + GIV(threads) = newThreads; + + /* begin populate:from:to: */ + nThreads = (newNumThreads - (GIV(numThreads) + 1)) + 1; + vmThreads = calloc(nThreads, sizeof(CogVMThread)); + if (!vmThreads) { + goto l2; + } + + /* Since 0 is not a valid index, in C we allocate one extra CogVMThread and use 1-relative indices. */ + if ((GIV(numThreads) + 1) == 1) { + newThreads[0] = null; + } + for (i = (GIV(numThreads) + 1); i <= newNumThreads; i += 1) { + vmThread = (&(vmThreads[i - (GIV(numThreads) + 1)])); + initializeThreadState(vmThread); + if (ioNewOSSemaphore((&((vmThread->osSemaphore))))) { + for (j = (GIV(numThreads) + 1); j < i; j += 1) { + vmThread = (&(vmThreads[j - (GIV(numThreads) + 1)])); + ioDestroyOSSemaphore((&((vmThread->osSemaphore)))); + } + free(vmThreads); + goto l2; + } + newThreads[i] = vmThread; + (vmThread->index = i); + } + GIV(numThreads) = newNumThreads; + return 1; + goto l1; +l2: + + /* Allocation of new threads may fail, even after the array has been moved. + If this is the case, simply do not increase the number of useable threads. + The old ones will still point to the right addresses, they'll just be in a new list + which technically has too much space, but that doesn't hurt anything. */ + GIV(memoryIsScarce) = 1; + return 0; +l1:; + return 0; +} + + /* CogThreadManager>>#saveOwnerSwitchTo:successful: */ +static NoDbgRegParms sqInt +saveOwnerSwitchTosuccessful(sqInt newOwner, sqInt aBoolean) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int currentIndex; + CogVMOwnerLog *logEntry; + int newIndex; + sqInt timestamp; + + timestamp = ioUTCMicrosecondsNow(); + currentIndex = atomic_load((&GIV(ownerLogIndex))); + do { + newIndex = (currentIndex + 1) % OwnerLogSize; + } while(!(atomic_compare_exchange_strong((&GIV(ownerLogIndex)), (¤tIndex), newIndex))); + if (newIndex < currentIndex) { + GIV(ownerLogWrapped) = 1; + } + logEntry = (&(GIV(ownerLog)[currentIndex])); + (logEntry->timestamp = timestamp); + (logEntry->successfulSwitch = aBoolean); + (logEntry->vmOwner = newOwner); + return 0; +} + + /* CogThreadManager>>#startThreadForThreadIndex: */ +static NoDbgRegParms sqInt +startThreadForThreadIndex(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(index > 0); + if (index > GIV(numThreads)) { + if (!(growThreadInfosToAtLeast(index))) { + return 0; + } + } + return startThreadForThreadInfo(vmThreadAt(index)); +} + + /* CogThreadManager>>#startThreadForThreadInfo: */ +static NoDbgRegParms sqInt +startThreadForThreadInfo(CogVMThread *vmThread) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentState; + + if (atomic_load((&((vmThread->state))))) { + return 1; + } + assert((vmThreadState(vmThread)) == CTMUninitialized); + + /* begin setVmThreadState: */ + currentState = atomic_load((&((vmThread->state)))); + if (currentState != CTMInitializing) { + switch (currentState) { + case CTMUninitialized: + break; + case CTMInitializing: + case CTMWantingOwnership: + assert(0 /* (CTMInitializing = CTMAssignableOrInVM) */); + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMInitializing = CTMUnavailable) */); + break; + case CTMUnavailable: + assert(0 /* (CTMInitializing = CTMWantingOwnership) */); + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((vmThread->state))), CTMInitializing); + + /* self cCode: '' + inSmalltalk: [coInterpreter transcript + cr; + nextPutAll: 'starting VM thread '; + print: vmThread index; + flush. + (thisContext home stackOfSize: 10) do: + [:ctxt| + coInterpreter transcript cr; print: ctxt; flush]]. */ + if (!(ioNewOSThread(((void (*)(void*)) startVMThread), vmThread))) { + ioTransferTimeslice(); + return 1; + } + GIV(memoryIsScarce) = 1; + + /* self cCode: [coInterpreter print: 'ERVT failed to spawn so memory is scarce'; cr] */ + return 0; +} + + +/* Initialize the threading subsystem, aborting if there is an error. */ + + /* CogThreadManager>>#startThreadSubsystem */ +static sqInt +startThreadSubsystem(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentState; + CogVMThread *vmThread; + + assert(GIV(threads) == null); + GIV(vmOSThread) = ioCurrentOSThread(); + GIV(numThreadsIncrement) = ((((((ioNumProcessors()) < 2) ? 2 : (ioNumProcessors()))) < 16) ? ((((ioNumProcessors()) < 2) ? 2 : (ioNumProcessors()))) : 16); + if (!(growThreadInfosToAtLeast(GIV(numThreadsIncrement) * 2))) { + error("no memory to start thread system"); + } + atomic_store((&GIV(vmOwner)), 1); + vmThread = GIV(threads)[atomic_load((&GIV(vmOwner)))]; + + /* begin setVmThreadState: */ + currentState = atomic_load((&((vmThread->state)))); + if (currentState != CTMInitializing) { + switch (currentState) { + case CTMUninitialized: + break; + case CTMInitializing: + case CTMWantingOwnership: + assert(0 /* (CTMInitializing = CTMAssignableOrInVM) */); + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMInitializing = CTMUnavailable) */); + break; + case CTMUnavailable: + assert(0 /* (CTMInitializing = CTMWantingOwnership) */); + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((vmThread->state))), CTMInitializing); + + /* begin registerVMThread: */ + assert((((vmThreadState(vmThread)) == CTMInitializing) + || ((vmThreadState(vmThread)) == CTMWantingOwnership)) + && (!((vmThread->osThread)))); + (vmThread->osThread = ioCurrentOSThread()); + ioThreadLocalSetThreadIndex((vmThread->index)); + assert((ioThreadLocalGetThreadIndex()) == ((vmThread->index))); + + /* begin setVmThreadState: */ + currentState = atomic_load((&((vmThread->state)))); + if (currentState != CTMAssignableOrInVM) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMAssignableOrInVM = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMAssignableOrInVM = CTMUnavailable) */); + break; + case CTMUnavailable: + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((vmThread->state))), CTMAssignableOrInVM); + + /* begin initializeOwnerLog */ + if (enableOwnerLog) { + if (!GIV(startTimestamp)) { + GIV(startTimestamp) = ioUTCMicrosecondsNow(); + } + GIV(ownerLog) = calloc(OwnerLogSize, sizeof(CogVMOwnerLog)); + atomic_store((&GIV(ownerLogIndex)), 0); + GIV(ownerLogWrapped) = 0; + } + return 0; +} + + +/* Start a VM thread that will attempt to acquire the VM and proceed + to run the VM, taking processes from the runnable process queue. */ + + /* CogThreadManager>>#startVMThread: */ +static NoDbgRegParms void +startVMThread(CogVMThread *vmThread) +{ + sqInt currentState; + + assert((vmThreadState(vmThread)) == CTMInitializing); + + /* begin registerVMThread: */ + assert((((vmThreadState(vmThread)) == CTMInitializing) + || ((vmThreadState(vmThread)) == CTMWantingOwnership)) + && (!((vmThread->osThread)))); + (vmThread->osThread = ioCurrentOSThread()); + ioThreadLocalSetThreadIndex((vmThread->index)); + assert((ioThreadLocalGetThreadIndex()) == ((vmThread->index))); + + /* begin setVmThreadState: */ + currentState = atomic_load((&((vmThread->state)))); + if (currentState != CTMAssignableOrInVM) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMAssignableOrInVM = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMAssignableOrInVM = CTMUnavailable) */); + break; + case CTMUnavailable: + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((vmThread->state))), CTMAssignableOrInVM); + threadSchedulingLoop(vmThread); +} + + +/* Test if threadAffinity is ok to run on a thread with the given index. */ + + /* CogThreadManager>>#threadIndex:isCompatibleWith: */ +static NoDbgRegParms sqInt +threadIndexisCompatibleWith(sqInt aThreadIndex, sqInt threadAffinity) +{ + assert(aThreadIndex > 0); + return (threadAffinity == 0) + || ((threadAffinity >= 0 + ? aThreadIndex == threadAffinity + : aThreadIndex != (-threadAffinity))); +} + + +/* Answer a pointer to an unused CogVMThread, growing the sequence if + necessary. + */ + + /* CogThreadManager>>#unusedThreadInfo */ +static CogVMThread * +unusedThreadInfo(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt index; + CogVMThread *vmThread; + + for (i = 1; i <= GIV(numThreads); i += 1) { + vmThread = GIV(threads)[i]; + assert(((vmThread->index)) == i); + if (!(atomic_load((&((vmThread->state)))))) { + return vmThread; + } + } + index = GIV(numThreads) + 1; + if (!(growThreadInfosToAtLeast(GIV(numThreads) + GIV(numThreadsIncrement)))) { + return null; + } + return GIV(threads)[index]; +} + + +/* Answer if the vm is owned */ +/* This is used in the platform support code */ + + /* CogThreadManager>>#vmIsOwned */ +sqInt +vmIsOwned(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (atomic_load((&GIV(vmOwner)))) > 0; +} + + +/* Answer if the vm is owned or is locked via CTMUnknownOwner. + Try to use this in asserts only. */ + + /* CogThreadManager>>#vmIsOwnedOrLocked */ +static int +vmIsOwnedOrLocked(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (atomic_load((&GIV(vmOwner)))) != 0; +} + + +/* Test if the vmOwner is index. */ + + /* CogThreadManager>>#vmOwnerIsCompatibleWith: */ +static NoDbgRegParms sqInt +vmOwnerIsCompatibleWith(sqInt processThreadId) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aThreadIndex; + + aThreadIndex = atomic_load((&GIV(vmOwner))); + + /* begin threadIndex:isCompatibleWith: */ + assert(aThreadIndex > 0); + return (processThreadId == 0) + || ((processThreadId >= 0 + ? aThreadIndex == processThreadId + : aThreadIndex != (-processThreadId))); +} + + +/* Test if the vmOwner is index. */ + + /* CogThreadManager>>#vmOwnerIs: */ +static NoDbgRegParms int +vmOwnerIs(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (atomic_load((&GIV(vmOwner)))) == index; +} + + /* CogThreadManager>>#vmThreadAt: */ +static NoDbgRegParms CogVMThread * +vmThreadAt(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(((index >= 0) && (index <= GIV(numThreads)))); + return (index > 0 + ? GIV(threads)[index] + : 0); +} + + /* CogThreadManager>>#wakeVMThread: */ +static NoDbgRegParms void +wakeVMThread(CogVMThread *vmThread) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt indexOrZero; + sqInt threadState; + + assert((vmIsOwnedOrLocked()) + && (!(vmOwnerIs((vmThread->index))))); + + /* Instead of going through a #disownVM: call, directly set the new VM owner. + This has the advantage of avoiding a race for the different threads to become the new + VM owner. + In Simulation, this means we need to simulate a thread-switch. */ + indexOrZero = (vmThread->index); + + /* begin setVMOwner: */ + assert(((getVMOwner()) == CTMUnknownOwner) + || ((getVMOwner()) == (ioThreadLocalGetThreadIndex()))); + assert((getVMOwner()) != indexOrZero); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(indexOrZero, 1); + } + + /* TODO: We could make this a `release` ordering, which may perform better on ARM. */ + atomic_store((&GIV(vmOwner)), indexOrZero); + threadState = atomic_load((&((vmThread->state)))); + if (threadState) { + assert(((threadState == CTMWantingOwnership) + || (threadState == CTMAssignableOrInVM)) + || (threadState == CTMInitializing)); + ioSignalOSSemaphore((&((vmThread->osSemaphore)))); + } + else { + if (!(startThreadForThreadInfo(vmThread))) { + /* begin releaseVM */ + /* begin setVMOwner: */ + assert(((getVMOwner()) == CTMUnknownOwner) + || ((getVMOwner()) == (ioThreadLocalGetThreadIndex()))); + assert((getVMOwner()) != 0); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(0, 1); + } + + /* TODO: We could make this a `release` ordering, which may perform better on ARM. */ + atomic_store((&GIV(vmOwner)), 0); + } + } + ioTransferTimeslice(); +} + + +/* Answer a pointer to a live CogVMThread in any of the ``will do VM work'' + states (other than the current owner if the VM is owned), or nil if none. + Preferentially answer threads wanting ownership. */ + + /* CogThreadManager>>#willingVMThread */ +static CogVMThread * +willingVMThread(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + CogVMThread *thread; + CogVMThread *threadWantingVM; + CogVMThread *threadWilling; + + threadWantingVM = (threadWilling = null); + for (i = 1; i <= GIV(numThreads); i += 1) { + if (!((atomic_load((&GIV(vmOwner)))) == i)) { + thread = GIV(threads)[i]; + if ((atomic_load((&((thread->state))))) == CTMWantingOwnership) { + if ((!threadWantingVM) + || (((threadWantingVM->priority)) < ((thread->priority)))) { + threadWantingVM = thread; + } + } + if ((atomic_load((&((thread->state))))) == CTMAssignableOrInVM) { + if ((!threadWilling) + || (((threadWilling->priority)) < ((thread->priority)))) { + threadWilling = thread; + } + } + } + } + return (!(threadWantingVM) + ? threadWilling + : threadWantingVM); +} + + +/* Unfortunately this cannot be inlined as Slang otherwise screws up the + generation of the `atomic_store` call. + */ +/* In comparision to #initialize, this is also called in C code to initialize + the VMThread, not just in the Smalltalk simulation. + */ + + /* CogVMThread>>#initializeThreadState */ +static NoDbgRegParms CogVMThread * +initializeThreadState(CogVMThread *self_in_CogVMThread) +{ + atomic_store((&((self_in_CogVMThread->state))), CTMUninitialized); + return self_in_CogVMThread; +} + + /* CogVMThread>>#vmThreadState */ +static NoDbgRegParms sqInt +vmThreadState(CogVMThread *self_in_CogVMThread) +{ + return atomic_load((&((self_in_CogVMThread->state)))); +} + + /* CoInterpreter>>#accessorDepthForPrimitiveIndex: */ +sqInt +accessorDepthForPrimitiveIndex(sqInt primIndex) +{ + return ((sqInt)((primitiveMetadataTable[primIndex]))) >> SpurPrimitiveAccessorDepthShift; +} + + +/* Activate newMethod when newMethod has been cogged, i.e. create a + machine-code frame and (re)enter machine-code. + */ + + /* CoInterpreter>>#activateNewCogMethod:inInterpreter: */ +static NoDbgRegParms void +activateNewCogMethodinInterpreter(CogMethod *cogMethod, sqInt inInterpreter) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int i; + sqInt methodHeader; + usqInt numTemps; + sqInt object; + sqInt rcvr; + char *sp; + sqInt switched; + + assert((rawHeaderOf(GIV(newMethod))) == (((sqInt)cogMethod))); + methodHeader = (cogMethod->methodHeader); + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (((cogMethod->cmNumArgs)) * BytesPerWord)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + if (!((cogMethod->stackCheckOffset))) { + /* frameless method; nothing to activate... */ + if ((numRegArgs()) > 0) { + if (((cogMethod->cmNumArgs)) <= (numRegArgs())) { + callRegisterArgCogMethodatreceiver(cogMethod, noCheckEntryOffset(), rcvr); + } + } + + /* dont use and: so as to get Slang to inline cogit numRegArgs > 0 */ + object = (((usqInt)cogMethod)) + (noCheckEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + ceCallCogCodePopReceiverReg(); + error("should not be reached"); + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),((usqInt)cogMethod)); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + for (i = (((cogMethod->cmNumArgs)) + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + if ((((methodHeader & AlternateHeaderHasPrimFlag) != 0)) + && (GIV(primFailCode) != 0)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeader); + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc). */ + if (GIV(stackPointer) >= GIV(stackLimit)) { + assert(((cogMethod->stackCheckOffset)) > (noCheckEntryOffset())); + object = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + ceEnterCogCodePopReceiverReg(); + error("should not be reached"); + } + GIV(instructionPointer) = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + switched = handleStackOverflowOrEventAllowContextSwitch(canContextSwitchIfActivatingheader(GIV(newMethod), methodHeader)); + returnToExecutivepostContextSwitch(inInterpreter, switched); +} + + /* CoInterpreter>>#activateNewMethod */ +static void +activateNewMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + usqInt i; + int inInterpreter; + sqInt methodHeader; + usqInt numArgs; + usqInt numTemps; + sqInt object; + sqInt rcvr; + sqInt switched; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeader)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(!(isOopForwarded(rcvr))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeader); + } + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc). */ + switched = 1; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(canContextSwitchIfActivatingheader(GIV(newMethod), methodHeader)); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); +} + + +/* Add the given entry to the method cache. + The policy is as follows: + Look for an empty entry anywhere in the reprobe chain. + If found, install the new entry there. + If not found, then install the new entry at the first probe position + and delete the entries in the rest of the reprobe chain. + This has two useful purposes: + If there is active contention over the first slot, the second + or third will likely be free for reentry after ejection. + Also, flushing is good when reprobe chains are getting full. */ +/* Override to refuse to cache other than compiled methods. + This protects open PICs against having to test for compiled methods. */ + + /* CoInterpreter>>#addNewMethodToCache: */ +static NoDbgRegParms void +addNewMethodToCache(sqInt classObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTag; + sqInt err; + usqInt firstBytecode; + sqInt hash; + int hashInt; + sqInt header; + sqInt methodHeader; + sqInt p; + sqInt primitiveIndex; + usqInt probe; + + if (!(/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + primitiveFunctionPointer = primitiveInvokeObjectAsMethod; + return; + } + + /* begin classTagForClass: */ + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classTag = ((hashInt = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hashInt + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + + /* begin methodCacheHashOf:with: */ + hash = GIV(messageSelector) ^ ((((usqInt)(classTag) << 2))); + if (/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) { + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]))); + } + else { + assert(!((isNonImmediate(GIV(newMethod))) + && (isForwarded(GIV(newMethod))))); + primitiveFunctionPointer = primitiveInvokeObjectAsMethod; + } + for (p = 0; p < CacheProbeMax; p += 1) { + probe = (((usqInt)(hash)) >> p) & MethodCacheMask; + if (!(GIV(methodCache)[probe + MethodCacheSelector])) { + /* Found an empty entry -- use it */ + GIV(methodCache)[probe + MethodCacheSelector] = GIV(messageSelector); + GIV(methodCache)[probe + MethodCacheClass] = (classTagForClass(classObj)); + GIV(methodCache)[probe + MethodCacheMethod] = GIV(newMethod); + GIV(methodCache)[probe + MethodCachePrimFunction] = (((sqIntptr_t) primitiveFunctionPointer)); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = probe; + return; + } + } + + /* OK, we failed to find an entry -- install at the first slot... */ + + /* first probe */ + probe = hash & MethodCacheMask; + GIV(methodCache)[probe + MethodCacheSelector] = GIV(messageSelector); + GIV(methodCache)[probe + MethodCacheClass] = (classTagForClass(classObj)); + GIV(methodCache)[probe + MethodCacheMethod] = GIV(newMethod); + GIV(methodCache)[probe + MethodCachePrimFunction] = (((sqIntptr_t) primitiveFunctionPointer)); + + /* this for primitiveExternalMethod */ + /* ...and zap the following entries */ + GIV(lastMethodCacheProbeWrite) = probe; + for (p = 1; p < CacheProbeMax; p += 1) { + probe = (((usqInt)(hash)) >> p) & MethodCacheMask; + GIV(methodCache)[probe + MethodCacheSelector] = 0; + } +} + + /* CoInterpreter>>#argumentCountAddress */ +usqInt +argumentCountAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(argumentCount)))); +} + + +/* Coerce either a CMMethod or a CMBlock to the home CMMethod */ + + /* CoInterpreter>>#asCogHomeMethod: */ +static NoDbgRegParms CogMethod * +asCogHomeMethod(CogBlockMethod *aCogMethod) +{ + return (((aCogMethod->cmType)) >= CMMethod + ? ((CogMethod *) aCogMethod) + : /* cmHomeMethod */ + (((aCogMethod->cmType)) >= CMMethod + ? ((CogMethod *) aCogMethod) + : (assert(CMBlock == (aCogMethod->cmType)), + ((CogMethod *) ((((usqInt)aCogMethod)) - ((aCogMethod->homeOffset))))))); +} + + /* CoInterpreter>>#assertValidExecutionPointe:r:s:imbar:line: */ +static NoDbgRegParms void +assertValidExecutionPointersimbarline(usqInt lip, char *lifp, char *lisp, sqInt inInterpreter, sqInt ln) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cogMethod; + sqInt methodField; + sqInt methodFieldSqInt; + usqInt theIP; + + assertl(GIV(stackPage) == (GIV(mostRecentlyUsedPage)), ln); + assertl(addressIsInPage(GIV(stackPage), lifp), ln); + assert(deferStackLimitSmashAroundwith(assertValidStackLimits, ln)); + assertl(lisp < lifp, ln); + assertl(lifp > lisp, ln); + assertl(lisp >= (((GIV(stackPage)->realStackLimit)) - (stackLimitOffset())), ln); + assertl(((lifp - lisp) / BytesPerOop) < LargeContextSlots, ln); + methodField = longAt(lifp + FoxMethod); + if (inInterpreter) { + assertl(!(isMachineCodeFrame(lifp)), ln); + assert(lisp <= (lifp + FoxIFReceiver)); + assertl(GIV(method) == methodField, ln); + assertl((methodUsesAlternateBytecodeSet(GIV(method))) == (GIV(bytecodeSetSelector) == 0x100), ln); + if (assertal(cheapAddressCouldBeInHeap(methodField), ln)) { + theIP = (lip == (ceReturnToInterpreterPC()) + ? ((usqInt)(longAt(lifp + FoxIFSavedIP))) + : lip); + assertl((theIP >= (methodField + (lastPointerOf(methodField)))) + && (theIP < (((methodField + (numBytesOfBytes(methodField))) + BaseHeaderSize) - 1)), ln); + } + assertl((iframeIsBlockActivation(lifp)) + || ((pushedReceiverOrClosureOfFrame(lifp)) == (iframeReceiver(lifp))), ln); + } + else { + assertl(isMachineCodeFrame(lifp), ln); + assert(lisp <= (lifp + FoxMFReceiver)); + if ((assertal((((usqInt)methodField)) >= (minCogMethodAddress()), ln)) + && (assertal((((usqInt)methodField)) < (maxCogMethodAddress()), ln))) { + /* begin mframeHomeMethod: */ + methodFieldSqInt = longAt(lifp + FoxMethod); + if (methodFieldSqInt & MFMethodFlagIsBlockFlag) { + cogMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodFieldSqInt & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodFieldSqInt & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodFieldSqInt & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodFieldSqInt & MFMethodMask))))) - (((((CogBlockMethod *) (methodFieldSqInt & MFMethodMask)))->homeOffset)))))); + goto l1; + } + cogMethod = ((CogMethod *) (methodFieldSqInt & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + assertl((lip > (methodField + ((mframeIsBlockActivation(lifp) + ? sizeof(CogBlockMethod) + : sizeof(CogMethod))))) + && (lip < (methodField + ((cogMethod->blockSize)))), ln); + } + assertl((mframeIsBlockActivation(lifp)) + || ((pushedReceiverOrClosureOfFrame(lifp)) == (mframeReceiver(lifp))), ln); + } + if (!(longAt(lifp + FoxSavedFP))) { + assertl(frameHasContext(lifp), ln); + assertl((frameContext(lifp)) == (longAt(((GIV(stackPage)->baseAddress)) - BytesPerWord)), ln); + } +} + + +/* Check that the stacked instruction pointers in the given page are correct. + Checks the interpreter sender/machine code callee contract. */ + + /* CoInterpreter>>#assertValidStackedInstructionPointersIn:line: */ +static NoDbgRegParms sqInt +assertValidStackedInstructionPointersInline(StackPage *aStackPage, sqInt ln) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt methodField; + usqInt methodObj; + sqInt prevFrameWasCogged; + char *theFP; + usqInt theIP; + CogMethod *theMethod; + + callerFP = ((char *) 0); + if (!(assertal(!(isFree(aStackPage)), ln))) { + return 0; + } + prevFrameWasCogged = 0; + + /* The top of stack of an inactive page is always the instructionPointer. + The top of stack of the active page may be the instructionPointer if it has been pushed, + which is indicated by a 0 instructionPointer. */ + if ((GIV(stackPage) == aStackPage) + && (GIV(instructionPointer) != 0)) { + theIP = GIV(instructionPointer); + theFP = GIV(framePointer); + } + else { + theIP = ((usqInt)(longAt((aStackPage->headSP)))); + theFP = (aStackPage->headFP); + if (GIV(stackPage) == aStackPage) { + assertl(GIV(framePointer) == theFP, ln); + } + } + while (1) { + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* begin mframeHomeMethod: */ + methodField = longAt(theFP + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + theMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + goto l1; + } + theMethod = ((CogMethod *) (methodField & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + assertl((theIP == (ceCannotResumePC())) + || ((theIP >= (((usqInt)theMethod))) + && (theIP < ((((usqInt)theMethod)) + ((theMethod->blockSize))))), ln); + prevFrameWasCogged = 1; + } + else { + methodObj = longAt(theFP + FoxMethod); + if (prevFrameWasCogged) { + assertl(theIP == (ceReturnToInterpreterPC()), ln); + } + if (theIP == (ceReturnToInterpreterPC())) { + theIP = ((usqInt)(longAt(theFP + FoxIFSavedIP))); + } + assertl((theIP >= (methodObj + (lastPointerOf(methodObj)))) + && (theIP < (((methodObj + (numBytesOfBytes(methodObj))) + BaseHeaderSize) - 1)), ln); + prevFrameWasCogged = 0; + } + + /* assert-check the interpreter frame. */ + theIP = ((usqInt)(longAt(theFP + FoxCallerSavedIP))); + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theFP = callerFP; + } + assertl(theIP == (ceBaseFrameReturnPC()), ln); + return 1; +} + + +/* Check that the stacked instruction pointers in all pages are correct. + Checks the interpreter sender/machine code callee contract. + Written so it will be optimized away if not in an assert VM. */ + + /* CoInterpreter>>#assertValidStackedInstructionPointers: */ +static NoDbgRegParms void +assertValidStackedInstructionPointers(sqInt ln) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + StackPage *thePage; + + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + assertl(assertValidStackedInstructionPointersInline(thePage, ln), ln); + } + } +} + + +/* Attempt to convert the current interpreted activation into a machine code + activation, and if this is popssible, jump into machine code. bcpc is the + 0-relative pc of the backward branch bytecode (not any preceding + extension). */ + + /* CoInterpreter>>#attemptToSwitchToMachineCode: */ +static NoDbgRegParms void +attemptToSwitchToMachineCode(sqInt bcpc) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt closure; + sqInt cls; + CogMethod *cogMethod; + CogBlockMethod *cogMethod1; + CogMethod *homeMethod; + sqInt methodField; + sqInt methodHeader; + char *p; + usqInt pc; + usqInt startBcpc; + char *theFP; + + if (!(methodHasCogMethod(GIV(method)))) { + if (((byteAt((GIV(framePointer) + FoxIFrameFlags) + 3)) != 0) + && (!(isVanillaBlockClosure((cls = frameStackedReceivernumArgs(GIV(framePointer), /* frameNumArgs: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(GIV(framePointer)))->cmNumArgs) + : byteAt((GIV(framePointer) + FoxIFrameFlags) + 1)))))))) { + /* Compiled block / full closure */ + cogFullBlockMethodnumCopied(GIV(method), (numPointerSlotsOf(cls)) - FullClosureFirstCopiedValueIndex); + } + else { + /* Compiled method */ + cogselector(GIV(method), GIV(nilObj)); + } + } + if (methodHasCogMethod(GIV(method))) { + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + + /* begin convertToMachineCodeFrame:bcpc: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + + /* Update the return pc, perhaps saving it in the caller's iframeSavedIP. */ + if (longAt(GIV(framePointer) + FoxSavedFP)) { + if (!((((usqInt)(longAt((((char *)(longAt(GIV(framePointer) + FoxSavedFP)))) + FoxMethod)))) < (startOfMemory()))) { + theFP = ((char *)(longAt(GIV(framePointer) + FoxSavedFP))); + + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(theFP))); + longAtput(theFP + FoxIFSavedIP,longAt(GIV(framePointer) + FoxCallerSavedIP)); + longAtput(GIV(framePointer) + FoxCallerSavedIP,ceReturnToInterpreterPC()); + } + } + else { + longAtput(GIV(framePointer) + FoxCallerSavedIP,ceBaseFrameReturnPC()); + } + + /* Compute the cog method field */ + if (byteAt((GIV(framePointer) + FoxIFrameFlags) + 3)) { + closure = longAt(GIV(framePointer) + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(GIV(framePointer)))->cmNumArgs) + : byteAt((GIV(framePointer) + FoxIFrameFlags) + 1)))) << (shiftForWord())))))); + + /* begin isVanillaBlockClosure: */ + assert((isPointers(closure)) + && ((numSlotsOf(closure)) >= ClosureFirstCopiedValueIndex)); + if (((((longAt((void *)((closure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1)) { + startBcpc = (((longAt((void *)((closure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))))) >> 3)) - 1; + cogMethod1 = findMethodForStartBcpcinHomeMethod(startBcpc, cogMethod); + } + else { + startBcpc = (((/* begin literalCountOfMethodHeader: */ + assert((((((cogMethod->methodHeader))) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((((cogMethod->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop; + cogMethod1 = ((CogBlockMethod *) cogMethod); + } + methodField = (((sqInt)cogMethod1)) + MFMethodFlagIsBlockFlag; + } + else { + startBcpc = (((/* begin literalCountOfMethodHeader: */ + assert((((((cogMethod->methodHeader))) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((((cogMethod->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop; + cogMethod1 = ((CogBlockMethod *) cogMethod); + methodField = ((sqInt)cogMethod); + } + + /* compute the pc before converting the frame to help with debugging. */ + pc = mcPCForBackwardBranchstartBcpcin(bcpc, startBcpc, cogMethod1); + assert(pc > ((((usqInt)cogMethod1)) + (noCheckEntryOffset()))); + assert(bcpc == (bytecodePCForstartBcpcin(pc, startBcpc, cogMethod1))); + + /* now convert to a machine code frame */ + longAtput(GIV(framePointer) + FoxMethod,methodField + ((byteAt((GIV(framePointer) + FoxIFrameFlags) + 2) + ? MFMethodFlagHasContextFlag + : 0))); + for (p = (GIV(framePointer) + FoxIFReceiver); p >= GIV(stackPointer); p += (-BytesPerWord)) { + longAtput((p + FoxMFReceiver) - FoxIFReceiver,longAt(p)); + } + GIV(stackPointer) = (GIV(stackPointer) + FoxMFReceiver) - FoxIFReceiver; + + /* begin assertValidMachineCodeFrame: */ + assert(isMachineCodeFrame(GIV(framePointer))); + cogMethod1 = ((CogBlockMethod *) ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodMask)); + homeMethod = /* asCogHomeMethod: */ + (((cogMethod1->cmType)) >= CMMethod + ? ((CogMethod *) cogMethod1) + : /* cmHomeMethod */ + (((cogMethod1->cmType)) >= CMMethod + ? ((CogMethod *) cogMethod1) + : (assert(CMBlock == (cogMethod1->cmType)), + ((CogMethod *) ((((usqInt)cogMethod1)) - ((cogMethod1->homeOffset))))))); + assert((methodFor(cogMethod1)) == homeMethod); + assert((pc > (((sqInt)cogMethod1))) + && (pc < ((((sqInt)homeMethod)) + ((homeMethod->blockSize))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),pc); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + ceEnterCogCodePopReceiverReg(); + } +} + + +/* Support for primitiveSuspend. + Assume suspendedContext is that of a process waiting on a condition + variable. Backup the PC of suspendedContext to the send that entered the + wait state. + primitiveEnterCriticalSection pushes false for blocked waiters. false must + be replaced by the condition variable. */ +/* The suspendedContext is in one of six states, hence six cases: + 1 & 2: single context with bytecode pc or single context with machine code + pc 3 & 4: married to machine code frame in embedded (v3) block or machine + code frame in method/full block + 5 & 6: married to interpreter frame with saved ip or interpreter frame + with pushed pc */ + + /* CoInterpreter>>#backupContext:toBlockingSendTo: */ +static NoDbgRegParms void +backupContexttoBlockingSendTo(sqInt suspendedContext, sqInt conditionVariable) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogBlockMethod *cogMethod; + sqInt mcpc; + sqInt pc; + sqInt senderOop; + sqInt sp; + usqInt startBcpc; + char *theFP; + sqInt theIP; + sqInt theMethod; + usqInt theNewIP; + StackPage *thePage; + + assert(isContext(suspendedContext)); + theMethod = longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* cases 1 & 2 */ + if ((!((longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))) & (tagMask())))) { + pc = longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + sp = longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + assert(((((pc) & 7) == 1)) + && (((pc >> 3)) != 0)); + assert(((((sp) & 7) == 1)) + && (((sp >> 3)) > 0)); + + /* case 2 */ + if (((pc = (pc >> 3))) < 0) { + pc = ((mustMapMachineCodePCcontext(pc, suspendedContext)) >> 3); + } + + /* context with machine code pc; must map... */ + theIP = ((theMethod + BaseHeaderSize) + pc) - 1; + theNewIP = pcPreviousToFunction(theIP, theMethod); + assert((theNewIP < theIP) + && ((theIP - theNewIP) <= 3)); + pc = ((theNewIP - theMethod) - BaseHeaderSize) + 1; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(suspendedContext)) + && (!(isForwarded(suspendedContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, suspendedContext, (((usqInt)pc << 3) | 1))); + longAtput((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),(((usqInt)pc << 3) | 1)); + + /* implicitly converts to 0 relative */ + sp = ((sp >> 3)) + ReceiverIndex; + assert(((fetchPointerofObject(sp, suspendedContext)) == (falseObject())) + || ((fetchPointerofObject(sp, suspendedContext)) == conditionVariable)); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(sp, suspendedContext, conditionVariable)); + assert(isNonImmediate(suspendedContext)); + if (oopisGreaterThanOrEqualTo(suspendedContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(conditionVariable & (tagMask())))) + && (oopisLessThan(conditionVariable, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(suspendedContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(suspendedContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(sp) << (shiftForWord()))))),conditionVariable); + return; + } + assert(isMarriedOrWidowedContext(suspendedContext)); + assert(!((isWidowedContextNoConvert(suspendedContext)))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(!((thePage == GIV(stackPage)))); + assert(theFP == ((thePage->headFP))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* a machine code pc... it must be converted... */ + mcpc = longAt((thePage->headSP)); + + /* begin mframeCogMethod: */ + cogMethod = ((CogBlockMethod *) ((longAt(theFP + FoxMethod)) & MFMethodMask)); + if (((cogMethod->cmType)) == CMBlock) { + assert((frameStackedReceivernumArgs(theFP, (cogMethod->cmNumArgs))) == (fetchPointerofObject(ClosureIndex, suspendedContext))); + + /* begin startPCOfClosure: */ + startBcpc = (((longAt((void *)(((longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))))) >> 3)) - 1; + } + else { + startBcpc = ((literalCountOf(theMethod)) + LiteralStart) * BytesPerOop; + } + theIP = bytecodePCForstartBcpcin(mcpc, startBcpc, cogMethod); + theIP = (theIP + theMethod) + BaseHeaderSize; + theNewIP = pcPreviousToFunction(theIP, theMethod); + assert((theNewIP < theIP) + && ((theIP - theNewIP) <= 3)); + convertFrametoInterpreterFrame(theFP, theIP - theNewIP); + } + else { + theIP = longAt((thePage->headSP)); + if (theIP == (ceReturnToInterpreterPC())) { + /* fetchByte uses pre-increment; must + 1 to point at correct bytecode... */ + theIP = (((usqInt)(longAt(theFP + FoxIFSavedIP)))) + 1; + theNewIP = pcPreviousToFunction(theIP, theMethod); + assert((theNewIP < theIP) + && ((theIP - theNewIP) <= 3)); + + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(theFP))); + longAtput(theFP + FoxIFSavedIP,theNewIP - 1); + } + else { + /* fetchByte uses pre-increment; must + 1 to point at correct bytecode... */ + theIP += 1; + assert(validInstructionPointerinMethodframePointer(theIP, theMethod, theFP)); + theNewIP = pcPreviousToFunction(theIP, theMethod); + assert((theNewIP < theIP) + && ((theIP - theNewIP) <= 3)); + longAtput((thePage->headSP),theNewIP - 1); + } + } + assert(((longAt(((thePage->headSP)) + BytesPerWord)) == (falseObject())) + || ((longAt(((thePage->headSP)) + BytesPerWord)) == conditionVariable)); + longAtput(((thePage->headSP)) + BytesPerWord,conditionVariable); +} + + /* CoInterpreter>>#callForCogCompiledCodeCompaction */ +void +callForCogCompiledCodeCompaction(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(cogCompiledCodeCompactionCalledFor) = 1; + forceInterruptCheck(); +} + + +/* convert + rcvr base + arg(s) + retpc <- sp + to + retpc base + entrypc + rcvr + arg(s) <- sp + and then enter at either the checked or the unchecked entry-point. */ + + /* CoInterpreter>>#callRegisterArgCogMethod:at:receiver: */ +static NoDbgRegParms void +callRegisterArgCogMethodatreceiver(CogMethod *cogMethod, sqInt entryOffset, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + char *sp; + + assert(((numRegArgs()) > 0) + && (((numRegArgs()) <= 2) + && (((cogMethod->cmNumArgs)) <= (numRegArgs())))); + if (((cogMethod->cmNumArgs)) == 2) { + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (3 * BytesPerWord),longAt(GIV(stackPointer))); + object = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (1 * BytesPerWord),longAt(GIV(stackPointer) + (3 * BytesPerWord))); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (2 * BytesPerWord),rcvr); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (3 * BytesPerWord),(((sqInt)cogMethod)) + entryOffset); + ceCallCogCodePopReceiverArg1Arg0Regs(); + } + if (((cogMethod->cmNumArgs)) == 1) { + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (2 * BytesPerWord),longAt(GIV(stackPointer))); + object = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (1 * BytesPerWord),rcvr); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (2 * BytesPerWord),(((sqInt)cogMethod)) + entryOffset); + ceCallCogCodePopReceiverArg0Regs(); + } + assert(((cogMethod->cmNumArgs)) == 0); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (1 * BytesPerWord),longAt(GIV(stackPointer))); + + /* stackValue:put: */ + longAtput(GIV(stackPointer),(((sqInt)cogMethod)) + entryOffset); + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),rcvr); + GIV(stackPointer) = sp; + ceCallCogCodePopReceiverReg(); +} + + +/* An external call or FFI primitive has failed. Build the frame and + activate as appropriate. Enter either the interpreter or machine + code depending on whether aPrimitiveMethod has been or is still + cogged. Note that we could always interpret but want the efficiency + of executing machine code if it is available. */ + + /* CoInterpreter>>#ceActivateFailingPrimitiveMethod: */ +void +ceActivateFailingPrimitiveMethod(sqInt aPrimitiveMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt methodHeader; + sqInt result; + + assert(GIV(primFailCode) != 0); + assert(GIV(newMethod) == aPrimitiveMethod); + + /* If we're on Spur, retry the primitive, if appropriate, returning if successful after retry. */ + + /* ugh; annoying. Most users want the simulated address for the primitive (e.g. compilation in the Cogit). + But here we want the object for the primitive. So we choose to side-step the simulator's implementation + rather than change the other clients to do the mapFunctionToAddress: the simulator's version does. */ + primitiveFunctionPointer = functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(aPrimitiveMethod, (primitiveIndexOf(aPrimitiveMethod)), null); + retryPrimitiveOnFailure(); + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + if (!GIV(primFailCode)) { + result = longAt(GIV(stackPointer)); + assert(isPCWithinMethodZone(GIV(instructionPointer))); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(instructionPointer)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),result); + ceEnterCogCodePopReceiverReg(); + } + methodHeader = longAt((void *)((aPrimitiveMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + activateNewCogMethodinInterpreter(((CogMethod *) methodHeader), 0); + } + else { + activateNewMethod(); + } +} + + +/* Return across a page boundary. The context to return to (which may be + married) is stored in the first word of the stack. We get here when a + return instruction jumps + to the ceBaseFrameReturn: address that is the return pc for base frames. A + consequence of this is that the current frame is no longer valid since an + interrupt may have overwritten + its state as soon as the stack pointer has been cut-back beyond the return + pc. So to have + a context to send the cannotReturn: message to we also store the base + frame's context + in the second word of the stack page. */ + + /* CoInterpreter>>#ceBaseFrameReturn: */ +sqInt +ceBaseFrameReturn(sqInt returnValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + char *callerFP; + sqInt contextToReturnFrom; + sqInt contextToReturnTo; + char *fp; + char *frameAbove; + sqInt header; + sqInt isAContext; + sqInt methodHeader; + StackPage *newPage; + sqInt senderOop; + char *sp; + StackPage *thePage; + + assert((stackPageFor(GIV(stackPointer))) == GIV(stackPage)); + assert((GIV(mostRecentlyUsedPage)) == GIV(stackPage)); + assertCStackWellAligned(); + assert(GIV(framePointer) == 0); + assert(GIV(stackPointer) <= (((GIV(stackPage)->baseAddress)) - BytesPerWord)); + assert((((GIV(stackPage)->baseFP)) + (2 * BytesPerWord)) < ((GIV(stackPage)->baseAddress))); + + /* We would like to use the following assert but we can't since the stack pointer will be above the + base frame pointer in the base frame return and hence the 0 a base frame pointer points at could + be overwritten which will cause the isBaseFrame assert in frameCallerContext: to fail. + self assert: (self frameCallerContext: stackPage baseFP) = (stackPages longAt: stackPage baseAddress). */ + assert((addressCouldBeObj(longAt(((GIV(stackPage)->baseAddress)) - BytesPerWord))) + && (isContext(longAt(((GIV(stackPage)->baseAddress)) - BytesPerWord)))); + contextToReturnTo = longAt((GIV(stackPage)->baseAddress)); + assert(addressCouldBeObj(contextToReturnTo)); + + /* The stack page is effectively free now, so free it. We must free it to be + correct in determining if contextToReturnTo is still married, and in case + makeBaseFrameFor: cogs a method, which may cause a code compaction, + in which case the frame must be free to avoid the relocation machinery + tracing the dead frame. Since freeing now temporarily violates the page-list + ordering invariant, use the assert-free version. */ + freeStackPageNoAssert(GIV(stackPage)); + isAContext = /* isContext: */ + ((!(contextToReturnTo & (tagMask())))) + && (((longAt((void *)(contextToReturnTo))) & (classIndexMask())) == ClassMethodContextCompactIndex); + if (isAContext + && (/* isStillMarriedContext: */ + (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(contextToReturnTo))))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + GIV(framePointer) = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(GIV(framePointer)), GIV(pages)); + if (GIV(framePointer) == ((thePage->headFP))) { + GIV(stackPointer) = (thePage->headSP); + } + else { + /* Returning to some interior frame, presumably because of a sender assignment. + Move the frames above to another page (they may be in use, e.g. via coroutining). + Make the interior frame the top frame. */ + + /* begin findFrameAbove:inPage: */ + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == GIV(framePointer)) { + frameAbove = 0; + goto l1; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == GIV(framePointer)) { + frameAbove = fp; + goto l1; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + frameAbove = 0; + /* end findFrameAbove:inPage: */ +l1: + + /* Since we've just deallocated a page we know that newStackPage won't deallocate an existing one. */ + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l2; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l2: + assert(newPage == GIV(stackPage)); + moveFramesInthroughtoPage(thePage, frameAbove, newPage); + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + } + } + else { + if (!(isAContext + && (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1)))) { + contextToReturnFrom = longAt(((GIV(stackPage)->baseAddress)) - BytesPerWord); + tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFromtoreturnValue(contextToReturnFrom, contextToReturnTo, returnValue); + + /* begin externalCannotReturn:from: */ + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),contextToReturnFrom); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + + /* Both ceBaseFrameReturnTrampoline & ceNonLocalReturnTrampoline pop + their caller's return pc into instructionPointer. In this uncommon case restore + it, since a send's call pushes the instructionPointer (after the arguments). */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), contextToReturnFrom, 1); + } + + /* void the instructionPointer to stop it being incorrectly updated in a code + compaction in makeBaseFrameFor:. */ + GIV(instructionPointer) = 0; + thePage = makeBaseFrameFor(contextToReturnTo); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + } + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + assert((stackPageFor(GIV(framePointer))) == GIV(stackPage)); + if ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())) { + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + ceEnterCogCodePopReceiverReg(); + } + GIV(instructionPointer) = longAt(GIV(stackPointer)); + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* stackTopPut: */ + longAtput(GIV(stackPointer),returnValue); + assert(checkIsStillMarriedContextcurrentFP(contextToReturnTo, GIV(framePointer))); + ceInvokeInterpret(); + + /* NOTREACHED */ + return null; +} + + +/* index is unboxed and 0-based. The call-back expects 1-based value (to + perform the operation with instVarAt:put: + */ + + /* CoInterpreter>>#ceCannotAssignTo:withIndex:valueToAssign: */ +#if IMMUTABILITY +sqInt +ceCannotAssignTowithIndexvalueToAssign(sqInt immutableObject, sqInt index, sqInt valueToAssign) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + sqInt top; + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),immutableObject); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),valueToAssign); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),(((usqInt)(index + 1) << 3) | 1)); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))), immutableObject, 2); +} +#endif /* IMMUTABILITY */ + + +/* A context that has been returned from, or otherwise has an invalid pc has + been reentered. + Until we have a cannotResume: selector, simply resend cannotReturn:. */ + + /* CoInterpreter>>#ceCannotResume */ +sqInt +ceCannotResume(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + sqInt resultOop; + + assert(isMachineCodeFrame(GIV(framePointer))); + assert(frameHasContext(GIV(framePointer))); + resultOop = longAt(GIV(stackPointer)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt(GIV(framePointer) + FoxThisContext)); + longAtput((GIV(stackPointer) -= BytesPerWord),resultOop); + + /* Make sure the happy couple remain returned from. */ + object = ceCannotResumePC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), longAt(GIV(framePointer) + FoxThisContext), 1); +} + + +/* Log failure and then retry if there's an accessorDepth or failure due to + no memory. + */ + + /* CoInterpreter>>#ceCheckAndMaybeRetryPrimitive: */ +void +ceCheckAndMaybeRetryPrimitive(sqInt primIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodSelectorOrImmediate; + sqInt retried; + sqInt tagBits; + + if ((recordPrimTrace()) + && (!primTracePluginName)) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveFailure << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + aMethodSelectorOrImmediate = /* fetchClassOf: */ + ((tagBits = (longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord))) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)))); + + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = aMethodSelectorOrImmediate; + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + retried = retryPrimitiveOnFailure(); + if (retried + && (recordPrimTrace())) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveRetry << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } +} + + /* CoInterpreter>>#ceContext:instVar: */ +sqInt +ceContextinstVar(sqInt maybeContext, sqInt slotIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt result; + sqInt top; + + if (((longAt((void *)(maybeContext))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + result = externalInstVarofContext(slotIndex, maybeContext); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + else { + result = longAt((void *)((maybeContext + BaseHeaderSize) + ((((usqInt)(slotIndex) << (shiftForWord())))))); + } + return result; +} + + +/* genStorePop:MaybeContextReceiverVariable: filters out unmarried contexts + but not arbitrary objects in subclasses. It answers maybeMarriedContext so + that the StackToRegisterMappingCogit can keep ReceiverResultReg live. */ + + /* CoInterpreter>>#ceContext:instVar:value: */ +sqInt +ceContextinstVarvalue(sqInt maybeMarriedContext, sqInt slotIndex, sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt top; + + if ((((longAt((void *)(maybeMarriedContext))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (((((longAt((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + externalInstVarofContextput(slotIndex, maybeMarriedContext, anOop); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + else { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(slotIndex, maybeMarriedContext, anOop)); + assert(isNonImmediate(maybeMarriedContext)); + if (oopisGreaterThanOrEqualTo(maybeMarriedContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anOop & (tagMask())))) + && (oopisLessThan(anOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(maybeMarriedContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(maybeMarriedContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(slotIndex) << (shiftForWord()))))),anOop); + } + return maybeMarriedContext; +} + + /* CoInterpreter>>#ceInterpretMethodFromPIC:receiver: */ +sqInt +ceInterpretMethodFromPICreceiver(sqInt aMethodObj, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + CogMethod *pic; + sqInt primitiveIndex; + sqInt top; + + + /* pop off inner return and locate open or closed PIC */ + pic = ((CogMethod *) ((popStack()) - (interpretOffset()))); + assert((isCMOpenPIC(((CogBlockMethod *) pic))) + || (isCMClosedPIC(((CogBlockMethod *) pic)))); + + /* If found from an open PIC then it must be an uncogged method and, since it's been found + in the method cache, should be cogged if possible. If found from a closed PIC then at the + time the closed PIC was created the method was uncoggable, either because there was + no space, it had too many literals or it contained an illegal bytecode). So don't try and cog + it, but subsequently it may have been cogged via another path. If the method is, or ends up + cogged, jump to machine code, otherwise interpret. */ + if (((pic->cmType)) == CMOpenPIC) { + assert(!(methodHasCogMethod(aMethodObj))); + + /* begin methodShouldBeCogged: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + goto l1; + } + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(aMethodObj); + } + goto l2; +l1: + cogselector(aMethodObj, (pic->selector)); +l2:; + } + if (methodHasCogMethod(aMethodObj)) { + executeCogMethodfromUnlinkedSendWithReceiver(cogMethodOf(aMethodObj), rcvr); + } + GIV(messageSelector) = (pic->selector); + GIV(newMethod) = aMethodObj; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (aMethodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]))); + GIV(argumentCount) = (pic->cmNumArgs); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + return interpretMethodFromMachineCode(); +} + + /* CoInterpreter>>#ceMNUFromPICMNUMethod:receiver: */ +sqInt +ceMNUFromPICMNUMethodreceiver(sqInt aMethodObj, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cPIC; + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + sqInt primitiveIndex; + sqInt selector; + sqInt tagBits; + sqInt top; + + assert(addressCouldBeOop(rcvr)); + assert((aMethodObj == 0) + || ((addressCouldBeObj(aMethodObj)) + && (isOopCompiledMethod(aMethodObj)))); + selector = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(selector), lengthOfMaybeImmediate(selector), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + cPIC = ((CogMethod *) ((popStack()) - (mnuOffset()))); + assert((isCMClosedPIC(((CogBlockMethod *) cPIC))) + || (isCMOpenPIC(((CogBlockMethod *) cPIC)))); + GIV(argumentCount) = (cPIC->cmNumArgs); + GIV(messageSelector) = (cPIC->selector); + if (aMethodObj) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + createActualMessageTo(/* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr))); + if (/* maybeMethodHasCogMethod: */ + ((!(aMethodObj & (tagMask())))) + && ((((byteAt((void *)(aMethodObj + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + executeCogMethodfromUnlinkedSendWithReceiver(cogMethodOf(aMethodObj), rcvr); + + /* NOTREACHED */ + assert(0); + } + GIV(newMethod) = aMethodObj; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (aMethodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]))); + return interpretMethodFromMachineCode(); + } + + /* handleMNU:InMachineCodeTo:classForMessage: assumes lkupClass is set, since every other use is + after a lookupMethodNoMNUEtcInClass: call, which sets lkupClass. Here we must set it manually. + Global variables. Bah! */ + handleMNUInMachineCodeToclassForMessage(SelectorDoesNotUnderstand, rcvr, (GIV(lkupClass) = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)))); + + /* NOTREACHED */ + assert(0); + return 0; +} + + +/* Called from machine code to set an object's identity hash to a new hash. + N.B. may be called from machine code on the Smalltalk stack so this should + not call deeply. */ +/* We know anObject has not a hash yet (or this entry-point would not be + called. Sets the hash, then answers it as a smallinteger */ + + /* CoInterpreter>>#ceNewHashOf: */ +sqInt +ceNewHashOf(sqInt anObject) +{ + assert((isNonImmediate(anObject)) + && ((rawHashBitsOf(anObject)) == 0)); + return (((newHashBitsOf(anObject)) << 3) | 1); +} + + /* CoInterpreter>>#ceNonLocalReturn: */ +sqInt +ceNonLocalReturn(sqInt returnValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt closure; + sqInt contextToReturnTo; + sqInt currentCtx; + char *frameToReturnTo; + sqInt home; + StackPage *newPage; + sqInt nextCntx; + sqInt ourContext; + sqInt ourContextSqInt; + sqInt senderOop; + char *sp; + char *theFP; + char *theFP1; + StackPage *thePage; + StackPage *thePage1; + sqInt top; + sqInt unwindContextOrNilOrZero; + + + /* self shortPrintFrameAndCallers: framePointer. + self printOop: returnValue. + self halt. */ + frameToReturnTo = ((char *) 0); + theFP = ((char *) 0); + assert(isMachineCodeFrame(GIV(framePointer))); + assert(frameIsBlockActivation(GIV(framePointer))); + + /* Update the current page's headFrame pointers to enable the search for unwind protects below + to identify widowed contexts correctly. */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* Since this is a block activation the closure is on the stack above any args and the frame. */ + closure = longAt(GIV(framePointer) + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(GIV(framePointer)))->cmNumArgs) + : byteAt((GIV(framePointer) + FoxIFrameFlags) + 1)))) << (shiftForWord())))))); + + /* avoid compiler warning */ + /* Walk the closure's lexical chain to find the context or frame to return from (home). */ + home = null; + while (closure != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + home = longAt((void *)((closure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + assert(isNonImmediate(home)); + if ((!((longAt((void *)(home))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + home = fixFollowedFieldofObjectwithInitialValue(ClosureOuterContextIndex, closure, home); + } + if (!(/* isContext: */ + ((!(home & (tagMask())))) + && (((longAt((void *)(home))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* error: can't find home on chain; cannot return */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + ourContext = longAt(GIV(framePointer) + FoxThisContext); + goto l1; + } + ourContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l1: + + /* begin externalCannotReturn:from: */ + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),ourContext); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + + /* Both ceBaseFrameReturnTrampoline & ceNonLocalReturnTrampoline pop + their caller's return pc into instructionPointer. In this uncommon case restore + it, since a send's call pushes the instructionPointer (after the arguments). */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), ourContext, 1); + } + + /* begin followObjField:ofObject: */ + closure = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord())))))); + assert(isNonImmediate(closure)); + if ((!((longAt((void *)(closure))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + closure = fixFollowedFieldofObjectwithInitialValue(ClosureIndex, home, closure); + } + } + + /* home is to be returned from provided there is no unwind-protect activation between + this frame and home's sender. Search for an unwind. findUnwindThroughContext: + will answer either the context for an unwind-protect activation or nilObj if the sender + cannot be found or 0 if no unwind is found but the sender is. */ + unwindContextOrNilOrZero = findUnwindThroughContext(home); + if (unwindContextOrNilOrZero == GIV(nilObj)) { + /* error: can't find home on chain; cannot return */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + ourContext = longAt(GIV(framePointer) + FoxThisContext); + goto l2; + } + ourContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l2: + + /* begin externalCannotReturn:from: */ + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),ourContext); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + + /* Both ceBaseFrameReturnTrampoline & ceNonLocalReturnTrampoline pop + their caller's return pc into instructionPointer. In this uncommon case restore + it, since a send's call pushes the instructionPointer (after the arguments). */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), ourContext, 1); + } + if (unwindContextOrNilOrZero) { + /* begin externalAboutToReturn:through: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + ourContextSqInt = longAt(GIV(framePointer) + FoxThisContext); + goto l3; + } + ourContextSqInt = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l3: + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),ourContextSqInt); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),unwindContextOrNilOrZero); + + /* The ceNonLocalReturnTrampoline pops its caller's return pc into instructionPointer. + In this uncommon case restore it, since a send's call pushes the instructionPointer (after the arguments). */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAboutToReturn) << (shiftForWord())))))), ourContextSqInt, 2); + } + + /* Now we know home is on the sender chain. + We could be returning to either a context or a frame. Find out which. */ + contextToReturnTo = null; + if (((((longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert(checkIsStillMarriedContextcurrentFP(home, GIV(framePointer))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (longAt(theFP + FoxSavedFP)) { + frameToReturnTo = ((char *)(longAt(theFP + FoxSavedFP))); + } + else { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + contextToReturnTo = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(contextToReturnTo)); + assert((contextToReturnTo == (nilObject())) + || (isContext(followMaybeForwarded(contextToReturnTo)))); + } + } + else { + contextToReturnTo = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + if ((/* isContext: */ + ((!(contextToReturnTo & (tagMask())))) + && (((longAt((void *)(contextToReturnTo))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + assert(checkIsStillMarriedContextcurrentFP(contextToReturnTo, GIV(framePointer))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + frameToReturnTo = ((char *)(senderOop - (smallIntegerTag()))); + contextToReturnTo = null; + } + } + + /* If returning to a context we must make a frame for it unless it is dead. */ + if (contextToReturnTo) { + frameToReturnTo = establishFrameForContextToReturnTo(contextToReturnTo); + if (!frameToReturnTo) { + /* error: home's sender is dead; cannot return */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + ourContext = longAt(GIV(framePointer) + FoxThisContext); + goto l4; + } + ourContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l4: + + /* begin externalCannotReturn:from: */ + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),ourContext); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + + /* Both ceBaseFrameReturnTrampoline & ceNonLocalReturnTrampoline pop + their caller's return pc into instructionPointer. In this uncommon case restore + it, since a send's call pushes the instructionPointer (after the arguments). */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), ourContext, 1); + } + } + + /* Now we have a frame to return to. If it is on a different page we must + free intervening pages and nil out intervening contexts. We must free + intervening stack pages because if we leave the pages to be divorced + then their contexts will be divorced with intact senders and instruction + pointers. This code is similar to primitiveTerminateTo. */ + assert(pageListIsWellFormed()); + + /* begin stackPageFor: */ + newPage = stackPageAtpages(pageIndexFor(frameToReturnTo), GIV(pages)); + if (newPage != GIV(stackPage)) { + theFP1 = (GIV(stackPage)->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP1)); + thePage1 = stackPageAtpages(pageIndexFor(theFP1), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + assert(isContext(currentCtx)); + freeStackPage(GIV(stackPage)); + while (1) { + assert(isContext(currentCtx)); + if ((((((longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && ((stackPageFor((theFP = frameOfMarriedContext(currentCtx)))) == newPage)) break; + if (((((longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + theFP1 = (thePage->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP1)); + thePage1 = stackPageAtpages(pageIndexFor(theFP1), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + freeStackPage(thePage); + } + else { + nextCntx = longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + + /* begin markContextAsDead: */ + assert(isContext(currentCtx)); + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(SenderIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + currentCtx = nextCntx; + } + } + + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (newPage->headSP); + GIV(framePointer) = (newPage->headFP); + } + + /* Two cases. Returning to the top frame or an interior frame. The + top frame has its instruction pointer on top of stack. An interior + frame has its instruction pointer in the caller frame. We need to + peel back any frames on the page until we get to the correct frame. */ + if (GIV(framePointer) == frameToReturnTo) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + } + else { + do { + callerFP = GIV(framePointer); + GIV(framePointer) = ((char *)(longAt(GIV(framePointer) + FoxSavedFP))); + } while(GIV(framePointer) != frameToReturnTo); + GIV(instructionPointer) = longAt(callerFP + FoxCallerSavedIP); + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(callerFP))); + GIV(stackPointer) = (callerFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(callerFP))->cmNumArgs) + : byteAt((callerFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + return returntoExecutive(returnValue, 0); +} + + /* CoInterpreter>>#ceReapAndResetErrorCodeFor: */ +void +ceReapAndResetErrorCodeFor(CogMethod *cogMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(GIV(primFailCode) != 0); + GIV(newMethod) = (cogMethod->methodObject); + reapAndResetErrorCodeToheader(GIV(stackPointer) + BytesPerWord, (cogMethod->methodHeader)); +} + + +/* Entry-point for an abort send in a CogMethod (aboutToReturn:through:, + cannotReturn: et al). + Try and dispatch the send, but the send may turn into an MNU in which case + defer to + handleMNUInMachineCodeTo:... which will dispatch the MNU. + + Continue execution via either executeMethod or + interpretMethodFromMachineCode: depending on whether the target method is + cogged or not. */ + + /* CoInterpreter>>#ceSendAbort:to:numArgs: */ +static NoDbgRegParms sqInt +ceSendAborttonumArgs(sqInt selector, sqInt rcvr, sqInt numArgs) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classObj; + sqInt classTablePage; + sqInt classTag; + CogMethod *cogMethod; + sqInt errSelIdx; + sqInt fieldIndex; + sqInt methodHeader; + sqInt tagBits; + sqInt top; + + + /* self printExternalHeadFrame */ + /* self printStringOf: selector */ + assertCStackWellAligned(); + assert(addressCouldBeOop(rcvr)); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(selector), lengthOfMaybeImmediate(selector), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + GIV(argumentCount) = numArgs; + classTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + if (lookupInMethodCacheSelclassTag(selector, classTag)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, selector); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), selector); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + GIV(messageSelector) = selector; + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((classTag >= 0) + && ((classTag <= (tagMask())) + || ((classTag >= (arrayClassIndexPun())) + && (classTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classObj = null; + goto l1; + } + fieldIndex = classTag & ((1U << (classTableMajorIndexShift())) - 1); + classObj = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l1: + if ((errSelIdx = lookupOrdinaryNoMNUEtcInClass(classObj))) { + handleMNUInMachineCodeToclassForMessage(errSelIdx, rcvr, classObj); + + /* NOTREACHED */ + assert(0); + } + } + + /* check for coggability because method is in the cache */ + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (/* maybeMethodHasCogMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + executeNewMethod(); + assert(0); + } + return interpretMethodFromMachineCode(); +} + + +/* Send from an Open PIC when the first-level method lookup probe has failed, + or to continue when PIC creation has failed (e.g. because we're out of + code space), + or when a send has failed due to a forwarded receiver. */ +/* self printFrame: stackPage headFP WithSP: stackPage headSP */ +/* self printStringOf: selector */ + + /* CoInterpreter>>#ceSendFromInLineCacheMiss: */ +sqInt +ceSendFromInLineCacheMiss(CogMethod *cogMethodOrPIC) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classObj; + sqInt classTablePage; + sqInt classTag; + CogMethod *cogMethod; + sqInt errSelIdx; + sqInt fieldIndex; + sqInt methodHeader; + sqInt rcvr; + sqInt selector; + sqInt tagBits; + sqInt top; + + GIV(argumentCount) = (cogMethodOrPIC->cmNumArgs); + + /* skip return pc */ + rcvr = longAt(GIV(stackPointer) + ((((cogMethodOrPIC->cmNumArgs)) + 1) * BytesPerWord)); + assert(addressCouldBeOop(rcvr)); + classTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + + /* would like to assert this but must also allow for an interpretable method in the cache. */ + if (lookupInMethodCacheSelclassTag((cogMethodOrPIC->selector), classTag)) { + selector = (cogMethodOrPIC->selector); + + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, selector); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), selector); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + if (/* isOopForwarded: */ + ((!(((cogMethodOrPIC->selector)) & (tagMask())))) + && ((!((longAt((void *)((cogMethodOrPIC->selector)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + handleForwardedSelectorFaultFor((cogMethodOrPIC->selector)); + return ceSendFromInLineCacheMiss(cogMethodOrPIC); + } + if (classTag == (isForwardedObjectClassIndexPun())) { + handleForwardedSendFaultForReceiverstackDelta(rcvr, 1); + + /* skip return pc */ + return ceSendFromInLineCacheMiss(cogMethodOrPIC); + } + GIV(messageSelector) = (cogMethodOrPIC->selector); + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((classTag >= 0) + && ((classTag <= (tagMask())) + || ((classTag >= (arrayClassIndexPun())) + && (classTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classObj = null; + goto l1; + } + fieldIndex = classTag & ((1U << (classTableMajorIndexShift())) - 1); + classObj = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l1: + if ((errSelIdx = lookupOrdinaryNoMNUEtcInClass(classObj))) { + handleMNUInMachineCodeToclassForMessage(errSelIdx, rcvr, classObj); + + /* NOTREACHED */ + assert(0); + } + } + + /* check for coggability because method is in the cache */ + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (/* maybeMethodHasCogMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + executeNewMethod(); + assert(0); + } + return interpretMethodFromMachineCode(); +} + + +/* For RegisterAllocatingCogit we want the pc following a conditional branch + not to be reachable, so + we don't have to generate code to reload registers. But notionally the pc + following a conditional + branch is reached when continuing from a mustBeBoolean error. Instead of + supporting this in the + JIT, simply convert to an interpreter frame, backup the pc to the branch, + reenter the interpreter + and hence retry the mustBeBoolean send therein. N.B. We could do this for + immutability violations + too, but immutability is used in actual applications and so should be + performant, whereas + mustBeBoolean errors are extremely rare and so we choose brevity over + performance in this case. */ + + /* CoInterpreter>>#ceSendMustBeBooleanTo:interpretingAtDelta: */ +sqInt +ceSendMustBeBooleanTointerpretingAtDelta(sqInt aNonBooleanObject, sqInt jumpSize) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + sqInt top; + + assert(addressCouldBeOop(aNonBooleanObject)); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + convertFrametoInterpreterFrame(GIV(framePointer), jumpSize); + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),aNonBooleanObject); + GIV(stackPointer) = sp; + + /* and now reenter the interpreter... */ + ceInvokeInterpret(); + + /* NOTREACHED */ + return null; +} + + /* CoInterpreter>>#ceSendMustBeBoolean: */ +sqInt +ceSendMustBeBoolean(sqInt anObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + sqInt top; + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),anObject); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))), anObject, 0); +} + + +/* Entry-point for an unlinked directed super send in a CogMethod. Smalltalk + stack looks like + receiver + args + head sp -> sender return pc + methodClassBinding is an association whose value is the class above which + to start the lookup. */ + + /* CoInterpreter>>#ceSend:aboveClassBinding:to:numArgs: */ +void +ceSendaboveClassBindingtonumArgs(sqInt selector, sqInt methodClassBinding, sqInt rcvr, sqInt numArgs) +{ + ceSendabovetonumArgs(selector, fetchPointerofObject(ValueIndex, /* followMaybeForwarded: */ + (/* isOopForwarded: */ + ((!(methodClassBinding & (tagMask())))) + && ((!((longAt((void *)(methodClassBinding))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + ? followForwarded(methodClassBinding) + : methodClassBinding)), rcvr, numArgs); +} + + +/* Entry-point for an unlinked directed super send in a CogMethod. Smalltalk + stack looks like + receiver + args + head sp -> sender return pc + methodClass is the class above which to start the lookup. + + If an MNU then defer to handleMNUInMachineCodeTo:... which will dispatch + the MNU and + may choose to allocate a closed PIC with a fast MNU dispatch for this + send. Otherwise + attempt to link the send site as efficiently as possible. All link + attempts may fail; e.g. + because we're out of code memory. + + Continue execution via either executeMethod or + interpretMethodFromMachineCode: depending on whether the target method is + cogged or not. */ + + /* CoInterpreter>>#ceSend:above:to:numArgs: */ +sqInt +ceSendabovetonumArgs(sqInt selector, sqInt methodClass, sqInt rcvr, sqInt numArgs) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classObj; + sqInt classObjSqInt; + sqInt classPointer; + sqInt classTablePage; + sqInt classTag; + CogMethod *cogMethod; + CogMethod *cogMethod1; + sqInt err; + sqInt errSelIdx; + sqInt fieldIndex; + int hash; + sqInt methodHeader; + CogMethod *newCogMethod; + sqInt objOop; + sqInt tagBits; + sqInt top; + + + /* self printExternalHeadFrame */ + /* self printStringOf: selector */ + assertCStackWellAligned(); + assert(addressCouldBeOop(rcvr)); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(selector), lengthOfMaybeImmediate(selector), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + classPointer = /* followMaybeForwarded: */ + (/* isOopForwarded: */ + ((!(methodClass & (tagMask())))) + && ((!((longAt((void *)(methodClass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + ? followForwarded(methodClass) + : methodClass); + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, objOop); + } + classObjSqInt = objOop; + + /* begin classTagForClass: */ + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObjSqInt)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classTag = ((hash = (long32At((void *)(classObjSqInt + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObjSqInt) + ? ((err = enterIntoClassTable(classObjSqInt)) + ? -err + : (long32At((void *)(classObjSqInt + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + GIV(argumentCount) = numArgs; + if (lookupInMethodCacheSelclassTag(selector, classTag)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod1 = ((CogMethod *) methodHeader); + if (((cogMethod1->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod1, selector); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), selector); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + assert(!((isForwardedClassTag(classTag)))); + if (/* isOopForwarded: */ + ((!(selector & (tagMask())))) + && ((!((longAt((void *)(selector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + return ceSendabovetonumArgs(handleForwardedSelectorFaultFor(selector), methodClass, rcvr, numArgs); + } + GIV(messageSelector) = selector; + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((classTag >= 0) + && ((classTag <= (tagMask())) + || ((classTag >= (arrayClassIndexPun())) + && (classTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classObj = null; + goto l1; + } + fieldIndex = classTag & ((1U << (classTableMajorIndexShift())) - 1); + classObj = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l1: + if ((errSelIdx = lookupOrdinaryNoMNUEtcInClass(classObj))) { + if ((errSelIdx == SelectorDoesNotUnderstand) + && ((((usqInt)((cogMethod = cogMNUPICSelectorreceivermethodOperandnumArgs(GIV(messageSelector), rcvr, mnuMethodOrNilFor(rcvr), GIV(argumentCount)))))) > (minCogMethodAddress()))) { + linkSendAtintooffsetreceiver(longAt(GIV(stackPointer)), mframeHomeMethod(GIV(framePointer)), cogMethod, noCheckEntryOffset(), rcvr); + } + handleMNUInMachineCodeToclassForMessage(errSelIdx, rcvr, classObj); + assert(0); + } + } + + /* check for coggability because method is in the cache + Method found and has a cog method. Attempt to link to it. The receiver's class may be young. + We must not link to an Open PIC since they perform normal sends. */ + if (/* maybeMethodHasCogMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, selector); + } + else { + /* Deal with anonymous accessors, e.g. in Newspeak. The cogMethod may not have the + correct selector. If not, try and compile a new method with the correct selector. */ + if (((cogMethod->selector)) != selector) { + if ((newCogMethod = cogselector(GIV(newMethod), selector))) { + cogMethod = newCogMethod; + } + } + } + if (((cogMethod->selector)) == selector) { + linkSendAtintooffsetreceiver(longAt(GIV(stackPointer)), mframeHomeMethod(GIV(framePointer)), cogMethod, noCheckEntryOffset(), rcvr); + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + executeNewMethod(); + assert(0); + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + return interpretMethodFromMachineCode(); +} + + +/* Entry-point for an unlinked send in a CogMethod. Smalltalk stack looks + like receiver + args + head sp -> sender return pc + + If an MNU then defer to handleMNUInMachineCodeTo:... which will dispatch + the MNU and + may choose to allocate a closed PIC with a fast MNU dispatch for this + send. Otherwise + attempt to link the send site as efficiently as possible. All link + attempts may fail; e.g. + because we're out of code memory. + + Continue execution via either executeMethod or + interpretMethodFromMachineCode: depending on whether the target method is + cogged or not. */ + + /* CoInterpreter>>#ceSend:super:to:numArgs: */ +sqInt +ceSendsupertonumArgs(sqInt selector, sqInt superNormalBar, sqInt rcvr, sqInt numArgs) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classObj; + sqInt classObjSqInt; + sqInt classPointer; + sqInt classTablePage; + sqInt classTag; + CogMethod *cogMethod; + CogMethod *cogMethod1; + sqInt err; + sqInt errSelIdx; + sqInt fieldIndex; + int hash; + sqInt literal; + sqInt methodHeader; + sqInt methodPointer; + CogMethod *newCogMethod; + sqInt objOop; + sqInt offset; + sqInt tagBits; + sqInt top; + + + /* self printExternalHeadFrame */ + /* self printStringOf: selector */ + assertCStackWellAligned(); + assert(addressCouldBeOop(rcvr)); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(selector), lengthOfMaybeImmediate(selector), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + if (superNormalBar) { + methodPointer = /* frameMethodObject: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(GIV(framePointer)))->methodObject) + : longAt(GIV(framePointer) + FoxMethod)); + + /* begin methodClassOf: */ + offset = (literalCountOf(methodPointer)) - 1; + literal = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = fixFollowedFieldofObjectwithInitialValue(offset + LiteralStart, methodPointer, literal); + } + classPointer = ((literal != GIV(nilObj)) + && (/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + ? (assert((numSlotsOf(literal)) > ValueIndex), + followFieldofObject(ValueIndex, literal)) + : GIV(nilObj)); + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, objOop); + } + classObjSqInt = objOop; + + /* begin classTagForClass: */ + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObjSqInt)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classTag = ((hash = (long32At((void *)(classObjSqInt + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObjSqInt) + ? ((err = enterIntoClassTable(classObjSqInt)) + ? -err + : (long32At((void *)(classObjSqInt + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + } + else { + classTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + } + GIV(argumentCount) = numArgs; + if (lookupInMethodCacheSelclassTag(selector, classTag)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod1 = ((CogMethod *) methodHeader); + if (((cogMethod1->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod1, selector); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), selector); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + if (/* isOopForwarded: */ + ((!(selector & (tagMask())))) + && ((!((longAt((void *)(selector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + return ceSendsupertonumArgs(handleForwardedSelectorFaultFor(selector), superNormalBar, rcvr, numArgs); + } + if (classTag == (isForwardedObjectClassIndexPun())) { + assert(superNormalBar == 0); + return ceSendsupertonumArgs(selector, superNormalBar, handleForwardedSendFaultForReceiverstackDelta(rcvr, 1), numArgs); + } + GIV(messageSelector) = selector; + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((classTag >= 0) + && ((classTag <= (tagMask())) + || ((classTag >= (arrayClassIndexPun())) + && (classTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classObj = null; + goto l1; + } + fieldIndex = classTag & ((1U << (classTableMajorIndexShift())) - 1); + classObj = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l1: + if ((errSelIdx = lookupOrdinaryNoMNUEtcInClass(classObj))) { + if ((errSelIdx == SelectorDoesNotUnderstand) + && ((((usqInt)((cogMethod = cogMNUPICSelectorreceivermethodOperandnumArgs(GIV(messageSelector), rcvr, mnuMethodOrNilFor(rcvr), GIV(argumentCount)))))) > (minCogMethodAddress()))) { + linkSendAtintooffsetreceiver(longAt(GIV(stackPointer)), mframeHomeMethod(GIV(framePointer)), cogMethod, (superNormalBar + ? noCheckEntryOffset() + : entryOffset()), rcvr); + } + handleMNUInMachineCodeToclassForMessage(errSelIdx, rcvr, classObj); + assert(0); + } + + /* Methods towards the roots of the class hierarchy are shared by many subclasses. + It is likely to be profitable to cog these methods but becAuse they're being sent to + instances of different classes they may not be found in the cache. Luckily we have + lastMethodCacheProbeWrite to track recent writes to the cache and hence can detect + when a method is used more than once but via different classes. */ + if ((GIV(methodCache)[GIV(lastMethodCacheProbeWrite) + MethodCacheMethod]) == GIV(newMethod)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod1 = ((CogMethod *) methodHeader); + if (((cogMethod1->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod1, selector); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), selector); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + } + + /* check for coggability because method is in the cache + Method found and has a cog method. Attempt to link to it. The receiver's class may be young. + If the Cogit can't store young classes in inline caches we can link to an open PIC instead. */ + if (/* maybeMethodHasCogMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, selector); + } + else { + /* Deal with anonymous accessors, e.g. in Newspeak. The cogMethod may not have the + correct selector. If not, try and compile a new method with the correct selector. */ + if (((cogMethod->selector)) != selector) { + if ((newCogMethod = cogselector(GIV(newMethod), selector))) { + cogMethod = newCogMethod; + } + } + } + if (((cogMethod->selector)) == selector) { + linkSendAtintooffsetreceiver(longAt(GIV(stackPointer)), mframeHomeMethod(GIV(framePointer)), cogMethod, (superNormalBar + ? noCheckEntryOffset() + : entryOffset()), rcvr); + } + else { + if (!superNormalBar) { + patchToOpenPICFornumArgsreceiver(selector, numArgs, rcvr); + } + } + + /* If patchToOpenPICFor:.. returns we're out of code memory */ + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + executeNewMethod(); + assert(0); + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + return interpretMethodFromMachineCode(); +} + + +/* If contextSwitchIfNotNil is nil we can't context switch. + contextSwitchIfNotNil is set to nil by + - the special primitiveClosureValueNoContextSwitch entry-point in block + dispatch - the stack check in methods with primitive 198. + In a normal method contextSwitchIfNotNil will be the method (see e.g. + SimpleStackBasedCogit>>compileFrameBuild). In a block it will be the + closure (see e.g. SimpleStackBasedCogit>>compileMethodBody). */ + + /* CoInterpreter>>#ceStackOverflow: */ +void +ceStackOverflow(sqInt contextSwitchIfNotNil) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ceMethodAbortRetAddr; + CogBlockMethod *cogMethod; + sqInt switched; + + /* begin popStack */ + ceMethodAbortRetAddr = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* begin mframeCogMethod: */ + cogMethod = ((CogBlockMethod *) ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodMask)); + assert((ceMethodAbortRetAddr - (abortOffset())) == (((usqInt)(asCogHomeMethod(cogMethod))))); + GIV(instructionPointer) = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + GIV(method) = (GIV(newMethod) = (GIV(messageSelector) = GIV(nilObj))); + switched = handleStackOverflowOrEventAllowContextSwitch(contextSwitchIfNotNil != 0); + returnToExecutivepostContextSwitch(0, switched); + error("should not be reached"); +} + + +/* The high resolution timer has been found to be greater than + nextProfileTick after calling a primitive. + Now take a sample iff nextProfileTick has not been reset. c.f. + checkProfileTickPostPrimitive:. If the + primitive succeeds then signal the profile semaphore (activate the + profiling process) immediately + so that the sample is attributable to the current sender. */ + + /* CoInterpreter>>#ceTakeProfileSample */ +void +ceTakeProfileSample(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + + /* avoid calling assert in real VM cuz this may run on Smalltalk stack. */ + if (!GIV(profileProcess)) { + if (GIV(nextProfileTick) > 0) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + GIV(profileProcess) = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + if (!GIV(primFailCode)) { + GIV(profileMethod) = GIV(newMethod); + + /* Zero nextProfileTick because signalling the semaphore is when the VM effectively delivers the sample. */ + + /* begin zeroNextProfileTick */ + GIV(nextProfileTick) = 0; + if (synchronousSignal(GIV(profileSemaphore))) { + returnToExecutivepostContextSwitch(0, 1); + assert(!(1)); + } + else { + /* we reach here when there is a higher priority process than the profile process active, e.g. the timer process */ + } + } + } + else { + GIV(profileMethod) = null; + } + } + + /* if not nil a sample has already been taken. + force an interrupt check, but do not use forceInterruptCheck (which may + call interruptCheckChain) so that this can be run on the Smalltalk stack + from compileOnStackExternalPrimitive:flags: */ + GIV(stackLimit) = ((char *) (((usqInt) -1))); +} + + /* CoInterpreter>>#ceTraceBlockActivation */ +void +ceTraceBlockActivation(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt thingOne; + + if (recordBlockTrace()) { + thingOne = ((mframeHomeMethod(GIV(framePointer)))->methodObject); + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceBlockActivation) << TraceTypeShift))))) + CSFromMachineCode); + GIV(traceLog)[GIV(traceLogIndex) + 1] = thingOne; + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + if (printOnTrace()) { + printActivationNameForreceiverisBlockfirstTemporary(((mframeHomeMethod(GIV(framePointer)))->methodObject), /* frameReceiver: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? longAt(GIV(framePointer) + FoxMFReceiver) + : longAt(GIV(framePointer) + FoxIFReceiver)), 1, null); + cr(); + } + } +} + + /* CoInterpreter>>#ceTraceLinkedSend: */ +void +ceTraceLinkedSend(sqInt theReceiver) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cogMethod; + sqInt selector; + sqInt tagBits; + sqInt thingOne; + sqInt thingTwo; + + cogMethod = ((CogMethod *) ((longAt(GIV(stackPointer))) - (traceLinkedSendOffset()))); + if (((cogMethod->cmNumArgs)) > (numRegArgs())) { + } + else { + } + GIV(stackPointer) + BytesPerWord; + + /* stackValue: */ + longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* empty maybeCheckStackDepth:sp:pc: */ + + /* cogit recordSendTrace ifTrue: is implicit; wouldn't compile the call otherwise. */ + thingOne = /* fetchClassOf: */ + ((tagBits = theReceiver & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(theReceiver)); + thingTwo = (cogMethod->selector); + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + TraceSend) + CSFromMachineCode); + GIV(traceLog)[GIV(traceLogIndex) + 1] = thingOne; + GIV(traceLog)[GIV(traceLogIndex) + 2] = thingTwo; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + if (printOnTrace()) { + printActivationNameForreceiverisBlockfirstTemporary((cogMethod->methodObject), theReceiver, 0, null); + cr(); + } + selector = (cogMethod->selector); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(selector), lengthOfMaybeImmediate(selector), /* fetchClassTagOf: */ + ((tagBits = theReceiver & (tagMask())) + ? tagBits + : (longAt((void *)(theReceiver))) & (classIndexMask()))); +} + + +/* For assertion checking. */ + + /* CoInterpreter>>#ceTraceStoreOf:into: */ +void +ceTraceStoreOfinto(sqInt aValue, sqInt anObject) +{ + assert((isImmediate(aValue)) + || (addressCouldBeObj(aValue))); + assert(addressCouldBeObj(anObject)); +} + + /* CoInterpreter>>#cFramePointerAddress */ +usqInt +cFramePointerAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(CFramePointer)))); +} + + /* CoInterpreter>>#checkAssertsEnabledInCoInterpreter */ +void +checkAssertsEnabledInCoInterpreter(void) +{ + sqInt assertsAreEnabledInCoInterpreter; + + assertsAreEnabledInCoInterpreter = 0; + assert(assertsAreEnabledInCoInterpreter); +} + + +/* Perform an integrity/leak check using the heapMap. Assume + clearLeakMapAndMapAccessibleObjects has set a bit at each + object's header. Check that all object references in machine + code are valid. Answer if all checks pass. */ + + /* CoInterpreter>>#checkCodeIntegrity: */ +static NoDbgRegParms sqInt +checkCodeIntegrity(sqInt gcModes) +{ + return checkIntegrityOfObjectReferencesInCode(gcModes); +} + + +/* In Spur a primitive may fail due to encountering a forwarder. On failure, + check the accessorDepth for the + primitive and if non-negative scan the args to the depth, following any + forwarders. Answer if any are found + so the prim can be retried. The primitive index is derived from newMethod. + + See + http://www.mirandabanda.org/cogblog/2014/02/08/primitives-and-the-partial-read-barrier/ + and SpurMemoryManager's class comment. */ +/* Override to log and export to Cogit */ + + /* CoInterpreter>>#checkForAndFollowForwardedPrimitiveState */ +sqInt +checkForAndFollowForwardedPrimitiveState(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt accessorDepth; + sqInt aMethodSelectorOrImmediate; + usqInt firstBytecode; + sqInt found; + sqInt header; + sqInt index; + sqInt methodHeader; + sqInt oop; + sqInt primIndex; + sqInt referent; + sqInt scannedStackFrame; + sqInt tagBits; + + if ((recordPrimTrace()) + && (!primTracePluginName)) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveFailure << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + aMethodSelectorOrImmediate = /* fetchClassOf: */ + ((tagBits = (longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord))) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)))); + + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = aMethodSelectorOrImmediate; + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + assert(failed()); + found = 0; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIndex = 0; + } + assert((GIV(argumentCount) == (argumentCountOf(GIV(newMethod)))) + || (isMetaPrimitiveIndex(primIndex))); + + /* First things first; make sure the metadata has been followed before it is accessed to derive accessorDepth... */ + if (((/* isCalloutPrimitiveIndex: */ + (primIndex == PrimNumberExternalCall) + || (primIndex == PrimNumberFFICall)) + || (primIndex == PrimNumberDoExternalCall)) + && (unfollowFirstLiteralOfMaybeCalloutMethodprimitiveIndex(GIV(newMethod), primIndex))) { + found = 1; + } + + /* If the primitive is one of the meta primitives PrimNumberDoPrimitive or PrimNumberDoExternalCall, then + metaAccessorDepth will have been set to nil at the start of the primitive, and to the accessor depth of the + called primitive (or external call) immediately before dispatch. Hence if primIndex is that of a meta primitive + then if metaAccessorDepth is -2, the accessor depth is that of the meta primitive, and if > -2, then + metaAccessorDepth is the accessor depth of the primitive (or external call). Similarly, if the primitive is + primitiveExternalCall then the accessor depth is that of primitiveExternalCall until primitiveFunctionPointer + is assigned, at which point the accessor depth is taken from the slot in newMethod's first literal. */ + accessorDepth = ((/* isMetaPrimitiveIndex: */ + (primIndex == PrimNumberDoPrimitive) + || (primIndex == PrimNumberDoExternalCall)) + && (GIV(metaAccessorDepth) > -2) + ? GIV(metaAccessorDepth) + : accessorDepthForPrimitiveMethod(GIV(newMethod))); + assert(((accessorDepth >= -1) && (accessorDepth <= 5))); + if (accessorDepth >= 0) { + scannedStackFrame = 0; + for (index = 0; index <= GIV(argumentCount); index += 1) { + oop = longAt((void *)(GIV(stackPointer) + (index * BytesPerWord))); + if ((!(oop & (tagMask())))) { + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + assert(index < GIV(argumentCount)); + found = 1; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + + /* stackValue:put: */ + longAtput((void *)(GIV(stackPointer) + (index * BytesPerWord)),oop); + if (!scannedStackFrame) { + scannedStackFrame = 1; + + /* Avoid repeated primitive failures by following all state in the current stack frame. */ + followForwardedFrameContentsstackPointer(GIV(framePointer), GIV(stackPointer) + ((GIV(argumentCount) + 1) * BytesPerWord)); + } + } + if ((accessorDepth > 0) + && ((/* hasPointerFields: */ + ((!(oop & (tagMask())))) + && (hasPointerFieldsNonImm(oop))) + && (followForwardedObjectFieldstoDepth(oop, accessorDepth)))) { + found = 1; + } + } + } + } + if (found + && (recordPrimTrace())) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveRetry << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + return found; +} + + +/* Necessary because we very much want CStackPointer and CFramePointer to be + static/private and grouped + with other interpreter variables which will hence be accessed via + VarBaseReg on platforms that have one. + */ + + /* CoInterpreter>>#checkIfCFramePointerInUse */ +sqInt +checkIfCFramePointerInUse(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return isCFramePointerInUse((&GIV(CFramePointer)), (&GIV(CStackPointer))); +} + + /* CoInterpreter>>#checkingLongRunningPrimitives */ +#if LRPCheck +int +checkingLongRunningPrimitives(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(longRunningPrimitiveCheckSemaphore); +} +#endif /* LRPCheck */ + + +/* Check if the argument is an ok object. + If this is a pointers object, check that its fields are all okay oops. */ + + /* CoInterpreter>>#checkOkayFields: */ +static NoDbgRegParms sqInt +checkOkayFields(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fieldOop; + sqInt hasYoung; + sqInt i; + sqInt iSqInt; + + if ((oop == null) + || (oop == 0)) { + return 1; + } + if ((((oop) & 7) == 1)) { + return 1; + } + if (!(checkOkayOop(oop))) { + return 0; + } + if (!(checkOopHasOkayClass(oop))) { + return 0; + } + if (!((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + || (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return 1; + } + hasYoung = 0 /* (hasSpurMemoryManagerAPI not) */; + i = (numPointerSlotsOf(oop)) - 1; + while (i >= 0) { + fieldOop = longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(fieldOop & (smallIntegerTag())))) { + if ((i == 0) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) { + if (!(methodFor(((void *)fieldOop)))) { + fprintf(GIV(transcript), + "method %p has an invalid cog method reference\n", + ((void *)oop)); + return 0; + } + } + else { + hasYoung = hasYoung + || (/* isYoung: */ + ((!(fieldOop & (tagMask())))) + && (oopisLessThan(fieldOop, GIV(oldSpaceStart)))); + if (!(checkOkayOop(fieldOop))) { + return 0; + } + if (!(checkOopHasOkayClass(fieldOop))) { + return 0; + } + } + } + i -= 1; + } + if (hasYoung) { + /* begin checkOkayYoungReferrer: */ + if (oopisLessThan(oop, GIV(oldSpaceStart))) { + return 1; + } + if (!((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + fprintf(GIV(transcript), + "remembered bit is not set in %p\n", + ((void *)oop)); + return 0; + } + + /* begin isInRememberedSet: */ + for (iSqInt = 0; iSqInt < GIV(rememberedSetSize); iSqInt += 1) { + if ((GIV(rememberedSet)[iSqInt]) == oop) { + return 1; + } + } + fprintf(GIV(transcript), + "%p has remembered bit set but is not in remembered set\n", + ((void *)oop)); + return 0; + } + return 1; +} + + +/* Check if the profile timer has expired during a primitive and if so take a + sample. If the primitive has failed sample the profileMethod as nil. */ +/* If continuing from primitive[Full]ClosureValueNoContextSwitch defer until + the next check + */ + + /* CoInterpreter>>#checkProfileTickPostPrimitive */ +static NeverInline void +checkProfileTickPostPrimitive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + + if (GIV(deferProfileCheckForVNCS)) { + GIV(deferProfileCheckForVNCS) = 0; + } + else { + if ((GIV(nextProfileTick) > 0) + && ((ioHighResClock()) >= GIV(nextProfileTick))) { + if (!GIV(profileProcess)) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + GIV(profileProcess) = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + if (GIV(primFailCode)) { + GIV(profileMethod) = null; + } + else { + GIV(profileMethod) = GIV(newMethod); + + /* begin zeroNextProfileTick */ + GIV(nextProfileTick) = 0; + if (synchronousSignal(GIV(profileSemaphore))) { + returnToExecutivepostContextSwitch(0, 1); + assert(!(1)); + } + else { + /* we reach here when there is a higher priority process than the profile process active, e.g. the timer process */ + } + } + forceInterruptCheck(); + } + } + } +} + + /* CoInterpreter>>#cogMethodHasRealNonProfilingPrimitive: */ +static NoDbgRegParms sqInt +cogMethodHasRealNonProfilingPrimitive(CogMethod *cogMethod) +{ + return (((cogMethod->cmType)) >= CMMethod) + && (isRealNonProfilingPrimitiveMethod((cogMethod->methodObject))); +} + + /* CoInterpreter>>#cogMethodHasTooManyLiterals: */ +sqInt +cogMethodHasTooManyLiterals(CogMethod *aCogMethod) +{ + return !(((/* begin literalCountOfMethodHeader: */ + assert((((((aCogMethod->methodHeader))) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((((aCogMethod->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile); +} + + /* CoInterpreter>>#cogMethodOf: */ +CogMethod * +cogMethodOf(sqInt aMethodOop) +{ + sqInt methodHeader; + + methodHeader = longAt((void *)((aMethodOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + return ((CogMethod *) methodHeader); +} + + /* CoInterpreter>>#commenceCogCompiledCodeCompaction */ +static void +commenceCogCompiledCodeCompaction(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong startTime; + sqInt top; + + GIV(cogCompiledCodeCompactionCalledFor) = 0; + if (recordEventTrace()) { + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceCodeCompaction) << TraceTypeShift)))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = 0; + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + if (recordPrimTrace()) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TraceCodeCompaction << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + startTime = ioUTCMicrosecondsNow(); + + /* This can be called in a number of circumstances. The instructionPointer + may contain a native pc that must be relocated. There may already be a + pushed instructionPointer on stack. Clients ensure that instructionPointer + is 0 if it should not be pushed and/or relocated. Pushing twice is a mistake + because only the top one will be relocated. */ + if (GIV(instructionPointer)) { + /* better not have already been pushed */ + assert((((usqInt)(stackTop()))) != GIV(instructionPointer)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + (GIV(stackPage)->headSP = GIV(stackPointer)); + } + assertValidStackedInstructionPointers(__LINE__); + compactCogCompiledCode(); + + /* begin nilUncoggableMethods */ + GIV(lastCoggableInterpretedBlockMethod) = (GIV(lastUncoggableInterpretedBlockMethod) = null); + if (GIV(instructionPointer)) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin externalWriteBackHeadStackPointer */ + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + (GIV(stackPage)->headSP = GIV(stackPointer)); + } + assertValidStackedInstructionPointers(__LINE__); + GIV(statCodeCompactionCount) += 1; + GIV(statCodeCompactionUsecs) += (ioUTCMicrosecondsNow()) - startTime; + if (checkForLeaks) { + clearLeakMapAndMapAccessibleObjects(); + asserta(checkCodeIntegrity(0)); + } +} + + /* CoInterpreter>>#compilationBreakpointFor: */ +void +compilationBreakpointFor(sqInt selectorOop) +{ + suppressHeartbeatFlag = 1; + warning("compilation send break (heartbeat suppressed)"); +} + + +/* Answer a value to store in the InstructionPointer index of a context + object for theIP and theFP. + Mapping native pcs to bytecode pcs is quite expensive, requiring a search + through the method + map. We mitigate this cost by deferring mapping until we really have to, + which is when a context's + instruction pointer is accessed by Smalltalk code (either direct inst var + access or through the + instVarAt: primitive). But to defer mapping we have to be able to + distinguish machine code from + bytecode pcs, which we do by using negative values for machine code pcs. + So if the frame is a + machine code one answer the negation of the offset in the cog method. + + As a whorish performance hack we also include the block method offset in + the pc of a block. + The least significant 16 bits are the native pc and the most significant + 14 bits are the block + start, in block alignment units. So when mapping back we can find the + start of the block. + + See mustMapMachineCodePC:context: for the code that does the actual + mapping. */ + + /* CoInterpreter>>#contextInstructionPointer:frame: */ +static NoDbgRegParms sqInt +contextInstructionPointerframe(sqInt theIP, char *theFP) +{ + sqInt blockOffset; + CogBlockMethod *cogMethod; + CogMethod *homeMethod; + + assert(validInstructionPointerinFrame(theIP, theFP)); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* begin mframeCogMethod: */ + cogMethod = ((CogBlockMethod *) ((longAt(theFP + FoxMethod)) & MFMethodMask)); + if (theIP == (ceCannotResumePC())) { + return HasBeenReturnedFromMCPCOop; + } + if (((cogMethod->cmType)) >= CMMethod) { + return (((usqInt)((((sqInt)cogMethod)) - theIP) << 3) | 1); + } + + /* cmHomeMethod */ + if (((cogMethod->cmType)) >= CMMethod) { + homeMethod = ((CogMethod *) cogMethod); + } + else { + assert(CMBlock == (cogMethod->cmType)); + homeMethod = ((CogMethod *) ((((usqInt)cogMethod)) - ((cogMethod->homeOffset)))); + } + blockOffset = ((((sqInt)homeMethod)) - (((sqInt)cogMethod))) / (blockAlignment()); + return (((usqInt)((((((usqInt)(blockOffset) << 16)))) | (((((sqInt)cogMethod)) - theIP) & 0xFFFF)) << 3) | 1); + } + return (((usqInt)(((((theIP == (ceReturnToInterpreterPC()) + ? ((usqInt)(longAt(theFP + FoxIFSavedIP))) + : theIP)) - (longAt(theFP + FoxMethod))) - BaseHeaderSize) + 2) << 3) | 1); +} + + +/* Convert the given machine code frame to an interpreter frame. Back up the + pc by + pcDelta. Support for backing up the pc in primitiveSuspend for processes + waiting on condition variables. Support for mustBeBoolean in the + RegisterAllocatingCogit and + for cloneContext: in shallowCopy when a code compaction is caused by + machine code + to bytecode pc mapping. */ + + /* CoInterpreter>>#convertFrame:toInterpreterFrame: */ +static NoDbgRegParms void +convertFrametoInterpreterFrame(char *theFP, sqInt pcDelta) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogBlockMethod *cogMethod; + CogMethod *homeMethod; + sqInt methodHeader; + sqInt methodObj; + sqInt oop; + char *p; + usqInt startBcpc; + usqInt theIP; + char *theNewSP; + StackPage *thePage; + char *theSP; + + thePage = ((StackPage *) 0); + assert(isMachineCodeFrame(theFP)); + if (theFP == GIV(framePointer)) { + theIP = GIV(instructionPointer); + theSP = GIV(stackPointer); + } + else { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(theFP == ((thePage->headFP))); + theSP = (thePage->headSP); + theIP = longAt(theSP); + } + + /* begin mframeCogMethod: */ + cogMethod = ((CogBlockMethod *) ((longAt(theFP + FoxMethod)) & MFMethodMask)); + if ((((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0) + && (!((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)))) { + /* cmHomeMethod */ + if (((cogMethod->cmType)) >= CMMethod) { + homeMethod = ((CogMethod *) cogMethod); + } + else { + assert(CMBlock == (cogMethod->cmType)); + homeMethod = ((CogMethod *) ((((usqInt)cogMethod)) - ((cogMethod->homeOffset)))); + } + methodHeader = (homeMethod->methodHeader); + methodObj = (homeMethod->methodObject); + startBcpc = (cogMethod->startpc); + } + else { + methodHeader = ((((CogMethod *) cogMethod))->methodHeader); + methodObj = ((((CogMethod *) cogMethod))->methodObject); + startBcpc = (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop; + } + + /* Map the machine code instructionPointer to the interpreter instructionPointer of the branch. */ + theIP = bytecodePCForstartBcpcin(theIP, startBcpc, cogMethod); + + /* fetchByte uses pre-increment, so pre decrement */ + theIP = (((methodObj + BaseHeaderSize) + theIP) - pcDelta) - 1; + assert(validInstructionPointerinMethodframePointer(theIP, methodObj, theFP)); + + /* Make space for the two extra fields in an interpreter frame */ + for (p = theSP; p <= (theFP + FoxMFReceiver); p += BytesPerWord) { + oop = longAt(p); + longAtput((p - BytesPerWord) - BytesPerWord,longAt(p)); + } + + /* Fill in the fields */ + longAtput(theFP + FoxIFrameFlags,/* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(((cogMethod->cmNumArgs))) << ((BytesPerWord * 8) - 8))))) + (((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag + ? 1ULL << ((BytesPerWord * 8) - 16) + : 0))) + (((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag + ? 1ULL << ((BytesPerWord * 8) - 24) + : 0)) + : ((1 + ((((usqInt)(((cogMethod->cmNumArgs))) << 8)))) + (((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag + ? 0x10000 + : 0))) + (((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag + ? 0x1000000 + : 0)))); + longAtput(theFP + FoxIFSavedIP,theIP); + longAtput(theFP + FoxMethod,methodObj); + if (theFP == GIV(framePointer)) { + GIV(stackPointer) = (GIV(stackPointer) - BytesPerWord) - BytesPerWord; + GIV(instructionPointer) = theIP; + + /* begin setMethod:methodHeader: */ + GIV(method) = methodObj; + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + } + else { + theNewSP = (theSP - BytesPerWord) - BytesPerWord; + (thePage->headSP = theNewSP); + longAtput(theNewSP,ceReturnToInterpreterPC()); + } +} + + /* CoInterpreter>>#cReturnAddressAddress */ +usqInt +cReturnAddressAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(CReturnAddress)))); +} + + /* CoInterpreter>>#cStackPointerAddress */ +usqInt +cStackPointerAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(CStackPointer)))); +} + + +/* Divorce all frames that satisfy criterion and answer the current + activeContext. + */ + + /* CoInterpreter>>#divorceAllFramesSuchThat: */ +static NoDbgRegParms sqInt +divorceAllFramesSuchThat(sqInt (*criterion)(char *fp)) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + StackPage *aPage; + char *calleeFP; + sqInt divorcedSome; + sqInt i; + sqInt object; + sqInt theContext; + char *theFP; + char *theSP; + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l1; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l1: + + /* begin ensurePushedInstructionPointer */ + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + do { + if (GIV(stackPage)) { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + + /* This is needed for the assert in externalDivorceFrame:andContext: + Slang can't currently cope with the lack of the variable here. + Something to do with the preceding statement. Take it out + and the code is good. leave it in and we get do { ... } while(l1:) */ + + /* begin divorceSomeFramesIf: */ + divorcedSome = 0; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + aPage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(aPage))) { + /* this to avoid assert in externalDivorceFrame:andContext: */ + markStackPageMostRecentlyUsed(GIV(stackPage)); + + /* begin divorceAFrameIf:in: */ + calleeFP = ((char *) 0); + theFP = (aPage->headFP); + theSP = (aPage->headSP); + + /* theSP points at hottest item on frame's stack */ + theSP += BytesPerWord; + while (1) { + if (criterion(theFP)) { + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + theContext = longAt(theFP + FoxThisContext); + goto l4; + } + theContext = marryFrameSP(theFP, theSP); + /* end ensureFrameIsMarried:SP: */ +l4: + externalDivorceFrameandContext(theFP, theContext); + if (theFP == GIV(framePointer)) { + GIV(framePointer) = 0; + } + goto l2; + } + calleeFP = theFP; + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!(theFP != 0)) break; + /* theSP points at stacked hottest item on frame's stack */ + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(calleeFP))); + theSP = (calleeFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(calleeFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(calleeFP))->cmNumArgs) + : byteAt((calleeFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + goto l3; +l2: + divorcedSome = 1; +l3:; + } + } + } while(divorcedSome); + return activeContext; +} + + /* CoInterpreter>>#dumpStackAlignmentInfo */ +void +dumpStackAlignmentInfo(void) +{ + printf("STACK_ALIGN_BYTES:%d STACK_ALIGN_MASK:%d STACK_FP_ALIGNMENT:%d STACK_SP_ALIGNMENT:%d\n", + ((int) STACK_ALIGN_BYTES), + ((int) STACK_ALIGN_MASK), + ((int) STACK_FP_ALIGNMENT), + ((int) STACK_SP_ALIGNMENT)); +} + + +/* Map all native pcs to bytecoded pcs in all contexts whose method matches + criterion. + */ + + /* CoInterpreter>>#ensureAllContextsHaveBytecodePCsIf: */ +static NoDbgRegParms void +ensureAllContextsHaveBytecodePCsIf(sqInt (*criterion)(sqInt methodOop)) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt pc; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + pc = 0; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (((((((pc = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))))))) & 7) == 1)) + && ((pc < 0) + && (criterion((followObjFieldofObject(MethodIndex, obj))))))) { + widowOrForceToBytecodePC(obj); + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } +} + + +/* Make sure the context has a byetcode pc. Can only be used on single + contexts. + */ + + /* CoInterpreter>>#ensureContextHasBytecodePC: */ +static NoDbgRegParms void +ensureContextHasBytecodePC(sqInt aContext) +{ + sqInt pc; + + assert(!(isMarriedOrWidowedContext(aContext))); + pc = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + if (((((pc) & 7) == 1)) + && (((pc = (pc >> 3))) < 0)) { + pc = mustMapMachineCodePCcontext(pc, aContext); + assert(validBCPCinMethod((pc >> 3), fetchPointerofObject(MethodIndex, aContext))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aContext)) + && (!(isForwarded(aContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, aContext, pc)); + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),pc); + } +} + + +/* Safety to give the JIT lattitude in calling convention. Conceptually, + returning a value to a context involves pushing that value onto the stack. + This is used + in Squeak methods such as ContextPart>>jump + jump + | top | + thisContext sender push: nil. + stackp = 0 ifTrue: [self stepToSendOrReturn]. + stackp = 0 ifTrue: [self push: nil]. + top := self pop. + thisContext privSender: self. + ^top + Here jump may pop the value of a temporary variable off the stack which + will, conceptually and, in the interpreter, actually, get pushed back on + return. But + if the JIT is mapping the stack to registers disaster may ensue since the + value may not get pushed to the stack and code may access an invalid value + (e.g. a pc). + + The solution is to fall back on the interpreter. If the stack pointer is + changed we + also ensure the pc is a bytecode pc (+ive) which will cause + makeBaseFrameFor: to create an interpreter frame if the context is + executed again. */ + + /* CoInterpreter>>#ensureContextIsExecutionSafeAfterAssignToStackPointer: */ +static NoDbgRegParms void +ensureContextIsExecutionSafeAfterAssignToStackPointer(sqInt aContext) +{ + ensureContextHasBytecodePC(aContext); +} + + +/* Execute a CogMethod from a linked send. The receiver, + arguments and return address are on the Smalltalk stack. First + push the entry-point and finally the register argument(s). Then write + back the frame pointers and call the routine that will pop off the + register argument(s) and jump to the entry by executing a return + instruction. + In the simple jit only the receiver gets passed in registers, so only the + receiver gets pushed. */ + + /* CoInterpreter>>#executeCogMethod:fromLinkedSendWithReceiver: */ +void +executeCogMethodfromLinkedSendWithReceiver(CogMethod *cogMethod, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + char *sp; + + assertCStackWellAligned(); + assert(isMachineCodeFrame(GIV(framePointer))); + assertValidExecutionPointersimbarline(longAt(GIV(stackPointer)), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + if ((numRegArgs()) > 0) { + if (((cogMethod->cmNumArgs)) <= (numRegArgs())) { + callRegisterArgCogMethodatreceiver(cogMethod, entryOffset(), rcvr); + } + } + + /* dont use and: so as to get Slang to inline cogit numRegArgs > 0 */ + object = (((sqInt)cogMethod)) + (entryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((sp = GIV(stackPointer) - BytesPerWord),rcvr); + GIV(stackPointer) = sp; + ceCallCogCodePopReceiverReg(); +} + + +/* Execute a CogMethod from an unlinked send. The receiver, + arguments and return address are on the Smalltalk stack. First + push the entry-point and finally the register argument(s). Then write + back the frame pointers and call the routine that will pop off the + register argument(s) and jump to the entry by executing a return + instruction. + In the simple jit only the receiver gets passed in registers, so only the + receiver gets pushed. */ + + /* CoInterpreter>>#executeCogMethod:fromUnlinkedSendWithReceiver: */ +static NoDbgRegParms void +executeCogMethodfromUnlinkedSendWithReceiver(CogMethod *cogMethod, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + char *sp; + + assertCStackWellAligned(); + assert(isMachineCodeFrame(GIV(framePointer))); + assertValidExecutionPointersimbarline(longAt(GIV(stackPointer)), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + if ((numRegArgs()) > 0) { + if (((cogMethod->cmNumArgs)) <= (numRegArgs())) { + callRegisterArgCogMethodatreceiver(cogMethod, noCheckEntryOffset(), rcvr); + } + } + + /* dont use and: so as to get Slang to inline cogit numRegArgs > 0 */ + object = (((sqInt)cogMethod)) + (noCheckEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((sp = GIV(stackPointer) - BytesPerWord),rcvr); + GIV(stackPointer) = sp; + ceCallCogCodePopReceiverReg(); +} + + +/* Execute a closed PIC from a linked send, to redispatch based on the rcvr. + The receiver, arguments and return address are on the Smalltalk stack. + First push the entry-point and finally the register argument(s). Then + write back the frame pointers and call the routine that will pop off the + register argument(s) and jump to the entry by executing a return + instruction. + In the simple jit only the receiver gets passed in registers, so only the + receiver gets pushed. */ + + /* CoInterpreter>>#executeCogPIC:fromLinkedSendWithReceiver:andCacheTag: */ +void +executeCogPICfromLinkedSendWithReceiverandCacheTag(CogMethod *cogPIC, sqInt rcvr, sqInt cacheTag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + char *sp; + + assertCStackWellAligned(); + assert(isMachineCodeFrame(GIV(framePointer))); + assertValidExecutionPointersimbarline(longAt(GIV(stackPointer)), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + object = (((sqInt)cogPIC)) + (entryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + if ((numRegArgs()) > 0) { + if (((cogPIC->cmNumArgs)) <= (numRegArgs())) { + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),cacheTag); + GIV(stackPointer) = sp; + switch ((cogPIC->cmNumArgs)) { + case 0: + ceCall0ArgsPIC(); + break; + case 1: + ceCall1ArgsPIC(); + break; + case 2: + ceCall2ArgsPIC(); + break; + default: +; + } + error("not reached"); + } + } + + /* dont use and: so as to get Slang to inline cogit numRegArgs > 0 */ + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),rcvr); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),cacheTag); + GIV(stackPointer) = sp; + ceCallCogCodePopReceiverAndClassRegs(); +} + + +/* Execute newMethod - either primitiveFunctionPointer must be set directly + (i.e. from primitiveExecuteMethod et al), or it would have been set + probing the method cache (i.e. primitivePerform et al). */ + + /* CoInterpreter>>#executeNewMethod */ +static sqInt +executeNewMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int inInterpreter; + sqInt methodHeader; + + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + if (primitiveFunctionPointer) { + if ((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex) { + externalQuickPrimitiveResponse(); + returntoExecutive(popStack(), inInterpreter); + return null; + } + + /* slowPrimitiveResponse may of course context-switch. If so we must reenter the + new process appopriately, returning only if we've reached here directly from the + interpreter and have found an interpreter frame. The instructionPointer tells us + from whence we came. */ + if (slowPrimitiveResponse()) { + returntoExecutive(popStack(), inInterpreter); + return null; + } + } + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* if not primitive, or primitive failed, activate the method */ + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,((sqInt)GIV(instructionPointer))); + GIV(instructionPointer) = ceReturnToInterpreterPC(); + } + activateNewCogMethodinInterpreter(((CogMethod *) methodHeader), inInterpreter); + } + else { + activateNewMethod(); + } + return 0; +} + + +/* Execute newMethod - either primitiveFunctionPointer must be set directly + (i.e. from primitiveExecuteMethod et al), or it would have been set + probing the method cache (i.e. primitivePerform et al). + Eagerly compile it if appropriate so that doits are fast. */ + + /* CoInterpreter>>#executeNewMethodJitting */ +static sqInt +executeNewMethodJitting(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int inInterpreter; + sqInt methodHeader; + + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + if (primitiveFunctionPointer) { + if ((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex) { + externalQuickPrimitiveResponse(); + returntoExecutive(popStack(), inInterpreter); + return null; + } + + /* slowPrimitiveResponse may of course context-switch. If so we must reenter the + new process appopriately, returning only if we've reached here directly from the + interpreter and have found an interpreter frame. The instructionPointer tells us + from whence we came. */ + if (slowPrimitiveResponse()) { + returntoExecutive(popStack(), inInterpreter); + return null; + } + } + + /* Eagerly compile it if appropriate so that doits are fast. */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if (!((!(methodHeader & (smallIntegerTag()))))) { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(nilObj)); + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + + /* if not primitive, or primitive failed, activate the method */ + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,((sqInt)GIV(instructionPointer))); + GIV(instructionPointer) = ceReturnToInterpreterPC(); + } + activateNewCogMethodinInterpreter(((CogMethod *) methodHeader), inInterpreter); + } + else { + activateNewMethod(); + } + return 0; +} + + +/* Fetch an instance variable from a maybe married context. + If the context is still married compute the value of the + relevant inst var from the spouse frame's state. + + If the context is single but has a negative instruction pointer + recognise that the instruction pointer is actually into machine + code and convert it to the corresponding bytecode pc. */ + + /* CoInterpreter>>#externalInstVar:ofContext: */ +static NoDbgRegParms sqInt +externalInstVarofContext(sqInt offset, sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt value; + + assert(isContext(aContext)); + assert(offset <= (ReceiverIndex + (checkStackPointerForMaybeMarriedContext(aContext)))); + + /* method, closureOrNil & receiver need no special handling; only + sender, pc & stackp have to be computed for married contexts. */ + if (!(offset <= StackPointerIndex)) { + return longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(offset) << (shiftForWord())))))); + } + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))) { + return fetchPointerofMarriedContext(offset, aContext); + } + value = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(offset) << (shiftForWord())))))); + + /* Why not update the slot to refer to the mapped pc? We're damned if we do, and damned if + we don't. If it is and the context is live and is returned to, then the pc will be mapped back + to machine code. If we don't then the mapping will be repeated on every access. What we + need are stats that show whether or not a read here predicts one or other outcome with any + certainty. For now, we keep it as it has been for a long time and do not update the slot. */ + if ((offset == InstructionPointerIndex) + && (((((value) & 7) == 1)) + && ((((int) value)) < 0))) { + return mustMapMachineCodePCcontext((value >> 3), aContext); + } + return value; +} + + +/* Find the compiled method to be run when the current messageSelector is + looked up in the + class/classIndex classTagArg, setting the values of newMethod and + primitiveFunctionPointer. + */ + + /* CoInterpreter>>#findNewMethodInClassTag: */ +static NoDbgRegParms void +findNewMethodInClassTag(sqInt classTagArg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt classTag; + CogMethod *cogMethod; + sqInt fieldIndex; + sqInt methodHeader; + + if (lookupInMethodCacheSelclassTag(GIV(messageSelector), classTagArg)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + /* entry was not found in the cache; perhaps soemthing was forwarded. */ + classTag = classTagArg; + if ((/* isOopForwarded: */ + ((!(GIV(messageSelector) & (tagMask())))) + && ((!((longAt((void *)(GIV(messageSelector)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + || (classTag == (isForwardedObjectClassIndexPun()))) { + if (/* isOopForwarded: */ + ((!(GIV(messageSelector) & (tagMask())))) + && ((!((longAt((void *)(GIV(messageSelector)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + GIV(messageSelector) = handleForwardedSelectorFaultFor(GIV(messageSelector)); + } + if (classTag == (isForwardedObjectClassIndexPun())) { + classTag = handleForwardedSendFaultForTag(classTag); + } + if (lookupInMethodCacheSelclassTag(GIV(messageSelector), classTag)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + return; + } + } + + /* entry was not found in the cache; look it up the hard way */ + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((classTag >= 0) + && ((classTag <= (tagMask())) + || ((classTag >= (arrayClassIndexPun())) + && (classTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + GIV(lkupClass) = null; + goto l1; + } + fieldIndex = classTag & ((1U << (classTableMajorIndexShift())) - 1); + GIV(lkupClass) = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l1: + lookupMethodInClass(GIV(lkupClass)); + addNewMethodToCache(GIV(lkupClass)); + } +} + + +/* The flag bit can be used to flag methods that are interpreted, if it has + been requested + from the image header flags. */ + + /* CoInterpreter>>#flagMethodAsInterpreted: */ +static NoDbgRegParms void +flagMethodAsInterpreted(sqInt aMethod) +{ + sqInt rawHeader; + sqInt realHeader; + + assert(flagInterpretedMethods()); + rawHeader = longAt((void *)((aMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + realHeader = ((!(rawHeader & (smallIntegerTag()))) + ? ((((CogMethod *) rawHeader))->methodHeader) + : rawHeader); + realHeader = realHeader | ((((usqInt)(1U << MethodHeaderFlagBitPosition) << 3) | 1)); + + /* begin isCogMethodReference: */ + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + ((((CogMethod *) rawHeader))->methodHeader = realHeader); + } + else { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aMethod)) + && (!(isForwarded(aMethod)))); + assert(validStorePointerUncheckedArgs(0, aMethod, realHeader)); + longAtput((void *)((aMethod + BaseHeaderSize) + (0U << (shiftForWord()))),realHeader); + } +} + + +/* Flush the references to external functions from plugin primitives. + Then continue execution answering self. + This will force a reload of those primitives when accessed next. + Note: We must flush the method cache here also, so that any failed + primitives are looked up again. + Override to ensure that any and all activations of an external method + have a bytecode pc so that if code generation changes (e.g. a primitive + method is used, unloaded, and the reloaded primitive is marked with + the FastCPrimitiveFlag) stale machine code pcs have been eliminated. + THIS MUST BE INVOKED IN THE CONTEXT OF A PRIMITIVE. */ + + /* CoInterpreter>>#flushExternalPrimitives */ +static void +flushExternalPrimitives(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt senderOop; + sqInt startObject; + char *theFP; + char *theFrame; + StackPage *thePage; + sqInt top; + + activeContext = divorceAllFramesSuchThat(isMachineCodeFrameForExternalPrimitiveMethod); + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (((byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + flushExternalPrimitiveOf(obj); + } + else { + if (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin mapToBytecodePCIfActivationOfExternalMethod: */ + if (isExternalMethodInPlugin(longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))))) { + if (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (isWidowedContext(obj)) { + goto l1; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + externalDivorceFrameandContext(theFP, obj); + } + } + ensureContextHasBytecodePC(obj); + } + /* end mapToBytecodePCIfActivationOfExternalMethod: */ +l1:; + } + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2: + assert(oopisGreaterThan(obj, prevObj)); + } + unlinkSendsToMethodsSuchThatAndFreeIf(cogMethodHasExternalPrim, 1); + + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + + /* begin flushExternalPrimitiveTable */ + memset(externalPrimitiveTable, 0, MaxExternalPrimitiveTableSize * (sizeof(externalPrimitiveTable[0]))); + GIV(externalPrimitiveTableFirstFreeIndex) = 0; + + /* If flushing led to divorce continue in the interpreter. */ + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + ceInvokeInterpret(); + } + + /* If not, work out where we are and continue */ + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + assert(((thePage->headFP)) == theFrame); + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* Arrange that any and all cog methods with machine code primitives can be + and are discarded. + Hence scan contexts and map their PCs to bytecode PCs if required, and + scan frames, divorcing + the frames of activations if required. Then continue execution answering + result. THIS MUST BE + INVOKED IN THE CONTEXT OF A PRIMITIVE. It exists to support + vmParameterAt:put:. */ + + /* CoInterpreter>>#flushMethodsWithMachineCodePrimitivesAndContinueAnswering: */ +static NoDbgRegParms void +flushMethodsWithMachineCodePrimitivesAndContinueAnswering(sqInt result) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt senderOop; + char *sp; + char *theFrame; + StackPage *thePage; + sqInt top; + + activeContext = divorceAllFramesSuchThat(isMachineCodeFrameForCogMethodWithMachineCodePrim); + ensureAllContextsHaveBytecodePCsIf(methodHasMachineCodePrimitiveMethod); + unlinkSendsToMethodsSuchThatAndFreeIf(cogMethodHasMachineCodePrim, 1); + + /* If the top frame was divorced then continue in the interpreter. */ + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* pop bogus machine-code instructionPointer, arguments and receiver */ + + /* begin pop:thenPush: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 2) - 1) * BytesPerWord)),result); + GIV(stackPointer) = sp; + ceInvokeInterpret(); + } + + /* If not, work out where we are and continue */ + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + assert(((thePage->headFP)) == theFrame); + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + + /* begin pop:thenPush: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),result); + GIV(stackPointer) = sp; +} + + /* CoInterpreter>>#followForwardedFieldsInCurrentMethod */ +static void +followForwardedFieldsInCurrentMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cogMethod; + sqInt methodField; + + if ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())) { + /* begin mframeHomeMethod: */ + methodField = longAt(GIV(framePointer) + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + cogMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + goto l1; + } + cogMethod = ((CogMethod *) (methodField & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + followForwardedObjectFieldstoDepth((cogMethod->methodObject), 0); + followForwardedLiteralsIn(cogMethod); + } + else { + followForwardedObjectFieldstoDepth(GIV(method), 0); + } +} + + +/* A more thorough version of followForwardingPointersInStackZone that also + follows all temporaries (but not stack contents after the temps). + This allows removal of the TempVectReadBarrier in the IGC */ + + /* CoInterpreter>>#followForwardingPointersOfReceiverAndTemporariesInStackZone */ +static void +followForwardingPointersOfReceiverAndTemporariesInStackZone(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + char *frameRcvrOffset; + sqInt header; + sqInt i; + sqInt methodHeader; + sqInt newOop; + sqInt offset; + usqInt oop; + sqInt referent; + char *theFP; + usqInt theIPPtr; + StackPage *thePage; + char *theSP; + + callerFP = ((char *) 0); + if (!GIV(stackPage)) { + assert((!(GIV(mostRecentlyUsedPage))) + || (isFree(GIV(mostRecentlyUsedPage)))); + return; + } + + /* the system must be snapshotting; nothing to do... */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((thePage->baseFP)) { + assert(ifCurrentStackPageHasValidHeadPointers(thePage)); + theFP = (thePage->headFP); + + /* Skip the instruction pointer on top of stack of inactive pages. */ + theIPPtr = (!(thePage == GIV(stackPage)) + ? ((usqInt)((thePage->headSP))) + : 0); + while (1) { + assert(addressIsInPage(thePage, theFP)); + assert((!theIPPtr) + || (addressIsInPage(thePage, ((void *)theIPPtr)))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + oop = longAt(theFP + FoxMFReceiver); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxMFReceiver,followForwarded(oop)); + } + assert(!(isForwarded(((mframeHomeMethod(theFP))->methodObject)))); + frameRcvrOffset = theFP + FoxMFReceiver; + methodHeader = ((mframeHomeMethod(theFP))->methodHeader); + } + else { + oop = longAt(theFP + FoxIFReceiver); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxIFReceiver,followForwarded(oop)); + } + oop = longAt(theFP + FoxMethod); + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + newOop = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(newOop & (tagMask())))) + && ((!((longAt((void *)(newOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + newOop = longAt((void *)((newOop + BaseHeaderSize) + (0U << (shiftForWord())))); + } + offset = newOop - oop; + if ((theIPPtr) + && ((longAt((void *)(theIPPtr))) > oop)) { + longAtput((void *)(theIPPtr),(longAt((void *)(theIPPtr))) + offset); + } + longAtput(theFP + FoxIFSavedIP,(longAt(theFP + FoxIFSavedIP)) + offset); + longAtput(theFP + FoxMethod,(oop = newOop)); + } + frameRcvrOffset = theFP + FoxIFReceiver; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(oop)); + header = longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + } + theSP = frameRcvrOffset - (((((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F) * BytesPerWord); + while (theSP <= frameRcvrOffset) { + oop = longAt(theSP); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + longAtput(theSP,oop); + } + theSP += BytesPerWord; + } + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) + && ((!((longAt((void *)(longAt(theFP + FoxThisContext)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxThisContext,followForwarded(longAt(theFP + FoxThisContext))); + } + offset = (FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))); + oop = longAt(theFP + offset); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + offset,followForwarded(oop)); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theIPPtr = ((usqInt)(theFP + FoxCallerSavedIP)); + theFP = callerFP; + } + + /* And finally follow the saved context and the caller context. */ + theSP = ((thePage->baseAddress)) - BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + longAtput(theSP,followForwarded(oop)); + } + theSP += BytesPerWord; + } + } + } +} + + +/* Spur's become: is lazy, turning the becommed object into a forwarding + object to the other. + The read-barrier is minimised by arranging that forwarding pointers will + fail a method cache + probe, since notionally objects' internals are accessed only via sending + messages to them, + the exception is primitives that access the internals of the non-receiver + argument(s). + To avoid a read barrier on bytecode, literal and inst var fetch and + non-local return, we scan + the receivers (including the stacked receiver for non-local return) and + method references + in the stack zone and follow any forwarded ones. This is of course way + cheaper than + scanning all of memory as in the old become. + + Override to handle machine code frames */ + + /* CoInterpreter>>#followForwardingPointersOfReceiversInStackZone */ +static void +followForwardingPointersOfReceiversInStackZone(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt i; + sqInt newOop; + sqInt offset; + usqInt oop; + char *theFP; + usqInt theIPPtr; + StackPage *thePage; + char *theSP; + + callerFP = ((char *) 0); + if (!GIV(stackPage)) { + assert((!(GIV(mostRecentlyUsedPage))) + || (isFree(GIV(mostRecentlyUsedPage)))); + return; + } + + /* the system must be snapshotting; nothing to do... */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((thePage->baseFP)) { + assert(ifCurrentStackPageHasValidHeadPointers(thePage)); + theFP = (thePage->headFP); + + /* Skip the instruction pointer on top of stack of inactive pages. */ + theIPPtr = (!(thePage == GIV(stackPage)) + ? ((usqInt)((thePage->headSP))) + : 0); + while (1) { + assert(addressIsInPage(thePage, theFP)); + assert((!theIPPtr) + || (addressIsInPage(thePage, ((void *)theIPPtr)))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + oop = longAt(theFP + FoxMFReceiver); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxMFReceiver,followForwarded(oop)); + } + assert(!(isForwarded(((mframeHomeMethod(theFP))->methodObject)))); + } + else { + oop = longAt(theFP + FoxIFReceiver); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxIFReceiver,followForwarded(oop)); + } + oop = longAt(theFP + FoxMethod); + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + newOop = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(newOop & (tagMask())))) + && ((!((longAt((void *)(newOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + newOop = longAt((void *)((newOop + BaseHeaderSize) + (0U << (shiftForWord())))); + } + offset = newOop - oop; + if ((theIPPtr) + && ((longAt((void *)(theIPPtr))) > oop)) { + longAtput((void *)(theIPPtr),(longAt((void *)(theIPPtr))) + offset); + } + longAtput(theFP + FoxIFSavedIP,(longAt(theFP + FoxIFSavedIP)) + offset); + longAtput(theFP + FoxMethod,(oop = newOop)); + } + } + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) + && ((!((longAt((void *)(longAt(theFP + FoxThisContext)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxThisContext,followForwarded(longAt(theFP + FoxThisContext))); + } + offset = (FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))); + oop = longAt(theFP + offset); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + offset,followForwarded(oop)); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theIPPtr = ((usqInt)(theFP + FoxCallerSavedIP)); + theFP = callerFP; + } + + /* And finally follow the saved context and the caller context. */ + theSP = ((thePage->baseAddress)) - BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + longAtput(theSP,followForwarded(oop)); + } + theSP += BytesPerWord; + } + } + } +} + + +/* In the StackInterpreter the saved ip field of a base frame holds the + base frame's caller context. But in the Cog VM the first word on the + stack holds the base frame's caller context, which is immediately + above the stacked receiver. */ + + /* CoInterpreter>>#frameCallerContext: */ +static NoDbgRegParms sqInt +frameCallerContext(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + StackPage *thePage; + + assert(isBaseFrame(theFP)); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + return callerContextOrNil; +} + + /* CoInterpreter>>#frameHasContext: */ +static NoDbgRegParms int +frameHasContext(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0); +} + + +/* */ + + /* CoInterpreter>>#frameIsBlockActivation: */ +static NoDbgRegParms int +frameIsBlockActivation(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0); +} + + /* CoInterpreter>>#frameMethodObject: */ +static NoDbgRegParms sqInt +frameMethodObject(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); +} + + +/* See encodeFrameFieldHasContext:numArgs: */ + + /* CoInterpreter>>#frameNumArgs: */ +static NoDbgRegParms usqInt +frameNumArgs(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)); +} + + /* CoInterpreter>>#framePointerAddress */ +usqInt +framePointerAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(framePointer)))); +} + + /* CoInterpreter>>#framePrintDescription: */ +static NoDbgRegParms void +framePrintDescription(sqInt it) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!it) { + return; + } + if (it == GIV(nilObj)) { + fprintf(GIV(transcript), "=nil"); + return; + } + if (it == (ceReturnToInterpreterPC())) { + fprintf(GIV(transcript), "=ceReturnToInterpreterPC"); + return; + } + if (it == (ceBaseFrameReturnPC())) { + fprintf(GIV(transcript), "=ceBaseFrameReturnPC"); + return; + } + if (it == (ceCannotResumePC())) { + fprintf(GIV(transcript), "=ceCannotResumePC"); + return; + } + fprintf(GIV(transcript), + "=%" PRIdSQINT "", + it); +} + + /* CoInterpreter>>#frameReceiver: */ +static NoDbgRegParms sqInt +frameReceiver(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? longAt(theFP + FoxMFReceiver) + : longAt(theFP + FoxIFReceiver)); +} + + /* CoInterpreter>>#functionPointerForCompiledMethod:primitiveIndex:primitivePropertyFlagsInto: */ +void +(*functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(sqInt methodObj, sqInt primitiveIndex, sqInt *flagsPtr))(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt flags; + void (*functionPointer)(void); + sqInt header; + sqInt headerSqInt; + sqInt index; + sqInt lit; + sqInt litSqInt; + sqInt metadata; + usqInt metadataFlags; + sqInt primIdx; + sqInt targetFunctionIndex; + + flags = 0; + if (flagsPtr) { + flagsPtr[0] = ((flags = primitivePropertyFlagsprimIndex(methodObj, primitiveIndex))); + } + if (primitiveIndex == PrimNumberFFICall) { + /* begin functionForPrimitiveCallout */ + if ((((sqInt)primitiveCalloutPointer)) == -1) { + reloadPrimitiveCalloutPointer(); + } + if (flagsPtr) { + if (GIV(primCalloutIsExternal)) { + flagsPtr[0] = (flags | PrimCallIsExternalCall); + } + } + return primitiveCalloutPointer; + } + if (primitiveIndex == PrimNumberExternalCall) { + /* begin attemptToLinkExternalPrimitive: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + headerSqInt = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* primitiveIndexOfMethod:header: */ + if (((header & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((header >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIdx = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIdx = 0; + } + lit = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + assert((primIdx == PrimNumberExternalCall) + && (((literalCountOfMethodHeader(header)) > 0) + && ((isArray(lit)) + && ((numSlotsOf(lit)) == 4)))); + targetFunctionIndex = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord())))))); + assert((((targetFunctionIndex) & 7) == 1)); + if (!((targetFunctionIndex >> 3))) { + linkExternalCallerrInto(lit, null); + } + if (flagsPtr) { + /* N.B. We only support the FastCPrimitiveFlag on Spur because Spur will not run a GC to + satisfy an allocation in a primitive. The V3 ObjectMemory will and hence the depth of + stack needed in a V3 primitive is probably too large to safely execute on a stack page. */ + metadata = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFlagsIndex) << (shiftForWord())))))); + if ((((metadata) & 7) == 1)) { + metadataFlags = ((metadata >> 3)) & SpurPrimitiveFlagsMask; + + /* combine the specific external primitive's flags with the base flags. + Hence e.g. if profiling is in effect (as indicated by PrimCallCollectsProfileSamples + in the base flags) it remains in effect after combining the specific flags. */ + flags = flags | metadataFlags; + } + if (numBytesOf(longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralModuleNameIndex) << (shiftForWord())))))))) { + flags = flags | PrimCallIsExternalCall; + } + flagsPtr[0] = flags; + } + + /* begin functionForPrimitiveExternalCall: */ + if (!((literalCountOf(methodObj)) > 0)) { + return primitiveExternalCall; + } + + /* Check if it's an array of length 4 */ + litSqInt = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(litSqInt & (tagMask())))) + && (((byteAt((void *)(litSqInt + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((lengthOf(litSqInt)) == 4))) { + return primitiveExternalCall; + } + index = longAt((void *)((litSqInt + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord())))))); + if (!(((((index) & 7) == 1)) + && ((((index = (index >> 3))) > 0) + && (index <= MaxExternalPrimitiveTableSize)))) { + return primitiveExternalCall; + } + functionPointer = externalPrimitiveTable[index - 1]; + if (!functionPointer) { + return primitiveExternalCall; + } + return functionPointer; + } + + /* begin functionPointerFor:inClass: */ + return ((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]); +} + + +/* currentBytecode will be private to the main dispatch loop in the generated + code. This method allows the currentBytecode to be retrieved from global + variables. Override to answer -1 if we're not in an interpreter frame. */ + + /* CoInterpreter>>#getCurrentBytecode */ +sqInt +getCurrentBytecode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)GIV(framePointer))) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)GIV(framePointer))) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)GIV(framePointer))) <= (((usqInt)GIV(pages)))))))) + && (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) + ? byteAt((void *)(GIV(instructionPointer))) + : -1); +} + + +/* Handle a send fault that may be due to a send to a forwarded object. + Unforward the receiver on the stack and answer it. */ + + /* CoInterpreter>>#handleForwardedSendFaultForReceiver:stackDelta: */ +static NoDbgRegParms sqInt +handleForwardedSendFaultForReceiverstackDelta(sqInt forwardedReceiver, sqInt stackDelta) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt rcvr; + sqInt rcvrStackIndex; + + + /* should *not* be a super send, so the receiver should be forwarded. */ + assert(isOopForwarded(forwardedReceiver)); + rcvrStackIndex = GIV(argumentCount) + stackDelta; + assert((stackValue(rcvrStackIndex)) == forwardedReceiver); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(forwardedReceiver)); + rcvr = longAt((void *)((forwardedReceiver + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord())))); + } + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (rcvrStackIndex * BytesPerWord),rcvr); + followForwardedFrameContentsstackPointer(GIV(framePointer), GIV(stackPointer) + ((rcvrStackIndex + 1) * BytesPerWord)); + if (isPointers(/* frameReceiver: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? longAt(GIV(framePointer) + FoxMFReceiver) + : longAt(GIV(framePointer) + FoxIFReceiver)))) { + followForwardedObjectFieldstoDepth(/* frameReceiver: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? longAt(GIV(framePointer) + FoxMFReceiver) + : longAt(GIV(framePointer) + FoxIFReceiver)), 0); + } + followForwardedFieldsInCurrentMethod(); + return rcvr; +} + + +/* A message send from either an open PIC or an unlinked send has not been + understood. Create a message and execute the relevant resulting MNU + method. messageSelector is an implicit argument (yuck). */ + + /* CoInterpreter>>#handleMNU:InMachineCodeTo:classForMessage: */ +static NoDbgRegParms sqInt +handleMNUInMachineCodeToclassForMessage(sqInt selectorIndex, sqInt rcvr, sqInt classForMessage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classForThisMessage; + CogMethod *cogMethod; + sqInt errSelIdx; + sqInt methodHeader; + sqInt top; + + assert(addressCouldBeOop(rcvr)); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + createActualMessageTo(classForMessage); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(selectorIndex) << (shiftForWord())))))); + if (lookupInMethodCacheSelclassTag(GIV(messageSelector), classTagForClass(GIV(lkupClass)))) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + errSelIdx = lookupMNUInClass((classForThisMessage = GIV(lkupClass))); + if (errSelIdx) { + if (selectorIndex == SelectorDoesNotUnderstand) { + error("Recursive not understood error encountered"); + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return handleMNUInMachineCodeToclassForMessage(errSelIdx, rcvr, classForThisMessage); + } + } + + /* check for coggability because method is in the cache */ + if (/* maybeMethodHasCogMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + executeCogMethodfromUnlinkedSendWithReceiver(cogMethodOf(GIV(newMethod)), rcvr); + + /* NOTREACHED */ + assert(0); + } + return interpretMethodFromMachineCode(); +} + + +/* */ + + /* CoInterpreter>>#iframeIsBlockActivation: */ +static NoDbgRegParms int +iframeIsBlockActivation(char *theFP) +{ + return (byteAt((theFP + FoxIFrameFlags) + 3)) != 0; +} + + /* CoInterpreter>>#iframeReceiver: */ +static NoDbgRegParms sqInt +iframeReceiver(char *theFP) +{ + return longAt(theFP + FoxIFReceiver); +} + + /* CoInterpreter>>#iframeSavedIP: */ +static NoDbgRegParms usqInt +iframeSavedIP(char *theFP) +{ + return ((usqInt)(longAt(theFP + FoxIFSavedIP))); +} + + +/* This is for low-level error reporting. If either of the C stack pointers + are pointing into the stack zone then write them back to framePointer + and/or stackPointer so that the stack backtrace will be up to date. Write + their original values through savedFPP & savedSPP if non-null. */ + + /* CoInterpreter>>#ifValidWriteBackStack:Pointers:Save:To: */ +void +ifValidWriteBackStackPointersSaveTo(void *theCFP, void *theCSP, char **savedFPP, char **savedSPP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (savedFPP) { + savedFPP[0] = GIV(framePointer); + } + if (savedSPP) { + savedSPP[0] = GIV(stackPointer); + } + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theCFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theCFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theCFP)) <= (((usqInt)GIV(pages)))))))) { + GIV(framePointer) = theCFP; + } + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theCSP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theCSP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theCSP)) <= (((usqInt)GIV(pages)))))))) { + GIV(stackPointer) = theCSP; + } +} + + /* CoInterpreter>>#instructionPointerAddress */ +usqInt +instructionPointerAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(instructionPointer)))); +} + + +/* Answer the bytecode pc object (i.e. SmallInteger) for an active frame. The + bytecode pc is derived from the frame's pc. If the frame is the top frame + on the current stack + the frame pc is whatever the current instruction pointer is. If the frame + is the top + frame on some other stack the frame pc is the value on top of stack. + Otherwise the + frame pc is the saved pc of the frame above. Once the frame pc is found it + must be + mapped to a bytecode pc. */ + + /* CoInterpreter>>#instructionPointerForFrame:currentFP:currentIP: */ +static NoDbgRegParms sqInt +instructionPointerForFramecurrentFPcurrentIP(char *spouseFP, char *currentFP, sqInt instrPtr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + char *fp; + char *theFPAbove; + sqInt theIP; + StackPage *thePage; + sqInt value; + + if (spouseFP == currentFP) { + theIP = instrPtr; + } + else { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(spouseFP), GIV(pages)); + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == spouseFP) { + theFPAbove = 0; + goto l1; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == spouseFP) { + theFPAbove = fp; + goto l1; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + theFPAbove = 0; + /* end findFrameAbove:inPage: */ +l1: + theIP = (theFPAbove + ? longAt(theFPAbove + FoxCallerSavedIP) + : longAt((thePage->headSP))); + } + value = contextInstructionPointerframe(theIP, spouseFP); + return ((((int) value)) < 0 + ? mustMapMachineCodePCcontext((value >> 3), longAt(spouseFP + FoxThisContext)) + : value); +} + + +/* At a rough approximation we may need to allocate up to a couple + of page's worth of contexts when switching stack pages, assigning + to senders, etc. But the snapshot primitive voids all stack pages. + So a safe margin is the size of a large context times the maximum + number of frames per page times the number of pages. */ + + /* CoInterpreter>>#interpreterAllocationReserveBytes */ +static NeverInline sqInt +interpreterAllocationReserveBytes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt maxFramesPerPage; + sqInt maxUsedBytesPerPage; + + maxUsedBytesPerPage = (stackPageFrameBytes()) + (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord); + maxFramesPerPage = (maxUsedBytesPerPage / BytesPerWord) / MFrameSlots; + return ((maxFramesPerPage * LargeContextSlots) * BytesPerOop) * GIV(numStackPages); +} + + +/* Execute a method interpretively from machine code. We assume (require) + that newMethod + messageSelector, primitiveFunctionPointer and argumentCount have been set + in the caller. + Once evaluated either continue in the interpreter via a jongjmp or in + machine code via an + enilopmart (a form of longjmp - a stinking rose by any other name). */ + + /* CoInterpreter>>#interpretMethodFromMachineCode */ +static sqInt +interpretMethodFromMachineCode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt nArgs; + char *savedFramePointer; + char *savedStackPointer; + + assertCStackWellAligned(); + assert(validInstructionPointerinFrame(GIV(instructionPointer), GIV(framePointer))); + if (primitiveFunctionPointer) { + if (primitiveFunctionPointer == primitiveInvokeObjectAsMethod) { + assert(!(isOopCompiledMethod(GIV(newMethod)))); + } + else { + assert((isOopCompiledMethod(GIV(newMethod))) + && ((primitiveIndexOf(GIV(newMethod))) != 0)); + } + + /* Invoke an interpreter primitive (because the method is to be interpreted or has not yet been + compiled). This is very similar to invoking an interpreter primitive from a compiled primitive + (see e.g. SimpleStackBasedCogit>>compileInterpreterPrimitive:). Cut back the stack pointer + (done above) to skip the return address and invoke the function. On return if it has succeeded + simply continue otherwise restore the stackPointer, collect the pc and interpret. Note that + frame building primitives such as primitiveClosureValue, primitiveEvaluateMethod et al will not + return but will instead jump into either machine code or longjmp back to the interpreter. + Assign stackPage headFP so we can tell if the primitive built a frame. We can't simply save + the framePointer since e.g. assignment to contexts (via primitiveInstVarAt:put:) can change the + framePointer. But context assignments will change both the framePointer and stackPage headFP. */ + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + if ((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex) { + externalQuickPrimitiveResponse(); + GIV(primFailCode) = 0; + } + else { + /* begin slowPrimitiveResponse */ + assert(!(isOopForwarded(stackValue(GIV(argumentCount))))); + assert((GIV(remapBufferCount)) == 0); + nArgs = GIV(argumentCount); + savedStackPointer = GIV(stackPointer); + savedFramePointer = GIV(framePointer); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if (/* recordPrimTraceForMethod: */ + (recordPrimTrace()) + && ((primTracePluginName + ? methodHasPrimitiveInPrimTracePlugin(GIV(newMethod)) + : 1))) { + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = GIV(newMethod); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + dispatchFunctionPointer(primitiveFunctionPointer); + assert(maybeLeakCheckExternalPrimCall(GIV(newMethod))); + + /* begin maybeRetryPrimitiveOnFailure */ + if (GIV(primFailCode)) { + retryPrimitiveOnFailure(); + } + if ((!GIV(primFailCode)) + && ((GIV(framePointer) == savedFramePointer) + && (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))))) { + if (GIV(stackPointer) != (savedStackPointer + (nArgs * BytesPerWord))) { + /* This is necessary but insufficient; the result may still have been written to the stack. + At least we'll know something is wrong. */ + failUnbalancedPrimitive(); + GIV(stackPointer) = savedStackPointer; + } + } + + /* Don't fail if primitive has done something radical, e.g. perform: + If we are profiling, take accurate primitive measures */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + + /* successful */ + !GIV(primFailCode); + } + if (!GIV(primFailCode)) { + returntoExecutive(popStack(), 0); + } + } + else { + assert((isOopCompiledMethod(GIV(newMethod))) + && (((primitiveIndexOf(GIV(newMethod))) == 0) + || (((functionPointerForinClass(primitiveIndexOf(GIV(newMethod)), nilObject())) == 0) + || (isNullExternalPrimitiveCall(GIV(newMethod)))))); + } + + /* if not primitive, or primitive failed, activate the method and reenter the interpreter */ + activateNewMethod(); + ceInvokeInterpret(); + + /* NOTREACHED */ + return null; +} + + +/* This version exists for asserts */ + + /* CoInterpreter>>#isCogMethodReferenceNoAssert: */ +static NoDbgRegParms int +isCogMethodReferenceNoAssert(sqInt methodHeader) +{ + return (!(methodHeader & (smallIntegerTag()))); +} + + /* CoInterpreter>>#isCogMethodReference: */ +int +isCogMethodReference(sqInt methodHeader) +{ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + return (!(methodHeader & (smallIntegerTag()))); +} + + /* CoInterpreter>>#isExternalMethodInPlugin: */ +static NoDbgRegParms sqInt +isExternalMethodInPlugin(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt headerSqInt; + sqInt literal; + usqInt numSlots; + sqInt primIdx; + + literal = 0; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + headerSqInt = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* primitiveIndexOfMethod:header: */ + if (((header & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((header >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIdx = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIdx = 0; + } + return (primIdx == PrimNumberExternalCall) + && ((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) > 0) + && ((isArray((literal = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord()))))))) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(literal)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(literal + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(literal - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4) + && ((isBytes(longAt((void *)((literal + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralModuleNameIndex) << (shiftForWord())))))))) + && (!((numBytesOf(longAt((void *)((literal + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralModuleNameIndex) << (shiftForWord())))))))) == 0)))))); +} + + +/* Answer if the argument is a full block method or not. Full block methods + (as introduced by the SistaV1BytecodeSet) are used to implement block + closures and have a reference to their enclosing block or method as their + last literal. Compiled methods have a class association as their last + literal. */ + + /* CoInterpreter>>#isFullBlockMethod: */ +sqInt +isFullBlockMethod(sqInt aMethodObj) +{ + sqInt oop; + + oop = ultimateLiteralOf(aMethodObj); + + /* begin isOopCompiledMethod: */ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())); +} + + /* CoInterpreter>>#isMachineCodeFrameForCogMethodFlaggedForBecome: */ +static NoDbgRegParms sqInt +isMachineCodeFrameForCogMethodFlaggedForBecome(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && (isCMMethodFlaggedForBecome(((CogBlockMethod *) (mframeHomeMethod(theFP))))); +} + + /* CoInterpreter>>#isMachineCodeFrameForCogMethodWithMachineCodePrim: */ +static NoDbgRegParms sqInt +isMachineCodeFrameForCogMethodWithMachineCodePrim(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && (cogMethodHasMachineCodePrim(mframeHomeMethod(theFP))); +} + + /* CoInterpreter>>#isMachineCodeFrameForCogMethodWithTooManyLiterals: */ +static NoDbgRegParms sqInt +isMachineCodeFrameForCogMethodWithTooManyLiterals(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && (!(((/* begin literalCountOfMethodHeader: */ + assert(((((((mframeHomeMethod(theFP))->methodHeader))) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + (((((mframeHomeMethod(theFP))->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile)); +} + + /* CoInterpreter>>#isMachineCodeFrameForExternalPrimitiveMethod: */ +static NoDbgRegParms sqInt +isMachineCodeFrameForExternalPrimitiveMethod(char *theFP) +{ + CogMethod *homeMethod; + + homeMethod = ((CogMethod *) 0); + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && ((cogMethodHasMachineCodePrim((homeMethod = mframeHomeMethod(theFP)))) + && (isExternalMethodInPlugin((homeMethod->methodObject)))); +} + + /* CoInterpreter>>#isMachineCodeFrameForRealNonProfilingPrimitive: */ +static NoDbgRegParms sqInt +isMachineCodeFrameForRealNonProfilingPrimitive(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt firstLiteral; + sqInt functionName; + sqInt header; + CogMethod *homeMethod; + sqInt methodField; + sqInt methodHeader; + sqInt methodObj; + usqInt numSlots; + void (*pf)(void); + sqInt primitiveIndex; + + if (!((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()))) { + return 0; + } + + /* begin mframeHomeMethod: */ + methodField = longAt(theFP + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + homeMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + goto l1; + } + homeMethod = ((CogMethod *) (methodField & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + methodObj = (homeMethod->methodObject); + + /* begin isRealNonProfilingPrimitiveMethod: */ + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin isRealNonProfilingPrimitiveMethod:primIndex: */ + if (!(((primitiveIndex >= 1) && (primitiveIndex <= MaxPrimitiveIndex)))) { + return 0; + } + + /* Filter-out the profiling primitives themselves. */ + if (primitiveIndex == PrimNumberExternalCall) { + firstLiteral = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(firstLiteral & (tagMask())))) + && (((byteAt((void *)(firstLiteral + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(firstLiteral)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(firstLiteral + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(firstLiteral - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4))) { + return 1; + } + functionName = longAt((void *)((firstLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFunctionNameIndex) << (shiftForWord())))))); + return ((numBytesOf(functionName)) < (21)) + || (0 != (strncmp("primitiveProfile", ((char *) (firstIndexableField(functionName))), 16))); + } + + /* Answer true if there's a real primitive here, excepting the ValueNoContextSwitch primitives, + because a sample can't be taken in them, as doing so could cause a context switch. */ + pf = primitiveTable[primitiveIndex]; + return (pf != (((void *)0))) + && ((pf != (((void (*)(void)) primitiveFail))) + && ((pf != primitiveClosureValueNoContextSwitch) + && (pf != primitiveFullClosureValueNoContextSwitch))); +} + + /* CoInterpreter>>#isMachineCodeFrame: */ +static NoDbgRegParms int +isMachineCodeFrame(char *theFP) +{ + return (((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()); +} + + +/* Answer if the methodObj's primitive is one that should be profiled. + Filter-out the profiling primitives themselves. */ + + /* CoInterpreter>>#isRealNonProfilingPrimitiveMethod: */ +static NoDbgRegParms sqInt +isRealNonProfilingPrimitiveMethod(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt firstLiteral; + sqInt functionName; + sqInt header; + sqInt methodHeader; + usqInt numSlots; + void (*pf)(void); + sqInt primitiveIndex; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin isRealNonProfilingPrimitiveMethod:primIndex: */ + if (!(((primitiveIndex >= 1) && (primitiveIndex <= MaxPrimitiveIndex)))) { + return 0; + } + + /* Filter-out the profiling primitives themselves. */ + if (primitiveIndex == PrimNumberExternalCall) { + firstLiteral = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(firstLiteral & (tagMask())))) + && (((byteAt((void *)(firstLiteral + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(firstLiteral)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(firstLiteral + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(firstLiteral - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4))) { + return 1; + } + functionName = longAt((void *)((firstLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFunctionNameIndex) << (shiftForWord())))))); + return ((numBytesOf(functionName)) < (21)) + || (0 != (strncmp("primitiveProfile", ((char *) (firstIndexableField(functionName))), 16))); + } + + /* Answer true if there's a real primitive here, excepting the ValueNoContextSwitch primitives, + because a sample can't be taken in them, as doing so could cause a context switch. */ + pf = primitiveTable[primitiveIndex]; + return (pf != (((void *)0))) + && ((pf != (((void (*)(void)) primitiveFail))) + && ((pf != primitiveClosureValueNoContextSwitch) + && (pf != primitiveFullClosureValueNoContextSwitch))); +} + + +/* Answer if the methodObj's primitive is one that should be profiled. + In particular answer false for the no-context-switch value primitives and + the profiling primitives themselves. */ + + /* CoInterpreter>>#isRealNonProfilingPrimitiveMethod:primIndex: */ +static NoDbgRegParms sqInt +isRealNonProfilingPrimitiveMethodprimIndex(sqInt methodObj, sqInt primitiveIndex) +{ + sqInt firstLiteral; + sqInt functionName; + usqInt numSlots; + void (*pf)(void); + + if (!(((primitiveIndex >= 1) && (primitiveIndex <= MaxPrimitiveIndex)))) { + return 0; + } + + /* Filter-out the profiling primitives themselves. */ + if (primitiveIndex == PrimNumberExternalCall) { + firstLiteral = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(firstLiteral & (tagMask())))) + && (((byteAt((void *)(firstLiteral + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(firstLiteral)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(firstLiteral + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(firstLiteral - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4))) { + return 1; + } + functionName = longAt((void *)((firstLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFunctionNameIndex) << (shiftForWord())))))); + return ((numBytesOf(functionName)) < (21)) + || (0 != (strncmp("primitiveProfile", ((char *) (firstIndexableField(functionName))), 16))); + } + + /* Answer true if there's a real primitive here, excepting the ValueNoContextSwitch primitives, + because a sample can't be taken in them, as doing so could cause a context switch. */ + pf = primitiveTable[primitiveIndex]; + return (pf != (((void *)0))) + && ((pf != (((void (*)(void)) primitiveFail))) + && ((pf != primitiveClosureValueNoContextSwitch) + && (pf != primitiveFullClosureValueNoContextSwitch))); +} + + +/* See if the argument is married to a live frame or not. i.e. see if there + is a matching + frame whose frameContext field is aOnceMarriedContext, or a forwarder to + it. If aOnceMarriedContext is not married to a live frame, turn it into a + bereaved single context. + This version is for use during scavenging when stack references may be + forwarded. Following what appear to be references to forwarded objects on + the stack is dangerous; + an instruction ponter may be correctly aligned and may point to bytes that + just happen + to look like a forwarder. So it is only safe to follow fields that we know + are frameContext + fields; hence the stack page is walked to check that aOnceMarriedContext + is pointing to + a live frame. This only has to happen during scavenging because after a + become: all + frameContext fields have been followed and so there is no need to follow + forwarders. */ +/* Override to check gcMode is correct. See comment in superclass, and in + superclass's isWidowedContext:. + */ + + /* CoInterpreter>>#isWidowedContextDuringGC: */ +static NoDbgRegParms sqInt +isWidowedContextDuringGC(sqInt aOnceMarriedContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anInteger; + char *maybeFrame; + sqInt maybeFrameCtxt; + sqInt referent; + sqInt senderOop; + char *shouldBeFrameCallerField; + StackPage *thePage; + + assert((GIV(gcMode) == GCModeNewSpace) + || ((GIV(gcMode) == GCModeFull) + || (GIV(gcMode) == GCModeImageSegment))); + assert((isContext(aOnceMarriedContext)) + && (isMarriedOrWidowedContext(aOnceMarriedContext))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + maybeFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(maybeFrame), GIV(pages)); + if (!((isFree(thePage)) + || (maybeFrame < ((thePage->headFP))))) { + /* The frame pointer is within the bounds of a live page. + Now check if it matches a frame. */ + anInteger = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + + /* begin withoutSmallIntegerTags: */ + assert((((anInteger) & 7) == 1)); + shouldBeFrameCallerField = ((char *)(anInteger - (smallIntegerTag()))); + if (((((char *)(longAt(maybeFrame + FoxSavedFP)))) == shouldBeFrameCallerField) + && (/* frameHasContext: */ + ((((usqInt)(longAt(maybeFrame + FoxMethod)))) < (startOfMemory()) + ? ((longAt(maybeFrame + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((maybeFrame + FoxIFrameFlags) + 2)) != 0))) { + maybeFrameCtxt = longAt(maybeFrame + FoxThisContext); + + /* On Spur we need to follow the context to check for a match, but since the VM is + only speculating about maybeFrame being a frame, and only speculating about + maybeContext being a context, we need to be sure before we can safely follow. */ + if ((isFrameonPage(maybeFrame, thePage)) + && ((!((longAt((void *)(maybeFrameCtxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(maybeFrameCtxt)); + referent = longAt((void *)((maybeFrameCtxt + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + maybeFrameCtxt = referent; + + /* begin setFrameContext:to: */ + longAtput(maybeFrame + FoxThisContext,maybeFrameCtxt); + } + if (maybeFrameCtxt == aOnceMarriedContext) { + return 0; + } + } + } + + /* It is out of range or doesn't match the frame's context. + It is widowed. Time to wear black. */ + + /* begin markContextAsDead: */ + assert(isContext(aOnceMarriedContext)); + assert((isNonImmediate(aOnceMarriedContext)) + && (!(isForwarded(aOnceMarriedContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, aOnceMarriedContext, GIV(nilObj))); + longAtput((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aOnceMarriedContext)) + && (!(isForwarded(aOnceMarriedContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, aOnceMarriedContext, GIV(nilObj))); + longAtput((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + return 1; +} + + +/* Answer if the argument is married to a live frame or not. + If it is not, turn it into a bereaved single context. This version is safe + for use + only when no frameContext fields may be forwarded (as maybe the case + when scavenging). Post become: all frameContext fields are followed, and + hence normally no following of frameContext fields is necessary. But + during a scavenge one must use isWidowedContextDuringGC:. */ +/* Override to check gcMode is correct. See comment in superclass. */ + + /* CoInterpreter>>#isWidowedContext: */ +static NoDbgRegParms sqInt +isWidowedContext(sqInt aOnceMarriedContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anInteger; + sqInt senderOop; + char *shouldBeFrameCallerField; + char *theFrame; + StackPage *thePage; + sqInt widowed; + + assert(GIV(gcMode) != GCModeNewSpace); + + /* begin isWidowedContextNoConvert: */ + assert((isContext(aOnceMarriedContext)) + && (isMarriedOrWidowedContext(aOnceMarriedContext))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + if (!((isFree(thePage)) + || (theFrame < ((thePage->headFP))))) { + /* The frame pointer is within the bounds of a live page. + Now check if it matches a frame. */ + anInteger = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + + /* begin withoutSmallIntegerTags: */ + assert((((anInteger) & 7) == 1)); + shouldBeFrameCallerField = ((char *)(anInteger - (smallIntegerTag()))); + if (((((char *)(longAt(theFrame + FoxSavedFP)))) == shouldBeFrameCallerField) + && (/* frameHasContext: */ + ((((usqInt)(longAt(theFrame + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFrame + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFrame + FoxIFrameFlags) + 2)) != 0))) { + assert(!(((isFrameonPage(theFrame, thePage)) + && (isForwarded(frameContext(theFrame)))))); + if ((longAt(theFrame + FoxThisContext)) == aOnceMarriedContext) { + widowed = 0; + goto l1; + } + } + } + widowed = 1; + /* end isWidowedContextNoConvert: */ +l1: + if (!widowed) { + return 0; + } + + /* begin markContextAsDead: */ + assert(isContext(aOnceMarriedContext)); + assert((isNonImmediate(aOnceMarriedContext)) + && (!(isForwarded(aOnceMarriedContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, aOnceMarriedContext, GIV(nilObj))); + longAtput((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aOnceMarriedContext)) + && (!(isForwarded(aOnceMarriedContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, aOnceMarriedContext, GIV(nilObj))); + longAtput((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + return 1; +} + + +/* If the method is assigned, any machine code pc must be mapped to a + bytecode one + before the method is changed. */ + + /* CoInterpreter>>#is:methodAssignmentToContextWithMachineCodePC: */ +static NoDbgRegParms sqInt +ismethodAssignmentToContextWithMachineCodePC(sqInt fieldIndex, sqInt anOop) +{ + sqInt thePC; + + thePC = 0; + return (fieldIndex == MethodIndex) + && (((((int) ((thePC = longAt((void *)((anOop + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))))) < 0) + && ((((thePC) & 7) == 1))); +} + + /* CoInterpreter>>#longRunningPrimitiveStopUsecsAddress */ +#if LRPCheck +usqInt +longRunningPrimitiveStopUsecsAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(longRunningPrimitiveStopUsecs)))); +} +#endif /* LRPCheck */ + + +/* Lookup selector in rcvr, without doing MNU processing, and answer either a + method or an error code if the message was not understood. Used to + populate closed PICs. */ +/* self printFrame: stackPage headFP WithSP: stackPage headSP */ +/* self printStringOf: selector */ + + /* CoInterpreter>>#lookupMNU:receiver: */ +sqInt +lookupMNUreceiver(sqInt selector, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTag; + sqInt erridx; + sqInt inCache; + sqInt tagBits; + + classTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + inCache = lookupInMethodCacheSelclassTag(selector, classTag); + if (!inCache) { + GIV(messageSelector) = selector; + erridx = lookupMNUInClass(classForClassTag(classTag)); + if (erridx) { + assert(erridx <= (maxLookupNoMNUErrorCode())); + return erridx; + } + } + return GIV(newMethod); +} + + +/* Lookup selector in rcvr, without doing MNU processing, and answer either a + method or an error code if the message was not understood. Used to + populate closed PICs. */ +/* self printFrame: stackPage headFP WithSP: stackPage headSP */ +/* self printStringOf: selector */ + + /* CoInterpreter>>#lookupOrdinary:receiver: */ +sqInt +lookupOrdinaryreceiver(sqInt selector, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTag; + sqInt erridx; + sqInt tagBits; + + classTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + if (!(lookupInMethodCacheSelclassTag(selector, classTag))) { + GIV(messageSelector) = selector; + if ((erridx = lookupOrdinaryNoMNUEtcInClass(classForClassTag(classTag)))) { + assert(erridx <= (maxLookupNoMNUErrorCode())); + return erridx; + } + } + return GIV(newMethod); +} + + +/* */ +/* Marry aContext with the base frame of a new stack page. Build the base + frame to reflect the context's state. Answer the new page. Override to + hold the caller context in a different place, In the StackInterpreter we + use the caller saved ip, but in the Cog VM caller saved ip is the + ceBaseReturn: trampoline. Simply hold the caller context in the first word + of the stack. */ + + /* CoInterpreter>>#makeBaseFrameFor: */ +static NoDbgRegParms StackPage * +makeBaseFrameFor(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cogMethod; + CogMethod *cogMethod1; + sqInt header; + sqInt headerSqInt; + sqInt i; + sqInt maybeClosure; + usqInt numArgs; + usqInt numSlots; + sqInt oop; + StackPage *page; + char *pointer; + sqInt rawHeader; + sqInt rcvr; + sqInt stackPtrIndex; + sqInt theIP; + sqInt theMethod; + sqInt valuePointer; + sqInt yetToCompact; + + + /* theIP must be typed as signed because it is assigned ceCannotResumePC and so maybe implicitly typed as unsigned. */ + assert(isContext(aContext)); + assert(isSingleContext(aContext)); + assert(goodContextSize(aContext)); + theIP = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + theIP = ((((theIP) & 7) == 1) + ? (theIP >> 3) + : HasBeenReturnedFromMCPC); + + /* begin followObjField:ofObject: */ + theMethod = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + assert(isNonImmediate(theMethod)); + if ((!((longAt((void *)(theMethod))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + theMethod = fixFollowedFieldofObjectwithInitialValue(MethodIndex, aContext, theMethod); + } + + /* begin newStackPage */ + page = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((page->baseFP))) { + goto l1; + } + divorceFramesIn(page); + /* end newStackPage */ +l1: + + /* first word on stack is caller context of base frame */ + longAtput((pointer = (page->baseAddress)),followObjFieldofObject(SenderIndex, aContext)); + + /* second word is the context itself; needed for cannotReturn processing; see ceBaseReturn:. */ + longAtput((pointer -= BytesPerWord),aContext); + + /* begin followField:ofObject: */ + rcvr = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = fixFollowedFieldofObjectwithInitialValue(ReceiverIndex, aContext, rcvr); + } + + /* If the frame is a closure activation then the closure should be on the stack in + the pushed receiver position (closures receive the value[:value:] messages). + Otherwise it should be the receiver proper. */ + maybeClosure = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord())))))); + if (maybeClosure != GIV(nilObj)) { + if ((!((longAt((void *)(maybeClosure))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + maybeClosure = fixFollowedFieldofObjectwithInitialValue(ClosureIndex, aContext, maybeClosure); + } + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + longAtput((pointer -= BytesPerWord),maybeClosure); + } + else { + /* begin methodHeaderOf: */ + assert(isCompiledMethod(theMethod)); + headerSqInt = longAt((void *)((theMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + numArgs = (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; + + /* If this is a synthetic context its IP could be pointing at the CallPrimitive opcode. If so, skip it. */ + if ((((header & AlternateHeaderHasPrimFlag) != 0)) + && (theIP == (1 + ((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop)))) { + theIP += 3 /* sizeOfCallPrimitiveBytecode: */; + } + longAtput((pointer -= BytesPerWord),rcvr); + } + + /* Put the arguments on the stack */ + for (i = 1; i <= numArgs; i += 1) { + longAtput((pointer -= BytesPerWord),longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))))); + } + + /* saved caller ip is base return trampoline */ + longAtput((pointer -= BytesPerWord),ceBaseFrameReturnPC()); + + /* base frame's saved fp is null */ + longAtput((pointer -= BytesPerWord),0); + + /* N.B. Don't set the baseFP, which marks the page as in use, until after + ensureMethodIsCogged: and/or instructionPointer:forContext:frame:. These + can cause a compiled code compaction which, if marked as in use, will + examine this partially initialized page and crash. */ + (page->headFP = pointer); + + /* Create either a machine code frame or an interpreter frame based on the pc. If the pc is -ve + it is a machine code pc and so we produce a machine code frame. If +ve an interpreter frame. + N.B. Do *not* change this to try and map from a bytecode pc to a machine code frame under + any circumstances. See ensureContextIsExecutionSafeAfterAssignToStackPointer: */ + if (theIP < 0) { + /* Since we would have to generate a machine-code method to be able to map + the native pc anyway we should create a native method and native frame. */ + + /* begin ensureMethodIsCogged:maybeClosure: */ + rawHeader = longAt((void *)((theMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) rawHeader); + goto l2; + } + yetToCompact = 1; + while (1) { + if ((maybeClosure != GIV(nilObj)) + && (!((/* begin isVanillaBlockClosure: */ + assert((isPointers(maybeClosure)) + && ((numSlotsOf(maybeClosure)) >= ClosureFirstCopiedValueIndex)), + ((((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1))))) { + cogMethod1 = cogFullBlockMethodnumCopied( + theMethod, + (/* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(maybeClosure)))), + ((/* begin numSlotsOf: */ + assert((classIndexOf(maybeClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(maybeClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(maybeClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex)); + } + else { + cogMethod1 = cogselector(theMethod, GIV(nilObj)); + } + if (!((cogMethod1 == null) + && (GIV(cogCompiledCodeCompactionCalledFor) + && (yetToCompact)))) break; + yetToCompact = 0; + commenceCogCompiledCodeCompaction(); + } + if (!(asserta(cogMethod1 != null))) { + error("could not compile method that should have been compiled"); + } + cogMethod = cogMethod1; + /* end ensureMethodIsCogged:maybeClosure: */ +l2: + theMethod = ((sqInt)cogMethod); + if (maybeClosure != GIV(nilObj)) { + /* begin isVanillaBlockClosure: */ + assert((isPointers(maybeClosure)) + && ((numSlotsOf(maybeClosure)) >= ClosureFirstCopiedValueIndex)); + if (((((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* If the pc is the special HasBeenReturnedFromMCPC pc set the pc + appropriately so that the frame stays in the cannotReturn: state. */ + if (theIP == HasBeenReturnedFromMCPC) { + theMethod = ((sqInt)(findMethodForStartBcpcinHomeMethod((((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))))) >> 3)) - 1, ((CogMethod *) theMethod)))); + if (!theMethod) { + error("cannot find machine code block matching closure's startpc"); + } + theIP = ceCannotResumePC(); + } + else { + assert((((theIP) >> 16)) < -1); + theMethod -= (((theIP) >> 16)) * (blockAlignment()); + theIP = theMethod - (((short)theIP)); + } + } + else { + assert((((theIP) >> 16)) >= -1); + + /* If the pc is the special HasBeenReturnedFromMCPC pc set the pc + appropriately so that the frame stays in the cannotReturn: state. */ + theIP = (theIP == HasBeenReturnedFromMCPC + ? ceCannotResumePC() + : (((sqInt)theMethod)) - theIP); + } + longAtput((pointer -= BytesPerWord),(theMethod + MFMethodFlagHasContextFlag) + MFMethodFlagIsBlockFlag); + } + else { + assert((((theIP) >> 16)) >= -1); + + /* If the pc is the special HasBeenReturnedFromMCPC pc set the pc + appropriately so that the frame stays in the cannotReturn: state. */ + theIP = (theIP == HasBeenReturnedFromMCPC + ? ceCannotResumePC() + : (((sqInt)theMethod)) - theIP); + longAtput((pointer -= BytesPerWord),theMethod + MFMethodFlagHasContextFlag); + } + longAtput((pointer -= BytesPerWord),aContext); + } + else { + longAtput((pointer -= BytesPerWord),theMethod); + longAtput((pointer -= BytesPerWord),aContext); + longAtput((pointer -= BytesPerWord),/* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8)))) + (1ULL << ((BytesPerWord * 8) - 16))) + ((maybeClosure != GIV(nilObj) + ? 1ULL << ((BytesPerWord * 8) - 24) + : 0)) + : ((1 + ((numArgs << 8))) + (0x10000)) + ((maybeClosure != GIV(nilObj) + ? 0x1000000 + : 0)))); + longAtput((pointer -= BytesPerWord),0); + + /* begin iframeInstructionPointerForIndex:method: */ + assert(((theIP >= (((LiteralStart + (literalCountOf(theMethod))) * BytesPerOop) + 1)) && (theIP <= (lengthOf(theMethod))))); + theIP = ((theMethod + theIP) + BaseHeaderSize) - 2; + } + (page->baseFP = (page->headFP)); + assert(frameHasContext((page->baseFP))); + assert((frameNumArgs((page->baseFP))) == numArgs); + longAtput((pointer -= BytesPerWord),rcvr); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + stackPtrIndex = (oop >> 3); + assert((ReceiverIndex + stackPtrIndex) < (lengthOf(aContext))); + for (i = (numArgs + 1); i <= stackPtrIndex; i += 1) { + longAtput((pointer -= BytesPerWord),longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))))); + + /* nil the slot in the context so that it doesn't inadvertently hang onto some collectable object. + Thanks to Ryan Macnak for identifying this bug */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aContext)) + && (!(isForwarded(aContext)))); + assert(validStorePointerUncheckedArgs(ReceiverIndex + i, aContext, GIV(nilObj))); + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))),GIV(nilObj)); + } + + /* top of stack is the instruction pointer */ + longAtput((pointer -= BytesPerWord),theIP); + (page->headSP = pointer); + assert(contexthasValidInversePCMappingOfin(aContext, theIP, (page->baseFP))); + + /* Mark context as married by setting its sender to the frame pointer plus SmallInteger + tags and the InstructionPointer to the saved fp (which ensures correct alignment + w.r.t. the frame when we check for validity) plus SmallInteger tags. */ + /* begin withSmallIntegerTags: */ + assert(((oopForPointer((page->baseFP))) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer((page->baseFP))) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aContext)) + && (!(isForwarded(aContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, aContext, valuePointer)); + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),valuePointer); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(0)) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(0)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aContext)) + && (!(isForwarded(aContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, aContext, valuePointer)); + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointer); + assert(((((fetchPointerofObject(SenderIndex, aContext))) & 7) == 1)); + assert((frameOfMarriedContext(aContext)) == ((page->baseFP))); + assert(validStackPageBaseFrame(page)); + return page; +} + + /* CoInterpreter>>#mapStackPages */ +static NeverInline void +mapStackPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + char *frameRcvrOffset; + sqInt i; + sqInt numLivePages; + sqInt oop; + char *theFP; + sqInt theIP; + char *theIPPtr; + StackPage *thePage; + char *theSP; + + callerFP = ((char *) 0); + theIP = 0; + numLivePages = 0; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((thePage->baseFP)) { + assert(ifCurrentStackPageHasValidHeadPointers(thePage)); + numLivePages += 1; + theSP = (thePage->headSP); + theFP = (thePage->headFP); + + /* Skip the instruction pointer on top of stack of inactive pages. */ + if (thePage == GIV(stackPage)) { + theIPPtr = (((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + || ((((usqInt)(longAt(theFP + FoxIFSavedIP)))) == 0) + ? 0 + : theFP + FoxIFSavedIP); + } + else { + theIPPtr = theSP; + theSP += BytesPerWord; + } + while (1) { + assert(addressIsInPage(thePage, theFP)); + assert(addressIsInPage(thePage, theSP)); + assert((theIPPtr == 0) + || (addressIsInPage(thePage, theIPPtr))); + frameRcvrOffset = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + while (theSP <= frameRcvrOffset) { + oop = longAt(theSP); + if (/* shouldRemapOop: */ + ((!(oop & (tagMask())))) + && (shouldRemapObj(oop))) { + longAtput(theSP,remapObj(oop)); + } + theSP += BytesPerWord; + } + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + if (shouldRemapObj(longAt(theFP + FoxThisContext))) { + longAtput(theFP + FoxThisContext,remapObj(longAt(theFP + FoxThisContext))); + } + + /* With SqueakV3 objectMemory or SpurPlanningCompactor can't assert since object body is yet to move. */ + if (!((GIV(gcPhaseInProgress) == SlidingCompactionInProgress) + || (GIV(gcPhaseInProgress) == ScavengeInProgress))) { + assert((isMarriedOrWidowedContext(frameContext(theFP))) + && ((frameOfMarriedContext(frameContext(theFP))) == theFP)); + } + } + if (!((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()))) { + if (shouldRemapObj(longAt(theFP + FoxMethod))) { + if (theIPPtr) { + theIP = longAt(theIPPtr); + if (theIP == (ceReturnToInterpreterPC())) { + assert((iframeSavedIP(theFP)) > (iframeMethod(theFP))); + theIPPtr = theFP + FoxIFSavedIP; + theIP = longAt(theIPPtr); + } + else { + assert(theIP > (iframeMethod(theFP))); + } + theIP -= longAt(theFP + FoxMethod); + } + longAtput(theFP + FoxMethod,remapObj(longAt(theFP + FoxMethod))); + if (theIPPtr) { + longAtput(theIPPtr,theIP + (longAt(theFP + FoxMethod))); + } + } + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theSP = ((theIPPtr = theFP + FoxCallerSavedIP)) + BytesPerWord; + theFP = callerFP; + } + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if (/* shouldRemapOop: */ + ((!(oop & (tagMask())))) + && (shouldRemapObj(oop))) { + longAtput(theSP,remapObj(oop)); + } + theSP += BytesPerWord; + } + } + } + + /* begin recordLivePagesOnMapping: */ + GIV(statNumMaps) += 1; + GIV(statPageCountWhenMappingSum) += numLivePages; + GIV(statMaxPageCountWhenMapping) = ((GIV(statMaxPageCountWhenMapping) < numLivePages) ? numLivePages : GIV(statMaxPageCountWhenMapping)); +} + + +/* If instructionPointer is referring to machine code, as it will be if a + primitive is in progress + (see isCodeCompactingPrimitiveIndex:) it may refer to a method, and if so + that method + must be retained. */ + + /* CoInterpreter>>#markActiveMethodsAndReferents */ +void +markActiveMethodsAndReferents(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + CogMethod *primCogMethod; + StackPage *thePage; + + if (GIV(instructionPointer)) { + if ((primCogMethod = cogMethodContaining(GIV(instructionPointer)))) { + markMethodAndReferents(((CogBlockMethod *) primCogMethod)); + } + } + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + markCogMethodsAndReferentsOnPage(thePage); + } + } +} + + /* CoInterpreter>>#markAndTraceStackPage: */ +static NoDbgRegParms void +markAndTraceStackPage(StackPage *thePage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogBlockMethod *aCogMethod; + char *callerFP; + char *frameRcvrOffset; + CogMethod *homeMethod; + sqInt oop; + sqInt referent; + char *theFP; + char *theSP; + + callerFP = ((char *) 0); + assert(!(isFree(thePage))); + assert(ifCurrentStackPageHasValidHeadPointers(thePage)); + assert(((thePage->trace)) != StackPageTraced); + (thePage->trace = StackPageTraced); + theSP = (thePage->headSP); + theFP = (thePage->headFP); + + /* Skip the instruction pointer on top of stack of inactive pages. */ + if (!(thePage == GIV(stackPage))) { + theSP += BytesPerWord; + } + while (1) { + frameRcvrOffset = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + while (theSP <= frameRcvrOffset) { + oop = longAt(theSP); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + longAtput(theSP,oop); + } + if (!(((oop & (tagMask())) != 0))) { + markAndTrace(oop); + } + theSP += BytesPerWord; + } + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + markAndTrace(frameContext(theFP)); + } + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* begin mframeCogMethod: */ + aCogMethod = ((CogBlockMethod *) ((longAt(theFP + FoxMethod)) & MFMethodMask)); + homeMethod = /* asCogHomeMethod: */ + (((aCogMethod->cmType)) >= CMMethod + ? ((CogMethod *) aCogMethod) + : /* cmHomeMethod */ + (((aCogMethod->cmType)) >= CMMethod + ? ((CogMethod *) aCogMethod) + : (assert(CMBlock == (aCogMethod->cmType)), + ((CogMethod *) ((((usqInt)aCogMethod)) - ((aCogMethod->homeOffset))))))); + markAndTrace((homeMethod->methodObject)); + } + else { + markAndTrace(iframeMethod(theFP)); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + theFP = callerFP; + } + + /* caller ip is ceBaseReturnPC */ + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + longAtput(theSP,oop); + } + if (!(((oop & (tagMask())) != 0))) { + markAndTrace(oop); + } + theSP += BytesPerWord; + } +} + + /* CoInterpreter>>#markCogMethodsAndReferentsOnPage: */ +static NoDbgRegParms void +markCogMethodsAndReferentsOnPage(StackPage *thePage) +{ + char *callerFP; + char *theFP; + + callerFP = ((char *) 0); + assert(!(isFree(thePage))); + assert(ifCurrentStackPageHasValidHeadPointers(thePage)); + theFP = (thePage->headFP); + while (1) { + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + markMethodAndReferents(mframeCogMethod(theFP)); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theFP = callerFP; + } +} + + +/* Marry an unmarried frame. This means creating a spouse context + initialized with a subset of the frame's state that references the frame. + For the default closure implementation we do not need to copy temps. + + This method is important enough for performance to be worth streamlining. + + Override to set the ``has context'' flag appropriately for both machine + code and + interpreter frames and to streamline the machine code/interpreter + differences. + */ + + /* CoInterpreter>>#marryFrame:SP: */ +static NoDbgRegParms usqInt +marryFrameSP(char *theFP, char *theSP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt closureOrNil; + CogMethod *cogMethod; + sqInt header; + sqInt headerSqInt; + sqInt i; + sqInt methodFieldOrObj; + usqInt newObj; + usqInt numArgs; + usqInt numBytes; + sqInt numSlots; + sqInt numStack; + sqInt rcvr; + usqInt theContext; + sqInt valuePointer; + + assert(!((frameHasContext(theFP)))); + assert(!((isBaseFrame(theFP)))); + assert(addressCouldBeOop(longAt(theSP))); + + /* Decide how much of the stack to preserve in widowed contexts. Preserving too much state will potentially + hold onto garbage. Holding onto too little may mean that a dead context isn't informative enough in a + debugging situation. But the fundamental issue is that the temporary values captured when the frame is + married are likely stale, so if the temporary variable values are important they need to be updated later; + see primitiveContextPreserveTemps. So when marrying we capture the arguments and set the stack pointer + to accomodate (at least *) the temporaries, but the temporaries are all set to nil. In machine code contexts + are created via code generated by genGetActiveContextLarge:inBlock: which must agree perfectly with + this method. + * the V3PlusClosures bytecode set has to use pushNil bytecodes to establish slots for temporary variables + in block contexts, but a pushNil may be ambiguous with oushing a receiver, or even argument, e.g. + [nil == nil]. Instead of sweating the issue of using an accurate temporary count, we simply set the stack + pointer to its current value and set all slots following the arguments to nil. */ + methodFieldOrObj = longAt(theFP + FoxMethod); + if ((((usqInt)methodFieldOrObj)) < (startOfMemory())) { + longAtput(theFP + FoxMethod,methodFieldOrObj | MFMethodFlagHasContextFlag); + cogMethod = ((CogMethod *) (methodFieldOrObj & MFMethodMask)); + numArgs = (cogMethod->cmNumArgs); + if (((cogMethod->cmType)) >= CMMethod) { + closureOrNil = ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock) + ? longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((numArgs << (shiftForWord()))))) + : GIV(nilObj)); + } + else { + assert(isCMBlock(((CogBlockMethod *) cogMethod))); + + /* cmHomeMethod */ + if ((((((CogBlockMethod *) cogMethod))->cmType)) >= CMMethod) { + cogMethod = ((CogMethod *) (((CogBlockMethod *) cogMethod))); + } + else { + assert(CMBlock == ((((CogBlockMethod *) cogMethod))->cmType)); + cogMethod = ((CogMethod *) ((((usqInt)(((CogBlockMethod *) cogMethod)))) - (((((CogBlockMethod *) cogMethod))->homeOffset)))); + } + closureOrNil = longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((numArgs << (shiftForWord()))))); + } + methodFieldOrObj = (cogMethod->methodObject); + header = (cogMethod->methodHeader); + rcvr = longAt(theFP + FoxMFReceiver); + } + else { + /* begin setIFrameHasContext: */ + byteAtput((theFP + FoxIFrameFlags) + 2,1); + assert(isCompiledMethod(methodFieldOrObj)); + headerSqInt = longAt((void *)((methodFieldOrObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + if (byteAt((theFP + FoxIFrameFlags) + 3)) { + numArgs = byteAt((theFP + FoxIFrameFlags) + 1); + closureOrNil = longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((numArgs << (shiftForWord()))))); + } + else { + numArgs = (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; + closureOrNil = GIV(nilObj); + } + rcvr = longAt(theFP + FoxIFReceiver); + } + + /* inline (self isMachineCodeFrame: theFP) */ + numStack = (((usqInt)(((theFP + FoxMFReceiver) - theSP))) >> (shiftForWord())) + numArgs; + numSlots = (header & LargeContextBit + ? LargeContextSlots + : SmallContextSlots); + + /* begin eeInstantiateMethodContextSlots: */ + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + theContext = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((indexablePointersFormat())) << (formatShift()))))) + ClassMethodContextCompactIndex); + GIV(freeStart) += numBytes; + theContext = newObj; + /* end eeInstantiateMethodContextSlots: */ +l1: + + /* begin setFrameContext:to: */ + longAtput(theFP + FoxThisContext,theContext); + + /* Mark context as married by setting its sender to the frame pointer plus SmallInteger tags and the InstructionPointer + to the saved fp (which ensures correct alignment w.r.t. the frame when we check for validity) */ + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(theFP)) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(theFP)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, theContext, valuePointer)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),valuePointer); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(((char *)(longAt(theFP + FoxSavedFP))))) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(((char *)(longAt(theFP + FoxSavedFP))))) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, theContext, valuePointer)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(StackPointerIndex, theContext, (((usqInt)numStack << 3) | 1))); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord()))))),(((usqInt)numStack << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(MethodIndex, theContext, methodFieldOrObj)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord()))))),methodFieldOrObj); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(ClosureIndex, theContext, closureOrNil)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord()))))),closureOrNil); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(ReceiverIndex, theContext, rcvr)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord()))))),rcvr); + for (i = 1; i <= numArgs; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(ReceiverIndex + i, theContext, longAt((void *)((theFP + FoxCallerSavedIP) + ((numArgs - (i - 1)) * BytesPerWord))))); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))),longAt((void *)((theFP + FoxCallerSavedIP) + ((numArgs - (i - 1)) * BytesPerWord)))); + } + for (i = (numArgs + 1); i <= numStack; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(ReceiverIndex + i, theContext, GIV(nilObj))); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))),GIV(nilObj)); + } + assert(frameHasContext(theFP)); + assert(frameOfMarriedContext(theContext) == theFP); + assert((numStack + ReceiverIndex) < (lengthOf(theContext))); + return theContext; +} + + /* CoInterpreter>>#maxLookupNoMNUErrorCode */ +sqInt +maxLookupNoMNUErrorCode(void) +{ + return ((SelectorCannotInterpret < SelectorDoesNotUnderstand) ? SelectorDoesNotUnderstand : SelectorCannotInterpret); +} + + /* CoInterpreter>>#methodCacheAddress */ +usqInt +methodCacheAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)GIV(methodCache)); +} + + /* CoInterpreter>>#methodHasCogMethod: */ +int +methodHasCogMethod(sqInt aMethodOop) +{ + sqInt methodHeader; + + assert(isNonImmediate(aMethodOop)); + methodHeader = longAt((void *)((aMethodOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + return (!(methodHeader & (smallIntegerTag()))); +} + + /* CoInterpreter>>#methodHasMachineCodePrimitiveMethod: */ +static NoDbgRegParms sqInt +methodHasMachineCodePrimitiveMethod(sqInt methodObj) +{ + sqInt methodHeader; + + methodHeader = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + return ((/* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))), + /* isNonIntegerObject: */ + (!(methodHeader & (smallIntegerTag()))))) + && (cogMethodHasMachineCodePrim(((CogMethod *) methodHeader))); +} + + /* CoInterpreter>>#methodHasPrimitiveInPrimTracePlugin: */ +static NoDbgRegParms NeverInline sqInt +methodHasPrimitiveInPrimTracePlugin(sqInt aMethodObj) +{ + sqInt len; + sqInt lit; + usqInt numSlots; + sqInt ok; + sqInt pluginName; + + if (!(((primitiveIndexOf(aMethodObj)) == PrimNumberExternalCall) + && ((literalCountOf(aMethodObj)) > 0))) { + return 0; + } + lit = longAt((void *)((aMethodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(lit & (tagMask())))) + && (((byteAt((void *)(lit + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(lit)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(lit + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(lit - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4))) { + return 0; + } + assert(primTracePluginName); + pluginName = longAt((void *)((lit + BaseHeaderSize) + (0U << (shiftForWord())))); + if (!(/* isBytes: */ + ((!(pluginName & (tagMask())))) + && (((byteAt((void *)(pluginName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + return 0; + } + + /* begin object:equalsString: */ + len = strlen(primTracePluginName); + ok = /* object:equalsString:ofSize: */ + (/* isBytes: */ + ((!(pluginName & (tagMask())))) + && (((byteAt((void *)(pluginName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((!(((byteAt((void *)(pluginName + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((numBytesOfBytes(pluginName)) == len) + && ((strncmp(primTracePluginName, firstIndexableField(pluginName), len)) == 0))); + return ok; +} + + /* CoInterpreter>>#methodHasTooManyLiterals: */ +static NoDbgRegParms sqInt +methodHasTooManyLiterals(sqInt methodObj) +{ + return !(methodWithHeaderShouldBeCogged(methodHeaderOf(methodObj))); +} + + +/* Note: We now have 10 bits of primitive index, but they are in two places + for temporary backward compatibility. The time to unpack is negligible, + since the derived primitive function pointer is stored in the method + cache. */ + + /* CoInterpreter>>#methodHeaderHasPrimitive: */ +#if VMInvestigations +int +methodHeaderHasPrimitive(sqInt methodHeader) +{ + return ((methodHeader & AlternateHeaderHasPrimFlag) != 0); +} +#endif /* VMInvestigations */ + + /* CoInterpreter>>#methodNeedsLargeContext: */ +int +methodNeedsLargeContext(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + sqInt methodHeader; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin methodHeaderIndicatesLargeFrame: */ + return (methodHeader & LargeContextBit) != 0; +} + + /* CoInterpreter>>#methodShouldBeCogged: */ +sqInt +methodShouldBeCogged(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + sqInt methodHeader; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + return 1; + } + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(aMethodObj); + } + return 0; +} + + +/* At the moment jit any method with less than N literals, where N defaults + to 60. + See e.g. SimpleStackBasedCogit class>>initialize. + In my dev image eem 2/22/2009 13:39 + (30 to: 100 by: 5) collect: + [:n| n -> (SystemNavigation default allSelect: [:m| m numLiterals > n]) + size] #(30->1681 35->1150 40->765 45->523 50->389 55->289 60->206 + 65->151 70->124 75->99 80->73 85->63 90->54 95->42 100->38). + And running the CogVMSimulator with flagging of interpreted methods turned + on reveals + the following sizes of interpreted methods. + | sizes | + sizes := Bag new. + SystemNavigation default allSelect: [:m| m flag ifTrue: [sizes add: m + numLiterals]. false]. + sizes sortedElements asArray + #( 40->4 41->1 42->2 44->1 45->3 46->1 47->2 48->1 + 50->2 51->1 53->1 55->1 56->1 + 87->1 108->1 171->1) + literalCountOfHeader: does not include the header word. */ + + /* CoInterpreter>>#methodWithHeaderShouldBeCogged: */ +static NoDbgRegParms int +methodWithHeaderShouldBeCogged(sqInt methodHeader) +{ + return ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile; +} + + +/* Answer the Cog method for a machine code frame. This may be + either a full CogMethod or merely a CogBlockMethod rump header. */ + + /* CoInterpreter>>#mframeCogMethod: */ +static NoDbgRegParms CogBlockMethod * +mframeCogMethod(char *theFP) +{ + return ((CogBlockMethod *) ((longAt(theFP + FoxMethod)) & MFMethodMask)); +} + + /* CoInterpreter>>#mframeHomeMethodExport */ +CogMethod * +mframeHomeMethodExport(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt methodField; + + /* begin mframeHomeMethod: */ + methodField = longAt(GIV(framePointer) + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + return /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + } + return ((CogMethod *) (methodField & MFMethodMask)); +} + + +/* Answer the home method for a machine code frame. From a block frame we + find the home method through the block's homeOffset field which is the + delta to it. + In both cases we need to strip the isBlock and isContext flags from the + method field. */ + + /* CoInterpreter>>#mframeHomeMethod: */ +static NoDbgRegParms CogMethod * +mframeHomeMethod(char *theFP) +{ + sqInt methodField; + + methodField = longAt(theFP + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + return /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + } + return ((CogMethod *) (methodField & MFMethodMask)); +} + + +/* */ + + /* CoInterpreter>>#mframeIsBlockActivation: */ +static NoDbgRegParms int +mframeIsBlockActivation(char *theFP) +{ + return ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0; +} + + /* CoInterpreter>>#mframeReceiver: */ +static NoDbgRegParms sqInt +mframeReceiver(char *theFP) +{ + return longAt(theFP + FoxMFReceiver); +} + + +/* Traverse all stack pages looking for non-zero bytes in the headroom part + of each page. + Answer the minimum size of unused headroom (zero bytes) in the pages. This + is for + checking that there is enough headroom allocated in stack pages. */ + + /* CoInterpreter>>#minimumUnusedHeadroom */ +static NeverInline sqInt +minimumUnusedHeadroom(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt minUnused; + char *p; + StackPage *page; + sqInt unused; + + if (!(GIV(pages))) { + return -1; + } + minUnused = (((stackPageAt(0))->baseAddress)) - (((stackPageAt(0))->lastAddress)); + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + page = stackPageAtpages(i, GIV(pages)); + p = (page->lastAddress); + do { + p += BytesPerWord; + } while(((longAt(p)) == 0) + && (p <= ((page->baseAddress)))); + unused = (p - BytesPerWord) - ((page->lastAddress)); + if (unused < minUnused) { + minUnused = unused; + } + } + return minUnused; +} + + +/* Lookup the doesNotUnderstand: selector in the class of the argument rcvr. + Answer either the matching method (cogged if appropriate), or nil, if not + found. + */ + + /* CoInterpreter>>#mnuMethodOrNilFor: */ +static NoDbgRegParms sqInt +mnuMethodOrNilFor(sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentClass; + sqInt dictionary; + usqInt index; + usqInt length; + sqInt mask; + sqInt methodArray; + sqInt methodHeader; + sqInt mnuMethod; + sqInt mnuSelector; + sqInt nextSelector; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt tagBits; + sqInt wrapAround; + + assert(!((isOopForwarded(rcvr)))); + currentClass = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)); + mnuSelector = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))); + while (currentClass != GIV(nilObj)) { + dictionary = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + if (dictionary == GIV(nilObj)) { + return null; + } + + /* begin lookupMethodFor:InDictionary: */ + /* begin numSlotsOf: */ + assert((classIndexOf(dictionary)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(dictionary + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(dictionary - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + mask = (length - SelectorStart) - 1; + index = SelectorStart + (mask & ((((mnuSelector & (tagMask())) != 0) + ? (mnuSelector >> 3) + : (long32At((void *)(mnuSelector + 4))) & (identityHashHalfWordMask())))); + + /* It is assumed that there are some nils in this dictionary, and search will + stop when one is encountered. However, if there are no nils, then wrapAround + will be detected the second time the loop gets to the end of the table. */ + wrapAround = 0; + while (1) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((index << (shiftForWord()))))); + if (nextSelector == GIV(nilObj)) { + mnuMethod = null; + goto l1; + } + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == mnuSelector) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOop = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(index - SelectorStart, methodArray, objOop); + } + mnuMethod = objOop; + goto l1; + } + index += 1; + if (index == length) { + if (wrapAround) { + mnuMethod = null; + goto l1; + } + wrapAround = 1; + index = SelectorStart; + } + } + mnuMethod = null; + /* end lookupMethodFor:InDictionary: */ +l1: + if (mnuMethod) { + methodHeader = longAt((void *)((mnuMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + if ((!((/* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))), + /* isNonIntegerObject: */ + (!(methodHeader & (smallIntegerTag())))))) + && (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile)) { + cogselector(mnuMethod, mnuSelector); + } + return mnuMethod; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOopSqInt = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOopSqInt)); + if ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOopSqInt); + } + currentClass = objOopSqInt; + } + return null; +} + + +/* Move frames from the hot end of oldPage through to theFP to newPage. + This has the effect of making theFP a base frame which can be stored into. + Answer theFP's new location. */ + + /* CoInterpreter>>#moveFramesIn:through:toPage: */ +static NoDbgRegParms char * +moveFramesInthroughtoPage(StackPage *oldPage, char *theFP, StackPage *newPage) +{ + char *callerFP; + usqInt callerIP; + sqInt delta; + char *fpInNewPage; + char *newFP; + char *newSP; + char *offsetCallerFP; + char *source; + sqInt stackedReceiverOffset; + sqInt theContext; + char *theFP1; + char *theSP; + sqInt valuePointer; + sqInt valuePointerSqInt; + + + /* A base frame must have a context for cannotReturn: processing. */ + assert(!(isBaseFrame(theFP))); + assert(validStackPageBaseFrames()); + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + assert(frameHasContext(callerFP)); + assert(isContext(frameContext(callerFP))); + theSP = theFP + (((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? FoxMFReceiver + : FoxIFReceiver)); + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + theContext = longAt(theFP + FoxThisContext); + goto l1; + } + theContext = marryFrameSP(theFP, theSP); + /* end ensureFrameIsMarried:SP: */ +l1: + longAtput((newSP = (newPage->baseAddress)),longAt(callerFP + FoxThisContext)); + longAtput((newSP -= BytesPerWord),theContext); + stackedReceiverOffset = (FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))); + + /* First move the data, leaving room for the caller and base frame contexts. We will fix up frame pointers later. */ + for (source = (theFP + stackedReceiverOffset); source >= ((oldPage->headSP)); source += (-BytesPerWord)) { + newSP -= BytesPerWord; + longAtput(newSP,longAt(source)); + } + + /* newSP = oldSP + delta => delta = newSP - oldSP */ + delta = newSP - ((oldPage->headSP)); + newFP = (((newPage->baseAddress)) - stackedReceiverOffset) - (2 * BytesPerWord); + theFP1 = ((oldPage->headFP)) + delta; + + /* begin setHeadFP:andSP:inPage: */ + assert(newSP < theFP1); + assert((newSP < ((newPage->baseAddress))) + && (newSP > (((newPage->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((theFP1 < ((newPage->baseAddress))) + && (theFP1 > (((newPage->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (newPage->headFP = theFP1); + (newPage->headSP = newSP); + (newPage->baseFP = newFP); + callerIP = longAt(theFP + FoxCallerSavedIP); + if ((((usqInt)callerIP)) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(callerFP))); + longAtput(callerFP + FoxIFSavedIP,callerIP); + callerIP = ceReturnToInterpreterPC(); + } + longAtput(theFP + stackedReceiverOffset,callerIP); + assert((callerFP < ((oldPage->baseAddress))) + && (callerFP > (((oldPage->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (oldPage->headFP = callerFP); + (oldPage->headSP = theFP + stackedReceiverOffset); + + /* Mark the new base frame in the new page */ + longAtput(newFP + FoxCallerSavedIP,ceBaseFrameReturnPC()); + longAtput(newFP + FoxSavedFP,0); + + /* Now relocate frame pointers, updating married contexts to refer to their moved spouse frames. */ + fpInNewPage = (newPage->headFP); + do { + offsetCallerFP = ((char *)(longAt(fpInNewPage + FoxSavedFP))); + if (offsetCallerFP) { + offsetCallerFP += delta; + } + longAtput(fpInNewPage + FoxSavedFP,oopForPointer(offsetCallerFP)); + if (/* frameHasContext: */ + ((((usqInt)(longAt(fpInNewPage + FoxMethod)))) < (startOfMemory()) + ? ((longAt(fpInNewPage + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((fpInNewPage + FoxIFrameFlags) + 2)) != 0)) { + theContext = longAt(fpInNewPage + FoxThisContext); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(fpInNewPage)) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(fpInNewPage)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, theContext, valuePointer)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),valuePointer); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(offsetCallerFP)) & (BytesPerWord - 1)) == 0); + valuePointerSqInt = (oopForPointer(offsetCallerFP)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, theContext, valuePointerSqInt)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointerSqInt); + } + fpInNewPage = offsetCallerFP; + } while(fpInNewPage != 0); + assert(validStackPageBaseFrames()); + return newFP; +} + + +/* Map the native pc theIP into a bytecode pc integer object and answer it. + See contextInstructionPointer:frame: for the explanation. */ + + /* CoInterpreter>>#mustMapMachineCodePC:context: */ +static NoDbgRegParms sqInt +mustMapMachineCodePCcontext(sqInt theIP, sqInt aOnceMarriedContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bcpc; + CogMethod *cogMethod; + CogMethod *cogMethod1; + CogBlockMethod *cogMethodForIP; + sqInt maybeClosure; + sqInt mcpc; + sqInt methodObj; + usqInt numSlots; + sqInt rawHeader; + usqInt startBcpc; + sqInt yetToCompact; + + if (theIP == HasBeenReturnedFromMCPC) { + return GIV(nilObj); + } + maybeClosure = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord())))))); + methodObj = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + if ((maybeClosure != GIV(nilObj)) + && ((/* begin isVanillaBlockClosure: */ + assert((isPointers(maybeClosure)) + && ((numSlotsOf(maybeClosure)) >= ClosureFirstCopiedValueIndex)), + ((((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1)))) { + assert((((theIP) >> 16)) < -1); + startBcpc = (((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))))) >> 3)) - 1; + } + else { + assert((((theIP) >> 16)) == -1); + startBcpc = ((literalCountOf(methodObj)) + LiteralStart) * BytesPerOop; + } + + /* begin ensureMethodIsCogged:maybeClosure: */ + rawHeader = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) rawHeader); + goto l2; + } + yetToCompact = 1; + while (1) { + if ((maybeClosure != GIV(nilObj)) + && (!((/* begin isVanillaBlockClosure: */ + assert((isPointers(maybeClosure)) + && ((numSlotsOf(maybeClosure)) >= ClosureFirstCopiedValueIndex)), + ((((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1))))) { + cogMethod1 = cogFullBlockMethodnumCopied( + methodObj, + (/* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(maybeClosure)))), + ((/* begin numSlotsOf: */ + assert((classIndexOf(maybeClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(maybeClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(maybeClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex)); + } + else { + cogMethod1 = cogselector(methodObj, GIV(nilObj)); + } + if (!((cogMethod1 == null) + && (GIV(cogCompiledCodeCompactionCalledFor) + && (yetToCompact)))) break; + yetToCompact = 0; + commenceCogCompiledCodeCompaction(); + } + if (!(asserta(cogMethod1 != null))) { + error("could not compile method that should have been compiled"); + } + cogMethod = cogMethod1; + /* end ensureMethodIsCogged:maybeClosure: */ +l2: + + /* begin bytecodePCFor:cogMethod:startBcpc: */ + assert(theIP < 0); + if ((((theIP) >> 16)) < -1) { + cogMethodForIP = ((CogBlockMethod *) ((((sqInt)cogMethod)) - ((((theIP) >> 16)) * (blockAlignment())))); + assert(isCMBlock(cogMethodForIP)); + assert((cmHomeMethod(cogMethodForIP)) == cogMethod); + mcpc = (((sqInt)cogMethodForIP)) - (((short)theIP)); + } + else { + cogMethodForIP = ((CogBlockMethod *) cogMethod); + assert(isCMMethodEtAl(cogMethodForIP)); + mcpc = (((sqInt)cogMethod)) - theIP; + + /* map any pcs in primitive code (i.e. return addresses for interpreter primitive calls) to the initial pc */ + if ((((usqInt)mcpc)) < ((cogMethod->stackCheckOffset))) { + bcpc = startBcpc; + goto l1; + } + } + + /* See contextInstructionPointer:frame: */ + assert(((mcpc >= (((sqInt)cogMethod))) && (mcpc <= ((((sqInt)cogMethod)) + ((cogMethod->blockSize)))))); + bcpc = bytecodePCForstartBcpcin(mcpc, startBcpc, cogMethodForIP); + /* end bytecodePCFor:cogMethod:startBcpc: */ +l1: + assert(bcpc >= (startPCOfMethod(methodObj))); + + /* If there's a CallPrimitive we need to skip it. */ + if ((bcpc == startBcpc) + && ((maybeClosure == GIV(nilObj)) + && (((((cogMethod->methodHeader)) & AlternateHeaderHasPrimFlag) != 0)))) { + bcpc += 3 /* sizeOfCallPrimitiveBytecode: */; + } + return (((usqInt)(bcpc + 1) << 3) | 1); +} + + /* CoInterpreter>>#newMethodAddress */ +usqInt +newMethodAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(newMethod)))); +} + + +/* N.B. nextProfileTick is 64-bits */ + + /* CoInterpreter>>#nextProfileTickAddress */ +usqInt +nextProfileTickAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(nextProfileTick)))); +} + + /* CoInterpreter>>#noAssertHeaderOf: */ +sqInt +noAssertHeaderOf(sqInt methodPointer) +{ + sqInt methodHeader; + + methodHeader = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + return ((!(methodHeader & (smallIntegerTag()))) + ? ((((CogMethod *) methodHeader))->methodHeader) + : methodHeader); +} + + +/* integerValue is interpreted as POSITIVE, e.g. as the result of Bitmap>at:. + N.B. Returning in each arm separately enables Slang inlining. + /Don't/ return the ifTrue:ifFalse: unless Slang inlining of conditionals + is fixed. */ + + /* CoInterpreter>>#positive32BitIntegerFor: */ +sqInt +positive32BitIntegerFor(unsigned int integerValue) +{ + return (((usqInt)((((usqInt)integerValue)) & 0xFFFFFFFFU) << 3) | 1); +} + + +/* Convert the given object into an integer value. + The object may be either a positive SmallInteger or a four-byte + LargePositiveInteger. + */ + + /* CoInterpreter>>#positive32BitValueOf: */ +unsigned int +positive32BitValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt value64; + + if ((((oop) & 7) == 1)) { + value64 = (oop >> 3); + if ((value64 < 0) + || ((((unsigned int) value64)) != value64)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + value64 = 0; + } + return value64; + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; +} + + +/* Answer a Large Positive Integer object for the given integer value. N.B. + will *not* cause a GC. */ + + /* CoInterpreter>>#positive64BitIntegerFor: */ +sqInt +positive64BitIntegerFor(usqLong integerValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newLargeInteger; + usqInt newObj; + usqInt numBytes; + sqInt numSlots; + sqInt objFormat; + sqInt sz; + + if (integerValue <= (MaxSmallInteger)) { + return ((integerValue << 3) | 1); + } + sz = 8; + objFormat = (firstByteFormat()) + ((8 - sz) & (BytesPerWord - 1)); + numSlots = 8 / BytesPerOop; + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((numSlots >= 0) + && ((knownClassAtIndex(ClassLargePositiveIntegerCompactIndex)) != GIV(nilObj))); + assert(((objFormat < (firstByteFormat()) + ? objFormat + : objFormat & (byteFormatMask()))) == (instSpecOfClass(knownClassAtIndex(ClassLargePositiveIntegerCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newLargeInteger = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + ClassLargePositiveIntegerCompactIndex); + GIV(freeStart) += numBytes; + newLargeInteger = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((newLargeInteger + BaseHeaderSize)),SQ_SWAP_8_BYTES_IF_BIGENDIAN(integerValue)); + return newLargeInteger; +} + + +/* Convert the given object into an integer value. + The object may be either a positive SmallInteger or up to an eight-byte + LargePositiveInteger. + */ + + /* CoInterpreter>>#positive64BitValueOf: */ +usqLong +positive64BitValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + sqInt fmt; + usqInt numSlots; + int ok; + sqInt smallIntValue; + sqInt sz; + + if ((((oop) & 7) == 1)) { + smallIntValue = (oop >> 3); + if (smallIntValue < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return smallIntValue; + } + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (!ok) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + sz = ((((/* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (sz > (sizeof(sqLong))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + if (sz > 4) { + return SQ_SWAP_8_BYTES_IF_BIGENDIAN((long64At((void *)((oop + BaseHeaderSize))))); + } + return ((unsigned int) (SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((oop + BaseHeaderSize))))))); +} + + +/* Signal the gc semaphore, and inform the display subsystem if the display + bitmap has moved. + */ +/* Attempt to shrink free memory, signal the gc semaphore and let the Cogit + do its post GC thang + */ + + /* CoInterpreter>>#postGCAction: */ +static NoDbgRegParms void +postGCAction(sqInt gcModeArg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(gcModeArg == GIV(gcMode)); + signalSemaphoreWithIndex(GIV(gcSemaphoreIndex)); + postGCUpdateDisplayBits(); + cogitPostGCAction(gcModeArg); + + /* begin nilUncoggableMethods */ + GIV(lastCoggableInterpretedBlockMethod) = (GIV(lastUncoggableInterpretedBlockMethod) = null); + GIV(gcMode) = 0; +} + + +/* Need to write back the frame pointers unless all pages are free (as in + snapshot) + */ +/* Need to write back the frame pointers unless all pages are free (as in + snapshot). Need to set gcMode var (to avoid passing the flag through a lot + of the updating code) */ + + /* CoInterpreter>>#preGCAction: */ +static NoDbgRegParms void +preGCAction(sqInt gcModeArg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aTraceCode; + int traceType; + + if (GIV(stackPage)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + if (recordPrimTrace()) { + aTraceCode = (gcModeArg == GCModeFull + ? TraceFullGC + : TraceIncrementalGC); + + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)aTraceCode << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + if (gcModeArg == GCModeFull) { + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + } + GIV(gcMode) = gcModeArg; + if (recordEventTrace()) { + traceType = (gcModeArg == GCModeFull + ? TraceFullGC + : TraceIncrementalGC); + + /* begin recordTrace: */ + assert(traceType < ((((TraceOwnerShift - TraceTypeShift) < 0) ? (((usqInt)(1)) >> (-(TraceOwnerShift - TraceTypeShift))) : (1U << (TraceOwnerShift - TraceTypeShift))))); + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(traceType) << TraceTypeShift)))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = 0; + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } +} + + /* CoInterpreter>>#primFailCodeAddress */ +usqInt +primFailCodeAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(primFailCode)))); +} + + +/* Return from a become primitive after invoking + become:with:twoWay:copyHash:. If succeeding and the top frame has been + divorced then continue in the interpreter. + See the comment in primitiveArrayBecome. */ + + /* CoInterpreter>>#primitiveBecomeReturn: */ +static NoDbgRegParms void +primitiveBecomeReturn(sqInt ec) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContextOrNil; + sqInt activeProc; + sqInt objOop; + + if (ec) { + /* primitiveFailFor: */ + GIV(primFailCode) = ec; + } + else { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + activeContextOrNil = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if (!GIV(framePointer)) { + assert(isContext(activeContextOrNil)); + + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContextOrNil); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(activeProc)) + && (!(isForwarded(activeProc)))); + assert(validStorePointerUncheckedArgs(SuspendedContextIndex, activeProc, GIV(nilObj))); + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* pop bogus machine-code instructionPointer and any primitive arguments */ + + /* begin pop: */ + GIV(stackPointer) += (GIV(argumentCount) + 1) * BytesPerWord; + ceInvokeInterpret(); + } + + /* If the top frame was divorced then continue in the interpreter. + If not, check where we are and continue... */ + if (!(activeContextOrNil == GIV(nilObj))) { + /* divorcing should not have disturbed the topFrame if it wasn;t divorced */ + assert(isContext(activeContextOrNil)); + assert(GIV(framePointer) == (frameOfMarriedContext(activeContextOrNil))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(activeProc)) + && (!(isForwarded(activeProc)))); + assert(validStorePointerUncheckedArgs(SuspendedContextIndex, activeProc, GIV(nilObj))); + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),GIV(nilObj)); + } + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } +} + + +/* receiver, args, then method are on top of stack. Execute method against + receiver and args. + Set primitiveFunctionPointer because no cache lookup has been done for the + method, and + hence primitiveFunctionPointer is stale. */ + + /* CoInterpreter>>#primitiveExecuteMethod */ +static void +primitiveExecuteMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodArgument; + sqInt methodHeader; + sqInt primitiveIndex; + sqInt top; + + methodArgument = longAt(GIV(stackPointer)); + if (!(/* isOopCompiledMethod: */ + ((!(methodArgument & (tagMask())))) + && (((byteAt((void *)(methodArgument + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (!((GIV(argumentCount) - 1) == (argumentCountOf(methodArgument)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(newMethod) = top; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]))); + GIV(argumentCount) -= 1; + executeNewMethodJitting(); + + /* Recursive xeq affects primErrorCode */ + + /* begin initPrimCall */ + GIV(primFailCode) = 0; +} + + +/* receiver, argsArray, then method are on top of stack. Execute method + against receiver and args. Allow for up to two extra arguments (e.g. for + mirror primitives). + Set primitiveFunctionPointer because no cache lookup has been done for the + method, and hence primitiveFunctionPointer is stale. */ + + /* CoInterpreter>>#primitiveExecuteMethodArgsArray */ +static void +primitiveExecuteMethodArgsArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt argCnt; + sqInt argumentArray; + usqInt firstBytecode; + sqInt header; + sqInt i; + sqInt methodArgument; + sqInt methodHeader; + usqInt numSlots; + sqInt primitiveIndex; + sqInt rcvr; + + methodArgument = longAt(GIV(stackPointer)); + argumentArray = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((/* isOopCompiledMethod: */ + ((!(methodArgument & (tagMask())))) + && (((byteAt((void *)(methodArgument + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* begin argumentCountOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodArgument)); + header = longAt((void *)((methodArgument + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + argCnt = (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; + if (!(argCnt == ((/* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + if (GIV(argumentCount) > 2) { + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if ((GIV(argumentCount) > 4) + || (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord),rcvr); + } + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + for (i = 0; i < argCnt; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))))); + } + GIV(newMethod) = methodArgument; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]))); + GIV(argumentCount) = argCnt; + executeNewMethodJitting(); + + /* Recursive xeq affects primErrorCode */ + + /* begin initPrimCall */ + GIV(primFailCode) = 0; +} + + +/* Answer any special requirements of the given primitive */ + + /* CoInterpreter>>#primitivePropertyFlags:primIndex: */ +sqInt +primitivePropertyFlagsprimIndex(sqInt methodObj, sqInt primIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int baseFlags; + + /* begin primitivePropertyFlagsForSpur:primIndex: */ + if (primIndex == PrimNumberRelinquishProcessor) { + return (GIV(profileSemaphore) + ? (PrimCallNeedsNewMethod + PrimCallMayEndureCodeCompaction) + PrimCallCollectsProfileSamples + : PrimCallNeedsNewMethod + PrimCallMayEndureCodeCompaction); + } + baseFlags = 0; + if (GIV(profileSemaphore)) { + if (isRealNonProfilingPrimitiveMethodprimIndex(methodObj, primIndex)) { + baseFlags = PrimCallCollectsProfileSamples + PrimCallNeedsNewMethod; + } + } +# if LRPCheck + if (GIV(longRunningPrimitiveCheckSemaphore)) { + baseFlags = baseFlags | PrimCallNeedsNewMethod; + } +# endif + + + /* Because one primitive number is used for all three vmParameter primtiives + the metadata can't be attached to the method implementation. */ + if ((primIndex == PrimNumberVMParameter) + && ((argumentCountOf(methodObj)) == 1)) { + return (baseFlags + PrimCallOnSmalltalkStack) + PrimCallOnSmalltalkStackAlign2x; + } + + /* vmParameterAt: */ + return baseFlags | ((primitiveMetadataTable[primIndex]) & SpurPrimitiveFlagsMask); +} + + /* CoInterpreter>>#primTraceLogAddress */ +void * +primTraceLogAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(primTraceLog); +} + + +/* N.B. primTraceLogIndex is 8-bits */ + + /* CoInterpreter>>#primTraceLogIndexAddress */ +usqInt +primTraceLogIndexAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(primTraceLogIndex)))); +} + + /* CoInterpreter>>#printCogMethod: */ +void +printCogMethod(CogMethod *cogMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + usqInt firstBytecode; + sqInt primitive; + sqInt s; + + address = ((sqInt)cogMethod); + printHex(address); + print(" <-> "); + printHex(address + ((cogMethod->blockSize))); + if (((cogMethod->cmType)) >= CMMethod) { + print(": method: "); + printHex((cogMethod->methodObject)); + + /* primitiveIndexOfMethod:header: */ + if (((((cogMethod->methodHeader)) & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (((cogMethod->methodObject)) + ((LiteralStart + (((((cogMethod->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitive = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitive = 0; + } + if (primitive) { + print(" prim "); + printNum(primitive); + } + if (addressCouldBeObj((cogMethod->methodObject))) { + if ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) { + print(" [full]"); + } + else { + if (addressCouldBeObj(methodClassOf((cogMethod->methodObject)))) { + /* begin space */ + printChar(' '); + printNameOfClasscount(methodClassOf((cogMethod->methodObject)), 2); + } + } + } + } + if (((cogMethod->cmType)) == CMBlock) { + print(": block home: "); + printHex(((usqInt)(/* cmHomeMethod */ + ((((((CogBlockMethod *) cogMethod))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) cogMethod))) + : (assert(CMBlock == ((((CogBlockMethod *) cogMethod))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) cogMethod)))) - (((((CogBlockMethod *) cogMethod))->homeOffset))))))))); + } + if (((cogMethod->cmType)) == CMClosedPIC) { + print(": Closed PIC N: "); + printHex((cogMethod->cPICNumCases)); + } + if (((cogMethod->cmType)) == CMOpenPIC) { + print(": Open PIC "); + } + print(" selector: "); + printHex((cogMethod->selector)); + if (((cogMethod->selector)) == GIV(nilObj)) { + if ((((cogMethod->cmType)) >= CMMethod) + && (((s = maybeSelectorOfMethod((cogMethod->methodObject)))))) { + print(" (nil: "); + printStringOf(s); + print(")"); + } + else { + print(" (nil)"); + } + } + else { + /* begin space */ + printChar(' '); + printStringOf((cogMethod->selector)); + } + cr(); +} + + /* CoInterpreter>>#printFrameFlagsForFP: */ +static NoDbgRegParms void +printFrameFlagsForFP(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *address; + usqInt it; + + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + address = theFP + FoxMethod; + it = (longAt(address)) & 7; + } + else { + address = theFP + FoxIFrameFlags; + it = longAt(address); + } + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%3sfrm flags: %p", + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? "mc" + : "int"), + ((void *)it)); + if (it) { + fprintf(GIV(transcript), + "=%d", + ((int) it)); + } + fprintf(GIV(transcript), + " numArgs: %d %sContext %sBlock\n", + ((int) (/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))), + (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0) + ? "is" + : "no"), + (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0) + ? "is" + : "not")); +} + + /* CoInterpreter>>#printFrameMethodFor: */ +static NoDbgRegParms void +printFrameMethodFor(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *address; + sqInt it; + + address = theFP + FoxMethod; + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + " method: %16p\t" /* pst: */, + ((void *)it)); + if (((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && (((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0)) { + fprintf(GIV(transcript), + "hm: %p\t", + ((void *)(mframeHomeMethod(theFP)))); + } + shortPrintOop(/* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod))); +} + + /* CoInterpreter>>#printFrameThing:at:extra: */ +static NoDbgRegParms void +printFrameThingatextra(char *name, char *address, sqInt extraValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt it; + + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%12s: %16p" /* pst: */, + name, + ((void *)it)); + framePrintDescription(it); + fprintf(GIV(transcript), + " %" PRIdSQINT "\n", + extraValue); +} + + +/* useful for VM debugging */ + + /* CoInterpreter>>#printFrame:WithSP: */ +sqInt +printFrameWithSP(char *theFP, char *theSP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *addr; + CogBlockMethod *cogMethod; + CogMethod *homeMethod; + usqInt i; + usqInt index; + usqInt numArgs; + usqInt numSlots; + usqInt numTemps; + char *rcvrAddress; + sqInt rcvrOrClosure; + usqInt theMethod; + sqInt theMethodEnd; + char *toDoLimit; + sqInt topThing; + + if (!(/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + fprintf(GIV(transcript), + "%p is not in the stack zone?!\n", + theFP); + return null; + } + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + cogMethod = mframeCogMethod(theFP); + homeMethod = mframeHomeMethod(theFP); + theMethod = ((usqInt)homeMethod); + theMethodEnd = (((sqInt)homeMethod)) + ((homeMethod->blockSize)); + numArgs = (cogMethod->cmNumArgs); + numTemps = (((usqInt)(((homeMethod->methodHeader)))) >> MethodHeaderTempCountShift) & 0x3F; + } + else { + theMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + theMethodEnd = theMethod + (((((/* begin numSlotsOf: */ + assert((classIndexOf(theMethod)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(theMethod + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(theMethod - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) + BaseHeaderSize); + numArgs = byteAt((theFP + FoxIFrameFlags) + 1); + numTemps = tempCountOf(theMethod); + } + if (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0)) { + /* No BlockLocalTempCounter in the Cogit's C code, so quick hack is to use numCopied + numArgs */ + rcvrOrClosure = longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))); + if (((!(rcvrOrClosure & (tagMask())))) + && ((addressCouldBeObj(rcvrOrClosure)) + && ((fetchClassOfNonImm(rcvrOrClosure)) == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassBlockClosure) << (shiftForWord())))))))))) { + numTemps = numArgs + (stSizeOf(rcvrOrClosure)); + } + else { + numTemps = numArgs; + } + } + shortPrintFrame(theFP); + rcvrAddress = theFP + ((FoxCallerSavedIP + BytesPerWord) + ((numArgs << (shiftForWord())))); + if (longAt(theFP + FoxSavedFP)) { + } + else { + rcvrAddress + (2 * BytesPerWord); + + /* empty frameRange:to: */ + printFrameOopat("(caller ctxt", rcvrAddress + (2 * BytesPerWord)); + printFrameOopat("(saved ctxt", rcvrAddress + (1 * BytesPerWord)); + } + printFrameOopat("rcvr/clsr", rcvrAddress); + for (i = numArgs; i >= 1; i += -1) { + printFrameOopindexat("arg", numArgs - i, (theFP + FoxCallerSavedIP) + (i * BytesPerWord)); + } + printFrameThingatextraString("caller ip", theFP + FoxCallerSavedIP, ((longAt(theFP + FoxCallerSavedIP)) == (ceReturnToInterpreterPC()) + ? "ceReturnToInterpreter" + : 0)); + + /* begin printFrameThing:at: */ + printFrameThingatextraString("saved fp", theFP + FoxSavedFP, ((char *)null)); + printFrameMethodFor(theFP); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + printFrameFlagsForFP(theFP); + } + printFrameOopat("context", theFP + FoxThisContext); + if (!((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()))) { + printFrameFlagsForFP(theFP); + } + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + rcvrAddress = theFP + FoxMFReceiver; + } + else { + printFrameThingatextra("saved ip", theFP + FoxIFSavedIP, (((usqInt)(longAt(theFP + FoxIFSavedIP))) + ? (((((usqInt)(longAt(theFP + FoxIFSavedIP)))) - theMethod) + 2) - BaseHeaderSize + : 0)); + rcvrAddress = theFP + FoxIFReceiver; + } + printFrameOopat("receiver", rcvrAddress); + topThing = longAt(theSP); + if ((oopisGreaterThanOrEqualToandLessThan(topThing, theMethod, theMethodEnd)) + || ((topThing == (ceReturnToInterpreterPC())) + || ((topThing == (ceBaseFrameReturnPC())) + || (topThing == (ceCannotResumePC()))))) { + toDoLimit = theSP + BytesPerWord; + for (addr = (rcvrAddress - BytesPerWord); addr >= toDoLimit; addr += (-BytesPerWord)) { + index = ((rcvrAddress - addr) / BytesPerWord) + numArgs; + if (index <= numTemps) { + printFrameOopindexat("temp", index - 1, addr); + } + else { + printFrameOopat((/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0) + ? "temp/stck" + : "stck"), addr); + } + } + printFrameThingatextra("frame ip", theSP, (topThing > theMethod + ? ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? topThing - theMethod + : ((topThing - theMethod) + 2) - BaseHeaderSize) + : topThing)); + } + else { + for (addr = (rcvrAddress - BytesPerWord); addr >= theSP; addr += (-BytesPerWord)) { + index = ((rcvrAddress - addr) / BytesPerWord) + numArgs; + if (index <= numTemps) { + printFrameOopindexat("temp", index - 1, addr); + } + else { + printFrameOopat((/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0) + ? "temp/stck" + : "stck"), addr); + } + } + } + return 0; +} + + +/* useful for VM debugging */ + + /* CoInterpreter>>#printMethodCacheFor: */ +void +printMethodCacheFor(sqInt thing) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqIntptr_t c; + int i; + sqIntptr_t m; + sqInt n; + sqIntptr_t p; + sqIntptr_t s; + + n = 0; + for (i = 0; i < MethodCacheSize; i += MethodCacheEntrySize) { + s = GIV(methodCache)[i + MethodCacheSelector]; + c = GIV(methodCache)[i + MethodCacheClass]; + m = GIV(methodCache)[i + MethodCacheMethod]; + p = GIV(methodCache)[i + MethodCachePrimFunction]; + if (((thing == -1) + || ((s == thing) + || ((c == thing) + || ((p == thing) + || ((m == thing) + || ((addressCouldBeObj(m)) + && ((/* maybeMethodHasCogMethod: */ + ((!(m & (tagMask())))) + && ((((byteAt((void *)(m + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((m + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) + && ((((sqInt)(cogMethodOf(m)))) == thing)))))))) + && ((/* addressCouldBeOop: */ + (((s & (tagMask())) != 0)) + || (addressCouldBeObj(s))) + && ((c != 0) + && ((addressCouldBeClassObj(c)) + || (addressCouldBeClassObj(classForClassTag(c))))))) { + n += 1; + fprintf(GIV(transcript), + "%d %x\n\t", + ((int) i), + ((int) i)); + if (((byteAt((void *)(s + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())) { + fprintf(GIV(transcript), + "%p %.*s\n", + ((void *)s), + ((int) (numBytesOfBytes(s))), + ((char *) (firstIndexableField(s)))); + } + else { + shortPrintOop(s); + } + + /* begin tab */ + printChar('\t'); + if (addressCouldBeClassObj(c)) { + shortPrintOop(c); + } + else { + printNum(c); + + /* begin space */ + printChar(' '); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)c)); + printChar(' '); + shortPrintOop(classForClassTag(c)); + } + + /* begin tab */ + printChar('\t'); + shortPrintOop(m); + + /* begin tab */ + printChar('\t'); + if (p > 0x400) { + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)p)); + } + else { + printNum(p); + } + cr(); + } + } + if (n > 1) { + printNum(n); + cr(); + } +} + + /* CoInterpreter>>#quickPrimitiveConstantFor: */ +sqInt +quickPrimitiveConstantFor(sqInt aQuickPrimitiveIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + switch (aQuickPrimitiveIndex) { + case 0x101: + return GIV(trueObj); + + case 258: + return GIV(falseObj); + + case 259: + return GIV(nilObj); + + case 260: + return ConstMinusOne; + + case 261: + return ConstZero; + + case 262: + return ConstOne; + + case 263: + return ConstTwo; + + default: + error("Case not found and no otherwise clause"); + } + return 0; +} + + /* CoInterpreter>>#quickPrimitiveGeneratorFor: */ +sqInt +(*quickPrimitiveGeneratorFor(sqInt aQuickPrimitiveIndex))(void) +{ + switch (aQuickPrimitiveIndex) { + case 0x100: + return genQuickReturnSelf; + + case 0x101: + case 258: + case 259: + case 260: + case 261: + case 262: + case 263: + return genQuickReturnConst; + + default: + return genQuickReturnInstVar; + } + return 0; +} + + /* CoInterpreter>>#quickPrimitiveInstVarIndexFor: */ +sqInt +quickPrimitiveInstVarIndexFor(sqInt primIndex) +{ + return primIndex - 264; +} + + /* CoInterpreter>>#rawHeaderOf: */ +sqInt +rawHeaderOf(sqInt methodPointer) +{ + return longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); +} + + +/* Since methods may be updated while forwarding during become, make the + assert accomodate this. + */ + + /* CoInterpreter>>#rawHeaderOf:put: */ +void +rawHeaderOfput(sqInt methodOop, sqInt cogMethodOrMethodHeader) +{ + assert(isCompiledMethodHeader(headerWhileForwardingOf(methodOop))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(methodOop)) + && (!(isForwarded(methodOop)))); + assert(validStorePointerUncheckedArgs(HeaderIndex, methodOop, cogMethodOrMethodHeader)); + longAtput((void *)((methodOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))),cogMethodOrMethodHeader); +} + + +/* Read an image from the given file stream, allocating an amount of memory + to its object heap. + + V3: desiredHeapSize is the total size of the heap. Fail if the image has + an unknown format or + requires more than the specified amount of memory. + + Spur: desiredHeapSize is ignored; this routine will attempt to provide at + least extraVMMemory's + ammount of free space after the image is loaded, taking any free space in + the image into account. + extraVMMemory is stored in the image header and is accessible as + vmParameterAt: 23. If + extraVMMemory is 0, the value defaults to the default grow headroom. Fail + if the image has an + unknown format or if sufficient memory cannot be allocated. + + Details: This method detects when the image was stored on a machine with + the opposite byte + ordering from this machine and swaps the bytes automatically. Furthermore, + it allows the header + information to start 512 bytes into the file, since some file transfer + programs for the Macintosh + apparently prepend a Mac-specific header of this size. Note that this same + 512 bytes of prefix + area could also be used to store an exec command on Unix systems, allowing + one to launch + Smalltalk by invoking the image name as a command. */ + + /* CoInterpreter>>#readImageFromFile:HeapSize:StartingAt: */ +size_t +readImageFromFileHeapSizeStartingAt(sqImageFile f, usqInt desiredHeapSize, squeakFileOffsetType imageOffset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt allocationReserve; + sqInt anObject; + sqInt bit; + sqInt bytes; + sqInt bytesRead; + sqInt bytesToShift; + sqInt classArrayClass; + sqInt classArrayObj; + sqInt cogCodeBase; + size_t dataSize; + sqInt firstSegSize; + sqInt freeOldSpaceInImage; + usqInt hdrCogCodeSize; + sqInt hdrEdenBytes; + usqInt hdrMaxExtSemTabSize; + usqInt hdrNumStackPages; + sqInt headerSize; + squeakFileOffsetType headerStart; + sqInt headroom; + sqInt headroomSqInt; + usqInt heapSize; + usqInt heapSizeUsqInt; + sqInt i; + usqInt mem; + usqInt minimumMemory; + usqInt numSlots; + sqInt objOop; + sqInt oop; + sqInt rawVersion; + sqInt seed; + int swapBytes; + sqInt toDoLimit; + sqInt toDoLimit1; + sqInt version; + int w; + unsigned short w1; + sqInt wSqInt; + + rawVersion = 0; + + /* stdout is not available at compile time. this is the earliest available point. */ + GIV(transcript) = stdout; + + /* guess Metaclass instSize */ + GIV(metaclassNumSlots) = 6; + + /* guess (Class instVarIndexFor: 'name' ifAbsent: []) - 1 */ + GIV(classNameIndex) = 6; + version = checkImageVersionFromstartingAtassignRawVersion(f, imageOffset, (&rawVersion)); + if (!version) { + /* begin bailOutOfImageLoad: */ + fprintf(stderr, + "This ver %d vm cannot read ver %" PRIdSQINT " image file %s\n", + ((int) 68021 /* imageFormatVersion */), + rawVersion, + getImageName()); + } + swapBytes = rawVersion != version; + GIV(multipleBytecodeSetsActive) = ((version & MultipleBytecodeSetsBitmask) != 0); + + /* record header start position */ + headerStart = (sqImageFilePosition(f)) - 4; + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + headerSize = (swapBytes + ? SQ_SWAP_4_BYTES(w) + : w); + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + dataSize = ((sqInt) ((swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt))); + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + GIV(oldImageBaseAddress) = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + anObject = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin specialObjectsOop: */ + GIV(specialObjectsOop) = anObject; + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + seed = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin lastHash: */ + if (!((GIV(lastHash) = seed & (identityHashHalfWordMask())))) { + while (((GIV(lastHash) = (((usqInt) (ioUTCMicrosecondsNow()))) & (identityHashHalfWordMask()))) == 0) { + } + } + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + GIV(savedWindowSize) = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + imageHeaderFlags = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + extraVMMemory = (swapBytes + ? SQ_SWAP_4_BYTES(w) + : w); + + /* begin getShortFromFile:swap: */ + w1 = 0; + sqImageFileRead((&w1), sizeof(unsigned short), 1, f); + hdrNumStackPages = (swapBytes + ? ((((usqInt)(w1)) >> 8) & 0xFF) | (((w1 & 0xFF) << 8)) + : w1); + + /* 4 stack pages is small. Should be able to run with as few as + three. 4 should be comfortable but slow. 8 is a reasonable + default. Can be changed via vmParameterAt: 43 put: n. + Can be set as a preference (Info.plist, VM.ini, command line etc). + If desiredNumStackPages is already non-zero then it has been + set as a preference. Ignore (but preserve) the header's default. */ + GIV(numStackPages) = (desiredNumStackPages + ? desiredNumStackPages + : (hdrNumStackPages + ? hdrNumStackPages + : defaultNumStackPages())); + desiredNumStackPages = hdrNumStackPages; + + /* This slot holds the size of the native method zone in 1k units. (pad to word boundary). */ + hdrCogCodeSize = (getShortFromFileswap(f, swapBytes)) * 0x400; + + /* set for vmParameter 47 */ + GIV(cogCodeSize) = (desiredCogCodeSize + ? desiredCogCodeSize + : (hdrCogCodeSize + ? (desiredCogCodeSize = hdrCogCodeSize) + : defaultCogCodeSize())); + if (GIV(cogCodeSize) > (maxCogCodeSize())) { + GIV(cogCodeSize) = maxCogCodeSize(); + } + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + hdrEdenBytes = (swapBytes + ? SQ_SWAP_4_BYTES(w) + : w); + bytes = (desiredEdenBytes + ? desiredEdenBytes + : (hdrEdenBytes + ? hdrEdenBytes + : defaultEdenBytes())); + + /* begin edenBytes: */ + GIV(edenBytes) = bytes; + desiredEdenBytes = hdrEdenBytes; + + /* begin getShortFromFile:swap: */ + w1 = 0; + sqImageFileRead((&w1), sizeof(unsigned short), 1, f); + hdrMaxExtSemTabSize = (swapBytes + ? ((((usqInt)(w1)) >> 8) & 0xFF) | (((w1 & 0xFF) << 8)) + : w1); + if (hdrMaxExtSemTabSize) { + /* begin setMaxExtSemSizeTo: */ + GIV(maxExtSemTabSizeSet) = 1; + ioSetMaxExtSemTableSize(hdrMaxExtSemTabSize); + } + + /* begin getShortFromFile:swap: */ + w1 = 0; + sqImageFileRead((&w1), sizeof(unsigned short), 1, f); + GIV(the2ndUnknownShort) = (swapBytes + ? ((((usqInt)(w1)) >> 8) & 0xFF) | (((w1 & 0xFF) << 8)) + : w1); + + /* Set maxLiteralCountForCompile unless it has already been set on the command line. */ + if (maxLiteralCountForCompile < 0) { + maxLiteralCountForCompile = (GIV(the2ndUnknownShort) + ? GIV(the2ndUnknownShort) + : MaxLiteralCountForCompile); + } + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + firstSegSize = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin firstSegmentSize: */ + GIV(firstSegmentSize) = firstSegSize; + if (!(((imageHeaderFlags & 4) != 0))) { + error("Process must have the threadAffinity inst var to thread"); + } + + /* compare memory requirements with availability */ + allocationReserve = interpreterAllocationReserveBytes(); + minimumMemory = ((GIV(cogCodeSize) + dataSize) + GIV(edenBytes)) + allocationReserve; + + /* no need to include the stackZone; this is alloca'ed + Compute how much space is needed for the initial heap allocation. + no need to include the stackZone; this is alloca'ed. + no need to include the JIT code zone size; this is allocated separately. */ + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + freeOldSpaceInImage = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin initialHeadroom:givenFreeOldSpaceInImage: */ + headroomSqInt = (extraVMMemory + ? extraVMMemory + : (!(GIV(growHeadroom)) + ? 0x1000000 + : GIV(growHeadroom))); + if (freeOldSpaceInImage >= headroomSqInt) { + headroom = 0; + goto l1; + } + if (freeOldSpaceInImage >= ((headroomSqInt * 7) / 8)) { + headroom = headroomSqInt / 8; + goto l1; + } + if (freeOldSpaceInImage >= ((headroomSqInt * 3) / 4)) { + headroom = headroomSqInt / 4; + goto l1; + } + if (freeOldSpaceInImage >= ((headroomSqInt * 5) / 8)) { + headroom = (headroomSqInt * 3) / 8; + goto l1; + } + if (freeOldSpaceInImage >= (headroomSqInt / 2)) { + headroom = headroomSqInt / 2; + goto l1; + } + headroom = headroomSqInt; + /* end initialHeadroom:givenFreeOldSpaceInImage: */ +l1: + heapSizeUsqInt = ((dataSize + headroom) + GIV(edenBytes)) + ((headroom > allocationReserve + ? 0 + : allocationReserve)); + + /* begin roundUpHeapSize: */ + bit = (((highBit(heapSizeUsqInt)) - 1) * 3) / 4; + heapSize = (((heapSizeUsqInt & ((1ULL << bit) - 1)) != 0) + ? (((heapSizeUsqInt | ((1ULL << bit) - 1)) - ((1ULL << bit) - 1))) + (1ULL << bit) + : heapSizeUsqInt); + + /* allocateJITMemory will assign the actual size allocated, which is rounded up to a page boundary. */ + cogCodeBase = ((sqInt)(allocateJITMemory((&GIV(cogCodeSize))))); + + /* allocate a contiguous block of memory for the Squeak heap and ancilliary data structures */ + if ((mem = ((usqInt)(allocateMemoryMinimumImageFileHeaderSize(heapSize, minimumMemory, f, headerSize))))) { + /* cannot clash with the variable memory still in use in NewCoObjectMemory and superclasses */ + setHeapBasememoryLimitendOfMemory((heapBase = mem), mem + heapSize, mem + dataSize); + } + else { + insufficientMemoryAvailableError(); + } + + /* position file after the header */ + sqImageFileSeek(f, headerStart + headerSize); + + /* read in the image in bulk, then swap the bytes if necessary */ + bytesRead = readHeapFromImageFiledataBytes(f, dataSize); + if (bytesRead != dataSize) { + unableToReadImageError(); + } + ensureImageFormatIsUpToDate(swapBytes); + + /* compute difference between old and new memory base addresses */ + bytesToShift = GIV(oldSpaceStart) - GIV(oldImageBaseAddress); + + /* begin initializeInterpreter: */ + interpreterProxy = sqGetInterpreterProxy(); + (&interpreterProxy); + initializeObjectMemory(bytesToShift); + + /* begin checkAssumedCompactClasses */ + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassArrayCompactIndex >= 1) && (ClassArrayCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassArrayCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("Array"); + } + + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassLargeNegativeInteger) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassLargeNegativeIntegerCompactIndex >= 1) && (ClassLargeNegativeIntegerCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassLargeNegativeIntegerCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("LargeNegativeInteger"); + } + + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassLargePositiveInteger) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassLargePositiveIntegerCompactIndex >= 1) && (ClassLargePositiveIntegerCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassLargePositiveIntegerCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("LargePositiveInteger"); + } + + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassFloat) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassFloatCompactIndex >= 1) && (ClassFloatCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassFloatCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("Float"); + } + + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassBlockClosure) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassBlockClosureCompactIndex >= 1) && (ClassBlockClosureCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassBlockClosureCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("BlockClosure"); + } + + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassMethodContext) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassMethodContextCompactIndex >= 1) && (ClassMethodContextCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassMethodContextCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("MethodContext"); + } + objOop = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteArray) << (shiftForWord())))))); + + /* begin compactIndexOfClass: */ + assert((rawHashBitsOf(objOop)) != 0); + GIV(classByteArrayCompactIndex) = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()); + + /* begin initializeExtraClassInstVarIndices */ + classArrayObj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))); + classArrayClass = fetchClassOfNonImm(classArrayObj); + + /* begin numSlotsOf: */ + assert((classIndexOf(classArrayClass)) > (isForwardedObjectClassIndexPun())); + + /* determine actual Metaclass instSize */ + GIV(metaclassNumSlots) = (((numSlots = byteAt((void *)(classArrayClass + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classArrayClass - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + + /* default */ + GIV(thisClassIndex) = 5; + /* begin numSlotsOf: */ + assert((classIndexOf(classArrayClass)) > (isForwardedObjectClassIndexPun())); + if (((numSlots = byteAt((void *)(classArrayClass + (numSlotsFieldByteOffset()))))) == (numSlotsMask())) { + toDoLimit = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classArrayClass - BaseHeaderSize)))) << 8)))))) >> 8; + } + else { + toDoLimit = numSlots; + } + for (i = (InstanceSpecificationIndex + 1); i <= toDoLimit; i += 1) { + if ((longAt((void *)((classArrayClass + BaseHeaderSize) + ((((usqInt)((i - 1)) << (shiftForWord()))))))) == classArrayObj) { + GIV(thisClassIndex) = i - 1; + } + } + + /* default */ + GIV(classNameIndex) = 6; + /* begin numSlotsOf: */ + assert((classIndexOf(classArrayObj)) > (isForwardedObjectClassIndexPun())); + if (((numSlots = byteAt((void *)(classArrayObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask())) { + toDoLimit1 = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classArrayObj - BaseHeaderSize)))) << 8)))))) >> 8; + } + else { + toDoLimit1 = numSlots; + } + for (i = (InstanceSpecificationIndex + 1); i <= toDoLimit1; i += 1) { + oop = longAt((void *)((classArrayObj + BaseHeaderSize) + ((((usqInt)((i - 1)) << (shiftForWord())))))); + if (objectequalsString(oop, "Array")) { + GIV(classNameIndex) = i - 1; + } + } + GIV(method) = (GIV(newMethod) = GIV(nilObj)); + GIV(methodDictLinearSearchLimit) = 8; + + /* begin initialCleanup */ + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + + /* begin flushExternalPrimitiveTable */ + memset(externalPrimitiveTable, 0, MaxExternalPrimitiveTableSize * (sizeof(externalPrimitiveTable[0]))); + GIV(externalPrimitiveTableFirstFreeIndex) = 0; + + /* cmd-. as used for Mac but no other OS */ + GIV(interruptKeycode) = ((0x800)) + (((sqInt)'.')); + while (GIV(globalSessionID) == 0) { + GIV(globalSessionID) = ((time(NULL)) + (ioMSecs())) & 0x7FFFFFFF; + } + GIV(metaAccessorDepth) = -2; + sHEAFn = ioLoadFunctionFrom("secHasEnvironmentAccess", "SecurityPlugin"); + if (!((lengthOf(GIV(specialObjectsOop))) > ForeignCallbackProcess)) { + error("The image is unsuitable for the MT VM because specialObjectsArray is too short to contain the foreign callback process."); + } + atomic_store((&GIV(maxWaitingPriority)), 0); + initializeCodeZoneFromupTo(cogCodeBase, cogCodeBase + GIV(cogCodeSize)); + return dataSize; +} + + +/* This is a little elaborate. The primTraceLog is only useful if it is not + full of noise. + To reduce noise when debugging a specific plugin we allow a plugin name to + be specified and will only generate the primTraceLog code for primitives + in that plugin. */ + + /* CoInterpreter>>#recordFastCCallPrimTraceForMethod: */ +sqInt +recordFastCCallPrimTraceForMethod(sqInt aMethodObj) +{ + return (recordFastCCallPrimTrace()) + && ((primTracePluginName + ? methodHasPrimitiveInPrimTracePlugin(aMethodObj) + : 1)); +} + + +/* This is a little elaborate. The primTraceLog is only useful if it is not + full of noise. + To reduce noise when debugging a specific plugin we allow a plugin name to + be specified and will only generate the primTraceLog code for primitives + in that plugin. */ + + /* CoInterpreter>>#recordPrimTraceForMethod: */ +sqInt +recordPrimTraceForMethod(sqInt aMethodObj) +{ + return (recordPrimTrace()) + && ((primTracePluginName + ? methodHasPrimitiveInPrimTracePlugin(aMethodObj) + : 1)); +} + + +/* noop in the StackInterpreter */ + + /* CoInterpreter>>#reloadPrimitiveCalloutPointer */ +static NeverInline void +reloadPrimitiveCalloutPointer(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + char *moduleName; + + primitiveCalloutPointer = ioLoadFunctionFrom("primitiveCallout", "SqueakFFIPrims"); + + /* Find out whether SqueakFFIPrims is internal or external */ + GIV(primCalloutIsExternal) = 1; + index = 1; + do { + if ((moduleName = ioListBuiltinModule(index))) { + if (!(strcmp(moduleName, "SqueakFFIPrims"))) { + GIV(primCalloutIsExternal) = 0; + return; + } + } + else { + return; + } + index += 1; + } while(1); +} + + +/* Report the stack page size and minimum unused headroom to stdout. */ + + /* CoInterpreter>>#reportMinimumUnusedHeadroom */ +void +reportMinimumUnusedHeadroom(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fprintf(GIV(transcript), + "stack page bytes %" PRIdSQINT " available headroom %" PRIdSQINT " minimum unused headroom %" PRIdSQINT "\n", + stackPageByteSize(), + ((stackPageByteSize()) - ((((stackPageFrameBytes()) < (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))) ? (stackPageFrameBytes()) : (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))))) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord), + minimumUnusedHeadroom()); +} + + +/* Report the stack page size and minimum unused headroom to a stream. */ + + /* CoInterpreter>>#reportMinimumUnusedHeadroomOn: */ +void +reportMinimumUnusedHeadroomOn(FILE *aStdioStream) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + FILE *savedTranscript; + + /* begin withRedirectedOutputTo:do: */ + savedTranscript = GIV(transcript); + GIV(transcript) = (aStdioStream + ? aStdioStream + : stdout); + + /* begin reportMinimumUnusedHeadroom */ + fprintf(GIV(transcript), + "stack page bytes %" PRIdSQINT " available headroom %" PRIdSQINT " minimum unused headroom %" PRIdSQINT "\n", + stackPageByteSize(), + ((stackPageByteSize()) - ((((stackPageFrameBytes()) < (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))) ? (stackPageFrameBytes()) : (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))))) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord), + minimumUnusedHeadroom()); + GIV(transcript) = savedTranscript; +} + + +/* Return to the current frame, either by entering machine code, or + longjmp-ing back to the + interpreter or simply returning, depending on where we are. To know + whether to return or + enter machine code we have to know from whence we came. We could have come + from the interpreter, either directly or via a machine code primitive. We + could have come from + machine code. The instructionPointer tells us where from. If it is above + startOfMemory we're + in the interpreter. If it is below, then we are in machine-code unless it + is ceReturnToInterpreterPC, + in which case we're in a machine-code primitive called from the + interpreter. */ + + /* CoInterpreter>>#returnToExecutive:postContextSwitch: */ +static NoDbgRegParms sqInt +returnToExecutivepostContextSwitch(sqInt inInterpreter, sqInt switchedContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + CogBlockMethod *cogMethod; + sqInt fullyInInterpreter; + sqInt header; + sqInt methodHeader; + sqInt retValue; + + assertCStackWellAligned(); + if ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())) { + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + + /* If returning after a context switch then a result may have to be popped from the stack. + If the process is suspended at a send then the result of the primitive in which the + process was suspended is still on the stack and must be popped into ReceiverResultReg. + If not, nothing should be popped and ReceiverResultReg gets the receiver. */ + if (switchedContext) { + /* begin mframeCogMethod: */ + cogMethod = ((CogBlockMethod *) ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodMask)); + assert((GIV(instructionPointer) > (minCogMethodAddress())) + && (GIV(instructionPointer) < (maxCogMethodAddress()))); + if ((GIV(instructionPointer) != ((((sqInt)cogMethod)) + ((cogMethod->stackCheckOffset)))) + && (isSendReturnPC(GIV(instructionPointer)))) { + assert(addressCouldBeOop(stackTop())); + + /* begin popStack */ + retValue = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + } + else { + retValue = longAt(GIV(framePointer) + FoxMFReceiver); + } + } + else { + retValue = longAt(GIV(framePointer) + FoxMFReceiver); + } + assert(isPCWithinMethodZone(GIV(instructionPointer))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),retValue); + ceEnterCogCodePopReceiverReg(); + } + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + fullyInInterpreter = inInterpreter; + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + fullyInInterpreter = 0; + } + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 1, __LINE__); + if (!fullyInInterpreter) { + ceInvokeInterpret(); + } + return null; +} + + +/* We have made a context switch, either when interpreting or from machine + code. Effectively return to the current frame, either by entering machine + code, or + longjmp-ing back to the interpreter or simply returning, depending on + where we are. */ + + /* CoInterpreter>>#return:toExecutive: */ +static NoDbgRegParms sqInt +returntoExecutive(sqInt returnValue, sqInt inInterpreter) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + sqInt header; + sqInt methodHeader; + char *sp; + + assertCStackWellAligned(); + if ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())) { + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + assert(isPCWithinMethodZone(GIV(instructionPointer))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + ceEnterCogCodePopReceiverReg(); + } + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 1, __LINE__); + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + if (inInterpreter) { + return null; + } + ceInvokeInterpret(); + + /* NOTREACHED */ + return null; +} + + /* CoInterpreter>>#shortPrintFrame: */ +static NoDbgRegParms sqInt +shortPrintFrame(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + sqInt mthd; + sqInt rcvr; + + if (!(/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "invalid frame pointer"); + cr(); + return null; + } + rcvr = /* frameReceiver: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? longAt(theFP + FoxMFReceiver) + : longAt(theFP + FoxIFReceiver)); + mthd = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + printHexPtrnp(theFP); + + /* begin space */ + printChar(' '); + printChar(((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? 'M' + : 'I')); + + /* begin space */ + printChar(' '); + printActivationNameForreceiverisBlockfirstTemporary(mthd, rcvr, /* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0), /* temporary:in: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (0 < ((frameNumArgs = ((mframeCogMethod(theFP))->cmNumArgs))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgs) * BytesPerWord)) + : longAt(((theFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs) * BytesPerWord))) + : /* itemporary:in: */ + (0 < ((frameNumArgsUsqInt = byteAt((theFP + FoxIFrameFlags) + 1))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt) * BytesPerWord)) + : longAt(((theFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt) * BytesPerWord))))); + + /* begin space */ + printChar(' '); + shortPrintOop(rcvr); + return 0; +} + + +/* Answer a full 32 bit integer object for the given integer value. + N.B. Returning in each arm separately enables Slang inlining. + /Don't/ return the ifTrue:ifFalse: unless Slang inlining of conditionals + is fixed. */ + + /* CoInterpreter>>#signed32BitIntegerFor: */ +sqInt +signed32BitIntegerFor(sqInt integerValue) +{ + return (((usqInt)(((int) integerValue)) << 3) | 1); +} + + +/* Convert the given object into an integer value. + The object may be either a SmallInteger or a four-byte LargeInteger. */ + + /* CoInterpreter>>#signed32BitValueOf: */ +int +signed32BitValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt value64; + + if ((((oop) & 7) == 1)) { + value64 = (oop >> 3); + if ((((int) value64)) != value64) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + value64 = 0; + } + return value64; + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; +} + + +/* Answer a Large Integer object for the given integer value. N.B. will *not* + cause a GC. */ + + /* CoInterpreter>>#signed64BitIntegerFor: */ +sqInt +signed64BitIntegerFor(sqLong integerValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt largeClass; + usqLong magnitude; + usqInt newLargeInteger; + usqInt newObj; + usqInt numBytes; + sqInt numSlots; + sqInt objFormat; + sqInt sz; + + if (integerValue < 0) { + if (integerValue >= (MinSmallInteger)) { + return (((usqInt)(((sqInt)integerValue)) << 3) | 1); + } + largeClass = ClassLargeNegativeIntegerCompactIndex; + magnitude = 0 - (((usqLong) integerValue)); + } + else { + if (integerValue <= (MaxSmallInteger)) { + return (((usqInt)(((sqInt)integerValue)) << 3) | 1); + } + largeClass = ClassLargePositiveIntegerCompactIndex; + magnitude = integerValue; + } + sz = 8; + objFormat = (firstByteFormat()) + ((8 - sz) & (BytesPerWord - 1)); + numSlots = (sz + 3) / BytesPerOop; + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((numSlots >= 0) + && ((largeClass != 0) + && ((knownClassAtIndex(largeClass)) != GIV(nilObj)))); + assert(((objFormat < (firstByteFormat()) + ? objFormat + : objFormat & (byteFormatMask()))) == (instSpecOfClass(knownClassAtIndex(largeClass)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newLargeInteger = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + largeClass); + GIV(freeStart) += numBytes; + newLargeInteger = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + + /* Memory is eight byte aligned in SPUR, so we are sure to have room for 64bits word whatever allocated sz */ + + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((newLargeInteger + BaseHeaderSize)),SQ_SWAP_8_BYTES_IF_BIGENDIAN(magnitude)); + return newLargeInteger; +} + + +/* Convert the given object into an integer value. + The object may be either a positive SmallInteger or a eight-byte + LargeInteger. + */ + + /* CoInterpreter>>#signed64BitValueOf: */ +sqLong +signed64BitValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + sqInt fmt; + usqLong magnitude; + sqInt negative; + usqInt numSlots; + int ok; + sqInt sz; + + if ((((oop) & 7) == 1)) { + return ((sqLong) ((oop >> 3))); + } + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (ok) { + negative = 0; + } + else { + negative = 1; + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargeNegativeIntegerCompactIndex == ccIndex; + if (!ok) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + sz = ((((/* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (sz > 4) { + magnitude = SQ_SWAP_8_BYTES_IF_BIGENDIAN((long64At((void *)((oop + BaseHeaderSize))))); + if ((sz > (sizeof(sqLong))) + || ((negative + ? magnitude > 0x8000000000000000ULL + : magnitude >= 0x8000000000000000ULL))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + } + else { + magnitude = ((unsigned int) (SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((oop + BaseHeaderSize))))))); + } + return (negative + ? 0 - magnitude + : magnitude); +} + + +/* */ + + /* CoInterpreter>>#specialSelectorNumArgs: */ +sqInt +specialSelectorNumArgs(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((fetchPointerofObject((index * 2) + 1, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))))) >> 3); +} + + /* CoInterpreter>>#stackLimitAddress */ +usqInt +stackLimitAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(stackLimit)))); +} + + +/* Answer the amount of slots needed to fit a new frame at the point the + stack limit is checked. A frame looks like this at the point the stack + limit is checked: + stacked receiver/closure + arg0 + ... + argN + caller's method ip/base frame's sender context + fp-> saved fp + method + context (uninitialized?) + method header fields (interpreter only) + saved method ip (uninitialized?; interpreter only) + receiver + first temp + ... + sp-> Nth temp + So the amount of headroom is + the maximum number of arguments + 1 (for stacked receiver and arguments) + + the frame size + + the max number of temps. + Since a method's number of temps includes its arguments the actual offset + is: */ + + /* CoInterpreter>>#stackLimitOffset */ +static sqInt +stackLimitOffset(void) +{ + return ((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord; +} + + +/* Return a minimum amount of headroom for each stack page (in bytes). + In the interpreter we don't actually need any headroom. In a JIT the stack + has to have room for interrupt handlers which will run on the stack. + Defer to the platform for this one. */ + + /* CoInterpreter>>#stackPageHeadroom */ +static sqInt +stackPageHeadroom(void) +{ + return osCogStackPageHeadroom(); +} + + /* CoInterpreter>>#stackPointerAddress */ +usqInt +stackPointerAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(stackPointer)))); +} + + +/* Return the 1-based index rel to the given frame. In the StackInterpreter + stacks grow down. + */ + + /* CoInterpreter>>#stackPointerIndexForFrame:WithSP: */ +static NoDbgRegParms sqInt +stackPointerIndexForFrameWithSP(char *theFP, char *theSP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (((usqInt)(((theFP + FoxMFReceiver) - theSP))) >> (shiftForWord())) + (((mframeCogMethod(theFP))->cmNumArgs)) + : (((usqInt)(((theFP + FoxIFReceiver) - theSP))) >> (shiftForWord())) + (byteAt((theFP + FoxIFrameFlags) + 1))); +} + + +/* Zero-relative version of CompiledMethod>>startpc. */ + + /* CoInterpreter>>#startPCOfMethodHeader: */ +usqInt +startPCOfMethodHeader(sqInt aCompiledMethodHeader) +{ + return (((/* begin literalCountOfMethodHeader: */ + assert((((aCompiledMethodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((aCompiledMethodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop; +} + + +/* Answer the startPC of lit if it is a (clean) block in aMethodObj, + otherwise answer nil. + */ + + /* CoInterpreter>>#startPCOrNilOfLiteral:in: */ +sqInt +startPCOrNilOfLiteralin(sqInt lit, sqInt aMethodObj) +{ + usqInt numSlots; + sqInt oop; + sqInt outerContext; + + if (!(((!(lit & (tagMask())))) + && ((((byteAt((void *)(lit + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat())) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(lit)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(lit + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(lit - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= ClosureFirstCopiedValueIndex)))) { + return null; + } + outerContext = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + if (!(/* isContext: */ + ((!(outerContext & (tagMask())))) + && (((longAt((void *)(outerContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + return null; + } + if (aMethodObj != (longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))))) { + return null; + } + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + return (oop >> 3); +} + + +/* Handle the cannot return response for a base frame return to an invalid + context. Build a new base frame for the context in the cannot resume state + ready for the + send of cannotReturn:. + + Since we have returned from the base frame of the page the context is + effectively widowed. + But its sender needs to be contextToReturnTo, and its pc needs to be the + HasBeenReturnedFromMCPC marker. So bereave it (as a side-effect of + isWidowedContext:), assign contextToReturnTo to + sender, and rebuild its frame, which will have the ceCannotResumePC as its + pc. Finally push + returnValue and set instructionPointer to ceCannotResumePC in preparation + for the send. */ + + /* CoInterpreter>>#tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFrom:to:returnValue: */ +static NoDbgRegParms void +tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFromtoreturnValue(sqInt contextToReturnFrom, sqInt contextToReturnTo, sqInt returnValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *newPage; + + assert((GIV(stackPage)) + && (isFree(GIV(stackPage)))); + isWidowedContext(contextToReturnFrom); + assert(!(isMarriedOrWidowedContext(contextToReturnFrom))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SenderIndex, contextToReturnFrom, contextToReturnTo)); + assert(isNonImmediate(contextToReturnFrom)); + if (oopisGreaterThanOrEqualTo(contextToReturnFrom, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(contextToReturnTo & (tagMask())))) + && (oopisLessThan(contextToReturnTo, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(contextToReturnFrom + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(contextToReturnFrom); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((contextToReturnFrom + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),contextToReturnTo); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(InstructionPointerIndex, contextToReturnFrom, HasBeenReturnedFromMCPCOop)); + assert(isNonImmediate(contextToReturnFrom)); + if (oopisGreaterThanOrEqualTo(contextToReturnFrom, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(HasBeenReturnedFromMCPCOop & (tagMask())))) + && (oopisLessThan(HasBeenReturnedFromMCPCOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(contextToReturnFrom + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(contextToReturnFrom); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((contextToReturnFrom + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),HasBeenReturnedFromMCPCOop); + + /* void the instructionPointer to stop it being incorrectly updated in a code + compaction in makeBaseFrameFor:. */ + GIV(instructionPointer) = 0; + newPage = makeBaseFrameFor(contextToReturnFrom); + assert(GIV(stackPage) == newPage); + + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (newPage->headSP); + GIV(framePointer) = (newPage->headFP); + assert((stackTop()) == (ceCannotResumePC())); + + /* overwrite the ceSendCannotResumePC on the stack. If ever re-executed + the returnValue will be taken from top-of-stack by ceCannotResume. */ + + /* stackTopPut: */ + longAtput(GIV(stackPointer),returnValue); + + /* Assign it to instructionPointer as externalCannotReturn:from: pushes it. */ + GIV(instructionPointer) = ceCannotResumePC(); +} + + +/* See StackInterpreter class>>initializeFrameIndices */ + + /* CoInterpreter>>#temporary:in: */ +static NoDbgRegParms sqInt +temporaryin(sqInt offset, char *theFP) +{ + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (offset < ((frameNumArgs = ((mframeCogMethod(theFP))->cmNumArgs))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgs - offset) * BytesPerWord)) + : longAt(((theFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs - offset) * BytesPerWord))) + : /* itemporary:in: */ + (offset < ((frameNumArgsUsqInt = byteAt((theFP + FoxIFrameFlags) + 1))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt - offset) * BytesPerWord)) + : longAt(((theFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt - offset) * BytesPerWord)))); +} + + /* CoInterpreter>>#temporary:in:put: */ +static NoDbgRegParms sqInt +temporaryinput(sqInt offset, char *theFP, sqInt valueOop) +{ + usqInt frameNumArgs; + unsigned char frameNumArgs1; + + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? longAtput((offset < ((frameNumArgs1 = ((mframeCogMethod(theFP))->cmNumArgs))) + ? (theFP + FoxCallerSavedIP) + ((frameNumArgs1 - offset) * BytesPerWord) + : ((theFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs1 - offset) * BytesPerWord)),valueOop) + : /* itemporary:in:put: */ + (offset < ((frameNumArgs = byteAt((theFP + FoxIFrameFlags) + 1))) + ? longAtput((theFP + FoxCallerSavedIP) + ((frameNumArgs - offset) * BytesPerWord),valueOop) + : longAtput(((theFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - offset) * BytesPerWord),valueOop))); +} + + +/* Go through all frames in the stack zone and mark their methods + so that compaction does not free any methods that are in use. */ +/* If instructionPointer is referring to machine code, as it will be if a + primitive is in progress + (see isCodeCompactingPrimitiveIndex:) it must be updated if it is + referring to a moved + method. */ + + /* CoInterpreter>>#updateStackZoneReferencesToCompiledCodePreCompaction */ +void +updateStackZoneReferencesToCompiledCodePreCompaction(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt i; + CogMethod *primCogMethod; + usqInt theFlags; + char *theFP; + usqInt theIP; + char *theIPPtr; + CogMethod *theMethod; + sqInt theMethodField; + StackPage *thePage; + + if (GIV(instructionPointer)) { + if ((primCogMethod = cogMethodContaining(GIV(instructionPointer)))) { + GIV(instructionPointer) += (primCogMethod->objectHeader); + } + } + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + /* begin updateStackZoneReferencesToCompiledCodePreCompactionOnPage: */ + callerFP = ((char *) 0); + theIPPtr = (thePage->headSP); + theFP = (thePage->headFP); + while (1) { + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + theMethodField = longAt(theFP + FoxMethod); + theFlags = theMethodField & MFMethodFlagsMask; + theMethod = ((CogMethod *) (theMethodField - theFlags)); + if (((theMethod->cmType)) == CMBlock) { + /* cmHomeMethod */ + if ((((((CogBlockMethod *) (theMethodField - theFlags)))->cmType)) >= CMMethod) { + theMethod = ((CogMethod *) (((CogBlockMethod *) (theMethodField - theFlags)))); + } + else { + assert(CMBlock == ((((CogBlockMethod *) (theMethodField - theFlags)))->cmType)); + theMethod = ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (theMethodField - theFlags))))) - (((((CogBlockMethod *) (theMethodField - theFlags)))->homeOffset)))); + } + } + theIP = ((usqInt)(longAt(theIPPtr))); + if ((theIP != (ceCannotResumePC())) + && (asserta((theIP >= (((usqInt)theMethod))) + && (theIP < ((((usqInt)theMethod)) + ((theMethod->blockSize))))))) { + longAtput(theIPPtr,theIP + ((theMethod->objectHeader))); + } + longAtput(theFP + FoxMethod,theMethodField + ((theMethod->objectHeader))); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theIPPtr = theFP + FoxCallerSavedIP; + theFP = callerFP; + } + } + } +} + + +/* Update the frame's spouse context with the frame's current state except + for the + sender and instruction pointer, which are used to mark the context as + married, and the receiver and method, which are already set. */ + + /* CoInterpreter>>#updateStateOfSpouseContextForFrame:WithSP: */ +static NoDbgRegParms void +updateStateOfSpouseContextForFrameWithSP(char *theFP, char *theSP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *argsPointer; + sqInt fieldIndex; + sqInt i; + char *pointer; + usqInt tempIndex; + sqInt theContext; + sqInt valuePointer; + + assert(frameHasContext(theFP)); + theContext = longAt(theFP + FoxThisContext); + assert(isContext(theContext)); + assert(frameReceiver(theFP) == noFixupFollowFieldofObject(ReceiverIndex, theContext)); + assert(frameMethodObject(theFP) == noFixupFollowFieldofObject(MethodIndex, theContext)); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + tempIndex = ((mframeCogMethod(theFP))->cmNumArgs); + pointer = (theFP + FoxMFReceiver) - BytesPerWord; + } + else { + tempIndex = byteAt((theFP + FoxIFrameFlags) + 1); + pointer = (theFP + FoxIFReceiver) - BytesPerWord; + } + + /* update the arguments. this would appear not to be strictly necessary, but is for two reasons. + First, the fact that arguments are read-only is only as convention in the Smalltalk compiler; + other languages may choose to modify arguments. + Second, the Squeak runUntilErrorOrReturnFrom: nightmare pops the stack top, which may, in + certain circumstances, be the last argument, and hence the last argument may not have been + stored into the context. */ + argsPointer = theFP + ((FoxCallerSavedIP + BytesPerWord) + ((tempIndex << (shiftForWord())))); + for (i = 1; i <= tempIndex; i += 1) { + argsPointer -= BytesPerWord; + assert(addressCouldBeOop(longAt(argsPointer))); + fieldIndex = ReceiverIndex + i; + valuePointer = longAt(argsPointer); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, theContext, valuePointer)); + assert(isNonImmediate(theContext)); + if (oopisGreaterThanOrEqualTo(theContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(theContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(theContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); + } + + /* now update the non-argument stack contents. */ + while (pointer >= theSP) { + assert(addressCouldBeOop(longAt(pointer))); + tempIndex += 1; + fieldIndex = ReceiverIndex + tempIndex; + valuePointer = longAt(pointer); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, theContext, valuePointer)); + assert(isNonImmediate(theContext)); + if (oopisGreaterThanOrEqualTo(theContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(theContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(theContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); + pointer -= BytesPerWord; + } + assert((ReceiverIndex + tempIndex) < (lengthOf(theContext))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(StackPointerIndex, theContext, ((tempIndex << 3) | 1))); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord()))))),((tempIndex << 3) | 1)); +} + + +/* Note that we accept anInstrPointer pointing to a callPrimitiveBytecode + at the start of a method that contains a primitive. This because methods + like Context(Part)>>reset have to be updated to skip the callPrimtiive + bytecode otherwise. */ +/* -1 for pre-increment in fetchNextBytecode */ + + /* CoInterpreter>>#validInstructionPointer:inMethod:framePointer: */ +sqInt +validInstructionPointerinMethodframePointer(usqInt instrPointer, usqInt aMethod, char *fp) +{ + CogMethod *cogMethod; + sqInt header; + usqInt theInstrPointer; + + if (instrPointer == (ceCannotResumePC())) { + return (((usqInt)(longAt(fp + FoxMethod)))) < (startOfMemory()); + } + if (instrPointer == (ceReturnToInterpreterPC())) { + if ((((usqInt)(longAt(fp + FoxMethod)))) < (startOfMemory())) { + return 0; + } + theInstrPointer = ((usqInt)(longAt(fp + FoxIFSavedIP))); + } + else { + theInstrPointer = instrPointer; + header = longAt((void *)((aMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + if (((/* begin isCogMethodReference: */ + assert(((((header) & 7) == 1)) + || (((((usqInt)header)) < (startOfMemory())) + && ((((usqInt)header)) >= (minCogMethodAddress())))), + /* isNonIntegerObject: */ + (!(header & (smallIntegerTag()))))) + && (theInstrPointer < (startOfMemory()))) { + cogMethod = ((CogMethod *) header); + return (theInstrPointer >= (header + (sizeof(CogMethod)))) + && (theInstrPointer < (header + ((cogMethod->blockSize)))); + } + } + return (theInstrPointer >= (((aMethod + (lastPointerOf(aMethod))) + BytesPerOop) - 1)) + && (theInstrPointer < (((aMethod + (numBytesOfBytes(aMethod))) + BaseHeaderSize) - 1)); +} + + +/* Check that the base frame in the stack page has a valid sender and saved + context. + */ + + /* CoInterpreter>>#validStackPageBaseFrame: */ +static NoDbgRegParms sqInt +validStackPageBaseFrame(StackPage *aPage) +{ + sqInt savedThisContext; + sqInt senderContextOrNil; + + senderContextOrNil = longAt((aPage->baseAddress)); + savedThisContext = longAt(((aPage->baseAddress)) - BytesPerWord); + if (!(asserta(((((aPage->baseFP)) + (frameStackedReceiverOffset((aPage->baseFP)))) + (2 * BytesPerWord)) == ((aPage->baseAddress))))) { + return 0; + } + if (!(asserta(addressCouldBeObj(senderContextOrNil)))) { + return 0; + } + if (!(asserta(addressCouldBeObj(savedThisContext)))) { + return 0; + } + if (!(asserta((senderContextOrNil == (nilObject())) + || (isContext(senderContextOrNil))))) { + return 0; + } + if (!(asserta(isContext(savedThisContext)))) { + return 0; + } + if (!(asserta((frameCallerContext((aPage->baseFP))) == senderContextOrNil))) { + return 0; + } + if (!(asserta((frameContext((aPage->baseFP))) == savedThisContext))) { + return 0; + } + return 1; +} + + +/* Make sure that all VM state that affects the heap contents is voided so + that the heap is + ready to be snapshotted. If flushExtPrims is true, flush references to + external primitives in methods. Answer the activeContext that should be + stored in the snapshot. */ +/* Make sure that all VM state that affects the heap contents is voided so + that the heap is + ready to be snapshotted. If flushExtPrims is true, flush references to + external primitives in methods. Answer the activeContext that should be + stored in the snapshot. */ + + /* CoInterpreter>>#voidVMStateForSnapshotFlushingExternalPrimitivesIf: */ +static NoDbgRegParms sqInt +voidVMStateForSnapshotFlushingExternalPrimitivesIf(sqInt flushExtPrims) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt address; + sqInt classIndex; + sqInt fmt; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* in case of code compactions. */ + GIV(instructionPointer) = 0; + activeContext = divorceAllFrames(); + + /* begin bereaveAllMarriedContextsForSnapshotFlushingExternalPrimitivesIf: */ + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + fmt = (byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask()); + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + makeContextSnapshotSafe(objSqInt); + } + + /* Clean out external functions from compiled methods */ + if (flushExtPrims + && (fmt >= (firstCompiledMethodFormat()))) { + flushExternalPrimitiveOf(objSqInt); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } + voidCogCompiledCode(); + return activeContext; +} + + +/* useful for VM debugging */ + + /* CoInterpreter>>#whereIs: */ +char * +whereIs(sqInt anOop) +{ + char *somewhere; + char *where; + + if ((somewhere = whereIsMaybeCodeThing(anOop))) { + return somewhere; + } + if ((where = whereIsMaybeHeapThing(anOop))) { + return where; + } + if ((where = whereIsMaybeStackThing(anOop))) { + return where; + } + return " is no where obvious"; +} + + +/* Either widow the context or map its pc to a bytecode one. + Used to implement primitiveVoidVMStateForMethod. */ +/* for debugging & saving space */ + + /* CoInterpreter>>#widowOrForceToBytecodePC: */ +static NoDbgRegParms NeverInline void +widowOrForceToBytecodePC(sqInt ctxt) +{ + if (((((longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (!(isWidowedContext(ctxt))) { + assert(!((isMachineCodeFrame(frameOfMarriedContext(ctxt))))); + } + } + else { + ensureContextHasBytecodePC(ctxt); + } +} + + /* CoInterpreterMT>>#ceCheckForInterrupt */ +void +ceCheckForInterrupt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt switched; + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + switched = checkForEventsMayContextSwitch(1); + returnToExecutivepostContextSwitch(0, switched); +} + + +/* Invoked from checkForEventsMayContextSwitch: to switch threads if a thread + wanting to acquire the VM has higher priority than the active process. */ + + /* CoInterpreterMT>>#cedeToHigherPriorityThreads */ +static void +cedeToHigherPriorityThreads(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt activePriority; + sqInt activeProc; + CogVMThread *activeThread; + sqInt aLinkedList; + int existing; + CogVMThread *highest; + sqInt i; + sqInt index; + sqInt link; + sqInt newMaxPriority; + CogVMThread *nextHighest; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt priority; + sqInt processAffinity; + sqInt processLists; + CogVMThread *vmThread; + CogVMThread *vmThread1; + sqInt waitingPriority; + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + activePriority = (oop >> 3); + processAffinity = threadAffinityOfProcess(activeProc); + + /* begin currentVMThread */ + index = atomic_load((&GIV(vmOwner))); + assert(((index >= 0) && (index <= GIV(numThreads)))); + activeThread = (index > 0 + ? GIV(threads)[index] + : ((CogVMThread *) null)); + assert(threadIndexisCompatibleWith((activeThread->index), processAffinity)); + waitingPriority = atomic_load((&GIV(maxWaitingPriority))); + (activeThread->priority = activePriority); + + /* begin highestPriorityThreadIfHigherThan:expectedMax: */ + highest = (nextHighest = null); + + /* To make this fair we could remember the last index at which we + found the highest and start the search at the following index. */ + for (i = 1; i <= GIV(numThreads); i += 1) { + vmThread1 = GIV(threads)[i]; + if ((atomic_load((&((vmThread1->state))))) == CTMWantingOwnership) { + assert(((vmThread1->priority)) <= waitingPriority); + if (!highest) { + highest = vmThread1; + } + else { + if (((vmThread1->priority)) > ((highest->priority))) { + nextHighest = highest; + highest = vmThread1; + } + else { + if (!nextHighest) { + nextHighest = vmThread1; + } + else { + if (((vmThread1->priority)) > ((nextHighest->priority))) { + nextHighest = vmThread1; + } + } + } + } + } + } + if (!highest) { + vmThread = null; + goto l1; + } + if (((highest->priority)) <= activePriority) { + vmThread = null; + goto l1; + } + waitingPriorityIsAtLeast((!nextHighest + ? 0 + : (nextHighest->priority))); + vmThread = highest; + /* end highestPriorityThreadIfHigherThan:expectedMax: */ +l1: + if ((!vmThread) + || (vmThread == activeThread)) { + if (waitingPriority > activePriority) { + /* We found no thread of sufficiently high priority, even though waitingPriority indicated there should be one. + So reduce the waiting priority back to the priority of the currently active process. */ + + /* begin reduceWaitingPriorityFrom:to: */ + existing = waitingPriority; + + /* This CPXCHG may fail, that's fine though, as there may have been + another thread that increased the priority in the meantime. + In that case that threads priority is the correct one to use. */ + atomic_compare_exchange_strong((&GIV(maxWaitingPriority)), (&existing), activePriority); + } + return; + } + + /* no waiting thread of sufficiently high priority. Do not switch. + The activeProcess needs to run on a different thread. Leave this to + threadSwitchIfNecessary:from: in checkForEventsMayContextSwitch: */ + assert(((vmThread->priority)) > activePriority); + assert(vmThread != (currentVMThread())); + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (waitingPriority > ((vmThread->priority))) { + newMaxPriority = (vmThread->priority); + + /* begin reduceWaitingPriorityFrom:to: */ + existing = waitingPriority; + + /* This CPXCHG may fail, that's fine though, as there may have been + another thread that increased the priority in the meantime. + In that case that threads priority is the correct one to use. */ + atomic_compare_exchange_strong((&GIV(maxWaitingPriority)), (&existing), newMaxPriority); + } + GIV(statProcessSwitch) += 1; + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l2; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l2: + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, activeProc, activeContext)); + assert(isNonImmediate(activeProc)); + if (oopisGreaterThanOrEqualTo(activeProc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeContext & (tagMask())))) + && (oopisLessThan(activeContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(activeProc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(activeProc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),activeContext); + + /* begin ensurePushedInstructionPointer */ + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* begin processListForProcess: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + priority = (oop >> 3); + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + processLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + aLinkedList = longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((priority - 1)) << (shiftForWord())))))); + + /* begin is:onList: */ + /* begin followField:ofObject: */ + link = longAt((void *)((aLinkedList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(link & (tagMask())))) + && ((!((longAt((void *)(link))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + link = fixFollowedFieldofObjectwithInitialValue(FirstLinkIndex, aLinkedList, link); + } + while (!(link == GIV(nilObj))) { + if (link == activeProc) { + goto l3; + } + + /* begin followField:ofObject: */ + objOop = longAt((void *)((link + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(NextLinkIndex, link, objOop); + } + link = objOop; + } + putToSleepyieldingIf(activeProc, (!(imageHeaderFlags & 16))); +l3: + + /* Transcript cr; print: #cedeToHighestPriorityThreadIfHigherThan:; cr. + self printExternalHeadFrame. + self print: 'ip: '; printHex: self instructionPointer. Transcript cr; flush. */ + returnToSchedulingLoopAndReleaseVMOrWakeThreadsource(vmThread, CSCheckEvents); +} + + +/* Check for possible interrupts and handle one if necessary. + Answer if a context switch has occurred. */ + + /* CoInterpreterMT>>#checkForEventsMayContextSwitch: */ +static NoDbgRegParms sqInt +checkForEventsMayContextSwitch(sqInt mayContextSwitch) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt heapSizePostGC; + usqLong now; + sqInt nowSqInt; + sqInt objOop; + sqInt sema; + sqInt sufficientSpaceAfterGCRV; + sqInt switched; + sqInt threadingThwartsIOProcessEventsInvocation; + + /* begin assertSaneThreadAndProcess */ + assert(vmIsOwned()); + assert((vmThreadState(currentVMThread())) == CTMAssignableOrInVM); + assert((fetchPointerofObject(MyListIndex, activeProcess())) == (nilObject())); + assertValidProcessorStackPointersForIndex(atomic_load((&GIV(vmOwner)))); + assertCStackWellAligned(); + GIV(statCheckForEvents) += 1; + + /* restore the stackLimit if it has been smashed. */ + + /* begin restoreStackLimit */ + (GIV(stackPage)->stackLimit = (GIV(stackPage)->realStackLimit)); + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + + /* Allow the platform to do anything it needs to do synchronously. */ + ioSynchronousCheckForEvents(); + + /* begin checkCogCompiledCodeCompactionCalledFor */ + if (GIV(cogCompiledCodeCompactionCalledFor)) { + commenceCogCompiledCodeCompaction(); + } + if (GIV(needGCFlag)) { + /* sufficientSpaceAfterGC: runs the incremental GC and + then, if not enough space is available, the fullGC. */ + + /* begin sufficientSpaceAfterGC: */ + scavengingGCTenuringIf(TenureByAge); + heapSizePostGC = ((/* begin totalOldSpaceCapacity */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + if (((((double) (heapSizePostGC - GIV(heapSizeAtPreviousGC)) )) / GIV(heapSizeAtPreviousGC)) >= GIV(heapGrowthToSizeGCRatio)) { + fullGC(); + } + else { + if (GIV(totalFreeOldSpace) > (GIV(shrinkThreshold) * 2)) { + /* begin attemptToShrink */ + if ((GIV(totalFreeOldSpace) > GIV(shrinkThreshold)) + && ((GIV(totalFreeOldSpace) > GIV(growHeadroom)) + && (shrinkObjectMemory((((GIV(totalFreeOldSpace) - GIV(growHeadroom)) < GIV(growHeadroom)) ? GIV(growHeadroom) : (GIV(totalFreeOldSpace) - GIV(growHeadroom))))))) { + GIV(statShrinkMemory) += 1; + } + sufficientSpaceAfterGCRV = 1; + goto l1; + } + } + + /* Also attempt to shrink if there is plenty of free space and no need to GC */ + while ((GIV(totalFreeOldSpace) < GIV(growHeadroom)) + && ((growOldSpaceByAtLeast(0)))) { + if (GIV(totalFreeOldSpace) >= GIV(growHeadroom)) { + sufficientSpaceAfterGCRV = 1; + goto l1; + } + } + + /* Answer false if space is low */ + sufficientSpaceAfterGCRV = GIV(lowSpaceThreshold) <= GIV(totalFreeOldSpace); + /* end sufficientSpaceAfterGC: */ +l1: + if (!sufficientSpaceAfterGCRV) { + setSignalLowSpaceFlagAndSaveProcess(); + } + } + if (!mayContextSwitch) { + return 0; + } + + /* do not switch process until the disowningThread (if any) has saved its state... */ + if (GIV(disowningThreadIndex)) { + if (GIV(disowningThreadIndex) != (atomic_load((&GIV(vmOwner))))) { + preemptDisowningThread(); + assert(GIV(disowningThreadIndex) == 0); + } + else { + assert(0); + } + } + + /* Do not switch threads while performing these checks. Instead defer the thread + switch until after all the necessary checks have been tested, in the right order. */ + assert(!GIV(deferThreadSwitch)); + GIV(deferThreadSwitch) = 1; + + /* begin checkProfileTickIfSwitched */ + if ((GIV(nextProfileTick) <= 0) + || ((ioHighResClock()) < GIV(nextProfileTick))) { + switched = 0; + goto l3; + } + + /* Take a sample (if not already done so) for the profiler. */ + if (!GIV(profileProcess)) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + GIV(profileProcess) = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + } + GIV(profileMethod) = null; + + /* Zero nextProfileTick because signalling the semaphore is when the VM effectively delivers the sample. */ + + /* begin zeroNextProfileTick */ + GIV(nextProfileTick) = 0; + switched = synchronousSignal(GIV(profileSemaphore)); + /* end checkProfileTickIfSwitched */ +l3: +# if LRPCheck + if (checkDeliveryOfLongRunningPrimitiveSignal()) { + switched = 1; + } +# endif + + if (GIV(signalLowSpace)) { + /* signalLowSpace: */ + GIV(signalLowSpace) = 0; + sema = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheLowSpaceSemaphore) << (shiftForWord())))))); + if ((sema != GIV(nilObj)) + && (synchronousSignal(sema))) { + switched = 1; + } + } + nowSqInt = (now = ioUTCMicroseconds()); + + /* begin checkInvokeIOProcessEvents: */ + if (((ioEventThreadAffinity()) > 0) + && ((ioEventThreadAffinity()) != (atomic_load((&GIV(vmOwner)))))) { + threadingThwartsIOProcessEventsInvocation = nowSqInt >= GIV(nextPollUsecs); + goto l2; + } + if (nowSqInt >= GIV(nextPollUsecs)) { + GIV(statIOProcessEvents) += 1; + ioProcessEvents(); + + /* msecs to wait before next call to ioProcessEvents. Note that strictly + speaking we might need to update 'now' at this point since + ioProcessEvents could take a very long time on some platforms */ + GIV(nextPollUsecs) = nowSqInt + 20000; + } + threadingThwartsIOProcessEventsInvocation = 0; + /* end checkInvokeIOProcessEvents: */ +l2: + if (GIV(interruptPending)) { + GIV(interruptPending) = 0; + + /* reset interrupt flag */ + sema = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheInterruptSemaphore) << (shiftForWord())))))); + if ((sema != GIV(nilObj)) + && (synchronousSignal(sema))) { + switched = 1; + } + } + if (GIV(nextWakeupUsecs)) { + if (now >= GIV(nextWakeupUsecs)) { + GIV(nextWakeupUsecs) = 0; + + /* set timer interrupt to 0 for 'no timer' */ + sema = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord())))))); + if ((sema != GIV(nilObj)) + && (synchronousSignal(sema))) { + switched = 1; + } + } + } + + /* signal any pending finalizations */ + if (GIV(pendingFinalizationSignals) > 0) { + GIV(pendingFinalizationSignals) = 0; + sema = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheFinalizationSemaphore) << (shiftForWord())))))); + if ((sema != GIV(nilObj)) + && (synchronousSignal(sema))) { + switched = 1; + } + } + + /* signal all semaphores in semaphoresToSignal */ + if (signalExternalSemaphores()) { + switched = 1; + } + GIV(deferThreadSwitch) = 0; + if (threadingThwartsIOProcessEventsInvocation + || (GIV(checkThreadActivation))) { + GIV(checkThreadActivation) = 0; + cedeToHigherPriorityThreads(); + } + threadSwitchIfNecessaryfrom(activeProcess(), CSCheckEvents); + return switched; +} + + +/* Check whether the VM is unowned and needs to set a thread running to try + and own it. + Do not attempt this if the image doesn't have a threadAffinity inst var in + Process; the VM + can't thread these images. */ +/* for Slang and the ifTrue: below... */ + + /* CoInterpreterMT>>#checkVMOwnershipFromHeartbeat */ +static void +checkVMOwnershipFromHeartbeat(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int expected; + sqInt ok; + sqInt threadState; + CogVMThread *vmThread; + + sqLowLevelMFence(); + + /* begin tryLockVMOwnerTo: */ + assert(!(0 /* (CTMUnknownOwner = 0) */)); + expected = 0; + ok = atomic_compare_exchange_strong((&GIV(vmOwner)), (&expected), CTMUnknownOwner); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(CTMUnknownOwner, ok); + } + if (ok) { + /* begin ensureRunningVMThread: */ + if ((vmThread = willingVMThread())) { + threadState = atomic_load((&((vmThread->state)))); + + /* If the VM is relinquishing the processor then only schedule a thread if it has work to do. */ + if (GIV(relinquishing) + && (threadState != CTMWantingOwnership)) { + /* begin releaseVM */ + /* begin setVMOwner: */ + assert(((getVMOwner()) == CTMUnknownOwner) + || ((getVMOwner()) == (ioThreadLocalGetThreadIndex()))); + assert((getVMOwner()) != 0); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(0, 1); + } + + /* TODO: We could make this a `release` ordering, which may perform better on ARM. */ + atomic_store((&GIV(vmOwner)), 0); + goto l1; + } + assert(((threadState == CTMAssignableOrInVM) + || (threadState == CTMInitializing)) + || (threadState == CTMWantingOwnership)); + + /* Ownership will be transferred to vmThread, no need to release the VM. */ + wakeVMThread(vmThread); + goto l1; + } + + /* If the VM is relinquishing the processor then only schedule a thread if it has work to do + (willingVMThread not nil above). + If we have failed to allocate thread storage before there is no point continuing to + try to do so. By this time we should have quite a few threads in the pool. */ + if (!(GIV(relinquishing) + || (GIV(memoryIsScarce)))) { + if ((vmThread = unusedThreadInfo())) { + wakeVMThread(vmThread); + goto l1; + } + } + + /* begin releaseVM */ + /* begin setVMOwner: */ + assert(((getVMOwner()) == CTMUnknownOwner) + || ((getVMOwner()) == (ioThreadLocalGetThreadIndex()))); + assert((getVMOwner()) != 0); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(0, 1); + } + + /* TODO: We could make this a `release` ordering, which may perform better on ARM. */ + atomic_store((&GIV(vmOwner)), 0); + goto l1; + /* end ensureRunningVMThread: */ +l1:; + } +} + + +/* Defer smashes of the stackLimit around the call of functionSymbol (for + assert checks). + + N.B. SYNCHRONIZE WITH forceInterruptCheckFromHeartbeat + */ + + /* CoInterpreterMT>>#deferStackLimitSmashAround:with: */ +static NoDbgRegParms sqInt +deferStackLimitSmashAroundwith(void (*functionSymbol)(sqInt), sqInt arg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(deferSmash) = 1; + sqLowLevelMFence(); + functionSymbol(arg); + GIV(deferSmash) = 0; + sqLowLevelMFence(); + if (GIV(deferredSmash)) { + GIV(deferredSmash) = 0; + sqLowLevelMFence(); + forceInterruptCheck(); + checkVMOwnershipFromHeartbeat(); + } + return 1; +} + + +/* Release the VM to other threads and answer the current thread's index plus + some status flags above it. + Currently valid flags: + DisownVMForFFICallFlag - informs the VM that it is entering an FFI + call DisownVMFromCallbackFlag - informs the VM that it is reentering an + FFI call + DisownVMForProcessorRelinquishFlag - informs the VM that the horrible + primitiveRelinquishProcessor is disowning the VM + OwnedVMFromForeignThreadFlag - indicates lowest-level entry from a + foreign thread + - not to be used explicitly by clients + - only set by ownVMFromUnidentifiedThread + VMAlreadyOwnedHenceDoNotDisown + - not to be used explicitly by clients + - only set by ownVMFromUnidentifiedThread + - indicates an ownVM from a callback was made when + the vm was still owned (e.g. from a plugin primitive). + + This is the entry-point for plugins and primitives that wish to release + the VM while + performing some operation that may potentially block, and for callbacks + returning back to some blocking operation. If this thread does not reclaim + the VM before- + hand then when the next heartbeat occurs the thread manager will schedule + a thread to acquire the VM which may start running the VM in place of this + thread. + N.B. Most of the state needed to resume after preemption is set in + preemptDisowningThread. */ + + /* CoInterpreterMT>>#disownVM: */ +sqInt +disownVM(sqInt flags) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + sqInt currentState; + sqInt objOop; + sqInt oop; + sqInt proc; + CogVMThread *selfOfPriority; + CogVMThread *selfOfStackPtrIndex; + CogVMThread *self_in_CogVMThread; + sqInt thingOne; + sqInt threadIndex; + CogVMThread *vmThread; + + assert(flags >= 0); + assert(((((flags & DisownVMForFFICallFlag) != 0)) != (((flags & DisownVMForProcessorRelinquishFlag) != 0))) + || ((((flags & DisownVMFromCallbackFlag) != 0)) + && ((!(flags & DisownVMForProcessorRelinquishFlag))))); + assert(successful()); + assert(vmOwnerIs(ioThreadLocalGetThreadIndex())); + if (recordFFIOrThreadTrace()) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + thingOne = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceDisownVM) << TraceTypeShift)))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = thingOne; + GIV(traceLog)[GIV(traceLogIndex) + 2] = flags; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + threadIndex = atomic_load((&GIV(vmOwner))); + if (((flags & VMAlreadyOwnedHenceDoNotDisownFlag) != 0)) { + /* begin vmThreadAt: */ + assert(((threadIndex >= 0) && (threadIndex <= GIV(numThreads)))); + self_in_CogVMThread = (threadIndex > 0 + ? GIV(threads)[threadIndex] + : ((CogVMThread *) null)); + + /* begin setVmThreadState: */ + currentState = atomic_load((&((self_in_CogVMThread->state)))); + if (currentState != CTMUnavailable) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMUnavailable = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + assert(0 /* (CTMUnavailable = CTMAssignableOrInVM) */); + break; + case CTMAssignableOrInVM: + break; + case CTMUnavailable: + assert(0 /* (CTMUnavailable = CTMWantingOwnership) */); + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((self_in_CogVMThread->state))), CTMUnavailable); + return threadIndex + (((((usqInt)(VMAlreadyOwnedHenceDoNotDisownFlag) << ThreadIdShift)))); + } + assertCStackPointersBelongToCurrentThread(); + assertValidNewMethodPropertyFlags(); +# if !NDEBUG + selfOfStackPtrIndex = vmThreadAt(threadIndex); + (selfOfStackPtrIndex->stackPtrIndex = /* stackPointerIndexForFrame:WithSP: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? (((usqInt)(((GIV(framePointer) + FoxMFReceiver) - GIV(stackPointer)))) >> (shiftForWord())) + (((mframeCogMethod(GIV(framePointer)))->cmNumArgs)) + : (((usqInt)(((GIV(framePointer) + FoxIFReceiver) - GIV(stackPointer)))) >> (shiftForWord())) + (byteAt((GIV(framePointer) + FoxIFrameFlags) + 1)))); +# endif // !NDEBUG + + if (((flags & DisownVMForProcessorRelinquishFlag) != 0)) { + if (((proc = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ForeignCallbackProcess) << (shiftForWord())))))))) != GIV(nilObj)) { + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((proc + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + GIV(foreignCallbackPriority) = (oop >> 3); + } + GIV(relinquishing) = 1; + sqLowLevelMFence(); + } + disownCount += 1; + + /* If we're disowning the VM because there's no active process to run, there's nothing to preempt later, + so don't indicate that there's a disowning thread that needs to be restored later. */ + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + if (activeProc != GIV(nilObj)) { + if (GIV(disowningThreadIndex) == (atomic_load((&GIV(vmOwner))))) { + assert(GIV(disowningCFramePointer) == GIV(CFramePointer)); + assert(GIV(disowningCStackPointer) == GIV(CStackPointer)); + } + else { + if (GIV(disowningThreadIndex)) { + preemptDisowningThread(); + assert(GIV(disowningThreadIndex) == 0); + } + GIV(disowningThreadIndex) = threadIndex; + GIV(disowningInstructionPointer) = GIV(instructionPointer); + GIV(disowningCFramePointer) = GIV(CFramePointer); + GIV(disowningCStackPointer) = GIV(CStackPointer); + sqLowLevelMFence(); + } + selfOfPriority = vmThreadAt(threadIndex); + (selfOfPriority->priority = quickFetchIntegerofObject(PriorityIndex, activeProc)); + } + + /* OwnedVMFromForeignThreadFlag indicates lowest-level of entry by a foreign + thread. If that's where we are then release the vmThread. Otherwise + indicate the vmThread is off doing something outside of the VM. */ + if (((flags & OwnedVMFromForeignThreadFlag) != 0)) { + /* I don't think this is quite right. Josh's use case is creating some foreign thread and then registering + it with the VM. That's not the same as binding a process to a foreign thread given that the foreign + callback process is about to terminate anyway (it is returning from a callback here). So do we need + an additional concept, that of a vmThread being either of the set known to the VM or floating? */ + if (!(isBoundProcess(activeProc))) { + /* begin vmThreadAt: */ + assert(((threadIndex >= 0) && (threadIndex <= GIV(numThreads)))); + vmThread = (threadIndex > 0 + ? GIV(threads)[threadIndex] + : ((CogVMThread *) null)); + + /* begin unregisterVMThread: */ + assert(((vmThreadState(vmThread)) != CTMUninitialized) + && ((vmThreadState(vmThread)) != CTMInitializing)); + initializeThreadState(vmThread); + (vmThread->osThread = null); + ioThreadLocalSetThreadIndex(0); + } + } + else { + /* begin vmThreadAt: */ + assert(((threadIndex >= 0) && (threadIndex <= GIV(numThreads)))); + self_in_CogVMThread = (threadIndex > 0 + ? GIV(threads)[threadIndex] + : ((CogVMThread *) null)); + + /* begin setVmThreadState: */ + currentState = atomic_load((&((self_in_CogVMThread->state)))); + if (currentState != CTMUnavailable) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMUnavailable = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + assert(0 /* (CTMUnavailable = CTMAssignableOrInVM) */); + break; + case CTMAssignableOrInVM: + break; + case CTMUnavailable: + assert(0 /* (CTMUnavailable = CTMWantingOwnership) */); + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((self_in_CogVMThread->state))), CTMUnavailable); + } + if (((flags & DisownVMForFFICallFlag) != 0)) { + ioThreadLocalSetInFFICall(threadIndex); + assert(ioThreadLocalGetInFFICall() == threadIndex); + } + assert(((((vmThreadAt(threadIndex))->boundProcess))) != (GIV(disowningThreadIndex) != 0)); + + /* begin releaseVM */ + /* begin setVMOwner: */ + assert(((getVMOwner()) == CTMUnknownOwner) + || ((getVMOwner()) == (ioThreadLocalGetThreadIndex()))); + assert((getVMOwner()) != 0); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(0, 1); + } + + /* TODO: We could make this a `release` ordering, which may perform better on ARM. */ + atomic_store((&GIV(vmOwner)), 0); + return threadIndex + (((((usqInt)(flags) << ThreadIdShift)))); +} + + +/* The trace log is a circular buffer of pairs of entries. If there is + an entry at traceLogIndex - 3 \\ TraceBufferSize it has entries. + If there is something at traceLogIndex it has wrapped. */ +/* Override to print legend of known flags */ + + /* CoInterpreterMT>>#dumpTraceLog */ +void +dumpTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt flags; + sqInt i; + sqInt j; + sqInt remainder; + sqInt thing; + + if (!(GIV(traceLog)[/* safe:mod: */ + (((remainder = (GIV(traceLogIndex) - 3) % TraceBufferSize)) < 0 + ? remainder + TraceBufferSize + : remainder)])) { + return; + } + if (GIV(traceLog)[GIV(traceLogIndex)]) { + for (j = GIV(traceLogIndex); j <= (TraceBufferSize - 3); j += 3) { + printLogEntryAt(j); + } + } + for (j = 0; j <= (GIV(traceLogIndex) - 3); j += 3) { + printLogEntryAt(j); + } + if (GIV(traceLog)[/* safe:mod: */ + (((remainder = (GIV(traceLogIndex) - 3) % TraceBufferSize)) < 0 + ? remainder + TraceBufferSize + : remainder)]) { + flags = 0; + if (GIV(traceLog)[GIV(traceLogIndex)]) { + for (i = GIV(traceLogIndex); i <= (TraceBufferSize - 3); i += 3) { + thing = GIV(traceLog)[i]; + if (thing == TraceOwnVM) { + flags = flags | (((((usqInt)((GIV(traceLog)[i + 1]))) >> ThreadIdShift) >> 3)); + } + if (thing == TraceDisownVM) { + flags = flags | (((GIV(traceLog)[i + 1]) >> 3)); + } + } + } + for (i = 0; i <= (GIV(traceLogIndex) - 3); i += 3) { + thing = GIV(traceLog)[i]; + if (thing == TraceOwnVM) { + flags = flags | (((((usqInt)((GIV(traceLog)[i + 1]))) >> ThreadIdShift) >> 3)); + } + if (thing == TraceDisownVM) { + flags = flags | (((GIV(traceLog)[i + 1]) >> 3)); + } + } + if (((flags & (((OwnedVMFromForeignThreadFlag + VMAlreadyOwnedHenceDoNotDisownFlag) + DisownVMForFFICallFlag) + DisownVMForProcessorRelinquishFlag)) != 0)) { + if (((flags & VMAlreadyOwnedHenceDoNotDisownFlag) != 0)) { + printHex(VMAlreadyOwnedHenceDoNotDisownFlag); + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " AlreadyOwned "); + } + if (((flags & OwnedVMFromForeignThreadFlag) != 0)) { + printHex(OwnedVMFromForeignThreadFlag); + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " OwnedVMFromForeignThread "); + } + if (((flags & DisownVMForFFICallFlag) != 0)) { + printHex(DisownVMForFFICallFlag); + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " DisownVMForFFI "); + } + if (((flags & DisownVMForProcessorRelinquishFlag) != 0)) { + printHex(DisownVMForProcessorRelinquishFlag); + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " DisownVMForProcessorRelinquish "); + } + cr(); + } + } +} + + +/* Main entry-point into the interpreter at each execution level, where an + execution level is either the start of execution or reentry for a + callback. Capture the C stack + pointers so that calls from machine-code into the C run-time occur at this + level. This is the actual implementation, separated from + enterSmalltalkExecutive so the + simulator can wrap it in an exception handler and hence simulate the + Cogit's jump + back into C code on interpreting; see ceInvokeInterpret. + + Conceptually, an invocation of interpret exists at each level of execution + from the + initial invocation through each callback. Entry to each execution level is + through this function. It captures the C stack & frame pointers for this + level of execution + and then either invokes machine code or interpret, depending on whether + the current frame (the effective entry-point into Smalltalk execution) is + a machine code + or interpreted frame. In addition, interpret captures the return address + of its caller + (this function). The Cogit then uses the captured C stack pointers and + return address to invoke interpret as if it had been called from this + function. */ +/* Main entry-point into the interpreter at each execution level, where an + execution level is either the start of execution or reentry for a + callback. Capture the C stack + pointers so that calls from machine-code into the C run-time occur at this + level. This is the actual implementation, separated from + enterSmalltalkExecutive so the + simulator can wrap it in an exception handler and hence simulate the + setjmp/longjmp. + Override to return if a longjmp to reenterInterpreter passes a parameter + greater than 1. + This causes a return to threadSchedulingLoop:startingVM: and is used to + surrender control to another thread. */ + + /* CoInterpreterMT>>#enterSmalltalkExecutiveImplementation */ +static sqInt +enterSmalltalkExecutiveImplementation(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin assertSaneThreadAndProcess */ + assert(vmIsOwned()); + assert((vmThreadState(currentVMThread())) == CTMAssignableOrInVM); + assert((fetchPointerofObject(MyListIndex, activeProcess())) == (nilObject())); + assertValidProcessorStackPointersForIndex(atomic_load((&GIV(vmOwner)))); + assertProcessorStackPointersBelongToCurrentThread(); + assertCStackWellAligned(); + ceCaptureCStackPointers(); + assertSavedCStackPointersWellAligned(); + if ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())) { + returnToExecutivepostContextSwitch(0, 1); + } + interpret(); + return 0; +} + + +/* Force an interrupt check ASAP. This version is the + entry-point to forceInterruptCheck for the heartbeat + timer to allow for repeatable debugging. + + N.B. SYNCHRONIZE WITH deferStackLimitSmashAround: */ + + /* CoInterpreterMT>>#forceInterruptCheckFromHeartbeat */ +void +forceInterruptCheckFromHeartbeat(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!suppressHeartbeatFlag) { + /* begin checkForLongRunningPrimitive */ +# if LRPCheck + if (!GIV(longRunningPrimitiveCheckSemaphore)) { + goto l1; + } + if ((GIV(longRunningPrimitiveStartUsecs) > 0) + && ((GIV(longRunningPrimitiveCheckMethod) == GIV(newMethod)) + && (GIV(longRunningPrimitiveCheckSequenceNumber) == GIV(statCheckForEvents)))) { + GIV(longRunningPrimitiveStopUsecs) = ioUTCMicroseconds(); + assert(GIV(longRunningPrimitiveStopUsecs) > GIV(longRunningPrimitiveStartUsecs)); + goto l1; + } + + /* See traceProfileState & mapProfileState. */ + if (!GIV(longRunningPrimitiveStopUsecs)) { + GIV(longRunningPrimitiveCheckSequenceNumber) = GIV(statCheckForEvents); + GIV(longRunningPrimitiveCheckMethod) = GIV(newMethod); + GIV(longRunningPrimitiveStartUsecs) = ioUTCMicroseconds(); + sqLowLevelMFence(); + } +# endif // LRPCheck + + /* end checkForLongRunningPrimitive */ +l1: + sqLowLevelMFence(); + if (GIV(deferSmash)) { + GIV(deferredSmash) = 1; + sqLowLevelMFence(); + } + else { + forceInterruptCheck(); + checkVMOwnershipFromHeartbeat(); + } + } +} + + +/* Main entry-point into the interpreter at system start-up. */ +/* Ensure that the myList of the activeProcess is nil. Needed to load + old images which don't nil myList in transferTo:{from:} */ + + /* CoInterpreterMT>>#initialEnterSmalltalkExecutive */ +static void +initialEnterSmalltalkExecutive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + objOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(MyListIndex, objOop, GIV(nilObj))); + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),GIV(nilObj)); + threadSchedulingLoop(vmThreadAt(1)); + + /* NOTREACHED. The following is to fool Slang */ + threadSchedulingLoopImplementation(currentVMThread()); +} + + /* CoInterpreterMT>>#isBoundProcess: */ +static NoDbgRegParms sqInt +isBoundProcess(sqInt aProcess) +{ + sqInt threadId; + + threadId = threadAffinityOfProcess(aProcess); + + /* begin isBoundThreadId: */ + return ((((threadId) & 7) == 1)) + && ((((threadId >> 3)) & 1) == 1); +} + + /* CoInterpreterMT>>#loadInitialContext */ +void +loadInitialContext(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt activeProc; + sqInt activeProc1; + sqInt obj; + sqInt objOop; + sqInt threadAffinity; + + runLeakCheckerFor(GCModeFull); + + /* primitiveSuspend needs to know the class of LinkedList */ + + /* begin getClassTagOfLinkedList */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + objOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + obj = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + GIV(classLinkedListClassTag) = (longAt((void *)(obj))) & (classIndexMask()); + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc1 = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + activeContext = longAt((void *)((activeProc1 + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + threadAffinity = threadAffinityOfProcess(activeProc); + assert((threadAffinity == 0) + || (threadAffinity == 1)); + startThreadSubsystem(); +} + + +/* Map all oops in the interpreter's state to their new values + during garbage collection or a become: operation. */ +/* Assume: All traced variables contain valid oops. */ +/* Map all oops in the interpreter's state to their new values + during garbage collection or a become: operation. */ +/* Assume: All traced variables contain valid oops. */ + + /* CoInterpreterMT>>#mapInterpreterOops */ +static void +mapInterpreterOops(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + DisownState *disownState; + sqInt i; + sqInt ipdelta; + sqInt result; + sqInt toDoLimit; + CogVMThread *vmThread; + + mapStackPages(); + + /* begin mapMachineCode: */ + mapObjectReferencesInMachineCode(GIV(gcMode)); + mapPrimTraceLog(); + mapTraceLog(); + + /* begin mapVMRegisters */ + ipdelta = 0; + if (shouldRemapObj(GIV(method))) { + ipdelta = (/* method:includesAddress: */ + (GIV(instructionPointer) > GIV(method)) + && (GIV(instructionPointer) < (GIV(method) + ((((numSlotsOfAny(GIV(method))) << (shiftForWord()))) + BaseHeaderSize))) + ? GIV(instructionPointer) - GIV(method) + : 0); + GIV(method) = remapObj(GIV(method)); + if (ipdelta) { + GIV(instructionPointer) = GIV(method) + ipdelta; + } + } + if (/* shouldRemapOop: */ + ((!(GIV(newMethod) & (tagMask())))) + && (shouldRemapObj(GIV(newMethod)))) { + ipdelta = ((!ipdelta) + && (/* method:includesAddress: */ + (GIV(instructionPointer) > GIV(newMethod)) + && (GIV(instructionPointer) < (GIV(newMethod) + ((((numSlotsOfAny(GIV(newMethod))) << (shiftForWord()))) + BaseHeaderSize)))) + ? GIV(instructionPointer) - GIV(newMethod) + : 0); + + /* don't relocate twice!! */ + GIV(newMethod) = remapObj(GIV(newMethod)); + if (ipdelta) { + GIV(instructionPointer) = GIV(newMethod) + ipdelta; + } + } + + /* begin mapProfileState */ + /* begin profileStateDoUpdating: */ + if (GIV(profileProcess)) { + if ((result = (shouldRemapObj(GIV(profileProcess)) + ? remapObj(GIV(profileProcess)) + : 0))) { + GIV(profileProcess) = result; + } + } + if (GIV(profileMethod)) { + if ((result = (shouldRemapObj(GIV(profileMethod)) + ? remapObj(GIV(profileMethod)) + : 0))) { + GIV(profileMethod) = result; + } + } + if (GIV(profileSemaphore)) { + if ((result = (shouldRemapObj(GIV(profileSemaphore)) + ? remapObj(GIV(profileSemaphore)) + : 0))) { + GIV(profileSemaphore) = result; + } + } +# if LRPCheck + /* The longRunningPrimitiveCheckMethod (LRPCM) is sampled in an interrupt. Be very careful with it. + If longRunningPrimitiveCheckSequenceNumber (LRPCSN) = statCheckForEvents then LRPCM has + been recenty sampled and could be mapped or not, but it must be newMethod and we can simply + copy newMethod. If LRPCSN ~= statCheckForEvents then LRPCM must be some extant object and + needs to be remapped. */ + sqLowLevelMFence(); + if (GIV(longRunningPrimitiveCheckMethod)) { + if (GIV(longRunningPrimitiveCheckSequenceNumber) == GIV(statCheckForEvents)) { + GIV(longRunningPrimitiveCheckMethod) = GIV(newMethod); + } + else { + if (shouldRemapObj(GIV(longRunningPrimitiveCheckMethod))) { + GIV(longRunningPrimitiveCheckMethod) = remapObj(GIV(longRunningPrimitiveCheckMethod)); + } + } + sqLowLevelMFence(); + } + if (GIV(longRunningPrimitiveCheckSemaphore)) { + if (shouldRemapObj(GIV(longRunningPrimitiveCheckSemaphore))) { + GIV(longRunningPrimitiveCheckSemaphore) = remapObj(GIV(longRunningPrimitiveCheckSemaphore)); + } + } +# endif // LRPCheck + + if ((GIV(tempOop) != 0) + && (/* shouldRemapOop: */ + ((!(GIV(tempOop) & (tagMask())))) + && (shouldRemapObj(GIV(tempOop))))) { + GIV(tempOop) = remapObj(GIV(tempOop)); + } + if ((GIV(tempOop2) != 0) + && (/* shouldRemapOop: */ + ((!(GIV(tempOop2) & (tagMask())))) + && (shouldRemapObj(GIV(tempOop2))))) { + GIV(tempOop2) = remapObj(GIV(tempOop2)); + } + + /* Per-thread state; trace each thread's own newMethods, including those in outer calls if a thread is nested in an outer disownVM:/ownVM:. */ + toDoLimit = getNumThreads(); + for (i = 1; i <= toDoLimit; i += 1) { + /* begin vmThreadAt: */ + assert(((i >= 0) && (i <= GIV(numThreads)))); + vmThread = (i > 0 + ? GIV(threads)[i] + : ((CogVMThread *) null)); + if ((atomic_load((&((vmThread->state))))) > CTMInitializing) { + if ((vmThread->boundProcess)) { + if (shouldRemapObj((vmThread->boundProcess))) { + (vmThread->boundProcess = remapObj((vmThread->boundProcess))); + } + } + disownState = ((DisownState *) vmThread); + do { + if ((((disownState->newMethodOrNull))) + && (shouldRemapObj((vmThread->newMethodOrNull)))) { + (vmThread->newMethodOrNull = remapObj((vmThread->newMethodOrNull))); + } + } while(((disownState = (disownState->nestedDisownStates)))); + } + } +} + + +/* Attempt to take ownership from a thread that as yet doesn't know its + index. This supports callbacks where the callback could originate from any + thread, and from called FFI code or from plugin primitives. If from the + latter there is + no requirement that the VM has been disowned by the calling-back thread. + In this case where the calling-back thread still own the VM, thunkEntry + should *not* disown the VM on return. To effect this + ownVMFromUnidentifiedThread answers the + VMAlreadyOwnedHenceDoNotDisownFlag. + From ownVM: + Answer 0 if the owning thread is known to the VM. + And from here + Answer OwnedVMFromForeignThreadFlag if the owning thread is unknown to the + VM and now owns the VM. + Answer VMAlreadyOwnedHenceDoNotDisownFlag if the thread already owns the + VM. Answer -1 if the owning thread is unknown to the VM and fails to own + the VM. + Answer -2 if the owning thread is unknown to the VM and there is no + foreign callback process installed. */ + + /* CoInterpreterMT>>#ownVMFromUnidentifiedThread */ +static sqInt +ownVMFromUnidentifiedThread(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt count; + sqInt currentState; + int expected; + sqInt indexOrZero; + CogVMThread *self_in_CogVMThread; + sqInt success; + sqInt threadIndex; + CogVMThread *vmThread; + + if ((threadIndex = ioThreadLocalGetThreadIndex())) { + /* this is a callback from a known thread */ + if ((atomic_load((&GIV(vmOwner)))) == threadIndex) { + assert((GIV(disowningThreadIndex) == 0) + || (GIV(disowningThreadIndex) == threadIndex)); + + /* begin vmThreadAt: */ + assert(((threadIndex >= 0) && (threadIndex <= GIV(numThreads)))); + self_in_CogVMThread = (threadIndex > 0 + ? GIV(threads)[threadIndex] + : ((CogVMThread *) null)); + + /* begin setVmThreadState: */ + currentState = atomic_load((&((self_in_CogVMThread->state)))); + if (currentState != CTMAssignableOrInVM) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMAssignableOrInVM = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMAssignableOrInVM = CTMUnavailable) */); + break; + case CTMUnavailable: + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((self_in_CogVMThread->state))), CTMAssignableOrInVM); + return VMAlreadyOwnedHenceDoNotDisownFlag; + } + } + if ((threadIndex == 0) + && (GIV(foreignCallbackPriority) == 0)) { + return -2; + } + count = 0; + + /* Before we can proceed, we need to temporarily lock the vm, so we can either find our CogVMThread struct or allocate a new one. */ + + /* begin acquireVMForIndex:withPriority: */ + while (1) { + /* begin tryLockVMOwnerTo: */ + assert(!((((threadIndex > 0 + ? threadIndex + : CTMUnknownOwner)) == 0))); + + /* begin doTryLockVMOwnerTo: */ + expected = 0; + success = (atomic_compare_exchange_strong((&GIV(vmOwner)), (&expected), (threadIndex > 0 + ? threadIndex + : CTMUnknownOwner))) + || ((/* We may already be vmOwner. The current vmOwner will be stored in expected. + However, if an unknown owner is present, we cannot assume that's us! */ + + (expected == ((threadIndex > 0 + ? threadIndex + : CTMUnknownOwner))) + && (((threadIndex > 0 + ? threadIndex + : CTMUnknownOwner)) != CTMUnknownOwner))); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful((threadIndex > 0 + ? threadIndex + : CTMUnknownOwner), success); + } + if (success) break; + waitingPriorityIsAtLeast(GIV(foreignCallbackPriority)); + ioTransferTimeslice(); + } + if (threadIndex) { + /* this is a callback from a known thread. Simply own the VM for that thread. */ + return ownVM(threadIndex); + } + + /* If the current thread doesn't have an index it's new to the vm + and we need to allocate a new threadInfo, failing if we can't. + We also need a process in the ForeignCallbackProcess slot upon + which to run the thread's eventual callback. */ + while ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ForeignCallbackProcess) << (shiftForWord()))))))) == GIV(nilObj)) { + /* begin releaseVM */ + /* begin setVMOwner: */ + assert(((getVMOwner()) == CTMUnknownOwner) + || ((getVMOwner()) == (ioThreadLocalGetThreadIndex()))); + assert((getVMOwner()) != 0); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(0, 1); + } + + /* TODO: We could make this a `release` ordering, which may perform better on ARM. */ + atomic_store((&GIV(vmOwner)), 0); + if (((count += 1)) > 1000) { + return -2; + } + ioMilliSleep(1); + + /* begin acquireVMForIndex:withPriority: */ + while (1) { + /* begin tryLockVMOwnerTo: */ + assert(!((((threadIndex > 0 + ? threadIndex + : CTMUnknownOwner)) == 0))); + + /* begin doTryLockVMOwnerTo: */ + expected = 0; + success = (atomic_compare_exchange_strong((&GIV(vmOwner)), (&expected), (threadIndex > 0 + ? threadIndex + : CTMUnknownOwner))) + || ((/* We may already be vmOwner. The current vmOwner will be stored in expected. + However, if an unknown owner is present, we cannot assume that's us! */ + + (expected == ((threadIndex > 0 + ? threadIndex + : CTMUnknownOwner))) + && (((threadIndex > 0 + ? threadIndex + : CTMUnknownOwner)) != CTMUnknownOwner))); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful((threadIndex > 0 + ? threadIndex + : CTMUnknownOwner), success); + } + if (success) break; + waitingPriorityIsAtLeast(GIV(foreignCallbackPriority)); + ioTransferTimeslice(); + } + } + vmThread = unusedThreadInfo(); + + /* N.B. Keep the VM locked anonymously so that we reserve the non-nil ForeignCallbackProcess + for this thread, avoiding the race between competing foreign callbacks. The acquireVMFor: in + ownVM: will set the vmOwner to the actual index. So only unlock on failure. */ + if (!vmThread) { + /* begin releaseVM */ + /* begin setVMOwner: */ + assert(((getVMOwner()) == CTMUnknownOwner) + || ((getVMOwner()) == (ioThreadLocalGetThreadIndex()))); + assert((getVMOwner()) != 0); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(0, 1); + } + + /* TODO: We could make this a `release` ordering, which may perform better on ARM. */ + atomic_store((&GIV(vmOwner)), 0); + return -1; + } + indexOrZero = (vmThread->index); + + /* begin setVMOwner: */ + assert(((getVMOwner()) == CTMUnknownOwner) + || ((getVMOwner()) == (ioThreadLocalGetThreadIndex()))); + assert((getVMOwner()) != indexOrZero); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(indexOrZero, 1); + } + + /* TODO: We could make this a `release` ordering, which may perform better on ARM. */ + atomic_store((&GIV(vmOwner)), indexOrZero); + (vmThread->priority = GIV(foreignCallbackPriority)); + + /* begin setVmThreadState: */ + currentState = atomic_load((&((vmThread->state)))); + if (currentState != CTMWantingOwnership) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMWantingOwnership = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + assert(0 /* (CTMWantingOwnership = CTMAssignableOrInVM) */); + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMWantingOwnership = CTMUnavailable) */); + break; + case CTMUnavailable: + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((vmThread->state))), CTMWantingOwnership); + + /* begin registerVMThread: */ + assert((((vmThreadState(vmThread)) == CTMInitializing) + || ((vmThreadState(vmThread)) == CTMWantingOwnership)) + && (!((vmThread->osThread)))); + (vmThread->osThread = ioCurrentOSThread()); + ioThreadLocalSetThreadIndex((vmThread->index)); + assert((ioThreadLocalGetThreadIndex()) == ((vmThread->index))); + ownVM((vmThread->index)); + return OwnedVMFromForeignThreadFlag; +} + + +/* This is the entry-point for plugins and primitives that wish to reacquire + the VM after having + released it via disownVM: or callbacks that want to acquire it without + knowing their ownership + status. This call will block until the VM is owned by the current thread + or an error occurs. + The argument should be the value answered by disownVM:, or 0 for callbacks + that don't know + if they have disowned or not. This is both an optimization to avoid having + to query thread- + local storage for the current thread's index (since it can easily keep it + in some local variable), + and a record of when an unbound process becomes affined to a thread for + the dynamic + extent of some operation. + + Answer 0 if the current thread is known to the VM (and on return owns the + VM). and from ownVMFromUnidentifiedThread: + Answer 1 if the current thread is unknown to the VM and takes ownership. + Answer -1 if the current thread is unknown to the VM and fails to take + ownership. Answer -2 if the owning thread is unknown to the VM and there + is no foreign callback process installed. */ +/* for Slang and the ifTrue: below... */ + + /* CoInterpreterMT>>#ownVM: */ +sqInt +ownVM(sqInt disownResult) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt activeProc; + sqInt aMethodObj; + sqInt aob; + sqInt aProcess; + sqInt cFramePointer; + sqInt context; + sqInt cStackPointer; + sqInt currentState; + DisownState *disownState; + int expected; + sqInt flags; + sqInt header; + StackPage *lastUsedPage; + sqInt methodHeader; + sqInt myProc; + sqInt newContext; + StackPage *newPage; + sqInt objOop; + sqInt ok; + sqInt okSqInt; + sqInt sched; + CogVMThread *self_in_CogVMThread; + sqInt senderOop; + char *theFrame; + StackPage *thePage; + sqInt thingOne; + sqInt threadIndex; + sqInt threadIndexSqInt; + sqInt threadState; + sqInt top; + sqInt traced; + CogVMThread *vmThread; + + if (recordFFIOrThreadTrace()) { + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceAttemptOwnVM) << TraceTypeShift)))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = 0; + GIV(traceLog)[GIV(traceLogIndex) + 2] = disownResult; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + traced = 0; + threadIndex = disownResult & ((1U << ThreadIdShift) - 1); + if (!threadIndex) { + return ownVMFromUnidentifiedThread(); + } + flags = ((usqInt)(disownResult)) >> ThreadIdShift; + assert(((!(flags & DisownVMFromCallbackFlag))) + || ((((vmThreadAt(threadIndex))->boundProcess)))); + assert(threadIndex == ioThreadLocalGetThreadIndex()); + + /* begin tryLockVMOwnerTo: */ + assert(!((threadIndex == 0))); + expected = 0; + ok = (atomic_compare_exchange_strong((&GIV(vmOwner)), (&expected), threadIndex)) + || ((/* We may already be vmOwner. The current vmOwner will be stored in expected. + However, if an unknown owner is present, we cannot assume that's us! */ + + (expected == threadIndex) + && (threadIndex != CTMUnknownOwner))); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(threadIndex, ok); + } + if (ok) { + assert(getVMOwner() == threadIndex); + if (recordFFIOrThreadTrace()) { + traced = 1; + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + thingOne = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceOwnVM) << TraceTypeShift)))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = thingOne; + GIV(traceLog)[GIV(traceLogIndex) + 2] = disownResult; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + + /* disowningThreadIndex is an optimization that allows disownVM:/ownVM: to avoid recording + the VM state, including marrying the current frame, if the same thread owns the VM after + disowning, and no other thread manages to own the VM in between. */ + if (threadIndex == GIV(disowningThreadIndex)) { + if (((flags & DisownVMForProcessorRelinquishFlag) != 0)) { + /* Presumably we have nothing to do; this primitive is typically called from the + background process. So we should /not/ try and activate any threads in the + pool; they will waste cycles finding there is no runnable process, and will + cause a VM abort if no runnable process is found. But we /do/ want to allow + FFI calls that have completed, or callbacks a chance to get into the VM; they + do have something to do. DisownVMForProcessorRelinquish indicates this. */ + GIV(relinquishing) = 0; + sqLowLevelMFence(); + } + if (((flags & DisownVMForFFICallFlag) != 0)) { + ioThreadLocalSetInFFICall(0); + } + + /* begin vmThreadAt: */ + assert(((threadIndex >= 0) && (threadIndex <= GIV(numThreads)))); + self_in_CogVMThread = (threadIndex > 0 + ? GIV(threads)[threadIndex] + : ((CogVMThread *) null)); + + /* begin setVmThreadState: */ + currentState = atomic_load((&((self_in_CogVMThread->state)))); + if (currentState != CTMAssignableOrInVM) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMAssignableOrInVM = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMAssignableOrInVM = CTMUnavailable) */); + break; + case CTMUnavailable: + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((self_in_CogVMThread->state))), CTMAssignableOrInVM); + GIV(disowningThreadIndex) = 0; + GIV(disowningCStackPointer) = null; +# if !NDEBUG + assert(((vmThreadAt(threadIndex))->stackPtrIndex) == stackPointerIndexForFrameWithSP(GIV(framePointer), GIV(stackPointer))); +# endif // !NDEBUG + + return 0; + } + if (GIV(disowningThreadIndex)) { + assert(!((GIV(disowningThreadIndex) == threadIndex))); + preemptDisowningThread(); + assert(GIV(disowningThreadIndex) == 0); + } + } + + /* begin vmThreadAt: */ + assert(((threadIndex >= 0) && (threadIndex <= GIV(numThreads)))); + vmThread = (threadIndex > 0 + ? GIV(threads)[threadIndex] + : ((CogVMThread *) null)); + assert((vmThread) + && (((vmThread->index)) == threadIndex)); + + /* begin acquireVMFor: */ + assert((vmThread->index) == ioThreadLocalGetThreadIndex()); + + /* Check if vmThreadState is any of CTMUnavailable (2), CTMAssignableOrInVM (3), or CTMWantingOwnership (4) */ +# if NDEBUG + assert((((vmThreadState(vmThread)) >= CTMUnavailable) && ((vmThreadState(vmThread)) <= CTMWantingOwnership))); +# else + threadState = atomic_load((&((vmThread->state)))); + assert(((threadState >= CTMUnavailable) && (threadState <= CTMWantingOwnership))); +# endif // NDEBUG + + + /* do we really need CTMUnavailable?? eem 4/3/2026 */ + threadIndexSqInt = (vmThread->index); + + /* begin tryLockVMOwnerTo: */ + assert(!((threadIndexSqInt == 0))); + expected = 0; + okSqInt = (atomic_compare_exchange_strong((&GIV(vmOwner)), (&expected), threadIndexSqInt)) + || ((/* We may already be vmOwner. The current vmOwner will be stored in expected. + However, if an unknown owner is present, we cannot assume that's us! */ + + (expected == threadIndexSqInt) + && (threadIndexSqInt != CTMUnknownOwner))); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(threadIndexSqInt, okSqInt); + } + if (okSqInt) { + /* begin setVmThreadState: */ + currentState = atomic_load((&((vmThread->state)))); + if (currentState != CTMAssignableOrInVM) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMAssignableOrInVM = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMAssignableOrInVM = CTMUnavailable) */); + break; + case CTMUnavailable: + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((vmThread->state))), CTMAssignableOrInVM); + } + else { + /* begin setVmThreadState: */ + currentState = atomic_load((&((vmThread->state)))); + if (currentState != CTMWantingOwnership) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMWantingOwnership = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + assert(0 /* (CTMWantingOwnership = CTMAssignableOrInVM) */); + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMWantingOwnership = CTMUnavailable) */); + break; + case CTMUnavailable: + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((vmThread->state))), CTMWantingOwnership); + while (1) { + threadIndexSqInt = (vmThread->index); + + /* begin tryLockVMOwnerTo: */ + assert(!((threadIndexSqInt == 0))); + expected = 0; + okSqInt = (atomic_compare_exchange_strong((&GIV(vmOwner)), (&expected), threadIndexSqInt)) + || ((/* We may already be vmOwner. The current vmOwner will be stored in expected. + However, if an unknown owner is present, we cannot assume that's us! */ + + (expected == threadIndexSqInt) + && (threadIndexSqInt != CTMUnknownOwner))); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(threadIndexSqInt, okSqInt); + } + if (okSqInt) break; + if ((vmThread->priority)) { + waitingPriorityIsAtLeast((vmThread->priority)); + } + if (!((atomic_load((&GIV(vmOwner)))) == ((vmThread->index)))) { + ioWaitOnOSSemaphore((&((vmThread->osSemaphore)))); + } + } + } + assertProcessorStackPointersBelongToCurrentThread(); + GIV(vmOSThread) = (vmThread->osThread); + + /* begin setVmThreadState: */ + currentState = atomic_load((&((vmThread->state)))); + if (currentState != CTMAssignableOrInVM) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMAssignableOrInVM = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMAssignableOrInVM = CTMUnavailable) */); + break; + case CTMUnavailable: + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((vmThread->state))), CTMAssignableOrInVM); + disownCount -= 1; + if ((recordFFIOrThreadTrace()) + && (!traced)) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + thingOne = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceOwnVM) << TraceTypeShift)))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = thingOne; + GIV(traceLog)[GIV(traceLogIndex) + 2] = disownResult; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + if (((flags & DisownVMForProcessorRelinquishFlag) != 0)) { + /* Presumably we have nothing to do; this primitive is typically called from the + background process. So we should /not/ try and activate any threads in the + pool; they will waste cycles finding there is no runnable process, and will + cause a VM abort if no runnable process is found. But we /do/ want to allow + FFI calls that have completed, or callbacks a chance to get into the VM; they + do have something to do. DisownVMForProcessorRelinquish indicates this. */ + GIV(relinquishing) = 0; + sqLowLevelMFence(); + } + else { + ioThreadLocalSetInFFICall(0); + } + if (GIV(disowningThreadIndex)) { + if (threadIndex == GIV(disowningThreadIndex)) { + GIV(disowningThreadIndex) = 0; + GIV(disowningCStackPointer) = null; +# if !NDEBUG + assert(((vmThreadAt(threadIndex))->stackPtrIndex) == stackPointerIndexForFrameWithSP(GIV(framePointer), GIV(stackPointer))); +# endif // !NDEBUG + + return 0; + } + preemptDisowningThread(); + assert(GIV(disowningThreadIndex) == 0); + } + + /* We've been preempted; we must restore state and update the threadId + in our process, and may have to put the active process to sleep. */ + + /* begin restoreVMStateFor:andFlags: */ + assert(GIV(disowningThreadIndex) == 0); + sched = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((sched + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + if (((flags & OwnedVMFromForeignThreadFlag) != 0)) { + myProc = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ForeignCallbackProcess) << (shiftForWord())))))); + assert(myProc != (nilObject())); + + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ForeignCallbackProcess, GIV(specialObjectsOop), GIV(nilObj))); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(GIV(nilObj) & (tagMask())))) + && (oopisLessThan(GIV(nilObj), GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ForeignCallbackProcess) << (shiftForWord()))))),GIV(nilObj)); + } + else { + myProc = (vmThread->boundProcess); + } + assert(couldBeProcess(myProc)); + assert(!((!myProc))); + if ((myProc != GIV(nilObj)) + && (activeProc != myProc)) { + if ((longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))))) == GIV(nilObj)) { + /* If the activeProcess doesn't have a context yet, it needs one from which we can resume later. + This mostly only happens when a threadSwitchIfNecessary:from: ends up switching to a thread that's CTMUnavailable (this thread). + See the comment in threadSwitchIfNecessary:from: */ + + /* begin ensureProcessHasContext: */ + if ((longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))))) == GIV(nilObj)) { + assert(activeProc == (activeProcess())); + + /* The instructionPointer is popped from the stack in 'externalSetStackPageAndPointersForSuspendedContextOfProcess:' */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + + /* We at least need to externalize the stack pointers to enable a thread switch... */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l1; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l1: + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, activeProc, activeContext)); + assert(isNonImmediate(activeProc)); + if (oopisGreaterThanOrEqualTo(activeProc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeContext & (tagMask())))) + && (oopisLessThan(activeContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(activeProc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(activeProc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),activeContext); + } + } + else { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(myProc)) + && (!(isForwarded(myProc)))); + assert(validStorePointerUncheckedArgs(MyListIndex, myProc, nilObject())); + longAtput((void *)((myProc + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),GIV(nilObj)); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ActiveProcessIndex, sched, myProc)); + assert(isNonImmediate(sched)); + if (oopisGreaterThanOrEqualTo(sched, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(myProc & (tagMask())))) + && (oopisLessThan(myProc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(sched + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(sched); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((sched + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord()))))),myProc); + } + if ((vmThread->cStackPointer)) { + GIV(newMethod) = (vmThread->newMethodOrNull); + GIV(argumentCount) = (vmThread->argumentCount); + cFramePointer = (vmThread->cFramePointer); + cStackPointer = (vmThread->cStackPointer); + + /* begin setCFramePointer:setCStackPointer: */ + assert(!(((!cFramePointer) + || (!cStackPointer)))); + GIV(CStackPointer) = cStackPointer; + GIV(CFramePointer) = cFramePointer; + assertSavedCStackPointersWellAligned(); + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if (myProc) { + assert(couldBeProcess(myProc)); + context = longAt((void *)((myProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + assert(isContext(context)); + if (((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert((vmThread->inMachineCode) == isMachineCodeFrame(frameOfMarriedContext(context))); + } + else { + assert((vmThread->inMachineCode) == (((fetchPointerofObject(InstructionPointerIndex, context)) >> 3)) < 0); + } + + /* begin externalSetStackPageAndPointersForSuspendedContextOfProcess: */ + newContext = longAt((void *)((myProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + assert(isContext(newContext)); + if (((((longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert(checkIsStillMarriedContextcurrentFP(newContext, GIV(framePointer))); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(myProc)) + && (!(isForwarded(myProc)))); + assert(validStorePointerUncheckedArgs(SuspendedContextIndex, myProc, nilObject())); + longAtput((void *)((myProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),GIV(nilObj)); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(newContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + if (theFrame != ((thePage->headFP))) { + /* explicit assignment of suspendedContext can cause switch to interior frame. */ + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l3; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l3: + moveFramesInthroughtoPage(thePage, findFrameAboveinPage(theFrame, thePage), newPage); + + /* begin markStackPageLeastMostRecentlyUsed: */ + assert(newPage == ((GIV(mostRecentlyUsedPage)->nextPage))); + lastUsedPage = (newPage->nextPage); + while (((lastUsedPage->baseFP)) == 0) { + lastUsedPage = (lastUsedPage->nextPage); + } + if (((lastUsedPage->nextPage)) == newPage) { + goto l2; + } + (((newPage->prevPage))->nextPage = (newPage->nextPage)); + (((newPage->nextPage))->prevPage = (newPage->prevPage)); + (((lastUsedPage->prevPage))->nextPage = newPage); + (newPage->prevPage = (lastUsedPage->prevPage)); + (newPage->nextPage = lastUsedPage); + (lastUsedPage->prevPage = newPage); + assert(pageListIsWellFormed()); + /* end markStackPageLeastMostRecentlyUsed: */ +l2:; + } + assert(((thePage->headFP)) == theFrame); + } + else { + thePage = makeBaseFrameFor(newContext); + theFrame = (thePage->baseFP); + } + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + assert((vmThread->inMachineCode) == isMachineCodeFrame(GIV(framePointer))); + } + + /* hmmm, there is an issue here with out-of-order callback returns on the same thread. + if a callback returns out-of-order we need to unwind the nestedDisownStates to match. + Some comparison against stack pointers is necessary. Some stacks may grow up. + We could reaosnably limit the maximum nesting (64, 128, etc), include it in a field in + the flags, and use this to unwind. We could include a nesting number in DisownState. */ + if ((vmThread->bindingPointKey)) { + if (((vmThread->bindingPointKey)) == ((vmThread->cStackPointer))) { + assert(!((vmThread->nestedDisownStates))); + if ((aob = (vmThread->affinityOnBind))) { + aProcess = (vmThread->boundProcess); + + /* begin threadAffinityFieldOf:put: */ + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aProcess)) + && (!(isForwarded(aProcess)))); + assert(validStorePointerUncheckedArgs(ThreadAffinityIndex, aProcess, aob)); + longAtput((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(ThreadAffinityIndex) << (shiftForWord()))))),aob); + } + (vmThread->boundProcess = null); + (vmThread->bindingPointKey = null); + (vmThread->affinityOnBind = null); + } + else { + disownState = (vmThread->nestedDisownStates); + memcpy(vmThread, disownState, sizeof(DisownState)); + free(disownState); + } + } + assert(GIV(newMethod)); + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + assert((((vmThreadAt(threadIndex))->state)) == CTMAssignableOrInVM); +# if !NDEBUG + assert(((vmThreadAt(threadIndex))->stackPtrIndex) == stackPointerIndexForFrameWithSP(GIV(framePointer), GIV(stackPointer))); +# endif // !NDEBUG + + return 0; +} + + +/* Set the relevant state for disowningVMThread so that it can resume after + being preempted and set disowningVMThread to nil to indicate preemption. + + N.B. This should only be sent from ownVM:. + + There are essentially four things to do. + a) save the VM's notion of the current C stack pointers; these are + pointers into a thread's stack and must be saved and restored on thread + switch. b) save the VM's notion of the current Smalltalk execution point. + This is + simply the suspend half of a process switch that saves the current context + in the current process. + c) ensure the thread and the activeProcess are bound. i.e. if not nested + within a disownVM:/ownVM: callback, set the boundProcess to the active + process. d) save the in-primitive VM state, newMethod, argumentCount, etc + + ownVM: will restore the VM context as of disownVM: from the above when it + finds it has been preempted. */ + + /* CoInterpreterMT>>#preemptDisowningThread */ +static void +preemptDisowningThread(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt activeProc; + CogVMThread *disowningVMThread; + DisownState *nestedState; + sqInt objOop; + + assert(!((GIV(disowningThreadIndex) == 0))); + assert(!((GIV(disowningThreadIndex) == (getVMOwner())))); + + /* begin vmThreadAt: */ + assert(((GIV(disowningThreadIndex) >= 0) && (GIV(disowningThreadIndex) <= GIV(numThreads)))); + disowningVMThread = (GIV(disowningThreadIndex) > 0 + ? GIV(threads)[GIV(disowningThreadIndex)] + : ((CogVMThread *) null)); + assert(((vmThreadState(disowningVMThread)) == CTMUnavailable) + || ((vmThreadState(disowningVMThread)) == CTMWantingOwnership)); + assertCStackPointersBelongToDisowningThread(); + if (recordFFIOrThreadTrace()) { + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TracePreemptDisowningThread) << TraceTypeShift)))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = GIV(disowningThreadIndex); + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + + /* If there is already a boundProcess then preemptDisowningThread is being invoked + to preempt a disowning thread that is within a nested invocation. Save that nested state. */ + if ((disowningVMThread->boundProcess)) { + nestedState = malloc(sizeof(DisownState)); + if (!nestedState) { + error("out of C memory in preemptDisowningThread"); + } + memcpy(nestedState, disowningVMThread, sizeof(DisownState)); + (disowningVMThread->nestedDisownStates = nestedState); + } + + /* Save the current state. Must happen *after* saving outer state. */ + (disowningVMThread->newMethodOrNull = GIV(newMethod)); + (disowningVMThread->argumentCount = GIV(argumentCount)); + (disowningVMThread->cStackPointer = GIV(disowningCStackPointer)); + (disowningVMThread->cFramePointer = GIV(disowningCFramePointer)); +# if !NDEBUG + (disowningVMThread->inMachineCode = GIV(disowningInstructionPointer) < (startOfMemory())); +# endif + + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + if ((disowningVMThread->boundProcess)) { + assert((disowningVMThread->boundProcess) == activeProc); + assert(((disowningVMThread->bindingPointKey))); + } + else { + assert(!((disowningVMThread->bindingPointKey))); + (disowningVMThread->boundProcess = activeProc); + (disowningVMThread->bindingPointKey = GIV(disowningCStackPointer)); + (disowningVMThread->affinityOnBind = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(ThreadAffinityIndex) << (shiftForWord()))))))); + } + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l1; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer) + BytesPerWord); + /* end ensureFrameIsMarried:SP: */ +l1: + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, activeProc, activeContext)); + assert(isNonImmediate(activeProc)); + if (oopisGreaterThanOrEqualTo(activeProc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeContext & (tagMask())))) + && (oopisLessThan(activeContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(activeProc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(activeProc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),activeContext); + + /* The instructionPointer must be pushed because the convention for inactive stack pages is that the + instructionPointer is top of stack. We need to know if this primitive is called from machine code + because the invariant that the return pc of an interpreter callee calling a machine code caller is + ceReturnToInterpreterPC must be maintained. */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + GIV(disowningThreadIndex) = 0; + GIV(disowningCStackPointer) = null; +} + + +/* Set PrimErrFFIException primitive failure and associated exceptionCode + (a.k.a. + secondaryErrorCode) and exceptionPC. Under control of the + ffiExceptionResponse flag, + if in a primitive with an error code and a thread-local flag indicates + we're in an FFI call, + then fail the primitive. + ffiExceptionResponse < 0 never fail (i.e. always crash) + ffiExceptionResponse = 0 fail if method has a primitive error code + (default) ffiExceptionResponse > 0 always fail */ + + /* CoInterpreterMT>>#primitiveFailForFFIException:at: */ +void +primitiveFailForFFIExceptionat(usqLong exceptionCode, usqInt pc) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cogMethod; + usqInt i; + usqInt initialIP; + sqInt methodHeader; + usqInt numArgs; + usqInt numTemps; + sqInt object; + sqInt rcvr; + sqInt vmThreadIndex; + + if ((((vmThreadIndex = ((sqInt) (ioThreadLocalGetInFFICall())))) == 0) + || (ffiExceptionResponse < 0)) { + return; + } + assert(vmThreadIndex >= 1); + assert((((sqInt) (ioThreadLocalGetThreadIndex()))) == vmThreadIndex); + ownVM(vmThreadIndex + ((((usqInt)((DisownVMForFFICallFlag + OwnVMForFFIExceptionFlag)) << ThreadIdShift)))); + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + GIV(secondaryErrorCode) = ((sqLong) exceptionCode); + GIV(exceptionPC) = pc; + GIV(primFailCode) = PrimErrFFIException; + if ((ffiExceptionResponse > 0) + || ((/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (methodUsesPrimitiveErrorCode(GIV(newMethod))))) { + assert(isCalloutPrimitiveIndex(primitiveIndexOf(GIV(newMethod)))); + + /* begin activateFailingPrimitiveMethod */ + assert(GIV(primFailCode) != 0); + assert(addressCouldBeObj(GIV(newMethod))); + assert(isCompiledMethod(GIV(newMethod))); + assert((primitiveIndexOf(GIV(newMethod))) != 0); + + /* begin justActivateNewMethod: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + cogMethod = null; + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + methodHeader = (cogMethod->methodHeader); + cogMethod = null; + } + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeader)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(addressCouldBeOop(rcvr)); + assert(!(isOopForwarded(rcvr))); + if (cogMethod) { + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + } + GIV(instructionPointer) = ceReturnToInterpreterPC(); + } + cogMethod = null; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + initialIP = ((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize; + if (cogMethod) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)cogMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + GIV(instructionPointer) = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + GIV(instructionPointer) = initialIP - 1; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + if (!cogMethod) { + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + } + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeader); + } + } + ceInvokeInterpret(); + + /* NOTREACHED */ + } +} + + +/* On some platforms, this primitive forces enqueued display updates to be + processed immediately. On others, it does nothing. + Override so that if the platform requires GUI activity to be restricted to + a given thread, we only force update if on the right thread. + */ + + /* CoInterpreterMT>>#primitiveForceDisplayUpdate */ +static void +primitiveForceDisplayUpdate(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!(((ioEventThreadAffinity()) > 0) + && ((ioEventThreadAffinity()) != (atomic_load((&GIV(vmOwner))))))) { + ioForceDisplayUpdate(); + } +} + + +/* With no arguments answer either nil if there is no long, or a three + element Array + of the size of the log in bytes, the address of the log and the address of + the index. + With one argument, take a buffer of the same size as owner log. Write the + owner log to the given ByteArray. All members of the owner log struct are + always written + as defined by the C struct. Answer the number of instances of + CogVMOwnerLog written into the ByteArray. */ + + /* CoInterpreterMT>>#primitiveGetOwnerLog */ +EXPORT(void) +primitiveGetOwnerLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt array; + char *bufferPointer; + sqInt bytesCopied; + sqInt bytesCopiedSqInt; + sqInt bytesToCopy; + sqInt classObj; + sqInt entriesToSpare; + sqInt index; + sqInt integer; + sqInt knownClassIndex; + sqInt logBuffer; + usqInt newObj; + usqInt numBytes; + sqInt objFormat; + sqInt startIndex; + sqInt valuePointer; + + if (GIV(argumentCount) > 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + if (!GIV(argumentCount)) { + if (!(GIV(ownerLog))) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); + return; + } + classObj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))); + + /* begin eeInstantiateSmallClass:numSlots: */ + assert((rawHashBitsOf(classObj)) != 0); + knownClassIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + objFormat = (((usqInt)((((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((knownClassIndex != 0) + && ((knownClassAtIndex(knownClassIndex)) != GIV(nilObj))); + assert(((objFormat < (firstByteFormat()) + ? objFormat + : objFormat & (byteFormatMask()))) == (instSpecOfClass(knownClassAtIndex(knownClassIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (3 * BytesPerOop); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + array = 0; + goto l2; + } + } + long64Atput((void *)(newObj),((((((usqLong) 3)) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + knownClassIndex); + GIV(freeStart) += numBytes; + array = newObj; + /* end eeInstantiateSmallClass:numSlots: */ +l2: + valuePointer = (((OwnerLogSize * (sizeof(CogVMOwnerLog))) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(0, array, valuePointer)); + longAtput((void *)((array + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&(GIV(ownerLog)))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(1, array, valuePointer)); + longAtput((void *)((array + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&(GIV(ownerLogIndex)))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(2, array, valuePointer)); + longAtput((void *)((array + BaseHeaderSize) + (2U << (shiftForWord()))),valuePointer); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),array); + return; + } + logBuffer = longAt(GIV(stackPointer)); + if (!(((!(logBuffer & (tagMask())))) + && ((isPureBitsNonImm(logBuffer)) + && ((numBytesOf(logBuffer)) >= (OwnerLogSize * (sizeof(CogVMOwnerLog))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + bufferPointer = firstIndexableField(logBuffer); + + /* begin copyLogTo: */ + if (!GIV(ownerLog)) { + bytesCopied = 0; + goto l1; + } + index = atomic_load((&GIV(ownerLogIndex))); + bytesCopiedSqInt = 0; + + /* NOTE: The ownerLogWrapped isn't synchronized atomically. + Therefore, if this primitive is called exactly when we start wrapping the very first time, we'll read the wrong value here. + However, as this is very unlikely, don't bother fixing this (yet). */ + if (GIV(ownerLogWrapped)) { + /* NOTE: The owner log is still written to whilst this primitive is running. + Therefore copy only 90% of the log, such that the remaining 10% can be spared + to still be written. */ + entriesToSpare = OwnerLogSize / 10; + startIndex = (((index + entriesToSpare) < (OwnerLogSize - 1)) ? (index + entriesToSpare) : (OwnerLogSize - 1)); + bytesToCopy = (OwnerLogSize - startIndex) * (sizeof(CogVMOwnerLog)); + memcpy(bufferPointer, GIV(ownerLog) + startIndex, bytesToCopy); + bytesCopiedSqInt += bytesToCopy; + } + bytesToCopy = index * (sizeof(CogVMOwnerLog)); + memcpy(bufferPointer + bytesCopiedSqInt, GIV(ownerLog), bytesToCopy); + bytesCopied = bytesCopiedSqInt + bytesToCopy; + /* end copyLogTo: */ +l1: + integer = bytesCopied / (sizeof(CogVMOwnerLog)); + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); +} + + +/* Attempt to bind the receiver to the thread affinity of the argument or + nil, where the receiver is a Process. + The thread affinity may be an integer where: + 0 - means no thread affinity, the process is free to run on any thread. + > 0 - positive values mean the process has to run on the thread with this + specific index. + < 0 - negative values mean the process may run on on any thread **APART** + from the thread + with the absolute value of the index. + + Usually values of 1, -1 and 0 are used. + Thread number 1 is the thread the VM started with. On some OSes this + thread has special priviliges. + I.e. on macOS only thread 1 can make draw calls. + Therefore it is mostly important whether a thread must run on thread 1, + must **not** run on thread 1 + or whether it doesn't care. + + If successful the VM will ensure that there is at least one compatible + thread active. */ + + /* CoInterpreterMT>>#primitiveProcessBindToThreadAffinity */ +static void +primitiveProcessBindToThreadAffinity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activePriority; + CogVMThread *affinedThread; + sqInt aProcess; + sqInt currentAffinity; + int existing; + sqInt newAffinity; + sqInt newAffinityOop; + sqInt oop; + sqInt threadAffinityOop; + sqInt waitingPriority; + + newAffinityOop = longAt(GIV(stackPointer)); + aProcess = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(((newAffinityOop == GIV(nilObj)) + || (((((newAffinityOop) & 7) == 1)) + && (newAffinityOop != ConstZero))) + && ((/* isPointers: */ + ((!(aProcess & (tagMask())))) + && (((byteAt((void *)(aProcess + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((slotSizeOf(aProcess)) >= (ThreadAffinityIndex + 1))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + newAffinity = (newAffinityOop == GIV(nilObj) + ? 0 + : (newAffinityOop >> 3)); + if ((SQABS(newAffinity)) >= ((1U << ThreadIdShift) - 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrLimitExceeded; + return; + } + + /* Check if the process is temporarily affined and executing external code. + If so simply assighn the new affinity to the affinityOnBind field of its thread. */ + threadAffinityOop = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(ThreadAffinityIndex) << (shiftForWord())))))); + currentAffinity = /* threadAffinityOfAffinityOop: */ + ((((threadAffinityOop) & 7) == 1) + ? (threadAffinityOop >> 3) + : 0); + if ((affinedThread = affinedThreadOrNilForProcesscurrentAffinityoop(aProcess, currentAffinity, threadAffinityOop))) { + if (((affinedThread->boundProcess)) == aProcess) { + (affinedThread->affinityOnBind = newAffinityOop); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return; + } + } + else { + if (currentAffinity > 0) { + if (!(startThreadForThreadIndex(currentAffinity))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrLimitExceeded; + return; + } + } + } + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + waitingPriority = atomic_load((&GIV(maxWaitingPriority))); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + activePriority = (oop >> 3); + if ((aProcess == (activeProcess())) + && ((currentAffinity != 0) + && (!(vmOwnerIsCompatibleWith(currentAffinity))))) { + if (activePriority < waitingPriority) { + /* begin reduceWaitingPriorityFrom:to: */ + existing = waitingPriority; + + /* This CPXCHG may fail, that's fine though, as there may have been + another thread that increased the priority in the meantime. + In that case that threads priority is the correct one to use. */ + atomic_compare_exchange_strong((&GIV(maxWaitingPriority)), (&existing), activePriority); + } + threadSwitchIfNecessaryfrom(aProcess, CSThreadBind); + } +} + + +/* Answer the receiver's current threadAffinity or nil, where the receiver is + a Process. + If the threadAffinity is positive then the receiver is bound to the thread + with that id. + If the threadAffinity is negative then the receiver is excluded from + running on the thread with that id. + */ + + /* CoInterpreterMT>>#primitiveProcessBoundThreadId */ +EXPORT(void) +primitiveProcessBoundThreadId(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aProcess; + sqInt id; + sqInt oop; + + aProcess = longAt(GIV(stackPointer)); + id = threadAffinityOfProcess(aProcess); + oop = (id + ? (((usqInt)id << 3) | 1) + : GIV(nilObj)); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} + + +/* Relinquish the processor for up to the given number of microseconds. + The exact behavior of this primitive is platform dependent. + Override to check for waiting threads. */ + + /* CoInterpreterMT>>#primitiveRelinquishProcessor */ +static void +primitiveRelinquishProcessor(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + volatile usqIntptr_t currentCFramePointer; + volatile usqIntptr_t currentCStackPointer; + sqInt disownResult; + sqInt microSecs; + + microSecs = longAt(GIV(stackPointer)); + if (!((((microSecs) & 7) == 1))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + assert((fetchPointerofObject(MyListIndex, activeProcess())) == (nilObject())); + assert(!GIV(relinquishing)); + + /* DO NOT allow relinquishing the processor while we are profiling since this + may skew the time base for our measures (it may reduce processor speed etc). + Instead we go full speed, therefore measuring the precise time we spend in the + inner idle loop as a busy loop. */ + if (!GIV(nextProfileTick)) { + currentCStackPointer = GIV(CStackPointer); + currentCFramePointer = GIV(CFramePointer); + disownResult = disownVM(DisownVMForProcessorRelinquishFlag); + assert((((((usqInt)(disownResult)) >> ThreadIdShift) & DisownVMForProcessorRelinquishFlag) != 0)); + assert(GIV(relinquishing)); + ioRelinquishProcessorForMicroseconds((microSecs >> 3)); + ownVM(disownResult); + assert(!(GIV(relinquishing))); + assert((vmThreadState(currentVMThread())) == CTMAssignableOrInVM); + assert(currentCStackPointer == GIV(CStackPointer)); + assert(currentCFramePointer == GIV(CFramePointer)); + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + + /* In simulation we allow ioRelinquishProcessorForMicroseconds: to fail so that + we can arrange that the simulator responds to input events promptly. This + *DOES NOT HAPPEN* in the real vm. */ + } + + /* Presumably we have nothing to do; this primitive is typically called from the + background process. So we should /not/ try and activate any threads in the + pool; they will waste cycles finding there is no runnable process, and will + cause a VM abort if no runnable process is found. But we /do/ want to allow + FFI calls that have completed, or callbacks a chance to get into the VM; they + do have something to do. DisownVMForProcessorRelinquishFlag indicates this. */ + assert((fetchPointerofObject(MyListIndex, activeProcess())) == (nilObject())); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; +} + + +/* Force the given rectangular section of the Display to be copied to the + screen. On some platforms, this primitive forces enqueued display updates + to be processed immediately. On others, it does nothing. + Override so that if the platform requires GUI activity to be restricted to + a given thread, we ony force update if on the right thread. */ + + /* CoInterpreterMT>>#primitiveShowDisplayRect */ +static void +primitiveShowDisplayRect(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bottom; + sqInt integerPointer; + sqInt left; + sqInt right; + sqInt top; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + bottom = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + bottom = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + top = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + top = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + right = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + right = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + left = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + left = 0; + } + if (!GIV(primFailCode)) { + updateDisplayLeftTopRightBottom(left, top, right, bottom); + if (!(((ioEventThreadAffinity()) > 0) + && ((ioEventThreadAffinity()) != (atomic_load((&GIV(vmOwner))))))) { + ioForceDisplayUpdate(); + } + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } +} + + +/* Answer the VM's current thread's Id */ + + /* CoInterpreterMT>>#primitiveVMCurrentThreadId */ +EXPORT(void) +primitiveVMCurrentThreadId(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integer; + + integer = atomic_load((&GIV(vmOwner))); + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); +} + + +/* Print all the stacks of all running processes, including those that are + currently suspended. + */ +/* useful for VM debugging */ +/* Print all the stacks of all running processes, including those that are + currently suspended. + Override to print the stacks of processes in external code (i.e. that are + threading). + */ + + /* CoInterpreterMT>>#printAllStacks */ +void +printAllStacks(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt boundProcess; + sqInt classPointer; + sqInt first; + sqInt followingWord; + usqInt followingWordAddress; + sqInt i; + sqInt linkedListClass; + sqInt minProcessInstSize; + sqInt myList; + sqInt myListClass; + usqInt numSlots; + sqInt objOop; + sqInt objSqInt; + usqInt p; + sqInt prevObj; + sqInt prevPrevObj; + sqInt pri; + sqInt proc; + sqInt processClass; + sqInt processList; + sqInt schedLists; + sqInt startObject; + sqInt tagBits; + + /* may not be an instance of process. may in exceptional circumstances be nilObject */ + proc = activeProcess(); + printNameOfClasscount(/* fetchClassOf: */ + ((tagBits = proc & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(proc)), 5); + + /* begin space */ + printChar(' '); + printHex(proc); + print(" priority "); + printNum(quickFetchIntegerofObject(PriorityIndex, proc)); + cr(); + if (GIV(framePointer)) { + printCallStack(); + } + else { + printProcessStack(proc); + } + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + schedLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + linkedListClass = null; + + /* then the runnable processes */ + p = (GIV(highestRunnableProcessPriority) + ? GIV(highestRunnableProcessPriority) + : numSlotsOf(schedLists)); + for (pri = (p - 1); pri >= 0; pri += -1) { + processList = longAt((void *)((schedLists + BaseHeaderSize) + ((((usqInt)(pri) << (shiftForWord())))))); + if (!(isEmptyList(processList))) { + if (proc == GIV(nilObj)) { + proc = longAt((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + } + cr(); + print("processes at priority "); + printNum(pri + 1); + printProcsOnList(processList); + } + if (!linkedListClass) { + linkedListClass = fetchClassOfNonImm(processList); + } + } + if (!linkedListClass) { + classPointer = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord())))))); + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + linkedListClass = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(linkedListClass)); + if ((!((longAt((void *)(linkedListClass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + linkedListClass = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, linkedListClass); + } + } + if (proc == GIV(nilObj)) { + cr(); + print("Cannot find a runnable process. Cannot therefore determine class Process. Cannot therefore print suspended processes"); + return; + } + cr(); + print("suspended processes"); + + /* Find the root of the Process hierarchy. It is the class, or superclass, + of a process, that has inst size at least large enough to include myList */ + processClass = (!(proc == GIV(nilObj)) + ? /* fetchClassOf: */ + ((tagBits = proc & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(proc)) + : 0); + minProcessInstSize = MyListIndex + 1; + while ((instanceSizeOf(superclassOf(processClass))) >= minProcessInstSize) { + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((processClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, processClass, objOop); + } + processClass = objOop; + } + minProcessInstSize = instanceSizeOf(processClass); + + /* look for all subInstances of process that have a context as a suspendedContext and are on a list other than a LinkedList */ + + /* begin allObjectsDoSafely: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + if (((longAt((void *)(objSqInt))) & (classIndexMask())) > (lastClassIndexPun())) { + if ((((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + && (((numSlotsOf(objSqInt)) >= minProcessInstSize) + && ((isContext(longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))))) + && (isKindOfClass(objSqInt, processClass))))) { + /* Is the process waiting on some delaying list? This will be a subclass of LinkedList. + If so, assume it is blocked on the list. */ + myList = longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord())))))); + if ((myList != GIV(nilObj)) + && ((((myListClass = fetchClassOfNonImm(myList))) != linkedListClass) + && (isKindOfClass(myList, linkedListClass)))) { + printProcessStack(objSqInt); + } + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } + first = 1; + for (i = 1; i <= GIV(numThreads); i += 1) { + if ((boundProcess = ((vmThreadAt(i))->boundProcess))) { + if (first) { + cr(); + print("processes in external code"); + first = 0; + } + printProcessStack(boundProcess); + } + } +} + + /* CoInterpreterMT>>#printLogEntryAt: */ +static NoDbgRegParms void +printLogEntryAt(sqInt i) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classMethodOrProcess; + sqInt selectorOrExtra; + sqInt source; + usqInt traceType; + sqInt vmOwnerTraceTypeAndSource; + + vmOwnerTraceTypeAndSource = GIV(traceLog)[i]; + traceType = (((usqInt)(vmOwnerTraceTypeAndSource)) >> TraceTypeShift) & 0xFF; + source = vmOwnerTraceTypeAndSource & (((1U << TraceTypeShift)) - 1); + + /* a.k.a. thingOne */ + classMethodOrProcess = GIV(traceLog)[i + 1]; + + /* a.k.a. thingTwo */ + selectorOrExtra = GIV(traceLog)[i + 2]; + fprintf(GIV(transcript), + "vmo:%d\t", + ((int) (((usqInt)(vmOwnerTraceTypeAndSource)) >> TraceOwnerShift))); + switch (traceType) { + case TraceSend: + /* begin printSendLogEntry:source:selector: */ + print((((source & CSFromMachineCode) != 0) + ? "m " + : "i ")); + printNameOfClasscount(classMethodOrProcess, 5); + print(">>"); + printStringOf(selectorOrExtra); + break; + case TraceStackOverflow: + print("stack overflow"); + break; + case TraceContextSwitch: + fprintf(GIV(transcript), + "context switch from %p to %p", + ((void *)classMethodOrProcess), + ((void *)selectorOrExtra)); + break; + case TraceBlockActivation: + fprintf(GIV(transcript), + " [] in %p", + ((void *)classMethodOrProcess)); + break; + case TraceBlockCreation: + fprintf(GIV(transcript), + "create [] %p", + ((void *)classMethodOrProcess)); + break; + case TraceIncrementalGC: + print("incrementalGC"); + break; + case TraceFullGC: + print("fullGC"); + break; + case TraceCodeCompaction: + print("compactCode"); + break; + case TraceVMCallback: + fprintf(GIV(transcript), + "callback %p", + ((void *)classMethodOrProcess)); + break; + case TraceVMCallbackReturn: + fprintf(GIV(transcript), + "callback return %p type %d", + ((void *)classMethodOrProcess), + ((int) source)); + break; + case TraceAttemptOwnVM: + fprintf(GIV(transcript), + "ownVM (try) by %d disownResult:%x", + ((int) (selectorOrExtra & (((1U << ThreadIdShift)) - 1))), + ((int) selectorOrExtra)); + break; + case TraceOwnVM: + fprintf(GIV(transcript), + "ownVM by %d activeProc %p disownResult:%x", + ((int) (selectorOrExtra & (((1U << ThreadIdShift)) - 1))), + ((void *)classMethodOrProcess), + ((int) selectorOrExtra)); + break; + case TraceDisownVM: + fprintf(GIV(transcript), + "disownVM activeProc %p flags:%x", + ((void *)classMethodOrProcess), + ((int) selectorOrExtra)); + break; + case TraceThreadSwitch: + fprintf(GIV(transcript), + "thread switch %d -> %d", + ((int) classMethodOrProcess), + ((int) selectorOrExtra)); + break; + case TracePreemptDisowningThread: + fprintf(GIV(transcript), + "preempt disowning thread %d", + ((int) classMethodOrProcess)); + break; + default: + print("UNKNOWN EVENT"); + } + if ((((traceType >= TraceStackOverflow) && (traceType <= TracePreemptDisowningThread))) + && (source > 1)) { + /* begin space */ + printChar(' '); + print(traceSources[((usqInt)(source)) >> 1]); + } + cr(); +} + + +/* Print all threads other than those that are uninitialized. */ + + /* CoInterpreterMT>>#printThreads */ +void +printThreads(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt state; + static const char * stateNames[] = { + "unknown owner", "unused", "initializing", "unavailable", "assignableOrInVM", + "wantingOwnership" + }; + CogVMThread *vmThread; + + for (i = 1; i <= GIV(numThreads); i += 1) { + vmThread = vmThreadAt(i); + state = atomic_load((&((vmThread->state)))); + if (!(((state >= 0) && (state <= CTMUninitialized)))) { + fprintf(GIV(transcript), + "thread %d %p %s prio %d newmeth %p argc %d boundProc %p nest: %p affinity: %p%s\n", + ((int) i), + vmThread, + stateNames[state + 1], + ((int) ((vmThread->priority))), + ((void *)((vmThread->newMethodOrNull))), + (vmThread->argumentCount), + ((void *)((vmThread->boundProcess))), + (vmThread->nestedDisownStates), + ((void *)((vmThread->affinityOnBind))), + (((vmThread->affinityOnBind)) == GIV(nilObj) + ? " (nil)" + : 0)); + } + } +} + + /* CoInterpreterMT>>#returnToSchedulingLoopAndReleaseVMOrWakeThread:source: */ +static NoDbgRegParms void +returnToSchedulingLoopAndReleaseVMOrWakeThreadsource(CogVMThread *vmThread, sqInt source) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogVMThread *activeThread; + sqInt index; + sqInt ownerIndex; + sqInt thingTwo; + + /* begin currentVMThread */ + index = atomic_load((&GIV(vmOwner))); + assert(((index >= 0) && (index <= GIV(numThreads)))); + activeThread = (index > 0 + ? GIV(threads)[index] + : ((CogVMThread *) null)); + ownerIndex = (vmThread + ? (vmThread->index) + : 0); + + /* begin recordThreadSwitchTo:source: */ + if (recordFFIOrThreadTrace()) { + thingTwo = atomic_load((&GIV(vmOwner))); + + /* begin recordTrace:source:thing:extra: */ + assert(source < ((1U << TraceTypeShift))); + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceThreadSwitch) << TraceTypeShift))))) + source); + GIV(traceLog)[GIV(traceLogIndex) + 1] = ownerIndex; + GIV(traceLog)[GIV(traceLogIndex) + 2] = thingTwo; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + if (vmThread) { + wakeVMThread(vmThread); + } + else { + /* begin releaseVM */ + /* begin setVMOwner: */ + assert(((getVMOwner()) == CTMUnknownOwner) + || ((getVMOwner()) == (ioThreadLocalGetThreadIndex()))); + assert((getVMOwner()) != 0); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(0, 1); + } + + /* TODO: We could make this a `release` ordering, which may perform better on ARM. */ + atomic_store((&GIV(vmOwner)), 0); + } + + /* I am not frightened of flying. + Any value will do. I don't mind. + Why should I be frightened of flying? + There's no reason for it. */ + _longjmp((activeThread->reenterThreadSchedulingLoop), 1); +} + + +/* Send the calllback message to Alien class with the supplied arg(s). Use + either the 1 arg + invokeCallbackContext: or the 4 arg invokeCallback:stack:registers:jmpbuf: + message, depending on what selector is installed in the + specialObjectsArray. Note that if invoking the + legacy invokeCallback:stack:registers:jmpbuf: we pass the + vmCallbackContext as the jmpbuf + argument (see reestablishContextPriorToCallback:). The arguments are raw C + addresses and + are converted to integer objects on the way. sendInvokeCallbackContext: & + returnAs:ThroughCallback:Context: along with ownVM: and disownVM: conspire + to save and + restore newMethod, argumentCount and primitiveFunctionPointer around a + callback. The VM depends on argumentCount being correct to cut-back the + correct number of + arguments on primitive return. Since this is an implicit send we need to + log it explicitly. + The return side is done via a primitive so that gets logged normally. */ +/* Override check stack alignment. */ +/* Override to add sanity assertions. */ + + /* CoInterpreterMT>>#sendInvokeCallbackContext: */ +sqInt +sendInvokeCallbackContext(VMCallbackContext *vmCallbackContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTag; + CogMethod *cogMethod; + usqInt i; + usqInt initialIP; + sqInt methodHeader; + usqInt numArgs; + usqInt numTemps; + sqInt object; + sqInt rcvr; + sqInt thunkp; + + /* begin assertSaneThreadAndProcess */ + assert(vmIsOwned()); + assert((vmThreadState(currentVMThread())) == CTMAssignableOrInVM); + assert((fetchPointerofObject(MyListIndex, activeProcess())) == (nilObject())); + assertValidProcessorStackPointersForIndex(atomic_load((&GIV(vmOwner)))); + assertCStackWellAligned(); + if (recordPrimTrace()) { + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorInvokeCallback) << (shiftForWord()))))))); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + thunkp = ((sqInt) ((vmCallbackContext->thunkp))); + + /* begin recordInvokeCallback: */ + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceVMCallback) << TraceTypeShift)))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = (((sqInt)thunkp)); + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + + /* begin fetchClassTagOfNonImm: */ + classTag = (longAt((void *)(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord()))))))))) & (classIndexMask()); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorInvokeCallback) << (shiftForWord())))))); + if (!(lookupInMethodCacheSelclassTag(GIV(messageSelector), classTag))) { + if (lookupOrdinaryNoMNUEtcInClass(classForClassTag(classTag))) { + return 0; + } + } + assert(((debugCallbackInvokes += 1)) > 0); + + /* self assert: debugCallbackInvokes < 3802. */ + + /* begin saveCStackStateForCallbackContext: */ + (vmCallbackContext->savedCStackPointer = ((void *)GIV(CStackPointer))); + (vmCallbackContext->savedCFramePointer = ((void *)GIV(CFramePointer))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord()))))))); + if ((argumentCountOf(GIV(newMethod))) == 4) { + object = positive64BitIntegerFor(((usqInt)((vmCallbackContext->thunkp)))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + object = positive64BitIntegerFor(((usqInt)((vmCallbackContext->stackp)))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + object = positive64BitIntegerFor(((usqInt)((vmCallbackContext->intregargsp)))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + object = positive64BitIntegerFor(((usqInt)vmCallbackContext)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + + /* begin justActivateNewMethod: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + cogMethod = null; + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + methodHeader = (cogMethod->methodHeader); + } + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeader)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(addressCouldBeOop(rcvr)); + assert(!(isOopForwarded(rcvr))); + if (cogMethod) { + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + } + GIV(instructionPointer) = ceReturnToInterpreterPC(); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + initialIP = ((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize; + if (cogMethod) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)cogMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + GIV(instructionPointer) = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + GIV(instructionPointer) = initialIP - 1; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + if (!cogMethod) { + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + } + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeader); + } + } + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + + /* begin checkForStackOverflow */ + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (GIV(stackPointer) < ((GIV(stackPage)->realStackLimit))) { + handleStackOverflow(); + } + assert((frameReceiver(GIV(framePointer))) == (splObj(ClassAlien))); + + /* begin enterSmalltalkExecutiveFromCallback */ + enterSmalltalkExecutive(); + + /* not reached */ + return 1; +} + + +/* useful for debugging, so don't inline */ + + /* CoInterpreterMT>>#threadAffinityOfProcess: */ +static NoDbgRegParms sqInt +threadAffinityOfProcess(sqInt aProcess) +{ + sqInt threadId; + + threadId = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(ThreadAffinityIndex) << (shiftForWord())))))); + + /* begin threadAffinityOfAffinityOop: */ + return ((((threadId) & 7) == 1) + ? (threadId >> 3) + : 0); +} + + +/* Enter a loop attempting to run the VM with the highest priority process + and blocking on the thread's OS semaphore when unable to run that process. + We will return to this via threadSwitchIfNecessary:from: which is called + in the + middle of transferTo:from: once the active process has been stored in the + scheduler. */ +/* for Slang and the ifTrue: below... */ + + /* CoInterpreterMT>>#threadSchedulingLoopImplementation: */ +static NoDbgRegParms void +threadSchedulingLoopImplementation(CogVMThread *vmThread) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + sqInt aMethodObj; + sqInt currentState; + int expected; + sqInt header; + StackPage *lastUsedPage; + sqInt methodHeader; + sqInt newContext; + StackPage *newPage; + sqInt objOop; + sqInt ok; + sqInt senderOop; + char *theFrame; + StackPage *thePage; + sqInt threadAffinity; + sqInt threadIndex; + sqInt top; + CogVMThread *willingThread; + + _setjmp((vmThread->reenterThreadSchedulingLoop)); + do { + assert((vmThreadState(vmThread)) == CTMAssignableOrInVM); + threadIndex = (vmThread->index); + + /* begin tryLockVMOwnerTo: */ + assert(!((threadIndex == 0))); + expected = 0; + ok = (atomic_compare_exchange_strong((&GIV(vmOwner)), (&expected), threadIndex)) + || ((/* We may already be vmOwner. The current vmOwner will be stored in expected. + However, if an unknown owner is present, we cannot assume that's us! */ + + (expected == threadIndex) + && (threadIndex != CTMUnknownOwner))); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(threadIndex, ok); + } + if (ok) { + /* Yay, we're the VM owner! + If relinquishing is true, then primitiveRelinquishProcessor has disowned the + VM and only a returning call or callback should take ownership in that case. */ + if (!GIV(relinquishing)) { + /* begin tryToExecuteSmalltalk: */ + assert(vmOwnerIs((vmThread->index))); + assert((ioThreadLocalGetThreadIndex()) == ((vmThread->index))); + sqLowLevelMFence(); + if (GIV(disowningThreadIndex)) { + preemptDisowningThread(); + assert(GIV(disowningThreadIndex) == 0); + activeProc = wakeHighestPriority(); + if (activeProc) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(activeProc)) + && (!(isForwarded(activeProc)))); + assert(validStorePointerUncheckedArgs(MyListIndex, activeProc, GIV(nilObj))); + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + activeProc = GIV(nilObj); + } + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ActiveProcessIndex, objOop, activeProc)); + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeProc & (tagMask())))) + && (oopisLessThan(activeProc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord()))))),activeProc); + } + else { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + } + + /* There is a special case here. + When the VM has relinquished, but then another thread finishes external code execution, there may no longer be a process to run. + However, the relinquishing flag may already have been reset by another thread that has owned the VM again. */ + if (activeProc == GIV(nilObj)) { + goto l3; + } + threadAffinity = threadAffinityOfProcess(activeProc); + if (vmOwnerIsCompatibleWith(threadAffinity)) { + assert((fetchPointerofObject(MyListIndex, activeProcess())) == (nilObject())); + + /* If we switch threads in threadSwitchIfNecessary:from:, the interpreter state is likely + already in the correct state. + In that case, there is no suspended context and nothing to restore. We can just continue + execution. + If there is a suspended context, assume that we need to restore the state from that. */ + if ((longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))))) != GIV(nilObj)) { + /* begin externalSetStackPageAndPointersForSuspendedContextOfProcess: */ + newContext = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + assert(isContext(newContext)); + if (((((longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert(checkIsStillMarriedContextcurrentFP(newContext, GIV(framePointer))); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(activeProc)) + && (!(isForwarded(activeProc)))); + assert(validStorePointerUncheckedArgs(SuspendedContextIndex, activeProc, nilObject())); + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),GIV(nilObj)); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(newContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + if (theFrame != ((thePage->headFP))) { + /* explicit assignment of suspendedContext can cause switch to interior frame. */ + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l1; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l1: + moveFramesInthroughtoPage(thePage, findFrameAboveinPage(theFrame, thePage), newPage); + + /* begin markStackPageLeastMostRecentlyUsed: */ + assert(newPage == ((GIV(mostRecentlyUsedPage)->nextPage))); + lastUsedPage = (newPage->nextPage); + while (((lastUsedPage->baseFP)) == 0) { + lastUsedPage = (lastUsedPage->nextPage); + } + if (((lastUsedPage->nextPage)) == newPage) { + goto l2; + } + (((newPage->prevPage))->nextPage = (newPage->nextPage)); + (((newPage->nextPage))->prevPage = (newPage->prevPage)); + (((lastUsedPage->prevPage))->nextPage = newPage); + (newPage->prevPage = (lastUsedPage->prevPage)); + (newPage->nextPage = lastUsedPage); + (lastUsedPage->prevPage = newPage); + assert(pageListIsWellFormed()); + /* end markStackPageLeastMostRecentlyUsed: */ +l2:; + } + assert(((thePage->headFP)) == theFrame); + } + else { + thePage = makeBaseFrameFor(newContext); + theFrame = (thePage->baseFP); + } + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + } + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + assert(!((isMachineCodeFrame(GIV(framePointer))))); + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + enterSmalltalkExecutive(); + + /* When we return here we should have already given up + the VM and so we cannot touch any interpreter state. */ + error("NOTREACHED"); + } + + /* begin returnToSchedulingLoopAndWakeThreadFor:source: */ + assert(!(vmOwnerIsCompatibleWith(threadAffinity))); + assert(threadAffinity != 0); + if (threadAffinity > 0) { + assert(((threadAffinity >= 1) && (threadAffinity <= GIV(numThreads)))); + returnToSchedulingLoopAndReleaseVMOrWakeThreadsource(GIV(threads)[threadAffinity], CSTryToExecuteSmalltalk); + } + else { + assert((getVMOwner()) == (-threadAffinity)); + + /* We know the thread affinity is 'any thread other then this one!'. */ + willingThread = ensureWillingThread(); + if (willingThread) { + returnToSchedulingLoopAndReleaseVMOrWakeThreadsource(willingThread, CSTryToExecuteSmalltalk); + } + } + /* end tryToExecuteSmalltalk: */ +l3:; + } + + /* tryToExecuteSmalltalk: may return if there's no runnable process. + Usually it doesn't return, but jumps straight back to the _setjmp at the top of this function, + so this is only reached in case there's no runnable process. + TODO: Do we need to saveRegisterStateForCurrentProcess here? */ + + /* begin releaseVM */ + /* begin setVMOwner: */ + assert(((getVMOwner()) == CTMUnknownOwner) + || ((getVMOwner()) == (ioThreadLocalGetThreadIndex()))); + assert((getVMOwner()) != 0); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(0, 1); + } + + /* TODO: We could make this a `release` ordering, which may perform better on ARM. */ + atomic_store((&GIV(vmOwner)), 0); + } + + /* begin waitForWork: */ + /* begin setVmThreadState: */ + currentState = atomic_load((&((vmThread->state)))); + if (currentState != CTMAssignableOrInVM) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMAssignableOrInVM = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMAssignableOrInVM = CTMUnavailable) */); + break; + case CTMUnavailable: + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((vmThread->state))), CTMAssignableOrInVM); + assert(!((vmOwnerIs((vmThread->index))))); + ioWaitOnOSSemaphore((&((vmThread->osSemaphore)))); + } while(1); +} + + +/* Invoked from transferTo:from: or primitiveProcessBindToThreadId to + switch threads if the new process is bound or affined to some other + thread. + */ + + /* CoInterpreterMT>>#threadSwitchIfNecessary:from: */ +static NoDbgRegParms void +threadSwitchIfNecessaryfrom(sqInt newProc, sqInt sourceCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentState; + sqInt newProcThreadAffinity; + sqInt threadSwitchNecessary; + CogVMThread *vmThread; + + assert(vmOwnerIs(ioThreadLocalGetThreadIndex())); + if (GIV(deferThreadSwitch)) { + return; + } + assertValidProcessorStackPointersForIndex(atomic_load((&GIV(vmOwner)))); + + /* If the current process is unaffined or it is affined to the current thread we're + ok to run, but we should yield asap if a higher-priority thread wants the VM. */ + newProcThreadAffinity = threadAffinityOfProcess(newProc); + threadSwitchNecessary = (newProcThreadAffinity != 0) + && (!(vmOwnerIsCompatibleWith(newProcThreadAffinity))); + if (!threadSwitchNecessary) { + if ((quickFetchIntegerofObject(PriorityIndex, newProc)) < (atomic_load((&GIV(maxWaitingPriority))))) { + GIV(checkThreadActivation) = 1; + forceInterruptCheck(); + } + + /* We're done, no thread switch necessary */ + return; + } + + /* The current process is affined to a thread, but not to the current owner. So switch to that owner. */ + + /* In most cases, we can just switch the thread here, without externalizing the stack pages. + If the Processes context is nil, it's state is on the stack. As we're already done context switching, + the new thread can just use the interpreter state as-is, without restoring the state from the context. + + tryToExecuteSmalltalk: already includes a check whether the SuspendedContext is nil. + If it is, it leaves the interpreter state alone and just assumes it's correct. + This is nice and fast. + Otherwise it calls externalSetStackPageAndPointersForSuspendedContextOfProcess: to restore the interpreter state. + + There is however a special case. When we switch to a thread that is currently CTMUnavailable, that thread will need + to restore its process when it tries to own the VM again. + The check to restore the context has been moved there (in restoreVMStateFor:andFlags:), so that it only happens in + that one case and not every time. + In case there are other such special-cases later, adding a call to ensureProcessHasContext: here should fix it. */ + if (newProcThreadAffinity < 0) { + assert((-newProcThreadAffinity) == (getVMOwner())); + vmThread = ensureWillingThread(); + assert(!((((vmThread->index)) == (getVMOwner())))); + assert(threadIndexisCompatibleWith((vmThread->index), newProcThreadAffinity)); + } + else { + /* begin vmThreadAt: */ + assert(((newProcThreadAffinity >= 0) && (newProcThreadAffinity <= GIV(numThreads)))); + vmThread = (newProcThreadAffinity > 0 + ? GIV(threads)[newProcThreadAffinity] + : ((CogVMThread *) null)); + (vmThread->priority = quickFetchIntegerofObject(PriorityIndex, newProc)); + if ((atomic_load((&((vmThread->state))))) == CTMUnavailable) { + /* begin setVmThreadState: */ + currentState = atomic_load((&((vmThread->state)))); + if (currentState != CTMWantingOwnership) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMWantingOwnership = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + assert(0 /* (CTMWantingOwnership = CTMAssignableOrInVM) */); + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMWantingOwnership = CTMUnavailable) */); + break; + case CTMUnavailable: + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((vmThread->state))), CTMWantingOwnership); + } + } + returnToSchedulingLoopAndReleaseVMOrWakeThreadsource(vmThread, CSSwitchIfNeccessary); +} + + +/* Record a process to be awoken on the next interpreter cycle. Override to + potentially switch threads either if the new process is bound to another + thread, or if there is no runnable process but there is a waiting thread. */ + + /* CoInterpreterMT>>#transferTo:from: */ +static NoDbgRegParms sqInt +transferTofrom(sqInt newProcOrNil, sqInt sourceCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt aMethodObj; + sqInt header; + StackPage *lastUsedPage; + usqInt lip; + sqInt methodHeader; + sqInt newContext; + StackPage *newPage; + sqInt oldProc; + sqInt sched; + sqInt senderOop; + char *theFrame; + StackPage *thePage; + sqInt top; + CogVMThread *vmThread; + + if (GIV(disowningThreadIndex)) { + preemptDisowningThread(); + assert(GIV(disowningThreadIndex) == 0); + } + GIV(statProcessSwitch) += 1; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + lip = (GIV(instructionPointer) >= (startOfMemory()) + ? GIV(instructionPointer) + 1 + : GIV(instructionPointer)); + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(lip, GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + + /* ensureMethodIsCogged: in makeBaseFrameFor: in + externalSetStackPageAndPointersForSuspendedContextOfProcess: + below may do a code compaction. Nil instructionPointer to avoid it getting pushed twice. */ + GIV(instructionPointer) = 0; + sched = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + oldProc = longAt((void *)((sched + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin recordContextSwitchFrom:to:in: */ + if ((recordEventTrace()) + && (!primTracePluginName)) { + /* begin recordTrace:source:thing:extra: */ + assert(sourceCode < ((1U << TraceTypeShift))); + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceContextSwitch) << TraceTypeShift))))) + sourceCode); + GIV(traceLog)[GIV(traceLogIndex) + 1] = oldProc; + GIV(traceLog)[GIV(traceLogIndex) + 2] = newProcOrNil; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l1; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer) + BytesPerWord); + /* end ensureFrameIsMarried:SP: */ +l1: + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, oldProc, activeContext)); + assert(isNonImmediate(oldProc)); + if (oopisGreaterThanOrEqualTo(oldProc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeContext & (tagMask())))) + && (oopisLessThan(activeContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(oldProc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(oldProc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((oldProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),activeContext); + if (!newProcOrNil) { + /* Two possibilities. One, there is at least one thread waiting to own the VM in which + case it should be activated. Two, there are no processes to run and so abort. This + is new in the MT VM, and only happens when the primitiveRelinquishProcessor has been + preempted. In that case the idle Process is not runnable and there is no Process to return to. + By setting the activeProcess to nilObject, any threads woken by the heartbeat don't actually + start running Smalltalk. This is then fixed when an AWOL thread comes back and restores its + previous state. */ + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ActiveProcessIndex, sched, GIV(nilObj))); + assert(isNonImmediate(sched)); + if (oopisGreaterThanOrEqualTo(sched, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(GIV(nilObj) & (tagMask())))) + && (oopisLessThan(GIV(nilObj), GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(sched + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(sched); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((sched + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord()))))),GIV(nilObj)); + if ((vmThread = willingVMThread())) { + if ((atomic_load((&((vmThread->state))))) == CTMWantingOwnership) { + returnToSchedulingLoopAndReleaseVMOrWakeThreadsource(vmThread, sourceCode); + } + } + + /* self error: 'scheduler could not find a runnable process' + relinquishing := true */ + returnToSchedulingLoopAndReleaseVMOrWakeThreadsource(null, sourceCode); + } + + /* Switch to the new process */ + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ActiveProcessIndex, sched, newProcOrNil)); + assert(isNonImmediate(sched)); + if (oopisGreaterThanOrEqualTo(sched, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(newProcOrNil & (tagMask())))) + && (oopisLessThan(newProcOrNil, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(sched + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(sched); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((sched + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord()))))),newProcOrNil); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newProcOrNil)) + && (!(isForwarded(newProcOrNil)))); + assert(validStorePointerUncheckedArgs(MyListIndex, newProcOrNil, GIV(nilObj))); + longAtput((void *)((newProcOrNil + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin externalSetStackPageAndPointersForSuspendedContextOfProcess: */ + newContext = longAt((void *)((newProcOrNil + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + assert(isContext(newContext)); + if (((((longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert(checkIsStillMarriedContextcurrentFP(newContext, GIV(framePointer))); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newProcOrNil)) + && (!(isForwarded(newProcOrNil)))); + assert(validStorePointerUncheckedArgs(SuspendedContextIndex, newProcOrNil, nilObject())); + longAtput((void *)((newProcOrNil + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),GIV(nilObj)); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(newContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + if (theFrame != ((thePage->headFP))) { + /* explicit assignment of suspendedContext can cause switch to interior frame. */ + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l3; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l3: + moveFramesInthroughtoPage(thePage, findFrameAboveinPage(theFrame, thePage), newPage); + + /* begin markStackPageLeastMostRecentlyUsed: */ + assert(newPage == ((GIV(mostRecentlyUsedPage)->nextPage))); + lastUsedPage = (newPage->nextPage); + while (((lastUsedPage->baseFP)) == 0) { + lastUsedPage = (lastUsedPage->nextPage); + } + if (((lastUsedPage->nextPage)) == newPage) { + goto l2; + } + (((newPage->prevPage))->nextPage = (newPage->nextPage)); + (((newPage->nextPage))->prevPage = (newPage->prevPage)); + (((lastUsedPage->prevPage))->nextPage = newPage); + (newPage->prevPage = (lastUsedPage->prevPage)); + (newPage->nextPage = lastUsedPage); + (lastUsedPage->prevPage = newPage); + assert(pageListIsWellFormed()); + /* end markStackPageLeastMostRecentlyUsed: */ +l2:; + } + assert(((thePage->headFP)) == theFrame); + } + else { + thePage = makeBaseFrameFor(newContext); + theFrame = (thePage->baseFP); + } + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + + /* Finally thread switch if required */ + threadSwitchIfNecessaryfrom(newProcOrNil, sourceCode); + return 0; +} + + +/* Set the maxWaitingPriority to at least minPriority on behalf + of a thread wanting to acquire the VM. If maxWaitingPriority + is increased, schedule a thread activation check asap. */ + + /* CoInterpreterMT>>#waitingPriorityIsAtLeast: */ +static NoDbgRegParms void +waitingPriorityIsAtLeast(sqInt minPriority) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int currentWaitingPriority; + sqInt didIncrease; + + currentWaitingPriority = atomic_load((&GIV(maxWaitingPriority))); + didIncrease = 0; + while (!((currentWaitingPriority >= minPriority) + || ((didIncrease = atomic_compare_exchange_strong((&GIV(maxWaitingPriority)), (¤tWaitingPriority), minPriority))))) { + } + if (didIncrease) { + assert(currentWaitingPriority < minPriority); + GIV(checkThreadActivation) = 1; + forceInterruptCheck(); + } +} + + /* CoInterpreterPrimitives>>#frameIsMarked: */ +static NoDbgRegParms int +frameIsMarked(sqInt theFPInt) +{ + sqInt methodField; + + methodField = longAt((void *)(theFPInt + FoxMethod)); + return ((((usqInt)methodField)) < (startOfMemory()) + ? (methodField & 4) != 0 + : ((longAt((void *)(theFPInt + FoxIFrameFlags))) & 2) != 0); +} + + /* CoInterpreterPrimitives>>#primitiveAllMethodsCompiledToMachineCode */ +#if VMInvestigations +EXPORT(int) +primitiveAllMethodsCompiledToMachineCode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayObj; + sqInt delta; + sqInt maxNumEntries; + sqInt nEntries; + + if (GIV(argumentCount)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + maxNumEntries = (numMethods()) + (16 /* (allocationUnit * 2) */ / BytesPerOop); + arrayObj = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), maxNumEntries); + if (!arrayObj) { + return -1; + } + nEntries = methodsCompiledToMachineCodeInto(arrayObj); + if (nEntries < maxNumEntries) { + /* begin shorten:toIndexableSize: */ + assert((nEntries >= 0) + && (nEntries < (lengthOf(arrayObj)))); + delta = doShortentoIndexableSize(arrayObj, nEntries); + assert((lengthOf(followMaybeForwarded(arrayObj))) == nEntries); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),arrayObj); + return 0; +} +#endif /* VMInvestigations */ + + /* CoInterpreterPrimitives>>#primitiveBenchmarkFollowForwardersInStackZone */ +#if VMInvestigations +EXPORT(void) +primitiveBenchmarkFollowForwardersInStackZone(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + usqLong then; + + /* begin primitiveReturnTimeTakenFor: */ + then = ioHighResClock(); + followForwardingPointersOfReceiversInStackZone(); + oop = positive64BitIntegerFor((ioHighResClock()) - then); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} +#endif /* VMInvestigations */ + + /* CoInterpreterPrimitives>>#primitiveBenchmarkFollowForwardersOfReceiverAndTemporariesInStackZone */ +#if VMInvestigations +EXPORT(void) +primitiveBenchmarkFollowForwardersOfReceiverAndTemporariesInStackZone(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + usqLong then; + + /* begin primitiveReturnTimeTakenFor: */ + then = ioHighResClock(); + followForwardingPointersOfReceiverAndTemporariesInStackZone(); + oop = positive64BitIntegerFor((ioHighResClock()) - then); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} +#endif /* VMInvestigations */ + + /* CoInterpreterPrimitives>>#primitiveBenchmarkJITReceiver */ +#if VMInvestigations +EXPORT(sqInt) +primitiveBenchmarkJITReceiver(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cloneMethod; + sqInt oop; + sqInt receiverMethod; + usqLong then; + + receiverMethod = longAt(GIV(stackPointer)); + if (!(/* isOopCompiledMethod: */ + ((!(receiverMethod & (tagMask())))) + && (((byteAt((void *)(receiverMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return (GIV(primFailCode) = PrimErrBadReceiver); + } + if (isFullBlockMethod(receiverMethod)) { + return (GIV(primFailCode) = PrimErrUnsupported); + } + cloneMethod = cloneObject(receiverMethod); + if (!cloneMethod) { + return (GIV(primFailCode) = PrimErrNoMemory); + } + + /* begin primitiveReturnTimeTakenFor: */ + then = ioHighResClock(); + if (!(cogselector(cloneMethod, maybeSelectorOfMethod(cloneMethod)))) { + return (GIV(primFailCode) = PrimErrOperationFailed); + } + freeCogMethod(cogMethodOf(cloneMethod)); + oop = positive64BitIntegerFor((ioHighResClock()) - then); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return 0; +} +#endif /* VMInvestigations */ + + /* CoInterpreterPrimitives>>#primitiveBenchmarkScavenge */ +#if VMInvestigations +EXPORT(void) +primitiveBenchmarkScavenge(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + usqLong then; + + /* begin primitiveReturnTimeTakenFor: */ + then = ioHighResClock(); + scavengingGCTenuringIf(TenureByAge); + (((usqInt)(((((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes())) < 0) ? 0 : ((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes())))) << 3) | 1); + oop = positive64BitIntegerFor((ioHighResClock()) - then); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} +#endif /* VMInvestigations */ + + +/* This is optional old obsolete stuff tedious to implement in Cog because of + the need to map from machine code pc to bytecode pc. So for now just fail. */ + + /* CoInterpreterPrimitives>>#primitiveClosureCopyWithCopiedValues */ +static void +primitiveClosureCopyWithCopiedValues(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } +} + + +/* Answer an Array of: + - the address of the CogMethod of the receiver, + - the CogMethod of the receiver as a ByteArray, and + - a sequence of relative pc/value/object triples for the imported + references embedded in the CogMethod. + Fail on V3. Fail if the receiver is not a cogged method. Fail if there is + insufficient memory for the result. + If there is an argument it is a boolean that if true includes more + detailed Closed PIC information. */ + + /* CoInterpreterPrimitives>>#primitiveCogitMethod */ +#if VMInvestigations +EXPORT(sqInt) +primitiveCogitMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt code; + CogMethod *cogMethod; + sqInt cogMethodData; + usqInt data; + sqInt detailCPICs; + sqInt methodHeader; + sqInt methodReceiver; + usqInt newObj; + usqInt numBytes; + sqInt valuePointer; + + if (GIV(argumentCount) > 0) { + if (GIV(argumentCount) > 1) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + if (!((detailCPICs = (longAt(GIV(stackPointer))) == GIV(trueObj)))) { + if (!((longAt(GIV(stackPointer))) == GIV(falseObj))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + } + } + else { + detailCPICs = 0; + } + methodReceiver = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + if (!((/* isOopCompiledMethod: */ + ((!(methodReceiver & (tagMask())))) + && (((byteAt((void *)(methodReceiver + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (methodHasCogMethod(methodReceiver)))) { + return (GIV(primFailCode) = PrimErrBadReceiver); + } + + /* begin eeInstantiateAnySmallClassIndex:format:numSlots: */ + assert((classAtIndex(ClassArrayCompactIndex)) != GIV(nilObj)); + assert((arrayFormat()) == (instSpecOfClass(classAtIndex(ClassArrayCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (3 * BytesPerOop); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + data = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) 3)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + GIV(freeStart) += numBytes; + data = newObj; + /* end eeInstantiateAnySmallClassIndex:format:numSlots: */ +l1: + + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((methodReceiver + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + valuePointer = positive64BitIntegerFor(((usqInt)cogMethod)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(data)) + && (!(isForwarded(data)))); + assert(validStorePointerUncheckedArgs(0, data, valuePointer)); + longAtput((void *)((data + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + if ((cogMethodData = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteArray) << (shiftForWord())))))), (cogMethod->blockSize)))) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(data)) + && (!(isForwarded(data)))); + assert(validStorePointerUncheckedArgs(1, data, cogMethodData)); + longAtput((void *)((data + BaseHeaderSize) + (1U << (shiftForWord()))),cogMethodData); + memcpy(firstIndexableField(cogMethodData), cogMethod, (cogMethod->blockSize)); + } + else { + return (GIV(primFailCode) = PrimErrNoMemory); + } + code = cogMethodImportsintodetailCPICs(cogMethod, data, detailCPICs); + if (code) { + return (GIV(primFailCode) = code); + } + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),data); + return 0; +} +#endif /* VMInvestigations */ + + +/* Answer an array of string,value pairs for all static symbols needed for + deferred disassembly. + */ + + /* CoInterpreterPrimitives>>#primitiveCogStaticSymbols */ +#if VMInvestigations +EXPORT(sqInt) +primitiveCogStaticSymbols(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *abiNameOrNil; + sqInt i; + sqInt symbols; + sqInt valuePointer; + + symbols = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 54); + valuePointer = stringForCString("wordSize"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(0, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(1, symbols, (((usqInt)BytesPerWord << 3) | 1))); + longAtput((void *)((symbols + BaseHeaderSize) + (1U << (shiftForWord()))),(((usqInt)BytesPerWord << 3) | 1)); + valuePointer = stringForCString("ISA"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(2, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (2U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString(isaName()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(3, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (3U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("ABI"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(4, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (4U << (shiftForWord()))),valuePointer); + valuePointer = ((abiNameOrNil = abiName()) + ? stringForCString(abiNameOrNil) + : GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(5, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (5U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("MULTIPLEBYTECODESETS"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(6, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (6U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(7, symbols, GIV(trueObj))); + longAtput((void *)((symbols + BaseHeaderSize) + (7U << (shiftForWord()))),GIV(trueObj)); + valuePointer = stringForCString("varBaseAddress"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(8, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (8U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(computeGoodVarBaseAddress()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(9, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (9U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("argumentCount"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(10, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (10U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(argumentCount))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(11, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (11U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("CFramePointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(12, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (12U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(CFramePointer))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(13, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (13U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("CStackPointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(14, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (14U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(CStackPointer))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(15, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (15U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("framePointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(16, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (16U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(framePointer))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(17, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (17U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("instructionPointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(18, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (18U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(instructionPointer))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(19, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (19U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("interpret"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(20, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (20U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)interpret)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(21, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (21U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("methodCache"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(22, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (22U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)GIV(methodCache))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(23, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (23U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("newMethod"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(24, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (24U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(newMethod))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(25, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (25U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("nextProfileTick"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(26, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (26U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(nextProfileTick))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(27, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (27U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("primFailCode"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(28, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (28U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(primFailCode))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(29, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (29U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("primitiveFunctionPointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(30, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (30U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&primitiveFunctionPointer)))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(0x1F, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (0x1FU << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("primTraceLog"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(32, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (32U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)GIV(primTraceLog))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(33, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (33U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("primTraceLogIndex"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(34, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (34U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(primTraceLogIndex))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(35, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (35U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("stackLimit"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(36, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (36U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(stackLimit))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(37, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (37U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("stackPointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(38, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (38U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(stackPointer))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(39, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (39U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("nil"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(40, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (40U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(41, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (41U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("false"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(42, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (42U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(falseObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(43, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (43U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("true"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(44, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (44U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(trueObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(45, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (45U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("classTableRootObj"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(46, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (46U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(hiddenRootsObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(47, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (47U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("methodZoneBase"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(48, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (48U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(getMethodZoneBase()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(49, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (49U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("checkForAndFollowForwardedPrimitiveState"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(50, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (50U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)checkForAndFollowForwardedPrimitiveState)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(51, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (51U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("ceCheckAndMaybeRetryPrimitive:"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(52, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (52U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)ceCheckAndMaybeRetryPrimitive)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(53, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (53U << (shiftForWord()))),valuePointer); + + /* these are needed because they are not annotated and hence not identified as exports by primitiveCogitMethod + this is needed to distinguish linked/unlinked sends + these two show up in Spur primitives, but maybe absolute calls, which won't show up in exports */ + for (i = 2; i <= 53; i += 1) { + if (!(longAt((void *)((symbols + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + return (GIV(primFailCode) = PrimErrNoMemory); + } + } + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),symbols); + return 0; +} +#endif /* VMInvestigations */ + + +/* Answer the contents of the code zone as an array of pair-wise element, + address in ascending + address order. Answer a string for a runtime routine or abstract label + (beginning, end, etc), + a CompiledMethod for a CMMethod, or a selector (presumably a Symbol) for a + PIC. If there is an argument and it is true, then collect inner + information about the CogMethod. */ + + /* CoInterpreterPrimitives>>#primitiveCollectCogCodeConstituents */ +static void +primitiveCollectCogCodeConstituents(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt constituents; + sqInt withDetails; + + if (GIV(argumentCount)) { + withDetails = longAt(GIV(stackPointer)); + if (!((withDetails == GIV(trueObj)) + || (withDetails == GIV(falseObj)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + withDetails = withDetails == GIV(trueObj); + } + else { + withDetails = 0; + } + constituents = cogCodeConstituents(withDetails); + if (!constituents) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + return; + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),constituents); +} + + +/* Lift the veil from a context and answer an integer describing its interior + state. Used for e.g. VM tests so they can verify they're testing what they + think they're testing. + 0 implies a vanilla heap context. + Bit 0 = is or was married to a frame + Bit 1 = is still married to a frame + Bit 2 = frame is executing machine code + Bit 3 = has machine code pc (as opposed to nil or a bytecode pc) + Bit 4 = method is currently compiled to machine code */ + + /* CoInterpreterPrimitives>>#primitiveContextXray */ +#if VMInvestigations +EXPORT(void) +primitiveContextXray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt context; + sqInt flags; + sqInt pc; + sqInt senderOop; + char *theFP; + sqInt theMethod; + + context = longAt(GIV(stackPointer)); + pc = longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + if (((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (checkIsStillMarriedContextcurrentFP(context, GIV(framePointer))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + flags = 7; + } + else { + flags = 3; + } + } + else { + flags = 1; + } + } + else { + flags = 0; + } + if (((((pc) & 7) == 1)) + && (((pc >> 3)) < 0)) { + flags = flags | 8; + } + theMethod = longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + if (/* maybeMethodHasCogMethod: */ + ((!(theMethod & (tagMask())))) + && ((((byteAt((void *)(theMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((theMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + flags = flags | 16; + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),(((usqInt)flags << 3) | 1)); +} +#endif /* VMInvestigations */ + + +/* Attempt to enter a CriticalSection/Mutex. If not owned, set the owner to + the current + process and answer false. If owned by the current process answer true. + Otherwise suspend the process. Answer if the receiver is owned by the + current process. + For simulation, if there is an argument it is taken to be the effective + activeProcess (see Process>>effectiveProcess). */ + + /* CoInterpreterPrimitives>>#primitiveEnterCriticalSection */ +static void +primitiveEnterCriticalSection(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + sqInt criticalSection; + int inInterpreter; + sqInt objOop; + sqInt owningProcess; + sqInt owningProcessIndex; + + if (GIV(argumentCount) > 0) { + /* rcvr */ + criticalSection = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + activeProc = longAt(GIV(stackPointer)); + if (/* isOopForwarded: */ + ((!(activeProc & (tagMask())))) + && ((!((longAt((void *)(activeProc))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + } + else { + /* rcvr */ + criticalSection = longAt(GIV(stackPointer)); + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + } + + /* CriticalSections are laid out like Semaphores */ + owningProcessIndex = ExcessSignalsIndex; + owningProcess = longAt((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord())))))); + if (owningProcess == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(owningProcessIndex, criticalSection, activeProc)); + assert(isNonImmediate(criticalSection)); + if (oopisGreaterThanOrEqualTo(criticalSection, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeProc & (tagMask())))) + && (oopisLessThan(activeProc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(criticalSection + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(criticalSection); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord()))))),activeProc); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(falseObj)); + return; + } + if (owningProcess == activeProc) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(trueObj)); + return; + } + + /* Arrange to answer false (unowned) when the process is resumed. */ + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(falseObj)); + + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + addLastLinktoList(activeProc, criticalSection); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSEnterCriticalSection); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); +} + + +/* Exit the critical section. + This may change the active process as a result. */ + + /* CoInterpreterPrimitives>>#primitiveExitCriticalSection */ +static void +primitiveExitCriticalSection(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt criticalSection; + int inInterpreter; + sqInt owningProcess; + sqInt owningProcessIndex; + + /* rcvr */ + criticalSection = longAt(GIV(stackPointer)); + + /* CriticalSections are laid out like Semaphores */ + owningProcessIndex = ExcessSignalsIndex; + + /* begin isEmptyList: */ + assert(!(isForwarded(criticalSection))); + if ((longAt((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(criticalSection)) + && (!(isForwarded(criticalSection)))); + assert(validStorePointerUncheckedArgs(owningProcessIndex, criticalSection, GIV(nilObj))); + longAtput((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + owningProcess = removeFirstLinkOfList(criticalSection); + + /* store check unnecessary because aSemaphore referred to owningProcess + via its FirstLinkIndex slot before owningProcess was removed. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(criticalSection)) + && (!(isForwarded(criticalSection)))); + assert(validStorePointerUncheckedArgs(owningProcessIndex, criticalSection, owningProcess)); + longAtput((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord()))))),owningProcess); + + /* Note that resume: isn't fair; it won't suspend the active process. + For fairness we must do the equivalent of a primitiveYield, but that + may break old code, so we stick with unfair resume:. */ + if (resumepreemptedYieldingIffrom(owningProcess, (!(imageHeaderFlags & 16)), CSExitCriticalSection)) { + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } + } +} + + +/* No-op for measuring performance of primitiveExternalCall/#117 linkage */ + + /* CoInterpreterPrimitives>>#primitiveExternalCallNoOp */ +EXPORT(void) +primitiveExternalCallNoOp(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* No-op for measuring performance of FastCPrimitiveFlag linkage */ + + /* CoInterpreterPrimitives>>#primitiveFastCNoOp */ +EXPORT(void) +primitiveFastCNoOp(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* No-op for measuring performance of + FastCPrimitiveFlag+FastCPrimitiveAlignForFloatsFlag linkage + */ + + /* CoInterpreterPrimitives>>#primitiveFastCNoOpAlignedForFloats */ +EXPORT(void) +primitiveFastCNoOpAlignedForFloats(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* The receiver is a compiledMethod. Clear all entries in the method lookup + cache that refer to this method, presumably because it has been redefined, + overridden or removed. + */ +/* The receiver is a compiledMethod. Clear all entries in the method lookup + cache that + refer to this method, presumably because it has been redefined, overridden + or removed. + Override to flush appropriate machine code caches also. */ + + /* CoInterpreterPrimitives>>#primitiveFlushCacheByMethod */ +static void +primitiveFlushCacheByMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + flushMethodCacheForMethod(longAt(GIV(stackPointer))); + unlinkSendsToandFreeIf(longAt(GIV(stackPointer)), 0); +} + + +/* The receiver is a message selector. Clear all entries in the method lookup + cache with this selector, presumably because an associated method has been + redefined. Override to also flush machine code caches. */ + + /* CoInterpreterPrimitives>>#primitiveFlushCacheBySelector */ +static void +primitiveFlushCacheBySelector(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt probe; + sqInt selector; + + selector = longAt(GIV(stackPointer)); + probe = 0; + for (i = 1; i <= MethodCacheEntries; i += 1) { + if ((GIV(methodCache)[probe + MethodCacheSelector]) == selector) { + GIV(methodCache)[probe + MethodCacheSelector] = 0; + } + probe += MethodCacheEntrySize; + } + unlinkSendsOfisMNUSelector(selector, selector == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))))); +} + + /* CoInterpreterPrimitives>>#primitiveFunctionPointerAddress */ +usqInt +primitiveFunctionPointerAddress(void) +{ + return ((usqInt)((&primitiveFunctionPointer))); +} + + +/* Primitive. Install the semaphore to be used for collecting long-running + primitives, + or nil if no semaphore should be used. */ + + /* CoInterpreterPrimitives>>#primitiveLongRunningPrimitiveSemaphore */ +#if LRPCheck +EXPORT(sqInt) +primitiveLongRunningPrimitiveSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + int flushState; + sqInt sema; + + if (GIV(argumentCount) != 1) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + sema = longAt(GIV(stackPointer)); + if (sema == GIV(nilObj)) { + flushState = GIV(longRunningPrimitiveCheckSemaphore); + GIV(longRunningPrimitiveCheckSemaphore) = null; + } + else { + flushState = !GIV(longRunningPrimitiveCheckSemaphore); + if (!(/* isSemaphoreOop: */ + ((!(sema & (tagMask())))) + && (((longAt((void *)(sema))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + GIV(longRunningPrimitiveCheckSemaphore) = sema; + } + + /* If we've switched checking on or off we must void machine code + (and machine code pcs in contexts) since we will start or stop setting + newMethod in machine code primitive invocations, and so generate + slightly different code from here on in. */ + if (flushState) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + activeContext = voidVMStateForSnapshotFlushingExternalPrimitivesIf(0); + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + assert((((stackValue(0)) == (nilObject())) + && (!GIV(longRunningPrimitiveCheckSemaphore))) + || (((stackValue(0)) == GIV(longRunningPrimitiveCheckSemaphore)) + && (isSemaphoreOop(sema)))); + } + voidLongRunningPrimitive("install"); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + if (flushState) { + ceInvokeInterpret(); + } + return 0; +} +#endif /* LRPCheck */ + + +/* Answer if a method is currently jitted. */ + + /* CoInterpreterPrimitives>>#primitiveMethodIsCogged */ +#if VMInvestigations +EXPORT(sqInt) +primitiveMethodIsCogged(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodOop; + sqInt boolean; + sqInt methodHeader; + char *sp; + + if (GIV(argumentCount)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + if (!(/* isOopCompiledMethod: */ + ((!((longAt(GIV(stackPointer))) & (tagMask())))) + && (((byteAt((void *)((longAt(GIV(stackPointer))) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return (GIV(primFailCode) = PrimErrBadReceiver); + } + aMethodOop = longAt(GIV(stackPointer)); + + /* begin methodHasCogMethod: */ + assert(isNonImmediate(aMethodOop)); + methodHeader = longAt((void *)((aMethodOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + boolean = (!(methodHeader & (smallIntegerTag()))); + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (boolean + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return 0; +} +#endif /* VMInvestigations */ + + /* CoInterpreterPrimitives>>#primitiveMethodPCData */ +EXPORT(sqInt) +primitiveMethodPCData(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cm; + CogMethod *cogMethod; + sqInt data; + sqInt dataSqInt; + sqInt delta; + sqInt methodHeader; + sqInt methodReceiver; + sqInt nEntries; + sqInt nSlots; + + if (GIV(argumentCount)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + methodReceiver = longAt(GIV(stackPointer)); + data = 0; + if (methodHasCogMethod(methodReceiver)) { + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((methodReceiver + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + + /* begin pcDataFor: */ + cm = (cogMethod->methodObject); + nSlots = (((byteSizeOf(cm)) - ((((/* begin literalCountOfMethodHeader: */ + assert((((((cogMethod->methodHeader))) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((((cogMethod->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop)) * 2) + (16 /* (allocationUnit * 2) */ / BytesPerOop); + dataSqInt = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), nSlots); + if (!dataSqInt) { + data = -1; + goto l1; + } + nEntries = mapPCDataForinto(cogMethod, dataSqInt); + if (!nEntries) { + data = 0; + goto l1; + } + if (nEntries < nSlots) { + /* begin shorten:toIndexableSize: */ + assert((nEntries >= 0) + && (nEntries < (lengthOf(dataSqInt)))); + delta = doShortentoIndexableSize(dataSqInt, nEntries); + assert((lengthOf(followMaybeForwarded(dataSqInt))) == nEntries); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + } + data = dataSqInt; + /* end pcDataFor: */ +l1: + if (data == -1) { + return (GIV(primFailCode) = PrimErrNoMemory); + } + } + if (!data) { + data = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 0); + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),data); + return 0; +} + + +/* Lift the veil from a method and answer an integer describing the interior + state of its machine code. + Used for e.g. VM tests so they can verify they're testing what they think + they're testing. + 0 implies a vanilla method. + Bit 0 = method might be compiled to machine code + Bit 1 = method is currently compiled to machine code + Bit 2 = is compiled frameless. + Bit 3 = method refers to young object. + Bit 4 = method too big to be jitted (more than 64k of code, or needs more + than 1.5Mb of stack space to compile) + Bit 5 = method contains unknown/unjittable bytecode + Bit 6 = method should not be jitted because it contains a primitive not to + be called from machine code (unused) */ + + /* CoInterpreterPrimitives>>#primitiveMethodXray */ +static void +primitiveMethodXray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt alreadyCogged; + CogMethod *cogMethod; + int flags; + sqInt header; + sqInt methodHeader; + sqInt methodObj; + + methodObj = longAt(GIV(stackPointer)); + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + alreadyCogged = /* maybeMethodHasCogMethod: */ + ((!((longAt(GIV(stackPointer))) & (tagMask())))) + && ((((byteAt((void *)((longAt(GIV(stackPointer))) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))))))); + flags = 1; + if (!alreadyCogged) { + cogMethod = cogselector(longAt(GIV(stackPointer)), GIV(nilObj)); + if ((cogMethod == null) + && (GIV(cogCompiledCodeCompactionCalledFor))) { + commenceCogCompiledCodeCompaction(); + cogMethod = cogselector(longAt(GIV(stackPointer)), GIV(nilObj)); + } + switch (((sqInt)cogMethod)) { + case MethodTooBig: + flags = 17; + break; + case EncounteredUnknownBytecode: + flags = 33; + break; + case ShouldNotJIT: + flags = 65; + break; + default: + assert(!(((((((sqInt)cogMethod)) >= MaxNegativeErrorCode) && ((((sqInt)cogMethod)) <= NotFullyInitialized))))); + } + } + if ((flags == 1) + && (/* maybeMethodHasCogMethod: */ + ((!((longAt(GIV(stackPointer))) & (tagMask())))) + && ((((byteAt((void *)((longAt(GIV(stackPointer))) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))))))))) { + /* begin cogMethodOf: */ + methodHeader = longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + flags = ((cogMethod->stackCheckOffset) + ? 3 + : 7); + if ((cogMethod->cmRefersToYoung)) { + flags += 8; + } + if (!alreadyCogged) { + freeCogMethod(cogMethod); + } + } + } + else { + flags = 0; + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),(((usqInt)flags << 3) | 1)); +} + + /* CoInterpreterPrimitives>>#primitiveMinimumUnusedHeadroom */ +EXPORT(void) +primitiveMinimumUnusedHeadroom(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integer; + + integer = minimumUnusedHeadroom(); + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); +} + + +/* Defined for CompiledMethods only */ + + /* CoInterpreterPrimitives>>#primitiveObjectAt */ +static void +primitiveObjectAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt integerPointer; + sqInt oop; + sqInt rawHeader; + sqInt realHeader; + sqInt thisReceiver; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + index = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + index = 0; + } + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + thisReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rawHeader = longAt((void *)((thisReceiver + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + realHeader = ((!(rawHeader & (smallIntegerTag()))) + ? ((((CogMethod *) rawHeader))->methodHeader) + : rawHeader); + if (!((index > 0) + && (index <= (((/* begin literalCountOfMethodHeader: */ + assert((((realHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((realHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + oop = (index == 1 + ? realHeader + : longAt((void *)((thisReceiver + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oop); +} + + +/* Store a literal into a CompiledMethod at the given index. Defined for + CompiledMethods only. + We assume that if the user is using this on active code then they will use + primitiveVoidVMStateForMethod to discard the machine code as required. */ + + /* CoInterpreterPrimitives>>#primitiveObjectAtPut */ +static void +primitiveObjectAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fieldIndex; + sqInt index; + sqInt newValue; + sqInt rawHeader; + sqInt realHeader; + sqInt thisReceiver; + + newValue = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((!(index & (smallIntegerTag())))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + index = (index >> 3); + thisReceiver = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(thisReceiver + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + return; + } + rawHeader = longAt((void *)((thisReceiver + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + realHeader = ((!(rawHeader & (smallIntegerTag()))) + ? ((((CogMethod *) rawHeader))->methodHeader) + : rawHeader); + if (!((index > 0) + && (index <= (((/* begin literalCountOfMethodHeader: */ + assert((((realHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((realHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (index == 1) { + if (((!(newValue & (smallIntegerTag())))) + || (((/* begin literalCountOfMethodHeader: */ + assert((((newValue) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((newValue >> 3)) & AlternateHeaderNumLiteralsMask)) != ((assert((((realHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((realHeader >> 3)) & AlternateHeaderNumLiteralsMask)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* begin isCogMethodReference: */ + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + ((((CogMethod *) rawHeader))->methodHeader = newValue); + } + else { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(thisReceiver)) + && (!(isForwarded(thisReceiver)))); + assert(validStorePointerUncheckedArgs(0, thisReceiver, newValue)); + longAtput((void *)((thisReceiver + BaseHeaderSize) + (0U << (shiftForWord()))),newValue); + } + } + else { + fieldIndex = index - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, thisReceiver, newValue)); + assert(isNonImmediate(thisReceiver)); + if (oopisGreaterThanOrEqualTo(thisReceiver, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(newValue & (tagMask())))) + && (oopisLessThan(newValue, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(thisReceiver + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(thisReceiver); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((thisReceiver + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),newValue); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),newValue); +} + + +/* Primitive. Install the semaphore to be used for profiling, + or nil if no semaphore should be used. + See also primitiveProfileStart. */ + + /* CoInterpreterPrimitives>>#primitiveProfileSemaphore */ +EXPORT(sqInt) +primitiveProfileSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt address; + sqInt classIndex; + int flushState; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt sema; + sqInt senderOop; + sqInt startObject; + char *theFP; + char *theFrame; + StackPage *thePage; + sqInt top; + + sema = longAt(GIV(stackPointer)); + if (sema == GIV(nilObj)) { + flushState = GIV(profileSemaphore); + GIV(profileSemaphore) = null; + + /* begin zeroNextProfileTick */ + GIV(nextProfileTick) = 0; + } + else { + flushState = !GIV(profileSemaphore); + if (!(/* isSemaphoreOop: */ + ((!(sema & (tagMask())))) + && (((longAt((void *)(sema))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + GIV(profileSemaphore) = sema; + } + GIV(profileProcess) = (GIV(profileMethod) = null); + + /* If we've switched profiling on or off we must void machine code + (and machine code pcs in contexts) since we will start or stop + testing the profile clock in machine code primitive invocations, + and so generate slightly different code from here on in. */ + if (flushState) { + /* begin mapActivationsOfRealPrimitivesToBytecodePCsAndFreeCoggedPrimitiveMethods */ + activeContext = divorceAllFramesSuchThat(isMachineCodeFrameForRealNonProfilingPrimitive); + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin mapToBytecodePCIfActivationOfRealNonProfilingPrimitive: */ + assert(isContextNonImm(obj)); + if (isRealNonProfilingPrimitiveMethod(longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))))) { + if (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (isWidowedContext(obj)) { + goto l2; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + externalDivorceFrameandContext(theFP, obj); + } + } + ensureContextHasBytecodePC(obj); + } + /* end mapToBytecodePCIfActivationOfRealNonProfilingPrimitive: */ +l2:; + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + unlinkSendsToMethodsSuchThatAndFreeIf(cogMethodHasRealNonProfilingPrimitive, 1); + + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + + /* If flushing led to divorce continue in the interpreter. */ + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + ceInvokeInterpret(); + } + + /* If not, work out where we are and continue */ + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + assert(((thePage->headFP)) == theFrame); + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + else { + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + return 0; +} + + +/* Put this process on the scheduler's lists thus allowing it to proceed next + time there is + a chance for processes of it's priority level. It must go to the back of + its run queue so + as not to preempt any already running processes at this level. If the + process's priority + is higher than the current process, preempt the current process. */ + + /* CoInterpreterPrimitives>>#primitiveResume */ +static void +primitiveResume(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + int inInterpreter; + sqInt proc; + + /* rcvr */ + /* Alas in Spur we need a read barrier */ + proc = longAt(GIV(stackPointer)); + + /* begin followObjField:ofObject: */ + ctxt = longAt((void *)((proc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + assert(isNonImmediate(ctxt)); + if ((!((longAt((void *)(ctxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + ctxt = fixFollowedFieldofObjectwithInitialValue(SuspendedContextIndex, proc, ctxt); + } + if (!(/* isContext: */ + ((!(ctxt & (tagMask())))) + && (((longAt((void *)(ctxt))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* We're about to switch process, either to an interpreted frame or a + machine code frame. To know whether to return or enter machine code + we have to know from whence we came. We could have come from the + interpreter, either directly or via a machine code primitive. We could have + come from machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + if (resumepreemptedYieldingIffrom(proc, (!(imageHeaderFlags & 16)), CSResume)) { + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } +} + + +/* Assign the priority of the receiver based on the argument, performing any + necessary rescheduling. + There are three main cases: + the active process + a quiesent/runnable process + a suspended/waiting process + There are scheduling implications only for the first two cases (the two + forms of runnable processes). */ + + /* CoInterpreterPrimitives>>#primitiveSetPriority */ +static void +primitiveSetPriority(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activePriority; + sqInt existingPriority; + int inInterpreter; + sqInt newPriority; + usqInt numSlots; + sqInt oop; + sqInt processList; + sqInt processLists; + sqInt receiverProcess; + sqInt scheduler; + sqInt theActiveProcess; + + scheduler = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + processLists = longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + receiverProcess = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + existingPriority = longAt((void *)((receiverProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + + /* Setting the priority when it is nil is simply instance initialization (typically from Process class>>#forContext:priority:). + If priority has any other non-integer value then there is something wrong and trhe primitive should fail. */ + if (!(((((existingPriority) & 7) == 1)) + || (existingPriority == GIV(nilObj)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + newPriority = longAt(GIV(stackPointer)); + if (!(((((newPriority) & 7) == 1)) + && (((((newPriority = (newPriority >> 3))) >= 1) && (newPriority <= ((/* begin numSlotsOf: */ + assert((classIndexOf(processLists)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(processLists + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(processLists - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + theActiveProcess = longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((theActiveProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + activePriority = (oop >> 3); + + /* assign priority for all cases. return receiver for all cases */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(receiverProcess)) + && (!(isForwarded(receiverProcess)))); + assert(validStorePointerUncheckedArgs(PriorityIndex, receiverProcess, (((usqInt)newPriority << 3) | 1))); + longAtput((void *)((receiverProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord()))))),(((usqInt)newPriority << 3) | 1)); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + + /* Setting the priority when it is nil is simply instance initialization */ + if (!((((existingPriority) & 7) == 1))) { + assert(nilObject() == existingPriority); + return; + } + existingPriority = (existingPriority >> 3); + + /* begin highestRunnablePriorityIsAtLeast: */ + if (GIV(highestRunnableProcessPriority)) { + GIV(highestRunnableProcessPriority) = ((GIV(highestRunnableProcessPriority) < newPriority) ? newPriority : GIV(highestRunnableProcessPriority)); + } + + /* We may switch process, either to an interpreted frame or a machine code frame. To + know whether to return or enter machine code we have to know from whence we came. + We could have come from the interpreter, either directly or via a machine code primitive. + We could have come from machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + + /* If setting the priority of the active process then setting the priority + higher or same is a noop, but setting it lower is like yielding. + But to avoid putting the process to sleep (creating its context etc) + first check if it is still the runnable process on lowering its priority. */ + if (theActiveProcess == receiverProcess) { + if ((newPriority < activePriority) + && ((findHighestPriority()) > newPriority)) { + processList = longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((newPriority - 1)) << (shiftForWord())))))); + addLastLinktoList(theActiveProcess, processList); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSSetPriority); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } + return; + } + + /* If receiverProcess's list is the quiesent list at its priority then it is quiescent/runnable, + otherwise receiverProcess is either suspended (myList = nil) or waiting on a condition + variable. If receiverProcess is quiescent then if setting the priority to less or equal to + the active priority then this is a noop, but if setting higher then effectively the active + process yields to the receiverProcess. */ + if (((longAt((void *)((receiverProcess + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))))) == (longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((existingPriority - 1)) << (shiftForWord())))))))) + && (newPriority > activePriority)) { + processList = longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((activePriority - 1)) << (shiftForWord())))))); + addLastLinktoList(theActiveProcess, processList); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSSetPriority); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } + return; +} + + +/* Synchronously signal the semaphore. + This may change the active process as a result. */ +/* We may be about to switch process, either to an interpreted frame or a + machine code frame. To know whether to return or enter machine code + we have to know from whence we came. We could have come from the + interpreter, either directly or via a machine code primitive. We could + have come from machine code. The instructionPointer tells us where from: */ + + /* CoInterpreterPrimitives>>#primitiveSignal */ +static void +primitiveSignal(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int inInterpreter; + + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + if (synchronousSignal(longAt(GIV(stackPointer)))) { + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } +} + + +/* Save a normal snapshot under the same name as it was loaded + unless it has been renamed by the last primitiveImageName. + Note that when executed this primitive answers false, but when the + resulting image is run afresh, the primitive answers true. + + Override to jump to the interpreter because the machine code zone is now + void. + */ + + /* CoInterpreterPrimitives>>#primitiveSnapshot */ +static void +primitiveSnapshot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + snapshot(0); + if ((longAt(GIV(framePointer) + FoxMethod)) == GIV(newMethod)) { + /* snapshot: has reached the end and built a frame. + In the JIT we need to back-up the pc before reentering the interpreter. */ + GIV(instructionPointer) -= 1; + } + ceInvokeInterpret(); +} + + +/* Save an embedded snapshot. + Note that when executed this primitive answers false, but when the + resulting image is run afresh, the primitive answers true. + + Override to jump to the interpreter because the machine code zone is now + void. + */ + + /* CoInterpreterPrimitives>>#primitiveSnapshotEmbedded */ +static void +primitiveSnapshotEmbedded(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + snapshot(1); + if ((longAt(GIV(framePointer) + FoxMethod)) == GIV(newMethod)) { + /* snapshot: has reached the end and built a frame. + In the JIT we need to back-up the pc before reentering the interpreter. */ + GIV(instructionPointer) -= 1; + } + ceInvokeInterpret(); +} + + +/* Primitive #88. Suspend the receiver, aProcess, such that it can be + executed again + by sending #resume. If the given process is not the active process, take + it off + its corresponding list. The primitive returns the list the receiver was + previously on. + c.f. primitiveSuspendBackingUpV1,#568 & primitiveSuspendBackingUpV2,#578 */ + + /* CoInterpreterPrimitives>>#primitiveSuspend */ +static void +primitiveSuspend(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int inInterpreter; + sqInt myContext; + sqInt myList; + usqInt numSlots; + sqInt ok; + sqInt process; + + process = longAt(GIV(stackPointer)); + if (process == (activeProcess())) { + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(nilObj)); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSSuspend); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + return; + } + myList = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord())))))); + myContext = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if (!((/* isPointers: */ + ((!(myList & (tagMask())))) + && (((byteAt((void *)(myList + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(myList)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(myList + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(myList - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > LastLinkIndex) + && ((((longAt((void *)(myContext))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((/* begin isResumableContext: */ + assert(isContext(myContext)), + ((((longAt((void *)((myContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + ok = removeProcessfromList(process, myList); + if (!ok) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrOperationFailed; + return; + } + + /* stackTopPut: */ + longAtput(GIV(stackPointer),myList); +} + + +/* Primitive #568. Suspend the receiver, aProcess, such that it can be + executed again + by sending #resume. If the given process is not the active process, take + it off + its corresponding list. If the list was not its run queue assume it was on + some condition variable (Semaphore, Mutex) and back up its pc to the send + that invoked the wait state the process entered. Hence when the process + resumes it will reenter the wait state. Answer the list the receiver was + previously on, + unless it was the activeProcess, in which case answer nil. + c.f. primitiveSuspend,#88 & primitiveSuspendBackingUpV2,#578 */ + + /* CoInterpreterPrimitives>>#primitiveSuspendBackingUpV1 */ +static void +primitiveSuspendBackingUpV1(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int inInterpreter; + sqInt myContext; + sqInt myList; + usqInt numSlots; + sqInt ok; + sqInt process; + + process = longAt(GIV(stackPointer)); + if (process == (activeProcess())) { + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(nilObj)); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSSuspend); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + return; + } + myList = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord())))))); + myContext = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if (!((/* isPointers: */ + ((!(myList & (tagMask())))) + && (((byteAt((void *)(myList + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(myList)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(myList + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(myList - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > LastLinkIndex) + && ((((longAt((void *)(myContext))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((/* begin isResumableContext: */ + assert(isContext(myContext)), + ((((longAt((void *)((myContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + ok = removeProcessfromList(process, myList); + if (!ok) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrOperationFailed; + return; + } + if (((longAt((void *)(myList))) & (classIndexMask())) != GIV(classLinkedListClassTag)) { + backupContexttoBlockingSendTo(myContext, myList); + } + + /* stackTopPut: */ + longAtput(GIV(stackPointer),myList); +} + + +/* Primitive #578. Suspend the receiver, aProcess, such that it can be + executed again + by sending #resume. If the given process is not the active process, take + it off + its corresponding list. If the list was not its run queue assume it was on + some condition variable (Semaphore, Mutex) and back up its pc to the send + that invoked the wait state the process entered. Hence when the process + resumes it will reenter the wait state. Answer the list the receiver was + previously on iff + it was not active and not blocked, otherwise answer nil. + c.f. primitiveSuspend,#88 & primitiveSuspendBackingUpV1,#568, + which always answer the list the process was on, even if blocked. */ + + /* CoInterpreterPrimitives>>#primitiveSuspendBackingUpV2 */ +EXPORT(void) +primitiveSuspendBackingUpV2(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int inInterpreter; + sqInt myContext; + sqInt myList; + usqInt numSlots; + sqInt ok; + sqInt process; + + process = longAt(GIV(stackPointer)); + if (process == (activeProcess())) { + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(nilObj)); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSSuspend); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + return; + } + myList = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord())))))); + myContext = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if (!((/* isPointers: */ + ((!(myList & (tagMask())))) + && (((byteAt((void *)(myList + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(myList)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(myList + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(myList - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > LastLinkIndex) + && ((((longAt((void *)(myContext))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((/* begin isResumableContext: */ + assert(isContext(myContext)), + ((((longAt((void *)((myContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + ok = removeProcessfromList(process, myList); + if (!ok) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrOperationFailed; + return; + } + if (((longAt((void *)(myList))) & (classIndexMask())) != GIV(classLinkedListClassTag)) { + backupContexttoBlockingSendTo(myContext, myList); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(nilObj)); + } + else { + /* stackTopPut: */ + longAtput(GIV(stackPointer),myList); + } +} + + +/* Primitive. Terminate up the context stack from the receiver up to but not + including the argument, if previousContext is on my Context stack. Make + previousContext my + sender. This prim has to shadow the code in ContextPart>terminateTo: to be + correct. + Override to ensure the caller's saved ip is correct, i.e. if an + interpreter frame it may + have to move to iframeSavedIP. */ + + /* CoInterpreterPrimitives>>#primitiveTerminateTo */ +static void +primitiveTerminateTo(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aContextOrNil; + char *callerFP; + char *callerFP1; + char *contextsFP; + usqInt contextsIP; + char *contextsSP; + sqInt currentCtx; + char *fp; + char *fp1; + char *frameAbove; + sqInt handlerOrNilOrZero; + char *newFP; + char *newSP; + sqInt nextCntx; + StackPage *pageToStopOn; + sqInt senderOop; + sqInt senderOopSqInt; + char *source; + sqInt stackedReceiverOffset; + char *theFP; + char *theFP1; + StackPage *thePage; + StackPage *thePage1; + sqInt thisCtx; + sqInt valuePointer; + + contextsFP = ((char *) 0); + aContextOrNil = longAt(GIV(stackPointer)); + if (!((aContextOrNil == GIV(nilObj)) + || (/* isContext: */ + ((!(aContextOrNil & (tagMask())))) + && (((longAt((void *)(aContextOrNil))) & (classIndexMask())) == ClassMethodContextCompactIndex)))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + thisCtx = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (thisCtx == aContextOrNil) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* If we're searching for aContextOrNil it might be on a stack page. Helps to know + if we can free a whole page or not, or if we can short-cut the termination. */ + if ((aContextOrNil != GIV(nilObj)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContextOrNil))))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + contextsFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + pageToStopOn = stackPageAtpages(pageIndexFor(contextsFP), GIV(pages)); + } + else { + pageToStopOn = 0; + } + + /* if thisCtx is married ensure it is a base frame. Then we can assign its sender. */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(thisCtx)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* Optimize terminating thisContext. Move its frame down to be next to + aContextOrNil's frame. Common in the exception system and so helps to be fast. */ + if ((theFP == GIV(framePointer)) + && (pageToStopOn == GIV(stackPage))) { + assertValidStackedInstructionPointersInline(GIV(stackPage), __LINE__); + if ((((char *)(longAt(theFP + FoxSavedFP)))) != contextsFP) { + stackedReceiverOffset = (FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))); + + /* begin findFrameAbove:inPage: */ + callerFP = ((char *) 0); + fp = (pageToStopOn->headFP); + if (fp == contextsFP) { + frameAbove = 0; + goto l1; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == contextsFP) { + frameAbove = fp; + goto l1; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + frameAbove = 0; + /* end findFrameAbove:inPage: */ +l1: + contextsIP = longAt(frameAbove + FoxCallerSavedIP); + assert((((((usqInt)contextsIP)) >= (startOfMemory())) + || (contextsIP == (ceReturnToInterpreterPC()))) == (!(isMachineCodeFrame(contextsFP)))); + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(frameAbove))); + newSP = (frameAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(frameAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(frameAbove))->cmNumArgs) + : byteAt((frameAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + newFP = (newSP - stackedReceiverOffset) - BytesPerWord; + for (source = (theFP + stackedReceiverOffset); source >= GIV(stackPointer); source += (-BytesPerWord)) { + newSP -= BytesPerWord; + longAtput(newSP,longAt(source)); + } + longAtput(newFP + FoxSavedFP,((usqInt)contextsFP)); + + /* Ensure contract between machine-code callee and interpreter caller frames is preserved. + Return pc needs to be ceReturnToInterpreterPC. */ + if (((((usqInt)(longAt(newFP + FoxMethod)))) < (startOfMemory())) + && (contextsIP >= (startOfMemory()))) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(contextsFP))); + longAtput(contextsFP + FoxIFSavedIP,contextsIP); + contextsIP = ceReturnToInterpreterPC(); + } + longAtput(newFP + FoxCallerSavedIP,contextsIP); + assert(isContext(thisCtx)); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(newFP)) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(newFP)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(thisCtx)) + && (!(isForwarded(thisCtx)))); + assert(validStorePointerUncheckedArgs(SenderIndex, thisCtx, valuePointer)); + longAtput((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),valuePointer); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(contextsFP)) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(contextsFP)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(thisCtx)) + && (!(isForwarded(thisCtx)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, thisCtx, valuePointer)); + longAtput((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointer); + GIV(framePointer) = newFP; + GIV(stackPointer) = newSP; + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + assertValidStackedInstructionPointersInline(GIV(stackPage), __LINE__); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + return; + } + assertValidStackedInstructionPointers(__LINE__); + + /* May cause a GC!! */ + theFP = externalEnsureIsBaseFrame(theFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + + /* May also reclaim aContextOrNil's page, hence... */ + if ((aContextOrNil != GIV(nilObj)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContextOrNil))))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + contextsFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + pageToStopOn = stackPageAtpages(pageIndexFor(contextsFP), GIV(pages)); + } + else { + pageToStopOn = 0; + } + } + else { + currentCtx = longAt((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + } + assertValidStackedInstructionPointers(__LINE__); + + /* begin context:hasSender: */ + handlerOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(-1, thisCtx, aContextOrNil); + if (!handlerOrNilOrZero) { + /* Need to walk the stack freeing stack pages and nilling contexts. */ + while (!((currentCtx == aContextOrNil) + || (currentCtx == GIV(nilObj)))) { + assert(isContext(currentCtx)); + if (((((longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + + /* If externalEnsureIsBaseFrame: above has moved thisContext to its own stack + then we will always terminate to a frame on a different page. But if we are + terminating some other context to a context somewhere on the current page + we must save the active frames above that context. Things will look e.g. like this: + thisCtx 499383332 s MethodContext(ContextPart)>resume: + 499380484 s BlockClosure>ensure: + 499377320 s MethodContext(ContextPart)>handleSignal: + 499373760 s MethodContext(ContextPart)>handleSignal: + 499372772 s MessageNotUnderstood(Exception)>signal + 499369068 s CodeSimulationTests(Object)>doesNotUnderstand: absentMethod + 499368708 s [] in CodeSimulationTests>testDNU + (sender is 0xbffc2480 I CodeSimulationTests>runSimulated:) + ------------ + framePointer 0xbffc234c M MethodContext(ContextPart)>doPrimitive:method:receiver:args: + 0xbffc2378 M MethodContext(ContextPart)>tryPrimitiveFor:receiver:args: + 0xbffc23ac M MethodContext(ContextPart)>send:to:with:super: + 0xbffc23e4 M MethodContext(ContextPart)>send:super:numArgs: + 0xbffc2418 M MethodContext(InstructionStream)>interpretNextInstructionFor: + 0xbffc2434 M MethodContext(ContextPart)>step + 0xbffc2458 I MethodContext(ContextPart)>runSimulated:contextAtEachStep: + ------------ + (499368708's sender) 0xbffc2480 I CodeSimulationTests>runSimulated: + 0xbffc249c M CodeSimulationTests>testDNU + 0xbffc24bc I CodeSimulationTests(TestCase)>performTest + 0xbffc24dc I [] in CodeSimulationTests(TestCase)>runCase + aContextOrNil 0xbffc24fc M BlockClosure>ensure: + 0xbffc2520 I CodeSimulationTests(TestCase)>runCase + 0xbffc253c M [] in TestResult>runCase: + When we find this case we move the frames above to a new page by making the + frame above currentCtx a base frame, i.e. making 0xbffc2458 in the above example + a base frame. But in this iteration of the loop we don't move down a frame i.e. currentCtx + doesn't change on this iteration. */ + if (thePage == GIV(stackPage)) { + /* begin findFrameAbove:inPage: */ + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == theFP) { + frameAbove = 0; + goto l2; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == theFP) { + frameAbove = fp; + goto l2; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + frameAbove = 0; + /* end findFrameAbove:inPage: */ +l2: + assert(frameAbove != 0); + + /* May cause a GC!! May also reclaim aContextOrNil's page, hence... */ + frameAbove = externalEnsureIsBaseFrame(frameAbove); + if ((aContextOrNil != GIV(nilObj)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContextOrNil))))) { + /* begin frameOfMarriedContext: */ + senderOopSqInt = longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOopSqInt) & 7) == 1)); + contextsFP = ((char *)(senderOopSqInt - (smallIntegerTag()))); + + /* begin stackPageFor: */ + pageToStopOn = stackPageAtpages(pageIndexFor(contextsFP), GIV(pages)); + } + else { + pageToStopOn = 0; + } + } + else { + if (thePage == pageToStopOn) { + /* We're here. Cut back the stack to aContextOrNil's frame, + push its instructionPointer if it's not already a head frame, + and we're done. */ + + /* begin findFrameAbove:inPage: */ + callerFP1 = ((char *) 0); + fp1 = (thePage->headFP); + if (fp1 == contextsFP) { + frameAbove = 0; + goto l3; + } + while (((callerFP1 = ((char *)(longAt(fp1 + FoxSavedFP))))) != 0) { + if (callerFP1 == contextsFP) { + frameAbove = fp1; + goto l3; + } + fp1 = callerFP1; + } + error("did not find theFP in stack page"); + frameAbove = 0; + /* end findFrameAbove:inPage: */ +l3: + if (frameAbove) { + contextsSP = ((/* begin frameCallerSP: */ + assert(!(isBaseFrame(frameAbove))), +(frameAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(frameAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(frameAbove))->cmNumArgs) + : byteAt((frameAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)) - BytesPerWord; + longAtput(contextsSP,longAt(frameAbove + FoxCallerSavedIP)); + + /* begin setHeadFP:andSP:inPage: */ + assert(contextsSP < contextsFP); + assert((contextsSP < ((thePage->baseAddress))) + && (contextsSP > (((thePage->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((contextsFP < ((thePage->baseAddress))) + && (contextsFP > (((thePage->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (thePage->headFP = contextsFP); + (thePage->headSP = contextsSP); + } + currentCtx = aContextOrNil; + } + else { + /* We can free the entire page without further ado. */ + theFP1 = (thePage->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP1)); + thePage1 = stackPageAtpages(pageIndexFor(theFP1), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + + /* for a short time invariant is violated; assert follows */ + freeStackPageNoAssert(thePage); + } + } + } + else { + nextCntx = longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + + /* begin markContextAsDead: */ + assert(isContext(currentCtx)); + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(SenderIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + currentCtx = nextCntx; + } + } + } + assert(pageListIsWellFormed()); + if (((((longAt((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert(checkIsStillMarriedContextcurrentFP(thisCtx, GIV(framePointer))); + assert(isBaseFrame(frameOfMarriedContext(thisCtx))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin frameCallerContext:put: */ + assert((aContextOrNil == (nilObject())) + || (isContext(aContextOrNil))); + assert(isBaseFrame(theFP)); + assert(((theFP + (frameStackedReceiverOffset(theFP))) + (2 * BytesPerWord)) == (((stackPageFor(theFP))->baseAddress))); + assert((longAt((theFP + (frameStackedReceiverOffset(theFP))) + BytesPerWord)) == (frameContext(theFP))); + longAtput((theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + (2 * BytesPerWord),aContextOrNil); + } + else { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SenderIndex, thisCtx, aContextOrNil)); + assert(isNonImmediate(thisCtx)); + if (oopisGreaterThanOrEqualTo(thisCtx, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(aContextOrNil & (tagMask())))) + && (oopisLessThan(aContextOrNil, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(thisCtx + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(thisCtx); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),aContextOrNil); + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + assertValidStackedInstructionPointers(__LINE__); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); +} + + +/* Primitive. Unload the module with the given name. + Reloading of the module will happen *later* automatically, when a + function from it is called. This is forced by invalidating all external + primitive methods and activations in flushExternalPrimitives. + N.B. since this is most likely a development time activity we don't care + about performance. */ + + /* CoInterpreterPrimitives>>#primitiveUnloadModule */ +static void +primitiveUnloadModule(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt moduleLength; + sqInt moduleName; + usqInt numSlots; + + moduleName = longAt(GIV(stackPointer)); + if (!(/* isBytes: */ + ((!(moduleName & (tagMask())))) + && (((byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + moduleLength = ((((/* begin numSlotsOf: */ + assert((classIndexOf(moduleName)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(moduleName + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(moduleName - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (!(ioUnloadModuleOfLength(oopForPointer(firstIndexableField(moduleName)), moduleLength))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + if (/* object:equalsString:ofSize: */ + (/* isBytes: */ + ((!(moduleName & (tagMask())))) + && (((byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((!(((byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((numBytesOfBytes(moduleName)) == moduleLength) + && ((strncmp("SqueakFFIPrims", firstIndexableField(moduleName), moduleLength)) == 0)))) { + primitiveCalloutPointer = ((void *) -1); + } + + /* N.B. flushExternalPrimitives continues. Do *not* do anything after flushExternalPrimitives in the CoInterpreter */ + forceInterruptCheck(); + flushExternalPrimitives(); +} + + +/* Void all internal VM state in the stack and machine code zones + + Override to jump to the interpreter because the machine code zone is now + void. + */ + + /* CoInterpreterPrimitives>>#primitiveVoidVMState */ +static void +primitiveVoidVMState(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + activeContext = voidVMStateForSnapshotFlushingExternalPrimitivesIf(0); + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + ceInvokeInterpret(); +} + + +/* The receiver (or first argument) must be a compiledMethod. The optional + (or second) argument must be a + boolean. Clear all VM state associated with the method, including any + machine code, or machine code pcs + in context objects. If the optional boolean argument is false do not scan + the heap looking for contexts. */ + + /* CoInterpreterPrimitives>>#primitiveVoidVMStateForMethod */ +static void +primitiveVoidVMStateForMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt address; + StackPage *aPage; + char *calleeFP; + sqInt classIndex; + CogMethod *cogMethod; + sqInt divorcedSome; + sqInt followingWord; + usqInt followingWordAddress; + int hasCogMethod; + sqInt i; + sqInt methodHeader; + sqInt methodObj; + usqInt numSlots; + sqInt obj; + sqInt object; + sqInt prevObj; + sqInt prevPrevObj; + sqInt scanHeapForContexts; + sqInt senderOop; + sqInt startObject; + sqInt theContext; + char *theFP; + char *theFrame; + StackPage *thePage; + char *theSP; + sqInt top; + + /* See comment ''One might think...'' below */ + /* In Smalltalk allow both aMethod voidCogVMState and aMethod voidCogVMStateScanningContextsIf: aBoolean */ + scanHeapForContexts = 1; + if (GIV(argumentCount)) { + methodObj = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin booleanValueOf: */ + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + scanHeapForContexts = 1; + goto l1; + } + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + scanHeapForContexts = 0; + goto l1; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + scanHeapForContexts = null; + /* end booleanValueOf: */ +l1: + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + else { + methodObj = longAt(GIV(stackPointer)); + } + if (GIV(argumentCount) > 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + + /* In the NewspeakVM we allow VMMirror voidStateFor: method scanningIf: aBoolean as well as the Smalltalk forms. */ + flushMethodCacheForMethod(methodObj); + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l2; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l2: + + /* begin ensurePushedInstructionPointer */ + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if ((hasCogMethod = methodHasCogMethod(methodObj))) { + /* begin divorceMachineCodeFramesWithMethod: */ + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + do { + if (GIV(stackPage)) { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + + /* This is needed for the assert in externalDivorceFrame:andContext: + Slang can't currently cope with the lack of the variable here. + Something to do with the preceding statement. Take it out + and the code is good. leave it in and we get do { ... } while(l1:) */ + + /* begin divorceSomeMachineCodeFramesWithMethod: */ + divorcedSome = 0; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + aPage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(aPage))) { + /* this to avoid assert in externalDivorceFrame:andContext: */ + markStackPageMostRecentlyUsed(GIV(stackPage)); + + /* begin divorceAMachineCodeFrameWithCogMethod:in: */ + calleeFP = ((char *) 0); + theFP = (aPage->headFP); + theSP = (aPage->headSP); + + /* theSP points at hottest item on frame's stack */ + theSP += BytesPerWord; + while (1) { + if (((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && (cogMethod == (mframeHomeMethod(theFP)))) { + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + theContext = longAt(theFP + FoxThisContext); + goto l6; + } + theContext = marryFrameSP(theFP, theSP); + /* end ensureFrameIsMarried:SP: */ +l6: + externalDivorceFrameandContext(theFP, theContext); + goto l4; + } + calleeFP = theFP; + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!(theFP != 0)) break; + /* theSP points at stacked hottest item on frame's stack */ + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(calleeFP))); + theSP = (calleeFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(calleeFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(calleeFP))->cmNumArgs) + : byteAt((calleeFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + goto l5; +l4: + divorcedSome = 1; +l5:; + } + } + } while(divorcedSome); + } + + /* One might think (as this author did) that the heap scan is unnecessary if the method does not + have a cog method. But it could be the case that the code zone has recently been reclaimed + and so not having a cog method is no indication that it didn't have a cog method some time in + the recent past, and that there are indeed still contexts with machine code pcs out there. The + only steps that can be avoided are divorcing frames in the stack zone, and scanning to unlink and + free if there isn't a cog method, unless we are told otherwise. */ + if (scanHeapForContexts) { + /* begin ensureAllContextsWithMethodHaveBytecodePCs: */ + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord()))))))) == methodObj)) { + widowOrForceToBytecodePC(obj); + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l3: + assert(oopisGreaterThan(obj, prevObj)); + } + } + if (hasCogMethod) { + unlinkSendsToandFreeIf(methodObj, 1); + } + + /* If flushing led to divorce continue in the interpreter. */ + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + assert((methodObj == (stackTop())) + || ((GIV(argumentCount) > 0) + && (methodObj == (stackValue(1))))); + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + ceInvokeInterpret(); + } + + /* If not, work out where we are and continue */ + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + assert(((thePage->headFP)) == theFrame); + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + assert((methodObj == (stackTop())) + || ((GIV(argumentCount) > 0) + && (methodObj == (stackValue(1))))); + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + /* CoInterpreterPrimitives>>#primitiveWait */ +static void +primitiveWait(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + sqLong excessSignals; + int inInterpreter; + sqInt objOop; + sqInt sema; + + /* rcvr */ + sema = longAt(GIV(stackPointer)); + excessSignals = fetchIntegerofObject(ExcessSignalsIndex, sema); + if (excessSignals > 0) { + storeIntegerofObjectwithValue(ExcessSignalsIndex, sema, excessSignals - 1); + } + else { + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + addLastLinktoList(activeProc, sema); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSEnterCriticalSection); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } +} + + +/* Primitively do the equivalent of Process>yield, avoiding the overhead of a + fork and a wait in the standard implementation. + Get the Processor object (1), from it get the activeProcess (2) and hence + the activeProcess's priority (3), and the array of + all runnable processes (5). Via the priority get the list of quiescent + processes at the active priority (6) (implicitly we know + there are no quiescent processes at higher priorities than the + activeProcess which is always one of the highest priority + runnable processes). */ +/* 1 */ + + /* CoInterpreterPrimitives>>#primitiveYield */ +static void +primitiveYield(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + int inInterpreter; + sqInt oop; + sqInt priority; + sqInt processList; + sqInt processLists; + sqInt scheduler; + + scheduler = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* 2 */ + activeProc = longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* 3 */ + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + priority = (oop >> 3); + + /* 4 */ + processLists = longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + + /* 5 */ + processList = longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((priority - 1)) << (shiftForWord())))))); + + /* If the list of quiescent processes at this priority is empty there are no processes to yield to. + If the list is not empty, activate the first one, and add the active process to the end of the list. */ + + /* begin isEmptyList: */ + assert(!(isForwarded(processList))); + if ((longAt((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj)) { + return; + } + + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + addLastLinktoList(activeProc, processList); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSYield); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); +} + + /* CoInterpreterPrimitives>>#unmarkAllFrames */ +static void +unmarkAllFrames(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt flags; + sqInt i; + sqInt methodField; + char *theFP; + StackPage *thePage; + + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + theFP = (thePage->headFP); + do { + methodField = longAt(theFP + FoxMethod); + if ((((usqInt)methodField)) < (startOfMemory())) { + if (methodField & 4) { + longAtput(theFP + FoxMethod,methodField - 4); + } + } + else { + flags = longAt(theFP + FoxIFrameFlags); + if (flags & 2) { + longAtput(theFP + FoxIFrameFlags,flags - 2); + } + } + } while(((theFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0); + } + } +} + + +/* Initialize the stack pages. In the C VM theStackPages will be alloca'ed + memory to hold the + stack pages on the C stack. In the simulator they are housed in the memory + between the + cogMethodZone and the heap. */ + + /* CoInterpreterStackPages>>#initializeStack:numSlots:pageSize: */ +static NoDbgRegParms void +initializeStacknumSlotspageSize(char *theStackPages, sqInt stackSlots, sqInt slotsPerPage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt count; + sqInt index; + sqInt numPages; + StackPage *page; + char *pageStructBase; + void *pointer; + usqIntptr_t structStackPageSize; + sqInt theIndex; + + structStackPageSize = sizeof(CogStackPage); + GIV(bytesPerPage) = slotsPerPage * BytesPerWord; + numPages = GIV(numStackPages); + + /* Because stack pages grow down baseAddress is at the top of a stack page and so to avoid + subtracting BytesPerWord from baseAddress and lastAddress in the init loop below we simply + push the stackPage array up one word to avoid the overlap. This word is extraStackBytes. */ + pageStructBase = (theStackPages + (numPages * GIV(bytesPerPage))) + BytesPerWord; + GIV(pages) = ((StackPage *) pageStructBase); + + /* make sure there's enough headroom */ + assert((((stackPageByteSize()) - (stackLimitBytes())) - (stackLimitOffset())) >= (stackPageHeadroom())); + for (index = 0; index < numPages; index += 1) { + /* begin stackPageAt: */ + page = stackPageAtpages(index, GIV(pages)); + (page->lastAddress = theStackPages + (index * GIV(bytesPerPage))); + (page->baseAddress = ((page->lastAddress)) + GIV(bytesPerPage)); + (page->stackLimit = ((page->baseAddress)) - ((((stackPageFrameBytes()) < (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))) ? (stackPageFrameBytes()) : (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))))); + (page->realStackLimit = (page->stackLimit)); + (page->baseFP = 0); + (page->nextPage = stackPageAt((index == (numPages - 1) + ? 0 + : index + 1))); + (page->prevPage = stackPageAt((index + ? index - 1 + : numPages - 1))); + } + + /* Now compute stackBasePlus1 so that the pageIndexFor: call maps all addresses from + aPage baseAddress to aBase limitAddress + 1 to the same index (stacks grow down) */ + GIV(stackBasePlus1) = (((char *) theStackPages)) + 1; + + /* The overflow limit is the amount of stack to retain when moving frames from an overflowing + stack to reduce thrashing. See stackOverflowOrEvent:mayContextSwitch: */ + + /* begin stackPageAt: */ + page = stackPageAtpages(0, GIV(pages)); + GIV(overflowLimit) = ((((page->baseAddress)) - ((page->realStackLimit))) * 3) / 5; + for (index = 0; index < numPages; index += 1) { + /* begin stackPageAt: */ + page = stackPageAtpages(index, GIV(pages)); + assert((pageIndexFor((page->baseAddress))) == index); + assert((pageIndexFor(((page->baseAddress)) - ((slotsPerPage - 1) * BytesPerWord))) == index); + assert((stackPageFor((page->baseAddress))) == page); + assert((stackPageFor((page->stackLimit))) == page); + + /* begin initializePageTraceToInvalid: */ + (page->trace = StackPageTraceInvalid); + } + GIV(mostRecentlyUsedPage) = stackPageAtpages(0, GIV(pages)); + page = GIV(mostRecentlyUsedPage); + count = 0; + do { + count += 1; + pointer = (page->baseAddress); + + /* begin pageIndexFor: */ +# if !NDEBUG + assert((((((char *) pointer)) >= (GIV(stackBasePlus1) - 1)) && ((((char *) pointer)) <= (((char *) GIV(pages)))))); +# endif // !NDEBUG + + + /* this to avoid an annoying warning: left operand of comma operator has no effect */ + theIndex = pageIndexForstackBasePlus1bytesPerPage(pointer, GIV(stackBasePlus1), GIV(bytesPerPage)); + assert((stackPageAt(theIndex)) == page); + assert((pageIndexFor((page->baseAddress))) == theIndex); + assert((pageIndexFor((page->stackLimit))) == theIndex); + assert((pageIndexFor(((page->lastAddress)) + 1)) == theIndex); + } while(((page = (page->nextPage))) != GIV(mostRecentlyUsedPage)); + assert(count == numPages); + assert(pageListIsWellFormed()); +} + + +/* */ +/* Answer the page index for a pointer into stack memory, i.e. the index + for the page the address is in. N.B. This is a zero-relative index. */ + + /* CoInterpreterStackPages>>#pageIndexFor: */ +static NoDbgRegParms sqInt +pageIndexFor(void *pointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + +# if !NDEBUG + assert((((((char *) pointer)) >= (GIV(stackBasePlus1) - 1)) && ((((char *) pointer)) <= (((char *) GIV(pages)))))); +# endif + + + /* this to avoid an annoying warning: left operand of comma operator has no effect */ + return pageIndexForstackBasePlus1bytesPerPage(pointer, GIV(stackBasePlus1), GIV(bytesPerPage)); +} + + +/* If anOop is an address within the stack zone answer a string stating that, + otherwise answer nil. + */ + + /* CoInterpreterStackPages>>#whereIsMaybeStackThing: */ +static NoDbgRegParms char * +whereIsMaybeStackThing(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (oopisGreaterThanOrEqualToandLessThan(anOop, GIV(stackBasePlus1) - 1, GIV(pages))) { + return " is in the stack zone"; + } + return null; +} + + +/* Answer either a malloced string with the null-terminated contents of oop + if oop is a string, + or the null pointer if oop is nil, or fail. It is the client's + responsibility to free the string later. */ + + /* InterpreterPrimitives>>#cStringOrNullFor: */ +char * +cStringOrNullFor(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + char *cString; + sqInt fmt; + sqInt isString; + sqInt len; + usqInt numSlots; + usqInt numSlotsUsqInt; + + /* begin isInstanceOfClassByteString: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((oop & (tagMask())) != 0)) { + isString = 0; + goto l2; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + isString = ClassByteStringCompactIndex == ccIndex; + /* end isInstanceOfClassByteString: */ +l2: + if (!isString) { + if (oop != GIV(nilObj)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + return 0; + } + + /* begin lengthOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + len = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + len = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + len = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + len = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + len = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + len = 0; + /* end lengthOf: */ +l1: + if (!len) { + return 0; + } + cString = malloc(len + 1); + if (!cString) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoCMemory; + return 0; + } + memcpy(cString, firstIndexableField(oop), len); + cString[len] = 0; + return cString; +} + + +/* In C, non-zero is true, so avoid computation by simply answering + primFailCode in the C version. + */ + + /* InterpreterPrimitives>>#failed */ +sqInt +failed(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(primFailCode); +} + + +/* Answer the identity hash of an object, assigning a hash if it doesn't have + one. On Spur refuse to assign a hash to something that looks like a + behavior. + */ + + /* InterpreterPrimitives>>#identityHashOf: */ +sqInt +identityHashOf(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt hash; + usqInt hashUsqInt; + + if (((anOop & (tagMask())) != 0)) { + /* begin immediateAsInteger:ifFail: */ + if ((((anOop) & 7) == 1)) { + return ((usqInt) ((anOop >> 3))); + } + if (((anOop & (characterTag())) != 0)) { + return ((((usqInt)anOop))) >> (numTagBits()); + } + if (((anOop & (smallFloatTag())) != 0)) { + /* begin rotatedFloatBitsOf: */ + assert(isImmediateFloat(anOop)); + return (((((usqInt)anOop))) >> ((numTagBits()) + 1)) + ((((anOop & (1U << (numTagBits()))) != 0) + ? ((((usqInt)-1)) << ((64 - (numTagBits())) - 1)) + : 0)); + } + return ((usqInt) -1); + } + if ((((long32At((void *)(anOop + 4))) & (identityHashHalfWordMask())) == 0) + && (objCouldBeClassObj(anOop))) { + return -1; + } + + /* begin hashBitsOf: */ + hash = (long32At((void *)(anOop + 4))) & (identityHashHalfWordMask()); + if (!hash) { + /* cb 1/19/2017 18:34: + would like to assert + self assert: (coInterpreter addressCouldBeClassObj: objOop) not + but instance-specific behaviors that are instances of themselves may + fail this test. + eem 12/28/2021 11:52 I'm not sure this is the issue. The issue is only validating objOop as a behavior. */ + + /* begin newHashBitsOf: */ + /* begin newObjectHash */ + assert(!((GIV(lastHash) == 0))); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 2)); + GIV(lastHash) = GIV(lastHash) ^ ((((GIV(lastHash) << 7))) & (identityHashHalfWordMask())); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 3)); + hashUsqInt = GIV(lastHash); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(anOop + 4),((((long32At((void *)(anOop + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hashUsqInt & (identityHashHalfWordMask()))); + hash = hashUsqInt & (identityHashHalfWordMask()); + } + return hash; +} + + +/* Answer true if integer object is negative. + Fail if object pointed by oop i not an integer. */ + + /* InterpreterPrimitives>>#isNegativeIntegerValueOf: */ +static NoDbgRegParms sqInt +isNegativeIntegerValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + int ok; + sqInt smallInt; + + if ((((oop) & 7) == 1)) { + smallInt = (oop >> 3); + return smallInt < 0; + } + if ((!(oop & (tagMask())))) { + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (ok) { + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargeNegativeIntegerCompactIndex == ccIndex; + if (ok) { + return 1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; +} + + +/* Answer if oop is a value of an integer in address range, i.e up to the + size of a machine word. + The object may be either a positive SmallInteger or a LargePositiveInteger + of size <= word size. + */ + + /* InterpreterPrimitives>>#isPositiveMachineIntegerObject: */ +sqInt +isPositiveMachineIntegerObject(sqInt oop) +{ + sqInt ccIndex; + int ok; + + if ((((oop) & 7) == 1)) { + return ((oop >> 3)) >= 0; + } + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + return ok + && ((numBytesOfBytes(oop)) <= (sizeof(usqIntptr_t))); +} + + +/* Return a Large Integer object for the given integer magnitude and sign */ + + /* InterpreterPrimitives>>#magnitude64BitIntegerFor:neg: */ +static NoDbgRegParms sqInt +magnitude64BitIntegerForneg(usqLong magnitude, sqInt isNegative) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int isSmall; + int largeClassIndex; + usqInt newLargeInteger; + usqInt newObj; + usqInt numBytesUsqInt; + sqInt numSlots; + sqInt objFormat; + sqInt smallVal; + sqInt sz; + + isSmall = (isNegative + ? magnitude <= ((MaxSmallInteger) + 1) + : magnitude <= (MaxSmallInteger)); + if (isSmall) { + smallVal = ((sqInt) magnitude); + if (isNegative) { + smallVal = 0 - smallVal; + } + return (((usqInt)smallVal << 3) | 1); + } + largeClassIndex = (isNegative + ? ClassLargeNegativeIntegerCompactIndex + : ClassLargePositiveIntegerCompactIndex); + sz = 8; + objFormat = (firstByteFormat()) + ((8 - sz) & (BytesPerWord - 1)); + + /* begin eeInstantiateSmallClassIndex:format:numBytes: */ + assert((sz >= 0) + && ((largeClassIndex != 0) + && ((knownClassAtIndex(largeClassIndex)) != GIV(nilObj)))); + assert(((objFormat < (firstByteFormat()) + ? objFormat + : objFormat & (byteFormatMask()))) == (instSpecOfClass(knownClassAtIndex(largeClassIndex)))); + numSlots = ((sz + BytesPerWord) - 1) / BytesPerWord; + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytesUsqInt = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytesUsqInt % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytesUsqInt) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytesUsqInt) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newLargeInteger = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + largeClassIndex); + GIV(freeStart) += numBytesUsqInt; + newLargeInteger = newObj; + /* end eeInstantiateSmallClassIndex:format:numBytes: */ +l1: + + /* Memory is eight byte aligned in SPUR, so we are sure to have room for 64bits word whatever allocated sz */ + + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((newLargeInteger + BaseHeaderSize)),SQ_SWAP_8_BYTES_IF_BIGENDIAN(magnitude)); + return newLargeInteger; +} + + +/* Convert the given object into an integer value. + The object may be either a positive SmallInteger or up to an eight-byte + LargeInteger. + */ + + /* InterpreterPrimitives>>#magnitude64BitValueOf: */ +static NoDbgRegParms usqLong +magnitude64BitValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + sqInt fmt; + usqInt numSlots; + int ok; + sqInt smallIntValue; + sqInt sz; + + if ((((oop) & 7) == 1)) { + smallIntValue = (oop >> 3); + if (smallIntValue < 0) { + smallIntValue = 0 - smallIntValue; + } + return smallIntValue; + } + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (!ok) { + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargeNegativeIntegerCompactIndex == ccIndex; + if (!ok) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + sz = ((((/* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (sz > (sizeof(sqLong))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + if (sz > 4) { + return SQ_SWAP_8_BYTES_IF_BIGENDIAN((long64At((void *)((oop + BaseHeaderSize))))); + } + return ((unsigned int) (SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((oop + BaseHeaderSize))))))); +} + + +/* Answer a value of an integer in address range, i.e up to the size of a + machine word. + The object is known not to be a SmallIntege. It is hoped to be a + LargePositiveInteger of size <= word size. + */ + + /* InterpreterPrimitives>>#positiveMachineIntegerValueOfObj: */ +static NoDbgRegParms usqIntptr_t +positiveMachineIntegerValueOfObj(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bs; + sqInt ccIndex; + sqInt fmt; + sqInt isClassOfNonImmequalTocompactClassIndexRV; + usqInt numSlots; + + assert(!(((((oop) & 7) == 1)))); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + isClassOfNonImmequalTocompactClassIndexRV = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (!isClassOfNonImmequalTocompactClassIndexRV) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + bs = ((((/* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (bs > (sizeof(usqIntptr_t))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + if (((sizeof(usqIntptr_t)) == 8) + && (bs > 4)) { + return SQ_SWAP_8_BYTES_IF_BIGENDIAN((long64At((void *)((oop + BaseHeaderSize))))); + } + return ((unsigned int) (SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((oop + BaseHeaderSize))))))); +} + + +/* Answer a value of an integer in address range, i.e up to the size of a + machine word. + The object may be either a positive SmallInteger or a LargePositiveInteger + of size <= word size. + */ +/* some important callers such as primitiveNewWithArg, so inline the common + case + */ + + /* InterpreterPrimitives>>#positiveMachineIntegerValueOf: */ +usqIntptr_t +positiveMachineIntegerValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt value; + + if ((((oop) & 7) == 1)) { + value = (oop >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return value; + } + + /* don't inline the rare case */ + return positiveMachineIntegerValueOfObj(oop); +} + + /* InterpreterPrimitives>>#primitiveAdd */ +static void +primitiveAdd(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerResult; + + integerResult = (stackIntegerValue(1)) + (stackIntegerValue(0)); + + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)(integerResult)) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerResult << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveAddLargeIntegers */ +EXPORT(void) +primitiveAddLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + sqInt resultIsNegative; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (GIV(primFailCode)) { + return; + } + if (aIsNegative == bIsNegative) { + /* Protect against overflow */ + if (a > (0xFFFFFFFFFFFFFFFFULL - b)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + result = a + b; + resultIsNegative = aIsNegative; + } + else { + if (a >= b) { + result = a - b; + resultIsNegative = aIsNegative; + } + else { + result = b - a; + resultIsNegative = bIsNegative; + } + } + oopResult = magnitude64BitIntegerForneg(result, resultIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Primitive. Change the class of the argument to make it an instance of the + receiver given that the format of the receiver matches the format of the + argument's class. + Fail if receiver or argument are SmallIntegers, or the receiver is an + instance of a + compact class and the argument isn't, or when the argument's class is + compact and + the receiver isn't, or when the format of the receiver is different from + the format of + the argument's class, or when the arguments class is fixed and the + receiver's size + differs from the size that an instance of the argument's class should + have. + */ + + /* InterpreterPrimitives>>#primitiveAdoptInstance */ +static void +primitiveAdoptInstance(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt err; + sqInt rcvr; + + arg = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((arg & (tagMask())) != 0)) + || ((GIV(argumentCount) > 1) + && ((((rcvr & (tagMask())) != 0)) + || (!(objCouldBeClassObj(rcvr)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + err = changeClassOfto(arg, rcvr); + if (err) { + /* changeClassOf:to: answers errors as if rcvr (the class) is an argument... */ + if (err == PrimErrBadReceiver) { + err = PrimErrBadArgument; + } + else { + if (err == PrimErrBadArgument) { + err = PrimErrBadReceiver; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = err; + } + else { + /* Flush at cache because rcvr's class has changed. */ + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + return; +} + + +/* Answer an array of all instances of the receiver that exist + when the primitive is called, excluding any that may be + garbage collected as a side effect of allocating the result array. */ + + /* InterpreterPrimitives>>#primitiveAllInstances */ +EXPORT(void) +primitiveAllInstances(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt delta; + sqInt result; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + result = allInstancesOf(longAt(GIV(stackPointer))); + if ((((result) & 7) == 1)) { + /* begin growToAccomodateContainerWithNumSlots: */ + delta = (BaseHeaderSize * 2) + (((result >> 3)) * BytesPerOop); + growOldSpaceByAtLeast(((GIV(growHeadroom) < delta) ? delta : GIV(growHeadroom))); + result = allInstancesOf(longAt(GIV(stackPointer))); + if ((((result) & 7) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + return; + } + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); +} + + +/* Answer an array of all objects that exist when the primitive + is called, excluding those that may be garbage collected as + a side effect of allocating the result array. */ + + /* InterpreterPrimitives>>#primitiveAllObjects */ +EXPORT(void) +primitiveAllObjects(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt delta; + sqInt result; + + result = allObjects(); + if ((((result) & 7) == 1)) { + /* begin growToAccomodateContainerWithNumSlots: */ + delta = (BaseHeaderSize * 2) + (((result >> 3)) * BytesPerOop); + growOldSpaceByAtLeast(((GIV(growHeadroom) < delta) ? delta : GIV(growHeadroom))); + result = allObjects(); + if ((((result) & 7) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + return; + } + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); +} + + +/* Computes arctan of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveArctan */ +static void +primitiveArctan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(atan(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + +/* Invoke the two-way become primitive. + We must at least flush the method cache here, to eliminate stale + references to mutated classes and/or selectors. + + If in the CoInterpreter, we must deal with jitted methods being becommed. + In the conception of the abstract VM, + unless a CompiledMethod is becommed to one with equivalent bytecode + contexts referring to the becommed + method will likely fetch invalid bytecodes on resumption. The + responsibility for validity here lies with the user of + the become primitive, not the VM. So one could imagine checking for + methods becomming equivalent methods + and updating Cog methods to refer to their becommed duals. But that + requires machinery to compare two + compiled methods to check if their code is equivalent. A much simpler + approach, which also gets the VM to fail + in a less confusing place if it is going to fail because the programmer + has not ensured compiled code validity + across become, is to simply divorce all frames and map context pcs back to + bytecode pcs when becomming + jitted methods. However, mapping native pcs in contexts whose methods are + flagged for become will not ensure + that pcs are mapped reliably. Consider this arc: a context on a method + that has been jitted exists with a native pc. + The method is unjitted to make room for other methods to be jitted. The + method is becommed. So the scan is only + effective for methods in the jit. Slowing down become so that a rare case + may fail more comprehensibly, when its + going to fail anyway, is a waste of effort. So we do divorce frames (since + machine code frames refer to CogMethods, + not methods), but don't scan the entire heap looking for native pcs in + contexts. Hence the primitive must be + prepared to have its calling frame divorced. We store the context for the + top frame in activeProcess. + BTW, as of late '22/early '23 this is only done for Spur. + See preBecomeAction: and postBecomeAction: */ + + /* InterpreterPrimitives>>#primitiveArrayBecome */ +static void +primitiveArrayBecome(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt ec; + sqInt rcvr; + + arg = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + ec = becomewithtwoWaycopyHash(rcvr, arg, 1, 0); + primitiveBecomeReturn(ec); +} + + +/* Invoke the one-way become primitive. + See the comment in primitiveArrayBecome for handling pervasive effects on + method cacheing and jitting. */ + + /* InterpreterPrimitives>>#primitiveArrayBecomeOneWay */ +static void +primitiveArrayBecomeOneWay(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt ec; + sqInt rcvr; + + arg = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + ec = becomewithtwoWaycopyHash(rcvr, arg, 0, 1); + primitiveBecomeReturn(ec); +} + + +/* Similar to primitiveArrayBecomeOneWay but accepts a third argument + deciding whether to + copy the receiver's elements identity hashes over the argument's elements + identity hashes. + See the comment in primitiveArrayBecome for handling pervasive effects on + method cacheing and jitting. + */ + + /* InterpreterPrimitives>>#primitiveArrayBecomeOneWayCopyHashArg */ +static void +primitiveArrayBecomeOneWayCopyHashArg(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt copyHashFlag; + sqInt ec; + + copyHashFlag = 0; + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + copyHashFlag = 1; + } + else { + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + copyHashFlag = 0; + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + ec = becomewithtwoWaycopyHash(longAt(GIV(stackPointer) + (2 * BytesPerWord)), longAt(GIV(stackPointer) + (1 * BytesPerWord)), 0, copyHashFlag); + primitiveBecomeReturn(ec); +} + + +/* Similar to primitiveArrayBecomeOneWay but does /not/ copy the receiver's + elements identity hashes over the argument's elements identity hashes. + See the comment in primitiveArrayBecome for handling pervasive effects on + method cacheing and jitting. */ + + /* InterpreterPrimitives>>#primitiveArrayBecomeOneWayNoCopyHash */ +static void +primitiveArrayBecomeOneWayNoCopyHash(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt ec; + sqInt rcvr; + + arg = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + ec = becomewithtwoWaycopyHash(rcvr, arg, 0, 0); + primitiveBecomeReturn(ec); +} + + /* InterpreterPrimitives>>#primitiveAsCharacter */ +static void +primitiveAsCharacter(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt characterCode; + sqInt characterObject; + + characterCode = longAt(GIV(stackPointer)); + if (!(((((characterCode) & 7) == 1)) + && (((characterCode = (characterCode >> 3)), + /* isInRangeCharacterCode: */ + ((characterCode >= 0) && (characterCode <= (0x3FFFFFFF))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = (GIV(argumentCount) + ? PrimErrBadArgument + : PrimErrBadReceiver); + return; + } + characterObject = ((((usqInt)(characterCode) << (numTagBits())))) + (characterTag()); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),characterObject); +} + + +/* N.B. This will answer inexact results for integers with > 53 bits of + magnitude. + */ + + /* InterpreterPrimitives>>#primitiveAsFloat */ +static void +primitiveAsFloat(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt rcvr; + char *sp; + + rcvr = longAt(GIV(stackPointer)); + assert((((rcvr) & 7) == 1)); + + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer)),floatObjectOf(((double) ((rcvr >> 3)) ))); + GIV(stackPointer) = sp; +} + + /* InterpreterPrimitives>>#primitiveAt */ +static void +primitiveAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt result; + sqInt sp1; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + + /* begin commonAt: */ + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + index = longAt(GIV(stackPointer)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l5; + } + + /* No need to test for large positive integers here. No object has 1g elements */ + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 1) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l5; + } + index = (index >> 3); + + /* begin stObject:at: */ + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + sp1 = stackPointerIndexForFrame(frameOfMarriedContext(rcvr)); + assert((ReceiverIndex + ((sp1 >> 3))) < (lengthOf(rcvr))); + stSize = sp1; + goto l3; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l3; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(rcvr))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l3: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))))) { + result = temporaryin(index - 1, frameOfMarriedContext(rcvr)); + goto l4; + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(rcvr)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + result = (((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + (index - 1)))) << 3) | 1); + goto l4; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + result = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << (shiftForWord())))))); + goto l4; + } + if (fmt >= (firstByteFormat())) { + result = (((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + ((index + fixedFields) - 1)))) << 3) | 1); + goto l4; + } + if (fmt >= (firstShortFormat())) { + result = (((usqInt)(((unsigned short) (shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))))))) << 3) | 1); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + result = positive64BitIntegerFor(long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))))); + goto l4; + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + result = (((usqInt)((((usqInt)(long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + goto l4; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + result = 0; + /* end stObject:at: */ +l4: + if (!GIV(primFailCode)) { + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); + } + /* end commonAt: */ +l5:; +} + + /* InterpreterPrimitives>>#primitiveAtPut */ +static void +primitiveAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fieldIndex; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt signedValueToStore; + sqInt signedValueToStoreSqInt; + sqInt sp; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + usqLong unsigned64BitValueToStore; + unsigned int unsignedValueToStore; + sqInt value; + + /* begin commonAtPut: */ + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + value = longAt(GIV(stackPointer)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l11; + } + + /* No need to test for large positive integers here. No object has 1g elements */ + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 2) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l11; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l11; + } + index = (index >> 3); + + /* begin stObject:at:put: */ + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l7; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l7; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l7; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l7; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l7; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l7: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(rcvr)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(rcvr))); + stSize = sp; + goto l9; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l9; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(rcvr))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l9: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))))) { + temporaryinput(index - 1, frameOfMarriedContext(rcvr), value); + goto l10; + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(rcvr)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + if (((((value) & 7) == 1)) + && ((((usqInt)((signedValueToStore = (value >> 3))))) <= 0xFF)) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + (index - 1)),signedValueToStore); + goto l10; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l10; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l6; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l6; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l6: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:storing:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + fieldIndex = (index + fixedFields) - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, rcvr, value)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),value); + goto l8; + } + if (fmt >= (firstByteFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l8; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l8; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + ((index + fixedFields) - 1)),signedValueToStoreSqInt); + goto l8; + } + if (fmt >= (firstShortFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l8; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l8; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))),signedValueToStoreSqInt); + goto l8; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l8; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l8: + goto l10; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + /* end stObject:at:put: */ +l10: + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + } + /* end commonAtPut: */ +l11:; +} + + +/* Set the cursor to the given shape. The Mac only supports 16x16 pixel + cursors. Cursor offsets are handled by Smalltalk. + */ + + /* InterpreterPrimitives>>#primitiveBeCursor */ +static void +primitiveBeCursor(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cursorBitsObj; + sqInt cursorObj; + sqInt depth; + sqInt extentX; + sqInt extentY; + sqInt maskBitsObj; + sqInt maskObj; + usqInt numSlots; + sqInt offsetObj; + sqInt offsetX; + sqInt offsetY; + + maskBitsObj = 0; + if ((((usqInt)GIV(argumentCount))) > 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + cursorObj = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + if (!((/* isPointers: */ + ((!(cursorObj & (tagMask())))) + && (((byteAt((void *)(cursorObj + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(cursorObj)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(cursorObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(cursorObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= 5))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + cursorBitsObj = longAt((void *)((cursorObj + BaseHeaderSize) + (0U << (shiftForWord())))); + offsetObj = longAt((void *)((cursorObj + BaseHeaderSize) + (4U << (shiftForWord())))); + if (!(((((((extentX = longAt((void *)((cursorObj + BaseHeaderSize) + (1U << (shiftForWord()))))))) & 7) == 1)) + && (((((((extentY = longAt((void *)((cursorObj + BaseHeaderSize) + (2U << (shiftForWord()))))))) & 7) == 1)) + && (((((((depth = longAt((void *)((cursorObj + BaseHeaderSize) + (3U << (shiftForWord()))))))) & 7) == 1)) + && ((/* isPointers: */ + ((!(offsetObj & (tagMask())))) + && (((byteAt((void *)(offsetObj + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(offsetObj)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(offsetObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(offsetObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= 2) + && (((((((offsetX = longAt((void *)((offsetObj + BaseHeaderSize) + (0U << (shiftForWord()))))))) & 7) == 1)) + && ((((((offsetY = longAt((void *)((offsetObj + BaseHeaderSize) + (1U << (shiftForWord()))))))) & 7) == 1))))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + offsetX = (offsetX >> 3); + offsetY = (offsetY >> 3); + extentX = (extentX >> 3); + extentY = (extentY >> 3); + depth = (depth >> 3); + if (!GIV(argumentCount)) { + if (depth == 32) { + if (!((extentX > 0) + && ((extentY > 0) + && ((((offsetX >= (-extentX)) && (offsetX <= 0))) + && ((((offsetY >= (-extentY)) && (offsetY <= 0))) + && ((/* isWords: */ + ((!(cursorBitsObj & (tagMask())))) + && (((((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1))))) + && ((lengthOfformat(cursorBitsObj, firstLongFormat())) == (extentX * extentY)))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* This should pass (objectMemory firstIndexableField: cursorBitsObj type: #'unsigned int *'), but the platform sources are venerable and expect sqInts */ + ioSetCursorARGB(((sqInt) (firstIndexableField(cursorBitsObj))), extentX, extentY, offsetX, offsetY); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return; + } + + /* Support arbitrary-sized 32 bit ARGB forms --bf 3/1/2007 23:51 */ + if (!((extentX == 16) + && ((extentY == 16) + && ((((offsetX >= -16) && (offsetX <= 0))) + && ((((offsetY >= -16) && (offsetY <= 0))) + && ((/* isWords: */ + ((!(cursorBitsObj & (tagMask())))) + && (((((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1))))) + && ((lengthOfformat(cursorBitsObj, firstLongFormat())) == 16))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* This should pass (objectMemory firstIndexableField: cursorBitsObj type: #'unsigned int *'), but the platform sources are venerable and expect sqInts */ + ioSetCursor(((sqInt) (firstIndexableField(cursorBitsObj))), offsetX, offsetY); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return; + } + if (!((extentX == 16) + && ((extentY == 16) + && ((((offsetX >= -16) && (offsetX <= 0))) + && ((((offsetY >= -16) && (offsetY <= 0))) + && ((/* isWords: */ + ((!(cursorBitsObj & (tagMask())))) + && (((((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1))))) + && ((lengthOfformat(cursorBitsObj, firstLongFormat())) == 16))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + maskObj = longAt(GIV(stackPointer)); + if (!((/* isPointers: */ + ((!(maskObj & (tagMask())))) + && (((byteAt((void *)(maskObj + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(maskObj)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(maskObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(maskObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= 5) + && (((longAt((void *)((maskObj + BaseHeaderSize) + (1U << (shiftForWord()))))) == ((((usqInt)16 << 3) | 1))) + && (((longAt((void *)((maskObj + BaseHeaderSize) + (2U << (shiftForWord()))))) == ((((usqInt)16 << 3) | 1))) + && (((longAt((void *)((maskObj + BaseHeaderSize) + (3U << (shiftForWord()))))) == ((((usqInt)1 << 3) | 1))) + && ((isWords((maskBitsObj = longAt((void *)((maskObj + BaseHeaderSize) + (0U << (shiftForWord()))))))) + && ((lengthOfformat(maskBitsObj, firstLongFormat())) == 16)))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* This should pass (objectMemory firstIndexableField: cursorBitsObj type: #'unsigned int *'), but the platform sources are venerable and expect sqInts */ + ioSetCursorWithMask(((sqInt) (firstIndexableField(cursorBitsObj))), ((sqInt) (firstIndexableField(maskBitsObj))), offsetX, offsetY); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* Record the system Display object in the specialObjectsTable, + and if possible pin the display bitmap. Further, invoke ioBeDisplay + to alow the VM to record the location, width heigth & depth of the bitmap. */ + + /* InterpreterPrimitives>>#primitiveBeDisplay */ +static void +primitiveBeDisplay(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bitsOop; + void *bitsOrHandle; + sqInt depthOop; + sqInt heightOop; + sqInt pinnedBits; + sqInt rcvr; + sqInt widthOop; + + rcvr = longAt(GIV(stackPointer)); + if (!((/* isPointers: */ + ((!(rcvr & (tagMask())))) + && (((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && (((lengthOf(rcvr)) >= 4) + && (((bitsOop = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord()))))), + ((/* isWordsOrBytes: */ + ((!(bitsOop & (tagMask())))) + && (isWordsOrBytesNonImm(bitsOop))) + || ((((bitsOop) & 7) == 1))) + && (((((((widthOop = longAt((void *)((rcvr + BaseHeaderSize) + (1U << (shiftForWord()))))))) & 7) == 1)) + && (((((((heightOop = longAt((void *)((rcvr + BaseHeaderSize) + (2U << (shiftForWord()))))))) & 7) == 1)) + && ((((((depthOop = longAt((void *)((rcvr + BaseHeaderSize) + (3U << (shiftForWord()))))))) & 7) == 1))))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheDisplay, GIV(specialObjectsOop), rcvr)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(rcvr & (tagMask())))) + && (oopisLessThan(rcvr, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheDisplay) << (shiftForWord()))))),rcvr); + if (((!(bitsOop & (tagMask())))) + && (!(((byteAt((void *)(bitsOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0))) { + /* Answers 0 if memory required to pin but not enough memory available. */ + pinnedBits = pinObject(bitsOop); + if (pinnedBits) { + bitsOop = pinnedBits; + } + } + bitsOrHandle = ((!(bitsOop & (tagMask()))) + ? firstIndexableField(bitsOop) + : ((void *)bitsOop)); + + /* begin ioBeDisplay:width:height:depth: */ + displayBits = bitsOrHandle; + displayWidth = (widthOop >> 3); + displayHeight = (heightOop >> 3); + displayDepth = (depthOop >> 3); + ioNoteDisplayChangedwidthheightdepth(bitsOrHandle, (widthOop >> 3), (heightOop >> 3), (depthOop >> 3)); +} + + +/* make the basic beep noise */ + + /* InterpreterPrimitives>>#primitiveBeep */ +static void +primitiveBeep(void) +{ + ioBeep(); +} + + +/* For the mirror prims check that the class obj is actually a valid class. + No need to check if it's the receiver since the method exists only on + Behavior. + */ + + /* InterpreterPrimitives>>#primitiveBehaviorHash */ +static void +primitiveBehaviorHash(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt err; + int hash; + sqInt hashOrError; + sqInt shouldBeClassObj; + + shouldBeClassObj = longAt(GIV(stackPointer)); + if (GIV(argumentCount) > 0) { + if ((((shouldBeClassObj & (tagMask())) != 0)) + || (((!((longAt((void *)(shouldBeClassObj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + || (!(objCouldBeClassObj(shouldBeClassObj))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + assert((isNonImmediate(shouldBeClassObj)) + && (addressCouldBeClassObj(shouldBeClassObj))); + + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(shouldBeClassObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + hashOrError = ((hash = (long32At((void *)(shouldBeClassObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(shouldBeClassObj) + ? ((err = enterIntoClassTable(shouldBeClassObj)) + ? -err + : (long32At((void *)(shouldBeClassObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (hashOrError < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -hashOrError; + return; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)hashOrError << 3) | 1)); +} + + +/* Note no short-cut for SmallIntegers. Either the inline interpreter + bytecode or the JIT primitive will handle this case. */ + + /* InterpreterPrimitives>>#primitiveBitAnd */ +static void +primitiveBitAnd(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt integerArgumentValue; + usqInt integerReceiverValue; + sqInt oop; + sqInt value; + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArgumentValue = ((usqIntptr_t) null); + goto l1; + } + integerArgumentValue = ((usqIntptr_t) value); + goto l1; + } + + /* don't inline the rare case */ + integerArgumentValue = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin positiveMachineIntegerValueOf: */ + if ((((oop) & 7) == 1)) { + value = (oop >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerReceiverValue = ((usqIntptr_t) null); + goto l2; + } + integerReceiverValue = ((usqIntptr_t) value); + goto l2; + } + + /* don't inline the rare case */ + integerReceiverValue = positiveMachineIntegerValueOfObj(oop); + /* end positiveMachineIntegerValueOf: */ +l2: + if (!GIV(primFailCode)) { + oop = positive64BitIntegerFor(integerArgumentValue & integerReceiverValue); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oop); + } +} + + +/* Primitive logical operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveBitAndLargeIntegers */ +EXPORT(void) +primitiveBitAndLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong integerArg; + usqLong integerRcvr; + sqInt oopResult; + + integerArg = positive64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = positive64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (GIV(primFailCode)) { + return; + } + oopResult = positive64BitIntegerFor(integerRcvr & integerArg); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Note no short-cut for SmallIntegers. Either the inline interpreter + bytecode or the JIT primitive will handle this case. */ + + /* InterpreterPrimitives>>#primitiveBitOr */ +static void +primitiveBitOr(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt integerArgumentValue; + usqInt integerReceiverValue; + sqInt oop; + sqInt value; + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArgumentValue = ((usqIntptr_t) null); + goto l1; + } + integerArgumentValue = ((usqIntptr_t) value); + goto l1; + } + + /* don't inline the rare case */ + integerArgumentValue = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin positiveMachineIntegerValueOf: */ + if ((((oop) & 7) == 1)) { + value = (oop >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerReceiverValue = ((usqIntptr_t) null); + goto l2; + } + integerReceiverValue = ((usqIntptr_t) value); + goto l2; + } + + /* don't inline the rare case */ + integerReceiverValue = positiveMachineIntegerValueOfObj(oop); + /* end positiveMachineIntegerValueOf: */ +l2: + if (!GIV(primFailCode)) { + oop = positive64BitIntegerFor(integerArgumentValue | integerReceiverValue); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oop); + } +} + + +/* Primitive logical operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveBitOrLargeIntegers */ +EXPORT(void) +primitiveBitOrLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong integerArg; + usqLong integerRcvr; + sqInt oopResult; + + integerArg = positive64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = positive64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (GIV(primFailCode)) { + return; + } + oopResult = positive64BitIntegerFor(integerRcvr | integerArg); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Perform a bitShift. In 32-bits deal only with non-negative 32-bit + integers. In 64-bits deal with signed 64-bit quantities (max (2^63)-1). */ + + /* InterpreterPrimitives>>#primitiveBitShift */ +static void +primitiveBitShift(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + sqInt shifted; + + integerArgument = longAt(GIV(stackPointer)); + if (!((((integerArgument) & 7) == 1))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + integerReceiver = signed64BitValueOf(integerReceiver); + if (!GIV(primFailCode)) { + if (((integerArgument = (integerArgument >> 3))) >= 0) { + if (!(integerArgument <= 61 /* numSmallIntegerBits */)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + shifted = ((sqInt)((usqInt)(integerReceiver) << integerArgument)); + if (!(integerReceiver == ((shifted) >> integerArgument))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + } + else { + if (!(integerArgument >= (-61 /* numSmallIntegerBits */))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + shifted = (integerReceiver) >> (0 - integerArgument); + } + + /* Left shift -- must fail bits would be lost + Right shift -- OK to lose bits */ + shifted = ((((((usqInt)(shifted)) >> 60) + 1) & 15) <= 1 + ? (((usqInt)shifted << 3) | 1) + : signed64BitIntegerFor(shifted)); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),shifted); + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveBitShiftLargeIntegers */ +EXPORT(void) +primitiveBitShiftLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + sqInt integerPointer; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + sqInt shift; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + shift = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + shift = 0; + } + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + a = magnitude64BitValueOf(oopRcvr); + if (GIV(primFailCode)) { + return; + } + if (shift >= 0) { + /* Protect against overflow */ + + /* This is to avoid undue (usqInt) cast */ + result = 0xFFFFFFFFFFFFFFFFULL; + if ((shift >= 64) + || (a > ((result) >> shift))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + result = (a << shift); + } + else { + shift = 0 - shift; + if (shift >= 64) { + result = 0; + } + else { + result = (a) >> shift; + } + + /* Fake 2 complement for negative values */ + if (aIsNegative + && (((result << shift)) != a)) { + result += 1; + } + } + oopResult = magnitude64BitIntegerForneg(result, aIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + /* InterpreterPrimitives>>#primitiveBitXor */ +static void +primitiveBitXor(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + usqInt integerArgumentValue; + sqInt integerReceiver; + usqInt integerReceiverValue; + sqInt oop; + sqInt value; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((((integerArgument) & 7) == 1)) + && ((((integerReceiver) & 7) == 1))) { + oop = (integerArgument ^ integerReceiver) + (smallIntegerTag()); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oop); + } + else { + /* begin positiveMachineIntegerValueOf: */ + if ((((integerArgument) & 7) == 1)) { + value = (integerArgument >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArgumentValue = ((usqIntptr_t) null); + goto l1; + } + integerArgumentValue = ((usqIntptr_t) value); + goto l1; + } + + /* don't inline the rare case */ + integerArgumentValue = positiveMachineIntegerValueOfObj(integerArgument); + /* end positiveMachineIntegerValueOf: */ +l1: + + /* begin positiveMachineIntegerValueOf: */ + if ((((integerReceiver) & 7) == 1)) { + value = (integerReceiver >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerReceiverValue = ((usqIntptr_t) null); + goto l2; + } + integerReceiverValue = ((usqIntptr_t) value); + goto l2; + } + + /* don't inline the rare case */ + integerReceiverValue = positiveMachineIntegerValueOfObj(integerReceiver); + /* end positiveMachineIntegerValueOf: */ +l2: + if (!GIV(primFailCode)) { + oop = positive64BitIntegerFor(integerArgumentValue ^ integerReceiverValue); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oop); + } + } +} + + +/* Primitive logical operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveBitXorLargeIntegers */ +EXPORT(void) +primitiveBitXorLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong integerArg; + usqLong integerRcvr; + sqInt oopResult; + + integerArg = positive64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = positive64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (GIV(primFailCode)) { + return; + } + oopResult = positive64BitIntegerFor(integerRcvr ^ integerArg); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Answer the encoder names for the supported bytecode sets. */ + + /* InterpreterPrimitives>>#primitiveBytecodeSetsAvailable */ +EXPORT(sqInt) +primitiveBytecodeSetsAvailable(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt encoderNames; + usqInt newObj; + usqInt numBytes; + sqInt valuePointer; + + if (GIV(argumentCount) > 0) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((knownClassAtIndex(ClassArrayCompactIndex)) != GIV(nilObj)); + assert((arrayFormat()) == (instSpecOfClass(knownClassAtIndex(ClassArrayCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (2 * BytesPerOop); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + encoderNames = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) 2)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + GIV(freeStart) += numBytes; + encoderNames = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + valuePointer = stringForCString("EncoderForV3PlusClosures"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(encoderNames)) + && (!(isForwarded(encoderNames)))); + assert(validStorePointerUncheckedArgs(0, encoderNames, valuePointer)); + longAtput((void *)((encoderNames + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("EncoderForSistaV1"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(encoderNames)) + && (!(isForwarded(encoderNames)))); + assert(validStorePointerUncheckedArgs(1, encoderNames, valuePointer)); + longAtput((void *)((encoderNames + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),encoderNames); + return 0; +} + + +/* Answer bytes available at this moment. For more meaningful + results, calls to this primitive should be precedeed by a full + or incremental garbage collection. */ + + /* InterpreterPrimitives>>#primitiveBytesLeft */ +static void +primitiveBytesLeft(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aBool; + sqInt chunk; + sqInt freeChunk; + sqInt i; + sqInt integerVal; + + if (!GIV(argumentCount)) { + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),((GIV(totalFreeOldSpace) << 3) | 1)); + return; + } + + /* old behavior - just return the size of free memory */ + if (GIV(argumentCount) == 1) { + /* Spur behavior; if argument is nil answer size of largest free chunk in oldSpace. */ + if ((longAt(GIV(stackPointer))) == GIV(nilObj)) { + /* begin sizeOfLargestFreeChunk */ + freeChunk = findLargestFreeChunk(); + if (!freeChunk) { + for (i = 0x3F; i >= 1; i += -1) { + if ((chunk = GIV(freeLists)[i])) { + integerVal = bytesInBody(chunk); + goto l1; + } + } + integerVal = 0; + goto l1; + } + integerVal = bytesInBody(freeChunk); + /* end sizeOfLargestFreeChunk */ +l1: + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerVal << 3) | 1)); + return; + } + + /* new behaviour -including or excluding swap space depending on aBool */ + + /* begin booleanValueOf: */ + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + aBool = 1; + goto l2; + } + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + aBool = 0; + goto l2; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + aBool = null; + /* end booleanValueOf: */ +l2: + if (!GIV(primFailCode)) { + integerVal = ((((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes())) < 0) ? 0 : ((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes()))); + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerVal << 3) | 1)); + return; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; +} + + +/* Perform a function call to a foreign function. + Only invoked from method containing explicit external call spec. + Due to this we use the pluggable prim mechanism explicitly here + (the first literal of any FFI spec'ed method is an ExternalFunction + and not an array as used in the pluggable primitive mechanism). */ +/* Manually copied from primitiveCalloutAccessorDepth in the + ThreadedFFIPlugins... + */ +/* since call may invoke a callback */ +/* since call signature is in first literal... */ + + /* InterpreterPrimitives>>#primitiveCalloutToFFI */ +static void +primitiveCalloutToFFI(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*primitiveCallout)(void); + + /* begin functionForPrimitiveCallout */ + if ((((sqInt)primitiveCalloutPointer)) == -1) { + reloadPrimitiveCalloutPointer(); + } + primitiveCallout = ((void (*)(void)) primitiveCalloutPointer); + if (primitiveCallout) { + dispatchFunctionPointer(primitiveCallout); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive. Change the class of the receiver into the class of the argument + given that + the format of the receiver matches the format of the argument's class. + Fail if the + receiver or argument are SmallIntegers, or the receiver is an instance of + a compact + class and the argument isn't, or when the argument's class is compact and + the receiver + isn't, or when the format of the receiver is different from the format of + the argument's + class, or when the arguments class is fixed and the receiver's size + differs from the size + that an instance of the argument's class should have. */ + + /* InterpreterPrimitives>>#primitiveChangeClass */ +static void +primitiveChangeClass(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt argClass; + sqInt err; + sqInt oop; + sqInt rcvr; + + /* begin stackObjectValue: */ + oop = longAt(GIV(stackPointer)); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = null; + goto l1; + } + arg = oop; + /* end stackObjectValue: */ +l1: + + /* begin stackObjectValue: */ + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = null; + goto l2; + } + rcvr = oop; + /* end stackObjectValue: */ +l2: + if (GIV(primFailCode)) { + return; + } + argClass = fetchClassOfNonImm(arg); + err = changeClassOfto(rcvr, argClass); + if (err) { + /* primitiveFailFor: */ + GIV(primFailCode) = err; + } + else { + /* Flush at cache because rcvr's class has changed. */ + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + return; +} + + /* InterpreterPrimitives>>#primitiveClass */ +static void +primitiveClass(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt instance; + sqInt oop; + sqInt tagBits; + + instance = longAt(GIV(stackPointer)); + if ((GIV(argumentCount) > 0) + && (/* isOopForwarded: */ + ((!(instance & (tagMask())))) + && ((!((longAt((void *)(instance))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + else { + oop = /* fetchClassOf: */ + ((tagBits = instance & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(instance)); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + } +} + + +/* Primitive. Void the VM profile histograms. */ + + /* InterpreterPrimitives>>#primitiveClearVMProfile */ +static void +primitiveClearVMProfile(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + ioClearProfile(); + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* When called with a single string argument, post the string to + the clipboard. When called with zero arguments, return a + string containing the current clipboard contents. */ + + /* InterpreterPrimitives>>#primitiveClipboardText */ +static void +primitiveClipboardText(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + usqInt numSlots; + sqInt s; + sqInt sz; + + if (GIV(argumentCount) == 1) { + s = longAt(GIV(stackPointer)); + if (!(/* isBytes: */ + ((!(s & (tagMask())))) + && (((byteAt((void *)(s + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + if (!GIV(primFailCode)) { + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(s + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + sz = ((((/* begin numSlotsOf: */ + assert((classIndexOf(s)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(s + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(s - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + clipboardWriteFromAt(sz, s + BaseHeaderSize, 0); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + } + else { + sz = clipboardSize(); + + /* begin allocateBytes:classIndex: */ + assert(addressCouldBeClassObj(classAtIndex(ClassByteStringCompactIndex))); + assert((instSpecOfClass(classAtIndex(ClassByteStringCompactIndex))) == (firstByteFormat())); + s = noInlineAllocateSlotsformatclassIndex(((sz + BytesPerOop) - 1) / BytesPerOop, byteFormatForNumBytes(sz), ClassByteStringCompactIndex); + if (!s) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + clipboardReadIntoAt(sz, s + BaseHeaderSize, 0); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),s); + } +} + + +/* Take a boolean which if true turns or keeps clock logging on. Answer an + array supplying + the size of the clock logs, the address of the usecs log, the index in it, + the address of the + msecs log, and the index into it. */ + + /* InterpreterPrimitives>>#primitiveClockLogAddresses */ +EXPORT(sqInt) +primitiveClockLogAddresses(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt midx; + void *msecs; + sqInt result; + sqInt runInNOut; + sqInt uidx; + void *usecs; + sqInt v1; + sqInt v2; + + + /* bypass type inference which would deduce int */ + midx = 0; + msecs = ((void *) 0); + uidx = 0; + usecs = ((void *) 0); + if (GIV(argumentCount) != 1) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + runInNOut = (longAt(GIV(stackPointer))) == GIV(trueObj); + ioGetClockLogSizeUsecsIdxMsecsIdx((&runInNOut), (&usecs), (&uidx), (&msecs), (&midx)); + result = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 5); + if (!result) { + return (GIV(primFailCode) = PrimErrNoMemory); + } + +#if SPURVM + v1 = positive64BitIntegerFor(((usqInt)usecs)); + v2 = positive64BitIntegerFor(((usqInt)msecs)); +#else /* SPURVM */ + pushRemappableOop(result); + v1 = positive64BitIntegerFor(((usqInt)usecs)); + pushRemappableOop(v1); + v2 = positive64BitIntegerFor(((usqInt)msecs)); + v1 = popRemappableOop(); + result = popRemappableOop(); +#endif /* SPURVM */ + if (GIV(primFailCode)) { + return (GIV(primFailCode) = PrimErrNoMemory); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(result)) + && (!(isForwarded(result)))); + assert(validStorePointerUncheckedArgs(0, result, (((usqInt)runInNOut << 3) | 1))); + longAtput((void *)((result + BaseHeaderSize) + (0U << (shiftForWord()))),(((usqInt)runInNOut << 3) | 1)); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(1, result, v1)); + assert(isNonImmediate(result)); + if (oopisGreaterThanOrEqualTo(result, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(v1 & (tagMask())))) + && (oopisLessThan(v1, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(result + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(result); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((result + BaseHeaderSize) + (1U << (shiftForWord()))),v1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(result)) + && (!(isForwarded(result)))); + assert(validStorePointerUncheckedArgs(2, result, (((usqInt)uidx << 3) | 1))); + longAtput((void *)((result + BaseHeaderSize) + (2U << (shiftForWord()))),(((usqInt)uidx << 3) | 1)); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(3, result, v2)); + assert(isNonImmediate(result)); + if (oopisGreaterThanOrEqualTo(result, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(v2 & (tagMask())))) + && (oopisLessThan(v2, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(result + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(result); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((result + BaseHeaderSize) + (3U << (shiftForWord()))),v2); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(result)) + && (!(isForwarded(result)))); + assert(validStorePointerUncheckedArgs(4, result, (((usqInt)midx << 3) | 1))); + longAtput((void *)((result + BaseHeaderSize) + (4U << (shiftForWord()))),(((usqInt)midx << 3) | 1)); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); + return 0; +} + + /* InterpreterPrimitives>>#primitiveClosureValue */ +static void +primitiveClosureValue(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt blockClosure; + sqInt closureIP; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt i; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt outerContext; + sqInt switched; + + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(GIV(argumentCount) == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Somewhat paranoiac checks we need while debugging that we may be able to discard + in a robust system. */ + outerContext = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + if (!(/* isContext: */ + ((!(outerContext & (tagMask())))) + && (((longAt((void *)(outerContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* Check if the closure's method is actually a CompiledMethod. */ + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin activateNewClosure:outer:method:numArgs:mayContextSwitch: */ + assert(isContext(outerContext)); + assert(isVanillaBlockClosure(blockClosure)); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - ClosureFirstCopiedValueIndex; + assert(closureMethod == (fetchPointerofObject(MethodIndex, outerContext))); + assert(isOopCompiledMethod(closureMethod)); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + ((cogMethod->blockEntryOffset)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogselector(closureMethod, GIV(nilObj)); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + ((cogMethod->blockEntryOffset)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(ReceiverIndex, outerContext, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + ClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* The initial instructions in the block nil-out remaining temps. + the instruction pointer is a pointer variable equal to + method oop + ip + BaseHeaderSize + -1 for 0-based addressing of fetchByte + -1 because it gets incremented BEFORE fetching currentByte */ + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + closureIP = (oop >> 3); + GIV(instructionPointer) = ((closureMethod + closureIP) + BaseHeaderSize) - 2; + + /* begin setMethod:methodHeader: */ + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(1); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewClosure:outer:method:numArgs:mayContextSwitch: */ +l1:; +} + + +/* An exact clone of primitiveClosureValue except that this version will not + check for interrupts on stack overflow. It may invoke the garbage + collector but will not switch processes. See + checkForInterruptsMayContextSwitch: */ + + /* InterpreterPrimitives>>#primitiveClosureValueNoContextSwitch */ +void +primitiveClosureValueNoContextSwitch(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt blockClosure; + sqInt closureIP; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt i; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt outerContext; + sqInt switched; + + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(GIV(argumentCount) == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Somewhat paranoiac checks we need while debugging that we may be able to discard + in a robust system. */ + outerContext = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + if (!(/* isContext: */ + ((!(outerContext & (tagMask())))) + && (((longAt((void *)(outerContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* Check if the closure's method is actually a CompiledMethod. */ + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + GIV(deferProfileCheckForVNCS) = 1; + + /* begin activateNewClosure:outer:method:numArgs:mayContextSwitch: */ + assert(isContext(outerContext)); + assert(isVanillaBlockClosure(blockClosure)); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - ClosureFirstCopiedValueIndex; + assert(closureMethod == (fetchPointerofObject(MethodIndex, outerContext))); + assert(isOopCompiledMethod(closureMethod)); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (((cogMethod->blockEntryOffset)) - (noContextSwitchBlockEntryOffset())); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogselector(closureMethod, GIV(nilObj)); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (((cogMethod->blockEntryOffset)) - (noContextSwitchBlockEntryOffset())); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(ReceiverIndex, outerContext, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + ClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* The initial instructions in the block nil-out remaining temps. + the instruction pointer is a pointer variable equal to + method oop + ip + BaseHeaderSize + -1 for 0-based addressing of fetchByte + -1 because it gets incremented BEFORE fetching currentByte */ + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + closureIP = (oop >> 3); + GIV(instructionPointer) = ((closureMethod + closureIP) + BaseHeaderSize) - 2; + + /* begin setMethod:methodHeader: */ + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(0); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewClosure:outer:method:numArgs:mayContextSwitch: */ +l1:; +} + + /* InterpreterPrimitives>>#primitiveClosureValueWithArgs */ +static void +primitiveClosureValueWithArgs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt argumentArray; + usqInt arraySize; + sqInt blockClosure; + sqInt closureIP; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt i; + sqInt index; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt outerContext; + sqInt switched; + sqInt top; + + argumentArray = longAt(GIV(stackPointer)); + if (!(/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Check for enough space in thisContext to push all args */ + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (!(arraySize <= (LargeContextSlots - CtxtTempFrameStart))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(arraySize == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Somewhat paranoiac checks we need while debugging that we may be able to discard + in a robust system. */ + outerContext = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + if (!(/* isContext: */ + ((!(outerContext & (tagMask())))) + && (((longAt((void *)(outerContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* Check if the closure's method is actually a CompiledMethod. */ + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* Copy the arguments to the stack, and activate */ + index = 1; + while (index <= numArgs) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + index += 1; + } + + /* begin activateNewClosure:outer:method:numArgs:mayContextSwitch: */ + assert(isContext(outerContext)); + assert(isVanillaBlockClosure(blockClosure)); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - ClosureFirstCopiedValueIndex; + assert(closureMethod == (fetchPointerofObject(MethodIndex, outerContext))); + assert(isOopCompiledMethod(closureMethod)); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + ((cogMethod->blockEntryOffset)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogselector(closureMethod, GIV(nilObj)); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + ((cogMethod->blockEntryOffset)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(ReceiverIndex, outerContext, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + ClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* The initial instructions in the block nil-out remaining temps. + the instruction pointer is a pointer variable equal to + method oop + ip + BaseHeaderSize + -1 for 0-based addressing of fetchByte + -1 because it gets incremented BEFORE fetching currentByte */ + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + closureIP = (oop >> 3); + GIV(instructionPointer) = ((closureMethod + closureIP) + BaseHeaderSize) - 2; + + /* begin setMethod:methodHeader: */ + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(1); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewClosure:outer:method:numArgs:mayContextSwitch: */ +l1:; +} + + +/* Return the value of the microsecond clock in the local timezone, as + updated by the heartbeat, as an integer. + This is the number of microseconds since the Smalltalk epoch, 1901/1/1 + 12:00am. The microsecond clock is at least 60 bits wide which means it'll + get to around August + 38435 before it wraps around. Be sure to put it on your calendar. The + coarse clock is + updated by the heartbeat thread and as such is much cheaper than + primitiveUTCMicrosecondClock, which always entails a system call. */ + + /* InterpreterPrimitives>>#primitiveCoarseLocalMicrosecondClock */ +static void +primitiveCoarseLocalMicrosecondClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = positive64BitIntegerFor(ioLocalMicroseconds()); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Return the value of the microsecond clock as updated by the heartbeat as + an integer. + This is the number of microseconds since the Smalltalk epoch, 1901/1/1 + 12:00am. The microsecond clock is at least 60 bits wide which means it'll + get to around August + 38435 before it wraps around. Be sure to put it on your calendar. The + coarse clock is + updated by the heartbeat thread and as such is much cheaper than + primitiveUTCMicrosecondClock, which always entails a system call. */ + + /* InterpreterPrimitives>>#primitiveCoarseUTCMicrosecondClock */ +static void +primitiveCoarseUTCMicrosecondClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = positive64BitIntegerFor(ioUTCMicroseconds()); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Primitive. Compare two byte-indexed objects for equality */ + + /* InterpreterPrimitives>>#primitiveCompareBytes */ +EXPORT(sqInt) +primitiveCompareBytes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg1; + sqInt arg2; + sqInt fmt; + sqInt i; + sqInt len; + usqInt numSlots; + + arg1 = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + arg2 = longAt(GIV(stackPointer)); + + /* Quick identity test */ + if (arg1 == arg2) { + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(trueObj)); + return 0; + } + if (!((/* isBytes: */ + ((!(arg1 & (tagMask())))) + && (((byteAt((void *)(arg1 + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && (/* isBytes: */ + ((!(arg2 & (tagMask())))) + && (((byteAt((void *)(arg2 + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(arg1 + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + len = ((((/* begin numSlotsOf: */ + assert((classIndexOf(arg1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(arg1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(arg1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (!(len == (numBytesOfBytes(arg2)))) { + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(falseObj)); + return 0; + } + for (i = 0; i < len; i += 1) { + if (!((byteAt((void *)((arg1 + BaseHeaderSize) + i))) == (byteAt((void *)((arg2 + BaseHeaderSize) + i))))) { + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(falseObj)); + return 0; + } + } + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(trueObj)); + return 0; +} + + +/* primitiveCompareWith: string2 [collated: order] */ +/* 1 - fetch the parameters from the stack */ + + /* InterpreterPrimitives>>#primitiveCompareWith */ +EXPORT(void) +primitiveCompareWith(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt c1; + sqInt c2; + sqInt fmt; + sqInt i; + sqInt min; + usqInt numSlots; + sqInt order; + sqInt result; + sqInt string1; + sqInt string2; + sqInt strLength1; + sqInt strLength2; + + order = 0; + if (!(GIV(argumentCount) == 1)) { + if (GIV(argumentCount) != 2) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + order = longAt(GIV(stackPointer)); + if (!((/* isBytes: */ + ((!(order & (tagMask())))) + && (((byteAt((void *)(order + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((numBytesOfBytes(order)) == 0x100))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + string1 = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* 2 - check their types - all parameters are ByteObject */ + string2 = longAt(GIV(stackPointer) + ((GIV(argumentCount) - 1) * BytesPerWord)); + if (!((/* isBytes: */ + ((!(string1 & (tagMask())))) + && (((byteAt((void *)(string1 + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && (/* isBytes: */ + ((!(string2 & (tagMask())))) + && (((byteAt((void *)(string2 + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* 3 - compare the strings */ + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(string1 + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + strLength1 = ((((/* begin numSlotsOf: */ + assert((classIndexOf(string1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(string1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(string1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(string2 + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + strLength2 = ((((/* begin numSlotsOf: */ + assert((classIndexOf(string2)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(string2 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(string2 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (order) { + /* begin rawCompare:length:with:length:accessBlock: */ + min = ((strLength1 < strLength2) ? strLength1 : strLength2); + for (i = 0; i < min; i += 1) { + c1 = byteAt((void *)((order + BaseHeaderSize) + ((byteAt((void *)((string1 + BaseHeaderSize) + i))) + 1))); + c2 = byteAt((void *)((order + BaseHeaderSize) + ((byteAt((void *)((string2 + BaseHeaderSize) + i))) + 1))); + if (!(c1 == c2)) { + result = c1 - c2; + goto l2; + } + } + result = strLength1 - strLength2; + /* end rawCompare:length:with:length:accessBlock: */ +l2:; + } + else { + /* begin rawCompare:length:with:length:accessBlock: */ + min = ((strLength1 < strLength2) ? strLength1 : strLength2); + for (i = 0; i < min; i += 1) { + c1 = byteAt((void *)((string1 + BaseHeaderSize) + i)); + c2 = byteAt((void *)((string2 + BaseHeaderSize) + i)); + if (!(c1 == c2)) { + result = c1 - c2; + goto l1; + } + } + result = strLength1 - strLength2; + /* end rawCompare:length:with:length:accessBlock: */ +l1:; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)result << 3) | 1)); +} + + +/* Fill the receiver, which must be an indexable non-pointer + object, with the given integer value. */ + + /* InterpreterPrimitives>>#primitiveConstantFill */ +static void +primitiveConstantFill(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt end; + usqLong fillValue; + sqInt format; + usqInt i; + sqInt oddBytes; + sqInt rcvr; + + /* begin primitiveConstantFillSpur */ + if (GIV(argumentCount) != 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + goto l1; + } + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(((!(rcvr & (tagMask())))) + && (((format = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()))) >= (sixtyFourBitIndexableFormat())))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l1; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l1; + } + fillValue = positive64BitValueOf(longAt(GIV(stackPointer))); + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + if (format >= (firstShortFormat())) { + if (format >= (firstByteFormat())) { + if ((fillValue > 0xFF) + || (format >= (firstCompiledMethodFormat()))) { + /* primitiveFailFor: */ + GIV(primFailCode) = (fillValue > 0xFF + ? PrimErrBadArgument + : PrimErrBadReceiver); + goto l1; + } + fillValue = ((fillValue + ((fillValue << 8))) + ((fillValue << 16))) + ((fillValue << 24)); + oddBytes = format & 7; + } + else { + if (fillValue > 0xFFFF) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + fillValue += (fillValue << 16); + oddBytes = ((format & 3) << 1); + } + fillValue += (fillValue << 32); + } + else { + if (format == (sixtyFourBitIndexableFormat())) { + oddBytes = 0; + } + else { + if (fillValue > 0xFFFFFFFFU) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + fillValue += (fillValue << 32); + oddBytes = ((format & 1) << 2); + } + } + end = addressAfter(rcvr); + i = rcvr + BaseHeaderSize; + while (i < end) { + long64Atput((void *)(i),fillValue); + i += 8; + } + + /* now ensure trailing bytes are zero */ + if (oddBytes > 0) { + fillValue = (fillValue) >> (8 * oddBytes); + long64Atput((void *)(i - 8),fillValue); + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + /* end primitiveConstantFillSpur */ +l1:; +} + + +/* Primitive. Start or stop the VM profiler. The first argument is a boolean + to switch profiling on or off. The second argument is an integer or nil. + If an integer it determines the maximum number of samples in the VM's + sample buffer. Answer the current number of samples in the buffer. */ + + /* InterpreterPrimitives>>#primitiveControlVMProfiling */ +static void +primitiveControlVMProfiling(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bufferSize; + sqInt numSamples; + sqInt onOffBar; + + if (GIV(argumentCount) != 2) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + if (((onOffBar = longAt(GIV(stackPointer) + (1 * BytesPerWord)))) == GIV(trueObj)) { + onOffBar = 1; + } + else { + if (onOffBar == GIV(falseObj)) { + onOffBar = 0; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + } + if (((bufferSize = longAt(GIV(stackPointer)))) == GIV(nilObj)) { + bufferSize = 0; + } + else { + if (!(((((bufferSize) & 7) == 1)) + && (((bufferSize = (bufferSize >> 3))) > 0))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + } + numSamples = ioControlNewProfile(onOffBar,bufferSize); + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),(((usqInt)numSamples << 3) | 1)); +} + + +/* Primitive. Copy the state of the receiver from the argument. + Fail if receiver and argument are of a different class. + Fail if the receiver or argument are contexts (because of context-to-stack + mapping). Fail if receiver and argument have different lengths (for + indexable objects). + Fail if the objects are not in a fit state to be copied (e.g. married + contexts and Cogged methods) */ + + /* InterpreterPrimitives>>#primitiveCopyObject */ +static void +primitiveCopyObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt fmt; + sqInt i; + usqInt length; + usqInt numBytes; + usqInt numSlots; + sqInt rcvr; + sqInt valuePointer; + + if (!(GIV(argumentCount) >= 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + arg = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if (((arg & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (((longAt((void *)(rcvr))) & (classIndexMask())) != ((longAt((void *)(arg))) & (classIndexMask()))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (isWordsOrBytesNonImm(rcvr)) { + /* begin numBytesOf: */ + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + length = numBytes - (fmt & 7); + goto l1; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + length = ((sqInt) numBytes); + goto l1; + } + if (fmt >= (firstShortFormat())) { + length = numBytes - (((fmt & 3) << 1)); + goto l1; + } + + /* fmt >= self firstLongFormat */ + length = numBytes - (((fmt & 1) << 2)); + /* end numBytesOf: */ +l1: + if (!((((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask())) == ((byteAt((void *)(arg + (formatFieldByteOffset())))) & (formatMask()))) + && (length == (numBytesOf(arg))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + memcpy(((void *)(rcvr + BaseHeaderSize)), ((void *)(arg + BaseHeaderSize)), length); + } + else { + if (!(isAppropriateForCopyObject(rcvr))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (!((isAppropriateForCopyObject(arg)) + && (length == (lengthOf(arg))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + for (i = 0; i < length; i += 1) { + valuePointer = longAt((void *)((arg + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(i, rcvr, valuePointer)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + } + + /* Note: The above could be faster for young receivers but I don't think it'll matter */ + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* Computes cosine of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveCosine */ +static void +primitiveCosine(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(cos(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + +/* Crash the VM by indirecting through a null pointer. If the sole argument + is true crash in this thread, and if it is false crash in a new thread. If + the argument is an integer use the method that implies. + bit 0 = thread to crash in; 1 => this thread + bit 1 = crash method; 0 => indirect through null pointer; 1 => call exit */ + + /* InterpreterPrimitives>>#primitiveCrashVM */ +EXPORT(sqInt) +primitiveCrashVM(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt crashInThisThread; + + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + crashInThisThread = ((longAt(GIV(stackPointer))) >> 3); + } + else { + /* begin booleanValueOf: */ + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + crashInThisThread = 1; + goto l1; + } + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + crashInThisThread = 0; + goto l1; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + crashInThisThread = null; + /* end booleanValueOf: */ +l1:; + } + if (GIV(primFailCode) + || (GIV(argumentCount) != 1)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + crashInThisOrAnotherThread(crashInThisThread); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + return 0; +} + + +/* Pass in a non-negative value to disable the architectures powermanager if + any, zero to enable. This is a named (not numbered) primitive in the null + module (ie the VM) + */ + + /* InterpreterPrimitives>>#primitiveDisablePowerManager */ +EXPORT(sqInt) +primitiveDisablePowerManager(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integer; + sqInt integerPointer; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + integer = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integer = 0; + } + if (!GIV(primFailCode)) { + ioDisablePowerManager(integer); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + return 0; +} + + /* InterpreterPrimitives>>#primitiveDiv */ +static void +primitiveDiv(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt quotient; + + quotient = doPrimitiveDivby(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)(quotient)) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)quotient << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } +} + + /* InterpreterPrimitives>>#primitiveDivide */ +static void +primitiveDivide(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerPointer; + sqInt integerReceiver; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + integerReceiver = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerReceiver = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + integerArgument = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArgument = 0; + } + if ((integerArgument != 0) + && ((integerReceiver % integerArgument) == 0)) { + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)((integerReceiver / integerArgument))) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)(integerReceiver / integerArgument) << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveDivideLargeIntegers */ +EXPORT(void) +primitiveDivideLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (GIV(primFailCode)) { + return; + } + + /* check for exact division */ + if (!((b != 0) + && ((a % b) == 0))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + result = a / b; + oopResult = magnitude64BitIntegerForneg(result, aIsNegative != bIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveDivLargeIntegers */ +EXPORT(void) +primitiveDivLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong rem; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (!b) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return; + } + result = a / b; + if (a) { + if (!(bIsNegative == aIsNegative)) { + /* Round toward negative infinity */ + rem = a % b; + if (rem) { + /* This can not overflow, because b > 1, otherwise rem = 0 */ + result += 1; + } + } + } + oopResult = magnitude64BitIntegerForneg(result, bIsNegative != aIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + /* InterpreterPrimitives>>#primitiveEqual */ +static void +primitiveEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + int result; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver == integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + result = (signed64BitValueOf(integerReceiver)) == (signed64BitValueOf(integerArgument)); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (result + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveEqualLargeIntegers */ +EXPORT(void) +primitiveEqualLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr == integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveExitToDebugger */ +static void +primitiveExitToDebugger(void) +{ + error("Exit to debugger at user request"); +} + + +/* Computes E raised to the receiver power. + Receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveExp */ +static void +primitiveExp(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(exp(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + +/* Exponent part of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveExponent */ +static void +primitiveExponent(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + double doubleValue; + int pwr; + sqInt rcvr; + + pwr = 0; + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + + /* rcvr = frac * 2^pwr, where frac is in [0.5..1.0) */ + frexp(doubleValue, (&pwr)); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),(((usqInt)(pwr - 1) << 3) | 1)); +} + + +/* Set general (unspecified) primitive failure. Don't overwrite an error code + that has already been set. */ +/* Use returnTypeC: #sqInt because that's the way it is defined in sq.h. + Use no explicit return so that Slang doesn't fail an inlining type-check + when a primitive with return type void uses ^self primitiveFail to exit. */ + + /* InterpreterPrimitives>>#primitiveFail */ +sqInt +primitiveFail(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; +} + + +/* Set PrimErrOSError primitive failure and associated osErrorCode. */ + + /* InterpreterPrimitives>>#primitiveFailForOSError: */ +sqInt +primitiveFailForOSError(sqLong osErrorCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(secondaryErrorCode) = osErrorCode; + return (GIV(primFailCode) = PrimErrOSError); +} + + +/* Set specific primitive failure. + N.B. primitiveFailFor: PrimNoErr is expected to clear the primFailCode. */ + + /* InterpreterPrimitives>>#primitiveFailFor: */ +sqInt +primitiveFailFor(sqInt reasonCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (GIV(primFailCode) = reasonCode); +} + + +/* Set primFailCode primitive failure and associated secondaryErrorCode. */ + + /* InterpreterPrimitives>>#primitiveFailFor:withSecondary: */ +sqInt +primitiveFailForwithSecondary(sqInt reasonCode, sqLong extraErrorCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(secondaryErrorCode) = extraErrorCode; + return (GIV(primFailCode) = reasonCode); +} + + /* InterpreterPrimitives>>#primitiveFailureCode */ +sqInt +primitiveFailureCode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(primFailCode); +} + + /* InterpreterPrimitives>>#primitiveFetchNextMourner */ +static void +primitiveFetchNextMourner(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt mourner; + + mourner = /* dequeueMourner */ + (GIV(mournQueue) != GIV(nilObj) + ? (assert(isValidObjStack(GIV(mournQueue))), + popObjStack(GIV(mournQueue))) + : 0); + if (mourner) { + assert(isValidObjStack(GIV(mournQueue))); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),mourner); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNotFound; + } +} + + /* InterpreterPrimitives>>#primitiveFloatAdd */ +static void +primitiveFloatAdd(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + sqInt argOop; + usqLong bits; + double rcvr; + sqInt rcvrOop; + double result; + char *sp; + sqInt tagBits; + double value; + + rcvrOop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + argOop = longAt(GIV(stackPointer)); + + /* begin primitiveFloatAdd:toArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l2: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr + arg)); + GIV(stackPointer) = sp; + } + return; +} + + +/* Index the receiver, which must be an indexable non-pointer object, and + yield a float. + */ + + /* InterpreterPrimitives>>#primitiveFloatArrayAt */ +static void +primitiveFloatArrayAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + float aFloat; + sqLong doubleBits; + sqInt fmt; + sqInt index; + usqInt newFloatObj; + usqInt newObj; + usqInt numBytes; + sqInt numSlots; + sqInt numSlotsSqInt; + sqInt oop; + usqLong rawFloatBits; + sqInt rcvr; + usqLong rot; + + /* begin primitiveSpurFloatArrayAt */ + index = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l3; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l3; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + doubleBits = long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3))))); + + /* begin floatObjectOfBits: */ + if (isSmallFloatValueBits(doubleBits)) { + rawFloatBits = doubleBits; + + /* begin smallFloatObjectOfBits: */ + assert(isSmallFloatValueBits(rawFloatBits)); + rot = (((rawFloatBits) >> 0x3F) & 1) + ((rawFloatBits << 1)); + if (rot > 1) { + rot -= (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + assert(rot > 0); + } + + /* a.k.a. ~= +/-0.0 */ + oop = ((rot << (numTagBits()))) + (smallFloatTag()); + goto l2; + } + numSlotsSqInt = (sizeof(double)) / BytesPerOop; + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((numSlotsSqInt >= 0) + && ((knownClassAtIndex(ClassFloatCompactIndex)) != GIV(nilObj))); + assert((firstLongFormat()) == (instSpecOfClass(knownClassAtIndex(ClassFloatCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlotsSqInt < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlotsSqInt < 1 + ? 8 /* allocationUnit */ + : numSlotsSqInt * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newFloatObj = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlotsSqInt)) << (numSlotsFullShift()))) + ((((usqInt)((firstLongFormat())) << (formatShift()))))) + ClassFloatCompactIndex); + GIV(freeStart) += numBytes; + newFloatObj = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((newFloatObj + BaseHeaderSize)),doubleBits); + oop = newFloatObj; + /* end floatObjectOfBits: */ +l2: + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + goto l3; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l3; + } + if ((fmt >= (firstLongFormat())) + && (fmt <= ((firstLongFormat()) + 1))) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + /* begin fetchFloat32:ofObject: */ + aFloat = singleFloatAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2))))); + oop = floatObjectOf(aFloat); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + goto l3; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l3; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + /* end primitiveSpurFloatArrayAt */ +l3:; +} + + +/* Index the receiver, which must be an indexable non-pointer object, and + store a float. + */ + + /* InterpreterPrimitives>>#primitiveFloatArrayAtPut */ +static void +primitiveFloatArrayAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + float aFloat; + usqLong bits; + sqInt fmt; + sqInt index; + sqInt numSlots; + sqInt rcvr; + double result; + sqInt tagBits; + sqLong value; + sqInt valueOop; + + /* begin primitiveSpurFloatArrayAtPut */ + valueOop = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (!((/* isFloatInstance: */ + ((tagBits = valueOop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(valueOop))) & (classIndexMask())) == ClassFloatCompactIndex)) + && ((((index) & 7) == 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l1; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l1; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + value = floatValueBitsOf(valueOop); + + /* begin storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3)))),value); + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + + /* N.B. Currently we simply truncate to 32-bits, which matches the behavior of the FloatArrayPlugin. + Maybe we should validate and range check. */ + if ((fmt >= (firstLongFormat())) + && (fmt <= ((firstLongFormat()) + 1))) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(valueOop)); + if (valueOop & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(valueOop)); + bits = ((((usqInt)valueOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&result), (&bits), sizeof(result)); + } + else { + fetchFloatAtinto(valueOop + BaseHeaderSize, result); + } + aFloat = result; + + /* begin storeFloat32:ofObject:withValue: */ + singleFloatAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2)))),aFloat); + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + /* end primitiveSpurFloatArrayAtPut */ +l1:; +} + + +/* Provide platform-independent access to 32-bit words comprising + a Float. Map index 1 onto the most significant word and index 2 + onto the least significant word. */ + + /* InterpreterPrimitives>>#primitiveFloatAt */ +static void +primitiveFloatAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + sqInt fieldIndex; + sqInt index; + unsigned int integerValue; + sqInt rcvr; + usqInt result; + + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + index = longAt(GIV(stackPointer)); + if (index == ConstOne) { + fieldIndex = (VMBIGENDIAN + ? 0 + : 1); + + /* begin fetchLong32:ofFloatObject: */ + if (!(((rcvr & (smallFloatTag())) != 0))) { + integerValue = long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2))))); + goto l1; + } + + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + integerValue = ((unsigned int) ((fieldIndex + ? (bits) >> 32 + : bits & 0xFFFFFFFFU))); + /* end fetchLong32:ofFloatObject: */ +l1: + + /* begin positive32BitIntegerFor: */ + result = (((usqInt)((((usqInt)integerValue)) & 0xFFFFFFFFU) << 3) | 1); + longAtput((GIV(stackPointer) += 1 * BytesPerWord),result); + return; + } + if (index == ConstTwo) { + fieldIndex = (VMBIGENDIAN + ? 1 + : 0); + + /* begin fetchLong32:ofFloatObject: */ + if (!(((rcvr & (smallFloatTag())) != 0))) { + integerValue = long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2))))); + goto l2; + } + + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + integerValue = ((unsigned int) ((fieldIndex + ? (bits) >> 32 + : bits & 0xFFFFFFFFU))); + /* end fetchLong32:ofFloatObject: */ +l2: + + /* begin positive32BitIntegerFor: */ + result = (((usqInt)((((usqInt)integerValue)) & 0xFFFFFFFFU) << 3) | 1); + longAtput((GIV(stackPointer) += 1 * BytesPerWord),result); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = ((((index) & 7) == 1) + ? PrimErrBadIndex + : PrimErrBadArgument); +} + + +/* Provide platform-independent access to 32-bit words comprising + a Float. Map index 1 onto the most significant word and index 2 + onto the least significant word. */ + + /* InterpreterPrimitives>>#primitiveFloatAtPut */ +static void +primitiveFloatAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt oopToStore; + sqInt rcvr; + usqInt valueToStore; + + oopToStore = longAt(GIV(stackPointer)); + valueToStore = positive32BitValueOf(oopToStore); + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((rcvr & (smallFloatTag())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + return; + } + if (index == ConstOne) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((VMBIGENDIAN + ? 0 + : 1))) << 2)))),valueToStore); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),oopToStore); + return; + } + if (index == ConstTwo) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((VMBIGENDIAN + ? 1 + : 0))) << 2)))),valueToStore); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),oopToStore); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = ((((index) & 7) == 1) + ? PrimErrBadIndex + : PrimErrBadArgument); +} + + /* InterpreterPrimitives>>#primitiveFloatDivide */ +static void +primitiveFloatDivide(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + sqInt argOop; + usqLong bits; + double rcvr; + sqInt rcvrOop; + double result; + char *sp; + sqInt tagBits; + double value; + + rcvrOop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + argOop = longAt(GIV(stackPointer)); + + /* begin primitiveFloatDivide:byArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l2: + + /* begin success: */ + if (!(arg != 0.0)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr / arg)); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatEqual */ +static void +primitiveFloatEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatEqualtoArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatGreaterOrEqual */ +static void +primitiveFloatGreaterOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatGreaterOrEqualtoArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatGreaterThan */ +static void +primitiveFloatGreaterThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatGreaterthanArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatLessOrEqual */ +static void +primitiveFloatLessOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatLessOrEqualtoArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatLessThan */ +static void +primitiveFloatLessThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatLessthanArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatMultiply */ +static void +primitiveFloatMultiply(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + sqInt argOop; + usqLong bits; + double rcvr; + sqInt rcvrOop; + double result; + char *sp; + sqInt tagBits; + double value; + + rcvrOop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + argOop = longAt(GIV(stackPointer)); + + /* begin primitiveFloatMultiply:byArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l2: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr * arg)); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatNotEqual */ +static void +primitiveFloatNotEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatEqualtoArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(falseObj) + : GIV(trueObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatSubtract */ +static void +primitiveFloatSubtract(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + sqInt argOop; + usqLong bits; + double rcvr; + sqInt rcvrOop; + double result; + char *sp; + sqInt tagBits; + double value; + + rcvrOop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + argOop = longAt(GIV(stackPointer)); + + /* begin primitiveFloatSubtract:fromArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l2: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr - arg)); + GIV(stackPointer) = sp; + } + return; +} + + +/* Clear the method lookup cache. This must be done after every programming + change. + */ + + /* InterpreterPrimitives>>#primitiveFlushCache */ +static void +primitiveFlushCache(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); +} + + +/* Primitive. Flush all the existing external primitives in the image thus + forcing a reload on next invocation. + */ + + /* InterpreterPrimitives>>#primitiveFlushExternalPrimitives */ +static void +primitiveFlushExternalPrimitives(void) +{ + flushExternalPrimitives(); +} + + +/* On platforms that support it, this primitive prints the receiver, assumed + to be a Form, to the default printer. + */ + + /* InterpreterPrimitives>>#primitiveFormPrint */ +static void +primitiveFormPrint(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bitsArray; + sqInt bitsArraySize; + sqLong depth; + sqInt fmt; + sqLong h; + double hScale; + sqInt landscapeFlag; + usqInt numBytes; + usqInt numSlots; + sqLong pixelsPerWord; + sqInt rcvr; + sqInt successBoolean; + double vScale; + sqLong w; + sqLong wordsPerLine; + + bitsArray = 0; + depth = 0; + h = 0; + w = 0; + + /* begin booleanValueOf: */ + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + landscapeFlag = 1; + goto l1; + } + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + landscapeFlag = 0; + goto l1; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + landscapeFlag = null; + /* end booleanValueOf: */ +l1: + vScale = floatValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + hScale = floatValueOf(longAt(GIV(stackPointer) + (2 * BytesPerWord))); + rcvr = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + if (!((/* isPointers: */ + ((!(rcvr & (tagMask())))) + && (((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((lengthOf(rcvr)) >= 4))) { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + bitsArray = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord())))); + w = fetchIntegerofObject(1, rcvr); + h = fetchIntegerofObject(2, rcvr); + depth = fetchIntegerofObject(3, rcvr); + if (!((w > 0) + && (h > 0))) { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + pixelsPerWord = 32 / depth; + wordsPerLine = (w + (pixelsPerWord - 1)) / pixelsPerWord; + if (/* isWordsOrBytes: */ + ((!(bitsArray & (tagMask())))) + && (isWordsOrBytesNonImm(bitsArray))) { + /* begin numBytesOf: */ + fmt = (byteAt((void *)(bitsArray + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(bitsArray)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(bitsArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(bitsArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + bitsArraySize = numBytes - (fmt & 7); + goto l2; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + bitsArraySize = numBytes; + goto l2; + } + if (fmt >= (firstShortFormat())) { + bitsArraySize = numBytes - (((fmt & 3) << 1)); + goto l2; + } + + /* fmt >= self firstLongFormat */ + bitsArraySize = numBytes - (((fmt & 1) << 2)); + /* end numBytesOf: */ +l2: + + /* begin success: */ + if (!(bitsArraySize == ((wordsPerLine * h) * 4))) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + if (!GIV(primFailCode)) { + successBoolean = ioFormPrint(bitsArray + BaseHeaderSize, w, h, depth, hScale, vScale, landscapeFlag); + + /* begin success: */ + if (!successBoolean) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + } +} + + +/* Fractional part of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveFractionalPart */ +static void +primitiveFractionalPart(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + double trunc; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(modf(doubleValue, (&trunc))); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveFullClosureValue */ +static void +primitiveFullClosureValue(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt blockClosure; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt header; + sqInt i; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + usqInt numTemps; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt switched; + + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(GIV(argumentCount) == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureCompiledBlockIndex) << (shiftForWord())))))); + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin activateNewFullClosure:method:numArgs:mayContextSwitch: */ + assert(closureMethod == (fetchPointerofObject(FullClosureCompiledBlockIndex, blockClosure))); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(blockClosure)))); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex; + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogFullBlockMethodnumCopied(closureMethod, numCopied); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(FullClosureReceiverIndex, blockClosure, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + FullClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(closureMethod)); + header = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + for (i = ((numArgs + numCopied) + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(closureMethod)))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + assert((((usqInt)closureMethod)) >= (startOfMemory())); + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeaderSqInt = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeaderSqInt)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(1); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewFullClosure:method:numArgs:mayContextSwitch: */ +l1:; +} + + +/* An exact clone of primitiveFullClosureValue except that this version will + not check for interrupts on stack overflow. It may invoke the garbage + collector but will not switch processes. See + checkForInterruptsMayContextSwitch: */ + + /* InterpreterPrimitives>>#primitiveFullClosureValueNoContextSwitch */ +void +primitiveFullClosureValueNoContextSwitch(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt blockClosure; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt header; + sqInt i; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + usqInt numTemps; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt switched; + + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(GIV(argumentCount) == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureCompiledBlockIndex) << (shiftForWord())))))); + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + GIV(deferProfileCheckForVNCS) = 1; + + /* begin activateNewFullClosure:method:numArgs:mayContextSwitch: */ + assert(closureMethod == (fetchPointerofObject(FullClosureCompiledBlockIndex, blockClosure))); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockNoContextSwitchEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(blockClosure)))); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex; + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogFullBlockMethodnumCopied(closureMethod, numCopied); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockNoContextSwitchEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(FullClosureReceiverIndex, blockClosure, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + FullClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(closureMethod)); + header = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + for (i = ((numArgs + numCopied) + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(closureMethod)))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + assert((((usqInt)closureMethod)) >= (startOfMemory())); + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeaderSqInt = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeaderSqInt)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(0); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewFullClosure:method:numArgs:mayContextSwitch: */ +l1:; +} + + /* InterpreterPrimitives>>#primitiveFullClosureValueWithArgs */ +static void +primitiveFullClosureValueWithArgs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt argumentArray; + usqInt arraySize; + sqInt blockClosure; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt header; + sqInt i; + sqInt index; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + usqInt numTemps; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt switched; + sqInt top; + + argumentArray = longAt(GIV(stackPointer)); + if (!(/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Check for enough space in thisContext to push all args */ + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (!(arraySize <= (LargeContextSlots - CtxtTempFrameStart))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(arraySize == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureCompiledBlockIndex) << (shiftForWord())))))); + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* Copy the arguments to the stack, and activate */ + index = 1; + while (index <= numArgs) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + index += 1; + } + + /* begin activateNewFullClosure:method:numArgs:mayContextSwitch: */ + assert(closureMethod == (fetchPointerofObject(FullClosureCompiledBlockIndex, blockClosure))); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(blockClosure)))); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex; + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogFullBlockMethodnumCopied(closureMethod, numCopied); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(FullClosureReceiverIndex, blockClosure, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + FullClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(closureMethod)); + header = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + for (i = ((numArgs + numCopied) + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(closureMethod)))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + assert((((usqInt)closureMethod)) >= (startOfMemory())); + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeaderSqInt = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeaderSqInt)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(1); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewFullClosure:method:numArgs:mayContextSwitch: */ +l1:; +} + + +/* Fetch the system attribute with the given integer ID. The result is a + string, or nil if the attribute is not defined. + */ + + /* InterpreterPrimitives>>#primitiveGetAttribute */ +static void +primitiveGetAttribute(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt attr; + const char *attributeString; + + attr = longAt(GIV(stackPointer)); + if (!((((attr) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if ((attributeString = getAttributeString((attr >> 3)))) { + methodReturnString(attributeString); + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); + } +} + + +/* Access to environment variables via getenv. No putenv or setenv as yet. */ + + /* InterpreterPrimitives>>#primitiveGetenv */ +EXPORT(sqInt) +primitiveGetenv(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *dstPtr; + sqInt fmt; + char *key; + usqInt numBytes; + usqInt numSlots; + sqInt obj; + sqInt sz; + char *val; + + if (sHEAFn) { + if (!(sHEAFn())) { + return (GIV(primFailCode) = PrimErrInappropriate); + } + } + + /* secHasEnvironmentAccess */ + + /* begin stackEphemeralStringValue: */ + obj = longAt(GIV(stackPointer)); + if (!(/* isBytes: */ + ((!(obj & (tagMask())))) + && (((byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + key = null; + goto l2; + } + + /* begin byteSizeOf: */ + if (((obj & (tagMask())) != 0)) { + sz = 0; + goto l1; + } + + /* begin numBytesOf: */ + fmt = (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + sz = numBytes - (fmt & 7); + goto l1; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + sz = numBytes; + goto l1; + } + if (fmt >= (firstShortFormat())) { + sz = numBytes - (((fmt & 3) << 1)); + goto l1; + } + + /* fmt >= self firstLongFormat */ + sz = numBytes - (((fmt & 1) << 2)); + /* end byteSizeOf: */ +l1: + dstPtr = alloca(sz + 1); + if (!dstPtr) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoCMemory; + key = null; + goto l2; + } + memcpy(dstPtr, ((char *) (firstIndexableField(obj))), sz); + dstPtr[sz] = 0; + key = dstPtr; + /* end stackEphemeralStringValue: */ +l2: + if (key) { + if ((val = getenv(key))) { + methodReturnString(val); + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); + } + } + return 0; +} + + /* InterpreterPrimitives>>#primitiveGetImmutability */ +#if IMMUTABILITY +static void +primitiveGetImmutability(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt boolean; + sqInt rcvr; + char *sp; + + rcvr = longAt(GIV(stackPointer)); + if ((GIV(argumentCount) > 0) + && (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + boolean = /* isOopImmutable: */ + (((rcvr & (tagMask())) != 0)) + || (((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0); + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (boolean + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; +} +#endif /* IMMUTABILITY */ + + +/* Primitive. Answer the VM's current log directory */ + + /* InterpreterPrimitives>>#primitiveGetLogDirectory */ +EXPORT(sqInt) +primitiveGetLogDirectory(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + char *ptr; + sqInt stringOop; + sqInt sz; + + ptr = ioGetLogDirectory(); + if (!ptr) { + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + sz = strlen(ptr); + stringOop = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord())))))), sz); + for (i = 0; i < sz; i += 1) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((stringOop + BaseHeaderSize) + i),ptr[i]); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),stringOop); + return 0; +} + + +/* Primitive. Return the next input event from the VM event queue. */ + + /* InterpreterPrimitives>>#primitiveGetNextEvent */ +static void +primitiveGetNextEvent(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqIntptr_t eventTypeIs; + static char * eventTypeNames[] = { + "None", "Mouse", "Keyboard", "DragDropFiles", "Menu", "Window", "Complex", "MouseWheel", + "Plugin" + }; + sqIntptr_t evtBuf[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + sqInt i; + sqIntptr_t value; + + arg = longAt(GIV(stackPointer)); + if (!((/* isArray: */ + ((!(arg & (tagMask())))) + && (((byteAt((void *)(arg + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((slotSizeOf(arg)) == 8))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + ioGetNextEvent(((sqInputEvent*) evtBuf)); + if (GIV(primFailCode)) { + return; + } + if ((eventTraceMask != 0) + && (((eventTraceMask & ((((evtBuf[0]) < 0) ? (((usqInt)(1)) >> (-(evtBuf[0]))) : (1ULL << (evtBuf[0]))))) != 0))) { + fprintf(stderr, + "Event%s/%" PRIdSQINT " @ %" PRIuSQINT "\t\t%" PRIdSQINT "/%" PRIxSQINT " %" PRIdSQINT "/%" PRIxSQINT "\n\t%" PRIdSQINT "/%" PRIxSQINT " %" PRIdSQINT "/%" PRIxSQINT "\t %" PRIdSQINT "/%" PRIxSQINT " %p\n", + ((((evtBuf[0]) >= 0) && ((evtBuf[0]) <= 8)) + ? eventTypeNames[evtBuf[0]] + : "?"), + evtBuf[0], + evtBuf[1], + evtBuf[2], + evtBuf[2], + evtBuf[3], + evtBuf[3], + evtBuf[4], + evtBuf[4], + evtBuf[5], + evtBuf[5], + evtBuf[6], + evtBuf[6], + ((void *)(evtBuf[7]))); + } + + /* Event type */ + eventTypeIs = evtBuf[0]; + storeIntegerofObjectwithValue(0, arg, evtBuf[0]); + if (GIV(primFailCode)) { + return; + } + if (eventTypeIs == 6) { + for (i = 1; i <= 7; i += 1) { + value = evtBuf[i]; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(i, arg, value)); + assert(isNonImmediate(arg)); + if (oopisGreaterThanOrEqualTo(arg, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(arg + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(arg); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((arg + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),value); + } + } + else { + storeIntegerofObjectwithValue(1, arg, (evtBuf[1]) & MillisecondClockMask); + if (GIV(primFailCode)) { + return; + } + for (i = 2; i <= 7; i += 1) { + value = evtBuf[i]; + if ((((((usqIntptr_t)(value)) >> 60) + 1) & 15) <= 1) { + storeIntegerofObjectwithValue(i, arg, value); + } + else { + value = positive64BitIntegerFor(value); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(i, arg, value)); + assert(isNonImmediate(arg)); + if (oopisGreaterThanOrEqualTo(arg, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(arg + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(arg); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((arg + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),value); + } + } + } + + /* Event is Complex, assume evtBuf is populated correctly and return + Event time stamp */ + if (!GIV(primFailCode)) { + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } +} + + /* InterpreterPrimitives>>#primitiveGreaterOrEqual */ +static void +primitiveGreaterOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver >= integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveGreaterOrEqualLargeIntegers */ +EXPORT(void) +primitiveGreaterOrEqualLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr >= integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveGreaterThan */ +static void +primitiveGreaterThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver > integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveGreaterThanLargeIntegers */ +EXPORT(void) +primitiveGreaterThanLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr > integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveGrowMemoryByAtLeast */ +static void +primitiveGrowMemoryByAtLeast(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ammount; + usqInt segSize; + + ammount = longAt(GIV(stackPointer)); + if (!((((ammount) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if ((segSize = growOldSpaceByAtLeast((ammount >> 3)))) { + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),((segSize << 3) | 1)); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + } +} + + +/* Implement 28-bit hashMultiply for SmallInteger and LargePositiveInteger + receivers. + */ + + /* InterpreterPrimitives>>#primitiveHashMultiply */ +static void +primitiveHashMultiply(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + sqInt ok; + sqInt value; + + value = longAt(GIV(stackPointer)); + if ((((value) & 7) == 1)) { + value = (value >> 3); + } + else { + /* begin is:instanceOf:compactClassIndex: */ + if (((value & (tagMask())) != 0)) { + ok = 0; + goto l1; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(value))); + ccIndex = (longAt((void *)(value))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + /* end is:instanceOf:compactClassIndex: */ +l1: + if (!ok) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + value = SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((value + BaseHeaderSize))))); + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),(((((((usqInt) value)) * HashMultiplyConstant) & 0xFFFFFFF) << 3) | 1)); +} + + +/* Answer the heartbeat frequency in beats per second. If the argument is + true, reset the frequency measure. */ + + /* InterpreterPrimitives>>#primitiveHeartbeatFrequency */ +EXPORT(sqInt) +primitiveHeartbeatFrequency(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + unsigned int integerValue; + sqInt oop; + sqInt reset; + + reset = (GIV(argumentCount) == 1) + && ((longAt(GIV(stackPointer))) == GIV(trueObj)); + integerValue = ((unsigned int) (ioHeartbeatFrequency(reset))); + + /* begin positive32BitIntegerFor: */ + oop = (((usqInt)((((usqInt)integerValue)) & 0xFFFFFFFFU) << 3) | 1); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return 0; +} + + /* InterpreterPrimitives>>#primitiveHighBit */ +static void +primitiveHighBit(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + unsigned long highestBitZeroBased; + sqInt integerReceiverOop; + sqInt leadingZeroCount; + + integerReceiverOop = longAt(GIV(stackPointer)); + + /* Convert the receiver Oop to use a single tag bit */ + integerReceiverOop = ((integerReceiverOop) >> 2 /* (numSmallIntegerTagBits - 1) */) | 1; + if (integerReceiverOop < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } +# if defined(__GNUC__) + /* Note: in gcc, result is undefined if input is zero (for compatibility with BSR fallback when no CLZ instruction available). + but input is never zero because we pass the oop with tag bits set, so we are safe */ + leadingZeroCount = __builtin_clzll(integerReceiverOop); + assert(!((leadingZeroCount == 0))); + + /* Nice bit trick: 1-based high-bit is (32 - clz) - 1 to account for tag bit. + This is like two-complement - clz - 1 on 5 bits, or in other words a bit-invert operation clz ^16r1F */ + + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),(((usqInt)(leadingZeroCount ^ ((BytesPerWord * 8) - 1)) << 3) | 1)); +# elif (defined(_MSC_VER)) || (defined(__ICC)) + /* In MSVC, _lzcnt and _lzcnt64 builtins do not fallback to BSR when not supported by CPU + Instead of messing with __cpuid() we always use the BSR intrinsic + Setting this variable is useless, but VMMaker will generate an automatic initialization at a worse place if this isn't initialized explicitly. */ + highestBitZeroBased = 0; + + /* We do not even test the return value, because integerReceiverOop is never zero */ + _BitScanReverse64((&highestBitZeroBased), integerReceiverOop); + + /* thanks to the tag bit, the +1 operation for getting 1-based rank is not necessary */ + + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),((highestBitZeroBased << 3) | 1)); +# else // defined(__GNUC__) || (defined(_MSC_VER)) || (defined(__ICC)) + /* not gcc/clang, nor MSVC/ICC, you have to implement if your compiler provides useful builtins */ + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } +# endif // defined(__GNUC__) || (defined(_MSC_VER)) || (defined(__ICC)) +} + + +/* Return the value of the high resolution clock if this system has any. + The exact frequency of the high res clock is undefined specifically so + that we can use + processor dependent instructions (like RDTSC). The only use for the high + res clock is for + profiling where we can allocate time based on sub-msec resolution of the + high res clock. + If no high-resolution counter is available, the platform should return + zero. ar 6/22/2007 */ + + /* InterpreterPrimitives>>#primitiveHighResClock */ +EXPORT(sqInt) +primitiveHighResClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = positive64BitIntegerFor(ioHighResClock()); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return 0; +} + + +/* is the receiver/first argument the same object as the (last) argument?. + pop argumentCount because this can be used as a mirror primitive. */ + + /* InterpreterPrimitives>>#primitiveIdentical */ +static void +primitiveIdentical(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt otherObject; + char *sp; + sqInt thisObject; + + thisObject = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + otherObject = longAt(GIV(stackPointer)); + if ((/* isOopForwarded: */ + ((!(otherObject & (tagMask())))) + && ((!((longAt((void *)(otherObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + || ((GIV(argumentCount) > 1) + && (/* isOopForwarded: */ + ((!(thisObject & (tagMask())))) + && ((!((longAt((void *)(thisObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (thisObject == otherObject + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveIdentityHash */ +static void +primitiveIdentityHash(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt hash; + usqInt hashUsqInt; + sqInt integer; + sqInt thisReceiver; + + thisReceiver = longAt(GIV(stackPointer)); + if ((((thisReceiver & (tagMask())) != 0)) + || ((GIV(argumentCount) > 0) + && ((!((longAt((void *)(thisReceiver))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = (GIV(argumentCount) > 0 + ? PrimErrBadArgument + : PrimErrBadReceiver); + return; + } + + /* fail if forwarded; the retry machinery will fix things up. */ + + /* begin hashBitsOf: */ + hash = (long32At((void *)(thisReceiver + 4))) & (identityHashHalfWordMask()); + if (!hash) { + /* cb 1/19/2017 18:34: + would like to assert + self assert: (coInterpreter addressCouldBeClassObj: objOop) not + but instance-specific behaviors that are instances of themselves may + fail this test. + eem 12/28/2021 11:52 I'm not sure this is the issue. The issue is only validating objOop as a behavior. */ + + /* begin newHashBitsOf: */ + /* begin newObjectHash */ + assert(!((GIV(lastHash) == 0))); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 2)); + GIV(lastHash) = GIV(lastHash) ^ ((((GIV(lastHash) << 7))) & (identityHashHalfWordMask())); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 3)); + hashUsqInt = GIV(lastHash); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(thisReceiver + 4),((((long32At((void *)(thisReceiver + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hashUsqInt & (identityHashHalfWordMask()))); + hash = hashUsqInt & (identityHashHalfWordMask()); + } + integer = hash; + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); +} + + +/* Answer an integer identifying the type of image. The image version number + may identify the format of the image (e.g. 32 or 64-bit word size) or + specific requirements + of the image (e.g. block closure support required). + + This is a named (not numbered) primitive in the null module (ie the VM) + */ + + /* InterpreterPrimitives>>#primitiveImageFormatVersion */ +EXPORT(sqInt) +primitiveImageFormatVersion(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = (((usqInt)((((usqInt)(/* imageFormatVersionForSnapshot */ + (GIV(multipleBytecodeSetsActive) + ? 68533 /* (imageFormatVersion bitOr: MultipleBytecodeSetsBitmask) */ + : 68021 /* imageFormatVersion */)))) & 0xFFFFFFFFU) << 3) | 1); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); + return 0; +} + + +/* When called with a single string argument, record the string as the + current image file name. + When called with zero arguments, return a string containing the current + image file name. + */ + + /* InterpreterPrimitives>>#primitiveImageName */ +static void +primitiveImageName(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + sqInt isString; + sqInt okToRename; + sqInt s; + void *sCRIfn; + sqInt sz; + + if (GIV(argumentCount) == 1) { + s = longAt(GIV(stackPointer)); + + /* begin isInstanceOfClassByteString: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((s & (tagMask())) != 0)) { + isString = 0; + goto l1; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(s))); + ccIndex = (longAt((void *)(s))) & (classIndexMask()); + isString = ClassByteStringCompactIndex == ccIndex; + /* end isInstanceOfClassByteString: */ +l1: + if (!isString) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* If the security plugin can be loaded, use it to check for rename permission. + If not, assume it's ok */ + sCRIfn = ioLoadFunctionFrom("secCanRenameImage", "SecurityPlugin"); + if (sCRIfn) { + okToRename = ((sqInt (*)(void))sCRIfn)(); + if (!okToRename) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + } + imageNamePutLength(s + BaseHeaderSize, numBytesOf(s)); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + return; + } + sz = imageNameSize(); + s = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord())))))), sz); + imageNameGetLength(s + BaseHeaderSize, sz); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),s); +} + + +/* For a Smalllnteger, answer itself. + For a Character, answer its code as an unsigned integer. + For a SmallFloat, answer the signed, but unadjusted bit pattern (so as to + keep the result a SmallInteger). + This is a good value for an immediate's hash. */ + + /* InterpreterPrimitives>>#primitiveImmediateAsInteger */ +static void +primitiveImmediateAsInteger(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + usqInt value; + + value = 0; + oop = longAt(GIV(stackPointer)); + + /* begin immediateAsInteger:ifFail: */ + if ((((oop) & 7) == 1)) { + value = (oop >> 3); + goto l1; + } + if (((oop & (characterTag())) != 0)) { + value = ((((usqInt)oop))) >> (numTagBits()); + goto l1; + } + if (((oop & (smallFloatTag())) != 0)) { + /* begin rotatedFloatBitsOf: */ + assert(isImmediateFloat(oop)); + value = (((((usqInt)oop))) >> ((numTagBits()) + 1)) + ((((oop & (1U << (numTagBits()))) != 0) + ? ((((usqInt)-1)) << ((64 - (numTagBits())) - 1)) + : 0)); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + /* end immediateAsInteger:ifFail: */ +l1: + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),((value << 3) | 1)); +} + + +/* Register the input semaphore. If the argument is not a + Semaphore, unregister the current input semaphore. */ + + /* InterpreterPrimitives>>#primitiveInputSemaphore */ +static void +primitiveInputSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + + arg = longAt(GIV(stackPointer)); + if ((((arg) & 7) == 1)) { + /* If arg is integer, then use it as an index + into the external objects array and install it + as the new event semaphore */ + ioSetInputSemaphore((arg >> 3)); + if (!GIV(primFailCode)) { + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + return; + } + + /* old code for compatibility */ + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; +} + + +/* Return an integer indicating the reason for the most recent input + interrupt. + */ + + /* InterpreterPrimitives>>#primitiveInputWord */ +static void +primitiveInputWord(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),(((usqInt)0 << 3) | 1)); +} + + /* InterpreterPrimitives>>#primitiveIntegerAt */ +static void +primitiveIntegerAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classPointer; + sqInt fmt; + sqInt fmtSqInt; + sqInt index; + sqInt numFixed; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt rcvr; + sqInt value; + + /* begin primitiveSpurIntegerAt */ + index = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l1; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + goto l1; + } + + /* begin numBytesOfBytes: */ + fmtSqInt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert(fmtSqInt >= (firstByteFormat())); + numSlots = ((((/* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) << (shiftForWord()))) - (fmtSqInt & 7); + if ((((usqInt)index)) < numSlots) { + value = byteAt((void *)((rcvr + BaseHeaderSize) + index)); + if (value > 0x7F) { + value -= 0x100; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)value << 3) | 1)); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt >= (firstShortFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 1; + if ((((usqInt)index)) < numSlots) { + value = shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 1))))); + if (value > 0x7FFF) { + value -= 0x10000; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)value << 3) | 1)); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + oop = signed64BitIntegerFor(long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3)))))); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt >= (firstLongFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + value = long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2))))); + if (value > 0x7FFFFFFF) { + value -= 0x100000000LL; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)value << 3) | 1)); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if ((fmt <= 5 /* lastPointerFormat */) + && (/* isIndexableFormat: */ + (fmt >= (arrayFormat())) + && ((fmt <= (weakArrayFormat())) + || (fmt >= (sixtyFourBitIndexableFormat()))))) { + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt == (arrayFormat())) { + if ((((usqInt)index)) < numSlots) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))))); + goto l1; + } + } + + /* begin numFixedSlotsOf: */ + classPointer = fetchClassOfNonImm(rcvr); + classFormat = ((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + numFixed = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + if ((((index + 1) >= numFixed) && ((index + 1) <= numSlots))) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index + numFixed)) << (shiftForWord()))))))); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + /* end primitiveSpurIntegerAt */ +l1:; +} + + /* InterpreterPrimitives>>#primitiveIntegerAtPut */ +static void +primitiveIntegerAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classPointer; + sqInt fieldIndex; + sqInt fmt; + sqInt fmtSqInt; + sqInt index; + sqInt numFixed; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqLong value; + sqInt valueOop; + + /* begin primitiveSpurIntegerAtPut */ + valueOop = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + value = signed64BitValueOf(valueOop); + if (!((!GIV(primFailCode)) + && ((((index) & 7) == 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l1; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l1; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + goto l1; + } + if (!((((usqInt)((((value) >> 7)) + 1))) <= 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + + /* begin numBytesOfBytes: */ + fmtSqInt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert(fmtSqInt >= (firstByteFormat())); + numSlots = ((((/* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) << (shiftForWord()))) - (fmtSqInt & 7); + if ((((usqInt)index)) < numSlots) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + index),value); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt >= (firstShortFormat())) { + if (!((((usqInt)((((value) >> 15)) + 1))) <= 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 1; + if ((((usqInt)index)) < numSlots) { + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 1)))),value); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + /* No range check on value in this case because signed64BitValueOf: performed it above. */ + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3)))),value); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt >= (firstLongFormat())) { + if (!((((usqInt)((((value) >> 0x1F)) + 1))) <= 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2)))),value); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if ((fmt <= 5 /* lastPointerFormat */) + && (/* isIndexableFormat: */ + (fmt >= (arrayFormat())) + && ((fmt <= (weakArrayFormat())) + || (fmt >= (sixtyFourBitIndexableFormat()))))) { + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt == (arrayFormat())) { + if ((((usqInt)index)) < numSlots) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, rcvr, valueOop)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valueOop & (tagMask())))) + && (oopisLessThan(valueOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),valueOop); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + } + + /* begin numFixedSlotsOf: */ + classPointer = fetchClassOfNonImm(rcvr); + classFormat = ((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + numFixed = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + if ((((index + 1) >= numFixed) && ((index + 1) <= numSlots))) { + fieldIndex = index + numFixed; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, rcvr, valueOop)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valueOop & (tagMask())))) + && (oopisLessThan(valueOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valueOop); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + /* end primitiveSpurIntegerAtPut */ +l1:; +} + + +/* Primitive. Answer the number of interrupt checks per milliseconds that we + execute on this machine. This can be used to adjust the sub-msecs profiler + to check (roughly) + n times per millisecond. + */ + + /* InterpreterPrimitives>>#primitiveInterruptChecksPerMSec */ +EXPORT(sqInt) +primitiveInterruptChecksPerMSec(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + if (GIV(argumentCount)) { + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + oop = (((usqInt)(ioHeartbeatMilliseconds()) << 3) | 1); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); + return 0; +} + + +/* Register the user interrupt semaphore. If the argument is + not a Semaphore, unregister the current interrupt + semaphore. */ + + /* InterpreterPrimitives>>#primitiveInterruptSemaphore */ +static void +primitiveInterruptSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + + arg = longAt(GIV(stackPointer)); + if ((arg == GIV(nilObj)) + || (/* isSemaphoreOop: */ + ((!(arg & (tagMask())))) + && (((longAt((void *)(arg))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))))) { + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheInterruptSemaphore, GIV(specialObjectsOop), arg)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(arg & (tagMask())))) + && (oopisLessThan(arg, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheInterruptSemaphore) << (shiftForWord()))))),arg); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } +} + + +/* Answer if running on a big endian machine. */ + + /* InterpreterPrimitives>>#primitiveIsBigEnder */ +EXPORT(sqInt) +primitiveIsBigEnder(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop:thenPushBool: */ + longAtput(GIV(stackPointer),GIV(falseObj)); + return 0; +} + + +/* Answer if the receiver is pinned, i.e. immobile. */ + + /* InterpreterPrimitives>>#primitiveIsPinned */ +static void +primitiveIsPinned(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt obj; + char *sp; + sqInt trueOrFalse; + + obj = longAt(GIV(stackPointer)); + if ((((obj & (tagMask())) != 0)) + || ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + trueOrFalse = ((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0; + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (trueOrFalse + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; +} + + /* InterpreterPrimitives>>#primitiveLessOrEqual */ +static void +primitiveLessOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver <= integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveLessOrEqualLargeIntegers */ +EXPORT(void) +primitiveLessOrEqualLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr <= integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveLessThan */ +static void +primitiveLessThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver < integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveLessThanLargeIntegers */ +EXPORT(void) +primitiveLessThanLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr < integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + +/* Primitive. Return the n-th builtin module name. */ + + /* InterpreterPrimitives>>#primitiveListBuiltinModule */ +static void +primitiveListBuiltinModule(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt integerPointer; + char *moduleName; + + if (!(GIV(argumentCount) == 1)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + index = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + index = 0; + } + if (GIV(primFailCode) + || (index <= 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + if ((moduleName = ioListBuiltinModule(index))) { + methodReturnString(moduleName); + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); + } +} + + +/* Primitive. Answer the n-th loaded external module name. */ + + /* InterpreterPrimitives>>#primitiveListExternalModule */ +static void +primitiveListExternalModule(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt integerPointer; + char *moduleName; + + if (!(GIV(argumentCount) == 1)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + index = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + index = 0; + } + if (GIV(primFailCode) + || (index <= 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + if ((moduleName = ioListLoadedModule(index))) { + methodReturnString(moduleName); + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); + } +} + + +/* This primitive is called from Smalltalk as... + loadSegmentFrom: aWordArray outPointers: anArray. + + This primitive will load a binary image segment created by + primitiveStoreImageSegment. It expects the outPointer array to be of the + proper size, and the wordArray to be well + formed. It will return as its value the original array of roots, and the + erstwhile segmentWordArray will have been truncated to a size of one word, + i.e. retaining the version + stamp. If this primitive should fail, the segmentWordArray will, sadly, + have been reduced to + an unrecognizable and unusable jumble. But what more could you have done + with it anyway? + [How about saving it so the system functions as primitives are intended? + eem 5/9/2017 16:31] + + In Spur, if the primitive succeeds, the segmentWordArray is also becomed + into the array of loaded + objects, to allow fixing up of loaded objects directly without nextObject, + which Spur doesn't support. */ + + /* InterpreterPrimitives>>#primitiveLoadImageSegment */ +static void +primitiveLoadImageSegment(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt outPointerArray; + sqInt result; + sqInt segmentWordArray; + + outPointerArray = longAt(GIV(stackPointer)); + segmentWordArray = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* Essential type checks */ + if (!((/* isArray: */ + ((!(outPointerArray & (tagMask())))) + && (((byteAt((void *)(outPointerArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (/* isWords: */ + ((!(segmentWordArray & (tagMask())))) + && (((((byteAt((void *)(segmentWordArray + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(segmentWordArray + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1))))))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Must be indexable pointers + Must be indexable words + the engine returns the roots array which was first in the segment, or an error code on failure. */ + result = loadImageSegmentFromoutPointers(segmentWordArray, outPointerArray); + if (oopisGreaterThan(result, segmentWordArray)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),result); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = result; + } +} + + +/* Return the value of the microsecond clock in the local timezone as an + integer. This is the number of microseconds since the Smalltalk epoch, + 1901/1/1 12:00am. + The microsecond clock is at least 60 bits wide which means it'll get to + around August + 38435 before it wraps around. Be sure to put it on your calendar. This + primitive accesses the time as answered by the OS. */ + + /* InterpreterPrimitives>>#primitiveLocalMicrosecondClock */ +static void +primitiveLocalMicrosecondClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = positive64BitIntegerFor(ioLocalMicrosecondsNow()); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Natural log of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveLogN */ +static void +primitiveLogN(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(log(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + +/* Register the low-space semaphore. If the argument is not a + Semaphore, unregister the current low-space Semaphore. */ + + /* InterpreterPrimitives>>#primitiveLowSpaceSemaphore */ +static void +primitiveLowSpaceSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + + arg = longAt(GIV(stackPointer)); + if ((arg == GIV(nilObj)) + || (/* isSemaphoreOop: */ + ((!(arg & (tagMask())))) + && (((longAt((void *)(arg))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))))) { + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheLowSpaceSemaphore, GIV(specialObjectsOop), arg)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(arg & (tagMask())))) + && (oopisLessThan(arg, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheLowSpaceSemaphore) << (shiftForWord()))))),arg); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } +} + + /* InterpreterPrimitives>>#primitiveMakePoint */ +static void +primitiveMakePoint(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + usqInt newObj; + usqInt numBytes; + usqInt pt; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + arg = longAt(GIV(stackPointer)); + if (!(/* isFloatOrInt: */ + (((arg & (tagMask())) != 0) + ? !(((arg & (characterTag())) != 0)) + : ((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* begin eeInstantiatePoint */ + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert(((YIndex + 1) >= 0) + && ((knownClassAtIndex(ClassPointCompactIndex)) != GIV(nilObj))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(knownClassAtIndex(ClassPointCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert((YIndex + 1) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (((YIndex + 1) < 1 + ? 8 /* allocationUnit */ + : (YIndex + 1) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + pt = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) (YIndex + 1))) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + ClassPointCompactIndex); + GIV(freeStart) += numBytes; + pt = newObj; + /* end eeInstantiatePoint */ +l1: + + /* No need to check since new object is always new. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pt)) + && (!(isForwarded(pt)))); + assert(validStorePointerUncheckedArgs(XIndex, pt, rcvr)); + longAtput((void *)((pt + BaseHeaderSize) + ((((usqInt)(XIndex) << (shiftForWord()))))),rcvr); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pt)) + && (!(isForwarded(pt)))); + assert(validStorePointerUncheckedArgs(YIndex, pt, arg)); + longAtput((void *)((pt + BaseHeaderSize) + ((((usqInt)(YIndex) << (shiftForWord()))))),arg); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),pt); +} + + /* InterpreterPrimitives>>#primitiveMaxIdentityHash */ +static void +primitiveMaxIdentityHash(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),(((usqInt)(identityHashHalfWordMask()) << 3) | 1)); +} + + +/* Return the value of the millisecond clock as an integer. Note that the + millisecond clock wraps around periodically. On some platforms it can wrap + daily. The range is limited to SmallInteger maxVal / 2 to allow delays of + up to that length without overflowing a SmallInteger. + */ + + /* InterpreterPrimitives>>#primitiveMillisecondClock */ +static void +primitiveMillisecondClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = ((((ioMSecs()) & MillisecondClockMask) << 3) | 1); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Provide access to the millisecond clock mask to support calculation + of durations based on the millisecond clock value. */ + + /* InterpreterPrimitives>>#primitiveMillisecondClockMask */ +EXPORT(sqInt) +primitiveMillisecondClockMask(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),(((usqInt)MillisecondClockMask << 3) | 1)); + return 0; +} + + /* InterpreterPrimitives>>#primitiveMod */ +static void +primitiveMod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt mod; + + mod = doPrimitiveModby(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)(mod)) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)mod << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveModLargeIntegers */ +EXPORT(void) +primitiveModLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (!b) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return; + } + result = a % b; + + /* Handle remainder of same sign as argument */ + if (result) { + if (!(bIsNegative == aIsNegative)) { + result = b - result; + } + } + oopResult = magnitude64BitIntegerForneg(result, bIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Given one boolean parameter, set multipleBytecodeSetsActive to inform + the VM that alternate bytecode sets such as SistaV1 are now in use and + that the image format number should be updated accordingly. With zero + parameters, answer the current value of multipleBytecodeSetsActive. */ + + /* InterpreterPrimitives>>#primitiveMultipleBytecodeSetsActive */ +EXPORT(sqInt) +primitiveMultipleBytecodeSetsActive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + if (GIV(argumentCount) > 1) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + if (GIV(argumentCount) == 1) { + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + GIV(multipleBytecodeSetsActive) = 1; + } + else { + if (!((longAt(GIV(stackPointer))) == GIV(falseObj))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + GIV(multipleBytecodeSetsActive) = 0; + } + } + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (GIV(multipleBytecodeSetsActive) + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return 0; +} + + /* InterpreterPrimitives>>#primitiveMultiply */ +static void +primitiveMultiply(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArg; + sqInt integerPointer; + sqInt integerRcvr; + sqInt integerResult; + sqInt overflow; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + integerRcvr = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerRcvr = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + integerArg = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArg = 0; + } + if (!GIV(primFailCode)) { + overflow = (integerRcvr > 0 + ? (integerArg > 0 + ? integerRcvr > ((MaxSmallInteger) / integerArg) + : integerArg < ((MinSmallInteger) / integerRcvr)) + : (integerArg > 0 + ? integerRcvr < ((MinSmallInteger) / integerArg) + : (integerRcvr < 0) + && (integerArg < ((MaxSmallInteger) / integerRcvr)))); + if (overflow) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + else { + integerResult = integerRcvr * integerArg; + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerResult << 3) | 1)); + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveMultiplyLargeIntegers */ +EXPORT(void) +primitiveMultiplyLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (GIV(primFailCode)) { + return; + } + + /* check for overflow */ + if ((a > 1) + && ((b > 1) + && (a > (0xFFFFFFFFFFFFFFFFULL / b)))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + result = a * b; + oopResult = magnitude64BitIntegerForneg(result, aIsNegative != bIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + /* InterpreterPrimitives>>#primitiveNew */ +static void +primitiveNew(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt obj; + sqInt reasonCode; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + + /* Allocate a new fixed-size instance. Fail if the allocation would leave + less than lowSpaceThreshold bytes free. This *will not* cause a GC :-) */ + if ((obj = instantiateClass(longAt(GIV(stackPointer))))) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + reasonCode = (isFixedSizePointerFormat((((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + /* InterpreterPrimitives>>#primitiveNewMethod */ +static void +primitiveNewMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bytecodeCount; + sqInt class; + sqInt classFormat; + sqInt classIndex; + sqInt err; + int hash; + sqInt header; + sqInt i; + sqInt instSpec; + usqInt literalCount; + sqInt newObj; + usqInt newObjUsqInt; + usqInt numBytes; + usqInt numSlots; + usqInt p; + sqInt reasonCode; + usqInt size; + sqInt theMethod; + usqInt toDoLimit; + + header = longAt(GIV(stackPointer)); + bytecodeCount = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(((((header) & 7) == 1)) + && (((((bytecodeCount) & 7) == 1)) + && (((bytecodeCount = (bytecodeCount >> 3))) >= 0)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + class = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + literalCount = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + size = ((literalCount + LiteralStart) * BytesPerOop) + bytecodeCount; + + /* begin instantiateCompiledMethodClass:indexableSize: */ + classFormat = ((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + if (instSpec != (firstCompiledMethodFormat())) { + theMethod = null; + goto l2; + } + numSlots = (size + 7) / 8; + instSpec += (8 - size) & 7; + + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(class)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(class + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(class) + ? ((err = enterIntoClassTable(class)) + ? -err + : (long32At((void *)(class + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + theMethod = null; + goto l2; + } + if (numSlots > ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (numSlots > (0x10000000000LL)) { + theMethod = null; + goto l2; + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpec, classIndex); + } + else { + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if (((numSlots) >> 56) > 0) { + newObj = null; + goto l1; + } + newObjUsqInt = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObjUsqInt = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, instSpec, classIndex); + goto l1; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObjUsqInt),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObjUsqInt),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObjUsqInt % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newObj = newObjUsqInt; + /* end allocateSlots:format:classIndex: */ +l1:; + } + if (newObj) { + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + theMethod = newObj; + /* end instantiateCompiledMethodClass:indexableSize: */ +l2: + if (!theMethod) { + reasonCode = (isCompiledMethodFormat((((usqInt)((((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + return; + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theMethod)) + && (!(isForwarded(theMethod)))); + assert(validStorePointerUncheckedArgs(HeaderIndex, theMethod, header)); + longAtput((void *)((theMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))),header); + for (i = 1; i <= literalCount; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theMethod)) + && (!(isForwarded(theMethod)))); + assert(validStorePointerUncheckedArgs(i, theMethod, GIV(nilObj))); + longAtput((void *)((theMethod + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),theMethod); +} + + +/* Allocate a new indexable instance. Fail if the allocation would leave less + than lowSpaceThreshold bytes free. May cause a GC. + */ + + /* InterpreterPrimitives>>#primitiveNewWithArg */ +static void +primitiveNewWithArg(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt instSpec; + sqInt obj; + sqInt reasonCode; + usqIntptr_t size; + sqInt value; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + size = null; + goto l1; + } + size = value; + goto l1; + } + + /* don't inline the rare case */ + size = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* positiveMachineIntegerValueOf: succeeds only for non-negative integers. */ + if ((obj = instantiateClassindexableSize(longAt(GIV(stackPointer) + (1 * BytesPerWord)), size))) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + instSpec = (((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer) + (1 * BytesPerWord))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + reasonCode = ((/* isIndexableFormat: */ + (instSpec >= (arrayFormat())) + && ((instSpec <= (weakArrayFormat())) + || (instSpec >= (sixtyFourBitIndexableFormat())))) + && (!(instSpec >= (firstCompiledMethodFormat()))) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + /* InterpreterPrimitives>>#primitiveNextInstance */ +static void +primitiveNextInstance(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt actualObj; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt object; + sqInt subsequentObject; + + object = longAt(GIV(stackPointer)); + if (!(((object & (tagMask())) != 0))) { + /* begin instanceAfter: */ + actualObj = object; + classIndex = (longAt((void *)(object))) & (classIndexMask()); + + /* begin isYoungObject: */ + assert(isNonImmediate(object)); + if (oopisLessThan(object, GIV(oldSpaceStart))) { + if (oopisGreaterThanOrEqualToandLessThan(object, ((GIV(eden)).start), GIV(freeStart))) { + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(actualObj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + actualObj = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + actualObj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + if (!(oopisLessThan(actualObj, GIV(freeStart)))) break; + if (classIndex == ((longAt((void *)(actualObj))) & (classIndexMask()))) { + subsequentObject = actualObj; + goto l4; + } + } + actualObj = (oopisGreaterThan(GIV(pastSpaceStart), ((GIV(pastSpace)).start)) + ? objectStartingAt(((GIV(pastSpace)).start)) + : GIV(nilObj)); + } + if (oopisGreaterThanOrEqualToandLessThan(actualObj, ((GIV(pastSpace)).start), GIV(pastSpaceStart))) { + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(actualObj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + actualObj = GIV(pastSpaceStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + actualObj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l2:; + if (!(oopisLessThan(actualObj, GIV(pastSpaceStart)))) break; + if (classIndex == ((longAt((void *)(actualObj))) & (classIndexMask()))) { + subsequentObject = actualObj; + goto l4; + } + } + actualObj = GIV(nilObj); + } + } + + /* it almost never is because usually this is preceded by a send of initialInstanceOf: */ + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(actualObj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + actualObj = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + actualObj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3:; + if (!(oopisLessThan(actualObj, GIV(endOfMemory)))) break; + if (classIndex == ((longAt((void *)(actualObj))) & (classIndexMask()))) { + subsequentObject = actualObj; + goto l4; + } + } + subsequentObject = null; + /* end instanceAfter: */ +l4: + if (subsequentObject) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),subsequentObject); + return; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } +} + + +/* Return the object following the receiver in the heap. Return the + SmallInteger zero when there are no more objects. + */ + + /* InterpreterPrimitives>>#primitiveNextObject */ +static void +primitiveNextObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt instance; + + if ((((longAt(GIV(stackPointer))) & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if ((instance = accessibleObjectAfter(longAt(GIV(stackPointer))))) { + assert(isInMemory(instance)); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),instance); + } + else { + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)0 << 3) | 1)); + } +} + + +/* A placeholder for primitives that haven't been implemented or are being + withdrawn gradually. Just absorbs any arguments and returns the receiver. + */ + + /* InterpreterPrimitives>>#primitiveNoop */ +static void +primitiveNoop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + /* InterpreterPrimitives>>#primitiveNotEqual */ +static void +primitiveNotEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + int result; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver != integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + result = (signedMachineIntegerValueOf(integerReceiver)) != (signedMachineIntegerValueOf(integerArgument)); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (result + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveNotEqualLargeIntegers */ +EXPORT(void) +primitiveNotEqualLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr != integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + +/* is the receiver/first argument not the same object as the (last) + argument?. pop argumentCount because this can be used as a mirror + primitive. + */ +/* is the receiver/first argument the same object as the (last) argument?. + pop argumentCount because this can be used as a mirror primitive. */ + + /* InterpreterPrimitives>>#primitiveNotIdentical */ +static void +primitiveNotIdentical(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt otherObject; + char *sp; + sqInt thisObject; + + thisObject = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + otherObject = longAt(GIV(stackPointer)); + if ((/* isOopForwarded: */ + ((!(otherObject & (tagMask())))) + && ((!((longAt((void *)(otherObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + || ((GIV(argumentCount) > 1) + && (/* isOopForwarded: */ + ((!(thisObject & (tagMask())))) + && ((!((longAt((void *)(thisObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (thisObject != otherObject + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + +/* This primitive is called from Squeak as... + arrayOfRoots uniquelyAccessibleObjects */ +/* This primitive answers an array of the receiver and every object in its + proper tree of subParts (ie, that is not refered to from anywhere else + outside the tree). + */ +/* This primitive could be used to implement the primitiveStoreImageSegment + segment, thanks to a suggestion from Igor Stassenko. Currently it is + used only to debug that primitive. */ + + /* InterpreterPrimitives>>#primitiveObjectsAccessibleFromRoots */ +static void +primitiveObjectsAccessibleFromRoots(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayOfRoots; + sqInt result; + + arrayOfRoots = longAt(GIV(stackPointer)); + + /* Essential type checks */ + if (!(/* isArray: */ + ((!(arrayOfRoots & (tagMask())))) + && (((byteAt((void *)(arrayOfRoots + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Must be indexable pointers */ + result = objectsAccessibleFromRoots(arrayOfRoots); + if (((((result) & 7) == 1)) + && (((result >> 3)) == PrimErrNoMemory)) { + fullGC(); + arrayOfRoots = longAt(GIV(stackPointer)); + result = objectsAccessibleFromRoots(arrayOfRoots); + } + if ((((result) & 7) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = (result >> 3); + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); + } +} + + +/* primitivePathTo: anObject using: stack followWeak: boolean + Answer a path to anObject from the root that does not pass through + the current context */ + + /* InterpreterPrimitives>>#primitivePathToUsing */ +EXPORT(sqInt) +primitivePathToUsing(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt current; + sqInt err; + sqInt fmt; + usqInt freeStartAtStart; + sqInt header; + sqInt headerSqInt; + sqInt i; + sqInt index; + sqInt iSqInt; + sqInt methodField; + sqInt next; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt path; + sqInt sp; + sqInt stack; + sqInt stackp; + sqInt stackSize; + sqInt toDoLimit; + sqInt toDoLimitSqInt; + + err = 0; + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (!(GIV(argumentCount) >= 2)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + if (!(((longAt(GIV(stackPointer))) == GIV(trueObj)) + || ((longAt(GIV(stackPointer))) == GIV(falseObj)))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + stack = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin pathTo:using:followWeak: */ + if (!(/* isArray: */ + ((!(stack & (tagMask())))) + && (((byteAt((void *)(stack + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + err = PrimErrBadArgument; + goto l5; + } + assert(allObjectsUnmarked()); + + /* check no allocations during search */ + freeStartAtStart = GIV(freeStart); + beRootIfOld(stack); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(stack + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(stack + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(stack - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + stackSize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + stackSize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + stackSize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + stackSize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + stackSize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + stackSize = 0; + /* end lengthOf: */ +l1: + + /* begin mark: */ + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(stack))); + byteAtput((void *)(stack + (markBitsByteOffset())),(byteAt((void *)(stack + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* no need. the current context is not reachable from the active process (suspendedContext is nil) + objectMemory mark: self activeProcess. */ + current = GIV(specialObjectsOop); + + /* begin mark: */ + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(current))); + byteAtput((void *)(current + (markBitsByteOffset())),(byteAt((void *)(current + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(current + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(current + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(current - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + index = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + index = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + index = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + index = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + index = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + index = 0; + /* end lengthOf: */ +l2: + stackp = 0; + while (1) { + while (((index -= 1)) >= -1) { + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)current)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)current)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)current)) <= (((usqInt)GIV(pages)))))))) { + next = (index >= 0 + ? fieldofFrame(index, ((char *) current)) + : GIV(nilObj)); + } + else { + if (index >= 0) { + next = (((longAt((void *)(current))) & (classIndexMask())) == ClassMethodContextCompactIndex + ? fieldOrSenderFPofContext(index, current) + : longAt((void *)((current + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))))); + } + else { + next = fetchClassOfNonImm(current); + } + } + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)next)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)next)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)next)) <= (((usqInt)GIV(pages)))))))) { + assert(isFrameonPage(((char *) next), stackPageFor(((char *) next)))); + } + else { + if (next >= heapBase) { + assert(checkOkayOop(next)); + } + } + if (next == (longAt(GIV(stackPointer) + (2 * BytesPerWord)))) { + assert(freeStartAtStart == (GIV(freeStart))); + unmarkAfterPathTo(); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(stackp, stack, current)); + assert(isNonImmediate(stack)); + if (oopisGreaterThanOrEqualTo(stack, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(current & (tagMask())))) + && (oopisLessThan(current, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(stack + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(stack); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(stackp) << (shiftForWord()))))),current); + pruneStackstackp(stack, stackp); + err = 0; + goto l5; + } + if (((!(next & (smallIntegerTag())))) + && ((/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)next)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)next)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)next)) <= (((usqInt)GIV(pages))))))) + ? !(frameIsMarked(next)) + : (next >= heapBase) + && ((!(((byteAt((void *)(next + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) + && (((/* isPointers: */ + ((!(next & (tagMask())))) + && (((byteAt((void *)(next + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + || (((byteAt((void *)(next + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((longAt(GIV(stackPointer))) == GIV(trueObj)) + || (!(isWeakNonImm(next))))))))) { + if ((stackp + 2) > stackSize) { + assert(freeStartAtStart == (GIV(freeStart))); + unmarkAfterPathTo(); + + /* begin nilFieldsOf: */ + toDoLimit = ((assert((classIndexOf(stack)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(stack + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(stack - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(i, stack, GIV(nilObj))); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } + err = PrimErrBadIndex; + goto l5; + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(stackp, stack, current)); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(stackp) << (shiftForWord()))))),current); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(stackp + 1, stack, (((usqInt)index << 3) | 1))); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)((stackp + 1)) << (shiftForWord()))))),(((usqInt)index << 3) | 1)); + stackp += 2; + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)(((char *) next)))) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)(((char *) next)))) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)(((char *) next)))) <= (((usqInt)GIV(pages)))))))) { + /* begin markFrame: */ + methodField = longAt((void *)(next + FoxMethod)); + if ((((usqInt)methodField)) < (startOfMemory())) { + longAtput((void *)(next + FoxMethod),methodField | 4); + } + else { + longAtput((void *)(next + FoxIFrameFlags),(longAt((void *)(next + FoxIFrameFlags))) | 2); + } + index = CtxtTempFrameStart + (stackPointerIndexForFrame(((char *) next))); + } + else { + /* begin mark: */ + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(next))); + byteAtput((void *)(next + (markBitsByteOffset())),(byteAt((void *)(next + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(next + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(next))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((next + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l3; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(next))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l3: + index = ((usqInt) (CtxtTempFrameStart + contextSize)); + goto l4; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(next)) > (isForwardedObjectClassIndexPun())); + index = (((numSlotsUsqInt = byteAt((void *)(next + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(next - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + goto l4; + } + if (fmt == (forwardedFormat())) { + index = 1; + goto l4; + } + if (fmt < (firstCompiledMethodFormat())) { + index = 0; + goto l4; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(next)); + headerSqInt = longAt((void *)((next + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + index = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l4:; + } + current = next; + } + } + if (current == GIV(specialObjectsOop)) { + assert(freeStartAtStart == (GIV(freeStart))); + unmarkAfterPathTo(); + + /* begin nilFieldsOf: */ + toDoLimitSqInt = ((assert((classIndexOf(stack)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(stack + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(stack - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) - 1; + for (iSqInt = 0; iSqInt <= toDoLimitSqInt; iSqInt += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(iSqInt, stack, GIV(nilObj))); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord()))))),GIV(nilObj)); + } + err = PrimErrNotFound; + goto l5; + } + index = ((longAt((void *)((stack + BaseHeaderSize) + ((((usqInt)((stackp - 1)) << (shiftForWord()))))))) >> 3); + current = longAt((void *)((stack + BaseHeaderSize) + ((((usqInt)((stackp - 2)) << (shiftForWord())))))); + stackp -= 2; + } + /* end pathTo:using:followWeak: */ +l5: + if (err) { + return (GIV(primFailCode) = err); + } + path = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),path); + return 0; +} + + /* InterpreterPrimitives>>#primitivePerformInSuperclass */ +static void +primitivePerformInSuperclass(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt argumentArray; + usqInt arraySize; + sqInt currentClass; + sqInt delta; + sqInt index; + sqInt lookupClass; + usqInt numSlots; + sqInt objOop; + sqInt offset; + sqInt performArgCount; + sqInt rcvr; + usqInt savedNewMethod; + sqInt selector; + sqInt tagBits; + sqInt top; + + rcvr = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + lookupClass = longAt(GIV(stackPointer)); + if (GIV(argumentCount) != 3) { + if (GIV(argumentCount) != 4) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + } + + /* e.g. object:perform:withArguments:inClass: */ + currentClass = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)); + while (currentClass != lookupClass) { + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOop); + } + currentClass = objOop; + if (currentClass == GIV(nilObj)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + selector = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + argumentArray = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin primitiveObject:perform:withArguments:lookedUpIn: */ + if (!(/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + + /* Check if number of arguments is reasonable; MaxNumArgs isn't available + so just use LargeContextSize */ + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (arraySize > (LargeContextSlots - CtxtTempFrameStart)) { + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + goto l1; + } + performArgCount = GIV(argumentCount); + + /* Recursion check: + | a | + a := Array with: #perform:withArguments: with: nil. + a at: 2 put: a. + a perform: a first withArguments: a. + | a | + a := Array with: #perform:withArguments: with: (Array with: #perform:withArguments: with: nil). + a last at: 2 put: a. + a perform: a first withArguments: a */ + if (!GIV(performWithArgumentsRecursionGuard)) { + GIV(performWithArgumentsRecursionGuard) = (lookupClass + ? longAt(GIV(stackPointer) + (1 * BytesPerWord)) + : longAt(GIV(stackPointer))); + } + + /* Push newMethod to save it in case of failure, + then push the actual receiver and the args in the array. */ + savedNewMethod = GIV(newMethod); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* Copy the arguments to the stack, in case of MNU, and lookup */ + for (index = 1; index <= arraySize; index += 1) { + arg = longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord())))))); + if (arg == GIV(performWithArgumentsRecursionGuard)) { + GIV(performWithArgumentsRecursionGuard) = null; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),arg); + } + if (!GIV(performWithArgumentsRecursionGuard)) { + /* begin pop: */ + GIV(stackPointer) += (2 + arraySize) * BytesPerWord; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l1; + } + GIV(argumentCount) = arraySize; + GIV(messageSelector) = selector; + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + if (printOnTrace()) { + printActivationNameForSelectorstartClass(GIV(messageSelector), (lookupClass + ? lookupClass + : /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)))); + cr(); + } + findNewMethodInClassTag((lookupClass + ? classTagForClass(lookupClass) + : /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())))); + + /* Only test CompiledMethods for argument count - any other objects playacting as CMs will have to take their chances */ + if ((/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && ((argumentCountOf(GIV(newMethod))) != GIV(argumentCount))) { + /* Restore the state by popping the array entries, the actual receiver and the saved + newMethod, leaving the selector and array, and fail. N.B. If an MNU has happened + then argumentCount will match newMethod, so this code will not be reached. + These asserts check that an MNU has not occurred if the argumentCount doesn't match the newMethod. */ + assert((stackTop()) == ((arraySize == 0 + ? rcvr + : fetchPointerofObject(arraySize - 1, argumentArray)))); + assert(GIV(argumentCount) == arraySize); + + /* begin pop: */ + GIV(stackPointer) += (arraySize + 1) * BytesPerWord; + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(newMethod) = top; + + /* Must reset primitiveFunctionPointer for checkForAndFollowForwardedPrimitiveState */ + GIV(argumentCount) = performArgCount; + primitiveFunctionPointer = (lookupClass + ? primitivePerformInSuperclass + : primitivePerformWithArgs); + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + goto l1; + } + + /* Cannot fail this primitive from here-on. Slide the actual receiver and arguments down + to replace the perform arguments and saved newMethod and then execute the new + method. Use argumentCount not arraySize because an MNU may have changed it. */ + + /* +2 = receiver + saved newMethod */ + delta = BytesPerWord * (performArgCount + 2); + for (offset = (GIV(argumentCount) * BytesPerWord); offset >= 0; offset += (-BytesPerWord)) { + longAtput((GIV(stackPointer) + offset) + delta,longAt(GIV(stackPointer) + offset)); + } + + /* Part of the recursion guard above. To ensure we fail in the original method, restore + newMethod, and don't do the activateNewMethod implicit in executeNewMethod. */ + if ((primitiveFunctionPointer == primitivePerformWithArgs) + || (primitiveFunctionPointer == primitivePerformInSuperclass)) { + /* This should of course be a tail call, which could be done via setjmp/longjmp. + But this is vanity code. After all how often is a recursive invocation of + primitivePerformWithArgs et al made? */ + GIV(newMethod) = savedNewMethod; + GIV(argumentCount) = performArgCount; + + /* begin pop: */ + GIV(stackPointer) += (performArgCount + 2) * BytesPerWord; + + /* prevent inlining... */ + slowPrimitiveResponse(); + GIV(performWithArgumentsRecursionGuard) = null; + goto l1; + } + GIV(performWithArgumentsRecursionGuard) = null; + + /* begin pop: */ + GIV(stackPointer) += (performArgCount + 2) * BytesPerWord; + + /* prevent inlining... */ + executeNewMethod(); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + /* end primitiveObject:perform:withArguments:lookedUpIn: */ +l1:; +} + + /* InterpreterPrimitives>>#primitivePerformWithArgs */ +static void +primitivePerformWithArgs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt actualReceiver; + sqInt arg; + sqInt argumentArray; + usqInt arraySize; + sqInt delta; + sqInt index; + usqInt numSlots; + sqInt offset; + sqInt performArgCount; + usqInt savedNewMethod; + sqInt selector; + sqInt tagBits; + sqInt top; + + actualReceiver = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + selector = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + argumentArray = longAt(GIV(stackPointer)); + + /* begin primitiveObject:perform:withArguments:lookedUpIn: */ + if (!(/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + + /* Check if number of arguments is reasonable; MaxNumArgs isn't available + so just use LargeContextSize */ + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (arraySize > (LargeContextSlots - CtxtTempFrameStart)) { + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + goto l1; + } + performArgCount = GIV(argumentCount); + + /* Recursion check: + | a | + a := Array with: #perform:withArguments: with: nil. + a at: 2 put: a. + a perform: a first withArguments: a. + | a | + a := Array with: #perform:withArguments: with: (Array with: #perform:withArguments: with: nil). + a last at: 2 put: a. + a perform: a first withArguments: a */ + if (!GIV(performWithArgumentsRecursionGuard)) { + GIV(performWithArgumentsRecursionGuard) = longAt(GIV(stackPointer)); + } + + /* Push newMethod to save it in case of failure, + then push the actual receiver and the args in the array. */ + savedNewMethod = GIV(newMethod); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),actualReceiver); + + /* Copy the arguments to the stack, in case of MNU, and lookup */ + for (index = 1; index <= arraySize; index += 1) { + arg = longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord())))))); + if (arg == GIV(performWithArgumentsRecursionGuard)) { + GIV(performWithArgumentsRecursionGuard) = null; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),arg); + } + if (!GIV(performWithArgumentsRecursionGuard)) { + /* begin pop: */ + GIV(stackPointer) += (2 + arraySize) * BytesPerWord; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l1; + } + GIV(argumentCount) = arraySize; + GIV(messageSelector) = selector; + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), /* fetchClassTagOf: */ + ((tagBits = actualReceiver & (tagMask())) + ? tagBits + : (longAt((void *)(actualReceiver))) & (classIndexMask()))); + if (printOnTrace()) { + printActivationNameForSelectorstartClass(GIV(messageSelector), /* fetchClassOf: */ + ((tagBits = actualReceiver & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(actualReceiver))); + cr(); + } + findNewMethodInClassTag(/* fetchClassTagOf: */ + ((tagBits = actualReceiver & (tagMask())) + ? tagBits + : (longAt((void *)(actualReceiver))) & (classIndexMask()))); + + /* Only test CompiledMethods for argument count - any other objects playacting as CMs will have to take their chances */ + if ((/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && ((argumentCountOf(GIV(newMethod))) != GIV(argumentCount))) { + /* Restore the state by popping the array entries, the actual receiver and the saved + newMethod, leaving the selector and array, and fail. N.B. If an MNU has happened + then argumentCount will match newMethod, so this code will not be reached. + These asserts check that an MNU has not occurred if the argumentCount doesn't match the newMethod. */ + assert((stackTop()) == ((arraySize == 0 + ? actualReceiver + : fetchPointerofObject(arraySize - 1, argumentArray)))); + assert(GIV(argumentCount) == arraySize); + + /* begin pop: */ + GIV(stackPointer) += (arraySize + 1) * BytesPerWord; + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(newMethod) = top; + + /* Must reset primitiveFunctionPointer for checkForAndFollowForwardedPrimitiveState */ + GIV(argumentCount) = performArgCount; + primitiveFunctionPointer = primitivePerformWithArgs; + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + goto l1; + } + + /* Cannot fail this primitive from here-on. Slide the actual receiver and arguments down + to replace the perform arguments and saved newMethod and then execute the new + method. Use argumentCount not arraySize because an MNU may have changed it. */ + + /* +2 = receiver + saved newMethod */ + delta = BytesPerWord * (performArgCount + 2); + for (offset = (GIV(argumentCount) * BytesPerWord); offset >= 0; offset += (-BytesPerWord)) { + longAtput((GIV(stackPointer) + offset) + delta,longAt(GIV(stackPointer) + offset)); + } + + /* Part of the recursion guard above. To ensure we fail in the original method, restore + newMethod, and don't do the activateNewMethod implicit in executeNewMethod. */ + if ((primitiveFunctionPointer == primitivePerformWithArgs) + || (primitiveFunctionPointer == primitivePerformInSuperclass)) { + /* This should of course be a tail call, which could be done via setjmp/longjmp. + But this is vanity code. After all how often is a recursive invocation of + primitivePerformWithArgs et al made? */ + GIV(newMethod) = savedNewMethod; + GIV(argumentCount) = performArgCount; + + /* begin pop: */ + GIV(stackPointer) += (performArgCount + 2) * BytesPerWord; + + /* prevent inlining... */ + slowPrimitiveResponse(); + GIV(performWithArgumentsRecursionGuard) = null; + goto l1; + } + GIV(performWithArgumentsRecursionGuard) = null; + + /* begin pop: */ + GIV(stackPointer) += (performArgCount + 2) * BytesPerWord; + + /* prevent inlining... */ + executeNewMethod(); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + /* end primitiveObject:perform:withArguments:lookedUpIn: */ +l1:; +} + + /* InterpreterPrimitives>>#primitivePinnedNew */ +static void +primitivePinnedNew(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt classObj; + sqInt err; + int hash; + sqInt instSpec; + sqInt newObj; + sqInt numSlots; + sqInt obj; + usqInt p; + sqInt reasonCode; + usqInt toDoLimit; + + + /* For the mirror prims check that the class obj is actually a valid class. + Allocate a new fixed-size instance. Fail if the allocation would leave + less than lowSpaceThreshold bytes free. This *will not* cause a GC :-) */ + classObj = longAt(GIV(stackPointer)); + + /* begin inOldSpaceInstantiatePinnedClass: */ + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + if (!(/* isFixedSizePointerFormat: */ + (instSpec <= (nonIndexablePointerFormat())) + || (instSpec == (ephemeronFormat())))) { + obj = null; + goto l1; + } + + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + obj = null; + goto l1; + } + numSlots = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((((usqInt)(numSlots) << (shiftForWord())))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpec, classIndex); + if (newObj) { + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),GIV(nilObj)); + } + } + obj = newObj; + /* end inOldSpaceInstantiatePinnedClass: */ +l1: + if (obj) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + reasonCode = (isFixedSizePointerFormat((((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + +/* Allocate a new pinned indexable instance. Fail if the allocation would + leave less than lowSpaceThreshold bytes free. + */ + + /* InterpreterPrimitives>>#primitivePinnedNewWithArg */ +static void +primitivePinnedNewWithArg(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt classObj; + sqInt err; + sqInt fillValue; + int hash; + sqInt instSpec; + sqInt instSpecSqInt; + sqInt newObj; + usqInt numSlots; + sqInt obj; + usqInt p; + sqInt reasonCode; + usqIntptr_t size; + usqInt toDoLimit; + sqInt value; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + size = null; + goto l1; + } + size = value; + goto l1; + } + + /* don't inline the rare case */ + size = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* positiveMachineIntegerValueOf: succeeds only for non-negative integers. */ + classObj = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin inOldSpaceInstantiatePinnedClass:indexableSize: */ + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpecSqInt = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + fillValue = 0; + switch (instSpecSqInt) { + case arrayFormat(): + numSlots = size; + fillValue = GIV(nilObj); + break; + case indexablePointersFormat(): + case weakArrayFormat(): + numSlots = (classFormat & ((1U << (fixedFieldsFieldWidth())) - 1)) + size; + fillValue = GIV(nilObj); + break; + case sixtyFourBitIndexableFormat(): + numSlots = size; + break; + case firstLongFormat(): + if ((classIndex == ClassFloatCompactIndex) + && (size != 2)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + obj = null; + goto l2; + } + numSlots = (size + 1) / 2; + instSpecSqInt += size & 1; + break; + case firstShortFormat(): + numSlots = (size + 3) / 4; + instSpecSqInt += (4 - size) & 3; + break; + case firstByteFormat(): + numSlots = (size + 7) / 8; + instSpecSqInt += (8 - size) & 7; + break; + default: + if ((size != 0) + || (instSpecSqInt > 5 /* lastPointerFormat */)) { + obj = null; + goto l2; + } + numSlots = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + fillValue = GIV(nilObj); + } + + /* non-indexable + Some Squeak images include funky fixed subclasses of abstract variable + superclasses. e.g. DirectoryEntry as a subclass of ArrayedCollection. + The (Threaded)FFIPlugin expects to be able to instantiate ExternalData via + this method. + Hence allow fixed classes to be instantiated here iff nElements = 0. */ + if (!classIndex) { + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + obj = null; + goto l2; + } + } + if (numSlots > (0x10000000000LL)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + obj = null; + goto l2; + } + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpecSqInt, classIndex); + if (newObj) { + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),fillValue); + } + } + obj = newObj; + /* end inOldSpaceInstantiatePinnedClass:indexableSize: */ +l2: + if (obj) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + instSpec = (((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer) + (1 * BytesPerWord))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + reasonCode = ((/* isIndexableFormat: */ + (instSpec >= (arrayFormat())) + && ((instSpec <= (weakArrayFormat())) + || (instSpec >= (sixtyFourBitIndexableFormat())))) + && (!(instSpec >= (firstCompiledMethodFormat()))) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + +/* Primitive. Answer the last primitive method sampled by the profiler. */ + + /* InterpreterPrimitives>>#primitiveProfilePrimitive */ +EXPORT(sqInt) +primitiveProfilePrimitive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = (!(GIV(profileMethod)) + ? GIV(nilObj) + : GIV(profileMethod)); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return 0; +} + + +/* Primitive. Answer the last sample taken by the profiler, or nil if the + profiler isn't active. + See also primitiveProfileStart. + */ + + /* InterpreterPrimitives>>#primitiveProfileSample */ +EXPORT(sqInt) +primitiveProfileSample(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = (!(GIV(profileProcess)) + ? GIV(nilObj) + : GIV(profileProcess)); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return 0; +} + + +/* Primitive. Begin profiling execution by using the high-resolution clock + instead of a time-based process + (which is limited to timing resolution and triggers off the same signal + that many of the processes being + profiled trigger off leading to consistently wrong results). The argument + is the number of ticks of the + high-resolution clock to elapse before taking a sample. The sample is + stored in the profileProcess and + profileMethod iVars which can be retrieved via primitiveProfileSample and + primitiveProfilePrimitive. So also void the sample iVars. Once a sample is + taken, the semaphore installed via + primitiveProfileSemaphore is signalled. If the argument is less or equal + to zero, profiling is disabled. */ + + /* InterpreterPrimitives>>#primitiveProfileStart */ +EXPORT(sqInt) +primitiveProfileStart(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt deltaTicks; + + deltaTicks = longAt(GIV(stackPointer)); + if ((((deltaTicks) & 7) == 1)) { + GIV(deferProfileCheckForVNCS) = 0; + deltaTicks = (deltaTicks >> 3); + GIV(nextProfileTick) = (deltaTicks > 0 + ? (ioHighResClock()) + deltaTicks + : 0); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return 0; + } + return (GIV(primFailCode) = PrimErrBadArgument); +} + + /* InterpreterPrimitives>>#primitiveQuit */ +static void +primitiveQuit(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + ioExitWithErrorCode((GIV(argumentCount) == 1 + ? ((longAt(GIV(stackPointer))) >> 3) + : 0)); +} + + +/* Rounds negative results towards zero. */ + + /* InterpreterPrimitives>>#primitiveQuo */ +static void +primitiveQuo(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArg; + sqInt integerPointer; + sqInt integerRcvr; + sqInt integerResult; + + integerResult = 0; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + integerRcvr = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerRcvr = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + integerArg = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArg = 0; + } + + /* begin success: */ + if (!integerArg) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + integerResult = /* quot:ient: */ + (integerRcvr > 0 + ? (integerArg > 0 + ? integerRcvr / integerArg + : 0 - (integerRcvr / (0 - integerArg))) + : (integerArg > 0 + ? 0 - ((0 - integerRcvr) / integerArg) + : (0 - integerRcvr) / (0 - integerArg))); + } + + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)(integerResult)) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerResult << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveQuoLargeIntegers */ +EXPORT(void) +primitiveQuoLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (!b) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return; + } + result = a / b; + oopResult = magnitude64BitIntegerForneg(result, bIsNegative != aIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveRemLargeIntegers */ +EXPORT(void) +primitiveRemLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (!b) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return; + } + result = a % b; + oopResult = magnitude64BitIntegerForneg(result, aIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* The character scanner primitive + primScanCharactersFrom: startIndex to: stopIndex in: sourceString rightX: + rightX stopConditions: stops kern: kernDelta + This is the inner loop of measurement and scanning for text display. March + through sourceString from startIndex to stopIndex. + If any character is flagged with a non-nil entry in stops, then return the + corresponding value. Determine width of each character + from xTable, indexed by map. If destX would exceed rightX, then return + stops at: 258. Advance destX by the width of the + character. If stopIndex has been reached, then return stops at: 257. + + Receiver inst vars: + destX horizontal position for next character (distance from left of + composition area) + lastIndex the Integer index of next character to be processed in the + sourceString argument + xTable an array mapping character code to glyph x coordinate in a form + of glyphs + map an array mapping character code to glyph position. + + Failure codes: + PrimErrBadArgument one of the indices is not a SmallInteger, or the sting + argument is not a byte string, or the stops array is too small. + PrimErrBadReceiver the receiver has less than four inst vars or any of + scanDestX & scanLastIndex are not SmallIntegers, or the scanXTable or + scanMap are not arrays of the right size + PrimErrBadIndex either a glyph index in scanMap or a start index in + scanXTable are not SmallIntegers + PrimErrLimitExceeded arithmetic on scanDestX has wrapped around to become + negative + */ + + /* InterpreterPrimitives>>#primitiveScanCharacters */ +static void +primitiveScanCharacters(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ascii; + sqInt glyphIndex; + sqInt kernDelta; + sqInt maxGlyph; + sqInt nextDestX; + sqInt rcvr; + sqInt scanDestX; + sqInt scanLastIndex; + sqInt scanMap; + sqInt scanRightX; + sqInt scanStartIndex; + sqInt scanStopIndex; + sqInt scanXTable; + sqInt sourceString; + sqInt sourceX; + sqInt sourceX2; + char *sp1; + sqInt stopReason; + sqInt stops; + + if (GIV(argumentCount) != 6) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + + /* Load the receiver and arguments */ + kernDelta = longAt(GIV(stackPointer)); + stops = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + scanRightX = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + sourceString = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + scanStopIndex = longAt(GIV(stackPointer) + (4 * BytesPerWord)); + scanStartIndex = longAt(GIV(stackPointer) + (5 * BytesPerWord)); + rcvr = longAt(GIV(stackPointer) + (6 * BytesPerWord)); + if (!(((((kernDelta) & 7) == 1)) + && (((((scanRightX) & 7) == 1)) + && (((((scanStopIndex) & 7) == 1)) + && ((((scanStartIndex) & 7) == 1)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + kernDelta = (kernDelta >> 3); + scanRightX = (scanRightX >> 3); + scanStopIndex = (scanStopIndex >> 3); + scanStartIndex = (scanStartIndex >> 3); + + /* check argument type and range and rcvr */ + if (!((/* isArray: */ + ((!(stops & (tagMask())))) + && (((byteAt((void *)(stops + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((slotSizeOf(stops)) >= 258) + && ((/* isBytes: */ + ((!(sourceString & (tagMask())))) + && (((byteAt((void *)(sourceString + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((scanStartIndex > 0) + && ((scanStopIndex > 0) + && (scanStopIndex <= (byteSizeOf(sourceString))))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (!((/* isPointers: */ + ((!(rcvr & (tagMask())))) + && (((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((slotSizeOf(rcvr)) >= 4))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* Check required rcvr instVars */ + scanDestX = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord())))); + scanLastIndex = longAt((void *)((rcvr + BaseHeaderSize) + (1U << (shiftForWord())))); + scanXTable = longAt((void *)((rcvr + BaseHeaderSize) + (2U << (shiftForWord())))); + scanMap = longAt((void *)((rcvr + BaseHeaderSize) + (3U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(scanXTable & (tagMask())))) + && (((byteAt((void *)(scanXTable + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((/* isArray: */ + ((!(scanMap & (tagMask())))) + && (((byteAt((void *)(scanMap + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((slotSizeOf(scanMap)) == 0x100) + && (((((scanDestX) & 7) == 1)) + && ((((scanLastIndex) & 7) == 1))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if (((scanDestX = (scanDestX >> 3))) < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + scanLastIndex = (scanLastIndex >> 3); + maxGlyph = (slotSizeOf(scanXTable)) - 2; + + /* Okay, here we go. We have eliminated nearly all failure + conditions, to optimize the inner fetches. */ + scanLastIndex = scanStartIndex; + while (scanLastIndex <= scanStopIndex) { + /* Known to be okay since scanStartIndex > 0 and scanStopIndex <= sourceString size */ + ascii = byteAt((void *)((sourceString + BaseHeaderSize) + (scanLastIndex - 1))); + + /* Known to be okay since stops size >= 258 */ + if (!(((stopReason = longAt((void *)((stops + BaseHeaderSize) + ((((usqInt)(ascii) << (shiftForWord())))))))) == GIV(nilObj))) { + if (!(scanDestX >= 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrLimitExceeded; + return; + } + storeIntegerofObjectwithValue(0, rcvr, scanDestX); + storeIntegerofObjectwithValue(1, rcvr, scanLastIndex); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),stopReason); + return; + } + + /* Store everything back and get out of here since some stop condition needs to be checked + Known to be okay since scanMap size = 256 */ + glyphIndex = longAt((void *)((scanMap + BaseHeaderSize) + ((((usqInt)(ascii) << (shiftForWord())))))); + + /* fail if the glyphIndex is out of range */ + if (!(((((glyphIndex) & 7) == 1)) + && ((((glyphIndex = (glyphIndex >> 3))) >= 0) + && (glyphIndex <= maxGlyph)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + sourceX = longAt((void *)((scanXTable + BaseHeaderSize) + ((((usqInt)(glyphIndex) << (shiftForWord())))))); + sourceX2 = longAt((void *)((scanXTable + BaseHeaderSize) + ((((usqInt)((glyphIndex + 1)) << (shiftForWord())))))); + + /* Above may fail if non-integer entries in scanXTable */ + if (!(((((sourceX) & 7) == 1)) + && ((((sourceX2) & 7) == 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + nextDestX = (scanDestX + ((sourceX2 >> 3))) - ((sourceX >> 3)); + if (nextDestX > scanRightX) { + /* Store everything back and get out of here since we got to the right edge */ + storeIntegerofObjectwithValue(0, rcvr, scanDestX); + storeIntegerofObjectwithValue(1, rcvr, scanLastIndex); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((sp1 = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),longAt((void *)((stops + BaseHeaderSize) + ((((usqInt)((CrossedX - 1)) << (shiftForWord()))))))); + GIV(stackPointer) = sp1; + return; + } + if (!((nextDestX >= 0) + && (((scanDestX = nextDestX + kernDelta)) >= 0))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrLimitExceeded; + return; + } + scanLastIndex += 1; + } + storeIntegerofObjectwithValue(0, rcvr, scanDestX); + storeIntegerofObjectwithValue(1, rcvr, scanStopIndex); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),longAt((void *)((stops + BaseHeaderSize) + ((((usqInt)((EndOfRun - 1)) << (shiftForWord()))))))); +} + + +/* Return a SmallInteger indicating the current depth of the OS screen. + Negative values are used to imply LSB type pixel format an there is some + support in the VM for handling either MSB or LSB + */ + + /* InterpreterPrimitives>>#primitiveScreenDepth */ +EXPORT(sqInt) +primitiveScreenDepth(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt depth; + + depth = ioScreenDepth(); + if (GIV(primFailCode)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),(((usqInt)depth << 3) | 1)); + return 0; +} + + +/* Answer a float indicating the current scale factor for pixels of the + Smalltalk window. + */ + + /* InterpreterPrimitives>>#primitiveScreenScaleFactor */ +EXPORT(sqInt) +primitiveScreenScaleFactor(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double factor; + char *sp; + + factor = ioScreenScaleFactor(); + if (!GIV(primFailCode)) { + /* begin methodReturnFloat: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),floatObjectOf(factor)); + GIV(stackPointer) = sp; + } + return 0; +} + + +/* Answer a point indicating the current size of the Smalltalk window. + Currently there is a limit of 65535 in each direction because the + point is encoded into a single 32bit value in the image header. + This might well become a problem one day */ + + /* InterpreterPrimitives>>#primitiveScreenSize */ +static void +primitiveScreenSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newObj; + usqInt numBytes; + sqInt oop; + usqInt pointResult; + sqInt pointWord; + sqInt xValue; + sqInt yValue; + + pointWord = ioScreenSize(); + xValue = (((usqInt)(pointWord)) >> 16) & 0xFFFF; + yValue = pointWord & 0xFFFF; + + /* begin makePointwithxValue:yValue: */ + /* begin eeInstantiatePoint */ + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert(((YIndex + 1) >= 0) + && ((knownClassAtIndex(ClassPointCompactIndex)) != GIV(nilObj))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(knownClassAtIndex(ClassPointCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert((YIndex + 1) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (((YIndex + 1) < 1 + ? 8 /* allocationUnit */ + : (YIndex + 1) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + pointResult = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) (YIndex + 1))) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + ClassPointCompactIndex); + GIV(freeStart) += numBytes; + pointResult = newObj; + /* end eeInstantiatePoint */ +l1: + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pointResult)) + && (!(isForwarded(pointResult)))); + assert(validStorePointerUncheckedArgs(XIndex, pointResult, (((usqInt)xValue << 3) | 1))); + longAtput((void *)((pointResult + BaseHeaderSize) + ((((usqInt)(XIndex) << (shiftForWord()))))),(((usqInt)xValue << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pointResult)) + && (!(isForwarded(pointResult)))); + assert(validStorePointerUncheckedArgs(YIndex, pointResult, (((usqInt)yValue << 3) | 1))); + longAtput((void *)((pointResult + BaseHeaderSize) + ((((usqInt)(YIndex) << (shiftForWord()))))),(((usqInt)yValue << 3) | 1)); + oop = pointResult; + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Return the number of seconds since January 1, 1901 as an integer. */ + + /* InterpreterPrimitives>>#primitiveSecondsClock */ +static void +primitiveSecondsClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = (((usqInt)((((usqInt)(ioSecondsNow()))) & 0xFFFFFFFFU) << 3) | 1); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Ask the GUI to set the requested display mode. + See DisplayScreen class depth:width:height:fullscreen: */ + + /* InterpreterPrimitives>>#primitiveSetDisplayMode */ +static void +primitiveSetDisplayMode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt d; + sqInt fsFlag; + sqInt h; + sqInt integerPointer; + sqInt okay; + char *sp; + sqInt w; + + /* begin booleanValueOf: */ + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + fsFlag = 1; + goto l1; + } + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + fsFlag = 0; + goto l1; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + fsFlag = null; + /* end booleanValueOf: */ +l1: + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + h = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + h = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + w = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + w = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + d = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + d = 0; + } + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + okay = ioSetDisplayMode(w, h, d, fsFlag); + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrOperationFailed; + return; + } + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (okay + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; +} + + +/* On platforms that support it, set full-screen mode to the value of the + boolean argument. + */ + + /* InterpreterPrimitives>>#primitiveSetFullScreen */ +static void +primitiveSetFullScreen(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt argOop; + + argOop = longAt(GIV(stackPointer)); + if (argOop == GIV(trueObj)) { + ioSetFullScreen(1); + } + else { + if (argOop == GIV(falseObj)) { + ioSetFullScreen(0); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + if (!GIV(primFailCode)) { + /* begin setFullScreenFlag: */ + imageHeaderFlags = (argOop == GIV(trueObj) + ? imageHeaderFlags | 1 + : ((imageHeaderFlags | 1) - 1)); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } +} + + /* InterpreterPrimitives>>#primitiveSetImmutability */ +#if IMMUTABILITY +static void +primitiveSetImmutability(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt processLists; + sqInt rcvr; + sqInt scheduler; + char *sp; + sqInt wasImmutable; + + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if ((GIV(argumentCount) > 1) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* fail if forwarded; the retry machinery will fix things up. */ + wasImmutable = +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ; + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + /* begin canBeImmutable: */ + assert(isNonImmediate(rcvr)); + + /* For now we fail the primitive for contexts to we ensure there are no immutable contexts. + Later we can consider having immutable contexts and send cannotReturn callback + when returning to an immutable context. That would mean that setting a context + to immutable would require a divorce and returns to immutable context are + necessarily across stack pages */ + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + goto l1; + } + + /* Weak structures can't be immutable */ + + /* begin isEphemeron: */ + assert(isNonImmediate(rcvr)); + format = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + if (format == (ephemeronFormat())) { + goto l1; + } + + /* begin isWeakNonImm: */ + format = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + if (format == (weakArrayFormat())) { + goto l1; + } + + /* No clue what is going on for semaphores so they can't be immutable */ + if (((longAt((void *)(rcvr))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))) { + goto l1; + } + + /* Simple version of process management: we forbid Process and LinkedList instances to be immutable + as well as the Processor and the array of activeProcess */ + + /* begin fetchPointer:ofObject: */ + scheduler = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + processLists = longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + if (rcvr == scheduler) { + goto l1; + } + if (rcvr == processLists) { + goto l1; + } + + /* Is it a linkedList ? */ + if ((classIndexOf(longAt((void *)((processLists + BaseHeaderSize) + (1U << (shiftForWord())))))) == ((longAt((void *)(rcvr))) & (classIndexMask()))) { + goto l1; + } + + /* is it a Process ? */ + if ((classIndexOf(longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))))) == ((longAt((void *)(rcvr))) & (classIndexMask()))) { + goto l1; + } + + /* The rest of the code is relative to process management: the Processor (the active + process scheduler) can't be immutable, as well as all the objects relative to Process management + scheduler := self fetchPointer: ValueIndex ofObject: (self splObj: SchedulerAssociation). + processLists := objectMemory fetchPointer: ProcessListsIndex ofObject: scheduler. + ((objectMemory formatOf: oop) = objectMemory nonIndexablePointerFormat) + ifFalse: + [ (objectMemory isArrayNonImm: oop) ifFalse: [^ true]. + ^ (oop = processLists) not ]. + (objectMemory numSlotsOf: oop) >= 2 ifFalse: [^ true]. + + is the oop the scheduler itself ? + oop = scheduler ifTrue: [^ false]. + 1 to: (objectMemory numSlotsOf: processLists) do: [ :i | + + is the oop one of the linked lists ? + (list := processLists at: i) = oop ifTrue: [^ false]. + + is the oop one of the runnable process ? + first := objectMemory fetchPointer: FirstLinkIndex ofObject: list. + first = objectMemory nilObject ifFalse: + [ last := objectMemory fetchPointer: LastLinkIndex ofObject: list. + link := first. + [ link = last ] whileFalse: + [ link = oop ifTrue: [^ false]. + link := objectMemory fetchPointer: NextLinkIndex ofObject: link. ] ] ]. */ + goto l2; +l1: + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + return; +l2: + + /* begin setIsImmutableOf:to: */ + longAtput((void *)(rcvr),(longAt((void *)(rcvr))) | (1U << (immutableBitShift()))); + } + else { + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + /* begin setIsImmutableOf:to: */ + longAtput((void *)(rcvr),(longAt((void *)(rcvr))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (wasImmutable + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; +} +#endif /* IMMUTABILITY */ + + +/* Set the user interrupt keycode. The keycode is an integer whose encoding + is described in the comment for primitiveKbdNext. + */ + + /* InterpreterPrimitives>>#primitiveSetInterruptKey */ +static void +primitiveSetInterruptKey(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerPointer; + sqInt keycode; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + keycode = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + keycode = 0; + } + if (!GIV(primFailCode)) { + /* begin setInterruptKeycode: */ + GIV(interruptKeycode) = keycode; + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } +} + + +/* Primitive. Set the VM's log directory */ + + /* InterpreterPrimitives>>#primitiveSetLogDirectory */ +EXPORT(sqInt) +primitiveSetLogDirectory(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + usqInt numBytes; + usqInt numSlots; + sqInt stringOop; + sqInt sz; + + stringOop = longAt(GIV(stackPointer)); + if (!(/* isBytes: */ + ((!(stringOop & (tagMask())))) + && (((byteAt((void *)(stringOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + + /* begin byteSizeOf: */ + if (((stringOop & (tagMask())) != 0)) { + sz = 0; + goto l1; + } + + /* begin numBytesOf: */ + fmt = (byteAt((void *)(stringOop + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(stringOop)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(stringOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(stringOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + sz = numBytes - (fmt & 7); + goto l1; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + sz = numBytes; + goto l1; + } + if (fmt >= (firstShortFormat())) { + sz = numBytes - (((fmt & 3) << 1)); + goto l1; + } + + /* fmt >= self firstLongFormat */ + sz = numBytes - (((fmt & 1) << 2)); + /* end byteSizeOf: */ +l1: + ioSetLogDirectoryOfSize(firstIndexableField(stringOop), sz); + if (!GIV(primFailCode)) { + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + return 0; +} + + /* InterpreterPrimitives>>#primitiveSetOrHasIdentityHash */ +static void +primitiveSetOrHasIdentityHash(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt hasHash; + sqInt hash; + sqInt isReceiverAClass; + sqInt lastArg; + int oldHash; + char *sp; + sqInt thisReceiver; + + if (!GIV(argumentCount)) { + hasHash = ((!((longAt(GIV(stackPointer))) & (tagMask())))) + && ((long32At((void *)((longAt(GIV(stackPointer))) + 4))) & (identityHashHalfWordMask())); + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (hasHash + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return; + } + isReceiverAClass = 0; + if (GIV(argumentCount) == 2) { + lastArg = longAt(GIV(stackPointer)); + if ((((lastArg) & 7) == 1)) { + hash = lastArg; + thisReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + } + else { + thisReceiver = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + hash = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (lastArg == GIV(trueObj)) { + isReceiverAClass = 1; + } + else { + if (!(lastArg == GIV(falseObj))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + } + } + else { + thisReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + hash = longAt(GIV(stackPointer)); + } + + /* anObject primitiveSetIdentityHashTo: hash */ + if (!(((((hash) & 7) == 1)) + && ((/* if written naturally, clang warns: unsequenced modification and access to 'hash' */ + (hash = (hash >> 3)), + (hash & (identityHashHalfWordMask())) == hash)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (!((!(thisReceiver & (tagMask()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = (thisReceiver == (longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord))) + ? PrimErrBadReceiver + : PrimErrBadArgument); + return; + } + oldHash = (long32At((void *)(thisReceiver + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(thisReceiver + 4),((((long32At((void *)(thisReceiver + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + if (isReceiverAClass) { + classAtIndexput(hash, thisReceiver); + + /* this figures out if the index is ambiguous and fixes all the instances if needed */ + allInstancesOf(thisReceiver); + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)oldHash << 3) | 1)); +} + + +/* Treat the receiver, which can be indexible by either bytes or words, as + an array of signed 16-bit values. Answer the contents of the given index. + Note that the index specifies the i-th 16-bit entry, not the i-th byte or + word. */ + + /* InterpreterPrimitives>>#primitiveShortAt */ +static void +primitiveShortAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt rcvr; + sqInt value; + + index = longAt(GIV(stackPointer)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(/* isWordsOrBytes: */ + ((!(rcvr & (tagMask())))) + && (isWordsOrBytesNonImm(rcvr)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + return; + } + index = (index >> 3); + if (!((index >= 1) + && (index <= (((usqInt)((numBytesOf(rcvr)))) >> 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + value = shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 1))))); + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)value << 3) | 1)); +} + + +/* Treat the receiver, which can be indexible by either bytes or words, as an + array of signed 16-bit values. Set the contents of the given index to the + given value. + Note that the index specifies the i-th 16-bit entry, not the i-th byte or + word. + */ + + /* InterpreterPrimitives>>#primitiveShortAtPut */ +static void +primitiveShortAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt rcvr; + sqInt value; + + value = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(((((value) & 7) == 1)) + && (((((index) & 7) == 1)) + && (((value = (value >> 3)), + (value >= -32768) + && (value <= 0x7FFF)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (!(/* isWordsOrBytes: */ + ((!(rcvr & (tagMask())))) + && (isWordsOrBytesNonImm(rcvr)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + return; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + return; + } + index = (index >> 3); + if (!((index >= 1) + && (index <= (((usqInt)((numBytesOf(rcvr)))) >> 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 1)))),value); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),(((usqInt)value << 3) | 1)); +} + + +/* Set the low-water mark for free space. When the free space falls + below this level, the new and new: primitives fail and system attempts + to allocate space (e.g., to create a method context) cause the low-space + semaphore (if one is registered) to be signalled. */ + + /* InterpreterPrimitives>>#primitiveSignalAtBytesLeft */ +static void +primitiveSignalAtBytesLeft(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bytes; + + bytes = longAt(GIV(stackPointer)); + if (((((bytes) & 7) == 1)) + && (((bytes = (bytes >> 3))) >= 0)) { + /* begin lowSpaceThreshold: */ + GIV(lowSpaceThreshold) = bytes; + + /* N.B. The threshold > 0 guard eliminates a warning when + self lowSpaceThreshold: 0 + is inlined into setSignalLowSpaceFlagAndSaveProcess */ + if ((bytes > 0) + && (GIV(totalFreeOldSpace) < bytes)) { + growOldSpaceByAtLeast(bytes - GIV(totalFreeOldSpace)); + } + assert(GIV(totalFreeOldSpace) >= GIV(lowSpaceThreshold)); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } +} + + +/* Computes sine of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveSine */ +static void +primitiveSine(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(sin(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSize */ +static void +primitiveSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt totalLength; + + fmt = 0; + rcvr = longAt(GIV(stackPointer)); + if ((((rcvr & (tagMask())) != 0)) + || (((hdr = long64At((void *)(rcvr))), + (((fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()))) < (arrayFormat())) + || (fmt == (forwardedFormat()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* Integers are not indexable + And if Spur, this is a forwarder. */ + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + primitiveContextSize(); + return; + } + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)(totalLength - fixedFields) << 3) | 1)); +} + + /* InterpreterPrimitives>>#primitiveSizeInBytes */ +static void +primitiveSizeInBytes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong byteSize; + sqInt oop; + + byteSize = /* totalByteSizeOf: */ + ((((longAt(GIV(stackPointer))) & (tagMask())) != 0) + ? 0 + : bytesInBody(longAt(GIV(stackPointer)))); + oop = positive64BitIntegerFor(byteSize); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} + + +/* Answer the byte size of an instance of the receiver. If num args > 0 + then the last argument is a variable size and the size answered is the + size of an instance of the receiver with that many indexable elements. */ + + /* InterpreterPrimitives>>#primitiveSizeInBytesOfInstance */ +static void +primitiveSizeInBytesOfInstance(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt byteSize; + sqInt classFormat; + int err; + sqInt instSpec; + usqInt numSlots; + sqInt numSlotsSqInt; + sqInt oop; + + if (GIV(argumentCount) > 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + + /* Support VMMirror>>byteSizeOfInstanceOf:WithIndexableVariables: */ + err = -1; + if (GIV(argumentCount) >= 1) { + if (!(((((longAt(GIV(stackPointer)))) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* begin byteSizeOfInstanceOf:withIndexableSlots:errInto: */ + classFormat = ((longAt((void *)(((longAt(GIV(stackPointer) + (1 * BytesPerWord))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + switch (instSpec) { + case arrayFormat(): + numSlots = ((longAt(GIV(stackPointer))) >> 3); + break; + case indexablePointersFormat(): + case weakArrayFormat(): + numSlots = (classFormat & ((1U << (fixedFieldsFieldWidth())) - 1)) + (((longAt(GIV(stackPointer))) >> 3)); + break; + case sixtyFourBitIndexableFormat(): + numSlots = ((longAt(GIV(stackPointer))) >> 3); + break; + case firstLongFormat(): + numSlots = ((((longAt(GIV(stackPointer))) >> 3)) + 1) / 2; + break; + case firstShortFormat(): + numSlots = ((((longAt(GIV(stackPointer))) >> 3)) + 3) / 4; + break; + case firstByteFormat(): + case firstCompiledMethodFormat(): + numSlots = ((((longAt(GIV(stackPointer))) >> 3)) + (BytesPerOop - 1)) / BytesPerOop; + break; + default: + byteSize = (err = PrimErrBadReceiver); + goto l1; + } + if (numSlots >= (((((usqInt)1)) << ((BytesPerOop * 8) - 3 /* logBytesPerOop */)))) { + byteSize = (err = ((((longAt(GIV(stackPointer))) >> 3)) < 0 + ? PrimErrBadArgument + : PrimErrLimitExceeded)); + goto l1; + } + byteSize = /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize); + /* end byteSizeOfInstanceOf:withIndexableSlots:errInto: */ +l1: + if (err >= 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = err; + return; + } + oop = positive64BitIntegerFor(byteSize); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return; + } + + /* begin byteSizeOfInstanceOf:errInto: */ + classFormat = ((longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + if (!(/* isFixedSizePointerFormat: */ + (instSpec <= (nonIndexablePointerFormat())) + || (instSpec == (ephemeronFormat())))) { + byteSize = (err = PrimErrBadReceiver); + goto l2; + } + numSlotsSqInt = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + byteSize = /* objectBytesForSlots: */ + (numSlotsSqInt + ? ((((usqInt)(numSlotsSqInt) << (shiftForWord())))) + ((numSlotsSqInt >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize); + /* end byteSizeOfInstanceOf:errInto: */ +l2: + if (err >= 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = err; + return; + } + + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),((byteSize << 3) | 1)); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatAdd */ +static void +primitiveSmallFloatAdd(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr + arg)); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatArctan */ +static void +primitiveSmallFloatArctan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + sqInt oop; + double rcvr; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + aValue = floatObjectOf(atan(rcvr)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatCosine */ +static void +primitiveSmallFloatCosine(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + sqInt oop; + double rcvr; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + aValue = floatObjectOf(cos(rcvr)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatDivide */ +static void +primitiveSmallFloatDivide(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + if (arg == 0.0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr / arg)); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatEqual */ +static void +primitiveSmallFloatEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) == intArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr == arg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + +/* Computes E raised to the receiver power. + Since SmallFloats cannot represent NaNs there's no need to special case. */ + + /* InterpreterPrimitives>>#primitiveSmallFloatExp */ +static void +primitiveSmallFloatExp(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + + aValue = floatObjectOf(exp(smallFloatValueOf(longAt(GIV(stackPointer))))); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + +/* Answer the exponent part of this float. */ + + /* InterpreterPrimitives>>#primitiveSmallFloatExponent */ +static void +primitiveSmallFloatExponent(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqInt exp; + + aValue = (((usqInt)(((/* begin exponentOfSmallFloat: */ + assert(((longAt(GIV(stackPointer))) & (tagMask())) == (smallFloatTag())), +((((usqInt)(longAt(GIV(stackPointer))))) <= 15 + ? 0 + : ((exp = ((((usqInt)(longAt(GIV(stackPointer)))))) >> (((numTagBits()) + (smallFloatMantissaBits())) + 1)), + (exp + (smallFloatExponentOffset())) - 0x3FE)))) - 1) << 3) | 1); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatFractionalPart */ +static void +primitiveSmallFloatFractionalPart(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double frac; + sqInt oop; + double rcvr; + double trunc; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + frac = modf(rcvr, (&trunc)); + aValue = floatObjectOf(frac); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatGreaterOrEqual */ +static void +primitiveSmallFloatGreaterOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) >= intArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr >= arg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatGreaterThan */ +static void +primitiveSmallFloatGreaterThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) > intArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr > arg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatLessOrEqual */ +static void +primitiveSmallFloatLessOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) <= intArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr <= arg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatLessThan */ +static void +primitiveSmallFloatLessThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) < intArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr < arg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + +/* Natural log. */ + + /* InterpreterPrimitives>>#primitiveSmallFloatLogN */ +static void +primitiveSmallFloatLogN(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + sqInt oop; + double rcvr; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + aValue = floatObjectOf(log(rcvr)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatMultiply */ +static void +primitiveSmallFloatMultiply(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr * arg)); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatNotEqual */ +static void +primitiveSmallFloatNotEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) == intArg + ? GIV(falseObj) + : GIV(trueObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr == arg + ? GIV(falseObj) + : GIV(trueObj))); + GIV(stackPointer) = sp; + } + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatSine */ +static void +primitiveSmallFloatSine(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + sqInt oop; + double rcvr; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + aValue = floatObjectOf(sin(rcvr)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatSquareRoot */ +static void +primitiveSmallFloatSquareRoot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + sqInt oop; + double rcvr; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + if (rcvr >= 0.0) { + aValue = floatObjectOf(sqrt(rcvr)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatSubtract */ +static void +primitiveSmallFloatSubtract(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr - arg)); + GIV(stackPointer) = sp; + } +} + + +/* Multiply the receiver by the power of the argument. */ + + /* InterpreterPrimitives>>#primitiveSmallFloatTimesTwoPower */ +static void +primitiveSmallFloatTimesTwoPower(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt rcvr; + sqInt result; + sqInt twiceMaxExponent; + + arg = longAt(GIV(stackPointer)); + if (!((((arg) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* N.B. SmallFloats are finite. NaN and Infinity overflow into boxed floats. + This is doing range checking work that is done in ldexp, but we include + it explicitly to exemplify bit manipulation of SmallFloats. */ + + /* begin isSmallFloatZero: */ + assert(isImmediateFloat(rcvr)); + if ((((usqInt)rcvr)) <= ((1U << (numTagBits())) + (smallFloatTag()))) { + result = rcvr; + } + else { + arg = (arg >> 3); + twiceMaxExponent = 0x1000; + if (arg < (-twiceMaxExponent)) { + /* begin mapSignedSmallFloatToSignedSmallFloatZero: */ + assert(isImmediateFloat(rcvr)); + result = rcvr & ((1U << (numTagBits())) + (tagMask())); + } + else { + /* clip arg to at most int range; ldexp's last arg is of type int */ + if (arg > twiceMaxExponent) { + arg = twiceMaxExponent; + } + result = floatObjectOf(ldexp(smallFloatValueOf(rcvr), ((int) arg))); + } + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),result); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatTruncated */ +static void +primitiveSmallFloatTruncated(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + sqInt oop; + double rcvr; + double trunc; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + modf(rcvr, (&trunc)); + if ((((trunc >= (((double) (MinSmallInteger) ))) && (trunc <= (((double) (MaxSmallInteger) ))))) + && ((((((usqInt)((((sqInt)trunc)))) >> 60) + 1) & 15) <= 1)) { + /* stackTopPut: */ + longAtput(GIV(stackPointer),(((usqInt)(((sqInt)trunc)) << 3) | 1)); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + /* InterpreterPrimitives>>#primitiveSomeInstance */ +static void +primitiveSomeInstance(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt instance; + + class = longAt(GIV(stackPointer)); + + /* For the mirror prims check that the class obj is actually a valid class. */ + instance = initialInstanceOf(class); + if (instance) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),instance); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Return the first object in the heap. */ + + /* InterpreterPrimitives>>#primitiveSomeObject */ +static void +primitiveSomeObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = firstAccessibleObject(); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} + + +/* Return the oop of the SpecialObjectsArray. */ + + /* InterpreterPrimitives>>#primitiveSpecialObjectsOop */ +static void +primitiveSpecialObjectsOop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),GIV(specialObjectsOop)); +} + + +/* Computes square root of float receiver; receiver *must* be a float + instance. + */ + + /* InterpreterPrimitives>>#primitiveSquareRoot */ +static void +primitiveSquareRoot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + if (doubleValue >= 0.0) { + aValue = floatObjectOf(sqrt(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* This primitive is called from Squeak as... + storeSegmentFor: arrayOfRoots into: aWordArray outPointers: + anArray. + */ +/* This primitive will store a binary image segment (in the same format as + the Squeak image file) of the receiver and every object in its proper tree + of subParts (ie, that is not refered to from anywhere else outside the + tree). All pointers from within the tree to objects outside the tree will + be copied into the array of outpointers. In their place in the image + segment will be an oop equal to the offset in the outPointer array (the + first would be 4). but with the high bit set. + */ +/* The primitive expects the array and wordArray to be more than adequately + long. In this case it returns normally, and truncates the two arrays to + exactly the right size. To simplify truncation, both incoming arrays are + required to be whatever the objectMemory considers long objects. If either + array is too small, the primitive will fail, but in no other case. + + During operation of the primitive, it is necessary to convert from both + internal and external oops to their mapped values. To make this fast, the + headers of the original objects in question are replaced by the mapped + values (and this is noted by adding the forbidden XX header type). Tables + are kept of both kinds of oops, as well as of the original headers for + restoration. + To be specific, there are two similar two-part tables, the outpointer + array, and one in the upper fifth of the segmentWordArray. Each grows oops + from the bottom up, and preserved headers from halfway up. + + In case of either success or failure, the headers must be restored. In the + event of primitive failure, the table of outpointers must also be nilled + out (since the garbage in the high half will not have been discarded. + */ + + /* InterpreterPrimitives>>#primitiveStoreImageSegment */ +static void +primitiveStoreImageSegment(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayOfRoots; + sqInt ecode; + sqInt outPointerArray; + sqInt segmentWordArray; + + outPointerArray = longAt(GIV(stackPointer)); + segmentWordArray = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + arrayOfRoots = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + + /* Essential type checks */ + if (!((/* isArray: */ + ((!(arrayOfRoots & (tagMask())))) + && (((byteAt((void *)(arrayOfRoots + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((/* isArray: */ + ((!(outPointerArray & (tagMask())))) + && (((byteAt((void *)(outPointerArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (/* isWords: */ + ((!(segmentWordArray & (tagMask())))) + && (((((byteAt((void *)(segmentWordArray + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(segmentWordArray + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1)))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* Must be indexable pointers + Must be indexable words */ + ecode = storeImageSegmentIntooutPointersroots(segmentWordArray, outPointerArray, arrayOfRoots); + if (ecode == PrimErrNeedCompaction) { + fullGC(); + outPointerArray = longAt(GIV(stackPointer)); + segmentWordArray = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + arrayOfRoots = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + ecode = storeImageSegmentIntooutPointersroots(segmentWordArray, outPointerArray, arrayOfRoots); + } + if (ecode) { + /* primitiveFailFor: */ + GIV(primFailCode) = ecode; + } + else { + /* begin pop: */ + GIV(stackPointer) += 3 * BytesPerWord; + } +} + + /* InterpreterPrimitives>>#primitiveStringAt */ +static void +primitiveStringAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt result; + sqInt sp1; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + + /* begin commonAt: */ + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + index = longAt(GIV(stackPointer)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l5; + } + + /* No need to test for large positive integers here. No object has 1g elements */ + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 1) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l5; + } + index = (index >> 3); + + /* begin stObject:at: */ + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + sp1 = stackPointerIndexForFrame(frameOfMarriedContext(rcvr)); + assert((ReceiverIndex + ((sp1 >> 3))) < (lengthOf(rcvr))); + stSize = sp1; + goto l3; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l3; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(rcvr))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l3: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))))) { + result = temporaryin(index - 1, frameOfMarriedContext(rcvr)); + goto l4; + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(rcvr)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + result = (((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + (index - 1)))) << 3) | 1); + goto l4; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + result = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << (shiftForWord())))))); + goto l4; + } + if (fmt >= (firstByteFormat())) { + result = (((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + ((index + fixedFields) - 1)))) << 3) | 1); + goto l4; + } + if (fmt >= (firstShortFormat())) { + result = (((usqInt)(((unsigned short) (shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))))))) << 3) | 1); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + result = positive64BitIntegerFor(long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))))); + goto l4; + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + result = (((usqInt)((((usqInt)(long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + goto l4; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + result = 0; + /* end stObject:at: */ +l4: + if (!GIV(primFailCode)) { + if (!(((result >= 0) && (result <= (0x3FFFFFFF))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l5; + } + result = ((((usqInt)(((result >> 3))) << (numTagBits())))) + (characterTag()); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); + } + /* end commonAt: */ +l5:; +} + + /* InterpreterPrimitives>>#primitiveStringAtPut */ +static void +primitiveStringAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fieldIndex; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt signedValueToStore; + sqInt signedValueToStoreSqInt; + sqInt sp; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + usqLong unsigned64BitValueToStore; + unsigned int unsignedValueToStore; + sqInt value; + sqInt valueSqInt; + + /* begin commonAtPut: */ + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + value = longAt(GIV(stackPointer)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l11; + } + + /* No need to test for large positive integers here. No object has 1g elements */ + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 2) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l11; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l11; + } + index = (index >> 3); + valueSqInt = asciiOfCharacter(value); + + /* begin stObject:at:put: */ + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l1: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(rcvr)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(rcvr))); + stSize = sp; + goto l2; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l2; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(rcvr))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l2: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))))) { + temporaryinput(index - 1, frameOfMarriedContext(rcvr), valueSqInt); + goto l5; + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(rcvr)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + if (((((valueSqInt) & 7) == 1)) + && ((((usqInt)((signedValueToStore = (valueSqInt >> 3))))) <= 0xFF)) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + (index - 1)),signedValueToStore); + goto l5; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l5; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l3; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l3; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l3: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:storing:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + fieldIndex = (index + fixedFields) - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, rcvr, valueSqInt)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valueSqInt & (tagMask())))) + && (oopisLessThan(valueSqInt, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valueSqInt); + goto l4; + } + if (fmt >= (firstByteFormat())) { + if (!((((valueSqInt) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStoreSqInt = (valueSqInt >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + ((index + fixedFields) - 1)),signedValueToStoreSqInt); + goto l4; + } + if (fmt >= (firstShortFormat())) { + if (!((((valueSqInt) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStoreSqInt = (valueSqInt >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))),signedValueToStoreSqInt); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(valueSqInt); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l4; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(valueSqInt); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l4: + goto l5; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + /* end stObject:at:put: */ +l5: + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + } + /* end commonAtPut: */ +l11:; +} + + +/* primReplaceFrom: start to: stop with: replacement startingAt: + repStart + */ + + /* InterpreterPrimitives>>#primitiveStringReplace */ +static void +primitiveStringReplace(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt array; + sqInt arrayFmt; + sqInt arrayInstSize; + usqInt arrayLength; + sqInt class; + sqInt i; + sqInt mustRemember; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt repl; + sqInt replFmt; + sqInt replInstSize; + usqInt replLength; + sqInt replStart; + sqInt srcDelta; + sqInt start; + sqInt stop; + sqInt valuePointer; + + /* begin primitiveSpurStringReplace */ + array = longAt(GIV(stackPointer) + (4 * BytesPerWord)); + start = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + stop = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + repl = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + replStart = longAt(GIV(stackPointer)); + if (((!(start & (smallIntegerTag())))) + || (((!(stop & (smallIntegerTag())))) + || (((!(replStart & (smallIntegerTag())))) + || (((repl & (tagMask())) != 0))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l7; + } + + /* can happen in LgInt copy */ + start = (start >> 3); + stop = (stop >> 3); + replStart = (replStart >> 3); + if ((stop >= start) + && ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(array + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + )) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l7; + } + arrayFmt = (byteAt((void *)(array + (formatFieldByteOffset())))) & (formatMask()); + replFmt = (byteAt((void *)(repl + (formatFieldByteOffset())))) & (formatMask()); + + /* N.B. In the below start - 1 to: stop - 1 do:, Slang is intelligent enough to use < instead of <= so avoiding the stop - 1. */ + if (arrayFmt <= 5 /* lastPointerFormat */) { + /* Array formats must be the same; but for copying, weak arrays are equivalent to arrays. */ + if (arrayFmt == (weakArrayFormat())) { + arrayFmt = arrayFormat(); + } + if (replFmt == (weakArrayFormat())) { + replFmt = arrayFormat(); + } + if (arrayFmt != replFmt) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l7; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(array)) > (isForwardedObjectClassIndexPun())); + arrayLength = (((numSlots = byteAt((void *)(array + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + + /* begin fixedFieldsOf:format:length: */ + if ((arrayFmt >= (sixtyFourBitIndexableFormat())) + || (arrayFmt == (arrayFormat()))) { + arrayInstSize = 0; + goto l5; + } + if (arrayFmt < (arrayFormat())) { + arrayInstSize = arrayLength; + goto l5; + } + class = fetchClassOfNonImm(array); + arrayInstSize = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l5: + + /* begin numSlotsOf: */ + assert((classIndexOf(repl)) > (isForwardedObjectClassIndexPun())); + replLength = (((numSlots = byteAt((void *)(repl + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(repl - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + + /* begin fixedFieldsOf:format:length: */ + if ((replFmt >= (sixtyFourBitIndexableFormat())) + || (replFmt == (arrayFormat()))) { + replInstSize = 0; + goto l6; + } + if (replFmt < (arrayFormat())) { + replInstSize = replLength; + goto l6; + } + class = fetchClassOfNonImm(repl); + replInstSize = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l6: + if (!((start >= 1) + && (((start - 1) <= stop) + && (((stop + arrayInstSize) <= arrayLength) + && ((replStart >= 1) + && ((((stop - start) + replStart) + replInstSize) <= replLength)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l7; + } + start += arrayInstSize; + stop += arrayInstSize; + srcDelta = (replStart + replInstSize) - start; + + /* begin isOldObject: */ + assert(isNonImmediate(array)); + if (oopisGreaterThanOrEqualTo(array, GIV(oldSpaceStart))) { + mustRemember = 0; + for (i = (start - 1); i < stop; i += 1) { + oop = longAt((void *)((repl + BaseHeaderSize) + ((((usqInt)((srcDelta + i)) << (shiftForWord())))))); + if (/* isYoung: */ + ((!(oop & (tagMask())))) + && (oopisLessThan(oop, GIV(oldSpaceStart)))) { + mustRemember = 1; + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(i, array, oop)); + longAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),oop); + } + if (mustRemember) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(array + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(array); + } + } + } + else { + for (i = (start - 1); i < stop; i += 1) { + valuePointer = longAt((void *)((repl + BaseHeaderSize) + ((((usqInt)((srcDelta + i)) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(i, array, valuePointer)); + longAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + } + + /* We might consider comparing stop - start to some value here and using forceInterruptCheck */ + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + goto l7; + } + + /* Non-pointer array formats must match */ + + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(array + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (arrayFmt <= (ephemeronFormat())) { + arrayLength = ((sqInt) numSlots); + goto l3; + } + if (arrayFmt >= (firstByteFormat())) { + arrayLength = ((numSlots << (shiftForWord()))) - (arrayFmt & 7); + goto l3; + } + + /* bytes, including CompiledMethod */ + if (arrayFmt >= (firstShortFormat())) { + arrayLength = ((numSlots << ((shiftForWord()) - 1))) - (arrayFmt & 3); + goto l3; + } + if (arrayFmt >= (firstLongFormat())) { + arrayLength = ((numSlots << ((shiftForWord()) - 2))) - (arrayFmt & 1); + goto l3; + } + if (arrayFmt == (sixtyFourBitIndexableFormat())) { + arrayLength = ((sqInt) numSlots); + goto l3; + } + + /* fmt = self forwardedFormat */ + arrayLength = 0; + /* end lengthOf:format: */ +l3: + + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(repl + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(repl - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (replFmt <= (ephemeronFormat())) { + replLength = ((sqInt) numSlots); + goto l4; + } + if (replFmt >= (firstByteFormat())) { + replLength = ((numSlots << (shiftForWord()))) - (replFmt & 7); + goto l4; + } + + /* bytes, including CompiledMethod */ + if (replFmt >= (firstShortFormat())) { + replLength = ((numSlots << ((shiftForWord()) - 1))) - (replFmt & 3); + goto l4; + } + if (replFmt >= (firstLongFormat())) { + replLength = ((numSlots << ((shiftForWord()) - 2))) - (replFmt & 1); + goto l4; + } + if (replFmt == (sixtyFourBitIndexableFormat())) { + replLength = ((sqInt) numSlots); + goto l4; + } + + /* fmt = self forwardedFormat */ + replLength = 0; + /* end lengthOf:format: */ +l4: + + /* begin classFormatFromInstFormat: */ + if (arrayFmt >= (firstByteFormat())) { + arrayFmt = arrayFmt & -8; + goto l1; + } + + /* this is likely the common case */ + if (arrayFmt <= (sixtyFourBitIndexableFormat())) { + goto l1; + } + if (arrayFmt < (firstShortFormat())) { + arrayFmt = arrayFmt & -2; + goto l1; + } + arrayFmt = arrayFmt & -4; + /* end classFormatFromInstFormat: */ +l1: + + /* begin classFormatFromInstFormat: */ + if (replFmt >= (firstByteFormat())) { + replFmt = replFmt & -8; + goto l2; + } + + /* this is likely the common case */ + if (replFmt <= (sixtyFourBitIndexableFormat())) { + goto l2; + } + if (replFmt < (firstShortFormat())) { + replFmt = replFmt & -2; + goto l2; + } + replFmt = replFmt & -4; + /* end classFormatFromInstFormat: */ +l2: + if (!((arrayFmt == replFmt) + && ((arrayFmt >= (sixtyFourBitIndexableFormat())) + && (arrayFmt < (firstCompiledMethodFormat()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l7; + } + if (!((start >= 1) + && (((start - 1) <= stop) + && ((stop <= arrayLength) + && ((replStart >= 1) + && (((stop - start) + replStart) <= replLength)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l7; + } + srcDelta = replStart - start; + if (arrayFmt >= (firstShortFormat())) { + if (arrayFmt >= (firstByteFormat())) { + for (i = (start - 1); i < stop; i += 1) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((array + BaseHeaderSize) + i),byteAt((void *)((repl + BaseHeaderSize) + (srcDelta + i)))); + } + } + else { + for (i = (start - 1); i < stop; i += 1) { + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << 1)))),shortAt((void *)((repl + BaseHeaderSize) + ((((usqInt)((srcDelta + i)) << 1)))))); + } + } + } + else { + if (arrayFmt >= (firstLongFormat())) { + for (i = (start - 1); i < stop; i += 1) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << 2)))),long32At((void *)((repl + BaseHeaderSize) + ((((usqInt)((srcDelta + i)) << 2)))))); + } + } + else { + for (i = (start - 1); i < stop; i += 1) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << 3)))),long64At((void *)((repl + BaseHeaderSize) + ((((usqInt)((srcDelta + i)) << 3)))))); + } + } + } + + /* 8 & 16-bit word type objects + 32 & 64-bit word type objects + We might consider comparing stop - start to some value here and using forceInterruptCheck */ + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + /* end primitiveSpurStringReplace */ +l7:; +} + + /* InterpreterPrimitives>>#primitiveSubtract */ +static void +primitiveSubtract(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerResult; + + integerResult = (stackIntegerValue(1)) - (stackIntegerValue(0)); + + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)(integerResult)) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerResult << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveSubtractLargeIntegers */ +EXPORT(void) +primitiveSubtractLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + sqInt resultIsNegative; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (GIV(primFailCode)) { + return; + } + if (aIsNegative != bIsNegative) { + /* Protect against overflow */ + if (a > (0xFFFFFFFFFFFFFFFFULL - b)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + result = a + b; + resultIsNegative = aIsNegative; + } + else { + if (a >= b) { + result = a - b; + resultIsNegative = aIsNegative; + } + else { + result = b - a; + resultIsNegative = !aIsNegative; + } + } + oopResult = magnitude64BitIntegerForneg(result, resultIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Attempt to test-and-set the ownership of the critical section. If not + owned, set the owner to the current process and answer false. If owned by + the current process answer true. If owned by some other process answer + nil. For simulation if there is an argument it is taken to be the + effective activeProcess + (see Process>>effectiveProcess). */ + + /* InterpreterPrimitives>>#primitiveTestAndSetOwnershipOfCriticalSection */ +static void +primitiveTestAndSetOwnershipOfCriticalSection(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + sqInt criticalSection; + sqInt objOop; + sqInt owningProcess; + sqInt owningProcessIndex; + + if (GIV(argumentCount) > 0) { + /* rcvr */ + criticalSection = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + activeProc = longAt(GIV(stackPointer)); + if (/* isOopForwarded: */ + ((!(activeProc & (tagMask())))) + && ((!((longAt((void *)(activeProc))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + } + else { + /* rcvr */ + criticalSection = longAt(GIV(stackPointer)); + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + } + + /* CriticalSections are laid out like Semaphores */ + owningProcessIndex = ExcessSignalsIndex; + owningProcess = longAt((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord())))))); + if (owningProcess == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(owningProcessIndex, criticalSection, activeProc)); + assert(isNonImmediate(criticalSection)); + if (oopisGreaterThanOrEqualTo(criticalSection, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeProc & (tagMask())))) + && (oopisLessThan(activeProc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(criticalSection + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(criticalSection); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord()))))),activeProc); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(falseObj)); + return; + } + if (owningProcess == activeProc) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(trueObj)); + return; + } + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); +} + + +/* Return true if the host OS does support the given display depth. */ + + /* InterpreterPrimitives>>#primitiveTestDisplayDepth */ +static void +primitiveTestDisplayDepth(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bitsPerPixel; + sqInt integerPointer; + sqInt okay; + char *sp; + + okay = 0; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + bitsPerPixel = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + bitsPerPixel = 0; + } + if (!GIV(primFailCode)) { + okay = ioHasDisplayDepth(bitsPerPixel); + } + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (okay + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + +/* Given an object with indexable pointer fields, reduce the size of the + indexable fields + to the requested size. Answer the number of bytes freed, or zero if the + object cannot + be shortened. + */ + + /* InterpreterPrimitives>>#primitiveTestShortenIndexableSize */ +#if TestingPrimitives +EXPORT(sqInt) +primitiveTestShortenIndexableSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt array; + sqInt delta; + sqInt indexableSize; + sqInt integer; + sqInt newSizeOop; + usqInt numSlots; + + newSizeOop = longAt(GIV(stackPointer)); + array = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(((((newSizeOop) & 7) == 1)) + && (((((newSizeOop >> 3)) >= 0) && (((newSizeOop >> 3)) <= ((stSizeOf(array)) - 1)))))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + if (!(/* canShorten: */ + ((!(array & (tagMask())))) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(array)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(array + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > 0) + && ((((byteAt((void *)(array + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())) + || (((((byteAt((void *)(array + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(array + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1)))))))) { + return (GIV(primFailCode) = PrimErrBadReceiver); + } + indexableSize = (newSizeOop >> 3); + + /* begin shorten:toIndexableSize: */ + assert((indexableSize >= 0) + && (indexableSize < (lengthOf(array)))); + delta = doShortentoIndexableSize(array, indexableSize); + assert((lengthOf(followMaybeForwarded(array))) == indexableSize); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + integer = delta; + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); + return 0; +} +#endif /* TestingPrimitives */ + + +/* Multiply the receiver by the power of the argument. + Receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveTimesTwoPower */ +static void +primitiveTimesTwoPower(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aFloatOop; + sqInt arg; + usqLong bits; + double rcvr; + double result; + char *sp; + sqInt twiceMaxExponent; + + arg = longAt(GIV(stackPointer)); + if (!((((arg) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + arg = (arg >> 3); + + /* clip arg to at most int range; ldexp's last arg is of type int */ + twiceMaxExponent = 0x1000; + if (arg < (-twiceMaxExponent)) { + arg = -twiceMaxExponent; + } + else { + if (arg > twiceMaxExponent) { + arg = twiceMaxExponent; + } + } + aFloatOop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(aFloatOop)); + if (aFloatOop & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(aFloatOop)); + bits = ((((usqInt)aFloatOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + } + else { + fetchFloatAtinto(aFloatOop + BaseHeaderSize, rcvr); + } + result = ldexp(rcvr, ((int) arg)); + + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(result)); + GIV(stackPointer) = sp; +} + + +/* Integral part of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveTruncated */ +static void +primitiveTruncated(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + double doubleValue; + sqInt rcvr; + double trunc; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + modf(doubleValue, (&trunc)); + if (((trunc >= (((double) (MinSmallInteger) ))) && (trunc <= (((double) (MaxSmallInteger) ))))) { + /* stackTopPut: */ + longAtput(GIV(stackPointer),(((usqInt)(((sqInt)trunc)) << 3) | 1)); + return; + } + + /* make Nicolas Cellier's otherwise recursive BoxedFloat64>>truncated nice 2/7/2025 07:34 work on 32-bits. */ + + /* a.k.a. trunc abs <= Float maxExactInteger asFloat */ + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } +} + + +/* Allocate a new indexable instance. Fail if the allocation would leave less + than lowSpaceThreshold bytes free. May cause a GC. + */ + + /* InterpreterPrimitives>>#primitiveUninitializedNewWithArg */ +static void +primitiveUninitializedNewWithArg(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt classObj; + sqInt err; + int hash; + sqInt instSpec; + sqInt instSpecSqInt; + sqInt newObj; + usqInt newObjUsqInt; + usqInt numBytes; + usqInt numSlots; + sqInt obj; + sqInt reasonCode; + usqIntptr_t size; + sqInt value; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + size = null; + goto l1; + } + size = value; + goto l1; + } + + /* don't inline the rare case */ + size = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* positiveMachineIntegerValueOf: succeeds only for non-negative integers. */ + classObj = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin instantiateUninitializedClass:indexableSize: */ + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpecSqInt = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + assert(isPureBitsFormat(instSpecSqInt)); + switch (instSpecSqInt) { + case sixtyFourBitIndexableFormat(): + numSlots = size; + break; + case firstLongFormat(): + if ((classIndex == ClassFloatCompactIndex) + && (size != 2)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + obj = null; + goto l3; + } + numSlots = (size + 1) / 2; + instSpecSqInt += size & 1; + break; + case firstShortFormat(): + numSlots = (size + 3) / 4; + instSpecSqInt += (4 - size) & 3; + break; + case firstByteFormat(): + numSlots = (size + 7) / 8; + instSpecSqInt += (8 - size) & 7; + break; + default: + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + obj = null; + goto l3; + } + + /* not bits indexable */ + if (!classIndex) { + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + obj = null; + goto l3; + } + } + if (numSlots > ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (numSlots > (0x10000000000LL)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + obj = null; + goto l3; + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpecSqInt, classIndex); + } + else { + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if (((numSlots) >> 56) > 0) { + newObj = null; + goto l2; + } + newObjUsqInt = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObjUsqInt = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, instSpecSqInt, classIndex); + goto l2; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObjUsqInt),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(instSpecSqInt) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObjUsqInt),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(instSpecSqInt) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObjUsqInt % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newObj = newObjUsqInt; + /* end allocateSlots:format:classIndex: */ +l2:; + } + obj = newObj; + /* end instantiateUninitializedClass:indexableSize: */ +l3: + if (obj) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + instSpec = (((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer) + (1 * BytesPerWord))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + reasonCode = (/* isPureBitsFormat: */ + (instSpec >= (sixtyFourBitIndexableFormat())) + && (instSpec < (firstCompiledMethodFormat())) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + +/* Allocate a new pinned indexable instance with junk contents, saving the + time for initializing with 0. Fail if the receiver us not a pure bits + class. Fail + if the allocation would leave less than lowSpaceThreshold bytes free. */ + + /* InterpreterPrimitives>>#primitiveUninitializedPinnedNewWithArg */ +static void +primitiveUninitializedPinnedNewWithArg(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt classObj; + sqInt err; + int hash; + sqInt instSpec; + sqInt instSpecSqInt; + sqInt newObj; + usqInt numSlots; + sqInt obj; + sqInt reasonCode; + usqIntptr_t size; + sqInt value; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + size = null; + goto l1; + } + size = value; + goto l1; + } + + /* don't inline the rare case */ + size = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* positiveMachineIntegerValueOf: succeeds only for non-negative integers. */ + classObj = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin inOldSpaceInstantiateUninitializedPinnedClass:indexableSize: */ + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpecSqInt = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + switch (instSpecSqInt) { + case sixtyFourBitIndexableFormat(): + numSlots = size; + break; + case firstLongFormat(): + if ((classIndex == ClassFloatCompactIndex) + && (size != 2)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + obj = null; + goto l2; + } + numSlots = (size + 1) / 2; + instSpecSqInt += size & 1; + break; + case firstShortFormat(): + numSlots = (size + 3) / 4; + instSpecSqInt += (4 - size) & 3; + break; + case firstByteFormat(): + numSlots = (size + 7) / 8; + instSpecSqInt += (8 - size) & 7; + break; + default: + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + obj = null; + goto l2; + } + + /* non-indexable and/or pointers */ + if (!classIndex) { + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + obj = null; + goto l2; + } + } + if (numSlots > (0x10000000000LL)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + obj = null; + goto l2; + } + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpecSqInt, classIndex); + obj = newObj; + /* end inOldSpaceInstantiateUninitializedPinnedClass:indexableSize: */ +l2: + if (obj) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + instSpec = (((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer) + (1 * BytesPerWord))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + reasonCode = (/* isPureBitsFormat: */ + (instSpec >= (sixtyFourBitIndexableFormat())) + && (instSpec < (firstCompiledMethodFormat())) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + +/* Update the VMs notion of the current timezone. The VM sets its notion + of the timezone once at start-up. If one wants the VM to keep its notion + up-to-date arrange to invoke this primitive periodically. */ + + /* InterpreterPrimitives>>#primitiveUpdateTimezone */ +static void +primitiveUpdateTimezone(void) +{ + ioUpdateVMTimezone(); +} + + +/* Return the value of the microsecond clock in UTC as an integer. + This is the number of microseconds since the Smalltalk epoch, 1901/1/1 + 12:00am. The microsecond clock is at least 60 bits wide which means it'll + get to around August + 38435 before it wraps around. Be sure to put it on your calendar. This + primitive accesses the time as answered by the OS. */ + + /* InterpreterPrimitives>>#primitiveUTCMicrosecondClock */ +static void +primitiveUTCMicrosecondClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = positive64BitIntegerFor(ioUTCMicrosecondsNow()); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Answer an array with UTC microseconds since the Smalltalk epoch and the + current seconds offset from GMT in the local time zone. Any pointer object + with two or more slots) may be supplied as a parameter. */ + + /* InterpreterPrimitives>>#primitiveUtcAndTimezoneOffset */ +static void +primitiveUtcAndTimezoneOffset(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt resultArray; + sqInt valuePointer; + + if (GIV(argumentCount) > 0) { + if (GIV(argumentCount) > 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + resultArray = longAt(GIV(stackPointer)); + if (!((/* isPointers: */ + ((!(resultArray & (tagMask())))) + && (((byteAt((void *)(resultArray + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((lengthOf(resultArray)) >= 2))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + else { + resultArray = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 2); + } + + /* N.B. No pushRemappableOop:/popRemappableOop in Cog because positive64BitIntegerFor: et al use + eeInstantiate... allocators which are guaranteed not to do a GC. */ + valuePointer = (((usqInt)(ioLocalSecondsOffset()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultArray)) + && (!(isForwarded(resultArray)))); + assert(validStorePointerUncheckedArgs(1, resultArray, valuePointer)); + longAtput((void *)((resultArray + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(ioUTCMicroseconds()); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(0, resultArray, valuePointer)); + assert(isNonImmediate(resultArray)); + if (oopisGreaterThanOrEqualTo(resultArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(resultArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(resultArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((resultArray + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),resultArray); +} + + +/* Answer an array with UTC microseconds since the Posix epoch and the + current seconds offset from GMT in the local time zone. An empty two + element array (or any object with two or more slots) may be supplied + as a parameter. + This is a named (not numbered) primitive in the null module (ie the VM) */ + + /* InterpreterPrimitives>>#primitiveUtcWithOffset */ +EXPORT(sqInt) +primitiveUtcWithOffset(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + static usqLong epochDelta = 2177452800000000ULL; + sqInt resultArray; + sqInt valuePointer; + + if (GIV(argumentCount) > 0) { + if (GIV(argumentCount) > 1) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + resultArray = longAt(GIV(stackPointer)); + if (!((/* isPointers: */ + ((!(resultArray & (tagMask())))) + && (((byteAt((void *)(resultArray + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((lengthOf(resultArray)) >= 2))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + } + else { + resultArray = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 2); + } + + /* N.B. No pushRemappableOop:/popRemappableOop in Cog because positive64BitIntegerFor: et al use + eeInstantiate... allocators which are guaranteed not to do a GC. */ + valuePointer = (((usqInt)(ioLocalSecondsOffset()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultArray)) + && (!(isForwarded(resultArray)))); + assert(validStorePointerUncheckedArgs(1, resultArray, valuePointer)); + longAtput((void *)((resultArray + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor((ioUTCMicrosecondsNow()) - epochDelta); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(0, resultArray, valuePointer)); + assert(isNonImmediate(resultArray)); + if (oopisGreaterThanOrEqualTo(resultArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(resultArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(resultArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((resultArray + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),resultArray); + return 0; +} + + +/* Return a string containing the path name of VM's directory. */ + + /* InterpreterPrimitives>>#primitiveVMPath */ +static void +primitiveVMPath(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt s; + sqInt sz; + + sz = vmPathSize(); + s = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord())))))), sz); + vmPathGetLength(s + BaseHeaderSize, sz); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),s); +} + + +/* Primitive. + 0 args: Answer whether the VM Profiler is running or not. + 1 arg: Copy the sample data into the supplied argument, which must be a + Bitmap of suitable size. Answer the number of samples copied into the + buffer. */ + + /* InterpreterPrimitives>>#primitiveVMProfileSamplesInto */ +static void +primitiveVMProfileSamplesInto(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + long bufferSize; + sqInt numSamples; + sqInt running; + sqInt sampleBuffer; + char *sp; + + bufferSize = 0; + running = 0; + ioNewProfileStatus((&running), (&bufferSize)); + if (!GIV(argumentCount)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer)),/* booleanObjectOf: */ + (running + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return; + } + if (!(GIV(argumentCount) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + sampleBuffer = longAt(GIV(stackPointer)); + if (!(((!(sampleBuffer & (tagMask())))) + && ((isPureBitsNonImm(sampleBuffer)) + && ((numBytesOf(sampleBuffer)) >= (bufferSize * BytesPerWord))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + numSamples = ioNewProfileSamplesInto(pointerForOop(sampleBuffer + BaseHeaderSize)); + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)numSamples << 3) | 1)); +} + + +/* Signal the given semaphore from within the interpreter. Used to serialize + callbacks. + */ + + /* InterpreterPrimitives>>#signalNoResume: */ +int +signalNoResume(sqInt aSemaphore) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int empty; + + /* begin isEmptyList: */ + assert(!(isForwarded(aSemaphore))); + empty = (longAt((void *)((aSemaphore + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj); + if (!empty) { + putToSleepyieldingIf(removeFirstLinkOfList(aSemaphore), 1); + } + return empty; +} + + +/* Answer a signed value of an integer up to the size of a machine word. + The object is not an immediate and is hoped to be a LargeInteger of size + <= word size. + */ + + /* InterpreterPrimitives>>#signedMachineIntegerValueOfObj: */ +static NoDbgRegParms sqIntptr_t +signedMachineIntegerValueOfObj(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bs; + sqInt ccIndex; + sqInt fmt; + sqInt isClassOfNonImmequalTocompactClassIndexRV; + usqIntptr_t limit; + usqIntptr_t magnitude; + sqInt negative; + usqInt numBytes; + usqInt numSlots; + usqIntptr_t value; + + assert(!(((((oop) & 7) == 1)))); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + isClassOfNonImmequalTocompactClassIndexRV = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (isClassOfNonImmequalTocompactClassIndexRV) { + negative = 0; + } + else { + negative = 1; + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + isClassOfNonImmequalTocompactClassIndexRV = ClassLargeNegativeIntegerCompactIndex == ccIndex; + if (!isClassOfNonImmequalTocompactClassIndexRV) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + } + + /* begin numBytesOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + bs = numBytes - (fmt & 7); + goto l1; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + bs = numBytes; + goto l1; + } + if (fmt >= (firstShortFormat())) { + bs = numBytes - (((fmt & 3) << 1)); + goto l1; + } + + /* fmt >= self firstLongFormat */ + bs = numBytes - (((fmt & 1) << 2)); + /* end numBytesOf: */ +l1: + if (bs > (sizeof(usqIntptr_t))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + if (((sizeof(sqIntptr_t)) == 8) + && (bs > 4)) { + magnitude = SQ_SWAP_8_BYTES_IF_BIGENDIAN((long64At((void *)((oop + BaseHeaderSize))))); + } + else { + magnitude = ((unsigned int) (SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((oop + BaseHeaderSize))))))); + } + limit = ((((usqIntptr_t)1)) << (((sizeof(sqIntptr_t)) * 8) - 1)); + if ((negative + ? magnitude > limit + : magnitude >= limit)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + if (negative) { + value = 0 - magnitude; + } + else { + value = magnitude; + } + return value; +} + + +/* Answer a signed value of an integer up to the size of a machine word. + The object may be either a positive SmallInteger or a LargeInteger of size + <= word size. + */ +/* inline the common case... */ + + /* InterpreterPrimitives>>#signedMachineIntegerValueOf: */ +sqIntptr_t +signedMachineIntegerValueOf(sqInt oop) +{ + if ((((oop) & 7) == 1)) { + return (oop >> 3); + } + return signedMachineIntegerValueOfObj(oop); +} + + +/* Answer the start of the Alien's data or fail if oop is not an Alien. */ + + /* InterpreterPrimitives>>#sizeOfAlienData: */ +usqInt +sizeOfAlienData(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt oopClass; + sqInt tagBits; + + /* begin is:KindOfClass: */ + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while ((oopClass != GIV(nilObj)) + && ((/* isPointers: */ + ((!(oopClass & (tagMask())))) + && (((byteAt((void *)(oopClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOfAny(oopClass)) > InstanceSpecificationIndex))) { + if (oopClass == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord())))))))) { + goto l1; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return 0; +l1: + return SQABS(longAt((void *)(oop + BaseHeaderSize))); +} + + +/* Answer the start of the Alien's data or fail if oop is not an Alien. */ + + /* InterpreterPrimitives>>#startOfAlienData: */ +void * +startOfAlienData(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt oopClass; + sqInt tagBits; + + /* begin is:KindOfClass: */ + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while ((oopClass != GIV(nilObj)) + && ((/* isPointers: */ + ((!(oopClass & (tagMask())))) + && (((byteAt((void *)(oopClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOfAny(oopClass)) > InstanceSpecificationIndex))) { + if (oopClass == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord())))))))) { + goto l1; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return 0; +l1: + return ((void *) (((longAt((void *)(oop + BaseHeaderSize))) > 0 + ? (oop + BaseHeaderSize) + BytesPerOop + : longAt((void *)((oop + BaseHeaderSize) + BytesPerOop))))); +} + + +/* Answer the state of the primitive failure code/success flag. If + primFailCode is non-zero a primitive has failed. If primFailCode + is greater than one then its value indicates the reason for failure. */ +/* In C, non-zero is true, so avoid computation by using not the C version. */ + + /* InterpreterPrimitives>>#successful */ +static int +successful(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return !GIV(primFailCode); +} + + +/* Set the state of the primitive failure code/success flag, iff + successBoolean is false. If primFailCode is non-zero a primitive has + failed. If primFailCode + is greater than one then its value indicates the reason for failure. */ +/* Use returnTypeC: #sqInt because that's the way it is defined in sq.h. + Use no explicit return so that Slang doesn't fail an inlining type-check + when a primitive with return type void uses ^self success: false to exit. */ + + /* InterpreterPrimitives>>#success: */ +sqInt +success(sqInt successBoolean) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!successBoolean) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + return 0; +} + + +/* Answer the appropriate become effect flags for objOop, or 0 if none. + The effect flags determine how much work is done after the become in + following forwarding pointers, + voiding method caches, etc. Flag jitted methods because these will have to + be freed after divorcing + frames and mapping native pcs to bytecode pcs in contexts using them. + See the comment in InterpreterPrimitives>>#primitiveArrayBecome */ + + /* Spur64BitCoMemoryManager>>#becomeEffectFlagsFor: */ +static NoDbgRegParms sqInt +becomeEffectFlagsFor(sqInt objOop) +{ + int hash; + sqInt methodHeader; + + hash = 0; + if (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) { + if ((((hash = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()))) != 0) + && ((classAtIndex(hash)) == objOop)) { + return BecamePointerObjectFlag + BecameActiveClassFlag; + } + return BecamePointerObjectFlag; + } + if (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + methodHeader = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + flagCogMethodForBecome(((CogMethod *) methodHeader)); + return BecameCompiledMethodFlag + BecameJittedCompiledMethodFlag; + } + return BecameCompiledMethodFlag; + } + return 0; +} + + +/* The check against scavengeThreshold in + genInstantiate64BitSignedIntegerValue:* et al compares + freeStart against scavengeThreshold minus the allocation size. Hence the + assert must compensate here. + */ + + /* Spur64BitCoMemoryManager>>#ceScheduleScavenge */ +void +ceScheduleScavenge(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(((GIV(freeStart) + BaseHeaderSize) + (sizeof(double))) >= GIV(scavengeThreshold)); + + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); +} + + /* Spur64BitCoMemoryManager>>#classSmallFloat */ +sqInt +classSmallFloat(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)((smallFloatTag())) << (shiftForWord())))))); +} + + +/* Perform an integrity/leak check using the heapMap. Set a bit at each + object's header. */ +/* Perform an integrity/leak check using the heapMap. Set a bit at each + object's header. + Override to set a bit at each Cog method */ + + /* Spur64BitCoMemoryManager>>#clearLeakMapAndMapAccessibleObjects */ +static void +clearLeakMapAndMapAccessibleObjects(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + clearHeapMap(); + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + heapMapAtWordPut(pointerForOop(obj), 1); + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + addCogMethodsToHeapMap(); +} + + +/* Ensure there are no forwarded literals in the argument. */ + + /* Spur64BitCoMemoryManager>>#ensureNoForwardedLiteralsIn: */ +void +ensureNoForwardedLiteralsIn(sqInt aMethodObj) +{ + followForwardedObjectFieldstoDepth(aMethodObj, 0); +} + + +/* Follow pointers in the object to depth. + Answer if any forwarders were found. + How to avoid cyclic structures?? A temporary mark bit? eem 6/22/2020 no + need since depth is always finite. */ + + /* Spur64BitCoMemoryManager>>#followForwardedObjectFields:toDepth: */ +static NoDbgRegParms sqInt +followForwardedObjectFieldstoDepth(sqInt objOop, sqInt depth) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt fmt; + sqInt found; + sqInt header; + sqInt headerSqInt; + sqInt i; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt referent; + sqInt sp; + + found = 0; + assert((isPointers(objOop)) + || (isOopCompiledMethod(objOop))); + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin numPointerSlotsOf:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + numSlots = CtxtTempFrameStart + contextSize; + goto l2; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + goto l2; + } + if (fmt == (forwardedFormat())) { + numSlots = 1; + goto l2; + } + if (fmt < (firstCompiledMethodFormat())) { + numSlots = 0; + goto l2; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf:format: */ +l2: + + /* It is essential to skip the first field of a method because it may be a + reference to a Cog method in the method zone, not a real object at all. */ + for (i = ((fmt >= (firstCompiledMethodFormat()) + ? 1 + : 0)); i < numSlots; i += 1) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(oop & (tagMask())))) { + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + found = 1; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(i, objOop, oop)); + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(oop & (tagMask())))) + && (oopisLessThan(oop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),oop); + } + if ((depth > 0) + && ((/* hasPointerFields: */ + ((!(oop & (tagMask())))) + && (hasPointerFieldsNonImm(oop))) + && (followForwardedObjectFieldstoDepth(oop, depth - 1)))) { + found = 1; + } + } + } + return found; +} + + /* Spur64BitCoMemoryManager>>#freeStartAddress */ +usqInt +freeStartAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(freeStart)))); +} + + /* Spur64BitCoMemoryManager>>#getScavengeThreshold */ +usqInt +getScavengeThreshold(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(scavengeThreshold); +} + + +/* Answer the header of the argument even though + it may have its header word in a forwarding block + (which shouldn't happen with Spur). */ + + /* Spur64BitCoMemoryManager>>#headerWhileForwardingOf: */ +static NoDbgRegParms sqLong +headerWhileForwardingOf(sqInt aCompiledMethodObjOop) +{ + assert(!(isForwarded(aCompiledMethodObjOop))); + return long64At((void *)(aCompiledMethodObjOop)); +} + + +/* Answer true if the argument contains indexable bytes. See comment in + formatOf: + */ +/* Note: Includes CompiledMethods. */ + + /* Spur64BitCoMemoryManager>>#isBytes: */ +sqInt +isBytes(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())); +} + + +/* A lenient tester of forwarded class indices for inline cache management in + the Cogit. + */ + + /* Spur64BitCoMemoryManager>>#isForwardedClassIndex: */ +sqInt +isForwardedClassIndex(sqInt maybeClassIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt entry; + sqInt fieldIndex; + + if ((((usqInt)maybeClassIndex)) >= (1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift())))) { + return 0; + } + fieldIndex = ((usqInt)(maybeClassIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return 0; + } + fieldIndex = maybeClassIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + entry = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + return (!((longAt((void *)(entry))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))); +} + + /* Spur64BitCoMemoryManager>>#isImmediateClass: */ +int +isImmediateClass(sqInt classObj) +{ + return ((((usqInt)((((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) == (forwardedFormat()); +} + + +/* Answer if obj is young. Require that obj is non-immediate. Override to + filter-out Cog methods + */ + + /* Spur64BitCoMemoryManager>>#isReallyYoungObject: */ +sqInt +isReallyYoungObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(isNonImmediate(objOop)); + return (oopisLessThan(objOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(objOop, GIV(newSpaceStart))); +} + + +/* Answer the method header of a CompiledMethod object. + If the method has been cogged then the header is a pointer to + the CogMethod and the real header will be stored in the CogMethod. */ + + /* Spur64BitCoMemoryManager>>#methodHeaderOf: */ +sqInt +methodHeaderOf(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + + assert(isCompiledMethod(methodObj)); + header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + return ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); +} + + /* Spur64BitCoMemoryManager>>#needGCFlagAddress */ +usqInt +needGCFlagAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(needGCFlag)))); +} + + /* Spur64BitCoMemoryManager>>#printCantBeObject:on: */ +static NoDbgRegParms void +printCantBeObjecton(sqInt oop, FILE *aStream) +{ + char *where; + + fprintf(aStream, + "%p%s\n", + ((void *)oop), + ((where = whereIsMaybeCodeThing(oop)) + ? where + : (oop & 7 /* (allocationUnit - 1) */ + ? " is misaligned" + : whereIs(oop)))); +} + + /* Spur64BitCoMemoryManager>>#specialObjectsOopAddress */ +usqInt +specialObjectsOopAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(specialObjectsOop)))); +} + + +/* For the purposes of become: send selector to the cogit with obj1, obj2 + and aBool and answer the result. Undo forwarding for the selector. */ + + /* Spur64BitCoMemoryManager>>#withoutForwardingOn:and:with:sendToCogit: */ +sqInt +withoutForwardingOnandwithsendToCogit(sqInt obj1, sqInt obj2, sqInt aBool, sqInt (*selector)(sqInt,sqInt,sqInt)) +{ + sqInt targetA; + sqInt targetB; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj1)); + targetA = longAt((void *)((obj1 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(targetA & (tagMask())))) + && ((!((longAt((void *)(targetA))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + targetA = longAt((void *)((targetA + BaseHeaderSize) + (0U << (shiftForWord())))); + } + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj2)); + targetB = longAt((void *)((obj2 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(targetB & (tagMask())))) + && ((!((longAt((void *)(targetB))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + targetB = longAt((void *)((targetB + BaseHeaderSize) + (0U << (shiftForWord())))); + } + return selector(targetA, targetB, aBool); +} + + +/* Answer the address immediately following an object. */ + + /* Spur64BitMemoryManager>>#addressAfter: */ +static NoDbgRegParms usqInt +addressAfter(sqInt objOop) +{ + usqInt numSlots; + + numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + if (!numSlots) { + return (objOop + 8 /* allocationUnit */) + BaseHeaderSize; + } + if (numSlots == (numSlotsMask())) { + numSlots = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8; + } + return (objOop + BaseHeaderSize) + (((numSlots << (shiftForWord())))); +} + + +/* Answer the oop of a chunk of space in oldSpace with numSlots slots. Try + and allocate in a segment that already includes pinned objects. The header + of the + result will have been filled-in but not the contents. */ + + /* Spur64BitMemoryManager>>#allocateSlotsForPinningInOldSpace:bytes:format:classIndex: */ +static NoDbgRegParms sqInt +allocateSlotsForPinningInOldSpacebytesformatclassIndex(sqInt numSlots, usqInt totalBytes, sqInt formatField, sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt acceptedChunk; + sqInt acceptedNode; + sqInt child; + usqInt childBytes; + sqInt chunk; + usqInt index; + usqInt initialIndex; + sqInt lilliputian; + sqInt lilliputianSqInt; + sqInt next; + sqInt nextFreeChunk; + sqInt nextSqInt; + sqInt node; + sqInt prev; + + /* begin allocateOldSpaceChunkOfBytes:suchThat: */ + next = 0; + prev = 0; + + /* be optimistic (& don't wait for the write) */ + GIV(totalFreeOldSpace) -= totalBytes; + initialIndex = totalBytes / 8 /* allocationUnit */; + if ((initialIndex < 64 /* numFreeLists */) + && ((1ULL << initialIndex) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << initialIndex)) != 0)) { + if ((node = GIV(freeLists)[initialIndex])) { + prev = 0; + while (node != 0) { + assert(node == (startOfObject(node))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(node)); + next = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + if (((segmentContainingObj(node))->containsPinned)) { + if (prev) { + /* begin setNextFreeChunkOf:withValue:chunkBytes: */ + /* begin isLilliputianSize: */ + assert(totalBytes >= (BaseHeaderSize + (allocationUnit()))); + lilliputianSqInt = totalBytes == (BaseHeaderSize + 8 /* allocationUnit */); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((next == 0) + || (isFreeObject(next))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),next); + if ((next != 0) + && (!lilliputianSqInt)) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + } + } + else { + /* begin unlinkFreeChunk:atIndex:chunkBytes: */ + /* begin isLilliputianSize: */ + assert(totalBytes >= (BaseHeaderSize + (allocationUnit()))); + lilliputian = totalBytes == (BaseHeaderSize + 8 /* allocationUnit */); + + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(node)) == (initialIndex * (allocationUnit()))) + && ((initialIndex > 1) + && ((startOfObject(node)) == node))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[initialIndex] = ((nextSqInt = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))))); + if ((!lilliputian) + && (nextSqInt != 0)) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextSqInt)); + longAtput((void *)((nextSqInt + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + } + chunk = node; + goto l1; + } + prev = node; + node = next; + } + } + else { + GIV(freeListsMask) -= 1ULL << initialIndex; + } + } + + /* first search for free chunks of a multiple of chunkBytes in size */ + index = initialIndex; + while ((((index += initialIndex)) < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << index)) != 0)) { + if ((node = GIV(freeLists)[index])) { + prev = 0; + while (node != 0) { + assert(node == (startOfObject(node))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(node)); + next = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + if (((segmentContainingObj(node))->containsPinned)) { + if (prev) { + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((next == 0) + || (isFreeObject(next))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),next); + if (next) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + } + } + else { + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(node)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(node)) == node))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((nextSqInt = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))))); + if (nextSqInt) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextSqInt)); + longAtput((void *)((nextSqInt + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + } + freeChunkWithBytesat((index * 8 /* allocationUnit */) - totalBytes, (/* startOfObject: */ + ((byteAt((void *)(node + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? node - BaseHeaderSize + : node)) + totalBytes); + chunk = node; + goto l1; + } + prev = node; + node = next; + } + } + else { + GIV(freeListsMask) -= 1ULL << index; + } + } + } + + /* now get desperate and use the first that'll fit. + Note that because the minimum free size is 16 bytes (2 * allocationUnit), to + leave room for the forwarding pointer/next free link, we can only break chunks + that are at least 16 bytes larger, hence start at initialIndex + 2. */ + index = initialIndex + 1; + while ((((index += 1)) < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << index)) != 0)) { + if ((node = GIV(freeLists)[index])) { + prev = 0; + while (node != 0) { + assert(node == (startOfObject(node))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(node)); + next = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + if (((segmentContainingObj(node))->containsPinned)) { + if (prev) { + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((next == 0) + || (isFreeObject(next))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),next); + if (next) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + } + } + else { + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(node)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(node)) == node))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((nextSqInt = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))))); + if (nextSqInt) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextSqInt)); + longAtput((void *)((nextSqInt + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + } + freeChunkWithBytesat((index * 8 /* allocationUnit */) - totalBytes, (/* startOfObject: */ + ((byteAt((void *)(node + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? node - BaseHeaderSize + : node)) + totalBytes); + chunk = node; + goto l1; + } + prev = node; + node = next; + } + } + else { + GIV(freeListsMask) -= 1ULL << index; + } + } + } + } + + /* Large chunk, or no space on small free lists. Search the large chunk list. + Large chunk list organized as a tree, each node of which is a list of chunks + of the same size. Beneath the node are smaller and larger blocks. + When the search ends parent should hold the smallest chunk at least as + large as chunkBytes, or 0 if none. acceptedChunk and acceptedNode save + us from having to back-up when the acceptanceBlock filters-out all nodes + of the right size, but there are nodes of the wrong size it does accept. */ + child = GIV(freeLists)[0]; + node = (acceptedChunk = (acceptedNode = 0)); + while (child != 0) { + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(child)); + childBytes = bytesInBody(child); + if (childBytes == totalBytes) { + node = child; + while (1) { + prev = node; + node = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + if (!(node != 0)) break; + if (((segmentContainingObj(node))->containsPinned)) { + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(node)); + nextFreeChunk = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + } + chunk = /* startOfObject: */ + ((byteAt((void *)(node + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? node - BaseHeaderSize + : node); + goto l1; + } + } + if (((segmentContainingObj(child))->containsPinned)) { + next = longAt((void *)((child + BaseHeaderSize) + (0U << (shiftForWord())))); + if (next) { + inFreeTreeReplacewith(child, next); + } + else { + unlinkSolitaryFreeTreeNode(child); + } + + /* no list; remove the interior node + list; replace node with it */ + chunk = /* startOfObject: */ + ((byteAt((void *)(child + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? child - BaseHeaderSize + : child); + goto l1; + } + } + + /* size match; try to remove from list at node. */ + if (child) { + /* Note that because the minimum free size is 16 bytes (2 * allocationUnit), to + leave room for the forwarding pointer/next free link, we can only break chunks + that are at least 16 bytes larger, hence reject chunks < 2 * allocationUnit larger. */ + if (childBytes <= (totalBytes + 8 /* allocationUnit */)) { + child = longAt((void *)((child + BaseHeaderSize) + (4U << (shiftForWord())))); + } + else { + node = child; + child = longAt((void *)((node + BaseHeaderSize) + (3U << (shiftForWord())))); + if (!acceptedNode) { + acceptedChunk = node; + + /* first search the list. */ + do { + acceptedChunk = longAt((void *)((acceptedChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + if ((acceptedChunk != 0) + && (((segmentContainingObj(acceptedChunk))->containsPinned))) { + acceptedNode = node; + } + } while((acceptedChunk != 0) + && (acceptedNode == 0)); + + /* nothing on the list; will the node do? This prefers + acceptable nodes higher up the tree over acceptable + list elements further down, but we haven't got all day... */ + if ((acceptedNode == 0) + && (((segmentContainingObj(node))->containsPinned))) { + acceptedNode = node; + + /* break out of loop now we have an acceptedNode */ + child = 0; + } + } + } + } + } + if (acceptedNode) { + if (acceptedChunk) { + assert((bytesInBody(acceptedChunk)) >= (totalBytes + (allocationUnit()))); + while (1) { + next = longAt((void *)((acceptedNode + BaseHeaderSize) + (0U << (shiftForWord())))); + if (!(next != acceptedChunk)) break; + acceptedNode = next; + } + nextFreeChunk = longAt((void *)((acceptedChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(acceptedNode)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((acceptedNode + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((acceptedNode == 0) + || (isFreeObject(acceptedNode))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),acceptedNode); + } + freeChunkWithBytesat((bytesInBody(acceptedChunk)) - totalBytes, (/* startOfObject: */ + ((byteAt((void *)(acceptedChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? acceptedChunk - BaseHeaderSize + : acceptedChunk)) + totalBytes); + chunk = /* startOfObject: */ + ((byteAt((void *)(acceptedChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? acceptedChunk - BaseHeaderSize + : acceptedChunk); + goto l1; + } + next = longAt((void *)((acceptedNode + BaseHeaderSize) + (0U << (shiftForWord())))); + if (next) { + inFreeTreeReplacewith(acceptedNode, next); + } + else { + unlinkSolitaryFreeTreeNode(acceptedNode); + } + + /* no list; remove the interior node + list; replace node with it */ + assert((bytesInBody(acceptedNode)) >= (totalBytes + (allocationUnit()))); + freeChunkWithBytesat((bytesInBody(acceptedNode)) - totalBytes, (/* startOfObject: */ + ((byteAt((void *)(acceptedNode + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? acceptedNode - BaseHeaderSize + : acceptedNode)) + totalBytes); + chunk = /* startOfObject: */ + ((byteAt((void *)(acceptedNode + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? acceptedNode - BaseHeaderSize + : acceptedNode); + goto l1; + } + + /* optimism was unfounded */ + GIV(totalFreeOldSpace) += totalBytes; + chunk = null; + /* end allocateOldSpaceChunkOfBytes:suchThat: */ +l1: + if (!chunk) { + chunk = allocateOldSpaceChunkOfBytes(totalBytes); + if (!chunk) { + return null; + } + ((segmentContainingObj(chunk))->containsPinned = 1); + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(chunk),numSlots + ((((usqInt)((numSlotsMask())) << (numSlotsFullShift()))))); + longAtput((void *)(chunk + BaseHeaderSize),(((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex) | (1U << (pinnedBitShift()))); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return chunk + BaseHeaderSize; + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + longAtput((void *)(chunk),(((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex) | (1U << (pinnedBitShift()))); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return chunk; +} + + +/* Answer the given integer with its bytes in the reverse order. */ + + /* Spur64BitMemoryManager>>#byteSwapped: */ +sqInt +byteSwapped(sqInt w) +{ + return SQ_SWAP_8_BYTES(w); +} + + +/* Answer the total number of bytes in an object including header and + possible overflow size header. + */ + + /* Spur64BitMemoryManager>>#bytesInBody: */ +static NoDbgRegParms usqInt +bytesInBody(sqInt objOop) +{ + usqLong header; + usqLong headerNumSlots; + usqInt numSlots; + + header = longAt((void *)(objOop)); + headerNumSlots = (header) >> (numSlotsFullShift()); + numSlots = (headerNumSlots == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : (headerNumSlots + ? headerNumSlots + : 1)); + return ((numSlots << (shiftForWord()))) + ((headerNumSlots == (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)); +} + + +/* Answer the default amount of memory to allocate for the eden space. + The actual value can be set via vmParameterAt: and/or a preference in the + ini file. + The shootout tests seem to plateau at 5 or 6Mb. + + Originally, both the 32-bit and 64-bit versions used the same 4Mb default. + Measuring the simulator on image start-up, the 64-bit system's eden at the + same point in start-up + (the first copyBits) is only 8% larger in bytes because it allocates 26% + fewer objects. + Some 21% of the objects in the 32-bit version's eden are large integers + and floats that + are representable as 64-bit immediates. + + But when running benchmarks such as the computer language shootout's + binary trees, + using the same amount of memory for the 64-bit system causes a significant + slow-down and a lot of compactions. So we now use 4Mb for 32-bits and 7Mb + for 64-bits. */ + + /* Spur64BitMemoryManager>>#defaultEdenBytes */ +static int +defaultEdenBytes(void) +{ + return 0x700000; +} + + +/* Answer the tag used in lookup caches for a receiver. This is the + receiver's classIndex. */ + + /* Spur64BitMemoryManager>>#fetchClassTagOf: */ +sqInt +fetchClassTagOf(sqInt oop) +{ + sqInt tagBits; + + return ((tagBits = oop & (tagMask())) + ? tagBits + : (longAt((void *)(oop))) & (classIndexMask())); +} + + /* Spur64BitMemoryManager>>#floatObjectOf: */ +sqInt +floatObjectOf(double aFloat) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newFloatObj; + usqInt newObj; + usqInt numBytes; + sqInt numSlots; + usqLong rawFloat; + usqLong rot; + + if (isSmallFloatValue(aFloat)) { + /* begin smallFloatObjectOf: */ + assert(isSmallFloatValue(aFloat)); + memcpy((&rawFloat), (&aFloat), sizeof(rawFloat)); + + /* begin smallFloatObjectOfBits: */ + assert(isSmallFloatValueBits(rawFloat)); + rot = (((rawFloat) >> 0x3F) & 1) + ((rawFloat << 1)); + if (rot > 1) { + rot -= (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + assert(rot > 0); + } + + /* a.k.a. ~= +/-0.0 */ + return ((rot << (numTagBits()))) + (smallFloatTag()); + } + numSlots = (sizeof(double)) / BytesPerOop; + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((numSlots >= 0) + && ((knownClassAtIndex(ClassFloatCompactIndex)) != GIV(nilObj))); + assert((firstLongFormat()) == (instSpecOfClass(knownClassAtIndex(ClassFloatCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newFloatObj = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((firstLongFormat())) << (formatShift()))))) + ClassFloatCompactIndex); + GIV(freeStart) += numBytes; + newFloatObj = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + storeFloatAtfrom(newFloatObj + BaseHeaderSize, aFloat); + return newFloatObj; +} + + +/* Answer the 64-bit value of the argument as raw bits. */ + + /* Spur64BitMemoryManager>>#floatValueBitsOf: */ +static NoDbgRegParms sqLong +floatValueBitsOf(sqInt floatOop) +{ + usqLong rot; + + assert(isFloatInstance(floatOop)); + if (floatOop & (tagMask())) { + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOop)); + rot = ((((usqInt)floatOop))) >> (numTagBits()); + if (rot > 1) { + rot += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + rot = ((rot << 0x3F)) + (((((usqInt)rot))) >> 1); + return rot; + } + return long64At((void *)((floatOop + BaseHeaderSize))); +} + + +/* Answer the C double precision floating point value of the argument, + or fail if it is not a Float, and answer 0. + Note: May be called by translated primitive code. */ + + /* Spur64BitMemoryManager>>#floatValueOf: */ +double +floatValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + double result; + sqInt tagBits; + double value; + + if ((tagBits = oop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + return value; + } + } + else { + if (((longAt((void *)(oop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(oop + BaseHeaderSize, result); + return result; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0.0; +} + + +/* This is a horrible hack for getting to the first object in eden if + pastSpace is almost full. + If there is only one (64-bit) word at the end of pastSpace there is no + room for a full + bridge, but there is room for this hack. */ + + /* Spur64BitMemoryManager>>#hackSlimBridgeTo:at: */ +static NoDbgRegParms void +hackSlimBridgeToat(sqInt objOop, sqInt startAddress) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(oopisLessThan(startAddress, objOop)); + assert(oopisLessThanOrEqualTo(objOop, GIV(oldSpaceStart))); + assert(((startAddress + (allocationUnit())) == objOop) + || (((startAddress + (allocationUnit())) + (allocationUnit())) == objOop)); + longAtput((void *)(startAddress),((startAddress + 8 /* allocationUnit */) == objOop + ? ((sqInt)((usqInt)((numSlotsMask())) << (numSlotsFullShift()))) + : ((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + 1)); +} + + +/* A negative header selects the alternate bytecode set. */ + + /* Spur64BitMemoryManager>>#headerIndicatesAlternateBytecodeSet: */ +int +headerIndicatesAlternateBytecodeSet(sqInt methodHeader) +{ + return (((sqLong) methodHeader)) < 0; +} + + +/* must have room for a header (single or double) plus the next free pointer */ + + /* Spur64BitMemoryManager>>#initFreeChunkWithBytes:at: */ +static NoDbgRegParms sqInt +initFreeChunkWithBytesat(usqLong numBytes, sqInt address) +{ + sqLong numSlots; + + assert(((numBytes % (allocationUnit())) == 0) + && (numBytes >= (BaseHeaderSize + BytesPerOop))); + + /* double header */ + if (numBytes >= (((((usqInt)((numSlotsMask())) << (shiftForWord())))) + BaseHeaderSize)) { + numSlots = ((usqLong)(((numBytes - BaseHeaderSize) - BaseHeaderSize))) >> (shiftForWord()); + longAtput((void *)(address),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(address + 8),((sqInt)((usqInt)((numSlotsMask())) << (numSlotsFullShift())))); + return address + 8; + } + + /* single header */ + numSlots = ((usqLong)((numBytes - BaseHeaderSize))) >> (shiftForWord()); + assert(numSlots < (numSlotsMask())); + longAtput((void *)(address),((sqLong)((usqLong)(numSlots) << (numSlotsFullShift())))); + return address; +} + + +/* Must have room for a double header or a short object with the forwarding + slot (16 bytes either way). + */ + + /* Spur64BitMemoryManager>>#initSegmentBridgeWithBytes:at: */ +static NoDbgRegParms void +initSegmentBridgeWithBytesat(usqLong numBytes, sqInt address) +{ + sqLong numSlots; + + assert(((numBytes % (allocationUnit())) == 0) + && (numBytes >= (BaseHeaderSize + BaseHeaderSize))); + numSlots = ((usqLong)(((numBytes - BaseHeaderSize) - BaseHeaderSize))) >> (shiftForWord()); + if (numSlots) { + longAtput((void *)(address),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(address + BaseHeaderSize),(((((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + (1U << (pinnedBitShift()))) + (1ULL << (markedBitFullShift()))) + ((((usqInt)((sixtyFourBitIndexableFormat())) << (formatShift()))))) + (segmentBridgePun())); + } + else { + longAtput((void *)(address),(((1U << (pinnedBitShift())) + (1ULL << (markedBitFullShift()))) + ((((usqInt)((sixtyFourBitIndexableFormat())) << (formatShift()))))) + (segmentBridgePun())); + } +} + + +/* Allocate an instance of a variable class, excepting CompiledMethod. */ + + /* Spur64BitMemoryManager>>#instantiateClass:indexableSize: */ +sqInt +instantiateClassindexableSize(sqInt classObj, usqInt nElements) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt err; + sqInt fillValue; + int hash; + sqInt instSpec; + sqInt newObj; + usqInt newObjUsqInt; + usqInt numBytes; + usqInt numSlots; + usqInt p; + usqInt toDoLimit; + + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + fillValue = 0; + switch (instSpec) { + case arrayFormat(): + numSlots = nElements; + fillValue = GIV(nilObj); + break; + case indexablePointersFormat(): + case weakArrayFormat(): + numSlots = (classFormat & ((1U << (fixedFieldsFieldWidth())) - 1)) + nElements; + fillValue = GIV(nilObj); + break; + case sixtyFourBitIndexableFormat(): + numSlots = nElements; + break; + case firstLongFormat(): + if ((classIndex == ClassFloatCompactIndex) + && (nElements != 2)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return null; + } + numSlots = (nElements + 1) / 2; + instSpec += nElements & 1; + break; + case firstShortFormat(): + numSlots = (nElements + 3) / 4; + instSpec += (4 - nElements) & 3; + break; + case firstByteFormat(): + numSlots = (nElements + 7) / 8; + instSpec += (8 - nElements) & 7; + break; + default: + if ((nElements != 0) + || (instSpec > 5 /* lastPointerFormat */)) { + return null; + } + numSlots = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + fillValue = GIV(nilObj); + } + + /* non-indexable + Some Squeak images include funky fixed subclasses of abstract variable + superclasses. e.g. DirectoryEntry as a subclass of ArrayedCollection. + The (Threaded)FFIPlugin expects to be able to instantiate ExternalData via + this method. + Hence allow fixed classes to be instantiated here iff nElements = 0. */ + if (!classIndex) { + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + return null; + } + } + if (numSlots > ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (numSlots > (0x10000000000LL)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return null; + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpec, classIndex); + } + else { + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if (((numSlots) >> 56) > 0) { + newObj = null; + goto l1; + } + newObjUsqInt = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObjUsqInt = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, instSpec, classIndex); + goto l1; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObjUsqInt),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObjUsqInt),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObjUsqInt % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newObj = newObjUsqInt; + /* end allocateSlots:format:classIndex: */ +l1:; + } + if (newObj) { + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),fillValue); + } + } + return newObj; +} + + +/* Convert the integer value, assumed to be in SmallInteger range, into a + tagged SmallInteger object. + In C, use a shift and an add to set the tag bit. + In Smalltalk we have to work harder because the simulator works with + strictly positive bit patterns. */ + + /* Spur64BitMemoryManager>>#integerObjectOf: */ +sqInt +integerObjectOf(sqInt value) +{ + return (((((usqInt)value)) << (numTagBits()))) + 1; +} + + +/* Translator produces 'oop >> 3' */ + + /* Spur64BitMemoryManager>>#integerValueOf: */ +sqInt +integerValueOf(sqInt oop) +{ + return (((((usqInt)(oop)) >> 0x3F)) == 1 + ? ((((((-(numTagBits())) < 0) ? (((usqInt)(oop)) >> (-(-(numTagBits())))) : ((((usqInt)(oop) << (-(numTagBits()))))))) & 0x1FFFFFFFFFFFFFFFLL) - 0x1FFFFFFFFFFFFFFFLL) - 1 + : (((-(numTagBits())) < 0) ? (((usqInt)(oop)) >> (-(-(numTagBits())))) : (((sqInt)((usqInt)(oop) << (-(numTagBits()))))))); +} + + /* Spur64BitMemoryManager>>#isFloatInstance: */ +static NoDbgRegParms int +isFloatInstance(sqInt oop) +{ + sqInt tagBits; + + return ((tagBits = oop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(oop))) & (classIndexMask())) == ClassFloatCompactIndex); +} + + /* Spur64BitMemoryManager>>#isImmediateFloat: */ +static NoDbgRegParms int +isImmediateFloat(sqInt oop) +{ + return ((oop & (smallFloatTag())) != 0); +} + + /* Spur64BitMemoryManager>>#isIntegerObject: */ +int +isIntegerObject(sqInt oop) +{ + return ((oop & (smallIntegerTag())) != 0); +} + + +/* Answer if the given value can be represented as a Smalltalk integer value. + In 64-bits we use a 3 bit tag which leaves 61 bits for 2's complement + signed integers. In C, use a shift add and mask to test if the top 4 bits + are all the same. + Since 16rFFFFFFFFFFFFFFFF >> 60 = 16rF the computation intValue >> 60 + 1 + bitAnd: 16rF + maps in-range -ve values to 0 and in-range +ve values to 1. */ + + /* Spur64BitMemoryManager>>#isIntegerValue: */ +int +isIntegerValue(sqInt intValue) +{ + return (((((usqInt)(intValue)) >> 60) + 1) & 15) <= 1; +} + + +/* To have a prev pointer, which follows the next pointer, we need at least + two slots. + */ + + /* Spur64BitMemoryManager>>#isLilliputianSize: */ +static NoDbgRegParms int +isLilliputianSize(sqInt chunkBytes) +{ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + return chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */); +} + + /* Spur64BitMemoryManager>>#isSmallFloatValueBits: */ +static NoDbgRegParms int +isSmallFloatValueBits(usqLong rawFloatBits) +{ + usqInt exponent; + + exponent = ((rawFloatBits) >> (smallFloatMantissaBits())) & 0x7FF; + return (exponent > (smallFloatExponentOffset()) + ? exponent <= (0xFF + (smallFloatExponentOffset())) + : (rawFloatBits & ((1ULL << (smallFloatMantissaBits())) - 1) + ? exponent == (smallFloatExponentOffset()) + : exponent == 0)); +} + + /* Spur64BitMemoryManager>>#isSmallFloatValue: */ +static NoDbgRegParms int +isSmallFloatValue(double aFloat) +{ + usqInt exponent; + usqLong rawFloat; + + memcpy((&rawFloat), (&aFloat), sizeof(rawFloat)); + exponent = ((rawFloat) >> (smallFloatMantissaBits())) & 0x7FF; + return (exponent > (smallFloatExponentOffset()) + ? exponent <= (0xFF + (smallFloatExponentOffset())) + : (rawFloat & ((1ULL << (smallFloatMantissaBits())) - 1) + ? exponent == (smallFloatExponentOffset()) + : exponent == 0)); +} + + +/* Answer if the argument contains only indexable words (no oops). See + comment in formatOf: + */ + + /* Spur64BitMemoryManager>>#isWordsNonImm: */ +static NoDbgRegParms int +isWordsNonImm(sqInt objOop) +{ + return ((((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1))); +} + + +/* Answer the number of indexable units in the given object. + For a CompiledMethod, the size of the method header (in bytes) + should be subtracted from the result of this method. */ + + /* Spur64BitMemoryManager>>#lengthOf:format: */ +static NoDbgRegParms sqInt +lengthOfformat(sqInt objOop, sqInt fmt) +{ + usqInt numSlots; + usqInt numSlotsUsqInt; + + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + +/* If floatOrInt is an integer and we enable mixed arithmetic in primitives, + then convert it to a C double float and return it. + If it is a Float, then load its value and return it. + Otherwise fail -- ie return with primErrorCode non-zero. */ + + /* Spur64BitMemoryManager>>#loadFloatOrIntFrom: */ +static NoDbgRegParms double +loadFloatOrIntFrom(sqInt floatOrIntOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + double result; + sqInt tagBits; + double value; + + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + return value; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + return ((double) ((floatOrIntOop >> 3)) ); + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + return result; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0.0; +} + + +/* Answer the maximum number of slots we are willing to attempt to allocate + in an object. + Must fit in 56-bits; c.f. bytesInObject: */ + + /* Spur64BitMemoryManager>>#maxSlotsForAlloc */ +static sqInt +maxSlotsForAlloc(void) +{ + return 0x10000000000LL; +} + + +/* Answer the C double precision floating point value of the argument, + which *must* be something for which self isFloatInstance: answers true. + Note: May be called by translated primitive code. */ + + /* Spur64BitMemoryManager>>#noFailFloatValueOf: */ +double +noFailFloatValueOf(sqInt aFloatOop) +{ + usqLong bits; + double result; + + assert(isFloatInstance(aFloatOop)); + if (aFloatOop & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(aFloatOop)); + bits = ((((usqInt)aFloatOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&result), (&bits), sizeof(result)); + } + else { + fetchFloatAtinto(aFloatOop + BaseHeaderSize, result); + } + return result; +} + + +/* Answer the number of free lists. We use freeListsMask, a bitmap, to avoid + reading empty list heads. This should fit in a machine word to end up in a + register during free chunk allocation. */ + + /* Spur64BitMemoryManager>>#numFreeLists */ +static sqInt +numFreeLists(void) +{ + return 64; +} + + +/* Object parsing. + 1. all objects have at least a word following the header, for a forwarding + pointer. 2. objects with an overflow size have a preceding word with a + saturated numSlots. If the word + following an object doesn't have a saturated numSlots field it must be a + single-header object. + If the word following does have a saturated numSlots it must be the + overflow size word. + + This variation on objectAfter:limit: allows for a single (64-bit) word + bridge which may be needed + to bridge from an almost full pastSpace to eden. It is only used in the + flat enumerators that use + startAddressForBridgedHeapEnumeration and enumerate over pastSpace, eden + and oldSpace + in that order. Note that the order for allObjects, and allInstances + enumerates over oldSpace first. + + This hack is cheap. It increases the size of the objectAfter code, but + saves two extra copies of + the inner loop, since the inner loop now enumerates over all of pastSpace, + eden and oldSpace. + The test for a slim bridge is only performed if applied to an overflow + header, and typically only + 1 in 400 objects have overflow headers in 32-bits, 1 in 500 in 64-bits. + The complication is that + image segment loading evaporates the word array by setting the overflow + slots to 1, and this + is ambiguous with a slimbridge. The resolution is that if the segmentArray + has an overflow header, + and is in new space, then its slot size can be zeroed and its overflow + header changed to a slimbridge. + + At some point we should allow slimbridges (slivers?) throughout object + memory, and use them to + provide object alignment by slimbridges (slivers?) padding up to the + following (aligned) object. + */ + + /* Spur64BitMemoryManager>>#objectAfterMaybeSlimBridge:limit: */ +static NoDbgRegParms sqInt +objectAfterMaybeSlimBridgelimit(sqInt objOop, sqInt limit) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, limit)) { + return limit; + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); +} + + +/* Answer the C double precision floating point value of the argument, a + SmallFloat. See section 61-bit Immediate Floats in the SpurMemoryManager + class comment. + msb lsb + Decode: [8expsubset][52mantissa][1s][3tags] + shift away tags: [ 000 ][8expsubset][52mantissa][1s] + add exponent offset: [ 11 exponent ][52mantissa][1s] + rot sign: [1s][ 11 exponent ][52mantissa] */ + + /* Spur64BitMemoryManager>>#smallFloatValueOf: */ +static NoDbgRegParms double +smallFloatValueOf(sqInt oop) +{ + usqLong bits; + double value; + + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + return value; +} + + +/* Answer the total number of bytes in an object without an overflow header, + including header bytes. + */ + + /* Spur64BitMemoryManager>>#smallObjectBytesForSlots: */ +usqInt +smallObjectBytesForSlots(sqInt numSlots) +{ + return BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); +} + + /* Spur64BitMemoryManager>>#wordIndexableFormat */ +static sqInt +wordIndexableFormat(void) +{ + return sixtyFourBitIndexableFormat(); +} + + /* Spur64BitMemoryManager>>#wordSizeClassIndexPun */ +static sqInt +wordSizeClassIndexPun(void) +{ + return sixtyFourBitLongsClassIndexPun(); +} + + +/* ephemeronCorpse is the corpse of an ephemeron that was copied and + forwarded. Later on its surviving copy must be scanned to nil weak + references. Thread the corpse onto the weakList. Later, the weakList can + be followed, and + the forwarding pointer followed to locate the survivor. */ +/* Should be too infrequent to lower icache density of copyAndForward: */ + + /* SpurGenerationScavenger>>#addToEphemeronList: */ +static NoDbgRegParms NeverInline void +addToEphemeronList(sqInt ephemeronCorpse) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ephemeronListOffset; + + assert(isYetToBeScavenged(keyOfEphemeron(followForwarded(ephemeronCorpse)))); + ephemeronListOffset = (!(GIV(ephemeronList)) + ? 0 + : GIV(ephemeronList)); + + /* begin setCorpseOffsetOf:to: */ + assert(isYoung(ephemeronCorpse)); + assert(isForwarded(ephemeronCorpse)); + long64Atput((void *)(ephemeronCorpse),headerForSlotshashformatclassIndex(byteAt((void *)(ephemeronCorpse + (numSlotsFieldByteOffset()))), ((usqInt)(ephemeronListOffset)) >> 8, ephemeronListOffset & 0xFF, isForwardedObjectClassIndexPun())); + GIV(ephemeronList) = (((usqInt)((ephemeronCorpse - GIV(newSpaceStart)))) >> 3 /* shiftForAllocationUnit */) + 1; + assert((firstCorpse(GIV(ephemeronList))) == ephemeronCorpse); +} + + +/* weakCorpse is the corpse of a weak array that was copied and forwarded. + Later on its surviving copy must be scanned to nil weak references. + Thread the corpse onto the weakList. Later, the weakList can be followed, + and the forwarding pointer followed to locate the survivor. */ +/* Should be too infrequent to lower icache density of copyAndForward: */ + + /* SpurGenerationScavenger>>#addToWeakList: */ +static NoDbgRegParms NeverInline void +addToWeakList(sqInt weakCorpse) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt weakListOffset; + + weakListOffset = (!(GIV(weakList)) + ? 0 + : GIV(weakList)); + + /* begin setCorpseOffsetOf:to: */ + assert(isYoung(weakCorpse)); + assert(isForwarded(weakCorpse)); + long64Atput((void *)(weakCorpse),headerForSlotshashformatclassIndex(byteAt((void *)(weakCorpse + (numSlotsFieldByteOffset()))), ((usqInt)(weakListOffset)) >> 8, weakListOffset & 0xFF, isForwardedObjectClassIndexPun())); + GIV(weakList) = (((usqInt)((weakCorpse - GIV(newSpaceStart)))) >> 3 /* shiftForAllocationUnit */) + 1; + assert((firstCorpse(GIV(weakList))) == weakCorpse); +} + + /* SpurGenerationScavenger>>#allNewSpaceObjectsHaveZeroRTRefCount */ +static sqInt +allNewSpaceObjectsHaveZeroRTRefCount(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + /* begin allNewSpaceObjectsDo: */ + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l2; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOopSqInt, GIV(freeStart))) { + assert(isBridgeOrEnumerableObjectNoAssert(objOopSqInt)); + if ((((usqInt)((byteAt((void *)(objOopSqInt + (formatFieldByteOffset())))))) >> (rememberedBitByteShift())) > 0) { + return 0; + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOopSqInt = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + /* end allNewSpaceEntitiesDo: */ +l2: + return 1; +} + + /* SpurGenerationScavenger>>#allWeakSurvivorsOnWeakList */ +static sqInt +allWeakSurvivorsOnWeakList(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + usqInt corpse; + sqInt followingWord; + usqInt followingWordAddress; + sqInt format; + sqInt limit; + sqInt listOffset; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + + /* begin allFutureSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + address = (GIV(futureSpace).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + limit = GIV(futureSurvivorStart); + while (oopisLessThan(objOop, limit)) { + /* begin isWeakNonImm: */ + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (format == (weakArrayFormat())) { + /* begin is:onWeaklingList: */ + corpse = ((((usqInt)((GIV(weakList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + while (corpse) { + if (objOop == (followForwarded(corpse))) { + goto l1; + } + + /* begin nextCorpseOrNil: */ + assert(isYoung(corpse)); + listOffset = ((((usqInt)(((long32At((void *)(corpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(corpse + (formatFieldByteOffset())))); + corpse = (listOffset + ? ((((usqInt)((listOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0); + } + return 0; + return 0; +l1:; + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, limit)) { + objOop = limit; + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l2:; + } + return 1; +} + + +/* Some time in every scavenger's life there may come a time when someone + writes code that stresses + the remembered table. One might conclude that if the remembered table is + full, then the right thing + to do is simply to tenure everything, emptying the remembered table. But + in some circumstances this + can be counter-productive, and result in the same situation arising soon + after tenuring everything. + Instead, we can try and selectively prune the remembered table, tenuring + only those objects that + are referenced by many objects in the remembered table. That's what this + algorithm does. It + reference counts young objects referenced from the remembered set, and + then sets a threshold + used to tenure objects oft referenced from the remembered set, thereby + allowing the remembered + set to shrink, while not tenuring everything. + + Once in a network monitoring application in a galaxy not dissimilar from + the one this code inhabits, + a tree of nodes referring to large integers was in precisely this + situation. The nodes were old, and + the integers were in new space. Some of the nodes referred to shared + numbers, some their own + unique numbers. The numbers were updated frequently. Were new space simply + tenured when the + remembered table was full, the remembered table would soon fill up as new + numbers were computed. + Only by selectively pruning the remembered table of nodes that shared + data, was a balance achieved + whereby the remembered table population was kept small, and tenuring rates + were low. */ + + /* SpurGenerationScavenger>>#computeRefCountToShrinkRT */ +static NeverInline void +computeRefCountToShrinkRT(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt count; + sqInt elephant; + sqInt entirePopulation; + int i; + sqInt iSqInt; + sqInt j; + long population[MaxRTRefCount + 1]; + sqInt refCount; + sqInt referent; + sqInt referentSqInt; + sqInt toDoLimit; + + memset(population, 0, (sizeof(long)) * (MaxRTRefCount + 1)); + assert(allNewSpaceObjectsHaveZeroRTRefCount()); + + /* begin referenceCountRememberedReferents: */ + for (iSqInt = 0; iSqInt < GIV(rememberedSetSize); iSqInt += 1) { + elephant = GIV(rememberedSet)[iSqInt]; + if ((!((longAt((void *)(elephant))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(elephant)); + referentSqInt = longAt((void *)((elephant + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + elephant = referentSqInt; + if (((elephant & (tagMask())) != 0)) { + elephant = GIV(nilObj); + } + + /* take care if elephant forwarded to an immediate */ + GIV(rememberedSet)[iSqInt] = elephant; + } + toDoLimit = (numPointerSlotsOf(elephant)) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + referent = longAt((void *)((elephant + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (/* isReallyYoung: */ + ((!(referent & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)), + (oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))))) { + refCount = ((usqInt)((byteAt((void *)(referent + (formatFieldByteOffset())))))) >> (rememberedBitByteShift()); + if (refCount < MaxRTRefCount) { + if (refCount > 0) { + population[refCount] = ((population[refCount]) - 1); + } + refCount += 1; + + /* begin rtRefCountOf:put: */ + assert(isYoungObject(referent)); + byteAtput((void *)(referent + (formatFieldByteOffset())),((byteAt((void *)(referent + (formatFieldByteOffset())))) & (formatMask())) + ((((usqInt)(refCount) << (rememberedBitByteShift()))))); + population[refCount] = ((population[refCount]) + 1); + } + } + } + } + + /* begin setRefCountToShrinkRT: */ + assert((population[0]) == 0); + entirePopulation = 0; + for (j = 1; j <= MaxRTRefCount; j += 1) { + entirePopulation += population[j]; + } + count = 0; + i = MaxRTRefCount + 1; + while ((count < (entirePopulation / 2)) + && (((i -= 1)) >= 0)) { + count += population[i]; + } + GIV(refCountToShrinkRT) = ((i < 0) ? 0 : i); +} + + +/* A special version of copyAndForward: for objects in the mournQueue. If + we're in the good times tenuring regime then copy to futureSpace, + otherwise tenure. + Also, don't repeat any of the ephemeron processing. */ + + /* SpurGenerationScavenger>>#copyAndForwardMourner: */ +static NoDbgRegParms sqInt +copyAndForwardMourner(sqInt mourner) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytesInObj; + sqInt format; + sqInt newLocation; + sqInt newStart; + sqInt objOop; + usqInt startOfSurvivor; + int tenure; + + assert((isInEden(mourner)) + || (isInPastSpace(mourner))); + + /* cog methods should be excluded. */ + bytesInObj = bytesInBody(mourner); + format = (byteAt((void *)(mourner + (formatFieldByteOffset())))) & (formatMask()); + + /* Allow Slang to inline. */ + tenure = !((GIV(tenureCriterion) == TenureByAge) + && (GIV(tenureThreshold) == 0)); + if (tenure + || ((GIV(futureSurvivorStart) + bytesInObj) > ((GIV(futureSpace).limit)))) { + newLocation = copyToOldSpacebytesformat(mourner, bytesInObj, format); + } + else { + /* begin copyToFutureSpace:bytes: */ + /* we hope writes are cheap... */ + GIV(statSurvivorCount) += 1; + assert((GIV(futureSurvivorStart) + bytesInObj) <= ((GIV(futureSpace).limit))); + startOfSurvivor = /* startOfObject: */ + ((byteAt((void *)(mourner + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? mourner - BaseHeaderSize + : mourner); + newStart = GIV(futureSurvivorStart); + GIV(futureSurvivorStart) += bytesInObj; + memcpy(((void *)newStart), ((void *)startOfSurvivor), bytesInObj); + if (GIV(tenureCriterion) == TenureToShrinkRT) { + objOop = newStart + (mourner - startOfSurvivor); + + /* begin rtRefCountOf:put: */ + assert(isYoungObject(objOop)); + byteAtput((void *)(objOop + (formatFieldByteOffset())),((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) + (0U << (rememberedBitByteShift()))); + } + newLocation = newStart + (mourner - startOfSurvivor); + } + + /* begin forwardSurvivor:to: */ + assert(isInNewSpace(mourner)); + assert((isInFutureSpace(newLocation)) + || (isInOldSpace(newLocation))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(mourner)) + && (!(isForwarded(mourner)))); + assert(validStorePointerUncheckedArgs(0, mourner, newLocation)); + longAtput((void *)((mourner + BaseHeaderSize) + (0U << (shiftForWord()))),newLocation); + + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(mourner),((longAt((void *)(mourner))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); + return newLocation; +} + + +/* copyAndForward: survivor copies a survivor object either to + futureSurvivorSpace or, if it is to be promoted, to oldSpace. + It leaves a forwarding pointer behind. If the object is weak + then corpse is threaded onto the weakList for later treatment. */ + + /* SpurGenerationScavenger>>#copyAndForward: */ +static NoDbgRegParms sqInt +copyAndForward(sqInt survivor) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytesInObj; + sqInt format; + sqInt newLocation; + sqInt newStart; + sqInt objOop; + usqInt startOfSurvivor; + + assert((isInEden(survivor)) + || (isInPastSpace(survivor))); + + /* cog methods should be excluded. */ + bytesInObj = bytesInBody(survivor); + format = (byteAt((void *)(survivor + (formatFieldByteOffset())))) & (formatMask()); + if (((GIV(futureSurvivorStart) + bytesInObj) > ((GIV(futureSpace).limit))) + || (/* shouldBeTenured: */ + (GIV(tenureCriterion) == TenureByAge + ? oopisLessThan(survivor, GIV(tenureThreshold)) + : (GIV(tenureCriterion) == TenureToShrinkRT + ? (((usqInt)((byteAt((void *)(survivor + (formatFieldByteOffset())))))) >> (rememberedBitByteShift())) >= GIV(refCountToShrinkRT) + : (GIV(tenureCriterion) == TenureByClass + ? ((longAt((void *)(survivor))) & (classIndexMask())) == GIV(tenuringClassIndex) + : 0))))) { + newLocation = copyToOldSpacebytesformat(survivor, bytesInObj, format); + } + else { + /* begin copyToFutureSpace:bytes: */ + /* we hope writes are cheap... */ + GIV(statSurvivorCount) += 1; + assert((GIV(futureSurvivorStart) + bytesInObj) <= ((GIV(futureSpace).limit))); + startOfSurvivor = /* startOfObject: */ + ((byteAt((void *)(survivor + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? survivor - BaseHeaderSize + : survivor); + newStart = GIV(futureSurvivorStart); + GIV(futureSurvivorStart) += bytesInObj; + memcpy(((void *)newStart), ((void *)startOfSurvivor), bytesInObj); + if (GIV(tenureCriterion) == TenureToShrinkRT) { + objOop = newStart + (survivor - startOfSurvivor); + + /* begin rtRefCountOf:put: */ + assert(isYoungObject(objOop)); + byteAtput((void *)(objOop + (formatFieldByteOffset())),((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) + (0U << (rememberedBitByteShift()))); + } + newLocation = newStart + (survivor - startOfSurvivor); + } + + /* begin forwardSurvivor:to: */ + assert(isInNewSpace(survivor)); + assert((isInFutureSpace(newLocation)) + || (isInOldSpace(newLocation))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(survivor)) + && (!(isForwarded(survivor)))); + assert(validStorePointerUncheckedArgs(0, survivor, newLocation)); + longAtput((void *)((survivor + BaseHeaderSize) + (0U << (shiftForWord()))),newLocation); + + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(survivor),((longAt((void *)(survivor))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); + + /* if weak or ephemeron add to the relevant list for subsequent scanning. */ + if (((format >= (weakArrayFormat())) && (format <= (ephemeronFormat())))) { + if (format == (weakArrayFormat())) { + addToWeakList(survivor); + } + else { + assert(isEphemeronFormat(format)); + if (isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(newLocation)) + && (isObjEphemeron(newLocation))), + /* fetchPointer:ofObject: */ + longAt((void *)((newLocation + BaseHeaderSize) + (0U << (shiftForWord()))))))) { + addToEphemeronList(survivor); + } + } + } + return newLocation; +} + + +/* Copy survivor to oldSpace. Answer the new oop of the object. */ +/* Should be too infrequent to lower icache density of copyAndForward: */ + + /* SpurGenerationScavenger>>#copyToOldSpace:bytes:format: */ +static NoDbgRegParms NeverInline sqInt +copyToOldSpacebytesformat(sqInt survivor, sqInt bytesInObject, sqInt formatOfSurvivor) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt field; + sqInt newOop; + sqInt newStart; + sqInt nTenures; + sqInt p; + usqInt startOfSurvivor; + sqInt toDoLimit; + + assert((formatOfSurvivor == (formatOf(survivor))) + && (((!(isMarked(survivor))) + || (GIV(tenureCriterion) == MarkOnTenure)) + && ((GIV(tenureCriterion) == TenureToShrinkRT) + || ((!(isPinned(survivor))) + && (!(isRemembered(survivor))))))); + nTenures = GIV(statTenures); + startOfSurvivor = /* startOfObject: */ + ((byteAt((void *)(survivor + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? survivor - BaseHeaderSize + : survivor); + newStart = allocateOldSpaceChunkOfBytes(bytesInObject); + if (!newStart) { + growOldSpaceByAtLeast(0); + newStart = allocateOldSpaceChunkOfBytes(bytesInObject); + if (!newStart) { + error("out of memory"); + } + } + + /* manager checkFreeSpace. */ + memcpy(((void *)newStart), ((void *)startOfSurvivor), bytesInObject); + newOop = newStart + (survivor - startOfSurvivor); + if (GIV(tenureCriterion) >= (((TenureToShrinkRT < MarkOnTenure) ? TenureToShrinkRT : MarkOnTenure))) { + if (GIV(tenureCriterion) == TenureToShrinkRT) { + /* begin rtRefCountOf:put: */ + assert(isYoungObject(newOop)); + byteAtput((void *)(newOop + (formatFieldByteOffset())),((byteAt((void *)(newOop + (formatFieldByteOffset())))) & (formatMask())) + (0U << (rememberedBitByteShift()))); + } + if (GIV(tenureCriterion) == MarkOnTenure) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(newOop))); + byteAtput((void *)(newOop + (markBitsByteOffset())),(byteAt((void *)(newOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + } + } + GIV(statTenures) = nTenures + 1; + if (/* isAnyPointerFormat: */ + (formatOfSurvivor <= 5 /* lastPointerFormat */) + || (formatOfSurvivor >= (firstCompiledMethodFormat()))) { + /* A very quick and dirty scan to find young referents. If we misidentify bytes + in a CompiledMethod as young we don't care; it's unlikely, and a subsequent + scan of the rt will filter the object out. But it's good to filter here because + otherwise an attempt to shrink the RT may simply fill it up with new objects, + and here the data is likely in the cache. */ + toDoLimit = (bytesInObject - (survivor - startOfSurvivor)) - BytesPerWord; + for (p = BaseHeaderSize; p <= toDoLimit; p += BytesPerWord) { + field = longAt((void *)(survivor + p)); + if (/* isReallyYoung: */ + ((!(field & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(field)), + (oopisLessThan(field, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(field, GIV(newSpaceStart)))))) { + remember(newOop); + return newOop; + } + } + } + return newOop; +} + + +/* Use the identityHash and format-containing byte to construct a 30 bit + offset through non-future newSpace and use this to implement lists for + weak array + and ephemeron processing. 30 bits of 8 byte allocationUnits units is 2 ^ + 33 bytes, or 8Gb, big enough for newSpace for a good few years yet. */ +/* debugging */ + + /* SpurGenerationScavenger>>#corpseForCorpseOffset: */ +usqInt +corpseForCorpseOffset(sqInt corpseOffset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((((usqInt)((corpseOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); +} + + /* SpurGenerationScavenger>>#firstCorpse: */ +static NoDbgRegParms usqInt +firstCorpse(sqInt headOfCorpseList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((((usqInt)((headOfCorpseList - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); +} + + /* SpurGenerationScavenger>>#growRememberedSet */ +static void +growRememberedSet(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt *base; + sqInt fudge; + sqInt i; + sqInt newObj; + sqInt nSlots; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt obj; + usqInt p; + usqInt toDoLimit; + + + /* Don't ruin locality in remember: */ + obj = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))); + + /* begin numSlotsOf: */ + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + assert(numSlots >= 0x400); + nSlots = numSlots * 2; + + /* begin allocatePinnedSlots: */ + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(nSlots, /* objectBytesForSlots: */ + (nSlots + ? ((((usqInt)(nSlots) << (shiftForWord())))) + ((nSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (newObj) { + assert(isPinned(newObj)); + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + if (!newObj) { + nSlots = numSlots + 0x400; + + /* begin allocatePinnedSlots: */ + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(nSlots, /* objectBytesForSlots: */ + (nSlots + ? ((((usqInt)(nSlots) << (shiftForWord())))) + ((nSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (newObj) { + assert(isPinned(newObj)); + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + if (!newObj) { + if (!(growOldSpaceByAtLeast((numSlots + 0x400) * BytesPerOop))) { + error("could not grow remembered set"); + } + nSlots = numSlots + 0x400; + + /* begin allocatePinnedSlots: */ + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(nSlots, /* objectBytesForSlots: */ + (nSlots + ? ((((usqInt)(nSlots) << (shiftForWord())))) + ((nSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (newObj) { + assert(isPinned(newObj)); + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + } + } + + /* begin rememberedSetObj: */ + assert(isOldObject(newObj)); + assert((isNonImmediate(GIV(hiddenRootsObj))) + && (!(isForwarded(GIV(hiddenRootsObj))))); + assert(validStorePointerUncheckedArgs(RememberedSetRootIndex, GIV(hiddenRootsObj), newObj)); + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord()))))),newObj); + base = firstIndexableField(newObj); + for (i = 0; i < GIV(rememberedSetSize); i += 1) { + base[i] = (GIV(rememberedSet)[i]); + } + + /* if growing in the middle of a GC, need to preserve marked status. */ + if ((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(newObj))); + byteAtput((void *)(newObj + (markBitsByteOffset())),(byteAt((void *)(newObj + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(obj))); + byteAtput((void *)(obj + (markBitsByteOffset())),(byteAt((void *)(obj + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + freeObject(obj); + GIV(rememberedSet) = base; + + /* begin numSlotsOf: */ + assert((classIndexOf(newObj)) > (isForwardedObjectClassIndexPun())); + GIV(rememberedSetLimit) = (((numSlotsUsqInt = byteAt((void *)(newObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(newObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + + /* begin setRememberedSetRedZone */ + fudge = ((((GIV(eden).limit)) - ((GIV(eden).start))) / BytesPerWord) / 0x400; + GIV(rememberedSetRedZone) = ((((GIV(rememberedSetLimit) * 3) / 4) < fudge) ? fudge : ((GIV(rememberedSetLimit) * 3) / 4)); +} + + /* SpurGenerationScavenger>>#isInRememberedSet: */ +static NoDbgRegParms sqInt +isInRememberedSet(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + for (i = 0; i < GIV(rememberedSetSize); i += 1) { + if ((GIV(rememberedSet)[i]) == objOop) { + return 1; + } + } + return 0; +} + + +/* Answer whether the oop has survived a scavenge. This version is + for processing weak survivors and must cope with the scavenge in + freeUnmarkedObjectsAndSortAndCoalesceFreeSpaceForPigCompact. */ + + /* SpurGenerationScavenger>>#isMaybeOldScavengeSurvivor: */ +static NoDbgRegParms sqInt +isMaybeOldScavengeSurvivor(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt target; + + if (((oop & (tagMask())) != 0)) { + return 1; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + target = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(target & (tagMask())))) + && ((!((longAt((void *)(target))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + target = longAt((void *)((target + BaseHeaderSize) + (0U << (shiftForWord())))); + } + if (((oop & (tagMask())) != 0)) { + return 1; + } + } + else { + target = oop; + } + return (oopisGreaterThanOrEqualTo(target, GIV(oldSpaceStart)) + ? (GIV(tenureCriterion) != MarkOnTenure) + || (((byteAt((void *)(target + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + : oopisGreaterThanOrEqualToandLessThan(target, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))); +} + + +/* Answer whether the oop has survived a scavenge. This is equivalent to + | target | + (manager isImmediate: oop) ifTrue: + [^true]. + target := (manager isForwarded: oop) + ifTrue: [manager followForwarded: oop] + ifFalse: [oop]. + ^((manager isInEden: target) + or: [(manager isInPastSpace: target)]) not */ + + /* SpurGenerationScavenger>>#isScavengeSurvivor: */ +static NoDbgRegParms sqInt +isScavengeSurvivor(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt target; + + if (((oop & (tagMask())) != 0)) { + return 1; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + target = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(target & (tagMask())))) + && ((!((longAt((void *)(target))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + target = longAt((void *)((target + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + else { + target = oop; + } + return (!(/* isReallyYoung: */ + ((!(target & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(target)), + (oopisLessThan(target, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(target, GIV(newSpaceStart))))))) + || (oopisGreaterThanOrEqualToandLessThan(target, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))); +} + + +/* Answer whether the oop is in eden or pastSpace and yet-to-be scavenged. */ + + /* SpurGenerationScavenger>>#isYetToBeScavenged: */ +static NoDbgRegParms sqInt +isYetToBeScavenged(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt target; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + target = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(target & (tagMask())))) + && ((!((longAt((void *)(target))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + target = longAt((void *)((target + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + else { + target = oop; + } + return (/* isReallyYoung: */ + ((!(target & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(target)), + (oopisLessThan(target, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(target, GIV(newSpaceStart)))))) + && (!(oopisGreaterThanOrEqualToandLessThan(target, ((GIV(futureSpace)).start), GIV(futureSurvivorStart)))); +} + + /* SpurGenerationScavenger>>#newSpaceCapacity */ +static usqInt +newSpaceCapacity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((GIV(eden).limit)) - (((((GIV(futureSpace).start)) < ((GIV(pastSpace).start))) ? ((GIV(futureSpace).start)) : ((GIV(pastSpace).start)))); +} + + +/* corpse is the corpse of a weak array that has been added to the weakList. + Answer the next object on the list, or nil if none. */ + + /* SpurGenerationScavenger>>#nextCorpseOrNil: */ +static NoDbgRegParms usqInt +nextCorpseOrNil(sqInt corpse) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt listOffset; + + assert(isYoung(corpse)); + listOffset = ((((usqInt)(((long32At((void *)(corpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(corpse + (formatFieldByteOffset())))); + return (listOffset + ? ((((usqInt)((listOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0); +} + + /* SpurGenerationScavenger>>#openScavengeLog */ +void +openScavengeLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(scavengeLog) = fopen("scavenge.log", "a+"); +} + + +/* Print the objects on the ephemeronList, indicating active or inactive. */ + + /* SpurGenerationScavenger>>#printEphemeronList */ +void +printEphemeronList(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt corpse; + sqInt ephemeron; + + corpse = ((((usqInt)((GIV(ephemeronList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + if (!corpse) { + print("empty"); + cr(); + return; + } + while (corpse) { + ephemeron = followForwarded(corpse); + fprintf(GIV(transcript), + "%x -> %p %s\n", + ((int) corpse), + ((void *)ephemeron), + (isYetToBeScavenged(keyOfMaybeFiredEphemeron(ephemeron)) + ? "active" + : "inactive")); + corpse = nextCorpseOrNil(corpse); + } +} + + +/* Print the objects in the remembered set. */ +/* useful for debugging */ + + /* SpurGenerationScavenger>>#printRememberedSet */ +void +printRememberedSet(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + for (i = 0; i < GIV(rememberedSetSize); i += 1) { + printNum(i); + + /* begin space */ + printChar(' '); + shortPrintOop(GIV(rememberedSet)[i]); + } +} + + +/* There are ephemerons to be scavenged, and maybe some to be fired. We can't + fire any + ephemerons until we have reached the fixed point of scavenging all + inactive ephemerons, + and removing them from the remembered set and/or the list. Once at that + fixed point the + rememberedTable has only unfired ephemerons in its numRememberedEphemerons + partition, and the ephemeronList is either empty or contains only firable + ephemerons. At this fixed point + fire the remaining ephemerons and then scavenge the keys of the ephemerons + in the + remembered set and the contents of the ephemeronList. Answer if any were + fired. + Since more that one ephemeron can refer to the same key, and firability + for a key is + determined by that key not (yet) having survived the scavenge, we must + first fire + ephemerons before scavenging their keys, since the first ephemeron that + scavenges its key will make all other ephemerons on that key appear to be + unfirable. + Read the class comment for a more in-depth description of the algorithm. */ + + /* SpurGenerationScavenger>>#processEphemerons */ +static sqInt +processEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt corpseOffset; + sqInt ephemeron; + usqInt ephemeronCorpse; + sqInt firedIRS; + sqInt firedOEL; + sqInt foundNewReferent; + sqInt i; + sqInt key; + sqInt listOffset; + usqInt numSlots; + sqInt offsetToNextCorpse; + sqInt referent; + sqInt referentSqInt; + sqInt scavengedUnfirable; + sqInt toDoLimit; + + scavengedUnfirable = 0; + if (scavengeInactiveEphemeronsInRememberedSet()) { + scavengedUnfirable = 1; + } + assert(validRememberedSetPartitionPostSIEIRS()); + if ((scavengeInactiveEphemeronsOnEphemeronList()) + || (scavengedUnfirable)) { + return 0; + } + assert(wholeEphemeronListIsFirable()); + + /* begin fireEphemeronsInRememberedSet */ + assert(!((unfiredEphemeronsAtEndOfRememberedSet()))); + if (!GIV(numRememberedEphemerons)) { + firedIRS = 0; + goto l1; + } + i = 0; + while (i < GIV(numRememberedEphemerons)) { + ephemeron = GIV(rememberedSet)[i]; + assert(isEphemeron(ephemeron)); + + /* begin keyOfEphemeron: */ + assert((isNonImmediate(ephemeron)) + && (isObjEphemeron(ephemeron))); + key = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isYetToBeScavenged(key)); + fireEphemeron(ephemeron); + i += 1; + } + + /* If any ephemerons were fired then processEphemerons is expected to + scavenge them in scavengeUnfiredEphemeronsInRememberedSet */ + firedIRS = 1; + /* end fireEphemeronsInRememberedSet */ +l1: + + /* begin fireEphemeronsOnEphemeronList */ + if (!GIV(ephemeronList)) { + firedOEL = 0; + goto l2; + } + ephemeronCorpse = ((((usqInt)((GIV(ephemeronList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + while (ephemeronCorpse) { + assert((isYoung(ephemeronCorpse)) + && (isForwarded(ephemeronCorpse))); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(ephemeronCorpse)); + ephemeron = longAt((void *)((ephemeronCorpse + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(ephemeron & (tagMask())))) + && ((!((longAt((void *)(ephemeron))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ephemeron = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + } + + /* begin keyOfMaybeFiredEphemeron: */ + assert(isMaybeFiredEphemeron(ephemeron)); + key = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isYetToBeScavenged(key)); + fireEphemeron(ephemeron); + + /* begin nextCorpseOrNil: */ + assert(isYoung(ephemeronCorpse)); + listOffset = ((((usqInt)(((long32At((void *)(ephemeronCorpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(ephemeronCorpse + (formatFieldByteOffset())))); + ephemeronCorpse = (listOffset + ? ((((usqInt)((listOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0); + } + firedOEL = 1; + /* end fireEphemeronsOnEphemeronList */ +l2: + if (firedIRS + || (GIV(numRememberedEphemerons) > 0)) { + /* begin scavengeEphemeronsInRememberedSet */ + assert(!((unfiredEphemeronsAtEndOfRememberedSet()))); + i = 0; + while (i < GIV(numRememberedEphemerons)) { + ephemeron = GIV(rememberedSet)[i]; + assert((isEphemeron(ephemeron)) + || (isonObjStack(ephemeron, GIV(mournQueue)))); + if (scavengeReferentsOfPointers(ephemeron)) { + i += 1; + } + else { + setIsRememberedOfto(ephemeron, 0); + + /* remove from set by overwriting with next-to-be scanned */ + GIV(numRememberedEphemerons) -= 1; + GIV(previousRememberedSetSize) -= 1; + GIV(rememberedSetSize) -= 1; + + /* First overwrite with last firable ephemeron (could be a noop if this is the last one). + Then overwrite last firable entry with next unscanned rememberedSet entry (could also be a noop). + Then overwrite next unscanned entry with last unscanned rememberedSet entry (could also be a noop). */ + GIV(rememberedSet)[i] = (GIV(rememberedSet)[GIV(numRememberedEphemerons)]); + GIV(rememberedSet)[GIV(numRememberedEphemerons)] = (GIV(rememberedSet)[GIV(previousRememberedSetSize)]); + GIV(rememberedSet)[GIV(previousRememberedSetSize)] = (GIV(rememberedSet)[GIV(rememberedSetSize)]); + } + } + + /* no more firable ephemerons in this cycle. + scavengeRememberedSetStartingAt: may find new ones. */ + GIV(numRememberedEphemerons) = 0; + } + + /* begin scavengeEphemeronsOnEphemeronList */ + if (!((corpseOffset = GIV(ephemeronList)))) { + goto l3; + } + GIV(ephemeronList) = null; + while (corpseOffset != 0) { + ephemeronCorpse = ((((usqInt)((corpseOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + assert(isForwarded(ephemeronCorpse)); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(ephemeronCorpse)); + ephemeron = longAt((void *)((ephemeronCorpse + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(ephemeron & (tagMask())))) + && ((!((longAt((void *)(ephemeron))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ephemeron = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + } + offsetToNextCorpse = ((((usqInt)(((long32At((void *)(ephemeronCorpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(ephemeronCorpse + (formatFieldByteOffset())))); + + /* begin scavengeReferentsOfPointers: */ + foundNewReferent = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(ephemeron)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(ephemeron + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(ephemeron - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((ephemeron + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((ephemeron + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + corpseOffset = offsetToNextCorpse; + } + /* end scavengeEphemeronsOnEphemeronList */ +l3: + assert(GIV(numRememberedEphemerons) == 0); + assert(!GIV(ephemeronList)); + return firedIRS + || (firedOEL); +} + + +/* Go through the remembered set and the weak list, nilling references to + any objects that didn't survive the scavenge. Read the class comment + for a more in-depth description of the algorithm. */ + + /* SpurGenerationScavenger>>#processWeaklings */ +static void +processWeaklings(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt i; + sqInt listOffset; + sqInt rootObj; + usqInt weakCorpse; + sqInt weakObj; + + assert(allWeakSurvivorsOnWeakList()); + i = 0; + while (i < GIV(rememberedSetSize)) { + rootObj = GIV(rememberedSet)[i]; + + /* begin isWeakNonImm: */ + format = (byteAt((void *)(rootObj + (formatFieldByteOffset())))) & (formatMask()); + if (format == (weakArrayFormat())) { + /* If no more referents, remove by overwriting with the last element in the set. */ + if (processWeakSurvivor(rootObj)) { + i += 1; + } + else { + setIsRememberedOfto(rootObj, 0); + if ((i + 1) < GIV(rememberedSetSize)) { + GIV(rememberedSet)[i] = (GIV(rememberedSet)[GIV(rememberedSetSize) - 1]); + } + GIV(rememberedSetSize) -= 1; + } + } + else { + i += 1; + } + } + if (GIV(weakList)) { + weakCorpse = ((((usqInt)((GIV(weakList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + while (weakCorpse) { + assert(isForwarded(weakCorpse)); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(weakCorpse)); + weakObj = longAt((void *)((weakCorpse + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(weakObj & (tagMask())))) + && ((!((longAt((void *)(weakObj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + weakObj = longAt((void *)((weakObj + BaseHeaderSize) + (0U << (shiftForWord())))); + } + + /* weakObj may have been tenured... */ + if ((processWeakSurvivor(weakObj)) + && (((/* begin isOldObject: */ + assert(isNonImmediate(weakObj)), + oopisGreaterThanOrEqualTo(weakObj, GIV(oldSpaceStart)))) + && (!(((byteAt((void *)(weakObj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0)))) { + remember(weakObj); + } + + /* begin nextCorpseOrNil: */ + assert(isYoung(weakCorpse)); + listOffset = ((((usqInt)(((long32At((void *)(weakCorpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(weakCorpse + (formatFieldByteOffset())))); + weakCorpse = (listOffset + ? ((((usqInt)((listOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0); + } + GIV(weakList) = null; + } +} + + +/* Process a weak survivor on the weakList. Those of its fields + which have not survived the scavenge should be nilled, and if any + are, the coInterpreter should be informed via fireFinalization:. + Answer if the weakObj has any young referents. */ + + /* SpurGenerationScavenger>>#processWeakSurvivor: */ +static NoDbgRegParms sqInt +processWeakSurvivor(sqInt weakObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classPointer; + sqInt hasYoungReferents; + sqInt i; + usqInt numSlots; + sqInt numStrongSlots; + sqInt referent; + sqInt referentSqInt; + sqInt toDoLimit; + sqInt weakObjShouldMourn; + + weakObjShouldMourn = (hasYoungReferents = 0); + + /* N.B. generateToByDoLimitExpression:negative:on: guards against (unsigned)0 - 1 going +ve */ + + /* begin numFixedSlotsOf: */ + classPointer = fetchClassOfNonImm(weakObj); + classFormat = ((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + numStrongSlots = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + for (i = 0; i < numStrongSlots; i += 1) { + referent = longAt((void *)((weakObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(referent & (tagMask())))) + && ((/* begin isYoungObject: */ + assert(isNonImmediate(referent)), + oopisLessThan(referent, GIV(oldSpaceStart))))) { + hasYoungReferents = 1; + } + } + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(weakObj)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(weakObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(weakObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = numStrongSlots; i <= toDoLimit; i += 1) { + referent = longAt((void *)((weakObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* Referent could be forwarded due to scavenging or a become:, don't assume. */ + if ((!(referent & (tagMask())))) { + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + + /* weakObj is either young or already in remembered table; no need to check */ + assert((isReallyYoungObject(weakObj)) + || (isRemembered(weakObj))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(weakObj)) + && (!(isForwarded(weakObj)))); + assert(validStorePointerUncheckedArgs(i, weakObj, referent)); + longAtput((void *)((weakObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + if (isMaybeOldScavengeSurvivor(referent)) { + /* begin isYoungObject: */ + assert(isNonImmediate(referent)); + if (oopisLessThan(referent, GIV(oldSpaceStart))) { + hasYoungReferents = 1; + } + } + else { + weakObjShouldMourn = 1; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(weakObj)) + && (!(isForwarded(weakObj)))); + assert(validStorePointerUncheckedArgs(i, weakObj, GIV(nilObj))); + longAtput((void *)((weakObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } + } + } + if (weakObjShouldMourn) { + /* begin fireFinalization: */ + if (((imageHeaderFlags & 64) != 0)) { + queueMourner(weakObj); + } + + /* begin signalFinalization: */ + forceInterruptCheck(); + GIV(pendingFinalizationSignals) += 1; + } + return hasYoungReferents; +} + + +/* Add the argument to the remembered set and set its isRemembered bit to + true. Answer the argument for the benefit of the Cogit. */ + + /* SpurGenerationScavenger>>#remember: */ +sqInt +remember(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(isNonImmediate(objOop)); + assert(!((isYoungObject(objOop)))); + assert(!((isRemembered(objOop)))); + assert(!((isInRememberedSet(objOop)))); + setIsRememberedOfto(objOop, 1); + if (GIV(rememberedSetSize) >= GIV(rememberedSetLimit)) { + growRememberedSet(); + } + GIV(rememberedSet)[GIV(rememberedSetSize)] = objOop; + if (((GIV(rememberedSetSize) += 1)) >= GIV(rememberedSetRedZone)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + return objOop; +} + + +/* scavengeFutureSurvivorSpaceStartingAt: does a depth-first traversal of the + new objects starting at the one at initialAddress in futureSurvivorSpace. */ + + /* SpurGenerationScavenger>>#scavengeFutureSurvivorSpaceStartingAt: */ +static NoDbgRegParms void +scavengeFutureSurvivorSpaceStartingAt(sqInt initialAddress) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + sqInt obj; + usqInt ptr; + + ptr = initialAddress; + while (ptr < GIV(futureSurvivorStart)) { + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(ptr + (numSlotsFieldByteOffset()))); + obj = (numSlots == (numSlotsMask()) + ? ptr + BaseHeaderSize + : ptr); + ptr = addressAfter(obj); + ((void) (scavengeReferentsOf(obj))); + } +} + + +/* There are ephemerons in the remembered set. Scavenge all with + scavenged/old keys, + leaving only firable/triggerable ephemerons. Answer if any + unfirable/inactive ephemerons + were scavenged. Answering false implies there are only firable ephemerons + (if any) within + the 0 to numRememberedEphemerons - 1 range. + Continue scavenging until at the fixed point of only having firable + ephemerons with still-to-be-scavenged keys in the numRememberedEphemerons + partition. */ + + /* SpurGenerationScavenger>>#scavengeInactiveEphemeronsInRememberedSet */ +static sqInt +scavengeInactiveEphemeronsInRememberedSet(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ephemeron; + sqInt i; + sqInt key; + sqInt numRememberedAtStartOfScan; + sqInt scavengedInactive; + + scavengedInactive = 0; + do { + numRememberedAtStartOfScan = GIV(numRememberedEphemerons); + i = 0; + while (i < GIV(numRememberedEphemerons)) { + ephemeron = GIV(rememberedSet)[i]; + assert((isEphemeron(ephemeron)) + || (isonObjStack(ephemeron, GIV(mournQueue)))); + + /* begin keyOfEphemeron: */ + assert((isNonImmediate(ephemeron)) + && (isObjEphemeron(ephemeron))); + key = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + if (isYetToBeScavenged(key)) { + i += 1; + } + else { + scavengedInactive = 1; + if (scavengeReferentsOfPointers(ephemeron)) { + if (i < (GIV(numRememberedEphemerons) - 1)) { + GIV(rememberedSet)[i] = (GIV(rememberedSet)[GIV(numRememberedEphemerons) - 1]); + GIV(rememberedSet)[GIV(numRememberedEphemerons) - 1] = ephemeron; + } + GIV(numRememberedEphemerons) -= 1; + } + else { + GIV(numRememberedEphemerons) -= 1; + GIV(previousRememberedSetSize) -= 1; + GIV(rememberedSetSize) -= 1; + + /* First overwrite with last firable ephemeron (could be a noop if this is the last one). + Then overwrite last firable entry with next unscanned rememberedSet entry (could also be a noop). + Then overwrite next unscanned entry with last unscanned rememberedSet entry (could also be a noop). */ + GIV(rememberedSet)[i] = (GIV(rememberedSet)[GIV(numRememberedEphemerons)]); + GIV(rememberedSet)[GIV(numRememberedEphemerons)] = (GIV(rememberedSet)[GIV(previousRememberedSetSize)]); + GIV(rememberedSet)[GIV(previousRememberedSetSize)] = (GIV(rememberedSet)[GIV(rememberedSetSize)]); + } + } + } + } while(!(GIV(numRememberedEphemerons) == numRememberedAtStartOfScan)); + + /* rememberedSet contains numRememberedEphemerons firable ephemerons in the rememberedSet. + These will be fired in processEphemerons fireEphemeronsInRememberedSet */ + return scavengedInactive; +} + + +/* Scavenge all untriggerable ephemerons on the ephemeronList and remove them + there-from. Answer if any were scavenged. */ + + /* SpurGenerationScavenger>>#scavengeInactiveEphemeronsOnEphemeronList */ +static sqInt +scavengeInactiveEphemeronsOnEphemeronList(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anyScavenged; + sqInt ephemeron; + usqInt ephemeronCorpse; + sqInt foundNewReferent; + sqInt i; + sqInt key; + sqInt nextCorpseOffset; + usqInt numSlots; + usqInt previousCorpse; + sqInt referent; + sqInt referentSqInt; + sqInt toDoLimit; + + if (!GIV(ephemeronList)) { + return 0; + } + anyScavenged = 0; + previousCorpse = null; + + /* In figuring out the termination condition I believe it is sufficient to consider four cases: + ephemeronList starts with + two firable corpses + two unfirable corpses + a firable corpse followed by an unfirable corpse + an unfirable corpse followed by a firable corpse */ + ephemeronCorpse = ((((usqInt)((GIV(ephemeronList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + while (ephemeronCorpse) { + assert((isYoung(ephemeronCorpse)) + && (isForwarded(ephemeronCorpse))); + nextCorpseOffset = ((((usqInt)(((long32At((void *)(ephemeronCorpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(ephemeronCorpse + (formatFieldByteOffset())))); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(ephemeronCorpse)); + ephemeron = longAt((void *)((ephemeronCorpse + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(ephemeron & (tagMask())))) + && ((!((longAt((void *)(ephemeron))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ephemeron = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + } + + /* begin keyOfMaybeFiredEphemeron: */ + assert(isMaybeFiredEphemeron(ephemeron)); + key = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + if (isYetToBeScavenged(key)) { + previousCorpse = ephemeronCorpse; + } + else { + anyScavenged = 1; + + /* begin scavengeReferentsOfPointers: */ + foundNewReferent = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(ephemeron)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(ephemeron + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(ephemeron - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((ephemeron + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((ephemeron + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + if (previousCorpse) { + /* begin setCorpseOffsetOf:to: */ + assert(isYoung(previousCorpse)); + assert(isForwarded(previousCorpse)); + long64Atput((void *)(previousCorpse),headerForSlotshashformatclassIndex(byteAt((void *)(previousCorpse + (numSlotsFieldByteOffset()))), ((usqInt)(nextCorpseOffset)) >> 8, nextCorpseOffset & 0xFF, isForwardedObjectClassIndexPun())); + } + else { + GIV(ephemeronList) = (nextCorpseOffset + ? nextCorpseOffset + : 0); + } + } + ephemeronCorpse = (nextCorpseOffset + ? ((((usqInt)((nextCorpseOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0); + } + if (previousCorpse) { + /* begin setCorpseOffsetOf:to: */ + assert(isYoung(previousCorpse)); + assert(isForwarded(previousCorpse)); + long64Atput((void *)(previousCorpse),headerForSlotshashformatclassIndex(byteAt((void *)(previousCorpse + (numSlotsFieldByteOffset()))), 0, 0, isForwardedObjectClassIndexPun())); + } + return anyScavenged; +} + + +/* This is the inner loop of the main routine, scavenge. It first scavenges + the new objects immediately + reachable from old ones and the stack. Then it scavenges those that are + transitively reachable. If any + results in a tenure, the tenuree gets remembered, and it first scavenges + objects adjacent to the tenure, + then scavenges the ones reachable from the tenuree. This loop continues + until no more reachable + objects are left. At that point, pastSurvivorSpace is exchanged with + futureSurvivorSpace. + Notice that each pointer in a live object is inspected once and only once. + The previousRememberedSetSize + and previousFutureSurvivorSpaceLimit variables ensure that no object is + scanned twice, as well as + detecting closure. If this were not true, some pointers might get + forwarded twice. + + An extension of the algorithm presented in David's original paper is to + handle weak arrays and ephemerons. + Weak arrays should not have their weak referents scavenged unless there + are strong references to them. + Ephemerons should fire if their key is not reachable other than from + ephemerons and weak arrays. + Handle this by maintaining a list for weak arrays and a list for + ephemerons, which allow scavenging these + objects once all other objects in new space have been scavenged, hence + allowing the scavenger to + detect which referents in new space of weak arrays are dead and of + ephemeron keys are only live due to + ephemerons. Read the class comment for a more in-depth description of the + algorithm. + Answer how many passes occurred. This is to use the value of p[ass which + is valuable for debugging. */ +/* debugging */ + + /* SpurGenerationScavenger>>#scavengeLoop */ +static sqInt +scavengeLoop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firedEphemerons; + sqInt firstTime; + sqInt i; + sqInt mournQueueSize; + sqInt newSize; + sqInt pass; + sqInt previousFutureSurvivorStart; + StackPage *thePage; + + mournQueueSize = 0; + assert(GIV(futureSurvivorStart) == ((GIV(futureSpace).start))); + GIV(weakList) = (GIV(ephemeronList) = null); + GIV(numRememberedEphemerons) = 0; + firstTime = 1; + GIV(previousRememberedSetSize) = 0; + previousFutureSurvivorStart = GIV(futureSurvivorStart); + firedEphemerons = 0; + pass = 0; + + /* begin initStackPageGC */ + if (GIV(stackPage)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + (thePage->trace = StackPageUnreached); + } + while (1) { + pass += 1; + scavengeRememberedSetStartingAt(GIV(previousRememberedSetSize)); + GIV(previousRememberedSetSize) = GIV(rememberedSetSize); + if (firstTime) { + mapInterpreterOops(); + mapMournQueue(); + mapExtraRoots(); + mournQueueSize = sizeOfObjStack(GIV(mournQueue)); + firstTime = 0; + } + + /* if nothing more copied and forwarded (or remembered by mapInterpreterOops et al) + to scavenge, and no ephemerons to process, the scavenge is done. */ + if ((GIV(previousRememberedSetSize) == GIV(rememberedSetSize)) + && ((previousFutureSurvivorStart == GIV(futureSurvivorStart)) + && ((!firedEphemerons) + && (!GIV(ephemeronList))))) { + assert(GIV(numRememberedEphemerons) == 0); + return pass; + } + scavengeFutureSurvivorSpaceStartingAt(previousFutureSurvivorStart); + previousFutureSurvivorStart = GIV(futureSurvivorStart); + + /* all reachable objects in this cycle have been promoted to futureSpace. + ephemerons can now be processed. */ + firedEphemerons = processEphemerons(); + if (((newSize = sizeOfObjStack(GIV(mournQueue)))) > mournQueueSize) { + mapMournQueue(); + mournQueueSize = newSize; + } + } + return pass; +} + + +/* Scavenge the referents of a normal pointer object. + Answer if it still refers to young objects. */ + + /* SpurGenerationScavenger>>#scavengeReferentsOfPointers: */ +static NoDbgRegParms sqInt +scavengeReferentsOfPointers(sqInt referrer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt foundNewReferent; + sqInt i; + usqInt numSlots; + sqInt referent; + sqInt referentSqInt; + sqInt toDoLimit; + + foundNewReferent = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(referrer)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(referrer + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(referrer - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + return foundNewReferent; +} + + +/* scavengeReferentsOf: referrer inspects relevant pointers in referrer. If + any are new objects, it has them moved to FutureSurvivorSpace, and + answers truth. If there are no new referents, it answers falsity. To + handle weak arrays, if the referrer is weak only scavenge strong slots and + answer true so that it won't be removed from the remembered set until + later. To handle ephemerons, only scavenge other slots if the key slot has + already been scavenged. */ +/* forwarding objects should be followed by callers, + unless the forwarder is a root in the remembered table. */ + + /* SpurGenerationScavenger>>#scavengeReferentsOf: */ +static NoDbgRegParms sqInt +scavengeReferentsOf(sqInt referrer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt foundNewReferent; + sqInt i; + usqInt numSlots; + sqInt referent; + sqInt referentSqInt; + sqInt toDoLimit; + + assert((!(isForwarded(referrer))) + || (isRemembered(referrer))); + assert((isInFutureSpace(referrer)) + || (isInOldSpace(referrer))); + switch ((byteAt((void *)(referrer + (formatFieldByteOffset())))) & (formatMask())) { + case nonIndexablePointerFormat(): + case arrayFormat(): + /* begin scavengeReferentsOfPointers: */ + foundNewReferent = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(referrer)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(referrer + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(referrer - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + return foundNewReferent; + + case indexablePointersFormat(): + /* begin scavengeReferentsOfIndexablePointers: */ + foundNewReferent = 0; + toDoLimit = (numSlotsOfIndexablePointerObj(referrer)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + return foundNewReferent; + + case weakArrayFormat(): + /* begin scavengeReferentsOfWeakling: */ + toDoLimit = (numStrongSlotsOfWeakling(referrer)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (!(oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart)))) { + referent = copyAndForward(referent); + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + + /* Answer true to keep any weak objects in the remembered set until later. */ + return 1; + + case ephemeronFormat(): + /* Scavenge the referents of an ephemeron. If the key + has not yet been scavenged, don't scavenge it yet. */ + if (isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(referrer)) + && (isObjEphemeron(referrer))), + /* fetchPointer:ofObject: */ + longAt((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord()))))))) { + return 1; + } + + /* begin scavengeReferentsOfPointers: */ + foundNewReferent = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(referrer)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(referrer + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(referrer - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + return foundNewReferent; + + case forwardedFormat(): + /* begin scavengeReferentOfForwarder: */ + foundNewReferent = 0; + referent = longAt((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord())))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord()))),referent); + } + return foundNewReferent; + + case firstCompiledMethodFormat(): + case (firstCompiledMethodFormat()) + 1: + case (firstCompiledMethodFormat()) + 2: + case (firstCompiledMethodFormat()) + 3: + case (firstCompiledMethodFormat()) + 4: + case (firstCompiledMethodFormat()) + 5: + case (firstCompiledMethodFormat()) + 6: + case (firstCompiledMethodFormat()) + 7: + /* begin scavengeReferentsOfCompiledCode: */ + foundNewReferent = 0; + toDoLimit = literalCountOf(referrer); + for (i = 1; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + return foundNewReferent; + + default: + assert(((numSlotsOf(referrer)) == 0) + || (isWordsOrBytesNonImm(referrer))); + } + return 0; +} + + +/* scavengeRememberedSetStartingAt: n traverses objects in the remembered + set starting at the nth one. If the object does not refer to any new + objects, it + is removed from the set. Otherwise, its new referents are scavenged. Defer + scavenging ephemerons until after a complete scavenge has been performed, + so that triggered ephemerons can be fired. Move them to the front of the + set and count them in numRememberedEphemerons for later scanning. */ + + /* SpurGenerationScavenger>>#scavengeRememberedSetStartingAt: */ +static NoDbgRegParms void +scavengeRememberedSetStartingAt(sqInt n) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt destIndex; + sqInt referrer; + sqInt sourceIndex; + + sourceIndex = (destIndex = n); + while (sourceIndex < GIV(rememberedSetSize)) { + referrer = GIV(rememberedSet)[sourceIndex]; + + /* Any potential firing ephemerons should not be scanned yet. + Move any to the front of the set to save time in later scanning. */ + if ((isEphemeron(referrer)) + && (isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(referrer)) + && (isObjEphemeron(referrer))), + /* fetchPointer:ofObject: */ + longAt((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord())))))))) { + assert(destIndex >= GIV(numRememberedEphemerons)); + GIV(rememberedSet)[destIndex] = (GIV(rememberedSet)[GIV(numRememberedEphemerons)]); + GIV(rememberedSet)[GIV(numRememberedEphemerons)] = referrer; + GIV(numRememberedEphemerons) += 1; + destIndex += 1; + } + else { + if (scavengeReferentsOf(referrer)) { + GIV(rememberedSet)[destIndex] = referrer; + destIndex += 1; + } + else { + setIsRememberedOfto(referrer, 0); + } + } + sourceIndex += 1; + } + GIV(rememberedSetSize) = destIndex; + assert(!((unfiredEphemeronsAtEndOfRememberedSet()))); +} + + +/* (Slang flattens so need unique selectors) */ + + /* SpurGenerationScavenger>>#scavengerTenuringThreshold */ +static float +scavengerTenuringThreshold(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (GIV(tenureThreshold) >= ((GIV(pastSpace).start)) + ? (((double) (GIV(tenureThreshold) - ((GIV(pastSpace).start))) )) / (((GIV(pastSpace).limit)) - ((GIV(pastSpace).start))) + : 0); +} + + +/* For assert checking only. */ + + /* SpurGenerationScavenger>>#unfiredEphemeronsAtEndOfRememberedSet */ +static sqInt +unfiredEphemeronsAtEndOfRememberedSet(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt i; + sqInt referrer; + + for (i = GIV(numRememberedEphemerons); i < GIV(rememberedSetSize); i += 1) { + referrer = GIV(rememberedSet)[i]; + + /* begin isEphemeron: */ + assert(isNonImmediate(referrer)); + format = (byteAt((void *)(referrer + (formatFieldByteOffset())))) & (formatMask()); + if (format == (ephemeronFormat())) { + if (isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(referrer)) + && (isObjEphemeron(referrer))), + /* fetchPointer:ofObject: */ + longAt((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord()))))))) { + return 1; + } + } + } + return 0; +} + + +/* Answer if the remembered table is laid out correctly after + scavengeInactiveEphemeronsInRememberedSet. This implies that up to + numRememberedEphemerons are only unfired ephemerons, and no unfired + ephemerons beyond. + */ + + /* SpurGenerationScavenger>>#validRememberedSetPartitionPostSIEIRS */ +static sqInt +validRememberedSetPartitionPostSIEIRS(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt i; + sqInt referrer; + + for (i = 0; i < GIV(rememberedSetSize); i += 1) { + referrer = GIV(rememberedSet)[i]; + + /* begin isEphemeron: */ + assert(isNonImmediate(referrer)); + format = (byteAt((void *)(referrer + (formatFieldByteOffset())))) & (formatMask()); + if (format == (ephemeronFormat())) { + if ((isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(referrer)) + && (isObjEphemeron(referrer))), + /* fetchPointer:ofObject: */ + longAt((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord())))))) + ? i >= GIV(numRememberedEphemerons) + : i < GIV(numRememberedEphemerons))) { + return 0; + } + } + } + return 1; +} + + +/* Answer if everything on the ephemeronList is a firable ephemeron */ + + /* SpurGenerationScavenger>>#wholeEphemeronListIsFirable */ +static sqInt +wholeEphemeronListIsFirable(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ephemeron; + usqInt ephemeronCorpse; + sqInt listOffset; + + if (!GIV(ephemeronList)) { + return 1; + } + ephemeronCorpse = ((((usqInt)((GIV(ephemeronList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + while (ephemeronCorpse) { + if (!((/* isYoung: */ + ((!(ephemeronCorpse & (tagMask())))) + && (oopisLessThan(ephemeronCorpse, GIV(oldSpaceStart)))) + && ((!((longAt((void *)(ephemeronCorpse))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + return 0; + } + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(ephemeronCorpse)); + ephemeron = longAt((void *)((ephemeronCorpse + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(ephemeron & (tagMask())))) + && ((!((longAt((void *)(ephemeron))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ephemeron = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + } + if (!((isEphemeron(ephemeron)) + && (isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(ephemeron)) + && (isObjEphemeron(ephemeron))), + /* fetchPointer:ofObject: */ + longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord()))))))))) { + return 0; + } + + /* begin nextCorpseOrNil: */ + assert(isYoung(ephemeronCorpse)); + listOffset = ((((usqInt)(((long32At((void *)(ephemeronCorpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(ephemeronCorpse + (formatFieldByteOffset())))); + ephemeronCorpse = (listOffset + ? ((((usqInt)((listOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0); + } + return 1; +} + + +/* Output the entire record. */ + + /* SpurGenerationScavenger>>#writeScavengeLog */ +static NeverInline void +writeScavengeLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + static char *policyNames[] = {"", "by age", "by class", "to shrink rt", "don't tenure", "mark on tenure"}; + + /* log data collected by logStartScavenge */ + fprintf(GIV(scavengeLog), + "scavenge %" PRIdSQINT " eden bytes: 0x%" PRIxSQINT "/%" PRIdSQINT " past bytes: 0x%" PRIxSQINT "/%" PRIdSQINT "\n\trem set: %" PRIdSQINT " redzone: %" PRIdSQINT " size: %" PRIdSQINT "\n", + GIV(statScavenges), + (GIV(scavengeLogRecord).sEdenBytes), + (GIV(scavengeLogRecord).sEdenBytes), + (GIV(scavengeLogRecord).sPastBytes), + (GIV(scavengeLogRecord).sPastBytes), + (GIV(scavengeLogRecord).sRememberedSetSize), + (GIV(scavengeLogRecord).sRememberedSetRedZone), + (GIV(scavengeLogRecord).sRememberedSetLimit)); + + /* log data collected by logTenuringPolicy */ + fprintf(GIV(scavengeLog), + (((GIV(scavengeLogRecord).tTenureCriterion)) == TenureToShrinkRT + ? "\ttenure below 0x%" PRIxSQINT "/%" PRIdSQINT " %s refct %" PRIdSQINT "\n" + : "\ttenure below 0x%" PRIxSQINT "/%" PRIdSQINT " %s\n"), + (GIV(scavengeLogRecord).tTenureThreshold), + (GIV(scavengeLogRecord).tTenureThreshold), + policyNames[(GIV(scavengeLogRecord).tTenureCriterion)], + (GIV(scavengeLogRecord).tRefCountToShrinkRT)); + + /* log data collected by logEndScavenge */ + fprintf(GIV(scavengeLog), + "\tsurvivor bytes: 0x%" PRIxSQINT "/%" PRIdSQINT " rem set: %" PRIdSQINT " tenured: %" PRIdSQINT " usecs: %" PRIdSQINT "\n", + (GIV(scavengeLogRecord).eSurvivorBytes), + (GIV(scavengeLogRecord).eSurvivorBytes), + (GIV(scavengeLogRecord).eRememberedSetSize), + ((GIV(scavengeLogRecord).eStatTenures)) - ((GIV(scavengeLogRecord).sStatTenures)), + GIV(statSGCDeltaUsecs)); + fflush(GIV(scavengeLog)); +} + + +/* Answer the accessible object following the given object or + free chunk in the heap. Return nil when heap is exhausted. + This is for primitiveNextObject subsequent to primitiveSomeObject. + It also tries to handle more general use by ordering objects as + eden + past + old + but this is tricky becaus ethe order in memory is + past + eden + old */ + + /* SpurMemoryManager>>#accessibleObjectAfter: */ +static NoDbgRegParms sqInt +accessibleObjectAfter(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objAfter; + + objAfter = objOop; + if (oopisLessThan(objAfter, GIV(nilObj))) { + assert((isInEden(objOop)) + || (isInPastSpace(objOop))); + if (oopisGreaterThan(objAfter, GIV(pastSpaceStart))) { + /* Obj is in eden. Answer next normal object in eden, if there is one. */ + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objAfter); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objAfter = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objAfter = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + if (!(oopisLessThan(objAfter, GIV(freeStart)))) break; + if (((longAt((void *)(objAfter))) & (classIndexMask())) > (lastClassIndexPun())) { + return objAfter; + } + } + + /* There wasn't a next object in eden. If past space is empty answer nilObj. */ + if (GIV(pastSpaceStart) <= (((GIV(pastSpace)).start))) { + return GIV(nilObj); + } + + /* If the first object in pastSpace is OK, answer it, otherwise fall through to enumerate past space. */ + address = ((GIV(pastSpace)).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objAfter = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + if (((longAt((void *)(objAfter))) & (classIndexMask())) > (lastClassIndexPun())) { + return objAfter; + } + } + + /* Either objOop was in pastSpace, or enumeration exhaused eden, so enumerate past space. */ + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objAfter); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + objAfter = GIV(pastSpaceStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objAfter = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l2:; + if (!(oopisLessThan(objAfter, GIV(pastSpaceStart)))) break; + if (((longAt((void *)(objAfter))) & (classIndexMask())) > (lastClassIndexPun())) { + return objAfter; + } + } + return GIV(nilObj); + } + + /* object in new space */ + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objAfter); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objAfter = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + objAfter = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3: + if (objAfter == GIV(endOfMemory)) { + return null; + } + if (((longAt((void *)(objAfter))) & (classIndexMask())) > (lastClassIndexPun())) { + return objAfter; + } + } + return 0; +} + + +/* Answer whether an ephemeron is active (has an unmarked + key) and was pushed on the unscanned ephemerons stack. */ + + /* SpurMemoryManager>>#activeAndDeferredScan: */ +static NoDbgRegParms NeverInline sqInt +activeAndDeferredScan(sqInt anEphemeron) +{ + sqInt key; + + key = 0; + assert(isEphemeron(anEphemeron)); + if ((isImmediate((/* begin keyOfEphemeron: */ + assert((isNonImmediate(anEphemeron)) + && (isObjEphemeron(anEphemeron))), + (key = longAt((void *)((anEphemeron + BaseHeaderSize) + (0U << (shiftForWord())))))))) + || (((byteAt((void *)(key + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) { + return 0; + } + return pushOnUnscannedEphemeronsStack(anEphemeron); +} + + +/* Add a freeChunk sub tree back into the large free chunk tree. + This is for allocateOldSpaceChunkOf[Exactly]Bytes:[suchThat:]. */ +/* N.B. *can't* use numSlotsOfAny: because of rounding up of odd slots + and/or step in size at 1032 bytes in 32-bits or 2048 bytes in 64-bits. */ + + /* SpurMemoryManager>>#addFreeSubTree: */ +static NoDbgRegParms void +addFreeSubTree(sqInt freeTree) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytesInArg; + usqInt bytesInNode; + sqInt subNode; + sqInt treeNode; + + assert(isFreeObject(freeTree)); + bytesInArg = bytesInBody(freeTree); + assert(bytesInArg >= ((numFreeLists()) * (allocationUnit()))); + treeNode = GIV(freeLists)[0]; + assert(treeNode != 0); + while (1) { + bytesInNode = bytesInBody(treeNode); + + /* check for overlap; could write this as self oop: (self objectAfter: freeChunk) isLessThanOrEqualTo: child... + but that relies on headers being correct, etc. So keep it clumsy... */ + assert((oopisLessThanOrEqualTo((freeTree + bytesInArg) - BaseHeaderSize, treeNode)) + || (oopisGreaterThanOrEqualTo(freeTree, (treeNode + bytesInNode) - BaseHeaderSize))); + assert(bytesInNode >= ((numFreeLists()) * (allocationUnit()))); + assert(bytesInArg != bytesInNode); + if (bytesInNode > bytesInArg) { + subNode = longAt((void *)((treeNode + BaseHeaderSize) + (3U << (shiftForWord())))); + if (!subNode) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(treeNode)); + assert((freeTree == 0) + || (isFreeObject(freeTree))); + longAtput((void *)((treeNode + BaseHeaderSize) + (3U << (shiftForWord()))),freeTree); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeTree)); + assert((treeNode == 0) + || (isFreeObject(treeNode))); + longAtput((void *)((freeTree + BaseHeaderSize) + (2U << (shiftForWord()))),treeNode); + return; + } + } + else { + subNode = longAt((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + if (!subNode) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(treeNode)); + assert((freeTree == 0) + || (isFreeObject(freeTree))); + longAtput((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord()))),freeTree); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeTree)); + assert((treeNode == 0) + || (isFreeObject(treeNode))); + longAtput((void *)((freeTree + BaseHeaderSize) + (2U << (shiftForWord()))),treeNode); + return; + } + } + treeNode = subNode; + } +} + + +/* Add the given variable location to the extra roots table. */ + + /* SpurMemoryManager>>#addGCRoot: */ +sqInt +addGCRoot(sqInt *varLoc) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (GIV(extraRootCount) >= ExtraRootsSize) { + return 0; + } + GIV(extraRoots)[(GIV(extraRootCount) += 1)] = varLoc; + return 1; +} + + +/* Answer if maybeClassObj looks like a class object */ + + /* SpurMemoryManager>>#addressCouldBeClassObj: */ +static NoDbgRegParms sqInt +addressCouldBeClassObj(sqInt maybeClassObj) +{ + return (addressCouldBeObj(maybeClassObj)) + && (objCouldBeClassObj(maybeClassObj)); +} + + /* SpurMemoryManager>>#addressCouldBeObj: */ +sqInt +addressCouldBeObj(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((address & (BaseHeaderSize - 1)) == 0) + && ((/* Inlined isInOldSpace: for speed, because it also segregates the newSpace objects */ + + (oopisGreaterThanOrEqualTo(address, GIV(oldSpaceStart)) + ? oopisLessThan(address, GIV(endOfMemory)) + : (oopisGreaterThanOrEqualToandLessThan(address, ((GIV(eden)).start), GIV(freeStart))) + || ((oopisGreaterThanOrEqualToandLessThan(address, ((GIV(pastSpace)).start), GIV(pastSpaceStart))) + || ((GIV(gcPhaseInProgress) == ScavengeInProgress) + && (oopisGreaterThanOrEqualToandLessThan(address, ((GIV(futureSpace)).start), GIV(futureSurvivorStart)))))))); +} + + +/* Answer if address appears to be that of either an immediate or an object. + For code disassembly and assertions. */ + + /* SpurMemoryManager>>#addressCouldBeOop: */ +sqInt +addressCouldBeOop(sqInt address) +{ + return (((address & (tagMask())) != 0)) + || (addressCouldBeObj(address)); +} + + +/* Add freeChunk to the relevant freeList. + For the benefit of sortedFreeObject:, if freeChunk is large, answer the + treeNode it + is added to, if it is added to the next list of a freeTreeNode, otherwise + answer 0. */ +/* coInterpreter transcript ensureCr. coInterpreter print: 'freeing '. self + printFreeChunk: freeChunk. + */ + + /* SpurMemoryManager>>#addToFreeList:bytes: */ +static NoDbgRegParms sqInt +addToFreeListbytes(sqInt freeChunk, sqInt chunkBytes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt child; + usqInt childBytes; + sqInt index; + sqInt lilliputian; + sqInt nextFreeChunk; + sqInt parent; + + assert(isFreeObject(freeChunk)); + assert(chunkBytes == (bytesInBody(freeChunk))); + + /* Too slow to be enabled byt default but useful to debug Selective... + self deny: (compactor isSegmentBeingCompacted: (segmentManager segmentContainingObj: freeChunk)). */ + index = chunkBytes / 8 /* allocationUnit */; + if (index < 64 /* numFreeLists */) { + nextFreeChunk = GIV(freeLists)[index]; + + /* begin setNextFreeChunkOf:withValue:chunkBytes: */ + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + lilliputian = chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if ((nextFreeChunk != 0) + && (!lilliputian)) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((freeChunk == 0) + || (isFreeObject(freeChunk))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),freeChunk); + } + + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + if (!(chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */))) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + GIV(freeLists)[index] = freeChunk; + GIV(freeListsMask) = GIV(freeListsMask) | (1ULL << index); + return 0; + } + + /* begin addToFreeTree:bytes: */ + /* begin initFreeTreeChunk:bytes: */ + assert(isFreeObject(freeChunk)); + assert(chunkBytes == (bytesInBody(freeChunk))); + assert(chunkBytes >= ((numFreeLists()) * (allocationUnit()))); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord()))),0); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),0); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (2U << (shiftForWord()))),0); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (3U << (shiftForWord()))),0); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (4U << (shiftForWord()))),0); + + /* Large chunk list organized as a tree, each node of which is a list of chunks of the same size. + Beneath the node are smaller and larger blocks. */ + parent = 0; + child = GIV(freeLists)[0]; + while (child != 0) { + childBytes = bytesInBody(child); + + /* check for overlap; could write this as self oop: (self objectAfter: freeChunk) isLessThanOrEqualTo: child... + but that relies on headers being correct, etc. So keep it clumsy... */ + assert((oopisLessThanOrEqualTo((freeChunk + chunkBytes) - BaseHeaderSize, child)) + || (oopisGreaterThanOrEqualTo(freeChunk, (child + childBytes) - BaseHeaderSize))); + if (childBytes == chunkBytes) { + nextFreeChunk = longAt((void *)((child + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((freeChunk == 0) + || (isFreeObject(freeChunk))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),freeChunk); + } + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(child)); + assert((freeChunk == 0) + || (isFreeObject(freeChunk))); + longAtput((void *)((child + BaseHeaderSize) + (0U << (shiftForWord()))),freeChunk); + if (freeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + assert((child == 0) + || (isFreeObject(child))); + longAtput((void *)((freeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),child); + } + return child; + } + + /* size match; add to list at node. + walk down the tree */ + parent = child; + child = longAt((void *)((child + BaseHeaderSize) + ((((usqInt)(((childBytes > chunkBytes + ? 3 /* freeChunkSmallerIndex */ + : 4 /* freeChunkLargerIndex */))) << (shiftForWord())))))); + } + if (!parent) { + assert((GIV(freeLists)[0]) == 0); + GIV(freeLists)[0] = freeChunk; + GIV(freeListsMask) = GIV(freeListsMask) | 1; + return 0; + } + assert(((GIV(freeListsMask) & 1) != 0)); + + /* insert in tree */ + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + assert((parent == 0) + || (isFreeObject(parent))); + longAtput((void *)((freeChunk + BaseHeaderSize) + (2U << (shiftForWord()))),parent); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(parent)); + assert((freeChunk == 0) + || (isFreeObject(freeChunk))); + longAtput((void *)((parent + BaseHeaderSize) + ((((usqInt)(((childBytes > chunkBytes + ? 3 /* freeChunkSmallerIndex */ + : 4 /* freeChunkLargerIndex */))) << (shiftForWord()))))),freeChunk); + return 0; +} + + +/* Attempt to answer an array of all objects, excluding those that may + be garbage collected as a side effect of allocating the result array. + If no memory is available answer the number of instances as a + SmallInteger. Since objects are at least 16 bytes big, and the largest + SmallInteger covers + 1/4 of the address space, the count can never overflow. */ + + /* SpurMemoryManager>>#allInstancesOf: */ +static NoDbgRegParms sqInt +allInstancesOf(sqInt aClass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt actualIndex; + usqInt bytes; + int classIndex; + sqInt classOrNil; + sqInt count; + sqInt countSqInt; + int expectedIndex; + int expectedIndexInt; + sqInt followingWord; + usqInt followingWordAddress; + sqInt freeChunk; + sqInt i; + sqInt index; + sqInt iSqInt; + sqInt j; + usqInt limit; + usqInt newObj; + usqInt numBytes; + usqInt numSlots; + sqInt objOop; + sqInt page; + sqInt prevObj; + sqInt prevPrevObj; + sqInt ptr; + sqInt ptrSqInt; + sqInt smallObj; + usqInt start; + usqInt startUsqInt; + sqInt toDoLimit; + sqInt valuePointer; + + classIndex = (long32At((void *)(aClass + 4))) & (identityHashHalfWordMask()); + if (!classIndex) { + /* begin allocateSlots:format:classIndex: */ + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + 8 /* allocationUnit */; + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (0 <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + freeChunk = allocateSlotsInOldSpacebytesformatclassIndex(0, numBytes, arrayFormat(), ClassArrayCompactIndex); + goto l1; + } + longAtput((void *)(newObj),((((((usqLong) 0)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + freeChunk = newObj; + /* end allocateSlots:format:classIndex: */ +l1: + return freeChunk; + } + + /* N.B. Does /not/ update totalFreeOldSpace */ + freeChunk = allocateLargestFreeChunk(); + start = freeChunk + BaseHeaderSize; + limit = addressAfter(freeChunk); + + /* begin isClassAtUniqueIndex: */ + expectedIndex = (long32At((void *)(aClass + 4))) & (identityHashHalfWordMask()); + for (iSqInt = 0; iSqInt < GIV(numClassTablePages); iSqInt += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNil = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNil != GIV(nilObj)) { + index = ((((usqInt)(iSqInt) << (classTableMajorIndexShift())))) + j; + if ((classOrNil == aClass) + && ((index != expectedIndex) + && (index > (lastClassIndexPun())))) { + goto l4; + } + } + } + } + + /* begin uniqueIndex:allInstancesInto:limit:resultsInto: */ + countSqInt = 0; + ptrSqInt = start; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + + /* continue enumerating even if no room so as to unmark all objects. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + countSqInt += 1; + if (ptrSqInt < limit) { + longAtput((void *)(ptrSqInt),objOop); + ptrSqInt += BytesPerOop; + } + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l10; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l10:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + startUsqInt = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (startUsqInt > GIV(freeStart)) { + goto l9; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(startUsqInt + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? startUsqInt + BaseHeaderSize + : startUsqInt); + while (oopisLessThan(objOop, GIV(freeStart))) { + /* continue enumerating even if no room so as to unmark all objects. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + countSqInt += 1; + if (ptrSqInt < limit) { + longAtput((void *)(ptrSqInt),objOop); + ptrSqInt += BytesPerOop; + } + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l8; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l8:; + } + /* end allNewSpaceEntitiesDo: */ +l9: + count = countSqInt; + ptr = ptrSqInt; + goto l3; +l4: + + /* begin ambiguousClass:allInstancesInto:limit:resultsInto: */ + countSqInt = 0; + ptrSqInt = start; + expectedIndex = (long32At((void *)(aClass + 4))) & (identityHashHalfWordMask()); + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + + /* continue enumerating even if no room so as to unmark all objects and/or normalize class indices. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + actualIndex = (longAt((void *)(objOop))) & (classIndexMask()); + if ((classOrNilAtIndex(actualIndex)) == aClass) { + if (actualIndex != expectedIndex) { + /* begin setClassIndexOf:to: */ + assert(((expectedIndex >= 0) && (expectedIndex <= (classIndexMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(classIndexMask()))) + expectedIndex); + } + countSqInt += 1; + if (ptrSqInt < limit) { + longAtput((void *)(ptrSqInt),objOop); + ptrSqInt += BytesPerOop; + } + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l7; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l7:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + startUsqInt = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (startUsqInt > GIV(freeStart)) { + goto l6; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(startUsqInt + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? startUsqInt + BaseHeaderSize + : startUsqInt); + while (oopisLessThan(objOop, GIV(freeStart))) { + /* continue enumerating even if no room so as to unmark all objects and/or normalize class indices. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + actualIndex = (longAt((void *)(objOop))) & (classIndexMask()); + if ((classOrNilAtIndex(actualIndex)) == aClass) { + if (actualIndex != expectedIndex) { + /* begin setClassIndexOf:to: */ + assert(((expectedIndex >= 0) && (expectedIndex <= (classIndexMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(classIndexMask()))) + expectedIndex); + } + countSqInt += 1; + if (ptrSqInt < limit) { + longAtput((void *)(ptrSqInt),objOop); + ptrSqInt += BytesPerOop; + } + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l5; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l5:; + } + /* end allNewSpaceEntitiesDo: */ +l6: + + /* begin purgeDuplicateClassTableEntriesFor: */ + expectedIndexInt = (long32At((void *)(aClass + 4))) & (identityHashHalfWordMask()); + for (iSqInt = 0; iSqInt < GIV(numClassTablePages); iSqInt += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNil = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNil != GIV(nilObj)) { + index = ((((usqInt)(iSqInt) << (classTableMajorIndexShift())))) + j; + if ((classOrNil == aClass) + && ((index != expectedIndexInt) + && (index > (lastClassIndexPun())))) { + classAtIndexput(index, GIV(nilObj)); + if (index < GIV(classTableIndex)) { + GIV(classTableIndex) = index; + } + } + } + } + } + + /* classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + assert(GIV(classTableIndex) >= (1U << (classTableMajorIndexShift()))); + count = countSqInt; + ptr = ptrSqInt; +l3: + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + if ((count > ((ptr - start) / BytesPerOop)) + || ((limit != ptr) + && ((limit - ptr) <= 8 /* allocationUnit */))) { + freeChunkWithBytesat(bytesInBody(freeChunk), /* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk)); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return (((usqInt)count << 3) | 1); + } + + /* not enough room + can't split a single word */ + if (count < (numSlotsMask())) { + /* begin allocateSlots:format:classIndex: */ + if (count >= (numSlotsMask())) { + if ((((usqInt)(count)) >> 56) > 0) { + smallObj = null; + goto l2; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (count * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((count < 1 + ? 8 /* allocationUnit */ + : count * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (count <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + smallObj = allocateSlotsInOldSpacebytesformatclassIndex(count, numBytes, arrayFormat(), ClassArrayCompactIndex); + goto l2; + } + if (count >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + count); + longAtput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + } + else { + longAtput((void *)(newObj),((((((usqLong) count)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + smallObj = newObj; + /* end allocateSlots:format:classIndex: */ +l2: + for (i = 0; i < count; i += 1) { + valuePointer = longAt((void *)((freeChunk + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(smallObj)) + && (!(isForwarded(smallObj)))); + assert(validStorePointerUncheckedArgs(i, smallObj, valuePointer)); + longAtput((void *)((smallObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + freeChunkWithBytesat(bytesInBody(freeChunk), /* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk)); + beRootIfOld(smallObj); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return smallObj; + } + bytes = (BaseHeaderSize + BaseHeaderSize) + (count * BytesPerOop); + start = /* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk); + freeChunkWithBytesat((limit - start) - bytes, start + bytes); + GIV(totalFreeOldSpace) -= bytes; + + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(freeChunk - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56))) + count); + + /* begin set:classIndexTo:formatTo: */ + assert(((ClassArrayCompactIndex >= 0) && (ClassArrayCompactIndex <= (classIndexMask())))); + assert((((arrayFormat()) >= 0) && ((arrayFormat()) <= (formatMask())))); + longAtput((void *)(freeChunk),((longAt((void *)(freeChunk))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + (ClassArrayCompactIndex + ((((usqInt)((arrayFormat())) << (formatShift())))))); + + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(freeChunk + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(freeChunk); + } + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + runLeakCheckerFor(GCModeFull); + return freeChunk; +} + + +/* Attempt to answer an array of all objects, excluding those that may + be garbage collected as a side effect of allocating the result array. + If no memory is available answer the number of objects as a SmallInteger. + Since objects are at least 16 bytes big, and the largest SmallInteger + covers 1/4 of the address space, the count can never overflow. */ + + /* SpurMemoryManager>>#allObjects */ +static sqInt +allObjects(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytes; + sqInt count; + sqInt followingWord; + usqInt followingWordAddress; + sqInt freeChunk; + usqInt limit; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + sqInt ptr; + usqInt start; + usqInt startUsqInt; + + + /* N.B. Does /not/ update totalFreeOldSpace */ + freeChunk = allocateLargestFreeChunk(); + ptr = (start = freeChunk + BaseHeaderSize); + limit = addressAfter(freeChunk); + count = 0; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + + /* continue enumerating even if no room so as to unmark all objects. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + count += 1; + if (ptr < limit) { + longAtput((void *)(ptr),objOop); + ptr += BytesPerOop; + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + startUsqInt = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (startUsqInt > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(startUsqInt + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? startUsqInt + BaseHeaderSize + : startUsqInt); + while (oopisLessThan(objOop, GIV(freeStart))) { + /* continue enumerating even if no room so as to unmark all objects. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + count += 1; + if (ptr < limit) { + longAtput((void *)(ptr),objOop); + ptr += BytesPerOop; + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3: + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + assert(count >= (numSlotsMask())); + if ((count > ((ptr - start) / BytesPerOop)) + || ((limit != ptr) + && ((limit - ptr) <= 8 /* allocationUnit */))) { + freeChunkWithBytesat(bytesInBody(freeChunk), /* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk)); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return (((usqInt)count << 3) | 1); + } + + /* not enough room + can't split a single word */ + bytes = (BaseHeaderSize + BaseHeaderSize) + (count * BytesPerOop); + start = /* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk); + freeChunkWithBytesat((limit - start) - bytes, start + bytes); + GIV(totalFreeOldSpace) -= bytes; + + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(freeChunk - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56))) + count); + + /* begin set:classIndexTo:formatTo: */ + assert(((ClassArrayCompactIndex >= 0) && (ClassArrayCompactIndex <= (classIndexMask())))); + assert((((arrayFormat()) >= 0) && ((arrayFormat()) <= (formatMask())))); + longAtput((void *)(freeChunk),((longAt((void *)(freeChunk))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + (ClassArrayCompactIndex + ((((usqInt)((arrayFormat())) << (formatShift())))))); + + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(freeChunk + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(freeChunk); + } + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + runLeakCheckerFor(GCModeFull); + return freeChunk; +} + + /* SpurMemoryManager>>#allObjectsUnmarked */ +static sqInt +allObjectsUnmarked(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + GIV(bogon) = obj; + return 0; + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + return 1; +} + + /* SpurMemoryManager>>#allOldMarkedWeakObjectsOnWeaklingStack */ +static sqInt +allOldMarkedWeakObjectsOnWeaklingStack(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if ((isWeakNonImm(objOop)) + && (((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) { + if (!(isonObjStack(objOop, GIV(weaklingStack)))) { + return 0; + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + return 1; +} + + +/* Answer the largest free chunk in the free lists. */ +/* would like to use ifNotNil: but the ^next inside the ^blah ifNotNil: + confused Slang + */ + + /* SpurMemoryManager>>#allocateLargestFreeChunk */ +static sqInt +allocateLargestFreeChunk(void) +{ + sqInt freeChunk; + sqInt next; + sqInt nextFreeChunk; + + freeChunk = findLargestFreeChunk(); + if (!freeChunk) { + return null; + } + + /* This will be the node, not a list element. Answer a list element in preference. */ + next = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + if (next) { + assert((bytesInBody(freeChunk)) >= (numFreeLists())); + nextFreeChunk = longAt((void *)((next + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((freeChunk == 0) + || (isFreeObject(freeChunk))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),freeChunk); + } + return next; + } + unlinkSolitaryFreeTreeNode(freeChunk); + return freeChunk; +} + + +/* Answer a chunk of oldSpace from the free lists, if available, + otherwise answer nil. Break up a larger chunk if one of the + exact size does not exist. N.B. the chunk is simply a pointer, it + has no valid header. The caller *must* fill in the header correctly. */ +/* for debugging: */ +/* totalFreeOldSpace := self totalFreeListBytes */ + + /* SpurMemoryManager>>#allocateOldSpaceChunkOfBytes: */ +static NoDbgRegParms sqInt +allocateOldSpaceChunkOfBytes(usqInt chunkBytes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt child; + usqInt childBytes; + sqInt chunk; + usqInt index; + usqInt initialIndex; + sqInt lilliputian; + sqInt next; + sqInt nextFreeChunk; + usqInt nodeBytes; + sqInt parent; + + nodeBytes = 0; + + /* be optimistic (& don't wait for the write) */ + GIV(totalFreeOldSpace) -= chunkBytes; + initialIndex = chunkBytes / 8 /* allocationUnit */; + if ((initialIndex < 64 /* numFreeLists */) + && ((1ULL << initialIndex) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << initialIndex)) != 0)) { + if ((chunk = GIV(freeLists)[initialIndex])) { + assert(chunk == (startOfObject(chunk))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(chunk)); + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + lilliputian = chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */); + + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(chunk)) == (initialIndex * (allocationUnit()))) + && ((initialIndex > 1) + && ((startOfObject(chunk)) == chunk))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[initialIndex] = ((next = longAt((void *)((chunk + BaseHeaderSize) + (0U << (shiftForWord())))))); + if ((!lilliputian) + && (next != 0)) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + return chunk; + } + GIV(freeListsMask) -= 1ULL << initialIndex; + } + + /* first search for free chunks of a multiple of chunkBytes in size */ + index = initialIndex; + while ((((index += index)) < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << index)) != 0)) { + if ((chunk = GIV(freeLists)[index])) { + assert(chunk == (startOfObject(chunk))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(chunk)); + assert(((bytesInBody(chunk)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(chunk)) == chunk))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((next = longAt((void *)((chunk + BaseHeaderSize) + (0U << (shiftForWord())))))); + if (next) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + assert((bytesInBody(chunk)) == (index * (allocationUnit()))); + freeChunkWithBytesat((index * 8 /* allocationUnit */) - chunkBytes, (/* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk)) + chunkBytes); + return chunk; + } + GIV(freeListsMask) -= 1ULL << index; + } + } + + /* now get desperate and use the first that'll fit. + Note that because the minimum free size is 16 bytes (2 * allocationUnit), to + leave room for the forwarding pointer/next free link, we can only break chunks + that are at least 16 bytes larger, hence start at initialIndex + 2. */ + index = initialIndex + 1; + while ((((index += 1)) < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << index)) != 0)) { + if ((chunk = GIV(freeLists)[index])) { + assert(chunk == (startOfObject(chunk))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(chunk)); + assert(((bytesInBody(chunk)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(chunk)) == chunk))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((next = longAt((void *)((chunk + BaseHeaderSize) + (0U << (shiftForWord())))))); + if (next) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + assert((bytesInBody(chunk)) == (index * (allocationUnit()))); + freeChunkWithBytesat((index * 8 /* allocationUnit */) - chunkBytes, (/* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk)) + chunkBytes); + return chunk; + } + GIV(freeListsMask) -= 1ULL << index; + } + } + } + + /* Large chunk, or no space on small free lists. Search the large chunk list. + Large chunk list organized as a tree, each node of which is a list of chunks + of the same size. Beneath the node are smaller and larger blocks. + When the search ends parent should hold the smallest chunk at least as + large as chunkBytes, or 0 if none. */ + parent = 0; + child = GIV(freeLists)[0]; + while (child != 0) { + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(child)); + childBytes = bytesInBody(child); + if (childBytes == chunkBytes) { + chunk = longAt((void *)((child + BaseHeaderSize) + (0U << (shiftForWord())))); + if (chunk) { + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(chunk)); + nextFreeChunk = longAt((void *)((chunk + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(child)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((child + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((child == 0) + || (isFreeObject(child))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),child); + } + return /* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk); + } + nodeBytes = childBytes; + parent = child; + + /* break out of loop to remove interior node */ + child = 0; + } + else { + if (childBytes <= (chunkBytes + 8 /* allocationUnit */)) { + child = longAt((void *)((child + BaseHeaderSize) + (4U << (shiftForWord())))); + } + else { + /* parent will be smallest node >= chunkBytes + allocationUnit */ + parent = child; + nodeBytes = childBytes; + child = longAt((void *)((child + BaseHeaderSize) + (3U << (shiftForWord())))); + } + } + } + if (!parent) { + /* optimism was unfounded */ + GIV(totalFreeOldSpace) += chunkBytes; + return null; + } + + /* self printFreeChunk: parent */ + assert((nodeBytes == chunkBytes) + || (nodeBytes >= (chunkBytes + (2 * (allocationUnit()))))); + assert((bytesInBody(parent)) == nodeBytes); + + /* attempt to remove from list */ + chunk = longAt((void *)((parent + BaseHeaderSize) + (0U << (shiftForWord())))); + if (chunk) { + assert((chunkBytes == nodeBytes) + || ((chunkBytes + (allocationUnit())) < nodeBytes)); + nextFreeChunk = longAt((void *)((chunk + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(parent)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((parent + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((parent == 0) + || (isFreeObject(parent))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),parent); + } + if (chunkBytes != nodeBytes) { + freeChunkWithBytesat(nodeBytes - chunkBytes, (/* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk)) + chunkBytes); + } + return /* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk); + } + + /* no list; remove the interior node */ + chunk = parent; + unlinkSolitaryFreeTreeNode(chunk); + + /* if there's space left over, add the fragment back. */ + if (chunkBytes != nodeBytes) { + freeChunkWithBytesat(nodeBytes - chunkBytes, (/* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk)) + chunkBytes); + } + return /* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk); +} + + /* SpurMemoryManager>>#allocatePinnedSlots: */ +sqInt +allocatePinnedSlots(sqInt nSlots) +{ + sqInt obj; + usqInt p; + usqInt toDoLimit; + + obj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(nSlots, /* objectBytesForSlots: */ + (nSlots + ? ((((usqInt)(nSlots) << (shiftForWord())))) + ((nSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (obj) { + assert(isPinned(obj)); + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((obj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1, addressAfter(obj))); + toDoLimit = ((usqInt)(((obj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(obj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + return obj; +} + + +/* Answer the oop of a chunk of space in oldSpace with numSlots slots. The + header will have been filled-in but not the contents. If no memory is + available answer nil. */ + + /* SpurMemoryManager>>#allocateSlotsInOldSpace:bytes:format:classIndex: */ +static NoDbgRegParms sqInt +allocateSlotsInOldSpacebytesformatclassIndex(sqInt numSlots, usqInt totalBytes, sqInt formatField, sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt chunk; + + if ((chunk = allocateOldSpaceChunkOfBytes(totalBytes))) { + if (numSlots >= (numSlotsMask())) { + long64Atput((void *)(chunk),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + long64Atput((void *)(chunk + BaseHeaderSize),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex); + + /* begin checkFreeSpace:ignoring: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, chunk + BaseHeaderSize); + } + return chunk + BaseHeaderSize; + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + long64Atput((void *)(chunk),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex); + + /* begin checkFreeSpace:ignoring: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, chunk); + } + return chunk; + } + return 0; +} + + +/* All objects are a multiple of 8 bytes in length */ + + /* SpurMemoryManager>>#allocationUnit */ +static sqInt +allocationUnit(void) +{ + return 8; +} + + +/* N.B. generateToByDoLimitExpression:negative:on: guards against (unsigned)0 + - 1 going +ve + */ + + /* SpurMemoryManager>>#allStrongSlotsOfWeaklingAreMarked: */ +static NoDbgRegParms sqInt +allStrongSlotsOfWeaklingAreMarked(sqInt aWeakling) +{ + sqInt i; + sqInt referent; + sqInt toDoLimit; + + toDoLimit = (numStrongSlotsOfWeakling(aWeakling)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((aWeakling + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + if (!((byteAt((void *)(referent + (markBitsByteOffset())))) & (1U << (markedBitByteShift())))) { + return 0; + } + } + } + return 1; +} + + /* SpurMemoryManager>>#allUnscannedEphemeronsAreActive */ +static sqInt +allUnscannedEphemeronsAreActive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt key; + usqInt p; + + for (p = ((GIV(unscannedEphemerons).start)); p <= ((GIV(unscannedEphemerons).top)); p += BytesPerOop) { + /* begin keyOfMaybeFiredEphemeron: */ + assert(isMaybeFiredEphemeron(longAt((void *)(p)))); + key = longAt((void *)(((longAt((void *)(p))) + BaseHeaderSize) + (0U << (shiftForWord())))); + if ((((key & (tagMask())) != 0)) + || (((byteAt((void *)(key + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) { + return 0; + } + } + return 1; +} + + +/* we don't want to inline so we can nest that in an assertion with the + return true so the production VM does not generate any code here, while in + simulation, the code breaks on the assertion we want to. + */ + + /* SpurMemoryManager>>#assertInnerValidFreeObject: */ +static NoDbgRegParms NeverInline sqInt +assertInnerValidFreeObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cameFrom; + sqInt chunk; + sqInt chunkBytes; + usqInt index; + sqInt largeChild; + sqInt smallChild; + sqInt treeNode; + + assert(oopisLessThanOrEqualTo(addressAfter(objOop), GIV(endOfMemory))); + chunk = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert((chunk == 0) + || (isFreeOop(chunk))); + chunkBytes = bytesInBody(objOop); + + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + if (!(chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */))) { + /* double linkedlist assertions */ + chunk = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (chunk) { + assert(isFreeOop(chunk)); + assert(objOop == (fetchPointerofFreeChunk(freeChunkPrevIndex(), chunk))); + } + chunk = longAt((void *)((objOop + BaseHeaderSize) + (1U << (shiftForWord())))); + index = (bytesInBody(objOop)) / 8 /* allocationUnit */; + if ((index < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + if ((GIV(freeLists)[index]) == objOop) { + assert(chunk == 0); + } + } + else { + /* begin freeTreeNodesDo: */ + treeNode = GIV(freeLists)[0]; + if (!treeNode) { + goto l1; + } + cameFrom = -1; + do { + assert((bytesInBody(treeNode)) >= ((numFreeLists()) * (allocationUnit()))); + smallChild = longAt((void *)((treeNode + BaseHeaderSize) + (3U << (shiftForWord())))); + largeChild = longAt((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + assert((smallChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), smallChild)))); + assert((largeChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), largeChild)))); + + /* apply if the node has no children, or it has no large children and we're + returning from the small child, or we're returning from the large child. */ + if (((smallChild == 0) + && (largeChild == 0)) + || ((largeChild + ? cameFrom == largeChild + : cameFrom == smallChild))) { + if (treeNode == objOop) { + assert(chunk == 0); + } + + /* and since we've applied we must move on up */ + cameFrom = treeNode; + treeNode = longAt((void *)((treeNode + BaseHeaderSize) + (2U << (shiftForWord())))); + } + else { + if ((smallChild != 0) + && (cameFrom != smallChild)) { + treeNode = smallChild; + } + else { + assert(largeChild != 0); + treeNode = largeChild; + } + cameFrom = -1; + } + } while(treeNode != 0); + /* end freeTreeNodesDo: */ +l1:; + } + if (chunk) { + assert(isFreeOop(chunk)); + assert(objOop == (fetchPointerofFreeChunk(freeChunkNextIndex(), chunk))); + } + } + if ((bytesInBody(objOop)) >= 0x200 /* (numFreeLists * #allocationUnit) */) { + /* Tree assertions */ + chunk = longAt((void *)((objOop + BaseHeaderSize) + (2U << (shiftForWord())))); + assert((chunk == 0) + || ((isFreeOop(chunk)) + && (isLargeFreeObject(chunk)))); + chunk = longAt((void *)((objOop + BaseHeaderSize) + (3U << (shiftForWord())))); + assert((chunk == 0) + || ((isFreeOop(chunk)) + && (isLargeFreeObject(chunk)))); + chunk = longAt((void *)((objOop + BaseHeaderSize) + (4U << (shiftForWord())))); + assert((chunk == 0) + || ((isFreeOop(chunk)) + && (isLargeFreeObject(chunk)))); + } + return 1; +} + + /* SpurMemoryManager>>#become:with: */ +sqInt +becomewith(sqInt array1, sqInt array2) +{ + return becomewithtwoWaycopyHash(array1, array2, 1, 1); +} + + +/* All references to each object in array1 are swapped with all references to + the corresponding object in array2. That is, all pointers to one object + are replaced + with with pointers to the other. The arguments must be arrays of the same + length. + Answers PrimNoErr if the primitive succeeds, otherwise a relevant error + code. + */ +/* Implementation: Uses lazy forwarding to defer updating references until + message send. + */ + + /* SpurMemoryManager>>#become:with:twoWay:copyHash: */ +static NoDbgRegParms sqInt +becomewithtwoWaycopyHash(sqInt array1, sqInt array2, sqInt twoWayFlag, sqInt copyHashFlag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt contextSize; + sqInt ec; + sqInt fieldOffset; + sqInt fmt; + sqInt hash; + sqInt header; + sqInt headerSqInt; + sqInt i; + sqInt newObj1; + sqInt newObj2; + usqInt numSlots; + usqInt numSlotsUsqInt; + usqLong o1ClassIndex; + sqLong o1Header; + usqLong o2ClassIndex; + sqLong o2Header; + sqInt obj1; + sqInt obj2; + sqInt objOop; + sqInt objOopSqInt; + sqInt oop1; + sqInt oop2; + sqInt referent; + sqInt referentSqInt; + sqInt size; + sqInt sp; + sqInt toDoLimit; + sqInt top; + + assert(GIV(becomeEffectsFlags) == 0); + runLeakCheckerFor(GCModeBecome); + if (!(/* isArray: */ + ((!(array1 & (tagMask())))) + && (((byteAt((void *)(array1 + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + return PrimErrBadReceiver; + } + if (!((/* isArray: */ + ((!(array2 & (tagMask())))) + && (((byteAt((void *)(array2 + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(array1)) > (isForwardedObjectClassIndexPun())), + (((numSlotsUsqInt = byteAt((void *)(array1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) == ((/* begin numSlotsOf: */ + assert((classIndexOf(array2)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(array2 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array2 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))))) { + return PrimErrBadArgument; + } + + /* begin containsOnlyValidBecomeObjects:and:twoWay:copyHash: */ + /* begin lastPointerOf: */ + fmt = (byteAt((void *)(array1 + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt != (forwardedFormat())); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(array1))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(array1))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + fieldOffset = (((CtxtTempFrameStart - 1) + contextSize) * BytesPerOop) + BaseHeaderSize; + goto l2; + } + fieldOffset = ((((/* begin numSlotsOf: */ + assert((classIndexOf(array1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(array1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1) * BytesPerOop) + BaseHeaderSize; + goto l2; + } + if (fmt < (firstCompiledMethodFormat())) { + fieldOffset = 0; + goto l2; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(array1)); + headerSqInt = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + fieldOffset = (((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) - 1) * BytesPerOop) + BaseHeaderSize; + /* end lastPointerOf: */ +l2: + size = 0; + + /* array1 is known to be the same size as array2 */ + while (fieldOffset >= BaseHeaderSize) { + oop1 = longAt((void *)(array1 + fieldOffset)); + if (/* isOopForwarded: */ + ((!(oop1 & (tagMask())))) + && ((!((longAt((void *)(oop1))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop1)); + referent = longAt((void *)((oop1 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop1 = referent; + longAtput((void *)(array1 + fieldOffset),oop1); + } + + /* begin ifOopInvalidForBecome:errorCodeInto: */ + if (((oop1 & (tagMask())) != 0)) { + ec = PrimErrInappropriate; + goto l5; + } + if ((byteAt((void *)(oop1 + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + ec = PrimErrObjectIsPinned; + goto l5; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(oop1 + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + ec = PrimErrNoModification; + goto l5; + } + /* end ifOopInvalidForBecome:errorCodeInto: */ +l3: + oop2 = longAt((void *)(array2 + fieldOffset)); + if (/* isOopForwarded: */ + ((!(oop2 & (tagMask())))) + && ((!((longAt((void *)(oop2))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop2)); + referentSqInt = longAt((void *)((oop2 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop2 = referentSqInt; + longAtput((void *)(array2 + fieldOffset),oop2); + } + if (oop1 != oop2) { + if (twoWayFlag) { + /* begin ifOopInvalidForBecome:errorCodeInto: */ + if (((oop2 & (tagMask())) != 0)) { + ec = PrimErrInappropriate; + goto l5; + } + if ((byteAt((void *)(oop2 + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + ec = PrimErrObjectIsPinned; + goto l5; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(oop2 + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + ) { + ec = PrimErrNoModification; + goto l5; + } + /* end ifOopInvalidForBecome:errorCodeInto: */ +l4: + size = (size + (bytesInBody(oop1))) + (bytesInBody(oop2)); + GIV(becomeEffectsFlags) = (GIV(becomeEffectsFlags) | (becomeEffectFlagsFor(oop1))) | (becomeEffectFlagsFor(oop2)); + } + else { + if (copyHashFlag) { + if (((oop2 & (tagMask())) != 0)) { + ec = PrimErrInappropriate; + goto l5; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(oop2 + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + ) { + ec = PrimErrNoModification; + goto l5; + } + } + GIV(becomeEffectsFlags) = GIV(becomeEffectsFlags) | (becomeEffectFlagsFor(oop1)); + } + } + fieldOffset -= BytesPerOop; + } + if (size >= (GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart)))) { + ec = PrimErrNoMemory; + goto l5; + } + ec = 0; + /* end containsOnlyValidBecomeObjects:and:twoWay:copyHash: */ +l5: + if (ec) { + /* begin zeroBecomeEffectFlagsOnFailure */ + if (((GIV(becomeEffectsFlags) & BecameJittedCompiledMethodFlag) != 0)) { + unflagBecomeFlaggedMethods(); + } + GIV(becomeEffectsFlags) = 0; + return ec; + } + + /* begin preBecomeAction: */ + if (GIV(stackPage)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + + /* see the comment in InterpreterPrimitives>>primitiveArrayBecome */ + if (((GIV(becomeEffectsFlags) & BecameJittedCompiledMethodFlag) != 0)) { + /* begin insulateFramesAndContextsFromCogMethodsFlaggedForBecome */ + activeContext = divorceAllFramesSuchThat(isMachineCodeFrameForCogMethodFlaggedForBecome); + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + assert((GIV(framePointer) != 0) == (isStillMarriedContext(activeContext))); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + objOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, objOop, activeContext)); + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeContext & (tagMask())))) + && (oopisLessThan(activeContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),activeContext); + } + } + + /* Need to set gcMode var (to avoid passing the flag through a lot of the updating code) */ + GIV(gcMode) = GCModeBecome; + if (twoWayFlag) { + /* begin innerBecomeObjectsIn:and:copyHash: */ + toDoLimit = ((assert((classIndexOf(array1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(array1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + /* At first blush it would appear unnecessary to use followField: here since + the validation in become:with:twoWay:copyHash: follows forwarders. But + there's nothing to ensure all elements of each array are unique and don't + appear in the other array. So the enumeration could encounter an object + already becommed earlier in the same enumeration. */ + + /* begin followField:ofObject: */ + obj1 = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(obj1 & (tagMask())))) + && ((!((longAt((void *)(obj1))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + obj1 = fixFollowedFieldofObjectwithInitialValue(i, array1, obj1); + } + + /* begin followField:ofObject: */ + obj2 = longAt((void *)((array2 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(obj2 & (tagMask())))) + && ((!((longAt((void *)(obj2))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + obj2 = fixFollowedFieldofObjectwithInitialValue(i, array2, obj2); + } + if (obj1 != obj2) { + /* begin doBecome:and:copyHash: */ + o1Header = long64At((void *)(obj1)); + o1ClassIndex = (((usqInt)(obj1)) >> (identityHashFullWordShift())) & (identityHashHalfWordMask()); + if ((o1ClassIndex != 0) + && ((classAtIndex(o1ClassIndex)) != obj1)) { + o1ClassIndex = 0; + } + o2Header = long64At((void *)(obj2)); + o2ClassIndex = (((usqInt)(obj2)) >> (identityHashFullWordShift())) & (identityHashHalfWordMask()); + if ((o2ClassIndex != 0) + && ((classAtIndex(o2ClassIndex)) != obj2)) { + o2ClassIndex = 0; + } + + /* Refuse to do an in-place become on classes since their being + forwarded is used in the flush method cache implementations. + Refuse to do an in-place become on compiled code because + doing so breaks the unmapping of methods from cog methods. */ + if ((((/* begin numSlotsOf: */ + assert((classIndexOf(obj1)) > (isForwardedObjectClassIndexPun())), + (((numSlotsUsqInt = byteAt((void *)(obj1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) == ((/* begin numSlotsOf: */ + assert((classIndexOf(obj2)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(obj2 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj2 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))) + && (((o1ClassIndex + o2ClassIndex) == 0) + && ((!(isCompiledMethodFormat((((usqLong)(o1Header)) >> (formatShift())) & (formatMask())))) + && (!(isCompiledMethodFormat((((usqLong)(o2Header)) >> (formatShift())) & (formatMask()))))))) { + inPlaceBecomeandcopyHashFlag(obj1, obj2, copyHashFlag); + goto l6; + } + outOfPlaceBecomeandcopyHashFlag(obj1, obj2, copyHashFlag); + + /* if copyHashFlag then nothing changes, since hashes were also swapped. */ + if (copyHashFlag) { + goto l6; + } + + /* if copyHash is false then the classTable entries must be updated. + We leave the following until postBecomeScanClassTable:, but must + swap the forwarders if two active classes have been becommed, + and assign hashes if not. */ + if (o1ClassIndex) { + if (o2ClassIndex) { + classAtIndexput(o1ClassIndex, obj2); + classAtIndexput(o2ClassIndex, obj1); + } + else { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj1)); + newObj2 = longAt((void *)((obj1 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(newObj2 & (tagMask())))) + && ((!((longAt((void *)(newObj2))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + newObj2 = longAt((void *)((newObj2 + BaseHeaderSize) + (0U << (shiftForWord())))); + } + assert((rawHashBitsOf(newObj2)) == 0); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(newObj2 + 4),((((long32At((void *)(newObj2 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (o1ClassIndex & (identityHashHalfWordMask()))); + } + } + else { + if (o2ClassIndex) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj2)); + newObj1 = longAt((void *)((obj2 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(newObj1 & (tagMask())))) + && ((!((longAt((void *)(newObj1))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + newObj1 = longAt((void *)((newObj1 + BaseHeaderSize) + (0U << (shiftForWord())))); + } + assert((rawHashBitsOf(newObj1)) == 0); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(newObj1 + 4),((((long32At((void *)(newObj1 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (o2ClassIndex & (identityHashHalfWordMask()))); + } + } + /* end doBecome:and:copyHash: */ +l6: + objOop = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(i, array1, objOop); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((array2 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(i, array2, objOopSqInt); + } + } + } + } + else { + /* begin innerBecomeObjectsIn:to:copyHash: */ + toDoLimit = ((assert((classIndexOf(array1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(array1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + /* At first blush it would appear unnecessary to use followField: here since + the validation in become:with:twoWay:copyHash: follows forwarders. But + there's nothing to ensure all elements of each array is unique and doesn't + appear in the other array. So the enumeration could encounter an object + already becommed earlier in the same enumeration. */ + + /* begin followField:ofObject: */ + obj1 = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(obj1 & (tagMask())))) + && ((!((longAt((void *)(obj1))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + obj1 = fixFollowedFieldofObjectwithInitialValue(i, array1, obj1); + } + + /* begin followField:ofObject: */ + obj2 = longAt((void *)((array2 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(obj2 & (tagMask())))) + && ((!((longAt((void *)(obj2))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + obj2 = fixFollowedFieldofObjectwithInitialValue(i, array2, obj2); + } + if (obj1 != obj2) { + /* begin doBecome:to:copyHash: */ + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(obj1),((longAt((void *)(obj1))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(obj1),(longAt((void *)(obj1))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(obj1)); + assert(!(isOopForwarded(obj2))); + + /* begin isOldObject: */ + assert(isNonImmediate(obj1)); + if (oopisGreaterThanOrEqualTo(obj1, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj2 & (tagMask())))) + && (oopisLessThan(obj2, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj1 + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj1); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj1 + BaseHeaderSize) + (0U << (shiftForWord()))),obj2); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(obj1 + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(obj1 + (numSlotsFieldByteOffset())),1); + } + if (copyHashFlag) { + hash = (long32At((void *)(obj1 + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(obj2 + 4),((((long32At((void *)(obj2 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + } + if (((/* begin isOldObject: */ + assert(isNonImmediate(obj1)), + oopisGreaterThanOrEqualTo(obj1, GIV(oldSpaceStart)))) + && (/* isYoung: */ + ((!(obj2 & (tagMask())))) + && (oopisLessThan(obj2, GIV(oldSpaceStart))))) { + GIV(becomeEffectsFlags) = GIV(becomeEffectsFlags) | OldBecameNewFlag; + } + assert(!((isOopForwarded(obj2)))); + + /* begin followField:ofObject: */ + objOop = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(i, array1, objOop); + } + assert(!(isOopForwarded(obj2))); + } + } + } + + /* begin followSpecialObjectsOop */ + if ((!((longAt((void *)(GIV(specialObjectsOop)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + GIV(validatedIntegerClassFlags) = 0; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(GIV(specialObjectsOop))); + referent = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + GIV(specialObjectsOop) = referent; + } + followForwardedObjectFieldstoDepth(GIV(specialObjectsOop), 0); + + /* N.B. perform coInterpreter's postBecomeAction: *before* postBecomeScanClassTable: + to allow the coInterpreter to void method cache entries by spotting classIndices that + refer to forwarded objects. postBecomeScanClassTable: follows forwarders in the table. */ + + /* begin postBecomeAction: */ + spurPostBecomeAction(GIV(becomeEffectsFlags)); + if (((GIV(becomeEffectsFlags) & BecameJittedCompiledMethodFlag) != 0)) { + freeBecomeFlaggedMethods(); + } + followMovableLiteralsAndUpdateYoungReferrers(); + + /* begin nilUncoggableMethods */ + GIV(lastCoggableInterpretedBlockMethod) = (GIV(lastUncoggableInterpretedBlockMethod) = null); + assert(kosherYoungReferrers()); + GIV(gcMode) = 0; + postBecomeScanClassTable(GIV(becomeEffectsFlags)); + if (twoWayFlag) { + assert(validPostBecomeArrayContents(array1)); + assert(validPostBecomeArrayContents(array2)); + } + else { + assert(validPostBecomeArrayContents(array2)); + } + assert(validClassTableHashes()); + runLeakCheckerFor(GCModeBecome); + + /* begin zeroBecomeEffectFlagsOnSuccess */ + GIV(becomeEffectsFlags) = 0; + return PrimNoErr; +} + + +/* If this object is old, mark it as a root (because a new object + may be stored into it). */ + + /* SpurMemoryManager>>#beRootIfOld: */ +void +beRootIfOld(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin isOldObject: */ + assert(isNonImmediate(oop)); + if (oopisGreaterThanOrEqualTo(oop, GIV(oldSpaceStart))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(oop); + } + } +} + + /* SpurMemoryManager>>#bitsSetInFreeSpaceMaskForAllFreeLists */ +static sqInt +bitsSetInFreeSpaceMaskForAllFreeLists(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + if (((GIV(freeLists)[i]) != 0) + && ((!((1ULL << i) & GIV(freeListsMask))))) { + return 0; + } + } + return 1; +} + + /* SpurMemoryManager>>#bridgeSize */ +static sqInt +bridgeSize(void) +{ + return 2 * BaseHeaderSize; +} + + /* SpurMemoryManager>>#byteFormatForNumBytes: */ +static NoDbgRegParms sqInt +byteFormatForNumBytes(sqInt numBytes) +{ + return (firstByteFormat()) + ((8 - numBytes) & (BytesPerWord - 1)); +} + + /* SpurMemoryManager>>#byteFormatMask */ +static sqInt +byteFormatMask(void) +{ + return 24; +} + + /* SpurMemoryManager>>#byteSizeOf: */ +sqInt +byteSizeOf(sqInt oop) +{ + sqInt fmt; + usqInt numBytes; + usqInt numSlots; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin numBytesOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + return numBytes - (fmt & 7); + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + return numBytes; + } + if (fmt >= (firstShortFormat())) { + return numBytes - (((fmt & 3) << 1)); + } + + /* fmt >= self firstLongFormat */ + return numBytes - (((fmt & 1) << 2)); +} + + +/* Answer the basic element size for the receiver. Answer 0 for immediates + and CompiledCode + (element size could be wordSize for literals or 1 for bytes, so its + indeterminable). Answer + wordSize for pointer objects. Otherwise answer the actual element size of + a bits container. */ + + /* SpurMemoryManager>>#bytesPerElement: */ +unsigned char +bytesPerElement(sqInt oop) +{ + unsigned char byteSizes[16] = + { wordSize(), wordSize(), wordSize(), wordSize(), + wordSize(), wordSize(), wordSize(), 0, /* forwarder */ wordSize(), + 8, + 4, 4, + 2, 2, 2, 2 }; + sqInt fmt; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + return 0; + } + return 1; + } + return byteSizes[fmt]; +} + + +/* Attempt to change the class of the receiver to the argument given that the + format of the receiver matches the format of the argument. If successful, + answer 0, otherwise answer an error code indicating the reason for + failure. + Fail if the format of the receiver is incompatible with the format of the + argument, or if the argument is a fixed class and the receiver's size + differs from the size + that an instance of the argument should have. */ + + /* SpurMemoryManager>>#changeClassOf:to: */ +static NoDbgRegParms sqInt +changeClassOfto(sqInt rcvr, sqInt argClass) +{ + sqInt classFormat; + sqInt classIndex; + sqInt fixedFields; + sqInt fmt; + sqInt instBytes; + sqInt instFormat; + usqInt instSlots; + sqInt newFormat; + usqInt numBytes; + usqInt numSlots; + + instBytes = 0; + instSlots = 0; + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + return PrimErrNoModification; + } + classFormat = ((longAt((void *)((argClass + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + fixedFields = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + classFormat = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + instFormat = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + + /* Fail for inability to access classIndex before making contexts snapshot-safe. */ + if (((classIndex = ensureBehaviorHash(argClass))) < 0) { + return -classIndex; + } + + /* Now check the instance for compatibility and compute odd bits if necessary. */ + if (classFormat <= 5 /* lastPointerFormat */) { + if (instFormat > 5 /* lastPointerFormat */) { + return PrimErrInappropriate; + } + if ((((/* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? (instSlots = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8) + : (instSlots = numSlots)))) < fixedFields) + || ((instSlots > fixedFields) + && (/* isFixedSizePointerFormat: */ + (classFormat <= (nonIndexablePointerFormat())) + || (classFormat == (ephemeronFormat()))))) { + return PrimErrBadReceiver; + } + if ((instFormat == (indexablePointersFormat())) + && (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + makeContextSnapshotSafe(rcvr); + } + newFormat = classFormat; + } + else { + /* Fail if the class's format is somehow invalid. */ + if (classFormat != (classFormatFromInstFormat(classFormat))) { + return PrimErrBadArgument; + } + + /* begin numBytesOf: */ + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + instBytes = numBytes - (fmt & 7); + goto l1; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + instBytes = numBytes; + goto l1; + } + if (fmt >= (firstShortFormat())) { + instBytes = numBytes - (((fmt & 3) << 1)); + goto l1; + } + + /* fmt >= self firstLongFormat */ + instBytes = numBytes - (((fmt & 1) << 2)); + /* end numBytesOf: */ +l1: + switch (classFormat) { + case sixtyFourBitIndexableFormat(): + if (((instBytes & 7) != 0)) { + return PrimErrBadReceiver; + } + newFormat = classFormat; + break; + case firstLongFormat(): + if (((instBytes & 3) != 0)) { + return PrimErrBadReceiver; + } + newFormat = classFormat + ((2 - (((usqInt)(instBytes)) >> 2)) & 1); + break; + case firstShortFormat(): + if (((instBytes & 1) != 0)) { + return PrimErrBadReceiver; + } + newFormat = classFormat + ((4 - (((usqInt)(instBytes)) >> 1)) & 3); + break; + case firstByteFormat(): + newFormat = classFormat + ((8 - instBytes) & 7); + break; + case firstCompiledMethodFormat(): + if (classFormat != (firstCompiledMethodFormat())) { + return PrimErrInappropriate; + } + newFormat = instFormat; + break; + default: + return PrimErrInappropriate; + } + } + + /* begin set:classIndexTo:formatTo: */ + assert(((classIndex >= 0) && (classIndex <= (classIndexMask())))); + assert(((newFormat >= 0) && (newFormat <= (formatMask())))); + longAtput((void *)(rcvr),((longAt((void *)(rcvr))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + (classIndex + ((((usqInt)(newFormat) << (formatShift())))))); + assert((numBytesOf(rcvr)) == ((classFormat <= (lastPointerFormat()) + ? instSlots * BytesPerOop + : instBytes))); + + /* ok */ + return 0; +} + + /* SpurMemoryManager>>#characterObjectOf: */ +sqInt +characterObjectOf(sqInt characterCode) +{ + return ((((usqInt)(characterCode) << (numTagBits())))) + (characterTag()); +} + + +/* Immediate characters are unsigned */ + + /* SpurMemoryManager>>#characterValueOf: */ +usqInt +characterValueOf(sqInt oop) +{ + return ((((usqInt)oop))) >> (numTagBits()); +} + + /* SpurMemoryManager>>#cheapAddressCouldBeInHeap: */ +static NoDbgRegParms sqInt +cheapAddressCouldBeInHeap(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((address & (BytesPerWord - 1)) == 0) + && ((oopisGreaterThanOrEqualTo(address, GIV(newSpaceStart))) + && (oopisLessThan(address, GIV(endOfMemory)))); +} + + +/* Assumes zero-based array indexing. */ + + /* SpurMemoryManager>>#checkedLongAt: */ +sqInt +checkedLongAt(sqInt byteAddress) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!(isInMemory(byteAddress))) { + warning("checkedLongAt bad address"); + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + return longAt((void *)(byteAddress)); +} + + +/* Perform an integrity/leak check using the heapMap. Assume + clearLeakMapAndMapAccessibleFreeSpace has set a bit at each free chunk's + header. Scan all objects in the heap checking that no pointer points + to a free chunk and that all free chunks that refer to others refer to + marked chunks. Answer if all checks pass. */ + + /* SpurMemoryManager>>#checkHeapFreeSpaceIntegrity */ +static sqInt +checkHeapFreeSpaceIntegrity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt chunkBytes; + sqInt fi; + sqInt fieldOop; + sqInt followingWord; + usqInt followingWordAddress; + sqInt i; + usqInt numSlots; + sqInt objOop; + sqInt ok; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + sqInt toDoLimit; + sqInt toDoLimit1; + usqInt total; + + ok = 1; + total = 0; + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + if (GIV(freeLists)[i]) { + if (!(heapMapAtWord(pointerForOop(GIV(freeLists)[i])))) { + fprintf(GIV(transcript), + "leak in free list %d to non-free %p\n", + ((int) i), + ((void *)(GIV(freeLists)[i]))); + ok = 0; + } + } + } + + /* Excuse the duplication but performance is at a premium and we avoid + some tests by splitting the newSpace and oldSpace enumerations. */ + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l2; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + fprintf(GIV(transcript), + "young object %p is free\n", + ((void *)objOop)); + ok = 0; + } + else { + if (objOop != GIV(freeSpaceCheckOopToIgnore)) { + toDoLimit = (numPointerSlotsOf(objOop)) - 1; + for (fi = 0; fi <= toDoLimit; fi += 1) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + if ((!(fieldOop & (tagMask())))) { + if (heapMapAtWord(pointerForOop(fieldOop))) { + fprintf(GIV(transcript), + "object leak in %p @ %d = %p is free\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + /* end allNewSpaceEntitiesDo: */ +l2: + + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + if (!(heapMapAtWord(pointerForOop(objOop)))) { + fprintf(GIV(transcript), + "leak in free chunk %p is unmapped?! \n", + ((void *)objOop)); + ok = 0; + } + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) == 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ 0 = %p is unmapped\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + chunkBytes = bytesInBody(objOop); + + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + if (!(chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */))) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (1U << (shiftForWord())))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) == 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ 0 = %p is unmapped\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + if ((bytesInBody(objOop)) >= 0x200 /* (numFreeLists * #allocationUnit) */) { + for (fi = 2 /* freeChunkParentIndex */; fi <= 4 /* freeChunkLargerIndex */; fi += 1) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) == 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ %d = %p is unmapped\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + } + total += bytesInBody(objOop); + } + else { + if (objOop != GIV(freeSpaceCheckOopToIgnore)) { + toDoLimit1 = (numPointerSlotsOf(objOop)) - 1; + for (fi = 0; fi <= toDoLimit1; fi += 1) { + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + assert(fi == 0); + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + } + else { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + } + + /* We keep #fetchPointer:ofObject: API here for assertions */ + if ((!(fieldOop & (tagMask())))) { + if (heapMapAtWord(pointerForOop(fieldOop))) { + fprintf(GIV(transcript), + "object leak in %p @ %d = %p is free\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3:; + } + if (total != GIV(totalFreeOldSpace)) { + fprintf(GIV(transcript), + "incorrect totalFreeOldSpace; expected %d found %d\n", + ((int) GIV(totalFreeOldSpace)), + ((int) total)); + ok = 0; + } + return ok; +} + + +/* Perform an integrity/leak check using the heapMap. Assume + clearLeakMapAndMapAccessibleObjects has set a bit at each (non-free) + object's header. Scan all objects in the heap checking that every + pointer points to a header. Scan the rememberedSet, remapBuffer and + extraRootTable checking + that every entry is a pointer to a header. Check that the number of roots + is correct and that all + rememberedSet entries have their isRemembered: flag set. Answer if all + checks pass. */ + + /* SpurMemoryManager>>#checkHeapIntegrity:classIndicesShouldBeValid: */ +static NoDbgRegParms sqInt +checkHeapIntegrityclassIndicesShouldBeValid(sqInt excludeUnmarkedObjs, sqInt classIndicesShouldBeValid) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt chunkBytes; + sqInt classIndex; + sqInt classIndexSqInt; + sqInt classOop; + sqInt classTablePage; + sqInt containsYoung; + sqInt fi; + sqInt fieldIndex; + sqInt fieldOop; + sqInt followingWord; + usqInt followingWordAddress; + sqInt freeListOop; + sqInt i; + sqInt iSqInt; + sqInt numOnThisPage; + sqInt numRememberedObjectsInHeap; + usqInt numSlots; + sqInt obj; + sqInt objOop; + sqInt objStackPage; + sqInt ok; + sqInt prevObj; + sqInt prevPrevObj; + sqInt ri; + usqInt start; + sqInt toDoLimit; + sqInt toDoLimit1; + + ok = 1; + numRememberedObjectsInHeap = 0; + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + if ((freeListOop = GIV(freeLists)[i])) { + if (oopisGreaterThanOrEqualToandLessThan(freeListOop, GIV(oldSpaceStart), GIV(endOfMemory))) { + if (heapMapAtWord(pointerForOop(freeListOop))) { + fprintf(GIV(transcript), + "leak in free list %d to non-free %p\n", + ((int) i), + ((void *)freeListOop)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "leak in free list %d to out-of-bounds %p\n", + ((int) i), + ((void *)freeListOop)); + ok = 0; + } + } + } + + /* Excuse the duplication but performance is at a premium and we avoid + some tests by splitting the newSpace and oldSpace enumerations. */ + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l6; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + fprintf(GIV(transcript), + "young object %p is free\n", + ((void *)objOop)); + ok = 0; + } + else { + if (!((!(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) + && (excludeUnmarkedObjs))) { + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) { + fprintf(GIV(transcript), + "young object %p is remembered\n", + ((void *)objOop)); + ok = 0; + } + } + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isInHeapBounds: */ + (oopisGreaterThanOrEqualTo(fieldOop, (!(minCogMethodAddress()) + ? GIV(nilObj) + : minCogMethodAddress()))) + && (oopisLessThan(fieldOop, GIV(endOfMemory)))) { + if (!(heapMapAtWord(pointerForOop(fieldOop)))) { + fprintf(GIV(transcript), + "object leak in forwarder %p to unmapped %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "object leak in forwarder %p to out-of-bounds %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + else { + classIndexSqInt = (classIndex = (longAt((void *)(objOop))) & (classIndexMask())); + + /* begin classOrNilAtIndex: */ + assert((classIndexSqInt <= (tagMask())) + || (classIndexSqInt >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndexSqInt)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classOop = GIV(nilObj); + goto l1; + } + fieldIndex = classIndexSqInt & ((1U << (classTableMajorIndexShift())) - 1); + classOop = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classOrNilAtIndex: */ +l1: + if (classIndicesShouldBeValid + && ((classOop == GIV(nilObj)) + && (!(((longAt((void *)(objOop))) & (classIndexMask())) <= (lastClassIndexPun()))))) { + fprintf(GIV(transcript), + "object leak in %p invalid class index %p -> nilObj\n", + ((void *)objOop), + ((void *)classIndex)); + ok = 0; + } + toDoLimit = (numPointerSlotsOf(objOop)) - 1; + for (fi = 0; fi <= toDoLimit; fi += 1) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + if ((!(fieldOop & (tagMask())))) { + if (/* isInHeapBounds: */ + (oopisGreaterThanOrEqualTo(fieldOop, (!(minCogMethodAddress()) + ? GIV(nilObj) + : minCogMethodAddress()))) + && (oopisLessThan(fieldOop, GIV(endOfMemory)))) { + if (!(heapMapAtWord(pointerForOop(fieldOop)))) { + fprintf(GIV(transcript), + "object leak in %p @ %d = %p\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "object leak in %p to out-of-bounds %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l5; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l5:; + } + /* end allNewSpaceEntitiesDo: */ +l6: + + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + if (heapMapAtWord(pointerForOop(objOop))) { + fprintf(GIV(transcript), + "leak in free chunk %p is mapped?! \n", + ((void *)objOop)); + ok = 0; + } + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) != 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ 0 = %p is mapped\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + chunkBytes = bytesInBody(objOop); + + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + if (!(chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */))) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (1U << (shiftForWord())))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) != 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ 1 = %p is mapped\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + if ((bytesInBody(objOop)) >= 0x200 /* (numFreeLists * #allocationUnit) */) { + for (fi = 2 /* freeChunkParentIndex */; fi <= 4 /* freeChunkLargerIndex */; fi += 1) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) != 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ %d = %p is mapped\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + } + } + else { + if (!(excludeUnmarkedObjs + && (!(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)))) { + containsYoung = 0; + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) { + numRememberedObjectsInHeap += 1; + + /* begin isInRememberedSet: */ + for (iSqInt = 0; iSqInt < GIV(rememberedSetSize); iSqInt += 1) { + if ((GIV(rememberedSet)[iSqInt]) == objOop) { + goto l2; + } + } + fprintf(GIV(transcript), + "remembered object %p is not in remembered table\n", + ((void *)objOop)); + ok = 0; +l2:; + } + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isInHeapBounds: */ + (oopisGreaterThanOrEqualTo(fieldOop, (!(minCogMethodAddress()) + ? GIV(nilObj) + : minCogMethodAddress()))) + && (oopisLessThan(fieldOop, GIV(endOfMemory)))) { + if (!(heapMapAtWord(pointerForOop(fieldOop)))) { + fprintf(GIV(transcript), + "object leak in forwarder %p to unmapped %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "object leak in forwarder %p to out-of-bounds %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + if (/* isReallyYoung: */ + ((!(fieldOop & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(fieldOop)), + (oopisLessThan(fieldOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(fieldOop, GIV(newSpaceStart)))))) { + containsYoung = 1; + } + } + else { + classIndexSqInt = (classIndex = (longAt((void *)(objOop))) & (classIndexMask())); + + /* begin classOrNilAtIndex: */ + assert((classIndexSqInt <= (tagMask())) + || (classIndexSqInt >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndexSqInt)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classOop = GIV(nilObj); + goto l3; + } + fieldIndex = classIndexSqInt & ((1U << (classTableMajorIndexShift())) - 1); + classOop = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classOrNilAtIndex: */ +l3: + if (classIndicesShouldBeValid + && ((classOop == GIV(nilObj)) + && (classIndex > (lastClassIndexPun())))) { + fprintf(GIV(transcript), + "object leak in %p invalid class index %p -> nilObj\n", + ((void *)objOop), + ((void *)classIndex)); + ok = 0; + } + toDoLimit1 = (numPointerSlotsOf(objOop)) - 1; + for (fi = 0; fi <= toDoLimit1; fi += 1) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + if ((!(fieldOop & (tagMask())))) { + if (/* isInHeapBounds: */ + (oopisGreaterThanOrEqualTo(fieldOop, (!(minCogMethodAddress()) + ? GIV(nilObj) + : minCogMethodAddress()))) + && (oopisLessThan(fieldOop, GIV(endOfMemory)))) { + if (!(heapMapAtWord(pointerForOop(fieldOop)))) { + fprintf(GIV(transcript), + "object leak in %p @ %d = %p\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "object leak in %p to out-of-bounds %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + + /* don't be misled by CogMethods; they appear to be young, but they're not */ + if (/* isReallyYoung: */ + ((!(fieldOop & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(fieldOop)), + (oopisLessThan(fieldOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(fieldOop, GIV(newSpaceStart)))))) { + containsYoung = 1; + } + } + } + } + if (containsYoung) { + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + fprintf(GIV(transcript), + "unremembered object %p contains young oop(s)\n", + ((void *)objOop)); + ok = 0; + } + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l7; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l7:; + } + if (numRememberedObjectsInHeap != GIV(rememberedSetSize)) { + fprintf(GIV(transcript), + "root count mismatch. #heap roots %d; #roots %d\n", + ((int) numRememberedObjectsInHeap), + ((int) GIV(rememberedSetSize))); + + /* But the system copes with overflow... */ + } + + /* begin rememberedSetWithIndexDo: */ + for (iSqInt = 0; iSqInt < GIV(rememberedSetSize); iSqInt += 1) { + obj = GIV(rememberedSet)[iSqInt]; + if (obj & (BytesPerWord - 1)) { + fprintf(GIV(transcript), + "misaligned oop in remembered set @ %d = %p\n", + ((int) iSqInt), + ((void *)obj)); + ok = 0; + } + else { + if (heapMapAtWord(pointerForOop(obj))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + fprintf(GIV(transcript), + "non-root in remembered set @ %d = %p\n", + ((int) iSqInt), + ((void *)obj)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "object leak in remembered set @ %d = %p\n", + ((int) iSqInt), + ((void *)obj)); + ok = 0; + } + } + } + + /* begin objStack:do: */ + if (GIV(mournQueue) == GIV(nilObj)) { + goto l4; + } + eassert(isValidObjStack(GIV(mournQueue))); + objStackPage = GIV(mournQueue); + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + obj = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (obj & (BytesPerWord - 1)) { + fprintf(GIV(transcript), + "misaligned oop in mournQueue @ %d in %p = %p\n", + ((int) iSqInt), + ((void *)objStackPage), + ((void *)obj)); + ok = 0; + } + else { + if (!(excludeUnmarkedObjs + && (!(((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)))) { + if (!(heapMapAtWord(pointerForOop(obj)))) { + fprintf(GIV(transcript), + "object leak in mournQueue @ %d in %p = %p\n", + ((int) iSqInt), + ((void *)objStackPage), + ((void *)obj)); + ok = 0; + } + } + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l4: + for (ri = 1; ri <= GIV(remapBufferCount); ri += 1) { + obj = GIV(remapBuffer)[ri]; + if (obj & (BytesPerWord - 1)) { + fprintf(GIV(transcript), + "misaligned remapRoot @ %d = %p\n", + ((int) ri), + ((void *)obj)); + ok = 0; + } + else { + if (!(heapMapAtWord(pointerForOop(obj)))) { + fprintf(GIV(transcript), + "object leak in remapRoots @ %d = %p\n", + ((int) ri), + ((void *)obj)); + ok = 0; + } + } + } + for (ri = 1; ri <= GIV(extraRootCount); ri += 1) { + obj = (GIV(extraRoots)[ri])[0]; + if (obj & (BytesPerWord - 1)) { + fprintf(GIV(transcript), + "misaligned extraRoot @ %d => %p\n", + ((int) ri), + ((void *)obj)); + ok = 0; + } + else { + if (!(heapMapAtWord(pointerForOop(obj)))) { + fprintf(GIV(transcript), + "object leak in extraRoots @ %d => %p\n", + ((int) ri), + ((void *)obj)); + ok = 0; + } + } + } + return ok; +} + + +/* Verify that the given oop is legitimate. Check address, header, and size + but not class. + Answer true if OK. Otherwise print reason and answer false. */ +/* useful for debugging */ + + /* SpurMemoryManager>>#checkOkayOop: */ +sqInt +checkOkayOop(usqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt fmt; + usqLong unusedBits; + sqInt unusedBitsInYoungObjects; + + + /* address and size checks */ + if (((oop & (tagMask())) != 0)) { + return 1; + } + if (!(addressCouldBeObj(oop))) { + fprintf(GIV(transcript), + "oop %p is not a valid address", + ((void *)oop)); + return 0; + } + if (!(oopisLessThanOrEqualTo(addressAfter(oop), GIV(endOfMemory)))) { + fprintf(GIV(transcript), + "oop %p size would make it extend beyond the end of memory", + ((void *)oop)); + return 0; + } + + /* header type checks */ + if (!(((classIndex = (longAt((void *)(oop))) & (classIndexMask()))) >= (firstClassIndexPun()))) { + fprintf(GIV(transcript), + "oop %p is a free chunk, or bridge, not an object", + ((void *)oop)); + return 0; + } + if (((byteAt((void *)(oop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) + && ((byteAt((void *)((oop - BaseHeaderSize) + (numSlotsFieldByteOffset())))) != (numSlotsMask()))) { + fprintf(GIV(transcript), + "oop %p header has overflow header word, but overflow word does not have a saturated numSlots field", + ((void *)oop)); + return 0; + } + + /* format check */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if ((fmt == 6) || (fmt == 8)) { + fprintf(GIV(transcript), + "oop %p has an unknown format type", + ((void *)oop)); + return 0; + } + if ((fmt == (forwardedFormat())) != (classIndex == (isForwardedObjectClassIndexPun()))) { + fprintf(GIV(transcript), + "oop %p has mis-matched format/classIndex fields; only one of them is the isForwarded value", + ((void *)oop)); + return 0; + } + + /* specific header bit checks */ + unusedBits = (0x400000) | (0x40000000000000LL); + if ((long64At((void *)(oop))) & unusedBits) { + fprintf(GIV(transcript), + "oop %p has some unused header bits set; should be zero", + ((void *)oop)); + return 0; + } + unusedBitsInYoungObjects = (1U << (unusedBitShift())) | ((1U << (pinnedBitShift())) | (1U << (rememberedBitShift()))); + if ((longAt((void *)(oop))) & unusedBitsInYoungObjects) { + fprintf(GIV(transcript), + "oop %p has some header bits unused in young objects set; should be zero", + ((void *)oop)); + return 0; + } + return 1; +} + + +/* Attempt to verify that the given obj has a reasonable behavior. The class + must be a + valid, non-integer oop and must not be nilObj. It must be a pointers + object with three + or more fields. Finally, the instance specification field of the behavior + must match that + of the instance. If OK answer true. If not, print reason and answer false. */ +/* useful for debugging */ + + /* SpurMemoryManager>>#checkOopHasOkayClass: */ +sqInt +checkOopHasOkayClass(usqInt obj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + usqInt objClass; + sqInt objFormat; + + if (!(checkOkayOop(obj))) { + return 0; + } + objClass = ((usqInt) (fetchClassOfNonImm(obj))); + if (((objClass & (tagMask())) != 0)) { + fprintf(GIV(transcript), + "obj %p an immediate is not a valid class or behavior\n", + ((void *)obj)); + return 0; + } + if (!(okayOop(objClass))) { + fprintf(GIV(transcript), + "obj %p class obj is not ok\n", + ((void *)obj)); + return 0; + } + if (!((((byteAt((void *)(objClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(objClass)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(objClass + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objClass - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= 3))) { + fprintf(GIV(transcript), + "obj %p a class (behavior) must be a pointers object of size >= 3\n", + ((void *)obj)); + return 0; + } + objFormat = (/* isBytes: */ + ((!(obj & (tagMask())))) + && (((byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())) + ? ((((byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask())) | 7) - 7) + : (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask())); + if (((((usqInt)((((longAt((void *)((objClass + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) != objFormat) { + fprintf(GIV(transcript), + "obj %p and its class (behavior) formats differ\n", + ((void *)obj)); + return 0; + } + return 1; +} + + /* SpurMemoryManager>>#checkOopIntegrity:named: */ +static NoDbgRegParms sqInt +checkOopIntegritynamed(sqInt obj, char *name) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((oopisLessThan(obj, GIV(endOfMemory))) + && ((heapMapAtWord(pointerForOop(obj))) != 0)) { + return 1; + } + fprintf(GIV(transcript), + "%s leak %p\n", + name, + ((void *)obj)); + return 0; +} + + /* SpurMemoryManager>>#checkOopIntegrity:named:index: */ +static NoDbgRegParms sqInt +checkOopIntegritynamedindex(sqInt obj, char *name, sqInt i) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((oopisLessThan(obj, GIV(endOfMemory))) + && ((heapMapAtWord(pointerForOop(obj))) != 0)) { + return 1; + } + fprintf(GIV(transcript), + "%s leak @ %d = %p\n", + name, + ((int) i), + ((void *)obj)); + return 0; +} + + /* SpurMemoryManager>>#classAlien */ +sqInt +classAlien(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classArray */ +sqInt +classArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classAtIndex: */ +sqInt +classAtIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt fieldIndex; + + assert((classIndex >= 0) + && ((classIndex <= (tagMask())) + || ((classIndex >= (arrayClassIndexPun())) + && (classIndex <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return null; + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + +/* for become & GC of classes */ + + /* SpurMemoryManager>>#classAtIndex:put: */ +static NoDbgRegParms sqInt +classAtIndexput(sqInt classIndex, sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt fieldIndex; + + assert((classIndex <= (tagMask())) + || (classIndex >= (arrayClassIndexPun()))); + assert((objOop == GIV(nilObj)) + || ((((rawHashBitsOf(objOop)) == classIndex) + || ((classAtIndex(rawHashBitsOf(objOop))) == objOop)) + && (objCouldBeClassObj(objOop)))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + error("attempt to add class to empty page"); + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, classTablePage, objOop)); + assert(isNonImmediate(classTablePage)); + if (oopisGreaterThanOrEqualTo(classTablePage, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objOop & (tagMask())))) + && (oopisLessThan(objOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(classTablePage + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(classTablePage); + } + } + } + + /* most stores into young objects */ + return longAtput((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),objOop); +} + + /* SpurMemoryManager>>#classBitmap */ +sqInt +classBitmap(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassBitmap) << (shiftForWord())))))); +} + + +/* a.k.a. self fetchPointer: ClassByteArrayCompactIndex ofObject: + classTableFirstPage + */ + + /* SpurMemoryManager>>#classByteArray */ +sqInt +classByteArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteArray) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classCharacter */ +sqInt +classCharacter(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassCharacter) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classDoubleByteArray */ +sqInt +classDoubleByteArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOop; + int hash; + + /* begin accessIntegerClass:withValidationFlag: */ + hash = 0; + classOop = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassDoubleByteArray) << (shiftForWord())))))); + if (((GIV(validatedIntegerClassFlags) & ValidatedClassDoubleByteArrayFlag) != 0)) { + return classOop; + } + if ((((classOop & (tagMask())) != 0)) + || ((((hash = (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask()))) == 0) + || ((classAtIndex(hash)) != classOop))) { + return GIV(nilObj); + } + GIV(validatedIntegerClassFlags) = GIV(validatedIntegerClassFlags) | ValidatedClassDoubleByteArrayFlag; + return classOop; +} + + /* SpurMemoryManager>>#classDoubleWordArray */ +sqInt +classDoubleWordArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOop; + int hash; + + /* begin accessIntegerClass:withValidationFlag: */ + hash = 0; + classOop = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassDoubleWordArray) << (shiftForWord())))))); + if (((GIV(validatedIntegerClassFlags) & ValidatedClassDoubleWordArrayFlag) != 0)) { + return classOop; + } + if ((((classOop & (tagMask())) != 0)) + || ((((hash = (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask()))) == 0) + || ((classAtIndex(hash)) != classOop))) { + return GIV(nilObj); + } + GIV(validatedIntegerClassFlags) = GIV(validatedIntegerClassFlags) | ValidatedClassDoubleWordArrayFlag; + return classOop; +} + + /* SpurMemoryManager>>#classExternalAddress */ +sqInt +classExternalAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassExternalAddress) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classExternalData */ +sqInt +classExternalData(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassExternalData) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classExternalFunction */ +sqInt +classExternalFunction(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassExternalFunction) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classExternalLibrary */ +sqInt +classExternalLibrary(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassExternalLibrary) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classExternalStructure */ +sqInt +classExternalStructure(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassExternalStructure) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classFloat */ +sqInt +classFloat(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassFloat) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classFloat32Array */ +sqInt +classFloat32Array(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(nilObj); +} + + /* SpurMemoryManager>>#classFloat64Array */ +sqInt +classFloat64Array(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(nilObj); +} + + +/* No need to check this; classAtIndex: has a stricter assert: + self assert: classIndex ~= self isForwardedObjectClassIndexPun. */ + + /* SpurMemoryManager>>#classForClassTag: */ +static NoDbgRegParms sqInt +classForClassTag(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt fieldIndex; + + /* begin classAtIndex: */ + assert((classIndex >= 0) + && ((classIndex <= (tagMask())) + || ((classIndex >= (arrayClassIndexPun())) + && (classIndex <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return null; + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + +/* 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState + et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + + /* SpurMemoryManager>>#classFormatFromInstFormat: */ +static NoDbgRegParms sqInt +classFormatFromInstFormat(sqInt instFormat) +{ + if (instFormat >= (firstByteFormat())) { + return instFormat & -8; + } + + /* this is likely the common case */ + if (instFormat <= (sixtyFourBitIndexableFormat())) { + return instFormat; + } + if (instFormat < (firstShortFormat())) { + return instFormat & -2; + } + return instFormat & -4; +} + + +/* 22-bit class mask => ~ 4M classes */ + + /* SpurMemoryManager>>#classIndexFieldWidth */ +static sqInt +classIndexFieldWidth(void) +{ + return 22; +} + + +/* Answer objOop's classIndex from the object header. + A note on performance: + This routine uses longAt:, which does a 32-bit load on the 32-bit system, + and a 64-bit load + on the 64-bit system. Since the only 64-bit implementation(s) is/are + little-endian, and + since all of UNUSED,isPinned,isRemembered,format,isImmutable and + classIndex fit within + the least significant 32-bits we could use long32At: to access these, in + the hope that the + 32-bit access will be quicker on 64-bits by virtue of fetching half the + bits. But experiments + show that doing this does not produce any increase; at least any signal is + lost in the noise. */ + + /* SpurMemoryManager>>#classIndexOf: */ +sqInt +classIndexOf(sqInt objOop) +{ + return (longAt((void *)(objOop))) & (classIndexMask()); +} + + /* SpurMemoryManager>>#classLargeNegativeInteger */ +sqInt +classLargeNegativeInteger(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin knownClassAtIndex: */ + assert(((ClassLargeNegativeIntegerCompactIndex >= 1) && (ClassLargeNegativeIntegerCompactIndex <= (classTablePageSize())))); + return longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassLargeNegativeIntegerCompactIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classLargePositiveInteger */ +sqInt +classLargePositiveInteger(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin knownClassAtIndex: */ + assert(((ClassLargePositiveIntegerCompactIndex >= 1) && (ClassLargePositiveIntegerCompactIndex <= (classTablePageSize())))); + return longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassLargePositiveIntegerCompactIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classOrNilAtIndex: */ +sqInt +classOrNilAtIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt fieldIndex; + + assert((classIndex <= (tagMask())) + || (classIndex >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return GIV(nilObj); + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classPoint */ +sqInt +classPoint(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassPoint) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classSemaphore */ +sqInt +classSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classSmallInteger */ +sqInt +classSmallInteger(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSmallInteger) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classString */ +sqInt +classString(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord())))))); +} + + +/* 1024 entries per page (2^10); 22 bit classIndex implies 2^12 pages */ +/* self basicNew classTableMinorIndexMask */ + + /* SpurMemoryManager>>#classTableMinorIndexMask */ +sqInt +classTableMinorIndexMask(void) +{ + return (1U << (classTableMajorIndexShift())) - 1; +} + + +/* 1024 entries per page (2^10); 22 bit classIndex implies 2^12 pages */ +/* self basicNew classTablePageSize */ + + /* SpurMemoryManager>>#classTablePageSize */ +sqInt +classTablePageSize(void) +{ + return 1U << (classTableMajorIndexShift()); +} + + +/* For Cogit & bootstrap */ + + /* SpurMemoryManager>>#classTableRootObj */ +sqInt +classTableRootObj(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(hiddenRootsObj); +} + + +/* Answer the number of slots for class table pages in the hidden root + object. + */ + + /* SpurMemoryManager>>#classTableRootSlots */ +static sqInt +classTableRootSlots(void) +{ + return 1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift())); +} + + +/* Answer the classObj's identityHash to use as a tag in the first-level + method lookup cache. + */ + + /* SpurMemoryManager>>#classTagForClass: */ +sqInt +classTagForClass(sqInt classObj) +{ + sqInt err; + int hash; + + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + return ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); +} + + /* SpurMemoryManager>>#classUnsafeAlien */ +sqInt +classUnsafeAlien(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassUnsafeAlien) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classWordArray */ +sqInt +classWordArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOop; + int hash; + + /* begin accessIntegerClass:withValidationFlag: */ + hash = 0; + classOop = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassWordArray) << (shiftForWord())))))); + if (((GIV(validatedIntegerClassFlags) & ValidatedClassWordArrayFlag) != 0)) { + return classOop; + } + if ((((classOop & (tagMask())) != 0)) + || ((((hash = (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask()))) == 0) + || ((classAtIndex(hash)) != classOop))) { + return GIV(nilObj); + } + GIV(validatedIntegerClassFlags) = GIV(validatedIntegerClassFlags) | ValidatedClassWordArrayFlag; + return classOop; +} + + +/* Perform an integrity/leak check using the heapMap. Set a bit at each free + chunk's header. */ + + /* SpurMemoryManager>>#clearLeakMapAndMapAccessibleFreeSpace */ +static void +clearLeakMapAndMapAccessibleFreeSpace(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + + clearHeapMap(); + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + heapMapAtWordPut(pointerForOop(objOopSqInt), 1); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } +} + + /* SpurMemoryManager>>#cloneInOldSpace:forPinning: */ +static NoDbgRegParms sqInt +cloneInOldSpaceforPinning(sqInt objOop, sqInt forPinning) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cogMethodOrMethodHeader; + sqInt fmt; + int hash; + sqInt hasYoung; + sqInt i; + sqInt newObj; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt rawHeader; + sqInt referent; + sqInt valuePointer; + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (forPinning) { + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), fmt, (longAt((void *)(objOop))) & (classIndexMask())); + } + else { + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), fmt, (longAt((void *)(objOop))) & (classIndexMask())); + } + if (!newObj) { + return 0; + } + if (fmt <= 5 /* lastPointerFormat */) { + hasYoung = 0; + for (i = 0; i < numSlots; i += 1) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + } + if (((!(oop & (tagMask())))) + && ((/* begin isYoungObject: */ + assert(isNonImmediate(oop)), + oopisLessThan(oop, GIV(oldSpaceStart))))) { + hasYoung = 1; + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(i, newObj, oop)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),oop); + } + if (hasYoung) { + remember(newObj); + } + } + else { + for (i = 0; i < numSlots; i += 1) { + valuePointer = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(i, newObj, valuePointer)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + if (fmt >= (firstCompiledMethodFormat())) { + /* begin maybeFixClonedCompiledMethod: */ + assert(isOopCompiledMethod(newObj)); + rawHeader = longAt((void *)((newObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + cogMethodOrMethodHeader = ((((CogMethod *) rawHeader))->methodHeader); + + /* begin rawHeaderOf:put: */ + assert(isCompiledMethodHeader(headerWhileForwardingOf(newObj))); + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(HeaderIndex, newObj, cogMethodOrMethodHeader)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))),cogMethodOrMethodHeader); + } + if (((/* begin isYoungObject: */ + assert(isNonImmediate(objOop)), + oopisLessThan(objOop, GIV(oldSpaceStart)))) + || (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0)) { + remember(newObj); + } + } + } + if ((hash = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()))) { + /* begin setHashBitsOf:to: */ + long32Atput((void *)(newObj + 4),((((long32At((void *)(newObj + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(objOop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* begin setIsImmutableOf:to: */ + longAtput((void *)(newObj),(longAt((void *)(newObj))) | (1U << (immutableBitShift()))); + } + return newObj; +} + + /* SpurMemoryManager>>#cloneObject: */ +sqInt +cloneObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt cogMethodOrMethodHeader; + sqInt fmt; + sqInt hasYoung; + sqInt i; + sqInt newObj; + usqInt newObjUsqInt; + usqInt numBytes; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt rawHeader; + sqInt referent; + sqInt valuePointer; + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (numSlots > ((1U << (fixedFieldsFieldWidth())) - 1)) { + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + + /* begin allocateSlotsInOldSpace:format:classIndex: */ + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), fmt, classIndex); + } + else { + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if (((numSlots) >> 56) > 0) { + newObj = null; + goto l1; + } + newObjUsqInt = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObjUsqInt = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, fmt, classIndex); + goto l1; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObjUsqInt),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(fmt) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObjUsqInt),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(fmt) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObjUsqInt % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newObj = newObjUsqInt; + /* end allocateSlots:format:classIndex: */ +l1:; + } + if (newObj) { + if (fmt <= 5 /* lastPointerFormat */) { + hasYoung = 0; + for (i = 0; i < numSlots; i += 1) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(oop & (tagMask())))) { + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + } + if (((!(oop & (tagMask())))) + && ((/* begin isYoungObject: */ + assert(isNonImmediate(oop)), + oopisLessThan(oop, GIV(oldSpaceStart))))) { + hasYoung = 1; + } + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(i, newObj, oop)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),oop); + } + if (hasYoung + && (!((/* begin isYoungObject: */ + assert(isNonImmediate(newObj)), + oopisLessThan(newObj, GIV(oldSpaceStart)))))) { + remember(newObj); + } + } + else { + for (i = 0; i < numSlots; i += 1) { + valuePointer = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(i, newObj, valuePointer)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + if (fmt >= (firstCompiledMethodFormat())) { + /* begin maybeFixClonedCompiledMethod: */ + assert(isOopCompiledMethod(newObj)); + rawHeader = longAt((void *)((newObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + cogMethodOrMethodHeader = ((((CogMethod *) rawHeader))->methodHeader); + + /* begin rawHeaderOf:put: */ + assert(isCompiledMethodHeader(headerWhileForwardingOf(newObj))); + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(HeaderIndex, newObj, cogMethodOrMethodHeader)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))),cogMethodOrMethodHeader); + } + if (((/* begin isOldObject: */ + assert(isNonImmediate(newObj)), + oopisGreaterThanOrEqualTo(newObj, GIV(oldSpaceStart)))) + && (((/* begin isYoungObject: */ + assert(isNonImmediate(objOop)), + oopisLessThan(objOop, GIV(oldSpaceStart)))) + || (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0))) { + remember(newObj); + } + } + } + } + return newObj; +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. + Copy objOop into the segment beginning at segAddr, and forward it to the + copy, saving its first field in savedFirstField, and setting its marked + bit to indicate it has + been copied. If it is a class in the class table, set the copy's hash to 0 + for reassignment + on load, and mark it as a class by setting its isRemembered bit. + Answer the next segmentAddr if successful. Answer an appropriate error + code if not */ +/* Copy the object... */ + + /* SpurMemoryManager>>#copyObj:toAddr:stopAt:savedFirstFields:index: */ +static NoDbgRegParms sqInt +copyObjtoAddrstopAtsavedFirstFieldsindex(sqInt objOop, sqInt segAddr, sqInt endSeg, sqInt savedFirstFields, sqInt i) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bodySize; + sqInt contextSize; + sqInt copy; + sqInt fmt; + int hash; + sqInt header; + sqInt headerSqInt; + sqInt iSqInt; + sqInt methodHeader; + usqInt numLiterals; + usqInt numMediatedSlots; + usqInt numSlots; + sqInt oop; + sqInt sp; + sqInt toDoLimit; + sqInt valuePointer; + + assert(!((isCopiedIntoSegment(objOop)))); + bodySize = bytesInBody(objOop); + if (oopisGreaterThanOrEqualTo(segAddr + bodySize, endSeg)) { + return PrimErrWritePastObject; + } + memcpy(((void *)segAddr), ((void *)(/* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop))), bodySize); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(segAddr + (numSlotsFieldByteOffset()))); + copy = (numSlots == (numSlotsMask()) + ? segAddr + BaseHeaderSize + : segAddr); + + /* Clear remembered, mark bits of all headers copied into the segment (except classes) */ + setIsRememberedOfto(copy, 0); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(copy))); + byteAtput((void *)(copy + (markBitsByteOffset())),(byteAt((void *)(copy + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + + /* Make any objects with hidden dynamic state (contexts, methods) look like normal objects. */ + + /* begin ifAProxy:updateCopy: */ + if (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + if (/* isStillMarriedContext: */ + (((((longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(objOop)))) { + /* begin numSlotsOfMarriedContext: */ + contextSize = stackPointerIndexForFrame(frameOfMarriedContext(objOop)); + assert((CtxtTempFrameStart + contextSize) <= (numSlotsOf(objOop))); + numMediatedSlots = CtxtTempFrameStart + contextSize; + for (iSqInt = 0; iSqInt < numMediatedSlots; iSqInt += 1) { + oop = fetchPointerofMarriedContext(iSqInt, objOop); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(copy)) + && (!(isForwarded(copy)))); + assert(validStorePointerUncheckedArgs(iSqInt, copy, oop)); + longAtput((void *)((copy + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord()))))),oop); + } + } + else { + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + numMediatedSlots = CtxtTempFrameStart + contextSize; + goto l2; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numMediatedSlots = (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l2; + } + if (fmt == (forwardedFormat())) { + numMediatedSlots = 1; + goto l2; + } + if (fmt < (firstCompiledMethodFormat())) { + numMediatedSlots = 0; + goto l2; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numMediatedSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l2:; + } + + /* And make sure to nil the slots beyond the top of stack... */ + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (iSqInt = numMediatedSlots; iSqInt <= toDoLimit; iSqInt += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(copy)) + && (!(isForwarded(copy)))); + assert(validStorePointerUncheckedArgs(iSqInt, copy, GIV(nilObj))); + longAtput((void *)((copy + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord()))))),GIV(nilObj)); + } + } + if (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + methodHeader = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(copy)) + && (!(isForwarded(copy)))); + assert(validStorePointerUncheckedArgs(HeaderIndex, copy, ((((CogMethod *) methodHeader))->methodHeader))); + longAtput((void *)((copy + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))),((((CogMethod *) methodHeader))->methodHeader)); + } + } + + /* If the object is a class, zero its identityHash (which is its classIndex) and set its + isRemembered bit. It will be assigned a new hash and entered into the table on load. */ + hash = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()); + if ((hash > (lastClassIndexPun())) + && ((classOrNilAtIndex(hash)) == objOop)) { + /* begin setHashBitsOf:to: */ + long32Atput((void *)(copy + 4),((((long32At((void *)(copy + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (0)); + setIsRememberedOfto(copy, 1); + } + + /* Now forward the object to its copy in the segment. */ + valuePointer = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(savedFirstFields)) + && (!(isForwarded(savedFirstFields)))); + assert(validStorePointerUncheckedArgs(i, savedFirstFields, valuePointer)); + longAtput((void *)((savedFirstFields + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(0, objOop, copy)); + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),copy); + + /* begin markAsCopiedIntoSegment: */ + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* Answer the new end of segment */ + return segAddr + bodySize; +} + + +/* print the count of marked and unmarked objects. + In addition if 1 is set in printFlags, short-print marked objects, + and/or if 2 is set, short-print unmarked obejcts. */ +/* useful for debugging */ + + /* SpurMemoryManager>>#countMarkedAndUnmarkdObjects: */ +void +countMarkedAndUnmarkdObjects(sqInt printFlags) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt nm; + sqInt nu; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + nm = (nu = 0); + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + nm += 1; + if (((printFlags & 1) != 0)) { + shortPrintOop(obj); + } + } + else { + nu += 1; + if (((printFlags & 2) != 0)) { + shortPrintOop(obj); + } + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "n marked: "); + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)nm)); + cr(); + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "n unmarked: "); + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)nu)); + cr(); +} + + +/* Compute the current allocated bytes since last set. + This is the cumulative total in statAllocatedBytes plus the allocation + since the last scavenge. */ +/* Slang infers the type of the difference between two unsigned variables as + signed. In this case we want it to be unsigned. */ + + /* SpurMemoryManager>>#currentAllocatedBytes */ +static usqLong +currentAllocatedBytes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt use; + + use = ((/* begin totalOldSpaceCapacity */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + return (GIV(statAllocatedBytes) + (GIV(freeStart) - (((GIV(eden)).start)))) + (use - GIV(oldSpaceUsePriorToScavenge)); +} + + +/* The inner shell for scavenge, abstrascted out so globalGarbageCollect can + use it. + */ + + /* SpurMemoryManager>>#doScavenge: */ +static NoDbgRegParms void +doScavenge(sqInt tenuringCriterion) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + float fractionSurvived; + sqInt previousFSStart; + SpurNewSpaceSpace temp; + usqInt use; + + /* begin doAllocationAccountingForScavenge */ + /* begin currentAllocatedBytes */ + use = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + GIV(statAllocatedBytes) = (GIV(statAllocatedBytes) + (GIV(freeStart) - (((GIV(eden)).start)))) + (use - GIV(oldSpaceUsePriorToScavenge)); + GIV(gcPhaseInProgress) = ScavengeInProgress; + + /* begin scavenge: */ + GIV(statSurvivorCount) = 0; + GIV(tenureCriterion) = tenuringCriterion; + + /* begin logStartScavenge */ + (GIV(scavengeLogRecord).sEdenBytes = GIV(freeStart) - ((GIV(eden).start))); + (GIV(scavengeLogRecord).sPastBytes = GIV(pastSpaceStart) - ((GIV(pastSpace).start))); + (GIV(scavengeLogRecord).sRememberedSetSize = GIV(rememberedSetSize)); + (GIV(scavengeLogRecord).sRememberedSetRedZone = GIV(rememberedSetRedZone)); + (GIV(scavengeLogRecord).sRememberedSetLimit = GIV(rememberedSetLimit)); + (GIV(scavengeLogRecord).sStatTenures = GIV(statTenures)); + + /* begin strategizeToLimitRememberedTable */ + if ((GIV(tenureCriterion) == TenureByAge) + && (GIV(rememberedSetSize) >= GIV(rememberedSetRedZone))) { + GIV(tenureCriterion) = TenureToShrinkRT; + computeRefCountToShrinkRT(); + } + + /* begin logTenuringPolicy */ + (GIV(scavengeLogRecord).tTenureCriterion = GIV(tenureCriterion)); + (GIV(scavengeLogRecord).tTenureThreshold = ((GIV(tenureCriterion) == TenureByAge) + && (GIV(tenureThreshold) > ((GIV(pastSpace).start))) + ? GIV(tenureThreshold) - ((GIV(pastSpace).start)) + : 0)); + (GIV(scavengeLogRecord).tRefCountToShrinkRT = GIV(refCountToShrinkRT)); + ((void) (scavengeLoop())); + processWeaklings(); + + /* begin computeTenuringThreshold */ + fractionSurvived = (((GIV(futureSpace).limit)) == ((GIV(futureSpace).start)) + ? 0.0 + : (((double) (GIV(futureSurvivorStart) - ((GIV(futureSpace).start))) )) / (((GIV(futureSpace).limit)) - ((GIV(futureSpace).start)))); + GIV(tenureThreshold) = (fractionSurvived > 0.9 + ? (round(((((GIV(pastSpace).limit)) - ((GIV(pastSpace).start))) * (1.0 - GIV(tenuringProportion))))) + ((GIV(pastSpace).start)) + : 0); + + /* begin exchangeSurvivorSpaces */ + temp = GIV(pastSpace); + GIV(pastSpace) = GIV(futureSpace); + GIV(futureSpace) = temp; + + /* begin logEndScavenge */ + (GIV(scavengeLogRecord).eSurvivorBytes = GIV(futureSurvivorStart) - ((GIV(pastSpace).start))); + (GIV(scavengeLogRecord).eRememberedSetSize = GIV(rememberedSetSize)); + (GIV(scavengeLogRecord).eStatTenures = GIV(statTenures)); + + /* begin initFutureSpaceStart */ + previousFSStart = GIV(futureSurvivorStart); + GIV(futureSurvivorStart) = (GIV(futureSpace).start); + GIV(pastSpaceStart) = previousFSStart; + assert(oopisGreaterThanOrEqualToandLessThanOrEqualTo(GIV(pastSpaceStart), ((GIV(pastSpace)).start), ((GIV(pastSpace)).limit))); + GIV(freeStart) = ((GIV(eden)).start); + GIV(gcPhaseInProgress) = 0; + + /* begin resetAllocationAccountingAfterGC */ + GIV(oldSpaceUsePriorToScavenge) = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); +} + + +/* Reduce the number of indexable fields in objOop, a pointer object, to + nSlots. If in oldSpace, + convert the unused residual to a free chunk. If in eden, cut back + freeStart if possible. + Without changes to numSlotsForShortening:toIndexableSize: this only works + for arrayFormat, longFormat, and on 64-bits sixtyFourBitIndexableFormat, + objects. Answer the number of bytes returned to free memory, which may be + zero if no change + was possible. */ + + /* SpurMemoryManager>>#doShorten:toIndexableSize: */ +static NoDbgRegParms sqInt +doShortentoIndexableSize(sqInt objOop, sqInt indexableSize) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytesBefore; + sqInt classIndex; + sqInt copy; + sqInt delta; + usqInt followingAddress; + sqInt format; + sqInt formatSqInt; + sqInt freeChunk; + sqInt i; + sqInt instFormat; + usqInt newObj; + usqInt numBytes; + sqInt numSlots; + sqInt valuePointer; + + /* for assert checking */ + followingAddress = addressAfter(objOop); + assert(oopisLessThanOrEqualTo(followingAddress, GIV(endOfMemory))); + + /* numSlotsForShortening:toIndexableSize: */ + switch ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) { + case arrayFormat(): + case sixtyFourBitIndexableFormat(): + numSlots = indexableSize; + break; + case firstLongFormat(): + case (firstLongFormat()) + 1: + numSlots = ((indexableSize * 4) + (BytesPerWord - 1)) / BytesPerWord; + break; + default: + assertf("invalid format for shortening"); + numSlots = 0; + } + bytesBefore = bytesInBody(objOop); + delta = bytesBefore - (/* objectBytesForSlots: */ + (numSlots + ? ((((usqInt)(numSlots) << (shiftForWord())))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize)); + + /* Since the system rounds objects up to 64-bits, losing a 32-bit + slot may not actually change the bytes occupied by the object. */ + if (!delta) { + if ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) { + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(objOop - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56))) + numSlots); + } + else { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),numSlots); + } + + /* begin updateFormatOfShortenedObject:to: */ + if (((((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) <= ((firstLongFormat()) + 1)))) { + formatSqInt = (firstLongFormat()) + (indexableSize & 1); + + /* begin setFormatOf:to: */ + assert(((formatSqInt >= 0) && (formatSqInt <= (formatMask())))); + byteAtput((void *)(objOop + (formatFieldByteOffset())),((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + formatSqInt); + } + return 0; + } + + /* Currently we can't have one word gaps in oldSpace but we can in newSpace. So only create + a copy and forward if objOop is in old space. If in newSpace we'll create a slimbridge below. */ + if ((delta <= 8 /* allocationUnit */) + && ((/* begin isOldObject: */ + assert(isNonImmediate(objOop)), + oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))))) { + /* begin normalisedFormatFor:indexableSize: */ + instFormat = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (instFormat >= (firstByteFormat())) { + formatSqInt = instFormat & -8; + goto l1; + } + + /* this is likely the common case */ + if (instFormat <= (sixtyFourBitIndexableFormat())) { + formatSqInt = instFormat; + goto l1; + } + if (instFormat < (firstShortFormat())) { + formatSqInt = instFormat & -2; + goto l1; + } + formatSqInt = instFormat & -4; + /* end classFormatFromInstFormat: */ +l1: + format = ((formatSqInt == (firstLongFormat())) + && (((indexableSize & 1) != 0)) + ? formatSqInt + 1 + : formatSqInt); + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if ((((usqInt)(numSlots)) >> 56) > 0) { + copy = null; + goto l2; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + copy = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, format, classIndex); + goto l2; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(format) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(format) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + copy = newObj; + /* end allocateSlots:format:classIndex: */ +l2: + if (!copy) { + error("shorten:toIndexableSize: attempted to shorten by allocationUnit and failed to allocate space for copy!"); + } + if (/* isPureBitsFormat: */ + (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat()))) { + memcpy(firstIndexableField(copy), firstIndexableField(objOop), numBytesOfBitsformat(copy, format)); + } + else { + for (i = 0; i < numSlots; i += 1) { + valuePointer = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(copy)) + && (!(isForwarded(copy)))); + assert(validStorePointerUncheckedArgs(i, copy, valuePointer)); + longAtput((void *)((copy + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + } + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) { + remember(copy); + } + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(objOop),(longAt((void *)(objOop))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(objOop)); + assert(!(isOopForwarded(copy))); + + /* begin isOldObject: */ + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(copy & (tagMask())))) + && (oopisLessThan(copy, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),copy); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),1); + } + return 0; + } + if ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) { + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(objOop - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56))) + numSlots); + + /* Setting an overflow slot count to 0 or 1 in newSpace creates a slimbridge. + So we must also change the normal slot count so that the object no + longer has the overflow header word, which has become the slimbridge. */ + if (numSlots <= 1) { + if (oopisLessThan(objOop, GIV(oldSpaceStart))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),numSlots); + hackSlimBridgeToat(objOop, objOop - 8 /* allocationUnit */); + } + } + } + else { + assert(numSlots < (numSlotsMask())); + + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),numSlots); + } + + /* {self addressAfter: objOop. delta. (self addressAfter: objOop) + delta. followingAddress} collect: #hex */ + delta = followingAddress - (addressAfter(objOop)); + assert((delta >= (allocationUnit())) + && ((delta % (allocationUnit())) == 0)); + if (delta == 8 /* allocationUnit */) { + assert(!((isOldObject(objOop)))); + delta = 0; + if (followingAddress == GIV(freeStart)) { + GIV(freeStart) = addressAfter(objOop); + } + else { + hackSlimBridgeToat(followingAddress, addressAfter(objOop)); + } + } + else { + freeChunk = initFreeChunkWithBytesat(delta, addressAfter(objOop)); + assert((objectAfter(objOop)) == freeChunk); + assert((addressAfter(freeChunk)) == followingAddress); + if (oopisGreaterThanOrEqualToandLessThan(objOop, GIV(oldSpaceStart), GIV(endOfMemory))) { + GIV(totalFreeOldSpace) += delta; + addToFreeListbytes(freeChunk, delta); + } + else { + delta = 0; + + /* begin setClassIndexOf:to: */ + assert((((sixtyFourBitLongsClassIndexPun()) >= 0) && ((sixtyFourBitLongsClassIndexPun()) <= (classIndexMask())))); + longAtput((void *)(freeChunk),((longAt((void *)(freeChunk))) & (~(usqIntptr_t)(classIndexMask()))) + (sixtyFourBitLongsClassIndexPun())); + + /* begin setFormatOf:to: */ + assert((((firstLongFormat()) >= 0) && ((firstLongFormat()) <= (formatMask())))); + byteAtput((void *)(freeChunk + (formatFieldByteOffset())),((byteAt((void *)(freeChunk + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (firstLongFormat())); + } + } + + /* begin normalisedFormatFor:indexableSize: */ + instFormat = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (instFormat >= (firstByteFormat())) { + formatSqInt = instFormat & -8; + goto l3; + } + + /* this is likely the common case */ + if (instFormat <= (sixtyFourBitIndexableFormat())) { + formatSqInt = instFormat; + goto l3; + } + if (instFormat < (firstShortFormat())) { + formatSqInt = instFormat & -2; + goto l3; + } + formatSqInt = instFormat & -4; + /* end classFormatFromInstFormat: */ +l3: + formatSqInt = ((formatSqInt == (firstLongFormat())) + && (((indexableSize & 1) != 0)) + ? formatSqInt + 1 + : formatSqInt); + + /* begin setFormatOf:to: */ + assert(((formatSqInt >= 0) && (formatSqInt <= (formatMask())))); + byteAtput((void *)(objOop + (formatFieldByteOffset())),((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + formatSqInt); + return delta; +} + + +/* Instantiate an instance of a compact class. ee stands for execution engine + and implies that this allocation will *NOT* cause a GC. N.B. the + instantiated object + IS NOT FILLED and must be completed before returning it to Smalltalk. + Since this + call is used in routines that do just that we are safe. Break this rule + and die in GC. + Result is guaranteed to be young. */ + + /* SpurMemoryManager>>#eeInstantiateClassIndex:format:numSlots: */ +usqInt +eeInstantiateClassIndexformatnumSlots(sqInt knownClassIndex, sqInt objFormat, sqInt numSlots) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newObj; + usqInt numBytes; + + assert((numSlots >= 0) + && ((knownClassIndex != 0) + && ((knownClassAtIndex(knownClassIndex)) != GIV(nilObj)))); + assert(((objFormat < (firstByteFormat()) + ? objFormat + : objFormat & (byteFormatMask()))) == (instSpecOfClass(knownClassAtIndex(knownClassIndex)))); + + /* begin allocateNewSpaceSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if (numSlots > 0xFFFFFFFFU) { + return null; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateNewSpaceSlots:format:classIndex:"); + return 0; + } + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),numSlots); + longAtput((void *)(GIV(freeStart) + 4),((sqInt)((usqInt)((numSlotsMask())) << (numSlotsHalfShift())))); + long64Atput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + knownClassIndex); + } + else { + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + knownClassIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + return newObj; +} + + +/* For breakpointing the end of a leak check print message */ + + /* SpurMemoryManager>>#eek */ +NeverInline void +eek(void) +{ +} + + +/* We use bridges to stitch segments together to make it appear that the heap + is one contiguous space. + Bridges at the end of oldSpace segments are maintained. Bridges at the end + of pastSpace and eden + are temporary, and are established here, depending on the current sizes of + pastSpace end eden. + + N.B. this introduces complications. Either or both pastSpace and eden may + be empty, so the + bridge from pastSpace may skip eden. pastSpace may be full, so there may + be no bridge at + the end of pastSpace. Most difficult, pastSpace could be one 64-bit word + short of full, but normal + bridges are two word objects. To make this work we introduce a hack, + objectAfterMaybeSlimBridge:limit:, which uses a fake overflow slot count + to get to the start of the next object, which is either one or two + words away, depending on whether the first object in eden has a normal or + an overflow header. */ + + /* SpurMemoryManager>>#enableObjectEnumerationFrom: */ +static NoDbgRegParms void +enableObjectEnumerationFrom(sqInt initialObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (oopisLessThan(initialObject, GIV(oldSpaceStart))) { + if (GIV(freeStart) > (((GIV(eden)).start))) { + /* begin bridgeEdenAndOldSpace */ + initSegmentBridgeWithBytesat(GIV(oldSpaceStart) - GIV(freeStart), GIV(freeStart)); + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + } + else { + /* begin bridgePastSpaceAndOldSpace */ + initSegmentBridgeWithBytesat(GIV(oldSpaceStart) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } +} + + /* SpurMemoryManager>>#ensureBehaviorHash: */ +static NoDbgRegParms sqInt +ensureBehaviorHash(sqInt aBehavior) +{ + sqInt err; + int hash; + + assert(addressCouldBeClassObj(aBehavior)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + return ((hash = (long32At((void *)(aBehavior + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(aBehavior) + ? ((err = enterIntoClassTable(aBehavior)) + ? -err + : (long32At((void *)(aBehavior + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); +} + + +/* An obj stack is a stack of objects stored in a hidden root slot, such as + the markStack or the ephemeronQueue. It is a linked list of segments, + with the hot end at the head of the list. It is a word object. The stack + pointer is in ObjStackTopx and 0 means empty. The list goes through + ObjStackNextx. We don't want to shrink objStacks, since they're used + in GC and its good to keep their memory around. So unused pages + created by popping emptying pages are kept on the ObjStackFreex list. */ + + /* SpurMemoryManager>>#ensureRoomOnObjStackAt: */ +static NoDbgRegParms sqInt +ensureRoomOnObjStackAt(sqInt objStackRootIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt freeOrNewPage; + sqInt stackOrNil; + + stackOrNil = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord())))))); + if ((stackOrNil == GIV(nilObj)) + || ((longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) >= ObjStackLimit)) { + freeOrNewPage = (stackOrNil == GIV(nilObj) + ? 0 + : longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))))); + if (freeOrNewPage) { + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(stackOrNil)) == (wordIndexableFormat())); + longAtput((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),0); + assert((!GIV(marking)) + || (isMarked(freeOrNewPage))); + } + else { + freeOrNewPage = allocateSlotsInOldSpacebytesformatclassIndex(ObjStackPageSlots, ((((usqInt)(ObjStackPageSlots) << (shiftForWord())))) + (BaseHeaderSize + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (!freeOrNewPage) { + /* Allocate a new segment and retry. This is very uncommon. But it happened to me (Clement). */ + growOldSpaceByAtLeast(ObjStackPageSlots * BytesPerOop); + freeOrNewPage = allocateSlotsInOldSpacebytesformatclassIndex(ObjStackPageSlots, ((((usqInt)(ObjStackPageSlots) << (shiftForWord())))) + (BaseHeaderSize + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (!freeOrNewPage) { + error("no memory to allocate or extend obj stack"); + } + } + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(freeOrNewPage)) == (wordIndexableFormat())); + longAtput((void *)((freeOrNewPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),0); + if (GIV(marking)) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(freeOrNewPage))); + byteAtput((void *)(freeOrNewPage + (markBitsByteOffset())),(byteAt((void *)(freeOrNewPage + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + } + } + + /* the free page list is always on the new page. */ + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(freeOrNewPage)) == (wordIndexableFormat())); + longAtput((void *)((freeOrNewPage + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord()))))),objStackRootIndex); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(freeOrNewPage)) == (wordIndexableFormat())); + longAtput((void *)((freeOrNewPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord()))))),(stackOrNil == GIV(nilObj) + ? 0 + : stackOrNil)); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(freeOrNewPage)) == (wordIndexableFormat())); + longAtput((void *)((freeOrNewPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))),0); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(objStackRootIndex, GIV(hiddenRootsObj), freeOrNewPage)); + assert(isNonImmediate(GIV(hiddenRootsObj))); + if (oopisGreaterThanOrEqualTo(GIV(hiddenRootsObj), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(freeOrNewPage & (tagMask())))) + && (oopisLessThan(freeOrNewPage, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(hiddenRootsObj) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(hiddenRootsObj)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord()))))),freeOrNewPage); + assert(isValidObjStackAt(objStackRootIndex)); + + /* Added a new page; now update and answer the relevant cached first page. */ + + /* begin updateRootOfObjStackAt:with: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(objStackRootIndex, GIV(hiddenRootsObj), freeOrNewPage)); + assert(isNonImmediate(GIV(hiddenRootsObj))); + if (oopisGreaterThanOrEqualTo(GIV(hiddenRootsObj), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(freeOrNewPage & (tagMask())))) + && (oopisLessThan(freeOrNewPage, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(hiddenRootsObj) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(hiddenRootsObj)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord()))))),freeOrNewPage); + switch (objStackRootIndex) { + case MarkStackRootIndex: + GIV(markStack) = freeOrNewPage; + break; + case WeaklingStackRootIndex: + GIV(weaklingStack) = freeOrNewPage; + break; + case MournQueueRootIndex: + GIV(mournQueue) = freeOrNewPage; + break; + default: + error("Case not found and no otherwise clause"); + } + stackOrNil = freeOrNewPage; + } + assert(isValidObjStackAt(objStackRootIndex)); + return stackOrNil; +} + + +/* Enter aBehavior into the class table and answer 0. Otherwise answer a + primitive failure code. */ + + /* SpurMemoryManager>>#enterIntoClassTable: */ +static NoDbgRegParms sqInt +enterIntoClassTable(sqInt aBehavior) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt initialMajorIndex; + sqInt majorIndex; + sqInt minorIndex; + usqInt p; + sqInt page; + sqInt toDoLimit; + usqInt toDoLimitUsqInt; + + majorIndex = ((usqInt)(GIV(classTableIndex))) >> (classTableMajorIndexShift()); + initialMajorIndex = majorIndex; + + /* classTableIndex should never index the first page; it's reserved for known classes */ + assert(initialMajorIndex > 0); + minorIndex = GIV(classTableIndex) & ((1U << (classTableMajorIndexShift())) - 1); + while (1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(majorIndex) << (shiftForWord())))))); + if (page == GIV(nilObj)) { + /* begin allocateSlotsInOldSpace:format:classIndex: */ + page = allocateSlotsInOldSpacebytesformatclassIndex(1U << (classTableMajorIndexShift()), /* objectBytesForSlots: */ + (1U << (classTableMajorIndexShift()) + ? ((((usqInt)((1U << (classTableMajorIndexShift()))) << (shiftForWord())))) + (((1U << (classTableMajorIndexShift())) >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), arrayFormat(), arrayClassIndexPun()); + if (!page) { + return PrimErrNoMemory; + } + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((page + BaseHeaderSize) + ((1U << (classTableMajorIndexShift())) * BytesPerOop)) - 1, addressAfter(page))); + toDoLimitUsqInt = ((usqInt)(((page + BaseHeaderSize) + ((1U << (classTableMajorIndexShift())) * BytesPerOop)) - 1)); + for (p = (((usqInt)(page + BaseHeaderSize))); p <= toDoLimitUsqInt; p += 8 /* allocationUnit */) { + longAtput((void *)(p),GIV(nilObj)); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(majorIndex, GIV(hiddenRootsObj), page)); + assert(isNonImmediate(GIV(hiddenRootsObj))); + if (oopisGreaterThanOrEqualTo(GIV(hiddenRootsObj), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(page & (tagMask())))) + && (oopisLessThan(page, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(hiddenRootsObj) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(hiddenRootsObj)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(majorIndex) << (shiftForWord()))))),page); + GIV(numClassTablePages) += 1; + minorIndex = 0; + } + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (i = minorIndex; i <= toDoLimit; i += 1) { + if ((longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))))) == GIV(nilObj)) { + GIV(classTableIndex) = ((((usqInt)(majorIndex) << (classTableMajorIndexShift())))) + i; + + /* classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + assert(GIV(classTableIndex) >= (1U << (classTableMajorIndexShift()))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(i, page, aBehavior)); + assert(isNonImmediate(page)); + if (oopisGreaterThanOrEqualTo(page, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(aBehavior & (tagMask())))) + && (oopisLessThan(aBehavior, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(page + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(page); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((page + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),aBehavior); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(aBehavior + 4),((((long32At((void *)(aBehavior + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (GIV(classTableIndex) & (identityHashHalfWordMask()))); + assert((classAtIndex(rawHashBitsOf(aBehavior))) == aBehavior); + return 0; + } + } + minorIndex = 0; + + /* classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + majorIndex = ((((majorIndex + 1) & ((1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift()))) - 1)) < 1) ? 1 : ((majorIndex + 1) & ((1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift()))) - 1))); + if (majorIndex == initialMajorIndex) { + return PrimErrLimitExceeded; + } + } + return 0; +} + + /* SpurMemoryManager>>#existInstancesInNewSpaceOf: */ +static NoDbgRegParms sqInt +existInstancesInNewSpaceOf(sqInt classObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + + /* begin allNewSpaceObjectsDo: */ + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l2; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOopSqInt, GIV(freeStart))) { + assert(isBridgeOrEnumerableObjectNoAssert(objOopSqInt)); + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == classIndex) { + return 1; + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOopSqInt = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + /* end allNewSpaceEntitiesDo: */ +l2: + return 0; +} + + +/* Remove aBehavior from the class table. */ + + /* SpurMemoryManager>>#expungeFromClassTable: */ +static NoDbgRegParms void +expungeFromClassTable(sqInt aBehavior) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int classIndex; + sqInt classTablePage; + sqInt majorIndex; + int minorIndex; + + assert(isInClassTable(aBehavior)); + classIndex = (long32At((void *)(aBehavior + 4))) & (identityHashHalfWordMask()); + majorIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + minorIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(majorIndex) << (shiftForWord())))))); + assert(classTablePage != GIV(classTableFirstPage)); + assert((numSlotsOf(classTablePage)) == (classTablePageSize())); + assert((fetchPointerofObject(minorIndex, classTablePage)) == aBehavior); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(classTablePage)) + && (!(isForwarded(classTablePage)))); + assert(validStorePointerUncheckedArgs(minorIndex, classTablePage, GIV(nilObj))); + longAtput((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(minorIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* If the removed class is before the classTableIndex, set the + classTableIndex to point to the empty slot so as to reuse it asap. */ + if (classIndex < GIV(classTableIndex)) { + GIV(classTableIndex) = classIndex; + } + + /* classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + assert(GIV(classTableIndex) >= (1U << (classTableMajorIndexShift()))); +} + + /* SpurMemoryManager>>#falseObject */ +sqInt +falseObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(falseObj); +} + + /* SpurMemoryManager>>#fetchByte:ofObject: */ +sqInt +fetchByteofObject(sqInt byteIndex, sqInt objOop) +{ + return byteAt((void *)((objOop + BaseHeaderSize) + byteIndex)); +} + + /* SpurMemoryManager>>#fetchClassOfNonImm: */ +static NoDbgRegParms NeverInline sqInt +fetchClassOfNonImm(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt classTablePage; + sqInt fieldIndex; + + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + if (classIndex <= (classIsItselfClassIndexPun())) { + if (classIndex == (classIsItselfClassIndexPun())) { + return objOop; + } + + /* Answer nil to avoid the assert failure in classOrNilAtIndex: */ + if (classIndex == (isForwardedObjectClassIndexPun())) { + return GIV(nilObj); + } + } + assert(classIndex >= (arrayClassIndexPun())); + + /* begin classOrNilAtIndex: */ + assert((classIndex <= (tagMask())) + || (classIndex >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return GIV(nilObj); + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#fetchClassOf: */ +sqInt +fetchClassOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt tagBits; + + return ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); +} + + +/* In Spur an object's classIndex is the tag in all method caches. */ + + /* SpurMemoryManager>>#fetchClassTagOfNonImm: */ +static NoDbgRegParms sqInt +fetchClassTagOfNonImm(sqInt obj) +{ + return (longAt((void *)(obj))) & (classIndexMask()); +} + + /* SpurMemoryManager>>#fetchInteger:ofObject: */ +sqLong +fetchIntegerofObject(sqInt fieldIndex, sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt oop; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + return ((((oop) & 7) == 1) + ? (oop >> 3) + : (GIV(primFailCode) = PrimErrInappropriate)); + } + return (fmt < (firstShortFormat()) + ? (fmt < (firstLongFormat()) + ? long64At((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 3))))) + : long32At((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2)))))) + : (fmt < (firstCompiledMethodFormat()) + ? (fmt < (firstByteFormat()) + ? byteAt((void *)((objOop + BaseHeaderSize) + fieldIndex)) + : shortAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 1)))))) + : (GIV(primFailCode) = PrimErrUnsupported))); +} + + +/* index by 32-bit units, and return a 32-bit value. Intended to replace + fetchWord:ofObject: + */ + + /* SpurMemoryManager>>#fetchLong32:ofObject: */ +int +fetchLong32ofObject(sqInt fieldIndex, sqInt oop) +{ + return long32At((void *)((oop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2))))); +} + + /* SpurMemoryManager>>#fetchPointer:ofFreeChunk: */ +static NoDbgRegParms sqInt +fetchPointerofFreeChunk(sqInt fieldIndex, sqInt objOop) +{ + return longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#fetchPointer:ofObject: */ +sqInt +fetchPointerofObject(sqInt fieldIndex, sqInt objOop) +{ + return longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#findClassNamed: */ +sqInt +findClassNamed(char *aString) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOrNil; + sqInt i; + sqInt ignored; + sqInt j; + sqInt page; + sqInt toDoLimit; + + /* begin classTableEntriesDo: */ + for (i = 0; i < GIV(numClassTablePages); i += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNil = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNil != GIV(nilObj)) { + ignored = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + if (classNameOfIs(classOrNil, aString)) { + return classOrNil; + } + } + } + } + return null; +} + + +/* Answer, but do not remove, the largest free chunk in the free lists. */ + + /* SpurMemoryManager>>#findLargestFreeChunk */ +static sqInt +findLargestFreeChunk(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt childNode; + sqInt treeNode; + + childNode = 0; + treeNode = GIV(freeLists)[0]; + if (!treeNode) { + return null; + } + while (1) { + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(treeNode)); + assert((bytesInBody(treeNode)) >= ((numFreeLists()) * (allocationUnit()))); + childNode = longAt((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + if (!(childNode != 0)) break; + treeNode = childNode; + } + return treeNode; +} + + +/* Print the oops of all string-like things that start with the same + characters as aCString + */ +/* useful for debugging */ + + /* SpurMemoryManager>>#findStringBeginningWith: */ +void +findStringBeginningWith(char *aCString) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aCStringStrlen; + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + aCStringStrlen = strlen(aCString); + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (/* object:beginsWithString:ofSize: */ + (/* isBytes: */ + ((!(objSqInt & (tagMask())))) + && (((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((!(((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((numBytesOfBytes(objSqInt)) >= aCStringStrlen) + && ((strncmp(aCString, firstIndexableField(objSqInt), aCStringStrlen)) == 0)))) { + printHex(objSqInt); + + /* begin space */ + printChar(' '); + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)(lengthOf(objSqInt)))); + printChar(' '); + printOopShortInner(objSqInt); + cr(); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + +/* Print the oops of all string-like things that have the same characters as + aCString + */ + + /* SpurMemoryManager>>#findString: */ +void +findString(char *aCString) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aCStringStrlen; + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + aCStringStrlen = strlen(aCString); + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (/* object:equalsString:ofSize: */ + (/* isBytes: */ + ((!(objSqInt & (tagMask())))) + && (((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((!(((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((numBytesOfBytes(objSqInt)) == aCStringStrlen) + && ((strncmp(aCString, firstIndexableField(objSqInt), aCStringStrlen)) == 0)))) { + printHex(objSqInt); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + cr(); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + /* SpurMemoryManager>>#fireEphemeron: */ +static NoDbgRegParms void +fireEphemeron(sqInt ephemeron) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + + assert(isNonImmediate(ephemeron)); + format = (byteAt((void *)(ephemeron + (formatFieldByteOffset())))) & (formatMask()); + if (format == (ephemeronFormat())) { + queueMourner(ephemeron); + + /* begin setEphemeronToFired: */ + assert((isNonImmediate(ephemeron)) + && (isEphemeron(ephemeron))); + + /* begin setFormatOf:to: */ + assert((((nonIndexablePointerFormat()) >= 0) && ((nonIndexablePointerFormat()) <= (formatMask())))); + byteAtput((void *)(ephemeron + (formatFieldByteOffset())),((byteAt((void *)(ephemeron + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (nonIndexablePointerFormat())); + + /* begin signalFinalization: */ + forceInterruptCheck(); + GIV(pendingFinalizationSignals) += 1; + } + else { + assert((format == (nonIndexablePointerFormat())) + && (isonObjStack(ephemeron, GIV(mournQueue)))); + } +} + + /* SpurMemoryManager>>#firstAccessibleObject */ +static sqInt +firstAccessibleObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt savedTenuringThreshold; + + assert(GIV(nilObj) == GIV(oldSpaceStart)); + + /* flush newSpace to settle the enumeration. */ + + /* begin flushNewSpace */ + savedTenuringThreshold = GIV(tenureThreshold); + GIV(tenureThreshold) = GIV(oldSpaceStart); + scavengingGCTenuringIf(TenureByAge); + + /* begin setRawTenuringThreshold: */ + GIV(tenureThreshold) = savedTenuringThreshold; + assert((GIV(rememberedSetSize)) == 0); + assert(GIV(pastSpaceStart) == (((GIV(pastSpace)).start))); + assert(GIV(freeStart) == (((GIV(eden)).start))); + return GIV(nilObj); +} + + +/* for the message send breakpoint; selectors can be immediates. */ + + /* SpurMemoryManager>>#firstFixedFieldOfMaybeImmediate: */ +static NoDbgRegParms void * +firstFixedFieldOfMaybeImmediate(sqInt oop) +{ + return (((oop & (tagMask())) != 0) + ? ((void *)oop) + : pointerForOop(oop + BaseHeaderSize)); +} + + /* SpurMemoryManager>>#firstFixedField: */ +void * +firstFixedField(sqInt objOop) +{ + return pointerForOop(objOop + BaseHeaderSize); +} + + +/* NOTE: overridden in various simulator subclasses to add coercion to + CArray, so please duplicate any changes. + There are only two important cases, both for objects with named inst vars, + i.e. formats 2,3 & 5. + The first indexable field for formats 2 & 5 is the slot count (by + convention, even though that's off the end + of the object). For 3 we must go to the class. */ + + /* SpurMemoryManager>>#firstIndexableField: */ +void * +firstIndexableField(sqInt objOop) +{ + sqInt classFormat; + sqInt classPointer; + sqInt fmt; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= (weakArrayFormat())) { + if (fmt == (arrayFormat())) { + return pointerForOop(objOop + BaseHeaderSize); + } + + /* array starts at 0. */ + if (fmt >= (indexablePointersFormat())) { + classPointer = fetchClassOfNonImm(objOop); + + /* begin formatOfClass: */ + classFormat = ((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + return pointerForOop((objOop + BaseHeaderSize) + ((((usqInt)((classFormat & ((1U << (fixedFieldsFieldWidth())) - 1))) << (shiftForWord()))))); + } + + /* indexable with inst vars; need to delve into the class format word + otherwise not indexable */ + return 0; + } + + /* All bit objects, and indeed CompiledMethod, though this is a no-no, start at 0 */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat()))) { + return pointerForOop(objOop + BaseHeaderSize); + } + + /* otherwise not indexable */ + return 0; +} + + /* SpurMemoryManager>>#fixedFieldsOfClassFormatMask */ +sqInt +fixedFieldsOfClassFormatMask(void) +{ + return (1U << (fixedFieldsFieldWidth())) - 1; +} + + /* SpurMemoryManager>>#fixedFieldsOfClassFormat: */ +sqInt +fixedFieldsOfClassFormat(sqInt classFormat) +{ + return classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); +} + + /* SpurMemoryManager>>#fixedFieldsOf:format:length: */ +static NoDbgRegParms sqInt +fixedFieldsOfformatlength(sqInt objOop, sqInt fmt, sqInt wordLength) +{ + sqInt class; + + + /* N.B. written to fall through to fetchClassOfNonImm: et al for forwarders + so as to trigger an assert fail. */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + return 0; + } + if (fmt < (arrayFormat())) { + return wordLength; + } + class = fetchClassOfNonImm(objOop); + return (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); +} + + +/* Private helper for followField:ofObject: to avoid code duplication for + rare case. + */ + + /* SpurMemoryManager>>#fixFollowedField:ofObject:withInitialValue: */ +static NoDbgRegParms NeverInline sqInt +fixFollowedFieldofObjectwithInitialValue(sqInt fieldIndex, sqInt anObject, sqInt initialValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + + assert(isOopForwarded(initialValue)); + + /* inlined followForwarded: for speed (one less test) */ + objOop = initialValue; + do { + objOop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + } while(/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, anObject, objOop)); + assert(isNonImmediate(anObject)); + if (oopisGreaterThanOrEqualTo(anObject, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objOop & (tagMask())))) + && (oopisLessThan(objOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(anObject + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(anObject); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((anObject + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),objOop); + return objOop; +} + + +/* Answer the object the ephemeron guards. This is its first element. */ + + /* SpurMemoryManager>>#followedKeyOfEphemeron: */ +static NoDbgRegParms sqInt +followedKeyOfEphemeron(sqInt objOop) +{ + sqInt oop; + + assert((isNonImmediate(objOop)) + && (isEphemeron(objOop))); + + /* begin followOopField:ofObject: */ + oop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + oop = fixFollowedFieldofObjectwithInitialValue(0, objOop, oop); + } + return oop; +} + + +/* Make sure the oop at fieldIndex in anObject is not forwarded (follow the + forwarder there-in if so). Answer the (possibly followed) oop at + fieldIndex. */ + + /* SpurMemoryManager>>#followField:ofObject: */ +static NoDbgRegParms sqInt +followFieldofObject(sqInt fieldIndex, sqInt anObject) +{ + sqInt objOop; + + objOop = longAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(fieldIndex, anObject, objOop); + } + return objOop; +} + + +/* Follow a forwarding pointer. This must be a loop because we cannot prevent + forwarders to + forwarders being created by lazy become. Consider the following example by + Igor Stasenko: + array := { a. b. c }. + - array at: 1 points to &a. array at: 2 points to &b. array at: 3 points + to &c + a becomeForward: b + - array at: 1 still points to &a. array at: 2 still points to &b. array + at: 3 still points to &c + b becomeForward: c. + - array at: 1 still points to &a. array at: 2 still points to &b. array + at: 3 still points to &c + - when accessing array first one has to follow a forwarding chain: + &a -> &b -> c */ + + /* SpurMemoryManager>>#followForwarded: */ +sqInt +followForwarded(sqInt objOop) +{ + sqInt referent; + + assert(isUnambiguouslyForwarder(objOop)); + referent = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + return referent; +} + + /* SpurMemoryManager>>#followMaybeForwarded: */ +static NoDbgRegParms sqInt +followMaybeForwarded(sqInt objOop) +{ + return (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + ? followForwarded(objOop) + : objOop); +} + + +/* Make sure the obj at fieldIndex in anObject is not forwarded (follow the + forwarder there-in if so). Answer the (possibly followed) obj at + fieldIndex. */ + + /* SpurMemoryManager>>#followObjField:ofObject: */ +static NoDbgRegParms sqInt +followObjFieldofObject(sqInt fieldIndex, sqInt anObject) +{ + sqInt objOop; + + objOop = longAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(fieldIndex, anObject, objOop); + } + return objOop; +} + + /* SpurMemoryManager>>#formatOfClass: */ +sqInt +formatOfClass(sqInt classPointer) +{ + return ((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); +} + + +/* 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState + et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable (lsb = # of unused 32-bit fields, 11 unused in 32 + bits) 12 - 15 16-bit indexable (ls2b = # of unused 16-bit fields, 14 & 15 + unused in 32-bits) + 16 - 23 byte indexable (ls3b = # of unused 8-bit fields, 20, 21, 22, 23 + unused in 32-bits) + 24 - 31 compiled method (ls3b = # of unused 8-bit fields, 20, 21, 22, 23 + unused in 32-bits) */ +/* A note on performance. Since the format field is, by design, aligned on a + byte boundary + in the fourth byte of the header (see headerForSlots:format:classIndex:) + it is accessed + as below. But it used to be accessed via + ^(self longAt: objOop) >> self formatShift bitAnd: self formatMask + This potentially involves more operations (a shift) and larger literals, + In practice clang + optimizes the long form to the byte form on x86_64, showing it is to be + preferred on at + least one architecture. On arm64 for example both forms take two + instructions. */ + + /* SpurMemoryManager>>#formatOf: */ +static NoDbgRegParms sqInt +formatOf(sqInt objOop) +{ + return (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); +} + + +/* Answer if anObject is itself forwarded, or is a pointer object containing + any references to forwarded objects. + */ + + /* SpurMemoryManager>>#forwardersIn: */ +static NoDbgRegParms sqInt +forwardersIn(sqInt anObject) +{ + sqInt i; + sqInt oop; + sqInt toDoLimit; + + if ((!((longAt((void *)(anObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + return 1; + } + toDoLimit = (numPointerSlotsOf(anObject)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + oop = longAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + return 1; + } + } + return 0; +} + + +/* for linking objecs on each free list, or, during pigCompact, doubly- + linking the free objects in address order using the xor link hack. */ + + /* SpurMemoryManager>>#freeChunkNextIndex */ +static sqInt +freeChunkNextIndex(void) +{ + return 0; +} + + +/* for organizing the tree of large free chunks. */ + + /* SpurMemoryManager>>#freeChunkParentIndex */ +static sqInt +freeChunkParentIndex(void) +{ + return 2; +} + + +/* For linking objecs on each free list, doubly-linking the free objects. + Free chunks of size 1 do not have a prev index. */ + + /* SpurMemoryManager>>#freeChunkPrevIndex */ +static sqInt +freeChunkPrevIndex(void) +{ + return 1; +} + + /* SpurMemoryManager>>#freeChunkWithBytes:at: */ +static NoDbgRegParms sqInt +freeChunkWithBytesat(sqInt bytes, sqInt address) +{ + sqInt freeChunk; + + assert(isInOldSpace(address)); + assert((segmentContainingObj(address)) == (segmentContainingObj(address + bytes))); + freeChunk = initFreeChunkWithBytesat(bytes, address); + addToFreeListbytes(freeChunk, bytes); + assert(freeChunk == (objectStartingAt(address))); + return freeChunk; +} + + +/* Free an object in oldSpace. Coalesce if possible to reduce fragmentation. */ + + /* SpurMemoryManager>>#freeObject: */ +sqInt +freeObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytes; + usqInt chunkBytes; + sqInt index; + sqInt next; + usqInt numSlots; + usqInt start; + + assert(isInOldSpace(objOop)); + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) { + /* begin forgetObject: */ + assert(GIV(rememberedSetSize) > 0); + assert(isRemembered(objOop)); + setIsRememberedOfto(objOop, 0); + if (!(objOop == (GIV(rememberedSet)[GIV(rememberedSetSize) - 1]))) { + index = 0; + while (index < GIV(rememberedSetSize)) { + if (objOop == (GIV(rememberedSet)[index])) { + GIV(rememberedSet)[index] = (GIV(rememberedSet)[GIV(rememberedSetSize) - 1]); + index = GIV(rememberedSetSize); + } + else { + index += 1; + } + } + } + GIV(rememberedSetSize) -= 1; + assert(GIV(rememberedSetSize) >= 0); + } + bytes = bytesInBody(objOop); + start = /* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)((start + bytes) + (numSlotsFieldByteOffset()))); + next = (numSlots == (numSlotsMask()) + ? (start + bytes) + BaseHeaderSize + : start + bytes); + if (((longAt((void *)(next))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + /* begin detachFreeObject: */ + chunkBytes = bytesInBody(next); + GIV(totalFreeOldSpace) -= chunkBytes; + unlinkFreeChunkchunkBytes(next, chunkBytes); + bytes += bytesInBody(next); + } + GIV(totalFreeOldSpace) += bytes; + return freeChunkWithBytesat(bytes, start); +} + + +/* Perform a full eager compacting GC. Answer the size of the largest free + chunk. + */ +/* for profiling */ + + /* SpurMemoryManager>>#fullGC */ +NeverInline usqLong +fullGC(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + usqLong compactionStartUsecs; + usqLong gcMarkEndUsecs; + sqInt i; + sqInt index; + usqInt numSlots; + sqInt obj; + sqInt objOop; + + GIV(needGCFlag) = 0; + GIV(gcStartUsecs) = ioUTCMicrosecondsNow(); + GIV(statMarkCount) = 0; + preGCAction(GCModeFull); + + /* begin globalGarbageCollect */ + assert(validObjStacks()); + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + + /* Mark objects /before/ scavenging, to empty the rememberedTable of unmarked roots. */ + markObjects(1); + gcMarkEndUsecs = ioUTCMicrosecondsNow(); + + /* begin forgetUnmarkedRememberedObjects */ + index = 0; + while (index < GIV(rememberedSetSize)) { + obj = GIV(rememberedSet)[index]; + if ((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + index += 1; + } + else { + setIsRememberedOfto(obj, 0); + GIV(rememberedSetSize) -= 1; + GIV(rememberedSet)[index] = (GIV(rememberedSet)[GIV(rememberedSetSize)]); + } + } + assert(GIV(rememberedSetSize) >= 0); + + /* begin setGCMode: */ + GIV(gcMode) = GCModeNewSpace; + doScavenge(MarkOnTenure); + + /* begin setGCMode: */ + GIV(gcMode) = GCModeFull; + + /* Mid-way the leak check must be more lenient. Unmarked classes will have been + expunged from the table, but unmarked instances will not yet have been reclaimed. */ + runLeakCheckerForexcludeUnmarkedObjsclassIndicesShouldBeValid(GCModeFull, 1, 1); + compactionStartUsecs = ioUTCMicrosecondsNow(); + + /* begin prepareForGlobalSweep */ + for (i = 0; i < GIV(numSegments); i += 1) { + ((GIV(segments)[i]).containsPinned = 0); + } + + /* begin bridgeAt: */ + /* begin bridgeFor: */ + address = (((((&(GIV(segments)[GIV(numSegments) - 1])))->segSize)) + ((((&(GIV(segments)[GIV(numSegments) - 1])))->segStart))) - (2 * BaseHeaderSize); + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + compact(); + + /* begin attemptToShrink */ + if ((GIV(totalFreeOldSpace) > GIV(shrinkThreshold)) + && ((GIV(totalFreeOldSpace) > GIV(growHeadroom)) + && (shrinkObjectMemory((((GIV(totalFreeOldSpace) - GIV(growHeadroom)) < GIV(growHeadroom)) ? GIV(growHeadroom) : (GIV(totalFreeOldSpace) - GIV(growHeadroom))))))) { + GIV(statShrinkMemory) += 1; + } + + /* begin setHeapSizeAtPreviousGC */ + GIV(heapSizeAtPreviousGC) = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + + /* begin resetAllocationAccountingAfterGC */ + GIV(oldSpaceUsePriorToScavenge) = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + assert(validObjStacks()); + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + assert(allObjectsUnmarked()); + runLeakCheckerFor(GCModeFull); + postGCAction(GCModeFull); + GIV(statGCEndUsecs) = ioUTCMicrosecondsNow(); + + /* begin updateFullGCStats */ + GIV(statFullGCs) += 1; + GIV(statFullGCUsecs) += GIV(statGCEndUsecs) - GIV(gcStartUsecs); + GIV(statCompactionUsecs) += GIV(statGCEndUsecs) - compactionStartUsecs; + if (GIV(gcSweepEndUsecs)) { + GIV(statSweepUsecs) += GIV(gcSweepEndUsecs) - compactionStartUsecs; + } + GIV(statMarkUsecs) += gcMarkEndUsecs - GIV(gcStartUsecs); + return (GIV(freeLists)[0] + ? bytesInBody(findLargestFreeChunk()) + : 0); +} + + /* SpurMemoryManager>>#getHeapGrowthToSizeGCRatio */ +static float +getHeapGrowthToSizeGCRatio(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(heapGrowthToSizeGCRatio); +} + + /* SpurMemoryManager>>#goodContextSize: */ +static NoDbgRegParms sqInt +goodContextSize(sqInt oop) +{ + usqInt numSlots; + usqInt numSlotsUsqInt; + + /* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + return (numSlots == SmallContextSlots) + || (numSlots == LargeContextSlots); +} + + +/* Attempt to grow memory by at least minAmmount. + Answer the size of the new segment, or nil if the attempt failed. */ + + /* SpurMemoryManager>>#growOldSpaceByAtLeast: */ +static NoDbgRegParms usqInt +growOldSpaceByAtLeast(sqInt minAmmount) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt ammount; + sqInt bytes; + sqInt headroom; + sqInt interval; + SpurSegmentInfo *segInfo; + sqInt start; + usqInt total; + + + /* statGrowMemory counts attempts, not successes. */ + /* we need to include overhead for a new object header plus the segment bridge. */ + GIV(statGrowMemory) += 1; + ammount = minAmmount + ((BaseHeaderSize * 2) + (2 * BaseHeaderSize)); + + /* round up to the nearest power of two. */ + ammount = 1ULL << (highBit(ammount - 1)); + + /* and grow by at least growHeadroom. */ + ammount = ((ammount < GIV(growHeadroom)) ? GIV(growHeadroom) : ammount); + + /* Now apply the maxOldSpaceSize limit, if one is in effect. */ + if (maxOldSpaceSize > 0) { + /* begin totalOldSpaceSize */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)); + total = GIV(totalHeapSizeIncludingBridges); + if (total >= maxOldSpaceSize) { + GIV(needGCFlag) = 1; + return null; + } + headroom = maxOldSpaceSize - total; + if (headroom < ammount) { + if (headroom < (minAmmount + ((BaseHeaderSize * 2) + (2 * BaseHeaderSize)))) { + GIV(needGCFlag) = 1; + return null; + } + ammount = headroom; + } + } + start = ioUTCMicrosecondsNow(); + if ((segInfo = addSegmentOfSize(ammount))) { + /* begin assimilateNewSegment: */ + if ((((segInfo->segSize)) + ((segInfo->segStart))) >= GIV(endOfMemory)) { + GIV(freeOldSpaceStart) = (GIV(endOfMemory) = (((segInfo->segSize)) + ((segInfo->segStart))) - (2 * BaseHeaderSize)); + } + + /* and add the new free chunk to the free list; done here + instead of in assimilateNewSegment: for the assert */ + bytes = ((segInfo->segSize)) - (2 * BaseHeaderSize); + address = (segInfo->segStart); + + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += bytes; + freeChunkWithBytesat(bytes, address); + assert((addressAfter(objectStartingAt((segInfo->segStart)))) == ((segLimit(segInfo)) - (bridgeSize()))); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCCheckFreeSpace)) == (GCCheckFreeSpace | GCCheckFreeSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + checkSegments(); + interval = (ioUTCMicrosecondsNow()) - start; + if (interval > GIV(statMaxAllocSegmentTime)) { + GIV(statMaxAllocSegmentTime) = interval; + } + return (segInfo->segSize); + } + GIV(needGCFlag) = 1; + return null; +} + + /* SpurMemoryManager>>#hasPointerFieldsNonImm: */ +static NoDbgRegParms sqInt +hasPointerFieldsNonImm(sqInt oop) +{ + sqInt format; + + format = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isAnyPointerFormat: */ + return (format <= 5 /* lastPointerFormat */) + || (format >= (firstCompiledMethodFormat())); +} + + +/* The header format in LSB is + MSB: | 8: numSlots | (on a byte boundary) + | 2 bits | (msb,lsb = {isMarked,isGrey}) + | 22: identityHash | (on a word boundary) + | 3 bits | (msb <-> lsb = {UNUSED,isPinned,isRemembered} + | 5: format | (on a byte boundary) + | 2 bits | (msb,lsb = {isImmutable,isExtraAligned}) + | 22: classIndex | (on a word boundary) : LSB + The 7 bit flags are used (or reserved) for + isExtraAligned (bit 22) + isImmutable (bit 23) + isRemembered (bit 29) + isPinned (bit 30) + UNUSED (bit 31) + isGrey (bit 54) + isMarked (bit 55) + The three bit field { UNUSED, isPinned, isRemembered } is for bits that + are never + set in young objects (forcing uses of the UNUSED bit to be for oldSPace + objects only). + This allows the remembered table to be pruned when full by using these + bits as a + reference count of newSpace objects from the remembered table. Objects + with a + high count should be tenured to prune the remembered table. */ + + /* SpurMemoryManager>>#headerForSlots:format:classIndex: */ +usqLong +headerForSlotsformatclassIndex(sqInt numSlots, sqInt formatField, sqInt classIndex) +{ + return ((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex; +} + + +/* The header format in LSB is + MSB: | 8: numSlots | (on a byte boundary) + | 2 bits | (msb,lsb = {isMarked,isGrey}) + | 22: identityHash | (on a word boundary) + | 3 bits | (msb <-> lsb = {UNUSED,isPinned,isRemembered} + | 5: format | (on a byte boundary) + | 2 bits | (msb,lsb = {isImmutable,isExtraAligned}) + | 22: classIndex | (on a word boundary) : LSB + The 7 bit flags are used (or reserved) for + isExtraAligned (bit 22) + isImmutable (bit 23) + isRemembered (bit 29) + isPinned (bit 30) + UNUSED (bit 31) + isGrey (bit 54) + isMarked (bit 55) + The three bit field { UNUSED, isPinned, isRemembered } is for bits that + are never + set in young objects (forcing uses of the UNUSED bit to be for oldSPace + objects only). + This allows the remembered table to be pruned when full by using these + bits as a + reference count of newSpace objects from the remembered table. Objects + with a + high count should be tenured to prune the remembered table. */ + + /* SpurMemoryManager>>#headerForSlots:hash:format:classIndex: */ +static NoDbgRegParms usqLong +headerForSlotshashformatclassIndex(sqInt numSlots, sqInt hash, sqInt formatField, sqInt classIndex) +{ + return (((((((usqLong) numSlots)) << (numSlotsFullShift()))) + (((((usqLong) hash)) << (identityHashFullWordShift())))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex; +} + + +/* Answer the number of extra root slots in the root of the hidden root + object. + */ + + /* SpurMemoryManager>>#hiddenRootSlots */ +static sqInt +hiddenRootSlots(void) +{ + return 8; +} + + /* SpurMemoryManager>>#hiddenRootsObject */ +static sqInt +hiddenRootsObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(hiddenRootsObj); +} + + +/* a more complex version that tells both the word reversal and the + endianness of the machine + it came from. Low half of word is e.g. 6521. Top byte is top byte of + #doesNotUnderstand: on + this machine. ($d on the Mac or $s on the PC) */ + + /* SpurMemoryManager>>#imageSegmentVersion */ +static sqInt +imageSegmentVersion(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int wholeWord; + + /* first data word, 'does' */ + wholeWord = long32At((void *)((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord()))))))) + BaseHeaderSize)); + return 68021 /* imageFormatVersion */ | (wholeWord & 0xFF000000U); +} + + +/* mask the immutable bit in the base header word */ + + /* SpurMemoryManager>>#immutableBitMask */ +#if IMMUTABILITY +sqInt +immutableBitMask(void) +{ + return 1U << (immutableBitShift()); +} +#endif /* IMMUTABILITY */ + + /* SpurMemoryManager>>#indexOf:in: */ +sqInt +indexOfin(sqInt anElement, sqInt anObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt fmtSqInt; + sqInt i; + usqInt numSlots; + usqInt numSlotsUsqInt; + + fmt = (byteAt((void *)(anObject + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(anObject)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(anObject + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(anObject - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + for (i = 0; i <= numSlots; i += 1) { + if (anElement == (longAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + return i; + } + } + } + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + return (GIV(primFailCode) = PrimErrUnsupported); + } + + /* begin numBytesOfBytes: */ + fmtSqInt = (byteAt((void *)(anObject + (formatFieldByteOffset())))) & (formatMask()); + assert(fmtSqInt >= (firstByteFormat())); + numSlots = ((((/* begin numSlotsOf: */ + assert((classIndexOf(anObject)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(anObject + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(anObject - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) << (shiftForWord()))) - (fmtSqInt & 7); + for (i = 0; i <= numSlots; i += 1) { + if (anElement == (byteAt((void *)((anObject + BaseHeaderSize) + i)))) { + return i; + } + } + } + if (fmt >= (firstShortFormat())) { + numSlots = ((usqInt)((numBytesOf(anObject)))) >> 1; + for (i = 0; i <= numSlots; i += 1) { + if (anElement == (((unsigned short) (shortAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(i) << 1))))))))) { + return i; + } + } + } + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(anObject)))) >> 3; + for (i = 0; i <= numSlots; i += 1) { + if (anElement == (long64At((void *)((anObject + BaseHeaderSize) + ((((usqInt)(i) << 3))))))) { + return i; + } + } + } + if (fmt >= (firstLongFormat())) { + numSlots = ((usqInt)((numBytesOf(anObject)))) >> 2; + for (i = 0; i <= numSlots; i += 1) { + if (anElement == (long32At((void *)((anObject + BaseHeaderSize) + ((((usqInt)(i) << 2))))))) { + return i; + } + } + } + return -1; +} + + +/* Part of reorderReversedTreeList:. Switch treeNode with newNode in + the tree, but do nothing to the list linked through freeChunkNextIndex. */ + + /* SpurMemoryManager>>#inFreeTreeReplace:with: */ +static NoDbgRegParms void +inFreeTreeReplacewith(sqInt treeNode, sqInt newNode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fieldIndex; + sqInt i; + sqInt relative; + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(newNode)); + longAtput((void *)((newNode + BaseHeaderSize) + (1U << (shiftForWord()))),0); + + /* copy parent, smaller, larger */ + for (i = 2 /* freeChunkParentIndex */; i <= 4 /* freeChunkLargerIndex */; i += 1) { + relative = longAt((void *)((treeNode + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (i == 2 /* freeChunkParentIndex */) { + if (relative) { + fieldIndex = (treeNode == (longAt((void *)((relative + BaseHeaderSize) + (3U << (shiftForWord()))))) + ? 3 /* freeChunkSmallerIndex */ + : 4 /* freeChunkLargerIndex */); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(relative)); + assert((newNode == 0) + || (isFreeObject(newNode))); + longAtput((void *)((relative + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),newNode); + } + else { + assert((GIV(freeLists)[0]) == treeNode); + GIV(freeLists)[0] = newNode; + } + } + else { + if (relative) { + assert((fetchPointerofFreeChunk(freeChunkParentIndex(), relative)) == treeNode); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(relative)); + assert((newNode == 0) + || (isFreeObject(newNode))); + longAtput((void *)((relative + BaseHeaderSize) + (2U << (shiftForWord()))),newNode); + } + } + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(newNode)); + assert((relative == 0) + || (isFreeObject(relative))); + longAtput((void *)((newNode + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),relative); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(treeNode)); + longAtput((void *)((treeNode + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),0); + } +} + + /* SpurMemoryManager>>#initialInstanceOf: */ +static NoDbgRegParms sqInt +initialInstanceOf(sqInt classObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int classIndex; + int classIndexInt; + sqInt classIndexSqInt; + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + if (classIndex) { + /* flush instances in newSpace to settle the enumeration. */ + + /* begin flushNewSpaceInstancesOf: */ + classIndexInt = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + if (!classIndexInt) { + goto l1; + } + + /* no instances; nothing to do */ + + /* begin tenuringClassIndex: */ + GIV(tenuringClassIndex) = classIndexInt; + scavengingGCTenuringIf(TenureByClass); + assert(!(existInstancesInNewSpaceOf(classObj))); + /* end flushNewSpaceInstancesOf: */ +l1: + + /* scan old space for the first one found. */ + + /* begin allOldSpaceObjectsDo: */ + /* begin allOldSpaceObjectsFrom:do: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndexSqInt = (longAt((void *)(objOopSqInt))) & (classIndexMask()); + assert((classIndexSqInt == (segmentBridgePun())) + || ((classIndexSqInt == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objOopSqInt))) != 0) + && (classIndexSqInt < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndexSqInt >= (isForwardedObjectClassIndexPun())) { + if (classIndex == ((longAt((void *)(objOopSqInt))) & (classIndexMask()))) { + return objOopSqInt; + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l2:; + } + } + return null; +} + + /* SpurMemoryManager>>#initializeNewSpaceVariables */ +static NeverInline void +initializeNewSpaceVariables(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(freeStart) = ((GIV(eden)).start); + GIV(pastSpaceStart) = ((GIV(pastSpace)).start); + GIV(scavengeThreshold) = ((((GIV(eden)).limit)) - ((((GIV(eden).limit)) - ((GIV(eden).start))) / 64)) - (interpreterAllocationReserveBytes()); + GIV(newSpaceStart) = (((((GIV(pastSpace)).start)) < (((GIV(futureSpace)).start))) ? (((GIV(pastSpace)).start)) : (((GIV(futureSpace)).start))); + assert(GIV(newSpaceStart) < (((GIV(eden)).start))); +} + + +/* Initialize object memory variables at startup time. Assume endOfMemory at + al are + initialised by the image-reading code via + setHeapBase:memoryLimit:endOfMemory:. endOfMemory is assumed to point to + the end of the last object in the image. + Assume: image reader also initializes the following variables: + specialObjectsOop + lastHash */ +/* Catch mis-initializations leading to bad translations to C */ + + /* SpurMemoryManager>>#initializeObjectMemory: */ +static NoDbgRegParms void +initializeObjectMemory(sqInt bytesToShift) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anOop; + sqInt cameFrom; + usqInt chunkBytes; + sqInt classIndex; + sqInt classTableRoot; + sqInt field; + sqInt fieldAddr; + sqInt fieldOop; + sqInt firstObj; + sqInt followingWord; + usqInt followingWordAddress; + sqInt freeChunk; + sqInt freeListObj; + usqInt freeOldStart; + sqInt fudge; + sqInt i; + sqInt index; + sqInt j; + sqInt largeChild; + usqInt limit; + sqInt min; + sqInt nilObjPreSwizzle; + usqInt numSlots; + sqInt obj; + usqInt p; + sqInt page; + sqInt smallChild; + usqInt startOfFreeOldSpace; + usqInt toDoLimit; + sqInt treeNode; + sqInt valuePointer; + + assert(BaseHeaderSize == BaseHeaderSize); + assert((((sqInt)((maxSlotsForAlloc()) * BytesPerWord))) > 0); + initSegmentBridgeWithBytesat(2 * BaseHeaderSize, GIV(endOfMemory) - (2 * BaseHeaderSize)); + adjustSegmentSwizzlesBy(bytesToShift); + + /* image may be at a different address; adjust oops for new location */ + + /* begin adjustAllOopsBy: */ + assert(newSpaceIsEmpty()); + numSlots = byteAt((void *)(GIV(oldSpaceStart) + (numSlotsFieldByteOffset()))); + firstObj = (numSlots == (numSlotsMask()) + ? GIV(oldSpaceStart) + BaseHeaderSize + : GIV(oldSpaceStart)); + classTableRoot = oldSpaceObjectAfter(oldSpaceObjectAfter(oldSpaceObjectAfter(oldSpaceObjectAfter(firstObj)))); + nilObjPreSwizzle = GIV(oldSpaceStart) - bytesToShift; + + /* begin numSlotsOf: */ + assert((classIndexOf(classTableRoot)) > (isForwardedObjectClassIndexPun())); + GIV(numClassTablePages) = (((numSlots = byteAt((void *)(classTableRoot + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classTableRoot - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + assert(GIV(numClassTablePages) == ((classTableRootSlots()) + (hiddenRootSlots()))); + for (i = 2; i < GIV(numClassTablePages); i += 1) { + if ((longAt((void *)((classTableRoot + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))))) == nilObjPreSwizzle) { + GIV(numClassTablePages) = i; + goto l4; + } + } + /* end countNumClassPagesPreSwizzle: */ +l4: + if ((bytesToShift != 0) + || (GIV(numSegments) > 1)) { + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(GIV(oldSpaceStart) + (numSlotsFieldByteOffset()))); + obj = (numSlots == (numSlotsMask()) + ? GIV(oldSpaceStart) + BaseHeaderSize + : GIV(oldSpaceStart)); + while (oopisLessThan(obj, GIV(freeOldSpaceStart))) { + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + /* begin swizzleFieldsOfObject: */ + fieldAddr = obj + (lastPointerOfWhileSwizzling(obj)); + while (oopisGreaterThanOrEqualTo(fieldAddr, obj + BaseHeaderSize)) { + fieldOop = longAt((void *)(fieldAddr)); + if ((!(fieldOop & (tagMask())))) { + longAtput((void *)(fieldAddr),swizzleObj(fieldOop)); + } + fieldAddr -= BytesPerOop; + } + } + else { + if (classIndex == (isFreeObjectClassIndexPun())) { + /* begin swizzleFieldsOfFreeChunk: */ + field = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + if (field) { + valuePointer = swizzleObj(field); + + /* begin storeUncheckedNoAssert:ofAny:withValue: */ + longAtput((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + } + chunkBytes = bytesInBody(obj); + + /* The prevPointer is not guaranteed to be valid in older images. + updateListStartingAt: via updateFreeLists does restore the prev pointer + in all small free lists, so simply avoid swizzling it now. */ + if (chunkBytes >= 0x200 /* (numFreeLists * #allocationUnit) */) { + for (index = 2 /* freeChunkParentIndex */; index <= 4 /* freeChunkLargerIndex */; index += 1) { + field = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (field) { + valuePointer = swizzleObj(field); + + /* begin storeUncheckedNoAssert:ofAny:withValue: */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),valuePointer); + } + } + } + } + } + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3:; + } + } + GIV(specialObjectsOop) = swizzleObj(GIV(specialObjectsOop)); + + /* heavily used special objects */ + GIV(nilObj) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(NilObject) << (shiftForWord())))))); + GIV(falseObj) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(FalseObject) << (shiftForWord())))))); + GIV(trueObj) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TrueObject) << (shiftForWord())))))); + + /* In Cog we insist that nil, true & false are next to each other (Cogit generates tighter + conditional branch code as a result). In addition, Spur places the free lists and + class table root page immediately following them. */ + assert(GIV(nilObj) == GIV(oldSpaceStart)); + assert(GIV(falseObj) == (oldSpaceObjectAfter(GIV(nilObj)))); + assert(GIV(trueObj) == (oldSpaceObjectAfter(GIV(falseObj)))); + freeListObj = oldSpaceObjectAfter(GIV(trueObj)); + anOop = oldSpaceObjectAfter(freeListObj); + + /* begin setHiddenRootsObj: */ + GIV(hiddenRootsObj) = anOop; + assert(validClassTableRootPages()); + GIV(classTableFirstPage) = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(((numSlotsOf(GIV(classTableFirstPage))) - 1) == (classTableMinorIndexMask())); + + /* Hack fix. A bug in markAndTraceClassOf: caused the class of the first class table page + to be changed from its pun. This can be restored manually, but we do it here too. */ + if (((longAt((void *)(GIV(classTableFirstPage)))) & (classIndexMask())) != (arrayClassIndexPun())) { + /* begin setClassIndexOf:to: */ + assert((((arrayClassIndexPun()) >= 0) && ((arrayClassIndexPun()) <= (classIndexMask())))); + longAtput((void *)(GIV(classTableFirstPage)),((longAt((void *)(GIV(classTableFirstPage)))) & (~(usqIntptr_t)(classIndexMask()))) + (arrayClassIndexPun())); + } + GIV(numClassTablePages) = 1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift())); + + /* If loading an image, set the classTableIndex to the first unused slot in the class table after the first page. + Set numClassTablePages to the number of used pages. + Set classTableIndex to point at the first unused entry. First set it to the max as a sentinel. */ + GIV(classTableIndex) = ((sqInt)((usqInt)(GIV(numClassTablePages)) << (classTableMajorIndexShift()))); + for (i = 1; i < GIV(numClassTablePages); i += 1) { + if (((page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) == GIV(nilObj)) { + if ((((usqInt)(GIV(classTableIndex))) >> (classTableMajorIndexShift())) > i) { + GIV(classTableIndex) = ((sqInt)((usqInt)(((((i - 1) < 1) ? 1 : (i - 1)))) << (classTableMajorIndexShift()))); + } + GIV(numClassTablePages) = i; + assert((classOrNilAtIndex(GIV(classTableIndex))) == GIV(nilObj)); + goto l1; + } + else { + if ((((usqInt)(GIV(classTableIndex))) >> (classTableMajorIndexShift())) > i) { + j = 0; + while (j < (1U << (classTableMajorIndexShift()))) { + if ((longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord()))))))) == GIV(nilObj)) { + GIV(classTableIndex) = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + j = 1U << (classTableMajorIndexShift()); + } + j += 1; + } + } + } + } + + /* no unused slots; set it to the start of the second page. */ + if ((((usqInt)(GIV(classTableIndex))) >> (classTableMajorIndexShift())) >= GIV(numClassTablePages)) { + GIV(classTableIndex) = 1U << (classTableMajorIndexShift()); + } + assert((classOrNilAtIndex(GIV(classTableIndex))) == GIV(nilObj)); + /* end setHiddenRootsObj: */ +l1: + GIV(markStack) = swizzleObjStackAt(MarkStackRootIndex); + GIV(weaklingStack) = swizzleObjStackAt(WeaklingStackRootIndex); + GIV(mournQueue) = swizzleObjStackAt(MournQueueRootIndex); + assert(validObjStacks()); + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + + /* begin initializeFreeSpacePostLoad: */ + assert((numSlotsOf(freeListObj)) == (numFreeLists())); + assert((formatOf(freeListObj)) == (wordIndexableFormat())); + GIV(freeLists) = firstIndexableField(freeListObj); + GIV(freeListsMask) = 0; + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + if (GIV(freeLists)[i]) { + GIV(freeListsMask) = GIV(freeListsMask) | (1ULL << i); + GIV(freeLists)[i] = (swizzleObj(GIV(freeLists)[i])); + } + } + collapseSegmentsPostSwizzle(); + + /* begin updateFreeLists */ + min = 3; + for (i = min; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + updateListStartingAt(GIV(freeLists)[i]); + } + + /* Large chunks */ + + /* begin freeTreeNodesDo: */ + treeNode = GIV(freeLists)[0]; + if (!treeNode) { + goto l2; + } + cameFrom = -1; + do { + assert((bytesInBody(treeNode)) >= ((numFreeLists()) * (allocationUnit()))); + smallChild = longAt((void *)((treeNode + BaseHeaderSize) + (3U << (shiftForWord())))); + largeChild = longAt((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + assert((smallChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), smallChild)))); + assert((largeChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), largeChild)))); + + /* apply if the node has no children, or it has no large children and we're + returning from the small child, or we're returning from the large child. */ + if (((smallChild == 0) + && (largeChild == 0)) + || ((largeChild + ? cameFrom == largeChild + : cameFrom == smallChild))) { + updateListStartingAt(treeNode); + + /* and since we've applied we must move on up */ + cameFrom = treeNode; + treeNode = longAt((void *)((treeNode + BaseHeaderSize) + (2U << (shiftForWord())))); + } + else { + if ((smallChild != 0) + && (cameFrom != smallChild)) { + treeNode = smallChild; + } + else { + assert(largeChild != 0); + treeNode = largeChild; + } + cameFrom = -1; + } + } while(treeNode != 0); + /* end freeTreeNodesDo: */ +l2: + + /* begin computeFreeSpacePostSwizzle */ + GIV(totalFreeOldSpace) = totalFreeListBytes(); + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + startOfFreeOldSpace = GIV(freeOldSpaceStart); + + /* begin initializeOldSpaceFirstFree: */ + limit = GIV(endOfMemory) - (2 * BaseHeaderSize); + if (limit > startOfFreeOldSpace) { + GIV(totalFreeOldSpace) += limit - startOfFreeOldSpace; + freeOldStart = startOfFreeOldSpace; + while ((limit - freeOldStart) >= (0x100000000LL)) { + freeChunk = freeChunkWithBytesat(0x100000000LL, freeOldStart); + freeOldStart += 0x100000000LL; + assert(freeOldStart == (addressAfter(freeChunk))); + } + if (freeOldStart < limit) { + freeChunk = freeChunkWithBytesat(limit - freeOldStart, freeOldStart); + assert((addressAfter(freeChunk)) == limit); + } + } + GIV(endOfMemory) -= 2 * BaseHeaderSize; + GIV(freeOldSpaceStart) = GIV(endOfMemory); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCCheckFreeSpace)) == (GCCheckFreeSpace | GCCheckFreeSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + initializeNewSpaceVariables(); + + /* begin initializeRememberedSet */ + obj = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))); + if (obj == GIV(nilObj)) { + /* begin allocatePinnedSlots: */ + obj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(0x400, (1024U << (shiftForWord())) + (BaseHeaderSize + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (obj) { + assert(isPinned(obj)); + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((obj + BaseHeaderSize) + (0x400 * BytesPerOop)) - 1, addressAfter(obj))); + toDoLimit = ((usqInt)(((obj + BaseHeaderSize) + (0x400 * BytesPerOop)) - 1)); + for (p = (((usqInt)(obj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + + /* begin rememberedSetObj: */ + assert(isOldObject(obj)); + assert((isNonImmediate(GIV(hiddenRootsObj))) + && (!(isForwarded(GIV(hiddenRootsObj))))); + assert(validStorePointerUncheckedArgs(RememberedSetRootIndex, GIV(hiddenRootsObj), obj)); + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord()))))),obj); + } + else { + /* begin setFormatOf:to: */ + assert((((sixtyFourBitIndexableFormat()) >= 0) && ((sixtyFourBitIndexableFormat()) <= (formatMask())))); + byteAtput((void *)(obj + (formatFieldByteOffset())),((byteAt((void *)(obj + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (sixtyFourBitIndexableFormat())); + } + + /* The Spur32to64BitBootstrap failed to set the type of rememberedSetObj to 64-bit indexability. + This is unimportant except for simulation; rememberedSet is declared as sqInt *, but to have + firstIndexableField: below answer a suitable type the format must be wordIndexableFormat. */ + assert((formatOf(obj)) == (wordIndexableFormat())); + assert(isPinned(obj)); + GIV(rememberedSet) = firstIndexableField(obj); + GIV(rememberedSetSize) = 0; + + /* begin numSlotsOf: */ + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())); + GIV(rememberedSetLimit) = (((numSlots = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + + /* begin setRememberedSetRedZone */ + fudge = ((((GIV(eden).limit)) - ((GIV(eden).start))) / BytesPerWord) / 0x400; + GIV(rememberedSetRedZone) = ((((GIV(rememberedSetLimit) * 3) / 4) < fudge) ? fudge : ((GIV(rememberedSetLimit) * 3) / 4)); + checkSegments(); + + /* begin biasForGC */ + GIV(biasForGC) = 1; + + /* These defaults should depend on machine size; e.g. too small on a powerful laptop, too big on a Pi. */ + + /* headroom when growing */ + GIV(growHeadroom) = 0x1000000; + + /* free space before shrinking */ + GIV(shrinkThreshold) = 0x2000000; + GIV(heapSizeAtPreviousGC) = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + + /* begin resetAllocationAccountingAfterGC */ + GIV(oldSpaceUsePriorToScavenge) = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + + /* By default GC after scavenge if heap has grown by a third since the last GC */ + GIV(heapGrowthToSizeGCRatio) = 0.333333; +} + + +/* Initialize unscannedEphemerons to use the largest unused new space, + which ever is the larger. We haven't yet scavenged, so eden may be + almost full, but futureSpace is known to be empty. N.B. we *cannot* + use free old space because it may be used to grow the markStack. */ + + /* SpurMemoryManager>>#initializeUnscannedEphemerons */ +static void +initializeUnscannedEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt sizeOfFutureSpace; + sqInt sizeOfUnusedEden; + + sizeOfUnusedEden = (((GIV(eden)).limit)) - GIV(freeStart); + sizeOfFutureSpace = (((GIV(futureSpace)).limit)) - (((GIV(futureSpace)).start)); + if (sizeOfUnusedEden > sizeOfFutureSpace) { + (GIV(unscannedEphemerons).start = GIV(freeStart)); + (GIV(unscannedEphemerons).limit = ((GIV(eden)).limit)); + } + else { + (GIV(unscannedEphemerons).start = ((GIV(futureSpace)).start)); + (GIV(unscannedEphemerons).limit = ((GIV(futureSpace)).limit)); + } + (GIV(unscannedEphemerons).top = ((GIV(unscannedEphemerons).start)) - BytesPerOop); + assert(numUnscannedEphemerons() == 0); +} + + +/* print free chunks in freeTree in order. */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#inOrderPrintFreeTree:printList: */ +void +inOrderPrintFreeTreeprintList(sqInt freeChunk, sqInt printNextList) +{ + sqInt next; + + if ((next = longAt((void *)((freeChunk + BaseHeaderSize) + (3U << (shiftForWord())))))) { + inOrderPrintFreeTreeprintList(next, printNextList); + } + printFreeChunkprintAsTreeNode(freeChunk, 1); + if (printNextList) { + next = freeChunk; + while (((next = longAt((void *)((next + BaseHeaderSize) + (0U << (shiftForWord())))))) != 0) { + /* begin tab */ + printChar('\t'); + printFreeChunkprintAsTreeNode(next, 0); + } + } + if ((next = longAt((void *)((freeChunk + BaseHeaderSize) + (4U << (shiftForWord())))))) { + inOrderPrintFreeTreeprintList(next, printNextList); + } +} + + +/* in an effort to fix a compiler bug with two-way become post r3427 */ +/* Do become in place by swapping object contents. */ + + /* SpurMemoryManager>>#inPlaceBecome:and:copyHashFlag: */ +static NoDbgRegParms NeverInline void +inPlaceBecomeandcopyHashFlag(sqInt obj1, sqInt obj2, sqInt copyHashFlag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqLong hashBits; + sqLong header1; + sqLong header2; + sqInt i; + usqInt numSlots; + sqInt o1HasYoung; + sqInt o2HasYoung; + sqLong remembered; + sqInt temp1; + sqInt temp2; + sqInt toDoLimit; + + assert((numSlotsOf(obj1)) == (numSlotsOf(obj2))); + assert(((rawHashBitsOf(obj1)) == 0) + || ((classOrNilAtIndex(rawHashBitsOf(obj1))) != obj1)); + assert(((rawHashBitsOf(obj2)) == 0) + || ((classOrNilAtIndex(rawHashBitsOf(obj2))) != obj2)); + + /* swap headers, but swapping headers swaps remembered bits and hashes; + remembered bits must be unswapped and hashes may be unswapped if + copyHash is false. */ + + /* begin cleverSwapHeaders:and:copyHashFlag: */ + header1 = long64At((void *)(obj1)); + header2 = long64At((void *)(obj2)); + remembered = (header1 ^ header2) & (1U << (rememberedBitShift())); + if (remembered) { + header1 = header1 ^ remembered; + header2 = header2 ^ remembered; + } + + /* swapping headers swaps hash; if not copyHashFlag then unswap hash */ + if (!copyHashFlag) { + hashBits = (header1 ^ header2) & 0x3FFFFF00000000LL /* identityHashFullWordMask */; + if (hashBits) { + header1 = header1 ^ hashBits; + header2 = header2 ^ hashBits; + } + } + long64Atput((void *)(obj1),header2); + long64Atput((void *)(obj2),header1); + o1HasYoung = (o2HasYoung = 0); + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(obj1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(obj1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + temp1 = longAt((void *)((obj1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + temp2 = longAt((void *)((obj2 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(obj1)) + && (!(isForwarded(obj1)))); + assert(validStorePointerUncheckedArgs(i, obj1, temp2)); + longAtput((void *)((obj1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),temp2); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(obj2)) + && (!(isForwarded(obj2)))); + assert(validStorePointerUncheckedArgs(i, obj2, temp1)); + longAtput((void *)((obj2 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),temp1); + if (/* isYoung: */ + ((!(temp2 & (tagMask())))) + && (oopisLessThan(temp2, GIV(oldSpaceStart)))) { + o1HasYoung = 1; + } + if (/* isYoung: */ + ((!(temp1 & (tagMask())))) + && (oopisLessThan(temp1, GIV(oldSpaceStart)))) { + o2HasYoung = 1; + } + } + if (o1HasYoung + && ((/* begin isOldObject: */ + assert(isNonImmediate(obj1)), + oopisGreaterThanOrEqualTo(obj1, GIV(oldSpaceStart))))) { + fmt = (byteAt((void *)(obj1 + (formatFieldByteOffset())))) & (formatMask()); + if (!(/* isPureBitsFormat: */ + (fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat())))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj1 + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj1); + } + } + } + if (o2HasYoung + && ((/* begin isOldObject: */ + assert(isNonImmediate(obj2)), + oopisGreaterThanOrEqualTo(obj2, GIV(oldSpaceStart))))) { + fmt = (byteAt((void *)(obj2 + (formatFieldByteOffset())))) & (formatMask()); + if (!(/* isPureBitsFormat: */ + (fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat())))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj2 + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj2); + } + } + } +} + + +/* Answer the number of slots in a class. For example the instanceSizeOf: + ClassPoint is 2, for the x & y slots. The instance size of non-pointer + classes is 0. */ + + /* SpurMemoryManager>>#instanceSizeOf: */ +sqInt +instanceSizeOf(sqInt classObj) +{ + assert(addressCouldBeClassObj(classObj)); + return (((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); +} + + /* SpurMemoryManager>>#instantiateClass: */ +static NoDbgRegParms sqInt +instantiateClass(sqInt classObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt err; + int hash; + sqInt instSpec; + sqInt newObj; + usqInt newObjUsqInt; + usqInt numBytes; + sqInt numSlots; + usqInt p; + usqInt toDoLimit; + + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + if (!(/* isFixedSizePointerFormat: */ + (instSpec <= (nonIndexablePointerFormat())) + || (instSpec == (ephemeronFormat())))) { + return null; + } + + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + return null; + } + numSlots = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if ((((usqInt)(numSlots)) >> 56) > 0) { + newObj = null; + goto l1; + } + newObjUsqInt = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObjUsqInt = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, instSpec, classIndex); + goto l1; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObjUsqInt),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObjUsqInt),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObjUsqInt % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newObj = newObjUsqInt; + /* end allocateSlots:format:classIndex: */ +l1: + if (newObj) { + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),GIV(nilObj)); + } + } + return newObj; +} + + /* SpurMemoryManager>>#instSpecOfClassFormat: */ +sqInt +instSpecOfClassFormat(sqInt classFormat) +{ + return (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); +} + + +/* This field in a class's format inst var corresponds to the 5-bit format + field stored in every object header + */ + + /* SpurMemoryManager>>#instSpecOfClass: */ +static NoDbgRegParms sqInt +instSpecOfClass(sqInt classPointer) +{ + return (((usqInt)((((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); +} + + /* SpurMemoryManager>>#invalidCompactClassError: */ +static NoDbgRegParms void +invalidCompactClassError(const char *className) +{ + printf("\nClass %s does not have the required class index\n", className); + exit(-1); +} + + +/* Answer true if this is an indexable object with pointer elements, e.g., an + array + */ + + /* SpurMemoryManager>>#isArray: */ +sqInt +isArray(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())); +} + + +/* Answer if objOop should be included in an allObjects...Do: enumeration. + This is for assert-checking only. */ + + /* SpurMemoryManager>>#isBridgeOrEnumerableObjectNoAssert: */ +static NoDbgRegParms int +isBridgeOrEnumerableObjectNoAssert(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + return (classIndex >= (isForwardedObjectClassIndexPun()) + ? classIndex < (GIV(numClassTablePages) * (1U << (classTableMajorIndexShift()))) + : classIndex == (segmentBridgePun())); +} + + /* SpurMemoryManager>>#isCharacterObject: */ +int +isCharacterObject(sqInt oop) +{ + return ((oop & (characterTag())) != 0); +} + + /* SpurMemoryManager>>#isCharacterValue: */ +int +isCharacterValue(sqInt anInteger) +{ + return ((anInteger >= 0) && (anInteger <= (0x3FFFFFFF))); +} + + /* SpurMemoryManager>>#isCompiledMethodFormat: */ +static NoDbgRegParms int +isCompiledMethodFormat(sqInt format) +{ + return format >= (firstCompiledMethodFormat()); +} + + +/* Answer whether the argument header has compiled method format */ + + /* SpurMemoryManager>>#isCompiledMethodHeader: */ +static NoDbgRegParms int +isCompiledMethodHeader(sqInt objHeader) +{ + return ((((usqInt)(objHeader)) >> (formatShift())) & (formatMask())) >= (firstCompiledMethodFormat()); +} + + +/* Answer whether the argument object is of compiled method format */ + + /* SpurMemoryManager>>#isCompiledMethod: */ +int +isCompiledMethod(sqInt objOop) +{ + return ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()); +} + + /* SpurMemoryManager>>#isContextHeader: */ +static NoDbgRegParms int +isContextHeader(sqInt aHeader) +{ + return (aHeader & (classIndexMask())) == ClassMethodContextCompactIndex; +} + + /* SpurMemoryManager>>#isContextNonImm: */ +static NoDbgRegParms int +isContextNonImm(sqInt oop) +{ + return ((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex; +} + + /* SpurMemoryManager>>#isContext: */ +static NoDbgRegParms sqInt +isContext(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex); +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. */ + + /* SpurMemoryManager>>#isCopiedIntoSegment: */ +static NoDbgRegParms int +isCopiedIntoSegment(sqInt anObjectInTheHeap) +{ + return ((byteAt((void *)(anObjectInTheHeap + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0; +} + + /* SpurMemoryManager>>#isEmptyObjStack: */ +static NoDbgRegParms sqInt +isEmptyObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (objStack == GIV(nilObj)) { + return 1; + } + eassert(isValidObjStack(objStack)); + return (0 == (longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))))) + && (0 == (longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))); +} + + +/* Answer if objOop should be included in an allObjects...Do: enumeration. + This is for assert-checking only. */ + + /* SpurMemoryManager>>#isEnumerableObjectNoAssert: */ +static NoDbgRegParms sqInt +isEnumerableObjectNoAssert(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + return (classIndex >= (isForwardedObjectClassIndexPun())) + && (classIndex < (GIV(numClassTablePages) * (1U << (classTableMajorIndexShift())))); +} + + /* SpurMemoryManager>>#isEphemeronFormat: */ +static NoDbgRegParms int +isEphemeronFormat(sqInt format) +{ + return format == (ephemeronFormat()); +} + + /* SpurMemoryManager>>#isEphemeron: */ +static NoDbgRegParms int +isEphemeron(sqInt objOop) +{ + sqInt format; + + assert(isNonImmediate(objOop)); + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isEphemeronFormat: */ + return format == (ephemeronFormat()); +} + + /* SpurMemoryManager>>#isFixedSizePointerFormat: */ +sqInt +isFixedSizePointerFormat(sqInt format) +{ + return (format <= (nonIndexablePointerFormat())) + || (format == (ephemeronFormat())); +} + + /* SpurMemoryManager>>#isForwardedClassTag: */ +static NoDbgRegParms int +isForwardedClassTag(sqInt classIndex) +{ + return classIndex == (isForwardedObjectClassIndexPun()); +} + + +/* Answer if objOop is that if a forwarder. Take advantage of + isForwardedObjectClassIndexPun being a power of two to generate a more + efficient test than the straight-forward + (self classIndexOf: objOop) = self isForwardedObjectClassIndexPun + at the cost of this being ambiguous with free chunks. So either never + apply this to free chunks + or guard with (self isFreeObject: foo) not. So far the idiom has been to + guard with isFreeObject: */ +/* self assert: (self isFreeObject: objOop) not. */ + + /* SpurMemoryManager>>#isForwarded: */ +int +isForwarded(sqInt objOop) +{ + return (!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))); +} + + /* SpurMemoryManager>>#isFreeObject: */ +static NoDbgRegParms int +isFreeObject(sqInt objOop) +{ + return ((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun()); +} + + /* SpurMemoryManager>>#isFreeOop: */ +static NoDbgRegParms sqInt +isFreeOop(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())); +} + + /* SpurMemoryManager>>#isImmediate: */ +int +isImmediate(sqInt oop) +{ + return ((oop & (tagMask())) != 0); +} + + /* SpurMemoryManager>>#isInClassTable: */ +static NoDbgRegParms sqInt +isInClassTable(sqInt objOop) +{ + int hash; + + hash = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()); + return (hash != 0) + && ((classAtIndex(hash)) == objOop); +} + + /* SpurMemoryManager>>#isIndexable: */ +sqInt +isIndexable(sqInt objOop) +{ + sqInt fmt; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + return /* isIndexableFormat: */ + (fmt >= (arrayFormat())) + && ((fmt <= (weakArrayFormat())) + || (fmt >= (sixtyFourBitIndexableFormat()))); +} + + /* SpurMemoryManager>>#isInEden: */ +static NoDbgRegParms sqInt +isInEden(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return oopisGreaterThanOrEqualToandLessThan(objOop, ((GIV(eden)).start), GIV(freeStart)); +} + + /* SpurMemoryManager>>#isInFutureSpace: */ +static NoDbgRegParms sqInt +isInFutureSpace(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return oopisGreaterThanOrEqualToandLessThan(address, ((GIV(futureSpace)).start), GIV(futureSurvivorStart)); +} + + +/* Answer if the given address is in ST object memory. */ + + /* SpurMemoryManager>>#isInMemory: */ +sqInt +isInMemory(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt addressUsqInt; + sqInt i; + + if (/* isInNewSpace: */ + (oopisLessThan(address, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(address, GIV(newSpaceStart)))) { + return (oopisGreaterThanOrEqualToandLessThan(address, ((GIV(eden)).start), GIV(freeStart))) + || ((oopisGreaterThanOrEqualToandLessThan(address, ((GIV(pastSpace)).start), GIV(pastSpaceStart))) + || ((GIV(gcPhaseInProgress) == ScavengeInProgress) + && (oopisGreaterThanOrEqualToandLessThan(address, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))))); + } + addressUsqInt = address; + + /* begin isInSegments: */ + for (i = 0; i < GIV(numSegments); i += 1) { + if (addressUsqInt < (((GIV(segments)[i]).segStart))) { + return 0; + } + if (addressUsqInt < ((((GIV(segments)[i]).segSize)) + (((GIV(segments)[i]).segStart)))) { + return 1; + } + } + return 0; +} + + /* SpurMemoryManager>>#isInNewSpace: */ +static NoDbgRegParms sqInt +isInNewSpace(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (oopisLessThan(objOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(objOop, GIV(newSpaceStart))); +} + + /* SpurMemoryManager>>#isInOldSpace: */ +sqInt +isInOldSpace(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return oopisGreaterThanOrEqualToandLessThan(address, GIV(oldSpaceStart), GIV(endOfMemory)); +} + + /* SpurMemoryManager>>#isInPastSpace: */ +static NoDbgRegParms sqInt +isInPastSpace(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return oopisGreaterThanOrEqualToandLessThan(address, ((GIV(pastSpace)).start), GIV(pastSpaceStart)); +} + + /* SpurMemoryManager>>#isLargeFreeObject: */ +static NoDbgRegParms int +isLargeFreeObject(sqInt objOop) +{ + return (bytesInBody(objOop)) >= 0x200 /* (numFreeLists * #allocationUnit) */; +} + + +/* Answer if the argument contains only indexable 64-bit double words (no + oops). See comment in formatOf: + */ + + /* SpurMemoryManager>>#isLong64s: */ +sqInt +isLong64s(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) == (sixtyFourBitIndexableFormat())); +} + + /* SpurMemoryManager>>#isMarked: */ +int +isMarked(sqInt objOop) +{ + return ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0; +} + + +/* Answer if objOop is an ephemeron that may or may not have been fired. + Unfired ephemerons have ephemeronFormat as their format. Fired ephemerons + have their format set to nonIndexablePointerFormat, so to disambiguate, + check the class is an ephemeron class in this case. */ + + /* SpurMemoryManager>>#isMaybeFiredEphemeron: */ +static NoDbgRegParms sqInt +isMaybeFiredEphemeron(sqInt objOop) +{ + sqInt format; + + if (((objOop & (tagMask())) != 0)) { + return 0; + } + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + return (format == (ephemeronFormat())) + || ((format == (nonIndexablePointerFormat())) + && ((instSpecOfClass(fetchClassOfNonImm(objOop))) == (ephemeronFormat()))); +} + + /* SpurMemoryManager>>#isNonImmediate: */ +int +isNonImmediate(sqInt oop) +{ + return (!(oop & (tagMask()))); +} + + /* SpurMemoryManager>>#isObjEphemeron: */ +static NoDbgRegParms int +isObjEphemeron(sqInt objOop) +{ + sqInt format; + + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isEphemeronFormat: */ + return format == (ephemeronFormat()); +} + + /* SpurMemoryManager>>#isObjImmutable: */ +static NoDbgRegParms sqInt +isObjImmutable(sqInt anOop) +{ + return +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(anOop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ; +} + + +/* Answer if obj is old. Require that obj is non-immediate. */ + + /* SpurMemoryManager>>#isOldObject: */ +static NoDbgRegParms int +isOldObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(isNonImmediate(objOop)); + return oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart)); +} + + +/* Answer whether the oop is an object of compiled method format */ + + /* SpurMemoryManager>>#isOopCompiledMethod: */ +sqInt +isOopCompiledMethod(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())); +} + + /* SpurMemoryManager>>#isOopForwarded: */ +sqInt +isOopForwarded(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))); +} + + /* SpurMemoryManager>>#isOopImmutable: */ +sqInt +isOopImmutable(sqInt oop) +{ + return (((oop & (tagMask())) != 0)) + || (((((usqInt)((byteAt((void *)(oop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0); +} + + /* SpurMemoryManager>>#isOopMutable: */ +sqInt +isOopMutable(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (!(((((usqInt)((byteAt((void *)(oop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0)); +} + + /* SpurMemoryManager>>#isPinned: */ +int +isPinned(sqInt objOop) +{ + return ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0; +} + + +/* Answer if the argument has only fields that can hold oops. See comment in + formatOf: + */ + + /* SpurMemoryManager>>#isPointers: */ +sqInt +isPointers(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */); +} + + +/* the inverse of isAnyPointerFormat: */ + + /* SpurMemoryManager>>#isPureBitsFormat: */ +static NoDbgRegParms sqInt +isPureBitsFormat(sqInt format) +{ + return (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat())); +} + + +/* Answer if the argument contains only indexable words (no oops). See + comment in formatOf: + */ + + /* SpurMemoryManager>>#isPureBitsNonImm: */ +static NoDbgRegParms sqInt +isPureBitsNonImm(sqInt objOop) +{ + sqInt format; + + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isPureBitsFormat: */ + return (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat())); +} + + /* SpurMemoryManager>>#isRemembered: */ +static NoDbgRegParms int +isRemembered(sqInt objOop) +{ + return ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0; +} + + +/* Maybe this should be in SpurSegmentManager only */ + + /* SpurMemoryManager>>#isSegmentBridge: */ +static NoDbgRegParms int +isSegmentBridge(sqInt objOop) +{ + return ((longAt((void *)(objOop))) & (classIndexMask())) == (segmentBridgePun()); +} + + /* SpurMemoryManager>>#isSemaphoreOop: */ +static NoDbgRegParms sqInt +isSemaphoreOop(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((!(anOop & (tagMask())))) + && (((longAt((void *)(anOop))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))); +} + + +/* Answer if the argument contains only indexable 16-bit half words (no + oops). See comment in formatOf: + */ + + /* SpurMemoryManager>>#isShorts: */ +sqInt +isShorts(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstShortFormat())) && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= ((firstByteFormat()) - 1)))); +} + + +/* This version is private to SpurMemoryManager (for asserts, etc). It does + not take advantage of the power-of-two optimization in isForwarded:. */ + + /* SpurMemoryManager>>#isUnambiguouslyForwarder: */ +static NoDbgRegParms int +isUnambiguouslyForwarder(sqInt objOop) +{ + return ((longAt((void *)(objOop))) & (classIndexMask())) == (isForwardedObjectClassIndexPun()); +} + + +/* For debugging using printOopsSuchThat: */ + + /* SpurMemoryManager>>#isUnmarked: */ +int +isUnmarked(sqInt objOop) +{ + return !(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0); +} + + /* SpurMemoryManager>>#isValidClassTag: */ +sqInt +isValidClassTag(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOrNil; + sqInt classTablePage; + sqInt fieldIndex; + + assert(((classIndex >= 0) && (classIndex <= ((1U << (classIndexFieldWidth())) - 1)))); + if (((classIndex >= ((tagMask()) + 1)) && (classIndex <= (lastClassIndexPun())))) { + return 0; + } + + /* begin classOrNilAtIndex: */ + assert((classIndex <= (tagMask())) + || (classIndex >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classOrNil = GIV(nilObj); + goto l1; + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + classOrNil = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classOrNilAtIndex: */ +l1: + return (classOrNil != GIV(nilObj)) + && (((long32At((void *)(classOrNil + 4))) & (identityHashHalfWordMask())) == classIndex); +} + + +/* Answer if the obj stack at objStackRootIndex is valid. */ + + /* SpurMemoryManager>>#isValidObjStackAt: */ +static NoDbgRegParms sqInt +isValidObjStackAt(sqInt objStackRootIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt stackOrNil; + + stackOrNil = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord())))))); + return (stackOrNil == GIV(nilObj)) + || (isValidObjStackPagemyIndexfirstPage(stackOrNil, objStackRootIndex, 1)); +} + + +/* Just check the page itself. */ + + /* SpurMemoryManager>>#isValidObjStackPage:myIndex: */ +static NoDbgRegParms sqInt +isValidObjStackPagemyIndex(sqInt objStackPage, sqInt myx) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!(((longAt((void *)(objStackPage))) & (classIndexMask())) == (sixtyFourBitLongsClassIndexPun()))) { + GIV(objStackInvalidBecause) = "wrong class index"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (!(((byteAt((void *)(objStackPage + (formatFieldByteOffset())))) & (formatMask())) == (sixtyFourBitIndexableFormat()))) { + GIV(objStackInvalidBecause) = "wrong format"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (!((numSlotsOfAny(objStackPage)) == ObjStackPageSlots)) { + GIV(objStackInvalidBecause) = "wrong num slots"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (!(myx == (longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord()))))))))) { + GIV(objStackInvalidBecause) = "wrong myx"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (GIV(marking) + && (!(((byteAt((void *)(objStackPage + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) { + GIV(objStackInvalidBecause) = "marking but page is unmarked"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + return 1; +} + + +/* Answer if the obj stack at stackRootIndex is valid. */ + + /* SpurMemoryManager>>#isValidObjStackPage:myIndex:firstPage: */ +static NoDbgRegParms sqInt +isValidObjStackPagemyIndexfirstPage(sqInt objStackPage, sqInt myx, sqInt isFirstPage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt freeOrNextPage; + sqInt index; + char *ns; + sqInt page; + + if (!(isValidObjStackPagemyIndex(objStackPage, myx))) { + return 0; + } + freeOrNextPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + while (freeOrNextPage != 0) { + if (!isFirstPage) { + GIV(objStackInvalidBecause) = "free page on other than first page"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (freeOrNextPage == (longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))) { + GIV(objStackInvalidBecause) = "free page = next page"; + GIV(invalidObjStackPage) = freeOrNextPage; + return 0; + } + if (!(isValidObjStackPagemyIndex(freeOrNextPage, myx))) { + /* begin stretch:cat: */ + ns = malloc(((strlen(GIV(objStackInvalidBecause))) + (14)) + 2); + strcpy(ns, GIV(objStackInvalidBecause)); + GIV(objStackInvalidBecause) = ((char *) (strcat(ns, ", on next page"))); + return 0; + } + page = longAt((void *)((freeOrNextPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + if ((page == freeOrNextPage) + || (page == objStackPage)) { + GIV(objStackInvalidBecause) = "circularity in free page list"; + GIV(invalidObjStackPage) = page; + return 0; + } + freeOrNextPage = page; + } + if (isFirstPage) { + if (!(((myx >= (1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift())))) && (myx <= (((1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift()))) + 8 /* hiddenRootSlots */) - 1))))) { + GIV(objStackInvalidBecause) = "myx out of range"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (!((longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(myx) << (shiftForWord()))))))) == objStackPage)) { + GIV(objStackInvalidBecause) = "firstPage is not root"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + } + index = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + if (!(((index >= 0) && (index <= ObjStackLimit)))) { + GIV(objStackInvalidBecause) = "bad topx"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + freeOrNextPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + if (!freeOrNextPage) { + return 1; + } + if (freeOrNextPage == objStackPage) { + GIV(objStackInvalidBecause) = "circularity in objStack page list"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + return isValidObjStackPagemyIndexfirstPage(freeOrNextPage, myx, 0); +} + + +/* Answer if the obj stack at objStackRootIndex is valid. */ + + /* SpurMemoryManager>>#isValidObjStack: */ +static NoDbgRegParms sqInt +isValidObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!((addressCouldBeObj(objStack)) + && ((numSlotsOfAny(objStack)) == ObjStackPageSlots))) { + GIV(objStackInvalidBecause) = "first page not obj or wrong size"; + GIV(invalidObjStackPage) = objStack; + return 0; + } + return isValidObjStackPagemyIndexfirstPage(objStack, longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))), 1); +} + + /* SpurMemoryManager>>#isWeakNonImm: */ +static NoDbgRegParms int +isWeakNonImm(sqInt objOop) +{ + sqInt format; + + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isWeakFormat: */ + return format == (weakArrayFormat()); +} + + +/* Answer if the argument has only weak fields that can hold oops. See + comment in formatOf: + */ + + /* SpurMemoryManager>>#isWeak: */ +sqInt +isWeak(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (isWeakNonImm(oop)); +} + + +/* Answer if the contains only indexable words or bytes (no oops). See + comment in formatOf: + */ +/* Note: Excludes CompiledMethods. */ + + /* SpurMemoryManager>>#isWordsOrBytesNonImm: */ +static NoDbgRegParms sqInt +isWordsOrBytesNonImm(sqInt objOop) +{ + sqInt format; + + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isPureBitsFormat: */ + return (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat())); +} + + +/* Answer if the contains only indexable words or bytes (no oops). See + comment in formatOf: + */ +/* Note: Excludes CompiledMethods. */ + + /* SpurMemoryManager>>#isWordsOrBytes: */ +sqInt +isWordsOrBytes(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (isWordsOrBytesNonImm(oop)); +} + + +/* Answer if the argument contains only indexable 16-bit half words or 32-bit + indexable words (no oops). + See comment in formatOf: */ + + /* SpurMemoryManager>>#isWordsOrShorts: */ +sqInt +isWordsOrShorts(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= ((firstByteFormat()) - 1)))); +} + + +/* Answer if the argument contains only indexable words (no oops). See + comment in formatOf: + */ + + /* SpurMemoryManager>>#isWords: */ +sqInt +isWords(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1)))); +} + + +/* Answer if obj is young. Require that obj is non-immediate. */ + + /* SpurMemoryManager>>#isYoungObject: */ +int +isYoungObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(isNonImmediate(objOop)); + return oopisLessThan(objOop, GIV(oldSpaceStart)); +} + + +/* Answer if oop is young. */ + + /* SpurMemoryManager>>#isYoung: */ +sqInt +isYoung(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((!(oop & (tagMask())))) + && (oopisLessThan(oop, GIV(oldSpaceStart))); +} + + +/* Answer if oop is an instance of the given class. If the class has a + (non-zero) compactClassIndex use that to speed up the check. N.B. Inlining + should result in classOop not being accessed if oop's compact class index + and compactClassIndex are non-zero. */ + + /* SpurMemoryManager>>#is:instanceOf:compactClassIndex: */ +static NoDbgRegParms sqInt +isinstanceOfcompactClassIndex(sqInt oop, sqInt classOop, sqInt compactClassIndex) +{ + sqInt ccIndex; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + if (compactClassIndex) { + return compactClassIndex == ccIndex; + } + else { + return classOop == (classAtIndex(ccIndex)); + } +} + + +/* useful for debugging... */ + + /* SpurMemoryManager>>#is:onObjStack: */ +sqInt +isonObjStack(sqInt oop, sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt nextPage; + + if (objStack == GIV(nilObj)) { + return 0; + } + assert((numSlotsOfAny(objStack)) == ObjStackPageSlots); + + /* There are four fixed slots in an obj stack, and a Topx of 0 indicates empty, so + if there were 6 slots in an oop stack, full would be 2, and the last 0-rel index is 5. */ + index = (longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) + ObjStackNextx; + while (index >= ObjStackFixedSlots) { + if (oop == (longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))))) { + return 1; + } + index -= 1; + } + nextPage = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + if (nextPage) { + if (isonObjStack(oop, nextPage)) { + return 1; + } + } + return 0; +} + + +/* Answer the object the ephemeron guards. This is its first element. */ + + /* SpurMemoryManager>>#keyOfEphemeron: */ +static NoDbgRegParms sqInt +keyOfEphemeron(sqInt objOop) +{ + assert((isNonImmediate(objOop)) + && (isObjEphemeron(objOop))); + return longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); +} + + +/* Answer the object the ephemeron guards. This is its first element. */ + + /* SpurMemoryManager>>#keyOfMaybeFiredEphemeron: */ +static NoDbgRegParms sqInt +keyOfMaybeFiredEphemeron(sqInt objOop) +{ + assert(isMaybeFiredEphemeron(objOop)); + return longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); +} + + /* SpurMemoryManager>>#knownClassAtIndex: */ +static NoDbgRegParms sqInt +knownClassAtIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(((classIndex >= 1) && (classIndex <= (classTablePageSize())))); + return longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(classIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#lastPointerFormat */ +static sqInt +lastPointerFormat(void) +{ + return 5; +} + + +/* Answer the byte offset of the last pointer field of the given object. + Works with CompiledMethods, as well as ordinary objects. + Does not examine the stack pointer of contexts to be sure to swizzle + the nils that fill contexts on snapshot. + It is invariant that on image load no object contains a forwarding + pointer, and the image contains no forwarders (see class comment). */ + + /* SpurMemoryManager>>#lastPointerOfWhileSwizzling: */ +static NoDbgRegParms sqInt +lastPointerOfWhileSwizzling(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt header; + sqInt headerSqInt; + usqInt numSlots; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt != (forwardedFormat())); + if (fmt <= 5 /* lastPointerFormat */) { + return ((((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1) * BytesPerOop) + BaseHeaderSize; + } + if (fmt < (firstCompiledMethodFormat())) { + return 0; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + return (((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) - 1) * BytesPerOop) + BaseHeaderSize; +} + + +/* Answer the byte offset of the last pointer field of the given object. + Works with CompiledMethods, as well as ordinary objects. */ + + /* SpurMemoryManager>>#lastPointerOf: */ +static NoDbgRegParms sqInt +lastPointerOf(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt fmt; + sqInt header; + sqInt headerSqInt; + usqInt numSlots; + sqInt sp; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt != (forwardedFormat())); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + return (((CtxtTempFrameStart - 1) + contextSize) * BytesPerOop) + BaseHeaderSize; + } + return ((((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1) * BytesPerOop) + BaseHeaderSize; + } + if (fmt < (firstCompiledMethodFormat())) { + return 0; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + return (((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) - 1) * BytesPerOop) + BaseHeaderSize; +} + + /* SpurMemoryManager>>#leakCheckFullGC */ +int +leakCheckFullGC(void) +{ + return ((checkForLeaks & GCModeFull) != 0); +} + + /* SpurMemoryManager>>#leakCheckNewSpaceGC */ +int +leakCheckNewSpaceGC(void) +{ + return ((checkForLeaks & GCModeNewSpace) != 0); +} + + +/* for the message send breakpoint; selectors can be immediates. */ + + /* SpurMemoryManager>>#lengthOfMaybeImmediate: */ +static NoDbgRegParms sqInt +lengthOfMaybeImmediate(sqInt oop) +{ + sqInt fmt; + usqInt numSlots; + usqInt numSlotsUsqInt; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin lengthOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + +/* Answer the number of indexable units in the given object. + For a CompiledMethod, the size of the method header (in bytes) should + be subtracted from the result. */ + + /* SpurMemoryManager>>#lengthOf: */ +static NoDbgRegParms sqInt +lengthOf(sqInt objOop) +{ + sqInt fmt; + usqInt numSlots; + usqInt numSlotsUsqInt; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + /* SpurMemoryManager>>#literalCountOfMethodHeader: */ +usqInt +literalCountOfMethodHeader(sqInt header) +{ + assert((((header) & 7) == 1)); + return ((header >> 3)) & AlternateHeaderNumLiteralsMask; +} + + /* SpurMemoryManager>>#literalCountOf: */ +usqInt +literalCountOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + return ((header >> 3)) & AlternateHeaderNumLiteralsMask; +} + + +/* This primitive is called from Smalltalk as... + loadSegmentFrom: aWordArray outPointers: anArray. */ +/* This primitive will load a binary image segment created by + primitiveStoreImageSegment. It expects the outPointer array to be of the + proper size, and the wordArray to be well formed. + It will return as its value the original array of roots, and the + segmentWordArray will become an + array of the loaded objects. If this primitive should fail, the + segmentWordArray will, sadly, have + been reduced to an unrecognizable and unusable jumble. But what more could + you have done + with it anyway? + + The primitive, if it succeeds, also becomes the segmentWordArray into the + array of loaded objects. + This allows fixing up of loaded objects directly, without nextObject, + which Spur doesn't support. */ + + /* SpurMemoryManager>>#loadImageSegmentFrom:outPointers: */ +static NoDbgRegParms NeverInline sqInt +loadImageSegmentFromoutPointers(sqInt segmentWordArray, sqInt outPointerArray) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt classOop; + sqInt classRef; + sqInt clone; + sqInt err; + sqInt errorCode; + sqInt errorCodeSqInt; + sqInt fillIdx; + sqInt followingWord; + usqInt followingWordAddress; + usqInt followingWordAddressUsqInt; + sqInt followingWordSqInt; + int hash; + sqInt i; + sqInt loadedObjectsArray; + usqInt mappedOop; + sqInt numLoadedObjects; + usqInt numOutPointers; + sqInt numSegObjs; + usqInt numSlots; + sqInt numSlotsSqInt; + sqInt objOop; + sqInt oldClone; + sqInt oop; + usqInt oopUsqInt; + sqInt scanClassTable; + usqInt segmentLimit; + sqInt segmentStart; + sqInt segVersion; + sqInt toDoLimit; + + /* begin numSlotsOf: */ + assert((classIndexOf(segmentWordArray)) > (isForwardedObjectClassIndexPun())); + segmentLimit = (((numSlots = byteAt((void *)(segmentWordArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(segmentWordArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if ((/* objectBytesForSlots: */ + (segmentLimit + ? ((segmentLimit << (shiftForWord()))) + ((segmentLimit >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize)) < (8 /* allocationUnit */ + BaseHeaderSize)) { + return PrimErrBadArgument; + } + + /* version info + one object header + Verify format. If the format is wrong, word-swap (since ImageSegment data are 32-bit longs). + If it is still wrong, undo the damage and fail. */ + segVersion = longAt((void *)(segmentWordArray + BaseHeaderSize)); + if (!(((((segVersion & 0xFFFFFF) | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */)) { + reverseBytesIn32BitWordsIn(segmentWordArray); + segVersion = longAt((void *)(segmentWordArray + BaseHeaderSize)); + if (!(((((segVersion & 0xFFFFFF) | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */)) { + reverseBytesIn32BitWordsIn(segmentWordArray); + return PrimErrBadArgument; + } + } + + /* low 3 bytes */ + segmentStart = (segmentWordArray + BaseHeaderSize) + 8 /* allocationUnit */; + segmentLimit = ((segmentLimit * BytesPerOop) + segmentWordArray) + BaseHeaderSize; + + /* Notionally reverse the Byte type objects if the data is from opposite endian machine. + Test top byte. $d on the Mac or $s on the PC. Rest of word is equal. If Spur is ever + ported to big-endian machines then the segment may have to be byte/word swapped, + but so far it only runs on little-endian machines, so for now just fail if endianness is wrong. */ + if (((((usqInt)(segVersion)) >> 24) & 0xFF) != ((((usqInt)((imageSegmentVersion()))) >> 24) & 0xFF)) { + return PrimErrBadArgument; + } + + /* Reverse the byte-type objects once + Avoid having to remember by arranging that there are no young outPointers if segment is in old space. */ + + /* begin isOldObject: */ + assert(isNonImmediate(segmentWordArray)); + if (oopisGreaterThanOrEqualTo(segmentWordArray, GIV(oldSpaceStart))) { + /* begin ensureNoNewObjectsIn: */ + hash = 0; + scanClassTable = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(outPointerArray)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(outPointerArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointerArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + oop = longAt((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isYoung: */ + ((!(oop & (tagMask())))) + && (oopisLessThan(oop, GIV(oldSpaceStart)))) { + clone = cloneInOldSpaceforPinning(oop, 0); + if (!clone) { + errorCode = PrimErrNoMemory; + goto l6; + } + if ((((hash = (long32At((void *)(oop + 4))) & (identityHashHalfWordMask()))) != 0) + && ((classOrNilAtIndex(hash)) == oop)) { + scanClassTable = 1; + } + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(oop),((longAt((void *)(oop))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(oop),(longAt((void *)(oop))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(oop)); + assert(!(isOopForwarded(clone))); + + /* begin isOldObject: */ + assert(isNonImmediate(oop)); + if (oopisGreaterThanOrEqualTo(oop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(clone & (tagMask())))) + && (oopisLessThan(clone, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(oop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord()))),clone); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(oop + (numSlotsFieldByteOffset())),1); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(outPointerArray)) + && (!(isForwarded(outPointerArray)))); + assert(validStorePointerUncheckedArgs(i, outPointerArray, clone)); + longAtput((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),clone); + } + } + if (scanClassTable) { + postBecomeScanClassTable(BecamePointerObjectFlag); + } + errorCode = 0; + /* end ensureNoNewObjectsIn: */ +l6: + if (errorCode) { + return errorCode; + } + } + + /* scan through mapping oops and validating class references. Defer entering any + class objects into the class table and/or pinning objects until a second pass. */ + + /* begin mapOopsAndValidateClassRefsFrom:to:outPointers: */ + /* begin numSlotsOf: */ + assert((classIndexOf(outPointerArray)) > (isForwardedObjectClassIndexPun())); + numOutPointers = (((numSlots = byteAt((void *)(outPointerArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointerArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numSegObjs = 0; + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(segmentStart + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? segmentStart + BaseHeaderSize + : segmentStart); + while (oopisLessThan(objOop, segmentLimit)) { + numSegObjs += 1; + + /* No object in the segment should be marked. If is is something is wrong. */ + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + errorCode = PrimErrInappropriate; + goto l8; + } + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + + /* validate the class ref, but don't update it until any internal classes have been added to the class table. */ + if (((classIndex & TopHashBit) != 0)) { + classIndex -= TopHashBit; + if (classIndex >= numOutPointers) { + errorCode = PrimErrBadIndex; + goto l8; + } + mappedOop = longAt((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)(classIndex) << (shiftForWord())))))); + hash = (long32At((void *)(mappedOop + 4))) & (identityHashHalfWordMask()); + if (!((hash == 0) + || ((hash > (lastClassIndexPun())) + && ((classOrNilAtIndex(hash)) == mappedOop)))) { + errorCode = PrimErrInappropriate; + goto l8; + } + } + else { + if (((oopUsqInt = ((classIndex - (firstClassIndexPun())) * 8 /* allocationUnit */) + segmentStart)) >= segmentLimit) { + errorCode = PrimErrBadIndex; + goto l8; + } + if ((long32At((void *)(oopUsqInt + 4))) & (identityHashHalfWordMask())) { + errorCode = PrimErrInappropriate; + goto l8; + } + } + + /* The class is contained within the segment. */ + toDoLimit = (numPointerSlotsOf(objOop)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + oopUsqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(oopUsqInt & (tagMask())))) { + if (((oopUsqInt & TopOopBit) != 0)) { + if (((oopUsqInt = (oopUsqInt - TopOopBit) / BytesPerOop)) >= numOutPointers) { + errorCode = PrimErrBadIndex; + goto l8; + } + mappedOop = longAt((void *)((outPointerArray + BaseHeaderSize) + ((oopUsqInt << (shiftForWord()))))); + } + else { + if (oopUsqInt & 7 /* (allocationUnit - 1) */) { + errorCode = PrimErrInappropriate; + goto l8; + } + if (((mappedOop = oopUsqInt + segmentStart)) >= segmentLimit) { + errorCode = PrimErrBadIndex; + goto l8; + } + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(i, objOop, mappedOop)); + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),mappedOop); + } + } + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, segmentLimit)) { + objOop = segmentLimit; + goto l7; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l7:; + } + errorCode = -numSegObjs; + /* end mapOopsAndValidateClassRefsFrom:to:outPointers: */ +l8: + if (errorCode > 0) { + return errorCode; + } + numLoadedObjects = -errorCode; + loadedObjectsArray = noInlineAllocateSlotsformatclassIndex(numLoadedObjects, arrayFormat(), ClassArrayCompactIndex); + if (!loadedObjectsArray) { + growOldSpaceByAtLeast((BaseHeaderSize + BaseHeaderSize) + (numLoadedObjects * BytesPerOop)); + loadedObjectsArray = noInlineAllocateSlotsformatclassIndex(numLoadedObjects, arrayFormat(), ClassArrayCompactIndex); + if (!loadedObjectsArray) { + return PrimErrNoMemory; + } + } + + /* Scan for classes contained in the segment, entering them into the class table. + Classes are at the front, after the root array and have the remembered bit set. */ + + /* begin enterClassesIntoClassTableFrom:to: */ + objOop = objectAfter(objectStartingAt(segmentStart)); + while ((oopisLessThan(objOop, segmentLimit)) + && (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0)) { + setIsRememberedOfto(objOop, 0); + if ((errorCodeSqInt = enterIntoClassTable(objOop))) { + oop = objOop; + objOop = objectAfter(objectStartingAt(segmentStart)); + while (oopisLessThan(objOop, oop)) { + expungeFromClassTable(objOop); + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, segmentLimit)) { + objOop = segmentLimit; + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + errorCode = errorCodeSqInt; + goto l3; + } + + /* begin objectAfter:limit: */ + followingWordAddressUsqInt = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddressUsqInt, segmentLimit)) { + objOop = segmentLimit; + goto l2; + } + followingWordSqInt = longAt((void *)(followingWordAddressUsqInt)); + objOop = ((((usqInt)(followingWordSqInt)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddressUsqInt + BaseHeaderSize + : followingWordAddressUsqInt); + /* end objectAfter:limit: */ +l2:; + } + errorCode = 0; + /* end enterClassesIntoClassTableFrom:to: */ +l3: + if (errorCode) { + return errorCode; + } + + /* Make a final pass, assigning class indices and/or pinning pinned objects and collecting the loaded objects in loadedObjectsArray */ + + /* begin assignClassIndicesAndPinFrom:to:outPointers:filling: */ + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(segmentStart + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? segmentStart + BaseHeaderSize + : segmentStart); + fillIdx = 0; + while (oopisLessThan(objOop, segmentLimit)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(loadedObjectsArray)) + && (!(isForwarded(loadedObjectsArray)))); + assert(validStorePointerUncheckedArgs(fillIdx, loadedObjectsArray, objOop)); + longAtput((void *)((loadedObjectsArray + BaseHeaderSize) + ((((usqInt)(fillIdx) << (shiftForWord()))))),objOop); + fillIdx += 1; + + /* In the segment, class indices are offset indexes into the segment data, + or into outPointers. See mapOopsFrom:to:outPointers:outHashes: and + newOutPointer:at:in:hashes:. */ + classRef = (longAt((void *)(objOop))) & (classIndexMask()); + classOop = (((classRef & TopHashBit) != 0) + ? longAt((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)((classRef - TopHashBit)) << (shiftForWord())))))) + : ((classRef - (firstClassIndexPun())) * 8 /* allocationUnit */) + segmentStart); + classIndex = (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask()); + if (!classIndex) { + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classOop)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classOop) + ? ((err = enterIntoClassTable(classOop)) + ? -err + : (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + errorCode = -classIndex; + goto l5; + } + } + assert((classIndex > (lastClassIndexPun())) + && ((classOrNilAtIndex(classIndex)) == classOop)); + + /* begin setClassIndexOf:to: */ + assert(((classIndex >= 0) && (classIndex <= (classIndexMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(classIndexMask()))) + classIndex); + if ((/* isInNewSpace: */ + (oopisLessThan(objOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(objOop, GIV(newSpaceStart)))) + && (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)) { + oldClone = cloneInOldSpaceforPinning(objOop, 1); + if (oldClone) { + setIsPinnedOfto(oldClone, 1); + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(objOop),(longAt((void *)(objOop))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(objOop)); + assert(!(isOopForwarded(oldClone))); + + /* begin isOldObject: */ + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(oldClone & (tagMask())))) + && (oopisLessThan(oldClone, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),oldClone); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),1); + } + } + } + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, segmentLimit)) { + objOop = segmentLimit; + goto l4; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l4:; + } + errorCode = 0; + /* end assignClassIndicesAndPinFrom:to:outPointers:filling: */ +l5: + if (errorCode) { + return errorCode; + } + + /* Evaporate the container, leaving the newly loaded objects in place. */ + if ((byteAt((void *)(segmentWordArray + (numSlotsFieldByteOffset())))) == (numSlotsMask())) { + if (oopisLessThan(segmentWordArray, GIV(oldSpaceStart))) { + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(segmentWordArray - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56)))); + + /* rawNumSlotsOf:put: */ + byteAtput((void *)(segmentWordArray + (numSlotsFieldByteOffset())),0); + } + else { + numSlotsSqInt = 8 /* allocationUnit */ / BytesPerOop; + + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(segmentWordArray - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56))) + numSlotsSqInt); + } + } + else { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(segmentWordArray + (numSlotsFieldByteOffset())),8 /* allocationUnit */ / BytesPerOop); + } + + /* N.B. setting the overflow slots to 1 creates a slimbridge in eden, so we also need to delete the overflow slot count in the segment itself + Finally forward the segmentWordArray to the loadedObjectsArray */ + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(segmentWordArray),((longAt((void *)(segmentWordArray))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(segmentWordArray),(longAt((void *)(segmentWordArray))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(segmentWordArray)); + assert(!(isOopForwarded(loadedObjectsArray))); + + /* begin isOldObject: */ + assert(isNonImmediate(segmentWordArray)); + if (oopisGreaterThanOrEqualTo(segmentWordArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(loadedObjectsArray & (tagMask())))) + && (oopisLessThan(loadedObjectsArray, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(segmentWordArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(segmentWordArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((segmentWordArray + BaseHeaderSize) + (0U << (shiftForWord()))),loadedObjectsArray); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(segmentWordArray + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(segmentWordArray + (numSlotsFieldByteOffset())),1); + } + runLeakCheckerFor(GCModeImageSegment); + + /* Answer the first object in the segment because this is what primitiveLoadImageSegment tests for, + even though the real result is the segmentWordArray forwarded to the loadedObjectsArray. */ + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(segmentStart + (numSlotsFieldByteOffset()))); + return (numSlots == (numSlotsMask()) + ? segmentStart + BaseHeaderSize + : segmentStart); +} + + +/* Scan the heap printing the oops of any and all objects that are instances + of aClassOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#longPrintInstancesOf: */ +void +longPrintInstancesOf(sqInt aClassOop) +{ + int classIndex; + + classIndex = (long32At((void *)(aClassOop + 4))) & (identityHashHalfWordMask()); + if (classIndex != (isFreeObjectClassIndexPun())) { + longPrintInstancesWithClassIndex(classIndex); + } +} + + +/* Scan the heap printing any and all objects whose classIndex equals the + argument. + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#longPrintInstancesWithClassIndex: */ +void +longPrintInstancesWithClassIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + longPrintOop(objOop); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + longPrintOop(objOop); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3:; +} + + +/* Scan the heap long printing the oops of any and all objects that refer to + anOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#longPrintReferencesTo: */ +void +longPrintReferencesTo(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt i; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt prntObj; + sqInt startObject; + + prntObj = 0; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + || (((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) { + if (((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + i = (literalCountOf(objSqInt)) + LiteralStart; + } + else { + if (((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + i = CtxtTempFrameStart + (fetchStackPointerOf(objSqInt)); + } + else { + /* begin numSlotsOf: */ + assert((classIndexOf(objSqInt)) > (isForwardedObjectClassIndexPun())); + i = (((numSlots = byteAt((void *)(objSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + } + } + while (((i -= 1)) >= 0) { + if (anOop == (longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + fprintf(GIV(transcript), + "%p @ %d\n", + ((void *)objSqInt), + ((int) i)); + prntObj = 1; + i = 0; + } + } + if (prntObj) { + prntObj = 0; + longPrintOop(objSqInt); + } + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + /* SpurMemoryManager>>#mapExtraRoots */ +static void +mapExtraRoots(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt oop; + + if (shouldRemapObj(GIV(specialObjectsOop))) { + GIV(validatedIntegerClassFlags) = 0; + GIV(specialObjectsOop) = remapObj(GIV(specialObjectsOop)); + } + assert(GIV(remapBufferCount) == 0); + + /* 1 to: remapBufferCount do: + [:i | | oop | + oop := remapBufferCount at: i. + ((self isImmediate: oop) or: [self isFreeObject: oop]) ifFalse: + [(self shouldRemapObj: oop) ifTrue: + [remapBuffer at: i put: (self remapObj: oop)]]]. */ + for (i = 1; i <= GIV(extraRootCount); i += 1) { + oop = (GIV(extraRoots)[i])[0]; + if (!((((oop & (tagMask())) != 0)) + || (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())))) { + if (shouldRemapObj(oop)) { + (GIV(extraRoots)[i])[0] = (remapObj(oop)); + } + } + } +} + + /* SpurMemoryManager>>#mapMournQueue */ +static NeverInline void +mapMournQueue(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt iSqInt; + sqInt mourner; + sqInt numOnThisPage; + sqInt objStackPage; + sqInt referent; + + /* begin objStack:do: */ + if (GIV(mournQueue) == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(GIV(mournQueue))); + objStackPage = GIV(mournQueue); + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + mourner = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if ((!(mourner & (tagMask())))) { + if ((!((longAt((void *)(mourner))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(mourner)); + referent = longAt((void *)((mourner + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + mourner = referent; + } + if (!(isScavengeSurvivor(mourner))) { + mourner = copyAndForwardMourner(mourner); + } + + /* we could check for change but writes are cheap with write buffers... */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objStackPage)) + && (!(isForwarded(objStackPage)))); + assert(validStorePointerUncheckedArgs(iSqInt, objStackPage, mourner)); + longAtput((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord()))))),mourner); + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l1:; +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. + Now scan, adding out pointers to the outPointersArray; all objects in + arrayOfObjects have had their first fields set to point to their copies in + segmentWordArray. Answer + the outIndex if the scan succeded. Fail if outPointers is too small and + answer -1. + + As established by copyObj:toAddr:startAt:stopAt:savedFirstFields:index:, + the marked bit is set for all objects in the segment + the remembered bit is set for all classes in the segment. + + Class indices should be set as follows (see + assignClassIndicesAndPinFrom:to:outPointers:filling:) - class indices for + classes in the segment */ + + /* SpurMemoryManager>>#mapOopsFrom:to:outPointers:outHashes: */ +static NoDbgRegParms sqInt +mapOopsFromtooutPointersoutHashes(sqInt segStart, sqInt segAddr, sqInt outPointerArray, sqInt savedOutHashes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + int hash; + sqInt heapOop; + sqInt i; + usqInt numSlots; + sqInt numSlotsSqInt; + usqInt numSlotsUsqInt; + sqInt objIndex; + sqInt objOop; + sqInt oop; + sqInt outIndex; + usqInt p; + sqInt segIndex; + sqInt toDoLimit; + usqInt toDoLimitUsqInt; + sqInt valueWord; + sqInt valueWordSqInt; + + /* objIndex is for debugging; it mirrors indices in the sender's arrayOfObjects. */ + outIndex = (objIndex = 0); + /* begin numSlotsOf: */ + assert((classIndexOf(outPointerArray)) > (isForwardedObjectClassIndexPun())); + if (((numSlots = byteAt((void *)(outPointerArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask())) { + numSlotsSqInt = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointerArray - BaseHeaderSize)))) << 8)))))) >> 8; + } + else { + numSlotsSqInt = numSlots; + } + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((outPointerArray + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1, addressAfter(outPointerArray))); + toDoLimitUsqInt = ((usqInt)(((outPointerArray + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1)); + for (p = (((usqInt)(outPointerArray + BaseHeaderSize))); p <= toDoLimitUsqInt; p += 8 /* allocationUnit */) { + longAtput((void *)(p),GIV(nilObj)); + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(segStart + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? segStart + BaseHeaderSize + : segStart); + while (oopisLessThan(objOop, segAddr)) { + heapOop = fetchClassOfNonImm(objOop); + + /* Set the classIndex of the instance. This is a segment offset (segAddr - segStart / allocationUnit) for instances of + classes within the segment, and an outPointer index (index in outPointers + TopHashBit) for classes outside the segment. */ + if ((byteAt((void *)(heapOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + oop = longAt((void *)((heapOop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(oopisGreaterThanOrEqualToandLessThan(oop, segStart, segAddr)); + segIndex = ((oop - segStart) / 8 /* allocationUnit */) + (firstClassIndexPun()); + if (((segIndex & TopHashBit) != 0)) { + return -1; + } + } + else { + hash = (long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask()); + if (/* is:outPointerClassHashFor:in:limit: */ + (((hash & TopHashBit) != 0)) + && (((hash - TopHashBit) <= outIndex) + && (heapOop == (longAt((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)((hash - TopHashBit)) << (shiftForWord())))))))))) { + segIndex = hash; + } + else { + /* begin newOutPointer:at:in:hashes: */ + if (outIndex >= ((/* begin numSlotsOf: */ + assert((classIndexOf(outPointerArray)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(outPointerArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointerArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))) { + /* no room in outPointers; fail */ + outIndex = 0; + goto l2; + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(outIndex, outPointerArray, heapOop)); + assert(isNonImmediate(outPointerArray)); + if (oopisGreaterThanOrEqualTo(outPointerArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(heapOop & (tagMask())))) + && (oopisLessThan(heapOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(outPointerArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(outPointerArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)(outIndex) << (shiftForWord()))))),heapOop); + valueWord = (long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask()); + + /* begin storeLong32:ofObject:withValue: */ + long32Atput((void *)((savedOutHashes + BaseHeaderSize) + ((((usqInt)(outIndex) << 2)))),valueWord); + long32Atput((void *)(heapOop + 4),((((long32At((void *)(heapOop + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + ((outIndex + TopHashBit) & (identityHashHalfWordMask()))); + outIndex += 1; + /* end newOutPointer:at:in:hashes: */ +l2: + if (!outIndex) { + return -1; + } + + /* no room in outPointers; fail */ + segIndex = (long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask()); + } + + /* oop is a new outPointer; allocate its oop */ + assert(((segIndex & TopHashBit) != 0)); + } + + /* oop is a class in the segment; storeImageSegmentInto:outPointers:roots: established offset is within range. + oop is an outPointer; locate or allocate its oop */ + + /* begin setClassIndexOf:to: */ + assert(((segIndex >= 0) && (segIndex <= (classIndexMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(classIndexMask()))) + segIndex); + toDoLimit = (numPointerSlotsOf(objOop)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + heapOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(heapOop & (tagMask())))) { + if ((byteAt((void *)(heapOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + oop = longAt((void *)((heapOop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(oopisGreaterThanOrEqualToandLessThan(oop, segStart, segAddr)); + oop -= segStart; + } + else { + hash = (long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask()); + if (/* is:outPointerClassHashFor:in:limit: */ + (((hash & TopHashBit) != 0)) + && (((hash - TopHashBit) <= outIndex) + && (heapOop == (longAt((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)((hash - TopHashBit)) << (shiftForWord())))))))))) { + oop = ((hash - TopHashBit) * BytesPerOop) + TopOopBit; + } + else { + /* begin newOutPointer:at:in:hashes: */ + if (outIndex >= ((/* begin numSlotsOf: */ + assert((classIndexOf(outPointerArray)) > (isForwardedObjectClassIndexPun())), + (((numSlotsUsqInt = byteAt((void *)(outPointerArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointerArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt)))) { + /* no room in outPointers; fail */ + outIndex = 0; + goto l3; + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(outIndex, outPointerArray, heapOop)); + assert(isNonImmediate(outPointerArray)); + if (oopisGreaterThanOrEqualTo(outPointerArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(heapOop & (tagMask())))) + && (oopisLessThan(heapOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(outPointerArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(outPointerArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)(outIndex) << (shiftForWord()))))),heapOop); + valueWordSqInt = (long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask()); + + /* begin storeLong32:ofObject:withValue: */ + long32Atput((void *)((savedOutHashes + BaseHeaderSize) + ((((usqInt)(outIndex) << 2)))),valueWordSqInt); + long32Atput((void *)(heapOop + 4),((((long32At((void *)(heapOop + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + ((outIndex + TopHashBit) & (identityHashHalfWordMask()))); + outIndex += 1; + /* end newOutPointer:at:in:hashes: */ +l3: + if (!outIndex) { + return -1; + } + + /* no room in outPointers; fail */ + assert((((rawHashBitsOf(heapOop)) & TopHashBit) != 0)); + oop = ((((long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask())) - TopHashBit) * BytesPerOop) + TopOopBit; + } + } + + /* oop is an object in the segment. + oop is an outPointer; locate or allocate its oop */ + + /* storeUncheckedNoAssert:ofAny:withValue: */ + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),oop); + } + } + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, segAddr)) { + objOop = segAddr; + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1: + objIndex += 1; + } + return outIndex; +} + + +/* Ensure the class of the argument is marked, pushing it on the markStack if + not already marked. + And for one-way become, which can create duplicate entries in the class + table, make sure + objOop's classIndex refers to the classObj's actual classIndex. + Note that this is recursive, but the metaclass chain should terminate + quickly. + */ + + /* SpurMemoryManager>>#markAndTraceClassOf: */ +static NoDbgRegParms void +markAndTraceClassOf(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt classObj; + sqInt classTablePage; + sqInt fieldIndex; + sqInt objStack; + int realClassIndex; + + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + + /* begin classOrNilAtIndex: */ + assert((classIndex <= (tagMask())) + || (classIndex >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classObj = GIV(nilObj); + goto l1; + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + classObj = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classOrNilAtIndex: */ +l1: + assert(objCouldBeClassObj(classObj)); + realClassIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + if ((classIndex != realClassIndex) + && (classIndex > (lastClassIndexPun()))) { + /* begin setClassIndexOf:to: */ + assert(((realClassIndex >= 0) && (realClassIndex <= (classIndexMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(classIndexMask()))) + realClassIndex); + } + if (!((byteAt((void *)(classObj + (markBitsByteOffset())))) & (1U << (markedBitByteShift())))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(classObj))); + byteAtput((void *)(classObj + (markBitsByteOffset())),(byteAt((void *)(classObj + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + markAndTraceClassOf(classObj); + objStack = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(classObj)); + if (((classObj & (tagMask())) != 0)) { + assert(objStack == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack)) + ? fetchPointerofObject(ObjStackNextx, objStack) + : objStack)))); + } + else { + assert(!((objStack == GIV(markStack)) + && (isWeakNonImm(classObj)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack != GIV(weaklingStack)) + || (isWeakNonImm(classObj))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(classObj, objStack); + } +} + + +/* An obj stack is a stack of objects stored in a hidden root slot, such + as the markStack or the ephemeronQueue. It is a linked list of + segments, with the hot end at the head of the list. It is a word object. + The stack pointer is in ObjStackTopx and 0 means empty. */ + + /* SpurMemoryManager>>#markAndTraceObjStack:andContents: */ +static NoDbgRegParms void +markAndTraceObjStackandContents(sqInt stackOrNil, sqInt markAndTraceContents) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt field; + sqInt index; + + if (stackOrNil == GIV(nilObj)) { + return; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(stackOrNil))); + byteAtput((void *)(stackOrNil + (markBitsByteOffset())),(byteAt((void *)(stackOrNil + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + field = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + if (field) { + markAndTraceObjStackandContents(field, markAndTraceContents); + } + field = stackOrNil; + while (1) { + field = longAt((void *)((field + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + if (!(field != 0)) break; + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(field))); + byteAtput((void *)(field + (markBitsByteOffset())),(byteAt((void *)(field + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + } + if (!markAndTraceContents) { + return; + } + + /* There are four fixed slots in an obj stack, and a Topx of 0 indicates empty, so + if there were 6 slots in an oop stack, full would be 2, and the last 0-rel index is 5. */ + index = (longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) + ObjStackNextx; + while (index >= ObjStackFixedSlots) { + /* begin followObjField:ofObject: */ + field = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(field)); + if ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + field = fixFollowedFieldofObjectwithInitialValue(index, stackOrNil, field); + } + if (!(((field & (tagMask())) != 0))) { + markAndTrace(field); + } + index -= 1; + } +} + + +/* Mark the argument, and all objects reachable from it, and any remaining + objects on the mark stack. Follow forwarding pointers in the scan. */ +/* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. + The assert for this is in markAndShouldScan: */ + + /* SpurMemoryManager>>#markAndTrace: */ +NeverInline void +markAndTrace(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt field; + sqInt fmt; + sqInt format; + sqInt formatSqInt; + sqInt header; + sqInt headerSqInt; + sqInt index; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt numStrongSlots; + sqInt objOop1; + sqInt objOopSqInt; + sqInt objOopSqIntSqInt; + sqInt objStack; + sqInt objStack1; + sqInt objStackSqInt; + sqInt objToScan; + sqInt scanLargeObject; + sqInt sp; + + /* begin markAndShouldScan: */ + if (((objOop & (tagMask())) != 0)) { + return; + } + + /* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. */ + assert(!(isForwarded(objOop))); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + return; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (/* isPureBitsFormat: */ + (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat()))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + markAndTraceClassOf(objOop); + } + return; + } + + /* avoid pushing non-pointer objects on the markStack. + Avoid tracing classes of non-objects on the heap, e.g. IRC caches, Sista counters. */ + if (format == (weakArrayFormat())) { + objStack = GIV(weaklingStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objOop)); + if (((objOop & (tagMask())) != 0)) { + assert(objStack == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack)) + ? fetchPointerofObject(ObjStackNextx, objStack) + : objStack)))); + } + else { + assert(!((objStack == GIV(markStack)) + && (isWeakNonImm(objOop)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack != GIV(weaklingStack)) + || (isWeakNonImm(objOop))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objOop, objStack); + return; + } + + /* push weaklings on the weakling stack to scan later */ + if ((format == (ephemeronFormat())) + && (activeAndDeferredScan(objOop))) { + return; + } + + /* Now scan the object, and any remaining objects on the mark stack. */ + + /* begin markLoopFrom: */ + numStrongSlots = 0; + objToScan = objOop; + + /* To avoid overflowing the mark stack when we encounter large objects, we + push the obj, then its numStrongSlots, and then index the object from the stack. */ + do { + if (((objToScan & (tagMask())) != 0)) { + scanLargeObject = 1; + } + else { + /* begin numStrongSlotsOfInephemeral: */ + fmt = (byteAt((void *)(objToScan + (formatFieldByteOffset())))) & (formatMask()); + assert((fmt != (ephemeronFormat())) + || ((isImmediate(keyOfEphemeron(objToScan))) + || (isMarked(keyOfEphemeron(objToScan))))); + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(objToScan)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objToScan + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objToScan - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (arrayFormat())) { + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (indexablePointersFormat())) { + if (((longAt((void *)(objToScan))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + setTraceFlagOnContextsFramesPageIfNeeded(objToScan); + + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l3; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objToScan))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l3: + numStrongSlots = CtxtTempFrameStart + contextSize; + goto l4; + } + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (weakArrayFormat())) { + objOopSqIntSqInt = fetchClassOfNonImm(objToScan); + numStrongSlots = (((longAt((void *)((objOopSqIntSqInt + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + goto l4; + } + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (forwardedFormat())) { + numStrongSlots = 1; + goto l4; + } + if (fmt < (firstCompiledMethodFormat())) { + numStrongSlots = 0; + goto l4; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objToScan)); + headerSqInt = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numStrongSlots = numLiterals + LiteralStart; + /* end numStrongSlotsOfInephemeral: */ +l4: + scanLargeObject = numStrongSlots > 64 /* traceImmediatelySlotLimit */; + } + if (scanLargeObject) { + if (((objToScan & (tagMask())) != 0)) { + index = (objToScan >> 3); + objToScan = topOfObjStack(GIV(markStack)); + } + else { + index = numStrongSlots; + markAndTraceClassOf(objToScan); + } + while (index > 0) { + index -= 1; + field = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if ((!(field & (tagMask())))) { + if ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + field = fixFollowedFieldofObjectwithInitialValue(index, objToScan, field); + } + + /* fixFollowedField: is /not/ inlined */ + + /* begin markAndShouldScan: */ + if (((field & (tagMask())) != 0)) { + goto l1; + } + + /* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. */ + assert(!(isForwarded(field))); + if ((byteAt((void *)(field + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + goto l1; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(field))); + byteAtput((void *)(field + (markBitsByteOffset())),(byteAt((void *)(field + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + format = (byteAt((void *)(field + (formatFieldByteOffset())))) & (formatMask()); + if (/* isPureBitsFormat: */ + (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat()))) { + if (((longAt((void *)(field))) & (classIndexMask())) > (lastClassIndexPun())) { + markAndTraceClassOf(field); + } + goto l1; + } + + /* avoid pushing non-pointer objects on the markStack. + Avoid tracing classes of non-objects on the heap, e.g. IRC caches, Sista counters. */ + if (format == (weakArrayFormat())) { + objStack1 = GIV(weaklingStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + goto l1; + } + + /* push weaklings on the weakling stack to scan later */ + if ((format == (ephemeronFormat())) + && (activeAndDeferredScan(field))) { + goto l1; + } + if (index > 0) { + if ((topOfObjStack(GIV(markStack))) != objToScan) { + objStack = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objToScan)); + if (((objToScan & (tagMask())) != 0)) { + assert(objStack == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack)) + ? fetchPointerofObject(ObjStackNextx, objStack) + : objStack)))); + } + else { + assert(!((objStack == GIV(markStack)) + && (isWeakNonImm(objToScan)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack != GIV(weaklingStack)) + || (isWeakNonImm(objToScan))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objToScan, objStack); + } + objOopSqInt = (((usqInt)index << 3) | 1); + objStackSqInt = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objOopSqInt)); + if (((objOopSqInt & (tagMask())) != 0)) { + assert(objStackSqInt == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStackSqInt)) + ? fetchPointerofObject(ObjStackNextx, objStackSqInt) + : objStackSqInt)))); + } + else { + assert(!((objStackSqInt == GIV(markStack)) + && (isWeakNonImm(objOopSqInt)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStackSqInt != GIV(weaklingStack)) + || (isWeakNonImm(objOopSqInt))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objOopSqInt, objStackSqInt); + } + objToScan = field; + index = -1; +l1:; + } + } + if (index >= 0) { + objToScan = popObjStack(GIV(markStack)); + if (objToScan == objOop) { + objToScan = popObjStack(GIV(markStack)); + } + } + } + else { + index = numStrongSlots; + markAndTraceClassOf(objToScan); + while (index > 0) { + index -= 1; + field = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if ((!(field & (tagMask())))) { + if ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + field = fixFollowedFieldofObjectwithInitialValue(index, objToScan, field); + } + + /* fixFollowedField: is /not/ inlined */ + + /* begin markAndShouldScan: */ + if (((field & (tagMask())) != 0)) { + goto l2; + } + + /* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. */ + assert(!(isForwarded(field))); + if ((byteAt((void *)(field + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + goto l2; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(field))); + byteAtput((void *)(field + (markBitsByteOffset())),(byteAt((void *)(field + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + formatSqInt = (byteAt((void *)(field + (formatFieldByteOffset())))) & (formatMask()); + if (/* isPureBitsFormat: */ + (formatSqInt >= (sixtyFourBitIndexableFormat())) + && (formatSqInt < (firstCompiledMethodFormat()))) { + if (((longAt((void *)(field))) & (classIndexMask())) > (lastClassIndexPun())) { + markAndTraceClassOf(field); + } + goto l2; + } + + /* avoid pushing non-pointer objects on the markStack. + Avoid tracing classes of non-objects on the heap, e.g. IRC caches, Sista counters. */ + if (formatSqInt == (weakArrayFormat())) { + objStack1 = GIV(weaklingStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + goto l2; + } + + /* push weaklings on the weakling stack to scan later */ + if ((formatSqInt == (ephemeronFormat())) + && (activeAndDeferredScan(field))) { + goto l2; + } + objStack1 = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + if (((byteAt((void *)(field + (numSlotsFieldByteOffset())))) > 64 /* traceImmediatelySlotLimit */) + && (((numStrongSlots = numStrongSlotsOfInephemeral(field))) > 64 /* traceImmediatelySlotLimit */)) { + objOop1 = (((usqInt)numStrongSlots << 3) | 1); + objStack1 = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objOop1)); + if (((objOop1 & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(objOop1)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(objOop1))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objOop1, objStack1); + } +l2:; + } + } + objToScan = popObjStack(GIV(markStack)); + } + + /* scanning a large object. scan until hitting an unmarked object, then switch to it, if any. + scanning a small object. scan, marking, pushing unmarked referents, then switch to the top of the stack. */ + } while(objToScan); +} + + +/* Go through the unscanned ephemerons, marking the inactive ones, and + removing them from the unscanned ephemerons. Answer if any inactive + ones were found. We cannot fire the ephemerons until all are found to + be active since scan-marking an inactive ephemeron later in the set may + render a previously-observed active ephemeron as inactive. */ + + /* SpurMemoryManager>>#markInactiveEphemerons */ +static sqInt +markInactiveEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt ephemeron; + sqInt field; + sqInt fmt; + sqInt format; + sqInt formatSqInt; + sqInt foundInactive; + sqInt header; + sqInt headerSqInt; + sqInt index; + sqInt key; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt numStrongSlots; + sqInt objOop; + sqInt objOop1; + sqInt objOopSqInt; + sqInt objOopSqIntSqInt; + sqInt objStack; + sqInt objStack1; + sqInt objStackSqInt; + sqInt objToScan; + usqInt ptr; + sqInt scanLargeObject; + sqInt sp; + + foundInactive = 0; + ptr = (GIV(unscannedEphemerons).start); + while (ptr <= ((GIV(unscannedEphemerons).top))) { + objOop = (ephemeron = longAt((void *)(ptr))); + + /* begin followedKeyOfEphemeron: */ + assert((isNonImmediate(objOop)) + && (isEphemeron(objOop))); + + /* begin followOopField:ofObject: */ + key = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isOopForwarded: */ + ((!(key & (tagMask())))) + && ((!((longAt((void *)(key))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + key = fixFollowedFieldofObjectwithInitialValue(0, objOop, key); + } + if ((((key & (tagMask())) != 0)) + || (((byteAt((void *)(key + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) { + foundInactive = 1; + + /* Now remove the inactive ephemeron from the set, and trace it. + Tracing it may add more ephemerons to the set. */ + if (((GIV(unscannedEphemerons).top)) > ptr) { + longAtput((void *)(ptr),longAt((void *)((GIV(unscannedEphemerons).top)))); + } + (GIV(unscannedEphemerons).top = ((GIV(unscannedEphemerons).top)) - BytesPerOop); + + /* To get markAndTrace: to trace the ephemeron we need to clear the mark bit. */ + + /* begin markLoopFrom: */ + numStrongSlots = 0; + objToScan = ephemeron; + + /* To avoid overflowing the mark stack when we encounter large objects, we + push the obj, then its numStrongSlots, and then index the object from the stack. */ + do { + if (((objToScan & (tagMask())) != 0)) { + scanLargeObject = 1; + } + else { + /* begin numStrongSlotsOfInephemeral: */ + fmt = (byteAt((void *)(objToScan + (formatFieldByteOffset())))) & (formatMask()); + assert((fmt != (ephemeronFormat())) + || ((isImmediate(keyOfEphemeron(objToScan))) + || (isMarked(keyOfEphemeron(objToScan))))); + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(objToScan)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objToScan + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objToScan - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (arrayFormat())) { + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (indexablePointersFormat())) { + if (((longAt((void *)(objToScan))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + setTraceFlagOnContextsFramesPageIfNeeded(objToScan); + + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l3; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objToScan))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l3: + numStrongSlots = CtxtTempFrameStart + contextSize; + goto l4; + } + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (weakArrayFormat())) { + objOopSqIntSqInt = fetchClassOfNonImm(objToScan); + numStrongSlots = (((longAt((void *)((objOopSqIntSqInt + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + goto l4; + } + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (forwardedFormat())) { + numStrongSlots = 1; + goto l4; + } + if (fmt < (firstCompiledMethodFormat())) { + numStrongSlots = 0; + goto l4; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objToScan)); + headerSqInt = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numStrongSlots = numLiterals + LiteralStart; + /* end numStrongSlotsOfInephemeral: */ +l4: + scanLargeObject = numStrongSlots > 64 /* traceImmediatelySlotLimit */; + } + if (scanLargeObject) { + if (((objToScan & (tagMask())) != 0)) { + index = (objToScan >> 3); + objToScan = topOfObjStack(GIV(markStack)); + } + else { + index = numStrongSlots; + markAndTraceClassOf(objToScan); + } + while (index > 0) { + index -= 1; + field = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if ((!(field & (tagMask())))) { + if ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + field = fixFollowedFieldofObjectwithInitialValue(index, objToScan, field); + } + + /* fixFollowedField: is /not/ inlined */ + + /* begin markAndShouldScan: */ + if (((field & (tagMask())) != 0)) { + goto l1; + } + + /* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. */ + assert(!(isForwarded(field))); + if ((byteAt((void *)(field + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + goto l1; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(field))); + byteAtput((void *)(field + (markBitsByteOffset())),(byteAt((void *)(field + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + format = (byteAt((void *)(field + (formatFieldByteOffset())))) & (formatMask()); + if (/* isPureBitsFormat: */ + (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat()))) { + if (((longAt((void *)(field))) & (classIndexMask())) > (lastClassIndexPun())) { + markAndTraceClassOf(field); + } + goto l1; + } + + /* avoid pushing non-pointer objects on the markStack. + Avoid tracing classes of non-objects on the heap, e.g. IRC caches, Sista counters. */ + if (format == (weakArrayFormat())) { + objStack1 = GIV(weaklingStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + goto l1; + } + + /* push weaklings on the weakling stack to scan later */ + if ((format == (ephemeronFormat())) + && (activeAndDeferredScan(field))) { + goto l1; + } + if (index > 0) { + if ((topOfObjStack(GIV(markStack))) != objToScan) { + objStack = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objToScan)); + if (((objToScan & (tagMask())) != 0)) { + assert(objStack == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack)) + ? fetchPointerofObject(ObjStackNextx, objStack) + : objStack)))); + } + else { + assert(!((objStack == GIV(markStack)) + && (isWeakNonImm(objToScan)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack != GIV(weaklingStack)) + || (isWeakNonImm(objToScan))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objToScan, objStack); + } + objOopSqInt = (((usqInt)index << 3) | 1); + objStackSqInt = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objOopSqInt)); + if (((objOopSqInt & (tagMask())) != 0)) { + assert(objStackSqInt == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStackSqInt)) + ? fetchPointerofObject(ObjStackNextx, objStackSqInt) + : objStackSqInt)))); + } + else { + assert(!((objStackSqInt == GIV(markStack)) + && (isWeakNonImm(objOopSqInt)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStackSqInt != GIV(weaklingStack)) + || (isWeakNonImm(objOopSqInt))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objOopSqInt, objStackSqInt); + } + objToScan = field; + index = -1; +l1:; + } + } + if (index >= 0) { + objToScan = popObjStack(GIV(markStack)); + if (objToScan == ephemeron) { + objToScan = popObjStack(GIV(markStack)); + } + } + } + else { + index = numStrongSlots; + markAndTraceClassOf(objToScan); + while (index > 0) { + index -= 1; + field = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if ((!(field & (tagMask())))) { + if ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + field = fixFollowedFieldofObjectwithInitialValue(index, objToScan, field); + } + + /* fixFollowedField: is /not/ inlined */ + + /* begin markAndShouldScan: */ + if (((field & (tagMask())) != 0)) { + goto l2; + } + + /* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. */ + assert(!(isForwarded(field))); + if ((byteAt((void *)(field + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + goto l2; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(field))); + byteAtput((void *)(field + (markBitsByteOffset())),(byteAt((void *)(field + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + formatSqInt = (byteAt((void *)(field + (formatFieldByteOffset())))) & (formatMask()); + if (/* isPureBitsFormat: */ + (formatSqInt >= (sixtyFourBitIndexableFormat())) + && (formatSqInt < (firstCompiledMethodFormat()))) { + if (((longAt((void *)(field))) & (classIndexMask())) > (lastClassIndexPun())) { + markAndTraceClassOf(field); + } + goto l2; + } + + /* avoid pushing non-pointer objects on the markStack. + Avoid tracing classes of non-objects on the heap, e.g. IRC caches, Sista counters. */ + if (formatSqInt == (weakArrayFormat())) { + objStack1 = GIV(weaklingStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + goto l2; + } + + /* push weaklings on the weakling stack to scan later */ + if ((formatSqInt == (ephemeronFormat())) + && (activeAndDeferredScan(field))) { + goto l2; + } + objStack1 = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + if (((byteAt((void *)(field + (numSlotsFieldByteOffset())))) > 64 /* traceImmediatelySlotLimit */) + && (((numStrongSlots = numStrongSlotsOfInephemeral(field))) > 64 /* traceImmediatelySlotLimit */)) { + objOop1 = (((usqInt)numStrongSlots << 3) | 1); + objStack1 = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objOop1)); + if (((objOop1 & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(objOop1)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(objOop1))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objOop1, objStack1); + } +l2:; + } + } + objToScan = popObjStack(GIV(markStack)); + } + + /* scanning a large object. scan until hitting an unmarked object, then switch to it, if any. + scanning a small object. scan, marking, pushing unmarked referents, then switch to the top of the stack. */ + } while(objToScan); + } + else { + ptr += BytesPerOop; + } + } + return foundInactive; +} + + +/* for profiling */ +/* Mark all accessible objects. + objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged is true if all + objects are unmarked and/or if unmarked classes shoud be removed from the + class table. */ +/* If the incremental collector is running mark bits may be set; stop it and + clear them if necessary. + */ + + /* SpurMemoryManager>>#markObjects: */ +static NoDbgRegParms NeverInline void +markObjects(sqInt objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt classOrNil; + sqInt classTablePage; + DisownState *disownState; + sqInt format; + sqInt i; + sqInt j; + sqInt objOop; + sqInt oop; + sqInt result; + StackPage *thePage; + sqInt toDoLimit; + CogVMThread *vmThread; + + runLeakCheckerFor(GCModeFull); + + /* begin shutDownGlobalIncrementalGC: */ + if (objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged) { + assert(allObjectsUnmarked()); + } + initializeUnscannedEphemerons(); + + /* begin initializeMarkStack */ + ensureRoomOnObjStackAt(MarkStackRootIndex); + ensureRoomOnObjStackAt(WeaklingStackRootIndex); + GIV(marking) = 1; + + /* begin markAccessibleObjectsAndFireEphemerons */ + assert(GIV(marking)); + assert(validClassTableRootPages()); + assert(allBridgesMarked()); + + /* for debugging markAndTrace: set (MarkStackRecord := OrderedCollection new) + This must come first to enable stack page reclamation. It clears + the trace flags on stack pages and so must precede any marking. + Otherwise it will clear the trace flags of reached pages. */ + + /* begin initStackPageGC */ + if (GIV(stackPage)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + (thePage->trace = StackPageUnreached); + } + + /* begin markAndTraceHiddenRoots */ + markAndTraceObjStackandContents(GIV(markStack), 0); + markAndTraceObjStackandContents(GIV(weaklingStack), 0); + markAndTraceObjStackandContents(GIV(mournQueue), 1); + objOop = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + /* begin freeListsObj */ + assert((firstIndexableField(oldSpaceObjectAfter(GIV(trueObj)))) == GIV(freeLists)); + objOop = oldSpaceObjectAfter(GIV(trueObj)); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* begin isWeakNonImm: */ + format = (byteAt((void *)(GIV(classTableFirstPage) + (formatFieldByteOffset())))) & (formatMask()); + if (format == (weakArrayFormat())) { + markAndTrace(GIV(hiddenRootsObj)); + goto l1; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(GIV(hiddenRootsObj)))); + byteAtput((void *)(GIV(hiddenRootsObj) + (markBitsByteOffset())),(byteAt((void *)(GIV(hiddenRootsObj) + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + markAndTrace(GIV(classTableFirstPage)); + for (i = 1; i < GIV(numClassTablePages); i += 1) { + objOop = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + } + /* end markAndTraceHiddenRoots */ +l1: + + /* begin markAndTraceExtraRoots */ + assert(GIV(remapBufferCount) == 0); + + /* 1 to: remapBufferCount do: + [:i| + oop := remapBuffer at: i. + ((self isImmediate: oop) or: [self isFreeObject: oop]) ifFalse: + [self markAndTrace: oop]]. */ + for (i = 1; i <= GIV(extraRootCount); i += 1) { + oop = (GIV(extraRoots)[i])[0]; + if (!((((oop & (tagMask())) != 0)) + || (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())))) { + markAndTrace(oop); + } + } + assert(validClassTableRootPages()); + + /* begin markAndTraceInterpreterOops: */ + markAndTraceStackPages(1); + markAndTraceTraceLog(); + markAndTracePrimTraceLog(); + markAndTrace(GIV(specialObjectsOop)); + if (!(((GIV(newMethod) & (tagMask())) != 0))) { + markAndTrace(GIV(newMethod)); + } + + /* begin traceProfileState */ + /* begin followForwardingPointersInProfileState */ + /* begin profileStateDoUpdating: */ + if (GIV(profileProcess)) { + if ((result = ((!((longAt((void *)(GIV(profileProcess)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileProcess)) + : 0))) { + GIV(profileProcess) = result; + } + } + if (GIV(profileMethod)) { + if ((result = ((!((longAt((void *)(GIV(profileMethod)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileMethod)) + : 0))) { + GIV(profileMethod) = result; + } + } + if (GIV(profileSemaphore)) { + if ((result = ((!((longAt((void *)(GIV(profileSemaphore)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileSemaphore)) + : 0))) { + GIV(profileSemaphore) = result; + } + } + + /* begin profileStateDo: */ + if (GIV(profileProcess)) { + markAndTrace(GIV(profileProcess)); + } + if (GIV(profileMethod)) { + markAndTrace(GIV(profileMethod)); + } + if (GIV(profileSemaphore)) { + markAndTrace(GIV(profileSemaphore)); + } +# if LRPCheck + /* The longRunningPrimitiveCheckMethod (LRPCM) is sampled in an interrupt. Be very careful with it. + If longRunningPrimitiveCheckSequenceNumber (LRPCSN) = statCheckForEvents then LRPCM has + been recenty sampled, but it must be newMethod and we don't need to trace it twice. If LRPCSN + ~= statCheckForEvents then LRPCM must be some extant object and needs to be traced. */ + sqLowLevelMFence(); + if ((GIV(longRunningPrimitiveCheckMethod) != null) + && (GIV(longRunningPrimitiveCheckSequenceNumber) != GIV(statCheckForEvents))) { + if ((!((longAt((void *)(GIV(longRunningPrimitiveCheckMethod)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + GIV(longRunningPrimitiveCheckMethod) = followForwarded(GIV(longRunningPrimitiveCheckMethod)); + } + markAndTrace(GIV(longRunningPrimitiveCheckMethod)); + } + if (GIV(longRunningPrimitiveCheckSemaphore)) { + if ((!((longAt((void *)(GIV(longRunningPrimitiveCheckSemaphore)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + GIV(longRunningPrimitiveCheckSemaphore) = followForwarded(GIV(longRunningPrimitiveCheckSemaphore)); + } + markAndTrace(GIV(longRunningPrimitiveCheckSemaphore)); + } +# endif // LRPCheck + + if (GIV(tempOop)) { + markAndTrace(GIV(tempOop)); + } + if (GIV(tempOop2)) { + markAndTrace(GIV(tempOop2)); + } + + /* V3 memory manager support */ + for (j = 1; j <= GIV(remapBufferCount); j += 1) { + oop = GIV(remapBuffer)[j]; + if (!(((oop & (tagMask())) != 0))) { + markAndTrace(oop); + } + } + + /* Per-thread state; trace each thread's own newMethods, including those in outer calls if a thread is nested in an outer disownVM:/ownVM:. */ + toDoLimit = getNumThreads(); + for (i = 1; i <= toDoLimit; i += 1) { + /* begin vmThreadAt: */ + assert(((i >= 0) && (i <= GIV(numThreads)))); + vmThread = (i > 0 + ? GIV(threads)[i] + : ((CogVMThread *) null)); + if ((atomic_load((&((vmThread->state))))) > CTMInitializing) { + if ((vmThread->boundProcess)) { + if ((!((longAt((void *)((vmThread->boundProcess)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + (vmThread->boundProcess = followForwarded((vmThread->boundProcess))); + } + markAndTrace((vmThread->boundProcess)); + } + disownState = ((DisownState *) vmThread); + do { + if ((disownState->newMethodOrNull)) { + if ((!((longAt((void *)((vmThread->newMethodOrNull)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + (vmThread->newMethodOrNull = followForwarded((vmThread->newMethodOrNull))); + } + markAndTrace((disownState->newMethodOrNull)); + } + } while(((disownState = (disownState->nestedDisownStates)))); + } + } + assert(validObjStacks()); + markWeaklingsAndMarkAndFireEphemerons(); + assert(validObjStacks()); + + /* begin expungeDuplicateAndUnmarkedClasses: */ + for (i = 1; i < GIV(numClassTablePages); i += 1) { + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNil = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + classIndex = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + assert((classOrNil == GIV(nilObj)) + || (addressCouldBeClassObj(classOrNil))); + + /* only remove a class if it is at a duplicate entry or it is unmarked and we're expunging unmarked classes. */ + if (classOrNil == GIV(nilObj)) { + if (classIndex < GIV(classTableIndex)) { + GIV(classTableIndex) = classIndex; + } + } + else { + if ((objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged + && (!(((byteAt((void *)(classOrNil + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) + || (((long32At((void *)(classOrNil + 4))) & (identityHashHalfWordMask())) != classIndex)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(classTablePage)) + && (!(isForwarded(classTablePage)))); + assert(validStorePointerUncheckedArgs(j, classTablePage, GIV(nilObj))); + longAtput((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord()))))),GIV(nilObj)); + + /* but if it is marked, it should still be in the table at its correct index. */ + assert((objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged + && (!(isMarked(classOrNil)))) + || ((classAtIndex(rawHashBitsOf(classOrNil))) == classOrNil)); + + /* If the removed class is before the classTableIndex, set the + classTableIndex to point to the empty slot so as to reuse it asap. */ + if (classIndex < GIV(classTableIndex)) { + GIV(classTableIndex) = classIndex; + } + } + } + } + } + + /* Avoid expunging the puns by not scanning the 0th page. + classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + assert(GIV(classTableIndex) >= (1U << (classTableMajorIndexShift()))); + nilUnmarkedWeaklingSlots(); + GIV(marking) = 0; +} + + +/* After the initial scan-mark is complete ephemerons can be processed. + Weaklings have accumulated on the weaklingStack, but more may be + uncovered during ephemeron processing. So trace the strong slots + of the weaklings, and as ephemerons are processed ensure any newly + reached weaklings are also traced. */ + + /* SpurMemoryManager>>#markWeaklingsAndMarkAndFireEphemerons */ +static void +markWeaklingsAndMarkAndFireEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ephemeron; + sqInt ephemeronSqInt; + sqInt field; + sqInt i; + sqInt iSqInt; + sqInt key; + sqInt keySqInt; + sqInt numOnThisPage; + sqInt numToEnumerate; + sqInt numToEnumerateOnThisPage; + sqInt numTracedWeaklings; + sqInt numUnscannedBeforeMarkAndFire; + sqInt objStackPage; + usqInt p; + sqInt size; + sqInt toDoLimit; + sqInt topIndex; + sqInt weakling; + + numTracedWeaklings = 0; + while (1) { + assert(unscannedEphemeronsContainsOnlyEphemerons()); + markAndTraceUntracedReachableStackPages(); + markAndTraceMachineCodeOfMarkedMethods(); + + /* Make sure all reached weaklings have their strong slots traced before firing ephemerons... */ + do { + /* begin markAndTraceWeaklingsFrom: */ + /* begin objStack:from:do: */ + eassert(isValidObjStack(GIV(weaklingStack))); + size = longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + objStackPage = longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + while (objStackPage != 0) { + size += ObjStackLimit; + assert((fetchPointerofObject(ObjStackTopx, objStackPage)) == ObjStackLimit); + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + numToEnumerate = size - numTracedWeaklings; + objStackPage = GIV(weaklingStack); + while (numToEnumerate > 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + numToEnumerateOnThisPage = ((numToEnumerate < numOnThisPage) ? numToEnumerate : numOnThisPage); + topIndex = (numOnThisPage + ObjStackFixedSlots) - 1; + for (iSqInt = topIndex; iSqInt >= ((topIndex - numToEnumerateOnThisPage) + 1); iSqInt += -1) { + assert(isWeak(fetchPointerofObject(iSqInt, objStackPage))); + weakling = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + assert(!((isForwarded(weakling)))); + markAndTraceClassOf(weakling); + + /* N.B. generateToByDoLimitExpression:negative:on: guards against (unsigned)0 - 1 going +ve */ + toDoLimit = (numStrongSlotsOfWeakling(weakling)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + /* begin followOopField:ofObject: */ + field = longAt((void *)((weakling + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(field & (tagMask())))) + && ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + field = fixFollowedFieldofObjectwithInitialValue(i, weakling, field); + } + if (!((((field & (tagMask())) != 0)) + || (((byteAt((void *)(field + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) { + markAndTrace(field); + } + } + } + numToEnumerate -= numToEnumerateOnThisPage; + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + numTracedWeaklings = size; + } while((sizeOfObjStack(GIV(weaklingStack))) > numTracedWeaklings); + if (((GIV(unscannedEphemerons).top)) < ((GIV(unscannedEphemerons).start))) { + markAndTraceUntracedReachableStackPages(); + markAndTraceMachineCodeOfMarkedMethods(); + freeUntracedStackPages(); + freeUnmarkedMachineCode(); + if (((GIV(unscannedEphemerons).top)) < ((GIV(unscannedEphemerons).start))) { + return; + } + } + assert(unscannedEphemeronsContainsOnlyEphemerons()); + while (markInactiveEphemerons()) { + } + assert(unscannedEphemeronsContainsOnlyActiveEphemerons()); + numUnscannedBeforeMarkAndFire = ((((sqInt) (((GIV(unscannedEphemerons).top)) - ((GIV(unscannedEphemerons).start))))) / BytesPerOop) + 1; + + /* begin fireAllUnscannedEphemerons */ + key = 0; + assert(allUnscannedEphemeronsAreActive()); + for (p = ((GIV(unscannedEphemerons).start)); p <= ((GIV(unscannedEphemerons).top)); p += BytesPerOop) { + ephemeron = longAt((void *)(p)); + if ((isEphemeron(ephemeron)) + && ((isNonImmediate((/* begin keyOfEphemeron: */ + assert((isNonImmediate(ephemeron)) + && (isObjEphemeron(ephemeron))), + (key = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))))))) + && (!(((byteAt((void *)(key + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)))) { + fireEphemeron(ephemeron); + } + } + + /* begin markAllUnscannedEphemerons */ + assert(allUnscannedEphemeronsAreActive()); + while (((GIV(unscannedEphemerons).top)) >= ((GIV(unscannedEphemerons).start))) { + ephemeronSqInt = longAt((void *)((GIV(unscannedEphemerons).start))); + longAtput((void *)((GIV(unscannedEphemerons).start)),longAt((void *)((GIV(unscannedEphemerons).top)))); + (GIV(unscannedEphemerons).top = ((GIV(unscannedEphemerons).top)) - BytesPerOop); + + /* begin followedKeyOfMaybeFiredEphemeron: */ + assert(isMaybeFiredEphemeron(ephemeronSqInt)); + keySqInt = longAt((void *)((ephemeronSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isOopForwarded: */ + ((!(keySqInt & (tagMask())))) + && ((!((longAt((void *)(keySqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + keySqInt = fixFollowedFieldofObjectwithInitialValue(0, ephemeronSqInt, keySqInt); + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(ephemeronSqInt))); + byteAtput((void *)(ephemeronSqInt + (markBitsByteOffset())),(byteAt((void *)(ephemeronSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + markAndTrace(keySqInt); + markAndTrace(ephemeronSqInt); + } + if (numUnscannedBeforeMarkAndFire == (((((sqInt) (((GIV(unscannedEphemerons).top)) - ((GIV(unscannedEphemerons).start))))) / BytesPerOop) + 1)) { + initializeUnscannedEphemerons(); + } + } +} + + /* SpurMemoryManager>>#maxIdentityHash */ +sqInt +maxIdentityHash(void) +{ + return identityHashHalfWordMask(); +} + + +/* Almost entirely arbitrary, but we dont want 1Mb bitmaps allocated in eden. + But this choice means no check for numSlots > maxSlotsForNewSpaceAlloc + for non-variable allocations. */ + + /* SpurMemoryManager>>#maxSlotsForNewSpaceAlloc */ +sqInt +maxSlotsForNewSpaceAlloc(void) +{ + return (1U << (fixedFieldsFieldWidth())) - 1; +} + + +/* Answers if the code is installed in a class instantiating objects with the + format. Used in primitive + generation to make a quick path based on where the method is installed. + This method cannot + be used as a guarantee as there can be false positive, it's just a + heuristic. Tries to interpret the last literal of the method as a behavior + (more than 3 fields, 3rd field a Smi). + If it can be interpreted as a behavior, answers if instSpec matches the + format, else answers false. */ + + /* SpurMemoryManager>>#maybeMethodClassOf:seemsToBeInstantiating: */ +sqInt +maybeMethodClassOfseemsToBeInstantiating(sqInt methodObj, sqInt format) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt instSpec; + sqInt literal; + sqInt maybeClassObj; + sqInt maybeFormat; + sqInt offset; + + /* begin methodClassOf: */ + offset = (literalCountOf(methodObj)) - 1; + literal = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = fixFollowedFieldofObjectwithInitialValue(offset + LiteralStart, methodObj, literal); + } + maybeClassObj = ((literal != GIV(nilObj)) + && (/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + ? (assert((numSlotsOf(literal)) > ValueIndex), + followFieldofObject(ValueIndex, literal)) + : GIV(nilObj)); + if (!(((byteAt((void *)(maybeClassObj + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) { + return 0; + } + if (!((numSlotsOfAny(maybeClassObj)) > InstanceSpecificationIndex)) { + return 0; + } + maybeFormat = longAt((void *)((maybeClassObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord())))))); + if (!((((maybeFormat) & 7) == 1))) { + return 0; + } + instSpec = (((usqInt)(((maybeFormat >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + return instSpec == format; +} + + +/* Answer the minimum number of additional slots to allocate in an object to + always be able to shorten it. + This is enough slots to allocate a minimum-sized object. */ + + /* SpurMemoryManager>>#minSlotsForShortening */ +sqInt +minSlotsForShortening(void) +{ + return 16 /* (allocationUnit * 2) */ / BytesPerOop; +} + + +/* N.B. may be called from machine code on the Smalltalk stack so this should + not call deeply. + */ + + /* SpurMemoryManager>>#newHashBitsOf: */ +static NoDbgRegParms usqInt +newHashBitsOf(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt hash; + + /* begin newObjectHash */ + assert(!((GIV(lastHash) == 0))); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 2)); + GIV(lastHash) = GIV(lastHash) ^ ((((GIV(lastHash) << 7))) & (identityHashHalfWordMask())); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 3)); + hash = GIV(lastHash); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(objOop + 4),((((long32At((void *)(objOop + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + return hash & (identityHashHalfWordMask()); +} + + /* SpurMemoryManager>>#newSpaceIsEmpty */ +static sqInt +newSpaceIsEmpty(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (GIV(freeStart) == (((GIV(eden)).start))) + && (GIV(pastSpaceStart) == (((GIV(pastSpace)).start))); +} + + /* SpurMemoryManager>>#nilObject */ +sqInt +nilObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(nilObj); +} + + +/* Nil the unmarked slots in the weaklings on the + weakling stack, finalizing those that lost references. + Finally, empty the weaklingStack. */ +/* for profiling */ + + /* SpurMemoryManager>>#nilUnmarkedWeaklingSlots */ +static NeverInline void +nilUnmarkedWeaklingSlots(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anyUnmarked; + sqInt i; + sqInt iSqInt; + sqInt nextNextPage; + sqInt nextPage; + sqInt numOnThisPage; + usqInt numSlots; + sqInt numToEnumerate; + sqInt numToEnumerateOnThisPage; + sqInt objStackPage; + sqInt referent; + sqInt size; + sqInt toDoLimit; + sqInt topIndex; + sqInt weakling; + + eassert(allOldMarkedWeakObjectsOnWeaklingStack()); + if (GIV(weaklingStack) == GIV(nilObj)) { + return; + } + + /* begin objStack:from:do: */ + eassert(isValidObjStack(GIV(weaklingStack))); + size = longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + objStackPage = longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + while (objStackPage != 0) { + size += ObjStackLimit; + assert((fetchPointerofObject(ObjStackTopx, objStackPage)) == ObjStackLimit); + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + numToEnumerate = size; + objStackPage = GIV(weaklingStack); + while (numToEnumerate > 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + numToEnumerateOnThisPage = ((numToEnumerate < numOnThisPage) ? numToEnumerate : numOnThisPage); + topIndex = (numOnThisPage + ObjStackFixedSlots) - 1; + for (i = topIndex; i >= ((topIndex - numToEnumerateOnThisPage) + 1); i += -1) { + assert(isWeak(fetchPointerofObject(i, objStackPage))); + weakling = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin nilUnmarkedWeaklingSlotsIn: */ + anyUnmarked = 0; + assert(allStrongSlotsOfWeaklingAreMarked(weakling)); + + /* N.B. generateToByDoLimitExpression:negative:on: guards against (unsigned)0 - 1 going +ve */ + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(weakling)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(weakling + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(weakling - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (iSqInt = (numStrongSlotsOfWeakling(weakling)); iSqInt <= toDoLimit; iSqInt += 1) { + referent = longAt((void *)((weakling + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + if (((longAt((void *)(referent))) & (classIndexMask())) == (isForwardedObjectClassIndexPun())) { + referent = fixFollowedFieldofObjectwithInitialValue(iSqInt, weakling, referent); + } + if (!((((referent & (tagMask())) != 0)) + || (((byteAt((void *)(referent + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(weakling)) + && (!(isForwarded(weakling)))); + assert(validStorePointerUncheckedArgs(iSqInt, weakling, GIV(nilObj))); + longAtput((void *)((weakling + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord()))))),GIV(nilObj)); + anyUnmarked = 1; + } + } + } + if (anyUnmarked) { + /* fireFinalization: could grow the mournQueue and if so, + additional pages must be marked to avoid being GC'ed. */ + assert(GIV(marking)); + + /* begin fireFinalization: */ + if (((imageHeaderFlags & 64) != 0)) { + queueMourner(weakling); + } + + /* begin signalFinalization: */ + forceInterruptCheck(); + GIV(pendingFinalizationSignals) += 1; + } + } + numToEnumerate -= numToEnumerateOnThisPage; + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + + /* begin emptyObjStack: */ + if (GIV(weaklingStack) == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(GIV(weaklingStack))); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(GIV(weaklingStack))) == (wordIndexableFormat())); + longAtput((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))),0); + nextPage = longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + while (nextPage != 0) { + nextNextPage = longAt((void *)((nextPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(nextPage)) == (wordIndexableFormat())); + longAtput((void *)((nextPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))))); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(nextPage)) == (wordIndexableFormat())); + longAtput((void *)((nextPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord()))))),0); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(GIV(weaklingStack))) == (wordIndexableFormat())); + longAtput((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),nextPage); + nextPage = nextNextPage; + } + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(GIV(weaklingStack))) == (wordIndexableFormat())); + longAtput((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord()))))),0); + eassert(isValidObjStack(GIV(weaklingStack))); + /* end emptyObjStack: */ +l1:; +} + + /* SpurMemoryManager>>#noCheckClassAtIndex: */ +static NoDbgRegParms sqInt +noCheckClassAtIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt fieldIndex; + + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return null; + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + +/* Push an element on an objStack. Split from push:onObjStack: for testing. */ + + /* SpurMemoryManager>>#noCheckPush:onObjStack: */ +static NoDbgRegParms sqInt +noCheckPushonObjStack(sqInt objOop, sqInt objStack) +{ + sqInt topx; + + eassert(isValidObjStack(objStack)); + + /* for debugging markAndTrace: set (MarkStackRecord := OrderedCollection new) */ + topx = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + if (topx >= ObjStackLimit) { + noCheckPushonObjStack(objOop, ensureRoomOnObjStackAt(longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))))); + } + else { + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(objStack)) == (wordIndexableFormat())); + longAtput((void *)((objStack + BaseHeaderSize) + ((((usqInt)((ObjStackFixedSlots + topx)) << (shiftForWord()))))),objOop); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(objStack)) == (wordIndexableFormat())); + longAtput((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))),topx + 1); + } + return objOop; +} + + +/* Make sure the oop at fieldIndex in anObject is not forwarded (follow the + forwarder there-in if so). Answer the (possibly followed) oop at + fieldIndex. */ + + /* SpurMemoryManager>>#noFixupFollowField:ofObject: */ +static NoDbgRegParms sqInt +noFixupFollowFieldofObject(sqInt fieldIndex, sqInt anObject) +{ + sqInt objOop; + sqInt referent; + + objOop = longAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + referent = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + objOop = referent; + } + return objOop; +} + + +/* Allocate an object with numSlots space. If there is room beneath + scavengeThreshold allocate in newSpace, otherwise alocate in oldSpace. If + there is not room in newSpace + and a scavenge is not already scheduled, schedule a scavenge. */ + + /* SpurMemoryManager>>#noInlineAllocateSlots:format:classIndex: */ +static NoDbgRegParms NeverInline sqInt +noInlineAllocateSlotsformatclassIndex(sqInt numSlots, sqInt formatField, sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newObj; + usqInt numBytes; + + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if ((((usqInt)(numSlots)) >> 56) > 0) { + return null; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + return allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, formatField, classIndex); + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + return newObj; +} + + /* SpurMemoryManager>>#noUnscannedEphemerons */ +static int +noUnscannedEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((GIV(unscannedEphemerons).top)) < ((GIV(unscannedEphemerons).start)); +} + + +/* Answer the number of indexable bytes in the given non-immediate pure bits + object with format. + */ + + /* SpurMemoryManager>>#numBytesOfBits:format: */ +static NoDbgRegParms sqInt +numBytesOfBitsformat(sqInt objOop, sqInt format) +{ + usqInt numBytes; + usqInt numSlots; + + assert((isPureBitsFormat(format)) + && ((formatOf(objOop)) == format)); + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (format < (firstShortFormat())) { + return (format <= (sixtyFourBitIndexableFormat()) + ? numBytes + : numBytes - (((format & 1) << 2))); + } + return (format < (firstByteFormat()) + ? numBytes - (((format & 3) << 1)) + : numBytes - (format & 7)); +} + + +/* Answer the number of indexable bytes in the given non-immediate + byte-indexable object. + */ + + /* SpurMemoryManager>>#numBytesOfBytes: */ +static NoDbgRegParms sqInt +numBytesOfBytes(sqInt objOop) +{ + sqInt fmt; + usqInt numSlots; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + return ((((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); +} + + +/* Answer the number of indexable bytes in the given non-immediate object. + Does not adjust the size of contexts by stackPointer. */ + + /* SpurMemoryManager>>#numBytesOf: */ +sqInt +numBytesOf(sqInt objOop) +{ + sqInt fmt; + usqInt numBytes; + usqInt numSlots; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + return numBytes - (fmt & 7); + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + return numBytes; + } + if (fmt >= (firstShortFormat())) { + return numBytes - (((fmt & 3) << 1)); + } + + /* fmt >= self firstLongFormat */ + return numBytes - (((fmt & 1) << 2)); +} + + +/* Answer the number of pointer fields in the given object. + Works with CompiledMethods, as well as ordinary objects. */ + + /* SpurMemoryManager>>#numPointerSlotsOf: */ +static NoDbgRegParms usqInt +numPointerSlotsOf(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt fmt; + sqInt header; + sqInt headerSqInt; + usqInt numLiterals; + usqInt numSlots; + sqInt sp; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin numPointerSlotsOf:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + return CtxtTempFrameStart + contextSize; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + return (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + } + if (fmt == (forwardedFormat())) { + return 1; + } + if (fmt < (firstCompiledMethodFormat())) { + return 0; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + return numLiterals + LiteralStart; +} + + /* SpurMemoryManager>>#numSlotsForBytes: */ +static NoDbgRegParms sqInt +numSlotsForBytes(sqInt numBytes) +{ + return (numBytes + (BytesPerWord - 1)) / BytesPerWord; +} + + +/* A private internal version of numSlotsOf: that is happy to be applied to + free or forwarded objects. + */ + + /* SpurMemoryManager>>#numSlotsOfAny: */ +static NoDbgRegParms usqInt +numSlotsOfAny(sqInt objOop) +{ + usqInt numSlots; + + numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + return (numSlots == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); +} + + +/* Answer the number of strong pointer fields in the given indexable pointer + object. + */ + + /* SpurMemoryManager>>#numSlotsOfIndexablePointerObj: */ +static NoDbgRegParms usqInt +numSlotsOfIndexablePointerObj(sqInt objOop) +{ + usqInt numSlots; + + assert((formatOf(objOop)) == (indexablePointersFormat())); + if (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + return CtxtTempFrameStart + (fetchStackPointerOf(objOop)); + } + else { + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + return (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + } +} + + +/* numSlotsOf: should not be applied to free or forwarded objects. */ + + /* SpurMemoryManager>>#numSlotsOf: */ +usqInt +numSlotsOf(sqInt objOop) +{ + usqInt numSlots; + + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + return (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); +} + + +/* Answer the number of strong pointer fields in the given object, + which is expected not to be an active ephemeron. + Works with CompiledMethods as well as ordinary objects. */ + + /* SpurMemoryManager>>#numStrongSlotsOfInephemeral: */ +static NoDbgRegParms sqInt +numStrongSlotsOfInephemeral(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt fmt; + sqInt header; + sqInt headerSqInt; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objOopSqInt; + sqInt sp; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + assert((fmt != (ephemeronFormat())) + || ((isImmediate(keyOfEphemeron(objOop))) + || (isMarked(keyOfEphemeron(objOop))))); + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (arrayFormat())) { + return numSlots; + } + if (fmt == (indexablePointersFormat())) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + setTraceFlagOnContextsFramesPageIfNeeded(objOop); + + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + return CtxtTempFrameStart + contextSize; + } + return numSlots; + } + if (fmt == (weakArrayFormat())) { + objOopSqInt = fetchClassOfNonImm(objOop); + + /* begin fixedFieldsOfClass: */ + return (((longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + } + return numSlots; + } + if (fmt == (forwardedFormat())) { + return 1; + } + if (fmt < (firstCompiledMethodFormat())) { + return 0; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + return numLiterals + LiteralStart; +} + + +/* Answer the number of strong pointer fields in the given weakling. */ + + /* SpurMemoryManager>>#numStrongSlotsOfWeakling: */ +static NoDbgRegParms sqInt +numStrongSlotsOfWeakling(sqInt objOop) +{ + sqInt objOopSqInt; + + assert((formatOf(objOop)) == (weakArrayFormat())); + objOopSqInt = fetchClassOfNonImm(objOop); + + /* begin fixedFieldsOfClass: */ + return (((longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); +} + + /* SpurMemoryManager>>#numUnscannedEphemerons */ +static sqInt +numUnscannedEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((((sqInt) (((GIV(unscannedEphemerons).top)) - ((GIV(unscannedEphemerons).start))))) / BytesPerOop) + 1; +} + + +/* Answer if objOop looks like a class object. WIth Spur be lenient if the + object doesn't + yet have a hash (i.e. is not yet in the classTable), and accept forwarding + pointers. + */ + + /* SpurMemoryManager>>#objCouldBeClassObj: */ +sqInt +objCouldBeClassObj(sqInt objOop) +{ + sqInt fieldOop; + + return (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + && (((numSlotsOfAny(objOop)) > InstanceSpecificationIndex) + && (((fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord()))))))), + ((addressCouldBeObj(fieldOop)) + && ((((byteAt((void *)(fieldOop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + || ((/* isOopForwarded: */ + ((!(fieldOop & (tagMask())))) + && ((!((longAt((void *)(fieldOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + && (isPointers(followForwarded(fieldOop)))))) + && (((fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord()))))))), + ((addressCouldBeObj(fieldOop)) + && ((((byteAt((void *)(fieldOop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + || ((/* isOopForwarded: */ + ((!(fieldOop & (tagMask())))) + && ((!((longAt((void *)(fieldOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + && (isPointers(followForwarded(fieldOop)))))) + && (((((longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord())))))))) & 7) == 1))))))); +} + + +/* Object parsing. + 1. all objects have at least a word following the header, for a forwarding + pointer. 2. objects with an overflow size have a preceeing word with a + saturated slotSize. If the word following + an object doesn't have a saturated size field it must be a single-header + object. If the word following + does have a saturated slotSize it must be the overflow size word. */ + + /* SpurMemoryManager>>#objectAfter: */ +sqInt +objectAfter(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + + if (oopisLessThan(objOop, GIV(oldSpaceStart))) { + if (oopisGreaterThanOrEqualToandLessThan(objOop, ((GIV(eden)).start), GIV(freeStart))) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + return GIV(freeStart); + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + } + if (oopisGreaterThanOrEqualToandLessThan(objOop, ((GIV(pastSpace)).start), GIV(pastSpaceStart))) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + return GIV(pastSpaceStart); + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + } + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(futureSurvivorStart))) { + return GIV(futureSurvivorStart); + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + } + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + return GIV(endOfMemory); + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); +} + + +/* useful for debugging */ + + /* SpurMemoryManager>>#objectBefore: */ +sqInt +objectBefore(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOopSqInt; + sqInt prev; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + prev = null; + if (oopisLessThan(objOop, GIV(oldSpaceStart))) { + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l2; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOopSqInt, GIV(freeStart))) { + if (oopisGreaterThanOrEqualTo(objOopSqInt, objOop)) { + return prev; + } + prev = objOopSqInt; + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOopSqInt = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + /* end allNewSpaceEntitiesDo: */ +l2: + return prev; + } + + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + if (oopisGreaterThanOrEqualTo(objOopSqInt, objOop)) { + return prev; + } + prev = objOopSqInt; + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3:; + } + return prev; +} + + +/* For assertions only... This ends the recursion in setting up the bridged + enumerations of new space that need objectBefore: for assertion checking. */ + + /* SpurMemoryManager>>#objectInPastSpaceBefore: */ +static NoDbgRegParms sqInt +objectInPastSpaceBefore(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt prev; + + if (GIV(pastSpaceStart) <= (((GIV(pastSpace)).start))) { + return null; + } + prev = null; + address = ((GIV(pastSpace)).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + obj = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + while (oopisLessThan(obj, GIV(pastSpaceStart))) { + if (oopisGreaterThanOrEqualTo(obj, objOop)) { + return prev; + } + prev = obj; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + obj = GIV(pastSpaceStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + return prev; +} + + +/* For enumerating objects find the header of the first object in a space. + If the object starts with an overflow size field it will start at the next + allocationUnit. c.f. numSlotsOf: */ + + /* SpurMemoryManager>>#objectStartingAt: */ +static NoDbgRegParms sqInt +objectStartingAt(sqInt address) +{ + usqInt numSlots; + + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + return (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); +} + + +/* This primitive is called from Squeak as... + arrayOfRoots uniquelyAccessibleObjects + + This primitive answers an array of the receiver and every object in its + proper tree of subParts (ie, that is not refered to from anywhere else + outside the tree). + + The primitive can fail for the following reasons with the specified + failure codes: + PrimErrNoMemory: additional allocations failed + */ + + /* SpurMemoryManager>>#objectsAccessibleFromRoots: */ +static NoDbgRegParms sqInt +objectsAccessibleFromRoots(sqInt arrayOfRootsArg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayOfObjects; + sqInt arrayOfRoots; + + runLeakCheckerFor(GCModeImageSegment); + + /* First scavenge to collect any new space garbage that refers to the graph. */ + + /* begin scavengingGC */ + scavengingGCTenuringIf(TenureByAge); + arrayOfRoots = /* updatePostScavenge: */ + ((!((longAt((void *)(arrayOfRootsArg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(arrayOfRootsArg) + : arrayOfRootsArg); + + /* Now compute the transitive closure, collecting the sequence of objects to be stored in the arrayOfObjects array. + Included in arrayOfObjects are the arrayOfRoots and all its contents. All objects have been unmarked. */ + arrayOfObjects = objectsReachableFromRoots(arrayOfRoots); + if (!arrayOfObjects) { + return (((usqInt)PrimErrNoMemory << 3) | 1); + } + + /* If objectsReachableFromRoots: answers an integer there is not enough continuous free space in which to allocate the + reachable objects. If there is sufficient free space then answer an error code to prompt a compacting GC and a retry. */ + if ((((arrayOfObjects) & 7) == 1)) { + if ((GIV(totalFreeOldSpace) - 8 /* allocationUnit */) >= ((arrayOfObjects >> 3))) { + return (((usqInt)PrimErrNeedCompaction << 3) | 1); + } + return (((usqInt)PrimErrNoMemory << 3) | 1); + } + assert(allObjectsUnmarked()); + assert(!((forwardersIn(arrayOfObjects)))); + return arrayOfObjects; +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. + Answer an Array of all the objects only reachable from the argument, an + Array of root objects, + starting with arrayOfRoots. If there is no space, answer a SmallInteger + whose value is the + number of slots required. This is used to collect the objects to include + in an image segment + on Spur, separate from creating the segment, hence simplifying the + implementation. Thanks to Igor Stasenko for this idea. */ + + /* SpurMemoryManager>>#objectsReachableFromRoots: */ +static NoDbgRegParms NeverInline sqInt +objectsReachableFromRoots(sqInt arrayOfRoots) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt count; + sqInt delta; + sqInt followingWord; + usqInt followingWordAddress; + usqInt freeChunk; + sqInt i; + sqInt iSqInt; + usqInt limit; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt oop; + sqInt oopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + usqInt ptr; + sqInt referent; + usqInt start; + usqInt startUsqInt; + sqInt toDoLimit; + sqInt toDoLimit1; + sqInt toDoLimitSqInt; + + + /* & hence start & ptr are too; limit is also because of addressAfter: */ + assert(isArray(arrayOfRoots)); + + /* Mark all objects except those only reachable from the arrayOfRoots by marking + each object in arrayOfRoots and then marking all reachable objects (from the + system roots). This leaves unmarked only objects reachable from the arrayOfRoots. + N.B. A side-effect of the marking is that all forwarders in arrayOfRoots will be followed. */ + assert(allObjectsUnmarked()); + + /* begin setGCMode: */ + GIV(gcMode) = GCModeImageSegment; + assert(!(isFreeObject(arrayOfRoots))); + byteAtput((void *)(arrayOfRoots + (markBitsByteOffset())),(byteAt((void *)(arrayOfRoots + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + toDoLimitSqInt = ((/* begin numSlotsOf: */ + assert((classIndexOf(arrayOfRoots)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(arrayOfRoots + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(arrayOfRoots - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (iSqInt = 0; iSqInt <= toDoLimitSqInt; iSqInt += 1) { + /* begin followField:ofObject: */ + oopSqInt = longAt((void *)((arrayOfRoots + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(oopSqInt & (tagMask())))) + && ((!((longAt((void *)(oopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + oopSqInt = fixFollowedFieldofObjectwithInitialValue(iSqInt, arrayOfRoots, oopSqInt); + } + if ((!(oopSqInt & (tagMask())))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oopSqInt))); + byteAtput((void *)(oopSqInt + (markBitsByteOffset())),(byteAt((void *)(oopSqInt + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + } + } + markObjects(0); + + /* After the mark phase all unreachable weak slots will have been nilled + and all active ephemerons fired. */ + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + assert(noUnscannedEphemerons()); + + /* Now unmark the roots before collecting the transitive closure of unmarked objects accessible from the roots. */ + + /* begin unmarkObjectsIn: */ + toDoLimitSqInt = ((assert((classIndexOf(arrayOfRoots)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(arrayOfRoots + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(arrayOfRoots - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (iSqInt = 0; iSqInt <= toDoLimitSqInt; iSqInt += 1) { + /* begin followField:ofObject: */ + oopSqInt = longAt((void *)((arrayOfRoots + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(oopSqInt & (tagMask())))) + && ((!((longAt((void *)(oopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + oopSqInt = fixFollowedFieldofObjectwithInitialValue(iSqInt, arrayOfRoots, oopSqInt); + } + if ((!(oopSqInt & (tagMask())))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oopSqInt))); + byteAtput((void *)(oopSqInt + (markBitsByteOffset())),(byteAt((void *)(oopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + + /* Use the largest free chunk to answer the result. */ + + /* N.B. Does /not/ update totalFreeOldSpace */ + freeChunk = allocateLargestFreeChunk(); + + /* but must update so that growth in the markStack does not cause assert fails. */ + GIV(totalFreeOldSpace) -= bytesInBody(freeChunk); + ptr = (start = freeChunk + BaseHeaderSize); + limit = addressAfter(freeChunk); + count = 0; + + /* First put the arrayOfRoots; order is important. */ + noCheckPushonObjStack(arrayOfRoots, GIV(markStack)); + + /* Now collect the roots and the transitive closure of unmarked objects from them. */ + while (!(isEmptyObjStack(GIV(markStack)))) { + objOop = popObjStack(GIV(markStack)); + assert(isMarked(objOop)); + count += 1; + if (ptr < limit) { + longAtput((void *)(ptr),objOop); + ptr += BytesPerOop; + } + oop = fetchClassOfNonImm(objOop); + if (!((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (markedBitByteShift())))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + noCheckPushonObjStack(oop, GIV(markStack)); + } + if ((((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(objOop))))) { + toDoLimit = (numSlotsOfMarriedContext(objOop)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + oop = fetchPointerofMarriedContext(i, objOop); + if (!((((oop & (tagMask())) != 0)) + || (((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + noCheckPushonObjStack(oop, GIV(markStack)); + } + } + } + else { + toDoLimit1 = (numPointerSlotsOf(objOop)) - 1; + for (i = 0; i <= toDoLimit1; i += 1) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (!((((oop & (tagMask())) != 0)) + || (((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + noCheckPushonObjStack(oop, GIV(markStack)); + } + } + } + } + + /* begin unmarkAllObjects */ + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + if ((byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) > (lastClassIndexPun())) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + else { + if (!(((longAt((void *)(objOopSqInt))) & (classIndexMask())) == (segmentBridgePun()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + startUsqInt = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (startUsqInt > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(startUsqInt + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? startUsqInt + BaseHeaderSize + : startUsqInt); + while (oopisLessThan(objOopSqInt, GIV(freeStart))) { + if ((byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) > (lastClassIndexPun())) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + else { + if (!(((longAt((void *)(objOopSqInt))) & (classIndexMask())) == (segmentBridgePun()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOopSqInt = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3: + + /* Now try and allocate the result */ + if ((count > ((ptr - start) / BytesPerOop)) + || ((limit != ptr) + && ((limit - ptr) <= 8 /* allocationUnit */))) { + freeObject(freeChunk); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeImageSegment)) == (GCCheckFreeSpace | GCModeImageSegment))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + + /* begin setGCMode: */ + GIV(gcMode) = 0; + return (((usqInt)count << 3) | 1); + } + + /* not enough room + can't split a single word + There's room; set the format, & classIndex and shorten. */ + + /* begin setFormatOf:to: */ + assert((((arrayFormat()) >= 0) && ((arrayFormat()) <= (formatMask())))); + byteAtput((void *)(freeChunk + (formatFieldByteOffset())),((byteAt((void *)(freeChunk + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (arrayFormat())); + + /* begin setClassIndexOf:to: */ + assert(((ClassArrayCompactIndex >= 0) && (ClassArrayCompactIndex <= (classIndexMask())))); + longAtput((void *)(freeChunk),((longAt((void *)(freeChunk))) & (~(usqIntptr_t)(classIndexMask()))) + ClassArrayCompactIndex); + + /* begin shorten:toIndexableSize: */ + assert((count >= 0) + && (count < (lengthOf(freeChunk)))); + delta = doShortentoIndexableSize(freeChunk, count); + assert((lengthOf(followMaybeForwarded(freeChunk))) == count); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + if ((!((longAt((void *)(freeChunk))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(freeChunk)); + referent = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + freeChunk = referent; + } + + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(freeChunk + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(freeChunk); + } + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeImageSegment)) == (GCCheckFreeSpace | GCModeImageSegment))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + runLeakCheckerFor(GCModeImageSegment); + + /* begin setGCMode: */ + GIV(gcMode) = 0; + return freeChunk; +} + + +/* Verify that the given oop is legitimate. Check address, header, and size + but not class. + */ + + /* SpurMemoryManager>>#okayOop: */ +static NoDbgRegParms sqInt +okayOop(sqInt signedOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt fmt; + usqInt oop; + usqLong unusedBits; + sqInt unusedBitsInYoungObjects; + + oop = ((usqInt) signedOop); + + /* address and size checks */ + if (((oop & (tagMask())) != 0)) { + return 1; + } + if (!(addressCouldBeObj(oop))) { + error("oop is not a valid address"); + return 0; + } + if (!(oopisLessThanOrEqualTo(addressAfter(oop), GIV(endOfMemory)))) { + error("oop size would make it extend beyond the end of memory"); + return 0; + } + + /* header type checks */ + if (!(((classIndex = (longAt((void *)(oop))) & (classIndexMask()))) >= (firstClassIndexPun()))) { + error("oop is a free chunk, or bridge, not an object"); + return 0; + } + if (((byteAt((void *)(oop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) + && ((byteAt((void *)((oop - BaseHeaderSize) + (numSlotsFieldByteOffset())))) != (numSlotsMask()))) { + error("oop header has overflow header word, but overflow word does not have a saturated numSlots field"); + return 0; + } + + /* format check */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if ((fmt == 6) || (fmt == 8)) { + error("oop has an unknown format type"); + return 0; + } + if ((fmt == (forwardedFormat())) != (classIndex == (isForwardedObjectClassIndexPun()))) { + error("oop has mis-matched format/classIndex fields; only one of them is the isForwarded value"); + return 0; + } + + /* specific header bit checks */ + unusedBits = (0x400000) | (0x40000000000000LL); + if ((long64At((void *)(oop))) & unusedBits) { + error("some unused header bits are set; should be zero"); + return 0; + } + unusedBitsInYoungObjects = ((1ULL << (greyBitShift())) | (1U << (pinnedBitShift()))) | (1U << (rememberedBitShift())); + if ((longAt((void *)(oop))) & unusedBitsInYoungObjects) { + error("some header bits unused in young objects are set; should be zero"); + return 0; + } + return 1; +} + + +/* useful for debugging */ +/* Object parsing. + 1. all objects have at least a word following the header, for a forwarding + pointer. 2. objects with an overflow size have a preceeing word with a + saturated slotSize. If the word following + an object doesn't have a saturated size field it must be a single-header + object. If the word following + does have a saturated slotSize it must be the overflow size word. */ + + /* SpurMemoryManager>>#oldSpaceObjectAfter: */ +sqInt +oldSpaceObjectAfter(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + return GIV(endOfMemory); + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); +} + + +/* in an effort to fix a compiler bug with two-way become post r3427 */ +/* Allocate two new objects, n1 & n2. Copy the contents appropriately. + Convert obj1 and obj2 + into forwarding objects pointing to n2 and n1 respectively. No need to + check if cloneObject: + succeeds because an earlier pass over objects ensured that there is enough + memory. + */ + + /* SpurMemoryManager>>#outOfPlaceBecome:and:copyHashFlag: */ +static NoDbgRegParms NeverInline void +outOfPlaceBecomeandcopyHashFlag(sqInt obj1, sqInt obj2, sqInt copyHashFlag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt clone1; + sqInt clone2; + sqInt hash; + + clone1 = (((longAt((void *)(obj1))) & (classIndexMask())) == ClassMethodContextCompactIndex + ? cloneContext(obj1) + : cloneObject(obj1)); + clone2 = (((longAt((void *)(obj2))) & (classIndexMask())) == ClassMethodContextCompactIndex + ? cloneContext(obj2) + : cloneObject(obj2)); + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(obj1 + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* begin setIsImmutableOf:to: */ + longAtput((void *)(clone1),(longAt((void *)(clone1))) | (1U << (immutableBitShift()))); + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(obj2 + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* begin setIsImmutableOf:to: */ + longAtput((void *)(clone2),(longAt((void *)(clone2))) | (1U << (immutableBitShift()))); + } + if (copyHashFlag) { + hash = (long32At((void *)(obj1 + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(clone1 + 4),((((long32At((void *)(clone1 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + hash = (long32At((void *)(obj2 + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(clone2 + 4),((((long32At((void *)(clone2 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + } + else { + hash = (long32At((void *)(obj2 + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(clone1 + 4),((((long32At((void *)(clone1 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + hash = (long32At((void *)(obj1 + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(clone2 + 4),((((long32At((void *)(clone2 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + } + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(obj1),((longAt((void *)(obj1))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(obj1),(longAt((void *)(obj1))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(obj1)); + assert(!(isOopForwarded(clone2))); + + /* begin isOldObject: */ + assert(isNonImmediate(obj1)); + if (oopisGreaterThanOrEqualTo(obj1, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(clone2 & (tagMask())))) + && (oopisLessThan(clone2, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj1 + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj1); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj1 + BaseHeaderSize) + (0U << (shiftForWord()))),clone2); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(obj1 + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(obj1 + (numSlotsFieldByteOffset())),1); + } + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(obj2),((longAt((void *)(obj2))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(obj2),(longAt((void *)(obj2))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(obj2)); + assert(!(isOopForwarded(clone1))); + + /* begin isOldObject: */ + assert(isNonImmediate(obj2)); + if (oopisGreaterThanOrEqualTo(obj2, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(clone1 & (tagMask())))) + && (oopisLessThan(clone1, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj2 + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj2); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj2 + BaseHeaderSize) + (0U << (shiftForWord()))),clone1); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(obj2 + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(obj2 + (numSlotsFieldByteOffset())),1); + } + if ((((/* begin isYoungObject: */ + assert(isNonImmediate(obj1)), + oopisLessThan(obj1, GIV(oldSpaceStart)))) != ((/* begin isYoungObject: */ + assert(isNonImmediate(clone2)), + oopisLessThan(clone2, GIV(oldSpaceStart))))) + || (((/* begin isYoungObject: */ + assert(isNonImmediate(obj2)), + oopisLessThan(obj2, GIV(oldSpaceStart)))) != ((/* begin isYoungObject: */ + assert(isNonImmediate(clone1)), + oopisLessThan(clone1, GIV(oldSpaceStart)))))) { + GIV(becomeEffectsFlags) = GIV(becomeEffectsFlags) | OldBecameNewFlag; + } +} + + +/* Attempt to pin objOop, which must not be immediate. + If the attempt succeeds answer objOop's (possibly moved) oop. + If the attempt fails, which can only occur if there is no memory, answer + 0. */ + + /* SpurMemoryManager>>#pinObject: */ +sqInt +pinObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt oldClone; + sqInt referent; + SpurSegmentInfo *seg; + + assert(isNonImmediate(objOop)); + assert(!((isForwarded(objOop)))); + + /* We choose to clone to keep pinned objects together to reduce fragmentation, + if the object is not too large, assuming that pinning is rare and that fragmentation is a bad thing. + Too large is defined as over 1mb. The size of a 640x480x4 bitmap is 1228800. */ + + /* begin isOldObject: */ + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if ((numBytesOf(objOop)) > (0x100000)) { + setIsPinnedOfto(objOop, 1); + return objOop; + } + seg = segmentContainingObj(objOop); + if ((seg->containsPinned)) { + setIsPinnedOfto(objOop, 1); + return objOop; + } + + /* begin someSegmentContainsPinned */ + for (i = 0; i < GIV(numSegments); i += 1) { + if (((GIV(segments)[i]).containsPinned)) { + goto l1; + } + } + setIsPinnedOfto(objOop, 1); + (seg->containsPinned = 1); + return objOop; +l1:; + } + oldClone = cloneInOldSpaceforPinning(objOop, 1); + if (oldClone) { + GIV(becomeEffectsFlags) = becomeEffectFlagsFor(objOop); + setIsPinnedOfto(oldClone, 1); + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(objOop),(longAt((void *)(objOop))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(objOop)); + assert(!(isOopForwarded(oldClone))); + + /* begin isOldObject: */ + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(oldClone & (tagMask())))) + && (oopisLessThan(oldClone, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),oldClone); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),1); + } + + /* begin followSpecialObjectsOop */ + if ((!((longAt((void *)(GIV(specialObjectsOop)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + GIV(validatedIntegerClassFlags) = 0; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(GIV(specialObjectsOop))); + referent = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + GIV(specialObjectsOop) = referent; + } + followForwardedObjectFieldstoDepth(GIV(specialObjectsOop), 0); + + /* begin postBecomeAction: */ + spurPostBecomeAction(GIV(becomeEffectsFlags)); + if (((GIV(becomeEffectsFlags) & BecameJittedCompiledMethodFlag) != 0)) { + freeBecomeFlaggedMethods(); + } + followMovableLiteralsAndUpdateYoungReferrers(); + + /* begin nilUncoggableMethods */ + GIV(lastCoggableInterpretedBlockMethod) = (GIV(lastUncoggableInterpretedBlockMethod) = null); + assert(kosherYoungReferrers()); + GIV(gcMode) = 0; + postBecomeScanClassTable(GIV(becomeEffectsFlags)); + GIV(becomeEffectsFlags) = 0; + } + return oldClone; +} + + /* SpurMemoryManager>>#popObjStack: */ +static NoDbgRegParms sqInt +popObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt myx; + sqInt nextPage; + sqInt top; + sqInt topx; + + eassert(isValidObjStack(objStack)); + topx = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + if (!topx) { + assert((fetchPointerofObject(ObjStackNextx, objStack)) == 0); + + /* for debugging markAndTrace: set (MarkStackRecord := OrderedCollection new) */ + return null; + } + topx -= 1; + top = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)((topx + ObjStackFixedSlots)) << (shiftForWord())))))); + assert(addressCouldBeOop(top)); + + /* for debugging markAndTrace: set (MarkStackRecord := OrderedCollection new) */ + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(objStack)) == (wordIndexableFormat())); + longAtput((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))),topx); + if ((topx == 0) + && (((nextPage = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))) != 0)) { + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(nextPage)) == (wordIndexableFormat())); + longAtput((void *)((nextPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),objStack); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(objStack)) == (wordIndexableFormat())); + longAtput((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord()))))),0); + myx = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))); + + /* begin updateRootOfObjStackAt:with: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(myx, GIV(hiddenRootsObj), nextPage)); + assert(isNonImmediate(GIV(hiddenRootsObj))); + if (oopisGreaterThanOrEqualTo(GIV(hiddenRootsObj), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(nextPage & (tagMask())))) + && (oopisLessThan(nextPage, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(hiddenRootsObj) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(hiddenRootsObj)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(myx) << (shiftForWord()))))),nextPage); + switch (myx) { + case MarkStackRootIndex: + GIV(markStack) = nextPage; + break; + case WeaklingStackRootIndex: + GIV(weaklingStack) = nextPage; + break; + case MournQueueRootIndex: + GIV(mournQueue) = nextPage; + break; + default: + error("Case not found and no otherwise clause"); + } + eassert(isValidObjStack(nextPage)); + } + else { + eassert(isValidObjStack(objStack)); + } + return top; +} + + +/* Pop and return the possibly remapped object from the remap buffer. + We support this excessence for compatibility with ObjectMemory. + Spur doesn't GC during allocation. */ + + /* SpurMemoryManager>>#popRemappableOop */ +sqInt +popRemappableOop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = GIV(remapBuffer)[GIV(remapBufferCount)]; + GIV(remapBufferCount) -= 1; + return oop; +} + + +/* Scan the class table post-become (iff an active class object was becommed) + to ensure no + forwarding pointers, and no unhashed classes exist in the class table. + + Note that one-way become can cause duplications in the class table. + So if pointer objects have been becommed, scan all pages in the class + table and eliminate ay entries that have a zero hash. + doBecome:and:copyHash: has + already ensured that any becomed class has been stored at the right index. */ + + /* SpurMemoryManager>>#postBecomeScanClassTable: */ +static NoDbgRegParms void +postBecomeScanClassTable(sqInt effectsFlags) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOrNil; + sqInt i; + sqInt j; + usqInt numSlots; + sqInt page; + sqInt referent; + sqInt toDoLimit; + + assert(validClassTableRootPages()); + if (!(((effectsFlags & BecamePointerObjectFlag) != 0))) { + return; + } + for (i = 0; i < GIV(numClassTablePages); i += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + assert(!(isForwarded(page))); + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(page)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(page + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(page - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNil = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNil != GIV(nilObj)) { + if ((!((longAt((void *)(classOrNil))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(classOrNil)); + referent = longAt((void *)((classOrNil + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + classOrNil = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(j, page, classOrNil)); + assert(isNonImmediate(page)); + if (oopisGreaterThanOrEqualTo(page, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(classOrNil & (tagMask())))) + && (oopisLessThan(classOrNil, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(page + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(page); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord()))))),classOrNil); + } + if (!((long32At((void *)(classOrNil + 4))) & (identityHashHalfWordMask()))) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(page)) + && (!(isForwarded(page)))); + assert(validStorePointerUncheckedArgs(j, page, GIV(nilObj))); + longAtput((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord()))))),GIV(nilObj)); + + /* If the removed class is before the classTableIndex, set the + classTableIndex to point to the empty slot so as to reuse it asap. */ + if ((((((usqInt)(i) << (classTableMajorIndexShift())))) + j) < GIV(classTableIndex)) { + GIV(classTableIndex) = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + } + } + } + } + } + + /* classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + assert(GIV(classTableIndex) >= (1U << (classTableMajorIndexShift()))); +} + + /* SpurMemoryManager>>#primitiveErrorTable */ +sqInt +primitiveErrorTable(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(PrimitiveErrorTableIndex) << (shiftForWord())))))); +} + + +/* Scan the heap printing the oops of any and all contexts that refer to + anOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printActivationsOf: */ +void +printActivationsOf(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (aMethodObj == (longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord()))))))))) { + printHex(objSqInt); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + print(" pc "); + printHex(longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))))); + cr(); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printBogons */ +void +printBogons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin printTheBogons: */ + print("bogon "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)GIV(bogon))); + cr(); + print("anomaly "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)GIV(anomaly))); + cr(); +} + + +/* Scan the heap printing the oops of any and all contexts that refer to + anOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printContextReferencesTo: */ +void +printContextReferencesTo(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt i; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + i = CtxtTempFrameStart + (fetchStackPointerOf(objSqInt)); + while (((i -= 1)) >= 0) { + if (anOop == (longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + printHex(objSqInt); + print(" @ "); + printNum(i); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + print(" pc "); + printHex(longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))))); + cr(); + i = 0; + } + } + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printEntity: */ +void +printEntity(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt n; + sqInt printFlags; + + printFlags = 0; + printHex(oop); + + /* begin space */ + printChar(' '); + if (!(addressCouldBeObj(oop))) { + print((((oop & (tagMask())) != 0) + ? "immediate" + : "unknown")); + return; + } + print((((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun()) + ? "free" + : (((longAt((void *)(oop))) & (classIndexMask())) == (segmentBridgePun()) + ? "bridge" + : ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? "forwarder" + : (((longAt((void *)(oop))) & (classIndexMask())) <= (lastClassIndexPun()) + ? ((printFlags = 1), + "pun/obj stack") + : ((printFlags = 1), + "object")))))); + + /* begin space */ + printChar(' '); + n = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + + /* begin printHexnpnp: */ + fprintf(GIV(transcript), + "%" PRIxSQINT "", + n); + print("/"); + n = bytesInBody(oop); + + /* begin printHexnpnp: */ + fprintf(GIV(transcript), + "%" PRIxSQINT "", + n); + print("/"); + printNum(bytesInBody(oop)); + if (printFlags) { + /* begin space */ + printChar(' '); + print((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 15 + ? "f:0" + : "f:")); + n = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin printHexnpnp: */ + fprintf(GIV(transcript), + "%" PRIxSQINT "", + n); + print(((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (greyBitByteShift())) + ? " g" + : " .")); + print(((((usqInt)((byteAt((void *)(oop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1 + ? "i" + : ".")); + print(((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (markedBitByteShift())) + ? "m" + : ".")); + print(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift())) + ? "p" + : ".")); + print(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())) + ? "r" + : ".")); + } + cr(); +} + + +/* Actually print all objects that have ephemeron format. */ + + /* SpurMemoryManager>>#printEphemerons */ +void +printEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt format; + sqInt nSoFar; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + nSoFar = 0; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + format = (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask()); + if (format == (ephemeronFormat())) { + printf("0x%" PRIxSQINT "", obj); + if (((nSoFar += 1)) >= 4) { + nSoFar = 0; + cr(); + } + else { + /* begin space */ + printChar(' '); + } + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + if (nSoFar > 0) { + cr(); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printForwarders */ +void +printForwarders(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == (isForwardedObjectClassIndexPun())) { + printHex(objOopSqInt); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOopSqInt, GIV(freeStart))) { + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == (isForwardedObjectClassIndexPun())) { + printHex(objOopSqInt); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOopSqInt = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3:; +} + + /* SpurMemoryManager>>#printForwarder:on: */ +static NoDbgRegParms void +printForwarderon(sqInt oop, FILE *aStream) +{ + fprintf(aStream, + "%p is a forwarded hdr%d slot size %" PRIuSQINT " object to %p\n", + ((void *)oop), + ((byteAt((void *)(oop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? 16 + : 8), + numSlotsOfAny(oop), + ((void *)(followForwarded(oop)))); +} + + +/* This version goes through memory, printing all free chunks. + Other versions go through the free lists. This one will show + all free chunks even if the free lists are corrupt. */ +/* useful for debugging */ + + /* SpurMemoryManager>>#printFreeChunks */ +void +printFreeChunks(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + sqInt seenNewFreeChunk; + usqInt start; + + seenNewFreeChunk = 0; + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l2; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + if (!seenNewFreeChunk) { + print("NewSpace CONTAINS FREE OBJECT(S)!!"); + cr(); + seenNewFreeChunk = 1; + } + printFreeChunk(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + /* end allNewSpaceEntitiesDo: */ +l2: + + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + printFreeChunk(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3:; + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printFreeChunk: */ +void +printFreeChunk(sqInt freeChunk) +{ + printFreeChunkprintAsTreeNode(freeChunk, 1); +} + + /* SpurMemoryManager>>#printFreeChunk:printAsTreeNode: */ +static NoDbgRegParms void +printFreeChunkprintAsTreeNode(sqInt freeChunk, sqInt printAsTreeNode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt n; + usqInt numBytes; + + numBytes = bytesInBody(freeChunk); + print("freeChunk "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)freeChunk)); + if (printAsTreeNode) { + print((freeChunk == (GIV(freeLists)[0]) + ? " + " + : " - ")); + n = addressAfter(freeChunk); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + } + print(" bytes "); + printNum(numBytes); + print(" next "); + n = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + if (!(isLilliputianSize(numBytes))) { + print(" prev "); + n = longAt((void *)((freeChunk + BaseHeaderSize) + (1U << (shiftForWord())))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + } + if ((numBytes >= 0x200 /* (numFreeLists * #allocationUnit) */) + && (printAsTreeNode)) { + print(" ^ "); + n = longAt((void *)((freeChunk + BaseHeaderSize) + (2U << (shiftForWord())))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + print(" < "); + n = longAt((void *)((freeChunk + BaseHeaderSize) + (3U << (shiftForWord())))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + print(" > "); + n = longAt((void *)((freeChunk + BaseHeaderSize) + (4U << (shiftForWord())))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + } + cr(); +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printFreeListHeads */ +void +printFreeListHeads(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt expectedMask; + sqInt i; + + expectedMask = 0; + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + printHex(GIV(freeLists)[i]); + if (GIV(freeLists)[i]) { + expectedMask += 1ULL << i; + } + if ((i + 1) % (4)) { + print(" "); + } + else { + cr(); + } + } + cr(); + print("mask: "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)GIV(freeListsMask))); + print(" expected: "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)expectedMask)); + cr(); +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printFreeList: */ +sqInt +printFreeList(sqInt chunkOrIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt freeChunk; + + if ((chunkOrIndex >= 0) + && (chunkOrIndex < 64 /* numFreeLists */)) { + return printFreeList(GIV(freeLists)[chunkOrIndex]); + } + freeChunk = chunkOrIndex; + while (freeChunk != 0) { + printFreeChunk(freeChunk); + freeChunk = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + } + return 0; +} + + /* SpurMemoryManager>>#printFreeObject:on: */ +static NoDbgRegParms void +printFreeObjecton(sqInt oop, FILE *aStream) +{ + fprintf(aStream, + "%p is a free chunk of size %" PRIdSQINT " 0th field:%p\n", + ((void *)oop), + bytesInBody(oop), + ((void *)(longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord()))))))); +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printFreeTree */ +void +printFreeTree(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + printFreeTreeChunk(GIV(freeLists)[0]); +} + + +/* Slang is blind-sided by the inlining of printFreeTreeChunk: into + printFreeTree. + */ + + /* SpurMemoryManager>>#printFreeTreeChunk: */ +static NoDbgRegParms void +printFreeTreeChunk(sqInt chunkOrZero) +{ + if (chunkOrZero > 0) { + printFreeTreeChunk(longAt((void *)((chunkOrZero + BaseHeaderSize) + (3U << (shiftForWord()))))); + printFreeChunk(chunkOrZero); + printFreeTreeChunk(longAt((void *)((chunkOrZero + BaseHeaderSize) + (4U << (shiftForWord()))))); + } +} + + +/* useful for VM debugging */ +/* N.B. No safety bounds checks!! We need to look e.g. at corpses. */ + + /* SpurMemoryManager>>#printHeaderOf: */ +void +printHeaderOf(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt n; + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)objOop)); + if ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) { + print(" hdr16 slotf "); + n = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + print(" slotc "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)(((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8))); + + /* begin space */ + printChar(' '); + } + else { + print(" hdr8 slots "); + n = numSlotsOfAny(objOop); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + } + printChar(' '); + printChar(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift())) + ? 'M' + : 'm')); + printChar(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (greyBitByteShift())) + ? 'G' + : 'g')); + printChar(((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift())) + ? 'P' + : 'p')); + printChar(((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())) + ? 'R' + : 'r')); + printChar(((((usqInt)((byteAt((void *)(objOop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1 + ? 'I' + : 'i')); + print(" hash "); + n = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + print(" fmt "); + n = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + print(" cidx "); + n = (longAt((void *)(objOop))) & (classIndexMask()); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + cr(); +} + + /* SpurMemoryManager>>#printImmediateObject:on: */ +static NoDbgRegParms void +printImmediateObjecton(sqInt oop, FILE *aStream) +{ + assert(isImmediate(oop)); + if ((((oop) & 7) == 1)) { + fprintf(aStream, + "%p=%" PRIdSQINT "\n", + ((void *)oop), + ((sqInt)((oop >> 3)))); + } + if (((oop & (characterTag())) != 0)) { + fprintf(aStream, + "%p=$%ld ($%lc)\n", + ((void *)oop), + ((long)(((((usqInt)oop))) >> (numTagBits()))), + ((wint_t) (((((usqInt)oop))) >> (numTagBits())))); + } + if (((oop & (smallFloatTag())) != 0)) { + fprintf(aStream, + "%p=%g\n", + ((void *)oop), + floatValueOf(oop)); + } +} + + +/* Scan the heap printing the oops of any and all objects that are instances + of aClassOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printInstancesOf: */ +void +printInstancesOf(sqInt aClassOop) +{ + int classIndex; + + classIndex = (long32At((void *)(aClassOop + 4))) & (identityHashHalfWordMask()); + if (classIndex != (isFreeObjectClassIndexPun())) { + printInstancesWithClassIndex(classIndex); + } +} + + +/* Scan the heap printing the oops of any and all objects whose classIndex + equals the argument. + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printInstancesWithClassIndex: */ +void +printInstancesWithClassIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + printHex(objOop); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + printHex(objOop); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3:; +} + + +/* Print the objects in the classTable that have bad hashes. */ +/* useful for debugging */ + + /* SpurMemoryManager>>#printInvalidClassTableEntries */ +void +printInvalidClassTableEntries(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOrNilSqInt; + int hash; + sqInt i; + sqInt index; + sqInt j; + sqInt page; + sqInt toDoLimit; + + hash = 0; + if (!(validClassTableRootPages())) { + print("class table invalid; cannot print"); + cr(); + return; + } + + /* begin classTableEntriesDo: */ + for (i = 0; i < GIV(numClassTablePages); i += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNilSqInt = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNilSqInt != GIV(nilObj)) { + index = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + if (((!((longAt((void *)(classOrNilSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + || ((((hash = (long32At((void *)(classOrNilSqInt + 4))) & (identityHashHalfWordMask()))) == 0) + || ((noCheckClassAtIndex(hash)) != classOrNilSqInt))) { + print("entry "); + printHex(index); + print(" oop "); + printHex(classOrNilSqInt); + print(" hash "); + printHex(hash); + print(" => "); + printHex(classAtIndex(hash)); + cr(); + } + } + } + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printMarkedOops */ +#if LLDB +void +printMarkedOops(void) +{ + printOopsSuchThat(isMarked); +} +#endif /* LLDB */ + + +/* Scan the heap printing the oops of any and all methods that implement + anOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printMethodImplementorsOf: */ +void +printMethodImplementorsOf(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && ((maybeSelectorOfMethod(objSqInt)) == anOop)) { + printHex(objSqInt); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + cr(); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + +/* Scan the heap printing the oops of any and all methods that refer to anOop */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printMethodReferencesTo: */ +void +printMethodReferencesTo(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt i; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + i = ((literalCountOf(objSqInt)) + LiteralStart) - 1; + while (((i -= 1)) >= 0) { + if (anOop == (longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + printHex(objSqInt); + print(" @ "); + printNum(i); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + cr(); + i = 0; + } + } + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + /* SpurMemoryManager>>#printMournQueue */ +void +printMournQueue(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt iSqInt; + sqInt mourner; + sqInt nSoFar; + sqInt numOnThisPage; + sqInt objStackPage; + + nSoFar = 0; + + /* begin objStack:do: */ + if (GIV(mournQueue) == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(GIV(mournQueue))); + objStackPage = GIV(mournQueue); + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + mourner = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + format = (((mourner & (tagMask())) != 0) + ? forwardedFormat() + : (byteAt((void *)(mourner + (formatFieldByteOffset())))) & (formatMask())); + fprintf(GIV(transcript), + "%p%s", + ((void *)mourner), + (format == (weakArrayFormat()) + ? " weak " + : (format == (ephemeronFormat()) + ? " eph?!" + : (format == (nonIndexablePointerFormat()) + ? " fired" + : " ???? ")))); + if (((nSoFar += 1)) >= 4) { + nSoFar = 0; + cr(); + } + else { + /* begin space */ + printChar(' '); + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l1: + if (nSoFar > 0) { + cr(); + } +} + + +/* useful for debugging */ + + /* SpurMemoryManager>>#printNewObjectsInObjStack: */ +void +printNewObjectsInObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt iSqInt; + sqInt n; + sqInt numOnThisPage; + sqInt obj; + sqInt objStackPage; + + n = 0; + + /* begin objStack:do: */ + if (objStack == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(objStack)); + objStackPage = objStack; + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + obj = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (/* isInNewSpace: */ + (oopisLessThan(obj, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(obj, GIV(newSpaceStart)))) { + /* begin space */ + printChar(' '); + printHex(obj); + if (!(((n += 1)) % 8)) { + cr(); + } + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l1: + if (n % 8) { + cr(); + } + fprintf(GIV(transcript), + "total %" PRIdSQINT "\n", + n); +} + + /* SpurMemoryManager>>#printNonPointerDataOf:on: */ +static NoDbgRegParms void +printNonPointerDataOfon(sqInt oop, FILE *aStream) +{ + sqInt elementsPerLine; + sqInt format; + sqInt index; + sqInt lastIndex; + usqInt numSlots; + usqInt numSlotsUsqInt; + + format = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert(((format >= (sixtyFourBitIndexableFormat())) && (format <= ((firstCompiledMethodFormat()) - 1)))); + + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (format <= (ephemeronFormat())) { + lastIndex = numSlots; + goto l1; + } + if (format >= (firstByteFormat())) { + lastIndex = ((numSlots << (shiftForWord()))) - (format & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (format >= (firstShortFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 1))) - (format & 3); + goto l1; + } + if (format >= (firstLongFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 2))) - (format & 1); + goto l1; + } + if (format == (sixtyFourBitIndexableFormat())) { + lastIndex = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + lastIndex = 0; + /* end lengthOf:format: */ +l1: + if (!lastIndex) { + return; + } + if (format == (sixtyFourBitIndexableFormat())) { + lastIndex = ((32 < lastIndex) ? 32 : lastIndex); + + /* 0x/16r0123456789ABCDEF x 4 = 76/80 */ + elementsPerLine = 4; + for (index = 1; index <= lastIndex; index += 1) { + fprintf(aStream, + "%18" PRIXSQINT "%c", + ((usqLong) (long64At((void *)((oop + BaseHeaderSize) + ((((usqInt)((index - 1)) << 3))))))), + (((index % elementsPerLine) == 0) + || (index == lastIndex) + ? '\n' + : ' ')); + } + return; + } + if (format < (firstShortFormat())) { + lastIndex = ((64 < lastIndex) ? 64 : lastIndex); + + /* 0x/16r12345678 x 8 = 80/88 */ + elementsPerLine = 8; + for (index = 1; index <= lastIndex; index += 1) { + fprintf(aStream, + "%10p%c", + ((void *)(((usqInt)(long32At((void *)((oop + BaseHeaderSize) + ((((usqInt)((index - 1)) << 2))))))))), + (((index % elementsPerLine) == 0) + || (index == lastIndex) + ? '\n' + : ' ')); + } + return; + } + if (format < (firstByteFormat())) { + lastIndex = ((128 < lastIndex) ? 128 : lastIndex); + + /* 0x/16r1234 x 10 = 70/80 */ + elementsPerLine = 10; + for (index = 1; index <= lastIndex; index += 1) { + fprintf(aStream, + "%6p%c", + ((void *)(shortAt((void *)((oop + BaseHeaderSize) + ((((usqInt)((index - 1)) << 1))))))), + (((index % elementsPerLine) == 0) + || (index == lastIndex) + ? '\n' + : ' ')); + } + return; + } + lastIndex = ((0x100 < lastIndex) ? 0x100 : lastIndex); + + /* 0x/16r12 x 16 = 80/96 */ + elementsPerLine = 16; + for (index = 1; index <= lastIndex; index += 1) { + fprintf(aStream, + "%4p%c", + ((void *)(byteAt((void *)((oop + BaseHeaderSize) + (index - 1))))), + (((index % elementsPerLine) == 0) + || (index == lastIndex) + ? '\n' + : ' ')); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printObjectsFrom:to: */ +void +printObjectsFromto(sqInt startAddress, sqInt endAddress) +{ + sqInt oop; + + oop = objectBefore(startAddress); + oop = (oop + ? ((objectAfter(oop)) == startAddress + ? startAddress + : oop) + : startAddress); + while (oopisLessThan(oop, endAddress)) { + if (!((((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) + || (((longAt((void *)(oop))) & (classIndexMask())) == (segmentBridgePun())))) { + printOop(oop); + } + oop = objectAfter(oop); + } +} + + +/* Scan the heap printing the oops of any and all objects whose hash equals + the argument. + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printObjectsWithHash: */ +void +printObjectsWithHash(sqInt hash) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((long32At((void *)(objOop + 4))) & (identityHashHalfWordMask())) == hash) { + shortPrintOop(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((long32At((void *)(objOop + 4))) & (identityHashHalfWordMask())) == hash) { + shortPrintOop(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3:; +} + + /* SpurMemoryManager>>#printObjStackAndContents: */ +void +printObjStackAndContents(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt total; + + /* begin printObjStack:printContents: */ + if (objStack == GIV(nilObj)) { + print("nil"); + cr(); + } + else { + total = printObjStackPagemyIndexpageTypeprintContents(objStack, longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))), ObjStackMyx, 1); + fprintf(GIV(transcript), + "total %" PRIdSQINT "\n", + total); + } +} + + +/* Print the page and all pages accessible from it. Answer the total number + of objects on all non free pages. + */ + + /* SpurMemoryManager>>#printObjStackPage:myIndex:pageType:printContents: */ +static NoDbgRegParms sqInt +printObjStackPagemyIndexpageTypeprintContents(sqInt objStackPage, sqInt myx, sqInt pageType, sqInt printContents) +{ + sqInt freeOrNextPage; + sqInt index; + int isFirstPage; + int isFreePage; + int isNextPage; + sqInt n; + sqInt page; + sqInt totalContents; + + isFirstPage = pageType == ObjStackMyx; + isNextPage = pageType == ObjStackNextx; + isFreePage = pageType == ObjStackFreex; + printObjStackPagemyIndextag(objStackPage, myx, (isFirstPage + ? "head" + : (isFreePage + ? "free" + : "next"))); + if (isFirstPage + || (isNextPage)) { + /* begin tab */ + printChar('\t'); + print("topx: "); + printNum(longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))); + print(" next: "); + printHex(longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord()))))))); + if (isFirstPage) { + print(" free: "); + printHex(longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))))); + } + cr(); + } + if (isFirstPage) { + freeOrNextPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + while (freeOrNextPage != 0) { + printObjStackPagemyIndexpageTypeprintContents(freeOrNextPage, myx, ObjStackFreex, 0); + page = longAt((void *)((freeOrNextPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + if ((page == freeOrNextPage) + || (page == objStackPage)) { + print("circularity in free page list!!"); + cr(); + page = 0; + } + freeOrNextPage = page; + } + } + freeOrNextPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + totalContents = (freeOrNextPage + ? printObjStackPagemyIndexpageTypeprintContents(freeOrNextPage, myx, ObjStackNextx, printContents) + : 0); + if (printContents) { + n = 0; + index = (longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) + ObjStackNextx; + while (index >= ObjStackFixedSlots) { + /* begin space */ + printChar(' '); + printHex(longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))))); + index -= 1; + if (((n += 1)) >= 8) { + cr(); + n = 0; + } + } + if (((longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) + ObjStackNextx) >= ObjStackFixedSlots) { + cr(); + } + } + return (isFreePage + ? 0 + : totalContents + (longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))))); +} + + /* SpurMemoryManager>>#printObjStackPage:myIndex:tag: */ +static NoDbgRegParms void +printObjStackPagemyIndextag(sqInt objStackPage, sqInt myx, char *pageType) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fprintf(GIV(transcript), + "%s %p cx %d (%d) fmt %d (%d) sz %d (%d) myx: %d (%d%s\n", + pageType, + ((void *)objStackPage), + ((int) ((longAt((void *)(objStackPage))) & (classIndexMask()))), + ((int) (sixtyFourBitLongsClassIndexPun())), + ((int) ((byteAt((void *)(objStackPage + (formatFieldByteOffset())))) & (formatMask()))), + ((int) (sixtyFourBitIndexableFormat())), + ((int) (numSlotsOfAny(objStackPage))), + ObjStackPageSlots, + ((int) (longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))))), + ((int) myx), + ((byteAt((void *)(objStackPage + (markBitsByteOffset())))) & (1U << (markedBitByteShift())) + ? ") mkd" + : ") unmkd")); +} + + +/* useful for debugging */ + + /* SpurMemoryManager>>#printObjStack: */ +void +printObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt total; + + /* begin printObjStack:printContents: */ + if (objStack == GIV(nilObj)) { + print("nil"); + cr(); + } + else { + total = printObjStackPagemyIndexpageTypeprintContents(objStack, longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))), ObjStackMyx, 0); + fprintf(GIV(transcript), + "total %" PRIdSQINT "\n", + total); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printOopsExcept: */ +NeverInline void +printOopsExcept(sqInt (*function)(sqInt)) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt n; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + n = 0; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (!(function(objOop))) { + n += 1; + printEntity(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (!(function(objOop))) { + n += 1; + printEntity(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3: + if (n > 4) { + printNum(n); + print(" objects"); + cr(); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printOopsFrom:to: */ +void +printOopsFromto(sqInt startAddress, sqInt endAddress) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firstNonEntity; + sqInt inEmptySpace; + sqInt lastNonEntity; + sqInt limit; + sqInt oop; + + firstNonEntity = 0; + oop = objectBefore(startAddress); + limit = (((((usqIntptr_t)endAddress)) < GIV(endOfMemory)) ? (((usqIntptr_t)endAddress)) : GIV(endOfMemory)); + oop = (oop + ? ((objectAfter(oop)) == startAddress + ? startAddress + : oop) + : startAddress); + inEmptySpace = 0; + while (oopisLessThan(oop, limit)) { + printEntity(oop); + while (1) { + oop = objectAfter(oop); + if (!((long64At((void *)(oop))) == 0)) break; + if (!inEmptySpace) { + inEmptySpace = 1; + firstNonEntity = oop; + } + lastNonEntity = oop; + } + if (inEmptySpace) { + inEmptySpace = 0; + print("skipped empty space from "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)firstNonEntity)); + print(" to "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)lastNonEntity)); + cr(); + oop = objectStartingAt(oop); + } + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printOopsSuchThat: */ +NeverInline void +printOopsSuchThat(sqInt (*function)(sqInt)) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt n; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + n = 0; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (function(objOop)) { + n += 1; + printEntity(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (function(objOop)) { + n += 1; + printEntity(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3: + if (n > 4) { + printNum(n); + print(" objects"); + cr(); + } +} + + +/* Scan the heap printing the oops of any and all objects that refer to anOop */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printReferencesTo: */ +void +printReferencesTo(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt contextSize; + sqInt fmt; + sqInt followingWord; + usqInt followingWordAddress; + sqInt header; + sqInt headerSqInt; + sqInt i; + usqInt numLiterals; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt sp; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l2; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objSqInt))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l2: + i = ((usqInt) (CtxtTempFrameStart + contextSize)); + goto l3; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objSqInt)) > (isForwardedObjectClassIndexPun())); + i = (((numSlots = byteAt((void *)(objSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l3; + } + if (fmt == (forwardedFormat())) { + i = 1; + goto l3; + } + if (fmt < (firstCompiledMethodFormat())) { + i = 0; + goto l3; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objSqInt)); + headerSqInt = longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + i = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l3: + while (((i -= 1)) >= 0) { + if (anOop == (longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + printHex(objSqInt); + print(" @ "); + printNum(i); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + cr(); + i = 0; + } + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } + if (isonObjStack(anOop, GIV(markStack))) { + print("on markStack"); + cr(); + } + if (isonObjStack(anOop, GIV(weaklingStack))) { + print("on weaklingStack"); + cr(); + } + if (isonObjStack(anOop, GIV(mournQueue))) { + print("in mournQueue"); + cr(); + } + if ((((byteAt((void *)(anOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0) + && (isInRememberedSet(anOop))) { + print("in rememberedSet"); + cr(); + } +} + + /* SpurMemoryManager>>#printStringDataOf:on: */ +static NoDbgRegParms void +printStringDataOfon(sqInt oop, FILE *aStream) +{ + char *buffer; + sqInt byte; + sqInt i; + sqInt limit; + sqInt n; + unsigned int *wideBuffer; + int word; + + if (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())) { + buffer = ((char *) (alloca(0x400))); + n = (i = 0); + limit = (((numBytesOfBytes(oop)) < 0x100) ? (numBytesOfBytes(oop)) : 0x100); + while (n < limit) { + byte = byteAt((void *)((oop + BaseHeaderSize) + i)); + i += 1; + if ((byte < 32) + && (byte != 9)) { + buffer[n] = '<'; + n += 1; + if ((byte == 10) + || (byte == 13)) { + if (byte == 10) { + buffer[n] = 'L'; + buffer[n + 1] = 'F'; + } + else { + buffer[n] = 'C'; + buffer[n + 1] = 'R'; + } + n += 2; + } + else { + if (byte >= 10) { + buffer[n] = ((byte / 10) + (((sqInt)'0'))); + n += 1; + } + buffer[n] = ((byte % 10) + (((sqInt)'0'))); + n += 1; + } + buffer[n] = '>'; + n += 1; + } + else { + buffer[n] = byte; + n += 1; + } + } + fprintf(aStream, + "%.*s%s\n", + ((int) n), + buffer, + ((numBytesOfBytes(oop)) > limit + ? "..." + : "")); + } + else { + assert(isWordsNonImm(oop)); + wideBuffer = ((unsigned int *) (alloca(0x1000))); + n = (i = 0); + limit = (((lengthOf(oop)) < 0x100) ? (lengthOf(oop)) : 0x100); + while (i < limit) { + word = long32At((void *)((oop + BaseHeaderSize) + ((((usqInt)(i) << 2))))); + i += 1; + if ((word < 32) + && (word != 9)) { + wideBuffer[n] = '<'; + n += 1; + if ((word == 10) + || (word == 13)) { + if (word == 10) { + wideBuffer[n] = 'L'; + wideBuffer[n + 1] = 'F'; + } + else { + wideBuffer[n] = 'C'; + wideBuffer[n + 1] = 'R'; + } + n += 2; + } + else { + if (word >= 10) { + wideBuffer[n] = ((word / 10) + (((sqInt)'0'))); + n += 1; + } + wideBuffer[n] = ((word % 10) + (((sqInt)'0'))); + n += 1; + } + wideBuffer[n] = '>'; + n += 1; + } + else { + wideBuffer[n] = word; + n += 1; + } + } + fwprintf(aStream, + L"%.*ls%s\n", + ((int) n), + wideBuffer, + ((lengthOf(oop)) > limit + ? "..." + : "")); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printUnmarkedOops */ +#if LLDB +void +printUnmarkedOops(void) +{ + printOopsExcept(isMarked); +} +#endif /* LLDB */ + + +/* Print all objects that have weak array format. */ + + /* SpurMemoryManager>>#printWeaklings */ +void +printWeaklings(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt format; + sqInt nSoFar; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + nSoFar = 0; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + format = (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask()); + if (format == (weakArrayFormat())) { + printf("0x%" PRIxSQINT "", obj); + if (((nSoFar += 1)) >= 4) { + nSoFar = 0; + cr(); + } + else { + /* begin space */ + printChar(' '); + } + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + if (nSoFar > 0) { + cr(); + } +} + + +/* Attempt to push anEphemeron on the unscanned ephemerons stack and answer + if the attempt succeeded. Note that the ephemeron stack overflowing isn't + a disaster; it simply means treating the ephemeron as strong in this GC + cycle. */ + + /* SpurMemoryManager>>#pushOnUnscannedEphemeronsStack: */ +static NoDbgRegParms sqInt +pushOnUnscannedEphemeronsStack(sqInt anEphemeron) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(isEphemeron(anEphemeron)); + if (((GIV(unscannedEphemerons).top)) >= ((GIV(unscannedEphemerons).limit))) { + return 0; + } + longAtput((void *)((GIV(unscannedEphemerons).top = ((GIV(unscannedEphemerons).top)) + BytesPerOop)),anEphemeron); + return 1; +} + + +/* Record the given object in a the remap buffer. Objects in this buffer are + remapped when a compaction occurs. This facility is used by the + interpreter to ensure that + objects in temporary variables are properly remapped. + We support this excessence for compatibility with ObjectMemory. + Spur doesn't GC during allocation. */ + + /* SpurMemoryManager>>#pushRemappableOop: */ +void +pushRemappableOop(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(addressCouldBeOop(oop)); + GIV(remapBuffer)[(GIV(remapBufferCount) += 1)] = oop; + if (!(GIV(remapBufferCount) <= RemapBufferSize)) { + error("remapBuffer overflow"); + } +} + + +/* Add the ephemeron or weak array to the queue. */ +/* If an ephemeron it shouldn't be added twice. */ + + /* SpurMemoryManager>>#queueMourner: */ +static NoDbgRegParms void +queueMourner(sqInt anEphemeronOrWeakArray) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt objStack; + + assert(!((((formatOf(anEphemeronOrWeakArray)) != (weakArrayFormat())) + && (isonObjStack(anEphemeronOrWeakArray, GIV(mournQueue)))))); + + /* it should be a weak array or an ephemeron */ + assert((isNonImmediate(anEphemeronOrWeakArray)) + && (((formatOf(anEphemeronOrWeakArray)) == (ephemeronFormat())) + || ((formatOf(anEphemeronOrWeakArray)) == (weakArrayFormat())))); + ensureRoomOnObjStackAt(MournQueueRootIndex); + + /* There is no point queueing weak arrays more than once. Note that it should be impossible + for ephemerons to be enqueued more than once since they are turned into non-ephemerons + in the relevant sender. */ + + /* begin isEphemeron: */ + assert(isNonImmediate(anEphemeronOrWeakArray)); + format = (byteAt((void *)(anEphemeronOrWeakArray + (formatFieldByteOffset())))) & (formatMask()); + if (!(format == (ephemeronFormat()))) { + if (isonObjStack(anEphemeronOrWeakArray, GIV(mournQueue))) { + return; + } + } + objStack = GIV(mournQueue); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(anEphemeronOrWeakArray)); + if (((anEphemeronOrWeakArray & (tagMask())) != 0)) { + assert(objStack == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack)) + ? fetchPointerofObject(ObjStackNextx, objStack) + : objStack)))); + } + else { + assert(!((objStack == GIV(markStack)) + && (isWeakNonImm(anEphemeronOrWeakArray)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack != GIV(weaklingStack)) + || (isWeakNonImm(anEphemeronOrWeakArray))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(anEphemeronOrWeakArray, objStack); +} + + /* SpurMemoryManager>>#rawHashBitsOf: */ +int +rawHashBitsOf(sqInt objOop) +{ + return (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()); +} + + +/* Answer the tag bits for the receiver based on the method's methodClass, if + any. These bits are extended with a bit that says that the method may have + a Context receiver, i.e. + mclass is Context or its superclasses. The absence of this bit is used to + avoid expensive store checks, etc. */ + + /* SpurMemoryManager>>#receiverTagBitsForMethod: */ +sqInt +receiverTagBitsForMethod(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classContextOrSuperclass; + sqInt literal; + sqInt methodClassOrNil; + sqInt objOop; + sqInt offset; + + /* begin methodClassOf: */ + offset = (literalCountOf(aMethodObj)) - 1; + literal = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = fixFollowedFieldofObjectwithInitialValue(offset + LiteralStart, aMethodObj, literal); + } + methodClassOrNil = ((literal != GIV(nilObj)) + && (/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + ? (assert((numSlotsOf(literal)) > ValueIndex), + followFieldofObject(ValueIndex, literal)) + : GIV(nilObj)); + if (methodClassOrNil == GIV(nilObj)) { + return (1U << (numTagBits())); + } + + /* If we don't know the methodClass be pessimal + Mixins don't necessarily have a format inst var; filter out non-integer format. */ + if (((((usqInt)((((longAt((void *)((methodClassOrNil + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) == (forwardedFormat())) { + /* begin receiverTagBitsForImmediateMethodClass: */ + if (methodClassOrNil == (longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)((smallIntegerTag())) << (shiftForWord())))))))) { + return smallIntegerTag(); + } + if (methodClassOrNil == (longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)((characterTag())) << (shiftForWord())))))))) { + return characterTag(); + } + assert(methodClassOrNil == (fetchPointerofObject(smallFloatTag(), GIV(classTableFirstPage)))); + return smallFloatTag(); + } + + /* Now check if mclass is one of Context's superclasses... */ + + /* begin knownClassAtIndex: */ + assert(((ClassMethodContextCompactIndex >= 1) && (ClassMethodContextCompactIndex <= (classTablePageSize())))); + classContextOrSuperclass = longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassMethodContextCompactIndex) << (shiftForWord())))))); + do { + if (classContextOrSuperclass == methodClassOrNil) { + return (1U << (numTagBits())); + } + + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classContextOrSuperclass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classContextOrSuperclass, objOop); + } + classContextOrSuperclass = objOop; + } while(classContextOrSuperclass != GIV(nilObj)); + return 0; +} + + +/* Ephemerons in the mourn queue will have been fired (had their + ephemeron-ness turned off), + but the ephemeron queue is not saved in the snapshot. So the snapshotted + ephemerons need to be unfired so they're still unqueued ephemerons in the + loaded image. + This must be undone post snapshot. */ + + /* SpurMemoryManager>>#refireQueuedEphemeronsPostSnapshot */ +static void +refireQueuedEphemeronsPostSnapshot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt iSqInt; + sqInt mourner; + sqInt numOnThisPage; + sqInt objStackPage; + + /* begin objStack:do: */ + if (GIV(mournQueue) == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(GIV(mournQueue))); + objStackPage = GIV(mournQueue); + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + mourner = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (((!(mourner & (tagMask())))) + && (((byteAt((void *)(mourner + (formatFieldByteOffset())))) & (formatMask())) == (ephemeronFormat()))) { + /* begin setFormatOf:to: */ + assert((((nonIndexablePointerFormat()) >= 0) && ((nonIndexablePointerFormat()) <= (formatMask())))); + byteAtput((void *)(mourner + (formatFieldByteOffset())),((byteAt((void *)(mourner + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (nonIndexablePointerFormat())); + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l1:; +} + + +/* Relocate all objStack pages that comprise objStack, including contents if + requested. + */ + + /* SpurMemoryManager>>#relocateObjStackForPlanningCompactor:andContents: */ +static NoDbgRegParms sqInt +relocateObjStackForPlanningCompactorandContents(sqInt objStack, sqInt relocateContents) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt finishIndex; + sqInt freeList; + sqInt fwd; + sqInt i; + sqInt next; + sqInt oop; + sqInt relocated; + sqInt result; + sqInt stackOrNil; + + next = 0; + if (objStack == GIV(nilObj)) { + return objStack; + } + assert((relocateContents + ? (rawHashBitsOf(objStack)) < ObjStackLimit + : (rawHashBitsOf(objStack)) == 0)); + stackOrNil = objStack; + freeList = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + while (1) { + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + + /* There are four fixed slots in an obj stack, and a Topx of 0 indicates empty, so + if there were 5 slots in an oop stack, full would be 2, and the last 0-rel index is 4. + Hence the last index is topx + fixed slots - 1, or topx + ObjStackNextx. The first + two slots, Topx and Myx are not object references. */ + next = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + finishIndex = ObjStackNextx + ((relocateContents + ? (long32At((void *)(stackOrNil + 4))) & (identityHashHalfWordMask()) + : 0)); + + /* begin relocateObjectsInHeapEntity:from:to: */ + for (i = ObjStackFreex; i <= finishIndex; i += 1) { + oop = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert(isMarked(oop)); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stackOrNil)) + && (!(isForwarded(stackOrNil)))); + assert(validStorePointerUncheckedArgs(i, stackOrNil, fwd)); + longAtput((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + relocated = (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(stackOrNil, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(stackOrNil + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)) + ? longAt((void *)((stackOrNil + BaseHeaderSize) + (0U << (shiftForWord())))) + : stackOrNil); + if (stackOrNil == objStack) { + result = relocated; + } + if (!(next != 0)) break; + stackOrNil = next; + } + while (freeList != 0) { + assert((numSlotsOfAny(freeList)) == ObjStackPageSlots); + next = longAt((void *)((freeList + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + + /* begin relocateObjectsInHeapEntity:from:to: */ + for (i = ObjStackFreex; i <= ObjStackFreex; i += 1) { + oop = longAt((void *)((freeList + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert(isMarked(oop)); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(freeList)) + && (!(isForwarded(freeList)))); + assert(validStorePointerUncheckedArgs(i, freeList, fwd)); + longAtput((void *)((freeList + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + if (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(freeList, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(freeList + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0))) { + /* fetchPointer:ofObject: */ + longAt((void *)((freeList + BaseHeaderSize) + (0U << (shiftForWord())))); + } + else { + } + freeList = next; + } + return relocated; +} + + +/* Remove the given variable location to the extra roots table. */ + + /* SpurMemoryManager>>#removeGCRoot: */ +sqInt +removeGCRoot(sqInt *varLoc) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int i; + + for (i = 1; i <= GIV(extraRootCount); i++) { + if (varLoc == (GIV(extraRoots)[i])) { + GIV(extraRoots)[i] = (GIV(extraRoots)[GIV(extraRootCount)]); + GIV(extraRootCount) -= 1; + return 1; + } + } + return 0; +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. */ + + /* SpurMemoryManager>>#return:restoringObjectsIn:savedFirstFields:and:savedHashes: */ +static NoDbgRegParms sqInt +returnrestoringObjectsInsavedFirstFieldsandsavedHashes(sqInt errCode, sqInt reachableObjectsArray, sqInt savedFirstFields, sqInt outPointersArray, sqInt savedHashes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int hash; + sqInt i; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt toDoLimit; + sqInt valuePointer; + + /* begin restoreObjectsIn:upTo:savedFirstFields: */ + /* begin numSlotsOf: */ + assert((classIndexOf(reachableObjectsArray)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(reachableObjectsArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(reachableObjectsArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + for (i = 0; i < numSlots; i += 1) { + oop = longAt((void *)((reachableObjectsArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + valuePointer = longAt((void *)((savedFirstFields + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(oop)) + && (!(isForwarded(oop)))); + assert(validStorePointerUncheckedArgs(0, oop, valuePointer)); + longAtput((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + if (oopisGreaterThanOrEqualToandLessThan(savedFirstFields, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedFirstFields); + } + + /* begin restoreObjectsIn:savedHashes: */ + toDoLimit = ((assert((classIndexOf(outPointersArray)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(outPointersArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointersArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + if (((hash = long32At((void *)((savedHashes + BaseHeaderSize) + ((((usqInt)(i) << 2))))))) > (identityHashHalfWordMask())) { + if (oopisGreaterThanOrEqualToandLessThan(savedHashes, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedHashes); + } + goto l1; + } + oop = longAt((void *)((outPointersArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(oop + 4),((((long32At((void *)(oop + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + if (oopisGreaterThanOrEqualToandLessThan(savedHashes, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedHashes); + } + /* end restoreObjectsIn:savedHashes: */ +l1: + freeObject(reachableObjectsArray); + runLeakCheckerFor(GCModeImageSegment); + assert(allObjectsUnmarked()); + return errCode; +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. */ + + /* SpurMemoryManager>>#return:restoringObjectsIn:upTo:savedFirstFields: */ +static NoDbgRegParms sqInt +returnrestoringObjectsInupTosavedFirstFields(sqInt errCode, sqInt reachableObjectsArray, sqInt limitOrTag, sqInt savedFirstFields) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt valuePointer; + + /* begin restoreObjectsIn:upTo:savedFirstFields: */ + numSlots = (limitOrTag == -1 + ? (/* begin numSlotsOf: */ + assert((classIndexOf(reachableObjectsArray)) > (isForwardedObjectClassIndexPun())), + (((numSlotsUsqInt = byteAt((void *)(reachableObjectsArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(reachableObjectsArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt)) + : limitOrTag); + for (i = 0; i < numSlots; i += 1) { + oop = longAt((void *)((reachableObjectsArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + valuePointer = longAt((void *)((savedFirstFields + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(oop)) + && (!(isForwarded(oop)))); + assert(validStorePointerUncheckedArgs(0, oop, valuePointer)); + longAtput((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + if (oopisGreaterThanOrEqualToandLessThan(savedFirstFields, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedFirstFields); + } + freeObject(reachableObjectsArray); + runLeakCheckerFor(GCModeImageSegment); + assert(allObjectsUnmarked()); + return errCode; +} + + +/* This exists to get around a compiler bug in Apple LLVM version 7.0.0 + (clang-700.1.76) that was avoiding the second comparison of segVersion + after the first byte swap. */ + + /* SpurMemoryManager>>#reverseBytesIn32BitWordsIn: */ +static NoDbgRegParms NeverInline void +reverseBytesIn32BitWordsIn(sqInt segmentWordArray) +{ + sqInt addr; + sqInt stopAddr; + + stopAddr = addressAfter(segmentWordArray); + + /* begin reverseBytesIn32BitWordsFrom:to: */ + addr = segmentWordArray + BaseHeaderSize; + while (oopisLessThan(addr, stopAddr)) { + long32Atput((void *)(addr),SQ_SWAP_4_BYTES((long32At((void *)(addr))))); + addr += 4; + } +} + + +/* Check free space integrity by setting bits in the map corresponding to all + free space objects + and checking tat no pointer field refers to a free object. anOopOrNil is + provided to filter-out + the as-yet-to-be initialized object in + primitiveShallowCopy/primitiveClone. + */ + + /* SpurMemoryManager>>#runLeakCheckerForFreeSpace:ignoring: */ +static NoDbgRegParms void +runLeakCheckerForFreeSpaceignoring(sqInt gcModes, sqInt anOopOrNil) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (((gcModes & GCCheckFreeSpace) != 0)) { + reverseDisplayFromto(16, 19); + clearLeakMapAndMapAccessibleFreeSpace(); + GIV(freeSpaceCheckOopToIgnore) = anOopOrNil; + asserta(checkHeapFreeSpaceIntegrity()); + GIV(freeSpaceCheckOopToIgnore) = null; + } +} + + /* SpurMemoryManager>>#runLeakCheckerFor: */ +static NoDbgRegParms void +runLeakCheckerFor(sqInt gcModes) +{ + /* begin inLineRunLeakCheckerFor:excludeUnmarkedObjs:classIndicesShouldBeValid: */ + if (((gcModes & checkForLeaks) != 0)) { + if (((gcModes & GCModeFull) != 0)) { + reverseDisplayFromto(0, 7); + } + else { + reverseDisplayFromto(8, 15); + } + clearLeakMapAndMapAccessibleObjects(); + asserta(checkHeapIntegrityclassIndicesShouldBeValid(0, 1)); + asserta((checkInterpreterIntegrity()) == 0); + asserta(checkStackIntegrity()); + asserta(checkCodeIntegrity(gcModes)); + if (((gcModes & GCCheckFreeSpace) != 0)) { + clearLeakMapAndMapAccessibleFreeSpace(); + asserta(checkHeapFreeSpaceIntegrity()); + } + } + return; +} + + /* SpurMemoryManager>>#runLeakCheckerFor:excludeUnmarkedObjs:classIndicesShouldBeValid: */ +static NoDbgRegParms void +runLeakCheckerForexcludeUnmarkedObjsclassIndicesShouldBeValid(sqInt gcModes, sqInt excludeUnmarkedObjs, sqInt classIndicesShouldBeValid) +{ + /* begin inLineRunLeakCheckerFor:excludeUnmarkedObjs:classIndicesShouldBeValid: */ + if (((gcModes & checkForLeaks) != 0)) { + if (((gcModes & GCModeFull) != 0)) { + reverseDisplayFromto(0, 7); + } + else { + reverseDisplayFromto(8, 15); + } + clearLeakMapAndMapAccessibleObjects(); + asserta(checkHeapIntegrityclassIndicesShouldBeValid(excludeUnmarkedObjs, classIndicesShouldBeValid)); + asserta((checkInterpreterIntegrity()) == 0); + asserta(checkStackIntegrity()); + asserta(checkCodeIntegrity(gcModes)); + if (((gcModes & GCCheckFreeSpace) != 0)) { + clearLeakMapAndMapAccessibleFreeSpace(); + asserta(checkHeapFreeSpaceIntegrity()); + } + } +} + + /* SpurMemoryManager>>#safePrintStringOf: */ +static NoDbgRegParms void +safePrintStringOf(sqInt oop) +{ + printStringOf(/* followMaybeForwarded: */ + (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + ? followForwarded(oop) + : oop)); + return; +} + + +/* Run the scavenger. */ + + /* SpurMemoryManager>>#scavengingGCTenuringIf: */ +static NoDbgRegParms void +scavengingGCTenuringIf(sqInt tenuringCriterion) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt n; + sqInt probe; + + assert(GIV(remapBufferCount) == 0); + if (!(asserta(((((GIV(eden)).limit)) - GIV(freeStart)) > (interpreterAllocationReserveBytes())))) { + /* begin tab */ + printChar('\t'); + n = (((GIV(eden)).limit)) - GIV(freeStart); + + /* begin printNum: */ + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)n)); + printChar(' '); + n = interpreterAllocationReserveBytes(); + + /* begin printNum: */ + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)n)); + printChar(' '); + n = (interpreterAllocationReserveBytes()) - ((((GIV(eden)).limit)) - GIV(freeStart)); + + /* begin printNum: */ + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)n)); + cr(); + } + + /* begin checkMemoryMap */ + assert(isYoungObject(GIV(newSpaceStart))); + assert(!(isOldObject(GIV(newSpaceStart)))); + assert(isYoungObject(GIV(oldSpaceStart) - BytesPerWord)); + assert(!(isOldObject(GIV(oldSpaceStart) - BytesPerWord))); + assert(!(isYoungObject(GIV(oldSpaceStart)))); + assert(isOldObject(GIV(oldSpaceStart))); + assert(!(isYoungObject(GIV(endOfMemory)))); + assert(isOldObject(GIV(endOfMemory))); + + /* we would like the following to be true, but we either choose one boundary check for + cogMethods vs objects (isMachineCodeFrame: et al) or one boundary check for + copyAndForward:. We can't have both, and the former is likely the highest dynamic + frequency. */ + assert(!(isOldObject(minCogMethodAddress()))); + assert(!(isOldObject(maxCogMethodAddress()))); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + runLeakCheckerFor(GCModeNewSpace); + preGCAction(GCModeNewSpace); + + /* begin flushMethodCacheFrom:to: */ + probe = 0; + for (i = 1; i <= MethodCacheEntries; i += 1) { + if (GIV(methodCache)[probe + MethodCacheSelector]) { + if (((oopisGreaterThanOrEqualTo(GIV(methodCache)[probe + MethodCacheSelector], GIV(newSpaceStart))) + && (oopisLessThan(GIV(methodCache)[probe + MethodCacheSelector], GIV(oldSpaceStart)))) + || ((oopisGreaterThanOrEqualTo(GIV(methodCache)[probe + MethodCacheMethod], GIV(newSpaceStart))) + && (oopisLessThan(GIV(methodCache)[probe + MethodCacheMethod], GIV(oldSpaceStart))))) { + GIV(methodCache)[probe + MethodCacheSelector] = 0; + } + } + probe += MethodCacheEntrySize; + } + + /* would prefer this to be in mapInterpreterOops, but + compatibility with ObjectMemory dictates it goes here. */ + GIV(needGCFlag) = 0; + GIV(gcStartUsecs) = ioUTCMicrosecondsNow(); + doScavenge(tenuringCriterion); + GIV(statScavenges) += 1; + GIV(statGCEndUsecs) = ioUTCMicrosecondsNow(); + GIV(statSGCDeltaUsecs) = GIV(statGCEndUsecs) - GIV(gcStartUsecs); + GIV(statScavengeGCUsecs) += GIV(statSGCDeltaUsecs); + GIV(statRootTableCount) = GIV(rememberedSetSize); + + /* begin logScavenge */ + if (GIV(scavengeLog)) { + writeScavengeLog(); + } + postGCAction(GCModeNewSpace); + runLeakCheckerFor(GCModeNewSpace); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } +} + + +/* Set the dimensions of the heap, answering the start of oldSpace. edenBytes + holds the desired ``size of eden'' + which is actually the total size of new space minus the reserve. edenBytes + is then divided up between eden + and the two survivor spaces, where each survivor space is a + scavengerDenominator (one seventh) of the total. */ +/* Transcript + cr; nextPutAll: 'heapBase: '; print: baseOfHeap; nextPut: $/; nextPutAll: + baseOfHeap hex; + nextPutAll: ' memLimit '; print: memLimit; nextPut: $/; nextPutAll: + memLimit hex; + nextPutAll: ' memEnd '; print: memEnd; nextPut: $/; nextPutAll: memEnd + hex; cr; flush. */ +/* This is more than a little counter-intuitive. Eden must include + interpreterAllocationReserveBytes. + */ + + /* SpurMemoryManager>>#setHeapBase:memoryLimit:endOfMemory: */ +static NoDbgRegParms NeverInline sqInt +setHeapBasememoryLimitendOfMemory(sqInt baseOfHeap, sqInt memLimit, sqInt memEnd) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt actualEdenBytes; + sqInt previousFSStart; + sqInt reserve; + sqInt survivorBytes; + sqInt totalBytes; + + reserve = interpreterAllocationReserveBytes(); + GIV(newSpaceStart) = baseOfHeap; + GIV(oldSpaceStart) = (baseOfHeap + GIV(edenBytes)) + reserve; + totalBytes = GIV(oldSpaceStart) - GIV(newSpaceStart); + + /* begin newSpaceStart:newSpaceBytes:survivorBytes: */ + survivorBytes = ((((GIV(oldSpaceStart) - GIV(newSpaceStart)) - reserve) / 7 /* scavengerDenominator */) & ~7); + actualEdenBytes = (((totalBytes - survivorBytes) - survivorBytes) & ~7); + assert((((totalBytes - actualEdenBytes) - survivorBytes) - survivorBytes) < (allocationUnit())); + if (((survivorBytes * 2) + actualEdenBytes) > ((0x200000000LL))) { + error("requested new space size exceeds 8Gb limit imposed by ephemeron/weak implementation."); + } + + /* for tenuring we require older objects below younger objects. since allocation + grows up this means that the survivor spaces must precede eden. */ + (GIV(pastSpace).start = GIV(newSpaceStart)); + (GIV(pastSpace).limit = GIV(newSpaceStart) + survivorBytes); + (GIV(futureSpace).start = (GIV(pastSpace).limit)); + (GIV(futureSpace).limit = ((GIV(pastSpace).limit)) + survivorBytes); + (GIV(eden).start = (GIV(futureSpace).limit)); + (GIV(eden).limit = GIV(newSpaceStart) + totalBytes); + assert((((GIV(futureSpace)).limit)) <= (GIV(newSpaceStart) + totalBytes)); + assert((((((GIV(eden)).start)) % (allocationUnit())) + ((((GIV(eden)).limit)) % (allocationUnit()))) == 0); + assert((((((GIV(pastSpace)).start)) % (allocationUnit())) + ((((GIV(pastSpace)).limit)) % (allocationUnit()))) == 0); + assert((((((GIV(futureSpace)).start)) % (allocationUnit())) + ((((GIV(futureSpace)).limit)) % (allocationUnit()))) == 0); + + /* begin initFutureSpaceStart */ + previousFSStart = GIV(futureSurvivorStart); + GIV(futureSurvivorStart) = (GIV(futureSpace).start); + GIV(tenuringProportion) = 0.9; + GIV(freeStart) = ((GIV(eden)).start); + GIV(pastSpaceStart) = ((GIV(pastSpace)).start); + GIV(freeOldSpaceStart) = memEnd; + GIV(endOfMemory) = memLimit; + return baseOfHeap; +} + + /* SpurMemoryManager>>#setIsPinnedOf:to: */ +static NoDbgRegParms void +setIsPinnedOfto(sqInt objOop, sqInt aBoolean) +{ + assert((isOldObject(objOop)) + && (!(isFreeObject(objOop)))); + byteAtput((void *)(objOop + (formatFieldByteOffset())),(aBoolean + ? (byteAt((void *)(objOop + (formatFieldByteOffset())))) | (1U << (pinnedBitByteShift())) + : (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (0xFF - (1U << (pinnedBitByteShift()))))); +} + + /* SpurMemoryManager>>#setIsRememberedOf:to: */ +static NoDbgRegParms void +setIsRememberedOfto(sqInt objOop, sqInt aBoolean) +{ + assert(!((isFreeObject(objOop)))); + byteAtput((void *)(objOop + (formatFieldByteOffset())),(aBoolean + ? (byteAt((void *)(objOop + (formatFieldByteOffset())))) | (1U << (rememberedBitByteShift())) + : (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (0xFF - (1U << (rememberedBitByteShift()))))); +} + + +/* Reduce the number of indexable fields in objOop, an arrayFormat or + longFormat object, to nSlots. + Convert the unused residual to a free chunk (if in oldSpace). + Answer the number of bytes returned to free memory, which may be zero. */ + + /* SpurMemoryManager>>#shorten:toIndexableSize: */ +sqInt +shortentoIndexableSize(sqInt objOop, sqInt indexableSize) +{ + sqInt delta; + + assert((indexableSize >= 0) + && (indexableSize < (lengthOf(objOop)))); + delta = doShortentoIndexableSize(objOop, indexableSize); + assert((lengthOf(followMaybeForwarded(objOop))) == indexableSize); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + return delta; +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#shortPrintObjectsFrom:to: */ +void +shortPrintObjectsFromto(sqInt startAddress, sqInt endAddress) +{ + sqInt oop; + + oop = objectBefore(startAddress); + oop = (oop + ? ((objectAfter(oop)) == startAddress + ? startAddress + : oop) + : startAddress); + while (oopisLessThan(oop, endAddress)) { + if (!(((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun()))) { + shortPrintOop(oop); + } + oop = objectAfter(oop); + } +} + + +/* Answer if the oop should be scavenged.. The method is called + shouldRemapOop: for compatibility with ObjectMemory. */ + + /* SpurMemoryManager>>#shouldRemapOop: */ +sqInt +shouldRemapOop(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (shouldRemapObj(oop)); +} + + /* SpurMemoryManager>>#sizeOfObjStack: */ +static NoDbgRegParms sqInt +sizeOfObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objStackPage; + sqInt total; + + if (objStack == GIV(nilObj)) { + return 0; + } + total = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + objStackPage = objStack; + while (1) { + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + if (!(objStackPage != 0)) break; + total += ObjStackLimit; + assert((fetchPointerofObject(ObjStackTopx, objStackPage)) == ObjStackLimit); + } + return total; +} + + /* SpurMemoryManager>>#slidingCompactionInProgress */ +static int +slidingCompactionInProgress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(gcPhaseInProgress) == SlidingCompactionInProgress; +} + + +/* *DO NOT CONFUSE THIS WITH numSlotsOf:. + This is an ObjectMemory compatibility method with questionable semantics. + Answers the number of slots in the receiver. + If the receiver is a byte object, return the number of bytes. + If the receiver is a short object, return the number of shorts. + If the receiver is a word object, return the number of words. + If the receiver is a double word object, return the number of double + words. Otherwise return the number of pointers. */ + + /* SpurMemoryManager>>#slotSizeOf: */ +sqInt +slotSizeOf(sqInt oop) +{ + sqInt fmt; + usqInt numSlots; + usqInt numSlotsUsqInt; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin lengthOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + +/* Return one of the objects in the specialObjectsArray */ + + /* SpurMemoryManager>>#splObj: */ +sqInt +splObj(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); +} + + +/* Answer the start of objOop, which is either the address of the overflow + size word, or objOop itself, depending on the size of the object. This may + be applied to any kind of object, normal, forwarders or free chunks. */ + + /* SpurMemoryManager>>#startOfObject: */ +static NoDbgRegParms usqInt +startOfObject(sqInt objOop) +{ + return ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); +} + + +/* Part of InterpreterProxy's 1.14 API */ + + /* SpurMemoryManager>>#statNumGCs */ +sqInt +statNumGCs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (GIV(statScavenges) + GIV(statIncrGCs)) + GIV(statFullGCs); +} + + +/* A renaming for the Cogit, which can't make sense of GIV(oldSpaceStart) */ + + /* SpurMemoryManager>>#storeCheckBoundary */ +usqInt +storeCheckBoundary(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(oldSpaceStart); +} + + +/* This primitive is called from Squeak as... + storeSegmentFor: arrayOfRoots into: aWordArray outPointers: + anArray. + This primitive will store a binary image segment (in the same format as + objects in the heap) of the + set of objects in arrayOfObjects. All pointers from within the set to + objects outside the set will be + copied into the array of outPointers. In their place in the image segment + will be an oop equal to the + offset in the outPointer array (the first would be 8), but with the high + bit set. + + Since Spur has a class table the load primitive must insert classes that + have instances into the + class table. This primitive marks such classes using the isRemembered bit, + which isn't meaningful + as a remembered bit in the segment. + + The primitive expects the segmentWordArray and outPointerArray to be more + than adequately long. + In this case it returns normally, and truncates the two arrays to exactly + the right size. + + The primitive can fail for the following reasons with the specified + failure codes: + PrimErrGenericFailure: the segmentWordArray is too small for the version + stamp PrimErrWritePastObject: the segmentWordArray is too small to + contain the reachable objects + PrimErrBadIndex: the outPointerArray is too small + PrimErrNoMemory: there is insufficient free space to store the array + answered by objectsReachableFromRoots:, + or the savedFirstFields and savedOutHashes arrays. + PrimErrNeedCompaction: a GC is needed to make room for the array answered + by objectsReachableFromRoots: + PrimErrLimitExceeded: there is no room in the hash field to store out + pointer indices or class references, + or the outPointerArray is larger than the max value of the hash field. + PrimErrNoModification: the segmentWordArrayArg or outPointerArrayArg are + immutable PrimErrObjectIsPinned: the segmentWordArrayArg or + outPointerArrayArg are pinned */ + + /* SpurMemoryManager>>#storeImageSegmentInto:outPointers:roots: */ +static NoDbgRegParms sqInt +storeImageSegmentIntooutPointersroots(sqInt segmentWordArrayArg, sqInt outPointersArrayArg, sqInt arrayOfRootsArg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayOfObjects; + sqInt arrayOfRoots; + sqInt delta; + usqInt endSeg; + int hash; + sqInt here; + sqInt i; + sqInt indexableSize; + sqInt newSegAddrOrError; + sqInt numClassesInSegment; + usqInt numSlots; + sqInt numSlotsSqInt; + sqInt objOop; + sqInt objOopSqInt; + sqInt outIndex; + sqInt outPointersArray; + usqInt p; + sqInt savedFirstFields; + sqInt savedOutHashes; + usqInt segAddr; + sqInt segmentWordArray; + usqInt segStart; + sqInt tempObjOop; + sqInt there; + sqInt toDoLimit; + sqInt toDoLimitSqInt; + usqInt toDoLimitUsqInt; + + if (( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(segmentWordArrayArg + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + ) + || ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(outPointersArrayArg + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + )) { + return PrimErrNoModification; + } + + /* Since segmentWordArrayArg & outPointersArrayArg may get shortened, they can't be pinned. */ + if ((((byteAt((void *)(segmentWordArrayArg + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0) + || (((byteAt((void *)(outPointersArrayArg + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)) { + return PrimErrObjectIsPinned; + } + if (((/* begin numSlotsOf: */ + assert((classIndexOf(outPointersArrayArg)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(outPointersArrayArg + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointersArrayArg - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > (identityHashHalfWordMask())) { + return PrimErrLimitExceeded; + } + runLeakCheckerFor(GCModeImageSegment); + + /* First scavenge to collect any new space garbage that refers to the graph. */ + + /* begin scavengingGC */ + scavengingGCTenuringIf(TenureByAge); + segmentWordArray = /* updatePostScavenge: */ + ((!((longAt((void *)(segmentWordArrayArg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(segmentWordArrayArg) + : segmentWordArrayArg); + outPointersArray = /* updatePostScavenge: */ + ((!((longAt((void *)(outPointersArrayArg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(outPointersArrayArg) + : outPointersArrayArg); + arrayOfRoots = /* updatePostScavenge: */ + ((!((longAt((void *)(arrayOfRootsArg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(arrayOfRootsArg) + : arrayOfRootsArg); + assert(!((forwardersIn(outPointersArray)))); + assert(!((forwardersIn(arrayOfRoots)))); + + /* Now compute the transitive closure, collecting the sequence of objects to be stored in the arrayOfObjects array. + Included in arrayOfObjects are the arrayOfRoots and all its contents. All objects have been unmarked. */ + arrayOfObjects = objectsReachableFromRoots(arrayOfRoots); + if (!arrayOfObjects) { + return PrimErrNoMemory; + } + + /* If objectsReachableFromRoots: answers an integer there is not enough continuous free space in which to allocate the + reachable objects. If there is sufficient free space then answer an error code to prompt a compacting GC and a retry. */ + if ((((arrayOfObjects) & 7) == 1)) { + if ((GIV(totalFreeOldSpace) - 8 /* allocationUnit */) >= ((arrayOfObjects >> 3))) { + return PrimErrNeedCompaction; + } + return PrimErrNoMemory; + } + assert(allObjectsUnmarked()); + assert(!((forwardersIn(arrayOfObjects)))); + runLeakCheckerFor(GCModeImageSegment); + + /* Both to expand the max size of segment and to reduce the length of the + load-time pass that adds classes to the class table, move classes to the + front of arrayOfObjects, leaving the root array as the first element. */ + + /* begin moveClassesForwardsIn: */ + /* if > 0, this is the index of the first non-class past the first element. */ + numClassesInSegment = (there = 0); + toDoLimitSqInt = ((/* begin numSlotsOf: */ + assert((classIndexOf(arrayOfObjects)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(arrayOfObjects + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(arrayOfObjects - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (here = 1; here <= toDoLimitSqInt; here += 1) { + objOopSqInt = longAt((void *)((arrayOfObjects + BaseHeaderSize) + ((((usqInt)(here) << (shiftForWord())))))); + hash = (long32At((void *)(objOopSqInt + 4))) & (identityHashHalfWordMask()); + if ((hash > (lastClassIndexPun())) + && ((classOrNilAtIndex(hash)) == objOopSqInt)) { + numClassesInSegment += 1; + if (there > 0) { + tempObjOop = longAt((void *)((arrayOfObjects + BaseHeaderSize) + ((((usqInt)(there) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(arrayOfObjects)) + && (!(isForwarded(arrayOfObjects)))); + assert(validStorePointerUncheckedArgs(there, arrayOfObjects, objOopSqInt)); + longAtput((void *)((arrayOfObjects + BaseHeaderSize) + ((((usqInt)(there) << (shiftForWord()))))),objOopSqInt); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(arrayOfObjects)) + && (!(isForwarded(arrayOfObjects)))); + assert(validStorePointerUncheckedArgs(here, arrayOfObjects, tempObjOop)); + longAtput((void *)((arrayOfObjects + BaseHeaderSize) + ((((usqInt)(here) << (shiftForWord()))))),tempObjOop); + there += 1; + } + } + else { + if (!there) { + there = here; + } + } + } + + /* The scheme is to copy the objects into segmentWordArray, and then map the oops in segmentWordArray. + Therefore the primitive needs to both map efficiently originals to copies in segmentWordArray and + be able to undo any side-effects if the primitive fails because either segmentWordArray or outPointersArray + is too small. The mapping is done by having the objects to be stored in arrayOfObjects refer to their mapped + locations through their first field, just like a forwarding pointer, but without becoming a forwarder, saving their + first field in savedFirstFields, and the objects in outPointersArray pointing to their locations in the outPointersArray + through their identityHashes, saved in savedOutHashes. + Since arrayOfObjects and its savedFirstFields, and outPointersArray and its saved hashes, can be enumerated + side-by-side, the hashes can be restored to the originals. So the first field of the heap object corresponding to + an object in arrayOfObjects is set to its location in segmentWordArray, and the hash of an object in outPointersArray + is set to its index in outPointersArray plus the top hash bit. Classes in arrayOfObjects have their marked bit set. + Oops in objects in segmentWordArray are therefore mapped by accessing the original oop, and following its first + field. Class indices in segmentWordArray are mapped by fetching the original class, and testing its marked bit. + If marked, the first field is followed to access the class copy in the segment. Out pointers (objects and classes, + which are unmarked), the object's identityHash is set (eek!!) to its index in the outPointersArray. So savedOutHashes + parallels the outPointersArray. The saved hash array is initialized with an out-of-range hash value so that the first + unused entry can be identified. */ + savedFirstFields = noInlineAllocateSlotsformatclassIndex(numSlotsOf(arrayOfObjects), wordIndexableFormat(), wordSizeClassIndexPun()); + savedOutHashes = noInlineAllocateSlotsformatclassIndex(numSlotsForBytes((numSlotsOf(outPointersArray)) * 4), firstLongFormat(), thirtyTwoBitLongsClassIndexPun()); + if ((!savedFirstFields) + || (!savedOutHashes)) { + freeObject(arrayOfObjects); + if (oopisGreaterThanOrEqualToandLessThan(savedFirstFields, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedFirstFields); + } + if (oopisGreaterThanOrEqualToandLessThan(savedOutHashes, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedOutHashes); + } + return PrimErrNoMemory; + } + /* begin numSlotsOf: */ + assert((classIndexOf(savedFirstFields)) > (isForwardedObjectClassIndexPun())); + if (((numSlots = byteAt((void *)(savedFirstFields + (numSlotsFieldByteOffset()))))) == (numSlotsMask())) { + numSlotsSqInt = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(savedFirstFields - BaseHeaderSize)))) << 8)))))) >> 8; + } + else { + numSlotsSqInt = numSlots; + } + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((savedFirstFields + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1, addressAfter(savedFirstFields))); + toDoLimitUsqInt = ((usqInt)(((savedFirstFields + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1)); + for (p = (((usqInt)(savedFirstFields + BaseHeaderSize))); p <= toDoLimitUsqInt; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + /* begin numSlotsOf: */ + assert((classIndexOf(savedOutHashes)) > (isForwardedObjectClassIndexPun())); + if (((numSlots = byteAt((void *)(savedOutHashes + (numSlotsFieldByteOffset()))))) == (numSlotsMask())) { + numSlotsSqInt = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(savedOutHashes - BaseHeaderSize)))) << 8)))))) >> 8; + } + else { + numSlotsSqInt = numSlots; + } + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((savedOutHashes + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1, addressAfter(savedOutHashes))); + toDoLimitUsqInt = ((usqInt)(((savedOutHashes + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1)); + for (p = (((usqInt)(savedOutHashes + BaseHeaderSize))); p <= toDoLimitUsqInt; p += 8 /* allocationUnit */) { + longAtput((void *)(p),((((usqInt)(((identityHashHalfWordMask()) + 1)) << 32))) + ((identityHashHalfWordMask()) + 1)); + } + segAddr = segmentWordArray + BaseHeaderSize; + endSeg = addressAfter(segmentWordArray); + + /* Write a version number for byte order and version check. */ + if (segAddr >= endSeg) { + return PrimErrGenericFailure; + } + long32Atput((void *)(segAddr),imageSegmentVersion()); + long32Atput((void *)(segAddr + 4),imageSegmentVersion()); + segStart = (segAddr += 8 /* allocationUnit */); + assert(arrayOfRoots == (fetchPointerofObject(0, arrayOfObjects))); + + /* Copy all reachable objects to the segment, setting the marked bit for all objects (clones) in the segment, + and the remembered bit for all classes (clones) in the segment. */ + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(arrayOfObjects)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(arrayOfObjects + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(arrayOfObjects - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + /* Check that classes in the segment are addressable. Since the top bit of the hash field is used to tag + classes external to the segment, the segment offset must not inadvertently set this bit. This limit still + allows for a million or more classes. */ + if ((i == numClassesInSegment) + && ((((segAddr - segStart) / 8 /* allocationUnit */) + (lastClassIndexPun())) >= TopHashBit)) { + return returnrestoringObjectsInupTosavedFirstFields(PrimErrLimitExceeded, arrayOfObjects, i, savedFirstFields); + } + objOop = longAt((void *)((arrayOfObjects + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + assert(!(((isImmediate(objOop)) + || (isForwarded(objOop))))); + newSegAddrOrError = copyObjtoAddrstopAtsavedFirstFieldsindex(objOop, segAddr, endSeg, savedFirstFields, i); + if (oopisLessThan(newSegAddrOrError, segStart)) { + return returnrestoringObjectsInupTosavedFirstFields(newSegAddrOrError, arrayOfObjects, i, savedFirstFields); + } + segAddr = newSegAddrOrError; + } + + /* Check that it can be safely shortened. */ + if ((endSeg != segAddr) + && ((endSeg - segAddr) < (BaseHeaderSize + BytesPerOop))) { + return returnrestoringObjectsInupTosavedFirstFields(PrimErrWritePastObject, arrayOfObjects, -1, savedFirstFields); + } + + /* Now scan, adding out pointers to the outPointersArray; all objects in arrayOfObjects + have their first field pointing to the corresponding copy in segmentWordArray. */ + if (((outIndex = mapOopsFromtooutPointersoutHashes(segStart, segAddr, outPointersArray, savedOutHashes))) < 0) { + return returnrestoringObjectsInsavedFirstFieldsandsavedHashes(PrimErrBadIndex, arrayOfObjects, savedFirstFields, outPointersArray, savedOutHashes); + } + + /* no room in outPointers; fail + We're done. Shorten the results, restore hashes and return. */ + indexableSize = (segAddr - (segmentWordArray + BaseHeaderSize)) / 4; + + /* begin shorten:toIndexableSize: */ + assert((indexableSize >= 0) + && (indexableSize < (lengthOf(segmentWordArray)))); + delta = doShortentoIndexableSize(segmentWordArray, indexableSize); + assert((lengthOf(followMaybeForwarded(segmentWordArray))) == indexableSize); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + + /* begin shorten:toIndexableSize: */ + assert((outIndex >= 0) + && (outIndex < (lengthOf(outPointersArray)))); + delta = doShortentoIndexableSize(outPointersArray, outIndex); + assert((lengthOf(followMaybeForwarded(outPointersArray))) == outIndex); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + return returnrestoringObjectsInsavedFirstFieldsandsavedHashes(PrimNoErr, arrayOfObjects, savedFirstFields, outPointersArray, savedOutHashes); +} + + /* SpurMemoryManager>>#storeInteger:ofObject:withValue: */ +void +storeIntegerofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqLong integerValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((((((usqLong)(integerValue)) >> 60) + 1) & 15) <= 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(fieldIndex, objOop, (((usqInt)integerValue << 3) | 1))); + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),(((usqInt)integerValue << 3) | 1)); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + } + } + else { + if (fmt < (firstShortFormat())) { + if (fmt < (firstLongFormat())) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 3)))),integerValue); + } + else { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2)))),integerValue); + } + } + else { + if (fmt < (firstCompiledMethodFormat())) { + if (fmt < (firstByteFormat())) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((objOop + BaseHeaderSize) + fieldIndex),integerValue); + } + else { + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 1)))),integerValue); + } + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + } + } + } +} + + /* SpurMemoryManager>>#storeLong32:ofObject:withValue: */ +int +storeLong32ofObjectwithValue(sqInt fieldIndex, sqInt obj, sqInt valueWord) +{ + return long32Atput((void *)((obj + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2)))),valueWord); +} + + /* SpurMemoryManager>>#storePointerUnchecked:ofObject:withValue: */ +sqInt +storePointerUncheckedofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqInt valuePointer) +{ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(fieldIndex, objOop, valuePointer)); + return longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); +} + + +/* Note must check here for stores of young objects into old ones. */ +/* See SistaCogit */ + + /* SpurMemoryManager>>#storePointer:ofObject:withValue: */ +sqInt +storePointerofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqInt valuePointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(validStorePointerArgs(fieldIndex, objOop, valuePointer)); + + /* begin isOldObject: */ + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + return longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); +} + + +/* Answer a new String copied from a null-terminated C string, + or nil if out of memory. */ + + /* SpurMemoryManager>>#stringForCString: */ +sqInt +stringForCString(const char *aCString) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt formatField; + sqInt len; + usqInt newObj; + sqInt newString; + usqInt numBytes; + sqInt numSlots; + + len = strlen(aCString); + numSlots = (len + (BytesPerWord - 1)) / BytesPerWord; + formatField = (firstByteFormat()) + ((8 - len) & (BytesPerWord - 1)); + + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if ((((usqInt)(numSlots)) >> 56) > 0) { + newString = null; + goto l1; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newString = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, formatField, ClassByteStringCompactIndex); + goto l1; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + ClassByteStringCompactIndex); + } + else { + longAtput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + ClassByteStringCompactIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newString = newObj; + /* end allocateSlots:format:classIndex: */ +l1: + if (newString) { + strncpy(((char *) (newString + BaseHeaderSize)), aCString, len); + } + return newString; +} + + +/* On load, swizzle the pointers in an obj stack. Answer the obj stack's oop. */ + + /* SpurMemoryManager>>#swizzleObjStackAt: */ +static NoDbgRegParms sqInt +swizzleObjStackAt(sqInt objStackRootIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt field; + sqInt firstPage; + sqInt index; + sqInt page; + sqInt stackOrNil; + + firstPage = (stackOrNil = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord()))))))); + if (stackOrNil == GIV(nilObj)) { + return stackOrNil; + } + do { + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + assert((fetchPointerofObject(ObjStackMyx, stackOrNil)) == objStackRootIndex); + + /* There are four fixed slots in an obj stack, and a Topx of 0 indicates empty, so + if there were 5 slots in an oop stack, full would be 2, and the last 0-rel index is 4. + Hence the last index is topx + fixed slots - 1, or topx + ObjStackNextx */ + index = (longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) + ObjStackNextx; + + /* swizzle fields including ObjStackNextx, excluding ObjStackFreex and leave field containing the next link. */ + do { + field = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (!((field == 0) + || (((field & (tagMask())) != 0)))) { + field = swizzleObj(field); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(stackOrNil)) == (wordIndexableFormat())); + longAtput((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),field); + } + } while(((index -= 1)) >= ObjStackNextx); + } while(((stackOrNil = field)) != 0); + if ((stackOrNil = longAt((void *)((firstPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))))) { + page = firstPage; + do { + stackOrNil = swizzleObj(stackOrNil); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(page)) == (wordIndexableFormat())); + longAtput((void *)((page + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),stackOrNil); + page = stackOrNil; + } while(((stackOrNil = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))))) != 0); + } + assert(isValidObjStackAt(objStackRootIndex)); + return longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord())))))); +} + + +/* Do an incremental GC that tenures all surviving young objects to old + space. The selector tenuringIncrementalGC refers to the V3 incremental + collector, which is the collector for young objects, equivalent in + function to the scavenger. + The Spur incremental collector is called the global incremental garbage + collector or globalIGC for short. + */ + + /* SpurMemoryManager>>#tenuringIncrementalGC */ +void +tenuringIncrementalGC(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt savedTenuringThreshold; + + /* begin flushNewSpace */ + savedTenuringThreshold = GIV(tenureThreshold); + GIV(tenureThreshold) = GIV(oldSpaceStart); + scavengingGCTenuringIf(TenureByAge); + + /* begin setRawTenuringThreshold: */ + GIV(tenureThreshold) = savedTenuringThreshold; + assert((GIV(rememberedSetSize)) == 0); + assert(GIV(pastSpaceStart) == (((GIV(pastSpace)).start))); + assert(GIV(freeStart) == (((GIV(eden)).start))); +} + + +/* This assert is tricky. push:onObjStack: may call topOfObjStack: just after + pushing an + empty page on the stack, and will ask if the second page is valid. */ + + /* SpurMemoryManager>>#topOfObjStack: */ +static NoDbgRegParms sqInt +topOfObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt topx; + + eassert(isValidObjStackPagemyIndexfirstPage(objStack, fetchPointerofObject(ObjStackMyx, objStack), objStack == (fetchPointerofObject(fetchPointerofObject(ObjStackMyx, objStack), GIV(hiddenRootsObj))))); + topx = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + if (!topx) { + assert((fetchPointerofObject(ObjStackNextx, objStack)) == 0); + return null; + } + return longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(((topx + ObjStackFixedSlots) - 1)) << (shiftForWord())))))); +} + + +/* Answers the top of the remappable oop stack. Useful when writing loops. + We support this excessence for compatibility with ObjectMemory. + Spur doesn't GC during allocation. */ + + /* SpurMemoryManager>>#topRemappableOop */ +sqInt +topRemappableOop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(remapBuffer)[GIV(remapBufferCount)]; +} + + +/* This method both computes the actual number of free bytes by traversing + all free objects + on the free lists/tree, and checks that the tree is valid. It is used + mainly by checkFreeSpace. */ + + /* SpurMemoryManager>>#totalFreeListBytes */ +static sqInt +totalFreeListBytes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bytesInChunk; + sqInt cameFrom; + sqInt i; + sqInt largeChild; + sqInt listNode; + sqInt nextNode; + sqInt smallChild; + sqInt totalFreeBytes; + sqInt treeNodeSqInt; + + totalFreeBytes = 0; + for (i = 1; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + bytesInChunk = i * 8 /* allocationUnit */; + listNode = GIV(freeLists)[i]; + while (listNode != 0) { + totalFreeBytes += bytesInChunk; + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(listNode)); + assert(bytesInChunk == (bytesInBody(listNode))); + nextNode = longAt((void *)((listNode + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(nextNode != listNode); + listNode = nextNode; + } + } + + /* begin freeTreeNodesDo: */ + treeNodeSqInt = GIV(freeLists)[0]; + if (!treeNodeSqInt) { + goto l1; + } + cameFrom = -1; + do { + assert((bytesInBody(treeNodeSqInt)) >= ((numFreeLists()) * (allocationUnit()))); + smallChild = longAt((void *)((treeNodeSqInt + BaseHeaderSize) + (3U << (shiftForWord())))); + largeChild = longAt((void *)((treeNodeSqInt + BaseHeaderSize) + (4U << (shiftForWord())))); + assert((smallChild == 0) + || (treeNodeSqInt == (fetchPointerofFreeChunk(freeChunkParentIndex(), smallChild)))); + assert((largeChild == 0) + || (treeNodeSqInt == (fetchPointerofFreeChunk(freeChunkParentIndex(), largeChild)))); + + /* apply if the node has no children, or it has no large children and we're + returning from the small child, or we're returning from the large child. */ + if (((smallChild == 0) + && (largeChild == 0)) + || ((largeChild + ? cameFrom == largeChild + : cameFrom == smallChild))) { + bytesInChunk = bytesInBody(treeNodeSqInt); + assert((bytesInChunk / (allocationUnit())) >= (numFreeLists())); + listNode = treeNodeSqInt; + while (listNode != 0) { + /* self printFreeChunk: listNode */ + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(listNode)); + assert((listNode == treeNodeSqInt) + || ((fetchPointerofFreeChunk(freeChunkParentIndex(), listNode)) == 0)); + totalFreeBytes += bytesInChunk; + assert(bytesInChunk == (bytesInBody(listNode))); + nextNode = longAt((void *)((listNode + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(nextNode != listNode); + listNode = nextNode; + } + + /* and since we've applied we must move on up */ + cameFrom = treeNodeSqInt; + treeNodeSqInt = longAt((void *)((treeNodeSqInt + BaseHeaderSize) + (2U << (shiftForWord())))); + } + else { + if ((smallChild != 0) + && (cameFrom != smallChild)) { + treeNodeSqInt = smallChild; + } + else { + assert(largeChild != 0); + treeNodeSqInt = largeChild; + } + cameFrom = -1; + } + } while(treeNodeSqInt != 0); + /* end freeTreeNodesDo: */ +l1: + return totalFreeBytes; +} + + /* SpurMemoryManager>>#trueObject */ +sqInt +trueObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(trueObj); +} + + +/* Ephemerons in the mourn queue will have been fired (had their + ephemeron-ness turned off), + but the ephemeron queue is not saved in the snapshot. So the snapshotted + ephemerons need to be unfired so they're still unqueued ephemerons in the + loaded image. */ + + /* SpurMemoryManager>>#unfireQueuedEphemeronsForSnapshot */ +static void +unfireQueuedEphemeronsForSnapshot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt iSqInt; + sqInt mourner; + sqInt numOnThisPage; + sqInt objStackPage; + + /* begin objStack:do: */ + if (GIV(mournQueue) == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(GIV(mournQueue))); + objStackPage = GIV(mournQueue); + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + mourner = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (((!(mourner & (tagMask())))) + && (((byteAt((void *)(mourner + (formatFieldByteOffset())))) & (formatMask())) == (nonIndexablePointerFormat()))) { + /* begin setFormatOf:to: */ + assert((((ephemeronFormat()) >= 0) && ((ephemeronFormat()) <= (formatMask())))); + byteAtput((void *)(mourner + (formatFieldByteOffset())),((byteAt((void *)(mourner + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (ephemeronFormat())); + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l1:; +} + + +/* Unlink a free object from the free lists. Do not alter totalFreeOldSpace. + Used for coalescing. + */ + + /* SpurMemoryManager>>#unlinkFreeChunk:chunkBytes: */ +static NoDbgRegParms sqInt +unlinkFreeChunkchunkBytes(sqInt freeChunk, sqInt chunkBytes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt lilliputian; + sqInt next; + sqInt nextFreeChunk; + sqInt nextSqInt; + sqInt prev; + + index = chunkBytes / 8 /* allocationUnit */; + + /* Pathological 64 bits case - size 1 - single linked list */ + + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + if (chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */)) { + return unlinkLilliputianChunkindex(freeChunk, index); + } + prev = longAt((void *)((freeChunk + BaseHeaderSize) + (1U << (shiftForWord())))); + + /* Has prev element: update double linked list */ + if (prev) { + nextFreeChunk = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:chunkBytes: */ + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + lilliputian = chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if ((nextFreeChunk != 0) + && (!lilliputian)) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + } + return freeChunk; + } + + /* Is the beginning of a list + Small chunk */ + if ((index < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(freeChunk)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(freeChunk)) == freeChunk))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((nextSqInt = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))))); + if (nextSqInt) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextSqInt)); + longAtput((void *)((nextSqInt + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + return freeChunk; + } + + /* Large chunk */ + next = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + if (next) { + inFreeTreeReplacewith(freeChunk, next); + } + else { + unlinkSolitaryFreeTreeNode(freeChunk); + } + + /* no list; remove the interior node + list; replace node with it */ + return freeChunk; +} + + /* SpurMemoryManager>>#unlinkLilliputianChunk:index: */ +static NoDbgRegParms NeverInline sqInt +unlinkLilliputianChunkindex(sqInt freeChunk, sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt next; + sqInt nextSqInt; + sqInt node; + sqInt prev; + + + /* for profiling */ + node = GIV(freeLists)[index]; + prev = 0; + while (node != 0) { + assert(node == (startOfObject(node))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(node)); + next = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + if (node == freeChunk) { + if (prev) { + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((next == 0) + || (isFreeObject(next))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),next); + } + else { + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(freeChunk)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(freeChunk)) == freeChunk))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((nextSqInt = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))))); + } + return freeChunk; + } + prev = node; + node = next; + } + error("freeChunk not found in lilliputian chunk free list"); + return 0; +} + + +/* Unlink a freeTreeNode. Assumes the node has no list (null next link). */ + + /* SpurMemoryManager>>#unlinkSolitaryFreeTreeNode: */ +static NoDbgRegParms void +unlinkSolitaryFreeTreeNode(sqInt freeTreeNode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fieldIndex; + sqInt larger; + sqInt parent; + sqInt smaller; + + assert((fetchPointerofFreeChunk(freeChunkNextIndex(), freeTreeNode)) == 0); + + /* case 1. interior node has one child, P = parent, N = node, S = subtree (mirrored for large vs small) + ___ ___ + | P | | P | + _/_ _/_ + | N | => | S | + _/_ + | S | + case 2: interior node has two children, , P = parent, N = node, L = smaller, left subtree, R = larger, right subtree. + add the left subtree to the bottom left of the right subtree (mirrored for large vs small) + ___ ___ + | P | | P | + _/_ _/_ + | N | => | R | + _/_ _\_ _/_ + | L | | R | | L | */ + smaller = longAt((void *)((freeTreeNode + BaseHeaderSize) + (3U << (shiftForWord())))); + larger = longAt((void *)((freeTreeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + parent = longAt((void *)((freeTreeNode + BaseHeaderSize) + (2U << (shiftForWord())))); + if (parent) { + if (smaller) { + fieldIndex = (freeTreeNode == (longAt((void *)((parent + BaseHeaderSize) + (3U << (shiftForWord()))))) + ? 3 /* freeChunkSmallerIndex */ + : 4 /* freeChunkLargerIndex */); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(parent)); + assert((smaller == 0) + || (isFreeObject(smaller))); + longAtput((void *)((parent + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),smaller); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(smaller)); + assert((parent == 0) + || (isFreeObject(parent))); + longAtput((void *)((smaller + BaseHeaderSize) + (2U << (shiftForWord()))),parent); + if (larger) { + addFreeSubTree(larger); + } + } + else { + fieldIndex = (freeTreeNode == (longAt((void *)((parent + BaseHeaderSize) + (3U << (shiftForWord()))))) + ? 3 /* freeChunkSmallerIndex */ + : 4 /* freeChunkLargerIndex */); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(parent)); + assert((larger == 0) + || (isFreeObject(larger))); + longAtput((void *)((parent + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),larger); + if (larger) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(larger)); + assert((parent == 0) + || (isFreeObject(parent))); + longAtput((void *)((larger + BaseHeaderSize) + (2U << (shiftForWord()))),parent); + } + } + } + else { + if (smaller) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(smaller)); + longAtput((void *)((smaller + BaseHeaderSize) + (2U << (shiftForWord()))),0); + GIV(freeLists)[0] = smaller; + if (larger) { + addFreeSubTree(larger); + } + } + else { + if (larger) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(larger)); + longAtput((void *)((larger + BaseHeaderSize) + (2U << (shiftForWord()))),0); + } + GIV(freeLists)[0] = larger; + } + } +} + + /* SpurMemoryManager>>#unpinObject: */ +sqInt +unpinObject(sqInt objOop) +{ + assert(isNonImmediate(objOop)); + setIsPinnedOfto(objOop, 0); + return 0; +} + + /* SpurMemoryManager>>#unscannedEphemeronsContainsOnlyActiveEphemerons */ +static sqInt +unscannedEphemeronsContainsOnlyActiveEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt entry; + usqInt p; + + for (p = ((GIV(unscannedEphemerons).start)); p <= ((GIV(unscannedEphemerons).top)); p += BytesPerOop) { + entry = longAt((void *)(p)); + if (!(((!(entry & (tagMask())))) + && ((isEphemeron(entry)) + && (!(isMarked(followedKeyOfEphemeron(entry))))))) { + return 0; + } + } + return 1; +} + + /* SpurMemoryManager>>#unscannedEphemeronsContainsOnlyEphemerons */ +static sqInt +unscannedEphemeronsContainsOnlyEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt entry; + usqInt p; + + for (p = ((GIV(unscannedEphemerons).start)); p <= ((GIV(unscannedEphemerons).top)); p += BytesPerOop) { + entry = longAt((void *)(p)); + if (!(((!(entry & (tagMask())))) + && (isEphemeron(entry)))) { + return 0; + } + } + return 1; +} + + /* SpurMemoryManager>>#updateListStartingAt: */ +static NoDbgRegParms void +updateListStartingAt(sqInt freeNode) +{ + sqInt obj; + sqInt prev; + + obj = 0; + if (!freeNode) { + return; + } + assert(!((isLilliputianSize(bytesInBody(freeNode))))); + prev = freeNode; + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + longAtput((void *)((prev + BaseHeaderSize) + (1U << (shiftForWord()))),0); + while (1) { + obj = longAt((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord())))); + if (!(obj != 0)) break; + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(obj)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((obj + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + prev = obj; + } +} + + +/* Check the hashes of classes in the table. The tricky thing here is that + classes may be duplicated + in the table. So each entry must be in the table at its hash, even if it + is elsewhere in the table. */ + + /* SpurMemoryManager>>#validClassTableHashes */ +static sqInt +validClassTableHashes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOrNilSqInt; + int hash; + sqInt i; + sqInt ignored; + sqInt j; + sqInt page; + sqInt toDoLimit; + + if (!(validClassTableRootPages())) { + return 0; + } + + /* begin classTableEntriesDo: */ + for (i = 0; i < GIV(numClassTablePages); i += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNilSqInt = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNilSqInt != GIV(nilObj)) { + ignored = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + if ((!((longAt((void *)(classOrNilSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + return 0; + } + hash = (long32At((void *)(classOrNilSqInt + 4))) & (identityHashHalfWordMask()); + if (!hash) { + return 0; + } + if ((noCheckClassAtIndex(hash)) != classOrNilSqInt) { + return 0; + } + } + } + } + return 1; +} + + +/* Answer if hiddenRootsObj is of the right size with the + expected contents, and if numClassTablePages is correct. */ + + /* SpurMemoryManager>>#validClassTableRootPages */ +static sqInt +validClassTableRootPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + usqInt numSlots; + sqInt obj; + sqInt toDoLimit; + + if (!(((/* begin numSlotsOf: */ + assert((classIndexOf(GIV(hiddenRootsObj))) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(GIV(hiddenRootsObj) + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(GIV(hiddenRootsObj) - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == ((1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift()))) + 8 /* hiddenRootSlots */))) { + return 0; + } + + /* is it in range? */ + if (!((GIV(numClassTablePages) > 1) + && (GIV(numClassTablePages) <= (1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift())))))) { + return 0; + } + + /* are all pages the right size? */ + for (i = 0; i < GIV(numClassTablePages); i += 1) { + obj = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (!((addressCouldBeObj(obj)) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == (1U << (classTableMajorIndexShift()))))) { + return 0; + } + } + + /* are all entries beyond numClassTablePages nil? */ + toDoLimit = (1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift()))) - 1; + for (i = GIV(numClassTablePages); i <= toDoLimit; i += 1) { + if ((longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))))) != GIV(nilObj)) { + return 0; + } + } + return 1; +} + + +/* useful for debugging */ + + /* SpurMemoryManager>>#validFreeTree */ +int +validFreeTree(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return !(validFreeTreeChunkparent(GIV(freeLists)[0], 0)); +} + + /* SpurMemoryManager>>#validFreeTreeChunk: */ +static NoDbgRegParms sqInt +validFreeTreeChunk(sqInt chunk) +{ + if (!(segmentContainingObj(chunk))) { + return 0; + } + return !(validFreeTreeChunkparent(chunk, longAt((void *)((chunk + BaseHeaderSize) + (2U << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#validFreeTreeChunk:parent: */ +static NoDbgRegParms const char * +validFreeTreeChunkparent(sqInt chunk, sqInt parent) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + const char *reason; + + if (!chunk) { + return null; + } + if (!(/* addressCouldBeOldObj: */ + ((chunk & (BaseHeaderSize - 1)) == 0) + && (oopisGreaterThanOrEqualToandLessThan(chunk, GIV(oldSpaceStart), GIV(endOfMemory))))) { + return "not in old space"; + } + if (((bytesInBody(chunk)) / 8 /* allocationUnit */) < 64 /* numFreeLists */) { + return "too small"; + } + if (parent != (longAt((void *)((chunk + BaseHeaderSize) + (2U << (shiftForWord())))))) { + return "bad parent"; + } + if ((segmentContainingObj(chunk)) != (segmentContainingObj(addressAfter(chunk)))) { + return "not in one segment"; + } + if ((reason = validFreeTreeChunkparent(longAt((void *)((chunk + BaseHeaderSize) + (3U << (shiftForWord())))), chunk))) { + return reason; + } + if ((reason = validFreeTreeChunkparent(longAt((void *)((chunk + BaseHeaderSize) + (4U << (shiftForWord())))), chunk))) { + return reason; + } + return null; +} + + /* SpurMemoryManager>>#validObjStacks */ +static sqInt +validObjStacks(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((GIV(markStack) == GIV(nilObj)) + || (isValidObjStack(GIV(markStack)))) + && (((GIV(weaklingStack) == GIV(nilObj)) + || (isValidObjStack(GIV(weaklingStack)))) + && ((GIV(mournQueue) == GIV(nilObj)) + || (isValidObjStack(GIV(mournQueue))))); +} + + +/* This is a hook for the CoMemoryManagers to check for valid compiled code. + It is a noop here. */ + + /* SpurMemoryManager>>#validPostBecomeArrayContents: */ +static NoDbgRegParms sqInt +validPostBecomeArrayContents(sqInt anArray) +{ + return 1; +} + + /* SpurMemoryManager>>#validStorePointerArgs:_:_: */ +static NoDbgRegParms sqInt +validStorePointerArgs(sqInt fieldIndex, sqInt objOop, sqInt valuePointer) +{ + usqInt numSlots; + + return (fieldIndex >= 0) + && ((fieldIndex < ((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))) + && (!((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))); +} + + /* SpurMemoryManager>>#validStorePointerUncheckedArgs:_:_: */ +static NoDbgRegParms sqInt +validStorePointerUncheckedArgs(sqInt fieldIndex, sqInt objOop, sqInt valuePointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + + return (fieldIndex >= 0) + && (((fieldIndex == 0) + || (fieldIndex < ((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))))) + && ((((valuePointer & (tagMask())) != 0)) + || ((/* isInHeapBounds: */ + (oopisGreaterThanOrEqualTo(valuePointer, (!(minCogMethodAddress()) + ? GIV(nilObj) + : minCogMethodAddress()))) + && (oopisLessThan(valuePointer, GIV(endOfMemory)))) + || (((fieldIndex == 0) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 0) + || (GIV(gcPhaseInProgress) == SlidingCompactionInProgress))) + || ((((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (sixtyFourBitIndexableFormat())) + && ((((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) < (firstCompiledMethodFormat())) + || (fieldIndex > (literalCountOf(objOop))))))))); +} + + +/* 1/true = big, 0/false = little. + Answer false for little endian, true for big endian (should of course be + vmIsBigEndian; sigh) */ + + /* SpurMemoryManager>>#vmEndianness */ +sqInt +vmEndianness(void) +{ + return VMBIGENDIAN; +} + + /* SpurMemoryManager>>#whereIsMaybeHeapThing: */ +static NoDbgRegParms char * +whereIsMaybeHeapThing(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (/* isInNewSpace: */ + (oopisLessThan(anOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(anOop, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(anOop, ((GIV(eden)).start), GIV(freeStart))) { + return " is in eden"; + } + if (oopisGreaterThanOrEqualToandLessThan(anOop, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + return " is in future space"; + } + if (oopisGreaterThanOrEqualToandLessThan(anOop, ((GIV(pastSpace)).start), GIV(pastSpaceStart))) { + return " is in past space"; + } + return " is in new space"; + } + if (oopisGreaterThanOrEqualToandLessThan(anOop, GIV(oldSpaceStart), GIV(endOfMemory))) { + if (segmentContainingObj(anOop)) { + return " is in old space"; + } + return " is between old space segments"; + } + return null; +} + + +/* Sweep all of old space, sliding unpinned marked objects down over free and + unmarked objects. + Let the segmentManager mark which segments contain pinned objects via + notePinned:. + */ + + /* SpurPlanningCompactor>>#compact */ +static NeverInline void +compact(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt finalPass; + sqInt firstPass; + sqInt followingWord; + usqInt followingWordAddress; + sqInt highestSuitableFreeBlock; + sqInt i; + sqInt largestFreeChunk; + sqInt largestFreeChunkSqInt; + usqInt numSlots; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt sizeOfEden; + sqInt spaceEstimate; + sqInt stackOrNil; + + + /* for profiling */ + /* begin initializeScanCheckingForFullyCompactedHeap */ + GIV(firstMobileObject) = (GIV(lastMobileObject) = (GIV(objectAfterLastMobileObject) = null)); + reinitializeScanFrom(GIV(hiddenRootsObj)); + if (!GIV(firstFreeObject)) { + error("uncompactable heap; no unmarked objects found"); + } + if (GIV(firstMobileObject) >= GIV(endOfMemory)) { + /* begin unmarkObjectsInFullyCompactedHeap */ + /* begin unmarkInitialImmobileObjects */ + /* begin allOldSpaceObjectsFrom:do: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objOopSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objOopSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (oopisGreaterThanOrEqualTo(objOopSqInt, GIV(firstMobileObject))) { + goto l2; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l4; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l4:; + } + /* end unmarkInitialImmobileObjects */ +l2: + + /* begin unmarkSurvivingObjectsForCompact */ + /* begin allPastSpaceObjectsDo: */ + /* begin allPastSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + address = ((GIV(pastSpace)).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + while (oopisLessThan(objOopSqInt, GIV(pastSpaceStart))) { + assert(isEnumerableObjectNoAssert(objOopSqInt)); + if ((byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + objOopSqInt = GIV(pastSpaceStart); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l3:; + } + return; + } + + /* begin initializeCompaction */ + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + + /* begin selectSavedFirstFieldsSpace */ + spaceEstimate = (GIV(endOfMemory) - GIV(nilObj)) / 40; + sizeOfEden = (((GIV(eden)).limit)) - (((GIV(eden)).start)); + if (spaceEstimate > sizeOfEden) { + /* begin findHighestSuitableFreeBlock: */ + if ((largestFreeChunk = findLargestFreeChunk())) { + if (((bytesInBody(largestFreeChunk)) >= spaceEstimate) + && ((((usqInt)largestFreeChunk)) > (((usqInt)(GIV(endOfMemory) - GIV(totalFreeOldSpace)))))) { + highestSuitableFreeBlock = largestFreeChunk; + goto l8; + } + } + highestSuitableFreeBlock = null; + /* end findHighestSuitableFreeBlock: */ +l8: + if (highestSuitableFreeBlock) { + if ((spaceEstimate > ((/* begin sizeOfFree: */ + assert(isFreeObject(highestSuitableFreeBlock)), + bytesInBody(highestSuitableFreeBlock)))) + && (useSegmentForSavedFirstFieldsSpace(spaceEstimate))) { + goto l5; + } + if (((/* begin sizeOfFree: */ + assert(isFreeObject(highestSuitableFreeBlock)), + bytesInBody(highestSuitableFreeBlock))) > sizeOfEden) { + /* begin useFreeChunkForSavedFirstFieldsSpace: */ + assert(validFreeTreeChunk(highestSuitableFreeBlock)); + (GIV(savedFirstFieldsSpace).start = highestSuitableFreeBlock + (4 /* freeChunkLargerIndex */ * BytesPerOop)); + (GIV(savedFirstFieldsSpace).limit = addressAfter(highestSuitableFreeBlock)); + GIV(savedFirstFieldsSpaceNotInOldSpace) = 0; + assert(!((savedFirstFieldsSpaceWasAllocated()))); + goto l5; + } + } + if (useSegmentForSavedFirstFieldsSpace(spaceEstimate)) { + goto l5; + } + } + + /* begin useEdenForSavedFirstFieldsSpace */ + (GIV(savedFirstFieldsSpace).start = ((GIV(eden)).start)); + (GIV(savedFirstFieldsSpace).limit = ((GIV(eden)).limit)); + GIV(savedFirstFieldsSpaceNotInOldSpace) = 1; + assert(!((savedFirstFieldsSpaceWasAllocated()))); + /* end selectSavedFirstFieldsSpace */ +l5: + + /* begin unpinRememberedSet */ + /* begin fetchPointer:ofObject: */ + GIV(firstFieldOfRememberedSet) = longAt((void *)(((longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord()))))))) + BaseHeaderSize) + (0U << (shiftForWord())))); + setIsPinnedOfto(longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))), 0); + + /* begin resetFreeListHeads */ + GIV(freeListsMask) = 0; + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + GIV(freeLists)[i] = 0; + } + + /* begin prepareObjStacksForPlanningCompactor */ + /* begin prepareObjStackForPlanningCompactor: */ + if (GIV(markStack) == GIV(nilObj)) { + goto l7; + } + stackOrNil = GIV(markStack); + do { + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(stackOrNil + 4),((((long32At((void *)(stackOrNil + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + ((longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) & (identityHashHalfWordMask()))); + } while(((stackOrNil = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))) != 0); + /* end prepareObjStackForPlanningCompactor: */ +l7: + if (GIV(weaklingStack) == GIV(nilObj)) { + goto l9; + } + stackOrNil = GIV(weaklingStack); + do { + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(stackOrNil + 4),((((long32At((void *)(stackOrNil + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + ((longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) & (identityHashHalfWordMask()))); + } while(((stackOrNil = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))) != 0); + /* end prepareObjStackForPlanningCompactor: */ +l9: + if (GIV(mournQueue) == GIV(nilObj)) { + goto l6; + } + stackOrNil = GIV(mournQueue); + do { + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(stackOrNil + 4),((((long32At((void *)(stackOrNil + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + ((longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) & (identityHashHalfWordMask()))); + } while(((stackOrNil = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))) != 0); + /* end prepareObjStackForPlanningCompactor: */ +l6: + GIV(totalFreeOldSpace) = 0; + GIV(gcPhaseInProgress) = SlidingCompactionInProgress; + (GIV(savedFirstFieldsSpace).top = ((GIV(savedFirstFieldsSpace).start)) - BytesPerOop); + firstPass = 1; + while (1) { + finalPass = planCompactSavingForwarders(); + assert((validRelocationPlanInPass(finalPass)) == 0); + updatePointers(); + copyAndUnmark(firstPass); + + /* Would like to check here, but can't if multi-pass. */ + + /* Currently we do only a single pass if a normal GC, assuming that a pass will + always compact plenty of space. But we should perhaps check this assumption + by looking at the large free tree and seeing that the ratio of the largest free + chunk to the total ammount of free space is high. */ + if (finalPass + || (GIV(biasForGC))) break; + firstPass = 0; + reinitializeScanFrom(GIV(firstFreeObject)); + if (!(GIV(firstMobileObject) > GIV(firstFreeObject))) { + /* We get here when firstFreeObject is the last object in memory. + firstMobileObject may be random value from previous compaction + in this case, and heap is fully compacted. Abort compaction. + We set firstFreeObject to firstMobileObject for unmarking. */ + GIV(firstFreeObject) = GIV(firstMobileObject); + unmarkObjectsFromFirstFreeObject(); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + endCompaction(); + return; + } + + /* begin updateSavedFirstFieldsSpaceIfNecessary */ + if ((/* savedFirstFieldsSpaceInFreeChunk */ + (!GIV(savedFirstFieldsSpaceNotInOldSpace)) + && (oopisGreaterThan((GIV(savedFirstFieldsSpace).start), GIV(nilObj)))) + && (((GIV(savedFirstFieldsSpace).start)) < GIV(lastMobileObject))) { + spaceEstimate = ((GIV(savedFirstFieldsSpace).limit)) - ((GIV(savedFirstFieldsSpace).start)); + + /* begin findHighestSuitableFreeBlock: */ + if ((largestFreeChunkSqInt = findLargestFreeChunk())) { + if (((bytesInBody(largestFreeChunkSqInt)) >= spaceEstimate) + && ((((usqInt)largestFreeChunkSqInt)) > (((usqInt)(GIV(endOfMemory) - GIV(totalFreeOldSpace)))))) { + largestFreeChunk = largestFreeChunkSqInt; + goto l1; + } + } + largestFreeChunk = null; + /* end findHighestSuitableFreeBlock: */ +l1: + if (largestFreeChunk) { + /* begin useFreeChunkForSavedFirstFieldsSpace: */ + assert(validFreeTreeChunk(largestFreeChunk)); + (GIV(savedFirstFieldsSpace).start = largestFreeChunk + (4 /* freeChunkLargerIndex */ * BytesPerOop)); + (GIV(savedFirstFieldsSpace).limit = addressAfter(largestFreeChunk)); + GIV(savedFirstFieldsSpaceNotInOldSpace) = 0; + assert(!((savedFirstFieldsSpaceWasAllocated()))); + } + else { + /* begin useEdenForSavedFirstFieldsSpace */ + (GIV(savedFirstFieldsSpace).start = ((GIV(eden)).start)); + (GIV(savedFirstFieldsSpace).limit = ((GIV(eden)).limit)); + GIV(savedFirstFieldsSpaceNotInOldSpace) = 1; + assert(!((savedFirstFieldsSpaceWasAllocated()))); + } + } + (GIV(savedFirstFieldsSpace).top = ((GIV(savedFirstFieldsSpace).start)) - BytesPerOop); + } + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + endCompaction(); +} + + +/* Sweep the mobile portion of the heap, moving objects to their eventual + locations, and clearing their marked bits. + Remember to update the savedFirstFields of pointer objects, as these have + been forwarded. + Answer if the end of the heap was reached (savedFirstFieldsSpace has not + overflowed). + The enumerations in planCompactSavingForwarders, + updatePointersInMobileObjects and copyAndUnmarkMobileObjects + match. We could implement them as a single enumeration method taking + several block arguments, but arguably that + would make understanding an already tricky algorithm more difficult. + Instead we tolerate the duplication and encourage + the reader to diff the three methods to see where they diverge (e.g. via + Cmd-shift-C). */ + + /* SpurPlanningCompactor>>#copyAndUnmarkMobileObjects */ +static NeverInline sqInt +copyAndUnmarkMobileObjects(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt availableSpace; + usqInt bytes; + sqInt destObj; + sqInt finalObject; + sqInt firstField; + sqInt followingWord; + usqInt followingWordAddress; + usqInt limit; + usqInt next; + usqInt nextBytes; + sqInt nextObj; + usqInt numSlots; + usqInt obj; + usqInt objBytes; + sqInt objOop; + usqInt previousPin; + sqInt prevObj; + sqInt prevPrevObj; + SpurSegmentInfo *seg; + usqInt start; + usqInt startOfPreviousPin; + usqInt toFinger; + usqInt top; + + availableSpace = 0; + previousPin = 0; + assert(!((isMarked(GIV(firstFreeObject))))); + toFinger = /* startOfObject: */ + ((byteAt((void *)(GIV(firstFreeObject) + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? GIV(firstFreeObject) - BaseHeaderSize + : GIV(firstFreeObject)); + top = (GIV(savedFirstFieldsSpace).start); + startOfPreviousPin = 0; + finalObject = (!(GIV(lastMobileObject)) + ? GIV(nilObj) + : GIV(lastMobileObject)); + + /* begin allOldSpaceEntitiesForCompactingFrom:to:do: */ + assert(isOldObject(GIV(firstFreeObject))); + assert(oopisLessThanOrEqualTo(finalObject, GIV(endOfMemory))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstFreeObject); + limit = (oopisLessThan(finalObject, GIV(endOfMemory)) + ? addressAfter(finalObject) + : GIV(endOfMemory)); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, limit))) break; + assert((long64At((void *)(objOop))) != 0); + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + nextObj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + nextObj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1: + assert((previousPin == null + ? toFinger <= (startOfObject(objOop)) + : (isMarked(previousPin)) + && (toFinger <= startOfPreviousPin))); + assert(GIV(savedFirstFieldsSpaceNotInOldSpace) + || (toFinger < top)); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + if (!previousPin) { + previousPin = objOop; + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + } + } + else { + bytes = bytesInBody(objOop); + while ((toFinger <= startOfPreviousPin) + && ((bytes != ((availableSpace = startOfPreviousPin - toFinger))) + && ((bytes + (16)) > availableSpace))) { + /* The object does not fit in the space between toFinger and previousPin. + Move toFinger up to point at the first unmarked or mobile object after + previousPin, or, if previousPin is contiguous with o, to the start of this + object. Update previousPin to be the next pinned object above toFInger + and below this object, or nil if no such pinned object exists. + Any unfillable gaps between adjacent pinned objects will be freed. */ + if (availableSpace > 0) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += availableSpace; + freeChunkWithBytesat(availableSpace, toFinger); + } + do { + assert((isMarked(previousPin)) + && (isPinned(previousPin))); + + /* begin unmarkPinned: */ + if (((longAt((void *)(previousPin))) & (classIndexMask())) == (segmentBridgePun())) { + assert(isMarked(previousPin)); + } + else { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(previousPin))); + byteAtput((void *)(previousPin + (markBitsByteOffset())),(byteAt((void *)(previousPin + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + + /* begin notePinned: */ + assert(isPinned(previousPin)); + assert(!((isSegmentBridge(previousPin)))); + seg = segmentContainingObj(previousPin); + (seg->containsPinned = 1); + } + toFinger = addressAfter(previousPin); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(toFinger + (numSlotsFieldByteOffset()))); + previousPin = (numSlots == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + } while((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && ((((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0) + && (previousPin < objOop))); + + /* Now previousPin is either equal to o or mobile. + Move it to the next pinned object below o */ + while (!((previousPin >= objOop) + || ((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && (((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + previousPin = oldSpaceObjectAfter(previousPin); + } + if (previousPin >= objOop) { + previousPin = null; + startOfPreviousPin = 0; + } + else { + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(previousPin + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? previousPin - BaseHeaderSize + : previousPin); + } + } + firstField = longAt((void *)(top)); + + /* begin copyAndUnmarkObject:to:bytes:firstField: */ + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + destObj = (numSlots == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + start = /* startOfObject:given: */ + (numSlots == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + + /* memmove must be used since the ranges may overlap. */ + memmove(((void *)toFinger), ((void *)start), bytes); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(destObj)) + && (!(isForwarded(destObj)))); + assert(validStorePointerUncheckedArgs(0, destObj, firstField)); + longAtput((void *)((destObj + BaseHeaderSize) + (0U << (shiftForWord()))),firstField); + toFinger += bytes; + if (((top += BytesPerOop)) >= ((GIV(savedFirstFieldsSpace).limit))) { + assert(((GIV(savedFirstFieldsSpace).top)) == (top - BytesPerOop)); + assert(nextObj == GIV(objectAfterLastMobileObject)); + if (!previousPin) { + previousPin = nextObj; + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(nextObj + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? nextObj - BaseHeaderSize + : nextObj); + } + + /* Create a free object for firstFreeObject to be set to on the next pass, but + do not link it into the free tree as it will be written over in that next pass. */ + if (toFinger < startOfPreviousPin) { + GIV(firstFreeObject) = initFreeChunkWithBytesat(startOfPreviousPin - toFinger, toFinger); + } + else { + GIV(firstFreeObject) = previousPin; + } + return 0; + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + objOop = nextObj; + } + freeFromupTonextObject(toFinger, GIV(endOfMemory), (!(previousPin) + ? (!(GIV(objectAfterLastMobileObject)) + ? objectAfter(GIV(firstFreeObject)) + : GIV(objectAfterLastMobileObject)) + : previousPin)); + + /* begin coalesceFrom: */ + next = 0; + if (toFinger >= GIV(endOfMemory)) { + goto l2; + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(toFinger + (numSlotsFieldByteOffset()))); + obj = (numSlots == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + while (1) { + next = oldSpaceObjectAfter(obj); + if (!(next < GIV(endOfMemory))) break; + if ((((longAt((void *)(obj))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) + && (((longAt((void *)(next))) & (classIndexMask())) == (isFreeObjectClassIndexPun()))) { + objBytes = bytesInBody(obj); + nextBytes = bytesInBody(next); + unlinkFreeChunkchunkBytes(obj, objBytes); + unlinkFreeChunkchunkBytes(next, nextBytes); + obj = freeChunkWithBytesat(objBytes + nextBytes, /* startOfObject: */ + ((byteAt((void *)(obj + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? obj - BaseHeaderSize + : obj)); + } + else { + obj = next; + } + } + /* end coalesceFrom: */ +l2: + return 1; +} + + +/* Sweep the heap, unmarking all objects and moving mobile objects to their + correct positions, + restoring their savedFirstFields. */ + + /* SpurPlanningCompactor>>#copyAndUnmark: */ +static NoDbgRegParms NeverInline void +copyAndUnmark(sqInt firstPass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt finalPass; + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + + if (firstPass) { + /* begin unmarkInitialImmobileObjects */ + /* begin allOldSpaceObjectsFrom:do: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objOopSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objOopSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (oopisGreaterThanOrEqualTo(objOopSqInt, GIV(firstMobileObject))) { + goto l2; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + /* end unmarkInitialImmobileObjects */ +l2:; + } + finalPass = copyAndUnmarkMobileObjects(); + if ((GIV(lastMobileObject)) + && ((!finalPass) + && (GIV(biasForGC)))) { + unmarkObjectsFromFirstFreeObject(); + } +} + + /* SpurPlanningCompactor>>#endCompaction */ +static void +endCompaction(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + + /* begin unmarkSurvivingObjectsForCompact */ + /* begin allPastSpaceObjectsDo: */ + /* begin allPastSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + address = ((GIV(pastSpace)).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + while (oopisLessThan(objOopSqInt, GIV(pastSpaceStart))) { + assert(isEnumerableObjectNoAssert(objOopSqInt)); + if ((byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + objOopSqInt = GIV(pastSpaceStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + + /* begin endSlidingCompaction */ + GIV(gcPhaseInProgress) = 0; + if (GIV(rememberedSetSize) > 0) { + objOop = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(0, objOop, GIV(firstFieldOfRememberedSet))); + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),GIV(firstFieldOfRememberedSet)); + } + setIsPinnedOfto(longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))), 1); + + /* begin relocateRememberedSet */ + GIV(rememberedSet) = firstIndexableField(longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord()))))))); + if (/* savedFirstFieldsSpaceWasAllocated */ + GIV(savedFirstFieldsSpaceNotInOldSpace) + && (oopisGreaterThan((GIV(savedFirstFieldsSpace).start), GIV(nilObj)))) { + sqDeallocateMemorySegmentAtOfSize(((void *)((GIV(savedFirstFieldsSpace).start))), ((GIV(savedFirstFieldsSpace).limit)) - ((GIV(savedFirstFieldsSpace).start))); + } +} + + +/* Free from toFinger up to limit, dealing with possible intervening pinned + objects. + */ + + /* SpurPlanningCompactor>>#freeFrom:upTo:nextObject: */ +static NoDbgRegParms void +freeFromupTonextObject(usqInt initialToFinger, usqInt limit, sqInt nextObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + sqInt obj; + usqInt objStart; + SpurSegmentInfo *seg; + usqInt toFinger; + + toFinger = initialToFinger; + objStart = /* startOfObject: */ + ((byteAt((void *)(nextObject + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? nextObject - BaseHeaderSize + : nextObject); + if (toFinger < objStart) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += objStart - toFinger; + freeChunkWithBytesat(objStart - toFinger, toFinger); + } + toFinger = objStart; + while (objStart < limit) { + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(objStart + (numSlotsFieldByteOffset()))); + obj = (numSlots == (numSlotsMask()) + ? objStart + BaseHeaderSize + : objStart); + if ((((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && (((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)) { + /* begin unmarkPinned: */ + if (((longAt((void *)(obj))) & (classIndexMask())) == (segmentBridgePun())) { + assert(isMarked(obj)); + } + else { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(obj))); + byteAtput((void *)(obj + (markBitsByteOffset())),(byteAt((void *)(obj + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + + /* begin notePinned: */ + assert(isPinned(obj)); + assert(!((isSegmentBridge(obj)))); + seg = segmentContainingObj(obj); + (seg->containsPinned = 1); + } + if (toFinger < objStart) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += objStart - toFinger; + freeChunkWithBytesat(objStart - toFinger, toFinger); + } + toFinger = (objStart = addressAfter(obj)); + } + else { + objStart = addressAfter(obj); + } + } + if (limit > toFinger) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += limit - toFinger; + freeChunkWithBytesat(limit - toFinger, toFinger); + } +} + + /* SpurPlanningCompactor>>#isMobile: */ +static NoDbgRegParms sqInt +isMobile(sqInt obj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (oopisGreaterThanOrEqualToandLessThanOrEqualTo(obj, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)); +} + + +/* For asserts */ + + /* SpurPlanningCompactor>>#isPostMobile: */ +static NoDbgRegParms sqInt +isPostMobile(sqInt obj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return oopisGreaterThanOrEqualToandLessThanOrEqualTo(obj, GIV(mobileStart), GIV(lastMobileObject)); +} + + +/* Sweep the heap from firstFreeObject forwarding marked objects to where + they can be moved to, saving their forwarding pointer in + savedFirstFieldsSpace. Continue until either the end of the heap is + reached or savedFirstFieldsSpace is full. + Answer if the end of the heap was reached (savedFirstFieldsSpace has not + overflowed). + The enumerations in planCompactSavingForwarders, + updatePointersInMobileObjects and copyAndUnmarkMobileObjects + match. We could implement them as a single enumeration method taking + several block arguments, but arguably that + would make understanding an already tricky algorithm more difficult. + Instead we tolerate the duplication and encourage + the reader to diff the three methods to see where they diverge (e.g. via + Cmd-shift-C). */ + + /* SpurPlanningCompactor>>#planCompactSavingForwarders */ +static NeverInline sqInt +planCompactSavingForwarders(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt availableSpace; + usqInt bytes; + sqInt eventualLocation; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + usqInt previousPin; + sqInt prevObj; + sqInt prevPrevObj; + usqInt startOfPreviousPin; + usqInt toFinger; + usqInt top; + + availableSpace = 0; + previousPin = 0; + assert(!((isMarked(GIV(firstFreeObject))))); + toFinger = /* startOfObject: */ + ((byteAt((void *)(GIV(firstFreeObject) + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? GIV(firstFreeObject) - BaseHeaderSize + : GIV(firstFreeObject)); + top = (GIV(savedFirstFieldsSpace).start); + startOfPreviousPin = 0; + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(firstFreeObject))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstFreeObject); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + assert((previousPin == null + ? toFinger <= (startOfObject(objOop)) + : (isMarked(previousPin)) + && (toFinger <= startOfPreviousPin))); + assert(GIV(savedFirstFieldsSpaceNotInOldSpace) + || (toFinger < top)); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + if (!previousPin) { + previousPin = objOop; + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + } + } + else { + bytes = bytesInBody(objOop); + while ((toFinger <= startOfPreviousPin) + && ((bytes != ((availableSpace = startOfPreviousPin - toFinger))) + && ((bytes + (16)) > availableSpace))) { + /* The object does not fit in the space between toFinger and previousPin. + Move toFinger up to point at the first unmarked or mobile object after + previousPin, or, if previousPin is contiguous with o, to the start of this + object. Update previousPin to be the next pinned object above toFInger + and below this object, or nil if no such pinned object exists. + Any unfillable gaps between adjacent pinned objects will be freed. */ + do { + toFinger = addressAfter(previousPin); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(toFinger + (numSlotsFieldByteOffset()))); + previousPin = (numSlots == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + } while((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && ((((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0) + && (previousPin < objOop))); + + /* Now previousPin is either equal to o or mobile. + Move it to the next pinned object below o */ + while (!((previousPin >= objOop) + || ((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && (((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + previousPin = oldSpaceObjectAfter(previousPin); + } + if (previousPin >= objOop) { + previousPin = null; + startOfPreviousPin = 0; + } + else { + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(previousPin + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? previousPin - BaseHeaderSize + : previousPin); + } + } + + /* begin forwardMobileObject:to:savedFirstFieldPtr: */ + GIV(lastMobileObject) = objOop; + eventualLocation = ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + longAtput((void *)(top),longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(0, objOop, eventualLocation)); + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),eventualLocation); + toFinger += bytes; + if (((top += BytesPerOop)) >= ((GIV(savedFirstFieldsSpace).limit))) { + (GIV(savedFirstFieldsSpace).top = top - BytesPerOop); + GIV(objectAfterLastMobileObject) = oldSpaceObjectAfter(GIV(lastMobileObject)); + return 0; + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* If the heap is already fully compacted there will be no lastMobileObject... */ + if (GIV(lastMobileObject)) { + (GIV(savedFirstFieldsSpace).top = top - BytesPerOop); + GIV(objectAfterLastMobileObject) = oldSpaceObjectAfter(GIV(lastMobileObject)); + } + return 1; +} + + +/* Search for firstFreeObject and firstMobileObject from initialObject, which + is the + hiddenRootsObject on the first pass, and the objectAfterLastMobileObject + on subsequent passes). */ + + /* SpurPlanningCompactor>>#reinitializeScanFrom: */ +static NoDbgRegParms void +reinitializeScanFrom(sqInt initialObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + + GIV(firstFreeObject) = scanForFirstFreeAndFirstMobileObjectFrom(initialObject); + if (GIV(firstFreeObject)) { + GIV(mobileStart) = /* startOfObject: */ + ((byteAt((void *)(GIV(firstFreeObject) + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? GIV(firstFreeObject) - BaseHeaderSize + : GIV(firstFreeObject)); + } + if (GIV(objectAfterLastMobileObject)) { + /* begin allOldSpaceEntitiesFrom:to:do: */ + assert((isNonImmediate(GIV(firstFreeObject))) + && (isInSegments(GIV(firstFreeObject)))); + assert((isNonImmediate(GIV(objectAfterLastMobileObject))) + && (isInSegments(GIV(objectAfterLastMobileObject)))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstFreeObject); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThanOrEqualTo(objOop, GIV(objectAfterLastMobileObject)))) break; + assert((long64At((void *)(objOop))) != 0); + if (!((((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0) + || ((!(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) + || (GIV(objectAfterLastMobileObject) == objOop)))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + GIV(firstMobileObject) = GIV(objectAfterLastMobileObject); + } +} + + +/* Scavenge or simply follow objOop. Answer the new location of objOop. + The send should have been guarded by a send of shouldRemapOop:. + The method is called remapObj: for compatibility with ObjectMemory. */ + + /* SpurPlanningCompactor>>#remapObj: */ +sqInt +remapObj(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt resolvedObj; + + /* begin slidingCompactionRemapObj: */ + assert(shouldRemapOop(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + resolvedObj = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(resolvedObj & (tagMask())))) + && ((!((longAt((void *)(resolvedObj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + resolvedObj = longAt((void *)((resolvedObj + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + else { + assert(!((isInFutureSpace(objOop)))); + resolvedObj = objOop; + } + if (GIV(gcPhaseInProgress) > 0) { + if (GIV(gcPhaseInProgress) == ScavengeInProgress) { + if ((/* isReallyYoung: */ + ((!(resolvedObj & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(resolvedObj)), + (oopisLessThan(resolvedObj, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(resolvedObj, GIV(newSpaceStart)))))) + && (!(oopisGreaterThanOrEqualToandLessThan(resolvedObj, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))))) { + return copyAndForward(resolvedObj); + } + } + else { + assert(slidingCompactionInProgress()); + if (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(objOop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0))) { + return longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + } + return resolvedObj; +} + + /* SpurPlanningCompactor>>#savedFirstFieldsSpaceWasAllocated */ +static sqInt +savedFirstFieldsSpaceWasAllocated(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(savedFirstFieldsSpaceNotInOldSpace) + && (oopisGreaterThan((GIV(savedFirstFieldsSpace).start), GIV(nilObj))); +} + + +/* Scan from initialObject, setting firstMobileObject to the first marked + object after the first free object found, or endOfMemory if none is found. + Answer the first free object found, or nil if none. */ + + /* SpurPlanningCompactor>>#scanForFirstFreeAndFirstMobileObjectFrom: */ +static NoDbgRegParms sqInt +scanForFirstFreeAndFirstMobileObjectFrom(sqInt initialObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firstFree; + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + + firstFree = 0; + GIV(firstMobileObject) = GIV(endOfMemory); + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(initialObject)); + prevPrevObj = (prevObj = null); + objOop = initialObject; + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (firstFree) { + GIV(firstMobileObject) = objOop; + return firstFree; + } + } + else { + if (!firstFree) { + firstFree = objOop; + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + return firstFree; +} + + +/* Answer if the obj should be scavenged, or simply followed. Sent via the + compactor from shouldRemapObj:. We test for being already scavenged + because mapStackPages + via mapInterpreterOops may be applied twice in the context of a global GC + where a + scavenge, followed by a scan-mark-free, and final compaction passes may + result in + scavenged fields being visited twice. */ + + /* SpurPlanningCompactor>>#shouldRemapObj: */ +sqInt +shouldRemapObj(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return /* slidingCompactionShouldRemapObj: */ + ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + || ((GIV(gcPhaseInProgress) > 0) + && ((GIV(gcPhaseInProgress) == ScavengeInProgress + ? ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(objOop)), + (oopisLessThan(objOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(objOop, GIV(newSpaceStart))))) + && (!(oopisGreaterThanOrEqualToandLessThan(objOop, ((GIV(futureSpace)).start), GIV(futureSurvivorStart)))) + : /* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(objOop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0))))); +} + + +/* Sweep the final immobile heap, freeing and coalescing unmarked and free + objects, and unmarking all marked objects up to the end of memory. */ + + /* SpurPlanningCompactor>>#unmarkObjectsFromFirstFreeObject */ +static void +unmarkObjectsFromFirstFreeObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt freeBytes; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + SpurSegmentInfo *seg; + usqInt startOfFree; + + startOfFree = 0; + freeBytes = 0; + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(firstFreeObject))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstFreeObject); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (startOfFree) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += freeBytes; + freeChunkWithBytesat(freeBytes, startOfFree); + startOfFree = null; + freeBytes = 0; + } + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + /* begin unmarkPinned: */ + if (((longAt((void *)(objOop))) & (classIndexMask())) == (segmentBridgePun())) { + assert(isMarked(objOop)); + } + else { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + + /* begin notePinned: */ + assert(isPinned(objOop)); + assert(!((isSegmentBridge(objOop)))); + seg = segmentContainingObj(objOop); + (seg->containsPinned = 1); + } + } + else { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + else { + if (!startOfFree) { + startOfFree = /* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + } + freeBytes += bytesInBody(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + if (startOfFree) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += freeBytes; + freeChunkWithBytesat(freeBytes, startOfFree); + } +} + + +/* Sweep the heap, updating all objects to their eventual locations. + Remember to update the savedFirstFields of pointer objects, as these have + been forwarded. */ + + /* SpurPlanningCompactor>>#updatePointers */ +static NeverInline void +updatePointers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt availableSpace; + usqInt bytes; + sqInt classIndex; + sqInt contextSize; + sqInt fmt; + sqInt followingWord; + usqInt followingWordAddress; + sqInt fwd; + sqInt header; + sqInt headerSqInt; + sqInt heapEntity; + sqInt i; + usqInt numLiterals; + usqInt numPointerSlots; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objOop; + sqInt objOopSqInt; + sqInt onePass; + sqInt oop; + usqInt previousPin; + sqInt prevObj; + sqInt prevPrevObj; + sqInt sp; + usqInt startOfPreviousPin; + usqInt toFinger; + usqInt top; + + if (!(GIV(lastMobileObject))) { + return; + } + assert((startOfObject(GIV(firstFreeObject))) == GIV(mobileStart)); + mapInterpreterOops(); + mapExtraRoots(); + + /* begin updatePointersInManagerHeapEntities */ + /* begin relocateObjStacksForPlanningCompactor */ + GIV(markStack) = relocateObjStackForPlanningCompactorandContents(GIV(markStack), 0); + GIV(weaklingStack) = relocateObjStackForPlanningCompactorandContents(GIV(weaklingStack), 0); + GIV(mournQueue) = relocateObjStackForPlanningCompactorandContents(GIV(mournQueue), 1); + if ((GIV(rememberedSetSize) > 0) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(GIV(firstFieldOfRememberedSet), GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(GIV(firstFieldOfRememberedSet) + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + GIV(firstFieldOfRememberedSet) = longAt((void *)((GIV(firstFieldOfRememberedSet) + BaseHeaderSize) + (0U << (shiftForWord())))); + } + heapEntity = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))); + + /* begin relocateObjectsInHeapEntity:from:to: */ + for (i = 1; i < GIV(rememberedSetSize); i += 1) { + oop = longAt((void *)((heapEntity + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert(isMarked(oop)); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(heapEntity)) + && (!(isForwarded(heapEntity)))); + assert(validStorePointerUncheckedArgs(i, heapEntity, fwd)); + longAtput((void *)((heapEntity + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + if (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(heapEntity, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(heapEntity + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0))) { + /* fetchPointer:ofObject: */ + longAt((void *)((heapEntity + BaseHeaderSize) + (0U << (shiftForWord())))); + } + else { + } + + /* begin updatePointersInSurvivingObjects */ + /* begin allPastSpaceObjectsDo: */ + /* begin allPastSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + address = ((GIV(pastSpace)).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + while (oopisLessThan(objOopSqInt, GIV(pastSpaceStart))) { + assert(isEnumerableObjectNoAssert(objOopSqInt)); + + /* begin updatePointersIn: */ + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objOopSqInt + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l3; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOopSqInt))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l3: + numPointerSlots = CtxtTempFrameStart + contextSize; + goto l2; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOopSqInt)) > (isForwardedObjectClassIndexPun())); + numPointerSlots = (((numSlots = byteAt((void *)(objOopSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOopSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l2; + } + if (fmt == (forwardedFormat())) { + numPointerSlots = 1; + goto l2; + } + if (fmt < (firstCompiledMethodFormat())) { + numPointerSlots = 0; + goto l2; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOopSqInt)); + headerSqInt = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numPointerSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l2: + for (i = 0; i < numPointerSlots; i += 1) { + oop = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert((isMarked(oop)) + || (objOopSqInt == (hiddenRootsObject()))); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOopSqInt)) + && (!(isForwarded(objOopSqInt)))); + assert(validStorePointerUncheckedArgs(i, objOopSqInt, fwd)); + longAtput((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + objOopSqInt = GIV(pastSpaceStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + + /* begin updatePointersInInitialImmobileObjects */ + /* begin allOldSpaceObjectsFrom:do: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objOopSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objOopSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (oopisGreaterThanOrEqualTo(objOopSqInt, GIV(firstFreeObject))) { + goto l7; + } + + /* would like to assert this, but it isn't true if more than one pass: self assert: (manager isMarked: o). */ + + /* begin updatePointersIn: */ + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objOopSqInt + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l4; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOopSqInt))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l4: + numPointerSlots = CtxtTempFrameStart + contextSize; + goto l6; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOopSqInt)) > (isForwardedObjectClassIndexPun())); + numPointerSlots = (((numSlots = byteAt((void *)(objOopSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOopSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l6; + } + if (fmt == (forwardedFormat())) { + numPointerSlots = 1; + goto l6; + } + if (fmt < (firstCompiledMethodFormat())) { + numPointerSlots = 0; + goto l6; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOopSqInt)); + headerSqInt = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numPointerSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l6: + for (i = 0; i < numPointerSlots; i += 1) { + oop = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert((isMarked(oop)) + || (objOopSqInt == (hiddenRootsObject()))); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOopSqInt)) + && (!(isForwarded(objOopSqInt)))); + assert(validStorePointerUncheckedArgs(i, objOopSqInt, fwd)); + longAtput((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l5; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l5:; + } + /* end updatePointersInInitialImmobileObjects */ +l7: + + /* begin updatePointersInMobileObjects */ + availableSpace = 0; + previousPin = 0; + assert(!((isMarked(GIV(firstFreeObject))))); + toFinger = /* startOfObject: */ + ((byteAt((void *)(GIV(firstFreeObject) + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? GIV(firstFreeObject) - BaseHeaderSize + : GIV(firstFreeObject)); + top = (GIV(savedFirstFieldsSpace).start); + startOfPreviousPin = 0; + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(firstFreeObject))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstFreeObject); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + assert((previousPin == null + ? toFinger <= (startOfObject(objOop)) + : (isMarked(previousPin)) + && (toFinger <= startOfPreviousPin))); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + if (!previousPin) { + previousPin = objOop; + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + } + + /* begin updatePointersIn: */ + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l8; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l8: + numPointerSlots = CtxtTempFrameStart + contextSize; + goto l10; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numPointerSlots = (((numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + goto l10; + } + if (fmt == (forwardedFormat())) { + numPointerSlots = 1; + goto l10; + } + if (fmt < (firstCompiledMethodFormat())) { + numPointerSlots = 0; + goto l10; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numPointerSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l10: + for (i = 0; i < numPointerSlots; i += 1) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert((isMarked(oop)) + || (objOop == (hiddenRootsObject()))); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(i, objOop, fwd)); + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + } + else { + bytes = bytesInBody(objOop); + while ((toFinger <= startOfPreviousPin) + && ((bytes != ((availableSpace = startOfPreviousPin - toFinger))) + && ((bytes + (16)) > availableSpace))) { + /* The object does not fit in the space between toFinger and previousPin. + Move toFinger up to point at the first unmarked or mobile object after + previousPin, or, if previousPin is contiguous with o, to the start of this + object. Update previousPin to be the next pinned object above toFInger + and below this object, or nil if no such pinned object exists. + Any unfillable gaps between adjacent pinned objects will be freed. */ + do { + toFinger = addressAfter(previousPin); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(toFinger + (numSlotsFieldByteOffset()))); + previousPin = (numSlots == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + } while((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && ((((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0) + && (previousPin < objOop))); + + /* Now previousPin is either equal to o or mobile. + Move it to the next pinned object below o */ + while (!((previousPin >= objOop) + || ((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && (((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + previousPin = oldSpaceObjectAfter(previousPin); + } + if (previousPin >= objOop) { + previousPin = null; + startOfPreviousPin = 0; + } + else { + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(previousPin + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? previousPin - BaseHeaderSize + : previousPin); + } + } + updatePointersInsavedFirstFieldPointer(objOop, top); + toFinger += bytes; + if (((top += BytesPerOop)) >= ((GIV(savedFirstFieldsSpace).limit))) { + assert(((GIV(savedFirstFieldsSpace).top)) == (top - BytesPerOop)); + onePass = 0; + goto l11; + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l9; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l9:; + } + assert(((GIV(savedFirstFieldsSpace).top)) == (top - BytesPerOop)); + onePass = 1; + /* end updatePointersInMobileObjects */ +l11: + if (!onePass) { + /* begin updatePointersInObjectsOverflowingSavedFirstFieldsSpace */ + /* begin allOldSpaceObjectsFrom:do: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(objectAfterLastMobileObject))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(objectAfterLastMobileObject); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objOopSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objOopSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + /* begin updatePointersIn: */ + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objOopSqInt + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l12; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOopSqInt))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l12: + numPointerSlots = CtxtTempFrameStart + contextSize; + goto l14; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOopSqInt)) > (isForwardedObjectClassIndexPun())); + numPointerSlots = (((numSlots = byteAt((void *)(objOopSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOopSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l14; + } + if (fmt == (forwardedFormat())) { + numPointerSlots = 1; + goto l14; + } + if (fmt < (firstCompiledMethodFormat())) { + numPointerSlots = 0; + goto l14; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOopSqInt)); + headerSqInt = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numPointerSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l14: + for (i = 0; i < numPointerSlots; i += 1) { + oop = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert((isMarked(oop)) + || (objOopSqInt == (hiddenRootsObject()))); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOopSqInt)) + && (!(isForwarded(objOopSqInt)))); + assert(validStorePointerUncheckedArgs(i, objOopSqInt, fwd)); + longAtput((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l13; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l13:; + } + } +} + + +/* Sweep the pointer fields in obj, updating all references to mobile objects + to their eventual locations. + firstFieldPtr is supplied for mobile objects so that the saved first field + can be updated, and so that + the first field of a compiled method (which is its header, or reference to + a CogMethod holding its header) + can be retrieved. */ + + /* SpurPlanningCompactor>>#updatePointersIn:savedFirstFieldPointer: */ +static NoDbgRegParms void +updatePointersInsavedFirstFieldPointer(sqInt obj, sqInt firstFieldPtr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt field; + sqInt fmt; + sqInt fwd; + sqInt header; + sqInt i; + usqInt numLiterals; + sqInt numPointerSlots; + usqInt numSlots; + sqInt oop; + sqInt sp; + + fmt = (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask()); + + /* begin numPointerSlotsWhileCompactingOf:withFormat:savedFirstFieldPointer: */ + assert((firstFieldPtr) + && (isMobile(obj))); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l2; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(obj))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l2: + numPointerSlots = ((usqInt) (CtxtTempFrameStart + contextSize)); + goto l3; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())); + numPointerSlots = (((numSlots = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l3; + } + assert(!((fmt == (forwardedFormat())))); + if (fmt < (firstCompiledMethodFormat())) { + numPointerSlots = 0; + goto l3; + } + field = longAt((void *)(firstFieldPtr)); + + /* begin methodHeaderFromSavedFirstField: */ + if ((((field) & 7) == 1)) { + header = field; + goto l1; + } + assert((isNonImmediate(field)) + && (field < GIV(newSpaceStart))); + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) field))->objectHeader)); + header = ((((CogMethod *) field))->methodHeader); + /* end methodHeaderFromSavedFirstField: */ +l1: + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numPointerSlots = numLiterals + LiteralStart; + /* end numPointerSlotsWhileCompactingOf:withFormat:savedFirstFieldPointer: */ +l3: + if ((fmt <= 5 /* lastPointerFormat */) + && (numPointerSlots > 0)) { + oop = longAt((void *)(firstFieldPtr)); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert(isMarked(oop)); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + longAtput((void *)(firstFieldPtr),fwd); + } + } + + /* excludes CompiledMethod + Relocate the saved first field; Note that CompiledMethods can be excluded since their + first field is either a SmallInteger or a reference to a CogMethod outside of oldSpace. */ + for (i = 1; i < numPointerSlots; i += 1) { + oop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert((isMarked(oop)) + || (obj == (hiddenRootsObject()))); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(obj)) + && (!(isForwarded(obj)))); + assert(validStorePointerUncheckedArgs(i, obj, fwd)); + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } +} + + +/* Attempt to allocate a memory segment large enough to hold the + savedFirstFieldsSpace. Invoked when neither eden nor a large free chunk + are found to be big enough for the job. */ + + /* SpurPlanningCompactor>>#useSegmentForSavedFirstFieldsSpace: */ +static NoDbgRegParms sqInt +useSegmentForSavedFirstFieldsSpace(sqInt spaceEstimate) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt allocatedSize; + sqInt roundedSize; + void *segAddress; + + allocatedSize = 0; + roundedSize = ((spaceEstimate + 0x3FF) / 0x400) * 0x400; + if ((segAddress = sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto(roundedSize, firstGapOfSizeAtLeast(roundedSize), (&allocatedSize)))) { + (GIV(savedFirstFieldsSpace).start = ((usqIntptr_t)segAddress)); + (GIV(savedFirstFieldsSpace).limit = (((usqIntptr_t)segAddress)) + allocatedSize); + GIV(savedFirstFieldsSpaceNotInOldSpace) = 1; + assert(savedFirstFieldsSpaceWasAllocated()); + return 1; + } + + /* sent to the manager so that the simulator can increase memory to simulate a new segment */ + return 0; +} + + +/* Answer 0 if all the mobile objects from firstMobileObject to + lastMobileObject have sane forwarding addresses, and that + savedFirstFieldsSpace is of + matching capacity. Otherwise answer an error code identifying the anomaly. */ + + /* SpurPlanningCompactor>>#validRelocationPlanInPass: */ +static NoDbgRegParms sqInt +validRelocationPlanInPass(sqInt onePass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt destination; + sqInt followingWord; + usqInt followingWordAddress; + sqInt nMobiles; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt toFinger; + + nMobiles = 0; + toFinger = GIV(mobileStart); + GIV(anomaly) = null; + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(firstMobileObject))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstMobileObject); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift())))) { + nMobiles += 1; + destination = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (!(destination >= toFinger)) { + GIV(anomaly) = objOop; + return 1; + } + toFinger += bytesInBody(objOop); + if (oopisGreaterThan(objOop, GIV(lastMobileObject))) { + GIV(anomaly) = objOop; + return 2; + } + if (objOop == GIV(lastMobileObject)) { + return ((((((GIV(savedFirstFieldsSpace).top)) + BytesPerOop) - ((GIV(savedFirstFieldsSpace).start))) / BytesPerOop) == nMobiles + ? 0 + : 3); + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* N.B. written this way so that if there are no mobiles the expression evaluates to 0 in Smalltalk /and/ in C unsigned arithmetic. */ + return ((((((GIV(savedFirstFieldsSpace).top)) + BytesPerOop) - ((GIV(savedFirstFieldsSpace).start))) / BytesPerOop) == nMobiles + ? 0 + : 4); +} + + /* SpurSegmentInfo>>#segLimit */ +static NoDbgRegParms usqInt +segLimit(SpurSegmentInfo *self_in_SpurSegmentInfo) +{ + return ((self_in_SpurSegmentInfo->segSize)) + ((self_in_SpurSegmentInfo->segStart)); +} + + /* SpurSegmentManager>>#addSegmentOfSize: */ +static NoDbgRegParms SpurSegmentInfo * +addSegmentOfSize(sqInt ammount) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt allocatedSize; + sqInt i; + sqInt idx; + sqInt lastSegIndex; + SpurSegmentInfo *newSeg; + sqInt newSegIndex; + void *segAddress; + sqInt segAddressSqInt; + sqInt segIndex; + + newSegIndex = 0; + allocatedSize = 0; + if ((segAddress = sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto(ammount, firstGapOfSizeAtLeast(ammount), (&allocatedSize)))) { + segAddressSqInt = ((usqIntptr_t)segAddress); + + /* begin insertSegmentFor: */ + assert(segAddressSqInt >= (segLimit(&GIV(segments)[0]))); + if (GIV(numSegments) == GIV(numSegInfos)) { + allocateOrExtendSegmentInfos(); + } + assert(GIV(numSegments) < GIV(numSegInfos)); + segIndex = (lastSegIndex = GIV(numSegments) - 1); + GIV(numSegments) += 1; + while (1) { + if (segAddressSqInt >= ((((GIV(segments)[segIndex]).segSize)) + (((GIV(segments)[segIndex]).segStart)))) { + segIndex += 1; + for (idx = lastSegIndex; idx >= segIndex; idx += -1) { + GIV(segments)[idx + 1] = (GIV(segments)[idx]); + } + newSegIndex = segIndex; + goto l1; + } + segIndex -= 1; + } + /* end insertSegmentFor: */ +l1: + + /* Simulation insertion code duplicates entries if newSegIndex ~= numSegments - 1 */ + newSeg = (&(GIV(segments)[newSegIndex])); + (newSeg->segStart = ((usqIntptr_t)segAddress)); + (newSeg->segSize = allocatedSize); + (newSeg->swizzle = 0); + assert(!(segmentOverlap())); + bridgeFromto((&(GIV(segments)[newSegIndex - 1])), newSeg); + bridgeFromto(newSeg, (!(newSegIndex == (GIV(numSegments) - 1)) + ? (&(GIV(segments)[newSegIndex + 1])) + : 0)); + GIV(totalHeapSizeIncludingBridges) += allocatedSize; + + /* test isInMemory: */ + for (i = 0; i < GIV(numSegments); i += 1) { + assert(isInSegments(((GIV(segments)[i]).segStart))); + assert(isInSegments((segLimit(&GIV(segments)[i])) - BytesPerWord)); + assert((!(isInSegments(segLimit(&GIV(segments)[i])))) + || ((i < (GIV(numSegments) - 1)) + && ((segLimit(&GIV(segments)[i])) == (((GIV(segments)[i + 1]).segStart))))); + assert((!(isInSegments((((GIV(segments)[i]).segStart)) - BytesPerWord))) + || ((i > 0) + && ((segLimit(&GIV(segments)[i - 1])) == (((GIV(segments)[i]).segStart))))); + } + return newSeg; + } + + /* sent to the manager so that the simulator can increase memory to simulate a new segment */ + return null; +} + + +/* Adjust swizzles by firstSegmentShift. Also computes segStarts as + they were in the image when it was written, so that oops' segments + can be determined and hence oops correctly swizzled. */ + + /* SpurSegmentManager>>#adjustSegmentSwizzlesBy: */ +static NoDbgRegParms void +adjustSegmentSwizzlesBy(sqInt firstSegmentShift) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt oldBaseAddr; + SpurSegmentInfo *segInfo; + + oldBaseAddr = GIV(oldSpaceStart) - firstSegmentShift; + for (i = 0; i < GIV(numSegments); i += 1) { + segInfo = (&(GIV(segments)[i])); + (segInfo->segStart = ((segInfo->segStart)) + oldBaseAddr); + (segInfo->swizzle = ((segInfo->swizzle)) - oldBaseAddr); + } + GIV(canSwizzle) = 1; +} + + /* SpurSegmentManager>>#allBridgesMarked */ +static sqInt +allBridgesMarked(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt bridgeObj; + sqInt i; + usqInt numSlots; + + for (i = 0; i < GIV(numSegments); i += 1) { + /* begin bridgeAt: */ + /* begin bridgeFor: */ + address = (((((&(GIV(segments)[i])))->segSize)) + ((((&(GIV(segments)[i])))->segStart))) - (2 * BaseHeaderSize); + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + bridgeObj = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + assert(isValidSegmentBridge(bridgeObj)); + if (!((byteAt((void *)(bridgeObj + (markBitsByteOffset())))) & (1U << (markedBitByteShift())))) { + return 0; + } + } + return 1; +} + + +/* Increase the number of allocated segInfos by 16. */ + + /* SpurSegmentManager>>#allocateOrExtendSegmentInfos */ +static void +allocateOrExtendSegmentInfos(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt newNumSegs; + + if (!GIV(numSegInfos)) { + GIV(numSegInfos) = 16; + GIV(segments) = calloc(GIV(numSegInfos), sizeof(SpurSegmentInfo)); + return; + } + newNumSegs = GIV(numSegInfos) + 16; + GIV(segments) = realloc(GIV(segments), newNumSegs * (sizeof(SpurSegmentInfo))); + if (!GIV(segments)) { + error("out of memory; cannot allocate more segments"); + } + memset(GIV(segments) + GIV(numSegInfos), 0, (newNumSegs - GIV(numSegInfos)) * (sizeof(SpurSegmentInfo))); + GIV(numSegInfos) = newNumSegs; +} + + /* SpurSegmentManager>>#bridgeAt: */ +static NoDbgRegParms sqInt +bridgeAt(sqInt segIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + usqInt numSlots; + + /* begin bridgeFor: */ + address = (((((&(GIV(segments)[segIndex])))->segSize)) + ((((&(GIV(segments)[segIndex])))->segStart))) - (2 * BaseHeaderSize); + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + return (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); +} + + /* SpurSegmentManager>>#bridgeFor: */ +static NoDbgRegParms sqInt +bridgeFor(SpurSegmentInfo *aSegment) +{ + sqInt address; + usqInt numSlots; + + address = (((aSegment->segSize)) + ((aSegment->segStart))) - (2 * BaseHeaderSize); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + return (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); +} + + +/* Create a bridge from aSegment to the next segment, + or create a terminating bridge if there is no next segment. */ + + /* SpurSegmentManager>>#bridgeFrom:to: */ +static NoDbgRegParms void +bridgeFromto(SpurSegmentInfo *aSegment, SpurSegmentInfo *nextSegmentOrNil) +{ + sqInt bridgeSpan; + sqInt clifton; + usqInt segEnd; + + segEnd = ((aSegment->segSize)) + ((aSegment->segStart)); + + /* clifton is where the Avon bridge begins... */ + clifton = segEnd - (2 * BaseHeaderSize); + bridgeSpan = (nextSegmentOrNil + ? (((nextSegmentOrNil->segStart)) - segEnd) + (2 * BaseHeaderSize) + : 2 * BaseHeaderSize); + assert(bridgeSpan >= 0); + initSegmentBridgeWithBytesat(bridgeSpan, clifton); + + /* the revised bridge should get us to the new segment */ + assert((addressAfter(objectStartingAt(clifton))) == ((nextSegmentOrNil == null + ? segLimit(aSegment) + : (nextSegmentOrNil->segStart)))); +} + + /* SpurSegmentManager>>#checkSegments */ +static void +checkSegments(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + assert(GIV(numSegments) >= 1); + for (i = 0; i < GIV(numSegments); i += 1) { + assert(addressCouldBeObj(((GIV(segments)[i]).segStart))); + assert(isValidSegmentBridge(bridgeAt(i))); + } + assert(((segLimit(&GIV(segments)[GIV(numSegments) - 1])) - (bridgeSize())) == (GIV(endOfMemory))); +} + + +/* The image has been loaded, old segments reconstructed, and the heap + swizzled into a single contiguous segment. Collapse the segments into one. */ + + /* SpurSegmentManager>>#collapseSegmentsPostSwizzle */ +static void +collapseSegmentsPostSwizzle(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + SpurSegmentInfo *cascade0; + + GIV(canSwizzle) = 0; + GIV(numSegments) = 1; + + /* begin computeTotalHeapSizeIncludingBridges */ + GIV(totalHeapSizeIncludingBridges) = GIV(endOfMemory) - GIV(oldSpaceStart); + cascade0 = (&(GIV(segments)[0])); + (cascade0->segStart = GIV(oldSpaceStart)); + (cascade0->segSize = GIV(totalHeapSizeIncludingBridges)); + assert(isSegmentBridge(bridgeAt(0))); + assert((numSlotsOfAny(bridgeAt(0))) == 0); +} + + +/* Answer the segment limit of the first segment followed by a gap of at + least size bytes. + */ + + /* SpurSegmentManager>>#firstGapOfSizeAtLeast: */ +static NoDbgRegParms void * +firstGapOfSizeAtLeast(sqInt size) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt bridge; + sqInt i; + usqInt numSlots; + + for (i = 0; i <= (GIV(numSegments) - 2); i += 1) { + /* begin bridgeAt: */ + /* begin bridgeFor: */ + address = (((((&(GIV(segments)[i])))->segSize)) + ((((&(GIV(segments)[i])))->segStart))) - (2 * BaseHeaderSize); + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + bridge = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + if (((bytesInBody(bridge)) - (2 * BaseHeaderSize)) >= size) { + return ((void *)((((GIV(segments)[i]).segSize)) + (((GIV(segments)[i]).segStart)))); + } + } + return ((void *)((((GIV(segments)[GIV(numSegments) - 1]).segSize)) + (((GIV(segments)[GIV(numSegments) - 1]).segStart)))); +} + + /* SpurSegmentManager>>#isEmptySegment: */ +static NoDbgRegParms sqInt +isEmptySegment(SpurSegmentInfo *seg) +{ + sqInt address; + sqInt firstObj; + usqInt numSlots; + + address = (seg->segStart); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + firstObj = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + return (((longAt((void *)(firstObj))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) + && ((addressAfter(firstObj)) == ((((seg->segSize)) + ((seg->segStart))) - (2 * BaseHeaderSize))); +} + + /* SpurSegmentManager>>#isInSegments: */ +static NoDbgRegParms sqInt +isInSegments(usqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + for (i = 0; i < GIV(numSegments); i += 1) { + if (address < (((GIV(segments)[i]).segStart))) { + return 0; + } + if (address < ((((GIV(segments)[i]).segSize)) + (((GIV(segments)[i]).segStart)))) { + return 1; + } + } + return 0; +} + + +/* bridges bridge the gaps between segments. They are the last object in each + segment. + */ + + /* SpurSegmentManager>>#isValidSegmentBridge: */ +static NoDbgRegParms sqInt +isValidSegmentBridge(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((addressCouldBeObj(objOop)) + || (objOop == GIV(endOfMemory))) + && ((((longAt((void *)(objOop))) & (classIndexMask())) == (segmentBridgePun())) + && (((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) + || ((numSlotsOfAny(objOop)) == 0))); +} + + +/* Answer the the next non-empty segment or nil. The size of a segment + includes that of its bridge. A segment containing just a free object and a + bridge will still + have a size of manager bridgeSize after shortening it in + prepareForSnapshot. + */ + + /* SpurSegmentManager>>#nextNonEmptySegmentAfter: */ +static NoDbgRegParms SpurSegmentInfo * +nextNonEmptySegmentAfter(sqInt i) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt nextx; + + nextx = i; + while (1) { + if (((nextx += 1)) >= GIV(numSegments)) { + return null; + } + if ((((GIV(segments)[nextx]).segSize)) > (2 * BaseHeaderSize)) { + return (&(GIV(segments)[nextx])); + } + } + return 0; +} + + +/* shorten all segments by any trailing free space. */ + + /* SpurSegmentManager>>#prepareForSnapshot */ +static NeverInline void +prepareForSnapshot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cameFrom; + SpurSegmentInfo *cascade0; + usqInt chunkBytes; + sqInt followingWord; + usqInt followingWordAddress; + usqInt freeChunk; + sqInt i; + sqInt largeChild; + sqInt newEndOfMemory; + sqInt next; + usqInt node; + SpurSegmentInfo *seg; + SpurSegmentInfo *selfOfSegSize; + sqInt smallChild; + sqInt treeNode; + + checkSegments(); + for (i = 0; i < GIV(numSegments); i += 1) { + cascade0 = (&(GIV(segments)[i])); + (cascade0->savedSegSize = ((GIV(segments)[i]).segSize)); + (cascade0->lastFreeObject = null); + } + + /* Ideally finding the lastFreeObject of each segment would be + done in some linear pass through the heap. But for now KISS. */ + + /* begin freeTreeNodesDo: */ + treeNode = GIV(freeLists)[0]; + if (!treeNode) { + goto l2; + } + cameFrom = -1; + do { + assert((bytesInBody(treeNode)) >= ((numFreeLists()) * (allocationUnit()))); + smallChild = longAt((void *)((treeNode + BaseHeaderSize) + (3U << (shiftForWord())))); + largeChild = longAt((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + assert((smallChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), smallChild)))); + assert((largeChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), largeChild)))); + + /* apply if the node has no children, or it has no large children and we're + returning from the small child, or we're returning from the large child. */ + if (((smallChild == 0) + && (largeChild == 0)) + || ((largeChild + ? cameFrom == largeChild + : cameFrom == smallChild))) { + node = treeNode; + while (node != 0) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(node); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + next = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + next = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1: + if (((longAt((void *)(next))) & (classIndexMask())) == (segmentBridgePun())) { + seg = segmentContainingObj(node); + (seg->lastFreeObject = node); + node = 0; + } + else { + node = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + + /* and since we've applied we must move on up */ + cameFrom = treeNode; + treeNode = longAt((void *)((treeNode + BaseHeaderSize) + (2U << (shiftForWord())))); + } + else { + if ((smallChild != 0) + && (cameFrom != smallChild)) { + treeNode = smallChild; + } + else { + assert(largeChild != 0); + treeNode = largeChild; + } + cameFrom = -1; + } + } while(treeNode != 0); + /* end freeTreeNodesDo: */ +l2: + for (i = 0; i < GIV(numSegments); i += 1) { + if ((freeChunk = ((GIV(segments)[i]).lastFreeObject))) { + /* begin detachFreeObject: */ + chunkBytes = bytesInBody(freeChunk); + GIV(totalFreeOldSpace) -= chunkBytes; + unlinkFreeChunkchunkBytes(freeChunk, chunkBytes); + selfOfSegSize = (&(GIV(segments)[i])); + (selfOfSegSize->segSize = ((/* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk)) + (2 * BaseHeaderSize)) - (((GIV(segments)[i]).segStart))); + bridgeFromto((&(GIV(segments)[i])), (i < (GIV(numSegments) - 1) + ? (&(GIV(segments)[i + 1])) + : 0)); + } + } + + /* perhaps this should read + manager setEndOfMemory: 0; assimilateNewSegment: (segments at: numSegments - 1) */ + newEndOfMemory = ((((GIV(segments)[GIV(numSegments) - 1]).segSize)) + (((GIV(segments)[GIV(numSegments) - 1]).segStart))) - (2 * BaseHeaderSize); + + /* begin setEndOfMemory: */ + GIV(endOfMemory) = newEndOfMemory; + if (GIV(freeOldSpaceStart) > newEndOfMemory) { + GIV(freeOldSpaceStart) = newEndOfMemory; + } +} + + +/* Read numBytes of image data from f into memory at memoryBaseForImageRead. + Answer the number of bytes written. In addition, read each segment, build + up the + segment info for swizzling, while eliminating the bridge objects at the + end of each + segment that specify the distance to and the size of the subsequent + segment. */ + + /* SpurSegmentManager>>#readHeapFromImageFile:dataBytes: */ +static NoDbgRegParms sqInt +readHeapFromImageFiledataBytes(sqImageFile f, sqInt numBytes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bridge; + sqInt bridgehead; + usqInt bridgeSpan; + sqInt bytesRead; + usqInt newBase; + usqInt nextSegmentSize; + sqInt oldBase; + SpurSegmentInfo *segInfo; + sqInt totalBytesRead; + + allocateOrExtendSegmentInfos(); + + /* segment sizes include the two-header-word bridge at the end of each segment. */ + GIV(numSegments) = (totalBytesRead = 0); + + /* N.B. still must be adjusted by oldBaseAddr. */ + oldBase = 0; + newBase = GIV(oldSpaceStart); + nextSegmentSize = GIV(firstSegmentSize); + bridgehead = (GIV(firstSegmentSize) + GIV(oldSpaceStart)) - (2 * BaseHeaderSize); + while (1) { + segInfo = (&(GIV(segments)[GIV(numSegments)])); + (segInfo->segStart = oldBase); + (segInfo->segSize = nextSegmentSize); + (segInfo->swizzle = newBase - oldBase); + bytesRead = sqImageFileRead(pointerForOop(newBase), sizeof(char), nextSegmentSize, f); + if (bytesRead > 0) { + totalBytesRead += bytesRead; + } + if (bytesRead != nextSegmentSize) { + return totalBytesRead; + } + if (((GIV(numSegments) += 1)) >= GIV(numSegInfos)) { + allocateOrExtendSegmentInfos(); + } + bridge = bridgehead + BaseHeaderSize; + bridgeSpan = (byteAt((void *)(bridgehead + (numSlotsFieldByteOffset()))) + ? BytesPerOop * (((((usqInt)(((sqInt)((usqInt)((longAt((void *)(bridge - BaseHeaderSize)))) << 8)))))) >> 8) + : 0); + oldBase = (oldBase + nextSegmentSize) + bridgeSpan; + newBase = (newBase + nextSegmentSize) - (2 * BaseHeaderSize); + nextSegmentSize = ((usqInt)(long64At((void *)(bridge)))); + if (!(nextSegmentSize != 0)) break; + bridgehead = (bridgehead - (2 * BaseHeaderSize)) + nextSegmentSize; + } + + /* newBase should point just past the last bridge. all others should have been eliminated. */ + assert((newBase - (GIV(oldSpaceStart))) == (totalBytesRead - (GIV(numSegments) * (bridgeSize())))); + + /* set freeOldSpaceStart now for adjustAllOopsBy: */ + + /* begin setFreeOldSpaceStart: */ + GIV(freeOldSpaceStart) = newBase; + + /* we're done. nil firstSegmentSize for a subsequent snapshot. */ + GIV(firstSegmentSize) = null; + return totalBytesRead; +} + + +/* Restore all shortened segments to their proper size, re-freeing the + trailing space. + */ + + /* SpurSegmentManager>>#restorePostSnapshot */ +static void +restorePostSnapshot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt bytes; + usqInt freeChunk; + sqInt i; + sqInt newEndOfMemory; + SpurSegmentInfo *seg; + + + /* Set endOfMemory first, to avoid assert fails in freeChunkWithBytes:at:. */ + seg = (&(GIV(segments)[GIV(numSegments) - 1])); + if ((seg->lastFreeObject)) { + newEndOfMemory = (((seg->savedSegSize)) + ((seg->segStart))) - (2 * BaseHeaderSize); + + /* begin setEndOfMemory: */ + GIV(endOfMemory) = newEndOfMemory; + if (GIV(freeOldSpaceStart) > newEndOfMemory) { + GIV(freeOldSpaceStart) = newEndOfMemory; + } + } + else { + assert((GIV(endOfMemory)) == ((segLimit(seg)) - (bridgeSize()))); + } + for (i = (GIV(numSegments) - 1); i >= 0; i += -1) { + seg = (&(GIV(segments)[i])); + if ((freeChunk = (seg->lastFreeObject))) { + address = (((seg->segSize)) + ((seg->segStart))) - (2 * BaseHeaderSize); + (seg->segSize = (seg->savedSegSize)); + bridgeFromto(seg, (i < (GIV(numSegments) - 1) + ? (&(GIV(segments)[i + 1])) + : 0)); + bytes = ((((seg->segSize)) + ((seg->segStart))) - address) - (2 * BaseHeaderSize); + + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += bytes; + freeChunkWithBytesat(bytes, address); + } + } + checkSegments(); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } +} + + +/* Answer the segment containing an object. This is mostly for assert + checking, but + variations on the incremental GC may use it in anger. Binary search is (of + course) marginally slower than linear search for a single segment (e.g. in + a 720k object heap, + 67.1ms vs 61.3ms, or 9.5% slower to derive the segment containing every + old space + entity), but usefully faster for many segments (e.g. 92.7ms vs 116ms, or + 20% faster + in the same heap extended with enough large arrays to require 11 segments; + and this + is pessimal; there are fewer objects at high addresses since the large + arrays are there). */ + + /* SpurSegmentManager>>#segmentContainingObj: */ +SpurSegmentInfo * +segmentContainingObj(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt high; + sqInt low; + sqInt mid; + SpurSegmentInfo *seg; + + low = 0; + mid = GIV(numSegments) / 2; + high = GIV(numSegments) - 1; + do { + seg = (&(GIV(segments)[mid])); + if (oopisGreaterThanOrEqualTo(objOop, (seg->segStart))) { + if (mid == high) { + return (oopisLessThan(objOop, ((seg->segSize)) + ((seg->segStart))) + ? seg + : 0); + } + else { + low = mid; + mid = ((mid + high) + 1) / 2; + } + } + else { + high = mid - 1; + mid = (low + mid) / 2; + } + } while(low <= high); + return null; +} + + +/* Answers true if a segment overlaps with another one. */ + + /* SpurSegmentManager>>#segmentOverlap */ +static sqInt +segmentOverlap(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt endi; + usqInt endj; + sqInt i; + sqInt j; + usqInt starti; + usqInt startj; + + for (i = 0; i < GIV(numSegments); i += 1) { + starti = ((GIV(segments)[i]).segStart); + endi = (((GIV(segments)[i]).segSize)) + (((GIV(segments)[i]).segStart)); + for (j = 0; j < GIV(numSegments); j += 1) { + startj = ((GIV(segments)[j]).segStart); + endj = (((GIV(segments)[j]).segSize)) + (((GIV(segments)[j]).segStart)); + if (!(i == j)) { + if (!((starti < startj) + || (starti >= endj))) { + return 1; + } + if (!((endi <= startj) + || (endi >= endj))) { + return 1; + } + } + } + } + return 0; +} + + +/* Answer if any shrinkage was achieved. */ + + /* SpurSegmentManager>>#shrinkObjectMemory: */ +static NoDbgRegParms int +shrinkObjectMemory(usqInt delta) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + usqInt chunkBytes; + usqInt currentEnd; + sqInt deltaSqInt; + SpurSegmentInfo *emptySeg; + sqInt freeChunk; + sqInt i; + sqInt iSqInt; + sqInt j; + sqInt k; + usqInt numSlots; + SpurSegmentInfo *seg; + SpurSegmentInfo *segInfo; + usqInt shrinkage; + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCCheckFreeSpace)) == (GCCheckFreeSpace | GCCheckFreeSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + shrinkage = delta; + while (1) { + /* begin findEmptySegNearestInSizeTo: */ + emptySeg = null; + deltaSqInt = shrinkage; + for (k = 0; k < GIV(numSegments); k += 1) { + seg = (&(GIV(segments)[k])); + if (isEmptySegment(seg)) { + if (emptySeg) { + if ((shrinkage >= (((seg->segSize)) * 0.75)) + && ((SQABS(((sqInt) (((seg->segSize)) - shrinkage)))) < deltaSqInt)) { + emptySeg = seg; + deltaSqInt = SQABS(((sqInt) (((seg->segSize)) - shrinkage))); + } + } + else { + emptySeg = seg; + } + } + } + if ((!emptySeg) + || (((emptySeg->segSize)) > shrinkage)) { + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCCheckFreeSpace)) == (GCCheckFreeSpace | GCCheckFreeSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return shrinkage < delta; + } + shrinkage -= (emptySeg->segSize); + address = (emptySeg->segStart); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + freeChunk = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin detachFreeObject: */ + chunkBytes = bytesInBody(freeChunk); + GIV(totalFreeOldSpace) -= chunkBytes; + unlinkFreeChunkchunkBytes(freeChunk, chunkBytes); + + /* begin removeSegment: */ + i = 0; + for (iSqInt = 0; iSqInt < GIV(numSegments); iSqInt += 1) { + if (((emptySeg->segStart)) == (((GIV(segments)[iSqInt]).segStart))) { + i = iSqInt; + goto l1; + } + } + error("segment not found"); + /* end indexOfSegment: */ +l1: + assert(i > 0); + GIV(totalHeapSizeIncludingBridges) -= (emptySeg->segSize); + sqDeallocateMemorySegmentAtOfSize(((void *)((emptySeg->segStart))), (emptySeg->segSize)); + for (j = i; j <= (GIV(numSegments) - 2); j += 1) { + GIV(segments)[j] = (GIV(segments)[j + 1]); + } + GIV(numSegments) -= 1; + bridgeFromto((&(GIV(segments)[i - 1])), (i <= (GIV(numSegments) - 1) + ? (&(GIV(segments)[i])) + : 0)); + segInfo = (&(GIV(segments)[GIV(numSegments) - 1])); + + /* begin setLastSegment: */ + currentEnd = (((segInfo->segSize)) + ((segInfo->segStart))) - (2 * BaseHeaderSize); + if (currentEnd <= GIV(endOfMemory)) { + GIV(endOfMemory) = currentEnd; + if (GIV(freeOldSpaceStart) > currentEnd) { + GIV(freeOldSpaceStart) = currentEnd; + } + } + } + return 0; +} + + /* SpurSegmentManager>>#swizzleObj: */ +static NoDbgRegParms sqInt +swizzleObj(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + assert(GIV(canSwizzle)); + for (i = (GIV(numSegments) - 1); i >= 1; i += -1) { + if (objOop >= (((GIV(segments)[i]).segStart))) { + return objOop + (((GIV(segments)[i]).swizzle)); + } + } + return objOop + (((GIV(segments)[0]).swizzle)); +} + + +/* This ``slow'' count is for asserts only. */ + + /* SpurSegmentManager>>#totalBytesInSegments */ +static usqInt +totalBytesInSegments(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + usqInt total; + + total = 0; + for (i = 0; i < GIV(numSegments); i += 1) { + total += ((GIV(segments)[i]).segSize); + } + return total; +} + + /* SpurSegmentManager>>#writeImageSegmentsToFile: */ +static NoDbgRegParms sqInt +writeImageSegmentsToFile(sqImageFile aBinaryStream) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt total; + + assert(((GIV(endOfMemory)) == (segLimit(&GIV(segments)[GIV(numSegments) - 1]))) + || (((GIV(endOfMemory)) + (bridgeSize())) == (segLimit(&GIV(segments)[GIV(numSegments) - 1])))); + if (GIV(firstSegmentSize)) { + assert(GIV(firstSegmentSize) == (((GIV(segments)[0]).segSize))); + } + assert((((GIV(segments)[0]).segSize)) > 0); + total = 0; + for (i = 0; i < GIV(numSegments); i += 1) { + if ((((GIV(segments)[i]).segSize)) > (2 * BaseHeaderSize)) { + total += writeSegmentnextSegmenttoFile((&(GIV(segments)[i])), nextNonEmptySegmentAfter(i), aBinaryStream); + } + } + return total; +} + + +/* Write the segment contents, the size of and the distance to the next + segment to aBinaryStream. + */ + + /* SpurSegmentManager>>#writeSegment:nextSegment:toFile: */ +static NoDbgRegParms sqInt +writeSegmentnextSegmenttoFile(SpurSegmentInfo *segment, SpurSegmentInfo *nextSegment, sqImageFile aBinaryStream) +{ + usqLong firstSavedBridgeWord; + sqInt nWritten; + sqInt pier1; + sqInt pier2; + usqLong secondSavedBridgeWord; + + pier1 = (((segment->segSize)) + ((segment->segStart))) - (2 * BaseHeaderSize); + pier2 = pier1 + BaseHeaderSize; + assert(isValidSegmentBridge(bridgeFor(segment))); + assert((startOfObject(bridgeFor(segment))) == pier1); + + /* Temporarily change the bridge to bridge to the next non-empty segment. + The first double word of the bridge includes the bridge size in slots, and + hence specifies the distance to the next segment. The following double + word is replaced by the size of the next segment, or 0 if there isn't one. */ + firstSavedBridgeWord = long64At((void *)(pier1)); + secondSavedBridgeWord = long64At((void *)(pier2)); + bridgeFromto(segment, nextSegment); + long64Atput((void *)(pier2),(nextSegment + ? (nextSegment->segSize) + : 0)); + nWritten = sqImageFileWrite(((void *)((segment->segStart))), 1, (segment->segSize), aBinaryStream); + long64Atput((void *)(pier1),firstSavedBridgeWord); + long64Atput((void *)(pier2),secondSavedBridgeWord); + return nWritten; +} + + /* StackInterpreter>>#accessorDepthForExternalPrimitiveMethod: */ +static NoDbgRegParms sqInt +accessorDepthForExternalPrimitiveMethod(sqInt methodObj) +{ + sqInt flags; + sqInt lit; + + assert(isLinkedExternalPrimitive(methodObj)); + lit = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + flags = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFlagsIndex) << (shiftForWord())))))); + return (((flags >> 3))) >> SpurPrimitiveAccessorDepthShift; +} + + /* StackInterpreter>>#accessorDepthForPrimitiveMethod: */ +sqInt +accessorDepthForPrimitiveMethod(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + sqInt primIndex; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (aMethodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIndex = 0; + } + return ((primIndex == PrimNumberExternalCall) + && (isLinkedExternalPrimitive(aMethodObj)) + ? accessorDepthForExternalPrimitiveMethod(aMethodObj) + : ((sqInt)((primitiveMetadataTable[primIndex]))) >> SpurPrimitiveAccessorDepthShift); +} + + +/* Answer the current activeProcess. */ +/* Now used for FFI error reaping */ + + /* StackInterpreter>>#activeProcess */ +sqInt +activeProcess(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); +} + + +/* The various poll/select calls in the VM should attempt to tally the + ammount of time spent at idle here, so as to render the uptime value + meaningful. + */ + + /* StackInterpreter>>#addIdleUsecs: */ +void +addIdleUsecs(sqInt idleUsecs) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(statIdleUsecs) += idleUsecs; +} + + +/* Add the given process to the end of the given linked list + and set the backpointer of process to its new list. */ + + /* StackInterpreter>>#addLastLink:toList: */ +static NoDbgRegParms void +addLastLinktoList(sqInt proc, sqInt aList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt lastLink; + + assert(!((isForwarded(proc)))); + assert(!((isForwarded(aList)))); + assert((fetchPointerofObject(NextLinkIndex, proc)) == (nilObject())); + + /* begin isEmptyList: */ + assert(!(isForwarded(aList))); + if ((longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(FirstLinkIndex, aList, proc)); + assert(isNonImmediate(aList)); + if (oopisGreaterThanOrEqualTo(aList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(proc & (tagMask())))) + && (oopisLessThan(proc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))),proc); + } + else { + lastLink = longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord())))))); + assert(lastLink != proc); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(NextLinkIndex, lastLink, proc)); + assert(isNonImmediate(lastLink)); + if (oopisGreaterThanOrEqualTo(lastLink, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(proc & (tagMask())))) + && (oopisLessThan(proc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(lastLink + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(lastLink); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((lastLink + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord()))))),proc); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(LastLinkIndex, aList, proc)); + assert(isNonImmediate(aList)); + if (oopisGreaterThanOrEqualTo(aList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(proc & (tagMask())))) + && (oopisLessThan(proc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord()))))),proc); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(MyListIndex, proc, aList)); + assert(isNonImmediate(proc)); + if (oopisGreaterThanOrEqualTo(proc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(aList & (tagMask())))) + && (oopisLessThan(aList, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(proc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(proc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((proc + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),aList); +} + + /* StackInterpreter>>#allOnesAsCharStar */ +static char * +allOnesAsCharStar(void) +{ + return ((char *) (((usqInt) -1))); +} + + +/* for Cogit */ + + /* StackInterpreter>>#argumentCountOfClosure: */ +sqInt +argumentCountOfClosure(sqInt closurePointer) +{ + sqInt oop; + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((closurePointer + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + return (oop >> 3); +} + + /* StackInterpreter>>#argumentCountOfMethodHeader: */ +usqInt +argumentCountOfMethodHeader(sqInt header) +{ + return (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; +} + + /* StackInterpreter>>#argumentCountOf: */ +usqInt +argumentCountOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin argumentCountOfMethodHeader: */ + return (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; +} + + +/* Return the address of first indexable field of resulting array object, or + fail if + the instance variable does not contain an indexable bytes or words object. */ +/* Note: May be called by translated primitive code. */ + + /* StackInterpreter>>#arrayValueOf: */ +void * +arrayValueOf(sqInt arrayOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (/* isWordsOrBytes: */ + ((!(arrayOop & (tagMask())))) + && (isWordsOrBytesNonImm(arrayOop))) { + return ((void *) (pointerForOop(arrayOop + BaseHeaderSize))); + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; +} + + +/* Returns an integer object */ + + /* StackInterpreter>>#asciiOfCharacter: */ +static NoDbgRegParms sqInt +asciiOfCharacter(sqInt characterObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (((characterObj & (characterTag())) != 0)) { + return characterObj - ((characterTag()) - (smallIntegerTag())); + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return ConstZero; +} + + /* StackInterpreter>>#assertValidExecutionPointe:r:s: */ +void +assertValidExecutionPointers(usqInt lip, char *lifp, char *lisp) +{ + assertValidExecutionPointersimbarline(lip, lifp, lisp, !((((usqInt)(longAt(lifp + FoxMethod)))) < (startOfMemory())), __LINE__); +} + + +/* Order in the stackLimit checks is important because stackLimit is smashed + by interrupts. So always check for unsmashed value first to avoid race + condition. + */ + + /* StackInterpreter>>#assertValidStackLimits: */ +static NoDbgRegParms void +assertValidStackLimits(sqInt ln) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assertl((GIV(stackLimit) == ((GIV(stackPage)->realStackLimit))) + || (GIV(stackLimit) == (allOnesAsCharStar())), ln); + assertl((((GIV(stackPage)->stackLimit)) == ((GIV(stackPage)->realStackLimit))) + || (((GIV(stackPage)->stackLimit)) == (allOnesAsCharStar())), ln); +} + + +/* convert true and false (Smalltalk) to true or false(C) */ + + /* StackInterpreter>>#booleanValueOf: */ +sqInt +booleanValueOf(sqInt obj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (obj == GIV(trueObj)) { + return 1; + } + if (obj == GIV(falseObj)) { + return 0; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; +} + + +/* Re-enter the interpreter to execute a (non-Alien) callback. */ + + /* StackInterpreter>>#callbackEnter: */ +sqInt +callbackEnter(sqInt *callbackID) +{ + warning("callbackEnter: is obsolete"); + return 0; +} + + +/* Leave from a previous callback */ + + /* StackInterpreter>>#callbackLeave: */ +sqInt +callbackLeave(sqInt cbID) +{ + warning("callbackLeave: is obsolete"); + return 0; +} + + +/* Context switch should not be allowed on every method activation. In + particular the + implementation of ensure: and ifCurtailed: depends on there being no + suspension point + on failing primitive 198 (primitiveMarkUnwindMethod, + primitiveMarkUnwindMethod). slowPrimitiveResponse used to state + ``N.B. This means there is no suspension point on primitive failure + which methods such as ensure: and ifCurtailed: rely on.'' + Rather than prevent context switch on all primitives but the ones we + really need + to be suspension points (primitiveSignal et al) we choose to allow context + switch for all but primitiveMarkUnwindMethod and + PrimNumberNoContextSwitch. */ + + /* StackInterpreter>>#canContextSwitchIfActivating:header: */ +sqInt +canContextSwitchIfActivatingheader(sqInt theMethod, sqInt methodHeader) +{ + usqInt firstBytecode; + sqInt primitiveIndex; + + /* primitiveIndexOfMethod:header: */ + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (theMethod + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + return (primitiveIndex != PrimNumberUnwindMarker) + && (primitiveIndex != PrimNumberNoContextSwitchMarker); +} + + +/* Above ObjectMemory, arg must lie in range 0-255! */ + + /* StackInterpreter>>#characterForAscii: */ +sqInt +characterForAscii(sqInt ascii) +{ + return ((((usqInt)(ascii) << (numTagBits())))) + (characterTag()); +} + + +/* Ensure that all accessible objects in the heap are okay. */ +/* useful for VM debugging */ + + /* StackInterpreter>>#checkAllAccessibleObjectsOkay */ +sqInt +checkAllAccessibleObjectsOkay(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt ok; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + ok = 1; + + /* begin allObjectsDoSafely: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + if (((longAt((void *)(obj))) & (classIndexMask())) > (lastClassIndexPun())) { + ok = ok && (checkOkayFields(obj)); + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + return ok; +} + + +/* Check for a hit of the longRunningPrimitive probe and if so attempt to + signal the + longRunningPrimitiveCheckSemaphore. Answer if a process switch occurred as + a result. */ + + /* StackInterpreter>>#checkDeliveryOfLongRunningPrimitiveSignal */ +#if LRPCheck +static NeverInline sqInt +checkDeliveryOfLongRunningPrimitiveSignal(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((GIV(longRunningPrimitiveStopUsecs) > GIV(longRunningPrimitiveStartUsecs)) + && ((GIV(longRunningPrimitiveCheckSemaphore)) + && (GIV(longRunningPrimitiveSignalUndelivered)))) { + GIV(longRunningPrimitiveSignalUndelivered) = 0; + GIV(longRunningPrimitiveGCUsecs) = ((GIV(gcStartUsecs) < GIV(longRunningPrimitiveStopUsecs)) + && (GIV(statGCEndUsecs) > GIV(longRunningPrimitiveStartUsecs)) + ? GIV(statGCEndUsecs) - GIV(gcStartUsecs) + : 0); + + /* Signal the LRP check semaphore if it is present */ + return synchronousSignal(GIV(longRunningPrimitiveCheckSemaphore)); + } + + /* a hit + but not yet delivered */ + return 0; +} +#endif /* LRPCheck */ + + +/* Note: May be called by translated primitive code. */ + + /* StackInterpreter>>#checkedIntegerValueOf: */ +sqInt +checkedIntegerValueOf(sqInt intOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((((intOop) & 7) == 1)) { + return (intOop >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } +} + + +/* Support for embedded images. Check that the first few bytes of a potential + header and answer if it + looks like something the VM can load, + The method checks the first three fields of the header (magic, header size + & data size) & the total size. + The magic number should be correct. + The header size should be correct. + The size of the data should be at least as long as the headerSize plus the + data size in the header */ +/* Need at least headerSize bytes; no point going further if not... */ + + /* StackInterpreter>>#checkImageHeaderFromBytes:AndSize: */ +sqInt +checkImageHeaderFromBytesAndSize(char *bytes, sqInt totalSize) +{ + sqInt dataSize; + sqInt headerSize; + int version; + + if (totalSize < (BytesPerWord * 16)) { + return 0; + } + version = long32At(bytes); + headerSize = long32At(bytes + 4); + dataSize = longAt(bytes + 8); + if (!((((version | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */)) { + if (!(((((SQ_SWAP_4_BYTES(version)) | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */)) { + return 0; + } + headerSize = SQ_SWAP_8_BYTES(headerSize); + dataSize = SQ_SWAP_8_BYTES(dataSize); + } + return (headerSize == (BytesPerWord * 16)) + && (totalSize >= (headerSize + dataSize)); +} + + +/* Read and verify the image file version number and answer it. + Assign through rawVersionPtr the unswapped version number. The caller can + then infer if the given image file needs to be byte-swapped by seeing if + the returned value + equals tha assigned through rawVersionPtr. + 0 is answered if no valid version could be found. */ + + /* StackInterpreter>>#checkImageVersionFrom:startingAt:assignRawVersion: */ +static NoDbgRegParms sqInt +checkImageVersionFromstartingAtassignRawVersion(sqImageFile f, squeakFileOffsetType imageOffset, sqInt *rawVersionPtr) +{ + sqInt version; + int w; + + rawVersionPtr[0] = 0; + + /* check the version number */ + sqImageFileSeek(f, imageOffset); + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + version = w; + rawVersionPtr[0] = version; + if ((((version | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */) { + return version; + } + + /* try with bytes reversed */ + sqImageFileSeek(f, imageOffset); + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + version = SQ_SWAP_4_BYTES(w); + if (!(/* isLikelyImageVersion: */ + (((rawVersionPtr[0]) / 100) == 65) + || (((rawVersionPtr[0]) / 1000) == 68))) { + rawVersionPtr[0] = version; + } + if ((((version | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */) { + return version; + } + + /* Note: The following is only meaningful if not reading an embedded image */ + if (!imageOffset) { + sqImageFileSeek(f, 0x200); + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + version = w; + if (!(/* isLikelyImageVersion: */ + (((rawVersionPtr[0]) / 100) == 65) + || (((rawVersionPtr[0]) / 1000) == 68))) { + rawVersionPtr[0] = version; + } + if ((((version | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */) { + return version; + } + + /* try skipping the first 512 bytes with bytes reversed */ + sqImageFileSeek(f, 0x200); + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + version = SQ_SWAP_4_BYTES(w); + if (!(/* isLikelyImageVersion: */ + (((rawVersionPtr[0]) / 100) == 65) + || (((rawVersionPtr[0]) / 1000) == 68))) { + rawVersionPtr[0] = version; + } + if ((((version | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */) { + return version; + } + } + + /* try skipping the first 512 bytes (prepended by certain Mac file transfer utilities) */ + if (!(/* isLikelyImageVersion: */ + (((rawVersionPtr[0]) / 100) == 65) + || (((rawVersionPtr[0]) / 1000) == 68))) { + rawVersionPtr[0] = version; + } + return 0; +} + + +/* Perform an integrity/leak check using the heapMap. Assume + clearLeakMapAndMapAccessibleObjects has set a bit at each + object's header. Check that all oops in the interpreter's state + points to a header. Answer 0 if all checks pass. */ + + /* StackInterpreter>>#checkInterpreterIntegrity */ +static sqInt +checkInterpreterIntegrity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt flags; + + flags = 0; + if (!(checkOopIntegritynamed(GIV(specialObjectsOop), "specialObjectsOop"))) { + flags = 1; + } + + /* No longer check messageSelector; it is ephemeral, not living beyond message lookup. + (objectMemory isNonImmediate: messageSelector) ifTrue: + [(objectMemory checkOopIntegrity: messageSelector named: 'messageSelector') ifFalse: + [flags := flags + N]]. */ + if (!(checkOopIntegritynamed(GIV(newMethod), "newMethod"))) { + flags += 2; + } + + /* No longer check lkupClass; it is ephemeral, not living beyond message lookup. + (objectMemory checkOopIntegrity: lkupClass named: 'lkupClass') ifFalse: + [flags := flags + N]. */ + if (GIV(profileProcess)) { + if (!(checkOopIntegritynamed(GIV(profileProcess), "profileProcess"))) { + flags += 4; + } + } + if (GIV(profileMethod)) { + if (!(checkOopIntegritynamed(GIV(profileMethod), "profileMethod"))) { + flags += 8; + } + } + if (GIV(profileSemaphore)) { + if (!(checkOopIntegritynamed(GIV(profileSemaphore), "profileSemaphore"))) { + flags += 16; + } + } + if (GIV(tempOop)) { + if (!(checkOopIntegritynamed(GIV(tempOop), "tempOop"))) { + flags += 32; + } + } + if (GIV(tempOop2)) { + if (!(checkOopIntegritynamed(GIV(tempOop2), "tempOop2"))) { + flags += 64; + } + } + if (!(checkLogIntegrity())) { + flags += 128; + } + return flags; +} + + +/* Another version of isWidowedContext: for debugging. + This will not bereave a widowed context. */ + + /* StackInterpreter>>#checkIsStillMarriedContext:currentFP: */ +static NoDbgRegParms sqInt +checkIsStillMarriedContextcurrentFP(sqInt aContext, char *currentFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *limitFP; + char *maybeFP; + sqInt maybeFrameCtxt; + sqInt referent; + sqInt senderOop; + StackPage *thePage; + + if (!((/* isContext: */ + ((!(aContext & (tagMask())))) + && (((longAt((void *)(aContext))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)))) { + return 0; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + maybeFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(maybeFP), GIV(pages)); + limitFP = ((thePage == GIV(stackPage)) + && (currentFP) + ? currentFP + : (thePage->headFP)); + if (!((maybeFP >= limitFP) + && (((!((((sqInt)(((char *)(longAt(maybeFP + FoxSavedFP)))))) & (tagMask())))) + && ((((/* begin withSmallIntegerTags: */ + assert(((oopForPointer(((char *)(longAt(maybeFP + FoxSavedFP))))) & (BytesPerWord - 1)) == 0), + (oopForPointer(((char *)(longAt(maybeFP + FoxSavedFP))))) + (smallIntegerTag()))) == (longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) + && (/* frameHasContext: */ + ((((usqInt)(longAt(maybeFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(maybeFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((maybeFP + FoxIFrameFlags) + 2)) != 0)))))) { + return 0; + } + maybeFrameCtxt = longAt(maybeFP + FoxThisContext); + + /* On Spur we need to follow the context to check for a match, but since the VM is + only speculating about maybeFrame being a frame, and only speculating about + maybeContext being a context, we need to be sure before we can safely follow. */ + if ((!(isFree(thePage))) + && ((isFrameonPage(maybeFP, thePage)) + && ((!((longAt((void *)(maybeFrameCtxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(maybeFrameCtxt)); + referent = longAt((void *)((maybeFrameCtxt + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + maybeFrameCtxt = referent; + } + return maybeFrameCtxt == aContext; +} + + +/* Check the log for leaks. The trace log is a circular buffer of pairs of + entries. If there is an entry at traceLogIndex - 3 \\ TraceBufferSize it + has entries. If + there is something at traceLogIndex it has wrapped. */ + + /* StackInterpreter>>#checkLogIntegrity */ +static sqInt +checkLogIntegrity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt limit; + sqInt ok; + sqInt oop; + sqInt remainder; + + limit = /* safe:mod: */ + (((remainder = (GIV(traceLogIndex) - 3) % TraceBufferSize)) < 0 + ? remainder + TraceBufferSize + : remainder); + if (!(GIV(traceLog)[limit])) { + return 1; + } + if (GIV(traceLog)[GIV(traceLogIndex)]) { + limit = TraceBufferSize - 3; + } + ok = 1; + for (i = 0; i <= limit; i += 3) { + oop = GIV(traceLog)[i]; + if (!(((oop & (tagMask())) != 0))) { + if (!(checkOopIntegritynamedindex(oop, "traceLog", i))) { + ok = 0; + } + } + oop = GIV(traceLog)[i + 1]; + if (!(((oop & (tagMask())) != 0))) { + if (!(checkOopIntegritynamedindex(oop, "traceLog", i + 1))) { + ok = 0; + } + } + } + return ok; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#checkOkayInterpreterObjects: */ +sqInt +checkOkayInterpreterObjects(sqInt writeBack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int i; + sqInt ok; + sqInt oop; + sqIntptr_t oopOrZero; + + ok = 1; + ok = ok && (checkOkayFields(GIV(nilObj))); + ok = ok && (checkOkayFields(GIV(falseObj))); + ok = ok && (checkOkayFields(GIV(trueObj))); + ok = ok && (checkOkayFields(GIV(specialObjectsOop))); + ok = ok && (checkOkayFields(GIV(messageSelector))); + ok = ok && (checkOkayFields(GIV(newMethod))); + ok = ok && (checkOkayFields(GIV(lkupClass))); + for (i = 0; i < MethodCacheEntries; i += MethodCacheEntrySize) { + oopOrZero = GIV(methodCache)[i + MethodCacheSelector]; + if (oopOrZero) { + ok = ok && (checkOkayFields(GIV(methodCache)[i + MethodCacheSelector])); + ok = ok && (checkOkayFields(GIV(methodCache)[i + MethodCacheMethod])); + } + } + for (i = 1; i <= GIV(remapBufferCount); i += 1) { + oop = GIV(remapBuffer)[i]; + if (!(((oop & (tagMask())) != 0))) { + ok = ok && (checkOkayFields(oop)); + } + } + ok = ok && (checkOkayStackZone(writeBack)); + return ok; +} + + /* StackInterpreter>>#checkOkayStackPage: */ +static NoDbgRegParms sqInt +checkOkayStackPage(StackPage *thePage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + char *frameRcvrOffset; + sqInt ok; + sqInt oop; + char *theFP; + char *theSP; + + callerFP = ((char *) 0); + theSP = (thePage->headSP); + theFP = (thePage->headFP); + ok = 1; + + /* Skip the instruction pointer on top of stack of inactive pages. */ + if (!(thePage == GIV(stackPage))) { + theSP += BytesPerWord; + } + while (1) { + frameRcvrOffset = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + while (theSP <= frameRcvrOffset) { + oop = longAt(theSP); + if (!((((oop & (tagMask())) != 0)) + || (checkOkayFields(oop)))) { + ok = 0; + } + theSP += BytesPerWord; + } + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + if (!(checkOkayFields(longAt(theFP + FoxThisContext)))) { + ok = 0; + } + } + if (!(checkOkayFields(/* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod))))) { + ok = 0; + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + theFP = callerFP; + } + + /* caller ip is frameCallerContext in a base frame */ + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if (!((((oop & (tagMask())) != 0)) + || (checkOkayFields(oop)))) { + ok = 0; + } + theSP += BytesPerWord; + } + return ok; +} + + +/* Check that all objects in the stack zone are okay */ + + /* StackInterpreter>>#checkOkayStackZone: */ +static NoDbgRegParms sqInt +checkOkayStackZone(sqInt writeBack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt ok; + StackPage *thePage; + + if (writeBack) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + ok = 1; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + ok = ok && (checkOkayStackPage(thePage)); + } + } + return ok; +} + + +/* Perform an integrity/leak check using the heapMap. Assume + clearLeakMapAndMapAccesibleObjects has set a bit at each + object's header. Scan all objects accessible from the stack + checking that every pointer points to a header. Answer if no + dangling pointers were detected. */ + + /* StackInterpreter>>#checkStackIntegrity */ +static sqInt +checkStackIntegrity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + CogMethod *cogMethod; + char *frameRcvrOffset; + sqInt i; + sqInt methodField; + sqInt ok; + usqInt oop; + char *theFP; + StackPage *thePage; + char *theSP; + + callerFP = ((char *) 0); + ok = 1; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + if (thePage == GIV(stackPage)) { + theSP = GIV(stackPointer); + theFP = GIV(framePointer); + } + else { + theSP = (thePage->headSP); + theFP = (thePage->headFP); + } + + /* Skip the instruction pointer on top of stack of inactive pages. */ + if (!(thePage == GIV(stackPage))) { + theSP += BytesPerWord; + } + while (1) { + frameRcvrOffset = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + while (theSP <= frameRcvrOffset) { + oop = longAt(theSP); + if (((!(oop & (tagMask())))) + && ((heapMapAtWord(pointerForOop(oop))) == 0)) { + printFrameThingandFrameat("object leak in frame temp", theFP, theSP); + ok = 0; + } + theSP += BytesPerWord; + } + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + oop = longAt(theFP + FoxThisContext); + if ((((oop & (tagMask())) != 0)) + || ((heapMapAtWord(pointerForOop(oop))) == 0)) { + printFrameThingandFrameat("object leak in frame ctxt", theFP, theFP + FoxThisContext); + ok = 0; + } + if (!(/* isContext: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + printFrameThingandFrameat("frame ctxt should be context", theFP, theFP + FoxThisContext); + ok = 0; + } + if (!((/* isContext: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (((((longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)))) { + printFrameThingandFrameat("frame ctxt should be married", theFP, theFP + FoxThisContext); + ok = 0; + } + if (!((/* isContext: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && ((frameOfMarriedContext(oop)) == theFP))) { + printFrameThingandFrameat("frame ctxt should be married to this frame ", theFP, theFP + FoxThisContext); + ok = 0; + } + } + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* begin mframeHomeMethod: */ + methodField = longAt(theFP + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + cogMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + goto l1; + } + cogMethod = ((CogMethod *) (methodField & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + if (!(heapMapAtWord(cogMethod))) { + printFrameThingandFrameat("object leak in mframe mthd", theFP, theFP + FoxMethod); + ok = 0; + } + } + else { + oop = longAt(theFP + FoxMethod); + if ((((oop & (tagMask())) != 0)) + || ((heapMapAtWord(pointerForOop(oop))) == 0)) { + printFrameThingandFrameat("object leak in iframe mthd", theFP, theFP + FoxMethod); + ok = 0; + } + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + theFP = callerFP; + } + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if (((!(oop & (tagMask())))) + && ((heapMapAtWord(pointerForOop(oop))) == 0)) { + printFrameThingandFrameat("object leak in frame arg", theFP, theSP); + ok = 0; + } + theSP += BytesPerWord; + } + } + } + return ok; +} + + +/* Version of stackPointerForMaybeMarriedContext: with no side-effects (does + not widow). + Used for assertion checking. Safe only in external primitives + (framePointer valid). + Answer the stackPointer of a Context. */ + + /* StackInterpreter>>#checkStackPointerForMaybeMarriedContext: */ +static NoDbgRegParms sqInt +checkStackPointerForMaybeMarriedContext(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + sqInt methodPointer; + sqInt sp; + sqInt spSqInt; + + if (checkIsStillMarriedContextcurrentFP(aContext, GIV(framePointer))) { + sp = checkStackPointerIndexForFrame(frameOfMarriedContext(aContext)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(aContext))); + return sp; + } + if (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + methodPointer = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* begin argumentCountOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin argumentCountOfMethodHeader: */ + return (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; + } + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + return 0; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + return (spSqInt >> 3); +} + + +/* Version of stackPointerIndexForFrame: that does not depend on writing back + head frame pointers. + Used for assertion checking. Safe only in external primitives + (framePointer valid). + Answer the 0-based index rel to the given frame. + (This is what stackPointer used to be before conversion to pointer) */ +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#checkStackPointerIndexForFrame: */ +static NoDbgRegParms sqInt +checkStackPointerIndexForFrame(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *aFrame; + char *prevFrame; + char *startFrame; + StackPage *thePage; + char *theSP; + + if (theFP == GIV(framePointer)) { + return /* stackPointerIndexForFrame:WithSP: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (((usqInt)(((theFP + FoxMFReceiver) - GIV(stackPointer)))) >> (shiftForWord())) + (((mframeCogMethod(theFP))->cmNumArgs)) + : (((usqInt)(((theFP + FoxIFReceiver) - GIV(stackPointer)))) >> (shiftForWord())) + (byteAt((theFP + FoxIFrameFlags) + 1))); + } + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + startFrame = (thePage == GIV(stackPage) + ? GIV(framePointer) + : (thePage->headFP)); + + /* begin findSPOrNilOf:on:startingFrom: */ + prevFrame = ((char *) 0); + if (startFrame == theFP) { + if (((thePage->headSP)) >= startFrame) { + /* If the SP is invalid return the pointer to the receiver field. */ + theSP = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + goto l1; + } + + /* Skip the instruction pointer on top of stack of inactive pages. */ + theSP = (thePage == GIV(stackPage) + ? (thePage->headSP) + : ((thePage->headSP)) + BytesPerWord); + goto l1; + } + aFrame = startFrame; + while (1) { + prevFrame = aFrame; + aFrame = ((char *)(longAt(aFrame + FoxSavedFP))); + if (!(aFrame != 0)) break; + if (theFP == aFrame) { + /* begin frameCallerSP: */ + assert(!(isBaseFrame(prevFrame))); + theSP = (prevFrame + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(prevFrame + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(prevFrame))->cmNumArgs) + : byteAt((prevFrame + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + goto l1; + } + } + theSP = null; + /* end findSPOrNilOf:on:startingFrom: */ +l1: + if (!theSP) { + return -1; + } + return /* stackPointerIndexForFrame:WithSP: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (((usqInt)(((theFP + FoxMFReceiver) - theSP))) >> (shiftForWord())) + (((mframeCogMethod(theFP))->cmNumArgs)) + : (((usqInt)(((theFP + FoxIFReceiver) - theSP))) >> (shiftForWord())) + (byteAt((theFP + FoxIFrameFlags) + 1))); +} + + +/* Check if aClass's name is className */ + + /* StackInterpreter>>#classNameOf:Is: */ +static NoDbgRegParms sqInt +classNameOfIs(sqInt aClass, char *className) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt i; + sqInt length; + sqInt name; + usqInt numSlots; + char *srcName; + + if (((/* begin numSlotsOf: */ + assert((classIndexOf(aClass)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(aClass + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aClass - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) <= GIV(classNameIndex)) { + return 0; + } + name = longAt((void *)((aClass + BaseHeaderSize) + ((((usqInt)(GIV(classNameIndex)) << (shiftForWord())))))); + if (!(/* isBytes: */ + ((!(name & (tagMask())))) + && (((byteAt((void *)(name + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + return 0; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(name + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + length = ((((/* begin numSlotsOf: */ + assert((classIndexOf(name)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(name + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(name - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + srcName = ((char *) (arrayValueOf(name))); + for (i = 0; i < length; i += 1) { + if (!((srcName[i]) == (className[i]))) { + return 0; + } + } + + /* Check if className really ends at this point */ + return (className[length]) == 0; +} + + /* StackInterpreter>>#clearTraceLog */ +void +clearTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + GIV(traceLogIndex) = 0; + for (i = 0; i < TraceBufferSize; i += 1) { + GIV(traceLog)[i] = 0; + } +} + + +/* For asserts. Check that theIP maps back correctly to the context's pc. + The CallPrimitive bytecode presents a complication. */ + + /* StackInterpreter>>#context:hasValidInversePCMappingOf:in: */ +static NoDbgRegParms sqInt +contexthasValidInversePCMappingOfin(sqInt aContext, sqInt theIP, char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt encodedip; + sqInt header; + sqInt methodHeader; + sqInt methodObj; + sqInt pc; + + pc = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + encodedip = contextInstructionPointerframe(theIP, theFP); + return (pc == encodedip) + || (((methodObj = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord()))))))), + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)), + (header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))))), + (methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader)))), + (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) + && ((((encodedip >> 3)) - ((pc >> 3))) == 3 /* sizeOfCallPrimitiveBytecode: */))); +} + + +/* for Cogit */ + + /* StackInterpreter>>#copiedValueCountOfClosure: */ +sqInt +copiedValueCountOfClosure(sqInt closureObj) +{ + usqInt numSlots; + + return (((((longAt((void *)((closureObj + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1) + ? (/* begin copiedValueCountOfVanillaClosure: */ + assert(isVanillaBlockClosure(closureObj)), + ((/* begin numSlotsOf: */ + assert((classIndexOf(closureObj)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(closureObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(closureObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - ClosureFirstCopiedValueIndex) + : (/* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(closureObj)))), + ((/* begin numSlotsOf: */ + assert((classIndexOf(closureObj)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(closureObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(closureObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex)); +} + + +/* This entry point needs to be implemented for the interpreter proxy. + Since BitBlt is now a plugin we need to look up BitBltPlugin:=copyBits + and call it. This entire mechanism should eventually go away and be + replaced with a dynamic lookup from BitBltPlugin itself but for backward + compatibility this stub is provided */ + + /* StackInterpreter>>#copyBits */ +sqInt +copyBits(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void *fn; + + fn = ioLoadFunctionFrom("copyBits", "BitBltPlugin"); + if (!fn) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return ((sqInt (*)(void))fn)(); +} + + +/* This entry point needs to be implemented for the interpreter proxy. + Since BitBlt is now a plugin we need to look up + BitBltPlugin:=copyBitsFrom:to:at: and call it. This entire mechanism + should eventually go away and be + replaced with a dynamic lookup from BitBltPlugin itself but for backward + compatibility this stub is provided + */ + + /* StackInterpreter>>#copyBitsFrom:to:at: */ +sqInt +copyBitsFromtoat(sqInt x0, sqInt x1, sqInt y) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void *fn; + + fn = ioLoadFunctionFrom("copyBitsFromtoat", "BitBltPlugin"); + if (!fn) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return ((sqInt (*)(sqInt, sqInt, sqInt))fn)(x0, x1, y); +} + + /* StackInterpreter>>#couldBeProcess: */ +static NoDbgRegParms NeverInline sqInt +couldBeProcess(sqInt oop) +{ + return (addressCouldBeObj(oop)) + && ((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + && ((!(((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (((lengthOf(oop)) > MyListIndex) + && (isContext(longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))))))))); +} + + +/* Bundle up the selector, arguments and lookupClass into a Message object. + In the process it pops the arguments off the stack, and pushes the message + object. + This can then be presented as the argument of e.g. #doesNotUnderstand: */ + + /* StackInterpreter>>#createActualMessageTo: */ +static NoDbgRegParms void +createActualMessageTo(sqInt lookupClass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt argumentArray; + sqInt i; + usqInt message; + usqInt newObj; + usqInt numBytes; + + + /* This is a useful break-point */ + assert((isImmediate(GIV(messageSelector))) + || (addressCouldBeObj(GIV(messageSelector)))); + + /* begin mnuBreakpoint:receiver: */ + mnuBreakpointreceiver(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), null); + assert((GIV(argumentCount) >= 0) + && ((knownClassAtIndex(ClassArrayCompactIndex)) != GIV(nilObj))); + assert((arrayFormat()) == (instSpecOfClass(knownClassAtIndex(ClassArrayCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(GIV(argumentCount) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((GIV(argumentCount) < 1 + ? 8 /* allocationUnit */ + : GIV(argumentCount) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + argumentArray = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) GIV(argumentCount))) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + GIV(freeStart) += numBytes; + argumentArray = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert(((MessageLookupClassIndex + 1) >= 0) + && ((knownClassAtIndex(ClassMessageCompactIndex)) != GIV(nilObj))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(knownClassAtIndex(ClassMessageCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert((MessageLookupClassIndex + 1) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (((MessageLookupClassIndex + 1) < 1 + ? 8 /* allocationUnit */ + : (MessageLookupClassIndex + 1) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + message = 0; + goto l2; + } + } + long64Atput((void *)(newObj),((((((usqLong) (MessageLookupClassIndex + 1))) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + ClassMessageCompactIndex); + GIV(freeStart) += numBytes; + message = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l2: + + /* Since the array is new can use unchecked stores. */ + for (i = ((GIV(argumentCount) - 1) * BytesPerOop); i >= 0; i += (-BytesPerOop)) { + longAtput((void *)((argumentArray + BaseHeaderSize) + i),popStack()); + } + + /* Since message is new can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(message)) + && (!(isForwarded(message)))); + assert(validStorePointerUncheckedArgs(MessageSelectorIndex, message, GIV(messageSelector))); + longAtput((void *)((message + BaseHeaderSize) + ((((usqInt)(MessageSelectorIndex) << (shiftForWord()))))),GIV(messageSelector)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(message)) + && (!(isForwarded(message)))); + assert(validStorePointerUncheckedArgs(MessageArgumentsIndex, message, argumentArray)); + longAtput((void *)((message + BaseHeaderSize) + ((((usqInt)(MessageArgumentsIndex) << (shiftForWord()))))),argumentArray); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(message)) + && (!(isForwarded(message)))); + assert(validStorePointerUncheckedArgs(MessageLookupClassIndex, message, lookupClass)); + longAtput((void *)((message + BaseHeaderSize) + ((((usqInt)(MessageLookupClassIndex) << (shiftForWord()))))),lookupClass); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),message); + GIV(argumentCount) = 1; +} + + +/* Return the default number of stack pages allocate at startup. + This V3 default suits Qwaq Forums (specifically general rendering). + The Spur default reflects tuning for GC performance ast Cadence. + It is probably a bit high for normal use but QF is profligate with + processes. The actual value can be set via vmParameterAt: and/or a + preference in the ini file. */ + + /* StackInterpreter>>#defaultNumStackPages */ +static sqInt +defaultNumStackPages(void) +{ + return 50; +} + + /* StackInterpreter>>#disableSendPrinting */ +#if SEND_PRINTING +void +disableSendPrinting(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(printSends) = 0; +} +#endif /* SEND_PRINTING */ + + /* StackInterpreter>>#divorceAllFrames */ +static sqInt +divorceAllFrames(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + StackPage *aPage; + sqInt i; + + if (GIV(stackPage)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l1; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer) + BytesPerWord); + /* end ensureFrameIsMarried:SP: */ +l1: + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + aPage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(aPage))) { + divorceFramesIn(aPage); + } + } + + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + return activeContext; +} + + /* StackInterpreter>>#divorceFramesIn: */ +static NoDbgRegParms void +divorceFramesIn(StackPage *aStackPage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt calleeContext; + char *calleeFP; + sqInt callerContextOrNil; + sqInt theContext; + char *theFP; + sqInt theIP; + StackPage *thePage; + char *theSP; + sqInt valuePointer; + + calleeFP = ((char *) 0); + GIV(statStackPageDivorce) += 1; + theFP = (aStackPage->headFP); + theSP = (aStackPage->headSP); + theIP = longAt(theSP); + + /* theSP points at hottest item on frame's stack */ + theSP += BytesPerWord; + calleeContext = null; + while (1) { + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + theContext = longAt(theFP + FoxThisContext); + goto l1; + } + theContext = marryFrameSP(theFP, theSP); + /* end ensureFrameIsMarried:SP: */ +l1: + updateStateOfSpouseContextForFrameWithSP(theFP, theSP); + valuePointer = contextInstructionPointerframe(theIP, theFP); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, theContext, valuePointer)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointer); + assert((frameReceiver(theFP)) == (followFieldofObject(ReceiverIndex, theContext))); + if (calleeContext) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SenderIndex, calleeContext, theContext)); + assert(isNonImmediate(calleeContext)); + if (oopisGreaterThanOrEqualTo(calleeContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(theContext & (tagMask())))) + && (oopisLessThan(theContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(calleeContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(calleeContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((calleeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),theContext); + } + calleeContext = theContext; + calleeFP = theFP; + theIP = longAt(theFP + FoxCallerSavedIP); + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!(theFP != 0)) break; + /* theSP points at stacked hottest item on frame's stack */ + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(calleeFP))); + theSP = (calleeFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(calleeFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(calleeFP))->cmNumArgs) + : byteAt((calleeFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + + /* begin frameCallerContext: */ + assert(isBaseFrame(calleeFP)); + thePage = stackPageAtpages(pageIndexFor(calleeFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + valuePointer = callerContextOrNil; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SenderIndex, theContext, valuePointer)); + assert(isNonImmediate(theContext)); + if (oopisGreaterThanOrEqualTo(theContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(theContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(theContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),valuePointer); + + /* The page is now free; mark it so. */ + (aStackPage->baseFP = 0); +} + + +/* Rounds negative results towards negative infinity, rather than zero. */ + + /* StackInterpreter>>#doPrimitiveDiv:by: */ +static NoDbgRegParms sqInt +doPrimitiveDivby(sqInt rcvr, sqInt arg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArg; + sqInt integerRcvr; + sqInt posArg; + sqInt posRcvr; + sqInt result; + + integerArg = 0; + integerRcvr = 0; + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + integerRcvr = (rcvr >> 3); + integerArg = (arg >> 3); + + /* begin success: */ + if (!integerArg) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return 1; + } + if (integerRcvr > 0) { + if (integerArg > 0) { + result = integerRcvr / integerArg; + } + else { + /* round negative result toward negative infinity */ + posArg = 0 - integerArg; + result = 0 - ((integerRcvr + (posArg - 1)) / posArg); + } + } + else { + posRcvr = 0 - integerRcvr; + if (integerArg > 0) { + /* round negative result toward negative infinity */ + result = 0 - ((posRcvr + (integerArg - 1)) / integerArg); + } + else { + posArg = 0 - integerArg; + result = posRcvr / posArg; + } + } + + /* begin success: */ + if (!((((((usqInt)(result)) >> 60) + 1) & 15) <= 1)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + return result; +} + + /* StackInterpreter>>#doPrimitiveMod:by: */ +static NoDbgRegParms sqInt +doPrimitiveModby(sqInt rcvr, sqInt arg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArg; + sqInt integerRcvr; + sqInt integerResult; + + integerArg = 0; + integerRcvr = 0; + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + integerRcvr = (rcvr >> 3); + integerArg = (arg >> 3); + + /* begin success: */ + if (!integerArg) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return 1; + } + integerResult = integerRcvr % integerArg; + + /* ensure that the result has the same sign as the integerArg */ + if (integerArg < 0) { + if (integerResult > 0) { + integerResult += integerArg; + } + } + else { + if (integerResult < 0) { + integerResult += integerArg; + } + } + + /* begin success: */ + if (!((((((usqInt)(integerResult)) >> 60) + 1) & 15) <= 1)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + return integerResult; +} + + +/* Signal the external semaphore with the given index. Answer if a context + switch occurs as a result. Do not bounds check. This has been done in the + caller. */ + + /* StackInterpreter>>#doSignalSemaphoreWithIndex: */ +sqInt +doSignalSemaphoreWithIndex(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt sema; + sqInt xArray; + + xArray = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ExternalObjectsArray) << (shiftForWord())))))); + assert(isArray(xArray)); + + /* Note: semaphore indices are 1-based */ + sema = longAt((void *)((xArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord())))))); + assert(!(isOopForwarded(sema))); + return (/* isSemaphoreOop: */ + ((!(sema & (tagMask())))) + && (((longAt((void *)(sema))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord())))))))))) + && (synchronousSignal(sema)); +} + + +/* Presumably this exists to squash C compiler warnings about unused + variables... + */ + + /* StackInterpreter>>#dummyReferToProxy */ +struct VirtualMachine * * +dummyReferToProxy(void) +{ + return (&interpreterProxy); +} + + +/* The prim trace log is a circular buffer of entries. If there is + an entry at primTraceLogIndex \\ PrimTraceLogSize it has entries. + If there is something at primTraceLogIndex it has wrapped. */ + + /* StackInterpreter>>#dumpPrimTraceLog */ +void +dumpPrimTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt hasParameter; + sqInt i; + sqInt remainder; + + if (!(GIV(primTraceLog)[/* safe:mod: */ + (((remainder = (GIV(primTraceLogIndex) - 1) % PrimTraceLogSize)) < 0 + ? remainder + PrimTraceLogSize + : remainder)])) { + return; + } + hasParameter = 0; + if (GIV(primTraceLog)[GIV(primTraceLogIndex)]) { + for (i = GIV(primTraceLogIndex); i < PrimTraceLogSize; i += 1) { + hasParameter = printPrimLogEntryAthasParameter(i, hasParameter); + } + } + for (i = 0; i < GIV(primTraceLogIndex); i += 1) { + hasParameter = printPrimLogEntryAthasParameter(i, hasParameter); + } +} + + +/* Print the prim trace log on a specific output stream. */ +/* essential for writing crash.dmp; use export: not api, so it will be + accessible on win32 and won't be written to cointerp.h + */ + + /* StackInterpreter>>#dumpPrimTraceLogOn: */ +void +dumpPrimTraceLogOn(FILE *aStdioStream) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + FILE *savedTranscript; + + /* begin withRedirectedOutputTo:do: */ + savedTranscript = GIV(transcript); + GIV(transcript) = (aStdioStream + ? aStdioStream + : stdout); + dumpPrimTraceLog(); + GIV(transcript) = savedTranscript; +} + + +/* For marking the end of a leak check print message */ + + /* StackInterpreter>>#eekcr */ +NeverInline void +eekcr(void) +{ + cr(); +} + + /* StackInterpreter>>#enableSendPrinting */ +#if SEND_PRINTING +void +enableSendPrinting(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(printSends) = 1; +} +#endif /* SEND_PRINTING */ + + +/* Answer the caller context for a frame. If the frame has a caller + frame that doesn't have a context, then marry the caller frame. */ + + /* StackInterpreter>>#ensureCallerContext: */ +static NoDbgRegParms sqInt +ensureCallerContext(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + char *callerFP; + StackPage *thePage; + + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!callerFP) { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + return callerContextOrNil; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP))); + return longAt(callerFP + FoxThisContext); + } + return marryFrameSP( + callerFP, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFP))), + (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); +} + + +/* Ensure the image data has been updated to suit the current VM. */ + + /* StackInterpreter>>#ensureImageFormatIsUpToDate: */ +static NoDbgRegParms void +ensureImageFormatIsUpToDate(sqInt swapBytes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt addr; + sqInt address; + sqInt classIndex; + sqInt fmt; + sqInt followingWord; + usqInt followingWordAddress; + sqInt methodHeader; + usqInt numSlots; + sqInt obj; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + sqInt stopAddr; + int swapFloatWords; + sqInt temp; + int tempInt; + sqInt wordAddr; + + if (swapBytes) { + /* begin reverseBytesInImage */ + /* begin reverseBytesInMemory */ + /* begin reverseBytesFrom:to: */ + addr = GIV(oldSpaceStart); + while (oopisLessThan(addr, GIV(endOfMemory))) { + longAtput((void *)(addr),SQ_SWAP_8_BYTES((longAt((void *)(addr))))); + addr += BytesPerWord; + } + + /* Second, return the bytes of bytes-type objects to their + orginal order, and perform any other format conversions. */ + + /* begin updateObjectsPostByteSwap */ + swapFloatWords = VMBIGENDIAN != ((!(imageHeaderFlags & 2))); + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + fmt = (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask()); + if (fmt >= (firstByteFormat())) { + wordAddr = obj + BaseHeaderSize; + if (fmt >= (firstCompiledMethodFormat())) { + methodHeader = longAt((void *)(obj + BaseHeaderSize)); + wordAddr += (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop; + } + + /* compiled method; start after methodHeader and literals */ + stopAddr = obj + (((((/* begin numSlotsOf: */ + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) + BaseHeaderSize); + + /* begin reverseBytesFrom:to: */ + addr = wordAddr; + while (oopisLessThan(addr, stopAddr)) { + longAtput((void *)(addr),SQ_SWAP_8_BYTES((longAt((void *)(addr))))); + addr += BytesPerWord; + } + } + + /* oop contains bytes */ + if (fmt == (firstLongFormat())) { + if (swapFloatWords + && (((longAt((void *)(obj))) & (classIndexMask())) == ClassFloatCompactIndex)) { + temp = longAt((void *)(obj + BaseHeaderSize)); + longAtput((void *)(obj + BaseHeaderSize),longAt((void *)((obj + BaseHeaderSize) + 4))); + longAtput((void *)((obj + BaseHeaderSize) + 4),temp); + } + else { + } + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + if (swapFloatWords) { + /* setImageFloatsBigEndian: */ + imageHeaderFlags = ((imageHeaderFlags | 2) - 2); + } + } + else { + /* begin convertFloatsToPlatformOrder */ + if (VMBIGENDIAN == ((!(imageHeaderFlags & 2)))) { + goto l3; + } + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassFloatCompactIndex) { + tempInt = long32At((void *)(objSqInt + BaseHeaderSize)); + long32Atput((void *)(objSqInt + BaseHeaderSize),long32At((void *)((objSqInt + BaseHeaderSize) + 4))); + long32Atput((void *)((objSqInt + BaseHeaderSize) + 4),tempInt); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2: + assert(oopisGreaterThan(objSqInt, prevObj)); + } + + /* setImageFloatsBigEndian: */ + imageHeaderFlags = ((imageHeaderFlags | 2) - 2); + /* end convertFloatsToPlatformOrder */ +l3:; + } +} + + /* StackInterpreter>>#establishFrameForContextToReturnTo: */ +static NoDbgRegParms char * +establishFrameForContextToReturnTo(sqInt contextToReturnTo) +{ + sqInt senderOop; + StackPage *thePage; + + if (!(/* isContext: */ + ((!(contextToReturnTo & (tagMask())))) + && (((longAt((void *)(contextToReturnTo))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + return 0; + } + if (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (isWidowedContext(contextToReturnTo)) { + /* error: home's sender is dead; cannot return */ + return 0; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + return ((char *)(senderOop - (smallIntegerTag()))); + } + if (!(((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1))) { + return 0; + } + thePage = makeBaseFrameFor(contextToReturnTo); + markStackPageMostRecentlyUsed(thePage); + return (thePage->baseFP); +} + + +/* Divorce a single frame and its context. If it is not the top frame of a + stack this means splitting its stack. */ +/* stackPage needs to have current head pointers to avoid confusion. */ + + /* StackInterpreter>>#externalDivorceFrame:andContext: */ +static NoDbgRegParms void +externalDivorceFrameandContext(char *theFP, sqInt ctxt) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + sqInt callerCtx; + char *callerFP; + char *callerFP1; + sqInt callerIP; + char *callerSP; + char *frameAbove; + StackPage *newPage; + int onCurrent; + sqInt theIP; + StackPage *thePage; + StackPage *thePage1; + char *theSP; + sqInt valuePointer; + + assert((!GIV(stackPage)) + || (GIV(stackPage) == (GIV(mostRecentlyUsedPage)))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (!((onCurrent = thePage == GIV(stackPage)))) { + markStackPageNextMostRecentlyUsed(thePage); + } + theSP = findSPOfon(theFP, thePage); + updateStateOfSpouseContextForFrameWithSP(theFP, theSP); + + /* begin ensureCallerContext: */ + callerFP1 = ((char *)(longAt(theFP + FoxSavedFP))); + if (!callerFP1) { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerContextOrNil = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + callerCtx = callerContextOrNil; + goto l2; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP1 + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP1 + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP1 + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP1))); + callerCtx = longAt(callerFP1 + FoxThisContext); + goto l2; + } + callerCtx = marryFrameSP( + callerFP1, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFP))), + (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + /* end ensureCallerContext: */ +l2: + if ((frameAbove = findFrameAboveinPage(theFP, thePage))) { + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l1; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l1: + theIP = longAt(frameAbove + FoxCallerSavedIP); + frameAbove = moveFramesInthroughtoPage(thePage, frameAbove, newPage); + if (onCurrent) { + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (newPage->headSP); + GIV(framePointer) = (newPage->headFP); + } + else { + markStackPageMostRecentlyUsed(newPage); + } + assert((frameCallerContext(frameAbove)) == ctxt); + } + else { + theIP = longAt((thePage->headSP)); + } + + /* If we're divorcing the top frame we can simply peel it off. + othewise move all frames above to a new stack and then peel the frame off. */ + valuePointer = contextInstructionPointerframe(theIP, theFP); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, ctxt, valuePointer)); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointer); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SenderIndex, ctxt, callerCtx)); + assert(isNonImmediate(ctxt)); + if (oopisGreaterThanOrEqualTo(ctxt, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(callerCtx & (tagMask())))) + && (oopisLessThan(callerCtx, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(ctxt + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(ctxt); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),callerCtx); + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (callerFP) { + callerIP = longAt(theFP + FoxCallerSavedIP); + callerSP = ((/* begin frameCallerSP: */ + assert(!(isBaseFrame(theFP))), +(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)) - BytesPerWord; + longAtput(callerSP,callerIP); + + /* begin setHeadFP:andSP:inPage: */ + assert(callerSP < callerFP); + assert((callerSP < ((thePage->baseAddress))) + && (callerSP > (((thePage->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((callerFP < ((thePage->baseAddress))) + && (callerFP > (((thePage->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (thePage->headFP = callerFP); + (thePage->headSP = callerSP); + } + else { + freeStackPage(thePage); + } +} + + +/* Ensure aFramePtr is a base frame. Then we can assign its sender. + Answer the possibly moved location of the frame. */ + + /* StackInterpreter>>#externalEnsureIsBaseFrame: */ +static NoDbgRegParms char * +externalEnsureIsBaseFrame(char *aFramePtr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int onCurrent; + char *theFP; + StackPage *thePage; + + if (!(longAt(aFramePtr + FoxSavedFP))) { + return aFramePtr; + } + theFP = aFramePtr; + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + onCurrent = thePage == GIV(stackPage); + + /* Storing the frame's sender with its caller's context + has the side effect of making theFP a base frame. */ + theFP = storeSenderOfFramewithValue(theFP, ensureCallerContext(theFP)); + if (onCurrent) { + assert(GIV(stackPage) != thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (GIV(stackPage)->headSP); + GIV(framePointer) = (GIV(stackPage)->headFP); + } + else { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + assert(pageListIsWellFormed()); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + return theFP; +} + + /* StackInterpreter>>#externalInstVar:ofContext:put: */ +static NoDbgRegParms sqInt +externalInstVarofContextput(sqInt index, sqInt maybeMarriedContext, sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int onCurrentPage; + sqInt senderOop; + char *theFP; + StackPage *thePage; + + assert(isContext(maybeMarriedContext)); + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* Assign the field of a married context. */ + assert(!((isObjImmutable(maybeMarriedContext)))); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(maybeMarriedContext))))) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, maybeMarriedContext, anOop)); + assert(isNonImmediate(maybeMarriedContext)); + if (oopisGreaterThanOrEqualTo(maybeMarriedContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anOop & (tagMask())))) + && (oopisLessThan(anOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(maybeMarriedContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(maybeMarriedContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),anOop); + if (index == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(maybeMarriedContext); + } + return null; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + onCurrentPage = thePage == GIV(stackPage); + if (index) { + externalDivorceFrameandContext(theFP, maybeMarriedContext); + if (ismethodAssignmentToContextWithMachineCodePC(index, maybeMarriedContext)) { + ensureContextHasBytecodePC(maybeMarriedContext); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, maybeMarriedContext, anOop)); + assert(isNonImmediate(maybeMarriedContext)); + if (oopisGreaterThanOrEqualTo(maybeMarriedContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anOop & (tagMask())))) + && (oopisLessThan(anOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(maybeMarriedContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(maybeMarriedContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),anOop); + if (index == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(maybeMarriedContext); + } + } + else { + storeSenderOfFramewithValue(theFP, anOop); + } + if (onCurrentPage) { + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (GIV(stackPage)->headSP); + GIV(framePointer) = (GIV(stackPage)->headFP); + } + else { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(pageListIsWellFormed()); + assert(validStackPageBaseFrames()); + return 0; +} + + +/* Invoke a quick primitive. + Called under the assumption that primFunctionPtr has been preloaded */ + + /* StackInterpreter>>#externalQuickPrimitiveResponse */ +static sqInt +externalQuickPrimitiveResponse(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt localPrimIndex; + sqInt oop; + + assert(isPrimitiveFunctionPointerAnIndex()); + localPrimIndex = ((sqInt) primitiveFunctionPointer); + assert((localPrimIndex > 0xFF) + && (localPrimIndex < 520)); + + /* Quick return inst vars */ + if (localPrimIndex >= 264) { + oop = longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)((localPrimIndex - 264)) << (shiftForWord())))))); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); + return 1; + } + + /* Quick return constants */ + if (localPrimIndex == 0x100) { + return 1; + } + if (localPrimIndex == 0x101) { + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),GIV(trueObj)); + return 1; + } + if (localPrimIndex == 258) { + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),GIV(falseObj)); + return 1; + } + if (localPrimIndex == 259) { + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),GIV(nilObj)); + return 1; + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),(((usqInt)(localPrimIndex - 261) << 3) | 1)); + return 1; +} + + +/* not inlined for breakpoint value... */ + + /* StackInterpreter>>#failUnbalancedPrimitive */ +static void +failUnbalancedPrimitive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; +} + + +/* Fetch the instance variable at the given index of the given object. Return + the address of first indexable field of resulting array object, or fail if + the instance variable does not contain an indexable bytes or words object. + */ +/* Note: May be called by translated primitive code. */ + + /* StackInterpreter>>#fetchArray:ofObject: */ +void * +fetchArrayofObject(sqInt fieldIndex, sqInt objectPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayOop; + + arrayOop = longAt((void *)((objectPointer + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + + /* begin arrayValueOf: */ + if (/* isWordsOrBytes: */ + ((!(arrayOop & (tagMask())))) + && (isWordsOrBytesNonImm(arrayOop))) { + return ((void *) (pointerForOop(arrayOop + BaseHeaderSize))); + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; +} + + +/* Fetch the instance variable at the given index of the given object. Return + the C double precision floating point value of that instance variable, or + fail if it is not a Float. + */ +/* Note: May be called by translated primitive code. */ + + /* StackInterpreter>>#fetchFloat:ofObject: */ +double +fetchFloatofObject(sqInt fieldIndex, sqInt objectPointer) +{ + sqInt floatOop; + + floatOop = longAt((void *)((objectPointer + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + return floatValueOf(floatOop); +} + + +/* Fetch a slot from a married context. Compute the value + of the relevant inst var from the spouse frame's state. + + This method assumes frame pointers have been written back. */ + + /* StackInterpreter>>#fetchPointer:ofMarriedContext: */ +static NoDbgRegParms sqInt +fetchPointerofMarriedContext(sqInt offset, sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + char *callerFP; + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + sqInt senderOop; + char *spouseFP; + StackPage *thePage; + + assert(isContext(aContext)); + assert((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer))); + assert(checkIsStillMarriedContextcurrentFP(aContext, GIV(framePointer))); + + /* method, closureOrNil & receiver need no special handling; only + sender, pc & stackp have to be computed for married contexts. */ + if (offset <= ReceiverIndex) { + if (!(offset <= StackPointerIndex)) { + return longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(offset) << (shiftForWord())))))); + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!offset) { + /* begin ensureCallerContext: */ + callerFP = ((char *)(longAt(spouseFP + FoxSavedFP))); + if (!callerFP) { + /* begin frameCallerContext: */ + assert(isBaseFrame(spouseFP)); + thePage = stackPageAtpages(pageIndexFor(spouseFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + return callerContextOrNil; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP))); + return longAt(callerFP + FoxThisContext); + } + return marryFrameSP( + callerFP, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(spouseFP))), + (spouseFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(spouseFP))->cmNumArgs) + : byteAt((spouseFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + } + if (offset == StackPointerIndex) { + return (((usqInt)(stackPointerIndexForFrame(spouseFP)) << 3) | 1); + } + if (offset == InstructionPointerIndex) { + return instructionPointerForFramecurrentFPcurrentIP(spouseFP, GIV(framePointer), GIV(instructionPointer)); + } + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + return ((((offset - ReceiverIndex) >= 1) && ((offset - ReceiverIndex) <= (stackPointerIndexForFrame(spouseFP)))) + ? /* temporary:in: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? ((offset - (ReceiverIndex + 1)) < ((frameNumArgs = ((mframeCogMethod(spouseFP))->cmNumArgs))) + ? longAt((spouseFP + FoxCallerSavedIP) + ((frameNumArgs - (offset - (ReceiverIndex + 1))) * BytesPerWord)) + : longAt(((spouseFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs - (offset - (ReceiverIndex + 1))) * BytesPerWord))) + : /* itemporary:in: */ + ((offset - (ReceiverIndex + 1)) < ((frameNumArgsUsqInt = byteAt((spouseFP + FoxIFrameFlags) + 1))) + ? longAt((spouseFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt - (offset - (ReceiverIndex + 1))) * BytesPerWord)) + : longAt(((spouseFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt - (offset - (ReceiverIndex + 1))) * BytesPerWord)))) + : GIV(nilObj)); +} + + +/* Return the stackPointer of a Context or BlockContext. + Does not deal with married contexts. Use only for debug + printing or object tracing functions. To obtain an accurate + stack pointer use stackPointerForMaybeMarriedContext: */ + + /* StackInterpreter>>#fetchStackPointerOf: */ +static NoDbgRegParms sqInt +fetchStackPointerOf(sqInt aContext) +{ + sqInt sp; + + sp = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + return 0; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(aContext))); + return (sp >> 3); +} + + +/* Answer if file times, including those from the FilePlugin and + FileAttributesPlugin, should be answered in UTC seconds from the epoch + rather than local seconds. */ + + /* StackInterpreter>>#fileTimesInUTC */ +int +fileTimesInUTC(void) +{ + return ((imageHeaderFlags & 0x200) != 0); +} + + /* StackInterpreter>>#findClassContainingMethod:startingAt: */ +static NoDbgRegParms sqInt +findClassContainingMethodstartingAt(sqInt meth, sqInt classObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classDict; + usqInt classDictSize; + sqInt currClass; + sqInt i; + sqInt methodArray; + usqInt numSlots; + sqInt objOop; + sqInt referent; + sqInt referentSqInt; + + if (/* isOopForwarded: */ + ((!(classObj & (tagMask())))) + && ((!((longAt((void *)(classObj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(classObj)); + currClass = longAt((void *)((classObj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(currClass & (tagMask())))) + && ((!((longAt((void *)(currClass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + currClass = longAt((void *)((currClass + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + else { + currClass = classObj; + } + do { + assert(!(isForwarded(currClass))); + if (!(addressCouldBeClassObj(currClass))) { + return GIV(nilObj); + } + + /* begin noFixupFollowField:ofObject: */ + classDict = longAt((void *)((currClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(classDict & (tagMask())))) + && ((!((longAt((void *)(classDict))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(classDict)); + referent = longAt((void *)((classDict + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + classDict = referent; + } + assert(!(isForwarded(classDict))); + + /* begin numSlotsOf: */ + assert((classIndexOf(classDict)) > (isForwardedObjectClassIndexPun())); + classDictSize = (((numSlots = byteAt((void *)(classDict + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classDict - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (classDictSize > MethodArrayIndex) { + /* begin noFixupFollowField:ofObject: */ + methodArray = longAt((void *)((classDict + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(methodArray & (tagMask())))) + && ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(methodArray)); + referentSqInt = longAt((void *)((methodArray + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + methodArray = referentSqInt; + } + assert(!(isForwarded(methodArray))); + i = 0; + while (i < (classDictSize - SelectorStart)) { + if (meth == (noFixupFollowFieldofObject(i, methodArray))) { + return currClass; + } + i += 1; + } + } + + /* begin noFixupSuperclassOf: */ + /* begin noFixupFollowField:ofObject: */ + objOop = longAt((void *)((currClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + referentSqInt = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + objOop = referentSqInt; + } + currClass = objOop; + } while(!(currClass == GIV(nilObj))); + return currClass; +} + + /* StackInterpreter>>#findClassOfMethod:forReceiver: */ +sqInt +findClassOfMethodforReceiver(sqInt meth, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt rclass; + sqInt tagBits; + + if ((/* addressCouldBeOop: */ + (((rcvr & (tagMask())) != 0)) + || (addressCouldBeObj(rcvr))) + && (!(/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))))) { + rclass = findClassContainingMethodstartingAt(meth, /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr))); + if (rclass != GIV(nilObj)) { + return rclass; + } + } + if (!((addressCouldBeObj(meth)) + && (((byteAt((void *)(meth + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return GIV(nilObj); + } + return findClassContainingMethodstartingAt(meth, safeMethodClassOf(meth)); +} + + +/* Answer the frame above theFP (adjacent frame nearest head end). + If theFP is the head frame answer 0. */ + + /* StackInterpreter>>#findFrameAbove:inPage: */ +static NoDbgRegParms char * +findFrameAboveinPage(char *theFP, StackPage *thePage) +{ + char *callerFP; + char *fp; + + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == theFP) { + return 0; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == theFP) { + return fp; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + return 0; +} + + +/* Answer the highest priority of runnable process without altering any + lists. Do *not* update highestRunnableProcessPriority. + Answer 0 if no runnable process can be found. */ + + /* StackInterpreter>>#findHighestPriority */ +static sqInt +findHighestPriority(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + sqInt objOop; + sqInt p; + sqInt processList; + sqInt schedLists; + + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + schedLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + p = (GIV(highestRunnableProcessPriority) + ? GIV(highestRunnableProcessPriority) + : (/* begin numSlotsOf: */ + assert((classIndexOf(schedLists)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(schedLists + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(schedLists - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))); + while (((p -= 1)) >= 0) { + processList = longAt((void *)((schedLists + BaseHeaderSize) + ((((usqInt)(p) << (shiftForWord())))))); + + /* begin isEmptyList: */ + assert(!(isForwarded(processList))); + if (!((longAt((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj))) { + return p + 1; + } + } + return 0; +} + + /* StackInterpreter>>#findHomeForContext: */ +static NoDbgRegParms sqInt +findHomeForContext(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt closureOrNil; + + if (!(/* isContext: */ + ((!(aContext & (tagMask())))) + && (((longAt((void *)(aContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + return null; + } + closureOrNil = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord())))))); + assert((isPointers(closureOrNil)) + && ((closureOrNil == (nilObject())) + || ((numSlotsOf(closureOrNil)) >= ClosureFirstCopiedValueIndex))); + if ((((closureOrNil & (tagMask())) != 0)) + || (closureOrNil == GIV(nilObj))) { + return aContext; + } + + /* robustness in the presence of bugs */ + if ((((longAt((void *)(closureOrNil))) & (classIndexMask())) != ClassBlockClosureCompactIndex) + && (((longAt((void *)(closureOrNil))) & (classIndexMask())) != ClassFullBlockClosureCompactIndex)) { + return null; + } + return findHomeForContext(longAt((void *)((closureOrNil + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord()))))))); +} + + +/* See findUnwindThroughContext:. Alas this is mutually recursive with + findMethodWithPrimitive:FromFP:SP:ThroughContext: instead of iterative. + We're doing the simplest thing that could possibly work. Niceties can + wait. */ +/* Being mutually-recursive with findMethodWithPrimitive:FromFP:UpToContext: + gives the author's type inference algorithm headaches. Wimp out by + declaring the return type. + */ + + /* StackInterpreter>>#findMethodWithPrimitive:FromContext:UpToContext: */ +static NoDbgRegParms sqInt +findMethodWithPrimitiveFromContextUpToContext(sqInt primitive, sqInt senderContext, sqInt homeContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt senderContextSqInt; + sqInt senderOop; + char *startFP; + sqInt theContext; + char *theFP; + char *theFPAbove; + sqInt theMethod; + sqInt theMethodSqInt; + StackPage *thePage; + char *theSP; + + assert((senderContext == (nilObject())) + || (isContext(senderContext))); + assert((homeContext == (nilObject())) + || (isContext(homeContext))); + theContext = senderContext; + while (1) { + if (theContext == GIV(nilObj)) { + return theContext; + } + if (((((longAt((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) break; + if (theContext == homeContext) { + return 0; + } + if (!((primitive == 0) + || ((longAt((void *)((theContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord()))))))) != GIV(nilObj)))) { + /* begin followObjField:ofObject: */ + theMethod = longAt((void *)((theContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + assert(isNonImmediate(theMethod)); + if ((!((longAt((void *)(theMethod))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + theMethod = fixFollowedFieldofObjectwithInitialValue(MethodIndex, theContext, theMethod); + } + if ((primitiveIndexOf(theMethod)) == primitive) { + return theContext; + } + } + + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SenderIndex, theContext, objOop); + } + theContext = objOop; + } + if (isWidowedContext(theContext)) { + return GIV(nilObj); + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + startFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin findMethodWithPrimitive:FromFP:UpToContext: */ + theFP = startFP; + theFPAbove = startFP; + do { + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) + && (homeContext == (longAt(theFP + FoxThisContext)))) { + return 0; + } + if (!((primitive == 0) + || (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0)))) { + theMethodSqInt = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + if ((primitiveIndexOf(theMethodSqInt)) == primitive) { + if (theFP == theFPAbove) { + theSP = findSPOfon(theFP, stackPageFor(theFP)); + } + else { + /* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFPAbove))); + theSP = (theFPAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFPAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFPAbove))->cmNumArgs) + : byteAt((theFPAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + return longAt(theFP + FoxThisContext); + } + return marryFrameSP(theFP, theSP); + } + } + theFPAbove = theFP; + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + } while(theFP != 0); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFPAbove)); + thePage = stackPageAtpages(pageIndexFor(theFPAbove), GIV(pages)); + senderContextSqInt = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(senderContextSqInt)); + assert((senderContextSqInt == (nilObject())) + || (isContext(followMaybeForwarded(senderContextSqInt)))); + if (!(/* isContext: */ + ((!(senderContextSqInt & (tagMask())))) + && (((longAt((void *)(senderContextSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + return GIV(nilObj); + } + return findMethodWithPrimitiveFromContextUpToContext(primitive, senderContextSqInt, homeContext); +} + + /* StackInterpreter>>#findSelectorOfMethod: */ +sqInt +findSelectorOfMethod(sqInt aMethodOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classDict; + usqInt classDictSize; + sqInt classObj; + sqInt homeMethod; + sqInt i; + sqInt methodArray; + usqInt numSlots; + sqInt objOop; + sqInt referent; + sqInt selector; + + if (!(addressCouldBeObj(aMethodOop))) { + return GIV(nilObj); + } + if ((!((longAt((void *)(aMethodOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + return findSelectorOfMethod(followForwarded(aMethodOop)); + } + if (!(/* isOopCompiledMethod: */ + ((!(aMethodOop & (tagMask())))) + && (((byteAt((void *)(aMethodOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return GIV(nilObj); + } + homeMethod = homeMethodOf(aMethodOop); + if ((selector = maybeSelectorOfMethod(homeMethod))) { + return selector; + } + classObj = safeMethodClassOf(homeMethod); + if (addressCouldBeClassObj(classObj)) { + /* begin noFixupFollowField:ofObject: */ + classDict = longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(classDict & (tagMask())))) + && ((!((longAt((void *)(classDict))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(classDict)); + referent = longAt((void *)((classDict + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + classDict = referent; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(classDict)) > (isForwardedObjectClassIndexPun())); + classDictSize = (((numSlots = byteAt((void *)(classDict + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classDict - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (classDictSize > MethodArrayIndex) { + /* begin noFixupFollowField:ofObject: */ + methodArray = longAt((void *)((classDict + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(methodArray & (tagMask())))) + && ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(methodArray)); + referent = longAt((void *)((methodArray + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + methodArray = referent; + } + i = 0; + while (i < (classDictSize - SelectorStart)) { + if (homeMethod == (noFixupFollowFieldofObject(i, methodArray))) { + /* begin noFixupFollowField:ofObject: */ + objOop = longAt((void *)((classDict + BaseHeaderSize) + ((((usqInt)((i + SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + referent = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + objOop = referent; + } + return objOop; + } + i += 1; + } + } + } + return GIV(nilObj); +} + + +/* Search for the stack pointer for theFP. This points to the hottest item on + the frame's stack. + DO NOT CALL THIS WITH theFP == localFP OR theFP == framePointer! */ + + /* StackInterpreter>>#findSPOf:on: */ +static NoDbgRegParms char * +findSPOfon(char *theFP, StackPage *thePage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *aFrame; + char *prevFrame; + char *startFrame; + char *theSP; + + assert(!((isFree(thePage)))); + startFrame = (thePage->headFP); + + /* begin findSPOrNilOf:on:startingFrom: */ + prevFrame = ((char *) 0); + if (startFrame == theFP) { + if (((thePage->headSP)) >= startFrame) { + /* If the SP is invalid return the pointer to the receiver field. */ + theSP = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + goto l1; + } + + /* Skip the instruction pointer on top of stack of inactive pages. */ + theSP = (thePage == GIV(stackPage) + ? (thePage->headSP) + : ((thePage->headSP)) + BytesPerWord); + goto l1; + } + aFrame = startFrame; + while (1) { + prevFrame = aFrame; + aFrame = ((char *)(longAt(aFrame + FoxSavedFP))); + if (!(aFrame != 0)) break; + if (theFP == aFrame) { + /* begin frameCallerSP: */ + assert(!(isBaseFrame(prevFrame))); + theSP = (prevFrame + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(prevFrame + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(prevFrame))->cmNumArgs) + : byteAt((prevFrame + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + goto l1; + } + } + theSP = null; + /* end findSPOrNilOf:on:startingFrom: */ +l1: + if (theSP) { + return theSP; + } + error("did not find theFP in stack page"); + return 0; +} + + +/* Search for either an unwind-protect (activation of method with primitive + 198, PrimNumberUnwindMarker) + or homeContext along the sender chain, which ever is found first. Return + values: 0 home context was found on sender chain with no intervening + unwind-protects nilObj home context could not be found => cannotReturn + context the context of an intervening unwind-protect implies home context + was found */ +/* Almost always (98%) the home is on the same page, in which case we know it + will be found. + */ + + /* StackInterpreter>>#findUnwindThroughContext: */ +static NoDbgRegParms sqInt +findUnwindThroughContext(sqInt homeContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxtOrNilOrZero; + sqInt onSamePage; + sqInt senderContext; + char *theFP; + char *theFPAbove; + sqInt theMethod; + sqInt theMethodSqInt; + StackPage *thePage; + char *theSP; + + onSamePage = (/* isStillMarriedContext: */ + (((((longAt((void *)((homeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(homeContext)))) + && ((pageIndexFor(GIV(framePointer))) == (pageIndexFor(frameOfMarriedContext(homeContext)))); + + /* Since nothing changes we don't need to internalize. */ + + /* begin findMethodWithPrimitive:FromFP:UpToContext: */ + theFP = GIV(framePointer); + theFPAbove = GIV(framePointer); + do { + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) + && (homeContext == (longAt(theFP + FoxThisContext)))) { + ctxtOrNilOrZero = 0; + goto l1; + } + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0))) { + theMethodSqInt = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + if ((primitiveIndexOf(theMethodSqInt)) == PrimNumberUnwindMarker) { + if (theFP == theFPAbove) { + theSP = findSPOfon(theFP, stackPageFor(theFP)); + } + else { + /* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFPAbove))); + theSP = (theFPAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFPAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFPAbove))->cmNumArgs) + : byteAt((theFPAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + ctxtOrNilOrZero = longAt(theFP + FoxThisContext); + goto l1; + } + ctxtOrNilOrZero = marryFrameSP(theFP, theSP); + goto l1; + } + } + theFPAbove = theFP; + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + } while(theFP != 0); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFPAbove)); + thePage = stackPageAtpages(pageIndexFor(theFPAbove), GIV(pages)); + senderContext = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(senderContext)); + assert((senderContext == (nilObject())) + || (isContext(followMaybeForwarded(senderContext)))); + if (!(/* isContext: */ + ((!(senderContext & (tagMask())))) + && (((longAt((void *)(senderContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + ctxtOrNilOrZero = GIV(nilObj); + goto l1; + } + ctxtOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(PrimNumberUnwindMarker, senderContext, homeContext); + /* end findMethodWithPrimitive:FromFP:UpToContext: */ +l1: + assert(!((onSamePage + && (ctxtOrNilOrZero == (nilObject()))))); + if (!ctxtOrNilOrZero) { + theMethod = longAt((void *)((homeContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + if ((primitiveIndexOf(theMethod)) == PrimNumberUnwindMarker) { + return homeContext; + } + return 0; + } + + /* If an unwind was found, can the home context be found also? No need to look if on the same page. + No need to look if cannot return (ctxtOrNilOrZero = objectMemory nilObject) */ + if (!(onSamePage + || (ctxtOrNilOrZero == GIV(nilObj)))) { + if ((findMethodWithPrimitiveFromContextUpToContext(0, ctxtOrNilOrZero, homeContext)) == GIV(nilObj)) { + return GIV(nilObj); + } + } + return ctxtOrNilOrZero; +} + + /* StackInterpreter>>#flush */ +void +flush(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fflush(GIV(transcript)); +} + + +/* methodObj is a CompiledMethod. If it contains an external primitive, + flush the function address and session ID of the CM. Answer the prim + index for the benefit of subclass overrides. */ + + /* StackInterpreter>>#flushExternalPrimitiveOf: */ +static NoDbgRegParms sqInt +flushExternalPrimitiveOf(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt headerSqInt; + sqInt lit; + usqInt numSlots; + sqInt primIdx; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + headerSqInt = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* primitiveIndexOfMethod:header: */ + if (((header & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((header >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIdx = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIdx = 0; + } + if ((primIdx == PrimNumberExternalCall) + && (((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) > 0)) { + lit = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if ((/* isArray: */ + ((!(lit & (tagMask())))) + && (((byteAt((void *)(lit + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(lit)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(lit + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(lit - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(lit)) + && (!(isForwarded(lit)))); + assert(validStorePointerUncheckedArgs(2, lit, ConstZero)); + longAtput((void *)((lit + BaseHeaderSize) + (2U << (shiftForWord()))),ConstZero); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(lit)) + && (!(isForwarded(lit)))); + assert(validStorePointerUncheckedArgs(3, lit, ConstZero)); + longAtput((void *)((lit + BaseHeaderSize) + (3U << (shiftForWord()))),ConstZero); + } + } + + /* If not, something's broken */ + return primIdx; +} + + +/* Flush the method caches of references to oldMethod. */ + + /* StackInterpreter>>#flushMethodCacheForMethod: */ +static NoDbgRegParms void +flushMethodCacheForMethod(sqInt oldMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt probe; + + probe = 0; + for (i = 1; i <= MethodCacheEntries; i += 1) { + if ((GIV(methodCache)[probe + MethodCacheMethod]) == oldMethod) { + GIV(methodCache)[probe + MethodCacheSelector] = 0; + } + probe += MethodCacheEntrySize; + } + flushExternalPrimitiveOf(oldMethod); +} + + +/* follow pointers in the current stack frame up to theSP. */ + + /* StackInterpreter>>#followForwardedFrameContents:stackPointer: */ +static NoDbgRegParms void +followForwardedFrameContentsstackPointer(char *theFP, char *theSP) +{ + sqInt oop; + char *ptr; + sqInt referent; + char *toDoLimit; + char *toDoLimit1; + + toDoLimit1 = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + for (ptr = theSP; ptr <= toDoLimit1; ptr += BytesPerWord) { + oop = longAt(ptr); + if (((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(ptr,followForwarded(oop)); + } + } + toDoLimit = theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord()))))); + for (ptr = ((theFP + FoxCallerSavedIP) + BytesPerWord); ptr <= toDoLimit; ptr += BytesPerWord) { + oop = longAt(ptr); + if (((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + longAtput(ptr,oop); + } + } + + /* If we're in a block then also follow fields in the stacked closure as it may be activated again. */ + if (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0)) { + assert(oop == (longAt(theFP + (frameStackedReceiverOffset(theFP))))); + followForwardedObjectFieldstoDepth(oop, 0); + } + assert(!(isForwarded(frameMethodObject(theFP)))); + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(!(isForwarded(frameContext(theFP)))); + } +} + + +/* Force an interrupt check ASAP. + Must set the stack page's limit before stackLimit to avoid + a race condition if this is called from an interrupt handler. */ + + /* StackInterpreter>>#forceInterruptCheck */ +sqInt +forceInterruptCheck(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*iccFunc)(); + StackPage *thePage; + + + /* Do _not_ set stackLimit until the stack system has been initialized. + stackLimit is the initialization flag for the stack system. */ + if (!GIV(stackLimit)) { + return null; + } + thePage = GIV(stackPage); + if ((thePage) + && (thePage != 0)) { + (thePage->stackLimit = ((char *) (((usqInt) -1)))); + } + GIV(stackLimit) = ((char *) (((usqInt) -1))); + sqLowLevelMFence(); + + /* There is a race condition if we test the function and then dereference + it a second time to call it. This is called from interrupt code but at the + same time other code could be clearing the interruptCheckChain via + setInterruptCheckChain:. */ + if (((iccFunc = interruptCheckChain))) { + iccFunc(); + } + GIV(statForceInterruptCheck) += 1; + return 0; +} + + +/* Answer the SP of the caller provided theFP is not a base frame. + This points to the hottest item on the caller frame's stack. */ + + /* StackInterpreter>>#frameCallerSP: */ +static NoDbgRegParms char * +frameCallerSP(char *theFP) +{ + assert(!(isBaseFrame(theFP))); + return (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; +} + + /* StackInterpreter>>#frameContext: */ +static NoDbgRegParms sqInt +frameContext(char *theFP) +{ + return longAt(theFP + FoxThisContext); +} + + /* StackInterpreter>>#frameOfMarriedContext: */ +static NoDbgRegParms char * +frameOfMarriedContext(sqInt aContext) +{ + sqInt senderOop; + + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + + /* begin withoutSmallIntegerTags: */ + assert((((senderOop) & 7) == 1)); + return ((char *)(senderOop - (smallIntegerTag()))); +} + + +/* Answer the offset in bytes from the the frame pointer to its stacked + receiver. The receiver of a message send or the closure of a block + activation is + always on the stack above any arguments and the frame itself. See the + diagram in StackInterpreter class>>initializeFrameIndices. */ + + /* StackInterpreter>>#frameStackedReceiverOffset: */ +static NoDbgRegParms sqInt +frameStackedReceiverOffset(char *theFP) +{ + return (FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))); +} + + +/* Answer the stacked receiver given the frame's argument count. + The receiver of a message send or the closure of a block activation is + always on the stack above any arguments and the frame itself. See the + diagram in StackInterpreter class>>initializeFrameIndices. */ + + /* StackInterpreter>>#frameStackedReceiver:numArgs: */ +static NoDbgRegParms sqInt +frameStackedReceivernumArgs(char *theFP, sqInt numArgs) +{ + return longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)(numArgs) << (shiftForWord())))))); +} + + +/* Free any untraced stack pages. */ + + /* StackInterpreter>>#freeUntracedStackPages */ +static void +freeUntracedStackPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + StackPage *thePage; + + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((!(isFree(thePage))) + && (((thePage->trace)) == StackPageUnreached)) { + assert(noMarkedContextsOnPage(thePage)); + freeStackPage(thePage); + } + assert(((thePage->trace = StackPageTraceInvalid)) != 0); + } +} + + +/* Repaint the entire smalltalk screen, ignoring the affected rectangle. Used + in some platform's code when the Smalltalk window is brought to the front + or uncovered. + */ + + /* StackInterpreter>>#fullDisplayUpdate */ +sqInt +fullDisplayUpdate(void) +{ + updateDisplayLeftTopRightBottom(0, 0, displayWidth, displayHeight); + ioForceDisplayUpdate(); + return 0; +} + + +/* Find an actual function pointer for this primitiveIndex. This is an + opportunity to specialise the prim for the relevant class (format for + example). Default for now is simply the entry in the base primitiveTable. */ + + /* StackInterpreter>>#functionPointerFor:inClass: */ +static NoDbgRegParms void +(*functionPointerForinClass(sqInt primIdx, sqInt theClass))(void) +{ + return ((((usqInt)primIdx)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primIdx]); +} + + +/* Answer an array of flags indicating various optional features of the Cog + VM. If the bit is set then... + Bit 0: supports two bytecode sets (MULTIPLEBYTECODESETS) + Bit 1: supports immutablity (IMMUTABILITY) + Bit 2: suffers from a UNIX setitimer signal-based heartbeat + Bit 3: the VM provides cross-platform bit-identical floating point + Bit 4: the VM can catch exceptions in FFI calls and answer them as + primitive failures + Bit 5: the VM has suspend primitives 568 & 578 which back up a process to + before the wait if it was waiting on a condition variable + Bit 6: the VM is a threading VM */ + + /* StackInterpreter>>#getCogVMFeatureFlags */ +static sqInt +getCogVMFeatureFlags(void) +{ + return (((usqInt)((((((1 + ( +#if IMMUTABILITY + (IMMUTABILITY + ? 2 + : 0) +#else + 0 +#endif + )) + ( +#if ITIMER_HEARTBEAT + 4 +#else + 0 +#endif + )) + ( +#if BIT_IDENTICAL_FLOATING_POINT + 8 +#else + 0 +#endif + )) + ((ioCanCatchFFIExceptions() + ? 16 + : 0))) + 32) + 64) << 3) | 1); +} + + +/* Answer the errorCode object to supply to a failing primitive method that + accepts one. + If there is a primitive error table and the primFailCode is a valid index + there-in answer + the corresponding entry in the table, otherwise simply answer the code as + an integer. + */ + + /* StackInterpreter>>#getErrorObjectFromPrimFailCode */ +static sqInt +getErrorObjectFromPrimFailCode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + usqInt clone; + usqInt errObj; + sqInt i; + usqInt newObj; + usqInt numBytes; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objOop; + sqInt table; + sqInt valuePointer; + + if (GIV(primFailCode) > 0) { + table = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(PrimitiveErrorTableIndex) << (shiftForWord())))))); + if (GIV(primFailCode) <= ((/* begin numSlotsOf: */ + assert((classIndexOf(table)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(table + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(table - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))) { + /* begin followField:ofObject: */ + objOop = longAt((void *)((table + BaseHeaderSize) + ((((usqInt)((GIV(primFailCode) - 1)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(GIV(primFailCode) - 1, table, objOop); + } + errObj = objOop; + + /* If there's a clonable object in the table at that index, + answer a clone of the error object with the second slot set to the value of secondaryErrorCode. */ + if (((byteAt((void *)(errObj + (formatFieldByteOffset())))) & (formatMask())) == (nonIndexablePointerFormat())) { + /* begin cloneErrorObj: */ + /* begin numSlotsOf: */ + assert((classIndexOf(errObj)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(errObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(errObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + classIndex = (longAt((void *)(errObj))) & (classIndexMask()); + + /* begin eeInstantiateAnySmallClassIndex:format:numSlots: */ + assert((numSlots >= 0) + && ((classIndex != 0) + && ((classAtIndex(classIndex)) != GIV(nilObj)))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(classAtIndex(classIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + clone = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + classIndex); + GIV(freeStart) += numBytes; + clone = newObj; + /* end eeInstantiateAnySmallClassIndex:format:numSlots: */ +l1: + for (i = 0; i < numSlots; i += 1) { + valuePointer = longAt((void *)((errObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(clone)) + && (!(isForwarded(clone)))); + assert(validStorePointerUncheckedArgs(i, clone, valuePointer)); + longAtput((void *)((clone + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + if ((numSlots > 2) + && (GIV(primFailCode) == PrimErrFFIException)) { + valuePointer = positive64BitIntegerFor(((usqLong) GIV(secondaryErrorCode))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(clone)) + && (!(isForwarded(clone)))); + assert(validStorePointerUncheckedArgs(1, clone, valuePointer)); + longAtput((void *)((clone + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(exceptionPC)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(clone)) + && (!(isForwarded(clone)))); + assert(validStorePointerUncheckedArgs(2, clone, valuePointer)); + longAtput((void *)((clone + BaseHeaderSize) + (2U << (shiftForWord()))),valuePointer); + } + else { + valuePointer = signed64BitIntegerFor(GIV(secondaryErrorCode)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(clone)) + && (!(isForwarded(clone)))); + assert(validStorePointerUncheckedArgs(1, clone, valuePointer)); + longAtput((void *)((clone + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + } + errObj = clone; + } + return errObj; + } + } + return (((usqInt)GIV(primFailCode) << 3) | 1); +} + + +/* If bit 0 of the imageHeaderFlags is set then the system should use the + full screen. + */ + + /* StackInterpreter>>#getFullScreenFlag */ +int +getFullScreenFlag(void) +{ + return ((imageHeaderFlags & 1) != 0); +} + + /* StackInterpreter>>#getInterruptKeycode */ +sqInt +getInterruptKeycode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(interruptKeycode); +} + + /* StackInterpreter>>#getInterruptPending */ +sqInt +getInterruptPending(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(interruptPending); +} + + /* StackInterpreter>>#getNextWakeupUsecs */ +usqLong +getNextWakeupUsecs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(nextWakeupUsecs); +} + + /* StackInterpreter>>#getSavedWindowSize */ +sqInt +getSavedWindowSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(savedWindowSize); +} + + +/* Answer the next 16 bit word read from aFile, byte-swapped according to the + swapFlag. + */ + + /* StackInterpreter>>#getShortFromFile:swap: */ +static NoDbgRegParms usqInt +getShortFromFileswap(sqImageFile aFile, sqInt swapFlag) +{ + unsigned short w; + + w = 0; + sqImageFileRead((&w), sizeof(unsigned short), 1, aFile); + return (swapFlag + ? ((((usqInt)(w)) >> 8) & 0xFF) | (((w & 0xFF) << 8)) + : w); +} + + +/* Answer the screen size to write to the snapshot. + If the actual screen size is zero then write the savedWindowSize instead. */ + + /* StackInterpreter>>#getSnapshotScreenSize */ +static sqInt +getSnapshotScreenSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt screenSize; + + screenSize = ioScreenSize(); + if (((screenSize & 0xFFFF) == 0) + || ((((usqInt)(screenSize)) >> 16) == 0)) { + screenSize = GIV(savedWindowSize); + } + return screenSize; +} + + +/* For Alien FFI */ + + /* StackInterpreter>>#getStackPointer */ +sqInt * +getStackPointer(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((sqInt *) GIV(stackPointer)); +} + + +/* return the global session ID value */ + + /* StackInterpreter>>#getThisSessionID */ +sqInt +getThisSessionID(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(globalSessionID); +} + + /* StackInterpreter>>#getTranscript */ +FILE * +getTranscript(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(transcript); +} + + +/* Handle a send fault that is due to a send using a forwarded selector. + Unforward the selector and follow the current method and special + selectors array to unforward the source of the forwarded selector. */ + + /* StackInterpreter>>#handleForwardedSelectorFaultFor: */ +static NoDbgRegParms sqInt +handleForwardedSelectorFaultFor(sqInt selectorOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt referent; + + assert(isOopForwarded(selectorOop)); + followForwardedFieldsInCurrentMethod(); + followForwardedObjectFieldstoDepth(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))), 0); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(selectorOop)); + referent = longAt((void *)((selectorOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + return referent; +} + + +/* Handle a send fault that may be due to a send to a forwarded object. + Unforward the receiver on the stack and answer its actual class. */ + + /* StackInterpreter>>#handleForwardedSendFaultForTag: */ +static NoDbgRegParms sqInt +handleForwardedSendFaultForTag(sqInt classTag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt rcvr; + sqInt referent; + sqInt tagBits; + + assert(isForwardedClassTag(classTag)); + rcvr = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* should *not* be a super send, so the receiver should be forwarded. */ + assert(isOopForwarded(rcvr)); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(rcvr)); + referent = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + rcvr = referent; + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord),rcvr); + followForwardedFrameContentsstackPointer(GIV(framePointer), GIV(stackPointer) + ((GIV(argumentCount) + 1) * BytesPerWord)); + if (isPointers(/* frameReceiver: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? longAt(GIV(framePointer) + FoxMFReceiver) + : longAt(GIV(framePointer) + FoxIFReceiver)))) { + followForwardedObjectFieldstoDepth(/* frameReceiver: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? longAt(GIV(framePointer) + FoxMFReceiver) + : longAt(GIV(framePointer) + FoxIFReceiver)), 0); + } + return /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); +} + + +/* Handle a special send fault that may be due to a special selector + send accessing a forwarded object. obj is forwarded. + Unforward stack contents and and inst vars and answer obj's target. */ + + /* StackInterpreter>>#handleSpecialSelectorSendFaultFor:fp:sp: */ +static NoDbgRegParms sqInt +handleSpecialSelectorSendFaultForfpsp(sqInt obj, char *theFP, char *theSP) +{ + sqInt rcvr; + sqInt referent; + + assert(isOopForwarded(obj)); + followForwardedFrameContentsstackPointer(theFP, theSP); + rcvr = /* frameReceiver: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? longAt(theFP + FoxMFReceiver) + : longAt(theFP + FoxIFReceiver)); + if (/* isPointers: */ + ((!(rcvr & (tagMask())))) + && (((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) { + followForwardedObjectFieldstoDepth(rcvr, 0); + } + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + return referent; +} + + +/* Check for stack overflow, moving frames to another stack if so. + This should *only* be sent from checkForStackOverflow. */ + + /* StackInterpreter>>#handleStackOverflow */ +static NeverInline void +handleStackOverflow(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + char *callerFP; + char *callerFP1; + StackPage *newPage; + sqInt overflowCount; + char *overflowLimitAddress; + sqInt sourceCode; + char *theFP; + StackPage *thePage; + + callerFP = ((char *) 0); + assert(GIV(stackPointer) < ((GIV(stackPage)->realStackLimit))); + + /* begin traceStackOverflow */ + if (recordOverflowTrace()) { + sourceCode = ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? CSFromMachineCode + : CSFromInterpreter); + + /* begin recordTrace:source:thing:extra: */ + assert(sourceCode < ((1U << TraceTypeShift))); + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceStackOverflow) << TraceTypeShift))))) + sourceCode); + GIV(traceLog)[GIV(traceLogIndex) + 1] = 0; + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + if ((recordPrimTrace()) + && (!primTracePluginName)) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TraceStackOverflow << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + GIV(statStackOverflow) += 1; + + /* The stack has overflowed this page. If the system is executing some recursive algorithm, + e.g. fibonacci, then the system could thrash overflowing the stack if the call soon returns + back to the current page. To avoid thrashing, since overflow is quite slow, we can move + more than one frame. The idea is to record which page has overflowed, and the first + time it overflows move one frame, the second time two frames, and so on. We move no + more frames than would leave the page half occupied. */ + theFP = GIV(framePointer); + if (GIV(stackPage) == GIV(overflowedPage)) { + overflowLimitAddress = ((GIV(stackPage)->baseAddress)) - GIV(overflowLimit); + overflowCount = (GIV(extraFramesToMoveOnOverflow) += 1); + while ((((overflowCount -= 1)) >= 0) + && ((((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) < overflowLimitAddress) + && (!((longAt(callerFP + FoxSavedFP)) == 0)))) { + theFP = callerFP; + } + } + else { + GIV(overflowedPage) = GIV(stackPage); + GIV(extraFramesToMoveOnOverflow) = 0; + } + + /* begin ensureCallerContext: */ + callerFP1 = ((char *)(longAt(theFP + FoxSavedFP))); + if (!callerFP1) { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + goto l2; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP1 + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP1 + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP1 + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP1))); + + /* frameContext: */ + goto l2; + } + marryFrameSP( + callerFP1, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFP))), + (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + /* end ensureCallerContext: */ +l2: + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l1; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l1: + moveFramesInthroughtoPage(GIV(stackPage), theFP, newPage); + + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (newPage->headSP); + GIV(framePointer) = (newPage->headFP); + assert(validInstructionPointerinFrame(GIV(instructionPointer) + 1, GIV(framePointer))); + assert((!(frameHasContext(GIV(framePointer)))) + || (isContext(frameContext(GIV(framePointer))))); +} + + +/* The stackPointer is below the stackLimit. This is either because of a + stack overflow or the setting of stackLimit to indicate a possible + interrupt. Check for stackOverflow and interrupts anddeal with each + appropriately. Answer if a context switch occurred. */ + + /* StackInterpreter>>#handleStackOverflowOrEventAllowContextSwitch: */ +static NoDbgRegParms sqInt +handleStackOverflowOrEventAllowContextSwitch(sqInt mayContextSwitch) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin checkForStackOverflow */ + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (GIV(stackPointer) < ((GIV(stackPage)->realStackLimit))) { + handleStackOverflow(); + } + + /* If the stackLimit differs from the realStackLimit then the stackLimit + has been set to indicate an event or interrupt that needs servicing. */ + if (GIV(stackLimit) == ((GIV(stackPage)->realStackLimit))) { + return 0; + } + return checkForEventsMayContextSwitch(mayContextSwitch); +} + + +/* This is a C implementation needed by ioSetMaxExtSemTableSize + and e.g. stackPageByteSize. */ + + /* StackInterpreter>>#highBit: */ +sqInt +highBit(usqInt anUnsignedValue) +{ + sqInt bitNo; + usqInt shifted; + + + /* so it shows up in senders... */ + shifted = anUnsignedValue; + bitNo = 0; +# if BytesPerWord > 4 + if (!(shifted < (0x100000000LL))) { + shifted = (shifted) >> 32; + bitNo += 32; + } +# endif + + if (!(shifted < (0x10000))) { + shifted = (shifted) >> 16; + bitNo += 16; + } + if (!(shifted < (0x100))) { + shifted = (shifted) >> 8; + bitNo += 8; + } + if (!(shifted < (16))) { + shifted = (shifted) >> 4; + bitNo += 4; + } + if (!(shifted < (4))) { + shifted = (shifted) >> 2; + bitNo += 2; + } + if (!(shifted < (2))) { + shifted = (shifted) >> 1; + bitNo += 1; + } + + /* shifted 0 or 1 now */ + return bitNo + shifted; +} + + +/* With the full block implementation a CompiledBlock's method is found along + the chain through the last literal. See CompiledBlock>>#method. Answer + the home mehtod, or nilObj if it cannot be found. */ + + /* StackInterpreter>>#homeMethodOf: */ +sqInt +homeMethodOf(sqInt aMethodOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt lastLiteral; + sqInt methodChain; + + assert(isOopCompiledMethod(aMethodOop)); + methodChain = aMethodOop; + while (1) { + lastLiteral = ultimateLiteralOf(methodChain); + if (!(/* isOopCompiledMethod: */ + ((!(lastLiteral & (tagMask())))) + && (((byteAt((void *)(lastLiteral + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return methodChain; + } + if (!(/* isOopCompiledMethod: */ + ((!(lastLiteral & (tagMask())))) + && (((byteAt((void *)(lastLiteral + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return GIV(nilObj); + } + methodChain = lastLiteral; + } + return 0; +} + + +/* If thePage is the stackPage and the stackPointer and/or the framePointer + are pointing within it, + answer if thePage's heapSP and headFP are equal to the stackPointer and + framePointer respectively. + */ + + /* StackInterpreter>>#ifCurrentStackPageHasValidHeadPointers: */ +static NoDbgRegParms sqInt +ifCurrentStackPageHasValidHeadPointers(StackPage *thePage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (thePage == GIV(stackPage)) { + if (/* addressIsInPage: */ + (((thePage->lastAddress)) < GIV(framePointer)) + && (GIV(framePointer) < ((thePage->baseAddress)))) { + if (((thePage->headFP)) != GIV(framePointer)) { + return 0; + } + } + if (/* addressIsInPage: */ + (((thePage->lastAddress)) < GIV(stackPointer)) + && (GIV(stackPointer) < ((thePage->baseAddress)))) { + if (((thePage->headSP)) != GIV(stackPointer)) { + return 0; + } + } + } + return 1; +} + + /* StackInterpreter>>#iframeMethod: */ +static NoDbgRegParms usqInt +iframeMethod(char *theFP) +{ + return longAt(theFP + FoxMethod); +} + + +/* Return the equivalent of + aClass includesBehavior: aSuperclass. + Note: written for efficiency and better inlining (only 1 temp) */ + + /* StackInterpreter>>#includesBehavior:ThatOf: */ +sqInt +includesBehaviorThatOf(sqInt aClass, sqInt aSuperclass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt theClass; + + if (aSuperclass == GIV(nilObj)) { + return 0; + } + theClass = aClass; + while (1) { + if (theClass == aSuperclass) { + return 1; + } + if (!(theClass != GIV(nilObj))) break; + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((theClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, theClass, objOop); + } + theClass = objOop; + } + return 0; +} + + +/* Initialize the stack pages and then invoke continuation. Use alloca'ed + memory so that + when we have a JIT its stack pointer will be on the native stack since + alloca allocates + memory on the stack. Certain thread systems use the native stack pointer + as the + frame ID so putting the stack anywhere else can confuse the thread system. + + This path is for the threaded VM where we may want to allocate the stack + zone on + the stack of a thread other than the main VM thread. */ + + /* StackInterpreter>>#initStackPagesAndContinueInto:with: */ +void +initStackPagesAndContinueIntowith(void (*continuation)(void *), void *argument) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt stackPageBytes; + usqIntptr_t stackZoneBytes; + void *theStackMemory; + + /* begin ensureInitializeStackZone */ + if (!GIV(pages)) { + stackPageBytes = stackPageByteSize(); + stackZoneBytes = (GIV(numStackPages) * ((sizeof(CogStackPage)) + (stackPageByteSize()))) + BytesPerWord; + theStackMemory = alloca(stackZoneBytes); + memset(theStackMemory, 0, stackZoneBytes); + initializeStacknumSlotspageSize(theStackMemory, stackZoneBytes / BytesPerWord, stackPageBytes / BytesPerWord); + } + continuation(argument); +} + + +/* the vm has to convert aFilenameString via any canonicalization and + char-mapping and put the result in aCharBuffer. + Note the resolveAliases flag - this is an awful artefact of OSX and Apples + demented alias handling. When opening a file, the flag must be true, when + closing or renaming it must be false. Sigh. + */ + + /* StackInterpreter>>#ioFilename:fromString:ofLength:resolveAliases: */ +void +ioFilenamefromStringofLengthresolveAliases(char *aCharBuffer, char *aFilenameString, sqInt filenameLength, sqInt aBoolean) +{ + sqGetFilenameFromString(aCharBuffer, aFilenameString, filenameLength, aBoolean); +} + + +/* A base frame (first frame in a stack page) is so marked by having a null + saved fp. + */ + + /* StackInterpreter>>#isBaseFrame: */ +static NoDbgRegParms int +isBaseFrame(char *theFP) +{ + return (longAt(theFP + FoxSavedFP)) == 0; +} + + /* StackInterpreter>>#isBooleanObject: */ +sqInt +isBooleanObject(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (oop == GIV(trueObj)) + || (oop == GIV(falseObj)); +} + + +/* This virtual machine provides two primitives that call external code, + primitiveExternalCall for plugin primitives, and primitiveCalloutToFFI + for FFI calls. */ + + /* StackInterpreter>>#isCalloutPrimitiveIndex: */ +static NoDbgRegParms sqInt +isCalloutPrimitiveIndex(sqInt primIndex) +{ + return (primIndex == PrimNumberExternalCall) + || (primIndex == PrimNumberFFICall); +} + + /* StackInterpreter>>#isEmptyList: */ +static NoDbgRegParms int +isEmptyList(sqInt aLinkedList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(!(isForwarded(aLinkedList))); + return (longAt((void *)((aLinkedList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj); +} + + /* StackInterpreter>>#isFloatObject: */ +int +isFloatObject(sqInt oop) +{ + sqInt tagBits; + + return /* isFloatInstance: */ + ((tagBits = oop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(oop))) & (classIndexMask())) == ClassFloatCompactIndex); +} + + /* StackInterpreter>>#isFrame:onPage: */ +static NoDbgRegParms sqInt +isFrameonPage(char *aFrame, StackPage *aPage) +{ + char *prevFP; + char *theFP; + + assert(!((isFree(aPage)))); + + /* Walk the static chain making sure progress is being made, + and in the right direction, looking for a match with aFrame. */ + theFP = (aPage->headFP); + prevFP = theFP - BytesPerWord; + while (1) { + if (theFP == aFrame) { + return 1; + } + if (!((theFP > prevFP) + && (theFP < ((aPage->baseFP))))) break; + prevFP = theFP; + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + } + return 0; +} + + +/* Answer true if the oop is kind of Integer (Small or Large). */ + + /* StackInterpreter>>#isKindOfInteger: */ +sqInt +isKindOfInteger(sqInt oop) +{ + return ((((oop) & 7) == 1)) + || (/* isLargeIntegerInstance: */ + ((!(oop & (tagMask())))) + && ((((usqInt)(((longAt((void *)(oop))) & (classIndexMask())) - ClassLargeNegativeIntegerCompactIndex))) <= 1)); +} + + /* StackInterpreter>>#isLargeIntegerObject: */ +sqInt +isLargeIntegerObject(sqInt oop) +{ + return /* isLargeIntegerInstance: */ + ((!(oop & (tagMask())))) + && ((((usqInt)(((longAt((void *)(oop))) & (classIndexMask())) - ClassLargeNegativeIntegerCompactIndex))) <= 1); +} + + /* StackInterpreter>>#isLargeNegativeIntegerObject: */ +sqInt +isLargeNegativeIntegerObject(sqInt oop) +{ + return /* isInstanceOfClassLargeNegativeInteger: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassLargeNegativeIntegerCompactIndex); +} + + /* StackInterpreter>>#isLargePositiveIntegerObject: */ +sqInt +isLargePositiveIntegerObject(sqInt oop) +{ + return /* isInstanceOfClassLargePositiveInteger: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassLargePositiveIntegerCompactIndex); +} + + /* StackInterpreter>>#isLinkedExternalPrimitive: */ +static NoDbgRegParms sqInt +isLinkedExternalPrimitive(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt headerSqInt; + sqInt literal; + usqInt numSlots; + sqInt primIdx; + sqInt targetFunctionIndex; + + literal = 0; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + headerSqInt = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* primitiveIndexOfMethod:header: */ + if (((header & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((header >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIdx = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIdx = 0; + } + return (primIdx == PrimNumberExternalCall) + && ((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) > 0) + && ((isArray((literal = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord()))))))) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(literal)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(literal + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(literal - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4) + && (((targetFunctionIndex = longAt((void *)((literal + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord()))))))), + ((((targetFunctionIndex) & 7) == 1)) + && (((targetFunctionIndex >> 3)) > 0)))))); +} + + +/* Answer if the argument, which can be any object, is a live context. */ + + /* StackInterpreter>>#isLiveContext: */ +static NoDbgRegParms sqInt +isLiveContext(sqInt oop) +{ + assert(!((isOopForwarded(oop)))); + if (!(/* isContext: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + return 0; + } + if ((!((longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))) & (tagMask())))) { + return ((((longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1); + } + return !(isWidowedContext(oop)); +} + + /* StackInterpreter>>#isMarriedOrWidowedContext: */ +static NoDbgRegParms int +isMarriedOrWidowedContext(sqInt aContext) +{ + return ((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1); +} + + +/* This virtual machine provides two primitives that executes arbitrary + primitives, one + for indexed primitivces and one for named primitives. These meta + primitives are used + in the debugger to execute primitives while simulating execution. Spur + needs to know + the accessor depth for a primitive so that failures due to forwarders can + be fixed up + and retried. This method identifies such meta primitives so that + metaAccessorDepth can be substituted when appropriate. */ + + /* StackInterpreter>>#isMetaPrimitiveIndex: */ +static NoDbgRegParms sqInt +isMetaPrimitiveIndex(sqInt primIndex) +{ + return (primIndex == PrimNumberDoPrimitive) + || (primIndex == PrimNumberDoExternalCall); +} + + +/* Answer if the method is an external primitive call (prim 117) with a null + external primitive. + This is just for an assert in the CoInterpreter. */ + + /* StackInterpreter>>#isNullExternalPrimitiveCall: */ +static NoDbgRegParms sqInt +isNullExternalPrimitiveCall(sqInt aMethodObj) +{ + sqInt lit; + usqInt numSlots; + + if (!(((primitiveIndexOf(aMethodObj)) == PrimNumberExternalCall) + && ((literalCountOf(aMethodObj)) > 0))) { + return 0; + } + lit = longAt((void *)((aMethodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + return (/* isArray: */ + ((!(lit & (tagMask())))) + && (((byteAt((void *)(lit + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(lit)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(lit + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(lit - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4) + && (((longAt((void *)((lit + BaseHeaderSize) + (3U << (shiftForWord()))))) == ConstZero) + || ((longAt((void *)((lit + BaseHeaderSize) + (3U << (shiftForWord()))))) == ConstMinusOne))); +} + + +/* We save slots in the method cache by using the primitiveFunctionPointer + to hold either a function pointer or the index of a quick primitive. Since + quick primitive indices are small they can't be confused with function + addresses. */ + + /* StackInterpreter>>#isPrimitiveFunctionPointerAnIndex */ +static int +isPrimitiveFunctionPointerAnIndex(void) +{ + return (((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex; +} + + /* StackInterpreter>>#isQuickPrimitiveIndex: */ +int +isQuickPrimitiveIndex(sqInt anInteger) +{ + return ((anInteger >= 0x100) && (anInteger <= 519)); +} + + +/* Reading the sender, instructionPointer and stackPointer inst vars of a + context must take + account of potentially married contexts and fetch the state from the + frame. method, + closureOrNil and receiver can safely be fetched from the context without + checking. + */ + + /* StackInterpreter>>#isReadMediatedContextInstVarIndex: */ +int +isReadMediatedContextInstVarIndex(sqInt index) +{ + return index <= StackPointerIndex; +} + + /* StackInterpreter>>#isSingleContext: */ +static NoDbgRegParms int +isSingleContext(sqInt aContext) +{ + return (!((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))) & (tagMask()))); +} + + +/* Answer if aContext is married or widowed and still married. + If a context is widowed then turn it into a single dead context. */ + + /* StackInterpreter>>#isStillMarriedContext: */ +static NoDbgRegParms sqInt +isStillMarriedContext(sqInt aContext) +{ + return (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext))); +} + + +/* Answer if aClosure is a vanilla BlockClosure as in the first Cog release, + BlockClosure laid out as + 'outerContext, startpc, numArgs. FullBlockClosure is laid out as + outerContext, method, numArgs, receiver. + So either answer true if we're not supporting FullBlockClosure, or test + the startpc/method field. */ + + /* StackInterpreter>>#isVanillaBlockClosure: */ +static NoDbgRegParms int +isVanillaBlockClosure(sqInt aClosure) +{ + assert((isPointers(aClosure)) + && ((numSlotsOf(aClosure)) >= ClosureFirstCopiedValueIndex)); + return ((((longAt((void *)((aClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1); +} + + +/* Answer if the argument is married to a live frame or not. + This method is safe for use only when no frameContext fields may be + forwarded (as maybe the case when scavenging). Post become: all + frameContext fields are followed, and hence normally no following of + frameContext fields is necessary. */ +/* i.e. inline into isWidowedContext: */ + + /* StackInterpreter>>#isWidowedContextNoConvert: */ +static NoDbgRegParms sqInt +isWidowedContextNoConvert(sqInt aOnceMarriedContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anInteger; + sqInt senderOop; + char *shouldBeFrameCallerField; + char *theFrame; + StackPage *thePage; + + assert((isContext(aOnceMarriedContext)) + && (isMarriedOrWidowedContext(aOnceMarriedContext))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + if (!((isFree(thePage)) + || (theFrame < ((thePage->headFP))))) { + /* The frame pointer is within the bounds of a live page. + Now check if it matches a frame. */ + anInteger = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + + /* begin withoutSmallIntegerTags: */ + assert((((anInteger) & 7) == 1)); + shouldBeFrameCallerField = ((char *)(anInteger - (smallIntegerTag()))); + if (((((char *)(longAt(theFrame + FoxSavedFP)))) == shouldBeFrameCallerField) + && (/* frameHasContext: */ + ((((usqInt)(longAt(theFrame + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFrame + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFrame + FoxIFrameFlags) + 2)) != 0))) { + assert(!(((isFrameonPage(theFrame, thePage)) + && (isForwarded(frameContext(theFrame)))))); + if ((longAt(theFrame + FoxThisContext)) == aOnceMarriedContext) { + return 0; + } + } + } + return 1; +} + + +/* Wrining any inst vars of a context must take account of potentially + married contexts + and set the state in the frame. Inst vars in subclasses don't need + mediation; subclasses + can't marry. */ + + /* StackInterpreter>>#isWriteMediatedContextInstVarIndex: */ +int +isWriteMediatedContextInstVarIndex(sqInt index) +{ + return index <= ReceiverIndex; +} + + +/* Support for external primitives. */ + + /* StackInterpreter>>#is:KindOfClass: */ +sqInt +isKindOfClass(sqInt oop, sqInt aClass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt oopClass; + sqInt tagBits; + + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while ((oopClass != GIV(nilObj)) + && ((/* isPointers: */ + ((!(oopClass & (tagMask())))) + && (((byteAt((void *)(oopClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOfAny(oopClass)) > InstanceSpecificationIndex))) { + if (oopClass == aClass) { + return 1; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + return 0; +} + + +/* Support for external primitives. */ + + /* StackInterpreter>>#is:KindOf: */ +sqInt +isKindOf(sqInt oop, char *className) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt oopClass; + sqInt tagBits; + + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while (!(oopClass == GIV(nilObj))) { + if (classNameOfIs(oopClass, className)) { + return 1; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + return 0; +} + + +/* Support for external primitives */ + + /* StackInterpreter>>#is:MemberOf: */ +sqInt +isMemberOf(sqInt oop, char *className) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oopClass; + sqInt tagBits; + + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + return classNameOfIs(oopClass, className); +} + + /* StackInterpreter>>#lengthOfNameOfClass: */ +static NoDbgRegParms sqInt +lengthOfNameOfClass(sqInt classOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + usqInt numSlots; + usqInt numSlots1; + usqInt numSlotsUsqInt; + sqInt objOop; + + /* begin numSlotsOf: */ + assert((classIndexOf(classOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(classOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (numSlots == GIV(metaclassNumSlots)) { + return lengthOfNameOfClass(longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord()))))))); + } + if (numSlots <= GIV(classNameIndex)) { + return 0; + } + objOop = longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(classNameIndex)) << (shiftForWord())))))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + numSlots1 = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots1; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots1 << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots1 << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots1 << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots1; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + /* StackInterpreter>>#literal:ofMethod: */ +sqInt +literalofMethod(sqInt offset, sqInt methodPointer) +{ + return longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); +} + + +/* This entry point needs to be implemented for the interpreter proxy. + Since BitBlt is now a plugin we need to look up + BitBltPlugin:=loadBitBltFrom and call it. This entire mechanism should + eventually go away and be + replaced with a dynamic lookup from BitBltPlugin itself but for backward + compatibility this stub is provided + */ + + /* StackInterpreter>>#loadBitBltFrom: */ +sqInt +loadBitBltFrom(sqInt bb) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void *fn; + + fn = ioLoadFunctionFrom("loadBitBltFrom", "BitBltPlugin"); + if (!fn) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return ((sqInt (*)(sqInt))fn)(bb); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#longPrintOop: */ +void +longPrintOop(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aClass; + int byte; + char *className; + sqInt classPointer; + sqInt cls; + CogMethod *cogMethod; + sqInt column; + sqInt contextSize; + sqInt fieldOop; + sqInt fmt; + sqInt fmtSqInt; + sqInt header; + sqInt headerSqInt; + sqInt i; + usqInt index; + sqInt lastIndex; + usqInt lastPointer; + sqInt length; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objOop; + sqInt oopClass; + sqInt sp; + sqInt tagBits; + char *where; + + length = 0; + if (((oop & (tagMask())) != 0)) { + printImmediateObjecton(oop, GIV(transcript)); + return; + } + if (!(addressCouldBeObj(oop))) { + /* begin printCantBeObject:on: */ + fprintf(GIV(transcript), + "%p%s\n", + ((void *)oop), + ((where = whereIsMaybeCodeThing(oop)) + ? where + : (oop & 7 /* (allocationUnit - 1) */ + ? " is misaligned" + : whereIs(oop)))); + return; + } + if (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + printFreeObjecton(oop, GIV(transcript)); + return; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + printForwarderon(oop, GIV(transcript)); + return; + } + if ((cls = fetchClassOfNonImm(oop))) { + className = nameOfClasslengthInto(cls, (&length)); + fprintf(GIV(transcript), + "%p: a(n) %.*s", + ((void *)oop), + ((int) length), + className); + fprintf(GIV(transcript), + "(%" PRIxSQINT "=>%p)", + (longAt((void *)(oop))) & (classIndexMask()), + ((void *)cls)); + } + else { + fprintf(GIV(transcript), + "%p has a nil class!!\n", + ((void *)oop)); + } + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + fprintf(GIV(transcript), + " format %" PRIxSQINT "", + fmt); + if (fmt > 5 /* lastPointerFormat */) { + fprintf(GIV(transcript), + " nbytes %" PRIdSQINT "", + numBytesOf(oop)); + } + else { + if (/* isIndexableFormat: */ + (fmt >= (arrayFormat())) + && ((fmt <= (weakArrayFormat())) + || (fmt >= (sixtyFourBitIndexableFormat())))) { + /* begin lengthOf: */ + fmtSqInt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmtSqInt <= (ephemeronFormat())) { + length = numSlots; + goto l2; + } + if (fmtSqInt >= (firstByteFormat())) { + length = ((numSlots << (shiftForWord()))) - (fmtSqInt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmtSqInt >= (firstShortFormat())) { + length = ((numSlots << ((shiftForWord()) - 1))) - (fmtSqInt & 3); + goto l2; + } + if (fmtSqInt >= (firstLongFormat())) { + length = ((numSlots << ((shiftForWord()) - 2))) - (fmtSqInt & 1); + goto l2; + } + if (fmtSqInt == (sixtyFourBitIndexableFormat())) { + length = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + length = 0; + /* end lengthOf: */ +l2: + fprintf(GIV(transcript), + " size %" PRIdSQINT "", + length - (fixedFieldsOfformatlength(oop, fmt, length))); + } + } + + /* begin printHeaderTypeOf:on: */ + fprintf(GIV(transcript), + " hdr%d %c%c%c%c%c", + ((byteAt((void *)(oop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? 16 + : 8), + ((((usqInt)((byteAt((void *)(oop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1 + ? 'i' + : '.'), + ((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())) + ? 'r' + : '.'), + ((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift())) + ? 'p' + : '.'), + ((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (markedBitByteShift())) + ? 'm' + : '.'), + ((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (greyBitByteShift())) + ? 'g' + : '.')); + fprintf(GIV(transcript), + " hash %x\n", + (long32At((void *)(oop + 4))) & (identityHashHalfWordMask())); + if (/* isPureBitsFormat: */ + (fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat()))) { + /* This will answer false if splObj: ClassAlien is nilObject */ + + /* begin is:KindOfClass: */ + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while ((oopClass != GIV(nilObj)) + && ((/* isPointers: */ + ((!(oopClass & (tagMask())))) + && (((byteAt((void *)(oopClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOfAny(oopClass)) > InstanceSpecificationIndex))) { + if (oopClass == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord())))))))) { + fprintf(GIV(transcript), + " datasize %" PRIdSQINT " %s @ %p\n", + longAt((void *)(oop + BaseHeaderSize)), + ((longAt((void *)(oop + BaseHeaderSize))) < 0 + ? "indirect" + : (longAt((void *)(oop + BaseHeaderSize)) + ? "direct" + : "pointer")), + startOfAlienData(oop)); + return; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + classPointer = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord())))))); + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, objOop); + } + aClass = objOop; + + /* begin is:KindOfClass: */ + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while ((oopClass != GIV(nilObj)) + && ((/* isPointers: */ + ((!(oopClass & (tagMask())))) + && (((byteAt((void *)(oopClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOfAny(oopClass)) > InstanceSpecificationIndex))) { + if (oopClass == aClass) { + printStringDataOfon(oop, GIV(transcript)); + return; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + printNonPointerDataOfon(oop, GIV(transcript)); + return; + } + + /* begin numPointerSlotsOf: */ + fmtSqInt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if (fmtSqInt <= 5 /* lastPointerFormat */) { + if ((fmtSqInt == (indexablePointersFormat())) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l3; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(oop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l3: + lastPointer = CtxtTempFrameStart + contextSize; + goto l4; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())); + lastPointer = (((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l4; + } + if (fmtSqInt == (forwardedFormat())) { + lastPointer = 1; + goto l4; + } + if (fmtSqInt < (firstCompiledMethodFormat())) { + lastPointer = 0; + goto l4; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(oop)); + headerSqInt = longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + lastPointer = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l4: + lastIndex = ((0x100 < lastPointer) ? 0x100 : lastPointer); + if (lastIndex > 0) { + for (i = 1; i <= lastIndex; i += 1) { + fieldOop = longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)((i - 1)) << (shiftForWord())))))); + + /* begin space */ + printChar(' '); + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)(i - 1))); + printChar(' '); + printHex(fieldOop); + + /* begin space */ + printChar(' '); + if ((i == 1) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) { + /* begin printMethodHeaderOop: */ + /* begin isCogMethodReference: */ + assert(((((fieldOop) & 7) == 1)) + || (((((usqInt)fieldOop)) < (startOfMemory())) + && ((((usqInt)fieldOop)) >= (minCogMethodAddress())))); + if ((!(fieldOop & (smallIntegerTag())))) { + if ((cogMethod = methodFor(((void *)fieldOop)))) { + printHex(fieldOop); + + /* begin space */ + printChar(' '); + printDecodeMethodHeaderOop((cogMethod->methodHeader)); + goto l1; + } + } + printDecodeMethodHeaderOop(fieldOop); + /* end printMethodHeaderOop: */ +l1:; + } + else { + printOopShortInner(fieldOop); + } + cr(); + } + } + if (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + lastPointer = (((literalCountOf(oop)) + LiteralStart) * BytesPerOop) + 1; + + /* begin lengthOf: */ + fmtSqInt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmtSqInt <= (ephemeronFormat())) { + lastIndex = numSlots; + goto l5; + } + if (fmtSqInt >= (firstByteFormat())) { + lastIndex = ((numSlots << (shiftForWord()))) - (fmtSqInt & 7); + goto l5; + } + + /* bytes, including CompiledMethod */ + if (fmtSqInt >= (firstShortFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 1))) - (fmtSqInt & 3); + goto l5; + } + if (fmtSqInt >= (firstLongFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 2))) - (fmtSqInt & 1); + goto l5; + } + if (fmtSqInt == (sixtyFourBitIndexableFormat())) { + lastIndex = numSlots; + goto l5; + } + + /* fmt = self forwardedFormat */ + lastIndex = 0; + /* end lengthOf: */ +l5: + if ((lastIndex - lastPointer) > 0x100) { + lastIndex = lastPointer + 0x100; + } + column = 1; + for (index = lastPointer; index <= lastIndex; index += 1) { + if (column == 1) { + fprintf(GIV(transcript), + "%10p ", + ((void *)(((oop + BaseHeaderSize) + index) - 1))); + } + byte = ((int) (byteAt((void *)((oop + BaseHeaderSize) + (index - 1))))); + fprintf(GIV(transcript), + "%02x/%-+3d%c", + byte, + byte, + (column == 8 + ? '\n' + : ' ')); + if (((column += 1)) > 8) { + column = 1; + } + } + if ((lengthOf(oop)) > lastIndex) { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "..."); + } + if (((column >= 2) && (column <= 7))) { + cr(); + } + } + else { + if (lastPointer > lastIndex) { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "..."); + cr(); + } + } +} + + +/* Answer the relevant long store temp bytecode, which indicates it has a + primitive error code. + */ +/* 234 11101010 i i i i i i i i Store Temporary Variable #iiiiiiii */ +/* 129 10000001 jjkkkkkk Store (Receiver Variable, Temporary Location, + Illegal, Literal Variable) [jj] #kkkkkk */ + + /* StackInterpreter>>#longStoreBytecodeForHeader: */ +sqInt +longStoreBytecodeForHeader(sqInt methodHeader) +{ + return ((((sqLong) methodHeader)) < 0 + ? AltLongStoreBytecode + : LongStoreBytecode); +} + + +/* This method implements a simple method lookup cache. If an entry for the + given selector and classTag is found in the cache, set the values of + 'newMethod' and + 'primitiveFunctionPointer' and answer true. Otherwise, answer false. */ + + /* StackInterpreter>>#lookupInMethodCacheSel:classTag: */ +static NoDbgRegParms sqInt +lookupInMethodCacheSelclassTag(sqInt selector, sqInt classTag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt hash; + usqInt probe; + + /* begin inlineLookupInMethodCacheSel:classTag: */ + /* shift drops two low-order zeros from addresses */ + hash = selector ^ ((((usqInt)(classTag) << 2))); + + /* first probe */ + probe = hash & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == selector) + && ((GIV(methodCache)[probe + MethodCacheClass]) == classTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + return 1; + } + + /* second probe */ + probe = (((usqInt)(hash)) >> 1) & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == selector) + && ((GIV(methodCache)[probe + MethodCacheClass]) == classTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + return 1; + } + probe = (((usqInt)(hash)) >> 2) & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == selector) + && ((GIV(methodCache)[probe + MethodCacheClass]) == classTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + return 1; + } + return 0; +} + + /* StackInterpreter>>#lookupMethodInClass: */ +static NoDbgRegParms sqInt +lookupMethodInClass(sqInt class) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentClass; + sqInt dictionary; + sqInt found; + sqInt index; + usqInt length; + sqInt mask; + sqInt methodArray; + sqInt nextSelector; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt tagBits; + sqInt wrapAround; + + assert(addressCouldBeClassObj(class)); + + /* begin lookupBreakFor: */ + if ((breakSelectorLength <= 0) + && (/* shouldBreakForLookupIn:given: */ + (breakLookupClassTag) + && ((class == breakLookupClassTag) + || (((long32At((void *)(class + 4))) & (identityHashHalfWordMask())) == breakLookupClassTag)))) { + warning("lookup class send break (heartbeat suppressed)"); + } + currentClass = class; + while (currentClass != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + dictionary = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + assert(isNonImmediate(dictionary)); + if ((!((longAt((void *)(dictionary))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + dictionary = fixFollowedFieldofObjectwithInitialValue(MethodDictionaryIndex, currentClass, dictionary); + } + if (dictionary == GIV(nilObj)) { + /* MethodDict pointer is nil (hopefully due a swapped out stub) + -- raise exception #cannotInterpret:. */ + createActualMessageTo(class); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotInterpret) << (shiftForWord())))))); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), /* fetchClassTagOf: */ + ((tagBits = 0) + ? tagBits + : (longAt((void *)(null))) & (classIndexMask()))); + return lookupMethodInClass(superclassOf(currentClass)); + } + + /* begin lookupMethodInDictionary: */ + /* begin numSlotsOf: */ + assert((classIndexOf(dictionary)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(dictionary + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(dictionary - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + mask = (length - SelectorStart) - 1; + + /* Use linear search on small dictionaries; its cheaper. + Also the limit can be set to force linear search of all dictionaries, which supports the + booting of images that need rehashing (e.g. because a tracer has generated an image + with different hashes but hasn't rehashed it yet.) */ + if (mask <= GIV(methodDictLinearSearchLimit)) { + index = 0; + while (index <= mask) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)((index + SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOop = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(index, methodArray, objOop); + } + GIV(newMethod) = objOop; + found = 1; + goto l1; + } + index += 1; + } + found = 0; + goto l1; + } + index = SelectorStart + (mask & ((((GIV(messageSelector) & (tagMask())) != 0) + ? (GIV(messageSelector) >> 3) + : (long32At((void *)(GIV(messageSelector) + 4))) & (identityHashHalfWordMask())))); + + /* It is assumed that there are some nils in this dictionary, and search will + stop when one is encountered. However, if there are no nils, then wrapAround + will be detected the second time the loop gets to the end of the table. */ + wrapAround = 0; + while (1) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (nextSelector == GIV(nilObj)) { + found = 0; + goto l1; + } + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOop = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(index - SelectorStart, methodArray, objOop); + } + GIV(newMethod) = objOop; + found = 1; + goto l1; + } + index += 1; + if (index == length) { + if (wrapAround) { + found = 0; + goto l1; + } + wrapAround = 1; + index = SelectorStart; + } + } + found = 0; + /* end lookupMethodInDictionary: */ +l1: + if (found) { + return currentClass; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOopSqInt = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOopSqInt)); + if ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOopSqInt); + } + currentClass = objOopSqInt; + } + + /* Could not find #doesNotUnderstand: -- unrecoverable error. */ + if (GIV(messageSelector) == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))))) { + error("Recursive not understood error encountered"); + } + + /* Cound not find a normal message -- raise exception #doesNotUnderstand: */ + createActualMessageTo(class); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))); + sendBreakpointclassTag(GIV(messageSelector) + BaseHeaderSize, lengthOf(GIV(messageSelector)), (long32At((void *)(class + 4))) & (identityHashHalfWordMask())); + return lookupMethodInClass(class); +} + + +/* Lookup messageSelector in class. Answer 0 on success. Answer the splObj: + index for the error selector to use on failure rather than performing MNU + processing etc. */ + + /* StackInterpreter>>#lookupMNUInClass: */ +static NoDbgRegParms sqInt +lookupMNUInClass(sqInt class) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentClass; + sqInt dictionary; + sqInt found; + sqInt index; + usqInt length; + sqInt mask; + sqInt methodArray; + sqInt nextSelector; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt wrapAround; + + /* begin lookupBreakFor: */ + if ((breakSelectorLength <= 0) + && (/* shouldBreakForLookupIn:given: */ + (breakLookupClassTag) + && ((class == breakLookupClassTag) + || (((long32At((void *)(class + 4))) & (identityHashHalfWordMask())) == breakLookupClassTag)))) { + warning("lookup class send break (heartbeat suppressed)"); + } + currentClass = class; + while (currentClass != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + dictionary = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + assert(isNonImmediate(dictionary)); + if ((!((longAt((void *)(dictionary))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + dictionary = fixFollowedFieldofObjectwithInitialValue(MethodDictionaryIndex, currentClass, dictionary); + } + if (dictionary == GIV(nilObj)) { + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOop); + } + GIV(lkupClass) = objOop; + return SelectorCannotInterpret; + } + + /* begin lookupMethodInDictionary: */ + /* begin numSlotsOf: */ + assert((classIndexOf(dictionary)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(dictionary + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(dictionary - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + mask = (length - SelectorStart) - 1; + + /* Use linear search on small dictionaries; its cheaper. + Also the limit can be set to force linear search of all dictionaries, which supports the + booting of images that need rehashing (e.g. because a tracer has generated an image + with different hashes but hasn't rehashed it yet.) */ + if (mask <= GIV(methodDictLinearSearchLimit)) { + index = 0; + while (index <= mask) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)((index + SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(index, methodArray, objOopSqInt); + } + GIV(newMethod) = objOopSqInt; + found = 1; + goto l1; + } + index += 1; + } + found = 0; + goto l1; + } + index = SelectorStart + (mask & ((((GIV(messageSelector) & (tagMask())) != 0) + ? (GIV(messageSelector) >> 3) + : (long32At((void *)(GIV(messageSelector) + 4))) & (identityHashHalfWordMask())))); + + /* It is assumed that there are some nils in this dictionary, and search will + stop when one is encountered. However, if there are no nils, then wrapAround + will be detected the second time the loop gets to the end of the table. */ + wrapAround = 0; + while (1) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (nextSelector == GIV(nilObj)) { + found = 0; + goto l1; + } + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(index - SelectorStart, methodArray, objOopSqInt); + } + GIV(newMethod) = objOopSqInt; + found = 1; + goto l1; + } + index += 1; + if (index == length) { + if (wrapAround) { + found = 0; + goto l1; + } + wrapAround = 1; + index = SelectorStart; + } + } + found = 0; + /* end lookupMethodInDictionary: */ +l1: + if (found) { + addNewMethodToCache(class); + return 0; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOopSqInt = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOopSqInt)); + if ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOopSqInt); + } + currentClass = objOopSqInt; + } + GIV(lkupClass) = class; + return SelectorDoesNotUnderstand; +} + + +/* Lookup messageSelector in class. Answer 0 on success. Answer the splObj: + index for the error selector to use on failure rather than performing MNU + processing etc. */ + + /* StackInterpreter>>#lookupOrdinaryNoMNUEtcInClass: */ +static NoDbgRegParms sqInt +lookupOrdinaryNoMNUEtcInClass(sqInt class) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentClass; + sqInt dictionary; + sqInt found; + sqInt index; + usqInt length; + sqInt mask; + sqInt methodArray; + sqInt nextSelector; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt wrapAround; + + /* begin lookupBreakFor: */ + if ((breakSelectorLength <= 0) + && (/* shouldBreakForLookupIn:given: */ + (breakLookupClassTag) + && ((class == breakLookupClassTag) + || (((long32At((void *)(class + 4))) & (identityHashHalfWordMask())) == breakLookupClassTag)))) { + warning("lookup class send break (heartbeat suppressed)"); + } + currentClass = class; + while (currentClass != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + dictionary = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + assert(isNonImmediate(dictionary)); + if ((!((longAt((void *)(dictionary))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + dictionary = fixFollowedFieldofObjectwithInitialValue(MethodDictionaryIndex, currentClass, dictionary); + } + if (dictionary == GIV(nilObj)) { + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOop); + } + GIV(lkupClass) = objOop; + return SelectorCannotInterpret; + } + + /* begin lookupMethodInDictionary: */ + /* begin numSlotsOf: */ + assert((classIndexOf(dictionary)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(dictionary + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(dictionary - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + mask = (length - SelectorStart) - 1; + + /* Use linear search on small dictionaries; its cheaper. + Also the limit can be set to force linear search of all dictionaries, which supports the + booting of images that need rehashing (e.g. because a tracer has generated an image + with different hashes but hasn't rehashed it yet.) */ + if (mask <= GIV(methodDictLinearSearchLimit)) { + index = 0; + while (index <= mask) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)((index + SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(index, methodArray, objOopSqInt); + } + GIV(newMethod) = objOopSqInt; + found = 1; + goto l1; + } + index += 1; + } + found = 0; + goto l1; + } + index = SelectorStart + (mask & ((((GIV(messageSelector) & (tagMask())) != 0) + ? (GIV(messageSelector) >> 3) + : (long32At((void *)(GIV(messageSelector) + 4))) & (identityHashHalfWordMask())))); + + /* It is assumed that there are some nils in this dictionary, and search will + stop when one is encountered. However, if there are no nils, then wrapAround + will be detected the second time the loop gets to the end of the table. */ + wrapAround = 0; + while (1) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (nextSelector == GIV(nilObj)) { + found = 0; + goto l1; + } + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(index - SelectorStart, methodArray, objOopSqInt); + } + GIV(newMethod) = objOopSqInt; + found = 1; + goto l1; + } + index += 1; + if (index == length) { + if (wrapAround) { + found = 0; + goto l1; + } + wrapAround = 1; + index = SelectorStart; + } + } + found = 0; + /* end lookupMethodInDictionary: */ +l1: + if (found) { + addNewMethodToCache(class); + return 0; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOopSqInt = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOopSqInt)); + if ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOopSqInt); + } + currentClass = objOopSqInt; + } + GIV(lkupClass) = class; + return SelectorDoesNotUnderstand; +} + + +/* Lookup selector in class. Answer the method or nil. This is a debugging + routine. It does /not/ side-effect lookupClass or newMethod. */ + + /* StackInterpreter>>#lookupSelector:inClass: */ +sqInt +lookupSelectorinClass(sqInt selector, sqInt class) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentClass; + sqInt dictionary; + usqInt index; + usqInt length; + sqInt mask; + sqInt meth; + sqInt methodArray; + sqInt nextSelector; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt wrapAround; + + currentClass = class; + while (currentClass != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + dictionary = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + assert(isNonImmediate(dictionary)); + if ((!((longAt((void *)(dictionary))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + dictionary = fixFollowedFieldofObjectwithInitialValue(MethodDictionaryIndex, currentClass, dictionary); + } + if (dictionary == GIV(nilObj)) { + return null; + } + + /* begin lookupMethodFor:InDictionary: */ + /* begin numSlotsOf: */ + assert((classIndexOf(dictionary)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(dictionary + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(dictionary - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + mask = (length - SelectorStart) - 1; + index = SelectorStart + (mask & ((((selector & (tagMask())) != 0) + ? (selector >> 3) + : (long32At((void *)(selector + 4))) & (identityHashHalfWordMask())))); + + /* It is assumed that there are some nils in this dictionary, and search will + stop when one is encountered. However, if there are no nils, then wrapAround + will be detected the second time the loop gets to the end of the table. */ + wrapAround = 0; + while (1) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((index << (shiftForWord()))))); + if (nextSelector == GIV(nilObj)) { + meth = null; + goto l1; + } + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == selector) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(index - SelectorStart, methodArray, objOopSqInt); + } + meth = objOopSqInt; + goto l1; + } + index += 1; + if (index == length) { + if (wrapAround) { + meth = null; + goto l1; + } + wrapAround = 1; + index = SelectorStart; + } + } + meth = null; + /* end lookupMethodFor:InDictionary: */ +l1: + if (meth) { + return meth; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOop); + } + currentClass = objOop; + } + return null; +} + + +/* Convert married contexts to widowed contexts, and in the CoInterpreter, + map any machine code pcs to bytecode pcs, and then nil all slots beyond + top of stack, so that the context can be resumed on any VM. */ + + /* StackInterpreter>>#makeContextSnapshotSafe: */ +static NoDbgRegParms void +makeContextSnapshotSafe(sqInt ctxt) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + usqInt numSlots; + sqInt toDoLimit; + sqInt toDoStart; + + assert(isContext(ctxt)); + + /* The stack pages have already been discarded. Any remaining married contexts are actually widows. */ + if (((((longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin markContextAsDead: */ + assert(isContext(ctxt)); + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs(SenderIndex, ctxt, GIV(nilObj))); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, ctxt, GIV(nilObj))); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + ensureContextHasBytecodePC(ctxt); + } + + /* Fill slots beyond top of stack with nil */ + toDoStart = (fetchStackPointerOf(ctxt)) + CtxtTempFrameStart; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(ctxt)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(ctxt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(ctxt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = toDoStart; i <= toDoLimit; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs(i, ctxt, GIV(nilObj))); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } +} + + +/* make a Point xValue@yValue. + We know both will be integers so no value nor root checking is needed */ + + /* StackInterpreter>>#makePointwithxValue:yValue: */ +usqInt +makePointwithxValueyValue(sqInt xValue, sqInt yValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newObj; + usqInt numBytes; + usqInt pointResult; + + /* begin eeInstantiatePoint */ + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert(((YIndex + 1) >= 0) + && ((knownClassAtIndex(ClassPointCompactIndex)) != GIV(nilObj))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(knownClassAtIndex(ClassPointCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert((YIndex + 1) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (((YIndex + 1) < 1 + ? 8 /* allocationUnit */ + : (YIndex + 1) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + pointResult = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) (YIndex + 1))) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + ClassPointCompactIndex); + GIV(freeStart) += numBytes; + pointResult = newObj; + /* end eeInstantiatePoint */ +l1: + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pointResult)) + && (!(isForwarded(pointResult)))); + assert(validStorePointerUncheckedArgs(XIndex, pointResult, (((usqInt)xValue << 3) | 1))); + longAtput((void *)((pointResult + BaseHeaderSize) + ((((usqInt)(XIndex) << (shiftForWord()))))),(((usqInt)xValue << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pointResult)) + && (!(isForwarded(pointResult)))); + assert(validStorePointerUncheckedArgs(YIndex, pointResult, (((usqInt)yValue << 3) | 1))); + longAtput((void *)((pointResult + BaseHeaderSize) + ((((usqInt)(YIndex) << (shiftForWord()))))),(((usqInt)yValue << 3) | 1)); + return pointResult; +} + + +/* The prim trace log is a circular buffer of objects. If there is + an entry at primTraceLogIndex - 1 \\ PrimTraceBufferSize it has entries. + If there is something at primTraceLogIndex it has wrapped. */ + + /* StackInterpreter>>#mapPrimTraceLog */ +static void +mapPrimTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt entryOop; + sqInt i; + sqInt remainder; + + if (!(GIV(primTraceLog)[/* safe:mod: */ + (((remainder = (GIV(primTraceLogIndex) - 1) % PrimTraceLogSize)) < 0 + ? remainder + PrimTraceLogSize + : remainder)])) { + return; + } + if (GIV(primTraceLog)[GIV(primTraceLogIndex)]) { + for (i = GIV(primTraceLogIndex); i < PrimTraceLogSize; i += 1) { + entryOop = GIV(primTraceLog)[i]; + if ((entryOop != 0) + && (/* shouldRemapOop: */ + ((!(entryOop & (tagMask())))) + && (shouldRemapObj(entryOop)))) { + GIV(primTraceLog)[i] = (remapObj(entryOop)); + } + } + } + for (i = 0; i < GIV(primTraceLogIndex); i += 1) { + entryOop = GIV(primTraceLog)[i]; + if ((entryOop != 0) + && (/* shouldRemapOop: */ + ((!(entryOop & (tagMask())))) + && (shouldRemapObj(entryOop)))) { + GIV(primTraceLog)[i] = (remapObj(entryOop)); + } + } +} + + +/* The trace log is a circular buffer of pairs of entries. If there is + an entry at traceLogIndex - 3 \\ TraceBufferSize it has entries. + If there is something at traceLogIndex it has wrapped. */ + + /* StackInterpreter>>#mapTraceLog */ +static void +mapTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt limit; + sqInt oop; + sqInt remainder; + sqInt traceType; + + oop = 0; + limit = /* safe:mod: */ + (((remainder = (GIV(traceLogIndex) - 3) % TraceBufferSize)) < 0 + ? remainder + TraceBufferSize + : remainder); + if (!(GIV(traceLog)[limit])) { + return; + } + if (GIV(traceLog)[GIV(traceLogIndex)]) { + limit = TraceBufferSize - 3; + } + for (i = 0; i <= limit; i += 3) { + traceType = ((usqInt)((GIV(traceLog)[i]))) >> TraceTypeShift; + if ((/* thingIsObjectForTraceType: */ + (traceType <= TraceBlockCreation) + || (((traceType >= TraceOwnVM) && (traceType <= TraceDisownVM)))) + && ((((oop = GIV(traceLog)[i + 1])) != 0) + && (/* shouldRemapOop: */ + ((!(oop & (tagMask())))) + && (shouldRemapObj(oop))))) { + GIV(traceLog)[i + 1] = (remapObj(oop)); + } + if ((/* extraIsObjectForTraceType: */ + (traceType == TraceSend) + || (traceType == TraceContextSwitch)) + && ((((oop = GIV(traceLog)[i + 2])) != 0) + && (/* shouldRemapOop: */ + ((!(oop & (tagMask())))) + && (shouldRemapObj(oop))))) { + GIV(traceLog)[i + 2] = (remapObj(oop)); + } + } +} + + +/* The prim trace log is a circular buffer of objects. If there is + an entry at primTraceLogIndex - 1 \\ PrimTraceBufferSize it has entries. + If there is something at primTraceLogIndex it has wrapped. */ + + /* StackInterpreter>>#markAndTracePrimTraceLog */ +static void +markAndTracePrimTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt entryOop; + sqInt i; + sqInt remainder; + + if (!(GIV(primTraceLog)[/* safe:mod: */ + (((remainder = (GIV(primTraceLogIndex) - 1) % PrimTraceLogSize)) < 0 + ? remainder + PrimTraceLogSize + : remainder)])) { + return; + } + if (GIV(primTraceLog)[GIV(primTraceLogIndex)]) { + for (i = GIV(primTraceLogIndex); i < PrimTraceLogSize; i += 1) { + entryOop = GIV(primTraceLog)[i]; + if ((entryOop != 0) + && ((!(entryOop & (tagMask()))))) { + markAndTrace(entryOop); + } + } + } + for (i = 0; i < GIV(primTraceLogIndex); i += 1) { + entryOop = GIV(primTraceLog)[i]; + if ((entryOop != 0) + && ((!(entryOop & (tagMask()))))) { + markAndTrace(entryOop); + } + } +} + + +/* GC of pages. Throwing away all stack pages on full GC is simple but + dangerous because it causes us to allocate lots of contexts immediately + before a GC. + Reclaiming pages whose top context is not referenced is poor because it + would take N incrementalGCs to reclaim N unused pages. Only the page whose + top context is not referred to by the bottom context of any other page + would be + reclaimed. Not until the next GC would the page whose top contect is the + previously reclaimed page's base frame's bottom context be reclaimed. + + Better is to not mark stack pages until their contexts are encountered. We + can eagerly trace the active page and the page reachable from its bottom + context if any, and so on. Other pages can be marked when we encounter a + married context. */ + + /* StackInterpreter>>#markAndTraceStackPages: */ +static NoDbgRegParms sqInt +markAndTraceStackPages(sqInt fullGCFlag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt context; + sqInt i; + void *pointer; + sqInt senderOop; + char *theFP; + StackPage *thePage; + StackPage *thePage1; + + + /* On an incremental GC simply consider all non-free stack pages to be roots. */ + if (!fullGCFlag) { + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + markAndTraceStackPage(thePage); + } + } + return null; + } + + /* On a full GC only eagerly trace pages referenced from + the base of the active page, i.e. on the active stack. */ + if (!GIV(stackPage)) { + return null; + } + thePage = GIV(stackPage); + do { + markAndTraceStackPage(thePage); + theFP = (thePage->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + context = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(context)); + assert((context == (nilObject())) + || (isContext(followMaybeForwarded(context)))); + if ((/* isContext: */ + ((!(context & (tagMask())))) + && (((longAt((void *)(context))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && ((((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(context)))))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + pointer = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(pointer), GIV(pages)); + assert(!(isFree(thePage))); + } + } while(((thePage->trace)) < StackPageTraced); + return 0; +} + + +/* The trace log is a circular buffer of pairs of entries. If there is an + entry at + traceLogIndex - 3 \\ TraceBufferSize it has entries. If there is something + at traceLogIndex it has wrapped. */ + + /* StackInterpreter>>#markAndTraceTraceLog */ +static void +markAndTraceTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt limit; + sqInt oop; + sqInt remainder; + sqInt traceType; + + limit = /* safe:mod: */ + (((remainder = (GIV(traceLogIndex) - 3) % TraceBufferSize)) < 0 + ? remainder + TraceBufferSize + : remainder); + if (!(GIV(traceLog)[limit])) { + return; + } + if (GIV(traceLog)[GIV(traceLogIndex)]) { + limit = TraceBufferSize - 3; + } + for (i = 0; i <= limit; i += 3) { + traceType = ((usqInt)((GIV(traceLog)[i]))) >> TraceTypeShift; + if (/* thingIsObjectForTraceType: */ + (traceType <= TraceBlockCreation) + || (((traceType >= TraceOwnVM) && (traceType <= TraceDisownVM)))) { + oop = GIV(traceLog)[i + 1]; + if ((oop != 0) + && ((!(oop & (tagMask()))))) { + markAndTrace(oop); + } + } + if (/* extraIsObjectForTraceType: */ + (traceType == TraceSend) + || (traceType == TraceContextSwitch)) { + oop = GIV(traceLog)[i + 2]; + if ((oop != 0) + && ((!(oop & (tagMask()))))) { + markAndTrace(oop); + } + } + } +} + + +/* Trace any untraced pages */ + + /* StackInterpreter>>#markAndTraceUntracedReachableStackPages */ +static void +markAndTraceUntracedReachableStackPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt foundToBeTracedPage; + sqInt i; + StackPage *thePage; + + do { + foundToBeTracedPage = 0; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((!(isFree(thePage))) + && (((thePage->trace)) == StackPageReachedButUntraced)) { + foundToBeTracedPage = 1; + markAndTraceStackPage(thePage); + } + } + } while(foundToBeTracedPage); +} + + +/* This is a helper for primitiveObjectPointsTo so it *does not* check the + frameContext field because that is an implicit self-reference not present + in the state . + */ + + /* StackInterpreter>>#marriedContext:pointsTo:stackDeltaForCurrentFrame: */ +static NoDbgRegParms sqInt +marriedContextpointsTostackDeltaForCurrentFrame(sqInt spouseContext, sqInt anOop, sqInt stackDeltaForCurrentFrame) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *rcvrOffset; + sqInt senderOop; + char *theFP; + StackPage *thePage; + char *theSP; + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((spouseContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (theFP == GIV(framePointer)) { + theSP = GIV(stackPointer) + (stackDeltaForCurrentFrame * BytesPerWord); + } + else { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + theSP = findSPOfon(theFP, thePage); + } + if ((((anOop) & 7) == 1)) { + if ((anOop == ((((usqInt)(/* stackPointerIndexForFrame:WithSP: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (((usqInt)(((theFP + FoxMFReceiver) - theSP))) >> (shiftForWord())) + (((mframeCogMethod(theFP))->cmNumArgs)) + : (((usqInt)(((theFP + FoxIFReceiver) - theSP))) >> (shiftForWord())) + (byteAt((theFP + FoxIFrameFlags) + 1)))) << 3) | 1))) + || (anOop == (externalInstVarofContext(InstructionPointerIndex, spouseContext)))) { + return 1; + } + } + else { + if (anOop == (/* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)))) { + return 1; + } + if (longAt(theFP + FoxSavedFP)) { + if ((/* frameHasContext: */ + ((((usqInt)(longAt((((char *)(longAt(theFP + FoxSavedFP)))) + FoxMethod)))) < (startOfMemory()) + ? ((longAt((((char *)(longAt(theFP + FoxSavedFP)))) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt(((((char *)(longAt(theFP + FoxSavedFP)))) + FoxIFrameFlags) + 2)) != 0)) + && (anOop == (longAt((((char *)(longAt(theFP + FoxSavedFP)))) + FoxThisContext)))) { + return 1; + } + } + else { + if (anOop == (frameCallerContext(theFP))) { + return 1; + } + } + } + + /* Check stack and instruction pointer fields. + Check method and sender fields, avoiding unnecessarily reifying sender context. + Now check receiver, temps and stack contents */ + rcvrOffset = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + while (theSP <= rcvrOffset) { + if (anOop == (longAt(theSP))) { + return 1; + } + theSP += BytesPerWord; + } + + /* Finally check stacked receiver (closure field or duplicate of receiver) and arguments */ + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + rcvrOffset = theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord()))))); + while (theSP <= rcvrOffset) { + if (anOop == (longAt(theSP))) { + return 1; + } + theSP += BytesPerWord; + } + return 0; +} + + +/* Establish aContext at the base of a new stackPage, make the stackPage the + active one and set-up the interreter registers. This is used to boot the + system and bring it back after a snapshot. */ + + /* StackInterpreter>>#marryContextInNewStackPageAndInitializeInterpreterRegisters: */ +static NoDbgRegParms void +marryContextInNewStackPageAndInitializeInterpreterRegisters(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + sqInt header; + sqInt methodHeader; + StackPage *newPage; + sqInt top; + + assert(!GIV(stackPage)); + newPage = makeBaseFrameFor(aContext); + + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (newPage->headSP); + GIV(framePointer) = (newPage->headFP); + aMethodObj = longAt(((GIV(stackPage)->headFP)) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; +} + + +/* Assert-only check for leaks after external prim calls if checkForLeaks + includes the GCCheckPrimCall flag. + This is ionly really useful from a low-level C debugger, hence no + accessors for checkedPluginName. + */ + + /* StackInterpreter>>#maybeLeakCheckExternalPrimCall: */ +static NoDbgRegParms sqInt +maybeLeakCheckExternalPrimCall(sqInt aMethodObj) +{ + if ((((checkForLeaks & GCCheckPrimCall) != 0)) + && (((primitiveIndexOf(aMethodObj)) == PrimNumberExternalCall) + && (checkedPluginName == (fetchPointerofObject(0, longAt((void *)((aMethodObj + BaseHeaderSize) + (1U << (shiftForWord()))))))))) { + runLeakCheckerFor(GCCheckPrimCall); + } + return 1; +} + + +/* Answer the selector of a method, assuming its penultimate literal is + either a symbol or a pointer object whose first slot references the method + and whose second slot is a symbol (i.e. an AdditionalMethodState). If a + Symbol can't be found answer nil. This isn't satisfactory, as it puts a + lot of information + into the VM, but it is needed for adequate crash debugging at Cadence. + With full blocks as of 9/2016 the last literal of a CompiledBlock is a + back pointer + to the enclosing block or compiled method. */ + + /* StackInterpreter>>#maybeSelectorOfMethod: */ +sqInt +maybeSelectorOfMethod(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt maybeSelector; + usqInt numSlots; + sqInt penultimateLiteral; + sqInt ultimateLiteral; + + assert(isOopCompiledMethod(methodObj)); + ultimateLiteral = ultimateLiteralOf(methodObj); + if (/* isOopCompiledMethod: */ + ((!(ultimateLiteral & (tagMask())))) + && (((byteAt((void *)(ultimateLiteral + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) { + return maybeSelectorOfMethod(ultimateLiteral); + } + penultimateLiteral = penultimateLiteralOf(methodObj); + if (/* isWordsOrBytes: */ + ((!(penultimateLiteral & (tagMask())))) + && (isWordsOrBytesNonImm(penultimateLiteral))) { + return (((longAt((void *)(penultimateLiteral))) & (classIndexMask())) == (fetchClassTagOfNonImm(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))))) + ? penultimateLiteral + : 0); + } + return ((/* isPointers: */ + ((!(penultimateLiteral & (tagMask())))) + && (((byteAt((void *)(penultimateLiteral + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(penultimateLiteral)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(penultimateLiteral + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(penultimateLiteral - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= 2) + && (((longAt((void *)((penultimateLiteral + BaseHeaderSize) + (0U << (shiftForWord()))))) == methodObj) + && (((maybeSelector = longAt((void *)((penultimateLiteral + BaseHeaderSize) + (1U << (shiftForWord()))))), + (/* isWordsOrBytes: */ + ((!(maybeSelector & (tagMask())))) + && (isWordsOrBytesNonImm(maybeSelector))) + && (((longAt((void *)(maybeSelector))) & (classIndexMask())) == (fetchClassTagOfNonImm(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord()))))))))))))) + ? maybeSelector + : 0); +} + + /* StackInterpreter>>#methodArgumentCount */ +sqInt +methodArgumentCount(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(argumentCount); +} + + /* StackInterpreter>>#methodClassAssociationOf: */ +sqInt +methodClassAssociationOf(sqInt methodPointer) +{ + sqInt offset; + + offset = (literalCountOf(methodPointer)) - 1; + + /* begin literal:ofMethod: */ + return longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); +} + + +/* Answer the method class of a method which is the value of an Association + in the last literal, + or answer nil if there isn't one. + Using a read barrier here simplifies the become implementation and costs + very little + because the class index and ValueIndex of the association almost certainly + share a cache line. */ + + /* StackInterpreter>>#methodClassOf: */ +sqInt +methodClassOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt literal; + sqInt offset; + + offset = (literalCountOf(methodPointer)) - 1; + + /* begin followLiteral:ofMethod: */ + /* begin followField:ofObject: */ + literal = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = fixFollowedFieldofObjectwithInitialValue(offset + LiteralStart, methodPointer, literal); + } + return ((literal != GIV(nilObj)) + && (/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + ? (assert((numSlotsOf(literal)) > ValueIndex), + followFieldofObject(ValueIndex, literal)) + : GIV(nilObj)); +} + + /* StackInterpreter>>#methodPrimitiveIndex */ +sqInt +methodPrimitiveIndex(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + + if (!((addressCouldBeObj(GIV(newMethod))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return -1; + } + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin primitiveIndexOfMethod:header: */ + return (((methodHeader & AlternateHeaderHasPrimFlag) != 0) + ? ((firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize), + (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8)))) + : 0); +} + + +/* Sets the return value for a method. In the CoInterpreter we replace the + cumbersome primResult machinery. */ + + /* StackInterpreter>>#methodReturnBool: */ +sqInt +methodReturnBool(sqInt boolean) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + assert(!((failed()))); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (boolean + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return 0; +} + + +/* Sets the return value for a method. */ + + /* StackInterpreter>>#methodReturnFloat: */ +sqInt +methodReturnFloat(double aFloat) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + assert(!((failed()))); + + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),floatObjectOf(aFloat)); + GIV(stackPointer) = sp; + return 0; +} + + +/* Sets the return value for a method. In the CoInterpreter we replace the + cumbersome primResult machinery. */ + + /* StackInterpreter>>#methodReturnInteger: */ +sqInt +methodReturnInteger(sqInt integer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + assert(!((failed()))); + + /* begin pop:thenPushInteger: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),(((usqInt)integer << 3) | 1)); + GIV(stackPointer) = sp; + return 0; +} + + +/* Sets the return value for a method */ + + /* StackInterpreter>>#methodReturnReceiver */ +sqInt +methodReturnReceiver(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(!((failed()))); + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return 0; +} + + +/* Attempt to answer a ByteString for a given C string as the result of a + primitive. + */ + + /* StackInterpreter>>#methodReturnString: */ +NeverInline sqInt +methodReturnString(const char *aCString) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt result; + + assert(!((failed()))); + if (aCString) { + if ((result = stringForCString(aCString))) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); + } + else { + GIV(primFailCode) = PrimErrNoMemory; + } + } + else { + GIV(primFailCode) = PrimErrOperationFailed; + } + return 0; +} + + +/* Sets the return value for a method. In the CoInterpreter we replace the + cumbersome primResult machinery. */ + + /* StackInterpreter>>#methodReturnValue: */ +sqInt +methodReturnValue(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + assert(!((failed()))); + + /* begin pop:thenPush: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),oop); + GIV(stackPointer) = sp; + return 0; +} + + +/* A negative header selects the alternate bytecode set. */ + + /* StackInterpreter>>#methodUsesAlternateBytecodeSet: */ +int +methodUsesAlternateBytecodeSet(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + sqInt methodHeader; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin headerIndicatesAlternateBytecodeSet: */ + return (((sqLong) methodHeader)) < 0; +} + + +/* Answer if aMethodObj contains a primitive and uses the primitive error + code. + */ + + /* StackInterpreter>>#methodUsesPrimitiveErrorCode: */ +static NoDbgRegParms sqInt +methodUsesPrimitiveErrorCode(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + return ((/* primitiveIndexOfMethod:header: */ + (((methodHeader & AlternateHeaderHasPrimFlag) != 0) + ? ((firstBytecode = (aMethodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize), + (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8)))) + : 0)) > 0) + && ((/* longStoreBytecodeForHeader: */ + ((((sqLong) methodHeader)) < 0 + ? AltLongStoreBytecode + : LongStoreBytecode)) == (fetchByteofObject(((((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop) + 3 /* sizeOfCallPrimitiveBytecode: */, aMethodObj))); +} + + +/* The module with the given name was just unloaded. + Make sure we have no dangling references. */ + + /* StackInterpreter>>#moduleUnloaded: */ +EXPORT(void) +moduleUnloaded(char *aModuleName) +{ + if (!(strcmp(aModuleName, "SurfacePlugin"))) { + /* Surface plugin went away. Should never happen. But then, who knows */ + showSurfaceFn = 0; + } +} + + +/* Brain-damaged nameOfClass: for C VM. Does *not* answer Foo class for + metaclasses. Use e.g. classIsMeta: to avoid being fooled. */ + + /* StackInterpreter>>#nameOfClass: */ +static NoDbgRegParms char * +nameOfClass(sqInt classOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt maybeNameOop; + sqInt maybeThisClassOop; + usqInt numSlots; + usqInt numSlotsUsqInt; + + /* begin numSlotsOf: */ + assert((classIndexOf(classOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(classOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (numSlots == GIV(metaclassNumSlots)) { + maybeThisClassOop = longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord())))))); + if (addressCouldBeClassObj(maybeThisClassOop)) { + return nameOfClass(longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord()))))))); + } + return "bad class"; + } + if (!((numSlots > GIV(classNameIndex)) + && (((maybeNameOop = longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(classNameIndex)) << (shiftForWord()))))))), + /* isBytes: */ + ((!(maybeNameOop & (tagMask())))) + && (((byteAt((void *)(maybeNameOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))))) { + return "bad class"; + } + return firstIndexableField(maybeNameOop); +} + + +/* Brain-damaged nameOfClass: for C VM. Does *not* answer Foo class for + metaclasses. Use e.g. classIsMeta: to avoid being fooled. */ + + /* StackInterpreter>>#nameOfClass:lengthInto: */ +static NoDbgRegParms char * +nameOfClasslengthInto(sqInt classOop, sqInt *lengthPtr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt maybeNameOop; + sqInt maybeThisClassOop; + usqInt numSlots; + usqInt numSlotsUsqInt; + + /* begin numSlotsOf: */ + assert((classIndexOf(classOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(classOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (numSlots == GIV(metaclassNumSlots)) { + maybeThisClassOop = longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord())))))); + if (addressCouldBeClassObj(maybeThisClassOop)) { + return nameOfClasslengthInto(longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord())))))), lengthPtr); + } + lengthPtr[0] = 9; + return "bad class"; + } + if ((numSlots > GIV(classNameIndex)) + && (((maybeNameOop = longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(classNameIndex)) << (shiftForWord()))))))), + /* isBytes: */ + ((!(maybeNameOop & (tagMask())))) + && (((byteAt((void *)(maybeNameOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))))) { + lengthPtr[0] = (numBytesOfBytes(maybeNameOop)); + return firstIndexableField(maybeNameOop); + } + lengthPtr[0] = 9; + return "bad class"; +} + + +/* Answer true if there are no marked contexts on thePage. */ + + /* StackInterpreter>>#noMarkedContextsOnPage: */ +static NoDbgRegParms sqInt +noMarkedContextsOnPage(StackPage *thePage) +{ + char *theFP; + + assert(!(isFree(thePage))); + theFP = (thePage->headFP); + do { + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + if ((byteAt((void *)((longAt(theFP + FoxThisContext)) + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + return 0; + } + } + } while(((theFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0); + return 1; +} + + +/* Answer the number of effective pointer fields in the given context. */ + + /* StackInterpreter>>#numSlotsOfMarriedContext: */ +static NoDbgRegParms sqInt +numSlotsOfMarriedContext(sqInt aContext) +{ + sqInt contextSize; + + contextSize = stackPointerIndexForFrame(frameOfMarriedContext(aContext)); + assert((CtxtTempFrameStart + contextSize) <= (numSlotsOf(aContext))); + return CtxtTempFrameStart + contextSize; +} + + /* StackInterpreter>>#numStkPages */ +static sqInt +numStkPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(numStackPages); +} + + /* StackInterpreter>>#object:equalsString: */ +static NoDbgRegParms sqInt +objectequalsString(sqInt anOop, char *aCString) +{ + sqInt len; + + len = strlen(aCString); + return /* object:equalsString:ofSize: */ + (/* isBytes: */ + ((!(anOop & (tagMask())))) + && (((byteAt((void *)(anOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((!(((byteAt((void *)(anOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((numBytesOfBytes(anOop)) == len) + && ((strncmp(aCString, firstIndexableField(anOop), len)) == 0))); +} + + /* StackInterpreter>>#pcPreviousTo:inSqueakV3PlusClosuresOrSistaV1Method: */ +usqInt +pcPreviousToinSqueakV3PlusClosuresOrSistaV1Method(sqInt theIP, sqInt aMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt byte; + static unsigned char extensionSizes[] = { + 2, 2, 2, 2, 3, 2, 2, 1, 1, 1, 2, 3, 3, 3, 3, 4 + }; + sqInt header; + sqInt methodHeader; + usqInt previousPC; + usqInt thisIP; + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethod)); + header = longAt((void *)((aMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + /* begin pcPreviousTo:inSistaV1Method: */ + thisIP = ((((literalCountOf(aMethod)) + LiteralStart) * BytesPerOop) + aMethod) + BaseHeaderSize; + assert(oopisLessThan(thisIP, theIP)); + while (oopisLessThan(thisIP, theIP)) { + previousPC = thisIP; + do { + byte = byteAt((void *)(thisIP)); + thisIP += /* sizeOfSistaV1Bytecode: */ + (byte < 224 + ? 1 + : (byte < 0xF8 + ? 2 + : 3)); + } while(((byte >= 224) && (byte <= 225))); + } + return previousPC; + } + + /* begin pcPreviousTo:inSqueakV3PlusClosuresMethod: */ + thisIP = ((((literalCountOf(aMethod)) + LiteralStart) * BytesPerOop) + aMethod) + BaseHeaderSize; + assert(oopisLessThan(thisIP, theIP)); + while (oopisLessThan(thisIP, theIP)) { + previousPC = thisIP; + byte = byteAt((void *)(thisIP)); + thisIP += (byte <= 125 + ? 1 + : (byte >= 176 + ? 1 + : (byte >= 160 + ? 2 + : (byte >= 144 + ? 1 + : (byte >= 128 + ? extensionSizes[byte - 128] + : (assertf("illegal bytecode in sizeOfSqueakV3Bytecode:"), + 1)))))); + } + return previousPC; +} + + /* StackInterpreter>>#penultimateLiteralOf: */ +static NoDbgRegParms sqInt +penultimateLiteralOf(sqInt aMethodOop) +{ + sqInt offset; + + assert(isOopCompiledMethod(aMethodOop)); + offset = (literalCountOf(aMethodOop)) - 2; + + /* begin literal:ofMethod: */ + return longAt((void *)((aMethodOop + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#popStack */ +sqInt +popStack(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt top; + + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + return top; +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#pop: */ +sqInt +pop(sqInt nItems) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(stackPointer) += nItems * BytesPerWord; + return null; +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#pop:thenPush: */ +void +popthenPush(sqInt nItems, sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + longAtput((sp = GIV(stackPointer) + ((nItems - 1) * BytesPerWord)),oop); + GIV(stackPointer) = sp; +} + + +/* Update the displayBits after a GC may have moved it. + Answer if the displayBits appear valid. The wrinkle here is that the + displayBits could be a surface handle. */ + + /* StackInterpreter>>#postGCUpdateDisplayBits */ +static sqInt +postGCUpdateDisplayBits(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void *bitsNow; + sqInt bitsOop; + sqInt displayObj; + + displayObj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheDisplay) << (shiftForWord())))))); + if (!((/* isPointers: */ + ((!(displayObj & (tagMask())))) + && (((byteAt((void *)(displayObj + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((lengthOf(displayObj)) >= 4))) { + return 0; + } + + /* begin followOopField:ofObject: */ + bitsOop = longAt((void *)((displayObj + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isOopForwarded: */ + ((!(bitsOop & (tagMask())))) + && ((!((longAt((void *)(bitsOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + bitsOop = fixFollowedFieldofObjectwithInitialValue(0, displayObj, bitsOop); + } + if ((bitsOop == GIV(nilObj)) + || ((((bitsOop) & 7) == 1))) { + return 1; + } + + /* it ain't yet set + It's a surface; our work here is done... */ + assert((addressCouldBeObj(bitsOop)) + && (isWordsOrBytes(bitsOop))); + if (!((byteAt((void *)(bitsOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift())))) { + if (GIV(stackPage)) { + pinObject(bitsOop); + + /* begin followOopField:ofObject: */ + bitsOop = longAt((void *)((displayObj + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isOopForwarded: */ + ((!(bitsOop & (tagMask())))) + && ((!((longAt((void *)(bitsOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + bitsOop = fixFollowedFieldofObjectwithInitialValue(0, displayObj, bitsOop); + } + } + + /* If stackPage is nil we're snapshotting and now is not the time to pin. */ + bitsNow = firstIndexableField(bitsOop); + if (displayBits != bitsNow) { + displayBits = bitsNow; + ioNoteDisplayChangedwidthheightdepth(displayBits, displayWidth, displayHeight, displayDepth); + } + } + return 1; +} + + +/* With 0 args answers whether ioProcessEvents is enabled and being called. + With 1 arg expects a boolean which will enable ioProcessEvents if true and + disable it if false, answering its previous state. */ + + /* StackInterpreter>>#primitiveEventProcessingControl */ +EXPORT(void) +primitiveEventProcessingControl(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int enabled; + char *sp; + + enabled = inIOProcessEvents >= 0; + if (!GIV(argumentCount)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer)),/* booleanObjectOf: */ + (enabled + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return; + } + if (GIV(argumentCount) == 1) { + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + if (inIOProcessEvents < 0) { + inIOProcessEvents = 0; + } + } + else { + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + inIOProcessEvents = -1; + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (enabled + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; +} + + /* StackInterpreter>>#primitiveFloatEqual:toArg: */ +static NoDbgRegParms int +primitiveFloatEqualtoArg(sqInt rcvrOop, sqInt argOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + double rcvr; + double result; + sqInt tagBits; + double value; + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l2: + if (rcvr == arg) { + if ((((argOop) & 7) == 1)) { + return (((sqInt)rcvr)) == ((argOop >> 3)); + } + + /* Resolve case of ambiguity so as to have comparison of exact values */ + if ((((rcvrOop) & 7) == 1)) { + /* Same when used from bytecodePrim... + note that rcvr and arg cannot be both integer (case is already handled) */ + return ((rcvrOop >> 3)) == (((sqInt)arg)); + } + } + return rcvr == arg; +} + + /* StackInterpreter>>#primitiveFloatGreaterOrEqual:toArg: */ +static NoDbgRegParms int +primitiveFloatGreaterOrEqualtoArg(sqInt rcvrOop, sqInt argOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + double rcvr; + double result; + sqInt tagBits; + double value; + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l2: + if (rcvr == arg) { + if ((((argOop) & 7) == 1)) { + return (((sqInt)rcvr)) >= ((argOop >> 3)); + } + + /* Resolve case of ambiguity so as to have comparison of exact values */ + if ((((rcvrOop) & 7) == 1)) { + /* Same when used from bytecodePrim... + note that rcvr and arg cannot be both integer (case is already handled) */ + return ((rcvrOop >> 3)) >= (((sqInt)arg)); + } + } + return rcvr >= arg; +} + + /* StackInterpreter>>#primitiveFloatGreater:thanArg: */ +static NoDbgRegParms int +primitiveFloatGreaterthanArg(sqInt rcvrOop, sqInt argOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + double rcvr; + double result; + sqInt tagBits; + double value; + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l2: + if (rcvr == arg) { + if ((((argOop) & 7) == 1)) { + return (((sqInt)rcvr)) > ((argOop >> 3)); + } + + /* Resolve case of ambiguity so as to have comparison of exact values */ + if ((((rcvrOop) & 7) == 1)) { + /* Same when used from bytecodePrim... + note that rcvr and arg cannot be both integer (case is already handled) */ + return ((rcvrOop >> 3)) > (((sqInt)arg)); + } + } + return rcvr > arg; +} + + /* StackInterpreter>>#primitiveFloatLessOrEqual:toArg: */ +static NoDbgRegParms int +primitiveFloatLessOrEqualtoArg(sqInt rcvrOop, sqInt argOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + double rcvr; + double result; + sqInt tagBits; + double value; + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l2: + if (rcvr == arg) { + if ((((argOop) & 7) == 1)) { + return (((sqInt)rcvr)) <= ((argOop >> 3)); + } + + /* Resolve case of ambiguity so as to have comparison of exact values */ + if ((((rcvrOop) & 7) == 1)) { + /* Same when used from bytecodePrim... + note that rcvr and arg cannot be both integer (case is already handled) */ + return ((rcvrOop >> 3)) <= (((sqInt)arg)); + } + } + return rcvr <= arg; +} + + /* StackInterpreter>>#primitiveFloatLess:thanArg: */ +static NoDbgRegParms int +primitiveFloatLessthanArg(sqInt rcvrOop, sqInt argOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + double rcvr; + double result; + sqInt tagBits; + double value; + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l2: + if (rcvr == arg) { + if ((((argOop) & 7) == 1)) { + return (((sqInt)rcvr)) < ((argOop >> 3)); + } + + /* Resolve case of ambiguity so as to have comparison of exact values */ + if ((((rcvrOop) & 7) == 1)) { + /* Same when used from bytecodePrim... + note that rcvr and arg cannot be both integer (case is already handled) */ + return ((rcvrOop >> 3)) < (((sqInt)arg)); + } + } + return rcvr < arg; +} + + +/* Note: With the Squeak V3 format we now have 10 bits of primitive index, + but they are + in two places for temporary backward compatibility. The time to unpack is + negligible, since the derived primitive function pointer is stored in the + method cache. With the + Spur format we assume a 3-byte CallPrimitive with a little-endian 16-bit + primitive index. */ + + /* StackInterpreter>>#primitiveIndexOfMethod:header: */ +sqInt +primitiveIndexOfMethodheader(sqInt theMethod, sqInt methodHeader) +{ + usqInt firstBytecode; + + return (((methodHeader & AlternateHeaderHasPrimFlag) != 0) + ? ((firstBytecode = (theMethod + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize), + (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8)))) + : 0); +} + + /* StackInterpreter>>#primitiveIndexOf: */ +sqInt +primitiveIndexOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin primitiveIndexOfMethod:header: */ + return (((methodHeader & AlternateHeaderHasPrimFlag) != 0) + ? ((firstBytecode = (methodPointer + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize), + (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8)))) + : 0); +} + + +/* Return the method an external primitive was defined in */ + + /* StackInterpreter>>#primitiveMethod */ +usqInt +primitiveMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(newMethod); +} + + /* StackInterpreter>>#printActivationNameForSelector:startClass: */ +static NoDbgRegParms void +printActivationNameForSelectorstartClass(sqInt aSelector, sqInt startClass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classDict; + usqInt classDictSize; + sqInt currClass; + sqInt i; + sqInt methClass; + usqInt numSlots; + sqInt objOop; + + if (addressCouldBeObj(startClass)) { + /* begin findClassForSelector:lookupClass:do: */ + currClass = startClass; + do { + classDict = longAt((void *)((currClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + + /* begin numSlotsOf: */ + assert((classIndexOf(classDict)) > (isForwardedObjectClassIndexPun())); + classDictSize = (((numSlots = byteAt((void *)(classDict + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classDict - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + i = SelectorStart; + while (i < classDictSize) { + if (aSelector == (longAt((void *)((classDict + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + methClass = currClass; + goto l1; + } + i += 1; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((currClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currClass, objOop); + } + currClass = objOop; + } while(!(currClass == GIV(nilObj))); + methClass = null; + /* end findClassForSelector:lookupClass:do: */ +l1: + if ((!methClass) + || (startClass == methClass)) { + printNameOfClasscount(methClass, 5); + printChar('>'); + if (!methClass) { + printStringOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord()))))))); + print(" "); + } + } + else { + printNameOfClasscount(startClass, 5); + printChar('('); + printNameOfClasscount(methClass, 5); + printChar(')'); + printChar('>'); + } + } + else { + print("INVALID CLASS"); + } + if (/* addressCouldBeOop: */ + (((aSelector & (tagMask())) != 0)) + || (addressCouldBeObj(aSelector))) { + if (/* isBytes: */ + ((!(aSelector & (tagMask())))) + && (((byteAt((void *)(aSelector + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) { + printStringOf(aSelector); + } + else { + /* begin printOopShort: */ + printOopShortInner(aSelector); + fflush(GIV(transcript)); + } + } + else { + print("INVALID SELECTOR"); + } +} + + /* StackInterpreter>>#printActivationNameFor:receiver:isBlock:firstTemporary: */ +static NoDbgRegParms void +printActivationNameForreceiverisBlockfirstTemporary(sqInt aMethod, sqInt anObject, sqInt isBlock, sqInt maybeMessage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classObj; + sqInt homeMethod; + sqInt methClass; + sqInt methodSel; + sqInt tagBits; + + if (isBlock) { + print("[] in "); + homeMethod = homeMethodOf(aMethod); + } + else { + homeMethod = aMethod; + } + methClass = findClassOfMethodforReceiver(homeMethod, anObject); + methodSel = findSelectorOfMethod(homeMethod); + if ((/* addressCouldBeOop: */ + (((anObject & (tagMask())) != 0)) + || (addressCouldBeObj(anObject))) + && ((!(/* isOopForwarded: */ + ((!(anObject & (tagMask())))) + && ((!((longAt((void *)(anObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) + && (addressCouldBeClassObj((classObj = /* fetchClassOf: */ + ((tagBits = anObject & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(anObject))))))) { + printNameOfClasscount(classObj, 5); + if (!((classObj == methClass) + || ((!methClass) + || (methClass == GIV(nilObj))))) { + printChar('('); + printNameOfClasscount(methClass, 5); + printChar(')'); + } + } + else { + print("INVALID RECEIVER"); + } + printChar('>'); + if (/* addressCouldBeOop: */ + (((methodSel & (tagMask())) != 0)) + || (addressCouldBeObj(methodSel))) { + if (methodSel == GIV(nilObj)) { + print("(nil)"); + } + else { + if (((methodSel & (tagMask())) != 0)) { + printChar('('); + printHex(methodSel); + printChar(')'); + } + else { + printStringOf(methodSel); + } + } + } + else { + print("INVALID SELECTOR = "); + printHex(methodSel); + } + if ((methodSel == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))))) + && ((addressCouldBeObj(maybeMessage)) + && ((fetchClassOfNonImm(maybeMessage)) == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassMessage) << (shiftForWord())))))))))) { + /* print arg message selector */ + methodSel = longAt((void *)((maybeMessage + BaseHeaderSize) + ((((usqInt)(MessageSelectorIndex) << (shiftForWord())))))); + + /* begin space */ + printChar(' '); + printStringOf(methodSel); + } +} + + +/* Print all the stacks of all running processes, including those that are + currently suspended. + */ +/* useful for VM debugging */ + + /* StackInterpreter>>#printAllStacksOn: */ +void +printAllStacksOn(FILE *aStdioStream) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + FILE *savedTranscript; + + /* begin withRedirectedOutputTo:do: */ + savedTranscript = GIV(transcript); + GIV(transcript) = (aStdioStream + ? aStdioStream + : stdout); + printAllStacks(); + GIV(transcript) = savedTranscript; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printCallStack */ +void +printCallStack(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *fp; + + if ((fp = GIV(framePointer))) { + printCallStackFP(fp); + } + else { + printCallStackOf(fetchPointerofObject(SuspendedContextIndex, activeProcess())); + } +} + + /* StackInterpreter>>#printCallStackFP: */ +static NoDbgRegParms sqInt +printCallStackFP(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt context; + + context = shortReversePrintFrameAndCallers(theFP); + while (!(context == GIV(nilObj))) { + if (((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (!(checkIsStillMarriedContextcurrentFP(context, GIV(framePointer)))) { + shortPrintContext(context); + return null; + } + context = shortReversePrintFrameAndCallers(frameOfMarriedContext(context)); + } + else { + context = printContextCallStackOf(context); + } + } + return 0; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printCallStackOf: */ +sqInt +printCallStackOf(sqInt aContextOrProcessOrFrame) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt context; + + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)aContextOrProcessOrFrame)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)aContextOrProcessOrFrame)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)aContextOrProcessOrFrame)) <= (((usqInt)GIV(pages)))))))) { + return printCallStackFP(((char *)aContextOrProcessOrFrame)); + } + if (aContextOrProcessOrFrame == (activeProcess())) { + return printCallStackOf(((sqInt)GIV(framePointer))); + } + if (couldBeProcess(aContextOrProcessOrFrame)) { + return printCallStackOf(longAt((void *)((aContextOrProcessOrFrame + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))))); + } + context = aContextOrProcessOrFrame; + while (!(context == GIV(nilObj))) { + if (((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (!(checkIsStillMarriedContextcurrentFP(context, GIV(framePointer)))) { + shortPrintContext(context); + return null; + } + context = shortReversePrintFrameAndCallers(frameOfMarriedContext(context)); + } + else { + context = printContextCallStackOf(context); + } + } + return 0; +} + + /* StackInterpreter>>#printCallStackOf:currentFP: */ +static NoDbgRegParms sqInt +printCallStackOfcurrentFP(sqInt aContext, char *currFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + sqInt senderOop; + char *theFP; + StackPage *thePage; + + ctxt = aContext; + while (!(ctxt == GIV(nilObj))) { + if (((((longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (checkIsStillMarriedContextcurrentFP(ctxt, currFP)) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (isFree(thePage)) { + fprintf(GIV(transcript), + "%p is on a free page?!\n", + theFP); + return null; + } + shortPrintFrameAndCallers(theFP); + theFP = (thePage->baseFP); + ctxt = frameCallerContext(theFP); + if ((!((longAt((void *)(ctxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + ctxt = followForwarded(ctxt); + } + } + else { + fprintf(GIV(transcript), + "widowed caller frame %p\n", + theFP); + return null; + } + } + else { + shortPrintContext(ctxt); + ctxt = longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + } + } + return 0; +} + + +/* Print the call stack on a specific output stream. */ +/* useful for VM debugging */ + + /* StackInterpreter>>#printCallStackOn: */ +void +printCallStackOn(FILE *aStdioStream) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + FILE *savedTranscript; + + /* begin withRedirectedOutputTo:do: */ + savedTranscript = GIV(transcript); + GIV(transcript) = (aStdioStream + ? aStdioStream + : stdout); + printCallStack(); + GIV(transcript) = savedTranscript; +} + + +/* Print the call stack of aContext until it links to a frame. */ + + /* StackInterpreter>>#printContextCallStackOf: */ +static NoDbgRegParms sqInt +printContextCallStackOf(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + + ctxt = aContext; + while (!((ctxt == GIV(nilObj)) + || (((((longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)))) { + shortPrintContext(ctxt); + ctxt = longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + } + return ctxt; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printContext: */ +void +printContext(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt ip; + sqInt meth; + sqInt sender; + sqInt sp; + + shortPrintContext(aContext); + sender = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + ip = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + if ((((sender) & 7) == 1)) { + if (checkIsStillMarriedContextcurrentFP(aContext, GIV(framePointer))) { + print("married (assuming framePointer valid)"); + cr(); + } + else { + print("widowed (assuming framePointer valid)"); + cr(); + } + print("sender "); + printNum(sender); + print(" ("); + printHexPtrnp( + (/* begin withoutSmallIntegerTags: */ + assert((((sender) & 7) == 1)), + ((char *)(sender - (smallIntegerTag()))))); + printChar(')'); + cr(); + print("ip "); + printNum(ip); + print(" ("); + printHexPtrnp( + (/* begin withoutSmallIntegerTags: */ + assert((((ip) & 7) == 1)), + ((char *)(ip - (smallIntegerTag()))))); + printChar(')'); + cr(); + } + else { + print("sender "); + shortPrintOop(sender); + print("ip "); + if (ip == GIV(nilObj)) { + shortPrintOop(ip); + } + else { + printNum(ip); + print(" ("); + printNum((ip >> 3)); + + /* begin space */ + printChar(' '); + printHex((ip >> 3)); + printChar(')'); + cr(); + } + } + sp = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + print("sp "); + printNum((sp >> 3)); + print(" ("); + printHex(sp); + printChar(')'); + cr(); + print("method "); + + /* begin printMethodFieldForPrintContext: */ + meth = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + fprintf(GIV(transcript), + "%p: ", + ((void *)meth)); + printOopShortInner(meth); + if (methodHasCogMethod(meth)) { + fprintf(GIV(transcript), + " (%p)", + cogMethodOf(meth)); + } + cr(); + print("closure "); + shortPrintOop(longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord()))))))); + print("receiver "); + shortPrintOop(longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord()))))))); + sp = (sp >> 3); + sp = ((sp < ((lengthOf(aContext)) - ReceiverIndex)) ? sp : ((lengthOf(aContext)) - ReceiverIndex)); + for (i = 1; i <= sp; i += 1) { + print(" "); + printNum(i); + + /* begin space */ + printChar(' '); + shortPrintOop(longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))))); + } +} + + /* StackInterpreter>>#printDecodeMethodHeaderOop: */ +static NoDbgRegParms void +printDecodeMethodHeaderOop(sqInt methodHeaderOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin printOopShort: */ + printOopShortInner(methodHeaderOop); + fflush(GIV(transcript)); + if (((methodHeaderOop & AlternateHeaderHasPrimFlag) != 0)) { + print(" hasPrim"); + } + if (methodHeaderOop & LargeContextBit) { + print(" largeFrame"); + } + if (((methodHeaderOop >> 3)) < 0) { + print(" altSet"); + } + print(" nLits "); + printNum( + (/* begin literalCountOfMethodHeader: */ + assert((((methodHeaderOop) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeaderOop >> 3)) & AlternateHeaderNumLiteralsMask)); + print(" nArgs "); + printNum((((usqInt)(methodHeaderOop)) >> MethodHeaderArgCountShift) & 15); + print(" nTemps "); + printNum((((usqInt)(methodHeaderOop)) >> MethodHeaderTempCountShift) & 0x3F); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printExternalHeadFrame */ +void +printExternalHeadFrame(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + printFrameWithSP(GIV(framePointer), GIV(stackPointer)); +} + + /* StackInterpreter>>#printFrameAndCallers:SP:short: */ +static NoDbgRegParms sqInt +printFrameAndCallersSPshort(char *theFP, char *theSP, sqInt printShort) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!(/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + return null; + } + if (longAt(theFP + FoxSavedFP)) { + printFrameAndCallersSPshort(((char *)(longAt(theFP + FoxSavedFP))), frameCallerSP(theFP), printShort); + } + if (printShort) { + shortPrintFrame(theFP); + return null; + } + cr(); + printFrameWithSP(theFP, theSP); + return 0; +} + + /* StackInterpreter>>#printFrameOop:at: */ +static NoDbgRegParms void +printFrameOopat(char *name, char *address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt it; + + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%12s: %16p\t=" /* pst: */, + name, + ((void *)it)); + printOopShortInner(it); + cr(); +} + + /* StackInterpreter>>#printFrameOop:index:at: */ +static NoDbgRegParms void +printFrameOopindexat(char *name, sqInt idx, char *address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt it; + + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%s%10s%" PRIdSQINT ": %16p\t" /* pst: */, + (idx > 9 + ? "" + : " "), + name, + idx, + ((void *)it)); + printOopShortInner(it); + cr(); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printFramesInPage: */ +void +printFramesInPage(StackPage *thePage) +{ + printFrameAndCallersSPshort((thePage->headFP), (thePage->headSP), 0); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printFramesOnStackPageListInUse */ +void +printFramesOnStackPageListInUse(void) +{ + StackPage *page; + + page = GIV(mostRecentlyUsedPage); + do { + if (!(isFree(page))) { + print("page "); + printHexPtrnp(page); + cr(); + printFramesInPage(page); + cr(); + } + } while(((page = (page->prevPage))) != (GIV(mostRecentlyUsedPage))); +} + + /* StackInterpreter>>#printFrameThing:andFrame:at: */ +static NoDbgRegParms void +printFrameThingandFrameat(char *name, char *theFP, char *address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt it; + + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%12s: %16p" /* pst: */, + name, + ((void *)it)); + if (it) { + if (it == GIV(nilObj)) { + fprintf(GIV(transcript), "=nil"); + } + else { + fprintf(GIV(transcript), + "=%" PRIdSQINT "", + it); + } + } + fprintf(GIV(transcript), + " frame: %p\n", + theFP); +} + + /* StackInterpreter>>#printFrameThing:at:extraString: */ +static NoDbgRegParms void +printFrameThingatextraString(char *name, char *address, char *extraStringOrNil) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt it; + + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%12s: %16p" /* pst: */, + name, + ((void *)it)); + framePrintDescription(it); + if (extraStringOrNil) { + fprintf(GIV(transcript), + "%s", + extraStringOrNil); + } + cr(); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printFrame: */ +sqInt +printFrame(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *aFrame; + char *frameAbove; + char *prevFrame; + char *startFrame; + StackPage *thePage; + char *theSP; + + if (!(/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + if ((addressCouldBeObj(((sqInt)theFP))) + && ((isInMemory(((sqInt)theFP))) + && ((((longAt((void *)(((sqInt)theFP)))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (checkIsStillMarriedContextcurrentFP(((sqInt)theFP), GIV(framePointer)))))) { + return printFrame(frameOfMarriedContext(((sqInt)theFP))); + } + fprintf(GIV(transcript), + "%p is not in the stack zone?!\n", + theFP); + return null; + } + frameAbove = null; + if (theFP == GIV(framePointer)) { + theSP = GIV(stackPointer); + } + else { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (isFree(thePage)) { + fprintf(GIV(transcript), + "%p is on a free page?!\n", + theFP); + return null; + } + if ((thePage != GIV(stackPage)) + && (theFP == ((thePage->headFP)))) { + theSP = (thePage->headSP); + } + else { + startFrame = ((thePage == GIV(stackPage)) + && (((GIV(framePointer) >= ((thePage->realStackLimit))) && (GIV(framePointer) <= ((thePage->baseAddress))))) + ? GIV(framePointer) + : (thePage->headFP)); + + /* begin safeFindFrameAbove:on:startingFrom: */ + prevFrame = ((char *) 0); + assert(!(isFree(thePage))); + if (startFrame == theFP) { + frameAbove = null; + goto l1; + } + aFrame = startFrame; + while (1) { + prevFrame = aFrame; + aFrame = ((char *)(longAt(aFrame + FoxSavedFP))); + if (!(aFrame != 0)) break; + if (theFP == aFrame) { + frameAbove = prevFrame; + goto l1; + } + } + frameAbove = null; + /* end safeFindFrameAbove:on:startingFrom: */ +l1: + theSP = (frameAbove + ? frameCallerSP(frameAbove) + : ((char *) null)); + } + } + if (!theSP) { + fprintf(GIV(transcript), "could not find sp; using bogus value\n"); + theSP = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + } + printFrameWithSP(theFP, theSP); + if (frameAbove) { + /* begin printFrameThing:at: */ + printFrameThingatextraString("frame pc", frameAbove + FoxCallerSavedIP, ((char *)null)); + } + return 0; +} + + +/* Print p in hex, unpadded, in the form 0x1234 (C)/16r1234 (here) */ + + /* StackInterpreter>>#printHexPtrnp: */ +static NoDbgRegParms sqInt +printHexPtrnp(void *p) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return fprintf(GIV(transcript), + "%p", + p); +} + + +/* Print n in hex, in the form ' 0x1234', padded to a width of 10 characters + in 32-bits ('0x' + 8 nibbles) or 18 characters in 64-bits ('0x' + 16 + nibbles). In the simulator use 16r as the prefix, padding to 11 characters + in 32-bits + or 19 characters in 64-bits. */ + + /* StackInterpreter>>#printHex: */ +void +printHex(usqInt n) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fprintf(GIV(transcript), + "%16p" /* pst: */, + ((void *)n)); +} + + +/* Print all methods whose penultimate literal is either selector, + or an object whose first inst var is the method and whose + second is selector (e.g. an AdditionalMethodState). */ +/* useful for VM debugging */ + + /* StackInterpreter>>#printLikelyImplementorsOfSelector: */ +void +printLikelyImplementorsOfSelector(sqInt selector) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt methodClassAssociation; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && ((maybeSelectorOfMethod(objSqInt)) == selector)) { + /* try and print the key of the method class association (the name of the implementing class) */ + methodClassAssociation = methodClassAssociationOf(objSqInt); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)objSqInt)); + + /* begin space */ + printChar(' '); + printOopShortInner(((/* isPointers: */ + ((!(methodClassAssociation & (tagMask())))) + && (((byteAt((void *)(methodClassAssociation + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOf(methodClassAssociation)) >= 2) + ? longAt((void *)((methodClassAssociation + BaseHeaderSize) + (0U << (shiftForWord())))) + : methodClassAssociation)); + cr(); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printMethodCache */ +void +printMethodCache(void) +{ + printMethodCacheFor(-1); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printMethodDictionaryOf: */ +void +printMethodDictionaryOf(sqInt behavior) +{ + printMethodDictionary(longAt((void *)((behavior + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord()))))))); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printMethodDictionary: */ +void +printMethodDictionary(sqInt dictionary) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt meth; + sqInt methodArray; + sqInt selector; + sqInt toDoLimit; + + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + toDoLimit = (numSlotsOf(dictionary)) - 1; + for (index = SelectorStart; index <= toDoLimit; index += 1) { + selector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (selector != GIV(nilObj)) { + meth = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + printOopShortInner(selector); + print(" => "); + printOopShortInner(meth); + print(" ("); + printHex(selector); + print(" => "); + printHex(meth); + print(")\n"); + } + } +} + + +/* Details: The count argument is used to avoid a possible infinite recursion + if classOop is a corrupted object. + */ + + /* StackInterpreter>>#printNameOfClass:count: */ +static NoDbgRegParms void +printNameOfClasscount(sqInt classOop, sqInt cnt) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + + if (!GIV(classNameIndex)) { + print("??nil cnidx??"); + return; + } + if ((!classOop) + || ((classOop == 0) + || (cnt <= 0))) { + print("bad class"); + return; + } + numSlots = numSlotsOf(classOop); + if ((numSlots == GIV(metaclassNumSlots)) + && (GIV(metaclassNumSlots) > GIV(thisClassIndex))) { + printNameOfClasscount(longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord())))))), cnt - 1); + print(" class"); + } + else { + if (numSlots <= GIV(classNameIndex)) { + print("bad class"); + } + else { + printStringOf(longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(classNameIndex)) << (shiftForWord()))))))); + } + } +} + + /* StackInterpreter>>#printNum: */ +static NoDbgRegParms void +printNum(sqInt n) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)n)); +} + + /* StackInterpreter>>#printOopShortInner: */ +static NoDbgRegParms void +printOopShortInner(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + sqInt classLookupKey; + sqInt classOop; + double f; + char *name; + sqInt nameLen; + sqInt objOop; + double result; + sqInt tagBits; + sqInt target; + + if (((oop & (tagMask())) != 0)) { + if (((oop & (characterTag())) != 0)) { + fprintf(GIV(transcript), + "$%c(%" PRIxSQINT ")", + ((int) (((((usqInt)oop))) >> (numTagBits()))), + ((((usqInt)oop))) >> (numTagBits())); + return; + } + if ((((oop) & 7) == 1)) { + fprintf(GIV(transcript), + "%" PRIdSQINT "(0x%" PRIxSQINT ")", + (oop >> 3), + (oop >> 3)); + return; + } + if (((oop & (smallFloatTag())) != 0)) { + fprintf(GIV(transcript), + "%g(0x%" PRIxSQINT ")", + noFailFloatValueOf(oop), + oop); + return; + } + fprintf(GIV(transcript), + "unknown immediate %p", + ((void *)oop)); + return; + } + if (!(addressCouldBeObj(oop))) { + print((oop & 7 /* (allocationUnit - 1) */ + ? " is misaligned" + : whereIs(oop))); + return; + } + if (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + print(" is a free chunk"); + return; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + target = followForwarded(oop); + fprintf(GIV(transcript), + " is a forwarder to %p", + ((void *)target)); + return; + } + if (/* isFloatInstance: */ + ((tagBits = oop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(oop))) & (classIndexMask())) == ClassFloatCompactIndex)) { + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(oop)); + if (oop & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&result), (&bits), sizeof(result)); + } + else { + fetchFloatAtinto(oop + BaseHeaderSize, result); + } + f = result; + + /* begin printFloat: */ + fprintf(GIV(transcript), + "%g", + f); + return; + } + classOop = fetchClassOfNonImm(oop); + if (!(addressCouldBeObj(classOop))) { + print("a ??"); + return; + } + if ((numSlotsOf(classOop)) == GIV(metaclassNumSlots)) { + printNameOfClasscount(oop, 5); + return; + } + if (oop == GIV(nilObj)) { + print("nil"); + return; + } + if (oop == GIV(trueObj)) { + print("true"); + return; + } + if (oop == GIV(falseObj)) { + print("false"); + return; + } + nameLen = lengthOfNameOfClass(classOop); + if (!nameLen) { + print("a ??"); + return; + } + name = nameOfClass(classOop); + if ((/* isBytes: */ + ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && (nameLen >= 6)) { + if (!(strncmp((name + nameLen) - 6, "String", 6))) { + printChar('\''); + printStringOf(oop); + printChar('\''); + return; + } + + /* strncmp is weird */ + if (!(strncmp((name + nameLen) - 6, "Symbol", 6))) { + printChar('#'); + printStringOf(oop); + return; + } + } + if ((nameLen == 9) + && ((strncmp(name, "Character", 9)) == 0)) { + printChar('$'); + printChar(((longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord()))))) >> 3)); + return; + } + fprintf(GIV(transcript), + "a(n) %.*s", + ((int) nameLen), + name); + + /* Try to spot association-like things; they're all subclasses of LookupKey */ + if ((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + && (((instanceSizeOf(classOop)) == (ValueIndex + 1)) + && (isBytes(longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(KeyIndex) << (shiftForWord())))))))))) { + classLookupKey = fetchClassOfNonImm(splObj(SchedulerAssociation)); + while (1) { + if (classLookupKey == GIV(nilObj)) { + return; + } + if ((instanceSizeOf(classLookupKey)) == (KeyIndex + 1)) break; + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classLookupKey + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classLookupKey, objOop); + } + classLookupKey = objOop; + } + if (includesBehaviorThatOf(classOop, classLookupKey)) { + /* begin space */ + printChar(' '); + printOopShortInner(longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(KeyIndex) << (shiftForWord()))))))); + print(" -> "); + fprintf(GIV(transcript), + "%p", + ((void *)(longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))))))); + } + } +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printOop: */ +void +printOop(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt byte; + sqInt bytecodesPerLine; + char *className; + sqInt cls; + sqInt column; + sqInt fmt; + sqInt fmtSqInt; + sqInt index; + sqInt lastIndex; + sqInt length; + usqInt numSlots; + usqInt numSlotsUsqInt; + usqInt startIP; + sqInt tagBits; + + length = 0; + if (((oop & (tagMask())) != 0)) { + printImmediateObjecton(oop, GIV(transcript)); + return; + } + if (!(addressCouldBeObj(oop))) { + printCantBeObjecton(oop, GIV(transcript)); + return; + } + if (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + printFreeObjecton(oop, GIV(transcript)); + return; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + printForwarderon(oop, GIV(transcript)); + return; + } + if (!((cls = fetchClassOfNonImm(oop)))) { + fprintf(GIV(transcript), + "%p has a nil class!!\n", + ((void *)oop)); + return; + } + className = nameOfClasslengthInto(cls, (&length)); + fprintf(GIV(transcript), + "%p: a(n) %.*s", + ((void *)oop), + ((int) length), + className); + if (/* isFloatInstance: */ + ((tagBits = oop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(oop))) & (classIndexMask())) == ClassFloatCompactIndex)) { + fprintf(GIV(transcript), + "\n%g\n", + noFailFloatValueOf(oop)); + return; + } + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt > 5 /* lastPointerFormat */) { + fprintf(GIV(transcript), + " nbytes %" PRIdSQINT "", + numBytesOf(oop)); + } + cr(); + if (/* isPureBitsFormat: */ + (fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat()))) { + /* This will answer false if splObj: ClassAlien is nilObject */ + if (isKindOfClass(oop, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord())))))))) { + fprintf(GIV(transcript), + " datasize %" PRIdSQINT " %s @ %p\n", + longAt((void *)(oop + BaseHeaderSize)), + ((longAt((void *)(oop + BaseHeaderSize))) < 0 + ? "indirect" + : (longAt((void *)(oop + BaseHeaderSize)) + ? "direct" + : "pointer")), + startOfAlienData(oop)); + return; + } + if (isKindOfClass(oop, superclassOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord()))))))))) { + printStringDataOfon(oop, GIV(transcript)); + return; + } + printNonPointerDataOfon(oop, GIV(transcript)); + return; + } + startIP = (fmt >= (firstCompiledMethodFormat()) + ? (((literalCountOf(oop)) + LiteralStart) * BytesPerOop) / BytesPerWord + : numSlotsOf(oop)); + lastIndex = ((0x100 < startIP) ? 0x100 : startIP); + if (lastIndex > 0) { + for (index = 1; index <= lastIndex; index += 1) { + printHex(longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + + /* begin space */ + printChar(' '); + if (!(index % 5 /* elementsPerPrintOopLine */)) { + cr(); + } + } + if (lastIndex % 5 /* elementsPerPrintOopLine */) { + cr(); + } + } + if (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + startIP = (((literalCountOf(oop)) + LiteralStart) * BytesPerOop) + 1; + + /* begin lengthOf: */ + fmtSqInt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmtSqInt <= (ephemeronFormat())) { + lastIndex = numSlots; + goto l1; + } + if (fmtSqInt >= (firstByteFormat())) { + lastIndex = ((numSlots << (shiftForWord()))) - (fmtSqInt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmtSqInt >= (firstShortFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 1))) - (fmtSqInt & 3); + goto l1; + } + if (fmtSqInt >= (firstLongFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 2))) - (fmtSqInt & 1); + goto l1; + } + if (fmtSqInt == (sixtyFourBitIndexableFormat())) { + lastIndex = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + lastIndex = 0; + /* end lengthOf: */ +l1: + if ((lastIndex - startIP) > 0x100) { + lastIndex = startIP + 0x100; + } + bytecodesPerLine = 8; + column = 1; + for (index = startIP; index <= lastIndex; index += 1) { + if (column == 1) { + fprintf(GIV(transcript), + "%10p", + ((void *)(((oop + BaseHeaderSize) + index) - 1))); + } + byte = byteAt((void *)((oop + BaseHeaderSize) + (index - 1))); + fprintf(GIV(transcript), + " %02x/%-+3d", + ((int) byte), + ((int) byte)); + column += 1; + if (column > bytecodesPerLine) { + column = 1; + cr(); + } + } + if ((lengthOf(oop)) > lastIndex) { + print("..."); + } + if (!(column == 1)) { + cr(); + } + } + else { + if (startIP > 64) { + print("..."); + cr(); + } + } +} + + +/* print the entry and answer if it takes a parameter (as the following + entry) + */ + + /* StackInterpreter>>#printPrimLogEntryAt:hasParameter: */ +static NoDbgRegParms sqInt +printPrimLogEntryAthasParameter(sqInt i, sqInt hasParameter) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *className; + sqInt entryOop; + sqInt length; + sqInt methodClass; + sqInt methodSel; + sqInt value; + + length = 0; + entryOop = GIV(primTraceLog)[i]; + if (hasParameter) { + if (addressCouldBeObj(entryOop)) { + className = nameOfClasslengthInto(entryOop, (&length)); + } + else { + className = "bad class"; + length = 9; + } + fprintf(GIV(transcript), + "%.*s\n", + ((int) length), + className); + return 0; + } + if (((entryOop & (tagMask())) != 0)) { + value = (entryOop >> 3); + if (value == TraceIncrementalGC) { + fprintf(GIV(transcript), "**IncrementalGC**\n"); + } + else { + if (value == TraceFullGC) { + fprintf(GIV(transcript), "**FullGC**\n"); + } + else { + if (value == TraceCodeCompaction) { + fprintf(GIV(transcript), "**CompactCode**\n"); + } + else { + if (value == TraceStackOverflow) { + fprintf(GIV(transcript), "**StackOverflow**\n"); + } + else { + if (value == TracePrimitiveFailure) { + fprintf(GIV(transcript), "**PrimitiveFailure** "); + return 1; + } + else { + if (value == TracePrimitiveRetry) { + fprintf(GIV(transcript), "**PrimitiveRetry**\n"); + } + else { + fprintf(GIV(transcript), "???\n"); + } + } + } + } + } + } + return 0; + } + if (addressCouldBeObj(entryOop)) { + if (((byteAt((void *)(entryOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + className = "???"; + length = 3; + methodClass = safeMethodClassOf(entryOop); + if (methodClass != GIV(nilObj)) { + className = nameOfClasslengthInto(methodClass, (&length)); + } + methodSel = findSelectorOfMethod(entryOop); + if (methodSel == GIV(nilObj)) { + fprintf(GIV(transcript), + "%.*s>>(selector not found)\n", + ((int) length), + className); + } + else { + fprintf(GIV(transcript), + "%.*s>>#%.*s\n", + ((int) length), + className, + ((int) (numBytesOfBytes(methodSel))), + ((char *) (firstIndexableField(methodSel)))); + } + } + else { + safePrintStringOf(entryOop); + cr(); + } + } + else { + fprintf(GIV(transcript), + "%" PRIdSQINT "!!!\n", + i); + } + return 0; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printProcessStack: */ +void +printProcessStack(sqInt aProcess) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctx; + sqInt tagBits; + + cr(); + printNameOfClasscount(/* fetchClassOf: */ + ((tagBits = aProcess & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(aProcess)), 5); + + /* begin space */ + printChar(' '); + printHex(aProcess); + print(" priority "); + printNum(quickFetchIntegerofObject(PriorityIndex, aProcess)); + cr(); + ctx = followFieldofObject(SuspendedContextIndex, aProcess); + if (!(ctx == GIV(nilObj))) { + printCallStackOfcurrentFP(ctx, GIV(framePointer)); + } +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printProcsOnList: */ +sqInt +printProcsOnList(sqInt procList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firstProc; + sqInt proc; + + proc = (firstProc = followFieldofObject(FirstLinkIndex, procList)); + while (!(proc == GIV(nilObj))) { + printProcessStack(proc); + proc = followFieldofObject(NextLinkIndex, proc); + if (proc == firstProc) { + warning("circular process list!!"); + return null; + } + } + return 0; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackCallStackOf: */ +sqInt +printStackCallStackOf(sqInt aContextOrProcessOrFrame) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt context; + sqInt senderOop; + char *theFP; + + if (addressCouldBeObj(aContextOrProcessOrFrame)) { + if ((((longAt((void *)(aContextOrProcessOrFrame))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (checkIsStillMarriedContextcurrentFP(aContextOrProcessOrFrame, null))) { + return printStackCallStackOf(((sqInt)(frameOfMarriedContext(aContextOrProcessOrFrame)))); + } + if (aContextOrProcessOrFrame == (activeProcess())) { + return printStackCallStackOf(((sqInt)GIV(framePointer))); + } + if (couldBeProcess(aContextOrProcessOrFrame)) { + return printCallStackOf(longAt((void *)((aContextOrProcessOrFrame + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))))); + } + return null; + } + theFP = ((void *)aContextOrProcessOrFrame); + while (1) { + context = shortReversePrintFrameAndCallers(theFP); + if (!((((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && ((/* begin frameOfMarriedContext: */ + (senderOop = longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))), + assert((((senderOop) & 7) == 1)), + (theFP = ((char *)(senderOop - (smallIntegerTag())))), + checkIsStillMarriedContextcurrentFP(context, theFP))))) { + return null; + } + } + return 0; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackPageList */ +void +printStackPageList(void) +{ + StackPage *page; + + page = GIV(mostRecentlyUsedPage); + do { + /* begin printStackPage: */ + printStackPageuseCount(page, -1); + cr(); + } while(((page = (page->prevPage))) != (GIV(mostRecentlyUsedPage))); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackPageListInUse */ +void +printStackPageListInUse(void) +{ + sqInt n; + StackPage *page; + + page = GIV(mostRecentlyUsedPage); + n = 0; + do { + if (!(isFree(page))) { + printStackPageuseCount(page, (n += 1)); + cr(); + } + } while(((page = (page->prevPage))) != (GIV(mostRecentlyUsedPage))); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackPages */ +void +printStackPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + StackPage *page; + + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + page = stackPageAtpages(i, GIV(pages)); + printStackPageuseCount(page, -1); + cr(); + } +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackPagesInUse */ +void +printStackPagesInUse(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt n; + + n = 0; + for (i = 0; i < GIV(numStackPages); i += 1) { + if (!(isFree(stackPageAt(i)))) { + printStackPageuseCount(stackPageAt(i), (n += 1)); + cr(); + } + } +} + + /* StackInterpreter>>#printStackPage:useCount: */ +static NoDbgRegParms void +printStackPageuseCount(StackPage *page, sqInt n) +{ + print("page "); + printHexPtrnp(page); + print(" ("); + printNum(pageIndexFor((page->realStackLimit))); + if (n >= 0) { + print(","); + printNum(n); + } + print(") (trace: "); + printNum((page->trace)); + printChar(')'); + if (isFree(page)) { + print(" (free)"); + } + if (page == (GIV(mostRecentlyUsedPage))) { + print(" (MRU)"); + } + if (((page->prevPage)) == (GIV(mostRecentlyUsedPage))) { + print(" (LRU)"); + } + cr(); + + /* begin tab */ + printChar('\t'); + print("ba: "); + printHexPtrnp((page->baseAddress)); + print(" - sl: "); + printHexPtrnp((page->realStackLimit)); + print(" - sl-so: "); + printHexPtrnp(((page->realStackLimit)) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)); + print(" - la:"); + printHexPtrnp((page->lastAddress)); + if (!(isFree(page))) { + cr(); + + /* begin tab */ + printChar('\t'); + print("baseFP "); + printHexPtrnp((page->baseFP)); + + /* cr; */ + + /* begin tab */ + printChar('\t'); + print("headFP "); + printHexPtrnp((page->headFP)); + + /* cr; */ + + /* begin tab */ + printChar('\t'); + print("headSP "); + printHexPtrnp((page->headSP)); + } + cr(); + + /* begin tab */ + printChar('\t'); + print("prev "); + printHexPtrnp(page->prevPage); + print(" ("); + printNum(pageIndexFor((((page->prevPage))->realStackLimit))); + printChar(')'); + + /* begin tab */ + printChar('\t'); + print("next "); + printHexPtrnp(page->nextPage); + print(" ("); + printNum(pageIndexFor((((page->nextPage))->realStackLimit))); + printChar(')'); + cr(); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackReferencesTo: */ +void +printStackReferencesTo(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt i; + char *theFP; + StackPage *thePage; + char *theSP; + + callerFP = ((char *) 0); + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((thePage->baseFP)) { + theSP = (thePage->headSP); + theFP = (thePage->headFP); + + /* Skip the instruction pointer on top of stack of inactive pages. */ + if (!(thePage == GIV(stackPage))) { + theSP += BytesPerWord; + } + while (1) { + while (theSP <= (/* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver))) { + if (oop == (longAt(theSP))) { + fprintf(GIV(transcript), + "FP:%p @ %p\n", + theFP, + theSP); + } + theSP += BytesPerWord; + } + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) + && (oop == (longAt(theFP + FoxThisContext)))) { + fprintf(GIV(transcript), + "FP:%p CTXT\n", + theFP); + } + if (oop == (longAt(theFP + FoxMethod))) { + fprintf(GIV(transcript), + "FP:%p MTHD\n", + theFP); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + theFP = callerFP; + } + + /* a.k.a. FoxCallerContext */ + theSP = theFP + FoxCallerSavedIP; + while (theSP <= ((thePage->baseAddress))) { + if (oop == (longAt(theSP))) { + fprintf(GIV(transcript), + "FP:%p @ %p\n", + theFP, + theSP); + } + theSP += BytesPerWord; + } + } + } +} + + /* StackInterpreter>>#printStringOf: */ +static NoDbgRegParms void +printStringOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cnt; + sqInt code; + sqInt fmt; + sqInt i; + sqInt len; + sqInt max; + + if (((oop & (tagMask())) != 0)) { + return; + } + if (!(addressCouldBeObj(oop))) { + return; + } + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt < (firstByteFormat())) { + return; + } + cnt = ((((max = 128)) < ((len = lengthOf(oop)))) ? ((max = 128)) : ((len = lengthOf(oop)))); + i = 0; + if ((isinstanceOfcompactClassIndex(oop, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteArray) << (shiftForWord())))))), GIV(classByteArrayCompactIndex))) + || (/* isLargeIntegerInstance: */ + ((!(oop & (tagMask())))) + && ((((usqInt)(((longAt((void *)(oop))) & (classIndexMask())) - ClassLargeNegativeIntegerCompactIndex))) <= 1))) { + while (i < cnt) { + printHex(byteAt((void *)((oop + BaseHeaderSize) + i))); + i += 1; + } + } + else { + while (i < cnt) { + code = byteAt((void *)((oop + BaseHeaderSize) + i)); + switch (code) { + case 10: + print(""); + break; + case 13: + print(""); + break; + default: + printChar(code); + } + i += 1; + } + } + if (len > max) { + print("..."); + } + + /* begin flush */ + fflush(GIV(transcript)); +} + + /* StackInterpreter>>#print: */ +void +print(char *s) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fprintf(GIV(transcript), + "%s", + s); +} + + +/* Answer the list aProcess belongs to when runnable but not the active + process + */ +/* Useful for debugging!! */ + + /* StackInterpreter>>#processListForProcess: */ +sqInt +processListForProcess(sqInt aProcess) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt oop; + sqInt priority; + sqInt processLists; + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + priority = (oop >> 3); + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + processLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + return longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((priority - 1)) << (shiftForWord())))))); +} + + +/* Answer the zero-relative offset of Process' 'osErr inst var, or -1 if + absent. osErr will be set with the + platform's error after any FFI call (e.g. errno on unix or GetLastError, + WSAGetLastError et al on Win32) + which allows collecting the error without the potential for preemption + between call and error fetch, + which could result in fetching an invalid error code. */ + + /* StackInterpreter>>#processOSErrInstVarOffset */ +sqInt +processOSErrInstVarOffset(void) +{ + return (((imageHeaderFlags & 32) != 0) + ? (((imageHeaderFlags & 4) != 0) + ? 5 + : 4) + : -1); +} + + /* StackInterpreter>>#pushBool: */ +void +pushBool(sqInt trueOrFalse) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + + object = /* booleanObjectOf: */ + (trueOrFalse + ? GIV(trueObj) + : GIV(falseObj)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); +} + + +/* The receiver of a message send or the closure of a block activation is + always on the stack above any arguments and the frame itself. See the + diagram in StackInterpreter class>>initializeFrameIndices. */ + + /* StackInterpreter>>#pushedReceiverOrClosureOfFrame: */ +static NoDbgRegParms sqInt +pushedReceiverOrClosureOfFrame(char *theFP) +{ + /* begin frameStackedReceiver:numArgs: */ + return longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))); +} + + /* StackInterpreter>>#pushFloat: */ +void +pushFloat(double f) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + + object = floatObjectOf(f); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); +} + + /* StackInterpreter>>#pushInteger: */ +sqInt +pushInteger(sqInt integerValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),(((usqInt)integerValue << 3) | 1)); + GIV(stackPointer) = sp; + return null; +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#push: */ +void +push(sqInt object) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + longAtput((sp = GIV(stackPointer) - BytesPerWord),object); + GIV(stackPointer) = sp; +} + + +/* Append aLong to aFile in this platform's 'natural' byte order. aLong is + either 32 or 64 bits, + depending on ObjectMemory. (Bytes will be swapped, if necessary, when the + image is read + on a different platform.) Set successFlag to false if the write fails. */ + + /* StackInterpreter>>#putLong:toFile: */ +static NoDbgRegParms void +putLongtoFile(sqInt aLong, sqImageFile aFile) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objectsWritten; + + objectsWritten = sqImageFileWrite((&aLong), sizeof(sqInt), 1, aFile); + + /* begin success: */ + if (!(objectsWritten == 1)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Append the 16-bit aShort to aFile in this platform's 'natural' byte order. + (Bytes will be swapped, if necessary, when the image is read on a + different platform.) Set successFlag to false if the write fails. */ + + /* StackInterpreter>>#putShort:toFile: */ +static NoDbgRegParms void +putShorttoFile(short aShort, sqImageFile aFile) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objectsWritten; + + objectsWritten = sqImageFileWrite((&aShort), sizeof(short), 1, aFile); + + /* begin success: */ + if (!(objectsWritten == 1)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Save the given process on the scheduler process list for its priority, + adding to the back if yieldImplicitly or to the front if not + yieldImplicitly. + */ + + /* StackInterpreter>>#putToSleep:yieldingIf: */ +static NoDbgRegParms void +putToSleepyieldingIf(sqInt aProcess, sqInt yieldImplicitly) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firstLink; + sqInt objOop; + sqInt oop; + sqInt priority; + sqInt processList; + sqInt processLists; + + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + priority = (oop >> 3); + if ((GIV(highestRunnableProcessPriority) != 0) + && (priority > GIV(highestRunnableProcessPriority))) { + GIV(highestRunnableProcessPriority) = priority; + } + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + processLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + processList = longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((priority - 1)) << (shiftForWord())))))); + if (yieldImplicitly) { + addLastLinktoList(aProcess, processList); + } + else { + /* begin addFirstLink:toList: */ + assert(!(isForwarded(processList))); + assert((fetchPointerofObject(NextLinkIndex, aProcess)) == (nilObject())); + firstLink = longAt((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + assert(firstLink != aProcess); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(FirstLinkIndex, processList, aProcess)); + assert(isNonImmediate(processList)); + if (oopisGreaterThanOrEqualTo(processList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(aProcess & (tagMask())))) + && (oopisLessThan(aProcess, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(processList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(processList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))),aProcess); + if (firstLink == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(LastLinkIndex, processList, aProcess)); + assert(isNonImmediate(processList)); + if (oopisGreaterThanOrEqualTo(processList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(aProcess & (tagMask())))) + && (oopisLessThan(aProcess, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(processList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(processList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((processList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord()))))),aProcess); + } + else { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(NextLinkIndex, aProcess, firstLink)); + assert(isNonImmediate(aProcess)); + if (oopisGreaterThanOrEqualTo(aProcess, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(firstLink & (tagMask())))) + && (oopisLessThan(firstLink, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aProcess + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aProcess); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord()))))),firstLink); + } + + /* a.k.a. (self isEmptyList: aList) */ + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(MyListIndex, aProcess, processList)); + assert(isNonImmediate(aProcess)); + if (oopisGreaterThanOrEqualTo(aProcess, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(processList & (tagMask())))) + && (oopisLessThan(processList, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aProcess + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aProcess); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),processList); + } +} + + +/* Append aWord32 to aFile in this platform's 'natural' byte order. aWord32 + is 32 bits, + depending on ObjectMemory. (Bytes will be swapped, if necessary, when the + image is read + on a different platform.) Set successFlag to false if the write fails. */ + + /* StackInterpreter>>#putWord32:toFile: */ +static NoDbgRegParms void +putWord32toFile(int aWord32, sqImageFile aFile) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objectsWritten; + + objectsWritten = sqImageFileWrite((&aWord32), 4, 1, aFile); + + /* begin success: */ + if (!(objectsWritten == 1)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Return the integer value of the field without verifying that it is an + integer value! For use in time-critical places where the integer-ness of + the field can be guaranteed. + */ + + /* StackInterpreter>>#quickFetchInteger:ofObject: */ +static NoDbgRegParms sqInt +quickFetchIntegerofObject(sqInt fieldIndex, sqInt objectPointer) +{ + sqInt oop; + + oop = longAt((void *)((objectPointer + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + return (oop >> 3); +} + + +/* Anwer if images of the given format are readable by this interpreter. + Allows a virtual machine to accept selected older image formats. */ + + /* StackInterpreter>>#readableFormat: */ +sqInt +readableFormat(sqInt imageVersion) +{ + return (((imageVersion | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */; +} + + +/* Assuming the primFailCode is non-zero, check if the method consumes the + error code + and if so, assign it through theSP. Then zero the primFailCode. This is + infrequent code, + so keep it out of the common path. */ + + /* StackInterpreter>>#reapAndResetErrorCodeTo:header: */ +static NoDbgRegParms NeverInline void +reapAndResetErrorCodeToheader(char *theSP, sqInt methodHeader) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt initialPC; + + assert(GIV(primFailCode) != 0); + initialPC = (((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) + 3 /* sizeOfCallPrimitiveBytecode: */; + if ((byteAt((void *)(initialPC))) == (/* longStoreBytecodeForHeader: */ + ((((sqLong) methodHeader)) < 0 + ? AltLongStoreBytecode + : LongStoreBytecode))) { + longAtput(theSP,getErrorObjectFromPrimFailCode()); + } + GIV(primFailCode) = 0; +} + + +/* callbackContext is an activation of + invokeCallback:[stack:registers:jmpbuf:]. Its sender is the VM's state + prior to the callback. Reestablish that state, + and mark calloutContext as dead. */ + + /* StackInterpreter>>#reestablishContextPriorToCallback: */ +sqInt +reestablishContextPriorToCallback(sqInt callbackContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt calloutContext; + sqInt senderOop; + char *theFP; + StackPage *thePage; + sqInt top; + + if (!(isLiveContext(callbackContext))) { + return 0; + } + calloutContext = externalInstVarofContext(SenderIndex, callbackContext); + if (!(isLiveContext(calloutContext))) { + return 0; + } + + /* We're about to leave this stack page; must save the current frame's instructionPointer. */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* Mark callbackContext as dead; the common case is that it is the current frame. + We go the extra mile for the debugger. */ + if ((!((longAt((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))) & (tagMask())))) { + /* begin markContextAsDead: */ + assert(isContext(callbackContext)); + assert((isNonImmediate(callbackContext)) + && (!(isForwarded(callbackContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, callbackContext, GIV(nilObj))); + longAtput((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(callbackContext)) + && (!(isForwarded(callbackContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, callbackContext, GIV(nilObj))); + longAtput((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (GIV(framePointer) == theFP) { + if (longAt(theFP + FoxSavedFP)) { + GIV(instructionPointer) = longAt(GIV(framePointer) + FoxCallerSavedIP); + GIV(stackPointer) = (GIV(framePointer) + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(GIV(framePointer)))->cmNumArgs) + : byteAt((GIV(framePointer) + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + GIV(framePointer) = ((char *)(longAt(GIV(framePointer) + FoxSavedFP))); + return 1; + } + else { + freeStackPage(GIV(stackPage)); + } + } + else { + externalDivorceFrameandContext(theFP, callbackContext); + + /* begin markContextAsDead: */ + assert(isContext(callbackContext)); + assert((isNonImmediate(callbackContext)) + && (!(isForwarded(callbackContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, callbackContext, GIV(nilObj))); + longAtput((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(callbackContext)) + && (!(isForwarded(callbackContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, callbackContext, GIV(nilObj))); + longAtput((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + } + } + + /* Make the calloutContext the active frame. The case where calloutContext + is immediately below callbackContext on the same page is handled above. */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((calloutContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(calloutContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((calloutContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + + /* findSPOf:on: points to the word beneath the instructionPointer, but + there is no instructionPointer on the top frame of the current page. */ + assert(thePage != GIV(stackPage)); + GIV(stackPointer) = (findSPOfon(theFP, thePage)) - BytesPerWord; + GIV(framePointer) = theFP; + assert(GIV(stackPointer) < GIV(framePointer)); + } + else { + thePage = makeBaseFrameFor(calloutContext); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + return 1; +} + + +/* Remove the first process from the given linked list. */ + + /* StackInterpreter>>#removeFirstLinkOfList: */ +static NoDbgRegParms sqInt +removeFirstLinkOfList(sqInt aList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt first; + sqInt last; + sqInt next; + + assert(!((isForwarded(aList)))); + first = longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + last = longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord())))))); + assert(!((isForwarded(first)))); + assert(!((isForwarded(last)))); + if (first == last) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aList)) + && (!(isForwarded(aList)))); + assert(validStorePointerUncheckedArgs(FirstLinkIndex, aList, GIV(nilObj))); + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aList)) + && (!(isForwarded(aList)))); + assert(validStorePointerUncheckedArgs(LastLinkIndex, aList, GIV(nilObj))); + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + next = longAt((void *)((first + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(FirstLinkIndex, aList, next)); + assert(isNonImmediate(aList)); + if (oopisGreaterThanOrEqualTo(aList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(next & (tagMask())))) + && (oopisLessThan(next, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))),next); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(first)) + && (!(isForwarded(first)))); + assert(validStorePointerUncheckedArgs(NextLinkIndex, first, GIV(nilObj))); + longAtput((void *)((first + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord()))))),GIV(nilObj)); + return first; +} + + +/* Attempt to remove a process from a linked list. Answer if the attempt + succeeded. + */ + + /* StackInterpreter>>#removeProcess:fromList: */ +static NoDbgRegParms sqInt +removeProcessfromList(sqInt aProcess, sqInt aList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firstLink; + sqInt lastLink; + sqInt nextLink; + usqInt numSlots; + sqInt tempLink; + + nextLink = 0; + assert(!((isForwarded(aProcess)))); + assert(!((isForwarded(aList)))); + + /* any process on the list could have been becomed, so use a read barrier... */ + + /* begin followObjField:ofObject: */ + firstLink = longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(firstLink)); + if ((!((longAt((void *)(firstLink))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + firstLink = fixFollowedFieldofObjectwithInitialValue(FirstLinkIndex, aList, firstLink); + } + + /* begin followObjField:ofObject: */ + lastLink = longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(lastLink)); + if ((!((longAt((void *)(lastLink))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + lastLink = fixFollowedFieldofObjectwithInitialValue(LastLinkIndex, aList, lastLink); + } + if (aProcess == firstLink) { + /* begin followObjField:ofObject: */ + nextLink = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(nextLink)); + if ((!((longAt((void *)(nextLink))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + nextLink = fixFollowedFieldofObjectwithInitialValue(NextLinkIndex, aProcess, nextLink); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(FirstLinkIndex, aList, nextLink)); + assert(isNonImmediate(aList)); + if (oopisGreaterThanOrEqualTo(aList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(nextLink & (tagMask())))) + && (oopisLessThan(nextLink, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))),nextLink); + if (aProcess == lastLink) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aList)) + && (!(isForwarded(aList)))); + assert(validStorePointerUncheckedArgs(LastLinkIndex, aList, GIV(nilObj))); + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord()))))),GIV(nilObj)); + } + } + else { + tempLink = firstLink; + + /* fail if any link doesn't look like a process... */ + while (1) { + if (!((/* isPointers: */ + ((!(tempLink & (tagMask())))) + && (((byteAt((void *)(tempLink + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(tempLink)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(tempLink + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(tempLink - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > MyListIndex))) { + return 0; + } + + /* begin followObjField:ofObject: */ + nextLink = longAt((void *)((tempLink + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(nextLink)); + if ((!((longAt((void *)(nextLink))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + nextLink = fixFollowedFieldofObjectwithInitialValue(NextLinkIndex, tempLink, nextLink); + } + if (nextLink == aProcess) break; + tempLink = nextLink; + } + nextLink = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(NextLinkIndex, tempLink, nextLink)); + assert(isNonImmediate(tempLink)); + if (oopisGreaterThanOrEqualTo(tempLink, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(nextLink & (tagMask())))) + && (oopisLessThan(nextLink, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(tempLink + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(tempLink); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((tempLink + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord()))))),nextLink); + if (aProcess == lastLink) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(LastLinkIndex, aList, tempLink)); + assert(isNonImmediate(aList)); + if (oopisGreaterThanOrEqualTo(aList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(tempLink & (tagMask())))) + && (oopisLessThan(tempLink, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord()))))),tempLink); + } + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aProcess)) + && (!(isForwarded(aProcess)))); + assert(validStorePointerUncheckedArgs(NextLinkIndex, aProcess, GIV(nilObj))); + longAtput((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aProcess)) + && (!(isForwarded(aProcess)))); + assert(validStorePointerUncheckedArgs(MyListIndex, aProcess, GIV(nilObj))); + longAtput((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),GIV(nilObj)); + return 1; +} + + +/* Make aProcess runnable and if its priority is higher than that of the + current process, preempt the current process. Answer if the current + process was preempted. If the current process was preempted then if + yieldImplicitly add the current process to the back of its run queue, + causing an implicit yiled to other processes on the run queue, otherwise + add the current process to the front of its run queue, hence not yielding. + Blue book behaviour is to yield implicitly but is arguably incorrect. */ + + /* StackInterpreter>>#resume:preemptedYieldingIf:from: */ +static NoDbgRegParms sqInt +resumepreemptedYieldingIffrom(sqInt aProcess, sqInt yieldImplicitly, sqInt sourceCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activePriority; + sqInt activeProc; + sqInt newPriority; + sqInt objOop; + sqInt oop; + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + activePriority = (oop >> 3); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + newPriority = (oop >> 3); + if (newPriority <= activePriority) { + putToSleepyieldingIf(aProcess, 1); + return 0; + } + putToSleepyieldingIf(activeProc, yieldImplicitly); + transferTofrom(aProcess, sourceCode); + return 1; +} + + +/* In Spur two cases of primitive failure are handled specially. A primitive + may fail due to validation + encountering a forwarder. On failure, check the accessorDepth for the + primitive and if non-negative + scan the args to the depth, following any forwarders. Retry the primitive + if any are found. Hence + lazily and transparently following forwarders on primitive failure. + Additionally a primitive might fail + due to an allocation failing. Retry if external primitives have failed + with PrimErrNoMemory after running + first the scavenger and then on a subsequent failure, the global + mark-sweep collector. Hence lazily + and transparently GC on memory exhaustion. */ + + /* StackInterpreter>>#retryPrimitiveOnFailure */ +static sqInt +retryPrimitiveOnFailure(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt accessorDepth; + sqInt aMethodSelectorOrImmediate; + sqInt canRetry; + usqInt firstBytecode; + sqInt followDone; + sqInt gcDone; + sqInt header; + sqInt index; + sqInt methodHeader; + sqInt oop; + sqInt primIndex; + sqInt primitiveIndex; + sqInt referent; + sqInt retried; + sqInt retry; + sqInt scannedStackFrame; + sqInt tagBits; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + assert(saneFunctionPointerForFailureOfPrimIndex(primitiveIndex)); + gcDone = 0; + followDone = (canRetry = (retried = 0)); + while (1) { + retry = 0; + if (GIV(primFailCode) == PrimErrNoMemory) { + if (((gcDone += 1)) == 1) { + canRetry = primitiveIndex == PrimNumberExternalCall; + } + if (canRetry) { + if (gcDone == 1) { + /* begin scavengingGC */ + scavengingGCTenuringIf(TenureByAge); + } + if (gcDone == 2) { + fullGC(); + } + retry = gcDone <= 2; + } + } + else { + if (!followDone) { + followDone = 1; + + /* begin checkForAndFollowForwardedPrimitiveState */ + if ((recordPrimTrace()) + && (!primTracePluginName)) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveFailure << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + aMethodSelectorOrImmediate = /* fetchClassOf: */ + ((tagBits = (longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord))) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)))); + + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = aMethodSelectorOrImmediate; + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + assert(failed()); + retry = 0; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIndex = 0; + } + assert((GIV(argumentCount) == (argumentCountOf(GIV(newMethod)))) + || (isMetaPrimitiveIndex(primIndex))); + + /* First things first; make sure the metadata has been followed before it is accessed to derive accessorDepth... */ + if (((/* isCalloutPrimitiveIndex: */ + (primIndex == PrimNumberExternalCall) + || (primIndex == PrimNumberFFICall)) + || (primIndex == PrimNumberDoExternalCall)) + && (unfollowFirstLiteralOfMaybeCalloutMethodprimitiveIndex(GIV(newMethod), primIndex))) { + retry = 1; + } + + /* If the primitive is one of the meta primitives PrimNumberDoPrimitive or PrimNumberDoExternalCall, then + metaAccessorDepth will have been set to nil at the start of the primitive, and to the accessor depth of the + called primitive (or external call) immediately before dispatch. Hence if primIndex is that of a meta primitive + then if metaAccessorDepth is -2, the accessor depth is that of the meta primitive, and if > -2, then + metaAccessorDepth is the accessor depth of the primitive (or external call). Similarly, if the primitive is + primitiveExternalCall then the accessor depth is that of primitiveExternalCall until primitiveFunctionPointer + is assigned, at which point the accessor depth is taken from the slot in newMethod's first literal. */ + accessorDepth = ((/* isMetaPrimitiveIndex: */ + (primIndex == PrimNumberDoPrimitive) + || (primIndex == PrimNumberDoExternalCall)) + && (GIV(metaAccessorDepth) > -2) + ? GIV(metaAccessorDepth) + : accessorDepthForPrimitiveMethod(GIV(newMethod))); + assert(((accessorDepth >= -1) && (accessorDepth <= 5))); + if (accessorDepth >= 0) { + scannedStackFrame = 0; + for (index = 0; index <= GIV(argumentCount); index += 1) { + oop = longAt((void *)(GIV(stackPointer) + (index * BytesPerWord))); + if ((!(oop & (tagMask())))) { + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + assert(index < GIV(argumentCount)); + retry = 1; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + + /* stackValue:put: */ + longAtput((void *)(GIV(stackPointer) + (index * BytesPerWord)),oop); + if (!scannedStackFrame) { + scannedStackFrame = 1; + + /* Avoid repeated primitive failures by following all state in the current stack frame. */ + followForwardedFrameContentsstackPointer(GIV(framePointer), GIV(stackPointer) + ((GIV(argumentCount) + 1) * BytesPerWord)); + } + } + if ((accessorDepth > 0) + && ((/* hasPointerFields: */ + ((!(oop & (tagMask())))) + && (hasPointerFieldsNonImm(oop))) + && (followForwardedObjectFieldstoDepth(oop, accessorDepth)))) { + retry = 1; + } + } + } + } + if (retry + && (recordPrimTrace())) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveRetry << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + } + } + if (!(retry)) break; + assert(GIV(primFailCode) != 0); + retried = 1; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + dispatchFunctionPointer(primitiveFunctionPointer); + } + return retried; +} + + +/* callbackMethodContext is an activation of + invokeCallback:[stack:registers:jmpbuf:]. Its sender is the VM's state + prior to the callback. Reestablish that state (via longjmp), + and mark callbackMethodContext as dead. */ + + /* StackInterpreter>>#returnAs:ThroughCallback:Context: */ +sqInt +returnAsThroughCallbackContext(sqInt returnTypeOop, VMCallbackContext *vmCallbackContext, sqInt callbackMethodContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + sqInt calloutMethodContext; + sqInt cFramePointer; + sqInt cStackPointer; + sqInt header; + sqInt methodHeader; + sqInt senderOop; + sqInt sourceCode; + char *theFP; + StackPage *thePage; + sqInt thunkp; + sqInt top; + + assert(GIV(primFailCode) == 0); + assert((((returnTypeOop) & 7) == 1)); + assert(!(isImmediate(((sqInt)vmCallbackContext)))); + assert((addressCouldBeObj(callbackMethodContext)) + && (isContext(callbackMethodContext))); + assert(((debugCallbackPath = 0)) == 0); + if (!(((((returnTypeOop) & 7) == 1)) + && (isLiveContext(callbackMethodContext)))) { + assert(((debugCallbackPath = 1)) == 1); + return 0; + } + calloutMethodContext = externalInstVarofContext(SenderIndex, callbackMethodContext); + if (!(isLiveContext(calloutMethodContext))) { + assert(((debugCallbackPath = 2)) == 2); + return 0; + } + assert(((debugCallbackReturns += 1)) > 0); + thunkp = ((sqInt) ((vmCallbackContext->thunkp))); + + /* begin recordCallbackReturn:thunkp: */ + sourceCode = (returnTypeOop >> 3); + assert(sourceCode < ((1U << TraceTypeShift))); + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceVMCallbackReturn) << TraceTypeShift))))) + sourceCode); + GIV(traceLog)[GIV(traceLogIndex) + 1] = (((sqInt)thunkp)); + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + + /* We're about to leave this stack page; must save the current frame's instructionPointer. */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* Mark callbackMethodContext as dead; the common case is that it is the current frame. + We go the extra mile for the debugger. */ + if ((!((longAt((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))) & (tagMask())))) { + assert(((debugCallbackPath = debugCallbackPath | 4)) > 0); + + /* begin markContextAsDead: */ + assert(isContext(callbackMethodContext)); + assert((isNonImmediate(callbackMethodContext)) + && (!(isForwarded(callbackMethodContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, callbackMethodContext, GIV(nilObj))); + longAtput((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(callbackMethodContext)) + && (!(isForwarded(callbackMethodContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, callbackMethodContext, GIV(nilObj))); + longAtput((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + assert(((debugCallbackPath = debugCallbackPath | 8)) > 0); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + assert((frameReceiver(theFP)) == (splObj(ClassAlien))); + if (GIV(framePointer) == theFP) { + assert(((debugCallbackPath = debugCallbackPath | 16)) > 0); + if (longAt(theFP + FoxSavedFP)) { + assert(((debugCallbackPath = debugCallbackPath | 32)) > 0); + GIV(instructionPointer) = longAt(theFP + FoxCallerSavedIP); + GIV(stackPointer) = (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + GIV(framePointer) = ((char *)(longAt(theFP + FoxSavedFP))); + aMethodObj = /* frameMethodObject: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(GIV(framePointer)))->methodObject) + : longAt(GIV(framePointer) + FoxMethod)); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* begin restoreCStackStateForCallbackContext: */ + cFramePointer = ((sqInt) ((vmCallbackContext->savedCFramePointer))); + cStackPointer = ((sqInt) ((vmCallbackContext->savedCStackPointer))); + + /* begin setCFramePointer:setCStackPointer: */ + assert(!(((!cFramePointer) + || (!cStackPointer)))); + GIV(CStackPointer) = cStackPointer; + GIV(CFramePointer) = cFramePointer; + assertSavedCStackPointersWellAligned(); + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + + /* N.B. siglongjmp is defines as _longjmp on non-win32 platforms. + This matches the use of _setjmp in ia32abicc.c. */ + _longjmp((vmCallbackContext->trampoline), (returnTypeOop >> 3)); + return 1; + } + + /* calloutMethodContext is immediately below on the same page. Make it current. */ + freeStackPage(GIV(stackPage)); + } + else { + assert(((debugCallbackPath = debugCallbackPath | 64)) > 0); + externalDivorceFrameandContext(theFP, callbackMethodContext); + + /* begin markContextAsDead: */ + assert(isContext(callbackMethodContext)); + assert((isNonImmediate(callbackMethodContext)) + && (!(isForwarded(callbackMethodContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, callbackMethodContext, GIV(nilObj))); + longAtput((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(callbackMethodContext)) + && (!(isForwarded(callbackMethodContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, callbackMethodContext, GIV(nilObj))); + longAtput((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + } + } + + /* Make the calloutMethodContext the active frame. The case where calloutMethodContext + is immediately below callbackMethodContext on the same page is handled above. */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((calloutMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(calloutMethodContext)))) { + assert(((debugCallbackPath = debugCallbackPath | 128)) > 0); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((calloutMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + + /* findSPOf:on: points to the word beneath the instructionPointer, but + there is no instructionPointer on the top frame of the current page. */ + assert(thePage != GIV(stackPage)); + GIV(stackPointer) = (((thePage->headFP)) == theFP + ? (thePage->headSP) + : (findSPOfon(theFP, thePage)) - BytesPerWord); + GIV(framePointer) = theFP; + assert(GIV(stackPointer) < GIV(framePointer)); + } + else { + assert(((debugCallbackPath = debugCallbackPath | 0x100)) > 0); + thePage = makeBaseFrameFor(calloutMethodContext); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + aMethodObj = longAt((void *)((calloutMethodContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin restoreCStackStateForCallbackContext: */ + cFramePointer = ((sqInt) ((vmCallbackContext->savedCFramePointer))); + cStackPointer = ((sqInt) ((vmCallbackContext->savedCStackPointer))); + + /* begin setCFramePointer:setCStackPointer: */ + assert(!(((!cFramePointer) + || (!cStackPointer)))); + GIV(CStackPointer) = cStackPointer; + GIV(CFramePointer) = cFramePointer; + assertSavedCStackPointersWellAligned(); + + /* N.B. siglongjmp is defined as _longjmp on non-win32 platforms. + This matches the use of _setjmp in ia32abicc.c. */ + _longjmp((vmCallbackContext->trampoline), (returnTypeOop >> 3)); + + /* NOTREACHED */ + return 1; +} + + +/* Reverse the given range of Display pixels, rounded to whole word boundary. + Used to give feedback during VM activities such as garbage collection when + debugging. It is assumed that the given word range falls entirely within + the first line of the Display. */ + + /* StackInterpreter>>#reverseDisplayFrom:to: */ +static NoDbgRegParms void +reverseDisplayFromto(sqInt startIndex, sqInt endIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt primFailCodeValue; + sqInt ptr; + unsigned int reversed; + sqInt toDoLimit; + sqInt wordEndIndex; + sqInt wordStartIndex; + + if (!(postGCUpdateDisplayBits())) { + return; + } + if ((displayBits == 0) + || (((((((sqInt)displayBits)) & (tagMask())) != 0)) + || (displayDepth <= 0))) { + return; + } + wordStartIndex = ((((startIndex < 0) ? 0 : startIndex)) * displayDepth) / 32; + wordEndIndex = ((((endIndex < displayWidth) ? endIndex : displayWidth)) * displayDepth) / 32; + toDoLimit = (((sqInt)displayBits)) + (wordEndIndex * 4); + for (ptr = ((((sqInt)displayBits)) + (wordStartIndex * 4)); ptr <= toDoLimit; ptr += 4) { + reversed = (long32At((void *)(ptr))) ^ 0xFFFFFFFFU; + long32Atput((void *)(ptr),reversed); + } + primFailCodeValue = GIV(primFailCode); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + updateDisplayLeftTopRightBottom(0, 0, displayWidth, 1); + ioForceDisplayUpdate(); + GIV(primFailCode) = primFailCodeValue; +} + + +/* Rewrite an existing entry in the method cache with a new primitive + function address. + Used by primitiveExternalCall to make direct calls to found external + prims, or quickly + fail not found external prims. */ + + /* StackInterpreter>>#rewriteMethodCacheEntryForExternalPrimitiveToFunction: */ +static NoDbgRegParms void +rewriteMethodCacheEntryForExternalPrimitiveToFunction(void (*localPrimAddress)()) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((GIV(methodCache)[GIV(lastMethodCacheProbeWrite) + MethodCacheMethod]) == GIV(newMethod)) { + GIV(methodCache)[GIV(lastMethodCacheProbeWrite) + MethodCachePrimFunction] = (((sqIntptr_t) localPrimAddress)); + } +} + + +/* Safe version of methodClassOf: that deals with malformed compiled methods, + etc, and does not fixup forwarding pointers.. */ + + /* StackInterpreter>>#safeMethodClassOf: */ +static NoDbgRegParms sqInt +safeMethodClassOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt literal; + sqInt maybeClass; + usqInt numSlots; + sqInt offset; + + offset = (literalCountOf(methodPointer)) - 1; + + /* begin literal:ofMethod: */ + literal = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = followForwarded(literal); + } + if (!((/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(literal)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(literal + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(literal - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > ValueIndex))) { + return GIV(nilObj); + } + maybeClass = longAt((void *)((literal + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(maybeClass & (tagMask())))) + && ((!((longAt((void *)(maybeClass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + maybeClass = followForwarded(maybeClass); + } + return maybeClass; +} + + +/* This is an assert function used to ensure consistency between the + primitiveFunctionPointer and the primitive index when a primitive fails in + Spur. Since Spur automagically retries + failing primitives we want to know that the primitiveFunctionPointer is + actually valid. This isn't + always possible for the `indirect'' primitives (calling a plugin + primitive, calling the ffi, using the + receiver:tryPrimitive:withArgs: primitive evaluator in the simulator. We + do the best we can. */ + + /* StackInterpreter>>#saneFunctionPointerForFailureOfPrimIndex: */ +static NoDbgRegParms sqInt +saneFunctionPointerForFailureOfPrimIndex(sqInt primIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*basePrimitive)(void); + + /* begin functionPointerFor:inClass: */ + basePrimitive = ((((usqInt)primIndex)) > MaxPrimitiveIndex + ? ((void (*)(void)) 0) + : primitiveTable[primIndex]); + return (primitiveFunctionPointer == basePrimitive) + || (((/* isCalloutPrimitiveIndex: */ + (primIndex == PrimNumberExternalCall) + || (primIndex == PrimNumberFFICall)) + && (!((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex))) + || ((/* isMetaPrimitiveIndex: */ + (primIndex == PrimNumberDoPrimitive) + || (primIndex == PrimNumberDoExternalCall)) + && (GIV(metaAccessorDepth) > -2))); +} + + +/* Useful for debugging!! */ + + /* StackInterpreter>>#schedulerPointer */ +sqInt +schedulerPointer(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin fetchPointer:ofObject: */ + return longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#setBreakMNUSelector: */ +void +setBreakMNUSelector(char *aString) +{ + if ((breakSelector = aString)) { + breakSelectorLength = -(strlen(aString)); + } + else { + breakSelectorLength = MinSmallInteger; + } +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#setBreakSelector: */ +void +setBreakSelector(char *aString) +{ + if ((breakSelector = aString)) { + breakSelectorLength = strlen(aString); + } + else { + breakSelectorLength = MinSmallInteger; + } +} + + +/* If bit 0 of the imageHeaderFlags is set then the system should use the + full screen. + */ + + /* StackInterpreter>>#setFullScreenFlag: */ +void +setFullScreenFlag(sqInt aBoolean) +{ + imageHeaderFlags = (aBoolean + ? imageHeaderFlags | 1 + : ((imageHeaderFlags | 1) - 1)); +} + + /* StackInterpreter>>#setInterruptCheckChain: */ +void * +setInterruptCheckChain(void (*aFunction)()) +{ + void (*prevFunction)(); + + prevFunction = interruptCheckChain; + interruptCheckChain = aFunction; + return prevFunction; +} + + /* StackInterpreter>>#setInterruptKeycode: */ +void +setInterruptKeycode(sqInt value) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(interruptKeycode) = value; +} + + +/* This is invoked frrom the platform GUI event subsystem when the user + inputs a a char code matching interruptKeycode. Set the value and then + forceInterruptCheck so as to call the interruptCheckChain function if set. + It's important to set interruptPending *before* calling + forceInterruptCheck so that functions on the interruptCheckChain can check + interruptPending. */ + + /* StackInterpreter>>#setInterruptPending: */ +void +setInterruptPending(sqInt value) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(interruptPending) = value; + forceInterruptCheck(); +} + + /* StackInterpreter>>#setNextWakeupUsecs: */ +void +setNextWakeupUsecs(usqLong value) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(nextWakeupUsecs) = value; +} + + /* StackInterpreter>>#setSavedWindowSize: */ +void +setSavedWindowSize(sqInt value) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(savedWindowSize) = value; +} + + +/* The low space semaphore is about to be signaled. Set the signalLowSpace + flag, and force an interrupt check. Save the currently active process in + the special + objects array so that the low space handler will be able to determine the + process that first triggered a low space condition. The image's low space + handler is expected + to nil out the special objects array slot when it handles the low space + condition. */ + + /* StackInterpreter>>#setSignalLowSpaceFlagAndSaveProcess */ +static void +setSignalLowSpaceFlagAndSaveProcess(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + sqInt lastSavedProcess; + sqInt objOop; + + /* signalLowSpace: */ + GIV(signalLowSpace) = 1; + + /* begin lowSpaceThreshold: */ + GIV(lowSpaceThreshold) = 0; + + /* N.B. The threshold > 0 guard eliminates a warning when + self lowSpaceThreshold: 0 + is inlined into setSignalLowSpaceFlagAndSaveProcess */ + assert(GIV(totalFreeOldSpace) >= GIV(lowSpaceThreshold)); + lastSavedProcess = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ProcessSignalingLowSpace) << (shiftForWord())))))); + if (lastSavedProcess == GIV(nilObj)) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ProcessSignalingLowSpace, GIV(specialObjectsOop), activeProc)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeProc & (tagMask())))) + && (oopisLessThan(activeProc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ProcessSignalingLowSpace) << (shiftForWord()))))),activeProc); + } + forceInterruptCheck(); +} + + /* StackInterpreter>>#setTraceFlagOnContextsFramesPageIfNeeded: */ +static NoDbgRegParms void +setTraceFlagOnContextsFramesPageIfNeeded(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void *pointer; + sqInt senderOop; + StackPage *thePage; + + if (/* isStillMarriedContextDuringGC: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContextDuringGC(aContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + pointer = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(pointer), GIV(pages)); + assert(((((thePage->trace)) >= StackPageUnreached) && (((thePage->trace)) <= StackPageTraced))); + if (!((thePage->trace))) { + (thePage->trace = StackPageReachedButUntraced); + } + } +} + + /* StackInterpreter>>#shortPrintContext: */ +static NoDbgRegParms sqInt +shortPrintContext(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt home; + sqInt rcvr; + sqInt senderOop; + char *theFP; + + if (!(/* isContext: */ + ((!(aContext & (tagMask())))) + && (((longAt((void *)(aContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + printHex(aContext); + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " is not a context"); + cr(); + return null; + } + printHex(aContext); + if (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (checkIsStillMarriedContextcurrentFP(aContext, GIV(framePointer))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " M ("); + } + else { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " I ("); + } + printHex(((usqIntptr_t)theFP)); + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + ") "); + } + else { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " w "); + } + } + else { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " s "); + } + if ((home = findHomeForContext(aContext))) { + rcvr = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord())))))); + printActivationNameForreceiverisBlockfirstTemporary(longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))), rcvr, home != aContext, longAt((void *)((home + BaseHeaderSize) + ((((usqInt)((0 + CtxtTempFrameStart)) << (shiftForWord()))))))); + + /* begin space */ + printChar(' '); + shortPrintOop(rcvr); + } + else { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " BOGUS CONTEXT (can't determine home)"); + cr(); + } + return 0; +} + + /* StackInterpreter>>#shortPrintFrameAndCallers: */ +static NoDbgRegParms sqInt +shortPrintFrameAndCallers(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!(/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + return null; + } + shortPrintFrame(theFP); + if (longAt(theFP + FoxSavedFP)) { + shortPrintFrameAndCallers(((char *)(longAt(theFP + FoxSavedFP)))); + } + return 0; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#shortPrintFramesInPage: */ +void +shortPrintFramesInPage(StackPage *thePage) +{ + printFrameAndCallersSPshort((thePage->headFP), (thePage->headSP), 1); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#shortPrintFramesOnStackPageListInUse */ +void +shortPrintFramesOnStackPageListInUse(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *page; + + page = GIV(mostRecentlyUsedPage); + do { + if (!(isFree(page))) { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "page "); + printHexPtrnp(page); + cr(); + shortPrintFramesInPage(page); + cr(); + } + } while(((page = (page->prevPage))) != (GIV(mostRecentlyUsedPage))); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#shortPrintFrame:AndNCallers: */ +void +shortPrintFrameAndNCallers(char *theFP, sqInt n) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((n != 0) + && (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + shortPrintFrame(theFP); + shortPrintFrameAndNCallers(((char *)(longAt(theFP + FoxSavedFP))), n - 1); + } +} + + /* StackInterpreter>>#shortPrintOop: */ +static NoDbgRegParms void +shortPrintOop(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *className; + sqInt length; + char *where; + + length = 0; + if (((oop & (tagMask())) != 0)) { + printImmediateObjecton(oop, GIV(transcript)); + return; + } + if (!(addressCouldBeObj(oop))) { + /* begin printCantBeObject:on: */ + fprintf(GIV(transcript), + "%p%s\n", + ((void *)oop), + ((where = whereIsMaybeCodeThing(oop)) + ? where + : (oop & 7 /* (allocationUnit - 1) */ + ? " is misaligned" + : whereIs(oop)))); + return; + } + if (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + printFreeObjecton(oop, GIV(transcript)); + return; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + printForwarderon(oop, GIV(transcript)); + return; + } + className = nameOfClasslengthInto(fetchClassOfNonImm(oop), (&length)); + fprintf(GIV(transcript), + "%p: a(n) %.*s\n", + ((void *)oop), + ((int) length), + className); +} + + /* StackInterpreter>>#shortReversePrintFrameAndCallers: */ +static NoDbgRegParms sqInt +shortReversePrintFrameAndCallers(char *aFramePointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt caller; + char *callerFP; + char *theFP; + StackPage *thePage; + + callerFP = ((char *) 0); + if (!aFramePointer) { + return GIV(nilObj); + } + theFP = aFramePointer; + while (1) { + shortPrintFrame(theFP); + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!(callerFP != 0)) break; + theFP = callerFP; + } + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + caller = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(caller)); + assert((caller == (nilObject())) + || (isContext(followMaybeForwarded(caller)))); + if ((!((longAt((void *)(caller))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + caller = followForwarded(caller); + } + return caller; +} + + +/* Repaint the portion of the Smalltalk screen bounded by the affected + rectangle. Used to synchronize the screen after a Bitblt to the Smalltalk + Display object. + */ + + /* StackInterpreter>>#showDisplayBits:Left:Top:Right:Bottom: */ +sqInt +showDisplayBitsLeftTopRightBottom(sqInt aForm, sqInt l, sqInt t, sqInt r, sqInt b) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (deferDisplayUpdates + || (aForm != (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheDisplay) << (shiftForWord()))))))))) { + return null; + } + updateDisplayLeftTopRightBottom(l, t, r, b); + return 0; +} + + +/* Signal all requested semaphores. Answer if a context switch has occurred. */ + + /* StackInterpreter>>#signalExternalSemaphores */ +static sqInt +signalExternalSemaphores(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + sqInt xArray; + + xArray = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ExternalObjectsArray) << (shiftForWord())))))); + return doSignalExternalSemaphores( + (/* begin numSlotsOf: */ + assert((classIndexOf(xArray)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(xArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(xArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))); +} + + +/* Answer the size of the CallPrimitive bytecode that may be used to store a + method's primitive. + */ +/* NewsqueakV4: 249 11111001 iiiiiiii jjjjjjjj Call Primitive #iiiiiiii + + (jjjjjjjj * 256) + */ +/* SistaV1: 248 11111000 iiiiiiii mssjjjjj Call Primitive #iiiiiiii + ( + jjjjj * 256) */ +/* V3+Closures: 139 11101111 iiiiiiii jjjjjjjj Call Primitive #iiiiiiii + + (jjjjjjjj * 256) + */ + + /* StackInterpreter>>#sizeOfCallPrimitiveBytecode: */ +sqInt +sizeOfCallPrimitiveBytecode(sqInt methodHeader) +{ + return 3; +} + + +/* 234 11101010 i i i i i i i i Store Temporary Variable #iiiiiiii */ +/* 129 10000001 jjkkkkkk Store (Receiver Variable, Temporary Location, + Illegal, Literal Variable) [jj] #kkkkkk */ + + /* StackInterpreter>>#sizeOfLongStoreTempBytecode: */ +sqInt +sizeOfLongStoreTempBytecode(sqInt methodHeader) +{ + return 2; +} + + +/* Return the number of indexable fields of the given object. This method is + to be called from an automatically generated C primitive. The argument is + assumed to be a pointer to the first indexable field of a words or bytes + object; the object header starts 4 bytes before that. + */ +/* Note: Only called by translated primitive code. */ + + /* StackInterpreter>>#sizeOfSTArrayFromCPrimitive: */ +sqInt +sizeOfSTArrayFromCPrimitive(void *cPtr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + + oop = (oopForPointer(cPtr)) - BaseHeaderSize; + if (!(/* isWordsOrBytes: */ + ((!(oop & (tagMask())))) + && (isWordsOrBytesNonImm(oop)))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin lengthOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + +/* Invoke a normal (non-quick) primitive. + Called under the assumption that primFunctionPointer has been preloaded. */ + + /* StackInterpreter>>#slowPrimitiveResponse */ +static int +slowPrimitiveResponse(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt nArgs; + char *savedFramePointer; + char *savedStackPointer; + + assert(!(isOopForwarded(stackValue(GIV(argumentCount))))); + assert((GIV(remapBufferCount)) == 0); + nArgs = GIV(argumentCount); + savedStackPointer = GIV(stackPointer); + savedFramePointer = GIV(framePointer); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if (/* recordPrimTraceForMethod: */ + (recordPrimTrace()) + && ((primTracePluginName + ? methodHasPrimitiveInPrimTracePlugin(GIV(newMethod)) + : 1))) { + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = GIV(newMethod); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + dispatchFunctionPointer(primitiveFunctionPointer); + assert(maybeLeakCheckExternalPrimCall(GIV(newMethod))); + + /* begin maybeRetryPrimitiveOnFailure */ + if (GIV(primFailCode)) { + retryPrimitiveOnFailure(); + } + if ((!GIV(primFailCode)) + && ((GIV(framePointer) == savedFramePointer) + && (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))))) { + if (GIV(stackPointer) != (savedStackPointer + (nArgs * BytesPerWord))) { + /* This is necessary but insufficient; the result may still have been written to the stack. + At least we'll know something is wrong. */ + failUnbalancedPrimitive(); + GIV(stackPointer) = savedStackPointer; + } + } + + /* Don't fail if primitive has done something radical, e.g. perform: + If we are profiling, take accurate primitive measures */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + return !GIV(primFailCode); +} + + +/* update state of active context */ + + /* StackInterpreter>>#snapshot: */ +static NoDbgRegParms sqInt +snapshot(sqInt embedded) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt activeProc; + CogMethod *cogMethod; + usqInt i; + usqInt initialIP; + sqInt methodHeader; + usqInt numArgs; + usqInt numTemps; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt rcvr; + sqInt rcvrSqInt; + sqInt savedTenuringThreshold; + void *setMacType; + sqInt stackIndex; + + + /* For now the stack munging below doesn't deal with more than one argument. + It can, and should. */ + rcvr = 0; + if (GIV(argumentCount)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + + /* Need to convert all frames into contexts since the snapshot file only holds objects. */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + activeContext = voidVMStateForSnapshotFlushingExternalPrimitivesIf(1); + + /* update state of active process */ + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, activeProc, activeContext)); + assert(isNonImmediate(activeProc)); + if (oopisGreaterThanOrEqualTo(activeProc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeContext & (tagMask())))) + && (oopisLessThan(activeContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(activeProc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(activeProc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),activeContext); + GIV(tempOop) = activeContext; + + /* begin garbageCollectForSnapshot */ + /* begin flushNewSpace */ + savedTenuringThreshold = GIV(tenureThreshold); + GIV(tenureThreshold) = GIV(oldSpaceStart); + scavengingGCTenuringIf(TenureByAge); + + /* begin setRawTenuringThreshold: */ + GIV(tenureThreshold) = savedTenuringThreshold; + assert((GIV(rememberedSetSize)) == 0); + assert(GIV(pastSpaceStart) == (((GIV(pastSpace)).start))); + assert(GIV(freeStart) == (((GIV(eden)).start))); + + /* begin biasForSnapshot */ + GIV(biasForGC) = 0; + fullGC(); + + /* begin biasForGC */ + GIV(biasForGC) = 1; + unfireQueuedEphemeronsForSnapshot(); + prepareForSnapshot(); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + + /* Nothing moves from here on so it is safe to grab the activeContext again. */ + activeContext = GIV(tempOop); + GIV(tempOop) = 0; + if (!GIV(primFailCode)) { + /* Without contexts or stacks simulate + rcvr := self popStack. ''pop rcvr'' + self push: trueObj. + to arrange that the snapshot resumes with true. N.B. stackIndex is one-relative. */ + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + stackIndex = (oop >> 3); + rcvr = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(((stackIndex + CtxtTempFrameStart) - 1)) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(activeContext)) + && (!(isForwarded(activeContext)))); + assert(validStorePointerUncheckedArgs((stackIndex + CtxtTempFrameStart) - 1, activeContext, GIV(trueObj))); + longAtput((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(((stackIndex + CtxtTempFrameStart) - 1)) << (shiftForWord()))))),GIV(trueObj)); + + /* now attempt to write the snapshot file */ + writeImageFileIO(); + if ((!GIV(primFailCode)) + && (!embedded)) { + /* set Mac file type and creator; this is a noop on other platforms */ + setMacType = ioLoadFunctionFrom("setMacFileTypeAndCreator", "FilePlugin"); + if (setMacType) { + ((sqInt (*)(char *, char *, char *))setMacType)(imageName, "STim", "FAST"); + } + } + + /* Without contexts or stacks simulate + self pop: 1 */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(activeContext)) + && (!(isForwarded(activeContext)))); + assert(validStorePointerUncheckedArgs(StackPointerIndex, activeContext, (((usqInt)(stackIndex - 1) << 3) | 1))); + longAtput((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord()))))),(((usqInt)(stackIndex - 1) << 3) | 1)); + } + + /* begin postSnapshot */ + restorePostSnapshot(); + refireQueuedEphemeronsPostSnapshot(); + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + if (GIV(primFailCode)) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + cogMethod = null; + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + methodHeader = (cogMethod->methodHeader); + cogMethod = null; + } + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeader)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvrSqInt = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(addressCouldBeOop(rcvrSqInt)); + assert(!(isOopForwarded(rcvrSqInt))); + if (cogMethod) { + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + } + GIV(instructionPointer) = ceReturnToInterpreterPC(); + } + cogMethod = null; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + initialIP = ((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize; + if (cogMethod) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)cogMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + GIV(instructionPointer) = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + GIV(instructionPointer) = initialIP - 1; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),rcvrSqInt); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + if (!cogMethod) { + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + } + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeader); + } + } + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(falseObj)); + } + return 0; +} + + /* StackInterpreter>>#specialSelector: */ +sqInt +specialSelector(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin fetchPointer:ofObject: */ + return longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)((index * 2)) << (shiftForWord())))))); +} + + +/* Insulate the stack zone from the effects of a become. + All receivers must be unfollowed for two reasons: + 1. inst var access is direct with no read barrier + 2. super sends (always to the receiver) have no class check and so don't + trap for forwarded receivers. This is an issue for primitives that assume + their receiver + is valid and don't validate. + Super sends require an explicit check to ensure receivers in super sends + are unforwarded. + e.g. super doSomethingWith: (self become: other) forwards the receiver + self pushed on the + stack. So we could avoid following non-pointer receivers. But this is too + tricky, Instead, we + always follow receivers. + Methods must be unfollowed since bytecode access is direct with no read + barrier. But this only needs to be done if the becomeEffectsFlags indicate + that a + CompiledMethod was becommed. + The scheduler state must be followed, but only if the becomeEffectsFlags + indicate that a pointer object was becommed. */ +/* For VM profiling */ + + /* StackInterpreter>>#spurPostBecomeAction: */ +static NoDbgRegParms void +spurPostBecomeAction(sqInt theBecomeEffectsFlags) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqIntptr_t c; + sqInt first; + int i; + sqInt ipdelta; + sqInt iSqInt; + sqInt last; + sqInt list; + sqIntptr_t m; + sqInt next; + usqInt numSlots; + sqInt obj; + sqInt procLists; + sqInt referent; + sqInt result; + sqIntptr_t s; + sqInt sched; + sqInt schedAssoc; + sqInt toDoLimit; + sqInt xArray; + + if (theBecomeEffectsFlags) { + if (((theBecomeEffectsFlags & BecameActiveClassFlag) != 0)) { + /* begin flushBecommedClassesInMethodCache */ + for (i = 0; i < MethodCacheSize; i += MethodCacheEntrySize) { + c = GIV(methodCache)[i + MethodCacheClass]; + s = GIV(methodCache)[i + MethodCacheSelector]; + if ((c != 0) + && ((s != 0) + && (isForwarded(classOrNilAtIndex(c))))) { + GIV(methodCache)[i + MethodCacheClass] = 0; + GIV(methodCache)[i + MethodCacheSelector] = 0; + } + } + unlinkSendsLinkedForInvalidClasses(); + } + if (((theBecomeEffectsFlags & BecamePointerObjectFlag) != 0)) { + /* begin followForwardingPointersInScheduler */ + schedAssoc = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord())))))); + + /* the GC follows pointers in the special objects array for us. */ + assert(!(isForwarded(schedAssoc))); + + /* Make sure the active process has been followed. */ + followForwardedObjectFieldstoDepth(schedAssoc, 1); + sched = longAt((void *)((schedAssoc + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin followObjField:ofObject: */ + procLists = longAt((void *)((sched + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + assert(isNonImmediate(procLists)); + if ((!((longAt((void *)(procLists))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + procLists = fixFollowedFieldofObjectwithInitialValue(ProcessListsIndex, sched, procLists); + } + + /* Follow all links in the process list to ensure the lists are valid. */ + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(procLists)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(procLists + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(procLists - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (iSqInt = 0; iSqInt <= toDoLimit; iSqInt += 1) { + /* begin followObjField:ofObject: */ + list = longAt((void *)((procLists + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + assert(isNonImmediate(list)); + if ((!((longAt((void *)(list))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + list = fixFollowedFieldofObjectwithInitialValue(iSqInt, procLists, list); + } + + /* begin followObjField:ofObject: */ + first = longAt((void *)((list + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(first)); + if ((!((longAt((void *)(first))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + first = fixFollowedFieldofObjectwithInitialValue(FirstLinkIndex, list, first); + } + + /* begin followObjField:ofObject: */ + last = longAt((void *)((list + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(last)); + if ((!((longAt((void *)(last))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + last = fixFollowedFieldofObjectwithInitialValue(LastLinkIndex, list, last); + } + while (first != last) { + /* begin followObjField:ofObject: */ + next = longAt((void *)((first + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(next)); + if ((!((longAt((void *)(next))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + next = fixFollowedFieldofObjectwithInitialValue(NextLinkIndex, first, next); + } + first = next; + } + } + + /* begin followForwardingPointersInSpecialObjectsArray */ + /* begin followSemaphoreIn:at: */ + obj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheLowSpaceSemaphore) << (shiftForWord())))))); + if ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + obj = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheLowSpaceSemaphore, GIV(specialObjectsOop), obj)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheLowSpaceSemaphore) << (shiftForWord()))))),obj); + } + + /* begin followSemaphoreIn:at: */ + obj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheInterruptSemaphore) << (shiftForWord())))))); + if ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + obj = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheInterruptSemaphore, GIV(specialObjectsOop), obj)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheInterruptSemaphore) << (shiftForWord()))))),obj); + } + + /* begin followSemaphoreIn:at: */ + obj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord())))))); + if ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + obj = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheTimerSemaphore, GIV(specialObjectsOop), obj)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord()))))),obj); + } + + /* begin followSemaphoreIn:at: */ + obj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheFinalizationSemaphore) << (shiftForWord())))))); + if ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + obj = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheFinalizationSemaphore, GIV(specialObjectsOop), obj)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheFinalizationSemaphore) << (shiftForWord()))))),obj); + } + xArray = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ExternalObjectsArray) << (shiftForWord())))))); + if ((!((longAt((void *)(xArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(xArray)); + referent = longAt((void *)((xArray + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + xArray = referent; + + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ExternalObjectsArray, GIV(specialObjectsOop), xArray)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(xArray & (tagMask())))) + && (oopisLessThan(xArray, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ExternalObjectsArray) << (shiftForWord()))))),xArray); + } + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(xArray)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(xArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(xArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (ipdelta = 0; ipdelta <= toDoLimit; ipdelta += 1) { + /* begin followSemaphoreIn:at: */ + obj = longAt((void *)((xArray + BaseHeaderSize) + ((((usqInt)(ipdelta) << (shiftForWord())))))); + if ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + obj = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ipdelta, xArray, obj)); + assert(isNonImmediate(xArray)); + if (oopisGreaterThanOrEqualTo(xArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(xArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(xArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((xArray + BaseHeaderSize) + ((((usqInt)(ipdelta) << (shiftForWord()))))),obj); + } + } + } + if (((theBecomeEffectsFlags & (BecamePointerObjectFlag + BecameCompiledMethodFlag)) != 0)) { + /* begin followForwardingPointersInProfileState */ + /* begin profileStateDoUpdating: */ + if (GIV(profileProcess)) { + if ((result = ((!((longAt((void *)(GIV(profileProcess)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileProcess)) + : 0))) { + GIV(profileProcess) = result; + } + } + if (GIV(profileMethod)) { + if ((result = ((!((longAt((void *)(GIV(profileMethod)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileMethod)) + : 0))) { + GIV(profileMethod) = result; + } + } + if (GIV(profileSemaphore)) { + if ((result = ((!((longAt((void *)(GIV(profileSemaphore)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileSemaphore)) + : 0))) { + GIV(profileSemaphore) = result; + } + } + if (((theBecomeEffectsFlags & BecameCompiledMethodFlag) != 0)) { + /* begin followForwardedMethodsInMethodCache */ + for (i = 0; i < MethodCacheSize; i += MethodCacheEntrySize) { + c = GIV(methodCache)[i + MethodCacheClass]; + s = GIV(methodCache)[i + MethodCacheSelector]; + m = GIV(methodCache)[i + MethodCacheMethod]; + if ((c != 0) + && ((s != 0) + && ((m != 0) + && (/* isOopForwarded: */ + ((!(m & (tagMask())))) + && ((!((longAt((void *)(m))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(m)); + referent = longAt((void *)((m + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + m = referent; + GIV(methodCache)[i + MethodCacheMethod] = m; + } + } + + /* begin followMethodNewMethodAndInstructionPointer */ + ipdelta = 0; + if ((!((longAt((void *)(GIV(method)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + ipdelta = (/* method:includesAddress: */ + (GIV(instructionPointer) > GIV(method)) + && (GIV(instructionPointer) < (GIV(method) + ((((numSlotsOfAny(GIV(method))) << (shiftForWord()))) + BaseHeaderSize))) + ? GIV(instructionPointer) - GIV(method) + : 0); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(GIV(method))); + referent = longAt((void *)((GIV(method) + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + GIV(method) = referent; + if (ipdelta) { + GIV(instructionPointer) = GIV(method) + ipdelta; + } + } + if (/* isOopForwarded: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((!((longAt((void *)(GIV(newMethod)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ipdelta = ((!ipdelta) + && (/* method:includesAddress: */ + (GIV(instructionPointer) > GIV(newMethod)) + && (GIV(instructionPointer) < (GIV(newMethod) + ((((numSlotsOfAny(GIV(newMethod))) << (shiftForWord()))) + BaseHeaderSize)))) + ? GIV(instructionPointer) - GIV(newMethod) + : 0); + + /* don't relocate twice!! */ + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(GIV(newMethod))); + referent = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + GIV(newMethod) = referent; + } + + /* maybe oop due to object-as-method */ + if (ipdelta) { + GIV(instructionPointer) = GIV(newMethod) + ipdelta; + } + } + } + } + followForwardingPointersOfReceiversInStackZone(); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackFloatValue: */ +double +stackFloatValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return floatValueOf(longAt(GIV(stackPointer) + (offset * BytesPerWord))); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackIntegerValue: */ +sqInt +stackIntegerValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerPointer; + + integerPointer = longAt(GIV(stackPointer) + (offset * BytesPerWord)); + + /* begin checkedIntegerValueOf: */ + if ((((integerPointer) & 7) == 1)) { + return (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } +} + + +/* Answer the actual stackLimit offset in a page. Since stackPageByteSize may + have chosen to shrink a page + this may be less than stackPageFrameBytes, but it should be no more than + stackPageFrameBytes. + */ + + /* StackInterpreter>>#stackLimitBytes */ +static sqInt +stackLimitBytes(void) +{ + return (((stackPageFrameBytes()) < (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))) ? (stackPageFrameBytes()) : (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))); +} + + +/* Ensures that the given object is a real object, not a SmallInteger. */ +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackMutableObjectValue: */ +sqInt +stackMutableObjectValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = longAt(GIV(stackPointer) + (offset * BytesPerWord)); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(oop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + } + return oop; +} + + +/* Ensures that the given object is a real object, not a SmallInteger. */ +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackObjectValue: */ +sqInt +stackObjectValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = longAt(GIV(stackPointer) + (offset * BytesPerWord)); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return oop; +} + + +/* Answer a page size that is a power-of-two and contains a useful number of + frames. Room for 256 slots for frames gives around 40 frames a page which + is a + good compromise between overflow rate and latency in divorcing a page. */ + + /* StackInterpreter>>#stackPageByteSize */ +static sqInt +stackPageByteSize(void) +{ + sqInt largeSize; + sqInt pageBytes; + sqInt smallSize; + + pageBytes = ((stackPageFrameBytes()) + (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) + (osCogStackPageHeadroom()); + if (!(pageBytes & (pageBytes - 1))) { + return pageBytes; + } + + /* = 0 => a power of two + round up or round down; that is the question. If rounding down reduces + the size by no more than 1/8th round down, otherwise roundup. */ + largeSize = 1ULL << (highBit(pageBytes)); + smallSize = 1ULL << ((highBit(pageBytes)) - 1); + assert((largeSize > pageBytes) + && (pageBytes > smallSize)); + return ((pageBytes - smallSize) <= (smallSize / 8) + ? smallSize + : largeSize); +} + + +/* Return the stackPointer of a Context or BlockContext. */ + + /* StackInterpreter>>#stackPointerForMaybeMarriedContext: */ +static NoDbgRegParms sqInt +stackPointerForMaybeMarriedContext(sqInt aContext) +{ + sqInt sp; + sqInt spSqInt; + + if (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(aContext)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(aContext))); + return sp; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + return 0; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + return (spSqInt >> 3); +} + + +/* Return the 0-based index rel to the given frame. + (This is what stackPointer used to be before conversion to pointer) */ +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackPointerIndexForFrame: */ +static NoDbgRegParms sqInt +stackPointerIndexForFrame(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *thePage; + char *theSP; + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + theSP = findSPOfon(theFP, thePage); + return /* stackPointerIndexForFrame:WithSP: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (((usqInt)(((theFP + FoxMFReceiver) - theSP))) >> (shiftForWord())) + (((mframeCogMethod(theFP))->cmNumArgs)) + : (((usqInt)(((theFP + FoxIFReceiver) - theSP))) >> (shiftForWord())) + (byteAt((theFP + FoxIFrameFlags) + 1))); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackPositiveMachineIntegerValue: */ +usqIntptr_t +stackPositiveMachineIntegerValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerPointer; + sqInt value; + + integerPointer = longAt(GIV(stackPointer) + (offset * BytesPerWord)); + + /* begin positiveMachineIntegerValueOf: */ + if ((((integerPointer) & 7) == 1)) { + value = (integerPointer >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return value; + } + + /* don't inline the rare case */ + return positiveMachineIntegerValueOfObj(integerPointer); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackSignedMachineIntegerValue: */ +sqIntptr_t +stackSignedMachineIntegerValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerPointer; + + integerPointer = longAt(GIV(stackPointer) + (offset * BytesPerWord)); + + /* begin signedMachineIntegerValueOf: */ + if ((((integerPointer) & 7) == 1)) { + return (integerPointer >> 3); + } + return signedMachineIntegerValueOfObj(integerPointer); +} + + /* StackInterpreter>>#stackTop */ +sqInt +stackTop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt(GIV(stackPointer)); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackValue: */ +sqInt +stackValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt(GIV(stackPointer) + (offset * BytesPerWord)); +} + + +/* Zero-relative version of CompiledMethod>>startpc. */ + + /* StackInterpreter>>#startPCOfMethod: */ +usqInt +startPCOfMethod(sqInt aCompiledMethod) +{ + return ((literalCountOf(aCompiledMethod)) + LiteralStart) * BytesPerOop; +} + + +/* Return what ST would return for at: index. */ + + /* StackInterpreter>>#stObject:at: */ +sqInt +stObjectat(sqInt array, sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt sp; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + + hdr = long64At((void *)(array)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(array + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(array)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(array)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(array))); + stSize = sp; + goto l3; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l3; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(array))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l3: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(array)))))) { + return temporaryin(index - 1, frameOfMarriedContext(array)); + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(array)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + return (((usqInt)(byteAt((void *)((array + BaseHeaderSize) + (index - 1)))) << 3) | 1); + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(array); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + return longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << (shiftForWord())))))); + } + if (fmt >= (firstByteFormat())) { + return (((usqInt)(byteAt((void *)((array + BaseHeaderSize) + ((index + fixedFields) - 1)))) << 3) | 1); + } + if (fmt >= (firstShortFormat())) { + return (((usqInt)(((unsigned short) (shortAt((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))))))) << 3) | 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return positive64BitIntegerFor(long64At((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))))); + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + return (((usqInt)((((usqInt)(long32At((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + return 0; +} + + +/* Do what ST would return for at: index put: value. */ + + /* StackInterpreter>>#stObject:at:put: */ +sqInt +stObjectatput(sqInt array, sqInt index, sqInt value) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fieldIndex; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt signedValueToStore; + sqInt signedValueToStoreSqInt; + sqInt sp; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + usqLong unsigned64BitValueToStore; + unsigned int unsignedValueToStore; + + hdr = long64At((void *)(array)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(array + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l1: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(array)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(array)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(array))); + stSize = sp; + goto l2; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l2; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(array))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l2: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(array)))))) { + return temporaryinput(index - 1, frameOfMarriedContext(array), value); + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(array)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + if (((((value) & 7) == 1)) + && ((((usqInt)((signedValueToStore = (value >> 3))))) <= 0xFF)) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((array + BaseHeaderSize) + (index - 1)),signedValueToStore); + return value; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return value; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l3; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l3; + } + class = fetchClassOfNonImm(array); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l3: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:storing:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + fieldIndex = (index + fixedFields) - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, array, value)); + assert(isNonImmediate(array)); + if (oopisGreaterThanOrEqualTo(array, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(array + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(array); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),value); + goto l4; + } + if (fmt >= (firstByteFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((array + BaseHeaderSize) + ((index + fixedFields) - 1)),signedValueToStoreSqInt); + goto l4; + } + if (fmt >= (firstShortFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))),signedValueToStoreSqInt); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l4; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l4: + return value; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + return value; +} + + +/* Set the sender of a frame. If the frame is a base frame then this is + trivial; merely store into the FoxCallerSavedIP/FoxCallerContext field. If + not, then + split the stack at the frame, moving the frame and those hotter than it to + a new stack page. In the new stack page the frame will be the base frame + and storing trivial. Answer the possibly changed location of theFP. */ + + /* StackInterpreter>>#storeSenderOfFrame:withValue: */ +static NoDbgRegParms char * +storeSenderOfFramewithValue(char *theFP, sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + char *callerFP; + StackPage *newPage; + int onCurrentPage; + char *theMovedFP; + StackPage *thePage; + StackPage *thePage1; + + if (!(longAt(theFP + FoxSavedFP))) { + /* begin frameCallerContext:put: */ + assert((anOop == (nilObject())) + || (isContext(anOop))); + assert(isBaseFrame(theFP)); + assert(((theFP + (frameStackedReceiverOffset(theFP))) + (2 * BytesPerWord)) == (((stackPageFor(theFP))->baseAddress))); + assert((longAt((theFP + (frameStackedReceiverOffset(theFP))) + BytesPerWord)) == (frameContext(theFP))); + longAtput((theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + (2 * BytesPerWord),anOop); + return theFP; + } + + /* begin ensureCallerContext: */ + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!callerFP) { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerContextOrNil = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + goto l2; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP))); + + /* frameContext: */ + goto l2; + } + marryFrameSP( + callerFP, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFP))), + (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + /* end ensureCallerContext: */ +l2: + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + onCurrentPage = thePage == GIV(stackPage); + if (!onCurrentPage) { + /* Make sure the frame's page isn't divorced when a new page is allocated. */ + markStackPageNextMostRecentlyUsed(thePage); + } + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l1; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l1: + theMovedFP = moveFramesInthroughtoPage(thePage, theFP, newPage); + if (onCurrentPage) { + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + } + else { + markStackPageMostRecentlyUsed(newPage); + } + assert(isBaseFrame(theMovedFP)); + + /* begin frameCallerContext:put: */ + assert((anOop == (nilObject())) + || (isContext(anOop))); + assert(isBaseFrame(theMovedFP)); + assert(((theMovedFP + (frameStackedReceiverOffset(theMovedFP))) + (2 * BytesPerWord)) == (((stackPageFor(theMovedFP))->baseAddress))); + assert((longAt((theMovedFP + (frameStackedReceiverOffset(theMovedFP))) + BytesPerWord)) == (frameContext(theMovedFP))); + longAtput((theMovedFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theMovedFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theMovedFP))->cmNumArgs) + : byteAt((theMovedFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + (2 * BytesPerWord),anOop); + return theMovedFP; +} + + +/* Answer the number of indexable fields in the given object. (i.e., what + Smalltalk would return for size). + */ +/* Note: Assume oop is not an immediate! */ + + /* StackInterpreter>>#stSizeOf: */ +sqInt +stSizeOf(sqInt oop) +{ + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt totalLength; + + hdr = long64At((void *)(oop)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l1: + if (/* isPureBitsFormat: */ + (fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat()))) { + return totalLength; + } + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l2; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l2; + } + class = fetchClassOfNonImm(oop); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l2: + if (fmt == (indexablePointersFormat())) { + assert(!(isContextHeader(hdr))); + } + return totalLength - fixedFields; +} + + +/* Using a read barrier here simplifies the become implementation and costs + very little because the class index and superclass almost certainly share + a cache line. */ + + /* StackInterpreter>>#superclassOf: */ +sqInt +superclassOf(sqInt classPointer) +{ + sqInt objOop; + + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, objOop); + } + return objOop; +} + + +/* Signal the given semaphore from within the interpreter. + Answer if the current process was preempted. */ + + /* StackInterpreter>>#synchronousSignal: */ +static NoDbgRegParms sqInt +synchronousSignal(sqInt aSemaphore) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + sqLong excessSignals; + sqInt proc; + sqInt referent; + + assert((addressCouldBeObj(aSemaphore)) + && ((!(isForwarded(aSemaphore))) + && ((numSlotsOf(aSemaphore)) > ExcessSignalsIndex))); + + /* begin isEmptyList: */ + assert(!(isForwarded(aSemaphore))); + if ((longAt((void *)((aSemaphore + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj)) { + /* no process is waiting on this semaphore */ + excessSignals = fetchIntegerofObject(ExcessSignalsIndex, aSemaphore); + storeIntegerofObjectwithValue(ExcessSignalsIndex, aSemaphore, excessSignals + 1); + return 0; + } + + /* begin ensureSemaphoreUnforwardedThroughContext: */ + proc = longAt((void *)((aSemaphore + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + if ((!((longAt((void *)(proc))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + followForwardedObjectFieldstoDepth(aSemaphore, 1); + proc = longAt((void *)((aSemaphore + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + } + assert(!((isForwarded(proc)))); + ctxt = longAt((void *)((proc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if ((!((longAt((void *)(ctxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(ctxt)); + referent = longAt((void *)((ctxt + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + ctxt = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, proc, ctxt)); + assert(isNonImmediate(proc)); + if (oopisGreaterThanOrEqualTo(proc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(ctxt & (tagMask())))) + && (oopisLessThan(ctxt, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(proc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(proc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((proc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),ctxt); + } + return resumepreemptedYieldingIffrom(removeFirstLinkOfList(aSemaphore), (!(imageHeaderFlags & 16)), CSSynchronousSignal); +} + + /* StackInterpreter>>#tempCountOf: */ +usqInt +tempCountOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin temporaryCountOfMethodHeader: */ + return (((usqInt)(header)) >> MethodHeaderTempCountShift) & 0x3F; +} + + /* StackInterpreter>>#temporaryCountOfMethodHeader: */ +usqInt +temporaryCountOfMethodHeader(sqInt header) +{ + return (((usqInt)(header)) >> MethodHeaderTempCountShift) & 0x3F; +} + + /* StackInterpreter>>#ultimateLiteralOf: */ +sqInt +ultimateLiteralOf(sqInt aMethodOop) +{ + sqInt offset; + + assert(isOopCompiledMethod(aMethodOop)); + offset = (literalCountOf(aMethodOop)) - 1; + + /* begin literal:ofMethod: */ + return longAt((void *)((aMethodOop + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); +} + + +/* Follow the first literal of either a primitiveCallout or + primitiveExternalCall primitive method. This + will be an ExternalFunction for primitiveCallout or a four element Array + for primitiveExternalCall. + This is here to avoid following all the literals in a method, which would + be slow. Remember + forwarders are unlikely, so we only want to follow what is necessary, and + for an FFI call or + external primitive only the first literal is salient. */ + + /* StackInterpreter>>#unfollowFirstLiteralOfMaybeCalloutMethod:primitiveIndex: */ +static NoDbgRegParms sqInt +unfollowFirstLiteralOfMaybeCalloutMethodprimitiveIndex(sqInt methodObj, sqInt primIndex) +{ + sqInt firstLiteral; + sqInt found; + + found = 0; + + /* inlined self literal: 0 ofMethod: methodObj for clarity... */ + firstLiteral = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(LiteralStart) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(firstLiteral & (tagMask())))) + && ((!((longAt((void *)(firstLiteral))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + found = 1; + firstLiteral = fixFollowedFieldofObjectwithInitialValue(LiteralStart, methodObj, firstLiteral); + } + if (followForwardedObjectFieldstoDepth(firstLiteral, (primIndex == PrimNumberFFICall + ? (((sqInt)((primitiveMetadataTable[PrimNumberFFICall]))) >> SpurPrimitiveAccessorDepthShift) - 1 + : 0))) { + found = 1; + } + return found; +} + + +/* Repaint the portion of the Smalltalk screen bounded by the affected + rectangle. Used to synchronize the screen after a Bitblt to the Smalltalk + Display object. */ + + /* StackInterpreter>>#updateDisplayLeft:Top:Right:Bottom: */ +static NoDbgRegParms sqInt +updateDisplayLeftTopRightBottom(sqInt l, sqInt t, sqInt r, sqInt b) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bottom; + sqInt left; + sqInt right; + sqInt surfaceHandle; + sqInt top; + + left = ((l < 0) ? 0 : l); + right = ((r < displayWidth) ? r : displayWidth); + top = ((t < 0) ? 0 : t); + bottom = ((b < displayHeight) ? b : displayHeight); + if (!((left <= right) + && (top <= bottom))) { + return null; + } + if (((((((sqInt)displayBits))) & 7) == 1)) { + surfaceHandle = ((((sqInt)displayBits)) >> 3); + if (!showSurfaceFn) { + showSurfaceFn = ioLoadFunctionFrom("ioShowSurface", "SurfacePlugin"); + if (!showSurfaceFn) { + return (GIV(primFailCode) = PrimErrNotFound); + } + } + showSurfaceFn(surfaceHandle, left, top, right - left, bottom - top); + } + else { + assert(isNonImmediate(((sqInt)displayBits))); + ioShowDisplay(((sqInt)displayBits), displayWidth, displayHeight, displayDepth, left, right, top, bottom); + } + return 0; +} + + +/* Note that we accept anInstrPointer pointing to a callPrimitiveBytecode + at the start of a method that contains a primitive. This because methods + like Context(Part)>>reset have to be updated to skip the callPrimtiive + bytecode otherwise. */ + + /* StackInterpreter>>#validBCPC:inMethod: */ +static NoDbgRegParms sqInt +validBCPCinMethod(sqInt thePC, usqInt aMethod) +{ + return (thePC >= (((literalCountOf(aMethod)) + LiteralStart) * BytesPerOop)) + && (thePC < (numBytesOfBytes(aMethod))); +} + + +/* Note that we accept anInstrPointer pointing to a callPrimitiveBytecode + at the start of a method that contains a primitive. This because methods + like Context(Part)>>reset have to be updated to skip the callPrimtiive + bytecode otherwise. */ + + /* StackInterpreter>>#validInstructionPointer:inFrame: */ +static NoDbgRegParms sqInt +validInstructionPointerinFrame(usqInt anInstrPointer, char *fp) +{ + return validInstructionPointerinMethodframePointer(anInstrPointer, /* frameMethodObject: */ + ((((usqInt)(longAt(fp + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(fp))->methodObject) + : longAt(fp + FoxMethod)), fp); +} + + +/* Check that the base frames in all in-use stack pages have a sender and a + saved context. + */ + + /* StackInterpreter>>#validStackPageBaseFrames */ +static sqInt +validStackPageBaseFrames(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *aPage; + sqInt i; + + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + aPage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(aPage))) { + if (!(validStackPageBaseFrame(aPage))) { + return 0; + } + } + } + return 1; +} + + +/* Void the state associated with the long-running primitive check. + This is done when a new semaphore is installed or when it appears + that is longRunningPrimitiveCheckMethod is invalid, e.g. because it + has eben sampled in the middle of a GC. */ + + /* StackInterpreter>>#voidLongRunningPrimitive: */ +#if LRPCheck +static NoDbgRegParms NeverInline void +voidLongRunningPrimitive(char *reason) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(longRunningPrimitiveCheckMethod) = null; + GIV(longRunningPrimitiveStartUsecs) = (GIV(longRunningPrimitiveStopUsecs) = 0); + GIV(longRunningPrimitiveSignalUndelivered) = 1; + sqLowLevelMFence(); +} +#endif /* LRPCheck */ + + +/* Find the highest priority process that is ready to run and + remove it from its list. + To save time looking at many empty lists before finding a + runnable process the VM maintains a variable holding the + highest priority runnable process. If this variable is 0 then the + VM does not know the highest priority and must search all lists. + Answer nil if no process can be found. */ + + /* StackInterpreter>>#wakeHighestPriority */ +static sqInt +wakeHighestPriority(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + usqInt numSlots; + sqInt objOop; + sqInt p; + sqInt proc; + sqInt processList; + sqInt schedLists; + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + schedLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + p = (GIV(highestRunnableProcessPriority) + ? GIV(highestRunnableProcessPriority) + : (/* begin numSlotsOf: */ + assert((classIndexOf(schedLists)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(schedLists + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(schedLists - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))); + while (((p -= 1)) >= 0) { + processList = longAt((void *)((schedLists + BaseHeaderSize) + ((((usqInt)(p) << (shiftForWord())))))); + while (1) { + /* begin isEmptyList: */ + assert(!(isForwarded(processList))); + if ((longAt((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj)) break; + /* Only answer processes with a runnable suspendedContext. + Discard those that aren't; the VM would crash otherwise. */ + proc = removeFirstLinkOfList(processList); + ctxt = longAt((void *)((proc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if (isLiveContext(ctxt)) { + GIV(highestRunnableProcessPriority) = p + 1; + return proc; + } + + /* This is uncommon, so we can deal with forwarders here instead of assuming there isn't. */ + if (/* isOopForwarded: */ + ((!(ctxt & (tagMask())))) + && ((!((longAt((void *)(ctxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ctxt = fixFollowedFieldofObjectwithInitialValue(SuspendedContextIndex, proc, ctxt); + } + if (isLiveContext(ctxt)) { + GIV(highestRunnableProcessPriority) = p + 1; + return proc; + } + warning("evicted zombie process from run queue"); + } + } + return null; +} + + +/* Write the image header and heap contents to imageFile for snapshot. c.f. + writeImageFileIOSimulation. The game below is to maintain 64-bit alignment + for all putLong:toFile: occurrences. */ + + /* StackInterpreter>>#writeImageFileIO */ +static NeverInline sqInt +writeImageFileIO(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bytesWritten; + sqImageFile f; + sqInt headerSize; + squeakFileOffsetType headerStart; + sqInt i; + sqInt imageBytes; + extern char imageName[]; + sqInt okToWrite; + void *sCWIfn; + + /* If the security plugin can be loaded, use it to check for write permission. + If not, assume it's ok */ + sCWIfn = ioLoadFunctionFrom("secCanWriteImage", "SecurityPlugin"); + if (sCWIfn) { + okToWrite = ((sqInt (*)(void))sCWIfn)(); + if (!okToWrite) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + } + + /* local constants */ + headerStart = 0; + + /* 64 or 128; header size in bytes; do not change! */ + headerSize = BytesPerWord * 16; + f = sqImageFileOpen(imageName, "wb"); + if (invalidSqImageFile(f)) { + return (GIV(primFailCode) = PrimErrOperationFailed); + } + + /* could not open the image file for writing */ + + /* begin imageSizeToWrite */ + assert(newSpaceIsEmpty()); + imageBytes = 0; + for (i = 0; i < GIV(numSegments); i += 1) { + if ((((GIV(segments)[i]).segSize)) > (2 * BaseHeaderSize)) { + imageBytes += ((GIV(segments)[i]).segSize); + } + } + headerStart = sqImageFileStartLocation(f, imageName, headerSize + imageBytes); + /* Note: on Unix systems one could put an exec command here, padded to 512 bytes */; + + /* position file to start of header */ + sqImageFileSeek(f, headerStart); + putWord32toFile(/* imageFormatVersionForSnapshot */ + (GIV(multipleBytecodeSetsActive) + ? 68533 /* (imageFormatVersion bitOr: MultipleBytecodeSetsBitmask) */ + : 68021 /* imageFormatVersion */), f); + putWord32toFile(headerSize, f); + putLongtoFile(imageBytes, f); + putLongtoFile(GIV(oldSpaceStart), f); + putLongtoFile(GIV(specialObjectsOop), f); + putLongtoFile(GIV(lastHash), f); + putLongtoFile(getSnapshotScreenSize(), f); + putLongtoFile(imageHeaderFlags, f); + putWord32toFile(extraVMMemory, f); + putShorttoFile(desiredNumStackPages, f); + putShorttoFile((desiredCogCodeSize + 0x3FF) / 0x400, f); + putWord32toFile(desiredEdenBytes, f); + putShorttoFile((GIV(maxExtSemTabSizeSet) + ? ioGetMaxExtSemTableSize() + : 0), f); + putShorttoFile(GIV(the2ndUnknownShort), f); + putLongtoFile(((GIV(segments)[0]).segSize), f); + putLongtoFile(GIV(totalFreeOldSpace), f); + putLongtoFile(0, f); + putLongtoFile(0, f); + putLongtoFile(0, f); + putLongtoFile(0, f); + putLongtoFile(0, f); + assert((headerStart + headerSize) == (sqImageFilePosition(f))); + + /* position file after the header */ + sqImageFileSeek(f, headerStart + headerSize); + if (GIV(primFailCode)) { + sqImageFileClose(f); + return null; + } + + /* file write or seek failure + write the image data */ + bytesWritten = writeImageSegmentsToFile(f); + + /* begin success: */ + if (!(bytesWritten == imageBytes)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + sqImageFileClose(f); + return 0; +} + + /* StackInterpreterPrimitives>>#cloneContext: */ +static NoDbgRegParms usqInt +cloneContext(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt cloned; + sqInt fieldIndex; + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + sqInt i; + usqInt newObj; + usqInt numBytes; + usqInt numSlots; + sqInt senderOop; + sqInt sp; + char *spouseFP; + usqInt sz; + sqInt valuePointer; + + /* begin numSlotsOf: */ + assert((classIndexOf(aContext)) > (isForwardedObjectClassIndexPun())); + sz = (((numSlots = byteAt((void *)(aContext + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aContext - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + + /* begin eeInstantiateMethodContextSlots: */ + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(sz < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((sz < 1 + ? 8 /* allocationUnit */ + : sz * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + cloned = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) sz)) << (numSlotsFullShift()))) + ((((usqInt)((indexablePointersFormat())) << (formatShift()))))) + ClassMethodContextCompactIndex); + GIV(freeStart) += numBytes; + cloned = newObj; + /* end eeInstantiateMethodContextSlots: */ +l1: + assert(!((cloned == 0))); + for (i = 0; i <= StackPointerIndex; i += 1) { + valuePointer = externalInstVarofContext(i, aContext); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(cloned)) + && (!(isForwarded(cloned)))); + assert(validStorePointerUncheckedArgs(i, cloned, valuePointer)); + longAtput((void *)((cloned + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + for (i = MethodIndex; i <= ReceiverIndex; i += 1) { + valuePointer = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(cloned)) + && (!(isForwarded(cloned)))); + assert(validStorePointerUncheckedArgs(i, cloned, valuePointer)); + longAtput((void *)((cloned + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + if (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + sp = (stackPointerIndexForFrame(spouseFP)) - 1; + for (i = 0; i <= sp; i += 1) { + fieldIndex = i + CtxtTempFrameStart; + valuePointer = /* temporary:in: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? (i < ((frameNumArgs = ((mframeCogMethod(spouseFP))->cmNumArgs))) + ? longAt((void *)((spouseFP + FoxCallerSavedIP) + ((frameNumArgs - i) * BytesPerWord))) + : longAt((void *)(((spouseFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs - i) * BytesPerWord)))) + : /* itemporary:in: */ + (i < ((frameNumArgsUsqInt = byteAt((spouseFP + FoxIFrameFlags) + 1))) + ? longAt((void *)((spouseFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt - i) * BytesPerWord))) + : longAt((void *)(((spouseFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt - i) * BytesPerWord))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(cloned)) + && (!(isForwarded(cloned)))); + assert(validStorePointerUncheckedArgs(fieldIndex, cloned, valuePointer)); + longAtput((void *)((cloned + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); + } + } + else { + sp = (fetchStackPointerOf(aContext)) - 1; + for (i = 0; i <= sp; i += 1) { + fieldIndex = i + CtxtTempFrameStart; + valuePointer = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)((i + CtxtTempFrameStart)) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(cloned)) + && (!(isForwarded(cloned)))); + assert(validStorePointerUncheckedArgs(fieldIndex, cloned, valuePointer)); + longAtput((void *)((cloned + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); + } + } + return cloned; +} + + +/* Arrange to answer naked frame pointers for unmarried + senders to avoid reifying contexts in the search. */ + + /* StackInterpreterPrimitives>>#fieldOrSenderFP:ofContext: */ +static NoDbgRegParms sqInt +fieldOrSenderFPofContext(sqInt index, sqInt contextObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt senderOop; + char *spouseFP; + sqInt tempIndex; + + tempIndex = index - CtxtTempFrameStart; + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((contextObj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(contextObj))))) { + if (tempIndex >= (fetchStackPointerOf(contextObj))) { + return GIV(nilObj); + } + else { + return longAt((void *)((contextObj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + } + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextObj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (tempIndex >= (stackPointerIndexForFrame(spouseFP))) { + return GIV(nilObj); + } + return fieldofFrame(index, spouseFP); +} + + +/* Arrange to answer naked frame pointers for unmarried + senders to avoid reifying contexts in the search. */ + + /* StackInterpreterPrimitives>>#field:ofFrame: */ +static NoDbgRegParms sqInt +fieldofFrame(sqInt index, char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + + switch (index) { + case SenderIndex: + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + return (callerFP + ? (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0) + ? (assert(checkIsStillMarriedContextcurrentFP(frameContext(callerFP), null)), + /* frameContext: */ + longAt(callerFP + FoxThisContext)) + : ((sqInt)callerFP)) + : frameCallerContext(theFP)); + + case StackPointerIndex: + case InstructionPointerIndex: + return ConstZero; + + case MethodIndex: + return /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + + case ClosureIndex: + return (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0) + ? frameStackedReceivernumArgs(theFP, /* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1))) + : GIV(nilObj)); + + case ReceiverIndex: + return /* frameReceiver: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? longAt(theFP + FoxMFReceiver) + : longAt(theFP + FoxIFReceiver)); + + default: + assert((((index - CtxtTempFrameStart) >= 0) && ((index - CtxtTempFrameStart) <= (stackPointerIndexForFrame(theFP))))); + return /* temporary:in: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((index - CtxtTempFrameStart) < ((frameNumArgs = ((mframeCogMethod(theFP))->cmNumArgs))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgs - (index - CtxtTempFrameStart)) * BytesPerWord)) + : longAt(((theFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs - (index - CtxtTempFrameStart)) * BytesPerWord))) + : /* itemporary:in: */ + ((index - CtxtTempFrameStart) < ((frameNumArgsUsqInt = byteAt((theFP + FoxIFrameFlags) + 1))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt - (index - CtxtTempFrameStart)) * BytesPerWord)) + : longAt(((theFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt - (index - CtxtTempFrameStart)) * BytesPerWord)))); + } + return 0; +} + + /* StackInterpreterPrimitives>>#isAppropriateForCopyObject: */ +static NoDbgRegParms sqInt +isAppropriateForCopyObject(sqInt oop) +{ + if (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) { + return 0; + } + if (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + return !(/* isStillMarriedContext: */ + (((((longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(oop)))); + } + + /* Note there is no version in CoInterpreterPrimitives such as + (objectMemory isCompiledMethod: oop) ifTrue: + [^(self methodHasCogMethod: oop) not]. + because isPointersNonImm: excludes compiled methods and the + copy loop in primitiveCopyObject cannot handle compiled methods. */ + return 1; +} + + +/* The function has not been loaded yet. Fetch module and function name. */ + + /* StackInterpreterPrimitives>>#linkExternalCall:errInto: */ +static NoDbgRegParms void +(*linkExternalCallerrInto(sqInt externalCallLiteral, sqInt *failPtr))(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*addr)(); + sqInt fmt; + void *functionAddress; + sqInt functionLength; + sqInt functionName; + sqInt i; + sqInt index; + sqInt metadata; + sqInt moduleLength; + sqInt moduleName; + usqInt numSlots; + + metadata = 0; + moduleName = longAt((void *)((externalCallLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralModuleNameIndex) << (shiftForWord())))))); + if (moduleName == GIV(nilObj)) { + moduleLength = 0; + } + else { + if (!(/* isBytes: */ + ((!(moduleName & (tagMask())))) + && (((byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + if (failPtr) { + failPtr[0] = PrimErrBadMethod; + } + return 0; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + moduleLength = ((((/* begin numSlotsOf: */ + assert((classIndexOf(moduleName)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(moduleName + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(moduleName - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + } + functionName = longAt((void *)((externalCallLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFunctionNameIndex) << (shiftForWord())))))); + if (!(/* isBytes: */ + ((!(functionName & (tagMask())))) + && (((byteAt((void *)(functionName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + if (failPtr) { + failPtr[0] = PrimErrBadMethod; + } + return 0; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(functionName + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + functionLength = ((((/* begin numSlotsOf: */ + assert((classIndexOf(functionName)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(functionName + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(functionName - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + + /* Spur needs metadata for the primitive, which is stored in the third slot of the literal. */ + addr = ioLoadExternalFunctionOfLengthFromModuleOfLengthMetadataInto(functionName + BaseHeaderSize, functionLength, moduleName + BaseHeaderSize, moduleLength, (&metadata)); + if (addr) { + assert(((((metadata) >> SpurPrimitiveAccessorDepthShift) >= -1) && (((metadata) >> SpurPrimitiveAccessorDepthShift) <= 5))); + functionAddress = ((void *) addr); + + /* begin addToExternalPrimitiveTable: */ + for (i = GIV(externalPrimitiveTableFirstFreeIndex); i < MaxExternalPrimitiveTableSize; i += 1) { + if (!(externalPrimitiveTable[i])) { + externalPrimitiveTable[i] = functionAddress; + index = (GIV(externalPrimitiveTableFirstFreeIndex) = i + 1); + goto l1; + } + } + + /* if no space left, return zero so it'll looked up again */ + index = 0; + /* end addToExternalPrimitiveTable: */ +l1: + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(externalCallLiteral)) + && (!(isForwarded(externalCallLiteral)))); + assert(validStorePointerUncheckedArgs(ExternalCallLiteralFlagsIndex, externalCallLiteral, (((usqInt)metadata << 3) | 1))); + longAtput((void *)((externalCallLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFlagsIndex) << (shiftForWord()))))),(((usqInt)metadata << 3) | 1)); + } + else { + index = -1; + } + + /* Store the index (or -1 if failure) back in the literal */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(externalCallLiteral)) + && (!(isForwarded(externalCallLiteral)))); + assert(validStorePointerUncheckedArgs(ExternalCallLiteralTargetFunctionIndex, externalCallLiteral, (((usqInt)index << 3) | 1))); + longAtput((void *)((externalCallLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord()))))),(((usqInt)index << 3) | 1)); + + /* If the function has been successfully loaded cache it */ + rewriteMethodCacheEntryForExternalPrimitiveToFunction((index >= 0 + ? addr + : 0)); + return addr; +} + + +/* Return a shallow copy of the receiver. + Special-case non-single contexts (because of context-to-stack mapping). + Can't fail for contexts cuz of image context instantiation code (sigh). */ +/* because of cloneContext: below */ + + /* StackInterpreterPrimitives>>#primitiveClone */ +static void +primitiveClone(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newCopy; + sqInt rcvr; + + newCopy = 0; + rcvr = longAt(GIV(stackPointer)); + if (((rcvr & (tagMask())) != 0)) { + newCopy = rcvr; + } + else { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + newCopy = cloneContext(rcvr); + } + else { + if ((GIV(argumentCount) == 0) + || (!((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + newCopy = cloneObject(rcvr); + } + } + if (!newCopy) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + return; + } + } + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newCopy); +} + + +/* Special version of primitiveAt for accessing contexts. + Written to be varargs for use from mirror primitives. */ + + /* StackInterpreterPrimitives>>#primitiveContextAt */ +static void +primitiveContextAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aContext; + sqInt class; + sqInt fixedFields; + sqInt fixedFieldsSqInt; + usqLong fmt; + usqLong fmtUsqLong; + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + sqLong hdr; + sqLong hdrSqLong; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt senderOop; + sqInt sp; + char *spouseFP; + sqInt spSqInt; + sqInt stSize; + sqInt stSizeSqInt; + sqInt totalLength; + sqInt totalLengthSqInt; + sqInt value; + + index = longAt(GIV(stackPointer)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + index = (index >> 3); + aContext = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* Duplicating much of stObject:at:put: here allows stObject:at:put: to omit tests for contexts. */ + hdr = long64At((void *)(aContext)); + if (!((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stObject:at: */ + hdrSqLong = long64At((void *)(aContext)); + fmtUsqLong = (((usqLong)(hdrSqLong)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(aContext + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aContext - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmtUsqLong <= (ephemeronFormat())) { + totalLengthSqInt = numSlots; + goto l7; + } + if (fmtUsqLong >= (firstByteFormat())) { + totalLengthSqInt = ((numSlots << (shiftForWord()))) - (fmtUsqLong & 7); + goto l7; + } + + /* bytes, including CompiledMethod */ + if (fmtUsqLong >= (firstShortFormat())) { + totalLengthSqInt = ((numSlots << ((shiftForWord()) - 1))) - (fmtUsqLong & 3); + goto l7; + } + if (fmtUsqLong >= (firstLongFormat())) { + totalLengthSqInt = ((numSlots << ((shiftForWord()) - 2))) - (fmtUsqLong & 1); + goto l7; + } + if (fmtUsqLong == (sixtyFourBitIndexableFormat())) { + totalLengthSqInt = numSlots; + goto l7; + } + + /* fmt = self forwardedFormat */ + totalLengthSqInt = 0; + /* end lengthOf:baseHeader:format: */ +l7: + if ((fmtUsqLong == (indexablePointersFormat())) + && ((hdrSqLong & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(aContext)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(aContext))); + stSizeSqInt = sp; + goto l5; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSizeSqInt = 0; + goto l5; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + stSizeSqInt = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l5: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSizeSqInt)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))))) { + value = temporaryin(index - 1, frameOfMarriedContext(aContext)); + goto l8; + } + } + if (fmtUsqLong >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(aContext)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLengthSqInt))) { + value = (((usqInt)(byteAt((void *)((aContext + BaseHeaderSize) + (index - 1)))) << 3) | 1); + goto l8; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmtUsqLong >= (sixtyFourBitIndexableFormat())) + || (fmtUsqLong == (arrayFormat()))) { + fixedFieldsSqInt = 0; + goto l6; + } + if (fmtUsqLong < (arrayFormat())) { + fixedFieldsSqInt = totalLengthSqInt; + goto l6; + } + class = fetchClassOfNonImm(aContext); + fixedFieldsSqInt = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l6: + stSizeSqInt = totalLengthSqInt - fixedFieldsSqInt; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSizeSqInt))) { + /* begin subscript:with:format: */ + if (fmtUsqLong <= 5 /* lastPointerFormat */) { + value = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << (shiftForWord())))))); + goto l8; + } + if (fmtUsqLong >= (firstByteFormat())) { + value = (((usqInt)(byteAt((void *)((aContext + BaseHeaderSize) + ((index + fixedFieldsSqInt) - 1)))) << 3) | 1); + goto l8; + } + if (fmtUsqLong >= (firstShortFormat())) { + value = (((usqInt)(((unsigned short) (shortAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 1)))))))) << 3) | 1); + goto l8; + } + if (fmtUsqLong == (sixtyFourBitIndexableFormat())) { + value = positive64BitIntegerFor(long64At((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 3)))))); + goto l8; + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + value = (((usqInt)((((usqInt)(long32At((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + goto l8; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmtUsqLong <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + value = 0; + /* end stObject:at: */ +l8: + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + } + return; + } + + /* might be an instance of a subclass */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext))))) { + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(aContext + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aContext - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l4; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l4; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l4; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l4; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l4: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l3; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l3; + } + class = fetchClassOfNonImm(aContext); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l3: + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l1; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + stSize = (spSqInt >> 3); + /* end fetchStackPointerOf: */ +l1: + if (!(((index >= 1) && (index <= stSize)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* begin subscript:with:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + value = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << (shiftForWord())))))); + goto l2; + } + if (fmt >= (firstByteFormat())) { + value = (((usqInt)(byteAt((void *)((aContext + BaseHeaderSize) + ((index + fixedFields) - 1)))) << 3) | 1); + goto l2; + } + if (fmt >= (firstShortFormat())) { + value = (((usqInt)(((unsigned short) (shortAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))))))) << 3) | 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + value = positive64BitIntegerFor(long64At((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))))); + goto l2; + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + value = (((usqInt)((((usqInt)(long32At((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + /* end subscript:with:format: */ +l2: + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + return; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!(((index >= 1) && (index <= (stackPointerIndexForFrame(spouseFP)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + value = /* temporary:in: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? ((index - 1) < ((frameNumArgs = ((mframeCogMethod(spouseFP))->cmNumArgs))) + ? longAt((spouseFP + FoxCallerSavedIP) + ((frameNumArgs - (index - 1)) * BytesPerWord)) + : longAt(((spouseFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs - (index - 1)) * BytesPerWord))) + : /* itemporary:in: */ + ((index - 1) < ((frameNumArgsUsqInt = byteAt((spouseFP + FoxIFrameFlags) + 1))) + ? longAt((spouseFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt - (index - 1)) * BytesPerWord)) + : longAt(((spouseFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt - (index - 1)) * BytesPerWord)))); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); +} + + +/* Special version of primitiveAtPut for accessing contexts. + Written to be varargs for use from mirror primitives. */ + + /* StackInterpreterPrimitives>>#primitiveContextAtPut */ +static void +primitiveContextAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aContext; + sqInt class; + sqInt fieldIndex; + sqInt fixedFields; + sqInt fixedFieldsSqInt; + usqLong fmt; + usqLong fmtUsqLong; + usqInt frameNumArgs; + unsigned char frameNumArgs1; + sqLong hdr; + sqLong hdrSqLong; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt senderOop; + sqInt signedValueToStore; + sqInt signedValueToStoreSqInt; + sqInt sp; + char *spouseFP; + sqInt spSqInt; + sqInt stSize; + sqInt stSizeSqInt; + sqInt totalLength; + sqInt totalLengthSqInt; + usqLong unsigned64BitValueToStore; + unsigned int unsignedValueToStore; + sqInt value; + + value = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aContext = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* Duplicating much of stObject:at:put: here allows stObject:at:put: to omit tests for contexts. */ + hdr = long64At((void *)(aContext)); + index = (index >> 3); + if (!((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stObject:at:put: */ + hdrSqLong = long64At((void *)(aContext)); + fmtUsqLong = (((usqLong)(hdrSqLong)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(aContext + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aContext - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmtUsqLong <= (ephemeronFormat())) { + totalLengthSqInt = numSlots; + goto l6; + } + if (fmtUsqLong >= (firstByteFormat())) { + totalLengthSqInt = ((numSlots << (shiftForWord()))) - (fmtUsqLong & 7); + goto l6; + } + + /* bytes, including CompiledMethod */ + if (fmtUsqLong >= (firstShortFormat())) { + totalLengthSqInt = ((numSlots << ((shiftForWord()) - 1))) - (fmtUsqLong & 3); + goto l6; + } + if (fmtUsqLong >= (firstLongFormat())) { + totalLengthSqInt = ((numSlots << ((shiftForWord()) - 2))) - (fmtUsqLong & 1); + goto l6; + } + if (fmtUsqLong == (sixtyFourBitIndexableFormat())) { + totalLengthSqInt = numSlots; + goto l6; + } + + /* fmt = self forwardedFormat */ + totalLengthSqInt = 0; + /* end lengthOf:baseHeader:format: */ +l6: + if ((fmtUsqLong == (indexablePointersFormat())) + && ((hdrSqLong & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(aContext)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(aContext))); + stSizeSqInt = sp; + goto l8; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSizeSqInt = 0; + goto l8; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + stSizeSqInt = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l8: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSizeSqInt)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))))) { + temporaryinput(index - 1, frameOfMarriedContext(aContext), value); + goto l9; + } + } + if (fmtUsqLong >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(aContext)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLengthSqInt))) { + if (((((value) & 7) == 1)) + && ((((usqInt)((signedValueToStore = (value >> 3))))) <= 0xFF)) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((aContext + BaseHeaderSize) + (index - 1)),signedValueToStore); + goto l9; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l9; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmtUsqLong >= (sixtyFourBitIndexableFormat())) + || (fmtUsqLong == (arrayFormat()))) { + fixedFieldsSqInt = 0; + goto l5; + } + if (fmtUsqLong < (arrayFormat())) { + fixedFieldsSqInt = totalLengthSqInt; + goto l5; + } + class = fetchClassOfNonImm(aContext); + fixedFieldsSqInt = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l5: + stSizeSqInt = totalLengthSqInt - fixedFieldsSqInt; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSizeSqInt))) { + /* begin subscript:with:storing:format: */ + if (fmtUsqLong <= 5 /* lastPointerFormat */) { + fieldIndex = (index + fixedFieldsSqInt) - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, aContext, value)); + assert(isNonImmediate(aContext)); + if (oopisGreaterThanOrEqualTo(aContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),value); + goto l7; + } + if (fmtUsqLong >= (firstByteFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l7; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l7; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((aContext + BaseHeaderSize) + ((index + fixedFieldsSqInt) - 1)),signedValueToStoreSqInt); + goto l7; + } + if (fmtUsqLong >= (firstShortFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l7; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l7; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 1)))),signedValueToStoreSqInt); + goto l7; + } + if (fmtUsqLong == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l7; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l7: + goto l9; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmtUsqLong <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + /* end stObject:at:put: */ +l9: + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + } + return; + } + + /* might be an instance of a subclass */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext))))) { + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(aContext + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aContext - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l3; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l3; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l3; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l3; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l3; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l3: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l2; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l2; + } + class = fetchClassOfNonImm(aContext); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l2: + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l1; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + stSize = (spSqInt >> 3); + /* end fetchStackPointerOf: */ +l1: + if (!(((index >= 1) && (index <= stSize)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* begin subscript:with:storing:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + fieldIndex = (index + fixedFields) - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, aContext, value)); + assert(isNonImmediate(aContext)); + if (oopisGreaterThanOrEqualTo(aContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),value); + goto l4; + } + if (fmt >= (firstByteFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStore = (value >> 3); + if (!((signedValueToStore >= 0) + && (signedValueToStore <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((aContext + BaseHeaderSize) + ((index + fixedFields) - 1)),signedValueToStore); + goto l4; + } + if (fmt >= (firstShortFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStore = (value >> 3); + if (!((signedValueToStore >= 0) + && (signedValueToStore <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))),signedValueToStore); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l4; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l4: + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + return; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!(((index >= 1) && (index <= (stackPointerIndexForFrame(spouseFP)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* temporary:in:put: */ + if ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory())) { + /* mtemporary:in:put: */ + longAtput(((index - 1) < ((frameNumArgs1 = ((mframeCogMethod(spouseFP))->cmNumArgs))) + ? (spouseFP + FoxCallerSavedIP) + ((frameNumArgs1 - (index - 1)) * BytesPerWord) + : ((spouseFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs1 - (index - 1)) * BytesPerWord)),value); + } + else { + /* itemporary:in:put: */ + if ((index - 1) < ((frameNumArgs = byteAt((spouseFP + FoxIFrameFlags) + 1)))) { + longAtput((spouseFP + FoxCallerSavedIP) + ((frameNumArgs - (index - 1)) * BytesPerWord),value); + } + else { + longAtput(((spouseFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - (index - 1)) * BytesPerWord),value); + } + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); +} + + +/* The core design of the opensmalltalk-vm's closure implementation has as + its rationale + breaking the dependence of a block on the temporaries of any outer + context, principally + the home context. The scheme is explained here: + + http://www.mirandabanda.org/cogblog/2009/01/14/under-cover-contexts-and-the-big-frame-up + Hence when a frame is married the VM *does not* copy temporary variables + into the context + object, copying only arguments (if any). See marryFrame:SP: and + genGetActiveContextLarge:inBlock:. Note that if a married frame and + context are divorced (which occurs when an inst var of a context is + assigned to or when the stack page on which the frame resides is reused + for newer frames) then + the full frame contents are written to the context, bringing it fully + up-to-date so that it becomes + the implementation of the relevant method/block activation, replacing the + frame. + Except in extraordinary circumstances there is no utility in accessing the + temporary variables of a + widowed (returned-from) context. The frame and context are married as soon + as thisContext is + referenced (either explicitly, or by creating a closure, or accessing the + sender of another context). + At this point there is no guarantee that temp vars have their final value, + and so there is no point + preserving them because their values at the time of marriage will very + likely be stale by the time + the context is widowed. N.B. One could imagine having the VM write the + values of frame + temporaries back to the context if it existed, but such a scheme would be + unacceptably slow. + + While I (eem 4/23/2026) have yet to see an example where temps should be + preserved, this + mechanism is provided just in case. It must be invoked after the last + modification of a temporary. + For example, the assignments to the completionStatus temps in + BlockClosure>>#ensure: and + BlockClosure>>#ifCurtailed: (as of 11/18/2025) are accessed from *while + the contexts are still active* + from Context>>#finishIncompleteUnwindsUpTo: and hence if married the VM + will access the temps + from the frame, not the context, and hence up-to-date values will be + answered. + There is a complication. Only methods and full blocks have an easily + accessible temporary count. + The original SqueakV3PlusClosures BlockClosure uses explicit pushNil + bytecodes to establish + block-local temps, and machinery such as + StackToRegisterMappingCogit>>#scanBlock: and + BlockLocalTempCounter to compute the number of temps. Hence this primitive + is actually two, + one that takes no arguments, accesses the temp count from the receiver's + method if possible, + failing if not with the error code PrimErrUnsupported, and one that takes + the temp count as its + argument. */ + + /* StackInterpreterPrimitives>>#primitiveContextPreserveTemps */ +static void +primitiveContextPreserveTemps(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt closureRcvr; + sqInt header; + sqInt methodPointer; + usqInt numArgs; + sqInt numNonArgTemps; + usqInt numTemps; + sqInt receiverCtxt; + sqInt senderOop; + char *theFP; + char *theSP; + + receiverCtxt = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + assert(isContext(receiverCtxt)); + + /* this is required for isWidowedContext: et al to function... */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* If the receiver is a single (heap) context then temps are already preserved. In normal use the + receiver would be married (e.g. as in executing thisContext preserveTemps where it would be + the current frame. But the execution simulation machinery could send it to a heap context. */ + if (!(((((longAt((void *)((receiverCtxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return; + } + + /* If the receiver is widowed then it's too late... */ + if (isWidowedContext(receiverCtxt)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + return; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((receiverCtxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + numArgs = /* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)); + if (GIV(argumentCount)) { + assert((methodArgumentCount()) == 1); + numTemps = longAt(GIV(stackPointer)); + if (!(((((numTemps) & 7) == 1)) + && (((((numTemps = (numTemps >> 3))) >= 0) && (numTemps <= ((LargeContextSlots - ReceiverIndex) - 1)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + else { + /* is this a normal method activation and/or a full block closure activation or not? + If not, fail and allow the image to compute the temp count. */ + if (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0)) { + closureRcvr = longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((numArgs << (shiftForWord()))))); + + /* begin isVanillaBlockClosure: */ + assert((isPointers(closureRcvr)) + && ((numSlotsOf(closureRcvr)) >= ClosureFirstCopiedValueIndex)); + if (((((longAt((void *)((closureRcvr + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + } + + /* N.B. the temp count includes the argument count */ + methodPointer = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + + /* begin tempCountOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(header)) >> MethodHeaderTempCountShift) & 0x3F; + } + numNonArgTemps = numTemps - numArgs; + theSP = (/* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver)) - (numNonArgTemps * BytesPerWord); + updateStateOfSpouseContextForFrameWithSP(theFP, theSP); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* Special version of primitiveSize for accessing contexts. + Written to be varargs for use from mirror primitives. */ + + /* StackInterpreterPrimitives>>#primitiveContextSize */ +static void +primitiveContextSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt sp1; + sqInt spSqInt; + sqInt sz; + sqInt totalLength; + + rcvr = longAt(GIV(stackPointer)); + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + if ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + sp1 = stackPointerIndexForFrame(frameOfMarriedContext(rcvr)); + assert((ReceiverIndex + ((sp1 >> 3))) < (lengthOf(rcvr))); + sz = sp1; + goto l3; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + sz = 0; + goto l3; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(rcvr))); + sz = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l3:; + } + else { + sz = totalLength - fixedFields; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)sz << 3) | 1)); +} + + +/* Set or clear the flag that controls whether modifications of + the Display object are propagated to the underlying + platform's screen. */ + + /* StackInterpreterPrimitives>>#primitiveDeferDisplayUpdates */ +static void +primitiveDeferDisplayUpdates(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt flag; + + if (cannotDeferDisplayUpdates) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + flag = longAt(GIV(stackPointer)); + if (flag == GIV(trueObj)) { + deferDisplayUpdates = 1; + } + else { + if (flag == GIV(falseObj)) { + deferDisplayUpdates = 0; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; +} + + +/* Simulate an primitiveExternalCall invocation (e.g. for the Debugger). Do + not cache anything. + e.g. ContextPart>>tryNamedPrimitiveIn: aCompiledMethod for: aReceiver + withArgs: arguments */ + + /* StackInterpreterPrimitives>>#primitiveDoNamedPrimitiveWithArgs */ +static void +primitiveDoNamedPrimitiveWithArgs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*addr)(void); + sqInt argumentArray; + usqInt arraySize; + sqInt ccIndex; + usqInt firstBytecode; + sqInt fmt; + sqInt functionLength; + sqInt functionName; + sqInt header; + sqInt index; + sqInt isArray; + sqInt methodArg; + sqInt methodHeader; + sqInt moduleLength; + sqInt moduleName; + usqInt newObj; + usqInt numBytes; + usqInt numSlots; + sqInt primRcvr; + sqInt spec; + sqInt successBoolean; + sqInt top; + sqInt valuePointer; + + GIV(metaAccessorDepth) = -2; + + /* See checkForAndFollowForwardedPrimitiveState */ + argumentArray = longAt(GIV(stackPointer)); + methodArg = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (!((/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (/* isOopCompiledMethod: */ + ((!(methodArg & (tagMask())))) + && (((byteAt((void *)(methodArg + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -2; + return; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (!(arraySize <= (LargeContextSlots - CtxtTempFrameStart))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -2; + return; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodArg)); + header = longAt((void *)((methodArg + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (!(((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) > 2)) { + /* primitiveFailFor: */ + GIV(primFailCode) = -3; + return; + } + spec = longAt((void *)((methodArg + BaseHeaderSize) + (1U << (shiftForWord())))); + + /* first literal */ + + /* begin isInstanceOfClassArray: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((spec & (tagMask())) != 0)) { + isArray = 0; + goto l2; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(spec))); + ccIndex = (longAt((void *)(spec))) & (classIndexMask()); + isArray = ClassArrayCompactIndex == ccIndex; + /* end isInstanceOfClassArray: */ +l2: + if (!(isArray + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(spec)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(spec + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(spec - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4) + && ((/* primitiveIndexOfMethod:header: */ + (((methodHeader & AlternateHeaderHasPrimFlag) != 0) + ? ((firstBytecode = (methodArg + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize), + (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8)))) + : 0)) == PrimNumberExternalCall)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -3; + return; + } + if (!(((((usqInt)(methodHeader)) >> MethodHeaderArgCountShift) & 15) == arraySize)) { + /* primitiveFailFor: */ + GIV(primFailCode) = -2; + return; + } + moduleName = longAt((void *)((spec + BaseHeaderSize) + (0U << (shiftForWord())))); + if (moduleName == GIV(nilObj)) { + moduleLength = 0; + } + else { + successBoolean = /* isBytes: */ + ((!(moduleName & (tagMask())))) + && (((byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())); + + /* begin success: */ + if (!successBoolean) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + moduleLength = ((((/* begin numSlotsOf: */ + assert((classIndexOf(moduleName)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(moduleName + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(moduleName - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + } + functionName = longAt((void *)((spec + BaseHeaderSize) + (1U << (shiftForWord())))); + successBoolean = /* isBytes: */ + ((!(functionName & (tagMask())))) + && (((byteAt((void *)(functionName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())); + + /* begin success: */ + if (!successBoolean) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(functionName + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + functionLength = ((((/* begin numSlotsOf: */ + assert((classIndexOf(functionName)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(functionName + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(functionName - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = -3; + return; + } + addr = ioLoadExternalFunctionOfLengthFromModuleOfLengthMetadataInto(functionName + BaseHeaderSize, functionLength, moduleName + BaseHeaderSize, moduleLength, (&GIV(metaAccessorDepth))); + + /* N.B. the accessor depth is the second byte of the primitive's metadata; + the first byte is various flags (currently l.s.b. = use fast C linkage). */ + GIV(metaAccessorDepth) = (addr + ? (((usqInt)(GIV(metaAccessorDepth))) >> 8) + : -2); + if (!addr) { + /* primitiveFailFor: */ + GIV(primFailCode) = -1; + return; + } + + /* begin eeInstantiateClassIndex:format:numSlots: */ + assert((knownClassAtIndex(ClassArrayCompactIndex)) != GIV(nilObj)); + assert((arrayFormat()) == (instSpecOfClass(knownClassAtIndex(ClassArrayCompactIndex)))); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (4 * BytesPerOop); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateNewSpaceSlots:format:classIndex:"); + GIV(tempOop) = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) 4)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + GIV(tempOop) = newObj; + /* end eeInstantiateClassIndex:format:numSlots: */ +l1: + + /* begin popStack */ + valuePointer = (argumentArray = longAt(GIV(stackPointer))); + GIV(stackPointer) += BytesPerWord; + valuePointer = (argumentArray); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(tempOop))) + && (!(isForwarded(GIV(tempOop))))); + assert(validStorePointerUncheckedArgs(0, GIV(tempOop), valuePointer)); + longAtput((void *)((GIV(tempOop) + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin popStack */ + valuePointer = (primRcvr = longAt(GIV(stackPointer))); + GIV(stackPointer) += BytesPerWord; + valuePointer = (primRcvr); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(tempOop))) + && (!(isForwarded(GIV(tempOop))))); + assert(validStorePointerUncheckedArgs(1, GIV(tempOop), valuePointer)); + longAtput((void *)((GIV(tempOop) + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + valuePointer = top; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(tempOop))) + && (!(isForwarded(GIV(tempOop))))); + assert(validStorePointerUncheckedArgs(2, GIV(tempOop), valuePointer)); + longAtput((void *)((GIV(tempOop) + BaseHeaderSize) + (2U << (shiftForWord()))),valuePointer); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + valuePointer = top; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(tempOop))) + && (!(isForwarded(GIV(tempOop))))); + assert(validStorePointerUncheckedArgs(3, GIV(tempOop), valuePointer)); + longAtput((void *)((GIV(tempOop) + BaseHeaderSize) + (3U << (shiftForWord()))),valuePointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),primRcvr); + GIV(argumentCount) = arraySize; + for (index = 1; index <= arraySize; index += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + } + primitiveFunctionPointer = addr; + dispatchFunctionPointer(addr); + if (GIV(primFailCode)) { + /* begin pop: */ + GIV(stackPointer) += (arraySize + 1) * BytesPerWord; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((GIV(tempOop) + BaseHeaderSize) + (3U << (shiftForWord()))))); + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((GIV(tempOop) + BaseHeaderSize) + (2U << (shiftForWord()))))); + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((GIV(tempOop) + BaseHeaderSize) + (1U << (shiftForWord()))))); + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((GIV(tempOop) + BaseHeaderSize) + (0U << (shiftForWord()))))); + GIV(argumentCount) = 3; + + /* Must reset primitiveFunctionPointer for checkForAndFollowForwardedPrimitiveState */ + primitiveFunctionPointer = primitiveDoNamedPrimitiveWithArgs; + + /* Hack. A nil prim error code (primErrorCode = 1) is interpreted by the image + as meaning this primitive is not implemented. So to pass back nil as an error + code we use -1 to indicate generic failure. */ + if (GIV(primFailCode) == 1) { + GIV(primFailCode) = -1; + } + } +} + + +/* Implement either ProtoObject>>tryPrimitive: primIndex withArgs: argArray + or Context>>receiver: anObject tryPrimitive: primIndex withArgs: argArray. + If this primitive fails, arrange that its error code is a negative + integer, to + distinguish between this failing and the primitive it invokes failing. */ + + /* StackInterpreterPrimitives>>#primitiveDoPrimitiveWithArgs */ +static void +primitiveDoPrimitiveWithArgs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt argumentArray; + usqInt arraySize; + sqInt index; + usqInt numSlots; + sqInt primIdx; + sqInt rcvr; + sqInt referent; + sqInt savedNumArgs; + + GIV(metaAccessorDepth) = -2; + + /* See checkForAndFollowForwardedPrimitiveState */ + if (!(((GIV(argumentCount) >= 2) && (GIV(argumentCount) <= 3)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -PrimErrUnsupported; + return; + } + argumentArray = longAt(GIV(stackPointer)); + primIdx = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((((primIdx) & 7) == 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -PrimErrBadArgument; + return; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (!(arraySize <= (LargeContextSlots - CtxtTempFrameStart))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -PrimErrLimitExceeded; + return; + } + primIdx = (primIdx >> 3); + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primIdx)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primIdx]))); + if (!primitiveFunctionPointer) { + primitiveFunctionPointer = primitiveDoPrimitiveWithArgs; + + /* primitiveFailFor: */ + GIV(primFailCode) = -PrimErrBadIndex; + return; + } + + /* Pop primIndex and argArray, then push args in place... */ + if (((savedNumArgs = GIV(argumentCount))) == 3) { + /* actual receiver */ + GIV(tempOop2) = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + + /* receiver for primitive */ + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(rcvr)); + referent = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + rcvr = referent; + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 3 * BytesPerWord),rcvr); + } + else { + /* begin pop: */ + GIV(stackPointer) += 2 * BytesPerWord; + } + + /* ...and receiver if the three arg form */ + GIV(argumentCount) = arraySize; + index = 1; + while (index <= arraySize) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + index += 1; + } + if ((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex) { + externalQuickPrimitiveResponse(); + GIV(tempOop2) = 0; + return; + } + + /* We use tempOop instead of pushRemappableOop:/popRemappableOop here because in + the Cogit primitiveEnterCriticalSection, primitiveSignal, primitiveResume et al longjmp back + to either the interpreter or machine code, depending on the process activated. So if we're + executing one of these primitives, control won't actually return here and the matching + popRemappableOop: wouldn't occur, potentially overflowing the remap buffer. + Note that while recursion could occur (nil tryPrimitive: 118 withArgs: #(118 #(110 #()))) + it counts as shooting oneself in the foot. */ + + /* prim might alloc/gc */ + /* Run the primitive (sets primFailCode) */ + GIV(tempOop) = argumentArray; + GIV(metaAccessorDepth) = ((sqInt)((primitiveMetadataTable[primIdx]))) >> SpurPrimitiveAccessorDepthShift; + + /* See checkForAndFollowForwardedPrimitiveState */ + if (!(slowPrimitiveResponse())) { + if (!GIV(tempOop)) { + return; + } + + /* the primitive failed in a recursive invocation. can't fix things with no value... */ + + /* begin pop: */ + GIV(stackPointer) += arraySize * BytesPerWord; + if (savedNumArgs == 3) { + rcvr = longAt(GIV(stackPointer)); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(tempOop2)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + } + longAtput((GIV(stackPointer) -= BytesPerWord),(((usqInt)primIdx << 3) | 1)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(tempOop)); + primitiveFunctionPointer = primitiveDoPrimitiveWithArgs; + GIV(argumentCount) = savedNumArgs; + } + + /* If primitive failed, then restore state for failure code */ + GIV(tempOop) = (GIV(tempOop2) = 0); +} + + +/* Call an external primitive. External primitive methods first literals are + an array of + * The module name (String | Symbol) + * The function name (String | Symbol) + * The session ID (SmallInteger) [OBSOLETE], or in Spur, the metadata + (accessorDepth and flags; Integer)) + * The function index (Integer) in the externalPrimitiveTable + For fast interpreter dispatch in subsequent invocations the + primitiveFunctionPointer in the method cache is rewritten, either to the + function itself, or to zero if the external + function is not found. This allows for fast responses as long as the + method stays in + the cache. The cache rewrite relies on lastMethodCacheProbeWrite which is + set in + addNewMethodToCache:. + Now that the VM flushes function addresses from its tables, the session ID + is obsolete, + but it is kept for backward compatibility. Also, a failed lookup is + reported specially. If a + method has been looked up and not been found, the function address is + stored as -1 + (i.e., the SmallInteger -1 to distinguish from 16rFFFFFFFF which may be + returned from + lookup), and the primitive fails with PrimErrNotFound. + */ +/* because the primitive accesses newMethod's first literal, which is checked + for explicitly in checkForAndFollowForwardedPrimitiveState + */ +/* since call may invoke a callback */ +/* since external primitive linkage is in first literal... */ +/* Check for it being a method for primitiveDoPrimitiveWithArgs. + Fetch the first literal of the method; check its an Array of length 4. + Look at the function index in case it has been loaded before */ + + /* StackInterpreterPrimitives>>#primitiveExternalCall */ +static void +primitiveExternalCall(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*addr)(void); + sqInt index; + sqInt lit; + usqInt numSlots; + sqInt reasonCode; + + if (!((/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((literalCountOf(GIV(newMethod))) > 0) + && (((lit = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + (1U << (shiftForWord()))))), + (/* isArray: */ + ((!(lit & (tagMask())))) + && (((byteAt((void *)(lit + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(lit)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(lit + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(lit - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4) + && (((index = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord()))))))), + (((index) & 7) == 1))))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadMethod; + return; + } + index = (index >> 3); + + /* Check if we have already looked up the function and failed. */ + if (index < 0) { + /* Function address was not found in this session, + Void the primitive function. */ + rewriteMethodCacheEntryForExternalPrimitiveToFunction(0); + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNotFound; + return; + } + + /* Try to call the function directly */ + if ((index > 0) + && (index <= MaxExternalPrimitiveTableSize)) { + addr = externalPrimitiveTable[index - 1]; + if (addr) { + rewriteMethodCacheEntryForExternalPrimitiveToFunction(addr); + + /* begin callExternalPrimitive: */ + primitiveFunctionPointer = addr; + dispatchFunctionPointer(addr); + + /* begin maybeRetryPrimitiveOnFailure */ + if (GIV(primFailCode)) { + retryPrimitiveOnFailure(); + } + return; + } + + /* if we get here, then an index to the external prim was + kept on the ST side although the underlying prim + table was already flushed */ + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNamedInternal; + return; + } + + /* Clean up session id/metadata and external primitive index */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(lit)) + && (!(isForwarded(lit)))); + assert(validStorePointerUncheckedArgs(ExternalCallLiteralFlagsIndex, lit, ConstZero)); + longAtput((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFlagsIndex) << (shiftForWord()))))),ConstZero); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(lit)) + && (!(isForwarded(lit)))); + assert(validStorePointerUncheckedArgs(ExternalCallLiteralTargetFunctionIndex, lit, ConstZero)); + longAtput((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord()))))),ConstZero); + + /* The function has not been loaded yet. Attempt to link it, cache it, and call it. */ + addr = linkExternalCallerrInto(lit, (&GIV(primFailCode))); + if (!addr) { + assert((fetchPointerofObject(ExternalCallLiteralFlagsIndex, lit)) == ConstZero); + reasonCode = (GIV(primFailCode) + ? GIV(primFailCode) + : PrimErrNotFound); + GIV(primFailCode) = reasonCode; + return; + } + + /* begin callExternalPrimitive: */ + primitiveFunctionPointer = addr; + dispatchFunctionPointer(addr); + + /* begin maybeRetryPrimitiveOnFailure */ + if (GIV(primFailCode)) { + retryPrimitiveOnFailure(); + } +} + + +/* Primitive. Search up the context stack for the next method context marked + for exception handling starting at the receiver. Return nil if none found */ + + /* StackInterpreterPrimitives>>#primitiveFindHandlerContext */ +static void +primitiveFindHandlerContext(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt handlerOrNilOrZero; + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + handlerOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(PrimNumberHandlerMarker, longAt(GIV(stackPointer)), GIV(nilObj)); + if (!handlerOrNilOrZero) { + handlerOrNilOrZero = GIV(nilObj); + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),handlerOrNilOrZero); +} + + +/* Primitive. Search up the context stack for the next method context marked + for unwind + handling from the receiver up to but not including the argument. Return + nil if none found. + */ + + /* StackInterpreterPrimitives>>#primitiveFindNextUnwindContext */ +static void +primitiveFindNextUnwindContext(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt calleeContext; + sqInt handlerOrNilOrZero; + sqInt senderContext; + sqInt senderOop; + sqInt startContext; + sqInt stopContext; + char *theFP; + char *theFP1; + char *theFPAbove; + sqInt theMethod; + StackPage *thePage; + char *theSP; + + stopContext = longAt(GIV(stackPointer)); + calleeContext = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((stopContext == GIV(nilObj)) + || (/* isContext: */ + ((!(stopContext & (tagMask())))) + && (((longAt((void *)(stopContext))) & (classIndexMask())) == ClassMethodContextCompactIndex)))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* The following should never be true, but developing full blocks, early in September + 2016 we were seeing invalid invocations of this primitive.. Hence the assert: */ + assert(stopContext != calleeContext); + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((calleeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(calleeContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((calleeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (longAt(theFP + FoxSavedFP)) { + /* begin findMethodWithPrimitive:FromFP:UpToContext: */ + theFP1 = ((char *)(longAt(theFP + FoxSavedFP))); + theFPAbove = ((char *)(longAt(theFP + FoxSavedFP))); + do { + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP1 + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP1 + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP1 + FoxIFrameFlags) + 2)) != 0)) + && (stopContext == (longAt(theFP1 + FoxThisContext)))) { + handlerOrNilOrZero = 0; + goto l1; + } + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP1 + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP1 + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP1 + FoxIFrameFlags) + 3)) != 0))) { + theMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP1 + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP1))->methodObject) + : longAt(theFP1 + FoxMethod)); + if ((primitiveIndexOf(theMethod)) == PrimNumberUnwindMarker) { + if (theFP1 == theFPAbove) { + theSP = findSPOfon(theFP1, stackPageFor(theFP1)); + } + else { + /* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFPAbove))); + theSP = (theFPAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFPAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFPAbove))->cmNumArgs) + : byteAt((theFPAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP1 + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP1 + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP1 + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP1))); + handlerOrNilOrZero = longAt(theFP1 + FoxThisContext); + goto l1; + } + handlerOrNilOrZero = marryFrameSP(theFP1, theSP); + goto l1; + } + } + theFPAbove = theFP1; + theFP1 = ((char *)(longAt(theFP1 + FoxSavedFP))); + } while(theFP1 != 0); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFPAbove)); + thePage = stackPageAtpages(pageIndexFor(theFPAbove), GIV(pages)); + senderContext = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(senderContext)); + assert((senderContext == (nilObject())) + || (isContext(followMaybeForwarded(senderContext)))); + if (!(/* isContext: */ + ((!(senderContext & (tagMask())))) + && (((longAt((void *)(senderContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + handlerOrNilOrZero = GIV(nilObj); + goto l1; + } + handlerOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(PrimNumberUnwindMarker, senderContext, stopContext); + /* end findMethodWithPrimitive:FromFP:UpToContext: */ +l1:; + } + else { + handlerOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(PrimNumberUnwindMarker, frameCallerContext(theFP), stopContext); + } + } + else { + startContext = longAt((void *)((calleeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + if (/* isContext: */ + ((!(startContext & (tagMask())))) + && (((longAt((void *)(startContext))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + handlerOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(PrimNumberUnwindMarker, startContext, stopContext); + } + else { + handlerOrNilOrZero = 0; + } + } + if (!handlerOrNilOrZero) { + handlerOrNilOrZero = GIV(nilObj); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),handlerOrNilOrZero); +} + + +/* Do a full garbage collection. In SqueakV3ObjectMemory, answer the number + of bytes available (including swap space if dynamic memory management is + supported). In Spur, answer the size of the largest free chunk. */ +/* Do a full garbage collection. In SqueakV3ObjectMemory, answer the number + of bytes available (including swap space if dynamic memory management is + supported). In Spur, answer the size of the largest free chunk. */ + + /* StackInterpreterPrimitives>>#primitiveFullGC */ +static void +primitiveFullGC(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integer; + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + integer = fullGC(); + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); +} + + /* StackInterpreterPrimitives>>#primitiveGetSetFFIExceptionHandling */ +static void +primitiveGetSetFFIExceptionHandling(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt integer; + + if (!GIV(argumentCount)) { + integer = (ioCanCatchFFIExceptions() + ? ffiExceptionResponse + : -1); + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); + return; + } + if (!(ioCanCatchFFIExceptions())) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + if (!(GIV(argumentCount) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + arg = longAt(GIV(stackPointer)); + if (!(((((arg) & 7) == 1)) + && (((((arg = (arg >> 3))) >= -1) && (arg <= 1))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + ffiExceptionResponse = arg; + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* Do a quick, incremental garbage collection and return the number of bytes + immediately available. + (Note: more space may be made available by doing a full garbage + collection. + */ +/* Do a quick, incremental garbage collection and return the number of bytes + immediately available. + (Note: more space may be made available by doing a full garbage + collection. + */ + + /* StackInterpreterPrimitives>>#primitiveIncrementalGC */ +static void +primitiveIncrementalGC(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerVal; + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* begin scavengingGC */ + scavengingGCTenuringIf(TenureByAge); + integerVal = ((((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes())) < 0) ? 0 : ((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes()))); + + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),(((usqInt)integerVal << 3) | 1)); +} + + +/* because of externalInstVar:ofContext: below */ + + /* StackInterpreterPrimitives>>#primitiveInstVarAt */ +static void +primitiveInstVarAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt totalLength; + sqInt value; + + index = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 1) + && (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + return; + } + index = (index >> 3); + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l3; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l3; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l3; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l3; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l3; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l3: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l2; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l2; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l2: + if (!((index >= 1) + && (index <= fixedFields))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + value = externalInstVarofContext(index - 1, rcvr); + } + else { + /* begin subscript:with:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + value = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord())))))); + goto l1; + } + if (fmt >= (firstByteFormat())) { + value = (((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + (index - 1)))) << 3) | 1); + goto l1; + } + if (fmt >= (firstShortFormat())) { + value = (((usqInt)(((unsigned short) (shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 1)))))))) << 3) | 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + value = positive64BitIntegerFor(long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 3)))))); + goto l1; + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + value = (((usqInt)((((usqInt)(long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + /* end subscript:with:format: */ +l1:; + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); +} + + /* StackInterpreterPrimitives>>#primitiveInstVarAtPut */ +static void +primitiveInstVarAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fieldIndex; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + sqInt newValue; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt signedValueToStore; + sqInt totalLength; + usqLong unsigned64BitValueToStore; + unsigned int unsignedValueToStore; + + newValue = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 2) + && (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + return; + } + index = (index >> 3); + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + if (!((index >= 1) + && (index <= fixedFields))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + externalInstVarofContextput(index - 1, rcvr, newValue); + } + else { + /* begin subscript:with:storing:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + fieldIndex = index - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, rcvr, newValue)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(newValue & (tagMask())))) + && (oopisLessThan(newValue, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),newValue); + goto l3; + } + if (fmt >= (firstByteFormat())) { + if (!((((newValue) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l3; + } + signedValueToStore = (newValue >> 3); + if (!((signedValueToStore >= 0) + && (signedValueToStore <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l3; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + (index - 1)),signedValueToStore); + goto l3; + } + if (fmt >= (firstShortFormat())) { + if (!((((newValue) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l3; + } + signedValueToStore = (newValue >> 3); + if (!((signedValueToStore >= 0) + && (signedValueToStore <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l3; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 1)))),signedValueToStore); + goto l3; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(newValue); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l3; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(newValue); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l3:; + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); +} + + +/* Primitive. 'Invoke' an object like a function, sending the special message + run: originalSelector with: arguments in: aReceiver. + */ + + /* StackInterpreterPrimitives>>#primitiveInvokeObjectAsMethod */ +static void +primitiveInvokeObjectAsMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt lookupClassTag; + usqInt newObj; + usqInt numBytes; + usqInt runArgs; + sqInt runReceiver; + sqInt tagBits; + sqInt top; + sqInt valuePointer; + + /* begin eeInstantiateClassIndex:format:numSlots: */ + assert((GIV(argumentCount) >= 0) + && ((knownClassAtIndex(ClassArrayCompactIndex)) != GIV(nilObj))); + assert((arrayFormat()) == (instSpecOfClass(knownClassAtIndex(ClassArrayCompactIndex)))); + + /* begin allocateNewSpaceSlots:format:classIndex: */ + if (GIV(argumentCount) >= (numSlotsMask())) { + if (GIV(argumentCount) > 0xFFFFFFFFU) { + runArgs = null; + goto l1; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (GIV(argumentCount) * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((GIV(argumentCount) < 1 + ? 8 /* allocationUnit */ + : GIV(argumentCount) * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateNewSpaceSlots:format:classIndex:"); + runArgs = 0; + goto l1; + } + } + if (GIV(argumentCount) >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),GIV(argumentCount)); + longAtput((void *)(GIV(freeStart) + 4),((sqInt)((usqInt)((numSlotsMask())) << (numSlotsHalfShift())))); + long64Atput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + } + else { + long64Atput((void *)(newObj),((((((usqLong) GIV(argumentCount))) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + runArgs = newObj; + /* end eeInstantiateClassIndex:format:numSlots: */ +l1: + for (i = (GIV(argumentCount) - 1); i >= 0; i += -1) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + valuePointer = top; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(runArgs)) + && (!(isForwarded(runArgs)))); + assert(validStorePointerUncheckedArgs(i, runArgs, valuePointer)); + longAtput((void *)((runArgs + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + + /* begin popStack */ + runReceiver = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* setup send of newMethod run: originalSelector with: runArgs in: runReceiver */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(messageSelector)); + + /* original selector */ + longAtput((GIV(stackPointer) -= BytesPerWord),runArgs); + longAtput((GIV(stackPointer) -= BytesPerWord),runReceiver); + + /* stack is clean here */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorRunWithIn) << (shiftForWord())))))); + GIV(argumentCount) = 3; + lookupClassTag = /* fetchClassTagOf: */ + ((tagBits = GIV(newMethod) & (tagMask())) + ? tagBits + : (longAt((void *)(GIV(newMethod)))) & (classIndexMask())); + findNewMethodInClassTag(lookupClassTag); + executeNewMethod(); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; +} + + +/* Primitive. Answer an Array with the current long-running primitive method + identified by + the heartbeat, the minimum number of milliseconds it was active for, and + the milliseconds + of GC activity there-in, or nil if none. */ + + /* StackInterpreterPrimitives>>#primitiveLongRunningPrimitive */ +#if LRPCheck +EXPORT(void) +primitiveLongRunningPrimitive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong gcms; + sqInt lrpcm; + sqLong primms; + sqInt result; + + lrpcm = 0; + sqLowLevelMFence(); + + /* Since the longRunningPrimitiveCheckMethod is sampled at + interrupt time be careful to validate it before returning it. */ + if ((GIV(longRunningPrimitiveStopUsecs) > GIV(longRunningPrimitiveStartUsecs)) + && ((((lrpcm = GIV(longRunningPrimitiveCheckMethod)))) + && ((addressCouldBeObj(lrpcm)) + && (((byteAt((void *)(lrpcm + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))))) { + result = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 3); + primms = ((GIV(longRunningPrimitiveStopUsecs) - GIV(longRunningPrimitiveStartUsecs)) + 500) / 1000; + gcms = (GIV(longRunningPrimitiveGCUsecs) + 500) / 1000; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(0, result, lrpcm)); + assert(isNonImmediate(result)); + if (oopisGreaterThanOrEqualTo(result, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(lrpcm & (tagMask())))) + && (oopisLessThan(lrpcm, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(result + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(result); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((result + BaseHeaderSize) + (0U << (shiftForWord()))),lrpcm); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(result)) + && (!(isForwarded(result)))); + assert(validStorePointerUncheckedArgs(1, result, (((usqInt)primms << 3) | 1))); + longAtput((void *)((result + BaseHeaderSize) + (1U << (shiftForWord()))),(((usqInt)primms << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(result)) + && (!(isForwarded(result)))); + assert(validStorePointerUncheckedArgs(2, result, ((gcms << 3) | 1))); + longAtput((void *)((result + BaseHeaderSize) + (2U << (shiftForWord()))),((gcms << 3) | 1)); + } + else { + result = GIV(nilObj); + } + + /* a hit */ + voidLongRunningPrimitive("get"); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); +} +#endif /* LRPCheck */ + + +/* This primitive is assumed to be fast (see e.g. + MethodDictionary>>includesKey:) so make it so. + N.B. Works correctly for cogged methods too. */ + + /* StackInterpreterPrimitives>>#primitiveObjectPointsTo */ +static void +primitiveObjectPointsTo(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong fmt; + sqLong header; + sqInt headerSqInt; + sqInt i; + sqInt methodHeader; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + char *sp; + sqInt thang; + sqInt trueOrFalse; + + thang = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((rcvr & (tagMask())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),GIV(falseObj)); + return; + } + + /* Inlined version of lastPointerOf: for speed in determining if rcvr is a context. */ + header = long64At((void *)(rcvr)); + fmt = (((usqLong)(header)) >> (formatShift())) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && ((header & (classIndexMask())) == ClassMethodContextCompactIndex)) { + if (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + trueOrFalse = marriedContextpointsTostackDeltaForCurrentFrame(rcvr, thang, 2); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (trueOrFalse + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return; + } + } + + /* contexts end at the stack pointer */ + numSlots = CtxtTempFrameStart + (fetchStackPointerOf(rcvr)); + } + else { + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + } + } + else { + if (fmt < (firstCompiledMethodFormat())) { + /* begin pop:thenPushBool: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),GIV(falseObj)); + return; + } + + /* no pointers + CompiledMethod: contains both pointers and bytes: */ + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(rcvr)); + headerSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + if (methodHeader == thang) { + /* begin pop:thenPushBool: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),GIV(trueObj)); + return; + } + numSlots = ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart; + } + assert((((numSlots - 1) * BytesPerOop) + BaseHeaderSize) == (lastPointerOf(rcvr))); + for (i = BaseHeaderSize; i <= (((numSlots - 1) * BytesPerOop) + BaseHeaderSize); i += BytesPerOop) { + if ((longAt((void *)(rcvr + i))) == thang) { + /* begin pop:thenPushBool: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),GIV(trueObj)); + return; + } + } + + /* begin pop:thenPushBool: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),GIV(falseObj)); +} + + /* StackInterpreterPrimitives>>#primitivePerform */ +static void +primitivePerform(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt lookupClassTag; + sqInt newReceiver; + usqInt performMethod; + sqInt tagBits; + + performMethod = GIV(newMethod); + GIV(messageSelector) = longAt(GIV(stackPointer) + ((GIV(argumentCount) - 1) * BytesPerWord)); + newReceiver = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* NOTE: the following lookup may fail and be converted to #doesNotUnderstand:, + so we must adjust argumentCount and slide args now, so that will work. + Slide arguments down over selector */ + GIV(argumentCount) -= 1; + for (i = GIV(argumentCount); i >= 1; i += -1) { + longAtput(GIV(stackPointer) + (i * BytesPerWord),longAt(GIV(stackPointer) + ((i - 1) * BytesPerWord))); + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + lookupClassTag = /* fetchClassTagOf: */ + ((tagBits = newReceiver & (tagMask())) + ? tagBits + : (longAt((void *)(newReceiver))) & (classIndexMask())); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), /* fetchClassTagOf: */ + ((tagBits = newReceiver & (tagMask())) + ? tagBits + : (longAt((void *)(newReceiver))) & (classIndexMask()))); + if (printOnTrace()) { + printActivationNameForSelectorstartClass(GIV(messageSelector), classForClassTag(lookupClassTag)); + cr(); + } + findNewMethodInClassTag(lookupClassTag); + + /* Only test CompiledMethods for argument count - other objects will have to take their chances */ + if (!((/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && ((argumentCountOf(GIV(newMethod))) == GIV(argumentCount)))) { + /* Slide the args back up (sigh) and re-insert the selector. */ + + /* begin unPop: */ + GIV(stackPointer) -= 1 * BytesPerWord; + for (i = 1; i <= GIV(argumentCount); i += 1) { + longAtput(GIV(stackPointer) + ((i - 1) * BytesPerWord),longAt(GIV(stackPointer) + (i * BytesPerWord))); + } + longAtput(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord),GIV(messageSelector)); + GIV(argumentCount) += 1; + GIV(newMethod) = performMethod; + + /* Must reset primitiveFunctionPointer for checkForAndFollowForwardedPrimitiveState */ + primitiveFunctionPointer = primitivePerform; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + executeNewMethod(); + + /* Recursive xeq affects primErrorCode */ + + /* begin initPrimCall */ + GIV(primFailCode) = 0; +} + + +/* Pin or unpin the receiver, i.e. make it immobile or mobile, based on the + argument. Answer whether the object was already pinned. N.B. pinning does + *not* prevent + an object from being garbage collected. */ + + /* StackInterpreterPrimitives>>#primitivePin */ +static void +primitivePin(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt boolean; + sqInt obj; + sqInt wasPinned; + + obj = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((obj & (tagMask())) != 0)) + || ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + boolean = longAt(GIV(stackPointer)); + if (!((boolean == GIV(falseObj)) + || (boolean == GIV(trueObj)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if ((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + wasPinned = GIV(trueObj); + if (boolean != wasPinned) { + setIsPinnedOfto(obj, 0); + } + } + else { + wasPinned = GIV(falseObj); + if (boolean == GIV(trueObj)) { + if ((/* isContext: */ + ((!(obj & (tagMask())))) + && (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(obj))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if (!(pinObject(obj))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + return; + } + } + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),wasPinned); +} + + +/* Primitive. Indicate the semaphore to be signalled for upon garbage + collection + */ + + /* StackInterpreterPrimitives>>#primitiveSetGCSemaphore */ +EXPORT(void) +primitiveSetGCSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt integerPointer; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + index = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + index = 0; + } + if (!GIV(primFailCode)) { + GIV(gcSemaphoreIndex) = index; + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } +} + + +/* Cause the time semaphore, if one has been registered, to be + signalled when the microsecond clock is greater than or equal to + the given tick value. A tick value of zero turns off timer interrupts. */ + + /* StackInterpreterPrimitives>>#primitiveSignalAtMilliseconds */ +static void +primitiveSignalAtMilliseconds(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong deltaMsecs; + sqLong limit; + sqLong msecs; + sqInt msecsObj; + sqInt sema; + + msecsObj = longAt(GIV(stackPointer)); + sema = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + msecs = positive32BitValueOf(msecsObj); + if (!GIV(primFailCode)) { + if (/* isSemaphoreOop: */ + ((!(sema & (tagMask())))) + && (((longAt((void *)(sema))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord())))))))))) { + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheTimerSemaphore, GIV(specialObjectsOop), sema)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(sema & (tagMask())))) + && (oopisLessThan(sema, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord()))))),sema); + deltaMsecs = msecs - ((ioMSecs()) & MillisecondClockMask); + limit = ((usqInt)(MillisecondClockMask)) >> 1; + + /* Handle a roll-over that could happen in between image invocation of ioMSecs and this invocation. + This will limit the maximum relative duration to MillisecondClockMask/2, about 3 days currently. + Every delay longer than that limit may lead to undefined behavior (shorten delay, or no delay at all) */ + if (deltaMsecs > limit) { + deltaMsecs -= MillisecondClockMask; + } + GIV(nextWakeupUsecs) = (deltaMsecs > 0 + ? (ioUTCMicroseconds()) + (deltaMsecs * 1000) + : ioUTCMicroseconds()); + + /* begin pop: */ + GIV(stackPointer) += 2 * BytesPerWord; + return; + } + if (sema == GIV(nilObj)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(specialObjectsOop))) + && (!(isForwarded(GIV(specialObjectsOop))))); + assert(validStorePointerUncheckedArgs(TheTimerSemaphore, GIV(specialObjectsOop), GIV(nilObj))); + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord()))))),GIV(nilObj)); + GIV(nextWakeupUsecs) = 0; + + /* begin pop: */ + GIV(stackPointer) += 2 * BytesPerWord; + return; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; +} + + +/* Cause the time semaphore, if one has been registered, to be + signalled when the microsecond clock is greater than or equal to + the given tick value. A tick value of zero turns off timer interrupts. */ + + /* StackInterpreterPrimitives>>#primitiveSignalAtUTCMicroseconds */ +static void +primitiveSignalAtUTCMicroseconds(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt sema; + usqLong usecs; + sqInt usecsObj; + + usecsObj = longAt(GIV(stackPointer)); + sema = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + usecs = positive64BitValueOf(usecsObj); + if (!GIV(primFailCode)) { + if (/* isSemaphoreOop: */ + ((!(sema & (tagMask())))) + && (((longAt((void *)(sema))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord())))))))))) { + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheTimerSemaphore, GIV(specialObjectsOop), sema)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(sema & (tagMask())))) + && (oopisLessThan(sema, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord()))))),sema); + GIV(nextWakeupUsecs) = usecs; + + /* begin pop: */ + GIV(stackPointer) += 2 * BytesPerWord; + return; + } + if (sema == GIV(nilObj)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(specialObjectsOop))) + && (!(isForwarded(GIV(specialObjectsOop))))); + assert(validStorePointerUncheckedArgs(TheTimerSemaphore, GIV(specialObjectsOop), GIV(nilObj))); + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord()))))),GIV(nilObj)); + GIV(nextWakeupUsecs) = 0; + + /* begin pop: */ + GIV(stackPointer) += 2 * BytesPerWord; + return; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; +} + + +/* Answer a slot in an object. This numbers all slots from 1, ignoring the + distinction between + named and indexed inst vars. In objects with both named and indexed inst + vars, the named + inst vars precede the indexed ones. In non-object indexed objects (objects + that contain + bits, not object references) this primitive answers the raw integral value + at each slot. + e.g. for Strings it answers the character code, not the Character object + at each slot. */ +/* because of externalInstVar:ofContext: below */ + + /* StackInterpreterPrimitives>>#primitiveSlotAt */ +static void +primitiveSlotAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt fmtSqInt; + sqInt index; + sqInt numLiveSlots; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt rcvr; + sqInt value; + + index = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if ((((usqInt)index)) < numSlots) { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + numLiveSlots = (stackPointerForMaybeMarriedContext(rcvr)) + CtxtTempFrameStart; + value = ((((usqInt)index)) < numLiveSlots + ? externalInstVarofContext(index, rcvr) + : GIV(nilObj)); + } + else { + value = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + + /* begin numBytesOfBytes: */ + fmtSqInt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert(fmtSqInt >= (firstByteFormat())); + numSlots = ((((/* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) << (shiftForWord()))) - (fmtSqInt & 7); + if ((((usqInt)index)) < numSlots) { + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + index))) << 3) | 1)); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt >= (firstShortFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 1; + if ((((usqInt)index)) < numSlots) { + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)(((unsigned short) (shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 1)))))))) << 3) | 1)); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + oop = positive64BitIntegerFor(long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3)))))); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt >= (firstLongFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)((((usqInt)(long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1)); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; +} + + +/* Assign a slot in an object. This numbers all slots from 1, ignoring the + distinction between named and indexed inst vars. In objects with both + named and indexed + inst vars, the named inst vars precede the indexed ones. In pure bits + indexed objects this primitive assigns a raw integral value to a slot. + N.B. *cannot* run on + Smalltalk stack because receiver may be a context. */ + + /* StackInterpreterPrimitives>>#primitiveSlotAtPut */ +static void +primitiveSlotAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt fmtSqInt; + sqInt index; + sqInt newValue; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + usqIntptr_t value; + sqInt valueSqInt; + + newValue = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + return; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if ((((usqInt)index)) < numSlots) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + externalInstVarofContextput(index, rcvr, newValue); + } + else { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, rcvr, newValue)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(newValue & (tagMask())))) + && (oopisLessThan(newValue, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),newValue); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* begin positiveMachineIntegerValueOf: */ + if ((((newValue) & 7) == 1)) { + valueSqInt = (newValue >> 3); + if (valueSqInt < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + value = null; + goto l1; + } + value = valueSqInt; + goto l1; + } + + /* don't inline the rare case */ + value = positiveMachineIntegerValueOfObj(newValue); + /* end positiveMachineIntegerValueOf: */ +l1: + if (GIV(primFailCode)) { + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + if ((((usqInt)value)) > 0xFF) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* begin numBytesOfBytes: */ + fmtSqInt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert(fmtSqInt >= (firstByteFormat())); + numSlots = ((((/* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) << (shiftForWord()))) - (fmtSqInt & 7); + if ((((usqInt)index)) < numSlots) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + index),value); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt >= (firstShortFormat())) { + if ((((usqInt)value)) > 0xFFFF) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 1; + if ((((usqInt)index)) < numSlots) { + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 1)))),value); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt >= (firstLongFormat())) { + if ((((usqInt)value)) > 0xFFFFFFFFU) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2)))),value); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3)))),value); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; +} + + +/* Atomic store into context stackPointer. + Also ensures that any newly accessible cells are initialized to nil */ + + /* StackInterpreterPrimitives>>#primitiveStoreStackp */ +static void +primitiveStoreStackp(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + sqInt i; + sqInt integerPointer; + sqInt newStackp; + usqInt numSlots; + int onCurrentPage; + sqInt senderOop; + sqInt sp; + sqInt stackp; + char *theFP; + StackPage *thePage; + + ctxt = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + newStackp = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + newStackp = 0; + } + if (!((!GIV(primFailCode)) + && (((newStackp >= 0) && (newStackp <= (((/* begin numSlotsOf: */ + assert((classIndexOf(ctxt)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(ctxt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(ctxt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - CtxtTempFrameStart)))))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(ctxt)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (((onCurrentPage = thePage == GIV(stackPage))) + && (theFP == GIV(framePointer))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + externalDivorceFrameandContext(theFP, ctxt); + if (onCurrentPage) { + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (GIV(stackPage)->headSP); + GIV(framePointer) = (GIV(stackPage)->headFP); + } + else { + assert(GIV(stackPage) == (stackPageFor(GIV(framePointer)))); + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + } + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + stackp = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(ctxt))); + stackp = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + + /* Nil any newly accessible cells */ + for (i = (stackp + 1); i <= newStackp; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs((i + CtxtTempFrameStart) - 1, ctxt, GIV(nilObj))); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(((i + CtxtTempFrameStart) - 1)) << (shiftForWord()))))),GIV(nilObj)); + } + + /* begin storeStackPointerValue:inContext: */ + assert((ReceiverIndex + newStackp) < (lengthOf(ctxt))); + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs(StackPointerIndex, ctxt, (((usqInt)newStackp << 3) | 1))); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord()))))),(((usqInt)newStackp << 3) | 1)); + ensureContextIsExecutionSafeAfterAssignToStackPointer(ctxt); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; +} + + +/* Behaviour depends on argument count: + 0 args: return an Array of VM parameter values; + 1 arg: return the indicated VM parameter; + 2 args: set the VM indicated parameter. + VM parameters are numbered as follows: + 1 end (v3)/size(Spur) of old-space (0-based, read-only) + 2 end (v3)/size(Spur) of young/new-space (read-only) + 3 end (v3)/size(Spur) of heap (read-only) + 4 nil (was allocationCount (read-only)) + 5 nil (was allocations between GCs (read-write) + 6 survivor count tenuring threshold (read-write) + 7 full GCs since startup (read-only) + 8 total milliseconds in full GCs since startup (read-only) + 9 incremental GCs (SqueakV3) or scavenges (Spur) since startup (read-only) + 10 total milliseconds in incremental GCs (SqueakV3) or scavenges (Spur) + since startup (read-only) + 11 tenures of surving objects since startup or reset (read-write) + 12-20 were specific to ikp's JITTER VM, now 12 16 open for use + 13 if started, the start time in utc microseconds of the high-priority + ticker 14 if started, the number of checkHighPriorityTickees calls + 15 if started, the number of tickee calls from checkHighPriorityTickees + 16 total microseconds at idle since start-up (if non-zero) + 17 fraction of the code zone to use (Sista only; used to control code zone + use to preserve sendAndBranchData on counter tripped callback) + 18 total milliseconds in compaction phase of full GC since start-up (Spur + only) 19 scavenge threshold, the effective size of eden. When eden fills + to the threshold a scavenge is scheduled. Newer Spur VMs only. + 20 utc microseconds at VM start-up (actually at time initialization, which + precedes image load). + 21 root/remembered table size (occupancy) (read-only) + 22 root table overflows since startup (read-only) + 23 bytes of extra memory to reserve for VM buffers, plugins, etc (stored + in image file header). + 24 memory threshold above which shrinking object memory (rw) + 25 memory headroom when growing object memory (rw) + 26 interruptChecksEveryNms - force an ioProcessEvents every N milliseconds + (rw) 27 number of times mark loop iterated for current IGC/FGC (read-only) + includes ALL marking + 28 number of times sweep loop iterated for current IGC/FGC (read-only) + 29 number of times make forward loop iterated for current IGC/FGC + (read-only) 30 number of times compact move loop iterated for current + IGC/FGC (read-only) + 31 number of grow memory requests (read-only) + 32 number of shrink memory requests (read-only) + 33 number of root table entries used for current IGC/FGC (read-only) + 34 Spur: bytes allocated in total since start-up or reset (read-write) + (Used to be number of allocations done before current IGC/FGC (read-only)) + 35 number of survivor objects after current IGC/FGC (read-only) + 36 millisecond clock when current IGC/FGC completed (read-only) + 37 number of marked objects for Roots of the world, not including Root + Table entries for current IGC/FGC (read-only) + 38 milliseconds taken by current IGC (read-only) + 39 Number of finalization signals for Weak Objects pending when current + IGC/FGC completed (read-only) + 40 BytesPerOop for this image + 41 imageFormatVersion for the VM + 42 number of stack pages in use + 43 desired number of stack pages (stored in image file header, max 65535) + 44 size of eden, in bytes + 45 desired size of eden, in bytes (stored in image file header) + 46 machine code zone size, in bytes (Cog only; otherwise nil) + 47 desired machine code zone size (stored in image file header; Cog only; + otherwise nil) + 48 various header flags. See getImageHeaderFlags. + 49 max size the image promises to grow the external semaphore table to (0 + sets to default, which is 256 as of writing) + + 50 max literal count for JIT compile (stored in image file header; Cog + only; otherwise nil) + 51 nil; reserved for VM parameters that persist in the image (such as eden + above) 52 root/remembered table capacity + 53 number of segments (Spur only; otherwise nil) + 54 total size of free old space (Spur only, otherwise nil) + 55 ratio of growth and image size at or above which a GC will be performed + post scavenge + 56 number of process switches since startup (read-only) + 57 number of ioProcessEvents calls since startup (read-only) + 58 number of ForceInterruptCheck calls since startup (read-only) + 59 number of check event calls since startup (read-only) + 60 number of stack page overflows since startup (read-only) + 61 number of stack page divorces since startup (read-only) + 62 compiled code compactions since startup (read-only; Cog only; otherwise + nil) 63 total milliseconds in compiled code compactions since startup + (read-only; Cog only; otherwise nil) + 64 the number of methods that currently have jitted machine-code + 65 various VM feature flags; see getCogVMFeatureFlags + 66 the byte size of a stack page + 67 the max allowed size of old space (Spur only; nil otherwise; 0 implies + no limit except that of the underlying platform) + 68 the average number of live stack pages when scanned by GC (at + scavenge/gc/become et al) (read-write) + 69 the maximum number of live stack pages when scanned by GC (at + scavenge/gc/become et al) (read-write) + 70 the vmProxyMajorVersion (the interpreterProxy VM_MAJOR_VERSION) + 71 the vmProxyMinorVersion (the interpreterProxy VM_MINOR_VERSION) + 72 total milliseconds in full GCs Mark phase since startup (read-only) + 73 total milliseconds in full GCs Sweep phase since startup (read-only, + can be 0 depending on compactors) + 74 maximum pause time due to segment allocation + 75 whether the arithmetic primitives perform conversion in case of mixed + SmallInteger/Float (true) or fail (false) + 76 the minimum unused headroom in all stack pages; Cog VMs only + 77 whether the numweric comparison primitives perform conversion in case + of mixed SmallInteger/Float (true) or fail (false) + + Note: Thanks to Ian Piumarta for this primitive. */ + + /* StackInterpreterPrimitives>>#primitiveVMParameter */ +static void +primitiveVMParameter(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aBool; + sqInt activeContext; + double aProportion; + sqIntptr_t arg; + sqInt argOop; + sqLong delta; + sqInt index; + usqInt limit; + int mustFlush; + usqInt oldMixedArithmeticHeaderFlags; + sqInt oop; + sqInt paramsArraySize; + sqInt result; + sqInt resultSqInt; + sqInt senderOop; + sqInt tagBits; + char *theFrame; + StackPage *thePage; + sqInt top; + sqInt value; + sqInt valuePointer; + + paramsArraySize = 77; + if (!GIV(argumentCount)) { + /* begin primitiveAllVMParameters: */ + resultSqInt = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), paramsArraySize); + valuePointer = positive64BitIntegerFor( + (/* begin oldSpaceSize *//* begin totalOldSpaceSize */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), + GIV(totalHeapSizeIncludingBridges))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(0, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)((GIV(freeStart) - (((GIV(eden)).start))) + (GIV(pastSpaceStart) - (((GIV(pastSpace)).start)))) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(1, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor((newSpaceCapacity()) + ((/* begin totalOldSpaceSize */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges)))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(2, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (2U << (shiftForWord()))),valuePointer); + + /* objectMemory storePointerUnchecked: 3 ofObject: result withValue: objectMemory nilObject was allocationCount + objectMemory storePointerUnchecked: 4 ofObject: result withValue: objectMemory nilObject allocationsBetweenGCs */ + valuePointer = (((usqInt)(((sqInt)(((scavengerTenuringThreshold()) * (((GIV(pastSpace).limit)) - ((GIV(pastSpace).start)))) / (8 * BytesPerOop)))) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(5, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (5U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(6, resultSqInt, (((usqInt)GIV(statFullGCs) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (6U << (shiftForWord()))),(((usqInt)GIV(statFullGCs) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(7, resultSqInt, ((((GIV(statFullGCUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (7U << (shiftForWord()))),((((GIV(statFullGCUsecs) + 500) / 1000) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(8, resultSqInt, (((usqInt)GIV(statScavenges) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (8U << (shiftForWord()))),(((usqInt)GIV(statScavenges) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(9, resultSqInt, ((((GIV(statScavengeGCUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (9U << (shiftForWord()))),((((GIV(statScavengeGCUsecs) + 500) / 1000) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(10, resultSqInt, (((usqInt)GIV(statTenures) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (10U << (shiftForWord()))),(((usqInt)GIV(statTenures) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(11, resultSqInt, (((usqInt)eventTraceMask << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (11U << (shiftForWord()))),(((usqInt)eventTraceMask << 3) | 1)); + valuePointer = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickerStartUSecs()) +# else + ConstZero +# endif + ; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(12, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (12U << (shiftForWord()))),valuePointer); + valuePointer = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickerCount()) +# else + ConstZero +# endif + ; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(13, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (13U << (shiftForWord()))),valuePointer); + valuePointer = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickeeCallCount()) +# else + ConstZero +# endif + ; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(14, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (14U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statIdleUsecs)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(15, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (15U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(16, resultSqInt, ConstZero)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (16U << (shiftForWord()))),ConstZero); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(17, resultSqInt, ((((GIV(statCompactionUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (17U << (shiftForWord()))),((((GIV(statCompactionUsecs) + 500) / 1000) << 3) | 1)); + valuePointer = (((usqInt)(GIV(scavengeThreshold) - (((GIV(eden)).start))) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(18, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (18U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(ioUTCStartMicroseconds()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(19, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (19U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)(rootTableCount()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(20, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (20U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(21, resultSqInt, (((usqInt)GIV(statRootTableOverflows) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (21U << (shiftForWord()))),(((usqInt)GIV(statRootTableOverflows) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(22, resultSqInt, (((usqInt)extraVMMemory << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (22U << (shiftForWord()))),(((usqInt)extraVMMemory << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(23, resultSqInt, (((usqInt)GIV(shrinkThreshold) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (23U << (shiftForWord()))),(((usqInt)GIV(shrinkThreshold) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(24, resultSqInt, (((usqInt)GIV(growHeadroom) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (24U << (shiftForWord()))),(((usqInt)GIV(growHeadroom) << 3) | 1)); + valuePointer = (((usqInt)(ioHeartbeatMilliseconds()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(25, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (25U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(26, resultSqInt, (((usqInt)GIV(statMarkCount) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (26U << (shiftForWord()))),(((usqInt)GIV(statMarkCount) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(27, resultSqInt, (((usqInt)0 /* statSweepCount */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (27U << (shiftForWord()))),(((usqInt)0 /* statSweepCount */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(28, resultSqInt, (((usqInt)0 /* statMkFwdCount */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (28U << (shiftForWord()))),(((usqInt)0 /* statMkFwdCount */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(29, resultSqInt, (((usqInt)GIV(statCompactPassCount) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (29U << (shiftForWord()))),(((usqInt)GIV(statCompactPassCount) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(30, resultSqInt, (((usqInt)GIV(statGrowMemory) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (30U << (shiftForWord()))),(((usqInt)GIV(statGrowMemory) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(0x1F, resultSqInt, (((usqInt)GIV(statShrinkMemory) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (0x1FU << (shiftForWord()))),(((usqInt)GIV(statShrinkMemory) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(32, resultSqInt, (((usqInt)GIV(statRootTableCount) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (32U << (shiftForWord()))),(((usqInt)GIV(statRootTableCount) << 3) | 1)); + valuePointer = positive64BitIntegerFor(currentAllocatedBytes()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(33, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (33U << (shiftForWord()))),valuePointer); + + /* was statAllocationCount */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(34, resultSqInt, (((usqInt)GIV(statSurvivorCount) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (34U << (shiftForWord()))),(((usqInt)GIV(statSurvivorCount) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(35, resultSqInt, ((((GIV(statGCEndUsecs) / 1000) & MillisecondClockMask) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (35U << (shiftForWord()))),((((GIV(statGCEndUsecs) / 1000) & MillisecondClockMask) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(36, resultSqInt, (((usqInt)0 /* statSpecialMarkCount */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (36U << (shiftForWord()))),(((usqInt)0 /* statSpecialMarkCount */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(37, resultSqInt, ((((GIV(statIGCDeltaUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (37U << (shiftForWord()))),((((GIV(statIGCDeltaUsecs) + 500) / 1000) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(38, resultSqInt, (((usqInt)GIV(statPendingFinalizationSignals) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (38U << (shiftForWord()))),(((usqInt)GIV(statPendingFinalizationSignals) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(39, resultSqInt, (((usqInt)BytesPerWord << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (39U << (shiftForWord()))),(((usqInt)BytesPerWord << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(40, resultSqInt, (((usqInt)68021 /* imageFormatVersion */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (40U << (shiftForWord()))),(((usqInt)68021 /* imageFormatVersion */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(41, resultSqInt, (((usqInt)GIV(numStackPages) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (41U << (shiftForWord()))),(((usqInt)GIV(numStackPages) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(42, resultSqInt, (((usqInt)desiredNumStackPages << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (42U << (shiftForWord()))),(((usqInt)desiredNumStackPages << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(43, resultSqInt, (((usqInt)(((GIV(eden).limit)) - ((GIV(eden).start))) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (43U << (shiftForWord()))),(((usqInt)(((GIV(eden).limit)) - ((GIV(eden).start))) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(44, resultSqInt, (((usqInt)desiredEdenBytes << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (44U << (shiftForWord()))),(((usqInt)desiredEdenBytes << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(45, resultSqInt, ((GIV(cogCodeSize) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (45U << (shiftForWord()))),((GIV(cogCodeSize) << 3) | 1)); + valuePointer = getDesiredCogCodeSize(); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(46, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (46U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(47, resultSqInt, (((usqInt)(((usqInt)(imageHeaderFlags)) >> 2) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (47U << (shiftForWord()))),(((usqInt)(((usqInt)(imageHeaderFlags)) >> 2) << 3) | 1)); + valuePointer = (((usqInt)(ioGetMaxExtSemTableSize()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(48, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (48U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(49, resultSqInt, (((usqInt)maxLiteralCountForCompile << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (49U << (shiftForWord()))),(((usqInt)maxLiteralCountForCompile << 3) | 1)); + valuePointer = (((usqInt)(((GIV(oldImageBaseAddress) != GIV(oldSpaceStart) + ? 1 + : 0)) + ((sqImageFileIsEmbedded() + ? 2 + : 0))) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(50, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (50U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)(rootTableCapacity()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(51, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (51U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(52, resultSqInt, (((usqInt)GIV(numSegments) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (52U << (shiftForWord()))),(((usqInt)GIV(numSegments) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(53, resultSqInt, ((GIV(totalFreeOldSpace) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (53U << (shiftForWord()))),((GIV(totalFreeOldSpace) << 3) | 1)); + valuePointer = floatObjectOf(getHeapGrowthToSizeGCRatio()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(54, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (54U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statProcessSwitch)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(55, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (55U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statIOProcessEvents)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(56, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (56U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statForceInterruptCheck)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(57, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (57U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statCheckForEvents)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(58, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (58U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statStackOverflow)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(59, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (59U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statStackPageDivorce)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(60, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (60U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(61, resultSqInt, (((usqInt)GIV(statCodeCompactionCount) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (61U << (shiftForWord()))),(((usqInt)GIV(statCodeCompactionCount) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(0x3E, resultSqInt, ((((GIV(statCodeCompactionUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (0x3EU << (shiftForWord()))),((((GIV(statCodeCompactionUsecs) + 500) / 1000) << 3) | 1)); + valuePointer = (((usqInt)(numMethodsOfType(CMMethod)) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(0x3F, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (0x3FU << (shiftForWord()))),valuePointer); + valuePointer = getCogVMFeatureFlags(); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(64, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (64U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(65, resultSqInt, (((usqInt)GIV(bytesPerPage) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (65U << (shiftForWord()))),(((usqInt)GIV(bytesPerPage) << 3) | 1)); + valuePointer = positive64BitIntegerFor(maxOldSpaceSize); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(66, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (66U << (shiftForWord()))),valuePointer); + valuePointer = floatObjectOf(statAverageLivePagesWhenMapping()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(67, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (67U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)(GIV(statMaxPageCountWhenMapping)) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(68, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (68U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(69, resultSqInt, (((usqInt)VM_PROXY_MAJOR /* vmProxyMajorVersion */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (69U << (shiftForWord()))),(((usqInt)VM_PROXY_MAJOR /* vmProxyMajorVersion */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(70, resultSqInt, (((usqInt)VM_PROXY_MINOR /* vmProxyMinorVersion */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (70U << (shiftForWord()))),(((usqInt)VM_PROXY_MINOR /* vmProxyMinorVersion */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(71, resultSqInt, ((((GIV(statMarkUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (71U << (shiftForWord()))),((((GIV(statMarkUsecs) + 500) / 1000) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(72, resultSqInt, ((((GIV(statSweepUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (72U << (shiftForWord()))),((((GIV(statSweepUsecs) + 500) / 1000) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(73, resultSqInt, (((usqInt)((GIV(statMaxAllocSegmentTime) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (73U << (shiftForWord()))),(((usqInt)((GIV(statMaxAllocSegmentTime) + 500) / 1000) << 3) | 1)); + aBool = numericPrimsMixArithmetic(); + + /* begin booleanObjectOf: */ + valuePointer = (aBool + ? GIV(trueObj) + : GIV(falseObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(74, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (74U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)(minimumUnusedHeadroom()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(75, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (75U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)(numericPrimsMixComparison()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(76, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (76U << (shiftForWord()))),valuePointer); + beRootIfOld(resultSqInt); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),resultSqInt); + return; + } + if (GIV(argumentCount) > 2) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + + /* index read & checks */ + index = longAt(GIV(stackPointer) + (((GIV(argumentCount) == 1 + ? 0 + : 1)) * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + index = (index >> 3); + if ((index < 1) + || (index > paramsArraySize)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (GIV(argumentCount) == 1) { + /* begin primitiveGetVMParameter: */ + switch (index) { + case 1: + result = positive64BitIntegerFor( + (/* begin oldSpaceSize *//* begin totalOldSpaceSize */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), + GIV(totalHeapSizeIncludingBridges))); + goto l1; + break; + case 2: + result = (((usqInt)((GIV(freeStart) - (((GIV(eden)).start))) + (GIV(pastSpaceStart) - (((GIV(pastSpace)).start)))) << 3) | 1); + goto l1; + break; + case 3: + result = positive64BitIntegerFor((newSpaceCapacity()) + ((/* begin totalOldSpaceSize */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges)))); + goto l1; + break; + case 6: + result = (((usqInt)(((sqInt)(((scavengerTenuringThreshold()) * (((GIV(pastSpace).limit)) - ((GIV(pastSpace).start)))) / (8 * BytesPerOop)))) << 3) | 1); + goto l1; + break; + case 7: + result = (((usqInt)GIV(statFullGCs) << 3) | 1); + goto l1; + break; + case 8: + result = ((((GIV(statFullGCUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 9: + result = (((usqInt)GIV(statScavenges) << 3) | 1); + goto l1; + break; + case 10: + result = ((((GIV(statScavengeGCUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 11: + result = (((usqInt)GIV(statTenures) << 3) | 1); + goto l1; + break; + case 12: + result = (((usqInt)eventTraceMask << 3) | 1); + goto l1; + break; + case 13: + result = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickerStartUSecs()) +# else + ConstZero +# endif + ; + goto l1; + break; + case 14: + result = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickerCount()) +# else + ConstZero +# endif + ; + goto l1; + break; + case 15: + result = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickeeCallCount()) +# else + ConstZero +# endif + ; + goto l1; + break; + case 16: + result = positive64BitIntegerFor(GIV(statIdleUsecs)); + goto l1; + break; + case 17: + result = ConstZero; + goto l1; + break; + case 18: + result = ((((GIV(statCompactionUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 19: + result = (((usqInt)(GIV(scavengeThreshold) - (((GIV(eden)).start))) << 3) | 1); + goto l1; + break; + case 20: + result = positive64BitIntegerFor(ioUTCStartMicroseconds()); + goto l1; + break; + case 21: + result = (((usqInt)(rootTableCount()) << 3) | 1); + goto l1; + break; + case 22: + result = (((usqInt)GIV(statRootTableOverflows) << 3) | 1); + goto l1; + break; + case 23: + result = (((usqInt)extraVMMemory << 3) | 1); + goto l1; + break; + case 24: + result = (((usqInt)GIV(shrinkThreshold) << 3) | 1); + goto l1; + break; + case 25: + result = (((usqInt)GIV(growHeadroom) << 3) | 1); + goto l1; + break; + case 26: + result = (((usqInt)(ioHeartbeatMilliseconds()) << 3) | 1); + goto l1; + break; + case 27: + result = (((usqInt)GIV(statMarkCount) << 3) | 1); + goto l1; + break; + case 28: + result = (((usqInt)0 /* statSweepCount */ << 3) | 1); + goto l1; + break; + case 29: + result = (((usqInt)0 /* statMkFwdCount */ << 3) | 1); + goto l1; + break; + case 30: + result = (((usqInt)GIV(statCompactPassCount) << 3) | 1); + goto l1; + break; + case 0x1F: + result = (((usqInt)GIV(statGrowMemory) << 3) | 1); + goto l1; + break; + case 32: + result = (((usqInt)GIV(statShrinkMemory) << 3) | 1); + goto l1; + break; + case 33: + result = (((usqInt)GIV(statRootTableCount) << 3) | 1); + goto l1; + break; + case 34: + result = positive64BitIntegerFor(currentAllocatedBytes()); + goto l1; + break; + case 35: + result = (((usqInt)GIV(statSurvivorCount) << 3) | 1); + goto l1; + break; + case 36: + result = ((((GIV(statGCEndUsecs) / 1000) & MillisecondClockMask) << 3) | 1); + goto l1; + break; + case 37: + result = (((usqInt)0 /* statSpecialMarkCount */ << 3) | 1); + goto l1; + break; + case 38: + result = ((((GIV(statIGCDeltaUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 39: + result = (((usqInt)GIV(statPendingFinalizationSignals) << 3) | 1); + goto l1; + break; + case 40: + result = (((usqInt)BytesPerWord << 3) | 1); + goto l1; + break; + case 41: + result = (((usqInt)68021 /* imageFormatVersion */ << 3) | 1); + goto l1; + break; + case 42: + result = (((usqInt)GIV(numStackPages) << 3) | 1); + goto l1; + break; + case 43: + result = (((usqInt)desiredNumStackPages << 3) | 1); + goto l1; + break; + case 44: + result = (((usqInt)(((GIV(eden).limit)) - ((GIV(eden).start))) << 3) | 1); + goto l1; + break; + case 45: + result = (((usqInt)desiredEdenBytes << 3) | 1); + goto l1; + break; + case 46: + result = ((GIV(cogCodeSize) << 3) | 1); + goto l1; + break; + case 47: + result = getDesiredCogCodeSize(); + goto l1; + break; + case 48: + result = (((usqInt)(((usqInt)(imageHeaderFlags)) >> 2) << 3) | 1); + goto l1; + break; + case 49: + result = (((usqInt)(ioGetMaxExtSemTableSize()) << 3) | 1); + goto l1; + break; + case 50: + result = (((usqInt)maxLiteralCountForCompile << 3) | 1); + goto l1; + break; + case 51: + result = (((usqInt)(((GIV(oldImageBaseAddress) != GIV(oldSpaceStart) + ? 1 + : 0)) + ((sqImageFileIsEmbedded() + ? 2 + : 0))) << 3) | 1); + goto l1; + break; + case 52: + result = (((usqInt)(rootTableCapacity()) << 3) | 1); + goto l1; + break; + case 53: + result = (((usqInt)GIV(numSegments) << 3) | 1); + goto l1; + break; + case 54: + result = ((GIV(totalFreeOldSpace) << 3) | 1); + goto l1; + break; + case 55: + result = floatObjectOf(getHeapGrowthToSizeGCRatio()); + goto l1; + break; + case 56: + result = positive64BitIntegerFor(GIV(statProcessSwitch)); + goto l1; + break; + case 57: + result = positive64BitIntegerFor(GIV(statIOProcessEvents)); + goto l1; + break; + case 58: + result = positive64BitIntegerFor(GIV(statForceInterruptCheck)); + goto l1; + break; + case 59: + result = positive64BitIntegerFor(GIV(statCheckForEvents)); + goto l1; + break; + case 60: + result = positive64BitIntegerFor(GIV(statStackOverflow)); + goto l1; + break; + case 61: + result = positive64BitIntegerFor(GIV(statStackPageDivorce)); + goto l1; + break; + case 0x3E: + result = (((usqInt)GIV(statCodeCompactionCount) << 3) | 1); + goto l1; + break; + case 0x3F: + result = ((((GIV(statCodeCompactionUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 64: + result = (((usqInt)(numMethodsOfType(CMMethod)) << 3) | 1); + goto l1; + break; + case 65: + result = getCogVMFeatureFlags(); + goto l1; + break; + case 66: + result = (((usqInt)GIV(bytesPerPage) << 3) | 1); + goto l1; + break; + case 67: + result = positive64BitIntegerFor(maxOldSpaceSize); + goto l1; + break; + case 68: + result = floatObjectOf(statAverageLivePagesWhenMapping()); + goto l1; + break; + case 69: + result = (((usqInt)(GIV(statMaxPageCountWhenMapping)) << 3) | 1); + goto l1; + break; + case 70: + result = (((usqInt)VM_PROXY_MAJOR /* vmProxyMajorVersion */ << 3) | 1); + goto l1; + break; + case 71: + result = (((usqInt)VM_PROXY_MINOR /* vmProxyMinorVersion */ << 3) | 1); + goto l1; + break; + case 72: + result = ((((GIV(statMarkUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 73: + result = ((((GIV(statSweepUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 74: + result = (((usqInt)((GIV(statMaxAllocSegmentTime) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 75: + aBool = numericPrimsMixArithmetic(); + result = (aBool + ? GIV(trueObj) + : GIV(falseObj)); + goto l1; + break; + case 76: + result = (((usqInt)(minimumUnusedHeadroom()) << 3) | 1); + goto l1; + break; + case 77: + aBool = numericPrimsMixComparison(); + result = (aBool + ? GIV(trueObj) + : GIV(falseObj)); + goto l1; + break; + default: + result = null; + goto l1; + } + /* end primitiveGetVMParameter: */ +l1: + oop = (!(result) + ? GIV(nilObj) + : result); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return; + } + + /* read VM parameter; written this way to avoid branch limits in V3 bytecode set + written thus to enable Slang inlining of primitiveGetVMParameter: + write a VM parameter */ + argOop = longAt(GIV(stackPointer)); + + /* begin primitiveSetVMParameter:arg: */ + switch (index) { + case 17: + case 55: + case 68: + if (!((/* isFloatInstance: */ + ((tagBits = argOop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex)) + || ((((argOop) & 7) == 1)))) { + GIV(primFailCode) = PrimErrBadArgument; + } + break; + case 67: + /* begin positiveMachineIntegerValueOf: */ + if ((((argOop) & 7) == 1)) { + value = (argOop >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = ((usqIntptr_t) null); + goto l2; + } + arg = ((usqIntptr_t) value); + goto l2; + } + + /* don't inline the rare case */ + arg = positiveMachineIntegerValueOfObj(argOop); + /* end positiveMachineIntegerValueOf: */ +l2:; + break; + case 75: + case 77: + /* begin booleanValueOf: */ + if (argOop == GIV(trueObj)) { + arg = 1; + goto l3; + } + if (argOop == GIV(falseObj)) { + arg = 0; + goto l3; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = null; + /* end booleanValueOf: */ +l3:; + break; + default: + arg = (argOop >> 3); + } + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l6; + } + + /* assume failure, then set success for handled indices */ + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + switch (index) { + case 5: + break; + case 6: + resultSqInt = (((usqInt)(((sqInt)(((scavengerTenuringThreshold()) * (((GIV(pastSpace).limit)) - ((GIV(pastSpace).start)))) / (8 * BytesPerOop)))) << 3) | 1); + + /* begin tenuringThreshold: */ + if (arg < 0) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + aProportion = (((double) (arg * (8 * BytesPerOop)) )) / (((double) (((GIV(pastSpace).limit)) - ((GIV(pastSpace).start))) )); + + /* begin scavengerTenuringThreshold: */ + GIV(tenuringProportion) = aProportion; + GIV(tenureThreshold) = (aProportion == 0.0 + ? 0 + : (round(((((GIV(pastSpace).limit)) - ((GIV(pastSpace).start))) * (1.0 - aProportion)))) + ((GIV(pastSpace).start))); + GIV(primFailCode) = 0; + /* end tenuringThreshold: */ +l4:; + break; + case 12: + resultSqInt = (((usqInt)eventTraceMask << 3) | 1); + eventTraceMask = arg; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + break; + case 11: + if (arg >= 0) { + resultSqInt = (((usqInt)GIV(statTenures) << 3) | 1); + + /* begin statTenures: */ + GIV(statTenures) = arg; + GIV(primFailCode) = 0; + } + break; + case 17: + break; + case 23: + resultSqInt = (((usqInt)extraVMMemory << 3) | 1); + extraVMMemory = arg; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + break; + case 24: + if (arg > 0) { + resultSqInt = (((usqInt)GIV(shrinkThreshold) << 3) | 1); + + /* begin shrinkThreshold: */ + GIV(shrinkThreshold) = arg; + GIV(primFailCode) = 0; + } + break; + case 25: + if (arg > 0) { + resultSqInt = (((usqInt)GIV(growHeadroom) << 3) | 1); + + /* growHeadroom: */ + GIV(growHeadroom) = arg; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + } + break; + case 26: + if (arg >= 0) { + resultSqInt = (((usqInt)(ioHeartbeatMilliseconds()) << 3) | 1); + ioSetHeartbeatMilliseconds(arg); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + } + break; + case 34: + if (arg >= 0) { + resultSqInt = positive64BitIntegerFor(currentAllocatedBytes()); + + /* begin setCurrentAllocatedBytesTo: */ + delta = (currentAllocatedBytes()) - GIV(statAllocatedBytes); + GIV(statAllocatedBytes) = arg; + GIV(oldSpaceUsePriorToScavenge) += delta; + assert((currentAllocatedBytes()) == arg); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + } + break; + case 43: + if (((arg >= 0) && (arg <= 0xFFFF))) { + resultSqInt = (((usqInt)desiredNumStackPages << 3) | 1); + desiredNumStackPages = arg; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + } + break; + case 45: + if (arg >= 0) { + resultSqInt = (((usqInt)desiredEdenBytes << 3) | 1); + desiredEdenBytes = arg; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + } + break; + case 47: + if (((arg >= 0) && (arg <= (maxCogCodeSize())))) { + resultSqInt = (((usqInt)(getDesiredCogCodeSize()) << 3) | 1); + + /* begin setDesiredCogCodeSize: */ + desiredCogCodeSize = arg; + GIV(primFailCode) = 0; + } + break; + case 48: + if (arg >= 0) { + /* answer old value */ + resultSqInt = (((usqInt)(((usqInt)(imageHeaderFlags)) >> 2) << 3) | 1); + + /* must flush/recompile if either of these are changed */ + oldMixedArithmeticHeaderFlags = imageHeaderFlags & 0x900; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if ((((usqInt)arg)) > 0x3FF) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + goto l5; + } + imageHeaderFlags = (((imageHeaderFlags | ((0xFFC))) - ((0xFFC)))) | (((((usqIntptr_t)(arg) << 2)))); + /* end setImageHeaderFlags: */ +l5: + if ((GIV(primFailCode) == 0) + && (oldMixedArithmeticHeaderFlags != (imageHeaderFlags & 0x900))) { + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + flushMethodsWithMachineCodePrimitivesAndContinueAnswering(resultSqInt); + goto l6; + } + } + break; + case 49: + if (((arg >= 0) && (arg <= 0xFFFF))) { + resultSqInt = (((usqInt)(ioGetMaxExtSemTableSize()) << 3) | 1); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + GIV(maxExtSemTabSizeSet) = 1; + ioSetMaxExtSemTableSize(arg); + } + break; + case 50: + if (((arg >= 0) && (arg <= 0x7FFF))) { + resultSqInt = (((usqInt)((((usqInt)maxLiteralCountForCompile << 3) | 1)) << 3) | 1); + + /* begin setMaxLiteralCountForCompile: */ + maxLiteralCountForCompile = (GIV(the2ndUnknownShort) = arg); + GIV(primFailCode) = 0; + activeContext = divorceAllFramesSuchThat(isMachineCodeFrameForCogMethodWithTooManyLiterals); + ensureAllContextsHaveBytecodePCsIf(methodHasTooManyLiterals); + unlinkSendsToMethodsSuchThatAndFreeIf(cogMethodHasTooManyLiterals, 1); + + /* If the top frame was divorced then continue in the interpreter. */ + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* pop bogus machine-code instructionPointer, arguments and receiver */ + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 2) - 1) * BytesPerWord),resultSqInt); + ceInvokeInterpret(); + } + + /* If not, work out where we are and continue */ + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + assert(((thePage->headFP)) == theFrame); + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),resultSqInt); + goto l6; + } + break; + case 55: + resultSqInt = floatObjectOf(getHeapGrowthToSizeGCRatio()); + + /* begin setHeapGrowthToSizeGCRatio: */ + GIV(heapGrowthToSizeGCRatio) = loadFloatOrIntFrom(argOop); + GIV(primFailCode) = 0; + break; + case 67: + if (arg >= 0) { + resultSqInt = positive64BitIntegerFor(maxOldSpaceSize); + limit = arg; + + /* begin setMaxOldSpaceSize: */ + maxOldSpaceSize = limit; + GIV(primFailCode) = 0; + } + break; + case 68: + resultSqInt = floatObjectOf(statAverageLivePagesWhenMapping()); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if ((loadFloatOrIntFrom(argOop)) == 0.0) { + GIV(statPageCountWhenMappingSum) = (GIV(statNumMaps) = 0); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + break; + case 69: + if (arg >= 0) { + resultSqInt = (((usqInt)(GIV(statMaxPageCountWhenMapping)) << 3) | 1); + + /* begin statMaxPageCountWhenMapping: */ + GIV(statMaxPageCountWhenMapping) = arg; + GIV(primFailCode) = 0; + } + break; + case 74: + if (arg >= 0) { + resultSqInt = (((usqInt)((GIV(statMaxAllocSegmentTime) + 500) / 1000) << 3) | 1); + + /* begin statMaxAllocSegmentTime: */ + GIV(statMaxAllocSegmentTime) = arg; + GIV(primFailCode) = 0; + } + break; + case 75: + aBool = numericPrimsMixArithmetic(); + + /* begin booleanObjectOf: */ + resultSqInt = (aBool + ? GIV(trueObj) + : GIV(falseObj)); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + mustFlush = (numericPrimsMixArithmetic()) != arg; + + /* setPrimitiveDoMixedArithmetic: */ + imageHeaderFlags = (arg + ? imageHeaderFlags | 0x100 + : ((imageHeaderFlags | 0x100) - 0x100)); + if (mustFlush) { + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + flushMethodsWithMachineCodePrimitivesAndContinueAnswering(resultSqInt); + goto l6; + } + break; + case 77: + aBool = numericPrimsMixComparison(); + + /* begin booleanObjectOf: */ + resultSqInt = (aBool + ? GIV(trueObj) + : GIV(falseObj)); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + mustFlush = (numericPrimsMixComparison()) != arg; + + /* setPrimitiveDoMixedNumericComparison: */ + imageHeaderFlags = (arg + ? imageHeaderFlags | 0x800 + : ((imageHeaderFlags | 0x800) - 0x800)); + if (mustFlush) { + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + flushMethodsWithMachineCodePrimitivesAndContinueAnswering(resultSqInt); + goto l6; + } + break; + default: +; + } + + /* disable inlining... + disable inlining... + disable inlining... */ + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),resultSqInt); + } + /* end primitiveSetVMParameter:arg: */ +l6:; +} + + +/* Potentially crash the VM by voiding the receiver. A subsequent inst var + access in the caller's frame should indirect through a null pointer. */ + + /* StackInterpreterPrimitives>>#primitiveVoidReceiver */ +EXPORT(void) +primitiveVoidReceiver(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + longAtput(/* frameReceiverLocation: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? GIV(framePointer) + FoxMFReceiver + : GIV(framePointer) + FoxIFReceiver),0); +} + + +/* Prune the stack to contain only the path, removing stacked indices + and mapping frame pointers to contexts The issue here is that a + GC can occur during ensureFrameIsMarried:SP:, but frame pointers + are not valid objects. So first prune back to objects and framePointers + as integers, and then replace frame pointers as integers by contexts. */ + + /* StackInterpreterPrimitives>>#pruneStack:stackp: */ +static NoDbgRegParms void +pruneStackstackp(sqInt stack, sqInt stackp) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt finger; + char *fp; + sqInt i; + sqInt objOrFP; + sqInt oop; + char *theFP; + char *theFPAbove; + StackPage *thePage; + char *theSP; + sqInt theStack; + sqInt toDoLimit; + + finger = 1; + for (i = 2; i < stackp; i += 2) { + objOrFP = longAt((void *)((stack + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)(((char *) objOrFP)))) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)(((char *) objOrFP)))) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)(((char *) objOrFP)))) <= (((usqInt)GIV(pages)))))))) { + objOrFP += smallIntegerTag(); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(finger, stack, objOrFP)); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(finger) << (shiftForWord()))))),objOrFP); + finger += 1; + } + toDoLimit = (lengthOf(stack)) - 1; + for (i = finger; i <= toDoLimit; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(i, stack, GIV(nilObj))); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } + oop = (theStack = stack); + + /* begin pushRemappableOop: */ + assert(addressCouldBeOop(oop)); + GIV(remapBuffer)[(GIV(remapBufferCount) += 1)] = oop; + if (!(GIV(remapBufferCount) <= RemapBufferSize)) { + error("remapBuffer overflow"); + } + for (i = 1; i < finger; i += 1) { + objOrFP = longAt((void *)((theStack + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((((objOrFP) & 7) == 1)) { + /* begin withoutSmallIntegerTags: */ + assert((((objOrFP) & 7) == 1)); + theFP = ((char *)(objOrFP - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == theFP) { + theFPAbove = 0; + goto l1; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == theFP) { + theFPAbove = fp; + goto l1; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + theFPAbove = 0; + /* end findFrameAbove:inPage: */ +l1: + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(theFPAbove))); + theSP = (theFPAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFPAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFPAbove))->cmNumArgs) + : byteAt((theFPAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + objOrFP = longAt(theFP + FoxThisContext); + goto l2; + } + objOrFP = marryFrameSP(theFP, theSP); + /* end ensureFrameIsMarried:SP: */ +l2: + theStack = GIV(remapBuffer)[GIV(remapBufferCount)]; + + /* after a GC stack may no longer be a root. */ + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(finger, theStack, objOrFP)); + assert(isNonImmediate(theStack)); + if (oopisGreaterThanOrEqualTo(theStack, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objOrFP & (tagMask())))) + && (oopisLessThan(objOrFP, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(theStack + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(theStack); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((theStack + BaseHeaderSize) + ((((usqInt)(finger) << (shiftForWord()))))),objOrFP); + } + } + + /* begin popRemappableOop */ + oop = GIV(remapBuffer)[GIV(remapBufferCount)]; + GIV(remapBufferCount) -= 1; +} + + /* StackInterpreterPrimitives>>#unmarkAfterPathTo */ +static void +unmarkAfterPathTo(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + unmarkAllFrames(); + + /* begin unmarkAllObjects */ + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + else { + if (!(((longAt((void *)(objOop))) & (classIndexMask())) == (segmentBridgePun()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + else { + if (!(((longAt((void *)(objOop))) & (classIndexMask())) == (segmentBridgePun()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3:; +} + +/*** Exports ***/ + +static char _m[] = ""; +void* vm_exports[][3] = { + {(void*)_m, "moduleUnloaded", (void*)moduleUnloaded}, + {(void*)_m, "primitiveAddLargeIntegers\000\000\001", (void*)primitiveAddLargeIntegers}, + {(void*)_m, "primitiveAllInstances\000\000\000", (void*)primitiveAllInstances}, +#if VMInvestigations + {(void*)_m, "primitiveAllMethodsCompiledToMachineCode\000\377\000", (void*)primitiveAllMethodsCompiledToMachineCode}, +#endif /* VMInvestigations */ + {(void*)_m, "primitiveAllObjects\000\377\000", (void*)primitiveAllObjects}, +#if VMInvestigations + {(void*)_m, "primitiveBenchmarkFollowForwardersInStackZone\000\377\000", (void*)primitiveBenchmarkFollowForwardersInStackZone}, +#endif /* VMInvestigations */ +#if VMInvestigations + {(void*)_m, "primitiveBenchmarkFollowForwardersOfReceiverAndTemporariesInStackZone\000\377\000", (void*)primitiveBenchmarkFollowForwardersOfReceiverAndTemporariesInStackZone}, +#endif /* VMInvestigations */ +#if VMInvestigations + {(void*)_m, "primitiveBenchmarkJITReceiver\000\000\000", (void*)primitiveBenchmarkJITReceiver}, +#endif /* VMInvestigations */ +#if VMInvestigations + {(void*)_m, "primitiveBenchmarkScavenge\000\377\000", (void*)primitiveBenchmarkScavenge}, +#endif /* VMInvestigations */ + {(void*)_m, "primitiveBitAndLargeIntegers\000\000\001", (void*)primitiveBitAndLargeIntegers}, + {(void*)_m, "primitiveBitOrLargeIntegers\000\000\001", (void*)primitiveBitOrLargeIntegers}, + {(void*)_m, "primitiveBitShiftLargeIntegers\000\000\001", (void*)primitiveBitShiftLargeIntegers}, + {(void*)_m, "primitiveBitXorLargeIntegers\000\000\001", (void*)primitiveBitXorLargeIntegers}, + {(void*)_m, "primitiveBytecodeSetsAvailable\000\377\000", (void*)primitiveBytecodeSetsAvailable}, + {(void*)_m, "primitiveClockLogAddresses\000\000\000", (void*)primitiveClockLogAddresses}, +#if VMInvestigations + {(void*)_m, "primitiveCogitMethod\000\000\000", (void*)primitiveCogitMethod}, +#endif /* VMInvestigations */ +#if VMInvestigations + {(void*)_m, "primitiveCogStaticSymbols\000\377\000", (void*)primitiveCogStaticSymbols}, +#endif /* VMInvestigations */ + {(void*)_m, "primitiveCompareBytes\000\001\001", (void*)primitiveCompareBytes}, + {(void*)_m, "primitiveCompareWith\000\001\000", (void*)primitiveCompareWith}, +#if VMInvestigations + {(void*)_m, "primitiveContextXray\000\001\000", (void*)primitiveContextXray}, +#endif /* VMInvestigations */ + {(void*)_m, "primitiveCrashVM\000\000\000", (void*)primitiveCrashVM}, + {(void*)_m, "primitiveDisablePowerManager\000\000\000", (void*)primitiveDisablePowerManager}, + {(void*)_m, "primitiveDivideLargeIntegers\000\000\001", (void*)primitiveDivideLargeIntegers}, + {(void*)_m, "primitiveDivLargeIntegers\000\000\001", (void*)primitiveDivLargeIntegers}, + {(void*)_m, "primitiveEqualLargeIntegers\000\000\001", (void*)primitiveEqualLargeIntegers}, + {(void*)_m, "primitiveEventProcessingControl\000\000\000", (void*)primitiveEventProcessingControl}, + {(void*)_m, "primitiveExternalCallNoOp\000\377\000", (void*)primitiveExternalCallNoOp}, + {(void*)_m, "primitiveFastCNoOp\000\377\001", (void*)primitiveFastCNoOp}, + {(void*)_m, "primitiveFastCNoOpAlignedForFloats\000\377\003", (void*)primitiveFastCNoOpAlignedForFloats}, + {(void*)_m, "primitiveGetenv\000\000\000", (void*)primitiveGetenv}, + {(void*)_m, "primitiveGetLogDirectory\000\377\000", (void*)primitiveGetLogDirectory}, + {(void*)_m, "primitiveGetOwnerLog\000\001\000", (void*)primitiveGetOwnerLog}, + {(void*)_m, "primitiveGreaterOrEqualLargeIntegers\000\000\001", (void*)primitiveGreaterOrEqualLargeIntegers}, + {(void*)_m, "primitiveGreaterThanLargeIntegers\000\000\001", (void*)primitiveGreaterThanLargeIntegers}, + {(void*)_m, "primitiveHeartbeatFrequency\000\000\000", (void*)primitiveHeartbeatFrequency}, + {(void*)_m, "primitiveHighResClock\000\377\001", (void*)primitiveHighResClock}, + {(void*)_m, "primitiveImageFormatVersion\000\377\000", (void*)primitiveImageFormatVersion}, + {(void*)_m, "primitiveInterruptChecksPerMSec\000\377\000", (void*)primitiveInterruptChecksPerMSec}, + {(void*)_m, "primitiveIsBigEnder\000\377\000", (void*)primitiveIsBigEnder}, + {(void*)_m, "primitiveLessOrEqualLargeIntegers\000\000\001", (void*)primitiveLessOrEqualLargeIntegers}, + {(void*)_m, "primitiveLessThanLargeIntegers\000\000\001", (void*)primitiveLessThanLargeIntegers}, +#if LRPCheck + {(void*)_m, "primitiveLongRunningPrimitive\000\377\000", (void*)primitiveLongRunningPrimitive}, +#endif /* LRPCheck */ +#if LRPCheck + {(void*)_m, "primitiveLongRunningPrimitiveSemaphore\000\000\000", (void*)primitiveLongRunningPrimitiveSemaphore}, +#endif /* LRPCheck */ +#if VMInvestigations + {(void*)_m, "primitiveMethodIsCogged\000\000\000", (void*)primitiveMethodIsCogged}, +#endif /* VMInvestigations */ + {(void*)_m, "primitiveMethodPCData\000\000\000", (void*)primitiveMethodPCData}, + {(void*)_m, "primitiveMillisecondClockMask\000\377\000", (void*)primitiveMillisecondClockMask}, + {(void*)_m, "primitiveMinimumUnusedHeadroom\000\377\000", (void*)primitiveMinimumUnusedHeadroom}, + {(void*)_m, "primitiveModLargeIntegers\000\000\001", (void*)primitiveModLargeIntegers}, + {(void*)_m, "primitiveMultipleBytecodeSetsActive\000\000\000", (void*)primitiveMultipleBytecodeSetsActive}, + {(void*)_m, "primitiveMultiplyLargeIntegers\000\000\001", (void*)primitiveMultiplyLargeIntegers}, + {(void*)_m, "primitiveNotEqualLargeIntegers\000\000\001", (void*)primitiveNotEqualLargeIntegers}, + {(void*)_m, "primitivePathToUsing\000\000\000", (void*)primitivePathToUsing}, + {(void*)_m, "primitiveProcessBoundThreadId\000\000\000", (void*)primitiveProcessBoundThreadId}, + {(void*)_m, "primitiveProfilePrimitive\000\377\001", (void*)primitiveProfilePrimitive}, + {(void*)_m, "primitiveProfileSample\000\377\001", (void*)primitiveProfileSample}, + {(void*)_m, "primitiveProfileSemaphore\000\000\014", (void*)primitiveProfileSemaphore}, + {(void*)_m, "primitiveProfileStart\000\000\001", (void*)primitiveProfileStart}, + {(void*)_m, "primitiveQuoLargeIntegers\000\000\001", (void*)primitiveQuoLargeIntegers}, + {(void*)_m, "primitiveRemLargeIntegers\000\000\001", (void*)primitiveRemLargeIntegers}, + {(void*)_m, "primitiveScreenDepth\000\377\000", (void*)primitiveScreenDepth}, + {(void*)_m, "primitiveScreenScaleFactor\000\377\000", (void*)primitiveScreenScaleFactor}, + {(void*)_m, "primitiveSetGCSemaphore\000\000\000", (void*)primitiveSetGCSemaphore}, + {(void*)_m, "primitiveSetLogDirectory\000\001\000", (void*)primitiveSetLogDirectory}, + {(void*)_m, "primitiveSubtractLargeIntegers\000\000\001", (void*)primitiveSubtractLargeIntegers}, + {(void*)_m, "primitiveSuspendBackingUpV2\000\002\000", (void*)primitiveSuspendBackingUpV2}, +#if TestingPrimitives + {(void*)_m, "primitiveTestShortenIndexableSize\000\001\000", (void*)primitiveTestShortenIndexableSize}, +#endif /* TestingPrimitives */ + {(void*)_m, "primitiveUtcWithOffset\000\001\000", (void*)primitiveUtcWithOffset}, + {(void*)_m, "primitiveVMCurrentThreadId\000\377\000", (void*)primitiveVMCurrentThreadId}, + {(void*)_m, "primitiveVoidReceiver\000\377\000", (void*)primitiveVoidReceiver}, + {NULL, NULL, NULL} +}; diff --git a/src/spur64.cog.livetyping/gcc3x-cointerp.c b/src/spur64.cog.livetyping/gcc3x-cointerp.c new file mode 100644 index 0000000000..87eb658264 --- /dev/null +++ b/src/spur64.cog.livetyping/gcc3x-cointerp.c @@ -0,0 +1,88358 @@ +/* This file has been post-processed for GNU C */ + + +/* Automatically generated by + CCodeGeneratorGlobalStructure * VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba + (Cog-eem.511, Compiler-ct.535) + from + LiveTypingCoInterpreter * VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba + */ +static char __buildInfo[] = "LiveTypingCoInterpreter * VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba " __DATE__ ; +char *__interpBuildInfo = __buildInfo; + + + +#if USE_GLOBAL_STRUCT +# define SQ_USE_GLOBAL_STRUCT 1 +#else +# define SQ_USE_GLOBAL_STRUCT 0 +#endif +#if USE_GLOBAL_STRUCT_REG +# define SQ_USE_GLOBAL_STRUCT_REG 1 +#else +# define SQ_USE_GLOBAL_STRUCT_REG 0 +#endif + +#include "sqGnu.h" +#include /* for printf */ +#include /* for e.g. alloca */ +#include +#include /* for wint_t */ +#include "vmCallback.h" +#include "sqMemoryFence.h" +#include "sqImageFileAccess.h" +#include "sqSetjmpShim.h" +#include "dispdbg.h" +#include "sqCogStackAlignment.h" +#include "cogmethod.h" +#include "cointerp.h" +#include "cogit.h" + + +/* StackInterpreter class>>preambleCCode */ +/* Disable Intel compiler inlining of warning which is used for breakpoints */ +#pragma auto_inline(off) +sqInt warnpid, erroronwarn; +EXPORT(void) +warning(const char *s) { /* Print an error message but don't necessarily exit. */ + if (erroronwarn) error(s); + if (warnpid) + printf("\n%s pid %ld\n", s, (long)warnpid); + else + printf("\n%s\n", s); +} +EXPORT(void) +warningat(const char *s, int l) { /* ditto with line number. */ + /* use alloca to call warning so one does not have to remember to set multiple breakpoints... */ + char *sl = alloca(strlen(s) + 16); + sprintf(sl, "%s %d", s, l); + warning(sl); +} +EXPORT(void) +warninginat(const char *s, const char *f, int l) { /* ditto with function name & line number. */ + /* use alloca to call warning so one does not have to remember to set multiple breakpoints... */ + char *sl = alloca(strlen(s) + strlen(f) + 17); + sprintf(sl, "%s %s %d", s, f, l); + warning(sl); +} +#pragma auto_inline(on) + +/* end StackInterpreter class>>preambleCCode */ + + +/*** Constants ***/ +#define ActiveProcessIndex 1 +#define AlternateHeaderHasPrimFlag 0x80000 +#define AlternateHeaderNumLiteralsMask 0x7FFF +#define AltLongStoreBytecode 245 +#define BecameActiveClassFlag 8 +#define BecameCompiledMethodFlag 2 +#define BecameJittedCompiledMethodFlag 16 +#define BecamePointerObjectFlag 1 +#define CacheProbeMax 3 +#define ClassAlien 52 +#define ClassArray 7 +#define ClassArrayCompactIndex 51 +#define ClassBitmap 4 +#define ClassBitmapCompactIndex 53 +#define ClassBlockClosure 36 +#define ClassBlockClosureCompactIndex 37 +#define ClassByteArray 26 +#define ClassByteString 6 +#define ClassByteStringCompactIndex 52 +#define ClassCharacter 19 +#define ClassDoubleByteArray 31 +#define ClassDoubleWordArray 33 +#define ClassExternalAddress 43 +#define ClassExternalData 45 +#define ClassExternalFunction 46 +#define ClassExternalLibrary 47 +#define ClassExternalStructure 44 +#define ClassFloat 9 +#define ClassFloatCompactIndex 34 +#define ClassFullBlockClosureCompactIndex 38 +#define ClassLargeNegativeInteger 42 +#define ClassLargeNegativeIntegerCompactIndex 32 +#define ClassLargePositiveInteger 13 +#define ClassLargePositiveIntegerCompactIndex 33 +#define ClassMessage 15 +#define ClassMessageCompactIndex 35 +#define ClassMethodContext 10 +#define ClassMethodContextCompactIndex 36 +#define ClassPoint 12 +#define ClassPointCompactIndex 54 +#define ClassSemaphore 18 +#define ClassSmallInteger 5 +#define ClassUnsafeAlien 54 +#define ClassWordArray 32 +#define ClosureFirstCopiedValueIndex 3 +#define ClosureIndex 4 +#define ClosureNumArgsIndex 2 +#define ClosureOuterContextIndex 0 +#define ClosureStartPCIndex 1 +#define CMBlock 4 +#define CMClosedPIC 2 +#define CMMethod 5 +#define CMMethodFlaggedForBecome 6 +#define CMOpenPIC 3 +#define ConstMinusOne 0xFFFFFFFFFFFFFFF9ULL +#define ConstOne 9 +#define ConstTwo 17 +#define ConstZero 1 +#define CrossedX 258 +#define CSEnterCriticalSection 2 +#define CSExitCriticalSection 4 +#define CSFromInterpreter 0 +#define CSFromMachineCode 1 +#define CSResume 6 +#define CSSetPriority 16 +#define CSSuspend 10 +#define CSSynchronousSignal 8 +#define CSWait 12 +#define CSYield 14 +#define CtxtTempFrameStart 6 +#define DisownVMForFFICallFlag 8 +#define DisownVMFromCallbackFlag 16 +#define EncounteredUnknownBytecode -6 +#define EndOfRun 0x101 +#define ExcessSignalsIndex 2 +#define ExternalCallLiteralFlagsIndex 2 +#define ExternalCallLiteralFunctionNameIndex 1 +#define ExternalCallLiteralModuleNameIndex 0 +#define ExternalCallLiteralTargetFunctionIndex 3 +#define ExternalObjectsArray 38 +#define ExtraRootsSize 64 +#define FalseObject 1 +#define FirstLinkIndex 0 +#define FoxCallerSavedIP 8 +#define FoxIFReceiver -40 +#define FoxIFrameFlags -24 +#define FoxIFSavedIP -32 +#define FoxMethod -8 +#define FoxMFReceiver -24 +#define FoxSavedFP 0 +#define FoxThisContext -16 +#define FullClosureCompiledBlockIndex 1 +#define FullClosureFirstCopiedValueIndex 4 +#define FullClosureReceiverIndex 3 +#define GCCheckFreeSpace 32 +#define GCCheckPrimCall 128 +#define GCCheckShorten 64 +#define GCModeBecome 8 +#define GCModeFull 1 +#define GCModeImageSegment 16 +#define GCModeNewSpace 2 +#define HasBeenReturnedFromMCPC -1 +#define HasBeenReturnedFromMCPCOop 0xFFFFFFFFFFFFFFF9ULL +#define HashMultiplyConstant 1664525 +#define HeaderIndex 0 +#define IFrameSlots 7 +#define InstanceSpecificationIndex 2 +#define InstructionPointerIndex 1 +#define InstVarTypesArraysIndex 5 +#define KeyIndex 0 +#define LargeContextBit 0x100000 +#define LargeContextSlots 62 +#define LastLinkIndex 1 +#define LiteralStart 1 +#define LongStoreBytecode 129 +#define MarkOnTenure 5 +#define MarkStackRootIndex 0x1000 +#define MaxExternalPrimitiveTableSize 4096 +#define MaxLiteralCountForCompile 60 +#define MaxNegativeErrorCode -8 +#define MaxPrimitiveIndex 583 +#define MaxQuickPrimitiveIndex 519 +#define MaxRTRefCount 7 +#define MessageArgumentsIndex 1 +#define MessageLookupClassIndex 2 +#define MessageSelectorIndex 0 +#define MethodArrayIndex 1 +#define MethodCacheClass 2 +#define MethodCacheEntries 0x400 +#define MethodCacheEntrySize 4 +#define MethodCacheMask 0xFFC +#define MethodCacheMethod 3 +#define MethodCachePrimFunction 4 +#define MethodCacheSelector 1 +#define MethodCacheSize 4096 +#define MethodDictionaryIndex 1 +#define MethodHeaderArgCountShift 27 +#define MethodHeaderFlagBitPosition 31 +#define MethodHeaderTempCountShift 21 +#define MethodIndex 3 +#define MethodTooBig -4 +#define MFMethodFlagHasContextFlag 1 +#define MFMethodFlagIsBlockFlag 2 +#define MFMethodFlagsMask 0x7 +#define MFMethodMask -0x8 +#define MFrameSlots 5 +#define MillisecondClockMask 0x1FFFFFFF +#define MinBackwardJumpCountForCompile 40 +#define MournQueueRootIndex 4098 +#define MultipleBytecodeSetsBitmask 0x200 +#define MyListIndex 3 +#define NextLinkIndex 0 +#define NilObject 0 +#define NotFullyInitialized -1 +#define ObjStackFixedSlots 4 +#define ObjStackFreex 2 +#define ObjStackLimit 0xFF8 +#define ObjStackMyx 1 +#define ObjStackNextx 3 +#define ObjStackPageSlots 0xFFC +#define ObjStackTopx 0 +#define OldBecameNewFlag 4 +#define PrimCallCollectsProfileSamples 16 +#define PrimCallIsExternalCall 32 +#define PrimCallNeedsNewMethod 4 +#define PrimCallOnSmalltalkStack 1 +#define PrimCallOnSmalltalkStackAlign2x 2 +#define PrimitiveErrorTableIndex 51 +#define PrimNumberDoExternalCall 218 +#define PrimNumberDoPrimitive 118 +#define PrimNumberExternalCall 117 +#define PrimNumberFFICall 120 +#define PrimNumberHandlerMarker 199 +#define PrimNumberNoContextSwitchMarker 123 +#define PrimNumberUnwindMarker 198 +#define PrimNumberVMParameter 254 +#define PrimTraceLogSize 256 +#define PriorityIndex 2 +#define ProcessListsIndex 0 +#define ProcessSignalingLowSpace 22 +#define ReceiverIndex 5 +#define RemapBufferSize 25 +#define RememberedSetRootIndex 4099 +#define RemoteTempVarTypesArraysIndex 4 +#define ReturnToInterpreter 1 +#define ReturnTypesArrayIndex 3 +#define ScavengeInProgress 1 +#define SchedulerAssociation 3 +#define SelectorAboutToReturn 48 +#define SelectorAttemptToAssign 50 +#define SelectorCannotInterpret 34 +#define SelectorCannotReturn 21 +#define SelectorDoesNotUnderstand 20 +#define SelectorInvokeCallback 53 +#define SelectorMustBeBoolean 25 +#define SelectorRunWithIn 49 +#define SelectorStart 2 +#define SelectorUnknownBytecode 57 +#define SenderIndex 0 +#define ShouldNotJIT -8 +#define SlidingCompactionInProgress 2 +#define SmallContextSlots 22 +#define SpecialSelectors 23 +#define SpurPrimitiveAccessorDepthShift 8 +#define SpurPrimitiveFlagsMask 0xFF +#define StackPageReachedButUntraced 1 +#define StackPageTraced 2 +#define StackPageTraceInvalid -1 +#define StackPageUnreached 0 +#define StackPointerIndex 2 +#define SuperclassIndex 0 +#define SuspendedContextIndex 1 +#define TempVarTypesArraysIndex 2 +#define TenureByAge 1 +#define TenureByClass 2 +#define TenureToShrinkRT 3 +#define TheDisplay 14 +#define TheFinalizationSemaphore 41 +#define TheInterruptSemaphore 30 +#define TheLowSpaceSemaphore 17 +#define TheTimerSemaphore 29 +#define TopHashBit 0x200000 +#define TopOopBit 0x8000000000000000ULL +#define TraceBlockActivation 2 +#define TraceBlockCreation 3 +#define TraceBufferSize 768 +#define TraceCodeCompaction 6 +#define TraceContextSwitch 1 +#define TraceFullGC 5 +#define TraceIncrementalGC 4 +#if !defined(TraceLog) /* Allow this to be overridden on the compiler command line */ +# define TraceLog 0 +#endif +#define TracePrimitiveFailure 10 +#define TracePrimitiveRetry 11 +#define TraceSend 0 +#define TraceStackOverflow 9 +#define TraceTypeShift 8 +#define TraceVMCallback 7 +#define TraceVMCallbackReturn 8 +#define TrueObject 2 +#define ValidatedClassDoubleByteArrayFlag 1 +#define ValidatedClassDoubleWordArrayFlag 4 +#define ValidatedClassWordArrayFlag 2 +#define ValueIndex 1 +#define WeaklingStackRootIndex 0x1001 +#define XIndex 0 +#define YIndex 1 + +typedef struct { + usqInt sEdenBytes; + usqInt sPastBytes; + usqInt sRememberedSetSize; + usqInt sRememberedSetRedZone; + usqInt sRememberedSetLimit; + usqInt sStatTenures; + usqInt tTenureCriterion; + usqInt tTenureThreshold; + usqInt tRefCountToShrinkRT; + usqInt eSurvivorBytes; + usqInt eRememberedSetSize; + usqInt eStatTenures; + } SpurScavengeLogRecord; + + +typedef struct { + usqInt segStart; + usqInt segSize; + sqInt swizzle; + usqInt containsPinned; + usqInt savedSegSize; + usqInt lastFreeObject; + } SpurSegmentInfo; + + +typedef struct { + usqInt start; + usqInt limit; + } SpurNewSpaceSpace; + + +typedef struct { + usqInt start; + usqInt limit; + usqInt top; + } SpurContiguousObjStack; + + +typedef struct _StackPage { + char *stackLimit; + char *headSP; + char *headFP; + char *baseFP; + char *baseAddress; + char *realStackLimit; + char *lastAddress; + int trace; + int padToWord; + struct _StackPage *nextPage; + struct _StackPage *prevPage; + } StackPage; + +#define CogStackPage StackPage + + + +/*** Function Prototypes ***/ + + +#if !PRODUCTION && defined(PlatformNoDbgRegParms) +# define NoDbgRegParms PlatformNoDbgRegParms +#endif + +#if !defined(NoDbgRegParms) +# define NoDbgRegParms /*empty*/ +#endif + + + +#if !defined(NeverInline) +# define NeverInline /*empty*/ +#endif + +extern sqInt interpret(void); +static NoDbgRegParms CogMethod * cmHomeMethod(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMBlock(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMClosedPIC(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMMethodEtAl(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMMethodFlaggedForBecome(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMOpenPIC(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms sqInt addressIsInPage(StackPage *self_in_CogStackPage, char *address); +static NoDbgRegParms int isFree(StackPage *self_in_CogStackPage); +static NoDbgRegParms sqInt freeStackPageNoAssert(StackPage *aPage); +static NoDbgRegParms void freeStackPage(StackPage *aPage); +static NoDbgRegParms sqInt markStackPageMostRecentlyUsed(StackPage *page); +static NoDbgRegParms sqInt markStackPageNextMostRecentlyUsed(StackPage *page); +static StackPage * newStackPage(void); +static sqInt pageListIsWellFormed(void); +static NoDbgRegParms StackPage * stackPageAt(sqInt index); +static NoDbgRegParms StackPage * stackPageFor(void *pointer); +static double statAverageLivePagesWhenMapping(void); +extern int vmIsInitialized(void); +extern sqInt accessorDepthForPrimitiveIndex(sqInt primIndex); +static NoDbgRegParms void activateNewCogMethodinInterpreter(CogMethod *cogMethod, sqInt inInterpreter); +static NoDbgRegParms void addNewMethodToCache(sqInt classObj); +extern usqInt argumentCountAddress(void); +static NoDbgRegParms CogMethod * asCogHomeMethod(CogBlockMethod *aCogMethod); +static NoDbgRegParms void assertValidExecutionPointersimbarline(usqInt lip, char *lifp, char *lisp, sqInt inInterpreter, sqInt ln); +static NoDbgRegParms sqInt assertValidStackedInstructionPointersInline(StackPage *aStackPage, sqInt ln); +static NoDbgRegParms void assertValidStackedInstructionPointers(sqInt ln); +static NoDbgRegParms void attemptToSwitchToMachineCode(sqInt bcpc); +static NoDbgRegParms void backupContexttoBlockingSendTo(sqInt suspendedContext, sqInt conditionVariable); +extern void callForCogCompiledCodeCompaction(void); +static NoDbgRegParms void callRegisterArgCogMethodatreceiver(CogMethod *cogMethod, sqInt entryOffset, sqInt rcvr); +extern void ceActivateFailingPrimitiveMethod(sqInt aPrimitiveMethod); +extern sqInt ceBaseFrameReturn(sqInt returnValue); +#if IMMUTABILITY +extern sqInt ceCannotAssignTowithIndexvalueToAssign(sqInt immutableObject, sqInt index, sqInt valueToAssign); +#endif /* IMMUTABILITY */ +extern sqInt ceCannotResume(void); +extern void ceCheckAndMaybeRetryPrimitive(sqInt primIndex); +extern void ceCheckForInterrupt(void); +extern sqInt ceContextinstVar(sqInt maybeContext, sqInt slotIndex); +extern sqInt ceContextinstVarvalue(sqInt maybeMarriedContext, sqInt slotIndex, sqInt anOop); +extern sqInt ceInterpretMethodFromPICreceiver(sqInt aMethodObj, sqInt rcvr); +extern sqInt ceMNUFromPICMNUMethodreceiver(sqInt aMethodObj, sqInt rcvr); +extern sqInt ceNewHashOf(sqInt anObject); +extern sqInt ceNonLocalReturn(sqInt returnValue); +extern void ceReapAndResetErrorCodeFor(CogMethod *cogMethod); +static NoDbgRegParms sqInt ceSendAborttonumArgs(sqInt selector, sqInt rcvr, sqInt numArgs); +extern sqInt ceSendFromInLineCacheMiss(CogMethod *cogMethodOrPIC); +extern sqInt ceSendMustBeBooleanTointerpretingAtDelta(sqInt aNonBooleanObject, sqInt jumpSize); +extern sqInt ceSendMustBeBoolean(sqInt anObject); +extern void ceSendaboveClassBindingtonumArgs(sqInt selector, sqInt methodClassBinding, sqInt rcvr, sqInt numArgs); +extern sqInt ceSendabovetonumArgs(sqInt selector, sqInt methodClass, sqInt rcvr, sqInt numArgs); +extern sqInt ceSendsupertonumArgs(sqInt selector, sqInt superNormalBar, sqInt rcvr, sqInt numArgs); +extern void ceStackOverflow(sqInt contextSwitchIfNotNil); +extern void ceTakeProfileSample(void); +extern void ceTraceBlockActivation(void); +extern void ceTraceLinkedSend(sqInt theReceiver); +extern void ceTraceStoreOfinto(sqInt aValue, sqInt anObject); +extern usqInt cFramePointerAddress(void); +extern void checkAssertsEnabledInCoInterpreter(void); +static NoDbgRegParms sqInt checkCodeIntegrity(sqInt gcModes); +extern sqInt checkForAndFollowForwardedPrimitiveState(void); +extern sqInt checkIfCFramePointerInUse(void); +#if LRPCheck +extern int checkingLongRunningPrimitives(void); +#endif /* LRPCheck */ +static NoDbgRegParms sqInt checkOkayFields(sqInt oop); +static NeverInline void checkProfileTickPostPrimitive(void); +static NoDbgRegParms sqInt cogMethodHasRealNonProfilingPrimitive(CogMethod *cogMethod); +extern sqInt cogMethodHasTooManyLiterals(CogMethod *aCogMethod); +extern CogMethod * cogMethodOf(sqInt aMethodOop); +static void commenceCogCompiledCodeCompaction(void); +extern void compilationBreakpointFor(sqInt selectorOop); +static NoDbgRegParms sqInt contextInstructionPointerframe(sqInt theIP, char *theFP); +static NoDbgRegParms void convertFrametoInterpreterFrame(char *theFP, sqInt pcDelta); +extern usqInt cReturnAddressAddress(void); +extern usqInt cStackPointerAddress(void); +static NoDbgRegParms sqInt deferStackLimitSmashAroundwith(void (*functionSymbol)(sqInt), sqInt arg); +static NoDbgRegParms sqInt divorceAllFramesSuchThat(sqInt (*criterion)(char *fp)); +extern void dumpStackAlignmentInfo(void); +static NoDbgRegParms void ensureAllContextsHaveBytecodePCsIf(sqInt (*criterion)(sqInt methodOop)); +static NoDbgRegParms void ensureContextHasBytecodePC(sqInt aContext); +static NoDbgRegParms void ensureContextIsExecutionSafeAfterAssignToStackPointer(sqInt aContext); +static sqInt enterSmalltalkExecutiveImplementation(void); +extern void executeCogMethodfromLinkedSendWithReceiver(CogMethod *cogMethod, sqInt rcvr); +static NoDbgRegParms void executeCogMethodfromUnlinkedSendWithReceiver(CogMethod *cogMethod, sqInt rcvr); +extern void executeCogPICfromLinkedSendWithReceiverandCacheTag(CogMethod *cogPIC, sqInt rcvr, sqInt cacheTag); +static sqInt executeNewMethod(void); +static sqInt executeNewMethodJitting(void); +static NoDbgRegParms sqInt externalInstVarofContext(sqInt offset, sqInt aContext); +static NoDbgRegParms void findNewMethodInClassTag(sqInt classTagArg); +static NoDbgRegParms void flagMethodAsInterpreted(sqInt aMethod); +static void flushExternalPrimitives(void); +static NoDbgRegParms void flushMethodsWithMachineCodePrimitivesAndContinueAnswering(sqInt result); +static void followForwardedFieldsInCurrentMethod(void); +static void followForwardingPointersOfReceiverAndTemporariesInStackZone(void); +static void followForwardingPointersOfReceiversInStackZone(void); +extern void forceInterruptCheckFromHeartbeat(void); +static NoDbgRegParms sqInt frameCallerContext(char *theFP); +static NoDbgRegParms int frameHasContext(char *theFP); +static NoDbgRegParms int frameIsBlockActivation(char *theFP); +static NoDbgRegParms sqInt frameMethodObject(char *theFP); +static NoDbgRegParms usqInt frameNumArgs(char *theFP); +extern usqInt framePointerAddress(void); +static NoDbgRegParms void framePrintDescription(sqInt it); +static NoDbgRegParms sqInt frameReceiver(char *theFP); +extern void (*functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(sqInt methodObj, sqInt primitiveIndex, sqInt *flagsPtr))(void); +extern sqInt getCurrentBytecode(void); +static NoDbgRegParms sqInt handleForwardedSendFaultForReceiverstackDelta(sqInt forwardedReceiver, sqInt stackDelta); +static NoDbgRegParms sqInt handleMNUInMachineCodeToclassForMessage(sqInt selectorIndex, sqInt rcvr, sqInt classForMessage); +static NoDbgRegParms int iframeIsBlockActivation(char *theFP); +static NoDbgRegParms sqInt iframeReceiver(char *theFP); +static NoDbgRegParms usqInt iframeSavedIP(char *theFP); +extern void ifValidWriteBackStackPointersSaveTo(void *theCFP, void *theCSP, char **savedFPP, char **savedSPP); +extern usqInt instructionPointerAddress(void); +static NoDbgRegParms sqInt instructionPointerForFramecurrentFPcurrentIP(char *spouseFP, char *currentFP, sqInt instrPtr); +static NeverInline sqInt interpreterAllocationReserveBytes(void); +static sqInt interpretMethodFromMachineCode(void); +static NoDbgRegParms int isCogMethodReferenceNoAssert(sqInt methodHeader); +extern int isCogMethodReference(sqInt methodHeader); +static NoDbgRegParms sqInt isExternalMethodInPlugin(sqInt methodObj); +extern sqInt isFullBlockMethod(sqInt aMethodObj); +static NoDbgRegParms sqInt isMachineCodeFrameForCogMethodFlaggedForBecome(char *theFP); +static NoDbgRegParms sqInt isMachineCodeFrameForCogMethodWithMachineCodePrim(char *theFP); +static NoDbgRegParms sqInt isMachineCodeFrameForCogMethodWithTooManyLiterals(char *theFP); +static NoDbgRegParms sqInt isMachineCodeFrameForExternalPrimitiveMethod(char *theFP); +static NoDbgRegParms sqInt isMachineCodeFrameForRealNonProfilingPrimitive(char *theFP); +static NoDbgRegParms int isMachineCodeFrame(char *theFP); +static NoDbgRegParms sqInt isRealNonProfilingPrimitiveMethod(sqInt methodObj); +static NoDbgRegParms sqInt isRealNonProfilingPrimitiveMethodprimIndex(sqInt methodObj, sqInt primitiveIndex); +static NoDbgRegParms sqInt isWidowedContextDuringGC(sqInt aOnceMarriedContext); +static NoDbgRegParms sqInt isWidowedContext(sqInt aOnceMarriedContext); +static NoDbgRegParms sqInt ismethodAssignmentToContextWithMachineCodePC(sqInt fieldIndex, sqInt anOop); +#if LRPCheck +extern usqInt longRunningPrimitiveStopUsecsAddress(void); +#endif /* LRPCheck */ +extern sqInt lookupMNUreceiver(sqInt selector, sqInt rcvr); +extern sqInt lookupOrdinaryreceiver(sqInt selector, sqInt rcvr); +static NoDbgRegParms StackPage * makeBaseFrameFor(sqInt aContext); +static NeverInline void mapStackPages(void); +extern void markActiveMethodsAndReferents(void); +static NoDbgRegParms void markAndTraceStackPage(StackPage *thePage); +static NoDbgRegParms void markCogMethodsAndReferentsOnPage(StackPage *thePage); +static NoDbgRegParms usqInt marryFrameSP(char *theFP, char *theSP); +extern sqInt maxLookupNoMNUErrorCode(void); +extern usqInt methodCacheAddress(void); +extern int methodHasCogMethod(sqInt aMethodOop); +static NoDbgRegParms sqInt methodHasMachineCodePrimitiveMethod(sqInt methodObj); +static NoDbgRegParms NeverInline sqInt methodHasPrimitiveInPrimTracePlugin(sqInt aMethodObj); +static NoDbgRegParms sqInt methodHasTooManyLiterals(sqInt methodObj); +#if VMInvestigations +extern int methodHeaderHasPrimitive(sqInt methodHeader); +#endif /* VMInvestigations */ +extern int methodNeedsLargeContext(sqInt methodObj); +extern sqInt methodShouldBeCogged(sqInt aMethodObj); +static NoDbgRegParms int methodWithHeaderShouldBeCogged(sqInt methodHeader); +static NoDbgRegParms CogBlockMethod * mframeCogMethod(char *theFP); +extern CogMethod * mframeHomeMethodExport(void); +static NoDbgRegParms CogMethod * mframeHomeMethod(char *theFP); +static NoDbgRegParms int mframeIsBlockActivation(char *theFP); +static NoDbgRegParms sqInt mframeReceiver(char *theFP); +static NeverInline sqInt minimumUnusedHeadroom(void); +static NoDbgRegParms sqInt mnuMethodOrNilFor(sqInt rcvr); +static NoDbgRegParms char * moveFramesInthroughtoPage(StackPage *oldPage, char *theFP, StackPage *newPage); +static NoDbgRegParms sqInt mustMapMachineCodePCcontext(sqInt theIP, sqInt aOnceMarriedContext); +extern usqInt newMethodAddress(void); +extern usqInt nextProfileTickAddress(void); +extern sqInt noAssertHeaderOf(sqInt methodPointer); +extern sqInt positive32BitIntegerFor(unsigned int integerValue); +extern unsigned int positive32BitValueOf(sqInt oop); +extern sqInt positive64BitIntegerFor(usqLong integerValue); +extern usqLong positive64BitValueOf(sqInt oop); +static NoDbgRegParms void postGCAction(sqInt gcModeArg); +static NoDbgRegParms void preGCAction(sqInt gcModeArg); +extern usqInt primFailCodeAddress(void); +static NoDbgRegParms void primitiveBecomeReturn(sqInt ec); +static void primitiveExecuteMethod(void); +static void primitiveExecuteMethodArgsArray(void); +extern sqInt primitivePropertyFlagsprimIndex(sqInt methodObj, sqInt primIndex); +extern void * primTraceLogAddress(void); +extern usqInt primTraceLogIndexAddress(void); +extern void printCogMethod(CogMethod *cogMethod); +static NoDbgRegParms void printFrameFlagsForFP(char *theFP); +static NoDbgRegParms void printFrameMethodFor(char *theFP); +static NoDbgRegParms void printFrameThingatextra(char *name, char *address, sqInt extraValue); +extern sqInt printFrameWithSP(char *theFP, char *theSP); +extern void printMethodCacheFor(sqInt thing); +extern sqInt quickPrimitiveConstantFor(sqInt aQuickPrimitiveIndex); +extern sqInt (*quickPrimitiveGeneratorFor(sqInt aQuickPrimitiveIndex))(void); +extern sqInt quickPrimitiveInstVarIndexFor(sqInt primIndex); +extern sqInt rawHeaderOf(sqInt methodPointer); +extern void rawHeaderOfput(sqInt methodOop, sqInt cogMethodOrMethodHeader); +extern size_t readImageFromFileHeapSizeStartingAt(sqImageFile f, usqInt desiredHeapSize, squeakFileOffsetType imageOffset); +extern sqInt recordFastCCallPrimTraceForMethod(sqInt aMethodObj); +extern sqInt recordPrimTraceForMethod(sqInt aMethodObj); +static NeverInline void reloadPrimitiveCalloutPointer(void); +extern void reportMinimumUnusedHeadroom(void); +extern void reportMinimumUnusedHeadroomOn(FILE *aStdioStream); +static NoDbgRegParms sqInt returnToExecutivepostContextSwitch(sqInt inInterpreter, sqInt switchedContext); +static NoDbgRegParms sqInt returntoExecutive(sqInt returnValue, sqInt inInterpreter); +extern sqInt sendInvokeCallbackContext(VMCallbackContext *vmCallbackContext); +static NoDbgRegParms sqInt shortPrintFrame(char *theFP); +extern sqInt signed32BitIntegerFor(sqInt integerValue); +extern int signed32BitValueOf(sqInt oop); +extern sqInt signed64BitIntegerFor(sqLong integerValue); +extern sqLong signed64BitValueOf(sqInt oop); +extern sqInt specialSelectorNumArgs(sqInt index); +extern usqInt stackLimitAddress(void); +static sqInt stackLimitOffset(void); +static sqInt stackPageHeadroom(void); +extern usqInt stackPointerAddress(void); +extern usqInt startPCOfMethodHeader(sqInt aCompiledMethodHeader); +extern sqInt startPCOrNilOfLiteralin(sqInt lit, sqInt aMethodObj); +static NoDbgRegParms void tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFromtoreturnValue(sqInt contextToReturnFrom, sqInt contextToReturnTo, sqInt returnValue); +static NoDbgRegParms sqInt temporaryin(sqInt offset, char *theFP); +static NoDbgRegParms sqInt temporaryinput(sqInt offset, char *theFP, sqInt valueOop); +static NoDbgRegParms sqInt transferTofrom(sqInt newProcOrNil, sqInt sourceCode); +extern void updateStackZoneReferencesToCompiledCodePreCompaction(void); +static NoDbgRegParms void updateStateOfSpouseContextForFrameWithSP(char *theFP, char *theSP); +extern sqInt validInstructionPointerinMethodframePointer(usqInt instrPointer, usqInt aMethod, char *fp); +static NoDbgRegParms sqInt validStackPageBaseFrame(StackPage *aPage); +static NoDbgRegParms sqInt voidVMStateForSnapshotFlushingExternalPrimitivesIf(sqInt flushExtPrims); +extern char * whereIs(sqInt anOop); +static NoDbgRegParms NeverInline void widowOrForceToBytecodePC(sqInt ctxt); +static NoDbgRegParms int frameIsMarked(sqInt theFPInt); +#if VMInvestigations +EXPORT(int) primitiveAllMethodsCompiledToMachineCode(void); +#endif /* VMInvestigations */ +#if VMInvestigations +EXPORT(void) primitiveBenchmarkFollowForwardersInStackZone(void); +#endif /* VMInvestigations */ +#if VMInvestigations +EXPORT(void) primitiveBenchmarkFollowForwardersOfReceiverAndTemporariesInStackZone(void); +#endif /* VMInvestigations */ +#if VMInvestigations +EXPORT(sqInt) primitiveBenchmarkJITReceiver(void); +#endif /* VMInvestigations */ +#if VMInvestigations +EXPORT(void) primitiveBenchmarkScavenge(void); +#endif /* VMInvestigations */ +static void primitiveClosureCopyWithCopiedValues(void); +#if VMInvestigations +EXPORT(sqInt) primitiveCogitMethod(void); +#endif /* VMInvestigations */ +#if VMInvestigations +EXPORT(sqInt) primitiveCogStaticSymbols(void); +#endif /* VMInvestigations */ +static void primitiveCollectCogCodeConstituents(void); +#if VMInvestigations +EXPORT(void) primitiveContextXray(void); +#endif /* VMInvestigations */ +static void primitiveEnterCriticalSection(void); +static void primitiveExitCriticalSection(void); +EXPORT(void) primitiveExternalCallNoOp(void); +EXPORT(void) primitiveFastCNoOp(void); +EXPORT(void) primitiveFastCNoOpAlignedForFloats(void); +static void primitiveFlushCacheByMethod(void); +static void primitiveFlushCacheBySelector(void); +extern usqInt primitiveFunctionPointerAddress(void); +#if LRPCheck +EXPORT(sqInt) primitiveLongRunningPrimitiveSemaphore(void); +#endif /* LRPCheck */ +#if VMInvestigations +EXPORT(sqInt) primitiveMethodIsCogged(void); +#endif /* VMInvestigations */ +EXPORT(sqInt) primitiveMethodPCData(void); +static void primitiveMethodXray(void); +EXPORT(void) primitiveMinimumUnusedHeadroom(void); +static void primitiveObjectAt(void); +static void primitiveObjectAtPut(void); +EXPORT(sqInt) primitiveProfileSemaphore(void); +static void primitiveResume(void); +static void primitiveSetPriority(void); +static void primitiveSignal(void); +static void primitiveSnapshot(void); +static void primitiveSnapshotEmbedded(void); +static void primitiveSuspend(void); +static void primitiveSuspendBackingUpV1(void); +EXPORT(void) primitiveSuspendBackingUpV2(void); +static void primitiveTerminateTo(void); +static void primitiveUnloadModule(void); +static void primitiveVoidVMState(void); +static void primitiveVoidVMStateForMethod(void); +static void primitiveWait(void); +static void primitiveYield(void); +static void unmarkAllFrames(void); +static NoDbgRegParms void initializeStacknumSlotspageSize(char *theStackPages, sqInt stackSlots, sqInt slotsPerPage); +static NoDbgRegParms sqInt pageIndexFor(void *pointer); +static NoDbgRegParms char * whereIsMaybeStackThing(sqInt anOop); +extern char * cStringOrNullFor(sqInt oop); +extern sqInt failed(void); +extern sqInt identityHashOf(sqInt anOop); +static NoDbgRegParms sqInt isInstanceOfClassArray(sqInt oop); +static NoDbgRegParms sqInt isNegativeIntegerValueOf(sqInt oop); +extern sqInt isPositiveMachineIntegerObject(sqInt oop); +static NoDbgRegParms sqInt magnitude64BitIntegerForneg(usqLong magnitude, sqInt isNegative); +static NoDbgRegParms usqLong magnitude64BitValueOf(sqInt oop); +static NoDbgRegParms usqIntptr_t positiveMachineIntegerValueOfObj(sqInt oop); +extern usqIntptr_t positiveMachineIntegerValueOf(sqInt oop); +static void primitiveAdd(void); +EXPORT(void) primitiveAddLargeIntegers(void); +static void primitiveAdoptInstance(void); +EXPORT(void) primitiveAllInstances(void); +EXPORT(void) primitiveAllObjects(void); +static void primitiveArctan(void); +static void primitiveArrayBecome(void); +static void primitiveArrayBecomeOneWay(void); +static void primitiveArrayBecomeOneWayCopyHashArg(void); +static void primitiveArrayBecomeOneWayNoCopyHash(void); +static void primitiveAsCharacter(void); +static void primitiveAsFloat(void); +static void primitiveAt(void); +static void primitiveAtPut(void); +static void primitiveBeCursor(void); +static void primitiveBeDisplay(void); +static void primitiveBeep(void); +static void primitiveBehaviorHash(void); +static void primitiveBitAnd(void); +EXPORT(void) primitiveBitAndLargeIntegers(void); +static void primitiveBitOr(void); +EXPORT(void) primitiveBitOrLargeIntegers(void); +static void primitiveBitShift(void); +EXPORT(void) primitiveBitShiftLargeIntegers(void); +static void primitiveBitXor(void); +EXPORT(void) primitiveBitXorLargeIntegers(void); +EXPORT(sqInt) primitiveBytecodeSetsAvailable(void); +static void primitiveBytesLeft(void); +static void primitiveCalloutToFFI(void); +static void primitiveChangeClass(void); +static void primitiveClass(void); +static void primitiveClearVMProfile(void); +static void primitiveClipboardText(void); +EXPORT(sqInt) primitiveClockLogAddresses(void); +static void primitiveClosureValue(void); +extern void primitiveClosureValueNoContextSwitch(void); +static void primitiveClosureValueWithArgs(void); +static void primitiveCoarseLocalMicrosecondClock(void); +static void primitiveCoarseUTCMicrosecondClock(void); +EXPORT(sqInt) primitiveCompareBytes(void); +EXPORT(void) primitiveCompareWith(void); +static void primitiveConstantFill(void); +static void primitiveControlVMProfiling(void); +static void primitiveCopyObject(void); +static void primitiveCosine(void); +EXPORT(sqInt) primitiveCrashVM(void); +EXPORT(sqInt) primitiveDisablePowerManager(void); +static void primitiveDiv(void); +static void primitiveDivide(void); +EXPORT(void) primitiveDivideLargeIntegers(void); +EXPORT(void) primitiveDivLargeIntegers(void); +static void primitiveEqual(void); +EXPORT(void) primitiveEqualLargeIntegers(void); +static void primitiveExitToDebugger(void); +static void primitiveExp(void); +static void primitiveExponent(void); +extern sqInt primitiveFail(void); +extern sqInt primitiveFailForOSError(sqLong osErrorCode); +extern sqInt primitiveFailFor(sqInt reasonCode); +extern sqInt primitiveFailForwithSecondary(sqInt reasonCode, sqLong extraErrorCode); +extern sqInt primitiveFailureCode(void); +static void primitiveFetchNextMourner(void); +static void primitiveFloatAdd(void); +static void primitiveFloatArrayAt(void); +static void primitiveFloatArrayAtPut(void); +static void primitiveFloatAt(void); +static void primitiveFloatAtPut(void); +static void primitiveFloatDivide(void); +static void primitiveFloatEqual(void); +static void primitiveFloatGreaterOrEqual(void); +static void primitiveFloatGreaterThan(void); +static void primitiveFloatLessOrEqual(void); +static void primitiveFloatLessThan(void); +static void primitiveFloatMultiply(void); +static void primitiveFloatNotEqual(void); +static void primitiveFloatSubtract(void); +static void primitiveFlushCache(void); +static void primitiveFlushExternalPrimitives(void); +static void primitiveForceDisplayUpdate(void); +static void primitiveFormPrint(void); +static void primitiveFractionalPart(void); +static void primitiveFullClosureValue(void); +extern void primitiveFullClosureValueNoContextSwitch(void); +static void primitiveFullClosureValueWithArgs(void); +static void primitiveGetAttribute(void); +EXPORT(sqInt) primitiveGetenv(void); +#if IMMUTABILITY +static void primitiveGetImmutability(void); +#else +# define primitiveGetImmutability (void (*)(void))0 +#endif /* IMMUTABILITY */ +EXPORT(sqInt) primitiveGetLogDirectory(void); +static void primitiveGetNextEvent(void); +static void primitiveGreaterOrEqual(void); +EXPORT(void) primitiveGreaterOrEqualLargeIntegers(void); +static void primitiveGreaterThan(void); +EXPORT(void) primitiveGreaterThanLargeIntegers(void); +static void primitiveGrowMemoryByAtLeast(void); +static void primitiveHashMultiply(void); +EXPORT(sqInt) primitiveHeartbeatFrequency(void); +static void primitiveHighBit(void); +EXPORT(sqInt) primitiveHighResClock(void); +static void primitiveIdentical(void); +static void primitiveIdentityHash(void); +EXPORT(sqInt) primitiveImageFormatVersion(void); +static void primitiveImageName(void); +static void primitiveImmediateAsInteger(void); +static void primitiveInputSemaphore(void); +static void primitiveInputWord(void); +static void primitiveIntegerAt(void); +static void primitiveIntegerAtPut(void); +EXPORT(sqInt) primitiveInterruptChecksPerMSec(void); +static void primitiveInterruptSemaphore(void); +EXPORT(sqInt) primitiveIsBigEnder(void); +static void primitiveIsPinned(void); +static void primitiveLessOrEqual(void); +EXPORT(void) primitiveLessOrEqualLargeIntegers(void); +static void primitiveLessThan(void); +EXPORT(void) primitiveLessThanLargeIntegers(void); +static void primitiveListBuiltinModule(void); +static void primitiveListExternalModule(void); +static void primitiveLoadImageSegment(void); +static void primitiveLocalMicrosecondClock(void); +static void primitiveLogN(void); +static void primitiveLowSpaceSemaphore(void); +static void primitiveMakePoint(void); +static void primitiveMaxIdentityHash(void); +static void primitiveMillisecondClock(void); +EXPORT(sqInt) primitiveMillisecondClockMask(void); +static void primitiveMod(void); +EXPORT(void) primitiveModLargeIntegers(void); +EXPORT(sqInt) primitiveMultipleBytecodeSetsActive(void); +static void primitiveMultiply(void); +EXPORT(void) primitiveMultiplyLargeIntegers(void); +static void primitiveNew(void); +static void primitiveNewMethod(void); +static void primitiveNewWithArg(void); +static void primitiveNextInstance(void); +static void primitiveNextObject(void); +static void primitiveNoop(void); +static void primitiveNotEqual(void); +EXPORT(void) primitiveNotEqualLargeIntegers(void); +static void primitiveNotIdentical(void); +static void primitiveObjectsAccessibleFromRoots(void); +EXPORT(sqInt) primitivePathToUsing(void); +static void primitivePerformInSuperclass(void); +static void primitivePerformWithArgs(void); +static void primitivePinnedNew(void); +static void primitivePinnedNewWithArg(void); +EXPORT(sqInt) primitiveProfilePrimitive(void); +EXPORT(sqInt) primitiveProfileSample(void); +EXPORT(sqInt) primitiveProfileStart(void); +static void primitiveQuit(void); +static void primitiveQuo(void); +EXPORT(void) primitiveQuoLargeIntegers(void); +static void primitiveRelinquishProcessor(void); +EXPORT(void) primitiveRemLargeIntegers(void); +static void primitiveScanCharacters(void); +EXPORT(sqInt) primitiveScreenDepth(void); +EXPORT(sqInt) primitiveScreenScaleFactor(void); +static void primitiveScreenSize(void); +static void primitiveSecondsClock(void); +static void primitiveSetDisplayMode(void); +static void primitiveSetFullScreen(void); +#if IMMUTABILITY +static void primitiveSetImmutability(void); +#else +# define primitiveSetImmutability (void (*)(void))0 +#endif /* IMMUTABILITY */ +static void primitiveSetInterruptKey(void); +EXPORT(sqInt) primitiveSetLogDirectory(void); +static void primitiveSetOrHasIdentityHash(void); +static void primitiveShortAt(void); +static void primitiveShortAtPut(void); +static void primitiveShowDisplayRect(void); +static void primitiveSignalAtBytesLeft(void); +static void primitiveSine(void); +static void primitiveSize(void); +static void primitiveSizeInBytes(void); +static void primitiveSizeInBytesOfInstance(void); +static void primitiveSmallFloatAdd(void); +static void primitiveSmallFloatArctan(void); +static void primitiveSmallFloatCosine(void); +static void primitiveSmallFloatDivide(void); +static void primitiveSmallFloatEqual(void); +static void primitiveSmallFloatExp(void); +static void primitiveSmallFloatExponent(void); +static void primitiveSmallFloatFractionalPart(void); +static void primitiveSmallFloatGreaterOrEqual(void); +static void primitiveSmallFloatGreaterThan(void); +static void primitiveSmallFloatLessOrEqual(void); +static void primitiveSmallFloatLessThan(void); +static void primitiveSmallFloatLogN(void); +static void primitiveSmallFloatMultiply(void); +static void primitiveSmallFloatNotEqual(void); +static void primitiveSmallFloatSine(void); +static void primitiveSmallFloatSquareRoot(void); +static void primitiveSmallFloatSubtract(void); +static void primitiveSmallFloatTimesTwoPower(void); +static void primitiveSmallFloatTruncated(void); +static void primitiveSomeInstance(void); +static void primitiveSomeObject(void); +static void primitiveSpecialObjectsOop(void); +static void primitiveSquareRoot(void); +static void primitiveStoreImageSegment(void); +static void primitiveStringAt(void); +static void primitiveStringAtPut(void); +static void primitiveStringReplace(void); +static void primitiveSubtract(void); +EXPORT(void) primitiveSubtractLargeIntegers(void); +static void primitiveTestAndSetOwnershipOfCriticalSection(void); +static void primitiveTestDisplayDepth(void); +#if TestingPrimitives +EXPORT(sqInt) primitiveTestShortenIndexableSize(void); +#endif /* TestingPrimitives */ +static void primitiveTimesTwoPower(void); +static void primitiveTruncated(void); +static void primitiveUninitializedNewWithArg(void); +static void primitiveUninitializedPinnedNewWithArg(void); +static void primitiveUpdateTimezone(void); +static void primitiveUTCMicrosecondClock(void); +static void primitiveUtcAndTimezoneOffset(void); +EXPORT(sqInt) primitiveUtcWithOffset(void); +static void primitiveVMPath(void); +static void primitiveVMProfileSamplesInto(void); +extern int signalNoResume(sqInt aSemaphore); +static NoDbgRegParms sqIntptr_t signedMachineIntegerValueOfObj(sqInt oop); +extern sqIntptr_t signedMachineIntegerValueOf(sqInt oop); +extern usqInt sizeOfAlienData(sqInt oop); +extern void * startOfAlienData(sqInt oop); +extern sqInt success(sqInt successBoolean); +extern void ceCollectArgumentsTypeForFramefulActivation(void); +extern void ceCollectArgumentsTypeForFramlessSpilledActivationOf(CogMethod *cogMethod); +extern void ceCollectArgumentsTypeForFramlessUnspilledActivationWithArgargof(sqInt arg0, sqInt arg1, CogMethod *cogMethod); +extern void ceCollectArgumentsTypeForFramlessUnspilledActivationWithArgof(sqInt arg, CogMethod *cogMethod); +extern void ceCollectInstVarTypeForatof(sqInt rcvr, sqInt instVarIndex, sqInt assignedObject); +extern void ceCollectRemoteTempVarTypeAtof(sqInt tempVarIndex, sqInt assignedObject); +extern void ceCollectReturnTypeForof(CogMethod *cogMethod, sqInt returnObject); +extern void ceCollectTempVarTypeAtof(sqInt tempVarIndex, sqInt assignedObject); +extern void ceCollectTypesForPrimitiveSlotAtputof(sqInt slotIndex, sqInt assignedObject, sqInt rcvr); +static void primitiveSlotAtPut(void); +static NoDbgRegParms sqInt becomeEffectFlagsFor(sqInt objOop); +extern void ceScheduleScavenge(void); +extern sqInt classSmallFloat(void); +static void clearLeakMapAndMapAccessibleObjects(void); +extern void ensureNoForwardedLiteralsIn(sqInt aMethodObj); +static NoDbgRegParms sqInt followForwardedObjectFieldstoDepth(sqInt objOop, sqInt depth); +extern usqInt freeStartAddress(void); +extern usqInt getScavengeThreshold(void); +static NoDbgRegParms sqLong headerWhileForwardingOf(sqInt aCompiledMethodObjOop); +extern sqInt isBytes(sqInt oop); +extern sqInt isForwardedClassIndex(sqInt maybeClassIndex); +extern int isImmediateClass(sqInt classObj); +extern sqInt isReallyYoungObject(sqInt objOop); +extern sqInt methodHeaderOf(sqInt methodObj); +extern usqInt needGCFlagAddress(void); +static NoDbgRegParms void printCantBeObjecton(sqInt oop, FILE *aStream); +extern usqInt specialObjectsOopAddress(void); +extern sqInt withoutForwardingOnandwithsendToCogit(sqInt obj1, sqInt obj2, sqInt aBool, sqInt (*selector)(sqInt,sqInt,sqInt)); +static NoDbgRegParms usqInt addressAfter(sqInt objOop); +static NoDbgRegParms sqInt allocateSlotsForPinningInOldSpacebytesformatclassIndex(sqInt numSlots, usqInt totalBytes, sqInt formatField, sqInt classIndex); +extern sqInt byteSwapped(sqInt w); +static NoDbgRegParms usqInt bytesInBody(sqInt objOop); +static int defaultEdenBytes(void); +extern sqInt fetchClassTagOf(sqInt oop); +extern sqInt floatObjectOf(double aFloat); +static NoDbgRegParms sqLong floatValueBitsOf(sqInt floatOop); +extern double floatValueOf(sqInt oop); +static NoDbgRegParms void hackSlimBridgeToat(sqInt objOop, sqInt startAddress); +extern int headerIndicatesAlternateBytecodeSet(sqInt methodHeader); +static NoDbgRegParms sqInt initFreeChunkWithBytesat(usqLong numBytes, sqInt address); +static NoDbgRegParms void initSegmentBridgeWithBytesat(usqLong numBytes, sqInt address); +extern sqInt instantiateClassindexableSize(sqInt classObj, usqInt nElements); +extern sqInt integerObjectOf(sqInt value); +extern sqInt integerValueOf(sqInt oop); +static NoDbgRegParms int isFloatInstance(sqInt oop); +static NoDbgRegParms int isImmediateFloat(sqInt oop); +extern int isIntegerObject(sqInt oop); +extern int isIntegerValue(sqInt intValue); +static NoDbgRegParms int isLilliputianSize(sqInt chunkBytes); +static NoDbgRegParms int isSmallFloatValueBits(usqLong rawFloatBits); +static NoDbgRegParms int isSmallFloatValue(double aFloat); +static NoDbgRegParms int isWordsNonImm(sqInt objOop); +static NoDbgRegParms sqInt lengthOfformat(sqInt objOop, sqInt fmt); +static NoDbgRegParms double loadFloatOrIntFrom(sqInt floatOrIntOop); +static sqInt maxSlotsForAlloc(void); +extern double noFailFloatValueOf(sqInt aFloatOop); +static sqInt numFreeLists(void); +static NoDbgRegParms sqInt objectAfterMaybeSlimBridgelimit(sqInt objOop, sqInt limit); +static NoDbgRegParms double smallFloatValueOf(sqInt oop); +extern usqInt smallObjectBytesForSlots(sqInt numSlots); +static sqInt wordIndexableFormat(void); +static sqInt wordSizeClassIndexPun(void); +static NoDbgRegParms NeverInline void addToEphemeronList(sqInt ephemeronCorpse); +static NoDbgRegParms NeverInline void addToWeakList(sqInt weakCorpse); +static sqInt allNewSpaceObjectsHaveZeroRTRefCount(void); +static sqInt allWeakSurvivorsOnWeakList(void); +static NeverInline void computeRefCountToShrinkRT(void); +static NoDbgRegParms sqInt copyAndForwardMourner(sqInt mourner); +static NoDbgRegParms sqInt copyAndForward(sqInt survivor); +static NoDbgRegParms NeverInline sqInt copyToOldSpacebytesformat(sqInt survivor, sqInt bytesInObject, sqInt formatOfSurvivor); +extern usqInt corpseForCorpseOffset(sqInt corpseOffset); +static NoDbgRegParms usqInt firstCorpse(sqInt headOfCorpseList); +static void growRememberedSet(void); +static NoDbgRegParms sqInt isInRememberedSet(sqInt objOop); +static NoDbgRegParms sqInt isMaybeOldScavengeSurvivor(sqInt oop); +static NoDbgRegParms sqInt isScavengeSurvivor(sqInt oop); +static NoDbgRegParms sqInt isYetToBeScavenged(sqInt oop); +static usqInt newSpaceCapacity(void); +static NoDbgRegParms sqInt nextCorpseOrNil(sqInt corpse); +extern void openScavengeLog(void); +extern void printEphemeronList(void); +extern void printRememberedSet(void); +static sqInt processEphemerons(void); +static void processWeaklings(void); +static NoDbgRegParms sqInt processWeakSurvivor(sqInt weakObj); +extern sqInt remember(sqInt objOop); +static NoDbgRegParms void scavengeFutureSurvivorSpaceStartingAt(sqInt initialAddress); +static sqInt scavengeInactiveEphemeronsInRememberedSet(void); +static sqInt scavengeInactiveEphemeronsOnEphemeronList(void); +static sqInt scavengeLoop(void); +static NoDbgRegParms sqInt scavengeReferentsOfPointers(sqInt referrer); +static NoDbgRegParms sqInt scavengeReferentsOf(sqInt referrer); +static NoDbgRegParms void scavengeRememberedSetStartingAt(sqInt n); +static float scavengerTenuringThreshold(void); +static sqInt unfiredEphemeronsAtEndOfRememberedSet(void); +static sqInt validRememberedSetPartitionPostSIEIRS(void); +static sqInt wholeEphemeronListIsFirable(void); +static NeverInline void writeScavengeLog(void); +static NoDbgRegParms sqInt accessibleObjectAfter(sqInt objOop); +static NoDbgRegParms NeverInline sqInt activeAndDeferredScan(sqInt anEphemeron); +static NoDbgRegParms void addFreeSubTree(sqInt freeTree); +extern sqInt addGCRoot(sqInt *varLoc); +static NoDbgRegParms sqInt addressCouldBeClassObj(sqInt maybeClassObj); +extern sqInt addressCouldBeObj(sqInt address); +extern sqInt addressCouldBeOop(sqInt address); +static NoDbgRegParms sqInt addToFreeListbytes(sqInt freeChunk, sqInt chunkBytes); +static NoDbgRegParms sqInt allInstancesOf(sqInt aClass); +static sqInt allObjects(void); +static sqInt allObjectsUnmarked(void); +static sqInt allOldMarkedWeakObjectsOnWeaklingStack(void); +static sqInt allocateLargestFreeChunk(void); +static NoDbgRegParms sqInt allocateOldSpaceChunkOfBytes(usqInt chunkBytes); +extern sqInt allocatePinnedSlots(sqInt nSlots); +static NoDbgRegParms sqInt allocateSlotsInOldSpacebytesformatclassIndex(sqInt numSlots, usqInt totalBytes, sqInt formatField, sqInt classIndex); +static sqInt allocationUnit(void); +static NoDbgRegParms sqInt allStrongSlotsOfWeaklingAreMarked(sqInt aWeakling); +static sqInt allUnscannedEphemeronsAreActive(void); +static NoDbgRegParms NeverInline sqInt assertInnerValidFreeObject(sqInt objOop); +extern sqInt becomewith(sqInt array1, sqInt array2); +static NoDbgRegParms sqInt becomewithtwoWaycopyHash(sqInt array1, sqInt array2, sqInt twoWayFlag, sqInt copyHashFlag); +extern void beRootIfOld(sqInt oop); +static sqInt bitsSetInFreeSpaceMaskForAllFreeLists(void); +static sqInt bridgeSize(void); +static NoDbgRegParms sqInt byteFormatForNumBytes(sqInt numBytes); +static sqInt byteFormatMask(void); +extern sqInt byteSizeOf(sqInt oop); +extern unsigned char bytesPerElement(sqInt oop); +static NoDbgRegParms sqInt changeClassOfto(sqInt rcvr, sqInt argClass); +extern sqInt characterObjectOf(sqInt characterCode); +extern usqInt characterValueOf(sqInt oop); +static NoDbgRegParms sqInt cheapAddressCouldBeInHeap(sqInt address); +extern sqInt checkedLongAt(sqInt byteAddress); +static sqInt checkHeapFreeSpaceIntegrity(void); +static NoDbgRegParms sqInt checkHeapIntegrityclassIndicesShouldBeValid(sqInt excludeUnmarkedObjs, sqInt classIndicesShouldBeValid); +extern sqInt checkOkayOop(usqInt oop); +extern sqInt checkOopHasOkayClass(usqInt obj); +static NoDbgRegParms sqInt checkOopIntegritynamed(sqInt obj, char *name); +static NoDbgRegParms sqInt checkOopIntegritynamedindex(sqInt obj, char *name, sqInt i); +extern sqInt classAlien(void); +extern sqInt classArray(void); +extern sqInt classAtIndex(sqInt classIndex); +static NoDbgRegParms sqInt classAtIndexput(sqInt classIndex, sqInt objOop); +extern sqInt classBitmap(void); +extern sqInt classByteArray(void); +extern sqInt classCharacter(void); +extern sqInt classDoubleByteArray(void); +extern sqInt classDoubleWordArray(void); +extern sqInt classExternalAddress(void); +extern sqInt classExternalData(void); +extern sqInt classExternalFunction(void); +extern sqInt classExternalLibrary(void); +extern sqInt classExternalStructure(void); +extern sqInt classFloat(void); +extern sqInt classFloat32Array(void); +extern sqInt classFloat64Array(void); +static NoDbgRegParms sqInt classForClassTag(sqInt classIndex); +static NoDbgRegParms sqInt classFormatFromInstFormat(sqInt instFormat); +static sqInt classIndexFieldWidth(void); +extern sqInt classIndexOf(sqInt objOop); +extern sqInt classLargeNegativeInteger(void); +extern sqInt classLargePositiveInteger(void); +extern sqInt classOrNilAtIndex(sqInt classIndex); +extern sqInt classPoint(void); +extern sqInt classSemaphore(void); +extern sqInt classSmallInteger(void); +extern sqInt classString(void); +extern sqInt classTableMinorIndexMask(void); +extern sqInt classTablePageSize(void); +extern sqInt classTableRootObj(void); +static sqInt classTableRootSlots(void); +extern sqInt classTagForClass(sqInt classObj); +extern sqInt classUnsafeAlien(void); +extern sqInt classWordArray(void); +static void clearLeakMapAndMapAccessibleFreeSpace(void); +static NoDbgRegParms sqInt cloneInOldSpaceforPinning(sqInt objOop, sqInt forPinning); +extern sqInt cloneObject(sqInt objOop); +static NoDbgRegParms sqInt copyObjtoAddrstopAtsavedFirstFieldsindex(sqInt objOop, sqInt segAddr, sqInt endSeg, sqInt savedFirstFields, sqInt i); +extern void countMarkedAndUnmarkdObjects(sqInt printFlags); +static usqLong currentAllocatedBytes(void); +static NoDbgRegParms void doScavenge(sqInt tenuringCriterion); +static NoDbgRegParms sqInt doShortentoIndexableSize(sqInt objOop, sqInt indexableSize); +extern usqInt eeInstantiateClassIndexformatnumSlots(sqInt knownClassIndex, sqInt objFormat, sqInt numSlots); +extern NeverInline void eek(void); +static NoDbgRegParms void enableObjectEnumerationFrom(sqInt initialObject); +static NoDbgRegParms sqInt ensureBehaviorHash(sqInt aBehavior); +static NoDbgRegParms sqInt ensureRoomOnObjStackAt(sqInt objStackRootIndex); +static NoDbgRegParms sqInt enterIntoClassTable(sqInt aBehavior); +static NoDbgRegParms sqInt existInstancesInNewSpaceOf(sqInt classObj); +static NoDbgRegParms void expungeFromClassTable(sqInt aBehavior); +extern sqInt falseObject(void); +extern sqInt fetchByteofObject(sqInt byteIndex, sqInt objOop); +static NoDbgRegParms NeverInline sqInt fetchClassOfNonImm(sqInt objOop); +extern sqInt fetchClassOf(sqInt oop); +static NoDbgRegParms sqInt fetchClassTagOfNonImm(sqInt obj); +extern sqLong fetchIntegerofObject(sqInt fieldIndex, sqInt objOop); +extern int fetchLong32ofObject(sqInt fieldIndex, sqInt oop); +static NoDbgRegParms sqInt fetchPointerofFreeChunk(sqInt fieldIndex, sqInt objOop); +extern sqInt fetchPointerofObject(sqInt fieldIndex, sqInt objOop); +extern sqInt findClassNamed(char *aString); +static sqInt findLargestFreeChunk(void); +extern void findStringBeginningWith(char *aCString); +extern void findString(char *aCString); +static NoDbgRegParms void fireEphemeron(sqInt ephemeron); +static sqInt firstAccessibleObject(void); +static NoDbgRegParms void * firstFixedFieldOfMaybeImmediate(sqInt oop); +extern void * firstFixedField(sqInt objOop); +extern void * firstIndexableField(sqInt objOop); +extern sqInt fixedFieldsOfClassFormatMask(void); +extern sqInt fixedFieldsOfClassFormat(sqInt classFormat); +static NoDbgRegParms sqInt fixedFieldsOfformatlength(sqInt objOop, sqInt fmt, sqInt wordLength); +static NoDbgRegParms NeverInline sqInt fixFollowedFieldofObjectwithInitialValue(sqInt fieldIndex, sqInt anObject, sqInt initialValue); +static NoDbgRegParms sqInt followedKeyOfEphemeron(sqInt objOop); +static NoDbgRegParms sqInt followFieldofObject(sqInt fieldIndex, sqInt anObject); +extern sqInt followForwarded(sqInt objOop); +static NoDbgRegParms sqInt followMaybeForwarded(sqInt objOop); +static NoDbgRegParms sqInt followObjFieldofObject(sqInt fieldIndex, sqInt anObject); +extern sqInt formatOfClass(sqInt classPointer); +static NoDbgRegParms sqInt formatOf(sqInt objOop); +static NoDbgRegParms sqInt forwardersIn(sqInt anObject); +static sqInt freeChunkNextIndex(void); +static sqInt freeChunkParentIndex(void); +static sqInt freeChunkPrevIndex(void); +static NoDbgRegParms sqInt freeChunkWithBytesat(sqInt bytes, sqInt address); +extern sqInt freeObject(sqInt objOop); +extern NeverInline usqLong fullGC(void); +static float getHeapGrowthToSizeGCRatio(void); +static NoDbgRegParms sqInt goodContextSize(sqInt oop); +static NoDbgRegParms usqInt growOldSpaceByAtLeast(sqInt minAmmount); +static NoDbgRegParms sqInt hasPointerFieldsNonImm(sqInt oop); +extern usqLong headerForSlotsformatclassIndex(sqInt numSlots, sqInt formatField, sqInt classIndex); +static NoDbgRegParms usqLong headerForSlotshashformatclassIndex(sqInt numSlots, sqInt hash, sqInt formatField, sqInt classIndex); +static sqInt hiddenRootSlots(void); +static sqInt hiddenRootsObject(void); +static sqInt imageSegmentVersion(void); +#if IMMUTABILITY +extern sqInt immutableBitMask(void); +#endif /* IMMUTABILITY */ +extern sqInt indexOfin(sqInt anElement, sqInt anObject); +static NoDbgRegParms void inFreeTreeReplacewith(sqInt treeNode, sqInt newNode); +static NoDbgRegParms sqInt initialInstanceOf(sqInt classObj); +static NeverInline void initializeNewSpaceVariables(void); +static NoDbgRegParms void initializeObjectMemory(sqInt bytesToShift); +static void initializeUnscannedEphemerons(void); +extern void inOrderPrintFreeTreeprintList(sqInt freeChunk, sqInt printNextList); +static NoDbgRegParms NeverInline void inPlaceBecomeandcopyHashFlag(sqInt obj1, sqInt obj2, sqInt copyHashFlag); +extern sqInt instanceSizeOf(sqInt classObj); +static NoDbgRegParms sqInt instantiateClass(sqInt classObj); +extern sqInt instSpecOfClassFormat(sqInt classFormat); +static NoDbgRegParms sqInt instSpecOfClass(sqInt classPointer); +static NoDbgRegParms void invalidCompactClassError(const char *className); +extern sqInt isArray(sqInt oop); +static NoDbgRegParms int isBridgeOrEnumerableObjectNoAssert(sqInt objOop); +extern int isCharacterObject(sqInt oop); +extern int isCharacterValue(sqInt anInteger); +static NoDbgRegParms int isCompiledMethodFormat(sqInt format); +static NoDbgRegParms int isCompiledMethodHeader(sqInt objHeader); +extern int isCompiledMethod(sqInt objOop); +static NoDbgRegParms int isContextHeader(sqInt aHeader); +static NoDbgRegParms int isContextNonImm(sqInt oop); +static NoDbgRegParms sqInt isContext(sqInt oop); +static NoDbgRegParms int isCopiedIntoSegment(sqInt anObjectInTheHeap); +static NoDbgRegParms sqInt isEmptyObjStack(sqInt objStack); +static NoDbgRegParms sqInt isEnumerableObjectNoAssert(sqInt objOop); +static NoDbgRegParms int isEphemeronFormat(sqInt format); +static NoDbgRegParms int isEphemeron(sqInt objOop); +extern sqInt isFixedSizePointerFormat(sqInt format); +static NoDbgRegParms int isForwardedClassTag(sqInt classIndex); +extern int isForwarded(sqInt objOop); +static NoDbgRegParms int isFreeObject(sqInt objOop); +static NoDbgRegParms sqInt isFreeOop(sqInt oop); +extern int isImmediate(sqInt oop); +static NoDbgRegParms sqInt isInClassTable(sqInt objOop); +extern sqInt isIndexable(sqInt objOop); +static NoDbgRegParms sqInt isInEden(sqInt objOop); +static NoDbgRegParms sqInt isInFutureSpace(sqInt address); +extern sqInt isInMemory(sqInt address); +static NoDbgRegParms sqInt isInNewSpace(sqInt objOop); +extern sqInt isInOldSpace(sqInt address); +static NoDbgRegParms sqInt isInPastSpace(sqInt address); +static NoDbgRegParms int isLargeFreeObject(sqInt objOop); +extern sqInt isLong64s(sqInt oop); +extern int isMarked(sqInt objOop); +static NoDbgRegParms sqInt isMaybeFiredEphemeron(sqInt objOop); +extern int isNonImmediate(sqInt oop); +static NoDbgRegParms int isObjEphemeron(sqInt objOop); +static NoDbgRegParms sqInt isObjImmutable(sqInt anOop); +static NoDbgRegParms int isOldObject(sqInt objOop); +extern sqInt isOopCompiledMethod(sqInt oop); +extern sqInt isOopForwarded(sqInt oop); +extern sqInt isOopImmutable(sqInt oop); +extern sqInt isOopMutable(sqInt oop); +extern int isPinned(sqInt objOop); +extern sqInt isPointers(sqInt oop); +static NoDbgRegParms sqInt isPureBitsFormat(sqInt format); +static NoDbgRegParms sqInt isPureBitsNonImm(sqInt objOop); +static NoDbgRegParms int isRemembered(sqInt objOop); +static NoDbgRegParms int isSegmentBridge(sqInt objOop); +static NoDbgRegParms sqInt isSemaphoreOop(sqInt anOop); +extern sqInt isShorts(sqInt oop); +static NoDbgRegParms int isUnambiguouslyForwarder(sqInt objOop); +extern int isUnmarked(sqInt objOop); +extern sqInt isValidClassTag(sqInt classIndex); +static NoDbgRegParms sqInt isValidObjStackAt(sqInt objStackRootIndex); +static NoDbgRegParms sqInt isValidObjStackPagemyIndex(sqInt objStackPage, sqInt myx); +static NoDbgRegParms sqInt isValidObjStackPagemyIndexfirstPage(sqInt objStackPage, sqInt myx, sqInt isFirstPage); +static NoDbgRegParms sqInt isValidObjStack(sqInt objStack); +static NoDbgRegParms int isWeakNonImm(sqInt objOop); +extern sqInt isWeak(sqInt oop); +static NoDbgRegParms sqInt isWordsOrBytesNonImm(sqInt objOop); +extern sqInt isWordsOrBytes(sqInt oop); +extern sqInt isWordsOrShorts(sqInt oop); +extern sqInt isWords(sqInt oop); +extern int isYoungObject(sqInt objOop); +extern sqInt isYoung(sqInt oop); +static NoDbgRegParms sqInt isinstanceOfcompactClassIndex(sqInt oop, sqInt classOop, sqInt compactClassIndex); +extern sqInt isonObjStack(sqInt oop, sqInt objStack); +static NoDbgRegParms sqInt keyOfEphemeron(sqInt objOop); +static NoDbgRegParms sqInt keyOfMaybeFiredEphemeron(sqInt objOop); +static NoDbgRegParms sqInt knownClassAtIndex(sqInt classIndex); +static sqInt lastPointerFormat(void); +static NoDbgRegParms sqInt lastPointerOfWhileSwizzling(sqInt objOop); +static NoDbgRegParms sqInt lastPointerOf(sqInt objOop); +extern int leakCheckFullGC(void); +extern int leakCheckNewSpaceGC(void); +static NoDbgRegParms sqInt lengthOfMaybeImmediate(sqInt oop); +static NoDbgRegParms sqInt lengthOf(sqInt objOop); +extern usqInt literalCountOfMethodHeader(sqInt header); +extern usqInt literalCountOf(sqInt methodPointer); +static NoDbgRegParms NeverInline sqInt loadImageSegmentFromoutPointers(sqInt segmentWordArray, sqInt outPointerArray); +extern void longPrintInstancesOf(sqInt aClassOop); +extern void longPrintInstancesWithClassIndex(sqInt classIndex); +extern void longPrintReferencesTo(sqInt anOop); +static void mapExtraRoots(void); +static NeverInline void mapMournQueue(void); +static NoDbgRegParms sqInt mapOopsFromtooutPointersoutHashes(sqInt segStart, sqInt segAddr, sqInt outPointerArray, sqInt savedOutHashes); +static NoDbgRegParms void markAndTraceClassOf(sqInt objOop); +static NoDbgRegParms void markAndTraceObjStackandContents(sqInt stackOrNil, sqInt markAndTraceContents); +extern NeverInline void markAndTrace(sqInt objOop); +static sqInt markInactiveEphemerons(void); +static NoDbgRegParms NeverInline void markObjects(sqInt objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged); +static void markWeaklingsAndMarkAndFireEphemerons(void); +extern sqInt maxIdentityHash(void); +extern sqInt maxSlotsForNewSpaceAlloc(void); +extern sqInt maybeMethodClassOfseemsToBeInstantiating(sqInt methodObj, sqInt format); +extern sqInt minSlotsForShortening(void); +static NoDbgRegParms usqInt newHashBitsOf(sqInt objOop); +static sqInt newSpaceIsEmpty(void); +extern sqInt nilObject(void); +static NeverInline void nilUnmarkedWeaklingSlots(void); +static NoDbgRegParms sqInt noCheckClassAtIndex(sqInt classIndex); +static NoDbgRegParms sqInt noCheckPushonObjStack(sqInt objOop, sqInt objStack); +static NoDbgRegParms sqInt noFixupFollowFieldofObject(sqInt fieldIndex, sqInt anObject); +static NoDbgRegParms NeverInline sqInt noInlineAllocateSlotsformatclassIndex(sqInt numSlots, sqInt formatField, sqInt classIndex); +static int noUnscannedEphemerons(void); +static NoDbgRegParms sqInt numBytesOfBitsformat(sqInt objOop, sqInt format); +static NoDbgRegParms sqInt numBytesOfBytes(sqInt objOop); +extern sqInt numBytesOf(sqInt objOop); +static NoDbgRegParms usqInt numPointerSlotsOf(sqInt objOop); +static NoDbgRegParms sqInt numSlotsForBytes(sqInt numBytes); +static NoDbgRegParms usqInt numSlotsOfAny(sqInt objOop); +static NoDbgRegParms usqInt numSlotsOfIndexablePointerObj(sqInt objOop); +extern usqInt numSlotsOf(sqInt objOop); +static NoDbgRegParms sqInt numStrongSlotsOfInephemeral(sqInt objOop); +static NoDbgRegParms sqInt numStrongSlotsOfWeakling(sqInt objOop); +static sqInt numUnscannedEphemerons(void); +extern sqInt objCouldBeClassObj(sqInt objOop); +extern sqInt objectAfter(sqInt objOop); +extern sqInt objectBefore(sqInt objOop); +static NoDbgRegParms sqInt objectInPastSpaceBefore(sqInt objOop); +static NoDbgRegParms sqInt objectStartingAt(sqInt address); +static NoDbgRegParms sqInt objectsAccessibleFromRoots(sqInt arrayOfRootsArg); +static NoDbgRegParms NeverInline sqInt objectsReachableFromRoots(sqInt arrayOfRoots); +static NoDbgRegParms sqInt okayOop(sqInt signedOop); +extern sqInt oldSpaceObjectAfter(sqInt objOop); +static NoDbgRegParms NeverInline void outOfPlaceBecomeandcopyHashFlag(sqInt obj1, sqInt obj2, sqInt copyHashFlag); +extern sqInt pinObject(sqInt objOop); +static NoDbgRegParms sqInt popObjStack(sqInt objStack); +extern sqInt popRemappableOop(void); +static NoDbgRegParms void postBecomeScanClassTable(sqInt effectsFlags); +extern sqInt primitiveErrorTable(void); +extern void printActivationsOf(sqInt aMethodObj); +extern void printBogons(void); +extern void printContextReferencesTo(sqInt anOop); +extern void printEntity(sqInt oop); +extern void printEphemerons(void); +extern void printForwarders(void); +static NoDbgRegParms void printForwarderon(sqInt oop, FILE *aStream); +extern void printFreeChunks(void); +extern void printFreeChunk(sqInt freeChunk); +static NoDbgRegParms void printFreeChunkprintAsTreeNode(sqInt freeChunk, sqInt printAsTreeNode); +extern void printFreeListHeads(void); +extern sqInt printFreeList(sqInt chunkOrIndex); +static NoDbgRegParms void printFreeObjecton(sqInt oop, FILE *aStream); +extern void printFreeTree(void); +static NoDbgRegParms void printFreeTreeChunk(sqInt chunkOrZero); +extern void printHeaderOf(sqInt objOop); +static NoDbgRegParms void printImmediateObjecton(sqInt oop, FILE *aStream); +extern void printInstancesOf(sqInt aClassOop); +extern void printInstancesWithClassIndex(sqInt classIndex); +extern void printInvalidClassTableEntries(void); +#if LLDB +extern void printMarkedOops(void); +#endif /* LLDB */ +extern void printMethodImplementorsOf(sqInt anOop); +extern void printMethodReferencesTo(sqInt anOop); +extern void printMournQueue(void); +extern void printNewObjectsInObjStack(sqInt objStack); +static NoDbgRegParms void printNonPointerDataOfon(sqInt oop, FILE *aStream); +extern void printObjectsFromto(sqInt startAddress, sqInt endAddress); +extern void printObjectsWithHash(sqInt hash); +extern void printObjStackAndContents(sqInt objStack); +static NoDbgRegParms sqInt printObjStackPagemyIndexpageTypeprintContents(sqInt objStackPage, sqInt myx, sqInt pageType, sqInt printContents); +static NoDbgRegParms void printObjStackPagemyIndextag(sqInt objStackPage, sqInt myx, char *pageType); +extern void printObjStack(sqInt objStack); +extern NeverInline void printOopsExcept(sqInt (*function)(sqInt)); +extern void printOopsFromto(sqInt startAddress, sqInt endAddress); +extern NeverInline void printOopsSuchThat(sqInt (*function)(sqInt)); +extern void printReferencesTo(sqInt anOop); +static NoDbgRegParms void printStringDataOfon(sqInt oop, FILE *aStream); +#if LLDB +extern void printUnmarkedOops(void); +#endif /* LLDB */ +extern void printWeaklings(void); +static NoDbgRegParms sqInt pushOnUnscannedEphemeronsStack(sqInt anEphemeron); +extern void pushRemappableOop(sqInt oop); +static NoDbgRegParms void queueMourner(sqInt anEphemeronOrWeakArray); +extern int rawHashBitsOf(sqInt objOop); +extern sqInt receiverTagBitsForMethod(sqInt aMethodObj); +static void refireQueuedEphemeronsPostSnapshot(void); +static NoDbgRegParms sqInt relocateObjStackForPlanningCompactorandContents(sqInt objStack, sqInt relocateContents); +extern sqInt removeGCRoot(sqInt *varLoc); +static NoDbgRegParms sqInt returnrestoringObjectsInsavedFirstFieldsandsavedHashes(sqInt errCode, sqInt reachableObjectsArray, sqInt savedFirstFields, sqInt outPointersArray, sqInt savedHashes); +static NoDbgRegParms sqInt returnrestoringObjectsInupTosavedFirstFields(sqInt errCode, sqInt reachableObjectsArray, sqInt limitOrTag, sqInt savedFirstFields); +static NoDbgRegParms NeverInline void reverseBytesIn32BitWordsIn(sqInt segmentWordArray); +static NoDbgRegParms void runLeakCheckerForFreeSpaceignoring(sqInt gcModes, sqInt anOopOrNil); +static NoDbgRegParms void runLeakCheckerFor(sqInt gcModes); +static NoDbgRegParms void runLeakCheckerForexcludeUnmarkedObjsclassIndicesShouldBeValid(sqInt gcModes, sqInt excludeUnmarkedObjs, sqInt classIndicesShouldBeValid); +static NoDbgRegParms void safePrintStringOf(sqInt oop); +static NoDbgRegParms void scavengingGCTenuringIf(sqInt tenuringCriterion); +static NoDbgRegParms NeverInline sqInt setHeapBasememoryLimitendOfMemory(sqInt baseOfHeap, sqInt memLimit, sqInt memEnd); +static NoDbgRegParms void setIsPinnedOfto(sqInt objOop, sqInt aBoolean); +static NoDbgRegParms void setIsRememberedOfto(sqInt objOop, sqInt aBoolean); +extern sqInt shortentoIndexableSize(sqInt objOop, sqInt indexableSize); +extern void shortPrintObjectsFromto(sqInt startAddress, sqInt endAddress); +extern sqInt shouldRemapOop(sqInt oop); +static NoDbgRegParms sqInt sizeOfObjStack(sqInt objStack); +static int slidingCompactionInProgress(void); +extern sqInt slotSizeOf(sqInt oop); +extern sqInt splObj(sqInt index); +static NoDbgRegParms usqInt startOfObject(sqInt objOop); +extern sqInt statNumGCs(void); +extern usqInt storeCheckBoundary(void); +static NoDbgRegParms sqInt storeImageSegmentIntooutPointersroots(sqInt segmentWordArrayArg, sqInt outPointersArrayArg, sqInt arrayOfRootsArg); +extern void storeIntegerofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqLong integerValue); +extern int storeLong32ofObjectwithValue(sqInt fieldIndex, sqInt obj, sqInt valueWord); +extern sqInt storePointerUncheckedofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqInt valuePointer); +extern sqInt storePointerofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqInt valuePointer); +extern sqInt stringForCString(const char *aCString); +static NoDbgRegParms sqInt swizzleObjStackAt(sqInt objStackRootIndex); +extern void tenuringIncrementalGC(void); +static NoDbgRegParms sqInt topOfObjStack(sqInt objStack); +extern sqInt topRemappableOop(void); +static sqInt totalFreeListBytes(void); +extern sqInt trueObject(void); +static void unfireQueuedEphemeronsForSnapshot(void); +static NoDbgRegParms sqInt unlinkFreeChunkchunkBytes(sqInt freeChunk, sqInt chunkBytes); +static NoDbgRegParms NeverInline sqInt unlinkLilliputianChunkindex(sqInt freeChunk, sqInt index); +static NoDbgRegParms void unlinkSolitaryFreeTreeNode(sqInt freeTreeNode); +extern sqInt unpinObject(sqInt objOop); +static sqInt unscannedEphemeronsContainsOnlyActiveEphemerons(void); +static sqInt unscannedEphemeronsContainsOnlyEphemerons(void); +static NoDbgRegParms void updateListStartingAt(sqInt freeNode); +static sqInt validClassTableHashes(void); +static sqInt validClassTableRootPages(void); +extern int validFreeTree(void); +static NoDbgRegParms sqInt validFreeTreeChunk(sqInt chunk); +static NoDbgRegParms const char * validFreeTreeChunkparent(sqInt chunk, sqInt parent); +static sqInt validObjStacks(void); +static NoDbgRegParms sqInt validPostBecomeArrayContents(sqInt anArray); +static NoDbgRegParms sqInt validStorePointerArgs(sqInt fieldIndex, sqInt objOop, sqInt valuePointer); +static NoDbgRegParms sqInt validStorePointerUncheckedArgs(sqInt fieldIndex, sqInt objOop, sqInt valuePointer); +extern sqInt vmEndianness(void); +static NoDbgRegParms char * whereIsMaybeHeapThing(sqInt anOop); +static NeverInline void compact(void); +static NeverInline sqInt copyAndUnmarkMobileObjects(void); +static NoDbgRegParms NeverInline void copyAndUnmark(sqInt firstPass); +static void endCompaction(void); +static NoDbgRegParms void freeFromupTonextObject(usqInt initialToFinger, usqInt limit, sqInt nextObject); +static NoDbgRegParms sqInt isMobile(sqInt obj); +static NoDbgRegParms sqInt isPostMobile(sqInt obj); +static NeverInline sqInt planCompactSavingForwarders(void); +static NoDbgRegParms void reinitializeScanFrom(sqInt initialObject); +extern sqInt remapObj(sqInt objOop); +static sqInt savedFirstFieldsSpaceWasAllocated(void); +static NoDbgRegParms sqInt scanForFirstFreeAndFirstMobileObjectFrom(sqInt initialObject); +extern sqInt shouldRemapObj(sqInt objOop); +static void unmarkObjectsFromFirstFreeObject(void); +static NeverInline void updatePointers(void); +static NoDbgRegParms void updatePointersInsavedFirstFieldPointer(sqInt obj, sqInt firstFieldPtr); +static NoDbgRegParms sqInt useSegmentForSavedFirstFieldsSpace(sqInt spaceEstimate); +static NoDbgRegParms sqInt validRelocationPlanInPass(sqInt onePass); +static NoDbgRegParms usqInt segLimit(SpurSegmentInfo *self_in_SpurSegmentInfo); +static NoDbgRegParms SpurSegmentInfo * addSegmentOfSize(sqInt ammount); +static NoDbgRegParms void adjustSegmentSwizzlesBy(sqInt firstSegmentShift); +static sqInt allBridgesMarked(void); +static void allocateOrExtendSegmentInfos(void); +static NoDbgRegParms sqInt bridgeAt(sqInt segIndex); +static NoDbgRegParms sqInt bridgeFor(SpurSegmentInfo *aSegment); +static NoDbgRegParms void bridgeFromto(SpurSegmentInfo *aSegment, SpurSegmentInfo *nextSegmentOrNil); +static void checkSegments(void); +static void collapseSegmentsPostSwizzle(void); +static NoDbgRegParms void * firstGapOfSizeAtLeast(sqInt size); +static NoDbgRegParms sqInt isEmptySegment(SpurSegmentInfo *seg); +static NoDbgRegParms sqInt isInSegments(usqInt address); +static NoDbgRegParms sqInt isValidSegmentBridge(sqInt objOop); +static NoDbgRegParms SpurSegmentInfo * nextNonEmptySegmentAfter(sqInt i); +static NeverInline void prepareForSnapshot(void); +static NoDbgRegParms sqInt readHeapFromImageFiledataBytes(sqImageFile f, sqInt numBytes); +static void restorePostSnapshot(void); +extern SpurSegmentInfo * segmentContainingObj(sqInt objOop); +static sqInt segmentOverlap(void); +static NoDbgRegParms int shrinkObjectMemory(usqInt delta); +static NoDbgRegParms sqInt swizzleObj(sqInt objOop); +static usqInt totalBytesInSegments(void); +static NoDbgRegParms sqInt writeImageSegmentsToFile(sqImageFile aBinaryStream); +static NoDbgRegParms sqInt writeSegmentnextSegmenttoFile(SpurSegmentInfo *segment, SpurSegmentInfo *nextSegment, sqImageFile aBinaryStream); +static NoDbgRegParms sqInt accessorDepthForExternalPrimitiveMethod(sqInt methodObj); +extern sqInt accessorDepthForPrimitiveMethod(sqInt aMethodObj); +extern sqInt activeProcess(void); +extern void addIdleUsecs(sqInt idleUsecs); +static NoDbgRegParms void addLastLinktoList(sqInt proc, sqInt aList); +static char * allOnesAsCharStar(void); +extern sqInt argumentCountOfClosure(sqInt closurePointer); +extern usqInt argumentCountOfMethodHeader(sqInt header); +extern usqInt argumentCountOf(sqInt methodPointer); +extern void * arrayValueOf(sqInt arrayOop); +static NoDbgRegParms sqInt asciiOfCharacter(sqInt characterObj); +extern void assertValidExecutionPointers(usqInt lip, char *lifp, char *lisp); +static NoDbgRegParms void assertValidStackLimits(sqInt ln); +extern sqInt booleanValueOf(sqInt obj); +extern sqInt callbackEnter(sqInt *callbackID); +extern sqInt callbackLeave(sqInt cbID); +extern sqInt canContextSwitchIfActivatingheader(sqInt theMethod, sqInt methodHeader); +extern sqInt characterForAscii(sqInt ascii); +extern sqInt checkAllAccessibleObjectsOkay(void); +#if LRPCheck +static NeverInline sqInt checkDeliveryOfLongRunningPrimitiveSignal(void); +#endif /* LRPCheck */ +extern sqInt checkedIntegerValueOf(sqInt intOop); +static NoDbgRegParms sqInt checkForEventsMayContextSwitch(sqInt mayContextSwitch); +extern sqInt checkImageHeaderFromBytesAndSize(char *bytes, sqInt totalSize); +static NoDbgRegParms sqInt checkImageVersionFromstartingAtassignRawVersion(sqImageFile f, squeakFileOffsetType imageOffset, sqInt *rawVersionPtr); +static sqInt checkInterpreterIntegrity(void); +static NoDbgRegParms sqInt checkIsStillMarriedContextcurrentFP(sqInt aContext, char *currentFP); +static sqInt checkLogIntegrity(void); +extern sqInt checkOkayInterpreterObjects(sqInt writeBack); +static NoDbgRegParms sqInt checkOkayStackPage(StackPage *thePage); +static NoDbgRegParms sqInt checkOkayStackZone(sqInt writeBack); +static sqInt checkStackIntegrity(void); +static NoDbgRegParms sqInt checkStackPointerForMaybeMarriedContext(sqInt aContext); +static NoDbgRegParms sqInt checkStackPointerIndexForFrame(char *theFP); +static NoDbgRegParms sqInt classNameOfIs(sqInt aClass, char *className); +extern void clearTraceLog(void); +static NoDbgRegParms sqInt contexthasValidInversePCMappingOfin(sqInt aContext, sqInt theIP, char *theFP); +extern sqInt copiedValueCountOfClosure(sqInt closureObj); +extern sqInt copyBits(void); +extern sqInt copyBitsFromtoat(sqInt x0, sqInt x1, sqInt y); +static NoDbgRegParms NeverInline sqInt couldBeProcess(sqInt oop); +static NoDbgRegParms void createActualMessageTo(sqInt lookupClass); +static sqInt defaultNumStackPages(void); +#if SEND_PRINTING +extern void disableSendPrinting(void); +#endif /* SEND_PRINTING */ +extern sqInt disownVM(sqInt flags); +static sqInt divorceAllFrames(void); +static NoDbgRegParms void divorceFramesIn(StackPage *aStackPage); +static NoDbgRegParms sqInt doPrimitiveDivby(sqInt rcvr, sqInt arg); +static NoDbgRegParms sqInt doPrimitiveModby(sqInt rcvr, sqInt arg); +extern sqInt doSignalSemaphoreWithIndex(sqInt index); +extern struct VirtualMachine * * dummyReferToProxy(void); +extern void dumpPrimTraceLog(void); +extern void dumpPrimTraceLogOn(FILE *aStdioStream); +extern void dumpTraceLog(void); +extern NeverInline void eekcr(void); +#if SEND_PRINTING +extern void enableSendPrinting(void); +#endif /* SEND_PRINTING */ +static NoDbgRegParms sqInt ensureCallerContext(char *theFP); +static NoDbgRegParms void ensureImageFormatIsUpToDate(sqInt swapBytes); +static NoDbgRegParms char * establishFrameForContextToReturnTo(sqInt contextToReturnTo); +static NoDbgRegParms void externalDivorceFrameandContext(char *theFP, sqInt ctxt); +static NoDbgRegParms char * externalEnsureIsBaseFrame(char *aFramePtr); +static NoDbgRegParms sqInt externalInstVarofContextput(sqInt index, sqInt maybeMarriedContext, sqInt anOop); +static sqInt externalQuickPrimitiveResponse(void); +static void failUnbalancedPrimitive(void); +extern void * fetchArrayofObject(sqInt fieldIndex, sqInt objectPointer); +extern double fetchFloatofObject(sqInt fieldIndex, sqInt objectPointer); +static NoDbgRegParms sqInt fetchPointerofMarriedContext(sqInt offset, sqInt aContext); +static NoDbgRegParms sqInt fetchStackPointerOf(sqInt aContext); +extern int fileTimesInUTC(void); +static NoDbgRegParms sqInt findClassContainingMethodstartingAt(sqInt meth, sqInt classObj); +extern sqInt findClassOfMethodforReceiver(sqInt meth, sqInt rcvr); +static NoDbgRegParms char * findFrameAboveinPage(char *theFP, StackPage *thePage); +static sqInt findHighestPriority(void); +static NoDbgRegParms sqInt findHomeForContext(sqInt aContext); +static NoDbgRegParms sqInt findMethodWithPrimitiveFromContextUpToContext(sqInt primitive, sqInt senderContext, sqInt homeContext); +extern sqInt findSelectorOfMethod(sqInt aMethodOop); +static NoDbgRegParms char * findSPOfon(char *theFP, StackPage *thePage); +static NoDbgRegParms sqInt findUnwindThroughContext(sqInt homeContext); +extern void flush(void); +static NoDbgRegParms sqInt flushExternalPrimitiveOf(sqInt methodObj); +static NoDbgRegParms void flushMethodCacheForMethod(sqInt oldMethod); +static NoDbgRegParms void followForwardedFrameContentsstackPointer(char *theFP, char *theSP); +extern sqInt forceInterruptCheck(void); +static NoDbgRegParms char * frameCallerSP(char *theFP); +static NoDbgRegParms sqInt frameContext(char *theFP); +static NoDbgRegParms char * frameOfMarriedContext(sqInt aContext); +static NoDbgRegParms sqInt frameStackedReceiverOffset(char *theFP); +static NoDbgRegParms sqInt frameStackedReceivernumArgs(char *theFP, sqInt numArgs); +static void freeUntracedStackPages(void); +extern sqInt fullDisplayUpdate(void); +static NoDbgRegParms void (*functionPointerForinClass(sqInt primIdx, sqInt theClass))(void); +static sqInt getCogVMFeatureFlags(void); +static sqInt getErrorObjectFromPrimFailCode(void); +extern int getFullScreenFlag(void); +extern sqInt getInterruptKeycode(void); +extern sqInt getInterruptPending(void); +extern usqLong getNextWakeupUsecs(void); +extern sqInt getSavedWindowSize(void); +static NoDbgRegParms usqInt getShortFromFileswap(sqImageFile aFile, sqInt swapFlag); +static sqInt getSnapshotScreenSize(void); +extern sqInt * getStackPointer(void); +extern sqInt getThisSessionID(void); +extern FILE * getTranscript(void); +static NoDbgRegParms sqInt handleForwardedSelectorFaultFor(sqInt selectorOop); +static NoDbgRegParms sqInt handleForwardedSendFaultForTag(sqInt classTag); +static NoDbgRegParms sqInt handleSpecialSelectorSendFaultForfpsp(sqInt obj, char *theFP, char *theSP); +static NeverInline void handleStackOverflow(void); +static NoDbgRegParms sqInt handleStackOverflowOrEventAllowContextSwitch(sqInt mayContextSwitch); +extern sqInt highBit(usqInt anUnsignedValue); +extern sqInt homeMethodOf(sqInt aMethodOop); +static NoDbgRegParms sqInt ifCurrentStackPageHasValidHeadPointers(StackPage *thePage); +static NoDbgRegParms usqInt iframeMethod(char *theFP); +extern sqInt includesBehaviorThatOf(sqInt aClass, sqInt aSuperclass); +extern void initStackPagesAndContinueIntowith(void (*continuation)(void *), void *argument); +extern void ioFilenamefromStringofLengthresolveAliases(char *aCharBuffer, char *aFilenameString, sqInt filenameLength, sqInt aBoolean); +static NoDbgRegParms int isBaseFrame(char *theFP); +extern sqInt isBooleanObject(sqInt oop); +static NoDbgRegParms int isEmptyList(sqInt aLinkedList); +extern int isFloatObject(sqInt oop); +static NoDbgRegParms sqInt isFrameonPage(char *aFrame, StackPage *aPage); +extern sqInt isKindOfInteger(sqInt oop); +extern sqInt isLargeIntegerObject(sqInt oop); +extern sqInt isLargeNegativeIntegerObject(sqInt oop); +extern sqInt isLargePositiveIntegerObject(sqInt oop); +static NoDbgRegParms sqInt isLinkedExternalPrimitive(sqInt methodObj); +static NoDbgRegParms sqInt isLiveContext(sqInt oop); +static NoDbgRegParms int isMarriedOrWidowedContext(sqInt aContext); +static NoDbgRegParms sqInt isMetaPrimitiveIndex(sqInt primIndex); +static NoDbgRegParms sqInt isNullExternalPrimitiveCall(sqInt aMethodObj); +static int isPrimitiveFunctionPointerAnIndex(void); +extern int isQuickPrimitiveIndex(sqInt anInteger); +extern int isReadMediatedContextInstVarIndex(sqInt index); +static NoDbgRegParms int isSingleContext(sqInt aContext); +static NoDbgRegParms sqInt isStillMarriedContext(sqInt aContext); +static NoDbgRegParms int isVanillaBlockClosure(sqInt aClosure); +static NoDbgRegParms sqInt isWidowedContextNoConvert(sqInt aOnceMarriedContext); +extern int isWriteMediatedContextInstVarIndex(sqInt index); +extern sqInt isKindOfClass(sqInt oop, sqInt aClass); +extern sqInt isKindOf(sqInt oop, char *className); +extern sqInt isMemberOf(sqInt oop, char *className); +static NoDbgRegParms sqInt lengthOfNameOfClass(sqInt classOop); +extern sqInt literalofMethod(sqInt offset, sqInt methodPointer); +extern sqInt loadBitBltFrom(sqInt bb); +extern void loadInitialContext(void); +extern void longPrintOop(sqInt oop); +extern sqInt longStoreBytecodeForHeader(sqInt methodHeader); +static NoDbgRegParms sqInt lookupInMethodCacheSelclassTag(sqInt selector, sqInt classTag); +static NoDbgRegParms sqInt lookupMethodInClass(sqInt class); +static NoDbgRegParms sqInt lookupMNUInClass(sqInt class); +static NoDbgRegParms sqInt lookupOrdinaryNoMNUEtcInClass(sqInt class); +extern sqInt lookupSelectorinClass(sqInt selector, sqInt class); +static NoDbgRegParms void makeContextSnapshotSafe(sqInt ctxt); +extern usqInt makePointwithxValueyValue(sqInt xValue, sqInt yValue); +static void mapInterpreterOops(void); +static void mapPrimTraceLog(void); +static void mapTraceLog(void); +static void markAndTracePrimTraceLog(void); +static NoDbgRegParms sqInt markAndTraceStackPages(sqInt fullGCFlag); +static void markAndTraceTraceLog(void); +static void markAndTraceUntracedReachableStackPages(void); +static NoDbgRegParms sqInt marriedContextpointsTostackDeltaForCurrentFrame(sqInt spouseContext, sqInt anOop, sqInt stackDeltaForCurrentFrame); +static NoDbgRegParms void marryContextInNewStackPageAndInitializeInterpreterRegisters(sqInt aContext); +static NoDbgRegParms sqInt maybeLeakCheckExternalPrimCall(sqInt aMethodObj); +extern sqInt maybeSelectorOfMethod(sqInt methodObj); +extern sqInt methodArgumentCount(void); +extern sqInt methodClassAssociationOf(sqInt methodPointer); +extern sqInt methodClassOf(sqInt methodPointer); +extern sqInt methodPrimitiveIndex(void); +extern sqInt methodReturnBool(sqInt boolean); +extern sqInt methodReturnFloat(double aFloat); +extern sqInt methodReturnInteger(sqInt integer); +extern sqInt methodReturnReceiver(void); +extern NeverInline sqInt methodReturnString(const char *aCString); +extern sqInt methodReturnValue(sqInt oop); +extern int methodUsesAlternateBytecodeSet(sqInt aMethodObj); +static NoDbgRegParms sqInt methodUsesPrimitiveErrorCode(sqInt aMethodObj); +EXPORT(void) moduleUnloaded(char *aModuleName); +static NoDbgRegParms char * nameOfClass(sqInt classOop); +static NoDbgRegParms char * nameOfClasslengthInto(sqInt classOop, sqInt *lengthPtr); +static NoDbgRegParms sqInt noMarkedContextsOnPage(StackPage *thePage); +static NoDbgRegParms sqInt numSlotsOfMarriedContext(sqInt aContext); +static sqInt numStkPages(void); +static NoDbgRegParms sqInt objectequalsString(sqInt anOop, char *aCString); +extern sqInt ownVM(sqInt disownResult); +extern usqInt pcPreviousToinSqueakV3PlusClosuresOrSistaV1Method(sqInt theIP, sqInt aMethod); +static NoDbgRegParms sqInt penultimateLiteralOf(sqInt aMethodOop); +extern sqInt popStack(void); +extern sqInt pop(sqInt nItems); +extern void popthenPush(sqInt nItems, sqInt oop); +static sqInt postGCUpdateDisplayBits(void); +EXPORT(void) primitiveEventProcessingControl(void); +extern void primitiveFailForFFIExceptionat(usqLong exceptionCode, usqInt pc); +static NoDbgRegParms int primitiveFloatEqualtoArg(sqInt rcvrOop, sqInt argOop); +static NoDbgRegParms int primitiveFloatGreaterOrEqualtoArg(sqInt rcvrOop, sqInt argOop); +static NoDbgRegParms int primitiveFloatGreaterthanArg(sqInt rcvrOop, sqInt argOop); +static NoDbgRegParms int primitiveFloatLessOrEqualtoArg(sqInt rcvrOop, sqInt argOop); +static NoDbgRegParms int primitiveFloatLessthanArg(sqInt rcvrOop, sqInt argOop); +extern sqInt primitiveIndexOfMethodheader(sqInt theMethod, sqInt methodHeader); +extern sqInt primitiveIndexOf(sqInt methodPointer); +extern usqInt primitiveMethod(void); +static NoDbgRegParms void printActivationNameForSelectorstartClass(sqInt aSelector, sqInt startClass); +static NoDbgRegParms void printActivationNameForreceiverisBlockfirstTemporary(sqInt aMethod, sqInt anObject, sqInt isBlock, sqInt maybeMessage); +extern void printAllStacks(void); +extern void printAllStacksOn(FILE *aStdioStream); +extern void printCallStack(void); +static NoDbgRegParms sqInt printCallStackFP(char *theFP); +extern sqInt printCallStackOf(sqInt aContextOrProcessOrFrame); +static NoDbgRegParms sqInt printCallStackOfcurrentFP(sqInt aContext, char *currFP); +extern void printCallStackOn(FILE *aStdioStream); +static NoDbgRegParms sqInt printContextCallStackOf(sqInt aContext); +extern void printContext(sqInt aContext); +static NoDbgRegParms void printDecodeMethodHeaderOop(sqInt methodHeaderOop); +extern void printExternalHeadFrame(void); +static NoDbgRegParms sqInt printFrameAndCallersSPshort(char *theFP, char *theSP, sqInt printShort); +static NoDbgRegParms void printFrameOopat(char *name, char *address); +static NoDbgRegParms void printFrameOopindexat(char *name, sqInt idx, char *address); +extern void printFramesInPage(StackPage *thePage); +extern void printFramesOnStackPageListInUse(void); +static NoDbgRegParms void printFrameThingandFrameat(char *name, char *theFP, char *address); +static NoDbgRegParms void printFrameThingatextraString(char *name, char *address, char *extraStringOrNil); +extern sqInt printFrame(char *theFP); +static NoDbgRegParms sqInt printHexPtrnp(void *p); +extern void printHex(usqInt n); +extern void printLikelyImplementorsOfSelector(sqInt selector); +static NoDbgRegParms void printLogEntryAt(sqInt i); +extern void printMethodCache(void); +extern void printMethodDictionaryOf(sqInt behavior); +extern void printMethodDictionary(sqInt dictionary); +static NoDbgRegParms void printNameOfClasscount(sqInt classOop, sqInt cnt); +static NoDbgRegParms void printNum(sqInt n); +static NoDbgRegParms void printOopShortInner(sqInt oop); +extern void printOop(sqInt oop); +static NoDbgRegParms sqInt printPrimLogEntryAthasParameter(sqInt i, sqInt hasParameter); +extern void printProcessStack(sqInt aProcess); +extern sqInt printProcsOnList(sqInt procList); +extern sqInt printStackCallStackOf(sqInt aContextOrProcessOrFrame); +extern void printStackPageList(void); +extern void printStackPageListInUse(void); +extern void printStackPages(void); +extern void printStackPagesInUse(void); +static NoDbgRegParms void printStackPageuseCount(StackPage *page, sqInt n); +extern void printStackReferencesTo(sqInt oop); +static NoDbgRegParms void printStringOf(sqInt oop); +extern void print(char *s); +extern sqInt processListForProcess(sqInt aProcess); +extern sqInt processOSErrInstVarOffset(void); +extern void pushBool(sqInt trueOrFalse); +static NoDbgRegParms sqInt pushedReceiverOrClosureOfFrame(char *theFP); +extern void pushFloat(double f); +extern sqInt pushInteger(sqInt integerValue); +extern void push(sqInt object); +static NoDbgRegParms void putLongtoFile(sqInt aLong, sqImageFile aFile); +static NoDbgRegParms void putShorttoFile(short aShort, sqImageFile aFile); +static NoDbgRegParms void putToSleepyieldingIf(sqInt aProcess, sqInt yieldImplicitly); +static NoDbgRegParms void putWord32toFile(int aWord32, sqImageFile aFile); +static NoDbgRegParms sqInt quickFetchIntegerofObject(sqInt fieldIndex, sqInt objectPointer); +extern sqInt readableFormat(sqInt imageVersion); +static NoDbgRegParms NeverInline void reapAndResetErrorCodeToheader(char *theSP, sqInt methodHeader); +extern sqInt reestablishContextPriorToCallback(sqInt callbackContext); +static NoDbgRegParms sqInt removeFirstLinkOfList(sqInt aList); +static NoDbgRegParms sqInt removeProcessfromList(sqInt aProcess, sqInt aList); +static NoDbgRegParms sqInt resumepreemptedYieldingIffrom(sqInt aProcess, sqInt yieldImplicitly, sqInt sourceCode); +static sqInt retryPrimitiveOnFailure(void); +extern sqInt returnAsThroughCallbackContext(sqInt returnTypeOop, VMCallbackContext *vmCallbackContext, sqInt callbackMethodContext); +static NoDbgRegParms void reverseDisplayFromto(sqInt startIndex, sqInt endIndex); +static NoDbgRegParms void rewriteMethodCacheEntryForExternalPrimitiveToFunction(void (*localPrimAddress)()); +static NoDbgRegParms sqInt safeMethodClassOf(sqInt methodPointer); +static NoDbgRegParms sqInt saneFunctionPointerForFailureOfPrimIndex(sqInt primIndex); +extern sqInt schedulerPointer(void); +extern void setBreakMNUSelector(char *aString); +extern void setBreakSelector(char *aString); +extern void setFullScreenFlag(sqInt aBoolean); +extern void * setInterruptCheckChain(void (*aFunction)()); +extern void setInterruptKeycode(sqInt value); +extern void setInterruptPending(sqInt value); +extern void setNextWakeupUsecs(usqLong value); +extern void setSavedWindowSize(sqInt value); +static void setSignalLowSpaceFlagAndSaveProcess(void); +static NoDbgRegParms void setTraceFlagOnContextsFramesPageIfNeeded(sqInt aContext); +static NoDbgRegParms sqInt shortPrintContext(sqInt aContext); +static NoDbgRegParms sqInt shortPrintFrameAndCallers(char *theFP); +extern void shortPrintFramesInPage(StackPage *thePage); +extern void shortPrintFramesOnStackPageListInUse(void); +extern void shortPrintFrameAndNCallers(char *theFP, sqInt n); +static NoDbgRegParms void shortPrintOop(sqInt oop); +static NoDbgRegParms sqInt shortReversePrintFrameAndCallers(char *aFramePointer); +extern sqInt showDisplayBitsLeftTopRightBottom(sqInt aForm, sqInt l, sqInt t, sqInt r, sqInt b); +static sqInt signalExternalSemaphores(void); +extern sqInt sizeOfCallPrimitiveBytecode(sqInt methodHeader); +extern sqInt sizeOfLongStoreTempBytecode(sqInt methodHeader); +extern sqInt sizeOfSTArrayFromCPrimitive(void *cPtr); +static int slowPrimitiveResponse(void); +static NoDbgRegParms sqInt snapshot(sqInt embedded); +extern sqInt specialSelector(sqInt index); +static NoDbgRegParms void spurPostBecomeAction(sqInt theBecomeEffectsFlags); +extern double stackFloatValue(sqInt offset); +extern sqInt stackIntegerValue(sqInt offset); +static sqInt stackLimitBytes(void); +extern sqInt stackMutableObjectValue(sqInt offset); +extern sqInt stackObjectValue(sqInt offset); +static sqInt stackPageByteSize(void); +static NoDbgRegParms sqInt stackPointerForMaybeMarriedContext(sqInt aContext); +static NoDbgRegParms sqInt stackPointerIndexForFrame(char *theFP); +extern usqIntptr_t stackPositiveMachineIntegerValue(sqInt offset); +extern sqIntptr_t stackSignedMachineIntegerValue(sqInt offset); +extern sqInt stackTop(void); +extern sqInt stackValue(sqInt offset); +extern usqInt startPCOfMethod(sqInt aCompiledMethod); +extern sqInt stObjectat(sqInt array, sqInt index); +extern sqInt stObjectatput(sqInt array, sqInt index, sqInt value); +static NoDbgRegParms char * storeSenderOfFramewithValue(char *theFP, sqInt anOop); +extern sqInt stSizeOf(sqInt oop); +extern sqInt superclassOf(sqInt classPointer); +static NoDbgRegParms sqInt synchronousSignal(sqInt aSemaphore); +extern usqInt tempCountOf(sqInt methodPointer); +extern usqInt temporaryCountOfMethodHeader(sqInt header); +extern sqInt ultimateLiteralOf(sqInt aMethodOop); +static NoDbgRegParms sqInt unfollowFirstLiteralOfMaybeCalloutMethodprimitiveIndex(sqInt methodObj, sqInt primIndex); +static NoDbgRegParms sqInt updateDisplayLeftTopRightBottom(sqInt l, sqInt t, sqInt r, sqInt b); +static NoDbgRegParms sqInt validBCPCinMethod(sqInt thePC, usqInt aMethod); +static NoDbgRegParms sqInt validInstructionPointerinFrame(usqInt anInstrPointer, char *fp); +static sqInt validStackPageBaseFrames(void); +#if LRPCheck +static NoDbgRegParms NeverInline void voidLongRunningPrimitive(char *reason); +#endif /* LRPCheck */ +static sqInt wakeHighestPriority(void); +static NeverInline sqInt writeImageFileIO(void); +static NoDbgRegParms usqInt cloneContext(sqInt aContext); +static NoDbgRegParms sqInt fieldOrSenderFPofContext(sqInt index, sqInt contextObj); +static NoDbgRegParms sqInt fieldofFrame(sqInt index, char *theFP); +static NoDbgRegParms sqInt isAppropriateForCopyObject(sqInt oop); +static NoDbgRegParms void (*linkExternalCallerrInto(sqInt externalCallLiteral, sqInt *failPtr))(void); +static void primitiveClone(void); +static void primitiveContextAt(void); +static void primitiveContextAtPut(void); +static void primitiveContextPreserveTemps(void); +static void primitiveContextSize(void); +static void primitiveDeferDisplayUpdates(void); +static void primitiveDoNamedPrimitiveWithArgs(void); +static void primitiveDoPrimitiveWithArgs(void); +static void primitiveExternalCall(void); +static void primitiveFindHandlerContext(void); +static void primitiveFindNextUnwindContext(void); +static void primitiveFullGC(void); +static void primitiveGetSetFFIExceptionHandling(void); +static void primitiveIncrementalGC(void); +static void primitiveInstVarAt(void); +static void primitiveInstVarAtPut(void); +static void primitiveInvokeObjectAsMethod(void); +#if LRPCheck +EXPORT(void) primitiveLongRunningPrimitive(void); +#endif /* LRPCheck */ +static void primitiveObjectPointsTo(void); +static void primitivePerform(void); +static void primitivePin(void); +EXPORT(void) primitiveSetGCSemaphore(void); +static void primitiveSignalAtMilliseconds(void); +static void primitiveSignalAtUTCMicroseconds(void); +static void primitiveSlotAt(void); +static void primitiveStoreStackp(void); +static void primitiveVMParameter(void); +EXPORT(void) primitiveVoidReceiver(void); +static NoDbgRegParms void pruneStackstackp(sqInt stack, sqInt stackp); +static void unmarkAfterPathTo(void); + +/*** Variables ***/ +#if SQ_USE_GLOBAL_STRUCT +# define _iss /* define in-struct static as void */ +static struct foo { +#else +# define _iss static +#endif +_iss char * stackLimit; +_iss char * stackPointer; +_iss char * framePointer; +_iss volatile usqIntptr_t CStackPointer; +_iss volatile usqIntptr_t CFramePointer; +_iss volatile usqIntptr_t CReturnAddress; +_iss usqInt scavengeThreshold; +_iss usqInt freeStart; +_iss sqInt needGCFlag; +_iss sqInt specialObjectsOop; +_iss sqInt primFailCode; +_iss unsigned char primTraceLogIndex; +_iss usqInt newMethod; +_iss usqInt instructionPointer; +_iss sqInt argumentCount; +_iss sqLong nextProfileTick; +_iss sqInt primTraceLog[256]; +_iss sqInt nilObj; +_iss StackPage * stackPage; +_iss usqInt oldSpaceStart; +_iss usqInt method; +_iss sqInt bytecodeSetSelector; +_iss FILE * transcript; +_iss usqInt endOfMemory; +_iss sqInt messageSelector; +_iss usqInt pastSpaceStart; +_iss usqInt newSpaceStart; +_iss sqInt trueObj; +_iss StackPage * pages; +_iss sqInt falseObj; +_iss usqInt totalFreeOldSpace; +_iss sqInt classTableFirstPage; +_iss sqInt hiddenRootsObj; +_iss SpurSegmentInfo * segments; +_iss sqInt markStack; +_iss sqInt numSegments; +_iss sqInt weaklingStack; +_iss sqInt traceLogIndex; +_iss sqInt * freeLists; +_iss sqInt rememberedSetSize; +_iss sqInt numClassTablePages; +_iss sqInt * rememberedSet; +_iss sqInt futureSurvivorStart; +_iss sqInt tempOop; +_iss usqInt firstFreeObject; +_iss usqInt freeListsMask; +_iss StackPage * mostRecentlyUsedPage; +_iss usqInt lastHash; +_iss sqInt mournQueue; +_iss char * stackBasePlus1; +_iss sqInt numRememberedEphemerons; +_iss sqInt numStackPages; +_iss usqInt totalHeapSizeIncludingBridges; +_iss sqInt classTableIndex; +_iss usqInt lastMobileObject; +_iss sqInt profileProcess; +_iss sqInt profileSemaphore; +_iss sqInt profileMethod; +_iss sqInt remapBufferCount; +_iss sqInt becomeEffectsFlags; +#if LRPCheck +_iss sqInt longRunningPrimitiveCheckSemaphore; +#endif +_iss sqInt mobileStart; +_iss sqInt performWithArgumentsRecursionGuard; +_iss sqInt growHeadroom; +_iss sqInt lkupClass; +_iss sqInt tenureCriterion; +_iss sqInt tenureThreshold; +_iss sqInt ephemeronList; +_iss sqInt gcMode; +_iss sqInt lastCoggableInterpretedBlockMethod; +_iss sqInt lastUncoggableInterpretedBlockMethod; +_iss char * objStackInvalidBecause; +_iss sqInt classNameIndex; +_iss sqInt gcPhaseInProgress; +#if LRPCheck +_iss sqInt longRunningPrimitiveCheckMethod; +#endif +_iss sqInt extraRootCount; +_iss sqInt highestRunnableProcessPriority; +_iss sqInt invalidObjStackPage; +_iss sqInt lastMethodCacheProbeWrite; +_iss sqInt metaAccessorDepth; +_iss usqInt objectAfterLastMobileObject; +_iss sqInt tempOop2; +_iss sqInt cogCompiledCodeCompactionCalledFor; +_iss usqInt freeOldSpaceStart; +_iss sqInt previousRememberedSetSize; +_iss sqInt validatedIntegerClassFlags; +_iss usqInt firstMobileObject; +_iss sqInt numSegInfos; +_iss sqInt savedFirstFieldsSpaceNotInOldSpace; +_iss usqLong statGCEndUsecs; +_iss sqInt bytesPerPage; +_iss usqInt cogCodeSize; +#if LRPCheck +_iss usqLong longRunningPrimitiveStopUsecs; +#endif +_iss usqLong nextWakeupUsecs; +_iss sqInt shrinkThreshold; +_iss sqInt statTenures; +_iss sqInt thisClassIndex; +#if LRPCheck +_iss usqLong longRunningPrimitiveStartUsecs; +#endif +_iss sqInt metaclassNumSlots; +_iss sqInt pendingFinalizationSignals; +_iss usqLong statCheckForEvents; +_iss sqInt statSurvivorCount; +_iss sqInt weakList; +_iss sqInt firstFieldOfRememberedSet; +_iss usqLong gcStartUsecs; +_iss sqInt marking; +_iss sqLong oldSpaceUsePriorToScavenge; +_iss sqInt statMaxAllocSegmentTime; +_iss sqInt statScavenges; +_iss sqInt statShrinkMemory; +_iss sqInt firstSegmentSize; +_iss sqInt multipleBytecodeSetsActive; +_iss sqInt rememberedSetLimit; +_iss FILE * scavengeLog; +_iss sqInt statFullGCs; +_iss sqInt biasForGC; +_iss sqInt deferProfileCheckForVNCS; +_iss usqInt lowSpaceThreshold; +_iss sqInt rememberedSetRedZone; +_iss sqLong secondaryErrorCode; +_iss usqLong statAllocatedBytes; +_iss sqInt statCodeCompactionCount; +_iss usqLong statCodeCompactionUsecs; +_iss usqLong statCompactionUsecs; +_iss usqLong statFullGCUsecs; +_iss sqInt statGrowMemory; +_iss usqLong statMarkUsecs; +_iss sqInt statNumMaps; +_iss usqLong statScavengeGCUsecs; +_iss usqLong statSweepUsecs; +_iss sqInt the2ndUnknownShort; +_iss sqInt anomaly; +_iss sqInt edenBytes; +_iss sqInt externalPrimitiveTableFirstFreeIndex; +_iss sqInt ffiCalloutVMHandle; +_iss sqInt freeSpaceCheckOopToIgnore; +_iss usqInt heapSizeAtPreviousGC; +_iss sqInt interruptKeycode; +_iss sqInt interruptPending; +#if LRPCheck +_iss sqInt longRunningPrimitiveCheckSequenceNumber; +#endif +_iss sqInt methodDictLinearSearchLimit; +_iss sqInt oldImageBaseAddress; +_iss sqInt savedWindowSize; +_iss usqLong statForceInterruptCheck; +_iss usqLong statIOProcessEvents; +_iss usqLong statIdleUsecs; +_iss sqInt statMarkCount; +_iss sqInt statMaxPageCountWhenMapping; +_iss sqInt statPageCountWhenMappingSum; +_iss usqLong statProcessSwitch; +_iss sqInt statRootTableCount; +_iss usqLong statStackOverflow; +_iss usqLong statStackPageDivorce; +_iss sqInt canSwizzle; +_iss sqInt classLinkedListClassTag; +_iss sqInt deferSmash; +_iss sqInt deferredSmash; +_iss sqInt extraFramesToMoveOnOverflow; +_iss sqInt globalSessionID; +#if LRPCheck +_iss sqInt longRunningPrimitiveSignalUndelivered; +#endif +_iss sqInt maxExtSemTabSizeSet; +_iss sqInt primCalloutIsExternal; +_iss sqInt refCountToShrinkRT; +_iss sqInt signalLowSpace; +_iss sqInt statCompactPassCount; +_iss usqLong statIGCDeltaUsecs; +_iss sqInt statPendingFinalizationSignals; +_iss sqInt statRootTableOverflows; +_iss usqLong statSGCDeltaUsecs; +_iss sqInt bogon; +_iss sqInt classByteArrayCompactIndex; +_iss usqInt exceptionPC; +_iss sqInt gcSemaphoreIndex; +_iss usqLong gcSweepEndUsecs; +#if LRPCheck +_iss usqLong longRunningPrimitiveGCUsecs; +#endif +_iss usqLong nextPollUsecs; +_iss sqInt overflowLimit; +_iss StackPage * overflowedPage; +_iss sqInt printSends; +_iss sqInt tenuringClassIndex; +_iss sqInt statIncrGCs; +_iss sqIntptr_t methodCache[MethodCacheSize + 1 /* 4097 */]; +_iss sqInt traceLog[TraceBufferSize /* 768 */]; +_iss sqInt remapBuffer[RemapBufferSize + 1 /* 26 */]; +_iss sqInt *extraRoots[ExtraRootsSize + 1 /* 65 */]; +_iss usqLong byteCount; +_iss sqInt typesCollector; +_iss SpurContiguousObjStack unscannedEphemerons; +_iss SpurContiguousObjStack savedFirstFieldsSpace; +_iss SpurNewSpaceSpace pastSpace; +_iss SpurScavengeLogRecord scavengeLogRecord; +_iss SpurNewSpaceSpace futureSpace; +_iss SpurNewSpaceSpace eden; +_iss float heapGrowthToSizeGCRatio; +_iss double tenuringProportion; +#undef _iss +#if SQ_USE_GLOBAL_STRUCT + } fum; +# if SQ_USE_GLOBAL_STRUCT_REG +# define DECL_MAYBE_SQ_GLOBAL_STRUCT /* using a global reg pointer */ +# define DECL_MAYBE_VOLATILE_SQ_GLOBAL_STRUCT /* using a global reg pointer */ +# else +# define DECL_MAYBE_SQ_GLOBAL_STRUCT register struct foo * foo = &fum; +# define DECL_MAYBE_VOLATILE_SQ_GLOBAL_STRUCT volatile register struct foo * foo = &fum; +# endif +# define GIV(interpreterInstVar) (foo->interpreterInstVar) +#else +# define DECL_MAYBE_SQ_GLOBAL_STRUCT /* oh, no mr bill! */ +# define DECL_MAYBE_VOLATILE_SQ_GLOBAL_STRUCT /* oh no, mr bill! */ +# define GIV(interpreterInstVar) interpreterInstVar +#endif +#if SQ_USE_GLOBAL_STRUCT +# if SQ_USE_GLOBAL_STRUCT_REG +# define fooxstr(s) foostr(s) +# define foostr(s) #s +register struct foo * foo asm(fooxstr(USE_GLOBAL_STRUCT_REG)); +# else +static struct foo * foo = &fum; +# endif +#endif +static void (*primitiveFunctionPointer)(); +static void *primitiveCalloutPointer = (void *)-1; +static usqInt (* const pcPreviousToFunction)(sqInt,sqInt) = pcPreviousToinSqueakV3PlusClosuresOrSistaV1Method; +static void (*interruptCheckChain)(void) = 0; +static int (*sHEAFn)() = 0; +static void (*primitiveTable[MaxPrimitiveIndex + 2 /* 585 */])(void) = { + /* 0 */ (void (*)(void))0, + /* 1 */ primitiveAdd, + /* 2 */ primitiveSubtract, + /* 3 */ primitiveLessThan, + /* 4 */ primitiveGreaterThan, + /* 5 */ primitiveLessOrEqual, + /* 6 */ primitiveGreaterOrEqual, + /* 7 */ primitiveEqual, + /* 8 */ primitiveNotEqual, + /* 9 */ primitiveMultiply, + /* 10 */ primitiveDivide, + /* 11 */ primitiveMod, + /* 12 */ primitiveDiv, + /* 13 */ primitiveQuo, + /* 14 */ primitiveBitAnd, + /* 15 */ primitiveBitOr, + /* 16 */ primitiveBitXor, + /* 17 */ primitiveBitShift, + /* 18 */ primitiveMakePoint, + /* 19 */ (void (*)(void))0, + /* 20 */ primitiveRemLargeIntegers, + /* 21 */ primitiveAddLargeIntegers, + /* 22 */ primitiveSubtractLargeIntegers, + /* 23 */ primitiveLessThanLargeIntegers, + /* 24 */ primitiveGreaterThanLargeIntegers, + /* 25 */ primitiveLessOrEqualLargeIntegers, + /* 26 */ primitiveGreaterOrEqualLargeIntegers, + /* 27 */ primitiveEqualLargeIntegers, + /* 28 */ primitiveNotEqualLargeIntegers, + /* 29 */ primitiveMultiplyLargeIntegers, + /* 30 */ primitiveDivideLargeIntegers, + /* 31 */ primitiveModLargeIntegers, + /* 32 */ primitiveDivLargeIntegers, + /* 33 */ primitiveQuoLargeIntegers, + /* 34 */ primitiveBitAndLargeIntegers, + /* 35 */ primitiveBitOrLargeIntegers, + /* 36 */ primitiveBitXorLargeIntegers, + /* 37 */ primitiveBitShiftLargeIntegers, + /* 38 */ primitiveFloatAt, + /* 39 */ primitiveFloatAtPut, + /* 40 */ primitiveAsFloat, + /* 41 */ primitiveFloatAdd, + /* 42 */ primitiveFloatSubtract, + /* 43 */ primitiveFloatLessThan, + /* 44 */ primitiveFloatGreaterThan, + /* 45 */ primitiveFloatLessOrEqual, + /* 46 */ primitiveFloatGreaterOrEqual, + /* 47 */ primitiveFloatEqual, + /* 48 */ primitiveFloatNotEqual, + /* 49 */ primitiveFloatMultiply, + /* 50 */ primitiveFloatDivide, + /* 51 */ primitiveTruncated, + /* 52 */ primitiveFractionalPart, + /* 53 */ primitiveExponent, + /* 54 */ primitiveTimesTwoPower, + /* 55 */ primitiveSquareRoot, + /* 56 */ primitiveSine, + /* 57 */ primitiveArctan, + /* 58 */ primitiveLogN, + /* 59 */ primitiveExp, + /* 60 */ primitiveAt, + /* 61 */ primitiveAtPut, + /* 62 */ primitiveSize, + /* 63 */ primitiveStringAt, + /* 64 */ primitiveStringAtPut, + /* 65 */ (void (*)(void))0, + /* 66 */ (void (*)(void))0, + /* 67 */ (void (*)(void))0, + /* 68 */ primitiveObjectAt, + /* 69 */ primitiveObjectAtPut, + /* 70 */ primitiveNew, + /* 71 */ primitiveNewWithArg, + /* 72 */ primitiveArrayBecomeOneWay, + /* 73 */ primitiveInstVarAt, + /* 74 */ primitiveInstVarAtPut, + /* 75 */ primitiveIdentityHash, + /* 76 */ primitiveStoreStackp, + /* 77 */ primitiveSomeInstance, + /* 78 */ primitiveNextInstance, + /* 79 */ primitiveNewMethod, + /* 80 */ (void (*)(void))0, + /* 81 */ (void (*)(void))0, + /* 82 */ (void (*)(void))0, + /* 83 */ primitivePerform, + /* 84 */ primitivePerformWithArgs, + /* 85 */ primitiveSignal, + /* 86 */ primitiveWait, + /* 87 */ primitiveResume, + /* 88 */ primitiveSuspend, + /* 89 */ primitiveFlushCache, + /* 90 */ (void (*)(void))0, + /* 91 */ primitiveTestDisplayDepth, + /* 92 */ primitiveSetDisplayMode, + /* 93 */ primitiveInputSemaphore, + /* 94 */ primitiveGetNextEvent, + /* 95 */ primitiveInputWord, + /* 96 */ primitiveObjectsAccessibleFromRoots, + /* 97 */ primitiveSnapshot, + /* 98 */ primitiveStoreImageSegment, + /* 99 */ primitiveLoadImageSegment, + /* 100 */ primitivePerformInSuperclass, + /* 101 */ primitiveBeCursor, + /* 102 */ primitiveBeDisplay, + /* 103 */ primitiveScanCharacters, + /* 104 */ (void (*)(void))0, + /* 105 */ primitiveStringReplace, + /* 106 */ primitiveScreenSize, + /* 107 */ (void (*)(void))0, + /* 108 */ (void (*)(void))0, + /* 109 */ (void (*)(void))0, + /* 110 */ primitiveIdentical, + /* 111 */ primitiveClass, + /* 112 */ primitiveBytesLeft, + /* 113 */ primitiveQuit, + /* 114 */ primitiveExitToDebugger, + /* 115 */ primitiveChangeClass, + /* 116 */ primitiveFlushCacheByMethod, + /* 117 */ primitiveExternalCall, + /* 118 */ primitiveDoPrimitiveWithArgs, + /* 119 */ primitiveFlushCacheBySelector, + /* 120 */ primitiveCalloutToFFI, + /* 121 */ primitiveImageName, + /* 122 */ primitiveNoop, + /* 123 */ (void (*)(void))0, + /* 124 */ primitiveLowSpaceSemaphore, + /* 125 */ primitiveSignalAtBytesLeft, + /* 126 */ primitiveDeferDisplayUpdates, + /* 127 */ primitiveShowDisplayRect, + /* 128 */ primitiveArrayBecome, + /* 129 */ primitiveSpecialObjectsOop, + /* 130 */ primitiveFullGC, + /* 131 */ primitiveIncrementalGC, + /* 132 */ primitiveObjectPointsTo, + /* 133 */ primitiveSetInterruptKey, + /* 134 */ primitiveInterruptSemaphore, + /* 135 */ primitiveMillisecondClock, + /* 136 */ primitiveSignalAtMilliseconds, + /* 137 */ primitiveSecondsClock, + /* 138 */ primitiveSomeObject, + /* 139 */ primitiveNextObject, + /* 140 */ primitiveBeep, + /* 141 */ primitiveClipboardText, + /* 142 */ primitiveVMPath, + /* 143 */ primitiveShortAt, + /* 144 */ primitiveShortAtPut, + /* 145 */ primitiveConstantFill, + /* 146 */ (void (*)(void))0, + /* 147 */ (void (*)(void))0, + /* 148 */ primitiveClone, + /* 149 */ primitiveGetAttribute, + /* 150 */ primitiveCosine, + /* 151 */ (void (*)(void))0, + /* 152 */ (void (*)(void))0, + /* 153 */ (void (*)(void))0, + /* 154 */ (void (*)(void))0, + /* 155 */ (void (*)(void))0, + /* 156 */ (void (*)(void))0, + /* 157 */ (void (*)(void))0, + /* 158 */ primitiveCompareWith, + /* 159 */ primitiveHashMultiply, + /* 160 */ primitiveAdoptInstance, + /* 161 */ primitiveSetOrHasIdentityHash, + /* 162 */ (void (*)(void))0, + /* 163 */ primitiveGetImmutability, + /* 164 */ primitiveSetImmutability, + /* 165 */ primitiveIntegerAt, + /* 166 */ primitiveIntegerAtPut, + /* 167 */ primitiveYield, + /* 168 */ primitiveCopyObject, + /* 169 */ primitiveNotIdentical, + /* 170 */ primitiveAsCharacter, + /* 171 */ primitiveImmediateAsInteger, + /* 172 */ primitiveFetchNextMourner, + /* 173 */ primitiveSlotAt, + /* 174 */ primitiveSlotAtPut, + /* 175 */ primitiveBehaviorHash, + /* 176 */ primitiveMaxIdentityHash, + /* 177 */ primitiveAllInstances, + /* 178 */ primitiveAllObjects, + /* 179 */ (void (*)(void))0, + /* 180 */ primitiveGrowMemoryByAtLeast, + /* 181 */ primitiveSizeInBytesOfInstance, + /* 182 */ primitiveSizeInBytes, + /* 183 */ primitiveIsPinned, + /* 184 */ primitivePin, + /* 185 */ primitiveExitCriticalSection, + /* 186 */ primitiveEnterCriticalSection, + /* 187 */ primitiveTestAndSetOwnershipOfCriticalSection, + /* 188 */ primitiveExecuteMethodArgsArray, + /* 189 */ primitiveExecuteMethod, + /* 190 */ (void (*)(void))0, + /* 191 */ (void (*)(void))0, + /* 192 */ (void (*)(void))0, + /* 193 */ (void (*)(void))0, + /* 194 */ (void (*)(void))0, + /* 195 */ primitiveFindNextUnwindContext, + /* 196 */ primitiveTerminateTo, + /* 197 */ primitiveFindHandlerContext, + /* 198 */ (void (*)(void))0, + /* 199 */ (void (*)(void))0, + /* 200 */ primitiveClosureCopyWithCopiedValues, + /* 201 */ primitiveClosureValue, + /* 202 */ primitiveClosureValue, + /* 203 */ primitiveClosureValue, + /* 204 */ primitiveClosureValue, + /* 205 */ primitiveClosureValue, + /* 206 */ primitiveClosureValueWithArgs, + /* 207 */ primitiveFullClosureValue, + /* 208 */ primitiveFullClosureValueWithArgs, + /* 209 */ primitiveFullClosureValueNoContextSwitch, + /* 210 */ primitiveContextAt, + /* 211 */ primitiveContextAtPut, + /* 212 */ primitiveContextSize, + /* 213 */ primitiveContextPreserveTemps, + /* 214 */ primitiveVoidVMState, + /* 215 */ primitiveVoidVMStateForMethod, + /* 216 */ primitiveMethodXray, + /* 217 */ (void (*)(void))0, + /* 218 */ primitiveDoNamedPrimitiveWithArgs, + /* 219 */ (void (*)(void))0, + /* 220 */ primitiveGetSetFFIExceptionHandling, + /* 221 */ primitiveClosureValueNoContextSwitch, + /* 222 */ primitiveClosureValueNoContextSwitch, + /* 223 */ (void (*)(void))0, + /* 224 */ (void (*)(void))0, + /* 225 */ (void (*)(void))0, + /* 226 */ (void (*)(void))0, + /* 227 */ (void (*)(void))0, + /* 228 */ (void (*)(void))0, + /* 229 */ (void (*)(void))0, + /* 230 */ primitiveRelinquishProcessor, + /* 231 */ primitiveForceDisplayUpdate, + /* 232 */ primitiveFormPrint, + /* 233 */ primitiveSetFullScreen, + /* 234 */ (void (*)(void))0, + /* 235 */ (void (*)(void))0, + /* 236 */ (void (*)(void))0, + /* 237 */ (void (*)(void))0, + /* 238 */ primitiveFloatArrayAt, + /* 239 */ primitiveFloatArrayAtPut, + /* 240 */ primitiveUTCMicrosecondClock, + /* 241 */ primitiveLocalMicrosecondClock, + /* 242 */ primitiveSignalAtUTCMicroseconds, + /* 243 */ primitiveUpdateTimezone, + /* 244 */ primitiveUtcAndTimezoneOffset, + /* 245 */ primitiveCoarseUTCMicrosecondClock, + /* 246 */ primitiveCoarseLocalMicrosecondClock, + /* 247 */ primitiveSnapshotEmbedded, + /* 248 */ primitiveArrayBecomeOneWayNoCopyHash, + /* 249 */ primitiveArrayBecomeOneWayCopyHashArg, + /* 250 */ primitiveClearVMProfile, + /* 251 */ primitiveControlVMProfiling, + /* 252 */ primitiveVMProfileSamplesInto, + /* 253 */ primitiveCollectCogCodeConstituents, + /* 254 */ primitiveVMParameter, + /* 255 */ (void (*)(void))0, + /* 256 */ (void (*)(void))256, + /* 257 */ (void (*)(void))257, + /* 258 */ (void (*)(void))258, + /* 259 */ (void (*)(void))259, + /* 260 */ (void (*)(void))260, + /* 261 */ (void (*)(void))261, + /* 262 */ (void (*)(void))262, + /* 263 */ (void (*)(void))263, + /* 264 */ (void (*)(void))264, + /* 265 */ (void (*)(void))265, + /* 266 */ (void (*)(void))266, + /* 267 */ (void (*)(void))267, + /* 268 */ (void (*)(void))268, + /* 269 */ (void (*)(void))269, + /* 270 */ (void (*)(void))270, + /* 271 */ (void (*)(void))271, + /* 272 */ (void (*)(void))272, + /* 273 */ (void (*)(void))273, + /* 274 */ (void (*)(void))274, + /* 275 */ (void (*)(void))275, + /* 276 */ (void (*)(void))276, + /* 277 */ (void (*)(void))277, + /* 278 */ (void (*)(void))278, + /* 279 */ (void (*)(void))279, + /* 280 */ (void (*)(void))280, + /* 281 */ (void (*)(void))281, + /* 282 */ (void (*)(void))282, + /* 283 */ (void (*)(void))283, + /* 284 */ (void (*)(void))284, + /* 285 */ (void (*)(void))285, + /* 286 */ (void (*)(void))286, + /* 287 */ (void (*)(void))287, + /* 288 */ (void (*)(void))288, + /* 289 */ (void (*)(void))289, + /* 290 */ (void (*)(void))290, + /* 291 */ (void (*)(void))291, + /* 292 */ (void (*)(void))292, + /* 293 */ (void (*)(void))293, + /* 294 */ (void (*)(void))294, + /* 295 */ (void (*)(void))295, + /* 296 */ (void (*)(void))296, + /* 297 */ (void (*)(void))297, + /* 298 */ (void (*)(void))298, + /* 299 */ (void (*)(void))299, + /* 300 */ (void (*)(void))300, + /* 301 */ (void (*)(void))301, + /* 302 */ (void (*)(void))302, + /* 303 */ (void (*)(void))303, + /* 304 */ (void (*)(void))304, + /* 305 */ (void (*)(void))305, + /* 306 */ (void (*)(void))306, + /* 307 */ (void (*)(void))307, + /* 308 */ (void (*)(void))308, + /* 309 */ (void (*)(void))309, + /* 310 */ (void (*)(void))310, + /* 311 */ (void (*)(void))311, + /* 312 */ (void (*)(void))312, + /* 313 */ (void (*)(void))313, + /* 314 */ (void (*)(void))314, + /* 315 */ (void (*)(void))315, + /* 316 */ (void (*)(void))316, + /* 317 */ (void (*)(void))317, + /* 318 */ (void (*)(void))318, + /* 319 */ (void (*)(void))319, + /* 320 */ (void (*)(void))320, + /* 321 */ (void (*)(void))321, + /* 322 */ (void (*)(void))322, + /* 323 */ (void (*)(void))323, + /* 324 */ (void (*)(void))324, + /* 325 */ (void (*)(void))325, + /* 326 */ (void (*)(void))326, + /* 327 */ (void (*)(void))327, + /* 328 */ (void (*)(void))328, + /* 329 */ (void (*)(void))329, + /* 330 */ (void (*)(void))330, + /* 331 */ (void (*)(void))331, + /* 332 */ (void (*)(void))332, + /* 333 */ (void (*)(void))333, + /* 334 */ (void (*)(void))334, + /* 335 */ (void (*)(void))335, + /* 336 */ (void (*)(void))336, + /* 337 */ (void (*)(void))337, + /* 338 */ (void (*)(void))338, + /* 339 */ (void (*)(void))339, + /* 340 */ (void (*)(void))340, + /* 341 */ (void (*)(void))341, + /* 342 */ (void (*)(void))342, + /* 343 */ (void (*)(void))343, + /* 344 */ (void (*)(void))344, + /* 345 */ (void (*)(void))345, + /* 346 */ (void (*)(void))346, + /* 347 */ (void (*)(void))347, + /* 348 */ (void (*)(void))348, + /* 349 */ (void (*)(void))349, + /* 350 */ (void (*)(void))350, + /* 351 */ (void (*)(void))351, + /* 352 */ (void (*)(void))352, + /* 353 */ (void (*)(void))353, + /* 354 */ (void (*)(void))354, + /* 355 */ (void (*)(void))355, + /* 356 */ (void (*)(void))356, + /* 357 */ (void (*)(void))357, + /* 358 */ (void (*)(void))358, + /* 359 */ (void (*)(void))359, + /* 360 */ (void (*)(void))360, + /* 361 */ (void (*)(void))361, + /* 362 */ (void (*)(void))362, + /* 363 */ (void (*)(void))363, + /* 364 */ (void (*)(void))364, + /* 365 */ (void (*)(void))365, + /* 366 */ (void (*)(void))366, + /* 367 */ (void (*)(void))367, + /* 368 */ (void (*)(void))368, + /* 369 */ (void (*)(void))369, + /* 370 */ (void (*)(void))370, + /* 371 */ (void (*)(void))371, + /* 372 */ (void (*)(void))372, + /* 373 */ (void (*)(void))373, + /* 374 */ (void (*)(void))374, + /* 375 */ (void (*)(void))375, + /* 376 */ (void (*)(void))376, + /* 377 */ (void (*)(void))377, + /* 378 */ (void (*)(void))378, + /* 379 */ (void (*)(void))379, + /* 380 */ (void (*)(void))380, + /* 381 */ (void (*)(void))381, + /* 382 */ (void (*)(void))382, + /* 383 */ (void (*)(void))383, + /* 384 */ (void (*)(void))384, + /* 385 */ (void (*)(void))385, + /* 386 */ (void (*)(void))386, + /* 387 */ (void (*)(void))387, + /* 388 */ (void (*)(void))388, + /* 389 */ (void (*)(void))389, + /* 390 */ (void (*)(void))390, + /* 391 */ (void (*)(void))391, + /* 392 */ (void (*)(void))392, + /* 393 */ (void (*)(void))393, + /* 394 */ (void (*)(void))394, + /* 395 */ (void (*)(void))395, + /* 396 */ (void (*)(void))396, + /* 397 */ (void (*)(void))397, + /* 398 */ (void (*)(void))398, + /* 399 */ (void (*)(void))399, + /* 400 */ (void (*)(void))400, + /* 401 */ (void (*)(void))401, + /* 402 */ (void (*)(void))402, + /* 403 */ (void (*)(void))403, + /* 404 */ (void (*)(void))404, + /* 405 */ (void (*)(void))405, + /* 406 */ (void (*)(void))406, + /* 407 */ (void (*)(void))407, + /* 408 */ (void (*)(void))408, + /* 409 */ (void (*)(void))409, + /* 410 */ (void (*)(void))410, + /* 411 */ (void (*)(void))411, + /* 412 */ (void (*)(void))412, + /* 413 */ (void (*)(void))413, + /* 414 */ (void (*)(void))414, + /* 415 */ (void (*)(void))415, + /* 416 */ (void (*)(void))416, + /* 417 */ (void (*)(void))417, + /* 418 */ (void (*)(void))418, + /* 419 */ (void (*)(void))419, + /* 420 */ (void (*)(void))420, + /* 421 */ (void (*)(void))421, + /* 422 */ (void (*)(void))422, + /* 423 */ (void (*)(void))423, + /* 424 */ (void (*)(void))424, + /* 425 */ (void (*)(void))425, + /* 426 */ (void (*)(void))426, + /* 427 */ (void (*)(void))427, + /* 428 */ (void (*)(void))428, + /* 429 */ (void (*)(void))429, + /* 430 */ (void (*)(void))430, + /* 431 */ (void (*)(void))431, + /* 432 */ (void (*)(void))432, + /* 433 */ (void (*)(void))433, + /* 434 */ (void (*)(void))434, + /* 435 */ (void (*)(void))435, + /* 436 */ (void (*)(void))436, + /* 437 */ (void (*)(void))437, + /* 438 */ (void (*)(void))438, + /* 439 */ (void (*)(void))439, + /* 440 */ (void (*)(void))440, + /* 441 */ (void (*)(void))441, + /* 442 */ (void (*)(void))442, + /* 443 */ (void (*)(void))443, + /* 444 */ (void (*)(void))444, + /* 445 */ (void (*)(void))445, + /* 446 */ (void (*)(void))446, + /* 447 */ (void (*)(void))447, + /* 448 */ (void (*)(void))448, + /* 449 */ (void (*)(void))449, + /* 450 */ (void (*)(void))450, + /* 451 */ (void (*)(void))451, + /* 452 */ (void (*)(void))452, + /* 453 */ (void (*)(void))453, + /* 454 */ (void (*)(void))454, + /* 455 */ (void (*)(void))455, + /* 456 */ (void (*)(void))456, + /* 457 */ (void (*)(void))457, + /* 458 */ (void (*)(void))458, + /* 459 */ (void (*)(void))459, + /* 460 */ (void (*)(void))460, + /* 461 */ (void (*)(void))461, + /* 462 */ (void (*)(void))462, + /* 463 */ (void (*)(void))463, + /* 464 */ (void (*)(void))464, + /* 465 */ (void (*)(void))465, + /* 466 */ (void (*)(void))466, + /* 467 */ (void (*)(void))467, + /* 468 */ (void (*)(void))468, + /* 469 */ (void (*)(void))469, + /* 470 */ (void (*)(void))470, + /* 471 */ (void (*)(void))471, + /* 472 */ (void (*)(void))472, + /* 473 */ (void (*)(void))473, + /* 474 */ (void (*)(void))474, + /* 475 */ (void (*)(void))475, + /* 476 */ (void (*)(void))476, + /* 477 */ (void (*)(void))477, + /* 478 */ (void (*)(void))478, + /* 479 */ (void (*)(void))479, + /* 480 */ (void (*)(void))480, + /* 481 */ (void (*)(void))481, + /* 482 */ (void (*)(void))482, + /* 483 */ (void (*)(void))483, + /* 484 */ (void (*)(void))484, + /* 485 */ (void (*)(void))485, + /* 486 */ (void (*)(void))486, + /* 487 */ (void (*)(void))487, + /* 488 */ (void (*)(void))488, + /* 489 */ (void (*)(void))489, + /* 490 */ (void (*)(void))490, + /* 491 */ (void (*)(void))491, + /* 492 */ (void (*)(void))492, + /* 493 */ (void (*)(void))493, + /* 494 */ (void (*)(void))494, + /* 495 */ (void (*)(void))495, + /* 496 */ (void (*)(void))496, + /* 497 */ (void (*)(void))497, + /* 498 */ (void (*)(void))498, + /* 499 */ (void (*)(void))499, + /* 500 */ (void (*)(void))500, + /* 501 */ (void (*)(void))501, + /* 502 */ (void (*)(void))502, + /* 503 */ (void (*)(void))503, + /* 504 */ (void (*)(void))504, + /* 505 */ (void (*)(void))505, + /* 506 */ (void (*)(void))506, + /* 507 */ (void (*)(void))507, + /* 508 */ (void (*)(void))508, + /* 509 */ (void (*)(void))509, + /* 510 */ (void (*)(void))510, + /* 511 */ (void (*)(void))511, + /* 512 */ (void (*)(void))512, + /* 513 */ (void (*)(void))513, + /* 514 */ (void (*)(void))514, + /* 515 */ (void (*)(void))515, + /* 516 */ (void (*)(void))516, + /* 517 */ (void (*)(void))517, + /* 518 */ (void (*)(void))518, + /* 519 */ (void (*)(void))519, + /* 520 */ (void (*)(void))0, + /* 521 */ (void (*)(void))0, + /* 522 */ (void (*)(void))0, + /* 523 */ (void (*)(void))0, + /* 524 */ (void (*)(void))0, + /* 525 */ (void (*)(void))0, + /* 526 */ (void (*)(void))0, + /* 527 */ (void (*)(void))0, + /* 528 */ (void (*)(void))0, + /* 529 */ (void (*)(void))0, + /* 530 */ (void (*)(void))0, + /* 531 */ (void (*)(void))0, + /* 532 */ (void (*)(void))0, + /* 533 */ (void (*)(void))0, + /* 534 */ (void (*)(void))0, + /* 535 */ (void (*)(void))0, + /* 536 */ (void (*)(void))0, + /* 537 */ (void (*)(void))0, + /* 538 */ (void (*)(void))0, + /* 539 */ (void (*)(void))0, + /* 540 */ (void (*)(void))0, + /* 541 */ primitiveSmallFloatAdd, + /* 542 */ primitiveSmallFloatSubtract, + /* 543 */ primitiveSmallFloatLessThan, + /* 544 */ primitiveSmallFloatGreaterThan, + /* 545 */ primitiveSmallFloatLessOrEqual, + /* 546 */ primitiveSmallFloatGreaterOrEqual, + /* 547 */ primitiveSmallFloatEqual, + /* 548 */ primitiveSmallFloatNotEqual, + /* 549 */ primitiveSmallFloatMultiply, + /* 550 */ primitiveSmallFloatDivide, + /* 551 */ primitiveSmallFloatTruncated, + /* 552 */ primitiveSmallFloatFractionalPart, + /* 553 */ primitiveSmallFloatExponent, + /* 554 */ primitiveSmallFloatTimesTwoPower, + /* 555 */ primitiveSmallFloatSquareRoot, + /* 556 */ primitiveSmallFloatSine, + /* 557 */ primitiveSmallFloatArctan, + /* 558 */ primitiveSmallFloatLogN, + /* 559 */ primitiveSmallFloatExp, + /* 560 */ primitiveSmallFloatCosine, + /* 561 */ (void (*)(void))0, + /* 562 */ (void (*)(void))0, + /* 563 */ (void (*)(void))0, + /* 564 */ (void (*)(void))0, + /* 565 */ (void (*)(void))0, + /* 566 */ (void (*)(void))0, + /* 567 */ primitiveSetPriority, + /* 568 */ primitiveSuspendBackingUpV1, + /* 569 */ (void (*)(void))0, + /* 570 */ primitiveFlushExternalPrimitives, + /* 571 */ primitiveUnloadModule, + /* 572 */ primitiveListBuiltinModule, + /* 573 */ primitiveListExternalModule, + /* 574 */ (void (*)(void))0, + /* 575 */ primitiveHighBit, + /* 576 */ (void (*)(void))0, + /* 577 */ (void (*)(void))0, + /* 578 */ primitiveSuspendBackingUpV2, + /* 579 */ (void (*)(void))0, + /* 580 */ primitivePinnedNew, + /* 581 */ primitivePinnedNewWithArg, + /* 582 */ primitiveUninitializedNewWithArg, + /* 583 */ primitiveUninitializedPinnedNewWithArg, + 0 }; +static void (*externalPrimitiveTable[MaxExternalPrimitiveTableSize + 1 /* 4097 */])(void); +static signed short primitiveMetadataTable[MaxPrimitiveIndex + 2 /* 585 */] = { +// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 +/*0*/ -256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-256, +/*20*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x101, 0x101, +/*40*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 3, 3, 3, 3, +/*60*/ 1, 1, 0, 1, 1,-256,-256,-256, 0x100, 0x100, 0, 0, 0, 268, 0x100, 0, 0x100, 0, +/*78*/ 0, 0, +/*80*/ -256,-256,-256, 4, 4, 0, 0x100, 0, 0x200,-256,-256, 0, 0, 0, 0x100,-256, 0,-256, +/*98*/ 0, 0, +/*100*/ 260, 0x200, 0x200, 0x200,-256, 513,-256,-256,-256,-256, 0, 0x100, 0, 0,-256, +/*115*/ 0x100, 0, 12, 260, 0, +/*120*/ 524, 0x100,-256,-256, 1, 0, 0, 0, 0,-255,-256, 0, 0, 0, 0,-256, 0,-256,-256, +/*139*/ 0, +/*140*/ -256, 0x100,-256, 0x101, 0x101, 0x101,-256,-256, 12, 0, 3,-256,-256,-256,-256, +/*155*/ -256,-256,-256, 0x100, 0x100, +/*160*/ 0, 0,-256, 1, 1, 0x100, 0x100,-256, 0x101, 0, 0, 0,-256, 268, 0x100, 0,-256, +/*177*/ 0,-256,-256, +/*180*/ 0, 1, 1, 1, 0x100, 0x100, 0x100, 0x100, 0x200, 0x100,-256,-256,-256,-256,-256, +/*195*/ 0x100, 0x200, 0,-256,-256, +/*200*/ -256, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x100, 0x100, 0x100, 0x100, 0x100, +/*212*/ 0x100, 0,-256, 0, 0,-256, 772,-256, +/*220*/ 0, 0x200, 0x200,-256,-256,-256,-256,-256,-256,-256, 0,-256, 0x200, 0,-256,-256, +/*236*/ -256,-256,-253,-253, +/*240*/ -256,-256, 0,-256, 0x100,-256,-256,-256, 0, 0,-256, 0, 0x100, 0, 0,-256,-256, +/*257*/ -256,-256,-256, +/*260*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*276*/ -256,-256,-256,-256, +/*280*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*296*/ -256,-256,-256,-256, +/*300*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*316*/ -256,-256,-256,-256, +/*320*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*336*/ -256,-256,-256,-256, +/*340*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*356*/ -256,-256,-256,-256, +/*360*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*376*/ -256,-256,-256,-256, +/*380*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*396*/ -256,-256,-256,-256, +/*400*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*416*/ -256,-256,-256,-256, +/*420*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*436*/ -256,-256,-256,-256, +/*440*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*456*/ -256,-256,-256,-256, +/*460*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*476*/ -256,-256,-256,-256, +/*480*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*496*/ -256,-256,-256,-256, +/*500*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*516*/ -256,-256,-256,-256, +/*520*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*536*/ -256,-256,-256,-256, +/*540*/ -256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 1, 3, 3, 3, 3, 3, 3, +/*560*/ 3,-256,-256,-256,-256,-256,-256, 0x100, 0x200,-256,-244, 0x100, 0, 0,-256, 1, +/*576*/ -256,-256, 0x200,-256, +/*580*/ 0, 0, 0, 0 + }; +static char * traceSources[] = { + "?", "enterCritical", "exitCritical", "resume", "syncsig", "suspend", "wait", + "yield", "priority" + }; +sqInt imageHeaderFlags; +sqInt checkForLeaks; +sqInt maxLiteralCountForCompile = MaxLiteralCountForCompile /* 60 */; +sqInt debugCallbackPath; +sqInt breakLookupClassTag; +char * primTracePluginName; +sqInt breakSelectorLength = MinSmallInteger; +void * displayBits; +sqInt desiredEdenBytes; +sqInt desiredNumStackPages; +sqInt extraVMMemory; +sqInt eventTraceMask; +usqInt maxOldSpaceSize; +sqInt desiredCogCodeSize; +char * breakSelector; +sqInt ffiExceptionResponse; +usqInt heapBase; +sqInt inIOProcessEvents; +int (*showSurfaceFn)(sqIntptr_t, int, int, int, int); +sqInt deferDisplayUpdates; +struct VirtualMachine* interpreterProxy; +sqInt debugCallbackInvokes; +sqInt debugCallbackReturns; +sqInt suppressHeartbeatFlag; +sqInt cannotDeferDisplayUpdates; +sqInt checkedPluginName; +const char *interpreterVersion = "Open Smalltalk Cog[Spur] VM [LiveTypingCoInterpreter * VMMaker.oscog-eem.3776]"; +const char * leakCheckFlagsMeanings[] = { + "1: check full GC", "2: check new space GC", "4: check incremental GC", "8: check become", + "16: check image segment", "32: check free space", "64: check shorten", "128: check prim call", + null + }; +sqInt minBackwardJumpCountForCompile = MinBackwardJumpCountForCompile /* 40 */; +int displayWidth; +int displayDepth; +int displayHeight; +char expensiveAsserts = 0; +volatile int sendTrace; + + +/*** Macros ***/ +#define cPICNumCases stackCheckOffset +#define cPICNumCasesHack hack hack hack i.e. the getter macro does all the work +#define nextOpenPIC methodObject +#define nextOpenPICHack hack hack hack i.e. the getter macro does all the work +#define mostRecentlyUsedPage() GIV(mostRecentlyUsedPage) +#define stackPageAtpages(index,pages) ((pages) + (index)) +#define statMaxPageCountWhenMapping() GIV(statMaxPageCountWhenMapping) +#define compilationBreakpointclassTagisMNUCase(sel, classTag, isMNU) do { \ + if (numBytesOf(sel) == (isMNU ? -breakSelectorLength : breakSelectorLength) \ + && !strncmp((char *)((sel) + BaseHeaderSize), breakSelector, (isMNU ? -breakSelectorLength : breakSelectorLength))) { \ + suppressHeartbeatFlag = 1; \ + compilationBreakpointFor(sel); \ + } \ +} while (0) +#define compilationBreakpointisMNUCase(sel, isMNU) do { \ + if (numBytesOf(sel) == (isMNU ? -breakSelectorLength : breakSelectorLength) \ + && !strncmp((char *)((sel) + BaseHeaderSize), breakSelector, (isMNU ? -breakSelectorLength : breakSelectorLength))) { \ + suppressHeartbeatFlag = 1; \ + compilationBreakpointFor(sel); \ + } \ +} while (0) +#define flagInterpretedMethods() (imageHeaderFlags & 8) +#define getDesiredCogCodeSize() integerObjectOf(desiredCogCodeSize) +#define heapBase() heapBase +#define pageIndexForstackBasePlus1bytesPerPage(pointer,stkBasePlus1,pageByteSize) (((char *)(pointer) - (stkBasePlus1)) / (pageByteSize)) +#define nullHeaderForMachineCodeMethod() 0x8000000A000035LL +#define startOfMemory() heapBase +#define numTagBits() 3 +#define shiftForWord() 3 +#define smallFloatExponentBits() 8 +#define smallFloatExponentOffset() 896 +#define smallFloatMantissaBits() 52 +#define smallFloatTag() 4 +#define tagMask() 0x7 +#define wordSize() 8 +#define arrayClassIndexPun() 16 +#define arrayFormat() 2 +#define characterTag() 2 +#define classIndexMask() 0x3FFFFF +#define classIsItselfClassIndexPun() 31 +#define classTableMajorIndexShift() 10 +#define ephemeronFormat() 5 +#define extraAlignedBitByteShift() 6 +#define extraAlignedBitShift() 22 +#define firstByteFormat() 16 +#define firstClassIndexPun() 16 +#define firstCompiledMethodFormat() 24 +#define firstLongFormat() 10 +#define firstShortFormat() 12 +#define fixedFieldsFieldWidth() 16 +#define formatFieldByteOffset() 3 +#define formatFieldWidthShift() 5 +#define formatMask() 0x1F +#define formatShift() 24 +#define forwardedFormat() 7 +#define greyBitByteShift() 6 +#define greyBitShift() 54 +#define identityHashFullWordShift() 32 +#define identityHashHalfWordMask() 0x3FFFFF +#define immutableBitByteShift() 7 +#define immutableBitShift() 23 +#define immutableExtraBitsByteOffset() 2 +#define indexablePointersFormat() 3 +#define isForwardedObjectClassIndexPun() 8 +#define isFreeObjectClassIndexPun() 0 +#define lastClassIndexPun() 31 +#define markBitsByteOffset() 6 +#define markedBitByteShift() 7 +#define markedBitFullShift() 55 +#define markedBitHalfShift() 23 +#define nonIndexablePointerFormat() 1 +#define numSlotsFieldByteOffset() 7 +#define numSlotsFullShift() 56 +#define numSlotsHalfShift() 24 +#define numSlotsMask() 0xFF +#define pinnedBitByteShift() 6 +#define pinnedBitShift() 30 +#define rememberedBitByteShift() 5 +#define rememberedBitShift() 29 +#define rootTableCapacity() GIV(rememberedSetLimit) +#define rootTableCount() GIV(rememberedSetSize) +#define segmentBridgePun() 3 +#define sixtyFourBitIndexableFormat() 9 +#define sixtyFourBitLongsClassIndexPun() 19 +#define smallIntegerTag() 1 +#define thirtyTwoBitLongsClassIndexPun() 18 +#define unusedBitByteShift() 7 +#define unusedBitShift() 31 +#define weakArrayClassIndexPun() 17 +#define weakArrayFormat() 4 +#define alternateHeaderNumLiteralsMask() 0x7FFF +#define cr() fputc('\n',transcript) +#define dispatchFunctionPointer(aFunctionPointer) (aFunctionPointer)() +#define enterSmalltalkExecutive() enterSmalltalkExecutiveImplementation() +#define initialEnterSmalltalkExecutive() enterSmalltalkExecutiveImplementation() +#define numericPrimsMixArithmetic() (imageHeaderFlags & 0x100) +#define numericPrimsMixComparison() (imageHeaderFlags & 0x800) +#define primTraceLogIndex(aValue) (GIV(primTraceLogIndex) = (aValue)) +#define printChar(aCharacter) fputc(aCharacter,transcript) +#define remoteIsInstVarAccess() 128 +#define stackPageFrameBytes() (256 * BytesPerWord) +#define allocatype(numElements, elementType) alloca((numElements)*sizeof(elementType)) +#define numElementsIn(anArray) (sizeof(anArray)/sizeof(anArray[0])) +#define oopisGreaterThanOrEqualTo(anOop,otherOop) ((usqInt)(anOop) >= (usqInt)(otherOop)) +#define oopisGreaterThanOrEqualToandLessThanOrEqualTo(anOop,baseOop,limitOop) ((usqInt)(anOop) >= (usqInt)(baseOop) && (usqInt)(anOop) <= (usqInt)(limitOop)) +#define oopisGreaterThanOrEqualToandLessThan(anOop,baseOop,limitOop) ((usqInt)(anOop) >= (usqInt)(baseOop) && (usqInt)(anOop) < (usqInt)(limitOop)) +#define oopisGreaterThan(anOop,otherOop) ((usqInt)(anOop) > (usqInt)(otherOop)) +#define oopisGreaterThanandLessThan(anOop,baseOop,limitOop) ((usqInt)(anOop) > (usqInt)(baseOop) && (usqInt)(anOop) < (usqInt)(limitOop)) +#define oopisLessThanOrEqualTo(anOop,otherOop) ((usqInt)(anOop) <= (usqInt)(otherOop)) +#define oopisLessThan(anOop,otherOop) ((usqInt)(anOop) < (usqInt)(otherOop)) + + +/*** Methods ***/ + + +/* This is the main interpreter loop. + In a pure interpreter it loops forever, fetching and executing bytecodes. + With the Cogit JIT executing code as well, the interpreter is reentered + from machine code + whenever the machine code wants to interpret a method instead of executing + its machine + code. Entry into the interpreter is done via a ''jump call'' in machine + code that uses + CFramePointer and CStackPointer to find the base of the C stack (set in + CoInterpreter>> enterSmalltalkExecutiveImplementation) and substitutes + CReturnAddress as the return + address in the code so it always appears that interpret has been called + from CoInterpreter>>enterSmalltalkExecutiveImplementation, which may be + important to, + for example, C exception handling inside the VM. + + When running in the context of a browser plugin VM the interpreter must + return control + to the browser periodically. This should done only when the state of the + currently running + Squeak thread is safely stored in the object heap. Since this is the case + at the moment + that a check for interrupts is performed, that is when we return to the + browser if it is time + to do so. Interrupt checks happen quite frequently. */ +/* If stacklimit is zero then the stack pages have not been initialized. */ + + /* CoInterpreter>>#interpret */ +sqInt +interpret(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + register sqInt currentBytecode CB_REG; + sqInt extA; + sqInt extB; + sqInt header; + sqInt lkupClassTag; + register char *localFP FP_REG; + register char *localIP IP_REG; + sqInt localReturnValue; + register char *localSP SP_REG; + sqInt methodHeader; + sqInt numExtB; + sqInt stackPageBytes; + usqIntptr_t stackZoneBytes; + void *theStackMemory; + JUMP_TABLE; + + if (!asserta((sizeof(jumpTable)/sizeof(jumpTable[0])) >= 512)) + error("bytecode jumpTable too small"); + + if (!GIV(stackLimit)) { + /* begin initStackPagesAndInterpret */ + /* begin ensureInitializeStackZone */ + if (!GIV(pages)) { + stackPageBytes = stackPageByteSize(); + stackZoneBytes = (GIV(numStackPages) * ((sizeof(CogStackPage)) + (stackPageByteSize()))) + BytesPerWord; + theStackMemory = alloca(stackZoneBytes); + memset(theStackMemory, 0, stackZoneBytes); + initializeStacknumSlotspageSize(theStackMemory, stackZoneBytes / BytesPerWord, stackPageBytes / BytesPerWord); + } + + /* Once the stack pages are initialized we can continue to bootstrap the system. */ + loadInitialContext(); + + /* We're ready for the heartbeat (poll interrupt) */ + ioInitHeartbeat(); + initialEnterSmalltalkExecutive(); + return null; + } + assertCStackWellAligned(); + + /* An unchecked write is probably faster, so instead of + CReturnAddress ifNil: + [CReturnAddress := self cCoerceSimple: self getReturnAddress to: #usqIntptr_t] + we have simply */ + assert((!GIV(CReturnAddress)) + || (GIV(CReturnAddress) == (((usqIntptr_t) (getReturnAddress()))))); + GIV(CReturnAddress) = ((usqIntptr_t) (getReturnAddress())); + + /* record entry time when running as a browser plug-in */ + browserPluginInitialiseIfNeeded(); + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + assert(!((GIV(instructionPointer) == (ceReturnToInterpreterPC())))); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 1, __LINE__); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + + /* begin initExtensions */ + extA = (numExtB = (extB = 0)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + while (1) { + /* begin dispatchBytecode */ + bytecodeDispatchDebugHook(); + + VM_LABEL(bytecodeDispatch); + switch (currentBytecode) { + CASE(0) // pushReceiverVariableBytecode + CASE(256) // 0 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize))); + } + BREAK; + CASE(1) // pushReceiverVariableBytecode + CASE(257) // 1 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode1); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 8 /* (1 << shiftForWord) */))); + } + BREAK; + CASE(2) // pushReceiverVariableBytecode + CASE(258) // 2 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode2); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 16 /* (2 << shiftForWord) */))); + } + BREAK; + CASE(3) // pushReceiverVariableBytecode + CASE(259) // 3 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode3); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 24 /* (3 << shiftForWord) */))); + } + BREAK; + CASE(4) // pushReceiverVariableBytecode + CASE(260) // 4 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode4); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 32 /* (4 << shiftForWord) */))); + } + BREAK; + CASE(5) // pushReceiverVariableBytecode + CASE(261) // 5 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode5); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 40 /* (5 << shiftForWord) */))); + } + BREAK; + CASE(6) // pushReceiverVariableBytecode + CASE(262) // 6 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode6); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 48 /* (6 << shiftForWord) */))); + } + BREAK; + CASE(7) // pushReceiverVariableBytecode + CASE(263) // 7 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode7); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 56 /* (7 << shiftForWord) */))); + } + BREAK; + CASE(8) // pushReceiverVariableBytecode + CASE(264) // 8 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode8); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 64 /* (8 << shiftForWord) */))); + } + BREAK; + CASE(9) // pushReceiverVariableBytecode + CASE(265) // 9 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode9); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 72 /* (9 << shiftForWord) */))); + } + BREAK; + CASE(10) // pushReceiverVariableBytecode + CASE(266) // 10 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode10); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 80 /* (10 << shiftForWord) */))); + } + BREAK; + CASE(11) // pushReceiverVariableBytecode + CASE(267) // 11 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode11); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 88 /* (11 << shiftForWord) */))); + } + BREAK; + CASE(12) // pushReceiverVariableBytecode + CASE(268) // 12 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode12); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 96 /* (12 << shiftForWord) */))); + } + BREAK; + CASE(13) // pushReceiverVariableBytecode + CASE(269) // 13 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode13); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 104 /* (13 << shiftForWord) */))); + } + BREAK; + CASE(14) // pushReceiverVariableBytecode + CASE(270) // 14 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode14); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 112 /* (14 << shiftForWord) */))); + } + BREAK; + CASE(15) // pushReceiverVariableBytecode + CASE(271) // 15 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode15); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 120 /* (15 << shiftForWord) */))); + } + BREAK; + CASE(16) // pushTemporaryVariableBytecode + CASE(320) // 64 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (0 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + (frameNumArgs * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + (frameNumArgs * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(17) // pushTemporaryVariableBytecode + CASE(321) // 65 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode1); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (1 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 1) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 1) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(18) // pushTemporaryVariableBytecode + CASE(322) // 66 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode2); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (2 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 2) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 2) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(19) // pushTemporaryVariableBytecode + CASE(323) // 67 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode3); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (3 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 3) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 3) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(20) // pushTemporaryVariableBytecode + CASE(324) // 68 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode4); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (4 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 4) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 4) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(21) // pushTemporaryVariableBytecode + CASE(325) // 69 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode5); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (5 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 5) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 5) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(22) // pushTemporaryVariableBytecode + CASE(326) // 70 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode6); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (6 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 6) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 6) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(23) // pushTemporaryVariableBytecode + CASE(327) // 71 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode7); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (7 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 7) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 7) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(24) // pushTemporaryVariableBytecode + CASE(328) // 72 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode8); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (8 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 8) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 8) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(25) // pushTemporaryVariableBytecode + CASE(329) // 73 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode9); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (9 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 9) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 9) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(26) // pushTemporaryVariableBytecode + CASE(330) // 74 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode10); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (10 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 10) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 10) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(27) // pushTemporaryVariableBytecode + CASE(331) // 75 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode11); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (11 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 11) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 11) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(28) // pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode12); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (12 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 12) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 12) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(29) // pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode13); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (13 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 13) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 13) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(30) // pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode14); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (14 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 14) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 14) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(31) // pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode15); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (15 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 15) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 15) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(32) // pushLiteralConstantBytecode + CASE(288) // 32 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 8 /* ((0 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(33) // pushLiteralConstantBytecode + CASE(289) // 33 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode1); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 16 /* ((1 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(34) // pushLiteralConstantBytecode + CASE(290) // 34 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode2); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 24 /* ((2 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(35) // pushLiteralConstantBytecode + CASE(291) // 35 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode3); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 32 /* ((3 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(36) // pushLiteralConstantBytecode + CASE(292) // 36 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode4); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 40 /* ((4 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(37) // pushLiteralConstantBytecode + CASE(293) // 37 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode5); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 48 /* ((5 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(38) // pushLiteralConstantBytecode + CASE(294) // 38 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode6); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 56 /* ((6 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(39) // pushLiteralConstantBytecode + CASE(295) // 39 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode7); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 64 /* ((7 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(40) // pushLiteralConstantBytecode + CASE(296) // 40 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode8); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 72 /* ((8 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(41) // pushLiteralConstantBytecode + CASE(297) // 41 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode9); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 80 /* ((9 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(42) // pushLiteralConstantBytecode + CASE(298) // 42 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode10); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 88 /* ((10 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(43) // pushLiteralConstantBytecode + CASE(299) // 43 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode11); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 96 /* ((11 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(44) // pushLiteralConstantBytecode + CASE(300) // 44 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode12); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 104 /* ((12 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(45) // pushLiteralConstantBytecode + CASE(301) // 45 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode13); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 112 /* ((13 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(46) // pushLiteralConstantBytecode + CASE(302) // 46 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode14); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 120 /* ((14 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(47) // pushLiteralConstantBytecode + CASE(303) // 47 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode15); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 128 /* ((15 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(48) // pushLiteralConstantBytecode + CASE(304) // 48 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode16); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 136 /* ((16 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(49) // pushLiteralConstantBytecode + CASE(305) // 49 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode17); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 144 /* ((17 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(50) // pushLiteralConstantBytecode + CASE(306) // 50 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode18); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 152 /* ((18 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(51) // pushLiteralConstantBytecode + CASE(307) // 51 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode19); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 160 /* ((19 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(52) // pushLiteralConstantBytecode + CASE(308) // 52 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode20); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 168 /* ((20 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(53) // pushLiteralConstantBytecode + CASE(309) // 53 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode21); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 176 /* ((21 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(54) // pushLiteralConstantBytecode + CASE(310) // 54 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode22); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 184 /* ((22 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(55) // pushLiteralConstantBytecode + CASE(311) // 55 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode23); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 192 /* ((23 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(56) // pushLiteralConstantBytecode + CASE(312) // 56 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode24); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 200 /* ((24 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(57) // pushLiteralConstantBytecode + CASE(313) // 57 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode25); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 208 /* ((25 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(58) // pushLiteralConstantBytecode + CASE(314) // 58 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode26); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 216 /* ((26 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(59) // pushLiteralConstantBytecode + CASE(315) // 59 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode27); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 224 /* ((27 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(60) // pushLiteralConstantBytecode + CASE(316) // 60 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode28); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 232 /* ((28 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(61) // pushLiteralConstantBytecode + CASE(317) // 61 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode29); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 240 /* ((29 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(62) // pushLiteralConstantBytecode + CASE(318) // 62 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode30); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 0xF8 /* ((30 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(63) // pushLiteralConstantBytecode + CASE(319) // 63 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode31); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 0x100 /* ((31 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(64) // pushLiteralVariableBytecode + CASE(272) // 16 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 8 /* ((0 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(1 /* (0 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(65) // pushLiteralVariableBytecode + CASE(273) // 17 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode1); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 16 /* ((1 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(2 /* (1 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(66) // pushLiteralVariableBytecode + CASE(274) // 18 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode2); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 24 /* ((2 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(3 /* (2 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(67) // pushLiteralVariableBytecode + CASE(275) // 19 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode3); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 32 /* ((3 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(4 /* (3 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(68) // pushLiteralVariableBytecode + CASE(276) // 20 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode4); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 40 /* ((4 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(5 /* (4 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(69) // pushLiteralVariableBytecode + CASE(277) // 21 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode5); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 48 /* ((5 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(6 /* (5 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(70) // pushLiteralVariableBytecode + CASE(278) // 22 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode6); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 56 /* ((6 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(7 /* (6 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(71) // pushLiteralVariableBytecode + CASE(279) // 23 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode7); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 64 /* ((7 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(8 /* (7 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(72) // pushLiteralVariableBytecode + CASE(280) // 24 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode8); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 72 /* ((8 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(9 /* (8 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(73) // pushLiteralVariableBytecode + CASE(281) // 25 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode9); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 80 /* ((9 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(10 /* (9 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(74) // pushLiteralVariableBytecode + CASE(282) // 26 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode10); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 88 /* ((10 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(11 /* (10 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(75) // pushLiteralVariableBytecode + CASE(283) // 27 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode11); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 96 /* ((11 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(12 /* (11 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(76) // pushLiteralVariableBytecode + CASE(284) // 28 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode12); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 104 /* ((12 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(13 /* (12 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(77) // pushLiteralVariableBytecode + CASE(285) // 29 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode13); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 112 /* ((13 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(14 /* (13 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(78) // pushLiteralVariableBytecode + CASE(286) // 30 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode14); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 120 /* ((14 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(15 /* (14 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(79) // pushLiteralVariableBytecode + CASE(287) // 31 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode15); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 128 /* ((15 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(16 /* (15 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(80) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode16); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 136 /* ((16 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(17 /* (16 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(81) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode17); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 144 /* ((17 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(18 /* (17 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(82) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode18); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 152 /* ((18 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(19 /* (18 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(83) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode19); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 160 /* ((19 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(20 /* (19 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(84) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode20); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 168 /* ((20 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(21 /* (20 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(85) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode21); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 176 /* ((21 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(22 /* (21 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(86) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode22); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 184 /* ((22 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(23 /* (22 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(87) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode23); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 192 /* ((23 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(24 /* (23 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(88) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode24); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 200 /* ((24 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(25 /* (24 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(89) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode25); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 208 /* ((25 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(26 /* (25 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(90) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode26); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 216 /* ((26 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(27 /* (26 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(91) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode27); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 224 /* ((27 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(28 /* (27 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(92) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode28); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 232 /* ((28 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(29 /* (28 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(93) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode29); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 240 /* ((29 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(30 /* (29 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(94) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode30); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 0xF8 /* ((30 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(0x1F /* (30 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(95) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode31); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 0x100 /* ((31 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(32 /* (31 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(96) // storeAndPopReceiverVariableBytecode + CASE(97) // storeAndPopReceiverVariableBytecode + CASE(98) // storeAndPopReceiverVariableBytecode + CASE(99) // storeAndPopReceiverVariableBytecode + CASE(100) // storeAndPopReceiverVariableBytecode + CASE(101) // storeAndPopReceiverVariableBytecode + CASE(102) // storeAndPopReceiverVariableBytecode + CASE(103) // storeAndPopReceiverVariableBytecode + CASE(456) // 200 storeAndPopReceiverVariableBytecode + CASE(457) // 201 storeAndPopReceiverVariableBytecode + CASE(458) // 202 storeAndPopReceiverVariableBytecode + CASE(459) // 203 storeAndPopReceiverVariableBytecode + CASE(460) // 204 storeAndPopReceiverVariableBytecode + CASE(461) // 205 storeAndPopReceiverVariableBytecode + CASE(462) // 206 storeAndPopReceiverVariableBytecode + CASE(463) // 207 storeAndPopReceiverVariableBytecode + { + sqInt fmt; + sqInt index; + usqInt instVarIndex; + sqInt instVarTypesArrays; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt rcvr; + sqInt rcvrClass; + sqInt tagBits; + sqInt top; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + VM_LABEL(storeAndPopReceiverVariableBytecode); + /* begin collectInstanceVariableTypeFor:at:of: */ + rcvrClass = /* fetchClassOf: */ + ((tagBits = (longAt(localFP + FoxIFReceiver)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(localFP + FoxIFReceiver))); + instVarTypesArrays = /* #instVarTypesArraysOf: #safeFollowObjField:ofObject: */ + (InstVarTypesArraysIndex < (lengthOf(rcvrClass)) + ? followObjFieldofObject(InstVarTypesArraysIndex, rcvrClass) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (instVarTypesArrays) + && (isInstanceOfClassArray(instVarTypesArrays)))) { + goto l3; + } + typesArray = /* safeFollowObjField:ofObject: */ + ((currentBytecode & 7) < (lengthOf(instVarTypesArrays)) + ? followObjFieldofObject(currentBytecode & 7, instVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l4; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l4; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l4; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l4; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l4: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2: + /* end collectTypeOf:in:at: */ +l3: + rcvr = longAt(localFP + FoxIFReceiver); + top = longAt(localSP); + instVarIndex = currentBytecode & 7; + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; +# if IMMUTABILITY + assert(!((isImmediate(rcvr)))); + if ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),rcvr); + longAtput((localSP -= BytesPerOop),top); + longAtput((localSP -= BytesPerOop),(((instVarIndex + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l1; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(instVarIndex, rcvr, top)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(top & (tagMask())))) + && (oopisLessThan(top, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((instVarIndex << (shiftForWord())))),top); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l1: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(104) // storeAndPopTemporaryVariableBytecode + CASE(464) // 208 storeAndPopTemporaryVariableBytecode + { + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + VM_LABEL(storeAndPopTemporaryVariableBytecode); + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 3)) != 0))) { + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l152; + } + typesArray = /* safeFollowObjField:ofObject: */ + (0 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(0, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l154; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << 3 /* shiftForWord */))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l153; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << 3 /* shiftForWord */)) - (fmt & 7); + goto l153; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << 2 /* (shiftForWord - 1) */)) - (fmt & 3); + goto l153; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << 1 /* (shiftForWord - 2) */)) - (fmt & 1); + goto l153; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l153; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l153: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l154; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */)))),objectClass); + goto l154; + } + } + /* end collectTypeOf:in: */ +l154: + /* end collectTypeOf:in:at: */ +l152:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (0 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + (frameNumArgs * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + (frameNumArgs * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(105) // storeAndPopTemporaryVariableBytecode + CASE(465) // 209 storeAndPopTemporaryVariableBytecode + { + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + VM_LABEL(storeAndPopTemporaryVariableBytecode1); + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 3)) != 0))) { + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l155; + } + typesArray = /* safeFollowObjField:ofObject: */ + (1 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(1, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l157; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << 3 /* shiftForWord */))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l156; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << 3 /* shiftForWord */)) - (fmt & 7); + goto l156; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << 2 /* (shiftForWord - 1) */)) - (fmt & 3); + goto l156; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << 1 /* (shiftForWord - 2) */)) - (fmt & 1); + goto l156; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l156; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l156: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l157; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */)))),objectClass); + goto l157; + } + } + /* end collectTypeOf:in: */ +l157: + /* end collectTypeOf:in:at: */ +l155:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (1 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 1) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 1) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(106) // storeAndPopTemporaryVariableBytecode + CASE(466) // 210 storeAndPopTemporaryVariableBytecode + { + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + VM_LABEL(storeAndPopTemporaryVariableBytecode2); + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 3)) != 0))) { + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l158; + } + typesArray = /* safeFollowObjField:ofObject: */ + (2 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(2, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l160; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << 3 /* shiftForWord */))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l159; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << 3 /* shiftForWord */)) - (fmt & 7); + goto l159; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << 2 /* (shiftForWord - 1) */)) - (fmt & 3); + goto l159; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << 1 /* (shiftForWord - 2) */)) - (fmt & 1); + goto l159; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l159; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l159: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l160; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */)))),objectClass); + goto l160; + } + } + /* end collectTypeOf:in: */ +l160: + /* end collectTypeOf:in:at: */ +l158:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (2 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 2) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 2) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(107) // storeAndPopTemporaryVariableBytecode + CASE(467) // 211 storeAndPopTemporaryVariableBytecode + { + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + VM_LABEL(storeAndPopTemporaryVariableBytecode3); + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 3)) != 0))) { + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l161; + } + typesArray = /* safeFollowObjField:ofObject: */ + (3 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(3, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l163; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << 3 /* shiftForWord */))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l162; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << 3 /* shiftForWord */)) - (fmt & 7); + goto l162; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << 2 /* (shiftForWord - 1) */)) - (fmt & 3); + goto l162; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << 1 /* (shiftForWord - 2) */)) - (fmt & 1); + goto l162; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l162; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l162: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l163; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */)))),objectClass); + goto l163; + } + } + /* end collectTypeOf:in: */ +l163: + /* end collectTypeOf:in:at: */ +l161:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (3 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 3) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 3) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(108) // storeAndPopTemporaryVariableBytecode + CASE(468) // 212 storeAndPopTemporaryVariableBytecode + { + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + VM_LABEL(storeAndPopTemporaryVariableBytecode4); + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 3)) != 0))) { + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l164; + } + typesArray = /* safeFollowObjField:ofObject: */ + (4 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(4, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l166; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << 3 /* shiftForWord */))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l165; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << 3 /* shiftForWord */)) - (fmt & 7); + goto l165; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << 2 /* (shiftForWord - 1) */)) - (fmt & 3); + goto l165; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << 1 /* (shiftForWord - 2) */)) - (fmt & 1); + goto l165; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l165; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l165: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l166; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */)))),objectClass); + goto l166; + } + } + /* end collectTypeOf:in: */ +l166: + /* end collectTypeOf:in:at: */ +l164:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (4 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 4) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 4) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(109) // storeAndPopTemporaryVariableBytecode + CASE(469) // 213 storeAndPopTemporaryVariableBytecode + { + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + VM_LABEL(storeAndPopTemporaryVariableBytecode5); + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 3)) != 0))) { + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l167; + } + typesArray = /* safeFollowObjField:ofObject: */ + (5 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(5, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l169; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << 3 /* shiftForWord */))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l168; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << 3 /* shiftForWord */)) - (fmt & 7); + goto l168; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << 2 /* (shiftForWord - 1) */)) - (fmt & 3); + goto l168; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << 1 /* (shiftForWord - 2) */)) - (fmt & 1); + goto l168; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l168; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l168: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l169; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */)))),objectClass); + goto l169; + } + } + /* end collectTypeOf:in: */ +l169: + /* end collectTypeOf:in:at: */ +l167:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (5 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 5) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 5) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(110) // storeAndPopTemporaryVariableBytecode + CASE(470) // 214 storeAndPopTemporaryVariableBytecode + { + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + VM_LABEL(storeAndPopTemporaryVariableBytecode6); + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 3)) != 0))) { + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l170; + } + typesArray = /* safeFollowObjField:ofObject: */ + (6 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(6, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l172; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << 3 /* shiftForWord */))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l171; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << 3 /* shiftForWord */)) - (fmt & 7); + goto l171; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << 2 /* (shiftForWord - 1) */)) - (fmt & 3); + goto l171; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << 1 /* (shiftForWord - 2) */)) - (fmt & 1); + goto l171; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l171; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l171: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l172; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */)))),objectClass); + goto l172; + } + } + /* end collectTypeOf:in: */ +l172: + /* end collectTypeOf:in:at: */ +l170:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (6 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 6) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 6) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(111) // storeAndPopTemporaryVariableBytecode + CASE(471) // 215 storeAndPopTemporaryVariableBytecode + { + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + VM_LABEL(storeAndPopTemporaryVariableBytecode7); + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 3)) != 0))) { + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l173; + } + typesArray = /* safeFollowObjField:ofObject: */ + (7 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(7, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l175; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << 3 /* shiftForWord */))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l174; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << 3 /* shiftForWord */)) - (fmt & 7); + goto l174; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << 2 /* (shiftForWord - 1) */)) - (fmt & 3); + goto l174; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << 1 /* (shiftForWord - 2) */)) - (fmt & 1); + goto l174; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l174; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l174: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l175; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << 3 /* shiftForWord */)))),objectClass); + goto l175; + } + } + /* end collectTypeOf:in: */ +l175: + /* end collectTypeOf:in:at: */ +l173:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (7 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 7) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 7) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(112) // pushReceiverBytecode + CASE(332) // 76 pushReceiverBytecode + { + VM_LABEL(pushReceiverBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt(localFP + FoxIFReceiver)); + } + BREAK; + CASE(113) // pushConstantTrueBytecode + CASE(333) // 77 pushConstantTrueBytecode + { + VM_LABEL(pushConstantTrueBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),GIV(trueObj)); + } + BREAK; + CASE(114) // pushConstantFalseBytecode + CASE(334) // 78 pushConstantFalseBytecode + { + VM_LABEL(pushConstantFalseBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),GIV(falseObj)); + } + BREAK; + CASE(115) // pushConstantNilBytecode + CASE(335) // 79 pushConstantNilBytecode + { + VM_LABEL(pushConstantNilBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),GIV(nilObj)); + } + BREAK; + CASE(116) // pushConstantMinusOneBytecode + { + VM_LABEL(pushConstantMinusOneBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),ConstMinusOne); + } + BREAK; + CASE(117) // pushConstantZeroBytecode + CASE(336) // 80 pushConstantZeroBytecode + { + VM_LABEL(pushConstantZeroBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),ConstZero); + } + BREAK; + CASE(118) // pushConstantOneBytecode + CASE(337) // 81 pushConstantOneBytecode + { + VM_LABEL(pushConstantOneBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),ConstOne); + } + BREAK; + CASE(119) // pushConstantTwoBytecode + { + VM_LABEL(pushConstantTwoBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),ConstTwo); + } + BREAK; + CASE(120) // returnReceiver + { + VM_LABEL(returnReceiver); + localReturnValue = longAt(localFP + FoxIFReceiver); + + /* goto commonReturn */ + } + + commonReturn: + /* commonReturn */ + { + sqInt additionalMethodState; + sqInt aMethodObj; + char *callerFP; + sqInt closure; + sqInt contextToReturnTo; + sqInt currentCtx; + sqInt fmt; + sqInt frameMethod; + char *frameToReturnTo; + sqInt header; + sqInt home; + sqInt index; + sqInt methodHeader; + StackPage *newPage; + sqInt nextCntx; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt ourContext; + sqInt returnTypesArray; + sqInt selectorOrProperties; + sqInt senderOop; + sqInt tagBits; + char *theFP; + char *theFP1; + StackPage *thePage; + StackPage *thePage1; + sqInt typeAtIndex; + sqInt typesArraySize; + sqInt unwindContextOrNilOrZero; + + VM_LABEL(commonReturn); + frameToReturnTo = ((char *) 0); + theFP = ((char *) 0); + + /* begin collectReturnTypeForFrame:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + returnTypesArray = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (ReturnTypesArrayIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(ReturnTypesArrayIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (returnTypesArray) + && (isInstanceOfClassArray(returnTypesArray)))) { + goto l177; + } + objectClass = /* fetchClassOf: */ + ((tagBits = localReturnValue & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(localReturnValue)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(returnTypesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(returnTypesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(returnTypesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l181; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l181; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l181; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l181; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l181; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l181: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((returnTypesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, returnTypesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l177; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, returnTypesArray, objectClass)); + assert(isNonImmediate(returnTypesArray)); + if (oopisGreaterThanOrEqualTo(returnTypesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(returnTypesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(returnTypesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((returnTypesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l177; + } + } + /* end collectTypeOf:in: */ +l177: + if (!(byteAt((localFP + FoxIFrameFlags) + 3))) { + goto commonCallerReturn; + goto l176; + } + + /* Update the current page's headFrame pointers to enable the search for unwind protects below + to identify widowed contexts correctly. */ + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* Since this is a block activation the closure is on the stack above any args and the frame. */ + closure = longAt(localFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(localFP))->cmNumArgs) + : byteAt((localFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))); + + /* avoid compiler warning */ + /* Walk the closure's lexical chain to find the context or frame to return from (home). + If home is missing (Sista closures) then throw cannotReturn rather than crash. */ + home = null; + while (closure != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + home = longAt((void *)((closure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + assert(isNonImmediate(home)); + if ((!((longAt((void *)(home))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + home = fixFollowedFieldofObjectwithInitialValue(ClosureOuterContextIndex, closure, home); + } + if (!(/* isContext: */ + ((!(home & (tagMask())))) + && (((longAt((void *)(home))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* begin internalCannotReturn: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l179; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l179: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),ourContext); + longAtput((localSP -= BytesPerOop),localReturnValue); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))); + GIV(argumentCount) = 1; + goto normalSend; + goto l176; + } + + /* begin followObjField:ofObject: */ + closure = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord())))))); + assert(isNonImmediate(closure)); + if ((!((longAt((void *)(closure))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + closure = fixFollowedFieldofObjectwithInitialValue(ClosureIndex, home, closure); + } + } + + /* home is to be returned from provided there is no unwind-protect activation between + this frame and home's sender. Search for an unwind. findUnwindThroughContext: + will answer either the context for an unwind-protect activation or nilObj if the sender + cannot be found or 0 if no unwind is found but the sender is. */ + unwindContextOrNilOrZero = findUnwindThroughContext(home); + if (unwindContextOrNilOrZero == GIV(nilObj)) { + /* error: can't find home on chain; cannot return */ + + /* begin internalCannotReturn: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l182; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l182: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),ourContext); + longAtput((localSP -= BytesPerOop),localReturnValue); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))); + GIV(argumentCount) = 1; + goto normalSend; + goto l176; + } + if (unwindContextOrNilOrZero) { + /* begin internalAboutToReturn:through: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l183; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l183: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),ourContext); + longAtput((localSP -= BytesPerOop),localReturnValue); + longAtput((localSP -= BytesPerOop),unwindContextOrNilOrZero); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAboutToReturn) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l176; + } + + /* Now we know home is on the sender chain. + We could be returning to either a context or a frame. Find out which. */ + contextToReturnTo = null; + if (((((longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert(checkIsStillMarriedContextcurrentFP(home, localFP)); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (longAt(theFP + FoxSavedFP)) { + frameToReturnTo = ((char *)(longAt(theFP + FoxSavedFP))); + } + else { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + contextToReturnTo = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(contextToReturnTo)); + assert((contextToReturnTo == (nilObject())) + || (isContext(followMaybeForwarded(contextToReturnTo)))); + } + } + else { + contextToReturnTo = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + if ((/* isContext: */ + ((!(contextToReturnTo & (tagMask())))) + && (((longAt((void *)(contextToReturnTo))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + assert(checkIsStillMarriedContextcurrentFP(contextToReturnTo, localFP)); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + frameToReturnTo = ((char *)(senderOop - (smallIntegerTag()))); + contextToReturnTo = null; + } + } + + /* If returning to a context we must make a frame for it unless it is dead. */ + if (contextToReturnTo) { + frameToReturnTo = establishFrameForContextToReturnTo(contextToReturnTo); + if (!frameToReturnTo) { + /* begin internalCannotReturn: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l178; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l178: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),ourContext); + longAtput((localSP -= BytesPerOop),localReturnValue); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))); + GIV(argumentCount) = 1; + goto normalSend; + goto l176; + } + } + + /* Now we have a frame to return to. If it is on a different page we must free intervening pages and + nil out intervening contexts. We must free intervening stack pages because if we leave the pages + to be divorced then their contexts will be divorced with intact senders and instruction pointers. This + code is similar to primitiveTerminateTo. We must move any frames on intervening pages above the + frame linked to because these may be in use, e.g. via co-routining (see baseFrameReturn). */ + assert(pageListIsWellFormed()); + + /* begin stackPageFor: */ + newPage = stackPageAtpages(pageIndexFor(frameToReturnTo), GIV(pages)); + if (newPage != GIV(stackPage)) { + theFP1 = (GIV(stackPage)->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP1)); + thePage1 = stackPageAtpages(pageIndexFor(theFP1), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + freeStackPage(GIV(stackPage)); + while (1) { + assert(isContext(currentCtx)); + if ((((((longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && ((stackPageFor((theFP = frameOfMarriedContext(currentCtx)))) == newPage)) break; + if (((((longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (theFP != ((thePage->headFP))) { + /* Since we've just deallocated a page we know that newStackPage won't deallocate an existing one. */ + moveFramesInthroughtoPage(thePage, findFrameAboveinPage(theFP, thePage), newStackPage()); + } + theFP1 = (thePage->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP1)); + thePage1 = stackPageAtpages(pageIndexFor(theFP1), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + freeStackPage(thePage); + } + else { + nextCntx = longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + + /* begin markContextAsDead: */ + assert(isContext(currentCtx)); + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(SenderIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + currentCtx = nextCntx; + } + } + + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + localSP = (GIV(stackPage)->headSP); + localFP = (GIV(stackPage)->headFP); + } + + /* Two cases. Returning to the top frame on a new page or an interior frame on the current page. + The top frame has its instruction pointer on top of stack. An interior frame has its instruction pointer + in the caller frame. We need to peel back any frames on the page until we get to the correct frame. */ + if (localFP == frameToReturnTo) { + localIP = ((char *)(longAt(localSP))); + } + else { + do { + callerFP = localFP; + localFP = ((char *)(longAt(localFP + FoxSavedFP))); + } while(localFP != frameToReturnTo); + localIP = ((char *)(longAt(callerFP + FoxCallerSavedIP))); + localSP = ((/* begin frameCallerSP: */ + assert(!(isBaseFrame(callerFP))), +(callerFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(callerFP))->cmNumArgs) + : byteAt((callerFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)) - BytesPerWord; + } + + /* pop the saved IP, push the return value and continue. */ + + /* begin maybeReturnToMachineCodeFrame */ + if ((((usqInt)localIP)) < (startOfMemory())) { + if ((((usqInt)localIP)) != (ceReturnToInterpreterPC())) { + /* localIP in the cog method zone indicates a return to machine code. */ + + /* begin returnToMachineCodeFrame */ + assertCStackWellAligned(); + assert((((usqInt)localIP)) < (startOfMemory())); + assert(isMachineCodeFrame(localFP)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 0, __LINE__); + assert(isPCWithinMethodZone(((usqInt)localIP))); + + /* internalStackTopPut: */ + longAtput(localSP,((usqInt)localIP)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),localReturnValue); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + ceEnterCogCodePopReceiverReg(); + goto l180; + } + localIP = ((char *)(((usqInt)(longAt(localFP + FoxIFSavedIP))))); + } + /* end maybeReturnToMachineCodeFrame */ +l180: + aMethodObj = longAt(localFP + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* internalStackTopPut: */ + longAtput(localSP,localReturnValue); + } + /* end case */ +l176:; + BREAK; + CASE(121) // returnTrue + CASE(345) // 89 returnTrue + { + VM_LABEL(returnTrue); + localReturnValue = GIV(trueObj); + goto commonReturn; + } + BREAK; + CASE(122) // returnFalse + CASE(346) // 90 returnFalse + { + VM_LABEL(returnFalse); + localReturnValue = GIV(falseObj); + goto commonReturn; + } + BREAK; + CASE(123) // returnNil + CASE(347) // 91 returnNil + { + VM_LABEL(returnNil); + localReturnValue = GIV(nilObj); + goto commonReturn; + } + BREAK; + CASE(124) // returnTopFromMethod + CASE(348) // 92 returnTopFromMethod + { + VM_LABEL(returnTopFromMethod); + localReturnValue = longAt(localSP); + goto commonReturn; + } + BREAK; + CASE(125) // returnTopFromBlock + { + VM_LABEL(returnTopFromBlock); + localReturnValue = longAt(localSP); + + /* goto commonCallerReturn */ + } + + commonCallerReturn: + /* commonCallerReturn */ + { + sqInt aMethodObj; + char *callerFP; + char *callersFPOrNull; + sqInt contextToReturnFrom; + sqInt contextToReturnTo; + char *fp; + char *frameAbove; + sqInt header; + sqInt methodHeader; + StackPage *newPage; + sqInt retToContext; + sqInt senderOop; + char *theFP; + StackPage *thePage; + StackPage *thePage1; + char *theSP; + + VM_LABEL(commonCallerReturn); + + /* TODO: Store/restore the nativeSP more properly, when it exists */ + callersFPOrNull = ((char *)(longAt(localFP + FoxSavedFP))); + if (!callersFPOrNull) { + assert(localFP == ((GIV(stackPage)->baseFP))); + + /* begin baseFrameReturn */ + /* begin frameCallerContext: */ + assert(isBaseFrame(localFP)); + thePage1 = stackPageAtpages(pageIndexFor(localFP), GIV(pages)); + contextToReturnTo = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(contextToReturnTo)); + assert((contextToReturnTo == (nilObject())) + || (isContext(followMaybeForwarded(contextToReturnTo)))); + + /* The stack page is effectively free now, so free it. We must free it to be + correct in determining if contextToReturnTo is still married, and in case + makeBaseFrameFor: cogs a method, which may cause a code compaction, + in which case the frame must be free to avoid the relocation machinery + tracing the dead frame. Since freeing now temporarily violates the page-list + ordering invariant, use the assert-free version. */ + freeStackPageNoAssert(GIV(stackPage)); + retToContext = /* isContext: */ + ((!(contextToReturnTo & (tagMask())))) + && (((longAt((void *)(contextToReturnTo))) & (classIndexMask())) == ClassMethodContextCompactIndex); + if (retToContext + && (/* isStillMarriedContext: */ + (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(contextToReturnTo))))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (theFP == ((thePage->headFP))) { + theSP = (thePage->headSP); + } + else { + /* Returning to some interior frame, presumably because of a sender assignment. + Move the frames above to another page (they may be in use, e.g. via coroutining). + Make the interior frame the top frame. */ + + /* begin findFrameAbove:inPage: */ + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == theFP) { + frameAbove = 0; + goto l185; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == theFP) { + frameAbove = fp; + goto l185; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + frameAbove = 0; + /* end findFrameAbove:inPage: */ +l185: + + /* Since we've just deallocated a page we know that newStackPage won't deallocate an existing one. */ + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l186; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l186: + assert(newPage == GIV(stackPage)); + moveFramesInthroughtoPage(thePage, frameAbove, newPage); + markStackPageMostRecentlyUsed(newPage); + theFP = (thePage->headFP); + theSP = (thePage->headSP); + } + } + else { + if (!(retToContext + && (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1)))) { + contextToReturnFrom = longAt(((GIV(stackPage)->baseAddress)) - BytesPerWord); + tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFromtoreturnValue(contextToReturnFrom, contextToReturnTo, localReturnValue); + + /* begin externalCannotReturn:from: */ + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),contextToReturnFrom); + longAtput((GIV(stackPointer) -= BytesPerWord),localReturnValue); + + /* Both ceBaseFrameReturnTrampoline & ceNonLocalReturnTrampoline pop + their caller's return pc into instructionPointer. In this uncommon case restore + it, since a send's call pushes the instructionPointer (after the arguments). */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + ((void) (ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), contextToReturnFrom, 1))); + goto l184; + } + + /* We must void the instructionPointer to stop it being updated if makeBaseFrameFor: + cogs a method, which may cause a code compaction. */ + GIV(instructionPointer) = 0; + thePage = makeBaseFrameFor(contextToReturnTo); + theFP = (thePage->headFP); + theSP = (thePage->headSP); + } + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + assert((stackPageFor(theFP)) == GIV(stackPage)); + localSP = theSP; + localFP = theFP; + localIP = ((char *)(longAt(localSP))); + if ((((usqInt)localIP)) < (startOfMemory())) { + if ((((usqInt)localIP)) != (ceReturnToInterpreterPC())) { + /* localIP in the cog method zone indicates a return to machine code. */ + + /* begin returnToMachineCodeFrame */ + assertCStackWellAligned(); + assert((((usqInt)localIP)) < (startOfMemory())); + assert(isMachineCodeFrame(localFP)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 0, __LINE__); + assert(isPCWithinMethodZone(((usqInt)localIP))); + + /* internalStackTopPut: */ + longAtput(localSP,((usqInt)localIP)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),localReturnValue); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + ceEnterCogCodePopReceiverReg(); + goto l184; + } + localIP = ((char *)(((usqInt)(longAt(localFP + FoxIFSavedIP))))); + } + assert(checkIsStillMarriedContextcurrentFP(contextToReturnTo, localFP)); + aMethodObj = longAt(localFP + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* internalStackTopPut: */ + longAtput(localSP,localReturnValue); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l184; + } + + /* baseFrame */ + localIP = ((char *)(longAt(localFP + FoxCallerSavedIP))); + localSP = localFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(localFP))->cmNumArgs) + : byteAt((localFP + FoxIFrameFlags) + 1)))) << (shiftForWord()))))); + localFP = callersFPOrNull; + if ((((usqInt)localIP)) < (startOfMemory())) { + if ((((usqInt)localIP)) != (ceReturnToInterpreterPC())) { + /* localIP in the cog method zone indicates a return to machine code. */ + + /* begin returnToMachineCodeFrame */ + assertCStackWellAligned(); + assert((((usqInt)localIP)) < (startOfMemory())); + assert(isMachineCodeFrame(localFP)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 0, __LINE__); + assert(isPCWithinMethodZone(((usqInt)localIP))); + + /* internalStackTopPut: */ + longAtput(localSP,((usqInt)localIP)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),localReturnValue); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + ceEnterCogCodePopReceiverReg(); + goto l184; + } + localIP = ((char *)(((usqInt)(longAt(localFP + FoxIFSavedIP))))); + } + aMethodObj = longAt(localFP + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* internalStackTopPut: */ + longAtput(localSP,localReturnValue); + } + /* end case */ +l184:; + BREAK; + CASE(126) // unknownBytecode + CASE(127) // unknownBytecode + + respondToUnknownBytecode: + /* respondToUnknownBytecode */ + { + usqInt numSlots; + sqInt ourContext; + + VM_LABEL(respondToUnknownBytecode); + GIV(messageSelector) = /* maybeSplObj: */ + (SelectorUnknownBytecode < ((/* begin numSlotsOf: */ + assert((classIndexOf(GIV(specialObjectsOop))) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(GIV(specialObjectsOop) + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(GIV(specialObjectsOop) - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) + ? longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorUnknownBytecode) << (shiftForWord())))))) + : 0); + if ((!GIV(messageSelector)) + || (GIV(messageSelector) == GIV(nilObj))) { + error("Unknown bytecode"); + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l187; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l187: + + /* undo fetch of bytecode so that context's pc is pointing to the unknown bytecode. */ + localIP -= 1; + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),ourContext); + GIV(argumentCount) = 0; + goto normalSend; + } + BREAK; + CASE(128) // extendedPushBytecode + { + sqInt descriptor; + usqInt frameNumArgs; + sqInt litVar; + sqInt object; + usqInt variableIndex; + usqInt variableType; + + VM_LABEL(extendedPushBytecode); + descriptor = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + variableType = (((usqInt)(descriptor)) >> 6) & 3; + variableIndex = descriptor & 0x3F; + if (!variableType) { + /* begin pushReceiverVariable: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + ((variableIndex << (shiftForWord())))))); + goto l8; + } + if (variableType == 1) { + /* begin pushTemporaryVariable: */ + object = /* itemporary:in: */ + (variableIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - variableIndex) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - variableIndex) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + goto l8; + } + if (variableType == 2) { + /* begin pushLiteralConstant: */ + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + (((variableIndex + LiteralStart) << (shiftForWord()))))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + goto l8; + } + if (variableType == 3) { + /* begin pushLiteralVariable: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + (((variableIndex + LiteralStart) << (shiftForWord()))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(variableIndex + LiteralStart, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))))); + goto l8; + } + } + /* end case */ +l8:; + BREAK; + CASE(129) // extendedStoreBytecode + { + sqInt additionalMethodState; + sqInt descriptor; + sqInt descriptor1; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt index; + sqInt instVarTypesArrays; + sqInt litVar; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt objOop; + sqInt rcvrClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + sqInt value; + usqInt variableIndex; + + VM_LABEL(extendedStoreBytecode); + /* begin extendedStoreBytecodePop: */ + descriptor = byteAt(localIP + 1); + switch ((((usqInt)(descriptor)) >> 6) & 3) { + case 0: + /* begin collectInstanceVariableTypeFor:at:of: */ + rcvrClass = /* fetchClassOf: */ + ((tagBits = (longAt(localFP + FoxIFReceiver)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(localFP + FoxIFReceiver))); + instVarTypesArrays = /* #instVarTypesArraysOf: #safeFollowObjField:ofObject: */ + (InstVarTypesArraysIndex < (lengthOf(rcvrClass)) + ? followObjFieldofObject(InstVarTypesArraysIndex, rcvrClass) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (instVarTypesArrays) + && (isInstanceOfClassArray(instVarTypesArrays)))) { + goto l14; + } + typesArray = /* safeFollowObjField:ofObject: */ + ((descriptor & 0x3F) < (lengthOf(instVarTypesArrays)) + ? followObjFieldofObject(descriptor & 0x3F, instVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l13; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l9; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l9; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l9; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l9; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l9; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l9: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l13; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l13; + } + } + /* end collectTypeOf:in: */ +l13: + /* end collectTypeOf:in:at: */ +l14:; + break; + case 1: + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 3)) != 0))) { + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l11; + } + typesArray = /* safeFollowObjField:ofObject: */ + ((descriptor & 0x3F) < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(descriptor & 0x3F, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l10; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l15; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l15; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l15; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l15; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l15; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l15: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l10; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l10; + } + } + /* end collectTypeOf:in: */ +l10: + /* end collectTypeOf:in:at: */ +l11:; + } + break; + default: +; + } + descriptor1 = byteAt(++localIP); + variableIndex = descriptor1 & 0x3F; + value = longAt(localSP); + switch ((((usqInt)(descriptor1)) >> 6) & 3) { + case 0: + objOop = longAt(localFP + FoxIFReceiver); + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(objOop)))); + if ((((usqInt)((byteAt((void *)(objOop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),objOop); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((variableIndex + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l12; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, objOop, value)); + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + ((variableIndex << (shiftForWord())))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l12:; + break; + case 1: + /* itemporary:in:put: */ + if (variableIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - variableIndex) * BytesPerWord),value); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - variableIndex) * BytesPerWord),value); + } + break; + case 2: + error("illegal store"); + break; + case 3: + /* begin storeLiteralVariable:withValue: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + (((variableIndex + LiteralStart) << (shiftForWord()))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(variableIndex + LiteralStart, GIV(method), litVar); + } + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(litVar)))); + if ((((usqInt)((byteAt((void *)(litVar + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),litVar); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((usqInt)2 /* (ValueIndex + 1) */ << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l16; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ValueIndex, litVar, value)); + assert(isNonImmediate(litVar)); + if (oopisGreaterThanOrEqualTo(litVar, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(litVar + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(litVar); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l16:; + break; + default: + error("Case not found and no otherwise clause"); + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(130) // extendedStoreAndPopBytecode + { + sqInt additionalMethodState; + sqInt descriptor; + sqInt descriptor1; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt index; + sqInt instVarTypesArrays; + sqInt litVar; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt objOop; + sqInt rcvrClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + sqInt value; + usqInt variableIndex; + + VM_LABEL(extendedStoreAndPopBytecode); + /* begin extendedStoreBytecodePop: */ + descriptor = byteAt(localIP + 1); + switch ((((usqInt)(descriptor)) >> 6) & 3) { + case 0: + /* begin collectInstanceVariableTypeFor:at:of: */ + rcvrClass = /* fetchClassOf: */ + ((tagBits = (longAt(localFP + FoxIFReceiver)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(localFP + FoxIFReceiver))); + instVarTypesArrays = /* #instVarTypesArraysOf: #safeFollowObjField:ofObject: */ + (InstVarTypesArraysIndex < (lengthOf(rcvrClass)) + ? followObjFieldofObject(InstVarTypesArraysIndex, rcvrClass) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (instVarTypesArrays) + && (isInstanceOfClassArray(instVarTypesArrays)))) { + goto l22; + } + typesArray = /* safeFollowObjField:ofObject: */ + ((descriptor & 0x3F) < (lengthOf(instVarTypesArrays)) + ? followObjFieldofObject(descriptor & 0x3F, instVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l21; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l17; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l17; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l17; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l17; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l17; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l17: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l21; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l21; + } + } + /* end collectTypeOf:in: */ +l21: + /* end collectTypeOf:in:at: */ +l22:; + break; + case 1: + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 3)) != 0))) { + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l19; + } + typesArray = /* safeFollowObjField:ofObject: */ + ((descriptor & 0x3F) < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(descriptor & 0x3F, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l18; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l23; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l23; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l23; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l23; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l23; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l23: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l18; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l18; + } + } + /* end collectTypeOf:in: */ +l18: + /* end collectTypeOf:in:at: */ +l19:; + } + break; + default: +; + } + descriptor1 = byteAt(++localIP); + variableIndex = descriptor1 & 0x3F; + value = longAt(localSP); + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + switch ((((usqInt)(descriptor1)) >> 6) & 3) { + case 0: + objOop = longAt(localFP + FoxIFReceiver); + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(objOop)))); + if ((((usqInt)((byteAt((void *)(objOop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),objOop); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((variableIndex + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l20; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, objOop, value)); + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + ((variableIndex << (shiftForWord())))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l20:; + break; + case 1: + /* itemporary:in:put: */ + if (variableIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - variableIndex) * BytesPerWord),value); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - variableIndex) * BytesPerWord),value); + } + break; + case 2: + error("illegal store"); + break; + case 3: + /* begin storeLiteralVariable:withValue: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + (((variableIndex + LiteralStart) << (shiftForWord()))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(variableIndex + LiteralStart, GIV(method), litVar); + } + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(litVar)))); + if ((((usqInt)((byteAt((void *)(litVar + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),litVar); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((usqInt)2 /* (ValueIndex + 1) */ << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l24; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ValueIndex, litVar, value)); + assert(isNonImmediate(litVar)); + if (oopisGreaterThanOrEqualTo(litVar, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(litVar + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(litVar); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l24:; + break; + default: + error("Case not found and no otherwise clause"); + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(131) // singleExtendedSendBytecode + { + sqInt descriptor; + + VM_LABEL(singleExtendedSendBytecode); + descriptor = byteAt(++localIP); + + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((descriptor & 0x1F) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = ((usqInt)(descriptor)) >> 5; + + /* goto normalSend */ + } + + normalSend: + /* normalSend */ + { + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(normalSend); + rcvr = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + lkupClassTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + assert(lkupClassTag != (nilObject())); + + commonSendOrdinary: + /* commonSendOrdinary */ + { + sqInt additionalMethodState; + sqInt arg; + sqInt argIndex; + sqInt classTablePage; + CogMethod *cogMethod; + sqInt fieldIndex; + sqInt fmt; + sqInt hash; + sqInt i; + sqInt index; + sqInt localPrimIndex; + sqInt maxNumArgs; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt nArgs; + usqInt numSlots; + usqInt numSlotsUsqInt; + usqInt numTemps; + sqInt object; + sqInt objectClass; + usqInt probe; + sqInt rcvr; + char *savedFramePointer; + char *savedStackPointer; + sqInt selectorOrProperties; + int succeeded; + sqInt switched; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt types; + sqInt typesArraySize; + sqInt wordSize; + + VM_LABEL(commonSendOrdinary); + rcvr = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + if (recordSendTrace()) { + /* begin recordTrace:source:thing:extra: */ + if (TraceLog) { + GIV(traceLog)[GIV(traceLogIndex)] = 0 /* (TraceSend + CSFromInterpreter) */; + GIV(traceLog)[GIV(traceLogIndex) + 1] = (classForClassTag(lkupClassTag)); + GIV(traceLog)[GIV(traceLogIndex) + 2] = GIV(messageSelector); + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + if (printOnTrace()) { + printActivationNameForSelectorstartClass(GIV(messageSelector), classForClassTag(lkupClassTag)); + cr(); + } + } + + /* begin internalFindNewMethodOrdinary */ + /* begin inlineLookupInMethodCacheSel:classTag: */ + /* shift drops two low-order zeros from addresses */ + hash = GIV(messageSelector) ^ ((((usqInt)(lkupClassTag) << 2))); + + /* first probe */ + probe = hash & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == GIV(messageSelector)) + && ((GIV(methodCache)[probe + MethodCacheClass]) == lkupClassTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + goto l195; + } + + /* second probe */ + probe = (((usqInt)(hash)) >> 1) & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == GIV(messageSelector)) + && ((GIV(methodCache)[probe + MethodCacheClass]) == lkupClassTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + goto l195; + } + probe = (((usqInt)(hash)) >> 2) & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == GIV(messageSelector)) + && ((GIV(methodCache)[probe + MethodCacheClass]) == lkupClassTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + goto l195; + } + goto l192; +l195: + + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + goto l189; +l192: + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + if ((/* isOopForwarded: */ + ((!(GIV(messageSelector) & (tagMask())))) + && ((!((longAt((void *)(GIV(messageSelector)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + || (lkupClassTag == (isForwardedObjectClassIndexPun()))) { + if (/* isOopForwarded: */ + ((!(GIV(messageSelector) & (tagMask())))) + && ((!((longAt((void *)(GIV(messageSelector)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + GIV(messageSelector) = handleForwardedSelectorFaultFor(GIV(messageSelector)); + } + if (lkupClassTag == (isForwardedObjectClassIndexPun())) { + lkupClassTag = handleForwardedSendFaultForTag(lkupClassTag); + } + if (lookupInMethodCacheSelclassTag(GIV(messageSelector), lkupClassTag)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + goto l190; + } + } + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((lkupClassTag >= 0) + && ((lkupClassTag <= (tagMask())) + || ((lkupClassTag >= (arrayClassIndexPun())) + && (lkupClassTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(lkupClassTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + GIV(lkupClass) = null; + goto l196; + } + fieldIndex = lkupClassTag & ((1U << (classTableMajorIndexShift())) - 1); + GIV(lkupClass) = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l196: + assert((GIV(lkupClass)) + && (addressCouldBeClassObj(GIV(lkupClass)))); + lookupMethodInClass(GIV(lkupClass)); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + addNewMethodToCache(GIV(lkupClass)); +l189: + /* end internalFindNewMethodOrdinary */ +l190: + + /* begin internalExecuteNewMethod */ + if (primitiveFunctionPointer) { + if ((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex) { + /* begin internalQuickPrimitiveResponse */ + assert(isPrimitiveFunctionPointerAnIndex()); + localPrimIndex = ((sqInt) primitiveFunctionPointer); + assert((localPrimIndex > 0xFF) + && (localPrimIndex < 520)); + + /* Quick return inst vars */ + if (localPrimIndex >= 264) { + longAtput(localSP,longAt((void *)(((longAt(localSP)) + BaseHeaderSize) + ((((usqInt)((localPrimIndex - 264)) << (shiftForWord()))))))); + goto l194; + } + + /* Quick return constants */ + if (localPrimIndex == 0x100) { + goto l194; + } + if (localPrimIndex == 0x101) { + /* internalStackTopPut: */ + longAtput(localSP,GIV(trueObj)); + goto l194; + } + if (localPrimIndex == 258) { + /* internalStackTopPut: */ + longAtput(localSP,GIV(falseObj)); + goto l194; + } + if (localPrimIndex == 259) { + /* internalStackTopPut: */ + longAtput(localSP,GIV(nilObj)); + goto l194; + } + + /* internalStackTopPut: */ + longAtput(localSP,(((usqInt)(localPrimIndex - 261) << 3) | 1)); + goto l194; + } + + /* slowPrimitiveResponse may of course context-switch. If so we must reenter the + new process appropriately, returning only if we've found an interpreter frame. */ + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin slowPrimitiveResponse */ + assert(!(isOopForwarded(stackValue(GIV(argumentCount))))); + assert((GIV(remapBufferCount)) == 0); + nArgs = GIV(argumentCount); + savedStackPointer = GIV(stackPointer); + savedFramePointer = GIV(framePointer); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if (/* recordPrimTraceForMethod: */ + (recordPrimTrace()) + && ((primTracePluginName + ? methodHasPrimitiveInPrimTracePlugin(GIV(newMethod)) + : 1))) { + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = GIV(newMethod); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + dispatchFunctionPointer(primitiveFunctionPointer); + assert(maybeLeakCheckExternalPrimCall(GIV(newMethod))); + + /* begin maybeRetryPrimitiveOnFailure */ + if (GIV(primFailCode)) { + retryPrimitiveOnFailure(); + } + if ((!GIV(primFailCode)) + && ((GIV(framePointer) == savedFramePointer) + && (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))))) { + if (GIV(stackPointer) != (savedStackPointer + (nArgs * BytesPerWord))) { + /* This is necessary but insufficient; the result may still have been written to the stack. + At least we'll know something is wrong. */ + failUnbalancedPrimitive(); + GIV(stackPointer) = savedStackPointer; + } + } + + /* Don't fail if primitive has done something radical, e.g. perform: + If we are profiling, take accurate primitive measures */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + succeeded = !GIV(primFailCode); + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (succeeded) { + returntoExecutive(popStack(), 1); + browserPluginReturnIfNeeded(); + goto l194; + } + } + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* if not primitive, or primitive failed, activate the method */ + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if (!((!(methodHeader & (smallIntegerTag()))))) { + /* begin internalActivateNewMethod */ + /* begin collectArgumentsTypeFor:fromStackAt:count: */ + /* begin tempVarTypesArraysOf: */ + /* begin additionalMethodStateOf:at: */ + /* begin additionalMethodStateOf: */ + selectorOrProperties = penultimateLiteralOf(GIV(newMethod)); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l188; + } + wordSize = BytesPerWord; + maxNumArgs = (((lengthOf(tempVarTypesArrays)) < GIV(argumentCount)) ? (lengthOf(tempVarTypesArrays)) : GIV(argumentCount)); + for (argIndex = 0; argIndex < maxNumArgs; argIndex += 1) { + /* begin followObjField:ofObject: */ + types = longAt((void *)((tempVarTypesArrays + BaseHeaderSize) + ((((usqInt)(argIndex) << (shiftForWord())))))); + assert(isNonImmediate(types)); + if ((!((longAt((void *)(types))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + types = fixFollowedFieldofObjectwithInitialValue(argIndex, tempVarTypesArrays, types); + } + arg = longAt((void *)(localSP + (argIndex * wordSize))); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (types) + && (isInstanceOfClassArray(types)))) { + goto l193; + } + objectClass = /* fetchClassOf: */ + ((tagBits = arg & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(arg)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(types + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(types + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(types - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l191; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l191; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l191; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l191; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l191; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l191: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, types, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l193; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, types, objectClass)); + assert(isNonImmediate(types)); + if (oopisGreaterThanOrEqualTo(types, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(types + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(types); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l193; + } + } + /* end collectTypeOf:in: */ +l193:; + } + /* end collectArgumentsTypeFor:fromStackAt:count: */ +l188: + methodHeaderSqInt = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(!((isCogMethodReferenceNoAssert(methodHeaderSqInt)))); + numTemps = (((usqInt)(methodHeaderSqInt)) >> MethodHeaderTempCountShift) & 0x3F; + assert(GIV(argumentCount) == (argumentCountOfMethodHeader(methodHeaderSqInt))); + + /* could new rcvr be set at point of send? */ + rcvr = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + assert(!(isOopForwarded(rcvr))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),((usqInt)localIP)); + longAtput((localSP -= BytesPerOop),((usqInt)localFP)); + localFP = localSP; + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeaderSqInt); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeaderSqInt)) < 0 + ? 0x100 + : 0); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((((usqInt)(methodHeaderSqInt)) >> MethodHeaderArgCountShift) & 15) << ((BytesPerWord * 8) - 8))))) + : ((1 + ((((((usqInt)(methodHeaderSqInt)) >> MethodHeaderArgCountShift) & 15) << 8))))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + longAtput((localSP -= BytesPerOop),0); + longAtput((localSP -= BytesPerOop),rcvr); + + /* Initialize temps... */ + for (i = (GIV(argumentCount) + 1); i <= numTemps; i += 1) { + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(nilObj)); + } + + /* -1 to account for pre-increment in fetchNextBytecode */ + localIP = (((char *)(((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeaderSqInt) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeaderSqInt >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize))) - 1; + if (((methodHeaderSqInt & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + localIP += 3 /* sizeOfCallPrimitiveBytecode: */; + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(localSP, methodHeaderSqInt); + } + } + assert((frameNumArgs(localFP)) == GIV(argumentCount)); + assert(!(frameIsBlockActivation(localFP))); + assert(!(frameHasContext(localFP))); + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc). */ + if (localSP < GIV(stackLimit)) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + switched = handleStackOverflowOrEventAllowContextSwitch(canContextSwitchIfActivatingheader(GIV(newMethod), methodHeaderSqInt)); + returnToExecutivepostContextSwitch(1, switched); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + } + goto l194; + } + + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(localFP))); + longAtput(localFP + FoxIFSavedIP,((sqInt)localIP)); + GIV(instructionPointer) = ceReturnToInterpreterPC(); + + /* begin externalizeFPandSP */ + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* This may context switch and hence return... */ + activateNewCogMethodinInterpreter(((CogMethod *) methodHeader), 1); + + /* Hence this si reachable... */ + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + /* end internalExecuteNewMethod */ +l194: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + } + BREAK; + CASE(132) // doubleExtendedDoAnythingBytecode + { + sqInt byte2; + sqInt byte3; + sqInt callerContextOrNil; + char *callerFP; + sqInt litVar; + sqInt obj; + sqInt object; + int onCurrentPage; + sqInt opType; + sqInt senderOop; + char *spouseFP; + char *theFP; + StackPage *thePage; + sqInt top; + sqInt value; + + VM_LABEL(doubleExtendedDoAnythingBytecode); + byte2 = byteAt(++localIP); + byte3 = byteAt(++localIP); + opType = ((usqInt)(byte2)) >> 5; + if (!opType) { + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((byte3 + LiteralStart)) << (shiftForWord())))))); + GIV(argumentCount) = byte2 & 0x1F; + goto normalSend; + goto l25; + } + if (opType == 1) { + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((byte3 + LiteralStart)) << (shiftForWord())))))); + GIV(argumentCount) = byte2 & 0x1F; + goto superclassSend; + goto l25; + } + if (opType == 2) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + obj = longAt(localFP + FoxIFReceiver); + if ((byte3 <= StackPointerIndex) + && (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin instVar:ofContext: */ + assert(byte3 < MethodIndex); + assert(isContext(obj)); + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + if (!(((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + value = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord())))))); + if ((byte3 == InstructionPointerIndex) + && (((((value) & 7) == 1)) + && ((((int) value)) < 0))) { + /* begin internalMustMapMachineCodePC:context: */ + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + value = mustMapMachineCodePCcontext((value >> 3), obj); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + } + object = value; + goto l28; + } + if (isWidowedContext(obj)) { + object = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord())))))); + goto l28; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!byte3) { + /* begin ensureCallerContext: */ + callerFP = ((char *)(longAt(spouseFP + FoxSavedFP))); + if (!callerFP) { + /* begin frameCallerContext: */ + assert(isBaseFrame(spouseFP)); + thePage = stackPageAtpages(pageIndexFor(spouseFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + object = callerContextOrNil; + goto l28; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP))); + object = longAt(callerFP + FoxThisContext); + goto l28; + } + object = marryFrameSP( + callerFP, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(spouseFP))), + (spouseFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(spouseFP))->cmNumArgs) + : byteAt((spouseFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + goto l28; + } + if (byte3 == StackPointerIndex) { + assert((ReceiverIndex + (stackPointerIndexForFrame(spouseFP))) < (lengthOf(obj))); + object = (((usqInt)(stackPointerIndexForFrame(spouseFP)) << 3) | 1); + goto l28; + } + if (byte3 == InstructionPointerIndex) { + object = instructionPointerForFramecurrentFPcurrentIP(spouseFP, localFP, oopForPointer(localIP)); + goto l28; + } + error("bad index"); + object = 0; + /* end instVar:ofContext: */ +l28: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + else { + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord()))))))); + } + goto l25; + } + if (opType == 3) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((byte3 + LiteralStart)) << (shiftForWord())))))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + goto l25; + } + if (opType == 4) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((byte3 + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(byte3 + LiteralStart, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))))); + goto l25; + } + top = longAt(localSP); + if (opType == 7) { + /* begin storeLiteralVariable:withValue: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((byte3 + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(byte3 + LiteralStart, GIV(method), litVar); + } + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(litVar)))); + if ((((usqInt)((byteAt((void *)(litVar + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),litVar); + longAtput((localSP -= BytesPerOop),top); + longAtput((localSP -= BytesPerOop),(((usqInt)2 /* (ValueIndex + 1) */ << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l27; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ValueIndex, litVar, top)); + assert(isNonImmediate(litVar)); + if (oopisGreaterThanOrEqualTo(litVar, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(top & (tagMask())))) + && (oopisLessThan(top, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(litVar + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(litVar); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))),top); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l27: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l25; + } + + /* opType = 5 is store; opType = 6 is storePop */ + if (opType == 6) { + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + obj = longAt(localFP + FoxIFReceiver); + if ((byte3 <= ReceiverIndex) + && ((((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + || (ismethodAssignmentToContextWithMachineCodePC(byte3, top))))) { + /* begin instVar:ofContext:put: */ + assert(isMarriedOrWidowedContext(obj)); + assert(!((isObjImmutable(obj)))); + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(obj))))) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(byte3, obj, top)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(top & (tagMask())))) + && (oopisLessThan(top, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord()))))),top); + if (byte3 == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + goto l26; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!byte3) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + onCurrentPage = thePage == GIV(stackPage); + storeSenderOfFramewithValue(theFP, top); + if (onCurrentPage) { + localFP = (GIV(stackPage)->headFP); + localSP = (GIV(stackPage)->headSP); + } + else { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + goto l26; + } + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + externalDivorceFrameandContext(theFP, obj); + if (ismethodAssignmentToContextWithMachineCodePC(byte3, obj)) { + ensureContextHasBytecodePC(obj); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(byte3, obj, top)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(top & (tagMask())))) + && (oopisLessThan(top, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord()))))),top); + if (byte3 == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + + /* Assigning various fields can force a divorce which can change the stackPage. */ + markStackPageMostRecentlyUsed(GIV(stackPage)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 1, __LINE__); + /* end instVar:ofContext:put: */ +l26:; + } + else { + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(obj)))); + if ((((usqInt)((byteAt((void *)(obj + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),obj); + longAtput((localSP -= BytesPerOop),top); + longAtput((localSP -= BytesPerOop),(((usqInt)(byte3 + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l29; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(byte3, obj, top)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(top & (tagMask())))) + && (oopisLessThan(top, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord()))))),top); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l29:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + /* end case */ +l25:; + BREAK; + CASE(133) // singleExtendedSuperBytecode + { + sqInt descriptor; + + VM_LABEL(singleExtendedSuperBytecode); + descriptor = byteAt(++localIP); + + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((descriptor & 0x1F) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = ((usqInt)(descriptor)) >> 5; + + /* goto superclassSend */ + } + + superclassSend: + /* superclassSend */ + { + sqInt aValue; + sqInt classPointer; + sqInt err; + int hash; + sqInt literal; + sqInt objOop; + sqInt offset; + sqInt referent; + sqInt superclass; + + VM_LABEL(superclassSend); + /* begin methodClassOf: */ + offset = (literalCountOf(GIV(method))) - 1; + literal = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = fixFollowedFieldofObjectwithInitialValue(offset + LiteralStart, GIV(method), literal); + } + classPointer = ((literal != GIV(nilObj)) + && (/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + ? (assert((numSlotsOf(literal)) > ValueIndex), + followFieldofObject(ValueIndex, literal)) + : GIV(nilObj)); + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + superclass = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(superclass)); + if ((!((longAt((void *)(superclass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + superclass = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, superclass); + } + + /* classTagForClass: uses ensureBehaviorHash: */ + + /* begin classTagForClass: */ + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(superclass)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + lkupClassTag = ((hash = (long32At((void *)(superclass + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(superclass) + ? ((err = enterIntoClassTable(superclass)) + ? -err + : (long32At((void *)(superclass + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + + /* To maintain the invariant that all receivers are unforwarded we need an explicit + read barrier in the super send cases. Even though we always follow receivers + on become e.g. super doSomethingWith: (self become: other) forwards the receiver + self pushed on the stack. */ + + /* begin ensureReceiverUnforwarded */ + if (/* isOopForwarded: */ + ((!((longAt(localSP + (GIV(argumentCount) * BytesPerOop))) & (tagMask())))) + && ((!((longAt((void *)(longAt(localSP + (GIV(argumentCount) * BytesPerOop))))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + referent = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + aValue = referent; + + /* begin internalStackValue:put: */ + longAtput(localSP + (GIV(argumentCount) * BytesPerOop),aValue); + } + assert(lkupClassTag != (nilObject())); + goto commonSendOrdinary; + } + BREAK; + CASE(134) // secondExtendedSendBytecode + { + sqInt descriptor; + + VM_LABEL(secondExtendedSendBytecode); + descriptor = byteAt(++localIP); + + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((descriptor & 0x3F) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = ((usqInt)(descriptor)) >> 6; + goto normalSend; + } + BREAK; + CASE(135) // popStackBytecode + CASE(472) // 216 popStackBytecode + { + VM_LABEL(popStackBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(136) // duplicateTopBytecode + CASE(339) // 83 duplicateTopBytecode + { + sqInt object; + + VM_LABEL(duplicateTopBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = longAt(localSP); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(137) // pushActiveContextBytecode + { + sqInt ourContext; + + VM_LABEL(pushActiveContextBytecode); + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l30; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l30: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),ourContext); + } + BREAK; + CASE(138) // pushNewArrayBytecode + CASE(487) // 231 pushNewArrayBytecode + { + usqInt array; + sqInt i; + usqInt newObj; + usqInt numBytes; + int popValues; + sqInt size; + + VM_LABEL(pushNewArrayBytecode); + size = byteAt(++localIP); + if ((popValues = size >= 128)) { + size -= 128; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((size >= 0) + && ((knownClassAtIndex(ClassArrayCompactIndex)) != GIV(nilObj))); + assert((arrayFormat()) == (instSpecOfClass(knownClassAtIndex(ClassArrayCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(size < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((size < 1 + ? 8 /* allocationUnit */ + : size * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + array = 0; + goto l31; + } + } + long64Atput((void *)(newObj),((((((usqLong) size)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + GIV(freeStart) += numBytes; + array = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l31: + + /* Can't use NewObjectMemory>>#eeInstantiateSmallClassIndex:format:numSlots: + cuz it assumes <= 252 bytes whereras this may allocate 127 * 4 + headerSize bytes. */ + if (popValues) { + for (i = 0; i < size; i += 1) { + /* Assume: have just allocated a new Array; it must be young. Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(i, array, longAt((void *)(localSP + (((size - i) - 1) * BytesPerOop))))); + longAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),longAt((void *)(localSP + (((size - i) - 1) * BytesPerOop)))); + } + + /* begin internalPop: */ + localSP += size * BytesPerOop; + } + else { + for (i = 0; i < size; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(i, array, GIV(nilObj))); + longAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),array); + } + BREAK; + CASE(139) // callPrimitiveBytecode + CASE(504) // 248 callPrimitiveBytecode + { + sqInt header; + sqInt headerSqInt; + + VM_LABEL(callPrimitiveBytecode); + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + headerSqInt = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + if ((((header & AlternateHeaderHasPrimFlag) != 0)) + && ((((sqInt)localIP)) == (((((usqInt)(pointerForOop(GIV(method))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize))) { + localIP = (localIP + 3 /* sizeOfCallPrimitiveBytecode: */) - 1; + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l32; + } + goto respondToUnknownBytecode; + } + /* end case */ +l32:; + BREAK; + CASE(140) // pushRemoteTempLongBytecode + CASE(507) // 251 pushRemoteTempLongBytecode + { + usqInt frameNumArgs; + sqInt remoteTempIndex; + sqInt tempVector; + sqInt tempVectorIndex; + + VM_LABEL(pushRemoteTempLongBytecode); + remoteTempIndex = byteAt(++localIP); + tempVectorIndex = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + tempVector = /* itemporary:in: */ + (tempVectorIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - tempVectorIndex) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - tempVectorIndex) * BytesPerWord))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((tempVector + BaseHeaderSize) + ((((usqInt)(remoteTempIndex) << (shiftForWord()))))))); + } + BREAK; + CASE(141) // storeRemoteTempLongBytecode + CASE(508) // 252 storeRemoteTempLongBytecode + { + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt indexSqInt; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt remoteTempIndex; + sqInt remoteTempTypesArrays; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVector; + sqInt tempVectorIndex; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + sqInt valuePointer; + + VM_LABEL(storeRemoteTempLongBytecode); + remoteTempIndex = byteAt(++localIP); + tempVectorIndex = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + remoteTempTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (RemoteTempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(RemoteTempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (remoteTempTypesArrays) + && (isInstanceOfClassArray(remoteTempTypesArrays)))) { + goto l35; + } + typesArray = /* safeFollowObjField:ofObject: */ + (remoteTempIndex < (lengthOf(remoteTempTypesArrays)) + ? followObjFieldofObject(remoteTempIndex, remoteTempTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l34; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l33; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l33; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l33; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l33; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l33; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l33: + for (indexSqInt = 0; indexSqInt < typesArraySize; indexSqInt += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(indexSqInt) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(indexSqInt, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l34; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(indexSqInt, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(indexSqInt) << (shiftForWord()))))),objectClass); + goto l34; + } + } + /* end collectTypeOf:in: */ +l34: + /* end collectTypeOf:in:at: */ +l35: + + /* begin temporaryVector:in: */ + tempVector = /* itemporary:in: */ + (tempVectorIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - tempVectorIndex) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - tempVectorIndex) * BytesPerWord))); + valuePointer = longAt(localSP); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(remoteTempIndex, tempVector, valuePointer)); + assert(isNonImmediate(tempVector)); + if (oopisGreaterThanOrEqualTo(tempVector, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(tempVector + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(tempVector); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((tempVector + BaseHeaderSize) + ((((usqInt)(remoteTempIndex) << (shiftForWord()))))),valuePointer); + } + BREAK; + CASE(142) // storeAndPopRemoteTempLongBytecode + CASE(509) // 253 storeAndPopRemoteTempLongBytecode + { + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + usqInt frameNumArgs; + sqInt indexSqInt; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt remoteTempIndex; + sqInt remoteTempTypesArrays; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVector; + sqInt tempVectorIndex; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + sqInt valuePointer; + + VM_LABEL(storeAndPopRemoteTempLongBytecode); + /* begin storeRemoteTempLongBytecode */ + remoteTempIndex = byteAt(++localIP); + tempVectorIndex = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(localFP))->methodObject) + : longAt(localFP + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + remoteTempTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (RemoteTempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(RemoteTempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (remoteTempTypesArrays) + && (isInstanceOfClassArray(remoteTempTypesArrays)))) { + goto l36; + } + typesArray = /* safeFollowObjField:ofObject: */ + (remoteTempIndex < (lengthOf(remoteTempTypesArrays)) + ? followObjFieldofObject(remoteTempIndex, remoteTempTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l38; + } + objectClass = /* fetchClassOf: */ + ((tagBits = (longAt(localSP)) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(localSP))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l37; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l37; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l37; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l37; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l37; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l37: + for (indexSqInt = 0; indexSqInt < typesArraySize; indexSqInt += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(indexSqInt) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(indexSqInt, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l38; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(indexSqInt, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(indexSqInt) << (shiftForWord()))))),objectClass); + goto l38; + } + } + /* end collectTypeOf:in: */ +l38: + /* end collectTypeOf:in:at: */ +l36: + + /* begin temporaryVector:in: */ + tempVector = /* itemporary:in: */ + (tempVectorIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - tempVectorIndex) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - tempVectorIndex) * BytesPerWord))); + valuePointer = longAt(localSP); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(remoteTempIndex, tempVector, valuePointer)); + assert(isNonImmediate(tempVector)); + if (oopisGreaterThanOrEqualTo(tempVector, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(tempVector + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(tempVector); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((tempVector + BaseHeaderSize) + ((((usqInt)(remoteTempIndex) << (shiftForWord()))))),valuePointer); + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(143) // pushClosureCopyCopiedValuesBytecode + { + sqInt blockSize; + sqInt context; + sqInt i; + sqInt initialIP; + usqInt newClosure; + usqInt newObj; + usqInt numArgs; + sqInt numArgsNumCopied; + usqInt numBytes; + sqInt numCopied; + sqInt numSlots; + + VM_LABEL(pushClosureCopyCopiedValuesBytecode); + numArgsNumCopied = byteAt(++localIP); + numArgs = numArgsNumCopied & 15; + numCopied = (((usqInt)(numArgsNumCopied)) >> 4); + + /* Split blockSize := (self fetchByte * 256) + self fetchByte. into two because evaluation order in C is undefined. */ + blockSize = ((sqInt)((usqInt)((byteAt(++localIP))) << 8)); + blockSize += byteAt(++localIP); + + /* begin pushClosureNumArgs:copiedValues:blockSize: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + context = longAt(localFP + FoxThisContext); + goto l39; + } + context = marryFrameSP(localFP, localSP + (numCopied * BytesPerOop)); + /* end ensureFrameIsMarried:SP: */ +l39: + initialIP = ((((usqInt)localIP)) + 2) - (GIV(method) + BaseHeaderSize); + + /* begin closureIn:numArgs:instructionPointer:numCopiedValues: */ + numSlots = ClosureFirstCopiedValueIndex + numCopied; + assert((numSlots >= 0) + && ((knownClassAtIndex(ClassBlockClosureCompactIndex)) != GIV(nilObj))); + assert((indexablePointersFormat()) == (instSpecOfClass(knownClassAtIndex(ClassBlockClosureCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newClosure = 0; + goto l40; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((indexablePointersFormat())) << (formatShift()))))) + ClassBlockClosureCompactIndex); + GIV(freeStart) += numBytes; + newClosure = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l40: + + /* Assume: have just allocated a new closure; it must be young. Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureOuterContextIndex, newClosure, context)); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord()))))),context); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureStartPCIndex, newClosure, (((usqInt)initialIP << 3) | 1))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))),(((usqInt)initialIP << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureNumArgsIndex, newClosure, ((numArgs << 3) | 1))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord()))))),((numArgs << 3) | 1)); + if (numCopied > 0) { + for (i = 0; i < numCopied; i += 1) { + /* Assume: have just allocated a new BlockClosure; it must be young. + Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(i + ClosureFirstCopiedValueIndex, newClosure, longAt((void *)(localSP + (((numCopied - i) - 1) * BytesPerOop))))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)((i + ClosureFirstCopiedValueIndex)) << (shiftForWord()))))),longAt((void *)(localSP + (((numCopied - i) - 1) * BytesPerOop)))); + } + + /* begin internalPop: */ + localSP += numCopied * BytesPerOop; + } + localIP += blockSize; + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),newClosure); + } + BREAK; + CASE(144) // shortUnconditionalJump + CASE(432) // 176 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump); + offset = 1; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(145) // shortUnconditionalJump + CASE(433) // 177 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump1); + offset = 2; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(146) // shortUnconditionalJump + CASE(434) // 178 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump2); + offset = 3; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(147) // shortUnconditionalJump + CASE(435) // 179 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump3); + offset = 4; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(148) // shortUnconditionalJump + CASE(436) // 180 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump4); + offset = 5; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(149) // shortUnconditionalJump + CASE(437) // 181 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump5); + offset = 6; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(150) // shortUnconditionalJump + CASE(438) // 182 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump6); + offset = 7; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(151) // shortUnconditionalJump + CASE(439) // 183 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump7); + offset = 8; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(152) // shortConditionalJumpFalse + CASE(153) // shortConditionalJumpFalse + CASE(154) // shortConditionalJumpFalse + CASE(155) // shortConditionalJumpFalse + CASE(156) // shortConditionalJumpFalse + CASE(157) // shortConditionalJumpFalse + CASE(158) // shortConditionalJumpFalse + CASE(159) // shortConditionalJumpFalse + CASE(448) // 192 shortConditionalJumpFalse + CASE(449) // 193 shortConditionalJumpFalse + CASE(450) // 194 shortConditionalJumpFalse + CASE(451) // 195 shortConditionalJumpFalse + CASE(452) // 196 shortConditionalJumpFalse + CASE(453) // 197 shortConditionalJumpFalse + CASE(454) // 198 shortConditionalJumpFalse + CASE(455) // 199 shortConditionalJumpFalse + { + sqInt boolean; + sqInt offset; + + VM_LABEL(shortConditionalJumpFalse); + offset = (currentBytecode & 7) + 1; + + /* begin jumplfFalseBy: */ + boolean = longAt(localSP); + if (boolean == GIV(falseObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(trueObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l41; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfFalseBy: */ +l41:; + } + BREAK; + CASE(160) // longUnconditionalJump + CASE(161) // longUnconditionalJump + CASE(162) // longUnconditionalJump + CASE(163) // longUnconditionalJump + CASE(164) // longUnconditionalJump + CASE(165) // longUnconditionalJump + CASE(166) // longUnconditionalJump + CASE(167) // longUnconditionalJump + { + sqInt backwardJumpCountByte; + sqInt header; + sqInt methodHeader; + sqInt offset; + sqInt switched; + + VM_LABEL(longUnconditionalJump); + offset = (((currentBytecode & 7) - 4) * 0x100) + (byteAt(++localIP)); + localIP += offset; + + /* begin ifBackwardsCheckForEvents: */ + if (offset >= 0) { + goto l42; + } + if (localSP < GIV(stackLimit)) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + switched = checkForEventsMayContextSwitch(1); + returnToExecutivepostContextSwitch(1, switched); + browserPluginReturnIfNeeded(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (switched) { + goto l42; + } + } + + /* We use the least significant byte of the flags word (which is marked as an immediate) and + subtract two each time to avoid disturbing the least significant tag bit. Since the byte is + initialized to 1 (on frame build), on first decrement it will become -1. Trip when it reaches 1 again. */ + backwardJumpCountByte = byteAt(localFP + ((VMBIGENDIAN + ? (FoxIFrameFlags + BytesPerWord) - 1 + : FoxIFrameFlags))); + if (((backwardJumpCountByte -= 2)) == 1) { + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + attemptToSwitchToMachineCode(((((oopForPointer(localIP)) - offset) - GIV(method)) - BaseHeaderSize) - 1); + } + + /* can't cog method; avoid asking to cog it again for the longest possible time. */ + backwardJumpCountByte = 0x7F; + } + else { + if (backwardJumpCountByte == -1) { + assert(minBackwardJumpCountForCompile <= 128); + backwardJumpCountByte = ((((usqInt)((minBackwardJumpCountForCompile - 1)) << 1))) + 1; + } + } + + /* begin iframeBackwardBranchByte:put: */ + byteAtput(localFP + ((VMBIGENDIAN + ? (FoxIFrameFlags + BytesPerWord) - 1 + : FoxIFrameFlags)),backwardJumpCountByte); + /* end ifBackwardsCheckForEvents: */ +l42: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(168) // longJumpIfTrue + CASE(169) // longJumpIfTrue + CASE(170) // longJumpIfTrue + CASE(171) // longJumpIfTrue + { + sqInt boolean; + sqInt offset; + + VM_LABEL(longJumpIfTrue); + offset = ((currentBytecode & 3) * 0x100) + (byteAt(++localIP)); + + /* begin jumplfTrueBy: */ + boolean = longAt(localSP); + if (boolean == GIV(trueObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(falseObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l43; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfTrueBy: */ +l43:; + } + BREAK; + CASE(172) // longJumpIfFalse + CASE(173) // longJumpIfFalse + CASE(174) // longJumpIfFalse + CASE(175) // longJumpIfFalse + { + sqInt boolean; + sqInt offset; + + VM_LABEL(longJumpIfFalse); + offset = ((currentBytecode & 3) * 0x100) + (byteAt(++localIP)); + + /* begin jumplfFalseBy: */ + boolean = longAt(localSP); + if (boolean == GIV(falseObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(trueObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l44; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfFalseBy: */ +l44:; + } + BREAK; + CASE(176) // bytecodePrimAdd + CASE(352) // 96 bytecodePrimAdd + { + sqInt arg; + double argDouble; + usqLong bits; + sqInt rcvr; + double rcvrDouble; + sqInt result; + double resultDouble; + char *sp; + sqInt tagBits; + double value; + + VM_LABEL(bytecodePrimAdd); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + result = ((rcvr >> 3)) + ((arg >> 3)); + if ((((((usqInt)(result)) >> 60) + 1) & 15) <= 1) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),(((usqInt)result << 3) | 1)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l45; + } + } + else { + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin primitiveFloatAdd:toArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvr & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvrDouble = value; + goto l46; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvrDouble = ((double) ((rcvr >> 3)) ); + goto l46; + } + } + else { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvr + BaseHeaderSize, resultDouble); + rcvrDouble = resultDouble; + goto l46; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvrDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l46: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = arg & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(arg)); + bits = ((((usqInt)arg))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + argDouble = value; + goto l47; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + argDouble = ((double) ((arg >> 3)) ); + goto l47; + } + } + else { + if (((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(arg + BaseHeaderSize, resultDouble); + argDouble = resultDouble; + goto l47; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + argDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l47: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvrDouble + argDouble)); + GIV(stackPointer) = sp; + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l45; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (0U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l45:; + BREAK; + CASE(177) // bytecodePrimSubtract + CASE(353) // 97 bytecodePrimSubtract + { + sqInt arg; + double argDouble; + usqLong bits; + sqInt rcvr; + double rcvrDouble; + sqInt result; + double resultDouble; + char *sp; + sqInt tagBits; + double value; + + VM_LABEL(bytecodePrimSubtract); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + result = ((rcvr >> 3)) - ((arg >> 3)); + if ((((((usqInt)(result)) >> 60) + 1) & 15) <= 1) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),(((usqInt)result << 3) | 1)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l48; + } + } + else { + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin primitiveFloatSubtract:fromArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvr & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvrDouble = value; + goto l49; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvrDouble = ((double) ((rcvr >> 3)) ); + goto l49; + } + } + else { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvr + BaseHeaderSize, resultDouble); + rcvrDouble = resultDouble; + goto l49; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvrDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l49: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = arg & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(arg)); + bits = ((((usqInt)arg))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + argDouble = value; + goto l50; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + argDouble = ((double) ((arg >> 3)) ); + goto l50; + } + } + else { + if (((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(arg + BaseHeaderSize, resultDouble); + argDouble = resultDouble; + goto l50; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + argDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l50: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvrDouble - argDouble)); + GIV(stackPointer) = sp; + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l48; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (2U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l48:; + BREAK; + CASE(178) // bytecodePrimLessThan + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimLessThan); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheat: */ + if (rcvr < arg) { + /* goto booleanCheatTrue */ + } + else { + goto booleanCheatFalse; + } + goto l51; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatLessthanArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l51; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (4U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l51: + + booleanCheatTrue: + /* booleanCheatTrue */ + { + sqInt bytecode; + sqInt offset; + + VM_LABEL(booleanCheatTrue); + /* assume next bytecode is jumpIfFalse (99%) */ + bytecode = byteAt(++localIP); + + /* begin internalPop: */ + localSP += 2 * BytesPerOop; + if ((bytecode < 173) + && (bytecode > 151)) { + if (bytecode < 160) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l197; + } + + /* short jumpIfFalse 152 - 159 */ + if (bytecode == 172) { + /* fetchByte */ + byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l197; + } + + /* long jumpIfFalse */ + if (bytecode > 167) { + offset = ((((usqInt)((bytecode - 168)) << 8))) + (byteAt(++localIP)); + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l197; + } + } + + /* not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result */ + currentBytecode = bytecode + GIV(bytecodeSetSelector); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(trueObj)); + } + /* end case */ +l197:; + BREAK; + CASE(179) // bytecodePrimGreaterThan + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimGreaterThan); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheat: */ + if (rcvr > arg) { + goto booleanCheatTrue; + } + else { + /* goto booleanCheatFalse */ + } + goto l52; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatGreaterthanArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l52; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (6U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l52: + + booleanCheatFalse: + /* booleanCheatFalse */ + { + sqInt bytecode; + sqInt offset; + + VM_LABEL(booleanCheatFalse); + /* assume next bytecode is jumpIfFalse (99%) */ + bytecode = byteAt(++localIP); + + /* begin internalPop: */ + localSP += 2 * BytesPerOop; + if ((bytecode < 160) + && (bytecode > 151)) { + /* begin jump: */ + localIP = (localIP + (bytecode - 151)) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l198; + } + + /* short jumpIfFalse */ + if (bytecode == 172) { + offset = byteAt(++localIP); + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l198; + } + + /* long jumpIfFalse + not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result */ + currentBytecode = bytecode + GIV(bytecodeSetSelector); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(falseObj)); + } + /* end case */ +l198:; + BREAK; + CASE(180) // bytecodePrimLessOrEqual + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimLessOrEqual); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheat: */ + if (rcvr <= arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l53; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatLessOrEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l53; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (8U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l53:; + BREAK; + CASE(181) // bytecodePrimGreaterOrEqual + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimGreaterOrEqual); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheat: */ + if (rcvr >= arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l54; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatGreaterOrEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l54; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (10U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l54:; + BREAK; + CASE(182) // bytecodePrimEqual + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimEqual); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* begin booleanCheat: */ + if (rcvr == arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l55; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l55; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (12U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l55:; + BREAK; + CASE(183) // bytecodePrimNotEqual + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimNotEqual); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* begin booleanCheat: */ + if (rcvr != arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l56; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatFalse; + } + else { + goto booleanCheatTrue; + } + goto l56; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (14U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l56:; + BREAK; + CASE(184) // bytecodePrimMultiply + CASE(360) // 104 bytecodePrimMultiply + { + sqInt arg; + double argDouble; + usqLong bits; + sqInt oop; + sqInt overflow; + sqInt rcvr; + double rcvrDouble; + sqInt result; + double resultDouble; + char *sp; + sqInt tagBits; + double value; + + VM_LABEL(bytecodePrimMultiply); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + rcvr = (rcvr >> 3); + arg = (arg >> 3); + overflow = (rcvr > 0 + ? (arg > 0 + ? rcvr > ((MaxSmallInteger) / arg) + : arg < ((MinSmallInteger) / rcvr)) + : (arg > 0 + ? rcvr < ((MinSmallInteger) / arg) + : (rcvr < 0) + && (arg < ((MaxSmallInteger) / rcvr)))); + if (!overflow) { + result = rcvr * arg; + oop = (((usqInt)result << 3) | 1); + + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),oop); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l57; + } + } + else { + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin primitiveFloatMultiply:byArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvr & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvrDouble = value; + goto l58; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvrDouble = ((double) ((rcvr >> 3)) ); + goto l58; + } + } + else { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvr + BaseHeaderSize, resultDouble); + rcvrDouble = resultDouble; + goto l58; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvrDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l58: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = arg & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(arg)); + bits = ((((usqInt)arg))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + argDouble = value; + goto l59; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + argDouble = ((double) ((arg >> 3)) ); + goto l59; + } + } + else { + if (((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(arg + BaseHeaderSize, resultDouble); + argDouble = resultDouble; + goto l59; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + argDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l59: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvrDouble * argDouble)); + GIV(stackPointer) = sp; + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l57; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (16U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l57:; + BREAK; + CASE(185) // bytecodePrimDivide + CASE(361) // 105 bytecodePrimDivide + { + sqInt arg; + double argDouble; + usqLong bits; + sqInt rcvr; + double rcvrDouble; + sqInt result; + double resultDouble; + char *sp; + sqInt tagBits; + double value; + + VM_LABEL(bytecodePrimDivide); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + rcvr = (rcvr >> 3); + arg = (arg >> 3); + if ((arg != 0) + && ((rcvr % arg) == 0)) { + result = rcvr / arg; + + /* generates C / operation */ + if ((((((usqInt)(result)) >> 60) + 1) & 15) <= 1) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),(((usqInt)result << 3) | 1)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l60; + } + } + } + else { + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin primitiveFloatDivide:byArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvr & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvrDouble = value; + goto l61; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvrDouble = ((double) ((rcvr >> 3)) ); + goto l61; + } + } + else { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvr + BaseHeaderSize, resultDouble); + rcvrDouble = resultDouble; + goto l61; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvrDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l61: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = arg & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(arg)); + bits = ((((usqInt)arg))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + argDouble = value; + goto l62; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + argDouble = ((double) ((arg >> 3)) ); + goto l62; + } + } + else { + if (((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(arg + BaseHeaderSize, resultDouble); + argDouble = resultDouble; + goto l62; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + argDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l62: + + /* begin success: */ + if (!(argDouble != 0.0)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvrDouble / argDouble)); + GIV(stackPointer) = sp; + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l60; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (18U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l60:; + BREAK; + CASE(186) // bytecodePrimMod + CASE(362) // 106 bytecodePrimMod + { + sqInt mod; + + VM_LABEL(bytecodePrimMod); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + mod = doPrimitiveModby(longAt(localSP + (1 * BytesPerOop)), longAt(localSP)); + if (!GIV(primFailCode)) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),(((usqInt)mod << 3) | 1)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l63; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (20U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l63:; + BREAK; + CASE(187) // bytecodePrimMakePoint + CASE(363) // 107 bytecodePrimMakePoint + { + sqInt arg; + usqInt newObj; + usqInt numBytes; + usqInt pt; + sqInt rcvr; + + VM_LABEL(bytecodePrimMakePoint); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((/* isFloatOrInt: */ + (((rcvr & (tagMask())) != 0) + ? !(((rcvr & (characterTag())) != 0)) + : ((longAt((void *)(rcvr))) & (classIndexMask())) == ClassFloatCompactIndex)) + && (/* isFloatOrInt: */ + (((arg & (tagMask())) != 0) + ? !(((arg & (characterTag())) != 0)) + : ((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex))) { + /* begin eeInstantiatePoint */ + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert(((YIndex + 1) >= 0) + && ((knownClassAtIndex(ClassPointCompactIndex)) != GIV(nilObj))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(knownClassAtIndex(ClassPointCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert((YIndex + 1) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (((YIndex + 1) < 1 + ? 8 /* allocationUnit */ + : (YIndex + 1) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + pt = 0; + goto l65; + } + } + long64Atput((void *)(newObj),((((((usqLong) (YIndex + 1))) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + ClassPointCompactIndex); + GIV(freeStart) += numBytes; + pt = newObj; + /* end eeInstantiatePoint */ +l65: + + /* No need to check since new object is always new. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pt)) + && (!(isForwarded(pt)))); + assert(validStorePointerUncheckedArgs(XIndex, pt, rcvr)); + longAtput((void *)((pt + BaseHeaderSize) + ((((usqInt)(XIndex) << (shiftForWord()))))),rcvr); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pt)) + && (!(isForwarded(pt)))); + assert(validStorePointerUncheckedArgs(YIndex, pt, arg)); + longAtput((void *)((pt + BaseHeaderSize) + ((((usqInt)(YIndex) << (shiftForWord()))))),arg); + + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),pt); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l64; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (22U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l64:; + BREAK; + CASE(188) // bytecodePrimBitShift + CASE(364) // 108 bytecodePrimBitShift + { + sqInt integerArgument; + sqInt integerReceiver; + sqInt shifted; + + VM_LABEL(bytecodePrimBitShift); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin primitiveBitShift */ + integerArgument = longAt(GIV(stackPointer)); + if (!((((integerArgument) & 7) == 1))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + goto l67; + } + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + integerReceiver = signed64BitValueOf(integerReceiver); + if (!GIV(primFailCode)) { + if (((integerArgument = (integerArgument >> 3))) >= 0) { + if (!(integerArgument <= 61 /* numSmallIntegerBits */)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + goto l67; + } + shifted = ((sqInt)((usqInt)(integerReceiver) << integerArgument)); + if (!(integerReceiver == ((shifted) >> integerArgument))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + goto l67; + } + } + else { + if (!(integerArgument >= (-61 /* numSmallIntegerBits */))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + goto l67; + } + shifted = (integerReceiver) >> (0 - integerArgument); + } + + /* Left shift -- must fail bits would be lost + Right shift -- OK to lose bits */ + shifted = ((((((usqInt)(shifted)) >> 60) + 1) & 15) <= 1 + ? (((usqInt)shifted << 3) | 1) + : signed64BitIntegerFor(shifted)); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),shifted); + } + /* end primitiveBitShift */ +l67: + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l66; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (24U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l66:; + BREAK; + CASE(189) // bytecodePrimDiv + CASE(365) // 109 bytecodePrimDiv + { + sqInt quotient; + + VM_LABEL(bytecodePrimDiv); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + quotient = doPrimitiveDivby(longAt(localSP + (1 * BytesPerOop)), longAt(localSP)); + if (!GIV(primFailCode)) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),(((usqInt)quotient << 3) | 1)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l68; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (26U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l68:; + BREAK; + CASE(190) // bytecodePrimBitAnd + CASE(366) // 110 bytecodePrimBitAnd + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimBitAnd); + arg = longAt(localSP); + rcvr = longAt(localSP + (1 * BytesPerOop)); + if (((((arg) & 7) == 1)) + && ((((rcvr) & 7) == 1))) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),arg & rcvr); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l69; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + primitiveBitAnd(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l69; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (28U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l69:; + BREAK; + CASE(191) // bytecodePrimBitOr + CASE(367) // 111 bytecodePrimBitOr + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimBitOr); + arg = longAt(localSP); + rcvr = longAt(localSP + (1 * BytesPerOop)); + if (((((arg) & 7) == 1)) + && ((((rcvr) & 7) == 1))) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),arg | rcvr); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l70; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + primitiveBitOr(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l70; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (30U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l70:; + BREAK; + CASE(192) // bytecodePrimAt + CASE(368) // 112 bytecodePrimAt + { + VM_LABEL(bytecodePrimAt); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (32U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + BREAK; + CASE(193) // bytecodePrimAtPut + CASE(369) // 113 bytecodePrimAtPut + { + VM_LABEL(bytecodePrimAtPut); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (34U << (shiftForWord())))); + GIV(argumentCount) = 2; + goto normalSend; + } + BREAK; + CASE(194) // bytecodePrimSize + CASE(370) // 114 bytecodePrimSize + { + sqInt ccIndex; + sqInt fmt; + sqInt isArray; + sqInt isString; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt sz; + + VM_LABEL(bytecodePrimSize); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(localSP); + + /* Shortcut the mega-lookup for ByteString and Array, the two big consumers of cycles + here. Both of these have compact class indices and neither has any added fields. */ + + /* begin isInstanceOfClassByteString: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((rcvr & (tagMask())) != 0)) { + isString = 0; + goto l72; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isString = ClassByteStringCompactIndex == ccIndex; + /* end isInstanceOfClassByteString: */ +l72: + if (isString) { + /* begin lengthOf: */ + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + sz = numSlots; + goto l73; + } + if (fmt >= (firstByteFormat())) { + sz = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l73; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + sz = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l73; + } + if (fmt >= (firstLongFormat())) { + sz = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l73; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + sz = numSlots; + goto l73; + } + + /* fmt = self forwardedFormat */ + sz = 0; + /* end lengthOf: */ +l73: + + /* internalStackTopPut: */ + longAtput(localSP,(((usqInt)sz << 3) | 1)); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l71; + } + + /* begin isInstanceOfClassArray: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((rcvr & (tagMask())) != 0)) { + isArray = 0; + goto l74; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isArray = ClassArrayCompactIndex == ccIndex; + /* end isInstanceOfClassArray: */ +l74: + if (isArray) { + /* begin lengthOf: */ + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + sz = numSlots; + goto l75; + } + if (fmt >= (firstByteFormat())) { + sz = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l75; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + sz = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l75; + } + if (fmt >= (firstLongFormat())) { + sz = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l75; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + sz = numSlots; + goto l75; + } + + /* fmt = self forwardedFormat */ + sz = 0; + /* end lengthOf: */ +l75: + + /* internalStackTopPut: */ + longAtput(localSP,(((usqInt)sz << 3) | 1)); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l71; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (36U << (shiftForWord())))); + GIV(argumentCount) = 0; + goto normalSend; + } + /* end case */ +l71:; + BREAK; + CASE(195) // bytecodePrimNext + CASE(371) // 115 bytecodePrimNext + { + VM_LABEL(bytecodePrimNext); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (38U << (shiftForWord())))); + GIV(argumentCount) = ((fetchPointerofObject(39, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))))) >> 3); + goto normalSend; + } + BREAK; + CASE(196) // bytecodePrimNextPut + CASE(372) // 116 bytecodePrimNextPut + { + VM_LABEL(bytecodePrimNextPut); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (40U << (shiftForWord())))); + GIV(argumentCount) = ((fetchPointerofObject(41, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))))) >> 3); + goto normalSend; + } + BREAK; + CASE(197) // bytecodePrimAtEnd + CASE(373) // 117 bytecodePrimAtEnd + { + VM_LABEL(bytecodePrimAtEnd); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (42U << (shiftForWord())))); + GIV(argumentCount) = ((fetchPointerofObject(43, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))))) >> 3); + goto normalSend; + } + BREAK; + CASE(198) // bytecodePrimIdentical + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimIdentical); + rcvr = longAt(localSP + (1 * BytesPerOop)); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = handleSpecialSelectorSendFaultForfpsp(rcvr, localFP, localSP); + } + arg = longAt(localSP); + if (/* isOopForwarded: */ + ((!(arg & (tagMask())))) + && ((!((longAt((void *)(arg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + arg = handleSpecialSelectorSendFaultForfpsp(arg, localFP, localSP); + } + + /* begin booleanCheat: */ + if (rcvr == arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + } + BREAK; + CASE(199) // bytecodePrimClass + CASE(375) // 119 bytecodePrimClass + { + sqInt aValue; + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(bytecodePrimClass); + rcvr = longAt(localSP); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = handleSpecialSelectorSendFaultForfpsp(rcvr, localFP, localSP); + } + aValue = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)); + + /* begin internalStackTopPut: */ + longAtput(localSP,aValue); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(200) // bytecodePrimNotIdentical + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimNotIdentical); + rcvr = longAt(localSP + (1 * BytesPerOop)); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = handleSpecialSelectorSendFaultForfpsp(rcvr, localFP, localSP); + } + arg = longAt(localSP); + if (/* isOopForwarded: */ + ((!(arg & (tagMask())))) + && ((!((longAt((void *)(arg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + arg = handleSpecialSelectorSendFaultForfpsp(arg, localFP, localSP); + } + + /* begin booleanCheat: */ + if (rcvr != arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + } + BREAK; + CASE(201) // bytecodePrimValue + CASE(377) // 121 bytecodePrimValue + { + sqInt ccIndex; + sqInt isBlock; + sqInt rcvr; + + VM_LABEL(bytecodePrimValue); + rcvr = longAt(localSP); + GIV(argumentCount) = 0; + + /* begin isInstanceOfClassBlockClosure: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((rcvr & (tagMask())) != 0)) { + isBlock = 0; + goto l77; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isBlock = ClassBlockClosureCompactIndex == ccIndex; + /* end isInstanceOfClassBlockClosure: */ +l77: + if (isBlock) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + primitiveClosureValue(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l76; + } + GIV(primFailCode) = 0; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (50U << (shiftForWord())))); + goto normalSend; + } + /* end case */ +l76:; + BREAK; + CASE(202) // bytecodePrimValueWithArg + CASE(378) // 122 bytecodePrimValueWithArg + { + sqInt ccIndex; + sqInt isBlock; + sqInt rcvr; + + VM_LABEL(bytecodePrimValueWithArg); + rcvr = longAt(localSP + (1 * BytesPerOop)); + GIV(argumentCount) = 1; + + /* begin isInstanceOfClassBlockClosure: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((rcvr & (tagMask())) != 0)) { + isBlock = 0; + goto l79; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isBlock = ClassBlockClosureCompactIndex == ccIndex; + /* end isInstanceOfClassBlockClosure: */ +l79: + if (isBlock) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + primitiveClosureValue(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l78; + } + GIV(primFailCode) = 0; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (52U << (shiftForWord())))); + goto normalSend; + } + /* end case */ +l78:; + BREAK; + CASE(203) // bytecodePrimDo + CASE(379) // 123 bytecodePrimDo + { + VM_LABEL(bytecodePrimDo); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (54U << (shiftForWord())))); + GIV(argumentCount) = ((fetchPointerofObject(55, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))))) >> 3); + goto normalSend; + } + BREAK; + CASE(204) // bytecodePrimNew + CASE(380) // 124 bytecodePrimNew + { + VM_LABEL(bytecodePrimNew); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (56U << (shiftForWord())))); + GIV(argumentCount) = 0; + goto normalSend; + } + BREAK; + CASE(205) // bytecodePrimNewWithArg + CASE(381) // 125 bytecodePrimNewWithArg + { + VM_LABEL(bytecodePrimNewWithArg); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (58U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + BREAK; + CASE(206) // bytecodePrimPointX + CASE(382) // 126 bytecodePrimPointX + { + sqInt ccIndex; + sqInt isClassOfNonImmequalTocompactClassIndexRV; + sqInt rcvr; + + VM_LABEL(bytecodePrimPointX); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(localSP); + if ((!(rcvr & (tagMask())))) { + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isClassOfNonImmequalTocompactClassIndexRV = ClassPointCompactIndex == ccIndex; + if (isClassOfNonImmequalTocompactClassIndexRV) { + longAtput(localSP,longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(XIndex) << (shiftForWord()))))))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l80; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (60U << (shiftForWord())))); + GIV(argumentCount) = 0; + goto normalSend; + } + /* end case */ +l80:; + BREAK; + CASE(207) // bytecodePrimPointY + CASE(383) // 127 bytecodePrimPointY + { + sqInt ccIndex; + sqInt isClassOfNonImmequalTocompactClassIndexRV; + sqInt rcvr; + + VM_LABEL(bytecodePrimPointY); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(localSP); + if ((!(rcvr & (tagMask())))) { + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isClassOfNonImmequalTocompactClassIndexRV = ClassPointCompactIndex == ccIndex; + if (isClassOfNonImmequalTocompactClassIndexRV) { + longAtput(localSP,longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(YIndex) << (shiftForWord()))))))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l81; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (0x3EU << (shiftForWord())))); + GIV(argumentCount) = 0; + goto normalSend; + } + /* end case */ +l81:; + BREAK; + CASE(208) // sendLiteralSelector0ArgsBytecode + CASE(209) // sendLiteralSelector0ArgsBytecode + CASE(210) // sendLiteralSelector0ArgsBytecode + CASE(211) // sendLiteralSelector0ArgsBytecode + CASE(212) // sendLiteralSelector0ArgsBytecode + CASE(213) // sendLiteralSelector0ArgsBytecode + CASE(214) // sendLiteralSelector0ArgsBytecode + CASE(215) // sendLiteralSelector0ArgsBytecode + CASE(216) // sendLiteralSelector0ArgsBytecode + CASE(217) // sendLiteralSelector0ArgsBytecode + CASE(218) // sendLiteralSelector0ArgsBytecode + CASE(219) // sendLiteralSelector0ArgsBytecode + CASE(220) // sendLiteralSelector0ArgsBytecode + CASE(221) // sendLiteralSelector0ArgsBytecode + CASE(222) // sendLiteralSelector0ArgsBytecode + CASE(223) // sendLiteralSelector0ArgsBytecode + CASE(384) // 128 sendLiteralSelector0ArgsBytecode + CASE(385) // 129 sendLiteralSelector0ArgsBytecode + CASE(386) // 130 sendLiteralSelector0ArgsBytecode + CASE(387) // 131 sendLiteralSelector0ArgsBytecode + CASE(388) // 132 sendLiteralSelector0ArgsBytecode + CASE(389) // 133 sendLiteralSelector0ArgsBytecode + CASE(390) // 134 sendLiteralSelector0ArgsBytecode + CASE(391) // 135 sendLiteralSelector0ArgsBytecode + CASE(392) // 136 sendLiteralSelector0ArgsBytecode + CASE(393) // 137 sendLiteralSelector0ArgsBytecode + CASE(394) // 138 sendLiteralSelector0ArgsBytecode + CASE(395) // 139 sendLiteralSelector0ArgsBytecode + CASE(396) // 140 sendLiteralSelector0ArgsBytecode + CASE(397) // 141 sendLiteralSelector0ArgsBytecode + CASE(398) // 142 sendLiteralSelector0ArgsBytecode + CASE(399) // 143 sendLiteralSelector0ArgsBytecode + { + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(sendLiteralSelector0ArgsBytecode); + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((currentBytecode & 15) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = 0; + rcvr = longAt(localSP); + lkupClassTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + assert(lkupClassTag != (nilObject())); + goto commonSendOrdinary; + } + BREAK; + CASE(224) // sendLiteralSelector1ArgBytecode + CASE(225) // sendLiteralSelector1ArgBytecode + CASE(226) // sendLiteralSelector1ArgBytecode + CASE(227) // sendLiteralSelector1ArgBytecode + CASE(228) // sendLiteralSelector1ArgBytecode + CASE(229) // sendLiteralSelector1ArgBytecode + CASE(230) // sendLiteralSelector1ArgBytecode + CASE(231) // sendLiteralSelector1ArgBytecode + CASE(232) // sendLiteralSelector1ArgBytecode + CASE(233) // sendLiteralSelector1ArgBytecode + CASE(234) // sendLiteralSelector1ArgBytecode + CASE(235) // sendLiteralSelector1ArgBytecode + CASE(236) // sendLiteralSelector1ArgBytecode + CASE(237) // sendLiteralSelector1ArgBytecode + CASE(238) // sendLiteralSelector1ArgBytecode + CASE(239) // sendLiteralSelector1ArgBytecode + CASE(400) // 144 sendLiteralSelector1ArgBytecode + CASE(401) // 145 sendLiteralSelector1ArgBytecode + CASE(402) // 146 sendLiteralSelector1ArgBytecode + CASE(403) // 147 sendLiteralSelector1ArgBytecode + CASE(404) // 148 sendLiteralSelector1ArgBytecode + CASE(405) // 149 sendLiteralSelector1ArgBytecode + CASE(406) // 150 sendLiteralSelector1ArgBytecode + CASE(407) // 151 sendLiteralSelector1ArgBytecode + CASE(408) // 152 sendLiteralSelector1ArgBytecode + CASE(409) // 153 sendLiteralSelector1ArgBytecode + CASE(410) // 154 sendLiteralSelector1ArgBytecode + CASE(411) // 155 sendLiteralSelector1ArgBytecode + CASE(412) // 156 sendLiteralSelector1ArgBytecode + CASE(413) // 157 sendLiteralSelector1ArgBytecode + CASE(414) // 158 sendLiteralSelector1ArgBytecode + CASE(415) // 159 sendLiteralSelector1ArgBytecode + { + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(sendLiteralSelector1ArgBytecode); + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((currentBytecode & 15) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = 1; + rcvr = longAt(localSP + (1 * BytesPerOop)); + lkupClassTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + assert(lkupClassTag != (nilObject())); + goto commonSendOrdinary; + } + BREAK; + CASE(240) // sendLiteralSelector2ArgsBytecode + CASE(241) // sendLiteralSelector2ArgsBytecode + CASE(242) // sendLiteralSelector2ArgsBytecode + CASE(243) // sendLiteralSelector2ArgsBytecode + CASE(244) // sendLiteralSelector2ArgsBytecode + CASE(245) // sendLiteralSelector2ArgsBytecode + CASE(246) // sendLiteralSelector2ArgsBytecode + CASE(247) // sendLiteralSelector2ArgsBytecode + CASE(248) // sendLiteralSelector2ArgsBytecode + CASE(249) // sendLiteralSelector2ArgsBytecode + CASE(250) // sendLiteralSelector2ArgsBytecode + CASE(251) // sendLiteralSelector2ArgsBytecode + CASE(252) // sendLiteralSelector2ArgsBytecode + CASE(253) // sendLiteralSelector2ArgsBytecode + CASE(254) // sendLiteralSelector2ArgsBytecode + CASE(255) // sendLiteralSelector2ArgsBytecode + CASE(416) // 160 sendLiteralSelector2ArgsBytecode + CASE(417) // 161 sendLiteralSelector2ArgsBytecode + CASE(418) // 162 sendLiteralSelector2ArgsBytecode + CASE(419) // 163 sendLiteralSelector2ArgsBytecode + CASE(420) // 164 sendLiteralSelector2ArgsBytecode + CASE(421) // 165 sendLiteralSelector2ArgsBytecode + CASE(422) // 166 sendLiteralSelector2ArgsBytecode + CASE(423) // 167 sendLiteralSelector2ArgsBytecode + CASE(424) // 168 sendLiteralSelector2ArgsBytecode + CASE(425) // 169 sendLiteralSelector2ArgsBytecode + CASE(426) // 170 sendLiteralSelector2ArgsBytecode + CASE(427) // 171 sendLiteralSelector2ArgsBytecode + CASE(428) // 172 sendLiteralSelector2ArgsBytecode + CASE(429) // 173 sendLiteralSelector2ArgsBytecode + CASE(430) // 174 sendLiteralSelector2ArgsBytecode + CASE(431) // 175 sendLiteralSelector2ArgsBytecode + { + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(sendLiteralSelector2ArgsBytecode); + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((currentBytecode & 15) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = 2; + rcvr = longAt(localSP + (2 * BytesPerOop)); + lkupClassTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + assert(lkupClassTag != (nilObject())); + goto commonSendOrdinary; + } + BREAK; + CASE(338) // 82 extPushPseudoVariable + { + sqInt objOop; + sqInt theThingToPush; + + VM_LABEL(extPushPseudoVariable); + switch (extB) { + case 0: + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + theThingToPush = longAt(localFP + FoxThisContext); + goto l82; + } + theThingToPush = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l82:; + break; + case 1: + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + theThingToPush = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + break; + default: + goto respondToUnknownBytecode; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),theThingToPush); + extB = 0; + numExtB = 0; + } + BREAK; + CASE(340) // 84 unknownBytecode + CASE(341) // 85 unknownBytecode + CASE(342) // 86 unknownBytecode + CASE(343) // 87 unknownBytecode + CASE(473) // 217 unconditionalTrapBytecode + CASE(474) // 218 unknownBytecode + CASE(475) // 219 unknownBytecode + CASE(476) // 220 unknownBytecode + CASE(477) // 221 unknownBytecode + CASE(478) // 222 unknownBytecode + CASE(479) // 223 unknownBytecode + CASE(486) // 230 unknownBytecode + CASE(502) // 246 unknownBytecode + CASE(503) // 247 unknownBytecode + CASE(510) // 254 unknownBytecode + CASE(511) // 255 unknownBytecode + { + VM_LABEL(unknownBytecode); + goto respondToUnknownBytecode; + } + BREAK; + CASE(344) // 88 returnReceiver + { + VM_LABEL(returnReceiver1); + localReturnValue = longAt(localFP + FoxIFReceiver); + goto commonReturn; + } + BREAK; + CASE(349) // 93 returnNilFromBlock + { + VM_LABEL(returnNilFromBlock); + localReturnValue = GIV(nilObj); + goto commonCallerReturn; + } + BREAK; + CASE(350) // 94 returnTopFromBlock + { + VM_LABEL(returnTopFromBlock1); + localReturnValue = longAt(localSP); + goto commonCallerReturn; + } + BREAK; + CASE(351) // 95 extNopBytecode + { + VM_LABEL(extNopBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + numExtB = (extA = (extB = 0)); + } + BREAK; + CASE(354) // 98 bytecodePrimLessThanSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimLessThanSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheatSistaV1: */ + if (rcvr < arg) { + /* goto booleanCheatTrueSistaV1 */ + } + else { + goto booleanCheatFalseSistaV1; + } + goto l89; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatLessthanArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l89; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (4U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l89: + + booleanCheatTrueSistaV1: + /* booleanCheatTrueSistaV1 */ + { + sqInt bytecode; + sqInt offset; + + VM_LABEL(booleanCheatTrueSistaV1); + /* assume next bytecode is jumpIfFalse (99%) */ + bytecode = byteAt(++localIP); + + /* begin internalPop: */ + localSP += 2 * BytesPerOop; + if (bytecode >= 192) { + if (bytecode <= 199) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l199; + } + + /* short jumpIfFalse 192 - 199 */ + if (bytecode == 239) { + /* fetchByte */ + byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l199; + } + + /* long jumpIfFalse */ + if (bytecode == 238) { + offset = byteAt(++localIP); + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l199; + } + } + + /* not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result */ + currentBytecode = bytecode + GIV(bytecodeSetSelector); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(trueObj)); + } + /* end case */ +l199:; + BREAK; + CASE(355) // 99 bytecodePrimGreaterThanSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimGreaterThanSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheatSistaV1: */ + if (rcvr > arg) { + goto booleanCheatTrueSistaV1; + } + else { + /* goto booleanCheatFalseSistaV1 */ + } + goto l90; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatGreaterthanArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l90; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (6U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l90: + + booleanCheatFalseSistaV1: + /* booleanCheatFalseSistaV1 */ + { + sqInt bytecode; + sqInt offset; + + VM_LABEL(booleanCheatFalseSistaV1); + /* assume next bytecode is jumpIfFalse (99%) */ + bytecode = byteAt(++localIP); + + /* begin internalPop: */ + localSP += 2 * BytesPerOop; + if ((bytecode < 199) + && (bytecode > 191)) { + /* begin jump: */ + localIP = (localIP + (bytecode - 191)) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l200; + } + + /* short jumpIfFalse */ + if (bytecode == 239) { + offset = byteAt(++localIP); + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l200; + } + + /* long jumpIfFalse + not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result */ + currentBytecode = bytecode + GIV(bytecodeSetSelector); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(falseObj)); + } + /* end case */ +l200:; + BREAK; + CASE(356) // 100 bytecodePrimLessOrEqualSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimLessOrEqualSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheatSistaV1: */ + if (rcvr <= arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l91; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatLessOrEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l91; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (8U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l91:; + BREAK; + CASE(357) // 101 bytecodePrimGreaterOrEqualSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimGreaterOrEqualSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheatSistaV1: */ + if (rcvr >= arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l92; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatGreaterOrEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l92; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (10U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l92:; + BREAK; + CASE(358) // 102 bytecodePrimEqualSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimEqualSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* begin booleanCheatSistaV1: */ + if (rcvr == arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l93; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l93; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (12U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l93:; + BREAK; + CASE(359) // 103 bytecodePrimNotEqualSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimNotEqualSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* begin booleanCheatSistaV1: */ + if (rcvr != arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l94; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatFalseSistaV1; + } + else { + goto booleanCheatTrueSistaV1; + } + goto l94; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (14U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l94:; + BREAK; + CASE(374) // 118 bytecodePrimIdenticalSistaV1 + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimIdenticalSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = handleSpecialSelectorSendFaultForfpsp(rcvr, localFP, localSP); + } + if (/* isOopForwarded: */ + ((!(arg & (tagMask())))) + && ((!((longAt((void *)(arg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + arg = handleSpecialSelectorSendFaultForfpsp(arg, localFP, localSP); + } + + /* begin booleanCheatSistaV1: */ + if (rcvr == arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + } + BREAK; + CASE(376) // 120 bytecodePrimNotIdenticalSistaV1 + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimNotIdenticalSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = handleSpecialSelectorSendFaultForfpsp(rcvr, localFP, localSP); + } + if (/* isOopForwarded: */ + ((!(arg & (tagMask())))) + && ((!((longAt((void *)(arg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + arg = handleSpecialSelectorSendFaultForfpsp(arg, localFP, localSP); + } + + /* begin booleanCheatSistaV1: */ + if (rcvr != arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + } + BREAK; + CASE(440) // 184 shortConditionalJumpTrue + CASE(441) // 185 shortConditionalJumpTrue + CASE(442) // 186 shortConditionalJumpTrue + CASE(443) // 187 shortConditionalJumpTrue + CASE(444) // 188 shortConditionalJumpTrue + CASE(445) // 189 shortConditionalJumpTrue + CASE(446) // 190 shortConditionalJumpTrue + CASE(447) // 191 shortConditionalJumpTrue + { + sqInt boolean; + sqInt offset; + + VM_LABEL(shortConditionalJumpTrue); + offset = (currentBytecode & 7) + 1; + + /* begin jumplfTrueBy: */ + boolean = longAt(localSP); + if (boolean == GIV(trueObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(falseObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l120; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfTrueBy: */ +l120:; + } + BREAK; + CASE(480) // 224 extABytecode + { + VM_LABEL(extABytecode); + extA = (((((usqInt)(extA) << 8)))) + (byteAt(++localIP)); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(481) // 225 extBBytecode + { + sqInt byte; + + VM_LABEL(extBBytecode); + byte = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + extB = ((numExtB == 0) + && (byte > 0x7F) + ? byte - 0x100 + : (((((usqInt)(extB) << 8)))) + byte); + numExtB += 1; + } + BREAK; + CASE(482) // 226 extPushReceiverVariableBytecode + { + sqInt callerContextOrNil; + char *callerFP; + sqInt index; + sqInt obj; + sqInt object; + sqInt senderOop; + char *spouseFP; + StackPage *thePage; + sqInt value; + + VM_LABEL(extPushReceiverVariableBytecode); + index = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + extA = 0; + + /* begin pushMaybeContextReceiverVariable: */ + obj = longAt(localFP + FoxIFReceiver); + if ((index <= StackPointerIndex) + && (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin instVar:ofContext: */ + assert(index < MethodIndex); + assert(isContext(obj)); + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + if (!(((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + value = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if ((index == InstructionPointerIndex) + && (((((value) & 7) == 1)) + && ((((int) value)) < 0))) { + /* begin internalMustMapMachineCodePC:context: */ + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + value = mustMapMachineCodePCcontext((value >> 3), obj); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + } + object = value; + goto l129; + } + if (isWidowedContext(obj)) { + object = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + goto l129; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!index) { + /* begin ensureCallerContext: */ + callerFP = ((char *)(longAt(spouseFP + FoxSavedFP))); + if (!callerFP) { + /* begin frameCallerContext: */ + assert(isBaseFrame(spouseFP)); + thePage = stackPageAtpages(pageIndexFor(spouseFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + object = callerContextOrNil; + goto l129; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP))); + object = longAt(callerFP + FoxThisContext); + goto l129; + } + object = marryFrameSP( + callerFP, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(spouseFP))), + (spouseFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(spouseFP))->cmNumArgs) + : byteAt((spouseFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + goto l129; + } + if (index == StackPointerIndex) { + assert((ReceiverIndex + (stackPointerIndexForFrame(spouseFP))) < (lengthOf(obj))); + object = (((usqInt)(stackPointerIndexForFrame(spouseFP)) << 3) | 1); + goto l129; + } + if (index == InstructionPointerIndex) { + object = instructionPointerForFramecurrentFPcurrentIP(spouseFP, localFP, oopForPointer(localIP)); + goto l129; + } + error("bad index"); + object = 0; + /* end instVar:ofContext: */ +l129: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + else { + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))))); + } + } + BREAK; + CASE(483) // 227 extPushLiteralVariableBytecode + { + sqInt index; + sqInt litVar; + + VM_LABEL(extPushLiteralVariableBytecode); + index = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + extA = 0; + + /* begin pushLiteralVariable: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((index + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(index + LiteralStart, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))))); + } + BREAK; + CASE(484) // 228 extPushLiteralBytecode + { + sqInt index; + sqInt object; + + VM_LABEL(extPushLiteralBytecode); + index = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + extA = 0; + + /* begin pushLiteralConstant: */ + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((index + LiteralStart)) << (shiftForWord())))))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(485) // 229 longPushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt index; + + VM_LABEL(longPushTemporaryVariableBytecode); + index = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),/* itemporary:in: */ + (index < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - index) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - index) * BytesPerWord)))); + } + BREAK; + CASE(488) // 232 extPushIntegerBytecode + { + sqInt value; + + VM_LABEL(extPushIntegerBytecode); + value = (byteAt(++localIP)) + ((((usqInt)(extB) << 8))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + extB = 0; + numExtB = 0; + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),(((usqInt)value << 3) | 1)); + } + BREAK; + CASE(489) // 233 extPushCharacterBytecode + { + sqInt object; + sqInt value; + + VM_LABEL(extPushCharacterBytecode); + value = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = ((((usqInt)(value) << (numTagBits())))) + (characterTag()); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + extA = 0; + } + BREAK; + CASE(490) // 234 extSendBytecode + { + sqInt byte; + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(extSendBytecode); + byte = byteAt(++localIP); + + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((((((usqInt)(byte)) >> 3) + ((((usqInt)(extA) << 5)))) + LiteralStart)) << (shiftForWord())))))); + extA = 0; + GIV(argumentCount) = (byte & 7) + ((((usqInt)(extB) << 3))); + extB = 0; + numExtB = 0; + rcvr = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + lkupClassTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + goto commonSendOrdinary; + } + BREAK; + CASE(491) // 235 extSendSuperBytecode + { + sqInt byte; + + VM_LABEL(extSendSuperBytecode); + byte = byteAt(++localIP); + + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((((((usqInt)(byte)) >> 3) + ((((usqInt)(extA) << 5)))) + LiteralStart)) << (shiftForWord())))))); + extA = 0; + if (extB >= 64) { + GIV(argumentCount) = (byte & 7) + ((((usqInt)((extB - 64)) << 3))); + extB = 0; + numExtB = 0; + + /* goto directedSuperclassSend */ + goto l131; + } + GIV(argumentCount) = (byte & 7) + ((((usqInt)(extB) << 3))); + extB = 0; + numExtB = 0; + goto superclassSend; + } + /* end case */ +l131: + + directedSuperclassSend: + /* directedSuperclassSend */ + { + sqInt aValue; + sqInt class; + sqInt err; + int hash; + sqInt objOop; + sqInt referent; + sqInt superclass; + + VM_LABEL(directedSuperclassSend); + /* begin internalPopStack */ + class = longAt(localSP); + localSP += BytesPerOop; + if ((!((longAt((void *)(class))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(class)); + referent = longAt((void *)((class + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + class = referent; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + superclass = longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(superclass)); + if ((!((longAt((void *)(superclass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + superclass = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, class, superclass); + } + + /* classTagForClass: uses ensureBehaviorHash: */ + + /* begin classTagForClass: */ + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(superclass)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + lkupClassTag = ((hash = (long32At((void *)(superclass + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(superclass) + ? ((err = enterIntoClassTable(superclass)) + ? -err + : (long32At((void *)(superclass + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + + /* To maintain the invariant that all receivers are unforwarded we need an explicit + read barrier in the super send cases. Even though we always follow receivers + on become e.g. super doSomethingWith: (self become: other) forwards the receiver + self pushed on the stack. */ + + /* begin ensureReceiverUnforwarded */ + if (/* isOopForwarded: */ + ((!((longAt(localSP + (GIV(argumentCount) * BytesPerOop))) & (tagMask())))) + && ((!((longAt((void *)(longAt(localSP + (GIV(argumentCount) * BytesPerOop))))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + referent = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + aValue = referent; + + /* begin internalStackValue:put: */ + longAtput(localSP + (GIV(argumentCount) * BytesPerOop),aValue); + } + assert(lkupClassTag != (nilObject())); + goto commonSendOrdinary; + } + BREAK; + CASE(492) // 236 callMappedInlinedPrimitive + { + VM_LABEL(callMappedInlinedPrimitive); + localIP -= 2; + goto respondToUnknownBytecode; + } + BREAK; + CASE(493) // 237 extUnconditionalJump + { + sqInt backwardJumpCountByte; + sqInt bcpcDelta; + sqInt byte; + sqInt header; + sqInt methodHeader; + sqInt offset; + sqInt switched; + + VM_LABEL(extUnconditionalJump); + byte = byteAt(++localIP); + offset = byte + ((((usqInt)(extB) << 8))); + bcpcDelta = (offset < 0 + ? numExtB * 2 + : 0); + extB = 0; + numExtB = 0; + localIP += offset; + + /* begin ifBackwardsCheckForEvents: */ + if ((offset + bcpcDelta) >= 0) { + goto l132; + } + if (localSP < GIV(stackLimit)) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + switched = checkForEventsMayContextSwitch(1); + returnToExecutivepostContextSwitch(1, switched); + browserPluginReturnIfNeeded(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (switched) { + goto l132; + } + } + + /* We use the least significant byte of the flags word (which is marked as an immediate) and + subtract two each time to avoid disturbing the least significant tag bit. Since the byte is + initialized to 1 (on frame build), on first decrement it will become -1. Trip when it reaches 1 again. */ + backwardJumpCountByte = byteAt(localFP + ((VMBIGENDIAN + ? (FoxIFrameFlags + BytesPerWord) - 1 + : FoxIFrameFlags))); + if (((backwardJumpCountByte -= 2)) == 1) { + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + attemptToSwitchToMachineCode(((((oopForPointer(localIP)) - (offset + bcpcDelta)) - GIV(method)) - BaseHeaderSize) - 1); + } + + /* can't cog method; avoid asking to cog it again for the longest possible time. */ + backwardJumpCountByte = 0x7F; + } + else { + if (backwardJumpCountByte == -1) { + assert(minBackwardJumpCountForCompile <= 128); + backwardJumpCountByte = ((((usqInt)((minBackwardJumpCountForCompile - 1)) << 1))) + 1; + } + } + + /* begin iframeBackwardBranchByte:put: */ + byteAtput(localFP + ((VMBIGENDIAN + ? (FoxIFrameFlags + BytesPerWord) - 1 + : FoxIFrameFlags)),backwardJumpCountByte); + /* end ifBackwardsCheckForEvents: */ +l132: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(494) // 238 extJumpIfTrue + { + sqInt boolean; + sqInt byte; + sqInt offset; + + VM_LABEL(extJumpIfTrue); + byte = byteAt(++localIP); + offset = byte + ((((usqInt)(extB) << 8))); + numExtB = (extB = (extA = 0)); + + /* begin jumplfTrueBy: */ + boolean = longAt(localSP); + if (boolean == GIV(trueObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(falseObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l133; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfTrueBy: */ +l133:; + } + BREAK; + CASE(495) // 239 extJumpIfFalse + { + sqInt boolean; + sqInt byte; + sqInt offset; + + VM_LABEL(extJumpIfFalse); + byte = byteAt(++localIP); + offset = byte + ((((usqInt)(extB) << 8))); + numExtB = (extB = (extA = 0)); + + /* begin jumplfFalseBy: */ + boolean = longAt(localSP); + if (boolean == GIV(falseObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(trueObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l134; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfFalseBy: */ +l134:; + } + BREAK; + CASE(496) // 240 extStoreAndPopReceiverVariableBytecode + { + sqInt obj; + int onCurrentPage; + sqInt senderOop; + char *theFP; + StackPage *thePage; + sqInt value; + sqInt variableIndex; + + VM_LABEL(extStoreAndPopReceiverVariableBytecode); + variableIndex = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + extA = 0; + value = longAt(localSP); + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + obj = longAt(localFP + FoxIFReceiver); + if ((variableIndex <= ReceiverIndex) + && ((((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + || (ismethodAssignmentToContextWithMachineCodePC(variableIndex, value))))) { + /* begin instVar:ofContext:put: */ + assert(isMarriedOrWidowedContext(obj)); + assert(!((isObjImmutable(obj)))); + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(obj))))) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, value)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),value); + if (variableIndex == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + goto l135; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!variableIndex) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + onCurrentPage = thePage == GIV(stackPage); + storeSenderOfFramewithValue(theFP, value); + if (onCurrentPage) { + localFP = (GIV(stackPage)->headFP); + localSP = (GIV(stackPage)->headSP); + } + else { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + goto l135; + } + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + externalDivorceFrameandContext(theFP, obj); + if (ismethodAssignmentToContextWithMachineCodePC(variableIndex, obj)) { + ensureContextHasBytecodePC(obj); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, value)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),value); + if (variableIndex == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + + /* Assigning various fields can force a divorce which can change the stackPage. */ + markStackPageMostRecentlyUsed(GIV(stackPage)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 1, __LINE__); + /* end instVar:ofContext:put: */ +l135:; + } + else { + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(obj)))); + if ((((usqInt)((byteAt((void *)(obj + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),obj); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((usqInt)(variableIndex + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l136; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, value)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l136:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(497) // 241 extStoreAndPopLiteralVariableBytecode + { + sqInt litVar; + sqInt value; + sqInt variableIndex; + + VM_LABEL(extStoreAndPopLiteralVariableBytecode); + variableIndex = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + value = longAt(localSP); + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + extA = 0; + + /* begin storeLiteralVariable:withValue: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((variableIndex + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(variableIndex + LiteralStart, GIV(method), litVar); + } + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(litVar)))); + if ((((usqInt)((byteAt((void *)(litVar + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),litVar); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((usqInt)2 /* (ValueIndex + 1) */ << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l137; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ValueIndex, litVar, value)); + assert(isNonImmediate(litVar)); + if (oopisGreaterThanOrEqualTo(litVar, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(litVar + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(litVar); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l137: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(498) // 242 longStoreAndPopTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt index; + + VM_LABEL(longStoreAndPopTemporaryVariableBytecode); + /* begin longStoreTemporaryVariableBytecode */ + index = byteAt(++localIP); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (index < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - index) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - index) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(499) // 243 extStoreReceiverVariableBytecode + { + sqInt anObject; + sqInt obj; + int onCurrentPage; + sqInt senderOop; + char *theFP; + StackPage *thePage; + sqInt variableIndex; + + VM_LABEL(extStoreReceiverVariableBytecode); + variableIndex = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + extA = 0; + anObject = longAt(localSP); + + /* begin storeMaybeContextReceiverVariable:withValue: */ + obj = longAt(localFP + FoxIFReceiver); + if ((variableIndex <= ReceiverIndex) + && ((((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + || (ismethodAssignmentToContextWithMachineCodePC(variableIndex, anObject))))) { + /* begin instVar:ofContext:put: */ + assert(isMarriedOrWidowedContext(obj)); + assert(!((isObjImmutable(obj)))); + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(obj))))) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, anObject)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anObject & (tagMask())))) + && (oopisLessThan(anObject, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),anObject); + if (variableIndex == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + goto l138; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!variableIndex) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + onCurrentPage = thePage == GIV(stackPage); + storeSenderOfFramewithValue(theFP, anObject); + if (onCurrentPage) { + localFP = (GIV(stackPage)->headFP); + localSP = (GIV(stackPage)->headSP); + } + else { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + goto l138; + } + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + externalDivorceFrameandContext(theFP, obj); + if (ismethodAssignmentToContextWithMachineCodePC(variableIndex, obj)) { + ensureContextHasBytecodePC(obj); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, anObject)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anObject & (tagMask())))) + && (oopisLessThan(anObject, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),anObject); + if (variableIndex == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + + /* Assigning various fields can force a divorce which can change the stackPage. */ + markStackPageMostRecentlyUsed(GIV(stackPage)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 1, __LINE__); + /* end instVar:ofContext:put: */ +l138:; + } + else { + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(obj)))); + if ((((usqInt)((byteAt((void *)(obj + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),obj); + longAtput((localSP -= BytesPerOop),anObject); + longAtput((localSP -= BytesPerOop),(((usqInt)(variableIndex + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l139; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, anObject)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anObject & (tagMask())))) + && (oopisLessThan(anObject, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),anObject); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l139:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(500) // 244 extStoreLiteralVariableBytecode + { + sqInt anObject; + sqInt litVar; + sqInt variableIndex; + + VM_LABEL(extStoreLiteralVariableBytecode); + variableIndex = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + extA = 0; + anObject = longAt(localSP); + + /* begin storeLiteralVariable:withValue: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((variableIndex + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(variableIndex + LiteralStart, GIV(method), litVar); + } + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(litVar)))); + if ((((usqInt)((byteAt((void *)(litVar + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),litVar); + longAtput((localSP -= BytesPerOop),anObject); + longAtput((localSP -= BytesPerOop),(((usqInt)2 /* (ValueIndex + 1) */ << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l140; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ValueIndex, litVar, anObject)); + assert(isNonImmediate(litVar)); + if (oopisGreaterThanOrEqualTo(litVar, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anObject & (tagMask())))) + && (oopisLessThan(anObject, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(litVar + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(litVar); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))),anObject); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l140: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(501) // 245 longStoreTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt index; + + VM_LABEL(longStoreTemporaryVariableBytecode); + index = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (index < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - index) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - index) * BytesPerWord),longAt(localSP)); + } + } + BREAK; + CASE(505) // 249 extPushFullClosureBytecode + { + sqInt byte; + sqInt compiledBlock; + sqInt compiledBlockLiteralIndex; + sqInt context; + sqInt header; + sqInt i; + int ignoreContext; + usqInt newClosure; + usqInt newObj; + usqInt numArgs; + usqInt numBytes; + sqInt numCopied; + sqInt numCopiedSqInt; + sqInt numSlots; + int receiverIsOnStack; + sqInt startIndex; + + VM_LABEL(extPushFullClosureBytecode); + compiledBlockLiteralIndex = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + extA = 0; + + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + compiledBlock = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((compiledBlockLiteralIndex + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(compiledBlock)); + if ((!((longAt((void *)(compiledBlock))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + compiledBlock = fixFollowedFieldofObjectwithInitialValue(compiledBlockLiteralIndex + LiteralStart, GIV(method), compiledBlock); + } + assert(isOopCompiledMethod(compiledBlock)); + + /* begin argumentCountOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(compiledBlock)); + header = longAt((void *)((compiledBlock + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numArgs = (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; + byte = byteAt(++localIP); + numCopied = byte & (0x3F); + receiverIsOnStack = ((byte & (128)) != 0); + ignoreContext = ((byte & (64)) != 0); + + /* begin pushFullClosureNumArgs:copiedValues:compiledBlock:receiverIsOnStack:ignoreContext: */ + if (ignoreContext) { + context = GIV(nilObj); + } + else { + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + context = longAt(localFP + FoxThisContext); + goto l142; + } + context = marryFrameSP(localFP, localSP + (numCopied * BytesPerOop)); + /* end ensureFrameIsMarried:SP: */ +l142:; + } + + /* begin fullClosureIn:numArgs:numCopiedValues:compiledBlock: */ + numSlots = FullClosureFirstCopiedValueIndex + numCopied; + assert((numSlots >= 0) + && ((knownClassAtIndex(ClassFullBlockClosureCompactIndex)) != GIV(nilObj))); + assert((indexablePointersFormat()) == (instSpecOfClass(knownClassAtIndex(ClassFullBlockClosureCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newClosure = 0; + goto l143; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((indexablePointersFormat())) << (formatShift()))))) + ClassFullBlockClosureCompactIndex); + GIV(freeStart) += numBytes; + newClosure = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l143: + + /* Assume: have just allocated a new closure; it must be young. Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureOuterContextIndex, newClosure, context)); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord()))))),context); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(FullClosureCompiledBlockIndex, newClosure, compiledBlock)); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(FullClosureCompiledBlockIndex) << (shiftForWord()))))),compiledBlock); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureNumArgsIndex, newClosure, ((numArgs << 3) | 1))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord()))))),((numArgs << 3) | 1)); + if (receiverIsOnStack) { + startIndex = FullClosureReceiverIndex; + numCopiedSqInt = numCopied + 1; + } + else { + startIndex = FullClosureFirstCopiedValueIndex; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(FullClosureReceiverIndex, newClosure, longAt(localFP + FoxIFReceiver))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(FullClosureReceiverIndex) << (shiftForWord()))))),longAt(localFP + FoxIFReceiver)); + numCopiedSqInt = numCopied; + } + if (numCopiedSqInt > 0) { + for (i = 0; i < numCopiedSqInt; i += 1) { + /* Assume: have just allocated a new BlockClosure; it must be young. + Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(i + startIndex, newClosure, longAt((void *)(localSP + (((numCopiedSqInt - i) - 1) * BytesPerOop))))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)((i + startIndex)) << (shiftForWord()))))),longAt((void *)(localSP + (((numCopiedSqInt - i) - 1) * BytesPerOop)))); + } + + /* begin internalPop: */ + localSP += numCopiedSqInt * BytesPerOop; + } + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),newClosure); + } + BREAK; + CASE(506) // 250 extPushClosureBytecode + { + sqInt blockSize; + sqInt byte; + sqInt context; + sqInt i; + sqInt initialIP; + usqInt newClosure; + usqInt newObj; + usqInt numArgs; + usqInt numBytes; + usqInt numCopied; + sqInt numSlots; + + VM_LABEL(extPushClosureBytecode); + byte = byteAt(++localIP); + numArgs = (byte & 7) + ((extA % 16) * 8); + numCopied = ((((usqInt)(byte)) >> 3) & 7) + ((extA / 16) * 8); + extA = 0; + blockSize = (byteAt(++localIP)) + ((((usqInt)(extB) << 8))); + numExtB = (extB = 0); + + /* begin pushClosureNumArgs:copiedValues:blockSize: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + context = longAt(localFP + FoxThisContext); + goto l144; + } + context = marryFrameSP(localFP, localSP + (numCopied * BytesPerOop)); + /* end ensureFrameIsMarried:SP: */ +l144: + initialIP = ((((usqInt)localIP)) + 2) - (GIV(method) + BaseHeaderSize); + + /* begin closureIn:numArgs:instructionPointer:numCopiedValues: */ + numSlots = ClosureFirstCopiedValueIndex + numCopied; + assert((numSlots >= 0) + && ((knownClassAtIndex(ClassBlockClosureCompactIndex)) != GIV(nilObj))); + assert((indexablePointersFormat()) == (instSpecOfClass(knownClassAtIndex(ClassBlockClosureCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newClosure = 0; + goto l145; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((indexablePointersFormat())) << (formatShift()))))) + ClassBlockClosureCompactIndex); + GIV(freeStart) += numBytes; + newClosure = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l145: + + /* Assume: have just allocated a new closure; it must be young. Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureOuterContextIndex, newClosure, context)); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord()))))),context); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureStartPCIndex, newClosure, (((usqInt)initialIP << 3) | 1))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))),(((usqInt)initialIP << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureNumArgsIndex, newClosure, ((numArgs << 3) | 1))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord()))))),((numArgs << 3) | 1)); + if (numCopied > 0) { + for (i = 0; i < numCopied; i += 1) { + /* Assume: have just allocated a new BlockClosure; it must be young. + Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(i + ClosureFirstCopiedValueIndex, newClosure, longAt((void *)(localSP + (((numCopied - i) - 1) * BytesPerOop))))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)((i + ClosureFirstCopiedValueIndex)) << (shiftForWord()))))),longAt((void *)(localSP + (((numCopied - i) - 1) * BytesPerOop)))); + } + + /* begin internalPop: */ + localSP += numCopied * BytesPerOop; + } + localIP += blockSize; + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),newClosure); + } + BREAK; + } + } + + /* undo the pre-increment of IP (in fetchByte in fetchNextBytecode) before returning */ + localIP -= 1; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + return null; +} + + /* CogBlockMethod>>#cmHomeMethod */ +static NoDbgRegParms CogMethod * +cmHomeMethod(CogBlockMethod *self_in_CogBlockMethod) +{ + return (((self_in_CogBlockMethod->cmType)) >= CMMethod + ? ((CogMethod *) self_in_CogBlockMethod) + : (assert(CMBlock == (self_in_CogBlockMethod->cmType)), + ((CogMethod *) ((((usqInt)self_in_CogBlockMethod)) - ((self_in_CogBlockMethod->homeOffset)))))); +} + + /* CogBlockMethod>>#isCMBlock */ +static NoDbgRegParms int +isCMBlock(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMBlock; +} + + /* CogBlockMethod>>#isCMClosedPIC */ +static NoDbgRegParms int +isCMClosedPIC(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMClosedPIC; +} + + /* CogBlockMethod>>#isCMMethodEtAl */ +static NoDbgRegParms int +isCMMethodEtAl(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) >= CMMethod; +} + + /* CogBlockMethod>>#isCMMethodFlaggedForBecome */ +static NoDbgRegParms int +isCMMethodFlaggedForBecome(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMMethodFlaggedForBecome; +} + + /* CogBlockMethod>>#isCMOpenPIC */ +static NoDbgRegParms int +isCMOpenPIC(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMOpenPIC; +} + + +/* For assert-checking */ + + /* CogStackPage>>#addressIsInPage: */ +static NoDbgRegParms sqInt +addressIsInPage(StackPage *self_in_CogStackPage, char *address) +{ + return (((self_in_CogStackPage->lastAddress)) < address) + && (address < ((self_in_CogStackPage->baseAddress))); +} + + /* CogStackPage>>#isFree */ +static NoDbgRegParms int +isFree(StackPage *self_in_CogStackPage) +{ + return ((self_in_CogStackPage->baseFP)) == 0; +} + + +/* */ +/* MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#freeStackPageNoAssert: */ +static NoDbgRegParms sqInt +freeStackPageNoAssert(StackPage *aPage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *prev; + StackPage *self_in_CogStackPage; + + (aPage->baseFP = 0); + if (aPage == GIV(mostRecentlyUsedPage)) { + GIV(mostRecentlyUsedPage) = (GIV(mostRecentlyUsedPage)->prevPage); + return null; + } + self_in_CogStackPage = (prev = (aPage->prevPage)); + if (!((self_in_CogStackPage->baseFP))) { + return null; + } + (prev->nextPage = (aPage->nextPage)); + (((aPage->nextPage))->prevPage = prev); + (aPage->nextPage = (GIV(mostRecentlyUsedPage)->nextPage)); + (((GIV(mostRecentlyUsedPage)->nextPage))->prevPage = aPage); + (aPage->prevPage = GIV(mostRecentlyUsedPage)); + (GIV(mostRecentlyUsedPage)->nextPage = aPage); + return 0; +} + + +/* */ +/* MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#freeStackPage: */ +static NoDbgRegParms void +freeStackPage(StackPage *aPage) +{ + freeStackPageNoAssert(aPage); + assert(pageListIsWellFormed()); +} + + +/* */ +/* MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#markStackPageMostRecentlyUsed: */ +static NoDbgRegParms sqInt +markStackPageMostRecentlyUsed(StackPage *page) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (page == GIV(mostRecentlyUsedPage)) { + return null; + } + + /* Common case; making new page most recently used. */ + if (((page->prevPage)) == GIV(mostRecentlyUsedPage)) { + GIV(mostRecentlyUsedPage) = page; + assert(pageListIsWellFormed()); + return null; + } + (((page->prevPage))->nextPage = (page->nextPage)); + (((page->nextPage))->prevPage = (page->prevPage)); + (((GIV(mostRecentlyUsedPage)->nextPage))->prevPage = page); + (page->prevPage = GIV(mostRecentlyUsedPage)); + (page->nextPage = (GIV(mostRecentlyUsedPage)->nextPage)); + (GIV(mostRecentlyUsedPage)->nextPage = page); + GIV(mostRecentlyUsedPage) = page; + assert(pageListIsWellFormed()); + return 0; +} + + +/* */ +/* This method is used to move a page to a position in the list such that it + cannot be deallocated when a new page is allocated, without changing the + most recently + used page. There must be at least 3 pages in the system. So making the + page the MRU's prevPage is sufficient to ensure it won't be deallocated. */ +/* MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#markStackPageNextMostRecentlyUsed: */ +static NoDbgRegParms sqInt +markStackPageNextMostRecentlyUsed(StackPage *page) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(page != GIV(mostRecentlyUsedPage)); + if (((page->nextPage)) == GIV(mostRecentlyUsedPage)) { + return null; + } + (((page->prevPage))->nextPage = (page->nextPage)); + (((page->nextPage))->prevPage = (page->prevPage)); + (((GIV(mostRecentlyUsedPage)->prevPage))->nextPage = page); + (page->prevPage = (GIV(mostRecentlyUsedPage)->prevPage)); + (page->nextPage = GIV(mostRecentlyUsedPage)); + (GIV(mostRecentlyUsedPage)->prevPage = page); + assert(pageListIsWellFormed()); + return 0; +} + + +/* MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#newStackPage */ +static StackPage * +newStackPage(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *lruOrFree; + + lruOrFree = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((lruOrFree->baseFP))) { + return lruOrFree; + } + divorceFramesIn(lruOrFree); + return lruOrFree; +} + + +/* Answer if the stack page list is well-formed. + MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#pageListIsWellFormed */ +static sqInt +pageListIsWellFormed(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt count; + sqInt limit; + sqInt ok; + StackPage *page; + + ok = 1; + page = (GIV(mostRecentlyUsedPage)->nextPage); + count = 1; + limit = GIV(numStackPages) * 2; + while ((((page->baseFP)) == 0) + && ((page != GIV(mostRecentlyUsedPage)) + && (count <= limit))) { + if (!(asserta(((((page->nextPage))->prevPage)) == page))) { + ok = 0; + } + page = (page->nextPage); + count += 1; + } + while ((page != GIV(mostRecentlyUsedPage)) + && (count <= limit)) { + if (!(asserta(((((page->nextPage))->prevPage)) == page))) { + ok = 0; + } + if (asserta(!(isFree(page)))) { + if (!(asserta((addressIsInPage(page, (page->baseFP))) + && (addressIsInPage(page, (page->headSP)))))) { + ok = 0; + } + } + else { + ok = 0; + } + page = (page->nextPage); + count += 1; + } + if (!(asserta(count == (numStkPages())))) { + ok = 0; + } + return ok; +} + + +/* Answer the page for a page index. + N.B. This is a zero-relative index. */ + + /* CogStackPages>>#stackPageAt: */ +static NoDbgRegParms StackPage * +stackPageAt(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return stackPageAtpages(index, GIV(pages)); +} + + +/* */ + + /* CogStackPages>>#stackPageFor: */ +static NoDbgRegParms StackPage * +stackPageFor(void *pointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return stackPageAtpages(pageIndexFor(pointer), GIV(pages)); +} + + /* CogStackPages>>#statAverageLivePagesWhenMapping */ +static double +statAverageLivePagesWhenMapping(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (GIV(statNumMaps) + ? (((double) GIV(statPageCountWhenMappingSum) )) / GIV(statNumMaps) + : 0.0); +} + + +/* Initialization is completed by initializing the stack zone on entry to the + interpreter. Answer when this has been done. This allows e.g. the crash + dump processing to + be avoided until the system has been initialized, easing debugging of new + ports as they fail in early system initialization attempts. */ + + /* CogStackPages>>#vmIsInitialized */ +int +vmIsInitialized(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return !!GIV(pages); +} + + /* CoInterpreter>>#accessorDepthForPrimitiveIndex: */ +sqInt +accessorDepthForPrimitiveIndex(sqInt primIndex) +{ + return ((sqInt)((primitiveMetadataTable[primIndex]))) >> SpurPrimitiveAccessorDepthShift; +} + + +/* Activate newMethod when newMethod has been cogged, i.e. create a + machine-code frame and (re)enter machine-code. + */ + + /* CoInterpreter>>#activateNewCogMethod:inInterpreter: */ +static NoDbgRegParms void +activateNewCogMethodinInterpreter(CogMethod *cogMethod, sqInt inInterpreter) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int i; + sqInt methodHeader; + usqInt numTemps; + sqInt object; + sqInt rcvr; + char *sp; + sqInt switched; + + assert((rawHeaderOf(GIV(newMethod))) == (((sqInt)cogMethod))); + methodHeader = (cogMethod->methodHeader); + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (((cogMethod->cmNumArgs)) * BytesPerWord)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + if (!((cogMethod->stackCheckOffset))) { + /* frameless method; nothing to activate... */ + if ((numRegArgs()) > 0) { + if (((cogMethod->cmNumArgs)) <= (numRegArgs())) { + callRegisterArgCogMethodatreceiver(cogMethod, noCheckEntryOffset(), rcvr); + } + } + + /* dont use and: so as to get Slang to inline cogit numRegArgs > 0 */ + object = (((usqInt)cogMethod)) + (noCheckEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + ceCallCogCodePopReceiverReg(); + error("should not be reached"); + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),((usqInt)cogMethod)); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + for (i = (((cogMethod->cmNumArgs)) + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + if ((((methodHeader & AlternateHeaderHasPrimFlag) != 0)) + && (GIV(primFailCode) != 0)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeader); + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc). */ + if (GIV(stackPointer) >= GIV(stackLimit)) { + assert(((cogMethod->stackCheckOffset)) > (noCheckEntryOffset())); + object = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + ceEnterCogCodePopReceiverReg(); + error("should not be reached"); + } + GIV(instructionPointer) = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + switched = handleStackOverflowOrEventAllowContextSwitch(canContextSwitchIfActivatingheader(GIV(newMethod), methodHeader)); + returnToExecutivepostContextSwitch(inInterpreter, switched); +} + + +/* Add the given entry to the method cache. + The policy is as follows: + Look for an empty entry anywhere in the reprobe chain. + If found, install the new entry there. + If not found, then install the new entry at the first probe position + and delete the entries in the rest of the reprobe chain. + This has two useful purposes: + If there is active contention over the first slot, the second + or third will likely be free for reentry after ejection. + Also, flushing is good when reprobe chains are getting full. */ +/* Override to refuse to cache other than compiled methods. + This protects open PICs against having to test for compiled methods. */ + + /* CoInterpreter>>#addNewMethodToCache: */ +static NoDbgRegParms void +addNewMethodToCache(sqInt classObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTag; + sqInt err; + usqInt firstBytecode; + sqInt hash; + int hashInt; + sqInt header; + sqInt methodHeader; + sqInt p; + sqInt primitiveIndex; + usqInt probe; + + if (!(/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + primitiveFunctionPointer = primitiveInvokeObjectAsMethod; + return; + } + + /* begin classTagForClass: */ + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classTag = ((hashInt = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hashInt + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + + /* begin methodCacheHashOf:with: */ + hash = GIV(messageSelector) ^ ((((usqInt)(classTag) << 2))); + if (/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) { + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]))); + } + else { + assert(!((isNonImmediate(GIV(newMethod))) + && (isForwarded(GIV(newMethod))))); + primitiveFunctionPointer = primitiveInvokeObjectAsMethod; + } + for (p = 0; p < CacheProbeMax; p += 1) { + probe = (((usqInt)(hash)) >> p) & MethodCacheMask; + if (!(GIV(methodCache)[probe + MethodCacheSelector])) { + /* Found an empty entry -- use it */ + GIV(methodCache)[probe + MethodCacheSelector] = GIV(messageSelector); + GIV(methodCache)[probe + MethodCacheClass] = (classTagForClass(classObj)); + GIV(methodCache)[probe + MethodCacheMethod] = GIV(newMethod); + GIV(methodCache)[probe + MethodCachePrimFunction] = (((sqIntptr_t) primitiveFunctionPointer)); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = probe; + return; + } + } + + /* OK, we failed to find an entry -- install at the first slot... */ + + /* first probe */ + probe = hash & MethodCacheMask; + GIV(methodCache)[probe + MethodCacheSelector] = GIV(messageSelector); + GIV(methodCache)[probe + MethodCacheClass] = (classTagForClass(classObj)); + GIV(methodCache)[probe + MethodCacheMethod] = GIV(newMethod); + GIV(methodCache)[probe + MethodCachePrimFunction] = (((sqIntptr_t) primitiveFunctionPointer)); + + /* this for primitiveExternalMethod */ + /* ...and zap the following entries */ + GIV(lastMethodCacheProbeWrite) = probe; + for (p = 1; p < CacheProbeMax; p += 1) { + probe = (((usqInt)(hash)) >> p) & MethodCacheMask; + GIV(methodCache)[probe + MethodCacheSelector] = 0; + } +} + + /* CoInterpreter>>#argumentCountAddress */ +usqInt +argumentCountAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(argumentCount)))); +} + + +/* Coerce either a CMMethod or a CMBlock to the home CMMethod */ + + /* CoInterpreter>>#asCogHomeMethod: */ +static NoDbgRegParms CogMethod * +asCogHomeMethod(CogBlockMethod *aCogMethod) +{ + return (((aCogMethod->cmType)) >= CMMethod + ? ((CogMethod *) aCogMethod) + : /* cmHomeMethod */ + (((aCogMethod->cmType)) >= CMMethod + ? ((CogMethod *) aCogMethod) + : (assert(CMBlock == (aCogMethod->cmType)), + ((CogMethod *) ((((usqInt)aCogMethod)) - ((aCogMethod->homeOffset))))))); +} + + /* CoInterpreter>>#assertValidExecutionPointe:r:s:imbar:line: */ +static NoDbgRegParms void +assertValidExecutionPointersimbarline(usqInt lip, char *lifp, char *lisp, sqInt inInterpreter, sqInt ln) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cogMethod; + sqInt methodField; + sqInt methodFieldSqInt; + usqInt theIP; + + assertl(GIV(stackPage) == (GIV(mostRecentlyUsedPage)), ln); + assertl(addressIsInPage(GIV(stackPage), lifp), ln); + assert(deferStackLimitSmashAroundwith(assertValidStackLimits, ln)); + assertl(lisp < lifp, ln); + assertl(lifp > lisp, ln); + assertl(lisp >= (((GIV(stackPage)->realStackLimit)) - (stackLimitOffset())), ln); + assertl(((lifp - lisp) / BytesPerOop) < LargeContextSlots, ln); + methodField = longAt(lifp + FoxMethod); + if (inInterpreter) { + assertl(!(isMachineCodeFrame(lifp)), ln); + assert(lisp <= (lifp + FoxIFReceiver)); + assertl(GIV(method) == methodField, ln); + assertl((methodUsesAlternateBytecodeSet(GIV(method))) == (GIV(bytecodeSetSelector) == 0x100), ln); + if (assertal(cheapAddressCouldBeInHeap(methodField), ln)) { + theIP = (lip == (ceReturnToInterpreterPC()) + ? ((usqInt)(longAt(lifp + FoxIFSavedIP))) + : lip); + assertl((theIP >= (methodField + (lastPointerOf(methodField)))) + && (theIP < (((methodField + (numBytesOfBytes(methodField))) + BaseHeaderSize) - 1)), ln); + } + assertl((iframeIsBlockActivation(lifp)) + || ((pushedReceiverOrClosureOfFrame(lifp)) == (iframeReceiver(lifp))), ln); + } + else { + assertl(isMachineCodeFrame(lifp), ln); + assert(lisp <= (lifp + FoxMFReceiver)); + if ((assertal((((usqInt)methodField)) >= (minCogMethodAddress()), ln)) + && (assertal((((usqInt)methodField)) < (maxCogMethodAddress()), ln))) { + /* begin mframeHomeMethod: */ + methodFieldSqInt = longAt(lifp + FoxMethod); + if (methodFieldSqInt & MFMethodFlagIsBlockFlag) { + cogMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodFieldSqInt & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodFieldSqInt & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodFieldSqInt & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodFieldSqInt & MFMethodMask))))) - (((((CogBlockMethod *) (methodFieldSqInt & MFMethodMask)))->homeOffset)))))); + goto l1; + } + cogMethod = ((CogMethod *) (methodFieldSqInt & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + assertl((lip > (methodField + ((mframeIsBlockActivation(lifp) + ? sizeof(CogBlockMethod) + : sizeof(CogMethod))))) + && (lip < (methodField + ((cogMethod->blockSize)))), ln); + } + assertl((mframeIsBlockActivation(lifp)) + || ((pushedReceiverOrClosureOfFrame(lifp)) == (mframeReceiver(lifp))), ln); + } + if (!(longAt(lifp + FoxSavedFP))) { + assertl(frameHasContext(lifp), ln); + assertl((frameContext(lifp)) == (longAt(((GIV(stackPage)->baseAddress)) - BytesPerWord)), ln); + } +} + + +/* Check that the stacked instruction pointers in the given page are correct. + Checks the interpreter sender/machine code callee contract. */ + + /* CoInterpreter>>#assertValidStackedInstructionPointersIn:line: */ +static NoDbgRegParms sqInt +assertValidStackedInstructionPointersInline(StackPage *aStackPage, sqInt ln) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt methodField; + usqInt methodObj; + sqInt prevFrameWasCogged; + char *theFP; + usqInt theIP; + CogMethod *theMethod; + + callerFP = ((char *) 0); + if (!(assertal(!(isFree(aStackPage)), ln))) { + return 0; + } + prevFrameWasCogged = 0; + + /* The top of stack of an inactive page is always the instructionPointer. + The top of stack of the active page may be the instructionPointer if it has been pushed, + which is indicated by a 0 instructionPointer. */ + if ((GIV(stackPage) == aStackPage) + && (GIV(instructionPointer) != 0)) { + theIP = GIV(instructionPointer); + theFP = GIV(framePointer); + } + else { + theIP = ((usqInt)(longAt((aStackPage->headSP)))); + theFP = (aStackPage->headFP); + if (GIV(stackPage) == aStackPage) { + assertl(GIV(framePointer) == theFP, ln); + } + } + while (1) { + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* begin mframeHomeMethod: */ + methodField = longAt(theFP + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + theMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + goto l1; + } + theMethod = ((CogMethod *) (methodField & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + assertl((theIP == (ceCannotResumePC())) + || ((theIP >= (((usqInt)theMethod))) + && (theIP < ((((usqInt)theMethod)) + ((theMethod->blockSize))))), ln); + prevFrameWasCogged = 1; + } + else { + methodObj = longAt(theFP + FoxMethod); + if (prevFrameWasCogged) { + assertl(theIP == (ceReturnToInterpreterPC()), ln); + } + if (theIP == (ceReturnToInterpreterPC())) { + theIP = ((usqInt)(longAt(theFP + FoxIFSavedIP))); + } + assertl((theIP >= (methodObj + (lastPointerOf(methodObj)))) + && (theIP < (((methodObj + (numBytesOfBytes(methodObj))) + BaseHeaderSize) - 1)), ln); + prevFrameWasCogged = 0; + } + + /* assert-check the interpreter frame. */ + theIP = ((usqInt)(longAt(theFP + FoxCallerSavedIP))); + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theFP = callerFP; + } + assertl(theIP == (ceBaseFrameReturnPC()), ln); + return 1; +} + + +/* Check that the stacked instruction pointers in all pages are correct. + Checks the interpreter sender/machine code callee contract. + Written so it will be optimized away if not in an assert VM. */ + + /* CoInterpreter>>#assertValidStackedInstructionPointers: */ +static NoDbgRegParms void +assertValidStackedInstructionPointers(sqInt ln) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + StackPage *thePage; + + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + assertl(assertValidStackedInstructionPointersInline(thePage, ln), ln); + } + } +} + + +/* Attempt to convert the current interpreted activation into a machine code + activation, and if this is popssible, jump into machine code. bcpc is the + 0-relative pc of the backward branch bytecode (not any preceding + extension). */ + + /* CoInterpreter>>#attemptToSwitchToMachineCode: */ +static NoDbgRegParms void +attemptToSwitchToMachineCode(sqInt bcpc) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt closure; + sqInt cls; + CogMethod *cogMethod; + CogBlockMethod *cogMethod1; + CogMethod *homeMethod; + sqInt methodField; + sqInt methodHeader; + char *p; + usqInt pc; + usqInt startBcpc; + char *theFP; + + if (!(methodHasCogMethod(GIV(method)))) { + if (((byteAt((GIV(framePointer) + FoxIFrameFlags) + 3)) != 0) + && (!(isVanillaBlockClosure((cls = frameStackedReceivernumArgs(GIV(framePointer), /* frameNumArgs: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(GIV(framePointer)))->cmNumArgs) + : byteAt((GIV(framePointer) + FoxIFrameFlags) + 1)))))))) { + /* Compiled block / full closure */ + cogFullBlockMethodnumCopied(GIV(method), (numPointerSlotsOf(cls)) - FullClosureFirstCopiedValueIndex); + } + else { + /* Compiled method */ + cogselector(GIV(method), GIV(nilObj)); + } + } + if (methodHasCogMethod(GIV(method))) { + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + + /* begin convertToMachineCodeFrame:bcpc: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + + /* Update the return pc, perhaps saving it in the caller's iframeSavedIP. */ + if (longAt(GIV(framePointer) + FoxSavedFP)) { + if (!((((usqInt)(longAt((((char *)(longAt(GIV(framePointer) + FoxSavedFP)))) + FoxMethod)))) < (startOfMemory()))) { + theFP = ((char *)(longAt(GIV(framePointer) + FoxSavedFP))); + + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(theFP))); + longAtput(theFP + FoxIFSavedIP,longAt(GIV(framePointer) + FoxCallerSavedIP)); + longAtput(GIV(framePointer) + FoxCallerSavedIP,ceReturnToInterpreterPC()); + } + } + else { + longAtput(GIV(framePointer) + FoxCallerSavedIP,ceBaseFrameReturnPC()); + } + + /* Compute the cog method field */ + if (byteAt((GIV(framePointer) + FoxIFrameFlags) + 3)) { + closure = longAt(GIV(framePointer) + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(GIV(framePointer)))->cmNumArgs) + : byteAt((GIV(framePointer) + FoxIFrameFlags) + 1)))) << (shiftForWord())))))); + + /* begin isVanillaBlockClosure: */ + assert((isPointers(closure)) + && ((numSlotsOf(closure)) >= ClosureFirstCopiedValueIndex)); + if (((((longAt((void *)((closure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1)) { + startBcpc = (((longAt((void *)((closure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))))) >> 3)) - 1; + cogMethod1 = findMethodForStartBcpcinHomeMethod(startBcpc, cogMethod); + } + else { + startBcpc = (((/* begin literalCountOfMethodHeader: */ + assert((((((cogMethod->methodHeader))) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((((cogMethod->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop; + cogMethod1 = ((CogBlockMethod *) cogMethod); + } + methodField = (((sqInt)cogMethod1)) + MFMethodFlagIsBlockFlag; + } + else { + startBcpc = (((/* begin literalCountOfMethodHeader: */ + assert((((((cogMethod->methodHeader))) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((((cogMethod->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop; + cogMethod1 = ((CogBlockMethod *) cogMethod); + methodField = ((sqInt)cogMethod); + } + + /* compute the pc before converting the frame to help with debugging. */ + pc = mcPCForBackwardBranchstartBcpcin(bcpc, startBcpc, cogMethod1); + assert(pc > ((((usqInt)cogMethod1)) + (noCheckEntryOffset()))); + assert(bcpc == (bytecodePCForstartBcpcin(pc, startBcpc, cogMethod1))); + + /* now convert to a machine code frame */ + longAtput(GIV(framePointer) + FoxMethod,methodField + ((byteAt((GIV(framePointer) + FoxIFrameFlags) + 2) + ? MFMethodFlagHasContextFlag + : 0))); + for (p = (GIV(framePointer) + FoxIFReceiver); p >= GIV(stackPointer); p += (-BytesPerWord)) { + longAtput((p + FoxMFReceiver) - FoxIFReceiver,longAt(p)); + } + GIV(stackPointer) = (GIV(stackPointer) + FoxMFReceiver) - FoxIFReceiver; + + /* begin assertValidMachineCodeFrame: */ + assert(isMachineCodeFrame(GIV(framePointer))); + cogMethod1 = ((CogBlockMethod *) ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodMask)); + homeMethod = /* asCogHomeMethod: */ + (((cogMethod1->cmType)) >= CMMethod + ? ((CogMethod *) cogMethod1) + : /* cmHomeMethod */ + (((cogMethod1->cmType)) >= CMMethod + ? ((CogMethod *) cogMethod1) + : (assert(CMBlock == (cogMethod1->cmType)), + ((CogMethod *) ((((usqInt)cogMethod1)) - ((cogMethod1->homeOffset))))))); + assert((methodFor(cogMethod1)) == homeMethod); + assert((pc > (((sqInt)cogMethod1))) + && (pc < ((((sqInt)homeMethod)) + ((homeMethod->blockSize))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),pc); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + ceEnterCogCodePopReceiverReg(); + } +} + + +/* Support for primitiveSuspend. + Assume suspendedContext is that of a process waiting on a condition + variable. Backup the PC of suspendedContext to the send that entered the + wait state. + primitiveEnterCriticalSection pushes false for blocked waiters. false must + be replaced by the condition variable. */ +/* The suspendedContext is in one of six states, hence six cases: + 1 & 2: single context with bytecode pc or single context with machine code + pc 3 & 4: married to machine code frame in embedded (v3) block or machine + code frame in method/full block + 5 & 6: married to interpreter frame with saved ip or interpreter frame + with pushed pc */ + + /* CoInterpreter>>#backupContext:toBlockingSendTo: */ +static NoDbgRegParms void +backupContexttoBlockingSendTo(sqInt suspendedContext, sqInt conditionVariable) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogBlockMethod *cogMethod; + sqInt mcpc; + sqInt pc; + sqInt senderOop; + sqInt sp; + usqInt startBcpc; + char *theFP; + sqInt theIP; + sqInt theMethod; + usqInt theNewIP; + StackPage *thePage; + + assert(isContext(suspendedContext)); + theMethod = longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* cases 1 & 2 */ + if ((!((longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))) & (tagMask())))) { + pc = longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + sp = longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + assert(((((pc) & 7) == 1)) + && (((pc >> 3)) != 0)); + assert(((((sp) & 7) == 1)) + && (((sp >> 3)) > 0)); + + /* case 2 */ + if (((pc = (pc >> 3))) < 0) { + pc = ((mustMapMachineCodePCcontext(pc, suspendedContext)) >> 3); + } + + /* context with machine code pc; must map... */ + theIP = ((theMethod + BaseHeaderSize) + pc) - 1; + theNewIP = pcPreviousToFunction(theIP, theMethod); + assert((theNewIP < theIP) + && ((theIP - theNewIP) <= 3)); + pc = ((theNewIP - theMethod) - BaseHeaderSize) + 1; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(suspendedContext)) + && (!(isForwarded(suspendedContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, suspendedContext, (((usqInt)pc << 3) | 1))); + longAtput((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),(((usqInt)pc << 3) | 1)); + + /* implicitly converts to 0 relative */ + sp = ((sp >> 3)) + ReceiverIndex; + assert(((fetchPointerofObject(sp, suspendedContext)) == (falseObject())) + || ((fetchPointerofObject(sp, suspendedContext)) == conditionVariable)); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(sp, suspendedContext, conditionVariable)); + assert(isNonImmediate(suspendedContext)); + if (oopisGreaterThanOrEqualTo(suspendedContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(conditionVariable & (tagMask())))) + && (oopisLessThan(conditionVariable, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(suspendedContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(suspendedContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(sp) << (shiftForWord()))))),conditionVariable); + return; + } + assert(isMarriedOrWidowedContext(suspendedContext)); + assert(!((isWidowedContextNoConvert(suspendedContext)))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(!((thePage == GIV(stackPage)))); + assert(theFP == ((thePage->headFP))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* a machine code pc... it must be converted... */ + mcpc = longAt((thePage->headSP)); + + /* begin mframeCogMethod: */ + cogMethod = ((CogBlockMethod *) ((longAt(theFP + FoxMethod)) & MFMethodMask)); + if (((cogMethod->cmType)) == CMBlock) { + assert((frameStackedReceivernumArgs(theFP, (cogMethod->cmNumArgs))) == (fetchPointerofObject(ClosureIndex, suspendedContext))); + + /* begin startPCOfClosure: */ + startBcpc = (((longAt((void *)(((longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))))) >> 3)) - 1; + } + else { + startBcpc = ((literalCountOf(theMethod)) + LiteralStart) * BytesPerOop; + } + theIP = bytecodePCForstartBcpcin(mcpc, startBcpc, cogMethod); + theIP = (theIP + theMethod) + BaseHeaderSize; + theNewIP = pcPreviousToFunction(theIP, theMethod); + assert((theNewIP < theIP) + && ((theIP - theNewIP) <= 3)); + convertFrametoInterpreterFrame(theFP, theIP - theNewIP); + } + else { + theIP = longAt((thePage->headSP)); + if (theIP == (ceReturnToInterpreterPC())) { + /* fetchByte uses pre-increment; must + 1 to point at correct bytecode... */ + theIP = (((usqInt)(longAt(theFP + FoxIFSavedIP)))) + 1; + theNewIP = pcPreviousToFunction(theIP, theMethod); + assert((theNewIP < theIP) + && ((theIP - theNewIP) <= 3)); + + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(theFP))); + longAtput(theFP + FoxIFSavedIP,theNewIP - 1); + } + else { + /* fetchByte uses pre-increment; must + 1 to point at correct bytecode... */ + theIP += 1; + assert(validInstructionPointerinMethodframePointer(theIP, theMethod, theFP)); + theNewIP = pcPreviousToFunction(theIP, theMethod); + assert((theNewIP < theIP) + && ((theIP - theNewIP) <= 3)); + longAtput((thePage->headSP),theNewIP - 1); + } + } + assert(((longAt(((thePage->headSP)) + BytesPerWord)) == (falseObject())) + || ((longAt(((thePage->headSP)) + BytesPerWord)) == conditionVariable)); + longAtput(((thePage->headSP)) + BytesPerWord,conditionVariable); +} + + /* CoInterpreter>>#callForCogCompiledCodeCompaction */ +void +callForCogCompiledCodeCompaction(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(cogCompiledCodeCompactionCalledFor) = 1; + forceInterruptCheck(); +} + + +/* convert + rcvr base + arg(s) + retpc <- sp + to + retpc base + entrypc + rcvr + arg(s) <- sp + and then enter at either the checked or the unchecked entry-point. */ + + /* CoInterpreter>>#callRegisterArgCogMethod:at:receiver: */ +static NoDbgRegParms void +callRegisterArgCogMethodatreceiver(CogMethod *cogMethod, sqInt entryOffset, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + char *sp; + + assert(((numRegArgs()) > 0) + && (((numRegArgs()) <= 2) + && (((cogMethod->cmNumArgs)) <= (numRegArgs())))); + if (((cogMethod->cmNumArgs)) == 2) { + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (3 * BytesPerWord),longAt(GIV(stackPointer))); + object = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (1 * BytesPerWord),longAt(GIV(stackPointer) + (3 * BytesPerWord))); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (2 * BytesPerWord),rcvr); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (3 * BytesPerWord),(((sqInt)cogMethod)) + entryOffset); + ceCallCogCodePopReceiverArg1Arg0Regs(); + } + if (((cogMethod->cmNumArgs)) == 1) { + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (2 * BytesPerWord),longAt(GIV(stackPointer))); + object = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (1 * BytesPerWord),rcvr); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (2 * BytesPerWord),(((sqInt)cogMethod)) + entryOffset); + ceCallCogCodePopReceiverArg0Regs(); + } + assert(((cogMethod->cmNumArgs)) == 0); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (1 * BytesPerWord),longAt(GIV(stackPointer))); + + /* stackValue:put: */ + longAtput(GIV(stackPointer),(((sqInt)cogMethod)) + entryOffset); + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),rcvr); + GIV(stackPointer) = sp; + ceCallCogCodePopReceiverReg(); +} + + +/* An external call or FFI primitive has failed. Build the frame and + activate as appropriate. Enter either the interpreter or machine + code depending on whether aPrimitiveMethod has been or is still + cogged. Note that we could always interpret but want the efficiency + of executing machine code if it is available. */ + + /* CoInterpreter>>#ceActivateFailingPrimitiveMethod: */ +void +ceActivateFailingPrimitiveMethod(sqInt aPrimitiveMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt arg; + sqInt argIndex; + sqInt fmt; + sqInt header; + usqInt i; + sqInt index; + int inInterpreter; + sqInt maxNumArgs; + sqInt methodHeader; + sqInt methodHeaderSqInt; + usqInt numArgs; + usqInt numSlots; + usqInt numSlotsUsqInt; + usqInt numTemps; + sqInt object; + sqInt objectClass; + sqInt rcvr; + sqInt result; + sqInt selectorOrProperties; + sqInt switched; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt types; + sqInt typesArraySize; + sqInt wordSize; + + assert(GIV(primFailCode) != 0); + assert(GIV(newMethod) == aPrimitiveMethod); + + /* If we're on Spur, retry the primitive, if appropriate, returning if successful after retry. */ + + /* ugh; annoying. Most users want the simulated address for the primitive (e.g. compilation in the Cogit). + But here we want the object for the primitive. So we choose to side-step the simulator's implementation + rather than change the other clients to do the mapFunctionToAddress: the simulator's version does. */ + primitiveFunctionPointer = functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(aPrimitiveMethod, (primitiveIndexOf(aPrimitiveMethod)), null); + retryPrimitiveOnFailure(); + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + if (!GIV(primFailCode)) { + result = longAt(GIV(stackPointer)); + assert(isPCWithinMethodZone(GIV(instructionPointer))); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(instructionPointer)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),result); + ceEnterCogCodePopReceiverReg(); + } + methodHeader = longAt((void *)((aPrimitiveMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + activateNewCogMethodinInterpreter(((CogMethod *) methodHeader), 0); + } + else { + /* begin activateNewMethod */ + /* begin collectArgumentsTypeFor:fromStackAt:count: */ + /* begin tempVarTypesArraysOf: */ + /* begin additionalMethodStateOf:at: */ + /* begin additionalMethodStateOf: */ + selectorOrProperties = penultimateLiteralOf(GIV(newMethod)); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l3; + } + wordSize = BytesPerWord; + maxNumArgs = (((lengthOf(tempVarTypesArrays)) < GIV(argumentCount)) ? (lengthOf(tempVarTypesArrays)) : GIV(argumentCount)); + for (argIndex = 0; argIndex < maxNumArgs; argIndex += 1) { + /* begin followObjField:ofObject: */ + types = longAt((void *)((tempVarTypesArrays + BaseHeaderSize) + ((((usqInt)(argIndex) << (shiftForWord())))))); + assert(isNonImmediate(types)); + if ((!((longAt((void *)(types))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + types = fixFollowedFieldofObjectwithInitialValue(argIndex, tempVarTypesArrays, types); + } + arg = longAt((void *)(GIV(stackPointer) + (argIndex * wordSize))); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (types) + && (isInstanceOfClassArray(types)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = arg & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(arg)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(types + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(types + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(types - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, types, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, types, objectClass)); + assert(isNonImmediate(types)); + if (oopisGreaterThanOrEqualTo(types, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(types + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(types); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2:; + } + /* end collectArgumentsTypeFor:fromStackAt:count: */ +l3: + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeaderSqInt = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeaderSqInt)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeaderSqInt)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(!(isOopForwarded(rcvr))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeaderSqInt); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeaderSqInt)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeaderSqInt) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeaderSqInt >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + if (((methodHeaderSqInt & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeaderSqInt); + } + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc). */ + switched = 1; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(canContextSwitchIfActivatingheader(GIV(newMethod), methodHeaderSqInt)); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + } +} + + +/* Return across a page boundary. The context to return to (which may be + married) is stored in the first word of the stack. We get here when a + return instruction jumps + to the ceBaseFrameReturn: address that is the return pc for base frames. A + consequence of this is that the current frame is no longer valid since an + interrupt may have overwritten + its state as soon as the stack pointer has been cut-back beyond the return + pc. So to have + a context to send the cannotReturn: message to we also store the base + frame's context + in the second word of the stack page. */ + + /* CoInterpreter>>#ceBaseFrameReturn: */ +sqInt +ceBaseFrameReturn(sqInt returnValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + char *callerFP; + sqInt contextToReturnFrom; + sqInt contextToReturnTo; + char *fp; + char *frameAbove; + sqInt header; + sqInt isAContext; + sqInt methodHeader; + StackPage *newPage; + sqInt senderOop; + char *sp; + StackPage *thePage; + + assert((stackPageFor(GIV(stackPointer))) == GIV(stackPage)); + assert((GIV(mostRecentlyUsedPage)) == GIV(stackPage)); + assertCStackWellAligned(); + assert(GIV(framePointer) == 0); + assert(GIV(stackPointer) <= (((GIV(stackPage)->baseAddress)) - BytesPerWord)); + assert((((GIV(stackPage)->baseFP)) + (2 * BytesPerWord)) < ((GIV(stackPage)->baseAddress))); + + /* We would like to use the following assert but we can't since the stack pointer will be above the + base frame pointer in the base frame return and hence the 0 a base frame pointer points at could + be overwritten which will cause the isBaseFrame assert in frameCallerContext: to fail. + self assert: (self frameCallerContext: stackPage baseFP) = (stackPages longAt: stackPage baseAddress). */ + assert((addressCouldBeObj(longAt(((GIV(stackPage)->baseAddress)) - BytesPerWord))) + && (isContext(longAt(((GIV(stackPage)->baseAddress)) - BytesPerWord)))); + contextToReturnTo = longAt((GIV(stackPage)->baseAddress)); + assert(addressCouldBeObj(contextToReturnTo)); + + /* The stack page is effectively free now, so free it. We must free it to be + correct in determining if contextToReturnTo is still married, and in case + makeBaseFrameFor: cogs a method, which may cause a code compaction, + in which case the frame must be free to avoid the relocation machinery + tracing the dead frame. Since freeing now temporarily violates the page-list + ordering invariant, use the assert-free version. */ + freeStackPageNoAssert(GIV(stackPage)); + isAContext = /* isContext: */ + ((!(contextToReturnTo & (tagMask())))) + && (((longAt((void *)(contextToReturnTo))) & (classIndexMask())) == ClassMethodContextCompactIndex); + if (isAContext + && (/* isStillMarriedContext: */ + (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(contextToReturnTo))))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + GIV(framePointer) = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(GIV(framePointer)), GIV(pages)); + if (GIV(framePointer) == ((thePage->headFP))) { + GIV(stackPointer) = (thePage->headSP); + } + else { + /* Returning to some interior frame, presumably because of a sender assignment. + Move the frames above to another page (they may be in use, e.g. via coroutining). + Make the interior frame the top frame. */ + + /* begin findFrameAbove:inPage: */ + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == GIV(framePointer)) { + frameAbove = 0; + goto l1; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == GIV(framePointer)) { + frameAbove = fp; + goto l1; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + frameAbove = 0; + /* end findFrameAbove:inPage: */ +l1: + + /* Since we've just deallocated a page we know that newStackPage won't deallocate an existing one. */ + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l2; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l2: + assert(newPage == GIV(stackPage)); + moveFramesInthroughtoPage(thePage, frameAbove, newPage); + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + } + } + else { + if (!(isAContext + && (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1)))) { + contextToReturnFrom = longAt(((GIV(stackPage)->baseAddress)) - BytesPerWord); + tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFromtoreturnValue(contextToReturnFrom, contextToReturnTo, returnValue); + + /* begin externalCannotReturn:from: */ + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),contextToReturnFrom); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + + /* Both ceBaseFrameReturnTrampoline & ceNonLocalReturnTrampoline pop + their caller's return pc into instructionPointer. In this uncommon case restore + it, since a send's call pushes the instructionPointer (after the arguments). */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), contextToReturnFrom, 1); + } + + /* void the instructionPointer to stop it being incorrectly updated in a code + compaction in makeBaseFrameFor:. */ + GIV(instructionPointer) = 0; + thePage = makeBaseFrameFor(contextToReturnTo); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + } + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + assert((stackPageFor(GIV(framePointer))) == GIV(stackPage)); + if ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())) { + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + ceEnterCogCodePopReceiverReg(); + } + GIV(instructionPointer) = longAt(GIV(stackPointer)); + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* stackTopPut: */ + longAtput(GIV(stackPointer),returnValue); + assert(checkIsStillMarriedContextcurrentFP(contextToReturnTo, GIV(framePointer))); + ceInvokeInterpret(); + + /* NOTREACHED */ + return null; +} + + +/* index is unboxed and 0-based. The call-back expects 1-based value (to + perform the operation with instVarAt:put: + */ + + /* CoInterpreter>>#ceCannotAssignTo:withIndex:valueToAssign: */ +#if IMMUTABILITY +sqInt +ceCannotAssignTowithIndexvalueToAssign(sqInt immutableObject, sqInt index, sqInt valueToAssign) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + sqInt top; + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),immutableObject); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),valueToAssign); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),(((usqInt)(index + 1) << 3) | 1)); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))), immutableObject, 2); +} +#endif /* IMMUTABILITY */ + + +/* A context that has been returned from, or otherwise has an invalid pc has + been reentered. + Until we have a cannotResume: selector, simply resend cannotReturn:. */ + + /* CoInterpreter>>#ceCannotResume */ +sqInt +ceCannotResume(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + sqInt resultOop; + + assert(isMachineCodeFrame(GIV(framePointer))); + assert(frameHasContext(GIV(framePointer))); + resultOop = longAt(GIV(stackPointer)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt(GIV(framePointer) + FoxThisContext)); + longAtput((GIV(stackPointer) -= BytesPerWord),resultOop); + + /* Make sure the happy couple remain returned from. */ + object = ceCannotResumePC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), longAt(GIV(framePointer) + FoxThisContext), 1); +} + + +/* Log failure and then retry if there's an accessorDepth or failure due to + no memory. + */ + + /* CoInterpreter>>#ceCheckAndMaybeRetryPrimitive: */ +void +ceCheckAndMaybeRetryPrimitive(sqInt primIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodSelectorOrImmediate; + sqInt retried; + sqInt tagBits; + + if ((recordPrimTrace()) + && (!primTracePluginName)) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveFailure << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + aMethodSelectorOrImmediate = /* fetchClassOf: */ + ((tagBits = (longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord))) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)))); + + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = aMethodSelectorOrImmediate; + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + retried = retryPrimitiveOnFailure(); + if (retried + && (recordPrimTrace())) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveRetry << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } +} + + /* CoInterpreter>>#ceCheckForInterrupt */ +void +ceCheckForInterrupt(void) +{ + sqInt switched; + + switched = checkForEventsMayContextSwitch(1); + returnToExecutivepostContextSwitch(0, switched); +} + + /* CoInterpreter>>#ceContext:instVar: */ +sqInt +ceContextinstVar(sqInt maybeContext, sqInt slotIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt result; + sqInt top; + + if (((longAt((void *)(maybeContext))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + result = externalInstVarofContext(slotIndex, maybeContext); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + else { + result = longAt((void *)((maybeContext + BaseHeaderSize) + ((((usqInt)(slotIndex) << (shiftForWord())))))); + } + return result; +} + + +/* genStorePop:MaybeContextReceiverVariable: filters out unmarried contexts + but not arbitrary objects in subclasses. It answers maybeMarriedContext so + that the StackToRegisterMappingCogit can keep ReceiverResultReg live. */ + + /* CoInterpreter>>#ceContext:instVar:value: */ +sqInt +ceContextinstVarvalue(sqInt maybeMarriedContext, sqInt slotIndex, sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt top; + + if ((((longAt((void *)(maybeMarriedContext))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (((((longAt((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + externalInstVarofContextput(slotIndex, maybeMarriedContext, anOop); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + else { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(slotIndex, maybeMarriedContext, anOop)); + assert(isNonImmediate(maybeMarriedContext)); + if (oopisGreaterThanOrEqualTo(maybeMarriedContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anOop & (tagMask())))) + && (oopisLessThan(anOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(maybeMarriedContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(maybeMarriedContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(slotIndex) << (shiftForWord()))))),anOop); + } + return maybeMarriedContext; +} + + /* CoInterpreter>>#ceInterpretMethodFromPIC:receiver: */ +sqInt +ceInterpretMethodFromPICreceiver(sqInt aMethodObj, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + CogMethod *pic; + sqInt primitiveIndex; + sqInt top; + + + /* pop off inner return and locate open or closed PIC */ + pic = ((CogMethod *) ((popStack()) - (interpretOffset()))); + assert((isCMOpenPIC(((CogBlockMethod *) pic))) + || (isCMClosedPIC(((CogBlockMethod *) pic)))); + + /* If found from an open PIC then it must be an uncogged method and, since it's been found + in the method cache, should be cogged if possible. If found from a closed PIC then at the + time the closed PIC was created the method was uncoggable, either because there was + no space, it had too many literals or it contained an illegal bytecode). So don't try and cog + it, but subsequently it may have been cogged via another path. If the method is, or ends up + cogged, jump to machine code, otherwise interpret. */ + if (((pic->cmType)) == CMOpenPIC) { + assert(!(methodHasCogMethod(aMethodObj))); + + /* begin methodShouldBeCogged: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + goto l1; + } + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(aMethodObj); + } + goto l2; +l1: + cogselector(aMethodObj, (pic->selector)); +l2:; + } + if (methodHasCogMethod(aMethodObj)) { + executeCogMethodfromUnlinkedSendWithReceiver(cogMethodOf(aMethodObj), rcvr); + } + GIV(messageSelector) = (pic->selector); + GIV(newMethod) = aMethodObj; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (aMethodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]))); + GIV(argumentCount) = (pic->cmNumArgs); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + return interpretMethodFromMachineCode(); +} + + /* CoInterpreter>>#ceMNUFromPICMNUMethod:receiver: */ +sqInt +ceMNUFromPICMNUMethodreceiver(sqInt aMethodObj, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cPIC; + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + sqInt primitiveIndex; + sqInt selector; + sqInt tagBits; + sqInt top; + + assert(addressCouldBeOop(rcvr)); + assert((aMethodObj == 0) + || ((addressCouldBeObj(aMethodObj)) + && (isOopCompiledMethod(aMethodObj)))); + selector = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(selector), lengthOfMaybeImmediate(selector), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + cPIC = ((CogMethod *) ((popStack()) - (mnuOffset()))); + assert((isCMClosedPIC(((CogBlockMethod *) cPIC))) + || (isCMOpenPIC(((CogBlockMethod *) cPIC)))); + GIV(argumentCount) = (cPIC->cmNumArgs); + GIV(messageSelector) = (cPIC->selector); + if (aMethodObj) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + createActualMessageTo(/* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr))); + if (/* maybeMethodHasCogMethod: */ + ((!(aMethodObj & (tagMask())))) + && ((((byteAt((void *)(aMethodObj + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + executeCogMethodfromUnlinkedSendWithReceiver(cogMethodOf(aMethodObj), rcvr); + + /* NOTREACHED */ + assert(0); + } + GIV(newMethod) = aMethodObj; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (aMethodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]))); + return interpretMethodFromMachineCode(); + } + + /* handleMNU:InMachineCodeTo:classForMessage: assumes lkupClass is set, since every other use is + after a lookupMethodNoMNUEtcInClass: call, which sets lkupClass. Here we must set it manually. + Global variables. Bah! */ + handleMNUInMachineCodeToclassForMessage(SelectorDoesNotUnderstand, rcvr, (GIV(lkupClass) = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)))); + + /* NOTREACHED */ + assert(0); + return 0; +} + + +/* Called from machine code to set an object's identity hash to a new hash. + N.B. may be called from machine code on the Smalltalk stack so this should + not call deeply. */ +/* We know anObject has not a hash yet (or this entry-point would not be + called. Sets the hash, then answers it as a smallinteger */ + + /* CoInterpreter>>#ceNewHashOf: */ +sqInt +ceNewHashOf(sqInt anObject) +{ + assert((isNonImmediate(anObject)) + && ((rawHashBitsOf(anObject)) == 0)); + return (((newHashBitsOf(anObject)) << 3) | 1); +} + + /* CoInterpreter>>#ceNonLocalReturn: */ +sqInt +ceNonLocalReturn(sqInt returnValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt closure; + sqInt contextToReturnTo; + sqInt currentCtx; + char *frameToReturnTo; + sqInt home; + StackPage *newPage; + sqInt nextCntx; + sqInt ourContext; + sqInt ourContextSqInt; + sqInt senderOop; + char *sp; + char *theFP; + char *theFP1; + StackPage *thePage; + StackPage *thePage1; + sqInt top; + sqInt unwindContextOrNilOrZero; + + + /* self shortPrintFrameAndCallers: framePointer. + self printOop: returnValue. + self halt. */ + frameToReturnTo = ((char *) 0); + theFP = ((char *) 0); + assert(isMachineCodeFrame(GIV(framePointer))); + assert(frameIsBlockActivation(GIV(framePointer))); + + /* Update the current page's headFrame pointers to enable the search for unwind protects below + to identify widowed contexts correctly. */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* Since this is a block activation the closure is on the stack above any args and the frame. */ + closure = longAt(GIV(framePointer) + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(GIV(framePointer)))->cmNumArgs) + : byteAt((GIV(framePointer) + FoxIFrameFlags) + 1)))) << (shiftForWord())))))); + + /* avoid compiler warning */ + /* Walk the closure's lexical chain to find the context or frame to return from (home). */ + home = null; + while (closure != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + home = longAt((void *)((closure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + assert(isNonImmediate(home)); + if ((!((longAt((void *)(home))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + home = fixFollowedFieldofObjectwithInitialValue(ClosureOuterContextIndex, closure, home); + } + if (!(/* isContext: */ + ((!(home & (tagMask())))) + && (((longAt((void *)(home))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* error: can't find home on chain; cannot return */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + ourContext = longAt(GIV(framePointer) + FoxThisContext); + goto l1; + } + ourContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l1: + + /* begin externalCannotReturn:from: */ + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),ourContext); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + + /* Both ceBaseFrameReturnTrampoline & ceNonLocalReturnTrampoline pop + their caller's return pc into instructionPointer. In this uncommon case restore + it, since a send's call pushes the instructionPointer (after the arguments). */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), ourContext, 1); + } + + /* begin followObjField:ofObject: */ + closure = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord())))))); + assert(isNonImmediate(closure)); + if ((!((longAt((void *)(closure))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + closure = fixFollowedFieldofObjectwithInitialValue(ClosureIndex, home, closure); + } + } + + /* home is to be returned from provided there is no unwind-protect activation between + this frame and home's sender. Search for an unwind. findUnwindThroughContext: + will answer either the context for an unwind-protect activation or nilObj if the sender + cannot be found or 0 if no unwind is found but the sender is. */ + unwindContextOrNilOrZero = findUnwindThroughContext(home); + if (unwindContextOrNilOrZero == GIV(nilObj)) { + /* error: can't find home on chain; cannot return */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + ourContext = longAt(GIV(framePointer) + FoxThisContext); + goto l2; + } + ourContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l2: + + /* begin externalCannotReturn:from: */ + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),ourContext); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + + /* Both ceBaseFrameReturnTrampoline & ceNonLocalReturnTrampoline pop + their caller's return pc into instructionPointer. In this uncommon case restore + it, since a send's call pushes the instructionPointer (after the arguments). */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), ourContext, 1); + } + if (unwindContextOrNilOrZero) { + /* begin externalAboutToReturn:through: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + ourContextSqInt = longAt(GIV(framePointer) + FoxThisContext); + goto l3; + } + ourContextSqInt = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l3: + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),ourContextSqInt); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),unwindContextOrNilOrZero); + + /* The ceNonLocalReturnTrampoline pops its caller's return pc into instructionPointer. + In this uncommon case restore it, since a send's call pushes the instructionPointer (after the arguments). */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAboutToReturn) << (shiftForWord())))))), ourContextSqInt, 2); + } + + /* Now we know home is on the sender chain. + We could be returning to either a context or a frame. Find out which. */ + contextToReturnTo = null; + if (((((longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert(checkIsStillMarriedContextcurrentFP(home, GIV(framePointer))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (longAt(theFP + FoxSavedFP)) { + frameToReturnTo = ((char *)(longAt(theFP + FoxSavedFP))); + } + else { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + contextToReturnTo = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(contextToReturnTo)); + assert((contextToReturnTo == (nilObject())) + || (isContext(followMaybeForwarded(contextToReturnTo)))); + } + } + else { + contextToReturnTo = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + if ((/* isContext: */ + ((!(contextToReturnTo & (tagMask())))) + && (((longAt((void *)(contextToReturnTo))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + assert(checkIsStillMarriedContextcurrentFP(contextToReturnTo, GIV(framePointer))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + frameToReturnTo = ((char *)(senderOop - (smallIntegerTag()))); + contextToReturnTo = null; + } + } + + /* If returning to a context we must make a frame for it unless it is dead. */ + if (contextToReturnTo) { + frameToReturnTo = establishFrameForContextToReturnTo(contextToReturnTo); + if (!frameToReturnTo) { + /* error: home's sender is dead; cannot return */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + ourContext = longAt(GIV(framePointer) + FoxThisContext); + goto l4; + } + ourContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l4: + + /* begin externalCannotReturn:from: */ + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),ourContext); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + + /* Both ceBaseFrameReturnTrampoline & ceNonLocalReturnTrampoline pop + their caller's return pc into instructionPointer. In this uncommon case restore + it, since a send's call pushes the instructionPointer (after the arguments). */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), ourContext, 1); + } + } + + /* Now we have a frame to return to. If it is on a different page we must + free intervening pages and nil out intervening contexts. We must free + intervening stack pages because if we leave the pages to be divorced + then their contexts will be divorced with intact senders and instruction + pointers. This code is similar to primitiveTerminateTo. */ + assert(pageListIsWellFormed()); + + /* begin stackPageFor: */ + newPage = stackPageAtpages(pageIndexFor(frameToReturnTo), GIV(pages)); + if (newPage != GIV(stackPage)) { + theFP1 = (GIV(stackPage)->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP1)); + thePage1 = stackPageAtpages(pageIndexFor(theFP1), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + assert(isContext(currentCtx)); + freeStackPage(GIV(stackPage)); + while (1) { + assert(isContext(currentCtx)); + if ((((((longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && ((stackPageFor((theFP = frameOfMarriedContext(currentCtx)))) == newPage)) break; + if (((((longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + theFP1 = (thePage->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP1)); + thePage1 = stackPageAtpages(pageIndexFor(theFP1), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + freeStackPage(thePage); + } + else { + nextCntx = longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + + /* begin markContextAsDead: */ + assert(isContext(currentCtx)); + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(SenderIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + currentCtx = nextCntx; + } + } + + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (newPage->headSP); + GIV(framePointer) = (newPage->headFP); + } + + /* Two cases. Returning to the top frame or an interior frame. The + top frame has its instruction pointer on top of stack. An interior + frame has its instruction pointer in the caller frame. We need to + peel back any frames on the page until we get to the correct frame. */ + if (GIV(framePointer) == frameToReturnTo) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + } + else { + do { + callerFP = GIV(framePointer); + GIV(framePointer) = ((char *)(longAt(GIV(framePointer) + FoxSavedFP))); + } while(GIV(framePointer) != frameToReturnTo); + GIV(instructionPointer) = longAt(callerFP + FoxCallerSavedIP); + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(callerFP))); + GIV(stackPointer) = (callerFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(callerFP))->cmNumArgs) + : byteAt((callerFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + return returntoExecutive(returnValue, 0); +} + + /* CoInterpreter>>#ceReapAndResetErrorCodeFor: */ +void +ceReapAndResetErrorCodeFor(CogMethod *cogMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(GIV(primFailCode) != 0); + GIV(newMethod) = (cogMethod->methodObject); + reapAndResetErrorCodeToheader(GIV(stackPointer) + BytesPerWord, (cogMethod->methodHeader)); +} + + +/* Entry-point for an abort send in a CogMethod (aboutToReturn:through:, + cannotReturn: et al). + Try and dispatch the send, but the send may turn into an MNU in which case + defer to + handleMNUInMachineCodeTo:... which will dispatch the MNU. + + Continue execution via either executeMethod or + interpretMethodFromMachineCode: depending on whether the target method is + cogged or not. */ + + /* CoInterpreter>>#ceSendAbort:to:numArgs: */ +static NoDbgRegParms sqInt +ceSendAborttonumArgs(sqInt selector, sqInt rcvr, sqInt numArgs) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classObj; + sqInt classTablePage; + sqInt classTag; + CogMethod *cogMethod; + sqInt errSelIdx; + sqInt fieldIndex; + sqInt methodHeader; + sqInt tagBits; + sqInt top; + + + /* self printExternalHeadFrame */ + /* self printStringOf: selector */ + assertCStackWellAligned(); + assert(addressCouldBeOop(rcvr)); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(selector), lengthOfMaybeImmediate(selector), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + GIV(argumentCount) = numArgs; + classTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + if (lookupInMethodCacheSelclassTag(selector, classTag)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, selector); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), selector); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + GIV(messageSelector) = selector; + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((classTag >= 0) + && ((classTag <= (tagMask())) + || ((classTag >= (arrayClassIndexPun())) + && (classTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classObj = null; + goto l1; + } + fieldIndex = classTag & ((1U << (classTableMajorIndexShift())) - 1); + classObj = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l1: + if ((errSelIdx = lookupOrdinaryNoMNUEtcInClass(classObj))) { + handleMNUInMachineCodeToclassForMessage(errSelIdx, rcvr, classObj); + + /* NOTREACHED */ + assert(0); + } + } + + /* check for coggability because method is in the cache */ + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (/* maybeMethodHasCogMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + executeNewMethod(); + assert(0); + } + return interpretMethodFromMachineCode(); +} + + +/* Send from an Open PIC when the first-level method lookup probe has failed, + or to continue when PIC creation has failed (e.g. because we're out of + code space), + or when a send has failed due to a forwarded receiver. */ +/* self printFrame: stackPage headFP WithSP: stackPage headSP */ +/* self printStringOf: selector */ + + /* CoInterpreter>>#ceSendFromInLineCacheMiss: */ +sqInt +ceSendFromInLineCacheMiss(CogMethod *cogMethodOrPIC) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classObj; + sqInt classTablePage; + sqInt classTag; + CogMethod *cogMethod; + sqInt errSelIdx; + sqInt fieldIndex; + sqInt methodHeader; + sqInt rcvr; + sqInt selector; + sqInt tagBits; + sqInt top; + + GIV(argumentCount) = (cogMethodOrPIC->cmNumArgs); + + /* skip return pc */ + rcvr = longAt(GIV(stackPointer) + ((((cogMethodOrPIC->cmNumArgs)) + 1) * BytesPerWord)); + assert(addressCouldBeOop(rcvr)); + classTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + + /* would like to assert this but must also allow for an interpretable method in the cache. */ + if (lookupInMethodCacheSelclassTag((cogMethodOrPIC->selector), classTag)) { + selector = (cogMethodOrPIC->selector); + + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, selector); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), selector); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + if (/* isOopForwarded: */ + ((!(((cogMethodOrPIC->selector)) & (tagMask())))) + && ((!((longAt((void *)((cogMethodOrPIC->selector)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + handleForwardedSelectorFaultFor((cogMethodOrPIC->selector)); + return ceSendFromInLineCacheMiss(cogMethodOrPIC); + } + if (classTag == (isForwardedObjectClassIndexPun())) { + handleForwardedSendFaultForReceiverstackDelta(rcvr, 1); + + /* skip return pc */ + return ceSendFromInLineCacheMiss(cogMethodOrPIC); + } + GIV(messageSelector) = (cogMethodOrPIC->selector); + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((classTag >= 0) + && ((classTag <= (tagMask())) + || ((classTag >= (arrayClassIndexPun())) + && (classTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classObj = null; + goto l1; + } + fieldIndex = classTag & ((1U << (classTableMajorIndexShift())) - 1); + classObj = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l1: + if ((errSelIdx = lookupOrdinaryNoMNUEtcInClass(classObj))) { + handleMNUInMachineCodeToclassForMessage(errSelIdx, rcvr, classObj); + + /* NOTREACHED */ + assert(0); + } + } + + /* check for coggability because method is in the cache */ + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (/* maybeMethodHasCogMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + executeNewMethod(); + assert(0); + } + return interpretMethodFromMachineCode(); +} + + +/* For RegisterAllocatingCogit we want the pc following a conditional branch + not to be reachable, so + we don't have to generate code to reload registers. But notionally the pc + following a conditional + branch is reached when continuing from a mustBeBoolean error. Instead of + supporting this in the + JIT, simply convert to an interpreter frame, backup the pc to the branch, + reenter the interpreter + and hence retry the mustBeBoolean send therein. N.B. We could do this for + immutability violations + too, but immutability is used in actual applications and so should be + performant, whereas + mustBeBoolean errors are extremely rare and so we choose brevity over + performance in this case. */ + + /* CoInterpreter>>#ceSendMustBeBooleanTo:interpretingAtDelta: */ +sqInt +ceSendMustBeBooleanTointerpretingAtDelta(sqInt aNonBooleanObject, sqInt jumpSize) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + sqInt top; + + assert(addressCouldBeOop(aNonBooleanObject)); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + convertFrametoInterpreterFrame(GIV(framePointer), jumpSize); + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),aNonBooleanObject); + GIV(stackPointer) = sp; + + /* and now reenter the interpreter... */ + ceInvokeInterpret(); + + /* NOTREACHED */ + return null; +} + + /* CoInterpreter>>#ceSendMustBeBoolean: */ +sqInt +ceSendMustBeBoolean(sqInt anObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + sqInt top; + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),anObject); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))), anObject, 0); +} + + +/* Entry-point for an unlinked directed super send in a CogMethod. Smalltalk + stack looks like + receiver + args + head sp -> sender return pc + methodClassBinding is an association whose value is the class above which + to start the lookup. */ + + /* CoInterpreter>>#ceSend:aboveClassBinding:to:numArgs: */ +void +ceSendaboveClassBindingtonumArgs(sqInt selector, sqInt methodClassBinding, sqInt rcvr, sqInt numArgs) +{ + ceSendabovetonumArgs(selector, fetchPointerofObject(ValueIndex, /* followMaybeForwarded: */ + (/* isOopForwarded: */ + ((!(methodClassBinding & (tagMask())))) + && ((!((longAt((void *)(methodClassBinding))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + ? followForwarded(methodClassBinding) + : methodClassBinding)), rcvr, numArgs); +} + + +/* Entry-point for an unlinked directed super send in a CogMethod. Smalltalk + stack looks like + receiver + args + head sp -> sender return pc + methodClass is the class above which to start the lookup. + + If an MNU then defer to handleMNUInMachineCodeTo:... which will dispatch + the MNU and + may choose to allocate a closed PIC with a fast MNU dispatch for this + send. Otherwise + attempt to link the send site as efficiently as possible. All link + attempts may fail; e.g. + because we're out of code memory. + + Continue execution via either executeMethod or + interpretMethodFromMachineCode: depending on whether the target method is + cogged or not. */ + + /* CoInterpreter>>#ceSend:above:to:numArgs: */ +sqInt +ceSendabovetonumArgs(sqInt selector, sqInt methodClass, sqInt rcvr, sqInt numArgs) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classObj; + sqInt classObjSqInt; + sqInt classPointer; + sqInt classTablePage; + sqInt classTag; + CogMethod *cogMethod; + CogMethod *cogMethod1; + sqInt err; + sqInt errSelIdx; + sqInt fieldIndex; + int hash; + sqInt methodHeader; + CogMethod *newCogMethod; + sqInt objOop; + sqInt tagBits; + sqInt top; + + + /* self printExternalHeadFrame */ + /* self printStringOf: selector */ + assertCStackWellAligned(); + assert(addressCouldBeOop(rcvr)); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(selector), lengthOfMaybeImmediate(selector), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + classPointer = /* followMaybeForwarded: */ + (/* isOopForwarded: */ + ((!(methodClass & (tagMask())))) + && ((!((longAt((void *)(methodClass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + ? followForwarded(methodClass) + : methodClass); + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, objOop); + } + classObjSqInt = objOop; + + /* begin classTagForClass: */ + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObjSqInt)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classTag = ((hash = (long32At((void *)(classObjSqInt + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObjSqInt) + ? ((err = enterIntoClassTable(classObjSqInt)) + ? -err + : (long32At((void *)(classObjSqInt + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + GIV(argumentCount) = numArgs; + if (lookupInMethodCacheSelclassTag(selector, classTag)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod1 = ((CogMethod *) methodHeader); + if (((cogMethod1->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod1, selector); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), selector); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + assert(!((isForwardedClassTag(classTag)))); + if (/* isOopForwarded: */ + ((!(selector & (tagMask())))) + && ((!((longAt((void *)(selector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + return ceSendabovetonumArgs(handleForwardedSelectorFaultFor(selector), methodClass, rcvr, numArgs); + } + GIV(messageSelector) = selector; + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((classTag >= 0) + && ((classTag <= (tagMask())) + || ((classTag >= (arrayClassIndexPun())) + && (classTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classObj = null; + goto l1; + } + fieldIndex = classTag & ((1U << (classTableMajorIndexShift())) - 1); + classObj = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l1: + if ((errSelIdx = lookupOrdinaryNoMNUEtcInClass(classObj))) { + if ((errSelIdx == SelectorDoesNotUnderstand) + && ((((usqInt)((cogMethod = cogMNUPICSelectorreceivermethodOperandnumArgs(GIV(messageSelector), rcvr, mnuMethodOrNilFor(rcvr), GIV(argumentCount)))))) > (minCogMethodAddress()))) { + linkSendAtintooffsetreceiver(longAt(GIV(stackPointer)), mframeHomeMethod(GIV(framePointer)), cogMethod, noCheckEntryOffset(), rcvr); + } + handleMNUInMachineCodeToclassForMessage(errSelIdx, rcvr, classObj); + assert(0); + } + } + + /* check for coggability because method is in the cache + Method found and has a cog method. Attempt to link to it. The receiver's class may be young. + We must not link to an Open PIC since they perform normal sends. */ + if (/* maybeMethodHasCogMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, selector); + } + else { + /* Deal with anonymous accessors, e.g. in Newspeak. The cogMethod may not have the + correct selector. If not, try and compile a new method with the correct selector. */ + if (((cogMethod->selector)) != selector) { + if ((newCogMethod = cogselector(GIV(newMethod), selector))) { + cogMethod = newCogMethod; + } + } + } + if (((cogMethod->selector)) == selector) { + linkSendAtintooffsetreceiver(longAt(GIV(stackPointer)), mframeHomeMethod(GIV(framePointer)), cogMethod, noCheckEntryOffset(), rcvr); + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + executeNewMethod(); + assert(0); + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + return interpretMethodFromMachineCode(); +} + + +/* Entry-point for an unlinked send in a CogMethod. Smalltalk stack looks + like receiver + args + head sp -> sender return pc + + If an MNU then defer to handleMNUInMachineCodeTo:... which will dispatch + the MNU and + may choose to allocate a closed PIC with a fast MNU dispatch for this + send. Otherwise + attempt to link the send site as efficiently as possible. All link + attempts may fail; e.g. + because we're out of code memory. + + Continue execution via either executeMethod or + interpretMethodFromMachineCode: depending on whether the target method is + cogged or not. */ + + /* CoInterpreter>>#ceSend:super:to:numArgs: */ +sqInt +ceSendsupertonumArgs(sqInt selector, sqInt superNormalBar, sqInt rcvr, sqInt numArgs) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classObj; + sqInt classObjSqInt; + sqInt classPointer; + sqInt classTablePage; + sqInt classTag; + CogMethod *cogMethod; + CogMethod *cogMethod1; + sqInt err; + sqInt errSelIdx; + sqInt fieldIndex; + int hash; + sqInt literal; + sqInt methodHeader; + sqInt methodPointer; + CogMethod *newCogMethod; + sqInt objOop; + sqInt offset; + sqInt tagBits; + sqInt top; + + + /* self printExternalHeadFrame */ + /* self printStringOf: selector */ + assertCStackWellAligned(); + assert(addressCouldBeOop(rcvr)); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(selector), lengthOfMaybeImmediate(selector), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + if (superNormalBar) { + methodPointer = /* frameMethodObject: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(GIV(framePointer)))->methodObject) + : longAt(GIV(framePointer) + FoxMethod)); + + /* begin methodClassOf: */ + offset = (literalCountOf(methodPointer)) - 1; + literal = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = fixFollowedFieldofObjectwithInitialValue(offset + LiteralStart, methodPointer, literal); + } + classPointer = ((literal != GIV(nilObj)) + && (/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + ? (assert((numSlotsOf(literal)) > ValueIndex), + followFieldofObject(ValueIndex, literal)) + : GIV(nilObj)); + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, objOop); + } + classObjSqInt = objOop; + + /* begin classTagForClass: */ + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObjSqInt)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classTag = ((hash = (long32At((void *)(classObjSqInt + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObjSqInt) + ? ((err = enterIntoClassTable(classObjSqInt)) + ? -err + : (long32At((void *)(classObjSqInt + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + } + else { + classTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + } + GIV(argumentCount) = numArgs; + if (lookupInMethodCacheSelclassTag(selector, classTag)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod1 = ((CogMethod *) methodHeader); + if (((cogMethod1->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod1, selector); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), selector); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + if (/* isOopForwarded: */ + ((!(selector & (tagMask())))) + && ((!((longAt((void *)(selector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + return ceSendsupertonumArgs(handleForwardedSelectorFaultFor(selector), superNormalBar, rcvr, numArgs); + } + if (classTag == (isForwardedObjectClassIndexPun())) { + assert(superNormalBar == 0); + return ceSendsupertonumArgs(selector, superNormalBar, handleForwardedSendFaultForReceiverstackDelta(rcvr, 1), numArgs); + } + GIV(messageSelector) = selector; + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((classTag >= 0) + && ((classTag <= (tagMask())) + || ((classTag >= (arrayClassIndexPun())) + && (classTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classObj = null; + goto l1; + } + fieldIndex = classTag & ((1U << (classTableMajorIndexShift())) - 1); + classObj = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l1: + if ((errSelIdx = lookupOrdinaryNoMNUEtcInClass(classObj))) { + if ((errSelIdx == SelectorDoesNotUnderstand) + && ((((usqInt)((cogMethod = cogMNUPICSelectorreceivermethodOperandnumArgs(GIV(messageSelector), rcvr, mnuMethodOrNilFor(rcvr), GIV(argumentCount)))))) > (minCogMethodAddress()))) { + linkSendAtintooffsetreceiver(longAt(GIV(stackPointer)), mframeHomeMethod(GIV(framePointer)), cogMethod, (superNormalBar + ? noCheckEntryOffset() + : entryOffset()), rcvr); + } + handleMNUInMachineCodeToclassForMessage(errSelIdx, rcvr, classObj); + assert(0); + } + + /* Methods towards the roots of the class hierarchy are shared by many subclasses. + It is likely to be profitable to cog these methods but becAuse they're being sent to + instances of different classes they may not be found in the cache. Luckily we have + lastMethodCacheProbeWrite to track recent writes to the cache and hence can detect + when a method is used more than once but via different classes. */ + if ((GIV(methodCache)[GIV(lastMethodCacheProbeWrite) + MethodCacheMethod]) == GIV(newMethod)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod1 = ((CogMethod *) methodHeader); + if (((cogMethod1->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod1, selector); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), selector); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + } + + /* check for coggability because method is in the cache + Method found and has a cog method. Attempt to link to it. The receiver's class may be young. + If the Cogit can't store young classes in inline caches we can link to an open PIC instead. */ + if (/* maybeMethodHasCogMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, selector); + } + else { + /* Deal with anonymous accessors, e.g. in Newspeak. The cogMethod may not have the + correct selector. If not, try and compile a new method with the correct selector. */ + if (((cogMethod->selector)) != selector) { + if ((newCogMethod = cogselector(GIV(newMethod), selector))) { + cogMethod = newCogMethod; + } + } + } + if (((cogMethod->selector)) == selector) { + linkSendAtintooffsetreceiver(longAt(GIV(stackPointer)), mframeHomeMethod(GIV(framePointer)), cogMethod, (superNormalBar + ? noCheckEntryOffset() + : entryOffset()), rcvr); + } + else { + if (!superNormalBar) { + patchToOpenPICFornumArgsreceiver(selector, numArgs, rcvr); + } + } + + /* If patchToOpenPICFor:.. returns we're out of code memory */ + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + executeNewMethod(); + assert(0); + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + return interpretMethodFromMachineCode(); +} + + +/* If contextSwitchIfNotNil is nil we can't context switch. + contextSwitchIfNotNil is set to nil by + - the special primitiveClosureValueNoContextSwitch entry-point in block + dispatch - the stack check in methods with primitive 198. + In a normal method contextSwitchIfNotNil will be the method (see e.g. + SimpleStackBasedCogit>>compileFrameBuild). In a block it will be the + closure (see e.g. SimpleStackBasedCogit>>compileMethodBody). */ + + /* CoInterpreter>>#ceStackOverflow: */ +void +ceStackOverflow(sqInt contextSwitchIfNotNil) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ceMethodAbortRetAddr; + CogBlockMethod *cogMethod; + sqInt switched; + + /* begin popStack */ + ceMethodAbortRetAddr = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* begin mframeCogMethod: */ + cogMethod = ((CogBlockMethod *) ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodMask)); + assert((ceMethodAbortRetAddr - (abortOffset())) == (((usqInt)(asCogHomeMethod(cogMethod))))); + GIV(instructionPointer) = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + GIV(method) = (GIV(newMethod) = (GIV(messageSelector) = GIV(nilObj))); + switched = handleStackOverflowOrEventAllowContextSwitch(contextSwitchIfNotNil != 0); + returnToExecutivepostContextSwitch(0, switched); + error("should not be reached"); +} + + +/* The high resolution timer has been found to be greater than + nextProfileTick after calling a primitive. + Now take a sample iff nextProfileTick has not been reset. c.f. + checkProfileTickPostPrimitive:. If the + primitive succeeds then signal the profile semaphore (activate the + profiling process) immediately + so that the sample is attributable to the current sender. */ + + /* CoInterpreter>>#ceTakeProfileSample */ +void +ceTakeProfileSample(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + + /* avoid calling assert in real VM cuz this may run on Smalltalk stack. */ + if (!GIV(profileProcess)) { + if (GIV(nextProfileTick) > 0) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + GIV(profileProcess) = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + if (!GIV(primFailCode)) { + GIV(profileMethod) = GIV(newMethod); + + /* Zero nextProfileTick because signalling the semaphore is when the VM effectively delivers the sample. */ + + /* begin zeroNextProfileTick */ + GIV(nextProfileTick) = 0; + if (synchronousSignal(GIV(profileSemaphore))) { + returnToExecutivepostContextSwitch(0, 1); + assert(!(1)); + } + else { + /* we reach here when there is a higher priority process than the profile process active, e.g. the timer process */ + } + } + } + else { + GIV(profileMethod) = null; + } + } + + /* if not nil a sample has already been taken. + force an interrupt check, but do not use forceInterruptCheck (which may + call interruptCheckChain) so that this can be run on the Smalltalk stack + from compileOnStackExternalPrimitive:flags: */ + GIV(stackLimit) = ((char *) (((usqInt) -1))); +} + + /* CoInterpreter>>#ceTraceBlockActivation */ +void +ceTraceBlockActivation(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt thingOne; + + if (recordBlockTrace()) { + thingOne = ((mframeHomeMethod(GIV(framePointer)))->methodObject); + + /* begin recordTrace:source:thing:extra: */ + if (TraceLog) { + GIV(traceLog)[GIV(traceLogIndex)] = ((((((usqInt)(TraceBlockActivation) << TraceTypeShift)))) + CSFromMachineCode); + GIV(traceLog)[GIV(traceLogIndex) + 1] = thingOne; + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + if (printOnTrace()) { + printActivationNameForreceiverisBlockfirstTemporary(((mframeHomeMethod(GIV(framePointer)))->methodObject), /* frameReceiver: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? longAt(GIV(framePointer) + FoxMFReceiver) + : longAt(GIV(framePointer) + FoxIFReceiver)), 1, null); + cr(); + } + } +} + + /* CoInterpreter>>#ceTraceLinkedSend: */ +void +ceTraceLinkedSend(sqInt theReceiver) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cogMethod; + sqInt selector; + sqInt tagBits; + sqInt thingOne; + sqInt thingTwo; + + cogMethod = ((CogMethod *) ((longAt(GIV(stackPointer))) - (traceLinkedSendOffset()))); + if (((cogMethod->cmNumArgs)) > (numRegArgs())) { + } + else { + } + GIV(stackPointer) + BytesPerWord; + + /* stackValue: */ + longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* empty maybeCheckStackDepth:sp:pc: */ + + /* cogit recordSendTrace ifTrue: is implicit; wouldn't compile the call otherwise. */ + thingOne = /* fetchClassOf: */ + ((tagBits = theReceiver & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(theReceiver)); + thingTwo = (cogMethod->selector); + + /* begin recordTrace:source:thing:extra: */ + if (TraceLog) { + GIV(traceLog)[GIV(traceLogIndex)] = 1 /* (TraceSend + CSFromMachineCode) */; + GIV(traceLog)[GIV(traceLogIndex) + 1] = thingOne; + GIV(traceLog)[GIV(traceLogIndex) + 2] = thingTwo; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + if (printOnTrace()) { + printActivationNameForreceiverisBlockfirstTemporary((cogMethod->methodObject), theReceiver, 0, null); + cr(); + } + selector = (cogMethod->selector); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(selector), lengthOfMaybeImmediate(selector), /* fetchClassTagOf: */ + ((tagBits = theReceiver & (tagMask())) + ? tagBits + : (longAt((void *)(theReceiver))) & (classIndexMask()))); +} + + +/* For assertion checking. */ + + /* CoInterpreter>>#ceTraceStoreOf:into: */ +void +ceTraceStoreOfinto(sqInt aValue, sqInt anObject) +{ + assert((isImmediate(aValue)) + || (addressCouldBeObj(aValue))); + assert(addressCouldBeObj(anObject)); +} + + /* CoInterpreter>>#cFramePointerAddress */ +usqInt +cFramePointerAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(CFramePointer)))); +} + + /* CoInterpreter>>#checkAssertsEnabledInCoInterpreter */ +void +checkAssertsEnabledInCoInterpreter(void) +{ + sqInt assertsAreEnabledInCoInterpreter; + + assertsAreEnabledInCoInterpreter = 0; + assert(assertsAreEnabledInCoInterpreter); +} + + +/* Perform an integrity/leak check using the heapMap. Assume + clearLeakMapAndMapAccessibleObjects has set a bit at each + object's header. Check that all object references in machine + code are valid. Answer if all checks pass. */ + + /* CoInterpreter>>#checkCodeIntegrity: */ +static NoDbgRegParms sqInt +checkCodeIntegrity(sqInt gcModes) +{ + return checkIntegrityOfObjectReferencesInCode(gcModes); +} + + +/* In Spur a primitive may fail due to encountering a forwarder. On failure, + check the accessorDepth for the + primitive and if non-negative scan the args to the depth, following any + forwarders. Answer if any are found + so the prim can be retried. The primitive index is derived from newMethod. + + See + http://www.mirandabanda.org/cogblog/2014/02/08/primitives-and-the-partial-read-barrier/ + and SpurMemoryManager's class comment. */ +/* Override to log and export to Cogit */ + + /* CoInterpreter>>#checkForAndFollowForwardedPrimitiveState */ +sqInt +checkForAndFollowForwardedPrimitiveState(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt accessorDepth; + sqInt aMethodSelectorOrImmediate; + usqInt firstBytecode; + sqInt found; + sqInt header; + sqInt index; + sqInt methodHeader; + sqInt oop; + sqInt primIndex; + sqInt referent; + sqInt scannedStackFrame; + sqInt tagBits; + + if ((recordPrimTrace()) + && (!primTracePluginName)) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveFailure << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + aMethodSelectorOrImmediate = /* fetchClassOf: */ + ((tagBits = (longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord))) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)))); + + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = aMethodSelectorOrImmediate; + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + assert(failed()); + found = 0; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIndex = 0; + } + assert((GIV(argumentCount) == (argumentCountOf(GIV(newMethod)))) + || (isMetaPrimitiveIndex(primIndex))); + + /* First things first; make sure the metadata has been followed before it is accessed to derive accessorDepth... */ + if (((/* isCalloutPrimitiveIndex: */ + (primIndex == PrimNumberExternalCall) + || (primIndex == PrimNumberFFICall)) + || (primIndex == PrimNumberDoExternalCall)) + && (unfollowFirstLiteralOfMaybeCalloutMethodprimitiveIndex(GIV(newMethod), primIndex))) { + found = 1; + } + + /* If the primitive is one of the meta primitives PrimNumberDoPrimitive or PrimNumberDoExternalCall, then + metaAccessorDepth will have been set to nil at the start of the primitive, and to the accessor depth of the + called primitive (or external call) immediately before dispatch. Hence if primIndex is that of a meta primitive + then if metaAccessorDepth is -2, the accessor depth is that of the meta primitive, and if > -2, then + metaAccessorDepth is the accessor depth of the primitive (or external call). Similarly, if the primitive is + primitiveExternalCall then the accessor depth is that of primitiveExternalCall until primitiveFunctionPointer + is assigned, at which point the accessor depth is taken from the slot in newMethod's first literal. */ + accessorDepth = ((/* isMetaPrimitiveIndex: */ + (primIndex == PrimNumberDoPrimitive) + || (primIndex == PrimNumberDoExternalCall)) + && (GIV(metaAccessorDepth) > -2) + ? GIV(metaAccessorDepth) + : accessorDepthForPrimitiveMethod(GIV(newMethod))); + assert(((accessorDepth >= -1) && (accessorDepth <= 5))); + if (accessorDepth >= 0) { + scannedStackFrame = 0; + for (index = 0; index <= GIV(argumentCount); index += 1) { + oop = longAt((void *)(GIV(stackPointer) + (index * BytesPerWord))); + if ((!(oop & (tagMask())))) { + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + assert(index < GIV(argumentCount)); + found = 1; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + + /* stackValue:put: */ + longAtput((void *)(GIV(stackPointer) + (index * BytesPerWord)),oop); + if (!scannedStackFrame) { + scannedStackFrame = 1; + + /* Avoid repeated primitive failures by following all state in the current stack frame. */ + followForwardedFrameContentsstackPointer(GIV(framePointer), GIV(stackPointer) + ((GIV(argumentCount) + 1) * BytesPerWord)); + } + } + if ((accessorDepth > 0) + && ((/* hasPointerFields: */ + ((!(oop & (tagMask())))) + && (hasPointerFieldsNonImm(oop))) + && (followForwardedObjectFieldstoDepth(oop, accessorDepth)))) { + found = 1; + } + } + } + } + if (found + && (recordPrimTrace())) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveRetry << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + return found; +} + + +/* Necessary because we very much want CStackPointer and CFramePointer to be + static/private and grouped + with other interpreter variables which will hence be accessed via + VarBaseReg on platforms that have one. + */ + + /* CoInterpreter>>#checkIfCFramePointerInUse */ +sqInt +checkIfCFramePointerInUse(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return isCFramePointerInUse((&GIV(CFramePointer)), (&GIV(CStackPointer))); +} + + /* CoInterpreter>>#checkingLongRunningPrimitives */ +#if LRPCheck +int +checkingLongRunningPrimitives(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(longRunningPrimitiveCheckSemaphore); +} +#endif /* LRPCheck */ + + +/* Check if the argument is an ok object. + If this is a pointers object, check that its fields are all okay oops. */ + + /* CoInterpreter>>#checkOkayFields: */ +static NoDbgRegParms sqInt +checkOkayFields(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fieldOop; + sqInt hasYoung; + sqInt i; + sqInt iSqInt; + + if ((oop == null) + || (oop == 0)) { + return 1; + } + if ((((oop) & 7) == 1)) { + return 1; + } + if (!(checkOkayOop(oop))) { + return 0; + } + if (!(checkOopHasOkayClass(oop))) { + return 0; + } + if (!((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + || (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return 1; + } + hasYoung = 0 /* (hasSpurMemoryManagerAPI not) */; + i = (numPointerSlotsOf(oop)) - 1; + while (i >= 0) { + fieldOop = longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(fieldOop & (smallIntegerTag())))) { + if ((i == 0) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) { + if (!(methodFor(((void *)fieldOop)))) { + fprintf(GIV(transcript), + "method %p has an invalid cog method reference\n", + ((void *)oop)); + return 0; + } + } + else { + hasYoung = hasYoung + || (/* isYoung: */ + ((!(fieldOop & (tagMask())))) + && (oopisLessThan(fieldOop, GIV(oldSpaceStart)))); + if (!(checkOkayOop(fieldOop))) { + return 0; + } + if (!(checkOopHasOkayClass(fieldOop))) { + return 0; + } + } + } + i -= 1; + } + if (hasYoung) { + /* begin checkOkayYoungReferrer: */ + if (oopisLessThan(oop, GIV(oldSpaceStart))) { + return 1; + } + if (!((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + fprintf(GIV(transcript), + "remembered bit is not set in %p\n", + ((void *)oop)); + return 0; + } + + /* begin isInRememberedSet: */ + for (iSqInt = 0; iSqInt < GIV(rememberedSetSize); iSqInt += 1) { + if ((GIV(rememberedSet)[iSqInt]) == oop) { + return 1; + } + } + fprintf(GIV(transcript), + "%p has remembered bit set but is not in remembered set\n", + ((void *)oop)); + return 0; + } + return 1; +} + + +/* Check if the profile timer has expired during a primitive and if so take a + sample. If the primitive has failed sample the profileMethod as nil. */ +/* If continuing from primitive[Full]ClosureValueNoContextSwitch defer until + the next check + */ + + /* CoInterpreter>>#checkProfileTickPostPrimitive */ +static NeverInline void +checkProfileTickPostPrimitive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + + if (GIV(deferProfileCheckForVNCS)) { + GIV(deferProfileCheckForVNCS) = 0; + } + else { + if ((GIV(nextProfileTick) > 0) + && ((ioHighResClock()) >= GIV(nextProfileTick))) { + if (!GIV(profileProcess)) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + GIV(profileProcess) = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + if (GIV(primFailCode)) { + GIV(profileMethod) = null; + } + else { + GIV(profileMethod) = GIV(newMethod); + + /* begin zeroNextProfileTick */ + GIV(nextProfileTick) = 0; + if (synchronousSignal(GIV(profileSemaphore))) { + returnToExecutivepostContextSwitch(0, 1); + assert(!(1)); + } + else { + /* we reach here when there is a higher priority process than the profile process active, e.g. the timer process */ + } + } + forceInterruptCheck(); + } + } + } +} + + /* CoInterpreter>>#cogMethodHasRealNonProfilingPrimitive: */ +static NoDbgRegParms sqInt +cogMethodHasRealNonProfilingPrimitive(CogMethod *cogMethod) +{ + return (((cogMethod->cmType)) >= CMMethod) + && (isRealNonProfilingPrimitiveMethod((cogMethod->methodObject))); +} + + /* CoInterpreter>>#cogMethodHasTooManyLiterals: */ +sqInt +cogMethodHasTooManyLiterals(CogMethod *aCogMethod) +{ + return !(((/* begin literalCountOfMethodHeader: */ + assert((((((aCogMethod->methodHeader))) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((((aCogMethod->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile); +} + + /* CoInterpreter>>#cogMethodOf: */ +CogMethod * +cogMethodOf(sqInt aMethodOop) +{ + sqInt methodHeader; + + methodHeader = longAt((void *)((aMethodOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + return ((CogMethod *) methodHeader); +} + + /* CoInterpreter>>#commenceCogCompiledCodeCompaction */ +static void +commenceCogCompiledCodeCompaction(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong startTime; + sqInt top; + + GIV(cogCompiledCodeCompactionCalledFor) = 0; + if (recordEventTrace()) { + /* begin recordTrace: */ + /* begin recordTrace:source:thing:extra: */ + if (TraceLog) { + GIV(traceLog)[GIV(traceLogIndex)] = ((((((usqInt)(TraceCodeCompaction) << TraceTypeShift))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = 0; + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + } + if (recordPrimTrace()) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TraceCodeCompaction << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + startTime = ioUTCMicrosecondsNow(); + + /* This can be called in a number of circumstances. The instructionPointer + may contain a native pc that must be relocated. There may already be a + pushed instructionPointer on stack. Clients ensure that instructionPointer + is 0 if it should not be pushed and/or relocated. Pushing twice is a mistake + because only the top one will be relocated. */ + if (GIV(instructionPointer)) { + /* better not have already been pushed */ + assert((((usqInt)(stackTop()))) != GIV(instructionPointer)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + (GIV(stackPage)->headSP = GIV(stackPointer)); + } + assertValidStackedInstructionPointers(__LINE__); + compactCogCompiledCode(); + + /* begin nilUncoggableMethods */ + GIV(lastCoggableInterpretedBlockMethod) = (GIV(lastUncoggableInterpretedBlockMethod) = null); + if (GIV(instructionPointer)) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin externalWriteBackHeadStackPointer */ + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + (GIV(stackPage)->headSP = GIV(stackPointer)); + } + assertValidStackedInstructionPointers(__LINE__); + GIV(statCodeCompactionCount) += 1; + GIV(statCodeCompactionUsecs) += (ioUTCMicrosecondsNow()) - startTime; + if (checkForLeaks) { + clearLeakMapAndMapAccessibleObjects(); + asserta(checkCodeIntegrity(0)); + } +} + + /* CoInterpreter>>#compilationBreakpointFor: */ +void +compilationBreakpointFor(sqInt selectorOop) +{ + suppressHeartbeatFlag = 1; + warning("compilation send break (heartbeat suppressed)"); +} + + +/* Answer a value to store in the InstructionPointer index of a context + object for theIP and theFP. + Mapping native pcs to bytecode pcs is quite expensive, requiring a search + through the method + map. We mitigate this cost by deferring mapping until we really have to, + which is when a context's + instruction pointer is accessed by Smalltalk code (either direct inst var + access or through the + instVarAt: primitive). But to defer mapping we have to be able to + distinguish machine code from + bytecode pcs, which we do by using negative values for machine code pcs. + So if the frame is a + machine code one answer the negation of the offset in the cog method. + + As a whorish performance hack we also include the block method offset in + the pc of a block. + The least significant 16 bits are the native pc and the most significant + 14 bits are the block + start, in block alignment units. So when mapping back we can find the + start of the block. + + See mustMapMachineCodePC:context: for the code that does the actual + mapping. */ + + /* CoInterpreter>>#contextInstructionPointer:frame: */ +static NoDbgRegParms sqInt +contextInstructionPointerframe(sqInt theIP, char *theFP) +{ + sqInt blockOffset; + CogBlockMethod *cogMethod; + CogMethod *homeMethod; + + assert(validInstructionPointerinFrame(theIP, theFP)); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* begin mframeCogMethod: */ + cogMethod = ((CogBlockMethod *) ((longAt(theFP + FoxMethod)) & MFMethodMask)); + if (theIP == (ceCannotResumePC())) { + return HasBeenReturnedFromMCPCOop; + } + if (((cogMethod->cmType)) >= CMMethod) { + return (((usqInt)((((sqInt)cogMethod)) - theIP) << 3) | 1); + } + + /* cmHomeMethod */ + if (((cogMethod->cmType)) >= CMMethod) { + homeMethod = ((CogMethod *) cogMethod); + } + else { + assert(CMBlock == (cogMethod->cmType)); + homeMethod = ((CogMethod *) ((((usqInt)cogMethod)) - ((cogMethod->homeOffset)))); + } + blockOffset = ((((sqInt)homeMethod)) - (((sqInt)cogMethod))) / (blockAlignment()); + return (((usqInt)((((((usqInt)(blockOffset) << 16)))) | (((((sqInt)cogMethod)) - theIP) & 0xFFFF)) << 3) | 1); + } + return (((usqInt)(((((theIP == (ceReturnToInterpreterPC()) + ? ((usqInt)(longAt(theFP + FoxIFSavedIP))) + : theIP)) - (longAt(theFP + FoxMethod))) - BaseHeaderSize) + 2) << 3) | 1); +} + + +/* Convert the given machine code frame to an interpreter frame. Back up the + pc by + pcDelta. Support for backing up the pc in primitiveSuspend for processes + waiting on condition variables. Support for mustBeBoolean in the + RegisterAllocatingCogit and + for cloneContext: in shallowCopy when a code compaction is caused by + machine code + to bytecode pc mapping. */ + + /* CoInterpreter>>#convertFrame:toInterpreterFrame: */ +static NoDbgRegParms void +convertFrametoInterpreterFrame(char *theFP, sqInt pcDelta) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogBlockMethod *cogMethod; + CogMethod *homeMethod; + sqInt methodHeader; + sqInt methodObj; + sqInt oop; + char *p; + usqInt startBcpc; + usqInt theIP; + char *theNewSP; + StackPage *thePage; + char *theSP; + + thePage = ((StackPage *) 0); + assert(isMachineCodeFrame(theFP)); + if (theFP == GIV(framePointer)) { + theIP = GIV(instructionPointer); + theSP = GIV(stackPointer); + } + else { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(theFP == ((thePage->headFP))); + theSP = (thePage->headSP); + theIP = longAt(theSP); + } + + /* begin mframeCogMethod: */ + cogMethod = ((CogBlockMethod *) ((longAt(theFP + FoxMethod)) & MFMethodMask)); + if ((((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0) + && (!((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)))) { + /* cmHomeMethod */ + if (((cogMethod->cmType)) >= CMMethod) { + homeMethod = ((CogMethod *) cogMethod); + } + else { + assert(CMBlock == (cogMethod->cmType)); + homeMethod = ((CogMethod *) ((((usqInt)cogMethod)) - ((cogMethod->homeOffset)))); + } + methodHeader = (homeMethod->methodHeader); + methodObj = (homeMethod->methodObject); + startBcpc = (cogMethod->startpc); + } + else { + methodHeader = ((((CogMethod *) cogMethod))->methodHeader); + methodObj = ((((CogMethod *) cogMethod))->methodObject); + startBcpc = (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop; + } + + /* Map the machine code instructionPointer to the interpreter instructionPointer of the branch. */ + theIP = bytecodePCForstartBcpcin(theIP, startBcpc, cogMethod); + + /* fetchByte uses pre-increment, so pre decrement */ + theIP = (((methodObj + BaseHeaderSize) + theIP) - pcDelta) - 1; + assert(validInstructionPointerinMethodframePointer(theIP, methodObj, theFP)); + + /* Make space for the two extra fields in an interpreter frame */ + for (p = theSP; p <= (theFP + FoxMFReceiver); p += BytesPerWord) { + oop = longAt(p); + longAtput((p - BytesPerWord) - BytesPerWord,longAt(p)); + } + + /* Fill in the fields */ + longAtput(theFP + FoxIFrameFlags,/* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(((cogMethod->cmNumArgs))) << ((BytesPerWord * 8) - 8))))) + (((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag + ? 1ULL << ((BytesPerWord * 8) - 16) + : 0))) + (((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag + ? 1ULL << ((BytesPerWord * 8) - 24) + : 0)) + : ((1 + ((((usqInt)(((cogMethod->cmNumArgs))) << 8)))) + (((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag + ? 0x10000 + : 0))) + (((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag + ? 0x1000000 + : 0)))); + longAtput(theFP + FoxIFSavedIP,theIP); + longAtput(theFP + FoxMethod,methodObj); + if (theFP == GIV(framePointer)) { + GIV(stackPointer) = (GIV(stackPointer) - BytesPerWord) - BytesPerWord; + GIV(instructionPointer) = theIP; + + /* begin setMethod:methodHeader: */ + GIV(method) = methodObj; + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + } + else { + theNewSP = (theSP - BytesPerWord) - BytesPerWord; + (thePage->headSP = theNewSP); + longAtput(theNewSP,ceReturnToInterpreterPC()); + } +} + + /* CoInterpreter>>#cReturnAddressAddress */ +usqInt +cReturnAddressAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(CReturnAddress)))); +} + + /* CoInterpreter>>#cStackPointerAddress */ +usqInt +cStackPointerAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(CStackPointer)))); +} + + +/* Defer smashes of the stackLimit around the call of functionSymbol (for + assert checks) + */ + + /* CoInterpreter>>#deferStackLimitSmashAround:with: */ +static NoDbgRegParms sqInt +deferStackLimitSmashAroundwith(void (*functionSymbol)(sqInt), sqInt arg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(deferSmash) = 1; + sqLowLevelMFence(); + functionSymbol(arg); + GIV(deferSmash) = 0; + sqLowLevelMFence(); + if (GIV(deferredSmash)) { + GIV(deferredSmash) = 0; + sqLowLevelMFence(); + forceInterruptCheck(); + } + return 1; +} + + +/* Divorce all frames that satisfy criterion and answer the current + activeContext. + */ + + /* CoInterpreter>>#divorceAllFramesSuchThat: */ +static NoDbgRegParms sqInt +divorceAllFramesSuchThat(sqInt (*criterion)(char *fp)) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + StackPage *aPage; + char *calleeFP; + sqInt divorcedSome; + sqInt i; + sqInt object; + sqInt theContext; + char *theFP; + char *theSP; + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l1; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l1: + + /* begin ensurePushedInstructionPointer */ + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + do { + if (GIV(stackPage)) { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + + /* This is needed for the assert in externalDivorceFrame:andContext: + Slang can't currently cope with the lack of the variable here. + Something to do with the preceding statement. Take it out + and the code is good. leave it in and we get do { ... } while(l1:) */ + + /* begin divorceSomeFramesIf: */ + divorcedSome = 0; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + aPage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(aPage))) { + /* this to avoid assert in externalDivorceFrame:andContext: */ + markStackPageMostRecentlyUsed(GIV(stackPage)); + + /* begin divorceAFrameIf:in: */ + calleeFP = ((char *) 0); + theFP = (aPage->headFP); + theSP = (aPage->headSP); + + /* theSP points at hottest item on frame's stack */ + theSP += BytesPerWord; + while (1) { + if (criterion(theFP)) { + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + theContext = longAt(theFP + FoxThisContext); + goto l4; + } + theContext = marryFrameSP(theFP, theSP); + /* end ensureFrameIsMarried:SP: */ +l4: + externalDivorceFrameandContext(theFP, theContext); + if (theFP == GIV(framePointer)) { + GIV(framePointer) = 0; + } + goto l2; + } + calleeFP = theFP; + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!(theFP != 0)) break; + /* theSP points at stacked hottest item on frame's stack */ + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(calleeFP))); + theSP = (calleeFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(calleeFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(calleeFP))->cmNumArgs) + : byteAt((calleeFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + goto l3; +l2: + divorcedSome = 1; +l3:; + } + } + } while(divorcedSome); + return activeContext; +} + + /* CoInterpreter>>#dumpStackAlignmentInfo */ +void +dumpStackAlignmentInfo(void) +{ + printf("STACK_ALIGN_BYTES:%d STACK_ALIGN_MASK:%d STACK_FP_ALIGNMENT:%d STACK_SP_ALIGNMENT:%d\n", + ((int) STACK_ALIGN_BYTES), + ((int) STACK_ALIGN_MASK), + ((int) STACK_FP_ALIGNMENT), + ((int) STACK_SP_ALIGNMENT)); +} + + +/* Map all native pcs to bytecoded pcs in all contexts whose method matches + criterion. + */ + + /* CoInterpreter>>#ensureAllContextsHaveBytecodePCsIf: */ +static NoDbgRegParms void +ensureAllContextsHaveBytecodePCsIf(sqInt (*criterion)(sqInt methodOop)) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt pc; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + pc = 0; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (((((((pc = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))))))) & 7) == 1)) + && ((pc < 0) + && (criterion((followObjFieldofObject(MethodIndex, obj))))))) { + widowOrForceToBytecodePC(obj); + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } +} + + +/* Make sure the context has a byetcode pc. Can only be used on single + contexts. + */ + + /* CoInterpreter>>#ensureContextHasBytecodePC: */ +static NoDbgRegParms void +ensureContextHasBytecodePC(sqInt aContext) +{ + sqInt pc; + + assert(!(isMarriedOrWidowedContext(aContext))); + pc = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + if (((((pc) & 7) == 1)) + && (((pc = (pc >> 3))) < 0)) { + pc = mustMapMachineCodePCcontext(pc, aContext); + assert(validBCPCinMethod((pc >> 3), fetchPointerofObject(MethodIndex, aContext))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aContext)) + && (!(isForwarded(aContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, aContext, pc)); + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),pc); + } +} + + +/* Safety to give the JIT lattitude in calling convention. Conceptually, + returning a value to a context involves pushing that value onto the stack. + This is used + in Squeak methods such as ContextPart>>jump + jump + | top | + thisContext sender push: nil. + stackp = 0 ifTrue: [self stepToSendOrReturn]. + stackp = 0 ifTrue: [self push: nil]. + top := self pop. + thisContext privSender: self. + ^top + Here jump may pop the value of a temporary variable off the stack which + will, conceptually and, in the interpreter, actually, get pushed back on + return. But + if the JIT is mapping the stack to registers disaster may ensue since the + value may not get pushed to the stack and code may access an invalid value + (e.g. a pc). + + The solution is to fall back on the interpreter. If the stack pointer is + changed we + also ensure the pc is a bytecode pc (+ive) which will cause + makeBaseFrameFor: to create an interpreter frame if the context is + executed again. */ + + /* CoInterpreter>>#ensureContextIsExecutionSafeAfterAssignToStackPointer: */ +static NoDbgRegParms void +ensureContextIsExecutionSafeAfterAssignToStackPointer(sqInt aContext) +{ + ensureContextHasBytecodePC(aContext); +} + + +/* Main entry-point into the interpreter at each execution level, where an + execution level is either the start of execution or reentry for a + callback. Capture the C stack + pointers so that calls from machine-code into the C run-time occur at this + level. This is the actual implementation, separated from + enterSmalltalkExecutive so the + simulator can wrap it in an exception handler and hence simulate the + Cogit's jump + back into C code on interpreting; see ceInvokeInterpret. + + Conceptually, an invocation of interpret exists at each level of execution + from the + initial invocation through each callback. Entry to each execution level is + through this function. It captures the C stack & frame pointers for this + level of execution + and then either invokes machine code or interpret, depending on whether + the current frame (the effective entry-point into Smalltalk execution) is + a machine code + or interpreted frame. In addition, interpret captures the return address + of its caller + (this function). The Cogit then uses the captured C stack pointers and + return address to invoke interpret as if it had been called from this + function. */ + + /* CoInterpreter>>#enterSmalltalkExecutiveImplementation */ +static sqInt +enterSmalltalkExecutiveImplementation(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assertCStackWellAligned(); + ceCaptureCStackPointers(); + assertSavedCStackPointersWellAligned(); + if ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())) { + returnToExecutivepostContextSwitch(0, 1); + } + interpret(); + return 0; +} + + +/* Execute a CogMethod from a linked send. The receiver, + arguments and return address are on the Smalltalk stack. First + push the entry-point and finally the register argument(s). Then write + back the frame pointers and call the routine that will pop off the + register argument(s) and jump to the entry by executing a return + instruction. + In the simple jit only the receiver gets passed in registers, so only the + receiver gets pushed. */ + + /* CoInterpreter>>#executeCogMethod:fromLinkedSendWithReceiver: */ +void +executeCogMethodfromLinkedSendWithReceiver(CogMethod *cogMethod, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + char *sp; + + assertCStackWellAligned(); + assert(isMachineCodeFrame(GIV(framePointer))); + assertValidExecutionPointersimbarline(longAt(GIV(stackPointer)), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + if ((numRegArgs()) > 0) { + if (((cogMethod->cmNumArgs)) <= (numRegArgs())) { + callRegisterArgCogMethodatreceiver(cogMethod, entryOffset(), rcvr); + } + } + + /* dont use and: so as to get Slang to inline cogit numRegArgs > 0 */ + object = (((sqInt)cogMethod)) + (entryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((sp = GIV(stackPointer) - BytesPerWord),rcvr); + GIV(stackPointer) = sp; + ceCallCogCodePopReceiverReg(); +} + + +/* Execute a CogMethod from an unlinked send. The receiver, + arguments and return address are on the Smalltalk stack. First + push the entry-point and finally the register argument(s). Then write + back the frame pointers and call the routine that will pop off the + register argument(s) and jump to the entry by executing a return + instruction. + In the simple jit only the receiver gets passed in registers, so only the + receiver gets pushed. */ + + /* CoInterpreter>>#executeCogMethod:fromUnlinkedSendWithReceiver: */ +static NoDbgRegParms void +executeCogMethodfromUnlinkedSendWithReceiver(CogMethod *cogMethod, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + char *sp; + + assertCStackWellAligned(); + assert(isMachineCodeFrame(GIV(framePointer))); + assertValidExecutionPointersimbarline(longAt(GIV(stackPointer)), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + if ((numRegArgs()) > 0) { + if (((cogMethod->cmNumArgs)) <= (numRegArgs())) { + callRegisterArgCogMethodatreceiver(cogMethod, noCheckEntryOffset(), rcvr); + } + } + + /* dont use and: so as to get Slang to inline cogit numRegArgs > 0 */ + object = (((sqInt)cogMethod)) + (noCheckEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((sp = GIV(stackPointer) - BytesPerWord),rcvr); + GIV(stackPointer) = sp; + ceCallCogCodePopReceiverReg(); +} + + +/* Execute a closed PIC from a linked send, to redispatch based on the rcvr. + The receiver, arguments and return address are on the Smalltalk stack. + First push the entry-point and finally the register argument(s). Then + write back the frame pointers and call the routine that will pop off the + register argument(s) and jump to the entry by executing a return + instruction. + In the simple jit only the receiver gets passed in registers, so only the + receiver gets pushed. */ + + /* CoInterpreter>>#executeCogPIC:fromLinkedSendWithReceiver:andCacheTag: */ +void +executeCogPICfromLinkedSendWithReceiverandCacheTag(CogMethod *cogPIC, sqInt rcvr, sqInt cacheTag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + char *sp; + + assertCStackWellAligned(); + assert(isMachineCodeFrame(GIV(framePointer))); + assertValidExecutionPointersimbarline(longAt(GIV(stackPointer)), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + object = (((sqInt)cogPIC)) + (entryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + if ((numRegArgs()) > 0) { + if (((cogPIC->cmNumArgs)) <= (numRegArgs())) { + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),cacheTag); + GIV(stackPointer) = sp; + switch ((cogPIC->cmNumArgs)) { + case 0: + ceCall0ArgsPIC(); + break; + case 1: + ceCall1ArgsPIC(); + break; + case 2: + ceCall2ArgsPIC(); + break; + default: +; + } + error("not reached"); + } + } + + /* dont use and: so as to get Slang to inline cogit numRegArgs > 0 */ + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),rcvr); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),cacheTag); + GIV(stackPointer) = sp; + ceCallCogCodePopReceiverAndClassRegs(); +} + + +/* Execute newMethod - either primitiveFunctionPointer must be set directly + (i.e. from primitiveExecuteMethod et al), or it would have been set + probing the method cache (i.e. primitivePerform et al). */ + + /* CoInterpreter>>#executeNewMethod */ +static sqInt +executeNewMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt arg; + sqInt argIndex; + sqInt fmt; + sqInt header; + usqInt i; + sqInt index; + int inInterpreter; + int inInterpreterInt; + sqInt maxNumArgs; + sqInt methodHeader; + sqInt methodHeaderSqInt; + usqInt numArgs; + usqInt numSlots; + usqInt numSlotsUsqInt; + usqInt numTemps; + sqInt object; + sqInt objectClass; + sqInt rcvr; + sqInt selectorOrProperties; + sqInt switched; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt types; + sqInt typesArraySize; + sqInt wordSize; + + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + if (primitiveFunctionPointer) { + if ((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex) { + externalQuickPrimitiveResponse(); + returntoExecutive(popStack(), inInterpreter); + return null; + } + + /* slowPrimitiveResponse may of course context-switch. If so we must reenter the + new process appopriately, returning only if we've reached here directly from the + interpreter and have found an interpreter frame. The instructionPointer tells us + from whence we came. */ + if (slowPrimitiveResponse()) { + returntoExecutive(popStack(), inInterpreter); + return null; + } + } + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* if not primitive, or primitive failed, activate the method */ + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,((sqInt)GIV(instructionPointer))); + GIV(instructionPointer) = ceReturnToInterpreterPC(); + } + activateNewCogMethodinInterpreter(((CogMethod *) methodHeader), inInterpreter); + } + else { + /* begin activateNewMethod */ + /* begin collectArgumentsTypeFor:fromStackAt:count: */ + /* begin tempVarTypesArraysOf: */ + /* begin additionalMethodStateOf:at: */ + /* begin additionalMethodStateOf: */ + selectorOrProperties = penultimateLiteralOf(GIV(newMethod)); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l3; + } + wordSize = BytesPerWord; + maxNumArgs = (((lengthOf(tempVarTypesArrays)) < GIV(argumentCount)) ? (lengthOf(tempVarTypesArrays)) : GIV(argumentCount)); + for (argIndex = 0; argIndex < maxNumArgs; argIndex += 1) { + /* begin followObjField:ofObject: */ + types = longAt((void *)((tempVarTypesArrays + BaseHeaderSize) + ((((usqInt)(argIndex) << (shiftForWord())))))); + assert(isNonImmediate(types)); + if ((!((longAt((void *)(types))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + types = fixFollowedFieldofObjectwithInitialValue(argIndex, tempVarTypesArrays, types); + } + arg = longAt((void *)(GIV(stackPointer) + (argIndex * wordSize))); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (types) + && (isInstanceOfClassArray(types)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = arg & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(arg)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(types + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(types + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(types - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, types, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, types, objectClass)); + assert(isNonImmediate(types)); + if (oopisGreaterThanOrEqualTo(types, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(types + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(types); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2:; + } + /* end collectArgumentsTypeFor:fromStackAt:count: */ +l3: + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeaderSqInt = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeaderSqInt)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeaderSqInt)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(!(isOopForwarded(rcvr))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreterInt = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeaderSqInt); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeaderSqInt)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeaderSqInt) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeaderSqInt >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + if (((methodHeaderSqInt & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeaderSqInt); + } + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc). */ + switched = 1; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(canContextSwitchIfActivatingheader(GIV(newMethod), methodHeaderSqInt)); + } + returnToExecutivepostContextSwitch(inInterpreterInt, switched); + } + return 0; +} + + +/* Execute newMethod - either primitiveFunctionPointer must be set directly + (i.e. from primitiveExecuteMethod et al), or it would have been set + probing the method cache (i.e. primitivePerform et al). + Eagerly compile it if appropriate so that doits are fast. */ + + /* CoInterpreter>>#executeNewMethodJitting */ +static sqInt +executeNewMethodJitting(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt arg; + sqInt argIndex; + sqInt fmt; + sqInt header; + usqInt i; + sqInt index; + int inInterpreter; + int inInterpreterInt; + sqInt maxNumArgs; + sqInt methodHeader; + sqInt methodHeaderSqInt; + usqInt numArgs; + usqInt numSlots; + usqInt numSlotsUsqInt; + usqInt numTemps; + sqInt object; + sqInt objectClass; + sqInt rcvr; + sqInt selectorOrProperties; + sqInt switched; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt types; + sqInt typesArraySize; + sqInt wordSize; + + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + if (primitiveFunctionPointer) { + if ((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex) { + externalQuickPrimitiveResponse(); + returntoExecutive(popStack(), inInterpreter); + return null; + } + + /* slowPrimitiveResponse may of course context-switch. If so we must reenter the + new process appopriately, returning only if we've reached here directly from the + interpreter and have found an interpreter frame. The instructionPointer tells us + from whence we came. */ + if (slowPrimitiveResponse()) { + returntoExecutive(popStack(), inInterpreter); + return null; + } + } + + /* Eagerly compile it if appropriate so that doits are fast. */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if (!((!(methodHeader & (smallIntegerTag()))))) { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(nilObj)); + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + + /* if not primitive, or primitive failed, activate the method */ + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,((sqInt)GIV(instructionPointer))); + GIV(instructionPointer) = ceReturnToInterpreterPC(); + } + activateNewCogMethodinInterpreter(((CogMethod *) methodHeader), inInterpreter); + } + else { + /* begin activateNewMethod */ + /* begin collectArgumentsTypeFor:fromStackAt:count: */ + /* begin tempVarTypesArraysOf: */ + /* begin additionalMethodStateOf:at: */ + /* begin additionalMethodStateOf: */ + selectorOrProperties = penultimateLiteralOf(GIV(newMethod)); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l3; + } + wordSize = BytesPerWord; + maxNumArgs = (((lengthOf(tempVarTypesArrays)) < GIV(argumentCount)) ? (lengthOf(tempVarTypesArrays)) : GIV(argumentCount)); + for (argIndex = 0; argIndex < maxNumArgs; argIndex += 1) { + /* begin followObjField:ofObject: */ + types = longAt((void *)((tempVarTypesArrays + BaseHeaderSize) + ((((usqInt)(argIndex) << (shiftForWord())))))); + assert(isNonImmediate(types)); + if ((!((longAt((void *)(types))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + types = fixFollowedFieldofObjectwithInitialValue(argIndex, tempVarTypesArrays, types); + } + arg = longAt((void *)(GIV(stackPointer) + (argIndex * wordSize))); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (types) + && (isInstanceOfClassArray(types)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = arg & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(arg)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(types + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(types + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(types - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, types, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, types, objectClass)); + assert(isNonImmediate(types)); + if (oopisGreaterThanOrEqualTo(types, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(types + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(types); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2:; + } + /* end collectArgumentsTypeFor:fromStackAt:count: */ +l3: + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeaderSqInt = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeaderSqInt)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeaderSqInt)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(!(isOopForwarded(rcvr))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreterInt = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeaderSqInt); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeaderSqInt)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeaderSqInt) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeaderSqInt >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + if (((methodHeaderSqInt & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeaderSqInt); + } + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc). */ + switched = 1; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(canContextSwitchIfActivatingheader(GIV(newMethod), methodHeaderSqInt)); + } + returnToExecutivepostContextSwitch(inInterpreterInt, switched); + } + return 0; +} + + +/* Fetch an instance variable from a maybe married context. + If the context is still married compute the value of the + relevant inst var from the spouse frame's state. + + If the context is single but has a negative instruction pointer + recognise that the instruction pointer is actually into machine + code and convert it to the corresponding bytecode pc. */ + + /* CoInterpreter>>#externalInstVar:ofContext: */ +static NoDbgRegParms sqInt +externalInstVarofContext(sqInt offset, sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt value; + + assert(isContext(aContext)); + assert(offset <= (ReceiverIndex + (checkStackPointerForMaybeMarriedContext(aContext)))); + + /* method, closureOrNil & receiver need no special handling; only + sender, pc & stackp have to be computed for married contexts. */ + if (!(offset <= StackPointerIndex)) { + return longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(offset) << (shiftForWord())))))); + } + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))) { + return fetchPointerofMarriedContext(offset, aContext); + } + value = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(offset) << (shiftForWord())))))); + + /* Why not update the slot to refer to the mapped pc? We're damned if we do, and damned if + we don't. If it is and the context is live and is returned to, then the pc will be mapped back + to machine code. If we don't then the mapping will be repeated on every access. What we + need are stats that show whether or not a read here predicts one or other outcome with any + certainty. For now, we keep it as it has been for a long time and do not update the slot. */ + if ((offset == InstructionPointerIndex) + && (((((value) & 7) == 1)) + && ((((int) value)) < 0))) { + return mustMapMachineCodePCcontext((value >> 3), aContext); + } + return value; +} + + +/* Find the compiled method to be run when the current messageSelector is + looked up in the + class/classIndex classTagArg, setting the values of newMethod and + primitiveFunctionPointer. + */ + + /* CoInterpreter>>#findNewMethodInClassTag: */ +static NoDbgRegParms void +findNewMethodInClassTag(sqInt classTagArg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt classTag; + CogMethod *cogMethod; + sqInt fieldIndex; + sqInt methodHeader; + + if (lookupInMethodCacheSelclassTag(GIV(messageSelector), classTagArg)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + /* entry was not found in the cache; perhaps soemthing was forwarded. */ + classTag = classTagArg; + if ((/* isOopForwarded: */ + ((!(GIV(messageSelector) & (tagMask())))) + && ((!((longAt((void *)(GIV(messageSelector)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + || (classTag == (isForwardedObjectClassIndexPun()))) { + if (/* isOopForwarded: */ + ((!(GIV(messageSelector) & (tagMask())))) + && ((!((longAt((void *)(GIV(messageSelector)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + GIV(messageSelector) = handleForwardedSelectorFaultFor(GIV(messageSelector)); + } + if (classTag == (isForwardedObjectClassIndexPun())) { + classTag = handleForwardedSendFaultForTag(classTag); + } + if (lookupInMethodCacheSelclassTag(GIV(messageSelector), classTag)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + return; + } + } + + /* entry was not found in the cache; look it up the hard way */ + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((classTag >= 0) + && ((classTag <= (tagMask())) + || ((classTag >= (arrayClassIndexPun())) + && (classTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + GIV(lkupClass) = null; + goto l1; + } + fieldIndex = classTag & ((1U << (classTableMajorIndexShift())) - 1); + GIV(lkupClass) = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l1: + lookupMethodInClass(GIV(lkupClass)); + addNewMethodToCache(GIV(lkupClass)); + } +} + + +/* The flag bit can be used to flag methods that are interpreted, if it has + been requested + from the image header flags. */ + + /* CoInterpreter>>#flagMethodAsInterpreted: */ +static NoDbgRegParms void +flagMethodAsInterpreted(sqInt aMethod) +{ + sqInt rawHeader; + sqInt realHeader; + + assert(flagInterpretedMethods()); + rawHeader = longAt((void *)((aMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + realHeader = ((!(rawHeader & (smallIntegerTag()))) + ? ((((CogMethod *) rawHeader))->methodHeader) + : rawHeader); + realHeader = realHeader | ((((usqInt)(1U << MethodHeaderFlagBitPosition) << 3) | 1)); + + /* begin isCogMethodReference: */ + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + ((((CogMethod *) rawHeader))->methodHeader = realHeader); + } + else { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aMethod)) + && (!(isForwarded(aMethod)))); + assert(validStorePointerUncheckedArgs(0, aMethod, realHeader)); + longAtput((void *)((aMethod + BaseHeaderSize) + (0U << (shiftForWord()))),realHeader); + } +} + + +/* Flush the references to external functions from plugin primitives. + Then continue execution answering self. + This will force a reload of those primitives when accessed next. + Note: We must flush the method cache here also, so that any failed + primitives are looked up again. + Override to ensure that any and all activations of an external method + have a bytecode pc so that if code generation changes (e.g. a primitive + method is used, unloaded, and the reloaded primitive is marked with + the FastCPrimitiveFlag) stale machine code pcs have been eliminated. + THIS MUST BE INVOKED IN THE CONTEXT OF A PRIMITIVE. */ + + /* CoInterpreter>>#flushExternalPrimitives */ +static void +flushExternalPrimitives(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt senderOop; + sqInt startObject; + char *theFP; + char *theFrame; + StackPage *thePage; + sqInt top; + + activeContext = divorceAllFramesSuchThat(isMachineCodeFrameForExternalPrimitiveMethod); + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (((byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + flushExternalPrimitiveOf(obj); + } + else { + if (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin mapToBytecodePCIfActivationOfExternalMethod: */ + if (isExternalMethodInPlugin(longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))))) { + if (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (isWidowedContext(obj)) { + goto l1; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + externalDivorceFrameandContext(theFP, obj); + } + } + ensureContextHasBytecodePC(obj); + } + /* end mapToBytecodePCIfActivationOfExternalMethod: */ +l1:; + } + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2: + assert(oopisGreaterThan(obj, prevObj)); + } + unlinkSendsToMethodsSuchThatAndFreeIf(cogMethodHasExternalPrim, 1); + + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + + /* begin flushExternalPrimitiveTable */ + memset(externalPrimitiveTable, 0, MaxExternalPrimitiveTableSize * (sizeof(externalPrimitiveTable[0]))); + GIV(externalPrimitiveTableFirstFreeIndex) = 0; + + /* If flushing led to divorce continue in the interpreter. */ + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + ceInvokeInterpret(); + } + + /* If not, work out where we are and continue */ + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + assert(((thePage->headFP)) == theFrame); + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* Arrange that any and all cog methods with machine code primitives can be + and are discarded. + Hence scan contexts and map their PCs to bytecode PCs if required, and + scan frames, divorcing + the frames of activations if required. Then continue execution answering + result. THIS MUST BE + INVOKED IN THE CONTEXT OF A PRIMITIVE. It exists to support + vmParameterAt:put:. */ + + /* CoInterpreter>>#flushMethodsWithMachineCodePrimitivesAndContinueAnswering: */ +static NoDbgRegParms void +flushMethodsWithMachineCodePrimitivesAndContinueAnswering(sqInt result) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt senderOop; + char *sp; + char *theFrame; + StackPage *thePage; + sqInt top; + + activeContext = divorceAllFramesSuchThat(isMachineCodeFrameForCogMethodWithMachineCodePrim); + ensureAllContextsHaveBytecodePCsIf(methodHasMachineCodePrimitiveMethod); + unlinkSendsToMethodsSuchThatAndFreeIf(cogMethodHasMachineCodePrim, 1); + + /* If the top frame was divorced then continue in the interpreter. */ + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* pop bogus machine-code instructionPointer, arguments and receiver */ + + /* begin pop:thenPush: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 2) - 1) * BytesPerWord)),result); + GIV(stackPointer) = sp; + ceInvokeInterpret(); + } + + /* If not, work out where we are and continue */ + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + assert(((thePage->headFP)) == theFrame); + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + + /* begin pop:thenPush: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),result); + GIV(stackPointer) = sp; +} + + /* CoInterpreter>>#followForwardedFieldsInCurrentMethod */ +static void +followForwardedFieldsInCurrentMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cogMethod; + sqInt methodField; + + if ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())) { + /* begin mframeHomeMethod: */ + methodField = longAt(GIV(framePointer) + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + cogMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + goto l1; + } + cogMethod = ((CogMethod *) (methodField & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + followForwardedObjectFieldstoDepth((cogMethod->methodObject), 0); + followForwardedLiteralsIn(cogMethod); + } + else { + followForwardedObjectFieldstoDepth(GIV(method), 0); + } +} + + +/* A more thorough version of followForwardingPointersInStackZone that also + follows all temporaries (but not stack contents after the temps). + This allows removal of the TempVectReadBarrier in the IGC */ + + /* CoInterpreter>>#followForwardingPointersOfReceiverAndTemporariesInStackZone */ +static void +followForwardingPointersOfReceiverAndTemporariesInStackZone(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + char *frameRcvrOffset; + sqInt header; + sqInt i; + sqInt methodHeader; + sqInt newOop; + sqInt offset; + usqInt oop; + sqInt referent; + char *theFP; + usqInt theIPPtr; + StackPage *thePage; + char *theSP; + + callerFP = ((char *) 0); + if (!GIV(stackPage)) { + assert((!(GIV(mostRecentlyUsedPage))) + || (isFree(GIV(mostRecentlyUsedPage)))); + return; + } + + /* the system must be snapshotting; nothing to do... */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((thePage->baseFP)) { + assert(ifCurrentStackPageHasValidHeadPointers(thePage)); + theFP = (thePage->headFP); + + /* Skip the instruction pointer on top of stack of inactive pages. */ + theIPPtr = (!(thePage == GIV(stackPage)) + ? ((usqInt)((thePage->headSP))) + : 0); + while (1) { + assert(addressIsInPage(thePage, theFP)); + assert((!theIPPtr) + || (addressIsInPage(thePage, ((void *)theIPPtr)))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + oop = longAt(theFP + FoxMFReceiver); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxMFReceiver,followForwarded(oop)); + } + assert(!(isForwarded(((mframeHomeMethod(theFP))->methodObject)))); + frameRcvrOffset = theFP + FoxMFReceiver; + methodHeader = ((mframeHomeMethod(theFP))->methodHeader); + } + else { + oop = longAt(theFP + FoxIFReceiver); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxIFReceiver,followForwarded(oop)); + } + oop = longAt(theFP + FoxMethod); + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + newOop = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(newOop & (tagMask())))) + && ((!((longAt((void *)(newOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + newOop = longAt((void *)((newOop + BaseHeaderSize) + (0U << (shiftForWord())))); + } + offset = newOop - oop; + if ((theIPPtr) + && ((longAt((void *)(theIPPtr))) > oop)) { + longAtput((void *)(theIPPtr),(longAt((void *)(theIPPtr))) + offset); + } + longAtput(theFP + FoxIFSavedIP,(longAt(theFP + FoxIFSavedIP)) + offset); + longAtput(theFP + FoxMethod,(oop = newOop)); + } + frameRcvrOffset = theFP + FoxIFReceiver; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(oop)); + header = longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + } + theSP = frameRcvrOffset - (((((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F) * BytesPerWord); + while (theSP <= frameRcvrOffset) { + oop = longAt(theSP); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + longAtput(theSP,oop); + } + theSP += BytesPerWord; + } + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) + && ((!((longAt((void *)(longAt(theFP + FoxThisContext)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxThisContext,followForwarded(longAt(theFP + FoxThisContext))); + } + offset = (FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))); + oop = longAt(theFP + offset); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + offset,followForwarded(oop)); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theIPPtr = ((usqInt)(theFP + FoxCallerSavedIP)); + theFP = callerFP; + } + + /* And finally follow the saved context and the caller context. */ + theSP = ((thePage->baseAddress)) - BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + longAtput(theSP,followForwarded(oop)); + } + theSP += BytesPerWord; + } + } + } +} + + +/* Spur's become: is lazy, turning the becommed object into a forwarding + object to the other. + The read-barrier is minimised by arranging that forwarding pointers will + fail a method cache + probe, since notionally objects' internals are accessed only via sending + messages to them, + the exception is primitives that access the internals of the non-receiver + argument(s). + To avoid a read barrier on bytecode, literal and inst var fetch and + non-local return, we scan + the receivers (including the stacked receiver for non-local return) and + method references + in the stack zone and follow any forwarded ones. This is of course way + cheaper than + scanning all of memory as in the old become. + + Override to handle machine code frames */ + + /* CoInterpreter>>#followForwardingPointersOfReceiversInStackZone */ +static void +followForwardingPointersOfReceiversInStackZone(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt i; + sqInt newOop; + sqInt offset; + usqInt oop; + char *theFP; + usqInt theIPPtr; + StackPage *thePage; + char *theSP; + + callerFP = ((char *) 0); + if (!GIV(stackPage)) { + assert((!(GIV(mostRecentlyUsedPage))) + || (isFree(GIV(mostRecentlyUsedPage)))); + return; + } + + /* the system must be snapshotting; nothing to do... */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((thePage->baseFP)) { + assert(ifCurrentStackPageHasValidHeadPointers(thePage)); + theFP = (thePage->headFP); + + /* Skip the instruction pointer on top of stack of inactive pages. */ + theIPPtr = (!(thePage == GIV(stackPage)) + ? ((usqInt)((thePage->headSP))) + : 0); + while (1) { + assert(addressIsInPage(thePage, theFP)); + assert((!theIPPtr) + || (addressIsInPage(thePage, ((void *)theIPPtr)))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + oop = longAt(theFP + FoxMFReceiver); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxMFReceiver,followForwarded(oop)); + } + assert(!(isForwarded(((mframeHomeMethod(theFP))->methodObject)))); + } + else { + oop = longAt(theFP + FoxIFReceiver); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxIFReceiver,followForwarded(oop)); + } + oop = longAt(theFP + FoxMethod); + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + newOop = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(newOop & (tagMask())))) + && ((!((longAt((void *)(newOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + newOop = longAt((void *)((newOop + BaseHeaderSize) + (0U << (shiftForWord())))); + } + offset = newOop - oop; + if ((theIPPtr) + && ((longAt((void *)(theIPPtr))) > oop)) { + longAtput((void *)(theIPPtr),(longAt((void *)(theIPPtr))) + offset); + } + longAtput(theFP + FoxIFSavedIP,(longAt(theFP + FoxIFSavedIP)) + offset); + longAtput(theFP + FoxMethod,(oop = newOop)); + } + } + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) + && ((!((longAt((void *)(longAt(theFP + FoxThisContext)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxThisContext,followForwarded(longAt(theFP + FoxThisContext))); + } + offset = (FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))); + oop = longAt(theFP + offset); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + offset,followForwarded(oop)); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theIPPtr = ((usqInt)(theFP + FoxCallerSavedIP)); + theFP = callerFP; + } + + /* And finally follow the saved context and the caller context. */ + theSP = ((thePage->baseAddress)) - BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + longAtput(theSP,followForwarded(oop)); + } + theSP += BytesPerWord; + } + } + } +} + + +/* Force an interrupt check ASAP. This version is the + entry-point to forceInterruptCheck for the heartbeat + timer to allow for repeatable debugging. */ + + /* CoInterpreter>>#forceInterruptCheckFromHeartbeat */ +void +forceInterruptCheckFromHeartbeat(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!suppressHeartbeatFlag) { + /* begin checkForLongRunningPrimitive */ +# if LRPCheck + if (!GIV(longRunningPrimitiveCheckSemaphore)) { + goto l1; + } + if ((GIV(longRunningPrimitiveStartUsecs) > 0) + && ((GIV(longRunningPrimitiveCheckMethod) == GIV(newMethod)) + && (GIV(longRunningPrimitiveCheckSequenceNumber) == GIV(statCheckForEvents)))) { + GIV(longRunningPrimitiveStopUsecs) = ioUTCMicroseconds(); + assert(GIV(longRunningPrimitiveStopUsecs) > GIV(longRunningPrimitiveStartUsecs)); + goto l1; + } + + /* See traceProfileState & mapProfileState. */ + if (!GIV(longRunningPrimitiveStopUsecs)) { + GIV(longRunningPrimitiveCheckSequenceNumber) = GIV(statCheckForEvents); + GIV(longRunningPrimitiveCheckMethod) = GIV(newMethod); + GIV(longRunningPrimitiveStartUsecs) = ioUTCMicroseconds(); + sqLowLevelMFence(); + } +# endif // LRPCheck + + /* end checkForLongRunningPrimitive */ +l1: + sqLowLevelMFence(); + if (GIV(deferSmash)) { + GIV(deferredSmash) = 1; + sqLowLevelMFence(); + } + else { + forceInterruptCheck(); + } + } +} + + +/* In the StackInterpreter the saved ip field of a base frame holds the + base frame's caller context. But in the Cog VM the first word on the + stack holds the base frame's caller context, which is immediately + above the stacked receiver. */ + + /* CoInterpreter>>#frameCallerContext: */ +static NoDbgRegParms sqInt +frameCallerContext(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + StackPage *thePage; + + assert(isBaseFrame(theFP)); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + return callerContextOrNil; +} + + /* CoInterpreter>>#frameHasContext: */ +static NoDbgRegParms int +frameHasContext(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0); +} + + +/* */ + + /* CoInterpreter>>#frameIsBlockActivation: */ +static NoDbgRegParms int +frameIsBlockActivation(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0); +} + + /* CoInterpreter>>#frameMethodObject: */ +static NoDbgRegParms sqInt +frameMethodObject(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); +} + + +/* See encodeFrameFieldHasContext:numArgs: */ + + /* CoInterpreter>>#frameNumArgs: */ +static NoDbgRegParms usqInt +frameNumArgs(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)); +} + + /* CoInterpreter>>#framePointerAddress */ +usqInt +framePointerAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(framePointer)))); +} + + /* CoInterpreter>>#framePrintDescription: */ +static NoDbgRegParms void +framePrintDescription(sqInt it) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!it) { + return; + } + if (it == GIV(nilObj)) { + fprintf(GIV(transcript), "=nil"); + return; + } + if (it == (ceReturnToInterpreterPC())) { + fprintf(GIV(transcript), "=ceReturnToInterpreterPC"); + return; + } + if (it == (ceBaseFrameReturnPC())) { + fprintf(GIV(transcript), "=ceBaseFrameReturnPC"); + return; + } + if (it == (ceCannotResumePC())) { + fprintf(GIV(transcript), "=ceCannotResumePC"); + return; + } + fprintf(GIV(transcript), + "=%" PRIdSQINT "", + it); +} + + /* CoInterpreter>>#frameReceiver: */ +static NoDbgRegParms sqInt +frameReceiver(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? longAt(theFP + FoxMFReceiver) + : longAt(theFP + FoxIFReceiver)); +} + + /* CoInterpreter>>#functionPointerForCompiledMethod:primitiveIndex:primitivePropertyFlagsInto: */ +void +(*functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(sqInt methodObj, sqInt primitiveIndex, sqInt *flagsPtr))(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt flags; + void (*functionPointer)(void); + sqInt header; + sqInt headerSqInt; + sqInt index; + sqInt lit; + sqInt litSqInt; + sqInt metadata; + usqInt metadataFlags; + sqInt primIdx; + sqInt targetFunctionIndex; + + flags = 0; + if (flagsPtr) { + flagsPtr[0] = ((flags = primitivePropertyFlagsprimIndex(methodObj, primitiveIndex))); + } + if (primitiveIndex == PrimNumberFFICall) { + /* begin functionForPrimitiveCallout */ + if ((((sqInt)primitiveCalloutPointer)) == -1) { + reloadPrimitiveCalloutPointer(); + } + if (flagsPtr) { + if (GIV(primCalloutIsExternal)) { + flagsPtr[0] = (flags | PrimCallIsExternalCall); + } + } + return primitiveCalloutPointer; + } + if (primitiveIndex == PrimNumberExternalCall) { + /* begin attemptToLinkExternalPrimitive: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + headerSqInt = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* primitiveIndexOfMethod:header: */ + if (((header & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((header >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIdx = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIdx = 0; + } + lit = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + assert((primIdx == PrimNumberExternalCall) + && (((literalCountOfMethodHeader(header)) > 0) + && ((isArray(lit)) + && ((numSlotsOf(lit)) == 4)))); + targetFunctionIndex = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord())))))); + assert((((targetFunctionIndex) & 7) == 1)); + if (!((targetFunctionIndex >> 3))) { + linkExternalCallerrInto(lit, null); + } + if (flagsPtr) { + /* N.B. We only support the FastCPrimitiveFlag on Spur because Spur will not run a GC to + satisfy an allocation in a primitive. The V3 ObjectMemory will and hence the depth of + stack needed in a V3 primitive is probably too large to safely execute on a stack page. */ + metadata = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFlagsIndex) << (shiftForWord())))))); + if ((((metadata) & 7) == 1)) { + metadataFlags = ((metadata >> 3)) & SpurPrimitiveFlagsMask; + + /* combine the specific external primitive's flags with the base flags. + Hence e.g. if profiling is in effect (as indicated by PrimCallCollectsProfileSamples + in the base flags) it remains in effect after combining the specific flags. */ + flags = flags | metadataFlags; + } + if (numBytesOf(longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralModuleNameIndex) << (shiftForWord())))))))) { + flags = flags | PrimCallIsExternalCall; + } + flagsPtr[0] = flags; + } + + /* begin functionForPrimitiveExternalCall: */ + if (!((literalCountOf(methodObj)) > 0)) { + return primitiveExternalCall; + } + + /* Check if it's an array of length 4 */ + litSqInt = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(litSqInt & (tagMask())))) + && (((byteAt((void *)(litSqInt + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((lengthOf(litSqInt)) == 4))) { + return primitiveExternalCall; + } + index = longAt((void *)((litSqInt + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord())))))); + if (!(((((index) & 7) == 1)) + && ((((index = (index >> 3))) > 0) + && (index <= MaxExternalPrimitiveTableSize)))) { + return primitiveExternalCall; + } + functionPointer = externalPrimitiveTable[index - 1]; + if (!functionPointer) { + return primitiveExternalCall; + } + return functionPointer; + } + + /* begin functionPointerFor:inClass: */ + return ((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]); +} + + +/* currentBytecode will be private to the main dispatch loop in the generated + code. This method allows the currentBytecode to be retrieved from global + variables. Override to answer -1 if we're not in an interpreter frame. */ + + /* CoInterpreter>>#getCurrentBytecode */ +sqInt +getCurrentBytecode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)GIV(framePointer))) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)GIV(framePointer))) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)GIV(framePointer))) <= (((usqInt)GIV(pages)))))))) + && (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) + ? byteAt((void *)(GIV(instructionPointer))) + : -1); +} + + +/* Handle a send fault that may be due to a send to a forwarded object. + Unforward the receiver on the stack and answer it. */ + + /* CoInterpreter>>#handleForwardedSendFaultForReceiver:stackDelta: */ +static NoDbgRegParms sqInt +handleForwardedSendFaultForReceiverstackDelta(sqInt forwardedReceiver, sqInt stackDelta) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt rcvr; + sqInt rcvrStackIndex; + + + /* should *not* be a super send, so the receiver should be forwarded. */ + assert(isOopForwarded(forwardedReceiver)); + rcvrStackIndex = GIV(argumentCount) + stackDelta; + assert((stackValue(rcvrStackIndex)) == forwardedReceiver); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(forwardedReceiver)); + rcvr = longAt((void *)((forwardedReceiver + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord())))); + } + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (rcvrStackIndex * BytesPerWord),rcvr); + followForwardedFrameContentsstackPointer(GIV(framePointer), GIV(stackPointer) + ((rcvrStackIndex + 1) * BytesPerWord)); + if (isPointers(/* frameReceiver: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? longAt(GIV(framePointer) + FoxMFReceiver) + : longAt(GIV(framePointer) + FoxIFReceiver)))) { + followForwardedObjectFieldstoDepth(/* frameReceiver: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? longAt(GIV(framePointer) + FoxMFReceiver) + : longAt(GIV(framePointer) + FoxIFReceiver)), 0); + } + followForwardedFieldsInCurrentMethod(); + return rcvr; +} + + +/* A message send from either an open PIC or an unlinked send has not been + understood. Create a message and execute the relevant resulting MNU + method. messageSelector is an implicit argument (yuck). */ + + /* CoInterpreter>>#handleMNU:InMachineCodeTo:classForMessage: */ +static NoDbgRegParms sqInt +handleMNUInMachineCodeToclassForMessage(sqInt selectorIndex, sqInt rcvr, sqInt classForMessage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classForThisMessage; + CogMethod *cogMethod; + sqInt errSelIdx; + sqInt methodHeader; + sqInt top; + + assert(addressCouldBeOop(rcvr)); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + createActualMessageTo(classForMessage); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(selectorIndex) << (shiftForWord())))))); + if (lookupInMethodCacheSelclassTag(GIV(messageSelector), classTagForClass(GIV(lkupClass)))) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + errSelIdx = lookupMNUInClass((classForThisMessage = GIV(lkupClass))); + if (errSelIdx) { + if (selectorIndex == SelectorDoesNotUnderstand) { + error("Recursive not understood error encountered"); + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return handleMNUInMachineCodeToclassForMessage(errSelIdx, rcvr, classForThisMessage); + } + } + + /* check for coggability because method is in the cache */ + if (/* maybeMethodHasCogMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + executeCogMethodfromUnlinkedSendWithReceiver(cogMethodOf(GIV(newMethod)), rcvr); + + /* NOTREACHED */ + assert(0); + } + return interpretMethodFromMachineCode(); +} + + +/* */ + + /* CoInterpreter>>#iframeIsBlockActivation: */ +static NoDbgRegParms int +iframeIsBlockActivation(char *theFP) +{ + return (byteAt((theFP + FoxIFrameFlags) + 3)) != 0; +} + + /* CoInterpreter>>#iframeReceiver: */ +static NoDbgRegParms sqInt +iframeReceiver(char *theFP) +{ + return longAt(theFP + FoxIFReceiver); +} + + /* CoInterpreter>>#iframeSavedIP: */ +static NoDbgRegParms usqInt +iframeSavedIP(char *theFP) +{ + return ((usqInt)(longAt(theFP + FoxIFSavedIP))); +} + + +/* This is for low-level error reporting. If either of the C stack pointers + are pointing into the stack zone then write them back to framePointer + and/or stackPointer so that the stack backtrace will be up to date. Write + their original values through savedFPP & savedSPP if non-null. */ + + /* CoInterpreter>>#ifValidWriteBackStack:Pointers:Save:To: */ +void +ifValidWriteBackStackPointersSaveTo(void *theCFP, void *theCSP, char **savedFPP, char **savedSPP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (savedFPP) { + savedFPP[0] = GIV(framePointer); + } + if (savedSPP) { + savedSPP[0] = GIV(stackPointer); + } + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theCFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theCFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theCFP)) <= (((usqInt)GIV(pages)))))))) { + GIV(framePointer) = theCFP; + } + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theCSP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theCSP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theCSP)) <= (((usqInt)GIV(pages)))))))) { + GIV(stackPointer) = theCSP; + } +} + + /* CoInterpreter>>#instructionPointerAddress */ +usqInt +instructionPointerAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(instructionPointer)))); +} + + +/* Answer the bytecode pc object (i.e. SmallInteger) for an active frame. The + bytecode pc is derived from the frame's pc. If the frame is the top frame + on the current stack + the frame pc is whatever the current instruction pointer is. If the frame + is the top + frame on some other stack the frame pc is the value on top of stack. + Otherwise the + frame pc is the saved pc of the frame above. Once the frame pc is found it + must be + mapped to a bytecode pc. */ + + /* CoInterpreter>>#instructionPointerForFrame:currentFP:currentIP: */ +static NoDbgRegParms sqInt +instructionPointerForFramecurrentFPcurrentIP(char *spouseFP, char *currentFP, sqInt instrPtr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + char *fp; + char *theFPAbove; + sqInt theIP; + StackPage *thePage; + sqInt value; + + if (spouseFP == currentFP) { + theIP = instrPtr; + } + else { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(spouseFP), GIV(pages)); + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == spouseFP) { + theFPAbove = 0; + goto l1; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == spouseFP) { + theFPAbove = fp; + goto l1; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + theFPAbove = 0; + /* end findFrameAbove:inPage: */ +l1: + theIP = (theFPAbove + ? longAt(theFPAbove + FoxCallerSavedIP) + : longAt((thePage->headSP))); + } + value = contextInstructionPointerframe(theIP, spouseFP); + return ((((int) value)) < 0 + ? mustMapMachineCodePCcontext((value >> 3), longAt(spouseFP + FoxThisContext)) + : value); +} + + +/* At a rough approximation we may need to allocate up to a couple + of page's worth of contexts when switching stack pages, assigning + to senders, etc. But the snapshot primitive voids all stack pages. + So a safe margin is the size of a large context times the maximum + number of frames per page times the number of pages. */ + + /* CoInterpreter>>#interpreterAllocationReserveBytes */ +static NeverInline sqInt +interpreterAllocationReserveBytes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt maxFramesPerPage; + sqInt maxUsedBytesPerPage; + + maxUsedBytesPerPage = (stackPageFrameBytes()) + (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord); + maxFramesPerPage = (maxUsedBytesPerPage / BytesPerWord) / MFrameSlots; + return ((maxFramesPerPage * LargeContextSlots) * BytesPerOop) * GIV(numStackPages); +} + + +/* Execute a method interpretively from machine code. We assume (require) + that newMethod + messageSelector, primitiveFunctionPointer and argumentCount have been set + in the caller. + Once evaluated either continue in the interpreter via a jongjmp or in + machine code via an + enilopmart (a form of longjmp - a stinking rose by any other name). */ + + /* CoInterpreter>>#interpretMethodFromMachineCode */ +static sqInt +interpretMethodFromMachineCode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt arg; + sqInt argIndex; + sqInt fmt; + sqInt header; + usqInt i; + sqInt index; + int inInterpreter; + sqInt maxNumArgs; + sqInt methodHeader; + sqInt nArgs; + usqInt numArgs; + usqInt numSlots; + usqInt numSlotsUsqInt; + usqInt numTemps; + sqInt object; + sqInt objectClass; + sqInt rcvr; + char *savedFramePointer; + char *savedStackPointer; + sqInt selectorOrProperties; + sqInt switched; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt types; + sqInt typesArraySize; + sqInt wordSize; + + assertCStackWellAligned(); + assert(validInstructionPointerinFrame(GIV(instructionPointer), GIV(framePointer))); + if (primitiveFunctionPointer) { + if (primitiveFunctionPointer == primitiveInvokeObjectAsMethod) { + assert(!(isOopCompiledMethod(GIV(newMethod)))); + } + else { + assert((isOopCompiledMethod(GIV(newMethod))) + && ((primitiveIndexOf(GIV(newMethod))) != 0)); + } + + /* Invoke an interpreter primitive (because the method is to be interpreted or has not yet been + compiled). This is very similar to invoking an interpreter primitive from a compiled primitive + (see e.g. SimpleStackBasedCogit>>compileInterpreterPrimitive:). Cut back the stack pointer + (done above) to skip the return address and invoke the function. On return if it has succeeded + simply continue otherwise restore the stackPointer, collect the pc and interpret. Note that + frame building primitives such as primitiveClosureValue, primitiveEvaluateMethod et al will not + return but will instead jump into either machine code or longjmp back to the interpreter. + Assign stackPage headFP so we can tell if the primitive built a frame. We can't simply save + the framePointer since e.g. assignment to contexts (via primitiveInstVarAt:put:) can change the + framePointer. But context assignments will change both the framePointer and stackPage headFP. */ + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + if ((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex) { + externalQuickPrimitiveResponse(); + GIV(primFailCode) = 0; + } + else { + /* begin slowPrimitiveResponse */ + assert(!(isOopForwarded(stackValue(GIV(argumentCount))))); + assert((GIV(remapBufferCount)) == 0); + nArgs = GIV(argumentCount); + savedStackPointer = GIV(stackPointer); + savedFramePointer = GIV(framePointer); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if (/* recordPrimTraceForMethod: */ + (recordPrimTrace()) + && ((primTracePluginName + ? methodHasPrimitiveInPrimTracePlugin(GIV(newMethod)) + : 1))) { + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = GIV(newMethod); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + dispatchFunctionPointer(primitiveFunctionPointer); + assert(maybeLeakCheckExternalPrimCall(GIV(newMethod))); + + /* begin maybeRetryPrimitiveOnFailure */ + if (GIV(primFailCode)) { + retryPrimitiveOnFailure(); + } + if ((!GIV(primFailCode)) + && ((GIV(framePointer) == savedFramePointer) + && (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))))) { + if (GIV(stackPointer) != (savedStackPointer + (nArgs * BytesPerWord))) { + /* This is necessary but insufficient; the result may still have been written to the stack. + At least we'll know something is wrong. */ + failUnbalancedPrimitive(); + GIV(stackPointer) = savedStackPointer; + } + } + + /* Don't fail if primitive has done something radical, e.g. perform: + If we are profiling, take accurate primitive measures */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + + /* successful */ + !GIV(primFailCode); + } + if (!GIV(primFailCode)) { + returntoExecutive(popStack(), 0); + } + } + else { + assert((isOopCompiledMethod(GIV(newMethod))) + && (((primitiveIndexOf(GIV(newMethod))) == 0) + || (((functionPointerForinClass(primitiveIndexOf(GIV(newMethod)), nilObject())) == 0) + || (isNullExternalPrimitiveCall(GIV(newMethod)))))); + } + + /* if not primitive, or primitive failed, activate the method and reenter the interpreter */ + + /* begin activateNewMethod */ + /* begin collectArgumentsTypeFor:fromStackAt:count: */ + /* begin tempVarTypesArraysOf: */ + /* begin additionalMethodStateOf:at: */ + /* begin additionalMethodStateOf: */ + selectorOrProperties = penultimateLiteralOf(GIV(newMethod)); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l3; + } + wordSize = BytesPerWord; + maxNumArgs = (((lengthOf(tempVarTypesArrays)) < GIV(argumentCount)) ? (lengthOf(tempVarTypesArrays)) : GIV(argumentCount)); + for (argIndex = 0; argIndex < maxNumArgs; argIndex += 1) { + /* begin followObjField:ofObject: */ + types = longAt((void *)((tempVarTypesArrays + BaseHeaderSize) + ((((usqInt)(argIndex) << (shiftForWord())))))); + assert(isNonImmediate(types)); + if ((!((longAt((void *)(types))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + types = fixFollowedFieldofObjectwithInitialValue(argIndex, tempVarTypesArrays, types); + } + arg = longAt((void *)(GIV(stackPointer) + (argIndex * wordSize))); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (types) + && (isInstanceOfClassArray(types)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = arg & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(arg)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(types + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(types + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(types - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, types, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, types, objectClass)); + assert(isNonImmediate(types)); + if (oopisGreaterThanOrEqualTo(types, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(types + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(types); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2:; + } + /* end collectArgumentsTypeFor:fromStackAt:count: */ +l3: + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeader)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(!(isOopForwarded(rcvr))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeader); + } + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc). */ + switched = 1; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(canContextSwitchIfActivatingheader(GIV(newMethod), methodHeader)); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + ceInvokeInterpret(); + + /* NOTREACHED */ + return null; +} + + +/* This version exists for asserts */ + + /* CoInterpreter>>#isCogMethodReferenceNoAssert: */ +static NoDbgRegParms int +isCogMethodReferenceNoAssert(sqInt methodHeader) +{ + return (!(methodHeader & (smallIntegerTag()))); +} + + /* CoInterpreter>>#isCogMethodReference: */ +int +isCogMethodReference(sqInt methodHeader) +{ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + return (!(methodHeader & (smallIntegerTag()))); +} + + /* CoInterpreter>>#isExternalMethodInPlugin: */ +static NoDbgRegParms sqInt +isExternalMethodInPlugin(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt headerSqInt; + sqInt literal; + usqInt numSlots; + sqInt primIdx; + + literal = 0; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + headerSqInt = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* primitiveIndexOfMethod:header: */ + if (((header & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((header >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIdx = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIdx = 0; + } + return (primIdx == PrimNumberExternalCall) + && ((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) > 0) + && ((isArray((literal = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord()))))))) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(literal)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(literal + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(literal - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4) + && ((isBytes(longAt((void *)((literal + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralModuleNameIndex) << (shiftForWord())))))))) + && (!((numBytesOf(longAt((void *)((literal + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralModuleNameIndex) << (shiftForWord())))))))) == 0)))))); +} + + +/* Answer if the argument is a full block method or not. Full block methods + (as introduced by the SistaV1BytecodeSet) are used to implement block + closures and have a reference to their enclosing block or method as their + last literal. Compiled methods have a class association as their last + literal. */ + + /* CoInterpreter>>#isFullBlockMethod: */ +sqInt +isFullBlockMethod(sqInt aMethodObj) +{ + sqInt oop; + + oop = ultimateLiteralOf(aMethodObj); + + /* begin isOopCompiledMethod: */ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())); +} + + /* CoInterpreter>>#isMachineCodeFrameForCogMethodFlaggedForBecome: */ +static NoDbgRegParms sqInt +isMachineCodeFrameForCogMethodFlaggedForBecome(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && (isCMMethodFlaggedForBecome(((CogBlockMethod *) (mframeHomeMethod(theFP))))); +} + + /* CoInterpreter>>#isMachineCodeFrameForCogMethodWithMachineCodePrim: */ +static NoDbgRegParms sqInt +isMachineCodeFrameForCogMethodWithMachineCodePrim(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && (cogMethodHasMachineCodePrim(mframeHomeMethod(theFP))); +} + + /* CoInterpreter>>#isMachineCodeFrameForCogMethodWithTooManyLiterals: */ +static NoDbgRegParms sqInt +isMachineCodeFrameForCogMethodWithTooManyLiterals(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && (!(((/* begin literalCountOfMethodHeader: */ + assert(((((((mframeHomeMethod(theFP))->methodHeader))) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + (((((mframeHomeMethod(theFP))->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile)); +} + + /* CoInterpreter>>#isMachineCodeFrameForExternalPrimitiveMethod: */ +static NoDbgRegParms sqInt +isMachineCodeFrameForExternalPrimitiveMethod(char *theFP) +{ + CogMethod *homeMethod; + + homeMethod = ((CogMethod *) 0); + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && ((cogMethodHasMachineCodePrim((homeMethod = mframeHomeMethod(theFP)))) + && (isExternalMethodInPlugin((homeMethod->methodObject)))); +} + + /* CoInterpreter>>#isMachineCodeFrameForRealNonProfilingPrimitive: */ +static NoDbgRegParms sqInt +isMachineCodeFrameForRealNonProfilingPrimitive(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt firstLiteral; + sqInt functionName; + sqInt header; + CogMethod *homeMethod; + sqInt methodField; + sqInt methodHeader; + sqInt methodObj; + usqInt numSlots; + void (*pf)(void); + sqInt primitiveIndex; + + if (!((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()))) { + return 0; + } + + /* begin mframeHomeMethod: */ + methodField = longAt(theFP + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + homeMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + goto l1; + } + homeMethod = ((CogMethod *) (methodField & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + methodObj = (homeMethod->methodObject); + + /* begin isRealNonProfilingPrimitiveMethod: */ + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin isRealNonProfilingPrimitiveMethod:primIndex: */ + if (!(((primitiveIndex >= 1) && (primitiveIndex <= MaxPrimitiveIndex)))) { + return 0; + } + + /* Filter-out the profiling primitives themselves. */ + if (primitiveIndex == PrimNumberExternalCall) { + firstLiteral = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(firstLiteral & (tagMask())))) + && (((byteAt((void *)(firstLiteral + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(firstLiteral)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(firstLiteral + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(firstLiteral - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4))) { + return 1; + } + functionName = longAt((void *)((firstLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFunctionNameIndex) << (shiftForWord())))))); + return ((numBytesOf(functionName)) < (21)) + || (0 != (strncmp("primitiveProfile", ((char *) (firstIndexableField(functionName))), 16))); + } + + /* Answer true if there's a real primitive here, excepting the ValueNoContextSwitch primitives, + because a sample can't be taken in them, as doing so could cause a context switch. */ + pf = primitiveTable[primitiveIndex]; + return (pf != (((void *)0))) + && ((pf != (((void (*)(void)) primitiveFail))) + && ((pf != primitiveClosureValueNoContextSwitch) + && (pf != primitiveFullClosureValueNoContextSwitch))); +} + + /* CoInterpreter>>#isMachineCodeFrame: */ +static NoDbgRegParms int +isMachineCodeFrame(char *theFP) +{ + return (((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()); +} + + +/* Answer if the methodObj's primitive is one that should be profiled. + Filter-out the profiling primitives themselves. */ + + /* CoInterpreter>>#isRealNonProfilingPrimitiveMethod: */ +static NoDbgRegParms sqInt +isRealNonProfilingPrimitiveMethod(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt firstLiteral; + sqInt functionName; + sqInt header; + sqInt methodHeader; + usqInt numSlots; + void (*pf)(void); + sqInt primitiveIndex; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin isRealNonProfilingPrimitiveMethod:primIndex: */ + if (!(((primitiveIndex >= 1) && (primitiveIndex <= MaxPrimitiveIndex)))) { + return 0; + } + + /* Filter-out the profiling primitives themselves. */ + if (primitiveIndex == PrimNumberExternalCall) { + firstLiteral = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(firstLiteral & (tagMask())))) + && (((byteAt((void *)(firstLiteral + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(firstLiteral)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(firstLiteral + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(firstLiteral - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4))) { + return 1; + } + functionName = longAt((void *)((firstLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFunctionNameIndex) << (shiftForWord())))))); + return ((numBytesOf(functionName)) < (21)) + || (0 != (strncmp("primitiveProfile", ((char *) (firstIndexableField(functionName))), 16))); + } + + /* Answer true if there's a real primitive here, excepting the ValueNoContextSwitch primitives, + because a sample can't be taken in them, as doing so could cause a context switch. */ + pf = primitiveTable[primitiveIndex]; + return (pf != (((void *)0))) + && ((pf != (((void (*)(void)) primitiveFail))) + && ((pf != primitiveClosureValueNoContextSwitch) + && (pf != primitiveFullClosureValueNoContextSwitch))); +} + + +/* Answer if the methodObj's primitive is one that should be profiled. + In particular answer false for the no-context-switch value primitives and + the profiling primitives themselves. */ + + /* CoInterpreter>>#isRealNonProfilingPrimitiveMethod:primIndex: */ +static NoDbgRegParms sqInt +isRealNonProfilingPrimitiveMethodprimIndex(sqInt methodObj, sqInt primitiveIndex) +{ + sqInt firstLiteral; + sqInt functionName; + usqInt numSlots; + void (*pf)(void); + + if (!(((primitiveIndex >= 1) && (primitiveIndex <= MaxPrimitiveIndex)))) { + return 0; + } + + /* Filter-out the profiling primitives themselves. */ + if (primitiveIndex == PrimNumberExternalCall) { + firstLiteral = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(firstLiteral & (tagMask())))) + && (((byteAt((void *)(firstLiteral + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(firstLiteral)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(firstLiteral + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(firstLiteral - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4))) { + return 1; + } + functionName = longAt((void *)((firstLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFunctionNameIndex) << (shiftForWord())))))); + return ((numBytesOf(functionName)) < (21)) + || (0 != (strncmp("primitiveProfile", ((char *) (firstIndexableField(functionName))), 16))); + } + + /* Answer true if there's a real primitive here, excepting the ValueNoContextSwitch primitives, + because a sample can't be taken in them, as doing so could cause a context switch. */ + pf = primitiveTable[primitiveIndex]; + return (pf != (((void *)0))) + && ((pf != (((void (*)(void)) primitiveFail))) + && ((pf != primitiveClosureValueNoContextSwitch) + && (pf != primitiveFullClosureValueNoContextSwitch))); +} + + +/* See if the argument is married to a live frame or not. i.e. see if there + is a matching + frame whose frameContext field is aOnceMarriedContext, or a forwarder to + it. If aOnceMarriedContext is not married to a live frame, turn it into a + bereaved single context. + This version is for use during scavenging when stack references may be + forwarded. Following what appear to be references to forwarded objects on + the stack is dangerous; + an instruction ponter may be correctly aligned and may point to bytes that + just happen + to look like a forwarder. So it is only safe to follow fields that we know + are frameContext + fields; hence the stack page is walked to check that aOnceMarriedContext + is pointing to + a live frame. This only has to happen during scavenging because after a + become: all + frameContext fields have been followed and so there is no need to follow + forwarders. */ +/* Override to check gcMode is correct. See comment in superclass, and in + superclass's isWidowedContext:. + */ + + /* CoInterpreter>>#isWidowedContextDuringGC: */ +static NoDbgRegParms sqInt +isWidowedContextDuringGC(sqInt aOnceMarriedContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anInteger; + char *maybeFrame; + sqInt maybeFrameCtxt; + sqInt referent; + sqInt senderOop; + char *shouldBeFrameCallerField; + StackPage *thePage; + + assert((GIV(gcMode) == GCModeNewSpace) + || ((GIV(gcMode) == GCModeFull) + || (GIV(gcMode) == GCModeImageSegment))); + assert((isContext(aOnceMarriedContext)) + && (isMarriedOrWidowedContext(aOnceMarriedContext))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + maybeFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(maybeFrame), GIV(pages)); + if (!((isFree(thePage)) + || (maybeFrame < ((thePage->headFP))))) { + /* The frame pointer is within the bounds of a live page. + Now check if it matches a frame. */ + anInteger = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + + /* begin withoutSmallIntegerTags: */ + assert((((anInteger) & 7) == 1)); + shouldBeFrameCallerField = ((char *)(anInteger - (smallIntegerTag()))); + if (((((char *)(longAt(maybeFrame + FoxSavedFP)))) == shouldBeFrameCallerField) + && (/* frameHasContext: */ + ((((usqInt)(longAt(maybeFrame + FoxMethod)))) < (startOfMemory()) + ? ((longAt(maybeFrame + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((maybeFrame + FoxIFrameFlags) + 2)) != 0))) { + maybeFrameCtxt = longAt(maybeFrame + FoxThisContext); + + /* On Spur we need to follow the context to check for a match, but since the VM is + only speculating about maybeFrame being a frame, and only speculating about + maybeContext being a context, we need to be sure before we can safely follow. */ + if ((isFrameonPage(maybeFrame, thePage)) + && ((!((longAt((void *)(maybeFrameCtxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(maybeFrameCtxt)); + referent = longAt((void *)((maybeFrameCtxt + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + maybeFrameCtxt = referent; + + /* begin setFrameContext:to: */ + longAtput(maybeFrame + FoxThisContext,maybeFrameCtxt); + } + if (maybeFrameCtxt == aOnceMarriedContext) { + return 0; + } + } + } + + /* It is out of range or doesn't match the frame's context. + It is widowed. Time to wear black. */ + + /* begin markContextAsDead: */ + assert(isContext(aOnceMarriedContext)); + assert((isNonImmediate(aOnceMarriedContext)) + && (!(isForwarded(aOnceMarriedContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, aOnceMarriedContext, GIV(nilObj))); + longAtput((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aOnceMarriedContext)) + && (!(isForwarded(aOnceMarriedContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, aOnceMarriedContext, GIV(nilObj))); + longAtput((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + return 1; +} + + +/* Answer if the argument is married to a live frame or not. + If it is not, turn it into a bereaved single context. This version is safe + for use + only when no frameContext fields may be forwarded (as maybe the case + when scavenging). Post become: all frameContext fields are followed, and + hence normally no following of frameContext fields is necessary. But + during a scavenge one must use isWidowedContextDuringGC:. */ +/* Override to check gcMode is correct. See comment in superclass. */ + + /* CoInterpreter>>#isWidowedContext: */ +static NoDbgRegParms sqInt +isWidowedContext(sqInt aOnceMarriedContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anInteger; + sqInt senderOop; + char *shouldBeFrameCallerField; + char *theFrame; + StackPage *thePage; + sqInt widowed; + + assert(GIV(gcMode) != GCModeNewSpace); + + /* begin isWidowedContextNoConvert: */ + assert((isContext(aOnceMarriedContext)) + && (isMarriedOrWidowedContext(aOnceMarriedContext))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + if (!((isFree(thePage)) + || (theFrame < ((thePage->headFP))))) { + /* The frame pointer is within the bounds of a live page. + Now check if it matches a frame. */ + anInteger = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + + /* begin withoutSmallIntegerTags: */ + assert((((anInteger) & 7) == 1)); + shouldBeFrameCallerField = ((char *)(anInteger - (smallIntegerTag()))); + if (((((char *)(longAt(theFrame + FoxSavedFP)))) == shouldBeFrameCallerField) + && (/* frameHasContext: */ + ((((usqInt)(longAt(theFrame + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFrame + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFrame + FoxIFrameFlags) + 2)) != 0))) { + assert(!(((isFrameonPage(theFrame, thePage)) + && (isForwarded(frameContext(theFrame)))))); + if ((longAt(theFrame + FoxThisContext)) == aOnceMarriedContext) { + widowed = 0; + goto l1; + } + } + } + widowed = 1; + /* end isWidowedContextNoConvert: */ +l1: + if (!widowed) { + return 0; + } + + /* begin markContextAsDead: */ + assert(isContext(aOnceMarriedContext)); + assert((isNonImmediate(aOnceMarriedContext)) + && (!(isForwarded(aOnceMarriedContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, aOnceMarriedContext, GIV(nilObj))); + longAtput((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aOnceMarriedContext)) + && (!(isForwarded(aOnceMarriedContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, aOnceMarriedContext, GIV(nilObj))); + longAtput((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + return 1; +} + + +/* If the method is assigned, any machine code pc must be mapped to a + bytecode one + before the method is changed. */ + + /* CoInterpreter>>#is:methodAssignmentToContextWithMachineCodePC: */ +static NoDbgRegParms sqInt +ismethodAssignmentToContextWithMachineCodePC(sqInt fieldIndex, sqInt anOop) +{ + sqInt thePC; + + thePC = 0; + return (fieldIndex == MethodIndex) + && (((((int) ((thePC = longAt((void *)((anOop + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))))) < 0) + && ((((thePC) & 7) == 1))); +} + + /* CoInterpreter>>#longRunningPrimitiveStopUsecsAddress */ +#if LRPCheck +usqInt +longRunningPrimitiveStopUsecsAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(longRunningPrimitiveStopUsecs)))); +} +#endif /* LRPCheck */ + + +/* Lookup selector in rcvr, without doing MNU processing, and answer either a + method or an error code if the message was not understood. Used to + populate closed PICs. */ +/* self printFrame: stackPage headFP WithSP: stackPage headSP */ +/* self printStringOf: selector */ + + /* CoInterpreter>>#lookupMNU:receiver: */ +sqInt +lookupMNUreceiver(sqInt selector, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTag; + sqInt erridx; + sqInt inCache; + sqInt tagBits; + + classTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + inCache = lookupInMethodCacheSelclassTag(selector, classTag); + if (!inCache) { + GIV(messageSelector) = selector; + erridx = lookupMNUInClass(classForClassTag(classTag)); + if (erridx) { + assert(erridx <= (maxLookupNoMNUErrorCode())); + return erridx; + } + } + return GIV(newMethod); +} + + +/* Lookup selector in rcvr, without doing MNU processing, and answer either a + method or an error code if the message was not understood. Used to + populate closed PICs. */ +/* self printFrame: stackPage headFP WithSP: stackPage headSP */ +/* self printStringOf: selector */ + + /* CoInterpreter>>#lookupOrdinary:receiver: */ +sqInt +lookupOrdinaryreceiver(sqInt selector, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTag; + sqInt erridx; + sqInt tagBits; + + classTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + if (!(lookupInMethodCacheSelclassTag(selector, classTag))) { + GIV(messageSelector) = selector; + if ((erridx = lookupOrdinaryNoMNUEtcInClass(classForClassTag(classTag)))) { + assert(erridx <= (maxLookupNoMNUErrorCode())); + return erridx; + } + } + return GIV(newMethod); +} + + +/* */ +/* Marry aContext with the base frame of a new stack page. Build the base + frame to reflect the context's state. Answer the new page. Override to + hold the caller context in a different place, In the StackInterpreter we + use the caller saved ip, but in the Cog VM caller saved ip is the + ceBaseReturn: trampoline. Simply hold the caller context in the first word + of the stack. */ + + /* CoInterpreter>>#makeBaseFrameFor: */ +static NoDbgRegParms StackPage * +makeBaseFrameFor(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cogMethod; + CogMethod *cogMethod1; + sqInt header; + sqInt headerSqInt; + sqInt i; + sqInt maybeClosure; + usqInt numArgs; + usqInt numSlots; + sqInt oop; + StackPage *page; + char *pointer; + sqInt rawHeader; + sqInt rcvr; + sqInt stackPtrIndex; + sqInt theIP; + sqInt theMethod; + sqInt valuePointer; + sqInt yetToCompact; + + + /* theIP must be typed as signed because it is assigned ceCannotResumePC and so maybe implicitly typed as unsigned. */ + assert(isContext(aContext)); + assert(isSingleContext(aContext)); + assert(goodContextSize(aContext)); + theIP = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + theIP = ((((theIP) & 7) == 1) + ? (theIP >> 3) + : HasBeenReturnedFromMCPC); + + /* begin followObjField:ofObject: */ + theMethod = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + assert(isNonImmediate(theMethod)); + if ((!((longAt((void *)(theMethod))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + theMethod = fixFollowedFieldofObjectwithInitialValue(MethodIndex, aContext, theMethod); + } + + /* begin newStackPage */ + page = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((page->baseFP))) { + goto l1; + } + divorceFramesIn(page); + /* end newStackPage */ +l1: + + /* first word on stack is caller context of base frame */ + longAtput((pointer = (page->baseAddress)),followObjFieldofObject(SenderIndex, aContext)); + + /* second word is the context itself; needed for cannotReturn processing; see ceBaseReturn:. */ + longAtput((pointer -= BytesPerWord),aContext); + + /* begin followField:ofObject: */ + rcvr = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = fixFollowedFieldofObjectwithInitialValue(ReceiverIndex, aContext, rcvr); + } + + /* If the frame is a closure activation then the closure should be on the stack in + the pushed receiver position (closures receive the value[:value:] messages). + Otherwise it should be the receiver proper. */ + maybeClosure = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord())))))); + if (maybeClosure != GIV(nilObj)) { + if ((!((longAt((void *)(maybeClosure))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + maybeClosure = fixFollowedFieldofObjectwithInitialValue(ClosureIndex, aContext, maybeClosure); + } + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + longAtput((pointer -= BytesPerWord),maybeClosure); + } + else { + /* begin methodHeaderOf: */ + assert(isCompiledMethod(theMethod)); + headerSqInt = longAt((void *)((theMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + numArgs = (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; + + /* If this is a synthetic context its IP could be pointing at the CallPrimitive opcode. If so, skip it. */ + if ((((header & AlternateHeaderHasPrimFlag) != 0)) + && (theIP == (1 + ((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop)))) { + theIP += 3 /* sizeOfCallPrimitiveBytecode: */; + } + longAtput((pointer -= BytesPerWord),rcvr); + } + + /* Put the arguments on the stack */ + for (i = 1; i <= numArgs; i += 1) { + longAtput((pointer -= BytesPerWord),longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))))); + } + + /* saved caller ip is base return trampoline */ + longAtput((pointer -= BytesPerWord),ceBaseFrameReturnPC()); + + /* base frame's saved fp is null */ + longAtput((pointer -= BytesPerWord),0); + + /* N.B. Don't set the baseFP, which marks the page as in use, until after + ensureMethodIsCogged: and/or instructionPointer:forContext:frame:. These + can cause a compiled code compaction which, if marked as in use, will + examine this partially initialized page and crash. */ + (page->headFP = pointer); + + /* Create either a machine code frame or an interpreter frame based on the pc. If the pc is -ve + it is a machine code pc and so we produce a machine code frame. If +ve an interpreter frame. + N.B. Do *not* change this to try and map from a bytecode pc to a machine code frame under + any circumstances. See ensureContextIsExecutionSafeAfterAssignToStackPointer: */ + if (theIP < 0) { + /* Since we would have to generate a machine-code method to be able to map + the native pc anyway we should create a native method and native frame. */ + + /* begin ensureMethodIsCogged:maybeClosure: */ + rawHeader = longAt((void *)((theMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) rawHeader); + goto l2; + } + yetToCompact = 1; + while (1) { + if ((maybeClosure != GIV(nilObj)) + && (!((/* begin isVanillaBlockClosure: */ + assert((isPointers(maybeClosure)) + && ((numSlotsOf(maybeClosure)) >= ClosureFirstCopiedValueIndex)), + ((((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1))))) { + cogMethod1 = cogFullBlockMethodnumCopied( + theMethod, + (/* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(maybeClosure)))), + ((/* begin numSlotsOf: */ + assert((classIndexOf(maybeClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(maybeClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(maybeClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex)); + } + else { + cogMethod1 = cogselector(theMethod, GIV(nilObj)); + } + if (!((cogMethod1 == null) + && (GIV(cogCompiledCodeCompactionCalledFor) + && (yetToCompact)))) break; + yetToCompact = 0; + commenceCogCompiledCodeCompaction(); + } + if (!(asserta(cogMethod1 != null))) { + error("could not compile method that should have been compiled"); + } + cogMethod = cogMethod1; + /* end ensureMethodIsCogged:maybeClosure: */ +l2: + theMethod = ((sqInt)cogMethod); + if (maybeClosure != GIV(nilObj)) { + /* begin isVanillaBlockClosure: */ + assert((isPointers(maybeClosure)) + && ((numSlotsOf(maybeClosure)) >= ClosureFirstCopiedValueIndex)); + if (((((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* If the pc is the special HasBeenReturnedFromMCPC pc set the pc + appropriately so that the frame stays in the cannotReturn: state. */ + if (theIP == HasBeenReturnedFromMCPC) { + theMethod = ((sqInt)(findMethodForStartBcpcinHomeMethod((((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))))) >> 3)) - 1, ((CogMethod *) theMethod)))); + if (!theMethod) { + error("cannot find machine code block matching closure's startpc"); + } + theIP = ceCannotResumePC(); + } + else { + assert((((theIP) >> 16)) < -1); + theMethod -= (((theIP) >> 16)) * (blockAlignment()); + theIP = theMethod - (((short)theIP)); + } + } + else { + assert((((theIP) >> 16)) >= -1); + + /* If the pc is the special HasBeenReturnedFromMCPC pc set the pc + appropriately so that the frame stays in the cannotReturn: state. */ + theIP = (theIP == HasBeenReturnedFromMCPC + ? ceCannotResumePC() + : (((sqInt)theMethod)) - theIP); + } + longAtput((pointer -= BytesPerWord),(theMethod + MFMethodFlagHasContextFlag) + MFMethodFlagIsBlockFlag); + } + else { + assert((((theIP) >> 16)) >= -1); + + /* If the pc is the special HasBeenReturnedFromMCPC pc set the pc + appropriately so that the frame stays in the cannotReturn: state. */ + theIP = (theIP == HasBeenReturnedFromMCPC + ? ceCannotResumePC() + : (((sqInt)theMethod)) - theIP); + longAtput((pointer -= BytesPerWord),theMethod + MFMethodFlagHasContextFlag); + } + longAtput((pointer -= BytesPerWord),aContext); + } + else { + longAtput((pointer -= BytesPerWord),theMethod); + longAtput((pointer -= BytesPerWord),aContext); + longAtput((pointer -= BytesPerWord),/* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8)))) + (1ULL << ((BytesPerWord * 8) - 16))) + ((maybeClosure != GIV(nilObj) + ? 1ULL << ((BytesPerWord * 8) - 24) + : 0)) + : ((1 + ((numArgs << 8))) + (0x10000)) + ((maybeClosure != GIV(nilObj) + ? 0x1000000 + : 0)))); + longAtput((pointer -= BytesPerWord),0); + + /* begin iframeInstructionPointerForIndex:method: */ + assert(((theIP >= (((LiteralStart + (literalCountOf(theMethod))) * BytesPerOop) + 1)) && (theIP <= (lengthOf(theMethod))))); + theIP = ((theMethod + theIP) + BaseHeaderSize) - 2; + } + (page->baseFP = (page->headFP)); + assert(frameHasContext((page->baseFP))); + assert((frameNumArgs((page->baseFP))) == numArgs); + longAtput((pointer -= BytesPerWord),rcvr); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + stackPtrIndex = (oop >> 3); + assert((ReceiverIndex + stackPtrIndex) < (lengthOf(aContext))); + for (i = (numArgs + 1); i <= stackPtrIndex; i += 1) { + longAtput((pointer -= BytesPerWord),longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))))); + + /* nil the slot in the context so that it doesn't inadvertently hang onto some collectable object. + Thanks to Ryan Macnak for identifying this bug */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aContext)) + && (!(isForwarded(aContext)))); + assert(validStorePointerUncheckedArgs(ReceiverIndex + i, aContext, GIV(nilObj))); + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))),GIV(nilObj)); + } + + /* top of stack is the instruction pointer */ + longAtput((pointer -= BytesPerWord),theIP); + (page->headSP = pointer); + assert(contexthasValidInversePCMappingOfin(aContext, theIP, (page->baseFP))); + + /* Mark context as married by setting its sender to the frame pointer plus SmallInteger + tags and the InstructionPointer to the saved fp (which ensures correct alignment + w.r.t. the frame when we check for validity) plus SmallInteger tags. */ + /* begin withSmallIntegerTags: */ + assert(((oopForPointer((page->baseFP))) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer((page->baseFP))) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aContext)) + && (!(isForwarded(aContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, aContext, valuePointer)); + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),valuePointer); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(0)) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(0)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aContext)) + && (!(isForwarded(aContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, aContext, valuePointer)); + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointer); + assert(((((fetchPointerofObject(SenderIndex, aContext))) & 7) == 1)); + assert((frameOfMarriedContext(aContext)) == ((page->baseFP))); + assert(validStackPageBaseFrame(page)); + return page; +} + + /* CoInterpreter>>#mapStackPages */ +static NeverInline void +mapStackPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + char *frameRcvrOffset; + sqInt i; + sqInt numLivePages; + sqInt oop; + char *theFP; + sqInt theIP; + char *theIPPtr; + StackPage *thePage; + char *theSP; + + callerFP = ((char *) 0); + theIP = 0; + numLivePages = 0; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((thePage->baseFP)) { + assert(ifCurrentStackPageHasValidHeadPointers(thePage)); + numLivePages += 1; + theSP = (thePage->headSP); + theFP = (thePage->headFP); + + /* Skip the instruction pointer on top of stack of inactive pages. */ + if (thePage == GIV(stackPage)) { + theIPPtr = (((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + || ((((usqInt)(longAt(theFP + FoxIFSavedIP)))) == 0) + ? 0 + : theFP + FoxIFSavedIP); + } + else { + theIPPtr = theSP; + theSP += BytesPerWord; + } + while (1) { + assert(addressIsInPage(thePage, theFP)); + assert(addressIsInPage(thePage, theSP)); + assert((theIPPtr == 0) + || (addressIsInPage(thePage, theIPPtr))); + frameRcvrOffset = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + while (theSP <= frameRcvrOffset) { + oop = longAt(theSP); + if (/* shouldRemapOop: */ + ((!(oop & (tagMask())))) + && (shouldRemapObj(oop))) { + longAtput(theSP,remapObj(oop)); + } + theSP += BytesPerWord; + } + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + if (shouldRemapObj(longAt(theFP + FoxThisContext))) { + longAtput(theFP + FoxThisContext,remapObj(longAt(theFP + FoxThisContext))); + } + + /* With SqueakV3 objectMemory or SpurPlanningCompactor can't assert since object body is yet to move. */ + if (!((GIV(gcPhaseInProgress) == SlidingCompactionInProgress) + || (GIV(gcPhaseInProgress) == ScavengeInProgress))) { + assert((isMarriedOrWidowedContext(frameContext(theFP))) + && ((frameOfMarriedContext(frameContext(theFP))) == theFP)); + } + } + if (!((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()))) { + if (shouldRemapObj(longAt(theFP + FoxMethod))) { + if (theIPPtr) { + theIP = longAt(theIPPtr); + if (theIP == (ceReturnToInterpreterPC())) { + assert((iframeSavedIP(theFP)) > (iframeMethod(theFP))); + theIPPtr = theFP + FoxIFSavedIP; + theIP = longAt(theIPPtr); + } + else { + assert(theIP > (iframeMethod(theFP))); + } + theIP -= longAt(theFP + FoxMethod); + } + longAtput(theFP + FoxMethod,remapObj(longAt(theFP + FoxMethod))); + if (theIPPtr) { + longAtput(theIPPtr,theIP + (longAt(theFP + FoxMethod))); + } + } + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theSP = ((theIPPtr = theFP + FoxCallerSavedIP)) + BytesPerWord; + theFP = callerFP; + } + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if (/* shouldRemapOop: */ + ((!(oop & (tagMask())))) + && (shouldRemapObj(oop))) { + longAtput(theSP,remapObj(oop)); + } + theSP += BytesPerWord; + } + } + } + + /* begin recordLivePagesOnMapping: */ + GIV(statNumMaps) += 1; + GIV(statPageCountWhenMappingSum) += numLivePages; + GIV(statMaxPageCountWhenMapping) = ((GIV(statMaxPageCountWhenMapping) < numLivePages) ? numLivePages : GIV(statMaxPageCountWhenMapping)); +} + + +/* If instructionPointer is referring to machine code, as it will be if a + primitive is in progress + (see isCodeCompactingPrimitiveIndex:) it may refer to a method, and if so + that method + must be retained. */ + + /* CoInterpreter>>#markActiveMethodsAndReferents */ +void +markActiveMethodsAndReferents(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + CogMethod *primCogMethod; + StackPage *thePage; + + if (GIV(instructionPointer)) { + if ((primCogMethod = cogMethodContaining(GIV(instructionPointer)))) { + markMethodAndReferents(((CogBlockMethod *) primCogMethod)); + } + } + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + markCogMethodsAndReferentsOnPage(thePage); + } + } +} + + /* CoInterpreter>>#markAndTraceStackPage: */ +static NoDbgRegParms void +markAndTraceStackPage(StackPage *thePage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogBlockMethod *aCogMethod; + char *callerFP; + char *frameRcvrOffset; + CogMethod *homeMethod; + sqInt oop; + sqInt referent; + char *theFP; + char *theSP; + + callerFP = ((char *) 0); + assert(!(isFree(thePage))); + assert(ifCurrentStackPageHasValidHeadPointers(thePage)); + assert(((thePage->trace)) != StackPageTraced); + (thePage->trace = StackPageTraced); + theSP = (thePage->headSP); + theFP = (thePage->headFP); + + /* Skip the instruction pointer on top of stack of inactive pages. */ + if (!(thePage == GIV(stackPage))) { + theSP += BytesPerWord; + } + while (1) { + frameRcvrOffset = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + while (theSP <= frameRcvrOffset) { + oop = longAt(theSP); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + longAtput(theSP,oop); + } + if (!(((oop & (tagMask())) != 0))) { + markAndTrace(oop); + } + theSP += BytesPerWord; + } + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + markAndTrace(frameContext(theFP)); + } + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* begin mframeCogMethod: */ + aCogMethod = ((CogBlockMethod *) ((longAt(theFP + FoxMethod)) & MFMethodMask)); + homeMethod = /* asCogHomeMethod: */ + (((aCogMethod->cmType)) >= CMMethod + ? ((CogMethod *) aCogMethod) + : /* cmHomeMethod */ + (((aCogMethod->cmType)) >= CMMethod + ? ((CogMethod *) aCogMethod) + : (assert(CMBlock == (aCogMethod->cmType)), + ((CogMethod *) ((((usqInt)aCogMethod)) - ((aCogMethod->homeOffset))))))); + markAndTrace((homeMethod->methodObject)); + } + else { + markAndTrace(iframeMethod(theFP)); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + theFP = callerFP; + } + + /* caller ip is ceBaseReturnPC */ + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + longAtput(theSP,oop); + } + if (!(((oop & (tagMask())) != 0))) { + markAndTrace(oop); + } + theSP += BytesPerWord; + } +} + + /* CoInterpreter>>#markCogMethodsAndReferentsOnPage: */ +static NoDbgRegParms void +markCogMethodsAndReferentsOnPage(StackPage *thePage) +{ + char *callerFP; + char *theFP; + + callerFP = ((char *) 0); + assert(!(isFree(thePage))); + assert(ifCurrentStackPageHasValidHeadPointers(thePage)); + theFP = (thePage->headFP); + while (1) { + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + markMethodAndReferents(mframeCogMethod(theFP)); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theFP = callerFP; + } +} + + +/* Marry an unmarried frame. This means creating a spouse context + initialized with a subset of the frame's state that references the frame. + For the default closure implementation we do not need to copy temps. + + This method is important enough for performance to be worth streamlining. + + Override to set the ``has context'' flag appropriately for both machine + code and + interpreter frames and to streamline the machine code/interpreter + differences. + */ + + /* CoInterpreter>>#marryFrame:SP: */ +static NoDbgRegParms usqInt +marryFrameSP(char *theFP, char *theSP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt closureOrNil; + CogMethod *cogMethod; + sqInt header; + sqInt headerSqInt; + sqInt i; + sqInt methodFieldOrObj; + usqInt newObj; + usqInt numArgs; + usqInt numBytes; + sqInt numSlots; + sqInt numStack; + sqInt rcvr; + usqInt theContext; + sqInt valuePointer; + + assert(!((frameHasContext(theFP)))); + assert(!((isBaseFrame(theFP)))); + assert(addressCouldBeOop(longAt(theSP))); + + /* Decide how much of the stack to preserve in widowed contexts. Preserving too much state will potentially + hold onto garbage. Holding onto too little may mean that a dead context isn't informative enough in a + debugging situation. But the fundamental issue is that the temporary values captured when the frame is + married are likely stale, so if the temporary variable values are important they need to be updated later; + see primitiveContextPreserveTemps. So when marrying we capture the arguments and set the stack pointer + to accomodate (at least *) the temporaries, but the temporaries are all set to nil. In machine code contexts + are created via code generated by genGetActiveContextLarge:inBlock: which must agree perfectly with + this method. + * the V3PlusClosures bytecode set has to use pushNil bytecodes to establish slots for temporary variables + in block contexts, but a pushNil may be ambiguous with oushing a receiver, or even argument, e.g. + [nil == nil]. Instead of sweating the issue of using an accurate temporary count, we simply set the stack + pointer to its current value and set all slots following the arguments to nil. */ + methodFieldOrObj = longAt(theFP + FoxMethod); + if ((((usqInt)methodFieldOrObj)) < (startOfMemory())) { + longAtput(theFP + FoxMethod,methodFieldOrObj | MFMethodFlagHasContextFlag); + cogMethod = ((CogMethod *) (methodFieldOrObj & MFMethodMask)); + numArgs = (cogMethod->cmNumArgs); + if (((cogMethod->cmType)) >= CMMethod) { + closureOrNil = ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock) + ? longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((numArgs << (shiftForWord()))))) + : GIV(nilObj)); + } + else { + assert(isCMBlock(((CogBlockMethod *) cogMethod))); + + /* cmHomeMethod */ + if ((((((CogBlockMethod *) cogMethod))->cmType)) >= CMMethod) { + cogMethod = ((CogMethod *) (((CogBlockMethod *) cogMethod))); + } + else { + assert(CMBlock == ((((CogBlockMethod *) cogMethod))->cmType)); + cogMethod = ((CogMethod *) ((((usqInt)(((CogBlockMethod *) cogMethod)))) - (((((CogBlockMethod *) cogMethod))->homeOffset)))); + } + closureOrNil = longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((numArgs << (shiftForWord()))))); + } + methodFieldOrObj = (cogMethod->methodObject); + header = (cogMethod->methodHeader); + rcvr = longAt(theFP + FoxMFReceiver); + } + else { + /* begin setIFrameHasContext: */ + byteAtput((theFP + FoxIFrameFlags) + 2,1); + assert(isCompiledMethod(methodFieldOrObj)); + headerSqInt = longAt((void *)((methodFieldOrObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + if (byteAt((theFP + FoxIFrameFlags) + 3)) { + numArgs = byteAt((theFP + FoxIFrameFlags) + 1); + closureOrNil = longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((numArgs << (shiftForWord()))))); + } + else { + numArgs = (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; + closureOrNil = GIV(nilObj); + } + rcvr = longAt(theFP + FoxIFReceiver); + } + + /* inline (self isMachineCodeFrame: theFP) */ + numStack = (((usqInt)(((theFP + FoxMFReceiver) - theSP))) >> (shiftForWord())) + numArgs; + numSlots = (header & LargeContextBit + ? LargeContextSlots + : SmallContextSlots); + + /* begin eeInstantiateMethodContextSlots: */ + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + theContext = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((indexablePointersFormat())) << (formatShift()))))) + ClassMethodContextCompactIndex); + GIV(freeStart) += numBytes; + theContext = newObj; + /* end eeInstantiateMethodContextSlots: */ +l1: + + /* begin setFrameContext:to: */ + longAtput(theFP + FoxThisContext,theContext); + + /* Mark context as married by setting its sender to the frame pointer plus SmallInteger tags and the InstructionPointer + to the saved fp (which ensures correct alignment w.r.t. the frame when we check for validity) */ + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(theFP)) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(theFP)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, theContext, valuePointer)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),valuePointer); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(((char *)(longAt(theFP + FoxSavedFP))))) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(((char *)(longAt(theFP + FoxSavedFP))))) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, theContext, valuePointer)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(StackPointerIndex, theContext, (((usqInt)numStack << 3) | 1))); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord()))))),(((usqInt)numStack << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(MethodIndex, theContext, methodFieldOrObj)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord()))))),methodFieldOrObj); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(ClosureIndex, theContext, closureOrNil)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord()))))),closureOrNil); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(ReceiverIndex, theContext, rcvr)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord()))))),rcvr); + for (i = 1; i <= numArgs; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(ReceiverIndex + i, theContext, longAt((void *)((theFP + FoxCallerSavedIP) + ((numArgs - (i - 1)) * BytesPerWord))))); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))),longAt((void *)((theFP + FoxCallerSavedIP) + ((numArgs - (i - 1)) * BytesPerWord)))); + } + for (i = (numArgs + 1); i <= numStack; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(ReceiverIndex + i, theContext, GIV(nilObj))); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))),GIV(nilObj)); + } + assert(frameHasContext(theFP)); + assert(frameOfMarriedContext(theContext) == theFP); + assert((numStack + ReceiverIndex) < (lengthOf(theContext))); + return theContext; +} + + /* CoInterpreter>>#maxLookupNoMNUErrorCode */ +sqInt +maxLookupNoMNUErrorCode(void) +{ + return ((SelectorCannotInterpret < SelectorDoesNotUnderstand) ? SelectorDoesNotUnderstand : SelectorCannotInterpret); +} + + /* CoInterpreter>>#methodCacheAddress */ +usqInt +methodCacheAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)GIV(methodCache)); +} + + /* CoInterpreter>>#methodHasCogMethod: */ +int +methodHasCogMethod(sqInt aMethodOop) +{ + sqInt methodHeader; + + assert(isNonImmediate(aMethodOop)); + methodHeader = longAt((void *)((aMethodOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + return (!(methodHeader & (smallIntegerTag()))); +} + + /* CoInterpreter>>#methodHasMachineCodePrimitiveMethod: */ +static NoDbgRegParms sqInt +methodHasMachineCodePrimitiveMethod(sqInt methodObj) +{ + sqInt methodHeader; + + methodHeader = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + return ((/* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))), + /* isNonIntegerObject: */ + (!(methodHeader & (smallIntegerTag()))))) + && (cogMethodHasMachineCodePrim(((CogMethod *) methodHeader))); +} + + /* CoInterpreter>>#methodHasPrimitiveInPrimTracePlugin: */ +static NoDbgRegParms NeverInline sqInt +methodHasPrimitiveInPrimTracePlugin(sqInt aMethodObj) +{ + sqInt len; + sqInt lit; + usqInt numSlots; + sqInt ok; + sqInt pluginName; + + if (!(((primitiveIndexOf(aMethodObj)) == PrimNumberExternalCall) + && ((literalCountOf(aMethodObj)) > 0))) { + return 0; + } + lit = longAt((void *)((aMethodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(lit & (tagMask())))) + && (((byteAt((void *)(lit + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(lit)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(lit + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(lit - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4))) { + return 0; + } + assert(primTracePluginName); + pluginName = longAt((void *)((lit + BaseHeaderSize) + (0U << (shiftForWord())))); + if (!(/* isBytes: */ + ((!(pluginName & (tagMask())))) + && (((byteAt((void *)(pluginName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + return 0; + } + + /* begin object:equalsString: */ + len = strlen(primTracePluginName); + ok = /* object:equalsString:ofSize: */ + (/* isBytes: */ + ((!(pluginName & (tagMask())))) + && (((byteAt((void *)(pluginName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((!(((byteAt((void *)(pluginName + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((numBytesOfBytes(pluginName)) == len) + && ((strncmp(primTracePluginName, firstIndexableField(pluginName), len)) == 0))); + return ok; +} + + /* CoInterpreter>>#methodHasTooManyLiterals: */ +static NoDbgRegParms sqInt +methodHasTooManyLiterals(sqInt methodObj) +{ + return !(methodWithHeaderShouldBeCogged(methodHeaderOf(methodObj))); +} + + +/* Note: We now have 10 bits of primitive index, but they are in two places + for temporary backward compatibility. The time to unpack is negligible, + since the derived primitive function pointer is stored in the method + cache. */ + + /* CoInterpreter>>#methodHeaderHasPrimitive: */ +#if VMInvestigations +int +methodHeaderHasPrimitive(sqInt methodHeader) +{ + return ((methodHeader & AlternateHeaderHasPrimFlag) != 0); +} +#endif /* VMInvestigations */ + + /* CoInterpreter>>#methodNeedsLargeContext: */ +int +methodNeedsLargeContext(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + sqInt methodHeader; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin methodHeaderIndicatesLargeFrame: */ + return (methodHeader & LargeContextBit) != 0; +} + + /* CoInterpreter>>#methodShouldBeCogged: */ +sqInt +methodShouldBeCogged(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + sqInt methodHeader; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + return 1; + } + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(aMethodObj); + } + return 0; +} + + +/* At the moment jit any method with less than N literals, where N defaults + to 60. + See e.g. SimpleStackBasedCogit class>>initialize. + In my dev image eem 2/22/2009 13:39 + (30 to: 100 by: 5) collect: + [:n| n -> (SystemNavigation default allSelect: [:m| m numLiterals > n]) + size] #(30->1681 35->1150 40->765 45->523 50->389 55->289 60->206 + 65->151 70->124 75->99 80->73 85->63 90->54 95->42 100->38). + And running the CogVMSimulator with flagging of interpreted methods turned + on reveals + the following sizes of interpreted methods. + | sizes | + sizes := Bag new. + SystemNavigation default allSelect: [:m| m flag ifTrue: [sizes add: m + numLiterals]. false]. + sizes sortedElements asArray + #( 40->4 41->1 42->2 44->1 45->3 46->1 47->2 48->1 + 50->2 51->1 53->1 55->1 56->1 + 87->1 108->1 171->1) + literalCountOfHeader: does not include the header word. */ + + /* CoInterpreter>>#methodWithHeaderShouldBeCogged: */ +static NoDbgRegParms int +methodWithHeaderShouldBeCogged(sqInt methodHeader) +{ + return ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile; +} + + +/* Answer the Cog method for a machine code frame. This may be + either a full CogMethod or merely a CogBlockMethod rump header. */ + + /* CoInterpreter>>#mframeCogMethod: */ +static NoDbgRegParms CogBlockMethod * +mframeCogMethod(char *theFP) +{ + return ((CogBlockMethod *) ((longAt(theFP + FoxMethod)) & MFMethodMask)); +} + + /* CoInterpreter>>#mframeHomeMethodExport */ +CogMethod * +mframeHomeMethodExport(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt methodField; + + /* begin mframeHomeMethod: */ + methodField = longAt(GIV(framePointer) + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + return /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + } + return ((CogMethod *) (methodField & MFMethodMask)); +} + + +/* Answer the home method for a machine code frame. From a block frame we + find the home method through the block's homeOffset field which is the + delta to it. + In both cases we need to strip the isBlock and isContext flags from the + method field. */ + + /* CoInterpreter>>#mframeHomeMethod: */ +static NoDbgRegParms CogMethod * +mframeHomeMethod(char *theFP) +{ + sqInt methodField; + + methodField = longAt(theFP + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + return /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + } + return ((CogMethod *) (methodField & MFMethodMask)); +} + + +/* */ + + /* CoInterpreter>>#mframeIsBlockActivation: */ +static NoDbgRegParms int +mframeIsBlockActivation(char *theFP) +{ + return ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0; +} + + /* CoInterpreter>>#mframeReceiver: */ +static NoDbgRegParms sqInt +mframeReceiver(char *theFP) +{ + return longAt(theFP + FoxMFReceiver); +} + + +/* Traverse all stack pages looking for non-zero bytes in the headroom part + of each page. + Answer the minimum size of unused headroom (zero bytes) in the pages. This + is for + checking that there is enough headroom allocated in stack pages. */ + + /* CoInterpreter>>#minimumUnusedHeadroom */ +static NeverInline sqInt +minimumUnusedHeadroom(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt minUnused; + char *p; + StackPage *page; + sqInt unused; + + if (!(GIV(pages))) { + return -1; + } + minUnused = (((stackPageAt(0))->baseAddress)) - (((stackPageAt(0))->lastAddress)); + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + page = stackPageAtpages(i, GIV(pages)); + p = (page->lastAddress); + do { + p += BytesPerWord; + } while(((longAt(p)) == 0) + && (p <= ((page->baseAddress)))); + unused = (p - BytesPerWord) - ((page->lastAddress)); + if (unused < minUnused) { + minUnused = unused; + } + } + return minUnused; +} + + +/* Lookup the doesNotUnderstand: selector in the class of the argument rcvr. + Answer either the matching method (cogged if appropriate), or nil, if not + found. + */ + + /* CoInterpreter>>#mnuMethodOrNilFor: */ +static NoDbgRegParms sqInt +mnuMethodOrNilFor(sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentClass; + sqInt dictionary; + usqInt index; + usqInt length; + sqInt mask; + sqInt methodArray; + sqInt methodHeader; + sqInt mnuMethod; + sqInt mnuSelector; + sqInt nextSelector; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt tagBits; + sqInt wrapAround; + + assert(!((isOopForwarded(rcvr)))); + currentClass = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)); + mnuSelector = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))); + while (currentClass != GIV(nilObj)) { + dictionary = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + if (dictionary == GIV(nilObj)) { + return null; + } + + /* begin lookupMethodFor:InDictionary: */ + /* begin numSlotsOf: */ + assert((classIndexOf(dictionary)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(dictionary + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(dictionary - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + mask = (length - SelectorStart) - 1; + index = SelectorStart + (mask & ((((mnuSelector & (tagMask())) != 0) + ? (mnuSelector >> 3) + : (long32At((void *)(mnuSelector + 4))) & (identityHashHalfWordMask())))); + + /* It is assumed that there are some nils in this dictionary, and search will + stop when one is encountered. However, if there are no nils, then wrapAround + will be detected the second time the loop gets to the end of the table. */ + wrapAround = 0; + while (1) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((index << (shiftForWord()))))); + if (nextSelector == GIV(nilObj)) { + mnuMethod = null; + goto l1; + } + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == mnuSelector) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOop = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(index - SelectorStart, methodArray, objOop); + } + mnuMethod = objOop; + goto l1; + } + index += 1; + if (index == length) { + if (wrapAround) { + mnuMethod = null; + goto l1; + } + wrapAround = 1; + index = SelectorStart; + } + } + mnuMethod = null; + /* end lookupMethodFor:InDictionary: */ +l1: + if (mnuMethod) { + methodHeader = longAt((void *)((mnuMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + if ((!((/* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))), + /* isNonIntegerObject: */ + (!(methodHeader & (smallIntegerTag())))))) + && (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile)) { + cogselector(mnuMethod, mnuSelector); + } + return mnuMethod; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOopSqInt = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOopSqInt)); + if ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOopSqInt); + } + currentClass = objOopSqInt; + } + return null; +} + + +/* Move frames from the hot end of oldPage through to theFP to newPage. + This has the effect of making theFP a base frame which can be stored into. + Answer theFP's new location. */ + + /* CoInterpreter>>#moveFramesIn:through:toPage: */ +static NoDbgRegParms char * +moveFramesInthroughtoPage(StackPage *oldPage, char *theFP, StackPage *newPage) +{ + char *callerFP; + usqInt callerIP; + sqInt delta; + char *fpInNewPage; + char *newFP; + char *newSP; + char *offsetCallerFP; + char *source; + sqInt stackedReceiverOffset; + sqInt theContext; + char *theFP1; + char *theSP; + sqInt valuePointer; + sqInt valuePointerSqInt; + + + /* A base frame must have a context for cannotReturn: processing. */ + assert(!(isBaseFrame(theFP))); + assert(validStackPageBaseFrames()); + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + assert(frameHasContext(callerFP)); + assert(isContext(frameContext(callerFP))); + theSP = theFP + (((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? FoxMFReceiver + : FoxIFReceiver)); + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + theContext = longAt(theFP + FoxThisContext); + goto l1; + } + theContext = marryFrameSP(theFP, theSP); + /* end ensureFrameIsMarried:SP: */ +l1: + longAtput((newSP = (newPage->baseAddress)),longAt(callerFP + FoxThisContext)); + longAtput((newSP -= BytesPerWord),theContext); + stackedReceiverOffset = (FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))); + + /* First move the data, leaving room for the caller and base frame contexts. We will fix up frame pointers later. */ + for (source = (theFP + stackedReceiverOffset); source >= ((oldPage->headSP)); source += (-BytesPerWord)) { + newSP -= BytesPerWord; + longAtput(newSP,longAt(source)); + } + + /* newSP = oldSP + delta => delta = newSP - oldSP */ + delta = newSP - ((oldPage->headSP)); + newFP = (((newPage->baseAddress)) - stackedReceiverOffset) - (2 * BytesPerWord); + theFP1 = ((oldPage->headFP)) + delta; + + /* begin setHeadFP:andSP:inPage: */ + assert(newSP < theFP1); + assert((newSP < ((newPage->baseAddress))) + && (newSP > (((newPage->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((theFP1 < ((newPage->baseAddress))) + && (theFP1 > (((newPage->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (newPage->headFP = theFP1); + (newPage->headSP = newSP); + (newPage->baseFP = newFP); + callerIP = longAt(theFP + FoxCallerSavedIP); + if ((((usqInt)callerIP)) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(callerFP))); + longAtput(callerFP + FoxIFSavedIP,callerIP); + callerIP = ceReturnToInterpreterPC(); + } + longAtput(theFP + stackedReceiverOffset,callerIP); + assert((callerFP < ((oldPage->baseAddress))) + && (callerFP > (((oldPage->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (oldPage->headFP = callerFP); + (oldPage->headSP = theFP + stackedReceiverOffset); + + /* Mark the new base frame in the new page */ + longAtput(newFP + FoxCallerSavedIP,ceBaseFrameReturnPC()); + longAtput(newFP + FoxSavedFP,0); + + /* Now relocate frame pointers, updating married contexts to refer to their moved spouse frames. */ + fpInNewPage = (newPage->headFP); + do { + offsetCallerFP = ((char *)(longAt(fpInNewPage + FoxSavedFP))); + if (offsetCallerFP) { + offsetCallerFP += delta; + } + longAtput(fpInNewPage + FoxSavedFP,oopForPointer(offsetCallerFP)); + if (/* frameHasContext: */ + ((((usqInt)(longAt(fpInNewPage + FoxMethod)))) < (startOfMemory()) + ? ((longAt(fpInNewPage + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((fpInNewPage + FoxIFrameFlags) + 2)) != 0)) { + theContext = longAt(fpInNewPage + FoxThisContext); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(fpInNewPage)) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(fpInNewPage)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, theContext, valuePointer)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),valuePointer); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(offsetCallerFP)) & (BytesPerWord - 1)) == 0); + valuePointerSqInt = (oopForPointer(offsetCallerFP)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, theContext, valuePointerSqInt)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointerSqInt); + } + fpInNewPage = offsetCallerFP; + } while(fpInNewPage != 0); + assert(validStackPageBaseFrames()); + return newFP; +} + + +/* Map the native pc theIP into a bytecode pc integer object and answer it. + See contextInstructionPointer:frame: for the explanation. */ + + /* CoInterpreter>>#mustMapMachineCodePC:context: */ +static NoDbgRegParms sqInt +mustMapMachineCodePCcontext(sqInt theIP, sqInt aOnceMarriedContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bcpc; + CogMethod *cogMethod; + CogMethod *cogMethod1; + CogBlockMethod *cogMethodForIP; + sqInt maybeClosure; + sqInt mcpc; + sqInt methodObj; + usqInt numSlots; + sqInt rawHeader; + usqInt startBcpc; + sqInt yetToCompact; + + if (theIP == HasBeenReturnedFromMCPC) { + return GIV(nilObj); + } + maybeClosure = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord())))))); + methodObj = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + if ((maybeClosure != GIV(nilObj)) + && ((/* begin isVanillaBlockClosure: */ + assert((isPointers(maybeClosure)) + && ((numSlotsOf(maybeClosure)) >= ClosureFirstCopiedValueIndex)), + ((((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1)))) { + assert((((theIP) >> 16)) < -1); + startBcpc = (((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))))) >> 3)) - 1; + } + else { + assert((((theIP) >> 16)) == -1); + startBcpc = ((literalCountOf(methodObj)) + LiteralStart) * BytesPerOop; + } + + /* begin ensureMethodIsCogged:maybeClosure: */ + rawHeader = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) rawHeader); + goto l2; + } + yetToCompact = 1; + while (1) { + if ((maybeClosure != GIV(nilObj)) + && (!((/* begin isVanillaBlockClosure: */ + assert((isPointers(maybeClosure)) + && ((numSlotsOf(maybeClosure)) >= ClosureFirstCopiedValueIndex)), + ((((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1))))) { + cogMethod1 = cogFullBlockMethodnumCopied( + methodObj, + (/* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(maybeClosure)))), + ((/* begin numSlotsOf: */ + assert((classIndexOf(maybeClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(maybeClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(maybeClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex)); + } + else { + cogMethod1 = cogselector(methodObj, GIV(nilObj)); + } + if (!((cogMethod1 == null) + && (GIV(cogCompiledCodeCompactionCalledFor) + && (yetToCompact)))) break; + yetToCompact = 0; + commenceCogCompiledCodeCompaction(); + } + if (!(asserta(cogMethod1 != null))) { + error("could not compile method that should have been compiled"); + } + cogMethod = cogMethod1; + /* end ensureMethodIsCogged:maybeClosure: */ +l2: + + /* begin bytecodePCFor:cogMethod:startBcpc: */ + assert(theIP < 0); + if ((((theIP) >> 16)) < -1) { + cogMethodForIP = ((CogBlockMethod *) ((((sqInt)cogMethod)) - ((((theIP) >> 16)) * (blockAlignment())))); + assert(isCMBlock(cogMethodForIP)); + assert((cmHomeMethod(cogMethodForIP)) == cogMethod); + mcpc = (((sqInt)cogMethodForIP)) - (((short)theIP)); + } + else { + cogMethodForIP = ((CogBlockMethod *) cogMethod); + assert(isCMMethodEtAl(cogMethodForIP)); + mcpc = (((sqInt)cogMethod)) - theIP; + + /* map any pcs in primitive code (i.e. return addresses for interpreter primitive calls) to the initial pc */ + if ((((usqInt)mcpc)) < ((cogMethod->stackCheckOffset))) { + bcpc = startBcpc; + goto l1; + } + } + + /* See contextInstructionPointer:frame: */ + assert(((mcpc >= (((sqInt)cogMethod))) && (mcpc <= ((((sqInt)cogMethod)) + ((cogMethod->blockSize)))))); + bcpc = bytecodePCForstartBcpcin(mcpc, startBcpc, cogMethodForIP); + /* end bytecodePCFor:cogMethod:startBcpc: */ +l1: + assert(bcpc >= (startPCOfMethod(methodObj))); + + /* If there's a CallPrimitive we need to skip it. */ + if ((bcpc == startBcpc) + && ((maybeClosure == GIV(nilObj)) + && (((((cogMethod->methodHeader)) & AlternateHeaderHasPrimFlag) != 0)))) { + bcpc += 3 /* sizeOfCallPrimitiveBytecode: */; + } + return (((usqInt)(bcpc + 1) << 3) | 1); +} + + /* CoInterpreter>>#newMethodAddress */ +usqInt +newMethodAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(newMethod)))); +} + + +/* N.B. nextProfileTick is 64-bits */ + + /* CoInterpreter>>#nextProfileTickAddress */ +usqInt +nextProfileTickAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(nextProfileTick)))); +} + + /* CoInterpreter>>#noAssertHeaderOf: */ +sqInt +noAssertHeaderOf(sqInt methodPointer) +{ + sqInt methodHeader; + + methodHeader = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + return ((!(methodHeader & (smallIntegerTag()))) + ? ((((CogMethod *) methodHeader))->methodHeader) + : methodHeader); +} + + +/* integerValue is interpreted as POSITIVE, e.g. as the result of Bitmap>at:. + N.B. Returning in each arm separately enables Slang inlining. + /Don't/ return the ifTrue:ifFalse: unless Slang inlining of conditionals + is fixed. */ + + /* CoInterpreter>>#positive32BitIntegerFor: */ +sqInt +positive32BitIntegerFor(unsigned int integerValue) +{ + return (((usqInt)((((usqInt)integerValue)) & 0xFFFFFFFFU) << 3) | 1); +} + + +/* Convert the given object into an integer value. + The object may be either a positive SmallInteger or a four-byte + LargePositiveInteger. + */ + + /* CoInterpreter>>#positive32BitValueOf: */ +unsigned int +positive32BitValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt value64; + + if ((((oop) & 7) == 1)) { + value64 = (oop >> 3); + if ((value64 < 0) + || ((((unsigned int) value64)) != value64)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + value64 = 0; + } + return value64; + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; +} + + +/* Answer a Large Positive Integer object for the given integer value. N.B. + will *not* cause a GC. */ + + /* CoInterpreter>>#positive64BitIntegerFor: */ +sqInt +positive64BitIntegerFor(usqLong integerValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newLargeInteger; + usqInt newObj; + usqInt numBytes; + sqInt numSlots; + sqInt objFormat; + sqInt sz; + + if (integerValue <= (MaxSmallInteger)) { + return ((integerValue << 3) | 1); + } + sz = 8; + objFormat = (firstByteFormat()) + ((8 - sz) & (BytesPerWord - 1)); + numSlots = 8 / BytesPerOop; + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((numSlots >= 0) + && ((knownClassAtIndex(ClassLargePositiveIntegerCompactIndex)) != GIV(nilObj))); + assert(((objFormat < (firstByteFormat()) + ? objFormat + : objFormat & (byteFormatMask()))) == (instSpecOfClass(knownClassAtIndex(ClassLargePositiveIntegerCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newLargeInteger = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + ClassLargePositiveIntegerCompactIndex); + GIV(freeStart) += numBytes; + newLargeInteger = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((newLargeInteger + BaseHeaderSize)),SQ_SWAP_8_BYTES_IF_BIGENDIAN(integerValue)); + return newLargeInteger; +} + + +/* Convert the given object into an integer value. + The object may be either a positive SmallInteger or up to an eight-byte + LargePositiveInteger. + */ + + /* CoInterpreter>>#positive64BitValueOf: */ +usqLong +positive64BitValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + sqInt fmt; + usqInt numSlots; + int ok; + sqInt smallIntValue; + sqInt sz; + + if ((((oop) & 7) == 1)) { + smallIntValue = (oop >> 3); + if (smallIntValue < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return smallIntValue; + } + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (!ok) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + sz = ((((/* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (sz > (sizeof(sqLong))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + if (sz > 4) { + return SQ_SWAP_8_BYTES_IF_BIGENDIAN((long64At((void *)((oop + BaseHeaderSize))))); + } + return ((unsigned int) (SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((oop + BaseHeaderSize))))))); +} + + +/* Signal the gc semaphore, and inform the display subsystem if the display + bitmap has moved. + */ +/* Attempt to shrink free memory, signal the gc semaphore and let the Cogit + do its post GC thang + */ + + /* CoInterpreter>>#postGCAction: */ +static NoDbgRegParms void +postGCAction(sqInt gcModeArg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(gcModeArg == GIV(gcMode)); + signalSemaphoreWithIndex(GIV(gcSemaphoreIndex)); + postGCUpdateDisplayBits(); + cogitPostGCAction(gcModeArg); + + /* begin nilUncoggableMethods */ + GIV(lastCoggableInterpretedBlockMethod) = (GIV(lastUncoggableInterpretedBlockMethod) = null); + GIV(gcMode) = 0; +} + + +/* Need to write back the frame pointers unless all pages are free (as in + snapshot) + */ +/* Need to write back the frame pointers unless all pages are free (as in + snapshot). Need to set gcMode var (to avoid passing the flag through a lot + of the updating code) */ + + /* CoInterpreter>>#preGCAction: */ +static NoDbgRegParms void +preGCAction(sqInt gcModeArg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aTraceCode; + int traceType; + + if (GIV(stackPage)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + if (recordPrimTrace()) { + aTraceCode = (gcModeArg == GCModeFull + ? TraceFullGC + : TraceIncrementalGC); + + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)aTraceCode << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + if (gcModeArg == GCModeFull) { + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + } + GIV(gcMode) = gcModeArg; + if (recordEventTrace()) { + traceType = (gcModeArg == GCModeFull + ? TraceFullGC + : TraceIncrementalGC); + + /* begin recordTrace: */ + /* begin recordTrace:source:thing:extra: */ + if (TraceLog) { + GIV(traceLog)[GIV(traceLogIndex)] = ((((((usqInt)(traceType) << TraceTypeShift))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = 0; + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + } +} + + /* CoInterpreter>>#primFailCodeAddress */ +usqInt +primFailCodeAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(primFailCode)))); +} + + +/* Return from a become primitive after invoking + become:with:twoWay:copyHash:. If succeeding and the top frame has been + divorced then continue in the interpreter. + See the comment in primitiveArrayBecome. */ + + /* CoInterpreter>>#primitiveBecomeReturn: */ +static NoDbgRegParms void +primitiveBecomeReturn(sqInt ec) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContextOrNil; + sqInt activeProc; + sqInt objOop; + + if (ec) { + /* primitiveFailFor: */ + GIV(primFailCode) = ec; + } + else { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + activeContextOrNil = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if (!GIV(framePointer)) { + assert(isContext(activeContextOrNil)); + + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContextOrNil); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(activeProc)) + && (!(isForwarded(activeProc)))); + assert(validStorePointerUncheckedArgs(SuspendedContextIndex, activeProc, GIV(nilObj))); + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* pop bogus machine-code instructionPointer and any primitive arguments */ + + /* begin pop: */ + GIV(stackPointer) += (GIV(argumentCount) + 1) * BytesPerWord; + ceInvokeInterpret(); + } + + /* If the top frame was divorced then continue in the interpreter. + If not, check where we are and continue... */ + if (!(activeContextOrNil == GIV(nilObj))) { + /* divorcing should not have disturbed the topFrame if it wasn;t divorced */ + assert(isContext(activeContextOrNil)); + assert(GIV(framePointer) == (frameOfMarriedContext(activeContextOrNil))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(activeProc)) + && (!(isForwarded(activeProc)))); + assert(validStorePointerUncheckedArgs(SuspendedContextIndex, activeProc, GIV(nilObj))); + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),GIV(nilObj)); + } + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } +} + + +/* receiver, args, then method are on top of stack. Execute method against + receiver and args. + Set primitiveFunctionPointer because no cache lookup has been done for the + method, and + hence primitiveFunctionPointer is stale. */ + + /* CoInterpreter>>#primitiveExecuteMethod */ +static void +primitiveExecuteMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodArgument; + sqInt methodHeader; + sqInt primitiveIndex; + sqInt top; + + methodArgument = longAt(GIV(stackPointer)); + if (!(/* isOopCompiledMethod: */ + ((!(methodArgument & (tagMask())))) + && (((byteAt((void *)(methodArgument + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (!((GIV(argumentCount) - 1) == (argumentCountOf(methodArgument)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(newMethod) = top; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]))); + GIV(argumentCount) -= 1; + executeNewMethodJitting(); + + /* Recursive xeq affects primErrorCode */ + + /* begin initPrimCall */ + GIV(primFailCode) = 0; +} + + +/* receiver, argsArray, then method are on top of stack. Execute method + against receiver and args. Allow for up to two extra arguments (e.g. for + mirror primitives). + Set primitiveFunctionPointer because no cache lookup has been done for the + method, and hence primitiveFunctionPointer is stale. */ + + /* CoInterpreter>>#primitiveExecuteMethodArgsArray */ +static void +primitiveExecuteMethodArgsArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt argCnt; + sqInt argumentArray; + usqInt firstBytecode; + sqInt header; + sqInt i; + sqInt methodArgument; + sqInt methodHeader; + usqInt numSlots; + sqInt primitiveIndex; + sqInt rcvr; + + methodArgument = longAt(GIV(stackPointer)); + argumentArray = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((/* isOopCompiledMethod: */ + ((!(methodArgument & (tagMask())))) + && (((byteAt((void *)(methodArgument + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* begin argumentCountOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodArgument)); + header = longAt((void *)((methodArgument + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + argCnt = (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; + if (!(argCnt == ((/* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + if (GIV(argumentCount) > 2) { + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if ((GIV(argumentCount) > 4) + || (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord),rcvr); + } + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + for (i = 0; i < argCnt; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))))); + } + GIV(newMethod) = methodArgument; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]))); + GIV(argumentCount) = argCnt; + executeNewMethodJitting(); + + /* Recursive xeq affects primErrorCode */ + + /* begin initPrimCall */ + GIV(primFailCode) = 0; +} + + +/* Answer any special requirements of the given primitive */ + + /* CoInterpreter>>#primitivePropertyFlags:primIndex: */ +sqInt +primitivePropertyFlagsprimIndex(sqInt methodObj, sqInt primIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int baseFlags; + + /* begin primitivePropertyFlagsForSpur:primIndex: */ + baseFlags = 0; + if (GIV(profileSemaphore)) { + if (isRealNonProfilingPrimitiveMethodprimIndex(methodObj, primIndex)) { + baseFlags = PrimCallCollectsProfileSamples + PrimCallNeedsNewMethod; + } + } +# if LRPCheck + if (GIV(longRunningPrimitiveCheckSemaphore)) { + baseFlags = baseFlags | PrimCallNeedsNewMethod; + } +# endif + + + /* Because one primitive number is used for all three vmParameter primtiives + the metadata can't be attached to the method implementation. */ + if ((primIndex == PrimNumberVMParameter) + && ((argumentCountOf(methodObj)) == 1)) { + return (baseFlags + PrimCallOnSmalltalkStack) + PrimCallOnSmalltalkStackAlign2x; + } + + /* vmParameterAt: */ + return baseFlags | ((primitiveMetadataTable[primIndex]) & SpurPrimitiveFlagsMask); +} + + /* CoInterpreter>>#primTraceLogAddress */ +void * +primTraceLogAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(primTraceLog); +} + + +/* N.B. primTraceLogIndex is 8-bits */ + + /* CoInterpreter>>#primTraceLogIndexAddress */ +usqInt +primTraceLogIndexAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(primTraceLogIndex)))); +} + + /* CoInterpreter>>#printCogMethod: */ +void +printCogMethod(CogMethod *cogMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + usqInt firstBytecode; + sqInt primitive; + sqInt s; + + address = ((sqInt)cogMethod); + printHex(address); + print(" <-> "); + printHex(address + ((cogMethod->blockSize))); + if (((cogMethod->cmType)) >= CMMethod) { + print(": method: "); + printHex((cogMethod->methodObject)); + + /* primitiveIndexOfMethod:header: */ + if (((((cogMethod->methodHeader)) & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (((cogMethod->methodObject)) + ((LiteralStart + (((((cogMethod->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitive = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitive = 0; + } + if (primitive) { + print(" prim "); + printNum(primitive); + } + if (addressCouldBeObj((cogMethod->methodObject))) { + if ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) { + print(" [full]"); + } + else { + if (addressCouldBeObj(methodClassOf((cogMethod->methodObject)))) { + /* begin space */ + printChar(' '); + printNameOfClasscount(methodClassOf((cogMethod->methodObject)), 2); + } + } + } + } + if (((cogMethod->cmType)) == CMBlock) { + print(": block home: "); + printHex(((usqInt)(/* cmHomeMethod */ + ((((((CogBlockMethod *) cogMethod))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) cogMethod))) + : (assert(CMBlock == ((((CogBlockMethod *) cogMethod))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) cogMethod)))) - (((((CogBlockMethod *) cogMethod))->homeOffset))))))))); + } + if (((cogMethod->cmType)) == CMClosedPIC) { + print(": Closed PIC N: "); + printHex((cogMethod->cPICNumCases)); + } + if (((cogMethod->cmType)) == CMOpenPIC) { + print(": Open PIC "); + } + print(" selector: "); + printHex((cogMethod->selector)); + if (((cogMethod->selector)) == GIV(nilObj)) { + if ((((cogMethod->cmType)) >= CMMethod) + && (((s = maybeSelectorOfMethod((cogMethod->methodObject)))))) { + print(" (nil: "); + printStringOf(s); + print(")"); + } + else { + print(" (nil)"); + } + } + else { + /* begin space */ + printChar(' '); + printStringOf((cogMethod->selector)); + } + cr(); +} + + /* CoInterpreter>>#printFrameFlagsForFP: */ +static NoDbgRegParms void +printFrameFlagsForFP(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *address; + usqInt it; + + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + address = theFP + FoxMethod; + it = (longAt(address)) & 7; + } + else { + address = theFP + FoxIFrameFlags; + it = longAt(address); + } + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%3sfrm flags: %p", + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? "mc" + : "int"), + ((void *)it)); + if (it) { + fprintf(GIV(transcript), + "=%d", + ((int) it)); + } + fprintf(GIV(transcript), + " numArgs: %d %sContext %sBlock\n", + ((int) (/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))), + (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0) + ? "is" + : "no"), + (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0) + ? "is" + : "not")); +} + + /* CoInterpreter>>#printFrameMethodFor: */ +static NoDbgRegParms void +printFrameMethodFor(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *address; + sqInt it; + + address = theFP + FoxMethod; + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + " method: %16p\t" /* pst: */, + ((void *)it)); + if (((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && (((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0)) { + fprintf(GIV(transcript), + "hm: %p\t", + ((void *)(mframeHomeMethod(theFP)))); + } + shortPrintOop(/* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod))); +} + + /* CoInterpreter>>#printFrameThing:at:extra: */ +static NoDbgRegParms void +printFrameThingatextra(char *name, char *address, sqInt extraValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt it; + + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%12s: %16p" /* pst: */, + name, + ((void *)it)); + framePrintDescription(it); + fprintf(GIV(transcript), + " %" PRIdSQINT "\n", + extraValue); +} + + +/* useful for VM debugging */ + + /* CoInterpreter>>#printFrame:WithSP: */ +sqInt +printFrameWithSP(char *theFP, char *theSP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *addr; + CogBlockMethod *cogMethod; + CogMethod *homeMethod; + usqInt i; + usqInt index; + usqInt numArgs; + usqInt numSlots; + usqInt numTemps; + char *rcvrAddress; + sqInt rcvrOrClosure; + usqInt theMethod; + sqInt theMethodEnd; + char *toDoLimit; + sqInt topThing; + + if (!(/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + fprintf(GIV(transcript), + "%p is not in the stack zone?!\n", + theFP); + return null; + } + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + cogMethod = mframeCogMethod(theFP); + homeMethod = mframeHomeMethod(theFP); + theMethod = ((usqInt)homeMethod); + theMethodEnd = (((sqInt)homeMethod)) + ((homeMethod->blockSize)); + numArgs = (cogMethod->cmNumArgs); + numTemps = (((usqInt)(((homeMethod->methodHeader)))) >> MethodHeaderTempCountShift) & 0x3F; + } + else { + theMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + theMethodEnd = theMethod + (((((/* begin numSlotsOf: */ + assert((classIndexOf(theMethod)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(theMethod + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(theMethod - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) + BaseHeaderSize); + numArgs = byteAt((theFP + FoxIFrameFlags) + 1); + numTemps = tempCountOf(theMethod); + } + if (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0)) { + /* No BlockLocalTempCounter in the Cogit's C code, so quick hack is to use numCopied + numArgs */ + rcvrOrClosure = longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))); + if (((!(rcvrOrClosure & (tagMask())))) + && ((addressCouldBeObj(rcvrOrClosure)) + && ((fetchClassOfNonImm(rcvrOrClosure)) == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassBlockClosure) << (shiftForWord())))))))))) { + numTemps = numArgs + (stSizeOf(rcvrOrClosure)); + } + else { + numTemps = numArgs; + } + } + shortPrintFrame(theFP); + rcvrAddress = theFP + ((FoxCallerSavedIP + BytesPerWord) + ((numArgs << (shiftForWord())))); + if (longAt(theFP + FoxSavedFP)) { + } + else { + rcvrAddress + (2 * BytesPerWord); + + /* empty frameRange:to: */ + printFrameOopat("(caller ctxt", rcvrAddress + (2 * BytesPerWord)); + printFrameOopat("(saved ctxt", rcvrAddress + (1 * BytesPerWord)); + } + printFrameOopat("rcvr/clsr", rcvrAddress); + for (i = numArgs; i >= 1; i += -1) { + printFrameOopindexat("arg", numArgs - i, (theFP + FoxCallerSavedIP) + (i * BytesPerWord)); + } + printFrameThingatextraString("caller ip", theFP + FoxCallerSavedIP, ((longAt(theFP + FoxCallerSavedIP)) == (ceReturnToInterpreterPC()) + ? "ceReturnToInterpreter" + : 0)); + + /* begin printFrameThing:at: */ + printFrameThingatextraString("saved fp", theFP + FoxSavedFP, ((char *)null)); + printFrameMethodFor(theFP); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + printFrameFlagsForFP(theFP); + } + printFrameOopat("context", theFP + FoxThisContext); + if (!((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()))) { + printFrameFlagsForFP(theFP); + } + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + rcvrAddress = theFP + FoxMFReceiver; + } + else { + printFrameThingatextra("saved ip", theFP + FoxIFSavedIP, (((usqInt)(longAt(theFP + FoxIFSavedIP))) + ? (((((usqInt)(longAt(theFP + FoxIFSavedIP)))) - theMethod) + 2) - BaseHeaderSize + : 0)); + rcvrAddress = theFP + FoxIFReceiver; + } + printFrameOopat("receiver", rcvrAddress); + topThing = longAt(theSP); + if ((oopisGreaterThanOrEqualToandLessThan(topThing, theMethod, theMethodEnd)) + || ((topThing == (ceReturnToInterpreterPC())) + || ((topThing == (ceBaseFrameReturnPC())) + || (topThing == (ceCannotResumePC()))))) { + toDoLimit = theSP + BytesPerWord; + for (addr = (rcvrAddress - BytesPerWord); addr >= toDoLimit; addr += (-BytesPerWord)) { + index = ((rcvrAddress - addr) / BytesPerWord) + numArgs; + if (index <= numTemps) { + printFrameOopindexat("temp", index - 1, addr); + } + else { + printFrameOopat((/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0) + ? "temp/stck" + : "stck"), addr); + } + } + printFrameThingatextra("frame ip", theSP, (topThing > theMethod + ? ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? topThing - theMethod + : ((topThing - theMethod) + 2) - BaseHeaderSize) + : topThing)); + } + else { + for (addr = (rcvrAddress - BytesPerWord); addr >= theSP; addr += (-BytesPerWord)) { + index = ((rcvrAddress - addr) / BytesPerWord) + numArgs; + if (index <= numTemps) { + printFrameOopindexat("temp", index - 1, addr); + } + else { + printFrameOopat((/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0) + ? "temp/stck" + : "stck"), addr); + } + } + } + return 0; +} + + +/* useful for VM debugging */ + + /* CoInterpreter>>#printMethodCacheFor: */ +void +printMethodCacheFor(sqInt thing) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqIntptr_t c; + int i; + sqIntptr_t m; + sqInt n; + sqIntptr_t p; + sqIntptr_t s; + + n = 0; + for (i = 0; i < MethodCacheSize; i += MethodCacheEntrySize) { + s = GIV(methodCache)[i + MethodCacheSelector]; + c = GIV(methodCache)[i + MethodCacheClass]; + m = GIV(methodCache)[i + MethodCacheMethod]; + p = GIV(methodCache)[i + MethodCachePrimFunction]; + if (((thing == -1) + || ((s == thing) + || ((c == thing) + || ((p == thing) + || ((m == thing) + || ((addressCouldBeObj(m)) + && ((/* maybeMethodHasCogMethod: */ + ((!(m & (tagMask())))) + && ((((byteAt((void *)(m + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((m + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) + && ((((sqInt)(cogMethodOf(m)))) == thing)))))))) + && ((/* addressCouldBeOop: */ + (((s & (tagMask())) != 0)) + || (addressCouldBeObj(s))) + && ((c != 0) + && ((addressCouldBeClassObj(c)) + || (addressCouldBeClassObj(classForClassTag(c))))))) { + n += 1; + fprintf(GIV(transcript), + "%d %x\n\t", + ((int) i), + ((int) i)); + if (((byteAt((void *)(s + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())) { + fprintf(GIV(transcript), + "%p %.*s\n", + ((void *)s), + ((int) (numBytesOfBytes(s))), + ((char *) (firstIndexableField(s)))); + } + else { + shortPrintOop(s); + } + + /* begin tab */ + printChar('\t'); + if (addressCouldBeClassObj(c)) { + shortPrintOop(c); + } + else { + printNum(c); + + /* begin space */ + printChar(' '); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)c)); + printChar(' '); + shortPrintOop(classForClassTag(c)); + } + + /* begin tab */ + printChar('\t'); + shortPrintOop(m); + + /* begin tab */ + printChar('\t'); + if (p > 0x400) { + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)p)); + } + else { + printNum(p); + } + cr(); + } + } + if (n > 1) { + printNum(n); + cr(); + } +} + + /* CoInterpreter>>#quickPrimitiveConstantFor: */ +sqInt +quickPrimitiveConstantFor(sqInt aQuickPrimitiveIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + switch (aQuickPrimitiveIndex) { + case 0x101: + return GIV(trueObj); + + case 258: + return GIV(falseObj); + + case 259: + return GIV(nilObj); + + case 260: + return ConstMinusOne; + + case 261: + return ConstZero; + + case 262: + return ConstOne; + + case 263: + return ConstTwo; + + default: + error("Case not found and no otherwise clause"); + } + return 0; +} + + /* CoInterpreter>>#quickPrimitiveGeneratorFor: */ +sqInt +(*quickPrimitiveGeneratorFor(sqInt aQuickPrimitiveIndex))(void) +{ + switch (aQuickPrimitiveIndex) { + case 0x100: + return genQuickReturnSelf; + + case 0x101: + case 258: + case 259: + case 260: + case 261: + case 262: + case 263: + return genQuickReturnConst; + + default: + return genQuickReturnInstVar; + } + return 0; +} + + /* CoInterpreter>>#quickPrimitiveInstVarIndexFor: */ +sqInt +quickPrimitiveInstVarIndexFor(sqInt primIndex) +{ + return primIndex - 264; +} + + /* CoInterpreter>>#rawHeaderOf: */ +sqInt +rawHeaderOf(sqInt methodPointer) +{ + return longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); +} + + +/* Since methods may be updated while forwarding during become, make the + assert accomodate this. + */ + + /* CoInterpreter>>#rawHeaderOf:put: */ +void +rawHeaderOfput(sqInt methodOop, sqInt cogMethodOrMethodHeader) +{ + assert(isCompiledMethodHeader(headerWhileForwardingOf(methodOop))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(methodOop)) + && (!(isForwarded(methodOop)))); + assert(validStorePointerUncheckedArgs(HeaderIndex, methodOop, cogMethodOrMethodHeader)); + longAtput((void *)((methodOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))),cogMethodOrMethodHeader); +} + + +/* Read an image from the given file stream, allocating an amount of memory + to its object heap. + + V3: desiredHeapSize is the total size of the heap. Fail if the image has + an unknown format or + requires more than the specified amount of memory. + + Spur: desiredHeapSize is ignored; this routine will attempt to provide at + least extraVMMemory's + ammount of free space after the image is loaded, taking any free space in + the image into account. + extraVMMemory is stored in the image header and is accessible as + vmParameterAt: 23. If + extraVMMemory is 0, the value defaults to the default grow headroom. Fail + if the image has an + unknown format or if sufficient memory cannot be allocated. + + Details: This method detects when the image was stored on a machine with + the opposite byte + ordering from this machine and swaps the bytes automatically. Furthermore, + it allows the header + information to start 512 bytes into the file, since some file transfer + programs for the Macintosh + apparently prepend a Mac-specific header of this size. Note that this same + 512 bytes of prefix + area could also be used to store an exec command on Unix systems, allowing + one to launch + Smalltalk by invoking the image name as a command. */ + + /* CoInterpreter>>#readImageFromFile:HeapSize:StartingAt: */ +size_t +readImageFromFileHeapSizeStartingAt(sqImageFile f, usqInt desiredHeapSize, squeakFileOffsetType imageOffset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt allocationReserve; + sqInt anObject; + sqInt bit; + sqInt bytes; + sqInt bytesRead; + sqInt bytesToShift; + sqInt classArrayClass; + sqInt classArrayObj; + sqInt cogCodeBase; + size_t dataSize; + sqInt firstSegSize; + sqInt freeOldSpaceInImage; + usqInt hdrCogCodeSize; + sqInt hdrEdenBytes; + usqInt hdrMaxExtSemTabSize; + usqInt hdrNumStackPages; + sqInt headerSize; + squeakFileOffsetType headerStart; + sqInt headroom; + sqInt headroomSqInt; + usqInt heapSize; + usqInt heapSizeUsqInt; + sqInt i; + usqInt mem; + usqInt minimumMemory; + usqInt numSlots; + sqInt objOop; + sqInt oop; + sqInt rawVersion; + sqInt seed; + int swapBytes; + sqInt toDoLimit; + sqInt toDoLimit1; + sqInt version; + int w; + unsigned short w1; + sqInt wSqInt; + + rawVersion = 0; + + /* stdout is not available at compile time. this is the earliest available point. */ + GIV(transcript) = stdout; + + /* guess Metaclass instSize */ + GIV(metaclassNumSlots) = 6; + + /* guess (Class instVarIndexFor: 'name' ifAbsent: []) - 1 */ + GIV(classNameIndex) = 6; + version = checkImageVersionFromstartingAtassignRawVersion(f, imageOffset, (&rawVersion)); + if (!version) { + /* begin bailOutOfImageLoad: */ + fprintf(stderr, + "This ver %d vm cannot read ver %" PRIdSQINT " image file %s\n", + ((int) 68021 /* imageFormatVersion */), + rawVersion, + getImageName()); + } + swapBytes = rawVersion != version; + GIV(multipleBytecodeSetsActive) = ((version & MultipleBytecodeSetsBitmask) != 0); + + /* record header start position */ + headerStart = (sqImageFilePosition(f)) - 4; + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + headerSize = (swapBytes + ? SQ_SWAP_4_BYTES(w) + : w); + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + dataSize = ((sqInt) ((swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt))); + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + GIV(oldImageBaseAddress) = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + anObject = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin specialObjectsOop: */ + GIV(specialObjectsOop) = anObject; + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + seed = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin lastHash: */ + if (!((GIV(lastHash) = seed & (identityHashHalfWordMask())))) { + while (((GIV(lastHash) = (((usqInt) (ioUTCMicrosecondsNow()))) & (identityHashHalfWordMask()))) == 0) { + } + } + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + GIV(savedWindowSize) = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + imageHeaderFlags = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + extraVMMemory = (swapBytes + ? SQ_SWAP_4_BYTES(w) + : w); + + /* begin getShortFromFile:swap: */ + w1 = 0; + sqImageFileRead((&w1), sizeof(unsigned short), 1, f); + hdrNumStackPages = (swapBytes + ? ((((usqInt)(w1)) >> 8) & 0xFF) | (((w1 & 0xFF) << 8)) + : w1); + + /* 4 stack pages is small. Should be able to run with as few as + three. 4 should be comfortable but slow. 8 is a reasonable + default. Can be changed via vmParameterAt: 43 put: n. + Can be set as a preference (Info.plist, VM.ini, command line etc). + If desiredNumStackPages is already non-zero then it has been + set as a preference. Ignore (but preserve) the header's default. */ + GIV(numStackPages) = (desiredNumStackPages + ? desiredNumStackPages + : (hdrNumStackPages + ? hdrNumStackPages + : defaultNumStackPages())); + desiredNumStackPages = hdrNumStackPages; + + /* This slot holds the size of the native method zone in 1k units. (pad to word boundary). */ + hdrCogCodeSize = (getShortFromFileswap(f, swapBytes)) * 0x400; + + /* set for vmParameter 47 */ + GIV(cogCodeSize) = (desiredCogCodeSize + ? desiredCogCodeSize + : (hdrCogCodeSize + ? (desiredCogCodeSize = hdrCogCodeSize) + : defaultCogCodeSize())); + if (GIV(cogCodeSize) > (maxCogCodeSize())) { + GIV(cogCodeSize) = maxCogCodeSize(); + } + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + hdrEdenBytes = (swapBytes + ? SQ_SWAP_4_BYTES(w) + : w); + bytes = (desiredEdenBytes + ? desiredEdenBytes + : (hdrEdenBytes + ? hdrEdenBytes + : defaultEdenBytes())); + + /* begin edenBytes: */ + GIV(edenBytes) = bytes; + desiredEdenBytes = hdrEdenBytes; + + /* begin getShortFromFile:swap: */ + w1 = 0; + sqImageFileRead((&w1), sizeof(unsigned short), 1, f); + hdrMaxExtSemTabSize = (swapBytes + ? ((((usqInt)(w1)) >> 8) & 0xFF) | (((w1 & 0xFF) << 8)) + : w1); + if (hdrMaxExtSemTabSize) { + /* begin setMaxExtSemSizeTo: */ + GIV(maxExtSemTabSizeSet) = 1; + ioSetMaxExtSemTableSize(hdrMaxExtSemTabSize); + } + + /* begin getShortFromFile:swap: */ + w1 = 0; + sqImageFileRead((&w1), sizeof(unsigned short), 1, f); + GIV(the2ndUnknownShort) = (swapBytes + ? ((((usqInt)(w1)) >> 8) & 0xFF) | (((w1 & 0xFF) << 8)) + : w1); + + /* Set maxLiteralCountForCompile unless it has already been set on the command line. */ + if (maxLiteralCountForCompile < 0) { + maxLiteralCountForCompile = (GIV(the2ndUnknownShort) + ? GIV(the2ndUnknownShort) + : MaxLiteralCountForCompile); + } + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + firstSegSize = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin firstSegmentSize: */ + GIV(firstSegmentSize) = firstSegSize; + + /* compare memory requirements with availability */ + allocationReserve = interpreterAllocationReserveBytes(); + minimumMemory = ((GIV(cogCodeSize) + dataSize) + GIV(edenBytes)) + allocationReserve; + + /* no need to include the stackZone; this is alloca'ed + Compute how much space is needed for the initial heap allocation. + no need to include the stackZone; this is alloca'ed. + no need to include the JIT code zone size; this is allocated separately. */ + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + freeOldSpaceInImage = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin initialHeadroom:givenFreeOldSpaceInImage: */ + headroomSqInt = (extraVMMemory + ? extraVMMemory + : (!(GIV(growHeadroom)) + ? 0x1000000 + : GIV(growHeadroom))); + if (freeOldSpaceInImage >= headroomSqInt) { + headroom = 0; + goto l1; + } + if (freeOldSpaceInImage >= ((headroomSqInt * 7) / 8)) { + headroom = headroomSqInt / 8; + goto l1; + } + if (freeOldSpaceInImage >= ((headroomSqInt * 3) / 4)) { + headroom = headroomSqInt / 4; + goto l1; + } + if (freeOldSpaceInImage >= ((headroomSqInt * 5) / 8)) { + headroom = (headroomSqInt * 3) / 8; + goto l1; + } + if (freeOldSpaceInImage >= (headroomSqInt / 2)) { + headroom = headroomSqInt / 2; + goto l1; + } + headroom = headroomSqInt; + /* end initialHeadroom:givenFreeOldSpaceInImage: */ +l1: + heapSizeUsqInt = ((dataSize + headroom) + GIV(edenBytes)) + ((headroom > allocationReserve + ? 0 + : allocationReserve)); + + /* begin roundUpHeapSize: */ + bit = (((highBit(heapSizeUsqInt)) - 1) * 3) / 4; + heapSize = (((heapSizeUsqInt & ((1ULL << bit) - 1)) != 0) + ? (((heapSizeUsqInt | ((1ULL << bit) - 1)) - ((1ULL << bit) - 1))) + (1ULL << bit) + : heapSizeUsqInt); + + /* allocateJITMemory will assign the actual size allocated, which is rounded up to a page boundary. */ + cogCodeBase = ((sqInt)(allocateJITMemory((&GIV(cogCodeSize))))); + + /* allocate a contiguous block of memory for the Squeak heap and ancilliary data structures */ + if ((mem = ((usqInt)(allocateMemoryMinimumImageFileHeaderSize(heapSize, minimumMemory, f, headerSize))))) { + /* cannot clash with the variable memory still in use in NewCoObjectMemory and superclasses */ + setHeapBasememoryLimitendOfMemory((heapBase = mem), mem + heapSize, mem + dataSize); + } + else { + insufficientMemoryAvailableError(); + } + + /* position file after the header */ + sqImageFileSeek(f, headerStart + headerSize); + + /* read in the image in bulk, then swap the bytes if necessary */ + bytesRead = readHeapFromImageFiledataBytes(f, dataSize); + if (bytesRead != dataSize) { + unableToReadImageError(); + } + ensureImageFormatIsUpToDate(swapBytes); + + /* compute difference between old and new memory base addresses */ + bytesToShift = GIV(oldSpaceStart) - GIV(oldImageBaseAddress); + + /* begin initializeInterpreter: */ + interpreterProxy = sqGetInterpreterProxy(); + (&interpreterProxy); + initializeObjectMemory(bytesToShift); + + /* begin checkAssumedCompactClasses */ + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassArrayCompactIndex >= 1) && (ClassArrayCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassArrayCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("Array"); + } + + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassLargeNegativeInteger) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassLargeNegativeIntegerCompactIndex >= 1) && (ClassLargeNegativeIntegerCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassLargeNegativeIntegerCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("LargeNegativeInteger"); + } + + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassLargePositiveInteger) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassLargePositiveIntegerCompactIndex >= 1) && (ClassLargePositiveIntegerCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassLargePositiveIntegerCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("LargePositiveInteger"); + } + + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassFloat) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassFloatCompactIndex >= 1) && (ClassFloatCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassFloatCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("Float"); + } + + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassBlockClosure) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassBlockClosureCompactIndex >= 1) && (ClassBlockClosureCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassBlockClosureCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("BlockClosure"); + } + + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassMethodContext) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassMethodContextCompactIndex >= 1) && (ClassMethodContextCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassMethodContextCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("MethodContext"); + } + objOop = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteArray) << (shiftForWord())))))); + + /* begin compactIndexOfClass: */ + assert((rawHashBitsOf(objOop)) != 0); + GIV(classByteArrayCompactIndex) = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()); + + /* begin initializeExtraClassInstVarIndices */ + classArrayObj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))); + classArrayClass = fetchClassOfNonImm(classArrayObj); + + /* begin numSlotsOf: */ + assert((classIndexOf(classArrayClass)) > (isForwardedObjectClassIndexPun())); + + /* determine actual Metaclass instSize */ + GIV(metaclassNumSlots) = (((numSlots = byteAt((void *)(classArrayClass + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classArrayClass - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + + /* default */ + GIV(thisClassIndex) = 5; + /* begin numSlotsOf: */ + assert((classIndexOf(classArrayClass)) > (isForwardedObjectClassIndexPun())); + if (((numSlots = byteAt((void *)(classArrayClass + (numSlotsFieldByteOffset()))))) == (numSlotsMask())) { + toDoLimit = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classArrayClass - BaseHeaderSize)))) << 8)))))) >> 8; + } + else { + toDoLimit = numSlots; + } + for (i = (InstanceSpecificationIndex + 1); i <= toDoLimit; i += 1) { + if ((longAt((void *)((classArrayClass + BaseHeaderSize) + ((((usqInt)((i - 1)) << (shiftForWord()))))))) == classArrayObj) { + GIV(thisClassIndex) = i - 1; + } + } + + /* default */ + GIV(classNameIndex) = 6; + /* begin numSlotsOf: */ + assert((classIndexOf(classArrayObj)) > (isForwardedObjectClassIndexPun())); + if (((numSlots = byteAt((void *)(classArrayObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask())) { + toDoLimit1 = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classArrayObj - BaseHeaderSize)))) << 8)))))) >> 8; + } + else { + toDoLimit1 = numSlots; + } + for (i = (InstanceSpecificationIndex + 1); i <= toDoLimit1; i += 1) { + oop = longAt((void *)((classArrayObj + BaseHeaderSize) + ((((usqInt)((i - 1)) << (shiftForWord())))))); + if (objectequalsString(oop, "Array")) { + GIV(classNameIndex) = i - 1; + } + } + GIV(method) = (GIV(newMethod) = GIV(nilObj)); + GIV(methodDictLinearSearchLimit) = 8; + + /* begin initialCleanup */ + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + + /* begin flushExternalPrimitiveTable */ + memset(externalPrimitiveTable, 0, MaxExternalPrimitiveTableSize * (sizeof(externalPrimitiveTable[0]))); + GIV(externalPrimitiveTableFirstFreeIndex) = 0; + + /* cmd-. as used for Mac but no other OS */ + GIV(interruptKeycode) = ((0x800)) + (((sqInt)'.')); + while (GIV(globalSessionID) == 0) { + GIV(globalSessionID) = ((time(NULL)) + (ioMSecs())) & 0x7FFFFFFF; + } + GIV(metaAccessorDepth) = -2; + sHEAFn = ioLoadFunctionFrom("secHasEnvironmentAccess", "SecurityPlugin"); + initializeCodeZoneFromupTo(cogCodeBase, cogCodeBase + GIV(cogCodeSize)); + return dataSize; +} + + +/* This is a little elaborate. The primTraceLog is only useful if it is not + full of noise. + To reduce noise when debugging a specific plugin we allow a plugin name to + be specified and will only generate the primTraceLog code for primitives + in that plugin. */ + + /* CoInterpreter>>#recordFastCCallPrimTraceForMethod: */ +sqInt +recordFastCCallPrimTraceForMethod(sqInt aMethodObj) +{ + return (recordFastCCallPrimTrace()) + && ((primTracePluginName + ? methodHasPrimitiveInPrimTracePlugin(aMethodObj) + : 1)); +} + + +/* This is a little elaborate. The primTraceLog is only useful if it is not + full of noise. + To reduce noise when debugging a specific plugin we allow a plugin name to + be specified and will only generate the primTraceLog code for primitives + in that plugin. */ + + /* CoInterpreter>>#recordPrimTraceForMethod: */ +sqInt +recordPrimTraceForMethod(sqInt aMethodObj) +{ + return (recordPrimTrace()) + && ((primTracePluginName + ? methodHasPrimitiveInPrimTracePlugin(aMethodObj) + : 1)); +} + + +/* noop in the StackInterpreter */ + + /* CoInterpreter>>#reloadPrimitiveCalloutPointer */ +static NeverInline void +reloadPrimitiveCalloutPointer(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + char *moduleName; + + primitiveCalloutPointer = ioLoadFunctionFrom("primitiveCallout", "SqueakFFIPrims"); + + /* Find out whether SqueakFFIPrims is internal or external */ + GIV(primCalloutIsExternal) = 1; + index = 1; + do { + if ((moduleName = ioListBuiltinModule(index))) { + if (!(strcmp(moduleName, "SqueakFFIPrims"))) { + GIV(primCalloutIsExternal) = 0; + return; + } + } + else { + return; + } + index += 1; + } while(1); +} + + +/* Report the stack page size and minimum unused headroom to stdout. */ + + /* CoInterpreter>>#reportMinimumUnusedHeadroom */ +void +reportMinimumUnusedHeadroom(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fprintf(GIV(transcript), + "stack page bytes %" PRIdSQINT " available headroom %" PRIdSQINT " minimum unused headroom %" PRIdSQINT "\n", + stackPageByteSize(), + ((stackPageByteSize()) - ((((stackPageFrameBytes()) < (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))) ? (stackPageFrameBytes()) : (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))))) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord), + minimumUnusedHeadroom()); +} + + +/* Report the stack page size and minimum unused headroom to a stream. */ + + /* CoInterpreter>>#reportMinimumUnusedHeadroomOn: */ +void +reportMinimumUnusedHeadroomOn(FILE *aStdioStream) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + FILE *savedTranscript; + + /* begin withRedirectedOutputTo:do: */ + savedTranscript = GIV(transcript); + GIV(transcript) = (aStdioStream + ? aStdioStream + : stdout); + + /* begin reportMinimumUnusedHeadroom */ + fprintf(GIV(transcript), + "stack page bytes %" PRIdSQINT " available headroom %" PRIdSQINT " minimum unused headroom %" PRIdSQINT "\n", + stackPageByteSize(), + ((stackPageByteSize()) - ((((stackPageFrameBytes()) < (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))) ? (stackPageFrameBytes()) : (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))))) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord), + minimumUnusedHeadroom()); + GIV(transcript) = savedTranscript; +} + + +/* Return to the current frame, either by entering machine code, or + longjmp-ing back to the + interpreter or simply returning, depending on where we are. To know + whether to return or + enter machine code we have to know from whence we came. We could have come + from the interpreter, either directly or via a machine code primitive. We + could have come from + machine code. The instructionPointer tells us where from. If it is above + startOfMemory we're + in the interpreter. If it is below, then we are in machine-code unless it + is ceReturnToInterpreterPC, + in which case we're in a machine-code primitive called from the + interpreter. */ + + /* CoInterpreter>>#returnToExecutive:postContextSwitch: */ +static NoDbgRegParms sqInt +returnToExecutivepostContextSwitch(sqInt inInterpreter, sqInt switchedContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + CogBlockMethod *cogMethod; + sqInt fullyInInterpreter; + sqInt header; + sqInt methodHeader; + sqInt retValue; + + assertCStackWellAligned(); + if ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())) { + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + + /* If returning after a context switch then a result may have to be popped from the stack. + If the process is suspended at a send then the result of the primitive in which the + process was suspended is still on the stack and must be popped into ReceiverResultReg. + If not, nothing should be popped and ReceiverResultReg gets the receiver. */ + if (switchedContext) { + /* begin mframeCogMethod: */ + cogMethod = ((CogBlockMethod *) ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodMask)); + assert((GIV(instructionPointer) > (minCogMethodAddress())) + && (GIV(instructionPointer) < (maxCogMethodAddress()))); + if ((GIV(instructionPointer) != ((((sqInt)cogMethod)) + ((cogMethod->stackCheckOffset)))) + && (isSendReturnPC(GIV(instructionPointer)))) { + assert(addressCouldBeOop(stackTop())); + + /* begin popStack */ + retValue = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + } + else { + retValue = longAt(GIV(framePointer) + FoxMFReceiver); + } + } + else { + retValue = longAt(GIV(framePointer) + FoxMFReceiver); + } + assert(isPCWithinMethodZone(GIV(instructionPointer))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),retValue); + ceEnterCogCodePopReceiverReg(); + } + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + fullyInInterpreter = inInterpreter; + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + fullyInInterpreter = 0; + } + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 1, __LINE__); + if (!fullyInInterpreter) { + ceInvokeInterpret(); + } + return null; +} + + +/* We have made a context switch, either when interpreting or from machine + code. Effectively return to the current frame, either by entering machine + code, or + longjmp-ing back to the interpreter or simply returning, depending on + where we are. */ + + /* CoInterpreter>>#return:toExecutive: */ +static NoDbgRegParms sqInt +returntoExecutive(sqInt returnValue, sqInt inInterpreter) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + sqInt header; + sqInt methodHeader; + char *sp; + + assertCStackWellAligned(); + if ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())) { + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + assert(isPCWithinMethodZone(GIV(instructionPointer))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + ceEnterCogCodePopReceiverReg(); + } + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 1, __LINE__); + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + if (inInterpreter) { + return null; + } + ceInvokeInterpret(); + + /* NOTREACHED */ + return null; +} + + +/* Send the calllback message to Alien class with the supplied arg(s). Use + either the 1 arg + invokeCallbackContext: or the 4 arg invokeCallback:stack:registers:jmpbuf: + message, depending on what selector is installed in the + specialObjectsArray. Note that if invoking the + legacy invokeCallback:stack:registers:jmpbuf: we pass the + vmCallbackContext as the jmpbuf + argument (see reestablishContextPriorToCallback:). The arguments are raw C + addresses and + are converted to integer objects on the way. sendInvokeCallbackContext: & + returnAs:ThroughCallback:Context: along with ownVM: and disownVM: conspire + to save and + restore newMethod, argumentCount and primitiveFunctionPointer around a + callback. The VM depends on argumentCount being correct to cut-back the + correct number of + arguments on primitive return. Since this is an implicit send we need to + log it explicitly. + The return side is done via a primitive so that gets logged normally. */ +/* Override check stack alignment. */ + + /* CoInterpreter>>#sendInvokeCallbackContext: */ +sqInt +sendInvokeCallbackContext(VMCallbackContext *vmCallbackContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTag; + CogMethod *cogMethod; + usqInt i; + usqInt initialIP; + sqInt methodHeader; + usqInt numArgs; + usqInt numTemps; + sqInt object; + sqInt rcvr; + sqInt thunkp; + + assertCStackWellAligned(); + if (recordPrimTrace()) { + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorInvokeCallback) << (shiftForWord()))))))); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + thunkp = ((sqInt) ((vmCallbackContext->thunkp))); + + /* begin recordInvokeCallback: */ + /* begin recordTrace:source:thing:extra: */ + if (TraceLog) { + GIV(traceLog)[GIV(traceLogIndex)] = ((((((usqInt)(TraceVMCallback) << TraceTypeShift))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = (((sqInt)thunkp)); + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + + /* begin fetchClassTagOfNonImm: */ + classTag = (longAt((void *)(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord()))))))))) & (classIndexMask()); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorInvokeCallback) << (shiftForWord())))))); + if (!(lookupInMethodCacheSelclassTag(GIV(messageSelector), classTag))) { + if (lookupOrdinaryNoMNUEtcInClass(classForClassTag(classTag))) { + return 0; + } + } + assert(((debugCallbackInvokes += 1)) > 0); + + /* self assert: debugCallbackInvokes < 3802. */ + + /* begin saveCStackStateForCallbackContext: */ + (vmCallbackContext->savedCStackPointer = ((void *)GIV(CStackPointer))); + (vmCallbackContext->savedCFramePointer = ((void *)GIV(CFramePointer))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord()))))))); + if ((argumentCountOf(GIV(newMethod))) == 4) { + object = positive64BitIntegerFor(((usqInt)((vmCallbackContext->thunkp)))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + object = positive64BitIntegerFor(((usqInt)((vmCallbackContext->stackp)))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + object = positive64BitIntegerFor(((usqInt)((vmCallbackContext->intregargsp)))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + object = positive64BitIntegerFor(((usqInt)vmCallbackContext)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + + /* begin justActivateNewMethod: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + cogMethod = null; + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + methodHeader = (cogMethod->methodHeader); + } + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeader)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(addressCouldBeOop(rcvr)); + assert(!(isOopForwarded(rcvr))); + if (cogMethod) { + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + } + GIV(instructionPointer) = ceReturnToInterpreterPC(); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + initialIP = ((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize; + if (cogMethod) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)cogMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + GIV(instructionPointer) = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + GIV(instructionPointer) = initialIP - 1; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + if (!cogMethod) { + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + } + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeader); + } + } + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + + /* begin checkForStackOverflow */ + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (GIV(stackPointer) < ((GIV(stackPage)->realStackLimit))) { + handleStackOverflow(); + } + assert((frameReceiver(GIV(framePointer))) == (splObj(ClassAlien))); + + /* begin enterSmalltalkExecutiveFromCallback */ + enterSmalltalkExecutive(); + + /* not reached */ + return 1; +} + + /* CoInterpreter>>#shortPrintFrame: */ +static NoDbgRegParms sqInt +shortPrintFrame(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + sqInt mthd; + sqInt rcvr; + + if (!(/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "invalid frame pointer"); + cr(); + return null; + } + rcvr = /* frameReceiver: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? longAt(theFP + FoxMFReceiver) + : longAt(theFP + FoxIFReceiver)); + mthd = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + printHexPtrnp(theFP); + + /* begin space */ + printChar(' '); + printChar(((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? 'M' + : 'I')); + + /* begin space */ + printChar(' '); + printActivationNameForreceiverisBlockfirstTemporary(mthd, rcvr, /* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0), /* temporary:in: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (0 < ((frameNumArgs = ((mframeCogMethod(theFP))->cmNumArgs))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgs) * BytesPerWord)) + : longAt(((theFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs) * BytesPerWord))) + : /* itemporary:in: */ + (0 < ((frameNumArgsUsqInt = byteAt((theFP + FoxIFrameFlags) + 1))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt) * BytesPerWord)) + : longAt(((theFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt) * BytesPerWord))))); + + /* begin space */ + printChar(' '); + shortPrintOop(rcvr); + return 0; +} + + +/* Answer a full 32 bit integer object for the given integer value. + N.B. Returning in each arm separately enables Slang inlining. + /Don't/ return the ifTrue:ifFalse: unless Slang inlining of conditionals + is fixed. */ + + /* CoInterpreter>>#signed32BitIntegerFor: */ +sqInt +signed32BitIntegerFor(sqInt integerValue) +{ + return (((usqInt)(((int) integerValue)) << 3) | 1); +} + + +/* Convert the given object into an integer value. + The object may be either a SmallInteger or a four-byte LargeInteger. */ + + /* CoInterpreter>>#signed32BitValueOf: */ +int +signed32BitValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt value64; + + if ((((oop) & 7) == 1)) { + value64 = (oop >> 3); + if ((((int) value64)) != value64) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + value64 = 0; + } + return value64; + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; +} + + +/* Answer a Large Integer object for the given integer value. N.B. will *not* + cause a GC. */ + + /* CoInterpreter>>#signed64BitIntegerFor: */ +sqInt +signed64BitIntegerFor(sqLong integerValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt largeClass; + usqLong magnitude; + usqInt newLargeInteger; + usqInt newObj; + usqInt numBytes; + sqInt numSlots; + sqInt objFormat; + sqInt sz; + + if (integerValue < 0) { + if (integerValue >= (MinSmallInteger)) { + return (((usqInt)(((sqInt)integerValue)) << 3) | 1); + } + largeClass = ClassLargeNegativeIntegerCompactIndex; + magnitude = 0 - (((usqLong) integerValue)); + } + else { + if (integerValue <= (MaxSmallInteger)) { + return (((usqInt)(((sqInt)integerValue)) << 3) | 1); + } + largeClass = ClassLargePositiveIntegerCompactIndex; + magnitude = integerValue; + } + sz = 8; + objFormat = (firstByteFormat()) + ((8 - sz) & (BytesPerWord - 1)); + numSlots = (sz + 3) / BytesPerOop; + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((numSlots >= 0) + && ((largeClass != 0) + && ((knownClassAtIndex(largeClass)) != GIV(nilObj)))); + assert(((objFormat < (firstByteFormat()) + ? objFormat + : objFormat & (byteFormatMask()))) == (instSpecOfClass(knownClassAtIndex(largeClass)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newLargeInteger = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + largeClass); + GIV(freeStart) += numBytes; + newLargeInteger = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + if (sz > 4) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((newLargeInteger + BaseHeaderSize)),SQ_SWAP_8_BYTES_IF_BIGENDIAN(magnitude)); + } + else { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((newLargeInteger + BaseHeaderSize)),SQ_SWAP_4_BYTES_IF_BIGENDIAN(magnitude)); + } + return newLargeInteger; +} + + +/* Convert the given object into an integer value. + The object may be either a positive SmallInteger or a eight-byte + LargeInteger. + */ + + /* CoInterpreter>>#signed64BitValueOf: */ +sqLong +signed64BitValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + sqInt fmt; + usqLong magnitude; + sqInt negative; + usqInt numSlots; + int ok; + sqInt sz; + + if ((((oop) & 7) == 1)) { + return ((sqLong) ((oop >> 3))); + } + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (ok) { + negative = 0; + } + else { + negative = 1; + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargeNegativeIntegerCompactIndex == ccIndex; + if (!ok) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + sz = ((((/* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (sz > 4) { + magnitude = SQ_SWAP_8_BYTES_IF_BIGENDIAN((long64At((void *)((oop + BaseHeaderSize))))); + if ((sz > (sizeof(sqLong))) + || ((negative + ? magnitude > 0x8000000000000000ULL + : magnitude >= 0x8000000000000000ULL))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + } + else { + magnitude = ((unsigned int) (SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((oop + BaseHeaderSize))))))); + } + return (negative + ? 0 - magnitude + : magnitude); +} + + +/* */ + + /* CoInterpreter>>#specialSelectorNumArgs: */ +sqInt +specialSelectorNumArgs(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((fetchPointerofObject((index * 2) + 1, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))))) >> 3); +} + + /* CoInterpreter>>#stackLimitAddress */ +usqInt +stackLimitAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(stackLimit)))); +} + + +/* Answer the amount of slots needed to fit a new frame at the point the + stack limit is checked. A frame looks like this at the point the stack + limit is checked: + stacked receiver/closure + arg0 + ... + argN + caller's method ip/base frame's sender context + fp-> saved fp + method + context (uninitialized?) + method header fields (interpreter only) + saved method ip (uninitialized?; interpreter only) + receiver + first temp + ... + sp-> Nth temp + So the amount of headroom is + the maximum number of arguments + 1 (for stacked receiver and arguments) + + the frame size + + the max number of temps. + Since a method's number of temps includes its arguments the actual offset + is: */ + + /* CoInterpreter>>#stackLimitOffset */ +static sqInt +stackLimitOffset(void) +{ + return ((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord; +} + + +/* Return a minimum amount of headroom for each stack page (in bytes). + In the interpreter we don't actually need any headroom. In a JIT the stack + has to have room for interrupt handlers which will run on the stack. + Defer to the platform for this one. */ + + /* CoInterpreter>>#stackPageHeadroom */ +static sqInt +stackPageHeadroom(void) +{ + return osCogStackPageHeadroom(); +} + + /* CoInterpreter>>#stackPointerAddress */ +usqInt +stackPointerAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(stackPointer)))); +} + + +/* Zero-relative version of CompiledMethod>>startpc. */ + + /* CoInterpreter>>#startPCOfMethodHeader: */ +usqInt +startPCOfMethodHeader(sqInt aCompiledMethodHeader) +{ + return (((/* begin literalCountOfMethodHeader: */ + assert((((aCompiledMethodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((aCompiledMethodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop; +} + + +/* Answer the startPC of lit if it is a (clean) block in aMethodObj, + otherwise answer nil. + */ + + /* CoInterpreter>>#startPCOrNilOfLiteral:in: */ +sqInt +startPCOrNilOfLiteralin(sqInt lit, sqInt aMethodObj) +{ + usqInt numSlots; + sqInt oop; + sqInt outerContext; + + if (!(((!(lit & (tagMask())))) + && ((((byteAt((void *)(lit + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat())) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(lit)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(lit + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(lit - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= ClosureFirstCopiedValueIndex)))) { + return null; + } + outerContext = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + if (!(/* isContext: */ + ((!(outerContext & (tagMask())))) + && (((longAt((void *)(outerContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + return null; + } + if (aMethodObj != (longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))))) { + return null; + } + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + return (oop >> 3); +} + + +/* Handle the cannot return response for a base frame return to an invalid + context. Build a new base frame for the context in the cannot resume state + ready for the + send of cannotReturn:. + + Since we have returned from the base frame of the page the context is + effectively widowed. + But its sender needs to be contextToReturnTo, and its pc needs to be the + HasBeenReturnedFromMCPC marker. So bereave it (as a side-effect of + isWidowedContext:), assign contextToReturnTo to + sender, and rebuild its frame, which will have the ceCannotResumePC as its + pc. Finally push + returnValue and set instructionPointer to ceCannotResumePC in preparation + for the send. */ + + /* CoInterpreter>>#tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFrom:to:returnValue: */ +static NoDbgRegParms void +tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFromtoreturnValue(sqInt contextToReturnFrom, sqInt contextToReturnTo, sqInt returnValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *newPage; + + assert((GIV(stackPage)) + && (isFree(GIV(stackPage)))); + isWidowedContext(contextToReturnFrom); + assert(!(isMarriedOrWidowedContext(contextToReturnFrom))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SenderIndex, contextToReturnFrom, contextToReturnTo)); + assert(isNonImmediate(contextToReturnFrom)); + if (oopisGreaterThanOrEqualTo(contextToReturnFrom, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(contextToReturnTo & (tagMask())))) + && (oopisLessThan(contextToReturnTo, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(contextToReturnFrom + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(contextToReturnFrom); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((contextToReturnFrom + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),contextToReturnTo); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(InstructionPointerIndex, contextToReturnFrom, HasBeenReturnedFromMCPCOop)); + assert(isNonImmediate(contextToReturnFrom)); + if (oopisGreaterThanOrEqualTo(contextToReturnFrom, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(HasBeenReturnedFromMCPCOop & (tagMask())))) + && (oopisLessThan(HasBeenReturnedFromMCPCOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(contextToReturnFrom + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(contextToReturnFrom); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((contextToReturnFrom + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),HasBeenReturnedFromMCPCOop); + + /* void the instructionPointer to stop it being incorrectly updated in a code + compaction in makeBaseFrameFor:. */ + GIV(instructionPointer) = 0; + newPage = makeBaseFrameFor(contextToReturnFrom); + assert(GIV(stackPage) == newPage); + + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (newPage->headSP); + GIV(framePointer) = (newPage->headFP); + assert((stackTop()) == (ceCannotResumePC())); + + /* overwrite the ceSendCannotResumePC on the stack. If ever re-executed + the returnValue will be taken from top-of-stack by ceCannotResume. */ + + /* stackTopPut: */ + longAtput(GIV(stackPointer),returnValue); + + /* Assign it to instructionPointer as externalCannotReturn:from: pushes it. */ + GIV(instructionPointer) = ceCannotResumePC(); +} + + +/* See StackInterpreter class>>initializeFrameIndices */ + + /* CoInterpreter>>#temporary:in: */ +static NoDbgRegParms sqInt +temporaryin(sqInt offset, char *theFP) +{ + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (offset < ((frameNumArgs = ((mframeCogMethod(theFP))->cmNumArgs))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgs - offset) * BytesPerWord)) + : longAt(((theFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs - offset) * BytesPerWord))) + : /* itemporary:in: */ + (offset < ((frameNumArgsUsqInt = byteAt((theFP + FoxIFrameFlags) + 1))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt - offset) * BytesPerWord)) + : longAt(((theFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt - offset) * BytesPerWord)))); +} + + /* CoInterpreter>>#temporary:in:put: */ +static NoDbgRegParms sqInt +temporaryinput(sqInt offset, char *theFP, sqInt valueOop) +{ + usqInt frameNumArgs; + unsigned char frameNumArgs1; + + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? longAtput((offset < ((frameNumArgs1 = ((mframeCogMethod(theFP))->cmNumArgs))) + ? (theFP + FoxCallerSavedIP) + ((frameNumArgs1 - offset) * BytesPerWord) + : ((theFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs1 - offset) * BytesPerWord)),valueOop) + : /* itemporary:in:put: */ + (offset < ((frameNumArgs = byteAt((theFP + FoxIFrameFlags) + 1))) + ? longAtput((theFP + FoxCallerSavedIP) + ((frameNumArgs - offset) * BytesPerWord),valueOop) + : longAtput(((theFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - offset) * BytesPerWord),valueOop))); +} + + +/* Record a process to be awoken on the next interpreter cycle. + Note: It is currently a fatal VM error if there is no runnable process + (indicated by newProcOrNil being nil), but the right thing to do is to + enter a wait state until the next event/expired delay/available input, + etc. Reimplement to cope with possible code compaction in + makeBaseFrameFor:. */ + + /* CoInterpreter>>#transferTo:from: */ +static NoDbgRegParms sqInt +transferTofrom(sqInt newProcOrNil, sqInt sourceCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt aMethodObj; + sqInt header; + usqInt ip; + StackPage *lastUsedPage; + sqInt methodHeader; + sqInt newContext; + StackPage *newPage; + sqInt oldProc; + char *ptr; + sqInt sched; + sqInt senderOop; + char *theFrame; + StackPage *thePage; + char *toDoLimit; + char *toDoLimit1; + sqInt top; + + GIV(statProcessSwitch) += 1; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + ip = (GIV(instructionPointer) >= (startOfMemory()) + ? GIV(instructionPointer) + 1 + : GIV(instructionPointer)); + + /* begin assertValidExternalFrameWithIP: */ + assertValidExecutionPointersimbarline(ip, GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + toDoLimit1 = /* frameReceiverLocation: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? GIV(framePointer) + FoxMFReceiver + : GIV(framePointer) + FoxIFReceiver); + for (ptr = (GIV(stackPointer) + BytesPerWord); ptr <= toDoLimit1; ptr += BytesPerWord) { + assert(addressCouldBeOop(longAt(ptr))); + } + + /* skip pushed instructionPointer */ + assert(isOopCompiledMethod(frameMethodObject(GIV(framePointer)))); + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(!((isForwarded(frameContext(GIV(framePointer)))))); + } + toDoLimit = GIV(framePointer) + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(GIV(framePointer)))->cmNumArgs) + : byteAt((GIV(framePointer) + FoxIFrameFlags) + 1)))) << (shiftForWord()))))); + for (ptr = ((GIV(framePointer) + FoxCallerSavedIP) + BytesPerWord); ptr <= toDoLimit; ptr += BytesPerWord) { + assert(addressCouldBeOop(longAt(ptr))); + } + + /* ensureMethodIsCogged: in makeBaseFrameFor: in + externalSetStackPageAndPointersForSuspendedContextOfProcess: + below may do a code compaction. Nil instructionPointer to avoid it getting pushed twice. */ + GIV(instructionPointer) = 0; + sched = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + oldProc = longAt((void *)((sched + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin recordContextSwitchFrom:to:in: */ + if ((recordEventTrace()) + && (!primTracePluginName)) { + /* begin recordTrace:source:thing:extra: */ + if (TraceLog) { + GIV(traceLog)[GIV(traceLogIndex)] = ((((((usqInt)(TraceContextSwitch) << TraceTypeShift)))) + sourceCode); + GIV(traceLog)[GIV(traceLogIndex) + 1] = oldProc; + GIV(traceLog)[GIV(traceLogIndex) + 2] = newProcOrNil; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l1; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer) + BytesPerWord); + /* end ensureFrameIsMarried:SP: */ +l1: + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, oldProc, activeContext)); + assert(isNonImmediate(oldProc)); + if (oopisGreaterThanOrEqualTo(oldProc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeContext & (tagMask())))) + && (oopisLessThan(activeContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(oldProc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(oldProc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((oldProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),activeContext); + if (!newProcOrNil) { + error("scheduler could not find a runnable process"); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ActiveProcessIndex, sched, newProcOrNil)); + assert(isNonImmediate(sched)); + if (oopisGreaterThanOrEqualTo(sched, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(newProcOrNil & (tagMask())))) + && (oopisLessThan(newProcOrNil, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(sched + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(sched); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((sched + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord()))))),newProcOrNil); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newProcOrNil)) + && (!(isForwarded(newProcOrNil)))); + assert(validStorePointerUncheckedArgs(MyListIndex, newProcOrNil, GIV(nilObj))); + longAtput((void *)((newProcOrNil + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin externalSetStackPageAndPointersForSuspendedContextOfProcess: */ + newContext = longAt((void *)((newProcOrNil + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + assert(isContext(newContext)); + if (((((longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert(checkIsStillMarriedContextcurrentFP(newContext, GIV(framePointer))); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newProcOrNil)) + && (!(isForwarded(newProcOrNil)))); + assert(validStorePointerUncheckedArgs(SuspendedContextIndex, newProcOrNil, GIV(nilObj))); + longAtput((void *)((newProcOrNil + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),GIV(nilObj)); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(newContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + if (theFrame != ((thePage->headFP))) { + /* explicit assignment of suspendedContext can cause switch to interior frame. */ + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l3; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l3: + moveFramesInthroughtoPage(thePage, findFrameAboveinPage(theFrame, thePage), newPage); + + /* begin markStackPageLeastMostRecentlyUsed: */ + assert(newPage == ((GIV(mostRecentlyUsedPage)->nextPage))); + lastUsedPage = (newPage->nextPage); + while (((lastUsedPage->baseFP)) == 0) { + lastUsedPage = (lastUsedPage->nextPage); + } + if (((lastUsedPage->nextPage)) == newPage) { + goto l2; + } + (((newPage->prevPage))->nextPage = (newPage->nextPage)); + (((newPage->nextPage))->prevPage = (newPage->prevPage)); + (((lastUsedPage->prevPage))->nextPage = newPage); + (newPage->prevPage = (lastUsedPage->prevPage)); + (newPage->nextPage = lastUsedPage); + (lastUsedPage->prevPage = newPage); + assert(pageListIsWellFormed()); + /* end markStackPageLeastMostRecentlyUsed: */ +l2:; + } + assert(((thePage->headFP)) == theFrame); + } + else { + thePage = makeBaseFrameFor(newContext); + theFrame = (thePage->baseFP); + } + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + return 0; +} + + +/* Go through all frames in the stack zone and mark their methods + so that compaction does not free any methods that are in use. */ +/* If instructionPointer is referring to machine code, as it will be if a + primitive is in progress + (see isCodeCompactingPrimitiveIndex:) it must be updated if it is + referring to a moved + method. */ + + /* CoInterpreter>>#updateStackZoneReferencesToCompiledCodePreCompaction */ +void +updateStackZoneReferencesToCompiledCodePreCompaction(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt i; + CogMethod *primCogMethod; + usqInt theFlags; + char *theFP; + usqInt theIP; + char *theIPPtr; + CogMethod *theMethod; + sqInt theMethodField; + StackPage *thePage; + + if (GIV(instructionPointer)) { + if ((primCogMethod = cogMethodContaining(GIV(instructionPointer)))) { + GIV(instructionPointer) += (primCogMethod->objectHeader); + } + } + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + /* begin updateStackZoneReferencesToCompiledCodePreCompactionOnPage: */ + callerFP = ((char *) 0); + theIPPtr = (thePage->headSP); + theFP = (thePage->headFP); + while (1) { + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + theMethodField = longAt(theFP + FoxMethod); + theFlags = theMethodField & MFMethodFlagsMask; + theMethod = ((CogMethod *) (theMethodField - theFlags)); + if (((theMethod->cmType)) == CMBlock) { + /* cmHomeMethod */ + if ((((((CogBlockMethod *) (theMethodField - theFlags)))->cmType)) >= CMMethod) { + theMethod = ((CogMethod *) (((CogBlockMethod *) (theMethodField - theFlags)))); + } + else { + assert(CMBlock == ((((CogBlockMethod *) (theMethodField - theFlags)))->cmType)); + theMethod = ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (theMethodField - theFlags))))) - (((((CogBlockMethod *) (theMethodField - theFlags)))->homeOffset)))); + } + } + theIP = ((usqInt)(longAt(theIPPtr))); + if ((theIP != (ceCannotResumePC())) + && (asserta((theIP >= (((usqInt)theMethod))) + && (theIP < ((((usqInt)theMethod)) + ((theMethod->blockSize))))))) { + longAtput(theIPPtr,theIP + ((theMethod->objectHeader))); + } + longAtput(theFP + FoxMethod,theMethodField + ((theMethod->objectHeader))); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theIPPtr = theFP + FoxCallerSavedIP; + theFP = callerFP; + } + } + } +} + + +/* Update the frame's spouse context with the frame's current state except + for the + sender and instruction pointer, which are used to mark the context as + married, and the receiver and method, which are already set. */ + + /* CoInterpreter>>#updateStateOfSpouseContextForFrame:WithSP: */ +static NoDbgRegParms void +updateStateOfSpouseContextForFrameWithSP(char *theFP, char *theSP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *argsPointer; + sqInt fieldIndex; + sqInt i; + char *pointer; + usqInt tempIndex; + sqInt theContext; + sqInt valuePointer; + + assert(frameHasContext(theFP)); + theContext = longAt(theFP + FoxThisContext); + assert(isContext(theContext)); + assert(frameReceiver(theFP) == noFixupFollowFieldofObject(ReceiverIndex, theContext)); + assert(frameMethodObject(theFP) == noFixupFollowFieldofObject(MethodIndex, theContext)); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + tempIndex = ((mframeCogMethod(theFP))->cmNumArgs); + pointer = (theFP + FoxMFReceiver) - BytesPerWord; + } + else { + tempIndex = byteAt((theFP + FoxIFrameFlags) + 1); + pointer = (theFP + FoxIFReceiver) - BytesPerWord; + } + + /* update the arguments. this would appear not to be strictly necessary, but is for two reasons. + First, the fact that arguments are read-only is only as convention in the Smalltalk compiler; + other languages may choose to modify arguments. + Second, the Squeak runUntilErrorOrReturnFrom: nightmare pops the stack top, which may, in + certain circumstances, be the last argument, and hence the last argument may not have been + stored into the context. */ + argsPointer = theFP + ((FoxCallerSavedIP + BytesPerWord) + ((tempIndex << (shiftForWord())))); + for (i = 1; i <= tempIndex; i += 1) { + argsPointer -= BytesPerWord; + assert(addressCouldBeOop(longAt(argsPointer))); + fieldIndex = ReceiverIndex + i; + valuePointer = longAt(argsPointer); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, theContext, valuePointer)); + assert(isNonImmediate(theContext)); + if (oopisGreaterThanOrEqualTo(theContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(theContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(theContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); + } + + /* now update the non-argument stack contents. */ + while (pointer >= theSP) { + assert(addressCouldBeOop(longAt(pointer))); + tempIndex += 1; + fieldIndex = ReceiverIndex + tempIndex; + valuePointer = longAt(pointer); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, theContext, valuePointer)); + assert(isNonImmediate(theContext)); + if (oopisGreaterThanOrEqualTo(theContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(theContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(theContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); + pointer -= BytesPerWord; + } + assert((ReceiverIndex + tempIndex) < (lengthOf(theContext))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(StackPointerIndex, theContext, ((tempIndex << 3) | 1))); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord()))))),((tempIndex << 3) | 1)); +} + + +/* Note that we accept anInstrPointer pointing to a callPrimitiveBytecode + at the start of a method that contains a primitive. This because methods + like Context(Part)>>reset have to be updated to skip the callPrimtiive + bytecode otherwise. */ +/* -1 for pre-increment in fetchNextBytecode */ + + /* CoInterpreter>>#validInstructionPointer:inMethod:framePointer: */ +sqInt +validInstructionPointerinMethodframePointer(usqInt instrPointer, usqInt aMethod, char *fp) +{ + CogMethod *cogMethod; + sqInt header; + usqInt theInstrPointer; + + if (instrPointer == (ceCannotResumePC())) { + return (((usqInt)(longAt(fp + FoxMethod)))) < (startOfMemory()); + } + if (instrPointer == (ceReturnToInterpreterPC())) { + if ((((usqInt)(longAt(fp + FoxMethod)))) < (startOfMemory())) { + return 0; + } + theInstrPointer = ((usqInt)(longAt(fp + FoxIFSavedIP))); + } + else { + theInstrPointer = instrPointer; + header = longAt((void *)((aMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + if (((/* begin isCogMethodReference: */ + assert(((((header) & 7) == 1)) + || (((((usqInt)header)) < (startOfMemory())) + && ((((usqInt)header)) >= (minCogMethodAddress())))), + /* isNonIntegerObject: */ + (!(header & (smallIntegerTag()))))) + && (theInstrPointer < (startOfMemory()))) { + cogMethod = ((CogMethod *) header); + return (theInstrPointer >= (header + (sizeof(CogMethod)))) + && (theInstrPointer < (header + ((cogMethod->blockSize)))); + } + } + return (theInstrPointer >= (((aMethod + (lastPointerOf(aMethod))) + BytesPerOop) - 1)) + && (theInstrPointer < (((aMethod + (numBytesOfBytes(aMethod))) + BaseHeaderSize) - 1)); +} + + +/* Check that the base frame in the stack page has a valid sender and saved + context. + */ + + /* CoInterpreter>>#validStackPageBaseFrame: */ +static NoDbgRegParms sqInt +validStackPageBaseFrame(StackPage *aPage) +{ + sqInt savedThisContext; + sqInt senderContextOrNil; + + senderContextOrNil = longAt((aPage->baseAddress)); + savedThisContext = longAt(((aPage->baseAddress)) - BytesPerWord); + if (!(asserta(((((aPage->baseFP)) + (frameStackedReceiverOffset((aPage->baseFP)))) + (2 * BytesPerWord)) == ((aPage->baseAddress))))) { + return 0; + } + if (!(asserta(addressCouldBeObj(senderContextOrNil)))) { + return 0; + } + if (!(asserta(addressCouldBeObj(savedThisContext)))) { + return 0; + } + if (!(asserta((senderContextOrNil == (nilObject())) + || (isContext(senderContextOrNil))))) { + return 0; + } + if (!(asserta(isContext(savedThisContext)))) { + return 0; + } + if (!(asserta((frameCallerContext((aPage->baseFP))) == senderContextOrNil))) { + return 0; + } + if (!(asserta((frameContext((aPage->baseFP))) == savedThisContext))) { + return 0; + } + return 1; +} + + +/* Make sure that all VM state that affects the heap contents is voided so + that the heap is + ready to be snapshotted. If flushExtPrims is true, flush references to + external primitives in methods. Answer the activeContext that should be + stored in the snapshot. */ +/* Make sure that all VM state that affects the heap contents is voided so + that the heap is + ready to be snapshotted. If flushExtPrims is true, flush references to + external primitives in methods. Answer the activeContext that should be + stored in the snapshot. */ + + /* CoInterpreter>>#voidVMStateForSnapshotFlushingExternalPrimitivesIf: */ +static NoDbgRegParms sqInt +voidVMStateForSnapshotFlushingExternalPrimitivesIf(sqInt flushExtPrims) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt address; + sqInt classIndex; + sqInt fmt; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* in case of code compactions. */ + GIV(instructionPointer) = 0; + activeContext = divorceAllFrames(); + + /* begin bereaveAllMarriedContextsForSnapshotFlushingExternalPrimitivesIf: */ + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + fmt = (byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask()); + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + makeContextSnapshotSafe(objSqInt); + } + + /* Clean out external functions from compiled methods */ + if (flushExtPrims + && (fmt >= (firstCompiledMethodFormat()))) { + flushExternalPrimitiveOf(objSqInt); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } + voidCogCompiledCode(); + return activeContext; +} + + +/* useful for VM debugging */ + + /* CoInterpreter>>#whereIs: */ +char * +whereIs(sqInt anOop) +{ + char *somewhere; + char *where; + + if ((somewhere = whereIsMaybeCodeThing(anOop))) { + return somewhere; + } + if ((where = whereIsMaybeHeapThing(anOop))) { + return where; + } + if ((where = whereIsMaybeStackThing(anOop))) { + return where; + } + return " is no where obvious"; +} + + +/* Either widow the context or map its pc to a bytecode one. + Used to implement primitiveVoidVMStateForMethod. */ +/* for debugging & saving space */ + + /* CoInterpreter>>#widowOrForceToBytecodePC: */ +static NoDbgRegParms NeverInline void +widowOrForceToBytecodePC(sqInt ctxt) +{ + if (((((longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (!(isWidowedContext(ctxt))) { + assert(!((isMachineCodeFrame(frameOfMarriedContext(ctxt))))); + } + } + else { + ensureContextHasBytecodePC(ctxt); + } +} + + /* CoInterpreterPrimitives>>#frameIsMarked: */ +static NoDbgRegParms int +frameIsMarked(sqInt theFPInt) +{ + sqInt methodField; + + methodField = longAt((void *)(theFPInt + FoxMethod)); + return ((((usqInt)methodField)) < (startOfMemory()) + ? (methodField & 4) != 0 + : ((longAt((void *)(theFPInt + FoxIFrameFlags))) & 2) != 0); +} + + /* CoInterpreterPrimitives>>#primitiveAllMethodsCompiledToMachineCode */ +#if VMInvestigations +EXPORT(int) +primitiveAllMethodsCompiledToMachineCode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayObj; + sqInt delta; + sqInt maxNumEntries; + sqInt nEntries; + + if (GIV(argumentCount)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + maxNumEntries = (numMethods()) + (16 /* (allocationUnit * 2) */ / BytesPerOop); + arrayObj = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), maxNumEntries); + if (!arrayObj) { + return -1; + } + nEntries = methodsCompiledToMachineCodeInto(arrayObj); + if (nEntries < maxNumEntries) { + /* begin shorten:toIndexableSize: */ + assert((nEntries >= 0) + && (nEntries < (lengthOf(arrayObj)))); + delta = doShortentoIndexableSize(arrayObj, nEntries); + assert((lengthOf(followMaybeForwarded(arrayObj))) == nEntries); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),arrayObj); + return 0; +} +#endif /* VMInvestigations */ + + /* CoInterpreterPrimitives>>#primitiveBenchmarkFollowForwardersInStackZone */ +#if VMInvestigations +EXPORT(void) +primitiveBenchmarkFollowForwardersInStackZone(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + usqLong then; + + /* begin primitiveReturnTimeTakenFor: */ + then = ioHighResClock(); + followForwardingPointersOfReceiversInStackZone(); + oop = positive64BitIntegerFor((ioHighResClock()) - then); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} +#endif /* VMInvestigations */ + + /* CoInterpreterPrimitives>>#primitiveBenchmarkFollowForwardersOfReceiverAndTemporariesInStackZone */ +#if VMInvestigations +EXPORT(void) +primitiveBenchmarkFollowForwardersOfReceiverAndTemporariesInStackZone(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + usqLong then; + + /* begin primitiveReturnTimeTakenFor: */ + then = ioHighResClock(); + followForwardingPointersOfReceiverAndTemporariesInStackZone(); + oop = positive64BitIntegerFor((ioHighResClock()) - then); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} +#endif /* VMInvestigations */ + + /* CoInterpreterPrimitives>>#primitiveBenchmarkJITReceiver */ +#if VMInvestigations +EXPORT(sqInt) +primitiveBenchmarkJITReceiver(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cloneMethod; + sqInt oop; + sqInt receiverMethod; + usqLong then; + + receiverMethod = longAt(GIV(stackPointer)); + if (!(/* isOopCompiledMethod: */ + ((!(receiverMethod & (tagMask())))) + && (((byteAt((void *)(receiverMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return (GIV(primFailCode) = PrimErrBadReceiver); + } + if (isFullBlockMethod(receiverMethod)) { + return (GIV(primFailCode) = PrimErrUnsupported); + } + cloneMethod = cloneObject(receiverMethod); + if (!cloneMethod) { + return (GIV(primFailCode) = PrimErrNoMemory); + } + + /* begin primitiveReturnTimeTakenFor: */ + then = ioHighResClock(); + if (!(cogselector(cloneMethod, maybeSelectorOfMethod(cloneMethod)))) { + return (GIV(primFailCode) = PrimErrOperationFailed); + } + freeCogMethod(cogMethodOf(cloneMethod)); + oop = positive64BitIntegerFor((ioHighResClock()) - then); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return 0; +} +#endif /* VMInvestigations */ + + /* CoInterpreterPrimitives>>#primitiveBenchmarkScavenge */ +#if VMInvestigations +EXPORT(void) +primitiveBenchmarkScavenge(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + usqLong then; + + /* begin primitiveReturnTimeTakenFor: */ + then = ioHighResClock(); + scavengingGCTenuringIf(TenureByAge); + (((usqInt)(((((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes())) < 0) ? 0 : ((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes())))) << 3) | 1); + oop = positive64BitIntegerFor((ioHighResClock()) - then); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} +#endif /* VMInvestigations */ + + +/* This is optional old obsolete stuff tedious to implement in Cog because of + the need to map from machine code pc to bytecode pc. So for now just fail. */ + + /* CoInterpreterPrimitives>>#primitiveClosureCopyWithCopiedValues */ +static void +primitiveClosureCopyWithCopiedValues(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } +} + + +/* Answer an Array of: + - the address of the CogMethod of the receiver, + - the CogMethod of the receiver as a ByteArray, and + - a sequence of relative pc/value/object triples for the imported + references embedded in the CogMethod. + Fail on V3. Fail if the receiver is not a cogged method. Fail if there is + insufficient memory for the result. + If there is an argument it is a boolean that if true includes more + detailed Closed PIC information. */ + + /* CoInterpreterPrimitives>>#primitiveCogitMethod */ +#if VMInvestigations +EXPORT(sqInt) +primitiveCogitMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt code; + CogMethod *cogMethod; + sqInt cogMethodData; + usqInt data; + sqInt detailCPICs; + sqInt methodHeader; + sqInt methodReceiver; + usqInt newObj; + usqInt numBytes; + sqInt valuePointer; + + if (GIV(argumentCount) > 0) { + if (GIV(argumentCount) > 1) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + if (!((detailCPICs = (longAt(GIV(stackPointer))) == GIV(trueObj)))) { + if (!((longAt(GIV(stackPointer))) == GIV(falseObj))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + } + } + else { + detailCPICs = 0; + } + methodReceiver = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + if (!((/* isOopCompiledMethod: */ + ((!(methodReceiver & (tagMask())))) + && (((byteAt((void *)(methodReceiver + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (methodHasCogMethod(methodReceiver)))) { + return (GIV(primFailCode) = PrimErrBadReceiver); + } + + /* begin eeInstantiateAnySmallClassIndex:format:numSlots: */ + assert((classAtIndex(ClassArrayCompactIndex)) != GIV(nilObj)); + assert((arrayFormat()) == (instSpecOfClass(classAtIndex(ClassArrayCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (3 * BytesPerOop); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + data = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) 3)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + GIV(freeStart) += numBytes; + data = newObj; + /* end eeInstantiateAnySmallClassIndex:format:numSlots: */ +l1: + + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((methodReceiver + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + valuePointer = positive64BitIntegerFor(((usqInt)cogMethod)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(data)) + && (!(isForwarded(data)))); + assert(validStorePointerUncheckedArgs(0, data, valuePointer)); + longAtput((void *)((data + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + if ((cogMethodData = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteArray) << (shiftForWord())))))), (cogMethod->blockSize)))) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(data)) + && (!(isForwarded(data)))); + assert(validStorePointerUncheckedArgs(1, data, cogMethodData)); + longAtput((void *)((data + BaseHeaderSize) + (1U << (shiftForWord()))),cogMethodData); + memcpy(firstIndexableField(cogMethodData), cogMethod, (cogMethod->blockSize)); + } + else { + return (GIV(primFailCode) = PrimErrNoMemory); + } + code = cogMethodImportsintodetailCPICs(cogMethod, data, detailCPICs); + if (code) { + return (GIV(primFailCode) = code); + } + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),data); + return 0; +} +#endif /* VMInvestigations */ + + +/* Answer an array of string,value pairs for all static symbols needed for + deferred disassembly. + */ + + /* CoInterpreterPrimitives>>#primitiveCogStaticSymbols */ +#if VMInvestigations +EXPORT(sqInt) +primitiveCogStaticSymbols(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *abiNameOrNil; + sqInt i; + sqInt symbols; + sqInt valuePointer; + + symbols = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 54); + valuePointer = stringForCString("wordSize"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(0, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(1, symbols, (((usqInt)BytesPerWord << 3) | 1))); + longAtput((void *)((symbols + BaseHeaderSize) + (1U << (shiftForWord()))),(((usqInt)BytesPerWord << 3) | 1)); + valuePointer = stringForCString("ISA"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(2, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (2U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString(isaName()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(3, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (3U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("ABI"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(4, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (4U << (shiftForWord()))),valuePointer); + valuePointer = ((abiNameOrNil = abiName()) + ? stringForCString(abiNameOrNil) + : GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(5, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (5U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("MULTIPLEBYTECODESETS"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(6, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (6U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(7, symbols, GIV(trueObj))); + longAtput((void *)((symbols + BaseHeaderSize) + (7U << (shiftForWord()))),GIV(trueObj)); + valuePointer = stringForCString("varBaseAddress"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(8, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (8U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(computeGoodVarBaseAddress()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(9, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (9U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("argumentCount"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(10, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (10U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(argumentCount))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(11, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (11U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("CFramePointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(12, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (12U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(CFramePointer))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(13, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (13U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("CStackPointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(14, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (14U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(CStackPointer))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(15, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (15U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("framePointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(16, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (16U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(framePointer))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(17, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (17U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("instructionPointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(18, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (18U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(instructionPointer))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(19, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (19U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("interpret"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(20, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (20U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)interpret)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(21, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (21U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("methodCache"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(22, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (22U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)GIV(methodCache))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(23, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (23U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("newMethod"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(24, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (24U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(newMethod))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(25, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (25U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("nextProfileTick"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(26, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (26U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(nextProfileTick))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(27, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (27U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("primFailCode"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(28, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (28U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(primFailCode))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(29, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (29U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("primitiveFunctionPointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(30, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (30U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&primitiveFunctionPointer)))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(0x1F, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (0x1FU << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("primTraceLog"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(32, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (32U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)GIV(primTraceLog))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(33, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (33U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("primTraceLogIndex"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(34, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (34U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(primTraceLogIndex))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(35, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (35U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("stackLimit"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(36, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (36U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(stackLimit))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(37, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (37U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("stackPointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(38, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (38U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(stackPointer))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(39, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (39U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("nil"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(40, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (40U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(41, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (41U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("false"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(42, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (42U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(falseObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(43, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (43U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("true"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(44, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (44U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(trueObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(45, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (45U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("classTableRootObj"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(46, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (46U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(hiddenRootsObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(47, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (47U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("methodZoneBase"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(48, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (48U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(getMethodZoneBase()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(49, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (49U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("checkForAndFollowForwardedPrimitiveState"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(50, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (50U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)checkForAndFollowForwardedPrimitiveState)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(51, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (51U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("ceCheckAndMaybeRetryPrimitive:"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(52, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (52U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)ceCheckAndMaybeRetryPrimitive)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(53, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (53U << (shiftForWord()))),valuePointer); + + /* these are needed because they are not annotated and hence not identified as exports by primitiveCogitMethod + this is needed to distinguish linked/unlinked sends + these two show up in Spur primitives, but maybe absolute calls, which won't show up in exports */ + for (i = 2; i <= 53; i += 1) { + if (!(longAt((void *)((symbols + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + return (GIV(primFailCode) = PrimErrNoMemory); + } + } + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),symbols); + return 0; +} +#endif /* VMInvestigations */ + + +/* Answer the contents of the code zone as an array of pair-wise element, + address in ascending + address order. Answer a string for a runtime routine or abstract label + (beginning, end, etc), + a CompiledMethod for a CMMethod, or a selector (presumably a Symbol) for a + PIC. If there is an argument and it is true, then collect inner + information about the CogMethod. */ + + /* CoInterpreterPrimitives>>#primitiveCollectCogCodeConstituents */ +static void +primitiveCollectCogCodeConstituents(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt constituents; + sqInt withDetails; + + if (GIV(argumentCount)) { + withDetails = longAt(GIV(stackPointer)); + if (!((withDetails == GIV(trueObj)) + || (withDetails == GIV(falseObj)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + withDetails = withDetails == GIV(trueObj); + } + else { + withDetails = 0; + } + constituents = cogCodeConstituents(withDetails); + if (!constituents) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + return; + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),constituents); +} + + +/* Lift the veil from a context and answer an integer describing its interior + state. Used for e.g. VM tests so they can verify they're testing what they + think they're testing. + 0 implies a vanilla heap context. + Bit 0 = is or was married to a frame + Bit 1 = is still married to a frame + Bit 2 = frame is executing machine code + Bit 3 = has machine code pc (as opposed to nil or a bytecode pc) + Bit 4 = method is currently compiled to machine code */ + + /* CoInterpreterPrimitives>>#primitiveContextXray */ +#if VMInvestigations +EXPORT(void) +primitiveContextXray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt context; + sqInt flags; + sqInt pc; + sqInt senderOop; + char *theFP; + sqInt theMethod; + + context = longAt(GIV(stackPointer)); + pc = longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + if (((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (checkIsStillMarriedContextcurrentFP(context, GIV(framePointer))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + flags = 7; + } + else { + flags = 3; + } + } + else { + flags = 1; + } + } + else { + flags = 0; + } + if (((((pc) & 7) == 1)) + && (((pc >> 3)) < 0)) { + flags = flags | 8; + } + theMethod = longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + if (/* maybeMethodHasCogMethod: */ + ((!(theMethod & (tagMask())))) + && ((((byteAt((void *)(theMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((theMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + flags = flags | 16; + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),(((usqInt)flags << 3) | 1)); +} +#endif /* VMInvestigations */ + + +/* Attempt to enter a CriticalSection/Mutex. If not owned, set the owner to + the current + process and answer false. If owned by the current process answer true. + Otherwise suspend the process. Answer if the receiver is owned by the + current process. + For simulation, if there is an argument it is taken to be the effective + activeProcess (see Process>>effectiveProcess). */ + + /* CoInterpreterPrimitives>>#primitiveEnterCriticalSection */ +static void +primitiveEnterCriticalSection(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + sqInt criticalSection; + int inInterpreter; + sqInt objOop; + sqInt owningProcess; + sqInt owningProcessIndex; + + if (GIV(argumentCount) > 0) { + /* rcvr */ + criticalSection = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + activeProc = longAt(GIV(stackPointer)); + if (/* isOopForwarded: */ + ((!(activeProc & (tagMask())))) + && ((!((longAt((void *)(activeProc))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + } + else { + /* rcvr */ + criticalSection = longAt(GIV(stackPointer)); + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + } + + /* CriticalSections are laid out like Semaphores */ + owningProcessIndex = ExcessSignalsIndex; + owningProcess = longAt((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord())))))); + if (owningProcess == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(owningProcessIndex, criticalSection, activeProc)); + assert(isNonImmediate(criticalSection)); + if (oopisGreaterThanOrEqualTo(criticalSection, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeProc & (tagMask())))) + && (oopisLessThan(activeProc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(criticalSection + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(criticalSection); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord()))))),activeProc); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(falseObj)); + return; + } + if (owningProcess == activeProc) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(trueObj)); + return; + } + + /* Arrange to answer false (unowned) when the process is resumed. */ + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(falseObj)); + + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + addLastLinktoList(activeProc, criticalSection); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSEnterCriticalSection); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); +} + + +/* Exit the critical section. + This may change the active process as a result. */ + + /* CoInterpreterPrimitives>>#primitiveExitCriticalSection */ +static void +primitiveExitCriticalSection(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt criticalSection; + int inInterpreter; + sqInt owningProcess; + sqInt owningProcessIndex; + + /* rcvr */ + criticalSection = longAt(GIV(stackPointer)); + + /* CriticalSections are laid out like Semaphores */ + owningProcessIndex = ExcessSignalsIndex; + + /* begin isEmptyList: */ + assert(!(isForwarded(criticalSection))); + if ((longAt((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(criticalSection)) + && (!(isForwarded(criticalSection)))); + assert(validStorePointerUncheckedArgs(owningProcessIndex, criticalSection, GIV(nilObj))); + longAtput((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + owningProcess = removeFirstLinkOfList(criticalSection); + + /* store check unnecessary because aSemaphore referred to owningProcess + via its FirstLinkIndex slot before owningProcess was removed. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(criticalSection)) + && (!(isForwarded(criticalSection)))); + assert(validStorePointerUncheckedArgs(owningProcessIndex, criticalSection, owningProcess)); + longAtput((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord()))))),owningProcess); + + /* Note that resume: isn't fair; it won't suspend the active process. + For fairness we must do the equivalent of a primitiveYield, but that + may break old code, so we stick with unfair resume:. */ + if (resumepreemptedYieldingIffrom(owningProcess, (!(imageHeaderFlags & 16)), CSExitCriticalSection)) { + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } + } +} + + +/* No-op for measuring performance of primitiveExternalCall/#117 linkage */ + + /* CoInterpreterPrimitives>>#primitiveExternalCallNoOp */ +EXPORT(void) +primitiveExternalCallNoOp(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* No-op for measuring performance of FastCPrimitiveFlag linkage */ + + /* CoInterpreterPrimitives>>#primitiveFastCNoOp */ +EXPORT(void) +primitiveFastCNoOp(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* No-op for measuring performance of + FastCPrimitiveFlag+FastCPrimitiveAlignForFloatsFlag linkage + */ + + /* CoInterpreterPrimitives>>#primitiveFastCNoOpAlignedForFloats */ +EXPORT(void) +primitiveFastCNoOpAlignedForFloats(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* The receiver is a compiledMethod. Clear all entries in the method lookup + cache that refer to this method, presumably because it has been redefined, + overridden or removed. + */ +/* The receiver is a compiledMethod. Clear all entries in the method lookup + cache that + refer to this method, presumably because it has been redefined, overridden + or removed. + Override to flush appropriate machine code caches also. */ + + /* CoInterpreterPrimitives>>#primitiveFlushCacheByMethod */ +static void +primitiveFlushCacheByMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + flushMethodCacheForMethod(longAt(GIV(stackPointer))); + unlinkSendsToandFreeIf(longAt(GIV(stackPointer)), 0); +} + + +/* The receiver is a message selector. Clear all entries in the method lookup + cache with this selector, presumably because an associated method has been + redefined. Override to also flush machine code caches. */ + + /* CoInterpreterPrimitives>>#primitiveFlushCacheBySelector */ +static void +primitiveFlushCacheBySelector(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt probe; + sqInt selector; + + selector = longAt(GIV(stackPointer)); + probe = 0; + for (i = 1; i <= MethodCacheEntries; i += 1) { + if ((GIV(methodCache)[probe + MethodCacheSelector]) == selector) { + GIV(methodCache)[probe + MethodCacheSelector] = 0; + } + probe += MethodCacheEntrySize; + } + unlinkSendsOfisMNUSelector(selector, selector == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))))); +} + + /* CoInterpreterPrimitives>>#primitiveFunctionPointerAddress */ +usqInt +primitiveFunctionPointerAddress(void) +{ + return ((usqInt)((&primitiveFunctionPointer))); +} + + +/* Primitive. Install the semaphore to be used for collecting long-running + primitives, + or nil if no semaphore should be used. */ + + /* CoInterpreterPrimitives>>#primitiveLongRunningPrimitiveSemaphore */ +#if LRPCheck +EXPORT(sqInt) +primitiveLongRunningPrimitiveSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + int flushState; + sqInt sema; + + if (GIV(argumentCount) != 1) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + sema = longAt(GIV(stackPointer)); + if (sema == GIV(nilObj)) { + flushState = GIV(longRunningPrimitiveCheckSemaphore); + GIV(longRunningPrimitiveCheckSemaphore) = null; + } + else { + flushState = !GIV(longRunningPrimitiveCheckSemaphore); + if (!(/* isSemaphoreOop: */ + ((!(sema & (tagMask())))) + && (((longAt((void *)(sema))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + GIV(longRunningPrimitiveCheckSemaphore) = sema; + } + + /* If we've switched checking on or off we must void machine code + (and machine code pcs in contexts) since we will start or stop setting + newMethod in machine code primitive invocations, and so generate + slightly different code from here on in. */ + if (flushState) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + activeContext = voidVMStateForSnapshotFlushingExternalPrimitivesIf(0); + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + assert((((stackValue(0)) == (nilObject())) + && (!GIV(longRunningPrimitiveCheckSemaphore))) + || (((stackValue(0)) == GIV(longRunningPrimitiveCheckSemaphore)) + && (isSemaphoreOop(sema)))); + } + voidLongRunningPrimitive("install"); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + if (flushState) { + ceInvokeInterpret(); + } + return 0; +} +#endif /* LRPCheck */ + + +/* Answer if a method is currently jitted. */ + + /* CoInterpreterPrimitives>>#primitiveMethodIsCogged */ +#if VMInvestigations +EXPORT(sqInt) +primitiveMethodIsCogged(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodOop; + sqInt boolean; + sqInt methodHeader; + char *sp; + + if (GIV(argumentCount)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + if (!(/* isOopCompiledMethod: */ + ((!((longAt(GIV(stackPointer))) & (tagMask())))) + && (((byteAt((void *)((longAt(GIV(stackPointer))) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return (GIV(primFailCode) = PrimErrBadReceiver); + } + aMethodOop = longAt(GIV(stackPointer)); + + /* begin methodHasCogMethod: */ + assert(isNonImmediate(aMethodOop)); + methodHeader = longAt((void *)((aMethodOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + boolean = (!(methodHeader & (smallIntegerTag()))); + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (boolean + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return 0; +} +#endif /* VMInvestigations */ + + /* CoInterpreterPrimitives>>#primitiveMethodPCData */ +EXPORT(sqInt) +primitiveMethodPCData(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cm; + CogMethod *cogMethod; + sqInt data; + sqInt dataSqInt; + sqInt delta; + sqInt methodHeader; + sqInt methodReceiver; + sqInt nEntries; + sqInt nSlots; + + if (GIV(argumentCount)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + methodReceiver = longAt(GIV(stackPointer)); + data = 0; + if (methodHasCogMethod(methodReceiver)) { + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((methodReceiver + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + + /* begin pcDataFor: */ + cm = (cogMethod->methodObject); + nSlots = (((byteSizeOf(cm)) - ((((/* begin literalCountOfMethodHeader: */ + assert((((((cogMethod->methodHeader))) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((((cogMethod->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop)) * 2) + (16 /* (allocationUnit * 2) */ / BytesPerOop); + dataSqInt = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), nSlots); + if (!dataSqInt) { + data = -1; + goto l1; + } + nEntries = mapPCDataForinto(cogMethod, dataSqInt); + if (!nEntries) { + data = 0; + goto l1; + } + if (nEntries < nSlots) { + /* begin shorten:toIndexableSize: */ + assert((nEntries >= 0) + && (nEntries < (lengthOf(dataSqInt)))); + delta = doShortentoIndexableSize(dataSqInt, nEntries); + assert((lengthOf(followMaybeForwarded(dataSqInt))) == nEntries); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + } + data = dataSqInt; + /* end pcDataFor: */ +l1: + if (data == -1) { + return (GIV(primFailCode) = PrimErrNoMemory); + } + } + if (!data) { + data = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 0); + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),data); + return 0; +} + + +/* Lift the veil from a method and answer an integer describing the interior + state of its machine code. + Used for e.g. VM tests so they can verify they're testing what they think + they're testing. + 0 implies a vanilla method. + Bit 0 = method might be compiled to machine code + Bit 1 = method is currently compiled to machine code + Bit 2 = is compiled frameless. + Bit 3 = method refers to young object. + Bit 4 = method too big to be jitted (more than 64k of code, or needs more + than 1.5Mb of stack space to compile) + Bit 5 = method contains unknown/unjittable bytecode + Bit 6 = method should not be jitted because it contains a primitive not to + be called from machine code (unused) */ + + /* CoInterpreterPrimitives>>#primitiveMethodXray */ +static void +primitiveMethodXray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt alreadyCogged; + CogMethod *cogMethod; + int flags; + sqInt header; + sqInt methodHeader; + sqInt methodObj; + + methodObj = longAt(GIV(stackPointer)); + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + alreadyCogged = /* maybeMethodHasCogMethod: */ + ((!((longAt(GIV(stackPointer))) & (tagMask())))) + && ((((byteAt((void *)((longAt(GIV(stackPointer))) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))))))); + flags = 1; + if (!alreadyCogged) { + cogMethod = cogselector(longAt(GIV(stackPointer)), GIV(nilObj)); + if ((cogMethod == null) + && (GIV(cogCompiledCodeCompactionCalledFor))) { + commenceCogCompiledCodeCompaction(); + cogMethod = cogselector(longAt(GIV(stackPointer)), GIV(nilObj)); + } + switch (((sqInt)cogMethod)) { + case MethodTooBig: + flags = 17; + break; + case EncounteredUnknownBytecode: + flags = 33; + break; + case ShouldNotJIT: + flags = 65; + break; + default: + assert(!(((((((sqInt)cogMethod)) >= MaxNegativeErrorCode) && ((((sqInt)cogMethod)) <= NotFullyInitialized))))); + } + } + if ((flags == 1) + && (/* maybeMethodHasCogMethod: */ + ((!((longAt(GIV(stackPointer))) & (tagMask())))) + && ((((byteAt((void *)((longAt(GIV(stackPointer))) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))))))))) { + /* begin cogMethodOf: */ + methodHeader = longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + flags = ((cogMethod->stackCheckOffset) + ? 3 + : 7); + if ((cogMethod->cmRefersToYoung)) { + flags += 8; + } + if (!alreadyCogged) { + freeCogMethod(cogMethod); + } + } + } + else { + flags = 0; + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),(((usqInt)flags << 3) | 1)); +} + + /* CoInterpreterPrimitives>>#primitiveMinimumUnusedHeadroom */ +EXPORT(void) +primitiveMinimumUnusedHeadroom(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integer; + + integer = minimumUnusedHeadroom(); + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); +} + + +/* Defined for CompiledMethods only */ + + /* CoInterpreterPrimitives>>#primitiveObjectAt */ +static void +primitiveObjectAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt integerPointer; + sqInt oop; + sqInt rawHeader; + sqInt realHeader; + sqInt thisReceiver; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + index = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + index = 0; + } + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + thisReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rawHeader = longAt((void *)((thisReceiver + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + realHeader = ((!(rawHeader & (smallIntegerTag()))) + ? ((((CogMethod *) rawHeader))->methodHeader) + : rawHeader); + if (!((index > 0) + && (index <= (((/* begin literalCountOfMethodHeader: */ + assert((((realHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((realHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + oop = (index == 1 + ? realHeader + : longAt((void *)((thisReceiver + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oop); +} + + +/* Store a literal into a CompiledMethod at the given index. Defined for + CompiledMethods only. + We assume that if the user is using this on active code then they will use + primitiveVoidVMStateForMethod to discard the machine code as required. */ + + /* CoInterpreterPrimitives>>#primitiveObjectAtPut */ +static void +primitiveObjectAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fieldIndex; + sqInt index; + sqInt newValue; + sqInt rawHeader; + sqInt realHeader; + sqInt thisReceiver; + + newValue = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((!(index & (smallIntegerTag())))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + index = (index >> 3); + thisReceiver = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(thisReceiver + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + return; + } + rawHeader = longAt((void *)((thisReceiver + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + realHeader = ((!(rawHeader & (smallIntegerTag()))) + ? ((((CogMethod *) rawHeader))->methodHeader) + : rawHeader); + if (!((index > 0) + && (index <= (((/* begin literalCountOfMethodHeader: */ + assert((((realHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((realHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (index == 1) { + if (((!(newValue & (smallIntegerTag())))) + || (((/* begin literalCountOfMethodHeader: */ + assert((((newValue) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((newValue >> 3)) & AlternateHeaderNumLiteralsMask)) != ((assert((((realHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((realHeader >> 3)) & AlternateHeaderNumLiteralsMask)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* begin isCogMethodReference: */ + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + ((((CogMethod *) rawHeader))->methodHeader = newValue); + } + else { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(thisReceiver)) + && (!(isForwarded(thisReceiver)))); + assert(validStorePointerUncheckedArgs(0, thisReceiver, newValue)); + longAtput((void *)((thisReceiver + BaseHeaderSize) + (0U << (shiftForWord()))),newValue); + } + } + else { + fieldIndex = index - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, thisReceiver, newValue)); + assert(isNonImmediate(thisReceiver)); + if (oopisGreaterThanOrEqualTo(thisReceiver, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(newValue & (tagMask())))) + && (oopisLessThan(newValue, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(thisReceiver + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(thisReceiver); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((thisReceiver + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),newValue); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),newValue); +} + + +/* Primitive. Install the semaphore to be used for profiling, + or nil if no semaphore should be used. + See also primitiveProfileStart. */ + + /* CoInterpreterPrimitives>>#primitiveProfileSemaphore */ +EXPORT(sqInt) +primitiveProfileSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt address; + sqInt classIndex; + int flushState; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt sema; + sqInt senderOop; + sqInt startObject; + char *theFP; + char *theFrame; + StackPage *thePage; + sqInt top; + + sema = longAt(GIV(stackPointer)); + if (sema == GIV(nilObj)) { + flushState = GIV(profileSemaphore); + GIV(profileSemaphore) = null; + + /* begin zeroNextProfileTick */ + GIV(nextProfileTick) = 0; + } + else { + flushState = !GIV(profileSemaphore); + if (!(/* isSemaphoreOop: */ + ((!(sema & (tagMask())))) + && (((longAt((void *)(sema))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + GIV(profileSemaphore) = sema; + } + GIV(profileProcess) = (GIV(profileMethod) = null); + + /* If we've switched profiling on or off we must void machine code + (and machine code pcs in contexts) since we will start or stop + testing the profile clock in machine code primitive invocations, + and so generate slightly different code from here on in. */ + if (flushState) { + /* begin mapActivationsOfRealPrimitivesToBytecodePCsAndFreeCoggedPrimitiveMethods */ + activeContext = divorceAllFramesSuchThat(isMachineCodeFrameForRealNonProfilingPrimitive); + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin mapToBytecodePCIfActivationOfRealNonProfilingPrimitive: */ + assert(isContextNonImm(obj)); + if (isRealNonProfilingPrimitiveMethod(longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))))) { + if (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (isWidowedContext(obj)) { + goto l2; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + externalDivorceFrameandContext(theFP, obj); + } + } + ensureContextHasBytecodePC(obj); + } + /* end mapToBytecodePCIfActivationOfRealNonProfilingPrimitive: */ +l2:; + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + unlinkSendsToMethodsSuchThatAndFreeIf(cogMethodHasRealNonProfilingPrimitive, 1); + + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + + /* If flushing led to divorce continue in the interpreter. */ + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + ceInvokeInterpret(); + } + + /* If not, work out where we are and continue */ + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + assert(((thePage->headFP)) == theFrame); + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + else { + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + return 0; +} + + +/* Put this process on the scheduler's lists thus allowing it to proceed next + time there is + a chance for processes of it's priority level. It must go to the back of + its run queue so + as not to preempt any already running processes at this level. If the + process's priority + is higher than the current process, preempt the current process. */ + + /* CoInterpreterPrimitives>>#primitiveResume */ +static void +primitiveResume(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + int inInterpreter; + sqInt proc; + + /* rcvr */ + /* Alas in Spur we need a read barrier */ + proc = longAt(GIV(stackPointer)); + + /* begin followObjField:ofObject: */ + ctxt = longAt((void *)((proc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + assert(isNonImmediate(ctxt)); + if ((!((longAt((void *)(ctxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + ctxt = fixFollowedFieldofObjectwithInitialValue(SuspendedContextIndex, proc, ctxt); + } + if (!(/* isContext: */ + ((!(ctxt & (tagMask())))) + && (((longAt((void *)(ctxt))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* We're about to switch process, either to an interpreted frame or a + machine code frame. To know whether to return or enter machine code + we have to know from whence we came. We could have come from the + interpreter, either directly or via a machine code primitive. We could have + come from machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + if (resumepreemptedYieldingIffrom(proc, (!(imageHeaderFlags & 16)), CSResume)) { + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } +} + + +/* Assign the priority of the receiver based on the argument, performing any + necessary rescheduling. + There are three main cases: + the active process + a quiesent/runnable process + a suspended/waiting process + There are scheduling implications only for the first two cases (the two + forms of runnable processes). */ + + /* CoInterpreterPrimitives>>#primitiveSetPriority */ +static void +primitiveSetPriority(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activePriority; + sqInt existingPriority; + int inInterpreter; + sqInt newPriority; + usqInt numSlots; + sqInt oop; + sqInt processList; + sqInt processLists; + sqInt receiverProcess; + sqInt scheduler; + sqInt theActiveProcess; + + scheduler = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + processLists = longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + receiverProcess = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + existingPriority = longAt((void *)((receiverProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + + /* Setting the priority when it is nil is simply instance initialization (typically from Process class>>#forContext:priority:). + If priority has any other non-integer value then there is something wrong and trhe primitive should fail. */ + if (!(((((existingPriority) & 7) == 1)) + || (existingPriority == GIV(nilObj)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + newPriority = longAt(GIV(stackPointer)); + if (!(((((newPriority) & 7) == 1)) + && (((((newPriority = (newPriority >> 3))) >= 1) && (newPriority <= ((/* begin numSlotsOf: */ + assert((classIndexOf(processLists)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(processLists + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(processLists - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + theActiveProcess = longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((theActiveProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + activePriority = (oop >> 3); + + /* assign priority for all cases. return receiver for all cases */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(receiverProcess)) + && (!(isForwarded(receiverProcess)))); + assert(validStorePointerUncheckedArgs(PriorityIndex, receiverProcess, (((usqInt)newPriority << 3) | 1))); + longAtput((void *)((receiverProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord()))))),(((usqInt)newPriority << 3) | 1)); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + + /* Setting the priority when it is nil is simply instance initialization */ + if (!((((existingPriority) & 7) == 1))) { + assert(nilObject() == existingPriority); + return; + } + existingPriority = (existingPriority >> 3); + + /* begin highestRunnablePriorityIsAtLeast: */ + if (GIV(highestRunnableProcessPriority)) { + GIV(highestRunnableProcessPriority) = ((GIV(highestRunnableProcessPriority) < newPriority) ? newPriority : GIV(highestRunnableProcessPriority)); + } + + /* We may switch process, either to an interpreted frame or a machine code frame. To + know whether to return or enter machine code we have to know from whence we came. + We could have come from the interpreter, either directly or via a machine code primitive. + We could have come from machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + + /* If setting the priority of the active process then setting the priority + higher or same is a noop, but setting it lower is like yielding. + But to avoid putting the process to sleep (creating its context etc) + first check if it is still the runnable process on lowering its priority. */ + if (theActiveProcess == receiverProcess) { + if ((newPriority < activePriority) + && ((findHighestPriority()) > newPriority)) { + processList = longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((newPriority - 1)) << (shiftForWord())))))); + addLastLinktoList(theActiveProcess, processList); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSSetPriority); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } + return; + } + + /* If receiverProcess's list is the quiesent list at its priority then it is quiescent/runnable, + otherwise receiverProcess is either suspended (myList = nil) or waiting on a condition + variable. If receiverProcess is quiescent then if setting the priority to less or equal to + the active priority then this is a noop, but if setting higher then effectively the active + process yields to the receiverProcess. */ + if (((longAt((void *)((receiverProcess + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))))) == (longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((existingPriority - 1)) << (shiftForWord())))))))) + && (newPriority > activePriority)) { + processList = longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((activePriority - 1)) << (shiftForWord())))))); + addLastLinktoList(theActiveProcess, processList); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSSetPriority); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } + return; +} + + +/* Synchronously signal the semaphore. + This may change the active process as a result. */ +/* We may be about to switch process, either to an interpreted frame or a + machine code frame. To know whether to return or enter machine code + we have to know from whence we came. We could have come from the + interpreter, either directly or via a machine code primitive. We could + have come from machine code. The instructionPointer tells us where from: */ + + /* CoInterpreterPrimitives>>#primitiveSignal */ +static void +primitiveSignal(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int inInterpreter; + + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + if (synchronousSignal(longAt(GIV(stackPointer)))) { + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } +} + + +/* Save a normal snapshot under the same name as it was loaded + unless it has been renamed by the last primitiveImageName. + Note that when executed this primitive answers false, but when the + resulting image is run afresh, the primitive answers true. + + Override to jump to the interpreter because the machine code zone is now + void. + */ + + /* CoInterpreterPrimitives>>#primitiveSnapshot */ +static void +primitiveSnapshot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + snapshot(0); + if ((longAt(GIV(framePointer) + FoxMethod)) == GIV(newMethod)) { + /* snapshot: has reached the end and built a frame. + In the JIT we need to back-up the pc before reentering the interpreter. */ + GIV(instructionPointer) -= 1; + } + ceInvokeInterpret(); +} + + +/* Save an embedded snapshot. + Note that when executed this primitive answers false, but when the + resulting image is run afresh, the primitive answers true. + + Override to jump to the interpreter because the machine code zone is now + void. + */ + + /* CoInterpreterPrimitives>>#primitiveSnapshotEmbedded */ +static void +primitiveSnapshotEmbedded(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + snapshot(1); + if ((longAt(GIV(framePointer) + FoxMethod)) == GIV(newMethod)) { + /* snapshot: has reached the end and built a frame. + In the JIT we need to back-up the pc before reentering the interpreter. */ + GIV(instructionPointer) -= 1; + } + ceInvokeInterpret(); +} + + +/* Primitive #88. Suspend the receiver, aProcess, such that it can be + executed again + by sending #resume. If the given process is not the active process, take + it off + its corresponding list. The primitive returns the list the receiver was + previously on. + c.f. primitiveSuspendBackingUpV1,#568 & primitiveSuspendBackingUpV2,#578 */ + + /* CoInterpreterPrimitives>>#primitiveSuspend */ +static void +primitiveSuspend(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int inInterpreter; + sqInt myContext; + sqInt myList; + usqInt numSlots; + sqInt ok; + sqInt process; + + process = longAt(GIV(stackPointer)); + if (process == (activeProcess())) { + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(nilObj)); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSSuspend); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + return; + } + myList = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord())))))); + myContext = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if (!((/* isPointers: */ + ((!(myList & (tagMask())))) + && (((byteAt((void *)(myList + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(myList)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(myList + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(myList - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > LastLinkIndex) + && ((((longAt((void *)(myContext))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((/* begin isResumableContext: */ + assert(isContext(myContext)), + ((((longAt((void *)((myContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + ok = removeProcessfromList(process, myList); + if (!ok) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrOperationFailed; + return; + } + + /* stackTopPut: */ + longAtput(GIV(stackPointer),myList); +} + + +/* Primitive #568. Suspend the receiver, aProcess, such that it can be + executed again + by sending #resume. If the given process is not the active process, take + it off + its corresponding list. If the list was not its run queue assume it was on + some condition variable (Semaphore, Mutex) and back up its pc to the send + that invoked the wait state the process entered. Hence when the process + resumes it will reenter the wait state. Answer the list the receiver was + previously on, + unless it was the activeProcess, in which case answer nil. + c.f. primitiveSuspend,#88 & primitiveSuspendBackingUpV2,#578 */ + + /* CoInterpreterPrimitives>>#primitiveSuspendBackingUpV1 */ +static void +primitiveSuspendBackingUpV1(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int inInterpreter; + sqInt myContext; + sqInt myList; + usqInt numSlots; + sqInt ok; + sqInt process; + + process = longAt(GIV(stackPointer)); + if (process == (activeProcess())) { + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(nilObj)); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSSuspend); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + return; + } + myList = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord())))))); + myContext = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if (!((/* isPointers: */ + ((!(myList & (tagMask())))) + && (((byteAt((void *)(myList + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(myList)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(myList + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(myList - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > LastLinkIndex) + && ((((longAt((void *)(myContext))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((/* begin isResumableContext: */ + assert(isContext(myContext)), + ((((longAt((void *)((myContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + ok = removeProcessfromList(process, myList); + if (!ok) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrOperationFailed; + return; + } + if (((longAt((void *)(myList))) & (classIndexMask())) != GIV(classLinkedListClassTag)) { + backupContexttoBlockingSendTo(myContext, myList); + } + + /* stackTopPut: */ + longAtput(GIV(stackPointer),myList); +} + + +/* Primitive #578. Suspend the receiver, aProcess, such that it can be + executed again + by sending #resume. If the given process is not the active process, take + it off + its corresponding list. If the list was not its run queue assume it was on + some condition variable (Semaphore, Mutex) and back up its pc to the send + that invoked the wait state the process entered. Hence when the process + resumes it will reenter the wait state. Answer the list the receiver was + previously on iff + it was not active and not blocked, otherwise answer nil. + c.f. primitiveSuspend,#88 & primitiveSuspendBackingUpV1,#568, + which always answer the list the process was on, even if blocked. */ + + /* CoInterpreterPrimitives>>#primitiveSuspendBackingUpV2 */ +EXPORT(void) +primitiveSuspendBackingUpV2(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int inInterpreter; + sqInt myContext; + sqInt myList; + usqInt numSlots; + sqInt ok; + sqInt process; + + process = longAt(GIV(stackPointer)); + if (process == (activeProcess())) { + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(nilObj)); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSSuspend); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + return; + } + myList = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord())))))); + myContext = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if (!((/* isPointers: */ + ((!(myList & (tagMask())))) + && (((byteAt((void *)(myList + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(myList)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(myList + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(myList - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > LastLinkIndex) + && ((((longAt((void *)(myContext))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((/* begin isResumableContext: */ + assert(isContext(myContext)), + ((((longAt((void *)((myContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + ok = removeProcessfromList(process, myList); + if (!ok) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrOperationFailed; + return; + } + if (((longAt((void *)(myList))) & (classIndexMask())) != GIV(classLinkedListClassTag)) { + backupContexttoBlockingSendTo(myContext, myList); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(nilObj)); + } + else { + /* stackTopPut: */ + longAtput(GIV(stackPointer),myList); + } +} + + +/* Primitive. Terminate up the context stack from the receiver up to but not + including the argument, if previousContext is on my Context stack. Make + previousContext my + sender. This prim has to shadow the code in ContextPart>terminateTo: to be + correct. + Override to ensure the caller's saved ip is correct, i.e. if an + interpreter frame it may + have to move to iframeSavedIP. */ + + /* CoInterpreterPrimitives>>#primitiveTerminateTo */ +static void +primitiveTerminateTo(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aContextOrNil; + char *callerFP; + char *callerFP1; + char *contextsFP; + usqInt contextsIP; + char *contextsSP; + sqInt currentCtx; + char *fp; + char *fp1; + char *frameAbove; + sqInt handlerOrNilOrZero; + char *newFP; + char *newSP; + sqInt nextCntx; + StackPage *pageToStopOn; + sqInt senderOop; + sqInt senderOopSqInt; + char *source; + sqInt stackedReceiverOffset; + char *theFP; + char *theFP1; + StackPage *thePage; + StackPage *thePage1; + sqInt thisCtx; + sqInt valuePointer; + + contextsFP = ((char *) 0); + aContextOrNil = longAt(GIV(stackPointer)); + if (!((aContextOrNil == GIV(nilObj)) + || (/* isContext: */ + ((!(aContextOrNil & (tagMask())))) + && (((longAt((void *)(aContextOrNil))) & (classIndexMask())) == ClassMethodContextCompactIndex)))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + thisCtx = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (thisCtx == aContextOrNil) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* If we're searching for aContextOrNil it might be on a stack page. Helps to know + if we can free a whole page or not, or if we can short-cut the termination. */ + if ((aContextOrNil != GIV(nilObj)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContextOrNil))))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + contextsFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + pageToStopOn = stackPageAtpages(pageIndexFor(contextsFP), GIV(pages)); + } + else { + pageToStopOn = 0; + } + + /* if thisCtx is married ensure it is a base frame. Then we can assign its sender. */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(thisCtx)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* Optimize terminating thisContext. Move its frame down to be next to + aContextOrNil's frame. Common in the exception system and so helps to be fast. */ + if ((theFP == GIV(framePointer)) + && (pageToStopOn == GIV(stackPage))) { + assertValidStackedInstructionPointersInline(GIV(stackPage), __LINE__); + if ((((char *)(longAt(theFP + FoxSavedFP)))) != contextsFP) { + stackedReceiverOffset = (FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))); + + /* begin findFrameAbove:inPage: */ + callerFP = ((char *) 0); + fp = (pageToStopOn->headFP); + if (fp == contextsFP) { + frameAbove = 0; + goto l1; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == contextsFP) { + frameAbove = fp; + goto l1; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + frameAbove = 0; + /* end findFrameAbove:inPage: */ +l1: + contextsIP = longAt(frameAbove + FoxCallerSavedIP); + assert((((((usqInt)contextsIP)) >= (startOfMemory())) + || (contextsIP == (ceReturnToInterpreterPC()))) == (!(isMachineCodeFrame(contextsFP)))); + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(frameAbove))); + newSP = (frameAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(frameAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(frameAbove))->cmNumArgs) + : byteAt((frameAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + newFP = (newSP - stackedReceiverOffset) - BytesPerWord; + for (source = (theFP + stackedReceiverOffset); source >= GIV(stackPointer); source += (-BytesPerWord)) { + newSP -= BytesPerWord; + longAtput(newSP,longAt(source)); + } + longAtput(newFP + FoxSavedFP,((usqInt)contextsFP)); + + /* Ensure contract between machine-code callee and interpreter caller frames is preserved. + Return pc needs to be ceReturnToInterpreterPC. */ + if (((((usqInt)(longAt(newFP + FoxMethod)))) < (startOfMemory())) + && (contextsIP >= (startOfMemory()))) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(contextsFP))); + longAtput(contextsFP + FoxIFSavedIP,contextsIP); + contextsIP = ceReturnToInterpreterPC(); + } + longAtput(newFP + FoxCallerSavedIP,contextsIP); + assert(isContext(thisCtx)); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(newFP)) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(newFP)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(thisCtx)) + && (!(isForwarded(thisCtx)))); + assert(validStorePointerUncheckedArgs(SenderIndex, thisCtx, valuePointer)); + longAtput((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),valuePointer); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(contextsFP)) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(contextsFP)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(thisCtx)) + && (!(isForwarded(thisCtx)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, thisCtx, valuePointer)); + longAtput((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointer); + GIV(framePointer) = newFP; + GIV(stackPointer) = newSP; + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + assertValidStackedInstructionPointersInline(GIV(stackPage), __LINE__); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + return; + } + assertValidStackedInstructionPointers(__LINE__); + + /* May cause a GC!! */ + theFP = externalEnsureIsBaseFrame(theFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + + /* May also reclaim aContextOrNil's page, hence... */ + if ((aContextOrNil != GIV(nilObj)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContextOrNil))))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + contextsFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + pageToStopOn = stackPageAtpages(pageIndexFor(contextsFP), GIV(pages)); + } + else { + pageToStopOn = 0; + } + } + else { + currentCtx = longAt((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + } + assertValidStackedInstructionPointers(__LINE__); + + /* begin context:hasSender: */ + handlerOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(-1, thisCtx, aContextOrNil); + if (!handlerOrNilOrZero) { + /* Need to walk the stack freeing stack pages and nilling contexts. */ + while (!((currentCtx == aContextOrNil) + || (currentCtx == GIV(nilObj)))) { + assert(isContext(currentCtx)); + if (((((longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + + /* If externalEnsureIsBaseFrame: above has moved thisContext to its own stack + then we will always terminate to a frame on a different page. But if we are + terminating some other context to a context somewhere on the current page + we must save the active frames above that context. Things will look e.g. like this: + thisCtx 499383332 s MethodContext(ContextPart)>resume: + 499380484 s BlockClosure>ensure: + 499377320 s MethodContext(ContextPart)>handleSignal: + 499373760 s MethodContext(ContextPart)>handleSignal: + 499372772 s MessageNotUnderstood(Exception)>signal + 499369068 s CodeSimulationTests(Object)>doesNotUnderstand: absentMethod + 499368708 s [] in CodeSimulationTests>testDNU + (sender is 0xbffc2480 I CodeSimulationTests>runSimulated:) + ------------ + framePointer 0xbffc234c M MethodContext(ContextPart)>doPrimitive:method:receiver:args: + 0xbffc2378 M MethodContext(ContextPart)>tryPrimitiveFor:receiver:args: + 0xbffc23ac M MethodContext(ContextPart)>send:to:with:super: + 0xbffc23e4 M MethodContext(ContextPart)>send:super:numArgs: + 0xbffc2418 M MethodContext(InstructionStream)>interpretNextInstructionFor: + 0xbffc2434 M MethodContext(ContextPart)>step + 0xbffc2458 I MethodContext(ContextPart)>runSimulated:contextAtEachStep: + ------------ + (499368708's sender) 0xbffc2480 I CodeSimulationTests>runSimulated: + 0xbffc249c M CodeSimulationTests>testDNU + 0xbffc24bc I CodeSimulationTests(TestCase)>performTest + 0xbffc24dc I [] in CodeSimulationTests(TestCase)>runCase + aContextOrNil 0xbffc24fc M BlockClosure>ensure: + 0xbffc2520 I CodeSimulationTests(TestCase)>runCase + 0xbffc253c M [] in TestResult>runCase: + When we find this case we move the frames above to a new page by making the + frame above currentCtx a base frame, i.e. making 0xbffc2458 in the above example + a base frame. But in this iteration of the loop we don't move down a frame i.e. currentCtx + doesn't change on this iteration. */ + if (thePage == GIV(stackPage)) { + /* begin findFrameAbove:inPage: */ + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == theFP) { + frameAbove = 0; + goto l2; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == theFP) { + frameAbove = fp; + goto l2; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + frameAbove = 0; + /* end findFrameAbove:inPage: */ +l2: + assert(frameAbove != 0); + + /* May cause a GC!! May also reclaim aContextOrNil's page, hence... */ + frameAbove = externalEnsureIsBaseFrame(frameAbove); + if ((aContextOrNil != GIV(nilObj)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContextOrNil))))) { + /* begin frameOfMarriedContext: */ + senderOopSqInt = longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOopSqInt) & 7) == 1)); + contextsFP = ((char *)(senderOopSqInt - (smallIntegerTag()))); + + /* begin stackPageFor: */ + pageToStopOn = stackPageAtpages(pageIndexFor(contextsFP), GIV(pages)); + } + else { + pageToStopOn = 0; + } + } + else { + if (thePage == pageToStopOn) { + /* We're here. Cut back the stack to aContextOrNil's frame, + push its instructionPointer if it's not already a head frame, + and we're done. */ + + /* begin findFrameAbove:inPage: */ + callerFP1 = ((char *) 0); + fp1 = (thePage->headFP); + if (fp1 == contextsFP) { + frameAbove = 0; + goto l3; + } + while (((callerFP1 = ((char *)(longAt(fp1 + FoxSavedFP))))) != 0) { + if (callerFP1 == contextsFP) { + frameAbove = fp1; + goto l3; + } + fp1 = callerFP1; + } + error("did not find theFP in stack page"); + frameAbove = 0; + /* end findFrameAbove:inPage: */ +l3: + if (frameAbove) { + contextsSP = ((/* begin frameCallerSP: */ + assert(!(isBaseFrame(frameAbove))), +(frameAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(frameAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(frameAbove))->cmNumArgs) + : byteAt((frameAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)) - BytesPerWord; + longAtput(contextsSP,longAt(frameAbove + FoxCallerSavedIP)); + + /* begin setHeadFP:andSP:inPage: */ + assert(contextsSP < contextsFP); + assert((contextsSP < ((thePage->baseAddress))) + && (contextsSP > (((thePage->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((contextsFP < ((thePage->baseAddress))) + && (contextsFP > (((thePage->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (thePage->headFP = contextsFP); + (thePage->headSP = contextsSP); + } + currentCtx = aContextOrNil; + } + else { + /* We can free the entire page without further ado. */ + theFP1 = (thePage->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP1)); + thePage1 = stackPageAtpages(pageIndexFor(theFP1), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + + /* for a short time invariant is violated; assert follows */ + freeStackPageNoAssert(thePage); + } + } + } + else { + nextCntx = longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + + /* begin markContextAsDead: */ + assert(isContext(currentCtx)); + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(SenderIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + currentCtx = nextCntx; + } + } + } + assert(pageListIsWellFormed()); + if (((((longAt((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert(checkIsStillMarriedContextcurrentFP(thisCtx, GIV(framePointer))); + assert(isBaseFrame(frameOfMarriedContext(thisCtx))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin frameCallerContext:put: */ + assert((aContextOrNil == (nilObject())) + || (isContext(aContextOrNil))); + assert(isBaseFrame(theFP)); + assert(((theFP + (frameStackedReceiverOffset(theFP))) + (2 * BytesPerWord)) == (((stackPageFor(theFP))->baseAddress))); + assert((longAt((theFP + (frameStackedReceiverOffset(theFP))) + BytesPerWord)) == (frameContext(theFP))); + longAtput((theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + (2 * BytesPerWord),aContextOrNil); + } + else { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SenderIndex, thisCtx, aContextOrNil)); + assert(isNonImmediate(thisCtx)); + if (oopisGreaterThanOrEqualTo(thisCtx, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(aContextOrNil & (tagMask())))) + && (oopisLessThan(aContextOrNil, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(thisCtx + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(thisCtx); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),aContextOrNil); + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + assertValidStackedInstructionPointers(__LINE__); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); +} + + +/* Primitive. Unload the module with the given name. + Reloading of the module will happen *later* automatically, when a + function from it is called. This is forced by invalidating all external + primitive methods and activations in flushExternalPrimitives. + N.B. since this is most likely a development time activity we don't care + about performance. */ + + /* CoInterpreterPrimitives>>#primitiveUnloadModule */ +static void +primitiveUnloadModule(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt moduleLength; + sqInt moduleName; + usqInt numSlots; + + moduleName = longAt(GIV(stackPointer)); + if (!(/* isBytes: */ + ((!(moduleName & (tagMask())))) + && (((byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + moduleLength = ((((/* begin numSlotsOf: */ + assert((classIndexOf(moduleName)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(moduleName + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(moduleName - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (!(ioUnloadModuleOfLength(oopForPointer(firstIndexableField(moduleName)), moduleLength))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + if (/* object:equalsString:ofSize: */ + (/* isBytes: */ + ((!(moduleName & (tagMask())))) + && (((byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((!(((byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((numBytesOfBytes(moduleName)) == moduleLength) + && ((strncmp("SqueakFFIPrims", firstIndexableField(moduleName), moduleLength)) == 0)))) { + primitiveCalloutPointer = ((void *) -1); + } + + /* N.B. flushExternalPrimitives continues. Do *not* do anything after flushExternalPrimitives in the CoInterpreter */ + forceInterruptCheck(); + flushExternalPrimitives(); +} + + +/* Void all internal VM state in the stack and machine code zones + + Override to jump to the interpreter because the machine code zone is now + void. + */ + + /* CoInterpreterPrimitives>>#primitiveVoidVMState */ +static void +primitiveVoidVMState(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + activeContext = voidVMStateForSnapshotFlushingExternalPrimitivesIf(0); + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + ceInvokeInterpret(); +} + + +/* The receiver (or first argument) must be a compiledMethod. The optional + (or second) argument must be a + boolean. Clear all VM state associated with the method, including any + machine code, or machine code pcs + in context objects. If the optional boolean argument is false do not scan + the heap looking for contexts. */ + + /* CoInterpreterPrimitives>>#primitiveVoidVMStateForMethod */ +static void +primitiveVoidVMStateForMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt address; + StackPage *aPage; + char *calleeFP; + sqInt classIndex; + CogMethod *cogMethod; + sqInt divorcedSome; + sqInt followingWord; + usqInt followingWordAddress; + int hasCogMethod; + sqInt i; + sqInt methodHeader; + sqInt methodObj; + usqInt numSlots; + sqInt obj; + sqInt object; + sqInt prevObj; + sqInt prevPrevObj; + sqInt scanHeapForContexts; + sqInt senderOop; + sqInt startObject; + sqInt theContext; + char *theFP; + char *theFrame; + StackPage *thePage; + char *theSP; + sqInt top; + + /* See comment ''One might think...'' below */ + /* In Smalltalk allow both aMethod voidCogVMState and aMethod voidCogVMStateScanningContextsIf: aBoolean */ + scanHeapForContexts = 1; + if (GIV(argumentCount)) { + methodObj = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin booleanValueOf: */ + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + scanHeapForContexts = 1; + goto l1; + } + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + scanHeapForContexts = 0; + goto l1; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + scanHeapForContexts = null; + /* end booleanValueOf: */ +l1: + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + else { + methodObj = longAt(GIV(stackPointer)); + } + if (GIV(argumentCount) > 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + + /* In the NewspeakVM we allow VMMirror voidStateFor: method scanningIf: aBoolean as well as the Smalltalk forms. */ + flushMethodCacheForMethod(methodObj); + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l2; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l2: + + /* begin ensurePushedInstructionPointer */ + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if ((hasCogMethod = methodHasCogMethod(methodObj))) { + /* begin divorceMachineCodeFramesWithMethod: */ + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + do { + if (GIV(stackPage)) { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + + /* This is needed for the assert in externalDivorceFrame:andContext: + Slang can't currently cope with the lack of the variable here. + Something to do with the preceding statement. Take it out + and the code is good. leave it in and we get do { ... } while(l1:) */ + + /* begin divorceSomeMachineCodeFramesWithMethod: */ + divorcedSome = 0; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + aPage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(aPage))) { + /* this to avoid assert in externalDivorceFrame:andContext: */ + markStackPageMostRecentlyUsed(GIV(stackPage)); + + /* begin divorceAMachineCodeFrameWithCogMethod:in: */ + calleeFP = ((char *) 0); + theFP = (aPage->headFP); + theSP = (aPage->headSP); + + /* theSP points at hottest item on frame's stack */ + theSP += BytesPerWord; + while (1) { + if (((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && (cogMethod == (mframeHomeMethod(theFP)))) { + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + theContext = longAt(theFP + FoxThisContext); + goto l6; + } + theContext = marryFrameSP(theFP, theSP); + /* end ensureFrameIsMarried:SP: */ +l6: + externalDivorceFrameandContext(theFP, theContext); + goto l4; + } + calleeFP = theFP; + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!(theFP != 0)) break; + /* theSP points at stacked hottest item on frame's stack */ + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(calleeFP))); + theSP = (calleeFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(calleeFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(calleeFP))->cmNumArgs) + : byteAt((calleeFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + goto l5; +l4: + divorcedSome = 1; +l5:; + } + } + } while(divorcedSome); + } + + /* One might think (as this author did) that the heap scan is unnecessary if the method does not + have a cog method. But it could be the case that the code zone has recently been reclaimed + and so not having a cog method is no indication that it didn't have a cog method some time in + the recent past, and that there are indeed still contexts with machine code pcs out there. The + only steps that can be avoided are divorcing frames in the stack zone, and scanning to unlink and + free if there isn't a cog method, unless we are told otherwise. */ + if (scanHeapForContexts) { + /* begin ensureAllContextsWithMethodHaveBytecodePCs: */ + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord()))))))) == methodObj)) { + widowOrForceToBytecodePC(obj); + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l3: + assert(oopisGreaterThan(obj, prevObj)); + } + } + if (hasCogMethod) { + unlinkSendsToandFreeIf(methodObj, 1); + } + + /* If flushing led to divorce continue in the interpreter. */ + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + assert((methodObj == (stackTop())) + || ((GIV(argumentCount) > 0) + && (methodObj == (stackValue(1))))); + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + ceInvokeInterpret(); + } + + /* If not, work out where we are and continue */ + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + assert(((thePage->headFP)) == theFrame); + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + assert((methodObj == (stackTop())) + || ((GIV(argumentCount) > 0) + && (methodObj == (stackValue(1))))); + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + /* CoInterpreterPrimitives>>#primitiveWait */ +static void +primitiveWait(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + sqLong excessSignals; + int inInterpreter; + sqInt objOop; + sqInt sema; + + /* rcvr */ + sema = longAt(GIV(stackPointer)); + excessSignals = fetchIntegerofObject(ExcessSignalsIndex, sema); + if (excessSignals > 0) { + storeIntegerofObjectwithValue(ExcessSignalsIndex, sema, excessSignals - 1); + } + else { + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + addLastLinktoList(activeProc, sema); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSEnterCriticalSection); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } +} + + +/* Primitively do the equivalent of Process>yield, avoiding the overhead of a + fork and a wait in the standard implementation. + Get the Processor object (1), from it get the activeProcess (2) and hence + the activeProcess's priority (3), and the array of + all runnable processes (5). Via the priority get the list of quiescent + processes at the active priority (6) (implicitly we know + there are no quiescent processes at higher priorities than the + activeProcess which is always one of the highest priority + runnable processes). */ +/* 1 */ + + /* CoInterpreterPrimitives>>#primitiveYield */ +static void +primitiveYield(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + int inInterpreter; + sqInt oop; + sqInt priority; + sqInt processList; + sqInt processLists; + sqInt scheduler; + + scheduler = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* 2 */ + activeProc = longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* 3 */ + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + priority = (oop >> 3); + + /* 4 */ + processLists = longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + + /* 5 */ + processList = longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((priority - 1)) << (shiftForWord())))))); + + /* If the list of quiescent processes at this priority is empty there are no processes to yield to. + If the list is not empty, activate the first one, and add the active process to the end of the list. */ + + /* begin isEmptyList: */ + assert(!(isForwarded(processList))); + if ((longAt((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj)) { + return; + } + + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + addLastLinktoList(activeProc, processList); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSYield); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); +} + + /* CoInterpreterPrimitives>>#unmarkAllFrames */ +static void +unmarkAllFrames(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt flags; + sqInt i; + sqInt methodField; + char *theFP; + StackPage *thePage; + + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + theFP = (thePage->headFP); + do { + methodField = longAt(theFP + FoxMethod); + if ((((usqInt)methodField)) < (startOfMemory())) { + if (methodField & 4) { + longAtput(theFP + FoxMethod,methodField - 4); + } + } + else { + flags = longAt(theFP + FoxIFrameFlags); + if (flags & 2) { + longAtput(theFP + FoxIFrameFlags,flags - 2); + } + } + } while(((theFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0); + } + } +} + + +/* Initialize the stack pages. In the C VM theStackPages will be alloca'ed + memory to hold the + stack pages on the C stack. In the simulator they are housed in the memory + between the + cogMethodZone and the heap. */ + + /* CoInterpreterStackPages>>#initializeStack:numSlots:pageSize: */ +static NoDbgRegParms void +initializeStacknumSlotspageSize(char *theStackPages, sqInt stackSlots, sqInt slotsPerPage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt count; + sqInt index; + sqInt numPages; + StackPage *page; + char *pageStructBase; + void *pointer; + usqIntptr_t structStackPageSize; + sqInt theIndex; + + structStackPageSize = sizeof(CogStackPage); + GIV(bytesPerPage) = slotsPerPage * BytesPerWord; + numPages = GIV(numStackPages); + + /* Because stack pages grow down baseAddress is at the top of a stack page and so to avoid + subtracting BytesPerWord from baseAddress and lastAddress in the init loop below we simply + push the stackPage array up one word to avoid the overlap. This word is extraStackBytes. */ + pageStructBase = (theStackPages + (numPages * GIV(bytesPerPage))) + BytesPerWord; + GIV(pages) = ((StackPage *) pageStructBase); + + /* make sure there's enough headroom */ + assert((((stackPageByteSize()) - (stackLimitBytes())) - (stackLimitOffset())) >= (stackPageHeadroom())); + for (index = 0; index < numPages; index += 1) { + /* begin stackPageAt: */ + page = stackPageAtpages(index, GIV(pages)); + (page->lastAddress = theStackPages + (index * GIV(bytesPerPage))); + (page->baseAddress = ((page->lastAddress)) + GIV(bytesPerPage)); + (page->stackLimit = ((page->baseAddress)) - ((((stackPageFrameBytes()) < (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))) ? (stackPageFrameBytes()) : (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))))); + (page->realStackLimit = (page->stackLimit)); + (page->baseFP = 0); + (page->nextPage = stackPageAt((index == (numPages - 1) + ? 0 + : index + 1))); + (page->prevPage = stackPageAt((index + ? index - 1 + : numPages - 1))); + } + + /* Now compute stackBasePlus1 so that the pageIndexFor: call maps all addresses from + aPage baseAddress to aBase limitAddress + 1 to the same index (stacks grow down) */ + GIV(stackBasePlus1) = (((char *) theStackPages)) + 1; + + /* The overflow limit is the amount of stack to retain when moving frames from an overflowing + stack to reduce thrashing. See stackOverflowOrEvent:mayContextSwitch: */ + + /* begin stackPageAt: */ + page = stackPageAtpages(0, GIV(pages)); + GIV(overflowLimit) = ((((page->baseAddress)) - ((page->realStackLimit))) * 3) / 5; + for (index = 0; index < numPages; index += 1) { + /* begin stackPageAt: */ + page = stackPageAtpages(index, GIV(pages)); + assert((pageIndexFor((page->baseAddress))) == index); + assert((pageIndexFor(((page->baseAddress)) - ((slotsPerPage - 1) * BytesPerWord))) == index); + assert((stackPageFor((page->baseAddress))) == page); + assert((stackPageFor((page->stackLimit))) == page); + + /* begin initializePageTraceToInvalid: */ + (page->trace = StackPageTraceInvalid); + } + GIV(mostRecentlyUsedPage) = stackPageAtpages(0, GIV(pages)); + page = GIV(mostRecentlyUsedPage); + count = 0; + do { + count += 1; + pointer = (page->baseAddress); + + /* begin pageIndexFor: */ +# if !NDEBUG + assert((((((char *) pointer)) >= (GIV(stackBasePlus1) - 1)) && ((((char *) pointer)) <= (((char *) GIV(pages)))))); +# endif // !NDEBUG + + + /* this to avoid an annoying warning: left operand of comma operator has no effect */ + theIndex = pageIndexForstackBasePlus1bytesPerPage(pointer, GIV(stackBasePlus1), GIV(bytesPerPage)); + assert((stackPageAt(theIndex)) == page); + assert((pageIndexFor((page->baseAddress))) == theIndex); + assert((pageIndexFor((page->stackLimit))) == theIndex); + assert((pageIndexFor(((page->lastAddress)) + 1)) == theIndex); + } while(((page = (page->nextPage))) != GIV(mostRecentlyUsedPage)); + assert(count == numPages); + assert(pageListIsWellFormed()); +} + + +/* */ +/* Answer the page index for a pointer into stack memory, i.e. the index + for the page the address is in. N.B. This is a zero-relative index. */ + + /* CoInterpreterStackPages>>#pageIndexFor: */ +static NoDbgRegParms sqInt +pageIndexFor(void *pointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + +# if !NDEBUG + assert((((((char *) pointer)) >= (GIV(stackBasePlus1) - 1)) && ((((char *) pointer)) <= (((char *) GIV(pages)))))); +# endif + + + /* this to avoid an annoying warning: left operand of comma operator has no effect */ + return pageIndexForstackBasePlus1bytesPerPage(pointer, GIV(stackBasePlus1), GIV(bytesPerPage)); +} + + +/* If anOop is an address within the stack zone answer a string stating that, + otherwise answer nil. + */ + + /* CoInterpreterStackPages>>#whereIsMaybeStackThing: */ +static NoDbgRegParms char * +whereIsMaybeStackThing(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (oopisGreaterThanOrEqualToandLessThan(anOop, GIV(stackBasePlus1) - 1, GIV(pages))) { + return " is in the stack zone"; + } + return null; +} + + +/* Answer either a malloced string with the null-terminated contents of oop + if oop is a string, + or the null pointer if oop is nil, or fail. It is the client's + responsibility to free the string later. */ + + /* InterpreterPrimitives>>#cStringOrNullFor: */ +char * +cStringOrNullFor(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + char *cString; + sqInt fmt; + sqInt isString; + sqInt len; + usqInt numSlots; + usqInt numSlotsUsqInt; + + /* begin isInstanceOfClassByteString: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((oop & (tagMask())) != 0)) { + isString = 0; + goto l2; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + isString = ClassByteStringCompactIndex == ccIndex; + /* end isInstanceOfClassByteString: */ +l2: + if (!isString) { + if (oop != GIV(nilObj)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + return 0; + } + + /* begin lengthOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + len = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + len = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + len = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + len = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + len = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + len = 0; + /* end lengthOf: */ +l1: + if (!len) { + return 0; + } + cString = malloc(len + 1); + if (!cString) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoCMemory; + return 0; + } + memcpy(cString, firstIndexableField(oop), len); + cString[len] = 0; + return cString; +} + + +/* In C, non-zero is true, so avoid computation by simply answering + primFailCode in the C version. + */ + + /* InterpreterPrimitives>>#failed */ +sqInt +failed(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(primFailCode); +} + + +/* Answer the identity hash of an object, assigning a hash if it doesn't have + one. On Spur refuse to assign a hash to something that looks like a + behavior. + */ + + /* InterpreterPrimitives>>#identityHashOf: */ +sqInt +identityHashOf(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt hash; + usqInt hashUsqInt; + + if (((anOop & (tagMask())) != 0)) { + /* begin immediateAsInteger:ifFail: */ + if ((((anOop) & 7) == 1)) { + return ((usqInt) ((anOop >> 3))); + } + if (((anOop & (characterTag())) != 0)) { + return ((((usqInt)anOop))) >> (numTagBits()); + } + if (((anOop & (smallFloatTag())) != 0)) { + /* begin rotatedFloatBitsOf: */ + assert(isImmediateFloat(anOop)); + return (((((usqInt)anOop))) >> ((numTagBits()) + 1)) + ((((anOop & (1U << (numTagBits()))) != 0) + ? ((((usqInt)-1)) << ((64 - (numTagBits())) - 1)) + : 0)); + } + return ((usqInt) -1); + } + if ((((long32At((void *)(anOop + 4))) & (identityHashHalfWordMask())) == 0) + && (objCouldBeClassObj(anOop))) { + return -1; + } + + /* begin hashBitsOf: */ + hash = (long32At((void *)(anOop + 4))) & (identityHashHalfWordMask()); + if (!hash) { + /* cb 1/19/2017 18:34: + would like to assert + self assert: (coInterpreter addressCouldBeClassObj: objOop) not + but instance-specific behaviors that are instances of themselves may + fail this test. + eem 12/28/2021 11:52 I'm not sure this is the issue. The issue is only validating objOop as a behavior. */ + + /* begin newHashBitsOf: */ + /* begin newObjectHash */ + assert(!((GIV(lastHash) == 0))); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 2)); + GIV(lastHash) = GIV(lastHash) ^ ((((GIV(lastHash) << 7))) & (identityHashHalfWordMask())); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 3)); + hashUsqInt = GIV(lastHash); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(anOop + 4),((((long32At((void *)(anOop + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hashUsqInt & (identityHashHalfWordMask()))); + hash = hashUsqInt & (identityHashHalfWordMask()); + } + return hash; +} + + +/* N.B. Because Slang always inlines is:instanceOf:compactClassIndex: + (because is:instanceOf:compactClassIndex: has an inline: pragma) the + phrase (objectMemory splObj: ClassArray) is expanded in-place and + is _not_ evaluated if oop has a non-zero CompactClassIndex. */ + + /* InterpreterPrimitives>>#isInstanceOfClassArray: */ +static NoDbgRegParms sqInt +isInstanceOfClassArray(sqInt oop) +{ + sqInt ccIndex; + + /* begin is:instanceOf:compactClassIndex: */ + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + return ClassArrayCompactIndex == ccIndex; +} + + +/* Answer true if integer object is negative. + Fail if object pointed by oop i not an integer. */ + + /* InterpreterPrimitives>>#isNegativeIntegerValueOf: */ +static NoDbgRegParms sqInt +isNegativeIntegerValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + int ok; + sqInt smallInt; + + if ((((oop) & 7) == 1)) { + smallInt = (oop >> 3); + return smallInt < 0; + } + if ((!(oop & (tagMask())))) { + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (ok) { + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargeNegativeIntegerCompactIndex == ccIndex; + if (ok) { + return 1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; +} + + +/* Answer if oop is a value of an integer in address range, i.e up to the + size of a machine word. + The object may be either a positive SmallInteger or a LargePositiveInteger + of size <= word size. + */ + + /* InterpreterPrimitives>>#isPositiveMachineIntegerObject: */ +sqInt +isPositiveMachineIntegerObject(sqInt oop) +{ + sqInt ccIndex; + int ok; + + if ((((oop) & 7) == 1)) { + return ((oop >> 3)) >= 0; + } + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + return ok + && ((numBytesOfBytes(oop)) <= (sizeof(usqIntptr_t))); +} + + +/* Return a Large Integer object for the given integer magnitude and sign */ + + /* InterpreterPrimitives>>#magnitude64BitIntegerFor:neg: */ +static NoDbgRegParms sqInt +magnitude64BitIntegerForneg(usqLong magnitude, sqInt isNegative) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int isSmall; + int largeClassIndex; + usqInt newLargeInteger; + usqInt newObj; + usqInt numBytesUsqInt; + sqInt numSlots; + sqInt objFormat; + sqInt smallVal; + sqInt sz; + + isSmall = (isNegative + ? magnitude <= ((MaxSmallInteger) + 1) + : magnitude <= (MaxSmallInteger)); + if (isSmall) { + smallVal = ((sqInt) magnitude); + if (isNegative) { + smallVal = 0 - smallVal; + } + return (((usqInt)smallVal << 3) | 1); + } + largeClassIndex = (isNegative + ? ClassLargeNegativeIntegerCompactIndex + : ClassLargePositiveIntegerCompactIndex); + sz = 8; + objFormat = (firstByteFormat()) + ((8 - sz) & (BytesPerWord - 1)); + + /* begin eeInstantiateSmallClassIndex:format:numBytes: */ + assert((sz >= 0) + && ((largeClassIndex != 0) + && ((knownClassAtIndex(largeClassIndex)) != GIV(nilObj)))); + assert(((objFormat < (firstByteFormat()) + ? objFormat + : objFormat & (byteFormatMask()))) == (instSpecOfClass(knownClassAtIndex(largeClassIndex)))); + numSlots = ((sz + BytesPerWord) - 1) / BytesPerWord; + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytesUsqInt = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytesUsqInt % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytesUsqInt) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytesUsqInt) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newLargeInteger = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + largeClassIndex); + GIV(freeStart) += numBytesUsqInt; + newLargeInteger = newObj; + /* end eeInstantiateSmallClassIndex:format:numBytes: */ +l1: + if (sz > 4) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((newLargeInteger + BaseHeaderSize)),SQ_SWAP_8_BYTES_IF_BIGENDIAN(magnitude)); + } + else { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((newLargeInteger + BaseHeaderSize)),SQ_SWAP_4_BYTES_IF_BIGENDIAN(magnitude)); + } + return newLargeInteger; +} + + +/* Convert the given object into an integer value. + The object may be either a positive SmallInteger or up to an eight-byte + LargeInteger. + */ + + /* InterpreterPrimitives>>#magnitude64BitValueOf: */ +static NoDbgRegParms usqLong +magnitude64BitValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + sqInt fmt; + usqInt numSlots; + int ok; + sqInt smallIntValue; + sqInt sz; + + if ((((oop) & 7) == 1)) { + smallIntValue = (oop >> 3); + if (smallIntValue < 0) { + smallIntValue = 0 - smallIntValue; + } + return smallIntValue; + } + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (!ok) { + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargeNegativeIntegerCompactIndex == ccIndex; + if (!ok) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + sz = ((((/* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (sz > (sizeof(sqLong))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + if (sz > 4) { + return SQ_SWAP_8_BYTES_IF_BIGENDIAN((long64At((void *)((oop + BaseHeaderSize))))); + } + return ((unsigned int) (SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((oop + BaseHeaderSize))))))); +} + + +/* Answer a value of an integer in address range, i.e up to the size of a + machine word. + The object is known not to be a SmallIntege. It is hoped to be a + LargePositiveInteger of size <= word size. + */ + + /* InterpreterPrimitives>>#positiveMachineIntegerValueOfObj: */ +static NoDbgRegParms usqIntptr_t +positiveMachineIntegerValueOfObj(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bs; + sqInt ccIndex; + sqInt fmt; + sqInt isClassOfNonImmequalTocompactClassIndexRV; + usqInt numSlots; + + assert(!(((((oop) & 7) == 1)))); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + isClassOfNonImmequalTocompactClassIndexRV = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (!isClassOfNonImmequalTocompactClassIndexRV) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + bs = ((((/* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (bs > (sizeof(usqIntptr_t))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + if (((sizeof(usqIntptr_t)) == 8) + && (bs > 4)) { + return SQ_SWAP_8_BYTES_IF_BIGENDIAN((long64At((void *)((oop + BaseHeaderSize))))); + } + return ((unsigned int) (SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((oop + BaseHeaderSize))))))); +} + + +/* Answer a value of an integer in address range, i.e up to the size of a + machine word. + The object may be either a positive SmallInteger or a LargePositiveInteger + of size <= word size. + */ +/* some important callers such as primitiveNewWithArg, so inline the common + case + */ + + /* InterpreterPrimitives>>#positiveMachineIntegerValueOf: */ +usqIntptr_t +positiveMachineIntegerValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt value; + + if ((((oop) & 7) == 1)) { + value = (oop >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return value; + } + + /* don't inline the rare case */ + return positiveMachineIntegerValueOfObj(oop); +} + + /* InterpreterPrimitives>>#primitiveAdd */ +static void +primitiveAdd(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerResult; + + integerResult = (stackIntegerValue(1)) + (stackIntegerValue(0)); + + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)(integerResult)) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerResult << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveAddLargeIntegers */ +EXPORT(void) +primitiveAddLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + sqInt resultIsNegative; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (GIV(primFailCode)) { + return; + } + if (aIsNegative == bIsNegative) { + /* Protect against overflow */ + if (a > (0xFFFFFFFFFFFFFFFFULL - b)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + result = a + b; + resultIsNegative = aIsNegative; + } + else { + if (a >= b) { + result = a - b; + resultIsNegative = aIsNegative; + } + else { + result = b - a; + resultIsNegative = bIsNegative; + } + } + oopResult = magnitude64BitIntegerForneg(result, resultIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Primitive. Change the class of the argument to make it an instance of the + receiver given that the format of the receiver matches the format of the + argument's class. + Fail if receiver or argument are SmallIntegers, or the receiver is an + instance of a + compact class and the argument isn't, or when the argument's class is + compact and + the receiver isn't, or when the format of the receiver is different from + the format of + the argument's class, or when the arguments class is fixed and the + receiver's size + differs from the size that an instance of the argument's class should + have. + */ + + /* InterpreterPrimitives>>#primitiveAdoptInstance */ +static void +primitiveAdoptInstance(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt err; + sqInt rcvr; + + arg = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((arg & (tagMask())) != 0)) + || ((GIV(argumentCount) > 1) + && ((((rcvr & (tagMask())) != 0)) + || (!(objCouldBeClassObj(rcvr)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + err = changeClassOfto(arg, rcvr); + if (err) { + /* changeClassOf:to: answers errors as if rcvr (the class) is an argument... */ + if (err == PrimErrBadReceiver) { + err = PrimErrBadArgument; + } + else { + if (err == PrimErrBadArgument) { + err = PrimErrBadReceiver; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = err; + } + else { + /* Flush at cache because rcvr's class has changed. */ + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + return; +} + + +/* Answer an array of all instances of the receiver that exist + when the primitive is called, excluding any that may be + garbage collected as a side effect of allocating the result array. */ + + /* InterpreterPrimitives>>#primitiveAllInstances */ +EXPORT(void) +primitiveAllInstances(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt delta; + sqInt result; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + result = allInstancesOf(longAt(GIV(stackPointer))); + if ((((result) & 7) == 1)) { + /* begin growToAccomodateContainerWithNumSlots: */ + delta = (BaseHeaderSize * 2) + (((result >> 3)) * BytesPerOop); + growOldSpaceByAtLeast(((GIV(growHeadroom) < delta) ? delta : GIV(growHeadroom))); + result = allInstancesOf(longAt(GIV(stackPointer))); + if ((((result) & 7) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + return; + } + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); +} + + +/* Answer an array of all objects that exist when the primitive + is called, excluding those that may be garbage collected as + a side effect of allocating the result array. */ + + /* InterpreterPrimitives>>#primitiveAllObjects */ +EXPORT(void) +primitiveAllObjects(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt delta; + sqInt result; + + result = allObjects(); + if ((((result) & 7) == 1)) { + /* begin growToAccomodateContainerWithNumSlots: */ + delta = (BaseHeaderSize * 2) + (((result >> 3)) * BytesPerOop); + growOldSpaceByAtLeast(((GIV(growHeadroom) < delta) ? delta : GIV(growHeadroom))); + result = allObjects(); + if ((((result) & 7) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + return; + } + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); +} + + +/* Computes arctan of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveArctan */ +static void +primitiveArctan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(atan(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + +/* Invoke the two-way become primitive. + We must at least flush the method cache here, to eliminate stale + references to mutated classes and/or selectors. + + If in the CoInterpreter, we must deal with jitted methods being becommed. + In the conception of the abstract VM, + unless a CompiledMethod is becommed to one with equivalent bytecode + contexts referring to the becommed + method will likely fetch invalid bytecodes on resumption. The + responsibility for validity here lies with the user of + the become primitive, not the VM. So one could imagine checking for + methods becomming equivalent methods + and updating Cog methods to refer to their becommed duals. But that + requires machinery to compare two + compiled methods to check if their code is equivalent. A much simpler + approach, which also gets the VM to fail + in a less confusing place if it is going to fail because the programmer + has not ensured compiled code validity + across become, is to simply divorce all frames and map context pcs back to + bytecode pcs when becomming + jitted methods. However, mapping native pcs in contexts whose methods are + flagged for become will not ensure + that pcs are mapped reliably. Consider this arc: a context on a method + that has been jitted exists with a native pc. + The method is unjitted to make room for other methods to be jitted. The + method is becommed. So the scan is only + effective for methods in the jit. Slowing down become so that a rare case + may fail more comprehensibly, when its + going to fail anyway, is a waste of effort. So we do divorce frames (since + machine code frames refer to CogMethods, + not methods), but don't scan the entire heap looking for native pcs in + contexts. Hence the primitive must be + prepared to have its calling frame divorced. We store the context for the + top frame in activeProcess. + BTW, as of late '22/early '23 this is only done for Spur. + See preBecomeAction: and postBecomeAction: */ + + /* InterpreterPrimitives>>#primitiveArrayBecome */ +static void +primitiveArrayBecome(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt ec; + sqInt rcvr; + + arg = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + ec = becomewithtwoWaycopyHash(rcvr, arg, 1, 0); + primitiveBecomeReturn(ec); +} + + +/* Invoke the one-way become primitive. + See the comment in primitiveArrayBecome for handling pervasive effects on + method cacheing and jitting. */ + + /* InterpreterPrimitives>>#primitiveArrayBecomeOneWay */ +static void +primitiveArrayBecomeOneWay(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt ec; + sqInt rcvr; + + arg = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + ec = becomewithtwoWaycopyHash(rcvr, arg, 0, 1); + primitiveBecomeReturn(ec); +} + + +/* Similar to primitiveArrayBecomeOneWay but accepts a third argument + deciding whether to + copy the receiver's elements identity hashes over the argument's elements + identity hashes. + See the comment in primitiveArrayBecome for handling pervasive effects on + method cacheing and jitting. + */ + + /* InterpreterPrimitives>>#primitiveArrayBecomeOneWayCopyHashArg */ +static void +primitiveArrayBecomeOneWayCopyHashArg(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt copyHashFlag; + sqInt ec; + + copyHashFlag = 0; + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + copyHashFlag = 1; + } + else { + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + copyHashFlag = 0; + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + ec = becomewithtwoWaycopyHash(longAt(GIV(stackPointer) + (2 * BytesPerWord)), longAt(GIV(stackPointer) + (1 * BytesPerWord)), 0, copyHashFlag); + primitiveBecomeReturn(ec); +} + + +/* Similar to primitiveArrayBecomeOneWay but does /not/ copy the receiver's + elements identity hashes over the argument's elements identity hashes. + See the comment in primitiveArrayBecome for handling pervasive effects on + method cacheing and jitting. */ + + /* InterpreterPrimitives>>#primitiveArrayBecomeOneWayNoCopyHash */ +static void +primitiveArrayBecomeOneWayNoCopyHash(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt ec; + sqInt rcvr; + + arg = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + ec = becomewithtwoWaycopyHash(rcvr, arg, 0, 0); + primitiveBecomeReturn(ec); +} + + /* InterpreterPrimitives>>#primitiveAsCharacter */ +static void +primitiveAsCharacter(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt characterCode; + sqInt characterObject; + + characterCode = longAt(GIV(stackPointer)); + if (!(((((characterCode) & 7) == 1)) + && (((characterCode = (characterCode >> 3)), + /* isInRangeCharacterCode: */ + ((characterCode >= 0) && (characterCode <= (0x3FFFFFFF))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = (GIV(argumentCount) + ? PrimErrBadArgument + : PrimErrBadReceiver); + return; + } + characterObject = ((((usqInt)(characterCode) << (numTagBits())))) + (characterTag()); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),characterObject); +} + + +/* N.B. This will answer inexact results for integers with > 53 bits of + magnitude. + */ + + /* InterpreterPrimitives>>#primitiveAsFloat */ +static void +primitiveAsFloat(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt rcvr; + char *sp; + + rcvr = longAt(GIV(stackPointer)); + assert((((rcvr) & 7) == 1)); + + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer)),floatObjectOf(((double) ((rcvr >> 3)) ))); + GIV(stackPointer) = sp; +} + + /* InterpreterPrimitives>>#primitiveAt */ +static void +primitiveAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt result; + sqInt sp1; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + + /* begin commonAt: */ + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + index = longAt(GIV(stackPointer)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l5; + } + + /* No need to test for large positive integers here. No object has 1g elements */ + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 1) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l5; + } + index = (index >> 3); + + /* begin stObject:at: */ + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + sp1 = stackPointerIndexForFrame(frameOfMarriedContext(rcvr)); + assert((ReceiverIndex + ((sp1 >> 3))) < (lengthOf(rcvr))); + stSize = sp1; + goto l3; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l3; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(rcvr))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l3: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))))) { + result = temporaryin(index - 1, frameOfMarriedContext(rcvr)); + goto l4; + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(rcvr)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + result = (((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + (index - 1)))) << 3) | 1); + goto l4; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + result = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << (shiftForWord())))))); + goto l4; + } + if (fmt >= (firstByteFormat())) { + result = (((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + ((index + fixedFields) - 1)))) << 3) | 1); + goto l4; + } + if (fmt >= (firstShortFormat())) { + result = (((usqInt)(((unsigned short) (shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))))))) << 3) | 1); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + result = positive64BitIntegerFor(long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))))); + goto l4; + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + result = (((usqInt)((((usqInt)(long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + goto l4; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + result = 0; + /* end stObject:at: */ +l4: + if (!GIV(primFailCode)) { + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); + } + /* end commonAt: */ +l5:; +} + + /* InterpreterPrimitives>>#primitiveAtPut */ +static void +primitiveAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fieldIndex; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt signedValueToStore; + sqInt signedValueToStoreSqInt; + sqInt sp; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + usqLong unsigned64BitValueToStore; + unsigned int unsignedValueToStore; + sqInt value; + + /* begin commonAtPut: */ + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + value = longAt(GIV(stackPointer)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l11; + } + + /* No need to test for large positive integers here. No object has 1g elements */ + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 2) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l11; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l11; + } + index = (index >> 3); + + /* begin stObject:at:put: */ + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l7; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l7; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l7; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l7; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l7; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l7: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(rcvr)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(rcvr))); + stSize = sp; + goto l9; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l9; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(rcvr))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l9: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))))) { + temporaryinput(index - 1, frameOfMarriedContext(rcvr), value); + goto l10; + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(rcvr)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + if (((((value) & 7) == 1)) + && ((((usqInt)((signedValueToStore = (value >> 3))))) <= 0xFF)) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + (index - 1)),signedValueToStore); + goto l10; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l10; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l6; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l6; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l6: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:storing:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + fieldIndex = (index + fixedFields) - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, rcvr, value)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),value); + goto l8; + } + if (fmt >= (firstByteFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l8; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l8; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + ((index + fixedFields) - 1)),signedValueToStoreSqInt); + goto l8; + } + if (fmt >= (firstShortFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l8; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l8; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))),signedValueToStoreSqInt); + goto l8; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l8; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l8: + goto l10; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + /* end stObject:at:put: */ +l10: + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + } + /* end commonAtPut: */ +l11:; +} + + +/* Set the cursor to the given shape. The Mac only supports 16x16 pixel + cursors. Cursor offsets are handled by Smalltalk. + */ + + /* InterpreterPrimitives>>#primitiveBeCursor */ +static void +primitiveBeCursor(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cursorBitsObj; + sqInt cursorObj; + sqInt depth; + sqInt extentX; + sqInt extentY; + sqInt maskBitsObj; + sqInt maskObj; + usqInt numSlots; + sqInt offsetObj; + sqInt offsetX; + sqInt offsetY; + + maskBitsObj = 0; + if ((((usqInt)GIV(argumentCount))) > 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + cursorObj = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + if (!((/* isPointers: */ + ((!(cursorObj & (tagMask())))) + && (((byteAt((void *)(cursorObj + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(cursorObj)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(cursorObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(cursorObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= 5))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + cursorBitsObj = longAt((void *)((cursorObj + BaseHeaderSize) + (0U << (shiftForWord())))); + offsetObj = longAt((void *)((cursorObj + BaseHeaderSize) + (4U << (shiftForWord())))); + if (!(((((((extentX = longAt((void *)((cursorObj + BaseHeaderSize) + (1U << (shiftForWord()))))))) & 7) == 1)) + && (((((((extentY = longAt((void *)((cursorObj + BaseHeaderSize) + (2U << (shiftForWord()))))))) & 7) == 1)) + && (((((((depth = longAt((void *)((cursorObj + BaseHeaderSize) + (3U << (shiftForWord()))))))) & 7) == 1)) + && ((/* isPointers: */ + ((!(offsetObj & (tagMask())))) + && (((byteAt((void *)(offsetObj + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(offsetObj)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(offsetObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(offsetObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= 2) + && (((((((offsetX = longAt((void *)((offsetObj + BaseHeaderSize) + (0U << (shiftForWord()))))))) & 7) == 1)) + && ((((((offsetY = longAt((void *)((offsetObj + BaseHeaderSize) + (1U << (shiftForWord()))))))) & 7) == 1))))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + offsetX = (offsetX >> 3); + offsetY = (offsetY >> 3); + extentX = (extentX >> 3); + extentY = (extentY >> 3); + depth = (depth >> 3); + if (!GIV(argumentCount)) { + if (depth == 32) { + if (!((extentX > 0) + && ((extentY > 0) + && ((((offsetX >= (-extentX)) && (offsetX <= 0))) + && ((((offsetY >= (-extentY)) && (offsetY <= 0))) + && ((/* isWords: */ + ((!(cursorBitsObj & (tagMask())))) + && (((((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1))))) + && ((lengthOfformat(cursorBitsObj, firstLongFormat())) == (extentX * extentY)))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* This should pass (objectMemory firstIndexableField: cursorBitsObj type: #'unsigned int *'), but the platform sources are venerable and expect sqInts */ + ioSetCursorARGB(((sqInt) (firstIndexableField(cursorBitsObj))), extentX, extentY, offsetX, offsetY); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return; + } + + /* Support arbitrary-sized 32 bit ARGB forms --bf 3/1/2007 23:51 */ + if (!((extentX == 16) + && ((extentY == 16) + && ((((offsetX >= -16) && (offsetX <= 0))) + && ((((offsetY >= -16) && (offsetY <= 0))) + && ((/* isWords: */ + ((!(cursorBitsObj & (tagMask())))) + && (((((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1))))) + && ((lengthOfformat(cursorBitsObj, firstLongFormat())) == 16))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* This should pass (objectMemory firstIndexableField: cursorBitsObj type: #'unsigned int *'), but the platform sources are venerable and expect sqInts */ + ioSetCursor(((sqInt) (firstIndexableField(cursorBitsObj))), offsetX, offsetY); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return; + } + if (!((extentX == 16) + && ((extentY == 16) + && ((((offsetX >= -16) && (offsetX <= 0))) + && ((((offsetY >= -16) && (offsetY <= 0))) + && ((/* isWords: */ + ((!(cursorBitsObj & (tagMask())))) + && (((((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1))))) + && ((lengthOfformat(cursorBitsObj, firstLongFormat())) == 16))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + maskObj = longAt(GIV(stackPointer)); + if (!((/* isPointers: */ + ((!(maskObj & (tagMask())))) + && (((byteAt((void *)(maskObj + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(maskObj)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(maskObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(maskObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= 5) + && (((longAt((void *)((maskObj + BaseHeaderSize) + (1U << (shiftForWord()))))) == ((((usqInt)16 << 3) | 1))) + && (((longAt((void *)((maskObj + BaseHeaderSize) + (2U << (shiftForWord()))))) == ((((usqInt)16 << 3) | 1))) + && (((longAt((void *)((maskObj + BaseHeaderSize) + (3U << (shiftForWord()))))) == ((((usqInt)1 << 3) | 1))) + && ((isWords((maskBitsObj = longAt((void *)((maskObj + BaseHeaderSize) + (0U << (shiftForWord()))))))) + && ((lengthOfformat(maskBitsObj, firstLongFormat())) == 16)))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* This should pass (objectMemory firstIndexableField: cursorBitsObj type: #'unsigned int *'), but the platform sources are venerable and expect sqInts */ + ioSetCursorWithMask(((sqInt) (firstIndexableField(cursorBitsObj))), ((sqInt) (firstIndexableField(maskBitsObj))), offsetX, offsetY); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* Record the system Display object in the specialObjectsTable, + and if possible pin the display bitmap. Further, invoke ioBeDisplay + to alow the VM to record the location, width heigth & depth of the bitmap. */ + + /* InterpreterPrimitives>>#primitiveBeDisplay */ +static void +primitiveBeDisplay(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bitsOop; + void *bitsOrHandle; + sqInt depthOop; + sqInt heightOop; + sqInt pinnedBits; + sqInt rcvr; + sqInt widthOop; + + rcvr = longAt(GIV(stackPointer)); + if (!((/* isPointers: */ + ((!(rcvr & (tagMask())))) + && (((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && (((lengthOf(rcvr)) >= 4) + && (((bitsOop = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord()))))), + ((/* isWordsOrBytes: */ + ((!(bitsOop & (tagMask())))) + && (isWordsOrBytesNonImm(bitsOop))) + || ((((bitsOop) & 7) == 1))) + && (((((((widthOop = longAt((void *)((rcvr + BaseHeaderSize) + (1U << (shiftForWord()))))))) & 7) == 1)) + && (((((((heightOop = longAt((void *)((rcvr + BaseHeaderSize) + (2U << (shiftForWord()))))))) & 7) == 1)) + && ((((((depthOop = longAt((void *)((rcvr + BaseHeaderSize) + (3U << (shiftForWord()))))))) & 7) == 1))))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheDisplay, GIV(specialObjectsOop), rcvr)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(rcvr & (tagMask())))) + && (oopisLessThan(rcvr, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheDisplay) << (shiftForWord()))))),rcvr); + if (((!(bitsOop & (tagMask())))) + && (!(((byteAt((void *)(bitsOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0))) { + /* Answers 0 if memory required to pin but not enough memory available. */ + pinnedBits = pinObject(bitsOop); + if (pinnedBits) { + bitsOop = pinnedBits; + } + } + bitsOrHandle = ((!(bitsOop & (tagMask()))) + ? firstIndexableField(bitsOop) + : ((void *)bitsOop)); + + /* begin ioBeDisplay:width:height:depth: */ + displayBits = bitsOrHandle; + displayWidth = (widthOop >> 3); + displayHeight = (heightOop >> 3); + displayDepth = (depthOop >> 3); + ioNoteDisplayChangedwidthheightdepth(bitsOrHandle, (widthOop >> 3), (heightOop >> 3), (depthOop >> 3)); +} + + +/* make the basic beep noise */ + + /* InterpreterPrimitives>>#primitiveBeep */ +static void +primitiveBeep(void) +{ + ioBeep(); +} + + +/* For the mirror prims check that the class obj is actually a valid class. + No need to check if it's the receiver since the method exists only on + Behavior. + */ + + /* InterpreterPrimitives>>#primitiveBehaviorHash */ +static void +primitiveBehaviorHash(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt err; + int hash; + sqInt hashOrError; + sqInt shouldBeClassObj; + + shouldBeClassObj = longAt(GIV(stackPointer)); + if (GIV(argumentCount) > 0) { + if ((((shouldBeClassObj & (tagMask())) != 0)) + || (((!((longAt((void *)(shouldBeClassObj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + || (!(objCouldBeClassObj(shouldBeClassObj))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + assert((isNonImmediate(shouldBeClassObj)) + && (addressCouldBeClassObj(shouldBeClassObj))); + + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(shouldBeClassObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + hashOrError = ((hash = (long32At((void *)(shouldBeClassObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(shouldBeClassObj) + ? ((err = enterIntoClassTable(shouldBeClassObj)) + ? -err + : (long32At((void *)(shouldBeClassObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (hashOrError < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -hashOrError; + return; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)hashOrError << 3) | 1)); +} + + +/* Note no short-cut for SmallIntegers. Either the inline interpreter + bytecode or the JIT primitive will handle this case. */ + + /* InterpreterPrimitives>>#primitiveBitAnd */ +static void +primitiveBitAnd(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt integerArgumentValue; + usqInt integerReceiverValue; + sqInt oop; + sqInt value; + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArgumentValue = ((usqIntptr_t) null); + goto l1; + } + integerArgumentValue = ((usqIntptr_t) value); + goto l1; + } + + /* don't inline the rare case */ + integerArgumentValue = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin positiveMachineIntegerValueOf: */ + if ((((oop) & 7) == 1)) { + value = (oop >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerReceiverValue = ((usqIntptr_t) null); + goto l2; + } + integerReceiverValue = ((usqIntptr_t) value); + goto l2; + } + + /* don't inline the rare case */ + integerReceiverValue = positiveMachineIntegerValueOfObj(oop); + /* end positiveMachineIntegerValueOf: */ +l2: + if (!GIV(primFailCode)) { + oop = positive64BitIntegerFor(integerArgumentValue & integerReceiverValue); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oop); + } +} + + +/* Primitive logical operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveBitAndLargeIntegers */ +EXPORT(void) +primitiveBitAndLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong integerArg; + usqLong integerRcvr; + sqInt oopResult; + + integerArg = positive64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = positive64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (GIV(primFailCode)) { + return; + } + oopResult = positive64BitIntegerFor(integerRcvr & integerArg); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Note no short-cut for SmallIntegers. Either the inline interpreter + bytecode or the JIT primitive will handle this case. */ + + /* InterpreterPrimitives>>#primitiveBitOr */ +static void +primitiveBitOr(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt integerArgumentValue; + usqInt integerReceiverValue; + sqInt oop; + sqInt value; + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArgumentValue = ((usqIntptr_t) null); + goto l1; + } + integerArgumentValue = ((usqIntptr_t) value); + goto l1; + } + + /* don't inline the rare case */ + integerArgumentValue = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin positiveMachineIntegerValueOf: */ + if ((((oop) & 7) == 1)) { + value = (oop >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerReceiverValue = ((usqIntptr_t) null); + goto l2; + } + integerReceiverValue = ((usqIntptr_t) value); + goto l2; + } + + /* don't inline the rare case */ + integerReceiverValue = positiveMachineIntegerValueOfObj(oop); + /* end positiveMachineIntegerValueOf: */ +l2: + if (!GIV(primFailCode)) { + oop = positive64BitIntegerFor(integerArgumentValue | integerReceiverValue); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oop); + } +} + + +/* Primitive logical operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveBitOrLargeIntegers */ +EXPORT(void) +primitiveBitOrLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong integerArg; + usqLong integerRcvr; + sqInt oopResult; + + integerArg = positive64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = positive64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (GIV(primFailCode)) { + return; + } + oopResult = positive64BitIntegerFor(integerRcvr | integerArg); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Perform a bitShift. In 32-bits deal only with non-negative 32-bit + integers. In 64-bits deal with signed 64-bit quantities (max (2^63)-1). */ + + /* InterpreterPrimitives>>#primitiveBitShift */ +static void +primitiveBitShift(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + sqInt shifted; + + integerArgument = longAt(GIV(stackPointer)); + if (!((((integerArgument) & 7) == 1))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + integerReceiver = signed64BitValueOf(integerReceiver); + if (!GIV(primFailCode)) { + if (((integerArgument = (integerArgument >> 3))) >= 0) { + if (!(integerArgument <= 61 /* numSmallIntegerBits */)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + shifted = ((sqInt)((usqInt)(integerReceiver) << integerArgument)); + if (!(integerReceiver == ((shifted) >> integerArgument))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + } + else { + if (!(integerArgument >= (-61 /* numSmallIntegerBits */))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + shifted = (integerReceiver) >> (0 - integerArgument); + } + + /* Left shift -- must fail bits would be lost + Right shift -- OK to lose bits */ + shifted = ((((((usqInt)(shifted)) >> 60) + 1) & 15) <= 1 + ? (((usqInt)shifted << 3) | 1) + : signed64BitIntegerFor(shifted)); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),shifted); + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveBitShiftLargeIntegers */ +EXPORT(void) +primitiveBitShiftLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + sqInt integerPointer; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + sqInt shift; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + shift = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + shift = 0; + } + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + a = magnitude64BitValueOf(oopRcvr); + if (GIV(primFailCode)) { + return; + } + if (shift >= 0) { + /* Protect against overflow */ + + /* This is to avoid undue (usqInt) cast */ + result = 0xFFFFFFFFFFFFFFFFULL; + if ((shift >= 64) + || (a > ((result) >> shift))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + result = (a << shift); + } + else { + shift = 0 - shift; + if (shift >= 64) { + result = 0; + } + else { + result = (a) >> shift; + } + + /* Fake 2 complement for negative values */ + if (aIsNegative + && (((result << shift)) != a)) { + result += 1; + } + } + oopResult = magnitude64BitIntegerForneg(result, aIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + /* InterpreterPrimitives>>#primitiveBitXor */ +static void +primitiveBitXor(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + usqInt integerArgumentValue; + sqInt integerReceiver; + usqInt integerReceiverValue; + sqInt oop; + sqInt value; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((((integerArgument) & 7) == 1)) + && ((((integerReceiver) & 7) == 1))) { + oop = (integerArgument ^ integerReceiver) + (smallIntegerTag()); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oop); + } + else { + /* begin positiveMachineIntegerValueOf: */ + if ((((integerArgument) & 7) == 1)) { + value = (integerArgument >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArgumentValue = ((usqIntptr_t) null); + goto l1; + } + integerArgumentValue = ((usqIntptr_t) value); + goto l1; + } + + /* don't inline the rare case */ + integerArgumentValue = positiveMachineIntegerValueOfObj(integerArgument); + /* end positiveMachineIntegerValueOf: */ +l1: + + /* begin positiveMachineIntegerValueOf: */ + if ((((integerReceiver) & 7) == 1)) { + value = (integerReceiver >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerReceiverValue = ((usqIntptr_t) null); + goto l2; + } + integerReceiverValue = ((usqIntptr_t) value); + goto l2; + } + + /* don't inline the rare case */ + integerReceiverValue = positiveMachineIntegerValueOfObj(integerReceiver); + /* end positiveMachineIntegerValueOf: */ +l2: + if (!GIV(primFailCode)) { + oop = positive64BitIntegerFor(integerArgumentValue ^ integerReceiverValue); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oop); + } + } +} + + +/* Primitive logical operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveBitXorLargeIntegers */ +EXPORT(void) +primitiveBitXorLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong integerArg; + usqLong integerRcvr; + sqInt oopResult; + + integerArg = positive64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = positive64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (GIV(primFailCode)) { + return; + } + oopResult = positive64BitIntegerFor(integerRcvr ^ integerArg); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Answer the encoder names for the supported bytecode sets. */ + + /* InterpreterPrimitives>>#primitiveBytecodeSetsAvailable */ +EXPORT(sqInt) +primitiveBytecodeSetsAvailable(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt encoderNames; + usqInt newObj; + usqInt numBytes; + sqInt valuePointer; + + if (GIV(argumentCount) > 0) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((knownClassAtIndex(ClassArrayCompactIndex)) != GIV(nilObj)); + assert((arrayFormat()) == (instSpecOfClass(knownClassAtIndex(ClassArrayCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (2 * BytesPerOop); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + encoderNames = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) 2)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + GIV(freeStart) += numBytes; + encoderNames = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + valuePointer = stringForCString("EncoderForV3PlusClosures"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(encoderNames)) + && (!(isForwarded(encoderNames)))); + assert(validStorePointerUncheckedArgs(0, encoderNames, valuePointer)); + longAtput((void *)((encoderNames + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("EncoderForSistaV1"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(encoderNames)) + && (!(isForwarded(encoderNames)))); + assert(validStorePointerUncheckedArgs(1, encoderNames, valuePointer)); + longAtput((void *)((encoderNames + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),encoderNames); + return 0; +} + + +/* Answer bytes available at this moment. For more meaningful + results, calls to this primitive should be precedeed by a full + or incremental garbage collection. */ + + /* InterpreterPrimitives>>#primitiveBytesLeft */ +static void +primitiveBytesLeft(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aBool; + sqInt chunk; + sqInt freeChunk; + sqInt i; + sqInt integerVal; + + if (!GIV(argumentCount)) { + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),((GIV(totalFreeOldSpace) << 3) | 1)); + return; + } + + /* old behavior - just return the size of free memory */ + if (GIV(argumentCount) == 1) { + /* Spur behavior; if argument is nil answer size of largest free chunk in oldSpace. */ + if ((longAt(GIV(stackPointer))) == GIV(nilObj)) { + /* begin sizeOfLargestFreeChunk */ + freeChunk = findLargestFreeChunk(); + if (!freeChunk) { + for (i = 0x3F; i >= 1; i += -1) { + if ((chunk = GIV(freeLists)[i])) { + integerVal = bytesInBody(chunk); + goto l1; + } + } + integerVal = 0; + goto l1; + } + integerVal = bytesInBody(freeChunk); + /* end sizeOfLargestFreeChunk */ +l1: + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerVal << 3) | 1)); + return; + } + + /* new behaviour -including or excluding swap space depending on aBool */ + + /* begin booleanValueOf: */ + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + aBool = 1; + goto l2; + } + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + aBool = 0; + goto l2; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + aBool = null; + /* end booleanValueOf: */ +l2: + if (!GIV(primFailCode)) { + integerVal = ((((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes())) < 0) ? 0 : ((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes()))); + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerVal << 3) | 1)); + return; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; +} + + +/* Perform a function call to a foreign function. + Only invoked from method containing explicit external call spec. + Due to this we use the pluggable prim mechanism explicitly here + (the first literal of any FFI spec'ed method is an ExternalFunction + and not an array as used in the pluggable primitive mechanism). */ +/* Manually copied from primitiveCalloutAccessorDepth in the + ThreadedFFIPlugins... + */ +/* since call may invoke a callback */ +/* since call signature is in first literal... */ + + /* InterpreterPrimitives>>#primitiveCalloutToFFI */ +static void +primitiveCalloutToFFI(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*primitiveCallout)(void); + + /* begin functionForPrimitiveCallout */ + if ((((sqInt)primitiveCalloutPointer)) == -1) { + reloadPrimitiveCalloutPointer(); + } + primitiveCallout = ((void (*)(void)) primitiveCalloutPointer); + if (primitiveCallout) { + dispatchFunctionPointer(primitiveCallout); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive. Change the class of the receiver into the class of the argument + given that + the format of the receiver matches the format of the argument's class. + Fail if the + receiver or argument are SmallIntegers, or the receiver is an instance of + a compact + class and the argument isn't, or when the argument's class is compact and + the receiver + isn't, or when the format of the receiver is different from the format of + the argument's + class, or when the arguments class is fixed and the receiver's size + differs from the size + that an instance of the argument's class should have. */ + + /* InterpreterPrimitives>>#primitiveChangeClass */ +static void +primitiveChangeClass(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt argClass; + sqInt err; + sqInt oop; + sqInt rcvr; + + /* begin stackObjectValue: */ + oop = longAt(GIV(stackPointer)); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = null; + goto l1; + } + arg = oop; + /* end stackObjectValue: */ +l1: + + /* begin stackObjectValue: */ + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = null; + goto l2; + } + rcvr = oop; + /* end stackObjectValue: */ +l2: + if (GIV(primFailCode)) { + return; + } + argClass = fetchClassOfNonImm(arg); + err = changeClassOfto(rcvr, argClass); + if (err) { + /* primitiveFailFor: */ + GIV(primFailCode) = err; + } + else { + /* Flush at cache because rcvr's class has changed. */ + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + return; +} + + /* InterpreterPrimitives>>#primitiveClass */ +static void +primitiveClass(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt instance; + sqInt oop; + sqInt tagBits; + + instance = longAt(GIV(stackPointer)); + if ((GIV(argumentCount) > 0) + && (/* isOopForwarded: */ + ((!(instance & (tagMask())))) + && ((!((longAt((void *)(instance))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + else { + oop = /* fetchClassOf: */ + ((tagBits = instance & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(instance)); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + } +} + + +/* Primitive. Void the VM profile histograms. */ + + /* InterpreterPrimitives>>#primitiveClearVMProfile */ +static void +primitiveClearVMProfile(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + ioClearProfile(); + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* When called with a single string argument, post the string to + the clipboard. When called with zero arguments, return a + string containing the current clipboard contents. */ + + /* InterpreterPrimitives>>#primitiveClipboardText */ +static void +primitiveClipboardText(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + usqInt numSlots; + sqInt s; + sqInt sz; + + if (GIV(argumentCount) == 1) { + s = longAt(GIV(stackPointer)); + if (!(/* isBytes: */ + ((!(s & (tagMask())))) + && (((byteAt((void *)(s + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + if (!GIV(primFailCode)) { + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(s + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + sz = ((((/* begin numSlotsOf: */ + assert((classIndexOf(s)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(s + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(s - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + clipboardWriteFromAt(sz, s + BaseHeaderSize, 0); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + } + else { + sz = clipboardSize(); + + /* begin allocateBytes:classIndex: */ + assert(addressCouldBeClassObj(classAtIndex(ClassByteStringCompactIndex))); + assert((instSpecOfClass(classAtIndex(ClassByteStringCompactIndex))) == (firstByteFormat())); + s = noInlineAllocateSlotsformatclassIndex(((sz + BytesPerOop) - 1) / BytesPerOop, byteFormatForNumBytes(sz), ClassByteStringCompactIndex); + if (!s) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + clipboardReadIntoAt(sz, s + BaseHeaderSize, 0); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),s); + } +} + + +/* Take a boolean which if true turns or keeps clock logging on. Answer an + array supplying + the size of the clock logs, the address of the usecs log, the index in it, + the address of the + msecs log, and the index into it. */ + + /* InterpreterPrimitives>>#primitiveClockLogAddresses */ +EXPORT(sqInt) +primitiveClockLogAddresses(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt midx; + void *msecs; + sqInt result; + sqInt runInNOut; + sqInt uidx; + void *usecs; + sqInt v1; + sqInt v2; + + + /* bypass type inference which would deduce int */ + midx = 0; + msecs = ((void *) 0); + uidx = 0; + usecs = ((void *) 0); + if (GIV(argumentCount) != 1) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + runInNOut = (longAt(GIV(stackPointer))) == GIV(trueObj); + ioGetClockLogSizeUsecsIdxMsecsIdx((&runInNOut), (&usecs), (&uidx), (&msecs), (&midx)); + result = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 5); + if (!result) { + return (GIV(primFailCode) = PrimErrNoMemory); + } + +#if SPURVM + v1 = positive64BitIntegerFor(((usqInt)usecs)); + v2 = positive64BitIntegerFor(((usqInt)msecs)); +#else /* SPURVM */ + pushRemappableOop(result); + v1 = positive64BitIntegerFor(((usqInt)usecs)); + pushRemappableOop(v1); + v2 = positive64BitIntegerFor(((usqInt)msecs)); + v1 = popRemappableOop(); + result = popRemappableOop(); +#endif /* SPURVM */ + if (GIV(primFailCode)) { + return (GIV(primFailCode) = PrimErrNoMemory); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(result)) + && (!(isForwarded(result)))); + assert(validStorePointerUncheckedArgs(0, result, (((usqInt)runInNOut << 3) | 1))); + longAtput((void *)((result + BaseHeaderSize) + (0U << (shiftForWord()))),(((usqInt)runInNOut << 3) | 1)); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(1, result, v1)); + assert(isNonImmediate(result)); + if (oopisGreaterThanOrEqualTo(result, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(v1 & (tagMask())))) + && (oopisLessThan(v1, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(result + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(result); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((result + BaseHeaderSize) + (1U << (shiftForWord()))),v1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(result)) + && (!(isForwarded(result)))); + assert(validStorePointerUncheckedArgs(2, result, (((usqInt)uidx << 3) | 1))); + longAtput((void *)((result + BaseHeaderSize) + (2U << (shiftForWord()))),(((usqInt)uidx << 3) | 1)); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(3, result, v2)); + assert(isNonImmediate(result)); + if (oopisGreaterThanOrEqualTo(result, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(v2 & (tagMask())))) + && (oopisLessThan(v2, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(result + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(result); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((result + BaseHeaderSize) + (3U << (shiftForWord()))),v2); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(result)) + && (!(isForwarded(result)))); + assert(validStorePointerUncheckedArgs(4, result, (((usqInt)midx << 3) | 1))); + longAtput((void *)((result + BaseHeaderSize) + (4U << (shiftForWord()))),(((usqInt)midx << 3) | 1)); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); + return 0; +} + + /* InterpreterPrimitives>>#primitiveClosureValue */ +static void +primitiveClosureValue(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt blockClosure; + sqInt closureIP; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt i; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt outerContext; + sqInt switched; + + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(GIV(argumentCount) == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Somewhat paranoiac checks we need while debugging that we may be able to discard + in a robust system. */ + outerContext = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + if (!(/* isContext: */ + ((!(outerContext & (tagMask())))) + && (((longAt((void *)(outerContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* Check if the closure's method is actually a CompiledMethod. */ + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin activateNewClosure:outer:method:numArgs:mayContextSwitch: */ + assert(isContext(outerContext)); + assert(isVanillaBlockClosure(blockClosure)); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - ClosureFirstCopiedValueIndex; + assert(closureMethod == (fetchPointerofObject(MethodIndex, outerContext))); + assert(isOopCompiledMethod(closureMethod)); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + ((cogMethod->blockEntryOffset)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogselector(closureMethod, GIV(nilObj)); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + ((cogMethod->blockEntryOffset)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(ReceiverIndex, outerContext, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + ClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* The initial instructions in the block nil-out remaining temps. + the instruction pointer is a pointer variable equal to + method oop + ip + BaseHeaderSize + -1 for 0-based addressing of fetchByte + -1 because it gets incremented BEFORE fetching currentByte */ + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + closureIP = (oop >> 3); + GIV(instructionPointer) = ((closureMethod + closureIP) + BaseHeaderSize) - 2; + + /* begin setMethod:methodHeader: */ + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(1); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewClosure:outer:method:numArgs:mayContextSwitch: */ +l1:; +} + + +/* An exact clone of primitiveClosureValue except that this version will not + check for interrupts on stack overflow. It may invoke the garbage + collector but will not switch processes. See + checkForInterruptsMayContextSwitch: */ + + /* InterpreterPrimitives>>#primitiveClosureValueNoContextSwitch */ +void +primitiveClosureValueNoContextSwitch(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt blockClosure; + sqInt closureIP; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt i; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt outerContext; + sqInt switched; + + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(GIV(argumentCount) == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Somewhat paranoiac checks we need while debugging that we may be able to discard + in a robust system. */ + outerContext = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + if (!(/* isContext: */ + ((!(outerContext & (tagMask())))) + && (((longAt((void *)(outerContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* Check if the closure's method is actually a CompiledMethod. */ + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + GIV(deferProfileCheckForVNCS) = 1; + + /* begin activateNewClosure:outer:method:numArgs:mayContextSwitch: */ + assert(isContext(outerContext)); + assert(isVanillaBlockClosure(blockClosure)); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - ClosureFirstCopiedValueIndex; + assert(closureMethod == (fetchPointerofObject(MethodIndex, outerContext))); + assert(isOopCompiledMethod(closureMethod)); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (((cogMethod->blockEntryOffset)) - (noContextSwitchBlockEntryOffset())); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogselector(closureMethod, GIV(nilObj)); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (((cogMethod->blockEntryOffset)) - (noContextSwitchBlockEntryOffset())); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(ReceiverIndex, outerContext, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + ClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* The initial instructions in the block nil-out remaining temps. + the instruction pointer is a pointer variable equal to + method oop + ip + BaseHeaderSize + -1 for 0-based addressing of fetchByte + -1 because it gets incremented BEFORE fetching currentByte */ + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + closureIP = (oop >> 3); + GIV(instructionPointer) = ((closureMethod + closureIP) + BaseHeaderSize) - 2; + + /* begin setMethod:methodHeader: */ + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(0); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewClosure:outer:method:numArgs:mayContextSwitch: */ +l1:; +} + + /* InterpreterPrimitives>>#primitiveClosureValueWithArgs */ +static void +primitiveClosureValueWithArgs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt argumentArray; + usqInt arraySize; + sqInt blockClosure; + sqInt closureIP; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt i; + sqInt index; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt outerContext; + sqInt switched; + sqInt top; + + argumentArray = longAt(GIV(stackPointer)); + if (!(/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Check for enough space in thisContext to push all args */ + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (!(arraySize <= (LargeContextSlots - CtxtTempFrameStart))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(arraySize == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Somewhat paranoiac checks we need while debugging that we may be able to discard + in a robust system. */ + outerContext = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + if (!(/* isContext: */ + ((!(outerContext & (tagMask())))) + && (((longAt((void *)(outerContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* Check if the closure's method is actually a CompiledMethod. */ + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* Copy the arguments to the stack, and activate */ + index = 1; + while (index <= numArgs) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + index += 1; + } + + /* begin activateNewClosure:outer:method:numArgs:mayContextSwitch: */ + assert(isContext(outerContext)); + assert(isVanillaBlockClosure(blockClosure)); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - ClosureFirstCopiedValueIndex; + assert(closureMethod == (fetchPointerofObject(MethodIndex, outerContext))); + assert(isOopCompiledMethod(closureMethod)); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + ((cogMethod->blockEntryOffset)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogselector(closureMethod, GIV(nilObj)); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + ((cogMethod->blockEntryOffset)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(ReceiverIndex, outerContext, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + ClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* The initial instructions in the block nil-out remaining temps. + the instruction pointer is a pointer variable equal to + method oop + ip + BaseHeaderSize + -1 for 0-based addressing of fetchByte + -1 because it gets incremented BEFORE fetching currentByte */ + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + closureIP = (oop >> 3); + GIV(instructionPointer) = ((closureMethod + closureIP) + BaseHeaderSize) - 2; + + /* begin setMethod:methodHeader: */ + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(1); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewClosure:outer:method:numArgs:mayContextSwitch: */ +l1:; +} + + +/* Return the value of the microsecond clock in the local timezone, as + updated by the heartbeat, as an integer. + This is the number of microseconds since the Smalltalk epoch, 1901/1/1 + 12:00am. The microsecond clock is at least 60 bits wide which means it'll + get to around August + 38435 before it wraps around. Be sure to put it on your calendar. The + coarse clock is + updated by the heartbeat thread and as such is much cheaper than + primitiveUTCMicrosecondClock, which always entails a system call. */ + + /* InterpreterPrimitives>>#primitiveCoarseLocalMicrosecondClock */ +static void +primitiveCoarseLocalMicrosecondClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = positive64BitIntegerFor(ioLocalMicroseconds()); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Return the value of the microsecond clock as updated by the heartbeat as + an integer. + This is the number of microseconds since the Smalltalk epoch, 1901/1/1 + 12:00am. The microsecond clock is at least 60 bits wide which means it'll + get to around August + 38435 before it wraps around. Be sure to put it on your calendar. The + coarse clock is + updated by the heartbeat thread and as such is much cheaper than + primitiveUTCMicrosecondClock, which always entails a system call. */ + + /* InterpreterPrimitives>>#primitiveCoarseUTCMicrosecondClock */ +static void +primitiveCoarseUTCMicrosecondClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = positive64BitIntegerFor(ioUTCMicroseconds()); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Primitive. Compare two byte-indexed objects for equality */ + + /* InterpreterPrimitives>>#primitiveCompareBytes */ +EXPORT(sqInt) +primitiveCompareBytes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg1; + sqInt arg2; + sqInt fmt; + sqInt i; + sqInt len; + usqInt numSlots; + + arg1 = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + arg2 = longAt(GIV(stackPointer)); + + /* Quick identity test */ + if (arg1 == arg2) { + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(trueObj)); + return 0; + } + if (!((/* isBytes: */ + ((!(arg1 & (tagMask())))) + && (((byteAt((void *)(arg1 + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && (/* isBytes: */ + ((!(arg2 & (tagMask())))) + && (((byteAt((void *)(arg2 + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(arg1 + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + len = ((((/* begin numSlotsOf: */ + assert((classIndexOf(arg1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(arg1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(arg1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (!(len == (numBytesOfBytes(arg2)))) { + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(falseObj)); + return 0; + } + for (i = 0; i < len; i += 1) { + if (!((byteAt((void *)((arg1 + BaseHeaderSize) + i))) == (byteAt((void *)((arg2 + BaseHeaderSize) + i))))) { + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(falseObj)); + return 0; + } + } + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(trueObj)); + return 0; +} + + +/* primitiveCompareWith: string2 [collated: order] */ +/* 1 - fetch the parameters from the stack */ + + /* InterpreterPrimitives>>#primitiveCompareWith */ +EXPORT(void) +primitiveCompareWith(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt c1; + sqInt c2; + sqInt fmt; + sqInt i; + sqInt min; + usqInt numSlots; + sqInt order; + sqInt result; + sqInt string1; + sqInt string2; + sqInt strLength1; + sqInt strLength2; + + order = 0; + if (!(GIV(argumentCount) == 1)) { + if (GIV(argumentCount) != 2) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + order = longAt(GIV(stackPointer)); + if (!((/* isBytes: */ + ((!(order & (tagMask())))) + && (((byteAt((void *)(order + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((numBytesOfBytes(order)) == 0x100))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + string1 = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* 2 - check their types - all parameters are ByteObject */ + string2 = longAt(GIV(stackPointer) + ((GIV(argumentCount) - 1) * BytesPerWord)); + if (!((/* isBytes: */ + ((!(string1 & (tagMask())))) + && (((byteAt((void *)(string1 + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && (/* isBytes: */ + ((!(string2 & (tagMask())))) + && (((byteAt((void *)(string2 + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* 3 - compare the strings */ + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(string1 + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + strLength1 = ((((/* begin numSlotsOf: */ + assert((classIndexOf(string1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(string1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(string1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(string2 + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + strLength2 = ((((/* begin numSlotsOf: */ + assert((classIndexOf(string2)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(string2 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(string2 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (order) { + /* begin rawCompare:length:with:length:accessBlock: */ + min = ((strLength1 < strLength2) ? strLength1 : strLength2); + for (i = 0; i < min; i += 1) { + c1 = byteAt((void *)((order + BaseHeaderSize) + ((byteAt((void *)((string1 + BaseHeaderSize) + i))) + 1))); + c2 = byteAt((void *)((order + BaseHeaderSize) + ((byteAt((void *)((string2 + BaseHeaderSize) + i))) + 1))); + if (!(c1 == c2)) { + result = c1 - c2; + goto l2; + } + } + result = strLength1 - strLength2; + /* end rawCompare:length:with:length:accessBlock: */ +l2:; + } + else { + /* begin rawCompare:length:with:length:accessBlock: */ + min = ((strLength1 < strLength2) ? strLength1 : strLength2); + for (i = 0; i < min; i += 1) { + c1 = byteAt((void *)((string1 + BaseHeaderSize) + i)); + c2 = byteAt((void *)((string2 + BaseHeaderSize) + i)); + if (!(c1 == c2)) { + result = c1 - c2; + goto l1; + } + } + result = strLength1 - strLength2; + /* end rawCompare:length:with:length:accessBlock: */ +l1:; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)result << 3) | 1)); +} + + +/* Fill the receiver, which must be an indexable non-pointer + object, with the given integer value. */ + + /* InterpreterPrimitives>>#primitiveConstantFill */ +static void +primitiveConstantFill(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt end; + usqLong fillValue; + sqInt format; + usqInt i; + sqInt oddBytes; + sqInt rcvr; + + /* begin primitiveConstantFillSpur */ + if (GIV(argumentCount) != 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + goto l1; + } + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(((!(rcvr & (tagMask())))) + && (((format = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()))) >= (sixtyFourBitIndexableFormat())))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l1; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l1; + } + fillValue = positive64BitValueOf(longAt(GIV(stackPointer))); + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + if (format >= (firstShortFormat())) { + if (format >= (firstByteFormat())) { + if ((fillValue > 0xFF) + || (format >= (firstCompiledMethodFormat()))) { + /* primitiveFailFor: */ + GIV(primFailCode) = (fillValue > 0xFF + ? PrimErrBadArgument + : PrimErrBadReceiver); + goto l1; + } + fillValue = ((fillValue + ((fillValue << 8))) + ((fillValue << 16))) + ((fillValue << 24)); + oddBytes = format & 7; + } + else { + if (fillValue > 0xFFFF) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + fillValue += (fillValue << 16); + oddBytes = ((format & 3) << 1); + } + fillValue += (fillValue << 32); + } + else { + if (format == (sixtyFourBitIndexableFormat())) { + oddBytes = 0; + } + else { + if (fillValue > 0xFFFFFFFFU) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + fillValue += (fillValue << 32); + oddBytes = ((format & 1) << 2); + } + } + end = addressAfter(rcvr); + i = rcvr + BaseHeaderSize; + while (i < end) { + long64Atput((void *)(i),fillValue); + i += 8; + } + + /* now ensure trailing bytes are zero */ + if (oddBytes > 0) { + fillValue = (fillValue) >> (8 * oddBytes); + long64Atput((void *)(i - 8),fillValue); + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + /* end primitiveConstantFillSpur */ +l1:; +} + + +/* Primitive. Start or stop the VM profiler. The first argument is a boolean + to switch profiling on or off. The second argument is an integer or nil. + If an integer it determines the maximum number of samples in the VM's + sample buffer. Answer the current number of samples in the buffer. */ + + /* InterpreterPrimitives>>#primitiveControlVMProfiling */ +static void +primitiveControlVMProfiling(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bufferSize; + sqInt numSamples; + sqInt onOffBar; + + if (GIV(argumentCount) != 2) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + if (((onOffBar = longAt(GIV(stackPointer) + (1 * BytesPerWord)))) == GIV(trueObj)) { + onOffBar = 1; + } + else { + if (onOffBar == GIV(falseObj)) { + onOffBar = 0; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + } + if (((bufferSize = longAt(GIV(stackPointer)))) == GIV(nilObj)) { + bufferSize = 0; + } + else { + if (!(((((bufferSize) & 7) == 1)) + && (((bufferSize = (bufferSize >> 3))) > 0))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + } + numSamples = ioControlNewProfile(onOffBar,bufferSize); + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),(((usqInt)numSamples << 3) | 1)); +} + + +/* Primitive. Copy the state of the receiver from the argument. + Fail if receiver and argument are of a different class. + Fail if the receiver or argument are contexts (because of context-to-stack + mapping). Fail if receiver and argument have different lengths (for + indexable objects). + Fail if the objects are not in a fit state to be copied (e.g. married + contexts and Cogged methods) */ + + /* InterpreterPrimitives>>#primitiveCopyObject */ +static void +primitiveCopyObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt fmt; + sqInt i; + usqInt length; + usqInt numBytes; + usqInt numSlots; + sqInt rcvr; + sqInt valuePointer; + + if (!(GIV(argumentCount) >= 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + arg = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if (((arg & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (((longAt((void *)(rcvr))) & (classIndexMask())) != ((longAt((void *)(arg))) & (classIndexMask()))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (isWordsOrBytesNonImm(rcvr)) { + /* begin numBytesOf: */ + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + length = numBytes - (fmt & 7); + goto l1; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + length = ((sqInt) numBytes); + goto l1; + } + if (fmt >= (firstShortFormat())) { + length = numBytes - (((fmt & 3) << 1)); + goto l1; + } + + /* fmt >= self firstLongFormat */ + length = numBytes - (((fmt & 1) << 2)); + /* end numBytesOf: */ +l1: + if (!((((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask())) == ((byteAt((void *)(arg + (formatFieldByteOffset())))) & (formatMask()))) + && (length == (numBytesOf(arg))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + memcpy(((void *)(rcvr + BaseHeaderSize)), ((void *)(arg + BaseHeaderSize)), length); + } + else { + if (!(isAppropriateForCopyObject(rcvr))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (!((isAppropriateForCopyObject(arg)) + && (length == (lengthOf(arg))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + for (i = 0; i < length; i += 1) { + valuePointer = longAt((void *)((arg + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(i, rcvr, valuePointer)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + } + + /* Note: The above could be faster for young receivers but I don't think it'll matter */ + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* Computes cosine of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveCosine */ +static void +primitiveCosine(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(cos(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + +/* Crash the VM by indirecting through a null pointer. If the sole argument + is true crash in this thread, and if it is false crash in a new thread. If + the argument is an integer use the method that implies. + bit 0 = thread to crash in; 1 => this thread + bit 1 = crash method; 0 => indirect through null pointer; 1 => call exit */ + + /* InterpreterPrimitives>>#primitiveCrashVM */ +EXPORT(sqInt) +primitiveCrashVM(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt crashInThisThread; + + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + crashInThisThread = ((longAt(GIV(stackPointer))) >> 3); + } + else { + /* begin booleanValueOf: */ + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + crashInThisThread = 1; + goto l1; + } + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + crashInThisThread = 0; + goto l1; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + crashInThisThread = null; + /* end booleanValueOf: */ +l1:; + } + if (GIV(primFailCode) + || (GIV(argumentCount) != 1)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + crashInThisOrAnotherThread(crashInThisThread); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + return 0; +} + + +/* Pass in a non-negative value to disable the architectures powermanager if + any, zero to enable. This is a named (not numbered) primitive in the null + module (ie the VM) + */ + + /* InterpreterPrimitives>>#primitiveDisablePowerManager */ +EXPORT(sqInt) +primitiveDisablePowerManager(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integer; + sqInt integerPointer; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + integer = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integer = 0; + } + if (!GIV(primFailCode)) { + ioDisablePowerManager(integer); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + return 0; +} + + /* InterpreterPrimitives>>#primitiveDiv */ +static void +primitiveDiv(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt quotient; + + quotient = doPrimitiveDivby(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)(quotient)) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)quotient << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } +} + + /* InterpreterPrimitives>>#primitiveDivide */ +static void +primitiveDivide(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerPointer; + sqInt integerReceiver; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + integerReceiver = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerReceiver = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + integerArgument = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArgument = 0; + } + if ((integerArgument != 0) + && ((integerReceiver % integerArgument) == 0)) { + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)((integerReceiver / integerArgument))) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)(integerReceiver / integerArgument) << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveDivideLargeIntegers */ +EXPORT(void) +primitiveDivideLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (GIV(primFailCode)) { + return; + } + + /* check for exact division */ + if (!((b != 0) + && ((a % b) == 0))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + result = a / b; + oopResult = magnitude64BitIntegerForneg(result, aIsNegative != bIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveDivLargeIntegers */ +EXPORT(void) +primitiveDivLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong rem; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (!b) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return; + } + result = a / b; + if (a) { + if (!(bIsNegative == aIsNegative)) { + /* Round toward negative infinity */ + rem = a % b; + if (rem) { + /* This can not overflow, because b > 1, otherwise rem = 0 */ + result += 1; + } + } + } + oopResult = magnitude64BitIntegerForneg(result, bIsNegative != aIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + /* InterpreterPrimitives>>#primitiveEqual */ +static void +primitiveEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + int result; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver == integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + result = (signed64BitValueOf(integerReceiver)) == (signed64BitValueOf(integerArgument)); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (result + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveEqualLargeIntegers */ +EXPORT(void) +primitiveEqualLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr == integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveExitToDebugger */ +static void +primitiveExitToDebugger(void) +{ + error("Exit to debugger at user request"); +} + + +/* Computes E raised to the receiver power. + Receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveExp */ +static void +primitiveExp(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(exp(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + +/* Exponent part of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveExponent */ +static void +primitiveExponent(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + double doubleValue; + int pwr; + sqInt rcvr; + + pwr = 0; + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + + /* rcvr = frac * 2^pwr, where frac is in [0.5..1.0) */ + frexp(doubleValue, (&pwr)); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),(((usqInt)(pwr - 1) << 3) | 1)); +} + + +/* Set general (unspecified) primitive failure. Don't overwrite an error code + that has already been set. */ +/* Use returnTypeC: #sqInt because that's the way it is defined in sq.h. + Use no explicit return so that Slang doesn't fail an inlining type-check + when a primitive with return type void uses ^self primitiveFail to exit. */ + + /* InterpreterPrimitives>>#primitiveFail */ +sqInt +primitiveFail(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; +} + + +/* Set PrimErrOSError primitive failure and associated osErrorCode. */ + + /* InterpreterPrimitives>>#primitiveFailForOSError: */ +sqInt +primitiveFailForOSError(sqLong osErrorCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(secondaryErrorCode) = osErrorCode; + return (GIV(primFailCode) = PrimErrOSError); +} + + +/* Set specific primitive failure. + N.B. primitiveFailFor: PrimNoErr is expected to clear the primFailCode. */ + + /* InterpreterPrimitives>>#primitiveFailFor: */ +sqInt +primitiveFailFor(sqInt reasonCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (GIV(primFailCode) = reasonCode); +} + + +/* Set primFailCode primitive failure and associated secondaryErrorCode. */ + + /* InterpreterPrimitives>>#primitiveFailFor:withSecondary: */ +sqInt +primitiveFailForwithSecondary(sqInt reasonCode, sqLong extraErrorCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(secondaryErrorCode) = extraErrorCode; + return (GIV(primFailCode) = reasonCode); +} + + /* InterpreterPrimitives>>#primitiveFailureCode */ +sqInt +primitiveFailureCode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(primFailCode); +} + + /* InterpreterPrimitives>>#primitiveFetchNextMourner */ +static void +primitiveFetchNextMourner(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt mourner; + + mourner = /* dequeueMourner */ + (GIV(mournQueue) != GIV(nilObj) + ? (assert(isValidObjStack(GIV(mournQueue))), + popObjStack(GIV(mournQueue))) + : 0); + if (mourner) { + assert(isValidObjStack(GIV(mournQueue))); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),mourner); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNotFound; + } +} + + /* InterpreterPrimitives>>#primitiveFloatAdd */ +static void +primitiveFloatAdd(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + sqInt argOop; + usqLong bits; + double rcvr; + sqInt rcvrOop; + double result; + char *sp; + sqInt tagBits; + double value; + + rcvrOop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + argOop = longAt(GIV(stackPointer)); + + /* begin primitiveFloatAdd:toArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l2: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr + arg)); + GIV(stackPointer) = sp; + } + return; +} + + +/* Index the receiver, which must be an indexable non-pointer object, and + yield a float. + */ + + /* InterpreterPrimitives>>#primitiveFloatArrayAt */ +static void +primitiveFloatArrayAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + float aFloat; + sqLong doubleBits; + sqInt fmt; + sqInt index; + usqInt newFloatObj; + usqInt newObj; + usqInt numBytes; + sqInt numSlots; + sqInt numSlotsSqInt; + sqInt oop; + usqLong rawFloatBits; + sqInt rcvr; + usqLong rot; + + /* begin primitiveSpurFloatArrayAt */ + index = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l3; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l3; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + doubleBits = long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3))))); + + /* begin floatObjectOfBits: */ + if (isSmallFloatValueBits(doubleBits)) { + rawFloatBits = doubleBits; + + /* begin smallFloatObjectOfBits: */ + assert(isSmallFloatValueBits(rawFloatBits)); + rot = (((rawFloatBits) >> 0x3F) & 1) + ((rawFloatBits << 1)); + if (rot > 1) { + rot -= (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + assert(rot > 0); + } + + /* a.k.a. ~= +/-0.0 */ + oop = ((rot << (numTagBits()))) + (smallFloatTag()); + goto l2; + } + numSlotsSqInt = (sizeof(double)) / BytesPerOop; + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((numSlotsSqInt >= 0) + && ((knownClassAtIndex(ClassFloatCompactIndex)) != GIV(nilObj))); + assert((firstLongFormat()) == (instSpecOfClass(knownClassAtIndex(ClassFloatCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlotsSqInt < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlotsSqInt < 1 + ? 8 /* allocationUnit */ + : numSlotsSqInt * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newFloatObj = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlotsSqInt)) << (numSlotsFullShift()))) + ((((usqInt)((firstLongFormat())) << (formatShift()))))) + ClassFloatCompactIndex); + GIV(freeStart) += numBytes; + newFloatObj = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((newFloatObj + BaseHeaderSize)),doubleBits); + oop = newFloatObj; + /* end floatObjectOfBits: */ +l2: + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + goto l3; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l3; + } + if ((fmt >= (firstLongFormat())) + && (fmt <= ((firstLongFormat()) + 1))) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + /* begin fetchFloat32:ofObject: */ + aFloat = singleFloatAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2))))); + oop = floatObjectOf(aFloat); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + goto l3; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l3; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + /* end primitiveSpurFloatArrayAt */ +l3:; +} + + +/* Index the receiver, which must be an indexable non-pointer object, and + store a float. + */ + + /* InterpreterPrimitives>>#primitiveFloatArrayAtPut */ +static void +primitiveFloatArrayAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + float aFloat; + usqLong bits; + sqInt fmt; + sqInt index; + sqInt numSlots; + sqInt rcvr; + double result; + sqInt tagBits; + sqLong value; + sqInt valueOop; + + /* begin primitiveSpurFloatArrayAtPut */ + valueOop = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (!((/* isFloatInstance: */ + ((tagBits = valueOop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(valueOop))) & (classIndexMask())) == ClassFloatCompactIndex)) + && ((((index) & 7) == 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l1; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l1; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + value = floatValueBitsOf(valueOop); + + /* begin storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3)))),value); + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + + /* N.B. Currently we simply truncate to 32-bits, which matches the behavior of the FloatArrayPlugin. + Maybe we should validate and range check. */ + if ((fmt >= (firstLongFormat())) + && (fmt <= ((firstLongFormat()) + 1))) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(valueOop)); + if (valueOop & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(valueOop)); + bits = ((((usqInt)valueOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&result), (&bits), sizeof(result)); + } + else { + fetchFloatAtinto(valueOop + BaseHeaderSize, result); + } + aFloat = result; + + /* begin storeFloat32:ofObject:withValue: */ + singleFloatAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2)))),aFloat); + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + /* end primitiveSpurFloatArrayAtPut */ +l1:; +} + + +/* Provide platform-independent access to 32-bit words comprising + a Float. Map index 1 onto the most significant word and index 2 + onto the least significant word. */ + + /* InterpreterPrimitives>>#primitiveFloatAt */ +static void +primitiveFloatAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + sqInt fieldIndex; + sqInt index; + unsigned int integerValue; + sqInt rcvr; + usqInt result; + + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + index = longAt(GIV(stackPointer)); + if (index == ConstOne) { + fieldIndex = (VMBIGENDIAN + ? 0 + : 1); + + /* begin fetchLong32:ofFloatObject: */ + if (!(((rcvr & (smallFloatTag())) != 0))) { + integerValue = long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2))))); + goto l1; + } + + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + integerValue = ((unsigned int) ((fieldIndex + ? (bits) >> 32 + : bits & 0xFFFFFFFFU))); + /* end fetchLong32:ofFloatObject: */ +l1: + + /* begin positive32BitIntegerFor: */ + result = (((usqInt)((((usqInt)integerValue)) & 0xFFFFFFFFU) << 3) | 1); + longAtput((GIV(stackPointer) += 1 * BytesPerWord),result); + return; + } + if (index == ConstTwo) { + fieldIndex = (VMBIGENDIAN + ? 1 + : 0); + + /* begin fetchLong32:ofFloatObject: */ + if (!(((rcvr & (smallFloatTag())) != 0))) { + integerValue = long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2))))); + goto l2; + } + + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + integerValue = ((unsigned int) ((fieldIndex + ? (bits) >> 32 + : bits & 0xFFFFFFFFU))); + /* end fetchLong32:ofFloatObject: */ +l2: + + /* begin positive32BitIntegerFor: */ + result = (((usqInt)((((usqInt)integerValue)) & 0xFFFFFFFFU) << 3) | 1); + longAtput((GIV(stackPointer) += 1 * BytesPerWord),result); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = ((((index) & 7) == 1) + ? PrimErrBadIndex + : PrimErrBadArgument); +} + + +/* Provide platform-independent access to 32-bit words comprising + a Float. Map index 1 onto the most significant word and index 2 + onto the least significant word. */ + + /* InterpreterPrimitives>>#primitiveFloatAtPut */ +static void +primitiveFloatAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt oopToStore; + sqInt rcvr; + usqInt valueToStore; + + oopToStore = longAt(GIV(stackPointer)); + valueToStore = positive32BitValueOf(oopToStore); + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((rcvr & (smallFloatTag())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + return; + } + if (index == ConstOne) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((VMBIGENDIAN + ? 0 + : 1))) << 2)))),valueToStore); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),oopToStore); + return; + } + if (index == ConstTwo) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((VMBIGENDIAN + ? 1 + : 0))) << 2)))),valueToStore); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),oopToStore); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = ((((index) & 7) == 1) + ? PrimErrBadIndex + : PrimErrBadArgument); +} + + /* InterpreterPrimitives>>#primitiveFloatDivide */ +static void +primitiveFloatDivide(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + sqInt argOop; + usqLong bits; + double rcvr; + sqInt rcvrOop; + double result; + char *sp; + sqInt tagBits; + double value; + + rcvrOop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + argOop = longAt(GIV(stackPointer)); + + /* begin primitiveFloatDivide:byArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l2: + + /* begin success: */ + if (!(arg != 0.0)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr / arg)); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatEqual */ +static void +primitiveFloatEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatEqualtoArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatGreaterOrEqual */ +static void +primitiveFloatGreaterOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatGreaterOrEqualtoArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatGreaterThan */ +static void +primitiveFloatGreaterThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatGreaterthanArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatLessOrEqual */ +static void +primitiveFloatLessOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatLessOrEqualtoArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatLessThan */ +static void +primitiveFloatLessThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatLessthanArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatMultiply */ +static void +primitiveFloatMultiply(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + sqInt argOop; + usqLong bits; + double rcvr; + sqInt rcvrOop; + double result; + char *sp; + sqInt tagBits; + double value; + + rcvrOop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + argOop = longAt(GIV(stackPointer)); + + /* begin primitiveFloatMultiply:byArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l2: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr * arg)); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatNotEqual */ +static void +primitiveFloatNotEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatEqualtoArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(falseObj) + : GIV(trueObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatSubtract */ +static void +primitiveFloatSubtract(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + sqInt argOop; + usqLong bits; + double rcvr; + sqInt rcvrOop; + double result; + char *sp; + sqInt tagBits; + double value; + + rcvrOop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + argOop = longAt(GIV(stackPointer)); + + /* begin primitiveFloatSubtract:fromArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l2: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr - arg)); + GIV(stackPointer) = sp; + } + return; +} + + +/* Clear the method lookup cache. This must be done after every programming + change. + */ + + /* InterpreterPrimitives>>#primitiveFlushCache */ +static void +primitiveFlushCache(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); +} + + +/* Primitive. Flush all the existing external primitives in the image thus + forcing a reload on next invocation. + */ + + /* InterpreterPrimitives>>#primitiveFlushExternalPrimitives */ +static void +primitiveFlushExternalPrimitives(void) +{ + flushExternalPrimitives(); +} + + +/* On some platforms, this primitive forces enqueued display updates to be + processed immediately. On others, it does nothing. + */ + + /* InterpreterPrimitives>>#primitiveForceDisplayUpdate */ +static void +primitiveForceDisplayUpdate(void) +{ + ioForceDisplayUpdate(); +} + + +/* On platforms that support it, this primitive prints the receiver, assumed + to be a Form, to the default printer. + */ + + /* InterpreterPrimitives>>#primitiveFormPrint */ +static void +primitiveFormPrint(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bitsArray; + sqInt bitsArraySize; + sqLong depth; + sqInt fmt; + sqLong h; + double hScale; + sqInt landscapeFlag; + usqInt numBytes; + usqInt numSlots; + sqLong pixelsPerWord; + sqInt rcvr; + sqInt successBoolean; + double vScale; + sqLong w; + sqLong wordsPerLine; + + bitsArray = 0; + depth = 0; + h = 0; + w = 0; + + /* begin booleanValueOf: */ + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + landscapeFlag = 1; + goto l1; + } + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + landscapeFlag = 0; + goto l1; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + landscapeFlag = null; + /* end booleanValueOf: */ +l1: + vScale = floatValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + hScale = floatValueOf(longAt(GIV(stackPointer) + (2 * BytesPerWord))); + rcvr = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + if (!((/* isPointers: */ + ((!(rcvr & (tagMask())))) + && (((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((lengthOf(rcvr)) >= 4))) { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + bitsArray = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord())))); + w = fetchIntegerofObject(1, rcvr); + h = fetchIntegerofObject(2, rcvr); + depth = fetchIntegerofObject(3, rcvr); + if (!((w > 0) + && (h > 0))) { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + pixelsPerWord = 32 / depth; + wordsPerLine = (w + (pixelsPerWord - 1)) / pixelsPerWord; + if (/* isWordsOrBytes: */ + ((!(bitsArray & (tagMask())))) + && (isWordsOrBytesNonImm(bitsArray))) { + /* begin numBytesOf: */ + fmt = (byteAt((void *)(bitsArray + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(bitsArray)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(bitsArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(bitsArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + bitsArraySize = numBytes - (fmt & 7); + goto l2; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + bitsArraySize = numBytes; + goto l2; + } + if (fmt >= (firstShortFormat())) { + bitsArraySize = numBytes - (((fmt & 3) << 1)); + goto l2; + } + + /* fmt >= self firstLongFormat */ + bitsArraySize = numBytes - (((fmt & 1) << 2)); + /* end numBytesOf: */ +l2: + + /* begin success: */ + if (!(bitsArraySize == ((wordsPerLine * h) * 4))) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + if (!GIV(primFailCode)) { + successBoolean = ioFormPrint(bitsArray + BaseHeaderSize, w, h, depth, hScale, vScale, landscapeFlag); + + /* begin success: */ + if (!successBoolean) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + } +} + + +/* Fractional part of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveFractionalPart */ +static void +primitiveFractionalPart(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + double trunc; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(modf(doubleValue, (&trunc))); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveFullClosureValue */ +static void +primitiveFullClosureValue(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt blockClosure; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt header; + sqInt i; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + usqInt numTemps; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt switched; + + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(GIV(argumentCount) == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureCompiledBlockIndex) << (shiftForWord())))))); + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin activateNewFullClosure:method:numArgs:mayContextSwitch: */ + assert(closureMethod == (fetchPointerofObject(FullClosureCompiledBlockIndex, blockClosure))); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(blockClosure)))); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex; + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogFullBlockMethodnumCopied(closureMethod, numCopied); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(FullClosureReceiverIndex, blockClosure, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + FullClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(closureMethod)); + header = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + for (i = ((numArgs + numCopied) + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(closureMethod)))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + assert((((usqInt)closureMethod)) >= (startOfMemory())); + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeaderSqInt = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeaderSqInt)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(1); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewFullClosure:method:numArgs:mayContextSwitch: */ +l1:; +} + + +/* An exact clone of primitiveFullClosureValue except that this version will + not check for interrupts on stack overflow. It may invoke the garbage + collector but will not switch processes. See + checkForInterruptsMayContextSwitch: */ + + /* InterpreterPrimitives>>#primitiveFullClosureValueNoContextSwitch */ +void +primitiveFullClosureValueNoContextSwitch(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt blockClosure; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt header; + sqInt i; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + usqInt numTemps; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt switched; + + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(GIV(argumentCount) == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureCompiledBlockIndex) << (shiftForWord())))))); + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + GIV(deferProfileCheckForVNCS) = 1; + + /* begin activateNewFullClosure:method:numArgs:mayContextSwitch: */ + assert(closureMethod == (fetchPointerofObject(FullClosureCompiledBlockIndex, blockClosure))); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockNoContextSwitchEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(blockClosure)))); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex; + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogFullBlockMethodnumCopied(closureMethod, numCopied); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockNoContextSwitchEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(FullClosureReceiverIndex, blockClosure, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + FullClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(closureMethod)); + header = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + for (i = ((numArgs + numCopied) + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(closureMethod)))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + assert((((usqInt)closureMethod)) >= (startOfMemory())); + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeaderSqInt = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeaderSqInt)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(0); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewFullClosure:method:numArgs:mayContextSwitch: */ +l1:; +} + + /* InterpreterPrimitives>>#primitiveFullClosureValueWithArgs */ +static void +primitiveFullClosureValueWithArgs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt argumentArray; + usqInt arraySize; + sqInt blockClosure; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt header; + sqInt i; + sqInt index; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + usqInt numTemps; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt switched; + sqInt top; + + argumentArray = longAt(GIV(stackPointer)); + if (!(/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Check for enough space in thisContext to push all args */ + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (!(arraySize <= (LargeContextSlots - CtxtTempFrameStart))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(arraySize == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureCompiledBlockIndex) << (shiftForWord())))))); + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* Copy the arguments to the stack, and activate */ + index = 1; + while (index <= numArgs) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + index += 1; + } + + /* begin activateNewFullClosure:method:numArgs:mayContextSwitch: */ + assert(closureMethod == (fetchPointerofObject(FullClosureCompiledBlockIndex, blockClosure))); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(blockClosure)))); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex; + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogFullBlockMethodnumCopied(closureMethod, numCopied); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(FullClosureReceiverIndex, blockClosure, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + FullClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(closureMethod)); + header = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + for (i = ((numArgs + numCopied) + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(closureMethod)))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + assert((((usqInt)closureMethod)) >= (startOfMemory())); + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeaderSqInt = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeaderSqInt)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(1); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewFullClosure:method:numArgs:mayContextSwitch: */ +l1:; +} + + +/* Fetch the system attribute with the given integer ID. The result is a + string, or nil if the attribute is not defined. + */ + + /* InterpreterPrimitives>>#primitiveGetAttribute */ +static void +primitiveGetAttribute(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt attr; + const char *attributeString; + + attr = longAt(GIV(stackPointer)); + if (!((((attr) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if ((attributeString = getAttributeString((attr >> 3)))) { + methodReturnString(attributeString); + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); + } +} + + +/* Access to environment variables via getenv. No putenv or setenv as yet. */ + + /* InterpreterPrimitives>>#primitiveGetenv */ +EXPORT(sqInt) +primitiveGetenv(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *dstPtr; + sqInt fmt; + char *key; + usqInt numBytes; + usqInt numSlots; + sqInt obj; + sqInt sz; + char *val; + + if (sHEAFn) { + if (!(sHEAFn())) { + return (GIV(primFailCode) = PrimErrInappropriate); + } + } + + /* secHasEnvironmentAccess */ + + /* begin stackEphemeralStringValue: */ + obj = longAt(GIV(stackPointer)); + if (!(/* isBytes: */ + ((!(obj & (tagMask())))) + && (((byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + key = null; + goto l2; + } + + /* begin byteSizeOf: */ + if (((obj & (tagMask())) != 0)) { + sz = 0; + goto l1; + } + + /* begin numBytesOf: */ + fmt = (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + sz = numBytes - (fmt & 7); + goto l1; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + sz = numBytes; + goto l1; + } + if (fmt >= (firstShortFormat())) { + sz = numBytes - (((fmt & 3) << 1)); + goto l1; + } + + /* fmt >= self firstLongFormat */ + sz = numBytes - (((fmt & 1) << 2)); + /* end byteSizeOf: */ +l1: + dstPtr = alloca(sz + 1); + if (!dstPtr) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoCMemory; + key = null; + goto l2; + } + memcpy(dstPtr, ((char *) (firstIndexableField(obj))), sz); + dstPtr[sz] = 0; + key = dstPtr; + /* end stackEphemeralStringValue: */ +l2: + if (key) { + if ((val = getenv(key))) { + methodReturnString(val); + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); + } + } + return 0; +} + + /* InterpreterPrimitives>>#primitiveGetImmutability */ +#if IMMUTABILITY +static void +primitiveGetImmutability(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt boolean; + sqInt rcvr; + char *sp; + + rcvr = longAt(GIV(stackPointer)); + if ((GIV(argumentCount) > 0) + && (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + boolean = /* isOopImmutable: */ + (((rcvr & (tagMask())) != 0)) + || (((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0); + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (boolean + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; +} +#endif /* IMMUTABILITY */ + + +/* Primitive. Answer the VM's current log directory */ + + /* InterpreterPrimitives>>#primitiveGetLogDirectory */ +EXPORT(sqInt) +primitiveGetLogDirectory(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + char *ptr; + sqInt stringOop; + sqInt sz; + + ptr = ioGetLogDirectory(); + if (!ptr) { + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + sz = strlen(ptr); + stringOop = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord())))))), sz); + for (i = 0; i < sz; i += 1) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((stringOop + BaseHeaderSize) + i),ptr[i]); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),stringOop); + return 0; +} + + +/* Primitive. Return the next input event from the VM event queue. */ + + /* InterpreterPrimitives>>#primitiveGetNextEvent */ +static void +primitiveGetNextEvent(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqIntptr_t eventTypeIs; + static char * eventTypeNames[] = { + "None", "Mouse", "Keyboard", "DragDropFiles", "Menu", "Window", "Complex", "MouseWheel", + "Plugin" + }; + sqIntptr_t evtBuf[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + sqInt i; + sqIntptr_t value; + + arg = longAt(GIV(stackPointer)); + if (!((/* isArray: */ + ((!(arg & (tagMask())))) + && (((byteAt((void *)(arg + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((slotSizeOf(arg)) == 8))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + ioGetNextEvent(((sqInputEvent*) evtBuf)); + if (GIV(primFailCode)) { + return; + } + if ((eventTraceMask != 0) + && (((eventTraceMask & ((((evtBuf[0]) < 0) ? (((usqInt)(1)) >> (-(evtBuf[0]))) : (1ULL << (evtBuf[0]))))) != 0))) { + fprintf(stderr, + "Event%s/%" PRIdSQINT " @ %" PRIuSQINT "\t\t%" PRIdSQINT "/%" PRIxSQINT " %" PRIdSQINT "/%" PRIxSQINT "\n\t%" PRIdSQINT "/%" PRIxSQINT " %" PRIdSQINT "/%" PRIxSQINT "\t %" PRIdSQINT "/%" PRIxSQINT " %p\n", + ((((evtBuf[0]) >= 0) && ((evtBuf[0]) <= 8)) + ? eventTypeNames[evtBuf[0]] + : "?"), + evtBuf[0], + evtBuf[1], + evtBuf[2], + evtBuf[2], + evtBuf[3], + evtBuf[3], + evtBuf[4], + evtBuf[4], + evtBuf[5], + evtBuf[5], + evtBuf[6], + evtBuf[6], + ((void *)(evtBuf[7]))); + } + + /* Event type */ + eventTypeIs = evtBuf[0]; + storeIntegerofObjectwithValue(0, arg, evtBuf[0]); + if (GIV(primFailCode)) { + return; + } + if (eventTypeIs == 6) { + for (i = 1; i <= 7; i += 1) { + value = evtBuf[i]; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(i, arg, value)); + assert(isNonImmediate(arg)); + if (oopisGreaterThanOrEqualTo(arg, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(arg + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(arg); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((arg + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),value); + } + } + else { + storeIntegerofObjectwithValue(1, arg, (evtBuf[1]) & MillisecondClockMask); + if (GIV(primFailCode)) { + return; + } + for (i = 2; i <= 7; i += 1) { + value = evtBuf[i]; + if ((((((usqIntptr_t)(value)) >> 60) + 1) & 15) <= 1) { + storeIntegerofObjectwithValue(i, arg, value); + } + else { + value = positive64BitIntegerFor(value); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(i, arg, value)); + assert(isNonImmediate(arg)); + if (oopisGreaterThanOrEqualTo(arg, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(arg + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(arg); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((arg + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),value); + } + } + } + + /* Event is Complex, assume evtBuf is populated correctly and return + Event time stamp */ + if (!GIV(primFailCode)) { + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } +} + + /* InterpreterPrimitives>>#primitiveGreaterOrEqual */ +static void +primitiveGreaterOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver >= integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveGreaterOrEqualLargeIntegers */ +EXPORT(void) +primitiveGreaterOrEqualLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr >= integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveGreaterThan */ +static void +primitiveGreaterThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver > integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveGreaterThanLargeIntegers */ +EXPORT(void) +primitiveGreaterThanLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr > integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveGrowMemoryByAtLeast */ +static void +primitiveGrowMemoryByAtLeast(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ammount; + usqInt segSize; + + ammount = longAt(GIV(stackPointer)); + if (!((((ammount) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if ((segSize = growOldSpaceByAtLeast((ammount >> 3)))) { + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),((segSize << 3) | 1)); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + } +} + + +/* Implement 28-bit hashMultiply for SmallInteger and LargePositiveInteger + receivers. + */ + + /* InterpreterPrimitives>>#primitiveHashMultiply */ +static void +primitiveHashMultiply(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + sqInt ok; + sqInt value; + + value = longAt(GIV(stackPointer)); + if ((((value) & 7) == 1)) { + value = (value >> 3); + } + else { + /* begin is:instanceOf:compactClassIndex: */ + if (((value & (tagMask())) != 0)) { + ok = 0; + goto l1; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(value))); + ccIndex = (longAt((void *)(value))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + /* end is:instanceOf:compactClassIndex: */ +l1: + if (!ok) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + value = SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((value + BaseHeaderSize))))); + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),(((((((usqInt) value)) * HashMultiplyConstant) & 0xFFFFFFF) << 3) | 1)); +} + + +/* Answer the heartbeat frequency in beats per second. If the argument is + true, reset the frequency measure. */ + + /* InterpreterPrimitives>>#primitiveHeartbeatFrequency */ +EXPORT(sqInt) +primitiveHeartbeatFrequency(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + unsigned int integerValue; + sqInt oop; + sqInt reset; + + reset = (GIV(argumentCount) == 1) + && ((longAt(GIV(stackPointer))) == GIV(trueObj)); + integerValue = ((unsigned int) (ioHeartbeatFrequency(reset))); + + /* begin positive32BitIntegerFor: */ + oop = (((usqInt)((((usqInt)integerValue)) & 0xFFFFFFFFU) << 3) | 1); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return 0; +} + + /* InterpreterPrimitives>>#primitiveHighBit */ +static void +primitiveHighBit(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + unsigned long highestBitZeroBased; + sqInt integerReceiverOop; + sqInt leadingZeroCount; + + integerReceiverOop = longAt(GIV(stackPointer)); + + /* Convert the receiver Oop to use a single tag bit */ + integerReceiverOop = ((integerReceiverOop) >> 2 /* (numSmallIntegerTagBits - 1) */) | 1; + if (integerReceiverOop < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } +# if defined(__GNUC__) + /* Note: in gcc, result is undefined if input is zero (for compatibility with BSR fallback when no CLZ instruction available). + but input is never zero because we pass the oop with tag bits set, so we are safe */ + leadingZeroCount = __builtin_clzll(integerReceiverOop); + assert(!((leadingZeroCount == 0))); + + /* Nice bit trick: 1-based high-bit is (32 - clz) - 1 to account for tag bit. + This is like two-complement - clz - 1 on 5 bits, or in other words a bit-invert operation clz ^16r1F */ + + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),(((usqInt)(leadingZeroCount ^ ((BytesPerWord * 8) - 1)) << 3) | 1)); +# elif (defined(_MSC_VER)) || (defined(__ICC)) + /* In MSVC, _lzcnt and _lzcnt64 builtins do not fallback to BSR when not supported by CPU + Instead of messing with __cpuid() we always use the BSR intrinsic + Setting this variable is useless, but VMMaker will generate an automatic initialization at a worse place if this isn't initialized explicitly. */ + highestBitZeroBased = 0; + + /* We do not even test the return value, because integerReceiverOop is never zero */ + _BitScanReverse64((&highestBitZeroBased), integerReceiverOop); + + /* thanks to the tag bit, the +1 operation for getting 1-based rank is not necessary */ + + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),((highestBitZeroBased << 3) | 1)); +# else // defined(__GNUC__) || (defined(_MSC_VER)) || (defined(__ICC)) + /* not gcc/clang, nor MSVC/ICC, you have to implement if your compiler provides useful builtins */ + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } +# endif // defined(__GNUC__) || (defined(_MSC_VER)) || (defined(__ICC)) +} + + +/* Return the value of the high resolution clock if this system has any. + The exact frequency of the high res clock is undefined specifically so + that we can use + processor dependent instructions (like RDTSC). The only use for the high + res clock is for + profiling where we can allocate time based on sub-msec resolution of the + high res clock. + If no high-resolution counter is available, the platform should return + zero. ar 6/22/2007 */ + + /* InterpreterPrimitives>>#primitiveHighResClock */ +EXPORT(sqInt) +primitiveHighResClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = positive64BitIntegerFor(ioHighResClock()); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return 0; +} + + +/* is the receiver/first argument the same object as the (last) argument?. + pop argumentCount because this can be used as a mirror primitive. */ + + /* InterpreterPrimitives>>#primitiveIdentical */ +static void +primitiveIdentical(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt otherObject; + char *sp; + sqInt thisObject; + + thisObject = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + otherObject = longAt(GIV(stackPointer)); + if ((/* isOopForwarded: */ + ((!(otherObject & (tagMask())))) + && ((!((longAt((void *)(otherObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + || ((GIV(argumentCount) > 1) + && (/* isOopForwarded: */ + ((!(thisObject & (tagMask())))) + && ((!((longAt((void *)(thisObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (thisObject == otherObject + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveIdentityHash */ +static void +primitiveIdentityHash(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt hash; + usqInt hashUsqInt; + sqInt integer; + sqInt thisReceiver; + + thisReceiver = longAt(GIV(stackPointer)); + if ((((thisReceiver & (tagMask())) != 0)) + || ((GIV(argumentCount) > 0) + && ((!((longAt((void *)(thisReceiver))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = (GIV(argumentCount) > 0 + ? PrimErrBadArgument + : PrimErrBadReceiver); + return; + } + + /* fail if forwarded; the retry machinery will fix things up. */ + + /* begin hashBitsOf: */ + hash = (long32At((void *)(thisReceiver + 4))) & (identityHashHalfWordMask()); + if (!hash) { + /* cb 1/19/2017 18:34: + would like to assert + self assert: (coInterpreter addressCouldBeClassObj: objOop) not + but instance-specific behaviors that are instances of themselves may + fail this test. + eem 12/28/2021 11:52 I'm not sure this is the issue. The issue is only validating objOop as a behavior. */ + + /* begin newHashBitsOf: */ + /* begin newObjectHash */ + assert(!((GIV(lastHash) == 0))); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 2)); + GIV(lastHash) = GIV(lastHash) ^ ((((GIV(lastHash) << 7))) & (identityHashHalfWordMask())); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 3)); + hashUsqInt = GIV(lastHash); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(thisReceiver + 4),((((long32At((void *)(thisReceiver + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hashUsqInt & (identityHashHalfWordMask()))); + hash = hashUsqInt & (identityHashHalfWordMask()); + } + integer = hash; + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); +} + + +/* Answer an integer identifying the type of image. The image version number + may identify the format of the image (e.g. 32 or 64-bit word size) or + specific requirements + of the image (e.g. block closure support required). + + This is a named (not numbered) primitive in the null module (ie the VM) + */ + + /* InterpreterPrimitives>>#primitiveImageFormatVersion */ +EXPORT(sqInt) +primitiveImageFormatVersion(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = (((usqInt)((((usqInt)(/* imageFormatVersionForSnapshot */ + (GIV(multipleBytecodeSetsActive) + ? 68533 /* (imageFormatVersion bitOr: MultipleBytecodeSetsBitmask) */ + : 68021 /* imageFormatVersion */)))) & 0xFFFFFFFFU) << 3) | 1); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); + return 0; +} + + +/* When called with a single string argument, record the string as the + current image file name. + When called with zero arguments, return a string containing the current + image file name. + */ + + /* InterpreterPrimitives>>#primitiveImageName */ +static void +primitiveImageName(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + sqInt isString; + sqInt okToRename; + sqInt s; + void *sCRIfn; + sqInt sz; + + if (GIV(argumentCount) == 1) { + s = longAt(GIV(stackPointer)); + + /* begin isInstanceOfClassByteString: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((s & (tagMask())) != 0)) { + isString = 0; + goto l1; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(s))); + ccIndex = (longAt((void *)(s))) & (classIndexMask()); + isString = ClassByteStringCompactIndex == ccIndex; + /* end isInstanceOfClassByteString: */ +l1: + if (!isString) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* If the security plugin can be loaded, use it to check for rename permission. + If not, assume it's ok */ + sCRIfn = ioLoadFunctionFrom("secCanRenameImage", "SecurityPlugin"); + if (sCRIfn) { + okToRename = ((sqInt (*)(void))sCRIfn)(); + if (!okToRename) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + } + imageNamePutLength(s + BaseHeaderSize, numBytesOf(s)); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + return; + } + sz = imageNameSize(); + s = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord())))))), sz); + imageNameGetLength(s + BaseHeaderSize, sz); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),s); +} + + +/* For a Smalllnteger, answer itself. + For a Character, answer its code as an unsigned integer. + For a SmallFloat, answer the signed, but unadjusted bit pattern (so as to + keep the result a SmallInteger). + This is a good value for an immediate's hash. */ + + /* InterpreterPrimitives>>#primitiveImmediateAsInteger */ +static void +primitiveImmediateAsInteger(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + usqInt value; + + value = 0; + oop = longAt(GIV(stackPointer)); + + /* begin immediateAsInteger:ifFail: */ + if ((((oop) & 7) == 1)) { + value = (oop >> 3); + goto l1; + } + if (((oop & (characterTag())) != 0)) { + value = ((((usqInt)oop))) >> (numTagBits()); + goto l1; + } + if (((oop & (smallFloatTag())) != 0)) { + /* begin rotatedFloatBitsOf: */ + assert(isImmediateFloat(oop)); + value = (((((usqInt)oop))) >> ((numTagBits()) + 1)) + ((((oop & (1U << (numTagBits()))) != 0) + ? ((((usqInt)-1)) << ((64 - (numTagBits())) - 1)) + : 0)); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + /* end immediateAsInteger:ifFail: */ +l1: + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),((value << 3) | 1)); +} + + +/* Register the input semaphore. If the argument is not a + Semaphore, unregister the current input semaphore. */ + + /* InterpreterPrimitives>>#primitiveInputSemaphore */ +static void +primitiveInputSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + + arg = longAt(GIV(stackPointer)); + if ((((arg) & 7) == 1)) { + /* If arg is integer, then use it as an index + into the external objects array and install it + as the new event semaphore */ + ioSetInputSemaphore((arg >> 3)); + if (!GIV(primFailCode)) { + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + return; + } + + /* old code for compatibility */ + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; +} + + +/* Return an integer indicating the reason for the most recent input + interrupt. + */ + + /* InterpreterPrimitives>>#primitiveInputWord */ +static void +primitiveInputWord(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),(((usqInt)0 << 3) | 1)); +} + + /* InterpreterPrimitives>>#primitiveIntegerAt */ +static void +primitiveIntegerAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classPointer; + sqInt fmt; + sqInt fmtSqInt; + sqInt index; + sqInt numFixed; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt rcvr; + sqInt value; + + /* begin primitiveSpurIntegerAt */ + index = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l1; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + goto l1; + } + + /* begin numBytesOfBytes: */ + fmtSqInt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert(fmtSqInt >= (firstByteFormat())); + numSlots = ((((/* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) << (shiftForWord()))) - (fmtSqInt & 7); + if ((((usqInt)index)) < numSlots) { + value = byteAt((void *)((rcvr + BaseHeaderSize) + index)); + if (value > 0x7F) { + value -= 0x100; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)value << 3) | 1)); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt >= (firstShortFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 1; + if ((((usqInt)index)) < numSlots) { + value = shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 1))))); + if (value > 0x7FFF) { + value -= 0x10000; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)value << 3) | 1)); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + oop = signed64BitIntegerFor(long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3)))))); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt >= (firstLongFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + value = long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2))))); + if (value > 0x7FFFFFFF) { + value -= 0x100000000LL; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)value << 3) | 1)); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if ((fmt <= 5 /* lastPointerFormat */) + && (/* isIndexableFormat: */ + (fmt >= (arrayFormat())) + && ((fmt <= (weakArrayFormat())) + || (fmt >= (sixtyFourBitIndexableFormat()))))) { + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt == (arrayFormat())) { + if ((((usqInt)index)) < numSlots) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))))); + goto l1; + } + } + + /* begin numFixedSlotsOf: */ + classPointer = fetchClassOfNonImm(rcvr); + classFormat = ((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + numFixed = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + if ((((index + 1) >= numFixed) && ((index + 1) <= numSlots))) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index + numFixed)) << (shiftForWord()))))))); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + /* end primitiveSpurIntegerAt */ +l1:; +} + + /* InterpreterPrimitives>>#primitiveIntegerAtPut */ +static void +primitiveIntegerAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classPointer; + sqInt fieldIndex; + sqInt fmt; + sqInt fmtSqInt; + sqInt index; + sqInt numFixed; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqLong value; + sqInt valueOop; + + /* begin primitiveSpurIntegerAtPut */ + valueOop = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + value = signed64BitValueOf(valueOop); + if (!((!GIV(primFailCode)) + && ((((index) & 7) == 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l1; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l1; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + goto l1; + } + if (!((((usqInt)((((value) >> 7)) + 1))) <= 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + + /* begin numBytesOfBytes: */ + fmtSqInt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert(fmtSqInt >= (firstByteFormat())); + numSlots = ((((/* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) << (shiftForWord()))) - (fmtSqInt & 7); + if ((((usqInt)index)) < numSlots) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + index),value); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt >= (firstShortFormat())) { + if (!((((usqInt)((((value) >> 15)) + 1))) <= 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 1; + if ((((usqInt)index)) < numSlots) { + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 1)))),value); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + /* No range check on value in this case because signed64BitValueOf: performed it above. */ + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3)))),value); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt >= (firstLongFormat())) { + if (!((((usqInt)((((value) >> 0x1F)) + 1))) <= 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2)))),value); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if ((fmt <= 5 /* lastPointerFormat */) + && (/* isIndexableFormat: */ + (fmt >= (arrayFormat())) + && ((fmt <= (weakArrayFormat())) + || (fmt >= (sixtyFourBitIndexableFormat()))))) { + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt == (arrayFormat())) { + if ((((usqInt)index)) < numSlots) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, rcvr, valueOop)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valueOop & (tagMask())))) + && (oopisLessThan(valueOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),valueOop); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + } + + /* begin numFixedSlotsOf: */ + classPointer = fetchClassOfNonImm(rcvr); + classFormat = ((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + numFixed = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + if ((((index + 1) >= numFixed) && ((index + 1) <= numSlots))) { + fieldIndex = index + numFixed; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, rcvr, valueOop)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valueOop & (tagMask())))) + && (oopisLessThan(valueOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valueOop); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + /* end primitiveSpurIntegerAtPut */ +l1:; +} + + +/* Primitive. Answer the number of interrupt checks per milliseconds that we + execute on this machine. This can be used to adjust the sub-msecs profiler + to check (roughly) + n times per millisecond. + */ + + /* InterpreterPrimitives>>#primitiveInterruptChecksPerMSec */ +EXPORT(sqInt) +primitiveInterruptChecksPerMSec(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + if (GIV(argumentCount)) { + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + oop = (((usqInt)(ioHeartbeatMilliseconds()) << 3) | 1); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); + return 0; +} + + +/* Register the user interrupt semaphore. If the argument is + not a Semaphore, unregister the current interrupt + semaphore. */ + + /* InterpreterPrimitives>>#primitiveInterruptSemaphore */ +static void +primitiveInterruptSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + + arg = longAt(GIV(stackPointer)); + if ((arg == GIV(nilObj)) + || (/* isSemaphoreOop: */ + ((!(arg & (tagMask())))) + && (((longAt((void *)(arg))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))))) { + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheInterruptSemaphore, GIV(specialObjectsOop), arg)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(arg & (tagMask())))) + && (oopisLessThan(arg, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheInterruptSemaphore) << (shiftForWord()))))),arg); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } +} + + +/* Answer if running on a big endian machine. */ + + /* InterpreterPrimitives>>#primitiveIsBigEnder */ +EXPORT(sqInt) +primitiveIsBigEnder(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop:thenPushBool: */ + longAtput(GIV(stackPointer),GIV(falseObj)); + return 0; +} + + +/* Answer if the receiver is pinned, i.e. immobile. */ + + /* InterpreterPrimitives>>#primitiveIsPinned */ +static void +primitiveIsPinned(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt obj; + char *sp; + sqInt trueOrFalse; + + obj = longAt(GIV(stackPointer)); + if ((((obj & (tagMask())) != 0)) + || ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + trueOrFalse = ((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0; + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (trueOrFalse + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; +} + + /* InterpreterPrimitives>>#primitiveLessOrEqual */ +static void +primitiveLessOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver <= integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveLessOrEqualLargeIntegers */ +EXPORT(void) +primitiveLessOrEqualLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr <= integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveLessThan */ +static void +primitiveLessThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver < integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveLessThanLargeIntegers */ +EXPORT(void) +primitiveLessThanLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr < integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + +/* Primitive. Return the n-th builtin module name. */ + + /* InterpreterPrimitives>>#primitiveListBuiltinModule */ +static void +primitiveListBuiltinModule(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt integerPointer; + char *moduleName; + + if (!(GIV(argumentCount) == 1)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + index = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + index = 0; + } + if (GIV(primFailCode) + || (index <= 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + if ((moduleName = ioListBuiltinModule(index))) { + methodReturnString(moduleName); + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); + } +} + + +/* Primitive. Answer the n-th loaded external module name. */ + + /* InterpreterPrimitives>>#primitiveListExternalModule */ +static void +primitiveListExternalModule(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt integerPointer; + char *moduleName; + + if (!(GIV(argumentCount) == 1)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + index = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + index = 0; + } + if (GIV(primFailCode) + || (index <= 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + if ((moduleName = ioListLoadedModule(index))) { + methodReturnString(moduleName); + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); + } +} + + +/* This primitive is called from Smalltalk as... + loadSegmentFrom: aWordArray outPointers: anArray. + + This primitive will load a binary image segment created by + primitiveStoreImageSegment. It expects the outPointer array to be of the + proper size, and the wordArray to be well + formed. It will return as its value the original array of roots, and the + erstwhile segmentWordArray will have been truncated to a size of one word, + i.e. retaining the version + stamp. If this primitive should fail, the segmentWordArray will, sadly, + have been reduced to + an unrecognizable and unusable jumble. But what more could you have done + with it anyway? + [How about saving it so the system functions as primitives are intended? + eem 5/9/2017 16:31] + + In Spur, if the primitive succeeds, the segmentWordArray is also becomed + into the array of loaded + objects, to allow fixing up of loaded objects directly without nextObject, + which Spur doesn't support. */ + + /* InterpreterPrimitives>>#primitiveLoadImageSegment */ +static void +primitiveLoadImageSegment(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt outPointerArray; + sqInt result; + sqInt segmentWordArray; + + outPointerArray = longAt(GIV(stackPointer)); + segmentWordArray = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* Essential type checks */ + if (!((/* isArray: */ + ((!(outPointerArray & (tagMask())))) + && (((byteAt((void *)(outPointerArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (/* isWords: */ + ((!(segmentWordArray & (tagMask())))) + && (((((byteAt((void *)(segmentWordArray + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(segmentWordArray + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1))))))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Must be indexable pointers + Must be indexable words + the engine returns the roots array which was first in the segment, or an error code on failure. */ + result = loadImageSegmentFromoutPointers(segmentWordArray, outPointerArray); + if (oopisGreaterThan(result, segmentWordArray)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),result); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = result; + } +} + + +/* Return the value of the microsecond clock in the local timezone as an + integer. This is the number of microseconds since the Smalltalk epoch, + 1901/1/1 12:00am. + The microsecond clock is at least 60 bits wide which means it'll get to + around August + 38435 before it wraps around. Be sure to put it on your calendar. This + primitive accesses the time as answered by the OS. */ + + /* InterpreterPrimitives>>#primitiveLocalMicrosecondClock */ +static void +primitiveLocalMicrosecondClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = positive64BitIntegerFor(ioLocalMicrosecondsNow()); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Natural log of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveLogN */ +static void +primitiveLogN(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(log(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + +/* Register the low-space semaphore. If the argument is not a + Semaphore, unregister the current low-space Semaphore. */ + + /* InterpreterPrimitives>>#primitiveLowSpaceSemaphore */ +static void +primitiveLowSpaceSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + + arg = longAt(GIV(stackPointer)); + if ((arg == GIV(nilObj)) + || (/* isSemaphoreOop: */ + ((!(arg & (tagMask())))) + && (((longAt((void *)(arg))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))))) { + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheLowSpaceSemaphore, GIV(specialObjectsOop), arg)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(arg & (tagMask())))) + && (oopisLessThan(arg, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheLowSpaceSemaphore) << (shiftForWord()))))),arg); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } +} + + /* InterpreterPrimitives>>#primitiveMakePoint */ +static void +primitiveMakePoint(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + usqInt newObj; + usqInt numBytes; + usqInt pt; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + arg = longAt(GIV(stackPointer)); + if (!(/* isFloatOrInt: */ + (((arg & (tagMask())) != 0) + ? !(((arg & (characterTag())) != 0)) + : ((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* begin eeInstantiatePoint */ + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert(((YIndex + 1) >= 0) + && ((knownClassAtIndex(ClassPointCompactIndex)) != GIV(nilObj))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(knownClassAtIndex(ClassPointCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert((YIndex + 1) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (((YIndex + 1) < 1 + ? 8 /* allocationUnit */ + : (YIndex + 1) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + pt = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) (YIndex + 1))) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + ClassPointCompactIndex); + GIV(freeStart) += numBytes; + pt = newObj; + /* end eeInstantiatePoint */ +l1: + + /* No need to check since new object is always new. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pt)) + && (!(isForwarded(pt)))); + assert(validStorePointerUncheckedArgs(XIndex, pt, rcvr)); + longAtput((void *)((pt + BaseHeaderSize) + ((((usqInt)(XIndex) << (shiftForWord()))))),rcvr); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pt)) + && (!(isForwarded(pt)))); + assert(validStorePointerUncheckedArgs(YIndex, pt, arg)); + longAtput((void *)((pt + BaseHeaderSize) + ((((usqInt)(YIndex) << (shiftForWord()))))),arg); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),pt); +} + + /* InterpreterPrimitives>>#primitiveMaxIdentityHash */ +static void +primitiveMaxIdentityHash(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),(((usqInt)(identityHashHalfWordMask()) << 3) | 1)); +} + + +/* Return the value of the millisecond clock as an integer. Note that the + millisecond clock wraps around periodically. On some platforms it can wrap + daily. The range is limited to SmallInteger maxVal / 2 to allow delays of + up to that length without overflowing a SmallInteger. + */ + + /* InterpreterPrimitives>>#primitiveMillisecondClock */ +static void +primitiveMillisecondClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = ((((ioMSecs()) & MillisecondClockMask) << 3) | 1); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Provide access to the millisecond clock mask to support calculation + of durations based on the millisecond clock value. */ + + /* InterpreterPrimitives>>#primitiveMillisecondClockMask */ +EXPORT(sqInt) +primitiveMillisecondClockMask(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),(((usqInt)MillisecondClockMask << 3) | 1)); + return 0; +} + + /* InterpreterPrimitives>>#primitiveMod */ +static void +primitiveMod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt mod; + + mod = doPrimitiveModby(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)(mod)) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)mod << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveModLargeIntegers */ +EXPORT(void) +primitiveModLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (!b) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return; + } + result = a % b; + + /* Handle remainder of same sign as argument */ + if (result) { + if (!(bIsNegative == aIsNegative)) { + result = b - result; + } + } + oopResult = magnitude64BitIntegerForneg(result, bIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Given one boolean parameter, set multipleBytecodeSetsActive to inform + the VM that alternate bytecode sets such as SistaV1 are now in use and + that the image format number should be updated accordingly. With zero + parameters, answer the current value of multipleBytecodeSetsActive. */ + + /* InterpreterPrimitives>>#primitiveMultipleBytecodeSetsActive */ +EXPORT(sqInt) +primitiveMultipleBytecodeSetsActive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + if (GIV(argumentCount) > 1) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + if (GIV(argumentCount) == 1) { + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + GIV(multipleBytecodeSetsActive) = 1; + } + else { + if (!((longAt(GIV(stackPointer))) == GIV(falseObj))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + GIV(multipleBytecodeSetsActive) = 0; + } + } + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (GIV(multipleBytecodeSetsActive) + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return 0; +} + + /* InterpreterPrimitives>>#primitiveMultiply */ +static void +primitiveMultiply(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArg; + sqInt integerPointer; + sqInt integerRcvr; + sqInt integerResult; + sqInt overflow; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + integerRcvr = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerRcvr = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + integerArg = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArg = 0; + } + if (!GIV(primFailCode)) { + overflow = (integerRcvr > 0 + ? (integerArg > 0 + ? integerRcvr > ((MaxSmallInteger) / integerArg) + : integerArg < ((MinSmallInteger) / integerRcvr)) + : (integerArg > 0 + ? integerRcvr < ((MinSmallInteger) / integerArg) + : (integerRcvr < 0) + && (integerArg < ((MaxSmallInteger) / integerRcvr)))); + if (overflow) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + else { + integerResult = integerRcvr * integerArg; + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerResult << 3) | 1)); + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveMultiplyLargeIntegers */ +EXPORT(void) +primitiveMultiplyLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (GIV(primFailCode)) { + return; + } + + /* check for overflow */ + if ((a > 1) + && ((b > 1) + && (a > (0xFFFFFFFFFFFFFFFFULL / b)))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + result = a * b; + oopResult = magnitude64BitIntegerForneg(result, aIsNegative != bIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + /* InterpreterPrimitives>>#primitiveNew */ +static void +primitiveNew(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt obj; + sqInt reasonCode; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + + /* Allocate a new fixed-size instance. Fail if the allocation would leave + less than lowSpaceThreshold bytes free. This *will not* cause a GC :-) */ + if ((obj = instantiateClass(longAt(GIV(stackPointer))))) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + reasonCode = (isFixedSizePointerFormat((((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + /* InterpreterPrimitives>>#primitiveNewMethod */ +static void +primitiveNewMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bytecodeCount; + sqInt class; + sqInt classFormat; + sqInt classIndex; + sqInt err; + int hash; + sqInt header; + sqInt i; + sqInt instSpec; + usqInt literalCount; + sqInt newObj; + usqInt newObjUsqInt; + usqInt numBytes; + usqInt numSlots; + usqInt p; + sqInt reasonCode; + usqInt size; + sqInt theMethod; + usqInt toDoLimit; + + header = longAt(GIV(stackPointer)); + bytecodeCount = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(((((header) & 7) == 1)) + && (((((bytecodeCount) & 7) == 1)) + && (((bytecodeCount = (bytecodeCount >> 3))) >= 0)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + class = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + literalCount = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + size = ((literalCount + LiteralStart) * BytesPerOop) + bytecodeCount; + + /* begin instantiateCompiledMethodClass:indexableSize: */ + classFormat = ((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + if (instSpec != (firstCompiledMethodFormat())) { + theMethod = null; + goto l2; + } + numSlots = (size + 7) / 8; + instSpec += (8 - size) & 7; + + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(class)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(class + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(class) + ? ((err = enterIntoClassTable(class)) + ? -err + : (long32At((void *)(class + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + theMethod = null; + goto l2; + } + if (numSlots > ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (numSlots > (0x10000000000LL)) { + theMethod = null; + goto l2; + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpec, classIndex); + } + else { + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if (((numSlots) >> 56) > 0) { + newObj = null; + goto l1; + } + newObjUsqInt = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObjUsqInt = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, instSpec, classIndex); + goto l1; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObjUsqInt),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObjUsqInt),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObjUsqInt % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newObj = newObjUsqInt; + /* end allocateSlots:format:classIndex: */ +l1:; + } + if (newObj) { + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + theMethod = newObj; + /* end instantiateCompiledMethodClass:indexableSize: */ +l2: + if (!theMethod) { + reasonCode = (isCompiledMethodFormat((((usqInt)((((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + return; + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theMethod)) + && (!(isForwarded(theMethod)))); + assert(validStorePointerUncheckedArgs(HeaderIndex, theMethod, header)); + longAtput((void *)((theMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))),header); + for (i = 1; i <= literalCount; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theMethod)) + && (!(isForwarded(theMethod)))); + assert(validStorePointerUncheckedArgs(i, theMethod, GIV(nilObj))); + longAtput((void *)((theMethod + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),theMethod); +} + + +/* Allocate a new indexable instance. Fail if the allocation would leave less + than lowSpaceThreshold bytes free. May cause a GC. + */ + + /* InterpreterPrimitives>>#primitiveNewWithArg */ +static void +primitiveNewWithArg(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt instSpec; + sqInt obj; + sqInt reasonCode; + usqIntptr_t size; + sqInt value; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + size = null; + goto l1; + } + size = value; + goto l1; + } + + /* don't inline the rare case */ + size = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* positiveMachineIntegerValueOf: succeeds only for non-negative integers. */ + if ((obj = instantiateClassindexableSize(longAt(GIV(stackPointer) + (1 * BytesPerWord)), size))) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + instSpec = (((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer) + (1 * BytesPerWord))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + reasonCode = ((/* isIndexableFormat: */ + (instSpec >= (arrayFormat())) + && ((instSpec <= (weakArrayFormat())) + || (instSpec >= (sixtyFourBitIndexableFormat())))) + && (!(instSpec >= (firstCompiledMethodFormat()))) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + /* InterpreterPrimitives>>#primitiveNextInstance */ +static void +primitiveNextInstance(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt actualObj; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt object; + sqInt subsequentObject; + + object = longAt(GIV(stackPointer)); + if (!(((object & (tagMask())) != 0))) { + /* begin instanceAfter: */ + actualObj = object; + classIndex = (longAt((void *)(object))) & (classIndexMask()); + + /* begin isYoungObject: */ + assert(isNonImmediate(object)); + if (oopisLessThan(object, GIV(oldSpaceStart))) { + if (oopisGreaterThanOrEqualToandLessThan(object, ((GIV(eden)).start), GIV(freeStart))) { + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(actualObj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + actualObj = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + actualObj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + if (!(oopisLessThan(actualObj, GIV(freeStart)))) break; + if (classIndex == ((longAt((void *)(actualObj))) & (classIndexMask()))) { + subsequentObject = actualObj; + goto l4; + } + } + actualObj = (oopisGreaterThan(GIV(pastSpaceStart), ((GIV(pastSpace)).start)) + ? objectStartingAt(((GIV(pastSpace)).start)) + : GIV(nilObj)); + } + if (oopisGreaterThanOrEqualToandLessThan(actualObj, ((GIV(pastSpace)).start), GIV(pastSpaceStart))) { + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(actualObj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + actualObj = GIV(pastSpaceStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + actualObj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l2:; + if (!(oopisLessThan(actualObj, GIV(pastSpaceStart)))) break; + if (classIndex == ((longAt((void *)(actualObj))) & (classIndexMask()))) { + subsequentObject = actualObj; + goto l4; + } + } + actualObj = GIV(nilObj); + } + } + + /* it almost never is because usually this is preceded by a send of initialInstanceOf: */ + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(actualObj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + actualObj = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + actualObj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3:; + if (!(oopisLessThan(actualObj, GIV(endOfMemory)))) break; + if (classIndex == ((longAt((void *)(actualObj))) & (classIndexMask()))) { + subsequentObject = actualObj; + goto l4; + } + } + subsequentObject = null; + /* end instanceAfter: */ +l4: + if (subsequentObject) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),subsequentObject); + return; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } +} + + +/* Return the object following the receiver in the heap. Return the + SmallInteger zero when there are no more objects. + */ + + /* InterpreterPrimitives>>#primitiveNextObject */ +static void +primitiveNextObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt instance; + + if ((((longAt(GIV(stackPointer))) & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if ((instance = accessibleObjectAfter(longAt(GIV(stackPointer))))) { + assert(isInMemory(instance)); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),instance); + } + else { + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)0 << 3) | 1)); + } +} + + +/* A placeholder for primitives that haven't been implemented or are being + withdrawn gradually. Just absorbs any arguments and returns the receiver. + */ + + /* InterpreterPrimitives>>#primitiveNoop */ +static void +primitiveNoop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + /* InterpreterPrimitives>>#primitiveNotEqual */ +static void +primitiveNotEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + int result; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver != integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + result = (signedMachineIntegerValueOf(integerReceiver)) != (signedMachineIntegerValueOf(integerArgument)); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (result + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveNotEqualLargeIntegers */ +EXPORT(void) +primitiveNotEqualLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr != integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + +/* is the receiver/first argument not the same object as the (last) + argument?. pop argumentCount because this can be used as a mirror + primitive. + */ +/* is the receiver/first argument the same object as the (last) argument?. + pop argumentCount because this can be used as a mirror primitive. */ + + /* InterpreterPrimitives>>#primitiveNotIdentical */ +static void +primitiveNotIdentical(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt otherObject; + char *sp; + sqInt thisObject; + + thisObject = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + otherObject = longAt(GIV(stackPointer)); + if ((/* isOopForwarded: */ + ((!(otherObject & (tagMask())))) + && ((!((longAt((void *)(otherObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + || ((GIV(argumentCount) > 1) + && (/* isOopForwarded: */ + ((!(thisObject & (tagMask())))) + && ((!((longAt((void *)(thisObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (thisObject != otherObject + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + +/* This primitive is called from Squeak as... + arrayOfRoots uniquelyAccessibleObjects */ +/* This primitive answers an array of the receiver and every object in its + proper tree of subParts (ie, that is not refered to from anywhere else + outside the tree). + */ +/* This primitive could be used to implement the primitiveStoreImageSegment + segment, thanks to a suggestion from Igor Stassenko. Currently it is + used only to debug that primitive. */ + + /* InterpreterPrimitives>>#primitiveObjectsAccessibleFromRoots */ +static void +primitiveObjectsAccessibleFromRoots(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayOfRoots; + sqInt result; + + arrayOfRoots = longAt(GIV(stackPointer)); + + /* Essential type checks */ + if (!(/* isArray: */ + ((!(arrayOfRoots & (tagMask())))) + && (((byteAt((void *)(arrayOfRoots + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Must be indexable pointers */ + result = objectsAccessibleFromRoots(arrayOfRoots); + if (((((result) & 7) == 1)) + && (((result >> 3)) == PrimErrNoMemory)) { + fullGC(); + arrayOfRoots = longAt(GIV(stackPointer)); + result = objectsAccessibleFromRoots(arrayOfRoots); + } + if ((((result) & 7) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = (result >> 3); + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); + } +} + + +/* primitivePathTo: anObject using: stack followWeak: boolean + Answer a path to anObject from the root that does not pass through + the current context */ + + /* InterpreterPrimitives>>#primitivePathToUsing */ +EXPORT(sqInt) +primitivePathToUsing(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt current; + sqInt err; + sqInt fmt; + usqInt freeStartAtStart; + sqInt header; + sqInt headerSqInt; + sqInt i; + sqInt index; + sqInt iSqInt; + sqInt methodField; + sqInt next; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt path; + sqInt sp; + sqInt stack; + sqInt stackp; + sqInt stackSize; + sqInt toDoLimit; + sqInt toDoLimitSqInt; + + err = 0; + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (!(GIV(argumentCount) >= 2)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + if (!(((longAt(GIV(stackPointer))) == GIV(trueObj)) + || ((longAt(GIV(stackPointer))) == GIV(falseObj)))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + stack = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin pathTo:using:followWeak: */ + if (!(/* isArray: */ + ((!(stack & (tagMask())))) + && (((byteAt((void *)(stack + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + err = PrimErrBadArgument; + goto l5; + } + assert(allObjectsUnmarked()); + + /* check no allocations during search */ + freeStartAtStart = GIV(freeStart); + beRootIfOld(stack); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(stack + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(stack + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(stack - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + stackSize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + stackSize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + stackSize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + stackSize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + stackSize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + stackSize = 0; + /* end lengthOf: */ +l1: + + /* begin mark: */ + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(stack))); + byteAtput((void *)(stack + (markBitsByteOffset())),(byteAt((void *)(stack + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* no need. the current context is not reachable from the active process (suspendedContext is nil) + objectMemory mark: self activeProcess. */ + current = GIV(specialObjectsOop); + + /* begin mark: */ + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(current))); + byteAtput((void *)(current + (markBitsByteOffset())),(byteAt((void *)(current + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(current + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(current + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(current - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + index = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + index = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + index = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + index = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + index = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + index = 0; + /* end lengthOf: */ +l2: + stackp = 0; + while (1) { + while (((index -= 1)) >= -1) { + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)current)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)current)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)current)) <= (((usqInt)GIV(pages)))))))) { + next = (index >= 0 + ? fieldofFrame(index, ((char *) current)) + : GIV(nilObj)); + } + else { + if (index >= 0) { + next = (((longAt((void *)(current))) & (classIndexMask())) == ClassMethodContextCompactIndex + ? fieldOrSenderFPofContext(index, current) + : longAt((void *)((current + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))))); + } + else { + next = fetchClassOfNonImm(current); + } + } + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)next)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)next)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)next)) <= (((usqInt)GIV(pages)))))))) { + assert(isFrameonPage(((char *) next), stackPageFor(((char *) next)))); + } + else { + if (next >= heapBase) { + assert(checkOkayOop(next)); + } + } + if (next == (longAt(GIV(stackPointer) + (2 * BytesPerWord)))) { + assert(freeStartAtStart == (GIV(freeStart))); + unmarkAfterPathTo(); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(stackp, stack, current)); + assert(isNonImmediate(stack)); + if (oopisGreaterThanOrEqualTo(stack, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(current & (tagMask())))) + && (oopisLessThan(current, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(stack + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(stack); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(stackp) << (shiftForWord()))))),current); + pruneStackstackp(stack, stackp); + err = 0; + goto l5; + } + if (((!(next & (smallIntegerTag())))) + && ((/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)next)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)next)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)next)) <= (((usqInt)GIV(pages))))))) + ? !(frameIsMarked(next)) + : (next >= heapBase) + && ((!(((byteAt((void *)(next + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) + && (((/* isPointers: */ + ((!(next & (tagMask())))) + && (((byteAt((void *)(next + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + || (((byteAt((void *)(next + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((longAt(GIV(stackPointer))) == GIV(trueObj)) + || (!(isWeakNonImm(next))))))))) { + if ((stackp + 2) > stackSize) { + assert(freeStartAtStart == (GIV(freeStart))); + unmarkAfterPathTo(); + + /* begin nilFieldsOf: */ + toDoLimit = ((assert((classIndexOf(stack)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(stack + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(stack - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(i, stack, GIV(nilObj))); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } + err = PrimErrBadIndex; + goto l5; + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(stackp, stack, current)); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(stackp) << (shiftForWord()))))),current); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(stackp + 1, stack, (((usqInt)index << 3) | 1))); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)((stackp + 1)) << (shiftForWord()))))),(((usqInt)index << 3) | 1)); + stackp += 2; + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)(((char *) next)))) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)(((char *) next)))) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)(((char *) next)))) <= (((usqInt)GIV(pages)))))))) { + /* begin markFrame: */ + methodField = longAt((void *)(next + FoxMethod)); + if ((((usqInt)methodField)) < (startOfMemory())) { + longAtput((void *)(next + FoxMethod),methodField | 4); + } + else { + longAtput((void *)(next + FoxIFrameFlags),(longAt((void *)(next + FoxIFrameFlags))) | 2); + } + index = CtxtTempFrameStart + (stackPointerIndexForFrame(((char *) next))); + } + else { + /* begin mark: */ + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(next))); + byteAtput((void *)(next + (markBitsByteOffset())),(byteAt((void *)(next + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(next + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(next))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((next + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l3; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(next))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l3: + index = ((usqInt) (CtxtTempFrameStart + contextSize)); + goto l4; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(next)) > (isForwardedObjectClassIndexPun())); + index = (((numSlotsUsqInt = byteAt((void *)(next + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(next - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + goto l4; + } + if (fmt == (forwardedFormat())) { + index = 1; + goto l4; + } + if (fmt < (firstCompiledMethodFormat())) { + index = 0; + goto l4; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(next)); + headerSqInt = longAt((void *)((next + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + index = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l4:; + } + current = next; + } + } + if (current == GIV(specialObjectsOop)) { + assert(freeStartAtStart == (GIV(freeStart))); + unmarkAfterPathTo(); + + /* begin nilFieldsOf: */ + toDoLimitSqInt = ((assert((classIndexOf(stack)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(stack + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(stack - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) - 1; + for (iSqInt = 0; iSqInt <= toDoLimitSqInt; iSqInt += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(iSqInt, stack, GIV(nilObj))); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord()))))),GIV(nilObj)); + } + err = PrimErrNotFound; + goto l5; + } + index = ((longAt((void *)((stack + BaseHeaderSize) + ((((usqInt)((stackp - 1)) << (shiftForWord()))))))) >> 3); + current = longAt((void *)((stack + BaseHeaderSize) + ((((usqInt)((stackp - 2)) << (shiftForWord())))))); + stackp -= 2; + } + /* end pathTo:using:followWeak: */ +l5: + if (err) { + return (GIV(primFailCode) = err); + } + path = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),path); + return 0; +} + + /* InterpreterPrimitives>>#primitivePerformInSuperclass */ +static void +primitivePerformInSuperclass(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt argumentArray; + usqInt arraySize; + sqInt currentClass; + sqInt delta; + sqInt index; + sqInt lookupClass; + usqInt numSlots; + sqInt objOop; + sqInt offset; + sqInt performArgCount; + sqInt rcvr; + usqInt savedNewMethod; + sqInt selector; + sqInt tagBits; + sqInt top; + + rcvr = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + lookupClass = longAt(GIV(stackPointer)); + if (GIV(argumentCount) != 3) { + if (GIV(argumentCount) != 4) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + } + + /* e.g. object:perform:withArguments:inClass: */ + currentClass = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)); + while (currentClass != lookupClass) { + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOop); + } + currentClass = objOop; + if (currentClass == GIV(nilObj)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + selector = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + argumentArray = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin primitiveObject:perform:withArguments:lookedUpIn: */ + if (!(/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + + /* Check if number of arguments is reasonable; MaxNumArgs isn't available + so just use LargeContextSize */ + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (arraySize > (LargeContextSlots - CtxtTempFrameStart)) { + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + goto l1; + } + performArgCount = GIV(argumentCount); + + /* Recursion check: + | a | + a := Array with: #perform:withArguments: with: nil. + a at: 2 put: a. + a perform: a first withArguments: a. + | a | + a := Array with: #perform:withArguments: with: (Array with: #perform:withArguments: with: nil). + a last at: 2 put: a. + a perform: a first withArguments: a */ + if (!GIV(performWithArgumentsRecursionGuard)) { + GIV(performWithArgumentsRecursionGuard) = (lookupClass + ? longAt(GIV(stackPointer) + (1 * BytesPerWord)) + : longAt(GIV(stackPointer))); + } + + /* Push newMethod to save it in case of failure, + then push the actual receiver and the args in the array. */ + savedNewMethod = GIV(newMethod); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* Copy the arguments to the stack, in case of MNU, and lookup */ + for (index = 1; index <= arraySize; index += 1) { + arg = longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord())))))); + if (arg == GIV(performWithArgumentsRecursionGuard)) { + GIV(performWithArgumentsRecursionGuard) = null; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),arg); + } + if (!GIV(performWithArgumentsRecursionGuard)) { + /* begin pop: */ + GIV(stackPointer) += (2 + arraySize) * BytesPerWord; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l1; + } + GIV(argumentCount) = arraySize; + GIV(messageSelector) = selector; + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + if (printOnTrace()) { + printActivationNameForSelectorstartClass(GIV(messageSelector), (lookupClass + ? lookupClass + : /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)))); + cr(); + } + findNewMethodInClassTag((lookupClass + ? classTagForClass(lookupClass) + : /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())))); + + /* Only test CompiledMethods for argument count - any other objects playacting as CMs will have to take their chances */ + if ((/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && ((argumentCountOf(GIV(newMethod))) != GIV(argumentCount))) { + /* Restore the state by popping the array entries, the actual receiver and the saved + newMethod, leaving the selector and array, and fail. N.B. If an MNU has happened + then argumentCount will match newMethod, so this code will not be reached. + These asserts check that an MNU has not occurred if the argumentCount doesn't match the newMethod. */ + assert((stackTop()) == ((arraySize == 0 + ? rcvr + : fetchPointerofObject(arraySize - 1, argumentArray)))); + assert(GIV(argumentCount) == arraySize); + + /* begin pop: */ + GIV(stackPointer) += (arraySize + 1) * BytesPerWord; + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(newMethod) = top; + + /* Must reset primitiveFunctionPointer for checkForAndFollowForwardedPrimitiveState */ + GIV(argumentCount) = performArgCount; + primitiveFunctionPointer = (lookupClass + ? primitivePerformInSuperclass + : primitivePerformWithArgs); + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + goto l1; + } + + /* Cannot fail this primitive from here-on. Slide the actual receiver and arguments down + to replace the perform arguments and saved newMethod and then execute the new + method. Use argumentCount not arraySize because an MNU may have changed it. */ + + /* +2 = receiver + saved newMethod */ + delta = BytesPerWord * (performArgCount + 2); + for (offset = (GIV(argumentCount) * BytesPerWord); offset >= 0; offset += (-BytesPerWord)) { + longAtput((GIV(stackPointer) + offset) + delta,longAt(GIV(stackPointer) + offset)); + } + + /* Part of the recursion guard above. To ensure we fail in the original method, restore + newMethod, and don't do the activateNewMethod implicit in executeNewMethod. */ + if ((primitiveFunctionPointer == primitivePerformWithArgs) + || (primitiveFunctionPointer == primitivePerformInSuperclass)) { + /* This should of course be a tail call, which could be done via setjmp/longjmp. + But this is vanity code. After all how often is a recursive invocation of + primitivePerformWithArgs et al made? */ + GIV(newMethod) = savedNewMethod; + GIV(argumentCount) = performArgCount; + + /* begin pop: */ + GIV(stackPointer) += (performArgCount + 2) * BytesPerWord; + + /* prevent inlining... */ + slowPrimitiveResponse(); + GIV(performWithArgumentsRecursionGuard) = null; + goto l1; + } + GIV(performWithArgumentsRecursionGuard) = null; + + /* begin pop: */ + GIV(stackPointer) += (performArgCount + 2) * BytesPerWord; + + /* prevent inlining... */ + executeNewMethod(); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + /* end primitiveObject:perform:withArguments:lookedUpIn: */ +l1:; +} + + /* InterpreterPrimitives>>#primitivePerformWithArgs */ +static void +primitivePerformWithArgs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt actualReceiver; + sqInt arg; + sqInt argumentArray; + usqInt arraySize; + sqInt delta; + sqInt index; + usqInt numSlots; + sqInt offset; + sqInt performArgCount; + usqInt savedNewMethod; + sqInt selector; + sqInt tagBits; + sqInt top; + + actualReceiver = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + selector = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + argumentArray = longAt(GIV(stackPointer)); + + /* begin primitiveObject:perform:withArguments:lookedUpIn: */ + if (!(/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + + /* Check if number of arguments is reasonable; MaxNumArgs isn't available + so just use LargeContextSize */ + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (arraySize > (LargeContextSlots - CtxtTempFrameStart)) { + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + goto l1; + } + performArgCount = GIV(argumentCount); + + /* Recursion check: + | a | + a := Array with: #perform:withArguments: with: nil. + a at: 2 put: a. + a perform: a first withArguments: a. + | a | + a := Array with: #perform:withArguments: with: (Array with: #perform:withArguments: with: nil). + a last at: 2 put: a. + a perform: a first withArguments: a */ + if (!GIV(performWithArgumentsRecursionGuard)) { + GIV(performWithArgumentsRecursionGuard) = longAt(GIV(stackPointer)); + } + + /* Push newMethod to save it in case of failure, + then push the actual receiver and the args in the array. */ + savedNewMethod = GIV(newMethod); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),actualReceiver); + + /* Copy the arguments to the stack, in case of MNU, and lookup */ + for (index = 1; index <= arraySize; index += 1) { + arg = longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord())))))); + if (arg == GIV(performWithArgumentsRecursionGuard)) { + GIV(performWithArgumentsRecursionGuard) = null; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),arg); + } + if (!GIV(performWithArgumentsRecursionGuard)) { + /* begin pop: */ + GIV(stackPointer) += (2 + arraySize) * BytesPerWord; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l1; + } + GIV(argumentCount) = arraySize; + GIV(messageSelector) = selector; + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), /* fetchClassTagOf: */ + ((tagBits = actualReceiver & (tagMask())) + ? tagBits + : (longAt((void *)(actualReceiver))) & (classIndexMask()))); + if (printOnTrace()) { + printActivationNameForSelectorstartClass(GIV(messageSelector), /* fetchClassOf: */ + ((tagBits = actualReceiver & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(actualReceiver))); + cr(); + } + findNewMethodInClassTag(/* fetchClassTagOf: */ + ((tagBits = actualReceiver & (tagMask())) + ? tagBits + : (longAt((void *)(actualReceiver))) & (classIndexMask()))); + + /* Only test CompiledMethods for argument count - any other objects playacting as CMs will have to take their chances */ + if ((/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && ((argumentCountOf(GIV(newMethod))) != GIV(argumentCount))) { + /* Restore the state by popping the array entries, the actual receiver and the saved + newMethod, leaving the selector and array, and fail. N.B. If an MNU has happened + then argumentCount will match newMethod, so this code will not be reached. + These asserts check that an MNU has not occurred if the argumentCount doesn't match the newMethod. */ + assert((stackTop()) == ((arraySize == 0 + ? actualReceiver + : fetchPointerofObject(arraySize - 1, argumentArray)))); + assert(GIV(argumentCount) == arraySize); + + /* begin pop: */ + GIV(stackPointer) += (arraySize + 1) * BytesPerWord; + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(newMethod) = top; + + /* Must reset primitiveFunctionPointer for checkForAndFollowForwardedPrimitiveState */ + GIV(argumentCount) = performArgCount; + primitiveFunctionPointer = primitivePerformWithArgs; + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + goto l1; + } + + /* Cannot fail this primitive from here-on. Slide the actual receiver and arguments down + to replace the perform arguments and saved newMethod and then execute the new + method. Use argumentCount not arraySize because an MNU may have changed it. */ + + /* +2 = receiver + saved newMethod */ + delta = BytesPerWord * (performArgCount + 2); + for (offset = (GIV(argumentCount) * BytesPerWord); offset >= 0; offset += (-BytesPerWord)) { + longAtput((GIV(stackPointer) + offset) + delta,longAt(GIV(stackPointer) + offset)); + } + + /* Part of the recursion guard above. To ensure we fail in the original method, restore + newMethod, and don't do the activateNewMethod implicit in executeNewMethod. */ + if ((primitiveFunctionPointer == primitivePerformWithArgs) + || (primitiveFunctionPointer == primitivePerformInSuperclass)) { + /* This should of course be a tail call, which could be done via setjmp/longjmp. + But this is vanity code. After all how often is a recursive invocation of + primitivePerformWithArgs et al made? */ + GIV(newMethod) = savedNewMethod; + GIV(argumentCount) = performArgCount; + + /* begin pop: */ + GIV(stackPointer) += (performArgCount + 2) * BytesPerWord; + + /* prevent inlining... */ + slowPrimitiveResponse(); + GIV(performWithArgumentsRecursionGuard) = null; + goto l1; + } + GIV(performWithArgumentsRecursionGuard) = null; + + /* begin pop: */ + GIV(stackPointer) += (performArgCount + 2) * BytesPerWord; + + /* prevent inlining... */ + executeNewMethod(); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + /* end primitiveObject:perform:withArguments:lookedUpIn: */ +l1:; +} + + /* InterpreterPrimitives>>#primitivePinnedNew */ +static void +primitivePinnedNew(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt classObj; + sqInt err; + int hash; + sqInt instSpec; + sqInt newObj; + sqInt numSlots; + sqInt obj; + usqInt p; + sqInt reasonCode; + usqInt toDoLimit; + + + /* For the mirror prims check that the class obj is actually a valid class. + Allocate a new fixed-size instance. Fail if the allocation would leave + less than lowSpaceThreshold bytes free. This *will not* cause a GC :-) */ + classObj = longAt(GIV(stackPointer)); + + /* begin inOldSpaceInstantiatePinnedClass: */ + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + if (!(/* isFixedSizePointerFormat: */ + (instSpec <= (nonIndexablePointerFormat())) + || (instSpec == (ephemeronFormat())))) { + obj = null; + goto l1; + } + + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + obj = null; + goto l1; + } + numSlots = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((((usqInt)(numSlots) << (shiftForWord())))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpec, classIndex); + if (newObj) { + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),GIV(nilObj)); + } + } + obj = newObj; + /* end inOldSpaceInstantiatePinnedClass: */ +l1: + if (obj) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + reasonCode = (isFixedSizePointerFormat((((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + +/* Allocate a new pinned indexable instance. Fail if the allocation would + leave less than lowSpaceThreshold bytes free. + */ + + /* InterpreterPrimitives>>#primitivePinnedNewWithArg */ +static void +primitivePinnedNewWithArg(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt classObj; + sqInt err; + sqInt fillValue; + int hash; + sqInt instSpec; + sqInt instSpecSqInt; + sqInt newObj; + usqInt numSlots; + sqInt obj; + usqInt p; + sqInt reasonCode; + usqIntptr_t size; + usqInt toDoLimit; + sqInt value; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + size = null; + goto l1; + } + size = value; + goto l1; + } + + /* don't inline the rare case */ + size = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* positiveMachineIntegerValueOf: succeeds only for non-negative integers. */ + classObj = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin inOldSpaceInstantiatePinnedClass:indexableSize: */ + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpecSqInt = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + fillValue = 0; + switch (instSpecSqInt) { + case arrayFormat(): + numSlots = size; + fillValue = GIV(nilObj); + break; + case indexablePointersFormat(): + case weakArrayFormat(): + numSlots = (classFormat & ((1U << (fixedFieldsFieldWidth())) - 1)) + size; + fillValue = GIV(nilObj); + break; + case sixtyFourBitIndexableFormat(): + numSlots = size; + break; + case firstLongFormat(): + if ((classIndex == ClassFloatCompactIndex) + && (size != 2)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + obj = null; + goto l2; + } + numSlots = (size + 1) / 2; + instSpecSqInt += size & 1; + break; + case firstShortFormat(): + numSlots = (size + 3) / 4; + instSpecSqInt += (4 - size) & 3; + break; + case firstByteFormat(): + numSlots = (size + 7) / 8; + instSpecSqInt += (8 - size) & 7; + break; + default: + if ((size != 0) + || (instSpecSqInt > 5 /* lastPointerFormat */)) { + obj = null; + goto l2; + } + numSlots = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + fillValue = GIV(nilObj); + } + + /* non-indexable + Some Squeak images include funky fixed subclasses of abstract variable + superclasses. e.g. DirectoryEntry as a subclass of ArrayedCollection. + The (Threaded)FFIPlugin expects to be able to instantiate ExternalData via + this method. + Hence allow fixed classes to be instantiated here iff nElements = 0. */ + if (!classIndex) { + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + obj = null; + goto l2; + } + } + if (numSlots > (0x10000000000LL)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + obj = null; + goto l2; + } + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpecSqInt, classIndex); + if (newObj) { + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),fillValue); + } + } + obj = newObj; + /* end inOldSpaceInstantiatePinnedClass:indexableSize: */ +l2: + if (obj) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + instSpec = (((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer) + (1 * BytesPerWord))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + reasonCode = ((/* isIndexableFormat: */ + (instSpec >= (arrayFormat())) + && ((instSpec <= (weakArrayFormat())) + || (instSpec >= (sixtyFourBitIndexableFormat())))) + && (!(instSpec >= (firstCompiledMethodFormat()))) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + +/* Primitive. Answer the last primitive method sampled by the profiler. */ + + /* InterpreterPrimitives>>#primitiveProfilePrimitive */ +EXPORT(sqInt) +primitiveProfilePrimitive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = (!(GIV(profileMethod)) + ? GIV(nilObj) + : GIV(profileMethod)); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return 0; +} + + +/* Primitive. Answer the last sample taken by the profiler, or nil if the + profiler isn't active. + See also primitiveProfileStart. + */ + + /* InterpreterPrimitives>>#primitiveProfileSample */ +EXPORT(sqInt) +primitiveProfileSample(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = (!(GIV(profileProcess)) + ? GIV(nilObj) + : GIV(profileProcess)); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return 0; +} + + +/* Primitive. Begin profiling execution by using the high-resolution clock + instead of a time-based process + (which is limited to timing resolution and triggers off the same signal + that many of the processes being + profiled trigger off leading to consistently wrong results). The argument + is the number of ticks of the + high-resolution clock to elapse before taking a sample. The sample is + stored in the profileProcess and + profileMethod iVars which can be retrieved via primitiveProfileSample and + primitiveProfilePrimitive. So also void the sample iVars. Once a sample is + taken, the semaphore installed via + primitiveProfileSemaphore is signalled. If the argument is less or equal + to zero, profiling is disabled. */ + + /* InterpreterPrimitives>>#primitiveProfileStart */ +EXPORT(sqInt) +primitiveProfileStart(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt deltaTicks; + + deltaTicks = longAt(GIV(stackPointer)); + if ((((deltaTicks) & 7) == 1)) { + GIV(deferProfileCheckForVNCS) = 0; + deltaTicks = (deltaTicks >> 3); + GIV(nextProfileTick) = (deltaTicks > 0 + ? (ioHighResClock()) + deltaTicks + : 0); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return 0; + } + return (GIV(primFailCode) = PrimErrBadArgument); +} + + /* InterpreterPrimitives>>#primitiveQuit */ +static void +primitiveQuit(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + ioExitWithErrorCode((GIV(argumentCount) == 1 + ? ((longAt(GIV(stackPointer))) >> 3) + : 0)); +} + + +/* Rounds negative results towards zero. */ + + /* InterpreterPrimitives>>#primitiveQuo */ +static void +primitiveQuo(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArg; + sqInt integerPointer; + sqInt integerRcvr; + sqInt integerResult; + + integerResult = 0; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + integerRcvr = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerRcvr = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + integerArg = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArg = 0; + } + + /* begin success: */ + if (!integerArg) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + integerResult = /* quot:ient: */ + (integerRcvr > 0 + ? (integerArg > 0 + ? integerRcvr / integerArg + : 0 - (integerRcvr / (0 - integerArg))) + : (integerArg > 0 + ? 0 - ((0 - integerRcvr) / integerArg) + : (0 - integerRcvr) / (0 - integerArg))); + } + + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)(integerResult)) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerResult << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveQuoLargeIntegers */ +EXPORT(void) +primitiveQuoLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (!b) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return; + } + result = a / b; + oopResult = magnitude64BitIntegerForneg(result, bIsNegative != aIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Relinquish the processor for up to the given number of microseconds. The + exact behavior of this primitive is platform dependent. + */ + + /* InterpreterPrimitives>>#primitiveRelinquishProcessor */ +static void +primitiveRelinquishProcessor(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerPointer; + sqInt microSecs; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + microSecs = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + microSecs = 0; + } + if (!GIV(primFailCode)) { + if (!GIV(nextProfileTick)) { + ioRelinquishProcessorForMicroseconds(microSecs); + + /* In simulation we allow ioRelinquishProcessorForMicroseconds: to fail so that + we can arrange that the simulator responds to input events promply. This + *DOES NOT HAPPEN* in the real vm. */ + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveRemLargeIntegers */ +EXPORT(void) +primitiveRemLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (!b) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return; + } + result = a % b; + oopResult = magnitude64BitIntegerForneg(result, aIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* The character scanner primitive + primScanCharactersFrom: startIndex to: stopIndex in: sourceString rightX: + rightX stopConditions: stops kern: kernDelta + This is the inner loop of measurement and scanning for text display. March + through sourceString from startIndex to stopIndex. + If any character is flagged with a non-nil entry in stops, then return the + corresponding value. Determine width of each character + from xTable, indexed by map. If destX would exceed rightX, then return + stops at: 258. Advance destX by the width of the + character. If stopIndex has been reached, then return stops at: 257. + + Receiver inst vars: + destX horizontal position for next character (distance from left of + composition area) + lastIndex the Integer index of next character to be processed in the + sourceString argument + xTable an array mapping character code to glyph x coordinate in a form + of glyphs + map an array mapping character code to glyph position. + + Failure codes: + PrimErrBadArgument one of the indices is not a SmallInteger, or the sting + argument is not a byte string, or the stops array is too small. + PrimErrBadReceiver the receiver has less than four inst vars or any of + scanDestX & scanLastIndex are not SmallIntegers, or the scanXTable or + scanMap are not arrays of the right size + PrimErrBadIndex either a glyph index in scanMap or a start index in + scanXTable are not SmallIntegers + PrimErrLimitExceeded arithmetic on scanDestX has wrapped around to become + negative + */ + + /* InterpreterPrimitives>>#primitiveScanCharacters */ +static void +primitiveScanCharacters(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ascii; + sqInt glyphIndex; + sqInt kernDelta; + sqInt maxGlyph; + sqInt nextDestX; + sqInt rcvr; + sqInt scanDestX; + sqInt scanLastIndex; + sqInt scanMap; + sqInt scanRightX; + sqInt scanStartIndex; + sqInt scanStopIndex; + sqInt scanXTable; + sqInt sourceString; + sqInt sourceX; + sqInt sourceX2; + char *sp1; + sqInt stopReason; + sqInt stops; + + if (GIV(argumentCount) != 6) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + + /* Load the receiver and arguments */ + kernDelta = longAt(GIV(stackPointer)); + stops = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + scanRightX = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + sourceString = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + scanStopIndex = longAt(GIV(stackPointer) + (4 * BytesPerWord)); + scanStartIndex = longAt(GIV(stackPointer) + (5 * BytesPerWord)); + rcvr = longAt(GIV(stackPointer) + (6 * BytesPerWord)); + if (!(((((kernDelta) & 7) == 1)) + && (((((scanRightX) & 7) == 1)) + && (((((scanStopIndex) & 7) == 1)) + && ((((scanStartIndex) & 7) == 1)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + kernDelta = (kernDelta >> 3); + scanRightX = (scanRightX >> 3); + scanStopIndex = (scanStopIndex >> 3); + scanStartIndex = (scanStartIndex >> 3); + + /* check argument type and range and rcvr */ + if (!((/* isArray: */ + ((!(stops & (tagMask())))) + && (((byteAt((void *)(stops + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((slotSizeOf(stops)) >= 258) + && ((/* isBytes: */ + ((!(sourceString & (tagMask())))) + && (((byteAt((void *)(sourceString + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((scanStartIndex > 0) + && ((scanStopIndex > 0) + && (scanStopIndex <= (byteSizeOf(sourceString))))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (!((/* isPointers: */ + ((!(rcvr & (tagMask())))) + && (((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((slotSizeOf(rcvr)) >= 4))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* Check required rcvr instVars */ + scanDestX = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord())))); + scanLastIndex = longAt((void *)((rcvr + BaseHeaderSize) + (1U << (shiftForWord())))); + scanXTable = longAt((void *)((rcvr + BaseHeaderSize) + (2U << (shiftForWord())))); + scanMap = longAt((void *)((rcvr + BaseHeaderSize) + (3U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(scanXTable & (tagMask())))) + && (((byteAt((void *)(scanXTable + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((/* isArray: */ + ((!(scanMap & (tagMask())))) + && (((byteAt((void *)(scanMap + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((slotSizeOf(scanMap)) == 0x100) + && (((((scanDestX) & 7) == 1)) + && ((((scanLastIndex) & 7) == 1))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if (((scanDestX = (scanDestX >> 3))) < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + scanLastIndex = (scanLastIndex >> 3); + maxGlyph = (slotSizeOf(scanXTable)) - 2; + + /* Okay, here we go. We have eliminated nearly all failure + conditions, to optimize the inner fetches. */ + scanLastIndex = scanStartIndex; + while (scanLastIndex <= scanStopIndex) { + /* Known to be okay since scanStartIndex > 0 and scanStopIndex <= sourceString size */ + ascii = byteAt((void *)((sourceString + BaseHeaderSize) + (scanLastIndex - 1))); + + /* Known to be okay since stops size >= 258 */ + if (!(((stopReason = longAt((void *)((stops + BaseHeaderSize) + ((((usqInt)(ascii) << (shiftForWord())))))))) == GIV(nilObj))) { + if (!(scanDestX >= 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrLimitExceeded; + return; + } + storeIntegerofObjectwithValue(0, rcvr, scanDestX); + storeIntegerofObjectwithValue(1, rcvr, scanLastIndex); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),stopReason); + return; + } + + /* Store everything back and get out of here since some stop condition needs to be checked + Known to be okay since scanMap size = 256 */ + glyphIndex = longAt((void *)((scanMap + BaseHeaderSize) + ((((usqInt)(ascii) << (shiftForWord())))))); + + /* fail if the glyphIndex is out of range */ + if (!(((((glyphIndex) & 7) == 1)) + && ((((glyphIndex = (glyphIndex >> 3))) >= 0) + && (glyphIndex <= maxGlyph)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + sourceX = longAt((void *)((scanXTable + BaseHeaderSize) + ((((usqInt)(glyphIndex) << (shiftForWord())))))); + sourceX2 = longAt((void *)((scanXTable + BaseHeaderSize) + ((((usqInt)((glyphIndex + 1)) << (shiftForWord())))))); + + /* Above may fail if non-integer entries in scanXTable */ + if (!(((((sourceX) & 7) == 1)) + && ((((sourceX2) & 7) == 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + nextDestX = (scanDestX + ((sourceX2 >> 3))) - ((sourceX >> 3)); + if (nextDestX > scanRightX) { + /* Store everything back and get out of here since we got to the right edge */ + storeIntegerofObjectwithValue(0, rcvr, scanDestX); + storeIntegerofObjectwithValue(1, rcvr, scanLastIndex); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((sp1 = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),longAt((void *)((stops + BaseHeaderSize) + ((((usqInt)((CrossedX - 1)) << (shiftForWord()))))))); + GIV(stackPointer) = sp1; + return; + } + if (!((nextDestX >= 0) + && (((scanDestX = nextDestX + kernDelta)) >= 0))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrLimitExceeded; + return; + } + scanLastIndex += 1; + } + storeIntegerofObjectwithValue(0, rcvr, scanDestX); + storeIntegerofObjectwithValue(1, rcvr, scanStopIndex); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),longAt((void *)((stops + BaseHeaderSize) + ((((usqInt)((EndOfRun - 1)) << (shiftForWord()))))))); +} + + +/* Return a SmallInteger indicating the current depth of the OS screen. + Negative values are used to imply LSB type pixel format an there is some + support in the VM for handling either MSB or LSB + */ + + /* InterpreterPrimitives>>#primitiveScreenDepth */ +EXPORT(sqInt) +primitiveScreenDepth(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt depth; + + depth = ioScreenDepth(); + if (GIV(primFailCode)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),(((usqInt)depth << 3) | 1)); + return 0; +} + + +/* Answer a float indicating the current scale factor for pixels of the + Smalltalk window. + */ + + /* InterpreterPrimitives>>#primitiveScreenScaleFactor */ +EXPORT(sqInt) +primitiveScreenScaleFactor(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double factor; + char *sp; + + factor = ioScreenScaleFactor(); + if (!GIV(primFailCode)) { + /* begin methodReturnFloat: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),floatObjectOf(factor)); + GIV(stackPointer) = sp; + } + return 0; +} + + +/* Answer a point indicating the current size of the Smalltalk window. + Currently there is a limit of 65535 in each direction because the + point is encoded into a single 32bit value in the image header. + This might well become a problem one day */ + + /* InterpreterPrimitives>>#primitiveScreenSize */ +static void +primitiveScreenSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newObj; + usqInt numBytes; + sqInt oop; + usqInt pointResult; + sqInt pointWord; + sqInt xValue; + sqInt yValue; + + pointWord = ioScreenSize(); + xValue = (((usqInt)(pointWord)) >> 16) & 0xFFFF; + yValue = pointWord & 0xFFFF; + + /* begin makePointwithxValue:yValue: */ + /* begin eeInstantiatePoint */ + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert(((YIndex + 1) >= 0) + && ((knownClassAtIndex(ClassPointCompactIndex)) != GIV(nilObj))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(knownClassAtIndex(ClassPointCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert((YIndex + 1) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (((YIndex + 1) < 1 + ? 8 /* allocationUnit */ + : (YIndex + 1) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + pointResult = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) (YIndex + 1))) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + ClassPointCompactIndex); + GIV(freeStart) += numBytes; + pointResult = newObj; + /* end eeInstantiatePoint */ +l1: + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pointResult)) + && (!(isForwarded(pointResult)))); + assert(validStorePointerUncheckedArgs(XIndex, pointResult, (((usqInt)xValue << 3) | 1))); + longAtput((void *)((pointResult + BaseHeaderSize) + ((((usqInt)(XIndex) << (shiftForWord()))))),(((usqInt)xValue << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pointResult)) + && (!(isForwarded(pointResult)))); + assert(validStorePointerUncheckedArgs(YIndex, pointResult, (((usqInt)yValue << 3) | 1))); + longAtput((void *)((pointResult + BaseHeaderSize) + ((((usqInt)(YIndex) << (shiftForWord()))))),(((usqInt)yValue << 3) | 1)); + oop = pointResult; + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Return the number of seconds since January 1, 1901 as an integer. */ + + /* InterpreterPrimitives>>#primitiveSecondsClock */ +static void +primitiveSecondsClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = (((usqInt)((((usqInt)(ioSecondsNow()))) & 0xFFFFFFFFU) << 3) | 1); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Ask the GUI to set the requested display mode. + See DisplayScreen class depth:width:height:fullscreen: */ + + /* InterpreterPrimitives>>#primitiveSetDisplayMode */ +static void +primitiveSetDisplayMode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt d; + sqInt fsFlag; + sqInt h; + sqInt integerPointer; + sqInt okay; + char *sp; + sqInt w; + + /* begin booleanValueOf: */ + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + fsFlag = 1; + goto l1; + } + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + fsFlag = 0; + goto l1; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + fsFlag = null; + /* end booleanValueOf: */ +l1: + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + h = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + h = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + w = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + w = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + d = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + d = 0; + } + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + okay = ioSetDisplayMode(w, h, d, fsFlag); + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrOperationFailed; + return; + } + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (okay + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; +} + + +/* On platforms that support it, set full-screen mode to the value of the + boolean argument. + */ + + /* InterpreterPrimitives>>#primitiveSetFullScreen */ +static void +primitiveSetFullScreen(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt argOop; + + argOop = longAt(GIV(stackPointer)); + if (argOop == GIV(trueObj)) { + ioSetFullScreen(1); + } + else { + if (argOop == GIV(falseObj)) { + ioSetFullScreen(0); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + if (!GIV(primFailCode)) { + /* begin setFullScreenFlag: */ + imageHeaderFlags = (argOop == GIV(trueObj) + ? imageHeaderFlags | 1 + : ((imageHeaderFlags | 1) - 1)); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } +} + + /* InterpreterPrimitives>>#primitiveSetImmutability */ +#if IMMUTABILITY +static void +primitiveSetImmutability(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt processLists; + sqInt rcvr; + sqInt scheduler; + char *sp; + sqInt wasImmutable; + + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if ((GIV(argumentCount) > 1) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* fail if forwarded; the retry machinery will fix things up. */ + wasImmutable = +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ; + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + /* begin canBeImmutable: */ + assert(isNonImmediate(rcvr)); + + /* For now we fail the primitive for contexts to we ensure there are no immutable contexts. + Later we can consider having immutable contexts and send cannotReturn callback + when returning to an immutable context. That would mean that setting a context + to immutable would require a divorce and returns to immutable context are + necessarily across stack pages */ + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + goto l1; + } + + /* Weak structures can't be immutable */ + + /* begin isEphemeron: */ + assert(isNonImmediate(rcvr)); + format = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + if (format == (ephemeronFormat())) { + goto l1; + } + + /* begin isWeakNonImm: */ + format = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + if (format == (weakArrayFormat())) { + goto l1; + } + + /* No clue what is going on for semaphores so they can't be immutable */ + if (((longAt((void *)(rcvr))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))) { + goto l1; + } + + /* Simple version of process management: we forbid Process and LinkedList instances to be immutable + as well as the Processor and the array of activeProcess */ + + /* begin fetchPointer:ofObject: */ + scheduler = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + processLists = longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + if (rcvr == scheduler) { + goto l1; + } + if (rcvr == processLists) { + goto l1; + } + + /* Is it a linkedList ? */ + if ((classIndexOf(longAt((void *)((processLists + BaseHeaderSize) + (1U << (shiftForWord())))))) == ((longAt((void *)(rcvr))) & (classIndexMask()))) { + goto l1; + } + + /* is it a Process ? */ + if ((classIndexOf(longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))))) == ((longAt((void *)(rcvr))) & (classIndexMask()))) { + goto l1; + } + + /* The rest of the code is relative to process management: the Processor (the active + process scheduler) can't be immutable, as well as all the objects relative to Process management + scheduler := self fetchPointer: ValueIndex ofObject: (self splObj: SchedulerAssociation). + processLists := objectMemory fetchPointer: ProcessListsIndex ofObject: scheduler. + ((objectMemory formatOf: oop) = objectMemory nonIndexablePointerFormat) + ifFalse: + [ (objectMemory isArrayNonImm: oop) ifFalse: [^ true]. + ^ (oop = processLists) not ]. + (objectMemory numSlotsOf: oop) >= 2 ifFalse: [^ true]. + + is the oop the scheduler itself ? + oop = scheduler ifTrue: [^ false]. + 1 to: (objectMemory numSlotsOf: processLists) do: [ :i | + + is the oop one of the linked lists ? + (list := processLists at: i) = oop ifTrue: [^ false]. + + is the oop one of the runnable process ? + first := objectMemory fetchPointer: FirstLinkIndex ofObject: list. + first = objectMemory nilObject ifFalse: + [ last := objectMemory fetchPointer: LastLinkIndex ofObject: list. + link := first. + [ link = last ] whileFalse: + [ link = oop ifTrue: [^ false]. + link := objectMemory fetchPointer: NextLinkIndex ofObject: link. ] ] ]. */ + goto l2; +l1: + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + return; +l2: + + /* begin setIsImmutableOf:to: */ + longAtput((void *)(rcvr),(longAt((void *)(rcvr))) | (1U << (immutableBitShift()))); + } + else { + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + /* begin setIsImmutableOf:to: */ + longAtput((void *)(rcvr),(longAt((void *)(rcvr))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (wasImmutable + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; +} +#endif /* IMMUTABILITY */ + + +/* Set the user interrupt keycode. The keycode is an integer whose encoding + is described in the comment for primitiveKbdNext. + */ + + /* InterpreterPrimitives>>#primitiveSetInterruptKey */ +static void +primitiveSetInterruptKey(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerPointer; + sqInt keycode; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + keycode = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + keycode = 0; + } + if (!GIV(primFailCode)) { + /* begin setInterruptKeycode: */ + GIV(interruptKeycode) = keycode; + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } +} + + +/* Primitive. Set the VM's log directory */ + + /* InterpreterPrimitives>>#primitiveSetLogDirectory */ +EXPORT(sqInt) +primitiveSetLogDirectory(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + usqInt numBytes; + usqInt numSlots; + sqInt stringOop; + sqInt sz; + + stringOop = longAt(GIV(stackPointer)); + if (!(/* isBytes: */ + ((!(stringOop & (tagMask())))) + && (((byteAt((void *)(stringOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + + /* begin byteSizeOf: */ + if (((stringOop & (tagMask())) != 0)) { + sz = 0; + goto l1; + } + + /* begin numBytesOf: */ + fmt = (byteAt((void *)(stringOop + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(stringOop)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(stringOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(stringOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + sz = numBytes - (fmt & 7); + goto l1; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + sz = numBytes; + goto l1; + } + if (fmt >= (firstShortFormat())) { + sz = numBytes - (((fmt & 3) << 1)); + goto l1; + } + + /* fmt >= self firstLongFormat */ + sz = numBytes - (((fmt & 1) << 2)); + /* end byteSizeOf: */ +l1: + ioSetLogDirectoryOfSize(firstIndexableField(stringOop), sz); + if (!GIV(primFailCode)) { + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + return 0; +} + + /* InterpreterPrimitives>>#primitiveSetOrHasIdentityHash */ +static void +primitiveSetOrHasIdentityHash(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt hasHash; + sqInt hash; + sqInt isReceiverAClass; + sqInt lastArg; + int oldHash; + char *sp; + sqInt thisReceiver; + + if (!GIV(argumentCount)) { + hasHash = ((!((longAt(GIV(stackPointer))) & (tagMask())))) + && ((long32At((void *)((longAt(GIV(stackPointer))) + 4))) & (identityHashHalfWordMask())); + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (hasHash + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return; + } + isReceiverAClass = 0; + if (GIV(argumentCount) == 2) { + lastArg = longAt(GIV(stackPointer)); + if ((((lastArg) & 7) == 1)) { + hash = lastArg; + thisReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + } + else { + thisReceiver = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + hash = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (lastArg == GIV(trueObj)) { + isReceiverAClass = 1; + } + else { + if (!(lastArg == GIV(falseObj))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + } + } + else { + thisReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + hash = longAt(GIV(stackPointer)); + } + + /* anObject primitiveSetIdentityHashTo: hash */ + if (!(((((hash) & 7) == 1)) + && ((/* if written naturally, clang warns: unsequenced modification and access to 'hash' */ + (hash = (hash >> 3)), + (hash & (identityHashHalfWordMask())) == hash)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (!((!(thisReceiver & (tagMask()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = (thisReceiver == (longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord))) + ? PrimErrBadReceiver + : PrimErrBadArgument); + return; + } + oldHash = (long32At((void *)(thisReceiver + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(thisReceiver + 4),((((long32At((void *)(thisReceiver + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + if (isReceiverAClass) { + classAtIndexput(hash, thisReceiver); + + /* this figures out if the index is ambiguous and fixes all the instances if needed */ + allInstancesOf(thisReceiver); + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)oldHash << 3) | 1)); +} + + +/* Treat the receiver, which can be indexible by either bytes or words, as + an array of signed 16-bit values. Answer the contents of the given index. + Note that the index specifies the i-th 16-bit entry, not the i-th byte or + word. */ + + /* InterpreterPrimitives>>#primitiveShortAt */ +static void +primitiveShortAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt rcvr; + sqInt value; + + index = longAt(GIV(stackPointer)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(/* isWordsOrBytes: */ + ((!(rcvr & (tagMask())))) + && (isWordsOrBytesNonImm(rcvr)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + return; + } + index = (index >> 3); + if (!((index >= 1) + && (index <= (((usqInt)((numBytesOf(rcvr)))) >> 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + value = shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 1))))); + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)value << 3) | 1)); +} + + +/* Treat the receiver, which can be indexible by either bytes or words, as an + array of signed 16-bit values. Set the contents of the given index to the + given value. + Note that the index specifies the i-th 16-bit entry, not the i-th byte or + word. + */ + + /* InterpreterPrimitives>>#primitiveShortAtPut */ +static void +primitiveShortAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt rcvr; + sqInt value; + + value = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(((((value) & 7) == 1)) + && (((((index) & 7) == 1)) + && (((value = (value >> 3)), + (value >= -32768) + && (value <= 0x7FFF)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (!(/* isWordsOrBytes: */ + ((!(rcvr & (tagMask())))) + && (isWordsOrBytesNonImm(rcvr)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + return; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + return; + } + index = (index >> 3); + if (!((index >= 1) + && (index <= (((usqInt)((numBytesOf(rcvr)))) >> 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 1)))),value); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),(((usqInt)value << 3) | 1)); +} + + +/* Force the given rectangular section of the Display to be + copied to the screen. */ + + /* InterpreterPrimitives>>#primitiveShowDisplayRect */ +static void +primitiveShowDisplayRect(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bottom; + sqInt integerPointer; + sqInt left; + sqInt right; + sqInt top; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + bottom = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + bottom = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + top = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + top = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + right = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + right = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + left = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + left = 0; + } + if (!GIV(primFailCode)) { + updateDisplayLeftTopRightBottom(left, top, right, bottom); + ioForceDisplayUpdate(); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } +} + + +/* Set the low-water mark for free space. When the free space falls + below this level, the new and new: primitives fail and system attempts + to allocate space (e.g., to create a method context) cause the low-space + semaphore (if one is registered) to be signalled. */ + + /* InterpreterPrimitives>>#primitiveSignalAtBytesLeft */ +static void +primitiveSignalAtBytesLeft(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bytes; + + bytes = longAt(GIV(stackPointer)); + if (((((bytes) & 7) == 1)) + && (((bytes = (bytes >> 3))) >= 0)) { + /* begin lowSpaceThreshold: */ + GIV(lowSpaceThreshold) = bytes; + + /* N.B. The threshold > 0 guard eliminates a warning when + self lowSpaceThreshold: 0 + is inlined into setSignalLowSpaceFlagAndSaveProcess */ + if ((bytes > 0) + && (GIV(totalFreeOldSpace) < bytes)) { + growOldSpaceByAtLeast(bytes - GIV(totalFreeOldSpace)); + } + assert(GIV(totalFreeOldSpace) >= GIV(lowSpaceThreshold)); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } +} + + +/* Computes sine of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveSine */ +static void +primitiveSine(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(sin(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSize */ +static void +primitiveSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt totalLength; + + fmt = 0; + rcvr = longAt(GIV(stackPointer)); + if ((((rcvr & (tagMask())) != 0)) + || (((hdr = long64At((void *)(rcvr))), + (((fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()))) < (arrayFormat())) + || (fmt == (forwardedFormat()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* Integers are not indexable + And if Spur, this is a forwarder. */ + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + primitiveContextSize(); + return; + } + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)(totalLength - fixedFields) << 3) | 1)); +} + + /* InterpreterPrimitives>>#primitiveSizeInBytes */ +static void +primitiveSizeInBytes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong byteSize; + sqInt oop; + + byteSize = /* totalByteSizeOf: */ + ((((longAt(GIV(stackPointer))) & (tagMask())) != 0) + ? 0 + : bytesInBody(longAt(GIV(stackPointer)))); + oop = positive64BitIntegerFor(byteSize); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} + + +/* Answer the byte size of an instance of the receiver. If num args > 0 + then the last argument is a variable size and the size answered is the + size of an instance of the receiver with that many indexable elements. */ + + /* InterpreterPrimitives>>#primitiveSizeInBytesOfInstance */ +static void +primitiveSizeInBytesOfInstance(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt byteSize; + sqInt classFormat; + int err; + sqInt instSpec; + usqInt numSlots; + sqInt numSlotsSqInt; + sqInt oop; + + if (GIV(argumentCount) > 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + + /* Support VMMirror>>byteSizeOfInstanceOf:WithIndexableVariables: */ + err = -1; + if (GIV(argumentCount) >= 1) { + if (!(((((longAt(GIV(stackPointer)))) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* begin byteSizeOfInstanceOf:withIndexableSlots:errInto: */ + classFormat = ((longAt((void *)(((longAt(GIV(stackPointer) + (1 * BytesPerWord))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + switch (instSpec) { + case arrayFormat(): + numSlots = ((longAt(GIV(stackPointer))) >> 3); + break; + case indexablePointersFormat(): + case weakArrayFormat(): + numSlots = (classFormat & ((1U << (fixedFieldsFieldWidth())) - 1)) + (((longAt(GIV(stackPointer))) >> 3)); + break; + case sixtyFourBitIndexableFormat(): + numSlots = ((longAt(GIV(stackPointer))) >> 3); + break; + case firstLongFormat(): + numSlots = ((((longAt(GIV(stackPointer))) >> 3)) + 1) / 2; + break; + case firstShortFormat(): + numSlots = ((((longAt(GIV(stackPointer))) >> 3)) + 3) / 4; + break; + case firstByteFormat(): + case firstCompiledMethodFormat(): + numSlots = ((((longAt(GIV(stackPointer))) >> 3)) + (BytesPerOop - 1)) / BytesPerOop; + break; + default: + byteSize = (err = PrimErrBadReceiver); + goto l1; + } + if (numSlots >= (((((usqInt)1)) << ((BytesPerOop * 8) - 3 /* logBytesPerOop */)))) { + byteSize = (err = ((((longAt(GIV(stackPointer))) >> 3)) < 0 + ? PrimErrBadArgument + : PrimErrLimitExceeded)); + goto l1; + } + byteSize = /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize); + /* end byteSizeOfInstanceOf:withIndexableSlots:errInto: */ +l1: + if (err >= 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = err; + return; + } + oop = positive64BitIntegerFor(byteSize); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return; + } + + /* begin byteSizeOfInstanceOf:errInto: */ + classFormat = ((longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + if (!(/* isFixedSizePointerFormat: */ + (instSpec <= (nonIndexablePointerFormat())) + || (instSpec == (ephemeronFormat())))) { + byteSize = (err = PrimErrBadReceiver); + goto l2; + } + numSlotsSqInt = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + byteSize = /* objectBytesForSlots: */ + (numSlotsSqInt + ? ((((usqInt)(numSlotsSqInt) << (shiftForWord())))) + ((numSlotsSqInt >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize); + /* end byteSizeOfInstanceOf:errInto: */ +l2: + if (err >= 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = err; + return; + } + + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),((byteSize << 3) | 1)); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatAdd */ +static void +primitiveSmallFloatAdd(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr + arg)); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatArctan */ +static void +primitiveSmallFloatArctan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + sqInt oop; + double rcvr; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + aValue = floatObjectOf(atan(rcvr)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatCosine */ +static void +primitiveSmallFloatCosine(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + sqInt oop; + double rcvr; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + aValue = floatObjectOf(cos(rcvr)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatDivide */ +static void +primitiveSmallFloatDivide(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + if (arg == 0.0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr / arg)); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatEqual */ +static void +primitiveSmallFloatEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) == intArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr == arg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + +/* Computes E raised to the receiver power. + Since SmallFloats cannot represent NaNs there's no need to special case. */ + + /* InterpreterPrimitives>>#primitiveSmallFloatExp */ +static void +primitiveSmallFloatExp(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + + aValue = floatObjectOf(exp(smallFloatValueOf(longAt(GIV(stackPointer))))); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + +/* Answer the exponent part of this float. */ + + /* InterpreterPrimitives>>#primitiveSmallFloatExponent */ +static void +primitiveSmallFloatExponent(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqInt exp; + + aValue = (((usqInt)(((/* begin exponentOfSmallFloat: */ + assert(((longAt(GIV(stackPointer))) & (tagMask())) == (smallFloatTag())), +((((usqInt)(longAt(GIV(stackPointer))))) <= 15 + ? 0 + : ((exp = ((((usqInt)(longAt(GIV(stackPointer)))))) >> (((numTagBits()) + (smallFloatMantissaBits())) + 1)), + (exp + (smallFloatExponentOffset())) - 0x3FE)))) - 1) << 3) | 1); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatFractionalPart */ +static void +primitiveSmallFloatFractionalPart(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double frac; + sqInt oop; + double rcvr; + double trunc; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + frac = modf(rcvr, (&trunc)); + aValue = floatObjectOf(frac); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatGreaterOrEqual */ +static void +primitiveSmallFloatGreaterOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) >= intArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr >= arg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatGreaterThan */ +static void +primitiveSmallFloatGreaterThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) > intArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr > arg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatLessOrEqual */ +static void +primitiveSmallFloatLessOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) <= intArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr <= arg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatLessThan */ +static void +primitiveSmallFloatLessThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) < intArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr < arg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + +/* Natural log. */ + + /* InterpreterPrimitives>>#primitiveSmallFloatLogN */ +static void +primitiveSmallFloatLogN(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + sqInt oop; + double rcvr; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + aValue = floatObjectOf(log(rcvr)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatMultiply */ +static void +primitiveSmallFloatMultiply(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr * arg)); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatNotEqual */ +static void +primitiveSmallFloatNotEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) == intArg + ? GIV(falseObj) + : GIV(trueObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr == arg + ? GIV(falseObj) + : GIV(trueObj))); + GIV(stackPointer) = sp; + } + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatSine */ +static void +primitiveSmallFloatSine(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + sqInt oop; + double rcvr; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + aValue = floatObjectOf(sin(rcvr)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatSquareRoot */ +static void +primitiveSmallFloatSquareRoot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + sqInt oop; + double rcvr; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + if (rcvr >= 0.0) { + aValue = floatObjectOf(sqrt(rcvr)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatSubtract */ +static void +primitiveSmallFloatSubtract(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr - arg)); + GIV(stackPointer) = sp; + } +} + + +/* Multiply the receiver by the power of the argument. */ + + /* InterpreterPrimitives>>#primitiveSmallFloatTimesTwoPower */ +static void +primitiveSmallFloatTimesTwoPower(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt rcvr; + sqInt result; + sqInt twiceMaxExponent; + + arg = longAt(GIV(stackPointer)); + if (!((((arg) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* N.B. SmallFloats are finite. NaN and Infinity overflow into boxed floats. + This is doing range checking work that is done in ldexp, but we include + it explicitly to exemplify bit manipulation of SmallFloats. */ + + /* begin isSmallFloatZero: */ + assert(isImmediateFloat(rcvr)); + if ((((usqInt)rcvr)) <= ((1U << (numTagBits())) + (smallFloatTag()))) { + result = rcvr; + } + else { + arg = (arg >> 3); + twiceMaxExponent = 0x1000; + if (arg < (-twiceMaxExponent)) { + /* begin mapSignedSmallFloatToSignedSmallFloatZero: */ + assert(isImmediateFloat(rcvr)); + result = rcvr & ((1U << (numTagBits())) + (tagMask())); + } + else { + /* clip arg to at most int range; ldexp's last arg is of type int */ + if (arg > twiceMaxExponent) { + arg = twiceMaxExponent; + } + result = floatObjectOf(ldexp(smallFloatValueOf(rcvr), ((int) arg))); + } + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),result); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatTruncated */ +static void +primitiveSmallFloatTruncated(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + sqInt oop; + double rcvr; + double trunc; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + modf(rcvr, (&trunc)); + if ((((trunc >= (((double) (MinSmallInteger) ))) && (trunc <= (((double) (MaxSmallInteger) ))))) + && ((((((usqInt)((((sqInt)trunc)))) >> 60) + 1) & 15) <= 1)) { + /* stackTopPut: */ + longAtput(GIV(stackPointer),(((usqInt)(((sqInt)trunc)) << 3) | 1)); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + /* InterpreterPrimitives>>#primitiveSomeInstance */ +static void +primitiveSomeInstance(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt instance; + + class = longAt(GIV(stackPointer)); + + /* For the mirror prims check that the class obj is actually a valid class. */ + instance = initialInstanceOf(class); + if (instance) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),instance); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Return the first object in the heap. */ + + /* InterpreterPrimitives>>#primitiveSomeObject */ +static void +primitiveSomeObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = firstAccessibleObject(); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} + + +/* Return the oop of the SpecialObjectsArray. */ + + /* InterpreterPrimitives>>#primitiveSpecialObjectsOop */ +static void +primitiveSpecialObjectsOop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),GIV(specialObjectsOop)); +} + + +/* Computes square root of float receiver; receiver *must* be a float + instance. + */ + + /* InterpreterPrimitives>>#primitiveSquareRoot */ +static void +primitiveSquareRoot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + if (doubleValue >= 0.0) { + aValue = floatObjectOf(sqrt(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* This primitive is called from Squeak as... + storeSegmentFor: arrayOfRoots into: aWordArray outPointers: + anArray. + */ +/* This primitive will store a binary image segment (in the same format as + the Squeak image file) of the receiver and every object in its proper tree + of subParts (ie, that is not refered to from anywhere else outside the + tree). All pointers from within the tree to objects outside the tree will + be copied into the array of outpointers. In their place in the image + segment will be an oop equal to the offset in the outPointer array (the + first would be 4). but with the high bit set. + */ +/* The primitive expects the array and wordArray to be more than adequately + long. In this case it returns normally, and truncates the two arrays to + exactly the right size. To simplify truncation, both incoming arrays are + required to be whatever the objectMemory considers long objects. If either + array is too small, the primitive will fail, but in no other case. + + During operation of the primitive, it is necessary to convert from both + internal and external oops to their mapped values. To make this fast, the + headers of the original objects in question are replaced by the mapped + values (and this is noted by adding the forbidden XX header type). Tables + are kept of both kinds of oops, as well as of the original headers for + restoration. + To be specific, there are two similar two-part tables, the outpointer + array, and one in the upper fifth of the segmentWordArray. Each grows oops + from the bottom up, and preserved headers from halfway up. + + In case of either success or failure, the headers must be restored. In the + event of primitive failure, the table of outpointers must also be nilled + out (since the garbage in the high half will not have been discarded. + */ + + /* InterpreterPrimitives>>#primitiveStoreImageSegment */ +static void +primitiveStoreImageSegment(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayOfRoots; + sqInt ecode; + sqInt outPointerArray; + sqInt segmentWordArray; + + outPointerArray = longAt(GIV(stackPointer)); + segmentWordArray = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + arrayOfRoots = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + + /* Essential type checks */ + if (!((/* isArray: */ + ((!(arrayOfRoots & (tagMask())))) + && (((byteAt((void *)(arrayOfRoots + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((/* isArray: */ + ((!(outPointerArray & (tagMask())))) + && (((byteAt((void *)(outPointerArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (/* isWords: */ + ((!(segmentWordArray & (tagMask())))) + && (((((byteAt((void *)(segmentWordArray + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(segmentWordArray + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1)))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* Must be indexable pointers + Must be indexable words */ + ecode = storeImageSegmentIntooutPointersroots(segmentWordArray, outPointerArray, arrayOfRoots); + if (ecode == PrimErrNeedCompaction) { + fullGC(); + outPointerArray = longAt(GIV(stackPointer)); + segmentWordArray = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + arrayOfRoots = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + ecode = storeImageSegmentIntooutPointersroots(segmentWordArray, outPointerArray, arrayOfRoots); + } + if (ecode) { + /* primitiveFailFor: */ + GIV(primFailCode) = ecode; + } + else { + /* begin pop: */ + GIV(stackPointer) += 3 * BytesPerWord; + } +} + + /* InterpreterPrimitives>>#primitiveStringAt */ +static void +primitiveStringAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt result; + sqInt sp1; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + + /* begin commonAt: */ + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + index = longAt(GIV(stackPointer)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l5; + } + + /* No need to test for large positive integers here. No object has 1g elements */ + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 1) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l5; + } + index = (index >> 3); + + /* begin stObject:at: */ + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + sp1 = stackPointerIndexForFrame(frameOfMarriedContext(rcvr)); + assert((ReceiverIndex + ((sp1 >> 3))) < (lengthOf(rcvr))); + stSize = sp1; + goto l3; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l3; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(rcvr))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l3: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))))) { + result = temporaryin(index - 1, frameOfMarriedContext(rcvr)); + goto l4; + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(rcvr)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + result = (((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + (index - 1)))) << 3) | 1); + goto l4; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + result = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << (shiftForWord())))))); + goto l4; + } + if (fmt >= (firstByteFormat())) { + result = (((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + ((index + fixedFields) - 1)))) << 3) | 1); + goto l4; + } + if (fmt >= (firstShortFormat())) { + result = (((usqInt)(((unsigned short) (shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))))))) << 3) | 1); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + result = positive64BitIntegerFor(long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))))); + goto l4; + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + result = (((usqInt)((((usqInt)(long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + goto l4; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + result = 0; + /* end stObject:at: */ +l4: + if (!GIV(primFailCode)) { + if (!(((result >= 0) && (result <= (0x3FFFFFFF))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l5; + } + result = ((((usqInt)(((result >> 3))) << (numTagBits())))) + (characterTag()); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); + } + /* end commonAt: */ +l5:; +} + + /* InterpreterPrimitives>>#primitiveStringAtPut */ +static void +primitiveStringAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fieldIndex; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt signedValueToStore; + sqInt signedValueToStoreSqInt; + sqInt sp; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + usqLong unsigned64BitValueToStore; + unsigned int unsignedValueToStore; + sqInt value; + sqInt valueSqInt; + + /* begin commonAtPut: */ + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + value = longAt(GIV(stackPointer)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l11; + } + + /* No need to test for large positive integers here. No object has 1g elements */ + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 2) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l11; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l11; + } + index = (index >> 3); + valueSqInt = asciiOfCharacter(value); + + /* begin stObject:at:put: */ + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l1: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(rcvr)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(rcvr))); + stSize = sp; + goto l2; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l2; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(rcvr))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l2: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))))) { + temporaryinput(index - 1, frameOfMarriedContext(rcvr), valueSqInt); + goto l5; + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(rcvr)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + if (((((valueSqInt) & 7) == 1)) + && ((((usqInt)((signedValueToStore = (valueSqInt >> 3))))) <= 0xFF)) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + (index - 1)),signedValueToStore); + goto l5; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l5; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l3; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l3; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l3: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:storing:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + fieldIndex = (index + fixedFields) - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, rcvr, valueSqInt)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valueSqInt & (tagMask())))) + && (oopisLessThan(valueSqInt, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valueSqInt); + goto l4; + } + if (fmt >= (firstByteFormat())) { + if (!((((valueSqInt) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStoreSqInt = (valueSqInt >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + ((index + fixedFields) - 1)),signedValueToStoreSqInt); + goto l4; + } + if (fmt >= (firstShortFormat())) { + if (!((((valueSqInt) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStoreSqInt = (valueSqInt >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))),signedValueToStoreSqInt); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(valueSqInt); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l4; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(valueSqInt); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l4: + goto l5; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + /* end stObject:at:put: */ +l5: + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + } + /* end commonAtPut: */ +l11:; +} + + +/* primReplaceFrom: start to: stop with: replacement startingAt: + repStart + */ + + /* InterpreterPrimitives>>#primitiveStringReplace */ +static void +primitiveStringReplace(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt array; + sqInt arrayFmt; + sqInt arrayInstSize; + usqInt arrayLength; + sqInt class; + sqInt i; + sqInt mustRemember; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt repl; + sqInt replFmt; + sqInt replInstSize; + usqInt replLength; + sqInt replStart; + sqInt srcDelta; + sqInt start; + sqInt stop; + sqInt valuePointer; + + /* begin primitiveSpurStringReplace */ + array = longAt(GIV(stackPointer) + (4 * BytesPerWord)); + start = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + stop = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + repl = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + replStart = longAt(GIV(stackPointer)); + if (((!(start & (smallIntegerTag())))) + || (((!(stop & (smallIntegerTag())))) + || (((!(replStart & (smallIntegerTag())))) + || (((repl & (tagMask())) != 0))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l7; + } + + /* can happen in LgInt copy */ + start = (start >> 3); + stop = (stop >> 3); + replStart = (replStart >> 3); + if ((stop >= start) + && ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(array + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + )) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l7; + } + arrayFmt = (byteAt((void *)(array + (formatFieldByteOffset())))) & (formatMask()); + replFmt = (byteAt((void *)(repl + (formatFieldByteOffset())))) & (formatMask()); + + /* N.B. In the below start - 1 to: stop - 1 do:, Slang is intelligent enough to use < instead of <= so avoiding the stop - 1. */ + if (arrayFmt <= 5 /* lastPointerFormat */) { + /* Array formats must be the same; but for copying, weak arrays are equivalent to arrays. */ + if (arrayFmt == (weakArrayFormat())) { + arrayFmt = arrayFormat(); + } + if (replFmt == (weakArrayFormat())) { + replFmt = arrayFormat(); + } + if (arrayFmt != replFmt) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l7; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(array)) > (isForwardedObjectClassIndexPun())); + arrayLength = (((numSlots = byteAt((void *)(array + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + + /* begin fixedFieldsOf:format:length: */ + if ((arrayFmt >= (sixtyFourBitIndexableFormat())) + || (arrayFmt == (arrayFormat()))) { + arrayInstSize = 0; + goto l5; + } + if (arrayFmt < (arrayFormat())) { + arrayInstSize = arrayLength; + goto l5; + } + class = fetchClassOfNonImm(array); + arrayInstSize = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l5: + + /* begin numSlotsOf: */ + assert((classIndexOf(repl)) > (isForwardedObjectClassIndexPun())); + replLength = (((numSlots = byteAt((void *)(repl + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(repl - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + + /* begin fixedFieldsOf:format:length: */ + if ((replFmt >= (sixtyFourBitIndexableFormat())) + || (replFmt == (arrayFormat()))) { + replInstSize = 0; + goto l6; + } + if (replFmt < (arrayFormat())) { + replInstSize = replLength; + goto l6; + } + class = fetchClassOfNonImm(repl); + replInstSize = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l6: + if (!((start >= 1) + && (((start - 1) <= stop) + && (((stop + arrayInstSize) <= arrayLength) + && ((replStart >= 1) + && ((((stop - start) + replStart) + replInstSize) <= replLength)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l7; + } + start += arrayInstSize; + stop += arrayInstSize; + srcDelta = (replStart + replInstSize) - start; + + /* begin isOldObject: */ + assert(isNonImmediate(array)); + if (oopisGreaterThanOrEqualTo(array, GIV(oldSpaceStart))) { + mustRemember = 0; + for (i = (start - 1); i < stop; i += 1) { + oop = longAt((void *)((repl + BaseHeaderSize) + ((((usqInt)((srcDelta + i)) << (shiftForWord())))))); + if (/* isYoung: */ + ((!(oop & (tagMask())))) + && (oopisLessThan(oop, GIV(oldSpaceStart)))) { + mustRemember = 1; + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(i, array, oop)); + longAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),oop); + } + if (mustRemember) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(array + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(array); + } + } + } + else { + for (i = (start - 1); i < stop; i += 1) { + valuePointer = longAt((void *)((repl + BaseHeaderSize) + ((((usqInt)((srcDelta + i)) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(i, array, valuePointer)); + longAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + } + + /* We might consider comparing stop - start to some value here and using forceInterruptCheck */ + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + goto l7; + } + + /* Non-pointer array formats must match */ + + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(array + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (arrayFmt <= (ephemeronFormat())) { + arrayLength = ((sqInt) numSlots); + goto l3; + } + if (arrayFmt >= (firstByteFormat())) { + arrayLength = ((numSlots << (shiftForWord()))) - (arrayFmt & 7); + goto l3; + } + + /* bytes, including CompiledMethod */ + if (arrayFmt >= (firstShortFormat())) { + arrayLength = ((numSlots << ((shiftForWord()) - 1))) - (arrayFmt & 3); + goto l3; + } + if (arrayFmt >= (firstLongFormat())) { + arrayLength = ((numSlots << ((shiftForWord()) - 2))) - (arrayFmt & 1); + goto l3; + } + if (arrayFmt == (sixtyFourBitIndexableFormat())) { + arrayLength = ((sqInt) numSlots); + goto l3; + } + + /* fmt = self forwardedFormat */ + arrayLength = 0; + /* end lengthOf:format: */ +l3: + + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(repl + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(repl - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (replFmt <= (ephemeronFormat())) { + replLength = ((sqInt) numSlots); + goto l4; + } + if (replFmt >= (firstByteFormat())) { + replLength = ((numSlots << (shiftForWord()))) - (replFmt & 7); + goto l4; + } + + /* bytes, including CompiledMethod */ + if (replFmt >= (firstShortFormat())) { + replLength = ((numSlots << ((shiftForWord()) - 1))) - (replFmt & 3); + goto l4; + } + if (replFmt >= (firstLongFormat())) { + replLength = ((numSlots << ((shiftForWord()) - 2))) - (replFmt & 1); + goto l4; + } + if (replFmt == (sixtyFourBitIndexableFormat())) { + replLength = ((sqInt) numSlots); + goto l4; + } + + /* fmt = self forwardedFormat */ + replLength = 0; + /* end lengthOf:format: */ +l4: + + /* begin classFormatFromInstFormat: */ + if (arrayFmt >= (firstByteFormat())) { + arrayFmt = arrayFmt & -8; + goto l1; + } + + /* this is likely the common case */ + if (arrayFmt <= (sixtyFourBitIndexableFormat())) { + goto l1; + } + if (arrayFmt < (firstShortFormat())) { + arrayFmt = arrayFmt & -2; + goto l1; + } + arrayFmt = arrayFmt & -4; + /* end classFormatFromInstFormat: */ +l1: + + /* begin classFormatFromInstFormat: */ + if (replFmt >= (firstByteFormat())) { + replFmt = replFmt & -8; + goto l2; + } + + /* this is likely the common case */ + if (replFmt <= (sixtyFourBitIndexableFormat())) { + goto l2; + } + if (replFmt < (firstShortFormat())) { + replFmt = replFmt & -2; + goto l2; + } + replFmt = replFmt & -4; + /* end classFormatFromInstFormat: */ +l2: + if (!((arrayFmt == replFmt) + && ((arrayFmt >= (sixtyFourBitIndexableFormat())) + && (arrayFmt < (firstCompiledMethodFormat()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l7; + } + if (!((start >= 1) + && (((start - 1) <= stop) + && ((stop <= arrayLength) + && ((replStart >= 1) + && (((stop - start) + replStart) <= replLength)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l7; + } + srcDelta = replStart - start; + if (arrayFmt >= (firstShortFormat())) { + if (arrayFmt >= (firstByteFormat())) { + for (i = (start - 1); i < stop; i += 1) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((array + BaseHeaderSize) + i),byteAt((void *)((repl + BaseHeaderSize) + (srcDelta + i)))); + } + } + else { + for (i = (start - 1); i < stop; i += 1) { + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << 1)))),shortAt((void *)((repl + BaseHeaderSize) + ((((usqInt)((srcDelta + i)) << 1)))))); + } + } + } + else { + if (arrayFmt >= (firstLongFormat())) { + for (i = (start - 1); i < stop; i += 1) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << 2)))),long32At((void *)((repl + BaseHeaderSize) + ((((usqInt)((srcDelta + i)) << 2)))))); + } + } + else { + for (i = (start - 1); i < stop; i += 1) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << 3)))),long64At((void *)((repl + BaseHeaderSize) + ((((usqInt)((srcDelta + i)) << 3)))))); + } + } + } + + /* 8 & 16-bit word type objects + 32 & 64-bit word type objects + We might consider comparing stop - start to some value here and using forceInterruptCheck */ + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + /* end primitiveSpurStringReplace */ +l7:; +} + + /* InterpreterPrimitives>>#primitiveSubtract */ +static void +primitiveSubtract(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerResult; + + integerResult = (stackIntegerValue(1)) - (stackIntegerValue(0)); + + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)(integerResult)) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerResult << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveSubtractLargeIntegers */ +EXPORT(void) +primitiveSubtractLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + sqInt resultIsNegative; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (GIV(primFailCode)) { + return; + } + if (aIsNegative != bIsNegative) { + /* Protect against overflow */ + if (a > (0xFFFFFFFFFFFFFFFFULL - b)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + result = a + b; + resultIsNegative = aIsNegative; + } + else { + if (a >= b) { + result = a - b; + resultIsNegative = aIsNegative; + } + else { + result = b - a; + resultIsNegative = !aIsNegative; + } + } + oopResult = magnitude64BitIntegerForneg(result, resultIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Attempt to test-and-set the ownership of the critical section. If not + owned, set the owner to the current process and answer false. If owned by + the current process answer true. If owned by some other process answer + nil. For simulation if there is an argument it is taken to be the + effective activeProcess + (see Process>>effectiveProcess). */ + + /* InterpreterPrimitives>>#primitiveTestAndSetOwnershipOfCriticalSection */ +static void +primitiveTestAndSetOwnershipOfCriticalSection(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + sqInt criticalSection; + sqInt objOop; + sqInt owningProcess; + sqInt owningProcessIndex; + + if (GIV(argumentCount) > 0) { + /* rcvr */ + criticalSection = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + activeProc = longAt(GIV(stackPointer)); + if (/* isOopForwarded: */ + ((!(activeProc & (tagMask())))) + && ((!((longAt((void *)(activeProc))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + } + else { + /* rcvr */ + criticalSection = longAt(GIV(stackPointer)); + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + } + + /* CriticalSections are laid out like Semaphores */ + owningProcessIndex = ExcessSignalsIndex; + owningProcess = longAt((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord())))))); + if (owningProcess == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(owningProcessIndex, criticalSection, activeProc)); + assert(isNonImmediate(criticalSection)); + if (oopisGreaterThanOrEqualTo(criticalSection, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeProc & (tagMask())))) + && (oopisLessThan(activeProc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(criticalSection + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(criticalSection); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord()))))),activeProc); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(falseObj)); + return; + } + if (owningProcess == activeProc) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(trueObj)); + return; + } + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); +} + + +/* Return true if the host OS does support the given display depth. */ + + /* InterpreterPrimitives>>#primitiveTestDisplayDepth */ +static void +primitiveTestDisplayDepth(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bitsPerPixel; + sqInt integerPointer; + sqInt okay; + char *sp; + + okay = 0; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + bitsPerPixel = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + bitsPerPixel = 0; + } + if (!GIV(primFailCode)) { + okay = ioHasDisplayDepth(bitsPerPixel); + } + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (okay + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + +/* Given an object with indexable pointer fields, reduce the size of the + indexable fields + to the requested size. Answer the number of bytes freed, or zero if the + object cannot + be shortened. + */ + + /* InterpreterPrimitives>>#primitiveTestShortenIndexableSize */ +#if TestingPrimitives +EXPORT(sqInt) +primitiveTestShortenIndexableSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt array; + sqInt delta; + sqInt indexableSize; + sqInt integer; + sqInt newSizeOop; + usqInt numSlots; + + newSizeOop = longAt(GIV(stackPointer)); + array = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(((((newSizeOop) & 7) == 1)) + && (((((newSizeOop >> 3)) >= 0) && (((newSizeOop >> 3)) <= ((stSizeOf(array)) - 1)))))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + if (!(/* canShorten: */ + ((!(array & (tagMask())))) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(array)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(array + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > 0) + && ((((byteAt((void *)(array + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())) + || (((((byteAt((void *)(array + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(array + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1)))))))) { + return (GIV(primFailCode) = PrimErrBadReceiver); + } + indexableSize = (newSizeOop >> 3); + + /* begin shorten:toIndexableSize: */ + assert((indexableSize >= 0) + && (indexableSize < (lengthOf(array)))); + delta = doShortentoIndexableSize(array, indexableSize); + assert((lengthOf(followMaybeForwarded(array))) == indexableSize); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + integer = delta; + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); + return 0; +} +#endif /* TestingPrimitives */ + + +/* Multiply the receiver by the power of the argument. + Receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveTimesTwoPower */ +static void +primitiveTimesTwoPower(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aFloatOop; + sqInt arg; + usqLong bits; + double rcvr; + double result; + char *sp; + sqInt twiceMaxExponent; + + arg = longAt(GIV(stackPointer)); + if (!((((arg) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + arg = (arg >> 3); + + /* clip arg to at most int range; ldexp's last arg is of type int */ + twiceMaxExponent = 0x1000; + if (arg < (-twiceMaxExponent)) { + arg = -twiceMaxExponent; + } + else { + if (arg > twiceMaxExponent) { + arg = twiceMaxExponent; + } + } + aFloatOop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(aFloatOop)); + if (aFloatOop & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(aFloatOop)); + bits = ((((usqInt)aFloatOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + } + else { + fetchFloatAtinto(aFloatOop + BaseHeaderSize, rcvr); + } + result = ldexp(rcvr, ((int) arg)); + + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(result)); + GIV(stackPointer) = sp; +} + + +/* Integral part of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveTruncated */ +static void +primitiveTruncated(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + double doubleValue; + sqInt rcvr; + double trunc; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + modf(doubleValue, (&trunc)); + if (((trunc >= (((double) (MinSmallInteger) ))) && (trunc <= (((double) (MaxSmallInteger) ))))) { + /* stackTopPut: */ + longAtput(GIV(stackPointer),(((usqInt)(((sqInt)trunc)) << 3) | 1)); + return; + } + + /* make Nicolas Cellier's otherwise recursive BoxedFloat64>>truncated nice 2/7/2025 07:34 work on 32-bits. */ + + /* a.k.a. trunc abs <= Float maxExactInteger asFloat */ + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } +} + + +/* Allocate a new indexable instance. Fail if the allocation would leave less + than lowSpaceThreshold bytes free. May cause a GC. + */ + + /* InterpreterPrimitives>>#primitiveUninitializedNewWithArg */ +static void +primitiveUninitializedNewWithArg(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt classObj; + sqInt err; + int hash; + sqInt instSpec; + sqInt instSpecSqInt; + sqInt newObj; + usqInt newObjUsqInt; + usqInt numBytes; + usqInt numSlots; + sqInt obj; + sqInt reasonCode; + usqIntptr_t size; + sqInt value; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + size = null; + goto l1; + } + size = value; + goto l1; + } + + /* don't inline the rare case */ + size = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* positiveMachineIntegerValueOf: succeeds only for non-negative integers. */ + classObj = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin instantiateUninitializedClass:indexableSize: */ + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpecSqInt = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + assert(isPureBitsFormat(instSpecSqInt)); + switch (instSpecSqInt) { + case sixtyFourBitIndexableFormat(): + numSlots = size; + break; + case firstLongFormat(): + if ((classIndex == ClassFloatCompactIndex) + && (size != 2)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + obj = null; + goto l3; + } + numSlots = (size + 1) / 2; + instSpecSqInt += size & 1; + break; + case firstShortFormat(): + numSlots = (size + 3) / 4; + instSpecSqInt += (4 - size) & 3; + break; + case firstByteFormat(): + numSlots = (size + 7) / 8; + instSpecSqInt += (8 - size) & 7; + break; + default: + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + obj = null; + goto l3; + } + + /* not bits indexable */ + if (!classIndex) { + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + obj = null; + goto l3; + } + } + if (numSlots > ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (numSlots > (0x10000000000LL)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + obj = null; + goto l3; + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpecSqInt, classIndex); + } + else { + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if (((numSlots) >> 56) > 0) { + newObj = null; + goto l2; + } + newObjUsqInt = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObjUsqInt = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, instSpecSqInt, classIndex); + goto l2; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObjUsqInt),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(instSpecSqInt) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObjUsqInt),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(instSpecSqInt) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObjUsqInt % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newObj = newObjUsqInt; + /* end allocateSlots:format:classIndex: */ +l2:; + } + obj = newObj; + /* end instantiateUninitializedClass:indexableSize: */ +l3: + if (obj) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + instSpec = (((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer) + (1 * BytesPerWord))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + reasonCode = (/* isPureBitsFormat: */ + (instSpec >= (sixtyFourBitIndexableFormat())) + && (instSpec < (firstCompiledMethodFormat())) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + +/* Allocate a new pinned indexable instance with junk contents, saving the + time for initializing with 0. Fail if the receiver us not a pure bits + class. Fail + if the allocation would leave less than lowSpaceThreshold bytes free. */ + + /* InterpreterPrimitives>>#primitiveUninitializedPinnedNewWithArg */ +static void +primitiveUninitializedPinnedNewWithArg(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt classObj; + sqInt err; + int hash; + sqInt instSpec; + sqInt instSpecSqInt; + sqInt newObj; + usqInt numSlots; + sqInt obj; + sqInt reasonCode; + usqIntptr_t size; + sqInt value; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + size = null; + goto l1; + } + size = value; + goto l1; + } + + /* don't inline the rare case */ + size = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* positiveMachineIntegerValueOf: succeeds only for non-negative integers. */ + classObj = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin inOldSpaceInstantiateUninitializedPinnedClass:indexableSize: */ + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpecSqInt = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + switch (instSpecSqInt) { + case sixtyFourBitIndexableFormat(): + numSlots = size; + break; + case firstLongFormat(): + if ((classIndex == ClassFloatCompactIndex) + && (size != 2)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + obj = null; + goto l2; + } + numSlots = (size + 1) / 2; + instSpecSqInt += size & 1; + break; + case firstShortFormat(): + numSlots = (size + 3) / 4; + instSpecSqInt += (4 - size) & 3; + break; + case firstByteFormat(): + numSlots = (size + 7) / 8; + instSpecSqInt += (8 - size) & 7; + break; + default: + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + obj = null; + goto l2; + } + + /* non-indexable and/or pointers */ + if (!classIndex) { + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + obj = null; + goto l2; + } + } + if (numSlots > (0x10000000000LL)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + obj = null; + goto l2; + } + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpecSqInt, classIndex); + obj = newObj; + /* end inOldSpaceInstantiateUninitializedPinnedClass:indexableSize: */ +l2: + if (obj) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + instSpec = (((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer) + (1 * BytesPerWord))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + reasonCode = (/* isPureBitsFormat: */ + (instSpec >= (sixtyFourBitIndexableFormat())) + && (instSpec < (firstCompiledMethodFormat())) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + +/* Update the VMs notion of the current timezone. The VM sets its notion + of the timezone once at start-up. If one wants the VM to keep its notion + up-to-date arrange to invoke this primitive periodically. */ + + /* InterpreterPrimitives>>#primitiveUpdateTimezone */ +static void +primitiveUpdateTimezone(void) +{ + ioUpdateVMTimezone(); +} + + +/* Return the value of the microsecond clock in UTC as an integer. + This is the number of microseconds since the Smalltalk epoch, 1901/1/1 + 12:00am. The microsecond clock is at least 60 bits wide which means it'll + get to around August + 38435 before it wraps around. Be sure to put it on your calendar. This + primitive accesses the time as answered by the OS. */ + + /* InterpreterPrimitives>>#primitiveUTCMicrosecondClock */ +static void +primitiveUTCMicrosecondClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = positive64BitIntegerFor(ioUTCMicrosecondsNow()); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Answer an array with UTC microseconds since the Smalltalk epoch and the + current seconds offset from GMT in the local time zone. Any pointer object + with two or more slots) may be supplied as a parameter. */ + + /* InterpreterPrimitives>>#primitiveUtcAndTimezoneOffset */ +static void +primitiveUtcAndTimezoneOffset(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt resultArray; + sqInt valuePointer; + + if (GIV(argumentCount) > 0) { + if (GIV(argumentCount) > 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + resultArray = longAt(GIV(stackPointer)); + if (!((/* isPointers: */ + ((!(resultArray & (tagMask())))) + && (((byteAt((void *)(resultArray + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((lengthOf(resultArray)) >= 2))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + else { + resultArray = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 2); + } + + /* N.B. No pushRemappableOop:/popRemappableOop in Cog because positive64BitIntegerFor: et al use + eeInstantiate... allocators which are guaranteed not to do a GC. */ + valuePointer = (((usqInt)(ioLocalSecondsOffset()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultArray)) + && (!(isForwarded(resultArray)))); + assert(validStorePointerUncheckedArgs(1, resultArray, valuePointer)); + longAtput((void *)((resultArray + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(ioUTCMicroseconds()); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(0, resultArray, valuePointer)); + assert(isNonImmediate(resultArray)); + if (oopisGreaterThanOrEqualTo(resultArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(resultArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(resultArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((resultArray + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),resultArray); +} + + +/* Answer an array with UTC microseconds since the Posix epoch and the + current seconds offset from GMT in the local time zone. An empty two + element array (or any object with two or more slots) may be supplied + as a parameter. + This is a named (not numbered) primitive in the null module (ie the VM) */ + + /* InterpreterPrimitives>>#primitiveUtcWithOffset */ +EXPORT(sqInt) +primitiveUtcWithOffset(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + static usqLong epochDelta = 2177452800000000ULL; + sqInt resultArray; + sqInt valuePointer; + + if (GIV(argumentCount) > 0) { + if (GIV(argumentCount) > 1) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + resultArray = longAt(GIV(stackPointer)); + if (!((/* isPointers: */ + ((!(resultArray & (tagMask())))) + && (((byteAt((void *)(resultArray + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((lengthOf(resultArray)) >= 2))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + } + else { + resultArray = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 2); + } + + /* N.B. No pushRemappableOop:/popRemappableOop in Cog because positive64BitIntegerFor: et al use + eeInstantiate... allocators which are guaranteed not to do a GC. */ + valuePointer = (((usqInt)(ioLocalSecondsOffset()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultArray)) + && (!(isForwarded(resultArray)))); + assert(validStorePointerUncheckedArgs(1, resultArray, valuePointer)); + longAtput((void *)((resultArray + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor((ioUTCMicrosecondsNow()) - epochDelta); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(0, resultArray, valuePointer)); + assert(isNonImmediate(resultArray)); + if (oopisGreaterThanOrEqualTo(resultArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(resultArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(resultArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((resultArray + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),resultArray); + return 0; +} + + +/* Return a string containing the path name of VM's directory. */ + + /* InterpreterPrimitives>>#primitiveVMPath */ +static void +primitiveVMPath(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt s; + sqInt sz; + + sz = vmPathSize(); + s = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord())))))), sz); + vmPathGetLength(s + BaseHeaderSize, sz); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),s); +} + + +/* Primitive. + 0 args: Answer whether the VM Profiler is running or not. + 1 arg: Copy the sample data into the supplied argument, which must be a + Bitmap of suitable size. Answer the number of samples copied into the + buffer. */ + + /* InterpreterPrimitives>>#primitiveVMProfileSamplesInto */ +static void +primitiveVMProfileSamplesInto(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + long bufferSize; + sqInt numSamples; + sqInt running; + sqInt sampleBuffer; + char *sp; + + bufferSize = 0; + running = 0; + ioNewProfileStatus((&running), (&bufferSize)); + if (!GIV(argumentCount)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer)),/* booleanObjectOf: */ + (running + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return; + } + if (!(GIV(argumentCount) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + sampleBuffer = longAt(GIV(stackPointer)); + if (!(((!(sampleBuffer & (tagMask())))) + && ((isPureBitsNonImm(sampleBuffer)) + && ((numBytesOf(sampleBuffer)) >= (bufferSize * BytesPerWord))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + numSamples = ioNewProfileSamplesInto(pointerForOop(sampleBuffer + BaseHeaderSize)); + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)numSamples << 3) | 1)); +} + + +/* Signal the given semaphore from within the interpreter. Used to serialize + callbacks. + */ + + /* InterpreterPrimitives>>#signalNoResume: */ +int +signalNoResume(sqInt aSemaphore) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int empty; + + /* begin isEmptyList: */ + assert(!(isForwarded(aSemaphore))); + empty = (longAt((void *)((aSemaphore + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj); + if (!empty) { + putToSleepyieldingIf(removeFirstLinkOfList(aSemaphore), 1); + } + return empty; +} + + +/* Answer a signed value of an integer up to the size of a machine word. + The object is not an immediate and is hoped to be a LargeInteger of size + <= word size. + */ + + /* InterpreterPrimitives>>#signedMachineIntegerValueOfObj: */ +static NoDbgRegParms sqIntptr_t +signedMachineIntegerValueOfObj(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bs; + sqInt ccIndex; + sqInt fmt; + sqInt isClassOfNonImmequalTocompactClassIndexRV; + usqIntptr_t limit; + usqIntptr_t magnitude; + sqInt negative; + usqInt numBytes; + usqInt numSlots; + usqIntptr_t value; + + assert(!(((((oop) & 7) == 1)))); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + isClassOfNonImmequalTocompactClassIndexRV = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (isClassOfNonImmequalTocompactClassIndexRV) { + negative = 0; + } + else { + negative = 1; + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + isClassOfNonImmequalTocompactClassIndexRV = ClassLargeNegativeIntegerCompactIndex == ccIndex; + if (!isClassOfNonImmequalTocompactClassIndexRV) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + } + + /* begin numBytesOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + bs = numBytes - (fmt & 7); + goto l1; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + bs = numBytes; + goto l1; + } + if (fmt >= (firstShortFormat())) { + bs = numBytes - (((fmt & 3) << 1)); + goto l1; + } + + /* fmt >= self firstLongFormat */ + bs = numBytes - (((fmt & 1) << 2)); + /* end numBytesOf: */ +l1: + if (bs > (sizeof(usqIntptr_t))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + if (((sizeof(sqIntptr_t)) == 8) + && (bs > 4)) { + magnitude = SQ_SWAP_8_BYTES_IF_BIGENDIAN((long64At((void *)((oop + BaseHeaderSize))))); + } + else { + magnitude = ((unsigned int) (SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((oop + BaseHeaderSize))))))); + } + limit = ((((usqIntptr_t)1)) << (((sizeof(sqIntptr_t)) * 8) - 1)); + if ((negative + ? magnitude > limit + : magnitude >= limit)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + if (negative) { + value = 0 - magnitude; + } + else { + value = magnitude; + } + return value; +} + + +/* Answer a signed value of an integer up to the size of a machine word. + The object may be either a positive SmallInteger or a LargeInteger of size + <= word size. + */ +/* inline the common case... */ + + /* InterpreterPrimitives>>#signedMachineIntegerValueOf: */ +sqIntptr_t +signedMachineIntegerValueOf(sqInt oop) +{ + if ((((oop) & 7) == 1)) { + return (oop >> 3); + } + return signedMachineIntegerValueOfObj(oop); +} + + +/* Answer the start of the Alien's data or fail if oop is not an Alien. */ + + /* InterpreterPrimitives>>#sizeOfAlienData: */ +usqInt +sizeOfAlienData(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt oopClass; + sqInt tagBits; + + /* begin is:KindOfClass: */ + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while ((oopClass != GIV(nilObj)) + && ((/* isPointers: */ + ((!(oopClass & (tagMask())))) + && (((byteAt((void *)(oopClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOfAny(oopClass)) > InstanceSpecificationIndex))) { + if (oopClass == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord())))))))) { + goto l1; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return 0; +l1: + return SQABS(longAt((void *)(oop + BaseHeaderSize))); +} + + +/* Answer the start of the Alien's data or fail if oop is not an Alien. */ + + /* InterpreterPrimitives>>#startOfAlienData: */ +void * +startOfAlienData(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt oopClass; + sqInt tagBits; + + /* begin is:KindOfClass: */ + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while ((oopClass != GIV(nilObj)) + && ((/* isPointers: */ + ((!(oopClass & (tagMask())))) + && (((byteAt((void *)(oopClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOfAny(oopClass)) > InstanceSpecificationIndex))) { + if (oopClass == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord())))))))) { + goto l1; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return 0; +l1: + return ((void *) (((longAt((void *)(oop + BaseHeaderSize))) > 0 + ? (oop + BaseHeaderSize) + BytesPerOop + : longAt((void *)((oop + BaseHeaderSize) + BytesPerOop))))); +} + + +/* Set the state of the primitive failure code/success flag, iff + successBoolean is false. If primFailCode is non-zero a primitive has + failed. If primFailCode + is greater than one then its value indicates the reason for failure. */ +/* Use returnTypeC: #sqInt because that's the way it is defined in sq.h. + Use no explicit return so that Slang doesn't fail an inlining type-check + when a primitive with return type void uses ^self success: false to exit. */ + + /* InterpreterPrimitives>>#success: */ +sqInt +success(sqInt successBoolean) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!successBoolean) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + return 0; +} + + /* LiveTypingCoInterpreter>>#ceCollectArgumentsTypeForFramefulActivation */ +void +ceCollectArgumentsTypeForFramefulActivation(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt arg; + sqInt argIndex; + sqInt fmt; + CogMethod *frameMethod; + sqInt index; + char *lastArgPointer; + sqInt maxNumArgs; + sqInt methodField; + sqInt methodObject; + sqInt numArgs; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt types; + sqInt typesArraySize; + sqInt wordSize; + + /* begin mframeHomeMethod: */ + methodField = longAt(GIV(framePointer) + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + frameMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + goto l1; + } + frameMethod = ((CogMethod *) (methodField & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + lastArgPointer = (GIV(framePointer) + FoxCallerSavedIP) + BytesPerWord; + methodObject = (frameMethod->methodObject); + numArgs = (frameMethod->cmNumArgs); + + /* begin collectArgumentsTypeFor:fromStackAt:count: */ + /* begin tempVarTypesArraysOf: */ + /* begin additionalMethodStateOf:at: */ + /* begin additionalMethodStateOf: */ + selectorOrProperties = penultimateLiteralOf(methodObject); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l4; + } + wordSize = BytesPerWord; + maxNumArgs = (((lengthOf(tempVarTypesArrays)) < numArgs) ? (lengthOf(tempVarTypesArrays)) : numArgs); + for (argIndex = 0; argIndex < maxNumArgs; argIndex += 1) { + /* begin followObjField:ofObject: */ + types = longAt((void *)((tempVarTypesArrays + BaseHeaderSize) + ((((usqInt)(argIndex) << (shiftForWord())))))); + assert(isNonImmediate(types)); + if ((!((longAt((void *)(types))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + types = fixFollowedFieldofObjectwithInitialValue(argIndex, tempVarTypesArrays, types); + } + arg = longAt((void *)(lastArgPointer + (argIndex * wordSize))); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (types) + && (isInstanceOfClassArray(types)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = arg & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(arg)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(types + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(types + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(types - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l3; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l3; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l3; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l3; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l3; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l3: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, types, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, types, objectClass)); + assert(isNonImmediate(types)); + if (oopisGreaterThanOrEqualTo(types, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(types + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(types); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2:; + } + /* end collectArgumentsTypeFor:fromStackAt:count: */ +l4:; +} + + /* LiveTypingCoInterpreter>>#ceCollectArgumentsTypeForFramlessSpilledActivationOf: */ +void +ceCollectArgumentsTypeForFramlessSpilledActivationOf(CogMethod *cogMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt arg; + sqInt argIndex; + sqInt fmt; + sqInt index; + char *lastArgPointer; + sqInt maxNumArgs; + sqInt methodObject; + sqInt numArgs; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt types; + sqInt typesArraySize; + sqInt wordSize; + + lastArgPointer = GIV(stackPointer) + (2 * BytesPerWord); + methodObject = (cogMethod->methodObject); + numArgs = (cogMethod->cmNumArgs); + + /* begin collectArgumentsTypeFor:fromStackAt:count: */ + /* begin tempVarTypesArraysOf: */ + /* begin additionalMethodStateOf:at: */ + /* begin additionalMethodStateOf: */ + selectorOrProperties = penultimateLiteralOf(methodObject); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l3; + } + wordSize = BytesPerWord; + maxNumArgs = (((lengthOf(tempVarTypesArrays)) < numArgs) ? (lengthOf(tempVarTypesArrays)) : numArgs); + for (argIndex = 0; argIndex < maxNumArgs; argIndex += 1) { + /* begin followObjField:ofObject: */ + types = longAt((void *)((tempVarTypesArrays + BaseHeaderSize) + ((((usqInt)(argIndex) << (shiftForWord())))))); + assert(isNonImmediate(types)); + if ((!((longAt((void *)(types))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + types = fixFollowedFieldofObjectwithInitialValue(argIndex, tempVarTypesArrays, types); + } + arg = longAt((void *)(lastArgPointer + (argIndex * wordSize))); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (types) + && (isInstanceOfClassArray(types)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = arg & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(arg)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(types + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(types + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(types - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, types, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, types, objectClass)); + assert(isNonImmediate(types)); + if (oopisGreaterThanOrEqualTo(types, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(types + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(types); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((types + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2:; + } + /* end collectArgumentsTypeFor:fromStackAt:count: */ +l3:; +} + + /* LiveTypingCoInterpreter>>#ceCollectArgumentsTypeForFramlessUnspilledActivationWithArg:arg:of: */ +void +ceCollectArgumentsTypeForFramlessUnspilledActivationWithArgargof(sqInt arg0, sqInt arg1, CogMethod *cogMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt fmt; + sqInt index; + sqInt methodObject; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + methodObject = (cogMethod->methodObject); + + /* begin collectArgumentsTypeFor:arg:arg: */ + /* begin tempVarTypesArraysOf: */ + /* begin additionalMethodStateOf:at: */ + /* begin additionalMethodStateOf: */ + selectorOrProperties = penultimateLiteralOf(methodObject); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l5; + } + typesArray = /* safeFollowObjField:ofObject: */ + (1 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(1, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = arg0 & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(arg0)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2: + typesArray = /* safeFollowObjField:ofObject: */ + (0 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(0, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l4; + } + objectClass = /* fetchClassOf: */ + ((tagBits = arg1 & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(arg1)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l3; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l3; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l3; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l3; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l3; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l3: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l4; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l4; + } + } + /* end collectTypeOf:in: */ +l4: + /* end collectArgumentsTypeFor:arg:arg: */ +l5:; +} + + /* LiveTypingCoInterpreter>>#ceCollectArgumentsTypeForFramlessUnspilledActivationWithArg:of: */ +void +ceCollectArgumentsTypeForFramlessUnspilledActivationWithArgof(sqInt arg, CogMethod *cogMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt fmt; + sqInt index; + sqInt methodObject; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + methodObject = (cogMethod->methodObject); + + /* begin collectArgumentsTypeFor:arg: */ + /* begin tempVarTypesArraysOf: */ + /* begin additionalMethodStateOf:at: */ + /* begin additionalMethodStateOf: */ + selectorOrProperties = penultimateLiteralOf(methodObject); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l3; + } + typesArray = /* safeFollowObjField:ofObject: */ + (0 < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(0, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = arg & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(arg)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2: + /* end collectArgumentsTypeFor:arg: */ +l3:; +} + + /* LiveTypingCoInterpreter>>#ceCollectInstVarTypeFor:at:of: */ +void +ceCollectInstVarTypeForatof(sqInt rcvr, sqInt instVarIndex, sqInt assignedObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt index; + sqInt instVarTypesArrays; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt rcvrClass; + sqInt tagBits; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + /* begin collectInstanceVariableTypeFor:at:of: */ + rcvrClass = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)); + instVarTypesArrays = /* #instVarTypesArraysOf: #safeFollowObjField:ofObject: */ + (InstVarTypesArraysIndex < (lengthOf(rcvrClass)) + ? followObjFieldofObject(InstVarTypesArraysIndex, rcvrClass) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (instVarTypesArrays) + && (isInstanceOfClassArray(instVarTypesArrays)))) { + goto l3; + } + typesArray = /* safeFollowObjField:ofObject: */ + (instVarIndex < (lengthOf(instVarTypesArrays)) + ? followObjFieldofObject(instVarIndex, instVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = assignedObject & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(assignedObject)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2: + /* end collectTypeOf:in:at: */ +l3:; +} + + /* LiveTypingCoInterpreter>>#ceCollectRemoteTempVarTypeAt:of: */ +void +ceCollectRemoteTempVarTypeAtof(sqInt tempVarIndex, sqInt assignedObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt remoteTempTypesArrays; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + /* begin collectRemoteTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(GIV(framePointer)))->methodObject) + : longAt(GIV(framePointer) + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + remoteTempTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (RemoteTempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(RemoteTempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (remoteTempTypesArrays) + && (isInstanceOfClassArray(remoteTempTypesArrays)))) { + goto l3; + } + typesArray = /* safeFollowObjField:ofObject: */ + (tempVarIndex < (lengthOf(remoteTempTypesArrays)) + ? followObjFieldofObject(tempVarIndex, remoteTempTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = assignedObject & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(assignedObject)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2: + /* end collectTypeOf:in:at: */ +l3:; +} + + /* LiveTypingCoInterpreter>>#ceCollectReturnTypeFor:of: */ +void +ceCollectReturnTypeForof(CogMethod *cogMethod, sqInt returnObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt fmt; + sqInt index; + sqInt methodObject; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt returnTypesArray; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt typeAtIndex; + sqInt typesArraySize; + + methodObject = (cogMethod->methodObject); + + /* begin collectReturnTypeForMethod:of: */ + /* begin returnTypesArrayOf: */ + /* begin additionalMethodStateOf:at: */ + /* begin additionalMethodStateOf: */ + selectorOrProperties = penultimateLiteralOf(methodObject); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + returnTypesArray = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (ReturnTypesArrayIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(ReturnTypesArrayIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (returnTypesArray) + && (isInstanceOfClassArray(returnTypesArray)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = returnObject & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(returnObject)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(returnTypesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(returnTypesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(returnTypesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((returnTypesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, returnTypesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, returnTypesArray, objectClass)); + assert(isNonImmediate(returnTypesArray)); + if (oopisGreaterThanOrEqualTo(returnTypesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(returnTypesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(returnTypesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((returnTypesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2:; +} + + /* LiveTypingCoInterpreter>>#ceCollectTempVarTypeAt:of: */ +void +ceCollectTempVarTypeAtof(sqInt tempVarIndex, sqInt assignedObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt additionalMethodState; + sqInt fmt; + sqInt frameMethod; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt selectorOrProperties; + sqInt tagBits; + sqInt tempVarTypesArrays; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + /* begin collectTemporaryVariableTypeFor:at:of: */ + frameMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(GIV(framePointer)))->methodObject) + : longAt(GIV(framePointer) + FoxMethod)); + selectorOrProperties = penultimateLiteralOf(frameMethod); + + /* They can be told apart by their oop's format: Symbols are byte indexable while AdditionalMethodStates + are indexable objects with inst vars. See #formatOf:. */ + additionalMethodState = (((byteAt((void *)(selectorOrProperties + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat()) + ? selectorOrProperties + : 0); + tempVarTypesArrays = (!(!additionalMethodState) + ? /* safeFollowObjField:ofObject: */ + (TempVarTypesArraysIndex < (lengthOf(additionalMethodState)) + ? followObjFieldofObject(TempVarTypesArraysIndex, additionalMethodState) + : 0) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (tempVarTypesArrays) + && (isInstanceOfClassArray(tempVarTypesArrays)))) { + goto l3; + } + typesArray = /* safeFollowObjField:ofObject: */ + (tempVarIndex < (lengthOf(tempVarTypesArrays)) + ? followObjFieldofObject(tempVarIndex, tempVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = assignedObject & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(assignedObject)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2: + /* end collectTypeOf:in:at: */ +l3:; +} + + /* LiveTypingCoInterpreter>>#ceCollectTypesForPrimitiveSlotAt:put:of: */ +void +ceCollectTypesForPrimitiveSlotAtputof(sqInt slotIndex, sqInt assignedObject, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt index; + sqInt instVarIndex; + sqInt instVarTypesArrays; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt rcvrClass; + sqInt tagBits; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + + if (!((((slotIndex) & 7) == 1))) { + return; + } + + /* Slot indices are 1-relative */ + instVarIndex = ((slotIndex >> 3)) - 1; + if (instVarIndex < 0) { + return; + } + + /* begin collectInstanceVariableTypeFor:at:of: */ + rcvrClass = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)); + instVarTypesArrays = /* #instVarTypesArraysOf: #safeFollowObjField:ofObject: */ + (InstVarTypesArraysIndex < (lengthOf(rcvrClass)) + ? followObjFieldofObject(InstVarTypesArraysIndex, rcvrClass) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (instVarTypesArrays) + && (isInstanceOfClassArray(instVarTypesArrays)))) { + goto l3; + } + typesArray = /* safeFollowObjField:ofObject: */ + (instVarIndex < (lengthOf(instVarTypesArrays)) + ? followObjFieldofObject(instVarIndex, instVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l2; + } + objectClass = /* fetchClassOf: */ + ((tagBits = assignedObject & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(assignedObject)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l1: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l2; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l2; + } + } + /* end collectTypeOf:in: */ +l2: + /* end collectTypeOf:in:at: */ +l3:; +} + + /* LiveTypingCoInterpreter>>#primitiveSlotAtPut */ +static void +primitiveSlotAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt assignedObject; + sqInt fmt; + sqInt fmtSqInt; + sqInt index; + sqInt instVarIndex; + sqInt instVarTypesArrays; + sqInt newValue; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objectClass; + sqInt rcvr; + sqInt rcvrClass; + sqInt rcvrSqInt; + sqInt result; + sqInt slotIndex; + sqInt tagBits; + sqInt typeAtIndex; + sqInt typesArray; + sqInt typesArraySize; + usqIntptr_t value; + sqInt valueSqInt; + + assignedObject = longAt(GIV(stackPointer)); + slotIndex = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + + /* begin superPrimitiveSlotAtPut */ + newValue = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rcvrSqInt = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (!((((index) & 7) == 1))) { + result = (GIV(primFailCode) = PrimErrBadArgument); + goto l2; + } + if (((rcvrSqInt & (tagMask())) != 0)) { + result = (GIV(primFailCode) = PrimErrBadReceiver); + goto l2; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvrSqInt + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + ) { + result = (GIV(primFailCode) = PrimErrNoModification); + goto l2; + } + fmt = (byteAt((void *)(rcvrSqInt + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(rcvrSqInt)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(rcvrSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvrSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if ((((usqInt)index)) < numSlots) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(rcvrSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + externalInstVarofContextput(index, rcvrSqInt, newValue); + } + else { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, rcvrSqInt, newValue)); + assert(isNonImmediate(rcvrSqInt)); + if (oopisGreaterThanOrEqualTo(rcvrSqInt, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(newValue & (tagMask())))) + && (oopisLessThan(newValue, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvrSqInt + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvrSqInt); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvrSqInt + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),newValue); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); + result = 0; + goto l2; + } + result = (GIV(primFailCode) = PrimErrBadIndex); + goto l2; + } + + /* begin positiveMachineIntegerValueOf: */ + if ((((newValue) & 7) == 1)) { + valueSqInt = (newValue >> 3); + if (valueSqInt < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + value = null; + goto l1; + } + value = valueSqInt; + goto l1; + } + + /* don't inline the rare case */ + value = positiveMachineIntegerValueOfObj(newValue); + /* end positiveMachineIntegerValueOf: */ +l1: + if (GIV(primFailCode)) { + GIV(primFailCode) = PrimErrBadArgument; + result = 0; + goto l2; + } + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + result = (GIV(primFailCode) = PrimErrUnsupported); + goto l2; + } + if ((((usqInt)value)) > 0xFF) { + result = (GIV(primFailCode) = PrimErrBadArgument); + goto l2; + } + + /* begin numBytesOfBytes: */ + fmtSqInt = (byteAt((void *)(rcvrSqInt + (formatFieldByteOffset())))) & (formatMask()); + assert(fmtSqInt >= (firstByteFormat())); + numSlots = ((((/* begin numSlotsOf: */ + assert((classIndexOf(rcvrSqInt)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(rcvrSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvrSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) << (shiftForWord()))) - (fmtSqInt & 7); + if ((((usqInt)index)) < numSlots) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvrSqInt + BaseHeaderSize) + index),value); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); + result = 0; + goto l2; + } + result = (GIV(primFailCode) = PrimErrBadIndex); + goto l2; + } + if (fmt >= (firstShortFormat())) { + if ((((usqInt)value)) > 0xFFFF) { + result = (GIV(primFailCode) = PrimErrBadArgument); + goto l2; + } + numSlots = ((usqInt)((numBytesOf(rcvrSqInt)))) >> 1; + if ((((usqInt)index)) < numSlots) { + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvrSqInt + BaseHeaderSize) + ((((usqInt)(index) << 1)))),value); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); + result = 0; + goto l2; + } + result = (GIV(primFailCode) = PrimErrBadIndex); + goto l2; + } + if (fmt >= (firstLongFormat())) { + if ((((usqInt)value)) > 0xFFFFFFFFU) { + result = (GIV(primFailCode) = PrimErrBadArgument); + goto l2; + } + numSlots = ((usqInt)((numBytesOf(rcvrSqInt)))) >> 2; + if ((((usqInt)index)) < numSlots) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvrSqInt + BaseHeaderSize) + ((((usqInt)(index) << 2)))),value); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); + result = 0; + goto l2; + } + result = (GIV(primFailCode) = PrimErrBadIndex); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvrSqInt)))) >> 3; + if ((((usqInt)index)) < numSlots) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvrSqInt + BaseHeaderSize) + ((((usqInt)(index) << 3)))),value); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); + result = 0; + goto l2; + } + result = (GIV(primFailCode) = PrimErrBadIndex); + goto l2; + } + result = (GIV(primFailCode) = PrimErrBadReceiver); + /* end superPrimitiveSlotAtPut */ +l2: + if (!result) { + /* Slot indices are 1-relative */ + instVarIndex = ((slotIndex >> 3)) - 1; + + /* begin collectInstanceVariableTypeFor:at:of: */ + rcvrClass = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)); + instVarTypesArrays = /* #instVarTypesArraysOf: #safeFollowObjField:ofObject: */ + (InstVarTypesArraysIndex < (lengthOf(rcvrClass)) + ? followObjFieldofObject(InstVarTypesArraysIndex, rcvrClass) + : 0); + + /* begin collectTypeOf:in:at: */ + if (!(/* isArrayOop: */ + (instVarTypesArrays) + && (isInstanceOfClassArray(instVarTypesArrays)))) { + goto l3; + } + typesArray = /* safeFollowObjField:ofObject: */ + (instVarIndex < (lengthOf(instVarTypesArrays)) + ? followObjFieldofObject(instVarIndex, instVarTypesArrays) + : 0); + + /* begin collectTypeOf:in: */ + if (!(/* isArrayOop: */ + (typesArray) + && (isInstanceOfClassArray(typesArray)))) { + goto l5; + } + objectClass = /* fetchClassOf: */ + ((tagBits = assignedObject & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(assignedObject)); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(typesArray + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(typesArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + typesArraySize = numSlots; + goto l4; + } + if (fmt >= (firstByteFormat())) { + typesArraySize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l4; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l4; + } + if (fmt >= (firstLongFormat())) { + typesArraySize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + typesArraySize = numSlots; + goto l4; + } + + /* fmt = self forwardedFormat */ + typesArraySize = 0; + /* end lengthOf: */ +l4: + for (index = 0; index < typesArraySize; index += 1) { + /* begin followObjField:ofObject: */ + typeAtIndex = longAt((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(typeAtIndex)); + if ((!((longAt((void *)(typeAtIndex))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + typeAtIndex = fixFollowedFieldofObjectwithInitialValue(index, typesArray, typeAtIndex); + } + if (typeAtIndex == objectClass) { + goto l5; + } + if (typeAtIndex == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, typesArray, objectClass)); + assert(isNonImmediate(typesArray)); + if (oopisGreaterThanOrEqualTo(typesArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objectClass & (tagMask())))) + && (oopisLessThan(objectClass, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(typesArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(typesArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((typesArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),objectClass); + goto l5; + } + } + /* end collectTypeOf:in: */ +l5: + /* end collectTypeOf:in:at: */ +l3:; + } + return; +} + + +/* Answer the appropriate become effect flags for objOop, or 0 if none. + The effect flags determine how much work is done after the become in + following forwarding pointers, + voiding method caches, etc. Flag jitted methods because these will have to + be freed after divorcing + frames and mapping native pcs to bytecode pcs in contexts using them. + See the comment in InterpreterPrimitives>>#primitiveArrayBecome */ + + /* Spur64BitCoMemoryManager>>#becomeEffectFlagsFor: */ +static NoDbgRegParms sqInt +becomeEffectFlagsFor(sqInt objOop) +{ + int hash; + sqInt methodHeader; + + hash = 0; + if (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) { + if ((((hash = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()))) != 0) + && ((classAtIndex(hash)) == objOop)) { + return BecamePointerObjectFlag + BecameActiveClassFlag; + } + return BecamePointerObjectFlag; + } + if (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + methodHeader = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + flagCogMethodForBecome(((CogMethod *) methodHeader)); + return BecameCompiledMethodFlag + BecameJittedCompiledMethodFlag; + } + return BecameCompiledMethodFlag; + } + return 0; +} + + +/* The check against scavengeThreshold in + genInstantiate64BitSignedIntegerValue:* et al compares + freeStart against scavengeThreshold minus the allocation size. Hence the + assert must compensate here. + */ + + /* Spur64BitCoMemoryManager>>#ceScheduleScavenge */ +void +ceScheduleScavenge(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(((GIV(freeStart) + BaseHeaderSize) + (sizeof(double))) >= GIV(scavengeThreshold)); + + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); +} + + /* Spur64BitCoMemoryManager>>#classSmallFloat */ +sqInt +classSmallFloat(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)((smallFloatTag())) << (shiftForWord())))))); +} + + +/* Perform an integrity/leak check using the heapMap. Set a bit at each + object's header. */ +/* Perform an integrity/leak check using the heapMap. Set a bit at each + object's header. + Override to set a bit at each Cog method */ + + /* Spur64BitCoMemoryManager>>#clearLeakMapAndMapAccessibleObjects */ +static void +clearLeakMapAndMapAccessibleObjects(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + clearHeapMap(); + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + heapMapAtWordPut(pointerForOop(obj), 1); + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + addCogMethodsToHeapMap(); +} + + +/* Ensure there are no forwarded literals in the argument. */ + + /* Spur64BitCoMemoryManager>>#ensureNoForwardedLiteralsIn: */ +void +ensureNoForwardedLiteralsIn(sqInt aMethodObj) +{ + followForwardedObjectFieldstoDepth(aMethodObj, 0); +} + + +/* Follow pointers in the object to depth. + Answer if any forwarders were found. + How to avoid cyclic structures?? A temporary mark bit? eem 6/22/2020 no + need since depth is always finite. */ + + /* Spur64BitCoMemoryManager>>#followForwardedObjectFields:toDepth: */ +static NoDbgRegParms sqInt +followForwardedObjectFieldstoDepth(sqInt objOop, sqInt depth) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt fmt; + sqInt found; + sqInt header; + sqInt headerSqInt; + sqInt i; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt referent; + sqInt sp; + + found = 0; + assert((isPointers(objOop)) + || (isOopCompiledMethod(objOop))); + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin numPointerSlotsOf:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + numSlots = CtxtTempFrameStart + contextSize; + goto l2; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + goto l2; + } + if (fmt == (forwardedFormat())) { + numSlots = 1; + goto l2; + } + if (fmt < (firstCompiledMethodFormat())) { + numSlots = 0; + goto l2; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf:format: */ +l2: + + /* It is essential to skip the first field of a method because it may be a + reference to a Cog method in the method zone, not a real object at all. */ + for (i = ((fmt >= (firstCompiledMethodFormat()) + ? 1 + : 0)); i < numSlots; i += 1) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(oop & (tagMask())))) { + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + found = 1; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(i, objOop, oop)); + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(oop & (tagMask())))) + && (oopisLessThan(oop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),oop); + } + if ((depth > 0) + && ((/* hasPointerFields: */ + ((!(oop & (tagMask())))) + && (hasPointerFieldsNonImm(oop))) + && (followForwardedObjectFieldstoDepth(oop, depth - 1)))) { + found = 1; + } + } + } + return found; +} + + /* Spur64BitCoMemoryManager>>#freeStartAddress */ +usqInt +freeStartAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(freeStart)))); +} + + /* Spur64BitCoMemoryManager>>#getScavengeThreshold */ +usqInt +getScavengeThreshold(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(scavengeThreshold); +} + + +/* Answer the header of the argument even though + it may have its header word in a forwarding block + (which shouldn't happen with Spur). */ + + /* Spur64BitCoMemoryManager>>#headerWhileForwardingOf: */ +static NoDbgRegParms sqLong +headerWhileForwardingOf(sqInt aCompiledMethodObjOop) +{ + assert(!(isForwarded(aCompiledMethodObjOop))); + return long64At((void *)(aCompiledMethodObjOop)); +} + + +/* Answer true if the argument contains indexable bytes. See comment in + formatOf: + */ +/* Note: Includes CompiledMethods. */ + + /* Spur64BitCoMemoryManager>>#isBytes: */ +sqInt +isBytes(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())); +} + + +/* A lenient tester of forwarded class indices for inline cache management in + the Cogit. + */ + + /* Spur64BitCoMemoryManager>>#isForwardedClassIndex: */ +sqInt +isForwardedClassIndex(sqInt maybeClassIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt entry; + sqInt fieldIndex; + + if ((((usqInt)maybeClassIndex)) >= (1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift())))) { + return 0; + } + fieldIndex = ((usqInt)(maybeClassIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return 0; + } + fieldIndex = maybeClassIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + entry = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + return (!((longAt((void *)(entry))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))); +} + + /* Spur64BitCoMemoryManager>>#isImmediateClass: */ +int +isImmediateClass(sqInt classObj) +{ + return ((((usqInt)((((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) == (forwardedFormat()); +} + + +/* Answer if obj is young. Require that obj is non-immediate. Override to + filter-out Cog methods + */ + + /* Spur64BitCoMemoryManager>>#isReallyYoungObject: */ +sqInt +isReallyYoungObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(isNonImmediate(objOop)); + return (oopisLessThan(objOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(objOop, GIV(newSpaceStart))); +} + + +/* Answer the method header of a CompiledMethod object. + If the method has been cogged then the header is a pointer to + the CogMethod and the real header will be stored in the CogMethod. */ + + /* Spur64BitCoMemoryManager>>#methodHeaderOf: */ +sqInt +methodHeaderOf(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + + assert(isCompiledMethod(methodObj)); + header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + return ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); +} + + /* Spur64BitCoMemoryManager>>#needGCFlagAddress */ +usqInt +needGCFlagAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(needGCFlag)))); +} + + /* Spur64BitCoMemoryManager>>#printCantBeObject:on: */ +static NoDbgRegParms void +printCantBeObjecton(sqInt oop, FILE *aStream) +{ + char *where; + + fprintf(aStream, + "%p%s\n", + ((void *)oop), + ((where = whereIsMaybeCodeThing(oop)) + ? where + : (oop & 7 /* (allocationUnit - 1) */ + ? " is misaligned" + : whereIs(oop)))); +} + + /* Spur64BitCoMemoryManager>>#specialObjectsOopAddress */ +usqInt +specialObjectsOopAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(specialObjectsOop)))); +} + + +/* For the purposes of become: send selector to the cogit with obj1, obj2 + and aBool and answer the result. Undo forwarding for the selector. */ + + /* Spur64BitCoMemoryManager>>#withoutForwardingOn:and:with:sendToCogit: */ +sqInt +withoutForwardingOnandwithsendToCogit(sqInt obj1, sqInt obj2, sqInt aBool, sqInt (*selector)(sqInt,sqInt,sqInt)) +{ + sqInt targetA; + sqInt targetB; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj1)); + targetA = longAt((void *)((obj1 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(targetA & (tagMask())))) + && ((!((longAt((void *)(targetA))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + targetA = longAt((void *)((targetA + BaseHeaderSize) + (0U << (shiftForWord())))); + } + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj2)); + targetB = longAt((void *)((obj2 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(targetB & (tagMask())))) + && ((!((longAt((void *)(targetB))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + targetB = longAt((void *)((targetB + BaseHeaderSize) + (0U << (shiftForWord())))); + } + return selector(targetA, targetB, aBool); +} + + +/* Answer the address immediately following an object. */ + + /* Spur64BitMemoryManager>>#addressAfter: */ +static NoDbgRegParms usqInt +addressAfter(sqInt objOop) +{ + usqInt numSlots; + + numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + if (!numSlots) { + return (objOop + 8 /* allocationUnit */) + BaseHeaderSize; + } + if (numSlots == (numSlotsMask())) { + numSlots = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8; + } + return (objOop + BaseHeaderSize) + (((numSlots << (shiftForWord())))); +} + + +/* Answer the oop of a chunk of space in oldSpace with numSlots slots. Try + and allocate in a segment that already includes pinned objects. The header + of the + result will have been filled-in but not the contents. */ + + /* Spur64BitMemoryManager>>#allocateSlotsForPinningInOldSpace:bytes:format:classIndex: */ +static NoDbgRegParms sqInt +allocateSlotsForPinningInOldSpacebytesformatclassIndex(sqInt numSlots, usqInt totalBytes, sqInt formatField, sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt acceptedChunk; + sqInt acceptedNode; + sqInt child; + usqInt childBytes; + sqInt chunk; + usqInt index; + usqInt initialIndex; + sqInt lilliputian; + sqInt lilliputianSqInt; + sqInt next; + sqInt nextFreeChunk; + sqInt nextSqInt; + sqInt node; + sqInt prev; + + /* begin allocateOldSpaceChunkOfBytes:suchThat: */ + next = 0; + prev = 0; + + /* be optimistic (& don't wait for the write) */ + GIV(totalFreeOldSpace) -= totalBytes; + initialIndex = totalBytes / 8 /* allocationUnit */; + if ((initialIndex < 64 /* numFreeLists */) + && ((1ULL << initialIndex) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << initialIndex)) != 0)) { + if ((node = GIV(freeLists)[initialIndex])) { + prev = 0; + while (node != 0) { + assert(node == (startOfObject(node))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(node)); + next = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + if (((segmentContainingObj(node))->containsPinned)) { + if (prev) { + /* begin setNextFreeChunkOf:withValue:chunkBytes: */ + /* begin isLilliputianSize: */ + assert(totalBytes >= (BaseHeaderSize + (allocationUnit()))); + lilliputianSqInt = totalBytes == (BaseHeaderSize + 8 /* allocationUnit */); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((next == 0) + || (isFreeObject(next))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),next); + if ((next != 0) + && (!lilliputianSqInt)) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + } + } + else { + /* begin unlinkFreeChunk:atIndex:chunkBytes: */ + /* begin isLilliputianSize: */ + assert(totalBytes >= (BaseHeaderSize + (allocationUnit()))); + lilliputian = totalBytes == (BaseHeaderSize + 8 /* allocationUnit */); + + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(node)) == (initialIndex * (allocationUnit()))) + && ((initialIndex > 1) + && ((startOfObject(node)) == node))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[initialIndex] = ((nextSqInt = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))))); + if ((!lilliputian) + && (nextSqInt != 0)) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextSqInt)); + longAtput((void *)((nextSqInt + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + } + chunk = node; + goto l1; + } + prev = node; + node = next; + } + } + else { + GIV(freeListsMask) -= 1ULL << initialIndex; + } + } + + /* first search for free chunks of a multiple of chunkBytes in size */ + index = initialIndex; + while ((((index += initialIndex)) < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << index)) != 0)) { + if ((node = GIV(freeLists)[index])) { + prev = 0; + while (node != 0) { + assert(node == (startOfObject(node))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(node)); + next = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + if (((segmentContainingObj(node))->containsPinned)) { + if (prev) { + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((next == 0) + || (isFreeObject(next))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),next); + if (next) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + } + } + else { + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(node)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(node)) == node))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((nextSqInt = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))))); + if (nextSqInt) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextSqInt)); + longAtput((void *)((nextSqInt + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + } + freeChunkWithBytesat((index * 8 /* allocationUnit */) - totalBytes, (/* startOfObject: */ + ((byteAt((void *)(node + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? node - BaseHeaderSize + : node)) + totalBytes); + chunk = node; + goto l1; + } + prev = node; + node = next; + } + } + else { + GIV(freeListsMask) -= 1ULL << index; + } + } + } + + /* now get desperate and use the first that'll fit. + Note that because the minimum free size is 16 bytes (2 * allocationUnit), to + leave room for the forwarding pointer/next free link, we can only break chunks + that are at least 16 bytes larger, hence start at initialIndex + 2. */ + index = initialIndex + 1; + while ((((index += 1)) < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << index)) != 0)) { + if ((node = GIV(freeLists)[index])) { + prev = 0; + while (node != 0) { + assert(node == (startOfObject(node))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(node)); + next = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + if (((segmentContainingObj(node))->containsPinned)) { + if (prev) { + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((next == 0) + || (isFreeObject(next))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),next); + if (next) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + } + } + else { + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(node)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(node)) == node))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((nextSqInt = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))))); + if (nextSqInt) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextSqInt)); + longAtput((void *)((nextSqInt + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + } + freeChunkWithBytesat((index * 8 /* allocationUnit */) - totalBytes, (/* startOfObject: */ + ((byteAt((void *)(node + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? node - BaseHeaderSize + : node)) + totalBytes); + chunk = node; + goto l1; + } + prev = node; + node = next; + } + } + else { + GIV(freeListsMask) -= 1ULL << index; + } + } + } + } + + /* Large chunk, or no space on small free lists. Search the large chunk list. + Large chunk list organized as a tree, each node of which is a list of chunks + of the same size. Beneath the node are smaller and larger blocks. + When the search ends parent should hold the smallest chunk at least as + large as chunkBytes, or 0 if none. acceptedChunk and acceptedNode save + us from having to back-up when the acceptanceBlock filters-out all nodes + of the right size, but there are nodes of the wrong size it does accept. */ + child = GIV(freeLists)[0]; + node = (acceptedChunk = (acceptedNode = 0)); + while (child != 0) { + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(child)); + childBytes = bytesInBody(child); + if (childBytes == totalBytes) { + node = child; + while (1) { + prev = node; + node = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + if (!(node != 0)) break; + if (((segmentContainingObj(node))->containsPinned)) { + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(node)); + nextFreeChunk = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + } + chunk = /* startOfObject: */ + ((byteAt((void *)(node + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? node - BaseHeaderSize + : node); + goto l1; + } + } + if (((segmentContainingObj(child))->containsPinned)) { + next = longAt((void *)((child + BaseHeaderSize) + (0U << (shiftForWord())))); + if (next) { + inFreeTreeReplacewith(child, next); + } + else { + unlinkSolitaryFreeTreeNode(child); + } + + /* no list; remove the interior node + list; replace node with it */ + chunk = /* startOfObject: */ + ((byteAt((void *)(child + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? child - BaseHeaderSize + : child); + goto l1; + } + } + + /* size match; try to remove from list at node. */ + if (child) { + /* Note that because the minimum free size is 16 bytes (2 * allocationUnit), to + leave room for the forwarding pointer/next free link, we can only break chunks + that are at least 16 bytes larger, hence reject chunks < 2 * allocationUnit larger. */ + if (childBytes <= (totalBytes + 8 /* allocationUnit */)) { + child = longAt((void *)((child + BaseHeaderSize) + (4U << (shiftForWord())))); + } + else { + node = child; + child = longAt((void *)((node + BaseHeaderSize) + (3U << (shiftForWord())))); + if (!acceptedNode) { + acceptedChunk = node; + + /* first search the list. */ + do { + acceptedChunk = longAt((void *)((acceptedChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + if ((acceptedChunk != 0) + && (((segmentContainingObj(acceptedChunk))->containsPinned))) { + acceptedNode = node; + } + } while((acceptedChunk != 0) + && (acceptedNode == 0)); + + /* nothing on the list; will the node do? This prefers + acceptable nodes higher up the tree over acceptable + list elements further down, but we haven't got all day... */ + if ((acceptedNode == 0) + && (((segmentContainingObj(node))->containsPinned))) { + acceptedNode = node; + + /* break out of loop now we have an acceptedNode */ + child = 0; + } + } + } + } + } + if (acceptedNode) { + if (acceptedChunk) { + assert((bytesInBody(acceptedChunk)) >= (totalBytes + (allocationUnit()))); + while (1) { + next = longAt((void *)((acceptedNode + BaseHeaderSize) + (0U << (shiftForWord())))); + if (!(next != acceptedChunk)) break; + acceptedNode = next; + } + nextFreeChunk = longAt((void *)((acceptedChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(acceptedNode)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((acceptedNode + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((acceptedNode == 0) + || (isFreeObject(acceptedNode))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),acceptedNode); + } + freeChunkWithBytesat((bytesInBody(acceptedChunk)) - totalBytes, (/* startOfObject: */ + ((byteAt((void *)(acceptedChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? acceptedChunk - BaseHeaderSize + : acceptedChunk)) + totalBytes); + chunk = /* startOfObject: */ + ((byteAt((void *)(acceptedChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? acceptedChunk - BaseHeaderSize + : acceptedChunk); + goto l1; + } + next = longAt((void *)((acceptedNode + BaseHeaderSize) + (0U << (shiftForWord())))); + if (next) { + inFreeTreeReplacewith(acceptedNode, next); + } + else { + unlinkSolitaryFreeTreeNode(acceptedNode); + } + + /* no list; remove the interior node + list; replace node with it */ + assert((bytesInBody(acceptedNode)) >= (totalBytes + (allocationUnit()))); + freeChunkWithBytesat((bytesInBody(acceptedNode)) - totalBytes, (/* startOfObject: */ + ((byteAt((void *)(acceptedNode + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? acceptedNode - BaseHeaderSize + : acceptedNode)) + totalBytes); + chunk = /* startOfObject: */ + ((byteAt((void *)(acceptedNode + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? acceptedNode - BaseHeaderSize + : acceptedNode); + goto l1; + } + + /* optimism was unfounded */ + GIV(totalFreeOldSpace) += totalBytes; + chunk = null; + /* end allocateOldSpaceChunkOfBytes:suchThat: */ +l1: + if (!chunk) { + chunk = allocateOldSpaceChunkOfBytes(totalBytes); + if (!chunk) { + return null; + } + ((segmentContainingObj(chunk))->containsPinned = 1); + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(chunk),numSlots + ((((usqInt)((numSlotsMask())) << (numSlotsFullShift()))))); + longAtput((void *)(chunk + BaseHeaderSize),(((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex) | (1U << (pinnedBitShift()))); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return chunk + BaseHeaderSize; + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + longAtput((void *)(chunk),(((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex) | (1U << (pinnedBitShift()))); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return chunk; +} + + +/* Answer the given integer with its bytes in the reverse order. */ + + /* Spur64BitMemoryManager>>#byteSwapped: */ +sqInt +byteSwapped(sqInt w) +{ + return SQ_SWAP_8_BYTES(w); +} + + +/* Answer the total number of bytes in an object including header and + possible overflow size header. + */ + + /* Spur64BitMemoryManager>>#bytesInBody: */ +static NoDbgRegParms usqInt +bytesInBody(sqInt objOop) +{ + usqLong header; + usqLong headerNumSlots; + usqInt numSlots; + + header = longAt((void *)(objOop)); + headerNumSlots = (header) >> (numSlotsFullShift()); + numSlots = (headerNumSlots == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : (headerNumSlots + ? headerNumSlots + : 1)); + return ((numSlots << (shiftForWord()))) + ((headerNumSlots == (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)); +} + + +/* Answer the default amount of memory to allocate for the eden space. + The actual value can be set via vmParameterAt: and/or a preference in the + ini file. + The shootout tests seem to plateau at 5 or 6Mb. + + Originally, both the 32-bit and 64-bit versions used the same 4Mb default. + Measuring the simulator on image start-up, the 64-bit system's eden at the + same point in start-up + (the first copyBits) is only 8% larger in bytes because it allocates 26% + fewer objects. + Some 21% of the objects in the 32-bit version's eden are large integers + and floats that + are representable as 64-bit immediates. + + But when running benchmarks such as the computer language shootout's + binary trees, + using the same amount of memory for the 64-bit system causes a significant + slow-down and a lot of compactions. So we now use 4Mb for 32-bits and 7Mb + for 64-bits. */ + + /* Spur64BitMemoryManager>>#defaultEdenBytes */ +static int +defaultEdenBytes(void) +{ + return 0x700000; +} + + +/* Answer the tag used in lookup caches for a receiver. This is the + receiver's classIndex. */ + + /* Spur64BitMemoryManager>>#fetchClassTagOf: */ +sqInt +fetchClassTagOf(sqInt oop) +{ + sqInt tagBits; + + return ((tagBits = oop & (tagMask())) + ? tagBits + : (longAt((void *)(oop))) & (classIndexMask())); +} + + /* Spur64BitMemoryManager>>#floatObjectOf: */ +sqInt +floatObjectOf(double aFloat) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newFloatObj; + usqInt newObj; + usqInt numBytes; + sqInt numSlots; + usqLong rawFloat; + usqLong rot; + + if (isSmallFloatValue(aFloat)) { + /* begin smallFloatObjectOf: */ + assert(isSmallFloatValue(aFloat)); + memcpy((&rawFloat), (&aFloat), sizeof(rawFloat)); + + /* begin smallFloatObjectOfBits: */ + assert(isSmallFloatValueBits(rawFloat)); + rot = (((rawFloat) >> 0x3F) & 1) + ((rawFloat << 1)); + if (rot > 1) { + rot -= (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + assert(rot > 0); + } + + /* a.k.a. ~= +/-0.0 */ + return ((rot << (numTagBits()))) + (smallFloatTag()); + } + numSlots = (sizeof(double)) / BytesPerOop; + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((numSlots >= 0) + && ((knownClassAtIndex(ClassFloatCompactIndex)) != GIV(nilObj))); + assert((firstLongFormat()) == (instSpecOfClass(knownClassAtIndex(ClassFloatCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newFloatObj = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((firstLongFormat())) << (formatShift()))))) + ClassFloatCompactIndex); + GIV(freeStart) += numBytes; + newFloatObj = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + storeFloatAtfrom(newFloatObj + BaseHeaderSize, aFloat); + return newFloatObj; +} + + +/* Answer the 64-bit value of the argument as raw bits. */ + + /* Spur64BitMemoryManager>>#floatValueBitsOf: */ +static NoDbgRegParms sqLong +floatValueBitsOf(sqInt floatOop) +{ + usqLong rot; + + assert(isFloatInstance(floatOop)); + if (floatOop & (tagMask())) { + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOop)); + rot = ((((usqInt)floatOop))) >> (numTagBits()); + if (rot > 1) { + rot += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + rot = ((rot << 0x3F)) + (((((usqInt)rot))) >> 1); + return rot; + } + return long64At((void *)((floatOop + BaseHeaderSize))); +} + + +/* Answer the C double precision floating point value of the argument, + or fail if it is not a Float, and answer 0. + Note: May be called by translated primitive code. */ + + /* Spur64BitMemoryManager>>#floatValueOf: */ +double +floatValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + double result; + sqInt tagBits; + double value; + + if ((tagBits = oop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + return value; + } + } + else { + if (((longAt((void *)(oop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(oop + BaseHeaderSize, result); + return result; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0.0; +} + + +/* This is a horrible hack for getting to the first object in eden if + pastSpace is almost full. + If there is only one (64-bit) word at the end of pastSpace there is no + room for a full + bridge, but there is room for this hack. */ + + /* Spur64BitMemoryManager>>#hackSlimBridgeTo:at: */ +static NoDbgRegParms void +hackSlimBridgeToat(sqInt objOop, sqInt startAddress) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(oopisLessThan(startAddress, objOop)); + assert(oopisLessThanOrEqualTo(objOop, GIV(oldSpaceStart))); + assert(((startAddress + (allocationUnit())) == objOop) + || (((startAddress + (allocationUnit())) + (allocationUnit())) == objOop)); + longAtput((void *)(startAddress),((startAddress + 8 /* allocationUnit */) == objOop + ? ((sqInt)((usqInt)((numSlotsMask())) << (numSlotsFullShift()))) + : ((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + 1)); +} + + +/* A negative header selects the alternate bytecode set. */ + + /* Spur64BitMemoryManager>>#headerIndicatesAlternateBytecodeSet: */ +int +headerIndicatesAlternateBytecodeSet(sqInt methodHeader) +{ + return (((sqLong) methodHeader)) < 0; +} + + +/* must have room for a header (single or double) plus the next free pointer */ + + /* Spur64BitMemoryManager>>#initFreeChunkWithBytes:at: */ +static NoDbgRegParms sqInt +initFreeChunkWithBytesat(usqLong numBytes, sqInt address) +{ + sqLong numSlots; + + assert(((numBytes % (allocationUnit())) == 0) + && (numBytes >= (BaseHeaderSize + BytesPerOop))); + + /* double header */ + if (numBytes >= (((((usqInt)((numSlotsMask())) << (shiftForWord())))) + BaseHeaderSize)) { + numSlots = ((usqLong)(((numBytes - BaseHeaderSize) - BaseHeaderSize))) >> (shiftForWord()); + longAtput((void *)(address),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(address + 8),((sqInt)((usqInt)((numSlotsMask())) << (numSlotsFullShift())))); + return address + 8; + } + + /* single header */ + numSlots = ((usqLong)((numBytes - BaseHeaderSize))) >> (shiftForWord()); + assert(numSlots < (numSlotsMask())); + longAtput((void *)(address),((sqLong)((usqLong)(numSlots) << (numSlotsFullShift())))); + return address; +} + + +/* Must have room for a double header or a short object with the forwarding + slot (16 bytes either way). + */ + + /* Spur64BitMemoryManager>>#initSegmentBridgeWithBytes:at: */ +static NoDbgRegParms void +initSegmentBridgeWithBytesat(usqLong numBytes, sqInt address) +{ + sqLong numSlots; + + assert(((numBytes % (allocationUnit())) == 0) + && (numBytes >= (BaseHeaderSize + BaseHeaderSize))); + numSlots = ((usqLong)(((numBytes - BaseHeaderSize) - BaseHeaderSize))) >> (shiftForWord()); + if (numSlots) { + longAtput((void *)(address),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(address + BaseHeaderSize),(((((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + (1U << (pinnedBitShift()))) + (1ULL << (markedBitFullShift()))) + ((((usqInt)((sixtyFourBitIndexableFormat())) << (formatShift()))))) + (segmentBridgePun())); + } + else { + longAtput((void *)(address),(((1U << (pinnedBitShift())) + (1ULL << (markedBitFullShift()))) + ((((usqInt)((sixtyFourBitIndexableFormat())) << (formatShift()))))) + (segmentBridgePun())); + } +} + + +/* Allocate an instance of a variable class, excepting CompiledMethod. */ + + /* Spur64BitMemoryManager>>#instantiateClass:indexableSize: */ +sqInt +instantiateClassindexableSize(sqInt classObj, usqInt nElements) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt err; + sqInt fillValue; + int hash; + sqInt instSpec; + sqInt newObj; + usqInt newObjUsqInt; + usqInt numBytes; + usqInt numSlots; + usqInt p; + usqInt toDoLimit; + + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + fillValue = 0; + switch (instSpec) { + case arrayFormat(): + numSlots = nElements; + fillValue = GIV(nilObj); + break; + case indexablePointersFormat(): + case weakArrayFormat(): + numSlots = (classFormat & ((1U << (fixedFieldsFieldWidth())) - 1)) + nElements; + fillValue = GIV(nilObj); + break; + case sixtyFourBitIndexableFormat(): + numSlots = nElements; + break; + case firstLongFormat(): + if ((classIndex == ClassFloatCompactIndex) + && (nElements != 2)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return null; + } + numSlots = (nElements + 1) / 2; + instSpec += nElements & 1; + break; + case firstShortFormat(): + numSlots = (nElements + 3) / 4; + instSpec += (4 - nElements) & 3; + break; + case firstByteFormat(): + numSlots = (nElements + 7) / 8; + instSpec += (8 - nElements) & 7; + break; + default: + if ((nElements != 0) + || (instSpec > 5 /* lastPointerFormat */)) { + return null; + } + numSlots = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + fillValue = GIV(nilObj); + } + + /* non-indexable + Some Squeak images include funky fixed subclasses of abstract variable + superclasses. e.g. DirectoryEntry as a subclass of ArrayedCollection. + The (Threaded)FFIPlugin expects to be able to instantiate ExternalData via + this method. + Hence allow fixed classes to be instantiated here iff nElements = 0. */ + if (!classIndex) { + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + return null; + } + } + if (numSlots > ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (numSlots > (0x10000000000LL)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return null; + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpec, classIndex); + } + else { + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if (((numSlots) >> 56) > 0) { + newObj = null; + goto l1; + } + newObjUsqInt = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObjUsqInt = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, instSpec, classIndex); + goto l1; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObjUsqInt),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObjUsqInt),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObjUsqInt % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newObj = newObjUsqInt; + /* end allocateSlots:format:classIndex: */ +l1:; + } + if (newObj) { + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),fillValue); + } + } + return newObj; +} + + +/* Convert the integer value, assumed to be in SmallInteger range, into a + tagged SmallInteger object. + In C, use a shift and an add to set the tag bit. + In Smalltalk we have to work harder because the simulator works with + strictly positive bit patterns. */ + + /* Spur64BitMemoryManager>>#integerObjectOf: */ +sqInt +integerObjectOf(sqInt value) +{ + return (((((usqInt)value)) << (numTagBits()))) + 1; +} + + +/* Translator produces 'oop >> 3' */ + + /* Spur64BitMemoryManager>>#integerValueOf: */ +sqInt +integerValueOf(sqInt oop) +{ + return (((((usqInt)(oop)) >> 0x3F)) == 1 + ? ((((((-(numTagBits())) < 0) ? (((usqInt)(oop)) >> (-(-(numTagBits())))) : ((((usqInt)(oop) << (-(numTagBits()))))))) & 0x1FFFFFFFFFFFFFFFLL) - 0x1FFFFFFFFFFFFFFFLL) - 1 + : (((-(numTagBits())) < 0) ? (((usqInt)(oop)) >> (-(-(numTagBits())))) : (((sqInt)((usqInt)(oop) << (-(numTagBits()))))))); +} + + /* Spur64BitMemoryManager>>#isFloatInstance: */ +static NoDbgRegParms int +isFloatInstance(sqInt oop) +{ + sqInt tagBits; + + return ((tagBits = oop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(oop))) & (classIndexMask())) == ClassFloatCompactIndex); +} + + /* Spur64BitMemoryManager>>#isImmediateFloat: */ +static NoDbgRegParms int +isImmediateFloat(sqInt oop) +{ + return ((oop & (smallFloatTag())) != 0); +} + + /* Spur64BitMemoryManager>>#isIntegerObject: */ +int +isIntegerObject(sqInt oop) +{ + return ((oop & (smallIntegerTag())) != 0); +} + + +/* Answer if the given value can be represented as a Smalltalk integer value. + In 64-bits we use a 3 bit tag which leaves 61 bits for 2's complement + signed integers. In C, use a shift add and mask to test if the top 4 bits + are all the same. + Since 16rFFFFFFFFFFFFFFFF >> 60 = 16rF the computation intValue >> 60 + 1 + bitAnd: 16rF + maps in-range -ve values to 0 and in-range +ve values to 1. */ + + /* Spur64BitMemoryManager>>#isIntegerValue: */ +int +isIntegerValue(sqInt intValue) +{ + return (((((usqInt)(intValue)) >> 60) + 1) & 15) <= 1; +} + + +/* To have a prev pointer, which follows the next pointer, we need at least + two slots. + */ + + /* Spur64BitMemoryManager>>#isLilliputianSize: */ +static NoDbgRegParms int +isLilliputianSize(sqInt chunkBytes) +{ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + return chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */); +} + + /* Spur64BitMemoryManager>>#isSmallFloatValueBits: */ +static NoDbgRegParms int +isSmallFloatValueBits(usqLong rawFloatBits) +{ + usqInt exponent; + + exponent = ((rawFloatBits) >> (smallFloatMantissaBits())) & 0x7FF; + return (exponent > (smallFloatExponentOffset()) + ? exponent <= (0xFF + (smallFloatExponentOffset())) + : (rawFloatBits & ((1ULL << (smallFloatMantissaBits())) - 1) + ? exponent == (smallFloatExponentOffset()) + : exponent == 0)); +} + + /* Spur64BitMemoryManager>>#isSmallFloatValue: */ +static NoDbgRegParms int +isSmallFloatValue(double aFloat) +{ + usqInt exponent; + usqLong rawFloat; + + memcpy((&rawFloat), (&aFloat), sizeof(rawFloat)); + exponent = ((rawFloat) >> (smallFloatMantissaBits())) & 0x7FF; + return (exponent > (smallFloatExponentOffset()) + ? exponent <= (0xFF + (smallFloatExponentOffset())) + : (rawFloat & ((1ULL << (smallFloatMantissaBits())) - 1) + ? exponent == (smallFloatExponentOffset()) + : exponent == 0)); +} + + +/* Answer if the argument contains only indexable words (no oops). See + comment in formatOf: + */ + + /* Spur64BitMemoryManager>>#isWordsNonImm: */ +static NoDbgRegParms int +isWordsNonImm(sqInt objOop) +{ + return ((((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1))); +} + + +/* Answer the number of indexable units in the given object. + For a CompiledMethod, the size of the method header (in bytes) + should be subtracted from the result of this method. */ + + /* Spur64BitMemoryManager>>#lengthOf:format: */ +static NoDbgRegParms sqInt +lengthOfformat(sqInt objOop, sqInt fmt) +{ + usqInt numSlots; + usqInt numSlotsUsqInt; + + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + +/* If floatOrInt is an integer and we enable mixed arithmetic in primitives, + then convert it to a C double float and return it. + If it is a Float, then load its value and return it. + Otherwise fail -- ie return with primErrorCode non-zero. */ + + /* Spur64BitMemoryManager>>#loadFloatOrIntFrom: */ +static NoDbgRegParms double +loadFloatOrIntFrom(sqInt floatOrIntOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + double result; + sqInt tagBits; + double value; + + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + return value; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + return ((double) ((floatOrIntOop >> 3)) ); + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + return result; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0.0; +} + + +/* Answer the maximum number of slots we are willing to attempt to allocate + in an object. + Must fit in 56-bits; c.f. bytesInObject: */ + + /* Spur64BitMemoryManager>>#maxSlotsForAlloc */ +static sqInt +maxSlotsForAlloc(void) +{ + return 0x10000000000LL; +} + + +/* Answer the C double precision floating point value of the argument, + which *must* be something for which self isFloatInstance: answers true. + Note: May be called by translated primitive code. */ + + /* Spur64BitMemoryManager>>#noFailFloatValueOf: */ +double +noFailFloatValueOf(sqInt aFloatOop) +{ + usqLong bits; + double result; + + assert(isFloatInstance(aFloatOop)); + if (aFloatOop & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(aFloatOop)); + bits = ((((usqInt)aFloatOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&result), (&bits), sizeof(result)); + } + else { + fetchFloatAtinto(aFloatOop + BaseHeaderSize, result); + } + return result; +} + + +/* Answer the number of free lists. We use freeListsMask, a bitmap, to avoid + reading empty list heads. This should fit in a machine word to end up in a + register during free chunk allocation. */ + + /* Spur64BitMemoryManager>>#numFreeLists */ +static sqInt +numFreeLists(void) +{ + return 64; +} + + +/* Object parsing. + 1. all objects have at least a word following the header, for a forwarding + pointer. 2. objects with an overflow size have a preceding word with a + saturated numSlots. If the word + following an object doesn't have a saturated numSlots field it must be a + single-header object. + If the word following does have a saturated numSlots it must be the + overflow size word. + + This variation on objectAfter:limit: allows for a single (64-bit) word + bridge which may be needed + to bridge from an almost full pastSpace to eden. It is only used in the + flat enumerators that use + startAddressForBridgedHeapEnumeration and enumerate over pastSpace, eden + and oldSpace + in that order. Note that the order for allObjects, and allInstances + enumerates over oldSpace first. + + This hack is cheap. It increases the size of the objectAfter code, but + saves two extra copies of + the inner loop, since the inner loop now enumerates over all of pastSpace, + eden and oldSpace. + The test for a slim bridge is only performed if applied to an overflow + header, and typically only + 1 in 400 objects have overflow headers in 32-bits, 1 in 500 in 64-bits. + The complication is that + image segment loading evaporates the word array by setting the overflow + slots to 1, and this + is ambiguous with a slimbridge. The resolution is that if the segmentArray + has an overflow header, + and is in new space, then its slot size can be zeroed and its overflow + header changed to a slimbridge. + + At some point we should allow slimbridges (slivers?) throughout object + memory, and use them to + provide object alignment by slimbridges (slivers?) padding up to the + following (aligned) object. + */ + + /* Spur64BitMemoryManager>>#objectAfterMaybeSlimBridge:limit: */ +static NoDbgRegParms sqInt +objectAfterMaybeSlimBridgelimit(sqInt objOop, sqInt limit) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, limit)) { + return limit; + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); +} + + +/* Answer the C double precision floating point value of the argument, a + SmallFloat. See section 61-bit Immediate Floats in the SpurMemoryManager + class comment. + msb lsb + Decode: [8expsubset][52mantissa][1s][3tags] + shift away tags: [ 000 ][8expsubset][52mantissa][1s] + add exponent offset: [ 11 exponent ][52mantissa][1s] + rot sign: [1s][ 11 exponent ][52mantissa] */ + + /* Spur64BitMemoryManager>>#smallFloatValueOf: */ +static NoDbgRegParms double +smallFloatValueOf(sqInt oop) +{ + usqLong bits; + double value; + + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + return value; +} + + +/* Answer the total number of bytes in an object without an overflow header, + including header bytes. + */ + + /* Spur64BitMemoryManager>>#smallObjectBytesForSlots: */ +usqInt +smallObjectBytesForSlots(sqInt numSlots) +{ + return BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); +} + + /* Spur64BitMemoryManager>>#wordIndexableFormat */ +static sqInt +wordIndexableFormat(void) +{ + return sixtyFourBitIndexableFormat(); +} + + /* Spur64BitMemoryManager>>#wordSizeClassIndexPun */ +static sqInt +wordSizeClassIndexPun(void) +{ + return sixtyFourBitLongsClassIndexPun(); +} + + +/* ephemeronCorpse is the corpse of an ephemeron that was copied and + forwarded. Later on its surviving copy must be scanned to nil weak + references. Thread the corpse onto the weakList. Later, the weakList can + be followed, and + the forwarding pointer followed to locate the survivor. */ +/* Should be too infrequent to lower icache density of copyAndForward: */ + + /* SpurGenerationScavenger>>#addToEphemeronList: */ +static NoDbgRegParms NeverInline void +addToEphemeronList(sqInt ephemeronCorpse) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ephemeronListOffset; + + assert(isYetToBeScavenged(keyOfEphemeron(followForwarded(ephemeronCorpse)))); + ephemeronListOffset = (!(GIV(ephemeronList)) + ? 0 + : GIV(ephemeronList)); + + /* begin setCorpseOffsetOf:to: */ + assert(isYoung(ephemeronCorpse)); + assert(isForwarded(ephemeronCorpse)); + long64Atput((void *)(ephemeronCorpse),headerForSlotshashformatclassIndex(byteAt((void *)(ephemeronCorpse + (numSlotsFieldByteOffset()))), ((usqInt)(ephemeronListOffset)) >> 8, ephemeronListOffset & 0xFF, isForwardedObjectClassIndexPun())); + GIV(ephemeronList) = (((usqInt)((ephemeronCorpse - GIV(newSpaceStart)))) >> 3 /* shiftForAllocationUnit */) + 1; + assert((firstCorpse(GIV(ephemeronList))) == ephemeronCorpse); +} + + +/* weakCorpse is the corpse of a weak array that was copied and forwarded. + Later on its surviving copy must be scanned to nil weak references. + Thread the corpse onto the weakList. Later, the weakList can be followed, + and the forwarding pointer followed to locate the survivor. */ +/* Should be too infrequent to lower icache density of copyAndForward: */ + + /* SpurGenerationScavenger>>#addToWeakList: */ +static NoDbgRegParms NeverInline void +addToWeakList(sqInt weakCorpse) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt weakListOffset; + + weakListOffset = (!(GIV(weakList)) + ? 0 + : GIV(weakList)); + + /* begin setCorpseOffsetOf:to: */ + assert(isYoung(weakCorpse)); + assert(isForwarded(weakCorpse)); + long64Atput((void *)(weakCorpse),headerForSlotshashformatclassIndex(byteAt((void *)(weakCorpse + (numSlotsFieldByteOffset()))), ((usqInt)(weakListOffset)) >> 8, weakListOffset & 0xFF, isForwardedObjectClassIndexPun())); + GIV(weakList) = (((usqInt)((weakCorpse - GIV(newSpaceStart)))) >> 3 /* shiftForAllocationUnit */) + 1; + assert((firstCorpse(GIV(weakList))) == weakCorpse); +} + + /* SpurGenerationScavenger>>#allNewSpaceObjectsHaveZeroRTRefCount */ +static sqInt +allNewSpaceObjectsHaveZeroRTRefCount(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + /* begin allNewSpaceObjectsDo: */ + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l2; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOopSqInt, GIV(freeStart))) { + assert(isBridgeOrEnumerableObjectNoAssert(objOopSqInt)); + if ((((usqInt)((byteAt((void *)(objOopSqInt + (formatFieldByteOffset())))))) >> (rememberedBitByteShift())) > 0) { + return 0; + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOopSqInt = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + /* end allNewSpaceEntitiesDo: */ +l2: + return 1; +} + + /* SpurGenerationScavenger>>#allWeakSurvivorsOnWeakList */ +static sqInt +allWeakSurvivorsOnWeakList(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + usqInt corpse; + sqInt followingWord; + usqInt followingWordAddress; + sqInt format; + sqInt limit; + sqInt listOffset; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + + /* begin allFutureSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + address = (GIV(futureSpace).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + limit = GIV(futureSurvivorStart); + while (oopisLessThan(objOop, limit)) { + /* begin isWeakNonImm: */ + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (format == (weakArrayFormat())) { + /* begin is:onWeaklingList: */ + corpse = ((((usqInt)((GIV(weakList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + while (corpse) { + if (objOop == (followForwarded(corpse))) { + goto l1; + } + + /* begin nextCorpseOrNil: */ + assert(isYoung(corpse)); + listOffset = ((((usqInt)(((long32At((void *)(corpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(corpse + (formatFieldByteOffset())))); + corpse = ((sqInt) ((listOffset + ? ((((usqInt)((listOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0))); + } + return 0; + return 0; +l1:; + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, limit)) { + objOop = limit; + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l2:; + } + return 1; +} + + +/* Some time in every scavenger's life there may come a time when someone + writes code that stresses + the remembered table. One might conclude that if the remembered table is + full, then the right thing + to do is simply to tenure everything, emptying the remembered table. But + in some circumstances this + can be counter-productive, and result in the same situation arising soon + after tenuring everything. + Instead, we can try and selectively prune the remembered table, tenuring + only those objects that + are referenced by many objects in the remembered table. That's what this + algorithm does. It + reference counts young objects referenced from the remembered set, and + then sets a threshold + used to tenure objects oft referenced from the remembered set, thereby + allowing the remembered + set to shrink, while not tenuring everything. + + Once in a network monitoring application in a galaxy not dissimilar from + the one this code inhabits, + a tree of nodes referring to large integers was in precisely this + situation. The nodes were old, and + the integers were in new space. Some of the nodes referred to shared + numbers, some their own + unique numbers. The numbers were updated frequently. Were new space simply + tenured when the + remembered table was full, the remembered table would soon fill up as new + numbers were computed. + Only by selectively pruning the remembered table of nodes that shared + data, was a balance achieved + whereby the remembered table population was kept small, and tenuring rates + were low. */ + + /* SpurGenerationScavenger>>#computeRefCountToShrinkRT */ +static NeverInline void +computeRefCountToShrinkRT(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt count; + sqInt elephant; + sqInt entirePopulation; + int i; + sqInt iSqInt; + sqInt j; + long population[MaxRTRefCount + 1]; + sqInt refCount; + sqInt referent; + sqInt referentSqInt; + sqInt toDoLimit; + + memset(population, 0, (sizeof(long)) * (MaxRTRefCount + 1)); + assert(allNewSpaceObjectsHaveZeroRTRefCount()); + + /* begin referenceCountRememberedReferents: */ + for (iSqInt = 0; iSqInt < GIV(rememberedSetSize); iSqInt += 1) { + elephant = GIV(rememberedSet)[iSqInt]; + if ((!((longAt((void *)(elephant))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(elephant)); + referentSqInt = longAt((void *)((elephant + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + elephant = referentSqInt; + if (((elephant & (tagMask())) != 0)) { + elephant = GIV(nilObj); + } + + /* take care if elephant forwarded to an immediate */ + GIV(rememberedSet)[iSqInt] = elephant; + } + toDoLimit = (numPointerSlotsOf(elephant)) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + referent = longAt((void *)((elephant + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (/* isReallyYoung: */ + ((!(referent & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)), + (oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))))) { + refCount = ((usqInt)((byteAt((void *)(referent + (formatFieldByteOffset())))))) >> (rememberedBitByteShift()); + if (refCount < MaxRTRefCount) { + if (refCount > 0) { + population[refCount] = ((population[refCount]) - 1); + } + refCount += 1; + + /* begin rtRefCountOf:put: */ + assert(isYoungObject(referent)); + byteAtput((void *)(referent + (formatFieldByteOffset())),((byteAt((void *)(referent + (formatFieldByteOffset())))) & (formatMask())) + ((((usqInt)(refCount) << (rememberedBitByteShift()))))); + population[refCount] = ((population[refCount]) + 1); + } + } + } + } + + /* begin setRefCountToShrinkRT: */ + assert((population[0]) == 0); + entirePopulation = 0; + for (j = 1; j <= MaxRTRefCount; j += 1) { + entirePopulation += population[j]; + } + count = 0; + i = MaxRTRefCount + 1; + while ((count < (entirePopulation / 2)) + && (((i -= 1)) >= 0)) { + count += population[i]; + } + GIV(refCountToShrinkRT) = ((i < 0) ? 0 : i); +} + + +/* A special version of copyAndForward: for objects in the mournQueue. If + we're in the good times tenuring regime then copy to futureSpace, + otherwise tenure. + Also, don't repeat any of the ephemeron processing. */ + + /* SpurGenerationScavenger>>#copyAndForwardMourner: */ +static NoDbgRegParms sqInt +copyAndForwardMourner(sqInt mourner) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytesInObj; + sqInt format; + sqInt newLocation; + sqInt newStart; + sqInt objOop; + usqInt startOfSurvivor; + int tenure; + + assert((isInEden(mourner)) + || (isInPastSpace(mourner))); + + /* cog methods should be excluded. */ + bytesInObj = bytesInBody(mourner); + format = (byteAt((void *)(mourner + (formatFieldByteOffset())))) & (formatMask()); + + /* Allow Slang to inline. */ + tenure = !((GIV(tenureCriterion) == TenureByAge) + && (GIV(tenureThreshold) == 0)); + if (tenure + || ((GIV(futureSurvivorStart) + bytesInObj) > ((GIV(futureSpace).limit)))) { + newLocation = copyToOldSpacebytesformat(mourner, bytesInObj, format); + } + else { + /* begin copyToFutureSpace:bytes: */ + /* we hope writes are cheap... */ + GIV(statSurvivorCount) += 1; + assert((GIV(futureSurvivorStart) + bytesInObj) <= ((GIV(futureSpace).limit))); + startOfSurvivor = /* startOfObject: */ + ((byteAt((void *)(mourner + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? mourner - BaseHeaderSize + : mourner); + newStart = GIV(futureSurvivorStart); + GIV(futureSurvivorStart) += bytesInObj; + memcpy(((void *)newStart), ((void *)startOfSurvivor), bytesInObj); + if (GIV(tenureCriterion) == TenureToShrinkRT) { + objOop = newStart + (mourner - startOfSurvivor); + + /* begin rtRefCountOf:put: */ + assert(isYoungObject(objOop)); + byteAtput((void *)(objOop + (formatFieldByteOffset())),((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) + (0U << (rememberedBitByteShift()))); + } + newLocation = newStart + (mourner - startOfSurvivor); + } + + /* begin forwardSurvivor:to: */ + assert(isInNewSpace(mourner)); + assert((isInFutureSpace(newLocation)) + || (isInOldSpace(newLocation))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(mourner)) + && (!(isForwarded(mourner)))); + assert(validStorePointerUncheckedArgs(0, mourner, newLocation)); + longAtput((void *)((mourner + BaseHeaderSize) + (0U << (shiftForWord()))),newLocation); + + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(mourner),((longAt((void *)(mourner))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); + return newLocation; +} + + +/* copyAndForward: survivor copies a survivor object either to + futureSurvivorSpace or, if it is to be promoted, to oldSpace. + It leaves a forwarding pointer behind. If the object is weak + then corpse is threaded onto the weakList for later treatment. */ + + /* SpurGenerationScavenger>>#copyAndForward: */ +static NoDbgRegParms sqInt +copyAndForward(sqInt survivor) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytesInObj; + sqInt format; + sqInt newLocation; + sqInt newStart; + sqInt objOop; + usqInt startOfSurvivor; + + assert((isInEden(survivor)) + || (isInPastSpace(survivor))); + + /* cog methods should be excluded. */ + bytesInObj = bytesInBody(survivor); + format = (byteAt((void *)(survivor + (formatFieldByteOffset())))) & (formatMask()); + if (((GIV(futureSurvivorStart) + bytesInObj) > ((GIV(futureSpace).limit))) + || (/* shouldBeTenured: */ + (GIV(tenureCriterion) == TenureByAge + ? oopisLessThan(survivor, GIV(tenureThreshold)) + : (GIV(tenureCriterion) == TenureToShrinkRT + ? (((usqInt)((byteAt((void *)(survivor + (formatFieldByteOffset())))))) >> (rememberedBitByteShift())) >= GIV(refCountToShrinkRT) + : (GIV(tenureCriterion) == TenureByClass + ? ((longAt((void *)(survivor))) & (classIndexMask())) == GIV(tenuringClassIndex) + : 0))))) { + newLocation = copyToOldSpacebytesformat(survivor, bytesInObj, format); + } + else { + /* begin copyToFutureSpace:bytes: */ + /* we hope writes are cheap... */ + GIV(statSurvivorCount) += 1; + assert((GIV(futureSurvivorStart) + bytesInObj) <= ((GIV(futureSpace).limit))); + startOfSurvivor = /* startOfObject: */ + ((byteAt((void *)(survivor + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? survivor - BaseHeaderSize + : survivor); + newStart = GIV(futureSurvivorStart); + GIV(futureSurvivorStart) += bytesInObj; + memcpy(((void *)newStart), ((void *)startOfSurvivor), bytesInObj); + if (GIV(tenureCriterion) == TenureToShrinkRT) { + objOop = newStart + (survivor - startOfSurvivor); + + /* begin rtRefCountOf:put: */ + assert(isYoungObject(objOop)); + byteAtput((void *)(objOop + (formatFieldByteOffset())),((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) + (0U << (rememberedBitByteShift()))); + } + newLocation = newStart + (survivor - startOfSurvivor); + } + + /* begin forwardSurvivor:to: */ + assert(isInNewSpace(survivor)); + assert((isInFutureSpace(newLocation)) + || (isInOldSpace(newLocation))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(survivor)) + && (!(isForwarded(survivor)))); + assert(validStorePointerUncheckedArgs(0, survivor, newLocation)); + longAtput((void *)((survivor + BaseHeaderSize) + (0U << (shiftForWord()))),newLocation); + + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(survivor),((longAt((void *)(survivor))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); + + /* if weak or ephemeron add to the relevant list for subsequent scanning. */ + if (((format >= (weakArrayFormat())) && (format <= (ephemeronFormat())))) { + if (format == (weakArrayFormat())) { + addToWeakList(survivor); + } + else { + assert(isEphemeronFormat(format)); + if (isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(newLocation)) + && (isObjEphemeron(newLocation))), + /* fetchPointer:ofObject: */ + longAt((void *)((newLocation + BaseHeaderSize) + (0U << (shiftForWord()))))))) { + addToEphemeronList(survivor); + } + } + } + return newLocation; +} + + +/* Copy survivor to oldSpace. Answer the new oop of the object. */ +/* Should be too infrequent to lower icache density of copyAndForward: */ + + /* SpurGenerationScavenger>>#copyToOldSpace:bytes:format: */ +static NoDbgRegParms NeverInline sqInt +copyToOldSpacebytesformat(sqInt survivor, sqInt bytesInObject, sqInt formatOfSurvivor) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt field; + sqInt newOop; + sqInt newStart; + sqInt nTenures; + sqInt p; + usqInt startOfSurvivor; + sqInt toDoLimit; + + assert((formatOfSurvivor == (formatOf(survivor))) + && (((!(isMarked(survivor))) + || (GIV(tenureCriterion) == MarkOnTenure)) + && ((GIV(tenureCriterion) == TenureToShrinkRT) + || ((!(isPinned(survivor))) + && (!(isRemembered(survivor))))))); + nTenures = GIV(statTenures); + startOfSurvivor = /* startOfObject: */ + ((byteAt((void *)(survivor + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? survivor - BaseHeaderSize + : survivor); + newStart = allocateOldSpaceChunkOfBytes(bytesInObject); + if (!newStart) { + growOldSpaceByAtLeast(0); + newStart = allocateOldSpaceChunkOfBytes(bytesInObject); + if (!newStart) { + error("out of memory"); + } + } + + /* manager checkFreeSpace. */ + memcpy(((void *)newStart), ((void *)startOfSurvivor), bytesInObject); + newOop = newStart + (survivor - startOfSurvivor); + if (GIV(tenureCriterion) >= (((TenureToShrinkRT < MarkOnTenure) ? TenureToShrinkRT : MarkOnTenure))) { + if (GIV(tenureCriterion) == TenureToShrinkRT) { + /* begin rtRefCountOf:put: */ + assert(isYoungObject(newOop)); + byteAtput((void *)(newOop + (formatFieldByteOffset())),((byteAt((void *)(newOop + (formatFieldByteOffset())))) & (formatMask())) + (0U << (rememberedBitByteShift()))); + } + if (GIV(tenureCriterion) == MarkOnTenure) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(newOop))); + byteAtput((void *)(newOop + (markBitsByteOffset())),(byteAt((void *)(newOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + } + } + GIV(statTenures) = nTenures + 1; + if (/* isAnyPointerFormat: */ + (formatOfSurvivor <= 5 /* lastPointerFormat */) + || (formatOfSurvivor >= (firstCompiledMethodFormat()))) { + /* A very quick and dirty scan to find young referents. If we misidentify bytes + in a CompiledMethod as young we don't care; it's unlikely, and a subsequent + scan of the rt will filter the object out. But it's good to filter here because + otherwise an attempt to shrink the RT may simply fill it up with new objects, + and here the data is likely in the cache. */ + toDoLimit = (bytesInObject - (survivor - startOfSurvivor)) - BytesPerWord; + for (p = BaseHeaderSize; p <= toDoLimit; p += BytesPerWord) { + field = longAt((void *)(survivor + p)); + if (/* isReallyYoung: */ + ((!(field & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(field)), + (oopisLessThan(field, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(field, GIV(newSpaceStart)))))) { + remember(newOop); + return newOop; + } + } + } + return newOop; +} + + +/* Use the identityHash and format-containing byte to construct a 30 bit + offset through non-future newSpace and use this to implement lists for + weak array + and ephemeron processing. 30 bits of 8 byte allocationUnits units is 2 ^ + 33 bytes, or 8Gb, big enough for newSpace for a good few years yet. */ +/* debugging */ + + /* SpurGenerationScavenger>>#corpseForCorpseOffset: */ +usqInt +corpseForCorpseOffset(sqInt corpseOffset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((((usqInt)((corpseOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); +} + + /* SpurGenerationScavenger>>#firstCorpse: */ +static NoDbgRegParms usqInt +firstCorpse(sqInt headOfCorpseList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((((usqInt)((headOfCorpseList - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); +} + + /* SpurGenerationScavenger>>#growRememberedSet */ +static void +growRememberedSet(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt *base; + sqInt fudge; + sqInt i; + sqInt newObj; + sqInt nSlots; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt obj; + usqInt p; + usqInt toDoLimit; + + + /* Don't ruin locality in remember: */ + obj = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))); + + /* begin numSlotsOf: */ + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + assert(numSlots >= 0x400); + nSlots = numSlots * 2; + + /* begin allocatePinnedSlots: */ + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(nSlots, /* objectBytesForSlots: */ + (nSlots + ? ((((usqInt)(nSlots) << (shiftForWord())))) + ((nSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (newObj) { + assert(isPinned(newObj)); + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + if (!newObj) { + nSlots = numSlots + 0x400; + + /* begin allocatePinnedSlots: */ + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(nSlots, /* objectBytesForSlots: */ + (nSlots + ? ((((usqInt)(nSlots) << (shiftForWord())))) + ((nSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (newObj) { + assert(isPinned(newObj)); + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + if (!newObj) { + if (!(growOldSpaceByAtLeast((numSlots + 0x400) * BytesPerOop))) { + error("could not grow remembered set"); + } + nSlots = numSlots + 0x400; + + /* begin allocatePinnedSlots: */ + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(nSlots, /* objectBytesForSlots: */ + (nSlots + ? ((((usqInt)(nSlots) << (shiftForWord())))) + ((nSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (newObj) { + assert(isPinned(newObj)); + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + } + } + + /* begin rememberedSetObj: */ + assert(isOldObject(newObj)); + assert((isNonImmediate(GIV(hiddenRootsObj))) + && (!(isForwarded(GIV(hiddenRootsObj))))); + assert(validStorePointerUncheckedArgs(RememberedSetRootIndex, GIV(hiddenRootsObj), newObj)); + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord()))))),newObj); + base = firstIndexableField(newObj); + for (i = 0; i < GIV(rememberedSetSize); i += 1) { + base[i] = (GIV(rememberedSet)[i]); + } + + /* if growing in the middle of a GC, need to preserve marked status. */ + if ((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(newObj))); + byteAtput((void *)(newObj + (markBitsByteOffset())),(byteAt((void *)(newObj + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(obj))); + byteAtput((void *)(obj + (markBitsByteOffset())),(byteAt((void *)(obj + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + freeObject(obj); + GIV(rememberedSet) = base; + + /* begin numSlotsOf: */ + assert((classIndexOf(newObj)) > (isForwardedObjectClassIndexPun())); + GIV(rememberedSetLimit) = (((numSlotsUsqInt = byteAt((void *)(newObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(newObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + + /* begin setRememberedSetRedZone */ + fudge = ((((GIV(eden).limit)) - ((GIV(eden).start))) / BytesPerWord) / 0x400; + GIV(rememberedSetRedZone) = ((((GIV(rememberedSetLimit) * 3) / 4) < fudge) ? fudge : ((GIV(rememberedSetLimit) * 3) / 4)); +} + + /* SpurGenerationScavenger>>#isInRememberedSet: */ +static NoDbgRegParms sqInt +isInRememberedSet(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + for (i = 0; i < GIV(rememberedSetSize); i += 1) { + if ((GIV(rememberedSet)[i]) == objOop) { + return 1; + } + } + return 0; +} + + +/* Answer whether the oop has survived a scavenge. This version is + for processing weak survivors and must cope with the scavenge in + freeUnmarkedObjectsAndSortAndCoalesceFreeSpaceForPigCompact. */ + + /* SpurGenerationScavenger>>#isMaybeOldScavengeSurvivor: */ +static NoDbgRegParms sqInt +isMaybeOldScavengeSurvivor(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt target; + + if (((oop & (tagMask())) != 0)) { + return 1; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + target = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(target & (tagMask())))) + && ((!((longAt((void *)(target))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + target = longAt((void *)((target + BaseHeaderSize) + (0U << (shiftForWord())))); + } + if (((oop & (tagMask())) != 0)) { + return 1; + } + } + else { + target = oop; + } + return (oopisGreaterThanOrEqualTo(target, GIV(oldSpaceStart)) + ? (GIV(tenureCriterion) != MarkOnTenure) + || (((byteAt((void *)(target + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + : oopisGreaterThanOrEqualToandLessThan(target, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))); +} + + +/* Answer whether the oop has survived a scavenge. This is equivalent to + | target | + (manager isImmediate: oop) ifTrue: + [^true]. + target := (manager isForwarded: oop) + ifTrue: [manager followForwarded: oop] + ifFalse: [oop]. + ^((manager isInEden: target) + or: [(manager isInPastSpace: target)]) not */ + + /* SpurGenerationScavenger>>#isScavengeSurvivor: */ +static NoDbgRegParms sqInt +isScavengeSurvivor(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt target; + + if (((oop & (tagMask())) != 0)) { + return 1; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + target = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(target & (tagMask())))) + && ((!((longAt((void *)(target))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + target = longAt((void *)((target + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + else { + target = oop; + } + return (!(/* isReallyYoung: */ + ((!(target & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(target)), + (oopisLessThan(target, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(target, GIV(newSpaceStart))))))) + || (oopisGreaterThanOrEqualToandLessThan(target, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))); +} + + +/* Answer whether the oop is in eden or pastSpace and yet-to-be scavenged. */ + + /* SpurGenerationScavenger>>#isYetToBeScavenged: */ +static NoDbgRegParms sqInt +isYetToBeScavenged(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt target; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + target = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(target & (tagMask())))) + && ((!((longAt((void *)(target))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + target = longAt((void *)((target + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + else { + target = oop; + } + return (/* isReallyYoung: */ + ((!(target & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(target)), + (oopisLessThan(target, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(target, GIV(newSpaceStart)))))) + && (!(oopisGreaterThanOrEqualToandLessThan(target, ((GIV(futureSpace)).start), GIV(futureSurvivorStart)))); +} + + /* SpurGenerationScavenger>>#newSpaceCapacity */ +static usqInt +newSpaceCapacity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((GIV(eden).limit)) - (((((GIV(futureSpace).start)) < ((GIV(pastSpace).start))) ? ((GIV(futureSpace).start)) : ((GIV(pastSpace).start)))); +} + + +/* corpse is the corpse of a weak array that has been added to the weakList. + Answer the next object on the list, or nil if none. */ + + /* SpurGenerationScavenger>>#nextCorpseOrNil: */ +static NoDbgRegParms sqInt +nextCorpseOrNil(sqInt corpse) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt listOffset; + + assert(isYoung(corpse)); + listOffset = ((((usqInt)(((long32At((void *)(corpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(corpse + (formatFieldByteOffset())))); + return (listOffset + ? ((((usqInt)((listOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0); +} + + /* SpurGenerationScavenger>>#openScavengeLog */ +void +openScavengeLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(scavengeLog) = fopen("scavenge.log", "a+"); +} + + +/* Print the objects on the ephemeronList, indicating active or inactive. */ + + /* SpurGenerationScavenger>>#printEphemeronList */ +void +printEphemeronList(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt corpse; + sqInt ephemeron; + + corpse = ((((usqInt)((GIV(ephemeronList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + if (!corpse) { + print("empty"); + cr(); + return; + } + while (corpse) { + ephemeron = followForwarded(corpse); + fprintf(GIV(transcript), + "%x -> %p %s\n", + ((int) corpse), + ((void *)ephemeron), + (isYetToBeScavenged(keyOfMaybeFiredEphemeron(ephemeron)) + ? "active" + : "inactive")); + corpse = nextCorpseOrNil(corpse); + } +} + + +/* Print the objects in the remembered set. */ +/* useful for debugging */ + + /* SpurGenerationScavenger>>#printRememberedSet */ +void +printRememberedSet(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + for (i = 0; i < GIV(rememberedSetSize); i += 1) { + printNum(i); + + /* begin space */ + printChar(' '); + shortPrintOop(GIV(rememberedSet)[i]); + } +} + + +/* There are ephemerons to be scavenged, and maybe some to be fired. We can't + fire any + ephemerons until we have reached the fixed point of scavenging all + inactive ephemerons, + and removing them from the remembered set and/or the list. Once at that + fixed point the + rememberedTable has only unfired ephemerons in its numRememberedEphemerons + partition, and the ephemeronList is either empty or contains only firable + ephemerons. At this fixed point + fire the remaining ephemerons and then scavenge the keys of the ephemerons + in the + remembered set and the contents of the ephemeronList. Answer if any were + fired. + Since more that one ephemeron can refer to the same key, and firability + for a key is + determined by that key not (yet) having survived the scavenge, we must + first fire + ephemerons before scavenging their keys, since the first ephemeron that + scavenges its key will make all other ephemerons on that key appear to be + unfirable. + Read the class comment for a more in-depth description of the algorithm. */ + + /* SpurGenerationScavenger>>#processEphemerons */ +static sqInt +processEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt corpseOffset; + sqInt ephemeron; + usqInt ephemeronCorpse; + sqInt firedIRS; + sqInt firedOEL; + sqInt foundNewReferent; + sqInt i; + sqInt key; + sqInt listOffset; + usqInt numSlots; + sqInt offsetToNextCorpse; + sqInt referent; + sqInt referentSqInt; + sqInt scavengedUnfirable; + sqInt toDoLimit; + + scavengedUnfirable = 0; + if (scavengeInactiveEphemeronsInRememberedSet()) { + scavengedUnfirable = 1; + } + assert(validRememberedSetPartitionPostSIEIRS()); + if ((scavengeInactiveEphemeronsOnEphemeronList()) + || (scavengedUnfirable)) { + return 0; + } + assert(wholeEphemeronListIsFirable()); + + /* begin fireEphemeronsInRememberedSet */ + assert(!((unfiredEphemeronsAtEndOfRememberedSet()))); + if (!GIV(numRememberedEphemerons)) { + firedIRS = 0; + goto l1; + } + i = 0; + while (i < GIV(numRememberedEphemerons)) { + ephemeron = GIV(rememberedSet)[i]; + assert(isEphemeron(ephemeron)); + + /* begin keyOfEphemeron: */ + assert((isNonImmediate(ephemeron)) + && (isObjEphemeron(ephemeron))); + key = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isYetToBeScavenged(key)); + fireEphemeron(ephemeron); + i += 1; + } + + /* If any ephemerons were fired then processEphemerons is expected to + scavenge them in scavengeUnfiredEphemeronsInRememberedSet */ + firedIRS = 1; + /* end fireEphemeronsInRememberedSet */ +l1: + + /* begin fireEphemeronsOnEphemeronList */ + if (!GIV(ephemeronList)) { + firedOEL = 0; + goto l2; + } + ephemeronCorpse = ((((usqInt)((GIV(ephemeronList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + while (ephemeronCorpse) { + assert((isYoung(ephemeronCorpse)) + && (isForwarded(ephemeronCorpse))); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(ephemeronCorpse)); + ephemeron = longAt((void *)((ephemeronCorpse + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(ephemeron & (tagMask())))) + && ((!((longAt((void *)(ephemeron))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ephemeron = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + } + + /* begin keyOfMaybeFiredEphemeron: */ + assert(isMaybeFiredEphemeron(ephemeron)); + key = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isYetToBeScavenged(key)); + fireEphemeron(ephemeron); + + /* begin nextCorpseOrNil: */ + assert(isYoung(ephemeronCorpse)); + listOffset = ((((usqInt)(((long32At((void *)(ephemeronCorpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(ephemeronCorpse + (formatFieldByteOffset())))); + ephemeronCorpse = ((sqInt) ((listOffset + ? ((((usqInt)((listOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0))); + } + firedOEL = 1; + /* end fireEphemeronsOnEphemeronList */ +l2: + if (firedIRS + || (GIV(numRememberedEphemerons) > 0)) { + /* begin scavengeEphemeronsInRememberedSet */ + assert(!((unfiredEphemeronsAtEndOfRememberedSet()))); + i = 0; + while (i < GIV(numRememberedEphemerons)) { + ephemeron = GIV(rememberedSet)[i]; + assert((isEphemeron(ephemeron)) + || (isonObjStack(ephemeron, GIV(mournQueue)))); + if (scavengeReferentsOfPointers(ephemeron)) { + i += 1; + } + else { + setIsRememberedOfto(ephemeron, 0); + + /* remove from set by overwriting with next-to-be scanned */ + GIV(numRememberedEphemerons) -= 1; + GIV(previousRememberedSetSize) -= 1; + GIV(rememberedSetSize) -= 1; + + /* First overwrite with last firable ephemeron (could be a noop if this is the last one). + Then overwrite last firable entry with next unscanned rememberedSet entry (could also be a noop). + Then overwrite next unscanned entry with last unscanned rememberedSet entry (could also be a noop). */ + GIV(rememberedSet)[i] = (GIV(rememberedSet)[GIV(numRememberedEphemerons)]); + GIV(rememberedSet)[GIV(numRememberedEphemerons)] = (GIV(rememberedSet)[GIV(previousRememberedSetSize)]); + GIV(rememberedSet)[GIV(previousRememberedSetSize)] = (GIV(rememberedSet)[GIV(rememberedSetSize)]); + } + } + + /* no more firable ephemerons in this cycle. + scavengeRememberedSetStartingAt: may find new ones. */ + GIV(numRememberedEphemerons) = 0; + } + + /* begin scavengeEphemeronsOnEphemeronList */ + if (!((corpseOffset = GIV(ephemeronList)))) { + goto l3; + } + GIV(ephemeronList) = null; + while (corpseOffset != 0) { + ephemeronCorpse = ((((usqInt)((corpseOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + assert(isForwarded(ephemeronCorpse)); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(ephemeronCorpse)); + ephemeron = longAt((void *)((ephemeronCorpse + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(ephemeron & (tagMask())))) + && ((!((longAt((void *)(ephemeron))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ephemeron = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + } + offsetToNextCorpse = ((((usqInt)(((long32At((void *)(ephemeronCorpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(ephemeronCorpse + (formatFieldByteOffset())))); + + /* begin scavengeReferentsOfPointers: */ + foundNewReferent = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(ephemeron)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(ephemeron + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(ephemeron - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((ephemeron + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((ephemeron + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + corpseOffset = offsetToNextCorpse; + } + /* end scavengeEphemeronsOnEphemeronList */ +l3: + assert(GIV(numRememberedEphemerons) == 0); + assert(!GIV(ephemeronList)); + return firedIRS + || (firedOEL); +} + + +/* Go through the remembered set and the weak list, nilling references to + any objects that didn't survive the scavenge. Read the class comment + for a more in-depth description of the algorithm. */ + + /* SpurGenerationScavenger>>#processWeaklings */ +static void +processWeaklings(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt i; + sqInt listOffset; + sqInt rootObj; + usqInt weakCorpse; + sqInt weakObj; + + assert(allWeakSurvivorsOnWeakList()); + i = 0; + while (i < GIV(rememberedSetSize)) { + rootObj = GIV(rememberedSet)[i]; + + /* begin isWeakNonImm: */ + format = (byteAt((void *)(rootObj + (formatFieldByteOffset())))) & (formatMask()); + if (format == (weakArrayFormat())) { + /* If no more referents, remove by overwriting with the last element in the set. */ + if (processWeakSurvivor(rootObj)) { + i += 1; + } + else { + setIsRememberedOfto(rootObj, 0); + if ((i + 1) < GIV(rememberedSetSize)) { + GIV(rememberedSet)[i] = (GIV(rememberedSet)[GIV(rememberedSetSize) - 1]); + } + GIV(rememberedSetSize) -= 1; + } + } + else { + i += 1; + } + } + if (GIV(weakList)) { + weakCorpse = ((((usqInt)((GIV(weakList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + while (weakCorpse) { + assert(isForwarded(weakCorpse)); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(weakCorpse)); + weakObj = longAt((void *)((weakCorpse + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(weakObj & (tagMask())))) + && ((!((longAt((void *)(weakObj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + weakObj = longAt((void *)((weakObj + BaseHeaderSize) + (0U << (shiftForWord())))); + } + + /* weakObj may have been tenured... */ + if ((processWeakSurvivor(weakObj)) + && (((/* begin isOldObject: */ + assert(isNonImmediate(weakObj)), + oopisGreaterThanOrEqualTo(weakObj, GIV(oldSpaceStart)))) + && (!(((byteAt((void *)(weakObj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0)))) { + remember(weakObj); + } + + /* begin nextCorpseOrNil: */ + assert(isYoung(weakCorpse)); + listOffset = ((((usqInt)(((long32At((void *)(weakCorpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(weakCorpse + (formatFieldByteOffset())))); + weakCorpse = ((sqInt) ((listOffset + ? ((((usqInt)((listOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0))); + } + GIV(weakList) = null; + } +} + + +/* Process a weak survivor on the weakList. Those of its fields + which have not survived the scavenge should be nilled, and if any + are, the coInterpreter should be informed via fireFinalization:. + Answer if the weakObj has any young referents. */ + + /* SpurGenerationScavenger>>#processWeakSurvivor: */ +static NoDbgRegParms sqInt +processWeakSurvivor(sqInt weakObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classPointer; + sqInt hasYoungReferents; + sqInt i; + usqInt numSlots; + sqInt numStrongSlots; + sqInt referent; + sqInt referentSqInt; + sqInt toDoLimit; + sqInt weakObjShouldMourn; + + weakObjShouldMourn = (hasYoungReferents = 0); + + /* N.B. generateToByDoLimitExpression:negative:on: guards against (unsigned)0 - 1 going +ve */ + + /* begin numFixedSlotsOf: */ + classPointer = fetchClassOfNonImm(weakObj); + classFormat = ((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + numStrongSlots = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + for (i = 0; i < numStrongSlots; i += 1) { + referent = longAt((void *)((weakObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(referent & (tagMask())))) + && ((/* begin isYoungObject: */ + assert(isNonImmediate(referent)), + oopisLessThan(referent, GIV(oldSpaceStart))))) { + hasYoungReferents = 1; + } + } + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(weakObj)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(weakObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(weakObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = numStrongSlots; i <= toDoLimit; i += 1) { + referent = longAt((void *)((weakObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* Referent could be forwarded due to scavenging or a become:, don't assume. */ + if ((!(referent & (tagMask())))) { + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + + /* weakObj is either young or already in remembered table; no need to check */ + assert((isReallyYoungObject(weakObj)) + || (isRemembered(weakObj))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(weakObj)) + && (!(isForwarded(weakObj)))); + assert(validStorePointerUncheckedArgs(i, weakObj, referent)); + longAtput((void *)((weakObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + if (isMaybeOldScavengeSurvivor(referent)) { + /* begin isYoungObject: */ + assert(isNonImmediate(referent)); + if (oopisLessThan(referent, GIV(oldSpaceStart))) { + hasYoungReferents = 1; + } + } + else { + weakObjShouldMourn = 1; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(weakObj)) + && (!(isForwarded(weakObj)))); + assert(validStorePointerUncheckedArgs(i, weakObj, GIV(nilObj))); + longAtput((void *)((weakObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } + } + } + if (weakObjShouldMourn) { + /* begin fireFinalization: */ + if (((imageHeaderFlags & 64) != 0)) { + queueMourner(weakObj); + } + + /* begin signalFinalization: */ + forceInterruptCheck(); + GIV(pendingFinalizationSignals) += 1; + } + return hasYoungReferents; +} + + +/* Add the argument to the remembered set and set its isRemembered bit to + true. Answer the argument for the benefit of the Cogit. */ + + /* SpurGenerationScavenger>>#remember: */ +sqInt +remember(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(isNonImmediate(objOop)); + assert(!((isYoungObject(objOop)))); + assert(!((isRemembered(objOop)))); + assert(!((isInRememberedSet(objOop)))); + setIsRememberedOfto(objOop, 1); + if (GIV(rememberedSetSize) >= GIV(rememberedSetLimit)) { + growRememberedSet(); + } + GIV(rememberedSet)[GIV(rememberedSetSize)] = objOop; + if (((GIV(rememberedSetSize) += 1)) >= GIV(rememberedSetRedZone)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + return objOop; +} + + +/* scavengeFutureSurvivorSpaceStartingAt: does a depth-first traversal of the + new objects starting at the one at initialAddress in futureSurvivorSpace. */ + + /* SpurGenerationScavenger>>#scavengeFutureSurvivorSpaceStartingAt: */ +static NoDbgRegParms void +scavengeFutureSurvivorSpaceStartingAt(sqInt initialAddress) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + sqInt obj; + usqInt ptr; + + ptr = initialAddress; + while (ptr < GIV(futureSurvivorStart)) { + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(ptr + (numSlotsFieldByteOffset()))); + obj = (numSlots == (numSlotsMask()) + ? ptr + BaseHeaderSize + : ptr); + ptr = addressAfter(obj); + ((void) (scavengeReferentsOf(obj))); + } +} + + +/* There are ephemerons in the remembered set. Scavenge all with + scavenged/old keys, + leaving only firable/triggerable ephemerons. Answer if any + unfirable/inactive ephemerons + were scavenged. Answering false implies there are only firable ephemerons + (if any) within + the 0 to numRememberedEphemerons - 1 range. + Continue scavenging until at the fixed point of only having firable + ephemerons with still-to-be-scavenged keys in the numRememberedEphemerons + partition. */ + + /* SpurGenerationScavenger>>#scavengeInactiveEphemeronsInRememberedSet */ +static sqInt +scavengeInactiveEphemeronsInRememberedSet(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ephemeron; + sqInt i; + sqInt key; + sqInt numRememberedAtStartOfScan; + sqInt scavengedInactive; + + scavengedInactive = 0; + do { + numRememberedAtStartOfScan = GIV(numRememberedEphemerons); + i = 0; + while (i < GIV(numRememberedEphemerons)) { + ephemeron = GIV(rememberedSet)[i]; + assert((isEphemeron(ephemeron)) + || (isonObjStack(ephemeron, GIV(mournQueue)))); + + /* begin keyOfEphemeron: */ + assert((isNonImmediate(ephemeron)) + && (isObjEphemeron(ephemeron))); + key = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + if (isYetToBeScavenged(key)) { + i += 1; + } + else { + scavengedInactive = 1; + if (scavengeReferentsOfPointers(ephemeron)) { + if (i < (GIV(numRememberedEphemerons) - 1)) { + GIV(rememberedSet)[i] = (GIV(rememberedSet)[GIV(numRememberedEphemerons) - 1]); + GIV(rememberedSet)[GIV(numRememberedEphemerons) - 1] = ephemeron; + } + GIV(numRememberedEphemerons) -= 1; + } + else { + GIV(numRememberedEphemerons) -= 1; + GIV(previousRememberedSetSize) -= 1; + GIV(rememberedSetSize) -= 1; + + /* First overwrite with last firable ephemeron (could be a noop if this is the last one). + Then overwrite last firable entry with next unscanned rememberedSet entry (could also be a noop). + Then overwrite next unscanned entry with last unscanned rememberedSet entry (could also be a noop). */ + GIV(rememberedSet)[i] = (GIV(rememberedSet)[GIV(numRememberedEphemerons)]); + GIV(rememberedSet)[GIV(numRememberedEphemerons)] = (GIV(rememberedSet)[GIV(previousRememberedSetSize)]); + GIV(rememberedSet)[GIV(previousRememberedSetSize)] = (GIV(rememberedSet)[GIV(rememberedSetSize)]); + } + } + } + } while(!(GIV(numRememberedEphemerons) == numRememberedAtStartOfScan)); + + /* rememberedSet contains numRememberedEphemerons firable ephemerons in the rememberedSet. + These will be fired in processEphemerons fireEphemeronsInRememberedSet */ + return scavengedInactive; +} + + +/* Scavenge all untriggerable ephemerons on the ephemeronList and remove them + there-from. Answer if any were scavenged. */ + + /* SpurGenerationScavenger>>#scavengeInactiveEphemeronsOnEphemeronList */ +static sqInt +scavengeInactiveEphemeronsOnEphemeronList(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anyScavenged; + sqInt ephemeron; + usqInt ephemeronCorpse; + sqInt foundNewReferent; + sqInt i; + sqInt key; + sqInt nextCorpseOffset; + usqInt numSlots; + usqInt previousCorpse; + sqInt referent; + sqInt referentSqInt; + sqInt toDoLimit; + + if (!GIV(ephemeronList)) { + return 0; + } + anyScavenged = 0; + previousCorpse = null; + + /* In figuring out the termination condition I believe it is sufficient to consider four cases: + ephemeronList starts with + two firable corpses + two unfirable corpses + a firable corpse followed by an unfirable corpse + an unfirable corpse followed by a firable corpse */ + ephemeronCorpse = ((((usqInt)((GIV(ephemeronList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + while (ephemeronCorpse) { + assert((isYoung(ephemeronCorpse)) + && (isForwarded(ephemeronCorpse))); + nextCorpseOffset = ((((usqInt)(((long32At((void *)(ephemeronCorpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(ephemeronCorpse + (formatFieldByteOffset())))); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(ephemeronCorpse)); + ephemeron = longAt((void *)((ephemeronCorpse + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(ephemeron & (tagMask())))) + && ((!((longAt((void *)(ephemeron))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ephemeron = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + } + + /* begin keyOfMaybeFiredEphemeron: */ + assert(isMaybeFiredEphemeron(ephemeron)); + key = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + if (isYetToBeScavenged(key)) { + previousCorpse = ephemeronCorpse; + } + else { + anyScavenged = 1; + + /* begin scavengeReferentsOfPointers: */ + foundNewReferent = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(ephemeron)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(ephemeron + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(ephemeron - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((ephemeron + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((ephemeron + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + if (previousCorpse) { + /* begin setCorpseOffsetOf:to: */ + assert(isYoung(previousCorpse)); + assert(isForwarded(previousCorpse)); + long64Atput((void *)(previousCorpse),headerForSlotshashformatclassIndex(byteAt((void *)(previousCorpse + (numSlotsFieldByteOffset()))), ((usqInt)(nextCorpseOffset)) >> 8, nextCorpseOffset & 0xFF, isForwardedObjectClassIndexPun())); + } + else { + GIV(ephemeronList) = (nextCorpseOffset + ? nextCorpseOffset + : 0); + } + } + ephemeronCorpse = (nextCorpseOffset + ? ((((usqInt)((nextCorpseOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0); + } + if (previousCorpse) { + /* begin setCorpseOffsetOf:to: */ + assert(isYoung(previousCorpse)); + assert(isForwarded(previousCorpse)); + long64Atput((void *)(previousCorpse),headerForSlotshashformatclassIndex(byteAt((void *)(previousCorpse + (numSlotsFieldByteOffset()))), 0, 0, isForwardedObjectClassIndexPun())); + } + return anyScavenged; +} + + +/* This is the inner loop of the main routine, scavenge. It first scavenges + the new objects immediately + reachable from old ones and the stack. Then it scavenges those that are + transitively reachable. If any + results in a tenure, the tenuree gets remembered, and it first scavenges + objects adjacent to the tenure, + then scavenges the ones reachable from the tenuree. This loop continues + until no more reachable + objects are left. At that point, pastSurvivorSpace is exchanged with + futureSurvivorSpace. + Notice that each pointer in a live object is inspected once and only once. + The previousRememberedSetSize + and previousFutureSurvivorSpaceLimit variables ensure that no object is + scanned twice, as well as + detecting closure. If this were not true, some pointers might get + forwarded twice. + + An extension of the algorithm presented in David's original paper is to + handle weak arrays and ephemerons. + Weak arrays should not have their weak referents scavenged unless there + are strong references to them. + Ephemerons should fire if their key is not reachable other than from + ephemerons and weak arrays. + Handle this by maintaining a list for weak arrays and a list for + ephemerons, which allow scavenging these + objects once all other objects in new space have been scavenged, hence + allowing the scavenger to + detect which referents in new space of weak arrays are dead and of + ephemeron keys are only live due to + ephemerons. Read the class comment for a more in-depth description of the + algorithm. + Answer how many passes occurred. This is to use the value of p[ass which + is valuable for debugging. */ +/* debugging */ + + /* SpurGenerationScavenger>>#scavengeLoop */ +static sqInt +scavengeLoop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firedEphemerons; + sqInt firstTime; + sqInt i; + sqInt mournQueueSize; + sqInt newSize; + sqInt pass; + sqInt previousFutureSurvivorStart; + StackPage *thePage; + + mournQueueSize = 0; + assert(GIV(futureSurvivorStart) == ((GIV(futureSpace).start))); + GIV(weakList) = (GIV(ephemeronList) = null); + GIV(numRememberedEphemerons) = 0; + firstTime = 1; + GIV(previousRememberedSetSize) = 0; + previousFutureSurvivorStart = GIV(futureSurvivorStart); + firedEphemerons = 0; + pass = 0; + + /* begin initStackPageGC */ + if (GIV(stackPage)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + (thePage->trace = StackPageUnreached); + } + while (1) { + pass += 1; + scavengeRememberedSetStartingAt(GIV(previousRememberedSetSize)); + GIV(previousRememberedSetSize) = GIV(rememberedSetSize); + if (firstTime) { + mapInterpreterOops(); + mapMournQueue(); + mapExtraRoots(); + mournQueueSize = sizeOfObjStack(GIV(mournQueue)); + firstTime = 0; + } + + /* if nothing more copied and forwarded (or remembered by mapInterpreterOops et al) + to scavenge, and no ephemerons to process, the scavenge is done. */ + if ((GIV(previousRememberedSetSize) == GIV(rememberedSetSize)) + && ((previousFutureSurvivorStart == GIV(futureSurvivorStart)) + && ((!firedEphemerons) + && (!GIV(ephemeronList))))) { + assert(GIV(numRememberedEphemerons) == 0); + return pass; + } + scavengeFutureSurvivorSpaceStartingAt(previousFutureSurvivorStart); + previousFutureSurvivorStart = GIV(futureSurvivorStart); + + /* all reachable objects in this cycle have been promoted to futureSpace. + ephemerons can now be processed. */ + firedEphemerons = processEphemerons(); + if (((newSize = sizeOfObjStack(GIV(mournQueue)))) > mournQueueSize) { + mapMournQueue(); + mournQueueSize = newSize; + } + } + return pass; +} + + +/* Scavenge the referents of a normal pointer object. + Answer if it still refers to young objects. */ + + /* SpurGenerationScavenger>>#scavengeReferentsOfPointers: */ +static NoDbgRegParms sqInt +scavengeReferentsOfPointers(sqInt referrer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt foundNewReferent; + sqInt i; + usqInt numSlots; + sqInt referent; + sqInt referentSqInt; + sqInt toDoLimit; + + foundNewReferent = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(referrer)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(referrer + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(referrer - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + return foundNewReferent; +} + + +/* scavengeReferentsOf: referrer inspects relevant pointers in referrer. If + any are new objects, it has them moved to FutureSurvivorSpace, and + answers truth. If there are no new referents, it answers falsity. To + handle weak arrays, if the referrer is weak only scavenge strong slots and + answer true so that it won't be removed from the remembered set until + later. To handle ephemerons, only scavenge other slots if the key slot has + already been scavenged. */ +/* forwarding objects should be followed by callers, + unless the forwarder is a root in the remembered table. */ + + /* SpurGenerationScavenger>>#scavengeReferentsOf: */ +static NoDbgRegParms sqInt +scavengeReferentsOf(sqInt referrer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt foundNewReferent; + sqInt i; + usqInt numSlots; + sqInt referent; + sqInt referentSqInt; + sqInt toDoLimit; + + assert((!(isForwarded(referrer))) + || (isRemembered(referrer))); + assert((isInFutureSpace(referrer)) + || (isInOldSpace(referrer))); + switch ((byteAt((void *)(referrer + (formatFieldByteOffset())))) & (formatMask())) { + case nonIndexablePointerFormat(): + case arrayFormat(): + /* begin scavengeReferentsOfPointers: */ + foundNewReferent = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(referrer)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(referrer + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(referrer - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + return foundNewReferent; + + case indexablePointersFormat(): + /* begin scavengeReferentsOfIndexablePointers: */ + foundNewReferent = 0; + toDoLimit = (numSlotsOfIndexablePointerObj(referrer)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + return foundNewReferent; + + case weakArrayFormat(): + /* begin scavengeReferentsOfWeakling: */ + toDoLimit = (numStrongSlotsOfWeakling(referrer)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (!(oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart)))) { + referent = copyAndForward(referent); + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + + /* Answer true to keep any weak objects in the remembered set until later. */ + return 1; + + case ephemeronFormat(): + /* Scavenge the referents of an ephemeron. If the key + has not yet been scavenged, don't scavenge it yet. */ + if (isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(referrer)) + && (isObjEphemeron(referrer))), + /* fetchPointer:ofObject: */ + longAt((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord()))))))) { + return 1; + } + + /* begin scavengeReferentsOfPointers: */ + foundNewReferent = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(referrer)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(referrer + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(referrer - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + return foundNewReferent; + + case forwardedFormat(): + /* begin scavengeReferentOfForwarder: */ + foundNewReferent = 0; + referent = longAt((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord())))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord()))),referent); + } + return foundNewReferent; + + case firstCompiledMethodFormat(): + case (firstCompiledMethodFormat()) + 1: + case (firstCompiledMethodFormat()) + 2: + case (firstCompiledMethodFormat()) + 3: + case (firstCompiledMethodFormat()) + 4: + case (firstCompiledMethodFormat()) + 5: + case (firstCompiledMethodFormat()) + 6: + case (firstCompiledMethodFormat()) + 7: + /* begin scavengeReferentsOfCompiledCode: */ + foundNewReferent = 0; + toDoLimit = literalCountOf(referrer); + for (i = 1; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + return foundNewReferent; + + default: + assert(((numSlotsOf(referrer)) == 0) + || (isWordsOrBytesNonImm(referrer))); + } + return 0; +} + + +/* scavengeRememberedSetStartingAt: n traverses objects in the remembered + set starting at the nth one. If the object does not refer to any new + objects, it + is removed from the set. Otherwise, its new referents are scavenged. Defer + scavenging ephemerons until after a complete scavenge has been performed, + so that triggered ephemerons can be fired. Move them to the front of the + set and count them in numRememberedEphemerons for later scanning. */ + + /* SpurGenerationScavenger>>#scavengeRememberedSetStartingAt: */ +static NoDbgRegParms void +scavengeRememberedSetStartingAt(sqInt n) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt destIndex; + sqInt referrer; + sqInt sourceIndex; + + sourceIndex = (destIndex = n); + while (sourceIndex < GIV(rememberedSetSize)) { + referrer = GIV(rememberedSet)[sourceIndex]; + + /* Any potential firing ephemerons should not be scanned yet. + Move any to the front of the set to save time in later scanning. */ + if ((isEphemeron(referrer)) + && (isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(referrer)) + && (isObjEphemeron(referrer))), + /* fetchPointer:ofObject: */ + longAt((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord())))))))) { + assert(destIndex >= GIV(numRememberedEphemerons)); + GIV(rememberedSet)[destIndex] = (GIV(rememberedSet)[GIV(numRememberedEphemerons)]); + GIV(rememberedSet)[GIV(numRememberedEphemerons)] = referrer; + GIV(numRememberedEphemerons) += 1; + destIndex += 1; + } + else { + if (scavengeReferentsOf(referrer)) { + GIV(rememberedSet)[destIndex] = referrer; + destIndex += 1; + } + else { + setIsRememberedOfto(referrer, 0); + } + } + sourceIndex += 1; + } + GIV(rememberedSetSize) = destIndex; + assert(!((unfiredEphemeronsAtEndOfRememberedSet()))); +} + + +/* (Slang flattens so need unique selectors) */ + + /* SpurGenerationScavenger>>#scavengerTenuringThreshold */ +static float +scavengerTenuringThreshold(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (GIV(tenureThreshold) >= ((GIV(pastSpace).start)) + ? (((double) (GIV(tenureThreshold) - ((GIV(pastSpace).start))) )) / (((GIV(pastSpace).limit)) - ((GIV(pastSpace).start))) + : 0); +} + + +/* For assert checking only. */ + + /* SpurGenerationScavenger>>#unfiredEphemeronsAtEndOfRememberedSet */ +static sqInt +unfiredEphemeronsAtEndOfRememberedSet(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt i; + sqInt referrer; + + for (i = GIV(numRememberedEphemerons); i < GIV(rememberedSetSize); i += 1) { + referrer = GIV(rememberedSet)[i]; + + /* begin isEphemeron: */ + assert(isNonImmediate(referrer)); + format = (byteAt((void *)(referrer + (formatFieldByteOffset())))) & (formatMask()); + if (format == (ephemeronFormat())) { + if (isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(referrer)) + && (isObjEphemeron(referrer))), + /* fetchPointer:ofObject: */ + longAt((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord()))))))) { + return 1; + } + } + } + return 0; +} + + +/* Answer if the remembered table is laid out correctly after + scavengeInactiveEphemeronsInRememberedSet. This implies that up to + numRememberedEphemerons are only unfired ephemerons, and no unfired + ephemerons beyond. + */ + + /* SpurGenerationScavenger>>#validRememberedSetPartitionPostSIEIRS */ +static sqInt +validRememberedSetPartitionPostSIEIRS(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt i; + sqInt referrer; + + for (i = 0; i < GIV(rememberedSetSize); i += 1) { + referrer = GIV(rememberedSet)[i]; + + /* begin isEphemeron: */ + assert(isNonImmediate(referrer)); + format = (byteAt((void *)(referrer + (formatFieldByteOffset())))) & (formatMask()); + if (format == (ephemeronFormat())) { + if ((isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(referrer)) + && (isObjEphemeron(referrer))), + /* fetchPointer:ofObject: */ + longAt((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord())))))) + ? i >= GIV(numRememberedEphemerons) + : i < GIV(numRememberedEphemerons))) { + return 0; + } + } + } + return 1; +} + + +/* Answer if everything on the ephemeronList is a firable ephemeron */ + + /* SpurGenerationScavenger>>#wholeEphemeronListIsFirable */ +static sqInt +wholeEphemeronListIsFirable(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ephemeron; + usqInt ephemeronCorpse; + sqInt listOffset; + + if (!GIV(ephemeronList)) { + return 1; + } + ephemeronCorpse = ((((usqInt)((GIV(ephemeronList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + while (ephemeronCorpse) { + if (!((/* isYoung: */ + ((!(ephemeronCorpse & (tagMask())))) + && (oopisLessThan(ephemeronCorpse, GIV(oldSpaceStart)))) + && ((!((longAt((void *)(ephemeronCorpse))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + return 0; + } + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(ephemeronCorpse)); + ephemeron = longAt((void *)((ephemeronCorpse + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(ephemeron & (tagMask())))) + && ((!((longAt((void *)(ephemeron))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ephemeron = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + } + if (!((isEphemeron(ephemeron)) + && (isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(ephemeron)) + && (isObjEphemeron(ephemeron))), + /* fetchPointer:ofObject: */ + longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord()))))))))) { + return 0; + } + + /* begin nextCorpseOrNil: */ + assert(isYoung(ephemeronCorpse)); + listOffset = ((((usqInt)(((long32At((void *)(ephemeronCorpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(ephemeronCorpse + (formatFieldByteOffset())))); + ephemeronCorpse = ((sqInt) ((listOffset + ? ((((usqInt)((listOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0))); + } + return 1; +} + + +/* Output the entire record. */ + + /* SpurGenerationScavenger>>#writeScavengeLog */ +static NeverInline void +writeScavengeLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + static char *policyNames[] = {"", "by age", "by class", "to shrink rt", "don't tenure", "mark on tenure"}; + + /* log data collected by logStartScavenge */ + fprintf(GIV(scavengeLog), + "scavenge %" PRIdSQINT " eden bytes: 0x%" PRIxSQINT "/%" PRIdSQINT " past bytes: 0x%" PRIxSQINT "/%" PRIdSQINT "\n\trem set: %" PRIdSQINT " redzone: %" PRIdSQINT " size: %" PRIdSQINT "\n", + GIV(statScavenges), + (GIV(scavengeLogRecord).sEdenBytes), + (GIV(scavengeLogRecord).sEdenBytes), + (GIV(scavengeLogRecord).sPastBytes), + (GIV(scavengeLogRecord).sPastBytes), + (GIV(scavengeLogRecord).sRememberedSetSize), + (GIV(scavengeLogRecord).sRememberedSetRedZone), + (GIV(scavengeLogRecord).sRememberedSetLimit)); + + /* log data collected by logTenuringPolicy */ + fprintf(GIV(scavengeLog), + (((GIV(scavengeLogRecord).tTenureCriterion)) == TenureToShrinkRT + ? "\ttenure below 0x%" PRIxSQINT "/%" PRIdSQINT " %s refct %" PRIdSQINT "\n" + : "\ttenure below 0x%" PRIxSQINT "/%" PRIdSQINT " %s\n"), + (GIV(scavengeLogRecord).tTenureThreshold), + (GIV(scavengeLogRecord).tTenureThreshold), + policyNames[(GIV(scavengeLogRecord).tTenureCriterion)], + (GIV(scavengeLogRecord).tRefCountToShrinkRT)); + + /* log data collected by logEndScavenge */ + fprintf(GIV(scavengeLog), + "\tsurvivor bytes: 0x%" PRIxSQINT "/%" PRIdSQINT " rem set: %" PRIdSQINT " tenured: %" PRIdSQINT " usecs: %" PRIdSQINT "\n", + (GIV(scavengeLogRecord).eSurvivorBytes), + (GIV(scavengeLogRecord).eSurvivorBytes), + (GIV(scavengeLogRecord).eRememberedSetSize), + ((GIV(scavengeLogRecord).eStatTenures)) - ((GIV(scavengeLogRecord).sStatTenures)), + GIV(statSGCDeltaUsecs)); + fflush(GIV(scavengeLog)); +} + + +/* Answer the accessible object following the given object or + free chunk in the heap. Return nil when heap is exhausted. + This is for primitiveNextObject subsequent to primitiveSomeObject. + It also tries to handle more general use by ordering objects as + eden + past + old + but this is tricky becaus ethe order in memory is + past + eden + old */ + + /* SpurMemoryManager>>#accessibleObjectAfter: */ +static NoDbgRegParms sqInt +accessibleObjectAfter(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objAfter; + + objAfter = objOop; + if (oopisLessThan(objAfter, GIV(nilObj))) { + assert((isInEden(objOop)) + || (isInPastSpace(objOop))); + if (oopisGreaterThan(objAfter, GIV(pastSpaceStart))) { + /* Obj is in eden. Answer next normal object in eden, if there is one. */ + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objAfter); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objAfter = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objAfter = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + if (!(oopisLessThan(objAfter, GIV(freeStart)))) break; + if (((longAt((void *)(objAfter))) & (classIndexMask())) > (lastClassIndexPun())) { + return objAfter; + } + } + + /* There wasn't a next object in eden. If past space is empty answer nilObj. */ + if (GIV(pastSpaceStart) <= (((GIV(pastSpace)).start))) { + return GIV(nilObj); + } + + /* If the first object in pastSpace is OK, answer it, otherwise fall through to enumerate past space. */ + address = ((GIV(pastSpace)).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objAfter = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + if (((longAt((void *)(objAfter))) & (classIndexMask())) > (lastClassIndexPun())) { + return objAfter; + } + } + + /* Either objOop was in pastSpace, or enumeration exhaused eden, so enumerate past space. */ + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objAfter); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + objAfter = GIV(pastSpaceStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objAfter = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l2:; + if (!(oopisLessThan(objAfter, GIV(pastSpaceStart)))) break; + if (((longAt((void *)(objAfter))) & (classIndexMask())) > (lastClassIndexPun())) { + return objAfter; + } + } + return GIV(nilObj); + } + + /* object in new space */ + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objAfter); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objAfter = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + objAfter = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3: + if (objAfter == GIV(endOfMemory)) { + return null; + } + if (((longAt((void *)(objAfter))) & (classIndexMask())) > (lastClassIndexPun())) { + return objAfter; + } + } + return 0; +} + + +/* Answer whether an ephemeron is active (has an unmarked + key) and was pushed on the unscanned ephemerons stack. */ + + /* SpurMemoryManager>>#activeAndDeferredScan: */ +static NoDbgRegParms NeverInline sqInt +activeAndDeferredScan(sqInt anEphemeron) +{ + sqInt key; + + key = 0; + assert(isEphemeron(anEphemeron)); + if ((isImmediate((/* begin keyOfEphemeron: */ + assert((isNonImmediate(anEphemeron)) + && (isObjEphemeron(anEphemeron))), + (key = longAt((void *)((anEphemeron + BaseHeaderSize) + (0U << (shiftForWord())))))))) + || (((byteAt((void *)(key + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) { + return 0; + } + return pushOnUnscannedEphemeronsStack(anEphemeron); +} + + +/* Add a freeChunk sub tree back into the large free chunk tree. + This is for allocateOldSpaceChunkOf[Exactly]Bytes:[suchThat:]. */ +/* N.B. *can't* use numSlotsOfAny: because of rounding up of odd slots + and/or step in size at 1032 bytes in 32-bits or 2048 bytes in 64-bits. */ + + /* SpurMemoryManager>>#addFreeSubTree: */ +static NoDbgRegParms void +addFreeSubTree(sqInt freeTree) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytesInArg; + usqInt bytesInNode; + sqInt subNode; + sqInt treeNode; + + assert(isFreeObject(freeTree)); + bytesInArg = bytesInBody(freeTree); + assert(bytesInArg >= ((numFreeLists()) * (allocationUnit()))); + treeNode = GIV(freeLists)[0]; + assert(treeNode != 0); + while (1) { + bytesInNode = bytesInBody(treeNode); + + /* check for overlap; could write this as self oop: (self objectAfter: freeChunk) isLessThanOrEqualTo: child... + but that relies on headers being correct, etc. So keep it clumsy... */ + assert((oopisLessThanOrEqualTo((freeTree + bytesInArg) - BaseHeaderSize, treeNode)) + || (oopisGreaterThanOrEqualTo(freeTree, (treeNode + bytesInNode) - BaseHeaderSize))); + assert(bytesInNode >= ((numFreeLists()) * (allocationUnit()))); + assert(bytesInArg != bytesInNode); + if (bytesInNode > bytesInArg) { + subNode = longAt((void *)((treeNode + BaseHeaderSize) + (3U << (shiftForWord())))); + if (!subNode) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(treeNode)); + assert((freeTree == 0) + || (isFreeObject(freeTree))); + longAtput((void *)((treeNode + BaseHeaderSize) + (3U << (shiftForWord()))),freeTree); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeTree)); + assert((treeNode == 0) + || (isFreeObject(treeNode))); + longAtput((void *)((freeTree + BaseHeaderSize) + (2U << (shiftForWord()))),treeNode); + return; + } + } + else { + subNode = longAt((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + if (!subNode) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(treeNode)); + assert((freeTree == 0) + || (isFreeObject(freeTree))); + longAtput((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord()))),freeTree); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeTree)); + assert((treeNode == 0) + || (isFreeObject(treeNode))); + longAtput((void *)((freeTree + BaseHeaderSize) + (2U << (shiftForWord()))),treeNode); + return; + } + } + treeNode = subNode; + } +} + + +/* Add the given variable location to the extra roots table. */ + + /* SpurMemoryManager>>#addGCRoot: */ +sqInt +addGCRoot(sqInt *varLoc) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (GIV(extraRootCount) >= ExtraRootsSize) { + return 0; + } + GIV(extraRoots)[(GIV(extraRootCount) += 1)] = varLoc; + return 1; +} + + +/* Answer if maybeClassObj looks like a class object */ + + /* SpurMemoryManager>>#addressCouldBeClassObj: */ +static NoDbgRegParms sqInt +addressCouldBeClassObj(sqInt maybeClassObj) +{ + return (addressCouldBeObj(maybeClassObj)) + && (objCouldBeClassObj(maybeClassObj)); +} + + /* SpurMemoryManager>>#addressCouldBeObj: */ +sqInt +addressCouldBeObj(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((address & (BaseHeaderSize - 1)) == 0) + && ((/* Inlined isInOldSpace: for speed, because it also segregates the newSpace objects */ + + (oopisGreaterThanOrEqualTo(address, GIV(oldSpaceStart)) + ? oopisLessThan(address, GIV(endOfMemory)) + : (oopisGreaterThanOrEqualToandLessThan(address, ((GIV(eden)).start), GIV(freeStart))) + || ((oopisGreaterThanOrEqualToandLessThan(address, ((GIV(pastSpace)).start), GIV(pastSpaceStart))) + || ((GIV(gcPhaseInProgress) == ScavengeInProgress) + && (oopisGreaterThanOrEqualToandLessThan(address, ((GIV(futureSpace)).start), GIV(futureSurvivorStart)))))))); +} + + +/* Answer if address appears to be that of either an immediate or an object. + For code disassembly and assertions. */ + + /* SpurMemoryManager>>#addressCouldBeOop: */ +sqInt +addressCouldBeOop(sqInt address) +{ + return (((address & (tagMask())) != 0)) + || (addressCouldBeObj(address)); +} + + +/* Add freeChunk to the relevant freeList. + For the benefit of sortedFreeObject:, if freeChunk is large, answer the + treeNode it + is added to, if it is added to the next list of a freeTreeNode, otherwise + answer 0. */ +/* coInterpreter transcript ensureCr. coInterpreter print: 'freeing '. self + printFreeChunk: freeChunk. + */ + + /* SpurMemoryManager>>#addToFreeList:bytes: */ +static NoDbgRegParms sqInt +addToFreeListbytes(sqInt freeChunk, sqInt chunkBytes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt child; + usqInt childBytes; + sqInt index; + sqInt lilliputian; + sqInt nextFreeChunk; + sqInt parent; + + assert(isFreeObject(freeChunk)); + assert(chunkBytes == (bytesInBody(freeChunk))); + + /* Too slow to be enabled byt default but useful to debug Selective... + self deny: (compactor isSegmentBeingCompacted: (segmentManager segmentContainingObj: freeChunk)). */ + index = chunkBytes / 8 /* allocationUnit */; + if (index < 64 /* numFreeLists */) { + nextFreeChunk = GIV(freeLists)[index]; + + /* begin setNextFreeChunkOf:withValue:chunkBytes: */ + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + lilliputian = chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if ((nextFreeChunk != 0) + && (!lilliputian)) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((freeChunk == 0) + || (isFreeObject(freeChunk))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),freeChunk); + } + + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + if (!(chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */))) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + GIV(freeLists)[index] = freeChunk; + GIV(freeListsMask) = GIV(freeListsMask) | (1ULL << index); + return 0; + } + + /* begin addToFreeTree:bytes: */ + /* begin initFreeTreeChunk:bytes: */ + assert(isFreeObject(freeChunk)); + assert(chunkBytes == (bytesInBody(freeChunk))); + assert(chunkBytes >= ((numFreeLists()) * (allocationUnit()))); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord()))),0); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),0); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (2U << (shiftForWord()))),0); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (3U << (shiftForWord()))),0); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (4U << (shiftForWord()))),0); + + /* Large chunk list organized as a tree, each node of which is a list of chunks of the same size. + Beneath the node are smaller and larger blocks. */ + parent = 0; + child = GIV(freeLists)[0]; + while (child != 0) { + childBytes = bytesInBody(child); + + /* check for overlap; could write this as self oop: (self objectAfter: freeChunk) isLessThanOrEqualTo: child... + but that relies on headers being correct, etc. So keep it clumsy... */ + assert((oopisLessThanOrEqualTo((freeChunk + chunkBytes) - BaseHeaderSize, child)) + || (oopisGreaterThanOrEqualTo(freeChunk, (child + childBytes) - BaseHeaderSize))); + if (childBytes == chunkBytes) { + nextFreeChunk = longAt((void *)((child + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((freeChunk == 0) + || (isFreeObject(freeChunk))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),freeChunk); + } + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(child)); + assert((freeChunk == 0) + || (isFreeObject(freeChunk))); + longAtput((void *)((child + BaseHeaderSize) + (0U << (shiftForWord()))),freeChunk); + if (freeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + assert((child == 0) + || (isFreeObject(child))); + longAtput((void *)((freeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),child); + } + return child; + } + + /* size match; add to list at node. + walk down the tree */ + parent = child; + child = longAt((void *)((child + BaseHeaderSize) + ((((usqInt)(((childBytes > chunkBytes + ? 3 /* freeChunkSmallerIndex */ + : 4 /* freeChunkLargerIndex */))) << (shiftForWord())))))); + } + if (!parent) { + assert((GIV(freeLists)[0]) == 0); + GIV(freeLists)[0] = freeChunk; + GIV(freeListsMask) = GIV(freeListsMask) | 1; + return 0; + } + assert(((GIV(freeListsMask) & 1) != 0)); + + /* insert in tree */ + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + assert((parent == 0) + || (isFreeObject(parent))); + longAtput((void *)((freeChunk + BaseHeaderSize) + (2U << (shiftForWord()))),parent); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(parent)); + assert((freeChunk == 0) + || (isFreeObject(freeChunk))); + longAtput((void *)((parent + BaseHeaderSize) + ((((usqInt)(((childBytes > chunkBytes + ? 3 /* freeChunkSmallerIndex */ + : 4 /* freeChunkLargerIndex */))) << (shiftForWord()))))),freeChunk); + return 0; +} + + +/* Attempt to answer an array of all objects, excluding those that may + be garbage collected as a side effect of allocating the result array. + If no memory is available answer the number of instances as a + SmallInteger. Since objects are at least 16 bytes big, and the largest + SmallInteger covers + 1/4 of the address space, the count can never overflow. */ + + /* SpurMemoryManager>>#allInstancesOf: */ +static NoDbgRegParms sqInt +allInstancesOf(sqInt aClass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt actualIndex; + usqInt bytes; + int classIndex; + sqInt classOrNil; + sqInt count; + sqInt countSqInt; + int expectedIndex; + int expectedIndexInt; + sqInt followingWord; + usqInt followingWordAddress; + sqInt freeChunk; + sqInt i; + sqInt index; + sqInt iSqInt; + sqInt j; + usqInt limit; + usqInt newObj; + usqInt numBytes; + usqInt numSlots; + sqInt objOop; + sqInt page; + sqInt prevObj; + sqInt prevPrevObj; + sqInt ptr; + sqInt ptrSqInt; + sqInt smallObj; + usqInt start; + usqInt startUsqInt; + sqInt toDoLimit; + sqInt valuePointer; + + classIndex = (long32At((void *)(aClass + 4))) & (identityHashHalfWordMask()); + if (!classIndex) { + /* begin allocateSlots:format:classIndex: */ + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + 8 /* allocationUnit */; + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (0 <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + freeChunk = allocateSlotsInOldSpacebytesformatclassIndex(0, numBytes, arrayFormat(), ClassArrayCompactIndex); + goto l1; + } + longAtput((void *)(newObj),((((((usqLong) 0)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + freeChunk = newObj; + /* end allocateSlots:format:classIndex: */ +l1: + return freeChunk; + } + + /* N.B. Does /not/ update totalFreeOldSpace */ + freeChunk = allocateLargestFreeChunk(); + start = freeChunk + BaseHeaderSize; + limit = addressAfter(freeChunk); + + /* begin isClassAtUniqueIndex: */ + expectedIndex = (long32At((void *)(aClass + 4))) & (identityHashHalfWordMask()); + for (iSqInt = 0; iSqInt < GIV(numClassTablePages); iSqInt += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNil = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNil != GIV(nilObj)) { + index = ((((usqInt)(iSqInt) << (classTableMajorIndexShift())))) + j; + if ((classOrNil == aClass) + && ((index != expectedIndex) + && (index > (lastClassIndexPun())))) { + goto l4; + } + } + } + } + + /* begin uniqueIndex:allInstancesInto:limit:resultsInto: */ + countSqInt = 0; + ptrSqInt = start; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + + /* continue enumerating even if no room so as to unmark all objects. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + countSqInt += 1; + if (ptrSqInt < limit) { + longAtput((void *)(ptrSqInt),objOop); + ptrSqInt += BytesPerOop; + } + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l10; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l10:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + startUsqInt = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (startUsqInt > GIV(freeStart)) { + goto l9; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(startUsqInt + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? startUsqInt + BaseHeaderSize + : startUsqInt); + while (oopisLessThan(objOop, GIV(freeStart))) { + /* continue enumerating even if no room so as to unmark all objects. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + countSqInt += 1; + if (ptrSqInt < limit) { + longAtput((void *)(ptrSqInt),objOop); + ptrSqInt += BytesPerOop; + } + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l8; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l8:; + } + /* end allNewSpaceEntitiesDo: */ +l9: + count = countSqInt; + ptr = ptrSqInt; + goto l3; +l4: + + /* begin ambiguousClass:allInstancesInto:limit:resultsInto: */ + countSqInt = 0; + ptrSqInt = start; + expectedIndex = (long32At((void *)(aClass + 4))) & (identityHashHalfWordMask()); + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + + /* continue enumerating even if no room so as to unmark all objects and/or normalize class indices. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + actualIndex = (longAt((void *)(objOop))) & (classIndexMask()); + if ((classOrNilAtIndex(actualIndex)) == aClass) { + if (actualIndex != expectedIndex) { + /* begin setClassIndexOf:to: */ + assert(((expectedIndex >= 0) && (expectedIndex <= (classIndexMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(classIndexMask()))) + expectedIndex); + } + countSqInt += 1; + if (ptrSqInt < limit) { + longAtput((void *)(ptrSqInt),objOop); + ptrSqInt += BytesPerOop; + } + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l7; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l7:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + startUsqInt = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (startUsqInt > GIV(freeStart)) { + goto l6; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(startUsqInt + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? startUsqInt + BaseHeaderSize + : startUsqInt); + while (oopisLessThan(objOop, GIV(freeStart))) { + /* continue enumerating even if no room so as to unmark all objects and/or normalize class indices. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + actualIndex = (longAt((void *)(objOop))) & (classIndexMask()); + if ((classOrNilAtIndex(actualIndex)) == aClass) { + if (actualIndex != expectedIndex) { + /* begin setClassIndexOf:to: */ + assert(((expectedIndex >= 0) && (expectedIndex <= (classIndexMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(classIndexMask()))) + expectedIndex); + } + countSqInt += 1; + if (ptrSqInt < limit) { + longAtput((void *)(ptrSqInt),objOop); + ptrSqInt += BytesPerOop; + } + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l5; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l5:; + } + /* end allNewSpaceEntitiesDo: */ +l6: + + /* begin purgeDuplicateClassTableEntriesFor: */ + expectedIndexInt = (long32At((void *)(aClass + 4))) & (identityHashHalfWordMask()); + for (iSqInt = 0; iSqInt < GIV(numClassTablePages); iSqInt += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNil = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNil != GIV(nilObj)) { + index = ((((usqInt)(iSqInt) << (classTableMajorIndexShift())))) + j; + if ((classOrNil == aClass) + && ((index != expectedIndexInt) + && (index > (lastClassIndexPun())))) { + classAtIndexput(index, GIV(nilObj)); + if (index < GIV(classTableIndex)) { + GIV(classTableIndex) = index; + } + } + } + } + } + + /* classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + assert(GIV(classTableIndex) >= (1U << (classTableMajorIndexShift()))); + count = countSqInt; + ptr = ptrSqInt; +l3: + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + if ((count > ((ptr - start) / BytesPerOop)) + || ((limit != ptr) + && ((limit - ptr) <= 8 /* allocationUnit */))) { + freeChunkWithBytesat(bytesInBody(freeChunk), /* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk)); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return (((usqInt)count << 3) | 1); + } + + /* not enough room + can't split a single word */ + if (count < (numSlotsMask())) { + /* begin allocateSlots:format:classIndex: */ + if (count >= (numSlotsMask())) { + if ((((usqInt)(count)) >> 56) > 0) { + smallObj = null; + goto l2; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (count * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((count < 1 + ? 8 /* allocationUnit */ + : count * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (count <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + smallObj = allocateSlotsInOldSpacebytesformatclassIndex(count, numBytes, arrayFormat(), ClassArrayCompactIndex); + goto l2; + } + if (count >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + count); + longAtput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + } + else { + longAtput((void *)(newObj),((((((usqLong) count)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + smallObj = newObj; + /* end allocateSlots:format:classIndex: */ +l2: + for (i = 0; i < count; i += 1) { + valuePointer = longAt((void *)((freeChunk + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(smallObj)) + && (!(isForwarded(smallObj)))); + assert(validStorePointerUncheckedArgs(i, smallObj, valuePointer)); + longAtput((void *)((smallObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + freeChunkWithBytesat(bytesInBody(freeChunk), /* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk)); + beRootIfOld(smallObj); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return smallObj; + } + bytes = (BaseHeaderSize + BaseHeaderSize) + (count * BytesPerOop); + start = /* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk); + freeChunkWithBytesat((limit - start) - bytes, start + bytes); + GIV(totalFreeOldSpace) -= bytes; + + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(freeChunk - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56))) + count); + + /* begin set:classIndexTo:formatTo: */ + assert(((ClassArrayCompactIndex >= 0) && (ClassArrayCompactIndex <= (classIndexMask())))); + assert((((arrayFormat()) >= 0) && ((arrayFormat()) <= (formatMask())))); + longAtput((void *)(freeChunk),((longAt((void *)(freeChunk))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + (ClassArrayCompactIndex + ((((usqInt)((arrayFormat())) << (formatShift())))))); + + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(freeChunk + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(freeChunk); + } + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + runLeakCheckerFor(GCModeFull); + return freeChunk; +} + + +/* Attempt to answer an array of all objects, excluding those that may + be garbage collected as a side effect of allocating the result array. + If no memory is available answer the number of objects as a SmallInteger. + Since objects are at least 16 bytes big, and the largest SmallInteger + covers 1/4 of the address space, the count can never overflow. */ + + /* SpurMemoryManager>>#allObjects */ +static sqInt +allObjects(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytes; + sqInt count; + sqInt followingWord; + usqInt followingWordAddress; + sqInt freeChunk; + usqInt limit; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + sqInt ptr; + usqInt start; + usqInt startUsqInt; + + + /* N.B. Does /not/ update totalFreeOldSpace */ + freeChunk = allocateLargestFreeChunk(); + ptr = (start = freeChunk + BaseHeaderSize); + limit = addressAfter(freeChunk); + count = 0; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + + /* continue enumerating even if no room so as to unmark all objects. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + count += 1; + if (ptr < limit) { + longAtput((void *)(ptr),objOop); + ptr += BytesPerOop; + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + startUsqInt = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (startUsqInt > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(startUsqInt + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? startUsqInt + BaseHeaderSize + : startUsqInt); + while (oopisLessThan(objOop, GIV(freeStart))) { + /* continue enumerating even if no room so as to unmark all objects. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + count += 1; + if (ptr < limit) { + longAtput((void *)(ptr),objOop); + ptr += BytesPerOop; + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3: + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + assert(count >= (numSlotsMask())); + if ((count > ((ptr - start) / BytesPerOop)) + || ((limit != ptr) + && ((limit - ptr) <= 8 /* allocationUnit */))) { + freeChunkWithBytesat(bytesInBody(freeChunk), /* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk)); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return (((usqInt)count << 3) | 1); + } + + /* not enough room + can't split a single word */ + bytes = (BaseHeaderSize + BaseHeaderSize) + (count * BytesPerOop); + start = /* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk); + freeChunkWithBytesat((limit - start) - bytes, start + bytes); + GIV(totalFreeOldSpace) -= bytes; + + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(freeChunk - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56))) + count); + + /* begin set:classIndexTo:formatTo: */ + assert(((ClassArrayCompactIndex >= 0) && (ClassArrayCompactIndex <= (classIndexMask())))); + assert((((arrayFormat()) >= 0) && ((arrayFormat()) <= (formatMask())))); + longAtput((void *)(freeChunk),((longAt((void *)(freeChunk))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + (ClassArrayCompactIndex + ((((usqInt)((arrayFormat())) << (formatShift())))))); + + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(freeChunk + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(freeChunk); + } + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + runLeakCheckerFor(GCModeFull); + return freeChunk; +} + + /* SpurMemoryManager>>#allObjectsUnmarked */ +static sqInt +allObjectsUnmarked(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + GIV(bogon) = obj; + return 0; + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + return 1; +} + + /* SpurMemoryManager>>#allOldMarkedWeakObjectsOnWeaklingStack */ +static sqInt +allOldMarkedWeakObjectsOnWeaklingStack(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if ((isWeakNonImm(objOop)) + && (((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) { + if (!(isonObjStack(objOop, GIV(weaklingStack)))) { + return 0; + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + return 1; +} + + +/* Answer the largest free chunk in the free lists. */ +/* would like to use ifNotNil: but the ^next inside the ^blah ifNotNil: + confused Slang + */ + + /* SpurMemoryManager>>#allocateLargestFreeChunk */ +static sqInt +allocateLargestFreeChunk(void) +{ + sqInt freeChunk; + sqInt next; + sqInt nextFreeChunk; + + freeChunk = findLargestFreeChunk(); + if (!freeChunk) { + return null; + } + + /* This will be the node, not a list element. Answer a list element in preference. */ + next = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + if (next) { + assert((bytesInBody(freeChunk)) >= (numFreeLists())); + nextFreeChunk = longAt((void *)((next + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((freeChunk == 0) + || (isFreeObject(freeChunk))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),freeChunk); + } + return next; + } + unlinkSolitaryFreeTreeNode(freeChunk); + return freeChunk; +} + + +/* Answer a chunk of oldSpace from the free lists, if available, + otherwise answer nil. Break up a larger chunk if one of the + exact size does not exist. N.B. the chunk is simply a pointer, it + has no valid header. The caller *must* fill in the header correctly. */ +/* for debugging: */ +/* totalFreeOldSpace := self totalFreeListBytes */ + + /* SpurMemoryManager>>#allocateOldSpaceChunkOfBytes: */ +static NoDbgRegParms sqInt +allocateOldSpaceChunkOfBytes(usqInt chunkBytes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt child; + usqInt childBytes; + sqInt chunk; + usqInt index; + usqInt initialIndex; + sqInt lilliputian; + sqInt next; + sqInt nextFreeChunk; + usqInt nodeBytes; + sqInt parent; + + nodeBytes = 0; + + /* be optimistic (& don't wait for the write) */ + GIV(totalFreeOldSpace) -= chunkBytes; + initialIndex = chunkBytes / 8 /* allocationUnit */; + if ((initialIndex < 64 /* numFreeLists */) + && ((1ULL << initialIndex) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << initialIndex)) != 0)) { + if ((chunk = GIV(freeLists)[initialIndex])) { + assert(chunk == (startOfObject(chunk))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(chunk)); + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + lilliputian = chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */); + + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(chunk)) == (initialIndex * (allocationUnit()))) + && ((initialIndex > 1) + && ((startOfObject(chunk)) == chunk))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[initialIndex] = ((next = longAt((void *)((chunk + BaseHeaderSize) + (0U << (shiftForWord())))))); + if ((!lilliputian) + && (next != 0)) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + return chunk; + } + GIV(freeListsMask) -= 1ULL << initialIndex; + } + + /* first search for free chunks of a multiple of chunkBytes in size */ + index = initialIndex; + while ((((index += index)) < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << index)) != 0)) { + if ((chunk = GIV(freeLists)[index])) { + assert(chunk == (startOfObject(chunk))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(chunk)); + assert(((bytesInBody(chunk)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(chunk)) == chunk))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((next = longAt((void *)((chunk + BaseHeaderSize) + (0U << (shiftForWord())))))); + if (next) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + assert((bytesInBody(chunk)) == (index * (allocationUnit()))); + freeChunkWithBytesat((index * 8 /* allocationUnit */) - chunkBytes, (/* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk)) + chunkBytes); + return chunk; + } + GIV(freeListsMask) -= 1ULL << index; + } + } + + /* now get desperate and use the first that'll fit. + Note that because the minimum free size is 16 bytes (2 * allocationUnit), to + leave room for the forwarding pointer/next free link, we can only break chunks + that are at least 16 bytes larger, hence start at initialIndex + 2. */ + index = initialIndex + 1; + while ((((index += 1)) < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << index)) != 0)) { + if ((chunk = GIV(freeLists)[index])) { + assert(chunk == (startOfObject(chunk))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(chunk)); + assert(((bytesInBody(chunk)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(chunk)) == chunk))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((next = longAt((void *)((chunk + BaseHeaderSize) + (0U << (shiftForWord())))))); + if (next) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + assert((bytesInBody(chunk)) == (index * (allocationUnit()))); + freeChunkWithBytesat((index * 8 /* allocationUnit */) - chunkBytes, (/* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk)) + chunkBytes); + return chunk; + } + GIV(freeListsMask) -= 1ULL << index; + } + } + } + + /* Large chunk, or no space on small free lists. Search the large chunk list. + Large chunk list organized as a tree, each node of which is a list of chunks + of the same size. Beneath the node are smaller and larger blocks. + When the search ends parent should hold the smallest chunk at least as + large as chunkBytes, or 0 if none. */ + parent = 0; + child = GIV(freeLists)[0]; + while (child != 0) { + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(child)); + childBytes = bytesInBody(child); + if (childBytes == chunkBytes) { + chunk = longAt((void *)((child + BaseHeaderSize) + (0U << (shiftForWord())))); + if (chunk) { + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(chunk)); + nextFreeChunk = longAt((void *)((chunk + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(child)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((child + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((child == 0) + || (isFreeObject(child))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),child); + } + return /* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk); + } + nodeBytes = childBytes; + parent = child; + + /* break out of loop to remove interior node */ + child = 0; + } + else { + if (childBytes <= (chunkBytes + 8 /* allocationUnit */)) { + child = longAt((void *)((child + BaseHeaderSize) + (4U << (shiftForWord())))); + } + else { + /* parent will be smallest node >= chunkBytes + allocationUnit */ + parent = child; + nodeBytes = childBytes; + child = longAt((void *)((child + BaseHeaderSize) + (3U << (shiftForWord())))); + } + } + } + if (!parent) { + /* optimism was unfounded */ + GIV(totalFreeOldSpace) += chunkBytes; + return null; + } + + /* self printFreeChunk: parent */ + assert((nodeBytes == chunkBytes) + || (nodeBytes >= (chunkBytes + (2 * (allocationUnit()))))); + assert((bytesInBody(parent)) == nodeBytes); + + /* attempt to remove from list */ + chunk = longAt((void *)((parent + BaseHeaderSize) + (0U << (shiftForWord())))); + if (chunk) { + assert((chunkBytes == nodeBytes) + || ((chunkBytes + (allocationUnit())) < nodeBytes)); + nextFreeChunk = longAt((void *)((chunk + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(parent)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((parent + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((parent == 0) + || (isFreeObject(parent))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),parent); + } + if (chunkBytes != nodeBytes) { + freeChunkWithBytesat(nodeBytes - chunkBytes, (/* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk)) + chunkBytes); + } + return /* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk); + } + + /* no list; remove the interior node */ + chunk = parent; + unlinkSolitaryFreeTreeNode(chunk); + + /* if there's space left over, add the fragment back. */ + if (chunkBytes != nodeBytes) { + freeChunkWithBytesat(nodeBytes - chunkBytes, (/* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk)) + chunkBytes); + } + return /* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk); +} + + /* SpurMemoryManager>>#allocatePinnedSlots: */ +sqInt +allocatePinnedSlots(sqInt nSlots) +{ + sqInt obj; + usqInt p; + usqInt toDoLimit; + + obj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(nSlots, /* objectBytesForSlots: */ + (nSlots + ? ((((usqInt)(nSlots) << (shiftForWord())))) + ((nSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (obj) { + assert(isPinned(obj)); + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((obj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1, addressAfter(obj))); + toDoLimit = ((usqInt)(((obj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(obj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + return obj; +} + + +/* Answer the oop of a chunk of space in oldSpace with numSlots slots. The + header will have been filled-in but not the contents. If no memory is + available answer nil. */ + + /* SpurMemoryManager>>#allocateSlotsInOldSpace:bytes:format:classIndex: */ +static NoDbgRegParms sqInt +allocateSlotsInOldSpacebytesformatclassIndex(sqInt numSlots, usqInt totalBytes, sqInt formatField, sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt chunk; + + if ((chunk = allocateOldSpaceChunkOfBytes(totalBytes))) { + if (numSlots >= (numSlotsMask())) { + long64Atput((void *)(chunk),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + long64Atput((void *)(chunk + BaseHeaderSize),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex); + + /* begin checkFreeSpace:ignoring: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, chunk + BaseHeaderSize); + } + return chunk + BaseHeaderSize; + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + long64Atput((void *)(chunk),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex); + + /* begin checkFreeSpace:ignoring: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, chunk); + } + return chunk; + } + return 0; +} + + +/* All objects are a multiple of 8 bytes in length */ + + /* SpurMemoryManager>>#allocationUnit */ +static sqInt +allocationUnit(void) +{ + return 8; +} + + +/* N.B. generateToByDoLimitExpression:negative:on: guards against (unsigned)0 + - 1 going +ve + */ + + /* SpurMemoryManager>>#allStrongSlotsOfWeaklingAreMarked: */ +static NoDbgRegParms sqInt +allStrongSlotsOfWeaklingAreMarked(sqInt aWeakling) +{ + sqInt i; + sqInt referent; + sqInt toDoLimit; + + toDoLimit = (numStrongSlotsOfWeakling(aWeakling)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((aWeakling + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + if (!((byteAt((void *)(referent + (markBitsByteOffset())))) & (1U << (markedBitByteShift())))) { + return 0; + } + } + } + return 1; +} + + /* SpurMemoryManager>>#allUnscannedEphemeronsAreActive */ +static sqInt +allUnscannedEphemeronsAreActive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt key; + usqInt p; + + for (p = ((GIV(unscannedEphemerons).start)); p <= ((GIV(unscannedEphemerons).top)); p += BytesPerOop) { + /* begin keyOfMaybeFiredEphemeron: */ + assert(isMaybeFiredEphemeron(longAt((void *)(p)))); + key = longAt((void *)(((longAt((void *)(p))) + BaseHeaderSize) + (0U << (shiftForWord())))); + if ((((key & (tagMask())) != 0)) + || (((byteAt((void *)(key + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) { + return 0; + } + } + return 1; +} + + +/* we don't want to inline so we can nest that in an assertion with the + return true so the production VM does not generate any code here, while in + simulation, the code breaks on the assertion we want to. + */ + + /* SpurMemoryManager>>#assertInnerValidFreeObject: */ +static NoDbgRegParms NeverInline sqInt +assertInnerValidFreeObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cameFrom; + sqInt chunk; + sqInt chunkBytes; + usqInt index; + sqInt largeChild; + sqInt smallChild; + sqInt treeNode; + + assert(oopisLessThanOrEqualTo(addressAfter(objOop), GIV(endOfMemory))); + chunk = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert((chunk == 0) + || (isFreeOop(chunk))); + chunkBytes = bytesInBody(objOop); + + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + if (!(chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */))) { + /* double linkedlist assertions */ + chunk = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (chunk) { + assert(isFreeOop(chunk)); + assert(objOop == (fetchPointerofFreeChunk(freeChunkPrevIndex(), chunk))); + } + chunk = longAt((void *)((objOop + BaseHeaderSize) + (1U << (shiftForWord())))); + index = (bytesInBody(objOop)) / 8 /* allocationUnit */; + if ((index < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + if ((GIV(freeLists)[index]) == objOop) { + assert(chunk == 0); + } + } + else { + /* begin freeTreeNodesDo: */ + treeNode = GIV(freeLists)[0]; + if (!treeNode) { + goto l1; + } + cameFrom = -1; + do { + assert((bytesInBody(treeNode)) >= ((numFreeLists()) * (allocationUnit()))); + smallChild = longAt((void *)((treeNode + BaseHeaderSize) + (3U << (shiftForWord())))); + largeChild = longAt((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + assert((smallChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), smallChild)))); + assert((largeChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), largeChild)))); + + /* apply if the node has no children, or it has no large children and we're + returning from the small child, or we're returning from the large child. */ + if (((smallChild == 0) + && (largeChild == 0)) + || ((largeChild + ? cameFrom == largeChild + : cameFrom == smallChild))) { + if (treeNode == objOop) { + assert(chunk == 0); + } + + /* and since we've applied we must move on up */ + cameFrom = treeNode; + treeNode = longAt((void *)((treeNode + BaseHeaderSize) + (2U << (shiftForWord())))); + } + else { + if ((smallChild != 0) + && (cameFrom != smallChild)) { + treeNode = smallChild; + } + else { + assert(largeChild != 0); + treeNode = largeChild; + } + cameFrom = -1; + } + } while(treeNode != 0); + /* end freeTreeNodesDo: */ +l1:; + } + if (chunk) { + assert(isFreeOop(chunk)); + assert(objOop == (fetchPointerofFreeChunk(freeChunkNextIndex(), chunk))); + } + } + if ((bytesInBody(objOop)) >= 0x200 /* (numFreeLists * #allocationUnit) */) { + /* Tree assertions */ + chunk = longAt((void *)((objOop + BaseHeaderSize) + (2U << (shiftForWord())))); + assert((chunk == 0) + || ((isFreeOop(chunk)) + && (isLargeFreeObject(chunk)))); + chunk = longAt((void *)((objOop + BaseHeaderSize) + (3U << (shiftForWord())))); + assert((chunk == 0) + || ((isFreeOop(chunk)) + && (isLargeFreeObject(chunk)))); + chunk = longAt((void *)((objOop + BaseHeaderSize) + (4U << (shiftForWord())))); + assert((chunk == 0) + || ((isFreeOop(chunk)) + && (isLargeFreeObject(chunk)))); + } + return 1; +} + + /* SpurMemoryManager>>#become:with: */ +sqInt +becomewith(sqInt array1, sqInt array2) +{ + return becomewithtwoWaycopyHash(array1, array2, 1, 1); +} + + +/* All references to each object in array1 are swapped with all references to + the corresponding object in array2. That is, all pointers to one object + are replaced + with with pointers to the other. The arguments must be arrays of the same + length. + Answers PrimNoErr if the primitive succeeds, otherwise a relevant error + code. + */ +/* Implementation: Uses lazy forwarding to defer updating references until + message send. + */ + + /* SpurMemoryManager>>#become:with:twoWay:copyHash: */ +static NoDbgRegParms sqInt +becomewithtwoWaycopyHash(sqInt array1, sqInt array2, sqInt twoWayFlag, sqInt copyHashFlag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt contextSize; + sqInt ec; + sqInt fieldOffset; + sqInt fmt; + sqInt hash; + sqInt header; + sqInt headerSqInt; + sqInt i; + sqInt newObj1; + sqInt newObj2; + usqInt numSlots; + usqInt numSlotsUsqInt; + usqLong o1ClassIndex; + sqLong o1Header; + usqLong o2ClassIndex; + sqLong o2Header; + sqInt obj1; + sqInt obj2; + sqInt objOop; + sqInt objOopSqInt; + sqInt oop1; + sqInt oop2; + sqInt referent; + sqInt referentSqInt; + sqInt size; + sqInt sp; + sqInt toDoLimit; + sqInt top; + + assert(GIV(becomeEffectsFlags) == 0); + runLeakCheckerFor(GCModeBecome); + if (!(/* isArray: */ + ((!(array1 & (tagMask())))) + && (((byteAt((void *)(array1 + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + return PrimErrBadReceiver; + } + if (!((/* isArray: */ + ((!(array2 & (tagMask())))) + && (((byteAt((void *)(array2 + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(array1)) > (isForwardedObjectClassIndexPun())), + (((numSlotsUsqInt = byteAt((void *)(array1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) == ((/* begin numSlotsOf: */ + assert((classIndexOf(array2)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(array2 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array2 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))))) { + return PrimErrBadArgument; + } + + /* begin containsOnlyValidBecomeObjects:and:twoWay:copyHash: */ + /* begin lastPointerOf: */ + fmt = (byteAt((void *)(array1 + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt != (forwardedFormat())); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(array1))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(array1))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + fieldOffset = (((CtxtTempFrameStart - 1) + contextSize) * BytesPerOop) + BaseHeaderSize; + goto l2; + } + fieldOffset = ((((/* begin numSlotsOf: */ + assert((classIndexOf(array1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(array1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1) * BytesPerOop) + BaseHeaderSize; + goto l2; + } + if (fmt < (firstCompiledMethodFormat())) { + fieldOffset = 0; + goto l2; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(array1)); + headerSqInt = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + fieldOffset = (((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) - 1) * BytesPerOop) + BaseHeaderSize; + /* end lastPointerOf: */ +l2: + size = 0; + + /* array1 is known to be the same size as array2 */ + while (fieldOffset >= BaseHeaderSize) { + oop1 = longAt((void *)(array1 + fieldOffset)); + if (/* isOopForwarded: */ + ((!(oop1 & (tagMask())))) + && ((!((longAt((void *)(oop1))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop1)); + referent = longAt((void *)((oop1 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop1 = referent; + longAtput((void *)(array1 + fieldOffset),oop1); + } + + /* begin ifOopInvalidForBecome:errorCodeInto: */ + if (((oop1 & (tagMask())) != 0)) { + ec = PrimErrInappropriate; + goto l5; + } + if ((byteAt((void *)(oop1 + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + ec = PrimErrObjectIsPinned; + goto l5; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(oop1 + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + ec = PrimErrNoModification; + goto l5; + } + /* end ifOopInvalidForBecome:errorCodeInto: */ +l3: + oop2 = longAt((void *)(array2 + fieldOffset)); + if (/* isOopForwarded: */ + ((!(oop2 & (tagMask())))) + && ((!((longAt((void *)(oop2))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop2)); + referentSqInt = longAt((void *)((oop2 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop2 = referentSqInt; + longAtput((void *)(array2 + fieldOffset),oop2); + } + if (oop1 != oop2) { + if (twoWayFlag) { + /* begin ifOopInvalidForBecome:errorCodeInto: */ + if (((oop2 & (tagMask())) != 0)) { + ec = PrimErrInappropriate; + goto l5; + } + if ((byteAt((void *)(oop2 + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + ec = PrimErrObjectIsPinned; + goto l5; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(oop2 + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + ) { + ec = PrimErrNoModification; + goto l5; + } + /* end ifOopInvalidForBecome:errorCodeInto: */ +l4: + size = (size + (bytesInBody(oop1))) + (bytesInBody(oop2)); + GIV(becomeEffectsFlags) = (GIV(becomeEffectsFlags) | (becomeEffectFlagsFor(oop1))) | (becomeEffectFlagsFor(oop2)); + } + else { + if (copyHashFlag) { + if (((oop2 & (tagMask())) != 0)) { + ec = PrimErrInappropriate; + goto l5; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(oop2 + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + ) { + ec = PrimErrNoModification; + goto l5; + } + } + GIV(becomeEffectsFlags) = GIV(becomeEffectsFlags) | (becomeEffectFlagsFor(oop1)); + } + } + fieldOffset -= BytesPerOop; + } + if (size >= (GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart)))) { + ec = PrimErrNoMemory; + goto l5; + } + ec = 0; + /* end containsOnlyValidBecomeObjects:and:twoWay:copyHash: */ +l5: + if (ec) { + /* begin zeroBecomeEffectFlagsOnFailure */ + if (((GIV(becomeEffectsFlags) & BecameJittedCompiledMethodFlag) != 0)) { + unflagBecomeFlaggedMethods(); + } + GIV(becomeEffectsFlags) = 0; + return ec; + } + + /* begin preBecomeAction: */ + if (GIV(stackPage)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + + /* see the comment in InterpreterPrimitives>>primitiveArrayBecome */ + if (((GIV(becomeEffectsFlags) & BecameJittedCompiledMethodFlag) != 0)) { + /* begin insulateFramesAndContextsFromCogMethodsFlaggedForBecome */ + activeContext = divorceAllFramesSuchThat(isMachineCodeFrameForCogMethodFlaggedForBecome); + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + assert((GIV(framePointer) != 0) == (isStillMarriedContext(activeContext))); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + objOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, objOop, activeContext)); + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeContext & (tagMask())))) + && (oopisLessThan(activeContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),activeContext); + } + } + + /* Need to set gcMode var (to avoid passing the flag through a lot of the updating code) */ + GIV(gcMode) = GCModeBecome; + if (twoWayFlag) { + /* begin innerBecomeObjectsIn:and:copyHash: */ + toDoLimit = ((assert((classIndexOf(array1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(array1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + /* At first blush it would appear unnecessary to use followField: here since + the validation in become:with:twoWay:copyHash: follows forwarders. But + there's nothing to ensure all elements of each array are unique and don't + appear in the other array. So the enumeration could encounter an object + already becommed earlier in the same enumeration. */ + + /* begin followField:ofObject: */ + obj1 = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(obj1 & (tagMask())))) + && ((!((longAt((void *)(obj1))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + obj1 = fixFollowedFieldofObjectwithInitialValue(i, array1, obj1); + } + + /* begin followField:ofObject: */ + obj2 = longAt((void *)((array2 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(obj2 & (tagMask())))) + && ((!((longAt((void *)(obj2))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + obj2 = fixFollowedFieldofObjectwithInitialValue(i, array2, obj2); + } + if (obj1 != obj2) { + /* begin doBecome:and:copyHash: */ + o1Header = long64At((void *)(obj1)); + o1ClassIndex = (((usqInt)(obj1)) >> (identityHashFullWordShift())) & (identityHashHalfWordMask()); + if ((o1ClassIndex != 0) + && ((classAtIndex(o1ClassIndex)) != obj1)) { + o1ClassIndex = 0; + } + o2Header = long64At((void *)(obj2)); + o2ClassIndex = (((usqInt)(obj2)) >> (identityHashFullWordShift())) & (identityHashHalfWordMask()); + if ((o2ClassIndex != 0) + && ((classAtIndex(o2ClassIndex)) != obj2)) { + o2ClassIndex = 0; + } + + /* Refuse to do an in-place become on classes since their being + forwarded is used in the flush method cache implementations. + Refuse to do an in-place become on compiled code because + doing so breaks the unmapping of methods from cog methods. */ + if ((((/* begin numSlotsOf: */ + assert((classIndexOf(obj1)) > (isForwardedObjectClassIndexPun())), + (((numSlotsUsqInt = byteAt((void *)(obj1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) == ((/* begin numSlotsOf: */ + assert((classIndexOf(obj2)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(obj2 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj2 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))) + && (((o1ClassIndex + o2ClassIndex) == 0) + && ((!(isCompiledMethodFormat((((usqLong)(o1Header)) >> (formatShift())) & (formatMask())))) + && (!(isCompiledMethodFormat((((usqLong)(o2Header)) >> (formatShift())) & (formatMask()))))))) { + inPlaceBecomeandcopyHashFlag(obj1, obj2, copyHashFlag); + goto l6; + } + outOfPlaceBecomeandcopyHashFlag(obj1, obj2, copyHashFlag); + + /* if copyHashFlag then nothing changes, since hashes were also swapped. */ + if (copyHashFlag) { + goto l6; + } + + /* if copyHash is false then the classTable entries must be updated. + We leave the following until postBecomeScanClassTable:, but must + swap the forwarders if two active classes have been becommed, + and assign hashes if not. */ + if (o1ClassIndex) { + if (o2ClassIndex) { + classAtIndexput(o1ClassIndex, obj2); + classAtIndexput(o2ClassIndex, obj1); + } + else { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj1)); + newObj2 = longAt((void *)((obj1 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(newObj2 & (tagMask())))) + && ((!((longAt((void *)(newObj2))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + newObj2 = longAt((void *)((newObj2 + BaseHeaderSize) + (0U << (shiftForWord())))); + } + assert((rawHashBitsOf(newObj2)) == 0); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(newObj2 + 4),((((long32At((void *)(newObj2 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (o1ClassIndex & (identityHashHalfWordMask()))); + } + } + else { + if (o2ClassIndex) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj2)); + newObj1 = longAt((void *)((obj2 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(newObj1 & (tagMask())))) + && ((!((longAt((void *)(newObj1))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + newObj1 = longAt((void *)((newObj1 + BaseHeaderSize) + (0U << (shiftForWord())))); + } + assert((rawHashBitsOf(newObj1)) == 0); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(newObj1 + 4),((((long32At((void *)(newObj1 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (o2ClassIndex & (identityHashHalfWordMask()))); + } + } + /* end doBecome:and:copyHash: */ +l6: + objOop = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(i, array1, objOop); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((array2 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(i, array2, objOopSqInt); + } + } + } + } + else { + /* begin innerBecomeObjectsIn:to:copyHash: */ + toDoLimit = ((assert((classIndexOf(array1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(array1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + /* At first blush it would appear unnecessary to use followField: here since + the validation in become:with:twoWay:copyHash: follows forwarders. But + there's nothing to ensure all elements of each array is unique and doesn't + appear in the other array. So the enumeration could encounter an object + already becommed earlier in the same enumeration. */ + + /* begin followField:ofObject: */ + obj1 = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(obj1 & (tagMask())))) + && ((!((longAt((void *)(obj1))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + obj1 = fixFollowedFieldofObjectwithInitialValue(i, array1, obj1); + } + + /* begin followField:ofObject: */ + obj2 = longAt((void *)((array2 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(obj2 & (tagMask())))) + && ((!((longAt((void *)(obj2))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + obj2 = fixFollowedFieldofObjectwithInitialValue(i, array2, obj2); + } + if (obj1 != obj2) { + /* begin doBecome:to:copyHash: */ + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(obj1),((longAt((void *)(obj1))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(obj1),(longAt((void *)(obj1))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(obj1)); + assert(!(isOopForwarded(obj2))); + + /* begin isOldObject: */ + assert(isNonImmediate(obj1)); + if (oopisGreaterThanOrEqualTo(obj1, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj2 & (tagMask())))) + && (oopisLessThan(obj2, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj1 + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj1); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj1 + BaseHeaderSize) + (0U << (shiftForWord()))),obj2); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(obj1 + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(obj1 + (numSlotsFieldByteOffset())),1); + } + if (copyHashFlag) { + hash = (long32At((void *)(obj1 + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(obj2 + 4),((((long32At((void *)(obj2 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + } + if (((/* begin isOldObject: */ + assert(isNonImmediate(obj1)), + oopisGreaterThanOrEqualTo(obj1, GIV(oldSpaceStart)))) + && (/* isYoung: */ + ((!(obj2 & (tagMask())))) + && (oopisLessThan(obj2, GIV(oldSpaceStart))))) { + GIV(becomeEffectsFlags) = GIV(becomeEffectsFlags) | OldBecameNewFlag; + } + assert(!((isOopForwarded(obj2)))); + + /* begin followField:ofObject: */ + objOop = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(i, array1, objOop); + } + assert(!(isOopForwarded(obj2))); + } + } + } + + /* begin followSpecialObjectsOop */ + if ((!((longAt((void *)(GIV(specialObjectsOop)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + GIV(validatedIntegerClassFlags) = 0; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(GIV(specialObjectsOop))); + referent = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + GIV(specialObjectsOop) = referent; + } + followForwardedObjectFieldstoDepth(GIV(specialObjectsOop), 0); + + /* N.B. perform coInterpreter's postBecomeAction: *before* postBecomeScanClassTable: + to allow the coInterpreter to void method cache entries by spotting classIndices that + refer to forwarded objects. postBecomeScanClassTable: follows forwarders in the table. */ + + /* begin postBecomeAction: */ + spurPostBecomeAction(GIV(becomeEffectsFlags)); + if (((GIV(becomeEffectsFlags) & BecameJittedCompiledMethodFlag) != 0)) { + freeBecomeFlaggedMethods(); + } + followMovableLiteralsAndUpdateYoungReferrers(); + + /* begin nilUncoggableMethods */ + GIV(lastCoggableInterpretedBlockMethod) = (GIV(lastUncoggableInterpretedBlockMethod) = null); + assert(kosherYoungReferrers()); + GIV(gcMode) = 0; + postBecomeScanClassTable(GIV(becomeEffectsFlags)); + if (twoWayFlag) { + assert(validPostBecomeArrayContents(array1)); + assert(validPostBecomeArrayContents(array2)); + } + else { + assert(validPostBecomeArrayContents(array2)); + } + assert(validClassTableHashes()); + runLeakCheckerFor(GCModeBecome); + + /* begin zeroBecomeEffectFlagsOnSuccess */ + GIV(becomeEffectsFlags) = 0; + return PrimNoErr; +} + + +/* If this object is old, mark it as a root (because a new object + may be stored into it). */ + + /* SpurMemoryManager>>#beRootIfOld: */ +void +beRootIfOld(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin isOldObject: */ + assert(isNonImmediate(oop)); + if (oopisGreaterThanOrEqualTo(oop, GIV(oldSpaceStart))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(oop); + } + } +} + + /* SpurMemoryManager>>#bitsSetInFreeSpaceMaskForAllFreeLists */ +static sqInt +bitsSetInFreeSpaceMaskForAllFreeLists(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + if (((GIV(freeLists)[i]) != 0) + && ((!((1ULL << i) & GIV(freeListsMask))))) { + return 0; + } + } + return 1; +} + + /* SpurMemoryManager>>#bridgeSize */ +static sqInt +bridgeSize(void) +{ + return 2 * BaseHeaderSize; +} + + /* SpurMemoryManager>>#byteFormatForNumBytes: */ +static NoDbgRegParms sqInt +byteFormatForNumBytes(sqInt numBytes) +{ + return (firstByteFormat()) + ((8 - numBytes) & (BytesPerWord - 1)); +} + + /* SpurMemoryManager>>#byteFormatMask */ +static sqInt +byteFormatMask(void) +{ + return 24; +} + + /* SpurMemoryManager>>#byteSizeOf: */ +sqInt +byteSizeOf(sqInt oop) +{ + sqInt fmt; + usqInt numBytes; + usqInt numSlots; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin numBytesOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + return numBytes - (fmt & 7); + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + return numBytes; + } + if (fmt >= (firstShortFormat())) { + return numBytes - (((fmt & 3) << 1)); + } + + /* fmt >= self firstLongFormat */ + return numBytes - (((fmt & 1) << 2)); +} + + +/* Answer the basic element size for the receiver. Answer 0 for immediates + and CompiledCode + (element size could be wordSize for literals or 1 for bytes, so its + indeterminable). Answer + wordSize for pointer objects. Otherwise answer the actual element size of + a bits container. */ + + /* SpurMemoryManager>>#bytesPerElement: */ +unsigned char +bytesPerElement(sqInt oop) +{ + unsigned char byteSizes[16] = + { wordSize(), wordSize(), wordSize(), wordSize(), + wordSize(), wordSize(), wordSize(), 0, /* forwarder */ wordSize(), + 8, + 4, 4, + 2, 2, 2, 2 }; + sqInt fmt; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + return 0; + } + return 1; + } + return byteSizes[fmt]; +} + + +/* Attempt to change the class of the receiver to the argument given that the + format of the receiver matches the format of the argument. If successful, + answer 0, otherwise answer an error code indicating the reason for + failure. + Fail if the format of the receiver is incompatible with the format of the + argument, or if the argument is a fixed class and the receiver's size + differs from the size + that an instance of the argument should have. */ + + /* SpurMemoryManager>>#changeClassOf:to: */ +static NoDbgRegParms sqInt +changeClassOfto(sqInt rcvr, sqInt argClass) +{ + sqInt classFormat; + sqInt classIndex; + sqInt fixedFields; + sqInt fmt; + sqInt instBytes; + sqInt instFormat; + usqInt instSlots; + sqInt newFormat; + usqInt numBytes; + usqInt numSlots; + + instBytes = 0; + instSlots = 0; + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + return PrimErrNoModification; + } + classFormat = ((longAt((void *)((argClass + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + fixedFields = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + classFormat = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + instFormat = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + + /* Fail for inability to access classIndex before making contexts snapshot-safe. */ + if (((classIndex = ensureBehaviorHash(argClass))) < 0) { + return -classIndex; + } + + /* Now check the instance for compatibility and compute odd bits if necessary. */ + if (classFormat <= 5 /* lastPointerFormat */) { + if (instFormat > 5 /* lastPointerFormat */) { + return PrimErrInappropriate; + } + if ((((/* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? (instSlots = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8) + : (instSlots = numSlots)))) < fixedFields) + || ((instSlots > fixedFields) + && (/* isFixedSizePointerFormat: */ + (classFormat <= (nonIndexablePointerFormat())) + || (classFormat == (ephemeronFormat()))))) { + return PrimErrBadReceiver; + } + if ((instFormat == (indexablePointersFormat())) + && (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + makeContextSnapshotSafe(rcvr); + } + newFormat = classFormat; + } + else { + /* Fail if the class's format is somehow invalid. */ + if (classFormat != (classFormatFromInstFormat(classFormat))) { + return PrimErrBadArgument; + } + + /* begin numBytesOf: */ + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + instBytes = numBytes - (fmt & 7); + goto l1; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + instBytes = numBytes; + goto l1; + } + if (fmt >= (firstShortFormat())) { + instBytes = numBytes - (((fmt & 3) << 1)); + goto l1; + } + + /* fmt >= self firstLongFormat */ + instBytes = numBytes - (((fmt & 1) << 2)); + /* end numBytesOf: */ +l1: + switch (classFormat) { + case sixtyFourBitIndexableFormat(): + if (((instBytes & 7) != 0)) { + return PrimErrBadReceiver; + } + newFormat = classFormat; + break; + case firstLongFormat(): + if (((instBytes & 3) != 0)) { + return PrimErrBadReceiver; + } + newFormat = classFormat + ((2 - (((usqInt)(instBytes)) >> 2)) & 1); + break; + case firstShortFormat(): + if (((instBytes & 1) != 0)) { + return PrimErrBadReceiver; + } + newFormat = classFormat + ((4 - (((usqInt)(instBytes)) >> 1)) & 3); + break; + case firstByteFormat(): + newFormat = classFormat + ((8 - instBytes) & 7); + break; + case firstCompiledMethodFormat(): + if (classFormat != (firstCompiledMethodFormat())) { + return PrimErrInappropriate; + } + newFormat = instFormat; + break; + default: + return PrimErrInappropriate; + } + } + + /* begin set:classIndexTo:formatTo: */ + assert(((classIndex >= 0) && (classIndex <= (classIndexMask())))); + assert(((newFormat >= 0) && (newFormat <= (formatMask())))); + longAtput((void *)(rcvr),((longAt((void *)(rcvr))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + (classIndex + ((((usqInt)(newFormat) << (formatShift())))))); + assert((numBytesOf(rcvr)) == ((classFormat <= (lastPointerFormat()) + ? instSlots * BytesPerOop + : instBytes))); + + /* ok */ + return 0; +} + + /* SpurMemoryManager>>#characterObjectOf: */ +sqInt +characterObjectOf(sqInt characterCode) +{ + return ((((usqInt)(characterCode) << (numTagBits())))) + (characterTag()); +} + + +/* Immediate characters are unsigned */ + + /* SpurMemoryManager>>#characterValueOf: */ +usqInt +characterValueOf(sqInt oop) +{ + return ((((usqInt)oop))) >> (numTagBits()); +} + + /* SpurMemoryManager>>#cheapAddressCouldBeInHeap: */ +static NoDbgRegParms sqInt +cheapAddressCouldBeInHeap(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((address & (BytesPerWord - 1)) == 0) + && ((oopisGreaterThanOrEqualTo(address, GIV(newSpaceStart))) + && (oopisLessThan(address, GIV(endOfMemory)))); +} + + +/* Assumes zero-based array indexing. */ + + /* SpurMemoryManager>>#checkedLongAt: */ +sqInt +checkedLongAt(sqInt byteAddress) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!(isInMemory(byteAddress))) { + warning("checkedLongAt bad address"); + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + return longAt((void *)(byteAddress)); +} + + +/* Perform an integrity/leak check using the heapMap. Assume + clearLeakMapAndMapAccessibleFreeSpace has set a bit at each free chunk's + header. Scan all objects in the heap checking that no pointer points + to a free chunk and that all free chunks that refer to others refer to + marked chunks. Answer if all checks pass. */ + + /* SpurMemoryManager>>#checkHeapFreeSpaceIntegrity */ +static sqInt +checkHeapFreeSpaceIntegrity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt chunkBytes; + sqInt fi; + sqInt fieldOop; + sqInt followingWord; + usqInt followingWordAddress; + sqInt i; + usqInt numSlots; + sqInt objOop; + sqInt ok; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + sqInt toDoLimit; + sqInt toDoLimit1; + usqInt total; + + ok = 1; + total = 0; + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + if (GIV(freeLists)[i]) { + if (!(heapMapAtWord(pointerForOop(GIV(freeLists)[i])))) { + fprintf(GIV(transcript), + "leak in free list %d to non-free %p\n", + ((int) i), + ((void *)(GIV(freeLists)[i]))); + ok = 0; + } + } + } + + /* Excuse the duplication but performance is at a premium and we avoid + some tests by splitting the newSpace and oldSpace enumerations. */ + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l2; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + fprintf(GIV(transcript), + "young object %p is free\n", + ((void *)objOop)); + ok = 0; + } + else { + if (objOop != GIV(freeSpaceCheckOopToIgnore)) { + toDoLimit = (numPointerSlotsOf(objOop)) - 1; + for (fi = 0; fi <= toDoLimit; fi += 1) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + if ((!(fieldOop & (tagMask())))) { + if (heapMapAtWord(pointerForOop(fieldOop))) { + fprintf(GIV(transcript), + "object leak in %p @ %d = %p is free\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + /* end allNewSpaceEntitiesDo: */ +l2: + + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + if (!(heapMapAtWord(pointerForOop(objOop)))) { + fprintf(GIV(transcript), + "leak in free chunk %p is unmapped?! \n", + ((void *)objOop)); + ok = 0; + } + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) == 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ 0 = %p is unmapped\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + chunkBytes = bytesInBody(objOop); + + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + if (!(chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */))) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (1U << (shiftForWord())))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) == 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ 0 = %p is unmapped\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + if ((bytesInBody(objOop)) >= 0x200 /* (numFreeLists * #allocationUnit) */) { + for (fi = 2 /* freeChunkParentIndex */; fi <= 4 /* freeChunkLargerIndex */; fi += 1) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) == 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ %d = %p is unmapped\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + } + total += bytesInBody(objOop); + } + else { + if (objOop != GIV(freeSpaceCheckOopToIgnore)) { + toDoLimit1 = (numPointerSlotsOf(objOop)) - 1; + for (fi = 0; fi <= toDoLimit1; fi += 1) { + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + assert(fi == 0); + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + } + else { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + } + + /* We keep #fetchPointer:ofObject: API here for assertions */ + if ((!(fieldOop & (tagMask())))) { + if (heapMapAtWord(pointerForOop(fieldOop))) { + fprintf(GIV(transcript), + "object leak in %p @ %d = %p is free\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3:; + } + if (total != GIV(totalFreeOldSpace)) { + fprintf(GIV(transcript), + "incorrect totalFreeOldSpace; expected %d found %d\n", + ((int) GIV(totalFreeOldSpace)), + ((int) total)); + ok = 0; + } + return ok; +} + + +/* Perform an integrity/leak check using the heapMap. Assume + clearLeakMapAndMapAccessibleObjects has set a bit at each (non-free) + object's header. Scan all objects in the heap checking that every + pointer points to a header. Scan the rememberedSet, remapBuffer and + extraRootTable checking + that every entry is a pointer to a header. Check that the number of roots + is correct and that all + rememberedSet entries have their isRemembered: flag set. Answer if all + checks pass. */ + + /* SpurMemoryManager>>#checkHeapIntegrity:classIndicesShouldBeValid: */ +static NoDbgRegParms sqInt +checkHeapIntegrityclassIndicesShouldBeValid(sqInt excludeUnmarkedObjs, sqInt classIndicesShouldBeValid) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt chunkBytes; + sqInt classIndex; + sqInt classIndexSqInt; + sqInt classOop; + sqInt classTablePage; + sqInt containsYoung; + sqInt fi; + sqInt fieldIndex; + sqInt fieldOop; + sqInt followingWord; + usqInt followingWordAddress; + sqInt freeListOop; + sqInt i; + sqInt iSqInt; + sqInt numOnThisPage; + sqInt numRememberedObjectsInHeap; + usqInt numSlots; + sqInt obj; + sqInt objOop; + sqInt objStackPage; + sqInt ok; + sqInt prevObj; + sqInt prevPrevObj; + sqInt ri; + usqInt start; + sqInt toDoLimit; + sqInt toDoLimit1; + + ok = 1; + numRememberedObjectsInHeap = 0; + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + if ((freeListOop = GIV(freeLists)[i])) { + if (oopisGreaterThanOrEqualToandLessThan(freeListOop, GIV(oldSpaceStart), GIV(endOfMemory))) { + if (heapMapAtWord(pointerForOop(freeListOop))) { + fprintf(GIV(transcript), + "leak in free list %d to non-free %p\n", + ((int) i), + ((void *)freeListOop)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "leak in free list %d to out-of-bounds %p\n", + ((int) i), + ((void *)freeListOop)); + ok = 0; + } + } + } + + /* Excuse the duplication but performance is at a premium and we avoid + some tests by splitting the newSpace and oldSpace enumerations. */ + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l6; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + fprintf(GIV(transcript), + "young object %p is free\n", + ((void *)objOop)); + ok = 0; + } + else { + if (!((!(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) + && (excludeUnmarkedObjs))) { + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) { + fprintf(GIV(transcript), + "young object %p is remembered\n", + ((void *)objOop)); + ok = 0; + } + } + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isInHeapBounds: */ + (oopisGreaterThanOrEqualTo(fieldOop, (!(minCogMethodAddress()) + ? GIV(nilObj) + : minCogMethodAddress()))) + && (oopisLessThan(fieldOop, GIV(endOfMemory)))) { + if (!(heapMapAtWord(pointerForOop(fieldOop)))) { + fprintf(GIV(transcript), + "object leak in forwarder %p to unmapped %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "object leak in forwarder %p to out-of-bounds %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + else { + classIndexSqInt = (classIndex = (longAt((void *)(objOop))) & (classIndexMask())); + + /* begin classOrNilAtIndex: */ + assert((classIndexSqInt <= (tagMask())) + || (classIndexSqInt >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndexSqInt)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classOop = GIV(nilObj); + goto l1; + } + fieldIndex = classIndexSqInt & ((1U << (classTableMajorIndexShift())) - 1); + classOop = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classOrNilAtIndex: */ +l1: + if (classIndicesShouldBeValid + && ((classOop == GIV(nilObj)) + && (!(((longAt((void *)(objOop))) & (classIndexMask())) <= (lastClassIndexPun()))))) { + fprintf(GIV(transcript), + "object leak in %p invalid class index %p -> nilObj\n", + ((void *)objOop), + ((void *)classIndex)); + ok = 0; + } + toDoLimit = (numPointerSlotsOf(objOop)) - 1; + for (fi = 0; fi <= toDoLimit; fi += 1) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + if ((!(fieldOop & (tagMask())))) { + if (/* isInHeapBounds: */ + (oopisGreaterThanOrEqualTo(fieldOop, (!(minCogMethodAddress()) + ? GIV(nilObj) + : minCogMethodAddress()))) + && (oopisLessThan(fieldOop, GIV(endOfMemory)))) { + if (!(heapMapAtWord(pointerForOop(fieldOop)))) { + fprintf(GIV(transcript), + "object leak in %p @ %d = %p\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "object leak in %p to out-of-bounds %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l5; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l5:; + } + /* end allNewSpaceEntitiesDo: */ +l6: + + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + if (heapMapAtWord(pointerForOop(objOop))) { + fprintf(GIV(transcript), + "leak in free chunk %p is mapped?! \n", + ((void *)objOop)); + ok = 0; + } + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) != 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ 0 = %p is mapped\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + chunkBytes = bytesInBody(objOop); + + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + if (!(chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */))) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (1U << (shiftForWord())))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) != 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ 1 = %p is mapped\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + if ((bytesInBody(objOop)) >= 0x200 /* (numFreeLists * #allocationUnit) */) { + for (fi = 2 /* freeChunkParentIndex */; fi <= 4 /* freeChunkLargerIndex */; fi += 1) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) != 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ %d = %p is mapped\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + } + } + else { + if (!(excludeUnmarkedObjs + && (!(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)))) { + containsYoung = 0; + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) { + numRememberedObjectsInHeap += 1; + + /* begin isInRememberedSet: */ + for (iSqInt = 0; iSqInt < GIV(rememberedSetSize); iSqInt += 1) { + if ((GIV(rememberedSet)[iSqInt]) == objOop) { + goto l2; + } + } + fprintf(GIV(transcript), + "remembered object %p is not in remembered table\n", + ((void *)objOop)); + ok = 0; +l2:; + } + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isInHeapBounds: */ + (oopisGreaterThanOrEqualTo(fieldOop, (!(minCogMethodAddress()) + ? GIV(nilObj) + : minCogMethodAddress()))) + && (oopisLessThan(fieldOop, GIV(endOfMemory)))) { + if (!(heapMapAtWord(pointerForOop(fieldOop)))) { + fprintf(GIV(transcript), + "object leak in forwarder %p to unmapped %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "object leak in forwarder %p to out-of-bounds %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + if (/* isReallyYoung: */ + ((!(fieldOop & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(fieldOop)), + (oopisLessThan(fieldOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(fieldOop, GIV(newSpaceStart)))))) { + containsYoung = 1; + } + } + else { + classIndexSqInt = (classIndex = (longAt((void *)(objOop))) & (classIndexMask())); + + /* begin classOrNilAtIndex: */ + assert((classIndexSqInt <= (tagMask())) + || (classIndexSqInt >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndexSqInt)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classOop = GIV(nilObj); + goto l3; + } + fieldIndex = classIndexSqInt & ((1U << (classTableMajorIndexShift())) - 1); + classOop = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classOrNilAtIndex: */ +l3: + if (classIndicesShouldBeValid + && ((classOop == GIV(nilObj)) + && (classIndex > (lastClassIndexPun())))) { + fprintf(GIV(transcript), + "object leak in %p invalid class index %p -> nilObj\n", + ((void *)objOop), + ((void *)classIndex)); + ok = 0; + } + toDoLimit1 = (numPointerSlotsOf(objOop)) - 1; + for (fi = 0; fi <= toDoLimit1; fi += 1) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + if ((!(fieldOop & (tagMask())))) { + if (/* isInHeapBounds: */ + (oopisGreaterThanOrEqualTo(fieldOop, (!(minCogMethodAddress()) + ? GIV(nilObj) + : minCogMethodAddress()))) + && (oopisLessThan(fieldOop, GIV(endOfMemory)))) { + if (!(heapMapAtWord(pointerForOop(fieldOop)))) { + fprintf(GIV(transcript), + "object leak in %p @ %d = %p\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "object leak in %p to out-of-bounds %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + + /* don't be misled by CogMethods; they appear to be young, but they're not */ + if (/* isReallyYoung: */ + ((!(fieldOop & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(fieldOop)), + (oopisLessThan(fieldOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(fieldOop, GIV(newSpaceStart)))))) { + containsYoung = 1; + } + } + } + } + if (containsYoung) { + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + fprintf(GIV(transcript), + "unremembered object %p contains young oop(s)\n", + ((void *)objOop)); + ok = 0; + } + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l7; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l7:; + } + if (numRememberedObjectsInHeap != GIV(rememberedSetSize)) { + fprintf(GIV(transcript), + "root count mismatch. #heap roots %d; #roots %d\n", + ((int) numRememberedObjectsInHeap), + ((int) GIV(rememberedSetSize))); + + /* But the system copes with overflow... */ + } + + /* begin rememberedSetWithIndexDo: */ + for (iSqInt = 0; iSqInt < GIV(rememberedSetSize); iSqInt += 1) { + obj = GIV(rememberedSet)[iSqInt]; + if (obj & (BytesPerWord - 1)) { + fprintf(GIV(transcript), + "misaligned oop in remembered set @ %d = %p\n", + ((int) iSqInt), + ((void *)obj)); + ok = 0; + } + else { + if (heapMapAtWord(pointerForOop(obj))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + fprintf(GIV(transcript), + "non-root in remembered set @ %d = %p\n", + ((int) iSqInt), + ((void *)obj)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "object leak in remembered set @ %d = %p\n", + ((int) iSqInt), + ((void *)obj)); + ok = 0; + } + } + } + + /* begin objStack:do: */ + if (GIV(mournQueue) == GIV(nilObj)) { + goto l4; + } + eassert(isValidObjStack(GIV(mournQueue))); + objStackPage = GIV(mournQueue); + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + obj = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (obj & (BytesPerWord - 1)) { + fprintf(GIV(transcript), + "misaligned oop in mournQueue @ %d in %p = %p\n", + ((int) iSqInt), + ((void *)objStackPage), + ((void *)obj)); + ok = 0; + } + else { + if (!(excludeUnmarkedObjs + && (!(((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)))) { + if (!(heapMapAtWord(pointerForOop(obj)))) { + fprintf(GIV(transcript), + "object leak in mournQueue @ %d in %p = %p\n", + ((int) iSqInt), + ((void *)objStackPage), + ((void *)obj)); + ok = 0; + } + } + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l4: + for (ri = 1; ri <= GIV(remapBufferCount); ri += 1) { + obj = GIV(remapBuffer)[ri]; + if (obj & (BytesPerWord - 1)) { + fprintf(GIV(transcript), + "misaligned remapRoot @ %d = %p\n", + ((int) ri), + ((void *)obj)); + ok = 0; + } + else { + if (!(heapMapAtWord(pointerForOop(obj)))) { + fprintf(GIV(transcript), + "object leak in remapRoots @ %d = %p\n", + ((int) ri), + ((void *)obj)); + ok = 0; + } + } + } + for (ri = 1; ri <= GIV(extraRootCount); ri += 1) { + obj = (GIV(extraRoots)[ri])[0]; + if (obj & (BytesPerWord - 1)) { + fprintf(GIV(transcript), + "misaligned extraRoot @ %d => %p\n", + ((int) ri), + ((void *)obj)); + ok = 0; + } + else { + if (!(heapMapAtWord(pointerForOop(obj)))) { + fprintf(GIV(transcript), + "object leak in extraRoots @ %d => %p\n", + ((int) ri), + ((void *)obj)); + ok = 0; + } + } + } + return ok; +} + + +/* Verify that the given oop is legitimate. Check address, header, and size + but not class. + Answer true if OK. Otherwise print reason and answer false. */ +/* useful for debugging */ + + /* SpurMemoryManager>>#checkOkayOop: */ +sqInt +checkOkayOop(usqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt fmt; + usqLong unusedBits; + sqInt unusedBitsInYoungObjects; + + + /* address and size checks */ + if (((oop & (tagMask())) != 0)) { + return 1; + } + if (!(addressCouldBeObj(oop))) { + fprintf(GIV(transcript), + "oop %p is not a valid address", + ((void *)oop)); + return 0; + } + if (!(oopisLessThanOrEqualTo(addressAfter(oop), GIV(endOfMemory)))) { + fprintf(GIV(transcript), + "oop %p size would make it extend beyond the end of memory", + ((void *)oop)); + return 0; + } + + /* header type checks */ + if (!(((classIndex = (longAt((void *)(oop))) & (classIndexMask()))) >= (firstClassIndexPun()))) { + fprintf(GIV(transcript), + "oop %p is a free chunk, or bridge, not an object", + ((void *)oop)); + return 0; + } + if (((byteAt((void *)(oop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) + && ((byteAt((void *)((oop - BaseHeaderSize) + (numSlotsFieldByteOffset())))) != (numSlotsMask()))) { + fprintf(GIV(transcript), + "oop %p header has overflow header word, but overflow word does not have a saturated numSlots field", + ((void *)oop)); + return 0; + } + + /* format check */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if ((fmt == 6) || (fmt == 8)) { + fprintf(GIV(transcript), + "oop %p has an unknown format type", + ((void *)oop)); + return 0; + } + if ((fmt == (forwardedFormat())) != (classIndex == (isForwardedObjectClassIndexPun()))) { + fprintf(GIV(transcript), + "oop %p has mis-matched format/classIndex fields; only one of them is the isForwarded value", + ((void *)oop)); + return 0; + } + + /* specific header bit checks */ + unusedBits = (0x400000) | (0x40000000000000LL); + if ((long64At((void *)(oop))) & unusedBits) { + fprintf(GIV(transcript), + "oop %p has some unused header bits set; should be zero", + ((void *)oop)); + return 0; + } + unusedBitsInYoungObjects = (1U << (unusedBitShift())) | ((1U << (pinnedBitShift())) | (1U << (rememberedBitShift()))); + if ((longAt((void *)(oop))) & unusedBitsInYoungObjects) { + fprintf(GIV(transcript), + "oop %p has some header bits unused in young objects set; should be zero", + ((void *)oop)); + return 0; + } + return 1; +} + + +/* Attempt to verify that the given obj has a reasonable behavior. The class + must be a + valid, non-integer oop and must not be nilObj. It must be a pointers + object with three + or more fields. Finally, the instance specification field of the behavior + must match that + of the instance. If OK answer true. If not, print reason and answer false. */ +/* useful for debugging */ + + /* SpurMemoryManager>>#checkOopHasOkayClass: */ +sqInt +checkOopHasOkayClass(usqInt obj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + usqInt objClass; + sqInt objFormat; + + if (!(checkOkayOop(obj))) { + return 0; + } + objClass = ((usqInt) (fetchClassOfNonImm(obj))); + if (((objClass & (tagMask())) != 0)) { + fprintf(GIV(transcript), + "obj %p an immediate is not a valid class or behavior\n", + ((void *)obj)); + return 0; + } + if (!(okayOop(objClass))) { + fprintf(GIV(transcript), + "obj %p class obj is not ok\n", + ((void *)obj)); + return 0; + } + if (!((((byteAt((void *)(objClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(objClass)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(objClass + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objClass - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= 3))) { + fprintf(GIV(transcript), + "obj %p a class (behavior) must be a pointers object of size >= 3\n", + ((void *)obj)); + return 0; + } + objFormat = (/* isBytes: */ + ((!(obj & (tagMask())))) + && (((byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())) + ? ((((byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask())) | 7) - 7) + : (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask())); + if (((((usqInt)((((longAt((void *)((objClass + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) != objFormat) { + fprintf(GIV(transcript), + "obj %p and its class (behavior) formats differ\n", + ((void *)obj)); + return 0; + } + return 1; +} + + /* SpurMemoryManager>>#checkOopIntegrity:named: */ +static NoDbgRegParms sqInt +checkOopIntegritynamed(sqInt obj, char *name) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((oopisLessThan(obj, GIV(endOfMemory))) + && ((heapMapAtWord(pointerForOop(obj))) != 0)) { + return 1; + } + fprintf(GIV(transcript), + "%s leak %p\n", + name, + ((void *)obj)); + return 0; +} + + /* SpurMemoryManager>>#checkOopIntegrity:named:index: */ +static NoDbgRegParms sqInt +checkOopIntegritynamedindex(sqInt obj, char *name, sqInt i) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((oopisLessThan(obj, GIV(endOfMemory))) + && ((heapMapAtWord(pointerForOop(obj))) != 0)) { + return 1; + } + fprintf(GIV(transcript), + "%s leak @ %d = %p\n", + name, + ((int) i), + ((void *)obj)); + return 0; +} + + /* SpurMemoryManager>>#classAlien */ +sqInt +classAlien(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classArray */ +sqInt +classArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classAtIndex: */ +sqInt +classAtIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt fieldIndex; + + assert((classIndex >= 0) + && ((classIndex <= (tagMask())) + || ((classIndex >= (arrayClassIndexPun())) + && (classIndex <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return null; + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + +/* for become & GC of classes */ + + /* SpurMemoryManager>>#classAtIndex:put: */ +static NoDbgRegParms sqInt +classAtIndexput(sqInt classIndex, sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt fieldIndex; + + assert((classIndex <= (tagMask())) + || (classIndex >= (arrayClassIndexPun()))); + assert((objOop == GIV(nilObj)) + || ((((rawHashBitsOf(objOop)) == classIndex) + || ((classAtIndex(rawHashBitsOf(objOop))) == objOop)) + && (objCouldBeClassObj(objOop)))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + error("attempt to add class to empty page"); + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, classTablePage, objOop)); + assert(isNonImmediate(classTablePage)); + if (oopisGreaterThanOrEqualTo(classTablePage, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objOop & (tagMask())))) + && (oopisLessThan(objOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(classTablePage + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(classTablePage); + } + } + } + + /* most stores into young objects */ + return longAtput((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),objOop); +} + + /* SpurMemoryManager>>#classBitmap */ +sqInt +classBitmap(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassBitmap) << (shiftForWord())))))); +} + + +/* a.k.a. self fetchPointer: ClassByteArrayCompactIndex ofObject: + classTableFirstPage + */ + + /* SpurMemoryManager>>#classByteArray */ +sqInt +classByteArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteArray) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classCharacter */ +sqInt +classCharacter(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassCharacter) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classDoubleByteArray */ +sqInt +classDoubleByteArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOop; + int hash; + + /* begin accessIntegerClass:withValidationFlag: */ + hash = 0; + classOop = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassDoubleByteArray) << (shiftForWord())))))); + if (((GIV(validatedIntegerClassFlags) & ValidatedClassDoubleByteArrayFlag) != 0)) { + return classOop; + } + if ((((classOop & (tagMask())) != 0)) + || ((((hash = (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask()))) == 0) + || ((classAtIndex(hash)) != classOop))) { + return GIV(nilObj); + } + GIV(validatedIntegerClassFlags) = GIV(validatedIntegerClassFlags) | ValidatedClassDoubleByteArrayFlag; + return classOop; +} + + /* SpurMemoryManager>>#classDoubleWordArray */ +sqInt +classDoubleWordArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOop; + int hash; + + /* begin accessIntegerClass:withValidationFlag: */ + hash = 0; + classOop = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassDoubleWordArray) << (shiftForWord())))))); + if (((GIV(validatedIntegerClassFlags) & ValidatedClassDoubleWordArrayFlag) != 0)) { + return classOop; + } + if ((((classOop & (tagMask())) != 0)) + || ((((hash = (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask()))) == 0) + || ((classAtIndex(hash)) != classOop))) { + return GIV(nilObj); + } + GIV(validatedIntegerClassFlags) = GIV(validatedIntegerClassFlags) | ValidatedClassDoubleWordArrayFlag; + return classOop; +} + + /* SpurMemoryManager>>#classExternalAddress */ +sqInt +classExternalAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassExternalAddress) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classExternalData */ +sqInt +classExternalData(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassExternalData) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classExternalFunction */ +sqInt +classExternalFunction(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassExternalFunction) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classExternalLibrary */ +sqInt +classExternalLibrary(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassExternalLibrary) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classExternalStructure */ +sqInt +classExternalStructure(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassExternalStructure) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classFloat */ +sqInt +classFloat(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassFloat) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classFloat32Array */ +sqInt +classFloat32Array(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(nilObj); +} + + /* SpurMemoryManager>>#classFloat64Array */ +sqInt +classFloat64Array(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(nilObj); +} + + +/* No need to check this; classAtIndex: has a stricter assert: + self assert: classIndex ~= self isForwardedObjectClassIndexPun. */ + + /* SpurMemoryManager>>#classForClassTag: */ +static NoDbgRegParms sqInt +classForClassTag(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt fieldIndex; + + /* begin classAtIndex: */ + assert((classIndex >= 0) + && ((classIndex <= (tagMask())) + || ((classIndex >= (arrayClassIndexPun())) + && (classIndex <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return null; + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + +/* 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState + et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + + /* SpurMemoryManager>>#classFormatFromInstFormat: */ +static NoDbgRegParms sqInt +classFormatFromInstFormat(sqInt instFormat) +{ + if (instFormat >= (firstByteFormat())) { + return instFormat & -8; + } + + /* this is likely the common case */ + if (instFormat <= (sixtyFourBitIndexableFormat())) { + return instFormat; + } + if (instFormat < (firstShortFormat())) { + return instFormat & -2; + } + return instFormat & -4; +} + + +/* 22-bit class mask => ~ 4M classes */ + + /* SpurMemoryManager>>#classIndexFieldWidth */ +static sqInt +classIndexFieldWidth(void) +{ + return 22; +} + + +/* Answer objOop's classIndex from the object header. + A note on performance: + This routine uses longAt:, which does a 32-bit load on the 32-bit system, + and a 64-bit load + on the 64-bit system. Since the only 64-bit implementation(s) is/are + little-endian, and + since all of UNUSED,isPinned,isRemembered,format,isImmutable and + classIndex fit within + the least significant 32-bits we could use long32At: to access these, in + the hope that the + 32-bit access will be quicker on 64-bits by virtue of fetching half the + bits. But experiments + show that doing this does not produce any increase; at least any signal is + lost in the noise. */ + + /* SpurMemoryManager>>#classIndexOf: */ +sqInt +classIndexOf(sqInt objOop) +{ + return (longAt((void *)(objOop))) & (classIndexMask()); +} + + /* SpurMemoryManager>>#classLargeNegativeInteger */ +sqInt +classLargeNegativeInteger(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin knownClassAtIndex: */ + assert(((ClassLargeNegativeIntegerCompactIndex >= 1) && (ClassLargeNegativeIntegerCompactIndex <= (classTablePageSize())))); + return longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassLargeNegativeIntegerCompactIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classLargePositiveInteger */ +sqInt +classLargePositiveInteger(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin knownClassAtIndex: */ + assert(((ClassLargePositiveIntegerCompactIndex >= 1) && (ClassLargePositiveIntegerCompactIndex <= (classTablePageSize())))); + return longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassLargePositiveIntegerCompactIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classOrNilAtIndex: */ +sqInt +classOrNilAtIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt fieldIndex; + + assert((classIndex <= (tagMask())) + || (classIndex >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return GIV(nilObj); + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classPoint */ +sqInt +classPoint(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassPoint) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classSemaphore */ +sqInt +classSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classSmallInteger */ +sqInt +classSmallInteger(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSmallInteger) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classString */ +sqInt +classString(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord())))))); +} + + +/* 1024 entries per page (2^10); 22 bit classIndex implies 2^12 pages */ +/* self basicNew classTableMinorIndexMask */ + + /* SpurMemoryManager>>#classTableMinorIndexMask */ +sqInt +classTableMinorIndexMask(void) +{ + return (1U << (classTableMajorIndexShift())) - 1; +} + + +/* 1024 entries per page (2^10); 22 bit classIndex implies 2^12 pages */ +/* self basicNew classTablePageSize */ + + /* SpurMemoryManager>>#classTablePageSize */ +sqInt +classTablePageSize(void) +{ + return 1U << (classTableMajorIndexShift()); +} + + +/* For Cogit & bootstrap */ + + /* SpurMemoryManager>>#classTableRootObj */ +sqInt +classTableRootObj(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(hiddenRootsObj); +} + + +/* Answer the number of slots for class table pages in the hidden root + object. + */ + + /* SpurMemoryManager>>#classTableRootSlots */ +static sqInt +classTableRootSlots(void) +{ + return 1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift())); +} + + +/* Answer the classObj's identityHash to use as a tag in the first-level + method lookup cache. + */ + + /* SpurMemoryManager>>#classTagForClass: */ +sqInt +classTagForClass(sqInt classObj) +{ + sqInt err; + int hash; + + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + return ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); +} + + /* SpurMemoryManager>>#classUnsafeAlien */ +sqInt +classUnsafeAlien(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassUnsafeAlien) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classWordArray */ +sqInt +classWordArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOop; + int hash; + + /* begin accessIntegerClass:withValidationFlag: */ + hash = 0; + classOop = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassWordArray) << (shiftForWord())))))); + if (((GIV(validatedIntegerClassFlags) & ValidatedClassWordArrayFlag) != 0)) { + return classOop; + } + if ((((classOop & (tagMask())) != 0)) + || ((((hash = (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask()))) == 0) + || ((classAtIndex(hash)) != classOop))) { + return GIV(nilObj); + } + GIV(validatedIntegerClassFlags) = GIV(validatedIntegerClassFlags) | ValidatedClassWordArrayFlag; + return classOop; +} + + +/* Perform an integrity/leak check using the heapMap. Set a bit at each free + chunk's header. */ + + /* SpurMemoryManager>>#clearLeakMapAndMapAccessibleFreeSpace */ +static void +clearLeakMapAndMapAccessibleFreeSpace(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + + clearHeapMap(); + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + heapMapAtWordPut(pointerForOop(objOopSqInt), 1); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } +} + + /* SpurMemoryManager>>#cloneInOldSpace:forPinning: */ +static NoDbgRegParms sqInt +cloneInOldSpaceforPinning(sqInt objOop, sqInt forPinning) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cogMethodOrMethodHeader; + sqInt fmt; + int hash; + sqInt hasYoung; + sqInt i; + sqInt newObj; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt rawHeader; + sqInt referent; + sqInt valuePointer; + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (forPinning) { + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), fmt, (longAt((void *)(objOop))) & (classIndexMask())); + } + else { + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), fmt, (longAt((void *)(objOop))) & (classIndexMask())); + } + if (!newObj) { + return 0; + } + if (fmt <= 5 /* lastPointerFormat */) { + hasYoung = 0; + for (i = 0; i < numSlots; i += 1) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + } + if (((!(oop & (tagMask())))) + && ((/* begin isYoungObject: */ + assert(isNonImmediate(oop)), + oopisLessThan(oop, GIV(oldSpaceStart))))) { + hasYoung = 1; + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(i, newObj, oop)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),oop); + } + if (hasYoung) { + remember(newObj); + } + } + else { + for (i = 0; i < numSlots; i += 1) { + valuePointer = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(i, newObj, valuePointer)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + if (fmt >= (firstCompiledMethodFormat())) { + /* begin maybeFixClonedCompiledMethod: */ + assert(isOopCompiledMethod(newObj)); + rawHeader = longAt((void *)((newObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + cogMethodOrMethodHeader = ((((CogMethod *) rawHeader))->methodHeader); + + /* begin rawHeaderOf:put: */ + assert(isCompiledMethodHeader(headerWhileForwardingOf(newObj))); + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(HeaderIndex, newObj, cogMethodOrMethodHeader)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))),cogMethodOrMethodHeader); + } + if (((/* begin isYoungObject: */ + assert(isNonImmediate(objOop)), + oopisLessThan(objOop, GIV(oldSpaceStart)))) + || (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0)) { + remember(newObj); + } + } + } + if ((hash = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()))) { + /* begin setHashBitsOf:to: */ + long32Atput((void *)(newObj + 4),((((long32At((void *)(newObj + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(objOop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* begin setIsImmutableOf:to: */ + longAtput((void *)(newObj),(longAt((void *)(newObj))) | (1U << (immutableBitShift()))); + } + return newObj; +} + + /* SpurMemoryManager>>#cloneObject: */ +sqInt +cloneObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt cogMethodOrMethodHeader; + sqInt fmt; + sqInt hasYoung; + sqInt i; + sqInt newObj; + usqInt newObjUsqInt; + usqInt numBytes; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt rawHeader; + sqInt referent; + sqInt valuePointer; + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (numSlots > ((1U << (fixedFieldsFieldWidth())) - 1)) { + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + + /* begin allocateSlotsInOldSpace:format:classIndex: */ + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), fmt, classIndex); + } + else { + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if (((numSlots) >> 56) > 0) { + newObj = null; + goto l1; + } + newObjUsqInt = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObjUsqInt = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, fmt, classIndex); + goto l1; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObjUsqInt),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(fmt) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObjUsqInt),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(fmt) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObjUsqInt % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newObj = newObjUsqInt; + /* end allocateSlots:format:classIndex: */ +l1:; + } + if (newObj) { + if (fmt <= 5 /* lastPointerFormat */) { + hasYoung = 0; + for (i = 0; i < numSlots; i += 1) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(oop & (tagMask())))) { + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + } + if (((!(oop & (tagMask())))) + && ((/* begin isYoungObject: */ + assert(isNonImmediate(oop)), + oopisLessThan(oop, GIV(oldSpaceStart))))) { + hasYoung = 1; + } + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(i, newObj, oop)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),oop); + } + if (hasYoung + && (!((/* begin isYoungObject: */ + assert(isNonImmediate(newObj)), + oopisLessThan(newObj, GIV(oldSpaceStart)))))) { + remember(newObj); + } + } + else { + for (i = 0; i < numSlots; i += 1) { + valuePointer = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(i, newObj, valuePointer)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + if (fmt >= (firstCompiledMethodFormat())) { + /* begin maybeFixClonedCompiledMethod: */ + assert(isOopCompiledMethod(newObj)); + rawHeader = longAt((void *)((newObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + cogMethodOrMethodHeader = ((((CogMethod *) rawHeader))->methodHeader); + + /* begin rawHeaderOf:put: */ + assert(isCompiledMethodHeader(headerWhileForwardingOf(newObj))); + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(HeaderIndex, newObj, cogMethodOrMethodHeader)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))),cogMethodOrMethodHeader); + } + if (((/* begin isOldObject: */ + assert(isNonImmediate(newObj)), + oopisGreaterThanOrEqualTo(newObj, GIV(oldSpaceStart)))) + && (((/* begin isYoungObject: */ + assert(isNonImmediate(objOop)), + oopisLessThan(objOop, GIV(oldSpaceStart)))) + || (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0))) { + remember(newObj); + } + } + } + } + return newObj; +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. + Copy objOop into the segment beginning at segAddr, and forward it to the + copy, saving its first field in savedFirstField, and setting its marked + bit to indicate it has + been copied. If it is a class in the class table, set the copy's hash to 0 + for reassignment + on load, and mark it as a class by setting its isRemembered bit. + Answer the next segmentAddr if successful. Answer an appropriate error + code if not */ +/* Copy the object... */ + + /* SpurMemoryManager>>#copyObj:toAddr:stopAt:savedFirstFields:index: */ +static NoDbgRegParms sqInt +copyObjtoAddrstopAtsavedFirstFieldsindex(sqInt objOop, sqInt segAddr, sqInt endSeg, sqInt savedFirstFields, sqInt i) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bodySize; + sqInt contextSize; + sqInt copy; + sqInt fmt; + int hash; + sqInt header; + sqInt headerSqInt; + sqInt iSqInt; + sqInt methodHeader; + usqInt numLiterals; + usqInt numMediatedSlots; + usqInt numSlots; + sqInt oop; + sqInt sp; + sqInt toDoLimit; + sqInt valuePointer; + + assert(!((isCopiedIntoSegment(objOop)))); + bodySize = bytesInBody(objOop); + if (oopisGreaterThanOrEqualTo(segAddr + bodySize, endSeg)) { + return PrimErrWritePastObject; + } + memcpy(((void *)segAddr), ((void *)(/* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop))), bodySize); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(segAddr + (numSlotsFieldByteOffset()))); + copy = (numSlots == (numSlotsMask()) + ? segAddr + BaseHeaderSize + : segAddr); + + /* Clear remembered, mark bits of all headers copied into the segment (except classes) */ + setIsRememberedOfto(copy, 0); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(copy))); + byteAtput((void *)(copy + (markBitsByteOffset())),(byteAt((void *)(copy + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + + /* Make any objects with hidden dynamic state (contexts, methods) look like normal objects. */ + + /* begin ifAProxy:updateCopy: */ + if (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + if (/* isStillMarriedContext: */ + (((((longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(objOop)))) { + /* begin numSlotsOfMarriedContext: */ + contextSize = stackPointerIndexForFrame(frameOfMarriedContext(objOop)); + assert((CtxtTempFrameStart + contextSize) <= (numSlotsOf(objOop))); + numMediatedSlots = CtxtTempFrameStart + contextSize; + for (iSqInt = 0; iSqInt < numMediatedSlots; iSqInt += 1) { + oop = fetchPointerofMarriedContext(iSqInt, objOop); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(copy)) + && (!(isForwarded(copy)))); + assert(validStorePointerUncheckedArgs(iSqInt, copy, oop)); + longAtput((void *)((copy + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord()))))),oop); + } + } + else { + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + numMediatedSlots = CtxtTempFrameStart + contextSize; + goto l2; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numMediatedSlots = (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l2; + } + if (fmt == (forwardedFormat())) { + numMediatedSlots = 1; + goto l2; + } + if (fmt < (firstCompiledMethodFormat())) { + numMediatedSlots = 0; + goto l2; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numMediatedSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l2:; + } + + /* And make sure to nil the slots beyond the top of stack... */ + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (iSqInt = numMediatedSlots; iSqInt <= toDoLimit; iSqInt += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(copy)) + && (!(isForwarded(copy)))); + assert(validStorePointerUncheckedArgs(iSqInt, copy, GIV(nilObj))); + longAtput((void *)((copy + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord()))))),GIV(nilObj)); + } + } + if (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + methodHeader = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(copy)) + && (!(isForwarded(copy)))); + assert(validStorePointerUncheckedArgs(HeaderIndex, copy, ((((CogMethod *) methodHeader))->methodHeader))); + longAtput((void *)((copy + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))),((((CogMethod *) methodHeader))->methodHeader)); + } + } + + /* If the object is a class, zero its identityHash (which is its classIndex) and set its + isRemembered bit. It will be assigned a new hash and entered into the table on load. */ + hash = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()); + if ((hash > (lastClassIndexPun())) + && ((classOrNilAtIndex(hash)) == objOop)) { + /* begin setHashBitsOf:to: */ + long32Atput((void *)(copy + 4),((((long32At((void *)(copy + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (0)); + setIsRememberedOfto(copy, 1); + } + + /* Now forward the object to its copy in the segment. */ + valuePointer = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(savedFirstFields)) + && (!(isForwarded(savedFirstFields)))); + assert(validStorePointerUncheckedArgs(i, savedFirstFields, valuePointer)); + longAtput((void *)((savedFirstFields + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(0, objOop, copy)); + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),copy); + + /* begin markAsCopiedIntoSegment: */ + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* Answer the new end of segment */ + return segAddr + bodySize; +} + + +/* print the count of marked and unmarked objects. + In addition if 1 is set in printFlags, short-print marked objects, + and/or if 2 is set, short-print unmarked obejcts. */ +/* useful for debugging */ + + /* SpurMemoryManager>>#countMarkedAndUnmarkdObjects: */ +void +countMarkedAndUnmarkdObjects(sqInt printFlags) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt nm; + sqInt nu; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + nm = (nu = 0); + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + nm += 1; + if (((printFlags & 1) != 0)) { + shortPrintOop(obj); + } + } + else { + nu += 1; + if (((printFlags & 2) != 0)) { + shortPrintOop(obj); + } + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "n marked: "); + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)nm)); + cr(); + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "n unmarked: "); + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)nu)); + cr(); +} + + +/* Compute the current allocated bytes since last set. + This is the cumulative total in statAllocatedBytes plus the allocation + since the last scavenge. */ +/* Slang infers the type of the difference between two unsigned variables as + signed. In this case we want it to be unsigned. */ + + /* SpurMemoryManager>>#currentAllocatedBytes */ +static usqLong +currentAllocatedBytes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt use; + + use = ((/* begin totalOldSpaceCapacity */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + return (GIV(statAllocatedBytes) + (GIV(freeStart) - (((GIV(eden)).start)))) + (use - GIV(oldSpaceUsePriorToScavenge)); +} + + +/* The inner shell for scavenge, abstrascted out so globalGarbageCollect can + use it. + */ + + /* SpurMemoryManager>>#doScavenge: */ +static NoDbgRegParms void +doScavenge(sqInt tenuringCriterion) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + float fractionSurvived; + sqInt previousFSStart; + SpurNewSpaceSpace temp; + usqInt use; + + /* begin doAllocationAccountingForScavenge */ + /* begin currentAllocatedBytes */ + use = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + GIV(statAllocatedBytes) = (GIV(statAllocatedBytes) + (GIV(freeStart) - (((GIV(eden)).start)))) + (use - GIV(oldSpaceUsePriorToScavenge)); + GIV(gcPhaseInProgress) = ScavengeInProgress; + + /* begin scavenge: */ + GIV(statSurvivorCount) = 0; + GIV(tenureCriterion) = tenuringCriterion; + + /* begin logStartScavenge */ + (GIV(scavengeLogRecord).sEdenBytes = GIV(freeStart) - ((GIV(eden).start))); + (GIV(scavengeLogRecord).sPastBytes = GIV(pastSpaceStart) - ((GIV(pastSpace).start))); + (GIV(scavengeLogRecord).sRememberedSetSize = GIV(rememberedSetSize)); + (GIV(scavengeLogRecord).sRememberedSetRedZone = GIV(rememberedSetRedZone)); + (GIV(scavengeLogRecord).sRememberedSetLimit = GIV(rememberedSetLimit)); + (GIV(scavengeLogRecord).sStatTenures = GIV(statTenures)); + + /* begin strategizeToLimitRememberedTable */ + if ((GIV(tenureCriterion) == TenureByAge) + && (GIV(rememberedSetSize) >= GIV(rememberedSetRedZone))) { + GIV(tenureCriterion) = TenureToShrinkRT; + computeRefCountToShrinkRT(); + } + + /* begin logTenuringPolicy */ + (GIV(scavengeLogRecord).tTenureCriterion = GIV(tenureCriterion)); + (GIV(scavengeLogRecord).tTenureThreshold = ((GIV(tenureCriterion) == TenureByAge) + && (GIV(tenureThreshold) > ((GIV(pastSpace).start))) + ? GIV(tenureThreshold) - ((GIV(pastSpace).start)) + : 0)); + (GIV(scavengeLogRecord).tRefCountToShrinkRT = GIV(refCountToShrinkRT)); + ((void) (scavengeLoop())); + processWeaklings(); + + /* begin computeTenuringThreshold */ + fractionSurvived = (((GIV(futureSpace).limit)) == ((GIV(futureSpace).start)) + ? 0.0 + : (((double) (GIV(futureSurvivorStart) - ((GIV(futureSpace).start))) )) / (((GIV(futureSpace).limit)) - ((GIV(futureSpace).start)))); + GIV(tenureThreshold) = (fractionSurvived > 0.9 + ? (round(((((GIV(pastSpace).limit)) - ((GIV(pastSpace).start))) * (1.0 - GIV(tenuringProportion))))) + ((GIV(pastSpace).start)) + : 0); + + /* begin exchangeSurvivorSpaces */ + temp = GIV(pastSpace); + GIV(pastSpace) = GIV(futureSpace); + GIV(futureSpace) = temp; + + /* begin logEndScavenge */ + (GIV(scavengeLogRecord).eSurvivorBytes = GIV(futureSurvivorStart) - ((GIV(pastSpace).start))); + (GIV(scavengeLogRecord).eRememberedSetSize = GIV(rememberedSetSize)); + (GIV(scavengeLogRecord).eStatTenures = GIV(statTenures)); + + /* begin initFutureSpaceStart */ + previousFSStart = GIV(futureSurvivorStart); + GIV(futureSurvivorStart) = (GIV(futureSpace).start); + GIV(pastSpaceStart) = previousFSStart; + assert(oopisGreaterThanOrEqualToandLessThanOrEqualTo(GIV(pastSpaceStart), ((GIV(pastSpace)).start), ((GIV(pastSpace)).limit))); + GIV(freeStart) = ((GIV(eden)).start); + GIV(gcPhaseInProgress) = 0; + + /* begin resetAllocationAccountingAfterGC */ + GIV(oldSpaceUsePriorToScavenge) = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); +} + + +/* Reduce the number of indexable fields in objOop, a pointer object, to + nSlots. If in oldSpace, + convert the unused residual to a free chunk. If in eden, cut back + freeStart if possible. + Without changes to numSlotsForShortening:toIndexableSize: this only works + for arrayFormat, longFormat, and on 64-bits sixtyFourBitIndexableFormat, + objects. Answer the number of bytes returned to free memory, which may be + zero if no change + was possible. */ + + /* SpurMemoryManager>>#doShorten:toIndexableSize: */ +static NoDbgRegParms sqInt +doShortentoIndexableSize(sqInt objOop, sqInt indexableSize) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytesBefore; + sqInt classIndex; + sqInt copy; + sqInt delta; + usqInt followingAddress; + sqInt format; + sqInt formatSqInt; + sqInt freeChunk; + sqInt i; + sqInt instFormat; + usqInt newObj; + usqInt numBytes; + sqInt numSlots; + sqInt valuePointer; + + /* for assert checking */ + followingAddress = addressAfter(objOop); + assert(oopisLessThanOrEqualTo(followingAddress, GIV(endOfMemory))); + + /* numSlotsForShortening:toIndexableSize: */ + switch ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) { + case arrayFormat(): + case sixtyFourBitIndexableFormat(): + numSlots = indexableSize; + break; + case firstLongFormat(): + case (firstLongFormat()) + 1: + numSlots = ((indexableSize * 4) + (BytesPerWord - 1)) / BytesPerWord; + break; + default: + assertf("invalid format for shortening"); + numSlots = 0; + } + bytesBefore = bytesInBody(objOop); + delta = bytesBefore - (/* objectBytesForSlots: */ + (numSlots + ? ((((usqInt)(numSlots) << (shiftForWord())))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize)); + + /* Since the system rounds objects up to 64-bits, losing a 32-bit + slot may not actually change the bytes occupied by the object. */ + if (!delta) { + if ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) { + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(objOop - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56))) + numSlots); + } + else { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),numSlots); + } + + /* begin updateFormatOfShortenedObject:to: */ + if (((((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) <= ((firstLongFormat()) + 1)))) { + formatSqInt = (firstLongFormat()) + (indexableSize & 1); + + /* begin setFormatOf:to: */ + assert(((formatSqInt >= 0) && (formatSqInt <= (formatMask())))); + byteAtput((void *)(objOop + (formatFieldByteOffset())),((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + formatSqInt); + } + return 0; + } + + /* Currently we can't have one word gaps in oldSpace but we can in newSpace. So only create + a copy and forward if objOop is in old space. If in newSpace we'll create a slimbridge below. */ + if ((delta <= 8 /* allocationUnit */) + && ((/* begin isOldObject: */ + assert(isNonImmediate(objOop)), + oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))))) { + /* begin normalisedFormatFor:indexableSize: */ + instFormat = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (instFormat >= (firstByteFormat())) { + formatSqInt = instFormat & -8; + goto l1; + } + + /* this is likely the common case */ + if (instFormat <= (sixtyFourBitIndexableFormat())) { + formatSqInt = instFormat; + goto l1; + } + if (instFormat < (firstShortFormat())) { + formatSqInt = instFormat & -2; + goto l1; + } + formatSqInt = instFormat & -4; + /* end classFormatFromInstFormat: */ +l1: + format = ((formatSqInt == (firstLongFormat())) + && (((indexableSize & 1) != 0)) + ? formatSqInt + 1 + : formatSqInt); + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if ((((usqInt)(numSlots)) >> 56) > 0) { + copy = null; + goto l2; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + copy = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, format, classIndex); + goto l2; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(format) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(format) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + copy = newObj; + /* end allocateSlots:format:classIndex: */ +l2: + if (!copy) { + error("shorten:toIndexableSize: attempted to shorten by allocationUnit and failed to allocate space for copy!"); + } + if (/* isPureBitsFormat: */ + (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat()))) { + memcpy(firstIndexableField(copy), firstIndexableField(objOop), numBytesOfBitsformat(copy, format)); + } + else { + for (i = 0; i < numSlots; i += 1) { + valuePointer = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(copy)) + && (!(isForwarded(copy)))); + assert(validStorePointerUncheckedArgs(i, copy, valuePointer)); + longAtput((void *)((copy + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + } + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) { + remember(copy); + } + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(objOop),(longAt((void *)(objOop))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(objOop)); + assert(!(isOopForwarded(copy))); + + /* begin isOldObject: */ + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(copy & (tagMask())))) + && (oopisLessThan(copy, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),copy); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),1); + } + return 0; + } + if ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) { + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(objOop - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56))) + numSlots); + + /* Setting an overflow slot count to 0 or 1 in newSpace creates a slimbridge. + So we must also change the normal slot count so that the object no + longer has the overflow header word, which has become the slimbridge. */ + if (numSlots <= 1) { + if (oopisLessThan(objOop, GIV(oldSpaceStart))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),numSlots); + hackSlimBridgeToat(objOop, objOop - 8 /* allocationUnit */); + } + } + } + else { + assert(numSlots < (numSlotsMask())); + + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),numSlots); + } + + /* {self addressAfter: objOop. delta. (self addressAfter: objOop) + delta. followingAddress} collect: #hex */ + delta = followingAddress - (addressAfter(objOop)); + assert((delta >= (allocationUnit())) + && ((delta % (allocationUnit())) == 0)); + if (delta == 8 /* allocationUnit */) { + assert(!((isOldObject(objOop)))); + delta = 0; + if (followingAddress == GIV(freeStart)) { + GIV(freeStart) = addressAfter(objOop); + } + else { + hackSlimBridgeToat(followingAddress, addressAfter(objOop)); + } + } + else { + freeChunk = initFreeChunkWithBytesat(delta, addressAfter(objOop)); + assert((objectAfter(objOop)) == freeChunk); + assert((addressAfter(freeChunk)) == followingAddress); + if (oopisGreaterThanOrEqualToandLessThan(objOop, GIV(oldSpaceStart), GIV(endOfMemory))) { + GIV(totalFreeOldSpace) += delta; + addToFreeListbytes(freeChunk, delta); + } + else { + delta = 0; + + /* begin setClassIndexOf:to: */ + assert((((sixtyFourBitLongsClassIndexPun()) >= 0) && ((sixtyFourBitLongsClassIndexPun()) <= (classIndexMask())))); + longAtput((void *)(freeChunk),((longAt((void *)(freeChunk))) & (~(usqIntptr_t)(classIndexMask()))) + (sixtyFourBitLongsClassIndexPun())); + + /* begin setFormatOf:to: */ + assert((((firstLongFormat()) >= 0) && ((firstLongFormat()) <= (formatMask())))); + byteAtput((void *)(freeChunk + (formatFieldByteOffset())),((byteAt((void *)(freeChunk + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (firstLongFormat())); + } + } + + /* begin normalisedFormatFor:indexableSize: */ + instFormat = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (instFormat >= (firstByteFormat())) { + formatSqInt = instFormat & -8; + goto l3; + } + + /* this is likely the common case */ + if (instFormat <= (sixtyFourBitIndexableFormat())) { + formatSqInt = instFormat; + goto l3; + } + if (instFormat < (firstShortFormat())) { + formatSqInt = instFormat & -2; + goto l3; + } + formatSqInt = instFormat & -4; + /* end classFormatFromInstFormat: */ +l3: + formatSqInt = ((formatSqInt == (firstLongFormat())) + && (((indexableSize & 1) != 0)) + ? formatSqInt + 1 + : formatSqInt); + + /* begin setFormatOf:to: */ + assert(((formatSqInt >= 0) && (formatSqInt <= (formatMask())))); + byteAtput((void *)(objOop + (formatFieldByteOffset())),((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + formatSqInt); + return delta; +} + + +/* Instantiate an instance of a compact class. ee stands for execution engine + and implies that this allocation will *NOT* cause a GC. N.B. the + instantiated object + IS NOT FILLED and must be completed before returning it to Smalltalk. + Since this + call is used in routines that do just that we are safe. Break this rule + and die in GC. + Result is guaranteed to be young. */ + + /* SpurMemoryManager>>#eeInstantiateClassIndex:format:numSlots: */ +usqInt +eeInstantiateClassIndexformatnumSlots(sqInt knownClassIndex, sqInt objFormat, sqInt numSlots) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newObj; + usqInt numBytes; + + assert((numSlots >= 0) + && ((knownClassIndex != 0) + && ((knownClassAtIndex(knownClassIndex)) != GIV(nilObj)))); + assert(((objFormat < (firstByteFormat()) + ? objFormat + : objFormat & (byteFormatMask()))) == (instSpecOfClass(knownClassAtIndex(knownClassIndex)))); + + /* begin allocateNewSpaceSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if (numSlots > 0xFFFFFFFFU) { + return null; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateNewSpaceSlots:format:classIndex:"); + return 0; + } + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),numSlots); + longAtput((void *)(GIV(freeStart) + 4),((sqInt)((usqInt)((numSlotsMask())) << (numSlotsHalfShift())))); + long64Atput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + knownClassIndex); + } + else { + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + knownClassIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + return newObj; +} + + +/* For breakpointing the end of a leak check print message */ + + /* SpurMemoryManager>>#eek */ +NeverInline void +eek(void) +{ +} + + +/* We use bridges to stitch segments together to make it appear that the heap + is one contiguous space. + Bridges at the end of oldSpace segments are maintained. Bridges at the end + of pastSpace and eden + are temporary, and are established here, depending on the current sizes of + pastSpace end eden. + + N.B. this introduces complications. Either or both pastSpace and eden may + be empty, so the + bridge from pastSpace may skip eden. pastSpace may be full, so there may + be no bridge at + the end of pastSpace. Most difficult, pastSpace could be one 64-bit word + short of full, but normal + bridges are two word objects. To make this work we introduce a hack, + objectAfterMaybeSlimBridge:limit:, which uses a fake overflow slot count + to get to the start of the next object, which is either one or two + words away, depending on whether the first object in eden has a normal or + an overflow header. */ + + /* SpurMemoryManager>>#enableObjectEnumerationFrom: */ +static NoDbgRegParms void +enableObjectEnumerationFrom(sqInt initialObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (oopisLessThan(initialObject, GIV(oldSpaceStart))) { + if (GIV(freeStart) > (((GIV(eden)).start))) { + /* begin bridgeEdenAndOldSpace */ + initSegmentBridgeWithBytesat(GIV(oldSpaceStart) - GIV(freeStart), GIV(freeStart)); + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + } + else { + /* begin bridgePastSpaceAndOldSpace */ + initSegmentBridgeWithBytesat(GIV(oldSpaceStart) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } +} + + /* SpurMemoryManager>>#ensureBehaviorHash: */ +static NoDbgRegParms sqInt +ensureBehaviorHash(sqInt aBehavior) +{ + sqInt err; + int hash; + + assert(addressCouldBeClassObj(aBehavior)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + return ((hash = (long32At((void *)(aBehavior + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(aBehavior) + ? ((err = enterIntoClassTable(aBehavior)) + ? -err + : (long32At((void *)(aBehavior + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); +} + + +/* An obj stack is a stack of objects stored in a hidden root slot, such as + the markStack or the ephemeronQueue. It is a linked list of segments, + with the hot end at the head of the list. It is a word object. The stack + pointer is in ObjStackTopx and 0 means empty. The list goes through + ObjStackNextx. We don't want to shrink objStacks, since they're used + in GC and its good to keep their memory around. So unused pages + created by popping emptying pages are kept on the ObjStackFreex list. */ + + /* SpurMemoryManager>>#ensureRoomOnObjStackAt: */ +static NoDbgRegParms sqInt +ensureRoomOnObjStackAt(sqInt objStackRootIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt freeOrNewPage; + sqInt stackOrNil; + + stackOrNil = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord())))))); + if ((stackOrNil == GIV(nilObj)) + || ((longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) >= ObjStackLimit)) { + freeOrNewPage = (stackOrNil == GIV(nilObj) + ? 0 + : longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))))); + if (freeOrNewPage) { + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(stackOrNil)) == (wordIndexableFormat())); + longAtput((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),0); + assert((!GIV(marking)) + || (isMarked(freeOrNewPage))); + } + else { + freeOrNewPage = allocateSlotsInOldSpacebytesformatclassIndex(ObjStackPageSlots, ((((usqInt)(ObjStackPageSlots) << (shiftForWord())))) + (BaseHeaderSize + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (!freeOrNewPage) { + /* Allocate a new segment and retry. This is very uncommon. But it happened to me (Clement). */ + growOldSpaceByAtLeast(ObjStackPageSlots * BytesPerOop); + freeOrNewPage = allocateSlotsInOldSpacebytesformatclassIndex(ObjStackPageSlots, ((((usqInt)(ObjStackPageSlots) << (shiftForWord())))) + (BaseHeaderSize + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (!freeOrNewPage) { + error("no memory to allocate or extend obj stack"); + } + } + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(freeOrNewPage)) == (wordIndexableFormat())); + longAtput((void *)((freeOrNewPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),0); + if (GIV(marking)) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(freeOrNewPage))); + byteAtput((void *)(freeOrNewPage + (markBitsByteOffset())),(byteAt((void *)(freeOrNewPage + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + } + } + + /* the free page list is always on the new page. */ + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(freeOrNewPage)) == (wordIndexableFormat())); + longAtput((void *)((freeOrNewPage + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord()))))),objStackRootIndex); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(freeOrNewPage)) == (wordIndexableFormat())); + longAtput((void *)((freeOrNewPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord()))))),(stackOrNil == GIV(nilObj) + ? 0 + : stackOrNil)); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(freeOrNewPage)) == (wordIndexableFormat())); + longAtput((void *)((freeOrNewPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))),0); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(objStackRootIndex, GIV(hiddenRootsObj), freeOrNewPage)); + assert(isNonImmediate(GIV(hiddenRootsObj))); + if (oopisGreaterThanOrEqualTo(GIV(hiddenRootsObj), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(freeOrNewPage & (tagMask())))) + && (oopisLessThan(freeOrNewPage, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(hiddenRootsObj) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(hiddenRootsObj)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord()))))),freeOrNewPage); + assert(isValidObjStackAt(objStackRootIndex)); + + /* Added a new page; now update and answer the relevant cached first page. */ + + /* begin updateRootOfObjStackAt:with: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(objStackRootIndex, GIV(hiddenRootsObj), freeOrNewPage)); + assert(isNonImmediate(GIV(hiddenRootsObj))); + if (oopisGreaterThanOrEqualTo(GIV(hiddenRootsObj), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(freeOrNewPage & (tagMask())))) + && (oopisLessThan(freeOrNewPage, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(hiddenRootsObj) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(hiddenRootsObj)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord()))))),freeOrNewPage); + switch (objStackRootIndex) { + case MarkStackRootIndex: + GIV(markStack) = freeOrNewPage; + break; + case WeaklingStackRootIndex: + GIV(weaklingStack) = freeOrNewPage; + break; + case MournQueueRootIndex: + GIV(mournQueue) = freeOrNewPage; + break; + default: + error("Case not found and no otherwise clause"); + } + stackOrNil = freeOrNewPage; + } + assert(isValidObjStackAt(objStackRootIndex)); + return stackOrNil; +} + + +/* Enter aBehavior into the class table and answer 0. Otherwise answer a + primitive failure code. */ + + /* SpurMemoryManager>>#enterIntoClassTable: */ +static NoDbgRegParms sqInt +enterIntoClassTable(sqInt aBehavior) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt initialMajorIndex; + sqInt majorIndex; + sqInt minorIndex; + usqInt p; + sqInt page; + sqInt toDoLimit; + usqInt toDoLimitUsqInt; + + majorIndex = ((usqInt)(GIV(classTableIndex))) >> (classTableMajorIndexShift()); + initialMajorIndex = majorIndex; + + /* classTableIndex should never index the first page; it's reserved for known classes */ + assert(initialMajorIndex > 0); + minorIndex = GIV(classTableIndex) & ((1U << (classTableMajorIndexShift())) - 1); + while (1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(majorIndex) << (shiftForWord())))))); + if (page == GIV(nilObj)) { + /* begin allocateSlotsInOldSpace:format:classIndex: */ + page = allocateSlotsInOldSpacebytesformatclassIndex(1U << (classTableMajorIndexShift()), /* objectBytesForSlots: */ + (1U << (classTableMajorIndexShift()) + ? ((((usqInt)((1U << (classTableMajorIndexShift()))) << (shiftForWord())))) + (((1U << (classTableMajorIndexShift())) >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), arrayFormat(), arrayClassIndexPun()); + if (!page) { + return PrimErrNoMemory; + } + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((page + BaseHeaderSize) + ((1U << (classTableMajorIndexShift())) * BytesPerOop)) - 1, addressAfter(page))); + toDoLimitUsqInt = ((usqInt)(((page + BaseHeaderSize) + ((1U << (classTableMajorIndexShift())) * BytesPerOop)) - 1)); + for (p = (((usqInt)(page + BaseHeaderSize))); p <= toDoLimitUsqInt; p += 8 /* allocationUnit */) { + longAtput((void *)(p),GIV(nilObj)); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(majorIndex, GIV(hiddenRootsObj), page)); + assert(isNonImmediate(GIV(hiddenRootsObj))); + if (oopisGreaterThanOrEqualTo(GIV(hiddenRootsObj), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(page & (tagMask())))) + && (oopisLessThan(page, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(hiddenRootsObj) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(hiddenRootsObj)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(majorIndex) << (shiftForWord()))))),page); + GIV(numClassTablePages) += 1; + minorIndex = 0; + } + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (i = minorIndex; i <= toDoLimit; i += 1) { + if ((longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))))) == GIV(nilObj)) { + GIV(classTableIndex) = ((((usqInt)(majorIndex) << (classTableMajorIndexShift())))) + i; + + /* classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + assert(GIV(classTableIndex) >= (1U << (classTableMajorIndexShift()))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(i, page, aBehavior)); + assert(isNonImmediate(page)); + if (oopisGreaterThanOrEqualTo(page, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(aBehavior & (tagMask())))) + && (oopisLessThan(aBehavior, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(page + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(page); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((page + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),aBehavior); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(aBehavior + 4),((((long32At((void *)(aBehavior + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (GIV(classTableIndex) & (identityHashHalfWordMask()))); + assert((classAtIndex(rawHashBitsOf(aBehavior))) == aBehavior); + return 0; + } + } + minorIndex = 0; + + /* classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + majorIndex = ((((majorIndex + 1) & ((1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift()))) - 1)) < 1) ? 1 : ((majorIndex + 1) & ((1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift()))) - 1))); + if (majorIndex == initialMajorIndex) { + return PrimErrLimitExceeded; + } + } + return 0; +} + + /* SpurMemoryManager>>#existInstancesInNewSpaceOf: */ +static NoDbgRegParms sqInt +existInstancesInNewSpaceOf(sqInt classObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + + /* begin allNewSpaceObjectsDo: */ + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l2; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOopSqInt, GIV(freeStart))) { + assert(isBridgeOrEnumerableObjectNoAssert(objOopSqInt)); + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == classIndex) { + return 1; + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOopSqInt = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + /* end allNewSpaceEntitiesDo: */ +l2: + return 0; +} + + +/* Remove aBehavior from the class table. */ + + /* SpurMemoryManager>>#expungeFromClassTable: */ +static NoDbgRegParms void +expungeFromClassTable(sqInt aBehavior) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int classIndex; + sqInt classTablePage; + sqInt majorIndex; + int minorIndex; + + assert(isInClassTable(aBehavior)); + classIndex = (long32At((void *)(aBehavior + 4))) & (identityHashHalfWordMask()); + majorIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + minorIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(majorIndex) << (shiftForWord())))))); + assert(classTablePage != GIV(classTableFirstPage)); + assert((numSlotsOf(classTablePage)) == (classTablePageSize())); + assert((fetchPointerofObject(minorIndex, classTablePage)) == aBehavior); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(classTablePage)) + && (!(isForwarded(classTablePage)))); + assert(validStorePointerUncheckedArgs(minorIndex, classTablePage, GIV(nilObj))); + longAtput((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(minorIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* If the removed class is before the classTableIndex, set the + classTableIndex to point to the empty slot so as to reuse it asap. */ + if (classIndex < GIV(classTableIndex)) { + GIV(classTableIndex) = classIndex; + } + + /* classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + assert(GIV(classTableIndex) >= (1U << (classTableMajorIndexShift()))); +} + + /* SpurMemoryManager>>#falseObject */ +sqInt +falseObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(falseObj); +} + + /* SpurMemoryManager>>#fetchByte:ofObject: */ +sqInt +fetchByteofObject(sqInt byteIndex, sqInt objOop) +{ + return byteAt((void *)((objOop + BaseHeaderSize) + byteIndex)); +} + + /* SpurMemoryManager>>#fetchClassOfNonImm: */ +static NoDbgRegParms NeverInline sqInt +fetchClassOfNonImm(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt classTablePage; + sqInt fieldIndex; + + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + if (classIndex <= (classIsItselfClassIndexPun())) { + if (classIndex == (classIsItselfClassIndexPun())) { + return objOop; + } + + /* Answer nil to avoid the assert failure in classOrNilAtIndex: */ + if (classIndex == (isForwardedObjectClassIndexPun())) { + return GIV(nilObj); + } + } + assert(classIndex >= (arrayClassIndexPun())); + + /* begin classOrNilAtIndex: */ + assert((classIndex <= (tagMask())) + || (classIndex >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return GIV(nilObj); + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#fetchClassOf: */ +sqInt +fetchClassOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt tagBits; + + return ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); +} + + +/* In Spur an object's classIndex is the tag in all method caches. */ + + /* SpurMemoryManager>>#fetchClassTagOfNonImm: */ +static NoDbgRegParms sqInt +fetchClassTagOfNonImm(sqInt obj) +{ + return (longAt((void *)(obj))) & (classIndexMask()); +} + + /* SpurMemoryManager>>#fetchInteger:ofObject: */ +sqLong +fetchIntegerofObject(sqInt fieldIndex, sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt oop; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + return ((((oop) & 7) == 1) + ? (oop >> 3) + : (GIV(primFailCode) = PrimErrInappropriate)); + } + return (fmt < (firstShortFormat()) + ? (fmt < (firstLongFormat()) + ? long64At((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 3))))) + : long32At((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2)))))) + : (fmt < (firstCompiledMethodFormat()) + ? (fmt < (firstByteFormat()) + ? byteAt((void *)((objOop + BaseHeaderSize) + fieldIndex)) + : shortAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 1)))))) + : (GIV(primFailCode) = PrimErrUnsupported))); +} + + +/* index by 32-bit units, and return a 32-bit value. Intended to replace + fetchWord:ofObject: + */ + + /* SpurMemoryManager>>#fetchLong32:ofObject: */ +int +fetchLong32ofObject(sqInt fieldIndex, sqInt oop) +{ + return long32At((void *)((oop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2))))); +} + + /* SpurMemoryManager>>#fetchPointer:ofFreeChunk: */ +static NoDbgRegParms sqInt +fetchPointerofFreeChunk(sqInt fieldIndex, sqInt objOop) +{ + return longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#fetchPointer:ofObject: */ +sqInt +fetchPointerofObject(sqInt fieldIndex, sqInt objOop) +{ + return longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#findClassNamed: */ +sqInt +findClassNamed(char *aString) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOrNil; + sqInt i; + sqInt ignored; + sqInt j; + sqInt page; + sqInt toDoLimit; + + /* begin classTableEntriesDo: */ + for (i = 0; i < GIV(numClassTablePages); i += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNil = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNil != GIV(nilObj)) { + ignored = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + if (classNameOfIs(classOrNil, aString)) { + return classOrNil; + } + } + } + } + return null; +} + + +/* Answer, but do not remove, the largest free chunk in the free lists. */ + + /* SpurMemoryManager>>#findLargestFreeChunk */ +static sqInt +findLargestFreeChunk(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt childNode; + sqInt treeNode; + + childNode = 0; + treeNode = GIV(freeLists)[0]; + if (!treeNode) { + return null; + } + while (1) { + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(treeNode)); + assert((bytesInBody(treeNode)) >= ((numFreeLists()) * (allocationUnit()))); + childNode = longAt((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + if (!(childNode != 0)) break; + treeNode = childNode; + } + return treeNode; +} + + +/* Print the oops of all string-like things that start with the same + characters as aCString + */ +/* useful for debugging */ + + /* SpurMemoryManager>>#findStringBeginningWith: */ +void +findStringBeginningWith(char *aCString) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aCStringStrlen; + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + aCStringStrlen = strlen(aCString); + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (/* object:beginsWithString:ofSize: */ + (/* isBytes: */ + ((!(objSqInt & (tagMask())))) + && (((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((!(((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((numBytesOfBytes(objSqInt)) >= aCStringStrlen) + && ((strncmp(aCString, firstIndexableField(objSqInt), aCStringStrlen)) == 0)))) { + printHex(objSqInt); + + /* begin space */ + printChar(' '); + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)(lengthOf(objSqInt)))); + printChar(' '); + printOopShortInner(objSqInt); + cr(); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + +/* Print the oops of all string-like things that have the same characters as + aCString + */ + + /* SpurMemoryManager>>#findString: */ +void +findString(char *aCString) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aCStringStrlen; + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + aCStringStrlen = strlen(aCString); + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (/* object:equalsString:ofSize: */ + (/* isBytes: */ + ((!(objSqInt & (tagMask())))) + && (((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((!(((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((numBytesOfBytes(objSqInt)) == aCStringStrlen) + && ((strncmp(aCString, firstIndexableField(objSqInt), aCStringStrlen)) == 0)))) { + printHex(objSqInt); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + cr(); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + /* SpurMemoryManager>>#fireEphemeron: */ +static NoDbgRegParms void +fireEphemeron(sqInt ephemeron) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + + assert(isNonImmediate(ephemeron)); + format = (byteAt((void *)(ephemeron + (formatFieldByteOffset())))) & (formatMask()); + if (format == (ephemeronFormat())) { + queueMourner(ephemeron); + + /* begin setEphemeronToFired: */ + assert((isNonImmediate(ephemeron)) + && (isEphemeron(ephemeron))); + + /* begin setFormatOf:to: */ + assert((((nonIndexablePointerFormat()) >= 0) && ((nonIndexablePointerFormat()) <= (formatMask())))); + byteAtput((void *)(ephemeron + (formatFieldByteOffset())),((byteAt((void *)(ephemeron + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (nonIndexablePointerFormat())); + + /* begin signalFinalization: */ + forceInterruptCheck(); + GIV(pendingFinalizationSignals) += 1; + } + else { + assert((format == (nonIndexablePointerFormat())) + && (isonObjStack(ephemeron, GIV(mournQueue)))); + } +} + + /* SpurMemoryManager>>#firstAccessibleObject */ +static sqInt +firstAccessibleObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt savedTenuringThreshold; + + assert(GIV(nilObj) == GIV(oldSpaceStart)); + + /* flush newSpace to settle the enumeration. */ + + /* begin flushNewSpace */ + savedTenuringThreshold = GIV(tenureThreshold); + GIV(tenureThreshold) = GIV(oldSpaceStart); + scavengingGCTenuringIf(TenureByAge); + + /* begin setRawTenuringThreshold: */ + GIV(tenureThreshold) = savedTenuringThreshold; + assert((GIV(rememberedSetSize)) == 0); + assert(GIV(pastSpaceStart) == (((GIV(pastSpace)).start))); + assert(GIV(freeStart) == (((GIV(eden)).start))); + return GIV(nilObj); +} + + +/* for the message send breakpoint; selectors can be immediates. */ + + /* SpurMemoryManager>>#firstFixedFieldOfMaybeImmediate: */ +static NoDbgRegParms void * +firstFixedFieldOfMaybeImmediate(sqInt oop) +{ + return (((oop & (tagMask())) != 0) + ? ((void *)oop) + : pointerForOop(oop + BaseHeaderSize)); +} + + /* SpurMemoryManager>>#firstFixedField: */ +void * +firstFixedField(sqInt objOop) +{ + return pointerForOop(objOop + BaseHeaderSize); +} + + +/* NOTE: overridden in various simulator subclasses to add coercion to + CArray, so please duplicate any changes. + There are only two important cases, both for objects with named inst vars, + i.e. formats 2,3 & 5. + The first indexable field for formats 2 & 5 is the slot count (by + convention, even though that's off the end + of the object). For 3 we must go to the class. */ + + /* SpurMemoryManager>>#firstIndexableField: */ +void * +firstIndexableField(sqInt objOop) +{ + sqInt classFormat; + sqInt classPointer; + sqInt fmt; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= (weakArrayFormat())) { + if (fmt == (arrayFormat())) { + return pointerForOop(objOop + BaseHeaderSize); + } + + /* array starts at 0. */ + if (fmt >= (indexablePointersFormat())) { + classPointer = fetchClassOfNonImm(objOop); + + /* begin formatOfClass: */ + classFormat = ((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + return pointerForOop((objOop + BaseHeaderSize) + ((((usqInt)((classFormat & ((1U << (fixedFieldsFieldWidth())) - 1))) << (shiftForWord()))))); + } + + /* indexable with inst vars; need to delve into the class format word + otherwise not indexable */ + return 0; + } + + /* All bit objects, and indeed CompiledMethod, though this is a no-no, start at 0 */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat()))) { + return pointerForOop(objOop + BaseHeaderSize); + } + + /* otherwise not indexable */ + return 0; +} + + /* SpurMemoryManager>>#fixedFieldsOfClassFormatMask */ +sqInt +fixedFieldsOfClassFormatMask(void) +{ + return (1U << (fixedFieldsFieldWidth())) - 1; +} + + /* SpurMemoryManager>>#fixedFieldsOfClassFormat: */ +sqInt +fixedFieldsOfClassFormat(sqInt classFormat) +{ + return classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); +} + + /* SpurMemoryManager>>#fixedFieldsOf:format:length: */ +static NoDbgRegParms sqInt +fixedFieldsOfformatlength(sqInt objOop, sqInt fmt, sqInt wordLength) +{ + sqInt class; + + + /* N.B. written to fall through to fetchClassOfNonImm: et al for forwarders + so as to trigger an assert fail. */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + return 0; + } + if (fmt < (arrayFormat())) { + return wordLength; + } + class = fetchClassOfNonImm(objOop); + return (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); +} + + +/* Private helper for followField:ofObject: to avoid code duplication for + rare case. + */ + + /* SpurMemoryManager>>#fixFollowedField:ofObject:withInitialValue: */ +static NoDbgRegParms NeverInline sqInt +fixFollowedFieldofObjectwithInitialValue(sqInt fieldIndex, sqInt anObject, sqInt initialValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + + assert(isOopForwarded(initialValue)); + + /* inlined followForwarded: for speed (one less test) */ + objOop = initialValue; + do { + objOop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + } while(/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, anObject, objOop)); + assert(isNonImmediate(anObject)); + if (oopisGreaterThanOrEqualTo(anObject, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objOop & (tagMask())))) + && (oopisLessThan(objOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(anObject + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(anObject); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((anObject + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),objOop); + return objOop; +} + + +/* Answer the object the ephemeron guards. This is its first element. */ + + /* SpurMemoryManager>>#followedKeyOfEphemeron: */ +static NoDbgRegParms sqInt +followedKeyOfEphemeron(sqInt objOop) +{ + sqInt oop; + + assert((isNonImmediate(objOop)) + && (isEphemeron(objOop))); + + /* begin followOopField:ofObject: */ + oop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + oop = fixFollowedFieldofObjectwithInitialValue(0, objOop, oop); + } + return oop; +} + + +/* Make sure the oop at fieldIndex in anObject is not forwarded (follow the + forwarder there-in if so). Answer the (possibly followed) oop at + fieldIndex. */ + + /* SpurMemoryManager>>#followField:ofObject: */ +static NoDbgRegParms sqInt +followFieldofObject(sqInt fieldIndex, sqInt anObject) +{ + sqInt objOop; + + objOop = longAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(fieldIndex, anObject, objOop); + } + return objOop; +} + + +/* Follow a forwarding pointer. This must be a loop because we cannot prevent + forwarders to + forwarders being created by lazy become. Consider the following example by + Igor Stasenko: + array := { a. b. c }. + - array at: 1 points to &a. array at: 2 points to &b. array at: 3 points + to &c + a becomeForward: b + - array at: 1 still points to &a. array at: 2 still points to &b. array + at: 3 still points to &c + b becomeForward: c. + - array at: 1 still points to &a. array at: 2 still points to &b. array + at: 3 still points to &c + - when accessing array first one has to follow a forwarding chain: + &a -> &b -> c */ + + /* SpurMemoryManager>>#followForwarded: */ +sqInt +followForwarded(sqInt objOop) +{ + sqInt referent; + + assert(isUnambiguouslyForwarder(objOop)); + referent = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + return referent; +} + + /* SpurMemoryManager>>#followMaybeForwarded: */ +static NoDbgRegParms sqInt +followMaybeForwarded(sqInt objOop) +{ + return (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + ? followForwarded(objOop) + : objOop); +} + + +/* Make sure the obj at fieldIndex in anObject is not forwarded (follow the + forwarder there-in if so). Answer the (possibly followed) obj at + fieldIndex. */ + + /* SpurMemoryManager>>#followObjField:ofObject: */ +static NoDbgRegParms sqInt +followObjFieldofObject(sqInt fieldIndex, sqInt anObject) +{ + sqInt objOop; + + objOop = longAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(fieldIndex, anObject, objOop); + } + return objOop; +} + + /* SpurMemoryManager>>#formatOfClass: */ +sqInt +formatOfClass(sqInt classPointer) +{ + return ((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); +} + + +/* 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState + et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable (lsb = # of unused 32-bit fields, 11 unused in 32 + bits) 12 - 15 16-bit indexable (ls2b = # of unused 16-bit fields, 14 & 15 + unused in 32-bits) + 16 - 23 byte indexable (ls3b = # of unused 8-bit fields, 20, 21, 22, 23 + unused in 32-bits) + 24 - 31 compiled method (ls3b = # of unused 8-bit fields, 20, 21, 22, 23 + unused in 32-bits) */ +/* A note on performance. Since the format field is, by design, aligned on a + byte boundary + in the fourth byte of the header (see headerForSlots:format:classIndex:) + it is accessed + as below. But it used to be accessed via + ^(self longAt: objOop) >> self formatShift bitAnd: self formatMask + This potentially involves more operations (a shift) and larger literals, + In practice clang + optimizes the long form to the byte form on x86_64, showing it is to be + preferred on at + least one architecture. On arm64 for example both forms take two + instructions. */ + + /* SpurMemoryManager>>#formatOf: */ +static NoDbgRegParms sqInt +formatOf(sqInt objOop) +{ + return (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); +} + + +/* Answer if anObject is itself forwarded, or is a pointer object containing + any references to forwarded objects. + */ + + /* SpurMemoryManager>>#forwardersIn: */ +static NoDbgRegParms sqInt +forwardersIn(sqInt anObject) +{ + sqInt i; + sqInt oop; + sqInt toDoLimit; + + if ((!((longAt((void *)(anObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + return 1; + } + toDoLimit = (numPointerSlotsOf(anObject)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + oop = longAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + return 1; + } + } + return 0; +} + + +/* for linking objecs on each free list, or, during pigCompact, doubly- + linking the free objects in address order using the xor link hack. */ + + /* SpurMemoryManager>>#freeChunkNextIndex */ +static sqInt +freeChunkNextIndex(void) +{ + return 0; +} + + +/* for organizing the tree of large free chunks. */ + + /* SpurMemoryManager>>#freeChunkParentIndex */ +static sqInt +freeChunkParentIndex(void) +{ + return 2; +} + + +/* For linking objecs on each free list, doubly-linking the free objects. + Free chunks of size 1 do not have a prev index. */ + + /* SpurMemoryManager>>#freeChunkPrevIndex */ +static sqInt +freeChunkPrevIndex(void) +{ + return 1; +} + + /* SpurMemoryManager>>#freeChunkWithBytes:at: */ +static NoDbgRegParms sqInt +freeChunkWithBytesat(sqInt bytes, sqInt address) +{ + sqInt freeChunk; + + assert(isInOldSpace(address)); + assert((segmentContainingObj(address)) == (segmentContainingObj(address + bytes))); + freeChunk = initFreeChunkWithBytesat(bytes, address); + addToFreeListbytes(freeChunk, bytes); + assert(freeChunk == (objectStartingAt(address))); + return freeChunk; +} + + +/* Free an object in oldSpace. Coalesce if possible to reduce fragmentation. */ + + /* SpurMemoryManager>>#freeObject: */ +sqInt +freeObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytes; + usqInt chunkBytes; + sqInt index; + sqInt next; + usqInt numSlots; + usqInt start; + + assert(isInOldSpace(objOop)); + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) { + /* begin forgetObject: */ + assert(GIV(rememberedSetSize) > 0); + assert(isRemembered(objOop)); + setIsRememberedOfto(objOop, 0); + if (!(objOop == (GIV(rememberedSet)[GIV(rememberedSetSize) - 1]))) { + index = 0; + while (index < GIV(rememberedSetSize)) { + if (objOop == (GIV(rememberedSet)[index])) { + GIV(rememberedSet)[index] = (GIV(rememberedSet)[GIV(rememberedSetSize) - 1]); + index = GIV(rememberedSetSize); + } + else { + index += 1; + } + } + } + GIV(rememberedSetSize) -= 1; + assert(GIV(rememberedSetSize) >= 0); + } + bytes = bytesInBody(objOop); + start = /* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)((start + bytes) + (numSlotsFieldByteOffset()))); + next = (numSlots == (numSlotsMask()) + ? (start + bytes) + BaseHeaderSize + : start + bytes); + if (((longAt((void *)(next))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + /* begin detachFreeObject: */ + chunkBytes = bytesInBody(next); + GIV(totalFreeOldSpace) -= chunkBytes; + unlinkFreeChunkchunkBytes(next, chunkBytes); + bytes += bytesInBody(next); + } + GIV(totalFreeOldSpace) += bytes; + return freeChunkWithBytesat(bytes, start); +} + + +/* Perform a full eager compacting GC. Answer the size of the largest free + chunk. + */ +/* for profiling */ + + /* SpurMemoryManager>>#fullGC */ +NeverInline usqLong +fullGC(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + usqLong compactionStartUsecs; + usqLong gcMarkEndUsecs; + sqInt i; + sqInt index; + usqInt numSlots; + sqInt obj; + sqInt objOop; + + GIV(needGCFlag) = 0; + GIV(gcStartUsecs) = ioUTCMicrosecondsNow(); + GIV(statMarkCount) = 0; + preGCAction(GCModeFull); + + /* begin globalGarbageCollect */ + assert(validObjStacks()); + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + + /* Mark objects /before/ scavenging, to empty the rememberedTable of unmarked roots. */ + markObjects(1); + gcMarkEndUsecs = ioUTCMicrosecondsNow(); + + /* begin forgetUnmarkedRememberedObjects */ + index = 0; + while (index < GIV(rememberedSetSize)) { + obj = GIV(rememberedSet)[index]; + if ((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + index += 1; + } + else { + setIsRememberedOfto(obj, 0); + GIV(rememberedSetSize) -= 1; + GIV(rememberedSet)[index] = (GIV(rememberedSet)[GIV(rememberedSetSize)]); + } + } + assert(GIV(rememberedSetSize) >= 0); + + /* begin setGCMode: */ + GIV(gcMode) = GCModeNewSpace; + doScavenge(MarkOnTenure); + + /* begin setGCMode: */ + GIV(gcMode) = GCModeFull; + + /* Mid-way the leak check must be more lenient. Unmarked classes will have been + expunged from the table, but unmarked instances will not yet have been reclaimed. */ + runLeakCheckerForexcludeUnmarkedObjsclassIndicesShouldBeValid(GCModeFull, 1, 1); + compactionStartUsecs = ioUTCMicrosecondsNow(); + + /* begin prepareForGlobalSweep */ + for (i = 0; i < GIV(numSegments); i += 1) { + ((GIV(segments)[i]).containsPinned = 0); + } + + /* begin bridgeAt: */ + /* begin bridgeFor: */ + address = (((((&(GIV(segments)[GIV(numSegments) - 1])))->segSize)) + ((((&(GIV(segments)[GIV(numSegments) - 1])))->segStart))) - (2 * BaseHeaderSize); + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + compact(); + + /* begin attemptToShrink */ + if ((GIV(totalFreeOldSpace) > GIV(shrinkThreshold)) + && ((GIV(totalFreeOldSpace) > GIV(growHeadroom)) + && (shrinkObjectMemory((((GIV(totalFreeOldSpace) - GIV(growHeadroom)) < GIV(growHeadroom)) ? GIV(growHeadroom) : (GIV(totalFreeOldSpace) - GIV(growHeadroom))))))) { + GIV(statShrinkMemory) += 1; + } + + /* begin setHeapSizeAtPreviousGC */ + GIV(heapSizeAtPreviousGC) = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + + /* begin resetAllocationAccountingAfterGC */ + GIV(oldSpaceUsePriorToScavenge) = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + assert(validObjStacks()); + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + assert(allObjectsUnmarked()); + runLeakCheckerFor(GCModeFull); + postGCAction(GCModeFull); + GIV(statGCEndUsecs) = ioUTCMicrosecondsNow(); + + /* begin updateFullGCStats */ + GIV(statFullGCs) += 1; + GIV(statFullGCUsecs) += GIV(statGCEndUsecs) - GIV(gcStartUsecs); + GIV(statCompactionUsecs) += GIV(statGCEndUsecs) - compactionStartUsecs; + if (GIV(gcSweepEndUsecs)) { + GIV(statSweepUsecs) += GIV(gcSweepEndUsecs) - compactionStartUsecs; + } + GIV(statMarkUsecs) += gcMarkEndUsecs - GIV(gcStartUsecs); + return (GIV(freeLists)[0] + ? bytesInBody(findLargestFreeChunk()) + : 0); +} + + /* SpurMemoryManager>>#getHeapGrowthToSizeGCRatio */ +static float +getHeapGrowthToSizeGCRatio(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(heapGrowthToSizeGCRatio); +} + + /* SpurMemoryManager>>#goodContextSize: */ +static NoDbgRegParms sqInt +goodContextSize(sqInt oop) +{ + usqInt numSlots; + usqInt numSlotsUsqInt; + + /* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + return (numSlots == SmallContextSlots) + || (numSlots == LargeContextSlots); +} + + +/* Attempt to grow memory by at least minAmmount. + Answer the size of the new segment, or nil if the attempt failed. */ + + /* SpurMemoryManager>>#growOldSpaceByAtLeast: */ +static NoDbgRegParms usqInt +growOldSpaceByAtLeast(sqInt minAmmount) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt ammount; + sqInt bytes; + sqInt headroom; + sqInt interval; + SpurSegmentInfo *segInfo; + sqInt start; + usqInt total; + + + /* statGrowMemory counts attempts, not successes. */ + /* we need to include overhead for a new object header plus the segment bridge. */ + GIV(statGrowMemory) += 1; + ammount = minAmmount + ((BaseHeaderSize * 2) + (2 * BaseHeaderSize)); + + /* round up to the nearest power of two. */ + ammount = 1ULL << (highBit(ammount - 1)); + + /* and grow by at least growHeadroom. */ + ammount = ((ammount < GIV(growHeadroom)) ? GIV(growHeadroom) : ammount); + + /* Now apply the maxOldSpaceSize limit, if one is in effect. */ + if (maxOldSpaceSize > 0) { + /* begin totalOldSpaceSize */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)); + total = GIV(totalHeapSizeIncludingBridges); + if (total >= maxOldSpaceSize) { + GIV(needGCFlag) = 1; + return null; + } + headroom = maxOldSpaceSize - total; + if (headroom < ammount) { + if (headroom < (minAmmount + ((BaseHeaderSize * 2) + (2 * BaseHeaderSize)))) { + GIV(needGCFlag) = 1; + return null; + } + ammount = headroom; + } + } + start = ioUTCMicrosecondsNow(); + if ((segInfo = addSegmentOfSize(ammount))) { + /* begin assimilateNewSegment: */ + if ((((segInfo->segSize)) + ((segInfo->segStart))) >= GIV(endOfMemory)) { + GIV(freeOldSpaceStart) = (GIV(endOfMemory) = (((segInfo->segSize)) + ((segInfo->segStart))) - (2 * BaseHeaderSize)); + } + + /* and add the new free chunk to the free list; done here + instead of in assimilateNewSegment: for the assert */ + bytes = ((segInfo->segSize)) - (2 * BaseHeaderSize); + address = (segInfo->segStart); + + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += bytes; + freeChunkWithBytesat(bytes, address); + assert((addressAfter(objectStartingAt((segInfo->segStart)))) == ((segLimit(segInfo)) - (bridgeSize()))); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCCheckFreeSpace)) == (GCCheckFreeSpace | GCCheckFreeSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + checkSegments(); + interval = (ioUTCMicrosecondsNow()) - start; + if (interval > GIV(statMaxAllocSegmentTime)) { + GIV(statMaxAllocSegmentTime) = interval; + } + return (segInfo->segSize); + } + GIV(needGCFlag) = 1; + return null; +} + + /* SpurMemoryManager>>#hasPointerFieldsNonImm: */ +static NoDbgRegParms sqInt +hasPointerFieldsNonImm(sqInt oop) +{ + sqInt format; + + format = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isAnyPointerFormat: */ + return (format <= 5 /* lastPointerFormat */) + || (format >= (firstCompiledMethodFormat())); +} + + +/* The header format in LSB is + MSB: | 8: numSlots | (on a byte boundary) + | 2 bits | (msb,lsb = {isMarked,isGrey}) + | 22: identityHash | (on a word boundary) + | 3 bits | (msb <-> lsb = {UNUSED,isPinned,isRemembered} + | 5: format | (on a byte boundary) + | 2 bits | (msb,lsb = {isImmutable,isExtraAligned}) + | 22: classIndex | (on a word boundary) : LSB + The 7 bit flags are used (or reserved) for + isExtraAligned (bit 22) + isImmutable (bit 23) + isRemembered (bit 29) + isPinned (bit 30) + UNUSED (bit 31) + isGrey (bit 54) + isMarked (bit 55) + The three bit field { UNUSED, isPinned, isRemembered } is for bits that + are never + set in young objects (forcing uses of the UNUSED bit to be for oldSPace + objects only). + This allows the remembered table to be pruned when full by using these + bits as a + reference count of newSpace objects from the remembered table. Objects + with a + high count should be tenured to prune the remembered table. */ + + /* SpurMemoryManager>>#headerForSlots:format:classIndex: */ +usqLong +headerForSlotsformatclassIndex(sqInt numSlots, sqInt formatField, sqInt classIndex) +{ + return ((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex; +} + + +/* The header format in LSB is + MSB: | 8: numSlots | (on a byte boundary) + | 2 bits | (msb,lsb = {isMarked,isGrey}) + | 22: identityHash | (on a word boundary) + | 3 bits | (msb <-> lsb = {UNUSED,isPinned,isRemembered} + | 5: format | (on a byte boundary) + | 2 bits | (msb,lsb = {isImmutable,isExtraAligned}) + | 22: classIndex | (on a word boundary) : LSB + The 7 bit flags are used (or reserved) for + isExtraAligned (bit 22) + isImmutable (bit 23) + isRemembered (bit 29) + isPinned (bit 30) + UNUSED (bit 31) + isGrey (bit 54) + isMarked (bit 55) + The three bit field { UNUSED, isPinned, isRemembered } is for bits that + are never + set in young objects (forcing uses of the UNUSED bit to be for oldSPace + objects only). + This allows the remembered table to be pruned when full by using these + bits as a + reference count of newSpace objects from the remembered table. Objects + with a + high count should be tenured to prune the remembered table. */ + + /* SpurMemoryManager>>#headerForSlots:hash:format:classIndex: */ +static NoDbgRegParms usqLong +headerForSlotshashformatclassIndex(sqInt numSlots, sqInt hash, sqInt formatField, sqInt classIndex) +{ + return (((((((usqLong) numSlots)) << (numSlotsFullShift()))) + (((((usqLong) hash)) << (identityHashFullWordShift())))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex; +} + + +/* Answer the number of extra root slots in the root of the hidden root + object. + */ + + /* SpurMemoryManager>>#hiddenRootSlots */ +static sqInt +hiddenRootSlots(void) +{ + return 8; +} + + /* SpurMemoryManager>>#hiddenRootsObject */ +static sqInt +hiddenRootsObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(hiddenRootsObj); +} + + +/* a more complex version that tells both the word reversal and the + endianness of the machine + it came from. Low half of word is e.g. 6521. Top byte is top byte of + #doesNotUnderstand: on + this machine. ($d on the Mac or $s on the PC) */ + + /* SpurMemoryManager>>#imageSegmentVersion */ +static sqInt +imageSegmentVersion(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int wholeWord; + + /* first data word, 'does' */ + wholeWord = long32At((void *)((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord()))))))) + BaseHeaderSize)); + return 68021 /* imageFormatVersion */ | (wholeWord & 0xFF000000U); +} + + +/* mask the immutable bit in the base header word */ + + /* SpurMemoryManager>>#immutableBitMask */ +#if IMMUTABILITY +sqInt +immutableBitMask(void) +{ + return 1U << (immutableBitShift()); +} +#endif /* IMMUTABILITY */ + + /* SpurMemoryManager>>#indexOf:in: */ +sqInt +indexOfin(sqInt anElement, sqInt anObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt fmtSqInt; + sqInt i; + usqInt numSlots; + usqInt numSlotsUsqInt; + + fmt = (byteAt((void *)(anObject + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(anObject)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(anObject + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(anObject - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + for (i = 0; i <= numSlots; i += 1) { + if (anElement == (longAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + return i; + } + } + } + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + return (GIV(primFailCode) = PrimErrUnsupported); + } + + /* begin numBytesOfBytes: */ + fmtSqInt = (byteAt((void *)(anObject + (formatFieldByteOffset())))) & (formatMask()); + assert(fmtSqInt >= (firstByteFormat())); + numSlots = ((((/* begin numSlotsOf: */ + assert((classIndexOf(anObject)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(anObject + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(anObject - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) << (shiftForWord()))) - (fmtSqInt & 7); + for (i = 0; i <= numSlots; i += 1) { + if (anElement == (byteAt((void *)((anObject + BaseHeaderSize) + i)))) { + return i; + } + } + } + if (fmt >= (firstShortFormat())) { + numSlots = ((usqInt)((numBytesOf(anObject)))) >> 1; + for (i = 0; i <= numSlots; i += 1) { + if (anElement == (((unsigned short) (shortAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(i) << 1))))))))) { + return i; + } + } + } + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(anObject)))) >> 3; + for (i = 0; i <= numSlots; i += 1) { + if (anElement == (long64At((void *)((anObject + BaseHeaderSize) + ((((usqInt)(i) << 3))))))) { + return i; + } + } + } + if (fmt >= (firstLongFormat())) { + numSlots = ((usqInt)((numBytesOf(anObject)))) >> 2; + for (i = 0; i <= numSlots; i += 1) { + if (anElement == (long32At((void *)((anObject + BaseHeaderSize) + ((((usqInt)(i) << 2))))))) { + return i; + } + } + } + return -1; +} + + +/* Part of reorderReversedTreeList:. Switch treeNode with newNode in + the tree, but do nothing to the list linked through freeChunkNextIndex. */ + + /* SpurMemoryManager>>#inFreeTreeReplace:with: */ +static NoDbgRegParms void +inFreeTreeReplacewith(sqInt treeNode, sqInt newNode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fieldIndex; + sqInt i; + sqInt relative; + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(newNode)); + longAtput((void *)((newNode + BaseHeaderSize) + (1U << (shiftForWord()))),0); + + /* copy parent, smaller, larger */ + for (i = 2 /* freeChunkParentIndex */; i <= 4 /* freeChunkLargerIndex */; i += 1) { + relative = longAt((void *)((treeNode + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (i == 2 /* freeChunkParentIndex */) { + if (relative) { + fieldIndex = (treeNode == (longAt((void *)((relative + BaseHeaderSize) + (3U << (shiftForWord()))))) + ? 3 /* freeChunkSmallerIndex */ + : 4 /* freeChunkLargerIndex */); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(relative)); + assert((newNode == 0) + || (isFreeObject(newNode))); + longAtput((void *)((relative + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),newNode); + } + else { + assert((GIV(freeLists)[0]) == treeNode); + GIV(freeLists)[0] = newNode; + } + } + else { + if (relative) { + assert((fetchPointerofFreeChunk(freeChunkParentIndex(), relative)) == treeNode); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(relative)); + assert((newNode == 0) + || (isFreeObject(newNode))); + longAtput((void *)((relative + BaseHeaderSize) + (2U << (shiftForWord()))),newNode); + } + } + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(newNode)); + assert((relative == 0) + || (isFreeObject(relative))); + longAtput((void *)((newNode + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),relative); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(treeNode)); + longAtput((void *)((treeNode + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),0); + } +} + + /* SpurMemoryManager>>#initialInstanceOf: */ +static NoDbgRegParms sqInt +initialInstanceOf(sqInt classObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int classIndex; + int classIndexInt; + sqInt classIndexSqInt; + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + if (classIndex) { + /* flush instances in newSpace to settle the enumeration. */ + + /* begin flushNewSpaceInstancesOf: */ + classIndexInt = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + if (!classIndexInt) { + goto l1; + } + + /* no instances; nothing to do */ + + /* begin tenuringClassIndex: */ + GIV(tenuringClassIndex) = classIndexInt; + scavengingGCTenuringIf(TenureByClass); + assert(!(existInstancesInNewSpaceOf(classObj))); + /* end flushNewSpaceInstancesOf: */ +l1: + + /* scan old space for the first one found. */ + + /* begin allOldSpaceObjectsDo: */ + /* begin allOldSpaceObjectsFrom:do: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndexSqInt = (longAt((void *)(objOopSqInt))) & (classIndexMask()); + assert((classIndexSqInt == (segmentBridgePun())) + || ((classIndexSqInt == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objOopSqInt))) != 0) + && (classIndexSqInt < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndexSqInt >= (isForwardedObjectClassIndexPun())) { + if (classIndex == ((longAt((void *)(objOopSqInt))) & (classIndexMask()))) { + return objOopSqInt; + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l2:; + } + } + return null; +} + + /* SpurMemoryManager>>#initializeNewSpaceVariables */ +static NeverInline void +initializeNewSpaceVariables(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(freeStart) = ((GIV(eden)).start); + GIV(pastSpaceStart) = ((GIV(pastSpace)).start); + GIV(scavengeThreshold) = ((((GIV(eden)).limit)) - ((((GIV(eden).limit)) - ((GIV(eden).start))) / 64)) - (interpreterAllocationReserveBytes()); + GIV(newSpaceStart) = (((((GIV(pastSpace)).start)) < (((GIV(futureSpace)).start))) ? (((GIV(pastSpace)).start)) : (((GIV(futureSpace)).start))); + assert(GIV(newSpaceStart) < (((GIV(eden)).start))); +} + + +/* Initialize object memory variables at startup time. Assume endOfMemory at + al are + initialised by the image-reading code via + setHeapBase:memoryLimit:endOfMemory:. endOfMemory is assumed to point to + the end of the last object in the image. + Assume: image reader also initializes the following variables: + specialObjectsOop + lastHash */ +/* Catch mis-initializations leading to bad translations to C */ + + /* SpurMemoryManager>>#initializeObjectMemory: */ +static NoDbgRegParms void +initializeObjectMemory(sqInt bytesToShift) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anOop; + sqInt cameFrom; + usqInt chunkBytes; + sqInt classIndex; + sqInt classTableRoot; + sqInt field; + sqInt fieldAddr; + sqInt fieldOop; + sqInt firstObj; + sqInt followingWord; + usqInt followingWordAddress; + sqInt freeChunk; + sqInt freeListObj; + usqInt freeOldStart; + sqInt fudge; + sqInt i; + sqInt index; + sqInt j; + sqInt largeChild; + usqInt limit; + sqInt min; + sqInt nilObjPreSwizzle; + usqInt numSlots; + sqInt obj; + usqInt p; + sqInt page; + sqInt smallChild; + usqInt startOfFreeOldSpace; + usqInt toDoLimit; + sqInt treeNode; + sqInt valuePointer; + + assert(BaseHeaderSize == BaseHeaderSize); + assert((((sqInt)((maxSlotsForAlloc()) * BytesPerWord))) > 0); + initSegmentBridgeWithBytesat(2 * BaseHeaderSize, GIV(endOfMemory) - (2 * BaseHeaderSize)); + adjustSegmentSwizzlesBy(bytesToShift); + + /* image may be at a different address; adjust oops for new location */ + + /* begin adjustAllOopsBy: */ + assert(newSpaceIsEmpty()); + numSlots = byteAt((void *)(GIV(oldSpaceStart) + (numSlotsFieldByteOffset()))); + firstObj = (numSlots == (numSlotsMask()) + ? GIV(oldSpaceStart) + BaseHeaderSize + : GIV(oldSpaceStart)); + classTableRoot = oldSpaceObjectAfter(oldSpaceObjectAfter(oldSpaceObjectAfter(oldSpaceObjectAfter(firstObj)))); + nilObjPreSwizzle = GIV(oldSpaceStart) - bytesToShift; + + /* begin numSlotsOf: */ + assert((classIndexOf(classTableRoot)) > (isForwardedObjectClassIndexPun())); + GIV(numClassTablePages) = (((numSlots = byteAt((void *)(classTableRoot + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classTableRoot - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + assert(GIV(numClassTablePages) == ((classTableRootSlots()) + (hiddenRootSlots()))); + for (i = 2; i < GIV(numClassTablePages); i += 1) { + if ((longAt((void *)((classTableRoot + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))))) == nilObjPreSwizzle) { + GIV(numClassTablePages) = i; + goto l4; + } + } + /* end countNumClassPagesPreSwizzle: */ +l4: + if ((bytesToShift != 0) + || (GIV(numSegments) > 1)) { + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(GIV(oldSpaceStart) + (numSlotsFieldByteOffset()))); + obj = (numSlots == (numSlotsMask()) + ? GIV(oldSpaceStart) + BaseHeaderSize + : GIV(oldSpaceStart)); + while (oopisLessThan(obj, GIV(freeOldSpaceStart))) { + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + /* begin swizzleFieldsOfObject: */ + fieldAddr = obj + (lastPointerOfWhileSwizzling(obj)); + while (oopisGreaterThanOrEqualTo(fieldAddr, obj + BaseHeaderSize)) { + fieldOop = longAt((void *)(fieldAddr)); + if ((!(fieldOop & (tagMask())))) { + longAtput((void *)(fieldAddr),swizzleObj(fieldOop)); + } + fieldAddr -= BytesPerOop; + } + } + else { + if (classIndex == (isFreeObjectClassIndexPun())) { + /* begin swizzleFieldsOfFreeChunk: */ + field = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + if (field) { + valuePointer = swizzleObj(field); + + /* begin storeUncheckedNoAssert:ofAny:withValue: */ + longAtput((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + } + chunkBytes = bytesInBody(obj); + + /* The prevPointer is not guaranteed to be valid in older images. + updateListStartingAt: via updateFreeLists does restore the prev pointer + in all small free lists, so simply avoid swizzling it now. */ + if (chunkBytes >= 0x200 /* (numFreeLists * #allocationUnit) */) { + for (index = 2 /* freeChunkParentIndex */; index <= 4 /* freeChunkLargerIndex */; index += 1) { + field = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (field) { + valuePointer = swizzleObj(field); + + /* begin storeUncheckedNoAssert:ofAny:withValue: */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),valuePointer); + } + } + } + } + } + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3:; + } + } + GIV(specialObjectsOop) = swizzleObj(GIV(specialObjectsOop)); + + /* heavily used special objects */ + GIV(nilObj) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(NilObject) << (shiftForWord())))))); + GIV(falseObj) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(FalseObject) << (shiftForWord())))))); + GIV(trueObj) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TrueObject) << (shiftForWord())))))); + + /* In Cog we insist that nil, true & false are next to each other (Cogit generates tighter + conditional branch code as a result). In addition, Spur places the free lists and + class table root page immediately following them. */ + assert(GIV(nilObj) == GIV(oldSpaceStart)); + assert(GIV(falseObj) == (oldSpaceObjectAfter(GIV(nilObj)))); + assert(GIV(trueObj) == (oldSpaceObjectAfter(GIV(falseObj)))); + freeListObj = oldSpaceObjectAfter(GIV(trueObj)); + anOop = oldSpaceObjectAfter(freeListObj); + + /* begin setHiddenRootsObj: */ + GIV(hiddenRootsObj) = anOop; + assert(validClassTableRootPages()); + GIV(classTableFirstPage) = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(((numSlotsOf(GIV(classTableFirstPage))) - 1) == (classTableMinorIndexMask())); + + /* Hack fix. A bug in markAndTraceClassOf: caused the class of the first class table page + to be changed from its pun. This can be restored manually, but we do it here too. */ + if (((longAt((void *)(GIV(classTableFirstPage)))) & (classIndexMask())) != (arrayClassIndexPun())) { + /* begin setClassIndexOf:to: */ + assert((((arrayClassIndexPun()) >= 0) && ((arrayClassIndexPun()) <= (classIndexMask())))); + longAtput((void *)(GIV(classTableFirstPage)),((longAt((void *)(GIV(classTableFirstPage)))) & (~(usqIntptr_t)(classIndexMask()))) + (arrayClassIndexPun())); + } + GIV(numClassTablePages) = 1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift())); + + /* If loading an image, set the classTableIndex to the first unused slot in the class table after the first page. + Set numClassTablePages to the number of used pages. + Set classTableIndex to point at the first unused entry. First set it to the max as a sentinel. */ + GIV(classTableIndex) = ((sqInt)((usqInt)(GIV(numClassTablePages)) << (classTableMajorIndexShift()))); + for (i = 1; i < GIV(numClassTablePages); i += 1) { + if (((page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) == GIV(nilObj)) { + if ((((usqInt)(GIV(classTableIndex))) >> (classTableMajorIndexShift())) > i) { + GIV(classTableIndex) = ((sqInt)((usqInt)(((((i - 1) < 1) ? 1 : (i - 1)))) << (classTableMajorIndexShift()))); + } + GIV(numClassTablePages) = i; + assert((classOrNilAtIndex(GIV(classTableIndex))) == GIV(nilObj)); + goto l1; + } + else { + if ((((usqInt)(GIV(classTableIndex))) >> (classTableMajorIndexShift())) > i) { + j = 0; + while (j < (1U << (classTableMajorIndexShift()))) { + if ((longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord()))))))) == GIV(nilObj)) { + GIV(classTableIndex) = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + j = 1U << (classTableMajorIndexShift()); + } + j += 1; + } + } + } + } + + /* no unused slots; set it to the start of the second page. */ + if ((((usqInt)(GIV(classTableIndex))) >> (classTableMajorIndexShift())) >= GIV(numClassTablePages)) { + GIV(classTableIndex) = 1U << (classTableMajorIndexShift()); + } + assert((classOrNilAtIndex(GIV(classTableIndex))) == GIV(nilObj)); + /* end setHiddenRootsObj: */ +l1: + GIV(markStack) = swizzleObjStackAt(MarkStackRootIndex); + GIV(weaklingStack) = swizzleObjStackAt(WeaklingStackRootIndex); + GIV(mournQueue) = swizzleObjStackAt(MournQueueRootIndex); + assert(validObjStacks()); + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + + /* begin initializeFreeSpacePostLoad: */ + assert((numSlotsOf(freeListObj)) == (numFreeLists())); + assert((formatOf(freeListObj)) == (wordIndexableFormat())); + GIV(freeLists) = firstIndexableField(freeListObj); + GIV(freeListsMask) = 0; + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + if (GIV(freeLists)[i]) { + GIV(freeListsMask) = GIV(freeListsMask) | (1ULL << i); + GIV(freeLists)[i] = (swizzleObj(GIV(freeLists)[i])); + } + } + collapseSegmentsPostSwizzle(); + + /* begin updateFreeLists */ + min = 3; + for (i = min; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + updateListStartingAt(GIV(freeLists)[i]); + } + + /* Large chunks */ + + /* begin freeTreeNodesDo: */ + treeNode = GIV(freeLists)[0]; + if (!treeNode) { + goto l2; + } + cameFrom = -1; + do { + assert((bytesInBody(treeNode)) >= ((numFreeLists()) * (allocationUnit()))); + smallChild = longAt((void *)((treeNode + BaseHeaderSize) + (3U << (shiftForWord())))); + largeChild = longAt((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + assert((smallChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), smallChild)))); + assert((largeChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), largeChild)))); + + /* apply if the node has no children, or it has no large children and we're + returning from the small child, or we're returning from the large child. */ + if (((smallChild == 0) + && (largeChild == 0)) + || ((largeChild + ? cameFrom == largeChild + : cameFrom == smallChild))) { + updateListStartingAt(treeNode); + + /* and since we've applied we must move on up */ + cameFrom = treeNode; + treeNode = longAt((void *)((treeNode + BaseHeaderSize) + (2U << (shiftForWord())))); + } + else { + if ((smallChild != 0) + && (cameFrom != smallChild)) { + treeNode = smallChild; + } + else { + assert(largeChild != 0); + treeNode = largeChild; + } + cameFrom = -1; + } + } while(treeNode != 0); + /* end freeTreeNodesDo: */ +l2: + + /* begin computeFreeSpacePostSwizzle */ + GIV(totalFreeOldSpace) = totalFreeListBytes(); + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + startOfFreeOldSpace = GIV(freeOldSpaceStart); + + /* begin initializeOldSpaceFirstFree: */ + limit = GIV(endOfMemory) - (2 * BaseHeaderSize); + if (limit > startOfFreeOldSpace) { + GIV(totalFreeOldSpace) += limit - startOfFreeOldSpace; + freeOldStart = startOfFreeOldSpace; + while ((limit - freeOldStart) >= (0x100000000LL)) { + freeChunk = freeChunkWithBytesat(0x100000000LL, freeOldStart); + freeOldStart += 0x100000000LL; + assert(freeOldStart == (addressAfter(freeChunk))); + } + if (freeOldStart < limit) { + freeChunk = freeChunkWithBytesat(limit - freeOldStart, freeOldStart); + assert((addressAfter(freeChunk)) == limit); + } + } + GIV(endOfMemory) -= 2 * BaseHeaderSize; + GIV(freeOldSpaceStart) = GIV(endOfMemory); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCCheckFreeSpace)) == (GCCheckFreeSpace | GCCheckFreeSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + initializeNewSpaceVariables(); + + /* begin initializeRememberedSet */ + obj = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))); + if (obj == GIV(nilObj)) { + /* begin allocatePinnedSlots: */ + obj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(0x400, (1024U << (shiftForWord())) + (BaseHeaderSize + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (obj) { + assert(isPinned(obj)); + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((obj + BaseHeaderSize) + (0x400 * BytesPerOop)) - 1, addressAfter(obj))); + toDoLimit = ((usqInt)(((obj + BaseHeaderSize) + (0x400 * BytesPerOop)) - 1)); + for (p = (((usqInt)(obj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + + /* begin rememberedSetObj: */ + assert(isOldObject(obj)); + assert((isNonImmediate(GIV(hiddenRootsObj))) + && (!(isForwarded(GIV(hiddenRootsObj))))); + assert(validStorePointerUncheckedArgs(RememberedSetRootIndex, GIV(hiddenRootsObj), obj)); + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord()))))),obj); + } + else { + /* begin setFormatOf:to: */ + assert((((sixtyFourBitIndexableFormat()) >= 0) && ((sixtyFourBitIndexableFormat()) <= (formatMask())))); + byteAtput((void *)(obj + (formatFieldByteOffset())),((byteAt((void *)(obj + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (sixtyFourBitIndexableFormat())); + } + + /* The Spur32to64BitBootstrap failed to set the type of rememberedSetObj to 64-bit indexability. + This is unimportant except for simulation; rememberedSet is declared as sqInt *, but to have + firstIndexableField: below answer a suitable type the format must be wordIndexableFormat. */ + assert((formatOf(obj)) == (wordIndexableFormat())); + assert(isPinned(obj)); + GIV(rememberedSet) = firstIndexableField(obj); + GIV(rememberedSetSize) = 0; + + /* begin numSlotsOf: */ + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())); + GIV(rememberedSetLimit) = (((numSlots = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + + /* begin setRememberedSetRedZone */ + fudge = ((((GIV(eden).limit)) - ((GIV(eden).start))) / BytesPerWord) / 0x400; + GIV(rememberedSetRedZone) = ((((GIV(rememberedSetLimit) * 3) / 4) < fudge) ? fudge : ((GIV(rememberedSetLimit) * 3) / 4)); + checkSegments(); + + /* begin biasForGC */ + GIV(biasForGC) = 1; + + /* These defaults should depend on machine size; e.g. too small on a powerful laptop, too big on a Pi. */ + + /* headroom when growing */ + GIV(growHeadroom) = 0x1000000; + + /* free space before shrinking */ + GIV(shrinkThreshold) = 0x2000000; + GIV(heapSizeAtPreviousGC) = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + + /* begin resetAllocationAccountingAfterGC */ + GIV(oldSpaceUsePriorToScavenge) = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + + /* By default GC after scavenge if heap has grown by a third since the last GC */ + GIV(heapGrowthToSizeGCRatio) = 0.333333; +} + + +/* Initialize unscannedEphemerons to use the largest unused new space, + which ever is the larger. We haven't yet scavenged, so eden may be + almost full, but futureSpace is known to be empty. N.B. we *cannot* + use free old space because it may be used to grow the markStack. */ + + /* SpurMemoryManager>>#initializeUnscannedEphemerons */ +static void +initializeUnscannedEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt sizeOfFutureSpace; + sqInt sizeOfUnusedEden; + + sizeOfUnusedEden = (((GIV(eden)).limit)) - GIV(freeStart); + sizeOfFutureSpace = (((GIV(futureSpace)).limit)) - (((GIV(futureSpace)).start)); + if (sizeOfUnusedEden > sizeOfFutureSpace) { + (GIV(unscannedEphemerons).start = GIV(freeStart)); + (GIV(unscannedEphemerons).limit = ((GIV(eden)).limit)); + } + else { + (GIV(unscannedEphemerons).start = ((GIV(futureSpace)).start)); + (GIV(unscannedEphemerons).limit = ((GIV(futureSpace)).limit)); + } + (GIV(unscannedEphemerons).top = ((GIV(unscannedEphemerons).start)) - BytesPerOop); + assert(numUnscannedEphemerons() == 0); +} + + +/* print free chunks in freeTree in order. */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#inOrderPrintFreeTree:printList: */ +void +inOrderPrintFreeTreeprintList(sqInt freeChunk, sqInt printNextList) +{ + sqInt next; + + if ((next = longAt((void *)((freeChunk + BaseHeaderSize) + (3U << (shiftForWord())))))) { + inOrderPrintFreeTreeprintList(next, printNextList); + } + printFreeChunkprintAsTreeNode(freeChunk, 1); + if (printNextList) { + next = freeChunk; + while (((next = longAt((void *)((next + BaseHeaderSize) + (0U << (shiftForWord())))))) != 0) { + /* begin tab */ + printChar('\t'); + printFreeChunkprintAsTreeNode(next, 0); + } + } + if ((next = longAt((void *)((freeChunk + BaseHeaderSize) + (4U << (shiftForWord())))))) { + inOrderPrintFreeTreeprintList(next, printNextList); + } +} + + +/* in an effort to fix a compiler bug with two-way become post r3427 */ +/* Do become in place by swapping object contents. */ + + /* SpurMemoryManager>>#inPlaceBecome:and:copyHashFlag: */ +static NoDbgRegParms NeverInline void +inPlaceBecomeandcopyHashFlag(sqInt obj1, sqInt obj2, sqInt copyHashFlag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqLong hashBits; + sqLong header1; + sqLong header2; + sqInt i; + usqInt numSlots; + sqInt o1HasYoung; + sqInt o2HasYoung; + sqLong remembered; + sqInt temp1; + sqInt temp2; + sqInt toDoLimit; + + assert((numSlotsOf(obj1)) == (numSlotsOf(obj2))); + assert(((rawHashBitsOf(obj1)) == 0) + || ((classOrNilAtIndex(rawHashBitsOf(obj1))) != obj1)); + assert(((rawHashBitsOf(obj2)) == 0) + || ((classOrNilAtIndex(rawHashBitsOf(obj2))) != obj2)); + + /* swap headers, but swapping headers swaps remembered bits and hashes; + remembered bits must be unswapped and hashes may be unswapped if + copyHash is false. */ + + /* begin cleverSwapHeaders:and:copyHashFlag: */ + header1 = long64At((void *)(obj1)); + header2 = long64At((void *)(obj2)); + remembered = (header1 ^ header2) & (1U << (rememberedBitShift())); + if (remembered) { + header1 = header1 ^ remembered; + header2 = header2 ^ remembered; + } + + /* swapping headers swaps hash; if not copyHashFlag then unswap hash */ + if (!copyHashFlag) { + hashBits = (header1 ^ header2) & 0x3FFFFF00000000LL /* identityHashFullWordMask */; + if (hashBits) { + header1 = header1 ^ hashBits; + header2 = header2 ^ hashBits; + } + } + long64Atput((void *)(obj1),header2); + long64Atput((void *)(obj2),header1); + o1HasYoung = (o2HasYoung = 0); + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(obj1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(obj1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + temp1 = longAt((void *)((obj1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + temp2 = longAt((void *)((obj2 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(obj1)) + && (!(isForwarded(obj1)))); + assert(validStorePointerUncheckedArgs(i, obj1, temp2)); + longAtput((void *)((obj1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),temp2); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(obj2)) + && (!(isForwarded(obj2)))); + assert(validStorePointerUncheckedArgs(i, obj2, temp1)); + longAtput((void *)((obj2 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),temp1); + if (/* isYoung: */ + ((!(temp2 & (tagMask())))) + && (oopisLessThan(temp2, GIV(oldSpaceStart)))) { + o1HasYoung = 1; + } + if (/* isYoung: */ + ((!(temp1 & (tagMask())))) + && (oopisLessThan(temp1, GIV(oldSpaceStart)))) { + o2HasYoung = 1; + } + } + if (o1HasYoung + && ((/* begin isOldObject: */ + assert(isNonImmediate(obj1)), + oopisGreaterThanOrEqualTo(obj1, GIV(oldSpaceStart))))) { + fmt = (byteAt((void *)(obj1 + (formatFieldByteOffset())))) & (formatMask()); + if (!(/* isPureBitsFormat: */ + (fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat())))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj1 + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj1); + } + } + } + if (o2HasYoung + && ((/* begin isOldObject: */ + assert(isNonImmediate(obj2)), + oopisGreaterThanOrEqualTo(obj2, GIV(oldSpaceStart))))) { + fmt = (byteAt((void *)(obj2 + (formatFieldByteOffset())))) & (formatMask()); + if (!(/* isPureBitsFormat: */ + (fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat())))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj2 + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj2); + } + } + } +} + + +/* Answer the number of slots in a class. For example the instanceSizeOf: + ClassPoint is 2, for the x & y slots. The instance size of non-pointer + classes is 0. */ + + /* SpurMemoryManager>>#instanceSizeOf: */ +sqInt +instanceSizeOf(sqInt classObj) +{ + assert(addressCouldBeClassObj(classObj)); + return (((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); +} + + /* SpurMemoryManager>>#instantiateClass: */ +static NoDbgRegParms sqInt +instantiateClass(sqInt classObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt err; + int hash; + sqInt instSpec; + sqInt newObj; + usqInt newObjUsqInt; + usqInt numBytes; + sqInt numSlots; + usqInt p; + usqInt toDoLimit; + + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + if (!(/* isFixedSizePointerFormat: */ + (instSpec <= (nonIndexablePointerFormat())) + || (instSpec == (ephemeronFormat())))) { + return null; + } + + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + return null; + } + numSlots = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if ((((usqInt)(numSlots)) >> 56) > 0) { + newObj = null; + goto l1; + } + newObjUsqInt = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObjUsqInt = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, instSpec, classIndex); + goto l1; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObjUsqInt),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObjUsqInt),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObjUsqInt % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newObj = newObjUsqInt; + /* end allocateSlots:format:classIndex: */ +l1: + if (newObj) { + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),GIV(nilObj)); + } + } + return newObj; +} + + /* SpurMemoryManager>>#instSpecOfClassFormat: */ +sqInt +instSpecOfClassFormat(sqInt classFormat) +{ + return (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); +} + + +/* This field in a class's format inst var corresponds to the 5-bit format + field stored in every object header + */ + + /* SpurMemoryManager>>#instSpecOfClass: */ +static NoDbgRegParms sqInt +instSpecOfClass(sqInt classPointer) +{ + return (((usqInt)((((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); +} + + /* SpurMemoryManager>>#invalidCompactClassError: */ +static NoDbgRegParms void +invalidCompactClassError(const char *className) +{ + printf("\nClass %s does not have the required class index\n", className); + exit(-1); +} + + +/* Answer true if this is an indexable object with pointer elements, e.g., an + array + */ + + /* SpurMemoryManager>>#isArray: */ +sqInt +isArray(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())); +} + + +/* Answer if objOop should be included in an allObjects...Do: enumeration. + This is for assert-checking only. */ + + /* SpurMemoryManager>>#isBridgeOrEnumerableObjectNoAssert: */ +static NoDbgRegParms int +isBridgeOrEnumerableObjectNoAssert(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + return (classIndex >= (isForwardedObjectClassIndexPun()) + ? classIndex < (GIV(numClassTablePages) * (1U << (classTableMajorIndexShift()))) + : classIndex == (segmentBridgePun())); +} + + /* SpurMemoryManager>>#isCharacterObject: */ +int +isCharacterObject(sqInt oop) +{ + return ((oop & (characterTag())) != 0); +} + + /* SpurMemoryManager>>#isCharacterValue: */ +int +isCharacterValue(sqInt anInteger) +{ + return ((anInteger >= 0) && (anInteger <= (0x3FFFFFFF))); +} + + /* SpurMemoryManager>>#isCompiledMethodFormat: */ +static NoDbgRegParms int +isCompiledMethodFormat(sqInt format) +{ + return format >= (firstCompiledMethodFormat()); +} + + +/* Answer whether the argument header has compiled method format */ + + /* SpurMemoryManager>>#isCompiledMethodHeader: */ +static NoDbgRegParms int +isCompiledMethodHeader(sqInt objHeader) +{ + return ((((usqInt)(objHeader)) >> (formatShift())) & (formatMask())) >= (firstCompiledMethodFormat()); +} + + +/* Answer whether the argument object is of compiled method format */ + + /* SpurMemoryManager>>#isCompiledMethod: */ +int +isCompiledMethod(sqInt objOop) +{ + return ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()); +} + + /* SpurMemoryManager>>#isContextHeader: */ +static NoDbgRegParms int +isContextHeader(sqInt aHeader) +{ + return (aHeader & (classIndexMask())) == ClassMethodContextCompactIndex; +} + + /* SpurMemoryManager>>#isContextNonImm: */ +static NoDbgRegParms int +isContextNonImm(sqInt oop) +{ + return ((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex; +} + + /* SpurMemoryManager>>#isContext: */ +static NoDbgRegParms sqInt +isContext(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex); +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. */ + + /* SpurMemoryManager>>#isCopiedIntoSegment: */ +static NoDbgRegParms int +isCopiedIntoSegment(sqInt anObjectInTheHeap) +{ + return ((byteAt((void *)(anObjectInTheHeap + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0; +} + + /* SpurMemoryManager>>#isEmptyObjStack: */ +static NoDbgRegParms sqInt +isEmptyObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (objStack == GIV(nilObj)) { + return 1; + } + eassert(isValidObjStack(objStack)); + return (0 == (longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))))) + && (0 == (longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))); +} + + +/* Answer if objOop should be included in an allObjects...Do: enumeration. + This is for assert-checking only. */ + + /* SpurMemoryManager>>#isEnumerableObjectNoAssert: */ +static NoDbgRegParms sqInt +isEnumerableObjectNoAssert(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + return (classIndex >= (isForwardedObjectClassIndexPun())) + && (classIndex < (GIV(numClassTablePages) * (1U << (classTableMajorIndexShift())))); +} + + /* SpurMemoryManager>>#isEphemeronFormat: */ +static NoDbgRegParms int +isEphemeronFormat(sqInt format) +{ + return format == (ephemeronFormat()); +} + + /* SpurMemoryManager>>#isEphemeron: */ +static NoDbgRegParms int +isEphemeron(sqInt objOop) +{ + sqInt format; + + assert(isNonImmediate(objOop)); + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isEphemeronFormat: */ + return format == (ephemeronFormat()); +} + + /* SpurMemoryManager>>#isFixedSizePointerFormat: */ +sqInt +isFixedSizePointerFormat(sqInt format) +{ + return (format <= (nonIndexablePointerFormat())) + || (format == (ephemeronFormat())); +} + + /* SpurMemoryManager>>#isForwardedClassTag: */ +static NoDbgRegParms int +isForwardedClassTag(sqInt classIndex) +{ + return classIndex == (isForwardedObjectClassIndexPun()); +} + + +/* Answer if objOop is that if a forwarder. Take advantage of + isForwardedObjectClassIndexPun being a power of two to generate a more + efficient test than the straight-forward + (self classIndexOf: objOop) = self isForwardedObjectClassIndexPun + at the cost of this being ambiguous with free chunks. So either never + apply this to free chunks + or guard with (self isFreeObject: foo) not. So far the idiom has been to + guard with isFreeObject: */ +/* self assert: (self isFreeObject: objOop) not. */ + + /* SpurMemoryManager>>#isForwarded: */ +int +isForwarded(sqInt objOop) +{ + return (!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))); +} + + /* SpurMemoryManager>>#isFreeObject: */ +static NoDbgRegParms int +isFreeObject(sqInt objOop) +{ + return ((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun()); +} + + /* SpurMemoryManager>>#isFreeOop: */ +static NoDbgRegParms sqInt +isFreeOop(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())); +} + + /* SpurMemoryManager>>#isImmediate: */ +int +isImmediate(sqInt oop) +{ + return ((oop & (tagMask())) != 0); +} + + /* SpurMemoryManager>>#isInClassTable: */ +static NoDbgRegParms sqInt +isInClassTable(sqInt objOop) +{ + int hash; + + hash = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()); + return (hash != 0) + && ((classAtIndex(hash)) == objOop); +} + + /* SpurMemoryManager>>#isIndexable: */ +sqInt +isIndexable(sqInt objOop) +{ + sqInt fmt; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + return /* isIndexableFormat: */ + (fmt >= (arrayFormat())) + && ((fmt <= (weakArrayFormat())) + || (fmt >= (sixtyFourBitIndexableFormat()))); +} + + /* SpurMemoryManager>>#isInEden: */ +static NoDbgRegParms sqInt +isInEden(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return oopisGreaterThanOrEqualToandLessThan(objOop, ((GIV(eden)).start), GIV(freeStart)); +} + + /* SpurMemoryManager>>#isInFutureSpace: */ +static NoDbgRegParms sqInt +isInFutureSpace(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return oopisGreaterThanOrEqualToandLessThan(address, ((GIV(futureSpace)).start), GIV(futureSurvivorStart)); +} + + +/* Answer if the given address is in ST object memory. */ + + /* SpurMemoryManager>>#isInMemory: */ +sqInt +isInMemory(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt addressUsqInt; + sqInt i; + + if (/* isInNewSpace: */ + (oopisLessThan(address, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(address, GIV(newSpaceStart)))) { + return (oopisGreaterThanOrEqualToandLessThan(address, ((GIV(eden)).start), GIV(freeStart))) + || ((oopisGreaterThanOrEqualToandLessThan(address, ((GIV(pastSpace)).start), GIV(pastSpaceStart))) + || ((GIV(gcPhaseInProgress) == ScavengeInProgress) + && (oopisGreaterThanOrEqualToandLessThan(address, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))))); + } + addressUsqInt = address; + + /* begin isInSegments: */ + for (i = 0; i < GIV(numSegments); i += 1) { + if (addressUsqInt < (((GIV(segments)[i]).segStart))) { + return 0; + } + if (addressUsqInt < ((((GIV(segments)[i]).segSize)) + (((GIV(segments)[i]).segStart)))) { + return 1; + } + } + return 0; +} + + /* SpurMemoryManager>>#isInNewSpace: */ +static NoDbgRegParms sqInt +isInNewSpace(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (oopisLessThan(objOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(objOop, GIV(newSpaceStart))); +} + + /* SpurMemoryManager>>#isInOldSpace: */ +sqInt +isInOldSpace(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return oopisGreaterThanOrEqualToandLessThan(address, GIV(oldSpaceStart), GIV(endOfMemory)); +} + + /* SpurMemoryManager>>#isInPastSpace: */ +static NoDbgRegParms sqInt +isInPastSpace(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return oopisGreaterThanOrEqualToandLessThan(address, ((GIV(pastSpace)).start), GIV(pastSpaceStart)); +} + + /* SpurMemoryManager>>#isLargeFreeObject: */ +static NoDbgRegParms int +isLargeFreeObject(sqInt objOop) +{ + return (bytesInBody(objOop)) >= 0x200 /* (numFreeLists * #allocationUnit) */; +} + + +/* Answer if the argument contains only indexable 64-bit double words (no + oops). See comment in formatOf: + */ + + /* SpurMemoryManager>>#isLong64s: */ +sqInt +isLong64s(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) == (sixtyFourBitIndexableFormat())); +} + + /* SpurMemoryManager>>#isMarked: */ +int +isMarked(sqInt objOop) +{ + return ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0; +} + + +/* Answer if objOop is an ephemeron that may or may not have been fired. + Unfired ephemerons have ephemeronFormat as their format. Fired ephemerons + have their format set to nonIndexablePointerFormat, so to disambiguate, + check the class is an ephemeron class in this case. */ + + /* SpurMemoryManager>>#isMaybeFiredEphemeron: */ +static NoDbgRegParms sqInt +isMaybeFiredEphemeron(sqInt objOop) +{ + sqInt format; + + if (((objOop & (tagMask())) != 0)) { + return 0; + } + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + return (format == (ephemeronFormat())) + || ((format == (nonIndexablePointerFormat())) + && ((instSpecOfClass(fetchClassOfNonImm(objOop))) == (ephemeronFormat()))); +} + + /* SpurMemoryManager>>#isNonImmediate: */ +int +isNonImmediate(sqInt oop) +{ + return (!(oop & (tagMask()))); +} + + /* SpurMemoryManager>>#isObjEphemeron: */ +static NoDbgRegParms int +isObjEphemeron(sqInt objOop) +{ + sqInt format; + + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isEphemeronFormat: */ + return format == (ephemeronFormat()); +} + + /* SpurMemoryManager>>#isObjImmutable: */ +static NoDbgRegParms sqInt +isObjImmutable(sqInt anOop) +{ + return +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(anOop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ; +} + + +/* Answer if obj is old. Require that obj is non-immediate. */ + + /* SpurMemoryManager>>#isOldObject: */ +static NoDbgRegParms int +isOldObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(isNonImmediate(objOop)); + return oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart)); +} + + +/* Answer whether the oop is an object of compiled method format */ + + /* SpurMemoryManager>>#isOopCompiledMethod: */ +sqInt +isOopCompiledMethod(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())); +} + + /* SpurMemoryManager>>#isOopForwarded: */ +sqInt +isOopForwarded(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))); +} + + /* SpurMemoryManager>>#isOopImmutable: */ +sqInt +isOopImmutable(sqInt oop) +{ + return (((oop & (tagMask())) != 0)) + || (((((usqInt)((byteAt((void *)(oop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0); +} + + /* SpurMemoryManager>>#isOopMutable: */ +sqInt +isOopMutable(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (!(((((usqInt)((byteAt((void *)(oop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0)); +} + + /* SpurMemoryManager>>#isPinned: */ +int +isPinned(sqInt objOop) +{ + return ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0; +} + + +/* Answer if the argument has only fields that can hold oops. See comment in + formatOf: + */ + + /* SpurMemoryManager>>#isPointers: */ +sqInt +isPointers(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */); +} + + +/* the inverse of isAnyPointerFormat: */ + + /* SpurMemoryManager>>#isPureBitsFormat: */ +static NoDbgRegParms sqInt +isPureBitsFormat(sqInt format) +{ + return (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat())); +} + + +/* Answer if the argument contains only indexable words (no oops). See + comment in formatOf: + */ + + /* SpurMemoryManager>>#isPureBitsNonImm: */ +static NoDbgRegParms sqInt +isPureBitsNonImm(sqInt objOop) +{ + sqInt format; + + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isPureBitsFormat: */ + return (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat())); +} + + /* SpurMemoryManager>>#isRemembered: */ +static NoDbgRegParms int +isRemembered(sqInt objOop) +{ + return ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0; +} + + +/* Maybe this should be in SpurSegmentManager only */ + + /* SpurMemoryManager>>#isSegmentBridge: */ +static NoDbgRegParms int +isSegmentBridge(sqInt objOop) +{ + return ((longAt((void *)(objOop))) & (classIndexMask())) == (segmentBridgePun()); +} + + /* SpurMemoryManager>>#isSemaphoreOop: */ +static NoDbgRegParms sqInt +isSemaphoreOop(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((!(anOop & (tagMask())))) + && (((longAt((void *)(anOop))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))); +} + + +/* Answer if the argument contains only indexable 16-bit half words (no + oops). See comment in formatOf: + */ + + /* SpurMemoryManager>>#isShorts: */ +sqInt +isShorts(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstShortFormat())) && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= ((firstByteFormat()) - 1)))); +} + + +/* This version is private to SpurMemoryManager (for asserts, etc). It does + not take advantage of the power-of-two optimization in isForwarded:. */ + + /* SpurMemoryManager>>#isUnambiguouslyForwarder: */ +static NoDbgRegParms int +isUnambiguouslyForwarder(sqInt objOop) +{ + return ((longAt((void *)(objOop))) & (classIndexMask())) == (isForwardedObjectClassIndexPun()); +} + + +/* For debugging using printOopsSuchThat: */ + + /* SpurMemoryManager>>#isUnmarked: */ +int +isUnmarked(sqInt objOop) +{ + return !(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0); +} + + /* SpurMemoryManager>>#isValidClassTag: */ +sqInt +isValidClassTag(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOrNil; + sqInt classTablePage; + sqInt fieldIndex; + + assert(((classIndex >= 0) && (classIndex <= ((1U << (classIndexFieldWidth())) - 1)))); + if (((classIndex >= ((tagMask()) + 1)) && (classIndex <= (lastClassIndexPun())))) { + return 0; + } + + /* begin classOrNilAtIndex: */ + assert((classIndex <= (tagMask())) + || (classIndex >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classOrNil = GIV(nilObj); + goto l1; + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + classOrNil = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classOrNilAtIndex: */ +l1: + return (classOrNil != GIV(nilObj)) + && (((long32At((void *)(classOrNil + 4))) & (identityHashHalfWordMask())) == classIndex); +} + + +/* Answer if the obj stack at objStackRootIndex is valid. */ + + /* SpurMemoryManager>>#isValidObjStackAt: */ +static NoDbgRegParms sqInt +isValidObjStackAt(sqInt objStackRootIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt stackOrNil; + + stackOrNil = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord())))))); + return (stackOrNil == GIV(nilObj)) + || (isValidObjStackPagemyIndexfirstPage(stackOrNil, objStackRootIndex, 1)); +} + + +/* Just check the page itself. */ + + /* SpurMemoryManager>>#isValidObjStackPage:myIndex: */ +static NoDbgRegParms sqInt +isValidObjStackPagemyIndex(sqInt objStackPage, sqInt myx) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!(((longAt((void *)(objStackPage))) & (classIndexMask())) == (sixtyFourBitLongsClassIndexPun()))) { + GIV(objStackInvalidBecause) = "wrong class index"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (!(((byteAt((void *)(objStackPage + (formatFieldByteOffset())))) & (formatMask())) == (sixtyFourBitIndexableFormat()))) { + GIV(objStackInvalidBecause) = "wrong format"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (!((numSlotsOfAny(objStackPage)) == ObjStackPageSlots)) { + GIV(objStackInvalidBecause) = "wrong num slots"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (!(myx == (longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord()))))))))) { + GIV(objStackInvalidBecause) = "wrong myx"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (GIV(marking) + && (!(((byteAt((void *)(objStackPage + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) { + GIV(objStackInvalidBecause) = "marking but page is unmarked"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + return 1; +} + + +/* Answer if the obj stack at stackRootIndex is valid. */ + + /* SpurMemoryManager>>#isValidObjStackPage:myIndex:firstPage: */ +static NoDbgRegParms sqInt +isValidObjStackPagemyIndexfirstPage(sqInt objStackPage, sqInt myx, sqInt isFirstPage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt freeOrNextPage; + sqInt index; + char *ns; + sqInt page; + + if (!(isValidObjStackPagemyIndex(objStackPage, myx))) { + return 0; + } + freeOrNextPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + while (freeOrNextPage != 0) { + if (!isFirstPage) { + GIV(objStackInvalidBecause) = "free page on other than first page"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (freeOrNextPage == (longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))) { + GIV(objStackInvalidBecause) = "free page = next page"; + GIV(invalidObjStackPage) = freeOrNextPage; + return 0; + } + if (!(isValidObjStackPagemyIndex(freeOrNextPage, myx))) { + /* begin stretch:cat: */ + ns = malloc(((strlen(GIV(objStackInvalidBecause))) + (14)) + 2); + strcpy(ns, GIV(objStackInvalidBecause)); + GIV(objStackInvalidBecause) = ((char *) (strcat(ns, ", on next page"))); + return 0; + } + page = longAt((void *)((freeOrNextPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + if ((page == freeOrNextPage) + || (page == objStackPage)) { + GIV(objStackInvalidBecause) = "circularity in free page list"; + GIV(invalidObjStackPage) = page; + return 0; + } + freeOrNextPage = page; + } + if (isFirstPage) { + if (!(((myx >= (1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift())))) && (myx <= (((1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift()))) + 8 /* hiddenRootSlots */) - 1))))) { + GIV(objStackInvalidBecause) = "myx out of range"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (!((longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(myx) << (shiftForWord()))))))) == objStackPage)) { + GIV(objStackInvalidBecause) = "firstPage is not root"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + } + index = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + if (!(((index >= 0) && (index <= ObjStackLimit)))) { + GIV(objStackInvalidBecause) = "bad topx"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + freeOrNextPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + if (!freeOrNextPage) { + return 1; + } + if (freeOrNextPage == objStackPage) { + GIV(objStackInvalidBecause) = "circularity in objStack page list"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + return isValidObjStackPagemyIndexfirstPage(freeOrNextPage, myx, 0); +} + + +/* Answer if the obj stack at objStackRootIndex is valid. */ + + /* SpurMemoryManager>>#isValidObjStack: */ +static NoDbgRegParms sqInt +isValidObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!((addressCouldBeObj(objStack)) + && ((numSlotsOfAny(objStack)) == ObjStackPageSlots))) { + GIV(objStackInvalidBecause) = "first page not obj or wrong size"; + GIV(invalidObjStackPage) = objStack; + return 0; + } + return isValidObjStackPagemyIndexfirstPage(objStack, longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))), 1); +} + + /* SpurMemoryManager>>#isWeakNonImm: */ +static NoDbgRegParms int +isWeakNonImm(sqInt objOop) +{ + sqInt format; + + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isWeakFormat: */ + return format == (weakArrayFormat()); +} + + +/* Answer if the argument has only weak fields that can hold oops. See + comment in formatOf: + */ + + /* SpurMemoryManager>>#isWeak: */ +sqInt +isWeak(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (isWeakNonImm(oop)); +} + + +/* Answer if the contains only indexable words or bytes (no oops). See + comment in formatOf: + */ +/* Note: Excludes CompiledMethods. */ + + /* SpurMemoryManager>>#isWordsOrBytesNonImm: */ +static NoDbgRegParms sqInt +isWordsOrBytesNonImm(sqInt objOop) +{ + sqInt format; + + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isPureBitsFormat: */ + return (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat())); +} + + +/* Answer if the contains only indexable words or bytes (no oops). See + comment in formatOf: + */ +/* Note: Excludes CompiledMethods. */ + + /* SpurMemoryManager>>#isWordsOrBytes: */ +sqInt +isWordsOrBytes(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (isWordsOrBytesNonImm(oop)); +} + + +/* Answer if the argument contains only indexable 16-bit half words or 32-bit + indexable words (no oops). + See comment in formatOf: */ + + /* SpurMemoryManager>>#isWordsOrShorts: */ +sqInt +isWordsOrShorts(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= ((firstByteFormat()) - 1)))); +} + + +/* Answer if the argument contains only indexable words (no oops). See + comment in formatOf: + */ + + /* SpurMemoryManager>>#isWords: */ +sqInt +isWords(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1)))); +} + + +/* Answer if obj is young. Require that obj is non-immediate. */ + + /* SpurMemoryManager>>#isYoungObject: */ +int +isYoungObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(isNonImmediate(objOop)); + return oopisLessThan(objOop, GIV(oldSpaceStart)); +} + + +/* Answer if oop is young. */ + + /* SpurMemoryManager>>#isYoung: */ +sqInt +isYoung(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((!(oop & (tagMask())))) + && (oopisLessThan(oop, GIV(oldSpaceStart))); +} + + +/* Answer if oop is an instance of the given class. If the class has a + (non-zero) compactClassIndex use that to speed up the check. N.B. Inlining + should result in classOop not being accessed if oop's compact class index + and compactClassIndex are non-zero. */ + + /* SpurMemoryManager>>#is:instanceOf:compactClassIndex: */ +static NoDbgRegParms sqInt +isinstanceOfcompactClassIndex(sqInt oop, sqInt classOop, sqInt compactClassIndex) +{ + sqInt ccIndex; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + if (compactClassIndex) { + return compactClassIndex == ccIndex; + } + else { + return classOop == (classAtIndex(ccIndex)); + } +} + + +/* useful for debugging... */ + + /* SpurMemoryManager>>#is:onObjStack: */ +sqInt +isonObjStack(sqInt oop, sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt nextPage; + + if (objStack == GIV(nilObj)) { + return 0; + } + assert((numSlotsOfAny(objStack)) == ObjStackPageSlots); + + /* There are four fixed slots in an obj stack, and a Topx of 0 indicates empty, so + if there were 6 slots in an oop stack, full would be 2, and the last 0-rel index is 5. */ + index = (longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) + ObjStackNextx; + while (index >= ObjStackFixedSlots) { + if (oop == (longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))))) { + return 1; + } + index -= 1; + } + nextPage = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + if (nextPage) { + if (isonObjStack(oop, nextPage)) { + return 1; + } + } + return 0; +} + + +/* Answer the object the ephemeron guards. This is its first element. */ + + /* SpurMemoryManager>>#keyOfEphemeron: */ +static NoDbgRegParms sqInt +keyOfEphemeron(sqInt objOop) +{ + assert((isNonImmediate(objOop)) + && (isObjEphemeron(objOop))); + return longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); +} + + +/* Answer the object the ephemeron guards. This is its first element. */ + + /* SpurMemoryManager>>#keyOfMaybeFiredEphemeron: */ +static NoDbgRegParms sqInt +keyOfMaybeFiredEphemeron(sqInt objOop) +{ + assert(isMaybeFiredEphemeron(objOop)); + return longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); +} + + /* SpurMemoryManager>>#knownClassAtIndex: */ +static NoDbgRegParms sqInt +knownClassAtIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(((classIndex >= 1) && (classIndex <= (classTablePageSize())))); + return longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(classIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#lastPointerFormat */ +static sqInt +lastPointerFormat(void) +{ + return 5; +} + + +/* Answer the byte offset of the last pointer field of the given object. + Works with CompiledMethods, as well as ordinary objects. + Does not examine the stack pointer of contexts to be sure to swizzle + the nils that fill contexts on snapshot. + It is invariant that on image load no object contains a forwarding + pointer, and the image contains no forwarders (see class comment). */ + + /* SpurMemoryManager>>#lastPointerOfWhileSwizzling: */ +static NoDbgRegParms sqInt +lastPointerOfWhileSwizzling(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt header; + sqInt headerSqInt; + usqInt numSlots; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt != (forwardedFormat())); + if (fmt <= 5 /* lastPointerFormat */) { + return ((((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1) * BytesPerOop) + BaseHeaderSize; + } + if (fmt < (firstCompiledMethodFormat())) { + return 0; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + return (((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) - 1) * BytesPerOop) + BaseHeaderSize; +} + + +/* Answer the byte offset of the last pointer field of the given object. + Works with CompiledMethods, as well as ordinary objects. */ + + /* SpurMemoryManager>>#lastPointerOf: */ +static NoDbgRegParms sqInt +lastPointerOf(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt fmt; + sqInt header; + sqInt headerSqInt; + usqInt numSlots; + sqInt sp; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt != (forwardedFormat())); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + return (((CtxtTempFrameStart - 1) + contextSize) * BytesPerOop) + BaseHeaderSize; + } + return ((((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1) * BytesPerOop) + BaseHeaderSize; + } + if (fmt < (firstCompiledMethodFormat())) { + return 0; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + return (((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) - 1) * BytesPerOop) + BaseHeaderSize; +} + + /* SpurMemoryManager>>#leakCheckFullGC */ +int +leakCheckFullGC(void) +{ + return ((checkForLeaks & GCModeFull) != 0); +} + + /* SpurMemoryManager>>#leakCheckNewSpaceGC */ +int +leakCheckNewSpaceGC(void) +{ + return ((checkForLeaks & GCModeNewSpace) != 0); +} + + +/* for the message send breakpoint; selectors can be immediates. */ + + /* SpurMemoryManager>>#lengthOfMaybeImmediate: */ +static NoDbgRegParms sqInt +lengthOfMaybeImmediate(sqInt oop) +{ + sqInt fmt; + usqInt numSlots; + usqInt numSlotsUsqInt; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin lengthOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + +/* Answer the number of indexable units in the given object. + For a CompiledMethod, the size of the method header (in bytes) should + be subtracted from the result. */ + + /* SpurMemoryManager>>#lengthOf: */ +static NoDbgRegParms sqInt +lengthOf(sqInt objOop) +{ + sqInt fmt; + usqInt numSlots; + usqInt numSlotsUsqInt; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + /* SpurMemoryManager>>#literalCountOfMethodHeader: */ +usqInt +literalCountOfMethodHeader(sqInt header) +{ + assert((((header) & 7) == 1)); + return ((header >> 3)) & AlternateHeaderNumLiteralsMask; +} + + /* SpurMemoryManager>>#literalCountOf: */ +usqInt +literalCountOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + return ((header >> 3)) & AlternateHeaderNumLiteralsMask; +} + + +/* This primitive is called from Smalltalk as... + loadSegmentFrom: aWordArray outPointers: anArray. */ +/* This primitive will load a binary image segment created by + primitiveStoreImageSegment. It expects the outPointer array to be of the + proper size, and the wordArray to be well formed. + It will return as its value the original array of roots, and the + segmentWordArray will become an + array of the loaded objects. If this primitive should fail, the + segmentWordArray will, sadly, have + been reduced to an unrecognizable and unusable jumble. But what more could + you have done + with it anyway? + + The primitive, if it succeeds, also becomes the segmentWordArray into the + array of loaded objects. + This allows fixing up of loaded objects directly, without nextObject, + which Spur doesn't support. */ + + /* SpurMemoryManager>>#loadImageSegmentFrom:outPointers: */ +static NoDbgRegParms NeverInline sqInt +loadImageSegmentFromoutPointers(sqInt segmentWordArray, sqInt outPointerArray) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt classOop; + sqInt classRef; + sqInt clone; + sqInt err; + sqInt errorCode; + sqInt errorCodeSqInt; + sqInt fillIdx; + sqInt followingWord; + usqInt followingWordAddress; + usqInt followingWordAddressUsqInt; + sqInt followingWordSqInt; + int hash; + sqInt i; + sqInt loadedObjectsArray; + usqInt mappedOop; + sqInt numLoadedObjects; + usqInt numOutPointers; + sqInt numSegObjs; + usqInt numSlots; + sqInt numSlotsSqInt; + sqInt objOop; + sqInt oldClone; + sqInt oop; + usqInt oopUsqInt; + sqInt scanClassTable; + usqInt segmentLimit; + sqInt segmentStart; + sqInt segVersion; + sqInt toDoLimit; + + /* begin numSlotsOf: */ + assert((classIndexOf(segmentWordArray)) > (isForwardedObjectClassIndexPun())); + segmentLimit = (((numSlots = byteAt((void *)(segmentWordArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(segmentWordArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if ((/* objectBytesForSlots: */ + (segmentLimit + ? ((segmentLimit << (shiftForWord()))) + ((segmentLimit >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize)) < (8 /* allocationUnit */ + BaseHeaderSize)) { + return PrimErrBadArgument; + } + + /* version info + one object header + Verify format. If the format is wrong, word-swap (since ImageSegment data are 32-bit longs). + If it is still wrong, undo the damage and fail. */ + segVersion = longAt((void *)(segmentWordArray + BaseHeaderSize)); + if (!(((((segVersion & 0xFFFFFF) | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */)) { + reverseBytesIn32BitWordsIn(segmentWordArray); + segVersion = longAt((void *)(segmentWordArray + BaseHeaderSize)); + if (!(((((segVersion & 0xFFFFFF) | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */)) { + reverseBytesIn32BitWordsIn(segmentWordArray); + return PrimErrBadArgument; + } + } + + /* low 3 bytes */ + segmentStart = (segmentWordArray + BaseHeaderSize) + 8 /* allocationUnit */; + segmentLimit = ((segmentLimit * BytesPerOop) + segmentWordArray) + BaseHeaderSize; + + /* Notionally reverse the Byte type objects if the data is from opposite endian machine. + Test top byte. $d on the Mac or $s on the PC. Rest of word is equal. If Spur is ever + ported to big-endian machines then the segment may have to be byte/word swapped, + but so far it only runs on little-endian machines, so for now just fail if endianness is wrong. */ + if (((((usqInt)(segVersion)) >> 24) & 0xFF) != ((((usqInt)((imageSegmentVersion()))) >> 24) & 0xFF)) { + return PrimErrBadArgument; + } + + /* Reverse the byte-type objects once + Avoid having to remember by arranging that there are no young outPointers if segment is in old space. */ + + /* begin isOldObject: */ + assert(isNonImmediate(segmentWordArray)); + if (oopisGreaterThanOrEqualTo(segmentWordArray, GIV(oldSpaceStart))) { + /* begin ensureNoNewObjectsIn: */ + hash = 0; + scanClassTable = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(outPointerArray)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(outPointerArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointerArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + oop = longAt((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isYoung: */ + ((!(oop & (tagMask())))) + && (oopisLessThan(oop, GIV(oldSpaceStart)))) { + clone = cloneInOldSpaceforPinning(oop, 0); + if (!clone) { + errorCode = PrimErrNoMemory; + goto l6; + } + if ((((hash = (long32At((void *)(oop + 4))) & (identityHashHalfWordMask()))) != 0) + && ((classOrNilAtIndex(hash)) == oop)) { + scanClassTable = 1; + } + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(oop),((longAt((void *)(oop))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(oop),(longAt((void *)(oop))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(oop)); + assert(!(isOopForwarded(clone))); + + /* begin isOldObject: */ + assert(isNonImmediate(oop)); + if (oopisGreaterThanOrEqualTo(oop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(clone & (tagMask())))) + && (oopisLessThan(clone, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(oop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord()))),clone); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(oop + (numSlotsFieldByteOffset())),1); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(outPointerArray)) + && (!(isForwarded(outPointerArray)))); + assert(validStorePointerUncheckedArgs(i, outPointerArray, clone)); + longAtput((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),clone); + } + } + if (scanClassTable) { + postBecomeScanClassTable(BecamePointerObjectFlag); + } + errorCode = 0; + /* end ensureNoNewObjectsIn: */ +l6: + if (errorCode) { + return errorCode; + } + } + + /* scan through mapping oops and validating class references. Defer entering any + class objects into the class table and/or pinning objects until a second pass. */ + + /* begin mapOopsAndValidateClassRefsFrom:to:outPointers: */ + /* begin numSlotsOf: */ + assert((classIndexOf(outPointerArray)) > (isForwardedObjectClassIndexPun())); + numOutPointers = (((numSlots = byteAt((void *)(outPointerArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointerArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numSegObjs = 0; + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(segmentStart + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? segmentStart + BaseHeaderSize + : segmentStart); + while (oopisLessThan(objOop, segmentLimit)) { + numSegObjs += 1; + + /* No object in the segment should be marked. If is is something is wrong. */ + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + errorCode = PrimErrInappropriate; + goto l8; + } + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + + /* validate the class ref, but don't update it until any internal classes have been added to the class table. */ + if (((classIndex & TopHashBit) != 0)) { + classIndex -= TopHashBit; + if (classIndex >= numOutPointers) { + errorCode = PrimErrBadIndex; + goto l8; + } + mappedOop = longAt((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)(classIndex) << (shiftForWord())))))); + hash = (long32At((void *)(mappedOop + 4))) & (identityHashHalfWordMask()); + if (!((hash == 0) + || ((hash > (lastClassIndexPun())) + && ((classOrNilAtIndex(hash)) == mappedOop)))) { + errorCode = PrimErrInappropriate; + goto l8; + } + } + else { + if (((oopUsqInt = ((classIndex - (firstClassIndexPun())) * 8 /* allocationUnit */) + segmentStart)) >= segmentLimit) { + errorCode = PrimErrBadIndex; + goto l8; + } + if ((long32At((void *)(oopUsqInt + 4))) & (identityHashHalfWordMask())) { + errorCode = PrimErrInappropriate; + goto l8; + } + } + + /* The class is contained within the segment. */ + toDoLimit = (numPointerSlotsOf(objOop)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + oopUsqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(oopUsqInt & (tagMask())))) { + if (((oopUsqInt & TopOopBit) != 0)) { + if (((oopUsqInt = (oopUsqInt - TopOopBit) / BytesPerOop)) >= numOutPointers) { + errorCode = PrimErrBadIndex; + goto l8; + } + mappedOop = longAt((void *)((outPointerArray + BaseHeaderSize) + ((oopUsqInt << (shiftForWord()))))); + } + else { + if (oopUsqInt & 7 /* (allocationUnit - 1) */) { + errorCode = PrimErrInappropriate; + goto l8; + } + if (((mappedOop = oopUsqInt + segmentStart)) >= segmentLimit) { + errorCode = PrimErrBadIndex; + goto l8; + } + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(i, objOop, mappedOop)); + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),mappedOop); + } + } + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, segmentLimit)) { + objOop = segmentLimit; + goto l7; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l7:; + } + errorCode = -numSegObjs; + /* end mapOopsAndValidateClassRefsFrom:to:outPointers: */ +l8: + if (errorCode > 0) { + return errorCode; + } + numLoadedObjects = -errorCode; + loadedObjectsArray = noInlineAllocateSlotsformatclassIndex(numLoadedObjects, arrayFormat(), ClassArrayCompactIndex); + if (!loadedObjectsArray) { + growOldSpaceByAtLeast((BaseHeaderSize + BaseHeaderSize) + (numLoadedObjects * BytesPerOop)); + loadedObjectsArray = noInlineAllocateSlotsformatclassIndex(numLoadedObjects, arrayFormat(), ClassArrayCompactIndex); + if (!loadedObjectsArray) { + return PrimErrNoMemory; + } + } + + /* Scan for classes contained in the segment, entering them into the class table. + Classes are at the front, after the root array and have the remembered bit set. */ + + /* begin enterClassesIntoClassTableFrom:to: */ + objOop = objectAfter(objectStartingAt(segmentStart)); + while ((oopisLessThan(objOop, segmentLimit)) + && (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0)) { + setIsRememberedOfto(objOop, 0); + if ((errorCodeSqInt = enterIntoClassTable(objOop))) { + oop = objOop; + objOop = objectAfter(objectStartingAt(segmentStart)); + while (oopisLessThan(objOop, oop)) { + expungeFromClassTable(objOop); + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, segmentLimit)) { + objOop = segmentLimit; + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + errorCode = errorCodeSqInt; + goto l3; + } + + /* begin objectAfter:limit: */ + followingWordAddressUsqInt = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddressUsqInt, segmentLimit)) { + objOop = segmentLimit; + goto l2; + } + followingWordSqInt = longAt((void *)(followingWordAddressUsqInt)); + objOop = ((((usqInt)(followingWordSqInt)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddressUsqInt + BaseHeaderSize + : followingWordAddressUsqInt); + /* end objectAfter:limit: */ +l2:; + } + errorCode = 0; + /* end enterClassesIntoClassTableFrom:to: */ +l3: + if (errorCode) { + return errorCode; + } + + /* Make a final pass, assigning class indices and/or pinning pinned objects and collecting the loaded objects in loadedObjectsArray */ + + /* begin assignClassIndicesAndPinFrom:to:outPointers:filling: */ + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(segmentStart + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? segmentStart + BaseHeaderSize + : segmentStart); + fillIdx = 0; + while (oopisLessThan(objOop, segmentLimit)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(loadedObjectsArray)) + && (!(isForwarded(loadedObjectsArray)))); + assert(validStorePointerUncheckedArgs(fillIdx, loadedObjectsArray, objOop)); + longAtput((void *)((loadedObjectsArray + BaseHeaderSize) + ((((usqInt)(fillIdx) << (shiftForWord()))))),objOop); + fillIdx += 1; + + /* In the segment, class indices are offset indexes into the segment data, + or into outPointers. See mapOopsFrom:to:outPointers:outHashes: and + newOutPointer:at:in:hashes:. */ + classRef = (longAt((void *)(objOop))) & (classIndexMask()); + classOop = (((classRef & TopHashBit) != 0) + ? longAt((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)((classRef - TopHashBit)) << (shiftForWord())))))) + : ((classRef - (firstClassIndexPun())) * 8 /* allocationUnit */) + segmentStart); + classIndex = (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask()); + if (!classIndex) { + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classOop)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classOop) + ? ((err = enterIntoClassTable(classOop)) + ? -err + : (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + errorCode = -classIndex; + goto l5; + } + } + assert((classIndex > (lastClassIndexPun())) + && ((classOrNilAtIndex(classIndex)) == classOop)); + + /* begin setClassIndexOf:to: */ + assert(((classIndex >= 0) && (classIndex <= (classIndexMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(classIndexMask()))) + classIndex); + if ((/* isInNewSpace: */ + (oopisLessThan(objOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(objOop, GIV(newSpaceStart)))) + && (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)) { + oldClone = cloneInOldSpaceforPinning(objOop, 1); + if (oldClone) { + setIsPinnedOfto(oldClone, 1); + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(objOop),(longAt((void *)(objOop))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(objOop)); + assert(!(isOopForwarded(oldClone))); + + /* begin isOldObject: */ + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(oldClone & (tagMask())))) + && (oopisLessThan(oldClone, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),oldClone); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),1); + } + } + } + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, segmentLimit)) { + objOop = segmentLimit; + goto l4; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l4:; + } + errorCode = 0; + /* end assignClassIndicesAndPinFrom:to:outPointers:filling: */ +l5: + if (errorCode) { + return errorCode; + } + + /* Evaporate the container, leaving the newly loaded objects in place. */ + if ((byteAt((void *)(segmentWordArray + (numSlotsFieldByteOffset())))) == (numSlotsMask())) { + if (oopisLessThan(segmentWordArray, GIV(oldSpaceStart))) { + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(segmentWordArray - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56)))); + + /* rawNumSlotsOf:put: */ + byteAtput((void *)(segmentWordArray + (numSlotsFieldByteOffset())),0); + } + else { + numSlotsSqInt = 8 /* allocationUnit */ / BytesPerOop; + + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(segmentWordArray - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56))) + numSlotsSqInt); + } + } + else { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(segmentWordArray + (numSlotsFieldByteOffset())),8 /* allocationUnit */ / BytesPerOop); + } + + /* N.B. setting the overflow slots to 1 creates a slimbridge in eden, so we also need to delete the overflow slot count in the segment itself + Finally forward the segmentWordArray to the loadedObjectsArray */ + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(segmentWordArray),((longAt((void *)(segmentWordArray))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(segmentWordArray),(longAt((void *)(segmentWordArray))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(segmentWordArray)); + assert(!(isOopForwarded(loadedObjectsArray))); + + /* begin isOldObject: */ + assert(isNonImmediate(segmentWordArray)); + if (oopisGreaterThanOrEqualTo(segmentWordArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(loadedObjectsArray & (tagMask())))) + && (oopisLessThan(loadedObjectsArray, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(segmentWordArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(segmentWordArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((segmentWordArray + BaseHeaderSize) + (0U << (shiftForWord()))),loadedObjectsArray); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(segmentWordArray + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(segmentWordArray + (numSlotsFieldByteOffset())),1); + } + runLeakCheckerFor(GCModeImageSegment); + + /* Answer the first object in the segment because this is what primitiveLoadImageSegment tests for, + even though the real result is the segmentWordArray forwarded to the loadedObjectsArray. */ + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(segmentStart + (numSlotsFieldByteOffset()))); + return (numSlots == (numSlotsMask()) + ? segmentStart + BaseHeaderSize + : segmentStart); +} + + +/* Scan the heap printing the oops of any and all objects that are instances + of aClassOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#longPrintInstancesOf: */ +void +longPrintInstancesOf(sqInt aClassOop) +{ + int classIndex; + + classIndex = (long32At((void *)(aClassOop + 4))) & (identityHashHalfWordMask()); + if (classIndex != (isFreeObjectClassIndexPun())) { + longPrintInstancesWithClassIndex(classIndex); + } +} + + +/* Scan the heap printing any and all objects whose classIndex equals the + argument. + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#longPrintInstancesWithClassIndex: */ +void +longPrintInstancesWithClassIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + longPrintOop(objOop); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + longPrintOop(objOop); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3:; +} + + +/* Scan the heap long printing the oops of any and all objects that refer to + anOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#longPrintReferencesTo: */ +void +longPrintReferencesTo(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt i; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt prntObj; + sqInt startObject; + + prntObj = 0; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + || (((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) { + if (((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + i = (literalCountOf(objSqInt)) + LiteralStart; + } + else { + if (((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + i = CtxtTempFrameStart + (fetchStackPointerOf(objSqInt)); + } + else { + /* begin numSlotsOf: */ + assert((classIndexOf(objSqInt)) > (isForwardedObjectClassIndexPun())); + i = (((numSlots = byteAt((void *)(objSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + } + } + while (((i -= 1)) >= 0) { + if (anOop == (longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + fprintf(GIV(transcript), + "%p @ %d\n", + ((void *)objSqInt), + ((int) i)); + prntObj = 1; + i = 0; + } + } + if (prntObj) { + prntObj = 0; + longPrintOop(objSqInt); + } + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + /* SpurMemoryManager>>#mapExtraRoots */ +static void +mapExtraRoots(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt oop; + + if (shouldRemapObj(GIV(specialObjectsOop))) { + GIV(validatedIntegerClassFlags) = 0; + GIV(specialObjectsOop) = remapObj(GIV(specialObjectsOop)); + } + assert(GIV(remapBufferCount) == 0); + + /* 1 to: remapBufferCount do: + [:i | | oop | + oop := remapBufferCount at: i. + ((self isImmediate: oop) or: [self isFreeObject: oop]) ifFalse: + [(self shouldRemapObj: oop) ifTrue: + [remapBuffer at: i put: (self remapObj: oop)]]]. */ + for (i = 1; i <= GIV(extraRootCount); i += 1) { + oop = (GIV(extraRoots)[i])[0]; + if (!((((oop & (tagMask())) != 0)) + || (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())))) { + if (shouldRemapObj(oop)) { + (GIV(extraRoots)[i])[0] = (remapObj(oop)); + } + } + } +} + + /* SpurMemoryManager>>#mapMournQueue */ +static NeverInline void +mapMournQueue(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt iSqInt; + sqInt mourner; + sqInt numOnThisPage; + sqInt objStackPage; + sqInt referent; + + /* begin objStack:do: */ + if (GIV(mournQueue) == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(GIV(mournQueue))); + objStackPage = GIV(mournQueue); + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + mourner = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if ((!(mourner & (tagMask())))) { + if ((!((longAt((void *)(mourner))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(mourner)); + referent = longAt((void *)((mourner + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + mourner = referent; + } + if (!(isScavengeSurvivor(mourner))) { + mourner = copyAndForwardMourner(mourner); + } + + /* we could check for change but writes are cheap with write buffers... */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objStackPage)) + && (!(isForwarded(objStackPage)))); + assert(validStorePointerUncheckedArgs(iSqInt, objStackPage, mourner)); + longAtput((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord()))))),mourner); + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l1:; +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. + Now scan, adding out pointers to the outPointersArray; all objects in + arrayOfObjects have had their first fields set to point to their copies in + segmentWordArray. Answer + the outIndex if the scan succeded. Fail if outPointers is too small and + answer -1. + + As established by copyObj:toAddr:startAt:stopAt:savedFirstFields:index:, + the marked bit is set for all objects in the segment + the remembered bit is set for all classes in the segment. + + Class indices should be set as follows (see + assignClassIndicesAndPinFrom:to:outPointers:filling:) - class indices for + classes in the segment */ + + /* SpurMemoryManager>>#mapOopsFrom:to:outPointers:outHashes: */ +static NoDbgRegParms sqInt +mapOopsFromtooutPointersoutHashes(sqInt segStart, sqInt segAddr, sqInt outPointerArray, sqInt savedOutHashes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + int hash; + sqInt heapOop; + sqInt i; + usqInt numSlots; + sqInt numSlotsSqInt; + usqInt numSlotsUsqInt; + sqInt objIndex; + sqInt objOop; + sqInt oop; + sqInt outIndex; + usqInt p; + sqInt segIndex; + sqInt toDoLimit; + usqInt toDoLimitUsqInt; + sqInt valueWord; + sqInt valueWordSqInt; + + /* objIndex is for debugging; it mirrors indices in the sender's arrayOfObjects. */ + outIndex = (objIndex = 0); + /* begin numSlotsOf: */ + assert((classIndexOf(outPointerArray)) > (isForwardedObjectClassIndexPun())); + if (((numSlots = byteAt((void *)(outPointerArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask())) { + numSlotsSqInt = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointerArray - BaseHeaderSize)))) << 8)))))) >> 8; + } + else { + numSlotsSqInt = numSlots; + } + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((outPointerArray + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1, addressAfter(outPointerArray))); + toDoLimitUsqInt = ((usqInt)(((outPointerArray + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1)); + for (p = (((usqInt)(outPointerArray + BaseHeaderSize))); p <= toDoLimitUsqInt; p += 8 /* allocationUnit */) { + longAtput((void *)(p),GIV(nilObj)); + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(segStart + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? segStart + BaseHeaderSize + : segStart); + while (oopisLessThan(objOop, segAddr)) { + heapOop = fetchClassOfNonImm(objOop); + + /* Set the classIndex of the instance. This is a segment offset (segAddr - segStart / allocationUnit) for instances of + classes within the segment, and an outPointer index (index in outPointers + TopHashBit) for classes outside the segment. */ + if ((byteAt((void *)(heapOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + oop = longAt((void *)((heapOop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(oopisGreaterThanOrEqualToandLessThan(oop, segStart, segAddr)); + segIndex = ((oop - segStart) / 8 /* allocationUnit */) + (firstClassIndexPun()); + if (((segIndex & TopHashBit) != 0)) { + return -1; + } + } + else { + hash = (long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask()); + if (/* is:outPointerClassHashFor:in:limit: */ + (((hash & TopHashBit) != 0)) + && (((hash - TopHashBit) <= outIndex) + && (heapOop == (longAt((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)((hash - TopHashBit)) << (shiftForWord())))))))))) { + segIndex = hash; + } + else { + /* begin newOutPointer:at:in:hashes: */ + if (outIndex >= ((/* begin numSlotsOf: */ + assert((classIndexOf(outPointerArray)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(outPointerArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointerArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))) { + /* no room in outPointers; fail */ + outIndex = 0; + goto l2; + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(outIndex, outPointerArray, heapOop)); + assert(isNonImmediate(outPointerArray)); + if (oopisGreaterThanOrEqualTo(outPointerArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(heapOop & (tagMask())))) + && (oopisLessThan(heapOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(outPointerArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(outPointerArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)(outIndex) << (shiftForWord()))))),heapOop); + valueWord = (long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask()); + + /* begin storeLong32:ofObject:withValue: */ + long32Atput((void *)((savedOutHashes + BaseHeaderSize) + ((((usqInt)(outIndex) << 2)))),valueWord); + long32Atput((void *)(heapOop + 4),((((long32At((void *)(heapOop + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + ((outIndex + TopHashBit) & (identityHashHalfWordMask()))); + outIndex += 1; + /* end newOutPointer:at:in:hashes: */ +l2: + if (!outIndex) { + return -1; + } + + /* no room in outPointers; fail */ + segIndex = (long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask()); + } + + /* oop is a new outPointer; allocate its oop */ + assert(((segIndex & TopHashBit) != 0)); + } + + /* oop is a class in the segment; storeImageSegmentInto:outPointers:roots: established offset is within range. + oop is an outPointer; locate or allocate its oop */ + + /* begin setClassIndexOf:to: */ + assert(((segIndex >= 0) && (segIndex <= (classIndexMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(classIndexMask()))) + segIndex); + toDoLimit = (numPointerSlotsOf(objOop)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + heapOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(heapOop & (tagMask())))) { + if ((byteAt((void *)(heapOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + oop = longAt((void *)((heapOop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(oopisGreaterThanOrEqualToandLessThan(oop, segStart, segAddr)); + oop -= segStart; + } + else { + hash = (long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask()); + if (/* is:outPointerClassHashFor:in:limit: */ + (((hash & TopHashBit) != 0)) + && (((hash - TopHashBit) <= outIndex) + && (heapOop == (longAt((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)((hash - TopHashBit)) << (shiftForWord())))))))))) { + oop = ((hash - TopHashBit) * BytesPerOop) + TopOopBit; + } + else { + /* begin newOutPointer:at:in:hashes: */ + if (outIndex >= ((/* begin numSlotsOf: */ + assert((classIndexOf(outPointerArray)) > (isForwardedObjectClassIndexPun())), + (((numSlotsUsqInt = byteAt((void *)(outPointerArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointerArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt)))) { + /* no room in outPointers; fail */ + outIndex = 0; + goto l3; + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(outIndex, outPointerArray, heapOop)); + assert(isNonImmediate(outPointerArray)); + if (oopisGreaterThanOrEqualTo(outPointerArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(heapOop & (tagMask())))) + && (oopisLessThan(heapOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(outPointerArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(outPointerArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)(outIndex) << (shiftForWord()))))),heapOop); + valueWordSqInt = (long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask()); + + /* begin storeLong32:ofObject:withValue: */ + long32Atput((void *)((savedOutHashes + BaseHeaderSize) + ((((usqInt)(outIndex) << 2)))),valueWordSqInt); + long32Atput((void *)(heapOop + 4),((((long32At((void *)(heapOop + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + ((outIndex + TopHashBit) & (identityHashHalfWordMask()))); + outIndex += 1; + /* end newOutPointer:at:in:hashes: */ +l3: + if (!outIndex) { + return -1; + } + + /* no room in outPointers; fail */ + assert((((rawHashBitsOf(heapOop)) & TopHashBit) != 0)); + oop = ((((long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask())) - TopHashBit) * BytesPerOop) + TopOopBit; + } + } + + /* oop is an object in the segment. + oop is an outPointer; locate or allocate its oop */ + + /* storeUncheckedNoAssert:ofAny:withValue: */ + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),oop); + } + } + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, segAddr)) { + objOop = segAddr; + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1: + objIndex += 1; + } + return outIndex; +} + + +/* Ensure the class of the argument is marked, pushing it on the markStack if + not already marked. + And for one-way become, which can create duplicate entries in the class + table, make sure + objOop's classIndex refers to the classObj's actual classIndex. + Note that this is recursive, but the metaclass chain should terminate + quickly. + */ + + /* SpurMemoryManager>>#markAndTraceClassOf: */ +static NoDbgRegParms void +markAndTraceClassOf(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt classObj; + sqInt classTablePage; + sqInt fieldIndex; + sqInt objStack; + int realClassIndex; + + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + + /* begin classOrNilAtIndex: */ + assert((classIndex <= (tagMask())) + || (classIndex >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classObj = GIV(nilObj); + goto l1; + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + classObj = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classOrNilAtIndex: */ +l1: + assert(objCouldBeClassObj(classObj)); + realClassIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + if ((classIndex != realClassIndex) + && (classIndex > (lastClassIndexPun()))) { + /* begin setClassIndexOf:to: */ + assert(((realClassIndex >= 0) && (realClassIndex <= (classIndexMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(classIndexMask()))) + realClassIndex); + } + if (!((byteAt((void *)(classObj + (markBitsByteOffset())))) & (1U << (markedBitByteShift())))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(classObj))); + byteAtput((void *)(classObj + (markBitsByteOffset())),(byteAt((void *)(classObj + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + markAndTraceClassOf(classObj); + objStack = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(classObj)); + if (((classObj & (tagMask())) != 0)) { + assert(objStack == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack)) + ? fetchPointerofObject(ObjStackNextx, objStack) + : objStack)))); + } + else { + assert(!((objStack == GIV(markStack)) + && (isWeakNonImm(classObj)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack != GIV(weaklingStack)) + || (isWeakNonImm(classObj))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(classObj, objStack); + } +} + + +/* An obj stack is a stack of objects stored in a hidden root slot, such + as the markStack or the ephemeronQueue. It is a linked list of + segments, with the hot end at the head of the list. It is a word object. + The stack pointer is in ObjStackTopx and 0 means empty. */ + + /* SpurMemoryManager>>#markAndTraceObjStack:andContents: */ +static NoDbgRegParms void +markAndTraceObjStackandContents(sqInt stackOrNil, sqInt markAndTraceContents) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt field; + sqInt index; + + if (stackOrNil == GIV(nilObj)) { + return; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(stackOrNil))); + byteAtput((void *)(stackOrNil + (markBitsByteOffset())),(byteAt((void *)(stackOrNil + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + field = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + if (field) { + markAndTraceObjStackandContents(field, markAndTraceContents); + } + field = stackOrNil; + while (1) { + field = longAt((void *)((field + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + if (!(field != 0)) break; + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(field))); + byteAtput((void *)(field + (markBitsByteOffset())),(byteAt((void *)(field + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + } + if (!markAndTraceContents) { + return; + } + + /* There are four fixed slots in an obj stack, and a Topx of 0 indicates empty, so + if there were 6 slots in an oop stack, full would be 2, and the last 0-rel index is 5. */ + index = (longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) + ObjStackNextx; + while (index >= ObjStackFixedSlots) { + /* begin followObjField:ofObject: */ + field = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(field)); + if ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + field = fixFollowedFieldofObjectwithInitialValue(index, stackOrNil, field); + } + if (!(((field & (tagMask())) != 0))) { + markAndTrace(field); + } + index -= 1; + } +} + + +/* Mark the argument, and all objects reachable from it, and any remaining + objects on the mark stack. Follow forwarding pointers in the scan. */ +/* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. + The assert for this is in markAndShouldScan: */ + + /* SpurMemoryManager>>#markAndTrace: */ +NeverInline void +markAndTrace(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt field; + sqInt fmt; + sqInt format; + sqInt formatSqInt; + sqInt header; + sqInt headerSqInt; + sqInt index; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt numStrongSlots; + sqInt objOop1; + sqInt objOopSqInt; + sqInt objOopSqIntSqInt; + sqInt objStack; + sqInt objStack1; + sqInt objStackSqInt; + sqInt objToScan; + sqInt scanLargeObject; + sqInt sp; + + /* begin markAndShouldScan: */ + if (((objOop & (tagMask())) != 0)) { + return; + } + + /* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. */ + assert(!(isForwarded(objOop))); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + return; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (/* isPureBitsFormat: */ + (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat()))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + markAndTraceClassOf(objOop); + } + return; + } + + /* avoid pushing non-pointer objects on the markStack. + Avoid tracing classes of non-objects on the heap, e.g. IRC caches, Sista counters. */ + if (format == (weakArrayFormat())) { + objStack = GIV(weaklingStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objOop)); + if (((objOop & (tagMask())) != 0)) { + assert(objStack == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack)) + ? fetchPointerofObject(ObjStackNextx, objStack) + : objStack)))); + } + else { + assert(!((objStack == GIV(markStack)) + && (isWeakNonImm(objOop)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack != GIV(weaklingStack)) + || (isWeakNonImm(objOop))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objOop, objStack); + return; + } + + /* push weaklings on the weakling stack to scan later */ + if ((format == (ephemeronFormat())) + && (activeAndDeferredScan(objOop))) { + return; + } + + /* Now scan the object, and any remaining objects on the mark stack. */ + + /* begin markLoopFrom: */ + numStrongSlots = 0; + objToScan = objOop; + + /* To avoid overflowing the mark stack when we encounter large objects, we + push the obj, then its numStrongSlots, and then index the object from the stack. */ + do { + if (((objToScan & (tagMask())) != 0)) { + scanLargeObject = 1; + } + else { + /* begin numStrongSlotsOfInephemeral: */ + fmt = (byteAt((void *)(objToScan + (formatFieldByteOffset())))) & (formatMask()); + assert((fmt != (ephemeronFormat())) + || ((isImmediate(keyOfEphemeron(objToScan))) + || (isMarked(keyOfEphemeron(objToScan))))); + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(objToScan)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objToScan + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objToScan - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (arrayFormat())) { + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (indexablePointersFormat())) { + if (((longAt((void *)(objToScan))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + setTraceFlagOnContextsFramesPageIfNeeded(objToScan); + + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l3; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objToScan))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l3: + numStrongSlots = CtxtTempFrameStart + contextSize; + goto l4; + } + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (weakArrayFormat())) { + objOopSqIntSqInt = fetchClassOfNonImm(objToScan); + numStrongSlots = (((longAt((void *)((objOopSqIntSqInt + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + goto l4; + } + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (forwardedFormat())) { + numStrongSlots = 1; + goto l4; + } + if (fmt < (firstCompiledMethodFormat())) { + numStrongSlots = 0; + goto l4; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objToScan)); + headerSqInt = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numStrongSlots = numLiterals + LiteralStart; + /* end numStrongSlotsOfInephemeral: */ +l4: + scanLargeObject = numStrongSlots > 64 /* traceImmediatelySlotLimit */; + } + if (scanLargeObject) { + if (((objToScan & (tagMask())) != 0)) { + index = (objToScan >> 3); + objToScan = topOfObjStack(GIV(markStack)); + } + else { + index = numStrongSlots; + markAndTraceClassOf(objToScan); + } + while (index > 0) { + index -= 1; + field = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if ((!(field & (tagMask())))) { + if ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + field = fixFollowedFieldofObjectwithInitialValue(index, objToScan, field); + } + + /* fixFollowedField: is /not/ inlined */ + + /* begin markAndShouldScan: */ + if (((field & (tagMask())) != 0)) { + goto l1; + } + + /* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. */ + assert(!(isForwarded(field))); + if ((byteAt((void *)(field + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + goto l1; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(field))); + byteAtput((void *)(field + (markBitsByteOffset())),(byteAt((void *)(field + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + format = (byteAt((void *)(field + (formatFieldByteOffset())))) & (formatMask()); + if (/* isPureBitsFormat: */ + (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat()))) { + if (((longAt((void *)(field))) & (classIndexMask())) > (lastClassIndexPun())) { + markAndTraceClassOf(field); + } + goto l1; + } + + /* avoid pushing non-pointer objects on the markStack. + Avoid tracing classes of non-objects on the heap, e.g. IRC caches, Sista counters. */ + if (format == (weakArrayFormat())) { + objStack1 = GIV(weaklingStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + goto l1; + } + + /* push weaklings on the weakling stack to scan later */ + if ((format == (ephemeronFormat())) + && (activeAndDeferredScan(field))) { + goto l1; + } + if (index > 0) { + if ((topOfObjStack(GIV(markStack))) != objToScan) { + objStack = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objToScan)); + if (((objToScan & (tagMask())) != 0)) { + assert(objStack == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack)) + ? fetchPointerofObject(ObjStackNextx, objStack) + : objStack)))); + } + else { + assert(!((objStack == GIV(markStack)) + && (isWeakNonImm(objToScan)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack != GIV(weaklingStack)) + || (isWeakNonImm(objToScan))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objToScan, objStack); + } + objOopSqInt = (((usqInt)index << 3) | 1); + objStackSqInt = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objOopSqInt)); + if (((objOopSqInt & (tagMask())) != 0)) { + assert(objStackSqInt == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStackSqInt)) + ? fetchPointerofObject(ObjStackNextx, objStackSqInt) + : objStackSqInt)))); + } + else { + assert(!((objStackSqInt == GIV(markStack)) + && (isWeakNonImm(objOopSqInt)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStackSqInt != GIV(weaklingStack)) + || (isWeakNonImm(objOopSqInt))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objOopSqInt, objStackSqInt); + } + objToScan = field; + index = -1; +l1:; + } + } + if (index >= 0) { + objToScan = popObjStack(GIV(markStack)); + if (objToScan == objOop) { + objToScan = popObjStack(GIV(markStack)); + } + } + } + else { + index = numStrongSlots; + markAndTraceClassOf(objToScan); + while (index > 0) { + index -= 1; + field = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if ((!(field & (tagMask())))) { + if ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + field = fixFollowedFieldofObjectwithInitialValue(index, objToScan, field); + } + + /* fixFollowedField: is /not/ inlined */ + + /* begin markAndShouldScan: */ + if (((field & (tagMask())) != 0)) { + goto l2; + } + + /* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. */ + assert(!(isForwarded(field))); + if ((byteAt((void *)(field + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + goto l2; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(field))); + byteAtput((void *)(field + (markBitsByteOffset())),(byteAt((void *)(field + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + formatSqInt = (byteAt((void *)(field + (formatFieldByteOffset())))) & (formatMask()); + if (/* isPureBitsFormat: */ + (formatSqInt >= (sixtyFourBitIndexableFormat())) + && (formatSqInt < (firstCompiledMethodFormat()))) { + if (((longAt((void *)(field))) & (classIndexMask())) > (lastClassIndexPun())) { + markAndTraceClassOf(field); + } + goto l2; + } + + /* avoid pushing non-pointer objects on the markStack. + Avoid tracing classes of non-objects on the heap, e.g. IRC caches, Sista counters. */ + if (formatSqInt == (weakArrayFormat())) { + objStack1 = GIV(weaklingStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + goto l2; + } + + /* push weaklings on the weakling stack to scan later */ + if ((formatSqInt == (ephemeronFormat())) + && (activeAndDeferredScan(field))) { + goto l2; + } + objStack1 = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + if (((byteAt((void *)(field + (numSlotsFieldByteOffset())))) > 64 /* traceImmediatelySlotLimit */) + && (((numStrongSlots = numStrongSlotsOfInephemeral(field))) > 64 /* traceImmediatelySlotLimit */)) { + objOop1 = (((usqInt)numStrongSlots << 3) | 1); + objStack1 = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objOop1)); + if (((objOop1 & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(objOop1)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(objOop1))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objOop1, objStack1); + } +l2:; + } + } + objToScan = popObjStack(GIV(markStack)); + } + + /* scanning a large object. scan until hitting an unmarked object, then switch to it, if any. + scanning a small object. scan, marking, pushing unmarked referents, then switch to the top of the stack. */ + } while(objToScan); +} + + +/* Go through the unscanned ephemerons, marking the inactive ones, and + removing them from the unscanned ephemerons. Answer if any inactive + ones were found. We cannot fire the ephemerons until all are found to + be active since scan-marking an inactive ephemeron later in the set may + render a previously-observed active ephemeron as inactive. */ + + /* SpurMemoryManager>>#markInactiveEphemerons */ +static sqInt +markInactiveEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt ephemeron; + sqInt field; + sqInt fmt; + sqInt format; + sqInt formatSqInt; + sqInt foundInactive; + sqInt header; + sqInt headerSqInt; + sqInt index; + sqInt key; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt numStrongSlots; + sqInt objOop; + sqInt objOop1; + sqInt objOopSqInt; + sqInt objOopSqIntSqInt; + sqInt objStack; + sqInt objStack1; + sqInt objStackSqInt; + sqInt objToScan; + usqInt ptr; + sqInt scanLargeObject; + sqInt sp; + + foundInactive = 0; + ptr = (GIV(unscannedEphemerons).start); + while (ptr <= ((GIV(unscannedEphemerons).top))) { + objOop = (ephemeron = longAt((void *)(ptr))); + + /* begin followedKeyOfEphemeron: */ + assert((isNonImmediate(objOop)) + && (isEphemeron(objOop))); + + /* begin followOopField:ofObject: */ + key = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isOopForwarded: */ + ((!(key & (tagMask())))) + && ((!((longAt((void *)(key))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + key = fixFollowedFieldofObjectwithInitialValue(0, objOop, key); + } + if ((((key & (tagMask())) != 0)) + || (((byteAt((void *)(key + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) { + foundInactive = 1; + + /* Now remove the inactive ephemeron from the set, and trace it. + Tracing it may add more ephemerons to the set. */ + if (((GIV(unscannedEphemerons).top)) > ptr) { + longAtput((void *)(ptr),longAt((void *)((GIV(unscannedEphemerons).top)))); + } + (GIV(unscannedEphemerons).top = ((GIV(unscannedEphemerons).top)) - BytesPerOop); + + /* To get markAndTrace: to trace the ephemeron we need to clear the mark bit. */ + + /* begin markLoopFrom: */ + numStrongSlots = 0; + objToScan = ephemeron; + + /* To avoid overflowing the mark stack when we encounter large objects, we + push the obj, then its numStrongSlots, and then index the object from the stack. */ + do { + if (((objToScan & (tagMask())) != 0)) { + scanLargeObject = 1; + } + else { + /* begin numStrongSlotsOfInephemeral: */ + fmt = (byteAt((void *)(objToScan + (formatFieldByteOffset())))) & (formatMask()); + assert((fmt != (ephemeronFormat())) + || ((isImmediate(keyOfEphemeron(objToScan))) + || (isMarked(keyOfEphemeron(objToScan))))); + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(objToScan)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objToScan + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objToScan - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (arrayFormat())) { + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (indexablePointersFormat())) { + if (((longAt((void *)(objToScan))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + setTraceFlagOnContextsFramesPageIfNeeded(objToScan); + + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l3; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objToScan))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l3: + numStrongSlots = CtxtTempFrameStart + contextSize; + goto l4; + } + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (weakArrayFormat())) { + objOopSqIntSqInt = fetchClassOfNonImm(objToScan); + numStrongSlots = (((longAt((void *)((objOopSqIntSqInt + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + goto l4; + } + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (forwardedFormat())) { + numStrongSlots = 1; + goto l4; + } + if (fmt < (firstCompiledMethodFormat())) { + numStrongSlots = 0; + goto l4; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objToScan)); + headerSqInt = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numStrongSlots = numLiterals + LiteralStart; + /* end numStrongSlotsOfInephemeral: */ +l4: + scanLargeObject = numStrongSlots > 64 /* traceImmediatelySlotLimit */; + } + if (scanLargeObject) { + if (((objToScan & (tagMask())) != 0)) { + index = (objToScan >> 3); + objToScan = topOfObjStack(GIV(markStack)); + } + else { + index = numStrongSlots; + markAndTraceClassOf(objToScan); + } + while (index > 0) { + index -= 1; + field = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if ((!(field & (tagMask())))) { + if ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + field = fixFollowedFieldofObjectwithInitialValue(index, objToScan, field); + } + + /* fixFollowedField: is /not/ inlined */ + + /* begin markAndShouldScan: */ + if (((field & (tagMask())) != 0)) { + goto l1; + } + + /* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. */ + assert(!(isForwarded(field))); + if ((byteAt((void *)(field + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + goto l1; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(field))); + byteAtput((void *)(field + (markBitsByteOffset())),(byteAt((void *)(field + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + format = (byteAt((void *)(field + (formatFieldByteOffset())))) & (formatMask()); + if (/* isPureBitsFormat: */ + (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat()))) { + if (((longAt((void *)(field))) & (classIndexMask())) > (lastClassIndexPun())) { + markAndTraceClassOf(field); + } + goto l1; + } + + /* avoid pushing non-pointer objects on the markStack. + Avoid tracing classes of non-objects on the heap, e.g. IRC caches, Sista counters. */ + if (format == (weakArrayFormat())) { + objStack1 = GIV(weaklingStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + goto l1; + } + + /* push weaklings on the weakling stack to scan later */ + if ((format == (ephemeronFormat())) + && (activeAndDeferredScan(field))) { + goto l1; + } + if (index > 0) { + if ((topOfObjStack(GIV(markStack))) != objToScan) { + objStack = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objToScan)); + if (((objToScan & (tagMask())) != 0)) { + assert(objStack == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack)) + ? fetchPointerofObject(ObjStackNextx, objStack) + : objStack)))); + } + else { + assert(!((objStack == GIV(markStack)) + && (isWeakNonImm(objToScan)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack != GIV(weaklingStack)) + || (isWeakNonImm(objToScan))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objToScan, objStack); + } + objOopSqInt = (((usqInt)index << 3) | 1); + objStackSqInt = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objOopSqInt)); + if (((objOopSqInt & (tagMask())) != 0)) { + assert(objStackSqInt == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStackSqInt)) + ? fetchPointerofObject(ObjStackNextx, objStackSqInt) + : objStackSqInt)))); + } + else { + assert(!((objStackSqInt == GIV(markStack)) + && (isWeakNonImm(objOopSqInt)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStackSqInt != GIV(weaklingStack)) + || (isWeakNonImm(objOopSqInt))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objOopSqInt, objStackSqInt); + } + objToScan = field; + index = -1; +l1:; + } + } + if (index >= 0) { + objToScan = popObjStack(GIV(markStack)); + if (objToScan == ephemeron) { + objToScan = popObjStack(GIV(markStack)); + } + } + } + else { + index = numStrongSlots; + markAndTraceClassOf(objToScan); + while (index > 0) { + index -= 1; + field = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if ((!(field & (tagMask())))) { + if ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + field = fixFollowedFieldofObjectwithInitialValue(index, objToScan, field); + } + + /* fixFollowedField: is /not/ inlined */ + + /* begin markAndShouldScan: */ + if (((field & (tagMask())) != 0)) { + goto l2; + } + + /* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. */ + assert(!(isForwarded(field))); + if ((byteAt((void *)(field + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + goto l2; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(field))); + byteAtput((void *)(field + (markBitsByteOffset())),(byteAt((void *)(field + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + formatSqInt = (byteAt((void *)(field + (formatFieldByteOffset())))) & (formatMask()); + if (/* isPureBitsFormat: */ + (formatSqInt >= (sixtyFourBitIndexableFormat())) + && (formatSqInt < (firstCompiledMethodFormat()))) { + if (((longAt((void *)(field))) & (classIndexMask())) > (lastClassIndexPun())) { + markAndTraceClassOf(field); + } + goto l2; + } + + /* avoid pushing non-pointer objects on the markStack. + Avoid tracing classes of non-objects on the heap, e.g. IRC caches, Sista counters. */ + if (formatSqInt == (weakArrayFormat())) { + objStack1 = GIV(weaklingStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + goto l2; + } + + /* push weaklings on the weakling stack to scan later */ + if ((formatSqInt == (ephemeronFormat())) + && (activeAndDeferredScan(field))) { + goto l2; + } + objStack1 = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + if (((byteAt((void *)(field + (numSlotsFieldByteOffset())))) > 64 /* traceImmediatelySlotLimit */) + && (((numStrongSlots = numStrongSlotsOfInephemeral(field))) > 64 /* traceImmediatelySlotLimit */)) { + objOop1 = (((usqInt)numStrongSlots << 3) | 1); + objStack1 = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objOop1)); + if (((objOop1 & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(objOop1)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(objOop1))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objOop1, objStack1); + } +l2:; + } + } + objToScan = popObjStack(GIV(markStack)); + } + + /* scanning a large object. scan until hitting an unmarked object, then switch to it, if any. + scanning a small object. scan, marking, pushing unmarked referents, then switch to the top of the stack. */ + } while(objToScan); + } + else { + ptr += BytesPerOop; + } + } + return foundInactive; +} + + +/* for profiling */ +/* Mark all accessible objects. + objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged is true if all + objects are unmarked and/or if unmarked classes shoud be removed from the + class table. */ +/* If the incremental collector is running mark bits may be set; stop it and + clear them if necessary. + */ + + /* SpurMemoryManager>>#markObjects: */ +static NoDbgRegParms NeverInline void +markObjects(sqInt objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt classOrNil; + sqInt classTablePage; + sqInt format; + sqInt i; + sqInt j; + sqInt objOop; + sqInt oop; + sqInt result; + StackPage *thePage; + sqInt toDoLimit; + + runLeakCheckerFor(GCModeFull); + + /* begin shutDownGlobalIncrementalGC: */ + if (objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged) { + assert(allObjectsUnmarked()); + } + initializeUnscannedEphemerons(); + + /* begin initializeMarkStack */ + ensureRoomOnObjStackAt(MarkStackRootIndex); + ensureRoomOnObjStackAt(WeaklingStackRootIndex); + GIV(marking) = 1; + + /* begin markAccessibleObjectsAndFireEphemerons */ + assert(GIV(marking)); + assert(validClassTableRootPages()); + assert(allBridgesMarked()); + + /* for debugging markAndTrace: set (MarkStackRecord := OrderedCollection new) + This must come first to enable stack page reclamation. It clears + the trace flags on stack pages and so must precede any marking. + Otherwise it will clear the trace flags of reached pages. */ + + /* begin initStackPageGC */ + if (GIV(stackPage)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + (thePage->trace = StackPageUnreached); + } + + /* begin markAndTraceHiddenRoots */ + markAndTraceObjStackandContents(GIV(markStack), 0); + markAndTraceObjStackandContents(GIV(weaklingStack), 0); + markAndTraceObjStackandContents(GIV(mournQueue), 1); + objOop = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + /* begin freeListsObj */ + assert((firstIndexableField(oldSpaceObjectAfter(GIV(trueObj)))) == GIV(freeLists)); + objOop = oldSpaceObjectAfter(GIV(trueObj)); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* begin isWeakNonImm: */ + format = (byteAt((void *)(GIV(classTableFirstPage) + (formatFieldByteOffset())))) & (formatMask()); + if (format == (weakArrayFormat())) { + markAndTrace(GIV(hiddenRootsObj)); + goto l1; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(GIV(hiddenRootsObj)))); + byteAtput((void *)(GIV(hiddenRootsObj) + (markBitsByteOffset())),(byteAt((void *)(GIV(hiddenRootsObj) + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + markAndTrace(GIV(classTableFirstPage)); + for (i = 1; i < GIV(numClassTablePages); i += 1) { + objOop = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + } + /* end markAndTraceHiddenRoots */ +l1: + + /* begin markAndTraceExtraRoots */ + assert(GIV(remapBufferCount) == 0); + + /* 1 to: remapBufferCount do: + [:i| + oop := remapBuffer at: i. + ((self isImmediate: oop) or: [self isFreeObject: oop]) ifFalse: + [self markAndTrace: oop]]. */ + for (i = 1; i <= GIV(extraRootCount); i += 1) { + oop = (GIV(extraRoots)[i])[0]; + if (!((((oop & (tagMask())) != 0)) + || (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())))) { + markAndTrace(oop); + } + } + assert(validClassTableRootPages()); + + /* begin markAndTraceInterpreterOops: */ + markAndTraceStackPages(1); + markAndTraceTraceLog(); + markAndTracePrimTraceLog(); + markAndTrace(GIV(specialObjectsOop)); + if (!(((GIV(newMethod) & (tagMask())) != 0))) { + markAndTrace(GIV(newMethod)); + } + + /* begin traceProfileState */ + /* begin followForwardingPointersInProfileState */ + /* begin profileStateDoUpdating: */ + if (GIV(profileProcess)) { + if ((result = ((!((longAt((void *)(GIV(profileProcess)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileProcess)) + : 0))) { + GIV(profileProcess) = result; + } + } + if (GIV(profileMethod)) { + if ((result = ((!((longAt((void *)(GIV(profileMethod)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileMethod)) + : 0))) { + GIV(profileMethod) = result; + } + } + if (GIV(profileSemaphore)) { + if ((result = ((!((longAt((void *)(GIV(profileSemaphore)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileSemaphore)) + : 0))) { + GIV(profileSemaphore) = result; + } + } + + /* begin profileStateDo: */ + if (GIV(profileProcess)) { + markAndTrace(GIV(profileProcess)); + } + if (GIV(profileMethod)) { + markAndTrace(GIV(profileMethod)); + } + if (GIV(profileSemaphore)) { + markAndTrace(GIV(profileSemaphore)); + } +# if LRPCheck + /* The longRunningPrimitiveCheckMethod (LRPCM) is sampled in an interrupt. Be very careful with it. + If longRunningPrimitiveCheckSequenceNumber (LRPCSN) = statCheckForEvents then LRPCM has + been recenty sampled, but it must be newMethod and we don't need to trace it twice. If LRPCSN + ~= statCheckForEvents then LRPCM must be some extant object and needs to be traced. */ + sqLowLevelMFence(); + if ((GIV(longRunningPrimitiveCheckMethod) != null) + && (GIV(longRunningPrimitiveCheckSequenceNumber) != GIV(statCheckForEvents))) { + if ((!((longAt((void *)(GIV(longRunningPrimitiveCheckMethod)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + GIV(longRunningPrimitiveCheckMethod) = followForwarded(GIV(longRunningPrimitiveCheckMethod)); + } + markAndTrace(GIV(longRunningPrimitiveCheckMethod)); + } + if (GIV(longRunningPrimitiveCheckSemaphore)) { + if ((!((longAt((void *)(GIV(longRunningPrimitiveCheckSemaphore)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + GIV(longRunningPrimitiveCheckSemaphore) = followForwarded(GIV(longRunningPrimitiveCheckSemaphore)); + } + markAndTrace(GIV(longRunningPrimitiveCheckSemaphore)); + } +# endif // LRPCheck + + if (GIV(tempOop)) { + markAndTrace(GIV(tempOop)); + } + if (GIV(tempOop2)) { + markAndTrace(GIV(tempOop2)); + } + + /* V3 memory manager support */ + for (i = 1; i <= GIV(remapBufferCount); i += 1) { + oop = GIV(remapBuffer)[i]; + if (!(((oop & (tagMask())) != 0))) { + markAndTrace(oop); + } + } + assert(validObjStacks()); + markWeaklingsAndMarkAndFireEphemerons(); + assert(validObjStacks()); + + /* begin expungeDuplicateAndUnmarkedClasses: */ + for (i = 1; i < GIV(numClassTablePages); i += 1) { + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNil = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + classIndex = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + assert((classOrNil == GIV(nilObj)) + || (addressCouldBeClassObj(classOrNil))); + + /* only remove a class if it is at a duplicate entry or it is unmarked and we're expunging unmarked classes. */ + if (classOrNil == GIV(nilObj)) { + if (classIndex < GIV(classTableIndex)) { + GIV(classTableIndex) = classIndex; + } + } + else { + if ((objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged + && (!(((byteAt((void *)(classOrNil + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) + || (((long32At((void *)(classOrNil + 4))) & (identityHashHalfWordMask())) != classIndex)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(classTablePage)) + && (!(isForwarded(classTablePage)))); + assert(validStorePointerUncheckedArgs(j, classTablePage, GIV(nilObj))); + longAtput((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord()))))),GIV(nilObj)); + + /* but if it is marked, it should still be in the table at its correct index. */ + assert((objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged + && (!(isMarked(classOrNil)))) + || ((classAtIndex(rawHashBitsOf(classOrNil))) == classOrNil)); + + /* If the removed class is before the classTableIndex, set the + classTableIndex to point to the empty slot so as to reuse it asap. */ + if (classIndex < GIV(classTableIndex)) { + GIV(classTableIndex) = classIndex; + } + } + } + } + } + + /* Avoid expunging the puns by not scanning the 0th page. + classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + assert(GIV(classTableIndex) >= (1U << (classTableMajorIndexShift()))); + nilUnmarkedWeaklingSlots(); + GIV(marking) = 0; +} + + +/* After the initial scan-mark is complete ephemerons can be processed. + Weaklings have accumulated on the weaklingStack, but more may be + uncovered during ephemeron processing. So trace the strong slots + of the weaklings, and as ephemerons are processed ensure any newly + reached weaklings are also traced. */ + + /* SpurMemoryManager>>#markWeaklingsAndMarkAndFireEphemerons */ +static void +markWeaklingsAndMarkAndFireEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ephemeron; + sqInt ephemeronSqInt; + sqInt field; + sqInt i; + sqInt iSqInt; + sqInt key; + sqInt keySqInt; + sqInt numOnThisPage; + sqInt numToEnumerate; + sqInt numToEnumerateOnThisPage; + sqInt numTracedWeaklings; + sqInt numUnscannedBeforeMarkAndFire; + sqInt objStackPage; + usqInt p; + sqInt size; + sqInt toDoLimit; + sqInt topIndex; + sqInt weakling; + + numTracedWeaklings = 0; + while (1) { + assert(unscannedEphemeronsContainsOnlyEphemerons()); + markAndTraceUntracedReachableStackPages(); + markAndTraceMachineCodeOfMarkedMethods(); + + /* Make sure all reached weaklings have their strong slots traced before firing ephemerons... */ + do { + /* begin markAndTraceWeaklingsFrom: */ + /* begin objStack:from:do: */ + eassert(isValidObjStack(GIV(weaklingStack))); + size = longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + objStackPage = longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + while (objStackPage != 0) { + size += ObjStackLimit; + assert((fetchPointerofObject(ObjStackTopx, objStackPage)) == ObjStackLimit); + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + numToEnumerate = size - numTracedWeaklings; + objStackPage = GIV(weaklingStack); + while (numToEnumerate > 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + numToEnumerateOnThisPage = ((numToEnumerate < numOnThisPage) ? numToEnumerate : numOnThisPage); + topIndex = (numOnThisPage + ObjStackFixedSlots) - 1; + for (iSqInt = topIndex; iSqInt >= ((topIndex - numToEnumerateOnThisPage) + 1); iSqInt += -1) { + assert(isWeak(fetchPointerofObject(iSqInt, objStackPage))); + weakling = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + assert(!((isForwarded(weakling)))); + markAndTraceClassOf(weakling); + + /* N.B. generateToByDoLimitExpression:negative:on: guards against (unsigned)0 - 1 going +ve */ + toDoLimit = (numStrongSlotsOfWeakling(weakling)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + /* begin followOopField:ofObject: */ + field = longAt((void *)((weakling + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(field & (tagMask())))) + && ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + field = fixFollowedFieldofObjectwithInitialValue(i, weakling, field); + } + if (!((((field & (tagMask())) != 0)) + || (((byteAt((void *)(field + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) { + markAndTrace(field); + } + } + } + numToEnumerate -= numToEnumerateOnThisPage; + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + numTracedWeaklings = size; + } while((sizeOfObjStack(GIV(weaklingStack))) > numTracedWeaklings); + if (((GIV(unscannedEphemerons).top)) < ((GIV(unscannedEphemerons).start))) { + markAndTraceUntracedReachableStackPages(); + markAndTraceMachineCodeOfMarkedMethods(); + freeUntracedStackPages(); + freeUnmarkedMachineCode(); + if (((GIV(unscannedEphemerons).top)) < ((GIV(unscannedEphemerons).start))) { + return; + } + } + assert(unscannedEphemeronsContainsOnlyEphemerons()); + while (markInactiveEphemerons()) { + } + assert(unscannedEphemeronsContainsOnlyActiveEphemerons()); + numUnscannedBeforeMarkAndFire = ((((sqInt) (((GIV(unscannedEphemerons).top)) - ((GIV(unscannedEphemerons).start))))) / BytesPerOop) + 1; + + /* begin fireAllUnscannedEphemerons */ + key = 0; + assert(allUnscannedEphemeronsAreActive()); + for (p = ((GIV(unscannedEphemerons).start)); p <= ((GIV(unscannedEphemerons).top)); p += BytesPerOop) { + ephemeron = longAt((void *)(p)); + if ((isEphemeron(ephemeron)) + && ((isNonImmediate((/* begin keyOfEphemeron: */ + assert((isNonImmediate(ephemeron)) + && (isObjEphemeron(ephemeron))), + (key = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))))))) + && (!(((byteAt((void *)(key + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)))) { + fireEphemeron(ephemeron); + } + } + + /* begin markAllUnscannedEphemerons */ + assert(allUnscannedEphemeronsAreActive()); + while (((GIV(unscannedEphemerons).top)) >= ((GIV(unscannedEphemerons).start))) { + ephemeronSqInt = longAt((void *)((GIV(unscannedEphemerons).start))); + longAtput((void *)((GIV(unscannedEphemerons).start)),longAt((void *)((GIV(unscannedEphemerons).top)))); + (GIV(unscannedEphemerons).top = ((GIV(unscannedEphemerons).top)) - BytesPerOop); + + /* begin followedKeyOfMaybeFiredEphemeron: */ + assert(isMaybeFiredEphemeron(ephemeronSqInt)); + keySqInt = longAt((void *)((ephemeronSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isOopForwarded: */ + ((!(keySqInt & (tagMask())))) + && ((!((longAt((void *)(keySqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + keySqInt = fixFollowedFieldofObjectwithInitialValue(0, ephemeronSqInt, keySqInt); + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(ephemeronSqInt))); + byteAtput((void *)(ephemeronSqInt + (markBitsByteOffset())),(byteAt((void *)(ephemeronSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + markAndTrace(keySqInt); + markAndTrace(ephemeronSqInt); + } + if (numUnscannedBeforeMarkAndFire == (((((sqInt) (((GIV(unscannedEphemerons).top)) - ((GIV(unscannedEphemerons).start))))) / BytesPerOop) + 1)) { + initializeUnscannedEphemerons(); + } + } +} + + /* SpurMemoryManager>>#maxIdentityHash */ +sqInt +maxIdentityHash(void) +{ + return identityHashHalfWordMask(); +} + + +/* Almost entirely arbitrary, but we dont want 1Mb bitmaps allocated in eden. + But this choice means no check for numSlots > maxSlotsForNewSpaceAlloc + for non-variable allocations. */ + + /* SpurMemoryManager>>#maxSlotsForNewSpaceAlloc */ +sqInt +maxSlotsForNewSpaceAlloc(void) +{ + return (1U << (fixedFieldsFieldWidth())) - 1; +} + + +/* Answers if the code is installed in a class instantiating objects with the + format. Used in primitive + generation to make a quick path based on where the method is installed. + This method cannot + be used as a guarantee as there can be false positive, it's just a + heuristic. Tries to interpret the last literal of the method as a behavior + (more than 3 fields, 3rd field a Smi). + If it can be interpreted as a behavior, answers if instSpec matches the + format, else answers false. */ + + /* SpurMemoryManager>>#maybeMethodClassOf:seemsToBeInstantiating: */ +sqInt +maybeMethodClassOfseemsToBeInstantiating(sqInt methodObj, sqInt format) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt instSpec; + sqInt literal; + sqInt maybeClassObj; + sqInt maybeFormat; + sqInt offset; + + /* begin methodClassOf: */ + offset = (literalCountOf(methodObj)) - 1; + literal = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = fixFollowedFieldofObjectwithInitialValue(offset + LiteralStart, methodObj, literal); + } + maybeClassObj = ((literal != GIV(nilObj)) + && (/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + ? (assert((numSlotsOf(literal)) > ValueIndex), + followFieldofObject(ValueIndex, literal)) + : GIV(nilObj)); + if (!(((byteAt((void *)(maybeClassObj + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) { + return 0; + } + if (!((numSlotsOfAny(maybeClassObj)) > InstanceSpecificationIndex)) { + return 0; + } + maybeFormat = longAt((void *)((maybeClassObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord())))))); + if (!((((maybeFormat) & 7) == 1))) { + return 0; + } + instSpec = (((usqInt)(((maybeFormat >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + return instSpec == format; +} + + +/* Answer the minimum number of additional slots to allocate in an object to + always be able to shorten it. + This is enough slots to allocate a minimum-sized object. */ + + /* SpurMemoryManager>>#minSlotsForShortening */ +sqInt +minSlotsForShortening(void) +{ + return 16 /* (allocationUnit * 2) */ / BytesPerOop; +} + + +/* N.B. may be called from machine code on the Smalltalk stack so this should + not call deeply. + */ + + /* SpurMemoryManager>>#newHashBitsOf: */ +static NoDbgRegParms usqInt +newHashBitsOf(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt hash; + + /* begin newObjectHash */ + assert(!((GIV(lastHash) == 0))); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 2)); + GIV(lastHash) = GIV(lastHash) ^ ((((GIV(lastHash) << 7))) & (identityHashHalfWordMask())); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 3)); + hash = GIV(lastHash); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(objOop + 4),((((long32At((void *)(objOop + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + return hash & (identityHashHalfWordMask()); +} + + /* SpurMemoryManager>>#newSpaceIsEmpty */ +static sqInt +newSpaceIsEmpty(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (GIV(freeStart) == (((GIV(eden)).start))) + && (GIV(pastSpaceStart) == (((GIV(pastSpace)).start))); +} + + /* SpurMemoryManager>>#nilObject */ +sqInt +nilObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(nilObj); +} + + +/* Nil the unmarked slots in the weaklings on the + weakling stack, finalizing those that lost references. + Finally, empty the weaklingStack. */ +/* for profiling */ + + /* SpurMemoryManager>>#nilUnmarkedWeaklingSlots */ +static NeverInline void +nilUnmarkedWeaklingSlots(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anyUnmarked; + sqInt i; + sqInt iSqInt; + sqInt nextNextPage; + sqInt nextPage; + sqInt numOnThisPage; + usqInt numSlots; + sqInt numToEnumerate; + sqInt numToEnumerateOnThisPage; + sqInt objStackPage; + sqInt referent; + sqInt size; + sqInt toDoLimit; + sqInt topIndex; + sqInt weakling; + + eassert(allOldMarkedWeakObjectsOnWeaklingStack()); + if (GIV(weaklingStack) == GIV(nilObj)) { + return; + } + + /* begin objStack:from:do: */ + eassert(isValidObjStack(GIV(weaklingStack))); + size = longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + objStackPage = longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + while (objStackPage != 0) { + size += ObjStackLimit; + assert((fetchPointerofObject(ObjStackTopx, objStackPage)) == ObjStackLimit); + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + numToEnumerate = size; + objStackPage = GIV(weaklingStack); + while (numToEnumerate > 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + numToEnumerateOnThisPage = ((numToEnumerate < numOnThisPage) ? numToEnumerate : numOnThisPage); + topIndex = (numOnThisPage + ObjStackFixedSlots) - 1; + for (i = topIndex; i >= ((topIndex - numToEnumerateOnThisPage) + 1); i += -1) { + assert(isWeak(fetchPointerofObject(i, objStackPage))); + weakling = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin nilUnmarkedWeaklingSlotsIn: */ + anyUnmarked = 0; + assert(allStrongSlotsOfWeaklingAreMarked(weakling)); + + /* N.B. generateToByDoLimitExpression:negative:on: guards against (unsigned)0 - 1 going +ve */ + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(weakling)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(weakling + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(weakling - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (iSqInt = (numStrongSlotsOfWeakling(weakling)); iSqInt <= toDoLimit; iSqInt += 1) { + referent = longAt((void *)((weakling + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + if (((longAt((void *)(referent))) & (classIndexMask())) == (isForwardedObjectClassIndexPun())) { + referent = fixFollowedFieldofObjectwithInitialValue(iSqInt, weakling, referent); + } + if (!((((referent & (tagMask())) != 0)) + || (((byteAt((void *)(referent + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(weakling)) + && (!(isForwarded(weakling)))); + assert(validStorePointerUncheckedArgs(iSqInt, weakling, GIV(nilObj))); + longAtput((void *)((weakling + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord()))))),GIV(nilObj)); + anyUnmarked = 1; + } + } + } + if (anyUnmarked) { + /* fireFinalization: could grow the mournQueue and if so, + additional pages must be marked to avoid being GC'ed. */ + assert(GIV(marking)); + + /* begin fireFinalization: */ + if (((imageHeaderFlags & 64) != 0)) { + queueMourner(weakling); + } + + /* begin signalFinalization: */ + forceInterruptCheck(); + GIV(pendingFinalizationSignals) += 1; + } + } + numToEnumerate -= numToEnumerateOnThisPage; + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + + /* begin emptyObjStack: */ + if (GIV(weaklingStack) == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(GIV(weaklingStack))); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(GIV(weaklingStack))) == (wordIndexableFormat())); + longAtput((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))),0); + nextPage = longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + while (nextPage != 0) { + nextNextPage = longAt((void *)((nextPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(nextPage)) == (wordIndexableFormat())); + longAtput((void *)((nextPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))))); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(nextPage)) == (wordIndexableFormat())); + longAtput((void *)((nextPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord()))))),0); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(GIV(weaklingStack))) == (wordIndexableFormat())); + longAtput((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),nextPage); + nextPage = nextNextPage; + } + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(GIV(weaklingStack))) == (wordIndexableFormat())); + longAtput((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord()))))),0); + eassert(isValidObjStack(GIV(weaklingStack))); + /* end emptyObjStack: */ +l1:; +} + + /* SpurMemoryManager>>#noCheckClassAtIndex: */ +static NoDbgRegParms sqInt +noCheckClassAtIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt fieldIndex; + + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return null; + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + +/* Push an element on an objStack. Split from push:onObjStack: for testing. */ + + /* SpurMemoryManager>>#noCheckPush:onObjStack: */ +static NoDbgRegParms sqInt +noCheckPushonObjStack(sqInt objOop, sqInt objStack) +{ + sqInt topx; + + eassert(isValidObjStack(objStack)); + + /* for debugging markAndTrace: set (MarkStackRecord := OrderedCollection new) */ + topx = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + if (topx >= ObjStackLimit) { + noCheckPushonObjStack(objOop, ensureRoomOnObjStackAt(longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))))); + } + else { + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(objStack)) == (wordIndexableFormat())); + longAtput((void *)((objStack + BaseHeaderSize) + ((((usqInt)((ObjStackFixedSlots + topx)) << (shiftForWord()))))),objOop); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(objStack)) == (wordIndexableFormat())); + longAtput((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))),topx + 1); + } + return objOop; +} + + +/* Make sure the oop at fieldIndex in anObject is not forwarded (follow the + forwarder there-in if so). Answer the (possibly followed) oop at + fieldIndex. */ + + /* SpurMemoryManager>>#noFixupFollowField:ofObject: */ +static NoDbgRegParms sqInt +noFixupFollowFieldofObject(sqInt fieldIndex, sqInt anObject) +{ + sqInt objOop; + sqInt referent; + + objOop = longAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + referent = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + objOop = referent; + } + return objOop; +} + + +/* Allocate an object with numSlots space. If there is room beneath + scavengeThreshold allocate in newSpace, otherwise alocate in oldSpace. If + there is not room in newSpace + and a scavenge is not already scheduled, schedule a scavenge. */ + + /* SpurMemoryManager>>#noInlineAllocateSlots:format:classIndex: */ +static NoDbgRegParms NeverInline sqInt +noInlineAllocateSlotsformatclassIndex(sqInt numSlots, sqInt formatField, sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newObj; + usqInt numBytes; + + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if ((((usqInt)(numSlots)) >> 56) > 0) { + return null; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + return allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, formatField, classIndex); + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + return newObj; +} + + /* SpurMemoryManager>>#noUnscannedEphemerons */ +static int +noUnscannedEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((GIV(unscannedEphemerons).top)) < ((GIV(unscannedEphemerons).start)); +} + + +/* Answer the number of indexable bytes in the given non-immediate pure bits + object with format. + */ + + /* SpurMemoryManager>>#numBytesOfBits:format: */ +static NoDbgRegParms sqInt +numBytesOfBitsformat(sqInt objOop, sqInt format) +{ + usqInt numBytes; + usqInt numSlots; + + assert((isPureBitsFormat(format)) + && ((formatOf(objOop)) == format)); + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (format < (firstShortFormat())) { + return (format <= (sixtyFourBitIndexableFormat()) + ? numBytes + : numBytes - (((format & 1) << 2))); + } + return (format < (firstByteFormat()) + ? numBytes - (((format & 3) << 1)) + : numBytes - (format & 7)); +} + + +/* Answer the number of indexable bytes in the given non-immediate + byte-indexable object. + */ + + /* SpurMemoryManager>>#numBytesOfBytes: */ +static NoDbgRegParms sqInt +numBytesOfBytes(sqInt objOop) +{ + sqInt fmt; + usqInt numSlots; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + return ((((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); +} + + +/* Answer the number of indexable bytes in the given non-immediate object. + Does not adjust the size of contexts by stackPointer. */ + + /* SpurMemoryManager>>#numBytesOf: */ +sqInt +numBytesOf(sqInt objOop) +{ + sqInt fmt; + usqInt numBytes; + usqInt numSlots; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + return numBytes - (fmt & 7); + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + return numBytes; + } + if (fmt >= (firstShortFormat())) { + return numBytes - (((fmt & 3) << 1)); + } + + /* fmt >= self firstLongFormat */ + return numBytes - (((fmt & 1) << 2)); +} + + +/* Answer the number of pointer fields in the given object. + Works with CompiledMethods, as well as ordinary objects. */ + + /* SpurMemoryManager>>#numPointerSlotsOf: */ +static NoDbgRegParms usqInt +numPointerSlotsOf(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt fmt; + sqInt header; + sqInt headerSqInt; + usqInt numLiterals; + usqInt numSlots; + sqInt sp; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin numPointerSlotsOf:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + return CtxtTempFrameStart + contextSize; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + return (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + } + if (fmt == (forwardedFormat())) { + return 1; + } + if (fmt < (firstCompiledMethodFormat())) { + return 0; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + return numLiterals + LiteralStart; +} + + /* SpurMemoryManager>>#numSlotsForBytes: */ +static NoDbgRegParms sqInt +numSlotsForBytes(sqInt numBytes) +{ + return (numBytes + (BytesPerWord - 1)) / BytesPerWord; +} + + +/* A private internal version of numSlotsOf: that is happy to be applied to + free or forwarded objects. + */ + + /* SpurMemoryManager>>#numSlotsOfAny: */ +static NoDbgRegParms usqInt +numSlotsOfAny(sqInt objOop) +{ + usqInt numSlots; + + numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + return (numSlots == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); +} + + +/* Answer the number of strong pointer fields in the given indexable pointer + object. + */ + + /* SpurMemoryManager>>#numSlotsOfIndexablePointerObj: */ +static NoDbgRegParms usqInt +numSlotsOfIndexablePointerObj(sqInt objOop) +{ + usqInt numSlots; + + assert((formatOf(objOop)) == (indexablePointersFormat())); + if (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + return CtxtTempFrameStart + (fetchStackPointerOf(objOop)); + } + else { + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + return (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + } +} + + +/* numSlotsOf: should not be applied to free or forwarded objects. */ + + /* SpurMemoryManager>>#numSlotsOf: */ +usqInt +numSlotsOf(sqInt objOop) +{ + usqInt numSlots; + + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + return (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); +} + + +/* Answer the number of strong pointer fields in the given object, + which is expected not to be an active ephemeron. + Works with CompiledMethods as well as ordinary objects. */ + + /* SpurMemoryManager>>#numStrongSlotsOfInephemeral: */ +static NoDbgRegParms sqInt +numStrongSlotsOfInephemeral(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt fmt; + sqInt header; + sqInt headerSqInt; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objOopSqInt; + sqInt sp; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + assert((fmt != (ephemeronFormat())) + || ((isImmediate(keyOfEphemeron(objOop))) + || (isMarked(keyOfEphemeron(objOop))))); + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (arrayFormat())) { + return numSlots; + } + if (fmt == (indexablePointersFormat())) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + setTraceFlagOnContextsFramesPageIfNeeded(objOop); + + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + return CtxtTempFrameStart + contextSize; + } + return numSlots; + } + if (fmt == (weakArrayFormat())) { + objOopSqInt = fetchClassOfNonImm(objOop); + + /* begin fixedFieldsOfClass: */ + return (((longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + } + return numSlots; + } + if (fmt == (forwardedFormat())) { + return 1; + } + if (fmt < (firstCompiledMethodFormat())) { + return 0; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + return numLiterals + LiteralStart; +} + + +/* Answer the number of strong pointer fields in the given weakling. */ + + /* SpurMemoryManager>>#numStrongSlotsOfWeakling: */ +static NoDbgRegParms sqInt +numStrongSlotsOfWeakling(sqInt objOop) +{ + sqInt objOopSqInt; + + assert((formatOf(objOop)) == (weakArrayFormat())); + objOopSqInt = fetchClassOfNonImm(objOop); + + /* begin fixedFieldsOfClass: */ + return (((longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); +} + + /* SpurMemoryManager>>#numUnscannedEphemerons */ +static sqInt +numUnscannedEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((((sqInt) (((GIV(unscannedEphemerons).top)) - ((GIV(unscannedEphemerons).start))))) / BytesPerOop) + 1; +} + + +/* Answer if objOop looks like a class object. WIth Spur be lenient if the + object doesn't + yet have a hash (i.e. is not yet in the classTable), and accept forwarding + pointers. + */ + + /* SpurMemoryManager>>#objCouldBeClassObj: */ +sqInt +objCouldBeClassObj(sqInt objOop) +{ + sqInt fieldOop; + + return (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + && (((numSlotsOfAny(objOop)) > InstanceSpecificationIndex) + && (((fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord()))))))), + ((addressCouldBeObj(fieldOop)) + && ((((byteAt((void *)(fieldOop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + || ((/* isOopForwarded: */ + ((!(fieldOop & (tagMask())))) + && ((!((longAt((void *)(fieldOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + && (isPointers(followForwarded(fieldOop)))))) + && (((fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord()))))))), + ((addressCouldBeObj(fieldOop)) + && ((((byteAt((void *)(fieldOop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + || ((/* isOopForwarded: */ + ((!(fieldOop & (tagMask())))) + && ((!((longAt((void *)(fieldOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + && (isPointers(followForwarded(fieldOop)))))) + && (((((longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord())))))))) & 7) == 1))))))); +} + + +/* Object parsing. + 1. all objects have at least a word following the header, for a forwarding + pointer. 2. objects with an overflow size have a preceeing word with a + saturated slotSize. If the word following + an object doesn't have a saturated size field it must be a single-header + object. If the word following + does have a saturated slotSize it must be the overflow size word. */ + + /* SpurMemoryManager>>#objectAfter: */ +sqInt +objectAfter(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + + if (oopisLessThan(objOop, GIV(oldSpaceStart))) { + if (oopisGreaterThanOrEqualToandLessThan(objOop, ((GIV(eden)).start), GIV(freeStart))) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + return GIV(freeStart); + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + } + if (oopisGreaterThanOrEqualToandLessThan(objOop, ((GIV(pastSpace)).start), GIV(pastSpaceStart))) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + return GIV(pastSpaceStart); + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + } + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(futureSurvivorStart))) { + return GIV(futureSurvivorStart); + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + } + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + return GIV(endOfMemory); + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); +} + + +/* useful for debugging */ + + /* SpurMemoryManager>>#objectBefore: */ +sqInt +objectBefore(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOopSqInt; + sqInt prev; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + prev = null; + if (oopisLessThan(objOop, GIV(oldSpaceStart))) { + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l2; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOopSqInt, GIV(freeStart))) { + if (oopisGreaterThanOrEqualTo(objOopSqInt, objOop)) { + return prev; + } + prev = objOopSqInt; + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOopSqInt = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + /* end allNewSpaceEntitiesDo: */ +l2: + return prev; + } + + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + if (oopisGreaterThanOrEqualTo(objOopSqInt, objOop)) { + return prev; + } + prev = objOopSqInt; + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3:; + } + return prev; +} + + +/* For assertions only... This ends the recursion in setting up the bridged + enumerations of new space that need objectBefore: for assertion checking. */ + + /* SpurMemoryManager>>#objectInPastSpaceBefore: */ +static NoDbgRegParms sqInt +objectInPastSpaceBefore(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt prev; + + if (GIV(pastSpaceStart) <= (((GIV(pastSpace)).start))) { + return null; + } + prev = null; + address = ((GIV(pastSpace)).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + obj = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + while (oopisLessThan(obj, GIV(pastSpaceStart))) { + if (oopisGreaterThanOrEqualTo(obj, objOop)) { + return prev; + } + prev = obj; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + obj = GIV(pastSpaceStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + return prev; +} + + +/* For enumerating objects find the header of the first object in a space. + If the object starts with an overflow size field it will start at the next + allocationUnit. c.f. numSlotsOf: */ + + /* SpurMemoryManager>>#objectStartingAt: */ +static NoDbgRegParms sqInt +objectStartingAt(sqInt address) +{ + usqInt numSlots; + + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + return (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); +} + + +/* This primitive is called from Squeak as... + arrayOfRoots uniquelyAccessibleObjects + + This primitive answers an array of the receiver and every object in its + proper tree of subParts (ie, that is not refered to from anywhere else + outside the tree). + + The primitive can fail for the following reasons with the specified + failure codes: + PrimErrNoMemory: additional allocations failed + */ + + /* SpurMemoryManager>>#objectsAccessibleFromRoots: */ +static NoDbgRegParms sqInt +objectsAccessibleFromRoots(sqInt arrayOfRootsArg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayOfObjects; + sqInt arrayOfRoots; + + runLeakCheckerFor(GCModeImageSegment); + + /* First scavenge to collect any new space garbage that refers to the graph. */ + + /* begin scavengingGC */ + scavengingGCTenuringIf(TenureByAge); + arrayOfRoots = /* updatePostScavenge: */ + ((!((longAt((void *)(arrayOfRootsArg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(arrayOfRootsArg) + : arrayOfRootsArg); + + /* Now compute the transitive closure, collecting the sequence of objects to be stored in the arrayOfObjects array. + Included in arrayOfObjects are the arrayOfRoots and all its contents. All objects have been unmarked. */ + arrayOfObjects = objectsReachableFromRoots(arrayOfRoots); + if (!arrayOfObjects) { + return (((usqInt)PrimErrNoMemory << 3) | 1); + } + + /* If objectsReachableFromRoots: answers an integer there is not enough continuous free space in which to allocate the + reachable objects. If there is sufficient free space then answer an error code to prompt a compacting GC and a retry. */ + if ((((arrayOfObjects) & 7) == 1)) { + if ((GIV(totalFreeOldSpace) - 8 /* allocationUnit */) >= ((arrayOfObjects >> 3))) { + return (((usqInt)PrimErrNeedCompaction << 3) | 1); + } + return (((usqInt)PrimErrNoMemory << 3) | 1); + } + assert(allObjectsUnmarked()); + assert(!((forwardersIn(arrayOfObjects)))); + return arrayOfObjects; +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. + Answer an Array of all the objects only reachable from the argument, an + Array of root objects, + starting with arrayOfRoots. If there is no space, answer a SmallInteger + whose value is the + number of slots required. This is used to collect the objects to include + in an image segment + on Spur, separate from creating the segment, hence simplifying the + implementation. Thanks to Igor Stasenko for this idea. */ + + /* SpurMemoryManager>>#objectsReachableFromRoots: */ +static NoDbgRegParms NeverInline sqInt +objectsReachableFromRoots(sqInt arrayOfRoots) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt count; + sqInt delta; + sqInt followingWord; + usqInt followingWordAddress; + usqInt freeChunk; + sqInt i; + sqInt iSqInt; + usqInt limit; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt oop; + sqInt oopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + usqInt ptr; + sqInt referent; + usqInt start; + usqInt startUsqInt; + sqInt toDoLimit; + sqInt toDoLimit1; + sqInt toDoLimitSqInt; + + + /* & hence start & ptr are too; limit is also because of addressAfter: */ + assert(isArray(arrayOfRoots)); + + /* Mark all objects except those only reachable from the arrayOfRoots by marking + each object in arrayOfRoots and then marking all reachable objects (from the + system roots). This leaves unmarked only objects reachable from the arrayOfRoots. + N.B. A side-effect of the marking is that all forwarders in arrayOfRoots will be followed. */ + assert(allObjectsUnmarked()); + + /* begin setGCMode: */ + GIV(gcMode) = GCModeImageSegment; + assert(!(isFreeObject(arrayOfRoots))); + byteAtput((void *)(arrayOfRoots + (markBitsByteOffset())),(byteAt((void *)(arrayOfRoots + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + toDoLimitSqInt = ((/* begin numSlotsOf: */ + assert((classIndexOf(arrayOfRoots)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(arrayOfRoots + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(arrayOfRoots - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (iSqInt = 0; iSqInt <= toDoLimitSqInt; iSqInt += 1) { + /* begin followField:ofObject: */ + oopSqInt = longAt((void *)((arrayOfRoots + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(oopSqInt & (tagMask())))) + && ((!((longAt((void *)(oopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + oopSqInt = fixFollowedFieldofObjectwithInitialValue(iSqInt, arrayOfRoots, oopSqInt); + } + if ((!(oopSqInt & (tagMask())))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oopSqInt))); + byteAtput((void *)(oopSqInt + (markBitsByteOffset())),(byteAt((void *)(oopSqInt + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + } + } + markObjects(0); + + /* After the mark phase all unreachable weak slots will have been nilled + and all active ephemerons fired. */ + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + assert(noUnscannedEphemerons()); + + /* Now unmark the roots before collecting the transitive closure of unmarked objects accessible from the roots. */ + + /* begin unmarkObjectsIn: */ + toDoLimitSqInt = ((assert((classIndexOf(arrayOfRoots)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(arrayOfRoots + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(arrayOfRoots - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (iSqInt = 0; iSqInt <= toDoLimitSqInt; iSqInt += 1) { + /* begin followField:ofObject: */ + oopSqInt = longAt((void *)((arrayOfRoots + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(oopSqInt & (tagMask())))) + && ((!((longAt((void *)(oopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + oopSqInt = fixFollowedFieldofObjectwithInitialValue(iSqInt, arrayOfRoots, oopSqInt); + } + if ((!(oopSqInt & (tagMask())))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oopSqInt))); + byteAtput((void *)(oopSqInt + (markBitsByteOffset())),(byteAt((void *)(oopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + + /* Use the largest free chunk to answer the result. */ + + /* N.B. Does /not/ update totalFreeOldSpace */ + freeChunk = allocateLargestFreeChunk(); + + /* but must update so that growth in the markStack does not cause assert fails. */ + GIV(totalFreeOldSpace) -= bytesInBody(freeChunk); + ptr = (start = freeChunk + BaseHeaderSize); + limit = addressAfter(freeChunk); + count = 0; + + /* First put the arrayOfRoots; order is important. */ + noCheckPushonObjStack(arrayOfRoots, GIV(markStack)); + + /* Now collect the roots and the transitive closure of unmarked objects from them. */ + while (!(isEmptyObjStack(GIV(markStack)))) { + objOop = popObjStack(GIV(markStack)); + assert(isMarked(objOop)); + count += 1; + if (ptr < limit) { + longAtput((void *)(ptr),objOop); + ptr += BytesPerOop; + } + oop = fetchClassOfNonImm(objOop); + if (!((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (markedBitByteShift())))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + noCheckPushonObjStack(oop, GIV(markStack)); + } + if ((((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(objOop))))) { + toDoLimit = (numSlotsOfMarriedContext(objOop)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + oop = fetchPointerofMarriedContext(i, objOop); + if (!((((oop & (tagMask())) != 0)) + || (((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + noCheckPushonObjStack(oop, GIV(markStack)); + } + } + } + else { + toDoLimit1 = (numPointerSlotsOf(objOop)) - 1; + for (i = 0; i <= toDoLimit1; i += 1) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (!((((oop & (tagMask())) != 0)) + || (((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + noCheckPushonObjStack(oop, GIV(markStack)); + } + } + } + } + + /* begin unmarkAllObjects */ + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + if ((byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) > (lastClassIndexPun())) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + else { + if (!(((longAt((void *)(objOopSqInt))) & (classIndexMask())) == (segmentBridgePun()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + startUsqInt = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (startUsqInt > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(startUsqInt + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? startUsqInt + BaseHeaderSize + : startUsqInt); + while (oopisLessThan(objOopSqInt, GIV(freeStart))) { + if ((byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) > (lastClassIndexPun())) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + else { + if (!(((longAt((void *)(objOopSqInt))) & (classIndexMask())) == (segmentBridgePun()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOopSqInt = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3: + + /* Now try and allocate the result */ + if ((count > ((ptr - start) / BytesPerOop)) + || ((limit != ptr) + && ((limit - ptr) <= 8 /* allocationUnit */))) { + freeObject(freeChunk); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeImageSegment)) == (GCCheckFreeSpace | GCModeImageSegment))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + + /* begin setGCMode: */ + GIV(gcMode) = 0; + return (((usqInt)count << 3) | 1); + } + + /* not enough room + can't split a single word + There's room; set the format, & classIndex and shorten. */ + + /* begin setFormatOf:to: */ + assert((((arrayFormat()) >= 0) && ((arrayFormat()) <= (formatMask())))); + byteAtput((void *)(freeChunk + (formatFieldByteOffset())),((byteAt((void *)(freeChunk + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (arrayFormat())); + + /* begin setClassIndexOf:to: */ + assert(((ClassArrayCompactIndex >= 0) && (ClassArrayCompactIndex <= (classIndexMask())))); + longAtput((void *)(freeChunk),((longAt((void *)(freeChunk))) & (~(usqIntptr_t)(classIndexMask()))) + ClassArrayCompactIndex); + + /* begin shorten:toIndexableSize: */ + assert((count >= 0) + && (count < (lengthOf(freeChunk)))); + delta = doShortentoIndexableSize(freeChunk, count); + assert((lengthOf(followMaybeForwarded(freeChunk))) == count); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + if ((!((longAt((void *)(freeChunk))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(freeChunk)); + referent = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + freeChunk = referent; + } + + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(freeChunk + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(freeChunk); + } + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeImageSegment)) == (GCCheckFreeSpace | GCModeImageSegment))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + runLeakCheckerFor(GCModeImageSegment); + + /* begin setGCMode: */ + GIV(gcMode) = 0; + return freeChunk; +} + + +/* Verify that the given oop is legitimate. Check address, header, and size + but not class. + */ + + /* SpurMemoryManager>>#okayOop: */ +static NoDbgRegParms sqInt +okayOop(sqInt signedOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt fmt; + usqInt oop; + usqLong unusedBits; + sqInt unusedBitsInYoungObjects; + + oop = ((usqInt) signedOop); + + /* address and size checks */ + if (((oop & (tagMask())) != 0)) { + return 1; + } + if (!(addressCouldBeObj(oop))) { + error("oop is not a valid address"); + return 0; + } + if (!(oopisLessThanOrEqualTo(addressAfter(oop), GIV(endOfMemory)))) { + error("oop size would make it extend beyond the end of memory"); + return 0; + } + + /* header type checks */ + if (!(((classIndex = (longAt((void *)(oop))) & (classIndexMask()))) >= (firstClassIndexPun()))) { + error("oop is a free chunk, or bridge, not an object"); + return 0; + } + if (((byteAt((void *)(oop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) + && ((byteAt((void *)((oop - BaseHeaderSize) + (numSlotsFieldByteOffset())))) != (numSlotsMask()))) { + error("oop header has overflow header word, but overflow word does not have a saturated numSlots field"); + return 0; + } + + /* format check */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if ((fmt == 6) || (fmt == 8)) { + error("oop has an unknown format type"); + return 0; + } + if ((fmt == (forwardedFormat())) != (classIndex == (isForwardedObjectClassIndexPun()))) { + error("oop has mis-matched format/classIndex fields; only one of them is the isForwarded value"); + return 0; + } + + /* specific header bit checks */ + unusedBits = (0x400000) | (0x40000000000000LL); + if ((long64At((void *)(oop))) & unusedBits) { + error("some unused header bits are set; should be zero"); + return 0; + } + unusedBitsInYoungObjects = ((1ULL << (greyBitShift())) | (1U << (pinnedBitShift()))) | (1U << (rememberedBitShift())); + if ((longAt((void *)(oop))) & unusedBitsInYoungObjects) { + error("some header bits unused in young objects are set; should be zero"); + return 0; + } + return 1; +} + + +/* useful for debugging */ +/* Object parsing. + 1. all objects have at least a word following the header, for a forwarding + pointer. 2. objects with an overflow size have a preceeing word with a + saturated slotSize. If the word following + an object doesn't have a saturated size field it must be a single-header + object. If the word following + does have a saturated slotSize it must be the overflow size word. */ + + /* SpurMemoryManager>>#oldSpaceObjectAfter: */ +sqInt +oldSpaceObjectAfter(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + return GIV(endOfMemory); + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); +} + + +/* in an effort to fix a compiler bug with two-way become post r3427 */ +/* Allocate two new objects, n1 & n2. Copy the contents appropriately. + Convert obj1 and obj2 + into forwarding objects pointing to n2 and n1 respectively. No need to + check if cloneObject: + succeeds because an earlier pass over objects ensured that there is enough + memory. + */ + + /* SpurMemoryManager>>#outOfPlaceBecome:and:copyHashFlag: */ +static NoDbgRegParms NeverInline void +outOfPlaceBecomeandcopyHashFlag(sqInt obj1, sqInt obj2, sqInt copyHashFlag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt clone1; + sqInt clone2; + sqInt hash; + + clone1 = (((longAt((void *)(obj1))) & (classIndexMask())) == ClassMethodContextCompactIndex + ? cloneContext(obj1) + : cloneObject(obj1)); + clone2 = (((longAt((void *)(obj2))) & (classIndexMask())) == ClassMethodContextCompactIndex + ? cloneContext(obj2) + : cloneObject(obj2)); + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(obj1 + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* begin setIsImmutableOf:to: */ + longAtput((void *)(clone1),(longAt((void *)(clone1))) | (1U << (immutableBitShift()))); + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(obj2 + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* begin setIsImmutableOf:to: */ + longAtput((void *)(clone2),(longAt((void *)(clone2))) | (1U << (immutableBitShift()))); + } + if (copyHashFlag) { + hash = (long32At((void *)(obj1 + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(clone1 + 4),((((long32At((void *)(clone1 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + hash = (long32At((void *)(obj2 + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(clone2 + 4),((((long32At((void *)(clone2 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + } + else { + hash = (long32At((void *)(obj2 + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(clone1 + 4),((((long32At((void *)(clone1 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + hash = (long32At((void *)(obj1 + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(clone2 + 4),((((long32At((void *)(clone2 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + } + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(obj1),((longAt((void *)(obj1))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(obj1),(longAt((void *)(obj1))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(obj1)); + assert(!(isOopForwarded(clone2))); + + /* begin isOldObject: */ + assert(isNonImmediate(obj1)); + if (oopisGreaterThanOrEqualTo(obj1, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(clone2 & (tagMask())))) + && (oopisLessThan(clone2, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj1 + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj1); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj1 + BaseHeaderSize) + (0U << (shiftForWord()))),clone2); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(obj1 + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(obj1 + (numSlotsFieldByteOffset())),1); + } + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(obj2),((longAt((void *)(obj2))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(obj2),(longAt((void *)(obj2))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(obj2)); + assert(!(isOopForwarded(clone1))); + + /* begin isOldObject: */ + assert(isNonImmediate(obj2)); + if (oopisGreaterThanOrEqualTo(obj2, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(clone1 & (tagMask())))) + && (oopisLessThan(clone1, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj2 + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj2); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj2 + BaseHeaderSize) + (0U << (shiftForWord()))),clone1); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(obj2 + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(obj2 + (numSlotsFieldByteOffset())),1); + } + if ((((/* begin isYoungObject: */ + assert(isNonImmediate(obj1)), + oopisLessThan(obj1, GIV(oldSpaceStart)))) != ((/* begin isYoungObject: */ + assert(isNonImmediate(clone2)), + oopisLessThan(clone2, GIV(oldSpaceStart))))) + || (((/* begin isYoungObject: */ + assert(isNonImmediate(obj2)), + oopisLessThan(obj2, GIV(oldSpaceStart)))) != ((/* begin isYoungObject: */ + assert(isNonImmediate(clone1)), + oopisLessThan(clone1, GIV(oldSpaceStart)))))) { + GIV(becomeEffectsFlags) = GIV(becomeEffectsFlags) | OldBecameNewFlag; + } +} + + +/* Attempt to pin objOop, which must not be immediate. + If the attempt succeeds answer objOop's (possibly moved) oop. + If the attempt fails, which can only occur if there is no memory, answer + 0. */ + + /* SpurMemoryManager>>#pinObject: */ +sqInt +pinObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt oldClone; + sqInt referent; + SpurSegmentInfo *seg; + + assert(isNonImmediate(objOop)); + assert(!((isForwarded(objOop)))); + + /* We choose to clone to keep pinned objects together to reduce fragmentation, + if the object is not too large, assuming that pinning is rare and that fragmentation is a bad thing. + Too large is defined as over 1mb. The size of a 640x480x4 bitmap is 1228800. */ + + /* begin isOldObject: */ + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if ((numBytesOf(objOop)) > (0x100000)) { + setIsPinnedOfto(objOop, 1); + return objOop; + } + seg = segmentContainingObj(objOop); + if ((seg->containsPinned)) { + setIsPinnedOfto(objOop, 1); + return objOop; + } + + /* begin someSegmentContainsPinned */ + for (i = 0; i < GIV(numSegments); i += 1) { + if (((GIV(segments)[i]).containsPinned)) { + goto l1; + } + } + setIsPinnedOfto(objOop, 1); + (seg->containsPinned = 1); + return objOop; +l1:; + } + oldClone = cloneInOldSpaceforPinning(objOop, 1); + if (oldClone) { + GIV(becomeEffectsFlags) = becomeEffectFlagsFor(objOop); + setIsPinnedOfto(oldClone, 1); + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(objOop),(longAt((void *)(objOop))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(objOop)); + assert(!(isOopForwarded(oldClone))); + + /* begin isOldObject: */ + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(oldClone & (tagMask())))) + && (oopisLessThan(oldClone, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),oldClone); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),1); + } + + /* begin followSpecialObjectsOop */ + if ((!((longAt((void *)(GIV(specialObjectsOop)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + GIV(validatedIntegerClassFlags) = 0; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(GIV(specialObjectsOop))); + referent = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + GIV(specialObjectsOop) = referent; + } + followForwardedObjectFieldstoDepth(GIV(specialObjectsOop), 0); + + /* begin postBecomeAction: */ + spurPostBecomeAction(GIV(becomeEffectsFlags)); + if (((GIV(becomeEffectsFlags) & BecameJittedCompiledMethodFlag) != 0)) { + freeBecomeFlaggedMethods(); + } + followMovableLiteralsAndUpdateYoungReferrers(); + + /* begin nilUncoggableMethods */ + GIV(lastCoggableInterpretedBlockMethod) = (GIV(lastUncoggableInterpretedBlockMethod) = null); + assert(kosherYoungReferrers()); + GIV(gcMode) = 0; + postBecomeScanClassTable(GIV(becomeEffectsFlags)); + GIV(becomeEffectsFlags) = 0; + } + return oldClone; +} + + /* SpurMemoryManager>>#popObjStack: */ +static NoDbgRegParms sqInt +popObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt myx; + sqInt nextPage; + sqInt top; + sqInt topx; + + eassert(isValidObjStack(objStack)); + topx = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + if (!topx) { + assert((fetchPointerofObject(ObjStackNextx, objStack)) == 0); + + /* for debugging markAndTrace: set (MarkStackRecord := OrderedCollection new) */ + return null; + } + topx -= 1; + top = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)((topx + ObjStackFixedSlots)) << (shiftForWord())))))); + assert(addressCouldBeOop(top)); + + /* for debugging markAndTrace: set (MarkStackRecord := OrderedCollection new) */ + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(objStack)) == (wordIndexableFormat())); + longAtput((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))),topx); + if ((topx == 0) + && (((nextPage = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))) != 0)) { + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(nextPage)) == (wordIndexableFormat())); + longAtput((void *)((nextPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),objStack); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(objStack)) == (wordIndexableFormat())); + longAtput((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord()))))),0); + myx = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))); + + /* begin updateRootOfObjStackAt:with: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(myx, GIV(hiddenRootsObj), nextPage)); + assert(isNonImmediate(GIV(hiddenRootsObj))); + if (oopisGreaterThanOrEqualTo(GIV(hiddenRootsObj), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(nextPage & (tagMask())))) + && (oopisLessThan(nextPage, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(hiddenRootsObj) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(hiddenRootsObj)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(myx) << (shiftForWord()))))),nextPage); + switch (myx) { + case MarkStackRootIndex: + GIV(markStack) = nextPage; + break; + case WeaklingStackRootIndex: + GIV(weaklingStack) = nextPage; + break; + case MournQueueRootIndex: + GIV(mournQueue) = nextPage; + break; + default: + error("Case not found and no otherwise clause"); + } + eassert(isValidObjStack(nextPage)); + } + else { + eassert(isValidObjStack(objStack)); + } + return top; +} + + +/* Pop and return the possibly remapped object from the remap buffer. + We support this excessence for compatibility with ObjectMemory. + Spur doesn't GC during allocation. */ + + /* SpurMemoryManager>>#popRemappableOop */ +sqInt +popRemappableOop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = GIV(remapBuffer)[GIV(remapBufferCount)]; + GIV(remapBufferCount) -= 1; + return oop; +} + + +/* Scan the class table post-become (iff an active class object was becommed) + to ensure no + forwarding pointers, and no unhashed classes exist in the class table. + + Note that one-way become can cause duplications in the class table. + So if pointer objects have been becommed, scan all pages in the class + table and eliminate ay entries that have a zero hash. + doBecome:and:copyHash: has + already ensured that any becomed class has been stored at the right index. */ + + /* SpurMemoryManager>>#postBecomeScanClassTable: */ +static NoDbgRegParms void +postBecomeScanClassTable(sqInt effectsFlags) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOrNil; + sqInt i; + sqInt j; + usqInt numSlots; + sqInt page; + sqInt referent; + sqInt toDoLimit; + + assert(validClassTableRootPages()); + if (!(((effectsFlags & BecamePointerObjectFlag) != 0))) { + return; + } + for (i = 0; i < GIV(numClassTablePages); i += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + assert(!(isForwarded(page))); + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(page)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(page + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(page - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNil = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNil != GIV(nilObj)) { + if ((!((longAt((void *)(classOrNil))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(classOrNil)); + referent = longAt((void *)((classOrNil + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + classOrNil = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(j, page, classOrNil)); + assert(isNonImmediate(page)); + if (oopisGreaterThanOrEqualTo(page, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(classOrNil & (tagMask())))) + && (oopisLessThan(classOrNil, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(page + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(page); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord()))))),classOrNil); + } + if (!((long32At((void *)(classOrNil + 4))) & (identityHashHalfWordMask()))) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(page)) + && (!(isForwarded(page)))); + assert(validStorePointerUncheckedArgs(j, page, GIV(nilObj))); + longAtput((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord()))))),GIV(nilObj)); + + /* If the removed class is before the classTableIndex, set the + classTableIndex to point to the empty slot so as to reuse it asap. */ + if ((((((usqInt)(i) << (classTableMajorIndexShift())))) + j) < GIV(classTableIndex)) { + GIV(classTableIndex) = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + } + } + } + } + } + + /* classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + assert(GIV(classTableIndex) >= (1U << (classTableMajorIndexShift()))); +} + + /* SpurMemoryManager>>#primitiveErrorTable */ +sqInt +primitiveErrorTable(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(PrimitiveErrorTableIndex) << (shiftForWord())))))); +} + + +/* Scan the heap printing the oops of any and all contexts that refer to + anOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printActivationsOf: */ +void +printActivationsOf(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (aMethodObj == (longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord()))))))))) { + printHex(objSqInt); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + print(" pc "); + printHex(longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))))); + cr(); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printBogons */ +void +printBogons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin printTheBogons: */ + print("bogon "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)GIV(bogon))); + cr(); + print("anomaly "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)GIV(anomaly))); + cr(); +} + + +/* Scan the heap printing the oops of any and all contexts that refer to + anOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printContextReferencesTo: */ +void +printContextReferencesTo(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt i; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + i = CtxtTempFrameStart + (fetchStackPointerOf(objSqInt)); + while (((i -= 1)) >= 0) { + if (anOop == (longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + printHex(objSqInt); + print(" @ "); + printNum(i); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + print(" pc "); + printHex(longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))))); + cr(); + i = 0; + } + } + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printEntity: */ +void +printEntity(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt n; + sqInt printFlags; + + printFlags = 0; + printHex(oop); + + /* begin space */ + printChar(' '); + if (!(addressCouldBeObj(oop))) { + print((((oop & (tagMask())) != 0) + ? "immediate" + : "unknown")); + return; + } + print((((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun()) + ? "free" + : (((longAt((void *)(oop))) & (classIndexMask())) == (segmentBridgePun()) + ? "bridge" + : ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? "forwarder" + : (((longAt((void *)(oop))) & (classIndexMask())) <= (lastClassIndexPun()) + ? ((printFlags = 1), + "pun/obj stack") + : ((printFlags = 1), + "object")))))); + + /* begin space */ + printChar(' '); + n = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + + /* begin printHexnpnp: */ + fprintf(GIV(transcript), + "%" PRIxSQINT "", + n); + print("/"); + n = bytesInBody(oop); + + /* begin printHexnpnp: */ + fprintf(GIV(transcript), + "%" PRIxSQINT "", + n); + print("/"); + printNum(bytesInBody(oop)); + if (printFlags) { + /* begin space */ + printChar(' '); + print((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 15 + ? "f:0" + : "f:")); + n = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin printHexnpnp: */ + fprintf(GIV(transcript), + "%" PRIxSQINT "", + n); + print(((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (greyBitByteShift())) + ? " g" + : " .")); + print(((((usqInt)((byteAt((void *)(oop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1 + ? "i" + : ".")); + print(((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (markedBitByteShift())) + ? "m" + : ".")); + print(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift())) + ? "p" + : ".")); + print(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())) + ? "r" + : ".")); + } + cr(); +} + + +/* Actually print all objects that have ephemeron format. */ + + /* SpurMemoryManager>>#printEphemerons */ +void +printEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt format; + sqInt nSoFar; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + nSoFar = 0; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + format = (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask()); + if (format == (ephemeronFormat())) { + printf("0x%" PRIxSQINT "", obj); + if (((nSoFar += 1)) >= 4) { + nSoFar = 0; + cr(); + } + else { + /* begin space */ + printChar(' '); + } + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + if (nSoFar > 0) { + cr(); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printForwarders */ +void +printForwarders(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == (isForwardedObjectClassIndexPun())) { + printHex(objOopSqInt); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOopSqInt, GIV(freeStart))) { + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == (isForwardedObjectClassIndexPun())) { + printHex(objOopSqInt); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOopSqInt = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3:; +} + + /* SpurMemoryManager>>#printForwarder:on: */ +static NoDbgRegParms void +printForwarderon(sqInt oop, FILE *aStream) +{ + fprintf(aStream, + "%p is a forwarded hdr%d slot size %" PRIuSQINT " object to %p\n", + ((void *)oop), + ((byteAt((void *)(oop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? 16 + : 8), + numSlotsOfAny(oop), + ((void *)(followForwarded(oop)))); +} + + +/* This version goes through memory, printing all free chunks. + Other versions go through the free lists. This one will show + all free chunks even if the free lists are corrupt. */ +/* useful for debugging */ + + /* SpurMemoryManager>>#printFreeChunks */ +void +printFreeChunks(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + sqInt seenNewFreeChunk; + usqInt start; + + seenNewFreeChunk = 0; + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l2; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + if (!seenNewFreeChunk) { + print("NewSpace CONTAINS FREE OBJECT(S)!!"); + cr(); + seenNewFreeChunk = 1; + } + printFreeChunk(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + /* end allNewSpaceEntitiesDo: */ +l2: + + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + printFreeChunk(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3:; + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printFreeChunk: */ +void +printFreeChunk(sqInt freeChunk) +{ + printFreeChunkprintAsTreeNode(freeChunk, 1); +} + + /* SpurMemoryManager>>#printFreeChunk:printAsTreeNode: */ +static NoDbgRegParms void +printFreeChunkprintAsTreeNode(sqInt freeChunk, sqInt printAsTreeNode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt n; + usqInt numBytes; + + numBytes = bytesInBody(freeChunk); + print("freeChunk "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)freeChunk)); + if (printAsTreeNode) { + print((freeChunk == (GIV(freeLists)[0]) + ? " + " + : " - ")); + n = addressAfter(freeChunk); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + } + print(" bytes "); + printNum(numBytes); + print(" next "); + n = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + if (!(isLilliputianSize(numBytes))) { + print(" prev "); + n = longAt((void *)((freeChunk + BaseHeaderSize) + (1U << (shiftForWord())))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + } + if ((numBytes >= 0x200 /* (numFreeLists * #allocationUnit) */) + && (printAsTreeNode)) { + print(" ^ "); + n = longAt((void *)((freeChunk + BaseHeaderSize) + (2U << (shiftForWord())))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + print(" < "); + n = longAt((void *)((freeChunk + BaseHeaderSize) + (3U << (shiftForWord())))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + print(" > "); + n = longAt((void *)((freeChunk + BaseHeaderSize) + (4U << (shiftForWord())))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + } + cr(); +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printFreeListHeads */ +void +printFreeListHeads(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt expectedMask; + sqInt i; + + expectedMask = 0; + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + printHex(GIV(freeLists)[i]); + if (GIV(freeLists)[i]) { + expectedMask += 1ULL << i; + } + if ((i + 1) % (4)) { + print(" "); + } + else { + cr(); + } + } + cr(); + print("mask: "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)GIV(freeListsMask))); + print(" expected: "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)expectedMask)); + cr(); +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printFreeList: */ +sqInt +printFreeList(sqInt chunkOrIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt freeChunk; + + if ((chunkOrIndex >= 0) + && (chunkOrIndex < 64 /* numFreeLists */)) { + return printFreeList(GIV(freeLists)[chunkOrIndex]); + } + freeChunk = chunkOrIndex; + while (freeChunk != 0) { + printFreeChunk(freeChunk); + freeChunk = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + } + return 0; +} + + /* SpurMemoryManager>>#printFreeObject:on: */ +static NoDbgRegParms void +printFreeObjecton(sqInt oop, FILE *aStream) +{ + fprintf(aStream, + "%p is a free chunk of size %" PRIdSQINT " 0th field:%p\n", + ((void *)oop), + bytesInBody(oop), + ((void *)(longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord()))))))); +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printFreeTree */ +void +printFreeTree(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + printFreeTreeChunk(GIV(freeLists)[0]); +} + + +/* Slang is blind-sided by the inlining of printFreeTreeChunk: into + printFreeTree. + */ + + /* SpurMemoryManager>>#printFreeTreeChunk: */ +static NoDbgRegParms void +printFreeTreeChunk(sqInt chunkOrZero) +{ + if (chunkOrZero > 0) { + printFreeTreeChunk(longAt((void *)((chunkOrZero + BaseHeaderSize) + (3U << (shiftForWord()))))); + printFreeChunk(chunkOrZero); + printFreeTreeChunk(longAt((void *)((chunkOrZero + BaseHeaderSize) + (4U << (shiftForWord()))))); + } +} + + +/* useful for VM debugging */ +/* N.B. No safety bounds checks!! We need to look e.g. at corpses. */ + + /* SpurMemoryManager>>#printHeaderOf: */ +void +printHeaderOf(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt n; + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)objOop)); + if ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) { + print(" hdr16 slotf "); + n = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + print(" slotc "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)(((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8))); + + /* begin space */ + printChar(' '); + } + else { + print(" hdr8 slots "); + n = numSlotsOfAny(objOop); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + } + printChar(' '); + printChar(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift())) + ? 'M' + : 'm')); + printChar(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (greyBitByteShift())) + ? 'G' + : 'g')); + printChar(((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift())) + ? 'P' + : 'p')); + printChar(((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())) + ? 'R' + : 'r')); + printChar(((((usqInt)((byteAt((void *)(objOop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1 + ? 'I' + : 'i')); + print(" hash "); + n = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + print(" fmt "); + n = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + print(" cidx "); + n = (longAt((void *)(objOop))) & (classIndexMask()); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + cr(); +} + + /* SpurMemoryManager>>#printImmediateObject:on: */ +static NoDbgRegParms void +printImmediateObjecton(sqInt oop, FILE *aStream) +{ + assert(isImmediate(oop)); + if ((((oop) & 7) == 1)) { + fprintf(aStream, + "%p=%" PRIdSQINT "\n", + ((void *)oop), + ((sqInt)((oop >> 3)))); + } + if (((oop & (characterTag())) != 0)) { + fprintf(aStream, + "%p=$%ld ($%lc)\n", + ((void *)oop), + ((long)(((((usqInt)oop))) >> (numTagBits()))), + ((wint_t) (((((usqInt)oop))) >> (numTagBits())))); + } + if (((oop & (smallFloatTag())) != 0)) { + fprintf(aStream, + "%p=%g\n", + ((void *)oop), + floatValueOf(oop)); + } +} + + +/* Scan the heap printing the oops of any and all objects that are instances + of aClassOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printInstancesOf: */ +void +printInstancesOf(sqInt aClassOop) +{ + int classIndex; + + classIndex = (long32At((void *)(aClassOop + 4))) & (identityHashHalfWordMask()); + if (classIndex != (isFreeObjectClassIndexPun())) { + printInstancesWithClassIndex(classIndex); + } +} + + +/* Scan the heap printing the oops of any and all objects whose classIndex + equals the argument. + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printInstancesWithClassIndex: */ +void +printInstancesWithClassIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + printHex(objOop); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + printHex(objOop); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3:; +} + + +/* Print the objects in the classTable that have bad hashes. */ +/* useful for debugging */ + + /* SpurMemoryManager>>#printInvalidClassTableEntries */ +void +printInvalidClassTableEntries(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOrNilSqInt; + int hash; + sqInt i; + sqInt index; + sqInt j; + sqInt page; + sqInt toDoLimit; + + hash = 0; + if (!(validClassTableRootPages())) { + print("class table invalid; cannot print"); + cr(); + return; + } + + /* begin classTableEntriesDo: */ + for (i = 0; i < GIV(numClassTablePages); i += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNilSqInt = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNilSqInt != GIV(nilObj)) { + index = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + if (((!((longAt((void *)(classOrNilSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + || ((((hash = (long32At((void *)(classOrNilSqInt + 4))) & (identityHashHalfWordMask()))) == 0) + || ((noCheckClassAtIndex(hash)) != classOrNilSqInt))) { + print("entry "); + printHex(index); + print(" oop "); + printHex(classOrNilSqInt); + print(" hash "); + printHex(hash); + print(" => "); + printHex(classAtIndex(hash)); + cr(); + } + } + } + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printMarkedOops */ +#if LLDB +void +printMarkedOops(void) +{ + printOopsSuchThat(isMarked); +} +#endif /* LLDB */ + + +/* Scan the heap printing the oops of any and all methods that implement + anOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printMethodImplementorsOf: */ +void +printMethodImplementorsOf(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && ((maybeSelectorOfMethod(objSqInt)) == anOop)) { + printHex(objSqInt); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + cr(); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + +/* Scan the heap printing the oops of any and all methods that refer to anOop */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printMethodReferencesTo: */ +void +printMethodReferencesTo(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt i; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + i = ((literalCountOf(objSqInt)) + LiteralStart) - 1; + while (((i -= 1)) >= 0) { + if (anOop == (longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + printHex(objSqInt); + print(" @ "); + printNum(i); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + cr(); + i = 0; + } + } + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + /* SpurMemoryManager>>#printMournQueue */ +void +printMournQueue(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt iSqInt; + sqInt mourner; + sqInt nSoFar; + sqInt numOnThisPage; + sqInt objStackPage; + + nSoFar = 0; + + /* begin objStack:do: */ + if (GIV(mournQueue) == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(GIV(mournQueue))); + objStackPage = GIV(mournQueue); + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + mourner = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + format = (((mourner & (tagMask())) != 0) + ? forwardedFormat() + : (byteAt((void *)(mourner + (formatFieldByteOffset())))) & (formatMask())); + fprintf(GIV(transcript), + "%p%s", + ((void *)mourner), + (format == (weakArrayFormat()) + ? " weak " + : (format == (ephemeronFormat()) + ? " eph?!" + : (format == (nonIndexablePointerFormat()) + ? " fired" + : " ???? ")))); + if (((nSoFar += 1)) >= 4) { + nSoFar = 0; + cr(); + } + else { + /* begin space */ + printChar(' '); + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l1: + if (nSoFar > 0) { + cr(); + } +} + + +/* useful for debugging */ + + /* SpurMemoryManager>>#printNewObjectsInObjStack: */ +void +printNewObjectsInObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt iSqInt; + sqInt n; + sqInt numOnThisPage; + sqInt obj; + sqInt objStackPage; + + n = 0; + + /* begin objStack:do: */ + if (objStack == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(objStack)); + objStackPage = objStack; + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + obj = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (/* isInNewSpace: */ + (oopisLessThan(obj, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(obj, GIV(newSpaceStart)))) { + /* begin space */ + printChar(' '); + printHex(obj); + if (!(((n += 1)) % 8)) { + cr(); + } + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l1: + if (n % 8) { + cr(); + } + fprintf(GIV(transcript), + "total %" PRIdSQINT "\n", + n); +} + + /* SpurMemoryManager>>#printNonPointerDataOf:on: */ +static NoDbgRegParms void +printNonPointerDataOfon(sqInt oop, FILE *aStream) +{ + sqInt elementsPerLine; + sqInt format; + sqInt index; + sqInt lastIndex; + usqInt numSlots; + usqInt numSlotsUsqInt; + + format = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert(((format >= (sixtyFourBitIndexableFormat())) && (format <= ((firstCompiledMethodFormat()) - 1)))); + + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (format <= (ephemeronFormat())) { + lastIndex = numSlots; + goto l1; + } + if (format >= (firstByteFormat())) { + lastIndex = ((numSlots << (shiftForWord()))) - (format & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (format >= (firstShortFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 1))) - (format & 3); + goto l1; + } + if (format >= (firstLongFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 2))) - (format & 1); + goto l1; + } + if (format == (sixtyFourBitIndexableFormat())) { + lastIndex = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + lastIndex = 0; + /* end lengthOf:format: */ +l1: + if (!lastIndex) { + return; + } + if (format == (sixtyFourBitIndexableFormat())) { + lastIndex = ((32 < lastIndex) ? 32 : lastIndex); + + /* 0x/16r0123456789ABCDEF x 4 = 76/80 */ + elementsPerLine = 4; + for (index = 1; index <= lastIndex; index += 1) { + fprintf(aStream, + "%18" PRIXSQINT "%c", + ((usqLong) (long64At((void *)((oop + BaseHeaderSize) + ((((usqInt)((index - 1)) << 3))))))), + (((index % elementsPerLine) == 0) + || (index == lastIndex) + ? '\n' + : ' ')); + } + return; + } + if (format < (firstShortFormat())) { + lastIndex = ((64 < lastIndex) ? 64 : lastIndex); + + /* 0x/16r12345678 x 8 = 80/88 */ + elementsPerLine = 8; + for (index = 1; index <= lastIndex; index += 1) { + fprintf(aStream, + "%10p%c", + ((void *)(((usqInt)(long32At((void *)((oop + BaseHeaderSize) + ((((usqInt)((index - 1)) << 2))))))))), + (((index % elementsPerLine) == 0) + || (index == lastIndex) + ? '\n' + : ' ')); + } + return; + } + if (format < (firstByteFormat())) { + lastIndex = ((128 < lastIndex) ? 128 : lastIndex); + + /* 0x/16r1234 x 10 = 70/80 */ + elementsPerLine = 10; + for (index = 1; index <= lastIndex; index += 1) { + fprintf(aStream, + "%6p%c", + ((void *)(shortAt((void *)((oop + BaseHeaderSize) + ((((usqInt)((index - 1)) << 1))))))), + (((index % elementsPerLine) == 0) + || (index == lastIndex) + ? '\n' + : ' ')); + } + return; + } + lastIndex = ((0x100 < lastIndex) ? 0x100 : lastIndex); + + /* 0x/16r12 x 16 = 80/96 */ + elementsPerLine = 16; + for (index = 1; index <= lastIndex; index += 1) { + fprintf(aStream, + "%4p%c", + ((void *)(byteAt((void *)((oop + BaseHeaderSize) + (index - 1))))), + (((index % elementsPerLine) == 0) + || (index == lastIndex) + ? '\n' + : ' ')); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printObjectsFrom:to: */ +void +printObjectsFromto(sqInt startAddress, sqInt endAddress) +{ + sqInt oop; + + oop = objectBefore(startAddress); + oop = (oop + ? ((objectAfter(oop)) == startAddress + ? startAddress + : oop) + : startAddress); + while (oopisLessThan(oop, endAddress)) { + if (!((((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) + || (((longAt((void *)(oop))) & (classIndexMask())) == (segmentBridgePun())))) { + printOop(oop); + } + oop = objectAfter(oop); + } +} + + +/* Scan the heap printing the oops of any and all objects whose hash equals + the argument. + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printObjectsWithHash: */ +void +printObjectsWithHash(sqInt hash) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((long32At((void *)(objOop + 4))) & (identityHashHalfWordMask())) == hash) { + shortPrintOop(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((long32At((void *)(objOop + 4))) & (identityHashHalfWordMask())) == hash) { + shortPrintOop(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3:; +} + + /* SpurMemoryManager>>#printObjStackAndContents: */ +void +printObjStackAndContents(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt total; + + /* begin printObjStack:printContents: */ + if (objStack == GIV(nilObj)) { + print("nil"); + cr(); + } + else { + total = printObjStackPagemyIndexpageTypeprintContents(objStack, longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))), ObjStackMyx, 1); + fprintf(GIV(transcript), + "total %" PRIdSQINT "\n", + total); + } +} + + +/* Print the page and all pages accessible from it. Answer the total number + of objects on all non free pages. + */ + + /* SpurMemoryManager>>#printObjStackPage:myIndex:pageType:printContents: */ +static NoDbgRegParms sqInt +printObjStackPagemyIndexpageTypeprintContents(sqInt objStackPage, sqInt myx, sqInt pageType, sqInt printContents) +{ + sqInt freeOrNextPage; + sqInt index; + int isFirstPage; + int isFreePage; + int isNextPage; + sqInt n; + sqInt page; + sqInt totalContents; + + isFirstPage = pageType == ObjStackMyx; + isNextPage = pageType == ObjStackNextx; + isFreePage = pageType == ObjStackFreex; + printObjStackPagemyIndextag(objStackPage, myx, (isFirstPage + ? "head" + : (isFreePage + ? "free" + : "next"))); + if (isFirstPage + || (isNextPage)) { + /* begin tab */ + printChar('\t'); + print("topx: "); + printNum(longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))); + print(" next: "); + printHex(longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord()))))))); + if (isFirstPage) { + print(" free: "); + printHex(longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))))); + } + cr(); + } + if (isFirstPage) { + freeOrNextPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + while (freeOrNextPage != 0) { + printObjStackPagemyIndexpageTypeprintContents(freeOrNextPage, myx, ObjStackFreex, 0); + page = longAt((void *)((freeOrNextPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + if ((page == freeOrNextPage) + || (page == objStackPage)) { + print("circularity in free page list!!"); + cr(); + page = 0; + } + freeOrNextPage = page; + } + } + freeOrNextPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + totalContents = (freeOrNextPage + ? printObjStackPagemyIndexpageTypeprintContents(freeOrNextPage, myx, ObjStackNextx, printContents) + : 0); + if (printContents) { + n = 0; + index = (longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) + ObjStackNextx; + while (index >= ObjStackFixedSlots) { + /* begin space */ + printChar(' '); + printHex(longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))))); + index -= 1; + if (((n += 1)) >= 8) { + cr(); + n = 0; + } + } + if (((longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) + ObjStackNextx) >= ObjStackFixedSlots) { + cr(); + } + } + return (isFreePage + ? 0 + : totalContents + (longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))))); +} + + /* SpurMemoryManager>>#printObjStackPage:myIndex:tag: */ +static NoDbgRegParms void +printObjStackPagemyIndextag(sqInt objStackPage, sqInt myx, char *pageType) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fprintf(GIV(transcript), + "%s %p cx %d (%d) fmt %d (%d) sz %d (%d) myx: %d (%d%s\n", + pageType, + ((void *)objStackPage), + ((int) ((longAt((void *)(objStackPage))) & (classIndexMask()))), + ((int) (sixtyFourBitLongsClassIndexPun())), + ((int) ((byteAt((void *)(objStackPage + (formatFieldByteOffset())))) & (formatMask()))), + ((int) (sixtyFourBitIndexableFormat())), + ((int) (numSlotsOfAny(objStackPage))), + ObjStackPageSlots, + ((int) (longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))))), + ((int) myx), + ((byteAt((void *)(objStackPage + (markBitsByteOffset())))) & (1U << (markedBitByteShift())) + ? ") mkd" + : ") unmkd")); +} + + +/* useful for debugging */ + + /* SpurMemoryManager>>#printObjStack: */ +void +printObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt total; + + /* begin printObjStack:printContents: */ + if (objStack == GIV(nilObj)) { + print("nil"); + cr(); + } + else { + total = printObjStackPagemyIndexpageTypeprintContents(objStack, longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))), ObjStackMyx, 0); + fprintf(GIV(transcript), + "total %" PRIdSQINT "\n", + total); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printOopsExcept: */ +NeverInline void +printOopsExcept(sqInt (*function)(sqInt)) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt n; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + n = 0; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (!(function(objOop))) { + n += 1; + printEntity(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (!(function(objOop))) { + n += 1; + printEntity(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3: + if (n > 4) { + printNum(n); + print(" objects"); + cr(); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printOopsFrom:to: */ +void +printOopsFromto(sqInt startAddress, sqInt endAddress) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firstNonEntity; + sqInt inEmptySpace; + sqInt lastNonEntity; + sqInt limit; + sqInt oop; + + firstNonEntity = 0; + oop = objectBefore(startAddress); + limit = (((((usqIntptr_t)endAddress)) < GIV(endOfMemory)) ? (((usqIntptr_t)endAddress)) : GIV(endOfMemory)); + oop = (oop + ? ((objectAfter(oop)) == startAddress + ? startAddress + : oop) + : startAddress); + inEmptySpace = 0; + while (oopisLessThan(oop, limit)) { + printEntity(oop); + while (1) { + oop = objectAfter(oop); + if (!((long64At((void *)(oop))) == 0)) break; + if (!inEmptySpace) { + inEmptySpace = 1; + firstNonEntity = oop; + } + lastNonEntity = oop; + } + if (inEmptySpace) { + inEmptySpace = 0; + print("skipped empty space from "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)firstNonEntity)); + print(" to "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)lastNonEntity)); + cr(); + oop = objectStartingAt(oop); + } + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printOopsSuchThat: */ +NeverInline void +printOopsSuchThat(sqInt (*function)(sqInt)) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt n; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + n = 0; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (function(objOop)) { + n += 1; + printEntity(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (function(objOop)) { + n += 1; + printEntity(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3: + if (n > 4) { + printNum(n); + print(" objects"); + cr(); + } +} + + +/* Scan the heap printing the oops of any and all objects that refer to anOop */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printReferencesTo: */ +void +printReferencesTo(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt contextSize; + sqInt fmt; + sqInt followingWord; + usqInt followingWordAddress; + sqInt header; + sqInt headerSqInt; + sqInt i; + usqInt numLiterals; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt sp; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l2; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objSqInt))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l2: + i = ((usqInt) (CtxtTempFrameStart + contextSize)); + goto l3; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objSqInt)) > (isForwardedObjectClassIndexPun())); + i = (((numSlots = byteAt((void *)(objSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l3; + } + if (fmt == (forwardedFormat())) { + i = 1; + goto l3; + } + if (fmt < (firstCompiledMethodFormat())) { + i = 0; + goto l3; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objSqInt)); + headerSqInt = longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + i = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l3: + while (((i -= 1)) >= 0) { + if (anOop == (longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + printHex(objSqInt); + print(" @ "); + printNum(i); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + cr(); + i = 0; + } + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } + if (isonObjStack(anOop, GIV(markStack))) { + print("on markStack"); + cr(); + } + if (isonObjStack(anOop, GIV(weaklingStack))) { + print("on weaklingStack"); + cr(); + } + if (isonObjStack(anOop, GIV(mournQueue))) { + print("in mournQueue"); + cr(); + } + if ((((byteAt((void *)(anOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0) + && (isInRememberedSet(anOop))) { + print("in rememberedSet"); + cr(); + } +} + + /* SpurMemoryManager>>#printStringDataOf:on: */ +static NoDbgRegParms void +printStringDataOfon(sqInt oop, FILE *aStream) +{ + char *buffer; + sqInt byte; + sqInt i; + sqInt limit; + sqInt n; + unsigned int *wideBuffer; + int word; + + if (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())) { + buffer = ((char *) (alloca(0x400))); + n = (i = 0); + limit = (((numBytesOfBytes(oop)) < 0x100) ? (numBytesOfBytes(oop)) : 0x100); + while (n < limit) { + byte = byteAt((void *)((oop + BaseHeaderSize) + i)); + i += 1; + if ((byte < 32) + && (byte != 9)) { + buffer[n] = '<'; + n += 1; + if ((byte == 10) + || (byte == 13)) { + if (byte == 10) { + buffer[n] = 'L'; + buffer[n + 1] = 'F'; + } + else { + buffer[n] = 'C'; + buffer[n + 1] = 'R'; + } + n += 2; + } + else { + if (byte >= 10) { + buffer[n] = ((byte / 10) + (((sqInt)'0'))); + n += 1; + } + buffer[n] = ((byte % 10) + (((sqInt)'0'))); + n += 1; + } + buffer[n] = '>'; + n += 1; + } + else { + buffer[n] = byte; + n += 1; + } + } + fprintf(aStream, + "%.*s%s\n", + ((int) n), + buffer, + ((numBytesOfBytes(oop)) > limit + ? "..." + : "")); + } + else { + assert(isWordsNonImm(oop)); + wideBuffer = ((unsigned int *) (alloca(0x1000))); + n = (i = 0); + limit = (((lengthOf(oop)) < 0x100) ? (lengthOf(oop)) : 0x100); + while (i < limit) { + word = long32At((void *)((oop + BaseHeaderSize) + ((((usqInt)(i) << 2))))); + i += 1; + if ((word < 32) + && (word != 9)) { + wideBuffer[n] = '<'; + n += 1; + if ((word == 10) + || (word == 13)) { + if (word == 10) { + wideBuffer[n] = 'L'; + wideBuffer[n + 1] = 'F'; + } + else { + wideBuffer[n] = 'C'; + wideBuffer[n + 1] = 'R'; + } + n += 2; + } + else { + if (word >= 10) { + wideBuffer[n] = ((word / 10) + (((sqInt)'0'))); + n += 1; + } + wideBuffer[n] = ((word % 10) + (((sqInt)'0'))); + n += 1; + } + wideBuffer[n] = '>'; + n += 1; + } + else { + wideBuffer[n] = word; + n += 1; + } + } + fwprintf(aStream, + L"%.*ls%s\n", + ((int) n), + wideBuffer, + ((lengthOf(oop)) > limit + ? "..." + : "")); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printUnmarkedOops */ +#if LLDB +void +printUnmarkedOops(void) +{ + printOopsExcept(isMarked); +} +#endif /* LLDB */ + + +/* Print all objects that have weak array format. */ + + /* SpurMemoryManager>>#printWeaklings */ +void +printWeaklings(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt format; + sqInt nSoFar; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + nSoFar = 0; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + format = (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask()); + if (format == (weakArrayFormat())) { + printf("0x%" PRIxSQINT "", obj); + if (((nSoFar += 1)) >= 4) { + nSoFar = 0; + cr(); + } + else { + /* begin space */ + printChar(' '); + } + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + if (nSoFar > 0) { + cr(); + } +} + + +/* Attempt to push anEphemeron on the unscanned ephemerons stack and answer + if the attempt succeeded. Note that the ephemeron stack overflowing isn't + a disaster; it simply means treating the ephemeron as strong in this GC + cycle. */ + + /* SpurMemoryManager>>#pushOnUnscannedEphemeronsStack: */ +static NoDbgRegParms sqInt +pushOnUnscannedEphemeronsStack(sqInt anEphemeron) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(isEphemeron(anEphemeron)); + if (((GIV(unscannedEphemerons).top)) >= ((GIV(unscannedEphemerons).limit))) { + return 0; + } + longAtput((void *)((GIV(unscannedEphemerons).top = ((GIV(unscannedEphemerons).top)) + BytesPerOop)),anEphemeron); + return 1; +} + + +/* Record the given object in a the remap buffer. Objects in this buffer are + remapped when a compaction occurs. This facility is used by the + interpreter to ensure that + objects in temporary variables are properly remapped. + We support this excessence for compatibility with ObjectMemory. + Spur doesn't GC during allocation. */ + + /* SpurMemoryManager>>#pushRemappableOop: */ +void +pushRemappableOop(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(addressCouldBeOop(oop)); + GIV(remapBuffer)[(GIV(remapBufferCount) += 1)] = oop; + if (!(GIV(remapBufferCount) <= RemapBufferSize)) { + error("remapBuffer overflow"); + } +} + + +/* Add the ephemeron or weak array to the queue. */ +/* If an ephemeron it shouldn't be added twice. */ + + /* SpurMemoryManager>>#queueMourner: */ +static NoDbgRegParms void +queueMourner(sqInt anEphemeronOrWeakArray) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt objStack; + + assert(!((((formatOf(anEphemeronOrWeakArray)) != (weakArrayFormat())) + && (isonObjStack(anEphemeronOrWeakArray, GIV(mournQueue)))))); + + /* it should be a weak array or an ephemeron */ + assert((isNonImmediate(anEphemeronOrWeakArray)) + && (((formatOf(anEphemeronOrWeakArray)) == (ephemeronFormat())) + || ((formatOf(anEphemeronOrWeakArray)) == (weakArrayFormat())))); + ensureRoomOnObjStackAt(MournQueueRootIndex); + + /* There is no point queueing weak arrays more than once. Note that it should be impossible + for ephemerons to be enqueued more than once since they are turned into non-ephemerons + in the relevant sender. */ + + /* begin isEphemeron: */ + assert(isNonImmediate(anEphemeronOrWeakArray)); + format = (byteAt((void *)(anEphemeronOrWeakArray + (formatFieldByteOffset())))) & (formatMask()); + if (!(format == (ephemeronFormat()))) { + if (isonObjStack(anEphemeronOrWeakArray, GIV(mournQueue))) { + return; + } + } + objStack = GIV(mournQueue); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(anEphemeronOrWeakArray)); + if (((anEphemeronOrWeakArray & (tagMask())) != 0)) { + assert(objStack == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack)) + ? fetchPointerofObject(ObjStackNextx, objStack) + : objStack)))); + } + else { + assert(!((objStack == GIV(markStack)) + && (isWeakNonImm(anEphemeronOrWeakArray)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack != GIV(weaklingStack)) + || (isWeakNonImm(anEphemeronOrWeakArray))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(anEphemeronOrWeakArray, objStack); +} + + /* SpurMemoryManager>>#rawHashBitsOf: */ +int +rawHashBitsOf(sqInt objOop) +{ + return (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()); +} + + +/* Answer the tag bits for the receiver based on the method's methodClass, if + any. These bits are extended with a bit that says that the method may have + a Context receiver, i.e. + mclass is Context or its superclasses. The absence of this bit is used to + avoid expensive store checks, etc. */ + + /* SpurMemoryManager>>#receiverTagBitsForMethod: */ +sqInt +receiverTagBitsForMethod(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classContextOrSuperclass; + sqInt literal; + sqInt methodClassOrNil; + sqInt objOop; + sqInt offset; + + /* begin methodClassOf: */ + offset = (literalCountOf(aMethodObj)) - 1; + literal = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = fixFollowedFieldofObjectwithInitialValue(offset + LiteralStart, aMethodObj, literal); + } + methodClassOrNil = ((literal != GIV(nilObj)) + && (/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + ? (assert((numSlotsOf(literal)) > ValueIndex), + followFieldofObject(ValueIndex, literal)) + : GIV(nilObj)); + if (methodClassOrNil == GIV(nilObj)) { + return (1U << (numTagBits())); + } + + /* If we don't know the methodClass be pessimal + Mixins don't necessarily have a format inst var; filter out non-integer format. */ + if (((((usqInt)((((longAt((void *)((methodClassOrNil + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) == (forwardedFormat())) { + /* begin receiverTagBitsForImmediateMethodClass: */ + if (methodClassOrNil == (longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)((smallIntegerTag())) << (shiftForWord())))))))) { + return smallIntegerTag(); + } + if (methodClassOrNil == (longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)((characterTag())) << (shiftForWord())))))))) { + return characterTag(); + } + assert(methodClassOrNil == (fetchPointerofObject(smallFloatTag(), GIV(classTableFirstPage)))); + return smallFloatTag(); + } + + /* Now check if mclass is one of Context's superclasses... */ + + /* begin knownClassAtIndex: */ + assert(((ClassMethodContextCompactIndex >= 1) && (ClassMethodContextCompactIndex <= (classTablePageSize())))); + classContextOrSuperclass = longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassMethodContextCompactIndex) << (shiftForWord())))))); + do { + if (classContextOrSuperclass == methodClassOrNil) { + return (1U << (numTagBits())); + } + + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classContextOrSuperclass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classContextOrSuperclass, objOop); + } + classContextOrSuperclass = objOop; + } while(classContextOrSuperclass != GIV(nilObj)); + return 0; +} + + +/* Ephemerons in the mourn queue will have been fired (had their + ephemeron-ness turned off), + but the ephemeron queue is not saved in the snapshot. So the snapshotted + ephemerons need to be unfired so they're still unqueued ephemerons in the + loaded image. + This must be undone post snapshot. */ + + /* SpurMemoryManager>>#refireQueuedEphemeronsPostSnapshot */ +static void +refireQueuedEphemeronsPostSnapshot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt iSqInt; + sqInt mourner; + sqInt numOnThisPage; + sqInt objStackPage; + + /* begin objStack:do: */ + if (GIV(mournQueue) == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(GIV(mournQueue))); + objStackPage = GIV(mournQueue); + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + mourner = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (((!(mourner & (tagMask())))) + && (((byteAt((void *)(mourner + (formatFieldByteOffset())))) & (formatMask())) == (ephemeronFormat()))) { + /* begin setFormatOf:to: */ + assert((((nonIndexablePointerFormat()) >= 0) && ((nonIndexablePointerFormat()) <= (formatMask())))); + byteAtput((void *)(mourner + (formatFieldByteOffset())),((byteAt((void *)(mourner + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (nonIndexablePointerFormat())); + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l1:; +} + + +/* Relocate all objStack pages that comprise objStack, including contents if + requested. + */ + + /* SpurMemoryManager>>#relocateObjStackForPlanningCompactor:andContents: */ +static NoDbgRegParms sqInt +relocateObjStackForPlanningCompactorandContents(sqInt objStack, sqInt relocateContents) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt finishIndex; + sqInt freeList; + sqInt fwd; + sqInt i; + sqInt next; + sqInt oop; + sqInt relocated; + sqInt result; + sqInt stackOrNil; + + next = 0; + if (objStack == GIV(nilObj)) { + return objStack; + } + assert((relocateContents + ? (rawHashBitsOf(objStack)) < ObjStackLimit + : (rawHashBitsOf(objStack)) == 0)); + stackOrNil = objStack; + freeList = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + while (1) { + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + + /* There are four fixed slots in an obj stack, and a Topx of 0 indicates empty, so + if there were 5 slots in an oop stack, full would be 2, and the last 0-rel index is 4. + Hence the last index is topx + fixed slots - 1, or topx + ObjStackNextx. The first + two slots, Topx and Myx are not object references. */ + next = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + finishIndex = ObjStackNextx + ((relocateContents + ? (long32At((void *)(stackOrNil + 4))) & (identityHashHalfWordMask()) + : 0)); + + /* begin relocateObjectsInHeapEntity:from:to: */ + for (i = ObjStackFreex; i <= finishIndex; i += 1) { + oop = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert(isMarked(oop)); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stackOrNil)) + && (!(isForwarded(stackOrNil)))); + assert(validStorePointerUncheckedArgs(i, stackOrNil, fwd)); + longAtput((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + relocated = (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(stackOrNil, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(stackOrNil + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)) + ? longAt((void *)((stackOrNil + BaseHeaderSize) + (0U << (shiftForWord())))) + : stackOrNil); + if (stackOrNil == objStack) { + result = relocated; + } + if (!(next != 0)) break; + stackOrNil = next; + } + while (freeList != 0) { + assert((numSlotsOfAny(freeList)) == ObjStackPageSlots); + next = longAt((void *)((freeList + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + + /* begin relocateObjectsInHeapEntity:from:to: */ + for (i = ObjStackFreex; i <= ObjStackFreex; i += 1) { + oop = longAt((void *)((freeList + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert(isMarked(oop)); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(freeList)) + && (!(isForwarded(freeList)))); + assert(validStorePointerUncheckedArgs(i, freeList, fwd)); + longAtput((void *)((freeList + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + if (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(freeList, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(freeList + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0))) { + /* fetchPointer:ofObject: */ + longAt((void *)((freeList + BaseHeaderSize) + (0U << (shiftForWord())))); + } + else { + } + freeList = next; + } + return relocated; +} + + +/* Remove the given variable location to the extra roots table. */ + + /* SpurMemoryManager>>#removeGCRoot: */ +sqInt +removeGCRoot(sqInt *varLoc) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int i; + + for (i = 1; i <= GIV(extraRootCount); i++) { + if (varLoc == (GIV(extraRoots)[i])) { + GIV(extraRoots)[i] = (GIV(extraRoots)[GIV(extraRootCount)]); + GIV(extraRootCount) -= 1; + return 1; + } + } + return 0; +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. */ + + /* SpurMemoryManager>>#return:restoringObjectsIn:savedFirstFields:and:savedHashes: */ +static NoDbgRegParms sqInt +returnrestoringObjectsInsavedFirstFieldsandsavedHashes(sqInt errCode, sqInt reachableObjectsArray, sqInt savedFirstFields, sqInt outPointersArray, sqInt savedHashes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int hash; + sqInt i; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt toDoLimit; + sqInt valuePointer; + + /* begin restoreObjectsIn:upTo:savedFirstFields: */ + /* begin numSlotsOf: */ + assert((classIndexOf(reachableObjectsArray)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(reachableObjectsArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(reachableObjectsArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + for (i = 0; i < numSlots; i += 1) { + oop = longAt((void *)((reachableObjectsArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + valuePointer = longAt((void *)((savedFirstFields + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(oop)) + && (!(isForwarded(oop)))); + assert(validStorePointerUncheckedArgs(0, oop, valuePointer)); + longAtput((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + if (oopisGreaterThanOrEqualToandLessThan(savedFirstFields, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedFirstFields); + } + + /* begin restoreObjectsIn:savedHashes: */ + toDoLimit = ((assert((classIndexOf(outPointersArray)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(outPointersArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointersArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + if (((hash = long32At((void *)((savedHashes + BaseHeaderSize) + ((((usqInt)(i) << 2))))))) > (identityHashHalfWordMask())) { + if (oopisGreaterThanOrEqualToandLessThan(savedHashes, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedHashes); + } + goto l1; + } + oop = longAt((void *)((outPointersArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(oop + 4),((((long32At((void *)(oop + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + if (oopisGreaterThanOrEqualToandLessThan(savedHashes, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedHashes); + } + /* end restoreObjectsIn:savedHashes: */ +l1: + freeObject(reachableObjectsArray); + runLeakCheckerFor(GCModeImageSegment); + assert(allObjectsUnmarked()); + return errCode; +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. */ + + /* SpurMemoryManager>>#return:restoringObjectsIn:upTo:savedFirstFields: */ +static NoDbgRegParms sqInt +returnrestoringObjectsInupTosavedFirstFields(sqInt errCode, sqInt reachableObjectsArray, sqInt limitOrTag, sqInt savedFirstFields) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt valuePointer; + + /* begin restoreObjectsIn:upTo:savedFirstFields: */ + numSlots = (limitOrTag == -1 + ? (/* begin numSlotsOf: */ + assert((classIndexOf(reachableObjectsArray)) > (isForwardedObjectClassIndexPun())), + (((numSlotsUsqInt = byteAt((void *)(reachableObjectsArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(reachableObjectsArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt)) + : limitOrTag); + for (i = 0; i < numSlots; i += 1) { + oop = longAt((void *)((reachableObjectsArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + valuePointer = longAt((void *)((savedFirstFields + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(oop)) + && (!(isForwarded(oop)))); + assert(validStorePointerUncheckedArgs(0, oop, valuePointer)); + longAtput((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + if (oopisGreaterThanOrEqualToandLessThan(savedFirstFields, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedFirstFields); + } + freeObject(reachableObjectsArray); + runLeakCheckerFor(GCModeImageSegment); + assert(allObjectsUnmarked()); + return errCode; +} + + +/* This exists to get around a compiler bug in Apple LLVM version 7.0.0 + (clang-700.1.76) that was avoiding the second comparison of segVersion + after the first byte swap. */ + + /* SpurMemoryManager>>#reverseBytesIn32BitWordsIn: */ +static NoDbgRegParms NeverInline void +reverseBytesIn32BitWordsIn(sqInt segmentWordArray) +{ + sqInt addr; + sqInt stopAddr; + + stopAddr = addressAfter(segmentWordArray); + + /* begin reverseBytesIn32BitWordsFrom:to: */ + addr = segmentWordArray + BaseHeaderSize; + while (oopisLessThan(addr, stopAddr)) { + long32Atput((void *)(addr),SQ_SWAP_4_BYTES((long32At((void *)(addr))))); + addr += 4; + } +} + + +/* Check free space integrity by setting bits in the map corresponding to all + free space objects + and checking tat no pointer field refers to a free object. anOopOrNil is + provided to filter-out + the as-yet-to-be initialized object in + primitiveShallowCopy/primitiveClone. + */ + + /* SpurMemoryManager>>#runLeakCheckerForFreeSpace:ignoring: */ +static NoDbgRegParms void +runLeakCheckerForFreeSpaceignoring(sqInt gcModes, sqInt anOopOrNil) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (((gcModes & GCCheckFreeSpace) != 0)) { + reverseDisplayFromto(16, 19); + clearLeakMapAndMapAccessibleFreeSpace(); + GIV(freeSpaceCheckOopToIgnore) = anOopOrNil; + asserta(checkHeapFreeSpaceIntegrity()); + GIV(freeSpaceCheckOopToIgnore) = null; + } +} + + /* SpurMemoryManager>>#runLeakCheckerFor: */ +static NoDbgRegParms void +runLeakCheckerFor(sqInt gcModes) +{ + /* begin inLineRunLeakCheckerFor:excludeUnmarkedObjs:classIndicesShouldBeValid: */ + if (((gcModes & checkForLeaks) != 0)) { + if (((gcModes & GCModeFull) != 0)) { + reverseDisplayFromto(0, 7); + } + else { + reverseDisplayFromto(8, 15); + } + clearLeakMapAndMapAccessibleObjects(); + asserta(checkHeapIntegrityclassIndicesShouldBeValid(0, 1)); + asserta((checkInterpreterIntegrity()) == 0); + asserta(checkStackIntegrity()); + asserta(checkCodeIntegrity(gcModes)); + if (((gcModes & GCCheckFreeSpace) != 0)) { + clearLeakMapAndMapAccessibleFreeSpace(); + asserta(checkHeapFreeSpaceIntegrity()); + } + } + return; +} + + /* SpurMemoryManager>>#runLeakCheckerFor:excludeUnmarkedObjs:classIndicesShouldBeValid: */ +static NoDbgRegParms void +runLeakCheckerForexcludeUnmarkedObjsclassIndicesShouldBeValid(sqInt gcModes, sqInt excludeUnmarkedObjs, sqInt classIndicesShouldBeValid) +{ + /* begin inLineRunLeakCheckerFor:excludeUnmarkedObjs:classIndicesShouldBeValid: */ + if (((gcModes & checkForLeaks) != 0)) { + if (((gcModes & GCModeFull) != 0)) { + reverseDisplayFromto(0, 7); + } + else { + reverseDisplayFromto(8, 15); + } + clearLeakMapAndMapAccessibleObjects(); + asserta(checkHeapIntegrityclassIndicesShouldBeValid(excludeUnmarkedObjs, classIndicesShouldBeValid)); + asserta((checkInterpreterIntegrity()) == 0); + asserta(checkStackIntegrity()); + asserta(checkCodeIntegrity(gcModes)); + if (((gcModes & GCCheckFreeSpace) != 0)) { + clearLeakMapAndMapAccessibleFreeSpace(); + asserta(checkHeapFreeSpaceIntegrity()); + } + } +} + + /* SpurMemoryManager>>#safePrintStringOf: */ +static NoDbgRegParms void +safePrintStringOf(sqInt oop) +{ + printStringOf(/* followMaybeForwarded: */ + (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + ? followForwarded(oop) + : oop)); + return; +} + + +/* Run the scavenger. */ + + /* SpurMemoryManager>>#scavengingGCTenuringIf: */ +static NoDbgRegParms void +scavengingGCTenuringIf(sqInt tenuringCriterion) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt n; + sqInt probe; + + assert(GIV(remapBufferCount) == 0); + if (!(asserta(((((GIV(eden)).limit)) - GIV(freeStart)) > (interpreterAllocationReserveBytes())))) { + /* begin tab */ + printChar('\t'); + n = (((GIV(eden)).limit)) - GIV(freeStart); + + /* begin printNum: */ + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)n)); + printChar(' '); + n = interpreterAllocationReserveBytes(); + + /* begin printNum: */ + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)n)); + printChar(' '); + n = (interpreterAllocationReserveBytes()) - ((((GIV(eden)).limit)) - GIV(freeStart)); + + /* begin printNum: */ + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)n)); + cr(); + } + + /* begin checkMemoryMap */ + assert(isYoungObject(GIV(newSpaceStart))); + assert(!(isOldObject(GIV(newSpaceStart)))); + assert(isYoungObject(GIV(oldSpaceStart) - BytesPerWord)); + assert(!(isOldObject(GIV(oldSpaceStart) - BytesPerWord))); + assert(!(isYoungObject(GIV(oldSpaceStart)))); + assert(isOldObject(GIV(oldSpaceStart))); + assert(!(isYoungObject(GIV(endOfMemory)))); + assert(isOldObject(GIV(endOfMemory))); + + /* we would like the following to be true, but we either choose one boundary check for + cogMethods vs objects (isMachineCodeFrame: et al) or one boundary check for + copyAndForward:. We can't have both, and the former is likely the highest dynamic + frequency. */ + assert(!(isOldObject(minCogMethodAddress()))); + assert(!(isOldObject(maxCogMethodAddress()))); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + runLeakCheckerFor(GCModeNewSpace); + preGCAction(GCModeNewSpace); + + /* begin flushMethodCacheFrom:to: */ + probe = 0; + for (i = 1; i <= MethodCacheEntries; i += 1) { + if (GIV(methodCache)[probe + MethodCacheSelector]) { + if (((oopisGreaterThanOrEqualTo(GIV(methodCache)[probe + MethodCacheSelector], GIV(newSpaceStart))) + && (oopisLessThan(GIV(methodCache)[probe + MethodCacheSelector], GIV(oldSpaceStart)))) + || ((oopisGreaterThanOrEqualTo(GIV(methodCache)[probe + MethodCacheMethod], GIV(newSpaceStart))) + && (oopisLessThan(GIV(methodCache)[probe + MethodCacheMethod], GIV(oldSpaceStart))))) { + GIV(methodCache)[probe + MethodCacheSelector] = 0; + } + } + probe += MethodCacheEntrySize; + } + + /* would prefer this to be in mapInterpreterOops, but + compatibility with ObjectMemory dictates it goes here. */ + GIV(needGCFlag) = 0; + GIV(gcStartUsecs) = ioUTCMicrosecondsNow(); + doScavenge(tenuringCriterion); + GIV(statScavenges) += 1; + GIV(statGCEndUsecs) = ioUTCMicrosecondsNow(); + GIV(statSGCDeltaUsecs) = GIV(statGCEndUsecs) - GIV(gcStartUsecs); + GIV(statScavengeGCUsecs) += GIV(statSGCDeltaUsecs); + GIV(statRootTableCount) = GIV(rememberedSetSize); + + /* begin logScavenge */ + if (GIV(scavengeLog)) { + writeScavengeLog(); + } + postGCAction(GCModeNewSpace); + runLeakCheckerFor(GCModeNewSpace); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } +} + + +/* Set the dimensions of the heap, answering the start of oldSpace. edenBytes + holds the desired ``size of eden'' + which is actually the total size of new space minus the reserve. edenBytes + is then divided up between eden + and the two survivor spaces, where each survivor space is a + scavengerDenominator (one seventh) of the total. */ +/* Transcript + cr; nextPutAll: 'heapBase: '; print: baseOfHeap; nextPut: $/; nextPutAll: + baseOfHeap hex; + nextPutAll: ' memLimit '; print: memLimit; nextPut: $/; nextPutAll: + memLimit hex; + nextPutAll: ' memEnd '; print: memEnd; nextPut: $/; nextPutAll: memEnd + hex; cr; flush. */ +/* This is more than a little counter-intuitive. Eden must include + interpreterAllocationReserveBytes. + */ + + /* SpurMemoryManager>>#setHeapBase:memoryLimit:endOfMemory: */ +static NoDbgRegParms NeverInline sqInt +setHeapBasememoryLimitendOfMemory(sqInt baseOfHeap, sqInt memLimit, sqInt memEnd) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt actualEdenBytes; + sqInt previousFSStart; + sqInt reserve; + sqInt survivorBytes; + sqInt totalBytes; + + reserve = interpreterAllocationReserveBytes(); + GIV(newSpaceStart) = baseOfHeap; + GIV(oldSpaceStart) = (baseOfHeap + GIV(edenBytes)) + reserve; + totalBytes = GIV(oldSpaceStart) - GIV(newSpaceStart); + + /* begin newSpaceStart:newSpaceBytes:survivorBytes: */ + survivorBytes = ((((GIV(oldSpaceStart) - GIV(newSpaceStart)) - reserve) / 7 /* scavengerDenominator */) & ~7); + actualEdenBytes = (((totalBytes - survivorBytes) - survivorBytes) & ~7); + assert((((totalBytes - actualEdenBytes) - survivorBytes) - survivorBytes) < (allocationUnit())); + if (((survivorBytes * 2) + actualEdenBytes) > ((0x200000000LL))) { + error("requested new space size exceeds 8Gb limit imposed by ephemeron/weak implementation."); + } + + /* for tenuring we require older objects below younger objects. since allocation + grows up this means that the survivor spaces must precede eden. */ + (GIV(pastSpace).start = GIV(newSpaceStart)); + (GIV(pastSpace).limit = GIV(newSpaceStart) + survivorBytes); + (GIV(futureSpace).start = (GIV(pastSpace).limit)); + (GIV(futureSpace).limit = ((GIV(pastSpace).limit)) + survivorBytes); + (GIV(eden).start = (GIV(futureSpace).limit)); + (GIV(eden).limit = GIV(newSpaceStart) + totalBytes); + assert((((GIV(futureSpace)).limit)) <= (GIV(newSpaceStart) + totalBytes)); + assert((((((GIV(eden)).start)) % (allocationUnit())) + ((((GIV(eden)).limit)) % (allocationUnit()))) == 0); + assert((((((GIV(pastSpace)).start)) % (allocationUnit())) + ((((GIV(pastSpace)).limit)) % (allocationUnit()))) == 0); + assert((((((GIV(futureSpace)).start)) % (allocationUnit())) + ((((GIV(futureSpace)).limit)) % (allocationUnit()))) == 0); + + /* begin initFutureSpaceStart */ + previousFSStart = GIV(futureSurvivorStart); + GIV(futureSurvivorStart) = (GIV(futureSpace).start); + GIV(tenuringProportion) = 0.9; + GIV(freeStart) = ((GIV(eden)).start); + GIV(pastSpaceStart) = ((GIV(pastSpace)).start); + GIV(freeOldSpaceStart) = memEnd; + GIV(endOfMemory) = memLimit; + return baseOfHeap; +} + + /* SpurMemoryManager>>#setIsPinnedOf:to: */ +static NoDbgRegParms void +setIsPinnedOfto(sqInt objOop, sqInt aBoolean) +{ + assert((isOldObject(objOop)) + && (!(isFreeObject(objOop)))); + byteAtput((void *)(objOop + (formatFieldByteOffset())),(aBoolean + ? (byteAt((void *)(objOop + (formatFieldByteOffset())))) | (1U << (pinnedBitByteShift())) + : (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (0xFF - (1U << (pinnedBitByteShift()))))); +} + + /* SpurMemoryManager>>#setIsRememberedOf:to: */ +static NoDbgRegParms void +setIsRememberedOfto(sqInt objOop, sqInt aBoolean) +{ + assert(!((isFreeObject(objOop)))); + byteAtput((void *)(objOop + (formatFieldByteOffset())),(aBoolean + ? (byteAt((void *)(objOop + (formatFieldByteOffset())))) | (1U << (rememberedBitByteShift())) + : (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (0xFF - (1U << (rememberedBitByteShift()))))); +} + + +/* Reduce the number of indexable fields in objOop, an arrayFormat or + longFormat object, to nSlots. + Convert the unused residual to a free chunk (if in oldSpace). + Answer the number of bytes returned to free memory, which may be zero. */ + + /* SpurMemoryManager>>#shorten:toIndexableSize: */ +sqInt +shortentoIndexableSize(sqInt objOop, sqInt indexableSize) +{ + sqInt delta; + + assert((indexableSize >= 0) + && (indexableSize < (lengthOf(objOop)))); + delta = doShortentoIndexableSize(objOop, indexableSize); + assert((lengthOf(followMaybeForwarded(objOop))) == indexableSize); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + return delta; +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#shortPrintObjectsFrom:to: */ +void +shortPrintObjectsFromto(sqInt startAddress, sqInt endAddress) +{ + sqInt oop; + + oop = objectBefore(startAddress); + oop = (oop + ? ((objectAfter(oop)) == startAddress + ? startAddress + : oop) + : startAddress); + while (oopisLessThan(oop, endAddress)) { + if (!(((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun()))) { + shortPrintOop(oop); + } + oop = objectAfter(oop); + } +} + + +/* Answer if the oop should be scavenged.. The method is called + shouldRemapOop: for compatibility with ObjectMemory. */ + + /* SpurMemoryManager>>#shouldRemapOop: */ +sqInt +shouldRemapOop(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (shouldRemapObj(oop)); +} + + /* SpurMemoryManager>>#sizeOfObjStack: */ +static NoDbgRegParms sqInt +sizeOfObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objStackPage; + sqInt total; + + if (objStack == GIV(nilObj)) { + return 0; + } + total = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + objStackPage = objStack; + while (1) { + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + if (!(objStackPage != 0)) break; + total += ObjStackLimit; + assert((fetchPointerofObject(ObjStackTopx, objStackPage)) == ObjStackLimit); + } + return total; +} + + /* SpurMemoryManager>>#slidingCompactionInProgress */ +static int +slidingCompactionInProgress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(gcPhaseInProgress) == SlidingCompactionInProgress; +} + + +/* *DO NOT CONFUSE THIS WITH numSlotsOf:. + This is an ObjectMemory compatibility method with questionable semantics. + Answers the number of slots in the receiver. + If the receiver is a byte object, return the number of bytes. + If the receiver is a short object, return the number of shorts. + If the receiver is a word object, return the number of words. + If the receiver is a double word object, return the number of double + words. Otherwise return the number of pointers. */ + + /* SpurMemoryManager>>#slotSizeOf: */ +sqInt +slotSizeOf(sqInt oop) +{ + sqInt fmt; + usqInt numSlots; + usqInt numSlotsUsqInt; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin lengthOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + +/* Return one of the objects in the specialObjectsArray */ + + /* SpurMemoryManager>>#splObj: */ +sqInt +splObj(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); +} + + +/* Answer the start of objOop, which is either the address of the overflow + size word, or objOop itself, depending on the size of the object. This may + be applied to any kind of object, normal, forwarders or free chunks. */ + + /* SpurMemoryManager>>#startOfObject: */ +static NoDbgRegParms usqInt +startOfObject(sqInt objOop) +{ + return ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); +} + + +/* Part of InterpreterProxy's 1.14 API */ + + /* SpurMemoryManager>>#statNumGCs */ +sqInt +statNumGCs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (GIV(statScavenges) + GIV(statIncrGCs)) + GIV(statFullGCs); +} + + +/* A renaming for the Cogit, which can't make sense of GIV(oldSpaceStart) */ + + /* SpurMemoryManager>>#storeCheckBoundary */ +usqInt +storeCheckBoundary(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(oldSpaceStart); +} + + +/* This primitive is called from Squeak as... + storeSegmentFor: arrayOfRoots into: aWordArray outPointers: + anArray. + This primitive will store a binary image segment (in the same format as + objects in the heap) of the + set of objects in arrayOfObjects. All pointers from within the set to + objects outside the set will be + copied into the array of outPointers. In their place in the image segment + will be an oop equal to the + offset in the outPointer array (the first would be 8), but with the high + bit set. + + Since Spur has a class table the load primitive must insert classes that + have instances into the + class table. This primitive marks such classes using the isRemembered bit, + which isn't meaningful + as a remembered bit in the segment. + + The primitive expects the segmentWordArray and outPointerArray to be more + than adequately long. + In this case it returns normally, and truncates the two arrays to exactly + the right size. + + The primitive can fail for the following reasons with the specified + failure codes: + PrimErrGenericFailure: the segmentWordArray is too small for the version + stamp PrimErrWritePastObject: the segmentWordArray is too small to + contain the reachable objects + PrimErrBadIndex: the outPointerArray is too small + PrimErrNoMemory: there is insufficient free space to store the array + answered by objectsReachableFromRoots:, + or the savedFirstFields and savedOutHashes arrays. + PrimErrNeedCompaction: a GC is needed to make room for the array answered + by objectsReachableFromRoots: + PrimErrLimitExceeded: there is no room in the hash field to store out + pointer indices or class references, + or the outPointerArray is larger than the max value of the hash field. + PrimErrNoModification: the segmentWordArrayArg or outPointerArrayArg are + immutable PrimErrObjectIsPinned: the segmentWordArrayArg or + outPointerArrayArg are pinned */ + + /* SpurMemoryManager>>#storeImageSegmentInto:outPointers:roots: */ +static NoDbgRegParms sqInt +storeImageSegmentIntooutPointersroots(sqInt segmentWordArrayArg, sqInt outPointersArrayArg, sqInt arrayOfRootsArg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayOfObjects; + sqInt arrayOfRoots; + sqInt delta; + usqInt endSeg; + int hash; + sqInt here; + sqInt i; + sqInt indexableSize; + sqInt newSegAddrOrError; + sqInt numClassesInSegment; + usqInt numSlots; + sqInt numSlotsSqInt; + sqInt objOop; + sqInt objOopSqInt; + sqInt outIndex; + sqInt outPointersArray; + usqInt p; + sqInt savedFirstFields; + sqInt savedOutHashes; + usqInt segAddr; + sqInt segmentWordArray; + usqInt segStart; + sqInt tempObjOop; + sqInt there; + sqInt toDoLimit; + sqInt toDoLimitSqInt; + usqInt toDoLimitUsqInt; + + if (( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(segmentWordArrayArg + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + ) + || ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(outPointersArrayArg + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + )) { + return PrimErrNoModification; + } + + /* Since segmentWordArrayArg & outPointersArrayArg may get shortened, they can't be pinned. */ + if ((((byteAt((void *)(segmentWordArrayArg + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0) + || (((byteAt((void *)(outPointersArrayArg + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)) { + return PrimErrObjectIsPinned; + } + if (((/* begin numSlotsOf: */ + assert((classIndexOf(outPointersArrayArg)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(outPointersArrayArg + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointersArrayArg - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > (identityHashHalfWordMask())) { + return PrimErrLimitExceeded; + } + runLeakCheckerFor(GCModeImageSegment); + + /* First scavenge to collect any new space garbage that refers to the graph. */ + + /* begin scavengingGC */ + scavengingGCTenuringIf(TenureByAge); + segmentWordArray = /* updatePostScavenge: */ + ((!((longAt((void *)(segmentWordArrayArg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(segmentWordArrayArg) + : segmentWordArrayArg); + outPointersArray = /* updatePostScavenge: */ + ((!((longAt((void *)(outPointersArrayArg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(outPointersArrayArg) + : outPointersArrayArg); + arrayOfRoots = /* updatePostScavenge: */ + ((!((longAt((void *)(arrayOfRootsArg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(arrayOfRootsArg) + : arrayOfRootsArg); + assert(!((forwardersIn(outPointersArray)))); + assert(!((forwardersIn(arrayOfRoots)))); + + /* Now compute the transitive closure, collecting the sequence of objects to be stored in the arrayOfObjects array. + Included in arrayOfObjects are the arrayOfRoots and all its contents. All objects have been unmarked. */ + arrayOfObjects = objectsReachableFromRoots(arrayOfRoots); + if (!arrayOfObjects) { + return PrimErrNoMemory; + } + + /* If objectsReachableFromRoots: answers an integer there is not enough continuous free space in which to allocate the + reachable objects. If there is sufficient free space then answer an error code to prompt a compacting GC and a retry. */ + if ((((arrayOfObjects) & 7) == 1)) { + if ((GIV(totalFreeOldSpace) - 8 /* allocationUnit */) >= ((arrayOfObjects >> 3))) { + return PrimErrNeedCompaction; + } + return PrimErrNoMemory; + } + assert(allObjectsUnmarked()); + assert(!((forwardersIn(arrayOfObjects)))); + runLeakCheckerFor(GCModeImageSegment); + + /* Both to expand the max size of segment and to reduce the length of the + load-time pass that adds classes to the class table, move classes to the + front of arrayOfObjects, leaving the root array as the first element. */ + + /* begin moveClassesForwardsIn: */ + /* if > 0, this is the index of the first non-class past the first element. */ + numClassesInSegment = (there = 0); + toDoLimitSqInt = ((/* begin numSlotsOf: */ + assert((classIndexOf(arrayOfObjects)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(arrayOfObjects + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(arrayOfObjects - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (here = 1; here <= toDoLimitSqInt; here += 1) { + objOopSqInt = longAt((void *)((arrayOfObjects + BaseHeaderSize) + ((((usqInt)(here) << (shiftForWord())))))); + hash = (long32At((void *)(objOopSqInt + 4))) & (identityHashHalfWordMask()); + if ((hash > (lastClassIndexPun())) + && ((classOrNilAtIndex(hash)) == objOopSqInt)) { + numClassesInSegment += 1; + if (there > 0) { + tempObjOop = longAt((void *)((arrayOfObjects + BaseHeaderSize) + ((((usqInt)(there) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(arrayOfObjects)) + && (!(isForwarded(arrayOfObjects)))); + assert(validStorePointerUncheckedArgs(there, arrayOfObjects, objOopSqInt)); + longAtput((void *)((arrayOfObjects + BaseHeaderSize) + ((((usqInt)(there) << (shiftForWord()))))),objOopSqInt); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(arrayOfObjects)) + && (!(isForwarded(arrayOfObjects)))); + assert(validStorePointerUncheckedArgs(here, arrayOfObjects, tempObjOop)); + longAtput((void *)((arrayOfObjects + BaseHeaderSize) + ((((usqInt)(here) << (shiftForWord()))))),tempObjOop); + there += 1; + } + } + else { + if (!there) { + there = here; + } + } + } + + /* The scheme is to copy the objects into segmentWordArray, and then map the oops in segmentWordArray. + Therefore the primitive needs to both map efficiently originals to copies in segmentWordArray and + be able to undo any side-effects if the primitive fails because either segmentWordArray or outPointersArray + is too small. The mapping is done by having the objects to be stored in arrayOfObjects refer to their mapped + locations through their first field, just like a forwarding pointer, but without becoming a forwarder, saving their + first field in savedFirstFields, and the objects in outPointersArray pointing to their locations in the outPointersArray + through their identityHashes, saved in savedOutHashes. + Since arrayOfObjects and its savedFirstFields, and outPointersArray and its saved hashes, can be enumerated + side-by-side, the hashes can be restored to the originals. So the first field of the heap object corresponding to + an object in arrayOfObjects is set to its location in segmentWordArray, and the hash of an object in outPointersArray + is set to its index in outPointersArray plus the top hash bit. Classes in arrayOfObjects have their marked bit set. + Oops in objects in segmentWordArray are therefore mapped by accessing the original oop, and following its first + field. Class indices in segmentWordArray are mapped by fetching the original class, and testing its marked bit. + If marked, the first field is followed to access the class copy in the segment. Out pointers (objects and classes, + which are unmarked), the object's identityHash is set (eek!!) to its index in the outPointersArray. So savedOutHashes + parallels the outPointersArray. The saved hash array is initialized with an out-of-range hash value so that the first + unused entry can be identified. */ + savedFirstFields = noInlineAllocateSlotsformatclassIndex(numSlotsOf(arrayOfObjects), wordIndexableFormat(), wordSizeClassIndexPun()); + savedOutHashes = noInlineAllocateSlotsformatclassIndex(numSlotsForBytes((numSlotsOf(outPointersArray)) * 4), firstLongFormat(), thirtyTwoBitLongsClassIndexPun()); + if ((!savedFirstFields) + || (!savedOutHashes)) { + freeObject(arrayOfObjects); + if (oopisGreaterThanOrEqualToandLessThan(savedFirstFields, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedFirstFields); + } + if (oopisGreaterThanOrEqualToandLessThan(savedOutHashes, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedOutHashes); + } + return PrimErrNoMemory; + } + /* begin numSlotsOf: */ + assert((classIndexOf(savedFirstFields)) > (isForwardedObjectClassIndexPun())); + if (((numSlots = byteAt((void *)(savedFirstFields + (numSlotsFieldByteOffset()))))) == (numSlotsMask())) { + numSlotsSqInt = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(savedFirstFields - BaseHeaderSize)))) << 8)))))) >> 8; + } + else { + numSlotsSqInt = numSlots; + } + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((savedFirstFields + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1, addressAfter(savedFirstFields))); + toDoLimitUsqInt = ((usqInt)(((savedFirstFields + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1)); + for (p = (((usqInt)(savedFirstFields + BaseHeaderSize))); p <= toDoLimitUsqInt; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + /* begin numSlotsOf: */ + assert((classIndexOf(savedOutHashes)) > (isForwardedObjectClassIndexPun())); + if (((numSlots = byteAt((void *)(savedOutHashes + (numSlotsFieldByteOffset()))))) == (numSlotsMask())) { + numSlotsSqInt = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(savedOutHashes - BaseHeaderSize)))) << 8)))))) >> 8; + } + else { + numSlotsSqInt = numSlots; + } + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((savedOutHashes + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1, addressAfter(savedOutHashes))); + toDoLimitUsqInt = ((usqInt)(((savedOutHashes + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1)); + for (p = (((usqInt)(savedOutHashes + BaseHeaderSize))); p <= toDoLimitUsqInt; p += 8 /* allocationUnit */) { + longAtput((void *)(p),((((usqInt)(((identityHashHalfWordMask()) + 1)) << 32))) + ((identityHashHalfWordMask()) + 1)); + } + segAddr = segmentWordArray + BaseHeaderSize; + endSeg = addressAfter(segmentWordArray); + + /* Write a version number for byte order and version check. */ + if (segAddr >= endSeg) { + return PrimErrGenericFailure; + } + long32Atput((void *)(segAddr),imageSegmentVersion()); + long32Atput((void *)(segAddr + 4),imageSegmentVersion()); + segStart = (segAddr += 8 /* allocationUnit */); + assert(arrayOfRoots == (fetchPointerofObject(0, arrayOfObjects))); + + /* Copy all reachable objects to the segment, setting the marked bit for all objects (clones) in the segment, + and the remembered bit for all classes (clones) in the segment. */ + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(arrayOfObjects)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(arrayOfObjects + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(arrayOfObjects - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + /* Check that classes in the segment are addressable. Since the top bit of the hash field is used to tag + classes external to the segment, the segment offset must not inadvertently set this bit. This limit still + allows for a million or more classes. */ + if ((i == numClassesInSegment) + && ((((segAddr - segStart) / 8 /* allocationUnit */) + (lastClassIndexPun())) >= TopHashBit)) { + return returnrestoringObjectsInupTosavedFirstFields(PrimErrLimitExceeded, arrayOfObjects, i, savedFirstFields); + } + objOop = longAt((void *)((arrayOfObjects + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + assert(!(((isImmediate(objOop)) + || (isForwarded(objOop))))); + newSegAddrOrError = copyObjtoAddrstopAtsavedFirstFieldsindex(objOop, segAddr, endSeg, savedFirstFields, i); + if (oopisLessThan(newSegAddrOrError, segStart)) { + return returnrestoringObjectsInupTosavedFirstFields(newSegAddrOrError, arrayOfObjects, i, savedFirstFields); + } + segAddr = newSegAddrOrError; + } + + /* Check that it can be safely shortened. */ + if ((endSeg != segAddr) + && ((endSeg - segAddr) < (BaseHeaderSize + BytesPerOop))) { + return returnrestoringObjectsInupTosavedFirstFields(PrimErrWritePastObject, arrayOfObjects, -1, savedFirstFields); + } + + /* Now scan, adding out pointers to the outPointersArray; all objects in arrayOfObjects + have their first field pointing to the corresponding copy in segmentWordArray. */ + if (((outIndex = mapOopsFromtooutPointersoutHashes(segStart, segAddr, outPointersArray, savedOutHashes))) < 0) { + return returnrestoringObjectsInsavedFirstFieldsandsavedHashes(PrimErrBadIndex, arrayOfObjects, savedFirstFields, outPointersArray, savedOutHashes); + } + + /* no room in outPointers; fail + We're done. Shorten the results, restore hashes and return. */ + indexableSize = (segAddr - (segmentWordArray + BaseHeaderSize)) / 4; + + /* begin shorten:toIndexableSize: */ + assert((indexableSize >= 0) + && (indexableSize < (lengthOf(segmentWordArray)))); + delta = doShortentoIndexableSize(segmentWordArray, indexableSize); + assert((lengthOf(followMaybeForwarded(segmentWordArray))) == indexableSize); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + + /* begin shorten:toIndexableSize: */ + assert((outIndex >= 0) + && (outIndex < (lengthOf(outPointersArray)))); + delta = doShortentoIndexableSize(outPointersArray, outIndex); + assert((lengthOf(followMaybeForwarded(outPointersArray))) == outIndex); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + return returnrestoringObjectsInsavedFirstFieldsandsavedHashes(PrimNoErr, arrayOfObjects, savedFirstFields, outPointersArray, savedOutHashes); +} + + /* SpurMemoryManager>>#storeInteger:ofObject:withValue: */ +void +storeIntegerofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqLong integerValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((((((usqLong)(integerValue)) >> 60) + 1) & 15) <= 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(fieldIndex, objOop, (((usqInt)integerValue << 3) | 1))); + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),(((usqInt)integerValue << 3) | 1)); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + } + } + else { + if (fmt < (firstShortFormat())) { + if (fmt < (firstLongFormat())) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 3)))),integerValue); + } + else { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2)))),integerValue); + } + } + else { + if (fmt < (firstCompiledMethodFormat())) { + if (fmt < (firstByteFormat())) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((objOop + BaseHeaderSize) + fieldIndex),integerValue); + } + else { + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 1)))),integerValue); + } + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + } + } + } +} + + /* SpurMemoryManager>>#storeLong32:ofObject:withValue: */ +int +storeLong32ofObjectwithValue(sqInt fieldIndex, sqInt obj, sqInt valueWord) +{ + return long32Atput((void *)((obj + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2)))),valueWord); +} + + /* SpurMemoryManager>>#storePointerUnchecked:ofObject:withValue: */ +sqInt +storePointerUncheckedofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqInt valuePointer) +{ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(fieldIndex, objOop, valuePointer)); + return longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); +} + + +/* Note must check here for stores of young objects into old ones. */ +/* See SistaCogit */ + + /* SpurMemoryManager>>#storePointer:ofObject:withValue: */ +sqInt +storePointerofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqInt valuePointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(validStorePointerArgs(fieldIndex, objOop, valuePointer)); + + /* begin isOldObject: */ + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + return longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); +} + + +/* Answer a new String copied from a null-terminated C string, + or nil if out of memory. */ + + /* SpurMemoryManager>>#stringForCString: */ +sqInt +stringForCString(const char *aCString) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt formatField; + sqInt len; + usqInt newObj; + sqInt newString; + usqInt numBytes; + sqInt numSlots; + + len = strlen(aCString); + numSlots = (len + (BytesPerWord - 1)) / BytesPerWord; + formatField = (firstByteFormat()) + ((8 - len) & (BytesPerWord - 1)); + + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if ((((usqInt)(numSlots)) >> 56) > 0) { + newString = null; + goto l1; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newString = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, formatField, ClassByteStringCompactIndex); + goto l1; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + ClassByteStringCompactIndex); + } + else { + longAtput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + ClassByteStringCompactIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newString = newObj; + /* end allocateSlots:format:classIndex: */ +l1: + if (newString) { + strncpy(((char *) (newString + BaseHeaderSize)), aCString, len); + } + return newString; +} + + +/* On load, swizzle the pointers in an obj stack. Answer the obj stack's oop. */ + + /* SpurMemoryManager>>#swizzleObjStackAt: */ +static NoDbgRegParms sqInt +swizzleObjStackAt(sqInt objStackRootIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt field; + sqInt firstPage; + sqInt index; + sqInt page; + sqInt stackOrNil; + + firstPage = (stackOrNil = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord()))))))); + if (stackOrNil == GIV(nilObj)) { + return stackOrNil; + } + do { + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + assert((fetchPointerofObject(ObjStackMyx, stackOrNil)) == objStackRootIndex); + + /* There are four fixed slots in an obj stack, and a Topx of 0 indicates empty, so + if there were 5 slots in an oop stack, full would be 2, and the last 0-rel index is 4. + Hence the last index is topx + fixed slots - 1, or topx + ObjStackNextx */ + index = (longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) + ObjStackNextx; + + /* swizzle fields including ObjStackNextx, excluding ObjStackFreex and leave field containing the next link. */ + do { + field = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (!((field == 0) + || (((field & (tagMask())) != 0)))) { + field = swizzleObj(field); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(stackOrNil)) == (wordIndexableFormat())); + longAtput((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),field); + } + } while(((index -= 1)) >= ObjStackNextx); + } while(((stackOrNil = field)) != 0); + if ((stackOrNil = longAt((void *)((firstPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))))) { + page = firstPage; + do { + stackOrNil = swizzleObj(stackOrNil); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(page)) == (wordIndexableFormat())); + longAtput((void *)((page + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),stackOrNil); + page = stackOrNil; + } while(((stackOrNil = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))))) != 0); + } + assert(isValidObjStackAt(objStackRootIndex)); + return longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord())))))); +} + + +/* Do an incremental GC that tenures all surviving young objects to old + space. The selector tenuringIncrementalGC refers to the V3 incremental + collector, which is the collector for young objects, equivalent in + function to the scavenger. + The Spur incremental collector is called the global incremental garbage + collector or globalIGC for short. + */ + + /* SpurMemoryManager>>#tenuringIncrementalGC */ +void +tenuringIncrementalGC(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt savedTenuringThreshold; + + /* begin flushNewSpace */ + savedTenuringThreshold = GIV(tenureThreshold); + GIV(tenureThreshold) = GIV(oldSpaceStart); + scavengingGCTenuringIf(TenureByAge); + + /* begin setRawTenuringThreshold: */ + GIV(tenureThreshold) = savedTenuringThreshold; + assert((GIV(rememberedSetSize)) == 0); + assert(GIV(pastSpaceStart) == (((GIV(pastSpace)).start))); + assert(GIV(freeStart) == (((GIV(eden)).start))); +} + + +/* This assert is tricky. push:onObjStack: may call topOfObjStack: just after + pushing an + empty page on the stack, and will ask if the second page is valid. */ + + /* SpurMemoryManager>>#topOfObjStack: */ +static NoDbgRegParms sqInt +topOfObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt topx; + + eassert(isValidObjStackPagemyIndexfirstPage(objStack, fetchPointerofObject(ObjStackMyx, objStack), objStack == (fetchPointerofObject(fetchPointerofObject(ObjStackMyx, objStack), GIV(hiddenRootsObj))))); + topx = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + if (!topx) { + assert((fetchPointerofObject(ObjStackNextx, objStack)) == 0); + return null; + } + return longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(((topx + ObjStackFixedSlots) - 1)) << (shiftForWord())))))); +} + + +/* Answers the top of the remappable oop stack. Useful when writing loops. + We support this excessence for compatibility with ObjectMemory. + Spur doesn't GC during allocation. */ + + /* SpurMemoryManager>>#topRemappableOop */ +sqInt +topRemappableOop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(remapBuffer)[GIV(remapBufferCount)]; +} + + +/* This method both computes the actual number of free bytes by traversing + all free objects + on the free lists/tree, and checks that the tree is valid. It is used + mainly by checkFreeSpace. */ + + /* SpurMemoryManager>>#totalFreeListBytes */ +static sqInt +totalFreeListBytes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bytesInChunk; + sqInt cameFrom; + sqInt i; + sqInt largeChild; + sqInt listNode; + sqInt nextNode; + sqInt smallChild; + sqInt totalFreeBytes; + sqInt treeNodeSqInt; + + totalFreeBytes = 0; + for (i = 1; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + bytesInChunk = i * 8 /* allocationUnit */; + listNode = GIV(freeLists)[i]; + while (listNode != 0) { + totalFreeBytes += bytesInChunk; + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(listNode)); + assert(bytesInChunk == (bytesInBody(listNode))); + nextNode = longAt((void *)((listNode + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(nextNode != listNode); + listNode = nextNode; + } + } + + /* begin freeTreeNodesDo: */ + treeNodeSqInt = GIV(freeLists)[0]; + if (!treeNodeSqInt) { + goto l1; + } + cameFrom = -1; + do { + assert((bytesInBody(treeNodeSqInt)) >= ((numFreeLists()) * (allocationUnit()))); + smallChild = longAt((void *)((treeNodeSqInt + BaseHeaderSize) + (3U << (shiftForWord())))); + largeChild = longAt((void *)((treeNodeSqInt + BaseHeaderSize) + (4U << (shiftForWord())))); + assert((smallChild == 0) + || (treeNodeSqInt == (fetchPointerofFreeChunk(freeChunkParentIndex(), smallChild)))); + assert((largeChild == 0) + || (treeNodeSqInt == (fetchPointerofFreeChunk(freeChunkParentIndex(), largeChild)))); + + /* apply if the node has no children, or it has no large children and we're + returning from the small child, or we're returning from the large child. */ + if (((smallChild == 0) + && (largeChild == 0)) + || ((largeChild + ? cameFrom == largeChild + : cameFrom == smallChild))) { + bytesInChunk = bytesInBody(treeNodeSqInt); + assert((bytesInChunk / (allocationUnit())) >= (numFreeLists())); + listNode = treeNodeSqInt; + while (listNode != 0) { + /* self printFreeChunk: listNode */ + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(listNode)); + assert((listNode == treeNodeSqInt) + || ((fetchPointerofFreeChunk(freeChunkParentIndex(), listNode)) == 0)); + totalFreeBytes += bytesInChunk; + assert(bytesInChunk == (bytesInBody(listNode))); + nextNode = longAt((void *)((listNode + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(nextNode != listNode); + listNode = nextNode; + } + + /* and since we've applied we must move on up */ + cameFrom = treeNodeSqInt; + treeNodeSqInt = longAt((void *)((treeNodeSqInt + BaseHeaderSize) + (2U << (shiftForWord())))); + } + else { + if ((smallChild != 0) + && (cameFrom != smallChild)) { + treeNodeSqInt = smallChild; + } + else { + assert(largeChild != 0); + treeNodeSqInt = largeChild; + } + cameFrom = -1; + } + } while(treeNodeSqInt != 0); + /* end freeTreeNodesDo: */ +l1: + return totalFreeBytes; +} + + /* SpurMemoryManager>>#trueObject */ +sqInt +trueObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(trueObj); +} + + +/* Ephemerons in the mourn queue will have been fired (had their + ephemeron-ness turned off), + but the ephemeron queue is not saved in the snapshot. So the snapshotted + ephemerons need to be unfired so they're still unqueued ephemerons in the + loaded image. */ + + /* SpurMemoryManager>>#unfireQueuedEphemeronsForSnapshot */ +static void +unfireQueuedEphemeronsForSnapshot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt iSqInt; + sqInt mourner; + sqInt numOnThisPage; + sqInt objStackPage; + + /* begin objStack:do: */ + if (GIV(mournQueue) == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(GIV(mournQueue))); + objStackPage = GIV(mournQueue); + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + mourner = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (((!(mourner & (tagMask())))) + && (((byteAt((void *)(mourner + (formatFieldByteOffset())))) & (formatMask())) == (nonIndexablePointerFormat()))) { + /* begin setFormatOf:to: */ + assert((((ephemeronFormat()) >= 0) && ((ephemeronFormat()) <= (formatMask())))); + byteAtput((void *)(mourner + (formatFieldByteOffset())),((byteAt((void *)(mourner + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (ephemeronFormat())); + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l1:; +} + + +/* Unlink a free object from the free lists. Do not alter totalFreeOldSpace. + Used for coalescing. + */ + + /* SpurMemoryManager>>#unlinkFreeChunk:chunkBytes: */ +static NoDbgRegParms sqInt +unlinkFreeChunkchunkBytes(sqInt freeChunk, sqInt chunkBytes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt lilliputian; + sqInt next; + sqInt nextFreeChunk; + sqInt nextSqInt; + sqInt prev; + + index = chunkBytes / 8 /* allocationUnit */; + + /* Pathological 64 bits case - size 1 - single linked list */ + + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + if (chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */)) { + return unlinkLilliputianChunkindex(freeChunk, index); + } + prev = longAt((void *)((freeChunk + BaseHeaderSize) + (1U << (shiftForWord())))); + + /* Has prev element: update double linked list */ + if (prev) { + nextFreeChunk = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:chunkBytes: */ + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + lilliputian = chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if ((nextFreeChunk != 0) + && (!lilliputian)) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + } + return freeChunk; + } + + /* Is the beginning of a list + Small chunk */ + if ((index < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(freeChunk)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(freeChunk)) == freeChunk))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((nextSqInt = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))))); + if (nextSqInt) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextSqInt)); + longAtput((void *)((nextSqInt + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + return freeChunk; + } + + /* Large chunk */ + next = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + if (next) { + inFreeTreeReplacewith(freeChunk, next); + } + else { + unlinkSolitaryFreeTreeNode(freeChunk); + } + + /* no list; remove the interior node + list; replace node with it */ + return freeChunk; +} + + /* SpurMemoryManager>>#unlinkLilliputianChunk:index: */ +static NoDbgRegParms NeverInline sqInt +unlinkLilliputianChunkindex(sqInt freeChunk, sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt next; + sqInt nextSqInt; + sqInt node; + sqInt prev; + + + /* for profiling */ + node = GIV(freeLists)[index]; + prev = 0; + while (node != 0) { + assert(node == (startOfObject(node))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(node)); + next = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + if (node == freeChunk) { + if (prev) { + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((next == 0) + || (isFreeObject(next))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),next); + } + else { + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(freeChunk)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(freeChunk)) == freeChunk))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((nextSqInt = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))))); + } + return freeChunk; + } + prev = node; + node = next; + } + error("freeChunk not found in lilliputian chunk free list"); + return 0; +} + + +/* Unlink a freeTreeNode. Assumes the node has no list (null next link). */ + + /* SpurMemoryManager>>#unlinkSolitaryFreeTreeNode: */ +static NoDbgRegParms void +unlinkSolitaryFreeTreeNode(sqInt freeTreeNode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fieldIndex; + sqInt larger; + sqInt parent; + sqInt smaller; + + assert((fetchPointerofFreeChunk(freeChunkNextIndex(), freeTreeNode)) == 0); + + /* case 1. interior node has one child, P = parent, N = node, S = subtree (mirrored for large vs small) + ___ ___ + | P | | P | + _/_ _/_ + | N | => | S | + _/_ + | S | + case 2: interior node has two children, , P = parent, N = node, L = smaller, left subtree, R = larger, right subtree. + add the left subtree to the bottom left of the right subtree (mirrored for large vs small) + ___ ___ + | P | | P | + _/_ _/_ + | N | => | R | + _/_ _\_ _/_ + | L | | R | | L | */ + smaller = longAt((void *)((freeTreeNode + BaseHeaderSize) + (3U << (shiftForWord())))); + larger = longAt((void *)((freeTreeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + parent = longAt((void *)((freeTreeNode + BaseHeaderSize) + (2U << (shiftForWord())))); + if (parent) { + if (smaller) { + fieldIndex = (freeTreeNode == (longAt((void *)((parent + BaseHeaderSize) + (3U << (shiftForWord()))))) + ? 3 /* freeChunkSmallerIndex */ + : 4 /* freeChunkLargerIndex */); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(parent)); + assert((smaller == 0) + || (isFreeObject(smaller))); + longAtput((void *)((parent + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),smaller); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(smaller)); + assert((parent == 0) + || (isFreeObject(parent))); + longAtput((void *)((smaller + BaseHeaderSize) + (2U << (shiftForWord()))),parent); + if (larger) { + addFreeSubTree(larger); + } + } + else { + fieldIndex = (freeTreeNode == (longAt((void *)((parent + BaseHeaderSize) + (3U << (shiftForWord()))))) + ? 3 /* freeChunkSmallerIndex */ + : 4 /* freeChunkLargerIndex */); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(parent)); + assert((larger == 0) + || (isFreeObject(larger))); + longAtput((void *)((parent + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),larger); + if (larger) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(larger)); + assert((parent == 0) + || (isFreeObject(parent))); + longAtput((void *)((larger + BaseHeaderSize) + (2U << (shiftForWord()))),parent); + } + } + } + else { + if (smaller) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(smaller)); + longAtput((void *)((smaller + BaseHeaderSize) + (2U << (shiftForWord()))),0); + GIV(freeLists)[0] = smaller; + if (larger) { + addFreeSubTree(larger); + } + } + else { + if (larger) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(larger)); + longAtput((void *)((larger + BaseHeaderSize) + (2U << (shiftForWord()))),0); + } + GIV(freeLists)[0] = larger; + } + } +} + + /* SpurMemoryManager>>#unpinObject: */ +sqInt +unpinObject(sqInt objOop) +{ + assert(isNonImmediate(objOop)); + setIsPinnedOfto(objOop, 0); + return 0; +} + + /* SpurMemoryManager>>#unscannedEphemeronsContainsOnlyActiveEphemerons */ +static sqInt +unscannedEphemeronsContainsOnlyActiveEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt entry; + usqInt p; + + for (p = ((GIV(unscannedEphemerons).start)); p <= ((GIV(unscannedEphemerons).top)); p += BytesPerOop) { + entry = longAt((void *)(p)); + if (!(((!(entry & (tagMask())))) + && ((isEphemeron(entry)) + && (!(isMarked(followedKeyOfEphemeron(entry))))))) { + return 0; + } + } + return 1; +} + + /* SpurMemoryManager>>#unscannedEphemeronsContainsOnlyEphemerons */ +static sqInt +unscannedEphemeronsContainsOnlyEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt entry; + usqInt p; + + for (p = ((GIV(unscannedEphemerons).start)); p <= ((GIV(unscannedEphemerons).top)); p += BytesPerOop) { + entry = longAt((void *)(p)); + if (!(((!(entry & (tagMask())))) + && (isEphemeron(entry)))) { + return 0; + } + } + return 1; +} + + /* SpurMemoryManager>>#updateListStartingAt: */ +static NoDbgRegParms void +updateListStartingAt(sqInt freeNode) +{ + sqInt obj; + sqInt prev; + + obj = 0; + if (!freeNode) { + return; + } + assert(!((isLilliputianSize(bytesInBody(freeNode))))); + prev = freeNode; + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + longAtput((void *)((prev + BaseHeaderSize) + (1U << (shiftForWord()))),0); + while (1) { + obj = longAt((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord())))); + if (!(obj != 0)) break; + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(obj)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((obj + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + prev = obj; + } +} + + +/* Check the hashes of classes in the table. The tricky thing here is that + classes may be duplicated + in the table. So each entry must be in the table at its hash, even if it + is elsewhere in the table. */ + + /* SpurMemoryManager>>#validClassTableHashes */ +static sqInt +validClassTableHashes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOrNilSqInt; + int hash; + sqInt i; + sqInt ignored; + sqInt j; + sqInt page; + sqInt toDoLimit; + + if (!(validClassTableRootPages())) { + return 0; + } + + /* begin classTableEntriesDo: */ + for (i = 0; i < GIV(numClassTablePages); i += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNilSqInt = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNilSqInt != GIV(nilObj)) { + ignored = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + if ((!((longAt((void *)(classOrNilSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + return 0; + } + hash = (long32At((void *)(classOrNilSqInt + 4))) & (identityHashHalfWordMask()); + if (!hash) { + return 0; + } + if ((noCheckClassAtIndex(hash)) != classOrNilSqInt) { + return 0; + } + } + } + } + return 1; +} + + +/* Answer if hiddenRootsObj is of the right size with the + expected contents, and if numClassTablePages is correct. */ + + /* SpurMemoryManager>>#validClassTableRootPages */ +static sqInt +validClassTableRootPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + usqInt numSlots; + sqInt obj; + sqInt toDoLimit; + + if (!(((/* begin numSlotsOf: */ + assert((classIndexOf(GIV(hiddenRootsObj))) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(GIV(hiddenRootsObj) + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(GIV(hiddenRootsObj) - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == ((1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift()))) + 8 /* hiddenRootSlots */))) { + return 0; + } + + /* is it in range? */ + if (!((GIV(numClassTablePages) > 1) + && (GIV(numClassTablePages) <= (1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift())))))) { + return 0; + } + + /* are all pages the right size? */ + for (i = 0; i < GIV(numClassTablePages); i += 1) { + obj = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (!((addressCouldBeObj(obj)) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == (1U << (classTableMajorIndexShift()))))) { + return 0; + } + } + + /* are all entries beyond numClassTablePages nil? */ + toDoLimit = (1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift()))) - 1; + for (i = GIV(numClassTablePages); i <= toDoLimit; i += 1) { + if ((longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))))) != GIV(nilObj)) { + return 0; + } + } + return 1; +} + + +/* useful for debugging */ + + /* SpurMemoryManager>>#validFreeTree */ +int +validFreeTree(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return !(validFreeTreeChunkparent(GIV(freeLists)[0], 0)); +} + + /* SpurMemoryManager>>#validFreeTreeChunk: */ +static NoDbgRegParms sqInt +validFreeTreeChunk(sqInt chunk) +{ + if (!(segmentContainingObj(chunk))) { + return 0; + } + return !(validFreeTreeChunkparent(chunk, longAt((void *)((chunk + BaseHeaderSize) + (2U << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#validFreeTreeChunk:parent: */ +static NoDbgRegParms const char * +validFreeTreeChunkparent(sqInt chunk, sqInt parent) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + const char *reason; + + if (!chunk) { + return null; + } + if (!(/* addressCouldBeOldObj: */ + ((chunk & (BaseHeaderSize - 1)) == 0) + && (oopisGreaterThanOrEqualToandLessThan(chunk, GIV(oldSpaceStart), GIV(endOfMemory))))) { + return "not in old space"; + } + if (((bytesInBody(chunk)) / 8 /* allocationUnit */) < 64 /* numFreeLists */) { + return "too small"; + } + if (parent != (longAt((void *)((chunk + BaseHeaderSize) + (2U << (shiftForWord())))))) { + return "bad parent"; + } + if ((segmentContainingObj(chunk)) != (segmentContainingObj(addressAfter(chunk)))) { + return "not in one segment"; + } + if ((reason = validFreeTreeChunkparent(longAt((void *)((chunk + BaseHeaderSize) + (3U << (shiftForWord())))), chunk))) { + return reason; + } + if ((reason = validFreeTreeChunkparent(longAt((void *)((chunk + BaseHeaderSize) + (4U << (shiftForWord())))), chunk))) { + return reason; + } + return null; +} + + /* SpurMemoryManager>>#validObjStacks */ +static sqInt +validObjStacks(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((GIV(markStack) == GIV(nilObj)) + || (isValidObjStack(GIV(markStack)))) + && (((GIV(weaklingStack) == GIV(nilObj)) + || (isValidObjStack(GIV(weaklingStack)))) + && ((GIV(mournQueue) == GIV(nilObj)) + || (isValidObjStack(GIV(mournQueue))))); +} + + +/* This is a hook for the CoMemoryManagers to check for valid compiled code. + It is a noop here. */ + + /* SpurMemoryManager>>#validPostBecomeArrayContents: */ +static NoDbgRegParms sqInt +validPostBecomeArrayContents(sqInt anArray) +{ + return 1; +} + + /* SpurMemoryManager>>#validStorePointerArgs:_:_: */ +static NoDbgRegParms sqInt +validStorePointerArgs(sqInt fieldIndex, sqInt objOop, sqInt valuePointer) +{ + usqInt numSlots; + + return (fieldIndex >= 0) + && ((fieldIndex < ((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))) + && (!((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))); +} + + /* SpurMemoryManager>>#validStorePointerUncheckedArgs:_:_: */ +static NoDbgRegParms sqInt +validStorePointerUncheckedArgs(sqInt fieldIndex, sqInt objOop, sqInt valuePointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + + return (fieldIndex >= 0) + && (((fieldIndex == 0) + || (fieldIndex < ((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))))) + && ((((valuePointer & (tagMask())) != 0)) + || ((/* isInHeapBounds: */ + (oopisGreaterThanOrEqualTo(valuePointer, (!(minCogMethodAddress()) + ? GIV(nilObj) + : minCogMethodAddress()))) + && (oopisLessThan(valuePointer, GIV(endOfMemory)))) + || (((fieldIndex == 0) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 0) + || (GIV(gcPhaseInProgress) == SlidingCompactionInProgress))) + || ((((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (sixtyFourBitIndexableFormat())) + && ((((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) < (firstCompiledMethodFormat())) + || (fieldIndex > (literalCountOf(objOop))))))))); +} + + +/* 1/true = big, 0/false = little. + Answer false for little endian, true for big endian (should of course be + vmIsBigEndian; sigh) */ + + /* SpurMemoryManager>>#vmEndianness */ +sqInt +vmEndianness(void) +{ + return VMBIGENDIAN; +} + + /* SpurMemoryManager>>#whereIsMaybeHeapThing: */ +static NoDbgRegParms char * +whereIsMaybeHeapThing(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (/* isInNewSpace: */ + (oopisLessThan(anOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(anOop, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(anOop, ((GIV(eden)).start), GIV(freeStart))) { + return " is in eden"; + } + if (oopisGreaterThanOrEqualToandLessThan(anOop, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + return " is in future space"; + } + if (oopisGreaterThanOrEqualToandLessThan(anOop, ((GIV(pastSpace)).start), GIV(pastSpaceStart))) { + return " is in past space"; + } + return " is in new space"; + } + if (oopisGreaterThanOrEqualToandLessThan(anOop, GIV(oldSpaceStart), GIV(endOfMemory))) { + if (segmentContainingObj(anOop)) { + return " is in old space"; + } + return " is between old space segments"; + } + return null; +} + + +/* Sweep all of old space, sliding unpinned marked objects down over free and + unmarked objects. + Let the segmentManager mark which segments contain pinned objects via + notePinned:. + */ + + /* SpurPlanningCompactor>>#compact */ +static NeverInline void +compact(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt finalPass; + sqInt firstPass; + sqInt followingWord; + usqInt followingWordAddress; + sqInt highestSuitableFreeBlock; + sqInt i; + sqInt largestFreeChunk; + sqInt largestFreeChunkSqInt; + usqInt numSlots; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt sizeOfEden; + sqInt spaceEstimate; + sqInt stackOrNil; + + + /* for profiling */ + /* begin initializeScanCheckingForFullyCompactedHeap */ + GIV(firstMobileObject) = (GIV(lastMobileObject) = (GIV(objectAfterLastMobileObject) = null)); + reinitializeScanFrom(GIV(hiddenRootsObj)); + if (!GIV(firstFreeObject)) { + error("uncompactable heap; no unmarked objects found"); + } + if (GIV(firstMobileObject) >= GIV(endOfMemory)) { + /* begin unmarkObjectsInFullyCompactedHeap */ + /* begin unmarkInitialImmobileObjects */ + /* begin allOldSpaceObjectsFrom:do: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objOopSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objOopSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (oopisGreaterThanOrEqualTo(objOopSqInt, GIV(firstMobileObject))) { + goto l2; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l4; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l4:; + } + /* end unmarkInitialImmobileObjects */ +l2: + + /* begin unmarkSurvivingObjectsForCompact */ + /* begin allPastSpaceObjectsDo: */ + /* begin allPastSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + address = ((GIV(pastSpace)).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + while (oopisLessThan(objOopSqInt, GIV(pastSpaceStart))) { + assert(isEnumerableObjectNoAssert(objOopSqInt)); + if ((byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + objOopSqInt = GIV(pastSpaceStart); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l3:; + } + return; + } + + /* begin initializeCompaction */ + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + + /* begin selectSavedFirstFieldsSpace */ + spaceEstimate = (GIV(endOfMemory) - GIV(nilObj)) / 40; + sizeOfEden = (((GIV(eden)).limit)) - (((GIV(eden)).start)); + if (spaceEstimate > sizeOfEden) { + /* begin findHighestSuitableFreeBlock: */ + if ((largestFreeChunk = findLargestFreeChunk())) { + if (((bytesInBody(largestFreeChunk)) >= spaceEstimate) + && ((((usqInt)largestFreeChunk)) > (((usqInt)(GIV(endOfMemory) - GIV(totalFreeOldSpace)))))) { + highestSuitableFreeBlock = largestFreeChunk; + goto l8; + } + } + highestSuitableFreeBlock = null; + /* end findHighestSuitableFreeBlock: */ +l8: + if (highestSuitableFreeBlock) { + if ((spaceEstimate > ((/* begin sizeOfFree: */ + assert(isFreeObject(highestSuitableFreeBlock)), + bytesInBody(highestSuitableFreeBlock)))) + && (useSegmentForSavedFirstFieldsSpace(spaceEstimate))) { + goto l5; + } + if (((/* begin sizeOfFree: */ + assert(isFreeObject(highestSuitableFreeBlock)), + bytesInBody(highestSuitableFreeBlock))) > sizeOfEden) { + /* begin useFreeChunkForSavedFirstFieldsSpace: */ + assert(validFreeTreeChunk(highestSuitableFreeBlock)); + (GIV(savedFirstFieldsSpace).start = highestSuitableFreeBlock + (4 /* freeChunkLargerIndex */ * BytesPerOop)); + (GIV(savedFirstFieldsSpace).limit = addressAfter(highestSuitableFreeBlock)); + GIV(savedFirstFieldsSpaceNotInOldSpace) = 0; + assert(!((savedFirstFieldsSpaceWasAllocated()))); + goto l5; + } + } + if (useSegmentForSavedFirstFieldsSpace(spaceEstimate)) { + goto l5; + } + } + + /* begin useEdenForSavedFirstFieldsSpace */ + (GIV(savedFirstFieldsSpace).start = ((GIV(eden)).start)); + (GIV(savedFirstFieldsSpace).limit = ((GIV(eden)).limit)); + GIV(savedFirstFieldsSpaceNotInOldSpace) = 1; + assert(!((savedFirstFieldsSpaceWasAllocated()))); + /* end selectSavedFirstFieldsSpace */ +l5: + + /* begin unpinRememberedSet */ + /* begin fetchPointer:ofObject: */ + GIV(firstFieldOfRememberedSet) = longAt((void *)(((longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord()))))))) + BaseHeaderSize) + (0U << (shiftForWord())))); + setIsPinnedOfto(longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))), 0); + + /* begin resetFreeListHeads */ + GIV(freeListsMask) = 0; + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + GIV(freeLists)[i] = 0; + } + + /* begin prepareObjStacksForPlanningCompactor */ + /* begin prepareObjStackForPlanningCompactor: */ + if (GIV(markStack) == GIV(nilObj)) { + goto l7; + } + stackOrNil = GIV(markStack); + do { + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(stackOrNil + 4),((((long32At((void *)(stackOrNil + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + ((longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) & (identityHashHalfWordMask()))); + } while(((stackOrNil = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))) != 0); + /* end prepareObjStackForPlanningCompactor: */ +l7: + if (GIV(weaklingStack) == GIV(nilObj)) { + goto l9; + } + stackOrNil = GIV(weaklingStack); + do { + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(stackOrNil + 4),((((long32At((void *)(stackOrNil + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + ((longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) & (identityHashHalfWordMask()))); + } while(((stackOrNil = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))) != 0); + /* end prepareObjStackForPlanningCompactor: */ +l9: + if (GIV(mournQueue) == GIV(nilObj)) { + goto l6; + } + stackOrNil = GIV(mournQueue); + do { + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(stackOrNil + 4),((((long32At((void *)(stackOrNil + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + ((longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) & (identityHashHalfWordMask()))); + } while(((stackOrNil = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))) != 0); + /* end prepareObjStackForPlanningCompactor: */ +l6: + GIV(totalFreeOldSpace) = 0; + GIV(gcPhaseInProgress) = SlidingCompactionInProgress; + (GIV(savedFirstFieldsSpace).top = ((GIV(savedFirstFieldsSpace).start)) - BytesPerOop); + firstPass = 1; + while (1) { + finalPass = planCompactSavingForwarders(); + assert((validRelocationPlanInPass(finalPass)) == 0); + updatePointers(); + copyAndUnmark(firstPass); + + /* Would like to check here, but can't if multi-pass. */ + + /* Currently we do only a single pass if a normal GC, assuming that a pass will + always compact plenty of space. But we should perhaps check this assumption + by looking at the large free tree and seeing that the ratio of the largest free + chunk to the total ammount of free space is high. */ + if (finalPass + || (GIV(biasForGC))) break; + firstPass = 0; + reinitializeScanFrom(GIV(firstFreeObject)); + if (!(GIV(firstMobileObject) > GIV(firstFreeObject))) { + /* We get here when firstFreeObject is the last object in memory. + firstMobileObject may be random value from previous compaction + in this case, and heap is fully compacted. Abort compaction. + We set firstFreeObject to firstMobileObject for unmarking. */ + GIV(firstFreeObject) = GIV(firstMobileObject); + unmarkObjectsFromFirstFreeObject(); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + endCompaction(); + return; + } + + /* begin updateSavedFirstFieldsSpaceIfNecessary */ + if ((/* savedFirstFieldsSpaceInFreeChunk */ + (!GIV(savedFirstFieldsSpaceNotInOldSpace)) + && (oopisGreaterThan((GIV(savedFirstFieldsSpace).start), GIV(nilObj)))) + && (((GIV(savedFirstFieldsSpace).start)) < GIV(lastMobileObject))) { + spaceEstimate = ((GIV(savedFirstFieldsSpace).limit)) - ((GIV(savedFirstFieldsSpace).start)); + + /* begin findHighestSuitableFreeBlock: */ + if ((largestFreeChunkSqInt = findLargestFreeChunk())) { + if (((bytesInBody(largestFreeChunkSqInt)) >= spaceEstimate) + && ((((usqInt)largestFreeChunkSqInt)) > (((usqInt)(GIV(endOfMemory) - GIV(totalFreeOldSpace)))))) { + largestFreeChunk = largestFreeChunkSqInt; + goto l1; + } + } + largestFreeChunk = null; + /* end findHighestSuitableFreeBlock: */ +l1: + if (largestFreeChunk) { + /* begin useFreeChunkForSavedFirstFieldsSpace: */ + assert(validFreeTreeChunk(largestFreeChunk)); + (GIV(savedFirstFieldsSpace).start = largestFreeChunk + (4 /* freeChunkLargerIndex */ * BytesPerOop)); + (GIV(savedFirstFieldsSpace).limit = addressAfter(largestFreeChunk)); + GIV(savedFirstFieldsSpaceNotInOldSpace) = 0; + assert(!((savedFirstFieldsSpaceWasAllocated()))); + } + else { + /* begin useEdenForSavedFirstFieldsSpace */ + (GIV(savedFirstFieldsSpace).start = ((GIV(eden)).start)); + (GIV(savedFirstFieldsSpace).limit = ((GIV(eden)).limit)); + GIV(savedFirstFieldsSpaceNotInOldSpace) = 1; + assert(!((savedFirstFieldsSpaceWasAllocated()))); + } + } + (GIV(savedFirstFieldsSpace).top = ((GIV(savedFirstFieldsSpace).start)) - BytesPerOop); + } + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + endCompaction(); +} + + +/* Sweep the mobile portion of the heap, moving objects to their eventual + locations, and clearing their marked bits. + Remember to update the savedFirstFields of pointer objects, as these have + been forwarded. + Answer if the end of the heap was reached (savedFirstFieldsSpace has not + overflowed). + The enumerations in planCompactSavingForwarders, + updatePointersInMobileObjects and copyAndUnmarkMobileObjects + match. We could implement them as a single enumeration method taking + several block arguments, but arguably that + would make understanding an already tricky algorithm more difficult. + Instead we tolerate the duplication and encourage + the reader to diff the three methods to see where they diverge (e.g. via + Cmd-shift-C). */ + + /* SpurPlanningCompactor>>#copyAndUnmarkMobileObjects */ +static NeverInline sqInt +copyAndUnmarkMobileObjects(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt availableSpace; + usqInt bytes; + sqInt destObj; + sqInt finalObject; + sqInt firstField; + sqInt followingWord; + usqInt followingWordAddress; + usqInt limit; + usqInt next; + usqInt nextBytes; + sqInt nextObj; + usqInt numSlots; + usqInt obj; + usqInt objBytes; + sqInt objOop; + usqInt previousPin; + sqInt prevObj; + sqInt prevPrevObj; + SpurSegmentInfo *seg; + usqInt start; + usqInt startOfPreviousPin; + usqInt toFinger; + usqInt top; + + availableSpace = 0; + previousPin = 0; + assert(!((isMarked(GIV(firstFreeObject))))); + toFinger = /* startOfObject: */ + ((byteAt((void *)(GIV(firstFreeObject) + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? GIV(firstFreeObject) - BaseHeaderSize + : GIV(firstFreeObject)); + top = (GIV(savedFirstFieldsSpace).start); + startOfPreviousPin = 0; + finalObject = (!(GIV(lastMobileObject)) + ? GIV(nilObj) + : GIV(lastMobileObject)); + + /* begin allOldSpaceEntitiesForCompactingFrom:to:do: */ + assert(isOldObject(GIV(firstFreeObject))); + assert(oopisLessThanOrEqualTo(finalObject, GIV(endOfMemory))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstFreeObject); + limit = (oopisLessThan(finalObject, GIV(endOfMemory)) + ? addressAfter(finalObject) + : GIV(endOfMemory)); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, limit))) break; + assert((long64At((void *)(objOop))) != 0); + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + nextObj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + nextObj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1: + assert((previousPin == null + ? toFinger <= (startOfObject(objOop)) + : (isMarked(previousPin)) + && (toFinger <= startOfPreviousPin))); + assert(GIV(savedFirstFieldsSpaceNotInOldSpace) + || (toFinger < top)); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + if (!previousPin) { + previousPin = objOop; + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + } + } + else { + bytes = bytesInBody(objOop); + while ((toFinger <= startOfPreviousPin) + && ((bytes != ((availableSpace = startOfPreviousPin - toFinger))) + && ((bytes + (16)) > availableSpace))) { + /* The object does not fit in the space between toFinger and previousPin. + Move toFinger up to point at the first unmarked or mobile object after + previousPin, or, if previousPin is contiguous with o, to the start of this + object. Update previousPin to be the next pinned object above toFInger + and below this object, or nil if no such pinned object exists. + Any unfillable gaps between adjacent pinned objects will be freed. */ + if (availableSpace > 0) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += availableSpace; + freeChunkWithBytesat(availableSpace, toFinger); + } + do { + assert((isMarked(previousPin)) + && (isPinned(previousPin))); + + /* begin unmarkPinned: */ + if (((longAt((void *)(previousPin))) & (classIndexMask())) == (segmentBridgePun())) { + assert(isMarked(previousPin)); + } + else { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(previousPin))); + byteAtput((void *)(previousPin + (markBitsByteOffset())),(byteAt((void *)(previousPin + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + + /* begin notePinned: */ + assert(isPinned(previousPin)); + assert(!((isSegmentBridge(previousPin)))); + seg = segmentContainingObj(previousPin); + (seg->containsPinned = 1); + } + toFinger = addressAfter(previousPin); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(toFinger + (numSlotsFieldByteOffset()))); + previousPin = (numSlots == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + } while((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && ((((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0) + && (previousPin < objOop))); + + /* Now previousPin is either equal to o or mobile. + Move it to the next pinned object below o */ + while (!((previousPin >= objOop) + || ((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && (((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + previousPin = oldSpaceObjectAfter(previousPin); + } + if (previousPin >= objOop) { + previousPin = null; + startOfPreviousPin = 0; + } + else { + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(previousPin + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? previousPin - BaseHeaderSize + : previousPin); + } + } + firstField = longAt((void *)(top)); + + /* begin copyAndUnmarkObject:to:bytes:firstField: */ + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + destObj = (numSlots == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + start = /* startOfObject:given: */ + (numSlots == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + + /* memmove must be used since the ranges may overlap. */ + memmove(((void *)toFinger), ((void *)start), bytes); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(destObj)) + && (!(isForwarded(destObj)))); + assert(validStorePointerUncheckedArgs(0, destObj, firstField)); + longAtput((void *)((destObj + BaseHeaderSize) + (0U << (shiftForWord()))),firstField); + toFinger += bytes; + if (((top += BytesPerOop)) >= ((GIV(savedFirstFieldsSpace).limit))) { + assert(((GIV(savedFirstFieldsSpace).top)) == (top - BytesPerOop)); + assert(nextObj == GIV(objectAfterLastMobileObject)); + if (!previousPin) { + previousPin = nextObj; + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(nextObj + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? nextObj - BaseHeaderSize + : nextObj); + } + + /* Create a free object for firstFreeObject to be set to on the next pass, but + do not link it into the free tree as it will be written over in that next pass. */ + if (toFinger < startOfPreviousPin) { + GIV(firstFreeObject) = initFreeChunkWithBytesat(startOfPreviousPin - toFinger, toFinger); + } + else { + GIV(firstFreeObject) = previousPin; + } + return 0; + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + objOop = nextObj; + } + freeFromupTonextObject(toFinger, GIV(endOfMemory), (!(previousPin) + ? (!(GIV(objectAfterLastMobileObject)) + ? objectAfter(GIV(firstFreeObject)) + : GIV(objectAfterLastMobileObject)) + : previousPin)); + + /* begin coalesceFrom: */ + next = 0; + if (toFinger >= GIV(endOfMemory)) { + goto l2; + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(toFinger + (numSlotsFieldByteOffset()))); + obj = (numSlots == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + while (1) { + next = oldSpaceObjectAfter(obj); + if (!(next < GIV(endOfMemory))) break; + if ((((longAt((void *)(obj))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) + && (((longAt((void *)(next))) & (classIndexMask())) == (isFreeObjectClassIndexPun()))) { + objBytes = bytesInBody(obj); + nextBytes = bytesInBody(next); + unlinkFreeChunkchunkBytes(obj, objBytes); + unlinkFreeChunkchunkBytes(next, nextBytes); + obj = freeChunkWithBytesat(objBytes + nextBytes, /* startOfObject: */ + ((byteAt((void *)(obj + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? obj - BaseHeaderSize + : obj)); + } + else { + obj = next; + } + } + /* end coalesceFrom: */ +l2: + return 1; +} + + +/* Sweep the heap, unmarking all objects and moving mobile objects to their + correct positions, + restoring their savedFirstFields. */ + + /* SpurPlanningCompactor>>#copyAndUnmark: */ +static NoDbgRegParms NeverInline void +copyAndUnmark(sqInt firstPass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt finalPass; + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + + if (firstPass) { + /* begin unmarkInitialImmobileObjects */ + /* begin allOldSpaceObjectsFrom:do: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objOopSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objOopSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (oopisGreaterThanOrEqualTo(objOopSqInt, GIV(firstMobileObject))) { + goto l2; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + /* end unmarkInitialImmobileObjects */ +l2:; + } + finalPass = copyAndUnmarkMobileObjects(); + if ((GIV(lastMobileObject)) + && ((!finalPass) + && (GIV(biasForGC)))) { + unmarkObjectsFromFirstFreeObject(); + } +} + + /* SpurPlanningCompactor>>#endCompaction */ +static void +endCompaction(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + + /* begin unmarkSurvivingObjectsForCompact */ + /* begin allPastSpaceObjectsDo: */ + /* begin allPastSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + address = ((GIV(pastSpace)).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + while (oopisLessThan(objOopSqInt, GIV(pastSpaceStart))) { + assert(isEnumerableObjectNoAssert(objOopSqInt)); + if ((byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + objOopSqInt = GIV(pastSpaceStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + + /* begin endSlidingCompaction */ + GIV(gcPhaseInProgress) = 0; + if (GIV(rememberedSetSize) > 0) { + objOop = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(0, objOop, GIV(firstFieldOfRememberedSet))); + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),GIV(firstFieldOfRememberedSet)); + } + setIsPinnedOfto(longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))), 1); + + /* begin relocateRememberedSet */ + GIV(rememberedSet) = firstIndexableField(longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord()))))))); + if (/* savedFirstFieldsSpaceWasAllocated */ + GIV(savedFirstFieldsSpaceNotInOldSpace) + && (oopisGreaterThan((GIV(savedFirstFieldsSpace).start), GIV(nilObj)))) { + sqDeallocateMemorySegmentAtOfSize(((void *)((GIV(savedFirstFieldsSpace).start))), ((GIV(savedFirstFieldsSpace).limit)) - ((GIV(savedFirstFieldsSpace).start))); + } +} + + +/* Free from toFinger up to limit, dealing with possible intervening pinned + objects. + */ + + /* SpurPlanningCompactor>>#freeFrom:upTo:nextObject: */ +static NoDbgRegParms void +freeFromupTonextObject(usqInt initialToFinger, usqInt limit, sqInt nextObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + sqInt obj; + usqInt objStart; + SpurSegmentInfo *seg; + usqInt toFinger; + + toFinger = initialToFinger; + objStart = /* startOfObject: */ + ((byteAt((void *)(nextObject + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? nextObject - BaseHeaderSize + : nextObject); + if (toFinger < objStart) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += objStart - toFinger; + freeChunkWithBytesat(objStart - toFinger, toFinger); + } + toFinger = objStart; + while (objStart < limit) { + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(objStart + (numSlotsFieldByteOffset()))); + obj = (numSlots == (numSlotsMask()) + ? objStart + BaseHeaderSize + : objStart); + if ((((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && (((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)) { + /* begin unmarkPinned: */ + if (((longAt((void *)(obj))) & (classIndexMask())) == (segmentBridgePun())) { + assert(isMarked(obj)); + } + else { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(obj))); + byteAtput((void *)(obj + (markBitsByteOffset())),(byteAt((void *)(obj + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + + /* begin notePinned: */ + assert(isPinned(obj)); + assert(!((isSegmentBridge(obj)))); + seg = segmentContainingObj(obj); + (seg->containsPinned = 1); + } + if (toFinger < objStart) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += objStart - toFinger; + freeChunkWithBytesat(objStart - toFinger, toFinger); + } + toFinger = (objStart = addressAfter(obj)); + } + else { + objStart = addressAfter(obj); + } + } + if (limit > toFinger) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += limit - toFinger; + freeChunkWithBytesat(limit - toFinger, toFinger); + } +} + + /* SpurPlanningCompactor>>#isMobile: */ +static NoDbgRegParms sqInt +isMobile(sqInt obj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (oopisGreaterThanOrEqualToandLessThanOrEqualTo(obj, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)); +} + + +/* For asserts */ + + /* SpurPlanningCompactor>>#isPostMobile: */ +static NoDbgRegParms sqInt +isPostMobile(sqInt obj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return oopisGreaterThanOrEqualToandLessThanOrEqualTo(obj, GIV(mobileStart), GIV(lastMobileObject)); +} + + +/* Sweep the heap from firstFreeObject forwarding marked objects to where + they can be moved to, saving their forwarding pointer in + savedFirstFieldsSpace. Continue until either the end of the heap is + reached or savedFirstFieldsSpace is full. + Answer if the end of the heap was reached (savedFirstFieldsSpace has not + overflowed). + The enumerations in planCompactSavingForwarders, + updatePointersInMobileObjects and copyAndUnmarkMobileObjects + match. We could implement them as a single enumeration method taking + several block arguments, but arguably that + would make understanding an already tricky algorithm more difficult. + Instead we tolerate the duplication and encourage + the reader to diff the three methods to see where they diverge (e.g. via + Cmd-shift-C). */ + + /* SpurPlanningCompactor>>#planCompactSavingForwarders */ +static NeverInline sqInt +planCompactSavingForwarders(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt availableSpace; + usqInt bytes; + sqInt eventualLocation; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + usqInt previousPin; + sqInt prevObj; + sqInt prevPrevObj; + usqInt startOfPreviousPin; + usqInt toFinger; + usqInt top; + + availableSpace = 0; + previousPin = 0; + assert(!((isMarked(GIV(firstFreeObject))))); + toFinger = /* startOfObject: */ + ((byteAt((void *)(GIV(firstFreeObject) + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? GIV(firstFreeObject) - BaseHeaderSize + : GIV(firstFreeObject)); + top = (GIV(savedFirstFieldsSpace).start); + startOfPreviousPin = 0; + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(firstFreeObject))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstFreeObject); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + assert((previousPin == null + ? toFinger <= (startOfObject(objOop)) + : (isMarked(previousPin)) + && (toFinger <= startOfPreviousPin))); + assert(GIV(savedFirstFieldsSpaceNotInOldSpace) + || (toFinger < top)); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + if (!previousPin) { + previousPin = objOop; + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + } + } + else { + bytes = bytesInBody(objOop); + while ((toFinger <= startOfPreviousPin) + && ((bytes != ((availableSpace = startOfPreviousPin - toFinger))) + && ((bytes + (16)) > availableSpace))) { + /* The object does not fit in the space between toFinger and previousPin. + Move toFinger up to point at the first unmarked or mobile object after + previousPin, or, if previousPin is contiguous with o, to the start of this + object. Update previousPin to be the next pinned object above toFInger + and below this object, or nil if no such pinned object exists. + Any unfillable gaps between adjacent pinned objects will be freed. */ + do { + toFinger = addressAfter(previousPin); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(toFinger + (numSlotsFieldByteOffset()))); + previousPin = (numSlots == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + } while((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && ((((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0) + && (previousPin < objOop))); + + /* Now previousPin is either equal to o or mobile. + Move it to the next pinned object below o */ + while (!((previousPin >= objOop) + || ((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && (((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + previousPin = oldSpaceObjectAfter(previousPin); + } + if (previousPin >= objOop) { + previousPin = null; + startOfPreviousPin = 0; + } + else { + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(previousPin + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? previousPin - BaseHeaderSize + : previousPin); + } + } + + /* begin forwardMobileObject:to:savedFirstFieldPtr: */ + GIV(lastMobileObject) = objOop; + eventualLocation = ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + longAtput((void *)(top),longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(0, objOop, eventualLocation)); + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),eventualLocation); + toFinger += bytes; + if (((top += BytesPerOop)) >= ((GIV(savedFirstFieldsSpace).limit))) { + (GIV(savedFirstFieldsSpace).top = top - BytesPerOop); + GIV(objectAfterLastMobileObject) = oldSpaceObjectAfter(GIV(lastMobileObject)); + return 0; + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* If the heap is already fully compacted there will be no lastMobileObject... */ + if (GIV(lastMobileObject)) { + (GIV(savedFirstFieldsSpace).top = top - BytesPerOop); + GIV(objectAfterLastMobileObject) = oldSpaceObjectAfter(GIV(lastMobileObject)); + } + return 1; +} + + +/* Search for firstFreeObject and firstMobileObject from initialObject, which + is the + hiddenRootsObject on the first pass, and the objectAfterLastMobileObject + on subsequent passes). */ + + /* SpurPlanningCompactor>>#reinitializeScanFrom: */ +static NoDbgRegParms void +reinitializeScanFrom(sqInt initialObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + + GIV(firstFreeObject) = scanForFirstFreeAndFirstMobileObjectFrom(initialObject); + if (GIV(firstFreeObject)) { + GIV(mobileStart) = /* startOfObject: */ + ((byteAt((void *)(GIV(firstFreeObject) + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? GIV(firstFreeObject) - BaseHeaderSize + : GIV(firstFreeObject)); + } + if (GIV(objectAfterLastMobileObject)) { + /* begin allOldSpaceEntitiesFrom:to:do: */ + assert((isNonImmediate(GIV(firstFreeObject))) + && (isInSegments(GIV(firstFreeObject)))); + assert((isNonImmediate(GIV(objectAfterLastMobileObject))) + && (isInSegments(GIV(objectAfterLastMobileObject)))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstFreeObject); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThanOrEqualTo(objOop, GIV(objectAfterLastMobileObject)))) break; + assert((long64At((void *)(objOop))) != 0); + if (!((((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0) + || ((!(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) + || (GIV(objectAfterLastMobileObject) == objOop)))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + GIV(firstMobileObject) = GIV(objectAfterLastMobileObject); + } +} + + +/* Scavenge or simply follow objOop. Answer the new location of objOop. + The send should have been guarded by a send of shouldRemapOop:. + The method is called remapObj: for compatibility with ObjectMemory. */ + + /* SpurPlanningCompactor>>#remapObj: */ +sqInt +remapObj(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt resolvedObj; + + /* begin slidingCompactionRemapObj: */ + assert(shouldRemapOop(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + resolvedObj = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(resolvedObj & (tagMask())))) + && ((!((longAt((void *)(resolvedObj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + resolvedObj = longAt((void *)((resolvedObj + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + else { + assert(!((isInFutureSpace(objOop)))); + resolvedObj = objOop; + } + if (GIV(gcPhaseInProgress) > 0) { + if (GIV(gcPhaseInProgress) == ScavengeInProgress) { + if ((/* isReallyYoung: */ + ((!(resolvedObj & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(resolvedObj)), + (oopisLessThan(resolvedObj, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(resolvedObj, GIV(newSpaceStart)))))) + && (!(oopisGreaterThanOrEqualToandLessThan(resolvedObj, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))))) { + return copyAndForward(resolvedObj); + } + } + else { + assert(slidingCompactionInProgress()); + if (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(objOop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0))) { + return longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + } + return resolvedObj; +} + + /* SpurPlanningCompactor>>#savedFirstFieldsSpaceWasAllocated */ +static sqInt +savedFirstFieldsSpaceWasAllocated(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(savedFirstFieldsSpaceNotInOldSpace) + && (oopisGreaterThan((GIV(savedFirstFieldsSpace).start), GIV(nilObj))); +} + + +/* Scan from initialObject, setting firstMobileObject to the first marked + object after the first free object found, or endOfMemory if none is found. + Answer the first free object found, or nil if none. */ + + /* SpurPlanningCompactor>>#scanForFirstFreeAndFirstMobileObjectFrom: */ +static NoDbgRegParms sqInt +scanForFirstFreeAndFirstMobileObjectFrom(sqInt initialObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firstFree; + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + + firstFree = 0; + GIV(firstMobileObject) = GIV(endOfMemory); + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(initialObject)); + prevPrevObj = (prevObj = null); + objOop = initialObject; + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (firstFree) { + GIV(firstMobileObject) = objOop; + return firstFree; + } + } + else { + if (!firstFree) { + firstFree = objOop; + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + return firstFree; +} + + +/* Answer if the obj should be scavenged, or simply followed. Sent via the + compactor from shouldRemapObj:. We test for being already scavenged + because mapStackPages + via mapInterpreterOops may be applied twice in the context of a global GC + where a + scavenge, followed by a scan-mark-free, and final compaction passes may + result in + scavenged fields being visited twice. */ + + /* SpurPlanningCompactor>>#shouldRemapObj: */ +sqInt +shouldRemapObj(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return /* slidingCompactionShouldRemapObj: */ + ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + || ((GIV(gcPhaseInProgress) > 0) + && ((GIV(gcPhaseInProgress) == ScavengeInProgress + ? ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(objOop)), + (oopisLessThan(objOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(objOop, GIV(newSpaceStart))))) + && (!(oopisGreaterThanOrEqualToandLessThan(objOop, ((GIV(futureSpace)).start), GIV(futureSurvivorStart)))) + : /* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(objOop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0))))); +} + + +/* Sweep the final immobile heap, freeing and coalescing unmarked and free + objects, and unmarking all marked objects up to the end of memory. */ + + /* SpurPlanningCompactor>>#unmarkObjectsFromFirstFreeObject */ +static void +unmarkObjectsFromFirstFreeObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt freeBytes; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + SpurSegmentInfo *seg; + usqInt startOfFree; + + startOfFree = 0; + freeBytes = 0; + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(firstFreeObject))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstFreeObject); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (startOfFree) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += freeBytes; + freeChunkWithBytesat(freeBytes, startOfFree); + startOfFree = null; + freeBytes = 0; + } + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + /* begin unmarkPinned: */ + if (((longAt((void *)(objOop))) & (classIndexMask())) == (segmentBridgePun())) { + assert(isMarked(objOop)); + } + else { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + + /* begin notePinned: */ + assert(isPinned(objOop)); + assert(!((isSegmentBridge(objOop)))); + seg = segmentContainingObj(objOop); + (seg->containsPinned = 1); + } + } + else { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + else { + if (!startOfFree) { + startOfFree = /* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + } + freeBytes += bytesInBody(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + if (startOfFree) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += freeBytes; + freeChunkWithBytesat(freeBytes, startOfFree); + } +} + + +/* Sweep the heap, updating all objects to their eventual locations. + Remember to update the savedFirstFields of pointer objects, as these have + been forwarded. */ + + /* SpurPlanningCompactor>>#updatePointers */ +static NeverInline void +updatePointers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt availableSpace; + usqInt bytes; + sqInt classIndex; + sqInt contextSize; + sqInt fmt; + sqInt followingWord; + usqInt followingWordAddress; + sqInt fwd; + sqInt header; + sqInt headerSqInt; + sqInt heapEntity; + sqInt i; + usqInt numLiterals; + usqInt numPointerSlots; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objOop; + sqInt objOopSqInt; + sqInt onePass; + sqInt oop; + usqInt previousPin; + sqInt prevObj; + sqInt prevPrevObj; + sqInt sp; + usqInt startOfPreviousPin; + usqInt toFinger; + usqInt top; + + if (!(GIV(lastMobileObject))) { + return; + } + assert((startOfObject(GIV(firstFreeObject))) == GIV(mobileStart)); + mapInterpreterOops(); + mapExtraRoots(); + + /* begin updatePointersInManagerHeapEntities */ + /* begin relocateObjStacksForPlanningCompactor */ + GIV(markStack) = relocateObjStackForPlanningCompactorandContents(GIV(markStack), 0); + GIV(weaklingStack) = relocateObjStackForPlanningCompactorandContents(GIV(weaklingStack), 0); + GIV(mournQueue) = relocateObjStackForPlanningCompactorandContents(GIV(mournQueue), 1); + if ((GIV(rememberedSetSize) > 0) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(GIV(firstFieldOfRememberedSet), GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(GIV(firstFieldOfRememberedSet) + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + GIV(firstFieldOfRememberedSet) = longAt((void *)((GIV(firstFieldOfRememberedSet) + BaseHeaderSize) + (0U << (shiftForWord())))); + } + heapEntity = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))); + + /* begin relocateObjectsInHeapEntity:from:to: */ + for (i = 1; i < GIV(rememberedSetSize); i += 1) { + oop = longAt((void *)((heapEntity + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert(isMarked(oop)); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(heapEntity)) + && (!(isForwarded(heapEntity)))); + assert(validStorePointerUncheckedArgs(i, heapEntity, fwd)); + longAtput((void *)((heapEntity + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + if (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(heapEntity, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(heapEntity + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0))) { + /* fetchPointer:ofObject: */ + longAt((void *)((heapEntity + BaseHeaderSize) + (0U << (shiftForWord())))); + } + else { + } + + /* begin updatePointersInSurvivingObjects */ + /* begin allPastSpaceObjectsDo: */ + /* begin allPastSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + address = ((GIV(pastSpace)).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + while (oopisLessThan(objOopSqInt, GIV(pastSpaceStart))) { + assert(isEnumerableObjectNoAssert(objOopSqInt)); + + /* begin updatePointersIn: */ + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objOopSqInt + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l3; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOopSqInt))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l3: + numPointerSlots = CtxtTempFrameStart + contextSize; + goto l2; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOopSqInt)) > (isForwardedObjectClassIndexPun())); + numPointerSlots = (((numSlots = byteAt((void *)(objOopSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOopSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l2; + } + if (fmt == (forwardedFormat())) { + numPointerSlots = 1; + goto l2; + } + if (fmt < (firstCompiledMethodFormat())) { + numPointerSlots = 0; + goto l2; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOopSqInt)); + headerSqInt = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numPointerSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l2: + for (i = 0; i < numPointerSlots; i += 1) { + oop = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert((isMarked(oop)) + || (objOopSqInt == (hiddenRootsObject()))); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOopSqInt)) + && (!(isForwarded(objOopSqInt)))); + assert(validStorePointerUncheckedArgs(i, objOopSqInt, fwd)); + longAtput((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + objOopSqInt = GIV(pastSpaceStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + + /* begin updatePointersInInitialImmobileObjects */ + /* begin allOldSpaceObjectsFrom:do: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objOopSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objOopSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (oopisGreaterThanOrEqualTo(objOopSqInt, GIV(firstFreeObject))) { + goto l7; + } + + /* would like to assert this, but it isn't true if more than one pass: self assert: (manager isMarked: o). */ + + /* begin updatePointersIn: */ + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objOopSqInt + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l4; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOopSqInt))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l4: + numPointerSlots = CtxtTempFrameStart + contextSize; + goto l6; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOopSqInt)) > (isForwardedObjectClassIndexPun())); + numPointerSlots = (((numSlots = byteAt((void *)(objOopSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOopSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l6; + } + if (fmt == (forwardedFormat())) { + numPointerSlots = 1; + goto l6; + } + if (fmt < (firstCompiledMethodFormat())) { + numPointerSlots = 0; + goto l6; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOopSqInt)); + headerSqInt = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numPointerSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l6: + for (i = 0; i < numPointerSlots; i += 1) { + oop = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert((isMarked(oop)) + || (objOopSqInt == (hiddenRootsObject()))); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOopSqInt)) + && (!(isForwarded(objOopSqInt)))); + assert(validStorePointerUncheckedArgs(i, objOopSqInt, fwd)); + longAtput((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l5; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l5:; + } + /* end updatePointersInInitialImmobileObjects */ +l7: + + /* begin updatePointersInMobileObjects */ + availableSpace = 0; + previousPin = 0; + assert(!((isMarked(GIV(firstFreeObject))))); + toFinger = /* startOfObject: */ + ((byteAt((void *)(GIV(firstFreeObject) + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? GIV(firstFreeObject) - BaseHeaderSize + : GIV(firstFreeObject)); + top = (GIV(savedFirstFieldsSpace).start); + startOfPreviousPin = 0; + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(firstFreeObject))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstFreeObject); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + assert((previousPin == null + ? toFinger <= (startOfObject(objOop)) + : (isMarked(previousPin)) + && (toFinger <= startOfPreviousPin))); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + if (!previousPin) { + previousPin = objOop; + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + } + + /* begin updatePointersIn: */ + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l8; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l8: + numPointerSlots = CtxtTempFrameStart + contextSize; + goto l10; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numPointerSlots = (((numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + goto l10; + } + if (fmt == (forwardedFormat())) { + numPointerSlots = 1; + goto l10; + } + if (fmt < (firstCompiledMethodFormat())) { + numPointerSlots = 0; + goto l10; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numPointerSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l10: + for (i = 0; i < numPointerSlots; i += 1) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert((isMarked(oop)) + || (objOop == (hiddenRootsObject()))); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(i, objOop, fwd)); + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + } + else { + bytes = bytesInBody(objOop); + while ((toFinger <= startOfPreviousPin) + && ((bytes != ((availableSpace = startOfPreviousPin - toFinger))) + && ((bytes + (16)) > availableSpace))) { + /* The object does not fit in the space between toFinger and previousPin. + Move toFinger up to point at the first unmarked or mobile object after + previousPin, or, if previousPin is contiguous with o, to the start of this + object. Update previousPin to be the next pinned object above toFInger + and below this object, or nil if no such pinned object exists. + Any unfillable gaps between adjacent pinned objects will be freed. */ + do { + toFinger = addressAfter(previousPin); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(toFinger + (numSlotsFieldByteOffset()))); + previousPin = (numSlots == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + } while((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && ((((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0) + && (previousPin < objOop))); + + /* Now previousPin is either equal to o or mobile. + Move it to the next pinned object below o */ + while (!((previousPin >= objOop) + || ((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && (((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + previousPin = oldSpaceObjectAfter(previousPin); + } + if (previousPin >= objOop) { + previousPin = null; + startOfPreviousPin = 0; + } + else { + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(previousPin + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? previousPin - BaseHeaderSize + : previousPin); + } + } + updatePointersInsavedFirstFieldPointer(objOop, top); + toFinger += bytes; + if (((top += BytesPerOop)) >= ((GIV(savedFirstFieldsSpace).limit))) { + assert(((GIV(savedFirstFieldsSpace).top)) == (top - BytesPerOop)); + onePass = 0; + goto l11; + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l9; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l9:; + } + assert(((GIV(savedFirstFieldsSpace).top)) == (top - BytesPerOop)); + onePass = 1; + /* end updatePointersInMobileObjects */ +l11: + if (!onePass) { + /* begin updatePointersInObjectsOverflowingSavedFirstFieldsSpace */ + /* begin allOldSpaceObjectsFrom:do: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(objectAfterLastMobileObject))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(objectAfterLastMobileObject); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objOopSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objOopSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + /* begin updatePointersIn: */ + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objOopSqInt + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l12; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOopSqInt))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l12: + numPointerSlots = CtxtTempFrameStart + contextSize; + goto l14; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOopSqInt)) > (isForwardedObjectClassIndexPun())); + numPointerSlots = (((numSlots = byteAt((void *)(objOopSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOopSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l14; + } + if (fmt == (forwardedFormat())) { + numPointerSlots = 1; + goto l14; + } + if (fmt < (firstCompiledMethodFormat())) { + numPointerSlots = 0; + goto l14; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOopSqInt)); + headerSqInt = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numPointerSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l14: + for (i = 0; i < numPointerSlots; i += 1) { + oop = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert((isMarked(oop)) + || (objOopSqInt == (hiddenRootsObject()))); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOopSqInt)) + && (!(isForwarded(objOopSqInt)))); + assert(validStorePointerUncheckedArgs(i, objOopSqInt, fwd)); + longAtput((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l13; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l13:; + } + } +} + + +/* Sweep the pointer fields in obj, updating all references to mobile objects + to their eventual locations. + firstFieldPtr is supplied for mobile objects so that the saved first field + can be updated, and so that + the first field of a compiled method (which is its header, or reference to + a CogMethod holding its header) + can be retrieved. */ + + /* SpurPlanningCompactor>>#updatePointersIn:savedFirstFieldPointer: */ +static NoDbgRegParms void +updatePointersInsavedFirstFieldPointer(sqInt obj, sqInt firstFieldPtr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt field; + sqInt fmt; + sqInt fwd; + sqInt header; + sqInt i; + usqInt numLiterals; + sqInt numPointerSlots; + usqInt numSlots; + sqInt oop; + sqInt sp; + + fmt = (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask()); + + /* begin numPointerSlotsWhileCompactingOf:withFormat:savedFirstFieldPointer: */ + assert((firstFieldPtr) + && (isMobile(obj))); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l2; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(obj))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l2: + numPointerSlots = ((usqInt) (CtxtTempFrameStart + contextSize)); + goto l3; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())); + numPointerSlots = (((numSlots = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l3; + } + assert(!((fmt == (forwardedFormat())))); + if (fmt < (firstCompiledMethodFormat())) { + numPointerSlots = 0; + goto l3; + } + field = longAt((void *)(firstFieldPtr)); + + /* begin methodHeaderFromSavedFirstField: */ + if ((((field) & 7) == 1)) { + header = field; + goto l1; + } + assert((isNonImmediate(field)) + && (field < GIV(newSpaceStart))); + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) field))->objectHeader)); + header = ((((CogMethod *) field))->methodHeader); + /* end methodHeaderFromSavedFirstField: */ +l1: + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numPointerSlots = numLiterals + LiteralStart; + /* end numPointerSlotsWhileCompactingOf:withFormat:savedFirstFieldPointer: */ +l3: + if ((fmt <= 5 /* lastPointerFormat */) + && (numPointerSlots > 0)) { + oop = longAt((void *)(firstFieldPtr)); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert(isMarked(oop)); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + longAtput((void *)(firstFieldPtr),fwd); + } + } + + /* excludes CompiledMethod + Relocate the saved first field; Note that CompiledMethods can be excluded since their + first field is either a SmallInteger or a reference to a CogMethod outside of oldSpace. */ + for (i = 1; i < numPointerSlots; i += 1) { + oop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert((isMarked(oop)) + || (obj == (hiddenRootsObject()))); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(obj)) + && (!(isForwarded(obj)))); + assert(validStorePointerUncheckedArgs(i, obj, fwd)); + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } +} + + +/* Attempt to allocate a memory segment large enough to hold the + savedFirstFieldsSpace. Invoked when neither eden nor a large free chunk + are found to be big enough for the job. */ + + /* SpurPlanningCompactor>>#useSegmentForSavedFirstFieldsSpace: */ +static NoDbgRegParms sqInt +useSegmentForSavedFirstFieldsSpace(sqInt spaceEstimate) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt allocatedSize; + sqInt roundedSize; + void *segAddress; + + allocatedSize = 0; + roundedSize = ((spaceEstimate + 0x3FF) / 0x400) * 0x400; + if ((segAddress = sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto(roundedSize, firstGapOfSizeAtLeast(roundedSize), (&allocatedSize)))) { + (GIV(savedFirstFieldsSpace).start = ((usqIntptr_t)segAddress)); + (GIV(savedFirstFieldsSpace).limit = (((usqIntptr_t)segAddress)) + allocatedSize); + GIV(savedFirstFieldsSpaceNotInOldSpace) = 1; + assert(savedFirstFieldsSpaceWasAllocated()); + return 1; + } + + /* sent to the manager so that the simulator can increase memory to simulate a new segment */ + return 0; +} + + +/* Answer 0 if all the mobile objects from firstMobileObject to + lastMobileObject have sane forwarding addresses, and that + savedFirstFieldsSpace is of + matching capacity. Otherwise answer an error code identifying the anomaly. */ + + /* SpurPlanningCompactor>>#validRelocationPlanInPass: */ +static NoDbgRegParms sqInt +validRelocationPlanInPass(sqInt onePass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt destination; + sqInt followingWord; + usqInt followingWordAddress; + sqInt nMobiles; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt toFinger; + + nMobiles = 0; + toFinger = GIV(mobileStart); + GIV(anomaly) = null; + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(firstMobileObject))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstMobileObject); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift())))) { + nMobiles += 1; + destination = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (!(destination >= toFinger)) { + GIV(anomaly) = objOop; + return 1; + } + toFinger += bytesInBody(objOop); + if (oopisGreaterThan(objOop, GIV(lastMobileObject))) { + GIV(anomaly) = objOop; + return 2; + } + if (objOop == GIV(lastMobileObject)) { + return ((((((GIV(savedFirstFieldsSpace).top)) + BytesPerOop) - ((GIV(savedFirstFieldsSpace).start))) / BytesPerOop) == nMobiles + ? 0 + : 3); + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* N.B. written this way so that if there are no mobiles the expression evaluates to 0 in Smalltalk /and/ in C unsigned arithmetic. */ + return ((((((GIV(savedFirstFieldsSpace).top)) + BytesPerOop) - ((GIV(savedFirstFieldsSpace).start))) / BytesPerOop) == nMobiles + ? 0 + : 4); +} + + /* SpurSegmentInfo>>#segLimit */ +static NoDbgRegParms usqInt +segLimit(SpurSegmentInfo *self_in_SpurSegmentInfo) +{ + return ((self_in_SpurSegmentInfo->segSize)) + ((self_in_SpurSegmentInfo->segStart)); +} + + /* SpurSegmentManager>>#addSegmentOfSize: */ +static NoDbgRegParms SpurSegmentInfo * +addSegmentOfSize(sqInt ammount) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt allocatedSize; + sqInt i; + sqInt idx; + sqInt lastSegIndex; + SpurSegmentInfo *newSeg; + sqInt newSegIndex; + void *segAddress; + sqInt segAddressSqInt; + sqInt segIndex; + + newSegIndex = 0; + allocatedSize = 0; + if ((segAddress = sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto(ammount, firstGapOfSizeAtLeast(ammount), (&allocatedSize)))) { + segAddressSqInt = ((usqIntptr_t)segAddress); + + /* begin insertSegmentFor: */ + assert(segAddressSqInt >= (segLimit(&GIV(segments)[0]))); + if (GIV(numSegments) == GIV(numSegInfos)) { + allocateOrExtendSegmentInfos(); + } + assert(GIV(numSegments) < GIV(numSegInfos)); + segIndex = (lastSegIndex = GIV(numSegments) - 1); + GIV(numSegments) += 1; + while (1) { + if (segAddressSqInt >= ((((GIV(segments)[segIndex]).segSize)) + (((GIV(segments)[segIndex]).segStart)))) { + segIndex += 1; + for (idx = lastSegIndex; idx >= segIndex; idx += -1) { + GIV(segments)[idx + 1] = (GIV(segments)[idx]); + } + newSegIndex = segIndex; + goto l1; + } + segIndex -= 1; + } + /* end insertSegmentFor: */ +l1: + + /* Simulation insertion code duplicates entries if newSegIndex ~= numSegments - 1 */ + newSeg = (&(GIV(segments)[newSegIndex])); + (newSeg->segStart = ((usqIntptr_t)segAddress)); + (newSeg->segSize = allocatedSize); + (newSeg->swizzle = 0); + assert(!(segmentOverlap())); + bridgeFromto((&(GIV(segments)[newSegIndex - 1])), newSeg); + bridgeFromto(newSeg, (!(newSegIndex == (GIV(numSegments) - 1)) + ? (&(GIV(segments)[newSegIndex + 1])) + : 0)); + GIV(totalHeapSizeIncludingBridges) += allocatedSize; + + /* test isInMemory: */ + for (i = 0; i < GIV(numSegments); i += 1) { + assert(isInSegments(((GIV(segments)[i]).segStart))); + assert(isInSegments((segLimit(&GIV(segments)[i])) - BytesPerWord)); + assert((!(isInSegments(segLimit(&GIV(segments)[i])))) + || ((i < (GIV(numSegments) - 1)) + && ((segLimit(&GIV(segments)[i])) == (((GIV(segments)[i + 1]).segStart))))); + assert((!(isInSegments((((GIV(segments)[i]).segStart)) - BytesPerWord))) + || ((i > 0) + && ((segLimit(&GIV(segments)[i - 1])) == (((GIV(segments)[i]).segStart))))); + } + return newSeg; + } + + /* sent to the manager so that the simulator can increase memory to simulate a new segment */ + return null; +} + + +/* Adjust swizzles by firstSegmentShift. Also computes segStarts as + they were in the image when it was written, so that oops' segments + can be determined and hence oops correctly swizzled. */ + + /* SpurSegmentManager>>#adjustSegmentSwizzlesBy: */ +static NoDbgRegParms void +adjustSegmentSwizzlesBy(sqInt firstSegmentShift) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt oldBaseAddr; + SpurSegmentInfo *segInfo; + + oldBaseAddr = GIV(oldSpaceStart) - firstSegmentShift; + for (i = 0; i < GIV(numSegments); i += 1) { + segInfo = (&(GIV(segments)[i])); + (segInfo->segStart = ((segInfo->segStart)) + oldBaseAddr); + (segInfo->swizzle = ((segInfo->swizzle)) - oldBaseAddr); + } + GIV(canSwizzle) = 1; +} + + /* SpurSegmentManager>>#allBridgesMarked */ +static sqInt +allBridgesMarked(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt bridgeObj; + sqInt i; + usqInt numSlots; + + for (i = 0; i < GIV(numSegments); i += 1) { + /* begin bridgeAt: */ + /* begin bridgeFor: */ + address = (((((&(GIV(segments)[i])))->segSize)) + ((((&(GIV(segments)[i])))->segStart))) - (2 * BaseHeaderSize); + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + bridgeObj = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + assert(isValidSegmentBridge(bridgeObj)); + if (!((byteAt((void *)(bridgeObj + (markBitsByteOffset())))) & (1U << (markedBitByteShift())))) { + return 0; + } + } + return 1; +} + + +/* Increase the number of allocated segInfos by 16. */ + + /* SpurSegmentManager>>#allocateOrExtendSegmentInfos */ +static void +allocateOrExtendSegmentInfos(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt newNumSegs; + + if (!GIV(numSegInfos)) { + GIV(numSegInfos) = 16; + GIV(segments) = calloc(GIV(numSegInfos), sizeof(SpurSegmentInfo)); + return; + } + newNumSegs = GIV(numSegInfos) + 16; + GIV(segments) = realloc(GIV(segments), newNumSegs * (sizeof(SpurSegmentInfo))); + if (!GIV(segments)) { + error("out of memory; cannot allocate more segments"); + } + memset(GIV(segments) + GIV(numSegInfos), 0, (newNumSegs - GIV(numSegInfos)) * (sizeof(SpurSegmentInfo))); + GIV(numSegInfos) = newNumSegs; +} + + /* SpurSegmentManager>>#bridgeAt: */ +static NoDbgRegParms sqInt +bridgeAt(sqInt segIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + usqInt numSlots; + + /* begin bridgeFor: */ + address = (((((&(GIV(segments)[segIndex])))->segSize)) + ((((&(GIV(segments)[segIndex])))->segStart))) - (2 * BaseHeaderSize); + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + return (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); +} + + /* SpurSegmentManager>>#bridgeFor: */ +static NoDbgRegParms sqInt +bridgeFor(SpurSegmentInfo *aSegment) +{ + sqInt address; + usqInt numSlots; + + address = (((aSegment->segSize)) + ((aSegment->segStart))) - (2 * BaseHeaderSize); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + return (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); +} + + +/* Create a bridge from aSegment to the next segment, + or create a terminating bridge if there is no next segment. */ + + /* SpurSegmentManager>>#bridgeFrom:to: */ +static NoDbgRegParms void +bridgeFromto(SpurSegmentInfo *aSegment, SpurSegmentInfo *nextSegmentOrNil) +{ + sqInt bridgeSpan; + sqInt clifton; + usqInt segEnd; + + segEnd = ((aSegment->segSize)) + ((aSegment->segStart)); + + /* clifton is where the Avon bridge begins... */ + clifton = segEnd - (2 * BaseHeaderSize); + bridgeSpan = (nextSegmentOrNil + ? (((nextSegmentOrNil->segStart)) - segEnd) + (2 * BaseHeaderSize) + : 2 * BaseHeaderSize); + assert(bridgeSpan >= 0); + initSegmentBridgeWithBytesat(bridgeSpan, clifton); + + /* the revised bridge should get us to the new segment */ + assert((addressAfter(objectStartingAt(clifton))) == ((nextSegmentOrNil == null + ? segLimit(aSegment) + : (nextSegmentOrNil->segStart)))); +} + + /* SpurSegmentManager>>#checkSegments */ +static void +checkSegments(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + assert(GIV(numSegments) >= 1); + for (i = 0; i < GIV(numSegments); i += 1) { + assert(addressCouldBeObj(((GIV(segments)[i]).segStart))); + assert(isValidSegmentBridge(bridgeAt(i))); + } + assert(((segLimit(&GIV(segments)[GIV(numSegments) - 1])) - (bridgeSize())) == (GIV(endOfMemory))); +} + + +/* The image has been loaded, old segments reconstructed, and the heap + swizzled into a single contiguous segment. Collapse the segments into one. */ + + /* SpurSegmentManager>>#collapseSegmentsPostSwizzle */ +static void +collapseSegmentsPostSwizzle(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + SpurSegmentInfo *cascade0; + + GIV(canSwizzle) = 0; + GIV(numSegments) = 1; + + /* begin computeTotalHeapSizeIncludingBridges */ + GIV(totalHeapSizeIncludingBridges) = GIV(endOfMemory) - GIV(oldSpaceStart); + cascade0 = (&(GIV(segments)[0])); + (cascade0->segStart = GIV(oldSpaceStart)); + (cascade0->segSize = GIV(totalHeapSizeIncludingBridges)); + assert(isSegmentBridge(bridgeAt(0))); + assert((numSlotsOfAny(bridgeAt(0))) == 0); +} + + +/* Answer the segment limit of the first segment followed by a gap of at + least size bytes. + */ + + /* SpurSegmentManager>>#firstGapOfSizeAtLeast: */ +static NoDbgRegParms void * +firstGapOfSizeAtLeast(sqInt size) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt bridge; + sqInt i; + usqInt numSlots; + + for (i = 0; i <= (GIV(numSegments) - 2); i += 1) { + /* begin bridgeAt: */ + /* begin bridgeFor: */ + address = (((((&(GIV(segments)[i])))->segSize)) + ((((&(GIV(segments)[i])))->segStart))) - (2 * BaseHeaderSize); + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + bridge = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + if (((bytesInBody(bridge)) - (2 * BaseHeaderSize)) >= size) { + return ((void *)((((GIV(segments)[i]).segSize)) + (((GIV(segments)[i]).segStart)))); + } + } + return ((void *)((((GIV(segments)[GIV(numSegments) - 1]).segSize)) + (((GIV(segments)[GIV(numSegments) - 1]).segStart)))); +} + + /* SpurSegmentManager>>#isEmptySegment: */ +static NoDbgRegParms sqInt +isEmptySegment(SpurSegmentInfo *seg) +{ + sqInt address; + sqInt firstObj; + usqInt numSlots; + + address = (seg->segStart); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + firstObj = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + return (((longAt((void *)(firstObj))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) + && ((addressAfter(firstObj)) == ((((seg->segSize)) + ((seg->segStart))) - (2 * BaseHeaderSize))); +} + + /* SpurSegmentManager>>#isInSegments: */ +static NoDbgRegParms sqInt +isInSegments(usqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + for (i = 0; i < GIV(numSegments); i += 1) { + if (address < (((GIV(segments)[i]).segStart))) { + return 0; + } + if (address < ((((GIV(segments)[i]).segSize)) + (((GIV(segments)[i]).segStart)))) { + return 1; + } + } + return 0; +} + + +/* bridges bridge the gaps between segments. They are the last object in each + segment. + */ + + /* SpurSegmentManager>>#isValidSegmentBridge: */ +static NoDbgRegParms sqInt +isValidSegmentBridge(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((addressCouldBeObj(objOop)) + || (objOop == GIV(endOfMemory))) + && ((((longAt((void *)(objOop))) & (classIndexMask())) == (segmentBridgePun())) + && (((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) + || ((numSlotsOfAny(objOop)) == 0))); +} + + +/* Answer the the next non-empty segment or nil. The size of a segment + includes that of its bridge. A segment containing just a free object and a + bridge will still + have a size of manager bridgeSize after shortening it in + prepareForSnapshot. + */ + + /* SpurSegmentManager>>#nextNonEmptySegmentAfter: */ +static NoDbgRegParms SpurSegmentInfo * +nextNonEmptySegmentAfter(sqInt i) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt nextx; + + nextx = i; + while (1) { + if (((nextx += 1)) >= GIV(numSegments)) { + return null; + } + if ((((GIV(segments)[nextx]).segSize)) > (2 * BaseHeaderSize)) { + return (&(GIV(segments)[nextx])); + } + } + return 0; +} + + +/* shorten all segments by any trailing free space. */ + + /* SpurSegmentManager>>#prepareForSnapshot */ +static NeverInline void +prepareForSnapshot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cameFrom; + SpurSegmentInfo *cascade0; + usqInt chunkBytes; + sqInt followingWord; + usqInt followingWordAddress; + usqInt freeChunk; + sqInt i; + sqInt largeChild; + sqInt newEndOfMemory; + sqInt next; + sqInt node; + SpurSegmentInfo *seg; + SpurSegmentInfo *selfOfSegSize; + sqInt smallChild; + sqInt treeNode; + + checkSegments(); + for (i = 0; i < GIV(numSegments); i += 1) { + cascade0 = (&(GIV(segments)[i])); + (cascade0->savedSegSize = ((GIV(segments)[i]).segSize)); + (cascade0->lastFreeObject = null); + } + + /* Ideally finding the lastFreeObject of each segment would be + done in some linear pass through the heap. But for now KISS. */ + + /* begin freeTreeNodesDo: */ + treeNode = GIV(freeLists)[0]; + if (!treeNode) { + goto l2; + } + cameFrom = -1; + do { + assert((bytesInBody(treeNode)) >= ((numFreeLists()) * (allocationUnit()))); + smallChild = longAt((void *)((treeNode + BaseHeaderSize) + (3U << (shiftForWord())))); + largeChild = longAt((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + assert((smallChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), smallChild)))); + assert((largeChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), largeChild)))); + + /* apply if the node has no children, or it has no large children and we're + returning from the small child, or we're returning from the large child. */ + if (((smallChild == 0) + && (largeChild == 0)) + || ((largeChild + ? cameFrom == largeChild + : cameFrom == smallChild))) { + node = treeNode; + while (node != 0) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(node); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + next = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + next = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1: + if (((longAt((void *)(next))) & (classIndexMask())) == (segmentBridgePun())) { + seg = segmentContainingObj(node); + (seg->lastFreeObject = node); + node = 0; + } + else { + node = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + + /* and since we've applied we must move on up */ + cameFrom = treeNode; + treeNode = longAt((void *)((treeNode + BaseHeaderSize) + (2U << (shiftForWord())))); + } + else { + if ((smallChild != 0) + && (cameFrom != smallChild)) { + treeNode = smallChild; + } + else { + assert(largeChild != 0); + treeNode = largeChild; + } + cameFrom = -1; + } + } while(treeNode != 0); + /* end freeTreeNodesDo: */ +l2: + for (i = 0; i < GIV(numSegments); i += 1) { + if ((freeChunk = ((GIV(segments)[i]).lastFreeObject))) { + /* begin detachFreeObject: */ + chunkBytes = bytesInBody(freeChunk); + GIV(totalFreeOldSpace) -= chunkBytes; + unlinkFreeChunkchunkBytes(freeChunk, chunkBytes); + selfOfSegSize = (&(GIV(segments)[i])); + (selfOfSegSize->segSize = ((/* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk)) + (2 * BaseHeaderSize)) - (((GIV(segments)[i]).segStart))); + bridgeFromto((&(GIV(segments)[i])), (i < (GIV(numSegments) - 1) + ? (&(GIV(segments)[i + 1])) + : 0)); + } + } + + /* perhaps this should read + manager setEndOfMemory: 0; assimilateNewSegment: (segments at: numSegments - 1) */ + newEndOfMemory = ((((GIV(segments)[GIV(numSegments) - 1]).segSize)) + (((GIV(segments)[GIV(numSegments) - 1]).segStart))) - (2 * BaseHeaderSize); + + /* begin setEndOfMemory: */ + GIV(endOfMemory) = newEndOfMemory; + if (GIV(freeOldSpaceStart) > newEndOfMemory) { + GIV(freeOldSpaceStart) = newEndOfMemory; + } +} + + +/* Read numBytes of image data from f into memory at memoryBaseForImageRead. + Answer the number of bytes written. In addition, read each segment, build + up the + segment info for swizzling, while eliminating the bridge objects at the + end of each + segment that specify the distance to and the size of the subsequent + segment. */ + + /* SpurSegmentManager>>#readHeapFromImageFile:dataBytes: */ +static NoDbgRegParms sqInt +readHeapFromImageFiledataBytes(sqImageFile f, sqInt numBytes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bridge; + sqInt bridgehead; + usqInt bridgeSpan; + sqInt bytesRead; + usqInt newBase; + usqInt nextSegmentSize; + sqInt oldBase; + SpurSegmentInfo *segInfo; + sqInt totalBytesRead; + + allocateOrExtendSegmentInfos(); + + /* segment sizes include the two-header-word bridge at the end of each segment. */ + GIV(numSegments) = (totalBytesRead = 0); + + /* N.B. still must be adjusted by oldBaseAddr. */ + oldBase = 0; + newBase = GIV(oldSpaceStart); + nextSegmentSize = GIV(firstSegmentSize); + bridgehead = (GIV(firstSegmentSize) + GIV(oldSpaceStart)) - (2 * BaseHeaderSize); + while (1) { + segInfo = (&(GIV(segments)[GIV(numSegments)])); + (segInfo->segStart = oldBase); + (segInfo->segSize = nextSegmentSize); + (segInfo->swizzle = newBase - oldBase); + bytesRead = sqImageFileRead(pointerForOop(newBase), sizeof(char), nextSegmentSize, f); + if (bytesRead > 0) { + totalBytesRead += bytesRead; + } + if (bytesRead != nextSegmentSize) { + return totalBytesRead; + } + if (((GIV(numSegments) += 1)) >= GIV(numSegInfos)) { + allocateOrExtendSegmentInfos(); + } + bridge = bridgehead + BaseHeaderSize; + bridgeSpan = (byteAt((void *)(bridgehead + (numSlotsFieldByteOffset()))) + ? BytesPerOop * (((((usqInt)(((sqInt)((usqInt)((longAt((void *)(bridge - BaseHeaderSize)))) << 8)))))) >> 8) + : 0); + oldBase = (oldBase + nextSegmentSize) + bridgeSpan; + newBase = (newBase + nextSegmentSize) - (2 * BaseHeaderSize); + nextSegmentSize = ((usqInt)(long64At((void *)(bridge)))); + if (!(nextSegmentSize != 0)) break; + bridgehead = (bridgehead - (2 * BaseHeaderSize)) + nextSegmentSize; + } + + /* newBase should point just past the last bridge. all others should have been eliminated. */ + assert((newBase - (GIV(oldSpaceStart))) == (totalBytesRead - (GIV(numSegments) * (bridgeSize())))); + + /* set freeOldSpaceStart now for adjustAllOopsBy: */ + + /* begin setFreeOldSpaceStart: */ + GIV(freeOldSpaceStart) = newBase; + + /* we're done. nil firstSegmentSize for a subsequent snapshot. */ + GIV(firstSegmentSize) = null; + return totalBytesRead; +} + + +/* Restore all shortened segments to their proper size, re-freeing the + trailing space. + */ + + /* SpurSegmentManager>>#restorePostSnapshot */ +static void +restorePostSnapshot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt bytes; + usqInt freeChunk; + sqInt i; + sqInt newEndOfMemory; + SpurSegmentInfo *seg; + + + /* Set endOfMemory first, to avoid assert fails in freeChunkWithBytes:at:. */ + seg = (&(GIV(segments)[GIV(numSegments) - 1])); + if ((seg->lastFreeObject)) { + newEndOfMemory = (((seg->savedSegSize)) + ((seg->segStart))) - (2 * BaseHeaderSize); + + /* begin setEndOfMemory: */ + GIV(endOfMemory) = newEndOfMemory; + if (GIV(freeOldSpaceStart) > newEndOfMemory) { + GIV(freeOldSpaceStart) = newEndOfMemory; + } + } + else { + assert((GIV(endOfMemory)) == ((segLimit(seg)) - (bridgeSize()))); + } + for (i = (GIV(numSegments) - 1); i >= 0; i += -1) { + seg = (&(GIV(segments)[i])); + if ((freeChunk = (seg->lastFreeObject))) { + address = (((seg->segSize)) + ((seg->segStart))) - (2 * BaseHeaderSize); + (seg->segSize = (seg->savedSegSize)); + bridgeFromto(seg, (i < (GIV(numSegments) - 1) + ? (&(GIV(segments)[i + 1])) + : 0)); + bytes = ((((seg->segSize)) + ((seg->segStart))) - address) - (2 * BaseHeaderSize); + + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += bytes; + freeChunkWithBytesat(bytes, address); + } + } + checkSegments(); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } +} + + +/* Answer the segment containing an object. This is mostly for assert + checking, but + variations on the incremental GC may use it in anger. Binary search is (of + course) marginally slower than linear search for a single segment (e.g. in + a 720k object heap, + 67.1ms vs 61.3ms, or 9.5% slower to derive the segment containing every + old space + entity), but usefully faster for many segments (e.g. 92.7ms vs 116ms, or + 20% faster + in the same heap extended with enough large arrays to require 11 segments; + and this + is pessimal; there are fewer objects at high addresses since the large + arrays are there). */ + + /* SpurSegmentManager>>#segmentContainingObj: */ +SpurSegmentInfo * +segmentContainingObj(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt high; + sqInt low; + sqInt mid; + SpurSegmentInfo *seg; + + low = 0; + mid = GIV(numSegments) / 2; + high = GIV(numSegments) - 1; + do { + seg = (&(GIV(segments)[mid])); + if (oopisGreaterThanOrEqualTo(objOop, (seg->segStart))) { + if (mid == high) { + return (oopisLessThan(objOop, ((seg->segSize)) + ((seg->segStart))) + ? seg + : 0); + } + else { + low = mid; + mid = ((mid + high) + 1) / 2; + } + } + else { + high = mid - 1; + mid = (low + mid) / 2; + } + } while(low <= high); + return null; +} + + +/* Answers true if a segment overlaps with another one. */ + + /* SpurSegmentManager>>#segmentOverlap */ +static sqInt +segmentOverlap(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt endi; + usqInt endj; + sqInt i; + sqInt j; + usqInt starti; + usqInt startj; + + for (i = 0; i < GIV(numSegments); i += 1) { + starti = ((GIV(segments)[i]).segStart); + endi = (((GIV(segments)[i]).segSize)) + (((GIV(segments)[i]).segStart)); + for (j = 0; j < GIV(numSegments); j += 1) { + startj = ((GIV(segments)[j]).segStart); + endj = (((GIV(segments)[j]).segSize)) + (((GIV(segments)[j]).segStart)); + if (!(i == j)) { + if (!((starti < startj) + || (starti >= endj))) { + return 1; + } + if (!((endi <= startj) + || (endi >= endj))) { + return 1; + } + } + } + } + return 0; +} + + +/* Answer if any shrinkage was achieved. */ + + /* SpurSegmentManager>>#shrinkObjectMemory: */ +static NoDbgRegParms int +shrinkObjectMemory(usqInt delta) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + usqInt chunkBytes; + usqInt currentEnd; + sqInt deltaSqInt; + SpurSegmentInfo *emptySeg; + sqInt freeChunk; + sqInt i; + sqInt iSqInt; + sqInt j; + sqInt k; + usqInt numSlots; + SpurSegmentInfo *seg; + SpurSegmentInfo *segInfo; + usqInt shrinkage; + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCCheckFreeSpace)) == (GCCheckFreeSpace | GCCheckFreeSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + shrinkage = delta; + while (1) { + /* begin findEmptySegNearestInSizeTo: */ + emptySeg = null; + deltaSqInt = shrinkage; + for (k = 0; k < GIV(numSegments); k += 1) { + seg = (&(GIV(segments)[k])); + if (isEmptySegment(seg)) { + if (emptySeg) { + if ((shrinkage >= (((seg->segSize)) * 0.75)) + && ((SQABS(((sqInt) (((seg->segSize)) - shrinkage)))) < deltaSqInt)) { + emptySeg = seg; + deltaSqInt = SQABS(((sqInt) (((seg->segSize)) - shrinkage))); + } + } + else { + emptySeg = seg; + } + } + } + if ((!emptySeg) + || (((emptySeg->segSize)) > shrinkage)) { + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCCheckFreeSpace)) == (GCCheckFreeSpace | GCCheckFreeSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return shrinkage < delta; + } + shrinkage -= (emptySeg->segSize); + address = (emptySeg->segStart); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + freeChunk = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin detachFreeObject: */ + chunkBytes = bytesInBody(freeChunk); + GIV(totalFreeOldSpace) -= chunkBytes; + unlinkFreeChunkchunkBytes(freeChunk, chunkBytes); + + /* begin removeSegment: */ + i = 0; + for (iSqInt = 0; iSqInt < GIV(numSegments); iSqInt += 1) { + if (((emptySeg->segStart)) == (((GIV(segments)[iSqInt]).segStart))) { + i = iSqInt; + goto l1; + } + } + error("segment not found"); + /* end indexOfSegment: */ +l1: + assert(i > 0); + GIV(totalHeapSizeIncludingBridges) -= (emptySeg->segSize); + sqDeallocateMemorySegmentAtOfSize(((void *)((emptySeg->segStart))), (emptySeg->segSize)); + for (j = i; j <= (GIV(numSegments) - 2); j += 1) { + GIV(segments)[j] = (GIV(segments)[j + 1]); + } + GIV(numSegments) -= 1; + bridgeFromto((&(GIV(segments)[i - 1])), (i <= (GIV(numSegments) - 1) + ? (&(GIV(segments)[i])) + : 0)); + segInfo = (&(GIV(segments)[GIV(numSegments) - 1])); + + /* begin setLastSegment: */ + currentEnd = (((segInfo->segSize)) + ((segInfo->segStart))) - (2 * BaseHeaderSize); + if (currentEnd <= GIV(endOfMemory)) { + GIV(endOfMemory) = currentEnd; + if (GIV(freeOldSpaceStart) > currentEnd) { + GIV(freeOldSpaceStart) = currentEnd; + } + } + } + return 0; +} + + /* SpurSegmentManager>>#swizzleObj: */ +static NoDbgRegParms sqInt +swizzleObj(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + assert(GIV(canSwizzle)); + for (i = (GIV(numSegments) - 1); i >= 1; i += -1) { + if (objOop >= (((GIV(segments)[i]).segStart))) { + return objOop + (((GIV(segments)[i]).swizzle)); + } + } + return objOop + (((GIV(segments)[0]).swizzle)); +} + + +/* This ``slow'' count is for asserts only. */ + + /* SpurSegmentManager>>#totalBytesInSegments */ +static usqInt +totalBytesInSegments(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + usqInt total; + + total = 0; + for (i = 0; i < GIV(numSegments); i += 1) { + total += ((GIV(segments)[i]).segSize); + } + return total; +} + + /* SpurSegmentManager>>#writeImageSegmentsToFile: */ +static NoDbgRegParms sqInt +writeImageSegmentsToFile(sqImageFile aBinaryStream) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt total; + + assert(((GIV(endOfMemory)) == (segLimit(&GIV(segments)[GIV(numSegments) - 1]))) + || (((GIV(endOfMemory)) + (bridgeSize())) == (segLimit(&GIV(segments)[GIV(numSegments) - 1])))); + if (GIV(firstSegmentSize)) { + assert(GIV(firstSegmentSize) == (((GIV(segments)[0]).segSize))); + } + assert((((GIV(segments)[0]).segSize)) > 0); + total = 0; + for (i = 0; i < GIV(numSegments); i += 1) { + if ((((GIV(segments)[i]).segSize)) > (2 * BaseHeaderSize)) { + total += writeSegmentnextSegmenttoFile((&(GIV(segments)[i])), nextNonEmptySegmentAfter(i), aBinaryStream); + } + } + return total; +} + + +/* Write the segment contents, the size of and the distance to the next + segment to aBinaryStream. + */ + + /* SpurSegmentManager>>#writeSegment:nextSegment:toFile: */ +static NoDbgRegParms sqInt +writeSegmentnextSegmenttoFile(SpurSegmentInfo *segment, SpurSegmentInfo *nextSegment, sqImageFile aBinaryStream) +{ + usqLong firstSavedBridgeWord; + sqInt nWritten; + sqInt pier1; + sqInt pier2; + usqLong secondSavedBridgeWord; + + pier1 = (((segment->segSize)) + ((segment->segStart))) - (2 * BaseHeaderSize); + pier2 = pier1 + BaseHeaderSize; + assert(isValidSegmentBridge(bridgeFor(segment))); + assert((startOfObject(bridgeFor(segment))) == pier1); + + /* Temporarily change the bridge to bridge to the next non-empty segment. + The first double word of the bridge includes the bridge size in slots, and + hence specifies the distance to the next segment. The following double + word is replaced by the size of the next segment, or 0 if there isn't one. */ + firstSavedBridgeWord = long64At((void *)(pier1)); + secondSavedBridgeWord = long64At((void *)(pier2)); + bridgeFromto(segment, nextSegment); + long64Atput((void *)(pier2),(nextSegment + ? (nextSegment->segSize) + : 0)); + nWritten = sqImageFileWrite(((void *)((segment->segStart))), 1, (segment->segSize), aBinaryStream); + long64Atput((void *)(pier1),firstSavedBridgeWord); + long64Atput((void *)(pier2),secondSavedBridgeWord); + return nWritten; +} + + /* StackInterpreter>>#accessorDepthForExternalPrimitiveMethod: */ +static NoDbgRegParms sqInt +accessorDepthForExternalPrimitiveMethod(sqInt methodObj) +{ + sqInt flags; + sqInt lit; + + assert(isLinkedExternalPrimitive(methodObj)); + lit = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + flags = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFlagsIndex) << (shiftForWord())))))); + return (((flags >> 3))) >> SpurPrimitiveAccessorDepthShift; +} + + /* StackInterpreter>>#accessorDepthForPrimitiveMethod: */ +sqInt +accessorDepthForPrimitiveMethod(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + sqInt primIndex; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (aMethodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIndex = 0; + } + return ((primIndex == PrimNumberExternalCall) + && (isLinkedExternalPrimitive(aMethodObj)) + ? accessorDepthForExternalPrimitiveMethod(aMethodObj) + : ((sqInt)((primitiveMetadataTable[primIndex]))) >> SpurPrimitiveAccessorDepthShift); +} + + +/* Answer the current activeProcess. */ +/* Now used for FFI error reaping */ + + /* StackInterpreter>>#activeProcess */ +sqInt +activeProcess(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); +} + + +/* The various poll/select calls in the VM should attempt to tally the + ammount of time spent at idle here, so as to render the uptime value + meaningful. + */ + + /* StackInterpreter>>#addIdleUsecs: */ +void +addIdleUsecs(sqInt idleUsecs) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(statIdleUsecs) += idleUsecs; +} + + +/* Add the given process to the end of the given linked list + and set the backpointer of process to its new list. */ + + /* StackInterpreter>>#addLastLink:toList: */ +static NoDbgRegParms void +addLastLinktoList(sqInt proc, sqInt aList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt lastLink; + + assert(!((isForwarded(proc)))); + assert(!((isForwarded(aList)))); + assert((fetchPointerofObject(NextLinkIndex, proc)) == (nilObject())); + + /* begin isEmptyList: */ + assert(!(isForwarded(aList))); + if ((longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(FirstLinkIndex, aList, proc)); + assert(isNonImmediate(aList)); + if (oopisGreaterThanOrEqualTo(aList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(proc & (tagMask())))) + && (oopisLessThan(proc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))),proc); + } + else { + lastLink = longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord())))))); + assert(lastLink != proc); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(NextLinkIndex, lastLink, proc)); + assert(isNonImmediate(lastLink)); + if (oopisGreaterThanOrEqualTo(lastLink, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(proc & (tagMask())))) + && (oopisLessThan(proc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(lastLink + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(lastLink); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((lastLink + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord()))))),proc); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(LastLinkIndex, aList, proc)); + assert(isNonImmediate(aList)); + if (oopisGreaterThanOrEqualTo(aList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(proc & (tagMask())))) + && (oopisLessThan(proc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord()))))),proc); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(MyListIndex, proc, aList)); + assert(isNonImmediate(proc)); + if (oopisGreaterThanOrEqualTo(proc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(aList & (tagMask())))) + && (oopisLessThan(aList, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(proc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(proc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((proc + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),aList); +} + + /* StackInterpreter>>#allOnesAsCharStar */ +static char * +allOnesAsCharStar(void) +{ + return ((char *) (((usqInt) -1))); +} + + +/* for Cogit */ + + /* StackInterpreter>>#argumentCountOfClosure: */ +sqInt +argumentCountOfClosure(sqInt closurePointer) +{ + sqInt oop; + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((closurePointer + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + return (oop >> 3); +} + + /* StackInterpreter>>#argumentCountOfMethodHeader: */ +usqInt +argumentCountOfMethodHeader(sqInt header) +{ + return (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; +} + + /* StackInterpreter>>#argumentCountOf: */ +usqInt +argumentCountOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin argumentCountOfMethodHeader: */ + return (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; +} + + +/* Return the address of first indexable field of resulting array object, or + fail if + the instance variable does not contain an indexable bytes or words object. */ +/* Note: May be called by translated primitive code. */ + + /* StackInterpreter>>#arrayValueOf: */ +void * +arrayValueOf(sqInt arrayOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (/* isWordsOrBytes: */ + ((!(arrayOop & (tagMask())))) + && (isWordsOrBytesNonImm(arrayOop))) { + return ((void *) (pointerForOop(arrayOop + BaseHeaderSize))); + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; +} + + +/* Returns an integer object */ + + /* StackInterpreter>>#asciiOfCharacter: */ +static NoDbgRegParms sqInt +asciiOfCharacter(sqInt characterObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (((characterObj & (characterTag())) != 0)) { + return characterObj - ((characterTag()) - (smallIntegerTag())); + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return ConstZero; +} + + /* StackInterpreter>>#assertValidExecutionPointe:r:s: */ +void +assertValidExecutionPointers(usqInt lip, char *lifp, char *lisp) +{ + assertValidExecutionPointersimbarline(lip, lifp, lisp, !((((usqInt)(longAt(lifp + FoxMethod)))) < (startOfMemory())), __LINE__); +} + + +/* Order in the stackLimit checks is important because stackLimit is smashed + by interrupts. So always check for unsmashed value first to avoid race + condition. + */ + + /* StackInterpreter>>#assertValidStackLimits: */ +static NoDbgRegParms void +assertValidStackLimits(sqInt ln) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assertl((GIV(stackLimit) == ((GIV(stackPage)->realStackLimit))) + || (GIV(stackLimit) == (allOnesAsCharStar())), ln); + assertl((((GIV(stackPage)->stackLimit)) == ((GIV(stackPage)->realStackLimit))) + || (((GIV(stackPage)->stackLimit)) == (allOnesAsCharStar())), ln); +} + + +/* convert true and false (Smalltalk) to true or false(C) */ + + /* StackInterpreter>>#booleanValueOf: */ +sqInt +booleanValueOf(sqInt obj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (obj == GIV(trueObj)) { + return 1; + } + if (obj == GIV(falseObj)) { + return 0; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; +} + + +/* Re-enter the interpreter to execute a (non-Alien) callback. */ + + /* StackInterpreter>>#callbackEnter: */ +sqInt +callbackEnter(sqInt *callbackID) +{ + warning("callbackEnter: is obsolete"); + return 0; +} + + +/* Leave from a previous callback */ + + /* StackInterpreter>>#callbackLeave: */ +sqInt +callbackLeave(sqInt cbID) +{ + warning("callbackLeave: is obsolete"); + return 0; +} + + +/* Context switch should not be allowed on every method activation. In + particular the + implementation of ensure: and ifCurtailed: depends on there being no + suspension point + on failing primitive 198 (primitiveMarkUnwindMethod, + primitiveMarkUnwindMethod). slowPrimitiveResponse used to state + ``N.B. This means there is no suspension point on primitive failure + which methods such as ensure: and ifCurtailed: rely on.'' + Rather than prevent context switch on all primitives but the ones we + really need + to be suspension points (primitiveSignal et al) we choose to allow context + switch for all but primitiveMarkUnwindMethod and + PrimNumberNoContextSwitch. */ + + /* StackInterpreter>>#canContextSwitchIfActivating:header: */ +sqInt +canContextSwitchIfActivatingheader(sqInt theMethod, sqInt methodHeader) +{ + usqInt firstBytecode; + sqInt primitiveIndex; + + /* primitiveIndexOfMethod:header: */ + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (theMethod + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + return (primitiveIndex != PrimNumberUnwindMarker) + && (primitiveIndex != PrimNumberNoContextSwitchMarker); +} + + +/* Above ObjectMemory, arg must lie in range 0-255! */ + + /* StackInterpreter>>#characterForAscii: */ +sqInt +characterForAscii(sqInt ascii) +{ + return ((((usqInt)(ascii) << (numTagBits())))) + (characterTag()); +} + + +/* Ensure that all accessible objects in the heap are okay. */ +/* useful for VM debugging */ + + /* StackInterpreter>>#checkAllAccessibleObjectsOkay */ +sqInt +checkAllAccessibleObjectsOkay(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt ok; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + ok = 1; + + /* begin allObjectsDoSafely: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + if (((longAt((void *)(obj))) & (classIndexMask())) > (lastClassIndexPun())) { + ok = ok && (checkOkayFields(obj)); + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + return ok; +} + + +/* Check for a hit of the longRunningPrimitive probe and if so attempt to + signal the + longRunningPrimitiveCheckSemaphore. Answer if a process switch occurred as + a result. */ + + /* StackInterpreter>>#checkDeliveryOfLongRunningPrimitiveSignal */ +#if LRPCheck +static NeverInline sqInt +checkDeliveryOfLongRunningPrimitiveSignal(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((GIV(longRunningPrimitiveStopUsecs) > GIV(longRunningPrimitiveStartUsecs)) + && ((GIV(longRunningPrimitiveCheckSemaphore)) + && (GIV(longRunningPrimitiveSignalUndelivered)))) { + GIV(longRunningPrimitiveSignalUndelivered) = 0; + GIV(longRunningPrimitiveGCUsecs) = ((GIV(gcStartUsecs) < GIV(longRunningPrimitiveStopUsecs)) + && (GIV(statGCEndUsecs) > GIV(longRunningPrimitiveStartUsecs)) + ? GIV(statGCEndUsecs) - GIV(gcStartUsecs) + : 0); + + /* Signal the LRP check semaphore if it is present */ + return synchronousSignal(GIV(longRunningPrimitiveCheckSemaphore)); + } + + /* a hit + but not yet delivered */ + return 0; +} +#endif /* LRPCheck */ + + +/* Note: May be called by translated primitive code. */ + + /* StackInterpreter>>#checkedIntegerValueOf: */ +sqInt +checkedIntegerValueOf(sqInt intOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((((intOop) & 7) == 1)) { + return (intOop >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } +} + + +/* Check for possible interrupts and handle one if necessary. + Answer if a context switch has occurred. */ + + /* StackInterpreter>>#checkForEventsMayContextSwitch: */ +static NoDbgRegParms sqInt +checkForEventsMayContextSwitch(sqInt mayContextSwitch) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt heapSizePostGC; + usqLong now; + sqInt nowSqInt; + sqInt objOop; + sqInt sema; + sqInt sufficientSpaceAfterGCRV; + sqInt switched; + + GIV(statCheckForEvents) += 1; + + /* restore the stackLimit if it has been smashed. */ + + /* begin restoreStackLimit */ + (GIV(stackPage)->stackLimit = (GIV(stackPage)->realStackLimit)); + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + + /* Allow the platform to do anything it needs to do synchronously. */ + ioSynchronousCheckForEvents(); + + /* begin checkCogCompiledCodeCompactionCalledFor */ + if (GIV(cogCompiledCodeCompactionCalledFor)) { + commenceCogCompiledCodeCompaction(); + } + if (GIV(needGCFlag)) { + /* sufficientSpaceAfterGC: runs the incremental GC and + then, if not enough space is available, the fullGC. */ + + /* begin sufficientSpaceAfterGC: */ + scavengingGCTenuringIf(TenureByAge); + heapSizePostGC = ((/* begin totalOldSpaceCapacity */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + if (((((double) (heapSizePostGC - GIV(heapSizeAtPreviousGC)) )) / GIV(heapSizeAtPreviousGC)) >= GIV(heapGrowthToSizeGCRatio)) { + fullGC(); + } + else { + if (GIV(totalFreeOldSpace) > (GIV(shrinkThreshold) * 2)) { + /* begin attemptToShrink */ + if ((GIV(totalFreeOldSpace) > GIV(shrinkThreshold)) + && ((GIV(totalFreeOldSpace) > GIV(growHeadroom)) + && (shrinkObjectMemory((((GIV(totalFreeOldSpace) - GIV(growHeadroom)) < GIV(growHeadroom)) ? GIV(growHeadroom) : (GIV(totalFreeOldSpace) - GIV(growHeadroom))))))) { + GIV(statShrinkMemory) += 1; + } + sufficientSpaceAfterGCRV = 1; + goto l1; + } + } + + /* Also attempt to shrink if there is plenty of free space and no need to GC */ + while ((GIV(totalFreeOldSpace) < GIV(growHeadroom)) + && ((growOldSpaceByAtLeast(0)))) { + if (GIV(totalFreeOldSpace) >= GIV(growHeadroom)) { + sufficientSpaceAfterGCRV = 1; + goto l1; + } + } + + /* Answer false if space is low */ + sufficientSpaceAfterGCRV = GIV(lowSpaceThreshold) <= GIV(totalFreeOldSpace); + /* end sufficientSpaceAfterGC: */ +l1: + if (!sufficientSpaceAfterGCRV) { + setSignalLowSpaceFlagAndSaveProcess(); + } + } + if (!mayContextSwitch) { + return 0; + } + + /* begin checkProfileTickIfSwitched */ + if ((GIV(nextProfileTick) <= 0) + || ((ioHighResClock()) < GIV(nextProfileTick))) { + switched = 0; + goto l2; + } + + /* Take a sample (if not already done so) for the profiler. */ + if (!GIV(profileProcess)) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + GIV(profileProcess) = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + } + GIV(profileMethod) = null; + + /* Zero nextProfileTick because signalling the semaphore is when the VM effectively delivers the sample. */ + + /* begin zeroNextProfileTick */ + GIV(nextProfileTick) = 0; + switched = synchronousSignal(GIV(profileSemaphore)); + /* end checkProfileTickIfSwitched */ +l2: +# if LRPCheck + if (checkDeliveryOfLongRunningPrimitiveSignal()) { + switched = 1; + } +# endif + + if (GIV(signalLowSpace)) { + /* signalLowSpace: */ + GIV(signalLowSpace) = 0; + sema = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheLowSpaceSemaphore) << (shiftForWord())))))); + if ((sema != GIV(nilObj)) + && (synchronousSignal(sema))) { + switched = 1; + } + } + nowSqInt = (now = ioUTCMicroseconds()); + + /* begin checkInvokeIOProcessEvents: */ + if (nowSqInt >= GIV(nextPollUsecs)) { + GIV(statIOProcessEvents) += 1; + ioProcessEvents(); + + /* msecs to wait before next call to ioProcessEvents. Note that strictly + speaking we might need to update 'now' at this point since + ioProcessEvents could take a very long time on some platforms */ + GIV(nextPollUsecs) = nowSqInt + 20000; + } + if (GIV(interruptPending)) { + GIV(interruptPending) = 0; + + /* reset interrupt flag */ + sema = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheInterruptSemaphore) << (shiftForWord())))))); + if ((sema != GIV(nilObj)) + && (synchronousSignal(sema))) { + switched = 1; + } + } + if (GIV(nextWakeupUsecs)) { + if (now >= GIV(nextWakeupUsecs)) { + GIV(nextWakeupUsecs) = 0; + + /* set timer interrupt to 0 for 'no timer' */ + sema = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord())))))); + if ((sema != GIV(nilObj)) + && (synchronousSignal(sema))) { + switched = 1; + } + } + } + + /* signal any pending finalizations */ + if (GIV(pendingFinalizationSignals) > 0) { + GIV(pendingFinalizationSignals) = 0; + sema = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheFinalizationSemaphore) << (shiftForWord())))))); + if ((sema != GIV(nilObj)) + && (synchronousSignal(sema))) { + switched = 1; + } + } + + /* signal all semaphores in semaphoresToSignal */ + if (signalExternalSemaphores()) { + switched = 1; + } + return switched; +} + + +/* Support for embedded images. Check that the first few bytes of a potential + header and answer if it + looks like something the VM can load, + The method checks the first three fields of the header (magic, header size + & data size) & the total size. + The magic number should be correct. + The header size should be correct. + The size of the data should be at least as long as the headerSize plus the + data size in the header */ +/* Need at least headerSize bytes; no point going further if not... */ + + /* StackInterpreter>>#checkImageHeaderFromBytes:AndSize: */ +sqInt +checkImageHeaderFromBytesAndSize(char *bytes, sqInt totalSize) +{ + sqInt dataSize; + sqInt headerSize; + int version; + + if (totalSize < (BytesPerWord * 16)) { + return 0; + } + version = long32At(bytes); + headerSize = long32At(bytes + 4); + dataSize = longAt(bytes + 8); + if (!((((version | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */)) { + if (!(((((SQ_SWAP_4_BYTES(version)) | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */)) { + return 0; + } + headerSize = SQ_SWAP_8_BYTES(headerSize); + dataSize = SQ_SWAP_8_BYTES(dataSize); + } + return (headerSize == (BytesPerWord * 16)) + && (totalSize >= (headerSize + dataSize)); +} + + +/* Read and verify the image file version number and answer it. + Assign through rawVersionPtr the unswapped version number. The caller can + then infer if the given image file needs to be byte-swapped by seeing if + the returned value + equals tha assigned through rawVersionPtr. + 0 is answered if no valid version could be found. */ + + /* StackInterpreter>>#checkImageVersionFrom:startingAt:assignRawVersion: */ +static NoDbgRegParms sqInt +checkImageVersionFromstartingAtassignRawVersion(sqImageFile f, squeakFileOffsetType imageOffset, sqInt *rawVersionPtr) +{ + sqInt version; + int w; + + rawVersionPtr[0] = 0; + + /* check the version number */ + sqImageFileSeek(f, imageOffset); + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + version = w; + rawVersionPtr[0] = version; + if ((((version | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */) { + return version; + } + + /* try with bytes reversed */ + sqImageFileSeek(f, imageOffset); + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + version = SQ_SWAP_4_BYTES(w); + if (!(/* isLikelyImageVersion: */ + (((rawVersionPtr[0]) / 100) == 65) + || (((rawVersionPtr[0]) / 1000) == 68))) { + rawVersionPtr[0] = version; + } + if ((((version | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */) { + return version; + } + + /* Note: The following is only meaningful if not reading an embedded image */ + if (!imageOffset) { + sqImageFileSeek(f, 0x200); + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + version = w; + if (!(/* isLikelyImageVersion: */ + (((rawVersionPtr[0]) / 100) == 65) + || (((rawVersionPtr[0]) / 1000) == 68))) { + rawVersionPtr[0] = version; + } + if ((((version | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */) { + return version; + } + + /* try skipping the first 512 bytes with bytes reversed */ + sqImageFileSeek(f, 0x200); + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + version = SQ_SWAP_4_BYTES(w); + if (!(/* isLikelyImageVersion: */ + (((rawVersionPtr[0]) / 100) == 65) + || (((rawVersionPtr[0]) / 1000) == 68))) { + rawVersionPtr[0] = version; + } + if ((((version | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */) { + return version; + } + } + + /* try skipping the first 512 bytes (prepended by certain Mac file transfer utilities) */ + if (!(/* isLikelyImageVersion: */ + (((rawVersionPtr[0]) / 100) == 65) + || (((rawVersionPtr[0]) / 1000) == 68))) { + rawVersionPtr[0] = version; + } + return 0; +} + + +/* Perform an integrity/leak check using the heapMap. Assume + clearLeakMapAndMapAccessibleObjects has set a bit at each + object's header. Check that all oops in the interpreter's state + points to a header. Answer 0 if all checks pass. */ + + /* StackInterpreter>>#checkInterpreterIntegrity */ +static sqInt +checkInterpreterIntegrity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt flags; + + flags = 0; + if (!(checkOopIntegritynamed(GIV(specialObjectsOop), "specialObjectsOop"))) { + flags = 1; + } + + /* No longer check messageSelector; it is ephemeral, not living beyond message lookup. + (objectMemory isNonImmediate: messageSelector) ifTrue: + [(objectMemory checkOopIntegrity: messageSelector named: 'messageSelector') ifFalse: + [flags := flags + N]]. */ + if (!(checkOopIntegritynamed(GIV(newMethod), "newMethod"))) { + flags += 2; + } + + /* No longer check lkupClass; it is ephemeral, not living beyond message lookup. + (objectMemory checkOopIntegrity: lkupClass named: 'lkupClass') ifFalse: + [flags := flags + N]. */ + if (GIV(profileProcess)) { + if (!(checkOopIntegritynamed(GIV(profileProcess), "profileProcess"))) { + flags += 4; + } + } + if (GIV(profileMethod)) { + if (!(checkOopIntegritynamed(GIV(profileMethod), "profileMethod"))) { + flags += 8; + } + } + if (GIV(profileSemaphore)) { + if (!(checkOopIntegritynamed(GIV(profileSemaphore), "profileSemaphore"))) { + flags += 16; + } + } + if (GIV(tempOop)) { + if (!(checkOopIntegritynamed(GIV(tempOop), "tempOop"))) { + flags += 32; + } + } + if (GIV(tempOop2)) { + if (!(checkOopIntegritynamed(GIV(tempOop2), "tempOop2"))) { + flags += 64; + } + } + if (!(checkLogIntegrity())) { + flags += 128; + } + return flags; +} + + +/* Another version of isWidowedContext: for debugging. + This will not bereave a widowed context. */ + + /* StackInterpreter>>#checkIsStillMarriedContext:currentFP: */ +static NoDbgRegParms sqInt +checkIsStillMarriedContextcurrentFP(sqInt aContext, char *currentFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *limitFP; + char *maybeFP; + sqInt maybeFrameCtxt; + sqInt referent; + sqInt senderOop; + StackPage *thePage; + + if (!((/* isContext: */ + ((!(aContext & (tagMask())))) + && (((longAt((void *)(aContext))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)))) { + return 0; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + maybeFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(maybeFP), GIV(pages)); + limitFP = ((thePage == GIV(stackPage)) + && (currentFP) + ? currentFP + : (thePage->headFP)); + if (!((maybeFP >= limitFP) + && (((!((((sqInt)(((char *)(longAt(maybeFP + FoxSavedFP)))))) & (tagMask())))) + && ((((/* begin withSmallIntegerTags: */ + assert(((oopForPointer(((char *)(longAt(maybeFP + FoxSavedFP))))) & (BytesPerWord - 1)) == 0), + (oopForPointer(((char *)(longAt(maybeFP + FoxSavedFP))))) + (smallIntegerTag()))) == (longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) + && (/* frameHasContext: */ + ((((usqInt)(longAt(maybeFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(maybeFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((maybeFP + FoxIFrameFlags) + 2)) != 0)))))) { + return 0; + } + maybeFrameCtxt = longAt(maybeFP + FoxThisContext); + + /* On Spur we need to follow the context to check for a match, but since the VM is + only speculating about maybeFrame being a frame, and only speculating about + maybeContext being a context, we need to be sure before we can safely follow. */ + if ((!(isFree(thePage))) + && ((isFrameonPage(maybeFP, thePage)) + && ((!((longAt((void *)(maybeFrameCtxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(maybeFrameCtxt)); + referent = longAt((void *)((maybeFrameCtxt + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + maybeFrameCtxt = referent; + } + return maybeFrameCtxt == aContext; +} + + +/* Check the log for leaks. The trace log is a circular buffer of pairs of + entries. If there is an entry at traceLogIndex - 3 \\ TraceBufferSize it + has entries. If + there is something at traceLogIndex it has wrapped. */ + + /* StackInterpreter>>#checkLogIntegrity */ +static sqInt +checkLogIntegrity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt limit; + sqInt ok; + sqInt oop; + sqInt remainder; + + limit = /* safe:mod: */ + (((remainder = (GIV(traceLogIndex) - 3) % TraceBufferSize)) < 0 + ? remainder + TraceBufferSize + : remainder); + if (!(GIV(traceLog)[limit])) { + return 1; + } + if (GIV(traceLog)[GIV(traceLogIndex)]) { + limit = TraceBufferSize - 3; + } + ok = 1; + for (i = 0; i <= limit; i += 3) { + oop = GIV(traceLog)[i]; + if (!(((oop & (tagMask())) != 0))) { + if (!(checkOopIntegritynamedindex(oop, "traceLog", i))) { + ok = 0; + } + } + oop = GIV(traceLog)[i + 1]; + if (!(((oop & (tagMask())) != 0))) { + if (!(checkOopIntegritynamedindex(oop, "traceLog", i + 1))) { + ok = 0; + } + } + } + return ok; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#checkOkayInterpreterObjects: */ +sqInt +checkOkayInterpreterObjects(sqInt writeBack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int i; + sqInt ok; + sqInt oop; + sqIntptr_t oopOrZero; + + ok = 1; + ok = ok && (checkOkayFields(GIV(nilObj))); + ok = ok && (checkOkayFields(GIV(falseObj))); + ok = ok && (checkOkayFields(GIV(trueObj))); + ok = ok && (checkOkayFields(GIV(specialObjectsOop))); + ok = ok && (checkOkayFields(GIV(messageSelector))); + ok = ok && (checkOkayFields(GIV(newMethod))); + ok = ok && (checkOkayFields(GIV(lkupClass))); + for (i = 0; i < MethodCacheEntries; i += MethodCacheEntrySize) { + oopOrZero = GIV(methodCache)[i + MethodCacheSelector]; + if (oopOrZero) { + ok = ok && (checkOkayFields(GIV(methodCache)[i + MethodCacheSelector])); + ok = ok && (checkOkayFields(GIV(methodCache)[i + MethodCacheMethod])); + } + } + for (i = 1; i <= GIV(remapBufferCount); i += 1) { + oop = GIV(remapBuffer)[i]; + if (!(((oop & (tagMask())) != 0))) { + ok = ok && (checkOkayFields(oop)); + } + } + ok = ok && (checkOkayStackZone(writeBack)); + return ok; +} + + /* StackInterpreter>>#checkOkayStackPage: */ +static NoDbgRegParms sqInt +checkOkayStackPage(StackPage *thePage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + char *frameRcvrOffset; + sqInt ok; + sqInt oop; + char *theFP; + char *theSP; + + callerFP = ((char *) 0); + theSP = (thePage->headSP); + theFP = (thePage->headFP); + ok = 1; + + /* Skip the instruction pointer on top of stack of inactive pages. */ + if (!(thePage == GIV(stackPage))) { + theSP += BytesPerWord; + } + while (1) { + frameRcvrOffset = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + while (theSP <= frameRcvrOffset) { + oop = longAt(theSP); + if (!((((oop & (tagMask())) != 0)) + || (checkOkayFields(oop)))) { + ok = 0; + } + theSP += BytesPerWord; + } + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + if (!(checkOkayFields(longAt(theFP + FoxThisContext)))) { + ok = 0; + } + } + if (!(checkOkayFields(/* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod))))) { + ok = 0; + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + theFP = callerFP; + } + + /* caller ip is frameCallerContext in a base frame */ + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if (!((((oop & (tagMask())) != 0)) + || (checkOkayFields(oop)))) { + ok = 0; + } + theSP += BytesPerWord; + } + return ok; +} + + +/* Check that all objects in the stack zone are okay */ + + /* StackInterpreter>>#checkOkayStackZone: */ +static NoDbgRegParms sqInt +checkOkayStackZone(sqInt writeBack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt ok; + StackPage *thePage; + + if (writeBack) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + ok = 1; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + ok = ok && (checkOkayStackPage(thePage)); + } + } + return ok; +} + + +/* Perform an integrity/leak check using the heapMap. Assume + clearLeakMapAndMapAccesibleObjects has set a bit at each + object's header. Scan all objects accessible from the stack + checking that every pointer points to a header. Answer if no + dangling pointers were detected. */ + + /* StackInterpreter>>#checkStackIntegrity */ +static sqInt +checkStackIntegrity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + CogMethod *cogMethod; + char *frameRcvrOffset; + sqInt i; + sqInt methodField; + sqInt ok; + usqInt oop; + char *theFP; + StackPage *thePage; + char *theSP; + + callerFP = ((char *) 0); + ok = 1; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + if (thePage == GIV(stackPage)) { + theSP = GIV(stackPointer); + theFP = GIV(framePointer); + } + else { + theSP = (thePage->headSP); + theFP = (thePage->headFP); + } + + /* Skip the instruction pointer on top of stack of inactive pages. */ + if (!(thePage == GIV(stackPage))) { + theSP += BytesPerWord; + } + while (1) { + frameRcvrOffset = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + while (theSP <= frameRcvrOffset) { + oop = longAt(theSP); + if (((!(oop & (tagMask())))) + && ((heapMapAtWord(pointerForOop(oop))) == 0)) { + printFrameThingandFrameat("object leak in frame temp", theFP, theSP); + ok = 0; + } + theSP += BytesPerWord; + } + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + oop = longAt(theFP + FoxThisContext); + if ((((oop & (tagMask())) != 0)) + || ((heapMapAtWord(pointerForOop(oop))) == 0)) { + printFrameThingandFrameat("object leak in frame ctxt", theFP, theFP + FoxThisContext); + ok = 0; + } + if (!(/* isContext: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + printFrameThingandFrameat("frame ctxt should be context", theFP, theFP + FoxThisContext); + ok = 0; + } + if (!((/* isContext: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (((((longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)))) { + printFrameThingandFrameat("frame ctxt should be married", theFP, theFP + FoxThisContext); + ok = 0; + } + if (!((/* isContext: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && ((frameOfMarriedContext(oop)) == theFP))) { + printFrameThingandFrameat("frame ctxt should be married to this frame ", theFP, theFP + FoxThisContext); + ok = 0; + } + } + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* begin mframeHomeMethod: */ + methodField = longAt(theFP + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + cogMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + goto l1; + } + cogMethod = ((CogMethod *) (methodField & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + if (!(heapMapAtWord(cogMethod))) { + printFrameThingandFrameat("object leak in mframe mthd", theFP, theFP + FoxMethod); + ok = 0; + } + } + else { + oop = longAt(theFP + FoxMethod); + if ((((oop & (tagMask())) != 0)) + || ((heapMapAtWord(pointerForOop(oop))) == 0)) { + printFrameThingandFrameat("object leak in iframe mthd", theFP, theFP + FoxMethod); + ok = 0; + } + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + theFP = callerFP; + } + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if (((!(oop & (tagMask())))) + && ((heapMapAtWord(pointerForOop(oop))) == 0)) { + printFrameThingandFrameat("object leak in frame arg", theFP, theSP); + ok = 0; + } + theSP += BytesPerWord; + } + } + } + return ok; +} + + +/* Version of stackPointerForMaybeMarriedContext: with no side-effects (does + not widow). + Used for assertion checking. Safe only in external primitives + (framePointer valid). + Answer the stackPointer of a Context. */ + + /* StackInterpreter>>#checkStackPointerForMaybeMarriedContext: */ +static NoDbgRegParms sqInt +checkStackPointerForMaybeMarriedContext(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + sqInt methodPointer; + sqInt sp; + sqInt spSqInt; + + if (checkIsStillMarriedContextcurrentFP(aContext, GIV(framePointer))) { + sp = checkStackPointerIndexForFrame(frameOfMarriedContext(aContext)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(aContext))); + return sp; + } + if (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + methodPointer = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* begin argumentCountOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin argumentCountOfMethodHeader: */ + return (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; + } + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + return 0; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + return (spSqInt >> 3); +} + + +/* Version of stackPointerIndexForFrame: that does not depend on writing back + head frame pointers. + Used for assertion checking. Safe only in external primitives + (framePointer valid). + Answer the 0-based index rel to the given frame. + (This is what stackPointer used to be before conversion to pointer) */ +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#checkStackPointerIndexForFrame: */ +static NoDbgRegParms sqInt +checkStackPointerIndexForFrame(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *aFrame; + char *prevFrame; + char *startFrame; + StackPage *thePage; + char *theSP; + + if (theFP == GIV(framePointer)) { + return /* stackPointerIndexForFrame:WithSP: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (((usqInt)(((theFP + FoxMFReceiver) - GIV(stackPointer)))) >> (shiftForWord())) + (((mframeCogMethod(theFP))->cmNumArgs)) + : (((usqInt)(((theFP + FoxIFReceiver) - GIV(stackPointer)))) >> (shiftForWord())) + (byteAt((theFP + FoxIFrameFlags) + 1))); + } + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + startFrame = (thePage == GIV(stackPage) + ? GIV(framePointer) + : (thePage->headFP)); + + /* begin findSPOrNilOf:on:startingFrom: */ + prevFrame = ((char *) 0); + if (startFrame == theFP) { + if (((thePage->headSP)) >= startFrame) { + /* If the SP is invalid return the pointer to the receiver field. */ + theSP = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + goto l1; + } + + /* Skip the instruction pointer on top of stack of inactive pages. */ + theSP = (thePage == GIV(stackPage) + ? (thePage->headSP) + : ((thePage->headSP)) + BytesPerWord); + goto l1; + } + aFrame = startFrame; + while (1) { + prevFrame = aFrame; + aFrame = ((char *)(longAt(aFrame + FoxSavedFP))); + if (!(aFrame != 0)) break; + if (theFP == aFrame) { + /* begin frameCallerSP: */ + assert(!(isBaseFrame(prevFrame))); + theSP = (prevFrame + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(prevFrame + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(prevFrame))->cmNumArgs) + : byteAt((prevFrame + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + goto l1; + } + } + theSP = null; + /* end findSPOrNilOf:on:startingFrom: */ +l1: + if (!theSP) { + return -1; + } + return /* stackPointerIndexForFrame:WithSP: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (((usqInt)(((theFP + FoxMFReceiver) - theSP))) >> (shiftForWord())) + (((mframeCogMethod(theFP))->cmNumArgs)) + : (((usqInt)(((theFP + FoxIFReceiver) - theSP))) >> (shiftForWord())) + (byteAt((theFP + FoxIFrameFlags) + 1))); +} + + +/* Check if aClass's name is className */ + + /* StackInterpreter>>#classNameOf:Is: */ +static NoDbgRegParms sqInt +classNameOfIs(sqInt aClass, char *className) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt i; + sqInt length; + sqInt name; + usqInt numSlots; + char *srcName; + + if (((/* begin numSlotsOf: */ + assert((classIndexOf(aClass)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(aClass + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aClass - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) <= GIV(classNameIndex)) { + return 0; + } + name = longAt((void *)((aClass + BaseHeaderSize) + ((((usqInt)(GIV(classNameIndex)) << (shiftForWord())))))); + if (!(/* isBytes: */ + ((!(name & (tagMask())))) + && (((byteAt((void *)(name + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + return 0; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(name + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + length = ((((/* begin numSlotsOf: */ + assert((classIndexOf(name)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(name + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(name - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + srcName = ((char *) (arrayValueOf(name))); + for (i = 0; i < length; i += 1) { + if (!((srcName[i]) == (className[i]))) { + return 0; + } + } + + /* Check if className really ends at this point */ + return (className[length]) == 0; +} + + /* StackInterpreter>>#clearTraceLog */ +void +clearTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + GIV(traceLogIndex) = 0; + for (i = 0; i < TraceBufferSize; i += 1) { + GIV(traceLog)[i] = 0; + } +} + + +/* For asserts. Check that theIP maps back correctly to the context's pc. + The CallPrimitive bytecode presents a complication. */ + + /* StackInterpreter>>#context:hasValidInversePCMappingOf:in: */ +static NoDbgRegParms sqInt +contexthasValidInversePCMappingOfin(sqInt aContext, sqInt theIP, char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt encodedip; + sqInt header; + sqInt methodHeader; + sqInt methodObj; + sqInt pc; + + pc = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + encodedip = contextInstructionPointerframe(theIP, theFP); + return (pc == encodedip) + || (((methodObj = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord()))))))), + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)), + (header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))))), + (methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader)))), + (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) + && ((((encodedip >> 3)) - ((pc >> 3))) == 3 /* sizeOfCallPrimitiveBytecode: */))); +} + + +/* for Cogit */ + + /* StackInterpreter>>#copiedValueCountOfClosure: */ +sqInt +copiedValueCountOfClosure(sqInt closureObj) +{ + usqInt numSlots; + + return (((((longAt((void *)((closureObj + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1) + ? (/* begin copiedValueCountOfVanillaClosure: */ + assert(isVanillaBlockClosure(closureObj)), + ((/* begin numSlotsOf: */ + assert((classIndexOf(closureObj)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(closureObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(closureObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - ClosureFirstCopiedValueIndex) + : (/* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(closureObj)))), + ((/* begin numSlotsOf: */ + assert((classIndexOf(closureObj)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(closureObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(closureObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex)); +} + + +/* This entry point needs to be implemented for the interpreter proxy. + Since BitBlt is now a plugin we need to look up BitBltPlugin:=copyBits + and call it. This entire mechanism should eventually go away and be + replaced with a dynamic lookup from BitBltPlugin itself but for backward + compatibility this stub is provided */ + + /* StackInterpreter>>#copyBits */ +sqInt +copyBits(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void *fn; + + fn = ioLoadFunctionFrom("copyBits", "BitBltPlugin"); + if (!fn) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return ((sqInt (*)(void))fn)(); +} + + +/* This entry point needs to be implemented for the interpreter proxy. + Since BitBlt is now a plugin we need to look up + BitBltPlugin:=copyBitsFrom:to:at: and call it. This entire mechanism + should eventually go away and be + replaced with a dynamic lookup from BitBltPlugin itself but for backward + compatibility this stub is provided + */ + + /* StackInterpreter>>#copyBitsFrom:to:at: */ +sqInt +copyBitsFromtoat(sqInt x0, sqInt x1, sqInt y) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void *fn; + + fn = ioLoadFunctionFrom("copyBitsFromtoat", "BitBltPlugin"); + if (!fn) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return ((sqInt (*)(sqInt, sqInt, sqInt))fn)(x0, x1, y); +} + + /* StackInterpreter>>#couldBeProcess: */ +static NoDbgRegParms NeverInline sqInt +couldBeProcess(sqInt oop) +{ + return (addressCouldBeObj(oop)) + && ((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + && ((!(((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (((lengthOf(oop)) > MyListIndex) + && (isContext(longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))))))))); +} + + +/* Bundle up the selector, arguments and lookupClass into a Message object. + In the process it pops the arguments off the stack, and pushes the message + object. + This can then be presented as the argument of e.g. #doesNotUnderstand: */ + + /* StackInterpreter>>#createActualMessageTo: */ +static NoDbgRegParms void +createActualMessageTo(sqInt lookupClass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt argumentArray; + sqInt i; + usqInt message; + usqInt newObj; + usqInt numBytes; + + + /* This is a useful break-point */ + assert((isImmediate(GIV(messageSelector))) + || (addressCouldBeObj(GIV(messageSelector)))); + + /* begin mnuBreakpoint:receiver: */ + mnuBreakpointreceiver(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), null); + assert((GIV(argumentCount) >= 0) + && ((knownClassAtIndex(ClassArrayCompactIndex)) != GIV(nilObj))); + assert((arrayFormat()) == (instSpecOfClass(knownClassAtIndex(ClassArrayCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(GIV(argumentCount) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((GIV(argumentCount) < 1 + ? 8 /* allocationUnit */ + : GIV(argumentCount) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + argumentArray = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) GIV(argumentCount))) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + GIV(freeStart) += numBytes; + argumentArray = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert(((MessageLookupClassIndex + 1) >= 0) + && ((knownClassAtIndex(ClassMessageCompactIndex)) != GIV(nilObj))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(knownClassAtIndex(ClassMessageCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert((MessageLookupClassIndex + 1) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (((MessageLookupClassIndex + 1) < 1 + ? 8 /* allocationUnit */ + : (MessageLookupClassIndex + 1) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + message = 0; + goto l2; + } + } + long64Atput((void *)(newObj),((((((usqLong) (MessageLookupClassIndex + 1))) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + ClassMessageCompactIndex); + GIV(freeStart) += numBytes; + message = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l2: + + /* Since the array is new can use unchecked stores. */ + for (i = ((GIV(argumentCount) - 1) * BytesPerOop); i >= 0; i += (-BytesPerOop)) { + longAtput((void *)((argumentArray + BaseHeaderSize) + i),popStack()); + } + + /* Since message is new can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(message)) + && (!(isForwarded(message)))); + assert(validStorePointerUncheckedArgs(MessageSelectorIndex, message, GIV(messageSelector))); + longAtput((void *)((message + BaseHeaderSize) + ((((usqInt)(MessageSelectorIndex) << (shiftForWord()))))),GIV(messageSelector)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(message)) + && (!(isForwarded(message)))); + assert(validStorePointerUncheckedArgs(MessageArgumentsIndex, message, argumentArray)); + longAtput((void *)((message + BaseHeaderSize) + ((((usqInt)(MessageArgumentsIndex) << (shiftForWord()))))),argumentArray); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(message)) + && (!(isForwarded(message)))); + assert(validStorePointerUncheckedArgs(MessageLookupClassIndex, message, lookupClass)); + longAtput((void *)((message + BaseHeaderSize) + ((((usqInt)(MessageLookupClassIndex) << (shiftForWord()))))),lookupClass); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),message); + GIV(argumentCount) = 1; +} + + +/* Return the default number of stack pages allocate at startup. + This V3 default suits Qwaq Forums (specifically general rendering). + The Spur default reflects tuning for GC performance ast Cadence. + It is probably a bit high for normal use but QF is profligate with + processes. The actual value can be set via vmParameterAt: and/or a + preference in the ini file. */ + + /* StackInterpreter>>#defaultNumStackPages */ +static sqInt +defaultNumStackPages(void) +{ + return 50; +} + + /* StackInterpreter>>#disableSendPrinting */ +#if SEND_PRINTING +void +disableSendPrinting(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(printSends) = 0; +} +#endif /* SEND_PRINTING */ + + +/* Release the VM to other threads and answer the current thread's index. + Currently valid flags for the non-threaded VM are: + DisownVMForFFICallFlag - informs the VM that it is entering an FFI call + DisownVMFromCallbackFlag - informs the VM that it is reentering an FFI + call + This is the entry-point for plugins and primitives that wish to release + the VM while + performing some operation that may potentially block, and for callbacks + returning back to some blocking operation. While this exists for the + threaded FFI VM we use + it to reset newMethod and the argumentCount after a callback. + This routine returns a negative result on error; see CoInterpreter>>ownVM: + and the clients in platforms/Cross/plugins/IA32ABI/*.c */ + + /* StackInterpreter>>#disownVM: */ +sqInt +disownVM(sqInt flags) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt top; + + assert(GIV(primFailCode) == 0); + assert(flags != 0); + + /* In the single-threaded VM here, we treat flags directly as vmHandle. See MTVM. + We are either in disowning after a callback (see thunkEntry) or before an FFI callout. */ + assert((flags == DisownVMFromCallbackFlag) + || ((flags == (DisownVMFromCallbackFlag + 1)) + || (flags == DisownVMForFFICallFlag))); + + /* If DisownVMForFFICall this is from the FFI plugin and we're making a callout; remember the fact. */ + if (((flags & DisownVMForFFICallFlag) != 0)) { + assert((isOopCompiledMethod(GIV(newMethod))) + && ((argumentCountOf(GIV(newMethod))) == GIV(argumentCount))); + return (GIV(ffiCalloutVMHandle) = flags); + } + + /* Otherwise this is a callback return; restore argumentCount and newMethod as per the ownVM: on callback. */ + if (((flags & DisownVMFromCallbackFlag) != 0)) { + GIV(argumentCount) = ((popStack()) >> 3); + assert(((GIV(argumentCount) >= 0) && (GIV(argumentCount) <= (argumentCountOfMethodHeader(-1))))); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(newMethod) = top; + assert((isOopCompiledMethod(GIV(newMethod))) + && ((argumentCountOf(GIV(newMethod))) == GIV(argumentCount))); + return 0; + } + return flags; +} + + /* StackInterpreter>>#divorceAllFrames */ +static sqInt +divorceAllFrames(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + StackPage *aPage; + sqInt i; + + if (GIV(stackPage)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l1; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer) + BytesPerWord); + /* end ensureFrameIsMarried:SP: */ +l1: + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + aPage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(aPage))) { + divorceFramesIn(aPage); + } + } + + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + return activeContext; +} + + /* StackInterpreter>>#divorceFramesIn: */ +static NoDbgRegParms void +divorceFramesIn(StackPage *aStackPage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt calleeContext; + char *calleeFP; + sqInt callerContextOrNil; + sqInt theContext; + char *theFP; + sqInt theIP; + StackPage *thePage; + char *theSP; + sqInt valuePointer; + + calleeFP = ((char *) 0); + GIV(statStackPageDivorce) += 1; + theFP = (aStackPage->headFP); + theSP = (aStackPage->headSP); + theIP = longAt(theSP); + + /* theSP points at hottest item on frame's stack */ + theSP += BytesPerWord; + calleeContext = null; + while (1) { + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + theContext = longAt(theFP + FoxThisContext); + goto l1; + } + theContext = marryFrameSP(theFP, theSP); + /* end ensureFrameIsMarried:SP: */ +l1: + updateStateOfSpouseContextForFrameWithSP(theFP, theSP); + valuePointer = contextInstructionPointerframe(theIP, theFP); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, theContext, valuePointer)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointer); + assert((frameReceiver(theFP)) == (followFieldofObject(ReceiverIndex, theContext))); + if (calleeContext) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SenderIndex, calleeContext, theContext)); + assert(isNonImmediate(calleeContext)); + if (oopisGreaterThanOrEqualTo(calleeContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(theContext & (tagMask())))) + && (oopisLessThan(theContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(calleeContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(calleeContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((calleeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),theContext); + } + calleeContext = theContext; + calleeFP = theFP; + theIP = longAt(theFP + FoxCallerSavedIP); + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!(theFP != 0)) break; + /* theSP points at stacked hottest item on frame's stack */ + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(calleeFP))); + theSP = (calleeFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(calleeFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(calleeFP))->cmNumArgs) + : byteAt((calleeFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + + /* begin frameCallerContext: */ + assert(isBaseFrame(calleeFP)); + thePage = stackPageAtpages(pageIndexFor(calleeFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + valuePointer = callerContextOrNil; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SenderIndex, theContext, valuePointer)); + assert(isNonImmediate(theContext)); + if (oopisGreaterThanOrEqualTo(theContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(theContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(theContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),valuePointer); + + /* The page is now free; mark it so. */ + (aStackPage->baseFP = 0); +} + + +/* Rounds negative results towards negative infinity, rather than zero. */ + + /* StackInterpreter>>#doPrimitiveDiv:by: */ +static NoDbgRegParms sqInt +doPrimitiveDivby(sqInt rcvr, sqInt arg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArg; + sqInt integerRcvr; + sqInt posArg; + sqInt posRcvr; + sqInt result; + + integerArg = 0; + integerRcvr = 0; + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + integerRcvr = (rcvr >> 3); + integerArg = (arg >> 3); + + /* begin success: */ + if (!integerArg) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return 1; + } + if (integerRcvr > 0) { + if (integerArg > 0) { + result = integerRcvr / integerArg; + } + else { + /* round negative result toward negative infinity */ + posArg = 0 - integerArg; + result = 0 - ((integerRcvr + (posArg - 1)) / posArg); + } + } + else { + posRcvr = 0 - integerRcvr; + if (integerArg > 0) { + /* round negative result toward negative infinity */ + result = 0 - ((posRcvr + (integerArg - 1)) / integerArg); + } + else { + posArg = 0 - integerArg; + result = posRcvr / posArg; + } + } + + /* begin success: */ + if (!((((((usqInt)(result)) >> 60) + 1) & 15) <= 1)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + return result; +} + + /* StackInterpreter>>#doPrimitiveMod:by: */ +static NoDbgRegParms sqInt +doPrimitiveModby(sqInt rcvr, sqInt arg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArg; + sqInt integerRcvr; + sqInt integerResult; + + integerArg = 0; + integerRcvr = 0; + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + integerRcvr = (rcvr >> 3); + integerArg = (arg >> 3); + + /* begin success: */ + if (!integerArg) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return 1; + } + integerResult = integerRcvr % integerArg; + + /* ensure that the result has the same sign as the integerArg */ + if (integerArg < 0) { + if (integerResult > 0) { + integerResult += integerArg; + } + } + else { + if (integerResult < 0) { + integerResult += integerArg; + } + } + + /* begin success: */ + if (!((((((usqInt)(integerResult)) >> 60) + 1) & 15) <= 1)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + return integerResult; +} + + +/* Signal the external semaphore with the given index. Answer if a context + switch occurs as a result. Do not bounds check. This has been done in the + caller. */ + + /* StackInterpreter>>#doSignalSemaphoreWithIndex: */ +sqInt +doSignalSemaphoreWithIndex(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt sema; + sqInt xArray; + + xArray = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ExternalObjectsArray) << (shiftForWord())))))); + assert(isArray(xArray)); + + /* Note: semaphore indices are 1-based */ + sema = longAt((void *)((xArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord())))))); + assert(!(isOopForwarded(sema))); + return (/* isSemaphoreOop: */ + ((!(sema & (tagMask())))) + && (((longAt((void *)(sema))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord())))))))))) + && (synchronousSignal(sema)); +} + + +/* Presumably this exists to squash C compiler warnings about unused + variables... + */ + + /* StackInterpreter>>#dummyReferToProxy */ +struct VirtualMachine * * +dummyReferToProxy(void) +{ + return (&interpreterProxy); +} + + +/* The prim trace log is a circular buffer of entries. If there is + an entry at primTraceLogIndex \\ PrimTraceLogSize it has entries. + If there is something at primTraceLogIndex it has wrapped. */ + + /* StackInterpreter>>#dumpPrimTraceLog */ +void +dumpPrimTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt hasParameter; + sqInt i; + sqInt remainder; + + if (!(GIV(primTraceLog)[/* safe:mod: */ + (((remainder = (GIV(primTraceLogIndex) - 1) % PrimTraceLogSize)) < 0 + ? remainder + PrimTraceLogSize + : remainder)])) { + return; + } + hasParameter = 0; + if (GIV(primTraceLog)[GIV(primTraceLogIndex)]) { + for (i = GIV(primTraceLogIndex); i < PrimTraceLogSize; i += 1) { + hasParameter = printPrimLogEntryAthasParameter(i, hasParameter); + } + } + for (i = 0; i < GIV(primTraceLogIndex); i += 1) { + hasParameter = printPrimLogEntryAthasParameter(i, hasParameter); + } +} + + +/* Print the prim trace log on a specific output stream. */ +/* essential for writing crash.dmp; use export: not api, so it will be + accessible on win32 and won't be written to cointerp.h + */ + + /* StackInterpreter>>#dumpPrimTraceLogOn: */ +void +dumpPrimTraceLogOn(FILE *aStdioStream) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + FILE *savedTranscript; + + /* begin withRedirectedOutputTo:do: */ + savedTranscript = GIV(transcript); + GIV(transcript) = (aStdioStream + ? aStdioStream + : stdout); + dumpPrimTraceLog(); + GIV(transcript) = savedTranscript; +} + + +/* The trace log is a circular buffer of pairs of entries. If there is + an entry at traceLogIndex - 3 \\ TraceBufferSize it has entries. + If there is something at traceLogIndex it has wrapped. */ + + /* StackInterpreter>>#dumpTraceLog */ +void +dumpTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt remainder; + + if (!(GIV(traceLog)[/* safe:mod: */ + (((remainder = (GIV(traceLogIndex) - 3) % TraceBufferSize)) < 0 + ? remainder + TraceBufferSize + : remainder)])) { + return; + } + if (GIV(traceLog)[GIV(traceLogIndex)]) { + for (i = GIV(traceLogIndex); i <= (TraceBufferSize - 3); i += 3) { + printLogEntryAt(i); + } + } + for (i = 0; i <= (GIV(traceLogIndex) - 3); i += 3) { + printLogEntryAt(i); + } +} + + +/* For marking the end of a leak check print message */ + + /* StackInterpreter>>#eekcr */ +NeverInline void +eekcr(void) +{ + cr(); +} + + /* StackInterpreter>>#enableSendPrinting */ +#if SEND_PRINTING +void +enableSendPrinting(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(printSends) = 1; +} +#endif /* SEND_PRINTING */ + + +/* Answer the caller context for a frame. If the frame has a caller + frame that doesn't have a context, then marry the caller frame. */ + + /* StackInterpreter>>#ensureCallerContext: */ +static NoDbgRegParms sqInt +ensureCallerContext(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + char *callerFP; + StackPage *thePage; + + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!callerFP) { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + return callerContextOrNil; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP))); + return longAt(callerFP + FoxThisContext); + } + return marryFrameSP( + callerFP, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFP))), + (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); +} + + +/* Ensure the image data has been updated to suit the current VM. */ + + /* StackInterpreter>>#ensureImageFormatIsUpToDate: */ +static NoDbgRegParms void +ensureImageFormatIsUpToDate(sqInt swapBytes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt addr; + sqInt address; + sqInt classIndex; + sqInt fmt; + sqInt followingWord; + usqInt followingWordAddress; + sqInt methodHeader; + usqInt numSlots; + sqInt obj; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + sqInt stopAddr; + int swapFloatWords; + sqInt temp; + int tempInt; + sqInt wordAddr; + + if (swapBytes) { + /* begin reverseBytesInImage */ + /* begin reverseBytesInMemory */ + /* begin reverseBytesFrom:to: */ + addr = GIV(oldSpaceStart); + while (oopisLessThan(addr, GIV(endOfMemory))) { + longAtput((void *)(addr),SQ_SWAP_8_BYTES((longAt((void *)(addr))))); + addr += BytesPerWord; + } + + /* Second, return the bytes of bytes-type objects to their + orginal order, and perform any other format conversions. */ + + /* begin updateObjectsPostByteSwap */ + swapFloatWords = VMBIGENDIAN != ((!(imageHeaderFlags & 2))); + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + fmt = (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask()); + if (fmt >= (firstByteFormat())) { + wordAddr = obj + BaseHeaderSize; + if (fmt >= (firstCompiledMethodFormat())) { + methodHeader = longAt((void *)(obj + BaseHeaderSize)); + wordAddr += (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop; + } + + /* compiled method; start after methodHeader and literals */ + stopAddr = obj + (((((/* begin numSlotsOf: */ + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) + BaseHeaderSize); + + /* begin reverseBytesFrom:to: */ + addr = wordAddr; + while (oopisLessThan(addr, stopAddr)) { + longAtput((void *)(addr),SQ_SWAP_8_BYTES((longAt((void *)(addr))))); + addr += BytesPerWord; + } + } + + /* oop contains bytes */ + if (fmt == (firstLongFormat())) { + if (swapFloatWords + && (((longAt((void *)(obj))) & (classIndexMask())) == ClassFloatCompactIndex)) { + temp = longAt((void *)(obj + BaseHeaderSize)); + longAtput((void *)(obj + BaseHeaderSize),longAt((void *)((obj + BaseHeaderSize) + 4))); + longAtput((void *)((obj + BaseHeaderSize) + 4),temp); + } + else { + } + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + if (swapFloatWords) { + /* setImageFloatsBigEndian: */ + imageHeaderFlags = ((imageHeaderFlags | 2) - 2); + } + } + else { + /* begin convertFloatsToPlatformOrder */ + if (VMBIGENDIAN == ((!(imageHeaderFlags & 2)))) { + goto l3; + } + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassFloatCompactIndex) { + tempInt = long32At((void *)(objSqInt + BaseHeaderSize)); + long32Atput((void *)(objSqInt + BaseHeaderSize),long32At((void *)((objSqInt + BaseHeaderSize) + 4))); + long32Atput((void *)((objSqInt + BaseHeaderSize) + 4),tempInt); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2: + assert(oopisGreaterThan(objSqInt, prevObj)); + } + + /* setImageFloatsBigEndian: */ + imageHeaderFlags = ((imageHeaderFlags | 2) - 2); + /* end convertFloatsToPlatformOrder */ +l3:; + } +} + + /* StackInterpreter>>#establishFrameForContextToReturnTo: */ +static NoDbgRegParms char * +establishFrameForContextToReturnTo(sqInt contextToReturnTo) +{ + sqInt senderOop; + StackPage *thePage; + + if (!(/* isContext: */ + ((!(contextToReturnTo & (tagMask())))) + && (((longAt((void *)(contextToReturnTo))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + return 0; + } + if (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (isWidowedContext(contextToReturnTo)) { + /* error: home's sender is dead; cannot return */ + return 0; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + return ((char *)(senderOop - (smallIntegerTag()))); + } + if (!(((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1))) { + return 0; + } + thePage = makeBaseFrameFor(contextToReturnTo); + markStackPageMostRecentlyUsed(thePage); + return (thePage->baseFP); +} + + +/* Divorce a single frame and its context. If it is not the top frame of a + stack this means splitting its stack. */ +/* stackPage needs to have current head pointers to avoid confusion. */ + + /* StackInterpreter>>#externalDivorceFrame:andContext: */ +static NoDbgRegParms void +externalDivorceFrameandContext(char *theFP, sqInt ctxt) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + sqInt callerCtx; + char *callerFP; + char *callerFP1; + sqInt callerIP; + char *callerSP; + char *frameAbove; + StackPage *newPage; + int onCurrent; + sqInt theIP; + StackPage *thePage; + StackPage *thePage1; + char *theSP; + sqInt valuePointer; + + assert((!GIV(stackPage)) + || (GIV(stackPage) == (GIV(mostRecentlyUsedPage)))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (!((onCurrent = thePage == GIV(stackPage)))) { + markStackPageNextMostRecentlyUsed(thePage); + } + theSP = findSPOfon(theFP, thePage); + updateStateOfSpouseContextForFrameWithSP(theFP, theSP); + + /* begin ensureCallerContext: */ + callerFP1 = ((char *)(longAt(theFP + FoxSavedFP))); + if (!callerFP1) { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerContextOrNil = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + callerCtx = callerContextOrNil; + goto l2; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP1 + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP1 + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP1 + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP1))); + callerCtx = longAt(callerFP1 + FoxThisContext); + goto l2; + } + callerCtx = marryFrameSP( + callerFP1, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFP))), + (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + /* end ensureCallerContext: */ +l2: + if ((frameAbove = findFrameAboveinPage(theFP, thePage))) { + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l1; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l1: + theIP = longAt(frameAbove + FoxCallerSavedIP); + frameAbove = moveFramesInthroughtoPage(thePage, frameAbove, newPage); + if (onCurrent) { + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (newPage->headSP); + GIV(framePointer) = (newPage->headFP); + } + else { + markStackPageMostRecentlyUsed(newPage); + } + assert((frameCallerContext(frameAbove)) == ctxt); + } + else { + theIP = longAt((thePage->headSP)); + } + + /* If we're divorcing the top frame we can simply peel it off. + othewise move all frames above to a new stack and then peel the frame off. */ + valuePointer = contextInstructionPointerframe(theIP, theFP); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, ctxt, valuePointer)); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointer); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SenderIndex, ctxt, callerCtx)); + assert(isNonImmediate(ctxt)); + if (oopisGreaterThanOrEqualTo(ctxt, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(callerCtx & (tagMask())))) + && (oopisLessThan(callerCtx, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(ctxt + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(ctxt); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),callerCtx); + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (callerFP) { + callerIP = longAt(theFP + FoxCallerSavedIP); + callerSP = ((/* begin frameCallerSP: */ + assert(!(isBaseFrame(theFP))), +(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)) - BytesPerWord; + longAtput(callerSP,callerIP); + + /* begin setHeadFP:andSP:inPage: */ + assert(callerSP < callerFP); + assert((callerSP < ((thePage->baseAddress))) + && (callerSP > (((thePage->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((callerFP < ((thePage->baseAddress))) + && (callerFP > (((thePage->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (thePage->headFP = callerFP); + (thePage->headSP = callerSP); + } + else { + freeStackPage(thePage); + } +} + + +/* Ensure aFramePtr is a base frame. Then we can assign its sender. + Answer the possibly moved location of the frame. */ + + /* StackInterpreter>>#externalEnsureIsBaseFrame: */ +static NoDbgRegParms char * +externalEnsureIsBaseFrame(char *aFramePtr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int onCurrent; + char *theFP; + StackPage *thePage; + + if (!(longAt(aFramePtr + FoxSavedFP))) { + return aFramePtr; + } + theFP = aFramePtr; + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + onCurrent = thePage == GIV(stackPage); + + /* Storing the frame's sender with its caller's context + has the side effect of making theFP a base frame. */ + theFP = storeSenderOfFramewithValue(theFP, ensureCallerContext(theFP)); + if (onCurrent) { + assert(GIV(stackPage) != thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (GIV(stackPage)->headSP); + GIV(framePointer) = (GIV(stackPage)->headFP); + } + else { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + assert(pageListIsWellFormed()); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + return theFP; +} + + /* StackInterpreter>>#externalInstVar:ofContext:put: */ +static NoDbgRegParms sqInt +externalInstVarofContextput(sqInt index, sqInt maybeMarriedContext, sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int onCurrentPage; + sqInt senderOop; + char *theFP; + StackPage *thePage; + + assert(isContext(maybeMarriedContext)); + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* Assign the field of a married context. */ + assert(!((isObjImmutable(maybeMarriedContext)))); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(maybeMarriedContext))))) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, maybeMarriedContext, anOop)); + assert(isNonImmediate(maybeMarriedContext)); + if (oopisGreaterThanOrEqualTo(maybeMarriedContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anOop & (tagMask())))) + && (oopisLessThan(anOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(maybeMarriedContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(maybeMarriedContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),anOop); + if (index == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(maybeMarriedContext); + } + return null; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + onCurrentPage = thePage == GIV(stackPage); + if (index) { + externalDivorceFrameandContext(theFP, maybeMarriedContext); + if (ismethodAssignmentToContextWithMachineCodePC(index, maybeMarriedContext)) { + ensureContextHasBytecodePC(maybeMarriedContext); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, maybeMarriedContext, anOop)); + assert(isNonImmediate(maybeMarriedContext)); + if (oopisGreaterThanOrEqualTo(maybeMarriedContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anOop & (tagMask())))) + && (oopisLessThan(anOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(maybeMarriedContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(maybeMarriedContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),anOop); + if (index == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(maybeMarriedContext); + } + } + else { + storeSenderOfFramewithValue(theFP, anOop); + } + if (onCurrentPage) { + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (GIV(stackPage)->headSP); + GIV(framePointer) = (GIV(stackPage)->headFP); + } + else { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(pageListIsWellFormed()); + assert(validStackPageBaseFrames()); + return 0; +} + + +/* Invoke a quick primitive. + Called under the assumption that primFunctionPtr has been preloaded */ + + /* StackInterpreter>>#externalQuickPrimitiveResponse */ +static sqInt +externalQuickPrimitiveResponse(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt localPrimIndex; + sqInt oop; + + assert(isPrimitiveFunctionPointerAnIndex()); + localPrimIndex = ((sqInt) primitiveFunctionPointer); + assert((localPrimIndex > 0xFF) + && (localPrimIndex < 520)); + + /* Quick return inst vars */ + if (localPrimIndex >= 264) { + oop = longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)((localPrimIndex - 264)) << (shiftForWord())))))); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); + return 1; + } + + /* Quick return constants */ + if (localPrimIndex == 0x100) { + return 1; + } + if (localPrimIndex == 0x101) { + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),GIV(trueObj)); + return 1; + } + if (localPrimIndex == 258) { + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),GIV(falseObj)); + return 1; + } + if (localPrimIndex == 259) { + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),GIV(nilObj)); + return 1; + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),(((usqInt)(localPrimIndex - 261) << 3) | 1)); + return 1; +} + + +/* not inlined for breakpoint value... */ + + /* StackInterpreter>>#failUnbalancedPrimitive */ +static void +failUnbalancedPrimitive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; +} + + +/* Fetch the instance variable at the given index of the given object. Return + the address of first indexable field of resulting array object, or fail if + the instance variable does not contain an indexable bytes or words object. + */ +/* Note: May be called by translated primitive code. */ + + /* StackInterpreter>>#fetchArray:ofObject: */ +void * +fetchArrayofObject(sqInt fieldIndex, sqInt objectPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayOop; + + arrayOop = longAt((void *)((objectPointer + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + + /* begin arrayValueOf: */ + if (/* isWordsOrBytes: */ + ((!(arrayOop & (tagMask())))) + && (isWordsOrBytesNonImm(arrayOop))) { + return ((void *) (pointerForOop(arrayOop + BaseHeaderSize))); + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; +} + + +/* Fetch the instance variable at the given index of the given object. Return + the C double precision floating point value of that instance variable, or + fail if it is not a Float. + */ +/* Note: May be called by translated primitive code. */ + + /* StackInterpreter>>#fetchFloat:ofObject: */ +double +fetchFloatofObject(sqInt fieldIndex, sqInt objectPointer) +{ + sqInt floatOop; + + floatOop = longAt((void *)((objectPointer + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + return floatValueOf(floatOop); +} + + +/* Fetch a slot from a married context. Compute the value + of the relevant inst var from the spouse frame's state. + + This method assumes frame pointers have been written back. */ + + /* StackInterpreter>>#fetchPointer:ofMarriedContext: */ +static NoDbgRegParms sqInt +fetchPointerofMarriedContext(sqInt offset, sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + char *callerFP; + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + sqInt senderOop; + char *spouseFP; + StackPage *thePage; + + assert(isContext(aContext)); + assert((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer))); + assert(checkIsStillMarriedContextcurrentFP(aContext, GIV(framePointer))); + + /* method, closureOrNil & receiver need no special handling; only + sender, pc & stackp have to be computed for married contexts. */ + if (offset <= ReceiverIndex) { + if (!(offset <= StackPointerIndex)) { + return longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(offset) << (shiftForWord())))))); + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!offset) { + /* begin ensureCallerContext: */ + callerFP = ((char *)(longAt(spouseFP + FoxSavedFP))); + if (!callerFP) { + /* begin frameCallerContext: */ + assert(isBaseFrame(spouseFP)); + thePage = stackPageAtpages(pageIndexFor(spouseFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + return callerContextOrNil; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP))); + return longAt(callerFP + FoxThisContext); + } + return marryFrameSP( + callerFP, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(spouseFP))), + (spouseFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(spouseFP))->cmNumArgs) + : byteAt((spouseFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + } + if (offset == StackPointerIndex) { + return (((usqInt)(stackPointerIndexForFrame(spouseFP)) << 3) | 1); + } + if (offset == InstructionPointerIndex) { + return instructionPointerForFramecurrentFPcurrentIP(spouseFP, GIV(framePointer), GIV(instructionPointer)); + } + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + return ((((offset - ReceiverIndex) >= 1) && ((offset - ReceiverIndex) <= (stackPointerIndexForFrame(spouseFP)))) + ? /* temporary:in: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? ((offset - (ReceiverIndex + 1)) < ((frameNumArgs = ((mframeCogMethod(spouseFP))->cmNumArgs))) + ? longAt((spouseFP + FoxCallerSavedIP) + ((frameNumArgs - (offset - (ReceiverIndex + 1))) * BytesPerWord)) + : longAt(((spouseFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs - (offset - (ReceiverIndex + 1))) * BytesPerWord))) + : /* itemporary:in: */ + ((offset - (ReceiverIndex + 1)) < ((frameNumArgsUsqInt = byteAt((spouseFP + FoxIFrameFlags) + 1))) + ? longAt((spouseFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt - (offset - (ReceiverIndex + 1))) * BytesPerWord)) + : longAt(((spouseFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt - (offset - (ReceiverIndex + 1))) * BytesPerWord)))) + : GIV(nilObj)); +} + + +/* Return the stackPointer of a Context or BlockContext. + Does not deal with married contexts. Use only for debug + printing or object tracing functions. To obtain an accurate + stack pointer use stackPointerForMaybeMarriedContext: */ + + /* StackInterpreter>>#fetchStackPointerOf: */ +static NoDbgRegParms sqInt +fetchStackPointerOf(sqInt aContext) +{ + sqInt sp; + + sp = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + return 0; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(aContext))); + return (sp >> 3); +} + + +/* Answer if file times, including those from the FilePlugin and + FileAttributesPlugin, should be answered in UTC seconds from the epoch + rather than local seconds. */ + + /* StackInterpreter>>#fileTimesInUTC */ +int +fileTimesInUTC(void) +{ + return ((imageHeaderFlags & 0x200) != 0); +} + + /* StackInterpreter>>#findClassContainingMethod:startingAt: */ +static NoDbgRegParms sqInt +findClassContainingMethodstartingAt(sqInt meth, sqInt classObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classDict; + usqInt classDictSize; + sqInt currClass; + sqInt i; + sqInt methodArray; + usqInt numSlots; + sqInt objOop; + sqInt referent; + sqInt referentSqInt; + + if (/* isOopForwarded: */ + ((!(classObj & (tagMask())))) + && ((!((longAt((void *)(classObj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(classObj)); + currClass = longAt((void *)((classObj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(currClass & (tagMask())))) + && ((!((longAt((void *)(currClass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + currClass = longAt((void *)((currClass + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + else { + currClass = classObj; + } + do { + assert(!(isForwarded(currClass))); + if (!(addressCouldBeClassObj(currClass))) { + return GIV(nilObj); + } + + /* begin noFixupFollowField:ofObject: */ + classDict = longAt((void *)((currClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(classDict & (tagMask())))) + && ((!((longAt((void *)(classDict))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(classDict)); + referent = longAt((void *)((classDict + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + classDict = referent; + } + assert(!(isForwarded(classDict))); + + /* begin numSlotsOf: */ + assert((classIndexOf(classDict)) > (isForwardedObjectClassIndexPun())); + classDictSize = (((numSlots = byteAt((void *)(classDict + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classDict - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (classDictSize > MethodArrayIndex) { + /* begin noFixupFollowField:ofObject: */ + methodArray = longAt((void *)((classDict + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(methodArray & (tagMask())))) + && ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(methodArray)); + referentSqInt = longAt((void *)((methodArray + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + methodArray = referentSqInt; + } + assert(!(isForwarded(methodArray))); + i = 0; + while (i < (classDictSize - SelectorStart)) { + if (meth == (noFixupFollowFieldofObject(i, methodArray))) { + return currClass; + } + i += 1; + } + } + + /* begin noFixupSuperclassOf: */ + /* begin noFixupFollowField:ofObject: */ + objOop = longAt((void *)((currClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + referentSqInt = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + objOop = referentSqInt; + } + currClass = objOop; + } while(!(currClass == GIV(nilObj))); + return currClass; +} + + /* StackInterpreter>>#findClassOfMethod:forReceiver: */ +sqInt +findClassOfMethodforReceiver(sqInt meth, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt rclass; + sqInt tagBits; + + if ((/* addressCouldBeOop: */ + (((rcvr & (tagMask())) != 0)) + || (addressCouldBeObj(rcvr))) + && (!(/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))))) { + rclass = findClassContainingMethodstartingAt(meth, /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr))); + if (rclass != GIV(nilObj)) { + return rclass; + } + } + if (!((addressCouldBeObj(meth)) + && (((byteAt((void *)(meth + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return GIV(nilObj); + } + return findClassContainingMethodstartingAt(meth, safeMethodClassOf(meth)); +} + + +/* Answer the frame above theFP (adjacent frame nearest head end). + If theFP is the head frame answer 0. */ + + /* StackInterpreter>>#findFrameAbove:inPage: */ +static NoDbgRegParms char * +findFrameAboveinPage(char *theFP, StackPage *thePage) +{ + char *callerFP; + char *fp; + + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == theFP) { + return 0; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == theFP) { + return fp; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + return 0; +} + + +/* Answer the highest priority of runnable process without altering any + lists. Do *not* update highestRunnableProcessPriority. + Answer 0 if no runnable process can be found. */ + + /* StackInterpreter>>#findHighestPriority */ +static sqInt +findHighestPriority(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + sqInt objOop; + sqInt p; + sqInt processList; + sqInt schedLists; + + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + schedLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + p = (GIV(highestRunnableProcessPriority) + ? GIV(highestRunnableProcessPriority) + : (/* begin numSlotsOf: */ + assert((classIndexOf(schedLists)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(schedLists + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(schedLists - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))); + while (((p -= 1)) >= 0) { + processList = longAt((void *)((schedLists + BaseHeaderSize) + ((((usqInt)(p) << (shiftForWord())))))); + + /* begin isEmptyList: */ + assert(!(isForwarded(processList))); + if (!((longAt((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj))) { + return p + 1; + } + } + return 0; +} + + /* StackInterpreter>>#findHomeForContext: */ +static NoDbgRegParms sqInt +findHomeForContext(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt closureOrNil; + + if (!(/* isContext: */ + ((!(aContext & (tagMask())))) + && (((longAt((void *)(aContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + return null; + } + closureOrNil = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord())))))); + assert((isPointers(closureOrNil)) + && ((closureOrNil == (nilObject())) + || ((numSlotsOf(closureOrNil)) >= ClosureFirstCopiedValueIndex))); + if ((((closureOrNil & (tagMask())) != 0)) + || (closureOrNil == GIV(nilObj))) { + return aContext; + } + + /* robustness in the presence of bugs */ + if ((((longAt((void *)(closureOrNil))) & (classIndexMask())) != ClassBlockClosureCompactIndex) + && (((longAt((void *)(closureOrNil))) & (classIndexMask())) != ClassFullBlockClosureCompactIndex)) { + return null; + } + return findHomeForContext(longAt((void *)((closureOrNil + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord()))))))); +} + + +/* See findUnwindThroughContext:. Alas this is mutually recursive with + findMethodWithPrimitive:FromFP:SP:ThroughContext: instead of iterative. + We're doing the simplest thing that could possibly work. Niceties can + wait. */ +/* Being mutually-recursive with findMethodWithPrimitive:FromFP:UpToContext: + gives the author's type inference algorithm headaches. Wimp out by + declaring the return type. + */ + + /* StackInterpreter>>#findMethodWithPrimitive:FromContext:UpToContext: */ +static NoDbgRegParms sqInt +findMethodWithPrimitiveFromContextUpToContext(sqInt primitive, sqInt senderContext, sqInt homeContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt senderContextSqInt; + sqInt senderOop; + char *startFP; + sqInt theContext; + char *theFP; + char *theFPAbove; + sqInt theMethod; + sqInt theMethodSqInt; + StackPage *thePage; + char *theSP; + + assert((senderContext == (nilObject())) + || (isContext(senderContext))); + assert((homeContext == (nilObject())) + || (isContext(homeContext))); + theContext = senderContext; + while (1) { + if (theContext == GIV(nilObj)) { + return theContext; + } + if (((((longAt((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) break; + if (theContext == homeContext) { + return 0; + } + if (!((primitive == 0) + || ((longAt((void *)((theContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord()))))))) != GIV(nilObj)))) { + /* begin followObjField:ofObject: */ + theMethod = longAt((void *)((theContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + assert(isNonImmediate(theMethod)); + if ((!((longAt((void *)(theMethod))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + theMethod = fixFollowedFieldofObjectwithInitialValue(MethodIndex, theContext, theMethod); + } + if ((primitiveIndexOf(theMethod)) == primitive) { + return theContext; + } + } + + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SenderIndex, theContext, objOop); + } + theContext = objOop; + } + if (isWidowedContext(theContext)) { + return GIV(nilObj); + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + startFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin findMethodWithPrimitive:FromFP:UpToContext: */ + theFP = startFP; + theFPAbove = startFP; + do { + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) + && (homeContext == (longAt(theFP + FoxThisContext)))) { + return 0; + } + if (!((primitive == 0) + || (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0)))) { + theMethodSqInt = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + if ((primitiveIndexOf(theMethodSqInt)) == primitive) { + if (theFP == theFPAbove) { + theSP = findSPOfon(theFP, stackPageFor(theFP)); + } + else { + /* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFPAbove))); + theSP = (theFPAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFPAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFPAbove))->cmNumArgs) + : byteAt((theFPAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + return longAt(theFP + FoxThisContext); + } + return marryFrameSP(theFP, theSP); + } + } + theFPAbove = theFP; + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + } while(theFP != 0); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFPAbove)); + thePage = stackPageAtpages(pageIndexFor(theFPAbove), GIV(pages)); + senderContextSqInt = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(senderContextSqInt)); + assert((senderContextSqInt == (nilObject())) + || (isContext(followMaybeForwarded(senderContextSqInt)))); + if (!(/* isContext: */ + ((!(senderContextSqInt & (tagMask())))) + && (((longAt((void *)(senderContextSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + return GIV(nilObj); + } + return findMethodWithPrimitiveFromContextUpToContext(primitive, senderContextSqInt, homeContext); +} + + /* StackInterpreter>>#findSelectorOfMethod: */ +sqInt +findSelectorOfMethod(sqInt aMethodOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classDict; + usqInt classDictSize; + sqInt classObj; + sqInt homeMethod; + sqInt i; + sqInt methodArray; + usqInt numSlots; + sqInt objOop; + sqInt referent; + sqInt selector; + + if (!(addressCouldBeObj(aMethodOop))) { + return GIV(nilObj); + } + if ((!((longAt((void *)(aMethodOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + return findSelectorOfMethod(followForwarded(aMethodOop)); + } + if (!(/* isOopCompiledMethod: */ + ((!(aMethodOop & (tagMask())))) + && (((byteAt((void *)(aMethodOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return GIV(nilObj); + } + homeMethod = homeMethodOf(aMethodOop); + if ((selector = maybeSelectorOfMethod(homeMethod))) { + return selector; + } + classObj = safeMethodClassOf(homeMethod); + if (addressCouldBeClassObj(classObj)) { + /* begin noFixupFollowField:ofObject: */ + classDict = longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(classDict & (tagMask())))) + && ((!((longAt((void *)(classDict))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(classDict)); + referent = longAt((void *)((classDict + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + classDict = referent; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(classDict)) > (isForwardedObjectClassIndexPun())); + classDictSize = (((numSlots = byteAt((void *)(classDict + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classDict - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (classDictSize > MethodArrayIndex) { + /* begin noFixupFollowField:ofObject: */ + methodArray = longAt((void *)((classDict + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(methodArray & (tagMask())))) + && ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(methodArray)); + referent = longAt((void *)((methodArray + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + methodArray = referent; + } + i = 0; + while (i < (classDictSize - SelectorStart)) { + if (homeMethod == (noFixupFollowFieldofObject(i, methodArray))) { + /* begin noFixupFollowField:ofObject: */ + objOop = longAt((void *)((classDict + BaseHeaderSize) + ((((usqInt)((i + SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + referent = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + objOop = referent; + } + return objOop; + } + i += 1; + } + } + } + return GIV(nilObj); +} + + +/* Search for the stack pointer for theFP. This points to the hottest item on + the frame's stack. + DO NOT CALL THIS WITH theFP == localFP OR theFP == framePointer! */ + + /* StackInterpreter>>#findSPOf:on: */ +static NoDbgRegParms char * +findSPOfon(char *theFP, StackPage *thePage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *aFrame; + char *prevFrame; + char *startFrame; + char *theSP; + + assert(!((isFree(thePage)))); + startFrame = (thePage->headFP); + + /* begin findSPOrNilOf:on:startingFrom: */ + prevFrame = ((char *) 0); + if (startFrame == theFP) { + if (((thePage->headSP)) >= startFrame) { + /* If the SP is invalid return the pointer to the receiver field. */ + theSP = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + goto l1; + } + + /* Skip the instruction pointer on top of stack of inactive pages. */ + theSP = (thePage == GIV(stackPage) + ? (thePage->headSP) + : ((thePage->headSP)) + BytesPerWord); + goto l1; + } + aFrame = startFrame; + while (1) { + prevFrame = aFrame; + aFrame = ((char *)(longAt(aFrame + FoxSavedFP))); + if (!(aFrame != 0)) break; + if (theFP == aFrame) { + /* begin frameCallerSP: */ + assert(!(isBaseFrame(prevFrame))); + theSP = (prevFrame + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(prevFrame + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(prevFrame))->cmNumArgs) + : byteAt((prevFrame + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + goto l1; + } + } + theSP = null; + /* end findSPOrNilOf:on:startingFrom: */ +l1: + if (theSP) { + return theSP; + } + error("did not find theFP in stack page"); + return 0; +} + + +/* Search for either an unwind-protect (activation of method with primitive + 198, PrimNumberUnwindMarker) + or homeContext along the sender chain, which ever is found first. Return + values: 0 home context was found on sender chain with no intervening + unwind-protects nilObj home context could not be found => cannotReturn + context the context of an intervening unwind-protect implies home context + was found */ +/* Almost always (98%) the home is on the same page, in which case we know it + will be found. + */ + + /* StackInterpreter>>#findUnwindThroughContext: */ +static NoDbgRegParms sqInt +findUnwindThroughContext(sqInt homeContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxtOrNilOrZero; + sqInt onSamePage; + sqInt senderContext; + char *theFP; + char *theFPAbove; + sqInt theMethod; + sqInt theMethodSqInt; + StackPage *thePage; + char *theSP; + + onSamePage = (/* isStillMarriedContext: */ + (((((longAt((void *)((homeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(homeContext)))) + && ((pageIndexFor(GIV(framePointer))) == (pageIndexFor(frameOfMarriedContext(homeContext)))); + + /* Since nothing changes we don't need to internalize. */ + + /* begin findMethodWithPrimitive:FromFP:UpToContext: */ + theFP = GIV(framePointer); + theFPAbove = GIV(framePointer); + do { + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) + && (homeContext == (longAt(theFP + FoxThisContext)))) { + ctxtOrNilOrZero = 0; + goto l1; + } + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0))) { + theMethodSqInt = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + if ((primitiveIndexOf(theMethodSqInt)) == PrimNumberUnwindMarker) { + if (theFP == theFPAbove) { + theSP = findSPOfon(theFP, stackPageFor(theFP)); + } + else { + /* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFPAbove))); + theSP = (theFPAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFPAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFPAbove))->cmNumArgs) + : byteAt((theFPAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + ctxtOrNilOrZero = longAt(theFP + FoxThisContext); + goto l1; + } + ctxtOrNilOrZero = marryFrameSP(theFP, theSP); + goto l1; + } + } + theFPAbove = theFP; + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + } while(theFP != 0); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFPAbove)); + thePage = stackPageAtpages(pageIndexFor(theFPAbove), GIV(pages)); + senderContext = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(senderContext)); + assert((senderContext == (nilObject())) + || (isContext(followMaybeForwarded(senderContext)))); + if (!(/* isContext: */ + ((!(senderContext & (tagMask())))) + && (((longAt((void *)(senderContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + ctxtOrNilOrZero = GIV(nilObj); + goto l1; + } + ctxtOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(PrimNumberUnwindMarker, senderContext, homeContext); + /* end findMethodWithPrimitive:FromFP:UpToContext: */ +l1: + assert(!((onSamePage + && (ctxtOrNilOrZero == (nilObject()))))); + if (!ctxtOrNilOrZero) { + theMethod = longAt((void *)((homeContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + if ((primitiveIndexOf(theMethod)) == PrimNumberUnwindMarker) { + return homeContext; + } + return 0; + } + + /* If an unwind was found, can the home context be found also? No need to look if on the same page. + No need to look if cannot return (ctxtOrNilOrZero = objectMemory nilObject) */ + if (!(onSamePage + || (ctxtOrNilOrZero == GIV(nilObj)))) { + if ((findMethodWithPrimitiveFromContextUpToContext(0, ctxtOrNilOrZero, homeContext)) == GIV(nilObj)) { + return GIV(nilObj); + } + } + return ctxtOrNilOrZero; +} + + /* StackInterpreter>>#flush */ +void +flush(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fflush(GIV(transcript)); +} + + +/* methodObj is a CompiledMethod. If it contains an external primitive, + flush the function address and session ID of the CM. Answer the prim + index for the benefit of subclass overrides. */ + + /* StackInterpreter>>#flushExternalPrimitiveOf: */ +static NoDbgRegParms sqInt +flushExternalPrimitiveOf(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt headerSqInt; + sqInt lit; + usqInt numSlots; + sqInt primIdx; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + headerSqInt = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* primitiveIndexOfMethod:header: */ + if (((header & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((header >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIdx = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIdx = 0; + } + if ((primIdx == PrimNumberExternalCall) + && (((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) > 0)) { + lit = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if ((/* isArray: */ + ((!(lit & (tagMask())))) + && (((byteAt((void *)(lit + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(lit)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(lit + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(lit - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(lit)) + && (!(isForwarded(lit)))); + assert(validStorePointerUncheckedArgs(2, lit, ConstZero)); + longAtput((void *)((lit + BaseHeaderSize) + (2U << (shiftForWord()))),ConstZero); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(lit)) + && (!(isForwarded(lit)))); + assert(validStorePointerUncheckedArgs(3, lit, ConstZero)); + longAtput((void *)((lit + BaseHeaderSize) + (3U << (shiftForWord()))),ConstZero); + } + } + + /* If not, something's broken */ + return primIdx; +} + + +/* Flush the method caches of references to oldMethod. */ + + /* StackInterpreter>>#flushMethodCacheForMethod: */ +static NoDbgRegParms void +flushMethodCacheForMethod(sqInt oldMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt probe; + + probe = 0; + for (i = 1; i <= MethodCacheEntries; i += 1) { + if ((GIV(methodCache)[probe + MethodCacheMethod]) == oldMethod) { + GIV(methodCache)[probe + MethodCacheSelector] = 0; + } + probe += MethodCacheEntrySize; + } + flushExternalPrimitiveOf(oldMethod); +} + + +/* follow pointers in the current stack frame up to theSP. */ + + /* StackInterpreter>>#followForwardedFrameContents:stackPointer: */ +static NoDbgRegParms void +followForwardedFrameContentsstackPointer(char *theFP, char *theSP) +{ + sqInt oop; + char *ptr; + sqInt referent; + char *toDoLimit; + char *toDoLimit1; + + toDoLimit1 = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + for (ptr = theSP; ptr <= toDoLimit1; ptr += BytesPerWord) { + oop = longAt(ptr); + if (((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(ptr,followForwarded(oop)); + } + } + toDoLimit = theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord()))))); + for (ptr = ((theFP + FoxCallerSavedIP) + BytesPerWord); ptr <= toDoLimit; ptr += BytesPerWord) { + oop = longAt(ptr); + if (((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + longAtput(ptr,oop); + } + } + + /* If we're in a block then also follow fields in the stacked closure as it may be activated again. */ + if (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0)) { + assert(oop == (longAt(theFP + (frameStackedReceiverOffset(theFP))))); + followForwardedObjectFieldstoDepth(oop, 0); + } + assert(!(isForwarded(frameMethodObject(theFP)))); + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(!(isForwarded(frameContext(theFP)))); + } +} + + +/* Force an interrupt check ASAP. + Must set the stack page's limit before stackLimit to avoid + a race condition if this is called from an interrupt handler. */ + + /* StackInterpreter>>#forceInterruptCheck */ +sqInt +forceInterruptCheck(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*iccFunc)(); + StackPage *thePage; + + + /* Do _not_ set stackLimit until the stack system has been initialized. + stackLimit is the initialization flag for the stack system. */ + if (!GIV(stackLimit)) { + return null; + } + thePage = GIV(stackPage); + if ((thePage) + && (thePage != 0)) { + (thePage->stackLimit = ((char *) (((usqInt) -1)))); + } + GIV(stackLimit) = ((char *) (((usqInt) -1))); + sqLowLevelMFence(); + + /* There is a race condition if we test the function and then dereference + it a second time to call it. This is called from interrupt code but at the + same time other code could be clearing the interruptCheckChain via + setInterruptCheckChain:. */ + if (((iccFunc = interruptCheckChain))) { + iccFunc(); + } + GIV(statForceInterruptCheck) += 1; + return 0; +} + + +/* Answer the SP of the caller provided theFP is not a base frame. + This points to the hottest item on the caller frame's stack. */ + + /* StackInterpreter>>#frameCallerSP: */ +static NoDbgRegParms char * +frameCallerSP(char *theFP) +{ + assert(!(isBaseFrame(theFP))); + return (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; +} + + /* StackInterpreter>>#frameContext: */ +static NoDbgRegParms sqInt +frameContext(char *theFP) +{ + return longAt(theFP + FoxThisContext); +} + + /* StackInterpreter>>#frameOfMarriedContext: */ +static NoDbgRegParms char * +frameOfMarriedContext(sqInt aContext) +{ + sqInt senderOop; + + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + + /* begin withoutSmallIntegerTags: */ + assert((((senderOop) & 7) == 1)); + return ((char *)(senderOop - (smallIntegerTag()))); +} + + +/* Answer the offset in bytes from the the frame pointer to its stacked + receiver. The receiver of a message send or the closure of a block + activation is + always on the stack above any arguments and the frame itself. See the + diagram in StackInterpreter class>>initializeFrameIndices. */ + + /* StackInterpreter>>#frameStackedReceiverOffset: */ +static NoDbgRegParms sqInt +frameStackedReceiverOffset(char *theFP) +{ + return (FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))); +} + + +/* Answer the stacked receiver given the frame's argument count. + The receiver of a message send or the closure of a block activation is + always on the stack above any arguments and the frame itself. See the + diagram in StackInterpreter class>>initializeFrameIndices. */ + + /* StackInterpreter>>#frameStackedReceiver:numArgs: */ +static NoDbgRegParms sqInt +frameStackedReceivernumArgs(char *theFP, sqInt numArgs) +{ + return longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)(numArgs) << (shiftForWord())))))); +} + + +/* Free any untraced stack pages. */ + + /* StackInterpreter>>#freeUntracedStackPages */ +static void +freeUntracedStackPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + StackPage *thePage; + + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((!(isFree(thePage))) + && (((thePage->trace)) == StackPageUnreached)) { + assert(noMarkedContextsOnPage(thePage)); + freeStackPage(thePage); + } + assert(((thePage->trace = StackPageTraceInvalid)) != 0); + } +} + + +/* Repaint the entire smalltalk screen, ignoring the affected rectangle. Used + in some platform's code when the Smalltalk window is brought to the front + or uncovered. + */ + + /* StackInterpreter>>#fullDisplayUpdate */ +sqInt +fullDisplayUpdate(void) +{ + updateDisplayLeftTopRightBottom(0, 0, displayWidth, displayHeight); + ioForceDisplayUpdate(); + return 0; +} + + +/* Find an actual function pointer for this primitiveIndex. This is an + opportunity to specialise the prim for the relevant class (format for + example). Default for now is simply the entry in the base primitiveTable. */ + + /* StackInterpreter>>#functionPointerFor:inClass: */ +static NoDbgRegParms void +(*functionPointerForinClass(sqInt primIdx, sqInt theClass))(void) +{ + return ((((usqInt)primIdx)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primIdx]); +} + + +/* Answer an array of flags indicating various optional features of the Cog + VM. If the bit is set then... + Bit 0: supports two bytecode sets (MULTIPLEBYTECODESETS) + Bit 1: supports immutablity (IMMUTABILITY) + Bit 2: suffers from a UNIX setitimer signal-based heartbeat + Bit 3: the VM provides cross-platform bit-identical floating point + Bit 4: the VM can catch exceptions in FFI calls and answer them as + primitive failures + Bit 5: the VM has suspend primitives 568 & 578 which back up a process to + before the wait if it was waiting on a condition variable + Bit 6: the VM is a threading VM */ + + /* StackInterpreter>>#getCogVMFeatureFlags */ +static sqInt +getCogVMFeatureFlags(void) +{ + return (((usqInt)((((((1 + ( +#if IMMUTABILITY + (IMMUTABILITY + ? 2 + : 0) +#else + 0 +#endif + )) + ( +#if ITIMER_HEARTBEAT + 4 +#else + 0 +#endif + )) + ( +#if BIT_IDENTICAL_FLOATING_POINT + 8 +#else + 0 +#endif + )) + ((ioCanCatchFFIExceptions() + ? 16 + : 0))) + 32)) << 3) | 1); +} + + +/* Answer the errorCode object to supply to a failing primitive method that + accepts one. + If there is a primitive error table and the primFailCode is a valid index + there-in answer + the corresponding entry in the table, otherwise simply answer the code as + an integer. + */ + + /* StackInterpreter>>#getErrorObjectFromPrimFailCode */ +static sqInt +getErrorObjectFromPrimFailCode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt clone; + sqInt errObj; + sqInt i; + usqInt newObj; + usqInt numBytes; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt table; + sqInt valuePointer; + + if (GIV(primFailCode) > 0) { + table = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(PrimitiveErrorTableIndex) << (shiftForWord())))))); + if (GIV(primFailCode) <= ((/* begin numSlotsOf: */ + assert((classIndexOf(table)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(table + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(table - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))) { + /* begin followField:ofObject: */ + errObj = longAt((void *)((table + BaseHeaderSize) + ((((usqInt)((GIV(primFailCode) - 1)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(errObj & (tagMask())))) + && ((!((longAt((void *)(errObj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + errObj = fixFollowedFieldofObjectwithInitialValue(GIV(primFailCode) - 1, table, errObj); + } + + /* If there's a clonable object in the table at that index, + answer a clone of the error object with the second slot set to the value of secondaryErrorCode. */ + if (((byteAt((void *)(errObj + (formatFieldByteOffset())))) & (formatMask())) == (nonIndexablePointerFormat())) { + /* begin cloneErrorObj: */ + /* begin numSlotsOf: */ + assert((classIndexOf(errObj)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(errObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(errObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + classIndex = (longAt((void *)(errObj))) & (classIndexMask()); + + /* begin eeInstantiateAnySmallClassIndex:format:numSlots: */ + assert((numSlots >= 0) + && ((classIndex != 0) + && ((classAtIndex(classIndex)) != GIV(nilObj)))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(classAtIndex(classIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + clone = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + classIndex); + GIV(freeStart) += numBytes; + clone = newObj; + /* end eeInstantiateAnySmallClassIndex:format:numSlots: */ +l1: + for (i = 0; i < numSlots; i += 1) { + valuePointer = longAt((void *)((errObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(clone)) + && (!(isForwarded(clone)))); + assert(validStorePointerUncheckedArgs(i, clone, valuePointer)); + longAtput((void *)((clone + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + if ((numSlots > 2) + && (GIV(primFailCode) == PrimErrFFIException)) { + valuePointer = positive64BitIntegerFor(((usqLong) GIV(secondaryErrorCode))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(clone)) + && (!(isForwarded(clone)))); + assert(validStorePointerUncheckedArgs(1, clone, valuePointer)); + longAtput((void *)((clone + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(exceptionPC)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(clone)) + && (!(isForwarded(clone)))); + assert(validStorePointerUncheckedArgs(2, clone, valuePointer)); + longAtput((void *)((clone + BaseHeaderSize) + (2U << (shiftForWord()))),valuePointer); + } + else { + valuePointer = signed64BitIntegerFor(GIV(secondaryErrorCode)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(clone)) + && (!(isForwarded(clone)))); + assert(validStorePointerUncheckedArgs(1, clone, valuePointer)); + longAtput((void *)((clone + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + } + errObj = clone; + } + return errObj; + } + } + return (((usqInt)GIV(primFailCode) << 3) | 1); +} + + +/* If bit 0 of the imageHeaderFlags is set then the system should use the + full screen. + */ + + /* StackInterpreter>>#getFullScreenFlag */ +int +getFullScreenFlag(void) +{ + return ((imageHeaderFlags & 1) != 0); +} + + /* StackInterpreter>>#getInterruptKeycode */ +sqInt +getInterruptKeycode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(interruptKeycode); +} + + /* StackInterpreter>>#getInterruptPending */ +sqInt +getInterruptPending(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(interruptPending); +} + + /* StackInterpreter>>#getNextWakeupUsecs */ +usqLong +getNextWakeupUsecs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(nextWakeupUsecs); +} + + /* StackInterpreter>>#getSavedWindowSize */ +sqInt +getSavedWindowSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(savedWindowSize); +} + + +/* Answer the next 16 bit word read from aFile, byte-swapped according to the + swapFlag. + */ + + /* StackInterpreter>>#getShortFromFile:swap: */ +static NoDbgRegParms usqInt +getShortFromFileswap(sqImageFile aFile, sqInt swapFlag) +{ + unsigned short w; + + w = 0; + sqImageFileRead((&w), sizeof(unsigned short), 1, aFile); + return (swapFlag + ? ((((usqInt)(w)) >> 8) & 0xFF) | (((w & 0xFF) << 8)) + : w); +} + + +/* Answer the screen size to write to the snapshot. + If the actual screen size is zero then write the savedWindowSize instead. */ + + /* StackInterpreter>>#getSnapshotScreenSize */ +static sqInt +getSnapshotScreenSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt screenSize; + + screenSize = ioScreenSize(); + if (((screenSize & 0xFFFF) == 0) + || ((((usqInt)(screenSize)) >> 16) == 0)) { + screenSize = GIV(savedWindowSize); + } + return screenSize; +} + + +/* For Alien FFI */ + + /* StackInterpreter>>#getStackPointer */ +sqInt * +getStackPointer(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((sqInt *) GIV(stackPointer)); +} + + +/* return the global session ID value */ + + /* StackInterpreter>>#getThisSessionID */ +sqInt +getThisSessionID(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(globalSessionID); +} + + /* StackInterpreter>>#getTranscript */ +FILE * +getTranscript(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(transcript); +} + + +/* Handle a send fault that is due to a send using a forwarded selector. + Unforward the selector and follow the current method and special + selectors array to unforward the source of the forwarded selector. */ + + /* StackInterpreter>>#handleForwardedSelectorFaultFor: */ +static NoDbgRegParms sqInt +handleForwardedSelectorFaultFor(sqInt selectorOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt referent; + + assert(isOopForwarded(selectorOop)); + followForwardedFieldsInCurrentMethod(); + followForwardedObjectFieldstoDepth(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))), 0); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(selectorOop)); + referent = longAt((void *)((selectorOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + return referent; +} + + +/* Handle a send fault that may be due to a send to a forwarded object. + Unforward the receiver on the stack and answer its actual class. */ + + /* StackInterpreter>>#handleForwardedSendFaultForTag: */ +static NoDbgRegParms sqInt +handleForwardedSendFaultForTag(sqInt classTag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt rcvr; + sqInt referent; + sqInt tagBits; + + assert(isForwardedClassTag(classTag)); + rcvr = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* should *not* be a super send, so the receiver should be forwarded. */ + assert(isOopForwarded(rcvr)); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(rcvr)); + referent = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + rcvr = referent; + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord),rcvr); + followForwardedFrameContentsstackPointer(GIV(framePointer), GIV(stackPointer) + ((GIV(argumentCount) + 1) * BytesPerWord)); + if (isPointers(/* frameReceiver: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? longAt(GIV(framePointer) + FoxMFReceiver) + : longAt(GIV(framePointer) + FoxIFReceiver)))) { + followForwardedObjectFieldstoDepth(/* frameReceiver: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? longAt(GIV(framePointer) + FoxMFReceiver) + : longAt(GIV(framePointer) + FoxIFReceiver)), 0); + } + return /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); +} + + +/* Handle a special send fault that may be due to a special selector + send accessing a forwarded object. obj is forwarded. + Unforward stack contents and and inst vars and answer obj's target. */ + + /* StackInterpreter>>#handleSpecialSelectorSendFaultFor:fp:sp: */ +static NoDbgRegParms sqInt +handleSpecialSelectorSendFaultForfpsp(sqInt obj, char *theFP, char *theSP) +{ + sqInt rcvr; + sqInt referent; + + assert(isOopForwarded(obj)); + followForwardedFrameContentsstackPointer(theFP, theSP); + rcvr = /* frameReceiver: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? longAt(theFP + FoxMFReceiver) + : longAt(theFP + FoxIFReceiver)); + if (/* isPointers: */ + ((!(rcvr & (tagMask())))) + && (((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) { + followForwardedObjectFieldstoDepth(rcvr, 0); + } + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + return referent; +} + + +/* Check for stack overflow, moving frames to another stack if so. + This should *only* be sent from checkForStackOverflow. */ + + /* StackInterpreter>>#handleStackOverflow */ +static NeverInline void +handleStackOverflow(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + char *callerFP; + char *callerFP1; + StackPage *newPage; + sqInt overflowCount; + char *overflowLimitAddress; + sqInt sourceCode; + char *theFP; + StackPage *thePage; + + callerFP = ((char *) 0); + assert(GIV(stackPointer) < ((GIV(stackPage)->realStackLimit))); + + /* begin traceStackOverflow */ + if (recordOverflowTrace()) { + sourceCode = ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? CSFromMachineCode + : CSFromInterpreter); + + /* begin recordTrace:source:thing:extra: */ + if (TraceLog) { + GIV(traceLog)[GIV(traceLogIndex)] = ((((((usqInt)(TraceStackOverflow) << TraceTypeShift)))) + sourceCode); + GIV(traceLog)[GIV(traceLogIndex) + 1] = 0; + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + } + if ((recordPrimTrace()) + && (!primTracePluginName)) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TraceStackOverflow << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + GIV(statStackOverflow) += 1; + + /* The stack has overflowed this page. If the system is executing some recursive algorithm, + e.g. fibonacci, then the system could thrash overflowing the stack if the call soon returns + back to the current page. To avoid thrashing, since overflow is quite slow, we can move + more than one frame. The idea is to record which page has overflowed, and the first + time it overflows move one frame, the second time two frames, and so on. We move no + more frames than would leave the page half occupied. */ + theFP = GIV(framePointer); + if (GIV(stackPage) == GIV(overflowedPage)) { + overflowLimitAddress = ((GIV(stackPage)->baseAddress)) - GIV(overflowLimit); + overflowCount = (GIV(extraFramesToMoveOnOverflow) += 1); + while ((((overflowCount -= 1)) >= 0) + && ((((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) < overflowLimitAddress) + && (!((longAt(callerFP + FoxSavedFP)) == 0)))) { + theFP = callerFP; + } + } + else { + GIV(overflowedPage) = GIV(stackPage); + GIV(extraFramesToMoveOnOverflow) = 0; + } + + /* begin ensureCallerContext: */ + callerFP1 = ((char *)(longAt(theFP + FoxSavedFP))); + if (!callerFP1) { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + goto l2; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP1 + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP1 + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP1 + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP1))); + + /* frameContext: */ + goto l2; + } + marryFrameSP( + callerFP1, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFP))), + (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + /* end ensureCallerContext: */ +l2: + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l1; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l1: + moveFramesInthroughtoPage(GIV(stackPage), theFP, newPage); + + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (newPage->headSP); + GIV(framePointer) = (newPage->headFP); + assert(validInstructionPointerinFrame(GIV(instructionPointer) + 1, GIV(framePointer))); + assert((!(frameHasContext(GIV(framePointer)))) + || (isContext(frameContext(GIV(framePointer))))); +} + + +/* The stackPointer is below the stackLimit. This is either because of a + stack overflow or the setting of stackLimit to indicate a possible + interrupt. Check for stackOverflow and interrupts anddeal with each + appropriately. Answer if a context switch occurred. */ + + /* StackInterpreter>>#handleStackOverflowOrEventAllowContextSwitch: */ +static NoDbgRegParms sqInt +handleStackOverflowOrEventAllowContextSwitch(sqInt mayContextSwitch) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin checkForStackOverflow */ + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (GIV(stackPointer) < ((GIV(stackPage)->realStackLimit))) { + handleStackOverflow(); + } + + /* If the stackLimit differs from the realStackLimit then the stackLimit + has been set to indicate an event or interrupt that needs servicing. */ + if (GIV(stackLimit) == ((GIV(stackPage)->realStackLimit))) { + return 0; + } + return checkForEventsMayContextSwitch(mayContextSwitch); +} + + +/* This is a C implementation needed by ioSetMaxExtSemTableSize + and e.g. stackPageByteSize. */ + + /* StackInterpreter>>#highBit: */ +sqInt +highBit(usqInt anUnsignedValue) +{ + sqInt bitNo; + usqInt shifted; + + + /* so it shows up in senders... */ + shifted = anUnsignedValue; + bitNo = 0; +# if BytesPerWord > 4 + if (!(shifted < (0x100000000LL))) { + shifted = (shifted) >> 32; + bitNo += 32; + } +# endif + + if (!(shifted < (0x10000))) { + shifted = (shifted) >> 16; + bitNo += 16; + } + if (!(shifted < (0x100))) { + shifted = (shifted) >> 8; + bitNo += 8; + } + if (!(shifted < (16))) { + shifted = (shifted) >> 4; + bitNo += 4; + } + if (!(shifted < (4))) { + shifted = (shifted) >> 2; + bitNo += 2; + } + if (!(shifted < (2))) { + shifted = (shifted) >> 1; + bitNo += 1; + } + + /* shifted 0 or 1 now */ + return bitNo + shifted; +} + + +/* With the full block implementation a CompiledBlock's method is found along + the chain through the last literal. See CompiledBlock>>#method. Answer + the home mehtod, or nilObj if it cannot be found. */ + + /* StackInterpreter>>#homeMethodOf: */ +sqInt +homeMethodOf(sqInt aMethodOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt lastLiteral; + sqInt methodChain; + + assert(isOopCompiledMethod(aMethodOop)); + methodChain = aMethodOop; + while (1) { + lastLiteral = ultimateLiteralOf(methodChain); + if (!(/* isOopCompiledMethod: */ + ((!(lastLiteral & (tagMask())))) + && (((byteAt((void *)(lastLiteral + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return methodChain; + } + if (!(/* isOopCompiledMethod: */ + ((!(lastLiteral & (tagMask())))) + && (((byteAt((void *)(lastLiteral + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return GIV(nilObj); + } + methodChain = lastLiteral; + } + return 0; +} + + +/* If thePage is the stackPage and the stackPointer and/or the framePointer + are pointing within it, + answer if thePage's heapSP and headFP are equal to the stackPointer and + framePointer respectively. + */ + + /* StackInterpreter>>#ifCurrentStackPageHasValidHeadPointers: */ +static NoDbgRegParms sqInt +ifCurrentStackPageHasValidHeadPointers(StackPage *thePage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (thePage == GIV(stackPage)) { + if (/* addressIsInPage: */ + (((thePage->lastAddress)) < GIV(framePointer)) + && (GIV(framePointer) < ((thePage->baseAddress)))) { + if (((thePage->headFP)) != GIV(framePointer)) { + return 0; + } + } + if (/* addressIsInPage: */ + (((thePage->lastAddress)) < GIV(stackPointer)) + && (GIV(stackPointer) < ((thePage->baseAddress)))) { + if (((thePage->headSP)) != GIV(stackPointer)) { + return 0; + } + } + } + return 1; +} + + /* StackInterpreter>>#iframeMethod: */ +static NoDbgRegParms usqInt +iframeMethod(char *theFP) +{ + return longAt(theFP + FoxMethod); +} + + +/* Return the equivalent of + aClass includesBehavior: aSuperclass. + Note: written for efficiency and better inlining (only 1 temp) */ + + /* StackInterpreter>>#includesBehavior:ThatOf: */ +sqInt +includesBehaviorThatOf(sqInt aClass, sqInt aSuperclass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt theClass; + + if (aSuperclass == GIV(nilObj)) { + return 0; + } + theClass = aClass; + while (1) { + if (theClass == aSuperclass) { + return 1; + } + if (!(theClass != GIV(nilObj))) break; + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((theClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, theClass, objOop); + } + theClass = objOop; + } + return 0; +} + + +/* Initialize the stack pages and then invoke continuation. Use alloca'ed + memory so that + when we have a JIT its stack pointer will be on the native stack since + alloca allocates + memory on the stack. Certain thread systems use the native stack pointer + as the + frame ID so putting the stack anywhere else can confuse the thread system. + + This path is for the threaded VM where we may want to allocate the stack + zone on + the stack of a thread other than the main VM thread. */ + + /* StackInterpreter>>#initStackPagesAndContinueInto:with: */ +void +initStackPagesAndContinueIntowith(void (*continuation)(void *), void *argument) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt stackPageBytes; + usqIntptr_t stackZoneBytes; + void *theStackMemory; + + /* begin ensureInitializeStackZone */ + if (!GIV(pages)) { + stackPageBytes = stackPageByteSize(); + stackZoneBytes = (GIV(numStackPages) * ((sizeof(CogStackPage)) + (stackPageByteSize()))) + BytesPerWord; + theStackMemory = alloca(stackZoneBytes); + memset(theStackMemory, 0, stackZoneBytes); + initializeStacknumSlotspageSize(theStackMemory, stackZoneBytes / BytesPerWord, stackPageBytes / BytesPerWord); + } + continuation(argument); +} + + +/* the vm has to convert aFilenameString via any canonicalization and + char-mapping and put the result in aCharBuffer. + Note the resolveAliases flag - this is an awful artefact of OSX and Apples + demented alias handling. When opening a file, the flag must be true, when + closing or renaming it must be false. Sigh. + */ + + /* StackInterpreter>>#ioFilename:fromString:ofLength:resolveAliases: */ +void +ioFilenamefromStringofLengthresolveAliases(char *aCharBuffer, char *aFilenameString, sqInt filenameLength, sqInt aBoolean) +{ + sqGetFilenameFromString(aCharBuffer, aFilenameString, filenameLength, aBoolean); +} + + +/* A base frame (first frame in a stack page) is so marked by having a null + saved fp. + */ + + /* StackInterpreter>>#isBaseFrame: */ +static NoDbgRegParms int +isBaseFrame(char *theFP) +{ + return (longAt(theFP + FoxSavedFP)) == 0; +} + + /* StackInterpreter>>#isBooleanObject: */ +sqInt +isBooleanObject(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (oop == GIV(trueObj)) + || (oop == GIV(falseObj)); +} + + /* StackInterpreter>>#isEmptyList: */ +static NoDbgRegParms int +isEmptyList(sqInt aLinkedList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(!(isForwarded(aLinkedList))); + return (longAt((void *)((aLinkedList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj); +} + + /* StackInterpreter>>#isFloatObject: */ +int +isFloatObject(sqInt oop) +{ + sqInt tagBits; + + return /* isFloatInstance: */ + ((tagBits = oop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(oop))) & (classIndexMask())) == ClassFloatCompactIndex); +} + + /* StackInterpreter>>#isFrame:onPage: */ +static NoDbgRegParms sqInt +isFrameonPage(char *aFrame, StackPage *aPage) +{ + char *prevFP; + char *theFP; + + assert(!((isFree(aPage)))); + + /* Walk the static chain making sure progress is being made, + and in the right direction, looking for a match with aFrame. */ + theFP = (aPage->headFP); + prevFP = theFP - BytesPerWord; + while (1) { + if (theFP == aFrame) { + return 1; + } + if (!((theFP > prevFP) + && (theFP < ((aPage->baseFP))))) break; + prevFP = theFP; + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + } + return 0; +} + + +/* Answer true if the oop is kind of Integer (Small or Large). */ + + /* StackInterpreter>>#isKindOfInteger: */ +sqInt +isKindOfInteger(sqInt oop) +{ + return ((((oop) & 7) == 1)) + || (/* isLargeIntegerInstance: */ + ((!(oop & (tagMask())))) + && ((((usqInt)(((longAt((void *)(oop))) & (classIndexMask())) - ClassLargeNegativeIntegerCompactIndex))) <= 1)); +} + + /* StackInterpreter>>#isLargeIntegerObject: */ +sqInt +isLargeIntegerObject(sqInt oop) +{ + return /* isLargeIntegerInstance: */ + ((!(oop & (tagMask())))) + && ((((usqInt)(((longAt((void *)(oop))) & (classIndexMask())) - ClassLargeNegativeIntegerCompactIndex))) <= 1); +} + + /* StackInterpreter>>#isLargeNegativeIntegerObject: */ +sqInt +isLargeNegativeIntegerObject(sqInt oop) +{ + return /* isInstanceOfClassLargeNegativeInteger: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassLargeNegativeIntegerCompactIndex); +} + + /* StackInterpreter>>#isLargePositiveIntegerObject: */ +sqInt +isLargePositiveIntegerObject(sqInt oop) +{ + return /* isInstanceOfClassLargePositiveInteger: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassLargePositiveIntegerCompactIndex); +} + + /* StackInterpreter>>#isLinkedExternalPrimitive: */ +static NoDbgRegParms sqInt +isLinkedExternalPrimitive(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt headerSqInt; + sqInt literal; + usqInt numSlots; + sqInt primIdx; + sqInt targetFunctionIndex; + + literal = 0; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + headerSqInt = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* primitiveIndexOfMethod:header: */ + if (((header & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((header >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIdx = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIdx = 0; + } + return (primIdx == PrimNumberExternalCall) + && ((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) > 0) + && ((isArray((literal = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord()))))))) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(literal)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(literal + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(literal - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4) + && (((targetFunctionIndex = longAt((void *)((literal + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord()))))))), + ((((targetFunctionIndex) & 7) == 1)) + && (((targetFunctionIndex >> 3)) > 0)))))); +} + + +/* Answer if the argument, which can be any object, is a live context. */ + + /* StackInterpreter>>#isLiveContext: */ +static NoDbgRegParms sqInt +isLiveContext(sqInt oop) +{ + assert(!((isOopForwarded(oop)))); + if (!(/* isContext: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + return 0; + } + if ((!((longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))) & (tagMask())))) { + return ((((longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1); + } + return !(isWidowedContext(oop)); +} + + /* StackInterpreter>>#isMarriedOrWidowedContext: */ +static NoDbgRegParms int +isMarriedOrWidowedContext(sqInt aContext) +{ + return ((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1); +} + + +/* This virtual machine provides two primitives that executes arbitrary + primitives, one + for indexed primitivces and one for named primitives. These meta + primitives are used + in the debugger to execute primitives while simulating execution. Spur + needs to know + the accessor depth for a primitive so that failures due to forwarders can + be fixed up + and retried. This method identifies such meta primitives so that + metaAccessorDepth can be substituted when appropriate. */ + + /* StackInterpreter>>#isMetaPrimitiveIndex: */ +static NoDbgRegParms sqInt +isMetaPrimitiveIndex(sqInt primIndex) +{ + return (primIndex == PrimNumberDoPrimitive) + || (primIndex == PrimNumberDoExternalCall); +} + + +/* Answer if the method is an external primitive call (prim 117) with a null + external primitive. + This is just for an assert in the CoInterpreter. */ + + /* StackInterpreter>>#isNullExternalPrimitiveCall: */ +static NoDbgRegParms sqInt +isNullExternalPrimitiveCall(sqInt aMethodObj) +{ + sqInt lit; + usqInt numSlots; + + if (!(((primitiveIndexOf(aMethodObj)) == PrimNumberExternalCall) + && ((literalCountOf(aMethodObj)) > 0))) { + return 0; + } + lit = longAt((void *)((aMethodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + return (/* isArray: */ + ((!(lit & (tagMask())))) + && (((byteAt((void *)(lit + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(lit)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(lit + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(lit - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4) + && (((longAt((void *)((lit + BaseHeaderSize) + (3U << (shiftForWord()))))) == ConstZero) + || ((longAt((void *)((lit + BaseHeaderSize) + (3U << (shiftForWord()))))) == ConstMinusOne))); +} + + +/* We save slots in the method cache by using the primitiveFunctionPointer + to hold either a function pointer or the index of a quick primitive. Since + quick primitive indices are small they can't be confused with function + addresses. */ + + /* StackInterpreter>>#isPrimitiveFunctionPointerAnIndex */ +static int +isPrimitiveFunctionPointerAnIndex(void) +{ + return (((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex; +} + + /* StackInterpreter>>#isQuickPrimitiveIndex: */ +int +isQuickPrimitiveIndex(sqInt anInteger) +{ + return ((anInteger >= 0x100) && (anInteger <= 519)); +} + + +/* Reading the sender, instructionPointer and stackPointer inst vars of a + context must take + account of potentially married contexts and fetch the state from the + frame. method, + closureOrNil and receiver can safely be fetched from the context without + checking. + */ + + /* StackInterpreter>>#isReadMediatedContextInstVarIndex: */ +int +isReadMediatedContextInstVarIndex(sqInt index) +{ + return index <= StackPointerIndex; +} + + /* StackInterpreter>>#isSingleContext: */ +static NoDbgRegParms int +isSingleContext(sqInt aContext) +{ + return (!((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))) & (tagMask()))); +} + + +/* Answer if aContext is married or widowed and still married. + If a context is widowed then turn it into a single dead context. */ + + /* StackInterpreter>>#isStillMarriedContext: */ +static NoDbgRegParms sqInt +isStillMarriedContext(sqInt aContext) +{ + return (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext))); +} + + +/* Answer if aClosure is a vanilla BlockClosure as in the first Cog release, + BlockClosure laid out as + 'outerContext, startpc, numArgs. FullBlockClosure is laid out as + outerContext, method, numArgs, receiver. + So either answer true if we're not supporting FullBlockClosure, or test + the startpc/method field. */ + + /* StackInterpreter>>#isVanillaBlockClosure: */ +static NoDbgRegParms int +isVanillaBlockClosure(sqInt aClosure) +{ + assert((isPointers(aClosure)) + && ((numSlotsOf(aClosure)) >= ClosureFirstCopiedValueIndex)); + return ((((longAt((void *)((aClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1); +} + + +/* Answer if the argument is married to a live frame or not. + This method is safe for use only when no frameContext fields may be + forwarded (as maybe the case when scavenging). Post become: all + frameContext fields are followed, and hence normally no following of + frameContext fields is necessary. */ +/* i.e. inline into isWidowedContext: */ + + /* StackInterpreter>>#isWidowedContextNoConvert: */ +static NoDbgRegParms sqInt +isWidowedContextNoConvert(sqInt aOnceMarriedContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anInteger; + sqInt senderOop; + char *shouldBeFrameCallerField; + char *theFrame; + StackPage *thePage; + + assert((isContext(aOnceMarriedContext)) + && (isMarriedOrWidowedContext(aOnceMarriedContext))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + if (!((isFree(thePage)) + || (theFrame < ((thePage->headFP))))) { + /* The frame pointer is within the bounds of a live page. + Now check if it matches a frame. */ + anInteger = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + + /* begin withoutSmallIntegerTags: */ + assert((((anInteger) & 7) == 1)); + shouldBeFrameCallerField = ((char *)(anInteger - (smallIntegerTag()))); + if (((((char *)(longAt(theFrame + FoxSavedFP)))) == shouldBeFrameCallerField) + && (/* frameHasContext: */ + ((((usqInt)(longAt(theFrame + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFrame + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFrame + FoxIFrameFlags) + 2)) != 0))) { + assert(!(((isFrameonPage(theFrame, thePage)) + && (isForwarded(frameContext(theFrame)))))); + if ((longAt(theFrame + FoxThisContext)) == aOnceMarriedContext) { + return 0; + } + } + } + return 1; +} + + +/* Wrining any inst vars of a context must take account of potentially + married contexts + and set the state in the frame. Inst vars in subclasses don't need + mediation; subclasses + can't marry. */ + + /* StackInterpreter>>#isWriteMediatedContextInstVarIndex: */ +int +isWriteMediatedContextInstVarIndex(sqInt index) +{ + return index <= ReceiverIndex; +} + + +/* Support for external primitives. */ + + /* StackInterpreter>>#is:KindOfClass: */ +sqInt +isKindOfClass(sqInt oop, sqInt aClass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt oopClass; + sqInt tagBits; + + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while ((oopClass != GIV(nilObj)) + && ((/* isPointers: */ + ((!(oopClass & (tagMask())))) + && (((byteAt((void *)(oopClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOfAny(oopClass)) > InstanceSpecificationIndex))) { + if (oopClass == aClass) { + return 1; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + return 0; +} + + +/* Support for external primitives. */ + + /* StackInterpreter>>#is:KindOf: */ +sqInt +isKindOf(sqInt oop, char *className) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt oopClass; + sqInt tagBits; + + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while (!(oopClass == GIV(nilObj))) { + if (classNameOfIs(oopClass, className)) { + return 1; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + return 0; +} + + +/* Support for external primitives */ + + /* StackInterpreter>>#is:MemberOf: */ +sqInt +isMemberOf(sqInt oop, char *className) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oopClass; + sqInt tagBits; + + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + return classNameOfIs(oopClass, className); +} + + /* StackInterpreter>>#lengthOfNameOfClass: */ +static NoDbgRegParms sqInt +lengthOfNameOfClass(sqInt classOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + usqInt numSlots; + usqInt numSlots1; + usqInt numSlotsUsqInt; + sqInt objOop; + + /* begin numSlotsOf: */ + assert((classIndexOf(classOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(classOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (numSlots == GIV(metaclassNumSlots)) { + return lengthOfNameOfClass(longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord()))))))); + } + if (numSlots <= GIV(classNameIndex)) { + return 0; + } + objOop = longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(classNameIndex)) << (shiftForWord())))))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + numSlots1 = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots1; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots1 << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots1 << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots1 << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots1; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + /* StackInterpreter>>#literal:ofMethod: */ +sqInt +literalofMethod(sqInt offset, sqInt methodPointer) +{ + return longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); +} + + +/* This entry point needs to be implemented for the interpreter proxy. + Since BitBlt is now a plugin we need to look up + BitBltPlugin:=loadBitBltFrom and call it. This entire mechanism should + eventually go away and be + replaced with a dynamic lookup from BitBltPlugin itself but for backward + compatibility this stub is provided + */ + + /* StackInterpreter>>#loadBitBltFrom: */ +sqInt +loadBitBltFrom(sqInt bb) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void *fn; + + fn = ioLoadFunctionFrom("loadBitBltFrom", "BitBltPlugin"); + if (!fn) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return ((sqInt (*)(sqInt))fn)(bb); +} + + /* StackInterpreter>>#loadInitialContext */ +void +loadInitialContext(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt activeProc; + sqInt obj; + sqInt objOop; + + runLeakCheckerFor(GCModeFull); + + /* primitiveSuspend needs to know the class of LinkedList */ + + /* begin getClassTagOfLinkedList */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + objOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + obj = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + GIV(classLinkedListClassTag) = (longAt((void *)(obj))) & (classIndexMask()); + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + activeContext = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#longPrintOop: */ +void +longPrintOop(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aClass; + int byte; + char *className; + sqInt classPointer; + sqInt cls; + CogMethod *cogMethod; + sqInt column; + sqInt contextSize; + sqInt fieldOop; + sqInt fmt; + sqInt fmtSqInt; + sqInt header; + sqInt headerSqInt; + sqInt i; + usqInt index; + sqInt lastIndex; + usqInt lastPointer; + sqInt length; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objOop; + sqInt oopClass; + sqInt sp; + sqInt tagBits; + char *where; + + length = 0; + if (((oop & (tagMask())) != 0)) { + printImmediateObjecton(oop, GIV(transcript)); + return; + } + if (!(addressCouldBeObj(oop))) { + /* begin printCantBeObject:on: */ + fprintf(GIV(transcript), + "%p%s\n", + ((void *)oop), + ((where = whereIsMaybeCodeThing(oop)) + ? where + : (oop & 7 /* (allocationUnit - 1) */ + ? " is misaligned" + : whereIs(oop)))); + return; + } + if (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + printFreeObjecton(oop, GIV(transcript)); + return; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + printForwarderon(oop, GIV(transcript)); + return; + } + if ((cls = fetchClassOfNonImm(oop))) { + className = nameOfClasslengthInto(cls, (&length)); + fprintf(GIV(transcript), + "%p: a(n) %.*s", + ((void *)oop), + ((int) length), + className); + fprintf(GIV(transcript), + "(%" PRIxSQINT "=>%p)", + (longAt((void *)(oop))) & (classIndexMask()), + ((void *)cls)); + } + else { + fprintf(GIV(transcript), + "%p has a nil class!!\n", + ((void *)oop)); + } + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + fprintf(GIV(transcript), + " format %" PRIxSQINT "", + fmt); + if (fmt > 5 /* lastPointerFormat */) { + fprintf(GIV(transcript), + " nbytes %" PRIdSQINT "", + numBytesOf(oop)); + } + else { + if (/* isIndexableFormat: */ + (fmt >= (arrayFormat())) + && ((fmt <= (weakArrayFormat())) + || (fmt >= (sixtyFourBitIndexableFormat())))) { + /* begin lengthOf: */ + fmtSqInt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmtSqInt <= (ephemeronFormat())) { + length = numSlots; + goto l2; + } + if (fmtSqInt >= (firstByteFormat())) { + length = ((numSlots << (shiftForWord()))) - (fmtSqInt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmtSqInt >= (firstShortFormat())) { + length = ((numSlots << ((shiftForWord()) - 1))) - (fmtSqInt & 3); + goto l2; + } + if (fmtSqInt >= (firstLongFormat())) { + length = ((numSlots << ((shiftForWord()) - 2))) - (fmtSqInt & 1); + goto l2; + } + if (fmtSqInt == (sixtyFourBitIndexableFormat())) { + length = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + length = 0; + /* end lengthOf: */ +l2: + fprintf(GIV(transcript), + " size %" PRIdSQINT "", + length - (fixedFieldsOfformatlength(oop, fmt, length))); + } + } + + /* begin printHeaderTypeOf:on: */ + fprintf(GIV(transcript), + " hdr%d %c%c%c%c%c", + ((byteAt((void *)(oop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? 16 + : 8), + ((((usqInt)((byteAt((void *)(oop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1 + ? 'i' + : '.'), + ((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())) + ? 'r' + : '.'), + ((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift())) + ? 'p' + : '.'), + ((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (markedBitByteShift())) + ? 'm' + : '.'), + ((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (greyBitByteShift())) + ? 'g' + : '.')); + fprintf(GIV(transcript), + " hash %x\n", + (long32At((void *)(oop + 4))) & (identityHashHalfWordMask())); + if (/* isPureBitsFormat: */ + (fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat()))) { + /* This will answer false if splObj: ClassAlien is nilObject */ + + /* begin is:KindOfClass: */ + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while ((oopClass != GIV(nilObj)) + && ((/* isPointers: */ + ((!(oopClass & (tagMask())))) + && (((byteAt((void *)(oopClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOfAny(oopClass)) > InstanceSpecificationIndex))) { + if (oopClass == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord())))))))) { + fprintf(GIV(transcript), + " datasize %" PRIdSQINT " %s @ %p\n", + longAt((void *)(oop + BaseHeaderSize)), + ((longAt((void *)(oop + BaseHeaderSize))) < 0 + ? "indirect" + : (longAt((void *)(oop + BaseHeaderSize)) + ? "direct" + : "pointer")), + startOfAlienData(oop)); + return; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + classPointer = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord())))))); + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, objOop); + } + aClass = objOop; + + /* begin is:KindOfClass: */ + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while ((oopClass != GIV(nilObj)) + && ((/* isPointers: */ + ((!(oopClass & (tagMask())))) + && (((byteAt((void *)(oopClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOfAny(oopClass)) > InstanceSpecificationIndex))) { + if (oopClass == aClass) { + printStringDataOfon(oop, GIV(transcript)); + return; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + printNonPointerDataOfon(oop, GIV(transcript)); + return; + } + + /* begin numPointerSlotsOf: */ + fmtSqInt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if (fmtSqInt <= 5 /* lastPointerFormat */) { + if ((fmtSqInt == (indexablePointersFormat())) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l3; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(oop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l3: + lastPointer = CtxtTempFrameStart + contextSize; + goto l4; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())); + lastPointer = (((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l4; + } + if (fmtSqInt == (forwardedFormat())) { + lastPointer = 1; + goto l4; + } + if (fmtSqInt < (firstCompiledMethodFormat())) { + lastPointer = 0; + goto l4; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(oop)); + headerSqInt = longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + lastPointer = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l4: + lastIndex = ((0x100 < lastPointer) ? 0x100 : lastPointer); + if (lastIndex > 0) { + for (i = 1; i <= lastIndex; i += 1) { + fieldOop = longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)((i - 1)) << (shiftForWord())))))); + + /* begin space */ + printChar(' '); + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)(i - 1))); + printChar(' '); + printHex(fieldOop); + + /* begin space */ + printChar(' '); + if ((i == 1) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) { + /* begin printMethodHeaderOop: */ + /* begin isCogMethodReference: */ + assert(((((fieldOop) & 7) == 1)) + || (((((usqInt)fieldOop)) < (startOfMemory())) + && ((((usqInt)fieldOop)) >= (minCogMethodAddress())))); + if ((!(fieldOop & (smallIntegerTag())))) { + if ((cogMethod = methodFor(((void *)fieldOop)))) { + printHex(fieldOop); + + /* begin space */ + printChar(' '); + printDecodeMethodHeaderOop((cogMethod->methodHeader)); + goto l1; + } + } + printDecodeMethodHeaderOop(fieldOop); + /* end printMethodHeaderOop: */ +l1:; + } + else { + printOopShortInner(fieldOop); + } + cr(); + } + } + if (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + lastPointer = (((literalCountOf(oop)) + LiteralStart) * BytesPerOop) + 1; + + /* begin lengthOf: */ + fmtSqInt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmtSqInt <= (ephemeronFormat())) { + lastIndex = numSlots; + goto l5; + } + if (fmtSqInt >= (firstByteFormat())) { + lastIndex = ((numSlots << (shiftForWord()))) - (fmtSqInt & 7); + goto l5; + } + + /* bytes, including CompiledMethod */ + if (fmtSqInt >= (firstShortFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 1))) - (fmtSqInt & 3); + goto l5; + } + if (fmtSqInt >= (firstLongFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 2))) - (fmtSqInt & 1); + goto l5; + } + if (fmtSqInt == (sixtyFourBitIndexableFormat())) { + lastIndex = numSlots; + goto l5; + } + + /* fmt = self forwardedFormat */ + lastIndex = 0; + /* end lengthOf: */ +l5: + if ((lastIndex - lastPointer) > 0x100) { + lastIndex = lastPointer + 0x100; + } + column = 1; + for (index = lastPointer; index <= lastIndex; index += 1) { + if (column == 1) { + fprintf(GIV(transcript), + "%10p ", + ((void *)(((oop + BaseHeaderSize) + index) - 1))); + } + byte = ((int) (byteAt((void *)((oop + BaseHeaderSize) + (index - 1))))); + fprintf(GIV(transcript), + "%02x/%-+3d%c", + byte, + byte, + (column == 8 + ? '\n' + : ' ')); + if (((column += 1)) > 8) { + column = 1; + } + } + if ((lengthOf(oop)) > lastIndex) { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "..."); + } + if (((column >= 2) && (column <= 7))) { + cr(); + } + } + else { + if (lastPointer > lastIndex) { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "..."); + cr(); + } + } +} + + +/* Answer the relevant long store temp bytecode, which indicates it has a + primitive error code. + */ +/* 234 11101010 i i i i i i i i Store Temporary Variable #iiiiiiii */ +/* 129 10000001 jjkkkkkk Store (Receiver Variable, Temporary Location, + Illegal, Literal Variable) [jj] #kkkkkk */ + + /* StackInterpreter>>#longStoreBytecodeForHeader: */ +sqInt +longStoreBytecodeForHeader(sqInt methodHeader) +{ + return ((((sqLong) methodHeader)) < 0 + ? AltLongStoreBytecode + : LongStoreBytecode); +} + + +/* This method implements a simple method lookup cache. If an entry for the + given selector and classTag is found in the cache, set the values of + 'newMethod' and + 'primitiveFunctionPointer' and answer true. Otherwise, answer false. */ + + /* StackInterpreter>>#lookupInMethodCacheSel:classTag: */ +static NoDbgRegParms sqInt +lookupInMethodCacheSelclassTag(sqInt selector, sqInt classTag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt hash; + usqInt probe; + + /* begin inlineLookupInMethodCacheSel:classTag: */ + /* shift drops two low-order zeros from addresses */ + hash = selector ^ ((((usqInt)(classTag) << 2))); + + /* first probe */ + probe = hash & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == selector) + && ((GIV(methodCache)[probe + MethodCacheClass]) == classTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + return 1; + } + + /* second probe */ + probe = (((usqInt)(hash)) >> 1) & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == selector) + && ((GIV(methodCache)[probe + MethodCacheClass]) == classTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + return 1; + } + probe = (((usqInt)(hash)) >> 2) & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == selector) + && ((GIV(methodCache)[probe + MethodCacheClass]) == classTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + return 1; + } + return 0; +} + + /* StackInterpreter>>#lookupMethodInClass: */ +static NoDbgRegParms sqInt +lookupMethodInClass(sqInt class) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentClass; + sqInt dictionary; + sqInt found; + sqInt index; + usqInt length; + sqInt mask; + sqInt methodArray; + sqInt nextSelector; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt tagBits; + sqInt wrapAround; + + assert(addressCouldBeClassObj(class)); + + /* begin lookupBreakFor: */ + if ((breakSelectorLength <= 0) + && (/* shouldBreakForLookupIn:given: */ + (breakLookupClassTag) + && ((class == breakLookupClassTag) + || (((long32At((void *)(class + 4))) & (identityHashHalfWordMask())) == breakLookupClassTag)))) { + warning("lookup class send break (heartbeat suppressed)"); + } + currentClass = class; + while (currentClass != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + dictionary = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + assert(isNonImmediate(dictionary)); + if ((!((longAt((void *)(dictionary))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + dictionary = fixFollowedFieldofObjectwithInitialValue(MethodDictionaryIndex, currentClass, dictionary); + } + if (dictionary == GIV(nilObj)) { + /* MethodDict pointer is nil (hopefully due a swapped out stub) + -- raise exception #cannotInterpret:. */ + createActualMessageTo(class); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotInterpret) << (shiftForWord())))))); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), /* fetchClassTagOf: */ + ((tagBits = 0) + ? tagBits + : (longAt((void *)(null))) & (classIndexMask()))); + return lookupMethodInClass(superclassOf(currentClass)); + } + + /* begin lookupMethodInDictionary: */ + /* begin numSlotsOf: */ + assert((classIndexOf(dictionary)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(dictionary + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(dictionary - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + mask = (length - SelectorStart) - 1; + + /* Use linear search on small dictionaries; its cheaper. + Also the limit can be set to force linear search of all dictionaries, which supports the + booting of images that need rehashing (e.g. because a tracer has generated an image + with different hashes but hasn't rehashed it yet.) */ + if (mask <= GIV(methodDictLinearSearchLimit)) { + index = 0; + while (index <= mask) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)((index + SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOop = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(index, methodArray, objOop); + } + GIV(newMethod) = objOop; + found = 1; + goto l1; + } + index += 1; + } + found = 0; + goto l1; + } + index = SelectorStart + (mask & ((((GIV(messageSelector) & (tagMask())) != 0) + ? (GIV(messageSelector) >> 3) + : (long32At((void *)(GIV(messageSelector) + 4))) & (identityHashHalfWordMask())))); + + /* It is assumed that there are some nils in this dictionary, and search will + stop when one is encountered. However, if there are no nils, then wrapAround + will be detected the second time the loop gets to the end of the table. */ + wrapAround = 0; + while (1) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (nextSelector == GIV(nilObj)) { + found = 0; + goto l1; + } + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOop = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(index - SelectorStart, methodArray, objOop); + } + GIV(newMethod) = objOop; + found = 1; + goto l1; + } + index += 1; + if (index == length) { + if (wrapAround) { + found = 0; + goto l1; + } + wrapAround = 1; + index = SelectorStart; + } + } + found = 0; + /* end lookupMethodInDictionary: */ +l1: + if (found) { + return currentClass; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOopSqInt = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOopSqInt)); + if ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOopSqInt); + } + currentClass = objOopSqInt; + } + + /* Could not find #doesNotUnderstand: -- unrecoverable error. */ + if (GIV(messageSelector) == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))))) { + error("Recursive not understood error encountered"); + } + + /* Cound not find a normal message -- raise exception #doesNotUnderstand: */ + createActualMessageTo(class); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))); + sendBreakpointclassTag(GIV(messageSelector) + BaseHeaderSize, lengthOf(GIV(messageSelector)), (long32At((void *)(class + 4))) & (identityHashHalfWordMask())); + return lookupMethodInClass(class); +} + + +/* Lookup messageSelector in class. Answer 0 on success. Answer the splObj: + index for the error selector to use on failure rather than performing MNU + processing etc. */ + + /* StackInterpreter>>#lookupMNUInClass: */ +static NoDbgRegParms sqInt +lookupMNUInClass(sqInt class) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentClass; + sqInt dictionary; + sqInt found; + sqInt index; + usqInt length; + sqInt mask; + sqInt methodArray; + sqInt nextSelector; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt wrapAround; + + /* begin lookupBreakFor: */ + if ((breakSelectorLength <= 0) + && (/* shouldBreakForLookupIn:given: */ + (breakLookupClassTag) + && ((class == breakLookupClassTag) + || (((long32At((void *)(class + 4))) & (identityHashHalfWordMask())) == breakLookupClassTag)))) { + warning("lookup class send break (heartbeat suppressed)"); + } + currentClass = class; + while (currentClass != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + dictionary = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + assert(isNonImmediate(dictionary)); + if ((!((longAt((void *)(dictionary))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + dictionary = fixFollowedFieldofObjectwithInitialValue(MethodDictionaryIndex, currentClass, dictionary); + } + if (dictionary == GIV(nilObj)) { + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOop); + } + GIV(lkupClass) = objOop; + return SelectorCannotInterpret; + } + + /* begin lookupMethodInDictionary: */ + /* begin numSlotsOf: */ + assert((classIndexOf(dictionary)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(dictionary + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(dictionary - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + mask = (length - SelectorStart) - 1; + + /* Use linear search on small dictionaries; its cheaper. + Also the limit can be set to force linear search of all dictionaries, which supports the + booting of images that need rehashing (e.g. because a tracer has generated an image + with different hashes but hasn't rehashed it yet.) */ + if (mask <= GIV(methodDictLinearSearchLimit)) { + index = 0; + while (index <= mask) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)((index + SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(index, methodArray, objOopSqInt); + } + GIV(newMethod) = objOopSqInt; + found = 1; + goto l1; + } + index += 1; + } + found = 0; + goto l1; + } + index = SelectorStart + (mask & ((((GIV(messageSelector) & (tagMask())) != 0) + ? (GIV(messageSelector) >> 3) + : (long32At((void *)(GIV(messageSelector) + 4))) & (identityHashHalfWordMask())))); + + /* It is assumed that there are some nils in this dictionary, and search will + stop when one is encountered. However, if there are no nils, then wrapAround + will be detected the second time the loop gets to the end of the table. */ + wrapAround = 0; + while (1) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (nextSelector == GIV(nilObj)) { + found = 0; + goto l1; + } + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(index - SelectorStart, methodArray, objOopSqInt); + } + GIV(newMethod) = objOopSqInt; + found = 1; + goto l1; + } + index += 1; + if (index == length) { + if (wrapAround) { + found = 0; + goto l1; + } + wrapAround = 1; + index = SelectorStart; + } + } + found = 0; + /* end lookupMethodInDictionary: */ +l1: + if (found) { + addNewMethodToCache(class); + return 0; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOopSqInt = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOopSqInt)); + if ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOopSqInt); + } + currentClass = objOopSqInt; + } + GIV(lkupClass) = class; + return SelectorDoesNotUnderstand; +} + + +/* Lookup messageSelector in class. Answer 0 on success. Answer the splObj: + index for the error selector to use on failure rather than performing MNU + processing etc. */ + + /* StackInterpreter>>#lookupOrdinaryNoMNUEtcInClass: */ +static NoDbgRegParms sqInt +lookupOrdinaryNoMNUEtcInClass(sqInt class) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentClass; + sqInt dictionary; + sqInt found; + sqInt index; + usqInt length; + sqInt mask; + sqInt methodArray; + sqInt nextSelector; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt wrapAround; + + /* begin lookupBreakFor: */ + if ((breakSelectorLength <= 0) + && (/* shouldBreakForLookupIn:given: */ + (breakLookupClassTag) + && ((class == breakLookupClassTag) + || (((long32At((void *)(class + 4))) & (identityHashHalfWordMask())) == breakLookupClassTag)))) { + warning("lookup class send break (heartbeat suppressed)"); + } + currentClass = class; + while (currentClass != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + dictionary = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + assert(isNonImmediate(dictionary)); + if ((!((longAt((void *)(dictionary))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + dictionary = fixFollowedFieldofObjectwithInitialValue(MethodDictionaryIndex, currentClass, dictionary); + } + if (dictionary == GIV(nilObj)) { + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOop); + } + GIV(lkupClass) = objOop; + return SelectorCannotInterpret; + } + + /* begin lookupMethodInDictionary: */ + /* begin numSlotsOf: */ + assert((classIndexOf(dictionary)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(dictionary + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(dictionary - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + mask = (length - SelectorStart) - 1; + + /* Use linear search on small dictionaries; its cheaper. + Also the limit can be set to force linear search of all dictionaries, which supports the + booting of images that need rehashing (e.g. because a tracer has generated an image + with different hashes but hasn't rehashed it yet.) */ + if (mask <= GIV(methodDictLinearSearchLimit)) { + index = 0; + while (index <= mask) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)((index + SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(index, methodArray, objOopSqInt); + } + GIV(newMethod) = objOopSqInt; + found = 1; + goto l1; + } + index += 1; + } + found = 0; + goto l1; + } + index = SelectorStart + (mask & ((((GIV(messageSelector) & (tagMask())) != 0) + ? (GIV(messageSelector) >> 3) + : (long32At((void *)(GIV(messageSelector) + 4))) & (identityHashHalfWordMask())))); + + /* It is assumed that there are some nils in this dictionary, and search will + stop when one is encountered. However, if there are no nils, then wrapAround + will be detected the second time the loop gets to the end of the table. */ + wrapAround = 0; + while (1) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (nextSelector == GIV(nilObj)) { + found = 0; + goto l1; + } + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(index - SelectorStart, methodArray, objOopSqInt); + } + GIV(newMethod) = objOopSqInt; + found = 1; + goto l1; + } + index += 1; + if (index == length) { + if (wrapAround) { + found = 0; + goto l1; + } + wrapAround = 1; + index = SelectorStart; + } + } + found = 0; + /* end lookupMethodInDictionary: */ +l1: + if (found) { + addNewMethodToCache(class); + return 0; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOopSqInt = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOopSqInt)); + if ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOopSqInt); + } + currentClass = objOopSqInt; + } + GIV(lkupClass) = class; + return SelectorDoesNotUnderstand; +} + + +/* Lookup selector in class. Answer the method or nil. This is a debugging + routine. It does /not/ side-effect lookupClass or newMethod. */ + + /* StackInterpreter>>#lookupSelector:inClass: */ +sqInt +lookupSelectorinClass(sqInt selector, sqInt class) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentClass; + sqInt dictionary; + usqInt index; + usqInt length; + sqInt mask; + sqInt meth; + sqInt methodArray; + sqInt nextSelector; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt wrapAround; + + currentClass = class; + while (currentClass != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + dictionary = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + assert(isNonImmediate(dictionary)); + if ((!((longAt((void *)(dictionary))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + dictionary = fixFollowedFieldofObjectwithInitialValue(MethodDictionaryIndex, currentClass, dictionary); + } + if (dictionary == GIV(nilObj)) { + return null; + } + + /* begin lookupMethodFor:InDictionary: */ + /* begin numSlotsOf: */ + assert((classIndexOf(dictionary)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(dictionary + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(dictionary - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + mask = (length - SelectorStart) - 1; + index = SelectorStart + (mask & ((((selector & (tagMask())) != 0) + ? (selector >> 3) + : (long32At((void *)(selector + 4))) & (identityHashHalfWordMask())))); + + /* It is assumed that there are some nils in this dictionary, and search will + stop when one is encountered. However, if there are no nils, then wrapAround + will be detected the second time the loop gets to the end of the table. */ + wrapAround = 0; + while (1) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((index << (shiftForWord()))))); + if (nextSelector == GIV(nilObj)) { + meth = null; + goto l1; + } + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == selector) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(index - SelectorStart, methodArray, objOopSqInt); + } + meth = objOopSqInt; + goto l1; + } + index += 1; + if (index == length) { + if (wrapAround) { + meth = null; + goto l1; + } + wrapAround = 1; + index = SelectorStart; + } + } + meth = null; + /* end lookupMethodFor:InDictionary: */ +l1: + if (meth) { + return meth; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOop); + } + currentClass = objOop; + } + return null; +} + + +/* Convert married contexts to widowed contexts, and in the CoInterpreter, + map any machine code pcs to bytecode pcs, and then nil all slots beyond + top of stack, so that the context can be resumed on any VM. */ + + /* StackInterpreter>>#makeContextSnapshotSafe: */ +static NoDbgRegParms void +makeContextSnapshotSafe(sqInt ctxt) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + usqInt numSlots; + sqInt toDoLimit; + sqInt toDoStart; + + assert(isContext(ctxt)); + + /* The stack pages have already been discarded. Any remaining married contexts are actually widows. */ + if (((((longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin markContextAsDead: */ + assert(isContext(ctxt)); + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs(SenderIndex, ctxt, GIV(nilObj))); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, ctxt, GIV(nilObj))); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + ensureContextHasBytecodePC(ctxt); + } + + /* Fill slots beyond top of stack with nil */ + toDoStart = (fetchStackPointerOf(ctxt)) + CtxtTempFrameStart; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(ctxt)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(ctxt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(ctxt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = toDoStart; i <= toDoLimit; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs(i, ctxt, GIV(nilObj))); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } +} + + +/* make a Point xValue@yValue. + We know both will be integers so no value nor root checking is needed */ + + /* StackInterpreter>>#makePointwithxValue:yValue: */ +usqInt +makePointwithxValueyValue(sqInt xValue, sqInt yValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newObj; + usqInt numBytes; + usqInt pointResult; + + /* begin eeInstantiatePoint */ + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert(((YIndex + 1) >= 0) + && ((knownClassAtIndex(ClassPointCompactIndex)) != GIV(nilObj))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(knownClassAtIndex(ClassPointCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert((YIndex + 1) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (((YIndex + 1) < 1 + ? 8 /* allocationUnit */ + : (YIndex + 1) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + pointResult = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) (YIndex + 1))) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + ClassPointCompactIndex); + GIV(freeStart) += numBytes; + pointResult = newObj; + /* end eeInstantiatePoint */ +l1: + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pointResult)) + && (!(isForwarded(pointResult)))); + assert(validStorePointerUncheckedArgs(XIndex, pointResult, (((usqInt)xValue << 3) | 1))); + longAtput((void *)((pointResult + BaseHeaderSize) + ((((usqInt)(XIndex) << (shiftForWord()))))),(((usqInt)xValue << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pointResult)) + && (!(isForwarded(pointResult)))); + assert(validStorePointerUncheckedArgs(YIndex, pointResult, (((usqInt)yValue << 3) | 1))); + longAtput((void *)((pointResult + BaseHeaderSize) + ((((usqInt)(YIndex) << (shiftForWord()))))),(((usqInt)yValue << 3) | 1)); + return pointResult; +} + + +/* Map all oops in the interpreter's state to their new values + during garbage collection or a become: operation. */ +/* Assume: All traced variables contain valid oops. */ + + /* StackInterpreter>>#mapInterpreterOops */ +static void +mapInterpreterOops(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ipdelta; + sqInt result; + + mapStackPages(); + + /* begin mapMachineCode: */ + mapObjectReferencesInMachineCode(GIV(gcMode)); + mapPrimTraceLog(); + mapTraceLog(); + + /* begin mapVMRegisters */ + ipdelta = 0; + if (shouldRemapObj(GIV(method))) { + ipdelta = (/* method:includesAddress: */ + (GIV(instructionPointer) > GIV(method)) + && (GIV(instructionPointer) < (GIV(method) + ((((numSlotsOfAny(GIV(method))) << (shiftForWord()))) + BaseHeaderSize))) + ? GIV(instructionPointer) - GIV(method) + : 0); + GIV(method) = remapObj(GIV(method)); + if (ipdelta) { + GIV(instructionPointer) = GIV(method) + ipdelta; + } + } + if (/* shouldRemapOop: */ + ((!(GIV(newMethod) & (tagMask())))) + && (shouldRemapObj(GIV(newMethod)))) { + ipdelta = ((!ipdelta) + && (/* method:includesAddress: */ + (GIV(instructionPointer) > GIV(newMethod)) + && (GIV(instructionPointer) < (GIV(newMethod) + ((((numSlotsOfAny(GIV(newMethod))) << (shiftForWord()))) + BaseHeaderSize)))) + ? GIV(instructionPointer) - GIV(newMethod) + : 0); + + /* don't relocate twice!! */ + GIV(newMethod) = remapObj(GIV(newMethod)); + if (ipdelta) { + GIV(instructionPointer) = GIV(newMethod) + ipdelta; + } + } + + /* begin mapProfileState */ + /* begin profileStateDoUpdating: */ + if (GIV(profileProcess)) { + if ((result = (shouldRemapObj(GIV(profileProcess)) + ? remapObj(GIV(profileProcess)) + : 0))) { + GIV(profileProcess) = result; + } + } + if (GIV(profileMethod)) { + if ((result = (shouldRemapObj(GIV(profileMethod)) + ? remapObj(GIV(profileMethod)) + : 0))) { + GIV(profileMethod) = result; + } + } + if (GIV(profileSemaphore)) { + if ((result = (shouldRemapObj(GIV(profileSemaphore)) + ? remapObj(GIV(profileSemaphore)) + : 0))) { + GIV(profileSemaphore) = result; + } + } +# if LRPCheck + /* The longRunningPrimitiveCheckMethod (LRPCM) is sampled in an interrupt. Be very careful with it. + If longRunningPrimitiveCheckSequenceNumber (LRPCSN) = statCheckForEvents then LRPCM has + been recenty sampled and could be mapped or not, but it must be newMethod and we can simply + copy newMethod. If LRPCSN ~= statCheckForEvents then LRPCM must be some extant object and + needs to be remapped. */ + sqLowLevelMFence(); + if (GIV(longRunningPrimitiveCheckMethod)) { + if (GIV(longRunningPrimitiveCheckSequenceNumber) == GIV(statCheckForEvents)) { + GIV(longRunningPrimitiveCheckMethod) = GIV(newMethod); + } + else { + if (shouldRemapObj(GIV(longRunningPrimitiveCheckMethod))) { + GIV(longRunningPrimitiveCheckMethod) = remapObj(GIV(longRunningPrimitiveCheckMethod)); + } + } + sqLowLevelMFence(); + } + if (GIV(longRunningPrimitiveCheckSemaphore)) { + if (shouldRemapObj(GIV(longRunningPrimitiveCheckSemaphore))) { + GIV(longRunningPrimitiveCheckSemaphore) = remapObj(GIV(longRunningPrimitiveCheckSemaphore)); + } + } +# endif // LRPCheck + + if ((GIV(tempOop) != 0) + && (/* shouldRemapOop: */ + ((!(GIV(tempOop) & (tagMask())))) + && (shouldRemapObj(GIV(tempOop))))) { + GIV(tempOop) = remapObj(GIV(tempOop)); + } + if ((GIV(tempOop2) != 0) + && (/* shouldRemapOop: */ + ((!(GIV(tempOop2) & (tagMask())))) + && (shouldRemapObj(GIV(tempOop2))))) { + GIV(tempOop2) = remapObj(GIV(tempOop2)); + } +} + + +/* The prim trace log is a circular buffer of objects. If there is + an entry at primTraceLogIndex - 1 \\ PrimTraceBufferSize it has entries. + If there is something at primTraceLogIndex it has wrapped. */ + + /* StackInterpreter>>#mapPrimTraceLog */ +static void +mapPrimTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt entryOop; + sqInt i; + sqInt remainder; + + if (!(GIV(primTraceLog)[/* safe:mod: */ + (((remainder = (GIV(primTraceLogIndex) - 1) % PrimTraceLogSize)) < 0 + ? remainder + PrimTraceLogSize + : remainder)])) { + return; + } + if (GIV(primTraceLog)[GIV(primTraceLogIndex)]) { + for (i = GIV(primTraceLogIndex); i < PrimTraceLogSize; i += 1) { + entryOop = GIV(primTraceLog)[i]; + if ((entryOop != 0) + && (/* shouldRemapOop: */ + ((!(entryOop & (tagMask())))) + && (shouldRemapObj(entryOop)))) { + GIV(primTraceLog)[i] = (remapObj(entryOop)); + } + } + } + for (i = 0; i < GIV(primTraceLogIndex); i += 1) { + entryOop = GIV(primTraceLog)[i]; + if ((entryOop != 0) + && (/* shouldRemapOop: */ + ((!(entryOop & (tagMask())))) + && (shouldRemapObj(entryOop)))) { + GIV(primTraceLog)[i] = (remapObj(entryOop)); + } + } +} + + +/* The trace log is a circular buffer of pairs of entries. If there is + an entry at traceLogIndex - 3 \\ TraceBufferSize it has entries. + If there is something at traceLogIndex it has wrapped. */ + + /* StackInterpreter>>#mapTraceLog */ +static void +mapTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt limit; + sqInt oop; + sqInt remainder; + sqInt traceType; + + oop = 0; + limit = /* safe:mod: */ + (((remainder = (GIV(traceLogIndex) - 3) % TraceBufferSize)) < 0 + ? remainder + TraceBufferSize + : remainder); + if (!(GIV(traceLog)[limit])) { + return; + } + if (GIV(traceLog)[GIV(traceLogIndex)]) { + limit = TraceBufferSize - 3; + } + for (i = 0; i <= limit; i += 3) { + traceType = ((usqInt)((GIV(traceLog)[i]))) >> TraceTypeShift; + if ((traceType <= TraceBlockCreation) + && ((((oop = GIV(traceLog)[i + 1])) != 0) + && (/* shouldRemapOop: */ + ((!(oop & (tagMask())))) + && (shouldRemapObj(oop))))) { + GIV(traceLog)[i + 1] = (remapObj(oop)); + } + if ((/* extraIsObjectForTraceType: */ + (traceType == TraceSend) + || (traceType == TraceContextSwitch)) + && ((((oop = GIV(traceLog)[i + 2])) != 0) + && (/* shouldRemapOop: */ + ((!(oop & (tagMask())))) + && (shouldRemapObj(oop))))) { + GIV(traceLog)[i + 2] = (remapObj(oop)); + } + } +} + + +/* The prim trace log is a circular buffer of objects. If there is + an entry at primTraceLogIndex - 1 \\ PrimTraceBufferSize it has entries. + If there is something at primTraceLogIndex it has wrapped. */ + + /* StackInterpreter>>#markAndTracePrimTraceLog */ +static void +markAndTracePrimTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt entryOop; + sqInt i; + sqInt remainder; + + if (!(GIV(primTraceLog)[/* safe:mod: */ + (((remainder = (GIV(primTraceLogIndex) - 1) % PrimTraceLogSize)) < 0 + ? remainder + PrimTraceLogSize + : remainder)])) { + return; + } + if (GIV(primTraceLog)[GIV(primTraceLogIndex)]) { + for (i = GIV(primTraceLogIndex); i < PrimTraceLogSize; i += 1) { + entryOop = GIV(primTraceLog)[i]; + if ((entryOop != 0) + && ((!(entryOop & (tagMask()))))) { + markAndTrace(entryOop); + } + } + } + for (i = 0; i < GIV(primTraceLogIndex); i += 1) { + entryOop = GIV(primTraceLog)[i]; + if ((entryOop != 0) + && ((!(entryOop & (tagMask()))))) { + markAndTrace(entryOop); + } + } +} + + +/* GC of pages. Throwing away all stack pages on full GC is simple but + dangerous because it causes us to allocate lots of contexts immediately + before a GC. + Reclaiming pages whose top context is not referenced is poor because it + would take N incrementalGCs to reclaim N unused pages. Only the page whose + top context is not referred to by the bottom context of any other page + would be + reclaimed. Not until the next GC would the page whose top contect is the + previously reclaimed page's base frame's bottom context be reclaimed. + + Better is to not mark stack pages until their contexts are encountered. We + can eagerly trace the active page and the page reachable from its bottom + context if any, and so on. Other pages can be marked when we encounter a + married context. */ + + /* StackInterpreter>>#markAndTraceStackPages: */ +static NoDbgRegParms sqInt +markAndTraceStackPages(sqInt fullGCFlag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt context; + sqInt i; + void *pointer; + sqInt senderOop; + char *theFP; + StackPage *thePage; + StackPage *thePage1; + + + /* On an incremental GC simply consider all non-free stack pages to be roots. */ + if (!fullGCFlag) { + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + markAndTraceStackPage(thePage); + } + } + return null; + } + + /* On a full GC only eagerly trace pages referenced from + the base of the active page, i.e. on the active stack. */ + if (!GIV(stackPage)) { + return null; + } + thePage = GIV(stackPage); + do { + markAndTraceStackPage(thePage); + theFP = (thePage->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + context = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(context)); + assert((context == (nilObject())) + || (isContext(followMaybeForwarded(context)))); + if ((/* isContext: */ + ((!(context & (tagMask())))) + && (((longAt((void *)(context))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && ((((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(context)))))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + pointer = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(pointer), GIV(pages)); + assert(!(isFree(thePage))); + } + } while(((thePage->trace)) < StackPageTraced); + return 0; +} + + +/* The trace log is a circular buffer of pairs of entries. If there is an + entry at + traceLogIndex - 3 \\ TraceBufferSize it has entries. If there is something + at traceLogIndex it has wrapped. */ + + /* StackInterpreter>>#markAndTraceTraceLog */ +static void +markAndTraceTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt limit; + sqInt oop; + sqInt remainder; + sqInt traceType; + + limit = /* safe:mod: */ + (((remainder = (GIV(traceLogIndex) - 3) % TraceBufferSize)) < 0 + ? remainder + TraceBufferSize + : remainder); + if (!(GIV(traceLog)[limit])) { + return; + } + if (GIV(traceLog)[GIV(traceLogIndex)]) { + limit = TraceBufferSize - 3; + } + for (i = 0; i <= limit; i += 3) { + traceType = ((usqInt)((GIV(traceLog)[i]))) >> TraceTypeShift; + if (traceType <= TraceBlockCreation) { + oop = GIV(traceLog)[i + 1]; + if ((oop != 0) + && ((!(oop & (tagMask()))))) { + markAndTrace(oop); + } + } + if (/* extraIsObjectForTraceType: */ + (traceType == TraceSend) + || (traceType == TraceContextSwitch)) { + oop = GIV(traceLog)[i + 2]; + if ((oop != 0) + && ((!(oop & (tagMask()))))) { + markAndTrace(oop); + } + } + } +} + + +/* Trace any untraced pages */ + + /* StackInterpreter>>#markAndTraceUntracedReachableStackPages */ +static void +markAndTraceUntracedReachableStackPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt foundToBeTracedPage; + sqInt i; + StackPage *thePage; + + do { + foundToBeTracedPage = 0; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((!(isFree(thePage))) + && (((thePage->trace)) == StackPageReachedButUntraced)) { + foundToBeTracedPage = 1; + markAndTraceStackPage(thePage); + } + } + } while(foundToBeTracedPage); +} + + +/* This is a helper for primitiveObjectPointsTo so it *does not* check the + frameContext field because that is an implicit self-reference not present + in the state . + */ + + /* StackInterpreter>>#marriedContext:pointsTo:stackDeltaForCurrentFrame: */ +static NoDbgRegParms sqInt +marriedContextpointsTostackDeltaForCurrentFrame(sqInt spouseContext, sqInt anOop, sqInt stackDeltaForCurrentFrame) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *rcvrOffset; + sqInt senderOop; + char *theFP; + StackPage *thePage; + char *theSP; + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((spouseContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (theFP == GIV(framePointer)) { + theSP = GIV(stackPointer) + (stackDeltaForCurrentFrame * BytesPerWord); + } + else { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + theSP = findSPOfon(theFP, thePage); + } + if ((((anOop) & 7) == 1)) { + if ((anOop == ((((usqInt)(/* stackPointerIndexForFrame:WithSP: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (((usqInt)(((theFP + FoxMFReceiver) - theSP))) >> (shiftForWord())) + (((mframeCogMethod(theFP))->cmNumArgs)) + : (((usqInt)(((theFP + FoxIFReceiver) - theSP))) >> (shiftForWord())) + (byteAt((theFP + FoxIFrameFlags) + 1)))) << 3) | 1))) + || (anOop == (externalInstVarofContext(InstructionPointerIndex, spouseContext)))) { + return 1; + } + } + else { + if (anOop == (/* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)))) { + return 1; + } + if (longAt(theFP + FoxSavedFP)) { + if ((/* frameHasContext: */ + ((((usqInt)(longAt((((char *)(longAt(theFP + FoxSavedFP)))) + FoxMethod)))) < (startOfMemory()) + ? ((longAt((((char *)(longAt(theFP + FoxSavedFP)))) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt(((((char *)(longAt(theFP + FoxSavedFP)))) + FoxIFrameFlags) + 2)) != 0)) + && (anOop == (longAt((((char *)(longAt(theFP + FoxSavedFP)))) + FoxThisContext)))) { + return 1; + } + } + else { + if (anOop == (frameCallerContext(theFP))) { + return 1; + } + } + } + + /* Check stack and instruction pointer fields. + Check method and sender fields, avoiding unnecessarily reifying sender context. + Now check receiver, temps and stack contents */ + rcvrOffset = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + while (theSP <= rcvrOffset) { + if (anOop == (longAt(theSP))) { + return 1; + } + theSP += BytesPerWord; + } + + /* Finally check stacked receiver (closure field or duplicate of receiver) and arguments */ + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + rcvrOffset = theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord()))))); + while (theSP <= rcvrOffset) { + if (anOop == (longAt(theSP))) { + return 1; + } + theSP += BytesPerWord; + } + return 0; +} + + +/* Establish aContext at the base of a new stackPage, make the stackPage the + active one and set-up the interreter registers. This is used to boot the + system and bring it back after a snapshot. */ + + /* StackInterpreter>>#marryContextInNewStackPageAndInitializeInterpreterRegisters: */ +static NoDbgRegParms void +marryContextInNewStackPageAndInitializeInterpreterRegisters(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + sqInt header; + sqInt methodHeader; + StackPage *newPage; + sqInt top; + + assert(!GIV(stackPage)); + newPage = makeBaseFrameFor(aContext); + + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (newPage->headSP); + GIV(framePointer) = (newPage->headFP); + aMethodObj = longAt(((GIV(stackPage)->headFP)) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; +} + + +/* Assert-only check for leaks after external prim calls if checkForLeaks + includes the GCCheckPrimCall flag. + This is ionly really useful from a low-level C debugger, hence no + accessors for checkedPluginName. + */ + + /* StackInterpreter>>#maybeLeakCheckExternalPrimCall: */ +static NoDbgRegParms sqInt +maybeLeakCheckExternalPrimCall(sqInt aMethodObj) +{ + if ((((checkForLeaks & GCCheckPrimCall) != 0)) + && (((primitiveIndexOf(aMethodObj)) == PrimNumberExternalCall) + && (checkedPluginName == (fetchPointerofObject(0, longAt((void *)((aMethodObj + BaseHeaderSize) + (1U << (shiftForWord()))))))))) { + runLeakCheckerFor(GCCheckPrimCall); + } + return 1; +} + + +/* Answer the selector of a method, assuming its penultimate literal is + either a symbol or a pointer object whose first slot references the method + and whose second slot is a symbol (i.e. an AdditionalMethodState). If a + Symbol can't be found answer nil. This isn't satisfactory, as it puts a + lot of information + into the VM, but it is needed for adequate crash debugging at Cadence. + With full blocks as of 9/2016 the last literal of a CompiledBlock is a + back pointer + to the enclosing block or compiled method. */ + + /* StackInterpreter>>#maybeSelectorOfMethod: */ +sqInt +maybeSelectorOfMethod(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt maybeSelector; + usqInt numSlots; + sqInt penultimateLiteral; + sqInt ultimateLiteral; + + assert(isOopCompiledMethod(methodObj)); + ultimateLiteral = ultimateLiteralOf(methodObj); + if (/* isOopCompiledMethod: */ + ((!(ultimateLiteral & (tagMask())))) + && (((byteAt((void *)(ultimateLiteral + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) { + return maybeSelectorOfMethod(ultimateLiteral); + } + penultimateLiteral = penultimateLiteralOf(methodObj); + if (/* isWordsOrBytes: */ + ((!(penultimateLiteral & (tagMask())))) + && (isWordsOrBytesNonImm(penultimateLiteral))) { + return (((longAt((void *)(penultimateLiteral))) & (classIndexMask())) == (fetchClassTagOfNonImm(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))))) + ? penultimateLiteral + : 0); + } + return ((/* isPointers: */ + ((!(penultimateLiteral & (tagMask())))) + && (((byteAt((void *)(penultimateLiteral + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(penultimateLiteral)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(penultimateLiteral + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(penultimateLiteral - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= 2) + && (((longAt((void *)((penultimateLiteral + BaseHeaderSize) + (0U << (shiftForWord()))))) == methodObj) + && (((maybeSelector = longAt((void *)((penultimateLiteral + BaseHeaderSize) + (1U << (shiftForWord()))))), + (/* isWordsOrBytes: */ + ((!(maybeSelector & (tagMask())))) + && (isWordsOrBytesNonImm(maybeSelector))) + && (((longAt((void *)(maybeSelector))) & (classIndexMask())) == (fetchClassTagOfNonImm(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord()))))))))))))) + ? maybeSelector + : 0); +} + + /* StackInterpreter>>#methodArgumentCount */ +sqInt +methodArgumentCount(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(argumentCount); +} + + /* StackInterpreter>>#methodClassAssociationOf: */ +sqInt +methodClassAssociationOf(sqInt methodPointer) +{ + sqInt offset; + + offset = (literalCountOf(methodPointer)) - 1; + + /* begin literal:ofMethod: */ + return longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); +} + + +/* Answer the method class of a method which is the value of an Association + in the last literal, + or answer nil if there isn't one. + Using a read barrier here simplifies the become implementation and costs + very little + because the class index and ValueIndex of the association almost certainly + share a cache line. */ + + /* StackInterpreter>>#methodClassOf: */ +sqInt +methodClassOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt literal; + sqInt offset; + + offset = (literalCountOf(methodPointer)) - 1; + + /* begin followLiteral:ofMethod: */ + /* begin followField:ofObject: */ + literal = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = fixFollowedFieldofObjectwithInitialValue(offset + LiteralStart, methodPointer, literal); + } + return ((literal != GIV(nilObj)) + && (/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + ? (assert((numSlotsOf(literal)) > ValueIndex), + followFieldofObject(ValueIndex, literal)) + : GIV(nilObj)); +} + + /* StackInterpreter>>#methodPrimitiveIndex */ +sqInt +methodPrimitiveIndex(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + + if (!((addressCouldBeObj(GIV(newMethod))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return -1; + } + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin primitiveIndexOfMethod:header: */ + return (((methodHeader & AlternateHeaderHasPrimFlag) != 0) + ? ((firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize), + (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8)))) + : 0); +} + + +/* Sets the return value for a method. In the CoInterpreter we replace the + cumbersome primResult machinery. */ + + /* StackInterpreter>>#methodReturnBool: */ +sqInt +methodReturnBool(sqInt boolean) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + assert(!((failed()))); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (boolean + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return 0; +} + + +/* Sets the return value for a method. */ + + /* StackInterpreter>>#methodReturnFloat: */ +sqInt +methodReturnFloat(double aFloat) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + assert(!((failed()))); + + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),floatObjectOf(aFloat)); + GIV(stackPointer) = sp; + return 0; +} + + +/* Sets the return value for a method. In the CoInterpreter we replace the + cumbersome primResult machinery. */ + + /* StackInterpreter>>#methodReturnInteger: */ +sqInt +methodReturnInteger(sqInt integer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + assert(!((failed()))); + + /* begin pop:thenPushInteger: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),(((usqInt)integer << 3) | 1)); + GIV(stackPointer) = sp; + return 0; +} + + +/* Sets the return value for a method */ + + /* StackInterpreter>>#methodReturnReceiver */ +sqInt +methodReturnReceiver(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(!((failed()))); + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return 0; +} + + +/* Attempt to answer a ByteString for a given C string as the result of a + primitive. + */ + + /* StackInterpreter>>#methodReturnString: */ +NeverInline sqInt +methodReturnString(const char *aCString) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt result; + + assert(!((failed()))); + if (aCString) { + if ((result = stringForCString(aCString))) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); + } + else { + GIV(primFailCode) = PrimErrNoMemory; + } + } + else { + GIV(primFailCode) = PrimErrOperationFailed; + } + return 0; +} + + +/* Sets the return value for a method. In the CoInterpreter we replace the + cumbersome primResult machinery. */ + + /* StackInterpreter>>#methodReturnValue: */ +sqInt +methodReturnValue(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + assert(!((failed()))); + + /* begin pop:thenPush: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),oop); + GIV(stackPointer) = sp; + return 0; +} + + +/* A negative header selects the alternate bytecode set. */ + + /* StackInterpreter>>#methodUsesAlternateBytecodeSet: */ +int +methodUsesAlternateBytecodeSet(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + sqInt methodHeader; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin headerIndicatesAlternateBytecodeSet: */ + return (((sqLong) methodHeader)) < 0; +} + + +/* Answer if aMethodObj contains a primitive and uses the primitive error + code. + */ + + /* StackInterpreter>>#methodUsesPrimitiveErrorCode: */ +static NoDbgRegParms sqInt +methodUsesPrimitiveErrorCode(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + return ((/* primitiveIndexOfMethod:header: */ + (((methodHeader & AlternateHeaderHasPrimFlag) != 0) + ? ((firstBytecode = (aMethodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize), + (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8)))) + : 0)) > 0) + && ((/* longStoreBytecodeForHeader: */ + ((((sqLong) methodHeader)) < 0 + ? AltLongStoreBytecode + : LongStoreBytecode)) == (fetchByteofObject(((((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop) + 3 /* sizeOfCallPrimitiveBytecode: */, aMethodObj))); +} + + +/* The module with the given name was just unloaded. + Make sure we have no dangling references. */ + + /* StackInterpreter>>#moduleUnloaded: */ +EXPORT(void) +moduleUnloaded(char *aModuleName) +{ + if (!(strcmp(aModuleName, "SurfacePlugin"))) { + /* Surface plugin went away. Should never happen. But then, who knows */ + showSurfaceFn = 0; + } +} + + +/* Brain-damaged nameOfClass: for C VM. Does *not* answer Foo class for + metaclasses. Use e.g. classIsMeta: to avoid being fooled. */ + + /* StackInterpreter>>#nameOfClass: */ +static NoDbgRegParms char * +nameOfClass(sqInt classOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt maybeNameOop; + sqInt maybeThisClassOop; + usqInt numSlots; + usqInt numSlotsUsqInt; + + /* begin numSlotsOf: */ + assert((classIndexOf(classOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(classOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (numSlots == GIV(metaclassNumSlots)) { + maybeThisClassOop = longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord())))))); + if (addressCouldBeClassObj(maybeThisClassOop)) { + return nameOfClass(longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord()))))))); + } + return "bad class"; + } + if (!((numSlots > GIV(classNameIndex)) + && (((maybeNameOop = longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(classNameIndex)) << (shiftForWord()))))))), + /* isBytes: */ + ((!(maybeNameOop & (tagMask())))) + && (((byteAt((void *)(maybeNameOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))))) { + return "bad class"; + } + return firstIndexableField(maybeNameOop); +} + + +/* Brain-damaged nameOfClass: for C VM. Does *not* answer Foo class for + metaclasses. Use e.g. classIsMeta: to avoid being fooled. */ + + /* StackInterpreter>>#nameOfClass:lengthInto: */ +static NoDbgRegParms char * +nameOfClasslengthInto(sqInt classOop, sqInt *lengthPtr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt maybeNameOop; + sqInt maybeThisClassOop; + usqInt numSlots; + usqInt numSlotsUsqInt; + + /* begin numSlotsOf: */ + assert((classIndexOf(classOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(classOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (numSlots == GIV(metaclassNumSlots)) { + maybeThisClassOop = longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord())))))); + if (addressCouldBeClassObj(maybeThisClassOop)) { + return nameOfClasslengthInto(longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord())))))), lengthPtr); + } + lengthPtr[0] = 9; + return "bad class"; + } + if ((numSlots > GIV(classNameIndex)) + && (((maybeNameOop = longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(classNameIndex)) << (shiftForWord()))))))), + /* isBytes: */ + ((!(maybeNameOop & (tagMask())))) + && (((byteAt((void *)(maybeNameOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))))) { + lengthPtr[0] = (numBytesOfBytes(maybeNameOop)); + return firstIndexableField(maybeNameOop); + } + lengthPtr[0] = 9; + return "bad class"; +} + + +/* Answer true if there are no marked contexts on thePage. */ + + /* StackInterpreter>>#noMarkedContextsOnPage: */ +static NoDbgRegParms sqInt +noMarkedContextsOnPage(StackPage *thePage) +{ + char *theFP; + + assert(!(isFree(thePage))); + theFP = (thePage->headFP); + do { + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + if ((byteAt((void *)((longAt(theFP + FoxThisContext)) + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + return 0; + } + } + } while(((theFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0); + return 1; +} + + +/* Answer the number of effective pointer fields in the given context. */ + + /* StackInterpreter>>#numSlotsOfMarriedContext: */ +static NoDbgRegParms sqInt +numSlotsOfMarriedContext(sqInt aContext) +{ + sqInt contextSize; + + contextSize = stackPointerIndexForFrame(frameOfMarriedContext(aContext)); + assert((CtxtTempFrameStart + contextSize) <= (numSlotsOf(aContext))); + return CtxtTempFrameStart + contextSize; +} + + /* StackInterpreter>>#numStkPages */ +static sqInt +numStkPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(numStackPages); +} + + /* StackInterpreter>>#object:equalsString: */ +static NoDbgRegParms sqInt +objectequalsString(sqInt anOop, char *aCString) +{ + sqInt len; + + len = strlen(aCString); + return /* object:equalsString:ofSize: */ + (/* isBytes: */ + ((!(anOop & (tagMask())))) + && (((byteAt((void *)(anOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((!(((byteAt((void *)(anOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((numBytesOfBytes(anOop)) == len) + && ((strncmp(aCString, firstIndexableField(anOop), len)) == 0))); +} + + +/* This is the entry-point for plugins and primitives that wish to reacquire + the VM after having + released it via disownVM or callbacks that want to acquire it without + knowing their ownership + status. While this exists for the threaded FFI VM we use it to reset + newMethod and the + argumentCount after a callback. + + Answer 0 if the current thread is known to the VM (and on return owns the + VM). Answer 1 if the current thread is unknown to the VM and takes + ownership. Answer -1 if the current thread is unknown to the VM and fails + to take ownership. */ + + /* StackInterpreter>>#ownVM: */ +sqInt +ownVM(sqInt disownResult) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert((isOopCompiledMethod(GIV(newMethod))) + && ((argumentCountOf(GIV(newMethod))) == GIV(argumentCount))); + + /* From callbacks (see thunkEntry), we don't know our ownerhip. Stash newMethod + and argumentCount on the stack. */ + if (!disownResult) { + assert(GIV(primFailCode) == 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),(((usqInt)GIV(argumentCount) << 3) | 1)); + return 1; + } + if (((disownResult & DisownVMForFFICallFlag) != 0)) { + GIV(ffiCalloutVMHandle) = 0; + } + return 0; +} + + /* StackInterpreter>>#pcPreviousTo:inSqueakV3PlusClosuresOrSistaV1Method: */ +usqInt +pcPreviousToinSqueakV3PlusClosuresOrSistaV1Method(sqInt theIP, sqInt aMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt byte; + static unsigned char extensionSizes[] = { + 2, 2, 2, 2, 3, 2, 2, 1, 1, 1, 2, 3, 3, 3, 3, 4 + }; + sqInt header; + sqInt methodHeader; + usqInt previousPC; + usqInt thisIP; + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethod)); + header = longAt((void *)((aMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + /* begin pcPreviousTo:inSistaV1Method: */ + thisIP = ((((literalCountOf(aMethod)) + LiteralStart) * BytesPerOop) + aMethod) + BaseHeaderSize; + assert(oopisLessThan(thisIP, theIP)); + while (oopisLessThan(thisIP, theIP)) { + previousPC = thisIP; + do { + byte = byteAt((void *)(thisIP)); + thisIP += /* sizeOfSistaV1Bytecode: */ + (byte < 224 + ? 1 + : (byte < 0xF8 + ? 2 + : 3)); + } while(((byte >= 224) && (byte <= 225))); + } + return previousPC; + } + + /* begin pcPreviousTo:inSqueakV3PlusClosuresMethod: */ + thisIP = ((((literalCountOf(aMethod)) + LiteralStart) * BytesPerOop) + aMethod) + BaseHeaderSize; + assert(oopisLessThan(thisIP, theIP)); + while (oopisLessThan(thisIP, theIP)) { + previousPC = thisIP; + byte = byteAt((void *)(thisIP)); + thisIP += (byte <= 125 + ? 1 + : (byte >= 176 + ? 1 + : (byte >= 160 + ? 2 + : (byte >= 144 + ? 1 + : (byte >= 128 + ? extensionSizes[byte - 128] + : (assertf("illegal bytecode in sizeOfSqueakV3Bytecode:"), + 1)))))); + } + return previousPC; +} + + /* StackInterpreter>>#penultimateLiteralOf: */ +static NoDbgRegParms sqInt +penultimateLiteralOf(sqInt aMethodOop) +{ + sqInt offset; + + assert(isOopCompiledMethod(aMethodOop)); + offset = (literalCountOf(aMethodOop)) - 2; + + /* begin literal:ofMethod: */ + return longAt((void *)((aMethodOop + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#popStack */ +sqInt +popStack(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt top; + + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + return top; +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#pop: */ +sqInt +pop(sqInt nItems) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(stackPointer) += nItems * BytesPerWord; + return null; +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#pop:thenPush: */ +void +popthenPush(sqInt nItems, sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + longAtput((sp = GIV(stackPointer) + ((nItems - 1) * BytesPerWord)),oop); + GIV(stackPointer) = sp; +} + + +/* Update the displayBits after a GC may have moved it. + Answer if the displayBits appear valid. The wrinkle here is that the + displayBits could be a surface handle. */ + + /* StackInterpreter>>#postGCUpdateDisplayBits */ +static sqInt +postGCUpdateDisplayBits(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void *bitsNow; + sqInt bitsOop; + sqInt displayObj; + + displayObj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheDisplay) << (shiftForWord())))))); + if (!((/* isPointers: */ + ((!(displayObj & (tagMask())))) + && (((byteAt((void *)(displayObj + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((lengthOf(displayObj)) >= 4))) { + return 0; + } + + /* begin followOopField:ofObject: */ + bitsOop = longAt((void *)((displayObj + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isOopForwarded: */ + ((!(bitsOop & (tagMask())))) + && ((!((longAt((void *)(bitsOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + bitsOop = fixFollowedFieldofObjectwithInitialValue(0, displayObj, bitsOop); + } + if ((bitsOop == GIV(nilObj)) + || ((((bitsOop) & 7) == 1))) { + return 1; + } + + /* it ain't yet set + It's a surface; our work here is done... */ + assert((addressCouldBeObj(bitsOop)) + && (isWordsOrBytes(bitsOop))); + if (!((byteAt((void *)(bitsOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift())))) { + if (GIV(stackPage)) { + pinObject(bitsOop); + + /* begin followOopField:ofObject: */ + bitsOop = longAt((void *)((displayObj + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isOopForwarded: */ + ((!(bitsOop & (tagMask())))) + && ((!((longAt((void *)(bitsOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + bitsOop = fixFollowedFieldofObjectwithInitialValue(0, displayObj, bitsOop); + } + } + + /* If stackPage is nil we're snapshotting and now is not the time to pin. */ + bitsNow = firstIndexableField(bitsOop); + if (displayBits != bitsNow) { + displayBits = bitsNow; + ioNoteDisplayChangedwidthheightdepth(displayBits, displayWidth, displayHeight, displayDepth); + } + } + return 1; +} + + +/* With 0 args answers whether ioProcessEvents is enabled and being called. + With 1 arg expects a boolean which will enable ioProcessEvents if true and + disable it if false, answering its previous state. */ + + /* StackInterpreter>>#primitiveEventProcessingControl */ +EXPORT(void) +primitiveEventProcessingControl(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int enabled; + char *sp; + + enabled = inIOProcessEvents >= 0; + if (!GIV(argumentCount)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer)),/* booleanObjectOf: */ + (enabled + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return; + } + if (GIV(argumentCount) == 1) { + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + if (inIOProcessEvents < 0) { + inIOProcessEvents = 0; + } + } + else { + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + inIOProcessEvents = -1; + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (enabled + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; +} + + +/* Set PrimErrFFIException primitive failure and associated exceptionCode + (a.k.a. + secondaryErrorCode) and exceptionPC. Under control of the + ffiExceptionResponse flag, + if in a primitive with an error code and ffiCalloutVMHandle indicates + we're in an FFI call, + then fail the primitive. + ffiExceptionResponse < 0 never fail (i.e. always crash) + ffiExceptionResponse = 0 fail if method has a primitive error code + (default) ffiExceptionResponse > 0 always fail */ + + /* StackInterpreter>>#primitiveFailForFFIException:at: */ +void +primitiveFailForFFIExceptionat(usqLong exceptionCode, usqInt pc) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cogMethod; + usqInt i; + usqInt initialIP; + sqInt methodHeader; + usqInt numArgs; + usqInt numTemps; + sqInt object; + sqInt rcvr; + + if ((GIV(ffiCalloutVMHandle) == 0) + || (ffiExceptionResponse < 0)) { + return; + } + + /* i.e. not in an FFI call + i.e. never fail */ + GIV(secondaryErrorCode) = ((sqLong) exceptionCode); + GIV(exceptionPC) = pc; + GIV(primFailCode) = PrimErrFFIException; + if ((ffiExceptionResponse > 0) + || ((/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (methodUsesPrimitiveErrorCode(GIV(newMethod))))) { + ownVM(GIV(ffiCalloutVMHandle)); + + /* begin activateFailingPrimitiveMethod */ + assert(GIV(primFailCode) != 0); + assert(addressCouldBeObj(GIV(newMethod))); + assert(isCompiledMethod(GIV(newMethod))); + assert((primitiveIndexOf(GIV(newMethod))) != 0); + + /* begin justActivateNewMethod: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + cogMethod = null; + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + methodHeader = (cogMethod->methodHeader); + cogMethod = null; + } + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeader)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(addressCouldBeOop(rcvr)); + assert(!(isOopForwarded(rcvr))); + if (cogMethod) { + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + } + GIV(instructionPointer) = ceReturnToInterpreterPC(); + } + cogMethod = null; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + initialIP = ((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize; + if (cogMethod) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)cogMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + GIV(instructionPointer) = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + GIV(instructionPointer) = initialIP - 1; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + if (!cogMethod) { + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + } + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeader); + } + } + ceInvokeInterpret(); + + /* NOTREACHED */ + } +} + + /* StackInterpreter>>#primitiveFloatEqual:toArg: */ +static NoDbgRegParms int +primitiveFloatEqualtoArg(sqInt rcvrOop, sqInt argOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + double rcvr; + double result; + sqInt tagBits; + double value; + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l2: + if (rcvr == arg) { + if ((((argOop) & 7) == 1)) { + return (((sqInt)rcvr)) == ((argOop >> 3)); + } + + /* Resolve case of ambiguity so as to have comparison of exact values */ + if ((((rcvrOop) & 7) == 1)) { + /* Same when used from bytecodePrim... + note that rcvr and arg cannot be both integer (case is already handled) */ + return ((rcvrOop >> 3)) == (((sqInt)arg)); + } + } + return rcvr == arg; +} + + /* StackInterpreter>>#primitiveFloatGreaterOrEqual:toArg: */ +static NoDbgRegParms int +primitiveFloatGreaterOrEqualtoArg(sqInt rcvrOop, sqInt argOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + double rcvr; + double result; + sqInt tagBits; + double value; + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l2: + if (rcvr == arg) { + if ((((argOop) & 7) == 1)) { + return (((sqInt)rcvr)) >= ((argOop >> 3)); + } + + /* Resolve case of ambiguity so as to have comparison of exact values */ + if ((((rcvrOop) & 7) == 1)) { + /* Same when used from bytecodePrim... + note that rcvr and arg cannot be both integer (case is already handled) */ + return ((rcvrOop >> 3)) >= (((sqInt)arg)); + } + } + return rcvr >= arg; +} + + /* StackInterpreter>>#primitiveFloatGreater:thanArg: */ +static NoDbgRegParms int +primitiveFloatGreaterthanArg(sqInt rcvrOop, sqInt argOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + double rcvr; + double result; + sqInt tagBits; + double value; + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l2: + if (rcvr == arg) { + if ((((argOop) & 7) == 1)) { + return (((sqInt)rcvr)) > ((argOop >> 3)); + } + + /* Resolve case of ambiguity so as to have comparison of exact values */ + if ((((rcvrOop) & 7) == 1)) { + /* Same when used from bytecodePrim... + note that rcvr and arg cannot be both integer (case is already handled) */ + return ((rcvrOop >> 3)) > (((sqInt)arg)); + } + } + return rcvr > arg; +} + + /* StackInterpreter>>#primitiveFloatLessOrEqual:toArg: */ +static NoDbgRegParms int +primitiveFloatLessOrEqualtoArg(sqInt rcvrOop, sqInt argOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + double rcvr; + double result; + sqInt tagBits; + double value; + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l2: + if (rcvr == arg) { + if ((((argOop) & 7) == 1)) { + return (((sqInt)rcvr)) <= ((argOop >> 3)); + } + + /* Resolve case of ambiguity so as to have comparison of exact values */ + if ((((rcvrOop) & 7) == 1)) { + /* Same when used from bytecodePrim... + note that rcvr and arg cannot be both integer (case is already handled) */ + return ((rcvrOop >> 3)) <= (((sqInt)arg)); + } + } + return rcvr <= arg; +} + + /* StackInterpreter>>#primitiveFloatLess:thanArg: */ +static NoDbgRegParms int +primitiveFloatLessthanArg(sqInt rcvrOop, sqInt argOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + double rcvr; + double result; + sqInt tagBits; + double value; + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l2: + if (rcvr == arg) { + if ((((argOop) & 7) == 1)) { + return (((sqInt)rcvr)) < ((argOop >> 3)); + } + + /* Resolve case of ambiguity so as to have comparison of exact values */ + if ((((rcvrOop) & 7) == 1)) { + /* Same when used from bytecodePrim... + note that rcvr and arg cannot be both integer (case is already handled) */ + return ((rcvrOop >> 3)) < (((sqInt)arg)); + } + } + return rcvr < arg; +} + + +/* Note: With the Squeak V3 format we now have 10 bits of primitive index, + but they are + in two places for temporary backward compatibility. The time to unpack is + negligible, since the derived primitive function pointer is stored in the + method cache. With the + Spur format we assume a 3-byte CallPrimitive with a little-endian 16-bit + primitive index. */ + + /* StackInterpreter>>#primitiveIndexOfMethod:header: */ +sqInt +primitiveIndexOfMethodheader(sqInt theMethod, sqInt methodHeader) +{ + usqInt firstBytecode; + + return (((methodHeader & AlternateHeaderHasPrimFlag) != 0) + ? ((firstBytecode = (theMethod + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize), + (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8)))) + : 0); +} + + /* StackInterpreter>>#primitiveIndexOf: */ +sqInt +primitiveIndexOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin primitiveIndexOfMethod:header: */ + return (((methodHeader & AlternateHeaderHasPrimFlag) != 0) + ? ((firstBytecode = (methodPointer + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize), + (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8)))) + : 0); +} + + +/* Return the method an external primitive was defined in */ + + /* StackInterpreter>>#primitiveMethod */ +usqInt +primitiveMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(newMethod); +} + + /* StackInterpreter>>#printActivationNameForSelector:startClass: */ +static NoDbgRegParms void +printActivationNameForSelectorstartClass(sqInt aSelector, sqInt startClass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classDict; + usqInt classDictSize; + sqInt currClass; + sqInt i; + sqInt methClass; + usqInt numSlots; + sqInt objOop; + + if (addressCouldBeObj(startClass)) { + /* begin findClassForSelector:lookupClass:do: */ + currClass = startClass; + do { + classDict = longAt((void *)((currClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + + /* begin numSlotsOf: */ + assert((classIndexOf(classDict)) > (isForwardedObjectClassIndexPun())); + classDictSize = (((numSlots = byteAt((void *)(classDict + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classDict - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + i = SelectorStart; + while (i < classDictSize) { + if (aSelector == (longAt((void *)((classDict + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + methClass = currClass; + goto l1; + } + i += 1; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((currClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currClass, objOop); + } + currClass = objOop; + } while(!(currClass == GIV(nilObj))); + methClass = null; + /* end findClassForSelector:lookupClass:do: */ +l1: + if ((!methClass) + || (startClass == methClass)) { + printNameOfClasscount(methClass, 5); + printChar('>'); + if (!methClass) { + printStringOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord()))))))); + print(" "); + } + } + else { + printNameOfClasscount(startClass, 5); + printChar('('); + printNameOfClasscount(methClass, 5); + printChar(')'); + printChar('>'); + } + } + else { + print("INVALID CLASS"); + } + if (/* addressCouldBeOop: */ + (((aSelector & (tagMask())) != 0)) + || (addressCouldBeObj(aSelector))) { + if (/* isBytes: */ + ((!(aSelector & (tagMask())))) + && (((byteAt((void *)(aSelector + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) { + printStringOf(aSelector); + } + else { + /* begin printOopShort: */ + printOopShortInner(aSelector); + fflush(GIV(transcript)); + } + } + else { + print("INVALID SELECTOR"); + } +} + + /* StackInterpreter>>#printActivationNameFor:receiver:isBlock:firstTemporary: */ +static NoDbgRegParms void +printActivationNameForreceiverisBlockfirstTemporary(sqInt aMethod, sqInt anObject, sqInt isBlock, sqInt maybeMessage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classObj; + sqInt homeMethod; + sqInt methClass; + sqInt methodSel; + sqInt tagBits; + + if (isBlock) { + print("[] in "); + homeMethod = homeMethodOf(aMethod); + } + else { + homeMethod = aMethod; + } + methClass = findClassOfMethodforReceiver(homeMethod, anObject); + methodSel = findSelectorOfMethod(homeMethod); + if ((/* addressCouldBeOop: */ + (((anObject & (tagMask())) != 0)) + || (addressCouldBeObj(anObject))) + && ((!(/* isOopForwarded: */ + ((!(anObject & (tagMask())))) + && ((!((longAt((void *)(anObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) + && (addressCouldBeClassObj((classObj = /* fetchClassOf: */ + ((tagBits = anObject & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(anObject))))))) { + printNameOfClasscount(classObj, 5); + if (!((classObj == methClass) + || ((!methClass) + || (methClass == GIV(nilObj))))) { + printChar('('); + printNameOfClasscount(methClass, 5); + printChar(')'); + } + } + else { + print("INVALID RECEIVER"); + } + printChar('>'); + if (/* addressCouldBeOop: */ + (((methodSel & (tagMask())) != 0)) + || (addressCouldBeObj(methodSel))) { + if (methodSel == GIV(nilObj)) { + print("(nil)"); + } + else { + if (((methodSel & (tagMask())) != 0)) { + printChar('('); + printHex(methodSel); + printChar(')'); + } + else { + printStringOf(methodSel); + } + } + } + else { + print("INVALID SELECTOR = "); + printHex(methodSel); + } + if ((methodSel == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))))) + && ((addressCouldBeObj(maybeMessage)) + && ((fetchClassOfNonImm(maybeMessage)) == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassMessage) << (shiftForWord())))))))))) { + /* print arg message selector */ + methodSel = longAt((void *)((maybeMessage + BaseHeaderSize) + ((((usqInt)(MessageSelectorIndex) << (shiftForWord())))))); + + /* begin space */ + printChar(' '); + printStringOf(methodSel); + } +} + + +/* Print all the stacks of all running processes, including those that are + currently suspended. + */ +/* useful for VM debugging */ + + /* StackInterpreter>>#printAllStacks */ +void +printAllStacks(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classPointer; + sqInt followingWord; + usqInt followingWordAddress; + sqInt linkedListClass; + sqInt minProcessInstSize; + sqInt myList; + sqInt myListClass; + usqInt numSlots; + sqInt objOop; + sqInt objSqInt; + usqInt p; + sqInt prevObj; + sqInt prevPrevObj; + sqInt pri; + sqInt proc; + sqInt processClass; + sqInt processList; + sqInt schedLists; + sqInt startObject; + sqInt tagBits; + + /* may not be an instance of process. may in exceptional circumstances be nilObject */ + proc = activeProcess(); + printNameOfClasscount(/* fetchClassOf: */ + ((tagBits = proc & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(proc)), 5); + + /* begin space */ + printChar(' '); + printHex(proc); + print(" priority "); + printNum(quickFetchIntegerofObject(PriorityIndex, proc)); + cr(); + if (GIV(framePointer)) { + printCallStack(); + } + else { + printProcessStack(proc); + } + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + schedLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + linkedListClass = null; + + /* then the runnable processes */ + p = (GIV(highestRunnableProcessPriority) + ? GIV(highestRunnableProcessPriority) + : numSlotsOf(schedLists)); + for (pri = (p - 1); pri >= 0; pri += -1) { + processList = longAt((void *)((schedLists + BaseHeaderSize) + ((((usqInt)(pri) << (shiftForWord())))))); + if (!(isEmptyList(processList))) { + if (proc == GIV(nilObj)) { + proc = longAt((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + } + cr(); + print("processes at priority "); + printNum(pri + 1); + printProcsOnList(processList); + } + if (!linkedListClass) { + linkedListClass = fetchClassOfNonImm(processList); + } + } + if (!linkedListClass) { + classPointer = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord())))))); + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + linkedListClass = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(linkedListClass)); + if ((!((longAt((void *)(linkedListClass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + linkedListClass = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, linkedListClass); + } + } + if (proc == GIV(nilObj)) { + cr(); + print("Cannot find a runnable process. Cannot therefore determine class Process. Cannot therefore print suspended processes"); + return; + } + cr(); + print("suspended processes"); + + /* Find the root of the Process hierarchy. It is the class, or superclass, + of a process, that has inst size at least large enough to include myList */ + processClass = (!(proc == GIV(nilObj)) + ? /* fetchClassOf: */ + ((tagBits = proc & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(proc)) + : 0); + minProcessInstSize = MyListIndex + 1; + while ((instanceSizeOf(superclassOf(processClass))) >= minProcessInstSize) { + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((processClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, processClass, objOop); + } + processClass = objOop; + } + minProcessInstSize = instanceSizeOf(processClass); + + /* look for all subInstances of process that have a context as a suspendedContext and are on a list other than a LinkedList */ + + /* begin allObjectsDoSafely: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + if (((longAt((void *)(objSqInt))) & (classIndexMask())) > (lastClassIndexPun())) { + if ((((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + && (((numSlotsOf(objSqInt)) >= minProcessInstSize) + && ((isContext(longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))))) + && (isKindOfClass(objSqInt, processClass))))) { + /* Is the process waiting on some delaying list? This will be a subclass of LinkedList. + If so, assume it is blocked on the list. */ + myList = longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord())))))); + if ((myList != GIV(nilObj)) + && ((((myListClass = fetchClassOfNonImm(myList))) != linkedListClass) + && (isKindOfClass(myList, linkedListClass)))) { + printProcessStack(objSqInt); + } + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + +/* Print all the stacks of all running processes, including those that are + currently suspended. + */ +/* useful for VM debugging */ + + /* StackInterpreter>>#printAllStacksOn: */ +void +printAllStacksOn(FILE *aStdioStream) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + FILE *savedTranscript; + + /* begin withRedirectedOutputTo:do: */ + savedTranscript = GIV(transcript); + GIV(transcript) = (aStdioStream + ? aStdioStream + : stdout); + printAllStacks(); + GIV(transcript) = savedTranscript; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printCallStack */ +void +printCallStack(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *fp; + + if ((fp = GIV(framePointer))) { + printCallStackFP(fp); + } + else { + printCallStackOf(fetchPointerofObject(SuspendedContextIndex, activeProcess())); + } +} + + /* StackInterpreter>>#printCallStackFP: */ +static NoDbgRegParms sqInt +printCallStackFP(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt context; + + context = shortReversePrintFrameAndCallers(theFP); + while (!(context == GIV(nilObj))) { + if (((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (!(checkIsStillMarriedContextcurrentFP(context, GIV(framePointer)))) { + shortPrintContext(context); + return null; + } + context = shortReversePrintFrameAndCallers(frameOfMarriedContext(context)); + } + else { + context = printContextCallStackOf(context); + } + } + return 0; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printCallStackOf: */ +sqInt +printCallStackOf(sqInt aContextOrProcessOrFrame) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt context; + + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)aContextOrProcessOrFrame)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)aContextOrProcessOrFrame)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)aContextOrProcessOrFrame)) <= (((usqInt)GIV(pages)))))))) { + return printCallStackFP(((char *)aContextOrProcessOrFrame)); + } + if (aContextOrProcessOrFrame == (activeProcess())) { + return printCallStackOf(((sqInt)GIV(framePointer))); + } + if (couldBeProcess(aContextOrProcessOrFrame)) { + return printCallStackOf(longAt((void *)((aContextOrProcessOrFrame + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))))); + } + context = aContextOrProcessOrFrame; + while (!(context == GIV(nilObj))) { + if (((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (!(checkIsStillMarriedContextcurrentFP(context, GIV(framePointer)))) { + shortPrintContext(context); + return null; + } + context = shortReversePrintFrameAndCallers(frameOfMarriedContext(context)); + } + else { + context = printContextCallStackOf(context); + } + } + return 0; +} + + /* StackInterpreter>>#printCallStackOf:currentFP: */ +static NoDbgRegParms sqInt +printCallStackOfcurrentFP(sqInt aContext, char *currFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + sqInt senderOop; + char *theFP; + StackPage *thePage; + + ctxt = aContext; + while (!(ctxt == GIV(nilObj))) { + if (((((longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (checkIsStillMarriedContextcurrentFP(ctxt, currFP)) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (isFree(thePage)) { + fprintf(GIV(transcript), + "%p is on a free page?!\n", + theFP); + return null; + } + shortPrintFrameAndCallers(theFP); + theFP = (thePage->baseFP); + ctxt = frameCallerContext(theFP); + if ((!((longAt((void *)(ctxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + ctxt = followForwarded(ctxt); + } + } + else { + fprintf(GIV(transcript), + "widowed caller frame %p\n", + theFP); + return null; + } + } + else { + shortPrintContext(ctxt); + ctxt = longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + } + } + return 0; +} + + +/* Print the call stack on a specific output stream. */ +/* useful for VM debugging */ + + /* StackInterpreter>>#printCallStackOn: */ +void +printCallStackOn(FILE *aStdioStream) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + FILE *savedTranscript; + + /* begin withRedirectedOutputTo:do: */ + savedTranscript = GIV(transcript); + GIV(transcript) = (aStdioStream + ? aStdioStream + : stdout); + printCallStack(); + GIV(transcript) = savedTranscript; +} + + +/* Print the call stack of aContext until it links to a frame. */ + + /* StackInterpreter>>#printContextCallStackOf: */ +static NoDbgRegParms sqInt +printContextCallStackOf(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + + ctxt = aContext; + while (!((ctxt == GIV(nilObj)) + || (((((longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)))) { + shortPrintContext(ctxt); + ctxt = longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + } + return ctxt; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printContext: */ +void +printContext(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt ip; + sqInt meth; + sqInt sender; + sqInt sp; + + shortPrintContext(aContext); + sender = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + ip = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + if ((((sender) & 7) == 1)) { + if (checkIsStillMarriedContextcurrentFP(aContext, GIV(framePointer))) { + print("married (assuming framePointer valid)"); + cr(); + } + else { + print("widowed (assuming framePointer valid)"); + cr(); + } + print("sender "); + printNum(sender); + print(" ("); + printHexPtrnp( + (/* begin withoutSmallIntegerTags: */ + assert((((sender) & 7) == 1)), + ((char *)(sender - (smallIntegerTag()))))); + printChar(')'); + cr(); + print("ip "); + printNum(ip); + print(" ("); + printHexPtrnp( + (/* begin withoutSmallIntegerTags: */ + assert((((ip) & 7) == 1)), + ((char *)(ip - (smallIntegerTag()))))); + printChar(')'); + cr(); + } + else { + print("sender "); + shortPrintOop(sender); + print("ip "); + if (ip == GIV(nilObj)) { + shortPrintOop(ip); + } + else { + printNum(ip); + print(" ("); + printNum((ip >> 3)); + + /* begin space */ + printChar(' '); + printHex((ip >> 3)); + printChar(')'); + cr(); + } + } + sp = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + print("sp "); + printNum((sp >> 3)); + print(" ("); + printHex(sp); + printChar(')'); + cr(); + print("method "); + + /* begin printMethodFieldForPrintContext: */ + meth = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + fprintf(GIV(transcript), + "%p: ", + ((void *)meth)); + printOopShortInner(meth); + if (methodHasCogMethod(meth)) { + fprintf(GIV(transcript), + " (%p)", + cogMethodOf(meth)); + } + cr(); + print("closure "); + shortPrintOop(longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord()))))))); + print("receiver "); + shortPrintOop(longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord()))))))); + sp = (sp >> 3); + sp = ((sp < ((lengthOf(aContext)) - ReceiverIndex)) ? sp : ((lengthOf(aContext)) - ReceiverIndex)); + for (i = 1; i <= sp; i += 1) { + print(" "); + printNum(i); + + /* begin space */ + printChar(' '); + shortPrintOop(longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))))); + } +} + + /* StackInterpreter>>#printDecodeMethodHeaderOop: */ +static NoDbgRegParms void +printDecodeMethodHeaderOop(sqInt methodHeaderOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin printOopShort: */ + printOopShortInner(methodHeaderOop); + fflush(GIV(transcript)); + if (((methodHeaderOop & AlternateHeaderHasPrimFlag) != 0)) { + print(" hasPrim"); + } + if (methodHeaderOop & LargeContextBit) { + print(" largeFrame"); + } + if (((methodHeaderOop >> 3)) < 0) { + print(" altSet"); + } + print(" nLits "); + printNum( + (/* begin literalCountOfMethodHeader: */ + assert((((methodHeaderOop) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeaderOop >> 3)) & AlternateHeaderNumLiteralsMask)); + print(" nArgs "); + printNum((((usqInt)(methodHeaderOop)) >> MethodHeaderArgCountShift) & 15); + print(" nTemps "); + printNum((((usqInt)(methodHeaderOop)) >> MethodHeaderTempCountShift) & 0x3F); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printExternalHeadFrame */ +void +printExternalHeadFrame(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + printFrameWithSP(GIV(framePointer), GIV(stackPointer)); +} + + /* StackInterpreter>>#printFrameAndCallers:SP:short: */ +static NoDbgRegParms sqInt +printFrameAndCallersSPshort(char *theFP, char *theSP, sqInt printShort) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!(/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + return null; + } + if (longAt(theFP + FoxSavedFP)) { + printFrameAndCallersSPshort(((char *)(longAt(theFP + FoxSavedFP))), frameCallerSP(theFP), printShort); + } + if (printShort) { + shortPrintFrame(theFP); + return null; + } + cr(); + printFrameWithSP(theFP, theSP); + return 0; +} + + /* StackInterpreter>>#printFrameOop:at: */ +static NoDbgRegParms void +printFrameOopat(char *name, char *address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt it; + + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%12s: %16p\t=" /* pst: */, + name, + ((void *)it)); + printOopShortInner(it); + cr(); +} + + /* StackInterpreter>>#printFrameOop:index:at: */ +static NoDbgRegParms void +printFrameOopindexat(char *name, sqInt idx, char *address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt it; + + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%s%10s%" PRIdSQINT ": %16p\t" /* pst: */, + (idx > 9 + ? "" + : " "), + name, + idx, + ((void *)it)); + printOopShortInner(it); + cr(); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printFramesInPage: */ +void +printFramesInPage(StackPage *thePage) +{ + printFrameAndCallersSPshort((thePage->headFP), (thePage->headSP), 0); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printFramesOnStackPageListInUse */ +void +printFramesOnStackPageListInUse(void) +{ + StackPage *page; + + page = GIV(mostRecentlyUsedPage); + do { + if (!(isFree(page))) { + print("page "); + printHexPtrnp(page); + cr(); + printFramesInPage(page); + cr(); + } + } while(((page = (page->prevPage))) != (GIV(mostRecentlyUsedPage))); +} + + /* StackInterpreter>>#printFrameThing:andFrame:at: */ +static NoDbgRegParms void +printFrameThingandFrameat(char *name, char *theFP, char *address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt it; + + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%12s: %16p" /* pst: */, + name, + ((void *)it)); + if (it) { + if (it == GIV(nilObj)) { + fprintf(GIV(transcript), "=nil"); + } + else { + fprintf(GIV(transcript), + "=%" PRIdSQINT "", + it); + } + } + fprintf(GIV(transcript), + " frame: %p\n", + theFP); +} + + /* StackInterpreter>>#printFrameThing:at:extraString: */ +static NoDbgRegParms void +printFrameThingatextraString(char *name, char *address, char *extraStringOrNil) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt it; + + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%12s: %16p" /* pst: */, + name, + ((void *)it)); + framePrintDescription(it); + if (extraStringOrNil) { + fprintf(GIV(transcript), + "%s", + extraStringOrNil); + } + cr(); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printFrame: */ +sqInt +printFrame(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *aFrame; + char *frameAbove; + char *prevFrame; + char *startFrame; + StackPage *thePage; + char *theSP; + + if (!(/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + if ((addressCouldBeObj(((sqInt)theFP))) + && ((isInMemory(((sqInt)theFP))) + && ((((longAt((void *)(((sqInt)theFP)))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (checkIsStillMarriedContextcurrentFP(((sqInt)theFP), GIV(framePointer)))))) { + return printFrame(frameOfMarriedContext(((sqInt)theFP))); + } + fprintf(GIV(transcript), + "%p is not in the stack zone?!\n", + theFP); + return null; + } + frameAbove = null; + if (theFP == GIV(framePointer)) { + theSP = GIV(stackPointer); + } + else { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (isFree(thePage)) { + fprintf(GIV(transcript), + "%p is on a free page?!\n", + theFP); + return null; + } + if ((thePage != GIV(stackPage)) + && (theFP == ((thePage->headFP)))) { + theSP = (thePage->headSP); + } + else { + startFrame = ((thePage == GIV(stackPage)) + && (((GIV(framePointer) >= ((thePage->realStackLimit))) && (GIV(framePointer) <= ((thePage->baseAddress))))) + ? GIV(framePointer) + : (thePage->headFP)); + + /* begin safeFindFrameAbove:on:startingFrom: */ + prevFrame = ((char *) 0); + assert(!(isFree(thePage))); + if (startFrame == theFP) { + frameAbove = null; + goto l1; + } + aFrame = startFrame; + while (1) { + prevFrame = aFrame; + aFrame = ((char *)(longAt(aFrame + FoxSavedFP))); + if (!(aFrame != 0)) break; + if (theFP == aFrame) { + frameAbove = prevFrame; + goto l1; + } + } + frameAbove = null; + /* end safeFindFrameAbove:on:startingFrom: */ +l1: + theSP = (frameAbove + ? frameCallerSP(frameAbove) + : ((char *) null)); + } + } + if (!theSP) { + fprintf(GIV(transcript), "could not find sp; using bogus value\n"); + theSP = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + } + printFrameWithSP(theFP, theSP); + if (frameAbove) { + /* begin printFrameThing:at: */ + printFrameThingatextraString("frame pc", frameAbove + FoxCallerSavedIP, ((char *)null)); + } + return 0; +} + + +/* Print p in hex, unpadded, in the form 0x1234 (C)/16r1234 (here) */ + + /* StackInterpreter>>#printHexPtrnp: */ +static NoDbgRegParms sqInt +printHexPtrnp(void *p) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return fprintf(GIV(transcript), + "%p", + p); +} + + +/* Print n in hex, in the form ' 0x1234', padded to a width of 10 characters + in 32-bits ('0x' + 8 nibbles) or 18 characters in 64-bits ('0x' + 16 + nibbles). In the simulator use 16r as the prefix, padding to 11 characters + in 32-bits + or 19 characters in 64-bits. */ + + /* StackInterpreter>>#printHex: */ +void +printHex(usqInt n) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fprintf(GIV(transcript), + "%16p" /* pst: */, + ((void *)n)); +} + + +/* Print all methods whose penultimate literal is either selector, + or an object whose first inst var is the method and whose + second is selector (e.g. an AdditionalMethodState). */ +/* useful for VM debugging */ + + /* StackInterpreter>>#printLikelyImplementorsOfSelector: */ +void +printLikelyImplementorsOfSelector(sqInt selector) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt methodClassAssociation; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && ((maybeSelectorOfMethod(objSqInt)) == selector)) { + /* try and print the key of the method class association (the name of the implementing class) */ + methodClassAssociation = methodClassAssociationOf(objSqInt); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)objSqInt)); + + /* begin space */ + printChar(' '); + printOopShortInner(((/* isPointers: */ + ((!(methodClassAssociation & (tagMask())))) + && (((byteAt((void *)(methodClassAssociation + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOf(methodClassAssociation)) >= 2) + ? longAt((void *)((methodClassAssociation + BaseHeaderSize) + (0U << (shiftForWord())))) + : methodClassAssociation)); + cr(); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + /* StackInterpreter>>#printLogEntryAt: */ +static NoDbgRegParms void +printLogEntryAt(sqInt i) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classMethodOrProcess; + sqInt selectorOrExtra; + sqInt source; + sqInt traceType; + sqInt traceTypeAndSource; + + traceTypeAndSource = GIV(traceLog)[i]; + traceType = ((usqInt)(traceTypeAndSource)) >> TraceTypeShift; + source = traceTypeAndSource & (((1U << TraceTypeShift)) - 1); + + /* a.k.a. thingOne */ + classMethodOrProcess = GIV(traceLog)[i + 1]; + + /* a.k.a. thingTwo */ + selectorOrExtra = GIV(traceLog)[i + 2]; + switch (traceType) { + case TraceSend: + /* begin printSendLogEntry:source:selector: */ + print((((source & CSFromMachineCode) != 0) + ? "m " + : "i ")); + printNameOfClasscount(classMethodOrProcess, 5); + print(">>"); + printStringOf(selectorOrExtra); + break; + case TraceStackOverflow: + print("stack overflow"); + break; + case TraceContextSwitch: + fprintf(GIV(transcript), + "context switch from %p to %p", + ((void *)classMethodOrProcess), + ((void *)selectorOrExtra)); + break; + case TraceBlockActivation: + fprintf(GIV(transcript), + " [] in %p", + ((void *)classMethodOrProcess)); + break; + case TraceBlockCreation: + fprintf(GIV(transcript), + "create [] %p", + ((void *)classMethodOrProcess)); + break; + case TraceIncrementalGC: + print("incrementalGC"); + break; + case TraceFullGC: + print("fullGC"); + break; + case TraceCodeCompaction: + print("compactCode"); + break; + case TraceVMCallback: + fprintf(GIV(transcript), + "callback %p", + ((void *)classMethodOrProcess)); + break; + case TraceVMCallbackReturn: + fprintf(GIV(transcript), + "callback return %p type %d", + ((void *)classMethodOrProcess), + ((int) source)); + break; + default: + print("UNKNOWN EVENT"); + } + if ((((traceType >= TraceStackOverflow) && (traceType <= TraceVMCallbackReturn))) + && (source > 1)) { + /* begin space */ + printChar(' '); + print(traceSources[((usqInt)(source)) >> 1]); + } + cr(); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printMethodCache */ +void +printMethodCache(void) +{ + printMethodCacheFor(-1); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printMethodDictionaryOf: */ +void +printMethodDictionaryOf(sqInt behavior) +{ + printMethodDictionary(longAt((void *)((behavior + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord()))))))); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printMethodDictionary: */ +void +printMethodDictionary(sqInt dictionary) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt meth; + sqInt methodArray; + sqInt selector; + sqInt toDoLimit; + + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + toDoLimit = (numSlotsOf(dictionary)) - 1; + for (index = SelectorStart; index <= toDoLimit; index += 1) { + selector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (selector != GIV(nilObj)) { + meth = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + printOopShortInner(selector); + print(" => "); + printOopShortInner(meth); + print(" ("); + printHex(selector); + print(" => "); + printHex(meth); + print(")\n"); + } + } +} + + +/* Details: The count argument is used to avoid a possible infinite recursion + if classOop is a corrupted object. + */ + + /* StackInterpreter>>#printNameOfClass:count: */ +static NoDbgRegParms void +printNameOfClasscount(sqInt classOop, sqInt cnt) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + + if (!GIV(classNameIndex)) { + print("??nil cnidx??"); + return; + } + if ((!classOop) + || ((classOop == 0) + || (cnt <= 0))) { + print("bad class"); + return; + } + numSlots = numSlotsOf(classOop); + if ((numSlots == GIV(metaclassNumSlots)) + && (GIV(metaclassNumSlots) > GIV(thisClassIndex))) { + printNameOfClasscount(longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord())))))), cnt - 1); + print(" class"); + } + else { + if (numSlots <= GIV(classNameIndex)) { + print("bad class"); + } + else { + printStringOf(longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(classNameIndex)) << (shiftForWord()))))))); + } + } +} + + /* StackInterpreter>>#printNum: */ +static NoDbgRegParms void +printNum(sqInt n) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)n)); +} + + /* StackInterpreter>>#printOopShortInner: */ +static NoDbgRegParms void +printOopShortInner(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + sqInt classLookupKey; + sqInt classOop; + double f; + char *name; + sqInt nameLen; + sqInt objOop; + double result; + sqInt tagBits; + sqInt target; + + if (((oop & (tagMask())) != 0)) { + if (((oop & (characterTag())) != 0)) { + fprintf(GIV(transcript), + "$%c(%" PRIxSQINT ")", + ((int) (((((usqInt)oop))) >> (numTagBits()))), + ((((usqInt)oop))) >> (numTagBits())); + return; + } + if ((((oop) & 7) == 1)) { + fprintf(GIV(transcript), + "%" PRIdSQINT "(0x%" PRIxSQINT ")", + (oop >> 3), + (oop >> 3)); + return; + } + if (((oop & (smallFloatTag())) != 0)) { + fprintf(GIV(transcript), + "%g(0x%" PRIxSQINT ")", + noFailFloatValueOf(oop), + oop); + return; + } + fprintf(GIV(transcript), + "unknown immediate %p", + ((void *)oop)); + return; + } + if (!(addressCouldBeObj(oop))) { + print((oop & 7 /* (allocationUnit - 1) */ + ? " is misaligned" + : whereIs(oop))); + return; + } + if (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + print(" is a free chunk"); + return; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + target = followForwarded(oop); + fprintf(GIV(transcript), + " is a forwarder to %p", + ((void *)target)); + return; + } + if (/* isFloatInstance: */ + ((tagBits = oop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(oop))) & (classIndexMask())) == ClassFloatCompactIndex)) { + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(oop)); + if (oop & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&result), (&bits), sizeof(result)); + } + else { + fetchFloatAtinto(oop + BaseHeaderSize, result); + } + f = result; + + /* begin printFloat: */ + fprintf(GIV(transcript), + "%g", + f); + return; + } + classOop = fetchClassOfNonImm(oop); + if (!(addressCouldBeObj(classOop))) { + print("a ??"); + return; + } + if ((numSlotsOf(classOop)) == GIV(metaclassNumSlots)) { + printNameOfClasscount(oop, 5); + return; + } + if (oop == GIV(nilObj)) { + print("nil"); + return; + } + if (oop == GIV(trueObj)) { + print("true"); + return; + } + if (oop == GIV(falseObj)) { + print("false"); + return; + } + nameLen = lengthOfNameOfClass(classOop); + if (!nameLen) { + print("a ??"); + return; + } + name = nameOfClass(classOop); + if ((/* isBytes: */ + ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && (nameLen >= 6)) { + if (!(strncmp((name + nameLen) - 6, "String", 6))) { + printChar('\''); + printStringOf(oop); + printChar('\''); + return; + } + + /* strncmp is weird */ + if (!(strncmp((name + nameLen) - 6, "Symbol", 6))) { + printChar('#'); + printStringOf(oop); + return; + } + } + if ((nameLen == 9) + && ((strncmp(name, "Character", 9)) == 0)) { + printChar('$'); + printChar(((longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord()))))) >> 3)); + return; + } + fprintf(GIV(transcript), + "a(n) %.*s", + ((int) nameLen), + name); + + /* Try to spot association-like things; they're all subclasses of LookupKey */ + if ((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + && (((instanceSizeOf(classOop)) == (ValueIndex + 1)) + && (isBytes(longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(KeyIndex) << (shiftForWord())))))))))) { + classLookupKey = fetchClassOfNonImm(splObj(SchedulerAssociation)); + while (1) { + if (classLookupKey == GIV(nilObj)) { + return; + } + if ((instanceSizeOf(classLookupKey)) == (KeyIndex + 1)) break; + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classLookupKey + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classLookupKey, objOop); + } + classLookupKey = objOop; + } + if (includesBehaviorThatOf(classOop, classLookupKey)) { + /* begin space */ + printChar(' '); + printOopShortInner(longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(KeyIndex) << (shiftForWord()))))))); + print(" -> "); + fprintf(GIV(transcript), + "%p", + ((void *)(longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))))))); + } + } +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printOop: */ +void +printOop(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt byte; + sqInt bytecodesPerLine; + char *className; + sqInt cls; + sqInt column; + sqInt fmt; + sqInt fmtSqInt; + sqInt index; + sqInt lastIndex; + sqInt length; + usqInt numSlots; + usqInt numSlotsUsqInt; + usqInt startIP; + sqInt tagBits; + + length = 0; + if (((oop & (tagMask())) != 0)) { + printImmediateObjecton(oop, GIV(transcript)); + return; + } + if (!(addressCouldBeObj(oop))) { + printCantBeObjecton(oop, GIV(transcript)); + return; + } + if (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + printFreeObjecton(oop, GIV(transcript)); + return; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + printForwarderon(oop, GIV(transcript)); + return; + } + if (!((cls = fetchClassOfNonImm(oop)))) { + fprintf(GIV(transcript), + "%p has a nil class!!\n", + ((void *)oop)); + return; + } + className = nameOfClasslengthInto(cls, (&length)); + fprintf(GIV(transcript), + "%p: a(n) %.*s", + ((void *)oop), + ((int) length), + className); + if (/* isFloatInstance: */ + ((tagBits = oop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(oop))) & (classIndexMask())) == ClassFloatCompactIndex)) { + fprintf(GIV(transcript), + "\n%g\n", + noFailFloatValueOf(oop)); + return; + } + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt > 5 /* lastPointerFormat */) { + fprintf(GIV(transcript), + " nbytes %" PRIdSQINT "", + numBytesOf(oop)); + } + cr(); + if (/* isPureBitsFormat: */ + (fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat()))) { + /* This will answer false if splObj: ClassAlien is nilObject */ + if (isKindOfClass(oop, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord())))))))) { + fprintf(GIV(transcript), + " datasize %" PRIdSQINT " %s @ %p\n", + longAt((void *)(oop + BaseHeaderSize)), + ((longAt((void *)(oop + BaseHeaderSize))) < 0 + ? "indirect" + : (longAt((void *)(oop + BaseHeaderSize)) + ? "direct" + : "pointer")), + startOfAlienData(oop)); + return; + } + if (isKindOfClass(oop, superclassOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord()))))))))) { + printStringDataOfon(oop, GIV(transcript)); + return; + } + printNonPointerDataOfon(oop, GIV(transcript)); + return; + } + startIP = (fmt >= (firstCompiledMethodFormat()) + ? (((literalCountOf(oop)) + LiteralStart) * BytesPerOop) / BytesPerWord + : numSlotsOf(oop)); + lastIndex = ((0x100 < startIP) ? 0x100 : startIP); + if (lastIndex > 0) { + for (index = 1; index <= lastIndex; index += 1) { + printHex(longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + + /* begin space */ + printChar(' '); + if (!(index % 5 /* elementsPerPrintOopLine */)) { + cr(); + } + } + if (lastIndex % 5 /* elementsPerPrintOopLine */) { + cr(); + } + } + if (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + startIP = (((literalCountOf(oop)) + LiteralStart) * BytesPerOop) + 1; + + /* begin lengthOf: */ + fmtSqInt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmtSqInt <= (ephemeronFormat())) { + lastIndex = numSlots; + goto l1; + } + if (fmtSqInt >= (firstByteFormat())) { + lastIndex = ((numSlots << (shiftForWord()))) - (fmtSqInt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmtSqInt >= (firstShortFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 1))) - (fmtSqInt & 3); + goto l1; + } + if (fmtSqInt >= (firstLongFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 2))) - (fmtSqInt & 1); + goto l1; + } + if (fmtSqInt == (sixtyFourBitIndexableFormat())) { + lastIndex = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + lastIndex = 0; + /* end lengthOf: */ +l1: + if ((lastIndex - startIP) > 0x100) { + lastIndex = startIP + 0x100; + } + bytecodesPerLine = 8; + column = 1; + for (index = startIP; index <= lastIndex; index += 1) { + if (column == 1) { + fprintf(GIV(transcript), + "%10p", + ((void *)(((oop + BaseHeaderSize) + index) - 1))); + } + byte = byteAt((void *)((oop + BaseHeaderSize) + (index - 1))); + fprintf(GIV(transcript), + " %02x/%-+3d", + ((int) byte), + ((int) byte)); + column += 1; + if (column > bytecodesPerLine) { + column = 1; + cr(); + } + } + if ((lengthOf(oop)) > lastIndex) { + print("..."); + } + if (!(column == 1)) { + cr(); + } + } + else { + if (startIP > 64) { + print("..."); + cr(); + } + } +} + + +/* print the entry and answer if it takes a parameter (as the following + entry) + */ + + /* StackInterpreter>>#printPrimLogEntryAt:hasParameter: */ +static NoDbgRegParms sqInt +printPrimLogEntryAthasParameter(sqInt i, sqInt hasParameter) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *className; + sqInt entryOop; + sqInt length; + sqInt methodClass; + sqInt methodSel; + sqInt value; + + length = 0; + entryOop = GIV(primTraceLog)[i]; + if (hasParameter) { + if (addressCouldBeObj(entryOop)) { + className = nameOfClasslengthInto(entryOop, (&length)); + } + else { + className = "bad class"; + length = 9; + } + fprintf(GIV(transcript), + "%.*s\n", + ((int) length), + className); + return 0; + } + if (((entryOop & (tagMask())) != 0)) { + value = (entryOop >> 3); + if (value == TraceIncrementalGC) { + fprintf(GIV(transcript), "**IncrementalGC**\n"); + } + else { + if (value == TraceFullGC) { + fprintf(GIV(transcript), "**FullGC**\n"); + } + else { + if (value == TraceCodeCompaction) { + fprintf(GIV(transcript), "**CompactCode**\n"); + } + else { + if (value == TraceStackOverflow) { + fprintf(GIV(transcript), "**StackOverflow**\n"); + } + else { + if (value == TracePrimitiveFailure) { + fprintf(GIV(transcript), "**PrimitiveFailure** "); + return 1; + } + else { + if (value == TracePrimitiveRetry) { + fprintf(GIV(transcript), "**PrimitiveRetry**\n"); + } + else { + fprintf(GIV(transcript), "???\n"); + } + } + } + } + } + } + return 0; + } + if (addressCouldBeObj(entryOop)) { + if (((byteAt((void *)(entryOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + className = "???"; + length = 3; + methodClass = safeMethodClassOf(entryOop); + if (methodClass != GIV(nilObj)) { + className = nameOfClasslengthInto(methodClass, (&length)); + } + methodSel = findSelectorOfMethod(entryOop); + if (methodSel == GIV(nilObj)) { + fprintf(GIV(transcript), + "%.*s>>(selector not found)\n", + ((int) length), + className); + } + else { + fprintf(GIV(transcript), + "%.*s>>#%.*s\n", + ((int) length), + className, + ((int) (numBytesOfBytes(methodSel))), + ((char *) (firstIndexableField(methodSel)))); + } + } + else { + safePrintStringOf(entryOop); + cr(); + } + } + else { + fprintf(GIV(transcript), + "%" PRIdSQINT "!!!\n", + i); + } + return 0; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printProcessStack: */ +void +printProcessStack(sqInt aProcess) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctx; + sqInt tagBits; + + cr(); + printNameOfClasscount(/* fetchClassOf: */ + ((tagBits = aProcess & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(aProcess)), 5); + + /* begin space */ + printChar(' '); + printHex(aProcess); + print(" priority "); + printNum(quickFetchIntegerofObject(PriorityIndex, aProcess)); + cr(); + ctx = followFieldofObject(SuspendedContextIndex, aProcess); + if (!(ctx == GIV(nilObj))) { + printCallStackOfcurrentFP(ctx, GIV(framePointer)); + } +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printProcsOnList: */ +sqInt +printProcsOnList(sqInt procList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firstProc; + sqInt proc; + + proc = (firstProc = followFieldofObject(FirstLinkIndex, procList)); + while (!(proc == GIV(nilObj))) { + printProcessStack(proc); + proc = followFieldofObject(NextLinkIndex, proc); + if (proc == firstProc) { + warning("circular process list!!"); + return null; + } + } + return 0; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackCallStackOf: */ +sqInt +printStackCallStackOf(sqInt aContextOrProcessOrFrame) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt context; + sqInt senderOop; + char *theFP; + + if (addressCouldBeObj(aContextOrProcessOrFrame)) { + if ((((longAt((void *)(aContextOrProcessOrFrame))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (checkIsStillMarriedContextcurrentFP(aContextOrProcessOrFrame, null))) { + return printStackCallStackOf(((sqInt)(frameOfMarriedContext(aContextOrProcessOrFrame)))); + } + if (aContextOrProcessOrFrame == (activeProcess())) { + return printStackCallStackOf(((sqInt)GIV(framePointer))); + } + if (couldBeProcess(aContextOrProcessOrFrame)) { + return printCallStackOf(longAt((void *)((aContextOrProcessOrFrame + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))))); + } + return null; + } + theFP = ((void *)aContextOrProcessOrFrame); + while (1) { + context = shortReversePrintFrameAndCallers(theFP); + if (!((((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && ((/* begin frameOfMarriedContext: */ + (senderOop = longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))), + assert((((senderOop) & 7) == 1)), + (theFP = ((char *)(senderOop - (smallIntegerTag())))), + checkIsStillMarriedContextcurrentFP(context, theFP))))) { + return null; + } + } + return 0; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackPageList */ +void +printStackPageList(void) +{ + StackPage *page; + + page = GIV(mostRecentlyUsedPage); + do { + /* begin printStackPage: */ + printStackPageuseCount(page, -1); + cr(); + } while(((page = (page->prevPage))) != (GIV(mostRecentlyUsedPage))); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackPageListInUse */ +void +printStackPageListInUse(void) +{ + sqInt n; + StackPage *page; + + page = GIV(mostRecentlyUsedPage); + n = 0; + do { + if (!(isFree(page))) { + printStackPageuseCount(page, (n += 1)); + cr(); + } + } while(((page = (page->prevPage))) != (GIV(mostRecentlyUsedPage))); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackPages */ +void +printStackPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + StackPage *page; + + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + page = stackPageAtpages(i, GIV(pages)); + printStackPageuseCount(page, -1); + cr(); + } +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackPagesInUse */ +void +printStackPagesInUse(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt n; + + n = 0; + for (i = 0; i < GIV(numStackPages); i += 1) { + if (!(isFree(stackPageAt(i)))) { + printStackPageuseCount(stackPageAt(i), (n += 1)); + cr(); + } + } +} + + /* StackInterpreter>>#printStackPage:useCount: */ +static NoDbgRegParms void +printStackPageuseCount(StackPage *page, sqInt n) +{ + print("page "); + printHexPtrnp(page); + print(" ("); + printNum(pageIndexFor((page->realStackLimit))); + if (n >= 0) { + print(","); + printNum(n); + } + print(") (trace: "); + printNum((page->trace)); + printChar(')'); + if (isFree(page)) { + print(" (free)"); + } + if (page == (GIV(mostRecentlyUsedPage))) { + print(" (MRU)"); + } + if (((page->prevPage)) == (GIV(mostRecentlyUsedPage))) { + print(" (LRU)"); + } + cr(); + + /* begin tab */ + printChar('\t'); + print("ba: "); + printHexPtrnp((page->baseAddress)); + print(" - sl: "); + printHexPtrnp((page->realStackLimit)); + print(" - sl-so: "); + printHexPtrnp(((page->realStackLimit)) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)); + print(" - la:"); + printHexPtrnp((page->lastAddress)); + if (!(isFree(page))) { + cr(); + + /* begin tab */ + printChar('\t'); + print("baseFP "); + printHexPtrnp((page->baseFP)); + + /* cr; */ + + /* begin tab */ + printChar('\t'); + print("headFP "); + printHexPtrnp((page->headFP)); + + /* cr; */ + + /* begin tab */ + printChar('\t'); + print("headSP "); + printHexPtrnp((page->headSP)); + } + cr(); + + /* begin tab */ + printChar('\t'); + print("prev "); + printHexPtrnp(page->prevPage); + print(" ("); + printNum(pageIndexFor((((page->prevPage))->realStackLimit))); + printChar(')'); + + /* begin tab */ + printChar('\t'); + print("next "); + printHexPtrnp(page->nextPage); + print(" ("); + printNum(pageIndexFor((((page->nextPage))->realStackLimit))); + printChar(')'); + cr(); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackReferencesTo: */ +void +printStackReferencesTo(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt i; + char *theFP; + StackPage *thePage; + char *theSP; + + callerFP = ((char *) 0); + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((thePage->baseFP)) { + theSP = (thePage->headSP); + theFP = (thePage->headFP); + + /* Skip the instruction pointer on top of stack of inactive pages. */ + if (!(thePage == GIV(stackPage))) { + theSP += BytesPerWord; + } + while (1) { + while (theSP <= (/* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver))) { + if (oop == (longAt(theSP))) { + fprintf(GIV(transcript), + "FP:%p @ %p\n", + theFP, + theSP); + } + theSP += BytesPerWord; + } + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) + && (oop == (longAt(theFP + FoxThisContext)))) { + fprintf(GIV(transcript), + "FP:%p CTXT\n", + theFP); + } + if (oop == (longAt(theFP + FoxMethod))) { + fprintf(GIV(transcript), + "FP:%p MTHD\n", + theFP); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + theFP = callerFP; + } + + /* a.k.a. FoxCallerContext */ + theSP = theFP + FoxCallerSavedIP; + while (theSP <= ((thePage->baseAddress))) { + if (oop == (longAt(theSP))) { + fprintf(GIV(transcript), + "FP:%p @ %p\n", + theFP, + theSP); + } + theSP += BytesPerWord; + } + } + } +} + + /* StackInterpreter>>#printStringOf: */ +static NoDbgRegParms void +printStringOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cnt; + sqInt code; + sqInt fmt; + sqInt i; + sqInt len; + sqInt max; + + if (((oop & (tagMask())) != 0)) { + return; + } + if (!(addressCouldBeObj(oop))) { + return; + } + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt < (firstByteFormat())) { + return; + } + cnt = ((((max = 128)) < ((len = lengthOf(oop)))) ? ((max = 128)) : ((len = lengthOf(oop)))); + i = 0; + if ((isinstanceOfcompactClassIndex(oop, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteArray) << (shiftForWord())))))), GIV(classByteArrayCompactIndex))) + || (/* isLargeIntegerInstance: */ + ((!(oop & (tagMask())))) + && ((((usqInt)(((longAt((void *)(oop))) & (classIndexMask())) - ClassLargeNegativeIntegerCompactIndex))) <= 1))) { + while (i < cnt) { + printHex(byteAt((void *)((oop + BaseHeaderSize) + i))); + i += 1; + } + } + else { + while (i < cnt) { + code = byteAt((void *)((oop + BaseHeaderSize) + i)); + switch (code) { + case 10: + print(""); + break; + case 13: + print(""); + break; + default: + printChar(code); + } + i += 1; + } + } + if (len > max) { + print("..."); + } + + /* begin flush */ + fflush(GIV(transcript)); +} + + /* StackInterpreter>>#print: */ +void +print(char *s) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fprintf(GIV(transcript), + "%s", + s); +} + + +/* Answer the list aProcess belongs to when runnable but not the active + process + */ +/* Useful for debugging!! */ + + /* StackInterpreter>>#processListForProcess: */ +sqInt +processListForProcess(sqInt aProcess) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt oop; + sqInt priority; + sqInt processLists; + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + priority = (oop >> 3); + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + processLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + return longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((priority - 1)) << (shiftForWord())))))); +} + + +/* Answer the zero-relative offset of Process' 'osErr inst var, or -1 if + absent. osErr will be set with the + platform's error after any FFI call (e.g. errno on unix or GetLastError, + WSAGetLastError et al on Win32) + which allows collecting the error without the potential for preemption + between call and error fetch, + which could result in fetching an invalid error code. */ + + /* StackInterpreter>>#processOSErrInstVarOffset */ +sqInt +processOSErrInstVarOffset(void) +{ + return (((imageHeaderFlags & 32) != 0) + ? (((imageHeaderFlags & 4) != 0) + ? 5 + : 4) + : -1); +} + + /* StackInterpreter>>#pushBool: */ +void +pushBool(sqInt trueOrFalse) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + + object = /* booleanObjectOf: */ + (trueOrFalse + ? GIV(trueObj) + : GIV(falseObj)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); +} + + +/* The receiver of a message send or the closure of a block activation is + always on the stack above any arguments and the frame itself. See the + diagram in StackInterpreter class>>initializeFrameIndices. */ + + /* StackInterpreter>>#pushedReceiverOrClosureOfFrame: */ +static NoDbgRegParms sqInt +pushedReceiverOrClosureOfFrame(char *theFP) +{ + /* begin frameStackedReceiver:numArgs: */ + return longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))); +} + + /* StackInterpreter>>#pushFloat: */ +void +pushFloat(double f) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + + object = floatObjectOf(f); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); +} + + /* StackInterpreter>>#pushInteger: */ +sqInt +pushInteger(sqInt integerValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),(((usqInt)integerValue << 3) | 1)); + GIV(stackPointer) = sp; + return null; +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#push: */ +void +push(sqInt object) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + longAtput((sp = GIV(stackPointer) - BytesPerWord),object); + GIV(stackPointer) = sp; +} + + +/* Append aLong to aFile in this platform's 'natural' byte order. aLong is + either 32 or 64 bits, + depending on ObjectMemory. (Bytes will be swapped, if necessary, when the + image is read + on a different platform.) Set successFlag to false if the write fails. */ + + /* StackInterpreter>>#putLong:toFile: */ +static NoDbgRegParms void +putLongtoFile(sqInt aLong, sqImageFile aFile) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objectsWritten; + + objectsWritten = sqImageFileWrite((&aLong), sizeof(sqInt), 1, aFile); + + /* begin success: */ + if (!(objectsWritten == 1)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Append the 16-bit aShort to aFile in this platform's 'natural' byte order. + (Bytes will be swapped, if necessary, when the image is read on a + different platform.) Set successFlag to false if the write fails. */ + + /* StackInterpreter>>#putShort:toFile: */ +static NoDbgRegParms void +putShorttoFile(short aShort, sqImageFile aFile) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objectsWritten; + + objectsWritten = sqImageFileWrite((&aShort), sizeof(short), 1, aFile); + + /* begin success: */ + if (!(objectsWritten == 1)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Save the given process on the scheduler process list for its priority, + adding to the back if yieldImplicitly or to the front if not + yieldImplicitly. + */ + + /* StackInterpreter>>#putToSleep:yieldingIf: */ +static NoDbgRegParms void +putToSleepyieldingIf(sqInt aProcess, sqInt yieldImplicitly) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firstLink; + sqInt objOop; + sqInt oop; + sqInt priority; + sqInt processList; + sqInt processLists; + + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + priority = (oop >> 3); + if ((GIV(highestRunnableProcessPriority) != 0) + && (priority > GIV(highestRunnableProcessPriority))) { + GIV(highestRunnableProcessPriority) = priority; + } + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + processLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + processList = longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((priority - 1)) << (shiftForWord())))))); + if (yieldImplicitly) { + addLastLinktoList(aProcess, processList); + } + else { + /* begin addFirstLink:toList: */ + assert(!(isForwarded(processList))); + assert((fetchPointerofObject(NextLinkIndex, aProcess)) == (nilObject())); + firstLink = longAt((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + assert(firstLink != aProcess); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(FirstLinkIndex, processList, aProcess)); + assert(isNonImmediate(processList)); + if (oopisGreaterThanOrEqualTo(processList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(aProcess & (tagMask())))) + && (oopisLessThan(aProcess, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(processList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(processList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))),aProcess); + if (firstLink == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(LastLinkIndex, processList, aProcess)); + assert(isNonImmediate(processList)); + if (oopisGreaterThanOrEqualTo(processList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(aProcess & (tagMask())))) + && (oopisLessThan(aProcess, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(processList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(processList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((processList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord()))))),aProcess); + } + else { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(NextLinkIndex, aProcess, firstLink)); + assert(isNonImmediate(aProcess)); + if (oopisGreaterThanOrEqualTo(aProcess, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(firstLink & (tagMask())))) + && (oopisLessThan(firstLink, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aProcess + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aProcess); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord()))))),firstLink); + } + + /* a.k.a. (self isEmptyList: aList) */ + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(MyListIndex, aProcess, processList)); + assert(isNonImmediate(aProcess)); + if (oopisGreaterThanOrEqualTo(aProcess, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(processList & (tagMask())))) + && (oopisLessThan(processList, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aProcess + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aProcess); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),processList); + } +} + + +/* Append aWord32 to aFile in this platform's 'natural' byte order. aWord32 + is 32 bits, + depending on ObjectMemory. (Bytes will be swapped, if necessary, when the + image is read + on a different platform.) Set successFlag to false if the write fails. */ + + /* StackInterpreter>>#putWord32:toFile: */ +static NoDbgRegParms void +putWord32toFile(int aWord32, sqImageFile aFile) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objectsWritten; + + objectsWritten = sqImageFileWrite((&aWord32), 4, 1, aFile); + + /* begin success: */ + if (!(objectsWritten == 1)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Return the integer value of the field without verifying that it is an + integer value! For use in time-critical places where the integer-ness of + the field can be guaranteed. + */ + + /* StackInterpreter>>#quickFetchInteger:ofObject: */ +static NoDbgRegParms sqInt +quickFetchIntegerofObject(sqInt fieldIndex, sqInt objectPointer) +{ + sqInt oop; + + oop = longAt((void *)((objectPointer + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + return (oop >> 3); +} + + +/* Anwer if images of the given format are readable by this interpreter. + Allows a virtual machine to accept selected older image formats. */ + + /* StackInterpreter>>#readableFormat: */ +sqInt +readableFormat(sqInt imageVersion) +{ + return (((imageVersion | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */; +} + + +/* Assuming the primFailCode is non-zero, check if the method consumes the + error code + and if so, assign it through theSP. Then zero the primFailCode. This is + infrequent code, + so keep it out of the common path. */ + + /* StackInterpreter>>#reapAndResetErrorCodeTo:header: */ +static NoDbgRegParms NeverInline void +reapAndResetErrorCodeToheader(char *theSP, sqInt methodHeader) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt initialPC; + + assert(GIV(primFailCode) != 0); + initialPC = (((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) + 3 /* sizeOfCallPrimitiveBytecode: */; + if ((byteAt((void *)(initialPC))) == (/* longStoreBytecodeForHeader: */ + ((((sqLong) methodHeader)) < 0 + ? AltLongStoreBytecode + : LongStoreBytecode))) { + longAtput(theSP,getErrorObjectFromPrimFailCode()); + } + GIV(primFailCode) = 0; +} + + +/* callbackContext is an activation of + invokeCallback:[stack:registers:jmpbuf:]. Its sender is the VM's state + prior to the callback. Reestablish that state, + and mark calloutContext as dead. */ + + /* StackInterpreter>>#reestablishContextPriorToCallback: */ +sqInt +reestablishContextPriorToCallback(sqInt callbackContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt calloutContext; + sqInt senderOop; + char *theFP; + StackPage *thePage; + sqInt top; + + if (!(isLiveContext(callbackContext))) { + return 0; + } + calloutContext = externalInstVarofContext(SenderIndex, callbackContext); + if (!(isLiveContext(calloutContext))) { + return 0; + } + + /* We're about to leave this stack page; must save the current frame's instructionPointer. */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* Mark callbackContext as dead; the common case is that it is the current frame. + We go the extra mile for the debugger. */ + if ((!((longAt((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))) & (tagMask())))) { + /* begin markContextAsDead: */ + assert(isContext(callbackContext)); + assert((isNonImmediate(callbackContext)) + && (!(isForwarded(callbackContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, callbackContext, GIV(nilObj))); + longAtput((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(callbackContext)) + && (!(isForwarded(callbackContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, callbackContext, GIV(nilObj))); + longAtput((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (GIV(framePointer) == theFP) { + if (longAt(theFP + FoxSavedFP)) { + GIV(instructionPointer) = longAt(GIV(framePointer) + FoxCallerSavedIP); + GIV(stackPointer) = (GIV(framePointer) + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(GIV(framePointer)))->cmNumArgs) + : byteAt((GIV(framePointer) + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + GIV(framePointer) = ((char *)(longAt(GIV(framePointer) + FoxSavedFP))); + return 1; + } + else { + freeStackPage(GIV(stackPage)); + } + } + else { + externalDivorceFrameandContext(theFP, callbackContext); + + /* begin markContextAsDead: */ + assert(isContext(callbackContext)); + assert((isNonImmediate(callbackContext)) + && (!(isForwarded(callbackContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, callbackContext, GIV(nilObj))); + longAtput((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(callbackContext)) + && (!(isForwarded(callbackContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, callbackContext, GIV(nilObj))); + longAtput((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + } + } + + /* Make the calloutContext the active frame. The case where calloutContext + is immediately below callbackContext on the same page is handled above. */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((calloutContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(calloutContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((calloutContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + + /* findSPOf:on: points to the word beneath the instructionPointer, but + there is no instructionPointer on the top frame of the current page. */ + assert(thePage != GIV(stackPage)); + GIV(stackPointer) = (findSPOfon(theFP, thePage)) - BytesPerWord; + GIV(framePointer) = theFP; + assert(GIV(stackPointer) < GIV(framePointer)); + } + else { + thePage = makeBaseFrameFor(calloutContext); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + return 1; +} + + +/* Remove the first process from the given linked list. */ + + /* StackInterpreter>>#removeFirstLinkOfList: */ +static NoDbgRegParms sqInt +removeFirstLinkOfList(sqInt aList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt first; + sqInt last; + sqInt next; + + assert(!((isForwarded(aList)))); + first = longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + last = longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord())))))); + assert(!((isForwarded(first)))); + assert(!((isForwarded(last)))); + if (first == last) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aList)) + && (!(isForwarded(aList)))); + assert(validStorePointerUncheckedArgs(FirstLinkIndex, aList, GIV(nilObj))); + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aList)) + && (!(isForwarded(aList)))); + assert(validStorePointerUncheckedArgs(LastLinkIndex, aList, GIV(nilObj))); + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + next = longAt((void *)((first + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(FirstLinkIndex, aList, next)); + assert(isNonImmediate(aList)); + if (oopisGreaterThanOrEqualTo(aList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(next & (tagMask())))) + && (oopisLessThan(next, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))),next); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(first)) + && (!(isForwarded(first)))); + assert(validStorePointerUncheckedArgs(NextLinkIndex, first, GIV(nilObj))); + longAtput((void *)((first + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord()))))),GIV(nilObj)); + return first; +} + + +/* Attempt to remove a process from a linked list. Answer if the attempt + succeeded. + */ + + /* StackInterpreter>>#removeProcess:fromList: */ +static NoDbgRegParms sqInt +removeProcessfromList(sqInt aProcess, sqInt aList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firstLink; + sqInt lastLink; + sqInt nextLink; + usqInt numSlots; + sqInt tempLink; + + nextLink = 0; + assert(!((isForwarded(aProcess)))); + assert(!((isForwarded(aList)))); + + /* any process on the list could have been becomed, so use a read barrier... */ + + /* begin followObjField:ofObject: */ + firstLink = longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(firstLink)); + if ((!((longAt((void *)(firstLink))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + firstLink = fixFollowedFieldofObjectwithInitialValue(FirstLinkIndex, aList, firstLink); + } + + /* begin followObjField:ofObject: */ + lastLink = longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(lastLink)); + if ((!((longAt((void *)(lastLink))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + lastLink = fixFollowedFieldofObjectwithInitialValue(LastLinkIndex, aList, lastLink); + } + if (aProcess == firstLink) { + /* begin followObjField:ofObject: */ + nextLink = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(nextLink)); + if ((!((longAt((void *)(nextLink))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + nextLink = fixFollowedFieldofObjectwithInitialValue(NextLinkIndex, aProcess, nextLink); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(FirstLinkIndex, aList, nextLink)); + assert(isNonImmediate(aList)); + if (oopisGreaterThanOrEqualTo(aList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(nextLink & (tagMask())))) + && (oopisLessThan(nextLink, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))),nextLink); + if (aProcess == lastLink) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aList)) + && (!(isForwarded(aList)))); + assert(validStorePointerUncheckedArgs(LastLinkIndex, aList, GIV(nilObj))); + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord()))))),GIV(nilObj)); + } + } + else { + tempLink = firstLink; + + /* fail if any link doesn't look like a process... */ + while (1) { + if (!((/* isPointers: */ + ((!(tempLink & (tagMask())))) + && (((byteAt((void *)(tempLink + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(tempLink)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(tempLink + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(tempLink - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > MyListIndex))) { + return 0; + } + + /* begin followObjField:ofObject: */ + nextLink = longAt((void *)((tempLink + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(nextLink)); + if ((!((longAt((void *)(nextLink))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + nextLink = fixFollowedFieldofObjectwithInitialValue(NextLinkIndex, tempLink, nextLink); + } + if (nextLink == aProcess) break; + tempLink = nextLink; + } + nextLink = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(NextLinkIndex, tempLink, nextLink)); + assert(isNonImmediate(tempLink)); + if (oopisGreaterThanOrEqualTo(tempLink, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(nextLink & (tagMask())))) + && (oopisLessThan(nextLink, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(tempLink + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(tempLink); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((tempLink + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord()))))),nextLink); + if (aProcess == lastLink) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(LastLinkIndex, aList, tempLink)); + assert(isNonImmediate(aList)); + if (oopisGreaterThanOrEqualTo(aList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(tempLink & (tagMask())))) + && (oopisLessThan(tempLink, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord()))))),tempLink); + } + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aProcess)) + && (!(isForwarded(aProcess)))); + assert(validStorePointerUncheckedArgs(NextLinkIndex, aProcess, GIV(nilObj))); + longAtput((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aProcess)) + && (!(isForwarded(aProcess)))); + assert(validStorePointerUncheckedArgs(MyListIndex, aProcess, GIV(nilObj))); + longAtput((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),GIV(nilObj)); + return 1; +} + + +/* Make aProcess runnable and if its priority is higher than that of the + current process, preempt the current process. Answer if the current + process was preempted. If the current process was preempted then if + yieldImplicitly add the current process to the back of its run queue, + causing an implicit yiled to other processes on the run queue, otherwise + add the current process to the front of its run queue, hence not yielding. + Blue book behaviour is to yield implicitly but is arguably incorrect. */ + + /* StackInterpreter>>#resume:preemptedYieldingIf:from: */ +static NoDbgRegParms sqInt +resumepreemptedYieldingIffrom(sqInt aProcess, sqInt yieldImplicitly, sqInt sourceCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activePriority; + sqInt activeProc; + sqInt newPriority; + sqInt objOop; + sqInt oop; + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + activePriority = (oop >> 3); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + newPriority = (oop >> 3); + if (newPriority <= activePriority) { + putToSleepyieldingIf(aProcess, 1); + return 0; + } + putToSleepyieldingIf(activeProc, yieldImplicitly); + transferTofrom(aProcess, sourceCode); + return 1; +} + + +/* In Spur two cases of primitive failure are handled specially. A primitive + may fail due to validation + encountering a forwarder. On failure, check the accessorDepth for the + primitive and if non-negative + scan the args to the depth, following any forwarders. Retry the primitive + if any are found. Hence + lazily and transparently following forwarders on primitive failure. + Additionally a primitive might fail + due to an allocation failing. Retry if external primitives have failed + with PrimErrNoMemory after running + first the scavenger and then on a subsequent failure, the global + mark-sweep collector. Hence lazily + and transparently GC on memory exhaustion. */ + + /* StackInterpreter>>#retryPrimitiveOnFailure */ +static sqInt +retryPrimitiveOnFailure(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt accessorDepth; + sqInt aMethodSelectorOrImmediate; + sqInt canRetry; + usqInt firstBytecode; + sqInt followDone; + sqInt gcDone; + sqInt header; + sqInt index; + sqInt methodHeader; + sqInt oop; + sqInt primIndex; + sqInt primitiveIndex; + sqInt referent; + sqInt retried; + sqInt retry; + sqInt scannedStackFrame; + sqInt tagBits; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + assert(saneFunctionPointerForFailureOfPrimIndex(primitiveIndex)); + gcDone = 0; + followDone = (canRetry = (retried = 0)); + while (1) { + retry = 0; + if (GIV(primFailCode) == PrimErrNoMemory) { + if (((gcDone += 1)) == 1) { + canRetry = primitiveIndex == PrimNumberExternalCall; + } + if (canRetry) { + if (gcDone == 1) { + /* begin scavengingGC */ + scavengingGCTenuringIf(TenureByAge); + } + if (gcDone == 2) { + fullGC(); + } + retry = gcDone <= 2; + } + } + else { + if (!followDone) { + followDone = 1; + + /* begin checkForAndFollowForwardedPrimitiveState */ + if ((recordPrimTrace()) + && (!primTracePluginName)) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveFailure << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + aMethodSelectorOrImmediate = /* fetchClassOf: */ + ((tagBits = (longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord))) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)))); + + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = aMethodSelectorOrImmediate; + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + assert(failed()); + retry = 0; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIndex = 0; + } + assert((GIV(argumentCount) == (argumentCountOf(GIV(newMethod)))) + || (isMetaPrimitiveIndex(primIndex))); + + /* First things first; make sure the metadata has been followed before it is accessed to derive accessorDepth... */ + if (((/* isCalloutPrimitiveIndex: */ + (primIndex == PrimNumberExternalCall) + || (primIndex == PrimNumberFFICall)) + || (primIndex == PrimNumberDoExternalCall)) + && (unfollowFirstLiteralOfMaybeCalloutMethodprimitiveIndex(GIV(newMethod), primIndex))) { + retry = 1; + } + + /* If the primitive is one of the meta primitives PrimNumberDoPrimitive or PrimNumberDoExternalCall, then + metaAccessorDepth will have been set to nil at the start of the primitive, and to the accessor depth of the + called primitive (or external call) immediately before dispatch. Hence if primIndex is that of a meta primitive + then if metaAccessorDepth is -2, the accessor depth is that of the meta primitive, and if > -2, then + metaAccessorDepth is the accessor depth of the primitive (or external call). Similarly, if the primitive is + primitiveExternalCall then the accessor depth is that of primitiveExternalCall until primitiveFunctionPointer + is assigned, at which point the accessor depth is taken from the slot in newMethod's first literal. */ + accessorDepth = ((/* isMetaPrimitiveIndex: */ + (primIndex == PrimNumberDoPrimitive) + || (primIndex == PrimNumberDoExternalCall)) + && (GIV(metaAccessorDepth) > -2) + ? GIV(metaAccessorDepth) + : accessorDepthForPrimitiveMethod(GIV(newMethod))); + assert(((accessorDepth >= -1) && (accessorDepth <= 5))); + if (accessorDepth >= 0) { + scannedStackFrame = 0; + for (index = 0; index <= GIV(argumentCount); index += 1) { + oop = longAt((void *)(GIV(stackPointer) + (index * BytesPerWord))); + if ((!(oop & (tagMask())))) { + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + assert(index < GIV(argumentCount)); + retry = 1; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + + /* stackValue:put: */ + longAtput((void *)(GIV(stackPointer) + (index * BytesPerWord)),oop); + if (!scannedStackFrame) { + scannedStackFrame = 1; + + /* Avoid repeated primitive failures by following all state in the current stack frame. */ + followForwardedFrameContentsstackPointer(GIV(framePointer), GIV(stackPointer) + ((GIV(argumentCount) + 1) * BytesPerWord)); + } + } + if ((accessorDepth > 0) + && ((/* hasPointerFields: */ + ((!(oop & (tagMask())))) + && (hasPointerFieldsNonImm(oop))) + && (followForwardedObjectFieldstoDepth(oop, accessorDepth)))) { + retry = 1; + } + } + } + } + if (retry + && (recordPrimTrace())) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveRetry << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + } + } + if (!(retry)) break; + assert(GIV(primFailCode) != 0); + retried = 1; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + dispatchFunctionPointer(primitiveFunctionPointer); + } + return retried; +} + + +/* callbackMethodContext is an activation of + invokeCallback:[stack:registers:jmpbuf:]. Its sender is the VM's state + prior to the callback. Reestablish that state (via longjmp), + and mark callbackMethodContext as dead. */ + + /* StackInterpreter>>#returnAs:ThroughCallback:Context: */ +sqInt +returnAsThroughCallbackContext(sqInt returnTypeOop, VMCallbackContext *vmCallbackContext, sqInt callbackMethodContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + sqInt calloutMethodContext; + sqInt cFramePointer; + sqInt cStackPointer; + sqInt header; + sqInt methodHeader; + sqInt senderOop; + char *theFP; + StackPage *thePage; + sqInt thunkp; + sqInt top; + + assert(GIV(primFailCode) == 0); + assert((((returnTypeOop) & 7) == 1)); + assert(!(isImmediate(((sqInt)vmCallbackContext)))); + assert((addressCouldBeObj(callbackMethodContext)) + && (isContext(callbackMethodContext))); + assert(((debugCallbackPath = 0)) == 0); + if (!(((((returnTypeOop) & 7) == 1)) + && (isLiveContext(callbackMethodContext)))) { + assert(((debugCallbackPath = 1)) == 1); + return 0; + } + calloutMethodContext = externalInstVarofContext(SenderIndex, callbackMethodContext); + if (!(isLiveContext(calloutMethodContext))) { + assert(((debugCallbackPath = 2)) == 2); + return 0; + } + assert(((debugCallbackReturns += 1)) > 0); + thunkp = ((sqInt) ((vmCallbackContext->thunkp))); + + /* begin recordCallbackReturn:thunkp: */ + /* begin recordTrace:source:thing:extra: */ + if (TraceLog) { + GIV(traceLog)[GIV(traceLogIndex)] = ((((((usqInt)(TraceVMCallbackReturn) << TraceTypeShift)))) + ((returnTypeOop >> 3))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = (((sqInt)thunkp)); + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + + /* We're about to leave this stack page; must save the current frame's instructionPointer. */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* Mark callbackMethodContext as dead; the common case is that it is the current frame. + We go the extra mile for the debugger. */ + if ((!((longAt((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))) & (tagMask())))) { + assert(((debugCallbackPath = debugCallbackPath | 4)) > 0); + + /* begin markContextAsDead: */ + assert(isContext(callbackMethodContext)); + assert((isNonImmediate(callbackMethodContext)) + && (!(isForwarded(callbackMethodContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, callbackMethodContext, GIV(nilObj))); + longAtput((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(callbackMethodContext)) + && (!(isForwarded(callbackMethodContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, callbackMethodContext, GIV(nilObj))); + longAtput((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + assert(((debugCallbackPath = debugCallbackPath | 8)) > 0); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + assert((frameReceiver(theFP)) == (splObj(ClassAlien))); + if (GIV(framePointer) == theFP) { + assert(((debugCallbackPath = debugCallbackPath | 16)) > 0); + if (longAt(theFP + FoxSavedFP)) { + assert(((debugCallbackPath = debugCallbackPath | 32)) > 0); + GIV(instructionPointer) = longAt(theFP + FoxCallerSavedIP); + GIV(stackPointer) = (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + GIV(framePointer) = ((char *)(longAt(theFP + FoxSavedFP))); + aMethodObj = /* frameMethodObject: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(GIV(framePointer)))->methodObject) + : longAt(GIV(framePointer) + FoxMethod)); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* begin restoreCStackStateForCallbackContext: */ + cFramePointer = ((sqInt) ((vmCallbackContext->savedCFramePointer))); + cStackPointer = ((sqInt) ((vmCallbackContext->savedCStackPointer))); + + /* begin setCFramePointer:setCStackPointer: */ + assert(!(((!cFramePointer) + || (!cStackPointer)))); + GIV(CStackPointer) = cStackPointer; + GIV(CFramePointer) = cFramePointer; + assertSavedCStackPointersWellAligned(); + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + + /* N.B. siglongjmp is defines as _longjmp on non-win32 platforms. + This matches the use of _setjmp in ia32abicc.c. */ + _longjmp((vmCallbackContext->trampoline), (returnTypeOop >> 3)); + return 1; + } + + /* calloutMethodContext is immediately below on the same page. Make it current. */ + freeStackPage(GIV(stackPage)); + } + else { + assert(((debugCallbackPath = debugCallbackPath | 64)) > 0); + externalDivorceFrameandContext(theFP, callbackMethodContext); + + /* begin markContextAsDead: */ + assert(isContext(callbackMethodContext)); + assert((isNonImmediate(callbackMethodContext)) + && (!(isForwarded(callbackMethodContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, callbackMethodContext, GIV(nilObj))); + longAtput((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(callbackMethodContext)) + && (!(isForwarded(callbackMethodContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, callbackMethodContext, GIV(nilObj))); + longAtput((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + } + } + + /* Make the calloutMethodContext the active frame. The case where calloutMethodContext + is immediately below callbackMethodContext on the same page is handled above. */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((calloutMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(calloutMethodContext)))) { + assert(((debugCallbackPath = debugCallbackPath | 128)) > 0); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((calloutMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + + /* findSPOf:on: points to the word beneath the instructionPointer, but + there is no instructionPointer on the top frame of the current page. */ + assert(thePage != GIV(stackPage)); + GIV(stackPointer) = (((thePage->headFP)) == theFP + ? (thePage->headSP) + : (findSPOfon(theFP, thePage)) - BytesPerWord); + GIV(framePointer) = theFP; + assert(GIV(stackPointer) < GIV(framePointer)); + } + else { + assert(((debugCallbackPath = debugCallbackPath | 0x100)) > 0); + thePage = makeBaseFrameFor(calloutMethodContext); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + aMethodObj = longAt((void *)((calloutMethodContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin restoreCStackStateForCallbackContext: */ + cFramePointer = ((sqInt) ((vmCallbackContext->savedCFramePointer))); + cStackPointer = ((sqInt) ((vmCallbackContext->savedCStackPointer))); + + /* begin setCFramePointer:setCStackPointer: */ + assert(!(((!cFramePointer) + || (!cStackPointer)))); + GIV(CStackPointer) = cStackPointer; + GIV(CFramePointer) = cFramePointer; + assertSavedCStackPointersWellAligned(); + + /* N.B. siglongjmp is defined as _longjmp on non-win32 platforms. + This matches the use of _setjmp in ia32abicc.c. */ + _longjmp((vmCallbackContext->trampoline), (returnTypeOop >> 3)); + + /* NOTREACHED */ + return 1; +} + + +/* Reverse the given range of Display pixels, rounded to whole word boundary. + Used to give feedback during VM activities such as garbage collection when + debugging. It is assumed that the given word range falls entirely within + the first line of the Display. */ + + /* StackInterpreter>>#reverseDisplayFrom:to: */ +static NoDbgRegParms void +reverseDisplayFromto(sqInt startIndex, sqInt endIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt primFailCodeValue; + sqInt ptr; + unsigned int reversed; + sqInt toDoLimit; + sqInt wordEndIndex; + sqInt wordStartIndex; + + if (!(postGCUpdateDisplayBits())) { + return; + } + if ((displayBits == 0) + || (((((((sqInt)displayBits)) & (tagMask())) != 0)) + || (displayDepth <= 0))) { + return; + } + wordStartIndex = ((((startIndex < 0) ? 0 : startIndex)) * displayDepth) / 32; + wordEndIndex = ((((endIndex < displayWidth) ? endIndex : displayWidth)) * displayDepth) / 32; + toDoLimit = (((sqInt)displayBits)) + (wordEndIndex * 4); + for (ptr = ((((sqInt)displayBits)) + (wordStartIndex * 4)); ptr <= toDoLimit; ptr += 4) { + reversed = (long32At((void *)(ptr))) ^ 0xFFFFFFFFU; + long32Atput((void *)(ptr),reversed); + } + primFailCodeValue = GIV(primFailCode); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + updateDisplayLeftTopRightBottom(0, 0, displayWidth, 1); + ioForceDisplayUpdate(); + GIV(primFailCode) = primFailCodeValue; +} + + +/* Rewrite an existing entry in the method cache with a new primitive + function address. + Used by primitiveExternalCall to make direct calls to found external + prims, or quickly + fail not found external prims. */ + + /* StackInterpreter>>#rewriteMethodCacheEntryForExternalPrimitiveToFunction: */ +static NoDbgRegParms void +rewriteMethodCacheEntryForExternalPrimitiveToFunction(void (*localPrimAddress)()) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((GIV(methodCache)[GIV(lastMethodCacheProbeWrite) + MethodCacheMethod]) == GIV(newMethod)) { + GIV(methodCache)[GIV(lastMethodCacheProbeWrite) + MethodCachePrimFunction] = (((sqIntptr_t) localPrimAddress)); + } +} + + +/* Safe version of methodClassOf: that deals with malformed compiled methods, + etc, and does not fixup forwarding pointers.. */ + + /* StackInterpreter>>#safeMethodClassOf: */ +static NoDbgRegParms sqInt +safeMethodClassOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt literal; + sqInt maybeClass; + usqInt numSlots; + sqInt offset; + + offset = (literalCountOf(methodPointer)) - 1; + + /* begin literal:ofMethod: */ + literal = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = followForwarded(literal); + } + if (!((/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(literal)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(literal + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(literal - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > ValueIndex))) { + return GIV(nilObj); + } + maybeClass = longAt((void *)((literal + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(maybeClass & (tagMask())))) + && ((!((longAt((void *)(maybeClass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + maybeClass = followForwarded(maybeClass); + } + return maybeClass; +} + + +/* This is an assert function used to ensure consistency between the + primitiveFunctionPointer and the primitive index when a primitive fails in + Spur. Since Spur automagically retries + failing primitives we want to know that the primitiveFunctionPointer is + actually valid. This isn't + always possible for the `indirect'' primitives (calling a plugin + primitive, calling the ffi, using the + receiver:tryPrimitive:withArgs: primitive evaluator in the simulator. We + do the best we can. */ + + /* StackInterpreter>>#saneFunctionPointerForFailureOfPrimIndex: */ +static NoDbgRegParms sqInt +saneFunctionPointerForFailureOfPrimIndex(sqInt primIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*basePrimitive)(void); + + /* begin functionPointerFor:inClass: */ + basePrimitive = ((((usqInt)primIndex)) > MaxPrimitiveIndex + ? ((void (*)(void)) 0) + : primitiveTable[primIndex]); + return (primitiveFunctionPointer == basePrimitive) + || (((/* isCalloutPrimitiveIndex: */ + (primIndex == PrimNumberExternalCall) + || (primIndex == PrimNumberFFICall)) + && (!((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex))) + || ((/* isMetaPrimitiveIndex: */ + (primIndex == PrimNumberDoPrimitive) + || (primIndex == PrimNumberDoExternalCall)) + && (GIV(metaAccessorDepth) > -2))); +} + + +/* Useful for debugging!! */ + + /* StackInterpreter>>#schedulerPointer */ +sqInt +schedulerPointer(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin fetchPointer:ofObject: */ + return longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#setBreakMNUSelector: */ +void +setBreakMNUSelector(char *aString) +{ + if ((breakSelector = aString)) { + breakSelectorLength = -(strlen(aString)); + } + else { + breakSelectorLength = MinSmallInteger; + } +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#setBreakSelector: */ +void +setBreakSelector(char *aString) +{ + if ((breakSelector = aString)) { + breakSelectorLength = strlen(aString); + } + else { + breakSelectorLength = MinSmallInteger; + } +} + + +/* If bit 0 of the imageHeaderFlags is set then the system should use the + full screen. + */ + + /* StackInterpreter>>#setFullScreenFlag: */ +void +setFullScreenFlag(sqInt aBoolean) +{ + imageHeaderFlags = (aBoolean + ? imageHeaderFlags | 1 + : ((imageHeaderFlags | 1) - 1)); +} + + /* StackInterpreter>>#setInterruptCheckChain: */ +void * +setInterruptCheckChain(void (*aFunction)()) +{ + void (*prevFunction)(); + + prevFunction = interruptCheckChain; + interruptCheckChain = aFunction; + return prevFunction; +} + + /* StackInterpreter>>#setInterruptKeycode: */ +void +setInterruptKeycode(sqInt value) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(interruptKeycode) = value; +} + + +/* This is invoked frrom the platform GUI event subsystem when the user + inputs a a char code matching interruptKeycode. Set the value and then + forceInterruptCheck so as to call the interruptCheckChain function if set. + It's important to set interruptPending *before* calling + forceInterruptCheck so that functions on the interruptCheckChain can check + interruptPending. */ + + /* StackInterpreter>>#setInterruptPending: */ +void +setInterruptPending(sqInt value) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(interruptPending) = value; + forceInterruptCheck(); +} + + /* StackInterpreter>>#setNextWakeupUsecs: */ +void +setNextWakeupUsecs(usqLong value) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(nextWakeupUsecs) = value; +} + + /* StackInterpreter>>#setSavedWindowSize: */ +void +setSavedWindowSize(sqInt value) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(savedWindowSize) = value; +} + + +/* The low space semaphore is about to be signaled. Set the signalLowSpace + flag, and force an interrupt check. Save the currently active process in + the special + objects array so that the low space handler will be able to determine the + process that first triggered a low space condition. The image's low space + handler is expected + to nil out the special objects array slot when it handles the low space + condition. */ + + /* StackInterpreter>>#setSignalLowSpaceFlagAndSaveProcess */ +static void +setSignalLowSpaceFlagAndSaveProcess(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + sqInt lastSavedProcess; + sqInt objOop; + + /* signalLowSpace: */ + GIV(signalLowSpace) = 1; + + /* begin lowSpaceThreshold: */ + GIV(lowSpaceThreshold) = 0; + + /* N.B. The threshold > 0 guard eliminates a warning when + self lowSpaceThreshold: 0 + is inlined into setSignalLowSpaceFlagAndSaveProcess */ + assert(GIV(totalFreeOldSpace) >= GIV(lowSpaceThreshold)); + lastSavedProcess = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ProcessSignalingLowSpace) << (shiftForWord())))))); + if (lastSavedProcess == GIV(nilObj)) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ProcessSignalingLowSpace, GIV(specialObjectsOop), activeProc)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeProc & (tagMask())))) + && (oopisLessThan(activeProc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ProcessSignalingLowSpace) << (shiftForWord()))))),activeProc); + } + forceInterruptCheck(); +} + + /* StackInterpreter>>#setTraceFlagOnContextsFramesPageIfNeeded: */ +static NoDbgRegParms void +setTraceFlagOnContextsFramesPageIfNeeded(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void *pointer; + sqInt senderOop; + StackPage *thePage; + + if (/* isStillMarriedContextDuringGC: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContextDuringGC(aContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + pointer = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(pointer), GIV(pages)); + assert(((((thePage->trace)) >= StackPageUnreached) && (((thePage->trace)) <= StackPageTraced))); + if (!((thePage->trace))) { + (thePage->trace = StackPageReachedButUntraced); + } + } +} + + /* StackInterpreter>>#shortPrintContext: */ +static NoDbgRegParms sqInt +shortPrintContext(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt home; + sqInt rcvr; + sqInt senderOop; + char *theFP; + + if (!(/* isContext: */ + ((!(aContext & (tagMask())))) + && (((longAt((void *)(aContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + printHex(aContext); + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " is not a context"); + cr(); + return null; + } + printHex(aContext); + if (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (checkIsStillMarriedContextcurrentFP(aContext, GIV(framePointer))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " M ("); + } + else { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " I ("); + } + printHex(((usqIntptr_t)theFP)); + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + ") "); + } + else { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " w "); + } + } + else { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " s "); + } + if ((home = findHomeForContext(aContext))) { + rcvr = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord())))))); + printActivationNameForreceiverisBlockfirstTemporary(longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))), rcvr, home != aContext, longAt((void *)((home + BaseHeaderSize) + ((((usqInt)((0 + CtxtTempFrameStart)) << (shiftForWord()))))))); + + /* begin space */ + printChar(' '); + shortPrintOop(rcvr); + } + else { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " BOGUS CONTEXT (can't determine home)"); + cr(); + } + return 0; +} + + /* StackInterpreter>>#shortPrintFrameAndCallers: */ +static NoDbgRegParms sqInt +shortPrintFrameAndCallers(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!(/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + return null; + } + shortPrintFrame(theFP); + if (longAt(theFP + FoxSavedFP)) { + shortPrintFrameAndCallers(((char *)(longAt(theFP + FoxSavedFP)))); + } + return 0; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#shortPrintFramesInPage: */ +void +shortPrintFramesInPage(StackPage *thePage) +{ + printFrameAndCallersSPshort((thePage->headFP), (thePage->headSP), 1); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#shortPrintFramesOnStackPageListInUse */ +void +shortPrintFramesOnStackPageListInUse(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *page; + + page = GIV(mostRecentlyUsedPage); + do { + if (!(isFree(page))) { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "page "); + printHexPtrnp(page); + cr(); + shortPrintFramesInPage(page); + cr(); + } + } while(((page = (page->prevPage))) != (GIV(mostRecentlyUsedPage))); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#shortPrintFrame:AndNCallers: */ +void +shortPrintFrameAndNCallers(char *theFP, sqInt n) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((n != 0) + && (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + shortPrintFrame(theFP); + shortPrintFrameAndNCallers(((char *)(longAt(theFP + FoxSavedFP))), n - 1); + } +} + + /* StackInterpreter>>#shortPrintOop: */ +static NoDbgRegParms void +shortPrintOop(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *className; + sqInt length; + char *where; + + length = 0; + if (((oop & (tagMask())) != 0)) { + printImmediateObjecton(oop, GIV(transcript)); + return; + } + if (!(addressCouldBeObj(oop))) { + /* begin printCantBeObject:on: */ + fprintf(GIV(transcript), + "%p%s\n", + ((void *)oop), + ((where = whereIsMaybeCodeThing(oop)) + ? where + : (oop & 7 /* (allocationUnit - 1) */ + ? " is misaligned" + : whereIs(oop)))); + return; + } + if (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + printFreeObjecton(oop, GIV(transcript)); + return; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + printForwarderon(oop, GIV(transcript)); + return; + } + className = nameOfClasslengthInto(fetchClassOfNonImm(oop), (&length)); + fprintf(GIV(transcript), + "%p: a(n) %.*s\n", + ((void *)oop), + ((int) length), + className); +} + + /* StackInterpreter>>#shortReversePrintFrameAndCallers: */ +static NoDbgRegParms sqInt +shortReversePrintFrameAndCallers(char *aFramePointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt caller; + char *callerFP; + char *theFP; + StackPage *thePage; + + callerFP = ((char *) 0); + if (!aFramePointer) { + return GIV(nilObj); + } + theFP = aFramePointer; + while (1) { + shortPrintFrame(theFP); + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!(callerFP != 0)) break; + theFP = callerFP; + } + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + caller = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(caller)); + assert((caller == (nilObject())) + || (isContext(followMaybeForwarded(caller)))); + if ((!((longAt((void *)(caller))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + caller = followForwarded(caller); + } + return caller; +} + + +/* Repaint the portion of the Smalltalk screen bounded by the affected + rectangle. Used to synchronize the screen after a Bitblt to the Smalltalk + Display object. + */ + + /* StackInterpreter>>#showDisplayBits:Left:Top:Right:Bottom: */ +sqInt +showDisplayBitsLeftTopRightBottom(sqInt aForm, sqInt l, sqInt t, sqInt r, sqInt b) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (deferDisplayUpdates + || (aForm != (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheDisplay) << (shiftForWord()))))))))) { + return null; + } + updateDisplayLeftTopRightBottom(l, t, r, b); + return 0; +} + + +/* Signal all requested semaphores. Answer if a context switch has occurred. */ + + /* StackInterpreter>>#signalExternalSemaphores */ +static sqInt +signalExternalSemaphores(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + sqInt xArray; + + xArray = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ExternalObjectsArray) << (shiftForWord())))))); + return doSignalExternalSemaphores( + (/* begin numSlotsOf: */ + assert((classIndexOf(xArray)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(xArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(xArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))); +} + + +/* Answer the size of the CallPrimitive bytecode that may be used to store a + method's primitive. + */ +/* NewsqueakV4: 249 11111001 iiiiiiii jjjjjjjj Call Primitive #iiiiiiii + + (jjjjjjjj * 256) + */ +/* SistaV1: 248 11111000 iiiiiiii mssjjjjj Call Primitive #iiiiiiii + ( + jjjjj * 256) */ +/* V3+Closures: 139 11101111 iiiiiiii jjjjjjjj Call Primitive #iiiiiiii + + (jjjjjjjj * 256) + */ + + /* StackInterpreter>>#sizeOfCallPrimitiveBytecode: */ +sqInt +sizeOfCallPrimitiveBytecode(sqInt methodHeader) +{ + return 3; +} + + +/* 234 11101010 i i i i i i i i Store Temporary Variable #iiiiiiii */ +/* 129 10000001 jjkkkkkk Store (Receiver Variable, Temporary Location, + Illegal, Literal Variable) [jj] #kkkkkk */ + + /* StackInterpreter>>#sizeOfLongStoreTempBytecode: */ +sqInt +sizeOfLongStoreTempBytecode(sqInt methodHeader) +{ + return 2; +} + + +/* Return the number of indexable fields of the given object. This method is + to be called from an automatically generated C primitive. The argument is + assumed to be a pointer to the first indexable field of a words or bytes + object; the object header starts 4 bytes before that. + */ +/* Note: Only called by translated primitive code. */ + + /* StackInterpreter>>#sizeOfSTArrayFromCPrimitive: */ +sqInt +sizeOfSTArrayFromCPrimitive(void *cPtr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + + oop = (oopForPointer(cPtr)) - BaseHeaderSize; + if (!(/* isWordsOrBytes: */ + ((!(oop & (tagMask())))) + && (isWordsOrBytesNonImm(oop)))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin lengthOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + +/* Invoke a normal (non-quick) primitive. + Called under the assumption that primFunctionPointer has been preloaded. */ + + /* StackInterpreter>>#slowPrimitiveResponse */ +static int +slowPrimitiveResponse(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt nArgs; + char *savedFramePointer; + char *savedStackPointer; + + assert(!(isOopForwarded(stackValue(GIV(argumentCount))))); + assert((GIV(remapBufferCount)) == 0); + nArgs = GIV(argumentCount); + savedStackPointer = GIV(stackPointer); + savedFramePointer = GIV(framePointer); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if (/* recordPrimTraceForMethod: */ + (recordPrimTrace()) + && ((primTracePluginName + ? methodHasPrimitiveInPrimTracePlugin(GIV(newMethod)) + : 1))) { + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = GIV(newMethod); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + dispatchFunctionPointer(primitiveFunctionPointer); + assert(maybeLeakCheckExternalPrimCall(GIV(newMethod))); + + /* begin maybeRetryPrimitiveOnFailure */ + if (GIV(primFailCode)) { + retryPrimitiveOnFailure(); + } + if ((!GIV(primFailCode)) + && ((GIV(framePointer) == savedFramePointer) + && (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))))) { + if (GIV(stackPointer) != (savedStackPointer + (nArgs * BytesPerWord))) { + /* This is necessary but insufficient; the result may still have been written to the stack. + At least we'll know something is wrong. */ + failUnbalancedPrimitive(); + GIV(stackPointer) = savedStackPointer; + } + } + + /* Don't fail if primitive has done something radical, e.g. perform: + If we are profiling, take accurate primitive measures */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + return !GIV(primFailCode); +} + + +/* update state of active context */ + + /* StackInterpreter>>#snapshot: */ +static NoDbgRegParms sqInt +snapshot(sqInt embedded) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt activeProc; + CogMethod *cogMethod; + usqInt i; + usqInt initialIP; + sqInt methodHeader; + usqInt numArgs; + usqInt numTemps; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt rcvr; + sqInt rcvrSqInt; + sqInt savedTenuringThreshold; + void *setMacType; + sqInt stackIndex; + + + /* For now the stack munging below doesn't deal with more than one argument. + It can, and should. */ + rcvr = 0; + if (GIV(argumentCount)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + + /* Need to convert all frames into contexts since the snapshot file only holds objects. */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + activeContext = voidVMStateForSnapshotFlushingExternalPrimitivesIf(1); + + /* update state of active process */ + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, activeProc, activeContext)); + assert(isNonImmediate(activeProc)); + if (oopisGreaterThanOrEqualTo(activeProc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeContext & (tagMask())))) + && (oopisLessThan(activeContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(activeProc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(activeProc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),activeContext); + GIV(tempOop) = activeContext; + + /* begin garbageCollectForSnapshot */ + /* begin flushNewSpace */ + savedTenuringThreshold = GIV(tenureThreshold); + GIV(tenureThreshold) = GIV(oldSpaceStart); + scavengingGCTenuringIf(TenureByAge); + + /* begin setRawTenuringThreshold: */ + GIV(tenureThreshold) = savedTenuringThreshold; + assert((GIV(rememberedSetSize)) == 0); + assert(GIV(pastSpaceStart) == (((GIV(pastSpace)).start))); + assert(GIV(freeStart) == (((GIV(eden)).start))); + + /* begin biasForSnapshot */ + GIV(biasForGC) = 0; + fullGC(); + + /* begin biasForGC */ + GIV(biasForGC) = 1; + unfireQueuedEphemeronsForSnapshot(); + prepareForSnapshot(); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + + /* Nothing moves from here on so it is safe to grab the activeContext again. */ + activeContext = GIV(tempOop); + GIV(tempOop) = 0; + if (!GIV(primFailCode)) { + /* Without contexts or stacks simulate + rcvr := self popStack. ''pop rcvr'' + self push: trueObj. + to arrange that the snapshot resumes with true. N.B. stackIndex is one-relative. */ + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + stackIndex = (oop >> 3); + rcvr = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(((stackIndex + CtxtTempFrameStart) - 1)) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(activeContext)) + && (!(isForwarded(activeContext)))); + assert(validStorePointerUncheckedArgs((stackIndex + CtxtTempFrameStart) - 1, activeContext, GIV(trueObj))); + longAtput((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(((stackIndex + CtxtTempFrameStart) - 1)) << (shiftForWord()))))),GIV(trueObj)); + + /* now attempt to write the snapshot file */ + writeImageFileIO(); + if ((!GIV(primFailCode)) + && (!embedded)) { + /* set Mac file type and creator; this is a noop on other platforms */ + setMacType = ioLoadFunctionFrom("setMacFileTypeAndCreator", "FilePlugin"); + if (setMacType) { + ((sqInt (*)(char *, char *, char *))setMacType)(imageName, "STim", "FAST"); + } + } + + /* Without contexts or stacks simulate + self pop: 1 */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(activeContext)) + && (!(isForwarded(activeContext)))); + assert(validStorePointerUncheckedArgs(StackPointerIndex, activeContext, (((usqInt)(stackIndex - 1) << 3) | 1))); + longAtput((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord()))))),(((usqInt)(stackIndex - 1) << 3) | 1)); + } + + /* begin postSnapshot */ + restorePostSnapshot(); + refireQueuedEphemeronsPostSnapshot(); + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + if (GIV(primFailCode)) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + cogMethod = null; + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + methodHeader = (cogMethod->methodHeader); + cogMethod = null; + } + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeader)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvrSqInt = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(addressCouldBeOop(rcvrSqInt)); + assert(!(isOopForwarded(rcvrSqInt))); + if (cogMethod) { + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + } + GIV(instructionPointer) = ceReturnToInterpreterPC(); + } + cogMethod = null; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + initialIP = ((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize; + if (cogMethod) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)cogMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + GIV(instructionPointer) = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + GIV(instructionPointer) = initialIP - 1; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),rcvrSqInt); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + if (!cogMethod) { + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + } + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeader); + } + } + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(falseObj)); + } + return 0; +} + + /* StackInterpreter>>#specialSelector: */ +sqInt +specialSelector(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin fetchPointer:ofObject: */ + return longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)((index * 2)) << (shiftForWord())))))); +} + + +/* Insulate the stack zone from the effects of a become. + All receivers must be unfollowed for two reasons: + 1. inst var access is direct with no read barrier + 2. super sends (always to the receiver) have no class check and so don't + trap for forwarded receivers. This is an issue for primitives that assume + their receiver + is valid and don't validate. + Super sends require an explicit check to ensure receivers in super sends + are unforwarded. + e.g. super doSomethingWith: (self become: other) forwards the receiver + self pushed on the + stack. So we could avoid following non-pointer receivers. But this is too + tricky, Instead, we + always follow receivers. + Methods must be unfollowed since bytecode access is direct with no read + barrier. But this only needs to be done if the becomeEffectsFlags indicate + that a + CompiledMethod was becommed. + The scheduler state must be followed, but only if the becomeEffectsFlags + indicate that a pointer object was becommed. */ +/* For VM profiling */ + + /* StackInterpreter>>#spurPostBecomeAction: */ +static NoDbgRegParms void +spurPostBecomeAction(sqInt theBecomeEffectsFlags) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqIntptr_t c; + sqInt first; + int i; + sqInt ipdelta; + sqInt iSqInt; + sqInt last; + sqInt list; + sqIntptr_t m; + sqInt next; + usqInt numSlots; + sqInt obj; + sqInt procLists; + sqInt referent; + sqInt result; + sqIntptr_t s; + sqInt sched; + sqInt schedAssoc; + sqInt toDoLimit; + sqInt xArray; + + if (theBecomeEffectsFlags) { + if (((theBecomeEffectsFlags & BecameActiveClassFlag) != 0)) { + /* begin flushBecommedClassesInMethodCache */ + for (i = 0; i < MethodCacheSize; i += MethodCacheEntrySize) { + c = GIV(methodCache)[i + MethodCacheClass]; + s = GIV(methodCache)[i + MethodCacheSelector]; + if ((c != 0) + && ((s != 0) + && (isForwarded(classOrNilAtIndex(c))))) { + GIV(methodCache)[i + MethodCacheClass] = 0; + GIV(methodCache)[i + MethodCacheSelector] = 0; + } + } + unlinkSendsLinkedForInvalidClasses(); + } + if (((theBecomeEffectsFlags & BecamePointerObjectFlag) != 0)) { + /* begin followForwardingPointersInScheduler */ + schedAssoc = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord())))))); + + /* the GC follows pointers in the special objects array for us. */ + assert(!(isForwarded(schedAssoc))); + + /* Make sure the active process has been followed. */ + followForwardedObjectFieldstoDepth(schedAssoc, 1); + sched = longAt((void *)((schedAssoc + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin followObjField:ofObject: */ + procLists = longAt((void *)((sched + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + assert(isNonImmediate(procLists)); + if ((!((longAt((void *)(procLists))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + procLists = fixFollowedFieldofObjectwithInitialValue(ProcessListsIndex, sched, procLists); + } + + /* Follow all links in the process list to ensure the lists are valid. */ + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(procLists)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(procLists + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(procLists - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (iSqInt = 0; iSqInt <= toDoLimit; iSqInt += 1) { + /* begin followObjField:ofObject: */ + list = longAt((void *)((procLists + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + assert(isNonImmediate(list)); + if ((!((longAt((void *)(list))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + list = fixFollowedFieldofObjectwithInitialValue(iSqInt, procLists, list); + } + + /* begin followObjField:ofObject: */ + first = longAt((void *)((list + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(first)); + if ((!((longAt((void *)(first))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + first = fixFollowedFieldofObjectwithInitialValue(FirstLinkIndex, list, first); + } + + /* begin followObjField:ofObject: */ + last = longAt((void *)((list + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(last)); + if ((!((longAt((void *)(last))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + last = fixFollowedFieldofObjectwithInitialValue(LastLinkIndex, list, last); + } + while (first != last) { + /* begin followObjField:ofObject: */ + next = longAt((void *)((first + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(next)); + if ((!((longAt((void *)(next))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + next = fixFollowedFieldofObjectwithInitialValue(NextLinkIndex, first, next); + } + first = next; + } + } + + /* begin followForwardingPointersInSpecialObjectsArray */ + /* begin followSemaphoreIn:at: */ + obj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheLowSpaceSemaphore) << (shiftForWord())))))); + if ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + obj = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheLowSpaceSemaphore, GIV(specialObjectsOop), obj)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheLowSpaceSemaphore) << (shiftForWord()))))),obj); + } + + /* begin followSemaphoreIn:at: */ + obj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheInterruptSemaphore) << (shiftForWord())))))); + if ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + obj = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheInterruptSemaphore, GIV(specialObjectsOop), obj)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheInterruptSemaphore) << (shiftForWord()))))),obj); + } + + /* begin followSemaphoreIn:at: */ + obj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord())))))); + if ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + obj = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheTimerSemaphore, GIV(specialObjectsOop), obj)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord()))))),obj); + } + + /* begin followSemaphoreIn:at: */ + obj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheFinalizationSemaphore) << (shiftForWord())))))); + if ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + obj = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheFinalizationSemaphore, GIV(specialObjectsOop), obj)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheFinalizationSemaphore) << (shiftForWord()))))),obj); + } + xArray = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ExternalObjectsArray) << (shiftForWord())))))); + if ((!((longAt((void *)(xArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(xArray)); + referent = longAt((void *)((xArray + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + xArray = referent; + + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ExternalObjectsArray, GIV(specialObjectsOop), xArray)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(xArray & (tagMask())))) + && (oopisLessThan(xArray, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ExternalObjectsArray) << (shiftForWord()))))),xArray); + } + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(xArray)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(xArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(xArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (ipdelta = 0; ipdelta <= toDoLimit; ipdelta += 1) { + /* begin followSemaphoreIn:at: */ + obj = longAt((void *)((xArray + BaseHeaderSize) + ((((usqInt)(ipdelta) << (shiftForWord())))))); + if ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + obj = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ipdelta, xArray, obj)); + assert(isNonImmediate(xArray)); + if (oopisGreaterThanOrEqualTo(xArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(xArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(xArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((xArray + BaseHeaderSize) + ((((usqInt)(ipdelta) << (shiftForWord()))))),obj); + } + } + } + if (((theBecomeEffectsFlags & (BecamePointerObjectFlag + BecameCompiledMethodFlag)) != 0)) { + /* begin followForwardingPointersInProfileState */ + /* begin profileStateDoUpdating: */ + if (GIV(profileProcess)) { + if ((result = ((!((longAt((void *)(GIV(profileProcess)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileProcess)) + : 0))) { + GIV(profileProcess) = result; + } + } + if (GIV(profileMethod)) { + if ((result = ((!((longAt((void *)(GIV(profileMethod)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileMethod)) + : 0))) { + GIV(profileMethod) = result; + } + } + if (GIV(profileSemaphore)) { + if ((result = ((!((longAt((void *)(GIV(profileSemaphore)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileSemaphore)) + : 0))) { + GIV(profileSemaphore) = result; + } + } + if (((theBecomeEffectsFlags & BecameCompiledMethodFlag) != 0)) { + /* begin followForwardedMethodsInMethodCache */ + for (i = 0; i < MethodCacheSize; i += MethodCacheEntrySize) { + c = GIV(methodCache)[i + MethodCacheClass]; + s = GIV(methodCache)[i + MethodCacheSelector]; + m = GIV(methodCache)[i + MethodCacheMethod]; + if ((c != 0) + && ((s != 0) + && ((m != 0) + && (/* isOopForwarded: */ + ((!(m & (tagMask())))) + && ((!((longAt((void *)(m))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(m)); + referent = longAt((void *)((m + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + m = referent; + GIV(methodCache)[i + MethodCacheMethod] = m; + } + } + + /* begin followMethodNewMethodAndInstructionPointer */ + ipdelta = 0; + if ((!((longAt((void *)(GIV(method)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + ipdelta = (/* method:includesAddress: */ + (GIV(instructionPointer) > GIV(method)) + && (GIV(instructionPointer) < (GIV(method) + ((((numSlotsOfAny(GIV(method))) << (shiftForWord()))) + BaseHeaderSize))) + ? GIV(instructionPointer) - GIV(method) + : 0); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(GIV(method))); + referent = longAt((void *)((GIV(method) + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + GIV(method) = referent; + if (ipdelta) { + GIV(instructionPointer) = GIV(method) + ipdelta; + } + } + if (/* isOopForwarded: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((!((longAt((void *)(GIV(newMethod)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ipdelta = ((!ipdelta) + && (/* method:includesAddress: */ + (GIV(instructionPointer) > GIV(newMethod)) + && (GIV(instructionPointer) < (GIV(newMethod) + ((((numSlotsOfAny(GIV(newMethod))) << (shiftForWord()))) + BaseHeaderSize)))) + ? GIV(instructionPointer) - GIV(newMethod) + : 0); + + /* don't relocate twice!! */ + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(GIV(newMethod))); + referent = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + GIV(newMethod) = referent; + } + + /* maybe oop due to object-as-method */ + if (ipdelta) { + GIV(instructionPointer) = GIV(newMethod) + ipdelta; + } + } + } + } + followForwardingPointersOfReceiversInStackZone(); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackFloatValue: */ +double +stackFloatValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return floatValueOf(longAt(GIV(stackPointer) + (offset * BytesPerWord))); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackIntegerValue: */ +sqInt +stackIntegerValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerPointer; + + integerPointer = longAt(GIV(stackPointer) + (offset * BytesPerWord)); + + /* begin checkedIntegerValueOf: */ + if ((((integerPointer) & 7) == 1)) { + return (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } +} + + +/* Answer the actual stackLimit offset in a page. Since stackPageByteSize may + have chosen to shrink a page + this may be less than stackPageFrameBytes, but it should be no more than + stackPageFrameBytes. + */ + + /* StackInterpreter>>#stackLimitBytes */ +static sqInt +stackLimitBytes(void) +{ + return (((stackPageFrameBytes()) < (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))) ? (stackPageFrameBytes()) : (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))); +} + + +/* Ensures that the given object is a real object, not a SmallInteger. */ +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackMutableObjectValue: */ +sqInt +stackMutableObjectValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = longAt(GIV(stackPointer) + (offset * BytesPerWord)); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(oop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + } + return oop; +} + + +/* Ensures that the given object is a real object, not a SmallInteger. */ +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackObjectValue: */ +sqInt +stackObjectValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = longAt(GIV(stackPointer) + (offset * BytesPerWord)); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return oop; +} + + +/* Answer a page size that is a power-of-two and contains a useful number of + frames. Room for 256 slots for frames gives around 40 frames a page which + is a + good compromise between overflow rate and latency in divorcing a page. */ + + /* StackInterpreter>>#stackPageByteSize */ +static sqInt +stackPageByteSize(void) +{ + sqInt largeSize; + sqInt pageBytes; + sqInt smallSize; + + pageBytes = ((stackPageFrameBytes()) + (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) + (osCogStackPageHeadroom()); + if (!(pageBytes & (pageBytes - 1))) { + return pageBytes; + } + + /* = 0 => a power of two + round up or round down; that is the question. If rounding down reduces + the size by no more than 1/8th round down, otherwise roundup. */ + largeSize = 1ULL << (highBit(pageBytes)); + smallSize = 1ULL << ((highBit(pageBytes)) - 1); + assert((largeSize > pageBytes) + && (pageBytes > smallSize)); + return ((pageBytes - smallSize) <= (smallSize / 8) + ? smallSize + : largeSize); +} + + +/* Return the stackPointer of a Context or BlockContext. */ + + /* StackInterpreter>>#stackPointerForMaybeMarriedContext: */ +static NoDbgRegParms sqInt +stackPointerForMaybeMarriedContext(sqInt aContext) +{ + sqInt sp; + sqInt spSqInt; + + if (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(aContext)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(aContext))); + return sp; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + return 0; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + return (spSqInt >> 3); +} + + +/* Return the 0-based index rel to the given frame. + (This is what stackPointer used to be before conversion to pointer) */ +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackPointerIndexForFrame: */ +static NoDbgRegParms sqInt +stackPointerIndexForFrame(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *thePage; + char *theSP; + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + theSP = findSPOfon(theFP, thePage); + return /* stackPointerIndexForFrame:WithSP: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (((usqInt)(((theFP + FoxMFReceiver) - theSP))) >> (shiftForWord())) + (((mframeCogMethod(theFP))->cmNumArgs)) + : (((usqInt)(((theFP + FoxIFReceiver) - theSP))) >> (shiftForWord())) + (byteAt((theFP + FoxIFrameFlags) + 1))); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackPositiveMachineIntegerValue: */ +usqIntptr_t +stackPositiveMachineIntegerValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerPointer; + sqInt value; + + integerPointer = longAt(GIV(stackPointer) + (offset * BytesPerWord)); + + /* begin positiveMachineIntegerValueOf: */ + if ((((integerPointer) & 7) == 1)) { + value = (integerPointer >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return value; + } + + /* don't inline the rare case */ + return positiveMachineIntegerValueOfObj(integerPointer); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackSignedMachineIntegerValue: */ +sqIntptr_t +stackSignedMachineIntegerValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerPointer; + + integerPointer = longAt(GIV(stackPointer) + (offset * BytesPerWord)); + + /* begin signedMachineIntegerValueOf: */ + if ((((integerPointer) & 7) == 1)) { + return (integerPointer >> 3); + } + return signedMachineIntegerValueOfObj(integerPointer); +} + + /* StackInterpreter>>#stackTop */ +sqInt +stackTop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt(GIV(stackPointer)); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackValue: */ +sqInt +stackValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt(GIV(stackPointer) + (offset * BytesPerWord)); +} + + +/* Zero-relative version of CompiledMethod>>startpc. */ + + /* StackInterpreter>>#startPCOfMethod: */ +usqInt +startPCOfMethod(sqInt aCompiledMethod) +{ + return ((literalCountOf(aCompiledMethod)) + LiteralStart) * BytesPerOop; +} + + +/* Return what ST would return for at: index. */ + + /* StackInterpreter>>#stObject:at: */ +sqInt +stObjectat(sqInt array, sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt sp; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + + hdr = long64At((void *)(array)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(array + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(array)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(array)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(array))); + stSize = sp; + goto l3; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l3; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(array))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l3: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(array)))))) { + return temporaryin(index - 1, frameOfMarriedContext(array)); + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(array)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + return (((usqInt)(byteAt((void *)((array + BaseHeaderSize) + (index - 1)))) << 3) | 1); + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(array); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + return longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << (shiftForWord())))))); + } + if (fmt >= (firstByteFormat())) { + return (((usqInt)(byteAt((void *)((array + BaseHeaderSize) + ((index + fixedFields) - 1)))) << 3) | 1); + } + if (fmt >= (firstShortFormat())) { + return (((usqInt)(((unsigned short) (shortAt((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))))))) << 3) | 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return positive64BitIntegerFor(long64At((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))))); + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + return (((usqInt)((((usqInt)(long32At((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + return 0; +} + + +/* Do what ST would return for at: index put: value. */ + + /* StackInterpreter>>#stObject:at:put: */ +sqInt +stObjectatput(sqInt array, sqInt index, sqInt value) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fieldIndex; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt signedValueToStore; + sqInt signedValueToStoreSqInt; + sqInt sp; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + usqLong unsigned64BitValueToStore; + unsigned int unsignedValueToStore; + + hdr = long64At((void *)(array)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(array + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l1: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(array)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(array)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(array))); + stSize = sp; + goto l2; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l2; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(array))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l2: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(array)))))) { + return temporaryinput(index - 1, frameOfMarriedContext(array), value); + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(array)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + if (((((value) & 7) == 1)) + && ((((usqInt)((signedValueToStore = (value >> 3))))) <= 0xFF)) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((array + BaseHeaderSize) + (index - 1)),signedValueToStore); + return value; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return value; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l3; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l3; + } + class = fetchClassOfNonImm(array); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l3: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:storing:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + fieldIndex = (index + fixedFields) - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, array, value)); + assert(isNonImmediate(array)); + if (oopisGreaterThanOrEqualTo(array, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(array + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(array); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),value); + goto l4; + } + if (fmt >= (firstByteFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((array + BaseHeaderSize) + ((index + fixedFields) - 1)),signedValueToStoreSqInt); + goto l4; + } + if (fmt >= (firstShortFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))),signedValueToStoreSqInt); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l4; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l4: + return value; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + return value; +} + + +/* Set the sender of a frame. If the frame is a base frame then this is + trivial; merely store into the FoxCallerSavedIP/FoxCallerContext field. If + not, then + split the stack at the frame, moving the frame and those hotter than it to + a new stack page. In the new stack page the frame will be the base frame + and storing trivial. Answer the possibly changed location of theFP. */ + + /* StackInterpreter>>#storeSenderOfFrame:withValue: */ +static NoDbgRegParms char * +storeSenderOfFramewithValue(char *theFP, sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + char *callerFP; + StackPage *newPage; + int onCurrentPage; + char *theMovedFP; + StackPage *thePage; + StackPage *thePage1; + + if (!(longAt(theFP + FoxSavedFP))) { + /* begin frameCallerContext:put: */ + assert((anOop == (nilObject())) + || (isContext(anOop))); + assert(isBaseFrame(theFP)); + assert(((theFP + (frameStackedReceiverOffset(theFP))) + (2 * BytesPerWord)) == (((stackPageFor(theFP))->baseAddress))); + assert((longAt((theFP + (frameStackedReceiverOffset(theFP))) + BytesPerWord)) == (frameContext(theFP))); + longAtput((theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + (2 * BytesPerWord),anOop); + return theFP; + } + + /* begin ensureCallerContext: */ + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!callerFP) { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerContextOrNil = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + goto l2; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP))); + + /* frameContext: */ + goto l2; + } + marryFrameSP( + callerFP, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFP))), + (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + /* end ensureCallerContext: */ +l2: + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + onCurrentPage = thePage == GIV(stackPage); + if (!onCurrentPage) { + /* Make sure the frame's page isn't divorced when a new page is allocated. */ + markStackPageNextMostRecentlyUsed(thePage); + } + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l1; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l1: + theMovedFP = moveFramesInthroughtoPage(thePage, theFP, newPage); + if (onCurrentPage) { + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + } + else { + markStackPageMostRecentlyUsed(newPage); + } + assert(isBaseFrame(theMovedFP)); + + /* begin frameCallerContext:put: */ + assert((anOop == (nilObject())) + || (isContext(anOop))); + assert(isBaseFrame(theMovedFP)); + assert(((theMovedFP + (frameStackedReceiverOffset(theMovedFP))) + (2 * BytesPerWord)) == (((stackPageFor(theMovedFP))->baseAddress))); + assert((longAt((theMovedFP + (frameStackedReceiverOffset(theMovedFP))) + BytesPerWord)) == (frameContext(theMovedFP))); + longAtput((theMovedFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theMovedFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theMovedFP))->cmNumArgs) + : byteAt((theMovedFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + (2 * BytesPerWord),anOop); + return theMovedFP; +} + + +/* Answer the number of indexable fields in the given object. (i.e., what + Smalltalk would return for size). + */ +/* Note: Assume oop is not an immediate! */ + + /* StackInterpreter>>#stSizeOf: */ +sqInt +stSizeOf(sqInt oop) +{ + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt totalLength; + + hdr = long64At((void *)(oop)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l1: + if (/* isPureBitsFormat: */ + (fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat()))) { + return totalLength; + } + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l2; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l2; + } + class = fetchClassOfNonImm(oop); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l2: + if (fmt == (indexablePointersFormat())) { + assert(!(isContextHeader(hdr))); + } + return totalLength - fixedFields; +} + + +/* Using a read barrier here simplifies the become implementation and costs + very little because the class index and superclass almost certainly share + a cache line. */ + + /* StackInterpreter>>#superclassOf: */ +sqInt +superclassOf(sqInt classPointer) +{ + sqInt objOop; + + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, objOop); + } + return objOop; +} + + +/* Signal the given semaphore from within the interpreter. + Answer if the current process was preempted. */ + + /* StackInterpreter>>#synchronousSignal: */ +static NoDbgRegParms sqInt +synchronousSignal(sqInt aSemaphore) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + sqLong excessSignals; + sqInt proc; + sqInt referent; + + assert((addressCouldBeObj(aSemaphore)) + && ((!(isForwarded(aSemaphore))) + && ((numSlotsOf(aSemaphore)) > ExcessSignalsIndex))); + + /* begin isEmptyList: */ + assert(!(isForwarded(aSemaphore))); + if ((longAt((void *)((aSemaphore + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj)) { + /* no process is waiting on this semaphore */ + excessSignals = fetchIntegerofObject(ExcessSignalsIndex, aSemaphore); + storeIntegerofObjectwithValue(ExcessSignalsIndex, aSemaphore, excessSignals + 1); + return 0; + } + + /* begin ensureSemaphoreUnforwardedThroughContext: */ + proc = longAt((void *)((aSemaphore + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + if ((!((longAt((void *)(proc))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + followForwardedObjectFieldstoDepth(aSemaphore, 1); + proc = longAt((void *)((aSemaphore + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + } + assert(!((isForwarded(proc)))); + ctxt = longAt((void *)((proc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if ((!((longAt((void *)(ctxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(ctxt)); + referent = longAt((void *)((ctxt + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + ctxt = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, proc, ctxt)); + assert(isNonImmediate(proc)); + if (oopisGreaterThanOrEqualTo(proc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(ctxt & (tagMask())))) + && (oopisLessThan(ctxt, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(proc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(proc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((proc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),ctxt); + } + return resumepreemptedYieldingIffrom(removeFirstLinkOfList(aSemaphore), (!(imageHeaderFlags & 16)), CSSynchronousSignal); +} + + /* StackInterpreter>>#tempCountOf: */ +usqInt +tempCountOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin temporaryCountOfMethodHeader: */ + return (((usqInt)(header)) >> MethodHeaderTempCountShift) & 0x3F; +} + + /* StackInterpreter>>#temporaryCountOfMethodHeader: */ +usqInt +temporaryCountOfMethodHeader(sqInt header) +{ + return (((usqInt)(header)) >> MethodHeaderTempCountShift) & 0x3F; +} + + /* StackInterpreter>>#ultimateLiteralOf: */ +sqInt +ultimateLiteralOf(sqInt aMethodOop) +{ + sqInt offset; + + assert(isOopCompiledMethod(aMethodOop)); + offset = (literalCountOf(aMethodOop)) - 1; + + /* begin literal:ofMethod: */ + return longAt((void *)((aMethodOop + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); +} + + +/* Follow the first literal of either a primitiveCallout or + primitiveExternalCall primitive method. This + will be an ExternalFunction for primitiveCallout or a four element Array + for primitiveExternalCall. + This is here to avoid following all the literals in a method, which would + be slow. Remember + forwarders are unlikely, so we only want to follow what is necessary, and + for an FFI call or + external primitive only the first literal is salient. */ + + /* StackInterpreter>>#unfollowFirstLiteralOfMaybeCalloutMethod:primitiveIndex: */ +static NoDbgRegParms sqInt +unfollowFirstLiteralOfMaybeCalloutMethodprimitiveIndex(sqInt methodObj, sqInt primIndex) +{ + sqInt firstLiteral; + sqInt found; + + found = 0; + + /* inlined self literal: 0 ofMethod: methodObj for clarity... */ + firstLiteral = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(LiteralStart) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(firstLiteral & (tagMask())))) + && ((!((longAt((void *)(firstLiteral))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + found = 1; + firstLiteral = fixFollowedFieldofObjectwithInitialValue(LiteralStart, methodObj, firstLiteral); + } + if (followForwardedObjectFieldstoDepth(firstLiteral, (primIndex == PrimNumberFFICall + ? (((sqInt)((primitiveMetadataTable[PrimNumberFFICall]))) >> SpurPrimitiveAccessorDepthShift) - 1 + : 0))) { + found = 1; + } + return found; +} + + +/* Repaint the portion of the Smalltalk screen bounded by the affected + rectangle. Used to synchronize the screen after a Bitblt to the Smalltalk + Display object. */ + + /* StackInterpreter>>#updateDisplayLeft:Top:Right:Bottom: */ +static NoDbgRegParms sqInt +updateDisplayLeftTopRightBottom(sqInt l, sqInt t, sqInt r, sqInt b) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bottom; + sqInt left; + sqInt right; + sqInt surfaceHandle; + sqInt top; + + left = ((l < 0) ? 0 : l); + right = ((r < displayWidth) ? r : displayWidth); + top = ((t < 0) ? 0 : t); + bottom = ((b < displayHeight) ? b : displayHeight); + if (!((left <= right) + && (top <= bottom))) { + return null; + } + if (((((((sqInt)displayBits))) & 7) == 1)) { + surfaceHandle = ((((sqInt)displayBits)) >> 3); + if (!showSurfaceFn) { + showSurfaceFn = ioLoadFunctionFrom("ioShowSurface", "SurfacePlugin"); + if (!showSurfaceFn) { + return (GIV(primFailCode) = PrimErrNotFound); + } + } + showSurfaceFn(surfaceHandle, left, top, right - left, bottom - top); + } + else { + assert(isNonImmediate(((sqInt)displayBits))); + ioShowDisplay(((sqInt)displayBits), displayWidth, displayHeight, displayDepth, left, right, top, bottom); + } + return 0; +} + + +/* Note that we accept anInstrPointer pointing to a callPrimitiveBytecode + at the start of a method that contains a primitive. This because methods + like Context(Part)>>reset have to be updated to skip the callPrimtiive + bytecode otherwise. */ + + /* StackInterpreter>>#validBCPC:inMethod: */ +static NoDbgRegParms sqInt +validBCPCinMethod(sqInt thePC, usqInt aMethod) +{ + return (thePC >= (((literalCountOf(aMethod)) + LiteralStart) * BytesPerOop)) + && (thePC < (numBytesOfBytes(aMethod))); +} + + +/* Note that we accept anInstrPointer pointing to a callPrimitiveBytecode + at the start of a method that contains a primitive. This because methods + like Context(Part)>>reset have to be updated to skip the callPrimtiive + bytecode otherwise. */ + + /* StackInterpreter>>#validInstructionPointer:inFrame: */ +static NoDbgRegParms sqInt +validInstructionPointerinFrame(usqInt anInstrPointer, char *fp) +{ + return validInstructionPointerinMethodframePointer(anInstrPointer, /* frameMethodObject: */ + ((((usqInt)(longAt(fp + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(fp))->methodObject) + : longAt(fp + FoxMethod)), fp); +} + + +/* Check that the base frames in all in-use stack pages have a sender and a + saved context. + */ + + /* StackInterpreter>>#validStackPageBaseFrames */ +static sqInt +validStackPageBaseFrames(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *aPage; + sqInt i; + + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + aPage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(aPage))) { + if (!(validStackPageBaseFrame(aPage))) { + return 0; + } + } + } + return 1; +} + + +/* Void the state associated with the long-running primitive check. + This is done when a new semaphore is installed or when it appears + that is longRunningPrimitiveCheckMethod is invalid, e.g. because it + has eben sampled in the middle of a GC. */ + + /* StackInterpreter>>#voidLongRunningPrimitive: */ +#if LRPCheck +static NoDbgRegParms NeverInline void +voidLongRunningPrimitive(char *reason) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(longRunningPrimitiveCheckMethod) = null; + GIV(longRunningPrimitiveStartUsecs) = (GIV(longRunningPrimitiveStopUsecs) = 0); + GIV(longRunningPrimitiveSignalUndelivered) = 1; + sqLowLevelMFence(); +} +#endif /* LRPCheck */ + + +/* Find the highest priority process that is ready to run and + remove it from its list. + To save time looking at many empty lists before finding a + runnable process the VM maintains a variable holding the + highest priority runnable process. If this variable is 0 then the + VM does not know the highest priority and must search all lists. + Answer nil if no process can be found. */ + + /* StackInterpreter>>#wakeHighestPriority */ +static sqInt +wakeHighestPriority(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + usqInt numSlots; + sqInt objOop; + sqInt p; + sqInt proc; + sqInt processList; + sqInt schedLists; + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + schedLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + p = (GIV(highestRunnableProcessPriority) + ? GIV(highestRunnableProcessPriority) + : (/* begin numSlotsOf: */ + assert((classIndexOf(schedLists)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(schedLists + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(schedLists - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))); + while (((p -= 1)) >= 0) { + processList = longAt((void *)((schedLists + BaseHeaderSize) + ((((usqInt)(p) << (shiftForWord())))))); + while (1) { + /* begin isEmptyList: */ + assert(!(isForwarded(processList))); + if ((longAt((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj)) break; + /* Only answer processes with a runnable suspendedContext. + Discard those that aren't; the VM would crash otherwise. */ + proc = removeFirstLinkOfList(processList); + ctxt = longAt((void *)((proc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if (isLiveContext(ctxt)) { + GIV(highestRunnableProcessPriority) = p + 1; + return proc; + } + warning("evicted zombie process from run queue"); + } + } + return null; +} + + +/* Write the image header and heap contents to imageFile for snapshot. c.f. + writeImageFileIOSimulation. The game below is to maintain 64-bit alignment + for all putLong:toFile: occurrences. */ + + /* StackInterpreter>>#writeImageFileIO */ +static NeverInline sqInt +writeImageFileIO(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bytesWritten; + sqImageFile f; + sqInt headerSize; + squeakFileOffsetType headerStart; + sqInt i; + sqInt imageBytes; + extern char imageName[]; + sqInt okToWrite; + void *sCWIfn; + + /* If the security plugin can be loaded, use it to check for write permission. + If not, assume it's ok */ + sCWIfn = ioLoadFunctionFrom("secCanWriteImage", "SecurityPlugin"); + if (sCWIfn) { + okToWrite = ((sqInt (*)(void))sCWIfn)(); + if (!okToWrite) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + } + + /* local constants */ + headerStart = 0; + + /* 64 or 128; header size in bytes; do not change! */ + headerSize = BytesPerWord * 16; + f = sqImageFileOpen(imageName, "wb"); + if (invalidSqImageFile(f)) { + return (GIV(primFailCode) = PrimErrOperationFailed); + } + + /* could not open the image file for writing */ + + /* begin imageSizeToWrite */ + assert(newSpaceIsEmpty()); + imageBytes = 0; + for (i = 0; i < GIV(numSegments); i += 1) { + if ((((GIV(segments)[i]).segSize)) > (2 * BaseHeaderSize)) { + imageBytes += ((GIV(segments)[i]).segSize); + } + } + headerStart = sqImageFileStartLocation(f, imageName, headerSize + imageBytes); + /* Note: on Unix systems one could put an exec command here, padded to 512 bytes */; + + /* position file to start of header */ + sqImageFileSeek(f, headerStart); + putWord32toFile(/* imageFormatVersionForSnapshot */ + (GIV(multipleBytecodeSetsActive) + ? 68533 /* (imageFormatVersion bitOr: MultipleBytecodeSetsBitmask) */ + : 68021 /* imageFormatVersion */), f); + putWord32toFile(headerSize, f); + putLongtoFile(imageBytes, f); + putLongtoFile(GIV(oldSpaceStart), f); + putLongtoFile(GIV(specialObjectsOop), f); + putLongtoFile(GIV(lastHash), f); + putLongtoFile(getSnapshotScreenSize(), f); + putLongtoFile(imageHeaderFlags, f); + putWord32toFile(extraVMMemory, f); + putShorttoFile(desiredNumStackPages, f); + putShorttoFile((desiredCogCodeSize + 0x3FF) / 0x400, f); + putWord32toFile(desiredEdenBytes, f); + putShorttoFile((GIV(maxExtSemTabSizeSet) + ? ioGetMaxExtSemTableSize() + : 0), f); + putShorttoFile(GIV(the2ndUnknownShort), f); + putLongtoFile(((GIV(segments)[0]).segSize), f); + putLongtoFile(GIV(totalFreeOldSpace), f); + putLongtoFile(0, f); + putLongtoFile(0, f); + putLongtoFile(0, f); + putLongtoFile(0, f); + putLongtoFile(0, f); + assert((headerStart + headerSize) == (sqImageFilePosition(f))); + + /* position file after the header */ + sqImageFileSeek(f, headerStart + headerSize); + if (GIV(primFailCode)) { + sqImageFileClose(f); + return null; + } + + /* file write or seek failure + write the image data */ + bytesWritten = writeImageSegmentsToFile(f); + + /* begin success: */ + if (!(bytesWritten == imageBytes)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + sqImageFileClose(f); + return 0; +} + + /* StackInterpreterPrimitives>>#cloneContext: */ +static NoDbgRegParms usqInt +cloneContext(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt cloned; + sqInt fieldIndex; + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + sqInt i; + usqInt newObj; + usqInt numBytes; + usqInt numSlots; + sqInt senderOop; + sqInt sp; + char *spouseFP; + usqInt sz; + sqInt valuePointer; + + /* begin numSlotsOf: */ + assert((classIndexOf(aContext)) > (isForwardedObjectClassIndexPun())); + sz = (((numSlots = byteAt((void *)(aContext + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aContext - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + + /* begin eeInstantiateMethodContextSlots: */ + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(sz < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((sz < 1 + ? 8 /* allocationUnit */ + : sz * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + cloned = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) sz)) << (numSlotsFullShift()))) + ((((usqInt)((indexablePointersFormat())) << (formatShift()))))) + ClassMethodContextCompactIndex); + GIV(freeStart) += numBytes; + cloned = newObj; + /* end eeInstantiateMethodContextSlots: */ +l1: + assert(!((cloned == 0))); + for (i = 0; i <= StackPointerIndex; i += 1) { + valuePointer = externalInstVarofContext(i, aContext); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(cloned)) + && (!(isForwarded(cloned)))); + assert(validStorePointerUncheckedArgs(i, cloned, valuePointer)); + longAtput((void *)((cloned + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + for (i = MethodIndex; i <= ReceiverIndex; i += 1) { + valuePointer = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(cloned)) + && (!(isForwarded(cloned)))); + assert(validStorePointerUncheckedArgs(i, cloned, valuePointer)); + longAtput((void *)((cloned + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + if (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + sp = (stackPointerIndexForFrame(spouseFP)) - 1; + for (i = 0; i <= sp; i += 1) { + fieldIndex = i + CtxtTempFrameStart; + valuePointer = /* temporary:in: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? (i < ((frameNumArgs = ((mframeCogMethod(spouseFP))->cmNumArgs))) + ? longAt((void *)((spouseFP + FoxCallerSavedIP) + ((frameNumArgs - i) * BytesPerWord))) + : longAt((void *)(((spouseFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs - i) * BytesPerWord)))) + : /* itemporary:in: */ + (i < ((frameNumArgsUsqInt = byteAt((spouseFP + FoxIFrameFlags) + 1))) + ? longAt((void *)((spouseFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt - i) * BytesPerWord))) + : longAt((void *)(((spouseFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt - i) * BytesPerWord))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(cloned)) + && (!(isForwarded(cloned)))); + assert(validStorePointerUncheckedArgs(fieldIndex, cloned, valuePointer)); + longAtput((void *)((cloned + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); + } + } + else { + sp = (fetchStackPointerOf(aContext)) - 1; + for (i = 0; i <= sp; i += 1) { + fieldIndex = i + CtxtTempFrameStart; + valuePointer = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)((i + CtxtTempFrameStart)) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(cloned)) + && (!(isForwarded(cloned)))); + assert(validStorePointerUncheckedArgs(fieldIndex, cloned, valuePointer)); + longAtput((void *)((cloned + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); + } + } + return cloned; +} + + +/* Arrange to answer naked frame pointers for unmarried + senders to avoid reifying contexts in the search. */ + + /* StackInterpreterPrimitives>>#fieldOrSenderFP:ofContext: */ +static NoDbgRegParms sqInt +fieldOrSenderFPofContext(sqInt index, sqInt contextObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt senderOop; + char *spouseFP; + sqInt tempIndex; + + tempIndex = index - CtxtTempFrameStart; + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((contextObj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(contextObj))))) { + if (tempIndex >= (fetchStackPointerOf(contextObj))) { + return GIV(nilObj); + } + else { + return longAt((void *)((contextObj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + } + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextObj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (tempIndex >= (stackPointerIndexForFrame(spouseFP))) { + return GIV(nilObj); + } + return fieldofFrame(index, spouseFP); +} + + +/* Arrange to answer naked frame pointers for unmarried + senders to avoid reifying contexts in the search. */ + + /* StackInterpreterPrimitives>>#field:ofFrame: */ +static NoDbgRegParms sqInt +fieldofFrame(sqInt index, char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + + switch (index) { + case SenderIndex: + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + return (callerFP + ? (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0) + ? (assert(checkIsStillMarriedContextcurrentFP(frameContext(callerFP), null)), + /* frameContext: */ + longAt(callerFP + FoxThisContext)) + : ((sqInt)callerFP)) + : frameCallerContext(theFP)); + + case StackPointerIndex: + case InstructionPointerIndex: + return ConstZero; + + case MethodIndex: + return /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + + case ClosureIndex: + return (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0) + ? frameStackedReceivernumArgs(theFP, /* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1))) + : GIV(nilObj)); + + case ReceiverIndex: + return /* frameReceiver: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? longAt(theFP + FoxMFReceiver) + : longAt(theFP + FoxIFReceiver)); + + default: + assert((((index - CtxtTempFrameStart) >= 0) && ((index - CtxtTempFrameStart) <= (stackPointerIndexForFrame(theFP))))); + return /* temporary:in: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((index - CtxtTempFrameStart) < ((frameNumArgs = ((mframeCogMethod(theFP))->cmNumArgs))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgs - (index - CtxtTempFrameStart)) * BytesPerWord)) + : longAt(((theFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs - (index - CtxtTempFrameStart)) * BytesPerWord))) + : /* itemporary:in: */ + ((index - CtxtTempFrameStart) < ((frameNumArgsUsqInt = byteAt((theFP + FoxIFrameFlags) + 1))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt - (index - CtxtTempFrameStart)) * BytesPerWord)) + : longAt(((theFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt - (index - CtxtTempFrameStart)) * BytesPerWord)))); + } + return 0; +} + + /* StackInterpreterPrimitives>>#isAppropriateForCopyObject: */ +static NoDbgRegParms sqInt +isAppropriateForCopyObject(sqInt oop) +{ + if (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) { + return 0; + } + if (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + return !(/* isStillMarriedContext: */ + (((((longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(oop)))); + } + + /* Note there is no version in CoInterpreterPrimitives such as + (objectMemory isCompiledMethod: oop) ifTrue: + [^(self methodHasCogMethod: oop) not]. + because isPointersNonImm: excludes compiled methods and the + copy loop in primitiveCopyObject cannot handle compiled methods. */ + return 1; +} + + +/* The function has not been loaded yet. Fetch module and function name. */ + + /* StackInterpreterPrimitives>>#linkExternalCall:errInto: */ +static NoDbgRegParms void +(*linkExternalCallerrInto(sqInt externalCallLiteral, sqInt *failPtr))(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*addr)(); + sqInt fmt; + void *functionAddress; + sqInt functionLength; + sqInt functionName; + sqInt i; + sqInt index; + sqInt metadata; + sqInt moduleLength; + sqInt moduleName; + usqInt numSlots; + + metadata = 0; + moduleName = longAt((void *)((externalCallLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralModuleNameIndex) << (shiftForWord())))))); + if (moduleName == GIV(nilObj)) { + moduleLength = 0; + } + else { + if (!(/* isBytes: */ + ((!(moduleName & (tagMask())))) + && (((byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + if (failPtr) { + failPtr[0] = PrimErrBadMethod; + } + return 0; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + moduleLength = ((((/* begin numSlotsOf: */ + assert((classIndexOf(moduleName)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(moduleName + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(moduleName - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + } + functionName = longAt((void *)((externalCallLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFunctionNameIndex) << (shiftForWord())))))); + if (!(/* isBytes: */ + ((!(functionName & (tagMask())))) + && (((byteAt((void *)(functionName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + if (failPtr) { + failPtr[0] = PrimErrBadMethod; + } + return 0; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(functionName + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + functionLength = ((((/* begin numSlotsOf: */ + assert((classIndexOf(functionName)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(functionName + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(functionName - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + + /* Spur needs metadata for the primitive, which is stored in the third slot of the literal. */ + addr = ioLoadExternalFunctionOfLengthFromModuleOfLengthMetadataInto(functionName + BaseHeaderSize, functionLength, moduleName + BaseHeaderSize, moduleLength, (&metadata)); + if (addr) { + assert(((((metadata) >> SpurPrimitiveAccessorDepthShift) >= -1) && (((metadata) >> SpurPrimitiveAccessorDepthShift) <= 5))); + functionAddress = ((void *) addr); + + /* begin addToExternalPrimitiveTable: */ + for (i = GIV(externalPrimitiveTableFirstFreeIndex); i < MaxExternalPrimitiveTableSize; i += 1) { + if (!(externalPrimitiveTable[i])) { + externalPrimitiveTable[i] = functionAddress; + index = (GIV(externalPrimitiveTableFirstFreeIndex) = i + 1); + goto l1; + } + } + + /* if no space left, return zero so it'll looked up again */ + index = 0; + /* end addToExternalPrimitiveTable: */ +l1: + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(externalCallLiteral)) + && (!(isForwarded(externalCallLiteral)))); + assert(validStorePointerUncheckedArgs(ExternalCallLiteralFlagsIndex, externalCallLiteral, (((usqInt)metadata << 3) | 1))); + longAtput((void *)((externalCallLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFlagsIndex) << (shiftForWord()))))),(((usqInt)metadata << 3) | 1)); + } + else { + index = -1; + } + + /* Store the index (or -1 if failure) back in the literal */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(externalCallLiteral)) + && (!(isForwarded(externalCallLiteral)))); + assert(validStorePointerUncheckedArgs(ExternalCallLiteralTargetFunctionIndex, externalCallLiteral, (((usqInt)index << 3) | 1))); + longAtput((void *)((externalCallLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord()))))),(((usqInt)index << 3) | 1)); + + /* If the function has been successfully loaded cache it */ + rewriteMethodCacheEntryForExternalPrimitiveToFunction((index >= 0 + ? addr + : 0)); + return addr; +} + + +/* Return a shallow copy of the receiver. + Special-case non-single contexts (because of context-to-stack mapping). + Can't fail for contexts cuz of image context instantiation code (sigh). */ +/* because of cloneContext: below */ + + /* StackInterpreterPrimitives>>#primitiveClone */ +static void +primitiveClone(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newCopy; + sqInt rcvr; + + newCopy = 0; + rcvr = longAt(GIV(stackPointer)); + if (((rcvr & (tagMask())) != 0)) { + newCopy = rcvr; + } + else { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + newCopy = cloneContext(rcvr); + } + else { + if ((GIV(argumentCount) == 0) + || (!((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + newCopy = cloneObject(rcvr); + } + } + if (!newCopy) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + return; + } + } + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newCopy); +} + + +/* Special version of primitiveAt for accessing contexts. + Written to be varargs for use from mirror primitives. */ + + /* StackInterpreterPrimitives>>#primitiveContextAt */ +static void +primitiveContextAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aContext; + sqInt class; + sqInt fixedFields; + sqInt fixedFieldsSqInt; + usqLong fmt; + usqLong fmtUsqLong; + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + sqLong hdr; + sqLong hdrSqLong; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt senderOop; + sqInt sp; + char *spouseFP; + sqInt spSqInt; + sqInt stSize; + sqInt stSizeSqInt; + sqInt totalLength; + sqInt totalLengthSqInt; + sqInt value; + + index = longAt(GIV(stackPointer)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + index = (index >> 3); + aContext = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* Duplicating much of stObject:at:put: here allows stObject:at:put: to omit tests for contexts. */ + hdr = long64At((void *)(aContext)); + if (!((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stObject:at: */ + hdrSqLong = long64At((void *)(aContext)); + fmtUsqLong = (((usqLong)(hdrSqLong)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(aContext + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aContext - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmtUsqLong <= (ephemeronFormat())) { + totalLengthSqInt = numSlots; + goto l7; + } + if (fmtUsqLong >= (firstByteFormat())) { + totalLengthSqInt = ((numSlots << (shiftForWord()))) - (fmtUsqLong & 7); + goto l7; + } + + /* bytes, including CompiledMethod */ + if (fmtUsqLong >= (firstShortFormat())) { + totalLengthSqInt = ((numSlots << ((shiftForWord()) - 1))) - (fmtUsqLong & 3); + goto l7; + } + if (fmtUsqLong >= (firstLongFormat())) { + totalLengthSqInt = ((numSlots << ((shiftForWord()) - 2))) - (fmtUsqLong & 1); + goto l7; + } + if (fmtUsqLong == (sixtyFourBitIndexableFormat())) { + totalLengthSqInt = numSlots; + goto l7; + } + + /* fmt = self forwardedFormat */ + totalLengthSqInt = 0; + /* end lengthOf:baseHeader:format: */ +l7: + if ((fmtUsqLong == (indexablePointersFormat())) + && ((hdrSqLong & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(aContext)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(aContext))); + stSizeSqInt = sp; + goto l5; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSizeSqInt = 0; + goto l5; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + stSizeSqInt = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l5: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSizeSqInt)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))))) { + value = temporaryin(index - 1, frameOfMarriedContext(aContext)); + goto l8; + } + } + if (fmtUsqLong >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(aContext)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLengthSqInt))) { + value = (((usqInt)(byteAt((void *)((aContext + BaseHeaderSize) + (index - 1)))) << 3) | 1); + goto l8; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmtUsqLong >= (sixtyFourBitIndexableFormat())) + || (fmtUsqLong == (arrayFormat()))) { + fixedFieldsSqInt = 0; + goto l6; + } + if (fmtUsqLong < (arrayFormat())) { + fixedFieldsSqInt = totalLengthSqInt; + goto l6; + } + class = fetchClassOfNonImm(aContext); + fixedFieldsSqInt = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l6: + stSizeSqInt = totalLengthSqInt - fixedFieldsSqInt; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSizeSqInt))) { + /* begin subscript:with:format: */ + if (fmtUsqLong <= 5 /* lastPointerFormat */) { + value = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << (shiftForWord())))))); + goto l8; + } + if (fmtUsqLong >= (firstByteFormat())) { + value = (((usqInt)(byteAt((void *)((aContext + BaseHeaderSize) + ((index + fixedFieldsSqInt) - 1)))) << 3) | 1); + goto l8; + } + if (fmtUsqLong >= (firstShortFormat())) { + value = (((usqInt)(((unsigned short) (shortAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 1)))))))) << 3) | 1); + goto l8; + } + if (fmtUsqLong == (sixtyFourBitIndexableFormat())) { + value = positive64BitIntegerFor(long64At((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 3)))))); + goto l8; + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + value = (((usqInt)((((usqInt)(long32At((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + goto l8; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmtUsqLong <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + value = 0; + /* end stObject:at: */ +l8: + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + } + return; + } + + /* might be an instance of a subclass */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext))))) { + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(aContext + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aContext - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l4; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l4; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l4; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l4; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l4: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l3; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l3; + } + class = fetchClassOfNonImm(aContext); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l3: + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l1; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + stSize = (spSqInt >> 3); + /* end fetchStackPointerOf: */ +l1: + if (!(((index >= 1) && (index <= stSize)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* begin subscript:with:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + value = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << (shiftForWord())))))); + goto l2; + } + if (fmt >= (firstByteFormat())) { + value = (((usqInt)(byteAt((void *)((aContext + BaseHeaderSize) + ((index + fixedFields) - 1)))) << 3) | 1); + goto l2; + } + if (fmt >= (firstShortFormat())) { + value = (((usqInt)(((unsigned short) (shortAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))))))) << 3) | 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + value = positive64BitIntegerFor(long64At((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))))); + goto l2; + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + value = (((usqInt)((((usqInt)(long32At((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + /* end subscript:with:format: */ +l2: + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + return; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!(((index >= 1) && (index <= (stackPointerIndexForFrame(spouseFP)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + value = /* temporary:in: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? ((index - 1) < ((frameNumArgs = ((mframeCogMethod(spouseFP))->cmNumArgs))) + ? longAt((spouseFP + FoxCallerSavedIP) + ((frameNumArgs - (index - 1)) * BytesPerWord)) + : longAt(((spouseFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs - (index - 1)) * BytesPerWord))) + : /* itemporary:in: */ + ((index - 1) < ((frameNumArgsUsqInt = byteAt((spouseFP + FoxIFrameFlags) + 1))) + ? longAt((spouseFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt - (index - 1)) * BytesPerWord)) + : longAt(((spouseFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt - (index - 1)) * BytesPerWord)))); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); +} + + +/* Special version of primitiveAtPut for accessing contexts. + Written to be varargs for use from mirror primitives. */ + + /* StackInterpreterPrimitives>>#primitiveContextAtPut */ +static void +primitiveContextAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aContext; + sqInt class; + sqInt fieldIndex; + sqInt fixedFields; + sqInt fixedFieldsSqInt; + usqLong fmt; + usqLong fmtUsqLong; + usqInt frameNumArgs; + unsigned char frameNumArgs1; + sqLong hdr; + sqLong hdrSqLong; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt senderOop; + sqInt signedValueToStore; + sqInt signedValueToStoreSqInt; + sqInt sp; + char *spouseFP; + sqInt spSqInt; + sqInt stSize; + sqInt stSizeSqInt; + sqInt totalLength; + sqInt totalLengthSqInt; + usqLong unsigned64BitValueToStore; + unsigned int unsignedValueToStore; + sqInt value; + + value = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aContext = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* Duplicating much of stObject:at:put: here allows stObject:at:put: to omit tests for contexts. */ + hdr = long64At((void *)(aContext)); + index = (index >> 3); + if (!((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stObject:at:put: */ + hdrSqLong = long64At((void *)(aContext)); + fmtUsqLong = (((usqLong)(hdrSqLong)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(aContext + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aContext - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmtUsqLong <= (ephemeronFormat())) { + totalLengthSqInt = numSlots; + goto l6; + } + if (fmtUsqLong >= (firstByteFormat())) { + totalLengthSqInt = ((numSlots << (shiftForWord()))) - (fmtUsqLong & 7); + goto l6; + } + + /* bytes, including CompiledMethod */ + if (fmtUsqLong >= (firstShortFormat())) { + totalLengthSqInt = ((numSlots << ((shiftForWord()) - 1))) - (fmtUsqLong & 3); + goto l6; + } + if (fmtUsqLong >= (firstLongFormat())) { + totalLengthSqInt = ((numSlots << ((shiftForWord()) - 2))) - (fmtUsqLong & 1); + goto l6; + } + if (fmtUsqLong == (sixtyFourBitIndexableFormat())) { + totalLengthSqInt = numSlots; + goto l6; + } + + /* fmt = self forwardedFormat */ + totalLengthSqInt = 0; + /* end lengthOf:baseHeader:format: */ +l6: + if ((fmtUsqLong == (indexablePointersFormat())) + && ((hdrSqLong & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(aContext)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(aContext))); + stSizeSqInt = sp; + goto l8; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSizeSqInt = 0; + goto l8; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + stSizeSqInt = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l8: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSizeSqInt)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))))) { + temporaryinput(index - 1, frameOfMarriedContext(aContext), value); + goto l9; + } + } + if (fmtUsqLong >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(aContext)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLengthSqInt))) { + if (((((value) & 7) == 1)) + && ((((usqInt)((signedValueToStore = (value >> 3))))) <= 0xFF)) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((aContext + BaseHeaderSize) + (index - 1)),signedValueToStore); + goto l9; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l9; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmtUsqLong >= (sixtyFourBitIndexableFormat())) + || (fmtUsqLong == (arrayFormat()))) { + fixedFieldsSqInt = 0; + goto l5; + } + if (fmtUsqLong < (arrayFormat())) { + fixedFieldsSqInt = totalLengthSqInt; + goto l5; + } + class = fetchClassOfNonImm(aContext); + fixedFieldsSqInt = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l5: + stSizeSqInt = totalLengthSqInt - fixedFieldsSqInt; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSizeSqInt))) { + /* begin subscript:with:storing:format: */ + if (fmtUsqLong <= 5 /* lastPointerFormat */) { + fieldIndex = (index + fixedFieldsSqInt) - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, aContext, value)); + assert(isNonImmediate(aContext)); + if (oopisGreaterThanOrEqualTo(aContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),value); + goto l7; + } + if (fmtUsqLong >= (firstByteFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l7; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l7; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((aContext + BaseHeaderSize) + ((index + fixedFieldsSqInt) - 1)),signedValueToStoreSqInt); + goto l7; + } + if (fmtUsqLong >= (firstShortFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l7; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l7; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 1)))),signedValueToStoreSqInt); + goto l7; + } + if (fmtUsqLong == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l7; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l7: + goto l9; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmtUsqLong <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + /* end stObject:at:put: */ +l9: + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + } + return; + } + + /* might be an instance of a subclass */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext))))) { + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(aContext + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aContext - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l3; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l3; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l3; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l3; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l3; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l3: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l2; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l2; + } + class = fetchClassOfNonImm(aContext); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l2: + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l1; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + stSize = (spSqInt >> 3); + /* end fetchStackPointerOf: */ +l1: + if (!(((index >= 1) && (index <= stSize)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* begin subscript:with:storing:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + fieldIndex = (index + fixedFields) - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, aContext, value)); + assert(isNonImmediate(aContext)); + if (oopisGreaterThanOrEqualTo(aContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),value); + goto l4; + } + if (fmt >= (firstByteFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStore = (value >> 3); + if (!((signedValueToStore >= 0) + && (signedValueToStore <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((aContext + BaseHeaderSize) + ((index + fixedFields) - 1)),signedValueToStore); + goto l4; + } + if (fmt >= (firstShortFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStore = (value >> 3); + if (!((signedValueToStore >= 0) + && (signedValueToStore <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))),signedValueToStore); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l4; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l4: + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + return; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!(((index >= 1) && (index <= (stackPointerIndexForFrame(spouseFP)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* temporary:in:put: */ + if ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory())) { + /* mtemporary:in:put: */ + longAtput(((index - 1) < ((frameNumArgs1 = ((mframeCogMethod(spouseFP))->cmNumArgs))) + ? (spouseFP + FoxCallerSavedIP) + ((frameNumArgs1 - (index - 1)) * BytesPerWord) + : ((spouseFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs1 - (index - 1)) * BytesPerWord)),value); + } + else { + /* itemporary:in:put: */ + if ((index - 1) < ((frameNumArgs = byteAt((spouseFP + FoxIFrameFlags) + 1)))) { + longAtput((spouseFP + FoxCallerSavedIP) + ((frameNumArgs - (index - 1)) * BytesPerWord),value); + } + else { + longAtput(((spouseFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - (index - 1)) * BytesPerWord),value); + } + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); +} + + +/* The core design of the opensmalltalk-vm's closure implementation has as + its rationale + breaking the dependence of a block on the temporaries of any outer + context, principally + the home context. The scheme is explained here: + + http://www.mirandabanda.org/cogblog/2009/01/14/under-cover-contexts-and-the-big-frame-up + Hence when a frame is married the VM *does not* copy temporary variables + into the context + object, copying only arguments (if any). See marryFrame:SP: and + genGetActiveContextLarge:inBlock:. Note that if a married frame and + context are divorced (which occurs when an inst var of a context is + assigned to or when the stack page on which the frame resides is reused + for newer frames) then + the full frame contents are written to the context, bringing it fully + up-to-date so that it becomes + the implementation of the relevant method/block activation, replacing the + frame. + Except in extraordinary circumstances there is no utility in accessing the + temporary variables of a + widowed (returned-from) context. The frame and context are married as soon + as thisContext is + referenced (either explicitly, or by creating a closure, or accessing the + sender of another context). + At this point there is no guarantee that temp vars have their final value, + and so there is no point + preserving them because their values at the time of marriage will very + likely be stale by the time + the context is widowed. N.B. One could imagine having the VM write the + values of frame + temporaries back to the context if it existed, but such a scheme would be + unacceptably slow. + + While I (eem 4/23/2026) have yet to see an example where temps should be + preserved, this + mechanism is provided just in case. It must be invoked after the last + modification of a temporary. + For example, the assignments to the completionStatus temps in + BlockClosure>>#ensure: and + BlockClosure>>#ifCurtailed: (as of 11/18/2025) are accessed from *while + the contexts are still active* + from Context>>#finishIncompleteUnwindsUpTo: and hence if married the VM + will access the temps + from the frame, not the context, and hence up-to-date values will be + answered. + There is a complication. Only methods and full blocks have an easily + accessible temporary count. + The original SqueakV3PlusClosures BlockClosure uses explicit pushNil + bytecodes to establish + block-local temps, and machinery such as + StackToRegisterMappingCogit>>#scanBlock: and + BlockLocalTempCounter to compute the number of temps. Hence this primitive + is actually two, + one that takes no arguments, accesses the temp count from the receiver's + method if possible, + failing if not with the error code PrimErrUnsupported, and one that takes + the temp count as its + argument. */ + + /* StackInterpreterPrimitives>>#primitiveContextPreserveTemps */ +static void +primitiveContextPreserveTemps(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt closureRcvr; + sqInt header; + sqInt methodPointer; + usqInt numArgs; + sqInt numNonArgTemps; + usqInt numTemps; + sqInt receiverCtxt; + sqInt senderOop; + char *theFP; + char *theSP; + + receiverCtxt = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + assert(isContext(receiverCtxt)); + + /* this is required for isWidowedContext: et al to function... */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* If the receiver is a single (heap) context then temps are already preserved. In normal use the + receiver would be married (e.g. as in executing thisContext preserveTemps where it would be + the current frame. But the execution simulation machinery could send it to a heap context. */ + if (!(((((longAt((void *)((receiverCtxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return; + } + + /* If the receiver is widowed then it's too late... */ + if (isWidowedContext(receiverCtxt)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + return; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((receiverCtxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + numArgs = /* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)); + if (GIV(argumentCount)) { + assert((methodArgumentCount()) == 1); + numTemps = longAt(GIV(stackPointer)); + if (!(((((numTemps) & 7) == 1)) + && (((((numTemps = (numTemps >> 3))) >= 0) && (numTemps <= ((LargeContextSlots - ReceiverIndex) - 1)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + else { + /* is this a normal method activation and/or a full block closure activation or not? + If not, fail and allow the image to compute the temp count. */ + if (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0)) { + closureRcvr = longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((numArgs << (shiftForWord()))))); + + /* begin isVanillaBlockClosure: */ + assert((isPointers(closureRcvr)) + && ((numSlotsOf(closureRcvr)) >= ClosureFirstCopiedValueIndex)); + if (((((longAt((void *)((closureRcvr + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + } + + /* N.B. the temp count includes the argument count */ + methodPointer = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + + /* begin tempCountOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(header)) >> MethodHeaderTempCountShift) & 0x3F; + } + numNonArgTemps = numTemps - numArgs; + theSP = (/* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver)) - (numNonArgTemps * BytesPerWord); + updateStateOfSpouseContextForFrameWithSP(theFP, theSP); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* Special version of primitiveSize for accessing contexts. + Written to be varargs for use from mirror primitives. */ + + /* StackInterpreterPrimitives>>#primitiveContextSize */ +static void +primitiveContextSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt sp1; + sqInt spSqInt; + sqInt sz; + sqInt totalLength; + + rcvr = longAt(GIV(stackPointer)); + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + if ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + sp1 = stackPointerIndexForFrame(frameOfMarriedContext(rcvr)); + assert((ReceiverIndex + ((sp1 >> 3))) < (lengthOf(rcvr))); + sz = sp1; + goto l3; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + sz = 0; + goto l3; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(rcvr))); + sz = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l3:; + } + else { + sz = totalLength - fixedFields; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)sz << 3) | 1)); +} + + +/* Set or clear the flag that controls whether modifications of + the Display object are propagated to the underlying + platform's screen. */ + + /* StackInterpreterPrimitives>>#primitiveDeferDisplayUpdates */ +static void +primitiveDeferDisplayUpdates(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt flag; + + if (cannotDeferDisplayUpdates) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + flag = longAt(GIV(stackPointer)); + if (flag == GIV(trueObj)) { + deferDisplayUpdates = 1; + } + else { + if (flag == GIV(falseObj)) { + deferDisplayUpdates = 0; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; +} + + +/* Simulate an primitiveExternalCall invocation (e.g. for the Debugger). Do + not cache anything. + e.g. ContextPart>>tryNamedPrimitiveIn: aCompiledMethod for: aReceiver + withArgs: arguments */ + + /* StackInterpreterPrimitives>>#primitiveDoNamedPrimitiveWithArgs */ +static void +primitiveDoNamedPrimitiveWithArgs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*addr)(void); + sqInt argumentArray; + usqInt arraySize; + sqInt ccIndex; + usqInt firstBytecode; + sqInt fmt; + sqInt functionLength; + sqInt functionName; + sqInt header; + sqInt index; + sqInt isArray; + sqInt methodArg; + sqInt methodHeader; + sqInt moduleLength; + sqInt moduleName; + usqInt newObj; + usqInt numBytes; + usqInt numSlots; + sqInt primRcvr; + sqInt spec; + sqInt successBoolean; + sqInt top; + sqInt valuePointer; + + GIV(metaAccessorDepth) = -2; + + /* See checkForAndFollowForwardedPrimitiveState */ + argumentArray = longAt(GIV(stackPointer)); + methodArg = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (!((/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (/* isOopCompiledMethod: */ + ((!(methodArg & (tagMask())))) + && (((byteAt((void *)(methodArg + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -2; + return; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (!(arraySize <= (LargeContextSlots - CtxtTempFrameStart))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -2; + return; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodArg)); + header = longAt((void *)((methodArg + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (!(((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) > 2)) { + /* primitiveFailFor: */ + GIV(primFailCode) = -3; + return; + } + spec = longAt((void *)((methodArg + BaseHeaderSize) + (1U << (shiftForWord())))); + + /* first literal */ + + /* begin isInstanceOfClassArray: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((spec & (tagMask())) != 0)) { + isArray = 0; + goto l2; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(spec))); + ccIndex = (longAt((void *)(spec))) & (classIndexMask()); + isArray = ClassArrayCompactIndex == ccIndex; + /* end isInstanceOfClassArray: */ +l2: + if (!(isArray + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(spec)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(spec + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(spec - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4) + && ((/* primitiveIndexOfMethod:header: */ + (((methodHeader & AlternateHeaderHasPrimFlag) != 0) + ? ((firstBytecode = (methodArg + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize), + (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8)))) + : 0)) == PrimNumberExternalCall)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -3; + return; + } + if (!(((((usqInt)(methodHeader)) >> MethodHeaderArgCountShift) & 15) == arraySize)) { + /* primitiveFailFor: */ + GIV(primFailCode) = -2; + return; + } + moduleName = longAt((void *)((spec + BaseHeaderSize) + (0U << (shiftForWord())))); + if (moduleName == GIV(nilObj)) { + moduleLength = 0; + } + else { + successBoolean = /* isBytes: */ + ((!(moduleName & (tagMask())))) + && (((byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())); + + /* begin success: */ + if (!successBoolean) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + moduleLength = ((((/* begin numSlotsOf: */ + assert((classIndexOf(moduleName)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(moduleName + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(moduleName - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + } + functionName = longAt((void *)((spec + BaseHeaderSize) + (1U << (shiftForWord())))); + successBoolean = /* isBytes: */ + ((!(functionName & (tagMask())))) + && (((byteAt((void *)(functionName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())); + + /* begin success: */ + if (!successBoolean) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(functionName + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + functionLength = ((((/* begin numSlotsOf: */ + assert((classIndexOf(functionName)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(functionName + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(functionName - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = -3; + return; + } + addr = ioLoadExternalFunctionOfLengthFromModuleOfLengthMetadataInto(functionName + BaseHeaderSize, functionLength, moduleName + BaseHeaderSize, moduleLength, (&GIV(metaAccessorDepth))); + + /* N.B. the accessor depth is the second byte of the primitive's metadata; + the first byte is various flags (currently l.s.b. = use fast C linkage). */ + GIV(metaAccessorDepth) = (addr + ? (((usqInt)(GIV(metaAccessorDepth))) >> 8) + : -2); + if (!addr) { + /* primitiveFailFor: */ + GIV(primFailCode) = -1; + return; + } + + /* begin eeInstantiateClassIndex:format:numSlots: */ + assert((knownClassAtIndex(ClassArrayCompactIndex)) != GIV(nilObj)); + assert((arrayFormat()) == (instSpecOfClass(knownClassAtIndex(ClassArrayCompactIndex)))); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (4 * BytesPerOop); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateNewSpaceSlots:format:classIndex:"); + GIV(tempOop) = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) 4)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + GIV(tempOop) = newObj; + /* end eeInstantiateClassIndex:format:numSlots: */ +l1: + + /* begin popStack */ + valuePointer = (argumentArray = longAt(GIV(stackPointer))); + GIV(stackPointer) += BytesPerWord; + valuePointer = (argumentArray); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(tempOop))) + && (!(isForwarded(GIV(tempOop))))); + assert(validStorePointerUncheckedArgs(0, GIV(tempOop), valuePointer)); + longAtput((void *)((GIV(tempOop) + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin popStack */ + valuePointer = (primRcvr = longAt(GIV(stackPointer))); + GIV(stackPointer) += BytesPerWord; + valuePointer = (primRcvr); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(tempOop))) + && (!(isForwarded(GIV(tempOop))))); + assert(validStorePointerUncheckedArgs(1, GIV(tempOop), valuePointer)); + longAtput((void *)((GIV(tempOop) + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + valuePointer = top; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(tempOop))) + && (!(isForwarded(GIV(tempOop))))); + assert(validStorePointerUncheckedArgs(2, GIV(tempOop), valuePointer)); + longAtput((void *)((GIV(tempOop) + BaseHeaderSize) + (2U << (shiftForWord()))),valuePointer); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + valuePointer = top; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(tempOop))) + && (!(isForwarded(GIV(tempOop))))); + assert(validStorePointerUncheckedArgs(3, GIV(tempOop), valuePointer)); + longAtput((void *)((GIV(tempOop) + BaseHeaderSize) + (3U << (shiftForWord()))),valuePointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),primRcvr); + GIV(argumentCount) = arraySize; + for (index = 1; index <= arraySize; index += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + } + primitiveFunctionPointer = addr; + dispatchFunctionPointer(addr); + if (GIV(primFailCode)) { + /* begin pop: */ + GIV(stackPointer) += (arraySize + 1) * BytesPerWord; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((GIV(tempOop) + BaseHeaderSize) + (3U << (shiftForWord()))))); + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((GIV(tempOop) + BaseHeaderSize) + (2U << (shiftForWord()))))); + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((GIV(tempOop) + BaseHeaderSize) + (1U << (shiftForWord()))))); + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((GIV(tempOop) + BaseHeaderSize) + (0U << (shiftForWord()))))); + GIV(argumentCount) = 3; + + /* Must reset primitiveFunctionPointer for checkForAndFollowForwardedPrimitiveState */ + primitiveFunctionPointer = primitiveDoNamedPrimitiveWithArgs; + + /* Hack. A nil prim error code (primErrorCode = 1) is interpreted by the image + as meaning this primitive is not implemented. So to pass back nil as an error + code we use -1 to indicate generic failure. */ + if (GIV(primFailCode) == 1) { + GIV(primFailCode) = -1; + } + } +} + + +/* Implement either ProtoObject>>tryPrimitive: primIndex withArgs: argArray + or Context>>receiver: anObject tryPrimitive: primIndex withArgs: argArray. + If this primitive fails, arrange that its error code is a negative + integer, to + distinguish between this failing and the primitive it invokes failing. */ + + /* StackInterpreterPrimitives>>#primitiveDoPrimitiveWithArgs */ +static void +primitiveDoPrimitiveWithArgs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt argumentArray; + usqInt arraySize; + sqInt index; + usqInt numSlots; + sqInt primIdx; + sqInt rcvr; + sqInt referent; + sqInt savedNumArgs; + + GIV(metaAccessorDepth) = -2; + + /* See checkForAndFollowForwardedPrimitiveState */ + if (!(((GIV(argumentCount) >= 2) && (GIV(argumentCount) <= 3)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -PrimErrUnsupported; + return; + } + argumentArray = longAt(GIV(stackPointer)); + primIdx = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((((primIdx) & 7) == 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -PrimErrBadArgument; + return; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (!(arraySize <= (LargeContextSlots - CtxtTempFrameStart))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -PrimErrLimitExceeded; + return; + } + primIdx = (primIdx >> 3); + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primIdx)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primIdx]))); + if (!primitiveFunctionPointer) { + primitiveFunctionPointer = primitiveDoPrimitiveWithArgs; + + /* primitiveFailFor: */ + GIV(primFailCode) = -PrimErrBadIndex; + return; + } + + /* Pop primIndex and argArray, then push args in place... */ + if (((savedNumArgs = GIV(argumentCount))) == 3) { + /* actual receiver */ + GIV(tempOop2) = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + + /* receiver for primitive */ + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(rcvr)); + referent = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + rcvr = referent; + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 3 * BytesPerWord),rcvr); + } + else { + /* begin pop: */ + GIV(stackPointer) += 2 * BytesPerWord; + } + + /* ...and receiver if the three arg form */ + GIV(argumentCount) = arraySize; + index = 1; + while (index <= arraySize) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + index += 1; + } + if ((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex) { + externalQuickPrimitiveResponse(); + GIV(tempOop2) = 0; + return; + } + + /* We use tempOop instead of pushRemappableOop:/popRemappableOop here because in + the Cogit primitiveEnterCriticalSection, primitiveSignal, primitiveResume et al longjmp back + to either the interpreter or machine code, depending on the process activated. So if we're + executing one of these primitives, control won't actually return here and the matching + popRemappableOop: wouldn't occur, potentially overflowing the remap buffer. + Note that while recursion could occur (nil tryPrimitive: 118 withArgs: #(118 #(110 #()))) + it counts as shooting oneself in the foot. */ + + /* prim might alloc/gc */ + /* Run the primitive (sets primFailCode) */ + GIV(tempOop) = argumentArray; + GIV(metaAccessorDepth) = ((sqInt)((primitiveMetadataTable[primIdx]))) >> SpurPrimitiveAccessorDepthShift; + + /* See checkForAndFollowForwardedPrimitiveState */ + if (!(slowPrimitiveResponse())) { + if (!GIV(tempOop)) { + return; + } + + /* the primitive failed in a recursive invocation. can't fix things with no value... */ + + /* begin pop: */ + GIV(stackPointer) += arraySize * BytesPerWord; + if (savedNumArgs == 3) { + rcvr = longAt(GIV(stackPointer)); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(tempOop2)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + } + longAtput((GIV(stackPointer) -= BytesPerWord),(((usqInt)primIdx << 3) | 1)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(tempOop)); + primitiveFunctionPointer = primitiveDoPrimitiveWithArgs; + GIV(argumentCount) = savedNumArgs; + } + + /* If primitive failed, then restore state for failure code */ + GIV(tempOop) = (GIV(tempOop2) = 0); +} + + +/* Call an external primitive. External primitive methods first literals are + an array of + * The module name (String | Symbol) + * The function name (String | Symbol) + * The session ID (SmallInteger) [OBSOLETE], or in Spur, the metadata + (accessorDepth and flags; Integer)) + * The function index (Integer) in the externalPrimitiveTable + For fast interpreter dispatch in subsequent invocations the + primitiveFunctionPointer in the method cache is rewritten, either to the + function itself, or to zero if the external + function is not found. This allows for fast responses as long as the + method stays in + the cache. The cache rewrite relies on lastMethodCacheProbeWrite which is + set in + addNewMethodToCache:. + Now that the VM flushes function addresses from its tables, the session ID + is obsolete, + but it is kept for backward compatibility. Also, a failed lookup is + reported specially. If a + method has been looked up and not been found, the function address is + stored as -1 + (i.e., the SmallInteger -1 to distinguish from 16rFFFFFFFF which may be + returned from + lookup), and the primitive fails with PrimErrNotFound. + */ +/* because the primitive accesses newMethod's first literal, which is checked + for explicitly in checkForAndFollowForwardedPrimitiveState + */ +/* since call may invoke a callback */ +/* since external primitive linkage is in first literal... */ +/* Check for it being a method for primitiveDoPrimitiveWithArgs. + Fetch the first literal of the method; check its an Array of length 4. + Look at the function index in case it has been loaded before */ + + /* StackInterpreterPrimitives>>#primitiveExternalCall */ +static void +primitiveExternalCall(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*addr)(void); + sqInt index; + sqInt lit; + usqInt numSlots; + sqInt reasonCode; + + if (!((/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((literalCountOf(GIV(newMethod))) > 0) + && (((lit = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + (1U << (shiftForWord()))))), + (/* isArray: */ + ((!(lit & (tagMask())))) + && (((byteAt((void *)(lit + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(lit)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(lit + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(lit - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4) + && (((index = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord()))))))), + (((index) & 7) == 1))))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadMethod; + return; + } + index = (index >> 3); + + /* Check if we have already looked up the function and failed. */ + if (index < 0) { + /* Function address was not found in this session, + Void the primitive function. */ + rewriteMethodCacheEntryForExternalPrimitiveToFunction(0); + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNotFound; + return; + } + + /* Try to call the function directly */ + if ((index > 0) + && (index <= MaxExternalPrimitiveTableSize)) { + addr = externalPrimitiveTable[index - 1]; + if (addr) { + rewriteMethodCacheEntryForExternalPrimitiveToFunction(addr); + + /* begin callExternalPrimitive: */ + primitiveFunctionPointer = addr; + dispatchFunctionPointer(addr); + + /* begin maybeRetryPrimitiveOnFailure */ + if (GIV(primFailCode)) { + retryPrimitiveOnFailure(); + } + return; + } + + /* if we get here, then an index to the external prim was + kept on the ST side although the underlying prim + table was already flushed */ + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNamedInternal; + return; + } + + /* Clean up session id/metadata and external primitive index */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(lit)) + && (!(isForwarded(lit)))); + assert(validStorePointerUncheckedArgs(ExternalCallLiteralFlagsIndex, lit, ConstZero)); + longAtput((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFlagsIndex) << (shiftForWord()))))),ConstZero); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(lit)) + && (!(isForwarded(lit)))); + assert(validStorePointerUncheckedArgs(ExternalCallLiteralTargetFunctionIndex, lit, ConstZero)); + longAtput((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord()))))),ConstZero); + + /* The function has not been loaded yet. Attempt to link it, cache it, and call it. */ + addr = linkExternalCallerrInto(lit, (&GIV(primFailCode))); + if (!addr) { + assert((fetchPointerofObject(ExternalCallLiteralFlagsIndex, lit)) == ConstZero); + reasonCode = (GIV(primFailCode) + ? GIV(primFailCode) + : PrimErrNotFound); + GIV(primFailCode) = reasonCode; + return; + } + + /* begin callExternalPrimitive: */ + primitiveFunctionPointer = addr; + dispatchFunctionPointer(addr); + + /* begin maybeRetryPrimitiveOnFailure */ + if (GIV(primFailCode)) { + retryPrimitiveOnFailure(); + } +} + + +/* Primitive. Search up the context stack for the next method context marked + for exception handling starting at the receiver. Return nil if none found */ + + /* StackInterpreterPrimitives>>#primitiveFindHandlerContext */ +static void +primitiveFindHandlerContext(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt handlerOrNilOrZero; + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + handlerOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(PrimNumberHandlerMarker, longAt(GIV(stackPointer)), GIV(nilObj)); + if (!handlerOrNilOrZero) { + handlerOrNilOrZero = GIV(nilObj); + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),handlerOrNilOrZero); +} + + +/* Primitive. Search up the context stack for the next method context marked + for unwind + handling from the receiver up to but not including the argument. Return + nil if none found. + */ + + /* StackInterpreterPrimitives>>#primitiveFindNextUnwindContext */ +static void +primitiveFindNextUnwindContext(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt calleeContext; + sqInt handlerOrNilOrZero; + sqInt senderContext; + sqInt senderOop; + sqInt startContext; + sqInt stopContext; + char *theFP; + char *theFP1; + char *theFPAbove; + sqInt theMethod; + StackPage *thePage; + char *theSP; + + stopContext = longAt(GIV(stackPointer)); + calleeContext = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((stopContext == GIV(nilObj)) + || (/* isContext: */ + ((!(stopContext & (tagMask())))) + && (((longAt((void *)(stopContext))) & (classIndexMask())) == ClassMethodContextCompactIndex)))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* The following should never be true, but developing full blocks, early in September + 2016 we were seeing invalid invocations of this primitive.. Hence the assert: */ + assert(stopContext != calleeContext); + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((calleeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(calleeContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((calleeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (longAt(theFP + FoxSavedFP)) { + /* begin findMethodWithPrimitive:FromFP:UpToContext: */ + theFP1 = ((char *)(longAt(theFP + FoxSavedFP))); + theFPAbove = ((char *)(longAt(theFP + FoxSavedFP))); + do { + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP1 + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP1 + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP1 + FoxIFrameFlags) + 2)) != 0)) + && (stopContext == (longAt(theFP1 + FoxThisContext)))) { + handlerOrNilOrZero = 0; + goto l1; + } + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP1 + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP1 + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP1 + FoxIFrameFlags) + 3)) != 0))) { + theMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP1 + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP1))->methodObject) + : longAt(theFP1 + FoxMethod)); + if ((primitiveIndexOf(theMethod)) == PrimNumberUnwindMarker) { + if (theFP1 == theFPAbove) { + theSP = findSPOfon(theFP1, stackPageFor(theFP1)); + } + else { + /* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFPAbove))); + theSP = (theFPAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFPAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFPAbove))->cmNumArgs) + : byteAt((theFPAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP1 + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP1 + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP1 + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP1))); + handlerOrNilOrZero = longAt(theFP1 + FoxThisContext); + goto l1; + } + handlerOrNilOrZero = marryFrameSP(theFP1, theSP); + goto l1; + } + } + theFPAbove = theFP1; + theFP1 = ((char *)(longAt(theFP1 + FoxSavedFP))); + } while(theFP1 != 0); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFPAbove)); + thePage = stackPageAtpages(pageIndexFor(theFPAbove), GIV(pages)); + senderContext = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(senderContext)); + assert((senderContext == (nilObject())) + || (isContext(followMaybeForwarded(senderContext)))); + if (!(/* isContext: */ + ((!(senderContext & (tagMask())))) + && (((longAt((void *)(senderContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + handlerOrNilOrZero = GIV(nilObj); + goto l1; + } + handlerOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(PrimNumberUnwindMarker, senderContext, stopContext); + /* end findMethodWithPrimitive:FromFP:UpToContext: */ +l1:; + } + else { + handlerOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(PrimNumberUnwindMarker, frameCallerContext(theFP), stopContext); + } + } + else { + startContext = longAt((void *)((calleeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + if (/* isContext: */ + ((!(startContext & (tagMask())))) + && (((longAt((void *)(startContext))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + handlerOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(PrimNumberUnwindMarker, startContext, stopContext); + } + else { + handlerOrNilOrZero = 0; + } + } + if (!handlerOrNilOrZero) { + handlerOrNilOrZero = GIV(nilObj); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),handlerOrNilOrZero); +} + + +/* Do a full garbage collection. In SqueakV3ObjectMemory, answer the number + of bytes available (including swap space if dynamic memory management is + supported). In Spur, answer the size of the largest free chunk. */ +/* Do a full garbage collection. In SqueakV3ObjectMemory, answer the number + of bytes available (including swap space if dynamic memory management is + supported). In Spur, answer the size of the largest free chunk. */ + + /* StackInterpreterPrimitives>>#primitiveFullGC */ +static void +primitiveFullGC(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integer; + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + integer = fullGC(); + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); +} + + /* StackInterpreterPrimitives>>#primitiveGetSetFFIExceptionHandling */ +static void +primitiveGetSetFFIExceptionHandling(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt integer; + + if (!GIV(argumentCount)) { + integer = (ioCanCatchFFIExceptions() + ? ffiExceptionResponse + : -1); + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); + return; + } + if (!(ioCanCatchFFIExceptions())) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + if (!(GIV(argumentCount) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + arg = longAt(GIV(stackPointer)); + if (!(((((arg) & 7) == 1)) + && (((((arg = (arg >> 3))) >= -1) && (arg <= 1))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + ffiExceptionResponse = arg; + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* Do a quick, incremental garbage collection and return the number of bytes + immediately available. + (Note: more space may be made available by doing a full garbage + collection. + */ +/* Do a quick, incremental garbage collection and return the number of bytes + immediately available. + (Note: more space may be made available by doing a full garbage + collection. + */ + + /* StackInterpreterPrimitives>>#primitiveIncrementalGC */ +static void +primitiveIncrementalGC(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerVal; + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* begin scavengingGC */ + scavengingGCTenuringIf(TenureByAge); + integerVal = ((((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes())) < 0) ? 0 : ((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes()))); + + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),(((usqInt)integerVal << 3) | 1)); +} + + +/* because of externalInstVar:ofContext: below */ + + /* StackInterpreterPrimitives>>#primitiveInstVarAt */ +static void +primitiveInstVarAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt totalLength; + sqInt value; + + index = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 1) + && (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + return; + } + index = (index >> 3); + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l3; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l3; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l3; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l3; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l3; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l3: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l2; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l2; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l2: + if (!((index >= 1) + && (index <= fixedFields))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + value = externalInstVarofContext(index - 1, rcvr); + } + else { + /* begin subscript:with:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + value = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord())))))); + goto l1; + } + if (fmt >= (firstByteFormat())) { + value = (((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + (index - 1)))) << 3) | 1); + goto l1; + } + if (fmt >= (firstShortFormat())) { + value = (((usqInt)(((unsigned short) (shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 1)))))))) << 3) | 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + value = positive64BitIntegerFor(long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 3)))))); + goto l1; + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + value = (((usqInt)((((usqInt)(long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + /* end subscript:with:format: */ +l1:; + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); +} + + /* StackInterpreterPrimitives>>#primitiveInstVarAtPut */ +static void +primitiveInstVarAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fieldIndex; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + sqInt newValue; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt signedValueToStore; + sqInt totalLength; + usqLong unsigned64BitValueToStore; + unsigned int unsignedValueToStore; + + newValue = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 2) + && (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + return; + } + index = (index >> 3); + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + if (!((index >= 1) + && (index <= fixedFields))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + externalInstVarofContextput(index - 1, rcvr, newValue); + } + else { + /* begin subscript:with:storing:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + fieldIndex = index - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, rcvr, newValue)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(newValue & (tagMask())))) + && (oopisLessThan(newValue, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),newValue); + goto l3; + } + if (fmt >= (firstByteFormat())) { + if (!((((newValue) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l3; + } + signedValueToStore = (newValue >> 3); + if (!((signedValueToStore >= 0) + && (signedValueToStore <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l3; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + (index - 1)),signedValueToStore); + goto l3; + } + if (fmt >= (firstShortFormat())) { + if (!((((newValue) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l3; + } + signedValueToStore = (newValue >> 3); + if (!((signedValueToStore >= 0) + && (signedValueToStore <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l3; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 1)))),signedValueToStore); + goto l3; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(newValue); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l3; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(newValue); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l3:; + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); +} + + +/* Primitive. 'Invoke' an object like a function, sending the special message + run: originalSelector with: arguments in: aReceiver. + */ + + /* StackInterpreterPrimitives>>#primitiveInvokeObjectAsMethod */ +static void +primitiveInvokeObjectAsMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt lookupClassTag; + usqInt newObj; + usqInt numBytes; + sqInt runArgs; + sqInt runReceiver; + sqInt tagBits; + sqInt top; + sqInt valuePointer; + + /* begin eeInstantiateClassIndex:format:numSlots: */ + assert((GIV(argumentCount) >= 0) + && ((knownClassAtIndex(ClassArrayCompactIndex)) != GIV(nilObj))); + assert((arrayFormat()) == (instSpecOfClass(knownClassAtIndex(ClassArrayCompactIndex)))); + + /* begin allocateNewSpaceSlots:format:classIndex: */ + if (GIV(argumentCount) >= (numSlotsMask())) { + if (GIV(argumentCount) > 0xFFFFFFFFU) { + runArgs = ((usqInt) null); + goto l1; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (GIV(argumentCount) * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((GIV(argumentCount) < 1 + ? 8 /* allocationUnit */ + : GIV(argumentCount) * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateNewSpaceSlots:format:classIndex:"); + runArgs = 0; + goto l1; + } + } + if (GIV(argumentCount) >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),GIV(argumentCount)); + longAtput((void *)(GIV(freeStart) + 4),((sqInt)((usqInt)((numSlotsMask())) << (numSlotsHalfShift())))); + long64Atput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + } + else { + long64Atput((void *)(newObj),((((((usqLong) GIV(argumentCount))) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + runArgs = newObj; + /* end eeInstantiateClassIndex:format:numSlots: */ +l1: + for (i = (GIV(argumentCount) - 1); i >= 0; i += -1) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + valuePointer = top; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(runArgs)) + && (!(isForwarded(runArgs)))); + assert(validStorePointerUncheckedArgs(i, runArgs, valuePointer)); + longAtput((void *)((runArgs + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + + /* begin popStack */ + runReceiver = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* setup send of newMethod run: originalSelector with: runArgs in: runReceiver */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(messageSelector)); + + /* original selector */ + longAtput((GIV(stackPointer) -= BytesPerWord),runArgs); + longAtput((GIV(stackPointer) -= BytesPerWord),runReceiver); + + /* stack is clean here */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorRunWithIn) << (shiftForWord())))))); + GIV(argumentCount) = 3; + lookupClassTag = /* fetchClassTagOf: */ + ((tagBits = GIV(newMethod) & (tagMask())) + ? tagBits + : (longAt((void *)(GIV(newMethod)))) & (classIndexMask())); + findNewMethodInClassTag(lookupClassTag); + executeNewMethod(); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; +} + + +/* Primitive. Answer an Array with the current long-running primitive method + identified by + the heartbeat, the minimum number of milliseconds it was active for, and + the milliseconds + of GC activity there-in, or nil if none. */ + + /* StackInterpreterPrimitives>>#primitiveLongRunningPrimitive */ +#if LRPCheck +EXPORT(void) +primitiveLongRunningPrimitive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong gcms; + sqInt lrpcm; + sqLong primms; + sqInt result; + + lrpcm = 0; + sqLowLevelMFence(); + + /* Since the longRunningPrimitiveCheckMethod is sampled at + interrupt time be careful to validate it before returning it. */ + if ((GIV(longRunningPrimitiveStopUsecs) > GIV(longRunningPrimitiveStartUsecs)) + && ((((lrpcm = GIV(longRunningPrimitiveCheckMethod)))) + && ((addressCouldBeObj(lrpcm)) + && (((byteAt((void *)(lrpcm + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))))) { + result = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 3); + primms = ((GIV(longRunningPrimitiveStopUsecs) - GIV(longRunningPrimitiveStartUsecs)) + 500) / 1000; + gcms = (GIV(longRunningPrimitiveGCUsecs) + 500) / 1000; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(0, result, lrpcm)); + assert(isNonImmediate(result)); + if (oopisGreaterThanOrEqualTo(result, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(lrpcm & (tagMask())))) + && (oopisLessThan(lrpcm, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(result + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(result); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((result + BaseHeaderSize) + (0U << (shiftForWord()))),lrpcm); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(result)) + && (!(isForwarded(result)))); + assert(validStorePointerUncheckedArgs(1, result, (((usqInt)primms << 3) | 1))); + longAtput((void *)((result + BaseHeaderSize) + (1U << (shiftForWord()))),(((usqInt)primms << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(result)) + && (!(isForwarded(result)))); + assert(validStorePointerUncheckedArgs(2, result, ((gcms << 3) | 1))); + longAtput((void *)((result + BaseHeaderSize) + (2U << (shiftForWord()))),((gcms << 3) | 1)); + } + else { + result = GIV(nilObj); + } + + /* a hit */ + voidLongRunningPrimitive("get"); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); +} +#endif /* LRPCheck */ + + +/* This primitive is assumed to be fast (see e.g. + MethodDictionary>>includesKey:) so make it so. + N.B. Works correctly for cogged methods too. */ + + /* StackInterpreterPrimitives>>#primitiveObjectPointsTo */ +static void +primitiveObjectPointsTo(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong fmt; + sqLong header; + sqInt headerSqInt; + sqInt i; + sqInt methodHeader; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + char *sp; + sqInt thang; + sqInt trueOrFalse; + + thang = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((rcvr & (tagMask())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),GIV(falseObj)); + return; + } + + /* Inlined version of lastPointerOf: for speed in determining if rcvr is a context. */ + header = long64At((void *)(rcvr)); + fmt = (((usqLong)(header)) >> (formatShift())) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && ((header & (classIndexMask())) == ClassMethodContextCompactIndex)) { + if (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + trueOrFalse = marriedContextpointsTostackDeltaForCurrentFrame(rcvr, thang, 2); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (trueOrFalse + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return; + } + } + + /* contexts end at the stack pointer */ + numSlots = CtxtTempFrameStart + (fetchStackPointerOf(rcvr)); + } + else { + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + } + } + else { + if (fmt < (firstCompiledMethodFormat())) { + /* begin pop:thenPushBool: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),GIV(falseObj)); + return; + } + + /* no pointers + CompiledMethod: contains both pointers and bytes: */ + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(rcvr)); + headerSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + if (methodHeader == thang) { + /* begin pop:thenPushBool: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),GIV(trueObj)); + return; + } + numSlots = ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart; + } + assert((((numSlots - 1) * BytesPerOop) + BaseHeaderSize) == (lastPointerOf(rcvr))); + for (i = BaseHeaderSize; i <= (((numSlots - 1) * BytesPerOop) + BaseHeaderSize); i += BytesPerOop) { + if ((longAt((void *)(rcvr + i))) == thang) { + /* begin pop:thenPushBool: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),GIV(trueObj)); + return; + } + } + + /* begin pop:thenPushBool: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),GIV(falseObj)); +} + + /* StackInterpreterPrimitives>>#primitivePerform */ +static void +primitivePerform(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt lookupClassTag; + sqInt newReceiver; + usqInt performMethod; + sqInt tagBits; + + performMethod = GIV(newMethod); + GIV(messageSelector) = longAt(GIV(stackPointer) + ((GIV(argumentCount) - 1) * BytesPerWord)); + newReceiver = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* NOTE: the following lookup may fail and be converted to #doesNotUnderstand:, + so we must adjust argumentCount and slide args now, so that will work. + Slide arguments down over selector */ + GIV(argumentCount) -= 1; + for (i = GIV(argumentCount); i >= 1; i += -1) { + longAtput(GIV(stackPointer) + (i * BytesPerWord),longAt(GIV(stackPointer) + ((i - 1) * BytesPerWord))); + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + lookupClassTag = /* fetchClassTagOf: */ + ((tagBits = newReceiver & (tagMask())) + ? tagBits + : (longAt((void *)(newReceiver))) & (classIndexMask())); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), /* fetchClassTagOf: */ + ((tagBits = newReceiver & (tagMask())) + ? tagBits + : (longAt((void *)(newReceiver))) & (classIndexMask()))); + if (printOnTrace()) { + printActivationNameForSelectorstartClass(GIV(messageSelector), classForClassTag(lookupClassTag)); + cr(); + } + findNewMethodInClassTag(lookupClassTag); + + /* Only test CompiledMethods for argument count - other objects will have to take their chances */ + if (!((/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && ((argumentCountOf(GIV(newMethod))) == GIV(argumentCount)))) { + /* Slide the args back up (sigh) and re-insert the selector. */ + + /* begin unPop: */ + GIV(stackPointer) -= 1 * BytesPerWord; + for (i = 1; i <= GIV(argumentCount); i += 1) { + longAtput(GIV(stackPointer) + ((i - 1) * BytesPerWord),longAt(GIV(stackPointer) + (i * BytesPerWord))); + } + longAtput(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord),GIV(messageSelector)); + GIV(argumentCount) += 1; + GIV(newMethod) = performMethod; + + /* Must reset primitiveFunctionPointer for checkForAndFollowForwardedPrimitiveState */ + primitiveFunctionPointer = primitivePerform; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + executeNewMethod(); + + /* Recursive xeq affects primErrorCode */ + + /* begin initPrimCall */ + GIV(primFailCode) = 0; +} + + +/* Pin or unpin the receiver, i.e. make it immobile or mobile, based on the + argument. Answer whether the object was already pinned. N.B. pinning does + *not* prevent + an object from being garbage collected. */ + + /* StackInterpreterPrimitives>>#primitivePin */ +static void +primitivePin(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt boolean; + sqInt obj; + sqInt wasPinned; + + obj = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((obj & (tagMask())) != 0)) + || ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + boolean = longAt(GIV(stackPointer)); + if (!((boolean == GIV(falseObj)) + || (boolean == GIV(trueObj)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if ((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + wasPinned = GIV(trueObj); + if (boolean != wasPinned) { + setIsPinnedOfto(obj, 0); + } + } + else { + wasPinned = GIV(falseObj); + if (boolean == GIV(trueObj)) { + if ((/* isContext: */ + ((!(obj & (tagMask())))) + && (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(obj))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if (!(pinObject(obj))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + return; + } + } + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),wasPinned); +} + + +/* Primitive. Indicate the semaphore to be signalled for upon garbage + collection + */ + + /* StackInterpreterPrimitives>>#primitiveSetGCSemaphore */ +EXPORT(void) +primitiveSetGCSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt integerPointer; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + index = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + index = 0; + } + if (!GIV(primFailCode)) { + GIV(gcSemaphoreIndex) = index; + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } +} + + +/* Cause the time semaphore, if one has been registered, to be + signalled when the microsecond clock is greater than or equal to + the given tick value. A tick value of zero turns off timer interrupts. */ + + /* StackInterpreterPrimitives>>#primitiveSignalAtMilliseconds */ +static void +primitiveSignalAtMilliseconds(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong deltaMsecs; + sqLong limit; + sqLong msecs; + sqInt msecsObj; + sqInt sema; + + msecsObj = longAt(GIV(stackPointer)); + sema = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + msecs = positive32BitValueOf(msecsObj); + if (!GIV(primFailCode)) { + if (/* isSemaphoreOop: */ + ((!(sema & (tagMask())))) + && (((longAt((void *)(sema))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord())))))))))) { + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheTimerSemaphore, GIV(specialObjectsOop), sema)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(sema & (tagMask())))) + && (oopisLessThan(sema, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord()))))),sema); + deltaMsecs = msecs - ((ioMSecs()) & MillisecondClockMask); + limit = ((usqInt)(MillisecondClockMask)) >> 1; + + /* Handle a roll-over that could happen in between image invocation of ioMSecs and this invocation. + This will limit the maximum relative duration to MillisecondClockMask/2, about 3 days currently. + Every delay longer than that limit may lead to undefined behavior (shorten delay, or no delay at all) */ + if (deltaMsecs > limit) { + deltaMsecs -= MillisecondClockMask; + } + GIV(nextWakeupUsecs) = (deltaMsecs > 0 + ? (ioUTCMicroseconds()) + (deltaMsecs * 1000) + : ioUTCMicroseconds()); + + /* begin pop: */ + GIV(stackPointer) += 2 * BytesPerWord; + return; + } + if (sema == GIV(nilObj)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(specialObjectsOop))) + && (!(isForwarded(GIV(specialObjectsOop))))); + assert(validStorePointerUncheckedArgs(TheTimerSemaphore, GIV(specialObjectsOop), GIV(nilObj))); + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord()))))),GIV(nilObj)); + GIV(nextWakeupUsecs) = 0; + + /* begin pop: */ + GIV(stackPointer) += 2 * BytesPerWord; + return; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; +} + + +/* Cause the time semaphore, if one has been registered, to be + signalled when the microsecond clock is greater than or equal to + the given tick value. A tick value of zero turns off timer interrupts. */ + + /* StackInterpreterPrimitives>>#primitiveSignalAtUTCMicroseconds */ +static void +primitiveSignalAtUTCMicroseconds(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt sema; + usqLong usecs; + sqInt usecsObj; + + usecsObj = longAt(GIV(stackPointer)); + sema = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + usecs = positive64BitValueOf(usecsObj); + if (!GIV(primFailCode)) { + if (/* isSemaphoreOop: */ + ((!(sema & (tagMask())))) + && (((longAt((void *)(sema))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord())))))))))) { + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheTimerSemaphore, GIV(specialObjectsOop), sema)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(sema & (tagMask())))) + && (oopisLessThan(sema, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord()))))),sema); + GIV(nextWakeupUsecs) = usecs; + + /* begin pop: */ + GIV(stackPointer) += 2 * BytesPerWord; + return; + } + if (sema == GIV(nilObj)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(specialObjectsOop))) + && (!(isForwarded(GIV(specialObjectsOop))))); + assert(validStorePointerUncheckedArgs(TheTimerSemaphore, GIV(specialObjectsOop), GIV(nilObj))); + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord()))))),GIV(nilObj)); + GIV(nextWakeupUsecs) = 0; + + /* begin pop: */ + GIV(stackPointer) += 2 * BytesPerWord; + return; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; +} + + +/* Answer a slot in an object. This numbers all slots from 1, ignoring the + distinction between + named and indexed inst vars. In objects with both named and indexed inst + vars, the named + inst vars precede the indexed ones. In non-object indexed objects (objects + that contain + bits, not object references) this primitive answers the raw integral value + at each slot. + e.g. for Strings it answers the character code, not the Character object + at each slot. */ +/* because of externalInstVar:ofContext: below */ + + /* StackInterpreterPrimitives>>#primitiveSlotAt */ +static void +primitiveSlotAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt fmtSqInt; + sqInt index; + sqInt numLiveSlots; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt rcvr; + sqInt value; + + index = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if ((((usqInt)index)) < numSlots) { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + numLiveSlots = (stackPointerForMaybeMarriedContext(rcvr)) + CtxtTempFrameStart; + value = ((((usqInt)index)) < numLiveSlots + ? externalInstVarofContext(index, rcvr) + : GIV(nilObj)); + } + else { + value = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + + /* begin numBytesOfBytes: */ + fmtSqInt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert(fmtSqInt >= (firstByteFormat())); + numSlots = ((((/* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) << (shiftForWord()))) - (fmtSqInt & 7); + if ((((usqInt)index)) < numSlots) { + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + index))) << 3) | 1)); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt >= (firstShortFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 1; + if ((((usqInt)index)) < numSlots) { + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)(((unsigned short) (shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 1)))))))) << 3) | 1)); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + oop = positive64BitIntegerFor(long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3)))))); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt >= (firstLongFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)((((usqInt)(long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1)); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; +} + + +/* Atomic store into context stackPointer. + Also ensures that any newly accessible cells are initialized to nil */ + + /* StackInterpreterPrimitives>>#primitiveStoreStackp */ +static void +primitiveStoreStackp(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + sqInt i; + sqInt integerPointer; + sqInt newStackp; + usqInt numSlots; + int onCurrentPage; + sqInt senderOop; + sqInt sp; + sqInt stackp; + char *theFP; + StackPage *thePage; + + ctxt = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + newStackp = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + newStackp = 0; + } + if (!((!GIV(primFailCode)) + && (((newStackp >= 0) && (newStackp <= (((/* begin numSlotsOf: */ + assert((classIndexOf(ctxt)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(ctxt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(ctxt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - CtxtTempFrameStart)))))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(ctxt)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (((onCurrentPage = thePage == GIV(stackPage))) + && (theFP == GIV(framePointer))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + externalDivorceFrameandContext(theFP, ctxt); + if (onCurrentPage) { + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (GIV(stackPage)->headSP); + GIV(framePointer) = (GIV(stackPage)->headFP); + } + else { + assert(GIV(stackPage) == (stackPageFor(GIV(framePointer)))); + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + } + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + stackp = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(ctxt))); + stackp = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + + /* Nil any newly accessible cells */ + for (i = (stackp + 1); i <= newStackp; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs((i + CtxtTempFrameStart) - 1, ctxt, GIV(nilObj))); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(((i + CtxtTempFrameStart) - 1)) << (shiftForWord()))))),GIV(nilObj)); + } + + /* begin storeStackPointerValue:inContext: */ + assert((ReceiverIndex + newStackp) < (lengthOf(ctxt))); + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs(StackPointerIndex, ctxt, (((usqInt)newStackp << 3) | 1))); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord()))))),(((usqInt)newStackp << 3) | 1)); + ensureContextIsExecutionSafeAfterAssignToStackPointer(ctxt); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; +} + + +/* Behaviour depends on argument count: + 0 args: return an Array of VM parameter values; + 1 arg: return the indicated VM parameter; + 2 args: set the VM indicated parameter. + VM parameters are numbered as follows: + 1 end (v3)/size(Spur) of old-space (0-based, read-only) + 2 end (v3)/size(Spur) of young/new-space (read-only) + 3 end (v3)/size(Spur) of heap (read-only) + 4 nil (was allocationCount (read-only)) + 5 nil (was allocations between GCs (read-write) + 6 survivor count tenuring threshold (read-write) + 7 full GCs since startup (read-only) + 8 total milliseconds in full GCs since startup (read-only) + 9 incremental GCs (SqueakV3) or scavenges (Spur) since startup (read-only) + 10 total milliseconds in incremental GCs (SqueakV3) or scavenges (Spur) + since startup (read-only) + 11 tenures of surving objects since startup or reset (read-write) + 12-20 were specific to ikp's JITTER VM, now 12 16 open for use + 13 if started, the start time in utc microseconds of the high-priority + ticker 14 if started, the number of checkHighPriorityTickees calls + 15 if started, the number of tickee calls from checkHighPriorityTickees + 16 total microseconds at idle since start-up (if non-zero) + 17 fraction of the code zone to use (Sista only; used to control code zone + use to preserve sendAndBranchData on counter tripped callback) + 18 total milliseconds in compaction phase of full GC since start-up (Spur + only) 19 scavenge threshold, the effective size of eden. When eden fills + to the threshold a scavenge is scheduled. Newer Spur VMs only. + 20 utc microseconds at VM start-up (actually at time initialization, which + precedes image load). + 21 root/remembered table size (occupancy) (read-only) + 22 root table overflows since startup (read-only) + 23 bytes of extra memory to reserve for VM buffers, plugins, etc (stored + in image file header). + 24 memory threshold above which shrinking object memory (rw) + 25 memory headroom when growing object memory (rw) + 26 interruptChecksEveryNms - force an ioProcessEvents every N milliseconds + (rw) 27 number of times mark loop iterated for current IGC/FGC (read-only) + includes ALL marking + 28 number of times sweep loop iterated for current IGC/FGC (read-only) + 29 number of times make forward loop iterated for current IGC/FGC + (read-only) 30 number of times compact move loop iterated for current + IGC/FGC (read-only) + 31 number of grow memory requests (read-only) + 32 number of shrink memory requests (read-only) + 33 number of root table entries used for current IGC/FGC (read-only) + 34 Spur: bytes allocated in total since start-up or reset (read-write) + (Used to be number of allocations done before current IGC/FGC (read-only)) + 35 number of survivor objects after current IGC/FGC (read-only) + 36 millisecond clock when current IGC/FGC completed (read-only) + 37 number of marked objects for Roots of the world, not including Root + Table entries for current IGC/FGC (read-only) + 38 milliseconds taken by current IGC (read-only) + 39 Number of finalization signals for Weak Objects pending when current + IGC/FGC completed (read-only) + 40 BytesPerOop for this image + 41 imageFormatVersion for the VM + 42 number of stack pages in use + 43 desired number of stack pages (stored in image file header, max 65535) + 44 size of eden, in bytes + 45 desired size of eden, in bytes (stored in image file header) + 46 machine code zone size, in bytes (Cog only; otherwise nil) + 47 desired machine code zone size (stored in image file header; Cog only; + otherwise nil) + 48 various header flags. See getImageHeaderFlags. + 49 max size the image promises to grow the external semaphore table to (0 + sets to default, which is 256 as of writing) + 50 max literal count for JIT compile (stored in image file header; Cog + only; otherwise nil) + 51 nil; reserved for VM parameters that persist in the image (such as eden + above) 52 root/remembered table capacity + 53 number of segments (Spur only; otherwise nil) + 54 total size of free old space (Spur only, otherwise nil) + 55 ratio of growth and image size at or above which a GC will be performed + post scavenge + 56 number of process switches since startup (read-only) + 57 number of ioProcessEvents calls since startup (read-only) + 58 number of ForceInterruptCheck calls since startup (read-only) + 59 number of check event calls since startup (read-only) + 60 number of stack page overflows since startup (read-only) + 61 number of stack page divorces since startup (read-only) + 62 compiled code compactions since startup (read-only; Cog only; otherwise + nil) 63 total milliseconds in compiled code compactions since startup + (read-only; Cog only; otherwise nil) + 64 the number of methods that currently have jitted machine-code + 65 various VM feature flags; see getCogVMFeatureFlags + 66 the byte size of a stack page + 67 the max allowed size of old space (Spur only; nil otherwise; 0 implies + no limit except that of the underlying platform) + 68 the average number of live stack pages when scanned by GC (at + scavenge/gc/become et al) (read-write) + 69 the maximum number of live stack pages when scanned by GC (at + scavenge/gc/become et al) (read-write) + 70 the vmProxyMajorVersion (the interpreterProxy VM_MAJOR_VERSION) + 71 the vmProxyMinorVersion (the interpreterProxy VM_MINOR_VERSION) + 72 total milliseconds in full GCs Mark phase since startup (read-only) + 73 total milliseconds in full GCs Sweep phase since startup (read-only, + can be 0 depending on compactors) + 74 maximum pause time due to segment allocation + 75 whether the arithmetic primitives perform conversion in case of mixed + SmallInteger/Float (true) or fail (false) + 76 the minimum unused headroom in all stack pages; Cog VMs only + 77 whether the numweric comparison primitives perform conversion in case + of mixed SmallInteger/Float (true) or fail (false) + + Note: Thanks to Ian Piumarta for this primitive. */ + + /* StackInterpreterPrimitives>>#primitiveVMParameter */ +static void +primitiveVMParameter(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aBool; + sqInt activeContext; + double aProportion; + sqIntptr_t arg; + sqInt argOop; + sqLong delta; + sqInt index; + usqInt limit; + int mustFlush; + usqInt oldMixedArithmeticHeaderFlags; + sqInt oop; + sqInt paramsArraySize; + sqInt result; + sqInt resultSqInt; + sqInt senderOop; + sqInt tagBits; + char *theFrame; + StackPage *thePage; + sqInt top; + sqInt value; + sqInt valuePointer; + + paramsArraySize = 77; + if (!GIV(argumentCount)) { + /* begin primitiveAllVMParameters: */ + resultSqInt = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), paramsArraySize); + valuePointer = positive64BitIntegerFor( + (/* begin oldSpaceSize *//* begin totalOldSpaceSize */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), + GIV(totalHeapSizeIncludingBridges))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(0, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)((GIV(freeStart) - (((GIV(eden)).start))) + (GIV(pastSpaceStart) - (((GIV(pastSpace)).start)))) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(1, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor((newSpaceCapacity()) + ((/* begin totalOldSpaceSize */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges)))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(2, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (2U << (shiftForWord()))),valuePointer); + + /* objectMemory storePointerUnchecked: 3 ofObject: result withValue: objectMemory nilObject was allocationCount + objectMemory storePointerUnchecked: 4 ofObject: result withValue: objectMemory nilObject allocationsBetweenGCs */ + valuePointer = (((usqInt)(((sqInt)(((scavengerTenuringThreshold()) * (((GIV(pastSpace).limit)) - ((GIV(pastSpace).start)))) / (8 * BytesPerOop)))) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(5, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (5U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(6, resultSqInt, (((usqInt)GIV(statFullGCs) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (6U << (shiftForWord()))),(((usqInt)GIV(statFullGCs) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(7, resultSqInt, ((((GIV(statFullGCUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (7U << (shiftForWord()))),((((GIV(statFullGCUsecs) + 500) / 1000) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(8, resultSqInt, (((usqInt)GIV(statScavenges) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (8U << (shiftForWord()))),(((usqInt)GIV(statScavenges) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(9, resultSqInt, ((((GIV(statScavengeGCUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (9U << (shiftForWord()))),((((GIV(statScavengeGCUsecs) + 500) / 1000) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(10, resultSqInt, (((usqInt)GIV(statTenures) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (10U << (shiftForWord()))),(((usqInt)GIV(statTenures) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(11, resultSqInt, (((usqInt)eventTraceMask << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (11U << (shiftForWord()))),(((usqInt)eventTraceMask << 3) | 1)); + valuePointer = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickerStartUSecs()) +# else + ConstZero +# endif + ; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(12, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (12U << (shiftForWord()))),valuePointer); + valuePointer = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickerCount()) +# else + ConstZero +# endif + ; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(13, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (13U << (shiftForWord()))),valuePointer); + valuePointer = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickeeCallCount()) +# else + ConstZero +# endif + ; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(14, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (14U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statIdleUsecs)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(15, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (15U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(16, resultSqInt, ConstZero)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (16U << (shiftForWord()))),ConstZero); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(17, resultSqInt, ((((GIV(statCompactionUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (17U << (shiftForWord()))),((((GIV(statCompactionUsecs) + 500) / 1000) << 3) | 1)); + valuePointer = (((usqInt)(GIV(scavengeThreshold) - (((GIV(eden)).start))) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(18, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (18U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(ioUTCStartMicroseconds()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(19, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (19U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)(rootTableCount()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(20, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (20U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(21, resultSqInt, (((usqInt)GIV(statRootTableOverflows) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (21U << (shiftForWord()))),(((usqInt)GIV(statRootTableOverflows) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(22, resultSqInt, (((usqInt)extraVMMemory << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (22U << (shiftForWord()))),(((usqInt)extraVMMemory << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(23, resultSqInt, (((usqInt)GIV(shrinkThreshold) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (23U << (shiftForWord()))),(((usqInt)GIV(shrinkThreshold) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(24, resultSqInt, (((usqInt)GIV(growHeadroom) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (24U << (shiftForWord()))),(((usqInt)GIV(growHeadroom) << 3) | 1)); + valuePointer = (((usqInt)(ioHeartbeatMilliseconds()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(25, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (25U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(26, resultSqInt, (((usqInt)GIV(statMarkCount) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (26U << (shiftForWord()))),(((usqInt)GIV(statMarkCount) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(27, resultSqInt, (((usqInt)0 /* statSweepCount */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (27U << (shiftForWord()))),(((usqInt)0 /* statSweepCount */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(28, resultSqInt, (((usqInt)0 /* statMkFwdCount */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (28U << (shiftForWord()))),(((usqInt)0 /* statMkFwdCount */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(29, resultSqInt, (((usqInt)GIV(statCompactPassCount) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (29U << (shiftForWord()))),(((usqInt)GIV(statCompactPassCount) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(30, resultSqInt, (((usqInt)GIV(statGrowMemory) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (30U << (shiftForWord()))),(((usqInt)GIV(statGrowMemory) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(0x1F, resultSqInt, (((usqInt)GIV(statShrinkMemory) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (0x1FU << (shiftForWord()))),(((usqInt)GIV(statShrinkMemory) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(32, resultSqInt, (((usqInt)GIV(statRootTableCount) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (32U << (shiftForWord()))),(((usqInt)GIV(statRootTableCount) << 3) | 1)); + valuePointer = positive64BitIntegerFor(currentAllocatedBytes()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(33, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (33U << (shiftForWord()))),valuePointer); + + /* was statAllocationCount */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(34, resultSqInt, (((usqInt)GIV(statSurvivorCount) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (34U << (shiftForWord()))),(((usqInt)GIV(statSurvivorCount) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(35, resultSqInt, ((((GIV(statGCEndUsecs) / 1000) & MillisecondClockMask) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (35U << (shiftForWord()))),((((GIV(statGCEndUsecs) / 1000) & MillisecondClockMask) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(36, resultSqInt, (((usqInt)0 /* statSpecialMarkCount */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (36U << (shiftForWord()))),(((usqInt)0 /* statSpecialMarkCount */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(37, resultSqInt, ((((GIV(statIGCDeltaUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (37U << (shiftForWord()))),((((GIV(statIGCDeltaUsecs) + 500) / 1000) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(38, resultSqInt, (((usqInt)GIV(statPendingFinalizationSignals) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (38U << (shiftForWord()))),(((usqInt)GIV(statPendingFinalizationSignals) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(39, resultSqInt, (((usqInt)BytesPerWord << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (39U << (shiftForWord()))),(((usqInt)BytesPerWord << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(40, resultSqInt, (((usqInt)68021 /* imageFormatVersion */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (40U << (shiftForWord()))),(((usqInt)68021 /* imageFormatVersion */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(41, resultSqInt, (((usqInt)GIV(numStackPages) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (41U << (shiftForWord()))),(((usqInt)GIV(numStackPages) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(42, resultSqInt, (((usqInt)desiredNumStackPages << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (42U << (shiftForWord()))),(((usqInt)desiredNumStackPages << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(43, resultSqInt, (((usqInt)(((GIV(eden).limit)) - ((GIV(eden).start))) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (43U << (shiftForWord()))),(((usqInt)(((GIV(eden).limit)) - ((GIV(eden).start))) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(44, resultSqInt, (((usqInt)desiredEdenBytes << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (44U << (shiftForWord()))),(((usqInt)desiredEdenBytes << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(45, resultSqInt, ((GIV(cogCodeSize) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (45U << (shiftForWord()))),((GIV(cogCodeSize) << 3) | 1)); + valuePointer = getDesiredCogCodeSize(); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(46, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (46U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(47, resultSqInt, (((usqInt)(((usqInt)(imageHeaderFlags)) >> 2) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (47U << (shiftForWord()))),(((usqInt)(((usqInt)(imageHeaderFlags)) >> 2) << 3) | 1)); + valuePointer = (((usqInt)(ioGetMaxExtSemTableSize()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(48, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (48U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(49, resultSqInt, (((usqInt)maxLiteralCountForCompile << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (49U << (shiftForWord()))),(((usqInt)maxLiteralCountForCompile << 3) | 1)); + valuePointer = (((usqInt)(((GIV(oldImageBaseAddress) != GIV(oldSpaceStart) + ? 1 + : 0)) + ((sqImageFileIsEmbedded() + ? 2 + : 0))) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(50, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (50U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)(rootTableCapacity()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(51, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (51U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(52, resultSqInt, (((usqInt)GIV(numSegments) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (52U << (shiftForWord()))),(((usqInt)GIV(numSegments) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(53, resultSqInt, ((GIV(totalFreeOldSpace) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (53U << (shiftForWord()))),((GIV(totalFreeOldSpace) << 3) | 1)); + valuePointer = floatObjectOf(getHeapGrowthToSizeGCRatio()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(54, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (54U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statProcessSwitch)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(55, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (55U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statIOProcessEvents)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(56, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (56U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statForceInterruptCheck)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(57, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (57U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statCheckForEvents)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(58, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (58U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statStackOverflow)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(59, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (59U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statStackPageDivorce)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(60, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (60U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(61, resultSqInt, (((usqInt)GIV(statCodeCompactionCount) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (61U << (shiftForWord()))),(((usqInt)GIV(statCodeCompactionCount) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(0x3E, resultSqInt, ((((GIV(statCodeCompactionUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (0x3EU << (shiftForWord()))),((((GIV(statCodeCompactionUsecs) + 500) / 1000) << 3) | 1)); + valuePointer = (((usqInt)(numMethodsOfType(CMMethod)) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(0x3F, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (0x3FU << (shiftForWord()))),valuePointer); + valuePointer = getCogVMFeatureFlags(); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(64, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (64U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(65, resultSqInt, (((usqInt)GIV(bytesPerPage) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (65U << (shiftForWord()))),(((usqInt)GIV(bytesPerPage) << 3) | 1)); + valuePointer = positive64BitIntegerFor(maxOldSpaceSize); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(66, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (66U << (shiftForWord()))),valuePointer); + valuePointer = floatObjectOf(statAverageLivePagesWhenMapping()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(67, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (67U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)(GIV(statMaxPageCountWhenMapping)) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(68, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (68U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(69, resultSqInt, (((usqInt)VM_PROXY_MAJOR /* vmProxyMajorVersion */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (69U << (shiftForWord()))),(((usqInt)VM_PROXY_MAJOR /* vmProxyMajorVersion */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(70, resultSqInt, (((usqInt)VM_PROXY_MINOR /* vmProxyMinorVersion */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (70U << (shiftForWord()))),(((usqInt)VM_PROXY_MINOR /* vmProxyMinorVersion */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(71, resultSqInt, ((((GIV(statMarkUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (71U << (shiftForWord()))),((((GIV(statMarkUsecs) + 500) / 1000) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(72, resultSqInt, ((((GIV(statSweepUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (72U << (shiftForWord()))),((((GIV(statSweepUsecs) + 500) / 1000) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(73, resultSqInt, (((usqInt)((GIV(statMaxAllocSegmentTime) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (73U << (shiftForWord()))),(((usqInt)((GIV(statMaxAllocSegmentTime) + 500) / 1000) << 3) | 1)); + aBool = numericPrimsMixArithmetic(); + + /* begin booleanObjectOf: */ + valuePointer = (aBool + ? GIV(trueObj) + : GIV(falseObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(74, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (74U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)(minimumUnusedHeadroom()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(75, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (75U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)(numericPrimsMixComparison()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(76, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (76U << (shiftForWord()))),valuePointer); + beRootIfOld(resultSqInt); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),resultSqInt); + return; + } + if (GIV(argumentCount) > 2) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + + /* index read & checks */ + index = longAt(GIV(stackPointer) + (((GIV(argumentCount) == 1 + ? 0 + : 1)) * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + index = (index >> 3); + if ((index < 1) + || (index > paramsArraySize)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (GIV(argumentCount) == 1) { + /* begin primitiveGetVMParameter: */ + switch (index) { + case 1: + result = positive64BitIntegerFor( + (/* begin oldSpaceSize *//* begin totalOldSpaceSize */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), + GIV(totalHeapSizeIncludingBridges))); + goto l1; + break; + case 2: + result = (((usqInt)((GIV(freeStart) - (((GIV(eden)).start))) + (GIV(pastSpaceStart) - (((GIV(pastSpace)).start)))) << 3) | 1); + goto l1; + break; + case 3: + result = positive64BitIntegerFor((newSpaceCapacity()) + ((/* begin totalOldSpaceSize */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges)))); + goto l1; + break; + case 6: + result = (((usqInt)(((sqInt)(((scavengerTenuringThreshold()) * (((GIV(pastSpace).limit)) - ((GIV(pastSpace).start)))) / (8 * BytesPerOop)))) << 3) | 1); + goto l1; + break; + case 7: + result = (((usqInt)GIV(statFullGCs) << 3) | 1); + goto l1; + break; + case 8: + result = ((((GIV(statFullGCUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 9: + result = (((usqInt)GIV(statScavenges) << 3) | 1); + goto l1; + break; + case 10: + result = ((((GIV(statScavengeGCUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 11: + result = (((usqInt)GIV(statTenures) << 3) | 1); + goto l1; + break; + case 12: + result = (((usqInt)eventTraceMask << 3) | 1); + goto l1; + break; + case 13: + result = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickerStartUSecs()) +# else + ConstZero +# endif + ; + goto l1; + break; + case 14: + result = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickerCount()) +# else + ConstZero +# endif + ; + goto l1; + break; + case 15: + result = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickeeCallCount()) +# else + ConstZero +# endif + ; + goto l1; + break; + case 16: + result = positive64BitIntegerFor(GIV(statIdleUsecs)); + goto l1; + break; + case 17: + result = ConstZero; + goto l1; + break; + case 18: + result = ((((GIV(statCompactionUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 19: + result = (((usqInt)(GIV(scavengeThreshold) - (((GIV(eden)).start))) << 3) | 1); + goto l1; + break; + case 20: + result = positive64BitIntegerFor(ioUTCStartMicroseconds()); + goto l1; + break; + case 21: + result = (((usqInt)(rootTableCount()) << 3) | 1); + goto l1; + break; + case 22: + result = (((usqInt)GIV(statRootTableOverflows) << 3) | 1); + goto l1; + break; + case 23: + result = (((usqInt)extraVMMemory << 3) | 1); + goto l1; + break; + case 24: + result = (((usqInt)GIV(shrinkThreshold) << 3) | 1); + goto l1; + break; + case 25: + result = (((usqInt)GIV(growHeadroom) << 3) | 1); + goto l1; + break; + case 26: + result = (((usqInt)(ioHeartbeatMilliseconds()) << 3) | 1); + goto l1; + break; + case 27: + result = (((usqInt)GIV(statMarkCount) << 3) | 1); + goto l1; + break; + case 28: + result = (((usqInt)0 /* statSweepCount */ << 3) | 1); + goto l1; + break; + case 29: + result = (((usqInt)0 /* statMkFwdCount */ << 3) | 1); + goto l1; + break; + case 30: + result = (((usqInt)GIV(statCompactPassCount) << 3) | 1); + goto l1; + break; + case 0x1F: + result = (((usqInt)GIV(statGrowMemory) << 3) | 1); + goto l1; + break; + case 32: + result = (((usqInt)GIV(statShrinkMemory) << 3) | 1); + goto l1; + break; + case 33: + result = (((usqInt)GIV(statRootTableCount) << 3) | 1); + goto l1; + break; + case 34: + result = positive64BitIntegerFor(currentAllocatedBytes()); + goto l1; + break; + case 35: + result = (((usqInt)GIV(statSurvivorCount) << 3) | 1); + goto l1; + break; + case 36: + result = ((((GIV(statGCEndUsecs) / 1000) & MillisecondClockMask) << 3) | 1); + goto l1; + break; + case 37: + result = (((usqInt)0 /* statSpecialMarkCount */ << 3) | 1); + goto l1; + break; + case 38: + result = ((((GIV(statIGCDeltaUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 39: + result = (((usqInt)GIV(statPendingFinalizationSignals) << 3) | 1); + goto l1; + break; + case 40: + result = (((usqInt)BytesPerWord << 3) | 1); + goto l1; + break; + case 41: + result = (((usqInt)68021 /* imageFormatVersion */ << 3) | 1); + goto l1; + break; + case 42: + result = (((usqInt)GIV(numStackPages) << 3) | 1); + goto l1; + break; + case 43: + result = (((usqInt)desiredNumStackPages << 3) | 1); + goto l1; + break; + case 44: + result = (((usqInt)(((GIV(eden).limit)) - ((GIV(eden).start))) << 3) | 1); + goto l1; + break; + case 45: + result = (((usqInt)desiredEdenBytes << 3) | 1); + goto l1; + break; + case 46: + result = ((GIV(cogCodeSize) << 3) | 1); + goto l1; + break; + case 47: + result = getDesiredCogCodeSize(); + goto l1; + break; + case 48: + result = (((usqInt)(((usqInt)(imageHeaderFlags)) >> 2) << 3) | 1); + goto l1; + break; + case 49: + result = (((usqInt)(ioGetMaxExtSemTableSize()) << 3) | 1); + goto l1; + break; + case 50: + result = (((usqInt)maxLiteralCountForCompile << 3) | 1); + goto l1; + break; + case 51: + result = (((usqInt)(((GIV(oldImageBaseAddress) != GIV(oldSpaceStart) + ? 1 + : 0)) + ((sqImageFileIsEmbedded() + ? 2 + : 0))) << 3) | 1); + goto l1; + break; + case 52: + result = (((usqInt)(rootTableCapacity()) << 3) | 1); + goto l1; + break; + case 53: + result = (((usqInt)GIV(numSegments) << 3) | 1); + goto l1; + break; + case 54: + result = ((GIV(totalFreeOldSpace) << 3) | 1); + goto l1; + break; + case 55: + result = floatObjectOf(getHeapGrowthToSizeGCRatio()); + goto l1; + break; + case 56: + result = positive64BitIntegerFor(GIV(statProcessSwitch)); + goto l1; + break; + case 57: + result = positive64BitIntegerFor(GIV(statIOProcessEvents)); + goto l1; + break; + case 58: + result = positive64BitIntegerFor(GIV(statForceInterruptCheck)); + goto l1; + break; + case 59: + result = positive64BitIntegerFor(GIV(statCheckForEvents)); + goto l1; + break; + case 60: + result = positive64BitIntegerFor(GIV(statStackOverflow)); + goto l1; + break; + case 61: + result = positive64BitIntegerFor(GIV(statStackPageDivorce)); + goto l1; + break; + case 0x3E: + result = (((usqInt)GIV(statCodeCompactionCount) << 3) | 1); + goto l1; + break; + case 0x3F: + result = ((((GIV(statCodeCompactionUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 64: + result = (((usqInt)(numMethodsOfType(CMMethod)) << 3) | 1); + goto l1; + break; + case 65: + result = getCogVMFeatureFlags(); + goto l1; + break; + case 66: + result = (((usqInt)GIV(bytesPerPage) << 3) | 1); + goto l1; + break; + case 67: + result = positive64BitIntegerFor(maxOldSpaceSize); + goto l1; + break; + case 68: + result = floatObjectOf(statAverageLivePagesWhenMapping()); + goto l1; + break; + case 69: + result = (((usqInt)(GIV(statMaxPageCountWhenMapping)) << 3) | 1); + goto l1; + break; + case 70: + result = (((usqInt)VM_PROXY_MAJOR /* vmProxyMajorVersion */ << 3) | 1); + goto l1; + break; + case 71: + result = (((usqInt)VM_PROXY_MINOR /* vmProxyMinorVersion */ << 3) | 1); + goto l1; + break; + case 72: + result = ((((GIV(statMarkUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 73: + result = ((((GIV(statSweepUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 74: + result = (((usqInt)((GIV(statMaxAllocSegmentTime) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 75: + aBool = numericPrimsMixArithmetic(); + result = (aBool + ? GIV(trueObj) + : GIV(falseObj)); + goto l1; + break; + case 76: + result = (((usqInt)(minimumUnusedHeadroom()) << 3) | 1); + goto l1; + break; + case 77: + aBool = numericPrimsMixComparison(); + result = (aBool + ? GIV(trueObj) + : GIV(falseObj)); + goto l1; + break; + default: + result = null; + goto l1; + } + /* end primitiveGetVMParameter: */ +l1: + oop = (!(result) + ? GIV(nilObj) + : result); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return; + } + + /* read VM parameter; written this way to avoid branch limits in V3 bytecode set + written thus to enable Slang inlining of primitiveGetVMParameter: + write a VM parameter */ + argOop = longAt(GIV(stackPointer)); + + /* begin primitiveSetVMParameter:arg: */ + switch (index) { + case 17: + case 55: + case 68: + if (!((/* isFloatInstance: */ + ((tagBits = argOop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex)) + || ((((argOop) & 7) == 1)))) { + GIV(primFailCode) = PrimErrBadArgument; + } + break; + case 67: + /* begin positiveMachineIntegerValueOf: */ + if ((((argOop) & 7) == 1)) { + value = (argOop >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = ((usqIntptr_t) null); + goto l2; + } + arg = ((usqIntptr_t) value); + goto l2; + } + + /* don't inline the rare case */ + arg = positiveMachineIntegerValueOfObj(argOop); + /* end positiveMachineIntegerValueOf: */ +l2:; + break; + case 75: + case 77: + /* begin booleanValueOf: */ + if (argOop == GIV(trueObj)) { + arg = 1; + goto l3; + } + if (argOop == GIV(falseObj)) { + arg = 0; + goto l3; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = null; + /* end booleanValueOf: */ +l3:; + break; + default: + arg = (argOop >> 3); + } + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l6; + } + + /* assume failure, then set success for handled indices */ + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + switch (index) { + case 5: + break; + case 6: + resultSqInt = (((usqInt)(((sqInt)(((scavengerTenuringThreshold()) * (((GIV(pastSpace).limit)) - ((GIV(pastSpace).start)))) / (8 * BytesPerOop)))) << 3) | 1); + + /* begin tenuringThreshold: */ + if (arg < 0) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + aProportion = (((double) (arg * (8 * BytesPerOop)) )) / (((double) (((GIV(pastSpace).limit)) - ((GIV(pastSpace).start))) )); + + /* begin scavengerTenuringThreshold: */ + GIV(tenuringProportion) = aProportion; + GIV(tenureThreshold) = (aProportion == 0.0 + ? 0 + : (round(((((GIV(pastSpace).limit)) - ((GIV(pastSpace).start))) * (1.0 - aProportion)))) + ((GIV(pastSpace).start))); + GIV(primFailCode) = 0; + /* end tenuringThreshold: */ +l4:; + break; + case 12: + resultSqInt = (((usqInt)eventTraceMask << 3) | 1); + eventTraceMask = arg; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + break; + case 11: + if (arg >= 0) { + resultSqInt = (((usqInt)GIV(statTenures) << 3) | 1); + + /* begin statTenures: */ + GIV(statTenures) = arg; + GIV(primFailCode) = 0; + } + break; + case 17: + break; + case 23: + resultSqInt = (((usqInt)extraVMMemory << 3) | 1); + extraVMMemory = arg; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + break; + case 24: + if (arg > 0) { + resultSqInt = (((usqInt)GIV(shrinkThreshold) << 3) | 1); + + /* begin shrinkThreshold: */ + GIV(shrinkThreshold) = arg; + GIV(primFailCode) = 0; + } + break; + case 25: + if (arg > 0) { + resultSqInt = (((usqInt)GIV(growHeadroom) << 3) | 1); + + /* growHeadroom: */ + GIV(growHeadroom) = arg; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + } + break; + case 26: + if (arg >= 0) { + resultSqInt = (((usqInt)(ioHeartbeatMilliseconds()) << 3) | 1); + ioSetHeartbeatMilliseconds(arg); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + } + break; + case 34: + if (arg >= 0) { + resultSqInt = positive64BitIntegerFor(currentAllocatedBytes()); + + /* begin setCurrentAllocatedBytesTo: */ + delta = (currentAllocatedBytes()) - GIV(statAllocatedBytes); + GIV(statAllocatedBytes) = arg; + GIV(oldSpaceUsePriorToScavenge) += delta; + assert((currentAllocatedBytes()) == arg); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + } + break; + case 43: + if (((arg >= 0) && (arg <= 0xFFFF))) { + resultSqInt = (((usqInt)desiredNumStackPages << 3) | 1); + desiredNumStackPages = arg; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + } + break; + case 45: + if (arg >= 0) { + resultSqInt = (((usqInt)desiredEdenBytes << 3) | 1); + desiredEdenBytes = arg; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + } + break; + case 47: + if (((arg >= 0) && (arg <= (maxCogCodeSize())))) { + resultSqInt = (((usqInt)(getDesiredCogCodeSize()) << 3) | 1); + + /* begin setDesiredCogCodeSize: */ + desiredCogCodeSize = arg; + GIV(primFailCode) = 0; + } + break; + case 48: + if (arg >= 0) { + /* answer old value */ + resultSqInt = (((usqInt)(((usqInt)(imageHeaderFlags)) >> 2) << 3) | 1); + + /* must flush/recompile if either of these are changed */ + oldMixedArithmeticHeaderFlags = imageHeaderFlags & 0x900; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if ((((usqInt)arg)) > 0x3FF) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + goto l5; + } + imageHeaderFlags = (((imageHeaderFlags | ((0xFFC))) - ((0xFFC)))) | (((((usqIntptr_t)(arg) << 2)))); + /* end setImageHeaderFlags: */ +l5: + if ((GIV(primFailCode) == 0) + && (oldMixedArithmeticHeaderFlags != (imageHeaderFlags & 0x900))) { + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + flushMethodsWithMachineCodePrimitivesAndContinueAnswering(resultSqInt); + goto l6; + } + } + break; + case 49: + if (((arg >= 0) && (arg <= 0xFFFF))) { + resultSqInt = (((usqInt)(ioGetMaxExtSemTableSize()) << 3) | 1); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + GIV(maxExtSemTabSizeSet) = 1; + ioSetMaxExtSemTableSize(arg); + } + break; + case 50: + if (((arg >= 0) && (arg <= 0x7FFF))) { + resultSqInt = (((usqInt)((((usqInt)maxLiteralCountForCompile << 3) | 1)) << 3) | 1); + + /* begin setMaxLiteralCountForCompile: */ + maxLiteralCountForCompile = (GIV(the2ndUnknownShort) = arg); + GIV(primFailCode) = 0; + activeContext = divorceAllFramesSuchThat(isMachineCodeFrameForCogMethodWithTooManyLiterals); + ensureAllContextsHaveBytecodePCsIf(methodHasTooManyLiterals); + unlinkSendsToMethodsSuchThatAndFreeIf(cogMethodHasTooManyLiterals, 1); + + /* If the top frame was divorced then continue in the interpreter. */ + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* pop bogus machine-code instructionPointer, arguments and receiver */ + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 2) - 1) * BytesPerWord),resultSqInt); + ceInvokeInterpret(); + } + + /* If not, work out where we are and continue */ + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + assert(((thePage->headFP)) == theFrame); + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),resultSqInt); + goto l6; + } + break; + case 55: + resultSqInt = floatObjectOf(getHeapGrowthToSizeGCRatio()); + + /* begin setHeapGrowthToSizeGCRatio: */ + GIV(heapGrowthToSizeGCRatio) = loadFloatOrIntFrom(argOop); + GIV(primFailCode) = 0; + break; + case 67: + if (arg >= 0) { + resultSqInt = positive64BitIntegerFor(maxOldSpaceSize); + limit = arg; + + /* begin setMaxOldSpaceSize: */ + maxOldSpaceSize = limit; + GIV(primFailCode) = 0; + } + break; + case 68: + resultSqInt = floatObjectOf(statAverageLivePagesWhenMapping()); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if ((loadFloatOrIntFrom(argOop)) == 0.0) { + GIV(statPageCountWhenMappingSum) = (GIV(statNumMaps) = 0); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + break; + case 69: + if (arg >= 0) { + resultSqInt = (((usqInt)(GIV(statMaxPageCountWhenMapping)) << 3) | 1); + + /* begin statMaxPageCountWhenMapping: */ + GIV(statMaxPageCountWhenMapping) = arg; + GIV(primFailCode) = 0; + } + break; + case 74: + if (arg >= 0) { + resultSqInt = (((usqInt)((GIV(statMaxAllocSegmentTime) + 500) / 1000) << 3) | 1); + + /* begin statMaxAllocSegmentTime: */ + GIV(statMaxAllocSegmentTime) = arg; + GIV(primFailCode) = 0; + } + break; + case 75: + aBool = numericPrimsMixArithmetic(); + + /* begin booleanObjectOf: */ + resultSqInt = (aBool + ? GIV(trueObj) + : GIV(falseObj)); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + mustFlush = (numericPrimsMixArithmetic()) != arg; + + /* setPrimitiveDoMixedArithmetic: */ + imageHeaderFlags = (arg + ? imageHeaderFlags | 0x100 + : ((imageHeaderFlags | 0x100) - 0x100)); + if (mustFlush) { + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + flushMethodsWithMachineCodePrimitivesAndContinueAnswering(resultSqInt); + goto l6; + } + break; + case 77: + aBool = numericPrimsMixComparison(); + + /* begin booleanObjectOf: */ + resultSqInt = (aBool + ? GIV(trueObj) + : GIV(falseObj)); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + mustFlush = (numericPrimsMixComparison()) != arg; + + /* setPrimitiveDoMixedNumericComparison: */ + imageHeaderFlags = (arg + ? imageHeaderFlags | 0x800 + : ((imageHeaderFlags | 0x800) - 0x800)); + if (mustFlush) { + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + flushMethodsWithMachineCodePrimitivesAndContinueAnswering(resultSqInt); + goto l6; + } + break; + default: +; + } + + /* disable inlining... + disable inlining... + disable inlining... */ + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),resultSqInt); + } + /* end primitiveSetVMParameter:arg: */ +l6:; +} + + +/* Potentially crash the VM by voiding the receiver. A subsequent inst var + access in the caller's frame should indirect through a null pointer. */ + + /* StackInterpreterPrimitives>>#primitiveVoidReceiver */ +EXPORT(void) +primitiveVoidReceiver(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + longAtput(/* frameReceiverLocation: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? GIV(framePointer) + FoxMFReceiver + : GIV(framePointer) + FoxIFReceiver),0); +} + + +/* Prune the stack to contain only the path, removing stacked indices + and mapping frame pointers to contexts The issue here is that a + GC can occur during ensureFrameIsMarried:SP:, but frame pointers + are not valid objects. So first prune back to objects and framePointers + as integers, and then replace frame pointers as integers by contexts. */ + + /* StackInterpreterPrimitives>>#pruneStack:stackp: */ +static NoDbgRegParms void +pruneStackstackp(sqInt stack, sqInt stackp) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt finger; + char *fp; + sqInt i; + sqInt objOrFP; + sqInt oop; + char *theFP; + char *theFPAbove; + StackPage *thePage; + char *theSP; + sqInt theStack; + sqInt toDoLimit; + + finger = 1; + for (i = 2; i < stackp; i += 2) { + objOrFP = longAt((void *)((stack + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)(((char *) objOrFP)))) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)(((char *) objOrFP)))) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)(((char *) objOrFP)))) <= (((usqInt)GIV(pages)))))))) { + objOrFP += smallIntegerTag(); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(finger, stack, objOrFP)); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(finger) << (shiftForWord()))))),objOrFP); + finger += 1; + } + toDoLimit = (lengthOf(stack)) - 1; + for (i = finger; i <= toDoLimit; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(i, stack, GIV(nilObj))); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } + oop = (theStack = stack); + + /* begin pushRemappableOop: */ + assert(addressCouldBeOop(oop)); + GIV(remapBuffer)[(GIV(remapBufferCount) += 1)] = oop; + if (!(GIV(remapBufferCount) <= RemapBufferSize)) { + error("remapBuffer overflow"); + } + for (i = 1; i < finger; i += 1) { + objOrFP = longAt((void *)((theStack + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((((objOrFP) & 7) == 1)) { + /* begin withoutSmallIntegerTags: */ + assert((((objOrFP) & 7) == 1)); + theFP = ((char *)(objOrFP - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == theFP) { + theFPAbove = 0; + goto l1; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == theFP) { + theFPAbove = fp; + goto l1; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + theFPAbove = 0; + /* end findFrameAbove:inPage: */ +l1: + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(theFPAbove))); + theSP = (theFPAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFPAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFPAbove))->cmNumArgs) + : byteAt((theFPAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + objOrFP = longAt(theFP + FoxThisContext); + goto l2; + } + objOrFP = marryFrameSP(theFP, theSP); + /* end ensureFrameIsMarried:SP: */ +l2: + theStack = GIV(remapBuffer)[GIV(remapBufferCount)]; + + /* after a GC stack may no longer be a root. */ + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(finger, theStack, objOrFP)); + assert(isNonImmediate(theStack)); + if (oopisGreaterThanOrEqualTo(theStack, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objOrFP & (tagMask())))) + && (oopisLessThan(objOrFP, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(theStack + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(theStack); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((theStack + BaseHeaderSize) + ((((usqInt)(finger) << (shiftForWord()))))),objOrFP); + } + } + + /* begin popRemappableOop */ + oop = GIV(remapBuffer)[GIV(remapBufferCount)]; + GIV(remapBufferCount) -= 1; +} + + /* StackInterpreterPrimitives>>#unmarkAfterPathTo */ +static void +unmarkAfterPathTo(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + unmarkAllFrames(); + + /* begin unmarkAllObjects */ + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + else { + if (!(((longAt((void *)(objOop))) & (classIndexMask())) == (segmentBridgePun()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + else { + if (!(((longAt((void *)(objOop))) & (classIndexMask())) == (segmentBridgePun()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3:; +} + +/*** Exports ***/ + +static char _m[] = ""; +void* vm_exports[][3] = { + {(void*)_m, "moduleUnloaded", (void*)moduleUnloaded}, + {(void*)_m, "primitiveAddLargeIntegers\000\000\001", (void*)primitiveAddLargeIntegers}, + {(void*)_m, "primitiveAllInstances\000\000\000", (void*)primitiveAllInstances}, +#if VMInvestigations + {(void*)_m, "primitiveAllMethodsCompiledToMachineCode\000\377\000", (void*)primitiveAllMethodsCompiledToMachineCode}, +#endif /* VMInvestigations */ + {(void*)_m, "primitiveAllObjects\000\377\000", (void*)primitiveAllObjects}, +#if VMInvestigations + {(void*)_m, "primitiveBenchmarkFollowForwardersInStackZone\000\377\000", (void*)primitiveBenchmarkFollowForwardersInStackZone}, +#endif /* VMInvestigations */ +#if VMInvestigations + {(void*)_m, "primitiveBenchmarkFollowForwardersOfReceiverAndTemporariesInStackZone\000\377\000", (void*)primitiveBenchmarkFollowForwardersOfReceiverAndTemporariesInStackZone}, +#endif /* VMInvestigations */ +#if VMInvestigations + {(void*)_m, "primitiveBenchmarkJITReceiver\000\000\000", (void*)primitiveBenchmarkJITReceiver}, +#endif /* VMInvestigations */ +#if VMInvestigations + {(void*)_m, "primitiveBenchmarkScavenge\000\377\000", (void*)primitiveBenchmarkScavenge}, +#endif /* VMInvestigations */ + {(void*)_m, "primitiveBitAndLargeIntegers\000\000\001", (void*)primitiveBitAndLargeIntegers}, + {(void*)_m, "primitiveBitOrLargeIntegers\000\000\001", (void*)primitiveBitOrLargeIntegers}, + {(void*)_m, "primitiveBitShiftLargeIntegers\000\000\001", (void*)primitiveBitShiftLargeIntegers}, + {(void*)_m, "primitiveBitXorLargeIntegers\000\000\001", (void*)primitiveBitXorLargeIntegers}, + {(void*)_m, "primitiveBytecodeSetsAvailable\000\377\000", (void*)primitiveBytecodeSetsAvailable}, + {(void*)_m, "primitiveClockLogAddresses\000\000\000", (void*)primitiveClockLogAddresses}, +#if VMInvestigations + {(void*)_m, "primitiveCogitMethod\000\000\000", (void*)primitiveCogitMethod}, +#endif /* VMInvestigations */ +#if VMInvestigations + {(void*)_m, "primitiveCogStaticSymbols\000\377\000", (void*)primitiveCogStaticSymbols}, +#endif /* VMInvestigations */ + {(void*)_m, "primitiveCompareBytes\000\001\001", (void*)primitiveCompareBytes}, + {(void*)_m, "primitiveCompareWith\000\001\000", (void*)primitiveCompareWith}, +#if VMInvestigations + {(void*)_m, "primitiveContextXray\000\001\000", (void*)primitiveContextXray}, +#endif /* VMInvestigations */ + {(void*)_m, "primitiveCrashVM\000\000\000", (void*)primitiveCrashVM}, + {(void*)_m, "primitiveDisablePowerManager\000\000\000", (void*)primitiveDisablePowerManager}, + {(void*)_m, "primitiveDivideLargeIntegers\000\000\001", (void*)primitiveDivideLargeIntegers}, + {(void*)_m, "primitiveDivLargeIntegers\000\000\001", (void*)primitiveDivLargeIntegers}, + {(void*)_m, "primitiveEqualLargeIntegers\000\000\001", (void*)primitiveEqualLargeIntegers}, + {(void*)_m, "primitiveEventProcessingControl\000\000\000", (void*)primitiveEventProcessingControl}, + {(void*)_m, "primitiveExternalCallNoOp\000\377\000", (void*)primitiveExternalCallNoOp}, + {(void*)_m, "primitiveFastCNoOp\000\377\001", (void*)primitiveFastCNoOp}, + {(void*)_m, "primitiveFastCNoOpAlignedForFloats\000\377\003", (void*)primitiveFastCNoOpAlignedForFloats}, + {(void*)_m, "primitiveGetenv\000\000\000", (void*)primitiveGetenv}, + {(void*)_m, "primitiveGetLogDirectory\000\377\000", (void*)primitiveGetLogDirectory}, + {(void*)_m, "primitiveGreaterOrEqualLargeIntegers\000\000\001", (void*)primitiveGreaterOrEqualLargeIntegers}, + {(void*)_m, "primitiveGreaterThanLargeIntegers\000\000\001", (void*)primitiveGreaterThanLargeIntegers}, + {(void*)_m, "primitiveHeartbeatFrequency\000\000\000", (void*)primitiveHeartbeatFrequency}, + {(void*)_m, "primitiveHighResClock\000\377\001", (void*)primitiveHighResClock}, + {(void*)_m, "primitiveImageFormatVersion\000\377\000", (void*)primitiveImageFormatVersion}, + {(void*)_m, "primitiveInterruptChecksPerMSec\000\377\000", (void*)primitiveInterruptChecksPerMSec}, + {(void*)_m, "primitiveIsBigEnder\000\377\000", (void*)primitiveIsBigEnder}, + {(void*)_m, "primitiveLessOrEqualLargeIntegers\000\000\001", (void*)primitiveLessOrEqualLargeIntegers}, + {(void*)_m, "primitiveLessThanLargeIntegers\000\000\001", (void*)primitiveLessThanLargeIntegers}, +#if LRPCheck + {(void*)_m, "primitiveLongRunningPrimitive\000\377\000", (void*)primitiveLongRunningPrimitive}, +#endif /* LRPCheck */ +#if LRPCheck + {(void*)_m, "primitiveLongRunningPrimitiveSemaphore\000\000\000", (void*)primitiveLongRunningPrimitiveSemaphore}, +#endif /* LRPCheck */ +#if VMInvestigations + {(void*)_m, "primitiveMethodIsCogged\000\000\000", (void*)primitiveMethodIsCogged}, +#endif /* VMInvestigations */ + {(void*)_m, "primitiveMethodPCData\000\000\000", (void*)primitiveMethodPCData}, + {(void*)_m, "primitiveMillisecondClockMask\000\377\000", (void*)primitiveMillisecondClockMask}, + {(void*)_m, "primitiveMinimumUnusedHeadroom\000\377\000", (void*)primitiveMinimumUnusedHeadroom}, + {(void*)_m, "primitiveModLargeIntegers\000\000\001", (void*)primitiveModLargeIntegers}, + {(void*)_m, "primitiveMultipleBytecodeSetsActive\000\000\000", (void*)primitiveMultipleBytecodeSetsActive}, + {(void*)_m, "primitiveMultiplyLargeIntegers\000\000\001", (void*)primitiveMultiplyLargeIntegers}, + {(void*)_m, "primitiveNotEqualLargeIntegers\000\000\001", (void*)primitiveNotEqualLargeIntegers}, + {(void*)_m, "primitivePathToUsing\000\000\000", (void*)primitivePathToUsing}, + {(void*)_m, "primitiveProfilePrimitive\000\377\001", (void*)primitiveProfilePrimitive}, + {(void*)_m, "primitiveProfileSample\000\377\001", (void*)primitiveProfileSample}, + {(void*)_m, "primitiveProfileSemaphore\000\000\014", (void*)primitiveProfileSemaphore}, + {(void*)_m, "primitiveProfileStart\000\000\001", (void*)primitiveProfileStart}, + {(void*)_m, "primitiveQuoLargeIntegers\000\000\001", (void*)primitiveQuoLargeIntegers}, + {(void*)_m, "primitiveRemLargeIntegers\000\000\001", (void*)primitiveRemLargeIntegers}, + {(void*)_m, "primitiveScreenDepth\000\377\000", (void*)primitiveScreenDepth}, + {(void*)_m, "primitiveScreenScaleFactor\000\377\000", (void*)primitiveScreenScaleFactor}, + {(void*)_m, "primitiveSetGCSemaphore\000\000\000", (void*)primitiveSetGCSemaphore}, + {(void*)_m, "primitiveSetLogDirectory\000\001\000", (void*)primitiveSetLogDirectory}, + {(void*)_m, "primitiveSubtractLargeIntegers\000\000\001", (void*)primitiveSubtractLargeIntegers}, + {(void*)_m, "primitiveSuspendBackingUpV2\000\002\000", (void*)primitiveSuspendBackingUpV2}, +#if TestingPrimitives + {(void*)_m, "primitiveTestShortenIndexableSize\000\001\000", (void*)primitiveTestShortenIndexableSize}, +#endif /* TestingPrimitives */ + {(void*)_m, "primitiveUtcWithOffset\000\001\000", (void*)primitiveUtcWithOffset}, + {(void*)_m, "primitiveVoidReceiver\000\377\000", (void*)primitiveVoidReceiver}, + {NULL, NULL, NULL} +}; diff --git a/src/spur64.cog.livetyping/gcc3x-cointerpmt.c b/src/spur64.cog.livetyping/gcc3x-cointerpmt.c new file mode 100644 index 0000000000..c32c401ccf --- /dev/null +++ b/src/spur64.cog.livetyping/gcc3x-cointerpmt.c @@ -0,0 +1,87679 @@ +/* This file has been post-processed for GNU C */ + + +/* Automatically generated by + CCodeGeneratorGlobalStructure VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba + (Cog-eem.511, Compiler-ct.535) + from + CoInterpreterMT VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba + */ +static char __buildInfo[] = "CoInterpreterMT VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba " __DATE__ ; +char *__interpBuildInfo = __buildInfo; + + + +#if USE_GLOBAL_STRUCT +# define SQ_USE_GLOBAL_STRUCT 1 +#else +# define SQ_USE_GLOBAL_STRUCT 0 +#endif +#if USE_GLOBAL_STRUCT_REG +# define SQ_USE_GLOBAL_STRUCT_REG 1 +#else +# define SQ_USE_GLOBAL_STRUCT_REG 0 +#endif + +#include "sqGnu.h" +#include /* for printf */ +#include /* for e.g. alloca */ +#include +#include /* for wint_t */ +#include "vmCallback.h" +#include "sqMemoryFence.h" +#include "sqImageFileAccess.h" +#include "sqSetjmpShim.h" +#include "dispdbg.h" +#include "sqCogStackAlignment.h" +#include "cogmethod.h" +#include "cointerp.h" +#include "cogit.h" +#include "sqAtomicOps.h" +#include + + +/* StackInterpreter class>>preambleCCode */ +/* Disable Intel compiler inlining of warning which is used for breakpoints */ +#pragma auto_inline(off) +sqInt warnpid, erroronwarn; +EXPORT(void) +warning(const char *s) { /* Print an error message but don't necessarily exit. */ + if (erroronwarn) error(s); + if (warnpid) + printf("\n%s pid %ld\n", s, (long)warnpid); + else + printf("\n%s\n", s); +} +EXPORT(void) +warningat(const char *s, int l) { /* ditto with line number. */ + /* use alloca to call warning so one does not have to remember to set multiple breakpoints... */ + char *sl = alloca(strlen(s) + 16); + sprintf(sl, "%s %d", s, l); + warning(sl); +} +EXPORT(void) +warninginat(const char *s, const char *f, int l) { /* ditto with function name & line number. */ + /* use alloca to call warning so one does not have to remember to set multiple breakpoints... */ + char *sl = alloca(strlen(s) + strlen(f) + 17); + sprintf(sl, "%s %s %d", s, f, l); + warning(sl); +} +#pragma auto_inline(on) + +/* end StackInterpreter class>>preambleCCode */ + + +/*** Constants ***/ +#define ActiveProcessIndex 1 +#define AlternateHeaderHasPrimFlag 0x80000 +#define AlternateHeaderNumLiteralsMask 0x7FFF +#define AltLongStoreBytecode 245 +#define BecameActiveClassFlag 8 +#define BecameCompiledMethodFlag 2 +#define BecameJittedCompiledMethodFlag 16 +#define BecamePointerObjectFlag 1 +#define CacheProbeMax 3 +#define ClassAlien 52 +#define ClassArray 7 +#define ClassArrayCompactIndex 51 +#define ClassBitmap 4 +#define ClassBitmapCompactIndex 53 +#define ClassBlockClosure 36 +#define ClassBlockClosureCompactIndex 37 +#define ClassByteArray 26 +#define ClassByteString 6 +#define ClassByteStringCompactIndex 52 +#define ClassCharacter 19 +#define ClassDoubleByteArray 31 +#define ClassDoubleWordArray 33 +#define ClassExternalAddress 43 +#define ClassExternalData 45 +#define ClassExternalFunction 46 +#define ClassExternalLibrary 47 +#define ClassExternalStructure 44 +#define ClassFloat 9 +#define ClassFloatCompactIndex 34 +#define ClassFullBlockClosureCompactIndex 38 +#define ClassLargeNegativeInteger 42 +#define ClassLargeNegativeIntegerCompactIndex 32 +#define ClassLargePositiveInteger 13 +#define ClassLargePositiveIntegerCompactIndex 33 +#define ClassMessage 15 +#define ClassMessageCompactIndex 35 +#define ClassMethodContext 10 +#define ClassMethodContextCompactIndex 36 +#define ClassPoint 12 +#define ClassPointCompactIndex 54 +#define ClassSemaphore 18 +#define ClassSmallInteger 5 +#define ClassUnsafeAlien 54 +#define ClassWordArray 32 +#define ClosureFirstCopiedValueIndex 3 +#define ClosureIndex 4 +#define ClosureNumArgsIndex 2 +#define ClosureOuterContextIndex 0 +#define ClosureStartPCIndex 1 +#define CMBlock 4 +#define CMClosedPIC 2 +#define CMMethod 5 +#define CMMethodFlaggedForBecome 6 +#define CMOpenPIC 3 +#define ConstMinusOne 0xFFFFFFFFFFFFFFF9ULL +#define ConstOne 9 +#define ConstTwo 17 +#define ConstZero 1 +#define CrossedX 258 +#define CSCheckEvents 18 +#define CSEnterCriticalSection 2 +#define CSExitCriticalSection 4 +#define CSFromInterpreter 0 +#define CSFromMachineCode 1 +#define CSResume 6 +#define CSSetPriority 16 +#define CSSuspend 10 +#define CSSwitchIfNeccessary 28 +#define CSSynchronousSignal 8 +#define CSThreadBind 26 +#define CSTryToExecuteSmalltalk 30 +#define CSWait 12 +#define CSYield 14 +#define CTMAssignableOrInVM 3 +#define CTMInitializing 1 +#define CTMUnavailable 2 +#define CTMUninitialized 0 +#define CTMUnknownOwner -1 +#define CTMWantingOwnership 4 +#define CtxtTempFrameStart 6 +#define DisownVMForFFICallFlag 8 +#define DisownVMForProcessorRelinquishFlag 32 +#define DisownVMFromCallbackFlag 16 +#define EncounteredUnknownBytecode -6 +#define EndOfRun 0x101 +#define ExcessSignalsIndex 2 +#define ExternalCallLiteralFlagsIndex 2 +#define ExternalCallLiteralFunctionNameIndex 1 +#define ExternalCallLiteralModuleNameIndex 0 +#define ExternalCallLiteralTargetFunctionIndex 3 +#define ExternalObjectsArray 38 +#define ExtraRootsSize 64 +#define FalseObject 1 +#define FirstLinkIndex 0 +#define ForeignCallbackProcess 56 +#define FoxCallerSavedIP 8 +#define FoxIFReceiver -40 +#define FoxIFrameFlags -24 +#define FoxIFSavedIP -32 +#define FoxMethod -8 +#define FoxMFReceiver -24 +#define FoxSavedFP 0 +#define FoxThisContext -16 +#define FullClosureCompiledBlockIndex 1 +#define FullClosureFirstCopiedValueIndex 4 +#define FullClosureReceiverIndex 3 +#define GCCheckFreeSpace 32 +#define GCCheckPrimCall 128 +#define GCCheckShorten 64 +#define GCModeBecome 8 +#define GCModeFull 1 +#define GCModeImageSegment 16 +#define GCModeNewSpace 2 +#define HasBeenReturnedFromMCPC -1 +#define HasBeenReturnedFromMCPCOop 0xFFFFFFFFFFFFFFF9ULL +#define HashMultiplyConstant 1664525 +#define HeaderIndex 0 +#define IFrameSlots 7 +#define InstanceSpecificationIndex 2 +#define InstructionPointerIndex 1 +#define KeyIndex 0 +#define LargeContextBit 0x100000 +#define LargeContextSlots 62 +#define LastLinkIndex 1 +#define LiteralStart 1 +#define LongStoreBytecode 129 +#define MarkOnTenure 5 +#define MarkStackRootIndex 0x1000 +#define MaxExternalPrimitiveTableSize 4096 +#define MaxLiteralCountForCompile 60 +#define MaxNegativeErrorCode -8 +#define MaxPrimitiveIndex 583 +#define MaxQuickPrimitiveIndex 519 +#define MaxRTRefCount 7 +#define MessageArgumentsIndex 1 +#define MessageLookupClassIndex 2 +#define MessageSelectorIndex 0 +#define MethodArrayIndex 1 +#define MethodCacheClass 2 +#define MethodCacheEntries 0x400 +#define MethodCacheEntrySize 4 +#define MethodCacheMask 0xFFC +#define MethodCacheMethod 3 +#define MethodCachePrimFunction 4 +#define MethodCacheSelector 1 +#define MethodCacheSize 4096 +#define MethodDictionaryIndex 1 +#define MethodHeaderArgCountShift 27 +#define MethodHeaderFlagBitPosition 31 +#define MethodHeaderTempCountShift 21 +#define MethodIndex 3 +#define MethodTooBig -4 +#define MFMethodFlagHasContextFlag 1 +#define MFMethodFlagIsBlockFlag 2 +#define MFMethodFlagsMask 0x7 +#define MFMethodMask -0x8 +#define MFrameSlots 5 +#define MillisecondClockMask 0x1FFFFFFF +#define MinBackwardJumpCountForCompile 40 +#define MournQueueRootIndex 4098 +#define MultipleBytecodeSetsBitmask 0x200 +#define MyListIndex 3 +#define NextLinkIndex 0 +#define NilObject 0 +#define NotFullyInitialized -1 +#define ObjStackFixedSlots 4 +#define ObjStackFreex 2 +#define ObjStackLimit 0xFF8 +#define ObjStackMyx 1 +#define ObjStackNextx 3 +#define ObjStackPageSlots 0xFFC +#define ObjStackTopx 0 +#define OldBecameNewFlag 4 +#define OwnedVMFromForeignThreadFlag 2 +#define OwnerLogSize 65536 +#define OwnVMForFFIExceptionFlag 16 +#define PrimCallCollectsProfileSamples 16 +#define PrimCallIsExternalCall 32 +#define PrimCallMayEndureCodeCompaction 8 +#define PrimCallNeedsNewMethod 4 +#define PrimCallOnSmalltalkStack 1 +#define PrimCallOnSmalltalkStackAlign2x 2 +#define PrimitiveErrorTableIndex 51 +#define PrimNumberDoExternalCall 218 +#define PrimNumberDoPrimitive 118 +#define PrimNumberExternalCall 117 +#define PrimNumberFFICall 120 +#define PrimNumberHandlerMarker 199 +#define PrimNumberNoContextSwitchMarker 123 +#define PrimNumberRelinquishProcessor 230 +#define PrimNumberUnwindMarker 198 +#define PrimNumberVMParameter 254 +#define PrimTraceLogSize 256 +#define PriorityIndex 2 +#define ProcessListsIndex 0 +#define ProcessSignalingLowSpace 22 +#define ReceiverIndex 5 +#define RemapBufferSize 25 +#define RememberedSetRootIndex 4099 +#define ReturnToInterpreter 1 +#define ScavengeInProgress 1 +#define SchedulerAssociation 3 +#define SelectorAboutToReturn 48 +#define SelectorAttemptToAssign 50 +#define SelectorCannotInterpret 34 +#define SelectorCannotReturn 21 +#define SelectorDoesNotUnderstand 20 +#define SelectorInvokeCallback 53 +#define SelectorMustBeBoolean 25 +#define SelectorRunWithIn 49 +#define SelectorStart 2 +#define SelectorUnknownBytecode 57 +#define SenderIndex 0 +#define ShouldNotJIT -8 +#define SlidingCompactionInProgress 2 +#define SmallContextSlots 22 +#define SpecialSelectors 23 +#define SpurPrimitiveAccessorDepthShift 8 +#define SpurPrimitiveFlagsMask 0xFF +#define StackPageReachedButUntraced 1 +#define StackPageTraced 2 +#define StackPageTraceInvalid -1 +#define StackPageUnreached 0 +#define StackPointerIndex 2 +#define SuperclassIndex 0 +#define SuspendedContextIndex 1 +#define TenureByAge 1 +#define TenureByClass 2 +#define TenureToShrinkRT 3 +#define TheDisplay 14 +#define TheFinalizationSemaphore 41 +#define TheInterruptSemaphore 30 +#define TheLowSpaceSemaphore 17 +#define TheTimerSemaphore 29 +#define ThreadAffinityIndex 4 +#define ThreadIdShift 16 +#define TopHashBit 0x200000 +#define TopOopBit 0x8000000000000000ULL +#define TraceAttemptOwnVM 12 +#define TraceBlockActivation 2 +#define TraceBlockCreation 3 +#define TraceBufferSize 768 +#define TraceCodeCompaction 6 +#define TraceContextSwitch 1 +#define TraceDisownVM 14 +#define TraceFullGC 5 +#define TraceIncrementalGC 4 +#if !defined(TraceLog) /* Allow this to be overridden on the compiler command line */ +# define TraceLog 0 +#endif +#define TraceOwnerShift 16 +#define TraceOwnVM 13 +#define TracePreemptDisowningThread 16 +#define TracePrimitiveFailure 10 +#define TracePrimitiveRetry 11 +#define TraceSend 0 +#define TraceStackOverflow 9 +#define TraceThreadSwitch 15 +#define TraceTypeShift 8 +#define TraceVMCallback 7 +#define TraceVMCallbackReturn 8 +#define TrueObject 2 +#define ValidatedClassDoubleByteArrayFlag 1 +#define ValidatedClassDoubleWordArrayFlag 4 +#define ValidatedClassWordArrayFlag 2 +#define ValueIndex 1 +#define VMAlreadyOwnedHenceDoNotDisownFlag 1 +#define WeaklingStackRootIndex 0x1001 +#define XIndex 0 +#define YIndex 1 + +typedef struct { + usqInt sEdenBytes; + usqInt sPastBytes; + usqInt sRememberedSetSize; + usqInt sRememberedSetRedZone; + usqInt sRememberedSetLimit; + usqInt sStatTenures; + usqInt tTenureCriterion; + usqInt tTenureThreshold; + usqInt tRefCountToShrinkRT; + usqInt eSurvivorBytes; + usqInt eRememberedSetSize; + usqInt eStatTenures; + } SpurScavengeLogRecord; + + +typedef struct { + usqInt segStart; + usqInt segSize; + sqInt swizzle; + usqInt containsPinned; + usqInt savedSegSize; + usqInt lastFreeObject; + } SpurSegmentInfo; + + +typedef struct { + usqInt start; + usqInt limit; + } SpurNewSpaceSpace; + + +typedef struct { + usqInt start; + usqInt limit; + usqInt top; + } SpurContiguousObjStack; + + +typedef struct _StackPage { + char *stackLimit; + char *headSP; + char *headFP; + char *baseFP; + char *baseAddress; + char *realStackLimit; + char *lastAddress; + int trace; + int padToWord; + struct _StackPage *nextPage; + struct _StackPage *prevPage; + } StackPage; + +#define CogStackPage StackPage + + +typedef struct _DisownState { + sqInt newMethodOrNull; + usqIntptr_t cStackPointer; + usqIntptr_t cFramePointer; + unsigned char argumentCount; +#ifndef NDEBUG + unsigned char inMachineCode; +#endif +#ifndef NDEBUG + unsigned char stackPtrIndex; +#endif + struct _DisownState *nestedDisownStates; + } DisownState; + + +typedef struct { + sqInt newMethodOrNull; + usqIntptr_t cStackPointer; + usqIntptr_t cFramePointer; + unsigned char argumentCount; +#ifndef NDEBUG + unsigned char inMachineCode; +#endif +#ifndef NDEBUG + unsigned char stackPtrIndex; +#endif + struct _DisownState *nestedDisownStates; + sqInt index; + volatile atomic_int state; + sqInt priority; + sqInt boundProcess; + usqIntptr_t bindingPointKey; + sqInt affinityOnBind; + sqOSSemaphore osSemaphore; + sqOSThread osThread; + jmp_buf reenterThreadSchedulingLoop; + } CogVMThread; + + +typedef struct { + usqLong timestamp; + int vmOwner; + int successfulSwitch; + } CogVMOwnerLog; + + + +/*** Function Prototypes ***/ + + +#if !PRODUCTION && defined(PlatformNoDbgRegParms) +# define NoDbgRegParms PlatformNoDbgRegParms +#endif + +#if !defined(NoDbgRegParms) +# define NoDbgRegParms /*empty*/ +#endif + + + +#if !defined(NeverInline) +# define NeverInline /*empty*/ +#endif + +extern sqInt interpret(void); +static NoDbgRegParms CogMethod * cmHomeMethod(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMBlock(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMClosedPIC(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMMethodEtAl(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMMethodFlaggedForBecome(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms int isCMOpenPIC(CogBlockMethod *self_in_CogBlockMethod); +static NoDbgRegParms sqInt addressIsInPage(StackPage *self_in_CogStackPage, char *address); +static NoDbgRegParms int isFree(StackPage *self_in_CogStackPage); +static NoDbgRegParms sqInt freeStackPageNoAssert(StackPage *aPage); +static NoDbgRegParms void freeStackPage(StackPage *aPage); +static NoDbgRegParms sqInt markStackPageMostRecentlyUsed(StackPage *page); +static NoDbgRegParms sqInt markStackPageNextMostRecentlyUsed(StackPage *page); +static StackPage * newStackPage(void); +static sqInt pageListIsWellFormed(void); +static NoDbgRegParms StackPage * stackPageAt(sqInt index); +static NoDbgRegParms StackPage * stackPageFor(void *pointer); +static double statAverageLivePagesWhenMapping(void); +extern int vmIsInitialized(void); +static NoDbgRegParms CogVMThread * affinedThreadOrNilForProcesscurrentAffinityoop(sqInt aProcess, sqInt currentAffinity, sqInt threadAffinityOop); +static CogVMThread * currentVMThread(void); +extern void dumpOwnerLog(void); +extern void dumpOwnerLogMax(sqInt maxEntries); +extern void dumpOwnerLogOn(FILE *aFile); +extern void dumpOwnerLogOnMax(FILE *aFile, sqInt maxEntries); +static CogVMThread * ensureWillingThread(void); +extern sqOSThread getVMOSThread(void); +static sqInt getVMOwner(void); +static NoDbgRegParms sqInt growThreadInfosToAtLeast(sqInt index); +static NoDbgRegParms sqInt saveOwnerSwitchTosuccessful(sqInt newOwner, sqInt aBoolean); +static NoDbgRegParms sqInt startThreadForThreadIndex(sqInt index); +static NoDbgRegParms sqInt startThreadForThreadInfo(CogVMThread *vmThread); +static sqInt startThreadSubsystem(void); +static NoDbgRegParms void startVMThread(CogVMThread *vmThread); +static NoDbgRegParms sqInt threadIndexisCompatibleWith(sqInt aThreadIndex, sqInt threadAffinity); +static CogVMThread * unusedThreadInfo(void); +extern sqInt vmIsOwned(void); +static int vmIsOwnedOrLocked(void); +static NoDbgRegParms sqInt vmOwnerIsCompatibleWith(sqInt processThreadId); +static NoDbgRegParms int vmOwnerIs(sqInt index); +static NoDbgRegParms CogVMThread * vmThreadAt(sqInt index); +static NoDbgRegParms void wakeVMThread(CogVMThread *vmThread); +static CogVMThread * willingVMThread(void); +static NoDbgRegParms CogVMThread * initializeThreadState(CogVMThread *self_in_CogVMThread); +static NoDbgRegParms sqInt vmThreadState(CogVMThread *self_in_CogVMThread); +extern sqInt accessorDepthForPrimitiveIndex(sqInt primIndex); +static NoDbgRegParms void activateNewCogMethodinInterpreter(CogMethod *cogMethod, sqInt inInterpreter); +static void activateNewMethod(void); +static NoDbgRegParms void addNewMethodToCache(sqInt classObj); +extern usqInt argumentCountAddress(void); +static NoDbgRegParms CogMethod * asCogHomeMethod(CogBlockMethod *aCogMethod); +static NoDbgRegParms void assertValidExecutionPointersimbarline(usqInt lip, char *lifp, char *lisp, sqInt inInterpreter, sqInt ln); +static NoDbgRegParms sqInt assertValidStackedInstructionPointersInline(StackPage *aStackPage, sqInt ln); +static NoDbgRegParms void assertValidStackedInstructionPointers(sqInt ln); +static NoDbgRegParms void attemptToSwitchToMachineCode(sqInt bcpc); +static NoDbgRegParms void backupContexttoBlockingSendTo(sqInt suspendedContext, sqInt conditionVariable); +extern void callForCogCompiledCodeCompaction(void); +static NoDbgRegParms void callRegisterArgCogMethodatreceiver(CogMethod *cogMethod, sqInt entryOffset, sqInt rcvr); +extern void ceActivateFailingPrimitiveMethod(sqInt aPrimitiveMethod); +extern sqInt ceBaseFrameReturn(sqInt returnValue); +#if IMMUTABILITY +extern sqInt ceCannotAssignTowithIndexvalueToAssign(sqInt immutableObject, sqInt index, sqInt valueToAssign); +#endif /* IMMUTABILITY */ +extern sqInt ceCannotResume(void); +extern void ceCheckAndMaybeRetryPrimitive(sqInt primIndex); +extern sqInt ceContextinstVar(sqInt maybeContext, sqInt slotIndex); +extern sqInt ceContextinstVarvalue(sqInt maybeMarriedContext, sqInt slotIndex, sqInt anOop); +extern sqInt ceInterpretMethodFromPICreceiver(sqInt aMethodObj, sqInt rcvr); +extern sqInt ceMNUFromPICMNUMethodreceiver(sqInt aMethodObj, sqInt rcvr); +extern sqInt ceNewHashOf(sqInt anObject); +extern sqInt ceNonLocalReturn(sqInt returnValue); +extern void ceReapAndResetErrorCodeFor(CogMethod *cogMethod); +static NoDbgRegParms sqInt ceSendAborttonumArgs(sqInt selector, sqInt rcvr, sqInt numArgs); +extern sqInt ceSendFromInLineCacheMiss(CogMethod *cogMethodOrPIC); +extern sqInt ceSendMustBeBooleanTointerpretingAtDelta(sqInt aNonBooleanObject, sqInt jumpSize); +extern sqInt ceSendMustBeBoolean(sqInt anObject); +extern void ceSendaboveClassBindingtonumArgs(sqInt selector, sqInt methodClassBinding, sqInt rcvr, sqInt numArgs); +extern sqInt ceSendabovetonumArgs(sqInt selector, sqInt methodClass, sqInt rcvr, sqInt numArgs); +extern sqInt ceSendsupertonumArgs(sqInt selector, sqInt superNormalBar, sqInt rcvr, sqInt numArgs); +extern void ceStackOverflow(sqInt contextSwitchIfNotNil); +extern void ceTakeProfileSample(void); +extern void ceTraceBlockActivation(void); +extern void ceTraceLinkedSend(sqInt theReceiver); +extern void ceTraceStoreOfinto(sqInt aValue, sqInt anObject); +extern usqInt cFramePointerAddress(void); +extern void checkAssertsEnabledInCoInterpreter(void); +static NoDbgRegParms sqInt checkCodeIntegrity(sqInt gcModes); +extern sqInt checkForAndFollowForwardedPrimitiveState(void); +extern sqInt checkIfCFramePointerInUse(void); +#if LRPCheck +extern int checkingLongRunningPrimitives(void); +#endif /* LRPCheck */ +static NoDbgRegParms sqInt checkOkayFields(sqInt oop); +static NeverInline void checkProfileTickPostPrimitive(void); +static NoDbgRegParms sqInt cogMethodHasRealNonProfilingPrimitive(CogMethod *cogMethod); +extern sqInt cogMethodHasTooManyLiterals(CogMethod *aCogMethod); +extern CogMethod * cogMethodOf(sqInt aMethodOop); +static void commenceCogCompiledCodeCompaction(void); +extern void compilationBreakpointFor(sqInt selectorOop); +static NoDbgRegParms sqInt contextInstructionPointerframe(sqInt theIP, char *theFP); +static NoDbgRegParms void convertFrametoInterpreterFrame(char *theFP, sqInt pcDelta); +extern usqInt cReturnAddressAddress(void); +extern usqInt cStackPointerAddress(void); +static NoDbgRegParms sqInt divorceAllFramesSuchThat(sqInt (*criterion)(char *fp)); +extern void dumpStackAlignmentInfo(void); +static NoDbgRegParms void ensureAllContextsHaveBytecodePCsIf(sqInt (*criterion)(sqInt methodOop)); +static NoDbgRegParms void ensureContextHasBytecodePC(sqInt aContext); +static NoDbgRegParms void ensureContextIsExecutionSafeAfterAssignToStackPointer(sqInt aContext); +extern void executeCogMethodfromLinkedSendWithReceiver(CogMethod *cogMethod, sqInt rcvr); +static NoDbgRegParms void executeCogMethodfromUnlinkedSendWithReceiver(CogMethod *cogMethod, sqInt rcvr); +extern void executeCogPICfromLinkedSendWithReceiverandCacheTag(CogMethod *cogPIC, sqInt rcvr, sqInt cacheTag); +static sqInt executeNewMethod(void); +static sqInt executeNewMethodJitting(void); +static NoDbgRegParms sqInt externalInstVarofContext(sqInt offset, sqInt aContext); +static NoDbgRegParms void findNewMethodInClassTag(sqInt classTagArg); +static NoDbgRegParms void flagMethodAsInterpreted(sqInt aMethod); +static void flushExternalPrimitives(void); +static NoDbgRegParms void flushMethodsWithMachineCodePrimitivesAndContinueAnswering(sqInt result); +static void followForwardedFieldsInCurrentMethod(void); +static void followForwardingPointersOfReceiverAndTemporariesInStackZone(void); +static void followForwardingPointersOfReceiversInStackZone(void); +static NoDbgRegParms sqInt frameCallerContext(char *theFP); +static NoDbgRegParms int frameHasContext(char *theFP); +static NoDbgRegParms int frameIsBlockActivation(char *theFP); +static NoDbgRegParms sqInt frameMethodObject(char *theFP); +static NoDbgRegParms usqInt frameNumArgs(char *theFP); +extern usqInt framePointerAddress(void); +static NoDbgRegParms void framePrintDescription(sqInt it); +static NoDbgRegParms sqInt frameReceiver(char *theFP); +extern void (*functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(sqInt methodObj, sqInt primitiveIndex, sqInt *flagsPtr))(void); +extern sqInt getCurrentBytecode(void); +static NoDbgRegParms sqInt handleForwardedSendFaultForReceiverstackDelta(sqInt forwardedReceiver, sqInt stackDelta); +static NoDbgRegParms sqInt handleMNUInMachineCodeToclassForMessage(sqInt selectorIndex, sqInt rcvr, sqInt classForMessage); +static NoDbgRegParms int iframeIsBlockActivation(char *theFP); +static NoDbgRegParms sqInt iframeReceiver(char *theFP); +static NoDbgRegParms usqInt iframeSavedIP(char *theFP); +extern void ifValidWriteBackStackPointersSaveTo(void *theCFP, void *theCSP, char **savedFPP, char **savedSPP); +extern usqInt instructionPointerAddress(void); +static NoDbgRegParms sqInt instructionPointerForFramecurrentFPcurrentIP(char *spouseFP, char *currentFP, sqInt instrPtr); +static NeverInline sqInt interpreterAllocationReserveBytes(void); +static sqInt interpretMethodFromMachineCode(void); +static NoDbgRegParms int isCogMethodReferenceNoAssert(sqInt methodHeader); +extern int isCogMethodReference(sqInt methodHeader); +static NoDbgRegParms sqInt isExternalMethodInPlugin(sqInt methodObj); +extern sqInt isFullBlockMethod(sqInt aMethodObj); +static NoDbgRegParms sqInt isMachineCodeFrameForCogMethodFlaggedForBecome(char *theFP); +static NoDbgRegParms sqInt isMachineCodeFrameForCogMethodWithMachineCodePrim(char *theFP); +static NoDbgRegParms sqInt isMachineCodeFrameForCogMethodWithTooManyLiterals(char *theFP); +static NoDbgRegParms sqInt isMachineCodeFrameForExternalPrimitiveMethod(char *theFP); +static NoDbgRegParms sqInt isMachineCodeFrameForRealNonProfilingPrimitive(char *theFP); +static NoDbgRegParms int isMachineCodeFrame(char *theFP); +static NoDbgRegParms sqInt isRealNonProfilingPrimitiveMethod(sqInt methodObj); +static NoDbgRegParms sqInt isRealNonProfilingPrimitiveMethodprimIndex(sqInt methodObj, sqInt primitiveIndex); +static NoDbgRegParms sqInt isWidowedContextDuringGC(sqInt aOnceMarriedContext); +static NoDbgRegParms sqInt isWidowedContext(sqInt aOnceMarriedContext); +static NoDbgRegParms sqInt ismethodAssignmentToContextWithMachineCodePC(sqInt fieldIndex, sqInt anOop); +#if LRPCheck +extern usqInt longRunningPrimitiveStopUsecsAddress(void); +#endif /* LRPCheck */ +extern sqInt lookupMNUreceiver(sqInt selector, sqInt rcvr); +extern sqInt lookupOrdinaryreceiver(sqInt selector, sqInt rcvr); +static NoDbgRegParms StackPage * makeBaseFrameFor(sqInt aContext); +static NeverInline void mapStackPages(void); +extern void markActiveMethodsAndReferents(void); +static NoDbgRegParms void markAndTraceStackPage(StackPage *thePage); +static NoDbgRegParms void markCogMethodsAndReferentsOnPage(StackPage *thePage); +static NoDbgRegParms usqInt marryFrameSP(char *theFP, char *theSP); +extern sqInt maxLookupNoMNUErrorCode(void); +extern usqInt methodCacheAddress(void); +extern int methodHasCogMethod(sqInt aMethodOop); +static NoDbgRegParms sqInt methodHasMachineCodePrimitiveMethod(sqInt methodObj); +static NoDbgRegParms NeverInline sqInt methodHasPrimitiveInPrimTracePlugin(sqInt aMethodObj); +static NoDbgRegParms sqInt methodHasTooManyLiterals(sqInt methodObj); +#if VMInvestigations +extern int methodHeaderHasPrimitive(sqInt methodHeader); +#endif /* VMInvestigations */ +extern int methodNeedsLargeContext(sqInt methodObj); +extern sqInt methodShouldBeCogged(sqInt aMethodObj); +static NoDbgRegParms int methodWithHeaderShouldBeCogged(sqInt methodHeader); +static NoDbgRegParms CogBlockMethod * mframeCogMethod(char *theFP); +extern CogMethod * mframeHomeMethodExport(void); +static NoDbgRegParms CogMethod * mframeHomeMethod(char *theFP); +static NoDbgRegParms int mframeIsBlockActivation(char *theFP); +static NoDbgRegParms sqInt mframeReceiver(char *theFP); +static NeverInline sqInt minimumUnusedHeadroom(void); +static NoDbgRegParms sqInt mnuMethodOrNilFor(sqInt rcvr); +static NoDbgRegParms char * moveFramesInthroughtoPage(StackPage *oldPage, char *theFP, StackPage *newPage); +static NoDbgRegParms sqInt mustMapMachineCodePCcontext(sqInt theIP, sqInt aOnceMarriedContext); +extern usqInt newMethodAddress(void); +extern usqInt nextProfileTickAddress(void); +extern sqInt noAssertHeaderOf(sqInt methodPointer); +extern sqInt positive32BitIntegerFor(unsigned int integerValue); +extern unsigned int positive32BitValueOf(sqInt oop); +extern sqInt positive64BitIntegerFor(usqLong integerValue); +extern usqLong positive64BitValueOf(sqInt oop); +static NoDbgRegParms void postGCAction(sqInt gcModeArg); +static NoDbgRegParms void preGCAction(sqInt gcModeArg); +extern usqInt primFailCodeAddress(void); +static NoDbgRegParms void primitiveBecomeReturn(sqInt ec); +static void primitiveExecuteMethod(void); +static void primitiveExecuteMethodArgsArray(void); +extern sqInt primitivePropertyFlagsprimIndex(sqInt methodObj, sqInt primIndex); +extern void * primTraceLogAddress(void); +extern usqInt primTraceLogIndexAddress(void); +extern void printCogMethod(CogMethod *cogMethod); +static NoDbgRegParms void printFrameFlagsForFP(char *theFP); +static NoDbgRegParms void printFrameMethodFor(char *theFP); +static NoDbgRegParms void printFrameThingatextra(char *name, char *address, sqInt extraValue); +extern sqInt printFrameWithSP(char *theFP, char *theSP); +extern void printMethodCacheFor(sqInt thing); +extern sqInt quickPrimitiveConstantFor(sqInt aQuickPrimitiveIndex); +extern sqInt (*quickPrimitiveGeneratorFor(sqInt aQuickPrimitiveIndex))(void); +extern sqInt quickPrimitiveInstVarIndexFor(sqInt primIndex); +extern sqInt rawHeaderOf(sqInt methodPointer); +extern void rawHeaderOfput(sqInt methodOop, sqInt cogMethodOrMethodHeader); +extern size_t readImageFromFileHeapSizeStartingAt(sqImageFile f, usqInt desiredHeapSize, squeakFileOffsetType imageOffset); +extern sqInt recordFastCCallPrimTraceForMethod(sqInt aMethodObj); +extern sqInt recordPrimTraceForMethod(sqInt aMethodObj); +static NeverInline void reloadPrimitiveCalloutPointer(void); +extern void reportMinimumUnusedHeadroom(void); +extern void reportMinimumUnusedHeadroomOn(FILE *aStdioStream); +static NoDbgRegParms sqInt returnToExecutivepostContextSwitch(sqInt inInterpreter, sqInt switchedContext); +static NoDbgRegParms sqInt returntoExecutive(sqInt returnValue, sqInt inInterpreter); +static NoDbgRegParms sqInt shortPrintFrame(char *theFP); +extern sqInt signed32BitIntegerFor(sqInt integerValue); +extern int signed32BitValueOf(sqInt oop); +extern sqInt signed64BitIntegerFor(sqLong integerValue); +extern sqLong signed64BitValueOf(sqInt oop); +extern sqInt specialSelectorNumArgs(sqInt index); +extern usqInt stackLimitAddress(void); +static sqInt stackLimitOffset(void); +static sqInt stackPageHeadroom(void); +extern usqInt stackPointerAddress(void); +static NoDbgRegParms sqInt stackPointerIndexForFrameWithSP(char *theFP, char *theSP); +extern usqInt startPCOfMethodHeader(sqInt aCompiledMethodHeader); +extern sqInt startPCOrNilOfLiteralin(sqInt lit, sqInt aMethodObj); +static NoDbgRegParms void tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFromtoreturnValue(sqInt contextToReturnFrom, sqInt contextToReturnTo, sqInt returnValue); +static NoDbgRegParms sqInt temporaryin(sqInt offset, char *theFP); +static NoDbgRegParms sqInt temporaryinput(sqInt offset, char *theFP, sqInt valueOop); +extern void updateStackZoneReferencesToCompiledCodePreCompaction(void); +static NoDbgRegParms void updateStateOfSpouseContextForFrameWithSP(char *theFP, char *theSP); +extern sqInt validInstructionPointerinMethodframePointer(usqInt instrPointer, usqInt aMethod, char *fp); +static NoDbgRegParms sqInt validStackPageBaseFrame(StackPage *aPage); +static NoDbgRegParms sqInt voidVMStateForSnapshotFlushingExternalPrimitivesIf(sqInt flushExtPrims); +extern char * whereIs(sqInt anOop); +static NoDbgRegParms NeverInline void widowOrForceToBytecodePC(sqInt ctxt); +extern void ceCheckForInterrupt(void); +static void cedeToHigherPriorityThreads(void); +static NoDbgRegParms sqInt checkForEventsMayContextSwitch(sqInt mayContextSwitch); +static void checkVMOwnershipFromHeartbeat(void); +static NoDbgRegParms sqInt deferStackLimitSmashAroundwith(void (*functionSymbol)(sqInt), sqInt arg); +extern sqInt disownVM(sqInt flags); +extern void dumpTraceLog(void); +static sqInt enterSmalltalkExecutiveImplementation(void); +extern void forceInterruptCheckFromHeartbeat(void); +static void initialEnterSmalltalkExecutive(void); +static NoDbgRegParms sqInt isBoundProcess(sqInt aProcess); +extern void loadInitialContext(void); +static void mapInterpreterOops(void); +static sqInt ownVMFromUnidentifiedThread(void); +extern sqInt ownVM(sqInt disownResult); +static void preemptDisowningThread(void); +extern void primitiveFailForFFIExceptionat(usqLong exceptionCode, usqInt pc); +static void primitiveForceDisplayUpdate(void); +EXPORT(void) primitiveGetOwnerLog(void); +static void primitiveProcessBindToThreadAffinity(void); +EXPORT(void) primitiveProcessBoundThreadId(void); +static void primitiveRelinquishProcessor(void); +static void primitiveShowDisplayRect(void); +EXPORT(void) primitiveVMCurrentThreadId(void); +extern void printAllStacks(void); +static NoDbgRegParms void printLogEntryAt(sqInt i); +extern void printThreads(void); +static NoDbgRegParms void returnToSchedulingLoopAndReleaseVMOrWakeThreadsource(CogVMThread *vmThread, sqInt source); +extern sqInt sendInvokeCallbackContext(VMCallbackContext *vmCallbackContext); +static NoDbgRegParms sqInt threadAffinityOfProcess(sqInt aProcess); +static NoDbgRegParms void threadSchedulingLoopImplementation(CogVMThread *vmThread); +static NoDbgRegParms void threadSwitchIfNecessaryfrom(sqInt newProc, sqInt sourceCode); +static NoDbgRegParms sqInt transferTofrom(sqInt newProcOrNil, sqInt sourceCode); +static NoDbgRegParms void waitingPriorityIsAtLeast(sqInt minPriority); +static NoDbgRegParms int frameIsMarked(sqInt theFPInt); +#if VMInvestigations +EXPORT(int) primitiveAllMethodsCompiledToMachineCode(void); +#endif /* VMInvestigations */ +#if VMInvestigations +EXPORT(void) primitiveBenchmarkFollowForwardersInStackZone(void); +#endif /* VMInvestigations */ +#if VMInvestigations +EXPORT(void) primitiveBenchmarkFollowForwardersOfReceiverAndTemporariesInStackZone(void); +#endif /* VMInvestigations */ +#if VMInvestigations +EXPORT(sqInt) primitiveBenchmarkJITReceiver(void); +#endif /* VMInvestigations */ +#if VMInvestigations +EXPORT(void) primitiveBenchmarkScavenge(void); +#endif /* VMInvestigations */ +static void primitiveClosureCopyWithCopiedValues(void); +#if VMInvestigations +EXPORT(sqInt) primitiveCogitMethod(void); +#endif /* VMInvestigations */ +#if VMInvestigations +EXPORT(sqInt) primitiveCogStaticSymbols(void); +#endif /* VMInvestigations */ +static void primitiveCollectCogCodeConstituents(void); +#if VMInvestigations +EXPORT(void) primitiveContextXray(void); +#endif /* VMInvestigations */ +static void primitiveEnterCriticalSection(void); +static void primitiveExitCriticalSection(void); +EXPORT(void) primitiveExternalCallNoOp(void); +EXPORT(void) primitiveFastCNoOp(void); +EXPORT(void) primitiveFastCNoOpAlignedForFloats(void); +static void primitiveFlushCacheByMethod(void); +static void primitiveFlushCacheBySelector(void); +extern usqInt primitiveFunctionPointerAddress(void); +#if LRPCheck +EXPORT(sqInt) primitiveLongRunningPrimitiveSemaphore(void); +#endif /* LRPCheck */ +#if VMInvestigations +EXPORT(sqInt) primitiveMethodIsCogged(void); +#endif /* VMInvestigations */ +EXPORT(sqInt) primitiveMethodPCData(void); +static void primitiveMethodXray(void); +EXPORT(void) primitiveMinimumUnusedHeadroom(void); +static void primitiveObjectAt(void); +static void primitiveObjectAtPut(void); +EXPORT(sqInt) primitiveProfileSemaphore(void); +static void primitiveResume(void); +static void primitiveSetPriority(void); +static void primitiveSignal(void); +static void primitiveSnapshot(void); +static void primitiveSnapshotEmbedded(void); +static void primitiveSuspend(void); +static void primitiveSuspendBackingUpV1(void); +EXPORT(void) primitiveSuspendBackingUpV2(void); +static void primitiveTerminateTo(void); +static void primitiveUnloadModule(void); +static void primitiveVoidVMState(void); +static void primitiveVoidVMStateForMethod(void); +static void primitiveWait(void); +static void primitiveYield(void); +static void unmarkAllFrames(void); +static NoDbgRegParms void initializeStacknumSlotspageSize(char *theStackPages, sqInt stackSlots, sqInt slotsPerPage); +static NoDbgRegParms sqInt pageIndexFor(void *pointer); +static NoDbgRegParms char * whereIsMaybeStackThing(sqInt anOop); +extern char * cStringOrNullFor(sqInt oop); +extern sqInt failed(void); +extern sqInt identityHashOf(sqInt anOop); +static NoDbgRegParms sqInt isNegativeIntegerValueOf(sqInt oop); +extern sqInt isPositiveMachineIntegerObject(sqInt oop); +static NoDbgRegParms sqInt magnitude64BitIntegerForneg(usqLong magnitude, sqInt isNegative); +static NoDbgRegParms usqLong magnitude64BitValueOf(sqInt oop); +static NoDbgRegParms usqIntptr_t positiveMachineIntegerValueOfObj(sqInt oop); +extern usqIntptr_t positiveMachineIntegerValueOf(sqInt oop); +static void primitiveAdd(void); +EXPORT(void) primitiveAddLargeIntegers(void); +static void primitiveAdoptInstance(void); +EXPORT(void) primitiveAllInstances(void); +EXPORT(void) primitiveAllObjects(void); +static void primitiveArctan(void); +static void primitiveArrayBecome(void); +static void primitiveArrayBecomeOneWay(void); +static void primitiveArrayBecomeOneWayCopyHashArg(void); +static void primitiveArrayBecomeOneWayNoCopyHash(void); +static void primitiveAsCharacter(void); +static void primitiveAsFloat(void); +static void primitiveAt(void); +static void primitiveAtPut(void); +static void primitiveBeCursor(void); +static void primitiveBeDisplay(void); +static void primitiveBeep(void); +static void primitiveBehaviorHash(void); +static void primitiveBitAnd(void); +EXPORT(void) primitiveBitAndLargeIntegers(void); +static void primitiveBitOr(void); +EXPORT(void) primitiveBitOrLargeIntegers(void); +static void primitiveBitShift(void); +EXPORT(void) primitiveBitShiftLargeIntegers(void); +static void primitiveBitXor(void); +EXPORT(void) primitiveBitXorLargeIntegers(void); +EXPORT(sqInt) primitiveBytecodeSetsAvailable(void); +static void primitiveBytesLeft(void); +static void primitiveCalloutToFFI(void); +static void primitiveChangeClass(void); +static void primitiveClass(void); +static void primitiveClearVMProfile(void); +static void primitiveClipboardText(void); +EXPORT(sqInt) primitiveClockLogAddresses(void); +static void primitiveClosureValue(void); +extern void primitiveClosureValueNoContextSwitch(void); +static void primitiveClosureValueWithArgs(void); +static void primitiveCoarseLocalMicrosecondClock(void); +static void primitiveCoarseUTCMicrosecondClock(void); +EXPORT(sqInt) primitiveCompareBytes(void); +EXPORT(void) primitiveCompareWith(void); +static void primitiveConstantFill(void); +static void primitiveControlVMProfiling(void); +static void primitiveCopyObject(void); +static void primitiveCosine(void); +EXPORT(sqInt) primitiveCrashVM(void); +EXPORT(sqInt) primitiveDisablePowerManager(void); +static void primitiveDiv(void); +static void primitiveDivide(void); +EXPORT(void) primitiveDivideLargeIntegers(void); +EXPORT(void) primitiveDivLargeIntegers(void); +static void primitiveEqual(void); +EXPORT(void) primitiveEqualLargeIntegers(void); +static void primitiveExitToDebugger(void); +static void primitiveExp(void); +static void primitiveExponent(void); +extern sqInt primitiveFail(void); +extern sqInt primitiveFailForOSError(sqLong osErrorCode); +extern sqInt primitiveFailFor(sqInt reasonCode); +extern sqInt primitiveFailForwithSecondary(sqInt reasonCode, sqLong extraErrorCode); +extern sqInt primitiveFailureCode(void); +static void primitiveFetchNextMourner(void); +static void primitiveFloatAdd(void); +static void primitiveFloatArrayAt(void); +static void primitiveFloatArrayAtPut(void); +static void primitiveFloatAt(void); +static void primitiveFloatAtPut(void); +static void primitiveFloatDivide(void); +static void primitiveFloatEqual(void); +static void primitiveFloatGreaterOrEqual(void); +static void primitiveFloatGreaterThan(void); +static void primitiveFloatLessOrEqual(void); +static void primitiveFloatLessThan(void); +static void primitiveFloatMultiply(void); +static void primitiveFloatNotEqual(void); +static void primitiveFloatSubtract(void); +static void primitiveFlushCache(void); +static void primitiveFlushExternalPrimitives(void); +static void primitiveFormPrint(void); +static void primitiveFractionalPart(void); +static void primitiveFullClosureValue(void); +extern void primitiveFullClosureValueNoContextSwitch(void); +static void primitiveFullClosureValueWithArgs(void); +static void primitiveGetAttribute(void); +EXPORT(sqInt) primitiveGetenv(void); +#if IMMUTABILITY +static void primitiveGetImmutability(void); +#else +# define primitiveGetImmutability (void (*)(void))0 +#endif /* IMMUTABILITY */ +EXPORT(sqInt) primitiveGetLogDirectory(void); +static void primitiveGetNextEvent(void); +static void primitiveGreaterOrEqual(void); +EXPORT(void) primitiveGreaterOrEqualLargeIntegers(void); +static void primitiveGreaterThan(void); +EXPORT(void) primitiveGreaterThanLargeIntegers(void); +static void primitiveGrowMemoryByAtLeast(void); +static void primitiveHashMultiply(void); +EXPORT(sqInt) primitiveHeartbeatFrequency(void); +static void primitiveHighBit(void); +EXPORT(sqInt) primitiveHighResClock(void); +static void primitiveIdentical(void); +static void primitiveIdentityHash(void); +EXPORT(sqInt) primitiveImageFormatVersion(void); +static void primitiveImageName(void); +static void primitiveImmediateAsInteger(void); +static void primitiveInputSemaphore(void); +static void primitiveInputWord(void); +static void primitiveIntegerAt(void); +static void primitiveIntegerAtPut(void); +EXPORT(sqInt) primitiveInterruptChecksPerMSec(void); +static void primitiveInterruptSemaphore(void); +EXPORT(sqInt) primitiveIsBigEnder(void); +static void primitiveIsPinned(void); +static void primitiveLessOrEqual(void); +EXPORT(void) primitiveLessOrEqualLargeIntegers(void); +static void primitiveLessThan(void); +EXPORT(void) primitiveLessThanLargeIntegers(void); +static void primitiveListBuiltinModule(void); +static void primitiveListExternalModule(void); +static void primitiveLoadImageSegment(void); +static void primitiveLocalMicrosecondClock(void); +static void primitiveLogN(void); +static void primitiveLowSpaceSemaphore(void); +static void primitiveMakePoint(void); +static void primitiveMaxIdentityHash(void); +static void primitiveMillisecondClock(void); +EXPORT(sqInt) primitiveMillisecondClockMask(void); +static void primitiveMod(void); +EXPORT(void) primitiveModLargeIntegers(void); +EXPORT(sqInt) primitiveMultipleBytecodeSetsActive(void); +static void primitiveMultiply(void); +EXPORT(void) primitiveMultiplyLargeIntegers(void); +static void primitiveNew(void); +static void primitiveNewMethod(void); +static void primitiveNewWithArg(void); +static void primitiveNextInstance(void); +static void primitiveNextObject(void); +static void primitiveNoop(void); +static void primitiveNotEqual(void); +EXPORT(void) primitiveNotEqualLargeIntegers(void); +static void primitiveNotIdentical(void); +static void primitiveObjectsAccessibleFromRoots(void); +EXPORT(sqInt) primitivePathToUsing(void); +static void primitivePerformInSuperclass(void); +static void primitivePerformWithArgs(void); +static void primitivePinnedNew(void); +static void primitivePinnedNewWithArg(void); +EXPORT(sqInt) primitiveProfilePrimitive(void); +EXPORT(sqInt) primitiveProfileSample(void); +EXPORT(sqInt) primitiveProfileStart(void); +static void primitiveQuit(void); +static void primitiveQuo(void); +EXPORT(void) primitiveQuoLargeIntegers(void); +EXPORT(void) primitiveRemLargeIntegers(void); +static void primitiveScanCharacters(void); +EXPORT(sqInt) primitiveScreenDepth(void); +EXPORT(sqInt) primitiveScreenScaleFactor(void); +static void primitiveScreenSize(void); +static void primitiveSecondsClock(void); +static void primitiveSetDisplayMode(void); +static void primitiveSetFullScreen(void); +#if IMMUTABILITY +static void primitiveSetImmutability(void); +#else +# define primitiveSetImmutability (void (*)(void))0 +#endif /* IMMUTABILITY */ +static void primitiveSetInterruptKey(void); +EXPORT(sqInt) primitiveSetLogDirectory(void); +static void primitiveSetOrHasIdentityHash(void); +static void primitiveShortAt(void); +static void primitiveShortAtPut(void); +static void primitiveSignalAtBytesLeft(void); +static void primitiveSine(void); +static void primitiveSize(void); +static void primitiveSizeInBytes(void); +static void primitiveSizeInBytesOfInstance(void); +static void primitiveSmallFloatAdd(void); +static void primitiveSmallFloatArctan(void); +static void primitiveSmallFloatCosine(void); +static void primitiveSmallFloatDivide(void); +static void primitiveSmallFloatEqual(void); +static void primitiveSmallFloatExp(void); +static void primitiveSmallFloatExponent(void); +static void primitiveSmallFloatFractionalPart(void); +static void primitiveSmallFloatGreaterOrEqual(void); +static void primitiveSmallFloatGreaterThan(void); +static void primitiveSmallFloatLessOrEqual(void); +static void primitiveSmallFloatLessThan(void); +static void primitiveSmallFloatLogN(void); +static void primitiveSmallFloatMultiply(void); +static void primitiveSmallFloatNotEqual(void); +static void primitiveSmallFloatSine(void); +static void primitiveSmallFloatSquareRoot(void); +static void primitiveSmallFloatSubtract(void); +static void primitiveSmallFloatTimesTwoPower(void); +static void primitiveSmallFloatTruncated(void); +static void primitiveSomeInstance(void); +static void primitiveSomeObject(void); +static void primitiveSpecialObjectsOop(void); +static void primitiveSquareRoot(void); +static void primitiveStoreImageSegment(void); +static void primitiveStringAt(void); +static void primitiveStringAtPut(void); +static void primitiveStringReplace(void); +static void primitiveSubtract(void); +EXPORT(void) primitiveSubtractLargeIntegers(void); +static void primitiveTestAndSetOwnershipOfCriticalSection(void); +static void primitiveTestDisplayDepth(void); +#if TestingPrimitives +EXPORT(sqInt) primitiveTestShortenIndexableSize(void); +#endif /* TestingPrimitives */ +static void primitiveTimesTwoPower(void); +static void primitiveTruncated(void); +static void primitiveUninitializedNewWithArg(void); +static void primitiveUninitializedPinnedNewWithArg(void); +static void primitiveUpdateTimezone(void); +static void primitiveUTCMicrosecondClock(void); +static void primitiveUtcAndTimezoneOffset(void); +EXPORT(sqInt) primitiveUtcWithOffset(void); +static void primitiveVMPath(void); +static void primitiveVMProfileSamplesInto(void); +extern int signalNoResume(sqInt aSemaphore); +static NoDbgRegParms sqIntptr_t signedMachineIntegerValueOfObj(sqInt oop); +extern sqIntptr_t signedMachineIntegerValueOf(sqInt oop); +extern usqInt sizeOfAlienData(sqInt oop); +extern void * startOfAlienData(sqInt oop); +static int successful(void); +extern sqInt success(sqInt successBoolean); +static NoDbgRegParms sqInt becomeEffectFlagsFor(sqInt objOop); +extern void ceScheduleScavenge(void); +extern sqInt classSmallFloat(void); +static void clearLeakMapAndMapAccessibleObjects(void); +extern void ensureNoForwardedLiteralsIn(sqInt aMethodObj); +static NoDbgRegParms sqInt followForwardedObjectFieldstoDepth(sqInt objOop, sqInt depth); +extern usqInt freeStartAddress(void); +extern usqInt getScavengeThreshold(void); +static NoDbgRegParms sqLong headerWhileForwardingOf(sqInt aCompiledMethodObjOop); +extern sqInt isBytes(sqInt oop); +extern sqInt isForwardedClassIndex(sqInt maybeClassIndex); +extern int isImmediateClass(sqInt classObj); +extern sqInt isReallyYoungObject(sqInt objOop); +extern sqInt methodHeaderOf(sqInt methodObj); +extern usqInt needGCFlagAddress(void); +static NoDbgRegParms void printCantBeObjecton(sqInt oop, FILE *aStream); +extern usqInt specialObjectsOopAddress(void); +extern sqInt withoutForwardingOnandwithsendToCogit(sqInt obj1, sqInt obj2, sqInt aBool, sqInt (*selector)(sqInt,sqInt,sqInt)); +static NoDbgRegParms usqInt addressAfter(sqInt objOop); +static NoDbgRegParms sqInt allocateSlotsForPinningInOldSpacebytesformatclassIndex(sqInt numSlots, usqInt totalBytes, sqInt formatField, sqInt classIndex); +extern sqInt byteSwapped(sqInt w); +static NoDbgRegParms usqInt bytesInBody(sqInt objOop); +static int defaultEdenBytes(void); +extern sqInt fetchClassTagOf(sqInt oop); +extern sqInt floatObjectOf(double aFloat); +static NoDbgRegParms sqLong floatValueBitsOf(sqInt floatOop); +extern double floatValueOf(sqInt oop); +static NoDbgRegParms void hackSlimBridgeToat(sqInt objOop, sqInt startAddress); +extern int headerIndicatesAlternateBytecodeSet(sqInt methodHeader); +static NoDbgRegParms sqInt initFreeChunkWithBytesat(usqLong numBytes, sqInt address); +static NoDbgRegParms void initSegmentBridgeWithBytesat(usqLong numBytes, sqInt address); +extern sqInt instantiateClassindexableSize(sqInt classObj, usqInt nElements); +extern sqInt integerObjectOf(sqInt value); +extern sqInt integerValueOf(sqInt oop); +static NoDbgRegParms int isFloatInstance(sqInt oop); +static NoDbgRegParms int isImmediateFloat(sqInt oop); +extern int isIntegerObject(sqInt oop); +extern int isIntegerValue(sqInt intValue); +static NoDbgRegParms int isLilliputianSize(sqInt chunkBytes); +static NoDbgRegParms int isSmallFloatValueBits(usqLong rawFloatBits); +static NoDbgRegParms int isSmallFloatValue(double aFloat); +static NoDbgRegParms int isWordsNonImm(sqInt objOop); +static NoDbgRegParms sqInt lengthOfformat(sqInt objOop, sqInt fmt); +static NoDbgRegParms double loadFloatOrIntFrom(sqInt floatOrIntOop); +static sqInt maxSlotsForAlloc(void); +extern double noFailFloatValueOf(sqInt aFloatOop); +static sqInt numFreeLists(void); +static NoDbgRegParms sqInt objectAfterMaybeSlimBridgelimit(sqInt objOop, sqInt limit); +static NoDbgRegParms double smallFloatValueOf(sqInt oop); +extern usqInt smallObjectBytesForSlots(sqInt numSlots); +static sqInt wordIndexableFormat(void); +static sqInt wordSizeClassIndexPun(void); +static NoDbgRegParms NeverInline void addToEphemeronList(sqInt ephemeronCorpse); +static NoDbgRegParms NeverInline void addToWeakList(sqInt weakCorpse); +static sqInt allNewSpaceObjectsHaveZeroRTRefCount(void); +static sqInt allWeakSurvivorsOnWeakList(void); +static NeverInline void computeRefCountToShrinkRT(void); +static NoDbgRegParms sqInt copyAndForwardMourner(sqInt mourner); +static NoDbgRegParms sqInt copyAndForward(sqInt survivor); +static NoDbgRegParms NeverInline sqInt copyToOldSpacebytesformat(sqInt survivor, sqInt bytesInObject, sqInt formatOfSurvivor); +extern usqInt corpseForCorpseOffset(sqInt corpseOffset); +static NoDbgRegParms usqInt firstCorpse(sqInt headOfCorpseList); +static void growRememberedSet(void); +static NoDbgRegParms sqInt isInRememberedSet(sqInt objOop); +static NoDbgRegParms sqInt isMaybeOldScavengeSurvivor(sqInt oop); +static NoDbgRegParms sqInt isScavengeSurvivor(sqInt oop); +static NoDbgRegParms sqInt isYetToBeScavenged(sqInt oop); +static usqInt newSpaceCapacity(void); +static NoDbgRegParms usqInt nextCorpseOrNil(sqInt corpse); +extern void openScavengeLog(void); +extern void printEphemeronList(void); +extern void printRememberedSet(void); +static sqInt processEphemerons(void); +static void processWeaklings(void); +static NoDbgRegParms sqInt processWeakSurvivor(sqInt weakObj); +extern sqInt remember(sqInt objOop); +static NoDbgRegParms void scavengeFutureSurvivorSpaceStartingAt(sqInt initialAddress); +static sqInt scavengeInactiveEphemeronsInRememberedSet(void); +static sqInt scavengeInactiveEphemeronsOnEphemeronList(void); +static sqInt scavengeLoop(void); +static NoDbgRegParms sqInt scavengeReferentsOfPointers(sqInt referrer); +static NoDbgRegParms sqInt scavengeReferentsOf(sqInt referrer); +static NoDbgRegParms void scavengeRememberedSetStartingAt(sqInt n); +static float scavengerTenuringThreshold(void); +static sqInt unfiredEphemeronsAtEndOfRememberedSet(void); +static sqInt validRememberedSetPartitionPostSIEIRS(void); +static sqInt wholeEphemeronListIsFirable(void); +static NeverInline void writeScavengeLog(void); +static NoDbgRegParms sqInt accessibleObjectAfter(sqInt objOop); +static NoDbgRegParms NeverInline sqInt activeAndDeferredScan(sqInt anEphemeron); +static NoDbgRegParms void addFreeSubTree(sqInt freeTree); +extern sqInt addGCRoot(sqInt *varLoc); +static NoDbgRegParms sqInt addressCouldBeClassObj(sqInt maybeClassObj); +extern sqInt addressCouldBeObj(sqInt address); +extern sqInt addressCouldBeOop(sqInt address); +static NoDbgRegParms sqInt addToFreeListbytes(sqInt freeChunk, sqInt chunkBytes); +static NoDbgRegParms sqInt allInstancesOf(sqInt aClass); +static sqInt allObjects(void); +static sqInt allObjectsUnmarked(void); +static sqInt allOldMarkedWeakObjectsOnWeaklingStack(void); +static sqInt allocateLargestFreeChunk(void); +static NoDbgRegParms sqInt allocateOldSpaceChunkOfBytes(usqInt chunkBytes); +extern sqInt allocatePinnedSlots(sqInt nSlots); +static NoDbgRegParms sqInt allocateSlotsInOldSpacebytesformatclassIndex(sqInt numSlots, usqInt totalBytes, sqInt formatField, sqInt classIndex); +static sqInt allocationUnit(void); +static NoDbgRegParms sqInt allStrongSlotsOfWeaklingAreMarked(sqInt aWeakling); +static sqInt allUnscannedEphemeronsAreActive(void); +static NoDbgRegParms NeverInline sqInt assertInnerValidFreeObject(sqInt objOop); +extern sqInt becomewith(sqInt array1, sqInt array2); +static NoDbgRegParms sqInt becomewithtwoWaycopyHash(sqInt array1, sqInt array2, sqInt twoWayFlag, sqInt copyHashFlag); +extern void beRootIfOld(sqInt oop); +static sqInt bitsSetInFreeSpaceMaskForAllFreeLists(void); +static sqInt bridgeSize(void); +static NoDbgRegParms sqInt byteFormatForNumBytes(sqInt numBytes); +static sqInt byteFormatMask(void); +extern sqInt byteSizeOf(sqInt oop); +extern unsigned char bytesPerElement(sqInt oop); +static NoDbgRegParms sqInt changeClassOfto(sqInt rcvr, sqInt argClass); +extern sqInt characterObjectOf(sqInt characterCode); +extern usqInt characterValueOf(sqInt oop); +static NoDbgRegParms sqInt cheapAddressCouldBeInHeap(sqInt address); +extern sqInt checkedLongAt(sqInt byteAddress); +static sqInt checkHeapFreeSpaceIntegrity(void); +static NoDbgRegParms sqInt checkHeapIntegrityclassIndicesShouldBeValid(sqInt excludeUnmarkedObjs, sqInt classIndicesShouldBeValid); +extern sqInt checkOkayOop(usqInt oop); +extern sqInt checkOopHasOkayClass(usqInt obj); +static NoDbgRegParms sqInt checkOopIntegritynamed(sqInt obj, char *name); +static NoDbgRegParms sqInt checkOopIntegritynamedindex(sqInt obj, char *name, sqInt i); +extern sqInt classAlien(void); +extern sqInt classArray(void); +extern sqInt classAtIndex(sqInt classIndex); +static NoDbgRegParms sqInt classAtIndexput(sqInt classIndex, sqInt objOop); +extern sqInt classBitmap(void); +extern sqInt classByteArray(void); +extern sqInt classCharacter(void); +extern sqInt classDoubleByteArray(void); +extern sqInt classDoubleWordArray(void); +extern sqInt classExternalAddress(void); +extern sqInt classExternalData(void); +extern sqInt classExternalFunction(void); +extern sqInt classExternalLibrary(void); +extern sqInt classExternalStructure(void); +extern sqInt classFloat(void); +extern sqInt classFloat32Array(void); +extern sqInt classFloat64Array(void); +static NoDbgRegParms sqInt classForClassTag(sqInt classIndex); +static NoDbgRegParms sqInt classFormatFromInstFormat(sqInt instFormat); +static sqInt classIndexFieldWidth(void); +extern sqInt classIndexOf(sqInt objOop); +extern sqInt classLargeNegativeInteger(void); +extern sqInt classLargePositiveInteger(void); +extern sqInt classOrNilAtIndex(sqInt classIndex); +extern sqInt classPoint(void); +extern sqInt classSemaphore(void); +extern sqInt classSmallInteger(void); +extern sqInt classString(void); +extern sqInt classTableMinorIndexMask(void); +extern sqInt classTablePageSize(void); +extern sqInt classTableRootObj(void); +static sqInt classTableRootSlots(void); +extern sqInt classTagForClass(sqInt classObj); +extern sqInt classUnsafeAlien(void); +extern sqInt classWordArray(void); +static void clearLeakMapAndMapAccessibleFreeSpace(void); +static NoDbgRegParms sqInt cloneInOldSpaceforPinning(sqInt objOop, sqInt forPinning); +extern sqInt cloneObject(sqInt objOop); +static NoDbgRegParms sqInt copyObjtoAddrstopAtsavedFirstFieldsindex(sqInt objOop, sqInt segAddr, sqInt endSeg, sqInt savedFirstFields, sqInt i); +extern void countMarkedAndUnmarkdObjects(sqInt printFlags); +static usqLong currentAllocatedBytes(void); +static NoDbgRegParms void doScavenge(sqInt tenuringCriterion); +static NoDbgRegParms sqInt doShortentoIndexableSize(sqInt objOop, sqInt indexableSize); +extern usqInt eeInstantiateClassIndexformatnumSlots(sqInt knownClassIndex, sqInt objFormat, sqInt numSlots); +extern NeverInline void eek(void); +static NoDbgRegParms void enableObjectEnumerationFrom(sqInt initialObject); +static NoDbgRegParms sqInt ensureBehaviorHash(sqInt aBehavior); +static NoDbgRegParms sqInt ensureRoomOnObjStackAt(sqInt objStackRootIndex); +static NoDbgRegParms sqInt enterIntoClassTable(sqInt aBehavior); +static NoDbgRegParms sqInt existInstancesInNewSpaceOf(sqInt classObj); +static NoDbgRegParms void expungeFromClassTable(sqInt aBehavior); +extern sqInt falseObject(void); +extern sqInt fetchByteofObject(sqInt byteIndex, sqInt objOop); +static NoDbgRegParms NeverInline sqInt fetchClassOfNonImm(sqInt objOop); +extern sqInt fetchClassOf(sqInt oop); +static NoDbgRegParms sqInt fetchClassTagOfNonImm(sqInt obj); +extern sqLong fetchIntegerofObject(sqInt fieldIndex, sqInt objOop); +extern int fetchLong32ofObject(sqInt fieldIndex, sqInt oop); +static NoDbgRegParms sqInt fetchPointerofFreeChunk(sqInt fieldIndex, sqInt objOop); +extern sqInt fetchPointerofObject(sqInt fieldIndex, sqInt objOop); +extern sqInt findClassNamed(char *aString); +static sqInt findLargestFreeChunk(void); +extern void findStringBeginningWith(char *aCString); +extern void findString(char *aCString); +static NoDbgRegParms void fireEphemeron(sqInt ephemeron); +static sqInt firstAccessibleObject(void); +static NoDbgRegParms void * firstFixedFieldOfMaybeImmediate(sqInt oop); +extern void * firstFixedField(sqInt objOop); +extern void * firstIndexableField(sqInt objOop); +extern sqInt fixedFieldsOfClassFormatMask(void); +extern sqInt fixedFieldsOfClassFormat(sqInt classFormat); +static NoDbgRegParms sqInt fixedFieldsOfformatlength(sqInt objOop, sqInt fmt, sqInt wordLength); +static NoDbgRegParms NeverInline sqInt fixFollowedFieldofObjectwithInitialValue(sqInt fieldIndex, sqInt anObject, sqInt initialValue); +static NoDbgRegParms sqInt followedKeyOfEphemeron(sqInt objOop); +static NoDbgRegParms sqInt followFieldofObject(sqInt fieldIndex, sqInt anObject); +extern sqInt followForwarded(sqInt objOop); +static NoDbgRegParms sqInt followMaybeForwarded(sqInt objOop); +static NoDbgRegParms sqInt followObjFieldofObject(sqInt fieldIndex, sqInt anObject); +extern sqInt formatOfClass(sqInt classPointer); +static NoDbgRegParms sqInt formatOf(sqInt objOop); +static NoDbgRegParms sqInt forwardersIn(sqInt anObject); +static sqInt freeChunkNextIndex(void); +static sqInt freeChunkParentIndex(void); +static sqInt freeChunkPrevIndex(void); +static NoDbgRegParms sqInt freeChunkWithBytesat(sqInt bytes, sqInt address); +extern sqInt freeObject(sqInt objOop); +extern NeverInline usqLong fullGC(void); +static float getHeapGrowthToSizeGCRatio(void); +static NoDbgRegParms sqInt goodContextSize(sqInt oop); +static NoDbgRegParms usqInt growOldSpaceByAtLeast(sqInt minAmmount); +static NoDbgRegParms sqInt hasPointerFieldsNonImm(sqInt oop); +extern usqLong headerForSlotsformatclassIndex(sqInt numSlots, sqInt formatField, sqInt classIndex); +static NoDbgRegParms usqLong headerForSlotshashformatclassIndex(sqInt numSlots, sqInt hash, sqInt formatField, sqInt classIndex); +static sqInt hiddenRootSlots(void); +static sqInt hiddenRootsObject(void); +static sqInt imageSegmentVersion(void); +#if IMMUTABILITY +extern sqInt immutableBitMask(void); +#endif /* IMMUTABILITY */ +extern sqInt indexOfin(sqInt anElement, sqInt anObject); +static NoDbgRegParms void inFreeTreeReplacewith(sqInt treeNode, sqInt newNode); +static NoDbgRegParms sqInt initialInstanceOf(sqInt classObj); +static NeverInline void initializeNewSpaceVariables(void); +static NoDbgRegParms void initializeObjectMemory(sqInt bytesToShift); +static void initializeUnscannedEphemerons(void); +extern void inOrderPrintFreeTreeprintList(sqInt freeChunk, sqInt printNextList); +static NoDbgRegParms NeverInline void inPlaceBecomeandcopyHashFlag(sqInt obj1, sqInt obj2, sqInt copyHashFlag); +extern sqInt instanceSizeOf(sqInt classObj); +static NoDbgRegParms sqInt instantiateClass(sqInt classObj); +extern sqInt instSpecOfClassFormat(sqInt classFormat); +static NoDbgRegParms sqInt instSpecOfClass(sqInt classPointer); +static NoDbgRegParms void invalidCompactClassError(const char *className); +extern sqInt isArray(sqInt oop); +static NoDbgRegParms int isBridgeOrEnumerableObjectNoAssert(sqInt objOop); +extern int isCharacterObject(sqInt oop); +extern int isCharacterValue(sqInt anInteger); +static NoDbgRegParms int isCompiledMethodFormat(sqInt format); +static NoDbgRegParms int isCompiledMethodHeader(sqInt objHeader); +extern int isCompiledMethod(sqInt objOop); +static NoDbgRegParms int isContextHeader(sqInt aHeader); +static NoDbgRegParms int isContextNonImm(sqInt oop); +static NoDbgRegParms sqInt isContext(sqInt oop); +static NoDbgRegParms int isCopiedIntoSegment(sqInt anObjectInTheHeap); +static NoDbgRegParms sqInt isEmptyObjStack(sqInt objStack); +static NoDbgRegParms sqInt isEnumerableObjectNoAssert(sqInt objOop); +static NoDbgRegParms int isEphemeronFormat(sqInt format); +static NoDbgRegParms int isEphemeron(sqInt objOop); +extern sqInt isFixedSizePointerFormat(sqInt format); +static NoDbgRegParms int isForwardedClassTag(sqInt classIndex); +extern int isForwarded(sqInt objOop); +static NoDbgRegParms int isFreeObject(sqInt objOop); +static NoDbgRegParms sqInt isFreeOop(sqInt oop); +extern int isImmediate(sqInt oop); +static NoDbgRegParms sqInt isInClassTable(sqInt objOop); +extern sqInt isIndexable(sqInt objOop); +static NoDbgRegParms sqInt isInEden(sqInt objOop); +static NoDbgRegParms sqInt isInFutureSpace(sqInt address); +extern sqInt isInMemory(sqInt address); +static NoDbgRegParms sqInt isInNewSpace(sqInt objOop); +extern sqInt isInOldSpace(sqInt address); +static NoDbgRegParms sqInt isInPastSpace(sqInt address); +static NoDbgRegParms int isLargeFreeObject(sqInt objOop); +extern sqInt isLong64s(sqInt oop); +extern int isMarked(sqInt objOop); +static NoDbgRegParms sqInt isMaybeFiredEphemeron(sqInt objOop); +extern int isNonImmediate(sqInt oop); +static NoDbgRegParms int isObjEphemeron(sqInt objOop); +static NoDbgRegParms sqInt isObjImmutable(sqInt anOop); +static NoDbgRegParms int isOldObject(sqInt objOop); +extern sqInt isOopCompiledMethod(sqInt oop); +extern sqInt isOopForwarded(sqInt oop); +extern sqInt isOopImmutable(sqInt oop); +extern sqInt isOopMutable(sqInt oop); +extern int isPinned(sqInt objOop); +extern sqInt isPointers(sqInt oop); +static NoDbgRegParms sqInt isPureBitsFormat(sqInt format); +static NoDbgRegParms sqInt isPureBitsNonImm(sqInt objOop); +static NoDbgRegParms int isRemembered(sqInt objOop); +static NoDbgRegParms int isSegmentBridge(sqInt objOop); +static NoDbgRegParms sqInt isSemaphoreOop(sqInt anOop); +extern sqInt isShorts(sqInt oop); +static NoDbgRegParms int isUnambiguouslyForwarder(sqInt objOop); +extern int isUnmarked(sqInt objOop); +extern sqInt isValidClassTag(sqInt classIndex); +static NoDbgRegParms sqInt isValidObjStackAt(sqInt objStackRootIndex); +static NoDbgRegParms sqInt isValidObjStackPagemyIndex(sqInt objStackPage, sqInt myx); +static NoDbgRegParms sqInt isValidObjStackPagemyIndexfirstPage(sqInt objStackPage, sqInt myx, sqInt isFirstPage); +static NoDbgRegParms sqInt isValidObjStack(sqInt objStack); +static NoDbgRegParms int isWeakNonImm(sqInt objOop); +extern sqInt isWeak(sqInt oop); +static NoDbgRegParms sqInt isWordsOrBytesNonImm(sqInt objOop); +extern sqInt isWordsOrBytes(sqInt oop); +extern sqInt isWordsOrShorts(sqInt oop); +extern sqInt isWords(sqInt oop); +extern int isYoungObject(sqInt objOop); +extern sqInt isYoung(sqInt oop); +static NoDbgRegParms sqInt isinstanceOfcompactClassIndex(sqInt oop, sqInt classOop, sqInt compactClassIndex); +extern sqInt isonObjStack(sqInt oop, sqInt objStack); +static NoDbgRegParms sqInt keyOfEphemeron(sqInt objOop); +static NoDbgRegParms sqInt keyOfMaybeFiredEphemeron(sqInt objOop); +static NoDbgRegParms sqInt knownClassAtIndex(sqInt classIndex); +static sqInt lastPointerFormat(void); +static NoDbgRegParms sqInt lastPointerOfWhileSwizzling(sqInt objOop); +static NoDbgRegParms sqInt lastPointerOf(sqInt objOop); +extern int leakCheckFullGC(void); +extern int leakCheckNewSpaceGC(void); +static NoDbgRegParms sqInt lengthOfMaybeImmediate(sqInt oop); +static NoDbgRegParms sqInt lengthOf(sqInt objOop); +extern usqInt literalCountOfMethodHeader(sqInt header); +extern usqInt literalCountOf(sqInt methodPointer); +static NoDbgRegParms NeverInline sqInt loadImageSegmentFromoutPointers(sqInt segmentWordArray, sqInt outPointerArray); +extern void longPrintInstancesOf(sqInt aClassOop); +extern void longPrintInstancesWithClassIndex(sqInt classIndex); +extern void longPrintReferencesTo(sqInt anOop); +static void mapExtraRoots(void); +static NeverInline void mapMournQueue(void); +static NoDbgRegParms sqInt mapOopsFromtooutPointersoutHashes(sqInt segStart, sqInt segAddr, sqInt outPointerArray, sqInt savedOutHashes); +static NoDbgRegParms void markAndTraceClassOf(sqInt objOop); +static NoDbgRegParms void markAndTraceObjStackandContents(sqInt stackOrNil, sqInt markAndTraceContents); +extern NeverInline void markAndTrace(sqInt objOop); +static sqInt markInactiveEphemerons(void); +static NoDbgRegParms NeverInline void markObjects(sqInt objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged); +static void markWeaklingsAndMarkAndFireEphemerons(void); +extern sqInt maxIdentityHash(void); +extern sqInt maxSlotsForNewSpaceAlloc(void); +extern sqInt maybeMethodClassOfseemsToBeInstantiating(sqInt methodObj, sqInt format); +extern sqInt minSlotsForShortening(void); +static NoDbgRegParms usqInt newHashBitsOf(sqInt objOop); +static sqInt newSpaceIsEmpty(void); +extern sqInt nilObject(void); +static NeverInline void nilUnmarkedWeaklingSlots(void); +static NoDbgRegParms sqInt noCheckClassAtIndex(sqInt classIndex); +static NoDbgRegParms sqInt noCheckPushonObjStack(sqInt objOop, sqInt objStack); +static NoDbgRegParms sqInt noFixupFollowFieldofObject(sqInt fieldIndex, sqInt anObject); +static NoDbgRegParms NeverInline sqInt noInlineAllocateSlotsformatclassIndex(sqInt numSlots, sqInt formatField, sqInt classIndex); +static int noUnscannedEphemerons(void); +static NoDbgRegParms sqInt numBytesOfBitsformat(sqInt objOop, sqInt format); +static NoDbgRegParms sqInt numBytesOfBytes(sqInt objOop); +extern sqInt numBytesOf(sqInt objOop); +static NoDbgRegParms usqInt numPointerSlotsOf(sqInt objOop); +static NoDbgRegParms sqInt numSlotsForBytes(sqInt numBytes); +static NoDbgRegParms usqInt numSlotsOfAny(sqInt objOop); +static NoDbgRegParms usqInt numSlotsOfIndexablePointerObj(sqInt objOop); +extern usqInt numSlotsOf(sqInt objOop); +static NoDbgRegParms sqInt numStrongSlotsOfInephemeral(sqInt objOop); +static NoDbgRegParms sqInt numStrongSlotsOfWeakling(sqInt objOop); +static sqInt numUnscannedEphemerons(void); +extern sqInt objCouldBeClassObj(sqInt objOop); +extern sqInt objectAfter(sqInt objOop); +extern sqInt objectBefore(sqInt objOop); +static NoDbgRegParms sqInt objectInPastSpaceBefore(sqInt objOop); +static NoDbgRegParms sqInt objectStartingAt(sqInt address); +static NoDbgRegParms sqInt objectsAccessibleFromRoots(sqInt arrayOfRootsArg); +static NoDbgRegParms NeverInline sqInt objectsReachableFromRoots(sqInt arrayOfRoots); +static NoDbgRegParms sqInt okayOop(sqInt signedOop); +extern sqInt oldSpaceObjectAfter(sqInt objOop); +static NoDbgRegParms NeverInline void outOfPlaceBecomeandcopyHashFlag(sqInt obj1, sqInt obj2, sqInt copyHashFlag); +extern sqInt pinObject(sqInt objOop); +static NoDbgRegParms sqInt popObjStack(sqInt objStack); +extern sqInt popRemappableOop(void); +static NoDbgRegParms void postBecomeScanClassTable(sqInt effectsFlags); +extern sqInt primitiveErrorTable(void); +extern void printActivationsOf(sqInt aMethodObj); +extern void printBogons(void); +extern void printContextReferencesTo(sqInt anOop); +extern void printEntity(sqInt oop); +extern void printEphemerons(void); +extern void printForwarders(void); +static NoDbgRegParms void printForwarderon(sqInt oop, FILE *aStream); +extern void printFreeChunks(void); +extern void printFreeChunk(sqInt freeChunk); +static NoDbgRegParms void printFreeChunkprintAsTreeNode(sqInt freeChunk, sqInt printAsTreeNode); +extern void printFreeListHeads(void); +extern sqInt printFreeList(sqInt chunkOrIndex); +static NoDbgRegParms void printFreeObjecton(sqInt oop, FILE *aStream); +extern void printFreeTree(void); +static NoDbgRegParms void printFreeTreeChunk(sqInt chunkOrZero); +extern void printHeaderOf(sqInt objOop); +static NoDbgRegParms void printImmediateObjecton(sqInt oop, FILE *aStream); +extern void printInstancesOf(sqInt aClassOop); +extern void printInstancesWithClassIndex(sqInt classIndex); +extern void printInvalidClassTableEntries(void); +#if LLDB +extern void printMarkedOops(void); +#endif /* LLDB */ +extern void printMethodImplementorsOf(sqInt anOop); +extern void printMethodReferencesTo(sqInt anOop); +extern void printMournQueue(void); +extern void printNewObjectsInObjStack(sqInt objStack); +static NoDbgRegParms void printNonPointerDataOfon(sqInt oop, FILE *aStream); +extern void printObjectsFromto(sqInt startAddress, sqInt endAddress); +extern void printObjectsWithHash(sqInt hash); +extern void printObjStackAndContents(sqInt objStack); +static NoDbgRegParms sqInt printObjStackPagemyIndexpageTypeprintContents(sqInt objStackPage, sqInt myx, sqInt pageType, sqInt printContents); +static NoDbgRegParms void printObjStackPagemyIndextag(sqInt objStackPage, sqInt myx, char *pageType); +extern void printObjStack(sqInt objStack); +extern NeverInline void printOopsExcept(sqInt (*function)(sqInt)); +extern void printOopsFromto(sqInt startAddress, sqInt endAddress); +extern NeverInline void printOopsSuchThat(sqInt (*function)(sqInt)); +extern void printReferencesTo(sqInt anOop); +static NoDbgRegParms void printStringDataOfon(sqInt oop, FILE *aStream); +#if LLDB +extern void printUnmarkedOops(void); +#endif /* LLDB */ +extern void printWeaklings(void); +static NoDbgRegParms sqInt pushOnUnscannedEphemeronsStack(sqInt anEphemeron); +extern void pushRemappableOop(sqInt oop); +static NoDbgRegParms void queueMourner(sqInt anEphemeronOrWeakArray); +extern int rawHashBitsOf(sqInt objOop); +extern sqInt receiverTagBitsForMethod(sqInt aMethodObj); +static void refireQueuedEphemeronsPostSnapshot(void); +static NoDbgRegParms sqInt relocateObjStackForPlanningCompactorandContents(sqInt objStack, sqInt relocateContents); +extern sqInt removeGCRoot(sqInt *varLoc); +static NoDbgRegParms sqInt returnrestoringObjectsInsavedFirstFieldsandsavedHashes(sqInt errCode, sqInt reachableObjectsArray, sqInt savedFirstFields, sqInt outPointersArray, sqInt savedHashes); +static NoDbgRegParms sqInt returnrestoringObjectsInupTosavedFirstFields(sqInt errCode, sqInt reachableObjectsArray, sqInt limitOrTag, sqInt savedFirstFields); +static NoDbgRegParms NeverInline void reverseBytesIn32BitWordsIn(sqInt segmentWordArray); +static NoDbgRegParms void runLeakCheckerForFreeSpaceignoring(sqInt gcModes, sqInt anOopOrNil); +static NoDbgRegParms void runLeakCheckerFor(sqInt gcModes); +static NoDbgRegParms void runLeakCheckerForexcludeUnmarkedObjsclassIndicesShouldBeValid(sqInt gcModes, sqInt excludeUnmarkedObjs, sqInt classIndicesShouldBeValid); +static NoDbgRegParms void safePrintStringOf(sqInt oop); +static NoDbgRegParms void scavengingGCTenuringIf(sqInt tenuringCriterion); +static NoDbgRegParms NeverInline sqInt setHeapBasememoryLimitendOfMemory(sqInt baseOfHeap, sqInt memLimit, sqInt memEnd); +static NoDbgRegParms void setIsPinnedOfto(sqInt objOop, sqInt aBoolean); +static NoDbgRegParms void setIsRememberedOfto(sqInt objOop, sqInt aBoolean); +extern sqInt shortentoIndexableSize(sqInt objOop, sqInt indexableSize); +extern void shortPrintObjectsFromto(sqInt startAddress, sqInt endAddress); +extern sqInt shouldRemapOop(sqInt oop); +static NoDbgRegParms sqInt sizeOfObjStack(sqInt objStack); +static int slidingCompactionInProgress(void); +extern sqInt slotSizeOf(sqInt oop); +extern sqInt splObj(sqInt index); +static NoDbgRegParms usqInt startOfObject(sqInt objOop); +extern sqInt statNumGCs(void); +extern usqInt storeCheckBoundary(void); +static NoDbgRegParms sqInt storeImageSegmentIntooutPointersroots(sqInt segmentWordArrayArg, sqInt outPointersArrayArg, sqInt arrayOfRootsArg); +extern void storeIntegerofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqLong integerValue); +extern int storeLong32ofObjectwithValue(sqInt fieldIndex, sqInt obj, sqInt valueWord); +extern sqInt storePointerUncheckedofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqInt valuePointer); +extern sqInt storePointerofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqInt valuePointer); +extern sqInt stringForCString(const char *aCString); +static NoDbgRegParms sqInt swizzleObjStackAt(sqInt objStackRootIndex); +extern void tenuringIncrementalGC(void); +static NoDbgRegParms sqInt topOfObjStack(sqInt objStack); +extern sqInt topRemappableOop(void); +static sqInt totalFreeListBytes(void); +extern sqInt trueObject(void); +static void unfireQueuedEphemeronsForSnapshot(void); +static NoDbgRegParms sqInt unlinkFreeChunkchunkBytes(sqInt freeChunk, sqInt chunkBytes); +static NoDbgRegParms NeverInline sqInt unlinkLilliputianChunkindex(sqInt freeChunk, sqInt index); +static NoDbgRegParms void unlinkSolitaryFreeTreeNode(sqInt freeTreeNode); +extern sqInt unpinObject(sqInt objOop); +static sqInt unscannedEphemeronsContainsOnlyActiveEphemerons(void); +static sqInt unscannedEphemeronsContainsOnlyEphemerons(void); +static NoDbgRegParms void updateListStartingAt(sqInt freeNode); +static sqInt validClassTableHashes(void); +static sqInt validClassTableRootPages(void); +extern int validFreeTree(void); +static NoDbgRegParms sqInt validFreeTreeChunk(sqInt chunk); +static NoDbgRegParms const char * validFreeTreeChunkparent(sqInt chunk, sqInt parent); +static sqInt validObjStacks(void); +static NoDbgRegParms sqInt validPostBecomeArrayContents(sqInt anArray); +static NoDbgRegParms sqInt validStorePointerArgs(sqInt fieldIndex, sqInt objOop, sqInt valuePointer); +static NoDbgRegParms sqInt validStorePointerUncheckedArgs(sqInt fieldIndex, sqInt objOop, sqInt valuePointer); +extern sqInt vmEndianness(void); +static NoDbgRegParms char * whereIsMaybeHeapThing(sqInt anOop); +static NeverInline void compact(void); +static NeverInline sqInt copyAndUnmarkMobileObjects(void); +static NoDbgRegParms NeverInline void copyAndUnmark(sqInt firstPass); +static void endCompaction(void); +static NoDbgRegParms void freeFromupTonextObject(usqInt initialToFinger, usqInt limit, sqInt nextObject); +static NoDbgRegParms sqInt isMobile(sqInt obj); +static NoDbgRegParms sqInt isPostMobile(sqInt obj); +static NeverInline sqInt planCompactSavingForwarders(void); +static NoDbgRegParms void reinitializeScanFrom(sqInt initialObject); +extern sqInt remapObj(sqInt objOop); +static sqInt savedFirstFieldsSpaceWasAllocated(void); +static NoDbgRegParms sqInt scanForFirstFreeAndFirstMobileObjectFrom(sqInt initialObject); +extern sqInt shouldRemapObj(sqInt objOop); +static void unmarkObjectsFromFirstFreeObject(void); +static NeverInline void updatePointers(void); +static NoDbgRegParms void updatePointersInsavedFirstFieldPointer(sqInt obj, sqInt firstFieldPtr); +static NoDbgRegParms sqInt useSegmentForSavedFirstFieldsSpace(sqInt spaceEstimate); +static NoDbgRegParms sqInt validRelocationPlanInPass(sqInt onePass); +static NoDbgRegParms usqInt segLimit(SpurSegmentInfo *self_in_SpurSegmentInfo); +static NoDbgRegParms SpurSegmentInfo * addSegmentOfSize(sqInt ammount); +static NoDbgRegParms void adjustSegmentSwizzlesBy(sqInt firstSegmentShift); +static sqInt allBridgesMarked(void); +static void allocateOrExtendSegmentInfos(void); +static NoDbgRegParms sqInt bridgeAt(sqInt segIndex); +static NoDbgRegParms sqInt bridgeFor(SpurSegmentInfo *aSegment); +static NoDbgRegParms void bridgeFromto(SpurSegmentInfo *aSegment, SpurSegmentInfo *nextSegmentOrNil); +static void checkSegments(void); +static void collapseSegmentsPostSwizzle(void); +static NoDbgRegParms void * firstGapOfSizeAtLeast(sqInt size); +static NoDbgRegParms sqInt isEmptySegment(SpurSegmentInfo *seg); +static NoDbgRegParms sqInt isInSegments(usqInt address); +static NoDbgRegParms sqInt isValidSegmentBridge(sqInt objOop); +static NoDbgRegParms SpurSegmentInfo * nextNonEmptySegmentAfter(sqInt i); +static NeverInline void prepareForSnapshot(void); +static NoDbgRegParms sqInt readHeapFromImageFiledataBytes(sqImageFile f, sqInt numBytes); +static void restorePostSnapshot(void); +extern SpurSegmentInfo * segmentContainingObj(sqInt objOop); +static sqInt segmentOverlap(void); +static NoDbgRegParms int shrinkObjectMemory(usqInt delta); +static NoDbgRegParms sqInt swizzleObj(sqInt objOop); +static usqInt totalBytesInSegments(void); +static NoDbgRegParms sqInt writeImageSegmentsToFile(sqImageFile aBinaryStream); +static NoDbgRegParms sqInt writeSegmentnextSegmenttoFile(SpurSegmentInfo *segment, SpurSegmentInfo *nextSegment, sqImageFile aBinaryStream); +static NoDbgRegParms sqInt accessorDepthForExternalPrimitiveMethod(sqInt methodObj); +extern sqInt accessorDepthForPrimitiveMethod(sqInt aMethodObj); +extern sqInt activeProcess(void); +extern void addIdleUsecs(sqInt idleUsecs); +static NoDbgRegParms void addLastLinktoList(sqInt proc, sqInt aList); +static char * allOnesAsCharStar(void); +extern sqInt argumentCountOfClosure(sqInt closurePointer); +extern usqInt argumentCountOfMethodHeader(sqInt header); +extern usqInt argumentCountOf(sqInt methodPointer); +extern void * arrayValueOf(sqInt arrayOop); +static NoDbgRegParms sqInt asciiOfCharacter(sqInt characterObj); +extern void assertValidExecutionPointers(usqInt lip, char *lifp, char *lisp); +static NoDbgRegParms void assertValidStackLimits(sqInt ln); +extern sqInt booleanValueOf(sqInt obj); +extern sqInt callbackEnter(sqInt *callbackID); +extern sqInt callbackLeave(sqInt cbID); +extern sqInt canContextSwitchIfActivatingheader(sqInt theMethod, sqInt methodHeader); +extern sqInt characterForAscii(sqInt ascii); +extern sqInt checkAllAccessibleObjectsOkay(void); +#if LRPCheck +static NeverInline sqInt checkDeliveryOfLongRunningPrimitiveSignal(void); +#endif /* LRPCheck */ +extern sqInt checkedIntegerValueOf(sqInt intOop); +extern sqInt checkImageHeaderFromBytesAndSize(char *bytes, sqInt totalSize); +static NoDbgRegParms sqInt checkImageVersionFromstartingAtassignRawVersion(sqImageFile f, squeakFileOffsetType imageOffset, sqInt *rawVersionPtr); +static sqInt checkInterpreterIntegrity(void); +static NoDbgRegParms sqInt checkIsStillMarriedContextcurrentFP(sqInt aContext, char *currentFP); +static sqInt checkLogIntegrity(void); +extern sqInt checkOkayInterpreterObjects(sqInt writeBack); +static NoDbgRegParms sqInt checkOkayStackPage(StackPage *thePage); +static NoDbgRegParms sqInt checkOkayStackZone(sqInt writeBack); +static sqInt checkStackIntegrity(void); +static NoDbgRegParms sqInt checkStackPointerForMaybeMarriedContext(sqInt aContext); +static NoDbgRegParms sqInt checkStackPointerIndexForFrame(char *theFP); +static NoDbgRegParms sqInt classNameOfIs(sqInt aClass, char *className); +extern void clearTraceLog(void); +static NoDbgRegParms sqInt contexthasValidInversePCMappingOfin(sqInt aContext, sqInt theIP, char *theFP); +extern sqInt copiedValueCountOfClosure(sqInt closureObj); +extern sqInt copyBits(void); +extern sqInt copyBitsFromtoat(sqInt x0, sqInt x1, sqInt y); +static NoDbgRegParms NeverInline sqInt couldBeProcess(sqInt oop); +static NoDbgRegParms void createActualMessageTo(sqInt lookupClass); +static sqInt defaultNumStackPages(void); +#if SEND_PRINTING +extern void disableSendPrinting(void); +#endif /* SEND_PRINTING */ +static sqInt divorceAllFrames(void); +static NoDbgRegParms void divorceFramesIn(StackPage *aStackPage); +static NoDbgRegParms sqInt doPrimitiveDivby(sqInt rcvr, sqInt arg); +static NoDbgRegParms sqInt doPrimitiveModby(sqInt rcvr, sqInt arg); +extern sqInt doSignalSemaphoreWithIndex(sqInt index); +extern struct VirtualMachine * * dummyReferToProxy(void); +extern void dumpPrimTraceLog(void); +extern void dumpPrimTraceLogOn(FILE *aStdioStream); +extern NeverInline void eekcr(void); +#if SEND_PRINTING +extern void enableSendPrinting(void); +#endif /* SEND_PRINTING */ +static NoDbgRegParms sqInt ensureCallerContext(char *theFP); +static NoDbgRegParms void ensureImageFormatIsUpToDate(sqInt swapBytes); +static NoDbgRegParms char * establishFrameForContextToReturnTo(sqInt contextToReturnTo); +static NoDbgRegParms void externalDivorceFrameandContext(char *theFP, sqInt ctxt); +static NoDbgRegParms char * externalEnsureIsBaseFrame(char *aFramePtr); +static NoDbgRegParms sqInt externalInstVarofContextput(sqInt index, sqInt maybeMarriedContext, sqInt anOop); +static sqInt externalQuickPrimitiveResponse(void); +static void failUnbalancedPrimitive(void); +extern void * fetchArrayofObject(sqInt fieldIndex, sqInt objectPointer); +extern double fetchFloatofObject(sqInt fieldIndex, sqInt objectPointer); +static NoDbgRegParms sqInt fetchPointerofMarriedContext(sqInt offset, sqInt aContext); +static NoDbgRegParms sqInt fetchStackPointerOf(sqInt aContext); +extern int fileTimesInUTC(void); +static NoDbgRegParms sqInt findClassContainingMethodstartingAt(sqInt meth, sqInt classObj); +extern sqInt findClassOfMethodforReceiver(sqInt meth, sqInt rcvr); +static NoDbgRegParms char * findFrameAboveinPage(char *theFP, StackPage *thePage); +static sqInt findHighestPriority(void); +static NoDbgRegParms sqInt findHomeForContext(sqInt aContext); +static NoDbgRegParms sqInt findMethodWithPrimitiveFromContextUpToContext(sqInt primitive, sqInt senderContext, sqInt homeContext); +extern sqInt findSelectorOfMethod(sqInt aMethodOop); +static NoDbgRegParms char * findSPOfon(char *theFP, StackPage *thePage); +static NoDbgRegParms sqInt findUnwindThroughContext(sqInt homeContext); +extern void flush(void); +static NoDbgRegParms sqInt flushExternalPrimitiveOf(sqInt methodObj); +static NoDbgRegParms void flushMethodCacheForMethod(sqInt oldMethod); +static NoDbgRegParms void followForwardedFrameContentsstackPointer(char *theFP, char *theSP); +extern sqInt forceInterruptCheck(void); +static NoDbgRegParms char * frameCallerSP(char *theFP); +static NoDbgRegParms sqInt frameContext(char *theFP); +static NoDbgRegParms char * frameOfMarriedContext(sqInt aContext); +static NoDbgRegParms sqInt frameStackedReceiverOffset(char *theFP); +static NoDbgRegParms sqInt frameStackedReceivernumArgs(char *theFP, sqInt numArgs); +static void freeUntracedStackPages(void); +extern sqInt fullDisplayUpdate(void); +static NoDbgRegParms void (*functionPointerForinClass(sqInt primIdx, sqInt theClass))(void); +static sqInt getCogVMFeatureFlags(void); +static sqInt getErrorObjectFromPrimFailCode(void); +extern int getFullScreenFlag(void); +extern sqInt getInterruptKeycode(void); +extern sqInt getInterruptPending(void); +extern usqLong getNextWakeupUsecs(void); +extern sqInt getSavedWindowSize(void); +static NoDbgRegParms usqInt getShortFromFileswap(sqImageFile aFile, sqInt swapFlag); +static sqInt getSnapshotScreenSize(void); +extern sqInt * getStackPointer(void); +extern sqInt getThisSessionID(void); +extern FILE * getTranscript(void); +static NoDbgRegParms sqInt handleForwardedSelectorFaultFor(sqInt selectorOop); +static NoDbgRegParms sqInt handleForwardedSendFaultForTag(sqInt classTag); +static NoDbgRegParms sqInt handleSpecialSelectorSendFaultForfpsp(sqInt obj, char *theFP, char *theSP); +static NeverInline void handleStackOverflow(void); +static NoDbgRegParms sqInt handleStackOverflowOrEventAllowContextSwitch(sqInt mayContextSwitch); +extern sqInt highBit(usqInt anUnsignedValue); +extern sqInt homeMethodOf(sqInt aMethodOop); +static NoDbgRegParms sqInt ifCurrentStackPageHasValidHeadPointers(StackPage *thePage); +static NoDbgRegParms usqInt iframeMethod(char *theFP); +extern sqInt includesBehaviorThatOf(sqInt aClass, sqInt aSuperclass); +extern void initStackPagesAndContinueIntowith(void (*continuation)(void *), void *argument); +extern void ioFilenamefromStringofLengthresolveAliases(char *aCharBuffer, char *aFilenameString, sqInt filenameLength, sqInt aBoolean); +static NoDbgRegParms int isBaseFrame(char *theFP); +extern sqInt isBooleanObject(sqInt oop); +static NoDbgRegParms sqInt isCalloutPrimitiveIndex(sqInt primIndex); +static NoDbgRegParms int isEmptyList(sqInt aLinkedList); +extern int isFloatObject(sqInt oop); +static NoDbgRegParms sqInt isFrameonPage(char *aFrame, StackPage *aPage); +extern sqInt isKindOfInteger(sqInt oop); +extern sqInt isLargeIntegerObject(sqInt oop); +extern sqInt isLargeNegativeIntegerObject(sqInt oop); +extern sqInt isLargePositiveIntegerObject(sqInt oop); +static NoDbgRegParms sqInt isLinkedExternalPrimitive(sqInt methodObj); +static NoDbgRegParms sqInt isLiveContext(sqInt oop); +static NoDbgRegParms int isMarriedOrWidowedContext(sqInt aContext); +static NoDbgRegParms sqInt isMetaPrimitiveIndex(sqInt primIndex); +static NoDbgRegParms sqInt isNullExternalPrimitiveCall(sqInt aMethodObj); +static int isPrimitiveFunctionPointerAnIndex(void); +extern int isQuickPrimitiveIndex(sqInt anInteger); +extern int isReadMediatedContextInstVarIndex(sqInt index); +static NoDbgRegParms int isSingleContext(sqInt aContext); +static NoDbgRegParms sqInt isStillMarriedContext(sqInt aContext); +static NoDbgRegParms int isVanillaBlockClosure(sqInt aClosure); +static NoDbgRegParms sqInt isWidowedContextNoConvert(sqInt aOnceMarriedContext); +extern int isWriteMediatedContextInstVarIndex(sqInt index); +extern sqInt isKindOfClass(sqInt oop, sqInt aClass); +extern sqInt isKindOf(sqInt oop, char *className); +extern sqInt isMemberOf(sqInt oop, char *className); +static NoDbgRegParms sqInt lengthOfNameOfClass(sqInt classOop); +extern sqInt literalofMethod(sqInt offset, sqInt methodPointer); +extern sqInt loadBitBltFrom(sqInt bb); +extern void longPrintOop(sqInt oop); +extern sqInt longStoreBytecodeForHeader(sqInt methodHeader); +static NoDbgRegParms sqInt lookupInMethodCacheSelclassTag(sqInt selector, sqInt classTag); +static NoDbgRegParms sqInt lookupMethodInClass(sqInt class); +static NoDbgRegParms sqInt lookupMNUInClass(sqInt class); +static NoDbgRegParms sqInt lookupOrdinaryNoMNUEtcInClass(sqInt class); +extern sqInt lookupSelectorinClass(sqInt selector, sqInt class); +static NoDbgRegParms void makeContextSnapshotSafe(sqInt ctxt); +extern usqInt makePointwithxValueyValue(sqInt xValue, sqInt yValue); +static void mapPrimTraceLog(void); +static void mapTraceLog(void); +static void markAndTracePrimTraceLog(void); +static NoDbgRegParms sqInt markAndTraceStackPages(sqInt fullGCFlag); +static void markAndTraceTraceLog(void); +static void markAndTraceUntracedReachableStackPages(void); +static NoDbgRegParms sqInt marriedContextpointsTostackDeltaForCurrentFrame(sqInt spouseContext, sqInt anOop, sqInt stackDeltaForCurrentFrame); +static NoDbgRegParms void marryContextInNewStackPageAndInitializeInterpreterRegisters(sqInt aContext); +static NoDbgRegParms sqInt maybeLeakCheckExternalPrimCall(sqInt aMethodObj); +extern sqInt maybeSelectorOfMethod(sqInt methodObj); +extern sqInt methodArgumentCount(void); +extern sqInt methodClassAssociationOf(sqInt methodPointer); +extern sqInt methodClassOf(sqInt methodPointer); +extern sqInt methodPrimitiveIndex(void); +extern sqInt methodReturnBool(sqInt boolean); +extern sqInt methodReturnFloat(double aFloat); +extern sqInt methodReturnInteger(sqInt integer); +extern sqInt methodReturnReceiver(void); +extern NeverInline sqInt methodReturnString(const char *aCString); +extern sqInt methodReturnValue(sqInt oop); +extern int methodUsesAlternateBytecodeSet(sqInt aMethodObj); +static NoDbgRegParms sqInt methodUsesPrimitiveErrorCode(sqInt aMethodObj); +EXPORT(void) moduleUnloaded(char *aModuleName); +static NoDbgRegParms char * nameOfClass(sqInt classOop); +static NoDbgRegParms char * nameOfClasslengthInto(sqInt classOop, sqInt *lengthPtr); +static NoDbgRegParms sqInt noMarkedContextsOnPage(StackPage *thePage); +static NoDbgRegParms sqInt numSlotsOfMarriedContext(sqInt aContext); +static sqInt numStkPages(void); +static NoDbgRegParms sqInt objectequalsString(sqInt anOop, char *aCString); +extern usqInt pcPreviousToinSqueakV3PlusClosuresOrSistaV1Method(sqInt theIP, sqInt aMethod); +static NoDbgRegParms sqInt penultimateLiteralOf(sqInt aMethodOop); +extern sqInt popStack(void); +extern sqInt pop(sqInt nItems); +extern void popthenPush(sqInt nItems, sqInt oop); +static sqInt postGCUpdateDisplayBits(void); +EXPORT(void) primitiveEventProcessingControl(void); +static NoDbgRegParms int primitiveFloatEqualtoArg(sqInt rcvrOop, sqInt argOop); +static NoDbgRegParms int primitiveFloatGreaterOrEqualtoArg(sqInt rcvrOop, sqInt argOop); +static NoDbgRegParms int primitiveFloatGreaterthanArg(sqInt rcvrOop, sqInt argOop); +static NoDbgRegParms int primitiveFloatLessOrEqualtoArg(sqInt rcvrOop, sqInt argOop); +static NoDbgRegParms int primitiveFloatLessthanArg(sqInt rcvrOop, sqInt argOop); +extern sqInt primitiveIndexOfMethodheader(sqInt theMethod, sqInt methodHeader); +extern sqInt primitiveIndexOf(sqInt methodPointer); +extern usqInt primitiveMethod(void); +static NoDbgRegParms void printActivationNameForSelectorstartClass(sqInt aSelector, sqInt startClass); +static NoDbgRegParms void printActivationNameForreceiverisBlockfirstTemporary(sqInt aMethod, sqInt anObject, sqInt isBlock, sqInt maybeMessage); +extern void printAllStacksOn(FILE *aStdioStream); +extern void printCallStack(void); +static NoDbgRegParms sqInt printCallStackFP(char *theFP); +extern sqInt printCallStackOf(sqInt aContextOrProcessOrFrame); +static NoDbgRegParms sqInt printCallStackOfcurrentFP(sqInt aContext, char *currFP); +extern void printCallStackOn(FILE *aStdioStream); +static NoDbgRegParms sqInt printContextCallStackOf(sqInt aContext); +extern void printContext(sqInt aContext); +static NoDbgRegParms void printDecodeMethodHeaderOop(sqInt methodHeaderOop); +extern void printExternalHeadFrame(void); +static NoDbgRegParms sqInt printFrameAndCallersSPshort(char *theFP, char *theSP, sqInt printShort); +static NoDbgRegParms void printFrameOopat(char *name, char *address); +static NoDbgRegParms void printFrameOopindexat(char *name, sqInt idx, char *address); +extern void printFramesInPage(StackPage *thePage); +extern void printFramesOnStackPageListInUse(void); +static NoDbgRegParms void printFrameThingandFrameat(char *name, char *theFP, char *address); +static NoDbgRegParms void printFrameThingatextraString(char *name, char *address, char *extraStringOrNil); +extern sqInt printFrame(char *theFP); +static NoDbgRegParms sqInt printHexPtrnp(void *p); +extern void printHex(usqInt n); +extern void printLikelyImplementorsOfSelector(sqInt selector); +extern void printMethodCache(void); +extern void printMethodDictionaryOf(sqInt behavior); +extern void printMethodDictionary(sqInt dictionary); +static NoDbgRegParms void printNameOfClasscount(sqInt classOop, sqInt cnt); +static NoDbgRegParms void printNum(sqInt n); +static NoDbgRegParms void printOopShortInner(sqInt oop); +extern void printOop(sqInt oop); +static NoDbgRegParms sqInt printPrimLogEntryAthasParameter(sqInt i, sqInt hasParameter); +extern void printProcessStack(sqInt aProcess); +extern sqInt printProcsOnList(sqInt procList); +extern sqInt printStackCallStackOf(sqInt aContextOrProcessOrFrame); +extern void printStackPageList(void); +extern void printStackPageListInUse(void); +extern void printStackPages(void); +extern void printStackPagesInUse(void); +static NoDbgRegParms void printStackPageuseCount(StackPage *page, sqInt n); +extern void printStackReferencesTo(sqInt oop); +static NoDbgRegParms void printStringOf(sqInt oop); +extern void print(char *s); +extern sqInt processListForProcess(sqInt aProcess); +extern sqInt processOSErrInstVarOffset(void); +extern void pushBool(sqInt trueOrFalse); +static NoDbgRegParms sqInt pushedReceiverOrClosureOfFrame(char *theFP); +extern void pushFloat(double f); +extern sqInt pushInteger(sqInt integerValue); +extern void push(sqInt object); +static NoDbgRegParms void putLongtoFile(sqInt aLong, sqImageFile aFile); +static NoDbgRegParms void putShorttoFile(short aShort, sqImageFile aFile); +static NoDbgRegParms void putToSleepyieldingIf(sqInt aProcess, sqInt yieldImplicitly); +static NoDbgRegParms void putWord32toFile(int aWord32, sqImageFile aFile); +static NoDbgRegParms sqInt quickFetchIntegerofObject(sqInt fieldIndex, sqInt objectPointer); +extern sqInt readableFormat(sqInt imageVersion); +static NoDbgRegParms NeverInline void reapAndResetErrorCodeToheader(char *theSP, sqInt methodHeader); +extern sqInt reestablishContextPriorToCallback(sqInt callbackContext); +static NoDbgRegParms sqInt removeFirstLinkOfList(sqInt aList); +static NoDbgRegParms sqInt removeProcessfromList(sqInt aProcess, sqInt aList); +static NoDbgRegParms sqInt resumepreemptedYieldingIffrom(sqInt aProcess, sqInt yieldImplicitly, sqInt sourceCode); +static sqInt retryPrimitiveOnFailure(void); +extern sqInt returnAsThroughCallbackContext(sqInt returnTypeOop, VMCallbackContext *vmCallbackContext, sqInt callbackMethodContext); +static NoDbgRegParms void reverseDisplayFromto(sqInt startIndex, sqInt endIndex); +static NoDbgRegParms void rewriteMethodCacheEntryForExternalPrimitiveToFunction(void (*localPrimAddress)()); +static NoDbgRegParms sqInt safeMethodClassOf(sqInt methodPointer); +static NoDbgRegParms sqInt saneFunctionPointerForFailureOfPrimIndex(sqInt primIndex); +extern sqInt schedulerPointer(void); +extern void setBreakMNUSelector(char *aString); +extern void setBreakSelector(char *aString); +extern void setFullScreenFlag(sqInt aBoolean); +extern void * setInterruptCheckChain(void (*aFunction)()); +extern void setInterruptKeycode(sqInt value); +extern void setInterruptPending(sqInt value); +extern void setNextWakeupUsecs(usqLong value); +extern void setSavedWindowSize(sqInt value); +static void setSignalLowSpaceFlagAndSaveProcess(void); +static NoDbgRegParms void setTraceFlagOnContextsFramesPageIfNeeded(sqInt aContext); +static NoDbgRegParms sqInt shortPrintContext(sqInt aContext); +static NoDbgRegParms sqInt shortPrintFrameAndCallers(char *theFP); +extern void shortPrintFramesInPage(StackPage *thePage); +extern void shortPrintFramesOnStackPageListInUse(void); +extern void shortPrintFrameAndNCallers(char *theFP, sqInt n); +static NoDbgRegParms void shortPrintOop(sqInt oop); +static NoDbgRegParms sqInt shortReversePrintFrameAndCallers(char *aFramePointer); +extern sqInt showDisplayBitsLeftTopRightBottom(sqInt aForm, sqInt l, sqInt t, sqInt r, sqInt b); +static sqInt signalExternalSemaphores(void); +extern sqInt sizeOfCallPrimitiveBytecode(sqInt methodHeader); +extern sqInt sizeOfLongStoreTempBytecode(sqInt methodHeader); +extern sqInt sizeOfSTArrayFromCPrimitive(void *cPtr); +static int slowPrimitiveResponse(void); +static NoDbgRegParms sqInt snapshot(sqInt embedded); +extern sqInt specialSelector(sqInt index); +static NoDbgRegParms void spurPostBecomeAction(sqInt theBecomeEffectsFlags); +extern double stackFloatValue(sqInt offset); +extern sqInt stackIntegerValue(sqInt offset); +static sqInt stackLimitBytes(void); +extern sqInt stackMutableObjectValue(sqInt offset); +extern sqInt stackObjectValue(sqInt offset); +static sqInt stackPageByteSize(void); +static NoDbgRegParms sqInt stackPointerForMaybeMarriedContext(sqInt aContext); +static NoDbgRegParms sqInt stackPointerIndexForFrame(char *theFP); +extern usqIntptr_t stackPositiveMachineIntegerValue(sqInt offset); +extern sqIntptr_t stackSignedMachineIntegerValue(sqInt offset); +extern sqInt stackTop(void); +extern sqInt stackValue(sqInt offset); +extern usqInt startPCOfMethod(sqInt aCompiledMethod); +extern sqInt stObjectat(sqInt array, sqInt index); +extern sqInt stObjectatput(sqInt array, sqInt index, sqInt value); +static NoDbgRegParms char * storeSenderOfFramewithValue(char *theFP, sqInt anOop); +extern sqInt stSizeOf(sqInt oop); +extern sqInt superclassOf(sqInt classPointer); +static NoDbgRegParms sqInt synchronousSignal(sqInt aSemaphore); +extern usqInt tempCountOf(sqInt methodPointer); +extern usqInt temporaryCountOfMethodHeader(sqInt header); +extern sqInt ultimateLiteralOf(sqInt aMethodOop); +static NoDbgRegParms sqInt unfollowFirstLiteralOfMaybeCalloutMethodprimitiveIndex(sqInt methodObj, sqInt primIndex); +static NoDbgRegParms sqInt updateDisplayLeftTopRightBottom(sqInt l, sqInt t, sqInt r, sqInt b); +static NoDbgRegParms sqInt validBCPCinMethod(sqInt thePC, usqInt aMethod); +static NoDbgRegParms sqInt validInstructionPointerinFrame(usqInt anInstrPointer, char *fp); +static sqInt validStackPageBaseFrames(void); +#if LRPCheck +static NoDbgRegParms NeverInline void voidLongRunningPrimitive(char *reason); +#endif /* LRPCheck */ +static sqInt wakeHighestPriority(void); +static NeverInline sqInt writeImageFileIO(void); +static NoDbgRegParms usqInt cloneContext(sqInt aContext); +static NoDbgRegParms sqInt fieldOrSenderFPofContext(sqInt index, sqInt contextObj); +static NoDbgRegParms sqInt fieldofFrame(sqInt index, char *theFP); +static NoDbgRegParms sqInt isAppropriateForCopyObject(sqInt oop); +static NoDbgRegParms void (*linkExternalCallerrInto(sqInt externalCallLiteral, sqInt *failPtr))(void); +static void primitiveClone(void); +static void primitiveContextAt(void); +static void primitiveContextAtPut(void); +static void primitiveContextPreserveTemps(void); +static void primitiveContextSize(void); +static void primitiveDeferDisplayUpdates(void); +static void primitiveDoNamedPrimitiveWithArgs(void); +static void primitiveDoPrimitiveWithArgs(void); +static void primitiveExternalCall(void); +static void primitiveFindHandlerContext(void); +static void primitiveFindNextUnwindContext(void); +static void primitiveFullGC(void); +static void primitiveGetSetFFIExceptionHandling(void); +static void primitiveIncrementalGC(void); +static void primitiveInstVarAt(void); +static void primitiveInstVarAtPut(void); +static void primitiveInvokeObjectAsMethod(void); +#if LRPCheck +EXPORT(void) primitiveLongRunningPrimitive(void); +#endif /* LRPCheck */ +static void primitiveObjectPointsTo(void); +static void primitivePerform(void); +static void primitivePin(void); +EXPORT(void) primitiveSetGCSemaphore(void); +static void primitiveSignalAtMilliseconds(void); +static void primitiveSignalAtUTCMicroseconds(void); +static void primitiveSlotAt(void); +static void primitiveSlotAtPut(void); +static void primitiveStoreStackp(void); +static void primitiveVMParameter(void); +EXPORT(void) primitiveVoidReceiver(void); +static NoDbgRegParms void pruneStackstackp(sqInt stack, sqInt stackp); +static void unmarkAfterPathTo(void); + +/*** Variables ***/ +#if SQ_USE_GLOBAL_STRUCT +# define _iss /* define in-struct static as void */ +static struct foo { +#else +# define _iss static +#endif +_iss char * stackLimit; +_iss char * stackPointer; +_iss char * framePointer; +_iss volatile usqIntptr_t CStackPointer; +_iss volatile usqIntptr_t CFramePointer; +_iss volatile usqIntptr_t CReturnAddress; +_iss usqInt scavengeThreshold; +_iss usqInt freeStart; +_iss sqInt needGCFlag; +_iss sqInt specialObjectsOop; +_iss sqInt primFailCode; +_iss unsigned char primTraceLogIndex; +_iss usqInt newMethod; +_iss usqInt instructionPointer; +_iss sqInt argumentCount; +_iss sqLong nextProfileTick; +_iss sqInt primTraceLog[256]; +_iss StackPage * stackPage; +_iss sqInt nilObj; +_iss usqInt oldSpaceStart; +_iss usqInt method; +_iss sqInt bytecodeSetSelector; +_iss FILE * transcript; +_iss usqInt endOfMemory; +_iss sqInt messageSelector; +_iss usqInt pastSpaceStart; +_iss usqInt newSpaceStart; +_iss StackPage * pages; +_iss sqInt trueObj; +_iss sqInt falseObj; +_iss usqInt totalFreeOldSpace; +_iss sqInt hiddenRootsObj; +_iss sqInt traceLogIndex; +_iss SpurSegmentInfo * segments; +_iss sqInt markStack; +_iss sqInt numSegments; +_iss sqInt weaklingStack; +_iss sqInt * freeLists; +_iss sqInt rememberedSetSize; +_iss sqInt numClassTablePages; +_iss sqInt classTableFirstPage; +_iss sqInt * rememberedSet; +_iss sqInt numThreads; +_iss sqInt futureSurvivorStart; +_iss StackPage * mostRecentlyUsedPage; +_iss sqInt tempOop; +_iss usqInt firstFreeObject; +_iss usqInt freeListsMask; +_iss usqInt lastHash; +_iss sqInt mournQueue; +_iss char * stackBasePlus1; +_iss sqInt disowningThreadIndex; +_iss sqInt numRememberedEphemerons; +_iss sqInt numStackPages; +_iss usqInt totalHeapSizeIncludingBridges; +_iss sqInt classTableIndex; +_iss usqInt lastMobileObject; +_iss CogVMOwnerLog * ownerLog; +_iss sqInt profileSemaphore; +_iss CogVMThread ** threads; +_iss sqInt profileProcess; +_iss sqInt profileMethod; +_iss sqInt remapBufferCount; +_iss sqInt becomeEffectsFlags; +#if LRPCheck +_iss sqInt longRunningPrimitiveCheckSemaphore; +#endif +_iss sqInt mobileStart; +_iss sqInt performWithArgumentsRecursionGuard; +_iss sqInt growHeadroom; +_iss sqInt lkupClass; +_iss sqInt tenureCriterion; +_iss sqInt tenureThreshold; +_iss sqInt ephemeronList; +_iss sqInt gcMode; +_iss sqInt lastCoggableInterpretedBlockMethod; +_iss sqInt lastUncoggableInterpretedBlockMethod; +_iss char * objStackInvalidBecause; +_iss sqInt classNameIndex; +_iss sqInt gcPhaseInProgress; +_iss sqInt highestRunnableProcessPriority; +#if LRPCheck +_iss sqInt longRunningPrimitiveCheckMethod; +#endif +_iss sqInt extraRootCount; +_iss sqInt invalidObjStackPage; +_iss sqInt lastMethodCacheProbeWrite; +_iss sqInt metaAccessorDepth; +_iss usqInt objectAfterLastMobileObject; +_iss sqInt tempOop2; +_iss sqInt cogCompiledCodeCompactionCalledFor; +_iss usqInt freeOldSpaceStart; +_iss sqInt previousRememberedSetSize; +_iss sqInt validatedIntegerClassFlags; +_iss usqInt firstMobileObject; +_iss sqInt numSegInfos; +_iss sqInt savedFirstFieldsSpaceNotInOldSpace; +_iss usqLong statGCEndUsecs; +_iss sqInt bytesPerPage; +_iss usqInt cogCodeSize; +#if LRPCheck +_iss usqLong longRunningPrimitiveStopUsecs; +#endif +_iss usqLong nextWakeupUsecs; +_iss sqInt relinquishing; +_iss sqInt shrinkThreshold; +_iss sqInt statTenures; +_iss sqInt thisClassIndex; +#if LRPCheck +_iss usqLong longRunningPrimitiveStartUsecs; +#endif +_iss sqInt metaclassNumSlots; +_iss sqInt pendingFinalizationSignals; +_iss usqLong statCheckForEvents; +_iss sqInt statSurvivorCount; +_iss sqInt weakList; +_iss sqInt disowningCStackPointer; +_iss sqInt firstFieldOfRememberedSet; +_iss usqLong gcStartUsecs; +_iss sqInt marking; +_iss sqLong oldSpaceUsePriorToScavenge; +_iss sqInt statMaxAllocSegmentTime; +_iss sqInt statScavenges; +_iss sqInt statShrinkMemory; +_iss sqInt firstSegmentSize; +_iss sqInt multipleBytecodeSetsActive; +_iss sqInt numThreadsIncrement; +_iss sqInt rememberedSetLimit; +_iss FILE * scavengeLog; +_iss sqInt statFullGCs; +_iss usqLong statProcessSwitch; +_iss sqInt biasForGC; +_iss sqInt deferProfileCheckForVNCS; +_iss sqInt foreignCallbackPriority; +_iss usqInt lowSpaceThreshold; +_iss sqInt memoryIsScarce; +_iss sqInt rememberedSetRedZone; +_iss sqLong secondaryErrorCode; +_iss usqLong statAllocatedBytes; +_iss sqInt statCodeCompactionCount; +_iss usqLong statCodeCompactionUsecs; +_iss usqLong statCompactionUsecs; +_iss usqLong statFullGCUsecs; +_iss sqInt statGrowMemory; +_iss usqLong statMarkUsecs; +_iss sqInt statNumMaps; +_iss usqLong statScavengeGCUsecs; +_iss usqLong statSweepUsecs; +_iss sqInt the2ndUnknownShort; +_iss sqInt anomaly; +_iss sqInt checkThreadActivation; +_iss sqInt deferThreadSwitch; +_iss sqInt edenBytes; +_iss sqInt externalPrimitiveTableFirstFreeIndex; +_iss sqInt freeSpaceCheckOopToIgnore; +_iss usqInt heapSizeAtPreviousGC; +_iss sqInt interruptKeycode; +_iss sqInt interruptPending; +#if LRPCheck +_iss sqInt longRunningPrimitiveCheckSequenceNumber; +#endif +_iss sqInt methodDictLinearSearchLimit; +_iss sqInt oldImageBaseAddress; +_iss sqInt ownerLogWrapped; +_iss sqInt savedWindowSize; +_iss usqLong startTimestamp; +_iss usqLong statForceInterruptCheck; +_iss usqLong statIOProcessEvents; +_iss usqLong statIdleUsecs; +_iss sqInt statMarkCount; +_iss sqInt statMaxPageCountWhenMapping; +_iss sqInt statPageCountWhenMappingSum; +_iss sqInt statRootTableCount; +_iss usqLong statStackOverflow; +_iss usqLong statStackPageDivorce; +_iss sqInt canSwizzle; +_iss sqInt classLinkedListClassTag; +_iss sqInt deferSmash; +_iss sqInt deferredSmash; +_iss sqInt disowningCFramePointer; +_iss sqInt extraFramesToMoveOnOverflow; +_iss sqInt globalSessionID; +#if LRPCheck +_iss sqInt longRunningPrimitiveSignalUndelivered; +#endif +_iss sqInt maxExtSemTabSizeSet; +_iss usqLong nextPollUsecs; +_iss sqInt primCalloutIsExternal; +_iss sqInt refCountToShrinkRT; +_iss sqInt signalLowSpace; +_iss sqInt statCompactPassCount; +_iss usqLong statIGCDeltaUsecs; +_iss sqInt statPendingFinalizationSignals; +_iss sqInt statRootTableOverflows; +_iss usqLong statSGCDeltaUsecs; +_iss sqInt bogon; +_iss sqInt classByteArrayCompactIndex; +_iss sqInt disowningInstructionPointer; +_iss usqInt exceptionPC; +_iss sqInt gcSemaphoreIndex; +_iss usqLong gcSweepEndUsecs; +#if LRPCheck +_iss usqLong longRunningPrimitiveGCUsecs; +#endif +_iss sqInt overflowLimit; +_iss StackPage * overflowedPage; +_iss sqInt printSends; +_iss sqInt tenuringClassIndex; +_iss sqInt statIncrGCs; +_iss sqIntptr_t methodCache[MethodCacheSize + 1 /* 4097 */]; +_iss sqInt traceLog[TraceBufferSize /* 768 */]; +_iss sqInt remapBuffer[RemapBufferSize + 1 /* 26 */]; +_iss sqInt *extraRoots[ExtraRootsSize + 1 /* 65 */]; +_iss usqLong byteCount; +_iss volatile atomic_int vmOwner; +_iss SpurContiguousObjStack unscannedEphemerons; +_iss SpurContiguousObjStack savedFirstFieldsSpace; +_iss SpurNewSpaceSpace pastSpace; +_iss SpurScavengeLogRecord scavengeLogRecord; +_iss SpurNewSpaceSpace futureSpace; +_iss SpurNewSpaceSpace eden; +_iss volatile atomic_int maxWaitingPriority; +_iss volatile atomic_int ownerLogIndex; +_iss float heapGrowthToSizeGCRatio; +_iss double tenuringProportion; +_iss sqOSThread vmOSThread; +#undef _iss +#if SQ_USE_GLOBAL_STRUCT + } fum; +# if SQ_USE_GLOBAL_STRUCT_REG +# define DECL_MAYBE_SQ_GLOBAL_STRUCT /* using a global reg pointer */ +# define DECL_MAYBE_VOLATILE_SQ_GLOBAL_STRUCT /* using a global reg pointer */ +# else +# define DECL_MAYBE_SQ_GLOBAL_STRUCT register struct foo * foo = &fum; +# define DECL_MAYBE_VOLATILE_SQ_GLOBAL_STRUCT volatile register struct foo * foo = &fum; +# endif +# define GIV(interpreterInstVar) (foo->interpreterInstVar) +#else +# define DECL_MAYBE_SQ_GLOBAL_STRUCT /* oh, no mr bill! */ +# define DECL_MAYBE_VOLATILE_SQ_GLOBAL_STRUCT /* oh no, mr bill! */ +# define GIV(interpreterInstVar) interpreterInstVar +#endif +#if SQ_USE_GLOBAL_STRUCT +# if SQ_USE_GLOBAL_STRUCT_REG +# define fooxstr(s) foostr(s) +# define foostr(s) #s +register struct foo * foo asm(fooxstr(USE_GLOBAL_STRUCT_REG)); +# else +static struct foo * foo = &fum; +# endif +#endif +static void (*primitiveFunctionPointer)(); +static void *primitiveCalloutPointer = (void *)-1; +static usqInt (* const pcPreviousToFunction)(sqInt,sqInt) = pcPreviousToinSqueakV3PlusClosuresOrSistaV1Method; +static void (*interruptCheckChain)(void) = 0; +static int (*sHEAFn)() = 0; +static void (*primitiveTable[MaxPrimitiveIndex + 2 /* 585 */])(void) = { + /* 0 */ (void (*)(void))0, + /* 1 */ primitiveAdd, + /* 2 */ primitiveSubtract, + /* 3 */ primitiveLessThan, + /* 4 */ primitiveGreaterThan, + /* 5 */ primitiveLessOrEqual, + /* 6 */ primitiveGreaterOrEqual, + /* 7 */ primitiveEqual, + /* 8 */ primitiveNotEqual, + /* 9 */ primitiveMultiply, + /* 10 */ primitiveDivide, + /* 11 */ primitiveMod, + /* 12 */ primitiveDiv, + /* 13 */ primitiveQuo, + /* 14 */ primitiveBitAnd, + /* 15 */ primitiveBitOr, + /* 16 */ primitiveBitXor, + /* 17 */ primitiveBitShift, + /* 18 */ primitiveMakePoint, + /* 19 */ (void (*)(void))0, + /* 20 */ primitiveRemLargeIntegers, + /* 21 */ primitiveAddLargeIntegers, + /* 22 */ primitiveSubtractLargeIntegers, + /* 23 */ primitiveLessThanLargeIntegers, + /* 24 */ primitiveGreaterThanLargeIntegers, + /* 25 */ primitiveLessOrEqualLargeIntegers, + /* 26 */ primitiveGreaterOrEqualLargeIntegers, + /* 27 */ primitiveEqualLargeIntegers, + /* 28 */ primitiveNotEqualLargeIntegers, + /* 29 */ primitiveMultiplyLargeIntegers, + /* 30 */ primitiveDivideLargeIntegers, + /* 31 */ primitiveModLargeIntegers, + /* 32 */ primitiveDivLargeIntegers, + /* 33 */ primitiveQuoLargeIntegers, + /* 34 */ primitiveBitAndLargeIntegers, + /* 35 */ primitiveBitOrLargeIntegers, + /* 36 */ primitiveBitXorLargeIntegers, + /* 37 */ primitiveBitShiftLargeIntegers, + /* 38 */ primitiveFloatAt, + /* 39 */ primitiveFloatAtPut, + /* 40 */ primitiveAsFloat, + /* 41 */ primitiveFloatAdd, + /* 42 */ primitiveFloatSubtract, + /* 43 */ primitiveFloatLessThan, + /* 44 */ primitiveFloatGreaterThan, + /* 45 */ primitiveFloatLessOrEqual, + /* 46 */ primitiveFloatGreaterOrEqual, + /* 47 */ primitiveFloatEqual, + /* 48 */ primitiveFloatNotEqual, + /* 49 */ primitiveFloatMultiply, + /* 50 */ primitiveFloatDivide, + /* 51 */ primitiveTruncated, + /* 52 */ primitiveFractionalPart, + /* 53 */ primitiveExponent, + /* 54 */ primitiveTimesTwoPower, + /* 55 */ primitiveSquareRoot, + /* 56 */ primitiveSine, + /* 57 */ primitiveArctan, + /* 58 */ primitiveLogN, + /* 59 */ primitiveExp, + /* 60 */ primitiveAt, + /* 61 */ primitiveAtPut, + /* 62 */ primitiveSize, + /* 63 */ primitiveStringAt, + /* 64 */ primitiveStringAtPut, + /* 65 */ (void (*)(void))0, + /* 66 */ (void (*)(void))0, + /* 67 */ (void (*)(void))0, + /* 68 */ primitiveObjectAt, + /* 69 */ primitiveObjectAtPut, + /* 70 */ primitiveNew, + /* 71 */ primitiveNewWithArg, + /* 72 */ primitiveArrayBecomeOneWay, + /* 73 */ primitiveInstVarAt, + /* 74 */ primitiveInstVarAtPut, + /* 75 */ primitiveIdentityHash, + /* 76 */ primitiveStoreStackp, + /* 77 */ primitiveSomeInstance, + /* 78 */ primitiveNextInstance, + /* 79 */ primitiveNewMethod, + /* 80 */ (void (*)(void))0, + /* 81 */ (void (*)(void))0, + /* 82 */ (void (*)(void))0, + /* 83 */ primitivePerform, + /* 84 */ primitivePerformWithArgs, + /* 85 */ primitiveSignal, + /* 86 */ primitiveWait, + /* 87 */ primitiveResume, + /* 88 */ primitiveSuspend, + /* 89 */ primitiveFlushCache, + /* 90 */ (void (*)(void))0, + /* 91 */ primitiveTestDisplayDepth, + /* 92 */ primitiveSetDisplayMode, + /* 93 */ primitiveInputSemaphore, + /* 94 */ primitiveGetNextEvent, + /* 95 */ primitiveInputWord, + /* 96 */ primitiveObjectsAccessibleFromRoots, + /* 97 */ primitiveSnapshot, + /* 98 */ primitiveStoreImageSegment, + /* 99 */ primitiveLoadImageSegment, + /* 100 */ primitivePerformInSuperclass, + /* 101 */ primitiveBeCursor, + /* 102 */ primitiveBeDisplay, + /* 103 */ primitiveScanCharacters, + /* 104 */ (void (*)(void))0, + /* 105 */ primitiveStringReplace, + /* 106 */ primitiveScreenSize, + /* 107 */ (void (*)(void))0, + /* 108 */ (void (*)(void))0, + /* 109 */ (void (*)(void))0, + /* 110 */ primitiveIdentical, + /* 111 */ primitiveClass, + /* 112 */ primitiveBytesLeft, + /* 113 */ primitiveQuit, + /* 114 */ primitiveExitToDebugger, + /* 115 */ primitiveChangeClass, + /* 116 */ primitiveFlushCacheByMethod, + /* 117 */ primitiveExternalCall, + /* 118 */ primitiveDoPrimitiveWithArgs, + /* 119 */ primitiveFlushCacheBySelector, + /* 120 */ primitiveCalloutToFFI, + /* 121 */ primitiveImageName, + /* 122 */ primitiveNoop, + /* 123 */ (void (*)(void))0, + /* 124 */ primitiveLowSpaceSemaphore, + /* 125 */ primitiveSignalAtBytesLeft, + /* 126 */ primitiveDeferDisplayUpdates, + /* 127 */ primitiveShowDisplayRect, + /* 128 */ primitiveArrayBecome, + /* 129 */ primitiveSpecialObjectsOop, + /* 130 */ primitiveFullGC, + /* 131 */ primitiveIncrementalGC, + /* 132 */ primitiveObjectPointsTo, + /* 133 */ primitiveSetInterruptKey, + /* 134 */ primitiveInterruptSemaphore, + /* 135 */ primitiveMillisecondClock, + /* 136 */ primitiveSignalAtMilliseconds, + /* 137 */ primitiveSecondsClock, + /* 138 */ primitiveSomeObject, + /* 139 */ primitiveNextObject, + /* 140 */ primitiveBeep, + /* 141 */ primitiveClipboardText, + /* 142 */ primitiveVMPath, + /* 143 */ primitiveShortAt, + /* 144 */ primitiveShortAtPut, + /* 145 */ primitiveConstantFill, + /* 146 */ (void (*)(void))0, + /* 147 */ (void (*)(void))0, + /* 148 */ primitiveClone, + /* 149 */ primitiveGetAttribute, + /* 150 */ primitiveCosine, + /* 151 */ (void (*)(void))0, + /* 152 */ (void (*)(void))0, + /* 153 */ (void (*)(void))0, + /* 154 */ (void (*)(void))0, + /* 155 */ (void (*)(void))0, + /* 156 */ (void (*)(void))0, + /* 157 */ (void (*)(void))0, + /* 158 */ primitiveCompareWith, + /* 159 */ primitiveHashMultiply, + /* 160 */ primitiveAdoptInstance, + /* 161 */ primitiveSetOrHasIdentityHash, + /* 162 */ (void (*)(void))0, + /* 163 */ primitiveGetImmutability, + /* 164 */ primitiveSetImmutability, + /* 165 */ primitiveIntegerAt, + /* 166 */ primitiveIntegerAtPut, + /* 167 */ primitiveYield, + /* 168 */ primitiveCopyObject, + /* 169 */ primitiveNotIdentical, + /* 170 */ primitiveAsCharacter, + /* 171 */ primitiveImmediateAsInteger, + /* 172 */ primitiveFetchNextMourner, + /* 173 */ primitiveSlotAt, + /* 174 */ primitiveSlotAtPut, + /* 175 */ primitiveBehaviorHash, + /* 176 */ primitiveMaxIdentityHash, + /* 177 */ primitiveAllInstances, + /* 178 */ primitiveAllObjects, + /* 179 */ (void (*)(void))0, + /* 180 */ primitiveGrowMemoryByAtLeast, + /* 181 */ primitiveSizeInBytesOfInstance, + /* 182 */ primitiveSizeInBytes, + /* 183 */ primitiveIsPinned, + /* 184 */ primitivePin, + /* 185 */ primitiveExitCriticalSection, + /* 186 */ primitiveEnterCriticalSection, + /* 187 */ primitiveTestAndSetOwnershipOfCriticalSection, + /* 188 */ primitiveExecuteMethodArgsArray, + /* 189 */ primitiveExecuteMethod, + /* 190 */ (void (*)(void))0, + /* 191 */ (void (*)(void))0, + /* 192 */ (void (*)(void))0, + /* 193 */ (void (*)(void))0, + /* 194 */ (void (*)(void))0, + /* 195 */ primitiveFindNextUnwindContext, + /* 196 */ primitiveTerminateTo, + /* 197 */ primitiveFindHandlerContext, + /* 198 */ (void (*)(void))0, + /* 199 */ (void (*)(void))0, + /* 200 */ primitiveClosureCopyWithCopiedValues, + /* 201 */ primitiveClosureValue, + /* 202 */ primitiveClosureValue, + /* 203 */ primitiveClosureValue, + /* 204 */ primitiveClosureValue, + /* 205 */ primitiveClosureValue, + /* 206 */ primitiveClosureValueWithArgs, + /* 207 */ primitiveFullClosureValue, + /* 208 */ primitiveFullClosureValueWithArgs, + /* 209 */ primitiveFullClosureValueNoContextSwitch, + /* 210 */ primitiveContextAt, + /* 211 */ primitiveContextAtPut, + /* 212 */ primitiveContextSize, + /* 213 */ primitiveContextPreserveTemps, + /* 214 */ primitiveVoidVMState, + /* 215 */ primitiveVoidVMStateForMethod, + /* 216 */ primitiveMethodXray, + /* 217 */ (void (*)(void))0, + /* 218 */ primitiveDoNamedPrimitiveWithArgs, + /* 219 */ (void (*)(void))0, + /* 220 */ primitiveGetSetFFIExceptionHandling, + /* 221 */ primitiveClosureValueNoContextSwitch, + /* 222 */ primitiveClosureValueNoContextSwitch, + /* 223 */ (void (*)(void))0, + /* 224 */ (void (*)(void))0, + /* 225 */ (void (*)(void))0, + /* 226 */ primitiveGetOwnerLog, + /* 227 */ primitiveVMCurrentThreadId, + /* 228 */ primitiveProcessBoundThreadId, + /* 229 */ primitiveProcessBindToThreadAffinity, + /* 230 */ primitiveRelinquishProcessor, + /* 231 */ primitiveForceDisplayUpdate, + /* 232 */ primitiveFormPrint, + /* 233 */ primitiveSetFullScreen, + /* 234 */ (void (*)(void))0, + /* 235 */ (void (*)(void))0, + /* 236 */ (void (*)(void))0, + /* 237 */ (void (*)(void))0, + /* 238 */ primitiveFloatArrayAt, + /* 239 */ primitiveFloatArrayAtPut, + /* 240 */ primitiveUTCMicrosecondClock, + /* 241 */ primitiveLocalMicrosecondClock, + /* 242 */ primitiveSignalAtUTCMicroseconds, + /* 243 */ primitiveUpdateTimezone, + /* 244 */ primitiveUtcAndTimezoneOffset, + /* 245 */ primitiveCoarseUTCMicrosecondClock, + /* 246 */ primitiveCoarseLocalMicrosecondClock, + /* 247 */ primitiveSnapshotEmbedded, + /* 248 */ primitiveArrayBecomeOneWayNoCopyHash, + /* 249 */ primitiveArrayBecomeOneWayCopyHashArg, + /* 250 */ primitiveClearVMProfile, + /* 251 */ primitiveControlVMProfiling, + /* 252 */ primitiveVMProfileSamplesInto, + /* 253 */ primitiveCollectCogCodeConstituents, + /* 254 */ primitiveVMParameter, + /* 255 */ (void (*)(void))0, + /* 256 */ (void (*)(void))256, + /* 257 */ (void (*)(void))257, + /* 258 */ (void (*)(void))258, + /* 259 */ (void (*)(void))259, + /* 260 */ (void (*)(void))260, + /* 261 */ (void (*)(void))261, + /* 262 */ (void (*)(void))262, + /* 263 */ (void (*)(void))263, + /* 264 */ (void (*)(void))264, + /* 265 */ (void (*)(void))265, + /* 266 */ (void (*)(void))266, + /* 267 */ (void (*)(void))267, + /* 268 */ (void (*)(void))268, + /* 269 */ (void (*)(void))269, + /* 270 */ (void (*)(void))270, + /* 271 */ (void (*)(void))271, + /* 272 */ (void (*)(void))272, + /* 273 */ (void (*)(void))273, + /* 274 */ (void (*)(void))274, + /* 275 */ (void (*)(void))275, + /* 276 */ (void (*)(void))276, + /* 277 */ (void (*)(void))277, + /* 278 */ (void (*)(void))278, + /* 279 */ (void (*)(void))279, + /* 280 */ (void (*)(void))280, + /* 281 */ (void (*)(void))281, + /* 282 */ (void (*)(void))282, + /* 283 */ (void (*)(void))283, + /* 284 */ (void (*)(void))284, + /* 285 */ (void (*)(void))285, + /* 286 */ (void (*)(void))286, + /* 287 */ (void (*)(void))287, + /* 288 */ (void (*)(void))288, + /* 289 */ (void (*)(void))289, + /* 290 */ (void (*)(void))290, + /* 291 */ (void (*)(void))291, + /* 292 */ (void (*)(void))292, + /* 293 */ (void (*)(void))293, + /* 294 */ (void (*)(void))294, + /* 295 */ (void (*)(void))295, + /* 296 */ (void (*)(void))296, + /* 297 */ (void (*)(void))297, + /* 298 */ (void (*)(void))298, + /* 299 */ (void (*)(void))299, + /* 300 */ (void (*)(void))300, + /* 301 */ (void (*)(void))301, + /* 302 */ (void (*)(void))302, + /* 303 */ (void (*)(void))303, + /* 304 */ (void (*)(void))304, + /* 305 */ (void (*)(void))305, + /* 306 */ (void (*)(void))306, + /* 307 */ (void (*)(void))307, + /* 308 */ (void (*)(void))308, + /* 309 */ (void (*)(void))309, + /* 310 */ (void (*)(void))310, + /* 311 */ (void (*)(void))311, + /* 312 */ (void (*)(void))312, + /* 313 */ (void (*)(void))313, + /* 314 */ (void (*)(void))314, + /* 315 */ (void (*)(void))315, + /* 316 */ (void (*)(void))316, + /* 317 */ (void (*)(void))317, + /* 318 */ (void (*)(void))318, + /* 319 */ (void (*)(void))319, + /* 320 */ (void (*)(void))320, + /* 321 */ (void (*)(void))321, + /* 322 */ (void (*)(void))322, + /* 323 */ (void (*)(void))323, + /* 324 */ (void (*)(void))324, + /* 325 */ (void (*)(void))325, + /* 326 */ (void (*)(void))326, + /* 327 */ (void (*)(void))327, + /* 328 */ (void (*)(void))328, + /* 329 */ (void (*)(void))329, + /* 330 */ (void (*)(void))330, + /* 331 */ (void (*)(void))331, + /* 332 */ (void (*)(void))332, + /* 333 */ (void (*)(void))333, + /* 334 */ (void (*)(void))334, + /* 335 */ (void (*)(void))335, + /* 336 */ (void (*)(void))336, + /* 337 */ (void (*)(void))337, + /* 338 */ (void (*)(void))338, + /* 339 */ (void (*)(void))339, + /* 340 */ (void (*)(void))340, + /* 341 */ (void (*)(void))341, + /* 342 */ (void (*)(void))342, + /* 343 */ (void (*)(void))343, + /* 344 */ (void (*)(void))344, + /* 345 */ (void (*)(void))345, + /* 346 */ (void (*)(void))346, + /* 347 */ (void (*)(void))347, + /* 348 */ (void (*)(void))348, + /* 349 */ (void (*)(void))349, + /* 350 */ (void (*)(void))350, + /* 351 */ (void (*)(void))351, + /* 352 */ (void (*)(void))352, + /* 353 */ (void (*)(void))353, + /* 354 */ (void (*)(void))354, + /* 355 */ (void (*)(void))355, + /* 356 */ (void (*)(void))356, + /* 357 */ (void (*)(void))357, + /* 358 */ (void (*)(void))358, + /* 359 */ (void (*)(void))359, + /* 360 */ (void (*)(void))360, + /* 361 */ (void (*)(void))361, + /* 362 */ (void (*)(void))362, + /* 363 */ (void (*)(void))363, + /* 364 */ (void (*)(void))364, + /* 365 */ (void (*)(void))365, + /* 366 */ (void (*)(void))366, + /* 367 */ (void (*)(void))367, + /* 368 */ (void (*)(void))368, + /* 369 */ (void (*)(void))369, + /* 370 */ (void (*)(void))370, + /* 371 */ (void (*)(void))371, + /* 372 */ (void (*)(void))372, + /* 373 */ (void (*)(void))373, + /* 374 */ (void (*)(void))374, + /* 375 */ (void (*)(void))375, + /* 376 */ (void (*)(void))376, + /* 377 */ (void (*)(void))377, + /* 378 */ (void (*)(void))378, + /* 379 */ (void (*)(void))379, + /* 380 */ (void (*)(void))380, + /* 381 */ (void (*)(void))381, + /* 382 */ (void (*)(void))382, + /* 383 */ (void (*)(void))383, + /* 384 */ (void (*)(void))384, + /* 385 */ (void (*)(void))385, + /* 386 */ (void (*)(void))386, + /* 387 */ (void (*)(void))387, + /* 388 */ (void (*)(void))388, + /* 389 */ (void (*)(void))389, + /* 390 */ (void (*)(void))390, + /* 391 */ (void (*)(void))391, + /* 392 */ (void (*)(void))392, + /* 393 */ (void (*)(void))393, + /* 394 */ (void (*)(void))394, + /* 395 */ (void (*)(void))395, + /* 396 */ (void (*)(void))396, + /* 397 */ (void (*)(void))397, + /* 398 */ (void (*)(void))398, + /* 399 */ (void (*)(void))399, + /* 400 */ (void (*)(void))400, + /* 401 */ (void (*)(void))401, + /* 402 */ (void (*)(void))402, + /* 403 */ (void (*)(void))403, + /* 404 */ (void (*)(void))404, + /* 405 */ (void (*)(void))405, + /* 406 */ (void (*)(void))406, + /* 407 */ (void (*)(void))407, + /* 408 */ (void (*)(void))408, + /* 409 */ (void (*)(void))409, + /* 410 */ (void (*)(void))410, + /* 411 */ (void (*)(void))411, + /* 412 */ (void (*)(void))412, + /* 413 */ (void (*)(void))413, + /* 414 */ (void (*)(void))414, + /* 415 */ (void (*)(void))415, + /* 416 */ (void (*)(void))416, + /* 417 */ (void (*)(void))417, + /* 418 */ (void (*)(void))418, + /* 419 */ (void (*)(void))419, + /* 420 */ (void (*)(void))420, + /* 421 */ (void (*)(void))421, + /* 422 */ (void (*)(void))422, + /* 423 */ (void (*)(void))423, + /* 424 */ (void (*)(void))424, + /* 425 */ (void (*)(void))425, + /* 426 */ (void (*)(void))426, + /* 427 */ (void (*)(void))427, + /* 428 */ (void (*)(void))428, + /* 429 */ (void (*)(void))429, + /* 430 */ (void (*)(void))430, + /* 431 */ (void (*)(void))431, + /* 432 */ (void (*)(void))432, + /* 433 */ (void (*)(void))433, + /* 434 */ (void (*)(void))434, + /* 435 */ (void (*)(void))435, + /* 436 */ (void (*)(void))436, + /* 437 */ (void (*)(void))437, + /* 438 */ (void (*)(void))438, + /* 439 */ (void (*)(void))439, + /* 440 */ (void (*)(void))440, + /* 441 */ (void (*)(void))441, + /* 442 */ (void (*)(void))442, + /* 443 */ (void (*)(void))443, + /* 444 */ (void (*)(void))444, + /* 445 */ (void (*)(void))445, + /* 446 */ (void (*)(void))446, + /* 447 */ (void (*)(void))447, + /* 448 */ (void (*)(void))448, + /* 449 */ (void (*)(void))449, + /* 450 */ (void (*)(void))450, + /* 451 */ (void (*)(void))451, + /* 452 */ (void (*)(void))452, + /* 453 */ (void (*)(void))453, + /* 454 */ (void (*)(void))454, + /* 455 */ (void (*)(void))455, + /* 456 */ (void (*)(void))456, + /* 457 */ (void (*)(void))457, + /* 458 */ (void (*)(void))458, + /* 459 */ (void (*)(void))459, + /* 460 */ (void (*)(void))460, + /* 461 */ (void (*)(void))461, + /* 462 */ (void (*)(void))462, + /* 463 */ (void (*)(void))463, + /* 464 */ (void (*)(void))464, + /* 465 */ (void (*)(void))465, + /* 466 */ (void (*)(void))466, + /* 467 */ (void (*)(void))467, + /* 468 */ (void (*)(void))468, + /* 469 */ (void (*)(void))469, + /* 470 */ (void (*)(void))470, + /* 471 */ (void (*)(void))471, + /* 472 */ (void (*)(void))472, + /* 473 */ (void (*)(void))473, + /* 474 */ (void (*)(void))474, + /* 475 */ (void (*)(void))475, + /* 476 */ (void (*)(void))476, + /* 477 */ (void (*)(void))477, + /* 478 */ (void (*)(void))478, + /* 479 */ (void (*)(void))479, + /* 480 */ (void (*)(void))480, + /* 481 */ (void (*)(void))481, + /* 482 */ (void (*)(void))482, + /* 483 */ (void (*)(void))483, + /* 484 */ (void (*)(void))484, + /* 485 */ (void (*)(void))485, + /* 486 */ (void (*)(void))486, + /* 487 */ (void (*)(void))487, + /* 488 */ (void (*)(void))488, + /* 489 */ (void (*)(void))489, + /* 490 */ (void (*)(void))490, + /* 491 */ (void (*)(void))491, + /* 492 */ (void (*)(void))492, + /* 493 */ (void (*)(void))493, + /* 494 */ (void (*)(void))494, + /* 495 */ (void (*)(void))495, + /* 496 */ (void (*)(void))496, + /* 497 */ (void (*)(void))497, + /* 498 */ (void (*)(void))498, + /* 499 */ (void (*)(void))499, + /* 500 */ (void (*)(void))500, + /* 501 */ (void (*)(void))501, + /* 502 */ (void (*)(void))502, + /* 503 */ (void (*)(void))503, + /* 504 */ (void (*)(void))504, + /* 505 */ (void (*)(void))505, + /* 506 */ (void (*)(void))506, + /* 507 */ (void (*)(void))507, + /* 508 */ (void (*)(void))508, + /* 509 */ (void (*)(void))509, + /* 510 */ (void (*)(void))510, + /* 511 */ (void (*)(void))511, + /* 512 */ (void (*)(void))512, + /* 513 */ (void (*)(void))513, + /* 514 */ (void (*)(void))514, + /* 515 */ (void (*)(void))515, + /* 516 */ (void (*)(void))516, + /* 517 */ (void (*)(void))517, + /* 518 */ (void (*)(void))518, + /* 519 */ (void (*)(void))519, + /* 520 */ (void (*)(void))0, + /* 521 */ (void (*)(void))0, + /* 522 */ (void (*)(void))0, + /* 523 */ (void (*)(void))0, + /* 524 */ (void (*)(void))0, + /* 525 */ (void (*)(void))0, + /* 526 */ (void (*)(void))0, + /* 527 */ (void (*)(void))0, + /* 528 */ (void (*)(void))0, + /* 529 */ (void (*)(void))0, + /* 530 */ (void (*)(void))0, + /* 531 */ (void (*)(void))0, + /* 532 */ (void (*)(void))0, + /* 533 */ (void (*)(void))0, + /* 534 */ (void (*)(void))0, + /* 535 */ (void (*)(void))0, + /* 536 */ (void (*)(void))0, + /* 537 */ (void (*)(void))0, + /* 538 */ (void (*)(void))0, + /* 539 */ (void (*)(void))0, + /* 540 */ (void (*)(void))0, + /* 541 */ primitiveSmallFloatAdd, + /* 542 */ primitiveSmallFloatSubtract, + /* 543 */ primitiveSmallFloatLessThan, + /* 544 */ primitiveSmallFloatGreaterThan, + /* 545 */ primitiveSmallFloatLessOrEqual, + /* 546 */ primitiveSmallFloatGreaterOrEqual, + /* 547 */ primitiveSmallFloatEqual, + /* 548 */ primitiveSmallFloatNotEqual, + /* 549 */ primitiveSmallFloatMultiply, + /* 550 */ primitiveSmallFloatDivide, + /* 551 */ primitiveSmallFloatTruncated, + /* 552 */ primitiveSmallFloatFractionalPart, + /* 553 */ primitiveSmallFloatExponent, + /* 554 */ primitiveSmallFloatTimesTwoPower, + /* 555 */ primitiveSmallFloatSquareRoot, + /* 556 */ primitiveSmallFloatSine, + /* 557 */ primitiveSmallFloatArctan, + /* 558 */ primitiveSmallFloatLogN, + /* 559 */ primitiveSmallFloatExp, + /* 560 */ primitiveSmallFloatCosine, + /* 561 */ (void (*)(void))0, + /* 562 */ (void (*)(void))0, + /* 563 */ (void (*)(void))0, + /* 564 */ (void (*)(void))0, + /* 565 */ (void (*)(void))0, + /* 566 */ (void (*)(void))0, + /* 567 */ primitiveSetPriority, + /* 568 */ primitiveSuspendBackingUpV1, + /* 569 */ (void (*)(void))0, + /* 570 */ primitiveFlushExternalPrimitives, + /* 571 */ primitiveUnloadModule, + /* 572 */ primitiveListBuiltinModule, + /* 573 */ primitiveListExternalModule, + /* 574 */ (void (*)(void))0, + /* 575 */ primitiveHighBit, + /* 576 */ (void (*)(void))0, + /* 577 */ (void (*)(void))0, + /* 578 */ primitiveSuspendBackingUpV2, + /* 579 */ (void (*)(void))0, + /* 580 */ primitivePinnedNew, + /* 581 */ primitivePinnedNewWithArg, + /* 582 */ primitiveUninitializedNewWithArg, + /* 583 */ primitiveUninitializedPinnedNewWithArg, + 0 }; +static void (*externalPrimitiveTable[MaxExternalPrimitiveTableSize + 1 /* 4097 */])(void); +static signed short primitiveMetadataTable[MaxPrimitiveIndex + 2 /* 585 */] = { +// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 +/*0*/ -256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-256, +/*20*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0x101, 0x101, +/*40*/ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 3, 3, 3, 3, +/*60*/ 1, 1, 0, 1, 1,-256,-256,-256, 0x100, 0x100, 0, 0, 0, 268, 0x100, 0, 0x100, 0, +/*78*/ 0, 0, +/*80*/ -256,-256,-256, 4, 4, 0, 0x100, 0, 0x200,-256,-256, 0, 0, 0, 0x100,-256, 0,-256, +/*98*/ 0, 0, +/*100*/ 260, 0x200, 0x200, 0x200,-256, 513,-256,-256,-256,-256, 0, 0x100, 0, 0,-256, +/*115*/ 0x100, 0, 12, 260, 0, +/*120*/ 524, 0x100,-256,-256, 1, 0, 0, 0, 0,-255,-256, 0, 0, 0, 0,-256, 0,-256,-256, +/*139*/ 0, +/*140*/ -256, 0x100,-256, 0x101, 0x101, 0x101,-256,-256, 12, 0, 3,-256,-256,-256,-256, +/*155*/ -256,-256,-256, 0x100, 0x100, +/*160*/ 0, 0,-256, 1, 1, 0x100, 0x100,-256, 0x101, 0, 0, 0,-256, 268, 0x100, 0,-256, +/*177*/ 0,-256,-256, +/*180*/ 0, 1, 1, 1, 0x100, 0x100, 0x100, 0x100, 0x200, 0x100,-256,-256,-256,-256,-256, +/*195*/ 0x100, 0x200, 0,-256,-256, +/*200*/ -256, 0x200, 0x200, 0x200, 0x200, 0x200, 0x200, 0x100, 0x100, 0x100, 0x100, 0x100, +/*212*/ 0x100, 0,-256, 0, 0,-256, 772,-256, +/*220*/ 0, 0x200, 0x200,-256,-256,-256, 0x100,-256, 0, 0x100, 0,-256, 0x200, 0,-256, +/*235*/ -256,-256,-256,-253,-253, +/*240*/ -256,-256, 0,-256, 0x100,-256,-256,-256, 0, 0,-256, 0, 0x100, 0, 0,-256,-256, +/*257*/ -256,-256,-256, +/*260*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*276*/ -256,-256,-256,-256, +/*280*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*296*/ -256,-256,-256,-256, +/*300*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*316*/ -256,-256,-256,-256, +/*320*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*336*/ -256,-256,-256,-256, +/*340*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*356*/ -256,-256,-256,-256, +/*360*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*376*/ -256,-256,-256,-256, +/*380*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*396*/ -256,-256,-256,-256, +/*400*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*416*/ -256,-256,-256,-256, +/*420*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*436*/ -256,-256,-256,-256, +/*440*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*456*/ -256,-256,-256,-256, +/*460*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*476*/ -256,-256,-256,-256, +/*480*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*496*/ -256,-256,-256,-256, +/*500*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*516*/ -256,-256,-256,-256, +/*520*/ -256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256,-256, +/*536*/ -256,-256,-256,-256, +/*540*/ -256, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 3, 1, 3, 3, 3, 3, 3, 3, +/*560*/ 3,-256,-256,-256,-256,-256,-256, 0x100, 0x200,-256,-244, 0x100, 0, 0,-256, 1, +/*576*/ -256,-256, 0x200,-256, +/*580*/ 0, 0, 0, 0 + }; +static char * traceSources[] = { + "?", "enterCritical", "exitCritical", "resume", "syncsig", "suspend", "wait", + "yield", "priority", "eventcheck", "threadsched", "ownVM", "bindToThread", "switchIfNecessary", + "tryToExecuteSmalltalk" + }; +sqInt imageHeaderFlags; +sqInt checkForLeaks; +sqInt maxLiteralCountForCompile = MaxLiteralCountForCompile /* 60 */; +sqInt debugCallbackPath; +sqInt breakLookupClassTag; +char * primTracePluginName; +sqInt breakSelectorLength = MinSmallInteger; +void * displayBits; +sqInt desiredEdenBytes; +sqInt desiredNumStackPages; +sqInt extraVMMemory; +sqInt eventTraceMask; +usqInt maxOldSpaceSize; +sqInt desiredCogCodeSize; +char * breakSelector; +sqInt disownCount; +sqInt ffiExceptionResponse; +usqInt heapBase; +sqInt inIOProcessEvents; +int (*showSurfaceFn)(sqIntptr_t, int, int, int, int); +sqInt deferDisplayUpdates; +struct VirtualMachine* interpreterProxy; +sqInt debugCallbackInvokes; +sqInt debugCallbackReturns; +sqInt suppressHeartbeatFlag; +sqInt cannotDeferDisplayUpdates; +sqInt enableOwnerLog; +sqInt checkedPluginName; +const char *interpreterVersion = "Open Smalltalk Cog MT VM [CoInterpreterMT VMMaker.oscog-eem.3776]"; +const char * leakCheckFlagsMeanings[] = { + "1: check full GC", "2: check new space GC", "4: check incremental GC", "8: check become", + "16: check image segment", "32: check free space", "64: check shorten", "128: check prim call", + null + }; +sqInt minBackwardJumpCountForCompile = MinBackwardJumpCountForCompile /* 40 */; +int displayWidth; +int displayDepth; +int displayHeight; +char expensiveAsserts = 0; +volatile int sendTrace; + + +/*** Macros ***/ +#define cPICNumCases stackCheckOffset +#define cPICNumCasesHack hack hack hack i.e. the getter macro does all the work +#define nextOpenPIC methodObject +#define nextOpenPICHack hack hack hack i.e. the getter macro does all the work +#define mostRecentlyUsedPage() GIV(mostRecentlyUsedPage) +#define stackPageAtpages(index,pages) ((pages) + (index)) +#define statMaxPageCountWhenMapping() GIV(statMaxPageCountWhenMapping) +#define assertValidProcessorStackPointersForIndex(ignored) 0 +#define getNumThreads() GIV(numThreads) +#define ownerLog() GIV(ownerLog) +#define ownerLogIndex() GIV(ownerLogIndex) +#define compilationBreakpointclassTagisMNUCase(sel, classTag, isMNU) do { \ + if (numBytesOf(sel) == (isMNU ? -breakSelectorLength : breakSelectorLength) \ + && !strncmp((char *)((sel) + BaseHeaderSize), breakSelector, (isMNU ? -breakSelectorLength : breakSelectorLength))) { \ + suppressHeartbeatFlag = 1; \ + compilationBreakpointFor(sel); \ + } \ +} while (0) +#define compilationBreakpointisMNUCase(sel, isMNU) do { \ + if (numBytesOf(sel) == (isMNU ? -breakSelectorLength : breakSelectorLength) \ + && !strncmp((char *)((sel) + BaseHeaderSize), breakSelector, (isMNU ? -breakSelectorLength : breakSelectorLength))) { \ + suppressHeartbeatFlag = 1; \ + compilationBreakpointFor(sel); \ + } \ +} while (0) +#define flagInterpretedMethods() (imageHeaderFlags & 8) +#define getDesiredCogCodeSize() integerObjectOf(desiredCogCodeSize) +#define heapBase() heapBase +#define assertCStackPointersBelongToCurrentThread(ignored) 0 +#define assertCStackPointersBelongToDisowningThread() 0 +#define assertProcessorStackPointersBelongToCurrentThread(ignored) 0 +#define assertValidNewMethodPropertyFlags() 0 +#define disownResultFlags(disownResult) ((disownResult) >> ThreadIdShift) +#define enterSmalltalkExecutive() enterSmalltalkExecutiveImplementation() +#define threadSchedulingLoop(vmThread) threadSchedulingLoopImplementation(vmThread) +#define pageIndexForstackBasePlus1bytesPerPage(pointer,stkBasePlus1,pageByteSize) (((char *)(pointer) - (stkBasePlus1)) / (pageByteSize)) +#define nullHeaderForMachineCodeMethod() 0x8000000A000035LL +#define startOfMemory() heapBase +#define numTagBits() 3 +#define shiftForWord() 3 +#define smallFloatExponentBits() 8 +#define smallFloatExponentOffset() 896 +#define smallFloatMantissaBits() 52 +#define smallFloatTag() 4 +#define tagMask() 0x7 +#define wordSize() 8 +#define arrayClassIndexPun() 16 +#define arrayFormat() 2 +#define characterTag() 2 +#define classIndexMask() 0x3FFFFF +#define classIsItselfClassIndexPun() 31 +#define classTableMajorIndexShift() 10 +#define ephemeronFormat() 5 +#define extraAlignedBitByteShift() 6 +#define extraAlignedBitShift() 22 +#define firstByteFormat() 16 +#define firstClassIndexPun() 16 +#define firstCompiledMethodFormat() 24 +#define firstLongFormat() 10 +#define firstShortFormat() 12 +#define fixedFieldsFieldWidth() 16 +#define formatFieldByteOffset() 3 +#define formatFieldWidthShift() 5 +#define formatMask() 0x1F +#define formatShift() 24 +#define forwardedFormat() 7 +#define greyBitByteShift() 6 +#define greyBitShift() 54 +#define identityHashFullWordShift() 32 +#define identityHashHalfWordMask() 0x3FFFFF +#define immutableBitByteShift() 7 +#define immutableBitShift() 23 +#define immutableExtraBitsByteOffset() 2 +#define indexablePointersFormat() 3 +#define isForwardedObjectClassIndexPun() 8 +#define isFreeObjectClassIndexPun() 0 +#define lastClassIndexPun() 31 +#define markBitsByteOffset() 6 +#define markedBitByteShift() 7 +#define markedBitFullShift() 55 +#define markedBitHalfShift() 23 +#define nonIndexablePointerFormat() 1 +#define numSlotsFieldByteOffset() 7 +#define numSlotsFullShift() 56 +#define numSlotsHalfShift() 24 +#define numSlotsMask() 0xFF +#define pinnedBitByteShift() 6 +#define pinnedBitShift() 30 +#define rememberedBitByteShift() 5 +#define rememberedBitShift() 29 +#define rootTableCapacity() GIV(rememberedSetLimit) +#define rootTableCount() GIV(rememberedSetSize) +#define segmentBridgePun() 3 +#define sixtyFourBitIndexableFormat() 9 +#define sixtyFourBitLongsClassIndexPun() 19 +#define smallIntegerTag() 1 +#define thirtyTwoBitLongsClassIndexPun() 18 +#define unusedBitByteShift() 7 +#define unusedBitShift() 31 +#define weakArrayClassIndexPun() 17 +#define weakArrayFormat() 4 +#define alternateHeaderNumLiteralsMask() 0x7FFF +#define cr() fputc('\n',transcript) +#define dispatchFunctionPointer(aFunctionPointer) (aFunctionPointer)() +#define numericPrimsMixArithmetic() (imageHeaderFlags & 0x100) +#define numericPrimsMixComparison() (imageHeaderFlags & 0x800) +#define primTraceLogIndex(aValue) (GIV(primTraceLogIndex) = (aValue)) +#define printChar(aCharacter) fputc(aCharacter,transcript) +#define remoteIsInstVarAccess() 128 +#define stackPageFrameBytes() (256 * BytesPerWord) +#define allocatype(numElements, elementType) alloca((numElements)*sizeof(elementType)) +#define numElementsIn(anArray) (sizeof(anArray)/sizeof(anArray[0])) +#define oopisGreaterThanOrEqualTo(anOop,otherOop) ((usqInt)(anOop) >= (usqInt)(otherOop)) +#define oopisGreaterThanOrEqualToandLessThanOrEqualTo(anOop,baseOop,limitOop) ((usqInt)(anOop) >= (usqInt)(baseOop) && (usqInt)(anOop) <= (usqInt)(limitOop)) +#define oopisGreaterThanOrEqualToandLessThan(anOop,baseOop,limitOop) ((usqInt)(anOop) >= (usqInt)(baseOop) && (usqInt)(anOop) < (usqInt)(limitOop)) +#define oopisGreaterThan(anOop,otherOop) ((usqInt)(anOop) > (usqInt)(otherOop)) +#define oopisGreaterThanandLessThan(anOop,baseOop,limitOop) ((usqInt)(anOop) > (usqInt)(baseOop) && (usqInt)(anOop) < (usqInt)(limitOop)) +#define oopisLessThanOrEqualTo(anOop,otherOop) ((usqInt)(anOop) <= (usqInt)(otherOop)) +#define oopisLessThan(anOop,otherOop) ((usqInt)(anOop) < (usqInt)(otherOop)) + + +/*** Methods ***/ + + +/* This is the main interpreter loop. + In a pure interpreter it loops forever, fetching and executing bytecodes. + With the Cogit JIT executing code as well, the interpreter is reentered + from machine code + whenever the machine code wants to interpret a method instead of executing + its machine + code. Entry into the interpreter is done via a ''jump call'' in machine + code that uses + CFramePointer and CStackPointer to find the base of the C stack (set in + CoInterpreter>> enterSmalltalkExecutiveImplementation) and substitutes + CReturnAddress as the return + address in the code so it always appears that interpret has been called + from CoInterpreter>>enterSmalltalkExecutiveImplementation, which may be + important to, + for example, C exception handling inside the VM. + + When running in the context of a browser plugin VM the interpreter must + return control + to the browser periodically. This should done only when the state of the + currently running + Squeak thread is safely stored in the object heap. Since this is the case + at the moment + that a check for interrupts is performed, that is when we return to the + browser if it is time + to do so. Interrupt checks happen quite frequently. */ +/* If stacklimit is zero then the stack pages have not been initialized. */ +/* See superclass method comment. + Override to add a variable to cache getReturnAddress (cuz it's a macro) */ + + /* CoInterpreterMT>>#interpret */ +sqInt +interpret(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + register sqInt currentBytecode CB_REG; + sqInt extA; + sqInt extB; + sqInt header; + sqInt lkupClassTag; + register char *localFP FP_REG; + register char *localIP IP_REG; + sqInt localReturnValue; + register char *localSP SP_REG; + sqInt methodHeader; + sqInt numExtB; + sqInt stackPageBytes; + usqIntptr_t stackZoneBytes; + sqInt theRetAddr; + void *theStackMemory; + JUMP_TABLE; + + if (!asserta((sizeof(jumpTable)/sizeof(jumpTable[0])) >= 512)) + error("bytecode jumpTable too small"); + + +# if !NDEBUG + theRetAddr = getReturnAddress(); +# endif + + if (!GIV(stackLimit)) { + /* begin initStackPagesAndInterpret */ + /* begin ensureInitializeStackZone */ + if (!GIV(pages)) { + stackPageBytes = stackPageByteSize(); + stackZoneBytes = (GIV(numStackPages) * ((sizeof(CogStackPage)) + (stackPageByteSize()))) + BytesPerWord; + theStackMemory = alloca(stackZoneBytes); + memset(theStackMemory, 0, stackZoneBytes); + initializeStacknumSlotspageSize(theStackMemory, stackZoneBytes / BytesPerWord, stackPageBytes / BytesPerWord); + } + + /* Once the stack pages are initialized we can continue to bootstrap the system. */ + loadInitialContext(); + + /* We're ready for the heartbeat (poll interrupt) */ + ioInitHeartbeat(); + initialEnterSmalltalkExecutive(); + return null; + } + assertCStackWellAligned(); + + /* An unchecked write is probably faster, so instead of + CReturnAddress ifNil: + [CReturnAddress := self cCoerceSimple: self getReturnAddress to: #usqIntptr_t] + we have simply */ + assert((!GIV(CReturnAddress)) + || (GIV(CReturnAddress) == (((usqIntptr_t) (getReturnAddress()))))); + GIV(CReturnAddress) = ((usqIntptr_t) (getReturnAddress())); + + /* record entry time when running as a browser plug-in */ + browserPluginInitialiseIfNeeded(); + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + assert(!((GIV(instructionPointer) == (ceReturnToInterpreterPC())))); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 1, __LINE__); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + + /* begin initExtensions */ + extA = (numExtB = (extB = 0)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + while (1) { + /* begin dispatchBytecode */ + bytecodeDispatchDebugHook(); + + VM_LABEL(bytecodeDispatch); + switch (currentBytecode) { + CASE(0) // pushReceiverVariableBytecode + CASE(256) // 0 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize))); + } + BREAK; + CASE(1) // pushReceiverVariableBytecode + CASE(257) // 1 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode1); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 8 /* (1 << shiftForWord) */))); + } + BREAK; + CASE(2) // pushReceiverVariableBytecode + CASE(258) // 2 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode2); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 16 /* (2 << shiftForWord) */))); + } + BREAK; + CASE(3) // pushReceiverVariableBytecode + CASE(259) // 3 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode3); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 24 /* (3 << shiftForWord) */))); + } + BREAK; + CASE(4) // pushReceiverVariableBytecode + CASE(260) // 4 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode4); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 32 /* (4 << shiftForWord) */))); + } + BREAK; + CASE(5) // pushReceiverVariableBytecode + CASE(261) // 5 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode5); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 40 /* (5 << shiftForWord) */))); + } + BREAK; + CASE(6) // pushReceiverVariableBytecode + CASE(262) // 6 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode6); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 48 /* (6 << shiftForWord) */))); + } + BREAK; + CASE(7) // pushReceiverVariableBytecode + CASE(263) // 7 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode7); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 56 /* (7 << shiftForWord) */))); + } + BREAK; + CASE(8) // pushReceiverVariableBytecode + CASE(264) // 8 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode8); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 64 /* (8 << shiftForWord) */))); + } + BREAK; + CASE(9) // pushReceiverVariableBytecode + CASE(265) // 9 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode9); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 72 /* (9 << shiftForWord) */))); + } + BREAK; + CASE(10) // pushReceiverVariableBytecode + CASE(266) // 10 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode10); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 80 /* (10 << shiftForWord) */))); + } + BREAK; + CASE(11) // pushReceiverVariableBytecode + CASE(267) // 11 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode11); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 88 /* (11 << shiftForWord) */))); + } + BREAK; + CASE(12) // pushReceiverVariableBytecode + CASE(268) // 12 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode12); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 96 /* (12 << shiftForWord) */))); + } + BREAK; + CASE(13) // pushReceiverVariableBytecode + CASE(269) // 13 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode13); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 104 /* (13 << shiftForWord) */))); + } + BREAK; + CASE(14) // pushReceiverVariableBytecode + CASE(270) // 14 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode14); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 112 /* (14 << shiftForWord) */))); + } + BREAK; + CASE(15) // pushReceiverVariableBytecode + CASE(271) // 15 pushReceiverVariableBytecode + { + VM_LABEL(pushReceiverVariableBytecode15); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + 120 /* (15 << shiftForWord) */))); + } + BREAK; + CASE(16) // pushTemporaryVariableBytecode + CASE(320) // 64 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (0 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + (frameNumArgs * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + (frameNumArgs * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(17) // pushTemporaryVariableBytecode + CASE(321) // 65 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode1); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (1 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 1) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 1) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(18) // pushTemporaryVariableBytecode + CASE(322) // 66 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode2); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (2 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 2) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 2) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(19) // pushTemporaryVariableBytecode + CASE(323) // 67 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode3); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (3 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 3) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 3) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(20) // pushTemporaryVariableBytecode + CASE(324) // 68 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode4); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (4 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 4) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 4) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(21) // pushTemporaryVariableBytecode + CASE(325) // 69 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode5); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (5 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 5) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 5) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(22) // pushTemporaryVariableBytecode + CASE(326) // 70 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode6); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (6 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 6) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 6) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(23) // pushTemporaryVariableBytecode + CASE(327) // 71 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode7); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (7 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 7) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 7) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(24) // pushTemporaryVariableBytecode + CASE(328) // 72 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode8); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (8 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 8) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 8) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(25) // pushTemporaryVariableBytecode + CASE(329) // 73 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode9); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (9 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 9) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 9) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(26) // pushTemporaryVariableBytecode + CASE(330) // 74 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode10); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (10 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 10) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 10) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(27) // pushTemporaryVariableBytecode + CASE(331) // 75 pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode11); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (11 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 11) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 11) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(28) // pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode12); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (12 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 12) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 12) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(29) // pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode13); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (13 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 13) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 13) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(30) // pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode14); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (14 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 14) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 14) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(31) // pushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt object; + + VM_LABEL(pushTemporaryVariableBytecode15); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = /* itemporary:in: */ + (15 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - 15) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 15) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(32) // pushLiteralConstantBytecode + CASE(288) // 32 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 8 /* ((0 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(33) // pushLiteralConstantBytecode + CASE(289) // 33 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode1); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 16 /* ((1 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(34) // pushLiteralConstantBytecode + CASE(290) // 34 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode2); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 24 /* ((2 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(35) // pushLiteralConstantBytecode + CASE(291) // 35 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode3); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 32 /* ((3 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(36) // pushLiteralConstantBytecode + CASE(292) // 36 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode4); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 40 /* ((4 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(37) // pushLiteralConstantBytecode + CASE(293) // 37 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode5); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 48 /* ((5 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(38) // pushLiteralConstantBytecode + CASE(294) // 38 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode6); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 56 /* ((6 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(39) // pushLiteralConstantBytecode + CASE(295) // 39 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode7); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 64 /* ((7 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(40) // pushLiteralConstantBytecode + CASE(296) // 40 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode8); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 72 /* ((8 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(41) // pushLiteralConstantBytecode + CASE(297) // 41 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode9); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 80 /* ((9 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(42) // pushLiteralConstantBytecode + CASE(298) // 42 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode10); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 88 /* ((10 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(43) // pushLiteralConstantBytecode + CASE(299) // 43 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode11); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 96 /* ((11 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(44) // pushLiteralConstantBytecode + CASE(300) // 44 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode12); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 104 /* ((12 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(45) // pushLiteralConstantBytecode + CASE(301) // 45 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode13); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 112 /* ((13 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(46) // pushLiteralConstantBytecode + CASE(302) // 46 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode14); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 120 /* ((14 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(47) // pushLiteralConstantBytecode + CASE(303) // 47 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode15); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 128 /* ((15 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(48) // pushLiteralConstantBytecode + CASE(304) // 48 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode16); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 136 /* ((16 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(49) // pushLiteralConstantBytecode + CASE(305) // 49 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode17); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 144 /* ((17 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(50) // pushLiteralConstantBytecode + CASE(306) // 50 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode18); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 152 /* ((18 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(51) // pushLiteralConstantBytecode + CASE(307) // 51 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode19); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 160 /* ((19 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(52) // pushLiteralConstantBytecode + CASE(308) // 52 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode20); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 168 /* ((20 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(53) // pushLiteralConstantBytecode + CASE(309) // 53 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode21); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 176 /* ((21 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(54) // pushLiteralConstantBytecode + CASE(310) // 54 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode22); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 184 /* ((22 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(55) // pushLiteralConstantBytecode + CASE(311) // 55 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode23); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 192 /* ((23 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(56) // pushLiteralConstantBytecode + CASE(312) // 56 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode24); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 200 /* ((24 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(57) // pushLiteralConstantBytecode + CASE(313) // 57 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode25); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 208 /* ((25 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(58) // pushLiteralConstantBytecode + CASE(314) // 58 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode26); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 216 /* ((26 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(59) // pushLiteralConstantBytecode + CASE(315) // 59 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode27); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 224 /* ((27 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(60) // pushLiteralConstantBytecode + CASE(316) // 60 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode28); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 232 /* ((28 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(61) // pushLiteralConstantBytecode + CASE(317) // 61 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode29); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 240 /* ((29 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(62) // pushLiteralConstantBytecode + CASE(318) // 62 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode30); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 0xF8 /* ((30 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(63) // pushLiteralConstantBytecode + CASE(319) // 63 pushLiteralConstantBytecode + { + sqInt object; + + VM_LABEL(pushLiteralConstantBytecode31); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + 0x100 /* ((31 + LiteralStart) << shiftForWord) */)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(64) // pushLiteralVariableBytecode + CASE(272) // 16 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 8 /* ((0 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(1 /* (0 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(65) // pushLiteralVariableBytecode + CASE(273) // 17 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode1); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 16 /* ((1 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(2 /* (1 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(66) // pushLiteralVariableBytecode + CASE(274) // 18 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode2); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 24 /* ((2 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(3 /* (2 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(67) // pushLiteralVariableBytecode + CASE(275) // 19 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode3); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 32 /* ((3 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(4 /* (3 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(68) // pushLiteralVariableBytecode + CASE(276) // 20 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode4); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 40 /* ((4 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(5 /* (4 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(69) // pushLiteralVariableBytecode + CASE(277) // 21 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode5); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 48 /* ((5 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(6 /* (5 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(70) // pushLiteralVariableBytecode + CASE(278) // 22 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode6); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 56 /* ((6 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(7 /* (6 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(71) // pushLiteralVariableBytecode + CASE(279) // 23 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode7); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 64 /* ((7 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(8 /* (7 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(72) // pushLiteralVariableBytecode + CASE(280) // 24 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode8); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 72 /* ((8 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(9 /* (8 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(73) // pushLiteralVariableBytecode + CASE(281) // 25 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode9); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 80 /* ((9 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(10 /* (9 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(74) // pushLiteralVariableBytecode + CASE(282) // 26 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode10); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 88 /* ((10 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(11 /* (10 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(75) // pushLiteralVariableBytecode + CASE(283) // 27 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode11); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 96 /* ((11 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(12 /* (11 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(76) // pushLiteralVariableBytecode + CASE(284) // 28 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode12); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 104 /* ((12 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(13 /* (12 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(77) // pushLiteralVariableBytecode + CASE(285) // 29 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode13); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 112 /* ((13 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(14 /* (13 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(78) // pushLiteralVariableBytecode + CASE(286) // 30 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode14); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 120 /* ((14 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(15 /* (14 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(79) // pushLiteralVariableBytecode + CASE(287) // 31 pushLiteralVariable16CasesBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode15); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 128 /* ((15 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(16 /* (15 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(80) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode16); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 136 /* ((16 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(17 /* (16 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(81) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode17); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 144 /* ((17 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(18 /* (17 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(82) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode18); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 152 /* ((18 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(19 /* (18 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(83) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode19); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 160 /* ((19 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(20 /* (19 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(84) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode20); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 168 /* ((20 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(21 /* (20 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(85) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode21); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 176 /* ((21 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(22 /* (21 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(86) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode22); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 184 /* ((22 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(23 /* (22 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(87) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode23); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 192 /* ((23 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(24 /* (23 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(88) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode24); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 200 /* ((24 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(25 /* (24 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(89) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode25); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 208 /* ((25 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(26 /* (25 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(90) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode26); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 216 /* ((26 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(27 /* (26 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(91) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode27); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 224 /* ((27 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(28 /* (27 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(92) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode28); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 232 /* ((28 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(29 /* (28 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(93) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode29); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 240 /* ((29 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(30 /* (29 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(94) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode30); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 0xF8 /* ((30 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(0x1F /* (30 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(95) // pushLiteralVariableBytecode + { + sqInt litVar; + + VM_LABEL(pushLiteralVariableBytecode31); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + 0x100 /* ((31 + LiteralStart) << shiftForWord) */)); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(32 /* (31 + LiteralStart) */, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + 8 /* (ValueIndex << shiftForWord) */))); + } + BREAK; + CASE(96) // storeAndPopReceiverVariableBytecode + CASE(97) // storeAndPopReceiverVariableBytecode + CASE(98) // storeAndPopReceiverVariableBytecode + CASE(99) // storeAndPopReceiverVariableBytecode + CASE(100) // storeAndPopReceiverVariableBytecode + CASE(101) // storeAndPopReceiverVariableBytecode + CASE(102) // storeAndPopReceiverVariableBytecode + CASE(103) // storeAndPopReceiverVariableBytecode + CASE(456) // 200 storeAndPopReceiverVariableBytecode + CASE(457) // 201 storeAndPopReceiverVariableBytecode + CASE(458) // 202 storeAndPopReceiverVariableBytecode + CASE(459) // 203 storeAndPopReceiverVariableBytecode + CASE(460) // 204 storeAndPopReceiverVariableBytecode + CASE(461) // 205 storeAndPopReceiverVariableBytecode + CASE(462) // 206 storeAndPopReceiverVariableBytecode + CASE(463) // 207 storeAndPopReceiverVariableBytecode + { + usqInt instVarIndex; + sqInt rcvr; + sqInt top; + + VM_LABEL(storeAndPopReceiverVariableBytecode); + rcvr = longAt(localFP + FoxIFReceiver); + top = longAt(localSP); + instVarIndex = currentBytecode & 7; + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; +# if IMMUTABILITY + assert(!((isImmediate(rcvr)))); + if ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),rcvr); + longAtput((localSP -= BytesPerOop),top); + longAtput((localSP -= BytesPerOop),(((instVarIndex + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l1; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(instVarIndex, rcvr, top)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(top & (tagMask())))) + && (oopisLessThan(top, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((instVarIndex << (shiftForWord())))),top); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l1: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(104) // storeAndPopTemporaryVariableBytecode + CASE(464) // 208 storeAndPopTemporaryVariableBytecode + { + usqInt frameNumArgs; + + VM_LABEL(storeAndPopTemporaryVariableBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (0 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + (frameNumArgs * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + (frameNumArgs * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(105) // storeAndPopTemporaryVariableBytecode + CASE(465) // 209 storeAndPopTemporaryVariableBytecode + { + usqInt frameNumArgs; + + VM_LABEL(storeAndPopTemporaryVariableBytecode1); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (1 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 1) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 1) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(106) // storeAndPopTemporaryVariableBytecode + CASE(466) // 210 storeAndPopTemporaryVariableBytecode + { + usqInt frameNumArgs; + + VM_LABEL(storeAndPopTemporaryVariableBytecode2); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (2 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 2) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 2) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(107) // storeAndPopTemporaryVariableBytecode + CASE(467) // 211 storeAndPopTemporaryVariableBytecode + { + usqInt frameNumArgs; + + VM_LABEL(storeAndPopTemporaryVariableBytecode3); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (3 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 3) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 3) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(108) // storeAndPopTemporaryVariableBytecode + CASE(468) // 212 storeAndPopTemporaryVariableBytecode + { + usqInt frameNumArgs; + + VM_LABEL(storeAndPopTemporaryVariableBytecode4); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (4 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 4) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 4) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(109) // storeAndPopTemporaryVariableBytecode + CASE(469) // 213 storeAndPopTemporaryVariableBytecode + { + usqInt frameNumArgs; + + VM_LABEL(storeAndPopTemporaryVariableBytecode5); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (5 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 5) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 5) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(110) // storeAndPopTemporaryVariableBytecode + CASE(470) // 214 storeAndPopTemporaryVariableBytecode + { + usqInt frameNumArgs; + + VM_LABEL(storeAndPopTemporaryVariableBytecode6); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (6 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 6) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 6) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(111) // storeAndPopTemporaryVariableBytecode + CASE(471) // 215 storeAndPopTemporaryVariableBytecode + { + usqInt frameNumArgs; + + VM_LABEL(storeAndPopTemporaryVariableBytecode7); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (7 < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - 7) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - 7) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(112) // pushReceiverBytecode + CASE(332) // 76 pushReceiverBytecode + { + VM_LABEL(pushReceiverBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),longAt(localFP + FoxIFReceiver)); + } + BREAK; + CASE(113) // pushConstantTrueBytecode + CASE(333) // 77 pushConstantTrueBytecode + { + VM_LABEL(pushConstantTrueBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),GIV(trueObj)); + } + BREAK; + CASE(114) // pushConstantFalseBytecode + CASE(334) // 78 pushConstantFalseBytecode + { + VM_LABEL(pushConstantFalseBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),GIV(falseObj)); + } + BREAK; + CASE(115) // pushConstantNilBytecode + CASE(335) // 79 pushConstantNilBytecode + { + VM_LABEL(pushConstantNilBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),GIV(nilObj)); + } + BREAK; + CASE(116) // pushConstantMinusOneBytecode + { + VM_LABEL(pushConstantMinusOneBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),ConstMinusOne); + } + BREAK; + CASE(117) // pushConstantZeroBytecode + CASE(336) // 80 pushConstantZeroBytecode + { + VM_LABEL(pushConstantZeroBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),ConstZero); + } + BREAK; + CASE(118) // pushConstantOneBytecode + CASE(337) // 81 pushConstantOneBytecode + { + VM_LABEL(pushConstantOneBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),ConstOne); + } + BREAK; + CASE(119) // pushConstantTwoBytecode + { + VM_LABEL(pushConstantTwoBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),ConstTwo); + } + BREAK; + CASE(120) // returnReceiver + { + VM_LABEL(returnReceiver); + localReturnValue = longAt(localFP + FoxIFReceiver); + + /* goto commonReturn */ + } + + commonReturn: + /* commonReturn */ + { + sqInt aMethodObj; + char *callerFP; + sqInt closure; + sqInt contextToReturnTo; + sqInt currentCtx; + char *frameToReturnTo; + sqInt header; + sqInt home; + sqInt methodHeader; + StackPage *newPage; + sqInt nextCntx; + sqInt ourContext; + sqInt senderOop; + char *theFP; + char *theFP1; + StackPage *thePage; + StackPage *thePage1; + sqInt unwindContextOrNilOrZero; + + VM_LABEL(commonReturn); + + /* If this is a method simply return to the sender/caller. */ + frameToReturnTo = ((char *) 0); + theFP = ((char *) 0); + if (!(byteAt((localFP + FoxIFrameFlags) + 3))) { + goto commonCallerReturn; + goto l116; + } + + /* Update the current page's headFrame pointers to enable the search for unwind protects below + to identify widowed contexts correctly. */ + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* Since this is a block activation the closure is on the stack above any args and the frame. */ + closure = longAt(localFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(localFP))->cmNumArgs) + : byteAt((localFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))); + + /* avoid compiler warning */ + /* Walk the closure's lexical chain to find the context or frame to return from (home). + If home is missing (Sista closures) then throw cannotReturn rather than crash. */ + home = null; + while (closure != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + home = longAt((void *)((closure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + assert(isNonImmediate(home)); + if ((!((longAt((void *)(home))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + home = fixFollowedFieldofObjectwithInitialValue(ClosureOuterContextIndex, closure, home); + } + if (!(/* isContext: */ + ((!(home & (tagMask())))) + && (((longAt((void *)(home))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* begin internalCannotReturn: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l118; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l118: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),ourContext); + longAtput((localSP -= BytesPerOop),localReturnValue); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))); + GIV(argumentCount) = 1; + goto normalSend; + goto l116; + } + + /* begin followObjField:ofObject: */ + closure = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord())))))); + assert(isNonImmediate(closure)); + if ((!((longAt((void *)(closure))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + closure = fixFollowedFieldofObjectwithInitialValue(ClosureIndex, home, closure); + } + } + + /* home is to be returned from provided there is no unwind-protect activation between + this frame and home's sender. Search for an unwind. findUnwindThroughContext: + will answer either the context for an unwind-protect activation or nilObj if the sender + cannot be found or 0 if no unwind is found but the sender is. */ + unwindContextOrNilOrZero = findUnwindThroughContext(home); + if (unwindContextOrNilOrZero == GIV(nilObj)) { + /* error: can't find home on chain; cannot return */ + + /* begin internalCannotReturn: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l120; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l120: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),ourContext); + longAtput((localSP -= BytesPerOop),localReturnValue); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))); + GIV(argumentCount) = 1; + goto normalSend; + goto l116; + } + if (unwindContextOrNilOrZero) { + /* begin internalAboutToReturn:through: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l121; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l121: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),ourContext); + longAtput((localSP -= BytesPerOop),localReturnValue); + longAtput((localSP -= BytesPerOop),unwindContextOrNilOrZero); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAboutToReturn) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l116; + } + + /* Now we know home is on the sender chain. + We could be returning to either a context or a frame. Find out which. */ + contextToReturnTo = null; + if (((((longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert(checkIsStillMarriedContextcurrentFP(home, localFP)); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (longAt(theFP + FoxSavedFP)) { + frameToReturnTo = ((char *)(longAt(theFP + FoxSavedFP))); + } + else { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + contextToReturnTo = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(contextToReturnTo)); + assert((contextToReturnTo == (nilObject())) + || (isContext(followMaybeForwarded(contextToReturnTo)))); + } + } + else { + contextToReturnTo = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + if ((/* isContext: */ + ((!(contextToReturnTo & (tagMask())))) + && (((longAt((void *)(contextToReturnTo))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + assert(checkIsStillMarriedContextcurrentFP(contextToReturnTo, localFP)); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + frameToReturnTo = ((char *)(senderOop - (smallIntegerTag()))); + contextToReturnTo = null; + } + } + + /* If returning to a context we must make a frame for it unless it is dead. */ + if (contextToReturnTo) { + frameToReturnTo = establishFrameForContextToReturnTo(contextToReturnTo); + if (!frameToReturnTo) { + /* begin internalCannotReturn: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l117; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l117: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),ourContext); + longAtput((localSP -= BytesPerOop),localReturnValue); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))); + GIV(argumentCount) = 1; + goto normalSend; + goto l116; + } + } + + /* Now we have a frame to return to. If it is on a different page we must free intervening pages and + nil out intervening contexts. We must free intervening stack pages because if we leave the pages + to be divorced then their contexts will be divorced with intact senders and instruction pointers. This + code is similar to primitiveTerminateTo. We must move any frames on intervening pages above the + frame linked to because these may be in use, e.g. via co-routining (see baseFrameReturn). */ + assert(pageListIsWellFormed()); + + /* begin stackPageFor: */ + newPage = stackPageAtpages(pageIndexFor(frameToReturnTo), GIV(pages)); + if (newPage != GIV(stackPage)) { + theFP1 = (GIV(stackPage)->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP1)); + thePage1 = stackPageAtpages(pageIndexFor(theFP1), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + freeStackPage(GIV(stackPage)); + while (1) { + assert(isContext(currentCtx)); + if ((((((longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && ((stackPageFor((theFP = frameOfMarriedContext(currentCtx)))) == newPage)) break; + if (((((longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (theFP != ((thePage->headFP))) { + /* Since we've just deallocated a page we know that newStackPage won't deallocate an existing one. */ + moveFramesInthroughtoPage(thePage, findFrameAboveinPage(theFP, thePage), newStackPage()); + } + theFP1 = (thePage->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP1)); + thePage1 = stackPageAtpages(pageIndexFor(theFP1), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + freeStackPage(thePage); + } + else { + nextCntx = longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + + /* begin markContextAsDead: */ + assert(isContext(currentCtx)); + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(SenderIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + currentCtx = nextCntx; + } + } + + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + localSP = (GIV(stackPage)->headSP); + localFP = (GIV(stackPage)->headFP); + } + + /* Two cases. Returning to the top frame on a new page or an interior frame on the current page. + The top frame has its instruction pointer on top of stack. An interior frame has its instruction pointer + in the caller frame. We need to peel back any frames on the page until we get to the correct frame. */ + if (localFP == frameToReturnTo) { + localIP = ((char *)(longAt(localSP))); + } + else { + do { + callerFP = localFP; + localFP = ((char *)(longAt(localFP + FoxSavedFP))); + } while(localFP != frameToReturnTo); + localIP = ((char *)(longAt(callerFP + FoxCallerSavedIP))); + localSP = ((/* begin frameCallerSP: */ + assert(!(isBaseFrame(callerFP))), +(callerFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(callerFP))->cmNumArgs) + : byteAt((callerFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)) - BytesPerWord; + } + + /* pop the saved IP, push the return value and continue. */ + + /* begin maybeReturnToMachineCodeFrame */ + if ((((usqInt)localIP)) < (startOfMemory())) { + if ((((usqInt)localIP)) != (ceReturnToInterpreterPC())) { + /* localIP in the cog method zone indicates a return to machine code. */ + + /* begin returnToMachineCodeFrame */ + assertCStackWellAligned(); + assert((((usqInt)localIP)) < (startOfMemory())); + assert(isMachineCodeFrame(localFP)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 0, __LINE__); + assert(isPCWithinMethodZone(((usqInt)localIP))); + + /* internalStackTopPut: */ + longAtput(localSP,((usqInt)localIP)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),localReturnValue); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + ceEnterCogCodePopReceiverReg(); + goto l119; + } + localIP = ((char *)(((usqInt)(longAt(localFP + FoxIFSavedIP))))); + } + /* end maybeReturnToMachineCodeFrame */ +l119: + aMethodObj = longAt(localFP + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* internalStackTopPut: */ + longAtput(localSP,localReturnValue); + } + /* end case */ +l116:; + BREAK; + CASE(121) // returnTrue + CASE(345) // 89 returnTrue + { + VM_LABEL(returnTrue); + localReturnValue = GIV(trueObj); + goto commonReturn; + } + BREAK; + CASE(122) // returnFalse + CASE(346) // 90 returnFalse + { + VM_LABEL(returnFalse); + localReturnValue = GIV(falseObj); + goto commonReturn; + } + BREAK; + CASE(123) // returnNil + CASE(347) // 91 returnNil + { + VM_LABEL(returnNil); + localReturnValue = GIV(nilObj); + goto commonReturn; + } + BREAK; + CASE(124) // returnTopFromMethod + CASE(348) // 92 returnTopFromMethod + { + VM_LABEL(returnTopFromMethod); + localReturnValue = longAt(localSP); + goto commonReturn; + } + BREAK; + CASE(125) // returnTopFromBlock + { + VM_LABEL(returnTopFromBlock); + localReturnValue = longAt(localSP); + + /* goto commonCallerReturn */ + } + + commonCallerReturn: + /* commonCallerReturn */ + { + sqInt aMethodObj; + char *callerFP; + char *callersFPOrNull; + sqInt contextToReturnFrom; + sqInt contextToReturnTo; + char *fp; + char *frameAbove; + sqInt header; + sqInt methodHeader; + StackPage *newPage; + sqInt retToContext; + sqInt senderOop; + char *theFP; + StackPage *thePage; + StackPage *thePage1; + char *theSP; + + VM_LABEL(commonCallerReturn); + + /* TODO: Store/restore the nativeSP more properly, when it exists */ + callersFPOrNull = ((char *)(longAt(localFP + FoxSavedFP))); + if (!callersFPOrNull) { + assert(localFP == ((GIV(stackPage)->baseFP))); + + /* begin baseFrameReturn */ + /* begin frameCallerContext: */ + assert(isBaseFrame(localFP)); + thePage1 = stackPageAtpages(pageIndexFor(localFP), GIV(pages)); + contextToReturnTo = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(contextToReturnTo)); + assert((contextToReturnTo == (nilObject())) + || (isContext(followMaybeForwarded(contextToReturnTo)))); + + /* The stack page is effectively free now, so free it. We must free it to be + correct in determining if contextToReturnTo is still married, and in case + makeBaseFrameFor: cogs a method, which may cause a code compaction, + in which case the frame must be free to avoid the relocation machinery + tracing the dead frame. Since freeing now temporarily violates the page-list + ordering invariant, use the assert-free version. */ + freeStackPageNoAssert(GIV(stackPage)); + retToContext = /* isContext: */ + ((!(contextToReturnTo & (tagMask())))) + && (((longAt((void *)(contextToReturnTo))) & (classIndexMask())) == ClassMethodContextCompactIndex); + if (retToContext + && (/* isStillMarriedContext: */ + (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(contextToReturnTo))))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (theFP == ((thePage->headFP))) { + theSP = (thePage->headSP); + } + else { + /* Returning to some interior frame, presumably because of a sender assignment. + Move the frames above to another page (they may be in use, e.g. via coroutining). + Make the interior frame the top frame. */ + + /* begin findFrameAbove:inPage: */ + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == theFP) { + frameAbove = 0; + goto l123; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == theFP) { + frameAbove = fp; + goto l123; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + frameAbove = 0; + /* end findFrameAbove:inPage: */ +l123: + + /* Since we've just deallocated a page we know that newStackPage won't deallocate an existing one. */ + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l124; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l124: + assert(newPage == GIV(stackPage)); + moveFramesInthroughtoPage(thePage, frameAbove, newPage); + markStackPageMostRecentlyUsed(newPage); + theFP = (thePage->headFP); + theSP = (thePage->headSP); + } + } + else { + if (!(retToContext + && (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1)))) { + contextToReturnFrom = longAt(((GIV(stackPage)->baseAddress)) - BytesPerWord); + tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFromtoreturnValue(contextToReturnFrom, contextToReturnTo, localReturnValue); + + /* begin externalCannotReturn:from: */ + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),contextToReturnFrom); + longAtput((GIV(stackPointer) -= BytesPerWord),localReturnValue); + + /* Both ceBaseFrameReturnTrampoline & ceNonLocalReturnTrampoline pop + their caller's return pc into instructionPointer. In this uncommon case restore + it, since a send's call pushes the instructionPointer (after the arguments). */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + ((void) (ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), contextToReturnFrom, 1))); + goto l122; + } + + /* We must void the instructionPointer to stop it being updated if makeBaseFrameFor: + cogs a method, which may cause a code compaction. */ + GIV(instructionPointer) = 0; + thePage = makeBaseFrameFor(contextToReturnTo); + theFP = (thePage->headFP); + theSP = (thePage->headSP); + } + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + assert((stackPageFor(theFP)) == GIV(stackPage)); + localSP = theSP; + localFP = theFP; + localIP = ((char *)(longAt(localSP))); + if ((((usqInt)localIP)) < (startOfMemory())) { + if ((((usqInt)localIP)) != (ceReturnToInterpreterPC())) { + /* localIP in the cog method zone indicates a return to machine code. */ + + /* begin returnToMachineCodeFrame */ + assertCStackWellAligned(); + assert((((usqInt)localIP)) < (startOfMemory())); + assert(isMachineCodeFrame(localFP)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 0, __LINE__); + assert(isPCWithinMethodZone(((usqInt)localIP))); + + /* internalStackTopPut: */ + longAtput(localSP,((usqInt)localIP)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),localReturnValue); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + ceEnterCogCodePopReceiverReg(); + goto l122; + } + localIP = ((char *)(((usqInt)(longAt(localFP + FoxIFSavedIP))))); + } + assert(checkIsStillMarriedContextcurrentFP(contextToReturnTo, localFP)); + aMethodObj = longAt(localFP + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* internalStackTopPut: */ + longAtput(localSP,localReturnValue); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l122; + } + + /* baseFrame */ + localIP = ((char *)(longAt(localFP + FoxCallerSavedIP))); + localSP = localFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(localFP))->cmNumArgs) + : byteAt((localFP + FoxIFrameFlags) + 1)))) << (shiftForWord()))))); + localFP = callersFPOrNull; + if ((((usqInt)localIP)) < (startOfMemory())) { + if ((((usqInt)localIP)) != (ceReturnToInterpreterPC())) { + /* localIP in the cog method zone indicates a return to machine code. */ + + /* begin returnToMachineCodeFrame */ + assertCStackWellAligned(); + assert((((usqInt)localIP)) < (startOfMemory())); + assert(isMachineCodeFrame(localFP)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 0, __LINE__); + assert(isPCWithinMethodZone(((usqInt)localIP))); + + /* internalStackTopPut: */ + longAtput(localSP,((usqInt)localIP)); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),localReturnValue); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + ceEnterCogCodePopReceiverReg(); + goto l122; + } + localIP = ((char *)(((usqInt)(longAt(localFP + FoxIFSavedIP))))); + } + aMethodObj = longAt(localFP + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* internalStackTopPut: */ + longAtput(localSP,localReturnValue); + } + /* end case */ +l122:; + BREAK; + CASE(126) // unknownBytecode + CASE(127) // unknownBytecode + + respondToUnknownBytecode: + /* respondToUnknownBytecode */ + { + usqInt numSlots; + sqInt ourContext; + + VM_LABEL(respondToUnknownBytecode); + GIV(messageSelector) = /* maybeSplObj: */ + (SelectorUnknownBytecode < ((/* begin numSlotsOf: */ + assert((classIndexOf(GIV(specialObjectsOop))) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(GIV(specialObjectsOop) + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(GIV(specialObjectsOop) - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) + ? longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorUnknownBytecode) << (shiftForWord())))))) + : 0); + if ((!GIV(messageSelector)) + || (GIV(messageSelector) == GIV(nilObj))) { + error("Unknown bytecode"); + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l125; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l125: + + /* undo fetch of bytecode so that context's pc is pointing to the unknown bytecode. */ + localIP -= 1; + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),ourContext); + GIV(argumentCount) = 0; + goto normalSend; + } + BREAK; + CASE(128) // extendedPushBytecode + { + sqInt descriptor; + usqInt frameNumArgs; + sqInt litVar; + sqInt object; + usqInt variableIndex; + usqInt variableType; + + VM_LABEL(extendedPushBytecode); + descriptor = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + variableType = (((usqInt)(descriptor)) >> 6) & 3; + variableIndex = descriptor & 0x3F; + if (!variableType) { + /* begin pushReceiverVariable: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)(((longAt(localFP + FoxIFReceiver)) + BaseHeaderSize) + ((variableIndex << (shiftForWord())))))); + goto l2; + } + if (variableType == 1) { + /* begin pushTemporaryVariable: */ + object = /* itemporary:in: */ + (variableIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - variableIndex) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - variableIndex) * BytesPerWord))); + longAtput((localSP -= BytesPerOop),object); + goto l2; + } + if (variableType == 2) { + /* begin pushLiteralConstant: */ + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + (((variableIndex + LiteralStart) << (shiftForWord()))))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + goto l2; + } + if (variableType == 3) { + /* begin pushLiteralVariable: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + (((variableIndex + LiteralStart) << (shiftForWord()))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(variableIndex + LiteralStart, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))))); + goto l2; + } + } + /* end case */ +l2:; + BREAK; + CASE(129) // extendedStoreBytecode + { + sqInt descriptor; + usqInt frameNumArgs; + sqInt litVar; + sqInt objOop; + sqInt value; + usqInt variableIndex; + + VM_LABEL(extendedStoreBytecode); + /* begin extendedStoreBytecodePop: */ + descriptor = byteAt(++localIP); + variableIndex = descriptor & 0x3F; + value = longAt(localSP); + switch ((((usqInt)(descriptor)) >> 6) & 3) { + case 0: + objOop = longAt(localFP + FoxIFReceiver); + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(objOop)))); + if ((((usqInt)((byteAt((void *)(objOop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),objOop); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((variableIndex + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l3; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, objOop, value)); + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + ((variableIndex << (shiftForWord())))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l3:; + break; + case 1: + /* itemporary:in:put: */ + if (variableIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - variableIndex) * BytesPerWord),value); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - variableIndex) * BytesPerWord),value); + } + break; + case 2: + error("illegal store"); + break; + case 3: + /* begin storeLiteralVariable:withValue: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + (((variableIndex + LiteralStart) << (shiftForWord()))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(variableIndex + LiteralStart, GIV(method), litVar); + } + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(litVar)))); + if ((((usqInt)((byteAt((void *)(litVar + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),litVar); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((usqInt)2 /* (ValueIndex + 1) */ << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l4; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ValueIndex, litVar, value)); + assert(isNonImmediate(litVar)); + if (oopisGreaterThanOrEqualTo(litVar, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(litVar + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(litVar); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l4:; + break; + default: + error("Case not found and no otherwise clause"); + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(130) // extendedStoreAndPopBytecode + { + sqInt descriptor; + usqInt frameNumArgs; + sqInt litVar; + sqInt objOop; + sqInt value; + usqInt variableIndex; + + VM_LABEL(extendedStoreAndPopBytecode); + /* begin extendedStoreBytecodePop: */ + descriptor = byteAt(++localIP); + variableIndex = descriptor & 0x3F; + value = longAt(localSP); + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + switch ((((usqInt)(descriptor)) >> 6) & 3) { + case 0: + objOop = longAt(localFP + FoxIFReceiver); + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(objOop)))); + if ((((usqInt)((byteAt((void *)(objOop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),objOop); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((variableIndex + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l5; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, objOop, value)); + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + ((variableIndex << (shiftForWord())))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l5:; + break; + case 1: + /* itemporary:in:put: */ + if (variableIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - variableIndex) * BytesPerWord),value); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - variableIndex) * BytesPerWord),value); + } + break; + case 2: + error("illegal store"); + break; + case 3: + /* begin storeLiteralVariable:withValue: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + (((variableIndex + LiteralStart) << (shiftForWord()))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(variableIndex + LiteralStart, GIV(method), litVar); + } + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(litVar)))); + if ((((usqInt)((byteAt((void *)(litVar + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),litVar); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((usqInt)2 /* (ValueIndex + 1) */ << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l6; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ValueIndex, litVar, value)); + assert(isNonImmediate(litVar)); + if (oopisGreaterThanOrEqualTo(litVar, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(litVar + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(litVar); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l6:; + break; + default: + error("Case not found and no otherwise clause"); + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(131) // singleExtendedSendBytecode + { + sqInt descriptor; + + VM_LABEL(singleExtendedSendBytecode); + descriptor = byteAt(++localIP); + + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((descriptor & 0x1F) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = ((usqInt)(descriptor)) >> 5; + + /* goto normalSend */ + } + + normalSend: + /* normalSend */ + { + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(normalSend); + rcvr = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + lkupClassTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + assert(lkupClassTag != (nilObject())); + + commonSendOrdinary: + /* commonSendOrdinary */ + { + sqInt classTablePage; + CogMethod *cogMethod; + sqInt fieldIndex; + sqInt hash; + sqInt i; + sqInt localPrimIndex; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt nArgs; + usqInt numTemps; + sqInt object; + usqInt probe; + sqInt rcvr; + char *savedFramePointer; + char *savedStackPointer; + int succeeded; + sqInt switched; + sqInt tagBits; + + VM_LABEL(commonSendOrdinary); + rcvr = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + if (recordSendTrace()) { + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + TraceSend) + CSFromInterpreter); + GIV(traceLog)[GIV(traceLogIndex) + 1] = (classForClassTag(lkupClassTag)); + GIV(traceLog)[GIV(traceLogIndex) + 2] = GIV(messageSelector); + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + if (printOnTrace()) { + printActivationNameForSelectorstartClass(GIV(messageSelector), classForClassTag(lkupClassTag)); + cr(); + } + } + + /* begin internalFindNewMethodOrdinary */ + /* begin inlineLookupInMethodCacheSel:classTag: */ + /* shift drops two low-order zeros from addresses */ + hash = GIV(messageSelector) ^ ((((usqInt)(lkupClassTag) << 2))); + + /* first probe */ + probe = hash & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == GIV(messageSelector)) + && ((GIV(methodCache)[probe + MethodCacheClass]) == lkupClassTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + goto l128; + } + + /* second probe */ + probe = (((usqInt)(hash)) >> 1) & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == GIV(messageSelector)) + && ((GIV(methodCache)[probe + MethodCacheClass]) == lkupClassTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + goto l128; + } + probe = (((usqInt)(hash)) >> 2) & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == GIV(messageSelector)) + && ((GIV(methodCache)[probe + MethodCacheClass]) == lkupClassTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + goto l128; + } + goto l127; +l128: + + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + goto l131; +l127: + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + if ((/* isOopForwarded: */ + ((!(GIV(messageSelector) & (tagMask())))) + && ((!((longAt((void *)(GIV(messageSelector)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + || (lkupClassTag == (isForwardedObjectClassIndexPun()))) { + if (/* isOopForwarded: */ + ((!(GIV(messageSelector) & (tagMask())))) + && ((!((longAt((void *)(GIV(messageSelector)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + GIV(messageSelector) = handleForwardedSelectorFaultFor(GIV(messageSelector)); + } + if (lkupClassTag == (isForwardedObjectClassIndexPun())) { + lkupClassTag = handleForwardedSendFaultForTag(lkupClassTag); + } + if (lookupInMethodCacheSelclassTag(GIV(messageSelector), lkupClassTag)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + goto l126; + } + } + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((lkupClassTag >= 0) + && ((lkupClassTag <= (tagMask())) + || ((lkupClassTag >= (arrayClassIndexPun())) + && (lkupClassTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(lkupClassTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + GIV(lkupClass) = null; + goto l130; + } + fieldIndex = lkupClassTag & ((1U << (classTableMajorIndexShift())) - 1); + GIV(lkupClass) = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l130: + assert((GIV(lkupClass)) + && (addressCouldBeClassObj(GIV(lkupClass)))); + lookupMethodInClass(GIV(lkupClass)); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + addNewMethodToCache(GIV(lkupClass)); +l131: + /* end internalFindNewMethodOrdinary */ +l126: + + /* begin internalExecuteNewMethod */ + if (primitiveFunctionPointer) { + if ((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex) { + /* begin internalQuickPrimitiveResponse */ + assert(isPrimitiveFunctionPointerAnIndex()); + localPrimIndex = ((sqInt) primitiveFunctionPointer); + assert((localPrimIndex > 0xFF) + && (localPrimIndex < 520)); + + /* Quick return inst vars */ + if (localPrimIndex >= 264) { + longAtput(localSP,longAt((void *)(((longAt(localSP)) + BaseHeaderSize) + ((((usqInt)((localPrimIndex - 264)) << (shiftForWord()))))))); + goto l129; + } + + /* Quick return constants */ + if (localPrimIndex == 0x100) { + goto l129; + } + if (localPrimIndex == 0x101) { + /* internalStackTopPut: */ + longAtput(localSP,GIV(trueObj)); + goto l129; + } + if (localPrimIndex == 258) { + /* internalStackTopPut: */ + longAtput(localSP,GIV(falseObj)); + goto l129; + } + if (localPrimIndex == 259) { + /* internalStackTopPut: */ + longAtput(localSP,GIV(nilObj)); + goto l129; + } + + /* internalStackTopPut: */ + longAtput(localSP,(((usqInt)(localPrimIndex - 261) << 3) | 1)); + goto l129; + } + + /* slowPrimitiveResponse may of course context-switch. If so we must reenter the + new process appropriately, returning only if we've found an interpreter frame. */ + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin slowPrimitiveResponse */ + assert(!(isOopForwarded(stackValue(GIV(argumentCount))))); + assert((GIV(remapBufferCount)) == 0); + nArgs = GIV(argumentCount); + savedStackPointer = GIV(stackPointer); + savedFramePointer = GIV(framePointer); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if (/* recordPrimTraceForMethod: */ + (recordPrimTrace()) + && ((primTracePluginName + ? methodHasPrimitiveInPrimTracePlugin(GIV(newMethod)) + : 1))) { + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = GIV(newMethod); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + dispatchFunctionPointer(primitiveFunctionPointer); + assert(maybeLeakCheckExternalPrimCall(GIV(newMethod))); + + /* begin maybeRetryPrimitiveOnFailure */ + if (GIV(primFailCode)) { + retryPrimitiveOnFailure(); + } + if ((!GIV(primFailCode)) + && ((GIV(framePointer) == savedFramePointer) + && (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))))) { + if (GIV(stackPointer) != (savedStackPointer + (nArgs * BytesPerWord))) { + /* This is necessary but insufficient; the result may still have been written to the stack. + At least we'll know something is wrong. */ + failUnbalancedPrimitive(); + GIV(stackPointer) = savedStackPointer; + } + } + + /* Don't fail if primitive has done something radical, e.g. perform: + If we are profiling, take accurate primitive measures */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + succeeded = !GIV(primFailCode); + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (succeeded) { + returntoExecutive(popStack(), 1); + browserPluginReturnIfNeeded(); + goto l129; + } + } + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* if not primitive, or primitive failed, activate the method */ + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if (!((!(methodHeader & (smallIntegerTag()))))) { + /* begin internalActivateNewMethod */ + methodHeaderSqInt = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(!((isCogMethodReferenceNoAssert(methodHeaderSqInt)))); + numTemps = (((usqInt)(methodHeaderSqInt)) >> MethodHeaderTempCountShift) & 0x3F; + assert(GIV(argumentCount) == (argumentCountOfMethodHeader(methodHeaderSqInt))); + + /* could new rcvr be set at point of send? */ + rcvr = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + assert(!(isOopForwarded(rcvr))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),((usqInt)localIP)); + longAtput((localSP -= BytesPerOop),((usqInt)localFP)); + localFP = localSP; + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeaderSqInt); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeaderSqInt)) < 0 + ? 0x100 + : 0); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((((usqInt)(methodHeaderSqInt)) >> MethodHeaderArgCountShift) & 15) << ((BytesPerWord * 8) - 8))))) + : ((1 + ((((((usqInt)(methodHeaderSqInt)) >> MethodHeaderArgCountShift) & 15) << 8))))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + longAtput((localSP -= BytesPerOop),0); + longAtput((localSP -= BytesPerOop),rcvr); + + /* Initialize temps... */ + for (i = (GIV(argumentCount) + 1); i <= numTemps; i += 1) { + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(nilObj)); + } + + /* -1 to account for pre-increment in fetchNextBytecode */ + localIP = (((char *)(((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeaderSqInt) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeaderSqInt >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize))) - 1; + if (((methodHeaderSqInt & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + localIP += 3 /* sizeOfCallPrimitiveBytecode: */; + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(localSP, methodHeaderSqInt); + } + } + assert((frameNumArgs(localFP)) == GIV(argumentCount)); + assert(!(frameIsBlockActivation(localFP))); + assert(!(frameHasContext(localFP))); + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc). */ + if (localSP < GIV(stackLimit)) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + switched = handleStackOverflowOrEventAllowContextSwitch(canContextSwitchIfActivatingheader(GIV(newMethod), methodHeaderSqInt)); + returnToExecutivepostContextSwitch(1, switched); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + } + goto l129; + } + + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(localFP))); + longAtput(localFP + FoxIFSavedIP,((sqInt)localIP)); + GIV(instructionPointer) = ceReturnToInterpreterPC(); + + /* begin externalizeFPandSP */ + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* This may context switch and hence return... */ + activateNewCogMethodinInterpreter(((CogMethod *) methodHeader), 1); + + /* Hence this si reachable... */ + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + /* end internalExecuteNewMethod */ +l129: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + } + BREAK; + CASE(132) // doubleExtendedDoAnythingBytecode + { + sqInt byte2; + sqInt byte3; + sqInt callerContextOrNil; + char *callerFP; + sqInt litVar; + sqInt obj; + sqInt object; + int onCurrentPage; + sqInt opType; + sqInt senderOop; + char *spouseFP; + char *theFP; + StackPage *thePage; + sqInt top; + sqInt value; + + VM_LABEL(doubleExtendedDoAnythingBytecode); + byte2 = byteAt(++localIP); + byte3 = byteAt(++localIP); + opType = ((usqInt)(byte2)) >> 5; + if (!opType) { + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((byte3 + LiteralStart)) << (shiftForWord())))))); + GIV(argumentCount) = byte2 & 0x1F; + goto normalSend; + goto l7; + } + if (opType == 1) { + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((byte3 + LiteralStart)) << (shiftForWord())))))); + GIV(argumentCount) = byte2 & 0x1F; + goto superclassSend; + goto l7; + } + if (opType == 2) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + obj = longAt(localFP + FoxIFReceiver); + if ((byte3 <= StackPointerIndex) + && (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin instVar:ofContext: */ + assert(byte3 < MethodIndex); + assert(isContext(obj)); + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + if (!(((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + value = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord())))))); + if ((byte3 == InstructionPointerIndex) + && (((((value) & 7) == 1)) + && ((((int) value)) < 0))) { + /* begin internalMustMapMachineCodePC:context: */ + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + value = mustMapMachineCodePCcontext((value >> 3), obj); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + } + object = value; + goto l10; + } + if (isWidowedContext(obj)) { + object = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord())))))); + goto l10; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!byte3) { + /* begin ensureCallerContext: */ + callerFP = ((char *)(longAt(spouseFP + FoxSavedFP))); + if (!callerFP) { + /* begin frameCallerContext: */ + assert(isBaseFrame(spouseFP)); + thePage = stackPageAtpages(pageIndexFor(spouseFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + object = callerContextOrNil; + goto l10; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP))); + object = longAt(callerFP + FoxThisContext); + goto l10; + } + object = marryFrameSP( + callerFP, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(spouseFP))), + (spouseFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(spouseFP))->cmNumArgs) + : byteAt((spouseFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + goto l10; + } + if (byte3 == StackPointerIndex) { + assert((ReceiverIndex + (stackPointerIndexForFrame(spouseFP))) < (lengthOf(obj))); + object = (((usqInt)(stackPointerIndexForFrame(spouseFP)) << 3) | 1); + goto l10; + } + if (byte3 == InstructionPointerIndex) { + object = instructionPointerForFramecurrentFPcurrentIP(spouseFP, localFP, oopForPointer(localIP)); + goto l10; + } + error("bad index"); + object = 0; + /* end instVar:ofContext: */ +l10: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + else { + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord()))))))); + } + goto l7; + } + if (opType == 3) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((byte3 + LiteralStart)) << (shiftForWord())))))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + goto l7; + } + if (opType == 4) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((byte3 + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(byte3 + LiteralStart, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))))); + goto l7; + } + top = longAt(localSP); + if (opType == 7) { + /* begin storeLiteralVariable:withValue: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((byte3 + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(byte3 + LiteralStart, GIV(method), litVar); + } + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(litVar)))); + if ((((usqInt)((byteAt((void *)(litVar + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),litVar); + longAtput((localSP -= BytesPerOop),top); + longAtput((localSP -= BytesPerOop),(((usqInt)2 /* (ValueIndex + 1) */ << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l9; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ValueIndex, litVar, top)); + assert(isNonImmediate(litVar)); + if (oopisGreaterThanOrEqualTo(litVar, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(top & (tagMask())))) + && (oopisLessThan(top, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(litVar + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(litVar); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))),top); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l9: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l7; + } + + /* opType = 5 is store; opType = 6 is storePop */ + if (opType == 6) { + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + obj = longAt(localFP + FoxIFReceiver); + if ((byte3 <= ReceiverIndex) + && ((((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + || (ismethodAssignmentToContextWithMachineCodePC(byte3, top))))) { + /* begin instVar:ofContext:put: */ + assert(isMarriedOrWidowedContext(obj)); + assert(!((isObjImmutable(obj)))); + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(obj))))) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(byte3, obj, top)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(top & (tagMask())))) + && (oopisLessThan(top, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord()))))),top); + if (byte3 == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + goto l8; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!byte3) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + onCurrentPage = thePage == GIV(stackPage); + storeSenderOfFramewithValue(theFP, top); + if (onCurrentPage) { + localFP = (GIV(stackPage)->headFP); + localSP = (GIV(stackPage)->headSP); + } + else { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + goto l8; + } + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + externalDivorceFrameandContext(theFP, obj); + if (ismethodAssignmentToContextWithMachineCodePC(byte3, obj)) { + ensureContextHasBytecodePC(obj); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(byte3, obj, top)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(top & (tagMask())))) + && (oopisLessThan(top, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord()))))),top); + if (byte3 == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + + /* Assigning various fields can force a divorce which can change the stackPage. */ + markStackPageMostRecentlyUsed(GIV(stackPage)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 1, __LINE__); + /* end instVar:ofContext:put: */ +l8:; + } + else { + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(obj)))); + if ((((usqInt)((byteAt((void *)(obj + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),obj); + longAtput((localSP -= BytesPerOop),top); + longAtput((localSP -= BytesPerOop),(((usqInt)(byte3 + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l11; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(byte3, obj, top)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(top & (tagMask())))) + && (oopisLessThan(top, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(byte3) << (shiftForWord()))))),top); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l11:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + /* end case */ +l7:; + BREAK; + CASE(133) // singleExtendedSuperBytecode + { + sqInt descriptor; + + VM_LABEL(singleExtendedSuperBytecode); + descriptor = byteAt(++localIP); + + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((descriptor & 0x1F) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = ((usqInt)(descriptor)) >> 5; + + /* goto superclassSend */ + } + + superclassSend: + /* superclassSend */ + { + sqInt aValue; + sqInt classPointer; + sqInt err; + int hash; + sqInt literal; + sqInt objOop; + sqInt offset; + sqInt referent; + sqInt superclass; + + VM_LABEL(superclassSend); + /* begin methodClassOf: */ + offset = (literalCountOf(GIV(method))) - 1; + literal = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = fixFollowedFieldofObjectwithInitialValue(offset + LiteralStart, GIV(method), literal); + } + classPointer = ((literal != GIV(nilObj)) + && (/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + ? (assert((numSlotsOf(literal)) > ValueIndex), + followFieldofObject(ValueIndex, literal)) + : GIV(nilObj)); + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + superclass = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(superclass)); + if ((!((longAt((void *)(superclass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + superclass = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, superclass); + } + + /* classTagForClass: uses ensureBehaviorHash: */ + + /* begin classTagForClass: */ + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(superclass)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + lkupClassTag = ((hash = (long32At((void *)(superclass + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(superclass) + ? ((err = enterIntoClassTable(superclass)) + ? -err + : (long32At((void *)(superclass + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + + /* To maintain the invariant that all receivers are unforwarded we need an explicit + read barrier in the super send cases. Even though we always follow receivers + on become e.g. super doSomethingWith: (self become: other) forwards the receiver + self pushed on the stack. */ + + /* begin ensureReceiverUnforwarded */ + if (/* isOopForwarded: */ + ((!((longAt(localSP + (GIV(argumentCount) * BytesPerOop))) & (tagMask())))) + && ((!((longAt((void *)(longAt(localSP + (GIV(argumentCount) * BytesPerOop))))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + referent = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + aValue = referent; + + /* begin internalStackValue:put: */ + longAtput(localSP + (GIV(argumentCount) * BytesPerOop),aValue); + } + assert(lkupClassTag != (nilObject())); + goto commonSendOrdinary; + } + BREAK; + CASE(134) // secondExtendedSendBytecode + { + sqInt descriptor; + + VM_LABEL(secondExtendedSendBytecode); + descriptor = byteAt(++localIP); + + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((descriptor & 0x3F) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = ((usqInt)(descriptor)) >> 6; + goto normalSend; + } + BREAK; + CASE(135) // popStackBytecode + CASE(472) // 216 popStackBytecode + { + VM_LABEL(popStackBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(136) // duplicateTopBytecode + CASE(339) // 83 duplicateTopBytecode + { + sqInt object; + + VM_LABEL(duplicateTopBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = longAt(localSP); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(137) // pushActiveContextBytecode + { + sqInt ourContext; + + VM_LABEL(pushActiveContextBytecode); + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + ourContext = longAt(localFP + FoxThisContext); + goto l12; + } + ourContext = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l12: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),ourContext); + } + BREAK; + CASE(138) // pushNewArrayBytecode + CASE(487) // 231 pushNewArrayBytecode + { + usqInt array; + sqInt i; + usqInt newObj; + usqInt numBytes; + int popValues; + sqInt size; + + VM_LABEL(pushNewArrayBytecode); + size = byteAt(++localIP); + if ((popValues = size >= 128)) { + size -= 128; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((size >= 0) + && ((knownClassAtIndex(ClassArrayCompactIndex)) != GIV(nilObj))); + assert((arrayFormat()) == (instSpecOfClass(knownClassAtIndex(ClassArrayCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(size < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((size < 1 + ? 8 /* allocationUnit */ + : size * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + array = 0; + goto l13; + } + } + long64Atput((void *)(newObj),((((((usqLong) size)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + GIV(freeStart) += numBytes; + array = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l13: + + /* Can't use NewObjectMemory>>#eeInstantiateSmallClassIndex:format:numSlots: + cuz it assumes <= 252 bytes whereras this may allocate 127 * 4 + headerSize bytes. */ + if (popValues) { + for (i = 0; i < size; i += 1) { + /* Assume: have just allocated a new Array; it must be young. Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(i, array, longAt((void *)(localSP + (((size - i) - 1) * BytesPerOop))))); + longAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),longAt((void *)(localSP + (((size - i) - 1) * BytesPerOop)))); + } + + /* begin internalPop: */ + localSP += size * BytesPerOop; + } + else { + for (i = 0; i < size; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(i, array, GIV(nilObj))); + longAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),array); + } + BREAK; + CASE(139) // callPrimitiveBytecode + CASE(504) // 248 callPrimitiveBytecode + { + sqInt header; + sqInt headerSqInt; + + VM_LABEL(callPrimitiveBytecode); + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + headerSqInt = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + if ((((header & AlternateHeaderHasPrimFlag) != 0)) + && ((((sqInt)localIP)) == (((((usqInt)(pointerForOop(GIV(method))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize))) { + localIP = (localIP + 3 /* sizeOfCallPrimitiveBytecode: */) - 1; + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l14; + } + goto respondToUnknownBytecode; + } + /* end case */ +l14:; + BREAK; + CASE(140) // pushRemoteTempLongBytecode + CASE(507) // 251 pushRemoteTempLongBytecode + { + usqInt frameNumArgs; + sqInt remoteTempIndex; + sqInt tempVector; + sqInt tempVectorIndex; + + VM_LABEL(pushRemoteTempLongBytecode); + remoteTempIndex = byteAt(++localIP); + tempVectorIndex = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + tempVector = /* itemporary:in: */ + (tempVectorIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - tempVectorIndex) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - tempVectorIndex) * BytesPerWord))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((tempVector + BaseHeaderSize) + ((((usqInt)(remoteTempIndex) << (shiftForWord()))))))); + } + BREAK; + CASE(141) // storeRemoteTempLongBytecode + CASE(508) // 252 storeRemoteTempLongBytecode + { + usqInt frameNumArgs; + sqInt remoteTempIndex; + sqInt tempVector; + sqInt tempVectorIndex; + sqInt valuePointer; + + VM_LABEL(storeRemoteTempLongBytecode); + remoteTempIndex = byteAt(++localIP); + tempVectorIndex = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + tempVector = /* itemporary:in: */ + (tempVectorIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - tempVectorIndex) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - tempVectorIndex) * BytesPerWord))); + valuePointer = longAt(localSP); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(remoteTempIndex, tempVector, valuePointer)); + assert(isNonImmediate(tempVector)); + if (oopisGreaterThanOrEqualTo(tempVector, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(tempVector + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(tempVector); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((tempVector + BaseHeaderSize) + ((((usqInt)(remoteTempIndex) << (shiftForWord()))))),valuePointer); + } + BREAK; + CASE(142) // storeAndPopRemoteTempLongBytecode + CASE(509) // 253 storeAndPopRemoteTempLongBytecode + { + usqInt frameNumArgs; + sqInt remoteTempIndex; + sqInt tempVector; + sqInt tempVectorIndex; + sqInt valuePointer; + + VM_LABEL(storeAndPopRemoteTempLongBytecode); + /* begin storeRemoteTempLongBytecode */ + remoteTempIndex = byteAt(++localIP); + tempVectorIndex = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + tempVector = /* itemporary:in: */ + (tempVectorIndex < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - tempVectorIndex) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - tempVectorIndex) * BytesPerWord))); + valuePointer = longAt(localSP); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(remoteTempIndex, tempVector, valuePointer)); + assert(isNonImmediate(tempVector)); + if (oopisGreaterThanOrEqualTo(tempVector, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(tempVector + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(tempVector); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((tempVector + BaseHeaderSize) + ((((usqInt)(remoteTempIndex) << (shiftForWord()))))),valuePointer); + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(143) // pushClosureCopyCopiedValuesBytecode + { + sqInt blockSize; + sqInt context; + sqInt i; + sqInt initialIP; + usqInt newClosure; + usqInt newObj; + usqInt numArgs; + sqInt numArgsNumCopied; + usqInt numBytes; + sqInt numCopied; + sqInt numSlots; + + VM_LABEL(pushClosureCopyCopiedValuesBytecode); + numArgsNumCopied = byteAt(++localIP); + numArgs = numArgsNumCopied & 15; + numCopied = (((usqInt)(numArgsNumCopied)) >> 4); + + /* Split blockSize := (self fetchByte * 256) + self fetchByte. into two because evaluation order in C is undefined. */ + blockSize = ((sqInt)((usqInt)((byteAt(++localIP))) << 8)); + blockSize += byteAt(++localIP); + + /* begin pushClosureNumArgs:copiedValues:blockSize: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + context = longAt(localFP + FoxThisContext); + goto l15; + } + context = marryFrameSP(localFP, localSP + (numCopied * BytesPerOop)); + /* end ensureFrameIsMarried:SP: */ +l15: + initialIP = ((((usqInt)localIP)) + 2) - (GIV(method) + BaseHeaderSize); + + /* begin closureIn:numArgs:instructionPointer:numCopiedValues: */ + numSlots = ClosureFirstCopiedValueIndex + numCopied; + assert((numSlots >= 0) + && ((knownClassAtIndex(ClassBlockClosureCompactIndex)) != GIV(nilObj))); + assert((indexablePointersFormat()) == (instSpecOfClass(knownClassAtIndex(ClassBlockClosureCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newClosure = 0; + goto l16; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((indexablePointersFormat())) << (formatShift()))))) + ClassBlockClosureCompactIndex); + GIV(freeStart) += numBytes; + newClosure = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l16: + + /* Assume: have just allocated a new closure; it must be young. Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureOuterContextIndex, newClosure, context)); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord()))))),context); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureStartPCIndex, newClosure, (((usqInt)initialIP << 3) | 1))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))),(((usqInt)initialIP << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureNumArgsIndex, newClosure, ((numArgs << 3) | 1))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord()))))),((numArgs << 3) | 1)); + if (numCopied > 0) { + for (i = 0; i < numCopied; i += 1) { + /* Assume: have just allocated a new BlockClosure; it must be young. + Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(i + ClosureFirstCopiedValueIndex, newClosure, longAt((void *)(localSP + (((numCopied - i) - 1) * BytesPerOop))))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)((i + ClosureFirstCopiedValueIndex)) << (shiftForWord()))))),longAt((void *)(localSP + (((numCopied - i) - 1) * BytesPerOop)))); + } + + /* begin internalPop: */ + localSP += numCopied * BytesPerOop; + } + localIP += blockSize; + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),newClosure); + } + BREAK; + CASE(144) // shortUnconditionalJump + CASE(432) // 176 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump); + offset = 1; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(145) // shortUnconditionalJump + CASE(433) // 177 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump1); + offset = 2; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(146) // shortUnconditionalJump + CASE(434) // 178 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump2); + offset = 3; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(147) // shortUnconditionalJump + CASE(435) // 179 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump3); + offset = 4; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(148) // shortUnconditionalJump + CASE(436) // 180 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump4); + offset = 5; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(149) // shortUnconditionalJump + CASE(437) // 181 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump5); + offset = 6; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(150) // shortUnconditionalJump + CASE(438) // 182 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump6); + offset = 7; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(151) // shortUnconditionalJump + CASE(439) // 183 shortUnconditionalJump + { + sqInt offset; + + VM_LABEL(shortUnconditionalJump7); + offset = 8; + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(152) // shortConditionalJumpFalse + CASE(153) // shortConditionalJumpFalse + CASE(154) // shortConditionalJumpFalse + CASE(155) // shortConditionalJumpFalse + CASE(156) // shortConditionalJumpFalse + CASE(157) // shortConditionalJumpFalse + CASE(158) // shortConditionalJumpFalse + CASE(159) // shortConditionalJumpFalse + CASE(448) // 192 shortConditionalJumpFalse + CASE(449) // 193 shortConditionalJumpFalse + CASE(450) // 194 shortConditionalJumpFalse + CASE(451) // 195 shortConditionalJumpFalse + CASE(452) // 196 shortConditionalJumpFalse + CASE(453) // 197 shortConditionalJumpFalse + CASE(454) // 198 shortConditionalJumpFalse + CASE(455) // 199 shortConditionalJumpFalse + { + sqInt boolean; + sqInt offset; + + VM_LABEL(shortConditionalJumpFalse); + offset = (currentBytecode & 7) + 1; + + /* begin jumplfFalseBy: */ + boolean = longAt(localSP); + if (boolean == GIV(falseObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(trueObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l17; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfFalseBy: */ +l17:; + } + BREAK; + CASE(160) // longUnconditionalJump + CASE(161) // longUnconditionalJump + CASE(162) // longUnconditionalJump + CASE(163) // longUnconditionalJump + CASE(164) // longUnconditionalJump + CASE(165) // longUnconditionalJump + CASE(166) // longUnconditionalJump + CASE(167) // longUnconditionalJump + { + sqInt backwardJumpCountByte; + sqInt header; + sqInt methodHeader; + sqInt offset; + sqInt switched; + + VM_LABEL(longUnconditionalJump); + offset = (((currentBytecode & 7) - 4) * 0x100) + (byteAt(++localIP)); + localIP += offset; + + /* begin ifBackwardsCheckForEvents: */ + if (offset >= 0) { + goto l18; + } + if (localSP < GIV(stackLimit)) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + switched = checkForEventsMayContextSwitch(1); + returnToExecutivepostContextSwitch(1, switched); + browserPluginReturnIfNeeded(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (switched) { + goto l18; + } + } + + /* We use the least significant byte of the flags word (which is marked as an immediate) and + subtract two each time to avoid disturbing the least significant tag bit. Since the byte is + initialized to 1 (on frame build), on first decrement it will become -1. Trip when it reaches 1 again. */ + backwardJumpCountByte = byteAt(localFP + ((VMBIGENDIAN + ? (FoxIFrameFlags + BytesPerWord) - 1 + : FoxIFrameFlags))); + if (((backwardJumpCountByte -= 2)) == 1) { + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + attemptToSwitchToMachineCode(((((oopForPointer(localIP)) - offset) - GIV(method)) - BaseHeaderSize) - 1); + } + + /* can't cog method; avoid asking to cog it again for the longest possible time. */ + backwardJumpCountByte = 0x7F; + } + else { + if (backwardJumpCountByte == -1) { + assert(minBackwardJumpCountForCompile <= 128); + backwardJumpCountByte = ((((usqInt)((minBackwardJumpCountForCompile - 1)) << 1))) + 1; + } + } + + /* begin iframeBackwardBranchByte:put: */ + byteAtput(localFP + ((VMBIGENDIAN + ? (FoxIFrameFlags + BytesPerWord) - 1 + : FoxIFrameFlags)),backwardJumpCountByte); + /* end ifBackwardsCheckForEvents: */ +l18: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(168) // longJumpIfTrue + CASE(169) // longJumpIfTrue + CASE(170) // longJumpIfTrue + CASE(171) // longJumpIfTrue + { + sqInt boolean; + sqInt offset; + + VM_LABEL(longJumpIfTrue); + offset = ((currentBytecode & 3) * 0x100) + (byteAt(++localIP)); + + /* begin jumplfTrueBy: */ + boolean = longAt(localSP); + if (boolean == GIV(trueObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(falseObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l19; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfTrueBy: */ +l19:; + } + BREAK; + CASE(172) // longJumpIfFalse + CASE(173) // longJumpIfFalse + CASE(174) // longJumpIfFalse + CASE(175) // longJumpIfFalse + { + sqInt boolean; + sqInt offset; + + VM_LABEL(longJumpIfFalse); + offset = ((currentBytecode & 3) * 0x100) + (byteAt(++localIP)); + + /* begin jumplfFalseBy: */ + boolean = longAt(localSP); + if (boolean == GIV(falseObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(trueObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l20; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfFalseBy: */ +l20:; + } + BREAK; + CASE(176) // bytecodePrimAdd + CASE(352) // 96 bytecodePrimAdd + { + sqInt arg; + double argDouble; + usqLong bits; + sqInt rcvr; + double rcvrDouble; + sqInt result; + double resultDouble; + char *sp; + sqInt tagBits; + double value; + + VM_LABEL(bytecodePrimAdd); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + result = ((rcvr >> 3)) + ((arg >> 3)); + if ((((((usqInt)(result)) >> 60) + 1) & 15) <= 1) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),(((usqInt)result << 3) | 1)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l21; + } + } + else { + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin primitiveFloatAdd:toArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvr & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvrDouble = value; + goto l22; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvrDouble = ((double) ((rcvr >> 3)) ); + goto l22; + } + } + else { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvr + BaseHeaderSize, resultDouble); + rcvrDouble = resultDouble; + goto l22; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvrDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l22: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = arg & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(arg)); + bits = ((((usqInt)arg))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + argDouble = value; + goto l23; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + argDouble = ((double) ((arg >> 3)) ); + goto l23; + } + } + else { + if (((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(arg + BaseHeaderSize, resultDouble); + argDouble = resultDouble; + goto l23; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + argDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l23: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvrDouble + argDouble)); + GIV(stackPointer) = sp; + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l21; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (0U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l21:; + BREAK; + CASE(177) // bytecodePrimSubtract + CASE(353) // 97 bytecodePrimSubtract + { + sqInt arg; + double argDouble; + usqLong bits; + sqInt rcvr; + double rcvrDouble; + sqInt result; + double resultDouble; + char *sp; + sqInt tagBits; + double value; + + VM_LABEL(bytecodePrimSubtract); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + result = ((rcvr >> 3)) - ((arg >> 3)); + if ((((((usqInt)(result)) >> 60) + 1) & 15) <= 1) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),(((usqInt)result << 3) | 1)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l24; + } + } + else { + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin primitiveFloatSubtract:fromArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvr & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvrDouble = value; + goto l25; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvrDouble = ((double) ((rcvr >> 3)) ); + goto l25; + } + } + else { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvr + BaseHeaderSize, resultDouble); + rcvrDouble = resultDouble; + goto l25; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvrDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l25: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = arg & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(arg)); + bits = ((((usqInt)arg))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + argDouble = value; + goto l26; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + argDouble = ((double) ((arg >> 3)) ); + goto l26; + } + } + else { + if (((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(arg + BaseHeaderSize, resultDouble); + argDouble = resultDouble; + goto l26; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + argDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l26: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvrDouble - argDouble)); + GIV(stackPointer) = sp; + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l24; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (2U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l24:; + BREAK; + CASE(178) // bytecodePrimLessThan + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimLessThan); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheat: */ + if (rcvr < arg) { + /* goto booleanCheatTrue */ + } + else { + goto booleanCheatFalse; + } + goto l27; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatLessthanArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l27; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (4U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l27: + + booleanCheatTrue: + /* booleanCheatTrue */ + { + sqInt bytecode; + sqInt offset; + + VM_LABEL(booleanCheatTrue); + /* assume next bytecode is jumpIfFalse (99%) */ + bytecode = byteAt(++localIP); + + /* begin internalPop: */ + localSP += 2 * BytesPerOop; + if ((bytecode < 173) + && (bytecode > 151)) { + if (bytecode < 160) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l132; + } + + /* short jumpIfFalse 152 - 159 */ + if (bytecode == 172) { + /* fetchByte */ + byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l132; + } + + /* long jumpIfFalse */ + if (bytecode > 167) { + offset = ((((usqInt)((bytecode - 168)) << 8))) + (byteAt(++localIP)); + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l132; + } + } + + /* not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result */ + currentBytecode = bytecode + GIV(bytecodeSetSelector); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(trueObj)); + } + /* end case */ +l132:; + BREAK; + CASE(179) // bytecodePrimGreaterThan + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimGreaterThan); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheat: */ + if (rcvr > arg) { + goto booleanCheatTrue; + } + else { + /* goto booleanCheatFalse */ + } + goto l28; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatGreaterthanArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l28; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (6U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l28: + + booleanCheatFalse: + /* booleanCheatFalse */ + { + sqInt bytecode; + sqInt offset; + + VM_LABEL(booleanCheatFalse); + /* assume next bytecode is jumpIfFalse (99%) */ + bytecode = byteAt(++localIP); + + /* begin internalPop: */ + localSP += 2 * BytesPerOop; + if ((bytecode < 160) + && (bytecode > 151)) { + /* begin jump: */ + localIP = (localIP + (bytecode - 151)) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l133; + } + + /* short jumpIfFalse */ + if (bytecode == 172) { + offset = byteAt(++localIP); + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l133; + } + + /* long jumpIfFalse + not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result */ + currentBytecode = bytecode + GIV(bytecodeSetSelector); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(falseObj)); + } + /* end case */ +l133:; + BREAK; + CASE(180) // bytecodePrimLessOrEqual + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimLessOrEqual); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheat: */ + if (rcvr <= arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l29; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatLessOrEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l29; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (8U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l29:; + BREAK; + CASE(181) // bytecodePrimGreaterOrEqual + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimGreaterOrEqual); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheat: */ + if (rcvr >= arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l30; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatGreaterOrEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l30; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (10U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l30:; + BREAK; + CASE(182) // bytecodePrimEqual + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimEqual); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* begin booleanCheat: */ + if (rcvr == arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l31; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l31; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (12U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l31:; + BREAK; + CASE(183) // bytecodePrimNotEqual + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimNotEqual); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* begin booleanCheat: */ + if (rcvr != arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + goto l32; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheat: */ + if (aBool) { + goto booleanCheatFalse; + } + else { + goto booleanCheatTrue; + } + goto l32; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (14U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l32:; + BREAK; + CASE(184) // bytecodePrimMultiply + CASE(360) // 104 bytecodePrimMultiply + { + sqInt arg; + double argDouble; + usqLong bits; + sqInt oop; + sqInt overflow; + sqInt rcvr; + double rcvrDouble; + sqInt result; + double resultDouble; + char *sp; + sqInt tagBits; + double value; + + VM_LABEL(bytecodePrimMultiply); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + rcvr = (rcvr >> 3); + arg = (arg >> 3); + overflow = (rcvr > 0 + ? (arg > 0 + ? rcvr > ((MaxSmallInteger) / arg) + : arg < ((MinSmallInteger) / rcvr)) + : (arg > 0 + ? rcvr < ((MinSmallInteger) / arg) + : (rcvr < 0) + && (arg < ((MaxSmallInteger) / rcvr)))); + if (!overflow) { + result = rcvr * arg; + oop = (((usqInt)result << 3) | 1); + + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),oop); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l33; + } + } + else { + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin primitiveFloatMultiply:byArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvr & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvrDouble = value; + goto l34; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvrDouble = ((double) ((rcvr >> 3)) ); + goto l34; + } + } + else { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvr + BaseHeaderSize, resultDouble); + rcvrDouble = resultDouble; + goto l34; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvrDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l34: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = arg & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(arg)); + bits = ((((usqInt)arg))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + argDouble = value; + goto l35; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + argDouble = ((double) ((arg >> 3)) ); + goto l35; + } + } + else { + if (((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(arg + BaseHeaderSize, resultDouble); + argDouble = resultDouble; + goto l35; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + argDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l35: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvrDouble * argDouble)); + GIV(stackPointer) = sp; + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l33; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (16U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l33:; + BREAK; + CASE(185) // bytecodePrimDivide + CASE(361) // 105 bytecodePrimDivide + { + sqInt arg; + double argDouble; + usqLong bits; + sqInt rcvr; + double rcvrDouble; + sqInt result; + double resultDouble; + char *sp; + sqInt tagBits; + double value; + + VM_LABEL(bytecodePrimDivide); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + rcvr = (rcvr >> 3); + arg = (arg >> 3); + if ((arg != 0) + && ((rcvr % arg) == 0)) { + result = rcvr / arg; + + /* generates C / operation */ + if ((((((usqInt)(result)) >> 60) + 1) & 15) <= 1) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),(((usqInt)result << 3) | 1)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l36; + } + } + } + else { + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin primitiveFloatDivide:byArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvr & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvrDouble = value; + goto l37; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvrDouble = ((double) ((rcvr >> 3)) ); + goto l37; + } + } + else { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvr + BaseHeaderSize, resultDouble); + rcvrDouble = resultDouble; + goto l37; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvrDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l37: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = arg & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(arg)); + bits = ((((usqInt)arg))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + argDouble = value; + goto l38; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + argDouble = ((double) ((arg >> 3)) ); + goto l38; + } + } + else { + if (((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(arg + BaseHeaderSize, resultDouble); + argDouble = resultDouble; + goto l38; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + argDouble = 0.0; + /* end loadFloatOrIntFrom: */ +l38: + + /* begin success: */ + if (!(argDouble != 0.0)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvrDouble / argDouble)); + GIV(stackPointer) = sp; + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l36; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (18U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l36:; + BREAK; + CASE(186) // bytecodePrimMod + CASE(362) // 106 bytecodePrimMod + { + sqInt mod; + + VM_LABEL(bytecodePrimMod); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + mod = doPrimitiveModby(longAt(localSP + (1 * BytesPerOop)), longAt(localSP)); + if (!GIV(primFailCode)) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),(((usqInt)mod << 3) | 1)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l39; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (20U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l39:; + BREAK; + CASE(187) // bytecodePrimMakePoint + CASE(363) // 107 bytecodePrimMakePoint + { + sqInt arg; + usqInt newObj; + usqInt numBytes; + usqInt pt; + sqInt rcvr; + + VM_LABEL(bytecodePrimMakePoint); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((/* isFloatOrInt: */ + (((rcvr & (tagMask())) != 0) + ? !(((rcvr & (characterTag())) != 0)) + : ((longAt((void *)(rcvr))) & (classIndexMask())) == ClassFloatCompactIndex)) + && (/* isFloatOrInt: */ + (((arg & (tagMask())) != 0) + ? !(((arg & (characterTag())) != 0)) + : ((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex))) { + /* begin eeInstantiatePoint */ + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert(((YIndex + 1) >= 0) + && ((knownClassAtIndex(ClassPointCompactIndex)) != GIV(nilObj))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(knownClassAtIndex(ClassPointCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert((YIndex + 1) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (((YIndex + 1) < 1 + ? 8 /* allocationUnit */ + : (YIndex + 1) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + pt = 0; + goto l41; + } + } + long64Atput((void *)(newObj),((((((usqLong) (YIndex + 1))) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + ClassPointCompactIndex); + GIV(freeStart) += numBytes; + pt = newObj; + /* end eeInstantiatePoint */ +l41: + + /* No need to check since new object is always new. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pt)) + && (!(isForwarded(pt)))); + assert(validStorePointerUncheckedArgs(XIndex, pt, rcvr)); + longAtput((void *)((pt + BaseHeaderSize) + ((((usqInt)(XIndex) << (shiftForWord()))))),rcvr); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pt)) + && (!(isForwarded(pt)))); + assert(validStorePointerUncheckedArgs(YIndex, pt, arg)); + longAtput((void *)((pt + BaseHeaderSize) + ((((usqInt)(YIndex) << (shiftForWord()))))),arg); + + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),pt); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l40; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (22U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l40:; + BREAK; + CASE(188) // bytecodePrimBitShift + CASE(364) // 108 bytecodePrimBitShift + { + sqInt integerArgument; + sqInt integerReceiver; + sqInt shifted; + + VM_LABEL(bytecodePrimBitShift); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin primitiveBitShift */ + integerArgument = longAt(GIV(stackPointer)); + if (!((((integerArgument) & 7) == 1))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + goto l43; + } + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + integerReceiver = signed64BitValueOf(integerReceiver); + if (!GIV(primFailCode)) { + if (((integerArgument = (integerArgument >> 3))) >= 0) { + if (!(integerArgument <= 61 /* numSmallIntegerBits */)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + goto l43; + } + shifted = ((sqInt)((usqInt)(integerReceiver) << integerArgument)); + if (!(integerReceiver == ((shifted) >> integerArgument))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + goto l43; + } + } + else { + if (!(integerArgument >= (-61 /* numSmallIntegerBits */))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + goto l43; + } + shifted = (integerReceiver) >> (0 - integerArgument); + } + + /* Left shift -- must fail bits would be lost + Right shift -- OK to lose bits */ + shifted = ((((((usqInt)(shifted)) >> 60) + 1) & 15) <= 1 + ? (((usqInt)shifted << 3) | 1) + : signed64BitIntegerFor(shifted)); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),shifted); + } + /* end primitiveBitShift */ +l43: + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l42; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (24U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l42:; + BREAK; + CASE(189) // bytecodePrimDiv + CASE(365) // 109 bytecodePrimDiv + { + sqInt quotient; + + VM_LABEL(bytecodePrimDiv); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + quotient = doPrimitiveDivby(longAt(localSP + (1 * BytesPerOop)), longAt(localSP)); + if (!GIV(primFailCode)) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),(((usqInt)quotient << 3) | 1)); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l44; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (26U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l44:; + BREAK; + CASE(190) // bytecodePrimBitAnd + CASE(366) // 110 bytecodePrimBitAnd + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimBitAnd); + arg = longAt(localSP); + rcvr = longAt(localSP + (1 * BytesPerOop)); + if (((((arg) & 7) == 1)) + && ((((rcvr) & 7) == 1))) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),arg & rcvr); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l45; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + primitiveBitAnd(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l45; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (28U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l45:; + BREAK; + CASE(191) // bytecodePrimBitOr + CASE(367) // 111 bytecodePrimBitOr + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimBitOr); + arg = longAt(localSP); + rcvr = longAt(localSP + (1 * BytesPerOop)); + if (((((arg) & 7) == 1)) + && ((((rcvr) & 7) == 1))) { + /* begin internalPop:thenPush: */ + longAtput((localSP += 1 * BytesPerOop),arg | rcvr); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l46; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + primitiveBitOr(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l46; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (30U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l46:; + BREAK; + CASE(192) // bytecodePrimAt + CASE(368) // 112 bytecodePrimAt + { + VM_LABEL(bytecodePrimAt); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (32U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + BREAK; + CASE(193) // bytecodePrimAtPut + CASE(369) // 113 bytecodePrimAtPut + { + VM_LABEL(bytecodePrimAtPut); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (34U << (shiftForWord())))); + GIV(argumentCount) = 2; + goto normalSend; + } + BREAK; + CASE(194) // bytecodePrimSize + CASE(370) // 114 bytecodePrimSize + { + sqInt ccIndex; + sqInt fmt; + sqInt isArray; + sqInt isString; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt sz; + + VM_LABEL(bytecodePrimSize); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(localSP); + + /* Shortcut the mega-lookup for ByteString and Array, the two big consumers of cycles + here. Both of these have compact class indices and neither has any added fields. */ + + /* begin isInstanceOfClassByteString: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((rcvr & (tagMask())) != 0)) { + isString = 0; + goto l48; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isString = ClassByteStringCompactIndex == ccIndex; + /* end isInstanceOfClassByteString: */ +l48: + if (isString) { + /* begin lengthOf: */ + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + sz = numSlots; + goto l49; + } + if (fmt >= (firstByteFormat())) { + sz = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l49; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + sz = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l49; + } + if (fmt >= (firstLongFormat())) { + sz = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l49; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + sz = numSlots; + goto l49; + } + + /* fmt = self forwardedFormat */ + sz = 0; + /* end lengthOf: */ +l49: + + /* internalStackTopPut: */ + longAtput(localSP,(((usqInt)sz << 3) | 1)); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l47; + } + + /* begin isInstanceOfClassArray: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((rcvr & (tagMask())) != 0)) { + isArray = 0; + goto l50; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isArray = ClassArrayCompactIndex == ccIndex; + /* end isInstanceOfClassArray: */ +l50: + if (isArray) { + /* begin lengthOf: */ + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + sz = numSlots; + goto l51; + } + if (fmt >= (firstByteFormat())) { + sz = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l51; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + sz = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l51; + } + if (fmt >= (firstLongFormat())) { + sz = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l51; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + sz = numSlots; + goto l51; + } + + /* fmt = self forwardedFormat */ + sz = 0; + /* end lengthOf: */ +l51: + + /* internalStackTopPut: */ + longAtput(localSP,(((usqInt)sz << 3) | 1)); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l47; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (36U << (shiftForWord())))); + GIV(argumentCount) = 0; + goto normalSend; + } + /* end case */ +l47:; + BREAK; + CASE(195) // bytecodePrimNext + CASE(371) // 115 bytecodePrimNext + { + VM_LABEL(bytecodePrimNext); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (38U << (shiftForWord())))); + GIV(argumentCount) = ((fetchPointerofObject(39, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))))) >> 3); + goto normalSend; + } + BREAK; + CASE(196) // bytecodePrimNextPut + CASE(372) // 116 bytecodePrimNextPut + { + VM_LABEL(bytecodePrimNextPut); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (40U << (shiftForWord())))); + GIV(argumentCount) = ((fetchPointerofObject(41, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))))) >> 3); + goto normalSend; + } + BREAK; + CASE(197) // bytecodePrimAtEnd + CASE(373) // 117 bytecodePrimAtEnd + { + VM_LABEL(bytecodePrimAtEnd); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (42U << (shiftForWord())))); + GIV(argumentCount) = ((fetchPointerofObject(43, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))))) >> 3); + goto normalSend; + } + BREAK; + CASE(198) // bytecodePrimIdentical + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimIdentical); + rcvr = longAt(localSP + (1 * BytesPerOop)); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = handleSpecialSelectorSendFaultForfpsp(rcvr, localFP, localSP); + } + arg = longAt(localSP); + if (/* isOopForwarded: */ + ((!(arg & (tagMask())))) + && ((!((longAt((void *)(arg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + arg = handleSpecialSelectorSendFaultForfpsp(arg, localFP, localSP); + } + + /* begin booleanCheat: */ + if (rcvr == arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + } + BREAK; + CASE(199) // bytecodePrimClass + CASE(375) // 119 bytecodePrimClass + { + sqInt aValue; + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(bytecodePrimClass); + rcvr = longAt(localSP); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = handleSpecialSelectorSendFaultForfpsp(rcvr, localFP, localSP); + } + aValue = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)); + + /* begin internalStackTopPut: */ + longAtput(localSP,aValue); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(200) // bytecodePrimNotIdentical + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimNotIdentical); + rcvr = longAt(localSP + (1 * BytesPerOop)); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = handleSpecialSelectorSendFaultForfpsp(rcvr, localFP, localSP); + } + arg = longAt(localSP); + if (/* isOopForwarded: */ + ((!(arg & (tagMask())))) + && ((!((longAt((void *)(arg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + arg = handleSpecialSelectorSendFaultForfpsp(arg, localFP, localSP); + } + + /* begin booleanCheat: */ + if (rcvr != arg) { + goto booleanCheatTrue; + } + else { + goto booleanCheatFalse; + } + } + BREAK; + CASE(201) // bytecodePrimValue + CASE(377) // 121 bytecodePrimValue + { + sqInt ccIndex; + sqInt isBlock; + sqInt rcvr; + + VM_LABEL(bytecodePrimValue); + rcvr = longAt(localSP); + GIV(argumentCount) = 0; + + /* begin isInstanceOfClassBlockClosure: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((rcvr & (tagMask())) != 0)) { + isBlock = 0; + goto l53; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isBlock = ClassBlockClosureCompactIndex == ccIndex; + /* end isInstanceOfClassBlockClosure: */ +l53: + if (isBlock) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + primitiveClosureValue(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l52; + } + GIV(primFailCode) = 0; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (50U << (shiftForWord())))); + goto normalSend; + } + /* end case */ +l52:; + BREAK; + CASE(202) // bytecodePrimValueWithArg + CASE(378) // 122 bytecodePrimValueWithArg + { + sqInt ccIndex; + sqInt isBlock; + sqInt rcvr; + + VM_LABEL(bytecodePrimValueWithArg); + rcvr = longAt(localSP + (1 * BytesPerOop)); + GIV(argumentCount) = 1; + + /* begin isInstanceOfClassBlockClosure: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((rcvr & (tagMask())) != 0)) { + isBlock = 0; + goto l55; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isBlock = ClassBlockClosureCompactIndex == ccIndex; + /* end isInstanceOfClassBlockClosure: */ +l55: + if (isBlock) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + primitiveClosureValue(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (!GIV(primFailCode)) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l54; + } + GIV(primFailCode) = 0; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (52U << (shiftForWord())))); + goto normalSend; + } + /* end case */ +l54:; + BREAK; + CASE(203) // bytecodePrimDo + CASE(379) // 123 bytecodePrimDo + { + VM_LABEL(bytecodePrimDo); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (54U << (shiftForWord())))); + GIV(argumentCount) = ((fetchPointerofObject(55, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))))) >> 3); + goto normalSend; + } + BREAK; + CASE(204) // bytecodePrimNew + CASE(380) // 124 bytecodePrimNew + { + VM_LABEL(bytecodePrimNew); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (56U << (shiftForWord())))); + GIV(argumentCount) = 0; + goto normalSend; + } + BREAK; + CASE(205) // bytecodePrimNewWithArg + CASE(381) // 125 bytecodePrimNewWithArg + { + VM_LABEL(bytecodePrimNewWithArg); + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (58U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + BREAK; + CASE(206) // bytecodePrimPointX + CASE(382) // 126 bytecodePrimPointX + { + sqInt ccIndex; + sqInt isClassOfNonImmequalTocompactClassIndexRV; + sqInt rcvr; + + VM_LABEL(bytecodePrimPointX); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(localSP); + if ((!(rcvr & (tagMask())))) { + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isClassOfNonImmequalTocompactClassIndexRV = ClassPointCompactIndex == ccIndex; + if (isClassOfNonImmequalTocompactClassIndexRV) { + longAtput(localSP,longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(XIndex) << (shiftForWord()))))))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l56; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (60U << (shiftForWord())))); + GIV(argumentCount) = 0; + goto normalSend; + } + /* end case */ +l56:; + BREAK; + CASE(207) // bytecodePrimPointY + CASE(383) // 127 bytecodePrimPointY + { + sqInt ccIndex; + sqInt isClassOfNonImmequalTocompactClassIndexRV; + sqInt rcvr; + + VM_LABEL(bytecodePrimPointY); + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(localSP); + if ((!(rcvr & (tagMask())))) { + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(rcvr))); + ccIndex = (longAt((void *)(rcvr))) & (classIndexMask()); + isClassOfNonImmequalTocompactClassIndexRV = ClassPointCompactIndex == ccIndex; + if (isClassOfNonImmequalTocompactClassIndexRV) { + longAtput(localSP,longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(YIndex) << (shiftForWord()))))))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l57; + } + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (0x3EU << (shiftForWord())))); + GIV(argumentCount) = 0; + goto normalSend; + } + /* end case */ +l57:; + BREAK; + CASE(208) // sendLiteralSelector0ArgsBytecode + CASE(209) // sendLiteralSelector0ArgsBytecode + CASE(210) // sendLiteralSelector0ArgsBytecode + CASE(211) // sendLiteralSelector0ArgsBytecode + CASE(212) // sendLiteralSelector0ArgsBytecode + CASE(213) // sendLiteralSelector0ArgsBytecode + CASE(214) // sendLiteralSelector0ArgsBytecode + CASE(215) // sendLiteralSelector0ArgsBytecode + CASE(216) // sendLiteralSelector0ArgsBytecode + CASE(217) // sendLiteralSelector0ArgsBytecode + CASE(218) // sendLiteralSelector0ArgsBytecode + CASE(219) // sendLiteralSelector0ArgsBytecode + CASE(220) // sendLiteralSelector0ArgsBytecode + CASE(221) // sendLiteralSelector0ArgsBytecode + CASE(222) // sendLiteralSelector0ArgsBytecode + CASE(223) // sendLiteralSelector0ArgsBytecode + CASE(384) // 128 sendLiteralSelector0ArgsBytecode + CASE(385) // 129 sendLiteralSelector0ArgsBytecode + CASE(386) // 130 sendLiteralSelector0ArgsBytecode + CASE(387) // 131 sendLiteralSelector0ArgsBytecode + CASE(388) // 132 sendLiteralSelector0ArgsBytecode + CASE(389) // 133 sendLiteralSelector0ArgsBytecode + CASE(390) // 134 sendLiteralSelector0ArgsBytecode + CASE(391) // 135 sendLiteralSelector0ArgsBytecode + CASE(392) // 136 sendLiteralSelector0ArgsBytecode + CASE(393) // 137 sendLiteralSelector0ArgsBytecode + CASE(394) // 138 sendLiteralSelector0ArgsBytecode + CASE(395) // 139 sendLiteralSelector0ArgsBytecode + CASE(396) // 140 sendLiteralSelector0ArgsBytecode + CASE(397) // 141 sendLiteralSelector0ArgsBytecode + CASE(398) // 142 sendLiteralSelector0ArgsBytecode + CASE(399) // 143 sendLiteralSelector0ArgsBytecode + { + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(sendLiteralSelector0ArgsBytecode); + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((currentBytecode & 15) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = 0; + rcvr = longAt(localSP); + lkupClassTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + assert(lkupClassTag != (nilObject())); + goto commonSendOrdinary; + } + BREAK; + CASE(224) // sendLiteralSelector1ArgBytecode + CASE(225) // sendLiteralSelector1ArgBytecode + CASE(226) // sendLiteralSelector1ArgBytecode + CASE(227) // sendLiteralSelector1ArgBytecode + CASE(228) // sendLiteralSelector1ArgBytecode + CASE(229) // sendLiteralSelector1ArgBytecode + CASE(230) // sendLiteralSelector1ArgBytecode + CASE(231) // sendLiteralSelector1ArgBytecode + CASE(232) // sendLiteralSelector1ArgBytecode + CASE(233) // sendLiteralSelector1ArgBytecode + CASE(234) // sendLiteralSelector1ArgBytecode + CASE(235) // sendLiteralSelector1ArgBytecode + CASE(236) // sendLiteralSelector1ArgBytecode + CASE(237) // sendLiteralSelector1ArgBytecode + CASE(238) // sendLiteralSelector1ArgBytecode + CASE(239) // sendLiteralSelector1ArgBytecode + CASE(400) // 144 sendLiteralSelector1ArgBytecode + CASE(401) // 145 sendLiteralSelector1ArgBytecode + CASE(402) // 146 sendLiteralSelector1ArgBytecode + CASE(403) // 147 sendLiteralSelector1ArgBytecode + CASE(404) // 148 sendLiteralSelector1ArgBytecode + CASE(405) // 149 sendLiteralSelector1ArgBytecode + CASE(406) // 150 sendLiteralSelector1ArgBytecode + CASE(407) // 151 sendLiteralSelector1ArgBytecode + CASE(408) // 152 sendLiteralSelector1ArgBytecode + CASE(409) // 153 sendLiteralSelector1ArgBytecode + CASE(410) // 154 sendLiteralSelector1ArgBytecode + CASE(411) // 155 sendLiteralSelector1ArgBytecode + CASE(412) // 156 sendLiteralSelector1ArgBytecode + CASE(413) // 157 sendLiteralSelector1ArgBytecode + CASE(414) // 158 sendLiteralSelector1ArgBytecode + CASE(415) // 159 sendLiteralSelector1ArgBytecode + { + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(sendLiteralSelector1ArgBytecode); + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((currentBytecode & 15) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = 1; + rcvr = longAt(localSP + (1 * BytesPerOop)); + lkupClassTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + assert(lkupClassTag != (nilObject())); + goto commonSendOrdinary; + } + BREAK; + CASE(240) // sendLiteralSelector2ArgsBytecode + CASE(241) // sendLiteralSelector2ArgsBytecode + CASE(242) // sendLiteralSelector2ArgsBytecode + CASE(243) // sendLiteralSelector2ArgsBytecode + CASE(244) // sendLiteralSelector2ArgsBytecode + CASE(245) // sendLiteralSelector2ArgsBytecode + CASE(246) // sendLiteralSelector2ArgsBytecode + CASE(247) // sendLiteralSelector2ArgsBytecode + CASE(248) // sendLiteralSelector2ArgsBytecode + CASE(249) // sendLiteralSelector2ArgsBytecode + CASE(250) // sendLiteralSelector2ArgsBytecode + CASE(251) // sendLiteralSelector2ArgsBytecode + CASE(252) // sendLiteralSelector2ArgsBytecode + CASE(253) // sendLiteralSelector2ArgsBytecode + CASE(254) // sendLiteralSelector2ArgsBytecode + CASE(255) // sendLiteralSelector2ArgsBytecode + CASE(416) // 160 sendLiteralSelector2ArgsBytecode + CASE(417) // 161 sendLiteralSelector2ArgsBytecode + CASE(418) // 162 sendLiteralSelector2ArgsBytecode + CASE(419) // 163 sendLiteralSelector2ArgsBytecode + CASE(420) // 164 sendLiteralSelector2ArgsBytecode + CASE(421) // 165 sendLiteralSelector2ArgsBytecode + CASE(422) // 166 sendLiteralSelector2ArgsBytecode + CASE(423) // 167 sendLiteralSelector2ArgsBytecode + CASE(424) // 168 sendLiteralSelector2ArgsBytecode + CASE(425) // 169 sendLiteralSelector2ArgsBytecode + CASE(426) // 170 sendLiteralSelector2ArgsBytecode + CASE(427) // 171 sendLiteralSelector2ArgsBytecode + CASE(428) // 172 sendLiteralSelector2ArgsBytecode + CASE(429) // 173 sendLiteralSelector2ArgsBytecode + CASE(430) // 174 sendLiteralSelector2ArgsBytecode + CASE(431) // 175 sendLiteralSelector2ArgsBytecode + { + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(sendLiteralSelector2ArgsBytecode); + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((currentBytecode & 15) + LiteralStart) << (shiftForWord()))))); + GIV(argumentCount) = 2; + rcvr = longAt(localSP + (2 * BytesPerOop)); + lkupClassTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + assert(lkupClassTag != (nilObject())); + goto commonSendOrdinary; + } + BREAK; + CASE(338) // 82 extPushPseudoVariable + { + sqInt objOop; + sqInt theThingToPush; + + VM_LABEL(extPushPseudoVariable); + switch (extB) { + case 0: + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + theThingToPush = longAt(localFP + FoxThisContext); + goto l58; + } + theThingToPush = marryFrameSP(localFP, localSP); + /* end ensureFrameIsMarried:SP: */ +l58:; + break; + case 1: + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + theThingToPush = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + break; + default: + goto respondToUnknownBytecode; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),theThingToPush); + extB = 0; + numExtB = 0; + } + BREAK; + CASE(340) // 84 unknownBytecode + CASE(341) // 85 unknownBytecode + CASE(342) // 86 unknownBytecode + CASE(343) // 87 unknownBytecode + CASE(473) // 217 unconditionalTrapBytecode + CASE(474) // 218 unknownBytecode + CASE(475) // 219 unknownBytecode + CASE(476) // 220 unknownBytecode + CASE(477) // 221 unknownBytecode + CASE(478) // 222 unknownBytecode + CASE(479) // 223 unknownBytecode + CASE(486) // 230 unknownBytecode + CASE(502) // 246 unknownBytecode + CASE(503) // 247 unknownBytecode + CASE(510) // 254 unknownBytecode + CASE(511) // 255 unknownBytecode + { + VM_LABEL(unknownBytecode); + goto respondToUnknownBytecode; + } + BREAK; + CASE(344) // 88 returnReceiver + { + VM_LABEL(returnReceiver1); + localReturnValue = longAt(localFP + FoxIFReceiver); + goto commonReturn; + } + BREAK; + CASE(349) // 93 returnNilFromBlock + { + VM_LABEL(returnNilFromBlock); + localReturnValue = GIV(nilObj); + goto commonCallerReturn; + } + BREAK; + CASE(350) // 94 returnTopFromBlock + { + VM_LABEL(returnTopFromBlock1); + localReturnValue = longAt(localSP); + goto commonCallerReturn; + } + BREAK; + CASE(351) // 95 extNopBytecode + { + VM_LABEL(extNopBytecode); + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + numExtB = (extA = (extB = 0)); + } + BREAK; + CASE(354) // 98 bytecodePrimLessThanSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimLessThanSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheatSistaV1: */ + if (rcvr < arg) { + /* goto booleanCheatTrueSistaV1 */ + } + else { + goto booleanCheatFalseSistaV1; + } + goto l65; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatLessthanArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l65; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (4U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l65: + + booleanCheatTrueSistaV1: + /* booleanCheatTrueSistaV1 */ + { + sqInt bytecode; + sqInt offset; + + VM_LABEL(booleanCheatTrueSistaV1); + /* assume next bytecode is jumpIfFalse (99%) */ + bytecode = byteAt(++localIP); + + /* begin internalPop: */ + localSP += 2 * BytesPerOop; + if (bytecode >= 192) { + if (bytecode <= 199) { + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l134; + } + + /* short jumpIfFalse 192 - 199 */ + if (bytecode == 239) { + /* fetchByte */ + byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + goto l134; + } + + /* long jumpIfFalse */ + if (bytecode == 238) { + offset = byteAt(++localIP); + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l134; + } + } + + /* not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result */ + currentBytecode = bytecode + GIV(bytecodeSetSelector); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(trueObj)); + } + /* end case */ +l134:; + BREAK; + CASE(355) // 99 bytecodePrimGreaterThanSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimGreaterThanSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheatSistaV1: */ + if (rcvr > arg) { + goto booleanCheatTrueSistaV1; + } + else { + /* goto booleanCheatFalseSistaV1 */ + } + goto l66; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatGreaterthanArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l66; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (6U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l66: + + booleanCheatFalseSistaV1: + /* booleanCheatFalseSistaV1 */ + { + sqInt bytecode; + sqInt offset; + + VM_LABEL(booleanCheatFalseSistaV1); + /* assume next bytecode is jumpIfFalse (99%) */ + bytecode = byteAt(++localIP); + + /* begin internalPop: */ + localSP += 2 * BytesPerOop; + if ((bytecode < 199) + && (bytecode > 191)) { + /* begin jump: */ + localIP = (localIP + (bytecode - 191)) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l135; + } + + /* short jumpIfFalse */ + if (bytecode == 239) { + offset = byteAt(++localIP); + + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + goto l135; + } + + /* long jumpIfFalse + not followed by a jumpIfFalse; (un)do instruction fetch and push boolean result */ + currentBytecode = bytecode + GIV(bytecodeSetSelector); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),GIV(falseObj)); + } + /* end case */ +l135:; + BREAK; + CASE(356) // 100 bytecodePrimLessOrEqualSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimLessOrEqualSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheatSistaV1: */ + if (rcvr <= arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l67; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatLessOrEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l67; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (8U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l67:; + BREAK; + CASE(357) // 101 bytecodePrimGreaterOrEqualSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimGreaterOrEqualSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* The C code can avoid detagging since tagged integers are still signed. + But this means the simulator must override to do detagging. */ + + /* begin booleanCheatSistaV1: */ + if (rcvr >= arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l68; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatGreaterOrEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l68; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (10U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l68:; + BREAK; + CASE(358) // 102 bytecodePrimEqualSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimEqualSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* begin booleanCheatSistaV1: */ + if (rcvr == arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l69; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l69; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (12U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l69:; + BREAK; + CASE(359) // 103 bytecodePrimNotEqualSistaV1 + { + int aBool; + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimNotEqualSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + /* begin booleanCheatSistaV1: */ + if (rcvr != arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + goto l70; + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + aBool = primitiveFloatEqualtoArg(rcvr, arg); + if (!GIV(primFailCode)) { + /* begin booleanCheatSistaV1: */ + if (aBool) { + goto booleanCheatFalseSistaV1; + } + else { + goto booleanCheatTrueSistaV1; + } + goto l70; + } + GIV(messageSelector) = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + (14U << (shiftForWord())))); + GIV(argumentCount) = 1; + goto normalSend; + } + /* end case */ +l70:; + BREAK; + CASE(374) // 118 bytecodePrimIdenticalSistaV1 + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimIdenticalSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = handleSpecialSelectorSendFaultForfpsp(rcvr, localFP, localSP); + } + if (/* isOopForwarded: */ + ((!(arg & (tagMask())))) + && ((!((longAt((void *)(arg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + arg = handleSpecialSelectorSendFaultForfpsp(arg, localFP, localSP); + } + + /* begin booleanCheatSistaV1: */ + if (rcvr == arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + } + BREAK; + CASE(376) // 120 bytecodePrimNotIdenticalSistaV1 + { + sqInt arg; + sqInt rcvr; + + VM_LABEL(bytecodePrimNotIdenticalSistaV1); + rcvr = longAt(localSP + (1 * BytesPerOop)); + arg = longAt(localSP); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = handleSpecialSelectorSendFaultForfpsp(rcvr, localFP, localSP); + } + if (/* isOopForwarded: */ + ((!(arg & (tagMask())))) + && ((!((longAt((void *)(arg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + arg = handleSpecialSelectorSendFaultForfpsp(arg, localFP, localSP); + } + + /* begin booleanCheatSistaV1: */ + if (rcvr != arg) { + goto booleanCheatTrueSistaV1; + } + else { + goto booleanCheatFalseSistaV1; + } + } + BREAK; + CASE(440) // 184 shortConditionalJumpTrue + CASE(441) // 185 shortConditionalJumpTrue + CASE(442) // 186 shortConditionalJumpTrue + CASE(443) // 187 shortConditionalJumpTrue + CASE(444) // 188 shortConditionalJumpTrue + CASE(445) // 189 shortConditionalJumpTrue + CASE(446) // 190 shortConditionalJumpTrue + CASE(447) // 191 shortConditionalJumpTrue + { + sqInt boolean; + sqInt offset; + + VM_LABEL(shortConditionalJumpTrue); + offset = (currentBytecode & 7) + 1; + + /* begin jumplfTrueBy: */ + boolean = longAt(localSP); + if (boolean == GIV(trueObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(falseObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l96; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfTrueBy: */ +l96:; + } + BREAK; + CASE(480) // 224 extABytecode + { + VM_LABEL(extABytecode); + extA = (((((usqInt)(extA) << 8)))) + (byteAt(++localIP)); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(481) // 225 extBBytecode + { + sqInt byte; + + VM_LABEL(extBBytecode); + byte = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + extB = ((numExtB == 0) + && (byte > 0x7F) + ? byte - 0x100 + : (((((usqInt)(extB) << 8)))) + byte); + numExtB += 1; + } + BREAK; + CASE(482) // 226 extPushReceiverVariableBytecode + { + sqInt callerContextOrNil; + char *callerFP; + sqInt index; + sqInt obj; + sqInt object; + sqInt senderOop; + char *spouseFP; + StackPage *thePage; + sqInt value; + + VM_LABEL(extPushReceiverVariableBytecode); + index = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + extA = 0; + + /* begin pushMaybeContextReceiverVariable: */ + obj = longAt(localFP + FoxIFReceiver); + if ((index <= StackPointerIndex) + && (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin instVar:ofContext: */ + assert(index < MethodIndex); + assert(isContext(obj)); + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + if (!(((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + value = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if ((index == InstructionPointerIndex) + && (((((value) & 7) == 1)) + && ((((int) value)) < 0))) { + /* begin internalMustMapMachineCodePC:context: */ + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + value = mustMapMachineCodePCcontext((value >> 3), obj); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + } + object = value; + goto l99; + } + if (isWidowedContext(obj)) { + object = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + goto l99; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!index) { + /* begin ensureCallerContext: */ + callerFP = ((char *)(longAt(spouseFP + FoxSavedFP))); + if (!callerFP) { + /* begin frameCallerContext: */ + assert(isBaseFrame(spouseFP)); + thePage = stackPageAtpages(pageIndexFor(spouseFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + object = callerContextOrNil; + goto l99; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP))); + object = longAt(callerFP + FoxThisContext); + goto l99; + } + object = marryFrameSP( + callerFP, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(spouseFP))), + (spouseFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(spouseFP))->cmNumArgs) + : byteAt((spouseFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + goto l99; + } + if (index == StackPointerIndex) { + assert((ReceiverIndex + (stackPointerIndexForFrame(spouseFP))) < (lengthOf(obj))); + object = (((usqInt)(stackPointerIndexForFrame(spouseFP)) << 3) | 1); + goto l99; + } + if (index == InstructionPointerIndex) { + object = instructionPointerForFramecurrentFPcurrentIP(spouseFP, localFP, oopForPointer(localIP)); + goto l99; + } + error("bad index"); + object = 0; + /* end instVar:ofContext: */ +l99: + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + else { + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))))); + } + } + BREAK; + CASE(483) // 227 extPushLiteralVariableBytecode + { + sqInt index; + sqInt litVar; + + VM_LABEL(extPushLiteralVariableBytecode); + index = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + extA = 0; + + /* begin pushLiteralVariable: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((index + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(index + LiteralStart, GIV(method), litVar); + } + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),longAt((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))))); + } + BREAK; + CASE(484) // 228 extPushLiteralBytecode + { + sqInt index; + sqInt object; + + VM_LABEL(extPushLiteralBytecode); + index = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + extA = 0; + + /* begin pushLiteralConstant: */ + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + object = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((index + LiteralStart)) << (shiftForWord())))))); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + } + BREAK; + CASE(485) // 229 longPushTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt index; + + VM_LABEL(longPushTemporaryVariableBytecode); + index = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),/* itemporary:in: */ + (index < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1))) + ? longAt((localFP + FoxCallerSavedIP) + ((frameNumArgs - index) * BytesPerWord)) + : longAt(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - index) * BytesPerWord)))); + } + BREAK; + CASE(488) // 232 extPushIntegerBytecode + { + sqInt value; + + VM_LABEL(extPushIntegerBytecode); + value = (byteAt(++localIP)) + ((((usqInt)(extB) << 8))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + extB = 0; + numExtB = 0; + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),(((usqInt)value << 3) | 1)); + } + BREAK; + CASE(489) // 233 extPushCharacterBytecode + { + sqInt object; + sqInt value; + + VM_LABEL(extPushCharacterBytecode); + value = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + object = ((((usqInt)(value) << (numTagBits())))) + (characterTag()); + + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),object); + extA = 0; + } + BREAK; + CASE(490) // 234 extSendBytecode + { + sqInt byte; + sqInt rcvr; + sqInt tagBits; + + VM_LABEL(extSendBytecode); + byte = byteAt(++localIP); + + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((((((usqInt)(byte)) >> 3) + ((((usqInt)(extA) << 5)))) + LiteralStart)) << (shiftForWord())))))); + extA = 0; + GIV(argumentCount) = (byte & 7) + ((((usqInt)(extB) << 3))); + extB = 0; + numExtB = 0; + rcvr = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + lkupClassTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + goto commonSendOrdinary; + } + BREAK; + CASE(491) // 235 extSendSuperBytecode + { + sqInt byte; + + VM_LABEL(extSendSuperBytecode); + byte = byteAt(++localIP); + + /* begin literal: */ + assert(GIV(method) == (iframeMethod(localFP))); + GIV(messageSelector) = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((((((usqInt)(byte)) >> 3) + ((((usqInt)(extA) << 5)))) + LiteralStart)) << (shiftForWord())))))); + extA = 0; + if (extB >= 64) { + GIV(argumentCount) = (byte & 7) + ((((usqInt)((extB - 64)) << 3))); + extB = 0; + numExtB = 0; + + /* goto directedSuperclassSend */ + goto l101; + } + GIV(argumentCount) = (byte & 7) + ((((usqInt)(extB) << 3))); + extB = 0; + numExtB = 0; + goto superclassSend; + } + /* end case */ +l101: + + directedSuperclassSend: + /* directedSuperclassSend */ + { + sqInt aValue; + sqInt class; + sqInt err; + int hash; + sqInt objOop; + sqInt referent; + sqInt superclass; + + VM_LABEL(directedSuperclassSend); + /* begin internalPopStack */ + class = longAt(localSP); + localSP += BytesPerOop; + if ((!((longAt((void *)(class))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(class)); + referent = longAt((void *)((class + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + class = referent; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + superclass = longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(superclass)); + if ((!((longAt((void *)(superclass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + superclass = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, class, superclass); + } + + /* classTagForClass: uses ensureBehaviorHash: */ + + /* begin classTagForClass: */ + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(superclass)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + lkupClassTag = ((hash = (long32At((void *)(superclass + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(superclass) + ? ((err = enterIntoClassTable(superclass)) + ? -err + : (long32At((void *)(superclass + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + + /* To maintain the invariant that all receivers are unforwarded we need an explicit + read barrier in the super send cases. Even though we always follow receivers + on become e.g. super doSomethingWith: (self become: other) forwards the receiver + self pushed on the stack. */ + + /* begin ensureReceiverUnforwarded */ + if (/* isOopForwarded: */ + ((!((longAt(localSP + (GIV(argumentCount) * BytesPerOop))) & (tagMask())))) + && ((!((longAt((void *)(longAt(localSP + (GIV(argumentCount) * BytesPerOop))))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = longAt(localSP + (GIV(argumentCount) * BytesPerOop)); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + referent = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + aValue = referent; + + /* begin internalStackValue:put: */ + longAtput(localSP + (GIV(argumentCount) * BytesPerOop),aValue); + } + assert(lkupClassTag != (nilObject())); + goto commonSendOrdinary; + } + BREAK; + CASE(492) // 236 callMappedInlinedPrimitive + { + VM_LABEL(callMappedInlinedPrimitive); + localIP -= 2; + goto respondToUnknownBytecode; + } + BREAK; + CASE(493) // 237 extUnconditionalJump + { + sqInt backwardJumpCountByte; + sqInt bcpcDelta; + sqInt byte; + sqInt header; + sqInt methodHeader; + sqInt offset; + sqInt switched; + + VM_LABEL(extUnconditionalJump); + byte = byteAt(++localIP); + offset = byte + ((((usqInt)(extB) << 8))); + bcpcDelta = (offset < 0 + ? numExtB * 2 + : 0); + extB = 0; + numExtB = 0; + localIP += offset; + + /* begin ifBackwardsCheckForEvents: */ + if ((offset + bcpcDelta) >= 0) { + goto l102; + } + if (localSP < GIV(stackLimit)) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + switched = checkForEventsMayContextSwitch(1); + returnToExecutivepostContextSwitch(1, switched); + browserPluginReturnIfNeeded(); + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + if (switched) { + goto l102; + } + } + + /* We use the least significant byte of the flags word (which is marked as an immediate) and + subtract two each time to avoid disturbing the least significant tag bit. Since the byte is + initialized to 1 (on frame build), on first decrement it will become -1. Trip when it reaches 1 again. */ + backwardJumpCountByte = byteAt(localFP + ((VMBIGENDIAN + ? (FoxIFrameFlags + BytesPerWord) - 1 + : FoxIFrameFlags))); + if (((backwardJumpCountByte -= 2)) == 1) { + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + attemptToSwitchToMachineCode(((((oopForPointer(localIP)) - (offset + bcpcDelta)) - GIV(method)) - BaseHeaderSize) - 1); + } + + /* can't cog method; avoid asking to cog it again for the longest possible time. */ + backwardJumpCountByte = 0x7F; + } + else { + if (backwardJumpCountByte == -1) { + assert(minBackwardJumpCountForCompile <= 128); + backwardJumpCountByte = ((((usqInt)((minBackwardJumpCountForCompile - 1)) << 1))) + 1; + } + } + + /* begin iframeBackwardBranchByte:put: */ + byteAtput(localFP + ((VMBIGENDIAN + ? (FoxIFrameFlags + BytesPerWord) - 1 + : FoxIFrameFlags)),backwardJumpCountByte); + /* end ifBackwardsCheckForEvents: */ +l102: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(494) // 238 extJumpIfTrue + { + sqInt boolean; + sqInt byte; + sqInt offset; + + VM_LABEL(extJumpIfTrue); + byte = byteAt(++localIP); + offset = byte + ((((usqInt)(extB) << 8))); + numExtB = (extB = (extA = 0)); + + /* begin jumplfTrueBy: */ + boolean = longAt(localSP); + if (boolean == GIV(trueObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(falseObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l103; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfTrueBy: */ +l103:; + } + BREAK; + CASE(495) // 239 extJumpIfFalse + { + sqInt boolean; + sqInt byte; + sqInt offset; + + VM_LABEL(extJumpIfFalse); + byte = byteAt(++localIP); + offset = byte + ((((usqInt)(extB) << 8))); + numExtB = (extB = (extA = 0)); + + /* begin jumplfFalseBy: */ + boolean = longAt(localSP); + if (boolean == GIV(falseObj)) { + /* begin jump: */ + localIP = (localIP + offset) + 1; + currentBytecode = (byteAt(localIP)) + GIV(bytecodeSetSelector); + } + else { + if (!(boolean == GIV(trueObj))) { + /* begin internalMustBeBoolean */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))); + GIV(argumentCount) = 0; + goto normalSend; + goto l104; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + localSP += 1 * BytesPerOop; + /* end jumplfFalseBy: */ +l104:; + } + BREAK; + CASE(496) // 240 extStoreAndPopReceiverVariableBytecode + { + sqInt obj; + int onCurrentPage; + sqInt senderOop; + char *theFP; + StackPage *thePage; + sqInt value; + sqInt variableIndex; + + VM_LABEL(extStoreAndPopReceiverVariableBytecode); + variableIndex = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + extA = 0; + value = longAt(localSP); + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + obj = longAt(localFP + FoxIFReceiver); + if ((variableIndex <= ReceiverIndex) + && ((((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + || (ismethodAssignmentToContextWithMachineCodePC(variableIndex, value))))) { + /* begin instVar:ofContext:put: */ + assert(isMarriedOrWidowedContext(obj)); + assert(!((isObjImmutable(obj)))); + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(obj))))) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, value)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),value); + if (variableIndex == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + goto l105; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!variableIndex) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + onCurrentPage = thePage == GIV(stackPage); + storeSenderOfFramewithValue(theFP, value); + if (onCurrentPage) { + localFP = (GIV(stackPage)->headFP); + localSP = (GIV(stackPage)->headSP); + } + else { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + goto l105; + } + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + externalDivorceFrameandContext(theFP, obj); + if (ismethodAssignmentToContextWithMachineCodePC(variableIndex, obj)) { + ensureContextHasBytecodePC(obj); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, value)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),value); + if (variableIndex == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + + /* Assigning various fields can force a divorce which can change the stackPage. */ + markStackPageMostRecentlyUsed(GIV(stackPage)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 1, __LINE__); + /* end instVar:ofContext:put: */ +l105:; + } + else { + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(obj)))); + if ((((usqInt)((byteAt((void *)(obj + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),obj); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((usqInt)(variableIndex + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l106; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, value)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l106:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(497) // 241 extStoreAndPopLiteralVariableBytecode + { + sqInt litVar; + sqInt value; + sqInt variableIndex; + + VM_LABEL(extStoreAndPopLiteralVariableBytecode); + variableIndex = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + value = longAt(localSP); + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + extA = 0; + + /* begin storeLiteralVariable:withValue: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((variableIndex + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(variableIndex + LiteralStart, GIV(method), litVar); + } + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(litVar)))); + if ((((usqInt)((byteAt((void *)(litVar + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),litVar); + longAtput((localSP -= BytesPerOop),value); + longAtput((localSP -= BytesPerOop),(((usqInt)2 /* (ValueIndex + 1) */ << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l107; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ValueIndex, litVar, value)); + assert(isNonImmediate(litVar)); + if (oopisGreaterThanOrEqualTo(litVar, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(litVar + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(litVar); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))),value); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l107: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(498) // 242 longStoreAndPopTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt index; + + VM_LABEL(longStoreAndPopTemporaryVariableBytecode); + /* begin longStoreTemporaryVariableBytecode */ + index = byteAt(++localIP); + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (index < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - index) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - index) * BytesPerWord),longAt(localSP)); + } + + /* begin internalPop: */ + localSP += 1 * BytesPerOop; + } + BREAK; + CASE(499) // 243 extStoreReceiverVariableBytecode + { + sqInt anObject; + sqInt obj; + int onCurrentPage; + sqInt senderOop; + char *theFP; + StackPage *thePage; + sqInt variableIndex; + + VM_LABEL(extStoreReceiverVariableBytecode); + variableIndex = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + extA = 0; + anObject = longAt(localSP); + + /* begin storeMaybeContextReceiverVariable:withValue: */ + obj = longAt(localFP + FoxIFReceiver); + if ((variableIndex <= ReceiverIndex) + && ((((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + || (ismethodAssignmentToContextWithMachineCodePC(variableIndex, anObject))))) { + /* begin instVar:ofContext:put: */ + assert(isMarriedOrWidowedContext(obj)); + assert(!((isObjImmutable(obj)))); + + /* begin writeBackHeadFramePointers */ + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(localSP < localFP); + assert((localSP < ((GIV(stackPage)->baseAddress))) + && (localSP > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((localFP < ((GIV(stackPage)->baseAddress))) + && (localFP > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = localFP); + (GIV(stackPage)->headSP = localSP); + assert(pageListIsWellFormed()); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(obj))))) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, anObject)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anObject & (tagMask())))) + && (oopisLessThan(anObject, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),anObject); + if (variableIndex == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + goto l108; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!variableIndex) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + onCurrentPage = thePage == GIV(stackPage); + storeSenderOfFramewithValue(theFP, anObject); + if (onCurrentPage) { + localFP = (GIV(stackPage)->headFP); + localSP = (GIV(stackPage)->headSP); + } + else { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + goto l108; + } + + /* begin externalizeIPandSP */ + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + externalDivorceFrameandContext(theFP, obj); + if (ismethodAssignmentToContextWithMachineCodePC(variableIndex, obj)) { + ensureContextHasBytecodePC(obj); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, anObject)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anObject & (tagMask())))) + && (oopisLessThan(anObject, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),anObject); + if (variableIndex == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(obj); + } + + /* begin internalizeIPandSP */ + assert(GIV(instructionPointer) != (ceReturnToInterpreterPC())); + localIP = ((char *)GIV(instructionPointer)); + localSP = GIV(stackPointer); + localFP = GIV(framePointer); + + /* Assigning various fields can force a divorce which can change the stackPage. */ + markStackPageMostRecentlyUsed(GIV(stackPage)); + assertValidExecutionPointersimbarline(((usqInt)localIP), localFP, localSP, 1, __LINE__); + /* end instVar:ofContext:put: */ +l108:; + } + else { + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(obj)))); + if ((((usqInt)((byteAt((void *)(obj + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),obj); + longAtput((localSP -= BytesPerOop),anObject); + longAtput((localSP -= BytesPerOop),(((usqInt)(variableIndex + 1) << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l109; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(variableIndex, obj, anObject)); + assert(isNonImmediate(obj)); + if (oopisGreaterThanOrEqualTo(obj, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anObject & (tagMask())))) + && (oopisLessThan(anObject, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(variableIndex) << (shiftForWord()))))),anObject); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l109:; + } + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(500) // 244 extStoreLiteralVariableBytecode + { + sqInt anObject; + sqInt litVar; + sqInt variableIndex; + + VM_LABEL(extStoreLiteralVariableBytecode); + variableIndex = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + extA = 0; + anObject = longAt(localSP); + + /* begin storeLiteralVariable:withValue: */ + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + litVar = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((variableIndex + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(litVar)); + if ((!((longAt((void *)(litVar))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + litVar = fixFollowedFieldofObjectwithInitialValue(variableIndex + LiteralStart, GIV(method), litVar); + } + + /* begin storePointerImmutabilityCheck:ofObject:withValue: */ +# if IMMUTABILITY + assert(!((isImmediate(litVar)))); + if ((((usqInt)((byteAt((void *)(litVar + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) { + /* begin cannotAssign:to:withIndex: */ + /* begin internalPush: */ + longAtput((localSP -= BytesPerOop),litVar); + longAtput((localSP -= BytesPerOop),anObject); + longAtput((localSP -= BytesPerOop),(((usqInt)2 /* (ValueIndex + 1) */ << 3) | 1)); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))); + GIV(argumentCount) = 2; + goto normalSend; + goto l110; + } +# endif // IMMUTABILITY + + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ValueIndex, litVar, anObject)); + assert(isNonImmediate(litVar)); + if (oopisGreaterThanOrEqualTo(litVar, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anObject & (tagMask())))) + && (oopisLessThan(anObject, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(litVar + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(litVar); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((litVar + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))),anObject); + /* end storePointerImmutabilityCheck:ofObject:withValue: */ +l110: + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + } + BREAK; + CASE(501) // 245 longStoreTemporaryVariableBytecode + { + usqInt frameNumArgs; + sqInt index; + + VM_LABEL(longStoreTemporaryVariableBytecode); + index = byteAt(++localIP); + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + + /* itemporary:in:put: */ + if (index < ((frameNumArgs = byteAt((localFP + FoxIFrameFlags) + 1)))) { + longAtput((localFP + FoxCallerSavedIP) + ((frameNumArgs - index) * BytesPerWord),longAt(localSP)); + } + else { + longAtput(((localFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - index) * BytesPerWord),longAt(localSP)); + } + } + BREAK; + CASE(505) // 249 extPushFullClosureBytecode + { + sqInt byte; + sqInt compiledBlock; + sqInt compiledBlockLiteralIndex; + sqInt context; + sqInt header; + sqInt i; + int ignoreContext; + usqInt newClosure; + usqInt newObj; + usqInt numArgs; + usqInt numBytes; + sqInt numCopied; + sqInt numCopiedSqInt; + sqInt numSlots; + int receiverIsOnStack; + sqInt startIndex; + + VM_LABEL(extPushFullClosureBytecode); + compiledBlockLiteralIndex = (byteAt(++localIP)) + ((((usqInt)(extA) << 8))); + extA = 0; + + /* begin followObjLiteral:ofMethod: */ + /* begin followObjField:ofObject: */ + compiledBlock = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)((compiledBlockLiteralIndex + LiteralStart)) << (shiftForWord())))))); + assert(isNonImmediate(compiledBlock)); + if ((!((longAt((void *)(compiledBlock))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + compiledBlock = fixFollowedFieldofObjectwithInitialValue(compiledBlockLiteralIndex + LiteralStart, GIV(method), compiledBlock); + } + assert(isOopCompiledMethod(compiledBlock)); + + /* begin argumentCountOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(compiledBlock)); + header = longAt((void *)((compiledBlock + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numArgs = (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; + byte = byteAt(++localIP); + numCopied = byte & (0x3F); + receiverIsOnStack = ((byte & (128)) != 0); + ignoreContext = ((byte & (64)) != 0); + + /* begin pushFullClosureNumArgs:copiedValues:compiledBlock:receiverIsOnStack:ignoreContext: */ + if (ignoreContext) { + context = GIV(nilObj); + } + else { + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + context = longAt(localFP + FoxThisContext); + goto l112; + } + context = marryFrameSP(localFP, localSP + (numCopied * BytesPerOop)); + /* end ensureFrameIsMarried:SP: */ +l112:; + } + + /* begin fullClosureIn:numArgs:numCopiedValues:compiledBlock: */ + numSlots = FullClosureFirstCopiedValueIndex + numCopied; + assert((numSlots >= 0) + && ((knownClassAtIndex(ClassFullBlockClosureCompactIndex)) != GIV(nilObj))); + assert((indexablePointersFormat()) == (instSpecOfClass(knownClassAtIndex(ClassFullBlockClosureCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newClosure = 0; + goto l113; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((indexablePointersFormat())) << (formatShift()))))) + ClassFullBlockClosureCompactIndex); + GIV(freeStart) += numBytes; + newClosure = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l113: + + /* Assume: have just allocated a new closure; it must be young. Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureOuterContextIndex, newClosure, context)); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord()))))),context); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(FullClosureCompiledBlockIndex, newClosure, compiledBlock)); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(FullClosureCompiledBlockIndex) << (shiftForWord()))))),compiledBlock); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureNumArgsIndex, newClosure, ((numArgs << 3) | 1))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord()))))),((numArgs << 3) | 1)); + if (receiverIsOnStack) { + startIndex = FullClosureReceiverIndex; + numCopiedSqInt = numCopied + 1; + } + else { + startIndex = FullClosureFirstCopiedValueIndex; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(FullClosureReceiverIndex, newClosure, longAt(localFP + FoxIFReceiver))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(FullClosureReceiverIndex) << (shiftForWord()))))),longAt(localFP + FoxIFReceiver)); + numCopiedSqInt = numCopied; + } + if (numCopiedSqInt > 0) { + for (i = 0; i < numCopiedSqInt; i += 1) { + /* Assume: have just allocated a new BlockClosure; it must be young. + Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(i + startIndex, newClosure, longAt((void *)(localSP + (((numCopiedSqInt - i) - 1) * BytesPerOop))))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)((i + startIndex)) << (shiftForWord()))))),longAt((void *)(localSP + (((numCopiedSqInt - i) - 1) * BytesPerOop)))); + } + + /* begin internalPop: */ + localSP += numCopiedSqInt * BytesPerOop; + } + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),newClosure); + } + BREAK; + CASE(506) // 250 extPushClosureBytecode + { + sqInt blockSize; + sqInt byte; + sqInt context; + sqInt i; + sqInt initialIP; + usqInt newClosure; + usqInt newObj; + usqInt numArgs; + usqInt numBytes; + usqInt numCopied; + sqInt numSlots; + + VM_LABEL(extPushClosureBytecode); + byte = byteAt(++localIP); + numArgs = (byte & 7) + ((extA % 16) * 8); + numCopied = ((((usqInt)(byte)) >> 3) & 7) + ((extA / 16) * 8); + extA = 0; + blockSize = (byteAt(++localIP)) + ((((usqInt)(extB) << 8))); + numExtB = (extB = 0); + + /* begin pushClosureNumArgs:copiedValues:blockSize: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(localFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(localFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((localFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(localFP))); + context = longAt(localFP + FoxThisContext); + goto l114; + } + context = marryFrameSP(localFP, localSP + (numCopied * BytesPerOop)); + /* end ensureFrameIsMarried:SP: */ +l114: + initialIP = ((((usqInt)localIP)) + 2) - (GIV(method) + BaseHeaderSize); + + /* begin closureIn:numArgs:instructionPointer:numCopiedValues: */ + numSlots = ClosureFirstCopiedValueIndex + numCopied; + assert((numSlots >= 0) + && ((knownClassAtIndex(ClassBlockClosureCompactIndex)) != GIV(nilObj))); + assert((indexablePointersFormat()) == (instSpecOfClass(knownClassAtIndex(ClassBlockClosureCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newClosure = 0; + goto l115; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((indexablePointersFormat())) << (formatShift()))))) + ClassBlockClosureCompactIndex); + GIV(freeStart) += numBytes; + newClosure = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l115: + + /* Assume: have just allocated a new closure; it must be young. Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureOuterContextIndex, newClosure, context)); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord()))))),context); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureStartPCIndex, newClosure, (((usqInt)initialIP << 3) | 1))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))),(((usqInt)initialIP << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(ClosureNumArgsIndex, newClosure, ((numArgs << 3) | 1))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord()))))),((numArgs << 3) | 1)); + if (numCopied > 0) { + for (i = 0; i < numCopied; i += 1) { + /* Assume: have just allocated a new BlockClosure; it must be young. + Thus, can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newClosure)) + && (!(isForwarded(newClosure)))); + assert(validStorePointerUncheckedArgs(i + ClosureFirstCopiedValueIndex, newClosure, longAt((void *)(localSP + (((numCopied - i) - 1) * BytesPerOop))))); + longAtput((void *)((newClosure + BaseHeaderSize) + ((((usqInt)((i + ClosureFirstCopiedValueIndex)) << (shiftForWord()))))),longAt((void *)(localSP + (((numCopied - i) - 1) * BytesPerOop)))); + } + + /* begin internalPop: */ + localSP += numCopied * BytesPerOop; + } + localIP += blockSize; + + /* begin fetchNextBytecode */ + currentBytecode = (byteAt(++localIP)) + GIV(bytecodeSetSelector); + longAtput((localSP -= BytesPerOop),newClosure); + } + BREAK; + } + } + + /* undo the pre-increment of IP (in fetchByte in fetchNextBytecode) before returning */ + localIP -= 1; + assert((((usqInt)localIP)) != (ceReturnToInterpreterPC())); + GIV(instructionPointer) = ((usqInt)localIP); + GIV(stackPointer) = localSP; + GIV(framePointer) = localFP; + return null; +} + + /* CogBlockMethod>>#cmHomeMethod */ +static NoDbgRegParms CogMethod * +cmHomeMethod(CogBlockMethod *self_in_CogBlockMethod) +{ + return (((self_in_CogBlockMethod->cmType)) >= CMMethod + ? ((CogMethod *) self_in_CogBlockMethod) + : (assert(CMBlock == (self_in_CogBlockMethod->cmType)), + ((CogMethod *) ((((usqInt)self_in_CogBlockMethod)) - ((self_in_CogBlockMethod->homeOffset)))))); +} + + /* CogBlockMethod>>#isCMBlock */ +static NoDbgRegParms int +isCMBlock(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMBlock; +} + + /* CogBlockMethod>>#isCMClosedPIC */ +static NoDbgRegParms int +isCMClosedPIC(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMClosedPIC; +} + + /* CogBlockMethod>>#isCMMethodEtAl */ +static NoDbgRegParms int +isCMMethodEtAl(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) >= CMMethod; +} + + /* CogBlockMethod>>#isCMMethodFlaggedForBecome */ +static NoDbgRegParms int +isCMMethodFlaggedForBecome(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMMethodFlaggedForBecome; +} + + /* CogBlockMethod>>#isCMOpenPIC */ +static NoDbgRegParms int +isCMOpenPIC(CogBlockMethod *self_in_CogBlockMethod) +{ + return ((self_in_CogBlockMethod->cmType)) == CMOpenPIC; +} + + +/* For assert-checking */ + + /* CogStackPage>>#addressIsInPage: */ +static NoDbgRegParms sqInt +addressIsInPage(StackPage *self_in_CogStackPage, char *address) +{ + return (((self_in_CogStackPage->lastAddress)) < address) + && (address < ((self_in_CogStackPage->baseAddress))); +} + + /* CogStackPage>>#isFree */ +static NoDbgRegParms int +isFree(StackPage *self_in_CogStackPage) +{ + return ((self_in_CogStackPage->baseFP)) == 0; +} + + +/* */ +/* MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#freeStackPageNoAssert: */ +static NoDbgRegParms sqInt +freeStackPageNoAssert(StackPage *aPage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *prev; + StackPage *self_in_CogStackPage; + + (aPage->baseFP = 0); + if (aPage == GIV(mostRecentlyUsedPage)) { + GIV(mostRecentlyUsedPage) = (GIV(mostRecentlyUsedPage)->prevPage); + return null; + } + self_in_CogStackPage = (prev = (aPage->prevPage)); + if (!((self_in_CogStackPage->baseFP))) { + return null; + } + (prev->nextPage = (aPage->nextPage)); + (((aPage->nextPage))->prevPage = prev); + (aPage->nextPage = (GIV(mostRecentlyUsedPage)->nextPage)); + (((GIV(mostRecentlyUsedPage)->nextPage))->prevPage = aPage); + (aPage->prevPage = GIV(mostRecentlyUsedPage)); + (GIV(mostRecentlyUsedPage)->nextPage = aPage); + return 0; +} + + +/* */ +/* MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#freeStackPage: */ +static NoDbgRegParms void +freeStackPage(StackPage *aPage) +{ + freeStackPageNoAssert(aPage); + assert(pageListIsWellFormed()); +} + + +/* */ +/* MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#markStackPageMostRecentlyUsed: */ +static NoDbgRegParms sqInt +markStackPageMostRecentlyUsed(StackPage *page) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (page == GIV(mostRecentlyUsedPage)) { + return null; + } + + /* Common case; making new page most recently used. */ + if (((page->prevPage)) == GIV(mostRecentlyUsedPage)) { + GIV(mostRecentlyUsedPage) = page; + assert(pageListIsWellFormed()); + return null; + } + (((page->prevPage))->nextPage = (page->nextPage)); + (((page->nextPage))->prevPage = (page->prevPage)); + (((GIV(mostRecentlyUsedPage)->nextPage))->prevPage = page); + (page->prevPage = GIV(mostRecentlyUsedPage)); + (page->nextPage = (GIV(mostRecentlyUsedPage)->nextPage)); + (GIV(mostRecentlyUsedPage)->nextPage = page); + GIV(mostRecentlyUsedPage) = page; + assert(pageListIsWellFormed()); + return 0; +} + + +/* */ +/* This method is used to move a page to a position in the list such that it + cannot be deallocated when a new page is allocated, without changing the + most recently + used page. There must be at least 3 pages in the system. So making the + page the MRU's prevPage is sufficient to ensure it won't be deallocated. */ +/* MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#markStackPageNextMostRecentlyUsed: */ +static NoDbgRegParms sqInt +markStackPageNextMostRecentlyUsed(StackPage *page) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(page != GIV(mostRecentlyUsedPage)); + if (((page->nextPage)) == GIV(mostRecentlyUsedPage)) { + return null; + } + (((page->prevPage))->nextPage = (page->nextPage)); + (((page->nextPage))->prevPage = (page->prevPage)); + (((GIV(mostRecentlyUsedPage)->prevPage))->nextPage = page); + (page->prevPage = (GIV(mostRecentlyUsedPage)->prevPage)); + (page->nextPage = GIV(mostRecentlyUsedPage)); + (GIV(mostRecentlyUsedPage)->prevPage = page); + assert(pageListIsWellFormed()); + return 0; +} + + +/* MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#newStackPage */ +static StackPage * +newStackPage(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *lruOrFree; + + lruOrFree = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((lruOrFree->baseFP))) { + return lruOrFree; + } + divorceFramesIn(lruOrFree); + return lruOrFree; +} + + +/* Answer if the stack page list is well-formed. + MRUP-->used page<->used page<->used page<->used page<--LRUP + ^ <-next-prev-> ^ + | | + v <-prev-next-> v + free page<->free page<->free page<->free page */ + + /* CogStackPages>>#pageListIsWellFormed */ +static sqInt +pageListIsWellFormed(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt count; + sqInt limit; + sqInt ok; + StackPage *page; + + ok = 1; + page = (GIV(mostRecentlyUsedPage)->nextPage); + count = 1; + limit = GIV(numStackPages) * 2; + while ((((page->baseFP)) == 0) + && ((page != GIV(mostRecentlyUsedPage)) + && (count <= limit))) { + if (!(asserta(((((page->nextPage))->prevPage)) == page))) { + ok = 0; + } + page = (page->nextPage); + count += 1; + } + while ((page != GIV(mostRecentlyUsedPage)) + && (count <= limit)) { + if (!(asserta(((((page->nextPage))->prevPage)) == page))) { + ok = 0; + } + if (asserta(!(isFree(page)))) { + if (!(asserta((addressIsInPage(page, (page->baseFP))) + && (addressIsInPage(page, (page->headSP)))))) { + ok = 0; + } + } + else { + ok = 0; + } + page = (page->nextPage); + count += 1; + } + if (!(asserta(count == (numStkPages())))) { + ok = 0; + } + return ok; +} + + +/* Answer the page for a page index. + N.B. This is a zero-relative index. */ + + /* CogStackPages>>#stackPageAt: */ +static NoDbgRegParms StackPage * +stackPageAt(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return stackPageAtpages(index, GIV(pages)); +} + + +/* */ + + /* CogStackPages>>#stackPageFor: */ +static NoDbgRegParms StackPage * +stackPageFor(void *pointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return stackPageAtpages(pageIndexFor(pointer), GIV(pages)); +} + + /* CogStackPages>>#statAverageLivePagesWhenMapping */ +static double +statAverageLivePagesWhenMapping(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (GIV(statNumMaps) + ? (((double) GIV(statPageCountWhenMappingSum) )) / GIV(statNumMaps) + : 0.0); +} + + +/* Initialization is completed by initializing the stack zone on entry to the + interpreter. Answer when this has been done. This allows e.g. the crash + dump processing to + be avoided until the system has been initialized, easing debugging of new + ports as they fail in early system initialization attempts. */ + + /* CogStackPages>>#vmIsInitialized */ +int +vmIsInitialized(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return !!GIV(pages); +} + + /* CogThreadManager>>#affinedThreadOrNilForProcess:currentAffinity:oop: */ +static NoDbgRegParms CogVMThread * +affinedThreadOrNilForProcesscurrentAffinityoop(sqInt aProcess, sqInt currentAffinity, sqInt threadAffinityOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + CogVMThread *vmThread; + + if (currentAffinity > 0) { + if ((((currentAffinity >= 1) && (currentAffinity <= GIV(numThreads)))) + && (aProcess == (((vmThread = vmThreadAt(currentAffinity))->boundProcess)))) { + assert(((vmThread->affinityOnBind)) == threadAffinityOop); + return vmThread; + } + return null; + } + for (i = 1; i <= GIV(numThreads); i += 1) { + if ((((vmThread = vmThreadAt(i))->boundProcess)) == aProcess) { + assert(((vmThread->affinityOnBind)) == threadAffinityOop); + return vmThread; + } + } + return null; +} + + /* CogThreadManager>>#currentVMThread */ +static CogVMThread * +currentVMThread(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + + index = atomic_load((&GIV(vmOwner))); + + /* begin vmThreadAt: */ + assert(((index >= 0) && (index <= GIV(numThreads)))); + return (index > 0 + ? GIV(threads)[index] + : 0); +} + + +/* Useful for debugging!! */ + + /* CogThreadManager>>#dumpOwnerLog */ +void +dumpOwnerLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + dumpOwnerLogOn(GIV(transcript)); +} + + +/* Useful for debugging!! */ + + /* CogThreadManager>>#dumpOwnerLogMax: */ +void +dumpOwnerLogMax(sqInt maxEntries) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + dumpOwnerLogOnMax(GIV(transcript), maxEntries); +} + + +/* Useful for debugging!! */ + + /* CogThreadManager>>#dumpOwnerLogOn: */ +void +dumpOwnerLogOn(FILE *aFile) +{ + dumpOwnerLogOnMax(aFile, OwnerLogSize); +} + + +/* Print the last at most maxElements entires of the owner log. + Useful for debugging!! */ + + /* CogThreadManager>>#dumpOwnerLogOn:Max: */ +void +dumpOwnerLogOnMax(FILE *aFile, sqInt maxEntries) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogVMOwnerLog *entry; + sqInt i; + sqInt index; + sqInt limit; + sqInt numWrappedToPrint; + + if (GIV(ownerLog)) { + index = atomic_load((&GIV(ownerLogIndex))); + if ((index == 0) + && ((((GIV(ownerLog)[index]).timestamp)) == 0)) { + fprintf(aFile, "owner log is empty\n"); + return; + } + assert(((index >= 0) && (index <= (OwnerLogSize - 1)))); + if (GIV(ownerLogWrapped)) { + numWrappedToPrint = (((maxEntries - (index + 1)) < 0) ? 0 : (maxEntries - (index + 1))); + limit = (OwnerLogSize - 1) - numWrappedToPrint; + for (i = (index + 1); i < OwnerLogSize; i += 1) { + if (i > limit) { + entry = (&(GIV(ownerLog)[i])); + fprintf(aFile, + "ts: %llu %d %s\n", + ((entry->timestamp)) - GIV(startTimestamp), + (entry->vmOwner), + ((entry->successfulSwitch) + ? "ok" + : "-")); + } + } + } + else { + numWrappedToPrint = 0; + } + limit = (index - maxEntries) - numWrappedToPrint; + for (i = 0; i <= index; i += 1) { + if (i >= limit) { + entry = (&(GIV(ownerLog)[i])); + fprintf(aFile, + "ts: %llu %d %s\n", + ((entry->timestamp)) - GIV(startTimestamp), + (entry->vmOwner), + ((entry->successfulSwitch) + ? "ok" + : "-")); + } + } + } + fprintf(aFile, + "vmOwner: %d\n", + GIV(vmOwner)); +} + + /* CogThreadManager>>#ensureWillingThread */ +static CogVMThread * +ensureWillingThread(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt indexSqInt; + sqInt newIndex; + CogVMThread *willingThread; + + willingThread = willingVMThread(); + if (willingThread) { + return willingThread; + } + for (index = 1; index <= GIV(numThreads); index += 1) { + if (!(vmThreadState(vmThreadAt(index)))) { + /* begin startThreadForThreadIndex: */ + assert(index > 0); + if (index > GIV(numThreads)) { + if (!(growThreadInfosToAtLeast(index))) { + goto l1; + } + } + startThreadForThreadInfo(vmThreadAt(index)); + /* end startThreadForThreadIndex: */ +l1: + + /* begin vmThreadAt: */ + assert(((index >= 0) && (index <= GIV(numThreads)))); + return (index > 0 + ? GIV(threads)[index] + : 0); + } + } + indexSqInt = (newIndex = GIV(numThreads) + 1); + + /* begin startThreadForThreadIndex: */ + assert(indexSqInt > 0); + if (indexSqInt > GIV(numThreads)) { + if (!(growThreadInfosToAtLeast(indexSqInt))) { + goto l2; + } + } + startThreadForThreadInfo(vmThreadAt(indexSqInt)); + /* end startThreadForThreadIndex: */ +l2: + + /* begin vmThreadAt: */ + assert(((newIndex >= 0) && (newIndex <= GIV(numThreads)))); + return (newIndex > 0 + ? GIV(threads)[newIndex] + : 0); +} + + +/* This is for signalSemaphoreWithIndex in Cross/vm/sqExternalSemaphores.c */ + + /* CogThreadManager>>#getVMOSThread */ +sqOSThread +getVMOSThread(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(vmOSThread); +} + + /* CogThreadManager>>#getVMOwner */ +static sqInt +getVMOwner(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return atomic_load((&GIV(vmOwner))); +} + + +/* Grow the thread infos to at least index in numThreadsIncrement quanta. */ + + /* CogThreadManager>>#growThreadInfosToAtLeast: */ +static NoDbgRegParms sqInt +growThreadInfosToAtLeast(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt j; + sqInt newNumThreads; + CogVMThread **newThreads; + sqInt nThreads; + CogVMThread *vmThread; + CogVMThread *vmThreads; + + if (GIV(memoryIsScarce)) { + return 0; + } + newNumThreads = (((index + GIV(numThreadsIncrement)) - 1) / GIV(numThreadsIncrement)) * GIV(numThreadsIncrement); + if (newNumThreads >= ((1U << ThreadIdShift) - 1)) { + return 0; + } + + /* Since 0 is not a valid index we allocate one extra CogVMThread and use 1-relative indices. */ + newThreads = realloc(GIV(threads), (newNumThreads + 1) * sizeof(CogVMThread *)); + if (!newThreads) { + GIV(memoryIsScarce) = 1; + return 0; + } + GIV(threads) = newThreads; + + /* begin populate:from:to: */ + nThreads = (newNumThreads - (GIV(numThreads) + 1)) + 1; + vmThreads = calloc(nThreads, sizeof(CogVMThread)); + if (!vmThreads) { + goto l2; + } + + /* Since 0 is not a valid index, in C we allocate one extra CogVMThread and use 1-relative indices. */ + if ((GIV(numThreads) + 1) == 1) { + newThreads[0] = null; + } + for (i = (GIV(numThreads) + 1); i <= newNumThreads; i += 1) { + vmThread = (&(vmThreads[i - (GIV(numThreads) + 1)])); + initializeThreadState(vmThread); + if (ioNewOSSemaphore((&((vmThread->osSemaphore))))) { + for (j = (GIV(numThreads) + 1); j < i; j += 1) { + vmThread = (&(vmThreads[j - (GIV(numThreads) + 1)])); + ioDestroyOSSemaphore((&((vmThread->osSemaphore)))); + } + free(vmThreads); + goto l2; + } + newThreads[i] = vmThread; + (vmThread->index = i); + } + GIV(numThreads) = newNumThreads; + return 1; + goto l1; +l2: + + /* Allocation of new threads may fail, even after the array has been moved. + If this is the case, simply do not increase the number of useable threads. + The old ones will still point to the right addresses, they'll just be in a new list + which technically has too much space, but that doesn't hurt anything. */ + GIV(memoryIsScarce) = 1; + return 0; +l1:; + return 0; +} + + /* CogThreadManager>>#saveOwnerSwitchTo:successful: */ +static NoDbgRegParms sqInt +saveOwnerSwitchTosuccessful(sqInt newOwner, sqInt aBoolean) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int currentIndex; + CogVMOwnerLog *logEntry; + int newIndex; + sqInt timestamp; + + timestamp = ioUTCMicrosecondsNow(); + currentIndex = atomic_load((&GIV(ownerLogIndex))); + do { + newIndex = (currentIndex + 1) % OwnerLogSize; + } while(!(atomic_compare_exchange_strong((&GIV(ownerLogIndex)), (¤tIndex), newIndex))); + if (newIndex < currentIndex) { + GIV(ownerLogWrapped) = 1; + } + logEntry = (&(GIV(ownerLog)[currentIndex])); + (logEntry->timestamp = timestamp); + (logEntry->successfulSwitch = aBoolean); + (logEntry->vmOwner = newOwner); + return 0; +} + + /* CogThreadManager>>#startThreadForThreadIndex: */ +static NoDbgRegParms sqInt +startThreadForThreadIndex(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(index > 0); + if (index > GIV(numThreads)) { + if (!(growThreadInfosToAtLeast(index))) { + return 0; + } + } + return startThreadForThreadInfo(vmThreadAt(index)); +} + + /* CogThreadManager>>#startThreadForThreadInfo: */ +static NoDbgRegParms sqInt +startThreadForThreadInfo(CogVMThread *vmThread) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentState; + + if (atomic_load((&((vmThread->state))))) { + return 1; + } + assert((vmThreadState(vmThread)) == CTMUninitialized); + + /* begin setVmThreadState: */ + currentState = atomic_load((&((vmThread->state)))); + if (currentState != CTMInitializing) { + switch (currentState) { + case CTMUninitialized: + break; + case CTMInitializing: + case CTMWantingOwnership: + assert(0 /* (CTMInitializing = CTMAssignableOrInVM) */); + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMInitializing = CTMUnavailable) */); + break; + case CTMUnavailable: + assert(0 /* (CTMInitializing = CTMWantingOwnership) */); + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((vmThread->state))), CTMInitializing); + + /* self cCode: '' + inSmalltalk: [coInterpreter transcript + cr; + nextPutAll: 'starting VM thread '; + print: vmThread index; + flush. + (thisContext home stackOfSize: 10) do: + [:ctxt| + coInterpreter transcript cr; print: ctxt; flush]]. */ + if (!(ioNewOSThread(((void (*)(void*)) startVMThread), vmThread))) { + ioTransferTimeslice(); + return 1; + } + GIV(memoryIsScarce) = 1; + + /* self cCode: [coInterpreter print: 'ERVT failed to spawn so memory is scarce'; cr] */ + return 0; +} + + +/* Initialize the threading subsystem, aborting if there is an error. */ + + /* CogThreadManager>>#startThreadSubsystem */ +static sqInt +startThreadSubsystem(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentState; + CogVMThread *vmThread; + + assert(GIV(threads) == null); + GIV(vmOSThread) = ioCurrentOSThread(); + GIV(numThreadsIncrement) = ((((((ioNumProcessors()) < 2) ? 2 : (ioNumProcessors()))) < 16) ? ((((ioNumProcessors()) < 2) ? 2 : (ioNumProcessors()))) : 16); + if (!(growThreadInfosToAtLeast(GIV(numThreadsIncrement) * 2))) { + error("no memory to start thread system"); + } + atomic_store((&GIV(vmOwner)), 1); + vmThread = GIV(threads)[atomic_load((&GIV(vmOwner)))]; + + /* begin setVmThreadState: */ + currentState = atomic_load((&((vmThread->state)))); + if (currentState != CTMInitializing) { + switch (currentState) { + case CTMUninitialized: + break; + case CTMInitializing: + case CTMWantingOwnership: + assert(0 /* (CTMInitializing = CTMAssignableOrInVM) */); + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMInitializing = CTMUnavailable) */); + break; + case CTMUnavailable: + assert(0 /* (CTMInitializing = CTMWantingOwnership) */); + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((vmThread->state))), CTMInitializing); + + /* begin registerVMThread: */ + assert((((vmThreadState(vmThread)) == CTMInitializing) + || ((vmThreadState(vmThread)) == CTMWantingOwnership)) + && (!((vmThread->osThread)))); + (vmThread->osThread = ioCurrentOSThread()); + ioThreadLocalSetThreadIndex((vmThread->index)); + assert((ioThreadLocalGetThreadIndex()) == ((vmThread->index))); + + /* begin setVmThreadState: */ + currentState = atomic_load((&((vmThread->state)))); + if (currentState != CTMAssignableOrInVM) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMAssignableOrInVM = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMAssignableOrInVM = CTMUnavailable) */); + break; + case CTMUnavailable: + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((vmThread->state))), CTMAssignableOrInVM); + + /* begin initializeOwnerLog */ + if (enableOwnerLog) { + if (!GIV(startTimestamp)) { + GIV(startTimestamp) = ioUTCMicrosecondsNow(); + } + GIV(ownerLog) = calloc(OwnerLogSize, sizeof(CogVMOwnerLog)); + atomic_store((&GIV(ownerLogIndex)), 0); + GIV(ownerLogWrapped) = 0; + } + return 0; +} + + +/* Start a VM thread that will attempt to acquire the VM and proceed + to run the VM, taking processes from the runnable process queue. */ + + /* CogThreadManager>>#startVMThread: */ +static NoDbgRegParms void +startVMThread(CogVMThread *vmThread) +{ + sqInt currentState; + + assert((vmThreadState(vmThread)) == CTMInitializing); + + /* begin registerVMThread: */ + assert((((vmThreadState(vmThread)) == CTMInitializing) + || ((vmThreadState(vmThread)) == CTMWantingOwnership)) + && (!((vmThread->osThread)))); + (vmThread->osThread = ioCurrentOSThread()); + ioThreadLocalSetThreadIndex((vmThread->index)); + assert((ioThreadLocalGetThreadIndex()) == ((vmThread->index))); + + /* begin setVmThreadState: */ + currentState = atomic_load((&((vmThread->state)))); + if (currentState != CTMAssignableOrInVM) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMAssignableOrInVM = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMAssignableOrInVM = CTMUnavailable) */); + break; + case CTMUnavailable: + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((vmThread->state))), CTMAssignableOrInVM); + threadSchedulingLoop(vmThread); +} + + +/* Test if threadAffinity is ok to run on a thread with the given index. */ + + /* CogThreadManager>>#threadIndex:isCompatibleWith: */ +static NoDbgRegParms sqInt +threadIndexisCompatibleWith(sqInt aThreadIndex, sqInt threadAffinity) +{ + assert(aThreadIndex > 0); + return (threadAffinity == 0) + || ((threadAffinity >= 0 + ? aThreadIndex == threadAffinity + : aThreadIndex != (-threadAffinity))); +} + + +/* Answer a pointer to an unused CogVMThread, growing the sequence if + necessary. + */ + + /* CogThreadManager>>#unusedThreadInfo */ +static CogVMThread * +unusedThreadInfo(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt index; + CogVMThread *vmThread; + + for (i = 1; i <= GIV(numThreads); i += 1) { + vmThread = GIV(threads)[i]; + assert(((vmThread->index)) == i); + if (!(atomic_load((&((vmThread->state)))))) { + return vmThread; + } + } + index = GIV(numThreads) + 1; + if (!(growThreadInfosToAtLeast(GIV(numThreads) + GIV(numThreadsIncrement)))) { + return null; + } + return GIV(threads)[index]; +} + + +/* Answer if the vm is owned */ +/* This is used in the platform support code */ + + /* CogThreadManager>>#vmIsOwned */ +sqInt +vmIsOwned(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (atomic_load((&GIV(vmOwner)))) > 0; +} + + +/* Answer if the vm is owned or is locked via CTMUnknownOwner. + Try to use this in asserts only. */ + + /* CogThreadManager>>#vmIsOwnedOrLocked */ +static int +vmIsOwnedOrLocked(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (atomic_load((&GIV(vmOwner)))) != 0; +} + + +/* Test if the vmOwner is index. */ + + /* CogThreadManager>>#vmOwnerIsCompatibleWith: */ +static NoDbgRegParms sqInt +vmOwnerIsCompatibleWith(sqInt processThreadId) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aThreadIndex; + + aThreadIndex = atomic_load((&GIV(vmOwner))); + + /* begin threadIndex:isCompatibleWith: */ + assert(aThreadIndex > 0); + return (processThreadId == 0) + || ((processThreadId >= 0 + ? aThreadIndex == processThreadId + : aThreadIndex != (-processThreadId))); +} + + +/* Test if the vmOwner is index. */ + + /* CogThreadManager>>#vmOwnerIs: */ +static NoDbgRegParms int +vmOwnerIs(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (atomic_load((&GIV(vmOwner)))) == index; +} + + /* CogThreadManager>>#vmThreadAt: */ +static NoDbgRegParms CogVMThread * +vmThreadAt(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(((index >= 0) && (index <= GIV(numThreads)))); + return (index > 0 + ? GIV(threads)[index] + : 0); +} + + /* CogThreadManager>>#wakeVMThread: */ +static NoDbgRegParms void +wakeVMThread(CogVMThread *vmThread) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt indexOrZero; + sqInt threadState; + + assert((vmIsOwnedOrLocked()) + && (!(vmOwnerIs((vmThread->index))))); + + /* Instead of going through a #disownVM: call, directly set the new VM owner. + This has the advantage of avoiding a race for the different threads to become the new + VM owner. + In Simulation, this means we need to simulate a thread-switch. */ + indexOrZero = (vmThread->index); + + /* begin setVMOwner: */ + assert(((getVMOwner()) == CTMUnknownOwner) + || ((getVMOwner()) == (ioThreadLocalGetThreadIndex()))); + assert((getVMOwner()) != indexOrZero); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(indexOrZero, 1); + } + + /* TODO: We could make this a `release` ordering, which may perform better on ARM. */ + atomic_store((&GIV(vmOwner)), indexOrZero); + threadState = atomic_load((&((vmThread->state)))); + if (threadState) { + assert(((threadState == CTMWantingOwnership) + || (threadState == CTMAssignableOrInVM)) + || (threadState == CTMInitializing)); + ioSignalOSSemaphore((&((vmThread->osSemaphore)))); + } + else { + if (!(startThreadForThreadInfo(vmThread))) { + /* begin releaseVM */ + /* begin setVMOwner: */ + assert(((getVMOwner()) == CTMUnknownOwner) + || ((getVMOwner()) == (ioThreadLocalGetThreadIndex()))); + assert((getVMOwner()) != 0); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(0, 1); + } + + /* TODO: We could make this a `release` ordering, which may perform better on ARM. */ + atomic_store((&GIV(vmOwner)), 0); + } + } + ioTransferTimeslice(); +} + + +/* Answer a pointer to a live CogVMThread in any of the ``will do VM work'' + states (other than the current owner if the VM is owned), or nil if none. + Preferentially answer threads wanting ownership. */ + + /* CogThreadManager>>#willingVMThread */ +static CogVMThread * +willingVMThread(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + CogVMThread *thread; + CogVMThread *threadWantingVM; + CogVMThread *threadWilling; + + threadWantingVM = (threadWilling = null); + for (i = 1; i <= GIV(numThreads); i += 1) { + if (!((atomic_load((&GIV(vmOwner)))) == i)) { + thread = GIV(threads)[i]; + if ((atomic_load((&((thread->state))))) == CTMWantingOwnership) { + if ((!threadWantingVM) + || (((threadWantingVM->priority)) < ((thread->priority)))) { + threadWantingVM = thread; + } + } + if ((atomic_load((&((thread->state))))) == CTMAssignableOrInVM) { + if ((!threadWilling) + || (((threadWilling->priority)) < ((thread->priority)))) { + threadWilling = thread; + } + } + } + } + return (!(threadWantingVM) + ? threadWilling + : threadWantingVM); +} + + +/* Unfortunately this cannot be inlined as Slang otherwise screws up the + generation of the `atomic_store` call. + */ +/* In comparision to #initialize, this is also called in C code to initialize + the VMThread, not just in the Smalltalk simulation. + */ + + /* CogVMThread>>#initializeThreadState */ +static NoDbgRegParms CogVMThread * +initializeThreadState(CogVMThread *self_in_CogVMThread) +{ + atomic_store((&((self_in_CogVMThread->state))), CTMUninitialized); + return self_in_CogVMThread; +} + + /* CogVMThread>>#vmThreadState */ +static NoDbgRegParms sqInt +vmThreadState(CogVMThread *self_in_CogVMThread) +{ + return atomic_load((&((self_in_CogVMThread->state)))); +} + + /* CoInterpreter>>#accessorDepthForPrimitiveIndex: */ +sqInt +accessorDepthForPrimitiveIndex(sqInt primIndex) +{ + return ((sqInt)((primitiveMetadataTable[primIndex]))) >> SpurPrimitiveAccessorDepthShift; +} + + +/* Activate newMethod when newMethod has been cogged, i.e. create a + machine-code frame and (re)enter machine-code. + */ + + /* CoInterpreter>>#activateNewCogMethod:inInterpreter: */ +static NoDbgRegParms void +activateNewCogMethodinInterpreter(CogMethod *cogMethod, sqInt inInterpreter) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int i; + sqInt methodHeader; + usqInt numTemps; + sqInt object; + sqInt rcvr; + char *sp; + sqInt switched; + + assert((rawHeaderOf(GIV(newMethod))) == (((sqInt)cogMethod))); + methodHeader = (cogMethod->methodHeader); + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (((cogMethod->cmNumArgs)) * BytesPerWord)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + if (!((cogMethod->stackCheckOffset))) { + /* frameless method; nothing to activate... */ + if ((numRegArgs()) > 0) { + if (((cogMethod->cmNumArgs)) <= (numRegArgs())) { + callRegisterArgCogMethodatreceiver(cogMethod, noCheckEntryOffset(), rcvr); + } + } + + /* dont use and: so as to get Slang to inline cogit numRegArgs > 0 */ + object = (((usqInt)cogMethod)) + (noCheckEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + ceCallCogCodePopReceiverReg(); + error("should not be reached"); + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),((usqInt)cogMethod)); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + for (i = (((cogMethod->cmNumArgs)) + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + if ((((methodHeader & AlternateHeaderHasPrimFlag) != 0)) + && (GIV(primFailCode) != 0)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeader); + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc). */ + if (GIV(stackPointer) >= GIV(stackLimit)) { + assert(((cogMethod->stackCheckOffset)) > (noCheckEntryOffset())); + object = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + ceEnterCogCodePopReceiverReg(); + error("should not be reached"); + } + GIV(instructionPointer) = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + switched = handleStackOverflowOrEventAllowContextSwitch(canContextSwitchIfActivatingheader(GIV(newMethod), methodHeader)); + returnToExecutivepostContextSwitch(inInterpreter, switched); +} + + /* CoInterpreter>>#activateNewMethod */ +static void +activateNewMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + usqInt i; + int inInterpreter; + sqInt methodHeader; + usqInt numArgs; + usqInt numTemps; + sqInt object; + sqInt rcvr; + sqInt switched; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeader)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(!(isOopForwarded(rcvr))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeader); + } + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc). */ + switched = 1; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(canContextSwitchIfActivatingheader(GIV(newMethod), methodHeader)); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); +} + + +/* Add the given entry to the method cache. + The policy is as follows: + Look for an empty entry anywhere in the reprobe chain. + If found, install the new entry there. + If not found, then install the new entry at the first probe position + and delete the entries in the rest of the reprobe chain. + This has two useful purposes: + If there is active contention over the first slot, the second + or third will likely be free for reentry after ejection. + Also, flushing is good when reprobe chains are getting full. */ +/* Override to refuse to cache other than compiled methods. + This protects open PICs against having to test for compiled methods. */ + + /* CoInterpreter>>#addNewMethodToCache: */ +static NoDbgRegParms void +addNewMethodToCache(sqInt classObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTag; + sqInt err; + usqInt firstBytecode; + sqInt hash; + int hashInt; + sqInt header; + sqInt methodHeader; + sqInt p; + sqInt primitiveIndex; + usqInt probe; + + if (!(/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + primitiveFunctionPointer = primitiveInvokeObjectAsMethod; + return; + } + + /* begin classTagForClass: */ + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classTag = ((hashInt = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hashInt + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + + /* begin methodCacheHashOf:with: */ + hash = GIV(messageSelector) ^ ((((usqInt)(classTag) << 2))); + if (/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) { + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]))); + } + else { + assert(!((isNonImmediate(GIV(newMethod))) + && (isForwarded(GIV(newMethod))))); + primitiveFunctionPointer = primitiveInvokeObjectAsMethod; + } + for (p = 0; p < CacheProbeMax; p += 1) { + probe = (((usqInt)(hash)) >> p) & MethodCacheMask; + if (!(GIV(methodCache)[probe + MethodCacheSelector])) { + /* Found an empty entry -- use it */ + GIV(methodCache)[probe + MethodCacheSelector] = GIV(messageSelector); + GIV(methodCache)[probe + MethodCacheClass] = (classTagForClass(classObj)); + GIV(methodCache)[probe + MethodCacheMethod] = GIV(newMethod); + GIV(methodCache)[probe + MethodCachePrimFunction] = (((sqIntptr_t) primitiveFunctionPointer)); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = probe; + return; + } + } + + /* OK, we failed to find an entry -- install at the first slot... */ + + /* first probe */ + probe = hash & MethodCacheMask; + GIV(methodCache)[probe + MethodCacheSelector] = GIV(messageSelector); + GIV(methodCache)[probe + MethodCacheClass] = (classTagForClass(classObj)); + GIV(methodCache)[probe + MethodCacheMethod] = GIV(newMethod); + GIV(methodCache)[probe + MethodCachePrimFunction] = (((sqIntptr_t) primitiveFunctionPointer)); + + /* this for primitiveExternalMethod */ + /* ...and zap the following entries */ + GIV(lastMethodCacheProbeWrite) = probe; + for (p = 1; p < CacheProbeMax; p += 1) { + probe = (((usqInt)(hash)) >> p) & MethodCacheMask; + GIV(methodCache)[probe + MethodCacheSelector] = 0; + } +} + + /* CoInterpreter>>#argumentCountAddress */ +usqInt +argumentCountAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(argumentCount)))); +} + + +/* Coerce either a CMMethod or a CMBlock to the home CMMethod */ + + /* CoInterpreter>>#asCogHomeMethod: */ +static NoDbgRegParms CogMethod * +asCogHomeMethod(CogBlockMethod *aCogMethod) +{ + return (((aCogMethod->cmType)) >= CMMethod + ? ((CogMethod *) aCogMethod) + : /* cmHomeMethod */ + (((aCogMethod->cmType)) >= CMMethod + ? ((CogMethod *) aCogMethod) + : (assert(CMBlock == (aCogMethod->cmType)), + ((CogMethod *) ((((usqInt)aCogMethod)) - ((aCogMethod->homeOffset))))))); +} + + /* CoInterpreter>>#assertValidExecutionPointe:r:s:imbar:line: */ +static NoDbgRegParms void +assertValidExecutionPointersimbarline(usqInt lip, char *lifp, char *lisp, sqInt inInterpreter, sqInt ln) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cogMethod; + sqInt methodField; + sqInt methodFieldSqInt; + usqInt theIP; + + assertl(GIV(stackPage) == (GIV(mostRecentlyUsedPage)), ln); + assertl(addressIsInPage(GIV(stackPage), lifp), ln); + assert(deferStackLimitSmashAroundwith(assertValidStackLimits, ln)); + assertl(lisp < lifp, ln); + assertl(lifp > lisp, ln); + assertl(lisp >= (((GIV(stackPage)->realStackLimit)) - (stackLimitOffset())), ln); + assertl(((lifp - lisp) / BytesPerOop) < LargeContextSlots, ln); + methodField = longAt(lifp + FoxMethod); + if (inInterpreter) { + assertl(!(isMachineCodeFrame(lifp)), ln); + assert(lisp <= (lifp + FoxIFReceiver)); + assertl(GIV(method) == methodField, ln); + assertl((methodUsesAlternateBytecodeSet(GIV(method))) == (GIV(bytecodeSetSelector) == 0x100), ln); + if (assertal(cheapAddressCouldBeInHeap(methodField), ln)) { + theIP = (lip == (ceReturnToInterpreterPC()) + ? ((usqInt)(longAt(lifp + FoxIFSavedIP))) + : lip); + assertl((theIP >= (methodField + (lastPointerOf(methodField)))) + && (theIP < (((methodField + (numBytesOfBytes(methodField))) + BaseHeaderSize) - 1)), ln); + } + assertl((iframeIsBlockActivation(lifp)) + || ((pushedReceiverOrClosureOfFrame(lifp)) == (iframeReceiver(lifp))), ln); + } + else { + assertl(isMachineCodeFrame(lifp), ln); + assert(lisp <= (lifp + FoxMFReceiver)); + if ((assertal((((usqInt)methodField)) >= (minCogMethodAddress()), ln)) + && (assertal((((usqInt)methodField)) < (maxCogMethodAddress()), ln))) { + /* begin mframeHomeMethod: */ + methodFieldSqInt = longAt(lifp + FoxMethod); + if (methodFieldSqInt & MFMethodFlagIsBlockFlag) { + cogMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodFieldSqInt & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodFieldSqInt & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodFieldSqInt & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodFieldSqInt & MFMethodMask))))) - (((((CogBlockMethod *) (methodFieldSqInt & MFMethodMask)))->homeOffset)))))); + goto l1; + } + cogMethod = ((CogMethod *) (methodFieldSqInt & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + assertl((lip > (methodField + ((mframeIsBlockActivation(lifp) + ? sizeof(CogBlockMethod) + : sizeof(CogMethod))))) + && (lip < (methodField + ((cogMethod->blockSize)))), ln); + } + assertl((mframeIsBlockActivation(lifp)) + || ((pushedReceiverOrClosureOfFrame(lifp)) == (mframeReceiver(lifp))), ln); + } + if (!(longAt(lifp + FoxSavedFP))) { + assertl(frameHasContext(lifp), ln); + assertl((frameContext(lifp)) == (longAt(((GIV(stackPage)->baseAddress)) - BytesPerWord)), ln); + } +} + + +/* Check that the stacked instruction pointers in the given page are correct. + Checks the interpreter sender/machine code callee contract. */ + + /* CoInterpreter>>#assertValidStackedInstructionPointersIn:line: */ +static NoDbgRegParms sqInt +assertValidStackedInstructionPointersInline(StackPage *aStackPage, sqInt ln) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt methodField; + usqInt methodObj; + sqInt prevFrameWasCogged; + char *theFP; + usqInt theIP; + CogMethod *theMethod; + + callerFP = ((char *) 0); + if (!(assertal(!(isFree(aStackPage)), ln))) { + return 0; + } + prevFrameWasCogged = 0; + + /* The top of stack of an inactive page is always the instructionPointer. + The top of stack of the active page may be the instructionPointer if it has been pushed, + which is indicated by a 0 instructionPointer. */ + if ((GIV(stackPage) == aStackPage) + && (GIV(instructionPointer) != 0)) { + theIP = GIV(instructionPointer); + theFP = GIV(framePointer); + } + else { + theIP = ((usqInt)(longAt((aStackPage->headSP)))); + theFP = (aStackPage->headFP); + if (GIV(stackPage) == aStackPage) { + assertl(GIV(framePointer) == theFP, ln); + } + } + while (1) { + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* begin mframeHomeMethod: */ + methodField = longAt(theFP + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + theMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + goto l1; + } + theMethod = ((CogMethod *) (methodField & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + assertl((theIP == (ceCannotResumePC())) + || ((theIP >= (((usqInt)theMethod))) + && (theIP < ((((usqInt)theMethod)) + ((theMethod->blockSize))))), ln); + prevFrameWasCogged = 1; + } + else { + methodObj = longAt(theFP + FoxMethod); + if (prevFrameWasCogged) { + assertl(theIP == (ceReturnToInterpreterPC()), ln); + } + if (theIP == (ceReturnToInterpreterPC())) { + theIP = ((usqInt)(longAt(theFP + FoxIFSavedIP))); + } + assertl((theIP >= (methodObj + (lastPointerOf(methodObj)))) + && (theIP < (((methodObj + (numBytesOfBytes(methodObj))) + BaseHeaderSize) - 1)), ln); + prevFrameWasCogged = 0; + } + + /* assert-check the interpreter frame. */ + theIP = ((usqInt)(longAt(theFP + FoxCallerSavedIP))); + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theFP = callerFP; + } + assertl(theIP == (ceBaseFrameReturnPC()), ln); + return 1; +} + + +/* Check that the stacked instruction pointers in all pages are correct. + Checks the interpreter sender/machine code callee contract. + Written so it will be optimized away if not in an assert VM. */ + + /* CoInterpreter>>#assertValidStackedInstructionPointers: */ +static NoDbgRegParms void +assertValidStackedInstructionPointers(sqInt ln) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + StackPage *thePage; + + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + assertl(assertValidStackedInstructionPointersInline(thePage, ln), ln); + } + } +} + + +/* Attempt to convert the current interpreted activation into a machine code + activation, and if this is popssible, jump into machine code. bcpc is the + 0-relative pc of the backward branch bytecode (not any preceding + extension). */ + + /* CoInterpreter>>#attemptToSwitchToMachineCode: */ +static NoDbgRegParms void +attemptToSwitchToMachineCode(sqInt bcpc) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt closure; + sqInt cls; + CogMethod *cogMethod; + CogBlockMethod *cogMethod1; + CogMethod *homeMethod; + sqInt methodField; + sqInt methodHeader; + char *p; + usqInt pc; + usqInt startBcpc; + char *theFP; + + if (!(methodHasCogMethod(GIV(method)))) { + if (((byteAt((GIV(framePointer) + FoxIFrameFlags) + 3)) != 0) + && (!(isVanillaBlockClosure((cls = frameStackedReceivernumArgs(GIV(framePointer), /* frameNumArgs: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(GIV(framePointer)))->cmNumArgs) + : byteAt((GIV(framePointer) + FoxIFrameFlags) + 1)))))))) { + /* Compiled block / full closure */ + cogFullBlockMethodnumCopied(GIV(method), (numPointerSlotsOf(cls)) - FullClosureFirstCopiedValueIndex); + } + else { + /* Compiled method */ + cogselector(GIV(method), GIV(nilObj)); + } + } + if (methodHasCogMethod(GIV(method))) { + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + + /* begin convertToMachineCodeFrame:bcpc: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + + /* Update the return pc, perhaps saving it in the caller's iframeSavedIP. */ + if (longAt(GIV(framePointer) + FoxSavedFP)) { + if (!((((usqInt)(longAt((((char *)(longAt(GIV(framePointer) + FoxSavedFP)))) + FoxMethod)))) < (startOfMemory()))) { + theFP = ((char *)(longAt(GIV(framePointer) + FoxSavedFP))); + + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(theFP))); + longAtput(theFP + FoxIFSavedIP,longAt(GIV(framePointer) + FoxCallerSavedIP)); + longAtput(GIV(framePointer) + FoxCallerSavedIP,ceReturnToInterpreterPC()); + } + } + else { + longAtput(GIV(framePointer) + FoxCallerSavedIP,ceBaseFrameReturnPC()); + } + + /* Compute the cog method field */ + if (byteAt((GIV(framePointer) + FoxIFrameFlags) + 3)) { + closure = longAt(GIV(framePointer) + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(GIV(framePointer)))->cmNumArgs) + : byteAt((GIV(framePointer) + FoxIFrameFlags) + 1)))) << (shiftForWord())))))); + + /* begin isVanillaBlockClosure: */ + assert((isPointers(closure)) + && ((numSlotsOf(closure)) >= ClosureFirstCopiedValueIndex)); + if (((((longAt((void *)((closure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1)) { + startBcpc = (((longAt((void *)((closure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))))) >> 3)) - 1; + cogMethod1 = findMethodForStartBcpcinHomeMethod(startBcpc, cogMethod); + } + else { + startBcpc = (((/* begin literalCountOfMethodHeader: */ + assert((((((cogMethod->methodHeader))) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((((cogMethod->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop; + cogMethod1 = ((CogBlockMethod *) cogMethod); + } + methodField = (((sqInt)cogMethod1)) + MFMethodFlagIsBlockFlag; + } + else { + startBcpc = (((/* begin literalCountOfMethodHeader: */ + assert((((((cogMethod->methodHeader))) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((((cogMethod->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop; + cogMethod1 = ((CogBlockMethod *) cogMethod); + methodField = ((sqInt)cogMethod); + } + + /* compute the pc before converting the frame to help with debugging. */ + pc = mcPCForBackwardBranchstartBcpcin(bcpc, startBcpc, cogMethod1); + assert(pc > ((((usqInt)cogMethod1)) + (noCheckEntryOffset()))); + assert(bcpc == (bytecodePCForstartBcpcin(pc, startBcpc, cogMethod1))); + + /* now convert to a machine code frame */ + longAtput(GIV(framePointer) + FoxMethod,methodField + ((byteAt((GIV(framePointer) + FoxIFrameFlags) + 2) + ? MFMethodFlagHasContextFlag + : 0))); + for (p = (GIV(framePointer) + FoxIFReceiver); p >= GIV(stackPointer); p += (-BytesPerWord)) { + longAtput((p + FoxMFReceiver) - FoxIFReceiver,longAt(p)); + } + GIV(stackPointer) = (GIV(stackPointer) + FoxMFReceiver) - FoxIFReceiver; + + /* begin assertValidMachineCodeFrame: */ + assert(isMachineCodeFrame(GIV(framePointer))); + cogMethod1 = ((CogBlockMethod *) ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodMask)); + homeMethod = /* asCogHomeMethod: */ + (((cogMethod1->cmType)) >= CMMethod + ? ((CogMethod *) cogMethod1) + : /* cmHomeMethod */ + (((cogMethod1->cmType)) >= CMMethod + ? ((CogMethod *) cogMethod1) + : (assert(CMBlock == (cogMethod1->cmType)), + ((CogMethod *) ((((usqInt)cogMethod1)) - ((cogMethod1->homeOffset))))))); + assert((methodFor(cogMethod1)) == homeMethod); + assert((pc > (((sqInt)cogMethod1))) + && (pc < ((((sqInt)homeMethod)) + ((homeMethod->blockSize))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),pc); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + ceEnterCogCodePopReceiverReg(); + } +} + + +/* Support for primitiveSuspend. + Assume suspendedContext is that of a process waiting on a condition + variable. Backup the PC of suspendedContext to the send that entered the + wait state. + primitiveEnterCriticalSection pushes false for blocked waiters. false must + be replaced by the condition variable. */ +/* The suspendedContext is in one of six states, hence six cases: + 1 & 2: single context with bytecode pc or single context with machine code + pc 3 & 4: married to machine code frame in embedded (v3) block or machine + code frame in method/full block + 5 & 6: married to interpreter frame with saved ip or interpreter frame + with pushed pc */ + + /* CoInterpreter>>#backupContext:toBlockingSendTo: */ +static NoDbgRegParms void +backupContexttoBlockingSendTo(sqInt suspendedContext, sqInt conditionVariable) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogBlockMethod *cogMethod; + sqInt mcpc; + sqInt pc; + sqInt senderOop; + sqInt sp; + usqInt startBcpc; + char *theFP; + sqInt theIP; + sqInt theMethod; + usqInt theNewIP; + StackPage *thePage; + + assert(isContext(suspendedContext)); + theMethod = longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* cases 1 & 2 */ + if ((!((longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))) & (tagMask())))) { + pc = longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + sp = longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + assert(((((pc) & 7) == 1)) + && (((pc >> 3)) != 0)); + assert(((((sp) & 7) == 1)) + && (((sp >> 3)) > 0)); + + /* case 2 */ + if (((pc = (pc >> 3))) < 0) { + pc = ((mustMapMachineCodePCcontext(pc, suspendedContext)) >> 3); + } + + /* context with machine code pc; must map... */ + theIP = ((theMethod + BaseHeaderSize) + pc) - 1; + theNewIP = pcPreviousToFunction(theIP, theMethod); + assert((theNewIP < theIP) + && ((theIP - theNewIP) <= 3)); + pc = ((theNewIP - theMethod) - BaseHeaderSize) + 1; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(suspendedContext)) + && (!(isForwarded(suspendedContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, suspendedContext, (((usqInt)pc << 3) | 1))); + longAtput((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),(((usqInt)pc << 3) | 1)); + + /* implicitly converts to 0 relative */ + sp = ((sp >> 3)) + ReceiverIndex; + assert(((fetchPointerofObject(sp, suspendedContext)) == (falseObject())) + || ((fetchPointerofObject(sp, suspendedContext)) == conditionVariable)); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(sp, suspendedContext, conditionVariable)); + assert(isNonImmediate(suspendedContext)); + if (oopisGreaterThanOrEqualTo(suspendedContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(conditionVariable & (tagMask())))) + && (oopisLessThan(conditionVariable, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(suspendedContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(suspendedContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(sp) << (shiftForWord()))))),conditionVariable); + return; + } + assert(isMarriedOrWidowedContext(suspendedContext)); + assert(!((isWidowedContextNoConvert(suspendedContext)))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(!((thePage == GIV(stackPage)))); + assert(theFP == ((thePage->headFP))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* a machine code pc... it must be converted... */ + mcpc = longAt((thePage->headSP)); + + /* begin mframeCogMethod: */ + cogMethod = ((CogBlockMethod *) ((longAt(theFP + FoxMethod)) & MFMethodMask)); + if (((cogMethod->cmType)) == CMBlock) { + assert((frameStackedReceivernumArgs(theFP, (cogMethod->cmNumArgs))) == (fetchPointerofObject(ClosureIndex, suspendedContext))); + + /* begin startPCOfClosure: */ + startBcpc = (((longAt((void *)(((longAt((void *)((suspendedContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))))) >> 3)) - 1; + } + else { + startBcpc = ((literalCountOf(theMethod)) + LiteralStart) * BytesPerOop; + } + theIP = bytecodePCForstartBcpcin(mcpc, startBcpc, cogMethod); + theIP = (theIP + theMethod) + BaseHeaderSize; + theNewIP = pcPreviousToFunction(theIP, theMethod); + assert((theNewIP < theIP) + && ((theIP - theNewIP) <= 3)); + convertFrametoInterpreterFrame(theFP, theIP - theNewIP); + } + else { + theIP = longAt((thePage->headSP)); + if (theIP == (ceReturnToInterpreterPC())) { + /* fetchByte uses pre-increment; must + 1 to point at correct bytecode... */ + theIP = (((usqInt)(longAt(theFP + FoxIFSavedIP)))) + 1; + theNewIP = pcPreviousToFunction(theIP, theMethod); + assert((theNewIP < theIP) + && ((theIP - theNewIP) <= 3)); + + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(theFP))); + longAtput(theFP + FoxIFSavedIP,theNewIP - 1); + } + else { + /* fetchByte uses pre-increment; must + 1 to point at correct bytecode... */ + theIP += 1; + assert(validInstructionPointerinMethodframePointer(theIP, theMethod, theFP)); + theNewIP = pcPreviousToFunction(theIP, theMethod); + assert((theNewIP < theIP) + && ((theIP - theNewIP) <= 3)); + longAtput((thePage->headSP),theNewIP - 1); + } + } + assert(((longAt(((thePage->headSP)) + BytesPerWord)) == (falseObject())) + || ((longAt(((thePage->headSP)) + BytesPerWord)) == conditionVariable)); + longAtput(((thePage->headSP)) + BytesPerWord,conditionVariable); +} + + /* CoInterpreter>>#callForCogCompiledCodeCompaction */ +void +callForCogCompiledCodeCompaction(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(cogCompiledCodeCompactionCalledFor) = 1; + forceInterruptCheck(); +} + + +/* convert + rcvr base + arg(s) + retpc <- sp + to + retpc base + entrypc + rcvr + arg(s) <- sp + and then enter at either the checked or the unchecked entry-point. */ + + /* CoInterpreter>>#callRegisterArgCogMethod:at:receiver: */ +static NoDbgRegParms void +callRegisterArgCogMethodatreceiver(CogMethod *cogMethod, sqInt entryOffset, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + char *sp; + + assert(((numRegArgs()) > 0) + && (((numRegArgs()) <= 2) + && (((cogMethod->cmNumArgs)) <= (numRegArgs())))); + if (((cogMethod->cmNumArgs)) == 2) { + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (3 * BytesPerWord),longAt(GIV(stackPointer))); + object = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (1 * BytesPerWord),longAt(GIV(stackPointer) + (3 * BytesPerWord))); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (2 * BytesPerWord),rcvr); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (3 * BytesPerWord),(((sqInt)cogMethod)) + entryOffset); + ceCallCogCodePopReceiverArg1Arg0Regs(); + } + if (((cogMethod->cmNumArgs)) == 1) { + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (2 * BytesPerWord),longAt(GIV(stackPointer))); + object = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (1 * BytesPerWord),rcvr); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (2 * BytesPerWord),(((sqInt)cogMethod)) + entryOffset); + ceCallCogCodePopReceiverArg0Regs(); + } + assert(((cogMethod->cmNumArgs)) == 0); + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (1 * BytesPerWord),longAt(GIV(stackPointer))); + + /* stackValue:put: */ + longAtput(GIV(stackPointer),(((sqInt)cogMethod)) + entryOffset); + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),rcvr); + GIV(stackPointer) = sp; + ceCallCogCodePopReceiverReg(); +} + + +/* An external call or FFI primitive has failed. Build the frame and + activate as appropriate. Enter either the interpreter or machine + code depending on whether aPrimitiveMethod has been or is still + cogged. Note that we could always interpret but want the efficiency + of executing machine code if it is available. */ + + /* CoInterpreter>>#ceActivateFailingPrimitiveMethod: */ +void +ceActivateFailingPrimitiveMethod(sqInt aPrimitiveMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt methodHeader; + sqInt result; + + assert(GIV(primFailCode) != 0); + assert(GIV(newMethod) == aPrimitiveMethod); + + /* If we're on Spur, retry the primitive, if appropriate, returning if successful after retry. */ + + /* ugh; annoying. Most users want the simulated address for the primitive (e.g. compilation in the Cogit). + But here we want the object for the primitive. So we choose to side-step the simulator's implementation + rather than change the other clients to do the mapFunctionToAddress: the simulator's version does. */ + primitiveFunctionPointer = functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(aPrimitiveMethod, (primitiveIndexOf(aPrimitiveMethod)), null); + retryPrimitiveOnFailure(); + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + if (!GIV(primFailCode)) { + result = longAt(GIV(stackPointer)); + assert(isPCWithinMethodZone(GIV(instructionPointer))); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(instructionPointer)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),result); + ceEnterCogCodePopReceiverReg(); + } + methodHeader = longAt((void *)((aPrimitiveMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + activateNewCogMethodinInterpreter(((CogMethod *) methodHeader), 0); + } + else { + activateNewMethod(); + } +} + + +/* Return across a page boundary. The context to return to (which may be + married) is stored in the first word of the stack. We get here when a + return instruction jumps + to the ceBaseFrameReturn: address that is the return pc for base frames. A + consequence of this is that the current frame is no longer valid since an + interrupt may have overwritten + its state as soon as the stack pointer has been cut-back beyond the return + pc. So to have + a context to send the cannotReturn: message to we also store the base + frame's context + in the second word of the stack page. */ + + /* CoInterpreter>>#ceBaseFrameReturn: */ +sqInt +ceBaseFrameReturn(sqInt returnValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + char *callerFP; + sqInt contextToReturnFrom; + sqInt contextToReturnTo; + char *fp; + char *frameAbove; + sqInt header; + sqInt isAContext; + sqInt methodHeader; + StackPage *newPage; + sqInt senderOop; + char *sp; + StackPage *thePage; + + assert((stackPageFor(GIV(stackPointer))) == GIV(stackPage)); + assert((GIV(mostRecentlyUsedPage)) == GIV(stackPage)); + assertCStackWellAligned(); + assert(GIV(framePointer) == 0); + assert(GIV(stackPointer) <= (((GIV(stackPage)->baseAddress)) - BytesPerWord)); + assert((((GIV(stackPage)->baseFP)) + (2 * BytesPerWord)) < ((GIV(stackPage)->baseAddress))); + + /* We would like to use the following assert but we can't since the stack pointer will be above the + base frame pointer in the base frame return and hence the 0 a base frame pointer points at could + be overwritten which will cause the isBaseFrame assert in frameCallerContext: to fail. + self assert: (self frameCallerContext: stackPage baseFP) = (stackPages longAt: stackPage baseAddress). */ + assert((addressCouldBeObj(longAt(((GIV(stackPage)->baseAddress)) - BytesPerWord))) + && (isContext(longAt(((GIV(stackPage)->baseAddress)) - BytesPerWord)))); + contextToReturnTo = longAt((GIV(stackPage)->baseAddress)); + assert(addressCouldBeObj(contextToReturnTo)); + + /* The stack page is effectively free now, so free it. We must free it to be + correct in determining if contextToReturnTo is still married, and in case + makeBaseFrameFor: cogs a method, which may cause a code compaction, + in which case the frame must be free to avoid the relocation machinery + tracing the dead frame. Since freeing now temporarily violates the page-list + ordering invariant, use the assert-free version. */ + freeStackPageNoAssert(GIV(stackPage)); + isAContext = /* isContext: */ + ((!(contextToReturnTo & (tagMask())))) + && (((longAt((void *)(contextToReturnTo))) & (classIndexMask())) == ClassMethodContextCompactIndex); + if (isAContext + && (/* isStillMarriedContext: */ + (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(contextToReturnTo))))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + GIV(framePointer) = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(GIV(framePointer)), GIV(pages)); + if (GIV(framePointer) == ((thePage->headFP))) { + GIV(stackPointer) = (thePage->headSP); + } + else { + /* Returning to some interior frame, presumably because of a sender assignment. + Move the frames above to another page (they may be in use, e.g. via coroutining). + Make the interior frame the top frame. */ + + /* begin findFrameAbove:inPage: */ + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == GIV(framePointer)) { + frameAbove = 0; + goto l1; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == GIV(framePointer)) { + frameAbove = fp; + goto l1; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + frameAbove = 0; + /* end findFrameAbove:inPage: */ +l1: + + /* Since we've just deallocated a page we know that newStackPage won't deallocate an existing one. */ + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l2; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l2: + assert(newPage == GIV(stackPage)); + moveFramesInthroughtoPage(thePage, frameAbove, newPage); + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + } + } + else { + if (!(isAContext + && (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1)))) { + contextToReturnFrom = longAt(((GIV(stackPage)->baseAddress)) - BytesPerWord); + tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFromtoreturnValue(contextToReturnFrom, contextToReturnTo, returnValue); + + /* begin externalCannotReturn:from: */ + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),contextToReturnFrom); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + + /* Both ceBaseFrameReturnTrampoline & ceNonLocalReturnTrampoline pop + their caller's return pc into instructionPointer. In this uncommon case restore + it, since a send's call pushes the instructionPointer (after the arguments). */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), contextToReturnFrom, 1); + } + + /* void the instructionPointer to stop it being incorrectly updated in a code + compaction in makeBaseFrameFor:. */ + GIV(instructionPointer) = 0; + thePage = makeBaseFrameFor(contextToReturnTo); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + } + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + assert((stackPageFor(GIV(framePointer))) == GIV(stackPage)); + if ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())) { + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + ceEnterCogCodePopReceiverReg(); + } + GIV(instructionPointer) = longAt(GIV(stackPointer)); + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* stackTopPut: */ + longAtput(GIV(stackPointer),returnValue); + assert(checkIsStillMarriedContextcurrentFP(contextToReturnTo, GIV(framePointer))); + ceInvokeInterpret(); + + /* NOTREACHED */ + return null; +} + + +/* index is unboxed and 0-based. The call-back expects 1-based value (to + perform the operation with instVarAt:put: + */ + + /* CoInterpreter>>#ceCannotAssignTo:withIndex:valueToAssign: */ +#if IMMUTABILITY +sqInt +ceCannotAssignTowithIndexvalueToAssign(sqInt immutableObject, sqInt index, sqInt valueToAssign) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + sqInt top; + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),immutableObject); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),valueToAssign); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),(((usqInt)(index + 1) << 3) | 1)); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAttemptToAssign) << (shiftForWord())))))), immutableObject, 2); +} +#endif /* IMMUTABILITY */ + + +/* A context that has been returned from, or otherwise has an invalid pc has + been reentered. + Until we have a cannotResume: selector, simply resend cannotReturn:. */ + + /* CoInterpreter>>#ceCannotResume */ +sqInt +ceCannotResume(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + sqInt resultOop; + + assert(isMachineCodeFrame(GIV(framePointer))); + assert(frameHasContext(GIV(framePointer))); + resultOop = longAt(GIV(stackPointer)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt(GIV(framePointer) + FoxThisContext)); + longAtput((GIV(stackPointer) -= BytesPerWord),resultOop); + + /* Make sure the happy couple remain returned from. */ + object = ceCannotResumePC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), longAt(GIV(framePointer) + FoxThisContext), 1); +} + + +/* Log failure and then retry if there's an accessorDepth or failure due to + no memory. + */ + + /* CoInterpreter>>#ceCheckAndMaybeRetryPrimitive: */ +void +ceCheckAndMaybeRetryPrimitive(sqInt primIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodSelectorOrImmediate; + sqInt retried; + sqInt tagBits; + + if ((recordPrimTrace()) + && (!primTracePluginName)) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveFailure << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + aMethodSelectorOrImmediate = /* fetchClassOf: */ + ((tagBits = (longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord))) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)))); + + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = aMethodSelectorOrImmediate; + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + retried = retryPrimitiveOnFailure(); + if (retried + && (recordPrimTrace())) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveRetry << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } +} + + /* CoInterpreter>>#ceContext:instVar: */ +sqInt +ceContextinstVar(sqInt maybeContext, sqInt slotIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt result; + sqInt top; + + if (((longAt((void *)(maybeContext))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + result = externalInstVarofContext(slotIndex, maybeContext); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + else { + result = longAt((void *)((maybeContext + BaseHeaderSize) + ((((usqInt)(slotIndex) << (shiftForWord())))))); + } + return result; +} + + +/* genStorePop:MaybeContextReceiverVariable: filters out unmarried contexts + but not arbitrary objects in subclasses. It answers maybeMarriedContext so + that the StackToRegisterMappingCogit can keep ReceiverResultReg live. */ + + /* CoInterpreter>>#ceContext:instVar:value: */ +sqInt +ceContextinstVarvalue(sqInt maybeMarriedContext, sqInt slotIndex, sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt top; + + if ((((longAt((void *)(maybeMarriedContext))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (((((longAt((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + externalInstVarofContextput(slotIndex, maybeMarriedContext, anOop); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + else { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(slotIndex, maybeMarriedContext, anOop)); + assert(isNonImmediate(maybeMarriedContext)); + if (oopisGreaterThanOrEqualTo(maybeMarriedContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anOop & (tagMask())))) + && (oopisLessThan(anOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(maybeMarriedContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(maybeMarriedContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(slotIndex) << (shiftForWord()))))),anOop); + } + return maybeMarriedContext; +} + + /* CoInterpreter>>#ceInterpretMethodFromPIC:receiver: */ +sqInt +ceInterpretMethodFromPICreceiver(sqInt aMethodObj, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + CogMethod *pic; + sqInt primitiveIndex; + sqInt top; + + + /* pop off inner return and locate open or closed PIC */ + pic = ((CogMethod *) ((popStack()) - (interpretOffset()))); + assert((isCMOpenPIC(((CogBlockMethod *) pic))) + || (isCMClosedPIC(((CogBlockMethod *) pic)))); + + /* If found from an open PIC then it must be an uncogged method and, since it's been found + in the method cache, should be cogged if possible. If found from a closed PIC then at the + time the closed PIC was created the method was uncoggable, either because there was + no space, it had too many literals or it contained an illegal bytecode). So don't try and cog + it, but subsequently it may have been cogged via another path. If the method is, or ends up + cogged, jump to machine code, otherwise interpret. */ + if (((pic->cmType)) == CMOpenPIC) { + assert(!(methodHasCogMethod(aMethodObj))); + + /* begin methodShouldBeCogged: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + goto l1; + } + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(aMethodObj); + } + goto l2; +l1: + cogselector(aMethodObj, (pic->selector)); +l2:; + } + if (methodHasCogMethod(aMethodObj)) { + executeCogMethodfromUnlinkedSendWithReceiver(cogMethodOf(aMethodObj), rcvr); + } + GIV(messageSelector) = (pic->selector); + GIV(newMethod) = aMethodObj; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (aMethodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]))); + GIV(argumentCount) = (pic->cmNumArgs); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + return interpretMethodFromMachineCode(); +} + + /* CoInterpreter>>#ceMNUFromPICMNUMethod:receiver: */ +sqInt +ceMNUFromPICMNUMethodreceiver(sqInt aMethodObj, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cPIC; + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + sqInt primitiveIndex; + sqInt selector; + sqInt tagBits; + sqInt top; + + assert(addressCouldBeOop(rcvr)); + assert((aMethodObj == 0) + || ((addressCouldBeObj(aMethodObj)) + && (isOopCompiledMethod(aMethodObj)))); + selector = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(selector), lengthOfMaybeImmediate(selector), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + cPIC = ((CogMethod *) ((popStack()) - (mnuOffset()))); + assert((isCMClosedPIC(((CogBlockMethod *) cPIC))) + || (isCMOpenPIC(((CogBlockMethod *) cPIC)))); + GIV(argumentCount) = (cPIC->cmNumArgs); + GIV(messageSelector) = (cPIC->selector); + if (aMethodObj) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + createActualMessageTo(/* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr))); + if (/* maybeMethodHasCogMethod: */ + ((!(aMethodObj & (tagMask())))) + && ((((byteAt((void *)(aMethodObj + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + executeCogMethodfromUnlinkedSendWithReceiver(cogMethodOf(aMethodObj), rcvr); + + /* NOTREACHED */ + assert(0); + } + GIV(newMethod) = aMethodObj; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (aMethodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]))); + return interpretMethodFromMachineCode(); + } + + /* handleMNU:InMachineCodeTo:classForMessage: assumes lkupClass is set, since every other use is + after a lookupMethodNoMNUEtcInClass: call, which sets lkupClass. Here we must set it manually. + Global variables. Bah! */ + handleMNUInMachineCodeToclassForMessage(SelectorDoesNotUnderstand, rcvr, (GIV(lkupClass) = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)))); + + /* NOTREACHED */ + assert(0); + return 0; +} + + +/* Called from machine code to set an object's identity hash to a new hash. + N.B. may be called from machine code on the Smalltalk stack so this should + not call deeply. */ +/* We know anObject has not a hash yet (or this entry-point would not be + called. Sets the hash, then answers it as a smallinteger */ + + /* CoInterpreter>>#ceNewHashOf: */ +sqInt +ceNewHashOf(sqInt anObject) +{ + assert((isNonImmediate(anObject)) + && ((rawHashBitsOf(anObject)) == 0)); + return (((newHashBitsOf(anObject)) << 3) | 1); +} + + /* CoInterpreter>>#ceNonLocalReturn: */ +sqInt +ceNonLocalReturn(sqInt returnValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt closure; + sqInt contextToReturnTo; + sqInt currentCtx; + char *frameToReturnTo; + sqInt home; + StackPage *newPage; + sqInt nextCntx; + sqInt ourContext; + sqInt ourContextSqInt; + sqInt senderOop; + char *sp; + char *theFP; + char *theFP1; + StackPage *thePage; + StackPage *thePage1; + sqInt top; + sqInt unwindContextOrNilOrZero; + + + /* self shortPrintFrameAndCallers: framePointer. + self printOop: returnValue. + self halt. */ + frameToReturnTo = ((char *) 0); + theFP = ((char *) 0); + assert(isMachineCodeFrame(GIV(framePointer))); + assert(frameIsBlockActivation(GIV(framePointer))); + + /* Update the current page's headFrame pointers to enable the search for unwind protects below + to identify widowed contexts correctly. */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* Since this is a block activation the closure is on the stack above any args and the frame. */ + closure = longAt(GIV(framePointer) + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(GIV(framePointer)))->cmNumArgs) + : byteAt((GIV(framePointer) + FoxIFrameFlags) + 1)))) << (shiftForWord())))))); + + /* avoid compiler warning */ + /* Walk the closure's lexical chain to find the context or frame to return from (home). */ + home = null; + while (closure != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + home = longAt((void *)((closure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + assert(isNonImmediate(home)); + if ((!((longAt((void *)(home))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + home = fixFollowedFieldofObjectwithInitialValue(ClosureOuterContextIndex, closure, home); + } + if (!(/* isContext: */ + ((!(home & (tagMask())))) + && (((longAt((void *)(home))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* error: can't find home on chain; cannot return */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + ourContext = longAt(GIV(framePointer) + FoxThisContext); + goto l1; + } + ourContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l1: + + /* begin externalCannotReturn:from: */ + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),ourContext); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + + /* Both ceBaseFrameReturnTrampoline & ceNonLocalReturnTrampoline pop + their caller's return pc into instructionPointer. In this uncommon case restore + it, since a send's call pushes the instructionPointer (after the arguments). */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), ourContext, 1); + } + + /* begin followObjField:ofObject: */ + closure = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord())))))); + assert(isNonImmediate(closure)); + if ((!((longAt((void *)(closure))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + closure = fixFollowedFieldofObjectwithInitialValue(ClosureIndex, home, closure); + } + } + + /* home is to be returned from provided there is no unwind-protect activation between + this frame and home's sender. Search for an unwind. findUnwindThroughContext: + will answer either the context for an unwind-protect activation or nilObj if the sender + cannot be found or 0 if no unwind is found but the sender is. */ + unwindContextOrNilOrZero = findUnwindThroughContext(home); + if (unwindContextOrNilOrZero == GIV(nilObj)) { + /* error: can't find home on chain; cannot return */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + ourContext = longAt(GIV(framePointer) + FoxThisContext); + goto l2; + } + ourContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l2: + + /* begin externalCannotReturn:from: */ + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),ourContext); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + + /* Both ceBaseFrameReturnTrampoline & ceNonLocalReturnTrampoline pop + their caller's return pc into instructionPointer. In this uncommon case restore + it, since a send's call pushes the instructionPointer (after the arguments). */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), ourContext, 1); + } + if (unwindContextOrNilOrZero) { + /* begin externalAboutToReturn:through: */ + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + ourContextSqInt = longAt(GIV(framePointer) + FoxThisContext); + goto l3; + } + ourContextSqInt = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l3: + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),ourContextSqInt); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),unwindContextOrNilOrZero); + + /* The ceNonLocalReturnTrampoline pops its caller's return pc into instructionPointer. + In this uncommon case restore it, since a send's call pushes the instructionPointer (after the arguments). */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorAboutToReturn) << (shiftForWord())))))), ourContextSqInt, 2); + } + + /* Now we know home is on the sender chain. + We could be returning to either a context or a frame. Find out which. */ + contextToReturnTo = null; + if (((((longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert(checkIsStillMarriedContextcurrentFP(home, GIV(framePointer))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (longAt(theFP + FoxSavedFP)) { + frameToReturnTo = ((char *)(longAt(theFP + FoxSavedFP))); + } + else { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + contextToReturnTo = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(contextToReturnTo)); + assert((contextToReturnTo == (nilObject())) + || (isContext(followMaybeForwarded(contextToReturnTo)))); + } + } + else { + contextToReturnTo = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + if ((/* isContext: */ + ((!(contextToReturnTo & (tagMask())))) + && (((longAt((void *)(contextToReturnTo))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + assert(checkIsStillMarriedContextcurrentFP(contextToReturnTo, GIV(framePointer))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + frameToReturnTo = ((char *)(senderOop - (smallIntegerTag()))); + contextToReturnTo = null; + } + } + + /* If returning to a context we must make a frame for it unless it is dead. */ + if (contextToReturnTo) { + frameToReturnTo = establishFrameForContextToReturnTo(contextToReturnTo); + if (!frameToReturnTo) { + /* error: home's sender is dead; cannot return */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + ourContext = longAt(GIV(framePointer) + FoxThisContext); + goto l4; + } + ourContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l4: + + /* begin externalCannotReturn:from: */ + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),ourContext); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + + /* Both ceBaseFrameReturnTrampoline & ceNonLocalReturnTrampoline pop + their caller's return pc into instructionPointer. In this uncommon case restore + it, since a send's call pushes the instructionPointer (after the arguments). */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotReturn) << (shiftForWord())))))), ourContext, 1); + } + } + + /* Now we have a frame to return to. If it is on a different page we must + free intervening pages and nil out intervening contexts. We must free + intervening stack pages because if we leave the pages to be divorced + then their contexts will be divorced with intact senders and instruction + pointers. This code is similar to primitiveTerminateTo. */ + assert(pageListIsWellFormed()); + + /* begin stackPageFor: */ + newPage = stackPageAtpages(pageIndexFor(frameToReturnTo), GIV(pages)); + if (newPage != GIV(stackPage)) { + theFP1 = (GIV(stackPage)->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP1)); + thePage1 = stackPageAtpages(pageIndexFor(theFP1), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + assert(isContext(currentCtx)); + freeStackPage(GIV(stackPage)); + while (1) { + assert(isContext(currentCtx)); + if ((((((longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && ((stackPageFor((theFP = frameOfMarriedContext(currentCtx)))) == newPage)) break; + if (((((longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + theFP1 = (thePage->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP1)); + thePage1 = stackPageAtpages(pageIndexFor(theFP1), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + freeStackPage(thePage); + } + else { + nextCntx = longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + + /* begin markContextAsDead: */ + assert(isContext(currentCtx)); + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(SenderIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + currentCtx = nextCntx; + } + } + + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (newPage->headSP); + GIV(framePointer) = (newPage->headFP); + } + + /* Two cases. Returning to the top frame or an interior frame. The + top frame has its instruction pointer on top of stack. An interior + frame has its instruction pointer in the caller frame. We need to + peel back any frames on the page until we get to the correct frame. */ + if (GIV(framePointer) == frameToReturnTo) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + } + else { + do { + callerFP = GIV(framePointer); + GIV(framePointer) = ((char *)(longAt(GIV(framePointer) + FoxSavedFP))); + } while(GIV(framePointer) != frameToReturnTo); + GIV(instructionPointer) = longAt(callerFP + FoxCallerSavedIP); + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(callerFP))); + GIV(stackPointer) = (callerFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(callerFP))->cmNumArgs) + : byteAt((callerFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + return returntoExecutive(returnValue, 0); +} + + /* CoInterpreter>>#ceReapAndResetErrorCodeFor: */ +void +ceReapAndResetErrorCodeFor(CogMethod *cogMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(GIV(primFailCode) != 0); + GIV(newMethod) = (cogMethod->methodObject); + reapAndResetErrorCodeToheader(GIV(stackPointer) + BytesPerWord, (cogMethod->methodHeader)); +} + + +/* Entry-point for an abort send in a CogMethod (aboutToReturn:through:, + cannotReturn: et al). + Try and dispatch the send, but the send may turn into an MNU in which case + defer to + handleMNUInMachineCodeTo:... which will dispatch the MNU. + + Continue execution via either executeMethod or + interpretMethodFromMachineCode: depending on whether the target method is + cogged or not. */ + + /* CoInterpreter>>#ceSendAbort:to:numArgs: */ +static NoDbgRegParms sqInt +ceSendAborttonumArgs(sqInt selector, sqInt rcvr, sqInt numArgs) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classObj; + sqInt classTablePage; + sqInt classTag; + CogMethod *cogMethod; + sqInt errSelIdx; + sqInt fieldIndex; + sqInt methodHeader; + sqInt tagBits; + sqInt top; + + + /* self printExternalHeadFrame */ + /* self printStringOf: selector */ + assertCStackWellAligned(); + assert(addressCouldBeOop(rcvr)); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(selector), lengthOfMaybeImmediate(selector), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + GIV(argumentCount) = numArgs; + classTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + if (lookupInMethodCacheSelclassTag(selector, classTag)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, selector); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), selector); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + GIV(messageSelector) = selector; + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((classTag >= 0) + && ((classTag <= (tagMask())) + || ((classTag >= (arrayClassIndexPun())) + && (classTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classObj = null; + goto l1; + } + fieldIndex = classTag & ((1U << (classTableMajorIndexShift())) - 1); + classObj = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l1: + if ((errSelIdx = lookupOrdinaryNoMNUEtcInClass(classObj))) { + handleMNUInMachineCodeToclassForMessage(errSelIdx, rcvr, classObj); + + /* NOTREACHED */ + assert(0); + } + } + + /* check for coggability because method is in the cache */ + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (/* maybeMethodHasCogMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + executeNewMethod(); + assert(0); + } + return interpretMethodFromMachineCode(); +} + + +/* Send from an Open PIC when the first-level method lookup probe has failed, + or to continue when PIC creation has failed (e.g. because we're out of + code space), + or when a send has failed due to a forwarded receiver. */ +/* self printFrame: stackPage headFP WithSP: stackPage headSP */ +/* self printStringOf: selector */ + + /* CoInterpreter>>#ceSendFromInLineCacheMiss: */ +sqInt +ceSendFromInLineCacheMiss(CogMethod *cogMethodOrPIC) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classObj; + sqInt classTablePage; + sqInt classTag; + CogMethod *cogMethod; + sqInt errSelIdx; + sqInt fieldIndex; + sqInt methodHeader; + sqInt rcvr; + sqInt selector; + sqInt tagBits; + sqInt top; + + GIV(argumentCount) = (cogMethodOrPIC->cmNumArgs); + + /* skip return pc */ + rcvr = longAt(GIV(stackPointer) + ((((cogMethodOrPIC->cmNumArgs)) + 1) * BytesPerWord)); + assert(addressCouldBeOop(rcvr)); + classTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + + /* would like to assert this but must also allow for an interpretable method in the cache. */ + if (lookupInMethodCacheSelclassTag((cogMethodOrPIC->selector), classTag)) { + selector = (cogMethodOrPIC->selector); + + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, selector); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), selector); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + if (/* isOopForwarded: */ + ((!(((cogMethodOrPIC->selector)) & (tagMask())))) + && ((!((longAt((void *)((cogMethodOrPIC->selector)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + handleForwardedSelectorFaultFor((cogMethodOrPIC->selector)); + return ceSendFromInLineCacheMiss(cogMethodOrPIC); + } + if (classTag == (isForwardedObjectClassIndexPun())) { + handleForwardedSendFaultForReceiverstackDelta(rcvr, 1); + + /* skip return pc */ + return ceSendFromInLineCacheMiss(cogMethodOrPIC); + } + GIV(messageSelector) = (cogMethodOrPIC->selector); + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((classTag >= 0) + && ((classTag <= (tagMask())) + || ((classTag >= (arrayClassIndexPun())) + && (classTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classObj = null; + goto l1; + } + fieldIndex = classTag & ((1U << (classTableMajorIndexShift())) - 1); + classObj = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l1: + if ((errSelIdx = lookupOrdinaryNoMNUEtcInClass(classObj))) { + handleMNUInMachineCodeToclassForMessage(errSelIdx, rcvr, classObj); + + /* NOTREACHED */ + assert(0); + } + } + + /* check for coggability because method is in the cache */ + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (/* maybeMethodHasCogMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + executeNewMethod(); + assert(0); + } + return interpretMethodFromMachineCode(); +} + + +/* For RegisterAllocatingCogit we want the pc following a conditional branch + not to be reachable, so + we don't have to generate code to reload registers. But notionally the pc + following a conditional + branch is reached when continuing from a mustBeBoolean error. Instead of + supporting this in the + JIT, simply convert to an interpreter frame, backup the pc to the branch, + reenter the interpreter + and hence retry the mustBeBoolean send therein. N.B. We could do this for + immutability violations + too, but immutability is used in actual applications and so should be + performant, whereas + mustBeBoolean errors are extremely rare and so we choose brevity over + performance in this case. */ + + /* CoInterpreter>>#ceSendMustBeBooleanTo:interpretingAtDelta: */ +sqInt +ceSendMustBeBooleanTointerpretingAtDelta(sqInt aNonBooleanObject, sqInt jumpSize) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + sqInt top; + + assert(addressCouldBeOop(aNonBooleanObject)); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + convertFrametoInterpreterFrame(GIV(framePointer), jumpSize); + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),aNonBooleanObject); + GIV(stackPointer) = sp; + + /* and now reenter the interpreter... */ + ceInvokeInterpret(); + + /* NOTREACHED */ + return null; +} + + /* CoInterpreter>>#ceSendMustBeBoolean: */ +sqInt +ceSendMustBeBoolean(sqInt anObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + sqInt top; + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),anObject); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return ceSendAborttonumArgs(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorMustBeBoolean) << (shiftForWord())))))), anObject, 0); +} + + +/* Entry-point for an unlinked directed super send in a CogMethod. Smalltalk + stack looks like + receiver + args + head sp -> sender return pc + methodClassBinding is an association whose value is the class above which + to start the lookup. */ + + /* CoInterpreter>>#ceSend:aboveClassBinding:to:numArgs: */ +void +ceSendaboveClassBindingtonumArgs(sqInt selector, sqInt methodClassBinding, sqInt rcvr, sqInt numArgs) +{ + ceSendabovetonumArgs(selector, fetchPointerofObject(ValueIndex, /* followMaybeForwarded: */ + (/* isOopForwarded: */ + ((!(methodClassBinding & (tagMask())))) + && ((!((longAt((void *)(methodClassBinding))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + ? followForwarded(methodClassBinding) + : methodClassBinding)), rcvr, numArgs); +} + + +/* Entry-point for an unlinked directed super send in a CogMethod. Smalltalk + stack looks like + receiver + args + head sp -> sender return pc + methodClass is the class above which to start the lookup. + + If an MNU then defer to handleMNUInMachineCodeTo:... which will dispatch + the MNU and + may choose to allocate a closed PIC with a fast MNU dispatch for this + send. Otherwise + attempt to link the send site as efficiently as possible. All link + attempts may fail; e.g. + because we're out of code memory. + + Continue execution via either executeMethod or + interpretMethodFromMachineCode: depending on whether the target method is + cogged or not. */ + + /* CoInterpreter>>#ceSend:above:to:numArgs: */ +sqInt +ceSendabovetonumArgs(sqInt selector, sqInt methodClass, sqInt rcvr, sqInt numArgs) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classObj; + sqInt classObjSqInt; + sqInt classPointer; + sqInt classTablePage; + sqInt classTag; + CogMethod *cogMethod; + CogMethod *cogMethod1; + sqInt err; + sqInt errSelIdx; + sqInt fieldIndex; + int hash; + sqInt methodHeader; + CogMethod *newCogMethod; + sqInt objOop; + sqInt tagBits; + sqInt top; + + + /* self printExternalHeadFrame */ + /* self printStringOf: selector */ + assertCStackWellAligned(); + assert(addressCouldBeOop(rcvr)); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(selector), lengthOfMaybeImmediate(selector), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + classPointer = /* followMaybeForwarded: */ + (/* isOopForwarded: */ + ((!(methodClass & (tagMask())))) + && ((!((longAt((void *)(methodClass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + ? followForwarded(methodClass) + : methodClass); + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, objOop); + } + classObjSqInt = objOop; + + /* begin classTagForClass: */ + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObjSqInt)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classTag = ((hash = (long32At((void *)(classObjSqInt + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObjSqInt) + ? ((err = enterIntoClassTable(classObjSqInt)) + ? -err + : (long32At((void *)(classObjSqInt + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + GIV(argumentCount) = numArgs; + if (lookupInMethodCacheSelclassTag(selector, classTag)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod1 = ((CogMethod *) methodHeader); + if (((cogMethod1->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod1, selector); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), selector); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + assert(!((isForwardedClassTag(classTag)))); + if (/* isOopForwarded: */ + ((!(selector & (tagMask())))) + && ((!((longAt((void *)(selector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + return ceSendabovetonumArgs(handleForwardedSelectorFaultFor(selector), methodClass, rcvr, numArgs); + } + GIV(messageSelector) = selector; + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((classTag >= 0) + && ((classTag <= (tagMask())) + || ((classTag >= (arrayClassIndexPun())) + && (classTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classObj = null; + goto l1; + } + fieldIndex = classTag & ((1U << (classTableMajorIndexShift())) - 1); + classObj = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l1: + if ((errSelIdx = lookupOrdinaryNoMNUEtcInClass(classObj))) { + if ((errSelIdx == SelectorDoesNotUnderstand) + && ((((usqInt)((cogMethod = cogMNUPICSelectorreceivermethodOperandnumArgs(GIV(messageSelector), rcvr, mnuMethodOrNilFor(rcvr), GIV(argumentCount)))))) > (minCogMethodAddress()))) { + linkSendAtintooffsetreceiver(longAt(GIV(stackPointer)), mframeHomeMethod(GIV(framePointer)), cogMethod, noCheckEntryOffset(), rcvr); + } + handleMNUInMachineCodeToclassForMessage(errSelIdx, rcvr, classObj); + assert(0); + } + } + + /* check for coggability because method is in the cache + Method found and has a cog method. Attempt to link to it. The receiver's class may be young. + We must not link to an Open PIC since they perform normal sends. */ + if (/* maybeMethodHasCogMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, selector); + } + else { + /* Deal with anonymous accessors, e.g. in Newspeak. The cogMethod may not have the + correct selector. If not, try and compile a new method with the correct selector. */ + if (((cogMethod->selector)) != selector) { + if ((newCogMethod = cogselector(GIV(newMethod), selector))) { + cogMethod = newCogMethod; + } + } + } + if (((cogMethod->selector)) == selector) { + linkSendAtintooffsetreceiver(longAt(GIV(stackPointer)), mframeHomeMethod(GIV(framePointer)), cogMethod, noCheckEntryOffset(), rcvr); + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + executeNewMethod(); + assert(0); + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + return interpretMethodFromMachineCode(); +} + + +/* Entry-point for an unlinked send in a CogMethod. Smalltalk stack looks + like receiver + args + head sp -> sender return pc + + If an MNU then defer to handleMNUInMachineCodeTo:... which will dispatch + the MNU and + may choose to allocate a closed PIC with a fast MNU dispatch for this + send. Otherwise + attempt to link the send site as efficiently as possible. All link + attempts may fail; e.g. + because we're out of code memory. + + Continue execution via either executeMethod or + interpretMethodFromMachineCode: depending on whether the target method is + cogged or not. */ + + /* CoInterpreter>>#ceSend:super:to:numArgs: */ +sqInt +ceSendsupertonumArgs(sqInt selector, sqInt superNormalBar, sqInt rcvr, sqInt numArgs) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classObj; + sqInt classObjSqInt; + sqInt classPointer; + sqInt classTablePage; + sqInt classTag; + CogMethod *cogMethod; + CogMethod *cogMethod1; + sqInt err; + sqInt errSelIdx; + sqInt fieldIndex; + int hash; + sqInt literal; + sqInt methodHeader; + sqInt methodPointer; + CogMethod *newCogMethod; + sqInt objOop; + sqInt offset; + sqInt tagBits; + sqInt top; + + + /* self printExternalHeadFrame */ + /* self printStringOf: selector */ + assertCStackWellAligned(); + assert(addressCouldBeOop(rcvr)); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(selector), lengthOfMaybeImmediate(selector), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + if (superNormalBar) { + methodPointer = /* frameMethodObject: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(GIV(framePointer)))->methodObject) + : longAt(GIV(framePointer) + FoxMethod)); + + /* begin methodClassOf: */ + offset = (literalCountOf(methodPointer)) - 1; + literal = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = fixFollowedFieldofObjectwithInitialValue(offset + LiteralStart, methodPointer, literal); + } + classPointer = ((literal != GIV(nilObj)) + && (/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + ? (assert((numSlotsOf(literal)) > ValueIndex), + followFieldofObject(ValueIndex, literal)) + : GIV(nilObj)); + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, objOop); + } + classObjSqInt = objOop; + + /* begin classTagForClass: */ + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObjSqInt)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classTag = ((hash = (long32At((void *)(classObjSqInt + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObjSqInt) + ? ((err = enterIntoClassTable(classObjSqInt)) + ? -err + : (long32At((void *)(classObjSqInt + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + } + else { + classTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + } + GIV(argumentCount) = numArgs; + if (lookupInMethodCacheSelclassTag(selector, classTag)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod1 = ((CogMethod *) methodHeader); + if (((cogMethod1->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod1, selector); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), selector); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + if (/* isOopForwarded: */ + ((!(selector & (tagMask())))) + && ((!((longAt((void *)(selector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + return ceSendsupertonumArgs(handleForwardedSelectorFaultFor(selector), superNormalBar, rcvr, numArgs); + } + if (classTag == (isForwardedObjectClassIndexPun())) { + assert(superNormalBar == 0); + return ceSendsupertonumArgs(selector, superNormalBar, handleForwardedSendFaultForReceiverstackDelta(rcvr, 1), numArgs); + } + GIV(messageSelector) = selector; + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((classTag >= 0) + && ((classTag <= (tagMask())) + || ((classTag >= (arrayClassIndexPun())) + && (classTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classObj = null; + goto l1; + } + fieldIndex = classTag & ((1U << (classTableMajorIndexShift())) - 1); + classObj = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l1: + if ((errSelIdx = lookupOrdinaryNoMNUEtcInClass(classObj))) { + if ((errSelIdx == SelectorDoesNotUnderstand) + && ((((usqInt)((cogMethod = cogMNUPICSelectorreceivermethodOperandnumArgs(GIV(messageSelector), rcvr, mnuMethodOrNilFor(rcvr), GIV(argumentCount)))))) > (minCogMethodAddress()))) { + linkSendAtintooffsetreceiver(longAt(GIV(stackPointer)), mframeHomeMethod(GIV(framePointer)), cogMethod, (superNormalBar + ? noCheckEntryOffset() + : entryOffset()), rcvr); + } + handleMNUInMachineCodeToclassForMessage(errSelIdx, rcvr, classObj); + assert(0); + } + + /* Methods towards the roots of the class hierarchy are shared by many subclasses. + It is likely to be profitable to cog these methods but becAuse they're being sent to + instances of different classes they may not be found in the cache. Luckily we have + lastMethodCacheProbeWrite to track recent writes to the cache and hence can detect + when a method is used more than once but via different classes. */ + if ((GIV(methodCache)[GIV(lastMethodCacheProbeWrite) + MethodCacheMethod]) == GIV(newMethod)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod1 = ((CogMethod *) methodHeader); + if (((cogMethod1->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod1, selector); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), selector); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + } + + /* check for coggability because method is in the cache + Method found and has a cog method. Attempt to link to it. The receiver's class may be young. + If the Cogit can't store young classes in inline caches we can link to an open PIC instead. */ + if (/* maybeMethodHasCogMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, selector); + } + else { + /* Deal with anonymous accessors, e.g. in Newspeak. The cogMethod may not have the + correct selector. If not, try and compile a new method with the correct selector. */ + if (((cogMethod->selector)) != selector) { + if ((newCogMethod = cogselector(GIV(newMethod), selector))) { + cogMethod = newCogMethod; + } + } + } + if (((cogMethod->selector)) == selector) { + linkSendAtintooffsetreceiver(longAt(GIV(stackPointer)), mframeHomeMethod(GIV(framePointer)), cogMethod, (superNormalBar + ? noCheckEntryOffset() + : entryOffset()), rcvr); + } + else { + if (!superNormalBar) { + patchToOpenPICFornumArgsreceiver(selector, numArgs, rcvr); + } + } + + /* If patchToOpenPICFor:.. returns we're out of code memory */ + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + executeNewMethod(); + assert(0); + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + return interpretMethodFromMachineCode(); +} + + +/* If contextSwitchIfNotNil is nil we can't context switch. + contextSwitchIfNotNil is set to nil by + - the special primitiveClosureValueNoContextSwitch entry-point in block + dispatch - the stack check in methods with primitive 198. + In a normal method contextSwitchIfNotNil will be the method (see e.g. + SimpleStackBasedCogit>>compileFrameBuild). In a block it will be the + closure (see e.g. SimpleStackBasedCogit>>compileMethodBody). */ + + /* CoInterpreter>>#ceStackOverflow: */ +void +ceStackOverflow(sqInt contextSwitchIfNotNil) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ceMethodAbortRetAddr; + CogBlockMethod *cogMethod; + sqInt switched; + + /* begin popStack */ + ceMethodAbortRetAddr = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* begin mframeCogMethod: */ + cogMethod = ((CogBlockMethod *) ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodMask)); + assert((ceMethodAbortRetAddr - (abortOffset())) == (((usqInt)(asCogHomeMethod(cogMethod))))); + GIV(instructionPointer) = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + GIV(method) = (GIV(newMethod) = (GIV(messageSelector) = GIV(nilObj))); + switched = handleStackOverflowOrEventAllowContextSwitch(contextSwitchIfNotNil != 0); + returnToExecutivepostContextSwitch(0, switched); + error("should not be reached"); +} + + +/* The high resolution timer has been found to be greater than + nextProfileTick after calling a primitive. + Now take a sample iff nextProfileTick has not been reset. c.f. + checkProfileTickPostPrimitive:. If the + primitive succeeds then signal the profile semaphore (activate the + profiling process) immediately + so that the sample is attributable to the current sender. */ + + /* CoInterpreter>>#ceTakeProfileSample */ +void +ceTakeProfileSample(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + + /* avoid calling assert in real VM cuz this may run on Smalltalk stack. */ + if (!GIV(profileProcess)) { + if (GIV(nextProfileTick) > 0) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + GIV(profileProcess) = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + if (!GIV(primFailCode)) { + GIV(profileMethod) = GIV(newMethod); + + /* Zero nextProfileTick because signalling the semaphore is when the VM effectively delivers the sample. */ + + /* begin zeroNextProfileTick */ + GIV(nextProfileTick) = 0; + if (synchronousSignal(GIV(profileSemaphore))) { + returnToExecutivepostContextSwitch(0, 1); + assert(!(1)); + } + else { + /* we reach here when there is a higher priority process than the profile process active, e.g. the timer process */ + } + } + } + else { + GIV(profileMethod) = null; + } + } + + /* if not nil a sample has already been taken. + force an interrupt check, but do not use forceInterruptCheck (which may + call interruptCheckChain) so that this can be run on the Smalltalk stack + from compileOnStackExternalPrimitive:flags: */ + GIV(stackLimit) = ((char *) (((usqInt) -1))); +} + + /* CoInterpreter>>#ceTraceBlockActivation */ +void +ceTraceBlockActivation(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt thingOne; + + if (recordBlockTrace()) { + thingOne = ((mframeHomeMethod(GIV(framePointer)))->methodObject); + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceBlockActivation) << TraceTypeShift))))) + CSFromMachineCode); + GIV(traceLog)[GIV(traceLogIndex) + 1] = thingOne; + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + if (printOnTrace()) { + printActivationNameForreceiverisBlockfirstTemporary(((mframeHomeMethod(GIV(framePointer)))->methodObject), /* frameReceiver: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? longAt(GIV(framePointer) + FoxMFReceiver) + : longAt(GIV(framePointer) + FoxIFReceiver)), 1, null); + cr(); + } + } +} + + /* CoInterpreter>>#ceTraceLinkedSend: */ +void +ceTraceLinkedSend(sqInt theReceiver) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cogMethod; + sqInt selector; + sqInt tagBits; + sqInt thingOne; + sqInt thingTwo; + + cogMethod = ((CogMethod *) ((longAt(GIV(stackPointer))) - (traceLinkedSendOffset()))); + if (((cogMethod->cmNumArgs)) > (numRegArgs())) { + } + else { + } + GIV(stackPointer) + BytesPerWord; + + /* stackValue: */ + longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* empty maybeCheckStackDepth:sp:pc: */ + + /* cogit recordSendTrace ifTrue: is implicit; wouldn't compile the call otherwise. */ + thingOne = /* fetchClassOf: */ + ((tagBits = theReceiver & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(theReceiver)); + thingTwo = (cogMethod->selector); + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + TraceSend) + CSFromMachineCode); + GIV(traceLog)[GIV(traceLogIndex) + 1] = thingOne; + GIV(traceLog)[GIV(traceLogIndex) + 2] = thingTwo; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + if (printOnTrace()) { + printActivationNameForreceiverisBlockfirstTemporary((cogMethod->methodObject), theReceiver, 0, null); + cr(); + } + selector = (cogMethod->selector); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(selector), lengthOfMaybeImmediate(selector), /* fetchClassTagOf: */ + ((tagBits = theReceiver & (tagMask())) + ? tagBits + : (longAt((void *)(theReceiver))) & (classIndexMask()))); +} + + +/* For assertion checking. */ + + /* CoInterpreter>>#ceTraceStoreOf:into: */ +void +ceTraceStoreOfinto(sqInt aValue, sqInt anObject) +{ + assert((isImmediate(aValue)) + || (addressCouldBeObj(aValue))); + assert(addressCouldBeObj(anObject)); +} + + /* CoInterpreter>>#cFramePointerAddress */ +usqInt +cFramePointerAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(CFramePointer)))); +} + + /* CoInterpreter>>#checkAssertsEnabledInCoInterpreter */ +void +checkAssertsEnabledInCoInterpreter(void) +{ + sqInt assertsAreEnabledInCoInterpreter; + + assertsAreEnabledInCoInterpreter = 0; + assert(assertsAreEnabledInCoInterpreter); +} + + +/* Perform an integrity/leak check using the heapMap. Assume + clearLeakMapAndMapAccessibleObjects has set a bit at each + object's header. Check that all object references in machine + code are valid. Answer if all checks pass. */ + + /* CoInterpreter>>#checkCodeIntegrity: */ +static NoDbgRegParms sqInt +checkCodeIntegrity(sqInt gcModes) +{ + return checkIntegrityOfObjectReferencesInCode(gcModes); +} + + +/* In Spur a primitive may fail due to encountering a forwarder. On failure, + check the accessorDepth for the + primitive and if non-negative scan the args to the depth, following any + forwarders. Answer if any are found + so the prim can be retried. The primitive index is derived from newMethod. + + See + http://www.mirandabanda.org/cogblog/2014/02/08/primitives-and-the-partial-read-barrier/ + and SpurMemoryManager's class comment. */ +/* Override to log and export to Cogit */ + + /* CoInterpreter>>#checkForAndFollowForwardedPrimitiveState */ +sqInt +checkForAndFollowForwardedPrimitiveState(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt accessorDepth; + sqInt aMethodSelectorOrImmediate; + usqInt firstBytecode; + sqInt found; + sqInt header; + sqInt index; + sqInt methodHeader; + sqInt oop; + sqInt primIndex; + sqInt referent; + sqInt scannedStackFrame; + sqInt tagBits; + + if ((recordPrimTrace()) + && (!primTracePluginName)) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveFailure << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + aMethodSelectorOrImmediate = /* fetchClassOf: */ + ((tagBits = (longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord))) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)))); + + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = aMethodSelectorOrImmediate; + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + assert(failed()); + found = 0; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIndex = 0; + } + assert((GIV(argumentCount) == (argumentCountOf(GIV(newMethod)))) + || (isMetaPrimitiveIndex(primIndex))); + + /* First things first; make sure the metadata has been followed before it is accessed to derive accessorDepth... */ + if (((/* isCalloutPrimitiveIndex: */ + (primIndex == PrimNumberExternalCall) + || (primIndex == PrimNumberFFICall)) + || (primIndex == PrimNumberDoExternalCall)) + && (unfollowFirstLiteralOfMaybeCalloutMethodprimitiveIndex(GIV(newMethod), primIndex))) { + found = 1; + } + + /* If the primitive is one of the meta primitives PrimNumberDoPrimitive or PrimNumberDoExternalCall, then + metaAccessorDepth will have been set to nil at the start of the primitive, and to the accessor depth of the + called primitive (or external call) immediately before dispatch. Hence if primIndex is that of a meta primitive + then if metaAccessorDepth is -2, the accessor depth is that of the meta primitive, and if > -2, then + metaAccessorDepth is the accessor depth of the primitive (or external call). Similarly, if the primitive is + primitiveExternalCall then the accessor depth is that of primitiveExternalCall until primitiveFunctionPointer + is assigned, at which point the accessor depth is taken from the slot in newMethod's first literal. */ + accessorDepth = ((/* isMetaPrimitiveIndex: */ + (primIndex == PrimNumberDoPrimitive) + || (primIndex == PrimNumberDoExternalCall)) + && (GIV(metaAccessorDepth) > -2) + ? GIV(metaAccessorDepth) + : accessorDepthForPrimitiveMethod(GIV(newMethod))); + assert(((accessorDepth >= -1) && (accessorDepth <= 5))); + if (accessorDepth >= 0) { + scannedStackFrame = 0; + for (index = 0; index <= GIV(argumentCount); index += 1) { + oop = longAt((void *)(GIV(stackPointer) + (index * BytesPerWord))); + if ((!(oop & (tagMask())))) { + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + assert(index < GIV(argumentCount)); + found = 1; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + + /* stackValue:put: */ + longAtput((void *)(GIV(stackPointer) + (index * BytesPerWord)),oop); + if (!scannedStackFrame) { + scannedStackFrame = 1; + + /* Avoid repeated primitive failures by following all state in the current stack frame. */ + followForwardedFrameContentsstackPointer(GIV(framePointer), GIV(stackPointer) + ((GIV(argumentCount) + 1) * BytesPerWord)); + } + } + if ((accessorDepth > 0) + && ((/* hasPointerFields: */ + ((!(oop & (tagMask())))) + && (hasPointerFieldsNonImm(oop))) + && (followForwardedObjectFieldstoDepth(oop, accessorDepth)))) { + found = 1; + } + } + } + } + if (found + && (recordPrimTrace())) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveRetry << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + return found; +} + + +/* Necessary because we very much want CStackPointer and CFramePointer to be + static/private and grouped + with other interpreter variables which will hence be accessed via + VarBaseReg on platforms that have one. + */ + + /* CoInterpreter>>#checkIfCFramePointerInUse */ +sqInt +checkIfCFramePointerInUse(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return isCFramePointerInUse((&GIV(CFramePointer)), (&GIV(CStackPointer))); +} + + /* CoInterpreter>>#checkingLongRunningPrimitives */ +#if LRPCheck +int +checkingLongRunningPrimitives(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(longRunningPrimitiveCheckSemaphore); +} +#endif /* LRPCheck */ + + +/* Check if the argument is an ok object. + If this is a pointers object, check that its fields are all okay oops. */ + + /* CoInterpreter>>#checkOkayFields: */ +static NoDbgRegParms sqInt +checkOkayFields(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fieldOop; + sqInt hasYoung; + sqInt i; + sqInt iSqInt; + + if ((oop == null) + || (oop == 0)) { + return 1; + } + if ((((oop) & 7) == 1)) { + return 1; + } + if (!(checkOkayOop(oop))) { + return 0; + } + if (!(checkOopHasOkayClass(oop))) { + return 0; + } + if (!((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + || (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return 1; + } + hasYoung = 0 /* (hasSpurMemoryManagerAPI not) */; + i = (numPointerSlotsOf(oop)) - 1; + while (i >= 0) { + fieldOop = longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(fieldOop & (smallIntegerTag())))) { + if ((i == 0) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) { + if (!(methodFor(((void *)fieldOop)))) { + fprintf(GIV(transcript), + "method %p has an invalid cog method reference\n", + ((void *)oop)); + return 0; + } + } + else { + hasYoung = hasYoung + || (/* isYoung: */ + ((!(fieldOop & (tagMask())))) + && (oopisLessThan(fieldOop, GIV(oldSpaceStart)))); + if (!(checkOkayOop(fieldOop))) { + return 0; + } + if (!(checkOopHasOkayClass(fieldOop))) { + return 0; + } + } + } + i -= 1; + } + if (hasYoung) { + /* begin checkOkayYoungReferrer: */ + if (oopisLessThan(oop, GIV(oldSpaceStart))) { + return 1; + } + if (!((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + fprintf(GIV(transcript), + "remembered bit is not set in %p\n", + ((void *)oop)); + return 0; + } + + /* begin isInRememberedSet: */ + for (iSqInt = 0; iSqInt < GIV(rememberedSetSize); iSqInt += 1) { + if ((GIV(rememberedSet)[iSqInt]) == oop) { + return 1; + } + } + fprintf(GIV(transcript), + "%p has remembered bit set but is not in remembered set\n", + ((void *)oop)); + return 0; + } + return 1; +} + + +/* Check if the profile timer has expired during a primitive and if so take a + sample. If the primitive has failed sample the profileMethod as nil. */ +/* If continuing from primitive[Full]ClosureValueNoContextSwitch defer until + the next check + */ + + /* CoInterpreter>>#checkProfileTickPostPrimitive */ +static NeverInline void +checkProfileTickPostPrimitive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + + if (GIV(deferProfileCheckForVNCS)) { + GIV(deferProfileCheckForVNCS) = 0; + } + else { + if ((GIV(nextProfileTick) > 0) + && ((ioHighResClock()) >= GIV(nextProfileTick))) { + if (!GIV(profileProcess)) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + GIV(profileProcess) = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + if (GIV(primFailCode)) { + GIV(profileMethod) = null; + } + else { + GIV(profileMethod) = GIV(newMethod); + + /* begin zeroNextProfileTick */ + GIV(nextProfileTick) = 0; + if (synchronousSignal(GIV(profileSemaphore))) { + returnToExecutivepostContextSwitch(0, 1); + assert(!(1)); + } + else { + /* we reach here when there is a higher priority process than the profile process active, e.g. the timer process */ + } + } + forceInterruptCheck(); + } + } + } +} + + /* CoInterpreter>>#cogMethodHasRealNonProfilingPrimitive: */ +static NoDbgRegParms sqInt +cogMethodHasRealNonProfilingPrimitive(CogMethod *cogMethod) +{ + return (((cogMethod->cmType)) >= CMMethod) + && (isRealNonProfilingPrimitiveMethod((cogMethod->methodObject))); +} + + /* CoInterpreter>>#cogMethodHasTooManyLiterals: */ +sqInt +cogMethodHasTooManyLiterals(CogMethod *aCogMethod) +{ + return !(((/* begin literalCountOfMethodHeader: */ + assert((((((aCogMethod->methodHeader))) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((((aCogMethod->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile); +} + + /* CoInterpreter>>#cogMethodOf: */ +CogMethod * +cogMethodOf(sqInt aMethodOop) +{ + sqInt methodHeader; + + methodHeader = longAt((void *)((aMethodOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + return ((CogMethod *) methodHeader); +} + + /* CoInterpreter>>#commenceCogCompiledCodeCompaction */ +static void +commenceCogCompiledCodeCompaction(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong startTime; + sqInt top; + + GIV(cogCompiledCodeCompactionCalledFor) = 0; + if (recordEventTrace()) { + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceCodeCompaction) << TraceTypeShift)))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = 0; + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + if (recordPrimTrace()) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TraceCodeCompaction << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + startTime = ioUTCMicrosecondsNow(); + + /* This can be called in a number of circumstances. The instructionPointer + may contain a native pc that must be relocated. There may already be a + pushed instructionPointer on stack. Clients ensure that instructionPointer + is 0 if it should not be pushed and/or relocated. Pushing twice is a mistake + because only the top one will be relocated. */ + if (GIV(instructionPointer)) { + /* better not have already been pushed */ + assert((((usqInt)(stackTop()))) != GIV(instructionPointer)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + (GIV(stackPage)->headSP = GIV(stackPointer)); + } + assertValidStackedInstructionPointers(__LINE__); + compactCogCompiledCode(); + + /* begin nilUncoggableMethods */ + GIV(lastCoggableInterpretedBlockMethod) = (GIV(lastUncoggableInterpretedBlockMethod) = null); + if (GIV(instructionPointer)) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin externalWriteBackHeadStackPointer */ + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + (GIV(stackPage)->headSP = GIV(stackPointer)); + } + assertValidStackedInstructionPointers(__LINE__); + GIV(statCodeCompactionCount) += 1; + GIV(statCodeCompactionUsecs) += (ioUTCMicrosecondsNow()) - startTime; + if (checkForLeaks) { + clearLeakMapAndMapAccessibleObjects(); + asserta(checkCodeIntegrity(0)); + } +} + + /* CoInterpreter>>#compilationBreakpointFor: */ +void +compilationBreakpointFor(sqInt selectorOop) +{ + suppressHeartbeatFlag = 1; + warning("compilation send break (heartbeat suppressed)"); +} + + +/* Answer a value to store in the InstructionPointer index of a context + object for theIP and theFP. + Mapping native pcs to bytecode pcs is quite expensive, requiring a search + through the method + map. We mitigate this cost by deferring mapping until we really have to, + which is when a context's + instruction pointer is accessed by Smalltalk code (either direct inst var + access or through the + instVarAt: primitive). But to defer mapping we have to be able to + distinguish machine code from + bytecode pcs, which we do by using negative values for machine code pcs. + So if the frame is a + machine code one answer the negation of the offset in the cog method. + + As a whorish performance hack we also include the block method offset in + the pc of a block. + The least significant 16 bits are the native pc and the most significant + 14 bits are the block + start, in block alignment units. So when mapping back we can find the + start of the block. + + See mustMapMachineCodePC:context: for the code that does the actual + mapping. */ + + /* CoInterpreter>>#contextInstructionPointer:frame: */ +static NoDbgRegParms sqInt +contextInstructionPointerframe(sqInt theIP, char *theFP) +{ + sqInt blockOffset; + CogBlockMethod *cogMethod; + CogMethod *homeMethod; + + assert(validInstructionPointerinFrame(theIP, theFP)); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* begin mframeCogMethod: */ + cogMethod = ((CogBlockMethod *) ((longAt(theFP + FoxMethod)) & MFMethodMask)); + if (theIP == (ceCannotResumePC())) { + return HasBeenReturnedFromMCPCOop; + } + if (((cogMethod->cmType)) >= CMMethod) { + return (((usqInt)((((sqInt)cogMethod)) - theIP) << 3) | 1); + } + + /* cmHomeMethod */ + if (((cogMethod->cmType)) >= CMMethod) { + homeMethod = ((CogMethod *) cogMethod); + } + else { + assert(CMBlock == (cogMethod->cmType)); + homeMethod = ((CogMethod *) ((((usqInt)cogMethod)) - ((cogMethod->homeOffset)))); + } + blockOffset = ((((sqInt)homeMethod)) - (((sqInt)cogMethod))) / (blockAlignment()); + return (((usqInt)((((((usqInt)(blockOffset) << 16)))) | (((((sqInt)cogMethod)) - theIP) & 0xFFFF)) << 3) | 1); + } + return (((usqInt)(((((theIP == (ceReturnToInterpreterPC()) + ? ((usqInt)(longAt(theFP + FoxIFSavedIP))) + : theIP)) - (longAt(theFP + FoxMethod))) - BaseHeaderSize) + 2) << 3) | 1); +} + + +/* Convert the given machine code frame to an interpreter frame. Back up the + pc by + pcDelta. Support for backing up the pc in primitiveSuspend for processes + waiting on condition variables. Support for mustBeBoolean in the + RegisterAllocatingCogit and + for cloneContext: in shallowCopy when a code compaction is caused by + machine code + to bytecode pc mapping. */ + + /* CoInterpreter>>#convertFrame:toInterpreterFrame: */ +static NoDbgRegParms void +convertFrametoInterpreterFrame(char *theFP, sqInt pcDelta) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogBlockMethod *cogMethod; + CogMethod *homeMethod; + sqInt methodHeader; + sqInt methodObj; + sqInt oop; + char *p; + usqInt startBcpc; + usqInt theIP; + char *theNewSP; + StackPage *thePage; + char *theSP; + + thePage = ((StackPage *) 0); + assert(isMachineCodeFrame(theFP)); + if (theFP == GIV(framePointer)) { + theIP = GIV(instructionPointer); + theSP = GIV(stackPointer); + } + else { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(theFP == ((thePage->headFP))); + theSP = (thePage->headSP); + theIP = longAt(theSP); + } + + /* begin mframeCogMethod: */ + cogMethod = ((CogBlockMethod *) ((longAt(theFP + FoxMethod)) & MFMethodMask)); + if ((((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0) + && (!((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)))) { + /* cmHomeMethod */ + if (((cogMethod->cmType)) >= CMMethod) { + homeMethod = ((CogMethod *) cogMethod); + } + else { + assert(CMBlock == (cogMethod->cmType)); + homeMethod = ((CogMethod *) ((((usqInt)cogMethod)) - ((cogMethod->homeOffset)))); + } + methodHeader = (homeMethod->methodHeader); + methodObj = (homeMethod->methodObject); + startBcpc = (cogMethod->startpc); + } + else { + methodHeader = ((((CogMethod *) cogMethod))->methodHeader); + methodObj = ((((CogMethod *) cogMethod))->methodObject); + startBcpc = (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop; + } + + /* Map the machine code instructionPointer to the interpreter instructionPointer of the branch. */ + theIP = bytecodePCForstartBcpcin(theIP, startBcpc, cogMethod); + + /* fetchByte uses pre-increment, so pre decrement */ + theIP = (((methodObj + BaseHeaderSize) + theIP) - pcDelta) - 1; + assert(validInstructionPointerinMethodframePointer(theIP, methodObj, theFP)); + + /* Make space for the two extra fields in an interpreter frame */ + for (p = theSP; p <= (theFP + FoxMFReceiver); p += BytesPerWord) { + oop = longAt(p); + longAtput((p - BytesPerWord) - BytesPerWord,longAt(p)); + } + + /* Fill in the fields */ + longAtput(theFP + FoxIFrameFlags,/* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(((cogMethod->cmNumArgs))) << ((BytesPerWord * 8) - 8))))) + (((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag + ? 1ULL << ((BytesPerWord * 8) - 16) + : 0))) + (((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag + ? 1ULL << ((BytesPerWord * 8) - 24) + : 0)) + : ((1 + ((((usqInt)(((cogMethod->cmNumArgs))) << 8)))) + (((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag + ? 0x10000 + : 0))) + (((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag + ? 0x1000000 + : 0)))); + longAtput(theFP + FoxIFSavedIP,theIP); + longAtput(theFP + FoxMethod,methodObj); + if (theFP == GIV(framePointer)) { + GIV(stackPointer) = (GIV(stackPointer) - BytesPerWord) - BytesPerWord; + GIV(instructionPointer) = theIP; + + /* begin setMethod:methodHeader: */ + GIV(method) = methodObj; + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + } + else { + theNewSP = (theSP - BytesPerWord) - BytesPerWord; + (thePage->headSP = theNewSP); + longAtput(theNewSP,ceReturnToInterpreterPC()); + } +} + + /* CoInterpreter>>#cReturnAddressAddress */ +usqInt +cReturnAddressAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(CReturnAddress)))); +} + + /* CoInterpreter>>#cStackPointerAddress */ +usqInt +cStackPointerAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(CStackPointer)))); +} + + +/* Divorce all frames that satisfy criterion and answer the current + activeContext. + */ + + /* CoInterpreter>>#divorceAllFramesSuchThat: */ +static NoDbgRegParms sqInt +divorceAllFramesSuchThat(sqInt (*criterion)(char *fp)) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + StackPage *aPage; + char *calleeFP; + sqInt divorcedSome; + sqInt i; + sqInt object; + sqInt theContext; + char *theFP; + char *theSP; + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l1; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l1: + + /* begin ensurePushedInstructionPointer */ + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + do { + if (GIV(stackPage)) { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + + /* This is needed for the assert in externalDivorceFrame:andContext: + Slang can't currently cope with the lack of the variable here. + Something to do with the preceding statement. Take it out + and the code is good. leave it in and we get do { ... } while(l1:) */ + + /* begin divorceSomeFramesIf: */ + divorcedSome = 0; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + aPage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(aPage))) { + /* this to avoid assert in externalDivorceFrame:andContext: */ + markStackPageMostRecentlyUsed(GIV(stackPage)); + + /* begin divorceAFrameIf:in: */ + calleeFP = ((char *) 0); + theFP = (aPage->headFP); + theSP = (aPage->headSP); + + /* theSP points at hottest item on frame's stack */ + theSP += BytesPerWord; + while (1) { + if (criterion(theFP)) { + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + theContext = longAt(theFP + FoxThisContext); + goto l4; + } + theContext = marryFrameSP(theFP, theSP); + /* end ensureFrameIsMarried:SP: */ +l4: + externalDivorceFrameandContext(theFP, theContext); + if (theFP == GIV(framePointer)) { + GIV(framePointer) = 0; + } + goto l2; + } + calleeFP = theFP; + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!(theFP != 0)) break; + /* theSP points at stacked hottest item on frame's stack */ + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(calleeFP))); + theSP = (calleeFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(calleeFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(calleeFP))->cmNumArgs) + : byteAt((calleeFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + goto l3; +l2: + divorcedSome = 1; +l3:; + } + } + } while(divorcedSome); + return activeContext; +} + + /* CoInterpreter>>#dumpStackAlignmentInfo */ +void +dumpStackAlignmentInfo(void) +{ + printf("STACK_ALIGN_BYTES:%d STACK_ALIGN_MASK:%d STACK_FP_ALIGNMENT:%d STACK_SP_ALIGNMENT:%d\n", + ((int) STACK_ALIGN_BYTES), + ((int) STACK_ALIGN_MASK), + ((int) STACK_FP_ALIGNMENT), + ((int) STACK_SP_ALIGNMENT)); +} + + +/* Map all native pcs to bytecoded pcs in all contexts whose method matches + criterion. + */ + + /* CoInterpreter>>#ensureAllContextsHaveBytecodePCsIf: */ +static NoDbgRegParms void +ensureAllContextsHaveBytecodePCsIf(sqInt (*criterion)(sqInt methodOop)) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt pc; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + pc = 0; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (((((((pc = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))))))) & 7) == 1)) + && ((pc < 0) + && (criterion((followObjFieldofObject(MethodIndex, obj))))))) { + widowOrForceToBytecodePC(obj); + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } +} + + +/* Make sure the context has a byetcode pc. Can only be used on single + contexts. + */ + + /* CoInterpreter>>#ensureContextHasBytecodePC: */ +static NoDbgRegParms void +ensureContextHasBytecodePC(sqInt aContext) +{ + sqInt pc; + + assert(!(isMarriedOrWidowedContext(aContext))); + pc = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + if (((((pc) & 7) == 1)) + && (((pc = (pc >> 3))) < 0)) { + pc = mustMapMachineCodePCcontext(pc, aContext); + assert(validBCPCinMethod((pc >> 3), fetchPointerofObject(MethodIndex, aContext))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aContext)) + && (!(isForwarded(aContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, aContext, pc)); + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),pc); + } +} + + +/* Safety to give the JIT lattitude in calling convention. Conceptually, + returning a value to a context involves pushing that value onto the stack. + This is used + in Squeak methods such as ContextPart>>jump + jump + | top | + thisContext sender push: nil. + stackp = 0 ifTrue: [self stepToSendOrReturn]. + stackp = 0 ifTrue: [self push: nil]. + top := self pop. + thisContext privSender: self. + ^top + Here jump may pop the value of a temporary variable off the stack which + will, conceptually and, in the interpreter, actually, get pushed back on + return. But + if the JIT is mapping the stack to registers disaster may ensue since the + value may not get pushed to the stack and code may access an invalid value + (e.g. a pc). + + The solution is to fall back on the interpreter. If the stack pointer is + changed we + also ensure the pc is a bytecode pc (+ive) which will cause + makeBaseFrameFor: to create an interpreter frame if the context is + executed again. */ + + /* CoInterpreter>>#ensureContextIsExecutionSafeAfterAssignToStackPointer: */ +static NoDbgRegParms void +ensureContextIsExecutionSafeAfterAssignToStackPointer(sqInt aContext) +{ + ensureContextHasBytecodePC(aContext); +} + + +/* Execute a CogMethod from a linked send. The receiver, + arguments and return address are on the Smalltalk stack. First + push the entry-point and finally the register argument(s). Then write + back the frame pointers and call the routine that will pop off the + register argument(s) and jump to the entry by executing a return + instruction. + In the simple jit only the receiver gets passed in registers, so only the + receiver gets pushed. */ + + /* CoInterpreter>>#executeCogMethod:fromLinkedSendWithReceiver: */ +void +executeCogMethodfromLinkedSendWithReceiver(CogMethod *cogMethod, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + char *sp; + + assertCStackWellAligned(); + assert(isMachineCodeFrame(GIV(framePointer))); + assertValidExecutionPointersimbarline(longAt(GIV(stackPointer)), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + if ((numRegArgs()) > 0) { + if (((cogMethod->cmNumArgs)) <= (numRegArgs())) { + callRegisterArgCogMethodatreceiver(cogMethod, entryOffset(), rcvr); + } + } + + /* dont use and: so as to get Slang to inline cogit numRegArgs > 0 */ + object = (((sqInt)cogMethod)) + (entryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((sp = GIV(stackPointer) - BytesPerWord),rcvr); + GIV(stackPointer) = sp; + ceCallCogCodePopReceiverReg(); +} + + +/* Execute a CogMethod from an unlinked send. The receiver, + arguments and return address are on the Smalltalk stack. First + push the entry-point and finally the register argument(s). Then write + back the frame pointers and call the routine that will pop off the + register argument(s) and jump to the entry by executing a return + instruction. + In the simple jit only the receiver gets passed in registers, so only the + receiver gets pushed. */ + + /* CoInterpreter>>#executeCogMethod:fromUnlinkedSendWithReceiver: */ +static NoDbgRegParms void +executeCogMethodfromUnlinkedSendWithReceiver(CogMethod *cogMethod, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + char *sp; + + assertCStackWellAligned(); + assert(isMachineCodeFrame(GIV(framePointer))); + assertValidExecutionPointersimbarline(longAt(GIV(stackPointer)), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + if ((numRegArgs()) > 0) { + if (((cogMethod->cmNumArgs)) <= (numRegArgs())) { + callRegisterArgCogMethodatreceiver(cogMethod, noCheckEntryOffset(), rcvr); + } + } + + /* dont use and: so as to get Slang to inline cogit numRegArgs > 0 */ + object = (((sqInt)cogMethod)) + (noCheckEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((sp = GIV(stackPointer) - BytesPerWord),rcvr); + GIV(stackPointer) = sp; + ceCallCogCodePopReceiverReg(); +} + + +/* Execute a closed PIC from a linked send, to redispatch based on the rcvr. + The receiver, arguments and return address are on the Smalltalk stack. + First push the entry-point and finally the register argument(s). Then + write back the frame pointers and call the routine that will pop off the + register argument(s) and jump to the entry by executing a return + instruction. + In the simple jit only the receiver gets passed in registers, so only the + receiver gets pushed. */ + + /* CoInterpreter>>#executeCogPIC:fromLinkedSendWithReceiver:andCacheTag: */ +void +executeCogPICfromLinkedSendWithReceiverandCacheTag(CogMethod *cogPIC, sqInt rcvr, sqInt cacheTag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + char *sp; + + assertCStackWellAligned(); + assert(isMachineCodeFrame(GIV(framePointer))); + assertValidExecutionPointersimbarline(longAt(GIV(stackPointer)), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + object = (((sqInt)cogPIC)) + (entryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + if ((numRegArgs()) > 0) { + if (((cogPIC->cmNumArgs)) <= (numRegArgs())) { + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),cacheTag); + GIV(stackPointer) = sp; + switch ((cogPIC->cmNumArgs)) { + case 0: + ceCall0ArgsPIC(); + break; + case 1: + ceCall1ArgsPIC(); + break; + case 2: + ceCall2ArgsPIC(); + break; + default: +; + } + error("not reached"); + } + } + + /* dont use and: so as to get Slang to inline cogit numRegArgs > 0 */ + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),rcvr); + GIV(stackPointer) = sp; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),cacheTag); + GIV(stackPointer) = sp; + ceCallCogCodePopReceiverAndClassRegs(); +} + + +/* Execute newMethod - either primitiveFunctionPointer must be set directly + (i.e. from primitiveExecuteMethod et al), or it would have been set + probing the method cache (i.e. primitivePerform et al). */ + + /* CoInterpreter>>#executeNewMethod */ +static sqInt +executeNewMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int inInterpreter; + sqInt methodHeader; + + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + if (primitiveFunctionPointer) { + if ((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex) { + externalQuickPrimitiveResponse(); + returntoExecutive(popStack(), inInterpreter); + return null; + } + + /* slowPrimitiveResponse may of course context-switch. If so we must reenter the + new process appopriately, returning only if we've reached here directly from the + interpreter and have found an interpreter frame. The instructionPointer tells us + from whence we came. */ + if (slowPrimitiveResponse()) { + returntoExecutive(popStack(), inInterpreter); + return null; + } + } + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* if not primitive, or primitive failed, activate the method */ + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,((sqInt)GIV(instructionPointer))); + GIV(instructionPointer) = ceReturnToInterpreterPC(); + } + activateNewCogMethodinInterpreter(((CogMethod *) methodHeader), inInterpreter); + } + else { + activateNewMethod(); + } + return 0; +} + + +/* Execute newMethod - either primitiveFunctionPointer must be set directly + (i.e. from primitiveExecuteMethod et al), or it would have been set + probing the method cache (i.e. primitivePerform et al). + Eagerly compile it if appropriate so that doits are fast. */ + + /* CoInterpreter>>#executeNewMethodJitting */ +static sqInt +executeNewMethodJitting(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int inInterpreter; + sqInt methodHeader; + + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + if (primitiveFunctionPointer) { + if ((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex) { + externalQuickPrimitiveResponse(); + returntoExecutive(popStack(), inInterpreter); + return null; + } + + /* slowPrimitiveResponse may of course context-switch. If so we must reenter the + new process appopriately, returning only if we've reached here directly from the + interpreter and have found an interpreter frame. The instructionPointer tells us + from whence we came. */ + if (slowPrimitiveResponse()) { + returntoExecutive(popStack(), inInterpreter); + return null; + } + } + + /* Eagerly compile it if appropriate so that doits are fast. */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if (!((!(methodHeader & (smallIntegerTag()))))) { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(nilObj)); + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + + /* if not primitive, or primitive failed, activate the method */ + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,((sqInt)GIV(instructionPointer))); + GIV(instructionPointer) = ceReturnToInterpreterPC(); + } + activateNewCogMethodinInterpreter(((CogMethod *) methodHeader), inInterpreter); + } + else { + activateNewMethod(); + } + return 0; +} + + +/* Fetch an instance variable from a maybe married context. + If the context is still married compute the value of the + relevant inst var from the spouse frame's state. + + If the context is single but has a negative instruction pointer + recognise that the instruction pointer is actually into machine + code and convert it to the corresponding bytecode pc. */ + + /* CoInterpreter>>#externalInstVar:ofContext: */ +static NoDbgRegParms sqInt +externalInstVarofContext(sqInt offset, sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt value; + + assert(isContext(aContext)); + assert(offset <= (ReceiverIndex + (checkStackPointerForMaybeMarriedContext(aContext)))); + + /* method, closureOrNil & receiver need no special handling; only + sender, pc & stackp have to be computed for married contexts. */ + if (!(offset <= StackPointerIndex)) { + return longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(offset) << (shiftForWord())))))); + } + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))) { + return fetchPointerofMarriedContext(offset, aContext); + } + value = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(offset) << (shiftForWord())))))); + + /* Why not update the slot to refer to the mapped pc? We're damned if we do, and damned if + we don't. If it is and the context is live and is returned to, then the pc will be mapped back + to machine code. If we don't then the mapping will be repeated on every access. What we + need are stats that show whether or not a read here predicts one or other outcome with any + certainty. For now, we keep it as it has been for a long time and do not update the slot. */ + if ((offset == InstructionPointerIndex) + && (((((value) & 7) == 1)) + && ((((int) value)) < 0))) { + return mustMapMachineCodePCcontext((value >> 3), aContext); + } + return value; +} + + +/* Find the compiled method to be run when the current messageSelector is + looked up in the + class/classIndex classTagArg, setting the values of newMethod and + primitiveFunctionPointer. + */ + + /* CoInterpreter>>#findNewMethodInClassTag: */ +static NoDbgRegParms void +findNewMethodInClassTag(sqInt classTagArg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt classTag; + CogMethod *cogMethod; + sqInt fieldIndex; + sqInt methodHeader; + + if (lookupInMethodCacheSelclassTag(GIV(messageSelector), classTagArg)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + /* entry was not found in the cache; perhaps soemthing was forwarded. */ + classTag = classTagArg; + if ((/* isOopForwarded: */ + ((!(GIV(messageSelector) & (tagMask())))) + && ((!((longAt((void *)(GIV(messageSelector)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + || (classTag == (isForwardedObjectClassIndexPun()))) { + if (/* isOopForwarded: */ + ((!(GIV(messageSelector) & (tagMask())))) + && ((!((longAt((void *)(GIV(messageSelector)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + GIV(messageSelector) = handleForwardedSelectorFaultFor(GIV(messageSelector)); + } + if (classTag == (isForwardedObjectClassIndexPun())) { + classTag = handleForwardedSendFaultForTag(classTag); + } + if (lookupInMethodCacheSelclassTag(GIV(messageSelector), classTag)) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + return; + } + } + + /* entry was not found in the cache; look it up the hard way */ + + /* begin classForClassTag: */ + /* begin classAtIndex: */ + assert((classTag >= 0) + && ((classTag <= (tagMask())) + || ((classTag >= (arrayClassIndexPun())) + && (classTag <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classTag)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + GIV(lkupClass) = null; + goto l1; + } + fieldIndex = classTag & ((1U << (classTableMajorIndexShift())) - 1); + GIV(lkupClass) = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classForClassTag: */ +l1: + lookupMethodInClass(GIV(lkupClass)); + addNewMethodToCache(GIV(lkupClass)); + } +} + + +/* The flag bit can be used to flag methods that are interpreted, if it has + been requested + from the image header flags. */ + + /* CoInterpreter>>#flagMethodAsInterpreted: */ +static NoDbgRegParms void +flagMethodAsInterpreted(sqInt aMethod) +{ + sqInt rawHeader; + sqInt realHeader; + + assert(flagInterpretedMethods()); + rawHeader = longAt((void *)((aMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + realHeader = ((!(rawHeader & (smallIntegerTag()))) + ? ((((CogMethod *) rawHeader))->methodHeader) + : rawHeader); + realHeader = realHeader | ((((usqInt)(1U << MethodHeaderFlagBitPosition) << 3) | 1)); + + /* begin isCogMethodReference: */ + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + ((((CogMethod *) rawHeader))->methodHeader = realHeader); + } + else { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aMethod)) + && (!(isForwarded(aMethod)))); + assert(validStorePointerUncheckedArgs(0, aMethod, realHeader)); + longAtput((void *)((aMethod + BaseHeaderSize) + (0U << (shiftForWord()))),realHeader); + } +} + + +/* Flush the references to external functions from plugin primitives. + Then continue execution answering self. + This will force a reload of those primitives when accessed next. + Note: We must flush the method cache here also, so that any failed + primitives are looked up again. + Override to ensure that any and all activations of an external method + have a bytecode pc so that if code generation changes (e.g. a primitive + method is used, unloaded, and the reloaded primitive is marked with + the FastCPrimitiveFlag) stale machine code pcs have been eliminated. + THIS MUST BE INVOKED IN THE CONTEXT OF A PRIMITIVE. */ + + /* CoInterpreter>>#flushExternalPrimitives */ +static void +flushExternalPrimitives(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt senderOop; + sqInt startObject; + char *theFP; + char *theFrame; + StackPage *thePage; + sqInt top; + + activeContext = divorceAllFramesSuchThat(isMachineCodeFrameForExternalPrimitiveMethod); + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (((byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + flushExternalPrimitiveOf(obj); + } + else { + if (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin mapToBytecodePCIfActivationOfExternalMethod: */ + if (isExternalMethodInPlugin(longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))))) { + if (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (isWidowedContext(obj)) { + goto l1; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + externalDivorceFrameandContext(theFP, obj); + } + } + ensureContextHasBytecodePC(obj); + } + /* end mapToBytecodePCIfActivationOfExternalMethod: */ +l1:; + } + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2: + assert(oopisGreaterThan(obj, prevObj)); + } + unlinkSendsToMethodsSuchThatAndFreeIf(cogMethodHasExternalPrim, 1); + + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + + /* begin flushExternalPrimitiveTable */ + memset(externalPrimitiveTable, 0, MaxExternalPrimitiveTableSize * (sizeof(externalPrimitiveTable[0]))); + GIV(externalPrimitiveTableFirstFreeIndex) = 0; + + /* If flushing led to divorce continue in the interpreter. */ + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + ceInvokeInterpret(); + } + + /* If not, work out where we are and continue */ + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + assert(((thePage->headFP)) == theFrame); + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* Arrange that any and all cog methods with machine code primitives can be + and are discarded. + Hence scan contexts and map their PCs to bytecode PCs if required, and + scan frames, divorcing + the frames of activations if required. Then continue execution answering + result. THIS MUST BE + INVOKED IN THE CONTEXT OF A PRIMITIVE. It exists to support + vmParameterAt:put:. */ + + /* CoInterpreter>>#flushMethodsWithMachineCodePrimitivesAndContinueAnswering: */ +static NoDbgRegParms void +flushMethodsWithMachineCodePrimitivesAndContinueAnswering(sqInt result) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt senderOop; + char *sp; + char *theFrame; + StackPage *thePage; + sqInt top; + + activeContext = divorceAllFramesSuchThat(isMachineCodeFrameForCogMethodWithMachineCodePrim); + ensureAllContextsHaveBytecodePCsIf(methodHasMachineCodePrimitiveMethod); + unlinkSendsToMethodsSuchThatAndFreeIf(cogMethodHasMachineCodePrim, 1); + + /* If the top frame was divorced then continue in the interpreter. */ + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* pop bogus machine-code instructionPointer, arguments and receiver */ + + /* begin pop:thenPush: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 2) - 1) * BytesPerWord)),result); + GIV(stackPointer) = sp; + ceInvokeInterpret(); + } + + /* If not, work out where we are and continue */ + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + assert(((thePage->headFP)) == theFrame); + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + + /* begin pop:thenPush: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),result); + GIV(stackPointer) = sp; +} + + /* CoInterpreter>>#followForwardedFieldsInCurrentMethod */ +static void +followForwardedFieldsInCurrentMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cogMethod; + sqInt methodField; + + if ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())) { + /* begin mframeHomeMethod: */ + methodField = longAt(GIV(framePointer) + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + cogMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + goto l1; + } + cogMethod = ((CogMethod *) (methodField & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + followForwardedObjectFieldstoDepth((cogMethod->methodObject), 0); + followForwardedLiteralsIn(cogMethod); + } + else { + followForwardedObjectFieldstoDepth(GIV(method), 0); + } +} + + +/* A more thorough version of followForwardingPointersInStackZone that also + follows all temporaries (but not stack contents after the temps). + This allows removal of the TempVectReadBarrier in the IGC */ + + /* CoInterpreter>>#followForwardingPointersOfReceiverAndTemporariesInStackZone */ +static void +followForwardingPointersOfReceiverAndTemporariesInStackZone(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + char *frameRcvrOffset; + sqInt header; + sqInt i; + sqInt methodHeader; + sqInt newOop; + sqInt offset; + usqInt oop; + sqInt referent; + char *theFP; + usqInt theIPPtr; + StackPage *thePage; + char *theSP; + + callerFP = ((char *) 0); + if (!GIV(stackPage)) { + assert((!(GIV(mostRecentlyUsedPage))) + || (isFree(GIV(mostRecentlyUsedPage)))); + return; + } + + /* the system must be snapshotting; nothing to do... */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((thePage->baseFP)) { + assert(ifCurrentStackPageHasValidHeadPointers(thePage)); + theFP = (thePage->headFP); + + /* Skip the instruction pointer on top of stack of inactive pages. */ + theIPPtr = (!(thePage == GIV(stackPage)) + ? ((usqInt)((thePage->headSP))) + : 0); + while (1) { + assert(addressIsInPage(thePage, theFP)); + assert((!theIPPtr) + || (addressIsInPage(thePage, ((void *)theIPPtr)))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + oop = longAt(theFP + FoxMFReceiver); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxMFReceiver,followForwarded(oop)); + } + assert(!(isForwarded(((mframeHomeMethod(theFP))->methodObject)))); + frameRcvrOffset = theFP + FoxMFReceiver; + methodHeader = ((mframeHomeMethod(theFP))->methodHeader); + } + else { + oop = longAt(theFP + FoxIFReceiver); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxIFReceiver,followForwarded(oop)); + } + oop = longAt(theFP + FoxMethod); + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + newOop = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(newOop & (tagMask())))) + && ((!((longAt((void *)(newOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + newOop = longAt((void *)((newOop + BaseHeaderSize) + (0U << (shiftForWord())))); + } + offset = newOop - oop; + if ((theIPPtr) + && ((longAt((void *)(theIPPtr))) > oop)) { + longAtput((void *)(theIPPtr),(longAt((void *)(theIPPtr))) + offset); + } + longAtput(theFP + FoxIFSavedIP,(longAt(theFP + FoxIFSavedIP)) + offset); + longAtput(theFP + FoxMethod,(oop = newOop)); + } + frameRcvrOffset = theFP + FoxIFReceiver; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(oop)); + header = longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + } + theSP = frameRcvrOffset - (((((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F) * BytesPerWord); + while (theSP <= frameRcvrOffset) { + oop = longAt(theSP); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + longAtput(theSP,oop); + } + theSP += BytesPerWord; + } + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) + && ((!((longAt((void *)(longAt(theFP + FoxThisContext)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxThisContext,followForwarded(longAt(theFP + FoxThisContext))); + } + offset = (FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))); + oop = longAt(theFP + offset); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + offset,followForwarded(oop)); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theIPPtr = ((usqInt)(theFP + FoxCallerSavedIP)); + theFP = callerFP; + } + + /* And finally follow the saved context and the caller context. */ + theSP = ((thePage->baseAddress)) - BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + longAtput(theSP,followForwarded(oop)); + } + theSP += BytesPerWord; + } + } + } +} + + +/* Spur's become: is lazy, turning the becommed object into a forwarding + object to the other. + The read-barrier is minimised by arranging that forwarding pointers will + fail a method cache + probe, since notionally objects' internals are accessed only via sending + messages to them, + the exception is primitives that access the internals of the non-receiver + argument(s). + To avoid a read barrier on bytecode, literal and inst var fetch and + non-local return, we scan + the receivers (including the stacked receiver for non-local return) and + method references + in the stack zone and follow any forwarded ones. This is of course way + cheaper than + scanning all of memory as in the old become. + + Override to handle machine code frames */ + + /* CoInterpreter>>#followForwardingPointersOfReceiversInStackZone */ +static void +followForwardingPointersOfReceiversInStackZone(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt i; + sqInt newOop; + sqInt offset; + usqInt oop; + char *theFP; + usqInt theIPPtr; + StackPage *thePage; + char *theSP; + + callerFP = ((char *) 0); + if (!GIV(stackPage)) { + assert((!(GIV(mostRecentlyUsedPage))) + || (isFree(GIV(mostRecentlyUsedPage)))); + return; + } + + /* the system must be snapshotting; nothing to do... */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((thePage->baseFP)) { + assert(ifCurrentStackPageHasValidHeadPointers(thePage)); + theFP = (thePage->headFP); + + /* Skip the instruction pointer on top of stack of inactive pages. */ + theIPPtr = (!(thePage == GIV(stackPage)) + ? ((usqInt)((thePage->headSP))) + : 0); + while (1) { + assert(addressIsInPage(thePage, theFP)); + assert((!theIPPtr) + || (addressIsInPage(thePage, ((void *)theIPPtr)))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + oop = longAt(theFP + FoxMFReceiver); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxMFReceiver,followForwarded(oop)); + } + assert(!(isForwarded(((mframeHomeMethod(theFP))->methodObject)))); + } + else { + oop = longAt(theFP + FoxIFReceiver); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxIFReceiver,followForwarded(oop)); + } + oop = longAt(theFP + FoxMethod); + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + newOop = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(newOop & (tagMask())))) + && ((!((longAt((void *)(newOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + newOop = longAt((void *)((newOop + BaseHeaderSize) + (0U << (shiftForWord())))); + } + offset = newOop - oop; + if ((theIPPtr) + && ((longAt((void *)(theIPPtr))) > oop)) { + longAtput((void *)(theIPPtr),(longAt((void *)(theIPPtr))) + offset); + } + longAtput(theFP + FoxIFSavedIP,(longAt(theFP + FoxIFSavedIP)) + offset); + longAtput(theFP + FoxMethod,(oop = newOop)); + } + } + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) + && ((!((longAt((void *)(longAt(theFP + FoxThisContext)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + FoxThisContext,followForwarded(longAt(theFP + FoxThisContext))); + } + offset = (FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))); + oop = longAt(theFP + offset); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(theFP + offset,followForwarded(oop)); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theIPPtr = ((usqInt)(theFP + FoxCallerSavedIP)); + theFP = callerFP; + } + + /* And finally follow the saved context and the caller context. */ + theSP = ((thePage->baseAddress)) - BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + longAtput(theSP,followForwarded(oop)); + } + theSP += BytesPerWord; + } + } + } +} + + +/* In the StackInterpreter the saved ip field of a base frame holds the + base frame's caller context. But in the Cog VM the first word on the + stack holds the base frame's caller context, which is immediately + above the stacked receiver. */ + + /* CoInterpreter>>#frameCallerContext: */ +static NoDbgRegParms sqInt +frameCallerContext(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + StackPage *thePage; + + assert(isBaseFrame(theFP)); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + return callerContextOrNil; +} + + /* CoInterpreter>>#frameHasContext: */ +static NoDbgRegParms int +frameHasContext(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0); +} + + +/* */ + + /* CoInterpreter>>#frameIsBlockActivation: */ +static NoDbgRegParms int +frameIsBlockActivation(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0); +} + + /* CoInterpreter>>#frameMethodObject: */ +static NoDbgRegParms sqInt +frameMethodObject(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); +} + + +/* See encodeFrameFieldHasContext:numArgs: */ + + /* CoInterpreter>>#frameNumArgs: */ +static NoDbgRegParms usqInt +frameNumArgs(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)); +} + + /* CoInterpreter>>#framePointerAddress */ +usqInt +framePointerAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(framePointer)))); +} + + /* CoInterpreter>>#framePrintDescription: */ +static NoDbgRegParms void +framePrintDescription(sqInt it) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!it) { + return; + } + if (it == GIV(nilObj)) { + fprintf(GIV(transcript), "=nil"); + return; + } + if (it == (ceReturnToInterpreterPC())) { + fprintf(GIV(transcript), "=ceReturnToInterpreterPC"); + return; + } + if (it == (ceBaseFrameReturnPC())) { + fprintf(GIV(transcript), "=ceBaseFrameReturnPC"); + return; + } + if (it == (ceCannotResumePC())) { + fprintf(GIV(transcript), "=ceCannotResumePC"); + return; + } + fprintf(GIV(transcript), + "=%" PRIdSQINT "", + it); +} + + /* CoInterpreter>>#frameReceiver: */ +static NoDbgRegParms sqInt +frameReceiver(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? longAt(theFP + FoxMFReceiver) + : longAt(theFP + FoxIFReceiver)); +} + + /* CoInterpreter>>#functionPointerForCompiledMethod:primitiveIndex:primitivePropertyFlagsInto: */ +void +(*functionPointerForCompiledMethodprimitiveIndexprimitivePropertyFlagsInto(sqInt methodObj, sqInt primitiveIndex, sqInt *flagsPtr))(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt flags; + void (*functionPointer)(void); + sqInt header; + sqInt headerSqInt; + sqInt index; + sqInt lit; + sqInt litSqInt; + sqInt metadata; + usqInt metadataFlags; + sqInt primIdx; + sqInt targetFunctionIndex; + + flags = 0; + if (flagsPtr) { + flagsPtr[0] = ((flags = primitivePropertyFlagsprimIndex(methodObj, primitiveIndex))); + } + if (primitiveIndex == PrimNumberFFICall) { + /* begin functionForPrimitiveCallout */ + if ((((sqInt)primitiveCalloutPointer)) == -1) { + reloadPrimitiveCalloutPointer(); + } + if (flagsPtr) { + if (GIV(primCalloutIsExternal)) { + flagsPtr[0] = (flags | PrimCallIsExternalCall); + } + } + return primitiveCalloutPointer; + } + if (primitiveIndex == PrimNumberExternalCall) { + /* begin attemptToLinkExternalPrimitive: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + headerSqInt = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* primitiveIndexOfMethod:header: */ + if (((header & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((header >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIdx = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIdx = 0; + } + lit = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + assert((primIdx == PrimNumberExternalCall) + && (((literalCountOfMethodHeader(header)) > 0) + && ((isArray(lit)) + && ((numSlotsOf(lit)) == 4)))); + targetFunctionIndex = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord())))))); + assert((((targetFunctionIndex) & 7) == 1)); + if (!((targetFunctionIndex >> 3))) { + linkExternalCallerrInto(lit, null); + } + if (flagsPtr) { + /* N.B. We only support the FastCPrimitiveFlag on Spur because Spur will not run a GC to + satisfy an allocation in a primitive. The V3 ObjectMemory will and hence the depth of + stack needed in a V3 primitive is probably too large to safely execute on a stack page. */ + metadata = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFlagsIndex) << (shiftForWord())))))); + if ((((metadata) & 7) == 1)) { + metadataFlags = ((metadata >> 3)) & SpurPrimitiveFlagsMask; + + /* combine the specific external primitive's flags with the base flags. + Hence e.g. if profiling is in effect (as indicated by PrimCallCollectsProfileSamples + in the base flags) it remains in effect after combining the specific flags. */ + flags = flags | metadataFlags; + } + if (numBytesOf(longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralModuleNameIndex) << (shiftForWord())))))))) { + flags = flags | PrimCallIsExternalCall; + } + flagsPtr[0] = flags; + } + + /* begin functionForPrimitiveExternalCall: */ + if (!((literalCountOf(methodObj)) > 0)) { + return primitiveExternalCall; + } + + /* Check if it's an array of length 4 */ + litSqInt = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(litSqInt & (tagMask())))) + && (((byteAt((void *)(litSqInt + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((lengthOf(litSqInt)) == 4))) { + return primitiveExternalCall; + } + index = longAt((void *)((litSqInt + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord())))))); + if (!(((((index) & 7) == 1)) + && ((((index = (index >> 3))) > 0) + && (index <= MaxExternalPrimitiveTableSize)))) { + return primitiveExternalCall; + } + functionPointer = externalPrimitiveTable[index - 1]; + if (!functionPointer) { + return primitiveExternalCall; + } + return functionPointer; + } + + /* begin functionPointerFor:inClass: */ + return ((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]); +} + + +/* currentBytecode will be private to the main dispatch loop in the generated + code. This method allows the currentBytecode to be retrieved from global + variables. Override to answer -1 if we're not in an interpreter frame. */ + + /* CoInterpreter>>#getCurrentBytecode */ +sqInt +getCurrentBytecode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)GIV(framePointer))) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)GIV(framePointer))) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)GIV(framePointer))) <= (((usqInt)GIV(pages)))))))) + && (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) + ? byteAt((void *)(GIV(instructionPointer))) + : -1); +} + + +/* Handle a send fault that may be due to a send to a forwarded object. + Unforward the receiver on the stack and answer it. */ + + /* CoInterpreter>>#handleForwardedSendFaultForReceiver:stackDelta: */ +static NoDbgRegParms sqInt +handleForwardedSendFaultForReceiverstackDelta(sqInt forwardedReceiver, sqInt stackDelta) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt rcvr; + sqInt rcvrStackIndex; + + + /* should *not* be a super send, so the receiver should be forwarded. */ + assert(isOopForwarded(forwardedReceiver)); + rcvrStackIndex = GIV(argumentCount) + stackDelta; + assert((stackValue(rcvrStackIndex)) == forwardedReceiver); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(forwardedReceiver)); + rcvr = longAt((void *)((forwardedReceiver + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord())))); + } + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (rcvrStackIndex * BytesPerWord),rcvr); + followForwardedFrameContentsstackPointer(GIV(framePointer), GIV(stackPointer) + ((rcvrStackIndex + 1) * BytesPerWord)); + if (isPointers(/* frameReceiver: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? longAt(GIV(framePointer) + FoxMFReceiver) + : longAt(GIV(framePointer) + FoxIFReceiver)))) { + followForwardedObjectFieldstoDepth(/* frameReceiver: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? longAt(GIV(framePointer) + FoxMFReceiver) + : longAt(GIV(framePointer) + FoxIFReceiver)), 0); + } + followForwardedFieldsInCurrentMethod(); + return rcvr; +} + + +/* A message send from either an open PIC or an unlinked send has not been + understood. Create a message and execute the relevant resulting MNU + method. messageSelector is an implicit argument (yuck). */ + + /* CoInterpreter>>#handleMNU:InMachineCodeTo:classForMessage: */ +static NoDbgRegParms sqInt +handleMNUInMachineCodeToclassForMessage(sqInt selectorIndex, sqInt rcvr, sqInt classForMessage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classForThisMessage; + CogMethod *cogMethod; + sqInt errSelIdx; + sqInt methodHeader; + sqInt top; + + assert(addressCouldBeOop(rcvr)); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + createActualMessageTo(classForMessage); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(selectorIndex) << (shiftForWord())))))); + if (lookupInMethodCacheSelclassTag(GIV(messageSelector), classTagForClass(GIV(lkupClass)))) { + /* begin ifAppropriateCompileToNativeCode:selector: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + } + else { + errSelIdx = lookupMNUInClass((classForThisMessage = GIV(lkupClass))); + if (errSelIdx) { + if (selectorIndex == SelectorDoesNotUnderstand) { + error("Recursive not understood error encountered"); + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + return handleMNUInMachineCodeToclassForMessage(errSelIdx, rcvr, classForThisMessage); + } + } + + /* check for coggability because method is in the cache */ + if (/* maybeMethodHasCogMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + executeCogMethodfromUnlinkedSendWithReceiver(cogMethodOf(GIV(newMethod)), rcvr); + + /* NOTREACHED */ + assert(0); + } + return interpretMethodFromMachineCode(); +} + + +/* */ + + /* CoInterpreter>>#iframeIsBlockActivation: */ +static NoDbgRegParms int +iframeIsBlockActivation(char *theFP) +{ + return (byteAt((theFP + FoxIFrameFlags) + 3)) != 0; +} + + /* CoInterpreter>>#iframeReceiver: */ +static NoDbgRegParms sqInt +iframeReceiver(char *theFP) +{ + return longAt(theFP + FoxIFReceiver); +} + + /* CoInterpreter>>#iframeSavedIP: */ +static NoDbgRegParms usqInt +iframeSavedIP(char *theFP) +{ + return ((usqInt)(longAt(theFP + FoxIFSavedIP))); +} + + +/* This is for low-level error reporting. If either of the C stack pointers + are pointing into the stack zone then write them back to framePointer + and/or stackPointer so that the stack backtrace will be up to date. Write + their original values through savedFPP & savedSPP if non-null. */ + + /* CoInterpreter>>#ifValidWriteBackStack:Pointers:Save:To: */ +void +ifValidWriteBackStackPointersSaveTo(void *theCFP, void *theCSP, char **savedFPP, char **savedSPP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (savedFPP) { + savedFPP[0] = GIV(framePointer); + } + if (savedSPP) { + savedSPP[0] = GIV(stackPointer); + } + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theCFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theCFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theCFP)) <= (((usqInt)GIV(pages)))))))) { + GIV(framePointer) = theCFP; + } + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theCSP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theCSP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theCSP)) <= (((usqInt)GIV(pages)))))))) { + GIV(stackPointer) = theCSP; + } +} + + /* CoInterpreter>>#instructionPointerAddress */ +usqInt +instructionPointerAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(instructionPointer)))); +} + + +/* Answer the bytecode pc object (i.e. SmallInteger) for an active frame. The + bytecode pc is derived from the frame's pc. If the frame is the top frame + on the current stack + the frame pc is whatever the current instruction pointer is. If the frame + is the top + frame on some other stack the frame pc is the value on top of stack. + Otherwise the + frame pc is the saved pc of the frame above. Once the frame pc is found it + must be + mapped to a bytecode pc. */ + + /* CoInterpreter>>#instructionPointerForFrame:currentFP:currentIP: */ +static NoDbgRegParms sqInt +instructionPointerForFramecurrentFPcurrentIP(char *spouseFP, char *currentFP, sqInt instrPtr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + char *fp; + char *theFPAbove; + sqInt theIP; + StackPage *thePage; + sqInt value; + + if (spouseFP == currentFP) { + theIP = instrPtr; + } + else { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(spouseFP), GIV(pages)); + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == spouseFP) { + theFPAbove = 0; + goto l1; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == spouseFP) { + theFPAbove = fp; + goto l1; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + theFPAbove = 0; + /* end findFrameAbove:inPage: */ +l1: + theIP = (theFPAbove + ? longAt(theFPAbove + FoxCallerSavedIP) + : longAt((thePage->headSP))); + } + value = contextInstructionPointerframe(theIP, spouseFP); + return ((((int) value)) < 0 + ? mustMapMachineCodePCcontext((value >> 3), longAt(spouseFP + FoxThisContext)) + : value); +} + + +/* At a rough approximation we may need to allocate up to a couple + of page's worth of contexts when switching stack pages, assigning + to senders, etc. But the snapshot primitive voids all stack pages. + So a safe margin is the size of a large context times the maximum + number of frames per page times the number of pages. */ + + /* CoInterpreter>>#interpreterAllocationReserveBytes */ +static NeverInline sqInt +interpreterAllocationReserveBytes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt maxFramesPerPage; + sqInt maxUsedBytesPerPage; + + maxUsedBytesPerPage = (stackPageFrameBytes()) + (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord); + maxFramesPerPage = (maxUsedBytesPerPage / BytesPerWord) / MFrameSlots; + return ((maxFramesPerPage * LargeContextSlots) * BytesPerOop) * GIV(numStackPages); +} + + +/* Execute a method interpretively from machine code. We assume (require) + that newMethod + messageSelector, primitiveFunctionPointer and argumentCount have been set + in the caller. + Once evaluated either continue in the interpreter via a jongjmp or in + machine code via an + enilopmart (a form of longjmp - a stinking rose by any other name). */ + + /* CoInterpreter>>#interpretMethodFromMachineCode */ +static sqInt +interpretMethodFromMachineCode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt nArgs; + char *savedFramePointer; + char *savedStackPointer; + + assertCStackWellAligned(); + assert(validInstructionPointerinFrame(GIV(instructionPointer), GIV(framePointer))); + if (primitiveFunctionPointer) { + if (primitiveFunctionPointer == primitiveInvokeObjectAsMethod) { + assert(!(isOopCompiledMethod(GIV(newMethod)))); + } + else { + assert((isOopCompiledMethod(GIV(newMethod))) + && ((primitiveIndexOf(GIV(newMethod))) != 0)); + } + + /* Invoke an interpreter primitive (because the method is to be interpreted or has not yet been + compiled). This is very similar to invoking an interpreter primitive from a compiled primitive + (see e.g. SimpleStackBasedCogit>>compileInterpreterPrimitive:). Cut back the stack pointer + (done above) to skip the return address and invoke the function. On return if it has succeeded + simply continue otherwise restore the stackPointer, collect the pc and interpret. Note that + frame building primitives such as primitiveClosureValue, primitiveEvaluateMethod et al will not + return but will instead jump into either machine code or longjmp back to the interpreter. + Assign stackPage headFP so we can tell if the primitive built a frame. We can't simply save + the framePointer since e.g. assignment to contexts (via primitiveInstVarAt:put:) can change the + framePointer. But context assignments will change both the framePointer and stackPage headFP. */ + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + if ((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex) { + externalQuickPrimitiveResponse(); + GIV(primFailCode) = 0; + } + else { + /* begin slowPrimitiveResponse */ + assert(!(isOopForwarded(stackValue(GIV(argumentCount))))); + assert((GIV(remapBufferCount)) == 0); + nArgs = GIV(argumentCount); + savedStackPointer = GIV(stackPointer); + savedFramePointer = GIV(framePointer); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if (/* recordPrimTraceForMethod: */ + (recordPrimTrace()) + && ((primTracePluginName + ? methodHasPrimitiveInPrimTracePlugin(GIV(newMethod)) + : 1))) { + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = GIV(newMethod); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + dispatchFunctionPointer(primitiveFunctionPointer); + assert(maybeLeakCheckExternalPrimCall(GIV(newMethod))); + + /* begin maybeRetryPrimitiveOnFailure */ + if (GIV(primFailCode)) { + retryPrimitiveOnFailure(); + } + if ((!GIV(primFailCode)) + && ((GIV(framePointer) == savedFramePointer) + && (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))))) { + if (GIV(stackPointer) != (savedStackPointer + (nArgs * BytesPerWord))) { + /* This is necessary but insufficient; the result may still have been written to the stack. + At least we'll know something is wrong. */ + failUnbalancedPrimitive(); + GIV(stackPointer) = savedStackPointer; + } + } + + /* Don't fail if primitive has done something radical, e.g. perform: + If we are profiling, take accurate primitive measures */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + + /* successful */ + !GIV(primFailCode); + } + if (!GIV(primFailCode)) { + returntoExecutive(popStack(), 0); + } + } + else { + assert((isOopCompiledMethod(GIV(newMethod))) + && (((primitiveIndexOf(GIV(newMethod))) == 0) + || (((functionPointerForinClass(primitiveIndexOf(GIV(newMethod)), nilObject())) == 0) + || (isNullExternalPrimitiveCall(GIV(newMethod)))))); + } + + /* if not primitive, or primitive failed, activate the method and reenter the interpreter */ + activateNewMethod(); + ceInvokeInterpret(); + + /* NOTREACHED */ + return null; +} + + +/* This version exists for asserts */ + + /* CoInterpreter>>#isCogMethodReferenceNoAssert: */ +static NoDbgRegParms int +isCogMethodReferenceNoAssert(sqInt methodHeader) +{ + return (!(methodHeader & (smallIntegerTag()))); +} + + /* CoInterpreter>>#isCogMethodReference: */ +int +isCogMethodReference(sqInt methodHeader) +{ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + return (!(methodHeader & (smallIntegerTag()))); +} + + /* CoInterpreter>>#isExternalMethodInPlugin: */ +static NoDbgRegParms sqInt +isExternalMethodInPlugin(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt headerSqInt; + sqInt literal; + usqInt numSlots; + sqInt primIdx; + + literal = 0; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + headerSqInt = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* primitiveIndexOfMethod:header: */ + if (((header & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((header >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIdx = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIdx = 0; + } + return (primIdx == PrimNumberExternalCall) + && ((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) > 0) + && ((isArray((literal = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord()))))))) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(literal)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(literal + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(literal - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4) + && ((isBytes(longAt((void *)((literal + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralModuleNameIndex) << (shiftForWord())))))))) + && (!((numBytesOf(longAt((void *)((literal + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralModuleNameIndex) << (shiftForWord())))))))) == 0)))))); +} + + +/* Answer if the argument is a full block method or not. Full block methods + (as introduced by the SistaV1BytecodeSet) are used to implement block + closures and have a reference to their enclosing block or method as their + last literal. Compiled methods have a class association as their last + literal. */ + + /* CoInterpreter>>#isFullBlockMethod: */ +sqInt +isFullBlockMethod(sqInt aMethodObj) +{ + sqInt oop; + + oop = ultimateLiteralOf(aMethodObj); + + /* begin isOopCompiledMethod: */ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())); +} + + /* CoInterpreter>>#isMachineCodeFrameForCogMethodFlaggedForBecome: */ +static NoDbgRegParms sqInt +isMachineCodeFrameForCogMethodFlaggedForBecome(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && (isCMMethodFlaggedForBecome(((CogBlockMethod *) (mframeHomeMethod(theFP))))); +} + + /* CoInterpreter>>#isMachineCodeFrameForCogMethodWithMachineCodePrim: */ +static NoDbgRegParms sqInt +isMachineCodeFrameForCogMethodWithMachineCodePrim(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && (cogMethodHasMachineCodePrim(mframeHomeMethod(theFP))); +} + + /* CoInterpreter>>#isMachineCodeFrameForCogMethodWithTooManyLiterals: */ +static NoDbgRegParms sqInt +isMachineCodeFrameForCogMethodWithTooManyLiterals(char *theFP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && (!(((/* begin literalCountOfMethodHeader: */ + assert(((((((mframeHomeMethod(theFP))->methodHeader))) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + (((((mframeHomeMethod(theFP))->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile)); +} + + /* CoInterpreter>>#isMachineCodeFrameForExternalPrimitiveMethod: */ +static NoDbgRegParms sqInt +isMachineCodeFrameForExternalPrimitiveMethod(char *theFP) +{ + CogMethod *homeMethod; + + homeMethod = ((CogMethod *) 0); + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && ((cogMethodHasMachineCodePrim((homeMethod = mframeHomeMethod(theFP)))) + && (isExternalMethodInPlugin((homeMethod->methodObject)))); +} + + /* CoInterpreter>>#isMachineCodeFrameForRealNonProfilingPrimitive: */ +static NoDbgRegParms sqInt +isMachineCodeFrameForRealNonProfilingPrimitive(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt firstLiteral; + sqInt functionName; + sqInt header; + CogMethod *homeMethod; + sqInt methodField; + sqInt methodHeader; + sqInt methodObj; + usqInt numSlots; + void (*pf)(void); + sqInt primitiveIndex; + + if (!((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()))) { + return 0; + } + + /* begin mframeHomeMethod: */ + methodField = longAt(theFP + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + homeMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + goto l1; + } + homeMethod = ((CogMethod *) (methodField & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + methodObj = (homeMethod->methodObject); + + /* begin isRealNonProfilingPrimitiveMethod: */ + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin isRealNonProfilingPrimitiveMethod:primIndex: */ + if (!(((primitiveIndex >= 1) && (primitiveIndex <= MaxPrimitiveIndex)))) { + return 0; + } + + /* Filter-out the profiling primitives themselves. */ + if (primitiveIndex == PrimNumberExternalCall) { + firstLiteral = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(firstLiteral & (tagMask())))) + && (((byteAt((void *)(firstLiteral + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(firstLiteral)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(firstLiteral + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(firstLiteral - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4))) { + return 1; + } + functionName = longAt((void *)((firstLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFunctionNameIndex) << (shiftForWord())))))); + return ((numBytesOf(functionName)) < (21)) + || (0 != (strncmp("primitiveProfile", ((char *) (firstIndexableField(functionName))), 16))); + } + + /* Answer true if there's a real primitive here, excepting the ValueNoContextSwitch primitives, + because a sample can't be taken in them, as doing so could cause a context switch. */ + pf = primitiveTable[primitiveIndex]; + return (pf != (((void *)0))) + && ((pf != (((void (*)(void)) primitiveFail))) + && ((pf != primitiveClosureValueNoContextSwitch) + && (pf != primitiveFullClosureValueNoContextSwitch))); +} + + /* CoInterpreter>>#isMachineCodeFrame: */ +static NoDbgRegParms int +isMachineCodeFrame(char *theFP) +{ + return (((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()); +} + + +/* Answer if the methodObj's primitive is one that should be profiled. + Filter-out the profiling primitives themselves. */ + + /* CoInterpreter>>#isRealNonProfilingPrimitiveMethod: */ +static NoDbgRegParms sqInt +isRealNonProfilingPrimitiveMethod(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt firstLiteral; + sqInt functionName; + sqInt header; + sqInt methodHeader; + usqInt numSlots; + void (*pf)(void); + sqInt primitiveIndex; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin isRealNonProfilingPrimitiveMethod:primIndex: */ + if (!(((primitiveIndex >= 1) && (primitiveIndex <= MaxPrimitiveIndex)))) { + return 0; + } + + /* Filter-out the profiling primitives themselves. */ + if (primitiveIndex == PrimNumberExternalCall) { + firstLiteral = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(firstLiteral & (tagMask())))) + && (((byteAt((void *)(firstLiteral + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(firstLiteral)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(firstLiteral + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(firstLiteral - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4))) { + return 1; + } + functionName = longAt((void *)((firstLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFunctionNameIndex) << (shiftForWord())))))); + return ((numBytesOf(functionName)) < (21)) + || (0 != (strncmp("primitiveProfile", ((char *) (firstIndexableField(functionName))), 16))); + } + + /* Answer true if there's a real primitive here, excepting the ValueNoContextSwitch primitives, + because a sample can't be taken in them, as doing so could cause a context switch. */ + pf = primitiveTable[primitiveIndex]; + return (pf != (((void *)0))) + && ((pf != (((void (*)(void)) primitiveFail))) + && ((pf != primitiveClosureValueNoContextSwitch) + && (pf != primitiveFullClosureValueNoContextSwitch))); +} + + +/* Answer if the methodObj's primitive is one that should be profiled. + In particular answer false for the no-context-switch value primitives and + the profiling primitives themselves. */ + + /* CoInterpreter>>#isRealNonProfilingPrimitiveMethod:primIndex: */ +static NoDbgRegParms sqInt +isRealNonProfilingPrimitiveMethodprimIndex(sqInt methodObj, sqInt primitiveIndex) +{ + sqInt firstLiteral; + sqInt functionName; + usqInt numSlots; + void (*pf)(void); + + if (!(((primitiveIndex >= 1) && (primitiveIndex <= MaxPrimitiveIndex)))) { + return 0; + } + + /* Filter-out the profiling primitives themselves. */ + if (primitiveIndex == PrimNumberExternalCall) { + firstLiteral = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(firstLiteral & (tagMask())))) + && (((byteAt((void *)(firstLiteral + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(firstLiteral)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(firstLiteral + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(firstLiteral - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4))) { + return 1; + } + functionName = longAt((void *)((firstLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFunctionNameIndex) << (shiftForWord())))))); + return ((numBytesOf(functionName)) < (21)) + || (0 != (strncmp("primitiveProfile", ((char *) (firstIndexableField(functionName))), 16))); + } + + /* Answer true if there's a real primitive here, excepting the ValueNoContextSwitch primitives, + because a sample can't be taken in them, as doing so could cause a context switch. */ + pf = primitiveTable[primitiveIndex]; + return (pf != (((void *)0))) + && ((pf != (((void (*)(void)) primitiveFail))) + && ((pf != primitiveClosureValueNoContextSwitch) + && (pf != primitiveFullClosureValueNoContextSwitch))); +} + + +/* See if the argument is married to a live frame or not. i.e. see if there + is a matching + frame whose frameContext field is aOnceMarriedContext, or a forwarder to + it. If aOnceMarriedContext is not married to a live frame, turn it into a + bereaved single context. + This version is for use during scavenging when stack references may be + forwarded. Following what appear to be references to forwarded objects on + the stack is dangerous; + an instruction ponter may be correctly aligned and may point to bytes that + just happen + to look like a forwarder. So it is only safe to follow fields that we know + are frameContext + fields; hence the stack page is walked to check that aOnceMarriedContext + is pointing to + a live frame. This only has to happen during scavenging because after a + become: all + frameContext fields have been followed and so there is no need to follow + forwarders. */ +/* Override to check gcMode is correct. See comment in superclass, and in + superclass's isWidowedContext:. + */ + + /* CoInterpreter>>#isWidowedContextDuringGC: */ +static NoDbgRegParms sqInt +isWidowedContextDuringGC(sqInt aOnceMarriedContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anInteger; + char *maybeFrame; + sqInt maybeFrameCtxt; + sqInt referent; + sqInt senderOop; + char *shouldBeFrameCallerField; + StackPage *thePage; + + assert((GIV(gcMode) == GCModeNewSpace) + || ((GIV(gcMode) == GCModeFull) + || (GIV(gcMode) == GCModeImageSegment))); + assert((isContext(aOnceMarriedContext)) + && (isMarriedOrWidowedContext(aOnceMarriedContext))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + maybeFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(maybeFrame), GIV(pages)); + if (!((isFree(thePage)) + || (maybeFrame < ((thePage->headFP))))) { + /* The frame pointer is within the bounds of a live page. + Now check if it matches a frame. */ + anInteger = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + + /* begin withoutSmallIntegerTags: */ + assert((((anInteger) & 7) == 1)); + shouldBeFrameCallerField = ((char *)(anInteger - (smallIntegerTag()))); + if (((((char *)(longAt(maybeFrame + FoxSavedFP)))) == shouldBeFrameCallerField) + && (/* frameHasContext: */ + ((((usqInt)(longAt(maybeFrame + FoxMethod)))) < (startOfMemory()) + ? ((longAt(maybeFrame + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((maybeFrame + FoxIFrameFlags) + 2)) != 0))) { + maybeFrameCtxt = longAt(maybeFrame + FoxThisContext); + + /* On Spur we need to follow the context to check for a match, but since the VM is + only speculating about maybeFrame being a frame, and only speculating about + maybeContext being a context, we need to be sure before we can safely follow. */ + if ((isFrameonPage(maybeFrame, thePage)) + && ((!((longAt((void *)(maybeFrameCtxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(maybeFrameCtxt)); + referent = longAt((void *)((maybeFrameCtxt + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + maybeFrameCtxt = referent; + + /* begin setFrameContext:to: */ + longAtput(maybeFrame + FoxThisContext,maybeFrameCtxt); + } + if (maybeFrameCtxt == aOnceMarriedContext) { + return 0; + } + } + } + + /* It is out of range or doesn't match the frame's context. + It is widowed. Time to wear black. */ + + /* begin markContextAsDead: */ + assert(isContext(aOnceMarriedContext)); + assert((isNonImmediate(aOnceMarriedContext)) + && (!(isForwarded(aOnceMarriedContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, aOnceMarriedContext, GIV(nilObj))); + longAtput((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aOnceMarriedContext)) + && (!(isForwarded(aOnceMarriedContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, aOnceMarriedContext, GIV(nilObj))); + longAtput((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + return 1; +} + + +/* Answer if the argument is married to a live frame or not. + If it is not, turn it into a bereaved single context. This version is safe + for use + only when no frameContext fields may be forwarded (as maybe the case + when scavenging). Post become: all frameContext fields are followed, and + hence normally no following of frameContext fields is necessary. But + during a scavenge one must use isWidowedContextDuringGC:. */ +/* Override to check gcMode is correct. See comment in superclass. */ + + /* CoInterpreter>>#isWidowedContext: */ +static NoDbgRegParms sqInt +isWidowedContext(sqInt aOnceMarriedContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anInteger; + sqInt senderOop; + char *shouldBeFrameCallerField; + char *theFrame; + StackPage *thePage; + sqInt widowed; + + assert(GIV(gcMode) != GCModeNewSpace); + + /* begin isWidowedContextNoConvert: */ + assert((isContext(aOnceMarriedContext)) + && (isMarriedOrWidowedContext(aOnceMarriedContext))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + if (!((isFree(thePage)) + || (theFrame < ((thePage->headFP))))) { + /* The frame pointer is within the bounds of a live page. + Now check if it matches a frame. */ + anInteger = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + + /* begin withoutSmallIntegerTags: */ + assert((((anInteger) & 7) == 1)); + shouldBeFrameCallerField = ((char *)(anInteger - (smallIntegerTag()))); + if (((((char *)(longAt(theFrame + FoxSavedFP)))) == shouldBeFrameCallerField) + && (/* frameHasContext: */ + ((((usqInt)(longAt(theFrame + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFrame + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFrame + FoxIFrameFlags) + 2)) != 0))) { + assert(!(((isFrameonPage(theFrame, thePage)) + && (isForwarded(frameContext(theFrame)))))); + if ((longAt(theFrame + FoxThisContext)) == aOnceMarriedContext) { + widowed = 0; + goto l1; + } + } + } + widowed = 1; + /* end isWidowedContextNoConvert: */ +l1: + if (!widowed) { + return 0; + } + + /* begin markContextAsDead: */ + assert(isContext(aOnceMarriedContext)); + assert((isNonImmediate(aOnceMarriedContext)) + && (!(isForwarded(aOnceMarriedContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, aOnceMarriedContext, GIV(nilObj))); + longAtput((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aOnceMarriedContext)) + && (!(isForwarded(aOnceMarriedContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, aOnceMarriedContext, GIV(nilObj))); + longAtput((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + return 1; +} + + +/* If the method is assigned, any machine code pc must be mapped to a + bytecode one + before the method is changed. */ + + /* CoInterpreter>>#is:methodAssignmentToContextWithMachineCodePC: */ +static NoDbgRegParms sqInt +ismethodAssignmentToContextWithMachineCodePC(sqInt fieldIndex, sqInt anOop) +{ + sqInt thePC; + + thePC = 0; + return (fieldIndex == MethodIndex) + && (((((int) ((thePC = longAt((void *)((anOop + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))))) < 0) + && ((((thePC) & 7) == 1))); +} + + /* CoInterpreter>>#longRunningPrimitiveStopUsecsAddress */ +#if LRPCheck +usqInt +longRunningPrimitiveStopUsecsAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(longRunningPrimitiveStopUsecs)))); +} +#endif /* LRPCheck */ + + +/* Lookup selector in rcvr, without doing MNU processing, and answer either a + method or an error code if the message was not understood. Used to + populate closed PICs. */ +/* self printFrame: stackPage headFP WithSP: stackPage headSP */ +/* self printStringOf: selector */ + + /* CoInterpreter>>#lookupMNU:receiver: */ +sqInt +lookupMNUreceiver(sqInt selector, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTag; + sqInt erridx; + sqInt inCache; + sqInt tagBits; + + classTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + inCache = lookupInMethodCacheSelclassTag(selector, classTag); + if (!inCache) { + GIV(messageSelector) = selector; + erridx = lookupMNUInClass(classForClassTag(classTag)); + if (erridx) { + assert(erridx <= (maxLookupNoMNUErrorCode())); + return erridx; + } + } + return GIV(newMethod); +} + + +/* Lookup selector in rcvr, without doing MNU processing, and answer either a + method or an error code if the message was not understood. Used to + populate closed PICs. */ +/* self printFrame: stackPage headFP WithSP: stackPage headSP */ +/* self printStringOf: selector */ + + /* CoInterpreter>>#lookupOrdinary:receiver: */ +sqInt +lookupOrdinaryreceiver(sqInt selector, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTag; + sqInt erridx; + sqInt tagBits; + + classTag = /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); + if (!(lookupInMethodCacheSelclassTag(selector, classTag))) { + GIV(messageSelector) = selector; + if ((erridx = lookupOrdinaryNoMNUEtcInClass(classForClassTag(classTag)))) { + assert(erridx <= (maxLookupNoMNUErrorCode())); + return erridx; + } + } + return GIV(newMethod); +} + + +/* */ +/* Marry aContext with the base frame of a new stack page. Build the base + frame to reflect the context's state. Answer the new page. Override to + hold the caller context in a different place, In the StackInterpreter we + use the caller saved ip, but in the Cog VM caller saved ip is the + ceBaseReturn: trampoline. Simply hold the caller context in the first word + of the stack. */ + + /* CoInterpreter>>#makeBaseFrameFor: */ +static NoDbgRegParms StackPage * +makeBaseFrameFor(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cogMethod; + CogMethod *cogMethod1; + sqInt header; + sqInt headerSqInt; + sqInt i; + sqInt maybeClosure; + usqInt numArgs; + usqInt numSlots; + sqInt oop; + StackPage *page; + char *pointer; + sqInt rawHeader; + sqInt rcvr; + sqInt stackPtrIndex; + sqInt theIP; + sqInt theMethod; + sqInt valuePointer; + sqInt yetToCompact; + + + /* theIP must be typed as signed because it is assigned ceCannotResumePC and so maybe implicitly typed as unsigned. */ + assert(isContext(aContext)); + assert(isSingleContext(aContext)); + assert(goodContextSize(aContext)); + theIP = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + theIP = ((((theIP) & 7) == 1) + ? (theIP >> 3) + : HasBeenReturnedFromMCPC); + + /* begin followObjField:ofObject: */ + theMethod = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + assert(isNonImmediate(theMethod)); + if ((!((longAt((void *)(theMethod))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + theMethod = fixFollowedFieldofObjectwithInitialValue(MethodIndex, aContext, theMethod); + } + + /* begin newStackPage */ + page = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((page->baseFP))) { + goto l1; + } + divorceFramesIn(page); + /* end newStackPage */ +l1: + + /* first word on stack is caller context of base frame */ + longAtput((pointer = (page->baseAddress)),followObjFieldofObject(SenderIndex, aContext)); + + /* second word is the context itself; needed for cannotReturn processing; see ceBaseReturn:. */ + longAtput((pointer -= BytesPerWord),aContext); + + /* begin followField:ofObject: */ + rcvr = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + rcvr = fixFollowedFieldofObjectwithInitialValue(ReceiverIndex, aContext, rcvr); + } + + /* If the frame is a closure activation then the closure should be on the stack in + the pushed receiver position (closures receive the value[:value:] messages). + Otherwise it should be the receiver proper. */ + maybeClosure = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord())))))); + if (maybeClosure != GIV(nilObj)) { + if ((!((longAt((void *)(maybeClosure))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + maybeClosure = fixFollowedFieldofObjectwithInitialValue(ClosureIndex, aContext, maybeClosure); + } + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + longAtput((pointer -= BytesPerWord),maybeClosure); + } + else { + /* begin methodHeaderOf: */ + assert(isCompiledMethod(theMethod)); + headerSqInt = longAt((void *)((theMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + numArgs = (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; + + /* If this is a synthetic context its IP could be pointing at the CallPrimitive opcode. If so, skip it. */ + if ((((header & AlternateHeaderHasPrimFlag) != 0)) + && (theIP == (1 + ((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop)))) { + theIP += 3 /* sizeOfCallPrimitiveBytecode: */; + } + longAtput((pointer -= BytesPerWord),rcvr); + } + + /* Put the arguments on the stack */ + for (i = 1; i <= numArgs; i += 1) { + longAtput((pointer -= BytesPerWord),longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))))); + } + + /* saved caller ip is base return trampoline */ + longAtput((pointer -= BytesPerWord),ceBaseFrameReturnPC()); + + /* base frame's saved fp is null */ + longAtput((pointer -= BytesPerWord),0); + + /* N.B. Don't set the baseFP, which marks the page as in use, until after + ensureMethodIsCogged: and/or instructionPointer:forContext:frame:. These + can cause a compiled code compaction which, if marked as in use, will + examine this partially initialized page and crash. */ + (page->headFP = pointer); + + /* Create either a machine code frame or an interpreter frame based on the pc. If the pc is -ve + it is a machine code pc and so we produce a machine code frame. If +ve an interpreter frame. + N.B. Do *not* change this to try and map from a bytecode pc to a machine code frame under + any circumstances. See ensureContextIsExecutionSafeAfterAssignToStackPointer: */ + if (theIP < 0) { + /* Since we would have to generate a machine-code method to be able to map + the native pc anyway we should create a native method and native frame. */ + + /* begin ensureMethodIsCogged:maybeClosure: */ + rawHeader = longAt((void *)((theMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) rawHeader); + goto l2; + } + yetToCompact = 1; + while (1) { + if ((maybeClosure != GIV(nilObj)) + && (!((/* begin isVanillaBlockClosure: */ + assert((isPointers(maybeClosure)) + && ((numSlotsOf(maybeClosure)) >= ClosureFirstCopiedValueIndex)), + ((((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1))))) { + cogMethod1 = cogFullBlockMethodnumCopied( + theMethod, + (/* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(maybeClosure)))), + ((/* begin numSlotsOf: */ + assert((classIndexOf(maybeClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(maybeClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(maybeClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex)); + } + else { + cogMethod1 = cogselector(theMethod, GIV(nilObj)); + } + if (!((cogMethod1 == null) + && (GIV(cogCompiledCodeCompactionCalledFor) + && (yetToCompact)))) break; + yetToCompact = 0; + commenceCogCompiledCodeCompaction(); + } + if (!(asserta(cogMethod1 != null))) { + error("could not compile method that should have been compiled"); + } + cogMethod = cogMethod1; + /* end ensureMethodIsCogged:maybeClosure: */ +l2: + theMethod = ((sqInt)cogMethod); + if (maybeClosure != GIV(nilObj)) { + /* begin isVanillaBlockClosure: */ + assert((isPointers(maybeClosure)) + && ((numSlotsOf(maybeClosure)) >= ClosureFirstCopiedValueIndex)); + if (((((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* If the pc is the special HasBeenReturnedFromMCPC pc set the pc + appropriately so that the frame stays in the cannotReturn: state. */ + if (theIP == HasBeenReturnedFromMCPC) { + theMethod = ((sqInt)(findMethodForStartBcpcinHomeMethod((((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))))) >> 3)) - 1, ((CogMethod *) theMethod)))); + if (!theMethod) { + error("cannot find machine code block matching closure's startpc"); + } + theIP = ceCannotResumePC(); + } + else { + assert((((theIP) >> 16)) < -1); + theMethod -= (((theIP) >> 16)) * (blockAlignment()); + theIP = theMethod - (((short)theIP)); + } + } + else { + assert((((theIP) >> 16)) >= -1); + + /* If the pc is the special HasBeenReturnedFromMCPC pc set the pc + appropriately so that the frame stays in the cannotReturn: state. */ + theIP = (theIP == HasBeenReturnedFromMCPC + ? ceCannotResumePC() + : (((sqInt)theMethod)) - theIP); + } + longAtput((pointer -= BytesPerWord),(theMethod + MFMethodFlagHasContextFlag) + MFMethodFlagIsBlockFlag); + } + else { + assert((((theIP) >> 16)) >= -1); + + /* If the pc is the special HasBeenReturnedFromMCPC pc set the pc + appropriately so that the frame stays in the cannotReturn: state. */ + theIP = (theIP == HasBeenReturnedFromMCPC + ? ceCannotResumePC() + : (((sqInt)theMethod)) - theIP); + longAtput((pointer -= BytesPerWord),theMethod + MFMethodFlagHasContextFlag); + } + longAtput((pointer -= BytesPerWord),aContext); + } + else { + longAtput((pointer -= BytesPerWord),theMethod); + longAtput((pointer -= BytesPerWord),aContext); + longAtput((pointer -= BytesPerWord),/* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8)))) + (1ULL << ((BytesPerWord * 8) - 16))) + ((maybeClosure != GIV(nilObj) + ? 1ULL << ((BytesPerWord * 8) - 24) + : 0)) + : ((1 + ((numArgs << 8))) + (0x10000)) + ((maybeClosure != GIV(nilObj) + ? 0x1000000 + : 0)))); + longAtput((pointer -= BytesPerWord),0); + + /* begin iframeInstructionPointerForIndex:method: */ + assert(((theIP >= (((LiteralStart + (literalCountOf(theMethod))) * BytesPerOop) + 1)) && (theIP <= (lengthOf(theMethod))))); + theIP = ((theMethod + theIP) + BaseHeaderSize) - 2; + } + (page->baseFP = (page->headFP)); + assert(frameHasContext((page->baseFP))); + assert((frameNumArgs((page->baseFP))) == numArgs); + longAtput((pointer -= BytesPerWord),rcvr); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + stackPtrIndex = (oop >> 3); + assert((ReceiverIndex + stackPtrIndex) < (lengthOf(aContext))); + for (i = (numArgs + 1); i <= stackPtrIndex; i += 1) { + longAtput((pointer -= BytesPerWord),longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))))); + + /* nil the slot in the context so that it doesn't inadvertently hang onto some collectable object. + Thanks to Ryan Macnak for identifying this bug */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aContext)) + && (!(isForwarded(aContext)))); + assert(validStorePointerUncheckedArgs(ReceiverIndex + i, aContext, GIV(nilObj))); + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))),GIV(nilObj)); + } + + /* top of stack is the instruction pointer */ + longAtput((pointer -= BytesPerWord),theIP); + (page->headSP = pointer); + assert(contexthasValidInversePCMappingOfin(aContext, theIP, (page->baseFP))); + + /* Mark context as married by setting its sender to the frame pointer plus SmallInteger + tags and the InstructionPointer to the saved fp (which ensures correct alignment + w.r.t. the frame when we check for validity) plus SmallInteger tags. */ + /* begin withSmallIntegerTags: */ + assert(((oopForPointer((page->baseFP))) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer((page->baseFP))) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aContext)) + && (!(isForwarded(aContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, aContext, valuePointer)); + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),valuePointer); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(0)) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(0)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aContext)) + && (!(isForwarded(aContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, aContext, valuePointer)); + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointer); + assert(((((fetchPointerofObject(SenderIndex, aContext))) & 7) == 1)); + assert((frameOfMarriedContext(aContext)) == ((page->baseFP))); + assert(validStackPageBaseFrame(page)); + return page; +} + + /* CoInterpreter>>#mapStackPages */ +static NeverInline void +mapStackPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + char *frameRcvrOffset; + sqInt i; + sqInt numLivePages; + sqInt oop; + char *theFP; + sqInt theIP; + char *theIPPtr; + StackPage *thePage; + char *theSP; + + callerFP = ((char *) 0); + theIP = 0; + numLivePages = 0; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((thePage->baseFP)) { + assert(ifCurrentStackPageHasValidHeadPointers(thePage)); + numLivePages += 1; + theSP = (thePage->headSP); + theFP = (thePage->headFP); + + /* Skip the instruction pointer on top of stack of inactive pages. */ + if (thePage == GIV(stackPage)) { + theIPPtr = (((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + || ((((usqInt)(longAt(theFP + FoxIFSavedIP)))) == 0) + ? 0 + : theFP + FoxIFSavedIP); + } + else { + theIPPtr = theSP; + theSP += BytesPerWord; + } + while (1) { + assert(addressIsInPage(thePage, theFP)); + assert(addressIsInPage(thePage, theSP)); + assert((theIPPtr == 0) + || (addressIsInPage(thePage, theIPPtr))); + frameRcvrOffset = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + while (theSP <= frameRcvrOffset) { + oop = longAt(theSP); + if (/* shouldRemapOop: */ + ((!(oop & (tagMask())))) + && (shouldRemapObj(oop))) { + longAtput(theSP,remapObj(oop)); + } + theSP += BytesPerWord; + } + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + if (shouldRemapObj(longAt(theFP + FoxThisContext))) { + longAtput(theFP + FoxThisContext,remapObj(longAt(theFP + FoxThisContext))); + } + + /* With SqueakV3 objectMemory or SpurPlanningCompactor can't assert since object body is yet to move. */ + if (!((GIV(gcPhaseInProgress) == SlidingCompactionInProgress) + || (GIV(gcPhaseInProgress) == ScavengeInProgress))) { + assert((isMarriedOrWidowedContext(frameContext(theFP))) + && ((frameOfMarriedContext(frameContext(theFP))) == theFP)); + } + } + if (!((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()))) { + if (shouldRemapObj(longAt(theFP + FoxMethod))) { + if (theIPPtr) { + theIP = longAt(theIPPtr); + if (theIP == (ceReturnToInterpreterPC())) { + assert((iframeSavedIP(theFP)) > (iframeMethod(theFP))); + theIPPtr = theFP + FoxIFSavedIP; + theIP = longAt(theIPPtr); + } + else { + assert(theIP > (iframeMethod(theFP))); + } + theIP -= longAt(theFP + FoxMethod); + } + longAtput(theFP + FoxMethod,remapObj(longAt(theFP + FoxMethod))); + if (theIPPtr) { + longAtput(theIPPtr,theIP + (longAt(theFP + FoxMethod))); + } + } + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theSP = ((theIPPtr = theFP + FoxCallerSavedIP)) + BytesPerWord; + theFP = callerFP; + } + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if (/* shouldRemapOop: */ + ((!(oop & (tagMask())))) + && (shouldRemapObj(oop))) { + longAtput(theSP,remapObj(oop)); + } + theSP += BytesPerWord; + } + } + } + + /* begin recordLivePagesOnMapping: */ + GIV(statNumMaps) += 1; + GIV(statPageCountWhenMappingSum) += numLivePages; + GIV(statMaxPageCountWhenMapping) = ((GIV(statMaxPageCountWhenMapping) < numLivePages) ? numLivePages : GIV(statMaxPageCountWhenMapping)); +} + + +/* If instructionPointer is referring to machine code, as it will be if a + primitive is in progress + (see isCodeCompactingPrimitiveIndex:) it may refer to a method, and if so + that method + must be retained. */ + + /* CoInterpreter>>#markActiveMethodsAndReferents */ +void +markActiveMethodsAndReferents(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + CogMethod *primCogMethod; + StackPage *thePage; + + if (GIV(instructionPointer)) { + if ((primCogMethod = cogMethodContaining(GIV(instructionPointer)))) { + markMethodAndReferents(((CogBlockMethod *) primCogMethod)); + } + } + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + markCogMethodsAndReferentsOnPage(thePage); + } + } +} + + /* CoInterpreter>>#markAndTraceStackPage: */ +static NoDbgRegParms void +markAndTraceStackPage(StackPage *thePage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogBlockMethod *aCogMethod; + char *callerFP; + char *frameRcvrOffset; + CogMethod *homeMethod; + sqInt oop; + sqInt referent; + char *theFP; + char *theSP; + + callerFP = ((char *) 0); + assert(!(isFree(thePage))); + assert(ifCurrentStackPageHasValidHeadPointers(thePage)); + assert(((thePage->trace)) != StackPageTraced); + (thePage->trace = StackPageTraced); + theSP = (thePage->headSP); + theFP = (thePage->headFP); + + /* Skip the instruction pointer on top of stack of inactive pages. */ + if (!(thePage == GIV(stackPage))) { + theSP += BytesPerWord; + } + while (1) { + frameRcvrOffset = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + while (theSP <= frameRcvrOffset) { + oop = longAt(theSP); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + longAtput(theSP,oop); + } + if (!(((oop & (tagMask())) != 0))) { + markAndTrace(oop); + } + theSP += BytesPerWord; + } + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + markAndTrace(frameContext(theFP)); + } + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* begin mframeCogMethod: */ + aCogMethod = ((CogBlockMethod *) ((longAt(theFP + FoxMethod)) & MFMethodMask)); + homeMethod = /* asCogHomeMethod: */ + (((aCogMethod->cmType)) >= CMMethod + ? ((CogMethod *) aCogMethod) + : /* cmHomeMethod */ + (((aCogMethod->cmType)) >= CMMethod + ? ((CogMethod *) aCogMethod) + : (assert(CMBlock == (aCogMethod->cmType)), + ((CogMethod *) ((((usqInt)aCogMethod)) - ((aCogMethod->homeOffset))))))); + markAndTrace((homeMethod->methodObject)); + } + else { + markAndTrace(iframeMethod(theFP)); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + theFP = callerFP; + } + + /* caller ip is ceBaseReturnPC */ + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + longAtput(theSP,oop); + } + if (!(((oop & (tagMask())) != 0))) { + markAndTrace(oop); + } + theSP += BytesPerWord; + } +} + + /* CoInterpreter>>#markCogMethodsAndReferentsOnPage: */ +static NoDbgRegParms void +markCogMethodsAndReferentsOnPage(StackPage *thePage) +{ + char *callerFP; + char *theFP; + + callerFP = ((char *) 0); + assert(!(isFree(thePage))); + assert(ifCurrentStackPageHasValidHeadPointers(thePage)); + theFP = (thePage->headFP); + while (1) { + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + markMethodAndReferents(mframeCogMethod(theFP)); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theFP = callerFP; + } +} + + +/* Marry an unmarried frame. This means creating a spouse context + initialized with a subset of the frame's state that references the frame. + For the default closure implementation we do not need to copy temps. + + This method is important enough for performance to be worth streamlining. + + Override to set the ``has context'' flag appropriately for both machine + code and + interpreter frames and to streamline the machine code/interpreter + differences. + */ + + /* CoInterpreter>>#marryFrame:SP: */ +static NoDbgRegParms usqInt +marryFrameSP(char *theFP, char *theSP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt closureOrNil; + CogMethod *cogMethod; + sqInt header; + sqInt headerSqInt; + sqInt i; + sqInt methodFieldOrObj; + usqInt newObj; + usqInt numArgs; + usqInt numBytes; + sqInt numSlots; + sqInt numStack; + sqInt rcvr; + usqInt theContext; + sqInt valuePointer; + + assert(!((frameHasContext(theFP)))); + assert(!((isBaseFrame(theFP)))); + assert(addressCouldBeOop(longAt(theSP))); + + /* Decide how much of the stack to preserve in widowed contexts. Preserving too much state will potentially + hold onto garbage. Holding onto too little may mean that a dead context isn't informative enough in a + debugging situation. But the fundamental issue is that the temporary values captured when the frame is + married are likely stale, so if the temporary variable values are important they need to be updated later; + see primitiveContextPreserveTemps. So when marrying we capture the arguments and set the stack pointer + to accomodate (at least *) the temporaries, but the temporaries are all set to nil. In machine code contexts + are created via code generated by genGetActiveContextLarge:inBlock: which must agree perfectly with + this method. + * the V3PlusClosures bytecode set has to use pushNil bytecodes to establish slots for temporary variables + in block contexts, but a pushNil may be ambiguous with oushing a receiver, or even argument, e.g. + [nil == nil]. Instead of sweating the issue of using an accurate temporary count, we simply set the stack + pointer to its current value and set all slots following the arguments to nil. */ + methodFieldOrObj = longAt(theFP + FoxMethod); + if ((((usqInt)methodFieldOrObj)) < (startOfMemory())) { + longAtput(theFP + FoxMethod,methodFieldOrObj | MFMethodFlagHasContextFlag); + cogMethod = ((CogMethod *) (methodFieldOrObj & MFMethodMask)); + numArgs = (cogMethod->cmNumArgs); + if (((cogMethod->cmType)) >= CMMethod) { + closureOrNil = ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock) + ? longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((numArgs << (shiftForWord()))))) + : GIV(nilObj)); + } + else { + assert(isCMBlock(((CogBlockMethod *) cogMethod))); + + /* cmHomeMethod */ + if ((((((CogBlockMethod *) cogMethod))->cmType)) >= CMMethod) { + cogMethod = ((CogMethod *) (((CogBlockMethod *) cogMethod))); + } + else { + assert(CMBlock == ((((CogBlockMethod *) cogMethod))->cmType)); + cogMethod = ((CogMethod *) ((((usqInt)(((CogBlockMethod *) cogMethod)))) - (((((CogBlockMethod *) cogMethod))->homeOffset)))); + } + closureOrNil = longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((numArgs << (shiftForWord()))))); + } + methodFieldOrObj = (cogMethod->methodObject); + header = (cogMethod->methodHeader); + rcvr = longAt(theFP + FoxMFReceiver); + } + else { + /* begin setIFrameHasContext: */ + byteAtput((theFP + FoxIFrameFlags) + 2,1); + assert(isCompiledMethod(methodFieldOrObj)); + headerSqInt = longAt((void *)((methodFieldOrObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + if (byteAt((theFP + FoxIFrameFlags) + 3)) { + numArgs = byteAt((theFP + FoxIFrameFlags) + 1); + closureOrNil = longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((numArgs << (shiftForWord()))))); + } + else { + numArgs = (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; + closureOrNil = GIV(nilObj); + } + rcvr = longAt(theFP + FoxIFReceiver); + } + + /* inline (self isMachineCodeFrame: theFP) */ + numStack = (((usqInt)(((theFP + FoxMFReceiver) - theSP))) >> (shiftForWord())) + numArgs; + numSlots = (header & LargeContextBit + ? LargeContextSlots + : SmallContextSlots); + + /* begin eeInstantiateMethodContextSlots: */ + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + theContext = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((indexablePointersFormat())) << (formatShift()))))) + ClassMethodContextCompactIndex); + GIV(freeStart) += numBytes; + theContext = newObj; + /* end eeInstantiateMethodContextSlots: */ +l1: + + /* begin setFrameContext:to: */ + longAtput(theFP + FoxThisContext,theContext); + + /* Mark context as married by setting its sender to the frame pointer plus SmallInteger tags and the InstructionPointer + to the saved fp (which ensures correct alignment w.r.t. the frame when we check for validity) */ + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(theFP)) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(theFP)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, theContext, valuePointer)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),valuePointer); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(((char *)(longAt(theFP + FoxSavedFP))))) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(((char *)(longAt(theFP + FoxSavedFP))))) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, theContext, valuePointer)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(StackPointerIndex, theContext, (((usqInt)numStack << 3) | 1))); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord()))))),(((usqInt)numStack << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(MethodIndex, theContext, methodFieldOrObj)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord()))))),methodFieldOrObj); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(ClosureIndex, theContext, closureOrNil)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord()))))),closureOrNil); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(ReceiverIndex, theContext, rcvr)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord()))))),rcvr); + for (i = 1; i <= numArgs; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(ReceiverIndex + i, theContext, longAt((void *)((theFP + FoxCallerSavedIP) + ((numArgs - (i - 1)) * BytesPerWord))))); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))),longAt((void *)((theFP + FoxCallerSavedIP) + ((numArgs - (i - 1)) * BytesPerWord)))); + } + for (i = (numArgs + 1); i <= numStack; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(ReceiverIndex + i, theContext, GIV(nilObj))); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))),GIV(nilObj)); + } + assert(frameHasContext(theFP)); + assert(frameOfMarriedContext(theContext) == theFP); + assert((numStack + ReceiverIndex) < (lengthOf(theContext))); + return theContext; +} + + /* CoInterpreter>>#maxLookupNoMNUErrorCode */ +sqInt +maxLookupNoMNUErrorCode(void) +{ + return ((SelectorCannotInterpret < SelectorDoesNotUnderstand) ? SelectorDoesNotUnderstand : SelectorCannotInterpret); +} + + /* CoInterpreter>>#methodCacheAddress */ +usqInt +methodCacheAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)GIV(methodCache)); +} + + /* CoInterpreter>>#methodHasCogMethod: */ +int +methodHasCogMethod(sqInt aMethodOop) +{ + sqInt methodHeader; + + assert(isNonImmediate(aMethodOop)); + methodHeader = longAt((void *)((aMethodOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + return (!(methodHeader & (smallIntegerTag()))); +} + + /* CoInterpreter>>#methodHasMachineCodePrimitiveMethod: */ +static NoDbgRegParms sqInt +methodHasMachineCodePrimitiveMethod(sqInt methodObj) +{ + sqInt methodHeader; + + methodHeader = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + return ((/* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))), + /* isNonIntegerObject: */ + (!(methodHeader & (smallIntegerTag()))))) + && (cogMethodHasMachineCodePrim(((CogMethod *) methodHeader))); +} + + /* CoInterpreter>>#methodHasPrimitiveInPrimTracePlugin: */ +static NoDbgRegParms NeverInline sqInt +methodHasPrimitiveInPrimTracePlugin(sqInt aMethodObj) +{ + sqInt len; + sqInt lit; + usqInt numSlots; + sqInt ok; + sqInt pluginName; + + if (!(((primitiveIndexOf(aMethodObj)) == PrimNumberExternalCall) + && ((literalCountOf(aMethodObj)) > 0))) { + return 0; + } + lit = longAt((void *)((aMethodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(lit & (tagMask())))) + && (((byteAt((void *)(lit + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(lit)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(lit + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(lit - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4))) { + return 0; + } + assert(primTracePluginName); + pluginName = longAt((void *)((lit + BaseHeaderSize) + (0U << (shiftForWord())))); + if (!(/* isBytes: */ + ((!(pluginName & (tagMask())))) + && (((byteAt((void *)(pluginName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + return 0; + } + + /* begin object:equalsString: */ + len = strlen(primTracePluginName); + ok = /* object:equalsString:ofSize: */ + (/* isBytes: */ + ((!(pluginName & (tagMask())))) + && (((byteAt((void *)(pluginName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((!(((byteAt((void *)(pluginName + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((numBytesOfBytes(pluginName)) == len) + && ((strncmp(primTracePluginName, firstIndexableField(pluginName), len)) == 0))); + return ok; +} + + /* CoInterpreter>>#methodHasTooManyLiterals: */ +static NoDbgRegParms sqInt +methodHasTooManyLiterals(sqInt methodObj) +{ + return !(methodWithHeaderShouldBeCogged(methodHeaderOf(methodObj))); +} + + +/* Note: We now have 10 bits of primitive index, but they are in two places + for temporary backward compatibility. The time to unpack is negligible, + since the derived primitive function pointer is stored in the method + cache. */ + + /* CoInterpreter>>#methodHeaderHasPrimitive: */ +#if VMInvestigations +int +methodHeaderHasPrimitive(sqInt methodHeader) +{ + return ((methodHeader & AlternateHeaderHasPrimFlag) != 0); +} +#endif /* VMInvestigations */ + + /* CoInterpreter>>#methodNeedsLargeContext: */ +int +methodNeedsLargeContext(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + sqInt methodHeader; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin methodHeaderIndicatesLargeFrame: */ + return (methodHeader & LargeContextBit) != 0; +} + + /* CoInterpreter>>#methodShouldBeCogged: */ +sqInt +methodShouldBeCogged(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + sqInt methodHeader; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + return 1; + } + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(aMethodObj); + } + return 0; +} + + +/* At the moment jit any method with less than N literals, where N defaults + to 60. + See e.g. SimpleStackBasedCogit class>>initialize. + In my dev image eem 2/22/2009 13:39 + (30 to: 100 by: 5) collect: + [:n| n -> (SystemNavigation default allSelect: [:m| m numLiterals > n]) + size] #(30->1681 35->1150 40->765 45->523 50->389 55->289 60->206 + 65->151 70->124 75->99 80->73 85->63 90->54 95->42 100->38). + And running the CogVMSimulator with flagging of interpreted methods turned + on reveals + the following sizes of interpreted methods. + | sizes | + sizes := Bag new. + SystemNavigation default allSelect: [:m| m flag ifTrue: [sizes add: m + numLiterals]. false]. + sizes sortedElements asArray + #( 40->4 41->1 42->2 44->1 45->3 46->1 47->2 48->1 + 50->2 51->1 53->1 55->1 56->1 + 87->1 108->1 171->1) + literalCountOfHeader: does not include the header word. */ + + /* CoInterpreter>>#methodWithHeaderShouldBeCogged: */ +static NoDbgRegParms int +methodWithHeaderShouldBeCogged(sqInt methodHeader) +{ + return ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile; +} + + +/* Answer the Cog method for a machine code frame. This may be + either a full CogMethod or merely a CogBlockMethod rump header. */ + + /* CoInterpreter>>#mframeCogMethod: */ +static NoDbgRegParms CogBlockMethod * +mframeCogMethod(char *theFP) +{ + return ((CogBlockMethod *) ((longAt(theFP + FoxMethod)) & MFMethodMask)); +} + + /* CoInterpreter>>#mframeHomeMethodExport */ +CogMethod * +mframeHomeMethodExport(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt methodField; + + /* begin mframeHomeMethod: */ + methodField = longAt(GIV(framePointer) + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + return /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + } + return ((CogMethod *) (methodField & MFMethodMask)); +} + + +/* Answer the home method for a machine code frame. From a block frame we + find the home method through the block's homeOffset field which is the + delta to it. + In both cases we need to strip the isBlock and isContext flags from the + method field. */ + + /* CoInterpreter>>#mframeHomeMethod: */ +static NoDbgRegParms CogMethod * +mframeHomeMethod(char *theFP) +{ + sqInt methodField; + + methodField = longAt(theFP + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + return /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + } + return ((CogMethod *) (methodField & MFMethodMask)); +} + + +/* */ + + /* CoInterpreter>>#mframeIsBlockActivation: */ +static NoDbgRegParms int +mframeIsBlockActivation(char *theFP) +{ + return ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0; +} + + /* CoInterpreter>>#mframeReceiver: */ +static NoDbgRegParms sqInt +mframeReceiver(char *theFP) +{ + return longAt(theFP + FoxMFReceiver); +} + + +/* Traverse all stack pages looking for non-zero bytes in the headroom part + of each page. + Answer the minimum size of unused headroom (zero bytes) in the pages. This + is for + checking that there is enough headroom allocated in stack pages. */ + + /* CoInterpreter>>#minimumUnusedHeadroom */ +static NeverInline sqInt +minimumUnusedHeadroom(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt minUnused; + char *p; + StackPage *page; + sqInt unused; + + if (!(GIV(pages))) { + return -1; + } + minUnused = (((stackPageAt(0))->baseAddress)) - (((stackPageAt(0))->lastAddress)); + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + page = stackPageAtpages(i, GIV(pages)); + p = (page->lastAddress); + do { + p += BytesPerWord; + } while(((longAt(p)) == 0) + && (p <= ((page->baseAddress)))); + unused = (p - BytesPerWord) - ((page->lastAddress)); + if (unused < minUnused) { + minUnused = unused; + } + } + return minUnused; +} + + +/* Lookup the doesNotUnderstand: selector in the class of the argument rcvr. + Answer either the matching method (cogged if appropriate), or nil, if not + found. + */ + + /* CoInterpreter>>#mnuMethodOrNilFor: */ +static NoDbgRegParms sqInt +mnuMethodOrNilFor(sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentClass; + sqInt dictionary; + usqInt index; + usqInt length; + sqInt mask; + sqInt methodArray; + sqInt methodHeader; + sqInt mnuMethod; + sqInt mnuSelector; + sqInt nextSelector; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt tagBits; + sqInt wrapAround; + + assert(!((isOopForwarded(rcvr)))); + currentClass = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)); + mnuSelector = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))); + while (currentClass != GIV(nilObj)) { + dictionary = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + if (dictionary == GIV(nilObj)) { + return null; + } + + /* begin lookupMethodFor:InDictionary: */ + /* begin numSlotsOf: */ + assert((classIndexOf(dictionary)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(dictionary + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(dictionary - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + mask = (length - SelectorStart) - 1; + index = SelectorStart + (mask & ((((mnuSelector & (tagMask())) != 0) + ? (mnuSelector >> 3) + : (long32At((void *)(mnuSelector + 4))) & (identityHashHalfWordMask())))); + + /* It is assumed that there are some nils in this dictionary, and search will + stop when one is encountered. However, if there are no nils, then wrapAround + will be detected the second time the loop gets to the end of the table. */ + wrapAround = 0; + while (1) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((index << (shiftForWord()))))); + if (nextSelector == GIV(nilObj)) { + mnuMethod = null; + goto l1; + } + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == mnuSelector) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOop = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(index - SelectorStart, methodArray, objOop); + } + mnuMethod = objOop; + goto l1; + } + index += 1; + if (index == length) { + if (wrapAround) { + mnuMethod = null; + goto l1; + } + wrapAround = 1; + index = SelectorStart; + } + } + mnuMethod = null; + /* end lookupMethodFor:InDictionary: */ +l1: + if (mnuMethod) { + methodHeader = longAt((void *)((mnuMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + if ((!((/* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))), + /* isNonIntegerObject: */ + (!(methodHeader & (smallIntegerTag())))))) + && (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile)) { + cogselector(mnuMethod, mnuSelector); + } + return mnuMethod; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOopSqInt = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOopSqInt)); + if ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOopSqInt); + } + currentClass = objOopSqInt; + } + return null; +} + + +/* Move frames from the hot end of oldPage through to theFP to newPage. + This has the effect of making theFP a base frame which can be stored into. + Answer theFP's new location. */ + + /* CoInterpreter>>#moveFramesIn:through:toPage: */ +static NoDbgRegParms char * +moveFramesInthroughtoPage(StackPage *oldPage, char *theFP, StackPage *newPage) +{ + char *callerFP; + usqInt callerIP; + sqInt delta; + char *fpInNewPage; + char *newFP; + char *newSP; + char *offsetCallerFP; + char *source; + sqInt stackedReceiverOffset; + sqInt theContext; + char *theFP1; + char *theSP; + sqInt valuePointer; + sqInt valuePointerSqInt; + + + /* A base frame must have a context for cannotReturn: processing. */ + assert(!(isBaseFrame(theFP))); + assert(validStackPageBaseFrames()); + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + assert(frameHasContext(callerFP)); + assert(isContext(frameContext(callerFP))); + theSP = theFP + (((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? FoxMFReceiver + : FoxIFReceiver)); + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + theContext = longAt(theFP + FoxThisContext); + goto l1; + } + theContext = marryFrameSP(theFP, theSP); + /* end ensureFrameIsMarried:SP: */ +l1: + longAtput((newSP = (newPage->baseAddress)),longAt(callerFP + FoxThisContext)); + longAtput((newSP -= BytesPerWord),theContext); + stackedReceiverOffset = (FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))); + + /* First move the data, leaving room for the caller and base frame contexts. We will fix up frame pointers later. */ + for (source = (theFP + stackedReceiverOffset); source >= ((oldPage->headSP)); source += (-BytesPerWord)) { + newSP -= BytesPerWord; + longAtput(newSP,longAt(source)); + } + + /* newSP = oldSP + delta => delta = newSP - oldSP */ + delta = newSP - ((oldPage->headSP)); + newFP = (((newPage->baseAddress)) - stackedReceiverOffset) - (2 * BytesPerWord); + theFP1 = ((oldPage->headFP)) + delta; + + /* begin setHeadFP:andSP:inPage: */ + assert(newSP < theFP1); + assert((newSP < ((newPage->baseAddress))) + && (newSP > (((newPage->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((theFP1 < ((newPage->baseAddress))) + && (theFP1 > (((newPage->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (newPage->headFP = theFP1); + (newPage->headSP = newSP); + (newPage->baseFP = newFP); + callerIP = longAt(theFP + FoxCallerSavedIP); + if ((((usqInt)callerIP)) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(callerFP))); + longAtput(callerFP + FoxIFSavedIP,callerIP); + callerIP = ceReturnToInterpreterPC(); + } + longAtput(theFP + stackedReceiverOffset,callerIP); + assert((callerFP < ((oldPage->baseAddress))) + && (callerFP > (((oldPage->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (oldPage->headFP = callerFP); + (oldPage->headSP = theFP + stackedReceiverOffset); + + /* Mark the new base frame in the new page */ + longAtput(newFP + FoxCallerSavedIP,ceBaseFrameReturnPC()); + longAtput(newFP + FoxSavedFP,0); + + /* Now relocate frame pointers, updating married contexts to refer to their moved spouse frames. */ + fpInNewPage = (newPage->headFP); + do { + offsetCallerFP = ((char *)(longAt(fpInNewPage + FoxSavedFP))); + if (offsetCallerFP) { + offsetCallerFP += delta; + } + longAtput(fpInNewPage + FoxSavedFP,oopForPointer(offsetCallerFP)); + if (/* frameHasContext: */ + ((((usqInt)(longAt(fpInNewPage + FoxMethod)))) < (startOfMemory()) + ? ((longAt(fpInNewPage + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((fpInNewPage + FoxIFrameFlags) + 2)) != 0)) { + theContext = longAt(fpInNewPage + FoxThisContext); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(fpInNewPage)) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(fpInNewPage)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, theContext, valuePointer)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),valuePointer); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(offsetCallerFP)) & (BytesPerWord - 1)) == 0); + valuePointerSqInt = (oopForPointer(offsetCallerFP)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, theContext, valuePointerSqInt)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointerSqInt); + } + fpInNewPage = offsetCallerFP; + } while(fpInNewPage != 0); + assert(validStackPageBaseFrames()); + return newFP; +} + + +/* Map the native pc theIP into a bytecode pc integer object and answer it. + See contextInstructionPointer:frame: for the explanation. */ + + /* CoInterpreter>>#mustMapMachineCodePC:context: */ +static NoDbgRegParms sqInt +mustMapMachineCodePCcontext(sqInt theIP, sqInt aOnceMarriedContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bcpc; + CogMethod *cogMethod; + CogMethod *cogMethod1; + CogBlockMethod *cogMethodForIP; + sqInt maybeClosure; + sqInt mcpc; + sqInt methodObj; + usqInt numSlots; + sqInt rawHeader; + usqInt startBcpc; + sqInt yetToCompact; + + if (theIP == HasBeenReturnedFromMCPC) { + return GIV(nilObj); + } + maybeClosure = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord())))))); + methodObj = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + if ((maybeClosure != GIV(nilObj)) + && ((/* begin isVanillaBlockClosure: */ + assert((isPointers(maybeClosure)) + && ((numSlotsOf(maybeClosure)) >= ClosureFirstCopiedValueIndex)), + ((((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1)))) { + assert((((theIP) >> 16)) < -1); + startBcpc = (((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord()))))))) >> 3)) - 1; + } + else { + assert((((theIP) >> 16)) == -1); + startBcpc = ((literalCountOf(methodObj)) + LiteralStart) * BytesPerOop; + } + + /* begin ensureMethodIsCogged:maybeClosure: */ + rawHeader = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) rawHeader); + goto l2; + } + yetToCompact = 1; + while (1) { + if ((maybeClosure != GIV(nilObj)) + && (!((/* begin isVanillaBlockClosure: */ + assert((isPointers(maybeClosure)) + && ((numSlotsOf(maybeClosure)) >= ClosureFirstCopiedValueIndex)), + ((((longAt((void *)((maybeClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1))))) { + cogMethod1 = cogFullBlockMethodnumCopied( + methodObj, + (/* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(maybeClosure)))), + ((/* begin numSlotsOf: */ + assert((classIndexOf(maybeClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(maybeClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(maybeClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex)); + } + else { + cogMethod1 = cogselector(methodObj, GIV(nilObj)); + } + if (!((cogMethod1 == null) + && (GIV(cogCompiledCodeCompactionCalledFor) + && (yetToCompact)))) break; + yetToCompact = 0; + commenceCogCompiledCodeCompaction(); + } + if (!(asserta(cogMethod1 != null))) { + error("could not compile method that should have been compiled"); + } + cogMethod = cogMethod1; + /* end ensureMethodIsCogged:maybeClosure: */ +l2: + + /* begin bytecodePCFor:cogMethod:startBcpc: */ + assert(theIP < 0); + if ((((theIP) >> 16)) < -1) { + cogMethodForIP = ((CogBlockMethod *) ((((sqInt)cogMethod)) - ((((theIP) >> 16)) * (blockAlignment())))); + assert(isCMBlock(cogMethodForIP)); + assert((cmHomeMethod(cogMethodForIP)) == cogMethod); + mcpc = (((sqInt)cogMethodForIP)) - (((short)theIP)); + } + else { + cogMethodForIP = ((CogBlockMethod *) cogMethod); + assert(isCMMethodEtAl(cogMethodForIP)); + mcpc = (((sqInt)cogMethod)) - theIP; + + /* map any pcs in primitive code (i.e. return addresses for interpreter primitive calls) to the initial pc */ + if ((((usqInt)mcpc)) < ((cogMethod->stackCheckOffset))) { + bcpc = startBcpc; + goto l1; + } + } + + /* See contextInstructionPointer:frame: */ + assert(((mcpc >= (((sqInt)cogMethod))) && (mcpc <= ((((sqInt)cogMethod)) + ((cogMethod->blockSize)))))); + bcpc = bytecodePCForstartBcpcin(mcpc, startBcpc, cogMethodForIP); + /* end bytecodePCFor:cogMethod:startBcpc: */ +l1: + assert(bcpc >= (startPCOfMethod(methodObj))); + + /* If there's a CallPrimitive we need to skip it. */ + if ((bcpc == startBcpc) + && ((maybeClosure == GIV(nilObj)) + && (((((cogMethod->methodHeader)) & AlternateHeaderHasPrimFlag) != 0)))) { + bcpc += 3 /* sizeOfCallPrimitiveBytecode: */; + } + return (((usqInt)(bcpc + 1) << 3) | 1); +} + + /* CoInterpreter>>#newMethodAddress */ +usqInt +newMethodAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(newMethod)))); +} + + +/* N.B. nextProfileTick is 64-bits */ + + /* CoInterpreter>>#nextProfileTickAddress */ +usqInt +nextProfileTickAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(nextProfileTick)))); +} + + /* CoInterpreter>>#noAssertHeaderOf: */ +sqInt +noAssertHeaderOf(sqInt methodPointer) +{ + sqInt methodHeader; + + methodHeader = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + return ((!(methodHeader & (smallIntegerTag()))) + ? ((((CogMethod *) methodHeader))->methodHeader) + : methodHeader); +} + + +/* integerValue is interpreted as POSITIVE, e.g. as the result of Bitmap>at:. + N.B. Returning in each arm separately enables Slang inlining. + /Don't/ return the ifTrue:ifFalse: unless Slang inlining of conditionals + is fixed. */ + + /* CoInterpreter>>#positive32BitIntegerFor: */ +sqInt +positive32BitIntegerFor(unsigned int integerValue) +{ + return (((usqInt)((((usqInt)integerValue)) & 0xFFFFFFFFU) << 3) | 1); +} + + +/* Convert the given object into an integer value. + The object may be either a positive SmallInteger or a four-byte + LargePositiveInteger. + */ + + /* CoInterpreter>>#positive32BitValueOf: */ +unsigned int +positive32BitValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt value64; + + if ((((oop) & 7) == 1)) { + value64 = (oop >> 3); + if ((value64 < 0) + || ((((unsigned int) value64)) != value64)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + value64 = 0; + } + return value64; + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; +} + + +/* Answer a Large Positive Integer object for the given integer value. N.B. + will *not* cause a GC. */ + + /* CoInterpreter>>#positive64BitIntegerFor: */ +sqInt +positive64BitIntegerFor(usqLong integerValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newLargeInteger; + usqInt newObj; + usqInt numBytes; + sqInt numSlots; + sqInt objFormat; + sqInt sz; + + if (integerValue <= (MaxSmallInteger)) { + return ((integerValue << 3) | 1); + } + sz = 8; + objFormat = (firstByteFormat()) + ((8 - sz) & (BytesPerWord - 1)); + numSlots = 8 / BytesPerOop; + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((numSlots >= 0) + && ((knownClassAtIndex(ClassLargePositiveIntegerCompactIndex)) != GIV(nilObj))); + assert(((objFormat < (firstByteFormat()) + ? objFormat + : objFormat & (byteFormatMask()))) == (instSpecOfClass(knownClassAtIndex(ClassLargePositiveIntegerCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newLargeInteger = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + ClassLargePositiveIntegerCompactIndex); + GIV(freeStart) += numBytes; + newLargeInteger = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((newLargeInteger + BaseHeaderSize)),SQ_SWAP_8_BYTES_IF_BIGENDIAN(integerValue)); + return newLargeInteger; +} + + +/* Convert the given object into an integer value. + The object may be either a positive SmallInteger or up to an eight-byte + LargePositiveInteger. + */ + + /* CoInterpreter>>#positive64BitValueOf: */ +usqLong +positive64BitValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + sqInt fmt; + usqInt numSlots; + int ok; + sqInt smallIntValue; + sqInt sz; + + if ((((oop) & 7) == 1)) { + smallIntValue = (oop >> 3); + if (smallIntValue < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return smallIntValue; + } + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (!ok) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + sz = ((((/* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (sz > (sizeof(sqLong))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + if (sz > 4) { + return SQ_SWAP_8_BYTES_IF_BIGENDIAN((long64At((void *)((oop + BaseHeaderSize))))); + } + return ((unsigned int) (SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((oop + BaseHeaderSize))))))); +} + + +/* Signal the gc semaphore, and inform the display subsystem if the display + bitmap has moved. + */ +/* Attempt to shrink free memory, signal the gc semaphore and let the Cogit + do its post GC thang + */ + + /* CoInterpreter>>#postGCAction: */ +static NoDbgRegParms void +postGCAction(sqInt gcModeArg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(gcModeArg == GIV(gcMode)); + signalSemaphoreWithIndex(GIV(gcSemaphoreIndex)); + postGCUpdateDisplayBits(); + cogitPostGCAction(gcModeArg); + + /* begin nilUncoggableMethods */ + GIV(lastCoggableInterpretedBlockMethod) = (GIV(lastUncoggableInterpretedBlockMethod) = null); + GIV(gcMode) = 0; +} + + +/* Need to write back the frame pointers unless all pages are free (as in + snapshot) + */ +/* Need to write back the frame pointers unless all pages are free (as in + snapshot). Need to set gcMode var (to avoid passing the flag through a lot + of the updating code) */ + + /* CoInterpreter>>#preGCAction: */ +static NoDbgRegParms void +preGCAction(sqInt gcModeArg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aTraceCode; + int traceType; + + if (GIV(stackPage)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + if (recordPrimTrace()) { + aTraceCode = (gcModeArg == GCModeFull + ? TraceFullGC + : TraceIncrementalGC); + + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)aTraceCode << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + if (gcModeArg == GCModeFull) { + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + } + GIV(gcMode) = gcModeArg; + if (recordEventTrace()) { + traceType = (gcModeArg == GCModeFull + ? TraceFullGC + : TraceIncrementalGC); + + /* begin recordTrace: */ + assert(traceType < ((((TraceOwnerShift - TraceTypeShift) < 0) ? (((usqInt)(1)) >> (-(TraceOwnerShift - TraceTypeShift))) : (1U << (TraceOwnerShift - TraceTypeShift))))); + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(traceType) << TraceTypeShift)))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = 0; + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } +} + + /* CoInterpreter>>#primFailCodeAddress */ +usqInt +primFailCodeAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(primFailCode)))); +} + + +/* Return from a become primitive after invoking + become:with:twoWay:copyHash:. If succeeding and the top frame has been + divorced then continue in the interpreter. + See the comment in primitiveArrayBecome. */ + + /* CoInterpreter>>#primitiveBecomeReturn: */ +static NoDbgRegParms void +primitiveBecomeReturn(sqInt ec) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContextOrNil; + sqInt activeProc; + sqInt objOop; + + if (ec) { + /* primitiveFailFor: */ + GIV(primFailCode) = ec; + } + else { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + activeContextOrNil = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if (!GIV(framePointer)) { + assert(isContext(activeContextOrNil)); + + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContextOrNil); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(activeProc)) + && (!(isForwarded(activeProc)))); + assert(validStorePointerUncheckedArgs(SuspendedContextIndex, activeProc, GIV(nilObj))); + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* pop bogus machine-code instructionPointer and any primitive arguments */ + + /* begin pop: */ + GIV(stackPointer) += (GIV(argumentCount) + 1) * BytesPerWord; + ceInvokeInterpret(); + } + + /* If the top frame was divorced then continue in the interpreter. + If not, check where we are and continue... */ + if (!(activeContextOrNil == GIV(nilObj))) { + /* divorcing should not have disturbed the topFrame if it wasn;t divorced */ + assert(isContext(activeContextOrNil)); + assert(GIV(framePointer) == (frameOfMarriedContext(activeContextOrNil))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(activeProc)) + && (!(isForwarded(activeProc)))); + assert(validStorePointerUncheckedArgs(SuspendedContextIndex, activeProc, GIV(nilObj))); + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),GIV(nilObj)); + } + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } +} + + +/* receiver, args, then method are on top of stack. Execute method against + receiver and args. + Set primitiveFunctionPointer because no cache lookup has been done for the + method, and + hence primitiveFunctionPointer is stale. */ + + /* CoInterpreter>>#primitiveExecuteMethod */ +static void +primitiveExecuteMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodArgument; + sqInt methodHeader; + sqInt primitiveIndex; + sqInt top; + + methodArgument = longAt(GIV(stackPointer)); + if (!(/* isOopCompiledMethod: */ + ((!(methodArgument & (tagMask())))) + && (((byteAt((void *)(methodArgument + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (!((GIV(argumentCount) - 1) == (argumentCountOf(methodArgument)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(newMethod) = top; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]))); + GIV(argumentCount) -= 1; + executeNewMethodJitting(); + + /* Recursive xeq affects primErrorCode */ + + /* begin initPrimCall */ + GIV(primFailCode) = 0; +} + + +/* receiver, argsArray, then method are on top of stack. Execute method + against receiver and args. Allow for up to two extra arguments (e.g. for + mirror primitives). + Set primitiveFunctionPointer because no cache lookup has been done for the + method, and hence primitiveFunctionPointer is stale. */ + + /* CoInterpreter>>#primitiveExecuteMethodArgsArray */ +static void +primitiveExecuteMethodArgsArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt argCnt; + sqInt argumentArray; + usqInt firstBytecode; + sqInt header; + sqInt i; + sqInt methodArgument; + sqInt methodHeader; + usqInt numSlots; + sqInt primitiveIndex; + sqInt rcvr; + + methodArgument = longAt(GIV(stackPointer)); + argumentArray = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((/* isOopCompiledMethod: */ + ((!(methodArgument & (tagMask())))) + && (((byteAt((void *)(methodArgument + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* begin argumentCountOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodArgument)); + header = longAt((void *)((methodArgument + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + argCnt = (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; + if (!(argCnt == ((/* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + if (GIV(argumentCount) > 2) { + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if ((GIV(argumentCount) > 4) + || (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord),rcvr); + } + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + for (i = 0; i < argCnt; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))))); + } + GIV(newMethod) = methodArgument; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primitiveIndex)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primitiveIndex]))); + GIV(argumentCount) = argCnt; + executeNewMethodJitting(); + + /* Recursive xeq affects primErrorCode */ + + /* begin initPrimCall */ + GIV(primFailCode) = 0; +} + + +/* Answer any special requirements of the given primitive */ + + /* CoInterpreter>>#primitivePropertyFlags:primIndex: */ +sqInt +primitivePropertyFlagsprimIndex(sqInt methodObj, sqInt primIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int baseFlags; + + /* begin primitivePropertyFlagsForSpur:primIndex: */ + if (primIndex == PrimNumberRelinquishProcessor) { + return (GIV(profileSemaphore) + ? (PrimCallNeedsNewMethod + PrimCallMayEndureCodeCompaction) + PrimCallCollectsProfileSamples + : PrimCallNeedsNewMethod + PrimCallMayEndureCodeCompaction); + } + baseFlags = 0; + if (GIV(profileSemaphore)) { + if (isRealNonProfilingPrimitiveMethodprimIndex(methodObj, primIndex)) { + baseFlags = PrimCallCollectsProfileSamples + PrimCallNeedsNewMethod; + } + } +# if LRPCheck + if (GIV(longRunningPrimitiveCheckSemaphore)) { + baseFlags = baseFlags | PrimCallNeedsNewMethod; + } +# endif + + + /* Because one primitive number is used for all three vmParameter primtiives + the metadata can't be attached to the method implementation. */ + if ((primIndex == PrimNumberVMParameter) + && ((argumentCountOf(methodObj)) == 1)) { + return (baseFlags + PrimCallOnSmalltalkStack) + PrimCallOnSmalltalkStackAlign2x; + } + + /* vmParameterAt: */ + return baseFlags | ((primitiveMetadataTable[primIndex]) & SpurPrimitiveFlagsMask); +} + + /* CoInterpreter>>#primTraceLogAddress */ +void * +primTraceLogAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(primTraceLog); +} + + +/* N.B. primTraceLogIndex is 8-bits */ + + /* CoInterpreter>>#primTraceLogIndexAddress */ +usqInt +primTraceLogIndexAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(primTraceLogIndex)))); +} + + /* CoInterpreter>>#printCogMethod: */ +void +printCogMethod(CogMethod *cogMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + usqInt firstBytecode; + sqInt primitive; + sqInt s; + + address = ((sqInt)cogMethod); + printHex(address); + print(" <-> "); + printHex(address + ((cogMethod->blockSize))); + if (((cogMethod->cmType)) >= CMMethod) { + print(": method: "); + printHex((cogMethod->methodObject)); + + /* primitiveIndexOfMethod:header: */ + if (((((cogMethod->methodHeader)) & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (((cogMethod->methodObject)) + ((LiteralStart + (((((cogMethod->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitive = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitive = 0; + } + if (primitive) { + print(" prim "); + printNum(primitive); + } + if (addressCouldBeObj((cogMethod->methodObject))) { + if ((cogMethod->cpicHasMNUCaseOrCMIsFullBlock)) { + print(" [full]"); + } + else { + if (addressCouldBeObj(methodClassOf((cogMethod->methodObject)))) { + /* begin space */ + printChar(' '); + printNameOfClasscount(methodClassOf((cogMethod->methodObject)), 2); + } + } + } + } + if (((cogMethod->cmType)) == CMBlock) { + print(": block home: "); + printHex(((usqInt)(/* cmHomeMethod */ + ((((((CogBlockMethod *) cogMethod))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) cogMethod))) + : (assert(CMBlock == ((((CogBlockMethod *) cogMethod))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) cogMethod)))) - (((((CogBlockMethod *) cogMethod))->homeOffset))))))))); + } + if (((cogMethod->cmType)) == CMClosedPIC) { + print(": Closed PIC N: "); + printHex((cogMethod->cPICNumCases)); + } + if (((cogMethod->cmType)) == CMOpenPIC) { + print(": Open PIC "); + } + print(" selector: "); + printHex((cogMethod->selector)); + if (((cogMethod->selector)) == GIV(nilObj)) { + if ((((cogMethod->cmType)) >= CMMethod) + && (((s = maybeSelectorOfMethod((cogMethod->methodObject)))))) { + print(" (nil: "); + printStringOf(s); + print(")"); + } + else { + print(" (nil)"); + } + } + else { + /* begin space */ + printChar(' '); + printStringOf((cogMethod->selector)); + } + cr(); +} + + /* CoInterpreter>>#printFrameFlagsForFP: */ +static NoDbgRegParms void +printFrameFlagsForFP(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *address; + usqInt it; + + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + address = theFP + FoxMethod; + it = (longAt(address)) & 7; + } + else { + address = theFP + FoxIFrameFlags; + it = longAt(address); + } + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%3sfrm flags: %p", + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? "mc" + : "int"), + ((void *)it)); + if (it) { + fprintf(GIV(transcript), + "=%d", + ((int) it)); + } + fprintf(GIV(transcript), + " numArgs: %d %sContext %sBlock\n", + ((int) (/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))), + (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0) + ? "is" + : "no"), + (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0) + ? "is" + : "not")); +} + + /* CoInterpreter>>#printFrameMethodFor: */ +static NoDbgRegParms void +printFrameMethodFor(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *address; + sqInt it; + + address = theFP + FoxMethod; + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + " method: %16p\t" /* pst: */, + ((void *)it)); + if (((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && (((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0)) { + fprintf(GIV(transcript), + "hm: %p\t", + ((void *)(mframeHomeMethod(theFP)))); + } + shortPrintOop(/* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod))); +} + + /* CoInterpreter>>#printFrameThing:at:extra: */ +static NoDbgRegParms void +printFrameThingatextra(char *name, char *address, sqInt extraValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt it; + + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%12s: %16p" /* pst: */, + name, + ((void *)it)); + framePrintDescription(it); + fprintf(GIV(transcript), + " %" PRIdSQINT "\n", + extraValue); +} + + +/* useful for VM debugging */ + + /* CoInterpreter>>#printFrame:WithSP: */ +sqInt +printFrameWithSP(char *theFP, char *theSP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *addr; + CogBlockMethod *cogMethod; + CogMethod *homeMethod; + usqInt i; + usqInt index; + usqInt numArgs; + usqInt numSlots; + usqInt numTemps; + char *rcvrAddress; + sqInt rcvrOrClosure; + usqInt theMethod; + sqInt theMethodEnd; + char *toDoLimit; + sqInt topThing; + + if (!(/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + fprintf(GIV(transcript), + "%p is not in the stack zone?!\n", + theFP); + return null; + } + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + cogMethod = mframeCogMethod(theFP); + homeMethod = mframeHomeMethod(theFP); + theMethod = ((usqInt)homeMethod); + theMethodEnd = (((sqInt)homeMethod)) + ((homeMethod->blockSize)); + numArgs = (cogMethod->cmNumArgs); + numTemps = (((usqInt)(((homeMethod->methodHeader)))) >> MethodHeaderTempCountShift) & 0x3F; + } + else { + theMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + theMethodEnd = theMethod + (((((/* begin numSlotsOf: */ + assert((classIndexOf(theMethod)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(theMethod + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(theMethod - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) + BaseHeaderSize); + numArgs = byteAt((theFP + FoxIFrameFlags) + 1); + numTemps = tempCountOf(theMethod); + } + if (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0)) { + /* No BlockLocalTempCounter in the Cogit's C code, so quick hack is to use numCopied + numArgs */ + rcvrOrClosure = longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))); + if (((!(rcvrOrClosure & (tagMask())))) + && ((addressCouldBeObj(rcvrOrClosure)) + && ((fetchClassOfNonImm(rcvrOrClosure)) == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassBlockClosure) << (shiftForWord())))))))))) { + numTemps = numArgs + (stSizeOf(rcvrOrClosure)); + } + else { + numTemps = numArgs; + } + } + shortPrintFrame(theFP); + rcvrAddress = theFP + ((FoxCallerSavedIP + BytesPerWord) + ((numArgs << (shiftForWord())))); + if (longAt(theFP + FoxSavedFP)) { + } + else { + rcvrAddress + (2 * BytesPerWord); + + /* empty frameRange:to: */ + printFrameOopat("(caller ctxt", rcvrAddress + (2 * BytesPerWord)); + printFrameOopat("(saved ctxt", rcvrAddress + (1 * BytesPerWord)); + } + printFrameOopat("rcvr/clsr", rcvrAddress); + for (i = numArgs; i >= 1; i += -1) { + printFrameOopindexat("arg", numArgs - i, (theFP + FoxCallerSavedIP) + (i * BytesPerWord)); + } + printFrameThingatextraString("caller ip", theFP + FoxCallerSavedIP, ((longAt(theFP + FoxCallerSavedIP)) == (ceReturnToInterpreterPC()) + ? "ceReturnToInterpreter" + : 0)); + + /* begin printFrameThing:at: */ + printFrameThingatextraString("saved fp", theFP + FoxSavedFP, ((char *)null)); + printFrameMethodFor(theFP); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + printFrameFlagsForFP(theFP); + } + printFrameOopat("context", theFP + FoxThisContext); + if (!((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()))) { + printFrameFlagsForFP(theFP); + } + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + rcvrAddress = theFP + FoxMFReceiver; + } + else { + printFrameThingatextra("saved ip", theFP + FoxIFSavedIP, (((usqInt)(longAt(theFP + FoxIFSavedIP))) + ? (((((usqInt)(longAt(theFP + FoxIFSavedIP)))) - theMethod) + 2) - BaseHeaderSize + : 0)); + rcvrAddress = theFP + FoxIFReceiver; + } + printFrameOopat("receiver", rcvrAddress); + topThing = longAt(theSP); + if ((oopisGreaterThanOrEqualToandLessThan(topThing, theMethod, theMethodEnd)) + || ((topThing == (ceReturnToInterpreterPC())) + || ((topThing == (ceBaseFrameReturnPC())) + || (topThing == (ceCannotResumePC()))))) { + toDoLimit = theSP + BytesPerWord; + for (addr = (rcvrAddress - BytesPerWord); addr >= toDoLimit; addr += (-BytesPerWord)) { + index = ((rcvrAddress - addr) / BytesPerWord) + numArgs; + if (index <= numTemps) { + printFrameOopindexat("temp", index - 1, addr); + } + else { + printFrameOopat((/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0) + ? "temp/stck" + : "stck"), addr); + } + } + printFrameThingatextra("frame ip", theSP, (topThing > theMethod + ? ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? topThing - theMethod + : ((topThing - theMethod) + 2) - BaseHeaderSize) + : topThing)); + } + else { + for (addr = (rcvrAddress - BytesPerWord); addr >= theSP; addr += (-BytesPerWord)) { + index = ((rcvrAddress - addr) / BytesPerWord) + numArgs; + if (index <= numTemps) { + printFrameOopindexat("temp", index - 1, addr); + } + else { + printFrameOopat((/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0) + ? "temp/stck" + : "stck"), addr); + } + } + } + return 0; +} + + +/* useful for VM debugging */ + + /* CoInterpreter>>#printMethodCacheFor: */ +void +printMethodCacheFor(sqInt thing) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqIntptr_t c; + int i; + sqIntptr_t m; + sqInt n; + sqIntptr_t p; + sqIntptr_t s; + + n = 0; + for (i = 0; i < MethodCacheSize; i += MethodCacheEntrySize) { + s = GIV(methodCache)[i + MethodCacheSelector]; + c = GIV(methodCache)[i + MethodCacheClass]; + m = GIV(methodCache)[i + MethodCacheMethod]; + p = GIV(methodCache)[i + MethodCachePrimFunction]; + if (((thing == -1) + || ((s == thing) + || ((c == thing) + || ((p == thing) + || ((m == thing) + || ((addressCouldBeObj(m)) + && ((/* maybeMethodHasCogMethod: */ + ((!(m & (tagMask())))) + && ((((byteAt((void *)(m + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((m + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) + && ((((sqInt)(cogMethodOf(m)))) == thing)))))))) + && ((/* addressCouldBeOop: */ + (((s & (tagMask())) != 0)) + || (addressCouldBeObj(s))) + && ((c != 0) + && ((addressCouldBeClassObj(c)) + || (addressCouldBeClassObj(classForClassTag(c))))))) { + n += 1; + fprintf(GIV(transcript), + "%d %x\n\t", + ((int) i), + ((int) i)); + if (((byteAt((void *)(s + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())) { + fprintf(GIV(transcript), + "%p %.*s\n", + ((void *)s), + ((int) (numBytesOfBytes(s))), + ((char *) (firstIndexableField(s)))); + } + else { + shortPrintOop(s); + } + + /* begin tab */ + printChar('\t'); + if (addressCouldBeClassObj(c)) { + shortPrintOop(c); + } + else { + printNum(c); + + /* begin space */ + printChar(' '); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)c)); + printChar(' '); + shortPrintOop(classForClassTag(c)); + } + + /* begin tab */ + printChar('\t'); + shortPrintOop(m); + + /* begin tab */ + printChar('\t'); + if (p > 0x400) { + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)p)); + } + else { + printNum(p); + } + cr(); + } + } + if (n > 1) { + printNum(n); + cr(); + } +} + + /* CoInterpreter>>#quickPrimitiveConstantFor: */ +sqInt +quickPrimitiveConstantFor(sqInt aQuickPrimitiveIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + switch (aQuickPrimitiveIndex) { + case 0x101: + return GIV(trueObj); + + case 258: + return GIV(falseObj); + + case 259: + return GIV(nilObj); + + case 260: + return ConstMinusOne; + + case 261: + return ConstZero; + + case 262: + return ConstOne; + + case 263: + return ConstTwo; + + default: + error("Case not found and no otherwise clause"); + } + return 0; +} + + /* CoInterpreter>>#quickPrimitiveGeneratorFor: */ +sqInt +(*quickPrimitiveGeneratorFor(sqInt aQuickPrimitiveIndex))(void) +{ + switch (aQuickPrimitiveIndex) { + case 0x100: + return genQuickReturnSelf; + + case 0x101: + case 258: + case 259: + case 260: + case 261: + case 262: + case 263: + return genQuickReturnConst; + + default: + return genQuickReturnInstVar; + } + return 0; +} + + /* CoInterpreter>>#quickPrimitiveInstVarIndexFor: */ +sqInt +quickPrimitiveInstVarIndexFor(sqInt primIndex) +{ + return primIndex - 264; +} + + /* CoInterpreter>>#rawHeaderOf: */ +sqInt +rawHeaderOf(sqInt methodPointer) +{ + return longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); +} + + +/* Since methods may be updated while forwarding during become, make the + assert accomodate this. + */ + + /* CoInterpreter>>#rawHeaderOf:put: */ +void +rawHeaderOfput(sqInt methodOop, sqInt cogMethodOrMethodHeader) +{ + assert(isCompiledMethodHeader(headerWhileForwardingOf(methodOop))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(methodOop)) + && (!(isForwarded(methodOop)))); + assert(validStorePointerUncheckedArgs(HeaderIndex, methodOop, cogMethodOrMethodHeader)); + longAtput((void *)((methodOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))),cogMethodOrMethodHeader); +} + + +/* Read an image from the given file stream, allocating an amount of memory + to its object heap. + + V3: desiredHeapSize is the total size of the heap. Fail if the image has + an unknown format or + requires more than the specified amount of memory. + + Spur: desiredHeapSize is ignored; this routine will attempt to provide at + least extraVMMemory's + ammount of free space after the image is loaded, taking any free space in + the image into account. + extraVMMemory is stored in the image header and is accessible as + vmParameterAt: 23. If + extraVMMemory is 0, the value defaults to the default grow headroom. Fail + if the image has an + unknown format or if sufficient memory cannot be allocated. + + Details: This method detects when the image was stored on a machine with + the opposite byte + ordering from this machine and swaps the bytes automatically. Furthermore, + it allows the header + information to start 512 bytes into the file, since some file transfer + programs for the Macintosh + apparently prepend a Mac-specific header of this size. Note that this same + 512 bytes of prefix + area could also be used to store an exec command on Unix systems, allowing + one to launch + Smalltalk by invoking the image name as a command. */ + + /* CoInterpreter>>#readImageFromFile:HeapSize:StartingAt: */ +size_t +readImageFromFileHeapSizeStartingAt(sqImageFile f, usqInt desiredHeapSize, squeakFileOffsetType imageOffset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt allocationReserve; + sqInt anObject; + sqInt bit; + sqInt bytes; + sqInt bytesRead; + sqInt bytesToShift; + sqInt classArrayClass; + sqInt classArrayObj; + sqInt cogCodeBase; + size_t dataSize; + sqInt firstSegSize; + sqInt freeOldSpaceInImage; + usqInt hdrCogCodeSize; + sqInt hdrEdenBytes; + usqInt hdrMaxExtSemTabSize; + usqInt hdrNumStackPages; + sqInt headerSize; + squeakFileOffsetType headerStart; + sqInt headroom; + sqInt headroomSqInt; + usqInt heapSize; + usqInt heapSizeUsqInt; + sqInt i; + usqInt mem; + usqInt minimumMemory; + usqInt numSlots; + sqInt objOop; + sqInt oop; + sqInt rawVersion; + sqInt seed; + int swapBytes; + sqInt toDoLimit; + sqInt toDoLimit1; + sqInt version; + int w; + unsigned short w1; + sqInt wSqInt; + + rawVersion = 0; + + /* stdout is not available at compile time. this is the earliest available point. */ + GIV(transcript) = stdout; + + /* guess Metaclass instSize */ + GIV(metaclassNumSlots) = 6; + + /* guess (Class instVarIndexFor: 'name' ifAbsent: []) - 1 */ + GIV(classNameIndex) = 6; + version = checkImageVersionFromstartingAtassignRawVersion(f, imageOffset, (&rawVersion)); + if (!version) { + /* begin bailOutOfImageLoad: */ + fprintf(stderr, + "This ver %d vm cannot read ver %" PRIdSQINT " image file %s\n", + ((int) 68021 /* imageFormatVersion */), + rawVersion, + getImageName()); + } + swapBytes = rawVersion != version; + GIV(multipleBytecodeSetsActive) = ((version & MultipleBytecodeSetsBitmask) != 0); + + /* record header start position */ + headerStart = (sqImageFilePosition(f)) - 4; + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + headerSize = (swapBytes + ? SQ_SWAP_4_BYTES(w) + : w); + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + dataSize = ((sqInt) ((swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt))); + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + GIV(oldImageBaseAddress) = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + anObject = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin specialObjectsOop: */ + GIV(specialObjectsOop) = anObject; + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + seed = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin lastHash: */ + if (!((GIV(lastHash) = seed & (identityHashHalfWordMask())))) { + while (((GIV(lastHash) = (((usqInt) (ioUTCMicrosecondsNow()))) & (identityHashHalfWordMask()))) == 0) { + } + } + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + GIV(savedWindowSize) = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + imageHeaderFlags = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + extraVMMemory = (swapBytes + ? SQ_SWAP_4_BYTES(w) + : w); + + /* begin getShortFromFile:swap: */ + w1 = 0; + sqImageFileRead((&w1), sizeof(unsigned short), 1, f); + hdrNumStackPages = (swapBytes + ? ((((usqInt)(w1)) >> 8) & 0xFF) | (((w1 & 0xFF) << 8)) + : w1); + + /* 4 stack pages is small. Should be able to run with as few as + three. 4 should be comfortable but slow. 8 is a reasonable + default. Can be changed via vmParameterAt: 43 put: n. + Can be set as a preference (Info.plist, VM.ini, command line etc). + If desiredNumStackPages is already non-zero then it has been + set as a preference. Ignore (but preserve) the header's default. */ + GIV(numStackPages) = (desiredNumStackPages + ? desiredNumStackPages + : (hdrNumStackPages + ? hdrNumStackPages + : defaultNumStackPages())); + desiredNumStackPages = hdrNumStackPages; + + /* This slot holds the size of the native method zone in 1k units. (pad to word boundary). */ + hdrCogCodeSize = (getShortFromFileswap(f, swapBytes)) * 0x400; + + /* set for vmParameter 47 */ + GIV(cogCodeSize) = (desiredCogCodeSize + ? desiredCogCodeSize + : (hdrCogCodeSize + ? (desiredCogCodeSize = hdrCogCodeSize) + : defaultCogCodeSize())); + if (GIV(cogCodeSize) > (maxCogCodeSize())) { + GIV(cogCodeSize) = maxCogCodeSize(); + } + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + hdrEdenBytes = (swapBytes + ? SQ_SWAP_4_BYTES(w) + : w); + bytes = (desiredEdenBytes + ? desiredEdenBytes + : (hdrEdenBytes + ? hdrEdenBytes + : defaultEdenBytes())); + + /* begin edenBytes: */ + GIV(edenBytes) = bytes; + desiredEdenBytes = hdrEdenBytes; + + /* begin getShortFromFile:swap: */ + w1 = 0; + sqImageFileRead((&w1), sizeof(unsigned short), 1, f); + hdrMaxExtSemTabSize = (swapBytes + ? ((((usqInt)(w1)) >> 8) & 0xFF) | (((w1 & 0xFF) << 8)) + : w1); + if (hdrMaxExtSemTabSize) { + /* begin setMaxExtSemSizeTo: */ + GIV(maxExtSemTabSizeSet) = 1; + ioSetMaxExtSemTableSize(hdrMaxExtSemTabSize); + } + + /* begin getShortFromFile:swap: */ + w1 = 0; + sqImageFileRead((&w1), sizeof(unsigned short), 1, f); + GIV(the2ndUnknownShort) = (swapBytes + ? ((((usqInt)(w1)) >> 8) & 0xFF) | (((w1 & 0xFF) << 8)) + : w1); + + /* Set maxLiteralCountForCompile unless it has already been set on the command line. */ + if (maxLiteralCountForCompile < 0) { + maxLiteralCountForCompile = (GIV(the2ndUnknownShort) + ? GIV(the2ndUnknownShort) + : MaxLiteralCountForCompile); + } + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + firstSegSize = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin firstSegmentSize: */ + GIV(firstSegmentSize) = firstSegSize; + if (!(((imageHeaderFlags & 4) != 0))) { + error("Process must have the threadAffinity inst var to thread"); + } + + /* compare memory requirements with availability */ + allocationReserve = interpreterAllocationReserveBytes(); + minimumMemory = ((GIV(cogCodeSize) + dataSize) + GIV(edenBytes)) + allocationReserve; + + /* no need to include the stackZone; this is alloca'ed + Compute how much space is needed for the initial heap allocation. + no need to include the stackZone; this is alloca'ed. + no need to include the JIT code zone size; this is allocated separately. */ + + /* begin getLongFromFile:swap: */ + wSqInt = 0; + sqImageFileRead((&wSqInt), sizeof(wSqInt), 1, f); + freeOldSpaceInImage = (swapBytes + ? SQ_SWAP_8_BYTES(wSqInt) + : wSqInt); + + /* begin initialHeadroom:givenFreeOldSpaceInImage: */ + headroomSqInt = (extraVMMemory + ? extraVMMemory + : (!(GIV(growHeadroom)) + ? 0x1000000 + : GIV(growHeadroom))); + if (freeOldSpaceInImage >= headroomSqInt) { + headroom = 0; + goto l1; + } + if (freeOldSpaceInImage >= ((headroomSqInt * 7) / 8)) { + headroom = headroomSqInt / 8; + goto l1; + } + if (freeOldSpaceInImage >= ((headroomSqInt * 3) / 4)) { + headroom = headroomSqInt / 4; + goto l1; + } + if (freeOldSpaceInImage >= ((headroomSqInt * 5) / 8)) { + headroom = (headroomSqInt * 3) / 8; + goto l1; + } + if (freeOldSpaceInImage >= (headroomSqInt / 2)) { + headroom = headroomSqInt / 2; + goto l1; + } + headroom = headroomSqInt; + /* end initialHeadroom:givenFreeOldSpaceInImage: */ +l1: + heapSizeUsqInt = ((dataSize + headroom) + GIV(edenBytes)) + ((headroom > allocationReserve + ? 0 + : allocationReserve)); + + /* begin roundUpHeapSize: */ + bit = (((highBit(heapSizeUsqInt)) - 1) * 3) / 4; + heapSize = (((heapSizeUsqInt & ((1ULL << bit) - 1)) != 0) + ? (((heapSizeUsqInt | ((1ULL << bit) - 1)) - ((1ULL << bit) - 1))) + (1ULL << bit) + : heapSizeUsqInt); + + /* allocateJITMemory will assign the actual size allocated, which is rounded up to a page boundary. */ + cogCodeBase = ((sqInt)(allocateJITMemory((&GIV(cogCodeSize))))); + + /* allocate a contiguous block of memory for the Squeak heap and ancilliary data structures */ + if ((mem = ((usqInt)(allocateMemoryMinimumImageFileHeaderSize(heapSize, minimumMemory, f, headerSize))))) { + /* cannot clash with the variable memory still in use in NewCoObjectMemory and superclasses */ + setHeapBasememoryLimitendOfMemory((heapBase = mem), mem + heapSize, mem + dataSize); + } + else { + insufficientMemoryAvailableError(); + } + + /* position file after the header */ + sqImageFileSeek(f, headerStart + headerSize); + + /* read in the image in bulk, then swap the bytes if necessary */ + bytesRead = readHeapFromImageFiledataBytes(f, dataSize); + if (bytesRead != dataSize) { + unableToReadImageError(); + } + ensureImageFormatIsUpToDate(swapBytes); + + /* compute difference between old and new memory base addresses */ + bytesToShift = GIV(oldSpaceStart) - GIV(oldImageBaseAddress); + + /* begin initializeInterpreter: */ + interpreterProxy = sqGetInterpreterProxy(); + (&interpreterProxy); + initializeObjectMemory(bytesToShift); + + /* begin checkAssumedCompactClasses */ + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassArrayCompactIndex >= 1) && (ClassArrayCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassArrayCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("Array"); + } + + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassLargeNegativeInteger) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassLargeNegativeIntegerCompactIndex >= 1) && (ClassLargeNegativeIntegerCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassLargeNegativeIntegerCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("LargeNegativeInteger"); + } + + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassLargePositiveInteger) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassLargePositiveIntegerCompactIndex >= 1) && (ClassLargePositiveIntegerCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassLargePositiveIntegerCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("LargePositiveInteger"); + } + + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassFloat) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassFloatCompactIndex >= 1) && (ClassFloatCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassFloatCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("Float"); + } + + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassBlockClosure) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassBlockClosureCompactIndex >= 1) && (ClassBlockClosureCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassBlockClosureCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("BlockClosure"); + } + + /* begin checkCompactIndex:isClass:named: */ + if ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassMethodContext) << (shiftForWord()))))))) != ((/* begin knownClassAtIndex: */ + assert(((ClassMethodContextCompactIndex >= 1) && (ClassMethodContextCompactIndex <= (classTablePageSize())))), + /* fetchPointer:ofObject: */ + longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassMethodContextCompactIndex) << (shiftForWord()))))))))) { + invalidCompactClassError("MethodContext"); + } + objOop = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteArray) << (shiftForWord())))))); + + /* begin compactIndexOfClass: */ + assert((rawHashBitsOf(objOop)) != 0); + GIV(classByteArrayCompactIndex) = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()); + + /* begin initializeExtraClassInstVarIndices */ + classArrayObj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))); + classArrayClass = fetchClassOfNonImm(classArrayObj); + + /* begin numSlotsOf: */ + assert((classIndexOf(classArrayClass)) > (isForwardedObjectClassIndexPun())); + + /* determine actual Metaclass instSize */ + GIV(metaclassNumSlots) = (((numSlots = byteAt((void *)(classArrayClass + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classArrayClass - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + + /* default */ + GIV(thisClassIndex) = 5; + /* begin numSlotsOf: */ + assert((classIndexOf(classArrayClass)) > (isForwardedObjectClassIndexPun())); + if (((numSlots = byteAt((void *)(classArrayClass + (numSlotsFieldByteOffset()))))) == (numSlotsMask())) { + toDoLimit = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classArrayClass - BaseHeaderSize)))) << 8)))))) >> 8; + } + else { + toDoLimit = numSlots; + } + for (i = (InstanceSpecificationIndex + 1); i <= toDoLimit; i += 1) { + if ((longAt((void *)((classArrayClass + BaseHeaderSize) + ((((usqInt)((i - 1)) << (shiftForWord()))))))) == classArrayObj) { + GIV(thisClassIndex) = i - 1; + } + } + + /* default */ + GIV(classNameIndex) = 6; + /* begin numSlotsOf: */ + assert((classIndexOf(classArrayObj)) > (isForwardedObjectClassIndexPun())); + if (((numSlots = byteAt((void *)(classArrayObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask())) { + toDoLimit1 = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classArrayObj - BaseHeaderSize)))) << 8)))))) >> 8; + } + else { + toDoLimit1 = numSlots; + } + for (i = (InstanceSpecificationIndex + 1); i <= toDoLimit1; i += 1) { + oop = longAt((void *)((classArrayObj + BaseHeaderSize) + ((((usqInt)((i - 1)) << (shiftForWord())))))); + if (objectequalsString(oop, "Array")) { + GIV(classNameIndex) = i - 1; + } + } + GIV(method) = (GIV(newMethod) = GIV(nilObj)); + GIV(methodDictLinearSearchLimit) = 8; + + /* begin initialCleanup */ + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + + /* begin flushExternalPrimitiveTable */ + memset(externalPrimitiveTable, 0, MaxExternalPrimitiveTableSize * (sizeof(externalPrimitiveTable[0]))); + GIV(externalPrimitiveTableFirstFreeIndex) = 0; + + /* cmd-. as used for Mac but no other OS */ + GIV(interruptKeycode) = ((0x800)) + (((sqInt)'.')); + while (GIV(globalSessionID) == 0) { + GIV(globalSessionID) = ((time(NULL)) + (ioMSecs())) & 0x7FFFFFFF; + } + GIV(metaAccessorDepth) = -2; + sHEAFn = ioLoadFunctionFrom("secHasEnvironmentAccess", "SecurityPlugin"); + if (!((lengthOf(GIV(specialObjectsOop))) > ForeignCallbackProcess)) { + error("The image is unsuitable for the MT VM because specialObjectsArray is too short to contain the foreign callback process."); + } + atomic_store((&GIV(maxWaitingPriority)), 0); + initializeCodeZoneFromupTo(cogCodeBase, cogCodeBase + GIV(cogCodeSize)); + return dataSize; +} + + +/* This is a little elaborate. The primTraceLog is only useful if it is not + full of noise. + To reduce noise when debugging a specific plugin we allow a plugin name to + be specified and will only generate the primTraceLog code for primitives + in that plugin. */ + + /* CoInterpreter>>#recordFastCCallPrimTraceForMethod: */ +sqInt +recordFastCCallPrimTraceForMethod(sqInt aMethodObj) +{ + return (recordFastCCallPrimTrace()) + && ((primTracePluginName + ? methodHasPrimitiveInPrimTracePlugin(aMethodObj) + : 1)); +} + + +/* This is a little elaborate. The primTraceLog is only useful if it is not + full of noise. + To reduce noise when debugging a specific plugin we allow a plugin name to + be specified and will only generate the primTraceLog code for primitives + in that plugin. */ + + /* CoInterpreter>>#recordPrimTraceForMethod: */ +sqInt +recordPrimTraceForMethod(sqInt aMethodObj) +{ + return (recordPrimTrace()) + && ((primTracePluginName + ? methodHasPrimitiveInPrimTracePlugin(aMethodObj) + : 1)); +} + + +/* noop in the StackInterpreter */ + + /* CoInterpreter>>#reloadPrimitiveCalloutPointer */ +static NeverInline void +reloadPrimitiveCalloutPointer(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + char *moduleName; + + primitiveCalloutPointer = ioLoadFunctionFrom("primitiveCallout", "SqueakFFIPrims"); + + /* Find out whether SqueakFFIPrims is internal or external */ + GIV(primCalloutIsExternal) = 1; + index = 1; + do { + if ((moduleName = ioListBuiltinModule(index))) { + if (!(strcmp(moduleName, "SqueakFFIPrims"))) { + GIV(primCalloutIsExternal) = 0; + return; + } + } + else { + return; + } + index += 1; + } while(1); +} + + +/* Report the stack page size and minimum unused headroom to stdout. */ + + /* CoInterpreter>>#reportMinimumUnusedHeadroom */ +void +reportMinimumUnusedHeadroom(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fprintf(GIV(transcript), + "stack page bytes %" PRIdSQINT " available headroom %" PRIdSQINT " minimum unused headroom %" PRIdSQINT "\n", + stackPageByteSize(), + ((stackPageByteSize()) - ((((stackPageFrameBytes()) < (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))) ? (stackPageFrameBytes()) : (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))))) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord), + minimumUnusedHeadroom()); +} + + +/* Report the stack page size and minimum unused headroom to a stream. */ + + /* CoInterpreter>>#reportMinimumUnusedHeadroomOn: */ +void +reportMinimumUnusedHeadroomOn(FILE *aStdioStream) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + FILE *savedTranscript; + + /* begin withRedirectedOutputTo:do: */ + savedTranscript = GIV(transcript); + GIV(transcript) = (aStdioStream + ? aStdioStream + : stdout); + + /* begin reportMinimumUnusedHeadroom */ + fprintf(GIV(transcript), + "stack page bytes %" PRIdSQINT " available headroom %" PRIdSQINT " minimum unused headroom %" PRIdSQINT "\n", + stackPageByteSize(), + ((stackPageByteSize()) - ((((stackPageFrameBytes()) < (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))) ? (stackPageFrameBytes()) : (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))))) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord), + minimumUnusedHeadroom()); + GIV(transcript) = savedTranscript; +} + + +/* Return to the current frame, either by entering machine code, or + longjmp-ing back to the + interpreter or simply returning, depending on where we are. To know + whether to return or + enter machine code we have to know from whence we came. We could have come + from the interpreter, either directly or via a machine code primitive. We + could have come from + machine code. The instructionPointer tells us where from. If it is above + startOfMemory we're + in the interpreter. If it is below, then we are in machine-code unless it + is ceReturnToInterpreterPC, + in which case we're in a machine-code primitive called from the + interpreter. */ + + /* CoInterpreter>>#returnToExecutive:postContextSwitch: */ +static NoDbgRegParms sqInt +returnToExecutivepostContextSwitch(sqInt inInterpreter, sqInt switchedContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + CogBlockMethod *cogMethod; + sqInt fullyInInterpreter; + sqInt header; + sqInt methodHeader; + sqInt retValue; + + assertCStackWellAligned(); + if ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())) { + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + + /* If returning after a context switch then a result may have to be popped from the stack. + If the process is suspended at a send then the result of the primitive in which the + process was suspended is still on the stack and must be popped into ReceiverResultReg. + If not, nothing should be popped and ReceiverResultReg gets the receiver. */ + if (switchedContext) { + /* begin mframeCogMethod: */ + cogMethod = ((CogBlockMethod *) ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodMask)); + assert((GIV(instructionPointer) > (minCogMethodAddress())) + && (GIV(instructionPointer) < (maxCogMethodAddress()))); + if ((GIV(instructionPointer) != ((((sqInt)cogMethod)) + ((cogMethod->stackCheckOffset)))) + && (isSendReturnPC(GIV(instructionPointer)))) { + assert(addressCouldBeOop(stackTop())); + + /* begin popStack */ + retValue = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + } + else { + retValue = longAt(GIV(framePointer) + FoxMFReceiver); + } + } + else { + retValue = longAt(GIV(framePointer) + FoxMFReceiver); + } + assert(isPCWithinMethodZone(GIV(instructionPointer))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),retValue); + ceEnterCogCodePopReceiverReg(); + } + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + fullyInInterpreter = inInterpreter; + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + fullyInInterpreter = 0; + } + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 1, __LINE__); + if (!fullyInInterpreter) { + ceInvokeInterpret(); + } + return null; +} + + +/* We have made a context switch, either when interpreting or from machine + code. Effectively return to the current frame, either by entering machine + code, or + longjmp-ing back to the interpreter or simply returning, depending on + where we are. */ + + /* CoInterpreter>>#return:toExecutive: */ +static NoDbgRegParms sqInt +returntoExecutive(sqInt returnValue, sqInt inInterpreter) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + sqInt header; + sqInt methodHeader; + char *sp; + + assertCStackWellAligned(); + if ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())) { + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 0, __LINE__); + assert(isPCWithinMethodZone(GIV(instructionPointer))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + ceEnterCogCodePopReceiverReg(); + } + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),returnValue); + GIV(stackPointer) = sp; + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), 1, __LINE__); + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + if (inInterpreter) { + return null; + } + ceInvokeInterpret(); + + /* NOTREACHED */ + return null; +} + + /* CoInterpreter>>#shortPrintFrame: */ +static NoDbgRegParms sqInt +shortPrintFrame(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + sqInt mthd; + sqInt rcvr; + + if (!(/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "invalid frame pointer"); + cr(); + return null; + } + rcvr = /* frameReceiver: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? longAt(theFP + FoxMFReceiver) + : longAt(theFP + FoxIFReceiver)); + mthd = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + printHexPtrnp(theFP); + + /* begin space */ + printChar(' '); + printChar(((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? 'M' + : 'I')); + + /* begin space */ + printChar(' '); + printActivationNameForreceiverisBlockfirstTemporary(mthd, rcvr, /* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0), /* temporary:in: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (0 < ((frameNumArgs = ((mframeCogMethod(theFP))->cmNumArgs))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgs) * BytesPerWord)) + : longAt(((theFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs) * BytesPerWord))) + : /* itemporary:in: */ + (0 < ((frameNumArgsUsqInt = byteAt((theFP + FoxIFrameFlags) + 1))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt) * BytesPerWord)) + : longAt(((theFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt) * BytesPerWord))))); + + /* begin space */ + printChar(' '); + shortPrintOop(rcvr); + return 0; +} + + +/* Answer a full 32 bit integer object for the given integer value. + N.B. Returning in each arm separately enables Slang inlining. + /Don't/ return the ifTrue:ifFalse: unless Slang inlining of conditionals + is fixed. */ + + /* CoInterpreter>>#signed32BitIntegerFor: */ +sqInt +signed32BitIntegerFor(sqInt integerValue) +{ + return (((usqInt)(((int) integerValue)) << 3) | 1); +} + + +/* Convert the given object into an integer value. + The object may be either a SmallInteger or a four-byte LargeInteger. */ + + /* CoInterpreter>>#signed32BitValueOf: */ +int +signed32BitValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt value64; + + if ((((oop) & 7) == 1)) { + value64 = (oop >> 3); + if ((((int) value64)) != value64) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + value64 = 0; + } + return value64; + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; +} + + +/* Answer a Large Integer object for the given integer value. N.B. will *not* + cause a GC. */ + + /* CoInterpreter>>#signed64BitIntegerFor: */ +sqInt +signed64BitIntegerFor(sqLong integerValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt largeClass; + usqLong magnitude; + usqInt newLargeInteger; + usqInt newObj; + usqInt numBytes; + sqInt numSlots; + sqInt objFormat; + sqInt sz; + + if (integerValue < 0) { + if (integerValue >= (MinSmallInteger)) { + return (((usqInt)(((sqInt)integerValue)) << 3) | 1); + } + largeClass = ClassLargeNegativeIntegerCompactIndex; + magnitude = 0 - (((usqLong) integerValue)); + } + else { + if (integerValue <= (MaxSmallInteger)) { + return (((usqInt)(((sqInt)integerValue)) << 3) | 1); + } + largeClass = ClassLargePositiveIntegerCompactIndex; + magnitude = integerValue; + } + sz = 8; + objFormat = (firstByteFormat()) + ((8 - sz) & (BytesPerWord - 1)); + numSlots = (sz + 3) / BytesPerOop; + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((numSlots >= 0) + && ((largeClass != 0) + && ((knownClassAtIndex(largeClass)) != GIV(nilObj)))); + assert(((objFormat < (firstByteFormat()) + ? objFormat + : objFormat & (byteFormatMask()))) == (instSpecOfClass(knownClassAtIndex(largeClass)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newLargeInteger = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + largeClass); + GIV(freeStart) += numBytes; + newLargeInteger = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + + /* Memory is eight byte aligned in SPUR, so we are sure to have room for 64bits word whatever allocated sz */ + + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((newLargeInteger + BaseHeaderSize)),SQ_SWAP_8_BYTES_IF_BIGENDIAN(magnitude)); + return newLargeInteger; +} + + +/* Convert the given object into an integer value. + The object may be either a positive SmallInteger or a eight-byte + LargeInteger. + */ + + /* CoInterpreter>>#signed64BitValueOf: */ +sqLong +signed64BitValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + sqInt fmt; + usqLong magnitude; + sqInt negative; + usqInt numSlots; + int ok; + sqInt sz; + + if ((((oop) & 7) == 1)) { + return ((sqLong) ((oop >> 3))); + } + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (ok) { + negative = 0; + } + else { + negative = 1; + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargeNegativeIntegerCompactIndex == ccIndex; + if (!ok) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + sz = ((((/* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (sz > 4) { + magnitude = SQ_SWAP_8_BYTES_IF_BIGENDIAN((long64At((void *)((oop + BaseHeaderSize))))); + if ((sz > (sizeof(sqLong))) + || ((negative + ? magnitude > 0x8000000000000000ULL + : magnitude >= 0x8000000000000000ULL))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + } + else { + magnitude = ((unsigned int) (SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((oop + BaseHeaderSize))))))); + } + return (negative + ? 0 - magnitude + : magnitude); +} + + +/* */ + + /* CoInterpreter>>#specialSelectorNumArgs: */ +sqInt +specialSelectorNumArgs(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((fetchPointerofObject((index * 2) + 1, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))))) >> 3); +} + + /* CoInterpreter>>#stackLimitAddress */ +usqInt +stackLimitAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(stackLimit)))); +} + + +/* Answer the amount of slots needed to fit a new frame at the point the + stack limit is checked. A frame looks like this at the point the stack + limit is checked: + stacked receiver/closure + arg0 + ... + argN + caller's method ip/base frame's sender context + fp-> saved fp + method + context (uninitialized?) + method header fields (interpreter only) + saved method ip (uninitialized?; interpreter only) + receiver + first temp + ... + sp-> Nth temp + So the amount of headroom is + the maximum number of arguments + 1 (for stacked receiver and arguments) + + the frame size + + the max number of temps. + Since a method's number of temps includes its arguments the actual offset + is: */ + + /* CoInterpreter>>#stackLimitOffset */ +static sqInt +stackLimitOffset(void) +{ + return ((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord; +} + + +/* Return a minimum amount of headroom for each stack page (in bytes). + In the interpreter we don't actually need any headroom. In a JIT the stack + has to have room for interrupt handlers which will run on the stack. + Defer to the platform for this one. */ + + /* CoInterpreter>>#stackPageHeadroom */ +static sqInt +stackPageHeadroom(void) +{ + return osCogStackPageHeadroom(); +} + + /* CoInterpreter>>#stackPointerAddress */ +usqInt +stackPointerAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(stackPointer)))); +} + + +/* Return the 1-based index rel to the given frame. In the StackInterpreter + stacks grow down. + */ + + /* CoInterpreter>>#stackPointerIndexForFrame:WithSP: */ +static NoDbgRegParms sqInt +stackPointerIndexForFrameWithSP(char *theFP, char *theSP) +{ + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (((usqInt)(((theFP + FoxMFReceiver) - theSP))) >> (shiftForWord())) + (((mframeCogMethod(theFP))->cmNumArgs)) + : (((usqInt)(((theFP + FoxIFReceiver) - theSP))) >> (shiftForWord())) + (byteAt((theFP + FoxIFrameFlags) + 1))); +} + + +/* Zero-relative version of CompiledMethod>>startpc. */ + + /* CoInterpreter>>#startPCOfMethodHeader: */ +usqInt +startPCOfMethodHeader(sqInt aCompiledMethodHeader) +{ + return (((/* begin literalCountOfMethodHeader: */ + assert((((aCompiledMethodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((aCompiledMethodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop; +} + + +/* Answer the startPC of lit if it is a (clean) block in aMethodObj, + otherwise answer nil. + */ + + /* CoInterpreter>>#startPCOrNilOfLiteral:in: */ +sqInt +startPCOrNilOfLiteralin(sqInt lit, sqInt aMethodObj) +{ + usqInt numSlots; + sqInt oop; + sqInt outerContext; + + if (!(((!(lit & (tagMask())))) + && ((((byteAt((void *)(lit + (formatFieldByteOffset())))) & (formatMask())) == (indexablePointersFormat())) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(lit)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(lit + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(lit - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= ClosureFirstCopiedValueIndex)))) { + return null; + } + outerContext = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + if (!(/* isContext: */ + ((!(outerContext & (tagMask())))) + && (((longAt((void *)(outerContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + return null; + } + if (aMethodObj != (longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))))) { + return null; + } + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + return (oop >> 3); +} + + +/* Handle the cannot return response for a base frame return to an invalid + context. Build a new base frame for the context in the cannot resume state + ready for the + send of cannotReturn:. + + Since we have returned from the base frame of the page the context is + effectively widowed. + But its sender needs to be contextToReturnTo, and its pc needs to be the + HasBeenReturnedFromMCPC marker. So bereave it (as a side-effect of + isWidowedContext:), assign contextToReturnTo to + sender, and rebuild its frame, which will have the ceCannotResumePC as its + pc. Finally push + returnValue and set instructionPointer to ceCannotResumePC in preparation + for the send. */ + + /* CoInterpreter>>#tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFrom:to:returnValue: */ +static NoDbgRegParms void +tearDownAndRebuildFrameForCannotReturnBaseFrameReturnFromtoreturnValue(sqInt contextToReturnFrom, sqInt contextToReturnTo, sqInt returnValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *newPage; + + assert((GIV(stackPage)) + && (isFree(GIV(stackPage)))); + isWidowedContext(contextToReturnFrom); + assert(!(isMarriedOrWidowedContext(contextToReturnFrom))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SenderIndex, contextToReturnFrom, contextToReturnTo)); + assert(isNonImmediate(contextToReturnFrom)); + if (oopisGreaterThanOrEqualTo(contextToReturnFrom, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(contextToReturnTo & (tagMask())))) + && (oopisLessThan(contextToReturnTo, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(contextToReturnFrom + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(contextToReturnFrom); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((contextToReturnFrom + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),contextToReturnTo); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(InstructionPointerIndex, contextToReturnFrom, HasBeenReturnedFromMCPCOop)); + assert(isNonImmediate(contextToReturnFrom)); + if (oopisGreaterThanOrEqualTo(contextToReturnFrom, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(HasBeenReturnedFromMCPCOop & (tagMask())))) + && (oopisLessThan(HasBeenReturnedFromMCPCOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(contextToReturnFrom + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(contextToReturnFrom); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((contextToReturnFrom + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),HasBeenReturnedFromMCPCOop); + + /* void the instructionPointer to stop it being incorrectly updated in a code + compaction in makeBaseFrameFor:. */ + GIV(instructionPointer) = 0; + newPage = makeBaseFrameFor(contextToReturnFrom); + assert(GIV(stackPage) == newPage); + + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (newPage->headSP); + GIV(framePointer) = (newPage->headFP); + assert((stackTop()) == (ceCannotResumePC())); + + /* overwrite the ceSendCannotResumePC on the stack. If ever re-executed + the returnValue will be taken from top-of-stack by ceCannotResume. */ + + /* stackTopPut: */ + longAtput(GIV(stackPointer),returnValue); + + /* Assign it to instructionPointer as externalCannotReturn:from: pushes it. */ + GIV(instructionPointer) = ceCannotResumePC(); +} + + +/* See StackInterpreter class>>initializeFrameIndices */ + + /* CoInterpreter>>#temporary:in: */ +static NoDbgRegParms sqInt +temporaryin(sqInt offset, char *theFP) +{ + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (offset < ((frameNumArgs = ((mframeCogMethod(theFP))->cmNumArgs))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgs - offset) * BytesPerWord)) + : longAt(((theFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs - offset) * BytesPerWord))) + : /* itemporary:in: */ + (offset < ((frameNumArgsUsqInt = byteAt((theFP + FoxIFrameFlags) + 1))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt - offset) * BytesPerWord)) + : longAt(((theFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt - offset) * BytesPerWord)))); +} + + /* CoInterpreter>>#temporary:in:put: */ +static NoDbgRegParms sqInt +temporaryinput(sqInt offset, char *theFP, sqInt valueOop) +{ + usqInt frameNumArgs; + unsigned char frameNumArgs1; + + return ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? longAtput((offset < ((frameNumArgs1 = ((mframeCogMethod(theFP))->cmNumArgs))) + ? (theFP + FoxCallerSavedIP) + ((frameNumArgs1 - offset) * BytesPerWord) + : ((theFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs1 - offset) * BytesPerWord)),valueOop) + : /* itemporary:in:put: */ + (offset < ((frameNumArgs = byteAt((theFP + FoxIFrameFlags) + 1))) + ? longAtput((theFP + FoxCallerSavedIP) + ((frameNumArgs - offset) * BytesPerWord),valueOop) + : longAtput(((theFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - offset) * BytesPerWord),valueOop))); +} + + +/* Go through all frames in the stack zone and mark their methods + so that compaction does not free any methods that are in use. */ +/* If instructionPointer is referring to machine code, as it will be if a + primitive is in progress + (see isCodeCompactingPrimitiveIndex:) it must be updated if it is + referring to a moved + method. */ + + /* CoInterpreter>>#updateStackZoneReferencesToCompiledCodePreCompaction */ +void +updateStackZoneReferencesToCompiledCodePreCompaction(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt i; + CogMethod *primCogMethod; + usqInt theFlags; + char *theFP; + usqInt theIP; + char *theIPPtr; + CogMethod *theMethod; + sqInt theMethodField; + StackPage *thePage; + + if (GIV(instructionPointer)) { + if ((primCogMethod = cogMethodContaining(GIV(instructionPointer)))) { + GIV(instructionPointer) += (primCogMethod->objectHeader); + } + } + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + /* begin updateStackZoneReferencesToCompiledCodePreCompactionOnPage: */ + callerFP = ((char *) 0); + theIPPtr = (thePage->headSP); + theFP = (thePage->headFP); + while (1) { + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + theMethodField = longAt(theFP + FoxMethod); + theFlags = theMethodField & MFMethodFlagsMask; + theMethod = ((CogMethod *) (theMethodField - theFlags)); + if (((theMethod->cmType)) == CMBlock) { + /* cmHomeMethod */ + if ((((((CogBlockMethod *) (theMethodField - theFlags)))->cmType)) >= CMMethod) { + theMethod = ((CogMethod *) (((CogBlockMethod *) (theMethodField - theFlags)))); + } + else { + assert(CMBlock == ((((CogBlockMethod *) (theMethodField - theFlags)))->cmType)); + theMethod = ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (theMethodField - theFlags))))) - (((((CogBlockMethod *) (theMethodField - theFlags)))->homeOffset)))); + } + } + theIP = ((usqInt)(longAt(theIPPtr))); + if ((theIP != (ceCannotResumePC())) + && (asserta((theIP >= (((usqInt)theMethod))) + && (theIP < ((((usqInt)theMethod)) + ((theMethod->blockSize))))))) { + longAtput(theIPPtr,theIP + ((theMethod->objectHeader))); + } + longAtput(theFP + FoxMethod,theMethodField + ((theMethod->objectHeader))); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theIPPtr = theFP + FoxCallerSavedIP; + theFP = callerFP; + } + } + } +} + + +/* Update the frame's spouse context with the frame's current state except + for the + sender and instruction pointer, which are used to mark the context as + married, and the receiver and method, which are already set. */ + + /* CoInterpreter>>#updateStateOfSpouseContextForFrame:WithSP: */ +static NoDbgRegParms void +updateStateOfSpouseContextForFrameWithSP(char *theFP, char *theSP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *argsPointer; + sqInt fieldIndex; + sqInt i; + char *pointer; + usqInt tempIndex; + sqInt theContext; + sqInt valuePointer; + + assert(frameHasContext(theFP)); + theContext = longAt(theFP + FoxThisContext); + assert(isContext(theContext)); + assert(frameReceiver(theFP) == noFixupFollowFieldofObject(ReceiverIndex, theContext)); + assert(frameMethodObject(theFP) == noFixupFollowFieldofObject(MethodIndex, theContext)); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + tempIndex = ((mframeCogMethod(theFP))->cmNumArgs); + pointer = (theFP + FoxMFReceiver) - BytesPerWord; + } + else { + tempIndex = byteAt((theFP + FoxIFrameFlags) + 1); + pointer = (theFP + FoxIFReceiver) - BytesPerWord; + } + + /* update the arguments. this would appear not to be strictly necessary, but is for two reasons. + First, the fact that arguments are read-only is only as convention in the Smalltalk compiler; + other languages may choose to modify arguments. + Second, the Squeak runUntilErrorOrReturnFrom: nightmare pops the stack top, which may, in + certain circumstances, be the last argument, and hence the last argument may not have been + stored into the context. */ + argsPointer = theFP + ((FoxCallerSavedIP + BytesPerWord) + ((tempIndex << (shiftForWord())))); + for (i = 1; i <= tempIndex; i += 1) { + argsPointer -= BytesPerWord; + assert(addressCouldBeOop(longAt(argsPointer))); + fieldIndex = ReceiverIndex + i; + valuePointer = longAt(argsPointer); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, theContext, valuePointer)); + assert(isNonImmediate(theContext)); + if (oopisGreaterThanOrEqualTo(theContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(theContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(theContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); + } + + /* now update the non-argument stack contents. */ + while (pointer >= theSP) { + assert(addressCouldBeOop(longAt(pointer))); + tempIndex += 1; + fieldIndex = ReceiverIndex + tempIndex; + valuePointer = longAt(pointer); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, theContext, valuePointer)); + assert(isNonImmediate(theContext)); + if (oopisGreaterThanOrEqualTo(theContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(theContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(theContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); + pointer -= BytesPerWord; + } + assert((ReceiverIndex + tempIndex) < (lengthOf(theContext))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(StackPointerIndex, theContext, ((tempIndex << 3) | 1))); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord()))))),((tempIndex << 3) | 1)); +} + + +/* Note that we accept anInstrPointer pointing to a callPrimitiveBytecode + at the start of a method that contains a primitive. This because methods + like Context(Part)>>reset have to be updated to skip the callPrimtiive + bytecode otherwise. */ +/* -1 for pre-increment in fetchNextBytecode */ + + /* CoInterpreter>>#validInstructionPointer:inMethod:framePointer: */ +sqInt +validInstructionPointerinMethodframePointer(usqInt instrPointer, usqInt aMethod, char *fp) +{ + CogMethod *cogMethod; + sqInt header; + usqInt theInstrPointer; + + if (instrPointer == (ceCannotResumePC())) { + return (((usqInt)(longAt(fp + FoxMethod)))) < (startOfMemory()); + } + if (instrPointer == (ceReturnToInterpreterPC())) { + if ((((usqInt)(longAt(fp + FoxMethod)))) < (startOfMemory())) { + return 0; + } + theInstrPointer = ((usqInt)(longAt(fp + FoxIFSavedIP))); + } + else { + theInstrPointer = instrPointer; + header = longAt((void *)((aMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + if (((/* begin isCogMethodReference: */ + assert(((((header) & 7) == 1)) + || (((((usqInt)header)) < (startOfMemory())) + && ((((usqInt)header)) >= (minCogMethodAddress())))), + /* isNonIntegerObject: */ + (!(header & (smallIntegerTag()))))) + && (theInstrPointer < (startOfMemory()))) { + cogMethod = ((CogMethod *) header); + return (theInstrPointer >= (header + (sizeof(CogMethod)))) + && (theInstrPointer < (header + ((cogMethod->blockSize)))); + } + } + return (theInstrPointer >= (((aMethod + (lastPointerOf(aMethod))) + BytesPerOop) - 1)) + && (theInstrPointer < (((aMethod + (numBytesOfBytes(aMethod))) + BaseHeaderSize) - 1)); +} + + +/* Check that the base frame in the stack page has a valid sender and saved + context. + */ + + /* CoInterpreter>>#validStackPageBaseFrame: */ +static NoDbgRegParms sqInt +validStackPageBaseFrame(StackPage *aPage) +{ + sqInt savedThisContext; + sqInt senderContextOrNil; + + senderContextOrNil = longAt((aPage->baseAddress)); + savedThisContext = longAt(((aPage->baseAddress)) - BytesPerWord); + if (!(asserta(((((aPage->baseFP)) + (frameStackedReceiverOffset((aPage->baseFP)))) + (2 * BytesPerWord)) == ((aPage->baseAddress))))) { + return 0; + } + if (!(asserta(addressCouldBeObj(senderContextOrNil)))) { + return 0; + } + if (!(asserta(addressCouldBeObj(savedThisContext)))) { + return 0; + } + if (!(asserta((senderContextOrNil == (nilObject())) + || (isContext(senderContextOrNil))))) { + return 0; + } + if (!(asserta(isContext(savedThisContext)))) { + return 0; + } + if (!(asserta((frameCallerContext((aPage->baseFP))) == senderContextOrNil))) { + return 0; + } + if (!(asserta((frameContext((aPage->baseFP))) == savedThisContext))) { + return 0; + } + return 1; +} + + +/* Make sure that all VM state that affects the heap contents is voided so + that the heap is + ready to be snapshotted. If flushExtPrims is true, flush references to + external primitives in methods. Answer the activeContext that should be + stored in the snapshot. */ +/* Make sure that all VM state that affects the heap contents is voided so + that the heap is + ready to be snapshotted. If flushExtPrims is true, flush references to + external primitives in methods. Answer the activeContext that should be + stored in the snapshot. */ + + /* CoInterpreter>>#voidVMStateForSnapshotFlushingExternalPrimitivesIf: */ +static NoDbgRegParms sqInt +voidVMStateForSnapshotFlushingExternalPrimitivesIf(sqInt flushExtPrims) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt address; + sqInt classIndex; + sqInt fmt; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* in case of code compactions. */ + GIV(instructionPointer) = 0; + activeContext = divorceAllFrames(); + + /* begin bereaveAllMarriedContextsForSnapshotFlushingExternalPrimitivesIf: */ + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + fmt = (byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask()); + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + makeContextSnapshotSafe(objSqInt); + } + + /* Clean out external functions from compiled methods */ + if (flushExtPrims + && (fmt >= (firstCompiledMethodFormat()))) { + flushExternalPrimitiveOf(objSqInt); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } + voidCogCompiledCode(); + return activeContext; +} + + +/* useful for VM debugging */ + + /* CoInterpreter>>#whereIs: */ +char * +whereIs(sqInt anOop) +{ + char *somewhere; + char *where; + + if ((somewhere = whereIsMaybeCodeThing(anOop))) { + return somewhere; + } + if ((where = whereIsMaybeHeapThing(anOop))) { + return where; + } + if ((where = whereIsMaybeStackThing(anOop))) { + return where; + } + return " is no where obvious"; +} + + +/* Either widow the context or map its pc to a bytecode one. + Used to implement primitiveVoidVMStateForMethod. */ +/* for debugging & saving space */ + + /* CoInterpreter>>#widowOrForceToBytecodePC: */ +static NoDbgRegParms NeverInline void +widowOrForceToBytecodePC(sqInt ctxt) +{ + if (((((longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (!(isWidowedContext(ctxt))) { + assert(!((isMachineCodeFrame(frameOfMarriedContext(ctxt))))); + } + } + else { + ensureContextHasBytecodePC(ctxt); + } +} + + /* CoInterpreterMT>>#ceCheckForInterrupt */ +void +ceCheckForInterrupt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt switched; + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + switched = checkForEventsMayContextSwitch(1); + returnToExecutivepostContextSwitch(0, switched); +} + + +/* Invoked from checkForEventsMayContextSwitch: to switch threads if a thread + wanting to acquire the VM has higher priority than the active process. */ + + /* CoInterpreterMT>>#cedeToHigherPriorityThreads */ +static void +cedeToHigherPriorityThreads(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt activePriority; + sqInt activeProc; + CogVMThread *activeThread; + sqInt aLinkedList; + int existing; + CogVMThread *highest; + sqInt i; + sqInt index; + sqInt link; + sqInt newMaxPriority; + CogVMThread *nextHighest; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt priority; + sqInt processAffinity; + sqInt processLists; + CogVMThread *vmThread; + CogVMThread *vmThread1; + sqInt waitingPriority; + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + activePriority = (oop >> 3); + processAffinity = threadAffinityOfProcess(activeProc); + + /* begin currentVMThread */ + index = atomic_load((&GIV(vmOwner))); + assert(((index >= 0) && (index <= GIV(numThreads)))); + activeThread = (index > 0 + ? GIV(threads)[index] + : ((CogVMThread *) null)); + assert(threadIndexisCompatibleWith((activeThread->index), processAffinity)); + waitingPriority = atomic_load((&GIV(maxWaitingPriority))); + (activeThread->priority = activePriority); + + /* begin highestPriorityThreadIfHigherThan:expectedMax: */ + highest = (nextHighest = null); + + /* To make this fair we could remember the last index at which we + found the highest and start the search at the following index. */ + for (i = 1; i <= GIV(numThreads); i += 1) { + vmThread1 = GIV(threads)[i]; + if ((atomic_load((&((vmThread1->state))))) == CTMWantingOwnership) { + assert(((vmThread1->priority)) <= waitingPriority); + if (!highest) { + highest = vmThread1; + } + else { + if (((vmThread1->priority)) > ((highest->priority))) { + nextHighest = highest; + highest = vmThread1; + } + else { + if (!nextHighest) { + nextHighest = vmThread1; + } + else { + if (((vmThread1->priority)) > ((nextHighest->priority))) { + nextHighest = vmThread1; + } + } + } + } + } + } + if (!highest) { + vmThread = null; + goto l1; + } + if (((highest->priority)) <= activePriority) { + vmThread = null; + goto l1; + } + waitingPriorityIsAtLeast((!nextHighest + ? 0 + : (nextHighest->priority))); + vmThread = highest; + /* end highestPriorityThreadIfHigherThan:expectedMax: */ +l1: + if ((!vmThread) + || (vmThread == activeThread)) { + if (waitingPriority > activePriority) { + /* We found no thread of sufficiently high priority, even though waitingPriority indicated there should be one. + So reduce the waiting priority back to the priority of the currently active process. */ + + /* begin reduceWaitingPriorityFrom:to: */ + existing = waitingPriority; + + /* This CPXCHG may fail, that's fine though, as there may have been + another thread that increased the priority in the meantime. + In that case that threads priority is the correct one to use. */ + atomic_compare_exchange_strong((&GIV(maxWaitingPriority)), (&existing), activePriority); + } + return; + } + + /* no waiting thread of sufficiently high priority. Do not switch. + The activeProcess needs to run on a different thread. Leave this to + threadSwitchIfNecessary:from: in checkForEventsMayContextSwitch: */ + assert(((vmThread->priority)) > activePriority); + assert(vmThread != (currentVMThread())); + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (waitingPriority > ((vmThread->priority))) { + newMaxPriority = (vmThread->priority); + + /* begin reduceWaitingPriorityFrom:to: */ + existing = waitingPriority; + + /* This CPXCHG may fail, that's fine though, as there may have been + another thread that increased the priority in the meantime. + In that case that threads priority is the correct one to use. */ + atomic_compare_exchange_strong((&GIV(maxWaitingPriority)), (&existing), newMaxPriority); + } + GIV(statProcessSwitch) += 1; + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l2; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l2: + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, activeProc, activeContext)); + assert(isNonImmediate(activeProc)); + if (oopisGreaterThanOrEqualTo(activeProc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeContext & (tagMask())))) + && (oopisLessThan(activeContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(activeProc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(activeProc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),activeContext); + + /* begin ensurePushedInstructionPointer */ + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* begin processListForProcess: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + priority = (oop >> 3); + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + processLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + aLinkedList = longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((priority - 1)) << (shiftForWord())))))); + + /* begin is:onList: */ + /* begin followField:ofObject: */ + link = longAt((void *)((aLinkedList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(link & (tagMask())))) + && ((!((longAt((void *)(link))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + link = fixFollowedFieldofObjectwithInitialValue(FirstLinkIndex, aLinkedList, link); + } + while (!(link == GIV(nilObj))) { + if (link == activeProc) { + goto l3; + } + + /* begin followField:ofObject: */ + objOop = longAt((void *)((link + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(NextLinkIndex, link, objOop); + } + link = objOop; + } + putToSleepyieldingIf(activeProc, (!(imageHeaderFlags & 16))); +l3: + + /* Transcript cr; print: #cedeToHighestPriorityThreadIfHigherThan:; cr. + self printExternalHeadFrame. + self print: 'ip: '; printHex: self instructionPointer. Transcript cr; flush. */ + returnToSchedulingLoopAndReleaseVMOrWakeThreadsource(vmThread, CSCheckEvents); +} + + +/* Check for possible interrupts and handle one if necessary. + Answer if a context switch has occurred. */ + + /* CoInterpreterMT>>#checkForEventsMayContextSwitch: */ +static NoDbgRegParms sqInt +checkForEventsMayContextSwitch(sqInt mayContextSwitch) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt heapSizePostGC; + usqLong now; + sqInt nowSqInt; + sqInt objOop; + sqInt sema; + sqInt sufficientSpaceAfterGCRV; + sqInt switched; + sqInt threadingThwartsIOProcessEventsInvocation; + + /* begin assertSaneThreadAndProcess */ + assert(vmIsOwned()); + assert((vmThreadState(currentVMThread())) == CTMAssignableOrInVM); + assert((fetchPointerofObject(MyListIndex, activeProcess())) == (nilObject())); + assertValidProcessorStackPointersForIndex(atomic_load((&GIV(vmOwner)))); + assertCStackWellAligned(); + GIV(statCheckForEvents) += 1; + + /* restore the stackLimit if it has been smashed. */ + + /* begin restoreStackLimit */ + (GIV(stackPage)->stackLimit = (GIV(stackPage)->realStackLimit)); + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + + /* Allow the platform to do anything it needs to do synchronously. */ + ioSynchronousCheckForEvents(); + + /* begin checkCogCompiledCodeCompactionCalledFor */ + if (GIV(cogCompiledCodeCompactionCalledFor)) { + commenceCogCompiledCodeCompaction(); + } + if (GIV(needGCFlag)) { + /* sufficientSpaceAfterGC: runs the incremental GC and + then, if not enough space is available, the fullGC. */ + + /* begin sufficientSpaceAfterGC: */ + scavengingGCTenuringIf(TenureByAge); + heapSizePostGC = ((/* begin totalOldSpaceCapacity */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + if (((((double) (heapSizePostGC - GIV(heapSizeAtPreviousGC)) )) / GIV(heapSizeAtPreviousGC)) >= GIV(heapGrowthToSizeGCRatio)) { + fullGC(); + } + else { + if (GIV(totalFreeOldSpace) > (GIV(shrinkThreshold) * 2)) { + /* begin attemptToShrink */ + if ((GIV(totalFreeOldSpace) > GIV(shrinkThreshold)) + && ((GIV(totalFreeOldSpace) > GIV(growHeadroom)) + && (shrinkObjectMemory((((GIV(totalFreeOldSpace) - GIV(growHeadroom)) < GIV(growHeadroom)) ? GIV(growHeadroom) : (GIV(totalFreeOldSpace) - GIV(growHeadroom))))))) { + GIV(statShrinkMemory) += 1; + } + sufficientSpaceAfterGCRV = 1; + goto l1; + } + } + + /* Also attempt to shrink if there is plenty of free space and no need to GC */ + while ((GIV(totalFreeOldSpace) < GIV(growHeadroom)) + && ((growOldSpaceByAtLeast(0)))) { + if (GIV(totalFreeOldSpace) >= GIV(growHeadroom)) { + sufficientSpaceAfterGCRV = 1; + goto l1; + } + } + + /* Answer false if space is low */ + sufficientSpaceAfterGCRV = GIV(lowSpaceThreshold) <= GIV(totalFreeOldSpace); + /* end sufficientSpaceAfterGC: */ +l1: + if (!sufficientSpaceAfterGCRV) { + setSignalLowSpaceFlagAndSaveProcess(); + } + } + if (!mayContextSwitch) { + return 0; + } + + /* do not switch process until the disowningThread (if any) has saved its state... */ + if (GIV(disowningThreadIndex)) { + if (GIV(disowningThreadIndex) != (atomic_load((&GIV(vmOwner))))) { + preemptDisowningThread(); + assert(GIV(disowningThreadIndex) == 0); + } + else { + assert(0); + } + } + + /* Do not switch threads while performing these checks. Instead defer the thread + switch until after all the necessary checks have been tested, in the right order. */ + assert(!GIV(deferThreadSwitch)); + GIV(deferThreadSwitch) = 1; + + /* begin checkProfileTickIfSwitched */ + if ((GIV(nextProfileTick) <= 0) + || ((ioHighResClock()) < GIV(nextProfileTick))) { + switched = 0; + goto l3; + } + + /* Take a sample (if not already done so) for the profiler. */ + if (!GIV(profileProcess)) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + GIV(profileProcess) = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + } + GIV(profileMethod) = null; + + /* Zero nextProfileTick because signalling the semaphore is when the VM effectively delivers the sample. */ + + /* begin zeroNextProfileTick */ + GIV(nextProfileTick) = 0; + switched = synchronousSignal(GIV(profileSemaphore)); + /* end checkProfileTickIfSwitched */ +l3: +# if LRPCheck + if (checkDeliveryOfLongRunningPrimitiveSignal()) { + switched = 1; + } +# endif + + if (GIV(signalLowSpace)) { + /* signalLowSpace: */ + GIV(signalLowSpace) = 0; + sema = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheLowSpaceSemaphore) << (shiftForWord())))))); + if ((sema != GIV(nilObj)) + && (synchronousSignal(sema))) { + switched = 1; + } + } + nowSqInt = (now = ioUTCMicroseconds()); + + /* begin checkInvokeIOProcessEvents: */ + if (((ioEventThreadAffinity()) > 0) + && ((ioEventThreadAffinity()) != (atomic_load((&GIV(vmOwner)))))) { + threadingThwartsIOProcessEventsInvocation = nowSqInt >= GIV(nextPollUsecs); + goto l2; + } + if (nowSqInt >= GIV(nextPollUsecs)) { + GIV(statIOProcessEvents) += 1; + ioProcessEvents(); + + /* msecs to wait before next call to ioProcessEvents. Note that strictly + speaking we might need to update 'now' at this point since + ioProcessEvents could take a very long time on some platforms */ + GIV(nextPollUsecs) = nowSqInt + 20000; + } + threadingThwartsIOProcessEventsInvocation = 0; + /* end checkInvokeIOProcessEvents: */ +l2: + if (GIV(interruptPending)) { + GIV(interruptPending) = 0; + + /* reset interrupt flag */ + sema = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheInterruptSemaphore) << (shiftForWord())))))); + if ((sema != GIV(nilObj)) + && (synchronousSignal(sema))) { + switched = 1; + } + } + if (GIV(nextWakeupUsecs)) { + if (now >= GIV(nextWakeupUsecs)) { + GIV(nextWakeupUsecs) = 0; + + /* set timer interrupt to 0 for 'no timer' */ + sema = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord())))))); + if ((sema != GIV(nilObj)) + && (synchronousSignal(sema))) { + switched = 1; + } + } + } + + /* signal any pending finalizations */ + if (GIV(pendingFinalizationSignals) > 0) { + GIV(pendingFinalizationSignals) = 0; + sema = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheFinalizationSemaphore) << (shiftForWord())))))); + if ((sema != GIV(nilObj)) + && (synchronousSignal(sema))) { + switched = 1; + } + } + + /* signal all semaphores in semaphoresToSignal */ + if (signalExternalSemaphores()) { + switched = 1; + } + GIV(deferThreadSwitch) = 0; + if (threadingThwartsIOProcessEventsInvocation + || (GIV(checkThreadActivation))) { + GIV(checkThreadActivation) = 0; + cedeToHigherPriorityThreads(); + } + threadSwitchIfNecessaryfrom(activeProcess(), CSCheckEvents); + return switched; +} + + +/* Check whether the VM is unowned and needs to set a thread running to try + and own it. + Do not attempt this if the image doesn't have a threadAffinity inst var in + Process; the VM + can't thread these images. */ +/* for Slang and the ifTrue: below... */ + + /* CoInterpreterMT>>#checkVMOwnershipFromHeartbeat */ +static void +checkVMOwnershipFromHeartbeat(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int expected; + sqInt ok; + sqInt threadState; + CogVMThread *vmThread; + + sqLowLevelMFence(); + + /* begin tryLockVMOwnerTo: */ + assert(!(0 /* (CTMUnknownOwner = 0) */)); + expected = 0; + ok = atomic_compare_exchange_strong((&GIV(vmOwner)), (&expected), CTMUnknownOwner); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(CTMUnknownOwner, ok); + } + if (ok) { + /* begin ensureRunningVMThread: */ + if ((vmThread = willingVMThread())) { + threadState = atomic_load((&((vmThread->state)))); + + /* If the VM is relinquishing the processor then only schedule a thread if it has work to do. */ + if (GIV(relinquishing) + && (threadState != CTMWantingOwnership)) { + /* begin releaseVM */ + /* begin setVMOwner: */ + assert(((getVMOwner()) == CTMUnknownOwner) + || ((getVMOwner()) == (ioThreadLocalGetThreadIndex()))); + assert((getVMOwner()) != 0); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(0, 1); + } + + /* TODO: We could make this a `release` ordering, which may perform better on ARM. */ + atomic_store((&GIV(vmOwner)), 0); + goto l1; + } + assert(((threadState == CTMAssignableOrInVM) + || (threadState == CTMInitializing)) + || (threadState == CTMWantingOwnership)); + + /* Ownership will be transferred to vmThread, no need to release the VM. */ + wakeVMThread(vmThread); + goto l1; + } + + /* If the VM is relinquishing the processor then only schedule a thread if it has work to do + (willingVMThread not nil above). + If we have failed to allocate thread storage before there is no point continuing to + try to do so. By this time we should have quite a few threads in the pool. */ + if (!(GIV(relinquishing) + || (GIV(memoryIsScarce)))) { + if ((vmThread = unusedThreadInfo())) { + wakeVMThread(vmThread); + goto l1; + } + } + + /* begin releaseVM */ + /* begin setVMOwner: */ + assert(((getVMOwner()) == CTMUnknownOwner) + || ((getVMOwner()) == (ioThreadLocalGetThreadIndex()))); + assert((getVMOwner()) != 0); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(0, 1); + } + + /* TODO: We could make this a `release` ordering, which may perform better on ARM. */ + atomic_store((&GIV(vmOwner)), 0); + goto l1; + /* end ensureRunningVMThread: */ +l1:; + } +} + + +/* Defer smashes of the stackLimit around the call of functionSymbol (for + assert checks). + + N.B. SYNCHRONIZE WITH forceInterruptCheckFromHeartbeat + */ + + /* CoInterpreterMT>>#deferStackLimitSmashAround:with: */ +static NoDbgRegParms sqInt +deferStackLimitSmashAroundwith(void (*functionSymbol)(sqInt), sqInt arg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(deferSmash) = 1; + sqLowLevelMFence(); + functionSymbol(arg); + GIV(deferSmash) = 0; + sqLowLevelMFence(); + if (GIV(deferredSmash)) { + GIV(deferredSmash) = 0; + sqLowLevelMFence(); + forceInterruptCheck(); + checkVMOwnershipFromHeartbeat(); + } + return 1; +} + + +/* Release the VM to other threads and answer the current thread's index plus + some status flags above it. + Currently valid flags: + DisownVMForFFICallFlag - informs the VM that it is entering an FFI + call DisownVMFromCallbackFlag - informs the VM that it is reentering an + FFI call + DisownVMForProcessorRelinquishFlag - informs the VM that the horrible + primitiveRelinquishProcessor is disowning the VM + OwnedVMFromForeignThreadFlag - indicates lowest-level entry from a + foreign thread + - not to be used explicitly by clients + - only set by ownVMFromUnidentifiedThread + VMAlreadyOwnedHenceDoNotDisown + - not to be used explicitly by clients + - only set by ownVMFromUnidentifiedThread + - indicates an ownVM from a callback was made when + the vm was still owned (e.g. from a plugin primitive). + + This is the entry-point for plugins and primitives that wish to release + the VM while + performing some operation that may potentially block, and for callbacks + returning back to some blocking operation. If this thread does not reclaim + the VM before- + hand then when the next heartbeat occurs the thread manager will schedule + a thread to acquire the VM which may start running the VM in place of this + thread. + N.B. Most of the state needed to resume after preemption is set in + preemptDisowningThread. */ + + /* CoInterpreterMT>>#disownVM: */ +sqInt +disownVM(sqInt flags) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + sqInt currentState; + sqInt objOop; + sqInt oop; + sqInt proc; + CogVMThread *selfOfPriority; + CogVMThread *selfOfStackPtrIndex; + CogVMThread *self_in_CogVMThread; + sqInt thingOne; + sqInt threadIndex; + CogVMThread *vmThread; + + assert(flags >= 0); + assert(((((flags & DisownVMForFFICallFlag) != 0)) != (((flags & DisownVMForProcessorRelinquishFlag) != 0))) + || ((((flags & DisownVMFromCallbackFlag) != 0)) + && ((!(flags & DisownVMForProcessorRelinquishFlag))))); + assert(successful()); + assert(vmOwnerIs(ioThreadLocalGetThreadIndex())); + if (recordFFIOrThreadTrace()) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + thingOne = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceDisownVM) << TraceTypeShift)))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = thingOne; + GIV(traceLog)[GIV(traceLogIndex) + 2] = flags; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + threadIndex = atomic_load((&GIV(vmOwner))); + if (((flags & VMAlreadyOwnedHenceDoNotDisownFlag) != 0)) { + /* begin vmThreadAt: */ + assert(((threadIndex >= 0) && (threadIndex <= GIV(numThreads)))); + self_in_CogVMThread = (threadIndex > 0 + ? GIV(threads)[threadIndex] + : ((CogVMThread *) null)); + + /* begin setVmThreadState: */ + currentState = atomic_load((&((self_in_CogVMThread->state)))); + if (currentState != CTMUnavailable) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMUnavailable = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + assert(0 /* (CTMUnavailable = CTMAssignableOrInVM) */); + break; + case CTMAssignableOrInVM: + break; + case CTMUnavailable: + assert(0 /* (CTMUnavailable = CTMWantingOwnership) */); + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((self_in_CogVMThread->state))), CTMUnavailable); + return threadIndex + (((((usqInt)(VMAlreadyOwnedHenceDoNotDisownFlag) << ThreadIdShift)))); + } + assertCStackPointersBelongToCurrentThread(); + assertValidNewMethodPropertyFlags(); +# if !NDEBUG + selfOfStackPtrIndex = vmThreadAt(threadIndex); + (selfOfStackPtrIndex->stackPtrIndex = /* stackPointerIndexForFrame:WithSP: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? (((usqInt)(((GIV(framePointer) + FoxMFReceiver) - GIV(stackPointer)))) >> (shiftForWord())) + (((mframeCogMethod(GIV(framePointer)))->cmNumArgs)) + : (((usqInt)(((GIV(framePointer) + FoxIFReceiver) - GIV(stackPointer)))) >> (shiftForWord())) + (byteAt((GIV(framePointer) + FoxIFrameFlags) + 1)))); +# endif // !NDEBUG + + if (((flags & DisownVMForProcessorRelinquishFlag) != 0)) { + if (((proc = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ForeignCallbackProcess) << (shiftForWord())))))))) != GIV(nilObj)) { + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((proc + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + GIV(foreignCallbackPriority) = (oop >> 3); + } + GIV(relinquishing) = 1; + sqLowLevelMFence(); + } + disownCount += 1; + + /* If we're disowning the VM because there's no active process to run, there's nothing to preempt later, + so don't indicate that there's a disowning thread that needs to be restored later. */ + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + if (activeProc != GIV(nilObj)) { + if (GIV(disowningThreadIndex) == (atomic_load((&GIV(vmOwner))))) { + assert(GIV(disowningCFramePointer) == GIV(CFramePointer)); + assert(GIV(disowningCStackPointer) == GIV(CStackPointer)); + } + else { + if (GIV(disowningThreadIndex)) { + preemptDisowningThread(); + assert(GIV(disowningThreadIndex) == 0); + } + GIV(disowningThreadIndex) = threadIndex; + GIV(disowningInstructionPointer) = GIV(instructionPointer); + GIV(disowningCFramePointer) = GIV(CFramePointer); + GIV(disowningCStackPointer) = GIV(CStackPointer); + sqLowLevelMFence(); + } + selfOfPriority = vmThreadAt(threadIndex); + (selfOfPriority->priority = quickFetchIntegerofObject(PriorityIndex, activeProc)); + } + + /* OwnedVMFromForeignThreadFlag indicates lowest-level of entry by a foreign + thread. If that's where we are then release the vmThread. Otherwise + indicate the vmThread is off doing something outside of the VM. */ + if (((flags & OwnedVMFromForeignThreadFlag) != 0)) { + /* I don't think this is quite right. Josh's use case is creating some foreign thread and then registering + it with the VM. That's not the same as binding a process to a foreign thread given that the foreign + callback process is about to terminate anyway (it is returning from a callback here). So do we need + an additional concept, that of a vmThread being either of the set known to the VM or floating? */ + if (!(isBoundProcess(activeProc))) { + /* begin vmThreadAt: */ + assert(((threadIndex >= 0) && (threadIndex <= GIV(numThreads)))); + vmThread = (threadIndex > 0 + ? GIV(threads)[threadIndex] + : ((CogVMThread *) null)); + + /* begin unregisterVMThread: */ + assert(((vmThreadState(vmThread)) != CTMUninitialized) + && ((vmThreadState(vmThread)) != CTMInitializing)); + initializeThreadState(vmThread); + (vmThread->osThread = null); + ioThreadLocalSetThreadIndex(0); + } + } + else { + /* begin vmThreadAt: */ + assert(((threadIndex >= 0) && (threadIndex <= GIV(numThreads)))); + self_in_CogVMThread = (threadIndex > 0 + ? GIV(threads)[threadIndex] + : ((CogVMThread *) null)); + + /* begin setVmThreadState: */ + currentState = atomic_load((&((self_in_CogVMThread->state)))); + if (currentState != CTMUnavailable) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMUnavailable = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + assert(0 /* (CTMUnavailable = CTMAssignableOrInVM) */); + break; + case CTMAssignableOrInVM: + break; + case CTMUnavailable: + assert(0 /* (CTMUnavailable = CTMWantingOwnership) */); + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((self_in_CogVMThread->state))), CTMUnavailable); + } + if (((flags & DisownVMForFFICallFlag) != 0)) { + ioThreadLocalSetInFFICall(threadIndex); + assert(ioThreadLocalGetInFFICall() == threadIndex); + } + assert(((((vmThreadAt(threadIndex))->boundProcess))) != (GIV(disowningThreadIndex) != 0)); + + /* begin releaseVM */ + /* begin setVMOwner: */ + assert(((getVMOwner()) == CTMUnknownOwner) + || ((getVMOwner()) == (ioThreadLocalGetThreadIndex()))); + assert((getVMOwner()) != 0); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(0, 1); + } + + /* TODO: We could make this a `release` ordering, which may perform better on ARM. */ + atomic_store((&GIV(vmOwner)), 0); + return threadIndex + (((((usqInt)(flags) << ThreadIdShift)))); +} + + +/* The trace log is a circular buffer of pairs of entries. If there is + an entry at traceLogIndex - 3 \\ TraceBufferSize it has entries. + If there is something at traceLogIndex it has wrapped. */ +/* Override to print legend of known flags */ + + /* CoInterpreterMT>>#dumpTraceLog */ +void +dumpTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt flags; + sqInt i; + sqInt j; + sqInt remainder; + sqInt thing; + + if (!(GIV(traceLog)[/* safe:mod: */ + (((remainder = (GIV(traceLogIndex) - 3) % TraceBufferSize)) < 0 + ? remainder + TraceBufferSize + : remainder)])) { + return; + } + if (GIV(traceLog)[GIV(traceLogIndex)]) { + for (j = GIV(traceLogIndex); j <= (TraceBufferSize - 3); j += 3) { + printLogEntryAt(j); + } + } + for (j = 0; j <= (GIV(traceLogIndex) - 3); j += 3) { + printLogEntryAt(j); + } + if (GIV(traceLog)[/* safe:mod: */ + (((remainder = (GIV(traceLogIndex) - 3) % TraceBufferSize)) < 0 + ? remainder + TraceBufferSize + : remainder)]) { + flags = 0; + if (GIV(traceLog)[GIV(traceLogIndex)]) { + for (i = GIV(traceLogIndex); i <= (TraceBufferSize - 3); i += 3) { + thing = GIV(traceLog)[i]; + if (thing == TraceOwnVM) { + flags = flags | (((((usqInt)((GIV(traceLog)[i + 1]))) >> ThreadIdShift) >> 3)); + } + if (thing == TraceDisownVM) { + flags = flags | (((GIV(traceLog)[i + 1]) >> 3)); + } + } + } + for (i = 0; i <= (GIV(traceLogIndex) - 3); i += 3) { + thing = GIV(traceLog)[i]; + if (thing == TraceOwnVM) { + flags = flags | (((((usqInt)((GIV(traceLog)[i + 1]))) >> ThreadIdShift) >> 3)); + } + if (thing == TraceDisownVM) { + flags = flags | (((GIV(traceLog)[i + 1]) >> 3)); + } + } + if (((flags & (((OwnedVMFromForeignThreadFlag + VMAlreadyOwnedHenceDoNotDisownFlag) + DisownVMForFFICallFlag) + DisownVMForProcessorRelinquishFlag)) != 0)) { + if (((flags & VMAlreadyOwnedHenceDoNotDisownFlag) != 0)) { + printHex(VMAlreadyOwnedHenceDoNotDisownFlag); + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " AlreadyOwned "); + } + if (((flags & OwnedVMFromForeignThreadFlag) != 0)) { + printHex(OwnedVMFromForeignThreadFlag); + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " OwnedVMFromForeignThread "); + } + if (((flags & DisownVMForFFICallFlag) != 0)) { + printHex(DisownVMForFFICallFlag); + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " DisownVMForFFI "); + } + if (((flags & DisownVMForProcessorRelinquishFlag) != 0)) { + printHex(DisownVMForProcessorRelinquishFlag); + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " DisownVMForProcessorRelinquish "); + } + cr(); + } + } +} + + +/* Main entry-point into the interpreter at each execution level, where an + execution level is either the start of execution or reentry for a + callback. Capture the C stack + pointers so that calls from machine-code into the C run-time occur at this + level. This is the actual implementation, separated from + enterSmalltalkExecutive so the + simulator can wrap it in an exception handler and hence simulate the + Cogit's jump + back into C code on interpreting; see ceInvokeInterpret. + + Conceptually, an invocation of interpret exists at each level of execution + from the + initial invocation through each callback. Entry to each execution level is + through this function. It captures the C stack & frame pointers for this + level of execution + and then either invokes machine code or interpret, depending on whether + the current frame (the effective entry-point into Smalltalk execution) is + a machine code + or interpreted frame. In addition, interpret captures the return address + of its caller + (this function). The Cogit then uses the captured C stack pointers and + return address to invoke interpret as if it had been called from this + function. */ +/* Main entry-point into the interpreter at each execution level, where an + execution level is either the start of execution or reentry for a + callback. Capture the C stack + pointers so that calls from machine-code into the C run-time occur at this + level. This is the actual implementation, separated from + enterSmalltalkExecutive so the + simulator can wrap it in an exception handler and hence simulate the + setjmp/longjmp. + Override to return if a longjmp to reenterInterpreter passes a parameter + greater than 1. + This causes a return to threadSchedulingLoop:startingVM: and is used to + surrender control to another thread. */ + + /* CoInterpreterMT>>#enterSmalltalkExecutiveImplementation */ +static sqInt +enterSmalltalkExecutiveImplementation(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin assertSaneThreadAndProcess */ + assert(vmIsOwned()); + assert((vmThreadState(currentVMThread())) == CTMAssignableOrInVM); + assert((fetchPointerofObject(MyListIndex, activeProcess())) == (nilObject())); + assertValidProcessorStackPointersForIndex(atomic_load((&GIV(vmOwner)))); + assertProcessorStackPointersBelongToCurrentThread(); + assertCStackWellAligned(); + ceCaptureCStackPointers(); + assertSavedCStackPointersWellAligned(); + if ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())) { + returnToExecutivepostContextSwitch(0, 1); + } + interpret(); + return 0; +} + + +/* Force an interrupt check ASAP. This version is the + entry-point to forceInterruptCheck for the heartbeat + timer to allow for repeatable debugging. + + N.B. SYNCHRONIZE WITH deferStackLimitSmashAround: */ + + /* CoInterpreterMT>>#forceInterruptCheckFromHeartbeat */ +void +forceInterruptCheckFromHeartbeat(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!suppressHeartbeatFlag) { + /* begin checkForLongRunningPrimitive */ +# if LRPCheck + if (!GIV(longRunningPrimitiveCheckSemaphore)) { + goto l1; + } + if ((GIV(longRunningPrimitiveStartUsecs) > 0) + && ((GIV(longRunningPrimitiveCheckMethod) == GIV(newMethod)) + && (GIV(longRunningPrimitiveCheckSequenceNumber) == GIV(statCheckForEvents)))) { + GIV(longRunningPrimitiveStopUsecs) = ioUTCMicroseconds(); + assert(GIV(longRunningPrimitiveStopUsecs) > GIV(longRunningPrimitiveStartUsecs)); + goto l1; + } + + /* See traceProfileState & mapProfileState. */ + if (!GIV(longRunningPrimitiveStopUsecs)) { + GIV(longRunningPrimitiveCheckSequenceNumber) = GIV(statCheckForEvents); + GIV(longRunningPrimitiveCheckMethod) = GIV(newMethod); + GIV(longRunningPrimitiveStartUsecs) = ioUTCMicroseconds(); + sqLowLevelMFence(); + } +# endif // LRPCheck + + /* end checkForLongRunningPrimitive */ +l1: + sqLowLevelMFence(); + if (GIV(deferSmash)) { + GIV(deferredSmash) = 1; + sqLowLevelMFence(); + } + else { + forceInterruptCheck(); + checkVMOwnershipFromHeartbeat(); + } + } +} + + +/* Main entry-point into the interpreter at system start-up. */ +/* Ensure that the myList of the activeProcess is nil. Needed to load + old images which don't nil myList in transferTo:{from:} */ + + /* CoInterpreterMT>>#initialEnterSmalltalkExecutive */ +static void +initialEnterSmalltalkExecutive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + objOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(MyListIndex, objOop, GIV(nilObj))); + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),GIV(nilObj)); + threadSchedulingLoop(vmThreadAt(1)); + + /* NOTREACHED. The following is to fool Slang */ + threadSchedulingLoopImplementation(currentVMThread()); +} + + /* CoInterpreterMT>>#isBoundProcess: */ +static NoDbgRegParms sqInt +isBoundProcess(sqInt aProcess) +{ + sqInt threadId; + + threadId = threadAffinityOfProcess(aProcess); + + /* begin isBoundThreadId: */ + return ((((threadId) & 7) == 1)) + && ((((threadId >> 3)) & 1) == 1); +} + + /* CoInterpreterMT>>#loadInitialContext */ +void +loadInitialContext(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt activeProc; + sqInt activeProc1; + sqInt obj; + sqInt objOop; + sqInt threadAffinity; + + runLeakCheckerFor(GCModeFull); + + /* primitiveSuspend needs to know the class of LinkedList */ + + /* begin getClassTagOfLinkedList */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + objOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + obj = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + GIV(classLinkedListClassTag) = (longAt((void *)(obj))) & (classIndexMask()); + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc1 = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + activeContext = longAt((void *)((activeProc1 + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + threadAffinity = threadAffinityOfProcess(activeProc); + assert((threadAffinity == 0) + || (threadAffinity == 1)); + startThreadSubsystem(); +} + + +/* Map all oops in the interpreter's state to their new values + during garbage collection or a become: operation. */ +/* Assume: All traced variables contain valid oops. */ +/* Map all oops in the interpreter's state to their new values + during garbage collection or a become: operation. */ +/* Assume: All traced variables contain valid oops. */ + + /* CoInterpreterMT>>#mapInterpreterOops */ +static void +mapInterpreterOops(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + DisownState *disownState; + sqInt i; + sqInt ipdelta; + sqInt result; + sqInt toDoLimit; + CogVMThread *vmThread; + + mapStackPages(); + + /* begin mapMachineCode: */ + mapObjectReferencesInMachineCode(GIV(gcMode)); + mapPrimTraceLog(); + mapTraceLog(); + + /* begin mapVMRegisters */ + ipdelta = 0; + if (shouldRemapObj(GIV(method))) { + ipdelta = (/* method:includesAddress: */ + (GIV(instructionPointer) > GIV(method)) + && (GIV(instructionPointer) < (GIV(method) + ((((numSlotsOfAny(GIV(method))) << (shiftForWord()))) + BaseHeaderSize))) + ? GIV(instructionPointer) - GIV(method) + : 0); + GIV(method) = remapObj(GIV(method)); + if (ipdelta) { + GIV(instructionPointer) = GIV(method) + ipdelta; + } + } + if (/* shouldRemapOop: */ + ((!(GIV(newMethod) & (tagMask())))) + && (shouldRemapObj(GIV(newMethod)))) { + ipdelta = ((!ipdelta) + && (/* method:includesAddress: */ + (GIV(instructionPointer) > GIV(newMethod)) + && (GIV(instructionPointer) < (GIV(newMethod) + ((((numSlotsOfAny(GIV(newMethod))) << (shiftForWord()))) + BaseHeaderSize)))) + ? GIV(instructionPointer) - GIV(newMethod) + : 0); + + /* don't relocate twice!! */ + GIV(newMethod) = remapObj(GIV(newMethod)); + if (ipdelta) { + GIV(instructionPointer) = GIV(newMethod) + ipdelta; + } + } + + /* begin mapProfileState */ + /* begin profileStateDoUpdating: */ + if (GIV(profileProcess)) { + if ((result = (shouldRemapObj(GIV(profileProcess)) + ? remapObj(GIV(profileProcess)) + : 0))) { + GIV(profileProcess) = result; + } + } + if (GIV(profileMethod)) { + if ((result = (shouldRemapObj(GIV(profileMethod)) + ? remapObj(GIV(profileMethod)) + : 0))) { + GIV(profileMethod) = result; + } + } + if (GIV(profileSemaphore)) { + if ((result = (shouldRemapObj(GIV(profileSemaphore)) + ? remapObj(GIV(profileSemaphore)) + : 0))) { + GIV(profileSemaphore) = result; + } + } +# if LRPCheck + /* The longRunningPrimitiveCheckMethod (LRPCM) is sampled in an interrupt. Be very careful with it. + If longRunningPrimitiveCheckSequenceNumber (LRPCSN) = statCheckForEvents then LRPCM has + been recenty sampled and could be mapped or not, but it must be newMethod and we can simply + copy newMethod. If LRPCSN ~= statCheckForEvents then LRPCM must be some extant object and + needs to be remapped. */ + sqLowLevelMFence(); + if (GIV(longRunningPrimitiveCheckMethod)) { + if (GIV(longRunningPrimitiveCheckSequenceNumber) == GIV(statCheckForEvents)) { + GIV(longRunningPrimitiveCheckMethod) = GIV(newMethod); + } + else { + if (shouldRemapObj(GIV(longRunningPrimitiveCheckMethod))) { + GIV(longRunningPrimitiveCheckMethod) = remapObj(GIV(longRunningPrimitiveCheckMethod)); + } + } + sqLowLevelMFence(); + } + if (GIV(longRunningPrimitiveCheckSemaphore)) { + if (shouldRemapObj(GIV(longRunningPrimitiveCheckSemaphore))) { + GIV(longRunningPrimitiveCheckSemaphore) = remapObj(GIV(longRunningPrimitiveCheckSemaphore)); + } + } +# endif // LRPCheck + + if ((GIV(tempOop) != 0) + && (/* shouldRemapOop: */ + ((!(GIV(tempOop) & (tagMask())))) + && (shouldRemapObj(GIV(tempOop))))) { + GIV(tempOop) = remapObj(GIV(tempOop)); + } + if ((GIV(tempOop2) != 0) + && (/* shouldRemapOop: */ + ((!(GIV(tempOop2) & (tagMask())))) + && (shouldRemapObj(GIV(tempOop2))))) { + GIV(tempOop2) = remapObj(GIV(tempOop2)); + } + + /* Per-thread state; trace each thread's own newMethods, including those in outer calls if a thread is nested in an outer disownVM:/ownVM:. */ + toDoLimit = getNumThreads(); + for (i = 1; i <= toDoLimit; i += 1) { + /* begin vmThreadAt: */ + assert(((i >= 0) && (i <= GIV(numThreads)))); + vmThread = (i > 0 + ? GIV(threads)[i] + : ((CogVMThread *) null)); + if ((atomic_load((&((vmThread->state))))) > CTMInitializing) { + if ((vmThread->boundProcess)) { + if (shouldRemapObj((vmThread->boundProcess))) { + (vmThread->boundProcess = remapObj((vmThread->boundProcess))); + } + } + disownState = ((DisownState *) vmThread); + do { + if ((((disownState->newMethodOrNull))) + && (shouldRemapObj((vmThread->newMethodOrNull)))) { + (vmThread->newMethodOrNull = remapObj((vmThread->newMethodOrNull))); + } + } while(((disownState = (disownState->nestedDisownStates)))); + } + } +} + + +/* Attempt to take ownership from a thread that as yet doesn't know its + index. This supports callbacks where the callback could originate from any + thread, and from called FFI code or from plugin primitives. If from the + latter there is + no requirement that the VM has been disowned by the calling-back thread. + In this case where the calling-back thread still own the VM, thunkEntry + should *not* disown the VM on return. To effect this + ownVMFromUnidentifiedThread answers the + VMAlreadyOwnedHenceDoNotDisownFlag. + From ownVM: + Answer 0 if the owning thread is known to the VM. + And from here + Answer OwnedVMFromForeignThreadFlag if the owning thread is unknown to the + VM and now owns the VM. + Answer VMAlreadyOwnedHenceDoNotDisownFlag if the thread already owns the + VM. Answer -1 if the owning thread is unknown to the VM and fails to own + the VM. + Answer -2 if the owning thread is unknown to the VM and there is no + foreign callback process installed. */ + + /* CoInterpreterMT>>#ownVMFromUnidentifiedThread */ +static sqInt +ownVMFromUnidentifiedThread(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt count; + sqInt currentState; + int expected; + sqInt indexOrZero; + CogVMThread *self_in_CogVMThread; + sqInt success; + sqInt threadIndex; + CogVMThread *vmThread; + + if ((threadIndex = ioThreadLocalGetThreadIndex())) { + /* this is a callback from a known thread */ + if ((atomic_load((&GIV(vmOwner)))) == threadIndex) { + assert((GIV(disowningThreadIndex) == 0) + || (GIV(disowningThreadIndex) == threadIndex)); + + /* begin vmThreadAt: */ + assert(((threadIndex >= 0) && (threadIndex <= GIV(numThreads)))); + self_in_CogVMThread = (threadIndex > 0 + ? GIV(threads)[threadIndex] + : ((CogVMThread *) null)); + + /* begin setVmThreadState: */ + currentState = atomic_load((&((self_in_CogVMThread->state)))); + if (currentState != CTMAssignableOrInVM) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMAssignableOrInVM = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMAssignableOrInVM = CTMUnavailable) */); + break; + case CTMUnavailable: + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((self_in_CogVMThread->state))), CTMAssignableOrInVM); + return VMAlreadyOwnedHenceDoNotDisownFlag; + } + } + if ((threadIndex == 0) + && (GIV(foreignCallbackPriority) == 0)) { + return -2; + } + count = 0; + + /* Before we can proceed, we need to temporarily lock the vm, so we can either find our CogVMThread struct or allocate a new one. */ + + /* begin acquireVMForIndex:withPriority: */ + while (1) { + /* begin tryLockVMOwnerTo: */ + assert(!((((threadIndex > 0 + ? threadIndex + : CTMUnknownOwner)) == 0))); + + /* begin doTryLockVMOwnerTo: */ + expected = 0; + success = (atomic_compare_exchange_strong((&GIV(vmOwner)), (&expected), (threadIndex > 0 + ? threadIndex + : CTMUnknownOwner))) + || ((/* We may already be vmOwner. The current vmOwner will be stored in expected. + However, if an unknown owner is present, we cannot assume that's us! */ + + (expected == ((threadIndex > 0 + ? threadIndex + : CTMUnknownOwner))) + && (((threadIndex > 0 + ? threadIndex + : CTMUnknownOwner)) != CTMUnknownOwner))); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful((threadIndex > 0 + ? threadIndex + : CTMUnknownOwner), success); + } + if (success) break; + waitingPriorityIsAtLeast(GIV(foreignCallbackPriority)); + ioTransferTimeslice(); + } + if (threadIndex) { + /* this is a callback from a known thread. Simply own the VM for that thread. */ + return ownVM(threadIndex); + } + + /* If the current thread doesn't have an index it's new to the vm + and we need to allocate a new threadInfo, failing if we can't. + We also need a process in the ForeignCallbackProcess slot upon + which to run the thread's eventual callback. */ + while ((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ForeignCallbackProcess) << (shiftForWord()))))))) == GIV(nilObj)) { + /* begin releaseVM */ + /* begin setVMOwner: */ + assert(((getVMOwner()) == CTMUnknownOwner) + || ((getVMOwner()) == (ioThreadLocalGetThreadIndex()))); + assert((getVMOwner()) != 0); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(0, 1); + } + + /* TODO: We could make this a `release` ordering, which may perform better on ARM. */ + atomic_store((&GIV(vmOwner)), 0); + if (((count += 1)) > 1000) { + return -2; + } + ioMilliSleep(1); + + /* begin acquireVMForIndex:withPriority: */ + while (1) { + /* begin tryLockVMOwnerTo: */ + assert(!((((threadIndex > 0 + ? threadIndex + : CTMUnknownOwner)) == 0))); + + /* begin doTryLockVMOwnerTo: */ + expected = 0; + success = (atomic_compare_exchange_strong((&GIV(vmOwner)), (&expected), (threadIndex > 0 + ? threadIndex + : CTMUnknownOwner))) + || ((/* We may already be vmOwner. The current vmOwner will be stored in expected. + However, if an unknown owner is present, we cannot assume that's us! */ + + (expected == ((threadIndex > 0 + ? threadIndex + : CTMUnknownOwner))) + && (((threadIndex > 0 + ? threadIndex + : CTMUnknownOwner)) != CTMUnknownOwner))); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful((threadIndex > 0 + ? threadIndex + : CTMUnknownOwner), success); + } + if (success) break; + waitingPriorityIsAtLeast(GIV(foreignCallbackPriority)); + ioTransferTimeslice(); + } + } + vmThread = unusedThreadInfo(); + + /* N.B. Keep the VM locked anonymously so that we reserve the non-nil ForeignCallbackProcess + for this thread, avoiding the race between competing foreign callbacks. The acquireVMFor: in + ownVM: will set the vmOwner to the actual index. So only unlock on failure. */ + if (!vmThread) { + /* begin releaseVM */ + /* begin setVMOwner: */ + assert(((getVMOwner()) == CTMUnknownOwner) + || ((getVMOwner()) == (ioThreadLocalGetThreadIndex()))); + assert((getVMOwner()) != 0); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(0, 1); + } + + /* TODO: We could make this a `release` ordering, which may perform better on ARM. */ + atomic_store((&GIV(vmOwner)), 0); + return -1; + } + indexOrZero = (vmThread->index); + + /* begin setVMOwner: */ + assert(((getVMOwner()) == CTMUnknownOwner) + || ((getVMOwner()) == (ioThreadLocalGetThreadIndex()))); + assert((getVMOwner()) != indexOrZero); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(indexOrZero, 1); + } + + /* TODO: We could make this a `release` ordering, which may perform better on ARM. */ + atomic_store((&GIV(vmOwner)), indexOrZero); + (vmThread->priority = GIV(foreignCallbackPriority)); + + /* begin setVmThreadState: */ + currentState = atomic_load((&((vmThread->state)))); + if (currentState != CTMWantingOwnership) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMWantingOwnership = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + assert(0 /* (CTMWantingOwnership = CTMAssignableOrInVM) */); + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMWantingOwnership = CTMUnavailable) */); + break; + case CTMUnavailable: + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((vmThread->state))), CTMWantingOwnership); + + /* begin registerVMThread: */ + assert((((vmThreadState(vmThread)) == CTMInitializing) + || ((vmThreadState(vmThread)) == CTMWantingOwnership)) + && (!((vmThread->osThread)))); + (vmThread->osThread = ioCurrentOSThread()); + ioThreadLocalSetThreadIndex((vmThread->index)); + assert((ioThreadLocalGetThreadIndex()) == ((vmThread->index))); + ownVM((vmThread->index)); + return OwnedVMFromForeignThreadFlag; +} + + +/* This is the entry-point for plugins and primitives that wish to reacquire + the VM after having + released it via disownVM: or callbacks that want to acquire it without + knowing their ownership + status. This call will block until the VM is owned by the current thread + or an error occurs. + The argument should be the value answered by disownVM:, or 0 for callbacks + that don't know + if they have disowned or not. This is both an optimization to avoid having + to query thread- + local storage for the current thread's index (since it can easily keep it + in some local variable), + and a record of when an unbound process becomes affined to a thread for + the dynamic + extent of some operation. + + Answer 0 if the current thread is known to the VM (and on return owns the + VM). and from ownVMFromUnidentifiedThread: + Answer 1 if the current thread is unknown to the VM and takes ownership. + Answer -1 if the current thread is unknown to the VM and fails to take + ownership. Answer -2 if the owning thread is unknown to the VM and there + is no foreign callback process installed. */ +/* for Slang and the ifTrue: below... */ + + /* CoInterpreterMT>>#ownVM: */ +sqInt +ownVM(sqInt disownResult) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt activeProc; + sqInt aMethodObj; + sqInt aob; + sqInt aProcess; + sqInt cFramePointer; + sqInt context; + sqInt cStackPointer; + sqInt currentState; + DisownState *disownState; + int expected; + sqInt flags; + sqInt header; + StackPage *lastUsedPage; + sqInt methodHeader; + sqInt myProc; + sqInt newContext; + StackPage *newPage; + sqInt objOop; + sqInt ok; + sqInt okSqInt; + sqInt sched; + CogVMThread *self_in_CogVMThread; + sqInt senderOop; + char *theFrame; + StackPage *thePage; + sqInt thingOne; + sqInt threadIndex; + sqInt threadIndexSqInt; + sqInt threadState; + sqInt top; + sqInt traced; + CogVMThread *vmThread; + + if (recordFFIOrThreadTrace()) { + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceAttemptOwnVM) << TraceTypeShift)))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = 0; + GIV(traceLog)[GIV(traceLogIndex) + 2] = disownResult; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + traced = 0; + threadIndex = disownResult & ((1U << ThreadIdShift) - 1); + if (!threadIndex) { + return ownVMFromUnidentifiedThread(); + } + flags = ((usqInt)(disownResult)) >> ThreadIdShift; + assert(((!(flags & DisownVMFromCallbackFlag))) + || ((((vmThreadAt(threadIndex))->boundProcess)))); + assert(threadIndex == ioThreadLocalGetThreadIndex()); + + /* begin tryLockVMOwnerTo: */ + assert(!((threadIndex == 0))); + expected = 0; + ok = (atomic_compare_exchange_strong((&GIV(vmOwner)), (&expected), threadIndex)) + || ((/* We may already be vmOwner. The current vmOwner will be stored in expected. + However, if an unknown owner is present, we cannot assume that's us! */ + + (expected == threadIndex) + && (threadIndex != CTMUnknownOwner))); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(threadIndex, ok); + } + if (ok) { + assert(getVMOwner() == threadIndex); + if (recordFFIOrThreadTrace()) { + traced = 1; + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + thingOne = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceOwnVM) << TraceTypeShift)))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = thingOne; + GIV(traceLog)[GIV(traceLogIndex) + 2] = disownResult; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + + /* disowningThreadIndex is an optimization that allows disownVM:/ownVM: to avoid recording + the VM state, including marrying the current frame, if the same thread owns the VM after + disowning, and no other thread manages to own the VM in between. */ + if (threadIndex == GIV(disowningThreadIndex)) { + if (((flags & DisownVMForProcessorRelinquishFlag) != 0)) { + /* Presumably we have nothing to do; this primitive is typically called from the + background process. So we should /not/ try and activate any threads in the + pool; they will waste cycles finding there is no runnable process, and will + cause a VM abort if no runnable process is found. But we /do/ want to allow + FFI calls that have completed, or callbacks a chance to get into the VM; they + do have something to do. DisownVMForProcessorRelinquish indicates this. */ + GIV(relinquishing) = 0; + sqLowLevelMFence(); + } + if (((flags & DisownVMForFFICallFlag) != 0)) { + ioThreadLocalSetInFFICall(0); + } + + /* begin vmThreadAt: */ + assert(((threadIndex >= 0) && (threadIndex <= GIV(numThreads)))); + self_in_CogVMThread = (threadIndex > 0 + ? GIV(threads)[threadIndex] + : ((CogVMThread *) null)); + + /* begin setVmThreadState: */ + currentState = atomic_load((&((self_in_CogVMThread->state)))); + if (currentState != CTMAssignableOrInVM) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMAssignableOrInVM = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMAssignableOrInVM = CTMUnavailable) */); + break; + case CTMUnavailable: + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((self_in_CogVMThread->state))), CTMAssignableOrInVM); + GIV(disowningThreadIndex) = 0; + GIV(disowningCStackPointer) = null; +# if !NDEBUG + assert(((vmThreadAt(threadIndex))->stackPtrIndex) == stackPointerIndexForFrameWithSP(GIV(framePointer), GIV(stackPointer))); +# endif // !NDEBUG + + return 0; + } + if (GIV(disowningThreadIndex)) { + assert(!((GIV(disowningThreadIndex) == threadIndex))); + preemptDisowningThread(); + assert(GIV(disowningThreadIndex) == 0); + } + } + + /* begin vmThreadAt: */ + assert(((threadIndex >= 0) && (threadIndex <= GIV(numThreads)))); + vmThread = (threadIndex > 0 + ? GIV(threads)[threadIndex] + : ((CogVMThread *) null)); + assert((vmThread) + && (((vmThread->index)) == threadIndex)); + + /* begin acquireVMFor: */ + assert((vmThread->index) == ioThreadLocalGetThreadIndex()); + + /* Check if vmThreadState is any of CTMUnavailable (2), CTMAssignableOrInVM (3), or CTMWantingOwnership (4) */ +# if NDEBUG + assert((((vmThreadState(vmThread)) >= CTMUnavailable) && ((vmThreadState(vmThread)) <= CTMWantingOwnership))); +# else + threadState = atomic_load((&((vmThread->state)))); + assert(((threadState >= CTMUnavailable) && (threadState <= CTMWantingOwnership))); +# endif // NDEBUG + + + /* do we really need CTMUnavailable?? eem 4/3/2026 */ + threadIndexSqInt = (vmThread->index); + + /* begin tryLockVMOwnerTo: */ + assert(!((threadIndexSqInt == 0))); + expected = 0; + okSqInt = (atomic_compare_exchange_strong((&GIV(vmOwner)), (&expected), threadIndexSqInt)) + || ((/* We may already be vmOwner. The current vmOwner will be stored in expected. + However, if an unknown owner is present, we cannot assume that's us! */ + + (expected == threadIndexSqInt) + && (threadIndexSqInt != CTMUnknownOwner))); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(threadIndexSqInt, okSqInt); + } + if (okSqInt) { + /* begin setVmThreadState: */ + currentState = atomic_load((&((vmThread->state)))); + if (currentState != CTMAssignableOrInVM) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMAssignableOrInVM = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMAssignableOrInVM = CTMUnavailable) */); + break; + case CTMUnavailable: + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((vmThread->state))), CTMAssignableOrInVM); + } + else { + /* begin setVmThreadState: */ + currentState = atomic_load((&((vmThread->state)))); + if (currentState != CTMWantingOwnership) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMWantingOwnership = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + assert(0 /* (CTMWantingOwnership = CTMAssignableOrInVM) */); + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMWantingOwnership = CTMUnavailable) */); + break; + case CTMUnavailable: + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((vmThread->state))), CTMWantingOwnership); + while (1) { + threadIndexSqInt = (vmThread->index); + + /* begin tryLockVMOwnerTo: */ + assert(!((threadIndexSqInt == 0))); + expected = 0; + okSqInt = (atomic_compare_exchange_strong((&GIV(vmOwner)), (&expected), threadIndexSqInt)) + || ((/* We may already be vmOwner. The current vmOwner will be stored in expected. + However, if an unknown owner is present, we cannot assume that's us! */ + + (expected == threadIndexSqInt) + && (threadIndexSqInt != CTMUnknownOwner))); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(threadIndexSqInt, okSqInt); + } + if (okSqInt) break; + if ((vmThread->priority)) { + waitingPriorityIsAtLeast((vmThread->priority)); + } + if (!((atomic_load((&GIV(vmOwner)))) == ((vmThread->index)))) { + ioWaitOnOSSemaphore((&((vmThread->osSemaphore)))); + } + } + } + assertProcessorStackPointersBelongToCurrentThread(); + GIV(vmOSThread) = (vmThread->osThread); + + /* begin setVmThreadState: */ + currentState = atomic_load((&((vmThread->state)))); + if (currentState != CTMAssignableOrInVM) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMAssignableOrInVM = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMAssignableOrInVM = CTMUnavailable) */); + break; + case CTMUnavailable: + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((vmThread->state))), CTMAssignableOrInVM); + disownCount -= 1; + if ((recordFFIOrThreadTrace()) + && (!traced)) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + thingOne = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceOwnVM) << TraceTypeShift)))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = thingOne; + GIV(traceLog)[GIV(traceLogIndex) + 2] = disownResult; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + if (((flags & DisownVMForProcessorRelinquishFlag) != 0)) { + /* Presumably we have nothing to do; this primitive is typically called from the + background process. So we should /not/ try and activate any threads in the + pool; they will waste cycles finding there is no runnable process, and will + cause a VM abort if no runnable process is found. But we /do/ want to allow + FFI calls that have completed, or callbacks a chance to get into the VM; they + do have something to do. DisownVMForProcessorRelinquish indicates this. */ + GIV(relinquishing) = 0; + sqLowLevelMFence(); + } + else { + ioThreadLocalSetInFFICall(0); + } + if (GIV(disowningThreadIndex)) { + if (threadIndex == GIV(disowningThreadIndex)) { + GIV(disowningThreadIndex) = 0; + GIV(disowningCStackPointer) = null; +# if !NDEBUG + assert(((vmThreadAt(threadIndex))->stackPtrIndex) == stackPointerIndexForFrameWithSP(GIV(framePointer), GIV(stackPointer))); +# endif // !NDEBUG + + return 0; + } + preemptDisowningThread(); + assert(GIV(disowningThreadIndex) == 0); + } + + /* We've been preempted; we must restore state and update the threadId + in our process, and may have to put the active process to sleep. */ + + /* begin restoreVMStateFor:andFlags: */ + assert(GIV(disowningThreadIndex) == 0); + sched = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((sched + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + if (((flags & OwnedVMFromForeignThreadFlag) != 0)) { + myProc = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ForeignCallbackProcess) << (shiftForWord())))))); + assert(myProc != (nilObject())); + + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ForeignCallbackProcess, GIV(specialObjectsOop), GIV(nilObj))); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(GIV(nilObj) & (tagMask())))) + && (oopisLessThan(GIV(nilObj), GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ForeignCallbackProcess) << (shiftForWord()))))),GIV(nilObj)); + } + else { + myProc = (vmThread->boundProcess); + } + assert(couldBeProcess(myProc)); + assert(!((!myProc))); + if ((myProc != GIV(nilObj)) + && (activeProc != myProc)) { + if ((longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))))) == GIV(nilObj)) { + /* If the activeProcess doesn't have a context yet, it needs one from which we can resume later. + This mostly only happens when a threadSwitchIfNecessary:from: ends up switching to a thread that's CTMUnavailable (this thread). + See the comment in threadSwitchIfNecessary:from: */ + + /* begin ensureProcessHasContext: */ + if ((longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))))) == GIV(nilObj)) { + assert(activeProc == (activeProcess())); + + /* The instructionPointer is popped from the stack in 'externalSetStackPageAndPointersForSuspendedContextOfProcess:' */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + + /* We at least need to externalize the stack pointers to enable a thread switch... */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l1; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l1: + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, activeProc, activeContext)); + assert(isNonImmediate(activeProc)); + if (oopisGreaterThanOrEqualTo(activeProc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeContext & (tagMask())))) + && (oopisLessThan(activeContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(activeProc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(activeProc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),activeContext); + } + } + else { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(myProc)) + && (!(isForwarded(myProc)))); + assert(validStorePointerUncheckedArgs(MyListIndex, myProc, nilObject())); + longAtput((void *)((myProc + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),GIV(nilObj)); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ActiveProcessIndex, sched, myProc)); + assert(isNonImmediate(sched)); + if (oopisGreaterThanOrEqualTo(sched, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(myProc & (tagMask())))) + && (oopisLessThan(myProc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(sched + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(sched); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((sched + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord()))))),myProc); + } + if ((vmThread->cStackPointer)) { + GIV(newMethod) = (vmThread->newMethodOrNull); + GIV(argumentCount) = (vmThread->argumentCount); + cFramePointer = (vmThread->cFramePointer); + cStackPointer = (vmThread->cStackPointer); + + /* begin setCFramePointer:setCStackPointer: */ + assert(!(((!cFramePointer) + || (!cStackPointer)))); + GIV(CStackPointer) = cStackPointer; + GIV(CFramePointer) = cFramePointer; + assertSavedCStackPointersWellAligned(); + } + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if (myProc) { + assert(couldBeProcess(myProc)); + context = longAt((void *)((myProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + assert(isContext(context)); + if (((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert((vmThread->inMachineCode) == isMachineCodeFrame(frameOfMarriedContext(context))); + } + else { + assert((vmThread->inMachineCode) == (((fetchPointerofObject(InstructionPointerIndex, context)) >> 3)) < 0); + } + + /* begin externalSetStackPageAndPointersForSuspendedContextOfProcess: */ + newContext = longAt((void *)((myProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + assert(isContext(newContext)); + if (((((longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert(checkIsStillMarriedContextcurrentFP(newContext, GIV(framePointer))); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(myProc)) + && (!(isForwarded(myProc)))); + assert(validStorePointerUncheckedArgs(SuspendedContextIndex, myProc, nilObject())); + longAtput((void *)((myProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),GIV(nilObj)); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(newContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + if (theFrame != ((thePage->headFP))) { + /* explicit assignment of suspendedContext can cause switch to interior frame. */ + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l3; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l3: + moveFramesInthroughtoPage(thePage, findFrameAboveinPage(theFrame, thePage), newPage); + + /* begin markStackPageLeastMostRecentlyUsed: */ + assert(newPage == ((GIV(mostRecentlyUsedPage)->nextPage))); + lastUsedPage = (newPage->nextPage); + while (((lastUsedPage->baseFP)) == 0) { + lastUsedPage = (lastUsedPage->nextPage); + } + if (((lastUsedPage->nextPage)) == newPage) { + goto l2; + } + (((newPage->prevPage))->nextPage = (newPage->nextPage)); + (((newPage->nextPage))->prevPage = (newPage->prevPage)); + (((lastUsedPage->prevPage))->nextPage = newPage); + (newPage->prevPage = (lastUsedPage->prevPage)); + (newPage->nextPage = lastUsedPage); + (lastUsedPage->prevPage = newPage); + assert(pageListIsWellFormed()); + /* end markStackPageLeastMostRecentlyUsed: */ +l2:; + } + assert(((thePage->headFP)) == theFrame); + } + else { + thePage = makeBaseFrameFor(newContext); + theFrame = (thePage->baseFP); + } + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + assert((vmThread->inMachineCode) == isMachineCodeFrame(GIV(framePointer))); + } + + /* hmmm, there is an issue here with out-of-order callback returns on the same thread. + if a callback returns out-of-order we need to unwind the nestedDisownStates to match. + Some comparison against stack pointers is necessary. Some stacks may grow up. + We could reaosnably limit the maximum nesting (64, 128, etc), include it in a field in + the flags, and use this to unwind. We could include a nesting number in DisownState. */ + if ((vmThread->bindingPointKey)) { + if (((vmThread->bindingPointKey)) == ((vmThread->cStackPointer))) { + assert(!((vmThread->nestedDisownStates))); + if ((aob = (vmThread->affinityOnBind))) { + aProcess = (vmThread->boundProcess); + + /* begin threadAffinityFieldOf:put: */ + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aProcess)) + && (!(isForwarded(aProcess)))); + assert(validStorePointerUncheckedArgs(ThreadAffinityIndex, aProcess, aob)); + longAtput((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(ThreadAffinityIndex) << (shiftForWord()))))),aob); + } + (vmThread->boundProcess = null); + (vmThread->bindingPointKey = null); + (vmThread->affinityOnBind = null); + } + else { + disownState = (vmThread->nestedDisownStates); + memcpy(vmThread, disownState, sizeof(DisownState)); + free(disownState); + } + } + assert(GIV(newMethod)); + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + assert((((vmThreadAt(threadIndex))->state)) == CTMAssignableOrInVM); +# if !NDEBUG + assert(((vmThreadAt(threadIndex))->stackPtrIndex) == stackPointerIndexForFrameWithSP(GIV(framePointer), GIV(stackPointer))); +# endif // !NDEBUG + + return 0; +} + + +/* Set the relevant state for disowningVMThread so that it can resume after + being preempted and set disowningVMThread to nil to indicate preemption. + + N.B. This should only be sent from ownVM:. + + There are essentially four things to do. + a) save the VM's notion of the current C stack pointers; these are + pointers into a thread's stack and must be saved and restored on thread + switch. b) save the VM's notion of the current Smalltalk execution point. + This is + simply the suspend half of a process switch that saves the current context + in the current process. + c) ensure the thread and the activeProcess are bound. i.e. if not nested + within a disownVM:/ownVM: callback, set the boundProcess to the active + process. d) save the in-primitive VM state, newMethod, argumentCount, etc + + ownVM: will restore the VM context as of disownVM: from the above when it + finds it has been preempted. */ + + /* CoInterpreterMT>>#preemptDisowningThread */ +static void +preemptDisowningThread(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt activeProc; + CogVMThread *disowningVMThread; + DisownState *nestedState; + sqInt objOop; + + assert(!((GIV(disowningThreadIndex) == 0))); + assert(!((GIV(disowningThreadIndex) == (getVMOwner())))); + + /* begin vmThreadAt: */ + assert(((GIV(disowningThreadIndex) >= 0) && (GIV(disowningThreadIndex) <= GIV(numThreads)))); + disowningVMThread = (GIV(disowningThreadIndex) > 0 + ? GIV(threads)[GIV(disowningThreadIndex)] + : ((CogVMThread *) null)); + assert(((vmThreadState(disowningVMThread)) == CTMUnavailable) + || ((vmThreadState(disowningVMThread)) == CTMWantingOwnership)); + assertCStackPointersBelongToDisowningThread(); + if (recordFFIOrThreadTrace()) { + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TracePreemptDisowningThread) << TraceTypeShift)))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = GIV(disowningThreadIndex); + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + + /* If there is already a boundProcess then preemptDisowningThread is being invoked + to preempt a disowning thread that is within a nested invocation. Save that nested state. */ + if ((disowningVMThread->boundProcess)) { + nestedState = malloc(sizeof(DisownState)); + if (!nestedState) { + error("out of C memory in preemptDisowningThread"); + } + memcpy(nestedState, disowningVMThread, sizeof(DisownState)); + (disowningVMThread->nestedDisownStates = nestedState); + } + + /* Save the current state. Must happen *after* saving outer state. */ + (disowningVMThread->newMethodOrNull = GIV(newMethod)); + (disowningVMThread->argumentCount = GIV(argumentCount)); + (disowningVMThread->cStackPointer = GIV(disowningCStackPointer)); + (disowningVMThread->cFramePointer = GIV(disowningCFramePointer)); +# if !NDEBUG + (disowningVMThread->inMachineCode = GIV(disowningInstructionPointer) < (startOfMemory())); +# endif + + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + if ((disowningVMThread->boundProcess)) { + assert((disowningVMThread->boundProcess) == activeProc); + assert(((disowningVMThread->bindingPointKey))); + } + else { + assert(!((disowningVMThread->bindingPointKey))); + (disowningVMThread->boundProcess = activeProc); + (disowningVMThread->bindingPointKey = GIV(disowningCStackPointer)); + (disowningVMThread->affinityOnBind = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(ThreadAffinityIndex) << (shiftForWord()))))))); + } + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l1; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer) + BytesPerWord); + /* end ensureFrameIsMarried:SP: */ +l1: + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, activeProc, activeContext)); + assert(isNonImmediate(activeProc)); + if (oopisGreaterThanOrEqualTo(activeProc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeContext & (tagMask())))) + && (oopisLessThan(activeContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(activeProc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(activeProc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),activeContext); + + /* The instructionPointer must be pushed because the convention for inactive stack pages is that the + instructionPointer is top of stack. We need to know if this primitive is called from machine code + because the invariant that the return pc of an interpreter callee calling a machine code caller is + ceReturnToInterpreterPC must be maintained. */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + GIV(disowningThreadIndex) = 0; + GIV(disowningCStackPointer) = null; +} + + +/* Set PrimErrFFIException primitive failure and associated exceptionCode + (a.k.a. + secondaryErrorCode) and exceptionPC. Under control of the + ffiExceptionResponse flag, + if in a primitive with an error code and a thread-local flag indicates + we're in an FFI call, + then fail the primitive. + ffiExceptionResponse < 0 never fail (i.e. always crash) + ffiExceptionResponse = 0 fail if method has a primitive error code + (default) ffiExceptionResponse > 0 always fail */ + + /* CoInterpreterMT>>#primitiveFailForFFIException:at: */ +void +primitiveFailForFFIExceptionat(usqLong exceptionCode, usqInt pc) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogMethod *cogMethod; + usqInt i; + usqInt initialIP; + sqInt methodHeader; + usqInt numArgs; + usqInt numTemps; + sqInt object; + sqInt rcvr; + sqInt vmThreadIndex; + + if ((((vmThreadIndex = ((sqInt) (ioThreadLocalGetInFFICall())))) == 0) + || (ffiExceptionResponse < 0)) { + return; + } + assert(vmThreadIndex >= 1); + assert((((sqInt) (ioThreadLocalGetThreadIndex()))) == vmThreadIndex); + ownVM(vmThreadIndex + ((((usqInt)((DisownVMForFFICallFlag + OwnVMForFFIExceptionFlag)) << ThreadIdShift)))); + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + GIV(secondaryErrorCode) = ((sqLong) exceptionCode); + GIV(exceptionPC) = pc; + GIV(primFailCode) = PrimErrFFIException; + if ((ffiExceptionResponse > 0) + || ((/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (methodUsesPrimitiveErrorCode(GIV(newMethod))))) { + assert(isCalloutPrimitiveIndex(primitiveIndexOf(GIV(newMethod)))); + + /* begin activateFailingPrimitiveMethod */ + assert(GIV(primFailCode) != 0); + assert(addressCouldBeObj(GIV(newMethod))); + assert(isCompiledMethod(GIV(newMethod))); + assert((primitiveIndexOf(GIV(newMethod))) != 0); + + /* begin justActivateNewMethod: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + cogMethod = null; + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + methodHeader = (cogMethod->methodHeader); + cogMethod = null; + } + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeader)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(addressCouldBeOop(rcvr)); + assert(!(isOopForwarded(rcvr))); + if (cogMethod) { + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + } + GIV(instructionPointer) = ceReturnToInterpreterPC(); + } + cogMethod = null; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + initialIP = ((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize; + if (cogMethod) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)cogMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + GIV(instructionPointer) = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + GIV(instructionPointer) = initialIP - 1; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + if (!cogMethod) { + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + } + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeader); + } + } + ceInvokeInterpret(); + + /* NOTREACHED */ + } +} + + +/* On some platforms, this primitive forces enqueued display updates to be + processed immediately. On others, it does nothing. + Override so that if the platform requires GUI activity to be restricted to + a given thread, we only force update if on the right thread. + */ + + /* CoInterpreterMT>>#primitiveForceDisplayUpdate */ +static void +primitiveForceDisplayUpdate(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!(((ioEventThreadAffinity()) > 0) + && ((ioEventThreadAffinity()) != (atomic_load((&GIV(vmOwner))))))) { + ioForceDisplayUpdate(); + } +} + + +/* With no arguments answer either nil if there is no long, or a three + element Array + of the size of the log in bytes, the address of the log and the address of + the index. + With one argument, take a buffer of the same size as owner log. Write the + owner log to the given ByteArray. All members of the owner log struct are + always written + as defined by the C struct. Answer the number of instances of + CogVMOwnerLog written into the ByteArray. */ + + /* CoInterpreterMT>>#primitiveGetOwnerLog */ +EXPORT(void) +primitiveGetOwnerLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt array; + char *bufferPointer; + sqInt bytesCopied; + sqInt bytesCopiedSqInt; + sqInt bytesToCopy; + sqInt classObj; + sqInt entriesToSpare; + sqInt index; + sqInt integer; + sqInt knownClassIndex; + sqInt logBuffer; + usqInt newObj; + usqInt numBytes; + sqInt objFormat; + sqInt startIndex; + sqInt valuePointer; + + if (GIV(argumentCount) > 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + if (!GIV(argumentCount)) { + if (!(GIV(ownerLog))) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); + return; + } + classObj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))); + + /* begin eeInstantiateSmallClass:numSlots: */ + assert((rawHashBitsOf(classObj)) != 0); + knownClassIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + objFormat = (((usqInt)((((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((knownClassIndex != 0) + && ((knownClassAtIndex(knownClassIndex)) != GIV(nilObj))); + assert(((objFormat < (firstByteFormat()) + ? objFormat + : objFormat & (byteFormatMask()))) == (instSpecOfClass(knownClassAtIndex(knownClassIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (3 * BytesPerOop); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + array = 0; + goto l2; + } + } + long64Atput((void *)(newObj),((((((usqLong) 3)) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + knownClassIndex); + GIV(freeStart) += numBytes; + array = newObj; + /* end eeInstantiateSmallClass:numSlots: */ +l2: + valuePointer = (((OwnerLogSize * (sizeof(CogVMOwnerLog))) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(0, array, valuePointer)); + longAtput((void *)((array + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&(GIV(ownerLog)))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(1, array, valuePointer)); + longAtput((void *)((array + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&(GIV(ownerLogIndex)))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(2, array, valuePointer)); + longAtput((void *)((array + BaseHeaderSize) + (2U << (shiftForWord()))),valuePointer); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),array); + return; + } + logBuffer = longAt(GIV(stackPointer)); + if (!(((!(logBuffer & (tagMask())))) + && ((isPureBitsNonImm(logBuffer)) + && ((numBytesOf(logBuffer)) >= (OwnerLogSize * (sizeof(CogVMOwnerLog))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + bufferPointer = firstIndexableField(logBuffer); + + /* begin copyLogTo: */ + if (!GIV(ownerLog)) { + bytesCopied = 0; + goto l1; + } + index = atomic_load((&GIV(ownerLogIndex))); + bytesCopiedSqInt = 0; + + /* NOTE: The ownerLogWrapped isn't synchronized atomically. + Therefore, if this primitive is called exactly when we start wrapping the very first time, we'll read the wrong value here. + However, as this is very unlikely, don't bother fixing this (yet). */ + if (GIV(ownerLogWrapped)) { + /* NOTE: The owner log is still written to whilst this primitive is running. + Therefore copy only 90% of the log, such that the remaining 10% can be spared + to still be written. */ + entriesToSpare = OwnerLogSize / 10; + startIndex = (((index + entriesToSpare) < (OwnerLogSize - 1)) ? (index + entriesToSpare) : (OwnerLogSize - 1)); + bytesToCopy = (OwnerLogSize - startIndex) * (sizeof(CogVMOwnerLog)); + memcpy(bufferPointer, GIV(ownerLog) + startIndex, bytesToCopy); + bytesCopiedSqInt += bytesToCopy; + } + bytesToCopy = index * (sizeof(CogVMOwnerLog)); + memcpy(bufferPointer + bytesCopiedSqInt, GIV(ownerLog), bytesToCopy); + bytesCopied = bytesCopiedSqInt + bytesToCopy; + /* end copyLogTo: */ +l1: + integer = bytesCopied / (sizeof(CogVMOwnerLog)); + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); +} + + +/* Attempt to bind the receiver to the thread affinity of the argument or + nil, where the receiver is a Process. + The thread affinity may be an integer where: + 0 - means no thread affinity, the process is free to run on any thread. + > 0 - positive values mean the process has to run on the thread with this + specific index. + < 0 - negative values mean the process may run on on any thread **APART** + from the thread + with the absolute value of the index. + + Usually values of 1, -1 and 0 are used. + Thread number 1 is the thread the VM started with. On some OSes this + thread has special priviliges. + I.e. on macOS only thread 1 can make draw calls. + Therefore it is mostly important whether a thread must run on thread 1, + must **not** run on thread 1 + or whether it doesn't care. + + If successful the VM will ensure that there is at least one compatible + thread active. */ + + /* CoInterpreterMT>>#primitiveProcessBindToThreadAffinity */ +static void +primitiveProcessBindToThreadAffinity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activePriority; + CogVMThread *affinedThread; + sqInt aProcess; + sqInt currentAffinity; + int existing; + sqInt newAffinity; + sqInt newAffinityOop; + sqInt oop; + sqInt threadAffinityOop; + sqInt waitingPriority; + + newAffinityOop = longAt(GIV(stackPointer)); + aProcess = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(((newAffinityOop == GIV(nilObj)) + || (((((newAffinityOop) & 7) == 1)) + && (newAffinityOop != ConstZero))) + && ((/* isPointers: */ + ((!(aProcess & (tagMask())))) + && (((byteAt((void *)(aProcess + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((slotSizeOf(aProcess)) >= (ThreadAffinityIndex + 1))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + newAffinity = (newAffinityOop == GIV(nilObj) + ? 0 + : (newAffinityOop >> 3)); + if ((SQABS(newAffinity)) >= ((1U << ThreadIdShift) - 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrLimitExceeded; + return; + } + + /* Check if the process is temporarily affined and executing external code. + If so simply assighn the new affinity to the affinityOnBind field of its thread. */ + threadAffinityOop = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(ThreadAffinityIndex) << (shiftForWord())))))); + currentAffinity = /* threadAffinityOfAffinityOop: */ + ((((threadAffinityOop) & 7) == 1) + ? (threadAffinityOop >> 3) + : 0); + if ((affinedThread = affinedThreadOrNilForProcesscurrentAffinityoop(aProcess, currentAffinity, threadAffinityOop))) { + if (((affinedThread->boundProcess)) == aProcess) { + (affinedThread->affinityOnBind = newAffinityOop); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return; + } + } + else { + if (currentAffinity > 0) { + if (!(startThreadForThreadIndex(currentAffinity))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrLimitExceeded; + return; + } + } + } + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + waitingPriority = atomic_load((&GIV(maxWaitingPriority))); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + activePriority = (oop >> 3); + if ((aProcess == (activeProcess())) + && ((currentAffinity != 0) + && (!(vmOwnerIsCompatibleWith(currentAffinity))))) { + if (activePriority < waitingPriority) { + /* begin reduceWaitingPriorityFrom:to: */ + existing = waitingPriority; + + /* This CPXCHG may fail, that's fine though, as there may have been + another thread that increased the priority in the meantime. + In that case that threads priority is the correct one to use. */ + atomic_compare_exchange_strong((&GIV(maxWaitingPriority)), (&existing), activePriority); + } + threadSwitchIfNecessaryfrom(aProcess, CSThreadBind); + } +} + + +/* Answer the receiver's current threadAffinity or nil, where the receiver is + a Process. + If the threadAffinity is positive then the receiver is bound to the thread + with that id. + If the threadAffinity is negative then the receiver is excluded from + running on the thread with that id. + */ + + /* CoInterpreterMT>>#primitiveProcessBoundThreadId */ +EXPORT(void) +primitiveProcessBoundThreadId(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aProcess; + sqInt id; + sqInt oop; + + aProcess = longAt(GIV(stackPointer)); + id = threadAffinityOfProcess(aProcess); + oop = (id + ? (((usqInt)id << 3) | 1) + : GIV(nilObj)); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} + + +/* Relinquish the processor for up to the given number of microseconds. + The exact behavior of this primitive is platform dependent. + Override to check for waiting threads. */ + + /* CoInterpreterMT>>#primitiveRelinquishProcessor */ +static void +primitiveRelinquishProcessor(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + volatile usqIntptr_t currentCFramePointer; + volatile usqIntptr_t currentCStackPointer; + sqInt disownResult; + sqInt microSecs; + + microSecs = longAt(GIV(stackPointer)); + if (!((((microSecs) & 7) == 1))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + assert((fetchPointerofObject(MyListIndex, activeProcess())) == (nilObject())); + assert(!GIV(relinquishing)); + + /* DO NOT allow relinquishing the processor while we are profiling since this + may skew the time base for our measures (it may reduce processor speed etc). + Instead we go full speed, therefore measuring the precise time we spend in the + inner idle loop as a busy loop. */ + if (!GIV(nextProfileTick)) { + currentCStackPointer = GIV(CStackPointer); + currentCFramePointer = GIV(CFramePointer); + disownResult = disownVM(DisownVMForProcessorRelinquishFlag); + assert((((((usqInt)(disownResult)) >> ThreadIdShift) & DisownVMForProcessorRelinquishFlag) != 0)); + assert(GIV(relinquishing)); + ioRelinquishProcessorForMicroseconds((microSecs >> 3)); + ownVM(disownResult); + assert(!(GIV(relinquishing))); + assert((vmThreadState(currentVMThread())) == CTMAssignableOrInVM); + assert(currentCStackPointer == GIV(CStackPointer)); + assert(currentCFramePointer == GIV(CFramePointer)); + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + + /* In simulation we allow ioRelinquishProcessorForMicroseconds: to fail so that + we can arrange that the simulator responds to input events promptly. This + *DOES NOT HAPPEN* in the real vm. */ + } + + /* Presumably we have nothing to do; this primitive is typically called from the + background process. So we should /not/ try and activate any threads in the + pool; they will waste cycles finding there is no runnable process, and will + cause a VM abort if no runnable process is found. But we /do/ want to allow + FFI calls that have completed, or callbacks a chance to get into the VM; they + do have something to do. DisownVMForProcessorRelinquishFlag indicates this. */ + assert((fetchPointerofObject(MyListIndex, activeProcess())) == (nilObject())); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; +} + + +/* Force the given rectangular section of the Display to be copied to the + screen. On some platforms, this primitive forces enqueued display updates + to be processed immediately. On others, it does nothing. + Override so that if the platform requires GUI activity to be restricted to + a given thread, we ony force update if on the right thread. */ + + /* CoInterpreterMT>>#primitiveShowDisplayRect */ +static void +primitiveShowDisplayRect(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bottom; + sqInt integerPointer; + sqInt left; + sqInt right; + sqInt top; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + bottom = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + bottom = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + top = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + top = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + right = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + right = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + left = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + left = 0; + } + if (!GIV(primFailCode)) { + updateDisplayLeftTopRightBottom(left, top, right, bottom); + if (!(((ioEventThreadAffinity()) > 0) + && ((ioEventThreadAffinity()) != (atomic_load((&GIV(vmOwner))))))) { + ioForceDisplayUpdate(); + } + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } +} + + +/* Answer the VM's current thread's Id */ + + /* CoInterpreterMT>>#primitiveVMCurrentThreadId */ +EXPORT(void) +primitiveVMCurrentThreadId(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integer; + + integer = atomic_load((&GIV(vmOwner))); + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); +} + + +/* Print all the stacks of all running processes, including those that are + currently suspended. + */ +/* useful for VM debugging */ +/* Print all the stacks of all running processes, including those that are + currently suspended. + Override to print the stacks of processes in external code (i.e. that are + threading). + */ + + /* CoInterpreterMT>>#printAllStacks */ +void +printAllStacks(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt boundProcess; + sqInt classPointer; + sqInt first; + sqInt followingWord; + usqInt followingWordAddress; + sqInt i; + sqInt linkedListClass; + sqInt minProcessInstSize; + sqInt myList; + sqInt myListClass; + usqInt numSlots; + sqInt objOop; + sqInt objSqInt; + usqInt p; + sqInt prevObj; + sqInt prevPrevObj; + sqInt pri; + sqInt proc; + sqInt processClass; + sqInt processList; + sqInt schedLists; + sqInt startObject; + sqInt tagBits; + + /* may not be an instance of process. may in exceptional circumstances be nilObject */ + proc = activeProcess(); + printNameOfClasscount(/* fetchClassOf: */ + ((tagBits = proc & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(proc)), 5); + + /* begin space */ + printChar(' '); + printHex(proc); + print(" priority "); + printNum(quickFetchIntegerofObject(PriorityIndex, proc)); + cr(); + if (GIV(framePointer)) { + printCallStack(); + } + else { + printProcessStack(proc); + } + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + schedLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + linkedListClass = null; + + /* then the runnable processes */ + p = (GIV(highestRunnableProcessPriority) + ? GIV(highestRunnableProcessPriority) + : numSlotsOf(schedLists)); + for (pri = (p - 1); pri >= 0; pri += -1) { + processList = longAt((void *)((schedLists + BaseHeaderSize) + ((((usqInt)(pri) << (shiftForWord())))))); + if (!(isEmptyList(processList))) { + if (proc == GIV(nilObj)) { + proc = longAt((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + } + cr(); + print("processes at priority "); + printNum(pri + 1); + printProcsOnList(processList); + } + if (!linkedListClass) { + linkedListClass = fetchClassOfNonImm(processList); + } + } + if (!linkedListClass) { + classPointer = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord())))))); + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + linkedListClass = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(linkedListClass)); + if ((!((longAt((void *)(linkedListClass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + linkedListClass = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, linkedListClass); + } + } + if (proc == GIV(nilObj)) { + cr(); + print("Cannot find a runnable process. Cannot therefore determine class Process. Cannot therefore print suspended processes"); + return; + } + cr(); + print("suspended processes"); + + /* Find the root of the Process hierarchy. It is the class, or superclass, + of a process, that has inst size at least large enough to include myList */ + processClass = (!(proc == GIV(nilObj)) + ? /* fetchClassOf: */ + ((tagBits = proc & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(proc)) + : 0); + minProcessInstSize = MyListIndex + 1; + while ((instanceSizeOf(superclassOf(processClass))) >= minProcessInstSize) { + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((processClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, processClass, objOop); + } + processClass = objOop; + } + minProcessInstSize = instanceSizeOf(processClass); + + /* look for all subInstances of process that have a context as a suspendedContext and are on a list other than a LinkedList */ + + /* begin allObjectsDoSafely: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + if (((longAt((void *)(objSqInt))) & (classIndexMask())) > (lastClassIndexPun())) { + if ((((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + && (((numSlotsOf(objSqInt)) >= minProcessInstSize) + && ((isContext(longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))))) + && (isKindOfClass(objSqInt, processClass))))) { + /* Is the process waiting on some delaying list? This will be a subclass of LinkedList. + If so, assume it is blocked on the list. */ + myList = longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord())))))); + if ((myList != GIV(nilObj)) + && ((((myListClass = fetchClassOfNonImm(myList))) != linkedListClass) + && (isKindOfClass(myList, linkedListClass)))) { + printProcessStack(objSqInt); + } + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } + first = 1; + for (i = 1; i <= GIV(numThreads); i += 1) { + if ((boundProcess = ((vmThreadAt(i))->boundProcess))) { + if (first) { + cr(); + print("processes in external code"); + first = 0; + } + printProcessStack(boundProcess); + } + } +} + + /* CoInterpreterMT>>#printLogEntryAt: */ +static NoDbgRegParms void +printLogEntryAt(sqInt i) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classMethodOrProcess; + sqInt selectorOrExtra; + sqInt source; + usqInt traceType; + sqInt vmOwnerTraceTypeAndSource; + + vmOwnerTraceTypeAndSource = GIV(traceLog)[i]; + traceType = (((usqInt)(vmOwnerTraceTypeAndSource)) >> TraceTypeShift) & 0xFF; + source = vmOwnerTraceTypeAndSource & (((1U << TraceTypeShift)) - 1); + + /* a.k.a. thingOne */ + classMethodOrProcess = GIV(traceLog)[i + 1]; + + /* a.k.a. thingTwo */ + selectorOrExtra = GIV(traceLog)[i + 2]; + fprintf(GIV(transcript), + "vmo:%d\t", + ((int) (((usqInt)(vmOwnerTraceTypeAndSource)) >> TraceOwnerShift))); + switch (traceType) { + case TraceSend: + /* begin printSendLogEntry:source:selector: */ + print((((source & CSFromMachineCode) != 0) + ? "m " + : "i ")); + printNameOfClasscount(classMethodOrProcess, 5); + print(">>"); + printStringOf(selectorOrExtra); + break; + case TraceStackOverflow: + print("stack overflow"); + break; + case TraceContextSwitch: + fprintf(GIV(transcript), + "context switch from %p to %p", + ((void *)classMethodOrProcess), + ((void *)selectorOrExtra)); + break; + case TraceBlockActivation: + fprintf(GIV(transcript), + " [] in %p", + ((void *)classMethodOrProcess)); + break; + case TraceBlockCreation: + fprintf(GIV(transcript), + "create [] %p", + ((void *)classMethodOrProcess)); + break; + case TraceIncrementalGC: + print("incrementalGC"); + break; + case TraceFullGC: + print("fullGC"); + break; + case TraceCodeCompaction: + print("compactCode"); + break; + case TraceVMCallback: + fprintf(GIV(transcript), + "callback %p", + ((void *)classMethodOrProcess)); + break; + case TraceVMCallbackReturn: + fprintf(GIV(transcript), + "callback return %p type %d", + ((void *)classMethodOrProcess), + ((int) source)); + break; + case TraceAttemptOwnVM: + fprintf(GIV(transcript), + "ownVM (try) by %d disownResult:%x", + ((int) (selectorOrExtra & (((1U << ThreadIdShift)) - 1))), + ((int) selectorOrExtra)); + break; + case TraceOwnVM: + fprintf(GIV(transcript), + "ownVM by %d activeProc %p disownResult:%x", + ((int) (selectorOrExtra & (((1U << ThreadIdShift)) - 1))), + ((void *)classMethodOrProcess), + ((int) selectorOrExtra)); + break; + case TraceDisownVM: + fprintf(GIV(transcript), + "disownVM activeProc %p flags:%x", + ((void *)classMethodOrProcess), + ((int) selectorOrExtra)); + break; + case TraceThreadSwitch: + fprintf(GIV(transcript), + "thread switch %d -> %d", + ((int) classMethodOrProcess), + ((int) selectorOrExtra)); + break; + case TracePreemptDisowningThread: + fprintf(GIV(transcript), + "preempt disowning thread %d", + ((int) classMethodOrProcess)); + break; + default: + print("UNKNOWN EVENT"); + } + if ((((traceType >= TraceStackOverflow) && (traceType <= TracePreemptDisowningThread))) + && (source > 1)) { + /* begin space */ + printChar(' '); + print(traceSources[((usqInt)(source)) >> 1]); + } + cr(); +} + + +/* Print all threads other than those that are uninitialized. */ + + /* CoInterpreterMT>>#printThreads */ +void +printThreads(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt state; + static const char * stateNames[] = { + "unknown owner", "unused", "initializing", "unavailable", "assignableOrInVM", + "wantingOwnership" + }; + CogVMThread *vmThread; + + for (i = 1; i <= GIV(numThreads); i += 1) { + vmThread = vmThreadAt(i); + state = atomic_load((&((vmThread->state)))); + if (!(((state >= 0) && (state <= CTMUninitialized)))) { + fprintf(GIV(transcript), + "thread %d %p %s prio %d newmeth %p argc %d boundProc %p nest: %p affinity: %p%s\n", + ((int) i), + vmThread, + stateNames[state + 1], + ((int) ((vmThread->priority))), + ((void *)((vmThread->newMethodOrNull))), + (vmThread->argumentCount), + ((void *)((vmThread->boundProcess))), + (vmThread->nestedDisownStates), + ((void *)((vmThread->affinityOnBind))), + (((vmThread->affinityOnBind)) == GIV(nilObj) + ? " (nil)" + : 0)); + } + } +} + + /* CoInterpreterMT>>#returnToSchedulingLoopAndReleaseVMOrWakeThread:source: */ +static NoDbgRegParms void +returnToSchedulingLoopAndReleaseVMOrWakeThreadsource(CogVMThread *vmThread, sqInt source) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + CogVMThread *activeThread; + sqInt index; + sqInt ownerIndex; + sqInt thingTwo; + + /* begin currentVMThread */ + index = atomic_load((&GIV(vmOwner))); + assert(((index >= 0) && (index <= GIV(numThreads)))); + activeThread = (index > 0 + ? GIV(threads)[index] + : ((CogVMThread *) null)); + ownerIndex = (vmThread + ? (vmThread->index) + : 0); + + /* begin recordThreadSwitchTo:source: */ + if (recordFFIOrThreadTrace()) { + thingTwo = atomic_load((&GIV(vmOwner))); + + /* begin recordTrace:source:thing:extra: */ + assert(source < ((1U << TraceTypeShift))); + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceThreadSwitch) << TraceTypeShift))))) + source); + GIV(traceLog)[GIV(traceLogIndex) + 1] = ownerIndex; + GIV(traceLog)[GIV(traceLogIndex) + 2] = thingTwo; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + if (vmThread) { + wakeVMThread(vmThread); + } + else { + /* begin releaseVM */ + /* begin setVMOwner: */ + assert(((getVMOwner()) == CTMUnknownOwner) + || ((getVMOwner()) == (ioThreadLocalGetThreadIndex()))); + assert((getVMOwner()) != 0); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(0, 1); + } + + /* TODO: We could make this a `release` ordering, which may perform better on ARM. */ + atomic_store((&GIV(vmOwner)), 0); + } + + /* I am not frightened of flying. + Any value will do. I don't mind. + Why should I be frightened of flying? + There's no reason for it. */ + _longjmp((activeThread->reenterThreadSchedulingLoop), 1); +} + + +/* Send the calllback message to Alien class with the supplied arg(s). Use + either the 1 arg + invokeCallbackContext: or the 4 arg invokeCallback:stack:registers:jmpbuf: + message, depending on what selector is installed in the + specialObjectsArray. Note that if invoking the + legacy invokeCallback:stack:registers:jmpbuf: we pass the + vmCallbackContext as the jmpbuf + argument (see reestablishContextPriorToCallback:). The arguments are raw C + addresses and + are converted to integer objects on the way. sendInvokeCallbackContext: & + returnAs:ThroughCallback:Context: along with ownVM: and disownVM: conspire + to save and + restore newMethod, argumentCount and primitiveFunctionPointer around a + callback. The VM depends on argumentCount being correct to cut-back the + correct number of + arguments on primitive return. Since this is an implicit send we need to + log it explicitly. + The return side is done via a primitive so that gets logged normally. */ +/* Override check stack alignment. */ +/* Override to add sanity assertions. */ + + /* CoInterpreterMT>>#sendInvokeCallbackContext: */ +sqInt +sendInvokeCallbackContext(VMCallbackContext *vmCallbackContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTag; + CogMethod *cogMethod; + usqInt i; + usqInt initialIP; + sqInt methodHeader; + usqInt numArgs; + usqInt numTemps; + sqInt object; + sqInt rcvr; + sqInt thunkp; + + /* begin assertSaneThreadAndProcess */ + assert(vmIsOwned()); + assert((vmThreadState(currentVMThread())) == CTMAssignableOrInVM); + assert((fetchPointerofObject(MyListIndex, activeProcess())) == (nilObject())); + assertValidProcessorStackPointersForIndex(atomic_load((&GIV(vmOwner)))); + assertCStackWellAligned(); + if (recordPrimTrace()) { + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorInvokeCallback) << (shiftForWord()))))))); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + thunkp = ((sqInt) ((vmCallbackContext->thunkp))); + + /* begin recordInvokeCallback: */ + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceVMCallback) << TraceTypeShift)))))); + GIV(traceLog)[GIV(traceLogIndex) + 1] = (((sqInt)thunkp)); + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + + /* begin fetchClassTagOfNonImm: */ + classTag = (longAt((void *)(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord()))))))))) & (classIndexMask()); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorInvokeCallback) << (shiftForWord())))))); + if (!(lookupInMethodCacheSelclassTag(GIV(messageSelector), classTag))) { + if (lookupOrdinaryNoMNUEtcInClass(classForClassTag(classTag))) { + return 0; + } + } + assert(((debugCallbackInvokes += 1)) > 0); + + /* self assert: debugCallbackInvokes < 3802. */ + + /* begin saveCStackStateForCallbackContext: */ + (vmCallbackContext->savedCStackPointer = ((void *)GIV(CStackPointer))); + (vmCallbackContext->savedCFramePointer = ((void *)GIV(CFramePointer))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord()))))))); + if ((argumentCountOf(GIV(newMethod))) == 4) { + object = positive64BitIntegerFor(((usqInt)((vmCallbackContext->thunkp)))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + object = positive64BitIntegerFor(((usqInt)((vmCallbackContext->stackp)))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + object = positive64BitIntegerFor(((usqInt)((vmCallbackContext->intregargsp)))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + object = positive64BitIntegerFor(((usqInt)vmCallbackContext)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + if (((cogMethod->selector)) == GIV(nilObj)) { + setSelectorOfto(cogMethod, GIV(messageSelector)); + } + } + else { + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + cogselector(GIV(newMethod), GIV(messageSelector)); + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + } + + /* begin justActivateNewMethod: */ + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + cogMethod = null; + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + methodHeader = (cogMethod->methodHeader); + } + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeader)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvr = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(addressCouldBeOop(rcvr)); + assert(!(isOopForwarded(rcvr))); + if (cogMethod) { + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + } + GIV(instructionPointer) = ceReturnToInterpreterPC(); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + initialIP = ((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize; + if (cogMethod) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)cogMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + GIV(instructionPointer) = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + GIV(instructionPointer) = initialIP - 1; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + if (!cogMethod) { + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + } + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeader); + } + } + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(GIV(newMethod)); + } + } + + /* begin checkForStackOverflow */ + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (GIV(stackPointer) < ((GIV(stackPage)->realStackLimit))) { + handleStackOverflow(); + } + assert((frameReceiver(GIV(framePointer))) == (splObj(ClassAlien))); + + /* begin enterSmalltalkExecutiveFromCallback */ + enterSmalltalkExecutive(); + + /* not reached */ + return 1; +} + + +/* useful for debugging, so don't inline */ + + /* CoInterpreterMT>>#threadAffinityOfProcess: */ +static NoDbgRegParms sqInt +threadAffinityOfProcess(sqInt aProcess) +{ + sqInt threadId; + + threadId = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(ThreadAffinityIndex) << (shiftForWord())))))); + + /* begin threadAffinityOfAffinityOop: */ + return ((((threadId) & 7) == 1) + ? (threadId >> 3) + : 0); +} + + +/* Enter a loop attempting to run the VM with the highest priority process + and blocking on the thread's OS semaphore when unable to run that process. + We will return to this via threadSwitchIfNecessary:from: which is called + in the + middle of transferTo:from: once the active process has been stored in the + scheduler. */ +/* for Slang and the ifTrue: below... */ + + /* CoInterpreterMT>>#threadSchedulingLoopImplementation: */ +static NoDbgRegParms void +threadSchedulingLoopImplementation(CogVMThread *vmThread) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + sqInt aMethodObj; + sqInt currentState; + int expected; + sqInt header; + StackPage *lastUsedPage; + sqInt methodHeader; + sqInt newContext; + StackPage *newPage; + sqInt objOop; + sqInt ok; + sqInt senderOop; + char *theFrame; + StackPage *thePage; + sqInt threadAffinity; + sqInt threadIndex; + sqInt top; + CogVMThread *willingThread; + + _setjmp((vmThread->reenterThreadSchedulingLoop)); + do { + assert((vmThreadState(vmThread)) == CTMAssignableOrInVM); + threadIndex = (vmThread->index); + + /* begin tryLockVMOwnerTo: */ + assert(!((threadIndex == 0))); + expected = 0; + ok = (atomic_compare_exchange_strong((&GIV(vmOwner)), (&expected), threadIndex)) + || ((/* We may already be vmOwner. The current vmOwner will be stored in expected. + However, if an unknown owner is present, we cannot assume that's us! */ + + (expected == threadIndex) + && (threadIndex != CTMUnknownOwner))); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(threadIndex, ok); + } + if (ok) { + /* Yay, we're the VM owner! + If relinquishing is true, then primitiveRelinquishProcessor has disowned the + VM and only a returning call or callback should take ownership in that case. */ + if (!GIV(relinquishing)) { + /* begin tryToExecuteSmalltalk: */ + assert(vmOwnerIs((vmThread->index))); + assert((ioThreadLocalGetThreadIndex()) == ((vmThread->index))); + sqLowLevelMFence(); + if (GIV(disowningThreadIndex)) { + preemptDisowningThread(); + assert(GIV(disowningThreadIndex) == 0); + activeProc = wakeHighestPriority(); + if (activeProc) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(activeProc)) + && (!(isForwarded(activeProc)))); + assert(validStorePointerUncheckedArgs(MyListIndex, activeProc, GIV(nilObj))); + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + activeProc = GIV(nilObj); + } + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ActiveProcessIndex, objOop, activeProc)); + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeProc & (tagMask())))) + && (oopisLessThan(activeProc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord()))))),activeProc); + } + else { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + } + + /* There is a special case here. + When the VM has relinquished, but then another thread finishes external code execution, there may no longer be a process to run. + However, the relinquishing flag may already have been reset by another thread that has owned the VM again. */ + if (activeProc == GIV(nilObj)) { + goto l3; + } + threadAffinity = threadAffinityOfProcess(activeProc); + if (vmOwnerIsCompatibleWith(threadAffinity)) { + assert((fetchPointerofObject(MyListIndex, activeProcess())) == (nilObject())); + + /* If we switch threads in threadSwitchIfNecessary:from:, the interpreter state is likely + already in the correct state. + In that case, there is no suspended context and nothing to restore. We can just continue + execution. + If there is a suspended context, assume that we need to restore the state from that. */ + if ((longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))))) != GIV(nilObj)) { + /* begin externalSetStackPageAndPointersForSuspendedContextOfProcess: */ + newContext = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + assert(isContext(newContext)); + if (((((longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert(checkIsStillMarriedContextcurrentFP(newContext, GIV(framePointer))); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(activeProc)) + && (!(isForwarded(activeProc)))); + assert(validStorePointerUncheckedArgs(SuspendedContextIndex, activeProc, nilObject())); + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),GIV(nilObj)); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(newContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + if (theFrame != ((thePage->headFP))) { + /* explicit assignment of suspendedContext can cause switch to interior frame. */ + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l1; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l1: + moveFramesInthroughtoPage(thePage, findFrameAboveinPage(theFrame, thePage), newPage); + + /* begin markStackPageLeastMostRecentlyUsed: */ + assert(newPage == ((GIV(mostRecentlyUsedPage)->nextPage))); + lastUsedPage = (newPage->nextPage); + while (((lastUsedPage->baseFP)) == 0) { + lastUsedPage = (lastUsedPage->nextPage); + } + if (((lastUsedPage->nextPage)) == newPage) { + goto l2; + } + (((newPage->prevPage))->nextPage = (newPage->nextPage)); + (((newPage->nextPage))->prevPage = (newPage->prevPage)); + (((lastUsedPage->prevPage))->nextPage = newPage); + (newPage->prevPage = (lastUsedPage->prevPage)); + (newPage->nextPage = lastUsedPage); + (lastUsedPage->prevPage = newPage); + assert(pageListIsWellFormed()); + /* end markStackPageLeastMostRecentlyUsed: */ +l2:; + } + assert(((thePage->headFP)) == theFrame); + } + else { + thePage = makeBaseFrameFor(newContext); + theFrame = (thePage->baseFP); + } + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + } + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + assert(!((isMachineCodeFrame(GIV(framePointer))))); + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + enterSmalltalkExecutive(); + + /* When we return here we should have already given up + the VM and so we cannot touch any interpreter state. */ + error("NOTREACHED"); + } + + /* begin returnToSchedulingLoopAndWakeThreadFor:source: */ + assert(!(vmOwnerIsCompatibleWith(threadAffinity))); + assert(threadAffinity != 0); + if (threadAffinity > 0) { + assert(((threadAffinity >= 1) && (threadAffinity <= GIV(numThreads)))); + returnToSchedulingLoopAndReleaseVMOrWakeThreadsource(GIV(threads)[threadAffinity], CSTryToExecuteSmalltalk); + } + else { + assert((getVMOwner()) == (-threadAffinity)); + + /* We know the thread affinity is 'any thread other then this one!'. */ + willingThread = ensureWillingThread(); + if (willingThread) { + returnToSchedulingLoopAndReleaseVMOrWakeThreadsource(willingThread, CSTryToExecuteSmalltalk); + } + } + /* end tryToExecuteSmalltalk: */ +l3:; + } + + /* tryToExecuteSmalltalk: may return if there's no runnable process. + Usually it doesn't return, but jumps straight back to the _setjmp at the top of this function, + so this is only reached in case there's no runnable process. + TODO: Do we need to saveRegisterStateForCurrentProcess here? */ + + /* begin releaseVM */ + /* begin setVMOwner: */ + assert(((getVMOwner()) == CTMUnknownOwner) + || ((getVMOwner()) == (ioThreadLocalGetThreadIndex()))); + assert((getVMOwner()) != 0); + + /* begin logOwnerSwitchTo:successful: */ + if (GIV(ownerLog)) { + saveOwnerSwitchTosuccessful(0, 1); + } + + /* TODO: We could make this a `release` ordering, which may perform better on ARM. */ + atomic_store((&GIV(vmOwner)), 0); + } + + /* begin waitForWork: */ + /* begin setVmThreadState: */ + currentState = atomic_load((&((vmThread->state)))); + if (currentState != CTMAssignableOrInVM) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMAssignableOrInVM = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMAssignableOrInVM = CTMUnavailable) */); + break; + case CTMUnavailable: + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((vmThread->state))), CTMAssignableOrInVM); + assert(!((vmOwnerIs((vmThread->index))))); + ioWaitOnOSSemaphore((&((vmThread->osSemaphore)))); + } while(1); +} + + +/* Invoked from transferTo:from: or primitiveProcessBindToThreadId to + switch threads if the new process is bound or affined to some other + thread. + */ + + /* CoInterpreterMT>>#threadSwitchIfNecessary:from: */ +static NoDbgRegParms void +threadSwitchIfNecessaryfrom(sqInt newProc, sqInt sourceCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentState; + sqInt newProcThreadAffinity; + sqInt threadSwitchNecessary; + CogVMThread *vmThread; + + assert(vmOwnerIs(ioThreadLocalGetThreadIndex())); + if (GIV(deferThreadSwitch)) { + return; + } + assertValidProcessorStackPointersForIndex(atomic_load((&GIV(vmOwner)))); + + /* If the current process is unaffined or it is affined to the current thread we're + ok to run, but we should yield asap if a higher-priority thread wants the VM. */ + newProcThreadAffinity = threadAffinityOfProcess(newProc); + threadSwitchNecessary = (newProcThreadAffinity != 0) + && (!(vmOwnerIsCompatibleWith(newProcThreadAffinity))); + if (!threadSwitchNecessary) { + if ((quickFetchIntegerofObject(PriorityIndex, newProc)) < (atomic_load((&GIV(maxWaitingPriority))))) { + GIV(checkThreadActivation) = 1; + forceInterruptCheck(); + } + + /* We're done, no thread switch necessary */ + return; + } + + /* The current process is affined to a thread, but not to the current owner. So switch to that owner. */ + + /* In most cases, we can just switch the thread here, without externalizing the stack pages. + If the Processes context is nil, it's state is on the stack. As we're already done context switching, + the new thread can just use the interpreter state as-is, without restoring the state from the context. + + tryToExecuteSmalltalk: already includes a check whether the SuspendedContext is nil. + If it is, it leaves the interpreter state alone and just assumes it's correct. + This is nice and fast. + Otherwise it calls externalSetStackPageAndPointersForSuspendedContextOfProcess: to restore the interpreter state. + + There is however a special case. When we switch to a thread that is currently CTMUnavailable, that thread will need + to restore its process when it tries to own the VM again. + The check to restore the context has been moved there (in restoreVMStateFor:andFlags:), so that it only happens in + that one case and not every time. + In case there are other such special-cases later, adding a call to ensureProcessHasContext: here should fix it. */ + if (newProcThreadAffinity < 0) { + assert((-newProcThreadAffinity) == (getVMOwner())); + vmThread = ensureWillingThread(); + assert(!((((vmThread->index)) == (getVMOwner())))); + assert(threadIndexisCompatibleWith((vmThread->index), newProcThreadAffinity)); + } + else { + /* begin vmThreadAt: */ + assert(((newProcThreadAffinity >= 0) && (newProcThreadAffinity <= GIV(numThreads)))); + vmThread = (newProcThreadAffinity > 0 + ? GIV(threads)[newProcThreadAffinity] + : ((CogVMThread *) null)); + (vmThread->priority = quickFetchIntegerofObject(PriorityIndex, newProc)); + if ((atomic_load((&((vmThread->state))))) == CTMUnavailable) { + /* begin setVmThreadState: */ + currentState = atomic_load((&((vmThread->state)))); + if (currentState != CTMWantingOwnership) { + switch (currentState) { + case CTMUninitialized: + assert(0 /* (CTMWantingOwnership = CTMInitializing) */); + break; + case CTMInitializing: + case CTMWantingOwnership: + assert(0 /* (CTMWantingOwnership = CTMAssignableOrInVM) */); + break; + case CTMAssignableOrInVM: + assert(0 /* (CTMWantingOwnership = CTMUnavailable) */); + break; + case CTMUnavailable: + break; + default: +; + } + } + + /* The actual meat of the operation. The previous checks are only for debugging. */ + atomic_store((&((vmThread->state))), CTMWantingOwnership); + } + } + returnToSchedulingLoopAndReleaseVMOrWakeThreadsource(vmThread, CSSwitchIfNeccessary); +} + + +/* Record a process to be awoken on the next interpreter cycle. Override to + potentially switch threads either if the new process is bound to another + thread, or if there is no runnable process but there is a waiting thread. */ + + /* CoInterpreterMT>>#transferTo:from: */ +static NoDbgRegParms sqInt +transferTofrom(sqInt newProcOrNil, sqInt sourceCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt aMethodObj; + sqInt header; + StackPage *lastUsedPage; + usqInt lip; + sqInt methodHeader; + sqInt newContext; + StackPage *newPage; + sqInt oldProc; + sqInt sched; + sqInt senderOop; + char *theFrame; + StackPage *thePage; + sqInt top; + CogVMThread *vmThread; + + if (GIV(disowningThreadIndex)) { + preemptDisowningThread(); + assert(GIV(disowningThreadIndex) == 0); + } + GIV(statProcessSwitch) += 1; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + lip = (GIV(instructionPointer) >= (startOfMemory()) + ? GIV(instructionPointer) + 1 + : GIV(instructionPointer)); + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(lip, GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + + /* ensureMethodIsCogged: in makeBaseFrameFor: in + externalSetStackPageAndPointersForSuspendedContextOfProcess: + below may do a code compaction. Nil instructionPointer to avoid it getting pushed twice. */ + GIV(instructionPointer) = 0; + sched = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + oldProc = longAt((void *)((sched + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin recordContextSwitchFrom:to:in: */ + if ((recordEventTrace()) + && (!primTracePluginName)) { + /* begin recordTrace:source:thing:extra: */ + assert(sourceCode < ((1U << TraceTypeShift))); + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceContextSwitch) << TraceTypeShift))))) + sourceCode); + GIV(traceLog)[GIV(traceLogIndex) + 1] = oldProc; + GIV(traceLog)[GIV(traceLogIndex) + 2] = newProcOrNil; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l1; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer) + BytesPerWord); + /* end ensureFrameIsMarried:SP: */ +l1: + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, oldProc, activeContext)); + assert(isNonImmediate(oldProc)); + if (oopisGreaterThanOrEqualTo(oldProc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeContext & (tagMask())))) + && (oopisLessThan(activeContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(oldProc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(oldProc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((oldProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),activeContext); + if (!newProcOrNil) { + /* Two possibilities. One, there is at least one thread waiting to own the VM in which + case it should be activated. Two, there are no processes to run and so abort. This + is new in the MT VM, and only happens when the primitiveRelinquishProcessor has been + preempted. In that case the idle Process is not runnable and there is no Process to return to. + By setting the activeProcess to nilObject, any threads woken by the heartbeat don't actually + start running Smalltalk. This is then fixed when an AWOL thread comes back and restores its + previous state. */ + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ActiveProcessIndex, sched, GIV(nilObj))); + assert(isNonImmediate(sched)); + if (oopisGreaterThanOrEqualTo(sched, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(GIV(nilObj) & (tagMask())))) + && (oopisLessThan(GIV(nilObj), GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(sched + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(sched); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((sched + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord()))))),GIV(nilObj)); + if ((vmThread = willingVMThread())) { + if ((atomic_load((&((vmThread->state))))) == CTMWantingOwnership) { + returnToSchedulingLoopAndReleaseVMOrWakeThreadsource(vmThread, sourceCode); + } + } + + /* self error: 'scheduler could not find a runnable process' + relinquishing := true */ + returnToSchedulingLoopAndReleaseVMOrWakeThreadsource(null, sourceCode); + } + + /* Switch to the new process */ + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ActiveProcessIndex, sched, newProcOrNil)); + assert(isNonImmediate(sched)); + if (oopisGreaterThanOrEqualTo(sched, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(newProcOrNil & (tagMask())))) + && (oopisLessThan(newProcOrNil, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(sched + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(sched); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((sched + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord()))))),newProcOrNil); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newProcOrNil)) + && (!(isForwarded(newProcOrNil)))); + assert(validStorePointerUncheckedArgs(MyListIndex, newProcOrNil, GIV(nilObj))); + longAtput((void *)((newProcOrNil + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin externalSetStackPageAndPointersForSuspendedContextOfProcess: */ + newContext = longAt((void *)((newProcOrNil + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + assert(isContext(newContext)); + if (((((longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert(checkIsStillMarriedContextcurrentFP(newContext, GIV(framePointer))); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newProcOrNil)) + && (!(isForwarded(newProcOrNil)))); + assert(validStorePointerUncheckedArgs(SuspendedContextIndex, newProcOrNil, nilObject())); + longAtput((void *)((newProcOrNil + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),GIV(nilObj)); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(newContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((newContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + if (theFrame != ((thePage->headFP))) { + /* explicit assignment of suspendedContext can cause switch to interior frame. */ + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l3; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l3: + moveFramesInthroughtoPage(thePage, findFrameAboveinPage(theFrame, thePage), newPage); + + /* begin markStackPageLeastMostRecentlyUsed: */ + assert(newPage == ((GIV(mostRecentlyUsedPage)->nextPage))); + lastUsedPage = (newPage->nextPage); + while (((lastUsedPage->baseFP)) == 0) { + lastUsedPage = (lastUsedPage->nextPage); + } + if (((lastUsedPage->nextPage)) == newPage) { + goto l2; + } + (((newPage->prevPage))->nextPage = (newPage->nextPage)); + (((newPage->nextPage))->prevPage = (newPage->prevPage)); + (((lastUsedPage->prevPage))->nextPage = newPage); + (newPage->prevPage = (lastUsedPage->prevPage)); + (newPage->nextPage = lastUsedPage); + (lastUsedPage->prevPage = newPage); + assert(pageListIsWellFormed()); + /* end markStackPageLeastMostRecentlyUsed: */ +l2:; + } + assert(((thePage->headFP)) == theFrame); + } + else { + thePage = makeBaseFrameFor(newContext); + theFrame = (thePage->baseFP); + } + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + aMethodObj = longAt(GIV(framePointer) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + + /* Finally thread switch if required */ + threadSwitchIfNecessaryfrom(newProcOrNil, sourceCode); + return 0; +} + + +/* Set the maxWaitingPriority to at least minPriority on behalf + of a thread wanting to acquire the VM. If maxWaitingPriority + is increased, schedule a thread activation check asap. */ + + /* CoInterpreterMT>>#waitingPriorityIsAtLeast: */ +static NoDbgRegParms void +waitingPriorityIsAtLeast(sqInt minPriority) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int currentWaitingPriority; + sqInt didIncrease; + + currentWaitingPriority = atomic_load((&GIV(maxWaitingPriority))); + didIncrease = 0; + while (!((currentWaitingPriority >= minPriority) + || ((didIncrease = atomic_compare_exchange_strong((&GIV(maxWaitingPriority)), (¤tWaitingPriority), minPriority))))) { + } + if (didIncrease) { + assert(currentWaitingPriority < minPriority); + GIV(checkThreadActivation) = 1; + forceInterruptCheck(); + } +} + + /* CoInterpreterPrimitives>>#frameIsMarked: */ +static NoDbgRegParms int +frameIsMarked(sqInt theFPInt) +{ + sqInt methodField; + + methodField = longAt((void *)(theFPInt + FoxMethod)); + return ((((usqInt)methodField)) < (startOfMemory()) + ? (methodField & 4) != 0 + : ((longAt((void *)(theFPInt + FoxIFrameFlags))) & 2) != 0); +} + + /* CoInterpreterPrimitives>>#primitiveAllMethodsCompiledToMachineCode */ +#if VMInvestigations +EXPORT(int) +primitiveAllMethodsCompiledToMachineCode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayObj; + sqInt delta; + sqInt maxNumEntries; + sqInt nEntries; + + if (GIV(argumentCount)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + maxNumEntries = (numMethods()) + (16 /* (allocationUnit * 2) */ / BytesPerOop); + arrayObj = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), maxNumEntries); + if (!arrayObj) { + return -1; + } + nEntries = methodsCompiledToMachineCodeInto(arrayObj); + if (nEntries < maxNumEntries) { + /* begin shorten:toIndexableSize: */ + assert((nEntries >= 0) + && (nEntries < (lengthOf(arrayObj)))); + delta = doShortentoIndexableSize(arrayObj, nEntries); + assert((lengthOf(followMaybeForwarded(arrayObj))) == nEntries); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),arrayObj); + return 0; +} +#endif /* VMInvestigations */ + + /* CoInterpreterPrimitives>>#primitiveBenchmarkFollowForwardersInStackZone */ +#if VMInvestigations +EXPORT(void) +primitiveBenchmarkFollowForwardersInStackZone(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + usqLong then; + + /* begin primitiveReturnTimeTakenFor: */ + then = ioHighResClock(); + followForwardingPointersOfReceiversInStackZone(); + oop = positive64BitIntegerFor((ioHighResClock()) - then); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} +#endif /* VMInvestigations */ + + /* CoInterpreterPrimitives>>#primitiveBenchmarkFollowForwardersOfReceiverAndTemporariesInStackZone */ +#if VMInvestigations +EXPORT(void) +primitiveBenchmarkFollowForwardersOfReceiverAndTemporariesInStackZone(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + usqLong then; + + /* begin primitiveReturnTimeTakenFor: */ + then = ioHighResClock(); + followForwardingPointersOfReceiverAndTemporariesInStackZone(); + oop = positive64BitIntegerFor((ioHighResClock()) - then); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} +#endif /* VMInvestigations */ + + /* CoInterpreterPrimitives>>#primitiveBenchmarkJITReceiver */ +#if VMInvestigations +EXPORT(sqInt) +primitiveBenchmarkJITReceiver(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cloneMethod; + sqInt oop; + sqInt receiverMethod; + usqLong then; + + receiverMethod = longAt(GIV(stackPointer)); + if (!(/* isOopCompiledMethod: */ + ((!(receiverMethod & (tagMask())))) + && (((byteAt((void *)(receiverMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return (GIV(primFailCode) = PrimErrBadReceiver); + } + if (isFullBlockMethod(receiverMethod)) { + return (GIV(primFailCode) = PrimErrUnsupported); + } + cloneMethod = cloneObject(receiverMethod); + if (!cloneMethod) { + return (GIV(primFailCode) = PrimErrNoMemory); + } + + /* begin primitiveReturnTimeTakenFor: */ + then = ioHighResClock(); + if (!(cogselector(cloneMethod, maybeSelectorOfMethod(cloneMethod)))) { + return (GIV(primFailCode) = PrimErrOperationFailed); + } + freeCogMethod(cogMethodOf(cloneMethod)); + oop = positive64BitIntegerFor((ioHighResClock()) - then); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return 0; +} +#endif /* VMInvestigations */ + + /* CoInterpreterPrimitives>>#primitiveBenchmarkScavenge */ +#if VMInvestigations +EXPORT(void) +primitiveBenchmarkScavenge(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + usqLong then; + + /* begin primitiveReturnTimeTakenFor: */ + then = ioHighResClock(); + scavengingGCTenuringIf(TenureByAge); + (((usqInt)(((((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes())) < 0) ? 0 : ((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes())))) << 3) | 1); + oop = positive64BitIntegerFor((ioHighResClock()) - then); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} +#endif /* VMInvestigations */ + + +/* This is optional old obsolete stuff tedious to implement in Cog because of + the need to map from machine code pc to bytecode pc. So for now just fail. */ + + /* CoInterpreterPrimitives>>#primitiveClosureCopyWithCopiedValues */ +static void +primitiveClosureCopyWithCopiedValues(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } +} + + +/* Answer an Array of: + - the address of the CogMethod of the receiver, + - the CogMethod of the receiver as a ByteArray, and + - a sequence of relative pc/value/object triples for the imported + references embedded in the CogMethod. + Fail on V3. Fail if the receiver is not a cogged method. Fail if there is + insufficient memory for the result. + If there is an argument it is a boolean that if true includes more + detailed Closed PIC information. */ + + /* CoInterpreterPrimitives>>#primitiveCogitMethod */ +#if VMInvestigations +EXPORT(sqInt) +primitiveCogitMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt code; + CogMethod *cogMethod; + sqInt cogMethodData; + usqInt data; + sqInt detailCPICs; + sqInt methodHeader; + sqInt methodReceiver; + usqInt newObj; + usqInt numBytes; + sqInt valuePointer; + + if (GIV(argumentCount) > 0) { + if (GIV(argumentCount) > 1) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + if (!((detailCPICs = (longAt(GIV(stackPointer))) == GIV(trueObj)))) { + if (!((longAt(GIV(stackPointer))) == GIV(falseObj))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + } + } + else { + detailCPICs = 0; + } + methodReceiver = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + if (!((/* isOopCompiledMethod: */ + ((!(methodReceiver & (tagMask())))) + && (((byteAt((void *)(methodReceiver + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (methodHasCogMethod(methodReceiver)))) { + return (GIV(primFailCode) = PrimErrBadReceiver); + } + + /* begin eeInstantiateAnySmallClassIndex:format:numSlots: */ + assert((classAtIndex(ClassArrayCompactIndex)) != GIV(nilObj)); + assert((arrayFormat()) == (instSpecOfClass(classAtIndex(ClassArrayCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (3 * BytesPerOop); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + data = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) 3)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + GIV(freeStart) += numBytes; + data = newObj; + /* end eeInstantiateAnySmallClassIndex:format:numSlots: */ +l1: + + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((methodReceiver + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + valuePointer = positive64BitIntegerFor(((usqInt)cogMethod)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(data)) + && (!(isForwarded(data)))); + assert(validStorePointerUncheckedArgs(0, data, valuePointer)); + longAtput((void *)((data + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + if ((cogMethodData = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteArray) << (shiftForWord())))))), (cogMethod->blockSize)))) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(data)) + && (!(isForwarded(data)))); + assert(validStorePointerUncheckedArgs(1, data, cogMethodData)); + longAtput((void *)((data + BaseHeaderSize) + (1U << (shiftForWord()))),cogMethodData); + memcpy(firstIndexableField(cogMethodData), cogMethod, (cogMethod->blockSize)); + } + else { + return (GIV(primFailCode) = PrimErrNoMemory); + } + code = cogMethodImportsintodetailCPICs(cogMethod, data, detailCPICs); + if (code) { + return (GIV(primFailCode) = code); + } + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),data); + return 0; +} +#endif /* VMInvestigations */ + + +/* Answer an array of string,value pairs for all static symbols needed for + deferred disassembly. + */ + + /* CoInterpreterPrimitives>>#primitiveCogStaticSymbols */ +#if VMInvestigations +EXPORT(sqInt) +primitiveCogStaticSymbols(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *abiNameOrNil; + sqInt i; + sqInt symbols; + sqInt valuePointer; + + symbols = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 54); + valuePointer = stringForCString("wordSize"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(0, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(1, symbols, (((usqInt)BytesPerWord << 3) | 1))); + longAtput((void *)((symbols + BaseHeaderSize) + (1U << (shiftForWord()))),(((usqInt)BytesPerWord << 3) | 1)); + valuePointer = stringForCString("ISA"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(2, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (2U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString(isaName()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(3, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (3U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("ABI"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(4, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (4U << (shiftForWord()))),valuePointer); + valuePointer = ((abiNameOrNil = abiName()) + ? stringForCString(abiNameOrNil) + : GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(5, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (5U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("MULTIPLEBYTECODESETS"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(6, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (6U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(7, symbols, GIV(trueObj))); + longAtput((void *)((symbols + BaseHeaderSize) + (7U << (shiftForWord()))),GIV(trueObj)); + valuePointer = stringForCString("varBaseAddress"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(8, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (8U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(computeGoodVarBaseAddress()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(9, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (9U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("argumentCount"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(10, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (10U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(argumentCount))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(11, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (11U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("CFramePointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(12, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (12U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(CFramePointer))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(13, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (13U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("CStackPointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(14, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (14U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(CStackPointer))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(15, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (15U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("framePointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(16, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (16U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(framePointer))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(17, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (17U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("instructionPointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(18, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (18U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(instructionPointer))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(19, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (19U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("interpret"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(20, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (20U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)interpret)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(21, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (21U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("methodCache"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(22, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (22U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)GIV(methodCache))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(23, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (23U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("newMethod"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(24, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (24U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(newMethod))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(25, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (25U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("nextProfileTick"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(26, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (26U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(nextProfileTick))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(27, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (27U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("primFailCode"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(28, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (28U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(primFailCode))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(29, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (29U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("primitiveFunctionPointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(30, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (30U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&primitiveFunctionPointer)))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(0x1F, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (0x1FU << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("primTraceLog"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(32, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (32U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)GIV(primTraceLog))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(33, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (33U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("primTraceLogIndex"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(34, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (34U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(primTraceLogIndex))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(35, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (35U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("stackLimit"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(36, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (36U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(stackLimit))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(37, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (37U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("stackPointer"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(38, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (38U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)((&GIV(stackPointer))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(39, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (39U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("nil"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(40, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (40U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(41, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (41U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("false"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(42, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (42U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(falseObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(43, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (43U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("true"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(44, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (44U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(trueObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(45, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (45U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("classTableRootObj"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(46, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (46U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(hiddenRootsObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(47, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (47U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("methodZoneBase"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(48, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (48U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(getMethodZoneBase()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(49, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (49U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("checkForAndFollowForwardedPrimitiveState"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(50, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (50U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)checkForAndFollowForwardedPrimitiveState)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(51, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (51U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("ceCheckAndMaybeRetryPrimitive:"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(52, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (52U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(((usqInt)ceCheckAndMaybeRetryPrimitive)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(symbols)) + && (!(isForwarded(symbols)))); + assert(validStorePointerUncheckedArgs(53, symbols, valuePointer)); + longAtput((void *)((symbols + BaseHeaderSize) + (53U << (shiftForWord()))),valuePointer); + + /* these are needed because they are not annotated and hence not identified as exports by primitiveCogitMethod + this is needed to distinguish linked/unlinked sends + these two show up in Spur primitives, but maybe absolute calls, which won't show up in exports */ + for (i = 2; i <= 53; i += 1) { + if (!(longAt((void *)((symbols + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + return (GIV(primFailCode) = PrimErrNoMemory); + } + } + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),symbols); + return 0; +} +#endif /* VMInvestigations */ + + +/* Answer the contents of the code zone as an array of pair-wise element, + address in ascending + address order. Answer a string for a runtime routine or abstract label + (beginning, end, etc), + a CompiledMethod for a CMMethod, or a selector (presumably a Symbol) for a + PIC. If there is an argument and it is true, then collect inner + information about the CogMethod. */ + + /* CoInterpreterPrimitives>>#primitiveCollectCogCodeConstituents */ +static void +primitiveCollectCogCodeConstituents(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt constituents; + sqInt withDetails; + + if (GIV(argumentCount)) { + withDetails = longAt(GIV(stackPointer)); + if (!((withDetails == GIV(trueObj)) + || (withDetails == GIV(falseObj)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + withDetails = withDetails == GIV(trueObj); + } + else { + withDetails = 0; + } + constituents = cogCodeConstituents(withDetails); + if (!constituents) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + return; + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),constituents); +} + + +/* Lift the veil from a context and answer an integer describing its interior + state. Used for e.g. VM tests so they can verify they're testing what they + think they're testing. + 0 implies a vanilla heap context. + Bit 0 = is or was married to a frame + Bit 1 = is still married to a frame + Bit 2 = frame is executing machine code + Bit 3 = has machine code pc (as opposed to nil or a bytecode pc) + Bit 4 = method is currently compiled to machine code */ + + /* CoInterpreterPrimitives>>#primitiveContextXray */ +#if VMInvestigations +EXPORT(void) +primitiveContextXray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt context; + sqInt flags; + sqInt pc; + sqInt senderOop; + char *theFP; + sqInt theMethod; + + context = longAt(GIV(stackPointer)); + pc = longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + if (((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (checkIsStillMarriedContextcurrentFP(context, GIV(framePointer))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + flags = 7; + } + else { + flags = 3; + } + } + else { + flags = 1; + } + } + else { + flags = 0; + } + if (((((pc) & 7) == 1)) + && (((pc >> 3)) < 0)) { + flags = flags | 8; + } + theMethod = longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + if (/* maybeMethodHasCogMethod: */ + ((!(theMethod & (tagMask())))) + && ((((byteAt((void *)(theMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)((theMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))))))) { + flags = flags | 16; + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),(((usqInt)flags << 3) | 1)); +} +#endif /* VMInvestigations */ + + +/* Attempt to enter a CriticalSection/Mutex. If not owned, set the owner to + the current + process and answer false. If owned by the current process answer true. + Otherwise suspend the process. Answer if the receiver is owned by the + current process. + For simulation, if there is an argument it is taken to be the effective + activeProcess (see Process>>effectiveProcess). */ + + /* CoInterpreterPrimitives>>#primitiveEnterCriticalSection */ +static void +primitiveEnterCriticalSection(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + sqInt criticalSection; + int inInterpreter; + sqInt objOop; + sqInt owningProcess; + sqInt owningProcessIndex; + + if (GIV(argumentCount) > 0) { + /* rcvr */ + criticalSection = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + activeProc = longAt(GIV(stackPointer)); + if (/* isOopForwarded: */ + ((!(activeProc & (tagMask())))) + && ((!((longAt((void *)(activeProc))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + } + else { + /* rcvr */ + criticalSection = longAt(GIV(stackPointer)); + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + } + + /* CriticalSections are laid out like Semaphores */ + owningProcessIndex = ExcessSignalsIndex; + owningProcess = longAt((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord())))))); + if (owningProcess == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(owningProcessIndex, criticalSection, activeProc)); + assert(isNonImmediate(criticalSection)); + if (oopisGreaterThanOrEqualTo(criticalSection, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeProc & (tagMask())))) + && (oopisLessThan(activeProc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(criticalSection + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(criticalSection); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord()))))),activeProc); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(falseObj)); + return; + } + if (owningProcess == activeProc) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(trueObj)); + return; + } + + /* Arrange to answer false (unowned) when the process is resumed. */ + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(falseObj)); + + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + addLastLinktoList(activeProc, criticalSection); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSEnterCriticalSection); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); +} + + +/* Exit the critical section. + This may change the active process as a result. */ + + /* CoInterpreterPrimitives>>#primitiveExitCriticalSection */ +static void +primitiveExitCriticalSection(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt criticalSection; + int inInterpreter; + sqInt owningProcess; + sqInt owningProcessIndex; + + /* rcvr */ + criticalSection = longAt(GIV(stackPointer)); + + /* CriticalSections are laid out like Semaphores */ + owningProcessIndex = ExcessSignalsIndex; + + /* begin isEmptyList: */ + assert(!(isForwarded(criticalSection))); + if ((longAt((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(criticalSection)) + && (!(isForwarded(criticalSection)))); + assert(validStorePointerUncheckedArgs(owningProcessIndex, criticalSection, GIV(nilObj))); + longAtput((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + owningProcess = removeFirstLinkOfList(criticalSection); + + /* store check unnecessary because aSemaphore referred to owningProcess + via its FirstLinkIndex slot before owningProcess was removed. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(criticalSection)) + && (!(isForwarded(criticalSection)))); + assert(validStorePointerUncheckedArgs(owningProcessIndex, criticalSection, owningProcess)); + longAtput((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord()))))),owningProcess); + + /* Note that resume: isn't fair; it won't suspend the active process. + For fairness we must do the equivalent of a primitiveYield, but that + may break old code, so we stick with unfair resume:. */ + if (resumepreemptedYieldingIffrom(owningProcess, (!(imageHeaderFlags & 16)), CSExitCriticalSection)) { + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } + } +} + + +/* No-op for measuring performance of primitiveExternalCall/#117 linkage */ + + /* CoInterpreterPrimitives>>#primitiveExternalCallNoOp */ +EXPORT(void) +primitiveExternalCallNoOp(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* No-op for measuring performance of FastCPrimitiveFlag linkage */ + + /* CoInterpreterPrimitives>>#primitiveFastCNoOp */ +EXPORT(void) +primitiveFastCNoOp(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* No-op for measuring performance of + FastCPrimitiveFlag+FastCPrimitiveAlignForFloatsFlag linkage + */ + + /* CoInterpreterPrimitives>>#primitiveFastCNoOpAlignedForFloats */ +EXPORT(void) +primitiveFastCNoOpAlignedForFloats(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* The receiver is a compiledMethod. Clear all entries in the method lookup + cache that refer to this method, presumably because it has been redefined, + overridden or removed. + */ +/* The receiver is a compiledMethod. Clear all entries in the method lookup + cache that + refer to this method, presumably because it has been redefined, overridden + or removed. + Override to flush appropriate machine code caches also. */ + + /* CoInterpreterPrimitives>>#primitiveFlushCacheByMethod */ +static void +primitiveFlushCacheByMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + flushMethodCacheForMethod(longAt(GIV(stackPointer))); + unlinkSendsToandFreeIf(longAt(GIV(stackPointer)), 0); +} + + +/* The receiver is a message selector. Clear all entries in the method lookup + cache with this selector, presumably because an associated method has been + redefined. Override to also flush machine code caches. */ + + /* CoInterpreterPrimitives>>#primitiveFlushCacheBySelector */ +static void +primitiveFlushCacheBySelector(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt probe; + sqInt selector; + + selector = longAt(GIV(stackPointer)); + probe = 0; + for (i = 1; i <= MethodCacheEntries; i += 1) { + if ((GIV(methodCache)[probe + MethodCacheSelector]) == selector) { + GIV(methodCache)[probe + MethodCacheSelector] = 0; + } + probe += MethodCacheEntrySize; + } + unlinkSendsOfisMNUSelector(selector, selector == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))))); +} + + /* CoInterpreterPrimitives>>#primitiveFunctionPointerAddress */ +usqInt +primitiveFunctionPointerAddress(void) +{ + return ((usqInt)((&primitiveFunctionPointer))); +} + + +/* Primitive. Install the semaphore to be used for collecting long-running + primitives, + or nil if no semaphore should be used. */ + + /* CoInterpreterPrimitives>>#primitiveLongRunningPrimitiveSemaphore */ +#if LRPCheck +EXPORT(sqInt) +primitiveLongRunningPrimitiveSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + int flushState; + sqInt sema; + + if (GIV(argumentCount) != 1) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + sema = longAt(GIV(stackPointer)); + if (sema == GIV(nilObj)) { + flushState = GIV(longRunningPrimitiveCheckSemaphore); + GIV(longRunningPrimitiveCheckSemaphore) = null; + } + else { + flushState = !GIV(longRunningPrimitiveCheckSemaphore); + if (!(/* isSemaphoreOop: */ + ((!(sema & (tagMask())))) + && (((longAt((void *)(sema))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + GIV(longRunningPrimitiveCheckSemaphore) = sema; + } + + /* If we've switched checking on or off we must void machine code + (and machine code pcs in contexts) since we will start or stop setting + newMethod in machine code primitive invocations, and so generate + slightly different code from here on in. */ + if (flushState) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + activeContext = voidVMStateForSnapshotFlushingExternalPrimitivesIf(0); + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + assert((((stackValue(0)) == (nilObject())) + && (!GIV(longRunningPrimitiveCheckSemaphore))) + || (((stackValue(0)) == GIV(longRunningPrimitiveCheckSemaphore)) + && (isSemaphoreOop(sema)))); + } + voidLongRunningPrimitive("install"); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + if (flushState) { + ceInvokeInterpret(); + } + return 0; +} +#endif /* LRPCheck */ + + +/* Answer if a method is currently jitted. */ + + /* CoInterpreterPrimitives>>#primitiveMethodIsCogged */ +#if VMInvestigations +EXPORT(sqInt) +primitiveMethodIsCogged(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodOop; + sqInt boolean; + sqInt methodHeader; + char *sp; + + if (GIV(argumentCount)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + if (!(/* isOopCompiledMethod: */ + ((!((longAt(GIV(stackPointer))) & (tagMask())))) + && (((byteAt((void *)((longAt(GIV(stackPointer))) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return (GIV(primFailCode) = PrimErrBadReceiver); + } + aMethodOop = longAt(GIV(stackPointer)); + + /* begin methodHasCogMethod: */ + assert(isNonImmediate(aMethodOop)); + methodHeader = longAt((void *)((aMethodOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + boolean = (!(methodHeader & (smallIntegerTag()))); + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (boolean + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return 0; +} +#endif /* VMInvestigations */ + + /* CoInterpreterPrimitives>>#primitiveMethodPCData */ +EXPORT(sqInt) +primitiveMethodPCData(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cm; + CogMethod *cogMethod; + sqInt data; + sqInt dataSqInt; + sqInt delta; + sqInt methodHeader; + sqInt methodReceiver; + sqInt nEntries; + sqInt nSlots; + + if (GIV(argumentCount)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + methodReceiver = longAt(GIV(stackPointer)); + data = 0; + if (methodHasCogMethod(methodReceiver)) { + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((methodReceiver + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + + /* begin pcDataFor: */ + cm = (cogMethod->methodObject); + nSlots = (((byteSizeOf(cm)) - ((((/* begin literalCountOfMethodHeader: */ + assert((((((cogMethod->methodHeader))) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((((cogMethod->methodHeader)) >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop)) * 2) + (16 /* (allocationUnit * 2) */ / BytesPerOop); + dataSqInt = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), nSlots); + if (!dataSqInt) { + data = -1; + goto l1; + } + nEntries = mapPCDataForinto(cogMethod, dataSqInt); + if (!nEntries) { + data = 0; + goto l1; + } + if (nEntries < nSlots) { + /* begin shorten:toIndexableSize: */ + assert((nEntries >= 0) + && (nEntries < (lengthOf(dataSqInt)))); + delta = doShortentoIndexableSize(dataSqInt, nEntries); + assert((lengthOf(followMaybeForwarded(dataSqInt))) == nEntries); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + } + data = dataSqInt; + /* end pcDataFor: */ +l1: + if (data == -1) { + return (GIV(primFailCode) = PrimErrNoMemory); + } + } + if (!data) { + data = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 0); + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),data); + return 0; +} + + +/* Lift the veil from a method and answer an integer describing the interior + state of its machine code. + Used for e.g. VM tests so they can verify they're testing what they think + they're testing. + 0 implies a vanilla method. + Bit 0 = method might be compiled to machine code + Bit 1 = method is currently compiled to machine code + Bit 2 = is compiled frameless. + Bit 3 = method refers to young object. + Bit 4 = method too big to be jitted (more than 64k of code, or needs more + than 1.5Mb of stack space to compile) + Bit 5 = method contains unknown/unjittable bytecode + Bit 6 = method should not be jitted because it contains a primitive not to + be called from machine code (unused) */ + + /* CoInterpreterPrimitives>>#primitiveMethodXray */ +static void +primitiveMethodXray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt alreadyCogged; + CogMethod *cogMethod; + int flags; + sqInt header; + sqInt methodHeader; + sqInt methodObj; + + methodObj = longAt(GIV(stackPointer)); + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + alreadyCogged = /* maybeMethodHasCogMethod: */ + ((!((longAt(GIV(stackPointer))) & (tagMask())))) + && ((((byteAt((void *)((longAt(GIV(stackPointer))) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))))))); + flags = 1; + if (!alreadyCogged) { + cogMethod = cogselector(longAt(GIV(stackPointer)), GIV(nilObj)); + if ((cogMethod == null) + && (GIV(cogCompiledCodeCompactionCalledFor))) { + commenceCogCompiledCodeCompaction(); + cogMethod = cogselector(longAt(GIV(stackPointer)), GIV(nilObj)); + } + switch (((sqInt)cogMethod)) { + case MethodTooBig: + flags = 17; + break; + case EncounteredUnknownBytecode: + flags = 33; + break; + case ShouldNotJIT: + flags = 65; + break; + default: + assert(!(((((((sqInt)cogMethod)) >= MaxNegativeErrorCode) && ((((sqInt)cogMethod)) <= NotFullyInitialized))))); + } + } + if ((flags == 1) + && (/* maybeMethodHasCogMethod: */ + ((!((longAt(GIV(stackPointer))) & (tagMask())))) + && ((((byteAt((void *)((longAt(GIV(stackPointer))) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && (isCogMethodReference(longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))))))))) { + /* begin cogMethodOf: */ + methodHeader = longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + flags = ((cogMethod->stackCheckOffset) + ? 3 + : 7); + if ((cogMethod->cmRefersToYoung)) { + flags += 8; + } + if (!alreadyCogged) { + freeCogMethod(cogMethod); + } + } + } + else { + flags = 0; + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),(((usqInt)flags << 3) | 1)); +} + + /* CoInterpreterPrimitives>>#primitiveMinimumUnusedHeadroom */ +EXPORT(void) +primitiveMinimumUnusedHeadroom(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integer; + + integer = minimumUnusedHeadroom(); + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); +} + + +/* Defined for CompiledMethods only */ + + /* CoInterpreterPrimitives>>#primitiveObjectAt */ +static void +primitiveObjectAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt integerPointer; + sqInt oop; + sqInt rawHeader; + sqInt realHeader; + sqInt thisReceiver; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + index = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + index = 0; + } + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + thisReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rawHeader = longAt((void *)((thisReceiver + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + realHeader = ((!(rawHeader & (smallIntegerTag()))) + ? ((((CogMethod *) rawHeader))->methodHeader) + : rawHeader); + if (!((index > 0) + && (index <= (((/* begin literalCountOfMethodHeader: */ + assert((((realHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((realHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + oop = (index == 1 + ? realHeader + : longAt((void *)((thisReceiver + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oop); +} + + +/* Store a literal into a CompiledMethod at the given index. Defined for + CompiledMethods only. + We assume that if the user is using this on active code then they will use + primitiveVoidVMStateForMethod to discard the machine code as required. */ + + /* CoInterpreterPrimitives>>#primitiveObjectAtPut */ +static void +primitiveObjectAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fieldIndex; + sqInt index; + sqInt newValue; + sqInt rawHeader; + sqInt realHeader; + sqInt thisReceiver; + + newValue = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((!(index & (smallIntegerTag())))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + index = (index >> 3); + thisReceiver = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(thisReceiver + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + return; + } + rawHeader = longAt((void *)((thisReceiver + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + realHeader = ((!(rawHeader & (smallIntegerTag()))) + ? ((((CogMethod *) rawHeader))->methodHeader) + : rawHeader); + if (!((index > 0) + && (index <= (((/* begin literalCountOfMethodHeader: */ + assert((((realHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((realHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (index == 1) { + if (((!(newValue & (smallIntegerTag())))) + || (((/* begin literalCountOfMethodHeader: */ + assert((((newValue) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((newValue >> 3)) & AlternateHeaderNumLiteralsMask)) != ((assert((((realHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((realHeader >> 3)) & AlternateHeaderNumLiteralsMask)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* begin isCogMethodReference: */ + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + ((((CogMethod *) rawHeader))->methodHeader = newValue); + } + else { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(thisReceiver)) + && (!(isForwarded(thisReceiver)))); + assert(validStorePointerUncheckedArgs(0, thisReceiver, newValue)); + longAtput((void *)((thisReceiver + BaseHeaderSize) + (0U << (shiftForWord()))),newValue); + } + } + else { + fieldIndex = index - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, thisReceiver, newValue)); + assert(isNonImmediate(thisReceiver)); + if (oopisGreaterThanOrEqualTo(thisReceiver, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(newValue & (tagMask())))) + && (oopisLessThan(newValue, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(thisReceiver + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(thisReceiver); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((thisReceiver + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),newValue); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),newValue); +} + + +/* Primitive. Install the semaphore to be used for profiling, + or nil if no semaphore should be used. + See also primitiveProfileStart. */ + + /* CoInterpreterPrimitives>>#primitiveProfileSemaphore */ +EXPORT(sqInt) +primitiveProfileSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt address; + sqInt classIndex; + int flushState; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt sema; + sqInt senderOop; + sqInt startObject; + char *theFP; + char *theFrame; + StackPage *thePage; + sqInt top; + + sema = longAt(GIV(stackPointer)); + if (sema == GIV(nilObj)) { + flushState = GIV(profileSemaphore); + GIV(profileSemaphore) = null; + + /* begin zeroNextProfileTick */ + GIV(nextProfileTick) = 0; + } + else { + flushState = !GIV(profileSemaphore); + if (!(/* isSemaphoreOop: */ + ((!(sema & (tagMask())))) + && (((longAt((void *)(sema))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + GIV(profileSemaphore) = sema; + } + GIV(profileProcess) = (GIV(profileMethod) = null); + + /* If we've switched profiling on or off we must void machine code + (and machine code pcs in contexts) since we will start or stop + testing the profile clock in machine code primitive invocations, + and so generate slightly different code from here on in. */ + if (flushState) { + /* begin mapActivationsOfRealPrimitivesToBytecodePCsAndFreeCoggedPrimitiveMethods */ + activeContext = divorceAllFramesSuchThat(isMachineCodeFrameForRealNonProfilingPrimitive); + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin mapToBytecodePCIfActivationOfRealNonProfilingPrimitive: */ + assert(isContextNonImm(obj)); + if (isRealNonProfilingPrimitiveMethod(longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))))) { + if (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (isWidowedContext(obj)) { + goto l2; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + externalDivorceFrameandContext(theFP, obj); + } + } + ensureContextHasBytecodePC(obj); + } + /* end mapToBytecodePCIfActivationOfRealNonProfilingPrimitive: */ +l2:; + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + unlinkSendsToMethodsSuchThatAndFreeIf(cogMethodHasRealNonProfilingPrimitive, 1); + + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + + /* If flushing led to divorce continue in the interpreter. */ + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + ceInvokeInterpret(); + } + + /* If not, work out where we are and continue */ + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + assert(((thePage->headFP)) == theFrame); + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + else { + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + return 0; +} + + +/* Put this process on the scheduler's lists thus allowing it to proceed next + time there is + a chance for processes of it's priority level. It must go to the back of + its run queue so + as not to preempt any already running processes at this level. If the + process's priority + is higher than the current process, preempt the current process. */ + + /* CoInterpreterPrimitives>>#primitiveResume */ +static void +primitiveResume(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + int inInterpreter; + sqInt proc; + + /* rcvr */ + /* Alas in Spur we need a read barrier */ + proc = longAt(GIV(stackPointer)); + + /* begin followObjField:ofObject: */ + ctxt = longAt((void *)((proc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + assert(isNonImmediate(ctxt)); + if ((!((longAt((void *)(ctxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + ctxt = fixFollowedFieldofObjectwithInitialValue(SuspendedContextIndex, proc, ctxt); + } + if (!(/* isContext: */ + ((!(ctxt & (tagMask())))) + && (((longAt((void *)(ctxt))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* We're about to switch process, either to an interpreted frame or a + machine code frame. To know whether to return or enter machine code + we have to know from whence we came. We could have come from the + interpreter, either directly or via a machine code primitive. We could have + come from machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + if (resumepreemptedYieldingIffrom(proc, (!(imageHeaderFlags & 16)), CSResume)) { + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } +} + + +/* Assign the priority of the receiver based on the argument, performing any + necessary rescheduling. + There are three main cases: + the active process + a quiesent/runnable process + a suspended/waiting process + There are scheduling implications only for the first two cases (the two + forms of runnable processes). */ + + /* CoInterpreterPrimitives>>#primitiveSetPriority */ +static void +primitiveSetPriority(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activePriority; + sqInt existingPriority; + int inInterpreter; + sqInt newPriority; + usqInt numSlots; + sqInt oop; + sqInt processList; + sqInt processLists; + sqInt receiverProcess; + sqInt scheduler; + sqInt theActiveProcess; + + scheduler = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + processLists = longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + receiverProcess = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + existingPriority = longAt((void *)((receiverProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + + /* Setting the priority when it is nil is simply instance initialization (typically from Process class>>#forContext:priority:). + If priority has any other non-integer value then there is something wrong and trhe primitive should fail. */ + if (!(((((existingPriority) & 7) == 1)) + || (existingPriority == GIV(nilObj)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + newPriority = longAt(GIV(stackPointer)); + if (!(((((newPriority) & 7) == 1)) + && (((((newPriority = (newPriority >> 3))) >= 1) && (newPriority <= ((/* begin numSlotsOf: */ + assert((classIndexOf(processLists)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(processLists + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(processLists - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + theActiveProcess = longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((theActiveProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + activePriority = (oop >> 3); + + /* assign priority for all cases. return receiver for all cases */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(receiverProcess)) + && (!(isForwarded(receiverProcess)))); + assert(validStorePointerUncheckedArgs(PriorityIndex, receiverProcess, (((usqInt)newPriority << 3) | 1))); + longAtput((void *)((receiverProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord()))))),(((usqInt)newPriority << 3) | 1)); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + + /* Setting the priority when it is nil is simply instance initialization */ + if (!((((existingPriority) & 7) == 1))) { + assert(nilObject() == existingPriority); + return; + } + existingPriority = (existingPriority >> 3); + + /* begin highestRunnablePriorityIsAtLeast: */ + if (GIV(highestRunnableProcessPriority)) { + GIV(highestRunnableProcessPriority) = ((GIV(highestRunnableProcessPriority) < newPriority) ? newPriority : GIV(highestRunnableProcessPriority)); + } + + /* We may switch process, either to an interpreted frame or a machine code frame. To + know whether to return or enter machine code we have to know from whence we came. + We could have come from the interpreter, either directly or via a machine code primitive. + We could have come from machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + + /* If setting the priority of the active process then setting the priority + higher or same is a noop, but setting it lower is like yielding. + But to avoid putting the process to sleep (creating its context etc) + first check if it is still the runnable process on lowering its priority. */ + if (theActiveProcess == receiverProcess) { + if ((newPriority < activePriority) + && ((findHighestPriority()) > newPriority)) { + processList = longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((newPriority - 1)) << (shiftForWord())))))); + addLastLinktoList(theActiveProcess, processList); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSSetPriority); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } + return; + } + + /* If receiverProcess's list is the quiesent list at its priority then it is quiescent/runnable, + otherwise receiverProcess is either suspended (myList = nil) or waiting on a condition + variable. If receiverProcess is quiescent then if setting the priority to less or equal to + the active priority then this is a noop, but if setting higher then effectively the active + process yields to the receiverProcess. */ + if (((longAt((void *)((receiverProcess + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))))) == (longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((existingPriority - 1)) << (shiftForWord())))))))) + && (newPriority > activePriority)) { + processList = longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((activePriority - 1)) << (shiftForWord())))))); + addLastLinktoList(theActiveProcess, processList); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSSetPriority); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } + return; +} + + +/* Synchronously signal the semaphore. + This may change the active process as a result. */ +/* We may be about to switch process, either to an interpreted frame or a + machine code frame. To know whether to return or enter machine code + we have to know from whence we came. We could have come from the + interpreter, either directly or via a machine code primitive. We could + have come from machine code. The instructionPointer tells us where from: */ + + /* CoInterpreterPrimitives>>#primitiveSignal */ +static void +primitiveSignal(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int inInterpreter; + + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + if (synchronousSignal(longAt(GIV(stackPointer)))) { + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } +} + + +/* Save a normal snapshot under the same name as it was loaded + unless it has been renamed by the last primitiveImageName. + Note that when executed this primitive answers false, but when the + resulting image is run afresh, the primitive answers true. + + Override to jump to the interpreter because the machine code zone is now + void. + */ + + /* CoInterpreterPrimitives>>#primitiveSnapshot */ +static void +primitiveSnapshot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + snapshot(0); + if ((longAt(GIV(framePointer) + FoxMethod)) == GIV(newMethod)) { + /* snapshot: has reached the end and built a frame. + In the JIT we need to back-up the pc before reentering the interpreter. */ + GIV(instructionPointer) -= 1; + } + ceInvokeInterpret(); +} + + +/* Save an embedded snapshot. + Note that when executed this primitive answers false, but when the + resulting image is run afresh, the primitive answers true. + + Override to jump to the interpreter because the machine code zone is now + void. + */ + + /* CoInterpreterPrimitives>>#primitiveSnapshotEmbedded */ +static void +primitiveSnapshotEmbedded(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + snapshot(1); + if ((longAt(GIV(framePointer) + FoxMethod)) == GIV(newMethod)) { + /* snapshot: has reached the end and built a frame. + In the JIT we need to back-up the pc before reentering the interpreter. */ + GIV(instructionPointer) -= 1; + } + ceInvokeInterpret(); +} + + +/* Primitive #88. Suspend the receiver, aProcess, such that it can be + executed again + by sending #resume. If the given process is not the active process, take + it off + its corresponding list. The primitive returns the list the receiver was + previously on. + c.f. primitiveSuspendBackingUpV1,#568 & primitiveSuspendBackingUpV2,#578 */ + + /* CoInterpreterPrimitives>>#primitiveSuspend */ +static void +primitiveSuspend(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int inInterpreter; + sqInt myContext; + sqInt myList; + usqInt numSlots; + sqInt ok; + sqInt process; + + process = longAt(GIV(stackPointer)); + if (process == (activeProcess())) { + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(nilObj)); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSSuspend); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + return; + } + myList = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord())))))); + myContext = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if (!((/* isPointers: */ + ((!(myList & (tagMask())))) + && (((byteAt((void *)(myList + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(myList)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(myList + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(myList - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > LastLinkIndex) + && ((((longAt((void *)(myContext))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((/* begin isResumableContext: */ + assert(isContext(myContext)), + ((((longAt((void *)((myContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + ok = removeProcessfromList(process, myList); + if (!ok) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrOperationFailed; + return; + } + + /* stackTopPut: */ + longAtput(GIV(stackPointer),myList); +} + + +/* Primitive #568. Suspend the receiver, aProcess, such that it can be + executed again + by sending #resume. If the given process is not the active process, take + it off + its corresponding list. If the list was not its run queue assume it was on + some condition variable (Semaphore, Mutex) and back up its pc to the send + that invoked the wait state the process entered. Hence when the process + resumes it will reenter the wait state. Answer the list the receiver was + previously on, + unless it was the activeProcess, in which case answer nil. + c.f. primitiveSuspend,#88 & primitiveSuspendBackingUpV2,#578 */ + + /* CoInterpreterPrimitives>>#primitiveSuspendBackingUpV1 */ +static void +primitiveSuspendBackingUpV1(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int inInterpreter; + sqInt myContext; + sqInt myList; + usqInt numSlots; + sqInt ok; + sqInt process; + + process = longAt(GIV(stackPointer)); + if (process == (activeProcess())) { + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(nilObj)); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSSuspend); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + return; + } + myList = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord())))))); + myContext = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if (!((/* isPointers: */ + ((!(myList & (tagMask())))) + && (((byteAt((void *)(myList + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(myList)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(myList + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(myList - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > LastLinkIndex) + && ((((longAt((void *)(myContext))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((/* begin isResumableContext: */ + assert(isContext(myContext)), + ((((longAt((void *)((myContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + ok = removeProcessfromList(process, myList); + if (!ok) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrOperationFailed; + return; + } + if (((longAt((void *)(myList))) & (classIndexMask())) != GIV(classLinkedListClassTag)) { + backupContexttoBlockingSendTo(myContext, myList); + } + + /* stackTopPut: */ + longAtput(GIV(stackPointer),myList); +} + + +/* Primitive #578. Suspend the receiver, aProcess, such that it can be + executed again + by sending #resume. If the given process is not the active process, take + it off + its corresponding list. If the list was not its run queue assume it was on + some condition variable (Semaphore, Mutex) and back up its pc to the send + that invoked the wait state the process entered. Hence when the process + resumes it will reenter the wait state. Answer the list the receiver was + previously on iff + it was not active and not blocked, otherwise answer nil. + c.f. primitiveSuspend,#88 & primitiveSuspendBackingUpV1,#568, + which always answer the list the process was on, even if blocked. */ + + /* CoInterpreterPrimitives>>#primitiveSuspendBackingUpV2 */ +EXPORT(void) +primitiveSuspendBackingUpV2(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int inInterpreter; + sqInt myContext; + sqInt myList; + usqInt numSlots; + sqInt ok; + sqInt process; + + process = longAt(GIV(stackPointer)); + if (process == (activeProcess())) { + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(nilObj)); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSSuspend); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + return; + } + myList = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord())))))); + myContext = longAt((void *)((process + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if (!((/* isPointers: */ + ((!(myList & (tagMask())))) + && (((byteAt((void *)(myList + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(myList)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(myList + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(myList - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > LastLinkIndex) + && ((((longAt((void *)(myContext))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((/* begin isResumableContext: */ + assert(isContext(myContext)), + ((((longAt((void *)((myContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + ok = removeProcessfromList(process, myList); + if (!ok) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrOperationFailed; + return; + } + if (((longAt((void *)(myList))) & (classIndexMask())) != GIV(classLinkedListClassTag)) { + backupContexttoBlockingSendTo(myContext, myList); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(nilObj)); + } + else { + /* stackTopPut: */ + longAtput(GIV(stackPointer),myList); + } +} + + +/* Primitive. Terminate up the context stack from the receiver up to but not + including the argument, if previousContext is on my Context stack. Make + previousContext my + sender. This prim has to shadow the code in ContextPart>terminateTo: to be + correct. + Override to ensure the caller's saved ip is correct, i.e. if an + interpreter frame it may + have to move to iframeSavedIP. */ + + /* CoInterpreterPrimitives>>#primitiveTerminateTo */ +static void +primitiveTerminateTo(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aContextOrNil; + char *callerFP; + char *callerFP1; + char *contextsFP; + usqInt contextsIP; + char *contextsSP; + sqInt currentCtx; + char *fp; + char *fp1; + char *frameAbove; + sqInt handlerOrNilOrZero; + char *newFP; + char *newSP; + sqInt nextCntx; + StackPage *pageToStopOn; + sqInt senderOop; + sqInt senderOopSqInt; + char *source; + sqInt stackedReceiverOffset; + char *theFP; + char *theFP1; + StackPage *thePage; + StackPage *thePage1; + sqInt thisCtx; + sqInt valuePointer; + + contextsFP = ((char *) 0); + aContextOrNil = longAt(GIV(stackPointer)); + if (!((aContextOrNil == GIV(nilObj)) + || (/* isContext: */ + ((!(aContextOrNil & (tagMask())))) + && (((longAt((void *)(aContextOrNil))) & (classIndexMask())) == ClassMethodContextCompactIndex)))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + thisCtx = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (thisCtx == aContextOrNil) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* If we're searching for aContextOrNil it might be on a stack page. Helps to know + if we can free a whole page or not, or if we can short-cut the termination. */ + if ((aContextOrNil != GIV(nilObj)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContextOrNil))))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + contextsFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + pageToStopOn = stackPageAtpages(pageIndexFor(contextsFP), GIV(pages)); + } + else { + pageToStopOn = 0; + } + + /* if thisCtx is married ensure it is a base frame. Then we can assign its sender. */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(thisCtx)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* Optimize terminating thisContext. Move its frame down to be next to + aContextOrNil's frame. Common in the exception system and so helps to be fast. */ + if ((theFP == GIV(framePointer)) + && (pageToStopOn == GIV(stackPage))) { + assertValidStackedInstructionPointersInline(GIV(stackPage), __LINE__); + if ((((char *)(longAt(theFP + FoxSavedFP)))) != contextsFP) { + stackedReceiverOffset = (FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))); + + /* begin findFrameAbove:inPage: */ + callerFP = ((char *) 0); + fp = (pageToStopOn->headFP); + if (fp == contextsFP) { + frameAbove = 0; + goto l1; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == contextsFP) { + frameAbove = fp; + goto l1; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + frameAbove = 0; + /* end findFrameAbove:inPage: */ +l1: + contextsIP = longAt(frameAbove + FoxCallerSavedIP); + assert((((((usqInt)contextsIP)) >= (startOfMemory())) + || (contextsIP == (ceReturnToInterpreterPC()))) == (!(isMachineCodeFrame(contextsFP)))); + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(frameAbove))); + newSP = (frameAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(frameAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(frameAbove))->cmNumArgs) + : byteAt((frameAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + newFP = (newSP - stackedReceiverOffset) - BytesPerWord; + for (source = (theFP + stackedReceiverOffset); source >= GIV(stackPointer); source += (-BytesPerWord)) { + newSP -= BytesPerWord; + longAtput(newSP,longAt(source)); + } + longAtput(newFP + FoxSavedFP,((usqInt)contextsFP)); + + /* Ensure contract between machine-code callee and interpreter caller frames is preserved. + Return pc needs to be ceReturnToInterpreterPC. */ + if (((((usqInt)(longAt(newFP + FoxMethod)))) < (startOfMemory())) + && (contextsIP >= (startOfMemory()))) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(contextsFP))); + longAtput(contextsFP + FoxIFSavedIP,contextsIP); + contextsIP = ceReturnToInterpreterPC(); + } + longAtput(newFP + FoxCallerSavedIP,contextsIP); + assert(isContext(thisCtx)); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(newFP)) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(newFP)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(thisCtx)) + && (!(isForwarded(thisCtx)))); + assert(validStorePointerUncheckedArgs(SenderIndex, thisCtx, valuePointer)); + longAtput((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),valuePointer); + /* begin withSmallIntegerTags: */ + assert(((oopForPointer(contextsFP)) & (BytesPerWord - 1)) == 0); + valuePointer = (oopForPointer(contextsFP)) + (smallIntegerTag()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(thisCtx)) + && (!(isForwarded(thisCtx)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, thisCtx, valuePointer)); + longAtput((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointer); + GIV(framePointer) = newFP; + GIV(stackPointer) = newSP; + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + assertValidStackedInstructionPointersInline(GIV(stackPage), __LINE__); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + return; + } + assertValidStackedInstructionPointers(__LINE__); + + /* May cause a GC!! */ + theFP = externalEnsureIsBaseFrame(theFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + + /* May also reclaim aContextOrNil's page, hence... */ + if ((aContextOrNil != GIV(nilObj)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContextOrNil))))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + contextsFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + pageToStopOn = stackPageAtpages(pageIndexFor(contextsFP), GIV(pages)); + } + else { + pageToStopOn = 0; + } + } + else { + currentCtx = longAt((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + } + assertValidStackedInstructionPointers(__LINE__); + + /* begin context:hasSender: */ + handlerOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(-1, thisCtx, aContextOrNil); + if (!handlerOrNilOrZero) { + /* Need to walk the stack freeing stack pages and nilling contexts. */ + while (!((currentCtx == aContextOrNil) + || (currentCtx == GIV(nilObj)))) { + assert(isContext(currentCtx)); + if (((((longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + + /* If externalEnsureIsBaseFrame: above has moved thisContext to its own stack + then we will always terminate to a frame on a different page. But if we are + terminating some other context to a context somewhere on the current page + we must save the active frames above that context. Things will look e.g. like this: + thisCtx 499383332 s MethodContext(ContextPart)>resume: + 499380484 s BlockClosure>ensure: + 499377320 s MethodContext(ContextPart)>handleSignal: + 499373760 s MethodContext(ContextPart)>handleSignal: + 499372772 s MessageNotUnderstood(Exception)>signal + 499369068 s CodeSimulationTests(Object)>doesNotUnderstand: absentMethod + 499368708 s [] in CodeSimulationTests>testDNU + (sender is 0xbffc2480 I CodeSimulationTests>runSimulated:) + ------------ + framePointer 0xbffc234c M MethodContext(ContextPart)>doPrimitive:method:receiver:args: + 0xbffc2378 M MethodContext(ContextPart)>tryPrimitiveFor:receiver:args: + 0xbffc23ac M MethodContext(ContextPart)>send:to:with:super: + 0xbffc23e4 M MethodContext(ContextPart)>send:super:numArgs: + 0xbffc2418 M MethodContext(InstructionStream)>interpretNextInstructionFor: + 0xbffc2434 M MethodContext(ContextPart)>step + 0xbffc2458 I MethodContext(ContextPart)>runSimulated:contextAtEachStep: + ------------ + (499368708's sender) 0xbffc2480 I CodeSimulationTests>runSimulated: + 0xbffc249c M CodeSimulationTests>testDNU + 0xbffc24bc I CodeSimulationTests(TestCase)>performTest + 0xbffc24dc I [] in CodeSimulationTests(TestCase)>runCase + aContextOrNil 0xbffc24fc M BlockClosure>ensure: + 0xbffc2520 I CodeSimulationTests(TestCase)>runCase + 0xbffc253c M [] in TestResult>runCase: + When we find this case we move the frames above to a new page by making the + frame above currentCtx a base frame, i.e. making 0xbffc2458 in the above example + a base frame. But in this iteration of the loop we don't move down a frame i.e. currentCtx + doesn't change on this iteration. */ + if (thePage == GIV(stackPage)) { + /* begin findFrameAbove:inPage: */ + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == theFP) { + frameAbove = 0; + goto l2; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == theFP) { + frameAbove = fp; + goto l2; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + frameAbove = 0; + /* end findFrameAbove:inPage: */ +l2: + assert(frameAbove != 0); + + /* May cause a GC!! May also reclaim aContextOrNil's page, hence... */ + frameAbove = externalEnsureIsBaseFrame(frameAbove); + if ((aContextOrNil != GIV(nilObj)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContextOrNil))))) { + /* begin frameOfMarriedContext: */ + senderOopSqInt = longAt((void *)((aContextOrNil + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOopSqInt) & 7) == 1)); + contextsFP = ((char *)(senderOopSqInt - (smallIntegerTag()))); + + /* begin stackPageFor: */ + pageToStopOn = stackPageAtpages(pageIndexFor(contextsFP), GIV(pages)); + } + else { + pageToStopOn = 0; + } + } + else { + if (thePage == pageToStopOn) { + /* We're here. Cut back the stack to aContextOrNil's frame, + push its instructionPointer if it's not already a head frame, + and we're done. */ + + /* begin findFrameAbove:inPage: */ + callerFP1 = ((char *) 0); + fp1 = (thePage->headFP); + if (fp1 == contextsFP) { + frameAbove = 0; + goto l3; + } + while (((callerFP1 = ((char *)(longAt(fp1 + FoxSavedFP))))) != 0) { + if (callerFP1 == contextsFP) { + frameAbove = fp1; + goto l3; + } + fp1 = callerFP1; + } + error("did not find theFP in stack page"); + frameAbove = 0; + /* end findFrameAbove:inPage: */ +l3: + if (frameAbove) { + contextsSP = ((/* begin frameCallerSP: */ + assert(!(isBaseFrame(frameAbove))), +(frameAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(frameAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(frameAbove))->cmNumArgs) + : byteAt((frameAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)) - BytesPerWord; + longAtput(contextsSP,longAt(frameAbove + FoxCallerSavedIP)); + + /* begin setHeadFP:andSP:inPage: */ + assert(contextsSP < contextsFP); + assert((contextsSP < ((thePage->baseAddress))) + && (contextsSP > (((thePage->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((contextsFP < ((thePage->baseAddress))) + && (contextsFP > (((thePage->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (thePage->headFP = contextsFP); + (thePage->headSP = contextsSP); + } + currentCtx = aContextOrNil; + } + else { + /* We can free the entire page without further ado. */ + theFP1 = (thePage->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP1)); + thePage1 = stackPageAtpages(pageIndexFor(theFP1), GIV(pages)); + currentCtx = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(currentCtx)); + assert((currentCtx == (nilObject())) + || (isContext(followMaybeForwarded(currentCtx)))); + + /* for a short time invariant is violated; assert follows */ + freeStackPageNoAssert(thePage); + } + } + } + else { + nextCntx = longAt((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + + /* begin markContextAsDead: */ + assert(isContext(currentCtx)); + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(SenderIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(currentCtx)) + && (!(isForwarded(currentCtx)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, currentCtx, GIV(nilObj))); + longAtput((void *)((currentCtx + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + currentCtx = nextCntx; + } + } + } + assert(pageListIsWellFormed()); + if (((((longAt((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + assert(checkIsStillMarriedContextcurrentFP(thisCtx, GIV(framePointer))); + assert(isBaseFrame(frameOfMarriedContext(thisCtx))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin frameCallerContext:put: */ + assert((aContextOrNil == (nilObject())) + || (isContext(aContextOrNil))); + assert(isBaseFrame(theFP)); + assert(((theFP + (frameStackedReceiverOffset(theFP))) + (2 * BytesPerWord)) == (((stackPageFor(theFP))->baseAddress))); + assert((longAt((theFP + (frameStackedReceiverOffset(theFP))) + BytesPerWord)) == (frameContext(theFP))); + longAtput((theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + (2 * BytesPerWord),aContextOrNil); + } + else { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SenderIndex, thisCtx, aContextOrNil)); + assert(isNonImmediate(thisCtx)); + if (oopisGreaterThanOrEqualTo(thisCtx, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(aContextOrNil & (tagMask())))) + && (oopisLessThan(aContextOrNil, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(thisCtx + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(thisCtx); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((thisCtx + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),aContextOrNil); + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + assertValidStackedInstructionPointers(__LINE__); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); +} + + +/* Primitive. Unload the module with the given name. + Reloading of the module will happen *later* automatically, when a + function from it is called. This is forced by invalidating all external + primitive methods and activations in flushExternalPrimitives. + N.B. since this is most likely a development time activity we don't care + about performance. */ + + /* CoInterpreterPrimitives>>#primitiveUnloadModule */ +static void +primitiveUnloadModule(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt moduleLength; + sqInt moduleName; + usqInt numSlots; + + moduleName = longAt(GIV(stackPointer)); + if (!(/* isBytes: */ + ((!(moduleName & (tagMask())))) + && (((byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + moduleLength = ((((/* begin numSlotsOf: */ + assert((classIndexOf(moduleName)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(moduleName + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(moduleName - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (!(ioUnloadModuleOfLength(oopForPointer(firstIndexableField(moduleName)), moduleLength))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + if (/* object:equalsString:ofSize: */ + (/* isBytes: */ + ((!(moduleName & (tagMask())))) + && (((byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((!(((byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((numBytesOfBytes(moduleName)) == moduleLength) + && ((strncmp("SqueakFFIPrims", firstIndexableField(moduleName), moduleLength)) == 0)))) { + primitiveCalloutPointer = ((void *) -1); + } + + /* N.B. flushExternalPrimitives continues. Do *not* do anything after flushExternalPrimitives in the CoInterpreter */ + forceInterruptCheck(); + flushExternalPrimitives(); +} + + +/* Void all internal VM state in the stack and machine code zones + + Override to jump to the interpreter because the machine code zone is now + void. + */ + + /* CoInterpreterPrimitives>>#primitiveVoidVMState */ +static void +primitiveVoidVMState(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + activeContext = voidVMStateForSnapshotFlushingExternalPrimitivesIf(0); + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + ceInvokeInterpret(); +} + + +/* The receiver (or first argument) must be a compiledMethod. The optional + (or second) argument must be a + boolean. Clear all VM state associated with the method, including any + machine code, or machine code pcs + in context objects. If the optional boolean argument is false do not scan + the heap looking for contexts. */ + + /* CoInterpreterPrimitives>>#primitiveVoidVMStateForMethod */ +static void +primitiveVoidVMStateForMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt address; + StackPage *aPage; + char *calleeFP; + sqInt classIndex; + CogMethod *cogMethod; + sqInt divorcedSome; + sqInt followingWord; + usqInt followingWordAddress; + int hasCogMethod; + sqInt i; + sqInt methodHeader; + sqInt methodObj; + usqInt numSlots; + sqInt obj; + sqInt object; + sqInt prevObj; + sqInt prevPrevObj; + sqInt scanHeapForContexts; + sqInt senderOop; + sqInt startObject; + sqInt theContext; + char *theFP; + char *theFrame; + StackPage *thePage; + char *theSP; + sqInt top; + + /* See comment ''One might think...'' below */ + /* In Smalltalk allow both aMethod voidCogVMState and aMethod voidCogVMStateScanningContextsIf: aBoolean */ + scanHeapForContexts = 1; + if (GIV(argumentCount)) { + methodObj = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin booleanValueOf: */ + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + scanHeapForContexts = 1; + goto l1; + } + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + scanHeapForContexts = 0; + goto l1; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + scanHeapForContexts = null; + /* end booleanValueOf: */ +l1: + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + else { + methodObj = longAt(GIV(stackPointer)); + } + if (GIV(argumentCount) > 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + + /* In the NewspeakVM we allow VMMirror voidStateFor: method scanningIf: aBoolean as well as the Smalltalk forms. */ + flushMethodCacheForMethod(methodObj); + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l2; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer)); + /* end ensureFrameIsMarried:SP: */ +l2: + + /* begin ensurePushedInstructionPointer */ + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if ((hasCogMethod = methodHasCogMethod(methodObj))) { + /* begin divorceMachineCodeFramesWithMethod: */ + /* begin cogMethodOf: */ + methodHeader = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeader)) + && ((((usqInt)methodHeader)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeader); + do { + if (GIV(stackPage)) { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + + /* This is needed for the assert in externalDivorceFrame:andContext: + Slang can't currently cope with the lack of the variable here. + Something to do with the preceding statement. Take it out + and the code is good. leave it in and we get do { ... } while(l1:) */ + + /* begin divorceSomeMachineCodeFramesWithMethod: */ + divorcedSome = 0; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + aPage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(aPage))) { + /* this to avoid assert in externalDivorceFrame:andContext: */ + markStackPageMostRecentlyUsed(GIV(stackPage)); + + /* begin divorceAMachineCodeFrameWithCogMethod:in: */ + calleeFP = ((char *) 0); + theFP = (aPage->headFP); + theSP = (aPage->headSP); + + /* theSP points at hottest item on frame's stack */ + theSP += BytesPerWord; + while (1) { + if (((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) + && (cogMethod == (mframeHomeMethod(theFP)))) { + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + theContext = longAt(theFP + FoxThisContext); + goto l6; + } + theContext = marryFrameSP(theFP, theSP); + /* end ensureFrameIsMarried:SP: */ +l6: + externalDivorceFrameandContext(theFP, theContext); + goto l4; + } + calleeFP = theFP; + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!(theFP != 0)) break; + /* theSP points at stacked hottest item on frame's stack */ + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(calleeFP))); + theSP = (calleeFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(calleeFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(calleeFP))->cmNumArgs) + : byteAt((calleeFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + goto l5; +l4: + divorcedSome = 1; +l5:; + } + } + } while(divorcedSome); + } + + /* One might think (as this author did) that the heap scan is unnecessary if the method does not + have a cog method. But it could be the case that the code zone has recently been reclaimed + and so not having a cog method is no indication that it didn't have a cog method some time in + the recent past, and that there are indeed still contexts with machine code pcs out there. The + only steps that can be avoided are divorcing frames in the stack zone, and scanning to unlink and + free if there isn't a cog method, unless we are told otherwise. */ + if (scanHeapForContexts) { + /* begin ensureAllContextsWithMethodHaveBytecodePCs: */ + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && ((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord()))))))) == methodObj)) { + widowOrForceToBytecodePC(obj); + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l3: + assert(oopisGreaterThan(obj, prevObj)); + } + } + if (hasCogMethod) { + unlinkSendsToandFreeIf(methodObj, 1); + } + + /* If flushing led to divorce continue in the interpreter. */ + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + assert((methodObj == (stackTop())) + || ((GIV(argumentCount) > 0) + && (methodObj == (stackValue(1))))); + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + ceInvokeInterpret(); + } + + /* If not, work out where we are and continue */ + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + assert(((thePage->headFP)) == theFrame); + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + assert((methodObj == (stackTop())) + || ((GIV(argumentCount) > 0) + && (methodObj == (stackValue(1))))); + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + /* CoInterpreterPrimitives>>#primitiveWait */ +static void +primitiveWait(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + sqLong excessSignals; + int inInterpreter; + sqInt objOop; + sqInt sema; + + /* rcvr */ + sema = longAt(GIV(stackPointer)); + excessSignals = fetchIntegerofObject(ExcessSignalsIndex, sema); + if (excessSignals > 0) { + storeIntegerofObjectwithValue(ExcessSignalsIndex, sema, excessSignals - 1); + } + else { + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + addLastLinktoList(activeProc, sema); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSEnterCriticalSection); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); + } +} + + +/* Primitively do the equivalent of Process>yield, avoiding the overhead of a + fork and a wait in the standard implementation. + Get the Processor object (1), from it get the activeProcess (2) and hence + the activeProcess's priority (3), and the array of + all runnable processes (5). Via the priority get the list of quiescent + processes at the active priority (6) (implicitly we know + there are no quiescent processes at higher priorities than the + activeProcess which is always one of the highest priority + runnable processes). */ +/* 1 */ + + /* CoInterpreterPrimitives>>#primitiveYield */ +static void +primitiveYield(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + int inInterpreter; + sqInt oop; + sqInt priority; + sqInt processList; + sqInt processLists; + sqInt scheduler; + + scheduler = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* 2 */ + activeProc = longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* 3 */ + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + priority = (oop >> 3); + + /* 4 */ + processLists = longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + + /* 5 */ + processList = longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((priority - 1)) << (shiftForWord())))))); + + /* If the list of quiescent processes at this priority is empty there are no processes to yield to. + If the list is not empty, activate the first one, and add the active process to the end of the list. */ + + /* begin isEmptyList: */ + assert(!(isForwarded(processList))); + if ((longAt((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj)) { + return; + } + + /* We're going to switch process, either to an interpreted frame or a machine + code frame. To know whether to return or enter machine code we have to + know from whence we came. We could have come from the interpreter, + either directly or via a machine code primitive. We could have come from + machine code. The instructionPointer tells us where from: */ + inInterpreter = GIV(instructionPointer) >= (startOfMemory()); + addLastLinktoList(activeProc, processList); + + /* transferToHighestPriorityProcessFrom: */ + transferTofrom(wakeHighestPriority(), CSYield); + + /* begin forProcessPrimitiveReturnToExecutivePostContextSwitch: */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + returnToExecutivepostContextSwitch(inInterpreter, 1); +} + + /* CoInterpreterPrimitives>>#unmarkAllFrames */ +static void +unmarkAllFrames(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt flags; + sqInt i; + sqInt methodField; + char *theFP; + StackPage *thePage; + + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + theFP = (thePage->headFP); + do { + methodField = longAt(theFP + FoxMethod); + if ((((usqInt)methodField)) < (startOfMemory())) { + if (methodField & 4) { + longAtput(theFP + FoxMethod,methodField - 4); + } + } + else { + flags = longAt(theFP + FoxIFrameFlags); + if (flags & 2) { + longAtput(theFP + FoxIFrameFlags,flags - 2); + } + } + } while(((theFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0); + } + } +} + + +/* Initialize the stack pages. In the C VM theStackPages will be alloca'ed + memory to hold the + stack pages on the C stack. In the simulator they are housed in the memory + between the + cogMethodZone and the heap. */ + + /* CoInterpreterStackPages>>#initializeStack:numSlots:pageSize: */ +static NoDbgRegParms void +initializeStacknumSlotspageSize(char *theStackPages, sqInt stackSlots, sqInt slotsPerPage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt count; + sqInt index; + sqInt numPages; + StackPage *page; + char *pageStructBase; + void *pointer; + usqIntptr_t structStackPageSize; + sqInt theIndex; + + structStackPageSize = sizeof(CogStackPage); + GIV(bytesPerPage) = slotsPerPage * BytesPerWord; + numPages = GIV(numStackPages); + + /* Because stack pages grow down baseAddress is at the top of a stack page and so to avoid + subtracting BytesPerWord from baseAddress and lastAddress in the init loop below we simply + push the stackPage array up one word to avoid the overlap. This word is extraStackBytes. */ + pageStructBase = (theStackPages + (numPages * GIV(bytesPerPage))) + BytesPerWord; + GIV(pages) = ((StackPage *) pageStructBase); + + /* make sure there's enough headroom */ + assert((((stackPageByteSize()) - (stackLimitBytes())) - (stackLimitOffset())) >= (stackPageHeadroom())); + for (index = 0; index < numPages; index += 1) { + /* begin stackPageAt: */ + page = stackPageAtpages(index, GIV(pages)); + (page->lastAddress = theStackPages + (index * GIV(bytesPerPage))); + (page->baseAddress = ((page->lastAddress)) + GIV(bytesPerPage)); + (page->stackLimit = ((page->baseAddress)) - ((((stackPageFrameBytes()) < (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))) ? (stackPageFrameBytes()) : (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))))); + (page->realStackLimit = (page->stackLimit)); + (page->baseFP = 0); + (page->nextPage = stackPageAt((index == (numPages - 1) + ? 0 + : index + 1))); + (page->prevPage = stackPageAt((index + ? index - 1 + : numPages - 1))); + } + + /* Now compute stackBasePlus1 so that the pageIndexFor: call maps all addresses from + aPage baseAddress to aBase limitAddress + 1 to the same index (stacks grow down) */ + GIV(stackBasePlus1) = (((char *) theStackPages)) + 1; + + /* The overflow limit is the amount of stack to retain when moving frames from an overflowing + stack to reduce thrashing. See stackOverflowOrEvent:mayContextSwitch: */ + + /* begin stackPageAt: */ + page = stackPageAtpages(0, GIV(pages)); + GIV(overflowLimit) = ((((page->baseAddress)) - ((page->realStackLimit))) * 3) / 5; + for (index = 0; index < numPages; index += 1) { + /* begin stackPageAt: */ + page = stackPageAtpages(index, GIV(pages)); + assert((pageIndexFor((page->baseAddress))) == index); + assert((pageIndexFor(((page->baseAddress)) - ((slotsPerPage - 1) * BytesPerWord))) == index); + assert((stackPageFor((page->baseAddress))) == page); + assert((stackPageFor((page->stackLimit))) == page); + + /* begin initializePageTraceToInvalid: */ + (page->trace = StackPageTraceInvalid); + } + GIV(mostRecentlyUsedPage) = stackPageAtpages(0, GIV(pages)); + page = GIV(mostRecentlyUsedPage); + count = 0; + do { + count += 1; + pointer = (page->baseAddress); + + /* begin pageIndexFor: */ +# if !NDEBUG + assert((((((char *) pointer)) >= (GIV(stackBasePlus1) - 1)) && ((((char *) pointer)) <= (((char *) GIV(pages)))))); +# endif // !NDEBUG + + + /* this to avoid an annoying warning: left operand of comma operator has no effect */ + theIndex = pageIndexForstackBasePlus1bytesPerPage(pointer, GIV(stackBasePlus1), GIV(bytesPerPage)); + assert((stackPageAt(theIndex)) == page); + assert((pageIndexFor((page->baseAddress))) == theIndex); + assert((pageIndexFor((page->stackLimit))) == theIndex); + assert((pageIndexFor(((page->lastAddress)) + 1)) == theIndex); + } while(((page = (page->nextPage))) != GIV(mostRecentlyUsedPage)); + assert(count == numPages); + assert(pageListIsWellFormed()); +} + + +/* */ +/* Answer the page index for a pointer into stack memory, i.e. the index + for the page the address is in. N.B. This is a zero-relative index. */ + + /* CoInterpreterStackPages>>#pageIndexFor: */ +static NoDbgRegParms sqInt +pageIndexFor(void *pointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + +# if !NDEBUG + assert((((((char *) pointer)) >= (GIV(stackBasePlus1) - 1)) && ((((char *) pointer)) <= (((char *) GIV(pages)))))); +# endif + + + /* this to avoid an annoying warning: left operand of comma operator has no effect */ + return pageIndexForstackBasePlus1bytesPerPage(pointer, GIV(stackBasePlus1), GIV(bytesPerPage)); +} + + +/* If anOop is an address within the stack zone answer a string stating that, + otherwise answer nil. + */ + + /* CoInterpreterStackPages>>#whereIsMaybeStackThing: */ +static NoDbgRegParms char * +whereIsMaybeStackThing(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (oopisGreaterThanOrEqualToandLessThan(anOop, GIV(stackBasePlus1) - 1, GIV(pages))) { + return " is in the stack zone"; + } + return null; +} + + +/* Answer either a malloced string with the null-terminated contents of oop + if oop is a string, + or the null pointer if oop is nil, or fail. It is the client's + responsibility to free the string later. */ + + /* InterpreterPrimitives>>#cStringOrNullFor: */ +char * +cStringOrNullFor(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + char *cString; + sqInt fmt; + sqInt isString; + sqInt len; + usqInt numSlots; + usqInt numSlotsUsqInt; + + /* begin isInstanceOfClassByteString: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((oop & (tagMask())) != 0)) { + isString = 0; + goto l2; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + isString = ClassByteStringCompactIndex == ccIndex; + /* end isInstanceOfClassByteString: */ +l2: + if (!isString) { + if (oop != GIV(nilObj)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + return 0; + } + + /* begin lengthOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + len = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + len = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + len = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + len = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + len = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + len = 0; + /* end lengthOf: */ +l1: + if (!len) { + return 0; + } + cString = malloc(len + 1); + if (!cString) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoCMemory; + return 0; + } + memcpy(cString, firstIndexableField(oop), len); + cString[len] = 0; + return cString; +} + + +/* In C, non-zero is true, so avoid computation by simply answering + primFailCode in the C version. + */ + + /* InterpreterPrimitives>>#failed */ +sqInt +failed(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(primFailCode); +} + + +/* Answer the identity hash of an object, assigning a hash if it doesn't have + one. On Spur refuse to assign a hash to something that looks like a + behavior. + */ + + /* InterpreterPrimitives>>#identityHashOf: */ +sqInt +identityHashOf(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt hash; + usqInt hashUsqInt; + + if (((anOop & (tagMask())) != 0)) { + /* begin immediateAsInteger:ifFail: */ + if ((((anOop) & 7) == 1)) { + return ((usqInt) ((anOop >> 3))); + } + if (((anOop & (characterTag())) != 0)) { + return ((((usqInt)anOop))) >> (numTagBits()); + } + if (((anOop & (smallFloatTag())) != 0)) { + /* begin rotatedFloatBitsOf: */ + assert(isImmediateFloat(anOop)); + return (((((usqInt)anOop))) >> ((numTagBits()) + 1)) + ((((anOop & (1U << (numTagBits()))) != 0) + ? ((((usqInt)-1)) << ((64 - (numTagBits())) - 1)) + : 0)); + } + return ((usqInt) -1); + } + if ((((long32At((void *)(anOop + 4))) & (identityHashHalfWordMask())) == 0) + && (objCouldBeClassObj(anOop))) { + return -1; + } + + /* begin hashBitsOf: */ + hash = (long32At((void *)(anOop + 4))) & (identityHashHalfWordMask()); + if (!hash) { + /* cb 1/19/2017 18:34: + would like to assert + self assert: (coInterpreter addressCouldBeClassObj: objOop) not + but instance-specific behaviors that are instances of themselves may + fail this test. + eem 12/28/2021 11:52 I'm not sure this is the issue. The issue is only validating objOop as a behavior. */ + + /* begin newHashBitsOf: */ + /* begin newObjectHash */ + assert(!((GIV(lastHash) == 0))); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 2)); + GIV(lastHash) = GIV(lastHash) ^ ((((GIV(lastHash) << 7))) & (identityHashHalfWordMask())); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 3)); + hashUsqInt = GIV(lastHash); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(anOop + 4),((((long32At((void *)(anOop + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hashUsqInt & (identityHashHalfWordMask()))); + hash = hashUsqInt & (identityHashHalfWordMask()); + } + return hash; +} + + +/* Answer true if integer object is negative. + Fail if object pointed by oop i not an integer. */ + + /* InterpreterPrimitives>>#isNegativeIntegerValueOf: */ +static NoDbgRegParms sqInt +isNegativeIntegerValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + int ok; + sqInt smallInt; + + if ((((oop) & 7) == 1)) { + smallInt = (oop >> 3); + return smallInt < 0; + } + if ((!(oop & (tagMask())))) { + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (ok) { + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargeNegativeIntegerCompactIndex == ccIndex; + if (ok) { + return 1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; +} + + +/* Answer if oop is a value of an integer in address range, i.e up to the + size of a machine word. + The object may be either a positive SmallInteger or a LargePositiveInteger + of size <= word size. + */ + + /* InterpreterPrimitives>>#isPositiveMachineIntegerObject: */ +sqInt +isPositiveMachineIntegerObject(sqInt oop) +{ + sqInt ccIndex; + int ok; + + if ((((oop) & 7) == 1)) { + return ((oop >> 3)) >= 0; + } + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + return ok + && ((numBytesOfBytes(oop)) <= (sizeof(usqIntptr_t))); +} + + +/* Return a Large Integer object for the given integer magnitude and sign */ + + /* InterpreterPrimitives>>#magnitude64BitIntegerFor:neg: */ +static NoDbgRegParms sqInt +magnitude64BitIntegerForneg(usqLong magnitude, sqInt isNegative) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int isSmall; + int largeClassIndex; + usqInt newLargeInteger; + usqInt newObj; + usqInt numBytesUsqInt; + sqInt numSlots; + sqInt objFormat; + sqInt smallVal; + sqInt sz; + + isSmall = (isNegative + ? magnitude <= ((MaxSmallInteger) + 1) + : magnitude <= (MaxSmallInteger)); + if (isSmall) { + smallVal = ((sqInt) magnitude); + if (isNegative) { + smallVal = 0 - smallVal; + } + return (((usqInt)smallVal << 3) | 1); + } + largeClassIndex = (isNegative + ? ClassLargeNegativeIntegerCompactIndex + : ClassLargePositiveIntegerCompactIndex); + sz = 8; + objFormat = (firstByteFormat()) + ((8 - sz) & (BytesPerWord - 1)); + + /* begin eeInstantiateSmallClassIndex:format:numBytes: */ + assert((sz >= 0) + && ((largeClassIndex != 0) + && ((knownClassAtIndex(largeClassIndex)) != GIV(nilObj)))); + assert(((objFormat < (firstByteFormat()) + ? objFormat + : objFormat & (byteFormatMask()))) == (instSpecOfClass(knownClassAtIndex(largeClassIndex)))); + numSlots = ((sz + BytesPerWord) - 1) / BytesPerWord; + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytesUsqInt = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytesUsqInt % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytesUsqInt) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytesUsqInt) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newLargeInteger = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + largeClassIndex); + GIV(freeStart) += numBytesUsqInt; + newLargeInteger = newObj; + /* end eeInstantiateSmallClassIndex:format:numBytes: */ +l1: + + /* Memory is eight byte aligned in SPUR, so we are sure to have room for 64bits word whatever allocated sz */ + + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((newLargeInteger + BaseHeaderSize)),SQ_SWAP_8_BYTES_IF_BIGENDIAN(magnitude)); + return newLargeInteger; +} + + +/* Convert the given object into an integer value. + The object may be either a positive SmallInteger or up to an eight-byte + LargeInteger. + */ + + /* InterpreterPrimitives>>#magnitude64BitValueOf: */ +static NoDbgRegParms usqLong +magnitude64BitValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + sqInt fmt; + usqInt numSlots; + int ok; + sqInt smallIntValue; + sqInt sz; + + if ((((oop) & 7) == 1)) { + smallIntValue = (oop >> 3); + if (smallIntValue < 0) { + smallIntValue = 0 - smallIntValue; + } + return smallIntValue; + } + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (!ok) { + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + ok = ClassLargeNegativeIntegerCompactIndex == ccIndex; + if (!ok) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + sz = ((((/* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (sz > (sizeof(sqLong))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + if (sz > 4) { + return SQ_SWAP_8_BYTES_IF_BIGENDIAN((long64At((void *)((oop + BaseHeaderSize))))); + } + return ((unsigned int) (SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((oop + BaseHeaderSize))))))); +} + + +/* Answer a value of an integer in address range, i.e up to the size of a + machine word. + The object is known not to be a SmallIntege. It is hoped to be a + LargePositiveInteger of size <= word size. + */ + + /* InterpreterPrimitives>>#positiveMachineIntegerValueOfObj: */ +static NoDbgRegParms usqIntptr_t +positiveMachineIntegerValueOfObj(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bs; + sqInt ccIndex; + sqInt fmt; + sqInt isClassOfNonImmequalTocompactClassIndexRV; + usqInt numSlots; + + assert(!(((((oop) & 7) == 1)))); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + isClassOfNonImmequalTocompactClassIndexRV = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (!isClassOfNonImmequalTocompactClassIndexRV) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + bs = ((((/* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (bs > (sizeof(usqIntptr_t))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + if (((sizeof(usqIntptr_t)) == 8) + && (bs > 4)) { + return SQ_SWAP_8_BYTES_IF_BIGENDIAN((long64At((void *)((oop + BaseHeaderSize))))); + } + return ((unsigned int) (SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((oop + BaseHeaderSize))))))); +} + + +/* Answer a value of an integer in address range, i.e up to the size of a + machine word. + The object may be either a positive SmallInteger or a LargePositiveInteger + of size <= word size. + */ +/* some important callers such as primitiveNewWithArg, so inline the common + case + */ + + /* InterpreterPrimitives>>#positiveMachineIntegerValueOf: */ +usqIntptr_t +positiveMachineIntegerValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt value; + + if ((((oop) & 7) == 1)) { + value = (oop >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return value; + } + + /* don't inline the rare case */ + return positiveMachineIntegerValueOfObj(oop); +} + + /* InterpreterPrimitives>>#primitiveAdd */ +static void +primitiveAdd(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerResult; + + integerResult = (stackIntegerValue(1)) + (stackIntegerValue(0)); + + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)(integerResult)) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerResult << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveAddLargeIntegers */ +EXPORT(void) +primitiveAddLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + sqInt resultIsNegative; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (GIV(primFailCode)) { + return; + } + if (aIsNegative == bIsNegative) { + /* Protect against overflow */ + if (a > (0xFFFFFFFFFFFFFFFFULL - b)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + result = a + b; + resultIsNegative = aIsNegative; + } + else { + if (a >= b) { + result = a - b; + resultIsNegative = aIsNegative; + } + else { + result = b - a; + resultIsNegative = bIsNegative; + } + } + oopResult = magnitude64BitIntegerForneg(result, resultIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Primitive. Change the class of the argument to make it an instance of the + receiver given that the format of the receiver matches the format of the + argument's class. + Fail if receiver or argument are SmallIntegers, or the receiver is an + instance of a + compact class and the argument isn't, or when the argument's class is + compact and + the receiver isn't, or when the format of the receiver is different from + the format of + the argument's class, or when the arguments class is fixed and the + receiver's size + differs from the size that an instance of the argument's class should + have. + */ + + /* InterpreterPrimitives>>#primitiveAdoptInstance */ +static void +primitiveAdoptInstance(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt err; + sqInt rcvr; + + arg = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((arg & (tagMask())) != 0)) + || ((GIV(argumentCount) > 1) + && ((((rcvr & (tagMask())) != 0)) + || (!(objCouldBeClassObj(rcvr)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + err = changeClassOfto(arg, rcvr); + if (err) { + /* changeClassOf:to: answers errors as if rcvr (the class) is an argument... */ + if (err == PrimErrBadReceiver) { + err = PrimErrBadArgument; + } + else { + if (err == PrimErrBadArgument) { + err = PrimErrBadReceiver; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = err; + } + else { + /* Flush at cache because rcvr's class has changed. */ + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + return; +} + + +/* Answer an array of all instances of the receiver that exist + when the primitive is called, excluding any that may be + garbage collected as a side effect of allocating the result array. */ + + /* InterpreterPrimitives>>#primitiveAllInstances */ +EXPORT(void) +primitiveAllInstances(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt delta; + sqInt result; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + result = allInstancesOf(longAt(GIV(stackPointer))); + if ((((result) & 7) == 1)) { + /* begin growToAccomodateContainerWithNumSlots: */ + delta = (BaseHeaderSize * 2) + (((result >> 3)) * BytesPerOop); + growOldSpaceByAtLeast(((GIV(growHeadroom) < delta) ? delta : GIV(growHeadroom))); + result = allInstancesOf(longAt(GIV(stackPointer))); + if ((((result) & 7) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + return; + } + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); +} + + +/* Answer an array of all objects that exist when the primitive + is called, excluding those that may be garbage collected as + a side effect of allocating the result array. */ + + /* InterpreterPrimitives>>#primitiveAllObjects */ +EXPORT(void) +primitiveAllObjects(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt delta; + sqInt result; + + result = allObjects(); + if ((((result) & 7) == 1)) { + /* begin growToAccomodateContainerWithNumSlots: */ + delta = (BaseHeaderSize * 2) + (((result >> 3)) * BytesPerOop); + growOldSpaceByAtLeast(((GIV(growHeadroom) < delta) ? delta : GIV(growHeadroom))); + result = allObjects(); + if ((((result) & 7) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + return; + } + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); +} + + +/* Computes arctan of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveArctan */ +static void +primitiveArctan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(atan(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + +/* Invoke the two-way become primitive. + We must at least flush the method cache here, to eliminate stale + references to mutated classes and/or selectors. + + If in the CoInterpreter, we must deal with jitted methods being becommed. + In the conception of the abstract VM, + unless a CompiledMethod is becommed to one with equivalent bytecode + contexts referring to the becommed + method will likely fetch invalid bytecodes on resumption. The + responsibility for validity here lies with the user of + the become primitive, not the VM. So one could imagine checking for + methods becomming equivalent methods + and updating Cog methods to refer to their becommed duals. But that + requires machinery to compare two + compiled methods to check if their code is equivalent. A much simpler + approach, which also gets the VM to fail + in a less confusing place if it is going to fail because the programmer + has not ensured compiled code validity + across become, is to simply divorce all frames and map context pcs back to + bytecode pcs when becomming + jitted methods. However, mapping native pcs in contexts whose methods are + flagged for become will not ensure + that pcs are mapped reliably. Consider this arc: a context on a method + that has been jitted exists with a native pc. + The method is unjitted to make room for other methods to be jitted. The + method is becommed. So the scan is only + effective for methods in the jit. Slowing down become so that a rare case + may fail more comprehensibly, when its + going to fail anyway, is a waste of effort. So we do divorce frames (since + machine code frames refer to CogMethods, + not methods), but don't scan the entire heap looking for native pcs in + contexts. Hence the primitive must be + prepared to have its calling frame divorced. We store the context for the + top frame in activeProcess. + BTW, as of late '22/early '23 this is only done for Spur. + See preBecomeAction: and postBecomeAction: */ + + /* InterpreterPrimitives>>#primitiveArrayBecome */ +static void +primitiveArrayBecome(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt ec; + sqInt rcvr; + + arg = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + ec = becomewithtwoWaycopyHash(rcvr, arg, 1, 0); + primitiveBecomeReturn(ec); +} + + +/* Invoke the one-way become primitive. + See the comment in primitiveArrayBecome for handling pervasive effects on + method cacheing and jitting. */ + + /* InterpreterPrimitives>>#primitiveArrayBecomeOneWay */ +static void +primitiveArrayBecomeOneWay(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt ec; + sqInt rcvr; + + arg = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + ec = becomewithtwoWaycopyHash(rcvr, arg, 0, 1); + primitiveBecomeReturn(ec); +} + + +/* Similar to primitiveArrayBecomeOneWay but accepts a third argument + deciding whether to + copy the receiver's elements identity hashes over the argument's elements + identity hashes. + See the comment in primitiveArrayBecome for handling pervasive effects on + method cacheing and jitting. + */ + + /* InterpreterPrimitives>>#primitiveArrayBecomeOneWayCopyHashArg */ +static void +primitiveArrayBecomeOneWayCopyHashArg(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt copyHashFlag; + sqInt ec; + + copyHashFlag = 0; + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + copyHashFlag = 1; + } + else { + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + copyHashFlag = 0; + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + ec = becomewithtwoWaycopyHash(longAt(GIV(stackPointer) + (2 * BytesPerWord)), longAt(GIV(stackPointer) + (1 * BytesPerWord)), 0, copyHashFlag); + primitiveBecomeReturn(ec); +} + + +/* Similar to primitiveArrayBecomeOneWay but does /not/ copy the receiver's + elements identity hashes over the argument's elements identity hashes. + See the comment in primitiveArrayBecome for handling pervasive effects on + method cacheing and jitting. */ + + /* InterpreterPrimitives>>#primitiveArrayBecomeOneWayNoCopyHash */ +static void +primitiveArrayBecomeOneWayNoCopyHash(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt ec; + sqInt rcvr; + + arg = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + ec = becomewithtwoWaycopyHash(rcvr, arg, 0, 0); + primitiveBecomeReturn(ec); +} + + /* InterpreterPrimitives>>#primitiveAsCharacter */ +static void +primitiveAsCharacter(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt characterCode; + sqInt characterObject; + + characterCode = longAt(GIV(stackPointer)); + if (!(((((characterCode) & 7) == 1)) + && (((characterCode = (characterCode >> 3)), + /* isInRangeCharacterCode: */ + ((characterCode >= 0) && (characterCode <= (0x3FFFFFFF))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = (GIV(argumentCount) + ? PrimErrBadArgument + : PrimErrBadReceiver); + return; + } + characterObject = ((((usqInt)(characterCode) << (numTagBits())))) + (characterTag()); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),characterObject); +} + + +/* N.B. This will answer inexact results for integers with > 53 bits of + magnitude. + */ + + /* InterpreterPrimitives>>#primitiveAsFloat */ +static void +primitiveAsFloat(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt rcvr; + char *sp; + + rcvr = longAt(GIV(stackPointer)); + assert((((rcvr) & 7) == 1)); + + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer)),floatObjectOf(((double) ((rcvr >> 3)) ))); + GIV(stackPointer) = sp; +} + + /* InterpreterPrimitives>>#primitiveAt */ +static void +primitiveAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt result; + sqInt sp1; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + + /* begin commonAt: */ + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + index = longAt(GIV(stackPointer)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l5; + } + + /* No need to test for large positive integers here. No object has 1g elements */ + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 1) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l5; + } + index = (index >> 3); + + /* begin stObject:at: */ + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + sp1 = stackPointerIndexForFrame(frameOfMarriedContext(rcvr)); + assert((ReceiverIndex + ((sp1 >> 3))) < (lengthOf(rcvr))); + stSize = sp1; + goto l3; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l3; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(rcvr))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l3: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))))) { + result = temporaryin(index - 1, frameOfMarriedContext(rcvr)); + goto l4; + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(rcvr)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + result = (((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + (index - 1)))) << 3) | 1); + goto l4; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + result = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << (shiftForWord())))))); + goto l4; + } + if (fmt >= (firstByteFormat())) { + result = (((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + ((index + fixedFields) - 1)))) << 3) | 1); + goto l4; + } + if (fmt >= (firstShortFormat())) { + result = (((usqInt)(((unsigned short) (shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))))))) << 3) | 1); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + result = positive64BitIntegerFor(long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))))); + goto l4; + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + result = (((usqInt)((((usqInt)(long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + goto l4; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + result = 0; + /* end stObject:at: */ +l4: + if (!GIV(primFailCode)) { + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); + } + /* end commonAt: */ +l5:; +} + + /* InterpreterPrimitives>>#primitiveAtPut */ +static void +primitiveAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fieldIndex; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt signedValueToStore; + sqInt signedValueToStoreSqInt; + sqInt sp; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + usqLong unsigned64BitValueToStore; + unsigned int unsignedValueToStore; + sqInt value; + + /* begin commonAtPut: */ + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + value = longAt(GIV(stackPointer)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l11; + } + + /* No need to test for large positive integers here. No object has 1g elements */ + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 2) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l11; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l11; + } + index = (index >> 3); + + /* begin stObject:at:put: */ + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l7; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l7; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l7; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l7; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l7; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l7: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(rcvr)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(rcvr))); + stSize = sp; + goto l9; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l9; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(rcvr))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l9: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))))) { + temporaryinput(index - 1, frameOfMarriedContext(rcvr), value); + goto l10; + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(rcvr)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + if (((((value) & 7) == 1)) + && ((((usqInt)((signedValueToStore = (value >> 3))))) <= 0xFF)) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + (index - 1)),signedValueToStore); + goto l10; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l10; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l6; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l6; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l6: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:storing:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + fieldIndex = (index + fixedFields) - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, rcvr, value)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),value); + goto l8; + } + if (fmt >= (firstByteFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l8; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l8; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + ((index + fixedFields) - 1)),signedValueToStoreSqInt); + goto l8; + } + if (fmt >= (firstShortFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l8; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l8; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))),signedValueToStoreSqInt); + goto l8; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l8; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l8: + goto l10; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + /* end stObject:at:put: */ +l10: + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + } + /* end commonAtPut: */ +l11:; +} + + +/* Set the cursor to the given shape. The Mac only supports 16x16 pixel + cursors. Cursor offsets are handled by Smalltalk. + */ + + /* InterpreterPrimitives>>#primitiveBeCursor */ +static void +primitiveBeCursor(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cursorBitsObj; + sqInt cursorObj; + sqInt depth; + sqInt extentX; + sqInt extentY; + sqInt maskBitsObj; + sqInt maskObj; + usqInt numSlots; + sqInt offsetObj; + sqInt offsetX; + sqInt offsetY; + + maskBitsObj = 0; + if ((((usqInt)GIV(argumentCount))) > 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + cursorObj = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + if (!((/* isPointers: */ + ((!(cursorObj & (tagMask())))) + && (((byteAt((void *)(cursorObj + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(cursorObj)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(cursorObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(cursorObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= 5))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + cursorBitsObj = longAt((void *)((cursorObj + BaseHeaderSize) + (0U << (shiftForWord())))); + offsetObj = longAt((void *)((cursorObj + BaseHeaderSize) + (4U << (shiftForWord())))); + if (!(((((((extentX = longAt((void *)((cursorObj + BaseHeaderSize) + (1U << (shiftForWord()))))))) & 7) == 1)) + && (((((((extentY = longAt((void *)((cursorObj + BaseHeaderSize) + (2U << (shiftForWord()))))))) & 7) == 1)) + && (((((((depth = longAt((void *)((cursorObj + BaseHeaderSize) + (3U << (shiftForWord()))))))) & 7) == 1)) + && ((/* isPointers: */ + ((!(offsetObj & (tagMask())))) + && (((byteAt((void *)(offsetObj + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(offsetObj)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(offsetObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(offsetObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= 2) + && (((((((offsetX = longAt((void *)((offsetObj + BaseHeaderSize) + (0U << (shiftForWord()))))))) & 7) == 1)) + && ((((((offsetY = longAt((void *)((offsetObj + BaseHeaderSize) + (1U << (shiftForWord()))))))) & 7) == 1))))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + offsetX = (offsetX >> 3); + offsetY = (offsetY >> 3); + extentX = (extentX >> 3); + extentY = (extentY >> 3); + depth = (depth >> 3); + if (!GIV(argumentCount)) { + if (depth == 32) { + if (!((extentX > 0) + && ((extentY > 0) + && ((((offsetX >= (-extentX)) && (offsetX <= 0))) + && ((((offsetY >= (-extentY)) && (offsetY <= 0))) + && ((/* isWords: */ + ((!(cursorBitsObj & (tagMask())))) + && (((((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1))))) + && ((lengthOfformat(cursorBitsObj, firstLongFormat())) == (extentX * extentY)))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* This should pass (objectMemory firstIndexableField: cursorBitsObj type: #'unsigned int *'), but the platform sources are venerable and expect sqInts */ + ioSetCursorARGB(((sqInt) (firstIndexableField(cursorBitsObj))), extentX, extentY, offsetX, offsetY); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return; + } + + /* Support arbitrary-sized 32 bit ARGB forms --bf 3/1/2007 23:51 */ + if (!((extentX == 16) + && ((extentY == 16) + && ((((offsetX >= -16) && (offsetX <= 0))) + && ((((offsetY >= -16) && (offsetY <= 0))) + && ((/* isWords: */ + ((!(cursorBitsObj & (tagMask())))) + && (((((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1))))) + && ((lengthOfformat(cursorBitsObj, firstLongFormat())) == 16))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* This should pass (objectMemory firstIndexableField: cursorBitsObj type: #'unsigned int *'), but the platform sources are venerable and expect sqInts */ + ioSetCursor(((sqInt) (firstIndexableField(cursorBitsObj))), offsetX, offsetY); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return; + } + if (!((extentX == 16) + && ((extentY == 16) + && ((((offsetX >= -16) && (offsetX <= 0))) + && ((((offsetY >= -16) && (offsetY <= 0))) + && ((/* isWords: */ + ((!(cursorBitsObj & (tagMask())))) + && (((((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(cursorBitsObj + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1))))) + && ((lengthOfformat(cursorBitsObj, firstLongFormat())) == 16))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + maskObj = longAt(GIV(stackPointer)); + if (!((/* isPointers: */ + ((!(maskObj & (tagMask())))) + && (((byteAt((void *)(maskObj + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(maskObj)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(maskObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(maskObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= 5) + && (((longAt((void *)((maskObj + BaseHeaderSize) + (1U << (shiftForWord()))))) == ((((usqInt)16 << 3) | 1))) + && (((longAt((void *)((maskObj + BaseHeaderSize) + (2U << (shiftForWord()))))) == ((((usqInt)16 << 3) | 1))) + && (((longAt((void *)((maskObj + BaseHeaderSize) + (3U << (shiftForWord()))))) == ((((usqInt)1 << 3) | 1))) + && ((isWords((maskBitsObj = longAt((void *)((maskObj + BaseHeaderSize) + (0U << (shiftForWord()))))))) + && ((lengthOfformat(maskBitsObj, firstLongFormat())) == 16)))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* This should pass (objectMemory firstIndexableField: cursorBitsObj type: #'unsigned int *'), but the platform sources are venerable and expect sqInts */ + ioSetCursorWithMask(((sqInt) (firstIndexableField(cursorBitsObj))), ((sqInt) (firstIndexableField(maskBitsObj))), offsetX, offsetY); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* Record the system Display object in the specialObjectsTable, + and if possible pin the display bitmap. Further, invoke ioBeDisplay + to alow the VM to record the location, width heigth & depth of the bitmap. */ + + /* InterpreterPrimitives>>#primitiveBeDisplay */ +static void +primitiveBeDisplay(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bitsOop; + void *bitsOrHandle; + sqInt depthOop; + sqInt heightOop; + sqInt pinnedBits; + sqInt rcvr; + sqInt widthOop; + + rcvr = longAt(GIV(stackPointer)); + if (!((/* isPointers: */ + ((!(rcvr & (tagMask())))) + && (((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && (((lengthOf(rcvr)) >= 4) + && (((bitsOop = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord()))))), + ((/* isWordsOrBytes: */ + ((!(bitsOop & (tagMask())))) + && (isWordsOrBytesNonImm(bitsOop))) + || ((((bitsOop) & 7) == 1))) + && (((((((widthOop = longAt((void *)((rcvr + BaseHeaderSize) + (1U << (shiftForWord()))))))) & 7) == 1)) + && (((((((heightOop = longAt((void *)((rcvr + BaseHeaderSize) + (2U << (shiftForWord()))))))) & 7) == 1)) + && ((((((depthOop = longAt((void *)((rcvr + BaseHeaderSize) + (3U << (shiftForWord()))))))) & 7) == 1))))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheDisplay, GIV(specialObjectsOop), rcvr)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(rcvr & (tagMask())))) + && (oopisLessThan(rcvr, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheDisplay) << (shiftForWord()))))),rcvr); + if (((!(bitsOop & (tagMask())))) + && (!(((byteAt((void *)(bitsOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0))) { + /* Answers 0 if memory required to pin but not enough memory available. */ + pinnedBits = pinObject(bitsOop); + if (pinnedBits) { + bitsOop = pinnedBits; + } + } + bitsOrHandle = ((!(bitsOop & (tagMask()))) + ? firstIndexableField(bitsOop) + : ((void *)bitsOop)); + + /* begin ioBeDisplay:width:height:depth: */ + displayBits = bitsOrHandle; + displayWidth = (widthOop >> 3); + displayHeight = (heightOop >> 3); + displayDepth = (depthOop >> 3); + ioNoteDisplayChangedwidthheightdepth(bitsOrHandle, (widthOop >> 3), (heightOop >> 3), (depthOop >> 3)); +} + + +/* make the basic beep noise */ + + /* InterpreterPrimitives>>#primitiveBeep */ +static void +primitiveBeep(void) +{ + ioBeep(); +} + + +/* For the mirror prims check that the class obj is actually a valid class. + No need to check if it's the receiver since the method exists only on + Behavior. + */ + + /* InterpreterPrimitives>>#primitiveBehaviorHash */ +static void +primitiveBehaviorHash(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt err; + int hash; + sqInt hashOrError; + sqInt shouldBeClassObj; + + shouldBeClassObj = longAt(GIV(stackPointer)); + if (GIV(argumentCount) > 0) { + if ((((shouldBeClassObj & (tagMask())) != 0)) + || (((!((longAt((void *)(shouldBeClassObj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + || (!(objCouldBeClassObj(shouldBeClassObj))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + assert((isNonImmediate(shouldBeClassObj)) + && (addressCouldBeClassObj(shouldBeClassObj))); + + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(shouldBeClassObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + hashOrError = ((hash = (long32At((void *)(shouldBeClassObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(shouldBeClassObj) + ? ((err = enterIntoClassTable(shouldBeClassObj)) + ? -err + : (long32At((void *)(shouldBeClassObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (hashOrError < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -hashOrError; + return; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)hashOrError << 3) | 1)); +} + + +/* Note no short-cut for SmallIntegers. Either the inline interpreter + bytecode or the JIT primitive will handle this case. */ + + /* InterpreterPrimitives>>#primitiveBitAnd */ +static void +primitiveBitAnd(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt integerArgumentValue; + usqInt integerReceiverValue; + sqInt oop; + sqInt value; + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArgumentValue = ((usqIntptr_t) null); + goto l1; + } + integerArgumentValue = ((usqIntptr_t) value); + goto l1; + } + + /* don't inline the rare case */ + integerArgumentValue = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin positiveMachineIntegerValueOf: */ + if ((((oop) & 7) == 1)) { + value = (oop >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerReceiverValue = ((usqIntptr_t) null); + goto l2; + } + integerReceiverValue = ((usqIntptr_t) value); + goto l2; + } + + /* don't inline the rare case */ + integerReceiverValue = positiveMachineIntegerValueOfObj(oop); + /* end positiveMachineIntegerValueOf: */ +l2: + if (!GIV(primFailCode)) { + oop = positive64BitIntegerFor(integerArgumentValue & integerReceiverValue); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oop); + } +} + + +/* Primitive logical operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveBitAndLargeIntegers */ +EXPORT(void) +primitiveBitAndLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong integerArg; + usqLong integerRcvr; + sqInt oopResult; + + integerArg = positive64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = positive64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (GIV(primFailCode)) { + return; + } + oopResult = positive64BitIntegerFor(integerRcvr & integerArg); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Note no short-cut for SmallIntegers. Either the inline interpreter + bytecode or the JIT primitive will handle this case. */ + + /* InterpreterPrimitives>>#primitiveBitOr */ +static void +primitiveBitOr(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt integerArgumentValue; + usqInt integerReceiverValue; + sqInt oop; + sqInt value; + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArgumentValue = ((usqIntptr_t) null); + goto l1; + } + integerArgumentValue = ((usqIntptr_t) value); + goto l1; + } + + /* don't inline the rare case */ + integerArgumentValue = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin positiveMachineIntegerValueOf: */ + if ((((oop) & 7) == 1)) { + value = (oop >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerReceiverValue = ((usqIntptr_t) null); + goto l2; + } + integerReceiverValue = ((usqIntptr_t) value); + goto l2; + } + + /* don't inline the rare case */ + integerReceiverValue = positiveMachineIntegerValueOfObj(oop); + /* end positiveMachineIntegerValueOf: */ +l2: + if (!GIV(primFailCode)) { + oop = positive64BitIntegerFor(integerArgumentValue | integerReceiverValue); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oop); + } +} + + +/* Primitive logical operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveBitOrLargeIntegers */ +EXPORT(void) +primitiveBitOrLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong integerArg; + usqLong integerRcvr; + sqInt oopResult; + + integerArg = positive64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = positive64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (GIV(primFailCode)) { + return; + } + oopResult = positive64BitIntegerFor(integerRcvr | integerArg); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Perform a bitShift. In 32-bits deal only with non-negative 32-bit + integers. In 64-bits deal with signed 64-bit quantities (max (2^63)-1). */ + + /* InterpreterPrimitives>>#primitiveBitShift */ +static void +primitiveBitShift(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + sqInt shifted; + + integerArgument = longAt(GIV(stackPointer)); + if (!((((integerArgument) & 7) == 1))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + integerReceiver = signed64BitValueOf(integerReceiver); + if (!GIV(primFailCode)) { + if (((integerArgument = (integerArgument >> 3))) >= 0) { + if (!(integerArgument <= 61 /* numSmallIntegerBits */)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + shifted = ((sqInt)((usqInt)(integerReceiver) << integerArgument)); + if (!(integerReceiver == ((shifted) >> integerArgument))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + } + else { + if (!(integerArgument >= (-61 /* numSmallIntegerBits */))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + shifted = (integerReceiver) >> (0 - integerArgument); + } + + /* Left shift -- must fail bits would be lost + Right shift -- OK to lose bits */ + shifted = ((((((usqInt)(shifted)) >> 60) + 1) & 15) <= 1 + ? (((usqInt)shifted << 3) | 1) + : signed64BitIntegerFor(shifted)); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),shifted); + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveBitShiftLargeIntegers */ +EXPORT(void) +primitiveBitShiftLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + sqInt integerPointer; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + sqInt shift; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + shift = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + shift = 0; + } + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + a = magnitude64BitValueOf(oopRcvr); + if (GIV(primFailCode)) { + return; + } + if (shift >= 0) { + /* Protect against overflow */ + + /* This is to avoid undue (usqInt) cast */ + result = 0xFFFFFFFFFFFFFFFFULL; + if ((shift >= 64) + || (a > ((result) >> shift))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + result = (a << shift); + } + else { + shift = 0 - shift; + if (shift >= 64) { + result = 0; + } + else { + result = (a) >> shift; + } + + /* Fake 2 complement for negative values */ + if (aIsNegative + && (((result << shift)) != a)) { + result += 1; + } + } + oopResult = magnitude64BitIntegerForneg(result, aIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + /* InterpreterPrimitives>>#primitiveBitXor */ +static void +primitiveBitXor(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + usqInt integerArgumentValue; + sqInt integerReceiver; + usqInt integerReceiverValue; + sqInt oop; + sqInt value; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((((integerArgument) & 7) == 1)) + && ((((integerReceiver) & 7) == 1))) { + oop = (integerArgument ^ integerReceiver) + (smallIntegerTag()); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oop); + } + else { + /* begin positiveMachineIntegerValueOf: */ + if ((((integerArgument) & 7) == 1)) { + value = (integerArgument >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArgumentValue = ((usqIntptr_t) null); + goto l1; + } + integerArgumentValue = ((usqIntptr_t) value); + goto l1; + } + + /* don't inline the rare case */ + integerArgumentValue = positiveMachineIntegerValueOfObj(integerArgument); + /* end positiveMachineIntegerValueOf: */ +l1: + + /* begin positiveMachineIntegerValueOf: */ + if ((((integerReceiver) & 7) == 1)) { + value = (integerReceiver >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerReceiverValue = ((usqIntptr_t) null); + goto l2; + } + integerReceiverValue = ((usqIntptr_t) value); + goto l2; + } + + /* don't inline the rare case */ + integerReceiverValue = positiveMachineIntegerValueOfObj(integerReceiver); + /* end positiveMachineIntegerValueOf: */ +l2: + if (!GIV(primFailCode)) { + oop = positive64BitIntegerFor(integerArgumentValue ^ integerReceiverValue); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oop); + } + } +} + + +/* Primitive logical operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveBitXorLargeIntegers */ +EXPORT(void) +primitiveBitXorLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong integerArg; + usqLong integerRcvr; + sqInt oopResult; + + integerArg = positive64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = positive64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (GIV(primFailCode)) { + return; + } + oopResult = positive64BitIntegerFor(integerRcvr ^ integerArg); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Answer the encoder names for the supported bytecode sets. */ + + /* InterpreterPrimitives>>#primitiveBytecodeSetsAvailable */ +EXPORT(sqInt) +primitiveBytecodeSetsAvailable(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt encoderNames; + usqInt newObj; + usqInt numBytes; + sqInt valuePointer; + + if (GIV(argumentCount) > 0) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((knownClassAtIndex(ClassArrayCompactIndex)) != GIV(nilObj)); + assert((arrayFormat()) == (instSpecOfClass(knownClassAtIndex(ClassArrayCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (2 * BytesPerOop); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + encoderNames = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) 2)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + GIV(freeStart) += numBytes; + encoderNames = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + valuePointer = stringForCString("EncoderForV3PlusClosures"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(encoderNames)) + && (!(isForwarded(encoderNames)))); + assert(validStorePointerUncheckedArgs(0, encoderNames, valuePointer)); + longAtput((void *)((encoderNames + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + valuePointer = stringForCString("EncoderForSistaV1"); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(encoderNames)) + && (!(isForwarded(encoderNames)))); + assert(validStorePointerUncheckedArgs(1, encoderNames, valuePointer)); + longAtput((void *)((encoderNames + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),encoderNames); + return 0; +} + + +/* Answer bytes available at this moment. For more meaningful + results, calls to this primitive should be precedeed by a full + or incremental garbage collection. */ + + /* InterpreterPrimitives>>#primitiveBytesLeft */ +static void +primitiveBytesLeft(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aBool; + sqInt chunk; + sqInt freeChunk; + sqInt i; + sqInt integerVal; + + if (!GIV(argumentCount)) { + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),((GIV(totalFreeOldSpace) << 3) | 1)); + return; + } + + /* old behavior - just return the size of free memory */ + if (GIV(argumentCount) == 1) { + /* Spur behavior; if argument is nil answer size of largest free chunk in oldSpace. */ + if ((longAt(GIV(stackPointer))) == GIV(nilObj)) { + /* begin sizeOfLargestFreeChunk */ + freeChunk = findLargestFreeChunk(); + if (!freeChunk) { + for (i = 0x3F; i >= 1; i += -1) { + if ((chunk = GIV(freeLists)[i])) { + integerVal = bytesInBody(chunk); + goto l1; + } + } + integerVal = 0; + goto l1; + } + integerVal = bytesInBody(freeChunk); + /* end sizeOfLargestFreeChunk */ +l1: + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerVal << 3) | 1)); + return; + } + + /* new behaviour -including or excluding swap space depending on aBool */ + + /* begin booleanValueOf: */ + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + aBool = 1; + goto l2; + } + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + aBool = 0; + goto l2; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + aBool = null; + /* end booleanValueOf: */ +l2: + if (!GIV(primFailCode)) { + integerVal = ((((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes())) < 0) ? 0 : ((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes()))); + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerVal << 3) | 1)); + return; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; +} + + +/* Perform a function call to a foreign function. + Only invoked from method containing explicit external call spec. + Due to this we use the pluggable prim mechanism explicitly here + (the first literal of any FFI spec'ed method is an ExternalFunction + and not an array as used in the pluggable primitive mechanism). */ +/* Manually copied from primitiveCalloutAccessorDepth in the + ThreadedFFIPlugins... + */ +/* since call may invoke a callback */ +/* since call signature is in first literal... */ + + /* InterpreterPrimitives>>#primitiveCalloutToFFI */ +static void +primitiveCalloutToFFI(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*primitiveCallout)(void); + + /* begin functionForPrimitiveCallout */ + if ((((sqInt)primitiveCalloutPointer)) == -1) { + reloadPrimitiveCalloutPointer(); + } + primitiveCallout = ((void (*)(void)) primitiveCalloutPointer); + if (primitiveCallout) { + dispatchFunctionPointer(primitiveCallout); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive. Change the class of the receiver into the class of the argument + given that + the format of the receiver matches the format of the argument's class. + Fail if the + receiver or argument are SmallIntegers, or the receiver is an instance of + a compact + class and the argument isn't, or when the argument's class is compact and + the receiver + isn't, or when the format of the receiver is different from the format of + the argument's + class, or when the arguments class is fixed and the receiver's size + differs from the size + that an instance of the argument's class should have. */ + + /* InterpreterPrimitives>>#primitiveChangeClass */ +static void +primitiveChangeClass(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt argClass; + sqInt err; + sqInt oop; + sqInt rcvr; + + /* begin stackObjectValue: */ + oop = longAt(GIV(stackPointer)); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = null; + goto l1; + } + arg = oop; + /* end stackObjectValue: */ +l1: + + /* begin stackObjectValue: */ + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = null; + goto l2; + } + rcvr = oop; + /* end stackObjectValue: */ +l2: + if (GIV(primFailCode)) { + return; + } + argClass = fetchClassOfNonImm(arg); + err = changeClassOfto(rcvr, argClass); + if (err) { + /* primitiveFailFor: */ + GIV(primFailCode) = err; + } + else { + /* Flush at cache because rcvr's class has changed. */ + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + return; +} + + /* InterpreterPrimitives>>#primitiveClass */ +static void +primitiveClass(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt instance; + sqInt oop; + sqInt tagBits; + + instance = longAt(GIV(stackPointer)); + if ((GIV(argumentCount) > 0) + && (/* isOopForwarded: */ + ((!(instance & (tagMask())))) + && ((!((longAt((void *)(instance))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + else { + oop = /* fetchClassOf: */ + ((tagBits = instance & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(instance)); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + } +} + + +/* Primitive. Void the VM profile histograms. */ + + /* InterpreterPrimitives>>#primitiveClearVMProfile */ +static void +primitiveClearVMProfile(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + ioClearProfile(); + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* When called with a single string argument, post the string to + the clipboard. When called with zero arguments, return a + string containing the current clipboard contents. */ + + /* InterpreterPrimitives>>#primitiveClipboardText */ +static void +primitiveClipboardText(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + usqInt numSlots; + sqInt s; + sqInt sz; + + if (GIV(argumentCount) == 1) { + s = longAt(GIV(stackPointer)); + if (!(/* isBytes: */ + ((!(s & (tagMask())))) + && (((byteAt((void *)(s + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + if (!GIV(primFailCode)) { + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(s + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + sz = ((((/* begin numSlotsOf: */ + assert((classIndexOf(s)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(s + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(s - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + clipboardWriteFromAt(sz, s + BaseHeaderSize, 0); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + } + else { + sz = clipboardSize(); + + /* begin allocateBytes:classIndex: */ + assert(addressCouldBeClassObj(classAtIndex(ClassByteStringCompactIndex))); + assert((instSpecOfClass(classAtIndex(ClassByteStringCompactIndex))) == (firstByteFormat())); + s = noInlineAllocateSlotsformatclassIndex(((sz + BytesPerOop) - 1) / BytesPerOop, byteFormatForNumBytes(sz), ClassByteStringCompactIndex); + if (!s) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + clipboardReadIntoAt(sz, s + BaseHeaderSize, 0); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),s); + } +} + + +/* Take a boolean which if true turns or keeps clock logging on. Answer an + array supplying + the size of the clock logs, the address of the usecs log, the index in it, + the address of the + msecs log, and the index into it. */ + + /* InterpreterPrimitives>>#primitiveClockLogAddresses */ +EXPORT(sqInt) +primitiveClockLogAddresses(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt midx; + void *msecs; + sqInt result; + sqInt runInNOut; + sqInt uidx; + void *usecs; + sqInt v1; + sqInt v2; + + + /* bypass type inference which would deduce int */ + midx = 0; + msecs = ((void *) 0); + uidx = 0; + usecs = ((void *) 0); + if (GIV(argumentCount) != 1) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + runInNOut = (longAt(GIV(stackPointer))) == GIV(trueObj); + ioGetClockLogSizeUsecsIdxMsecsIdx((&runInNOut), (&usecs), (&uidx), (&msecs), (&midx)); + result = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 5); + if (!result) { + return (GIV(primFailCode) = PrimErrNoMemory); + } + +#if SPURVM + v1 = positive64BitIntegerFor(((usqInt)usecs)); + v2 = positive64BitIntegerFor(((usqInt)msecs)); +#else /* SPURVM */ + pushRemappableOop(result); + v1 = positive64BitIntegerFor(((usqInt)usecs)); + pushRemappableOop(v1); + v2 = positive64BitIntegerFor(((usqInt)msecs)); + v1 = popRemappableOop(); + result = popRemappableOop(); +#endif /* SPURVM */ + if (GIV(primFailCode)) { + return (GIV(primFailCode) = PrimErrNoMemory); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(result)) + && (!(isForwarded(result)))); + assert(validStorePointerUncheckedArgs(0, result, (((usqInt)runInNOut << 3) | 1))); + longAtput((void *)((result + BaseHeaderSize) + (0U << (shiftForWord()))),(((usqInt)runInNOut << 3) | 1)); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(1, result, v1)); + assert(isNonImmediate(result)); + if (oopisGreaterThanOrEqualTo(result, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(v1 & (tagMask())))) + && (oopisLessThan(v1, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(result + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(result); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((result + BaseHeaderSize) + (1U << (shiftForWord()))),v1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(result)) + && (!(isForwarded(result)))); + assert(validStorePointerUncheckedArgs(2, result, (((usqInt)uidx << 3) | 1))); + longAtput((void *)((result + BaseHeaderSize) + (2U << (shiftForWord()))),(((usqInt)uidx << 3) | 1)); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(3, result, v2)); + assert(isNonImmediate(result)); + if (oopisGreaterThanOrEqualTo(result, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(v2 & (tagMask())))) + && (oopisLessThan(v2, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(result + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(result); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((result + BaseHeaderSize) + (3U << (shiftForWord()))),v2); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(result)) + && (!(isForwarded(result)))); + assert(validStorePointerUncheckedArgs(4, result, (((usqInt)midx << 3) | 1))); + longAtput((void *)((result + BaseHeaderSize) + (4U << (shiftForWord()))),(((usqInt)midx << 3) | 1)); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); + return 0; +} + + /* InterpreterPrimitives>>#primitiveClosureValue */ +static void +primitiveClosureValue(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt blockClosure; + sqInt closureIP; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt i; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt outerContext; + sqInt switched; + + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(GIV(argumentCount) == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Somewhat paranoiac checks we need while debugging that we may be able to discard + in a robust system. */ + outerContext = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + if (!(/* isContext: */ + ((!(outerContext & (tagMask())))) + && (((longAt((void *)(outerContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* Check if the closure's method is actually a CompiledMethod. */ + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin activateNewClosure:outer:method:numArgs:mayContextSwitch: */ + assert(isContext(outerContext)); + assert(isVanillaBlockClosure(blockClosure)); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - ClosureFirstCopiedValueIndex; + assert(closureMethod == (fetchPointerofObject(MethodIndex, outerContext))); + assert(isOopCompiledMethod(closureMethod)); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + ((cogMethod->blockEntryOffset)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogselector(closureMethod, GIV(nilObj)); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + ((cogMethod->blockEntryOffset)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(ReceiverIndex, outerContext, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + ClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* The initial instructions in the block nil-out remaining temps. + the instruction pointer is a pointer variable equal to + method oop + ip + BaseHeaderSize + -1 for 0-based addressing of fetchByte + -1 because it gets incremented BEFORE fetching currentByte */ + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + closureIP = (oop >> 3); + GIV(instructionPointer) = ((closureMethod + closureIP) + BaseHeaderSize) - 2; + + /* begin setMethod:methodHeader: */ + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(1); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewClosure:outer:method:numArgs:mayContextSwitch: */ +l1:; +} + + +/* An exact clone of primitiveClosureValue except that this version will not + check for interrupts on stack overflow. It may invoke the garbage + collector but will not switch processes. See + checkForInterruptsMayContextSwitch: */ + + /* InterpreterPrimitives>>#primitiveClosureValueNoContextSwitch */ +void +primitiveClosureValueNoContextSwitch(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt blockClosure; + sqInt closureIP; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt i; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt outerContext; + sqInt switched; + + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(GIV(argumentCount) == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Somewhat paranoiac checks we need while debugging that we may be able to discard + in a robust system. */ + outerContext = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + if (!(/* isContext: */ + ((!(outerContext & (tagMask())))) + && (((longAt((void *)(outerContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* Check if the closure's method is actually a CompiledMethod. */ + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + GIV(deferProfileCheckForVNCS) = 1; + + /* begin activateNewClosure:outer:method:numArgs:mayContextSwitch: */ + assert(isContext(outerContext)); + assert(isVanillaBlockClosure(blockClosure)); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - ClosureFirstCopiedValueIndex; + assert(closureMethod == (fetchPointerofObject(MethodIndex, outerContext))); + assert(isOopCompiledMethod(closureMethod)); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (((cogMethod->blockEntryOffset)) - (noContextSwitchBlockEntryOffset())); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogselector(closureMethod, GIV(nilObj)); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (((cogMethod->blockEntryOffset)) - (noContextSwitchBlockEntryOffset())); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(ReceiverIndex, outerContext, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + ClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* The initial instructions in the block nil-out remaining temps. + the instruction pointer is a pointer variable equal to + method oop + ip + BaseHeaderSize + -1 for 0-based addressing of fetchByte + -1 because it gets incremented BEFORE fetching currentByte */ + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + closureIP = (oop >> 3); + GIV(instructionPointer) = ((closureMethod + closureIP) + BaseHeaderSize) - 2; + + /* begin setMethod:methodHeader: */ + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(0); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewClosure:outer:method:numArgs:mayContextSwitch: */ +l1:; +} + + /* InterpreterPrimitives>>#primitiveClosureValueWithArgs */ +static void +primitiveClosureValueWithArgs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt argumentArray; + usqInt arraySize; + sqInt blockClosure; + sqInt closureIP; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt i; + sqInt index; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt outerContext; + sqInt switched; + sqInt top; + + argumentArray = longAt(GIV(stackPointer)); + if (!(/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Check for enough space in thisContext to push all args */ + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (!(arraySize <= (LargeContextSlots - CtxtTempFrameStart))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(arraySize == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Somewhat paranoiac checks we need while debugging that we may be able to discard + in a robust system. */ + outerContext = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord())))))); + if (!(/* isContext: */ + ((!(outerContext & (tagMask())))) + && (((longAt((void *)(outerContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* Check if the closure's method is actually a CompiledMethod. */ + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* Copy the arguments to the stack, and activate */ + index = 1; + while (index <= numArgs) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + index += 1; + } + + /* begin activateNewClosure:outer:method:numArgs:mayContextSwitch: */ + assert(isContext(outerContext)); + assert(isVanillaBlockClosure(blockClosure)); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - ClosureFirstCopiedValueIndex; + assert(closureMethod == (fetchPointerofObject(MethodIndex, outerContext))); + assert(isOopCompiledMethod(closureMethod)); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + ((cogMethod->blockEntryOffset)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogselector(closureMethod, GIV(nilObj)); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + ((cogMethod->blockEntryOffset)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((outerContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(ReceiverIndex, outerContext, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + ClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* The initial instructions in the block nil-out remaining temps. + the instruction pointer is a pointer variable equal to + method oop + ip + BaseHeaderSize + -1 for 0-based addressing of fetchByte + -1 because it gets incremented BEFORE fetching currentByte */ + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + closureIP = (oop >> 3); + GIV(instructionPointer) = ((closureMethod + closureIP) + BaseHeaderSize) - 2; + + /* begin setMethod:methodHeader: */ + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(1); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewClosure:outer:method:numArgs:mayContextSwitch: */ +l1:; +} + + +/* Return the value of the microsecond clock in the local timezone, as + updated by the heartbeat, as an integer. + This is the number of microseconds since the Smalltalk epoch, 1901/1/1 + 12:00am. The microsecond clock is at least 60 bits wide which means it'll + get to around August + 38435 before it wraps around. Be sure to put it on your calendar. The + coarse clock is + updated by the heartbeat thread and as such is much cheaper than + primitiveUTCMicrosecondClock, which always entails a system call. */ + + /* InterpreterPrimitives>>#primitiveCoarseLocalMicrosecondClock */ +static void +primitiveCoarseLocalMicrosecondClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = positive64BitIntegerFor(ioLocalMicroseconds()); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Return the value of the microsecond clock as updated by the heartbeat as + an integer. + This is the number of microseconds since the Smalltalk epoch, 1901/1/1 + 12:00am. The microsecond clock is at least 60 bits wide which means it'll + get to around August + 38435 before it wraps around. Be sure to put it on your calendar. The + coarse clock is + updated by the heartbeat thread and as such is much cheaper than + primitiveUTCMicrosecondClock, which always entails a system call. */ + + /* InterpreterPrimitives>>#primitiveCoarseUTCMicrosecondClock */ +static void +primitiveCoarseUTCMicrosecondClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = positive64BitIntegerFor(ioUTCMicroseconds()); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Primitive. Compare two byte-indexed objects for equality */ + + /* InterpreterPrimitives>>#primitiveCompareBytes */ +EXPORT(sqInt) +primitiveCompareBytes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg1; + sqInt arg2; + sqInt fmt; + sqInt i; + sqInt len; + usqInt numSlots; + + arg1 = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + arg2 = longAt(GIV(stackPointer)); + + /* Quick identity test */ + if (arg1 == arg2) { + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(trueObj)); + return 0; + } + if (!((/* isBytes: */ + ((!(arg1 & (tagMask())))) + && (((byteAt((void *)(arg1 + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && (/* isBytes: */ + ((!(arg2 & (tagMask())))) + && (((byteAt((void *)(arg2 + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(arg1 + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + len = ((((/* begin numSlotsOf: */ + assert((classIndexOf(arg1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(arg1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(arg1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (!(len == (numBytesOfBytes(arg2)))) { + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(falseObj)); + return 0; + } + for (i = 0; i < len; i += 1) { + if (!((byteAt((void *)((arg1 + BaseHeaderSize) + i))) == (byteAt((void *)((arg2 + BaseHeaderSize) + i))))) { + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(falseObj)); + return 0; + } + } + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(trueObj)); + return 0; +} + + +/* primitiveCompareWith: string2 [collated: order] */ +/* 1 - fetch the parameters from the stack */ + + /* InterpreterPrimitives>>#primitiveCompareWith */ +EXPORT(void) +primitiveCompareWith(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt c1; + sqInt c2; + sqInt fmt; + sqInt i; + sqInt min; + usqInt numSlots; + sqInt order; + sqInt result; + sqInt string1; + sqInt string2; + sqInt strLength1; + sqInt strLength2; + + order = 0; + if (!(GIV(argumentCount) == 1)) { + if (GIV(argumentCount) != 2) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + order = longAt(GIV(stackPointer)); + if (!((/* isBytes: */ + ((!(order & (tagMask())))) + && (((byteAt((void *)(order + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((numBytesOfBytes(order)) == 0x100))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + string1 = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* 2 - check their types - all parameters are ByteObject */ + string2 = longAt(GIV(stackPointer) + ((GIV(argumentCount) - 1) * BytesPerWord)); + if (!((/* isBytes: */ + ((!(string1 & (tagMask())))) + && (((byteAt((void *)(string1 + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && (/* isBytes: */ + ((!(string2 & (tagMask())))) + && (((byteAt((void *)(string2 + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* 3 - compare the strings */ + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(string1 + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + strLength1 = ((((/* begin numSlotsOf: */ + assert((classIndexOf(string1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(string1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(string1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(string2 + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + strLength2 = ((((/* begin numSlotsOf: */ + assert((classIndexOf(string2)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(string2 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(string2 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (order) { + /* begin rawCompare:length:with:length:accessBlock: */ + min = ((strLength1 < strLength2) ? strLength1 : strLength2); + for (i = 0; i < min; i += 1) { + c1 = byteAt((void *)((order + BaseHeaderSize) + ((byteAt((void *)((string1 + BaseHeaderSize) + i))) + 1))); + c2 = byteAt((void *)((order + BaseHeaderSize) + ((byteAt((void *)((string2 + BaseHeaderSize) + i))) + 1))); + if (!(c1 == c2)) { + result = c1 - c2; + goto l2; + } + } + result = strLength1 - strLength2; + /* end rawCompare:length:with:length:accessBlock: */ +l2:; + } + else { + /* begin rawCompare:length:with:length:accessBlock: */ + min = ((strLength1 < strLength2) ? strLength1 : strLength2); + for (i = 0; i < min; i += 1) { + c1 = byteAt((void *)((string1 + BaseHeaderSize) + i)); + c2 = byteAt((void *)((string2 + BaseHeaderSize) + i)); + if (!(c1 == c2)) { + result = c1 - c2; + goto l1; + } + } + result = strLength1 - strLength2; + /* end rawCompare:length:with:length:accessBlock: */ +l1:; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)result << 3) | 1)); +} + + +/* Fill the receiver, which must be an indexable non-pointer + object, with the given integer value. */ + + /* InterpreterPrimitives>>#primitiveConstantFill */ +static void +primitiveConstantFill(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt end; + usqLong fillValue; + sqInt format; + usqInt i; + sqInt oddBytes; + sqInt rcvr; + + /* begin primitiveConstantFillSpur */ + if (GIV(argumentCount) != 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + goto l1; + } + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(((!(rcvr & (tagMask())))) + && (((format = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()))) >= (sixtyFourBitIndexableFormat())))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l1; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l1; + } + fillValue = positive64BitValueOf(longAt(GIV(stackPointer))); + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + if (format >= (firstShortFormat())) { + if (format >= (firstByteFormat())) { + if ((fillValue > 0xFF) + || (format >= (firstCompiledMethodFormat()))) { + /* primitiveFailFor: */ + GIV(primFailCode) = (fillValue > 0xFF + ? PrimErrBadArgument + : PrimErrBadReceiver); + goto l1; + } + fillValue = ((fillValue + ((fillValue << 8))) + ((fillValue << 16))) + ((fillValue << 24)); + oddBytes = format & 7; + } + else { + if (fillValue > 0xFFFF) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + fillValue += (fillValue << 16); + oddBytes = ((format & 3) << 1); + } + fillValue += (fillValue << 32); + } + else { + if (format == (sixtyFourBitIndexableFormat())) { + oddBytes = 0; + } + else { + if (fillValue > 0xFFFFFFFFU) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + fillValue += (fillValue << 32); + oddBytes = ((format & 1) << 2); + } + } + end = addressAfter(rcvr); + i = rcvr + BaseHeaderSize; + while (i < end) { + long64Atput((void *)(i),fillValue); + i += 8; + } + + /* now ensure trailing bytes are zero */ + if (oddBytes > 0) { + fillValue = (fillValue) >> (8 * oddBytes); + long64Atput((void *)(i - 8),fillValue); + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + /* end primitiveConstantFillSpur */ +l1:; +} + + +/* Primitive. Start or stop the VM profiler. The first argument is a boolean + to switch profiling on or off. The second argument is an integer or nil. + If an integer it determines the maximum number of samples in the VM's + sample buffer. Answer the current number of samples in the buffer. */ + + /* InterpreterPrimitives>>#primitiveControlVMProfiling */ +static void +primitiveControlVMProfiling(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bufferSize; + sqInt numSamples; + sqInt onOffBar; + + if (GIV(argumentCount) != 2) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + if (((onOffBar = longAt(GIV(stackPointer) + (1 * BytesPerWord)))) == GIV(trueObj)) { + onOffBar = 1; + } + else { + if (onOffBar == GIV(falseObj)) { + onOffBar = 0; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + } + if (((bufferSize = longAt(GIV(stackPointer)))) == GIV(nilObj)) { + bufferSize = 0; + } + else { + if (!(((((bufferSize) & 7) == 1)) + && (((bufferSize = (bufferSize >> 3))) > 0))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + } + numSamples = ioControlNewProfile(onOffBar,bufferSize); + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),(((usqInt)numSamples << 3) | 1)); +} + + +/* Primitive. Copy the state of the receiver from the argument. + Fail if receiver and argument are of a different class. + Fail if the receiver or argument are contexts (because of context-to-stack + mapping). Fail if receiver and argument have different lengths (for + indexable objects). + Fail if the objects are not in a fit state to be copied (e.g. married + contexts and Cogged methods) */ + + /* InterpreterPrimitives>>#primitiveCopyObject */ +static void +primitiveCopyObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt fmt; + sqInt i; + usqInt length; + usqInt numBytes; + usqInt numSlots; + sqInt rcvr; + sqInt valuePointer; + + if (!(GIV(argumentCount) >= 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + arg = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if (((arg & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (((longAt((void *)(rcvr))) & (classIndexMask())) != ((longAt((void *)(arg))) & (classIndexMask()))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (isWordsOrBytesNonImm(rcvr)) { + /* begin numBytesOf: */ + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + length = numBytes - (fmt & 7); + goto l1; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + length = ((sqInt) numBytes); + goto l1; + } + if (fmt >= (firstShortFormat())) { + length = numBytes - (((fmt & 3) << 1)); + goto l1; + } + + /* fmt >= self firstLongFormat */ + length = numBytes - (((fmt & 1) << 2)); + /* end numBytesOf: */ +l1: + if (!((((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask())) == ((byteAt((void *)(arg + (formatFieldByteOffset())))) & (formatMask()))) + && (length == (numBytesOf(arg))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + memcpy(((void *)(rcvr + BaseHeaderSize)), ((void *)(arg + BaseHeaderSize)), length); + } + else { + if (!(isAppropriateForCopyObject(rcvr))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (!((isAppropriateForCopyObject(arg)) + && (length == (lengthOf(arg))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + for (i = 0; i < length; i += 1) { + valuePointer = longAt((void *)((arg + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(i, rcvr, valuePointer)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + } + + /* Note: The above could be faster for young receivers but I don't think it'll matter */ + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* Computes cosine of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveCosine */ +static void +primitiveCosine(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(cos(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + +/* Crash the VM by indirecting through a null pointer. If the sole argument + is true crash in this thread, and if it is false crash in a new thread. If + the argument is an integer use the method that implies. + bit 0 = thread to crash in; 1 => this thread + bit 1 = crash method; 0 => indirect through null pointer; 1 => call exit */ + + /* InterpreterPrimitives>>#primitiveCrashVM */ +EXPORT(sqInt) +primitiveCrashVM(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt crashInThisThread; + + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + crashInThisThread = ((longAt(GIV(stackPointer))) >> 3); + } + else { + /* begin booleanValueOf: */ + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + crashInThisThread = 1; + goto l1; + } + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + crashInThisThread = 0; + goto l1; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + crashInThisThread = null; + /* end booleanValueOf: */ +l1:; + } + if (GIV(primFailCode) + || (GIV(argumentCount) != 1)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + crashInThisOrAnotherThread(crashInThisThread); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + return 0; +} + + +/* Pass in a non-negative value to disable the architectures powermanager if + any, zero to enable. This is a named (not numbered) primitive in the null + module (ie the VM) + */ + + /* InterpreterPrimitives>>#primitiveDisablePowerManager */ +EXPORT(sqInt) +primitiveDisablePowerManager(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integer; + sqInt integerPointer; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + integer = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integer = 0; + } + if (!GIV(primFailCode)) { + ioDisablePowerManager(integer); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + return 0; +} + + /* InterpreterPrimitives>>#primitiveDiv */ +static void +primitiveDiv(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt quotient; + + quotient = doPrimitiveDivby(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)(quotient)) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)quotient << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } +} + + /* InterpreterPrimitives>>#primitiveDivide */ +static void +primitiveDivide(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerPointer; + sqInt integerReceiver; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + integerReceiver = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerReceiver = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + integerArgument = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArgument = 0; + } + if ((integerArgument != 0) + && ((integerReceiver % integerArgument) == 0)) { + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)((integerReceiver / integerArgument))) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)(integerReceiver / integerArgument) << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveDivideLargeIntegers */ +EXPORT(void) +primitiveDivideLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (GIV(primFailCode)) { + return; + } + + /* check for exact division */ + if (!((b != 0) + && ((a % b) == 0))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + result = a / b; + oopResult = magnitude64BitIntegerForneg(result, aIsNegative != bIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveDivLargeIntegers */ +EXPORT(void) +primitiveDivLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong rem; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (!b) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return; + } + result = a / b; + if (a) { + if (!(bIsNegative == aIsNegative)) { + /* Round toward negative infinity */ + rem = a % b; + if (rem) { + /* This can not overflow, because b > 1, otherwise rem = 0 */ + result += 1; + } + } + } + oopResult = magnitude64BitIntegerForneg(result, bIsNegative != aIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + /* InterpreterPrimitives>>#primitiveEqual */ +static void +primitiveEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + int result; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver == integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + result = (signed64BitValueOf(integerReceiver)) == (signed64BitValueOf(integerArgument)); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (result + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveEqualLargeIntegers */ +EXPORT(void) +primitiveEqualLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr == integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveExitToDebugger */ +static void +primitiveExitToDebugger(void) +{ + error("Exit to debugger at user request"); +} + + +/* Computes E raised to the receiver power. + Receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveExp */ +static void +primitiveExp(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(exp(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + +/* Exponent part of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveExponent */ +static void +primitiveExponent(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + double doubleValue; + int pwr; + sqInt rcvr; + + pwr = 0; + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + + /* rcvr = frac * 2^pwr, where frac is in [0.5..1.0) */ + frexp(doubleValue, (&pwr)); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),(((usqInt)(pwr - 1) << 3) | 1)); +} + + +/* Set general (unspecified) primitive failure. Don't overwrite an error code + that has already been set. */ +/* Use returnTypeC: #sqInt because that's the way it is defined in sq.h. + Use no explicit return so that Slang doesn't fail an inlining type-check + when a primitive with return type void uses ^self primitiveFail to exit. */ + + /* InterpreterPrimitives>>#primitiveFail */ +sqInt +primitiveFail(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; +} + + +/* Set PrimErrOSError primitive failure and associated osErrorCode. */ + + /* InterpreterPrimitives>>#primitiveFailForOSError: */ +sqInt +primitiveFailForOSError(sqLong osErrorCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(secondaryErrorCode) = osErrorCode; + return (GIV(primFailCode) = PrimErrOSError); +} + + +/* Set specific primitive failure. + N.B. primitiveFailFor: PrimNoErr is expected to clear the primFailCode. */ + + /* InterpreterPrimitives>>#primitiveFailFor: */ +sqInt +primitiveFailFor(sqInt reasonCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (GIV(primFailCode) = reasonCode); +} + + +/* Set primFailCode primitive failure and associated secondaryErrorCode. */ + + /* InterpreterPrimitives>>#primitiveFailFor:withSecondary: */ +sqInt +primitiveFailForwithSecondary(sqInt reasonCode, sqLong extraErrorCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(secondaryErrorCode) = extraErrorCode; + return (GIV(primFailCode) = reasonCode); +} + + /* InterpreterPrimitives>>#primitiveFailureCode */ +sqInt +primitiveFailureCode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(primFailCode); +} + + /* InterpreterPrimitives>>#primitiveFetchNextMourner */ +static void +primitiveFetchNextMourner(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt mourner; + + mourner = /* dequeueMourner */ + (GIV(mournQueue) != GIV(nilObj) + ? (assert(isValidObjStack(GIV(mournQueue))), + popObjStack(GIV(mournQueue))) + : 0); + if (mourner) { + assert(isValidObjStack(GIV(mournQueue))); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),mourner); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNotFound; + } +} + + /* InterpreterPrimitives>>#primitiveFloatAdd */ +static void +primitiveFloatAdd(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + sqInt argOop; + usqLong bits; + double rcvr; + sqInt rcvrOop; + double result; + char *sp; + sqInt tagBits; + double value; + + rcvrOop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + argOop = longAt(GIV(stackPointer)); + + /* begin primitiveFloatAdd:toArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l2: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr + arg)); + GIV(stackPointer) = sp; + } + return; +} + + +/* Index the receiver, which must be an indexable non-pointer object, and + yield a float. + */ + + /* InterpreterPrimitives>>#primitiveFloatArrayAt */ +static void +primitiveFloatArrayAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + float aFloat; + sqLong doubleBits; + sqInt fmt; + sqInt index; + usqInt newFloatObj; + usqInt newObj; + usqInt numBytes; + sqInt numSlots; + sqInt numSlotsSqInt; + sqInt oop; + usqLong rawFloatBits; + sqInt rcvr; + usqLong rot; + + /* begin primitiveSpurFloatArrayAt */ + index = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l3; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l3; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + doubleBits = long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3))))); + + /* begin floatObjectOfBits: */ + if (isSmallFloatValueBits(doubleBits)) { + rawFloatBits = doubleBits; + + /* begin smallFloatObjectOfBits: */ + assert(isSmallFloatValueBits(rawFloatBits)); + rot = (((rawFloatBits) >> 0x3F) & 1) + ((rawFloatBits << 1)); + if (rot > 1) { + rot -= (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + assert(rot > 0); + } + + /* a.k.a. ~= +/-0.0 */ + oop = ((rot << (numTagBits()))) + (smallFloatTag()); + goto l2; + } + numSlotsSqInt = (sizeof(double)) / BytesPerOop; + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((numSlotsSqInt >= 0) + && ((knownClassAtIndex(ClassFloatCompactIndex)) != GIV(nilObj))); + assert((firstLongFormat()) == (instSpecOfClass(knownClassAtIndex(ClassFloatCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlotsSqInt < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlotsSqInt < 1 + ? 8 /* allocationUnit */ + : numSlotsSqInt * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newFloatObj = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlotsSqInt)) << (numSlotsFullShift()))) + ((((usqInt)((firstLongFormat())) << (formatShift()))))) + ClassFloatCompactIndex); + GIV(freeStart) += numBytes; + newFloatObj = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((newFloatObj + BaseHeaderSize)),doubleBits); + oop = newFloatObj; + /* end floatObjectOfBits: */ +l2: + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + goto l3; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l3; + } + if ((fmt >= (firstLongFormat())) + && (fmt <= ((firstLongFormat()) + 1))) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + /* begin fetchFloat32:ofObject: */ + aFloat = singleFloatAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2))))); + oop = floatObjectOf(aFloat); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + goto l3; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l3; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + /* end primitiveSpurFloatArrayAt */ +l3:; +} + + +/* Index the receiver, which must be an indexable non-pointer object, and + store a float. + */ + + /* InterpreterPrimitives>>#primitiveFloatArrayAtPut */ +static void +primitiveFloatArrayAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + float aFloat; + usqLong bits; + sqInt fmt; + sqInt index; + sqInt numSlots; + sqInt rcvr; + double result; + sqInt tagBits; + sqLong value; + sqInt valueOop; + + /* begin primitiveSpurFloatArrayAtPut */ + valueOop = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (!((/* isFloatInstance: */ + ((tagBits = valueOop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(valueOop))) & (classIndexMask())) == ClassFloatCompactIndex)) + && ((((index) & 7) == 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l1; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l1; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + value = floatValueBitsOf(valueOop); + + /* begin storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3)))),value); + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + + /* N.B. Currently we simply truncate to 32-bits, which matches the behavior of the FloatArrayPlugin. + Maybe we should validate and range check. */ + if ((fmt >= (firstLongFormat())) + && (fmt <= ((firstLongFormat()) + 1))) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(valueOop)); + if (valueOop & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(valueOop)); + bits = ((((usqInt)valueOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&result), (&bits), sizeof(result)); + } + else { + fetchFloatAtinto(valueOop + BaseHeaderSize, result); + } + aFloat = result; + + /* begin storeFloat32:ofObject:withValue: */ + singleFloatAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2)))),aFloat); + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + /* end primitiveSpurFloatArrayAtPut */ +l1:; +} + + +/* Provide platform-independent access to 32-bit words comprising + a Float. Map index 1 onto the most significant word and index 2 + onto the least significant word. */ + + /* InterpreterPrimitives>>#primitiveFloatAt */ +static void +primitiveFloatAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + sqInt fieldIndex; + sqInt index; + unsigned int integerValue; + sqInt rcvr; + usqInt result; + + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + index = longAt(GIV(stackPointer)); + if (index == ConstOne) { + fieldIndex = (VMBIGENDIAN + ? 0 + : 1); + + /* begin fetchLong32:ofFloatObject: */ + if (!(((rcvr & (smallFloatTag())) != 0))) { + integerValue = long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2))))); + goto l1; + } + + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + integerValue = ((unsigned int) ((fieldIndex + ? (bits) >> 32 + : bits & 0xFFFFFFFFU))); + /* end fetchLong32:ofFloatObject: */ +l1: + + /* begin positive32BitIntegerFor: */ + result = (((usqInt)((((usqInt)integerValue)) & 0xFFFFFFFFU) << 3) | 1); + longAtput((GIV(stackPointer) += 1 * BytesPerWord),result); + return; + } + if (index == ConstTwo) { + fieldIndex = (VMBIGENDIAN + ? 1 + : 0); + + /* begin fetchLong32:ofFloatObject: */ + if (!(((rcvr & (smallFloatTag())) != 0))) { + integerValue = long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2))))); + goto l2; + } + + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + integerValue = ((unsigned int) ((fieldIndex + ? (bits) >> 32 + : bits & 0xFFFFFFFFU))); + /* end fetchLong32:ofFloatObject: */ +l2: + + /* begin positive32BitIntegerFor: */ + result = (((usqInt)((((usqInt)integerValue)) & 0xFFFFFFFFU) << 3) | 1); + longAtput((GIV(stackPointer) += 1 * BytesPerWord),result); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = ((((index) & 7) == 1) + ? PrimErrBadIndex + : PrimErrBadArgument); +} + + +/* Provide platform-independent access to 32-bit words comprising + a Float. Map index 1 onto the most significant word and index 2 + onto the least significant word. */ + + /* InterpreterPrimitives>>#primitiveFloatAtPut */ +static void +primitiveFloatAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt oopToStore; + sqInt rcvr; + usqInt valueToStore; + + oopToStore = longAt(GIV(stackPointer)); + valueToStore = positive32BitValueOf(oopToStore); + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((rcvr & (smallFloatTag())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + return; + } + if (index == ConstOne) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((VMBIGENDIAN + ? 0 + : 1))) << 2)))),valueToStore); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),oopToStore); + return; + } + if (index == ConstTwo) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((VMBIGENDIAN + ? 1 + : 0))) << 2)))),valueToStore); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),oopToStore); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = ((((index) & 7) == 1) + ? PrimErrBadIndex + : PrimErrBadArgument); +} + + /* InterpreterPrimitives>>#primitiveFloatDivide */ +static void +primitiveFloatDivide(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + sqInt argOop; + usqLong bits; + double rcvr; + sqInt rcvrOop; + double result; + char *sp; + sqInt tagBits; + double value; + + rcvrOop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + argOop = longAt(GIV(stackPointer)); + + /* begin primitiveFloatDivide:byArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l2: + + /* begin success: */ + if (!(arg != 0.0)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr / arg)); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatEqual */ +static void +primitiveFloatEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatEqualtoArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatGreaterOrEqual */ +static void +primitiveFloatGreaterOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatGreaterOrEqualtoArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatGreaterThan */ +static void +primitiveFloatGreaterThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatGreaterthanArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatLessOrEqual */ +static void +primitiveFloatLessOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatLessOrEqualtoArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatLessThan */ +static void +primitiveFloatLessThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatLessthanArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatMultiply */ +static void +primitiveFloatMultiply(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + sqInt argOop; + usqLong bits; + double rcvr; + sqInt rcvrOop; + double result; + char *sp; + sqInt tagBits; + double value; + + rcvrOop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + argOop = longAt(GIV(stackPointer)); + + /* begin primitiveFloatMultiply:byArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l2: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr * arg)); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatNotEqual */ +static void +primitiveFloatNotEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int aBool; + char *sp; + + aBool = primitiveFloatEqualtoArg(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (aBool + ? GIV(falseObj) + : GIV(trueObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveFloatSubtract */ +static void +primitiveFloatSubtract(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + sqInt argOop; + usqLong bits; + double rcvr; + sqInt rcvrOop; + double result; + char *sp; + sqInt tagBits; + double value; + + rcvrOop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + argOop = longAt(GIV(stackPointer)); + + /* begin primitiveFloatSubtract:fromArg: */ + /* begin loadFloatOrIntFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l2: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr - arg)); + GIV(stackPointer) = sp; + } + return; +} + + +/* Clear the method lookup cache. This must be done after every programming + change. + */ + + /* InterpreterPrimitives>>#primitiveFlushCache */ +static void +primitiveFlushCache(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); +} + + +/* Primitive. Flush all the existing external primitives in the image thus + forcing a reload on next invocation. + */ + + /* InterpreterPrimitives>>#primitiveFlushExternalPrimitives */ +static void +primitiveFlushExternalPrimitives(void) +{ + flushExternalPrimitives(); +} + + +/* On platforms that support it, this primitive prints the receiver, assumed + to be a Form, to the default printer. + */ + + /* InterpreterPrimitives>>#primitiveFormPrint */ +static void +primitiveFormPrint(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bitsArray; + sqInt bitsArraySize; + sqLong depth; + sqInt fmt; + sqLong h; + double hScale; + sqInt landscapeFlag; + usqInt numBytes; + usqInt numSlots; + sqLong pixelsPerWord; + sqInt rcvr; + sqInt successBoolean; + double vScale; + sqLong w; + sqLong wordsPerLine; + + bitsArray = 0; + depth = 0; + h = 0; + w = 0; + + /* begin booleanValueOf: */ + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + landscapeFlag = 1; + goto l1; + } + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + landscapeFlag = 0; + goto l1; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + landscapeFlag = null; + /* end booleanValueOf: */ +l1: + vScale = floatValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + hScale = floatValueOf(longAt(GIV(stackPointer) + (2 * BytesPerWord))); + rcvr = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + if (!((/* isPointers: */ + ((!(rcvr & (tagMask())))) + && (((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((lengthOf(rcvr)) >= 4))) { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + bitsArray = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord())))); + w = fetchIntegerofObject(1, rcvr); + h = fetchIntegerofObject(2, rcvr); + depth = fetchIntegerofObject(3, rcvr); + if (!((w > 0) + && (h > 0))) { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + pixelsPerWord = 32 / depth; + wordsPerLine = (w + (pixelsPerWord - 1)) / pixelsPerWord; + if (/* isWordsOrBytes: */ + ((!(bitsArray & (tagMask())))) + && (isWordsOrBytesNonImm(bitsArray))) { + /* begin numBytesOf: */ + fmt = (byteAt((void *)(bitsArray + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(bitsArray)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(bitsArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(bitsArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + bitsArraySize = numBytes - (fmt & 7); + goto l2; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + bitsArraySize = numBytes; + goto l2; + } + if (fmt >= (firstShortFormat())) { + bitsArraySize = numBytes - (((fmt & 3) << 1)); + goto l2; + } + + /* fmt >= self firstLongFormat */ + bitsArraySize = numBytes - (((fmt & 1) << 2)); + /* end numBytesOf: */ +l2: + + /* begin success: */ + if (!(bitsArraySize == ((wordsPerLine * h) * 4))) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + if (!GIV(primFailCode)) { + successBoolean = ioFormPrint(bitsArray + BaseHeaderSize, w, h, depth, hScale, vScale, landscapeFlag); + + /* begin success: */ + if (!successBoolean) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + } +} + + +/* Fractional part of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveFractionalPart */ +static void +primitiveFractionalPart(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + double trunc; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(modf(doubleValue, (&trunc))); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveFullClosureValue */ +static void +primitiveFullClosureValue(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt blockClosure; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt header; + sqInt i; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + usqInt numTemps; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt switched; + + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(GIV(argumentCount) == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureCompiledBlockIndex) << (shiftForWord())))))); + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin activateNewFullClosure:method:numArgs:mayContextSwitch: */ + assert(closureMethod == (fetchPointerofObject(FullClosureCompiledBlockIndex, blockClosure))); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(blockClosure)))); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex; + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogFullBlockMethodnumCopied(closureMethod, numCopied); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(FullClosureReceiverIndex, blockClosure, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + FullClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(closureMethod)); + header = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + for (i = ((numArgs + numCopied) + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(closureMethod)))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + assert((((usqInt)closureMethod)) >= (startOfMemory())); + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeaderSqInt = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeaderSqInt)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(1); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewFullClosure:method:numArgs:mayContextSwitch: */ +l1:; +} + + +/* An exact clone of primitiveFullClosureValue except that this version will + not check for interrupts on stack overflow. It may invoke the garbage + collector but will not switch processes. See + checkForInterruptsMayContextSwitch: */ + + /* InterpreterPrimitives>>#primitiveFullClosureValueNoContextSwitch */ +void +primitiveFullClosureValueNoContextSwitch(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt blockClosure; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt header; + sqInt i; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + usqInt numTemps; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt switched; + + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(GIV(argumentCount) == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureCompiledBlockIndex) << (shiftForWord())))))); + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + GIV(deferProfileCheckForVNCS) = 1; + + /* begin activateNewFullClosure:method:numArgs:mayContextSwitch: */ + assert(closureMethod == (fetchPointerofObject(FullClosureCompiledBlockIndex, blockClosure))); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockNoContextSwitchEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(blockClosure)))); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex; + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogFullBlockMethodnumCopied(closureMethod, numCopied); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockNoContextSwitchEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(FullClosureReceiverIndex, blockClosure, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + FullClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(closureMethod)); + header = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + for (i = ((numArgs + numCopied) + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(closureMethod)))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + assert((((usqInt)closureMethod)) >= (startOfMemory())); + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeaderSqInt = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeaderSqInt)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(0); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewFullClosure:method:numArgs:mayContextSwitch: */ +l1:; +} + + /* InterpreterPrimitives>>#primitiveFullClosureValueWithArgs */ +static void +primitiveFullClosureValueWithArgs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt argumentArray; + usqInt arraySize; + sqInt blockClosure; + sqInt closureMethod; + CogMethod *cogMethod; + sqInt header; + sqInt i; + sqInt index; + int inInterpreter; + sqInt methodHeader; + sqInt methodHeaderSqInt; + sqInt numArgs; + sqInt numCopied; + usqInt numSlots; + usqInt numTemps; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt switched; + sqInt top; + + argumentArray = longAt(GIV(stackPointer)); + if (!(/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Check for enough space in thisContext to push all args */ + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (!(arraySize <= (LargeContextSlots - CtxtTempFrameStart))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + blockClosure = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* begin argumentCountOfClosure: */ + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + numArgs = (oop >> 3); + if (!(arraySize == numArgs)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + closureMethod = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureCompiledBlockIndex) << (shiftForWord())))))); + if (!(/* isOopCompiledMethod: */ + ((!(closureMethod & (tagMask())))) + && (((byteAt((void *)(closureMethod + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* Copy the arguments to the stack, and activate */ + index = 1; + while (index <= numArgs) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + index += 1; + } + + /* begin activateNewFullClosure:method:numArgs:mayContextSwitch: */ + assert(closureMethod == (fetchPointerofObject(FullClosureCompiledBlockIndex, blockClosure))); + methodHeader = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + + /* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(blockClosure)))); + numCopied = ((/* begin numSlotsOf: */ + assert((classIndexOf(blockClosure)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(blockClosure + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(blockClosure - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex; + + /* How do we know when to compile a block method? + One simple criterion is to check if the block is running within its inner context, + i.e. if the outerContext is married. + Even simpler is to remember the previous block entered via the interpreter and + compile if this is the same one. But we can thrash trying to compile an uncoggable + method unless we try and remember which ones can't be cogged. So also record + the last block method we failed to compile and avoid recompiling it. */ + if (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) <= maxLiteralCountForCompile) { + if ((GIV(instructionPointer) < (startOfMemory())) + || (closureMethod == GIV(lastCoggableInterpretedBlockMethod))) { + if (closureMethod != GIV(lastUncoggableInterpretedBlockMethod)) { + cogFullBlockMethodnumCopied(closureMethod, numCopied); + if (methodHasCogMethod(closureMethod)) { + /* begin cogMethodOf: */ + methodHeaderSqInt = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + assert((isNonImmediate(methodHeaderSqInt)) + && ((((usqInt)methodHeaderSqInt)) < (startOfMemory()))); + cogMethod = ((CogMethod *) methodHeaderSqInt); + + /* begin executeFullCogBlock:closure:mayContextSwitch: */ + assertCStackWellAligned(); + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + object = ceReturnToInterpreterPC(); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + } + object = (((sqInt)cogMethod)) + (fullBlockEntryOffset()); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),blockClosure); + ceCallCogCodePopReceiverReg(); + goto l1; + } + if (!GIV(cogCompiledCodeCompactionCalledFor)) { + GIV(lastUncoggableInterpretedBlockMethod) = closureMethod; + } + } + } + else { + GIV(lastCoggableInterpretedBlockMethod) = closureMethod; + } + } + else { + if (flagInterpretedMethods()) { + flagMethodAsInterpreted(closureMethod); + } + } + assert(!(methodHasCogMethod(closureMethod))); + + /* Because this is an uncogged method we need to continue via the interpreter. + We could have been reached either from the interpreter, in which case we + should simply return, or from a machine code frame or from a compiled + primitive. In these latter two cases we must longjmp back to the interpreter. + The instructionPointer tells us which path we took. + If the sender was an interpreter frame but called through a (failing) primitive + then make sure we restore the saved instruction pointer and avoid pushing + ceReturnToInterpreterPC which is only valid between an interpreter caller + frame and a machine code callee frame. */ + if (!((inInterpreter = GIV(instructionPointer) >= (startOfMemory())))) { + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),closureMethod); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((((usqInt)(numArgs) << ((BytesPerWord * 8) - 8)))))) + (1ULL << ((BytesPerWord * 8) - 24)) + : ((1 + ((((usqInt)(numArgs) << 8))))) + (0x1000000)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + objOop = longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)(FullClosureReceiverIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(FullClosureReceiverIndex, blockClosure, objOop); + } + object = objOop; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + + /* Copy the copied values... */ + for (i = 0; i < numCopied; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((blockClosure + BaseHeaderSize) + ((((usqInt)((i + FullClosureFirstCopiedValueIndex)) << (shiftForWord()))))))); + } + assert(frameIsBlockActivation(GIV(framePointer))); + assert(!(frameHasContext(GIV(framePointer)))); + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(closureMethod)); + header = longAt((void *)((closureMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + for (i = ((numArgs + numCopied) + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + GIV(instructionPointer) = (((((usqInt)(pointerForOop(closureMethod)))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) - 1; + assert((((usqInt)closureMethod)) >= (startOfMemory())); + GIV(method) = closureMethod; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeaderSqInt = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeaderSqInt)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* Now check for stack overflow or an event (interrupt, must scavenge, etc) */ + switched = 0; + if (GIV(stackPointer) < GIV(stackLimit)) { + switched = handleStackOverflowOrEventAllowContextSwitch(1); + } + returnToExecutivepostContextSwitch(inInterpreter, switched); + /* end activateNewFullClosure:method:numArgs:mayContextSwitch: */ +l1:; +} + + +/* Fetch the system attribute with the given integer ID. The result is a + string, or nil if the attribute is not defined. + */ + + /* InterpreterPrimitives>>#primitiveGetAttribute */ +static void +primitiveGetAttribute(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt attr; + const char *attributeString; + + attr = longAt(GIV(stackPointer)); + if (!((((attr) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if ((attributeString = getAttributeString((attr >> 3)))) { + methodReturnString(attributeString); + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); + } +} + + +/* Access to environment variables via getenv. No putenv or setenv as yet. */ + + /* InterpreterPrimitives>>#primitiveGetenv */ +EXPORT(sqInt) +primitiveGetenv(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *dstPtr; + sqInt fmt; + char *key; + usqInt numBytes; + usqInt numSlots; + sqInt obj; + sqInt sz; + char *val; + + if (sHEAFn) { + if (!(sHEAFn())) { + return (GIV(primFailCode) = PrimErrInappropriate); + } + } + + /* secHasEnvironmentAccess */ + + /* begin stackEphemeralStringValue: */ + obj = longAt(GIV(stackPointer)); + if (!(/* isBytes: */ + ((!(obj & (tagMask())))) + && (((byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + key = null; + goto l2; + } + + /* begin byteSizeOf: */ + if (((obj & (tagMask())) != 0)) { + sz = 0; + goto l1; + } + + /* begin numBytesOf: */ + fmt = (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + sz = numBytes - (fmt & 7); + goto l1; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + sz = numBytes; + goto l1; + } + if (fmt >= (firstShortFormat())) { + sz = numBytes - (((fmt & 3) << 1)); + goto l1; + } + + /* fmt >= self firstLongFormat */ + sz = numBytes - (((fmt & 1) << 2)); + /* end byteSizeOf: */ +l1: + dstPtr = alloca(sz + 1); + if (!dstPtr) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoCMemory; + key = null; + goto l2; + } + memcpy(dstPtr, ((char *) (firstIndexableField(obj))), sz); + dstPtr[sz] = 0; + key = dstPtr; + /* end stackEphemeralStringValue: */ +l2: + if (key) { + if ((val = getenv(key))) { + methodReturnString(val); + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); + } + } + return 0; +} + + /* InterpreterPrimitives>>#primitiveGetImmutability */ +#if IMMUTABILITY +static void +primitiveGetImmutability(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt boolean; + sqInt rcvr; + char *sp; + + rcvr = longAt(GIV(stackPointer)); + if ((GIV(argumentCount) > 0) + && (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + boolean = /* isOopImmutable: */ + (((rcvr & (tagMask())) != 0)) + || (((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0); + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (boolean + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; +} +#endif /* IMMUTABILITY */ + + +/* Primitive. Answer the VM's current log directory */ + + /* InterpreterPrimitives>>#primitiveGetLogDirectory */ +EXPORT(sqInt) +primitiveGetLogDirectory(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + char *ptr; + sqInt stringOop; + sqInt sz; + + ptr = ioGetLogDirectory(); + if (!ptr) { + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + sz = strlen(ptr); + stringOop = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord())))))), sz); + for (i = 0; i < sz; i += 1) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((stringOop + BaseHeaderSize) + i),ptr[i]); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),stringOop); + return 0; +} + + +/* Primitive. Return the next input event from the VM event queue. */ + + /* InterpreterPrimitives>>#primitiveGetNextEvent */ +static void +primitiveGetNextEvent(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqIntptr_t eventTypeIs; + static char * eventTypeNames[] = { + "None", "Mouse", "Keyboard", "DragDropFiles", "Menu", "Window", "Complex", "MouseWheel", + "Plugin" + }; + sqIntptr_t evtBuf[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + sqInt i; + sqIntptr_t value; + + arg = longAt(GIV(stackPointer)); + if (!((/* isArray: */ + ((!(arg & (tagMask())))) + && (((byteAt((void *)(arg + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((slotSizeOf(arg)) == 8))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + ioGetNextEvent(((sqInputEvent*) evtBuf)); + if (GIV(primFailCode)) { + return; + } + if ((eventTraceMask != 0) + && (((eventTraceMask & ((((evtBuf[0]) < 0) ? (((usqInt)(1)) >> (-(evtBuf[0]))) : (1ULL << (evtBuf[0]))))) != 0))) { + fprintf(stderr, + "Event%s/%" PRIdSQINT " @ %" PRIuSQINT "\t\t%" PRIdSQINT "/%" PRIxSQINT " %" PRIdSQINT "/%" PRIxSQINT "\n\t%" PRIdSQINT "/%" PRIxSQINT " %" PRIdSQINT "/%" PRIxSQINT "\t %" PRIdSQINT "/%" PRIxSQINT " %p\n", + ((((evtBuf[0]) >= 0) && ((evtBuf[0]) <= 8)) + ? eventTypeNames[evtBuf[0]] + : "?"), + evtBuf[0], + evtBuf[1], + evtBuf[2], + evtBuf[2], + evtBuf[3], + evtBuf[3], + evtBuf[4], + evtBuf[4], + evtBuf[5], + evtBuf[5], + evtBuf[6], + evtBuf[6], + ((void *)(evtBuf[7]))); + } + + /* Event type */ + eventTypeIs = evtBuf[0]; + storeIntegerofObjectwithValue(0, arg, evtBuf[0]); + if (GIV(primFailCode)) { + return; + } + if (eventTypeIs == 6) { + for (i = 1; i <= 7; i += 1) { + value = evtBuf[i]; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(i, arg, value)); + assert(isNonImmediate(arg)); + if (oopisGreaterThanOrEqualTo(arg, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(arg + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(arg); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((arg + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),value); + } + } + else { + storeIntegerofObjectwithValue(1, arg, (evtBuf[1]) & MillisecondClockMask); + if (GIV(primFailCode)) { + return; + } + for (i = 2; i <= 7; i += 1) { + value = evtBuf[i]; + if ((((((usqIntptr_t)(value)) >> 60) + 1) & 15) <= 1) { + storeIntegerofObjectwithValue(i, arg, value); + } + else { + value = positive64BitIntegerFor(value); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(i, arg, value)); + assert(isNonImmediate(arg)); + if (oopisGreaterThanOrEqualTo(arg, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(arg + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(arg); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((arg + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),value); + } + } + } + + /* Event is Complex, assume evtBuf is populated correctly and return + Event time stamp */ + if (!GIV(primFailCode)) { + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } +} + + /* InterpreterPrimitives>>#primitiveGreaterOrEqual */ +static void +primitiveGreaterOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver >= integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveGreaterOrEqualLargeIntegers */ +EXPORT(void) +primitiveGreaterOrEqualLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr >= integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveGreaterThan */ +static void +primitiveGreaterThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver > integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveGreaterThanLargeIntegers */ +EXPORT(void) +primitiveGreaterThanLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr > integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveGrowMemoryByAtLeast */ +static void +primitiveGrowMemoryByAtLeast(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ammount; + usqInt segSize; + + ammount = longAt(GIV(stackPointer)); + if (!((((ammount) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if ((segSize = growOldSpaceByAtLeast((ammount >> 3)))) { + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),((segSize << 3) | 1)); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + } +} + + +/* Implement 28-bit hashMultiply for SmallInteger and LargePositiveInteger + receivers. + */ + + /* InterpreterPrimitives>>#primitiveHashMultiply */ +static void +primitiveHashMultiply(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + sqInt ok; + sqInt value; + + value = longAt(GIV(stackPointer)); + if ((((value) & 7) == 1)) { + value = (value >> 3); + } + else { + /* begin is:instanceOf:compactClassIndex: */ + if (((value & (tagMask())) != 0)) { + ok = 0; + goto l1; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(value))); + ccIndex = (longAt((void *)(value))) & (classIndexMask()); + ok = ClassLargePositiveIntegerCompactIndex == ccIndex; + /* end is:instanceOf:compactClassIndex: */ +l1: + if (!ok) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + value = SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((value + BaseHeaderSize))))); + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),(((((((usqInt) value)) * HashMultiplyConstant) & 0xFFFFFFF) << 3) | 1)); +} + + +/* Answer the heartbeat frequency in beats per second. If the argument is + true, reset the frequency measure. */ + + /* InterpreterPrimitives>>#primitiveHeartbeatFrequency */ +EXPORT(sqInt) +primitiveHeartbeatFrequency(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + unsigned int integerValue; + sqInt oop; + sqInt reset; + + reset = (GIV(argumentCount) == 1) + && ((longAt(GIV(stackPointer))) == GIV(trueObj)); + integerValue = ((unsigned int) (ioHeartbeatFrequency(reset))); + + /* begin positive32BitIntegerFor: */ + oop = (((usqInt)((((usqInt)integerValue)) & 0xFFFFFFFFU) << 3) | 1); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return 0; +} + + /* InterpreterPrimitives>>#primitiveHighBit */ +static void +primitiveHighBit(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + unsigned long highestBitZeroBased; + sqInt integerReceiverOop; + sqInt leadingZeroCount; + + integerReceiverOop = longAt(GIV(stackPointer)); + + /* Convert the receiver Oop to use a single tag bit */ + integerReceiverOop = ((integerReceiverOop) >> 2 /* (numSmallIntegerTagBits - 1) */) | 1; + if (integerReceiverOop < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } +# if defined(__GNUC__) + /* Note: in gcc, result is undefined if input is zero (for compatibility with BSR fallback when no CLZ instruction available). + but input is never zero because we pass the oop with tag bits set, so we are safe */ + leadingZeroCount = __builtin_clzll(integerReceiverOop); + assert(!((leadingZeroCount == 0))); + + /* Nice bit trick: 1-based high-bit is (32 - clz) - 1 to account for tag bit. + This is like two-complement - clz - 1 on 5 bits, or in other words a bit-invert operation clz ^16r1F */ + + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),(((usqInt)(leadingZeroCount ^ ((BytesPerWord * 8) - 1)) << 3) | 1)); +# elif (defined(_MSC_VER)) || (defined(__ICC)) + /* In MSVC, _lzcnt and _lzcnt64 builtins do not fallback to BSR when not supported by CPU + Instead of messing with __cpuid() we always use the BSR intrinsic + Setting this variable is useless, but VMMaker will generate an automatic initialization at a worse place if this isn't initialized explicitly. */ + highestBitZeroBased = 0; + + /* We do not even test the return value, because integerReceiverOop is never zero */ + _BitScanReverse64((&highestBitZeroBased), integerReceiverOop); + + /* thanks to the tag bit, the +1 operation for getting 1-based rank is not necessary */ + + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),((highestBitZeroBased << 3) | 1)); +# else // defined(__GNUC__) || (defined(_MSC_VER)) || (defined(__ICC)) + /* not gcc/clang, nor MSVC/ICC, you have to implement if your compiler provides useful builtins */ + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } +# endif // defined(__GNUC__) || (defined(_MSC_VER)) || (defined(__ICC)) +} + + +/* Return the value of the high resolution clock if this system has any. + The exact frequency of the high res clock is undefined specifically so + that we can use + processor dependent instructions (like RDTSC). The only use for the high + res clock is for + profiling where we can allocate time based on sub-msec resolution of the + high res clock. + If no high-resolution counter is available, the platform should return + zero. ar 6/22/2007 */ + + /* InterpreterPrimitives>>#primitiveHighResClock */ +EXPORT(sqInt) +primitiveHighResClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = positive64BitIntegerFor(ioHighResClock()); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return 0; +} + + +/* is the receiver/first argument the same object as the (last) argument?. + pop argumentCount because this can be used as a mirror primitive. */ + + /* InterpreterPrimitives>>#primitiveIdentical */ +static void +primitiveIdentical(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt otherObject; + char *sp; + sqInt thisObject; + + thisObject = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + otherObject = longAt(GIV(stackPointer)); + if ((/* isOopForwarded: */ + ((!(otherObject & (tagMask())))) + && ((!((longAt((void *)(otherObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + || ((GIV(argumentCount) > 1) + && (/* isOopForwarded: */ + ((!(thisObject & (tagMask())))) + && ((!((longAt((void *)(thisObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (thisObject == otherObject + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveIdentityHash */ +static void +primitiveIdentityHash(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt hash; + usqInt hashUsqInt; + sqInt integer; + sqInt thisReceiver; + + thisReceiver = longAt(GIV(stackPointer)); + if ((((thisReceiver & (tagMask())) != 0)) + || ((GIV(argumentCount) > 0) + && ((!((longAt((void *)(thisReceiver))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = (GIV(argumentCount) > 0 + ? PrimErrBadArgument + : PrimErrBadReceiver); + return; + } + + /* fail if forwarded; the retry machinery will fix things up. */ + + /* begin hashBitsOf: */ + hash = (long32At((void *)(thisReceiver + 4))) & (identityHashHalfWordMask()); + if (!hash) { + /* cb 1/19/2017 18:34: + would like to assert + self assert: (coInterpreter addressCouldBeClassObj: objOop) not + but instance-specific behaviors that are instances of themselves may + fail this test. + eem 12/28/2021 11:52 I'm not sure this is the issue. The issue is only validating objOop as a behavior. */ + + /* begin newHashBitsOf: */ + /* begin newObjectHash */ + assert(!((GIV(lastHash) == 0))); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 2)); + GIV(lastHash) = GIV(lastHash) ^ ((((GIV(lastHash) << 7))) & (identityHashHalfWordMask())); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 3)); + hashUsqInt = GIV(lastHash); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(thisReceiver + 4),((((long32At((void *)(thisReceiver + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hashUsqInt & (identityHashHalfWordMask()))); + hash = hashUsqInt & (identityHashHalfWordMask()); + } + integer = hash; + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); +} + + +/* Answer an integer identifying the type of image. The image version number + may identify the format of the image (e.g. 32 or 64-bit word size) or + specific requirements + of the image (e.g. block closure support required). + + This is a named (not numbered) primitive in the null module (ie the VM) + */ + + /* InterpreterPrimitives>>#primitiveImageFormatVersion */ +EXPORT(sqInt) +primitiveImageFormatVersion(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = (((usqInt)((((usqInt)(/* imageFormatVersionForSnapshot */ + (GIV(multipleBytecodeSetsActive) + ? 68533 /* (imageFormatVersion bitOr: MultipleBytecodeSetsBitmask) */ + : 68021 /* imageFormatVersion */)))) & 0xFFFFFFFFU) << 3) | 1); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); + return 0; +} + + +/* When called with a single string argument, record the string as the + current image file name. + When called with zero arguments, return a string containing the current + image file name. + */ + + /* InterpreterPrimitives>>#primitiveImageName */ +static void +primitiveImageName(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ccIndex; + sqInt isString; + sqInt okToRename; + sqInt s; + void *sCRIfn; + sqInt sz; + + if (GIV(argumentCount) == 1) { + s = longAt(GIV(stackPointer)); + + /* begin isInstanceOfClassByteString: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((s & (tagMask())) != 0)) { + isString = 0; + goto l1; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(s))); + ccIndex = (longAt((void *)(s))) & (classIndexMask()); + isString = ClassByteStringCompactIndex == ccIndex; + /* end isInstanceOfClassByteString: */ +l1: + if (!isString) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* If the security plugin can be loaded, use it to check for rename permission. + If not, assume it's ok */ + sCRIfn = ioLoadFunctionFrom("secCanRenameImage", "SecurityPlugin"); + if (sCRIfn) { + okToRename = ((sqInt (*)(void))sCRIfn)(); + if (!okToRename) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + } + imageNamePutLength(s + BaseHeaderSize, numBytesOf(s)); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + return; + } + sz = imageNameSize(); + s = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord())))))), sz); + imageNameGetLength(s + BaseHeaderSize, sz); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),s); +} + + +/* For a Smalllnteger, answer itself. + For a Character, answer its code as an unsigned integer. + For a SmallFloat, answer the signed, but unadjusted bit pattern (so as to + keep the result a SmallInteger). + This is a good value for an immediate's hash. */ + + /* InterpreterPrimitives>>#primitiveImmediateAsInteger */ +static void +primitiveImmediateAsInteger(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + usqInt value; + + value = 0; + oop = longAt(GIV(stackPointer)); + + /* begin immediateAsInteger:ifFail: */ + if ((((oop) & 7) == 1)) { + value = (oop >> 3); + goto l1; + } + if (((oop & (characterTag())) != 0)) { + value = ((((usqInt)oop))) >> (numTagBits()); + goto l1; + } + if (((oop & (smallFloatTag())) != 0)) { + /* begin rotatedFloatBitsOf: */ + assert(isImmediateFloat(oop)); + value = (((((usqInt)oop))) >> ((numTagBits()) + 1)) + ((((oop & (1U << (numTagBits()))) != 0) + ? ((((usqInt)-1)) << ((64 - (numTagBits())) - 1)) + : 0)); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + /* end immediateAsInteger:ifFail: */ +l1: + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),((value << 3) | 1)); +} + + +/* Register the input semaphore. If the argument is not a + Semaphore, unregister the current input semaphore. */ + + /* InterpreterPrimitives>>#primitiveInputSemaphore */ +static void +primitiveInputSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + + arg = longAt(GIV(stackPointer)); + if ((((arg) & 7) == 1)) { + /* If arg is integer, then use it as an index + into the external objects array and install it + as the new event semaphore */ + ioSetInputSemaphore((arg >> 3)); + if (!GIV(primFailCode)) { + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + return; + } + + /* old code for compatibility */ + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; +} + + +/* Return an integer indicating the reason for the most recent input + interrupt. + */ + + /* InterpreterPrimitives>>#primitiveInputWord */ +static void +primitiveInputWord(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),(((usqInt)0 << 3) | 1)); +} + + /* InterpreterPrimitives>>#primitiveIntegerAt */ +static void +primitiveIntegerAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classPointer; + sqInt fmt; + sqInt fmtSqInt; + sqInt index; + sqInt numFixed; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt rcvr; + sqInt value; + + /* begin primitiveSpurIntegerAt */ + index = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l1; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + goto l1; + } + + /* begin numBytesOfBytes: */ + fmtSqInt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert(fmtSqInt >= (firstByteFormat())); + numSlots = ((((/* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) << (shiftForWord()))) - (fmtSqInt & 7); + if ((((usqInt)index)) < numSlots) { + value = byteAt((void *)((rcvr + BaseHeaderSize) + index)); + if (value > 0x7F) { + value -= 0x100; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)value << 3) | 1)); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt >= (firstShortFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 1; + if ((((usqInt)index)) < numSlots) { + value = shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 1))))); + if (value > 0x7FFF) { + value -= 0x10000; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)value << 3) | 1)); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + oop = signed64BitIntegerFor(long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3)))))); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt >= (firstLongFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + value = long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2))))); + if (value > 0x7FFFFFFF) { + value -= 0x100000000LL; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)value << 3) | 1)); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if ((fmt <= 5 /* lastPointerFormat */) + && (/* isIndexableFormat: */ + (fmt >= (arrayFormat())) + && ((fmt <= (weakArrayFormat())) + || (fmt >= (sixtyFourBitIndexableFormat()))))) { + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt == (arrayFormat())) { + if ((((usqInt)index)) < numSlots) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))))); + goto l1; + } + } + + /* begin numFixedSlotsOf: */ + classPointer = fetchClassOfNonImm(rcvr); + classFormat = ((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + numFixed = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + if ((((index + 1) >= numFixed) && ((index + 1) <= numSlots))) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index + numFixed)) << (shiftForWord()))))))); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + /* end primitiveSpurIntegerAt */ +l1:; +} + + /* InterpreterPrimitives>>#primitiveIntegerAtPut */ +static void +primitiveIntegerAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classPointer; + sqInt fieldIndex; + sqInt fmt; + sqInt fmtSqInt; + sqInt index; + sqInt numFixed; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqLong value; + sqInt valueOop; + + /* begin primitiveSpurIntegerAtPut */ + valueOop = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + value = signed64BitValueOf(valueOop); + if (!((!GIV(primFailCode)) + && ((((index) & 7) == 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l1; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l1; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + goto l1; + } + if (!((((usqInt)((((value) >> 7)) + 1))) <= 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + + /* begin numBytesOfBytes: */ + fmtSqInt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert(fmtSqInt >= (firstByteFormat())); + numSlots = ((((/* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) << (shiftForWord()))) - (fmtSqInt & 7); + if ((((usqInt)index)) < numSlots) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + index),value); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt >= (firstShortFormat())) { + if (!((((usqInt)((((value) >> 15)) + 1))) <= 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 1; + if ((((usqInt)index)) < numSlots) { + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 1)))),value); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + /* No range check on value in this case because signed64BitValueOf: performed it above. */ + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3)))),value); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if (fmt >= (firstLongFormat())) { + if (!((((usqInt)((((value) >> 0x1F)) + 1))) <= 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2)))),value); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + if ((fmt <= 5 /* lastPointerFormat */) + && (/* isIndexableFormat: */ + (fmt >= (arrayFormat())) + && ((fmt <= (weakArrayFormat())) + || (fmt >= (sixtyFourBitIndexableFormat()))))) { + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt == (arrayFormat())) { + if ((((usqInt)index)) < numSlots) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, rcvr, valueOop)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valueOop & (tagMask())))) + && (oopisLessThan(valueOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),valueOop); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + } + + /* begin numFixedSlotsOf: */ + classPointer = fetchClassOfNonImm(rcvr); + classFormat = ((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + numFixed = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + if ((((index + 1) >= numFixed) && ((index + 1) <= numSlots))) { + fieldIndex = index + numFixed; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, rcvr, valueOop)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valueOop & (tagMask())))) + && (oopisLessThan(valueOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valueOop); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),valueOop); + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l1; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + /* end primitiveSpurIntegerAtPut */ +l1:; +} + + +/* Primitive. Answer the number of interrupt checks per milliseconds that we + execute on this machine. This can be used to adjust the sub-msecs profiler + to check (roughly) + n times per millisecond. + */ + + /* InterpreterPrimitives>>#primitiveInterruptChecksPerMSec */ +EXPORT(sqInt) +primitiveInterruptChecksPerMSec(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + if (GIV(argumentCount)) { + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + oop = (((usqInt)(ioHeartbeatMilliseconds()) << 3) | 1); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); + return 0; +} + + +/* Register the user interrupt semaphore. If the argument is + not a Semaphore, unregister the current interrupt + semaphore. */ + + /* InterpreterPrimitives>>#primitiveInterruptSemaphore */ +static void +primitiveInterruptSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + + arg = longAt(GIV(stackPointer)); + if ((arg == GIV(nilObj)) + || (/* isSemaphoreOop: */ + ((!(arg & (tagMask())))) + && (((longAt((void *)(arg))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))))) { + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheInterruptSemaphore, GIV(specialObjectsOop), arg)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(arg & (tagMask())))) + && (oopisLessThan(arg, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheInterruptSemaphore) << (shiftForWord()))))),arg); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } +} + + +/* Answer if running on a big endian machine. */ + + /* InterpreterPrimitives>>#primitiveIsBigEnder */ +EXPORT(sqInt) +primitiveIsBigEnder(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop:thenPushBool: */ + longAtput(GIV(stackPointer),GIV(falseObj)); + return 0; +} + + +/* Answer if the receiver is pinned, i.e. immobile. */ + + /* InterpreterPrimitives>>#primitiveIsPinned */ +static void +primitiveIsPinned(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt obj; + char *sp; + sqInt trueOrFalse; + + obj = longAt(GIV(stackPointer)); + if ((((obj & (tagMask())) != 0)) + || ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + trueOrFalse = ((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0; + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (trueOrFalse + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; +} + + /* InterpreterPrimitives>>#primitiveLessOrEqual */ +static void +primitiveLessOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver <= integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveLessOrEqualLargeIntegers */ +EXPORT(void) +primitiveLessOrEqualLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr <= integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveLessThan */ +static void +primitiveLessThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver < integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveLessThanLargeIntegers */ +EXPORT(void) +primitiveLessThanLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr < integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + +/* Primitive. Return the n-th builtin module name. */ + + /* InterpreterPrimitives>>#primitiveListBuiltinModule */ +static void +primitiveListBuiltinModule(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt integerPointer; + char *moduleName; + + if (!(GIV(argumentCount) == 1)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + index = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + index = 0; + } + if (GIV(primFailCode) + || (index <= 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + if ((moduleName = ioListBuiltinModule(index))) { + methodReturnString(moduleName); + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); + } +} + + +/* Primitive. Answer the n-th loaded external module name. */ + + /* InterpreterPrimitives>>#primitiveListExternalModule */ +static void +primitiveListExternalModule(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt integerPointer; + char *moduleName; + + if (!(GIV(argumentCount) == 1)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + index = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + index = 0; + } + if (GIV(primFailCode) + || (index <= 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + if ((moduleName = ioListLoadedModule(index))) { + methodReturnString(moduleName); + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); + } +} + + +/* This primitive is called from Smalltalk as... + loadSegmentFrom: aWordArray outPointers: anArray. + + This primitive will load a binary image segment created by + primitiveStoreImageSegment. It expects the outPointer array to be of the + proper size, and the wordArray to be well + formed. It will return as its value the original array of roots, and the + erstwhile segmentWordArray will have been truncated to a size of one word, + i.e. retaining the version + stamp. If this primitive should fail, the segmentWordArray will, sadly, + have been reduced to + an unrecognizable and unusable jumble. But what more could you have done + with it anyway? + [How about saving it so the system functions as primitives are intended? + eem 5/9/2017 16:31] + + In Spur, if the primitive succeeds, the segmentWordArray is also becomed + into the array of loaded + objects, to allow fixing up of loaded objects directly without nextObject, + which Spur doesn't support. */ + + /* InterpreterPrimitives>>#primitiveLoadImageSegment */ +static void +primitiveLoadImageSegment(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt outPointerArray; + sqInt result; + sqInt segmentWordArray; + + outPointerArray = longAt(GIV(stackPointer)); + segmentWordArray = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* Essential type checks */ + if (!((/* isArray: */ + ((!(outPointerArray & (tagMask())))) + && (((byteAt((void *)(outPointerArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (/* isWords: */ + ((!(segmentWordArray & (tagMask())))) + && (((((byteAt((void *)(segmentWordArray + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(segmentWordArray + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1))))))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Must be indexable pointers + Must be indexable words + the engine returns the roots array which was first in the segment, or an error code on failure. */ + result = loadImageSegmentFromoutPointers(segmentWordArray, outPointerArray); + if (oopisGreaterThan(result, segmentWordArray)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),result); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = result; + } +} + + +/* Return the value of the microsecond clock in the local timezone as an + integer. This is the number of microseconds since the Smalltalk epoch, + 1901/1/1 12:00am. + The microsecond clock is at least 60 bits wide which means it'll get to + around August + 38435 before it wraps around. Be sure to put it on your calendar. This + primitive accesses the time as answered by the OS. */ + + /* InterpreterPrimitives>>#primitiveLocalMicrosecondClock */ +static void +primitiveLocalMicrosecondClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = positive64BitIntegerFor(ioLocalMicrosecondsNow()); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Natural log of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveLogN */ +static void +primitiveLogN(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(log(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + +/* Register the low-space semaphore. If the argument is not a + Semaphore, unregister the current low-space Semaphore. */ + + /* InterpreterPrimitives>>#primitiveLowSpaceSemaphore */ +static void +primitiveLowSpaceSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + + arg = longAt(GIV(stackPointer)); + if ((arg == GIV(nilObj)) + || (/* isSemaphoreOop: */ + ((!(arg & (tagMask())))) + && (((longAt((void *)(arg))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))))) { + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheLowSpaceSemaphore, GIV(specialObjectsOop), arg)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(arg & (tagMask())))) + && (oopisLessThan(arg, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheLowSpaceSemaphore) << (shiftForWord()))))),arg); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } +} + + /* InterpreterPrimitives>>#primitiveMakePoint */ +static void +primitiveMakePoint(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + usqInt newObj; + usqInt numBytes; + usqInt pt; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + arg = longAt(GIV(stackPointer)); + if (!(/* isFloatOrInt: */ + (((arg & (tagMask())) != 0) + ? !(((arg & (characterTag())) != 0)) + : ((longAt((void *)(arg))) & (classIndexMask())) == ClassFloatCompactIndex))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* begin eeInstantiatePoint */ + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert(((YIndex + 1) >= 0) + && ((knownClassAtIndex(ClassPointCompactIndex)) != GIV(nilObj))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(knownClassAtIndex(ClassPointCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert((YIndex + 1) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (((YIndex + 1) < 1 + ? 8 /* allocationUnit */ + : (YIndex + 1) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + pt = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) (YIndex + 1))) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + ClassPointCompactIndex); + GIV(freeStart) += numBytes; + pt = newObj; + /* end eeInstantiatePoint */ +l1: + + /* No need to check since new object is always new. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pt)) + && (!(isForwarded(pt)))); + assert(validStorePointerUncheckedArgs(XIndex, pt, rcvr)); + longAtput((void *)((pt + BaseHeaderSize) + ((((usqInt)(XIndex) << (shiftForWord()))))),rcvr); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pt)) + && (!(isForwarded(pt)))); + assert(validStorePointerUncheckedArgs(YIndex, pt, arg)); + longAtput((void *)((pt + BaseHeaderSize) + ((((usqInt)(YIndex) << (shiftForWord()))))),arg); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),pt); +} + + /* InterpreterPrimitives>>#primitiveMaxIdentityHash */ +static void +primitiveMaxIdentityHash(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),(((usqInt)(identityHashHalfWordMask()) << 3) | 1)); +} + + +/* Return the value of the millisecond clock as an integer. Note that the + millisecond clock wraps around periodically. On some platforms it can wrap + daily. The range is limited to SmallInteger maxVal / 2 to allow delays of + up to that length without overflowing a SmallInteger. + */ + + /* InterpreterPrimitives>>#primitiveMillisecondClock */ +static void +primitiveMillisecondClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = ((((ioMSecs()) & MillisecondClockMask) << 3) | 1); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Provide access to the millisecond clock mask to support calculation + of durations based on the millisecond clock value. */ + + /* InterpreterPrimitives>>#primitiveMillisecondClockMask */ +EXPORT(sqInt) +primitiveMillisecondClockMask(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),(((usqInt)MillisecondClockMask << 3) | 1)); + return 0; +} + + /* InterpreterPrimitives>>#primitiveMod */ +static void +primitiveMod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt mod; + + mod = doPrimitiveModby(longAt(GIV(stackPointer) + (1 * BytesPerWord)), longAt(GIV(stackPointer))); + + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)(mod)) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)mod << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveModLargeIntegers */ +EXPORT(void) +primitiveModLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (!b) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return; + } + result = a % b; + + /* Handle remainder of same sign as argument */ + if (result) { + if (!(bIsNegative == aIsNegative)) { + result = b - result; + } + } + oopResult = magnitude64BitIntegerForneg(result, bIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Given one boolean parameter, set multipleBytecodeSetsActive to inform + the VM that alternate bytecode sets such as SistaV1 are now in use and + that the image format number should be updated accordingly. With zero + parameters, answer the current value of multipleBytecodeSetsActive. */ + + /* InterpreterPrimitives>>#primitiveMultipleBytecodeSetsActive */ +EXPORT(sqInt) +primitiveMultipleBytecodeSetsActive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + if (GIV(argumentCount) > 1) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + if (GIV(argumentCount) == 1) { + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + GIV(multipleBytecodeSetsActive) = 1; + } + else { + if (!((longAt(GIV(stackPointer))) == GIV(falseObj))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + GIV(multipleBytecodeSetsActive) = 0; + } + } + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (GIV(multipleBytecodeSetsActive) + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return 0; +} + + /* InterpreterPrimitives>>#primitiveMultiply */ +static void +primitiveMultiply(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArg; + sqInt integerPointer; + sqInt integerRcvr; + sqInt integerResult; + sqInt overflow; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + integerRcvr = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerRcvr = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + integerArg = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArg = 0; + } + if (!GIV(primFailCode)) { + overflow = (integerRcvr > 0 + ? (integerArg > 0 + ? integerRcvr > ((MaxSmallInteger) / integerArg) + : integerArg < ((MinSmallInteger) / integerRcvr)) + : (integerArg > 0 + ? integerRcvr < ((MinSmallInteger) / integerArg) + : (integerRcvr < 0) + && (integerArg < ((MaxSmallInteger) / integerRcvr)))); + if (overflow) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + else { + integerResult = integerRcvr * integerArg; + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerResult << 3) | 1)); + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveMultiplyLargeIntegers */ +EXPORT(void) +primitiveMultiplyLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (GIV(primFailCode)) { + return; + } + + /* check for overflow */ + if ((a > 1) + && ((b > 1) + && (a > (0xFFFFFFFFFFFFFFFFULL / b)))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + result = a * b; + oopResult = magnitude64BitIntegerForneg(result, aIsNegative != bIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + /* InterpreterPrimitives>>#primitiveNew */ +static void +primitiveNew(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt obj; + sqInt reasonCode; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + + /* Allocate a new fixed-size instance. Fail if the allocation would leave + less than lowSpaceThreshold bytes free. This *will not* cause a GC :-) */ + if ((obj = instantiateClass(longAt(GIV(stackPointer))))) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + reasonCode = (isFixedSizePointerFormat((((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + /* InterpreterPrimitives>>#primitiveNewMethod */ +static void +primitiveNewMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bytecodeCount; + sqInt class; + sqInt classFormat; + sqInt classIndex; + sqInt err; + int hash; + sqInt header; + sqInt i; + sqInt instSpec; + usqInt literalCount; + sqInt newObj; + usqInt newObjUsqInt; + usqInt numBytes; + usqInt numSlots; + usqInt p; + sqInt reasonCode; + usqInt size; + sqInt theMethod; + usqInt toDoLimit; + + header = longAt(GIV(stackPointer)); + bytecodeCount = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(((((header) & 7) == 1)) + && (((((bytecodeCount) & 7) == 1)) + && (((bytecodeCount = (bytecodeCount >> 3))) >= 0)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + class = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + literalCount = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + size = ((literalCount + LiteralStart) * BytesPerOop) + bytecodeCount; + + /* begin instantiateCompiledMethodClass:indexableSize: */ + classFormat = ((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + if (instSpec != (firstCompiledMethodFormat())) { + theMethod = null; + goto l2; + } + numSlots = (size + 7) / 8; + instSpec += (8 - size) & 7; + + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(class)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(class + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(class) + ? ((err = enterIntoClassTable(class)) + ? -err + : (long32At((void *)(class + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + theMethod = null; + goto l2; + } + if (numSlots > ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (numSlots > (0x10000000000LL)) { + theMethod = null; + goto l2; + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpec, classIndex); + } + else { + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if (((numSlots) >> 56) > 0) { + newObj = null; + goto l1; + } + newObjUsqInt = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObjUsqInt = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, instSpec, classIndex); + goto l1; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObjUsqInt),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObjUsqInt),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObjUsqInt % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newObj = newObjUsqInt; + /* end allocateSlots:format:classIndex: */ +l1:; + } + if (newObj) { + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + theMethod = newObj; + /* end instantiateCompiledMethodClass:indexableSize: */ +l2: + if (!theMethod) { + reasonCode = (isCompiledMethodFormat((((usqInt)((((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + return; + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theMethod)) + && (!(isForwarded(theMethod)))); + assert(validStorePointerUncheckedArgs(HeaderIndex, theMethod, header)); + longAtput((void *)((theMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))),header); + for (i = 1; i <= literalCount; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theMethod)) + && (!(isForwarded(theMethod)))); + assert(validStorePointerUncheckedArgs(i, theMethod, GIV(nilObj))); + longAtput((void *)((theMethod + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),theMethod); +} + + +/* Allocate a new indexable instance. Fail if the allocation would leave less + than lowSpaceThreshold bytes free. May cause a GC. + */ + + /* InterpreterPrimitives>>#primitiveNewWithArg */ +static void +primitiveNewWithArg(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt instSpec; + sqInt obj; + sqInt reasonCode; + usqIntptr_t size; + sqInt value; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + size = null; + goto l1; + } + size = value; + goto l1; + } + + /* don't inline the rare case */ + size = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* positiveMachineIntegerValueOf: succeeds only for non-negative integers. */ + if ((obj = instantiateClassindexableSize(longAt(GIV(stackPointer) + (1 * BytesPerWord)), size))) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + instSpec = (((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer) + (1 * BytesPerWord))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + reasonCode = ((/* isIndexableFormat: */ + (instSpec >= (arrayFormat())) + && ((instSpec <= (weakArrayFormat())) + || (instSpec >= (sixtyFourBitIndexableFormat())))) + && (!(instSpec >= (firstCompiledMethodFormat()))) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + /* InterpreterPrimitives>>#primitiveNextInstance */ +static void +primitiveNextInstance(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt actualObj; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt object; + sqInt subsequentObject; + + object = longAt(GIV(stackPointer)); + if (!(((object & (tagMask())) != 0))) { + /* begin instanceAfter: */ + actualObj = object; + classIndex = (longAt((void *)(object))) & (classIndexMask()); + + /* begin isYoungObject: */ + assert(isNonImmediate(object)); + if (oopisLessThan(object, GIV(oldSpaceStart))) { + if (oopisGreaterThanOrEqualToandLessThan(object, ((GIV(eden)).start), GIV(freeStart))) { + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(actualObj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + actualObj = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + actualObj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + if (!(oopisLessThan(actualObj, GIV(freeStart)))) break; + if (classIndex == ((longAt((void *)(actualObj))) & (classIndexMask()))) { + subsequentObject = actualObj; + goto l4; + } + } + actualObj = (oopisGreaterThan(GIV(pastSpaceStart), ((GIV(pastSpace)).start)) + ? objectStartingAt(((GIV(pastSpace)).start)) + : GIV(nilObj)); + } + if (oopisGreaterThanOrEqualToandLessThan(actualObj, ((GIV(pastSpace)).start), GIV(pastSpaceStart))) { + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(actualObj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + actualObj = GIV(pastSpaceStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + actualObj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l2:; + if (!(oopisLessThan(actualObj, GIV(pastSpaceStart)))) break; + if (classIndex == ((longAt((void *)(actualObj))) & (classIndexMask()))) { + subsequentObject = actualObj; + goto l4; + } + } + actualObj = GIV(nilObj); + } + } + + /* it almost never is because usually this is preceded by a send of initialInstanceOf: */ + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(actualObj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + actualObj = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + actualObj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3:; + if (!(oopisLessThan(actualObj, GIV(endOfMemory)))) break; + if (classIndex == ((longAt((void *)(actualObj))) & (classIndexMask()))) { + subsequentObject = actualObj; + goto l4; + } + } + subsequentObject = null; + /* end instanceAfter: */ +l4: + if (subsequentObject) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),subsequentObject); + return; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } +} + + +/* Return the object following the receiver in the heap. Return the + SmallInteger zero when there are no more objects. + */ + + /* InterpreterPrimitives>>#primitiveNextObject */ +static void +primitiveNextObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt instance; + + if ((((longAt(GIV(stackPointer))) & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if ((instance = accessibleObjectAfter(longAt(GIV(stackPointer))))) { + assert(isInMemory(instance)); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),instance); + } + else { + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)0 << 3) | 1)); + } +} + + +/* A placeholder for primitives that haven't been implemented or are being + withdrawn gradually. Just absorbs any arguments and returns the receiver. + */ + + /* InterpreterPrimitives>>#primitiveNoop */ +static void +primitiveNoop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + /* InterpreterPrimitives>>#primitiveNotEqual */ +static void +primitiveNotEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArgument; + sqInt integerReceiver; + int result; + char *sp; + + integerArgument = longAt(GIV(stackPointer)); + integerReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerReceiver & integerArgument) & (smallIntegerTag())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerReceiver != integerArgument + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + result = (signedMachineIntegerValueOf(integerReceiver)) != (signedMachineIntegerValueOf(integerArgument)); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (result + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + +/* Primitive comparison operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveNotEqualLargeIntegers */ +EXPORT(void) +primitiveNotEqualLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong integerArg; + sqLong integerRcvr; + char *sp; + + integerArg = signed64BitValueOf(longAt(GIV(stackPointer))); + integerRcvr = signed64BitValueOf(longAt(GIV(stackPointer) + (1 * BytesPerWord))); + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (integerRcvr != integerArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + +/* is the receiver/first argument not the same object as the (last) + argument?. pop argumentCount because this can be used as a mirror + primitive. + */ +/* is the receiver/first argument the same object as the (last) argument?. + pop argumentCount because this can be used as a mirror primitive. */ + + /* InterpreterPrimitives>>#primitiveNotIdentical */ +static void +primitiveNotIdentical(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt otherObject; + char *sp; + sqInt thisObject; + + thisObject = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + otherObject = longAt(GIV(stackPointer)); + if ((/* isOopForwarded: */ + ((!(otherObject & (tagMask())))) + && ((!((longAt((void *)(otherObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + || ((GIV(argumentCount) > 1) + && (/* isOopForwarded: */ + ((!(thisObject & (tagMask())))) + && ((!((longAt((void *)(thisObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (thisObject != otherObject + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + +/* This primitive is called from Squeak as... + arrayOfRoots uniquelyAccessibleObjects */ +/* This primitive answers an array of the receiver and every object in its + proper tree of subParts (ie, that is not refered to from anywhere else + outside the tree). + */ +/* This primitive could be used to implement the primitiveStoreImageSegment + segment, thanks to a suggestion from Igor Stassenko. Currently it is + used only to debug that primitive. */ + + /* InterpreterPrimitives>>#primitiveObjectsAccessibleFromRoots */ +static void +primitiveObjectsAccessibleFromRoots(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayOfRoots; + sqInt result; + + arrayOfRoots = longAt(GIV(stackPointer)); + + /* Essential type checks */ + if (!(/* isArray: */ + ((!(arrayOfRoots & (tagMask())))) + && (((byteAt((void *)(arrayOfRoots + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* Must be indexable pointers */ + result = objectsAccessibleFromRoots(arrayOfRoots); + if (((((result) & 7) == 1)) + && (((result >> 3)) == PrimErrNoMemory)) { + fullGC(); + arrayOfRoots = longAt(GIV(stackPointer)); + result = objectsAccessibleFromRoots(arrayOfRoots); + } + if ((((result) & 7) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = (result >> 3); + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); + } +} + + +/* primitivePathTo: anObject using: stack followWeak: boolean + Answer a path to anObject from the root that does not pass through + the current context */ + + /* InterpreterPrimitives>>#primitivePathToUsing */ +EXPORT(sqInt) +primitivePathToUsing(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt current; + sqInt err; + sqInt fmt; + usqInt freeStartAtStart; + sqInt header; + sqInt headerSqInt; + sqInt i; + sqInt index; + sqInt iSqInt; + sqInt methodField; + sqInt next; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt path; + sqInt sp; + sqInt stack; + sqInt stackp; + sqInt stackSize; + sqInt toDoLimit; + sqInt toDoLimitSqInt; + + err = 0; + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (!(GIV(argumentCount) >= 2)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + if (!(((longAt(GIV(stackPointer))) == GIV(trueObj)) + || ((longAt(GIV(stackPointer))) == GIV(falseObj)))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + stack = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin pathTo:using:followWeak: */ + if (!(/* isArray: */ + ((!(stack & (tagMask())))) + && (((byteAt((void *)(stack + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + err = PrimErrBadArgument; + goto l5; + } + assert(allObjectsUnmarked()); + + /* check no allocations during search */ + freeStartAtStart = GIV(freeStart); + beRootIfOld(stack); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(stack + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(stack + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(stack - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + stackSize = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + stackSize = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + stackSize = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + stackSize = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + stackSize = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + stackSize = 0; + /* end lengthOf: */ +l1: + + /* begin mark: */ + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(stack))); + byteAtput((void *)(stack + (markBitsByteOffset())),(byteAt((void *)(stack + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* no need. the current context is not reachable from the active process (suspendedContext is nil) + objectMemory mark: self activeProcess. */ + current = GIV(specialObjectsOop); + + /* begin mark: */ + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(current))); + byteAtput((void *)(current + (markBitsByteOffset())),(byteAt((void *)(current + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(current + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(current + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(current - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + index = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + index = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + index = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + index = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + index = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + index = 0; + /* end lengthOf: */ +l2: + stackp = 0; + while (1) { + while (((index -= 1)) >= -1) { + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)current)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)current)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)current)) <= (((usqInt)GIV(pages)))))))) { + next = (index >= 0 + ? fieldofFrame(index, ((char *) current)) + : GIV(nilObj)); + } + else { + if (index >= 0) { + next = (((longAt((void *)(current))) & (classIndexMask())) == ClassMethodContextCompactIndex + ? fieldOrSenderFPofContext(index, current) + : longAt((void *)((current + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))))); + } + else { + next = fetchClassOfNonImm(current); + } + } + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)next)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)next)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)next)) <= (((usqInt)GIV(pages)))))))) { + assert(isFrameonPage(((char *) next), stackPageFor(((char *) next)))); + } + else { + if (next >= heapBase) { + assert(checkOkayOop(next)); + } + } + if (next == (longAt(GIV(stackPointer) + (2 * BytesPerWord)))) { + assert(freeStartAtStart == (GIV(freeStart))); + unmarkAfterPathTo(); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(stackp, stack, current)); + assert(isNonImmediate(stack)); + if (oopisGreaterThanOrEqualTo(stack, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(current & (tagMask())))) + && (oopisLessThan(current, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(stack + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(stack); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(stackp) << (shiftForWord()))))),current); + pruneStackstackp(stack, stackp); + err = 0; + goto l5; + } + if (((!(next & (smallIntegerTag())))) + && ((/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)next)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)next)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)next)) <= (((usqInt)GIV(pages))))))) + ? !(frameIsMarked(next)) + : (next >= heapBase) + && ((!(((byteAt((void *)(next + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) + && (((/* isPointers: */ + ((!(next & (tagMask())))) + && (((byteAt((void *)(next + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + || (((byteAt((void *)(next + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((longAt(GIV(stackPointer))) == GIV(trueObj)) + || (!(isWeakNonImm(next))))))))) { + if ((stackp + 2) > stackSize) { + assert(freeStartAtStart == (GIV(freeStart))); + unmarkAfterPathTo(); + + /* begin nilFieldsOf: */ + toDoLimit = ((assert((classIndexOf(stack)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(stack + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(stack - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(i, stack, GIV(nilObj))); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } + err = PrimErrBadIndex; + goto l5; + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(stackp, stack, current)); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(stackp) << (shiftForWord()))))),current); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(stackp + 1, stack, (((usqInt)index << 3) | 1))); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)((stackp + 1)) << (shiftForWord()))))),(((usqInt)index << 3) | 1)); + stackp += 2; + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)(((char *) next)))) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)(((char *) next)))) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)(((char *) next)))) <= (((usqInt)GIV(pages)))))))) { + /* begin markFrame: */ + methodField = longAt((void *)(next + FoxMethod)); + if ((((usqInt)methodField)) < (startOfMemory())) { + longAtput((void *)(next + FoxMethod),methodField | 4); + } + else { + longAtput((void *)(next + FoxIFrameFlags),(longAt((void *)(next + FoxIFrameFlags))) | 2); + } + index = CtxtTempFrameStart + (stackPointerIndexForFrame(((char *) next))); + } + else { + /* begin mark: */ + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(next))); + byteAtput((void *)(next + (markBitsByteOffset())),(byteAt((void *)(next + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(next + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(next))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((next + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l3; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(next))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l3: + index = ((usqInt) (CtxtTempFrameStart + contextSize)); + goto l4; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(next)) > (isForwardedObjectClassIndexPun())); + index = (((numSlotsUsqInt = byteAt((void *)(next + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(next - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + goto l4; + } + if (fmt == (forwardedFormat())) { + index = 1; + goto l4; + } + if (fmt < (firstCompiledMethodFormat())) { + index = 0; + goto l4; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(next)); + headerSqInt = longAt((void *)((next + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + index = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l4:; + } + current = next; + } + } + if (current == GIV(specialObjectsOop)) { + assert(freeStartAtStart == (GIV(freeStart))); + unmarkAfterPathTo(); + + /* begin nilFieldsOf: */ + toDoLimitSqInt = ((assert((classIndexOf(stack)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(stack + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(stack - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) - 1; + for (iSqInt = 0; iSqInt <= toDoLimitSqInt; iSqInt += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(iSqInt, stack, GIV(nilObj))); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord()))))),GIV(nilObj)); + } + err = PrimErrNotFound; + goto l5; + } + index = ((longAt((void *)((stack + BaseHeaderSize) + ((((usqInt)((stackp - 1)) << (shiftForWord()))))))) >> 3); + current = longAt((void *)((stack + BaseHeaderSize) + ((((usqInt)((stackp - 2)) << (shiftForWord())))))); + stackp -= 2; + } + /* end pathTo:using:followWeak: */ +l5: + if (err) { + return (GIV(primFailCode) = err); + } + path = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),path); + return 0; +} + + /* InterpreterPrimitives>>#primitivePerformInSuperclass */ +static void +primitivePerformInSuperclass(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt argumentArray; + usqInt arraySize; + sqInt currentClass; + sqInt delta; + sqInt index; + sqInt lookupClass; + usqInt numSlots; + sqInt objOop; + sqInt offset; + sqInt performArgCount; + sqInt rcvr; + usqInt savedNewMethod; + sqInt selector; + sqInt tagBits; + sqInt top; + + rcvr = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + lookupClass = longAt(GIV(stackPointer)); + if (GIV(argumentCount) != 3) { + if (GIV(argumentCount) != 4) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + } + + /* e.g. object:perform:withArguments:inClass: */ + currentClass = /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)); + while (currentClass != lookupClass) { + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOop); + } + currentClass = objOop; + if (currentClass == GIV(nilObj)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + selector = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + argumentArray = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin primitiveObject:perform:withArguments:lookedUpIn: */ + if (!(/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + + /* Check if number of arguments is reasonable; MaxNumArgs isn't available + so just use LargeContextSize */ + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (arraySize > (LargeContextSlots - CtxtTempFrameStart)) { + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + goto l1; + } + performArgCount = GIV(argumentCount); + + /* Recursion check: + | a | + a := Array with: #perform:withArguments: with: nil. + a at: 2 put: a. + a perform: a first withArguments: a. + | a | + a := Array with: #perform:withArguments: with: (Array with: #perform:withArguments: with: nil). + a last at: 2 put: a. + a perform: a first withArguments: a */ + if (!GIV(performWithArgumentsRecursionGuard)) { + GIV(performWithArgumentsRecursionGuard) = (lookupClass + ? longAt(GIV(stackPointer) + (1 * BytesPerWord)) + : longAt(GIV(stackPointer))); + } + + /* Push newMethod to save it in case of failure, + then push the actual receiver and the args in the array. */ + savedNewMethod = GIV(newMethod); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + + /* Copy the arguments to the stack, in case of MNU, and lookup */ + for (index = 1; index <= arraySize; index += 1) { + arg = longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord())))))); + if (arg == GIV(performWithArgumentsRecursionGuard)) { + GIV(performWithArgumentsRecursionGuard) = null; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),arg); + } + if (!GIV(performWithArgumentsRecursionGuard)) { + /* begin pop: */ + GIV(stackPointer) += (2 + arraySize) * BytesPerWord; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l1; + } + GIV(argumentCount) = arraySize; + GIV(messageSelector) = selector; + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask()))); + if (printOnTrace()) { + printActivationNameForSelectorstartClass(GIV(messageSelector), (lookupClass + ? lookupClass + : /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr)))); + cr(); + } + findNewMethodInClassTag((lookupClass + ? classTagForClass(lookupClass) + : /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())))); + + /* Only test CompiledMethods for argument count - any other objects playacting as CMs will have to take their chances */ + if ((/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && ((argumentCountOf(GIV(newMethod))) != GIV(argumentCount))) { + /* Restore the state by popping the array entries, the actual receiver and the saved + newMethod, leaving the selector and array, and fail. N.B. If an MNU has happened + then argumentCount will match newMethod, so this code will not be reached. + These asserts check that an MNU has not occurred if the argumentCount doesn't match the newMethod. */ + assert((stackTop()) == ((arraySize == 0 + ? rcvr + : fetchPointerofObject(arraySize - 1, argumentArray)))); + assert(GIV(argumentCount) == arraySize); + + /* begin pop: */ + GIV(stackPointer) += (arraySize + 1) * BytesPerWord; + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(newMethod) = top; + + /* Must reset primitiveFunctionPointer for checkForAndFollowForwardedPrimitiveState */ + GIV(argumentCount) = performArgCount; + primitiveFunctionPointer = (lookupClass + ? primitivePerformInSuperclass + : primitivePerformWithArgs); + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + goto l1; + } + + /* Cannot fail this primitive from here-on. Slide the actual receiver and arguments down + to replace the perform arguments and saved newMethod and then execute the new + method. Use argumentCount not arraySize because an MNU may have changed it. */ + + /* +2 = receiver + saved newMethod */ + delta = BytesPerWord * (performArgCount + 2); + for (offset = (GIV(argumentCount) * BytesPerWord); offset >= 0; offset += (-BytesPerWord)) { + longAtput((GIV(stackPointer) + offset) + delta,longAt(GIV(stackPointer) + offset)); + } + + /* Part of the recursion guard above. To ensure we fail in the original method, restore + newMethod, and don't do the activateNewMethod implicit in executeNewMethod. */ + if ((primitiveFunctionPointer == primitivePerformWithArgs) + || (primitiveFunctionPointer == primitivePerformInSuperclass)) { + /* This should of course be a tail call, which could be done via setjmp/longjmp. + But this is vanity code. After all how often is a recursive invocation of + primitivePerformWithArgs et al made? */ + GIV(newMethod) = savedNewMethod; + GIV(argumentCount) = performArgCount; + + /* begin pop: */ + GIV(stackPointer) += (performArgCount + 2) * BytesPerWord; + + /* prevent inlining... */ + slowPrimitiveResponse(); + GIV(performWithArgumentsRecursionGuard) = null; + goto l1; + } + GIV(performWithArgumentsRecursionGuard) = null; + + /* begin pop: */ + GIV(stackPointer) += (performArgCount + 2) * BytesPerWord; + + /* prevent inlining... */ + executeNewMethod(); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + /* end primitiveObject:perform:withArguments:lookedUpIn: */ +l1:; +} + + /* InterpreterPrimitives>>#primitivePerformWithArgs */ +static void +primitivePerformWithArgs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt actualReceiver; + sqInt arg; + sqInt argumentArray; + usqInt arraySize; + sqInt delta; + sqInt index; + usqInt numSlots; + sqInt offset; + sqInt performArgCount; + usqInt savedNewMethod; + sqInt selector; + sqInt tagBits; + sqInt top; + + actualReceiver = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + selector = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + argumentArray = longAt(GIV(stackPointer)); + + /* begin primitiveObject:perform:withArguments:lookedUpIn: */ + if (!(/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l1; + } + + /* Check if number of arguments is reasonable; MaxNumArgs isn't available + so just use LargeContextSize */ + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (arraySize > (LargeContextSlots - CtxtTempFrameStart)) { + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + goto l1; + } + performArgCount = GIV(argumentCount); + + /* Recursion check: + | a | + a := Array with: #perform:withArguments: with: nil. + a at: 2 put: a. + a perform: a first withArguments: a. + | a | + a := Array with: #perform:withArguments: with: (Array with: #perform:withArguments: with: nil). + a last at: 2 put: a. + a perform: a first withArguments: a */ + if (!GIV(performWithArgumentsRecursionGuard)) { + GIV(performWithArgumentsRecursionGuard) = longAt(GIV(stackPointer)); + } + + /* Push newMethod to save it in case of failure, + then push the actual receiver and the args in the array. */ + savedNewMethod = GIV(newMethod); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),actualReceiver); + + /* Copy the arguments to the stack, in case of MNU, and lookup */ + for (index = 1; index <= arraySize; index += 1) { + arg = longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord())))))); + if (arg == GIV(performWithArgumentsRecursionGuard)) { + GIV(performWithArgumentsRecursionGuard) = null; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),arg); + } + if (!GIV(performWithArgumentsRecursionGuard)) { + /* begin pop: */ + GIV(stackPointer) += (2 + arraySize) * BytesPerWord; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l1; + } + GIV(argumentCount) = arraySize; + GIV(messageSelector) = selector; + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), /* fetchClassTagOf: */ + ((tagBits = actualReceiver & (tagMask())) + ? tagBits + : (longAt((void *)(actualReceiver))) & (classIndexMask()))); + if (printOnTrace()) { + printActivationNameForSelectorstartClass(GIV(messageSelector), /* fetchClassOf: */ + ((tagBits = actualReceiver & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(actualReceiver))); + cr(); + } + findNewMethodInClassTag(/* fetchClassTagOf: */ + ((tagBits = actualReceiver & (tagMask())) + ? tagBits + : (longAt((void *)(actualReceiver))) & (classIndexMask()))); + + /* Only test CompiledMethods for argument count - any other objects playacting as CMs will have to take their chances */ + if ((/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && ((argumentCountOf(GIV(newMethod))) != GIV(argumentCount))) { + /* Restore the state by popping the array entries, the actual receiver and the saved + newMethod, leaving the selector and array, and fail. N.B. If an MNU has happened + then argumentCount will match newMethod, so this code will not be reached. + These asserts check that an MNU has not occurred if the argumentCount doesn't match the newMethod. */ + assert((stackTop()) == ((arraySize == 0 + ? actualReceiver + : fetchPointerofObject(arraySize - 1, argumentArray)))); + assert(GIV(argumentCount) == arraySize); + + /* begin pop: */ + GIV(stackPointer) += (arraySize + 1) * BytesPerWord; + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(newMethod) = top; + + /* Must reset primitiveFunctionPointer for checkForAndFollowForwardedPrimitiveState */ + GIV(argumentCount) = performArgCount; + primitiveFunctionPointer = primitivePerformWithArgs; + GIV(performWithArgumentsRecursionGuard) = null; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + goto l1; + } + + /* Cannot fail this primitive from here-on. Slide the actual receiver and arguments down + to replace the perform arguments and saved newMethod and then execute the new + method. Use argumentCount not arraySize because an MNU may have changed it. */ + + /* +2 = receiver + saved newMethod */ + delta = BytesPerWord * (performArgCount + 2); + for (offset = (GIV(argumentCount) * BytesPerWord); offset >= 0; offset += (-BytesPerWord)) { + longAtput((GIV(stackPointer) + offset) + delta,longAt(GIV(stackPointer) + offset)); + } + + /* Part of the recursion guard above. To ensure we fail in the original method, restore + newMethod, and don't do the activateNewMethod implicit in executeNewMethod. */ + if ((primitiveFunctionPointer == primitivePerformWithArgs) + || (primitiveFunctionPointer == primitivePerformInSuperclass)) { + /* This should of course be a tail call, which could be done via setjmp/longjmp. + But this is vanity code. After all how often is a recursive invocation of + primitivePerformWithArgs et al made? */ + GIV(newMethod) = savedNewMethod; + GIV(argumentCount) = performArgCount; + + /* begin pop: */ + GIV(stackPointer) += (performArgCount + 2) * BytesPerWord; + + /* prevent inlining... */ + slowPrimitiveResponse(); + GIV(performWithArgumentsRecursionGuard) = null; + goto l1; + } + GIV(performWithArgumentsRecursionGuard) = null; + + /* begin pop: */ + GIV(stackPointer) += (performArgCount + 2) * BytesPerWord; + + /* prevent inlining... */ + executeNewMethod(); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + /* end primitiveObject:perform:withArguments:lookedUpIn: */ +l1:; +} + + /* InterpreterPrimitives>>#primitivePinnedNew */ +static void +primitivePinnedNew(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt classObj; + sqInt err; + int hash; + sqInt instSpec; + sqInt newObj; + sqInt numSlots; + sqInt obj; + usqInt p; + sqInt reasonCode; + usqInt toDoLimit; + + + /* For the mirror prims check that the class obj is actually a valid class. + Allocate a new fixed-size instance. Fail if the allocation would leave + less than lowSpaceThreshold bytes free. This *will not* cause a GC :-) */ + classObj = longAt(GIV(stackPointer)); + + /* begin inOldSpaceInstantiatePinnedClass: */ + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + if (!(/* isFixedSizePointerFormat: */ + (instSpec <= (nonIndexablePointerFormat())) + || (instSpec == (ephemeronFormat())))) { + obj = null; + goto l1; + } + + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + obj = null; + goto l1; + } + numSlots = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((((usqInt)(numSlots) << (shiftForWord())))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpec, classIndex); + if (newObj) { + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),GIV(nilObj)); + } + } + obj = newObj; + /* end inOldSpaceInstantiatePinnedClass: */ +l1: + if (obj) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + reasonCode = (isFixedSizePointerFormat((((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + +/* Allocate a new pinned indexable instance. Fail if the allocation would + leave less than lowSpaceThreshold bytes free. + */ + + /* InterpreterPrimitives>>#primitivePinnedNewWithArg */ +static void +primitivePinnedNewWithArg(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt classObj; + sqInt err; + sqInt fillValue; + int hash; + sqInt instSpec; + sqInt instSpecSqInt; + sqInt newObj; + usqInt numSlots; + sqInt obj; + usqInt p; + sqInt reasonCode; + usqIntptr_t size; + usqInt toDoLimit; + sqInt value; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + size = null; + goto l1; + } + size = value; + goto l1; + } + + /* don't inline the rare case */ + size = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* positiveMachineIntegerValueOf: succeeds only for non-negative integers. */ + classObj = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin inOldSpaceInstantiatePinnedClass:indexableSize: */ + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpecSqInt = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + fillValue = 0; + switch (instSpecSqInt) { + case arrayFormat(): + numSlots = size; + fillValue = GIV(nilObj); + break; + case indexablePointersFormat(): + case weakArrayFormat(): + numSlots = (classFormat & ((1U << (fixedFieldsFieldWidth())) - 1)) + size; + fillValue = GIV(nilObj); + break; + case sixtyFourBitIndexableFormat(): + numSlots = size; + break; + case firstLongFormat(): + if ((classIndex == ClassFloatCompactIndex) + && (size != 2)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + obj = null; + goto l2; + } + numSlots = (size + 1) / 2; + instSpecSqInt += size & 1; + break; + case firstShortFormat(): + numSlots = (size + 3) / 4; + instSpecSqInt += (4 - size) & 3; + break; + case firstByteFormat(): + numSlots = (size + 7) / 8; + instSpecSqInt += (8 - size) & 7; + break; + default: + if ((size != 0) + || (instSpecSqInt > 5 /* lastPointerFormat */)) { + obj = null; + goto l2; + } + numSlots = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + fillValue = GIV(nilObj); + } + + /* non-indexable + Some Squeak images include funky fixed subclasses of abstract variable + superclasses. e.g. DirectoryEntry as a subclass of ArrayedCollection. + The (Threaded)FFIPlugin expects to be able to instantiate ExternalData via + this method. + Hence allow fixed classes to be instantiated here iff nElements = 0. */ + if (!classIndex) { + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + obj = null; + goto l2; + } + } + if (numSlots > (0x10000000000LL)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + obj = null; + goto l2; + } + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpecSqInt, classIndex); + if (newObj) { + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),fillValue); + } + } + obj = newObj; + /* end inOldSpaceInstantiatePinnedClass:indexableSize: */ +l2: + if (obj) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + instSpec = (((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer) + (1 * BytesPerWord))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + reasonCode = ((/* isIndexableFormat: */ + (instSpec >= (arrayFormat())) + && ((instSpec <= (weakArrayFormat())) + || (instSpec >= (sixtyFourBitIndexableFormat())))) + && (!(instSpec >= (firstCompiledMethodFormat()))) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + +/* Primitive. Answer the last primitive method sampled by the profiler. */ + + /* InterpreterPrimitives>>#primitiveProfilePrimitive */ +EXPORT(sqInt) +primitiveProfilePrimitive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = (!(GIV(profileMethod)) + ? GIV(nilObj) + : GIV(profileMethod)); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return 0; +} + + +/* Primitive. Answer the last sample taken by the profiler, or nil if the + profiler isn't active. + See also primitiveProfileStart. + */ + + /* InterpreterPrimitives>>#primitiveProfileSample */ +EXPORT(sqInt) +primitiveProfileSample(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = (!(GIV(profileProcess)) + ? GIV(nilObj) + : GIV(profileProcess)); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return 0; +} + + +/* Primitive. Begin profiling execution by using the high-resolution clock + instead of a time-based process + (which is limited to timing resolution and triggers off the same signal + that many of the processes being + profiled trigger off leading to consistently wrong results). The argument + is the number of ticks of the + high-resolution clock to elapse before taking a sample. The sample is + stored in the profileProcess and + profileMethod iVars which can be retrieved via primitiveProfileSample and + primitiveProfilePrimitive. So also void the sample iVars. Once a sample is + taken, the semaphore installed via + primitiveProfileSemaphore is signalled. If the argument is less or equal + to zero, profiling is disabled. */ + + /* InterpreterPrimitives>>#primitiveProfileStart */ +EXPORT(sqInt) +primitiveProfileStart(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt deltaTicks; + + deltaTicks = longAt(GIV(stackPointer)); + if ((((deltaTicks) & 7) == 1)) { + GIV(deferProfileCheckForVNCS) = 0; + deltaTicks = (deltaTicks >> 3); + GIV(nextProfileTick) = (deltaTicks > 0 + ? (ioHighResClock()) + deltaTicks + : 0); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return 0; + } + return (GIV(primFailCode) = PrimErrBadArgument); +} + + /* InterpreterPrimitives>>#primitiveQuit */ +static void +primitiveQuit(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + ioExitWithErrorCode((GIV(argumentCount) == 1 + ? ((longAt(GIV(stackPointer))) >> 3) + : 0)); +} + + +/* Rounds negative results towards zero. */ + + /* InterpreterPrimitives>>#primitiveQuo */ +static void +primitiveQuo(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArg; + sqInt integerPointer; + sqInt integerRcvr; + sqInt integerResult; + + integerResult = 0; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + integerRcvr = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerRcvr = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + integerArg = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + integerArg = 0; + } + + /* begin success: */ + if (!integerArg) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + integerResult = /* quot:ient: */ + (integerRcvr > 0 + ? (integerArg > 0 + ? integerRcvr / integerArg + : 0 - (integerRcvr / (0 - integerArg))) + : (integerArg > 0 + ? 0 - ((0 - integerRcvr) / integerArg) + : (0 - integerRcvr) / (0 - integerArg))); + } + + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)(integerResult)) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerResult << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveQuoLargeIntegers */ +EXPORT(void) +primitiveQuoLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (!b) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return; + } + result = a / b; + oopResult = magnitude64BitIntegerForneg(result, bIsNegative != aIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveRemLargeIntegers */ +EXPORT(void) +primitiveRemLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (!b) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return; + } + result = a % b; + oopResult = magnitude64BitIntegerForneg(result, aIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* The character scanner primitive + primScanCharactersFrom: startIndex to: stopIndex in: sourceString rightX: + rightX stopConditions: stops kern: kernDelta + This is the inner loop of measurement and scanning for text display. March + through sourceString from startIndex to stopIndex. + If any character is flagged with a non-nil entry in stops, then return the + corresponding value. Determine width of each character + from xTable, indexed by map. If destX would exceed rightX, then return + stops at: 258. Advance destX by the width of the + character. If stopIndex has been reached, then return stops at: 257. + + Receiver inst vars: + destX horizontal position for next character (distance from left of + composition area) + lastIndex the Integer index of next character to be processed in the + sourceString argument + xTable an array mapping character code to glyph x coordinate in a form + of glyphs + map an array mapping character code to glyph position. + + Failure codes: + PrimErrBadArgument one of the indices is not a SmallInteger, or the sting + argument is not a byte string, or the stops array is too small. + PrimErrBadReceiver the receiver has less than four inst vars or any of + scanDestX & scanLastIndex are not SmallIntegers, or the scanXTable or + scanMap are not arrays of the right size + PrimErrBadIndex either a glyph index in scanMap or a start index in + scanXTable are not SmallIntegers + PrimErrLimitExceeded arithmetic on scanDestX has wrapped around to become + negative + */ + + /* InterpreterPrimitives>>#primitiveScanCharacters */ +static void +primitiveScanCharacters(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ascii; + sqInt glyphIndex; + sqInt kernDelta; + sqInt maxGlyph; + sqInt nextDestX; + sqInt rcvr; + sqInt scanDestX; + sqInt scanLastIndex; + sqInt scanMap; + sqInt scanRightX; + sqInt scanStartIndex; + sqInt scanStopIndex; + sqInt scanXTable; + sqInt sourceString; + sqInt sourceX; + sqInt sourceX2; + char *sp1; + sqInt stopReason; + sqInt stops; + + if (GIV(argumentCount) != 6) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + + /* Load the receiver and arguments */ + kernDelta = longAt(GIV(stackPointer)); + stops = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + scanRightX = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + sourceString = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + scanStopIndex = longAt(GIV(stackPointer) + (4 * BytesPerWord)); + scanStartIndex = longAt(GIV(stackPointer) + (5 * BytesPerWord)); + rcvr = longAt(GIV(stackPointer) + (6 * BytesPerWord)); + if (!(((((kernDelta) & 7) == 1)) + && (((((scanRightX) & 7) == 1)) + && (((((scanStopIndex) & 7) == 1)) + && ((((scanStartIndex) & 7) == 1)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + kernDelta = (kernDelta >> 3); + scanRightX = (scanRightX >> 3); + scanStopIndex = (scanStopIndex >> 3); + scanStartIndex = (scanStartIndex >> 3); + + /* check argument type and range and rcvr */ + if (!((/* isArray: */ + ((!(stops & (tagMask())))) + && (((byteAt((void *)(stops + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((slotSizeOf(stops)) >= 258) + && ((/* isBytes: */ + ((!(sourceString & (tagMask())))) + && (((byteAt((void *)(sourceString + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((scanStartIndex > 0) + && ((scanStopIndex > 0) + && (scanStopIndex <= (byteSizeOf(sourceString))))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (!((/* isPointers: */ + ((!(rcvr & (tagMask())))) + && (((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((slotSizeOf(rcvr)) >= 4))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* Check required rcvr instVars */ + scanDestX = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord())))); + scanLastIndex = longAt((void *)((rcvr + BaseHeaderSize) + (1U << (shiftForWord())))); + scanXTable = longAt((void *)((rcvr + BaseHeaderSize) + (2U << (shiftForWord())))); + scanMap = longAt((void *)((rcvr + BaseHeaderSize) + (3U << (shiftForWord())))); + if (!((/* isArray: */ + ((!(scanXTable & (tagMask())))) + && (((byteAt((void *)(scanXTable + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((/* isArray: */ + ((!(scanMap & (tagMask())))) + && (((byteAt((void *)(scanMap + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((slotSizeOf(scanMap)) == 0x100) + && (((((scanDestX) & 7) == 1)) + && ((((scanLastIndex) & 7) == 1))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if (((scanDestX = (scanDestX >> 3))) < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + scanLastIndex = (scanLastIndex >> 3); + maxGlyph = (slotSizeOf(scanXTable)) - 2; + + /* Okay, here we go. We have eliminated nearly all failure + conditions, to optimize the inner fetches. */ + scanLastIndex = scanStartIndex; + while (scanLastIndex <= scanStopIndex) { + /* Known to be okay since scanStartIndex > 0 and scanStopIndex <= sourceString size */ + ascii = byteAt((void *)((sourceString + BaseHeaderSize) + (scanLastIndex - 1))); + + /* Known to be okay since stops size >= 258 */ + if (!(((stopReason = longAt((void *)((stops + BaseHeaderSize) + ((((usqInt)(ascii) << (shiftForWord())))))))) == GIV(nilObj))) { + if (!(scanDestX >= 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrLimitExceeded; + return; + } + storeIntegerofObjectwithValue(0, rcvr, scanDestX); + storeIntegerofObjectwithValue(1, rcvr, scanLastIndex); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),stopReason); + return; + } + + /* Store everything back and get out of here since some stop condition needs to be checked + Known to be okay since scanMap size = 256 */ + glyphIndex = longAt((void *)((scanMap + BaseHeaderSize) + ((((usqInt)(ascii) << (shiftForWord())))))); + + /* fail if the glyphIndex is out of range */ + if (!(((((glyphIndex) & 7) == 1)) + && ((((glyphIndex = (glyphIndex >> 3))) >= 0) + && (glyphIndex <= maxGlyph)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + sourceX = longAt((void *)((scanXTable + BaseHeaderSize) + ((((usqInt)(glyphIndex) << (shiftForWord())))))); + sourceX2 = longAt((void *)((scanXTable + BaseHeaderSize) + ((((usqInt)((glyphIndex + 1)) << (shiftForWord())))))); + + /* Above may fail if non-integer entries in scanXTable */ + if (!(((((sourceX) & 7) == 1)) + && ((((sourceX2) & 7) == 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + nextDestX = (scanDestX + ((sourceX2 >> 3))) - ((sourceX >> 3)); + if (nextDestX > scanRightX) { + /* Store everything back and get out of here since we got to the right edge */ + storeIntegerofObjectwithValue(0, rcvr, scanDestX); + storeIntegerofObjectwithValue(1, rcvr, scanLastIndex); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((sp1 = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),longAt((void *)((stops + BaseHeaderSize) + ((((usqInt)((CrossedX - 1)) << (shiftForWord()))))))); + GIV(stackPointer) = sp1; + return; + } + if (!((nextDestX >= 0) + && (((scanDestX = nextDestX + kernDelta)) >= 0))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrLimitExceeded; + return; + } + scanLastIndex += 1; + } + storeIntegerofObjectwithValue(0, rcvr, scanDestX); + storeIntegerofObjectwithValue(1, rcvr, scanStopIndex); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),longAt((void *)((stops + BaseHeaderSize) + ((((usqInt)((EndOfRun - 1)) << (shiftForWord()))))))); +} + + +/* Return a SmallInteger indicating the current depth of the OS screen. + Negative values are used to imply LSB type pixel format an there is some + support in the VM for handling either MSB or LSB + */ + + /* InterpreterPrimitives>>#primitiveScreenDepth */ +EXPORT(sqInt) +primitiveScreenDepth(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt depth; + + depth = ioScreenDepth(); + if (GIV(primFailCode)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),(((usqInt)depth << 3) | 1)); + return 0; +} + + +/* Answer a float indicating the current scale factor for pixels of the + Smalltalk window. + */ + + /* InterpreterPrimitives>>#primitiveScreenScaleFactor */ +EXPORT(sqInt) +primitiveScreenScaleFactor(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double factor; + char *sp; + + factor = ioScreenScaleFactor(); + if (!GIV(primFailCode)) { + /* begin methodReturnFloat: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),floatObjectOf(factor)); + GIV(stackPointer) = sp; + } + return 0; +} + + +/* Answer a point indicating the current size of the Smalltalk window. + Currently there is a limit of 65535 in each direction because the + point is encoded into a single 32bit value in the image header. + This might well become a problem one day */ + + /* InterpreterPrimitives>>#primitiveScreenSize */ +static void +primitiveScreenSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newObj; + usqInt numBytes; + sqInt oop; + usqInt pointResult; + sqInt pointWord; + sqInt xValue; + sqInt yValue; + + pointWord = ioScreenSize(); + xValue = (((usqInt)(pointWord)) >> 16) & 0xFFFF; + yValue = pointWord & 0xFFFF; + + /* begin makePointwithxValue:yValue: */ + /* begin eeInstantiatePoint */ + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert(((YIndex + 1) >= 0) + && ((knownClassAtIndex(ClassPointCompactIndex)) != GIV(nilObj))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(knownClassAtIndex(ClassPointCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert((YIndex + 1) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (((YIndex + 1) < 1 + ? 8 /* allocationUnit */ + : (YIndex + 1) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + pointResult = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) (YIndex + 1))) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + ClassPointCompactIndex); + GIV(freeStart) += numBytes; + pointResult = newObj; + /* end eeInstantiatePoint */ +l1: + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pointResult)) + && (!(isForwarded(pointResult)))); + assert(validStorePointerUncheckedArgs(XIndex, pointResult, (((usqInt)xValue << 3) | 1))); + longAtput((void *)((pointResult + BaseHeaderSize) + ((((usqInt)(XIndex) << (shiftForWord()))))),(((usqInt)xValue << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pointResult)) + && (!(isForwarded(pointResult)))); + assert(validStorePointerUncheckedArgs(YIndex, pointResult, (((usqInt)yValue << 3) | 1))); + longAtput((void *)((pointResult + BaseHeaderSize) + ((((usqInt)(YIndex) << (shiftForWord()))))),(((usqInt)yValue << 3) | 1)); + oop = pointResult; + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Return the number of seconds since January 1, 1901 as an integer. */ + + /* InterpreterPrimitives>>#primitiveSecondsClock */ +static void +primitiveSecondsClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = (((usqInt)((((usqInt)(ioSecondsNow()))) & 0xFFFFFFFFU) << 3) | 1); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Ask the GUI to set the requested display mode. + See DisplayScreen class depth:width:height:fullscreen: */ + + /* InterpreterPrimitives>>#primitiveSetDisplayMode */ +static void +primitiveSetDisplayMode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt d; + sqInt fsFlag; + sqInt h; + sqInt integerPointer; + sqInt okay; + char *sp; + sqInt w; + + /* begin booleanValueOf: */ + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + fsFlag = 1; + goto l1; + } + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + fsFlag = 0; + goto l1; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + fsFlag = null; + /* end booleanValueOf: */ +l1: + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + h = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + h = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + w = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + w = 0; + } + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + if ((((integerPointer) & 7) == 1)) { + d = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + d = 0; + } + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + okay = ioSetDisplayMode(w, h, d, fsFlag); + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrOperationFailed; + return; + } + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (okay + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; +} + + +/* On platforms that support it, set full-screen mode to the value of the + boolean argument. + */ + + /* InterpreterPrimitives>>#primitiveSetFullScreen */ +static void +primitiveSetFullScreen(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt argOop; + + argOop = longAt(GIV(stackPointer)); + if (argOop == GIV(trueObj)) { + ioSetFullScreen(1); + } + else { + if (argOop == GIV(falseObj)) { + ioSetFullScreen(0); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + if (!GIV(primFailCode)) { + /* begin setFullScreenFlag: */ + imageHeaderFlags = (argOop == GIV(trueObj) + ? imageHeaderFlags | 1 + : ((imageHeaderFlags | 1) - 1)); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } +} + + /* InterpreterPrimitives>>#primitiveSetImmutability */ +#if IMMUTABILITY +static void +primitiveSetImmutability(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt processLists; + sqInt rcvr; + sqInt scheduler; + char *sp; + sqInt wasImmutable; + + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if ((GIV(argumentCount) > 1) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* fail if forwarded; the retry machinery will fix things up. */ + wasImmutable = +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ; + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + /* begin canBeImmutable: */ + assert(isNonImmediate(rcvr)); + + /* For now we fail the primitive for contexts to we ensure there are no immutable contexts. + Later we can consider having immutable contexts and send cannotReturn callback + when returning to an immutable context. That would mean that setting a context + to immutable would require a divorce and returns to immutable context are + necessarily across stack pages */ + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + goto l1; + } + + /* Weak structures can't be immutable */ + + /* begin isEphemeron: */ + assert(isNonImmediate(rcvr)); + format = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + if (format == (ephemeronFormat())) { + goto l1; + } + + /* begin isWeakNonImm: */ + format = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + if (format == (weakArrayFormat())) { + goto l1; + } + + /* No clue what is going on for semaphores so they can't be immutable */ + if (((longAt((void *)(rcvr))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))) { + goto l1; + } + + /* Simple version of process management: we forbid Process and LinkedList instances to be immutable + as well as the Processor and the array of activeProcess */ + + /* begin fetchPointer:ofObject: */ + scheduler = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + processLists = longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + if (rcvr == scheduler) { + goto l1; + } + if (rcvr == processLists) { + goto l1; + } + + /* Is it a linkedList ? */ + if ((classIndexOf(longAt((void *)((processLists + BaseHeaderSize) + (1U << (shiftForWord())))))) == ((longAt((void *)(rcvr))) & (classIndexMask()))) { + goto l1; + } + + /* is it a Process ? */ + if ((classIndexOf(longAt((void *)((scheduler + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))))) == ((longAt((void *)(rcvr))) & (classIndexMask()))) { + goto l1; + } + + /* The rest of the code is relative to process management: the Processor (the active + process scheduler) can't be immutable, as well as all the objects relative to Process management + scheduler := self fetchPointer: ValueIndex ofObject: (self splObj: SchedulerAssociation). + processLists := objectMemory fetchPointer: ProcessListsIndex ofObject: scheduler. + ((objectMemory formatOf: oop) = objectMemory nonIndexablePointerFormat) + ifFalse: + [ (objectMemory isArrayNonImm: oop) ifFalse: [^ true]. + ^ (oop = processLists) not ]. + (objectMemory numSlotsOf: oop) >= 2 ifFalse: [^ true]. + + is the oop the scheduler itself ? + oop = scheduler ifTrue: [^ false]. + 1 to: (objectMemory numSlotsOf: processLists) do: [ :i | + + is the oop one of the linked lists ? + (list := processLists at: i) = oop ifTrue: [^ false]. + + is the oop one of the runnable process ? + first := objectMemory fetchPointer: FirstLinkIndex ofObject: list. + first = objectMemory nilObject ifFalse: + [ last := objectMemory fetchPointer: LastLinkIndex ofObject: list. + link := first. + [ link = last ] whileFalse: + [ link = oop ifTrue: [^ false]. + link := objectMemory fetchPointer: NextLinkIndex ofObject: link. ] ] ]. */ + goto l2; +l1: + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + return; +l2: + + /* begin setIsImmutableOf:to: */ + longAtput((void *)(rcvr),(longAt((void *)(rcvr))) | (1U << (immutableBitShift()))); + } + else { + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + /* begin setIsImmutableOf:to: */ + longAtput((void *)(rcvr),(longAt((void *)(rcvr))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (wasImmutable + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; +} +#endif /* IMMUTABILITY */ + + +/* Set the user interrupt keycode. The keycode is an integer whose encoding + is described in the comment for primitiveKbdNext. + */ + + /* InterpreterPrimitives>>#primitiveSetInterruptKey */ +static void +primitiveSetInterruptKey(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerPointer; + sqInt keycode; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + keycode = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + keycode = 0; + } + if (!GIV(primFailCode)) { + /* begin setInterruptKeycode: */ + GIV(interruptKeycode) = keycode; + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } +} + + +/* Primitive. Set the VM's log directory */ + + /* InterpreterPrimitives>>#primitiveSetLogDirectory */ +EXPORT(sqInt) +primitiveSetLogDirectory(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + usqInt numBytes; + usqInt numSlots; + sqInt stringOop; + sqInt sz; + + stringOop = longAt(GIV(stackPointer)); + if (!(/* isBytes: */ + ((!(stringOop & (tagMask())))) + && (((byteAt((void *)(stringOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + + /* begin byteSizeOf: */ + if (((stringOop & (tagMask())) != 0)) { + sz = 0; + goto l1; + } + + /* begin numBytesOf: */ + fmt = (byteAt((void *)(stringOop + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(stringOop)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(stringOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(stringOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + sz = numBytes - (fmt & 7); + goto l1; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + sz = numBytes; + goto l1; + } + if (fmt >= (firstShortFormat())) { + sz = numBytes - (((fmt & 3) << 1)); + goto l1; + } + + /* fmt >= self firstLongFormat */ + sz = numBytes - (((fmt & 1) << 2)); + /* end byteSizeOf: */ +l1: + ioSetLogDirectoryOfSize(firstIndexableField(stringOop), sz); + if (!GIV(primFailCode)) { + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } + return 0; +} + + /* InterpreterPrimitives>>#primitiveSetOrHasIdentityHash */ +static void +primitiveSetOrHasIdentityHash(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt hasHash; + sqInt hash; + sqInt isReceiverAClass; + sqInt lastArg; + int oldHash; + char *sp; + sqInt thisReceiver; + + if (!GIV(argumentCount)) { + hasHash = ((!((longAt(GIV(stackPointer))) & (tagMask())))) + && ((long32At((void *)((longAt(GIV(stackPointer))) + 4))) & (identityHashHalfWordMask())); + + /* begin methodReturnBool: */ + assert(!((failed()))); + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (hasHash + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return; + } + isReceiverAClass = 0; + if (GIV(argumentCount) == 2) { + lastArg = longAt(GIV(stackPointer)); + if ((((lastArg) & 7) == 1)) { + hash = lastArg; + thisReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + } + else { + thisReceiver = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + hash = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (lastArg == GIV(trueObj)) { + isReceiverAClass = 1; + } + else { + if (!(lastArg == GIV(falseObj))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + } + } + else { + thisReceiver = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + hash = longAt(GIV(stackPointer)); + } + + /* anObject primitiveSetIdentityHashTo: hash */ + if (!(((((hash) & 7) == 1)) + && ((/* if written naturally, clang warns: unsequenced modification and access to 'hash' */ + (hash = (hash >> 3)), + (hash & (identityHashHalfWordMask())) == hash)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (!((!(thisReceiver & (tagMask()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = (thisReceiver == (longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord))) + ? PrimErrBadReceiver + : PrimErrBadArgument); + return; + } + oldHash = (long32At((void *)(thisReceiver + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(thisReceiver + 4),((((long32At((void *)(thisReceiver + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + if (isReceiverAClass) { + classAtIndexput(hash, thisReceiver); + + /* this figures out if the index is ambiguous and fixes all the instances if needed */ + allInstancesOf(thisReceiver); + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)oldHash << 3) | 1)); +} + + +/* Treat the receiver, which can be indexible by either bytes or words, as + an array of signed 16-bit values. Answer the contents of the given index. + Note that the index specifies the i-th 16-bit entry, not the i-th byte or + word. */ + + /* InterpreterPrimitives>>#primitiveShortAt */ +static void +primitiveShortAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt rcvr; + sqInt value; + + index = longAt(GIV(stackPointer)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(/* isWordsOrBytes: */ + ((!(rcvr & (tagMask())))) + && (isWordsOrBytesNonImm(rcvr)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + return; + } + index = (index >> 3); + if (!((index >= 1) + && (index <= (((usqInt)((numBytesOf(rcvr)))) >> 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + value = shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 1))))); + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)value << 3) | 1)); +} + + +/* Treat the receiver, which can be indexible by either bytes or words, as an + array of signed 16-bit values. Set the contents of the given index to the + given value. + Note that the index specifies the i-th 16-bit entry, not the i-th byte or + word. + */ + + /* InterpreterPrimitives>>#primitiveShortAtPut */ +static void +primitiveShortAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt rcvr; + sqInt value; + + value = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(((((value) & 7) == 1)) + && (((((index) & 7) == 1)) + && (((value = (value >> 3)), + (value >= -32768) + && (value <= 0x7FFF)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (!(/* isWordsOrBytes: */ + ((!(rcvr & (tagMask())))) + && (isWordsOrBytesNonImm(rcvr)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + return; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + return; + } + index = (index >> 3); + if (!((index >= 1) + && (index <= (((usqInt)((numBytesOf(rcvr)))) >> 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 1)))),value); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 2 * BytesPerWord),(((usqInt)value << 3) | 1)); +} + + +/* Set the low-water mark for free space. When the free space falls + below this level, the new and new: primitives fail and system attempts + to allocate space (e.g., to create a method context) cause the low-space + semaphore (if one is registered) to be signalled. */ + + /* InterpreterPrimitives>>#primitiveSignalAtBytesLeft */ +static void +primitiveSignalAtBytesLeft(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bytes; + + bytes = longAt(GIV(stackPointer)); + if (((((bytes) & 7) == 1)) + && (((bytes = (bytes >> 3))) >= 0)) { + /* begin lowSpaceThreshold: */ + GIV(lowSpaceThreshold) = bytes; + + /* N.B. The threshold > 0 guard eliminates a warning when + self lowSpaceThreshold: 0 + is inlined into setSignalLowSpaceFlagAndSaveProcess */ + if ((bytes > 0) + && (GIV(totalFreeOldSpace) < bytes)) { + growOldSpaceByAtLeast(bytes - GIV(totalFreeOldSpace)); + } + assert(GIV(totalFreeOldSpace) >= GIV(lowSpaceThreshold)); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } +} + + +/* Computes sine of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveSine */ +static void +primitiveSine(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + aValue = floatObjectOf(sin(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSize */ +static void +primitiveSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt totalLength; + + fmt = 0; + rcvr = longAt(GIV(stackPointer)); + if ((((rcvr & (tagMask())) != 0)) + || (((hdr = long64At((void *)(rcvr))), + (((fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()))) < (arrayFormat())) + || (fmt == (forwardedFormat()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + + /* Integers are not indexable + And if Spur, this is a forwarder. */ + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + primitiveContextSize(); + return; + } + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)(totalLength - fixedFields) << 3) | 1)); +} + + /* InterpreterPrimitives>>#primitiveSizeInBytes */ +static void +primitiveSizeInBytes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong byteSize; + sqInt oop; + + byteSize = /* totalByteSizeOf: */ + ((((longAt(GIV(stackPointer))) & (tagMask())) != 0) + ? 0 + : bytesInBody(longAt(GIV(stackPointer)))); + oop = positive64BitIntegerFor(byteSize); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} + + +/* Answer the byte size of an instance of the receiver. If num args > 0 + then the last argument is a variable size and the size answered is the + size of an instance of the receiver with that many indexable elements. */ + + /* InterpreterPrimitives>>#primitiveSizeInBytesOfInstance */ +static void +primitiveSizeInBytesOfInstance(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt byteSize; + sqInt classFormat; + int err; + sqInt instSpec; + usqInt numSlots; + sqInt numSlotsSqInt; + sqInt oop; + + if (GIV(argumentCount) > 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + + /* Support VMMirror>>byteSizeOfInstanceOf:WithIndexableVariables: */ + err = -1; + if (GIV(argumentCount) >= 1) { + if (!(((((longAt(GIV(stackPointer)))) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* begin byteSizeOfInstanceOf:withIndexableSlots:errInto: */ + classFormat = ((longAt((void *)(((longAt(GIV(stackPointer) + (1 * BytesPerWord))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + switch (instSpec) { + case arrayFormat(): + numSlots = ((longAt(GIV(stackPointer))) >> 3); + break; + case indexablePointersFormat(): + case weakArrayFormat(): + numSlots = (classFormat & ((1U << (fixedFieldsFieldWidth())) - 1)) + (((longAt(GIV(stackPointer))) >> 3)); + break; + case sixtyFourBitIndexableFormat(): + numSlots = ((longAt(GIV(stackPointer))) >> 3); + break; + case firstLongFormat(): + numSlots = ((((longAt(GIV(stackPointer))) >> 3)) + 1) / 2; + break; + case firstShortFormat(): + numSlots = ((((longAt(GIV(stackPointer))) >> 3)) + 3) / 4; + break; + case firstByteFormat(): + case firstCompiledMethodFormat(): + numSlots = ((((longAt(GIV(stackPointer))) >> 3)) + (BytesPerOop - 1)) / BytesPerOop; + break; + default: + byteSize = (err = PrimErrBadReceiver); + goto l1; + } + if (numSlots >= (((((usqInt)1)) << ((BytesPerOop * 8) - 3 /* logBytesPerOop */)))) { + byteSize = (err = ((((longAt(GIV(stackPointer))) >> 3)) < 0 + ? PrimErrBadArgument + : PrimErrLimitExceeded)); + goto l1; + } + byteSize = /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize); + /* end byteSizeOfInstanceOf:withIndexableSlots:errInto: */ +l1: + if (err >= 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = err; + return; + } + oop = positive64BitIntegerFor(byteSize); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return; + } + + /* begin byteSizeOfInstanceOf:errInto: */ + classFormat = ((longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + if (!(/* isFixedSizePointerFormat: */ + (instSpec <= (nonIndexablePointerFormat())) + || (instSpec == (ephemeronFormat())))) { + byteSize = (err = PrimErrBadReceiver); + goto l2; + } + numSlotsSqInt = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + byteSize = /* objectBytesForSlots: */ + (numSlotsSqInt + ? ((((usqInt)(numSlotsSqInt) << (shiftForWord())))) + ((numSlotsSqInt >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize); + /* end byteSizeOfInstanceOf:errInto: */ +l2: + if (err >= 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = err; + return; + } + + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),((byteSize << 3) | 1)); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatAdd */ +static void +primitiveSmallFloatAdd(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr + arg)); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatArctan */ +static void +primitiveSmallFloatArctan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + sqInt oop; + double rcvr; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + aValue = floatObjectOf(atan(rcvr)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatCosine */ +static void +primitiveSmallFloatCosine(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + sqInt oop; + double rcvr; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + aValue = floatObjectOf(cos(rcvr)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatDivide */ +static void +primitiveSmallFloatDivide(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + if (arg == 0.0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr / arg)); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatEqual */ +static void +primitiveSmallFloatEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) == intArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr == arg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + +/* Computes E raised to the receiver power. + Since SmallFloats cannot represent NaNs there's no need to special case. */ + + /* InterpreterPrimitives>>#primitiveSmallFloatExp */ +static void +primitiveSmallFloatExp(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + + aValue = floatObjectOf(exp(smallFloatValueOf(longAt(GIV(stackPointer))))); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + +/* Answer the exponent part of this float. */ + + /* InterpreterPrimitives>>#primitiveSmallFloatExponent */ +static void +primitiveSmallFloatExponent(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqInt exp; + + aValue = (((usqInt)(((/* begin exponentOfSmallFloat: */ + assert(((longAt(GIV(stackPointer))) & (tagMask())) == (smallFloatTag())), +((((usqInt)(longAt(GIV(stackPointer))))) <= 15 + ? 0 + : ((exp = ((((usqInt)(longAt(GIV(stackPointer)))))) >> (((numTagBits()) + (smallFloatMantissaBits())) + 1)), + (exp + (smallFloatExponentOffset())) - 0x3FE)))) - 1) << 3) | 1); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatFractionalPart */ +static void +primitiveSmallFloatFractionalPart(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double frac; + sqInt oop; + double rcvr; + double trunc; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + frac = modf(rcvr, (&trunc)); + aValue = floatObjectOf(frac); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatGreaterOrEqual */ +static void +primitiveSmallFloatGreaterOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) >= intArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr >= arg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatGreaterThan */ +static void +primitiveSmallFloatGreaterThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) > intArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr > arg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatLessOrEqual */ +static void +primitiveSmallFloatLessOrEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) <= intArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr <= arg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatLessThan */ +static void +primitiveSmallFloatLessThan(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) < intArg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr < arg + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } + } +} + + +/* Natural log. */ + + /* InterpreterPrimitives>>#primitiveSmallFloatLogN */ +static void +primitiveSmallFloatLogN(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + sqInt oop; + double rcvr; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + aValue = floatObjectOf(log(rcvr)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatMultiply */ +static void +primitiveSmallFloatMultiply(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr * arg)); + GIV(stackPointer) = sp; + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatNotEqual */ +static void +primitiveSmallFloatNotEqual(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt intArg; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + if (!GIV(primFailCode)) { + if ((rcvr == arg) + && (((((longAt(GIV(stackPointer)))) & 7) == 1))) { + intArg = ((longAt(GIV(stackPointer))) >> 3); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + ((((sqInt)rcvr)) == intArg + ? GIV(falseObj) + : GIV(trueObj))); + GIV(stackPointer) = sp; + } + else { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (rcvr == arg + ? GIV(falseObj) + : GIV(trueObj))); + GIV(stackPointer) = sp; + } + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatSine */ +static void +primitiveSmallFloatSine(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + sqInt oop; + double rcvr; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + aValue = floatObjectOf(sin(rcvr)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatSquareRoot */ +static void +primitiveSmallFloatSquareRoot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + sqInt oop; + double rcvr; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + if (rcvr >= 0.0) { + aValue = floatObjectOf(sqrt(rcvr)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + /* InterpreterPrimitives>>#primitiveSmallFloatSubtract */ +static void +primitiveSmallFloatSubtract(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + sqInt floatOrIntOop; + sqInt oop; + double rcvr; + double result; + char *sp; + sqInt tagBits; + double value; + + oop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + floatOrIntOop = longAt(GIV(stackPointer)); + + /* begin loadFloatOrIntFrom: */ + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l1; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((floatOrIntOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + arg = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntFrom: */ +l1: + if (!GIV(primFailCode)) { + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(rcvr - arg)); + GIV(stackPointer) = sp; + } +} + + +/* Multiply the receiver by the power of the argument. */ + + /* InterpreterPrimitives>>#primitiveSmallFloatTimesTwoPower */ +static void +primitiveSmallFloatTimesTwoPower(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt rcvr; + sqInt result; + sqInt twiceMaxExponent; + + arg = longAt(GIV(stackPointer)); + if (!((((arg) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* N.B. SmallFloats are finite. NaN and Infinity overflow into boxed floats. + This is doing range checking work that is done in ldexp, but we include + it explicitly to exemplify bit manipulation of SmallFloats. */ + + /* begin isSmallFloatZero: */ + assert(isImmediateFloat(rcvr)); + if ((((usqInt)rcvr)) <= ((1U << (numTagBits())) + (smallFloatTag()))) { + result = rcvr; + } + else { + arg = (arg >> 3); + twiceMaxExponent = 0x1000; + if (arg < (-twiceMaxExponent)) { + /* begin mapSignedSmallFloatToSignedSmallFloatZero: */ + assert(isImmediateFloat(rcvr)); + result = rcvr & ((1U << (numTagBits())) + (tagMask())); + } + else { + /* clip arg to at most int range; ldexp's last arg is of type int */ + if (arg > twiceMaxExponent) { + arg = twiceMaxExponent; + } + result = floatObjectOf(ldexp(smallFloatValueOf(rcvr), ((int) arg))); + } + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),result); +} + + /* InterpreterPrimitives>>#primitiveSmallFloatTruncated */ +static void +primitiveSmallFloatTruncated(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + sqInt oop; + double rcvr; + double trunc; + + oop = longAt(GIV(stackPointer)); + + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + modf(rcvr, (&trunc)); + if ((((trunc >= (((double) (MinSmallInteger) ))) && (trunc <= (((double) (MaxSmallInteger) ))))) + && ((((((usqInt)((((sqInt)trunc)))) >> 60) + 1) & 15) <= 1)) { + /* stackTopPut: */ + longAtput(GIV(stackPointer),(((usqInt)(((sqInt)trunc)) << 3) | 1)); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + /* InterpreterPrimitives>>#primitiveSomeInstance */ +static void +primitiveSomeInstance(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt instance; + + class = longAt(GIV(stackPointer)); + + /* For the mirror prims check that the class obj is actually a valid class. */ + instance = initialInstanceOf(class); + if (instance) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),instance); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Return the first object in the heap. */ + + /* InterpreterPrimitives>>#primitiveSomeObject */ +static void +primitiveSomeObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = firstAccessibleObject(); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); +} + + +/* Return the oop of the SpecialObjectsArray. */ + + /* InterpreterPrimitives>>#primitiveSpecialObjectsOop */ +static void +primitiveSpecialObjectsOop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),GIV(specialObjectsOop)); +} + + +/* Computes square root of float receiver; receiver *must* be a float + instance. + */ + + /* InterpreterPrimitives>>#primitiveSquareRoot */ +static void +primitiveSquareRoot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aValue; + usqLong bits; + double doubleValue; + sqInt rcvr; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + if (doubleValue >= 0.0) { + aValue = floatObjectOf(sqrt(doubleValue)); + + /* begin stackTopPut: */ + longAtput(GIV(stackPointer),aValue); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* This primitive is called from Squeak as... + storeSegmentFor: arrayOfRoots into: aWordArray outPointers: + anArray. + */ +/* This primitive will store a binary image segment (in the same format as + the Squeak image file) of the receiver and every object in its proper tree + of subParts (ie, that is not refered to from anywhere else outside the + tree). All pointers from within the tree to objects outside the tree will + be copied into the array of outpointers. In their place in the image + segment will be an oop equal to the offset in the outPointer array (the + first would be 4). but with the high bit set. + */ +/* The primitive expects the array and wordArray to be more than adequately + long. In this case it returns normally, and truncates the two arrays to + exactly the right size. To simplify truncation, both incoming arrays are + required to be whatever the objectMemory considers long objects. If either + array is too small, the primitive will fail, but in no other case. + + During operation of the primitive, it is necessary to convert from both + internal and external oops to their mapped values. To make this fast, the + headers of the original objects in question are replaced by the mapped + values (and this is noted by adding the forbidden XX header type). Tables + are kept of both kinds of oops, as well as of the original headers for + restoration. + To be specific, there are two similar two-part tables, the outpointer + array, and one in the upper fifth of the segmentWordArray. Each grows oops + from the bottom up, and preserved headers from halfway up. + + In case of either success or failure, the headers must be restored. In the + event of primitive failure, the table of outpointers must also be nilled + out (since the garbage in the high half will not have been discarded. + */ + + /* InterpreterPrimitives>>#primitiveStoreImageSegment */ +static void +primitiveStoreImageSegment(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayOfRoots; + sqInt ecode; + sqInt outPointerArray; + sqInt segmentWordArray; + + outPointerArray = longAt(GIV(stackPointer)); + segmentWordArray = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + arrayOfRoots = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + + /* Essential type checks */ + if (!((/* isArray: */ + ((!(arrayOfRoots & (tagMask())))) + && (((byteAt((void *)(arrayOfRoots + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((/* isArray: */ + ((!(outPointerArray & (tagMask())))) + && (((byteAt((void *)(outPointerArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (/* isWords: */ + ((!(segmentWordArray & (tagMask())))) + && (((((byteAt((void *)(segmentWordArray + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(segmentWordArray + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1)))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* Must be indexable pointers + Must be indexable words */ + ecode = storeImageSegmentIntooutPointersroots(segmentWordArray, outPointerArray, arrayOfRoots); + if (ecode == PrimErrNeedCompaction) { + fullGC(); + outPointerArray = longAt(GIV(stackPointer)); + segmentWordArray = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + arrayOfRoots = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + ecode = storeImageSegmentIntooutPointersroots(segmentWordArray, outPointerArray, arrayOfRoots); + } + if (ecode) { + /* primitiveFailFor: */ + GIV(primFailCode) = ecode; + } + else { + /* begin pop: */ + GIV(stackPointer) += 3 * BytesPerWord; + } +} + + /* InterpreterPrimitives>>#primitiveStringAt */ +static void +primitiveStringAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt result; + sqInt sp1; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + + /* begin commonAt: */ + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + index = longAt(GIV(stackPointer)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l5; + } + + /* No need to test for large positive integers here. No object has 1g elements */ + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 1) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l5; + } + index = (index >> 3); + + /* begin stObject:at: */ + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + sp1 = stackPointerIndexForFrame(frameOfMarriedContext(rcvr)); + assert((ReceiverIndex + ((sp1 >> 3))) < (lengthOf(rcvr))); + stSize = sp1; + goto l3; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l3; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(rcvr))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l3: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))))) { + result = temporaryin(index - 1, frameOfMarriedContext(rcvr)); + goto l4; + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(rcvr)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + result = (((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + (index - 1)))) << 3) | 1); + goto l4; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + result = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << (shiftForWord())))))); + goto l4; + } + if (fmt >= (firstByteFormat())) { + result = (((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + ((index + fixedFields) - 1)))) << 3) | 1); + goto l4; + } + if (fmt >= (firstShortFormat())) { + result = (((usqInt)(((unsigned short) (shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))))))) << 3) | 1); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + result = positive64BitIntegerFor(long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))))); + goto l4; + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + result = (((usqInt)((((usqInt)(long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + goto l4; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + result = 0; + /* end stObject:at: */ +l4: + if (!GIV(primFailCode)) { + if (!(((result >= 0) && (result <= (0x3FFFFFFF))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + goto l5; + } + result = ((((usqInt)(((result >> 3))) << (numTagBits())))) + (characterTag()); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); + } + /* end commonAt: */ +l5:; +} + + /* InterpreterPrimitives>>#primitiveStringAtPut */ +static void +primitiveStringAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fieldIndex; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt signedValueToStore; + sqInt signedValueToStoreSqInt; + sqInt sp; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + usqLong unsigned64BitValueToStore; + unsigned int unsignedValueToStore; + sqInt value; + sqInt valueSqInt; + + /* begin commonAtPut: */ + /* begin initPrimCall */ + GIV(primFailCode) = 0; + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + value = longAt(GIV(stackPointer)); + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l11; + } + + /* No need to test for large positive integers here. No object has 1g elements */ + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 2) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l11; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l11; + } + index = (index >> 3); + valueSqInt = asciiOfCharacter(value); + + /* begin stObject:at:put: */ + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l1: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(rcvr)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(rcvr))); + stSize = sp; + goto l2; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l2; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(rcvr))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l2: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))))) { + temporaryinput(index - 1, frameOfMarriedContext(rcvr), valueSqInt); + goto l5; + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(rcvr)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + if (((((valueSqInt) & 7) == 1)) + && ((((usqInt)((signedValueToStore = (valueSqInt >> 3))))) <= 0xFF)) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + (index - 1)),signedValueToStore); + goto l5; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l5; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l3; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l3; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l3: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:storing:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + fieldIndex = (index + fixedFields) - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, rcvr, valueSqInt)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valueSqInt & (tagMask())))) + && (oopisLessThan(valueSqInt, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valueSqInt); + goto l4; + } + if (fmt >= (firstByteFormat())) { + if (!((((valueSqInt) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStoreSqInt = (valueSqInt >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + ((index + fixedFields) - 1)),signedValueToStoreSqInt); + goto l4; + } + if (fmt >= (firstShortFormat())) { + if (!((((valueSqInt) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStoreSqInt = (valueSqInt >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))),signedValueToStoreSqInt); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(valueSqInt); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l4; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(valueSqInt); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l4: + goto l5; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + /* end stObject:at:put: */ +l5: + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + } + /* end commonAtPut: */ +l11:; +} + + +/* primReplaceFrom: start to: stop with: replacement startingAt: + repStart + */ + + /* InterpreterPrimitives>>#primitiveStringReplace */ +static void +primitiveStringReplace(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt array; + sqInt arrayFmt; + sqInt arrayInstSize; + usqInt arrayLength; + sqInt class; + sqInt i; + sqInt mustRemember; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt repl; + sqInt replFmt; + sqInt replInstSize; + usqInt replLength; + sqInt replStart; + sqInt srcDelta; + sqInt start; + sqInt stop; + sqInt valuePointer; + + /* begin primitiveSpurStringReplace */ + array = longAt(GIV(stackPointer) + (4 * BytesPerWord)); + start = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + stop = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + repl = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + replStart = longAt(GIV(stackPointer)); + if (((!(start & (smallIntegerTag())))) + || (((!(stop & (smallIntegerTag())))) + || (((!(replStart & (smallIntegerTag())))) + || (((repl & (tagMask())) != 0))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l7; + } + + /* can happen in LgInt copy */ + start = (start >> 3); + stop = (stop >> 3); + replStart = (replStart >> 3); + if ((stop >= start) + && ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(array + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + )) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + goto l7; + } + arrayFmt = (byteAt((void *)(array + (formatFieldByteOffset())))) & (formatMask()); + replFmt = (byteAt((void *)(repl + (formatFieldByteOffset())))) & (formatMask()); + + /* N.B. In the below start - 1 to: stop - 1 do:, Slang is intelligent enough to use < instead of <= so avoiding the stop - 1. */ + if (arrayFmt <= 5 /* lastPointerFormat */) { + /* Array formats must be the same; but for copying, weak arrays are equivalent to arrays. */ + if (arrayFmt == (weakArrayFormat())) { + arrayFmt = arrayFormat(); + } + if (replFmt == (weakArrayFormat())) { + replFmt = arrayFormat(); + } + if (arrayFmt != replFmt) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l7; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(array)) > (isForwardedObjectClassIndexPun())); + arrayLength = (((numSlots = byteAt((void *)(array + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + + /* begin fixedFieldsOf:format:length: */ + if ((arrayFmt >= (sixtyFourBitIndexableFormat())) + || (arrayFmt == (arrayFormat()))) { + arrayInstSize = 0; + goto l5; + } + if (arrayFmt < (arrayFormat())) { + arrayInstSize = arrayLength; + goto l5; + } + class = fetchClassOfNonImm(array); + arrayInstSize = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l5: + + /* begin numSlotsOf: */ + assert((classIndexOf(repl)) > (isForwardedObjectClassIndexPun())); + replLength = (((numSlots = byteAt((void *)(repl + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(repl - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + + /* begin fixedFieldsOf:format:length: */ + if ((replFmt >= (sixtyFourBitIndexableFormat())) + || (replFmt == (arrayFormat()))) { + replInstSize = 0; + goto l6; + } + if (replFmt < (arrayFormat())) { + replInstSize = replLength; + goto l6; + } + class = fetchClassOfNonImm(repl); + replInstSize = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l6: + if (!((start >= 1) + && (((start - 1) <= stop) + && (((stop + arrayInstSize) <= arrayLength) + && ((replStart >= 1) + && ((((stop - start) + replStart) + replInstSize) <= replLength)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l7; + } + start += arrayInstSize; + stop += arrayInstSize; + srcDelta = (replStart + replInstSize) - start; + + /* begin isOldObject: */ + assert(isNonImmediate(array)); + if (oopisGreaterThanOrEqualTo(array, GIV(oldSpaceStart))) { + mustRemember = 0; + for (i = (start - 1); i < stop; i += 1) { + oop = longAt((void *)((repl + BaseHeaderSize) + ((((usqInt)((srcDelta + i)) << (shiftForWord())))))); + if (/* isYoung: */ + ((!(oop & (tagMask())))) + && (oopisLessThan(oop, GIV(oldSpaceStart)))) { + mustRemember = 1; + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(i, array, oop)); + longAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),oop); + } + if (mustRemember) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(array + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(array); + } + } + } + else { + for (i = (start - 1); i < stop; i += 1) { + valuePointer = longAt((void *)((repl + BaseHeaderSize) + ((((usqInt)((srcDelta + i)) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(array)) + && (!(isForwarded(array)))); + assert(validStorePointerUncheckedArgs(i, array, valuePointer)); + longAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + } + + /* We might consider comparing stop - start to some value here and using forceInterruptCheck */ + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + goto l7; + } + + /* Non-pointer array formats must match */ + + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(array + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (arrayFmt <= (ephemeronFormat())) { + arrayLength = ((sqInt) numSlots); + goto l3; + } + if (arrayFmt >= (firstByteFormat())) { + arrayLength = ((numSlots << (shiftForWord()))) - (arrayFmt & 7); + goto l3; + } + + /* bytes, including CompiledMethod */ + if (arrayFmt >= (firstShortFormat())) { + arrayLength = ((numSlots << ((shiftForWord()) - 1))) - (arrayFmt & 3); + goto l3; + } + if (arrayFmt >= (firstLongFormat())) { + arrayLength = ((numSlots << ((shiftForWord()) - 2))) - (arrayFmt & 1); + goto l3; + } + if (arrayFmt == (sixtyFourBitIndexableFormat())) { + arrayLength = ((sqInt) numSlots); + goto l3; + } + + /* fmt = self forwardedFormat */ + arrayLength = 0; + /* end lengthOf:format: */ +l3: + + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(repl + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(repl - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (replFmt <= (ephemeronFormat())) { + replLength = ((sqInt) numSlots); + goto l4; + } + if (replFmt >= (firstByteFormat())) { + replLength = ((numSlots << (shiftForWord()))) - (replFmt & 7); + goto l4; + } + + /* bytes, including CompiledMethod */ + if (replFmt >= (firstShortFormat())) { + replLength = ((numSlots << ((shiftForWord()) - 1))) - (replFmt & 3); + goto l4; + } + if (replFmt >= (firstLongFormat())) { + replLength = ((numSlots << ((shiftForWord()) - 2))) - (replFmt & 1); + goto l4; + } + if (replFmt == (sixtyFourBitIndexableFormat())) { + replLength = ((sqInt) numSlots); + goto l4; + } + + /* fmt = self forwardedFormat */ + replLength = 0; + /* end lengthOf:format: */ +l4: + + /* begin classFormatFromInstFormat: */ + if (arrayFmt >= (firstByteFormat())) { + arrayFmt = arrayFmt & -8; + goto l1; + } + + /* this is likely the common case */ + if (arrayFmt <= (sixtyFourBitIndexableFormat())) { + goto l1; + } + if (arrayFmt < (firstShortFormat())) { + arrayFmt = arrayFmt & -2; + goto l1; + } + arrayFmt = arrayFmt & -4; + /* end classFormatFromInstFormat: */ +l1: + + /* begin classFormatFromInstFormat: */ + if (replFmt >= (firstByteFormat())) { + replFmt = replFmt & -8; + goto l2; + } + + /* this is likely the common case */ + if (replFmt <= (sixtyFourBitIndexableFormat())) { + goto l2; + } + if (replFmt < (firstShortFormat())) { + replFmt = replFmt & -2; + goto l2; + } + replFmt = replFmt & -4; + /* end classFormatFromInstFormat: */ +l2: + if (!((arrayFmt == replFmt) + && ((arrayFmt >= (sixtyFourBitIndexableFormat())) + && (arrayFmt < (firstCompiledMethodFormat()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + goto l7; + } + if (!((start >= 1) + && (((start - 1) <= stop) + && ((stop <= arrayLength) + && ((replStart >= 1) + && (((stop - start) + replStart) <= replLength)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + goto l7; + } + srcDelta = replStart - start; + if (arrayFmt >= (firstShortFormat())) { + if (arrayFmt >= (firstByteFormat())) { + for (i = (start - 1); i < stop; i += 1) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((array + BaseHeaderSize) + i),byteAt((void *)((repl + BaseHeaderSize) + (srcDelta + i)))); + } + } + else { + for (i = (start - 1); i < stop; i += 1) { + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << 1)))),shortAt((void *)((repl + BaseHeaderSize) + ((((usqInt)((srcDelta + i)) << 1)))))); + } + } + } + else { + if (arrayFmt >= (firstLongFormat())) { + for (i = (start - 1); i < stop; i += 1) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << 2)))),long32At((void *)((repl + BaseHeaderSize) + ((((usqInt)((srcDelta + i)) << 2)))))); + } + } + else { + for (i = (start - 1); i < stop; i += 1) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((array + BaseHeaderSize) + ((((usqInt)(i) << 3)))),long64At((void *)((repl + BaseHeaderSize) + ((((usqInt)((srcDelta + i)) << 3)))))); + } + } + } + + /* 8 & 16-bit word type objects + 32 & 64-bit word type objects + We might consider comparing stop - start to some value here and using forceInterruptCheck */ + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + /* end primitiveSpurStringReplace */ +l7:; +} + + /* InterpreterPrimitives>>#primitiveSubtract */ +static void +primitiveSubtract(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerResult; + + integerResult = (stackIntegerValue(1)) - (stackIntegerValue(0)); + + /* begin pop2AndPushIntegerIfOK: */ + if (!GIV(primFailCode)) { + if ((((((usqInt)(integerResult)) >> 60) + 1) & 15) <= 1) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),(((usqInt)integerResult << 3) | 1)); + } + else { + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } +} + + +/* Primitive arithmetic operations for large integers in 64 bit range */ + + /* InterpreterPrimitives>>#primitiveSubtractLargeIntegers */ +EXPORT(void) +primitiveSubtractLargeIntegers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong a; + sqInt aIsNegative; + usqLong b; + sqInt bIsNegative; + sqInt oopArg; + sqInt oopRcvr; + sqInt oopResult; + usqLong result; + sqInt resultIsNegative; + + oopArg = longAt(GIV(stackPointer)); + oopRcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aIsNegative = isNegativeIntegerValueOf(oopRcvr); + bIsNegative = isNegativeIntegerValueOf(oopArg); + a = magnitude64BitValueOf(oopRcvr); + b = magnitude64BitValueOf(oopArg); + if (GIV(primFailCode)) { + return; + } + if (aIsNegative != bIsNegative) { + /* Protect against overflow */ + if (a > (0xFFFFFFFFFFFFFFFFULL - b)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + result = a + b; + resultIsNegative = aIsNegative; + } + else { + if (a >= b) { + result = a - b; + resultIsNegative = aIsNegative; + } + else { + result = b - a; + resultIsNegative = !aIsNegative; + } + } + oopResult = magnitude64BitIntegerForneg(result, resultIsNegative); + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),oopResult); + } +} + + +/* Attempt to test-and-set the ownership of the critical section. If not + owned, set the owner to the current process and answer false. If owned by + the current process answer true. If owned by some other process answer + nil. For simulation if there is an argument it is taken to be the + effective activeProcess + (see Process>>effectiveProcess). */ + + /* InterpreterPrimitives>>#primitiveTestAndSetOwnershipOfCriticalSection */ +static void +primitiveTestAndSetOwnershipOfCriticalSection(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + sqInt criticalSection; + sqInt objOop; + sqInt owningProcess; + sqInt owningProcessIndex; + + if (GIV(argumentCount) > 0) { + /* rcvr */ + criticalSection = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + activeProc = longAt(GIV(stackPointer)); + if (/* isOopForwarded: */ + ((!(activeProc & (tagMask())))) + && ((!((longAt((void *)(activeProc))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + } + else { + /* rcvr */ + criticalSection = longAt(GIV(stackPointer)); + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + } + + /* CriticalSections are laid out like Semaphores */ + owningProcessIndex = ExcessSignalsIndex; + owningProcess = longAt((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord())))))); + if (owningProcess == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(owningProcessIndex, criticalSection, activeProc)); + assert(isNonImmediate(criticalSection)); + if (oopisGreaterThanOrEqualTo(criticalSection, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeProc & (tagMask())))) + && (oopisLessThan(activeProc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(criticalSection + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(criticalSection); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((criticalSection + BaseHeaderSize) + ((((usqInt)(owningProcessIndex) << (shiftForWord()))))),activeProc); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(falseObj)); + return; + } + if (owningProcess == activeProc) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(trueObj)); + return; + } + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),GIV(nilObj)); +} + + +/* Return true if the host OS does support the given display depth. */ + + /* InterpreterPrimitives>>#primitiveTestDisplayDepth */ +static void +primitiveTestDisplayDepth(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bitsPerPixel; + sqInt integerPointer; + sqInt okay; + char *sp; + + okay = 0; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + bitsPerPixel = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + bitsPerPixel = 0; + } + if (!GIV(primFailCode)) { + okay = ioHasDisplayDepth(bitsPerPixel); + } + if (!GIV(primFailCode)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (okay + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + } +} + + +/* Given an object with indexable pointer fields, reduce the size of the + indexable fields + to the requested size. Answer the number of bytes freed, or zero if the + object cannot + be shortened. + */ + + /* InterpreterPrimitives>>#primitiveTestShortenIndexableSize */ +#if TestingPrimitives +EXPORT(sqInt) +primitiveTestShortenIndexableSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt array; + sqInt delta; + sqInt indexableSize; + sqInt integer; + sqInt newSizeOop; + usqInt numSlots; + + newSizeOop = longAt(GIV(stackPointer)); + array = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!(((((newSizeOop) & 7) == 1)) + && (((((newSizeOop >> 3)) >= 0) && (((newSizeOop >> 3)) <= ((stSizeOf(array)) - 1)))))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + if (!(/* canShorten: */ + ((!(array & (tagMask())))) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(array)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(array + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > 0) + && ((((byteAt((void *)(array + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())) + || (((((byteAt((void *)(array + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(array + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1)))))))) { + return (GIV(primFailCode) = PrimErrBadReceiver); + } + indexableSize = (newSizeOop >> 3); + + /* begin shorten:toIndexableSize: */ + assert((indexableSize >= 0) + && (indexableSize < (lengthOf(array)))); + delta = doShortentoIndexableSize(array, indexableSize); + assert((lengthOf(followMaybeForwarded(array))) == indexableSize); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + integer = delta; + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); + return 0; +} +#endif /* TestingPrimitives */ + + +/* Multiply the receiver by the power of the argument. + Receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveTimesTwoPower */ +static void +primitiveTimesTwoPower(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aFloatOop; + sqInt arg; + usqLong bits; + double rcvr; + double result; + char *sp; + sqInt twiceMaxExponent; + + arg = longAt(GIV(stackPointer)); + if (!((((arg) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + arg = (arg >> 3); + + /* clip arg to at most int range; ldexp's last arg is of type int */ + twiceMaxExponent = 0x1000; + if (arg < (-twiceMaxExponent)) { + arg = -twiceMaxExponent; + } + else { + if (arg > twiceMaxExponent) { + arg = twiceMaxExponent; + } + } + aFloatOop = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(aFloatOop)); + if (aFloatOop & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(aFloatOop)); + bits = ((((usqInt)aFloatOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&rcvr), (&bits), sizeof(rcvr)); + } + else { + fetchFloatAtinto(aFloatOop + BaseHeaderSize, rcvr); + } + result = ldexp(rcvr, ((int) arg)); + + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),floatObjectOf(result)); + GIV(stackPointer) = sp; +} + + +/* Integral part of float receiver; receiver *must* be a float instance. */ + + /* InterpreterPrimitives>>#primitiveTruncated */ +static void +primitiveTruncated(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + double doubleValue; + sqInt rcvr; + double trunc; + + rcvr = longAt(GIV(stackPointer)); + + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(rcvr)); + if (rcvr & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvr)); + bits = ((((usqInt)rcvr))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&doubleValue), (&bits), sizeof(doubleValue)); + } + else { + fetchFloatAtinto(rcvr + BaseHeaderSize, doubleValue); + } + modf(doubleValue, (&trunc)); + if (((trunc >= (((double) (MinSmallInteger) ))) && (trunc <= (((double) (MaxSmallInteger) ))))) { + /* stackTopPut: */ + longAtput(GIV(stackPointer),(((usqInt)(((sqInt)trunc)) << 3) | 1)); + return; + } + + /* make Nicolas Cellier's otherwise recursive BoxedFloat64>>truncated nice 2/7/2025 07:34 work on 32-bits. */ + + /* a.k.a. trunc abs <= Float maxExactInteger asFloat */ + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } +} + + +/* Allocate a new indexable instance. Fail if the allocation would leave less + than lowSpaceThreshold bytes free. May cause a GC. + */ + + /* InterpreterPrimitives>>#primitiveUninitializedNewWithArg */ +static void +primitiveUninitializedNewWithArg(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt classObj; + sqInt err; + int hash; + sqInt instSpec; + sqInt instSpecSqInt; + sqInt newObj; + usqInt newObjUsqInt; + usqInt numBytes; + usqInt numSlots; + sqInt obj; + sqInt reasonCode; + usqIntptr_t size; + sqInt value; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + size = null; + goto l1; + } + size = value; + goto l1; + } + + /* don't inline the rare case */ + size = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* positiveMachineIntegerValueOf: succeeds only for non-negative integers. */ + classObj = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin instantiateUninitializedClass:indexableSize: */ + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpecSqInt = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + assert(isPureBitsFormat(instSpecSqInt)); + switch (instSpecSqInt) { + case sixtyFourBitIndexableFormat(): + numSlots = size; + break; + case firstLongFormat(): + if ((classIndex == ClassFloatCompactIndex) + && (size != 2)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + obj = null; + goto l3; + } + numSlots = (size + 1) / 2; + instSpecSqInt += size & 1; + break; + case firstShortFormat(): + numSlots = (size + 3) / 4; + instSpecSqInt += (4 - size) & 3; + break; + case firstByteFormat(): + numSlots = (size + 7) / 8; + instSpecSqInt += (8 - size) & 7; + break; + default: + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + obj = null; + goto l3; + } + + /* not bits indexable */ + if (!classIndex) { + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + obj = null; + goto l3; + } + } + if (numSlots > ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (numSlots > (0x10000000000LL)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + obj = null; + goto l3; + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpecSqInt, classIndex); + } + else { + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if (((numSlots) >> 56) > 0) { + newObj = null; + goto l2; + } + newObjUsqInt = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObjUsqInt = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, instSpecSqInt, classIndex); + goto l2; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObjUsqInt),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(instSpecSqInt) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObjUsqInt),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(instSpecSqInt) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObjUsqInt % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newObj = newObjUsqInt; + /* end allocateSlots:format:classIndex: */ +l2:; + } + obj = newObj; + /* end instantiateUninitializedClass:indexableSize: */ +l3: + if (obj) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + instSpec = (((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer) + (1 * BytesPerWord))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + reasonCode = (/* isPureBitsFormat: */ + (instSpec >= (sixtyFourBitIndexableFormat())) + && (instSpec < (firstCompiledMethodFormat())) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + +/* Allocate a new pinned indexable instance with junk contents, saving the + time for initializing with 0. Fail if the receiver us not a pure bits + class. Fail + if the allocation would leave less than lowSpaceThreshold bytes free. */ + + /* InterpreterPrimitives>>#primitiveUninitializedPinnedNewWithArg */ +static void +primitiveUninitializedPinnedNewWithArg(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt classObj; + sqInt err; + int hash; + sqInt instSpec; + sqInt instSpecSqInt; + sqInt newObj; + usqInt numSlots; + sqInt obj; + sqInt reasonCode; + usqIntptr_t size; + sqInt value; + + + /* For the mirror prims check that the class obj is actually a valid class. */ + + /* begin positiveMachineIntegerValueOf: */ + if (((((longAt(GIV(stackPointer)))) & 7) == 1)) { + value = ((longAt(GIV(stackPointer))) >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + size = null; + goto l1; + } + size = value; + goto l1; + } + + /* don't inline the rare case */ + size = positiveMachineIntegerValueOfObj(longAt(GIV(stackPointer))); + /* end positiveMachineIntegerValueOf: */ +l1: + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* positiveMachineIntegerValueOf: succeeds only for non-negative integers. */ + classObj = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin inOldSpaceInstantiateUninitializedPinnedClass:indexableSize: */ + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpecSqInt = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + switch (instSpecSqInt) { + case sixtyFourBitIndexableFormat(): + numSlots = size; + break; + case firstLongFormat(): + if ((classIndex == ClassFloatCompactIndex) + && (size != 2)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + obj = null; + goto l2; + } + numSlots = (size + 1) / 2; + instSpecSqInt += size & 1; + break; + case firstShortFormat(): + numSlots = (size + 3) / 4; + instSpecSqInt += (4 - size) & 3; + break; + case firstByteFormat(): + numSlots = (size + 7) / 8; + instSpecSqInt += (8 - size) & 7; + break; + default: + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + obj = null; + goto l2; + } + + /* non-indexable and/or pointers */ + if (!classIndex) { + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + obj = null; + goto l2; + } + } + if (numSlots > (0x10000000000LL)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + obj = null; + goto l2; + } + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpecSqInt, classIndex); + obj = newObj; + /* end inOldSpaceInstantiateUninitializedPinnedClass:indexableSize: */ +l2: + if (obj) { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),obj); + } + else { + instSpec = (((usqInt)((((longAt((void *)(((longAt(GIV(stackPointer) + (1 * BytesPerWord))) + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + reasonCode = (/* isPureBitsFormat: */ + (instSpec >= (sixtyFourBitIndexableFormat())) + && (instSpec < (firstCompiledMethodFormat())) + ? PrimErrNoMemory + : PrimErrBadReceiver); + + /* begin primitiveFailFor: */ + GIV(primFailCode) = reasonCode; + } +} + + +/* Update the VMs notion of the current timezone. The VM sets its notion + of the timezone once at start-up. If one wants the VM to keep its notion + up-to-date arrange to invoke this primitive periodically. */ + + /* InterpreterPrimitives>>#primitiveUpdateTimezone */ +static void +primitiveUpdateTimezone(void) +{ + ioUpdateVMTimezone(); +} + + +/* Return the value of the microsecond clock in UTC as an integer. + This is the number of microseconds since the Smalltalk epoch, 1901/1/1 + 12:00am. The microsecond clock is at least 60 bits wide which means it'll + get to around August + 38435 before it wraps around. Be sure to put it on your calendar. This + primitive accesses the time as answered by the OS. */ + + /* InterpreterPrimitives>>#primitiveUTCMicrosecondClock */ +static void +primitiveUTCMicrosecondClock(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = positive64BitIntegerFor(ioUTCMicrosecondsNow()); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); +} + + +/* Answer an array with UTC microseconds since the Smalltalk epoch and the + current seconds offset from GMT in the local time zone. Any pointer object + with two or more slots) may be supplied as a parameter. */ + + /* InterpreterPrimitives>>#primitiveUtcAndTimezoneOffset */ +static void +primitiveUtcAndTimezoneOffset(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt resultArray; + sqInt valuePointer; + + if (GIV(argumentCount) > 0) { + if (GIV(argumentCount) > 1) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + resultArray = longAt(GIV(stackPointer)); + if (!((/* isPointers: */ + ((!(resultArray & (tagMask())))) + && (((byteAt((void *)(resultArray + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((lengthOf(resultArray)) >= 2))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + else { + resultArray = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 2); + } + + /* N.B. No pushRemappableOop:/popRemappableOop in Cog because positive64BitIntegerFor: et al use + eeInstantiate... allocators which are guaranteed not to do a GC. */ + valuePointer = (((usqInt)(ioLocalSecondsOffset()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultArray)) + && (!(isForwarded(resultArray)))); + assert(validStorePointerUncheckedArgs(1, resultArray, valuePointer)); + longAtput((void *)((resultArray + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(ioUTCMicroseconds()); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(0, resultArray, valuePointer)); + assert(isNonImmediate(resultArray)); + if (oopisGreaterThanOrEqualTo(resultArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(resultArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(resultArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((resultArray + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),resultArray); +} + + +/* Answer an array with UTC microseconds since the Posix epoch and the + current seconds offset from GMT in the local time zone. An empty two + element array (or any object with two or more slots) may be supplied + as a parameter. + This is a named (not numbered) primitive in the null module (ie the VM) */ + + /* InterpreterPrimitives>>#primitiveUtcWithOffset */ +EXPORT(sqInt) +primitiveUtcWithOffset(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + static usqLong epochDelta = 2177452800000000ULL; + sqInt resultArray; + sqInt valuePointer; + + if (GIV(argumentCount) > 0) { + if (GIV(argumentCount) > 1) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + resultArray = longAt(GIV(stackPointer)); + if (!((/* isPointers: */ + ((!(resultArray & (tagMask())))) + && (((byteAt((void *)(resultArray + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((lengthOf(resultArray)) >= 2))) { + return (GIV(primFailCode) = PrimErrBadArgument); + } + } + else { + resultArray = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 2); + } + + /* N.B. No pushRemappableOop:/popRemappableOop in Cog because positive64BitIntegerFor: et al use + eeInstantiate... allocators which are guaranteed not to do a GC. */ + valuePointer = (((usqInt)(ioLocalSecondsOffset()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultArray)) + && (!(isForwarded(resultArray)))); + assert(validStorePointerUncheckedArgs(1, resultArray, valuePointer)); + longAtput((void *)((resultArray + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor((ioUTCMicrosecondsNow()) - epochDelta); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(0, resultArray, valuePointer)); + assert(isNonImmediate(resultArray)); + if (oopisGreaterThanOrEqualTo(resultArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(resultArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(resultArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((resultArray + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),resultArray); + return 0; +} + + +/* Return a string containing the path name of VM's directory. */ + + /* InterpreterPrimitives>>#primitiveVMPath */ +static void +primitiveVMPath(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt s; + sqInt sz; + + sz = vmPathSize(); + s = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord())))))), sz); + vmPathGetLength(s + BaseHeaderSize, sz); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),s); +} + + +/* Primitive. + 0 args: Answer whether the VM Profiler is running or not. + 1 arg: Copy the sample data into the supplied argument, which must be a + Bitmap of suitable size. Answer the number of samples copied into the + buffer. */ + + /* InterpreterPrimitives>>#primitiveVMProfileSamplesInto */ +static void +primitiveVMProfileSamplesInto(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + long bufferSize; + sqInt numSamples; + sqInt running; + sqInt sampleBuffer; + char *sp; + + bufferSize = 0; + running = 0; + ioNewProfileStatus((&running), (&bufferSize)); + if (!GIV(argumentCount)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer)),/* booleanObjectOf: */ + (running + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return; + } + if (!(GIV(argumentCount) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + sampleBuffer = longAt(GIV(stackPointer)); + if (!(((!(sampleBuffer & (tagMask())))) + && ((isPureBitsNonImm(sampleBuffer)) + && ((numBytesOf(sampleBuffer)) >= (bufferSize * BytesPerWord))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + numSamples = ioNewProfileSamplesInto(pointerForOop(sampleBuffer + BaseHeaderSize)); + + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)numSamples << 3) | 1)); +} + + +/* Signal the given semaphore from within the interpreter. Used to serialize + callbacks. + */ + + /* InterpreterPrimitives>>#signalNoResume: */ +int +signalNoResume(sqInt aSemaphore) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int empty; + + /* begin isEmptyList: */ + assert(!(isForwarded(aSemaphore))); + empty = (longAt((void *)((aSemaphore + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj); + if (!empty) { + putToSleepyieldingIf(removeFirstLinkOfList(aSemaphore), 1); + } + return empty; +} + + +/* Answer a signed value of an integer up to the size of a machine word. + The object is not an immediate and is hoped to be a LargeInteger of size + <= word size. + */ + + /* InterpreterPrimitives>>#signedMachineIntegerValueOfObj: */ +static NoDbgRegParms sqIntptr_t +signedMachineIntegerValueOfObj(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bs; + sqInt ccIndex; + sqInt fmt; + sqInt isClassOfNonImmequalTocompactClassIndexRV; + usqIntptr_t limit; + usqIntptr_t magnitude; + sqInt negative; + usqInt numBytes; + usqInt numSlots; + usqIntptr_t value; + + assert(!(((((oop) & 7) == 1)))); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + isClassOfNonImmequalTocompactClassIndexRV = ClassLargePositiveIntegerCompactIndex == ccIndex; + if (isClassOfNonImmequalTocompactClassIndexRV) { + negative = 0; + } + else { + negative = 1; + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + isClassOfNonImmequalTocompactClassIndexRV = ClassLargeNegativeIntegerCompactIndex == ccIndex; + if (!isClassOfNonImmequalTocompactClassIndexRV) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + } + + /* begin numBytesOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + bs = numBytes - (fmt & 7); + goto l1; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + bs = numBytes; + goto l1; + } + if (fmt >= (firstShortFormat())) { + bs = numBytes - (((fmt & 3) << 1)); + goto l1; + } + + /* fmt >= self firstLongFormat */ + bs = numBytes - (((fmt & 1) << 2)); + /* end numBytesOf: */ +l1: + if (bs > (sizeof(usqIntptr_t))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + if (((sizeof(sqIntptr_t)) == 8) + && (bs > 4)) { + magnitude = SQ_SWAP_8_BYTES_IF_BIGENDIAN((long64At((void *)((oop + BaseHeaderSize))))); + } + else { + magnitude = ((unsigned int) (SQ_SWAP_4_BYTES_IF_BIGENDIAN((long32At((void *)((oop + BaseHeaderSize))))))); + } + limit = ((((usqIntptr_t)1)) << (((sizeof(sqIntptr_t)) * 8) - 1)); + if ((negative + ? magnitude > limit + : magnitude >= limit)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + if (negative) { + value = 0 - magnitude; + } + else { + value = magnitude; + } + return value; +} + + +/* Answer a signed value of an integer up to the size of a machine word. + The object may be either a positive SmallInteger or a LargeInteger of size + <= word size. + */ +/* inline the common case... */ + + /* InterpreterPrimitives>>#signedMachineIntegerValueOf: */ +sqIntptr_t +signedMachineIntegerValueOf(sqInt oop) +{ + if ((((oop) & 7) == 1)) { + return (oop >> 3); + } + return signedMachineIntegerValueOfObj(oop); +} + + +/* Answer the start of the Alien's data or fail if oop is not an Alien. */ + + /* InterpreterPrimitives>>#sizeOfAlienData: */ +usqInt +sizeOfAlienData(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt oopClass; + sqInt tagBits; + + /* begin is:KindOfClass: */ + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while ((oopClass != GIV(nilObj)) + && ((/* isPointers: */ + ((!(oopClass & (tagMask())))) + && (((byteAt((void *)(oopClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOfAny(oopClass)) > InstanceSpecificationIndex))) { + if (oopClass == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord())))))))) { + goto l1; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return 0; +l1: + return SQABS(longAt((void *)(oop + BaseHeaderSize))); +} + + +/* Answer the start of the Alien's data or fail if oop is not an Alien. */ + + /* InterpreterPrimitives>>#startOfAlienData: */ +void * +startOfAlienData(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt oopClass; + sqInt tagBits; + + /* begin is:KindOfClass: */ + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while ((oopClass != GIV(nilObj)) + && ((/* isPointers: */ + ((!(oopClass & (tagMask())))) + && (((byteAt((void *)(oopClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOfAny(oopClass)) > InstanceSpecificationIndex))) { + if (oopClass == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord())))))))) { + goto l1; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return 0; +l1: + return ((void *) (((longAt((void *)(oop + BaseHeaderSize))) > 0 + ? (oop + BaseHeaderSize) + BytesPerOop + : longAt((void *)((oop + BaseHeaderSize) + BytesPerOop))))); +} + + +/* Answer the state of the primitive failure code/success flag. If + primFailCode is non-zero a primitive has failed. If primFailCode + is greater than one then its value indicates the reason for failure. */ +/* In C, non-zero is true, so avoid computation by using not the C version. */ + + /* InterpreterPrimitives>>#successful */ +static int +successful(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return !GIV(primFailCode); +} + + +/* Set the state of the primitive failure code/success flag, iff + successBoolean is false. If primFailCode is non-zero a primitive has + failed. If primFailCode + is greater than one then its value indicates the reason for failure. */ +/* Use returnTypeC: #sqInt because that's the way it is defined in sq.h. + Use no explicit return so that Slang doesn't fail an inlining type-check + when a primitive with return type void uses ^self success: false to exit. */ + + /* InterpreterPrimitives>>#success: */ +sqInt +success(sqInt successBoolean) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!successBoolean) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + return 0; +} + + +/* Answer the appropriate become effect flags for objOop, or 0 if none. + The effect flags determine how much work is done after the become in + following forwarding pointers, + voiding method caches, etc. Flag jitted methods because these will have to + be freed after divorcing + frames and mapping native pcs to bytecode pcs in contexts using them. + See the comment in InterpreterPrimitives>>#primitiveArrayBecome */ + + /* Spur64BitCoMemoryManager>>#becomeEffectFlagsFor: */ +static NoDbgRegParms sqInt +becomeEffectFlagsFor(sqInt objOop) +{ + int hash; + sqInt methodHeader; + + hash = 0; + if (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) { + if ((((hash = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()))) != 0) + && ((classAtIndex(hash)) == objOop)) { + return BecamePointerObjectFlag + BecameActiveClassFlag; + } + return BecamePointerObjectFlag; + } + if (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + methodHeader = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + flagCogMethodForBecome(((CogMethod *) methodHeader)); + return BecameCompiledMethodFlag + BecameJittedCompiledMethodFlag; + } + return BecameCompiledMethodFlag; + } + return 0; +} + + +/* The check against scavengeThreshold in + genInstantiate64BitSignedIntegerValue:* et al compares + freeStart against scavengeThreshold minus the allocation size. Hence the + assert must compensate here. + */ + + /* Spur64BitCoMemoryManager>>#ceScheduleScavenge */ +void +ceScheduleScavenge(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(((GIV(freeStart) + BaseHeaderSize) + (sizeof(double))) >= GIV(scavengeThreshold)); + + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); +} + + /* Spur64BitCoMemoryManager>>#classSmallFloat */ +sqInt +classSmallFloat(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)((smallFloatTag())) << (shiftForWord())))))); +} + + +/* Perform an integrity/leak check using the heapMap. Set a bit at each + object's header. */ +/* Perform an integrity/leak check using the heapMap. Set a bit at each + object's header. + Override to set a bit at each Cog method */ + + /* Spur64BitCoMemoryManager>>#clearLeakMapAndMapAccessibleObjects */ +static void +clearLeakMapAndMapAccessibleObjects(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + clearHeapMap(); + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + heapMapAtWordPut(pointerForOop(obj), 1); + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + addCogMethodsToHeapMap(); +} + + +/* Ensure there are no forwarded literals in the argument. */ + + /* Spur64BitCoMemoryManager>>#ensureNoForwardedLiteralsIn: */ +void +ensureNoForwardedLiteralsIn(sqInt aMethodObj) +{ + followForwardedObjectFieldstoDepth(aMethodObj, 0); +} + + +/* Follow pointers in the object to depth. + Answer if any forwarders were found. + How to avoid cyclic structures?? A temporary mark bit? eem 6/22/2020 no + need since depth is always finite. */ + + /* Spur64BitCoMemoryManager>>#followForwardedObjectFields:toDepth: */ +static NoDbgRegParms sqInt +followForwardedObjectFieldstoDepth(sqInt objOop, sqInt depth) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt fmt; + sqInt found; + sqInt header; + sqInt headerSqInt; + sqInt i; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt referent; + sqInt sp; + + found = 0; + assert((isPointers(objOop)) + || (isOopCompiledMethod(objOop))); + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin numPointerSlotsOf:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + numSlots = CtxtTempFrameStart + contextSize; + goto l2; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + goto l2; + } + if (fmt == (forwardedFormat())) { + numSlots = 1; + goto l2; + } + if (fmt < (firstCompiledMethodFormat())) { + numSlots = 0; + goto l2; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf:format: */ +l2: + + /* It is essential to skip the first field of a method because it may be a + reference to a Cog method in the method zone, not a real object at all. */ + for (i = ((fmt >= (firstCompiledMethodFormat()) + ? 1 + : 0)); i < numSlots; i += 1) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(oop & (tagMask())))) { + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + found = 1; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(i, objOop, oop)); + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(oop & (tagMask())))) + && (oopisLessThan(oop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),oop); + } + if ((depth > 0) + && ((/* hasPointerFields: */ + ((!(oop & (tagMask())))) + && (hasPointerFieldsNonImm(oop))) + && (followForwardedObjectFieldstoDepth(oop, depth - 1)))) { + found = 1; + } + } + } + return found; +} + + /* Spur64BitCoMemoryManager>>#freeStartAddress */ +usqInt +freeStartAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(freeStart)))); +} + + /* Spur64BitCoMemoryManager>>#getScavengeThreshold */ +usqInt +getScavengeThreshold(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(scavengeThreshold); +} + + +/* Answer the header of the argument even though + it may have its header word in a forwarding block + (which shouldn't happen with Spur). */ + + /* Spur64BitCoMemoryManager>>#headerWhileForwardingOf: */ +static NoDbgRegParms sqLong +headerWhileForwardingOf(sqInt aCompiledMethodObjOop) +{ + assert(!(isForwarded(aCompiledMethodObjOop))); + return long64At((void *)(aCompiledMethodObjOop)); +} + + +/* Answer true if the argument contains indexable bytes. See comment in + formatOf: + */ +/* Note: Includes CompiledMethods. */ + + /* Spur64BitCoMemoryManager>>#isBytes: */ +sqInt +isBytes(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())); +} + + +/* A lenient tester of forwarded class indices for inline cache management in + the Cogit. + */ + + /* Spur64BitCoMemoryManager>>#isForwardedClassIndex: */ +sqInt +isForwardedClassIndex(sqInt maybeClassIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt entry; + sqInt fieldIndex; + + if ((((usqInt)maybeClassIndex)) >= (1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift())))) { + return 0; + } + fieldIndex = ((usqInt)(maybeClassIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return 0; + } + fieldIndex = maybeClassIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + entry = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + return (!((longAt((void *)(entry))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))); +} + + /* Spur64BitCoMemoryManager>>#isImmediateClass: */ +int +isImmediateClass(sqInt classObj) +{ + return ((((usqInt)((((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) == (forwardedFormat()); +} + + +/* Answer if obj is young. Require that obj is non-immediate. Override to + filter-out Cog methods + */ + + /* Spur64BitCoMemoryManager>>#isReallyYoungObject: */ +sqInt +isReallyYoungObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(isNonImmediate(objOop)); + return (oopisLessThan(objOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(objOop, GIV(newSpaceStart))); +} + + +/* Answer the method header of a CompiledMethod object. + If the method has been cogged then the header is a pointer to + the CogMethod and the real header will be stored in the CogMethod. */ + + /* Spur64BitCoMemoryManager>>#methodHeaderOf: */ +sqInt +methodHeaderOf(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + + assert(isCompiledMethod(methodObj)); + header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + return ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); +} + + /* Spur64BitCoMemoryManager>>#needGCFlagAddress */ +usqInt +needGCFlagAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(needGCFlag)))); +} + + /* Spur64BitCoMemoryManager>>#printCantBeObject:on: */ +static NoDbgRegParms void +printCantBeObjecton(sqInt oop, FILE *aStream) +{ + char *where; + + fprintf(aStream, + "%p%s\n", + ((void *)oop), + ((where = whereIsMaybeCodeThing(oop)) + ? where + : (oop & 7 /* (allocationUnit - 1) */ + ? " is misaligned" + : whereIs(oop)))); +} + + /* Spur64BitCoMemoryManager>>#specialObjectsOopAddress */ +usqInt +specialObjectsOopAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((usqInt)((&GIV(specialObjectsOop)))); +} + + +/* For the purposes of become: send selector to the cogit with obj1, obj2 + and aBool and answer the result. Undo forwarding for the selector. */ + + /* Spur64BitCoMemoryManager>>#withoutForwardingOn:and:with:sendToCogit: */ +sqInt +withoutForwardingOnandwithsendToCogit(sqInt obj1, sqInt obj2, sqInt aBool, sqInt (*selector)(sqInt,sqInt,sqInt)) +{ + sqInt targetA; + sqInt targetB; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj1)); + targetA = longAt((void *)((obj1 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(targetA & (tagMask())))) + && ((!((longAt((void *)(targetA))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + targetA = longAt((void *)((targetA + BaseHeaderSize) + (0U << (shiftForWord())))); + } + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj2)); + targetB = longAt((void *)((obj2 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(targetB & (tagMask())))) + && ((!((longAt((void *)(targetB))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + targetB = longAt((void *)((targetB + BaseHeaderSize) + (0U << (shiftForWord())))); + } + return selector(targetA, targetB, aBool); +} + + +/* Answer the address immediately following an object. */ + + /* Spur64BitMemoryManager>>#addressAfter: */ +static NoDbgRegParms usqInt +addressAfter(sqInt objOop) +{ + usqInt numSlots; + + numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + if (!numSlots) { + return (objOop + 8 /* allocationUnit */) + BaseHeaderSize; + } + if (numSlots == (numSlotsMask())) { + numSlots = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8; + } + return (objOop + BaseHeaderSize) + (((numSlots << (shiftForWord())))); +} + + +/* Answer the oop of a chunk of space in oldSpace with numSlots slots. Try + and allocate in a segment that already includes pinned objects. The header + of the + result will have been filled-in but not the contents. */ + + /* Spur64BitMemoryManager>>#allocateSlotsForPinningInOldSpace:bytes:format:classIndex: */ +static NoDbgRegParms sqInt +allocateSlotsForPinningInOldSpacebytesformatclassIndex(sqInt numSlots, usqInt totalBytes, sqInt formatField, sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt acceptedChunk; + sqInt acceptedNode; + sqInt child; + usqInt childBytes; + sqInt chunk; + usqInt index; + usqInt initialIndex; + sqInt lilliputian; + sqInt lilliputianSqInt; + sqInt next; + sqInt nextFreeChunk; + sqInt nextSqInt; + sqInt node; + sqInt prev; + + /* begin allocateOldSpaceChunkOfBytes:suchThat: */ + next = 0; + prev = 0; + + /* be optimistic (& don't wait for the write) */ + GIV(totalFreeOldSpace) -= totalBytes; + initialIndex = totalBytes / 8 /* allocationUnit */; + if ((initialIndex < 64 /* numFreeLists */) + && ((1ULL << initialIndex) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << initialIndex)) != 0)) { + if ((node = GIV(freeLists)[initialIndex])) { + prev = 0; + while (node != 0) { + assert(node == (startOfObject(node))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(node)); + next = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + if (((segmentContainingObj(node))->containsPinned)) { + if (prev) { + /* begin setNextFreeChunkOf:withValue:chunkBytes: */ + /* begin isLilliputianSize: */ + assert(totalBytes >= (BaseHeaderSize + (allocationUnit()))); + lilliputianSqInt = totalBytes == (BaseHeaderSize + 8 /* allocationUnit */); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((next == 0) + || (isFreeObject(next))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),next); + if ((next != 0) + && (!lilliputianSqInt)) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + } + } + else { + /* begin unlinkFreeChunk:atIndex:chunkBytes: */ + /* begin isLilliputianSize: */ + assert(totalBytes >= (BaseHeaderSize + (allocationUnit()))); + lilliputian = totalBytes == (BaseHeaderSize + 8 /* allocationUnit */); + + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(node)) == (initialIndex * (allocationUnit()))) + && ((initialIndex > 1) + && ((startOfObject(node)) == node))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[initialIndex] = ((nextSqInt = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))))); + if ((!lilliputian) + && (nextSqInt != 0)) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextSqInt)); + longAtput((void *)((nextSqInt + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + } + chunk = node; + goto l1; + } + prev = node; + node = next; + } + } + else { + GIV(freeListsMask) -= 1ULL << initialIndex; + } + } + + /* first search for free chunks of a multiple of chunkBytes in size */ + index = initialIndex; + while ((((index += initialIndex)) < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << index)) != 0)) { + if ((node = GIV(freeLists)[index])) { + prev = 0; + while (node != 0) { + assert(node == (startOfObject(node))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(node)); + next = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + if (((segmentContainingObj(node))->containsPinned)) { + if (prev) { + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((next == 0) + || (isFreeObject(next))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),next); + if (next) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + } + } + else { + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(node)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(node)) == node))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((nextSqInt = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))))); + if (nextSqInt) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextSqInt)); + longAtput((void *)((nextSqInt + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + } + freeChunkWithBytesat((index * 8 /* allocationUnit */) - totalBytes, (/* startOfObject: */ + ((byteAt((void *)(node + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? node - BaseHeaderSize + : node)) + totalBytes); + chunk = node; + goto l1; + } + prev = node; + node = next; + } + } + else { + GIV(freeListsMask) -= 1ULL << index; + } + } + } + + /* now get desperate and use the first that'll fit. + Note that because the minimum free size is 16 bytes (2 * allocationUnit), to + leave room for the forwarding pointer/next free link, we can only break chunks + that are at least 16 bytes larger, hence start at initialIndex + 2. */ + index = initialIndex + 1; + while ((((index += 1)) < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << index)) != 0)) { + if ((node = GIV(freeLists)[index])) { + prev = 0; + while (node != 0) { + assert(node == (startOfObject(node))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(node)); + next = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + if (((segmentContainingObj(node))->containsPinned)) { + if (prev) { + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((next == 0) + || (isFreeObject(next))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),next); + if (next) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + } + } + else { + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(node)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(node)) == node))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((nextSqInt = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))))); + if (nextSqInt) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextSqInt)); + longAtput((void *)((nextSqInt + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + } + freeChunkWithBytesat((index * 8 /* allocationUnit */) - totalBytes, (/* startOfObject: */ + ((byteAt((void *)(node + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? node - BaseHeaderSize + : node)) + totalBytes); + chunk = node; + goto l1; + } + prev = node; + node = next; + } + } + else { + GIV(freeListsMask) -= 1ULL << index; + } + } + } + } + + /* Large chunk, or no space on small free lists. Search the large chunk list. + Large chunk list organized as a tree, each node of which is a list of chunks + of the same size. Beneath the node are smaller and larger blocks. + When the search ends parent should hold the smallest chunk at least as + large as chunkBytes, or 0 if none. acceptedChunk and acceptedNode save + us from having to back-up when the acceptanceBlock filters-out all nodes + of the right size, but there are nodes of the wrong size it does accept. */ + child = GIV(freeLists)[0]; + node = (acceptedChunk = (acceptedNode = 0)); + while (child != 0) { + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(child)); + childBytes = bytesInBody(child); + if (childBytes == totalBytes) { + node = child; + while (1) { + prev = node; + node = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + if (!(node != 0)) break; + if (((segmentContainingObj(node))->containsPinned)) { + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(node)); + nextFreeChunk = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + } + chunk = /* startOfObject: */ + ((byteAt((void *)(node + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? node - BaseHeaderSize + : node); + goto l1; + } + } + if (((segmentContainingObj(child))->containsPinned)) { + next = longAt((void *)((child + BaseHeaderSize) + (0U << (shiftForWord())))); + if (next) { + inFreeTreeReplacewith(child, next); + } + else { + unlinkSolitaryFreeTreeNode(child); + } + + /* no list; remove the interior node + list; replace node with it */ + chunk = /* startOfObject: */ + ((byteAt((void *)(child + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? child - BaseHeaderSize + : child); + goto l1; + } + } + + /* size match; try to remove from list at node. */ + if (child) { + /* Note that because the minimum free size is 16 bytes (2 * allocationUnit), to + leave room for the forwarding pointer/next free link, we can only break chunks + that are at least 16 bytes larger, hence reject chunks < 2 * allocationUnit larger. */ + if (childBytes <= (totalBytes + 8 /* allocationUnit */)) { + child = longAt((void *)((child + BaseHeaderSize) + (4U << (shiftForWord())))); + } + else { + node = child; + child = longAt((void *)((node + BaseHeaderSize) + (3U << (shiftForWord())))); + if (!acceptedNode) { + acceptedChunk = node; + + /* first search the list. */ + do { + acceptedChunk = longAt((void *)((acceptedChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + if ((acceptedChunk != 0) + && (((segmentContainingObj(acceptedChunk))->containsPinned))) { + acceptedNode = node; + } + } while((acceptedChunk != 0) + && (acceptedNode == 0)); + + /* nothing on the list; will the node do? This prefers + acceptable nodes higher up the tree over acceptable + list elements further down, but we haven't got all day... */ + if ((acceptedNode == 0) + && (((segmentContainingObj(node))->containsPinned))) { + acceptedNode = node; + + /* break out of loop now we have an acceptedNode */ + child = 0; + } + } + } + } + } + if (acceptedNode) { + if (acceptedChunk) { + assert((bytesInBody(acceptedChunk)) >= (totalBytes + (allocationUnit()))); + while (1) { + next = longAt((void *)((acceptedNode + BaseHeaderSize) + (0U << (shiftForWord())))); + if (!(next != acceptedChunk)) break; + acceptedNode = next; + } + nextFreeChunk = longAt((void *)((acceptedChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(acceptedNode)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((acceptedNode + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((acceptedNode == 0) + || (isFreeObject(acceptedNode))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),acceptedNode); + } + freeChunkWithBytesat((bytesInBody(acceptedChunk)) - totalBytes, (/* startOfObject: */ + ((byteAt((void *)(acceptedChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? acceptedChunk - BaseHeaderSize + : acceptedChunk)) + totalBytes); + chunk = /* startOfObject: */ + ((byteAt((void *)(acceptedChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? acceptedChunk - BaseHeaderSize + : acceptedChunk); + goto l1; + } + next = longAt((void *)((acceptedNode + BaseHeaderSize) + (0U << (shiftForWord())))); + if (next) { + inFreeTreeReplacewith(acceptedNode, next); + } + else { + unlinkSolitaryFreeTreeNode(acceptedNode); + } + + /* no list; remove the interior node + list; replace node with it */ + assert((bytesInBody(acceptedNode)) >= (totalBytes + (allocationUnit()))); + freeChunkWithBytesat((bytesInBody(acceptedNode)) - totalBytes, (/* startOfObject: */ + ((byteAt((void *)(acceptedNode + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? acceptedNode - BaseHeaderSize + : acceptedNode)) + totalBytes); + chunk = /* startOfObject: */ + ((byteAt((void *)(acceptedNode + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? acceptedNode - BaseHeaderSize + : acceptedNode); + goto l1; + } + + /* optimism was unfounded */ + GIV(totalFreeOldSpace) += totalBytes; + chunk = null; + /* end allocateOldSpaceChunkOfBytes:suchThat: */ +l1: + if (!chunk) { + chunk = allocateOldSpaceChunkOfBytes(totalBytes); + if (!chunk) { + return null; + } + ((segmentContainingObj(chunk))->containsPinned = 1); + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(chunk),numSlots + ((((usqInt)((numSlotsMask())) << (numSlotsFullShift()))))); + longAtput((void *)(chunk + BaseHeaderSize),(((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex) | (1U << (pinnedBitShift()))); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return chunk + BaseHeaderSize; + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + longAtput((void *)(chunk),(((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex) | (1U << (pinnedBitShift()))); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return chunk; +} + + +/* Answer the given integer with its bytes in the reverse order. */ + + /* Spur64BitMemoryManager>>#byteSwapped: */ +sqInt +byteSwapped(sqInt w) +{ + return SQ_SWAP_8_BYTES(w); +} + + +/* Answer the total number of bytes in an object including header and + possible overflow size header. + */ + + /* Spur64BitMemoryManager>>#bytesInBody: */ +static NoDbgRegParms usqInt +bytesInBody(sqInt objOop) +{ + usqLong header; + usqLong headerNumSlots; + usqInt numSlots; + + header = longAt((void *)(objOop)); + headerNumSlots = (header) >> (numSlotsFullShift()); + numSlots = (headerNumSlots == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : (headerNumSlots + ? headerNumSlots + : 1)); + return ((numSlots << (shiftForWord()))) + ((headerNumSlots == (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)); +} + + +/* Answer the default amount of memory to allocate for the eden space. + The actual value can be set via vmParameterAt: and/or a preference in the + ini file. + The shootout tests seem to plateau at 5 or 6Mb. + + Originally, both the 32-bit and 64-bit versions used the same 4Mb default. + Measuring the simulator on image start-up, the 64-bit system's eden at the + same point in start-up + (the first copyBits) is only 8% larger in bytes because it allocates 26% + fewer objects. + Some 21% of the objects in the 32-bit version's eden are large integers + and floats that + are representable as 64-bit immediates. + + But when running benchmarks such as the computer language shootout's + binary trees, + using the same amount of memory for the 64-bit system causes a significant + slow-down and a lot of compactions. So we now use 4Mb for 32-bits and 7Mb + for 64-bits. */ + + /* Spur64BitMemoryManager>>#defaultEdenBytes */ +static int +defaultEdenBytes(void) +{ + return 0x700000; +} + + +/* Answer the tag used in lookup caches for a receiver. This is the + receiver's classIndex. */ + + /* Spur64BitMemoryManager>>#fetchClassTagOf: */ +sqInt +fetchClassTagOf(sqInt oop) +{ + sqInt tagBits; + + return ((tagBits = oop & (tagMask())) + ? tagBits + : (longAt((void *)(oop))) & (classIndexMask())); +} + + /* Spur64BitMemoryManager>>#floatObjectOf: */ +sqInt +floatObjectOf(double aFloat) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newFloatObj; + usqInt newObj; + usqInt numBytes; + sqInt numSlots; + usqLong rawFloat; + usqLong rot; + + if (isSmallFloatValue(aFloat)) { + /* begin smallFloatObjectOf: */ + assert(isSmallFloatValue(aFloat)); + memcpy((&rawFloat), (&aFloat), sizeof(rawFloat)); + + /* begin smallFloatObjectOfBits: */ + assert(isSmallFloatValueBits(rawFloat)); + rot = (((rawFloat) >> 0x3F) & 1) + ((rawFloat << 1)); + if (rot > 1) { + rot -= (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + assert(rot > 0); + } + + /* a.k.a. ~= +/-0.0 */ + return ((rot << (numTagBits()))) + (smallFloatTag()); + } + numSlots = (sizeof(double)) / BytesPerOop; + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert((numSlots >= 0) + && ((knownClassAtIndex(ClassFloatCompactIndex)) != GIV(nilObj))); + assert((firstLongFormat()) == (instSpecOfClass(knownClassAtIndex(ClassFloatCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + newFloatObj = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((firstLongFormat())) << (formatShift()))))) + ClassFloatCompactIndex); + GIV(freeStart) += numBytes; + newFloatObj = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + storeFloatAtfrom(newFloatObj + BaseHeaderSize, aFloat); + return newFloatObj; +} + + +/* Answer the 64-bit value of the argument as raw bits. */ + + /* Spur64BitMemoryManager>>#floatValueBitsOf: */ +static NoDbgRegParms sqLong +floatValueBitsOf(sqInt floatOop) +{ + usqLong rot; + + assert(isFloatInstance(floatOop)); + if (floatOop & (tagMask())) { + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOop)); + rot = ((((usqInt)floatOop))) >> (numTagBits()); + if (rot > 1) { + rot += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + rot = ((rot << 0x3F)) + (((((usqInt)rot))) >> 1); + return rot; + } + return long64At((void *)((floatOop + BaseHeaderSize))); +} + + +/* Answer the C double precision floating point value of the argument, + or fail if it is not a Float, and answer 0. + Note: May be called by translated primitive code. */ + + /* Spur64BitMemoryManager>>#floatValueOf: */ +double +floatValueOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + double result; + sqInt tagBits; + double value; + + if ((tagBits = oop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + return value; + } + } + else { + if (((longAt((void *)(oop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(oop + BaseHeaderSize, result); + return result; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0.0; +} + + +/* This is a horrible hack for getting to the first object in eden if + pastSpace is almost full. + If there is only one (64-bit) word at the end of pastSpace there is no + room for a full + bridge, but there is room for this hack. */ + + /* Spur64BitMemoryManager>>#hackSlimBridgeTo:at: */ +static NoDbgRegParms void +hackSlimBridgeToat(sqInt objOop, sqInt startAddress) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(oopisLessThan(startAddress, objOop)); + assert(oopisLessThanOrEqualTo(objOop, GIV(oldSpaceStart))); + assert(((startAddress + (allocationUnit())) == objOop) + || (((startAddress + (allocationUnit())) + (allocationUnit())) == objOop)); + longAtput((void *)(startAddress),((startAddress + 8 /* allocationUnit */) == objOop + ? ((sqInt)((usqInt)((numSlotsMask())) << (numSlotsFullShift()))) + : ((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + 1)); +} + + +/* A negative header selects the alternate bytecode set. */ + + /* Spur64BitMemoryManager>>#headerIndicatesAlternateBytecodeSet: */ +int +headerIndicatesAlternateBytecodeSet(sqInt methodHeader) +{ + return (((sqLong) methodHeader)) < 0; +} + + +/* must have room for a header (single or double) plus the next free pointer */ + + /* Spur64BitMemoryManager>>#initFreeChunkWithBytes:at: */ +static NoDbgRegParms sqInt +initFreeChunkWithBytesat(usqLong numBytes, sqInt address) +{ + sqLong numSlots; + + assert(((numBytes % (allocationUnit())) == 0) + && (numBytes >= (BaseHeaderSize + BytesPerOop))); + + /* double header */ + if (numBytes >= (((((usqInt)((numSlotsMask())) << (shiftForWord())))) + BaseHeaderSize)) { + numSlots = ((usqLong)(((numBytes - BaseHeaderSize) - BaseHeaderSize))) >> (shiftForWord()); + longAtput((void *)(address),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(address + 8),((sqInt)((usqInt)((numSlotsMask())) << (numSlotsFullShift())))); + return address + 8; + } + + /* single header */ + numSlots = ((usqLong)((numBytes - BaseHeaderSize))) >> (shiftForWord()); + assert(numSlots < (numSlotsMask())); + longAtput((void *)(address),((sqLong)((usqLong)(numSlots) << (numSlotsFullShift())))); + return address; +} + + +/* Must have room for a double header or a short object with the forwarding + slot (16 bytes either way). + */ + + /* Spur64BitMemoryManager>>#initSegmentBridgeWithBytes:at: */ +static NoDbgRegParms void +initSegmentBridgeWithBytesat(usqLong numBytes, sqInt address) +{ + sqLong numSlots; + + assert(((numBytes % (allocationUnit())) == 0) + && (numBytes >= (BaseHeaderSize + BaseHeaderSize))); + numSlots = ((usqLong)(((numBytes - BaseHeaderSize) - BaseHeaderSize))) >> (shiftForWord()); + if (numSlots) { + longAtput((void *)(address),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(address + BaseHeaderSize),(((((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + (1U << (pinnedBitShift()))) + (1ULL << (markedBitFullShift()))) + ((((usqInt)((sixtyFourBitIndexableFormat())) << (formatShift()))))) + (segmentBridgePun())); + } + else { + longAtput((void *)(address),(((1U << (pinnedBitShift())) + (1ULL << (markedBitFullShift()))) + ((((usqInt)((sixtyFourBitIndexableFormat())) << (formatShift()))))) + (segmentBridgePun())); + } +} + + +/* Allocate an instance of a variable class, excepting CompiledMethod. */ + + /* Spur64BitMemoryManager>>#instantiateClass:indexableSize: */ +sqInt +instantiateClassindexableSize(sqInt classObj, usqInt nElements) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt err; + sqInt fillValue; + int hash; + sqInt instSpec; + sqInt newObj; + usqInt newObjUsqInt; + usqInt numBytes; + usqInt numSlots; + usqInt p; + usqInt toDoLimit; + + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + fillValue = 0; + switch (instSpec) { + case arrayFormat(): + numSlots = nElements; + fillValue = GIV(nilObj); + break; + case indexablePointersFormat(): + case weakArrayFormat(): + numSlots = (classFormat & ((1U << (fixedFieldsFieldWidth())) - 1)) + nElements; + fillValue = GIV(nilObj); + break; + case sixtyFourBitIndexableFormat(): + numSlots = nElements; + break; + case firstLongFormat(): + if ((classIndex == ClassFloatCompactIndex) + && (nElements != 2)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return null; + } + numSlots = (nElements + 1) / 2; + instSpec += nElements & 1; + break; + case firstShortFormat(): + numSlots = (nElements + 3) / 4; + instSpec += (4 - nElements) & 3; + break; + case firstByteFormat(): + numSlots = (nElements + 7) / 8; + instSpec += (8 - nElements) & 7; + break; + default: + if ((nElements != 0) + || (instSpec > 5 /* lastPointerFormat */)) { + return null; + } + numSlots = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + fillValue = GIV(nilObj); + } + + /* non-indexable + Some Squeak images include funky fixed subclasses of abstract variable + superclasses. e.g. DirectoryEntry as a subclass of ArrayedCollection. + The (Threaded)FFIPlugin expects to be able to instantiate ExternalData via + this method. + Hence allow fixed classes to be instantiated here iff nElements = 0. */ + if (!classIndex) { + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + return null; + } + } + if (numSlots > ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (numSlots > (0x10000000000LL)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return null; + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), instSpec, classIndex); + } + else { + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if (((numSlots) >> 56) > 0) { + newObj = null; + goto l1; + } + newObjUsqInt = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObjUsqInt = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, instSpec, classIndex); + goto l1; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObjUsqInt),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObjUsqInt),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObjUsqInt % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newObj = newObjUsqInt; + /* end allocateSlots:format:classIndex: */ +l1:; + } + if (newObj) { + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),fillValue); + } + } + return newObj; +} + + +/* Convert the integer value, assumed to be in SmallInteger range, into a + tagged SmallInteger object. + In C, use a shift and an add to set the tag bit. + In Smalltalk we have to work harder because the simulator works with + strictly positive bit patterns. */ + + /* Spur64BitMemoryManager>>#integerObjectOf: */ +sqInt +integerObjectOf(sqInt value) +{ + return (((((usqInt)value)) << (numTagBits()))) + 1; +} + + +/* Translator produces 'oop >> 3' */ + + /* Spur64BitMemoryManager>>#integerValueOf: */ +sqInt +integerValueOf(sqInt oop) +{ + return (((((usqInt)(oop)) >> 0x3F)) == 1 + ? ((((((-(numTagBits())) < 0) ? (((usqInt)(oop)) >> (-(-(numTagBits())))) : ((((usqInt)(oop) << (-(numTagBits()))))))) & 0x1FFFFFFFFFFFFFFFLL) - 0x1FFFFFFFFFFFFFFFLL) - 1 + : (((-(numTagBits())) < 0) ? (((usqInt)(oop)) >> (-(-(numTagBits())))) : (((sqInt)((usqInt)(oop) << (-(numTagBits()))))))); +} + + /* Spur64BitMemoryManager>>#isFloatInstance: */ +static NoDbgRegParms int +isFloatInstance(sqInt oop) +{ + sqInt tagBits; + + return ((tagBits = oop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(oop))) & (classIndexMask())) == ClassFloatCompactIndex); +} + + /* Spur64BitMemoryManager>>#isImmediateFloat: */ +static NoDbgRegParms int +isImmediateFloat(sqInt oop) +{ + return ((oop & (smallFloatTag())) != 0); +} + + /* Spur64BitMemoryManager>>#isIntegerObject: */ +int +isIntegerObject(sqInt oop) +{ + return ((oop & (smallIntegerTag())) != 0); +} + + +/* Answer if the given value can be represented as a Smalltalk integer value. + In 64-bits we use a 3 bit tag which leaves 61 bits for 2's complement + signed integers. In C, use a shift add and mask to test if the top 4 bits + are all the same. + Since 16rFFFFFFFFFFFFFFFF >> 60 = 16rF the computation intValue >> 60 + 1 + bitAnd: 16rF + maps in-range -ve values to 0 and in-range +ve values to 1. */ + + /* Spur64BitMemoryManager>>#isIntegerValue: */ +int +isIntegerValue(sqInt intValue) +{ + return (((((usqInt)(intValue)) >> 60) + 1) & 15) <= 1; +} + + +/* To have a prev pointer, which follows the next pointer, we need at least + two slots. + */ + + /* Spur64BitMemoryManager>>#isLilliputianSize: */ +static NoDbgRegParms int +isLilliputianSize(sqInt chunkBytes) +{ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + return chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */); +} + + /* Spur64BitMemoryManager>>#isSmallFloatValueBits: */ +static NoDbgRegParms int +isSmallFloatValueBits(usqLong rawFloatBits) +{ + usqInt exponent; + + exponent = ((rawFloatBits) >> (smallFloatMantissaBits())) & 0x7FF; + return (exponent > (smallFloatExponentOffset()) + ? exponent <= (0xFF + (smallFloatExponentOffset())) + : (rawFloatBits & ((1ULL << (smallFloatMantissaBits())) - 1) + ? exponent == (smallFloatExponentOffset()) + : exponent == 0)); +} + + /* Spur64BitMemoryManager>>#isSmallFloatValue: */ +static NoDbgRegParms int +isSmallFloatValue(double aFloat) +{ + usqInt exponent; + usqLong rawFloat; + + memcpy((&rawFloat), (&aFloat), sizeof(rawFloat)); + exponent = ((rawFloat) >> (smallFloatMantissaBits())) & 0x7FF; + return (exponent > (smallFloatExponentOffset()) + ? exponent <= (0xFF + (smallFloatExponentOffset())) + : (rawFloat & ((1ULL << (smallFloatMantissaBits())) - 1) + ? exponent == (smallFloatExponentOffset()) + : exponent == 0)); +} + + +/* Answer if the argument contains only indexable words (no oops). See + comment in formatOf: + */ + + /* Spur64BitMemoryManager>>#isWordsNonImm: */ +static NoDbgRegParms int +isWordsNonImm(sqInt objOop) +{ + return ((((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1))); +} + + +/* Answer the number of indexable units in the given object. + For a CompiledMethod, the size of the method header (in bytes) + should be subtracted from the result of this method. */ + + /* Spur64BitMemoryManager>>#lengthOf:format: */ +static NoDbgRegParms sqInt +lengthOfformat(sqInt objOop, sqInt fmt) +{ + usqInt numSlots; + usqInt numSlotsUsqInt; + + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + +/* If floatOrInt is an integer and we enable mixed arithmetic in primitives, + then convert it to a C double float and return it. + If it is a Float, then load its value and return it. + Otherwise fail -- ie return with primErrorCode non-zero. */ + + /* Spur64BitMemoryManager>>#loadFloatOrIntFrom: */ +static NoDbgRegParms double +loadFloatOrIntFrom(sqInt floatOrIntOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + double result; + sqInt tagBits; + double value; + + if ((tagBits = floatOrIntOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(floatOrIntOop)); + bits = ((((usqInt)floatOrIntOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + return value; + } + if ((numericPrimsMixArithmetic()) + && (tagBits == (smallIntegerTag()))) { + return ((double) ((floatOrIntOop >> 3)) ); + } + } + else { + if (((longAt((void *)(floatOrIntOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(floatOrIntOop + BaseHeaderSize, result); + return result; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0.0; +} + + +/* Answer the maximum number of slots we are willing to attempt to allocate + in an object. + Must fit in 56-bits; c.f. bytesInObject: */ + + /* Spur64BitMemoryManager>>#maxSlotsForAlloc */ +static sqInt +maxSlotsForAlloc(void) +{ + return 0x10000000000LL; +} + + +/* Answer the C double precision floating point value of the argument, + which *must* be something for which self isFloatInstance: answers true. + Note: May be called by translated primitive code. */ + + /* Spur64BitMemoryManager>>#noFailFloatValueOf: */ +double +noFailFloatValueOf(sqInt aFloatOop) +{ + usqLong bits; + double result; + + assert(isFloatInstance(aFloatOop)); + if (aFloatOop & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(aFloatOop)); + bits = ((((usqInt)aFloatOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&result), (&bits), sizeof(result)); + } + else { + fetchFloatAtinto(aFloatOop + BaseHeaderSize, result); + } + return result; +} + + +/* Answer the number of free lists. We use freeListsMask, a bitmap, to avoid + reading empty list heads. This should fit in a machine word to end up in a + register during free chunk allocation. */ + + /* Spur64BitMemoryManager>>#numFreeLists */ +static sqInt +numFreeLists(void) +{ + return 64; +} + + +/* Object parsing. + 1. all objects have at least a word following the header, for a forwarding + pointer. 2. objects with an overflow size have a preceding word with a + saturated numSlots. If the word + following an object doesn't have a saturated numSlots field it must be a + single-header object. + If the word following does have a saturated numSlots it must be the + overflow size word. + + This variation on objectAfter:limit: allows for a single (64-bit) word + bridge which may be needed + to bridge from an almost full pastSpace to eden. It is only used in the + flat enumerators that use + startAddressForBridgedHeapEnumeration and enumerate over pastSpace, eden + and oldSpace + in that order. Note that the order for allObjects, and allInstances + enumerates over oldSpace first. + + This hack is cheap. It increases the size of the objectAfter code, but + saves two extra copies of + the inner loop, since the inner loop now enumerates over all of pastSpace, + eden and oldSpace. + The test for a slim bridge is only performed if applied to an overflow + header, and typically only + 1 in 400 objects have overflow headers in 32-bits, 1 in 500 in 64-bits. + The complication is that + image segment loading evaporates the word array by setting the overflow + slots to 1, and this + is ambiguous with a slimbridge. The resolution is that if the segmentArray + has an overflow header, + and is in new space, then its slot size can be zeroed and its overflow + header changed to a slimbridge. + + At some point we should allow slimbridges (slivers?) throughout object + memory, and use them to + provide object alignment by slimbridges (slivers?) padding up to the + following (aligned) object. + */ + + /* Spur64BitMemoryManager>>#objectAfterMaybeSlimBridge:limit: */ +static NoDbgRegParms sqInt +objectAfterMaybeSlimBridgelimit(sqInt objOop, sqInt limit) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, limit)) { + return limit; + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); +} + + +/* Answer the C double precision floating point value of the argument, a + SmallFloat. See section 61-bit Immediate Floats in the SpurMemoryManager + class comment. + msb lsb + Decode: [8expsubset][52mantissa][1s][3tags] + shift away tags: [ 000 ][8expsubset][52mantissa][1s] + add exponent offset: [ 11 exponent ][52mantissa][1s] + rot sign: [1s][ 11 exponent ][52mantissa] */ + + /* Spur64BitMemoryManager>>#smallFloatValueOf: */ +static NoDbgRegParms double +smallFloatValueOf(sqInt oop) +{ + usqLong bits; + double value; + + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + return value; +} + + +/* Answer the total number of bytes in an object without an overflow header, + including header bytes. + */ + + /* Spur64BitMemoryManager>>#smallObjectBytesForSlots: */ +usqInt +smallObjectBytesForSlots(sqInt numSlots) +{ + return BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); +} + + /* Spur64BitMemoryManager>>#wordIndexableFormat */ +static sqInt +wordIndexableFormat(void) +{ + return sixtyFourBitIndexableFormat(); +} + + /* Spur64BitMemoryManager>>#wordSizeClassIndexPun */ +static sqInt +wordSizeClassIndexPun(void) +{ + return sixtyFourBitLongsClassIndexPun(); +} + + +/* ephemeronCorpse is the corpse of an ephemeron that was copied and + forwarded. Later on its surviving copy must be scanned to nil weak + references. Thread the corpse onto the weakList. Later, the weakList can + be followed, and + the forwarding pointer followed to locate the survivor. */ +/* Should be too infrequent to lower icache density of copyAndForward: */ + + /* SpurGenerationScavenger>>#addToEphemeronList: */ +static NoDbgRegParms NeverInline void +addToEphemeronList(sqInt ephemeronCorpse) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ephemeronListOffset; + + assert(isYetToBeScavenged(keyOfEphemeron(followForwarded(ephemeronCorpse)))); + ephemeronListOffset = (!(GIV(ephemeronList)) + ? 0 + : GIV(ephemeronList)); + + /* begin setCorpseOffsetOf:to: */ + assert(isYoung(ephemeronCorpse)); + assert(isForwarded(ephemeronCorpse)); + long64Atput((void *)(ephemeronCorpse),headerForSlotshashformatclassIndex(byteAt((void *)(ephemeronCorpse + (numSlotsFieldByteOffset()))), ((usqInt)(ephemeronListOffset)) >> 8, ephemeronListOffset & 0xFF, isForwardedObjectClassIndexPun())); + GIV(ephemeronList) = (((usqInt)((ephemeronCorpse - GIV(newSpaceStart)))) >> 3 /* shiftForAllocationUnit */) + 1; + assert((firstCorpse(GIV(ephemeronList))) == ephemeronCorpse); +} + + +/* weakCorpse is the corpse of a weak array that was copied and forwarded. + Later on its surviving copy must be scanned to nil weak references. + Thread the corpse onto the weakList. Later, the weakList can be followed, + and the forwarding pointer followed to locate the survivor. */ +/* Should be too infrequent to lower icache density of copyAndForward: */ + + /* SpurGenerationScavenger>>#addToWeakList: */ +static NoDbgRegParms NeverInline void +addToWeakList(sqInt weakCorpse) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt weakListOffset; + + weakListOffset = (!(GIV(weakList)) + ? 0 + : GIV(weakList)); + + /* begin setCorpseOffsetOf:to: */ + assert(isYoung(weakCorpse)); + assert(isForwarded(weakCorpse)); + long64Atput((void *)(weakCorpse),headerForSlotshashformatclassIndex(byteAt((void *)(weakCorpse + (numSlotsFieldByteOffset()))), ((usqInt)(weakListOffset)) >> 8, weakListOffset & 0xFF, isForwardedObjectClassIndexPun())); + GIV(weakList) = (((usqInt)((weakCorpse - GIV(newSpaceStart)))) >> 3 /* shiftForAllocationUnit */) + 1; + assert((firstCorpse(GIV(weakList))) == weakCorpse); +} + + /* SpurGenerationScavenger>>#allNewSpaceObjectsHaveZeroRTRefCount */ +static sqInt +allNewSpaceObjectsHaveZeroRTRefCount(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + /* begin allNewSpaceObjectsDo: */ + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l2; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOopSqInt, GIV(freeStart))) { + assert(isBridgeOrEnumerableObjectNoAssert(objOopSqInt)); + if ((((usqInt)((byteAt((void *)(objOopSqInt + (formatFieldByteOffset())))))) >> (rememberedBitByteShift())) > 0) { + return 0; + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOopSqInt = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + /* end allNewSpaceEntitiesDo: */ +l2: + return 1; +} + + /* SpurGenerationScavenger>>#allWeakSurvivorsOnWeakList */ +static sqInt +allWeakSurvivorsOnWeakList(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + usqInt corpse; + sqInt followingWord; + usqInt followingWordAddress; + sqInt format; + sqInt limit; + sqInt listOffset; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + + /* begin allFutureSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + address = (GIV(futureSpace).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + limit = GIV(futureSurvivorStart); + while (oopisLessThan(objOop, limit)) { + /* begin isWeakNonImm: */ + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (format == (weakArrayFormat())) { + /* begin is:onWeaklingList: */ + corpse = ((((usqInt)((GIV(weakList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + while (corpse) { + if (objOop == (followForwarded(corpse))) { + goto l1; + } + + /* begin nextCorpseOrNil: */ + assert(isYoung(corpse)); + listOffset = ((((usqInt)(((long32At((void *)(corpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(corpse + (formatFieldByteOffset())))); + corpse = (listOffset + ? ((((usqInt)((listOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0); + } + return 0; + return 0; +l1:; + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, limit)) { + objOop = limit; + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l2:; + } + return 1; +} + + +/* Some time in every scavenger's life there may come a time when someone + writes code that stresses + the remembered table. One might conclude that if the remembered table is + full, then the right thing + to do is simply to tenure everything, emptying the remembered table. But + in some circumstances this + can be counter-productive, and result in the same situation arising soon + after tenuring everything. + Instead, we can try and selectively prune the remembered table, tenuring + only those objects that + are referenced by many objects in the remembered table. That's what this + algorithm does. It + reference counts young objects referenced from the remembered set, and + then sets a threshold + used to tenure objects oft referenced from the remembered set, thereby + allowing the remembered + set to shrink, while not tenuring everything. + + Once in a network monitoring application in a galaxy not dissimilar from + the one this code inhabits, + a tree of nodes referring to large integers was in precisely this + situation. The nodes were old, and + the integers were in new space. Some of the nodes referred to shared + numbers, some their own + unique numbers. The numbers were updated frequently. Were new space simply + tenured when the + remembered table was full, the remembered table would soon fill up as new + numbers were computed. + Only by selectively pruning the remembered table of nodes that shared + data, was a balance achieved + whereby the remembered table population was kept small, and tenuring rates + were low. */ + + /* SpurGenerationScavenger>>#computeRefCountToShrinkRT */ +static NeverInline void +computeRefCountToShrinkRT(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt count; + sqInt elephant; + sqInt entirePopulation; + int i; + sqInt iSqInt; + sqInt j; + long population[MaxRTRefCount + 1]; + sqInt refCount; + sqInt referent; + sqInt referentSqInt; + sqInt toDoLimit; + + memset(population, 0, (sizeof(long)) * (MaxRTRefCount + 1)); + assert(allNewSpaceObjectsHaveZeroRTRefCount()); + + /* begin referenceCountRememberedReferents: */ + for (iSqInt = 0; iSqInt < GIV(rememberedSetSize); iSqInt += 1) { + elephant = GIV(rememberedSet)[iSqInt]; + if ((!((longAt((void *)(elephant))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(elephant)); + referentSqInt = longAt((void *)((elephant + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + elephant = referentSqInt; + if (((elephant & (tagMask())) != 0)) { + elephant = GIV(nilObj); + } + + /* take care if elephant forwarded to an immediate */ + GIV(rememberedSet)[iSqInt] = elephant; + } + toDoLimit = (numPointerSlotsOf(elephant)) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + referent = longAt((void *)((elephant + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (/* isReallyYoung: */ + ((!(referent & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)), + (oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))))) { + refCount = ((usqInt)((byteAt((void *)(referent + (formatFieldByteOffset())))))) >> (rememberedBitByteShift()); + if (refCount < MaxRTRefCount) { + if (refCount > 0) { + population[refCount] = ((population[refCount]) - 1); + } + refCount += 1; + + /* begin rtRefCountOf:put: */ + assert(isYoungObject(referent)); + byteAtput((void *)(referent + (formatFieldByteOffset())),((byteAt((void *)(referent + (formatFieldByteOffset())))) & (formatMask())) + ((((usqInt)(refCount) << (rememberedBitByteShift()))))); + population[refCount] = ((population[refCount]) + 1); + } + } + } + } + + /* begin setRefCountToShrinkRT: */ + assert((population[0]) == 0); + entirePopulation = 0; + for (j = 1; j <= MaxRTRefCount; j += 1) { + entirePopulation += population[j]; + } + count = 0; + i = MaxRTRefCount + 1; + while ((count < (entirePopulation / 2)) + && (((i -= 1)) >= 0)) { + count += population[i]; + } + GIV(refCountToShrinkRT) = ((i < 0) ? 0 : i); +} + + +/* A special version of copyAndForward: for objects in the mournQueue. If + we're in the good times tenuring regime then copy to futureSpace, + otherwise tenure. + Also, don't repeat any of the ephemeron processing. */ + + /* SpurGenerationScavenger>>#copyAndForwardMourner: */ +static NoDbgRegParms sqInt +copyAndForwardMourner(sqInt mourner) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytesInObj; + sqInt format; + sqInt newLocation; + sqInt newStart; + sqInt objOop; + usqInt startOfSurvivor; + int tenure; + + assert((isInEden(mourner)) + || (isInPastSpace(mourner))); + + /* cog methods should be excluded. */ + bytesInObj = bytesInBody(mourner); + format = (byteAt((void *)(mourner + (formatFieldByteOffset())))) & (formatMask()); + + /* Allow Slang to inline. */ + tenure = !((GIV(tenureCriterion) == TenureByAge) + && (GIV(tenureThreshold) == 0)); + if (tenure + || ((GIV(futureSurvivorStart) + bytesInObj) > ((GIV(futureSpace).limit)))) { + newLocation = copyToOldSpacebytesformat(mourner, bytesInObj, format); + } + else { + /* begin copyToFutureSpace:bytes: */ + /* we hope writes are cheap... */ + GIV(statSurvivorCount) += 1; + assert((GIV(futureSurvivorStart) + bytesInObj) <= ((GIV(futureSpace).limit))); + startOfSurvivor = /* startOfObject: */ + ((byteAt((void *)(mourner + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? mourner - BaseHeaderSize + : mourner); + newStart = GIV(futureSurvivorStart); + GIV(futureSurvivorStart) += bytesInObj; + memcpy(((void *)newStart), ((void *)startOfSurvivor), bytesInObj); + if (GIV(tenureCriterion) == TenureToShrinkRT) { + objOop = newStart + (mourner - startOfSurvivor); + + /* begin rtRefCountOf:put: */ + assert(isYoungObject(objOop)); + byteAtput((void *)(objOop + (formatFieldByteOffset())),((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) + (0U << (rememberedBitByteShift()))); + } + newLocation = newStart + (mourner - startOfSurvivor); + } + + /* begin forwardSurvivor:to: */ + assert(isInNewSpace(mourner)); + assert((isInFutureSpace(newLocation)) + || (isInOldSpace(newLocation))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(mourner)) + && (!(isForwarded(mourner)))); + assert(validStorePointerUncheckedArgs(0, mourner, newLocation)); + longAtput((void *)((mourner + BaseHeaderSize) + (0U << (shiftForWord()))),newLocation); + + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(mourner),((longAt((void *)(mourner))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); + return newLocation; +} + + +/* copyAndForward: survivor copies a survivor object either to + futureSurvivorSpace or, if it is to be promoted, to oldSpace. + It leaves a forwarding pointer behind. If the object is weak + then corpse is threaded onto the weakList for later treatment. */ + + /* SpurGenerationScavenger>>#copyAndForward: */ +static NoDbgRegParms sqInt +copyAndForward(sqInt survivor) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytesInObj; + sqInt format; + sqInt newLocation; + sqInt newStart; + sqInt objOop; + usqInt startOfSurvivor; + + assert((isInEden(survivor)) + || (isInPastSpace(survivor))); + + /* cog methods should be excluded. */ + bytesInObj = bytesInBody(survivor); + format = (byteAt((void *)(survivor + (formatFieldByteOffset())))) & (formatMask()); + if (((GIV(futureSurvivorStart) + bytesInObj) > ((GIV(futureSpace).limit))) + || (/* shouldBeTenured: */ + (GIV(tenureCriterion) == TenureByAge + ? oopisLessThan(survivor, GIV(tenureThreshold)) + : (GIV(tenureCriterion) == TenureToShrinkRT + ? (((usqInt)((byteAt((void *)(survivor + (formatFieldByteOffset())))))) >> (rememberedBitByteShift())) >= GIV(refCountToShrinkRT) + : (GIV(tenureCriterion) == TenureByClass + ? ((longAt((void *)(survivor))) & (classIndexMask())) == GIV(tenuringClassIndex) + : 0))))) { + newLocation = copyToOldSpacebytesformat(survivor, bytesInObj, format); + } + else { + /* begin copyToFutureSpace:bytes: */ + /* we hope writes are cheap... */ + GIV(statSurvivorCount) += 1; + assert((GIV(futureSurvivorStart) + bytesInObj) <= ((GIV(futureSpace).limit))); + startOfSurvivor = /* startOfObject: */ + ((byteAt((void *)(survivor + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? survivor - BaseHeaderSize + : survivor); + newStart = GIV(futureSurvivorStart); + GIV(futureSurvivorStart) += bytesInObj; + memcpy(((void *)newStart), ((void *)startOfSurvivor), bytesInObj); + if (GIV(tenureCriterion) == TenureToShrinkRT) { + objOop = newStart + (survivor - startOfSurvivor); + + /* begin rtRefCountOf:put: */ + assert(isYoungObject(objOop)); + byteAtput((void *)(objOop + (formatFieldByteOffset())),((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) + (0U << (rememberedBitByteShift()))); + } + newLocation = newStart + (survivor - startOfSurvivor); + } + + /* begin forwardSurvivor:to: */ + assert(isInNewSpace(survivor)); + assert((isInFutureSpace(newLocation)) + || (isInOldSpace(newLocation))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(survivor)) + && (!(isForwarded(survivor)))); + assert(validStorePointerUncheckedArgs(0, survivor, newLocation)); + longAtput((void *)((survivor + BaseHeaderSize) + (0U << (shiftForWord()))),newLocation); + + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(survivor),((longAt((void *)(survivor))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); + + /* if weak or ephemeron add to the relevant list for subsequent scanning. */ + if (((format >= (weakArrayFormat())) && (format <= (ephemeronFormat())))) { + if (format == (weakArrayFormat())) { + addToWeakList(survivor); + } + else { + assert(isEphemeronFormat(format)); + if (isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(newLocation)) + && (isObjEphemeron(newLocation))), + /* fetchPointer:ofObject: */ + longAt((void *)((newLocation + BaseHeaderSize) + (0U << (shiftForWord()))))))) { + addToEphemeronList(survivor); + } + } + } + return newLocation; +} + + +/* Copy survivor to oldSpace. Answer the new oop of the object. */ +/* Should be too infrequent to lower icache density of copyAndForward: */ + + /* SpurGenerationScavenger>>#copyToOldSpace:bytes:format: */ +static NoDbgRegParms NeverInline sqInt +copyToOldSpacebytesformat(sqInt survivor, sqInt bytesInObject, sqInt formatOfSurvivor) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt field; + sqInt newOop; + sqInt newStart; + sqInt nTenures; + sqInt p; + usqInt startOfSurvivor; + sqInt toDoLimit; + + assert((formatOfSurvivor == (formatOf(survivor))) + && (((!(isMarked(survivor))) + || (GIV(tenureCriterion) == MarkOnTenure)) + && ((GIV(tenureCriterion) == TenureToShrinkRT) + || ((!(isPinned(survivor))) + && (!(isRemembered(survivor))))))); + nTenures = GIV(statTenures); + startOfSurvivor = /* startOfObject: */ + ((byteAt((void *)(survivor + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? survivor - BaseHeaderSize + : survivor); + newStart = allocateOldSpaceChunkOfBytes(bytesInObject); + if (!newStart) { + growOldSpaceByAtLeast(0); + newStart = allocateOldSpaceChunkOfBytes(bytesInObject); + if (!newStart) { + error("out of memory"); + } + } + + /* manager checkFreeSpace. */ + memcpy(((void *)newStart), ((void *)startOfSurvivor), bytesInObject); + newOop = newStart + (survivor - startOfSurvivor); + if (GIV(tenureCriterion) >= (((TenureToShrinkRT < MarkOnTenure) ? TenureToShrinkRT : MarkOnTenure))) { + if (GIV(tenureCriterion) == TenureToShrinkRT) { + /* begin rtRefCountOf:put: */ + assert(isYoungObject(newOop)); + byteAtput((void *)(newOop + (formatFieldByteOffset())),((byteAt((void *)(newOop + (formatFieldByteOffset())))) & (formatMask())) + (0U << (rememberedBitByteShift()))); + } + if (GIV(tenureCriterion) == MarkOnTenure) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(newOop))); + byteAtput((void *)(newOop + (markBitsByteOffset())),(byteAt((void *)(newOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + } + } + GIV(statTenures) = nTenures + 1; + if (/* isAnyPointerFormat: */ + (formatOfSurvivor <= 5 /* lastPointerFormat */) + || (formatOfSurvivor >= (firstCompiledMethodFormat()))) { + /* A very quick and dirty scan to find young referents. If we misidentify bytes + in a CompiledMethod as young we don't care; it's unlikely, and a subsequent + scan of the rt will filter the object out. But it's good to filter here because + otherwise an attempt to shrink the RT may simply fill it up with new objects, + and here the data is likely in the cache. */ + toDoLimit = (bytesInObject - (survivor - startOfSurvivor)) - BytesPerWord; + for (p = BaseHeaderSize; p <= toDoLimit; p += BytesPerWord) { + field = longAt((void *)(survivor + p)); + if (/* isReallyYoung: */ + ((!(field & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(field)), + (oopisLessThan(field, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(field, GIV(newSpaceStart)))))) { + remember(newOop); + return newOop; + } + } + } + return newOop; +} + + +/* Use the identityHash and format-containing byte to construct a 30 bit + offset through non-future newSpace and use this to implement lists for + weak array + and ephemeron processing. 30 bits of 8 byte allocationUnits units is 2 ^ + 33 bytes, or 8Gb, big enough for newSpace for a good few years yet. */ +/* debugging */ + + /* SpurGenerationScavenger>>#corpseForCorpseOffset: */ +usqInt +corpseForCorpseOffset(sqInt corpseOffset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((((usqInt)((corpseOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); +} + + /* SpurGenerationScavenger>>#firstCorpse: */ +static NoDbgRegParms usqInt +firstCorpse(sqInt headOfCorpseList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((((usqInt)((headOfCorpseList - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); +} + + /* SpurGenerationScavenger>>#growRememberedSet */ +static void +growRememberedSet(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt *base; + sqInt fudge; + sqInt i; + sqInt newObj; + sqInt nSlots; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt obj; + usqInt p; + usqInt toDoLimit; + + + /* Don't ruin locality in remember: */ + obj = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))); + + /* begin numSlotsOf: */ + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + assert(numSlots >= 0x400); + nSlots = numSlots * 2; + + /* begin allocatePinnedSlots: */ + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(nSlots, /* objectBytesForSlots: */ + (nSlots + ? ((((usqInt)(nSlots) << (shiftForWord())))) + ((nSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (newObj) { + assert(isPinned(newObj)); + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + if (!newObj) { + nSlots = numSlots + 0x400; + + /* begin allocatePinnedSlots: */ + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(nSlots, /* objectBytesForSlots: */ + (nSlots + ? ((((usqInt)(nSlots) << (shiftForWord())))) + ((nSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (newObj) { + assert(isPinned(newObj)); + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + if (!newObj) { + if (!(growOldSpaceByAtLeast((numSlots + 0x400) * BytesPerOop))) { + error("could not grow remembered set"); + } + nSlots = numSlots + 0x400; + + /* begin allocatePinnedSlots: */ + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(nSlots, /* objectBytesForSlots: */ + (nSlots + ? ((((usqInt)(nSlots) << (shiftForWord())))) + ((nSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (newObj) { + assert(isPinned(newObj)); + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + } + } + + /* begin rememberedSetObj: */ + assert(isOldObject(newObj)); + assert((isNonImmediate(GIV(hiddenRootsObj))) + && (!(isForwarded(GIV(hiddenRootsObj))))); + assert(validStorePointerUncheckedArgs(RememberedSetRootIndex, GIV(hiddenRootsObj), newObj)); + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord()))))),newObj); + base = firstIndexableField(newObj); + for (i = 0; i < GIV(rememberedSetSize); i += 1) { + base[i] = (GIV(rememberedSet)[i]); + } + + /* if growing in the middle of a GC, need to preserve marked status. */ + if ((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(newObj))); + byteAtput((void *)(newObj + (markBitsByteOffset())),(byteAt((void *)(newObj + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(obj))); + byteAtput((void *)(obj + (markBitsByteOffset())),(byteAt((void *)(obj + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + freeObject(obj); + GIV(rememberedSet) = base; + + /* begin numSlotsOf: */ + assert((classIndexOf(newObj)) > (isForwardedObjectClassIndexPun())); + GIV(rememberedSetLimit) = (((numSlotsUsqInt = byteAt((void *)(newObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(newObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + + /* begin setRememberedSetRedZone */ + fudge = ((((GIV(eden).limit)) - ((GIV(eden).start))) / BytesPerWord) / 0x400; + GIV(rememberedSetRedZone) = ((((GIV(rememberedSetLimit) * 3) / 4) < fudge) ? fudge : ((GIV(rememberedSetLimit) * 3) / 4)); +} + + /* SpurGenerationScavenger>>#isInRememberedSet: */ +static NoDbgRegParms sqInt +isInRememberedSet(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + for (i = 0; i < GIV(rememberedSetSize); i += 1) { + if ((GIV(rememberedSet)[i]) == objOop) { + return 1; + } + } + return 0; +} + + +/* Answer whether the oop has survived a scavenge. This version is + for processing weak survivors and must cope with the scavenge in + freeUnmarkedObjectsAndSortAndCoalesceFreeSpaceForPigCompact. */ + + /* SpurGenerationScavenger>>#isMaybeOldScavengeSurvivor: */ +static NoDbgRegParms sqInt +isMaybeOldScavengeSurvivor(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt target; + + if (((oop & (tagMask())) != 0)) { + return 1; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + target = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(target & (tagMask())))) + && ((!((longAt((void *)(target))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + target = longAt((void *)((target + BaseHeaderSize) + (0U << (shiftForWord())))); + } + if (((oop & (tagMask())) != 0)) { + return 1; + } + } + else { + target = oop; + } + return (oopisGreaterThanOrEqualTo(target, GIV(oldSpaceStart)) + ? (GIV(tenureCriterion) != MarkOnTenure) + || (((byteAt((void *)(target + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + : oopisGreaterThanOrEqualToandLessThan(target, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))); +} + + +/* Answer whether the oop has survived a scavenge. This is equivalent to + | target | + (manager isImmediate: oop) ifTrue: + [^true]. + target := (manager isForwarded: oop) + ifTrue: [manager followForwarded: oop] + ifFalse: [oop]. + ^((manager isInEden: target) + or: [(manager isInPastSpace: target)]) not */ + + /* SpurGenerationScavenger>>#isScavengeSurvivor: */ +static NoDbgRegParms sqInt +isScavengeSurvivor(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt target; + + if (((oop & (tagMask())) != 0)) { + return 1; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + target = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(target & (tagMask())))) + && ((!((longAt((void *)(target))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + target = longAt((void *)((target + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + else { + target = oop; + } + return (!(/* isReallyYoung: */ + ((!(target & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(target)), + (oopisLessThan(target, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(target, GIV(newSpaceStart))))))) + || (oopisGreaterThanOrEqualToandLessThan(target, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))); +} + + +/* Answer whether the oop is in eden or pastSpace and yet-to-be scavenged. */ + + /* SpurGenerationScavenger>>#isYetToBeScavenged: */ +static NoDbgRegParms sqInt +isYetToBeScavenged(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt target; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + target = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(target & (tagMask())))) + && ((!((longAt((void *)(target))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + target = longAt((void *)((target + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + else { + target = oop; + } + return (/* isReallyYoung: */ + ((!(target & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(target)), + (oopisLessThan(target, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(target, GIV(newSpaceStart)))))) + && (!(oopisGreaterThanOrEqualToandLessThan(target, ((GIV(futureSpace)).start), GIV(futureSurvivorStart)))); +} + + /* SpurGenerationScavenger>>#newSpaceCapacity */ +static usqInt +newSpaceCapacity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((GIV(eden).limit)) - (((((GIV(futureSpace).start)) < ((GIV(pastSpace).start))) ? ((GIV(futureSpace).start)) : ((GIV(pastSpace).start)))); +} + + +/* corpse is the corpse of a weak array that has been added to the weakList. + Answer the next object on the list, or nil if none. */ + + /* SpurGenerationScavenger>>#nextCorpseOrNil: */ +static NoDbgRegParms usqInt +nextCorpseOrNil(sqInt corpse) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt listOffset; + + assert(isYoung(corpse)); + listOffset = ((((usqInt)(((long32At((void *)(corpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(corpse + (formatFieldByteOffset())))); + return (listOffset + ? ((((usqInt)((listOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0); +} + + /* SpurGenerationScavenger>>#openScavengeLog */ +void +openScavengeLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(scavengeLog) = fopen("scavenge.log", "a+"); +} + + +/* Print the objects on the ephemeronList, indicating active or inactive. */ + + /* SpurGenerationScavenger>>#printEphemeronList */ +void +printEphemeronList(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt corpse; + sqInt ephemeron; + + corpse = ((((usqInt)((GIV(ephemeronList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + if (!corpse) { + print("empty"); + cr(); + return; + } + while (corpse) { + ephemeron = followForwarded(corpse); + fprintf(GIV(transcript), + "%x -> %p %s\n", + ((int) corpse), + ((void *)ephemeron), + (isYetToBeScavenged(keyOfMaybeFiredEphemeron(ephemeron)) + ? "active" + : "inactive")); + corpse = nextCorpseOrNil(corpse); + } +} + + +/* Print the objects in the remembered set. */ +/* useful for debugging */ + + /* SpurGenerationScavenger>>#printRememberedSet */ +void +printRememberedSet(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + for (i = 0; i < GIV(rememberedSetSize); i += 1) { + printNum(i); + + /* begin space */ + printChar(' '); + shortPrintOop(GIV(rememberedSet)[i]); + } +} + + +/* There are ephemerons to be scavenged, and maybe some to be fired. We can't + fire any + ephemerons until we have reached the fixed point of scavenging all + inactive ephemerons, + and removing them from the remembered set and/or the list. Once at that + fixed point the + rememberedTable has only unfired ephemerons in its numRememberedEphemerons + partition, and the ephemeronList is either empty or contains only firable + ephemerons. At this fixed point + fire the remaining ephemerons and then scavenge the keys of the ephemerons + in the + remembered set and the contents of the ephemeronList. Answer if any were + fired. + Since more that one ephemeron can refer to the same key, and firability + for a key is + determined by that key not (yet) having survived the scavenge, we must + first fire + ephemerons before scavenging their keys, since the first ephemeron that + scavenges its key will make all other ephemerons on that key appear to be + unfirable. + Read the class comment for a more in-depth description of the algorithm. */ + + /* SpurGenerationScavenger>>#processEphemerons */ +static sqInt +processEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt corpseOffset; + sqInt ephemeron; + usqInt ephemeronCorpse; + sqInt firedIRS; + sqInt firedOEL; + sqInt foundNewReferent; + sqInt i; + sqInt key; + sqInt listOffset; + usqInt numSlots; + sqInt offsetToNextCorpse; + sqInt referent; + sqInt referentSqInt; + sqInt scavengedUnfirable; + sqInt toDoLimit; + + scavengedUnfirable = 0; + if (scavengeInactiveEphemeronsInRememberedSet()) { + scavengedUnfirable = 1; + } + assert(validRememberedSetPartitionPostSIEIRS()); + if ((scavengeInactiveEphemeronsOnEphemeronList()) + || (scavengedUnfirable)) { + return 0; + } + assert(wholeEphemeronListIsFirable()); + + /* begin fireEphemeronsInRememberedSet */ + assert(!((unfiredEphemeronsAtEndOfRememberedSet()))); + if (!GIV(numRememberedEphemerons)) { + firedIRS = 0; + goto l1; + } + i = 0; + while (i < GIV(numRememberedEphemerons)) { + ephemeron = GIV(rememberedSet)[i]; + assert(isEphemeron(ephemeron)); + + /* begin keyOfEphemeron: */ + assert((isNonImmediate(ephemeron)) + && (isObjEphemeron(ephemeron))); + key = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isYetToBeScavenged(key)); + fireEphemeron(ephemeron); + i += 1; + } + + /* If any ephemerons were fired then processEphemerons is expected to + scavenge them in scavengeUnfiredEphemeronsInRememberedSet */ + firedIRS = 1; + /* end fireEphemeronsInRememberedSet */ +l1: + + /* begin fireEphemeronsOnEphemeronList */ + if (!GIV(ephemeronList)) { + firedOEL = 0; + goto l2; + } + ephemeronCorpse = ((((usqInt)((GIV(ephemeronList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + while (ephemeronCorpse) { + assert((isYoung(ephemeronCorpse)) + && (isForwarded(ephemeronCorpse))); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(ephemeronCorpse)); + ephemeron = longAt((void *)((ephemeronCorpse + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(ephemeron & (tagMask())))) + && ((!((longAt((void *)(ephemeron))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ephemeron = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + } + + /* begin keyOfMaybeFiredEphemeron: */ + assert(isMaybeFiredEphemeron(ephemeron)); + key = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isYetToBeScavenged(key)); + fireEphemeron(ephemeron); + + /* begin nextCorpseOrNil: */ + assert(isYoung(ephemeronCorpse)); + listOffset = ((((usqInt)(((long32At((void *)(ephemeronCorpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(ephemeronCorpse + (formatFieldByteOffset())))); + ephemeronCorpse = (listOffset + ? ((((usqInt)((listOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0); + } + firedOEL = 1; + /* end fireEphemeronsOnEphemeronList */ +l2: + if (firedIRS + || (GIV(numRememberedEphemerons) > 0)) { + /* begin scavengeEphemeronsInRememberedSet */ + assert(!((unfiredEphemeronsAtEndOfRememberedSet()))); + i = 0; + while (i < GIV(numRememberedEphemerons)) { + ephemeron = GIV(rememberedSet)[i]; + assert((isEphemeron(ephemeron)) + || (isonObjStack(ephemeron, GIV(mournQueue)))); + if (scavengeReferentsOfPointers(ephemeron)) { + i += 1; + } + else { + setIsRememberedOfto(ephemeron, 0); + + /* remove from set by overwriting with next-to-be scanned */ + GIV(numRememberedEphemerons) -= 1; + GIV(previousRememberedSetSize) -= 1; + GIV(rememberedSetSize) -= 1; + + /* First overwrite with last firable ephemeron (could be a noop if this is the last one). + Then overwrite last firable entry with next unscanned rememberedSet entry (could also be a noop). + Then overwrite next unscanned entry with last unscanned rememberedSet entry (could also be a noop). */ + GIV(rememberedSet)[i] = (GIV(rememberedSet)[GIV(numRememberedEphemerons)]); + GIV(rememberedSet)[GIV(numRememberedEphemerons)] = (GIV(rememberedSet)[GIV(previousRememberedSetSize)]); + GIV(rememberedSet)[GIV(previousRememberedSetSize)] = (GIV(rememberedSet)[GIV(rememberedSetSize)]); + } + } + + /* no more firable ephemerons in this cycle. + scavengeRememberedSetStartingAt: may find new ones. */ + GIV(numRememberedEphemerons) = 0; + } + + /* begin scavengeEphemeronsOnEphemeronList */ + if (!((corpseOffset = GIV(ephemeronList)))) { + goto l3; + } + GIV(ephemeronList) = null; + while (corpseOffset != 0) { + ephemeronCorpse = ((((usqInt)((corpseOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + assert(isForwarded(ephemeronCorpse)); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(ephemeronCorpse)); + ephemeron = longAt((void *)((ephemeronCorpse + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(ephemeron & (tagMask())))) + && ((!((longAt((void *)(ephemeron))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ephemeron = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + } + offsetToNextCorpse = ((((usqInt)(((long32At((void *)(ephemeronCorpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(ephemeronCorpse + (formatFieldByteOffset())))); + + /* begin scavengeReferentsOfPointers: */ + foundNewReferent = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(ephemeron)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(ephemeron + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(ephemeron - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((ephemeron + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((ephemeron + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + corpseOffset = offsetToNextCorpse; + } + /* end scavengeEphemeronsOnEphemeronList */ +l3: + assert(GIV(numRememberedEphemerons) == 0); + assert(!GIV(ephemeronList)); + return firedIRS + || (firedOEL); +} + + +/* Go through the remembered set and the weak list, nilling references to + any objects that didn't survive the scavenge. Read the class comment + for a more in-depth description of the algorithm. */ + + /* SpurGenerationScavenger>>#processWeaklings */ +static void +processWeaklings(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt i; + sqInt listOffset; + sqInt rootObj; + usqInt weakCorpse; + sqInt weakObj; + + assert(allWeakSurvivorsOnWeakList()); + i = 0; + while (i < GIV(rememberedSetSize)) { + rootObj = GIV(rememberedSet)[i]; + + /* begin isWeakNonImm: */ + format = (byteAt((void *)(rootObj + (formatFieldByteOffset())))) & (formatMask()); + if (format == (weakArrayFormat())) { + /* If no more referents, remove by overwriting with the last element in the set. */ + if (processWeakSurvivor(rootObj)) { + i += 1; + } + else { + setIsRememberedOfto(rootObj, 0); + if ((i + 1) < GIV(rememberedSetSize)) { + GIV(rememberedSet)[i] = (GIV(rememberedSet)[GIV(rememberedSetSize) - 1]); + } + GIV(rememberedSetSize) -= 1; + } + } + else { + i += 1; + } + } + if (GIV(weakList)) { + weakCorpse = ((((usqInt)((GIV(weakList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + while (weakCorpse) { + assert(isForwarded(weakCorpse)); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(weakCorpse)); + weakObj = longAt((void *)((weakCorpse + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(weakObj & (tagMask())))) + && ((!((longAt((void *)(weakObj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + weakObj = longAt((void *)((weakObj + BaseHeaderSize) + (0U << (shiftForWord())))); + } + + /* weakObj may have been tenured... */ + if ((processWeakSurvivor(weakObj)) + && (((/* begin isOldObject: */ + assert(isNonImmediate(weakObj)), + oopisGreaterThanOrEqualTo(weakObj, GIV(oldSpaceStart)))) + && (!(((byteAt((void *)(weakObj + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0)))) { + remember(weakObj); + } + + /* begin nextCorpseOrNil: */ + assert(isYoung(weakCorpse)); + listOffset = ((((usqInt)(((long32At((void *)(weakCorpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(weakCorpse + (formatFieldByteOffset())))); + weakCorpse = (listOffset + ? ((((usqInt)((listOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0); + } + GIV(weakList) = null; + } +} + + +/* Process a weak survivor on the weakList. Those of its fields + which have not survived the scavenge should be nilled, and if any + are, the coInterpreter should be informed via fireFinalization:. + Answer if the weakObj has any young referents. */ + + /* SpurGenerationScavenger>>#processWeakSurvivor: */ +static NoDbgRegParms sqInt +processWeakSurvivor(sqInt weakObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classPointer; + sqInt hasYoungReferents; + sqInt i; + usqInt numSlots; + sqInt numStrongSlots; + sqInt referent; + sqInt referentSqInt; + sqInt toDoLimit; + sqInt weakObjShouldMourn; + + weakObjShouldMourn = (hasYoungReferents = 0); + + /* N.B. generateToByDoLimitExpression:negative:on: guards against (unsigned)0 - 1 going +ve */ + + /* begin numFixedSlotsOf: */ + classPointer = fetchClassOfNonImm(weakObj); + classFormat = ((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + numStrongSlots = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + for (i = 0; i < numStrongSlots; i += 1) { + referent = longAt((void *)((weakObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(referent & (tagMask())))) + && ((/* begin isYoungObject: */ + assert(isNonImmediate(referent)), + oopisLessThan(referent, GIV(oldSpaceStart))))) { + hasYoungReferents = 1; + } + } + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(weakObj)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(weakObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(weakObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = numStrongSlots; i <= toDoLimit; i += 1) { + referent = longAt((void *)((weakObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* Referent could be forwarded due to scavenging or a become:, don't assume. */ + if ((!(referent & (tagMask())))) { + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + + /* weakObj is either young or already in remembered table; no need to check */ + assert((isReallyYoungObject(weakObj)) + || (isRemembered(weakObj))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(weakObj)) + && (!(isForwarded(weakObj)))); + assert(validStorePointerUncheckedArgs(i, weakObj, referent)); + longAtput((void *)((weakObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + if (isMaybeOldScavengeSurvivor(referent)) { + /* begin isYoungObject: */ + assert(isNonImmediate(referent)); + if (oopisLessThan(referent, GIV(oldSpaceStart))) { + hasYoungReferents = 1; + } + } + else { + weakObjShouldMourn = 1; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(weakObj)) + && (!(isForwarded(weakObj)))); + assert(validStorePointerUncheckedArgs(i, weakObj, GIV(nilObj))); + longAtput((void *)((weakObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } + } + } + if (weakObjShouldMourn) { + /* begin fireFinalization: */ + if (((imageHeaderFlags & 64) != 0)) { + queueMourner(weakObj); + } + + /* begin signalFinalization: */ + forceInterruptCheck(); + GIV(pendingFinalizationSignals) += 1; + } + return hasYoungReferents; +} + + +/* Add the argument to the remembered set and set its isRemembered bit to + true. Answer the argument for the benefit of the Cogit. */ + + /* SpurGenerationScavenger>>#remember: */ +sqInt +remember(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(isNonImmediate(objOop)); + assert(!((isYoungObject(objOop)))); + assert(!((isRemembered(objOop)))); + assert(!((isInRememberedSet(objOop)))); + setIsRememberedOfto(objOop, 1); + if (GIV(rememberedSetSize) >= GIV(rememberedSetLimit)) { + growRememberedSet(); + } + GIV(rememberedSet)[GIV(rememberedSetSize)] = objOop; + if (((GIV(rememberedSetSize) += 1)) >= GIV(rememberedSetRedZone)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + return objOop; +} + + +/* scavengeFutureSurvivorSpaceStartingAt: does a depth-first traversal of the + new objects starting at the one at initialAddress in futureSurvivorSpace. */ + + /* SpurGenerationScavenger>>#scavengeFutureSurvivorSpaceStartingAt: */ +static NoDbgRegParms void +scavengeFutureSurvivorSpaceStartingAt(sqInt initialAddress) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + sqInt obj; + usqInt ptr; + + ptr = initialAddress; + while (ptr < GIV(futureSurvivorStart)) { + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(ptr + (numSlotsFieldByteOffset()))); + obj = (numSlots == (numSlotsMask()) + ? ptr + BaseHeaderSize + : ptr); + ptr = addressAfter(obj); + ((void) (scavengeReferentsOf(obj))); + } +} + + +/* There are ephemerons in the remembered set. Scavenge all with + scavenged/old keys, + leaving only firable/triggerable ephemerons. Answer if any + unfirable/inactive ephemerons + were scavenged. Answering false implies there are only firable ephemerons + (if any) within + the 0 to numRememberedEphemerons - 1 range. + Continue scavenging until at the fixed point of only having firable + ephemerons with still-to-be-scavenged keys in the numRememberedEphemerons + partition. */ + + /* SpurGenerationScavenger>>#scavengeInactiveEphemeronsInRememberedSet */ +static sqInt +scavengeInactiveEphemeronsInRememberedSet(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ephemeron; + sqInt i; + sqInt key; + sqInt numRememberedAtStartOfScan; + sqInt scavengedInactive; + + scavengedInactive = 0; + do { + numRememberedAtStartOfScan = GIV(numRememberedEphemerons); + i = 0; + while (i < GIV(numRememberedEphemerons)) { + ephemeron = GIV(rememberedSet)[i]; + assert((isEphemeron(ephemeron)) + || (isonObjStack(ephemeron, GIV(mournQueue)))); + + /* begin keyOfEphemeron: */ + assert((isNonImmediate(ephemeron)) + && (isObjEphemeron(ephemeron))); + key = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + if (isYetToBeScavenged(key)) { + i += 1; + } + else { + scavengedInactive = 1; + if (scavengeReferentsOfPointers(ephemeron)) { + if (i < (GIV(numRememberedEphemerons) - 1)) { + GIV(rememberedSet)[i] = (GIV(rememberedSet)[GIV(numRememberedEphemerons) - 1]); + GIV(rememberedSet)[GIV(numRememberedEphemerons) - 1] = ephemeron; + } + GIV(numRememberedEphemerons) -= 1; + } + else { + GIV(numRememberedEphemerons) -= 1; + GIV(previousRememberedSetSize) -= 1; + GIV(rememberedSetSize) -= 1; + + /* First overwrite with last firable ephemeron (could be a noop if this is the last one). + Then overwrite last firable entry with next unscanned rememberedSet entry (could also be a noop). + Then overwrite next unscanned entry with last unscanned rememberedSet entry (could also be a noop). */ + GIV(rememberedSet)[i] = (GIV(rememberedSet)[GIV(numRememberedEphemerons)]); + GIV(rememberedSet)[GIV(numRememberedEphemerons)] = (GIV(rememberedSet)[GIV(previousRememberedSetSize)]); + GIV(rememberedSet)[GIV(previousRememberedSetSize)] = (GIV(rememberedSet)[GIV(rememberedSetSize)]); + } + } + } + } while(!(GIV(numRememberedEphemerons) == numRememberedAtStartOfScan)); + + /* rememberedSet contains numRememberedEphemerons firable ephemerons in the rememberedSet. + These will be fired in processEphemerons fireEphemeronsInRememberedSet */ + return scavengedInactive; +} + + +/* Scavenge all untriggerable ephemerons on the ephemeronList and remove them + there-from. Answer if any were scavenged. */ + + /* SpurGenerationScavenger>>#scavengeInactiveEphemeronsOnEphemeronList */ +static sqInt +scavengeInactiveEphemeronsOnEphemeronList(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anyScavenged; + sqInt ephemeron; + usqInt ephemeronCorpse; + sqInt foundNewReferent; + sqInt i; + sqInt key; + sqInt nextCorpseOffset; + usqInt numSlots; + usqInt previousCorpse; + sqInt referent; + sqInt referentSqInt; + sqInt toDoLimit; + + if (!GIV(ephemeronList)) { + return 0; + } + anyScavenged = 0; + previousCorpse = null; + + /* In figuring out the termination condition I believe it is sufficient to consider four cases: + ephemeronList starts with + two firable corpses + two unfirable corpses + a firable corpse followed by an unfirable corpse + an unfirable corpse followed by a firable corpse */ + ephemeronCorpse = ((((usqInt)((GIV(ephemeronList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + while (ephemeronCorpse) { + assert((isYoung(ephemeronCorpse)) + && (isForwarded(ephemeronCorpse))); + nextCorpseOffset = ((((usqInt)(((long32At((void *)(ephemeronCorpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(ephemeronCorpse + (formatFieldByteOffset())))); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(ephemeronCorpse)); + ephemeron = longAt((void *)((ephemeronCorpse + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(ephemeron & (tagMask())))) + && ((!((longAt((void *)(ephemeron))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ephemeron = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + } + + /* begin keyOfMaybeFiredEphemeron: */ + assert(isMaybeFiredEphemeron(ephemeron)); + key = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + if (isYetToBeScavenged(key)) { + previousCorpse = ephemeronCorpse; + } + else { + anyScavenged = 1; + + /* begin scavengeReferentsOfPointers: */ + foundNewReferent = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(ephemeron)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(ephemeron + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(ephemeron - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((ephemeron + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((ephemeron + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + if (previousCorpse) { + /* begin setCorpseOffsetOf:to: */ + assert(isYoung(previousCorpse)); + assert(isForwarded(previousCorpse)); + long64Atput((void *)(previousCorpse),headerForSlotshashformatclassIndex(byteAt((void *)(previousCorpse + (numSlotsFieldByteOffset()))), ((usqInt)(nextCorpseOffset)) >> 8, nextCorpseOffset & 0xFF, isForwardedObjectClassIndexPun())); + } + else { + GIV(ephemeronList) = (nextCorpseOffset + ? nextCorpseOffset + : 0); + } + } + ephemeronCorpse = (nextCorpseOffset + ? ((((usqInt)((nextCorpseOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0); + } + if (previousCorpse) { + /* begin setCorpseOffsetOf:to: */ + assert(isYoung(previousCorpse)); + assert(isForwarded(previousCorpse)); + long64Atput((void *)(previousCorpse),headerForSlotshashformatclassIndex(byteAt((void *)(previousCorpse + (numSlotsFieldByteOffset()))), 0, 0, isForwardedObjectClassIndexPun())); + } + return anyScavenged; +} + + +/* This is the inner loop of the main routine, scavenge. It first scavenges + the new objects immediately + reachable from old ones and the stack. Then it scavenges those that are + transitively reachable. If any + results in a tenure, the tenuree gets remembered, and it first scavenges + objects adjacent to the tenure, + then scavenges the ones reachable from the tenuree. This loop continues + until no more reachable + objects are left. At that point, pastSurvivorSpace is exchanged with + futureSurvivorSpace. + Notice that each pointer in a live object is inspected once and only once. + The previousRememberedSetSize + and previousFutureSurvivorSpaceLimit variables ensure that no object is + scanned twice, as well as + detecting closure. If this were not true, some pointers might get + forwarded twice. + + An extension of the algorithm presented in David's original paper is to + handle weak arrays and ephemerons. + Weak arrays should not have their weak referents scavenged unless there + are strong references to them. + Ephemerons should fire if their key is not reachable other than from + ephemerons and weak arrays. + Handle this by maintaining a list for weak arrays and a list for + ephemerons, which allow scavenging these + objects once all other objects in new space have been scavenged, hence + allowing the scavenger to + detect which referents in new space of weak arrays are dead and of + ephemeron keys are only live due to + ephemerons. Read the class comment for a more in-depth description of the + algorithm. + Answer how many passes occurred. This is to use the value of p[ass which + is valuable for debugging. */ +/* debugging */ + + /* SpurGenerationScavenger>>#scavengeLoop */ +static sqInt +scavengeLoop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firedEphemerons; + sqInt firstTime; + sqInt i; + sqInt mournQueueSize; + sqInt newSize; + sqInt pass; + sqInt previousFutureSurvivorStart; + StackPage *thePage; + + mournQueueSize = 0; + assert(GIV(futureSurvivorStart) == ((GIV(futureSpace).start))); + GIV(weakList) = (GIV(ephemeronList) = null); + GIV(numRememberedEphemerons) = 0; + firstTime = 1; + GIV(previousRememberedSetSize) = 0; + previousFutureSurvivorStart = GIV(futureSurvivorStart); + firedEphemerons = 0; + pass = 0; + + /* begin initStackPageGC */ + if (GIV(stackPage)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + (thePage->trace = StackPageUnreached); + } + while (1) { + pass += 1; + scavengeRememberedSetStartingAt(GIV(previousRememberedSetSize)); + GIV(previousRememberedSetSize) = GIV(rememberedSetSize); + if (firstTime) { + mapInterpreterOops(); + mapMournQueue(); + mapExtraRoots(); + mournQueueSize = sizeOfObjStack(GIV(mournQueue)); + firstTime = 0; + } + + /* if nothing more copied and forwarded (or remembered by mapInterpreterOops et al) + to scavenge, and no ephemerons to process, the scavenge is done. */ + if ((GIV(previousRememberedSetSize) == GIV(rememberedSetSize)) + && ((previousFutureSurvivorStart == GIV(futureSurvivorStart)) + && ((!firedEphemerons) + && (!GIV(ephemeronList))))) { + assert(GIV(numRememberedEphemerons) == 0); + return pass; + } + scavengeFutureSurvivorSpaceStartingAt(previousFutureSurvivorStart); + previousFutureSurvivorStart = GIV(futureSurvivorStart); + + /* all reachable objects in this cycle have been promoted to futureSpace. + ephemerons can now be processed. */ + firedEphemerons = processEphemerons(); + if (((newSize = sizeOfObjStack(GIV(mournQueue)))) > mournQueueSize) { + mapMournQueue(); + mournQueueSize = newSize; + } + } + return pass; +} + + +/* Scavenge the referents of a normal pointer object. + Answer if it still refers to young objects. */ + + /* SpurGenerationScavenger>>#scavengeReferentsOfPointers: */ +static NoDbgRegParms sqInt +scavengeReferentsOfPointers(sqInt referrer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt foundNewReferent; + sqInt i; + usqInt numSlots; + sqInt referent; + sqInt referentSqInt; + sqInt toDoLimit; + + foundNewReferent = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(referrer)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(referrer + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(referrer - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + return foundNewReferent; +} + + +/* scavengeReferentsOf: referrer inspects relevant pointers in referrer. If + any are new objects, it has them moved to FutureSurvivorSpace, and + answers truth. If there are no new referents, it answers falsity. To + handle weak arrays, if the referrer is weak only scavenge strong slots and + answer true so that it won't be removed from the remembered set until + later. To handle ephemerons, only scavenge other slots if the key slot has + already been scavenged. */ +/* forwarding objects should be followed by callers, + unless the forwarder is a root in the remembered table. */ + + /* SpurGenerationScavenger>>#scavengeReferentsOf: */ +static NoDbgRegParms sqInt +scavengeReferentsOf(sqInt referrer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt foundNewReferent; + sqInt i; + usqInt numSlots; + sqInt referent; + sqInt referentSqInt; + sqInt toDoLimit; + + assert((!(isForwarded(referrer))) + || (isRemembered(referrer))); + assert((isInFutureSpace(referrer)) + || (isInOldSpace(referrer))); + switch ((byteAt((void *)(referrer + (formatFieldByteOffset())))) & (formatMask())) { + case nonIndexablePointerFormat(): + case arrayFormat(): + /* begin scavengeReferentsOfPointers: */ + foundNewReferent = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(referrer)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(referrer + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(referrer - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + return foundNewReferent; + + case indexablePointersFormat(): + /* begin scavengeReferentsOfIndexablePointers: */ + foundNewReferent = 0; + toDoLimit = (numSlotsOfIndexablePointerObj(referrer)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + return foundNewReferent; + + case weakArrayFormat(): + /* begin scavengeReferentsOfWeakling: */ + toDoLimit = (numStrongSlotsOfWeakling(referrer)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (!(oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart)))) { + referent = copyAndForward(referent); + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + + /* Answer true to keep any weak objects in the remembered set until later. */ + return 1; + + case ephemeronFormat(): + /* Scavenge the referents of an ephemeron. If the key + has not yet been scavenged, don't scavenge it yet. */ + if (isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(referrer)) + && (isObjEphemeron(referrer))), + /* fetchPointer:ofObject: */ + longAt((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord()))))))) { + return 1; + } + + /* begin scavengeReferentsOfPointers: */ + foundNewReferent = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(referrer)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(referrer + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(referrer - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + return foundNewReferent; + + case forwardedFormat(): + /* begin scavengeReferentOfForwarder: */ + foundNewReferent = 0; + referent = longAt((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord())))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord()))),referent); + } + return foundNewReferent; + + case firstCompiledMethodFormat(): + case (firstCompiledMethodFormat()) + 1: + case (firstCompiledMethodFormat()) + 2: + case (firstCompiledMethodFormat()) + 3: + case (firstCompiledMethodFormat()) + 4: + case (firstCompiledMethodFormat()) + 5: + case (firstCompiledMethodFormat()) + 6: + case (firstCompiledMethodFormat()) + 7: + /* begin scavengeReferentsOfCompiledCode: */ + foundNewReferent = 0; + toDoLimit = literalCountOf(referrer); + for (i = 1; i <= toDoLimit; i += 1) { + referent = longAt((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + /* a forwarding pointer could be because of become: or scavenging. */ + if ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(referent)); + referentSqInt = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + referent = referentSqInt; + } + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(referent, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + foundNewReferent = 1; + } + else { + referent = copyAndForward(referent); + + /* begin isReallyYoungObject: */ + assert(isNonImmediate(referent)); + if ((oopisLessThan(referent, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(referent, GIV(newSpaceStart)))) { + foundNewReferent = 1; + } + } + } + + /* if target is already in future space forwarding pointer was due to a become:. */ + + /* storePointerUnchecked:ofMaybeForwardedObject:withValue: */ + longAtput((void *)((referrer + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),referent); + } + } + return foundNewReferent; + + default: + assert(((numSlotsOf(referrer)) == 0) + || (isWordsOrBytesNonImm(referrer))); + } + return 0; +} + + +/* scavengeRememberedSetStartingAt: n traverses objects in the remembered + set starting at the nth one. If the object does not refer to any new + objects, it + is removed from the set. Otherwise, its new referents are scavenged. Defer + scavenging ephemerons until after a complete scavenge has been performed, + so that triggered ephemerons can be fired. Move them to the front of the + set and count them in numRememberedEphemerons for later scanning. */ + + /* SpurGenerationScavenger>>#scavengeRememberedSetStartingAt: */ +static NoDbgRegParms void +scavengeRememberedSetStartingAt(sqInt n) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt destIndex; + sqInt referrer; + sqInt sourceIndex; + + sourceIndex = (destIndex = n); + while (sourceIndex < GIV(rememberedSetSize)) { + referrer = GIV(rememberedSet)[sourceIndex]; + + /* Any potential firing ephemerons should not be scanned yet. + Move any to the front of the set to save time in later scanning. */ + if ((isEphemeron(referrer)) + && (isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(referrer)) + && (isObjEphemeron(referrer))), + /* fetchPointer:ofObject: */ + longAt((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord())))))))) { + assert(destIndex >= GIV(numRememberedEphemerons)); + GIV(rememberedSet)[destIndex] = (GIV(rememberedSet)[GIV(numRememberedEphemerons)]); + GIV(rememberedSet)[GIV(numRememberedEphemerons)] = referrer; + GIV(numRememberedEphemerons) += 1; + destIndex += 1; + } + else { + if (scavengeReferentsOf(referrer)) { + GIV(rememberedSet)[destIndex] = referrer; + destIndex += 1; + } + else { + setIsRememberedOfto(referrer, 0); + } + } + sourceIndex += 1; + } + GIV(rememberedSetSize) = destIndex; + assert(!((unfiredEphemeronsAtEndOfRememberedSet()))); +} + + +/* (Slang flattens so need unique selectors) */ + + /* SpurGenerationScavenger>>#scavengerTenuringThreshold */ +static float +scavengerTenuringThreshold(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (GIV(tenureThreshold) >= ((GIV(pastSpace).start)) + ? (((double) (GIV(tenureThreshold) - ((GIV(pastSpace).start))) )) / (((GIV(pastSpace).limit)) - ((GIV(pastSpace).start))) + : 0); +} + + +/* For assert checking only. */ + + /* SpurGenerationScavenger>>#unfiredEphemeronsAtEndOfRememberedSet */ +static sqInt +unfiredEphemeronsAtEndOfRememberedSet(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt i; + sqInt referrer; + + for (i = GIV(numRememberedEphemerons); i < GIV(rememberedSetSize); i += 1) { + referrer = GIV(rememberedSet)[i]; + + /* begin isEphemeron: */ + assert(isNonImmediate(referrer)); + format = (byteAt((void *)(referrer + (formatFieldByteOffset())))) & (formatMask()); + if (format == (ephemeronFormat())) { + if (isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(referrer)) + && (isObjEphemeron(referrer))), + /* fetchPointer:ofObject: */ + longAt((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord()))))))) { + return 1; + } + } + } + return 0; +} + + +/* Answer if the remembered table is laid out correctly after + scavengeInactiveEphemeronsInRememberedSet. This implies that up to + numRememberedEphemerons are only unfired ephemerons, and no unfired + ephemerons beyond. + */ + + /* SpurGenerationScavenger>>#validRememberedSetPartitionPostSIEIRS */ +static sqInt +validRememberedSetPartitionPostSIEIRS(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt i; + sqInt referrer; + + for (i = 0; i < GIV(rememberedSetSize); i += 1) { + referrer = GIV(rememberedSet)[i]; + + /* begin isEphemeron: */ + assert(isNonImmediate(referrer)); + format = (byteAt((void *)(referrer + (formatFieldByteOffset())))) & (formatMask()); + if (format == (ephemeronFormat())) { + if ((isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(referrer)) + && (isObjEphemeron(referrer))), + /* fetchPointer:ofObject: */ + longAt((void *)((referrer + BaseHeaderSize) + (0U << (shiftForWord())))))) + ? i >= GIV(numRememberedEphemerons) + : i < GIV(numRememberedEphemerons))) { + return 0; + } + } + } + return 1; +} + + +/* Answer if everything on the ephemeronList is a firable ephemeron */ + + /* SpurGenerationScavenger>>#wholeEphemeronListIsFirable */ +static sqInt +wholeEphemeronListIsFirable(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ephemeron; + usqInt ephemeronCorpse; + sqInt listOffset; + + if (!GIV(ephemeronList)) { + return 1; + } + ephemeronCorpse = ((((usqInt)((GIV(ephemeronList) - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart); + while (ephemeronCorpse) { + if (!((/* isYoung: */ + ((!(ephemeronCorpse & (tagMask())))) + && (oopisLessThan(ephemeronCorpse, GIV(oldSpaceStart)))) + && ((!((longAt((void *)(ephemeronCorpse))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + return 0; + } + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(ephemeronCorpse)); + ephemeron = longAt((void *)((ephemeronCorpse + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(ephemeron & (tagMask())))) + && ((!((longAt((void *)(ephemeron))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ephemeron = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))); + } + if (!((isEphemeron(ephemeron)) + && (isYetToBeScavenged( + (/* begin keyOfEphemeron: */ + assert((isNonImmediate(ephemeron)) + && (isObjEphemeron(ephemeron))), + /* fetchPointer:ofObject: */ + longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord()))))))))) { + return 0; + } + + /* begin nextCorpseOrNil: */ + assert(isYoung(ephemeronCorpse)); + listOffset = ((((usqInt)(((long32At((void *)(ephemeronCorpse + 4))) & (identityHashHalfWordMask()))) << 8))) + (byteAt((void *)(ephemeronCorpse + (formatFieldByteOffset())))); + ephemeronCorpse = (listOffset + ? ((((usqInt)((listOffset - 1)) << 3 /* shiftForAllocationUnit */))) + GIV(newSpaceStart) + : 0); + } + return 1; +} + + +/* Output the entire record. */ + + /* SpurGenerationScavenger>>#writeScavengeLog */ +static NeverInline void +writeScavengeLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + static char *policyNames[] = {"", "by age", "by class", "to shrink rt", "don't tenure", "mark on tenure"}; + + /* log data collected by logStartScavenge */ + fprintf(GIV(scavengeLog), + "scavenge %" PRIdSQINT " eden bytes: 0x%" PRIxSQINT "/%" PRIdSQINT " past bytes: 0x%" PRIxSQINT "/%" PRIdSQINT "\n\trem set: %" PRIdSQINT " redzone: %" PRIdSQINT " size: %" PRIdSQINT "\n", + GIV(statScavenges), + (GIV(scavengeLogRecord).sEdenBytes), + (GIV(scavengeLogRecord).sEdenBytes), + (GIV(scavengeLogRecord).sPastBytes), + (GIV(scavengeLogRecord).sPastBytes), + (GIV(scavengeLogRecord).sRememberedSetSize), + (GIV(scavengeLogRecord).sRememberedSetRedZone), + (GIV(scavengeLogRecord).sRememberedSetLimit)); + + /* log data collected by logTenuringPolicy */ + fprintf(GIV(scavengeLog), + (((GIV(scavengeLogRecord).tTenureCriterion)) == TenureToShrinkRT + ? "\ttenure below 0x%" PRIxSQINT "/%" PRIdSQINT " %s refct %" PRIdSQINT "\n" + : "\ttenure below 0x%" PRIxSQINT "/%" PRIdSQINT " %s\n"), + (GIV(scavengeLogRecord).tTenureThreshold), + (GIV(scavengeLogRecord).tTenureThreshold), + policyNames[(GIV(scavengeLogRecord).tTenureCriterion)], + (GIV(scavengeLogRecord).tRefCountToShrinkRT)); + + /* log data collected by logEndScavenge */ + fprintf(GIV(scavengeLog), + "\tsurvivor bytes: 0x%" PRIxSQINT "/%" PRIdSQINT " rem set: %" PRIdSQINT " tenured: %" PRIdSQINT " usecs: %" PRIdSQINT "\n", + (GIV(scavengeLogRecord).eSurvivorBytes), + (GIV(scavengeLogRecord).eSurvivorBytes), + (GIV(scavengeLogRecord).eRememberedSetSize), + ((GIV(scavengeLogRecord).eStatTenures)) - ((GIV(scavengeLogRecord).sStatTenures)), + GIV(statSGCDeltaUsecs)); + fflush(GIV(scavengeLog)); +} + + +/* Answer the accessible object following the given object or + free chunk in the heap. Return nil when heap is exhausted. + This is for primitiveNextObject subsequent to primitiveSomeObject. + It also tries to handle more general use by ordering objects as + eden + past + old + but this is tricky becaus ethe order in memory is + past + eden + old */ + + /* SpurMemoryManager>>#accessibleObjectAfter: */ +static NoDbgRegParms sqInt +accessibleObjectAfter(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objAfter; + + objAfter = objOop; + if (oopisLessThan(objAfter, GIV(nilObj))) { + assert((isInEden(objOop)) + || (isInPastSpace(objOop))); + if (oopisGreaterThan(objAfter, GIV(pastSpaceStart))) { + /* Obj is in eden. Answer next normal object in eden, if there is one. */ + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objAfter); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objAfter = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objAfter = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + if (!(oopisLessThan(objAfter, GIV(freeStart)))) break; + if (((longAt((void *)(objAfter))) & (classIndexMask())) > (lastClassIndexPun())) { + return objAfter; + } + } + + /* There wasn't a next object in eden. If past space is empty answer nilObj. */ + if (GIV(pastSpaceStart) <= (((GIV(pastSpace)).start))) { + return GIV(nilObj); + } + + /* If the first object in pastSpace is OK, answer it, otherwise fall through to enumerate past space. */ + address = ((GIV(pastSpace)).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objAfter = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + if (((longAt((void *)(objAfter))) & (classIndexMask())) > (lastClassIndexPun())) { + return objAfter; + } + } + + /* Either objOop was in pastSpace, or enumeration exhaused eden, so enumerate past space. */ + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objAfter); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + objAfter = GIV(pastSpaceStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objAfter = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l2:; + if (!(oopisLessThan(objAfter, GIV(pastSpaceStart)))) break; + if (((longAt((void *)(objAfter))) & (classIndexMask())) > (lastClassIndexPun())) { + return objAfter; + } + } + return GIV(nilObj); + } + + /* object in new space */ + while (1) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objAfter); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objAfter = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + objAfter = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3: + if (objAfter == GIV(endOfMemory)) { + return null; + } + if (((longAt((void *)(objAfter))) & (classIndexMask())) > (lastClassIndexPun())) { + return objAfter; + } + } + return 0; +} + + +/* Answer whether an ephemeron is active (has an unmarked + key) and was pushed on the unscanned ephemerons stack. */ + + /* SpurMemoryManager>>#activeAndDeferredScan: */ +static NoDbgRegParms NeverInline sqInt +activeAndDeferredScan(sqInt anEphemeron) +{ + sqInt key; + + key = 0; + assert(isEphemeron(anEphemeron)); + if ((isImmediate((/* begin keyOfEphemeron: */ + assert((isNonImmediate(anEphemeron)) + && (isObjEphemeron(anEphemeron))), + (key = longAt((void *)((anEphemeron + BaseHeaderSize) + (0U << (shiftForWord())))))))) + || (((byteAt((void *)(key + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) { + return 0; + } + return pushOnUnscannedEphemeronsStack(anEphemeron); +} + + +/* Add a freeChunk sub tree back into the large free chunk tree. + This is for allocateOldSpaceChunkOf[Exactly]Bytes:[suchThat:]. */ +/* N.B. *can't* use numSlotsOfAny: because of rounding up of odd slots + and/or step in size at 1032 bytes in 32-bits or 2048 bytes in 64-bits. */ + + /* SpurMemoryManager>>#addFreeSubTree: */ +static NoDbgRegParms void +addFreeSubTree(sqInt freeTree) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytesInArg; + usqInt bytesInNode; + sqInt subNode; + sqInt treeNode; + + assert(isFreeObject(freeTree)); + bytesInArg = bytesInBody(freeTree); + assert(bytesInArg >= ((numFreeLists()) * (allocationUnit()))); + treeNode = GIV(freeLists)[0]; + assert(treeNode != 0); + while (1) { + bytesInNode = bytesInBody(treeNode); + + /* check for overlap; could write this as self oop: (self objectAfter: freeChunk) isLessThanOrEqualTo: child... + but that relies on headers being correct, etc. So keep it clumsy... */ + assert((oopisLessThanOrEqualTo((freeTree + bytesInArg) - BaseHeaderSize, treeNode)) + || (oopisGreaterThanOrEqualTo(freeTree, (treeNode + bytesInNode) - BaseHeaderSize))); + assert(bytesInNode >= ((numFreeLists()) * (allocationUnit()))); + assert(bytesInArg != bytesInNode); + if (bytesInNode > bytesInArg) { + subNode = longAt((void *)((treeNode + BaseHeaderSize) + (3U << (shiftForWord())))); + if (!subNode) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(treeNode)); + assert((freeTree == 0) + || (isFreeObject(freeTree))); + longAtput((void *)((treeNode + BaseHeaderSize) + (3U << (shiftForWord()))),freeTree); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeTree)); + assert((treeNode == 0) + || (isFreeObject(treeNode))); + longAtput((void *)((freeTree + BaseHeaderSize) + (2U << (shiftForWord()))),treeNode); + return; + } + } + else { + subNode = longAt((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + if (!subNode) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(treeNode)); + assert((freeTree == 0) + || (isFreeObject(freeTree))); + longAtput((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord()))),freeTree); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeTree)); + assert((treeNode == 0) + || (isFreeObject(treeNode))); + longAtput((void *)((freeTree + BaseHeaderSize) + (2U << (shiftForWord()))),treeNode); + return; + } + } + treeNode = subNode; + } +} + + +/* Add the given variable location to the extra roots table. */ + + /* SpurMemoryManager>>#addGCRoot: */ +sqInt +addGCRoot(sqInt *varLoc) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (GIV(extraRootCount) >= ExtraRootsSize) { + return 0; + } + GIV(extraRoots)[(GIV(extraRootCount) += 1)] = varLoc; + return 1; +} + + +/* Answer if maybeClassObj looks like a class object */ + + /* SpurMemoryManager>>#addressCouldBeClassObj: */ +static NoDbgRegParms sqInt +addressCouldBeClassObj(sqInt maybeClassObj) +{ + return (addressCouldBeObj(maybeClassObj)) + && (objCouldBeClassObj(maybeClassObj)); +} + + /* SpurMemoryManager>>#addressCouldBeObj: */ +sqInt +addressCouldBeObj(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((address & (BaseHeaderSize - 1)) == 0) + && ((/* Inlined isInOldSpace: for speed, because it also segregates the newSpace objects */ + + (oopisGreaterThanOrEqualTo(address, GIV(oldSpaceStart)) + ? oopisLessThan(address, GIV(endOfMemory)) + : (oopisGreaterThanOrEqualToandLessThan(address, ((GIV(eden)).start), GIV(freeStart))) + || ((oopisGreaterThanOrEqualToandLessThan(address, ((GIV(pastSpace)).start), GIV(pastSpaceStart))) + || ((GIV(gcPhaseInProgress) == ScavengeInProgress) + && (oopisGreaterThanOrEqualToandLessThan(address, ((GIV(futureSpace)).start), GIV(futureSurvivorStart)))))))); +} + + +/* Answer if address appears to be that of either an immediate or an object. + For code disassembly and assertions. */ + + /* SpurMemoryManager>>#addressCouldBeOop: */ +sqInt +addressCouldBeOop(sqInt address) +{ + return (((address & (tagMask())) != 0)) + || (addressCouldBeObj(address)); +} + + +/* Add freeChunk to the relevant freeList. + For the benefit of sortedFreeObject:, if freeChunk is large, answer the + treeNode it + is added to, if it is added to the next list of a freeTreeNode, otherwise + answer 0. */ +/* coInterpreter transcript ensureCr. coInterpreter print: 'freeing '. self + printFreeChunk: freeChunk. + */ + + /* SpurMemoryManager>>#addToFreeList:bytes: */ +static NoDbgRegParms sqInt +addToFreeListbytes(sqInt freeChunk, sqInt chunkBytes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt child; + usqInt childBytes; + sqInt index; + sqInt lilliputian; + sqInt nextFreeChunk; + sqInt parent; + + assert(isFreeObject(freeChunk)); + assert(chunkBytes == (bytesInBody(freeChunk))); + + /* Too slow to be enabled byt default but useful to debug Selective... + self deny: (compactor isSegmentBeingCompacted: (segmentManager segmentContainingObj: freeChunk)). */ + index = chunkBytes / 8 /* allocationUnit */; + if (index < 64 /* numFreeLists */) { + nextFreeChunk = GIV(freeLists)[index]; + + /* begin setNextFreeChunkOf:withValue:chunkBytes: */ + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + lilliputian = chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if ((nextFreeChunk != 0) + && (!lilliputian)) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((freeChunk == 0) + || (isFreeObject(freeChunk))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),freeChunk); + } + + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + if (!(chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */))) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + GIV(freeLists)[index] = freeChunk; + GIV(freeListsMask) = GIV(freeListsMask) | (1ULL << index); + return 0; + } + + /* begin addToFreeTree:bytes: */ + /* begin initFreeTreeChunk:bytes: */ + assert(isFreeObject(freeChunk)); + assert(chunkBytes == (bytesInBody(freeChunk))); + assert(chunkBytes >= ((numFreeLists()) * (allocationUnit()))); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord()))),0); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),0); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (2U << (shiftForWord()))),0); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (3U << (shiftForWord()))),0); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + longAtput((void *)((freeChunk + BaseHeaderSize) + (4U << (shiftForWord()))),0); + + /* Large chunk list organized as a tree, each node of which is a list of chunks of the same size. + Beneath the node are smaller and larger blocks. */ + parent = 0; + child = GIV(freeLists)[0]; + while (child != 0) { + childBytes = bytesInBody(child); + + /* check for overlap; could write this as self oop: (self objectAfter: freeChunk) isLessThanOrEqualTo: child... + but that relies on headers being correct, etc. So keep it clumsy... */ + assert((oopisLessThanOrEqualTo((freeChunk + chunkBytes) - BaseHeaderSize, child)) + || (oopisGreaterThanOrEqualTo(freeChunk, (child + childBytes) - BaseHeaderSize))); + if (childBytes == chunkBytes) { + nextFreeChunk = longAt((void *)((child + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((freeChunk == 0) + || (isFreeObject(freeChunk))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),freeChunk); + } + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(child)); + assert((freeChunk == 0) + || (isFreeObject(freeChunk))); + longAtput((void *)((child + BaseHeaderSize) + (0U << (shiftForWord()))),freeChunk); + if (freeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + assert((child == 0) + || (isFreeObject(child))); + longAtput((void *)((freeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),child); + } + return child; + } + + /* size match; add to list at node. + walk down the tree */ + parent = child; + child = longAt((void *)((child + BaseHeaderSize) + ((((usqInt)(((childBytes > chunkBytes + ? 3 /* freeChunkSmallerIndex */ + : 4 /* freeChunkLargerIndex */))) << (shiftForWord())))))); + } + if (!parent) { + assert((GIV(freeLists)[0]) == 0); + GIV(freeLists)[0] = freeChunk; + GIV(freeListsMask) = GIV(freeListsMask) | 1; + return 0; + } + assert(((GIV(freeListsMask) & 1) != 0)); + + /* insert in tree */ + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + assert((parent == 0) + || (isFreeObject(parent))); + longAtput((void *)((freeChunk + BaseHeaderSize) + (2U << (shiftForWord()))),parent); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(parent)); + assert((freeChunk == 0) + || (isFreeObject(freeChunk))); + longAtput((void *)((parent + BaseHeaderSize) + ((((usqInt)(((childBytes > chunkBytes + ? 3 /* freeChunkSmallerIndex */ + : 4 /* freeChunkLargerIndex */))) << (shiftForWord()))))),freeChunk); + return 0; +} + + +/* Attempt to answer an array of all objects, excluding those that may + be garbage collected as a side effect of allocating the result array. + If no memory is available answer the number of instances as a + SmallInteger. Since objects are at least 16 bytes big, and the largest + SmallInteger covers + 1/4 of the address space, the count can never overflow. */ + + /* SpurMemoryManager>>#allInstancesOf: */ +static NoDbgRegParms sqInt +allInstancesOf(sqInt aClass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt actualIndex; + usqInt bytes; + int classIndex; + sqInt classOrNil; + sqInt count; + sqInt countSqInt; + int expectedIndex; + int expectedIndexInt; + sqInt followingWord; + usqInt followingWordAddress; + sqInt freeChunk; + sqInt i; + sqInt index; + sqInt iSqInt; + sqInt j; + usqInt limit; + usqInt newObj; + usqInt numBytes; + usqInt numSlots; + sqInt objOop; + sqInt page; + sqInt prevObj; + sqInt prevPrevObj; + sqInt ptr; + sqInt ptrSqInt; + sqInt smallObj; + usqInt start; + usqInt startUsqInt; + sqInt toDoLimit; + sqInt valuePointer; + + classIndex = (long32At((void *)(aClass + 4))) & (identityHashHalfWordMask()); + if (!classIndex) { + /* begin allocateSlots:format:classIndex: */ + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + 8 /* allocationUnit */; + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (0 <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + freeChunk = allocateSlotsInOldSpacebytesformatclassIndex(0, numBytes, arrayFormat(), ClassArrayCompactIndex); + goto l1; + } + longAtput((void *)(newObj),((((((usqLong) 0)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + freeChunk = newObj; + /* end allocateSlots:format:classIndex: */ +l1: + return freeChunk; + } + + /* N.B. Does /not/ update totalFreeOldSpace */ + freeChunk = allocateLargestFreeChunk(); + start = freeChunk + BaseHeaderSize; + limit = addressAfter(freeChunk); + + /* begin isClassAtUniqueIndex: */ + expectedIndex = (long32At((void *)(aClass + 4))) & (identityHashHalfWordMask()); + for (iSqInt = 0; iSqInt < GIV(numClassTablePages); iSqInt += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNil = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNil != GIV(nilObj)) { + index = ((((usqInt)(iSqInt) << (classTableMajorIndexShift())))) + j; + if ((classOrNil == aClass) + && ((index != expectedIndex) + && (index > (lastClassIndexPun())))) { + goto l4; + } + } + } + } + + /* begin uniqueIndex:allInstancesInto:limit:resultsInto: */ + countSqInt = 0; + ptrSqInt = start; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + + /* continue enumerating even if no room so as to unmark all objects. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + countSqInt += 1; + if (ptrSqInt < limit) { + longAtput((void *)(ptrSqInt),objOop); + ptrSqInt += BytesPerOop; + } + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l10; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l10:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + startUsqInt = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (startUsqInt > GIV(freeStart)) { + goto l9; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(startUsqInt + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? startUsqInt + BaseHeaderSize + : startUsqInt); + while (oopisLessThan(objOop, GIV(freeStart))) { + /* continue enumerating even if no room so as to unmark all objects. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + countSqInt += 1; + if (ptrSqInt < limit) { + longAtput((void *)(ptrSqInt),objOop); + ptrSqInt += BytesPerOop; + } + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l8; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l8:; + } + /* end allNewSpaceEntitiesDo: */ +l9: + count = countSqInt; + ptr = ptrSqInt; + goto l3; +l4: + + /* begin ambiguousClass:allInstancesInto:limit:resultsInto: */ + countSqInt = 0; + ptrSqInt = start; + expectedIndex = (long32At((void *)(aClass + 4))) & (identityHashHalfWordMask()); + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + + /* continue enumerating even if no room so as to unmark all objects and/or normalize class indices. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + actualIndex = (longAt((void *)(objOop))) & (classIndexMask()); + if ((classOrNilAtIndex(actualIndex)) == aClass) { + if (actualIndex != expectedIndex) { + /* begin setClassIndexOf:to: */ + assert(((expectedIndex >= 0) && (expectedIndex <= (classIndexMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(classIndexMask()))) + expectedIndex); + } + countSqInt += 1; + if (ptrSqInt < limit) { + longAtput((void *)(ptrSqInt),objOop); + ptrSqInt += BytesPerOop; + } + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l7; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l7:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + startUsqInt = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (startUsqInt > GIV(freeStart)) { + goto l6; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(startUsqInt + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? startUsqInt + BaseHeaderSize + : startUsqInt); + while (oopisLessThan(objOop, GIV(freeStart))) { + /* continue enumerating even if no room so as to unmark all objects and/or normalize class indices. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + actualIndex = (longAt((void *)(objOop))) & (classIndexMask()); + if ((classOrNilAtIndex(actualIndex)) == aClass) { + if (actualIndex != expectedIndex) { + /* begin setClassIndexOf:to: */ + assert(((expectedIndex >= 0) && (expectedIndex <= (classIndexMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(classIndexMask()))) + expectedIndex); + } + countSqInt += 1; + if (ptrSqInt < limit) { + longAtput((void *)(ptrSqInt),objOop); + ptrSqInt += BytesPerOop; + } + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l5; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l5:; + } + /* end allNewSpaceEntitiesDo: */ +l6: + + /* begin purgeDuplicateClassTableEntriesFor: */ + expectedIndexInt = (long32At((void *)(aClass + 4))) & (identityHashHalfWordMask()); + for (iSqInt = 0; iSqInt < GIV(numClassTablePages); iSqInt += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNil = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNil != GIV(nilObj)) { + index = ((((usqInt)(iSqInt) << (classTableMajorIndexShift())))) + j; + if ((classOrNil == aClass) + && ((index != expectedIndexInt) + && (index > (lastClassIndexPun())))) { + classAtIndexput(index, GIV(nilObj)); + if (index < GIV(classTableIndex)) { + GIV(classTableIndex) = index; + } + } + } + } + } + + /* classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + assert(GIV(classTableIndex) >= (1U << (classTableMajorIndexShift()))); + count = countSqInt; + ptr = ptrSqInt; +l3: + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + if ((count > ((ptr - start) / BytesPerOop)) + || ((limit != ptr) + && ((limit - ptr) <= 8 /* allocationUnit */))) { + freeChunkWithBytesat(bytesInBody(freeChunk), /* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk)); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return (((usqInt)count << 3) | 1); + } + + /* not enough room + can't split a single word */ + if (count < (numSlotsMask())) { + /* begin allocateSlots:format:classIndex: */ + if (count >= (numSlotsMask())) { + if ((((usqInt)(count)) >> 56) > 0) { + smallObj = null; + goto l2; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (count * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((count < 1 + ? 8 /* allocationUnit */ + : count * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (count <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + smallObj = allocateSlotsInOldSpacebytesformatclassIndex(count, numBytes, arrayFormat(), ClassArrayCompactIndex); + goto l2; + } + if (count >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + count); + longAtput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + } + else { + longAtput((void *)(newObj),((((((usqLong) count)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + smallObj = newObj; + /* end allocateSlots:format:classIndex: */ +l2: + for (i = 0; i < count; i += 1) { + valuePointer = longAt((void *)((freeChunk + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(smallObj)) + && (!(isForwarded(smallObj)))); + assert(validStorePointerUncheckedArgs(i, smallObj, valuePointer)); + longAtput((void *)((smallObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + freeChunkWithBytesat(bytesInBody(freeChunk), /* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk)); + beRootIfOld(smallObj); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return smallObj; + } + bytes = (BaseHeaderSize + BaseHeaderSize) + (count * BytesPerOop); + start = /* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk); + freeChunkWithBytesat((limit - start) - bytes, start + bytes); + GIV(totalFreeOldSpace) -= bytes; + + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(freeChunk - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56))) + count); + + /* begin set:classIndexTo:formatTo: */ + assert(((ClassArrayCompactIndex >= 0) && (ClassArrayCompactIndex <= (classIndexMask())))); + assert((((arrayFormat()) >= 0) && ((arrayFormat()) <= (formatMask())))); + longAtput((void *)(freeChunk),((longAt((void *)(freeChunk))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + (ClassArrayCompactIndex + ((((usqInt)((arrayFormat())) << (formatShift())))))); + + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(freeChunk + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(freeChunk); + } + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + runLeakCheckerFor(GCModeFull); + return freeChunk; +} + + +/* Attempt to answer an array of all objects, excluding those that may + be garbage collected as a side effect of allocating the result array. + If no memory is available answer the number of objects as a SmallInteger. + Since objects are at least 16 bytes big, and the largest SmallInteger + covers 1/4 of the address space, the count can never overflow. */ + + /* SpurMemoryManager>>#allObjects */ +static sqInt +allObjects(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytes; + sqInt count; + sqInt followingWord; + usqInt followingWordAddress; + sqInt freeChunk; + usqInt limit; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + sqInt ptr; + usqInt start; + usqInt startUsqInt; + + + /* N.B. Does /not/ update totalFreeOldSpace */ + freeChunk = allocateLargestFreeChunk(); + ptr = (start = freeChunk + BaseHeaderSize); + limit = addressAfter(freeChunk); + count = 0; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + + /* continue enumerating even if no room so as to unmark all objects. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + count += 1; + if (ptr < limit) { + longAtput((void *)(ptr),objOop); + ptr += BytesPerOop; + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + startUsqInt = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (startUsqInt > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(startUsqInt + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? startUsqInt + BaseHeaderSize + : startUsqInt); + while (oopisLessThan(objOop, GIV(freeStart))) { + /* continue enumerating even if no room so as to unmark all objects. */ + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + count += 1; + if (ptr < limit) { + longAtput((void *)(ptr),objOop); + ptr += BytesPerOop; + } + } + else { + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3: + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + assert(count >= (numSlotsMask())); + if ((count > ((ptr - start) / BytesPerOop)) + || ((limit != ptr) + && ((limit - ptr) <= 8 /* allocationUnit */))) { + freeChunkWithBytesat(bytesInBody(freeChunk), /* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk)); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return (((usqInt)count << 3) | 1); + } + + /* not enough room + can't split a single word */ + bytes = (BaseHeaderSize + BaseHeaderSize) + (count * BytesPerOop); + start = /* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk); + freeChunkWithBytesat((limit - start) - bytes, start + bytes); + GIV(totalFreeOldSpace) -= bytes; + + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(freeChunk - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56))) + count); + + /* begin set:classIndexTo:formatTo: */ + assert(((ClassArrayCompactIndex >= 0) && (ClassArrayCompactIndex <= (classIndexMask())))); + assert((((arrayFormat()) >= 0) && ((arrayFormat()) <= (formatMask())))); + longAtput((void *)(freeChunk),((longAt((void *)(freeChunk))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + (ClassArrayCompactIndex + ((((usqInt)((arrayFormat())) << (formatShift())))))); + + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(freeChunk + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(freeChunk); + } + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + runLeakCheckerFor(GCModeFull); + return freeChunk; +} + + /* SpurMemoryManager>>#allObjectsUnmarked */ +static sqInt +allObjectsUnmarked(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + GIV(bogon) = obj; + return 0; + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + return 1; +} + + /* SpurMemoryManager>>#allOldMarkedWeakObjectsOnWeaklingStack */ +static sqInt +allOldMarkedWeakObjectsOnWeaklingStack(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if ((isWeakNonImm(objOop)) + && (((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) { + if (!(isonObjStack(objOop, GIV(weaklingStack)))) { + return 0; + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + return 1; +} + + +/* Answer the largest free chunk in the free lists. */ +/* would like to use ifNotNil: but the ^next inside the ^blah ifNotNil: + confused Slang + */ + + /* SpurMemoryManager>>#allocateLargestFreeChunk */ +static sqInt +allocateLargestFreeChunk(void) +{ + sqInt freeChunk; + sqInt next; + sqInt nextFreeChunk; + + freeChunk = findLargestFreeChunk(); + if (!freeChunk) { + return null; + } + + /* This will be the node, not a list element. Answer a list element in preference. */ + next = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + if (next) { + assert((bytesInBody(freeChunk)) >= (numFreeLists())); + nextFreeChunk = longAt((void *)((next + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(freeChunk)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((freeChunk == 0) + || (isFreeObject(freeChunk))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),freeChunk); + } + return next; + } + unlinkSolitaryFreeTreeNode(freeChunk); + return freeChunk; +} + + +/* Answer a chunk of oldSpace from the free lists, if available, + otherwise answer nil. Break up a larger chunk if one of the + exact size does not exist. N.B. the chunk is simply a pointer, it + has no valid header. The caller *must* fill in the header correctly. */ +/* for debugging: */ +/* totalFreeOldSpace := self totalFreeListBytes */ + + /* SpurMemoryManager>>#allocateOldSpaceChunkOfBytes: */ +static NoDbgRegParms sqInt +allocateOldSpaceChunkOfBytes(usqInt chunkBytes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt child; + usqInt childBytes; + sqInt chunk; + usqInt index; + usqInt initialIndex; + sqInt lilliputian; + sqInt next; + sqInt nextFreeChunk; + usqInt nodeBytes; + sqInt parent; + + nodeBytes = 0; + + /* be optimistic (& don't wait for the write) */ + GIV(totalFreeOldSpace) -= chunkBytes; + initialIndex = chunkBytes / 8 /* allocationUnit */; + if ((initialIndex < 64 /* numFreeLists */) + && ((1ULL << initialIndex) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << initialIndex)) != 0)) { + if ((chunk = GIV(freeLists)[initialIndex])) { + assert(chunk == (startOfObject(chunk))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(chunk)); + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + lilliputian = chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */); + + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(chunk)) == (initialIndex * (allocationUnit()))) + && ((initialIndex > 1) + && ((startOfObject(chunk)) == chunk))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[initialIndex] = ((next = longAt((void *)((chunk + BaseHeaderSize) + (0U << (shiftForWord())))))); + if ((!lilliputian) + && (next != 0)) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + return chunk; + } + GIV(freeListsMask) -= 1ULL << initialIndex; + } + + /* first search for free chunks of a multiple of chunkBytes in size */ + index = initialIndex; + while ((((index += index)) < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << index)) != 0)) { + if ((chunk = GIV(freeLists)[index])) { + assert(chunk == (startOfObject(chunk))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(chunk)); + assert(((bytesInBody(chunk)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(chunk)) == chunk))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((next = longAt((void *)((chunk + BaseHeaderSize) + (0U << (shiftForWord())))))); + if (next) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + assert((bytesInBody(chunk)) == (index * (allocationUnit()))); + freeChunkWithBytesat((index * 8 /* allocationUnit */) - chunkBytes, (/* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk)) + chunkBytes); + return chunk; + } + GIV(freeListsMask) -= 1ULL << index; + } + } + + /* now get desperate and use the first that'll fit. + Note that because the minimum free size is 16 bytes (2 * allocationUnit), to + leave room for the forwarding pointer/next free link, we can only break chunks + that are at least 16 bytes larger, hence start at initialIndex + 2. */ + index = initialIndex + 1; + while ((((index += 1)) < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + if (((GIV(freeListsMask) & (1ULL << index)) != 0)) { + if ((chunk = GIV(freeLists)[index])) { + assert(chunk == (startOfObject(chunk))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(chunk)); + assert(((bytesInBody(chunk)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(chunk)) == chunk))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((next = longAt((void *)((chunk + BaseHeaderSize) + (0U << (shiftForWord())))))); + if (next) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(next)); + longAtput((void *)((next + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + assert((bytesInBody(chunk)) == (index * (allocationUnit()))); + freeChunkWithBytesat((index * 8 /* allocationUnit */) - chunkBytes, (/* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk)) + chunkBytes); + return chunk; + } + GIV(freeListsMask) -= 1ULL << index; + } + } + } + + /* Large chunk, or no space on small free lists. Search the large chunk list. + Large chunk list organized as a tree, each node of which is a list of chunks + of the same size. Beneath the node are smaller and larger blocks. + When the search ends parent should hold the smallest chunk at least as + large as chunkBytes, or 0 if none. */ + parent = 0; + child = GIV(freeLists)[0]; + while (child != 0) { + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(child)); + childBytes = bytesInBody(child); + if (childBytes == chunkBytes) { + chunk = longAt((void *)((child + BaseHeaderSize) + (0U << (shiftForWord())))); + if (chunk) { + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(chunk)); + nextFreeChunk = longAt((void *)((chunk + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(child)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((child + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((child == 0) + || (isFreeObject(child))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),child); + } + return /* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk); + } + nodeBytes = childBytes; + parent = child; + + /* break out of loop to remove interior node */ + child = 0; + } + else { + if (childBytes <= (chunkBytes + 8 /* allocationUnit */)) { + child = longAt((void *)((child + BaseHeaderSize) + (4U << (shiftForWord())))); + } + else { + /* parent will be smallest node >= chunkBytes + allocationUnit */ + parent = child; + nodeBytes = childBytes; + child = longAt((void *)((child + BaseHeaderSize) + (3U << (shiftForWord())))); + } + } + } + if (!parent) { + /* optimism was unfounded */ + GIV(totalFreeOldSpace) += chunkBytes; + return null; + } + + /* self printFreeChunk: parent */ + assert((nodeBytes == chunkBytes) + || (nodeBytes >= (chunkBytes + (2 * (allocationUnit()))))); + assert((bytesInBody(parent)) == nodeBytes); + + /* attempt to remove from list */ + chunk = longAt((void *)((parent + BaseHeaderSize) + (0U << (shiftForWord())))); + if (chunk) { + assert((chunkBytes == nodeBytes) + || ((chunkBytes + (allocationUnit())) < nodeBytes)); + nextFreeChunk = longAt((void *)((chunk + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(parent)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((parent + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if (nextFreeChunk) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((parent == 0) + || (isFreeObject(parent))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),parent); + } + if (chunkBytes != nodeBytes) { + freeChunkWithBytesat(nodeBytes - chunkBytes, (/* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk)) + chunkBytes); + } + return /* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk); + } + + /* no list; remove the interior node */ + chunk = parent; + unlinkSolitaryFreeTreeNode(chunk); + + /* if there's space left over, add the fragment back. */ + if (chunkBytes != nodeBytes) { + freeChunkWithBytesat(nodeBytes - chunkBytes, (/* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk)) + chunkBytes); + } + return /* startOfObject: */ + ((byteAt((void *)(chunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? chunk - BaseHeaderSize + : chunk); +} + + /* SpurMemoryManager>>#allocatePinnedSlots: */ +sqInt +allocatePinnedSlots(sqInt nSlots) +{ + sqInt obj; + usqInt p; + usqInt toDoLimit; + + obj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(nSlots, /* objectBytesForSlots: */ + (nSlots + ? ((((usqInt)(nSlots) << (shiftForWord())))) + ((nSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (obj) { + assert(isPinned(obj)); + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((obj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1, addressAfter(obj))); + toDoLimit = ((usqInt)(((obj + BaseHeaderSize) + (nSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(obj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + return obj; +} + + +/* Answer the oop of a chunk of space in oldSpace with numSlots slots. The + header will have been filled-in but not the contents. If no memory is + available answer nil. */ + + /* SpurMemoryManager>>#allocateSlotsInOldSpace:bytes:format:classIndex: */ +static NoDbgRegParms sqInt +allocateSlotsInOldSpacebytesformatclassIndex(sqInt numSlots, usqInt totalBytes, sqInt formatField, sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt chunk; + + if ((chunk = allocateOldSpaceChunkOfBytes(totalBytes))) { + if (numSlots >= (numSlotsMask())) { + long64Atput((void *)(chunk),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + long64Atput((void *)(chunk + BaseHeaderSize),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex); + + /* begin checkFreeSpace:ignoring: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, chunk + BaseHeaderSize); + } + return chunk + BaseHeaderSize; + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + long64Atput((void *)(chunk),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex); + + /* begin checkFreeSpace:ignoring: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, chunk); + } + return chunk; + } + return 0; +} + + +/* All objects are a multiple of 8 bytes in length */ + + /* SpurMemoryManager>>#allocationUnit */ +static sqInt +allocationUnit(void) +{ + return 8; +} + + +/* N.B. generateToByDoLimitExpression:negative:on: guards against (unsigned)0 + - 1 going +ve + */ + + /* SpurMemoryManager>>#allStrongSlotsOfWeaklingAreMarked: */ +static NoDbgRegParms sqInt +allStrongSlotsOfWeaklingAreMarked(sqInt aWeakling) +{ + sqInt i; + sqInt referent; + sqInt toDoLimit; + + toDoLimit = (numStrongSlotsOfWeakling(aWeakling)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + referent = longAt((void *)((aWeakling + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + if (!((byteAt((void *)(referent + (markBitsByteOffset())))) & (1U << (markedBitByteShift())))) { + return 0; + } + } + } + return 1; +} + + /* SpurMemoryManager>>#allUnscannedEphemeronsAreActive */ +static sqInt +allUnscannedEphemeronsAreActive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt key; + usqInt p; + + for (p = ((GIV(unscannedEphemerons).start)); p <= ((GIV(unscannedEphemerons).top)); p += BytesPerOop) { + /* begin keyOfMaybeFiredEphemeron: */ + assert(isMaybeFiredEphemeron(longAt((void *)(p)))); + key = longAt((void *)(((longAt((void *)(p))) + BaseHeaderSize) + (0U << (shiftForWord())))); + if ((((key & (tagMask())) != 0)) + || (((byteAt((void *)(key + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) { + return 0; + } + } + return 1; +} + + +/* we don't want to inline so we can nest that in an assertion with the + return true so the production VM does not generate any code here, while in + simulation, the code breaks on the assertion we want to. + */ + + /* SpurMemoryManager>>#assertInnerValidFreeObject: */ +static NoDbgRegParms NeverInline sqInt +assertInnerValidFreeObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cameFrom; + sqInt chunk; + sqInt chunkBytes; + usqInt index; + sqInt largeChild; + sqInt smallChild; + sqInt treeNode; + + assert(oopisLessThanOrEqualTo(addressAfter(objOop), GIV(endOfMemory))); + chunk = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert((chunk == 0) + || (isFreeOop(chunk))); + chunkBytes = bytesInBody(objOop); + + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + if (!(chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */))) { + /* double linkedlist assertions */ + chunk = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (chunk) { + assert(isFreeOop(chunk)); + assert(objOop == (fetchPointerofFreeChunk(freeChunkPrevIndex(), chunk))); + } + chunk = longAt((void *)((objOop + BaseHeaderSize) + (1U << (shiftForWord())))); + index = (bytesInBody(objOop)) / 8 /* allocationUnit */; + if ((index < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + if ((GIV(freeLists)[index]) == objOop) { + assert(chunk == 0); + } + } + else { + /* begin freeTreeNodesDo: */ + treeNode = GIV(freeLists)[0]; + if (!treeNode) { + goto l1; + } + cameFrom = -1; + do { + assert((bytesInBody(treeNode)) >= ((numFreeLists()) * (allocationUnit()))); + smallChild = longAt((void *)((treeNode + BaseHeaderSize) + (3U << (shiftForWord())))); + largeChild = longAt((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + assert((smallChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), smallChild)))); + assert((largeChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), largeChild)))); + + /* apply if the node has no children, or it has no large children and we're + returning from the small child, or we're returning from the large child. */ + if (((smallChild == 0) + && (largeChild == 0)) + || ((largeChild + ? cameFrom == largeChild + : cameFrom == smallChild))) { + if (treeNode == objOop) { + assert(chunk == 0); + } + + /* and since we've applied we must move on up */ + cameFrom = treeNode; + treeNode = longAt((void *)((treeNode + BaseHeaderSize) + (2U << (shiftForWord())))); + } + else { + if ((smallChild != 0) + && (cameFrom != smallChild)) { + treeNode = smallChild; + } + else { + assert(largeChild != 0); + treeNode = largeChild; + } + cameFrom = -1; + } + } while(treeNode != 0); + /* end freeTreeNodesDo: */ +l1:; + } + if (chunk) { + assert(isFreeOop(chunk)); + assert(objOop == (fetchPointerofFreeChunk(freeChunkNextIndex(), chunk))); + } + } + if ((bytesInBody(objOop)) >= 0x200 /* (numFreeLists * #allocationUnit) */) { + /* Tree assertions */ + chunk = longAt((void *)((objOop + BaseHeaderSize) + (2U << (shiftForWord())))); + assert((chunk == 0) + || ((isFreeOop(chunk)) + && (isLargeFreeObject(chunk)))); + chunk = longAt((void *)((objOop + BaseHeaderSize) + (3U << (shiftForWord())))); + assert((chunk == 0) + || ((isFreeOop(chunk)) + && (isLargeFreeObject(chunk)))); + chunk = longAt((void *)((objOop + BaseHeaderSize) + (4U << (shiftForWord())))); + assert((chunk == 0) + || ((isFreeOop(chunk)) + && (isLargeFreeObject(chunk)))); + } + return 1; +} + + /* SpurMemoryManager>>#become:with: */ +sqInt +becomewith(sqInt array1, sqInt array2) +{ + return becomewithtwoWaycopyHash(array1, array2, 1, 1); +} + + +/* All references to each object in array1 are swapped with all references to + the corresponding object in array2. That is, all pointers to one object + are replaced + with with pointers to the other. The arguments must be arrays of the same + length. + Answers PrimNoErr if the primitive succeeds, otherwise a relevant error + code. + */ +/* Implementation: Uses lazy forwarding to defer updating references until + message send. + */ + + /* SpurMemoryManager>>#become:with:twoWay:copyHash: */ +static NoDbgRegParms sqInt +becomewithtwoWaycopyHash(sqInt array1, sqInt array2, sqInt twoWayFlag, sqInt copyHashFlag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt contextSize; + sqInt ec; + sqInt fieldOffset; + sqInt fmt; + sqInt hash; + sqInt header; + sqInt headerSqInt; + sqInt i; + sqInt newObj1; + sqInt newObj2; + usqInt numSlots; + usqInt numSlotsUsqInt; + usqLong o1ClassIndex; + sqLong o1Header; + usqLong o2ClassIndex; + sqLong o2Header; + sqInt obj1; + sqInt obj2; + sqInt objOop; + sqInt objOopSqInt; + sqInt oop1; + sqInt oop2; + sqInt referent; + sqInt referentSqInt; + sqInt size; + sqInt sp; + sqInt toDoLimit; + sqInt top; + + assert(GIV(becomeEffectsFlags) == 0); + runLeakCheckerFor(GCModeBecome); + if (!(/* isArray: */ + ((!(array1 & (tagMask())))) + && (((byteAt((void *)(array1 + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())))) { + return PrimErrBadReceiver; + } + if (!((/* isArray: */ + ((!(array2 & (tagMask())))) + && (((byteAt((void *)(array2 + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(array1)) > (isForwardedObjectClassIndexPun())), + (((numSlotsUsqInt = byteAt((void *)(array1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) == ((/* begin numSlotsOf: */ + assert((classIndexOf(array2)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(array2 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array2 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))))) { + return PrimErrBadArgument; + } + + /* begin containsOnlyValidBecomeObjects:and:twoWay:copyHash: */ + /* begin lastPointerOf: */ + fmt = (byteAt((void *)(array1 + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt != (forwardedFormat())); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(array1))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(array1))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + fieldOffset = (((CtxtTempFrameStart - 1) + contextSize) * BytesPerOop) + BaseHeaderSize; + goto l2; + } + fieldOffset = ((((/* begin numSlotsOf: */ + assert((classIndexOf(array1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(array1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1) * BytesPerOop) + BaseHeaderSize; + goto l2; + } + if (fmt < (firstCompiledMethodFormat())) { + fieldOffset = 0; + goto l2; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(array1)); + headerSqInt = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + fieldOffset = (((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) - 1) * BytesPerOop) + BaseHeaderSize; + /* end lastPointerOf: */ +l2: + size = 0; + + /* array1 is known to be the same size as array2 */ + while (fieldOffset >= BaseHeaderSize) { + oop1 = longAt((void *)(array1 + fieldOffset)); + if (/* isOopForwarded: */ + ((!(oop1 & (tagMask())))) + && ((!((longAt((void *)(oop1))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop1)); + referent = longAt((void *)((oop1 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop1 = referent; + longAtput((void *)(array1 + fieldOffset),oop1); + } + + /* begin ifOopInvalidForBecome:errorCodeInto: */ + if (((oop1 & (tagMask())) != 0)) { + ec = PrimErrInappropriate; + goto l5; + } + if ((byteAt((void *)(oop1 + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + ec = PrimErrObjectIsPinned; + goto l5; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(oop1 + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + ec = PrimErrNoModification; + goto l5; + } + /* end ifOopInvalidForBecome:errorCodeInto: */ +l3: + oop2 = longAt((void *)(array2 + fieldOffset)); + if (/* isOopForwarded: */ + ((!(oop2 & (tagMask())))) + && ((!((longAt((void *)(oop2))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop2)); + referentSqInt = longAt((void *)((oop2 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop2 = referentSqInt; + longAtput((void *)(array2 + fieldOffset),oop2); + } + if (oop1 != oop2) { + if (twoWayFlag) { + /* begin ifOopInvalidForBecome:errorCodeInto: */ + if (((oop2 & (tagMask())) != 0)) { + ec = PrimErrInappropriate; + goto l5; + } + if ((byteAt((void *)(oop2 + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + ec = PrimErrObjectIsPinned; + goto l5; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(oop2 + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + ) { + ec = PrimErrNoModification; + goto l5; + } + /* end ifOopInvalidForBecome:errorCodeInto: */ +l4: + size = (size + (bytesInBody(oop1))) + (bytesInBody(oop2)); + GIV(becomeEffectsFlags) = (GIV(becomeEffectsFlags) | (becomeEffectFlagsFor(oop1))) | (becomeEffectFlagsFor(oop2)); + } + else { + if (copyHashFlag) { + if (((oop2 & (tagMask())) != 0)) { + ec = PrimErrInappropriate; + goto l5; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(oop2 + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + ) { + ec = PrimErrNoModification; + goto l5; + } + } + GIV(becomeEffectsFlags) = GIV(becomeEffectsFlags) | (becomeEffectFlagsFor(oop1)); + } + } + fieldOffset -= BytesPerOop; + } + if (size >= (GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart)))) { + ec = PrimErrNoMemory; + goto l5; + } + ec = 0; + /* end containsOnlyValidBecomeObjects:and:twoWay:copyHash: */ +l5: + if (ec) { + /* begin zeroBecomeEffectFlagsOnFailure */ + if (((GIV(becomeEffectsFlags) & BecameJittedCompiledMethodFlag) != 0)) { + unflagBecomeFlaggedMethods(); + } + GIV(becomeEffectsFlags) = 0; + return ec; + } + + /* begin preBecomeAction: */ + if (GIV(stackPage)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + + /* see the comment in InterpreterPrimitives>>primitiveArrayBecome */ + if (((GIV(becomeEffectsFlags) & BecameJittedCompiledMethodFlag) != 0)) { + /* begin insulateFramesAndContextsFromCogMethodsFlaggedForBecome */ + activeContext = divorceAllFramesSuchThat(isMachineCodeFrameForCogMethodFlaggedForBecome); + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + assert((GIV(framePointer) != 0) == (isStillMarriedContext(activeContext))); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + objOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, objOop, activeContext)); + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeContext & (tagMask())))) + && (oopisLessThan(activeContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),activeContext); + } + } + + /* Need to set gcMode var (to avoid passing the flag through a lot of the updating code) */ + GIV(gcMode) = GCModeBecome; + if (twoWayFlag) { + /* begin innerBecomeObjectsIn:and:copyHash: */ + toDoLimit = ((assert((classIndexOf(array1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(array1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + /* At first blush it would appear unnecessary to use followField: here since + the validation in become:with:twoWay:copyHash: follows forwarders. But + there's nothing to ensure all elements of each array are unique and don't + appear in the other array. So the enumeration could encounter an object + already becommed earlier in the same enumeration. */ + + /* begin followField:ofObject: */ + obj1 = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(obj1 & (tagMask())))) + && ((!((longAt((void *)(obj1))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + obj1 = fixFollowedFieldofObjectwithInitialValue(i, array1, obj1); + } + + /* begin followField:ofObject: */ + obj2 = longAt((void *)((array2 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(obj2 & (tagMask())))) + && ((!((longAt((void *)(obj2))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + obj2 = fixFollowedFieldofObjectwithInitialValue(i, array2, obj2); + } + if (obj1 != obj2) { + /* begin doBecome:and:copyHash: */ + o1Header = long64At((void *)(obj1)); + o1ClassIndex = (((usqInt)(obj1)) >> (identityHashFullWordShift())) & (identityHashHalfWordMask()); + if ((o1ClassIndex != 0) + && ((classAtIndex(o1ClassIndex)) != obj1)) { + o1ClassIndex = 0; + } + o2Header = long64At((void *)(obj2)); + o2ClassIndex = (((usqInt)(obj2)) >> (identityHashFullWordShift())) & (identityHashHalfWordMask()); + if ((o2ClassIndex != 0) + && ((classAtIndex(o2ClassIndex)) != obj2)) { + o2ClassIndex = 0; + } + + /* Refuse to do an in-place become on classes since their being + forwarded is used in the flush method cache implementations. + Refuse to do an in-place become on compiled code because + doing so breaks the unmapping of methods from cog methods. */ + if ((((/* begin numSlotsOf: */ + assert((classIndexOf(obj1)) > (isForwardedObjectClassIndexPun())), + (((numSlotsUsqInt = byteAt((void *)(obj1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) == ((/* begin numSlotsOf: */ + assert((classIndexOf(obj2)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(obj2 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj2 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))) + && (((o1ClassIndex + o2ClassIndex) == 0) + && ((!(isCompiledMethodFormat((((usqLong)(o1Header)) >> (formatShift())) & (formatMask())))) + && (!(isCompiledMethodFormat((((usqLong)(o2Header)) >> (formatShift())) & (formatMask()))))))) { + inPlaceBecomeandcopyHashFlag(obj1, obj2, copyHashFlag); + goto l6; + } + outOfPlaceBecomeandcopyHashFlag(obj1, obj2, copyHashFlag); + + /* if copyHashFlag then nothing changes, since hashes were also swapped. */ + if (copyHashFlag) { + goto l6; + } + + /* if copyHash is false then the classTable entries must be updated. + We leave the following until postBecomeScanClassTable:, but must + swap the forwarders if two active classes have been becommed, + and assign hashes if not. */ + if (o1ClassIndex) { + if (o2ClassIndex) { + classAtIndexput(o1ClassIndex, obj2); + classAtIndexput(o2ClassIndex, obj1); + } + else { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj1)); + newObj2 = longAt((void *)((obj1 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(newObj2 & (tagMask())))) + && ((!((longAt((void *)(newObj2))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + newObj2 = longAt((void *)((newObj2 + BaseHeaderSize) + (0U << (shiftForWord())))); + } + assert((rawHashBitsOf(newObj2)) == 0); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(newObj2 + 4),((((long32At((void *)(newObj2 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (o1ClassIndex & (identityHashHalfWordMask()))); + } + } + else { + if (o2ClassIndex) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj2)); + newObj1 = longAt((void *)((obj2 + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(newObj1 & (tagMask())))) + && ((!((longAt((void *)(newObj1))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + newObj1 = longAt((void *)((newObj1 + BaseHeaderSize) + (0U << (shiftForWord())))); + } + assert((rawHashBitsOf(newObj1)) == 0); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(newObj1 + 4),((((long32At((void *)(newObj1 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (o2ClassIndex & (identityHashHalfWordMask()))); + } + } + /* end doBecome:and:copyHash: */ +l6: + objOop = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(i, array1, objOop); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((array2 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(i, array2, objOopSqInt); + } + } + } + } + else { + /* begin innerBecomeObjectsIn:to:copyHash: */ + toDoLimit = ((assert((classIndexOf(array1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(array1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + /* At first blush it would appear unnecessary to use followField: here since + the validation in become:with:twoWay:copyHash: follows forwarders. But + there's nothing to ensure all elements of each array is unique and doesn't + appear in the other array. So the enumeration could encounter an object + already becommed earlier in the same enumeration. */ + + /* begin followField:ofObject: */ + obj1 = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(obj1 & (tagMask())))) + && ((!((longAt((void *)(obj1))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + obj1 = fixFollowedFieldofObjectwithInitialValue(i, array1, obj1); + } + + /* begin followField:ofObject: */ + obj2 = longAt((void *)((array2 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(obj2 & (tagMask())))) + && ((!((longAt((void *)(obj2))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + obj2 = fixFollowedFieldofObjectwithInitialValue(i, array2, obj2); + } + if (obj1 != obj2) { + /* begin doBecome:to:copyHash: */ + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(obj1),((longAt((void *)(obj1))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(obj1),(longAt((void *)(obj1))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(obj1)); + assert(!(isOopForwarded(obj2))); + + /* begin isOldObject: */ + assert(isNonImmediate(obj1)); + if (oopisGreaterThanOrEqualTo(obj1, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj2 & (tagMask())))) + && (oopisLessThan(obj2, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj1 + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj1); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj1 + BaseHeaderSize) + (0U << (shiftForWord()))),obj2); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(obj1 + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(obj1 + (numSlotsFieldByteOffset())),1); + } + if (copyHashFlag) { + hash = (long32At((void *)(obj1 + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(obj2 + 4),((((long32At((void *)(obj2 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + } + if (((/* begin isOldObject: */ + assert(isNonImmediate(obj1)), + oopisGreaterThanOrEqualTo(obj1, GIV(oldSpaceStart)))) + && (/* isYoung: */ + ((!(obj2 & (tagMask())))) + && (oopisLessThan(obj2, GIV(oldSpaceStart))))) { + GIV(becomeEffectsFlags) = GIV(becomeEffectsFlags) | OldBecameNewFlag; + } + assert(!((isOopForwarded(obj2)))); + + /* begin followField:ofObject: */ + objOop = longAt((void *)((array1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(i, array1, objOop); + } + assert(!(isOopForwarded(obj2))); + } + } + } + + /* begin followSpecialObjectsOop */ + if ((!((longAt((void *)(GIV(specialObjectsOop)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + GIV(validatedIntegerClassFlags) = 0; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(GIV(specialObjectsOop))); + referent = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + GIV(specialObjectsOop) = referent; + } + followForwardedObjectFieldstoDepth(GIV(specialObjectsOop), 0); + + /* N.B. perform coInterpreter's postBecomeAction: *before* postBecomeScanClassTable: + to allow the coInterpreter to void method cache entries by spotting classIndices that + refer to forwarded objects. postBecomeScanClassTable: follows forwarders in the table. */ + + /* begin postBecomeAction: */ + spurPostBecomeAction(GIV(becomeEffectsFlags)); + if (((GIV(becomeEffectsFlags) & BecameJittedCompiledMethodFlag) != 0)) { + freeBecomeFlaggedMethods(); + } + followMovableLiteralsAndUpdateYoungReferrers(); + + /* begin nilUncoggableMethods */ + GIV(lastCoggableInterpretedBlockMethod) = (GIV(lastUncoggableInterpretedBlockMethod) = null); + assert(kosherYoungReferrers()); + GIV(gcMode) = 0; + postBecomeScanClassTable(GIV(becomeEffectsFlags)); + if (twoWayFlag) { + assert(validPostBecomeArrayContents(array1)); + assert(validPostBecomeArrayContents(array2)); + } + else { + assert(validPostBecomeArrayContents(array2)); + } + assert(validClassTableHashes()); + runLeakCheckerFor(GCModeBecome); + + /* begin zeroBecomeEffectFlagsOnSuccess */ + GIV(becomeEffectsFlags) = 0; + return PrimNoErr; +} + + +/* If this object is old, mark it as a root (because a new object + may be stored into it). */ + + /* SpurMemoryManager>>#beRootIfOld: */ +void +beRootIfOld(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin isOldObject: */ + assert(isNonImmediate(oop)); + if (oopisGreaterThanOrEqualTo(oop, GIV(oldSpaceStart))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(oop); + } + } +} + + /* SpurMemoryManager>>#bitsSetInFreeSpaceMaskForAllFreeLists */ +static sqInt +bitsSetInFreeSpaceMaskForAllFreeLists(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + if (((GIV(freeLists)[i]) != 0) + && ((!((1ULL << i) & GIV(freeListsMask))))) { + return 0; + } + } + return 1; +} + + /* SpurMemoryManager>>#bridgeSize */ +static sqInt +bridgeSize(void) +{ + return 2 * BaseHeaderSize; +} + + /* SpurMemoryManager>>#byteFormatForNumBytes: */ +static NoDbgRegParms sqInt +byteFormatForNumBytes(sqInt numBytes) +{ + return (firstByteFormat()) + ((8 - numBytes) & (BytesPerWord - 1)); +} + + /* SpurMemoryManager>>#byteFormatMask */ +static sqInt +byteFormatMask(void) +{ + return 24; +} + + /* SpurMemoryManager>>#byteSizeOf: */ +sqInt +byteSizeOf(sqInt oop) +{ + sqInt fmt; + usqInt numBytes; + usqInt numSlots; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin numBytesOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + return numBytes - (fmt & 7); + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + return numBytes; + } + if (fmt >= (firstShortFormat())) { + return numBytes - (((fmt & 3) << 1)); + } + + /* fmt >= self firstLongFormat */ + return numBytes - (((fmt & 1) << 2)); +} + + +/* Answer the basic element size for the receiver. Answer 0 for immediates + and CompiledCode + (element size could be wordSize for literals or 1 for bytes, so its + indeterminable). Answer + wordSize for pointer objects. Otherwise answer the actual element size of + a bits container. */ + + /* SpurMemoryManager>>#bytesPerElement: */ +unsigned char +bytesPerElement(sqInt oop) +{ + unsigned char byteSizes[16] = + { wordSize(), wordSize(), wordSize(), wordSize(), + wordSize(), wordSize(), wordSize(), 0, /* forwarder */ wordSize(), + 8, + 4, 4, + 2, 2, 2, 2 }; + sqInt fmt; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + return 0; + } + return 1; + } + return byteSizes[fmt]; +} + + +/* Attempt to change the class of the receiver to the argument given that the + format of the receiver matches the format of the argument. If successful, + answer 0, otherwise answer an error code indicating the reason for + failure. + Fail if the format of the receiver is incompatible with the format of the + argument, or if the argument is a fixed class and the receiver's size + differs from the size + that an instance of the argument should have. */ + + /* SpurMemoryManager>>#changeClassOf:to: */ +static NoDbgRegParms sqInt +changeClassOfto(sqInt rcvr, sqInt argClass) +{ + sqInt classFormat; + sqInt classIndex; + sqInt fixedFields; + sqInt fmt; + sqInt instBytes; + sqInt instFormat; + usqInt instSlots; + sqInt newFormat; + usqInt numBytes; + usqInt numSlots; + + instBytes = 0; + instSlots = 0; + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + return PrimErrNoModification; + } + classFormat = ((longAt((void *)((argClass + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + fixedFields = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + classFormat = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + instFormat = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + + /* Fail for inability to access classIndex before making contexts snapshot-safe. */ + if (((classIndex = ensureBehaviorHash(argClass))) < 0) { + return -classIndex; + } + + /* Now check the instance for compatibility and compute odd bits if necessary. */ + if (classFormat <= 5 /* lastPointerFormat */) { + if (instFormat > 5 /* lastPointerFormat */) { + return PrimErrInappropriate; + } + if ((((/* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? (instSlots = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8) + : (instSlots = numSlots)))) < fixedFields) + || ((instSlots > fixedFields) + && (/* isFixedSizePointerFormat: */ + (classFormat <= (nonIndexablePointerFormat())) + || (classFormat == (ephemeronFormat()))))) { + return PrimErrBadReceiver; + } + if ((instFormat == (indexablePointersFormat())) + && (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + makeContextSnapshotSafe(rcvr); + } + newFormat = classFormat; + } + else { + /* Fail if the class's format is somehow invalid. */ + if (classFormat != (classFormatFromInstFormat(classFormat))) { + return PrimErrBadArgument; + } + + /* begin numBytesOf: */ + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + instBytes = numBytes - (fmt & 7); + goto l1; + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + instBytes = numBytes; + goto l1; + } + if (fmt >= (firstShortFormat())) { + instBytes = numBytes - (((fmt & 3) << 1)); + goto l1; + } + + /* fmt >= self firstLongFormat */ + instBytes = numBytes - (((fmt & 1) << 2)); + /* end numBytesOf: */ +l1: + switch (classFormat) { + case sixtyFourBitIndexableFormat(): + if (((instBytes & 7) != 0)) { + return PrimErrBadReceiver; + } + newFormat = classFormat; + break; + case firstLongFormat(): + if (((instBytes & 3) != 0)) { + return PrimErrBadReceiver; + } + newFormat = classFormat + ((2 - (((usqInt)(instBytes)) >> 2)) & 1); + break; + case firstShortFormat(): + if (((instBytes & 1) != 0)) { + return PrimErrBadReceiver; + } + newFormat = classFormat + ((4 - (((usqInt)(instBytes)) >> 1)) & 3); + break; + case firstByteFormat(): + newFormat = classFormat + ((8 - instBytes) & 7); + break; + case firstCompiledMethodFormat(): + if (classFormat != (firstCompiledMethodFormat())) { + return PrimErrInappropriate; + } + newFormat = instFormat; + break; + default: + return PrimErrInappropriate; + } + } + + /* begin set:classIndexTo:formatTo: */ + assert(((classIndex >= 0) && (classIndex <= (classIndexMask())))); + assert(((newFormat >= 0) && (newFormat <= (formatMask())))); + longAtput((void *)(rcvr),((longAt((void *)(rcvr))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + (classIndex + ((((usqInt)(newFormat) << (formatShift())))))); + assert((numBytesOf(rcvr)) == ((classFormat <= (lastPointerFormat()) + ? instSlots * BytesPerOop + : instBytes))); + + /* ok */ + return 0; +} + + /* SpurMemoryManager>>#characterObjectOf: */ +sqInt +characterObjectOf(sqInt characterCode) +{ + return ((((usqInt)(characterCode) << (numTagBits())))) + (characterTag()); +} + + +/* Immediate characters are unsigned */ + + /* SpurMemoryManager>>#characterValueOf: */ +usqInt +characterValueOf(sqInt oop) +{ + return ((((usqInt)oop))) >> (numTagBits()); +} + + /* SpurMemoryManager>>#cheapAddressCouldBeInHeap: */ +static NoDbgRegParms sqInt +cheapAddressCouldBeInHeap(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((address & (BytesPerWord - 1)) == 0) + && ((oopisGreaterThanOrEqualTo(address, GIV(newSpaceStart))) + && (oopisLessThan(address, GIV(endOfMemory)))); +} + + +/* Assumes zero-based array indexing. */ + + /* SpurMemoryManager>>#checkedLongAt: */ +sqInt +checkedLongAt(sqInt byteAddress) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!(isInMemory(byteAddress))) { + warning("checkedLongAt bad address"); + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + return longAt((void *)(byteAddress)); +} + + +/* Perform an integrity/leak check using the heapMap. Assume + clearLeakMapAndMapAccessibleFreeSpace has set a bit at each free chunk's + header. Scan all objects in the heap checking that no pointer points + to a free chunk and that all free chunks that refer to others refer to + marked chunks. Answer if all checks pass. */ + + /* SpurMemoryManager>>#checkHeapFreeSpaceIntegrity */ +static sqInt +checkHeapFreeSpaceIntegrity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt chunkBytes; + sqInt fi; + sqInt fieldOop; + sqInt followingWord; + usqInt followingWordAddress; + sqInt i; + usqInt numSlots; + sqInt objOop; + sqInt ok; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + sqInt toDoLimit; + sqInt toDoLimit1; + usqInt total; + + ok = 1; + total = 0; + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + if (GIV(freeLists)[i]) { + if (!(heapMapAtWord(pointerForOop(GIV(freeLists)[i])))) { + fprintf(GIV(transcript), + "leak in free list %d to non-free %p\n", + ((int) i), + ((void *)(GIV(freeLists)[i]))); + ok = 0; + } + } + } + + /* Excuse the duplication but performance is at a premium and we avoid + some tests by splitting the newSpace and oldSpace enumerations. */ + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l2; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + fprintf(GIV(transcript), + "young object %p is free\n", + ((void *)objOop)); + ok = 0; + } + else { + if (objOop != GIV(freeSpaceCheckOopToIgnore)) { + toDoLimit = (numPointerSlotsOf(objOop)) - 1; + for (fi = 0; fi <= toDoLimit; fi += 1) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + if ((!(fieldOop & (tagMask())))) { + if (heapMapAtWord(pointerForOop(fieldOop))) { + fprintf(GIV(transcript), + "object leak in %p @ %d = %p is free\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + /* end allNewSpaceEntitiesDo: */ +l2: + + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + if (!(heapMapAtWord(pointerForOop(objOop)))) { + fprintf(GIV(transcript), + "leak in free chunk %p is unmapped?! \n", + ((void *)objOop)); + ok = 0; + } + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) == 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ 0 = %p is unmapped\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + chunkBytes = bytesInBody(objOop); + + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + if (!(chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */))) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (1U << (shiftForWord())))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) == 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ 0 = %p is unmapped\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + if ((bytesInBody(objOop)) >= 0x200 /* (numFreeLists * #allocationUnit) */) { + for (fi = 2 /* freeChunkParentIndex */; fi <= 4 /* freeChunkLargerIndex */; fi += 1) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) == 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ %d = %p is unmapped\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + } + total += bytesInBody(objOop); + } + else { + if (objOop != GIV(freeSpaceCheckOopToIgnore)) { + toDoLimit1 = (numPointerSlotsOf(objOop)) - 1; + for (fi = 0; fi <= toDoLimit1; fi += 1) { + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + assert(fi == 0); + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + } + else { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + } + + /* We keep #fetchPointer:ofObject: API here for assertions */ + if ((!(fieldOop & (tagMask())))) { + if (heapMapAtWord(pointerForOop(fieldOop))) { + fprintf(GIV(transcript), + "object leak in %p @ %d = %p is free\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3:; + } + if (total != GIV(totalFreeOldSpace)) { + fprintf(GIV(transcript), + "incorrect totalFreeOldSpace; expected %d found %d\n", + ((int) GIV(totalFreeOldSpace)), + ((int) total)); + ok = 0; + } + return ok; +} + + +/* Perform an integrity/leak check using the heapMap. Assume + clearLeakMapAndMapAccessibleObjects has set a bit at each (non-free) + object's header. Scan all objects in the heap checking that every + pointer points to a header. Scan the rememberedSet, remapBuffer and + extraRootTable checking + that every entry is a pointer to a header. Check that the number of roots + is correct and that all + rememberedSet entries have their isRemembered: flag set. Answer if all + checks pass. */ + + /* SpurMemoryManager>>#checkHeapIntegrity:classIndicesShouldBeValid: */ +static NoDbgRegParms sqInt +checkHeapIntegrityclassIndicesShouldBeValid(sqInt excludeUnmarkedObjs, sqInt classIndicesShouldBeValid) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt chunkBytes; + sqInt classIndex; + sqInt classIndexSqInt; + sqInt classOop; + sqInt classTablePage; + sqInt containsYoung; + sqInt fi; + sqInt fieldIndex; + sqInt fieldOop; + sqInt followingWord; + usqInt followingWordAddress; + sqInt freeListOop; + sqInt i; + sqInt iSqInt; + sqInt numOnThisPage; + sqInt numRememberedObjectsInHeap; + usqInt numSlots; + sqInt obj; + sqInt objOop; + sqInt objStackPage; + sqInt ok; + sqInt prevObj; + sqInt prevPrevObj; + sqInt ri; + usqInt start; + sqInt toDoLimit; + sqInt toDoLimit1; + + ok = 1; + numRememberedObjectsInHeap = 0; + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + if ((freeListOop = GIV(freeLists)[i])) { + if (oopisGreaterThanOrEqualToandLessThan(freeListOop, GIV(oldSpaceStart), GIV(endOfMemory))) { + if (heapMapAtWord(pointerForOop(freeListOop))) { + fprintf(GIV(transcript), + "leak in free list %d to non-free %p\n", + ((int) i), + ((void *)freeListOop)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "leak in free list %d to out-of-bounds %p\n", + ((int) i), + ((void *)freeListOop)); + ok = 0; + } + } + } + + /* Excuse the duplication but performance is at a premium and we avoid + some tests by splitting the newSpace and oldSpace enumerations. */ + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l6; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + fprintf(GIV(transcript), + "young object %p is free\n", + ((void *)objOop)); + ok = 0; + } + else { + if (!((!(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) + && (excludeUnmarkedObjs))) { + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) { + fprintf(GIV(transcript), + "young object %p is remembered\n", + ((void *)objOop)); + ok = 0; + } + } + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isInHeapBounds: */ + (oopisGreaterThanOrEqualTo(fieldOop, (!(minCogMethodAddress()) + ? GIV(nilObj) + : minCogMethodAddress()))) + && (oopisLessThan(fieldOop, GIV(endOfMemory)))) { + if (!(heapMapAtWord(pointerForOop(fieldOop)))) { + fprintf(GIV(transcript), + "object leak in forwarder %p to unmapped %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "object leak in forwarder %p to out-of-bounds %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + else { + classIndexSqInt = (classIndex = (longAt((void *)(objOop))) & (classIndexMask())); + + /* begin classOrNilAtIndex: */ + assert((classIndexSqInt <= (tagMask())) + || (classIndexSqInt >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndexSqInt)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classOop = GIV(nilObj); + goto l1; + } + fieldIndex = classIndexSqInt & ((1U << (classTableMajorIndexShift())) - 1); + classOop = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classOrNilAtIndex: */ +l1: + if (classIndicesShouldBeValid + && ((classOop == GIV(nilObj)) + && (!(((longAt((void *)(objOop))) & (classIndexMask())) <= (lastClassIndexPun()))))) { + fprintf(GIV(transcript), + "object leak in %p invalid class index %p -> nilObj\n", + ((void *)objOop), + ((void *)classIndex)); + ok = 0; + } + toDoLimit = (numPointerSlotsOf(objOop)) - 1; + for (fi = 0; fi <= toDoLimit; fi += 1) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + if ((!(fieldOop & (tagMask())))) { + if (/* isInHeapBounds: */ + (oopisGreaterThanOrEqualTo(fieldOop, (!(minCogMethodAddress()) + ? GIV(nilObj) + : minCogMethodAddress()))) + && (oopisLessThan(fieldOop, GIV(endOfMemory)))) { + if (!(heapMapAtWord(pointerForOop(fieldOop)))) { + fprintf(GIV(transcript), + "object leak in %p @ %d = %p\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "object leak in %p to out-of-bounds %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l5; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l5:; + } + /* end allNewSpaceEntitiesDo: */ +l6: + + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + if (heapMapAtWord(pointerForOop(objOop))) { + fprintf(GIV(transcript), + "leak in free chunk %p is mapped?! \n", + ((void *)objOop)); + ok = 0; + } + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) != 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ 0 = %p is mapped\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + chunkBytes = bytesInBody(objOop); + + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + if (!(chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */))) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (1U << (shiftForWord())))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) != 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ 1 = %p is mapped\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + if ((bytesInBody(objOop)) >= 0x200 /* (numFreeLists * #allocationUnit) */) { + for (fi = 2 /* freeChunkParentIndex */; fi <= 4 /* freeChunkLargerIndex */; fi += 1) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + if ((fieldOop != 0) + && ((heapMapAtWord(pointerForOop(fieldOop))) != 0)) { + fprintf(GIV(transcript), + "leak in free chunk %p @ %d = %p is mapped\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + } + } + else { + if (!(excludeUnmarkedObjs + && (!(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)))) { + containsYoung = 0; + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) { + numRememberedObjectsInHeap += 1; + + /* begin isInRememberedSet: */ + for (iSqInt = 0; iSqInt < GIV(rememberedSetSize); iSqInt += 1) { + if ((GIV(rememberedSet)[iSqInt]) == objOop) { + goto l2; + } + } + fprintf(GIV(transcript), + "remembered object %p is not in remembered table\n", + ((void *)objOop)); + ok = 0; +l2:; + } + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isInHeapBounds: */ + (oopisGreaterThanOrEqualTo(fieldOop, (!(minCogMethodAddress()) + ? GIV(nilObj) + : minCogMethodAddress()))) + && (oopisLessThan(fieldOop, GIV(endOfMemory)))) { + if (!(heapMapAtWord(pointerForOop(fieldOop)))) { + fprintf(GIV(transcript), + "object leak in forwarder %p to unmapped %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "object leak in forwarder %p to out-of-bounds %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + if (/* isReallyYoung: */ + ((!(fieldOop & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(fieldOop)), + (oopisLessThan(fieldOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(fieldOop, GIV(newSpaceStart)))))) { + containsYoung = 1; + } + } + else { + classIndexSqInt = (classIndex = (longAt((void *)(objOop))) & (classIndexMask())); + + /* begin classOrNilAtIndex: */ + assert((classIndexSqInt <= (tagMask())) + || (classIndexSqInt >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndexSqInt)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classOop = GIV(nilObj); + goto l3; + } + fieldIndex = classIndexSqInt & ((1U << (classTableMajorIndexShift())) - 1); + classOop = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classOrNilAtIndex: */ +l3: + if (classIndicesShouldBeValid + && ((classOop == GIV(nilObj)) + && (classIndex > (lastClassIndexPun())))) { + fprintf(GIV(transcript), + "object leak in %p invalid class index %p -> nilObj\n", + ((void *)objOop), + ((void *)classIndex)); + ok = 0; + } + toDoLimit1 = (numPointerSlotsOf(objOop)) - 1; + for (fi = 0; fi <= toDoLimit1; fi += 1) { + fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fi) << (shiftForWord())))))); + if ((!(fieldOop & (tagMask())))) { + if (/* isInHeapBounds: */ + (oopisGreaterThanOrEqualTo(fieldOop, (!(minCogMethodAddress()) + ? GIV(nilObj) + : minCogMethodAddress()))) + && (oopisLessThan(fieldOop, GIV(endOfMemory)))) { + if (!(heapMapAtWord(pointerForOop(fieldOop)))) { + fprintf(GIV(transcript), + "object leak in %p @ %d = %p\n", + ((void *)objOop), + ((int) fi), + ((void *)fieldOop)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "object leak in %p to out-of-bounds %p\n", + ((void *)objOop), + ((void *)fieldOop)); + ok = 0; + } + + /* don't be misled by CogMethods; they appear to be young, but they're not */ + if (/* isReallyYoung: */ + ((!(fieldOop & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(fieldOop)), + (oopisLessThan(fieldOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(fieldOop, GIV(newSpaceStart)))))) { + containsYoung = 1; + } + } + } + } + if (containsYoung) { + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + fprintf(GIV(transcript), + "unremembered object %p contains young oop(s)\n", + ((void *)objOop)); + ok = 0; + } + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l7; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l7:; + } + if (numRememberedObjectsInHeap != GIV(rememberedSetSize)) { + fprintf(GIV(transcript), + "root count mismatch. #heap roots %d; #roots %d\n", + ((int) numRememberedObjectsInHeap), + ((int) GIV(rememberedSetSize))); + + /* But the system copes with overflow... */ + } + + /* begin rememberedSetWithIndexDo: */ + for (iSqInt = 0; iSqInt < GIV(rememberedSetSize); iSqInt += 1) { + obj = GIV(rememberedSet)[iSqInt]; + if (obj & (BytesPerWord - 1)) { + fprintf(GIV(transcript), + "misaligned oop in remembered set @ %d = %p\n", + ((int) iSqInt), + ((void *)obj)); + ok = 0; + } + else { + if (heapMapAtWord(pointerForOop(obj))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + fprintf(GIV(transcript), + "non-root in remembered set @ %d = %p\n", + ((int) iSqInt), + ((void *)obj)); + ok = 0; + } + } + else { + fprintf(GIV(transcript), + "object leak in remembered set @ %d = %p\n", + ((int) iSqInt), + ((void *)obj)); + ok = 0; + } + } + } + + /* begin objStack:do: */ + if (GIV(mournQueue) == GIV(nilObj)) { + goto l4; + } + eassert(isValidObjStack(GIV(mournQueue))); + objStackPage = GIV(mournQueue); + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + obj = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (obj & (BytesPerWord - 1)) { + fprintf(GIV(transcript), + "misaligned oop in mournQueue @ %d in %p = %p\n", + ((int) iSqInt), + ((void *)objStackPage), + ((void *)obj)); + ok = 0; + } + else { + if (!(excludeUnmarkedObjs + && (!(((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)))) { + if (!(heapMapAtWord(pointerForOop(obj)))) { + fprintf(GIV(transcript), + "object leak in mournQueue @ %d in %p = %p\n", + ((int) iSqInt), + ((void *)objStackPage), + ((void *)obj)); + ok = 0; + } + } + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l4: + for (ri = 1; ri <= GIV(remapBufferCount); ri += 1) { + obj = GIV(remapBuffer)[ri]; + if (obj & (BytesPerWord - 1)) { + fprintf(GIV(transcript), + "misaligned remapRoot @ %d = %p\n", + ((int) ri), + ((void *)obj)); + ok = 0; + } + else { + if (!(heapMapAtWord(pointerForOop(obj)))) { + fprintf(GIV(transcript), + "object leak in remapRoots @ %d = %p\n", + ((int) ri), + ((void *)obj)); + ok = 0; + } + } + } + for (ri = 1; ri <= GIV(extraRootCount); ri += 1) { + obj = (GIV(extraRoots)[ri])[0]; + if (obj & (BytesPerWord - 1)) { + fprintf(GIV(transcript), + "misaligned extraRoot @ %d => %p\n", + ((int) ri), + ((void *)obj)); + ok = 0; + } + else { + if (!(heapMapAtWord(pointerForOop(obj)))) { + fprintf(GIV(transcript), + "object leak in extraRoots @ %d => %p\n", + ((int) ri), + ((void *)obj)); + ok = 0; + } + } + } + return ok; +} + + +/* Verify that the given oop is legitimate. Check address, header, and size + but not class. + Answer true if OK. Otherwise print reason and answer false. */ +/* useful for debugging */ + + /* SpurMemoryManager>>#checkOkayOop: */ +sqInt +checkOkayOop(usqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt fmt; + usqLong unusedBits; + sqInt unusedBitsInYoungObjects; + + + /* address and size checks */ + if (((oop & (tagMask())) != 0)) { + return 1; + } + if (!(addressCouldBeObj(oop))) { + fprintf(GIV(transcript), + "oop %p is not a valid address", + ((void *)oop)); + return 0; + } + if (!(oopisLessThanOrEqualTo(addressAfter(oop), GIV(endOfMemory)))) { + fprintf(GIV(transcript), + "oop %p size would make it extend beyond the end of memory", + ((void *)oop)); + return 0; + } + + /* header type checks */ + if (!(((classIndex = (longAt((void *)(oop))) & (classIndexMask()))) >= (firstClassIndexPun()))) { + fprintf(GIV(transcript), + "oop %p is a free chunk, or bridge, not an object", + ((void *)oop)); + return 0; + } + if (((byteAt((void *)(oop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) + && ((byteAt((void *)((oop - BaseHeaderSize) + (numSlotsFieldByteOffset())))) != (numSlotsMask()))) { + fprintf(GIV(transcript), + "oop %p header has overflow header word, but overflow word does not have a saturated numSlots field", + ((void *)oop)); + return 0; + } + + /* format check */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if ((fmt == 6) || (fmt == 8)) { + fprintf(GIV(transcript), + "oop %p has an unknown format type", + ((void *)oop)); + return 0; + } + if ((fmt == (forwardedFormat())) != (classIndex == (isForwardedObjectClassIndexPun()))) { + fprintf(GIV(transcript), + "oop %p has mis-matched format/classIndex fields; only one of them is the isForwarded value", + ((void *)oop)); + return 0; + } + + /* specific header bit checks */ + unusedBits = (0x400000) | (0x40000000000000LL); + if ((long64At((void *)(oop))) & unusedBits) { + fprintf(GIV(transcript), + "oop %p has some unused header bits set; should be zero", + ((void *)oop)); + return 0; + } + unusedBitsInYoungObjects = (1U << (unusedBitShift())) | ((1U << (pinnedBitShift())) | (1U << (rememberedBitShift()))); + if ((longAt((void *)(oop))) & unusedBitsInYoungObjects) { + fprintf(GIV(transcript), + "oop %p has some header bits unused in young objects set; should be zero", + ((void *)oop)); + return 0; + } + return 1; +} + + +/* Attempt to verify that the given obj has a reasonable behavior. The class + must be a + valid, non-integer oop and must not be nilObj. It must be a pointers + object with three + or more fields. Finally, the instance specification field of the behavior + must match that + of the instance. If OK answer true. If not, print reason and answer false. */ +/* useful for debugging */ + + /* SpurMemoryManager>>#checkOopHasOkayClass: */ +sqInt +checkOopHasOkayClass(usqInt obj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + usqInt objClass; + sqInt objFormat; + + if (!(checkOkayOop(obj))) { + return 0; + } + objClass = ((usqInt) (fetchClassOfNonImm(obj))); + if (((objClass & (tagMask())) != 0)) { + fprintf(GIV(transcript), + "obj %p an immediate is not a valid class or behavior\n", + ((void *)obj)); + return 0; + } + if (!(okayOop(objClass))) { + fprintf(GIV(transcript), + "obj %p class obj is not ok\n", + ((void *)obj)); + return 0; + } + if (!((((byteAt((void *)(objClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(objClass)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(objClass + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objClass - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= 3))) { + fprintf(GIV(transcript), + "obj %p a class (behavior) must be a pointers object of size >= 3\n", + ((void *)obj)); + return 0; + } + objFormat = (/* isBytes: */ + ((!(obj & (tagMask())))) + && (((byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())) + ? ((((byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask())) | 7) - 7) + : (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask())); + if (((((usqInt)((((longAt((void *)((objClass + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) != objFormat) { + fprintf(GIV(transcript), + "obj %p and its class (behavior) formats differ\n", + ((void *)obj)); + return 0; + } + return 1; +} + + /* SpurMemoryManager>>#checkOopIntegrity:named: */ +static NoDbgRegParms sqInt +checkOopIntegritynamed(sqInt obj, char *name) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((oopisLessThan(obj, GIV(endOfMemory))) + && ((heapMapAtWord(pointerForOop(obj))) != 0)) { + return 1; + } + fprintf(GIV(transcript), + "%s leak %p\n", + name, + ((void *)obj)); + return 0; +} + + /* SpurMemoryManager>>#checkOopIntegrity:named:index: */ +static NoDbgRegParms sqInt +checkOopIntegritynamedindex(sqInt obj, char *name, sqInt i) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((oopisLessThan(obj, GIV(endOfMemory))) + && ((heapMapAtWord(pointerForOop(obj))) != 0)) { + return 1; + } + fprintf(GIV(transcript), + "%s leak @ %d = %p\n", + name, + ((int) i), + ((void *)obj)); + return 0; +} + + /* SpurMemoryManager>>#classAlien */ +sqInt +classAlien(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classArray */ +sqInt +classArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classAtIndex: */ +sqInt +classAtIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt fieldIndex; + + assert((classIndex >= 0) + && ((classIndex <= (tagMask())) + || ((classIndex >= (arrayClassIndexPun())) + && (classIndex <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return null; + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + +/* for become & GC of classes */ + + /* SpurMemoryManager>>#classAtIndex:put: */ +static NoDbgRegParms sqInt +classAtIndexput(sqInt classIndex, sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt fieldIndex; + + assert((classIndex <= (tagMask())) + || (classIndex >= (arrayClassIndexPun()))); + assert((objOop == GIV(nilObj)) + || ((((rawHashBitsOf(objOop)) == classIndex) + || ((classAtIndex(rawHashBitsOf(objOop))) == objOop)) + && (objCouldBeClassObj(objOop)))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + error("attempt to add class to empty page"); + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, classTablePage, objOop)); + assert(isNonImmediate(classTablePage)); + if (oopisGreaterThanOrEqualTo(classTablePage, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objOop & (tagMask())))) + && (oopisLessThan(objOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(classTablePage + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(classTablePage); + } + } + } + + /* most stores into young objects */ + return longAtput((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),objOop); +} + + /* SpurMemoryManager>>#classBitmap */ +sqInt +classBitmap(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassBitmap) << (shiftForWord())))))); +} + + +/* a.k.a. self fetchPointer: ClassByteArrayCompactIndex ofObject: + classTableFirstPage + */ + + /* SpurMemoryManager>>#classByteArray */ +sqInt +classByteArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteArray) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classCharacter */ +sqInt +classCharacter(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassCharacter) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classDoubleByteArray */ +sqInt +classDoubleByteArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOop; + int hash; + + /* begin accessIntegerClass:withValidationFlag: */ + hash = 0; + classOop = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassDoubleByteArray) << (shiftForWord())))))); + if (((GIV(validatedIntegerClassFlags) & ValidatedClassDoubleByteArrayFlag) != 0)) { + return classOop; + } + if ((((classOop & (tagMask())) != 0)) + || ((((hash = (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask()))) == 0) + || ((classAtIndex(hash)) != classOop))) { + return GIV(nilObj); + } + GIV(validatedIntegerClassFlags) = GIV(validatedIntegerClassFlags) | ValidatedClassDoubleByteArrayFlag; + return classOop; +} + + /* SpurMemoryManager>>#classDoubleWordArray */ +sqInt +classDoubleWordArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOop; + int hash; + + /* begin accessIntegerClass:withValidationFlag: */ + hash = 0; + classOop = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassDoubleWordArray) << (shiftForWord())))))); + if (((GIV(validatedIntegerClassFlags) & ValidatedClassDoubleWordArrayFlag) != 0)) { + return classOop; + } + if ((((classOop & (tagMask())) != 0)) + || ((((hash = (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask()))) == 0) + || ((classAtIndex(hash)) != classOop))) { + return GIV(nilObj); + } + GIV(validatedIntegerClassFlags) = GIV(validatedIntegerClassFlags) | ValidatedClassDoubleWordArrayFlag; + return classOop; +} + + /* SpurMemoryManager>>#classExternalAddress */ +sqInt +classExternalAddress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassExternalAddress) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classExternalData */ +sqInt +classExternalData(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassExternalData) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classExternalFunction */ +sqInt +classExternalFunction(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassExternalFunction) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classExternalLibrary */ +sqInt +classExternalLibrary(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassExternalLibrary) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classExternalStructure */ +sqInt +classExternalStructure(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassExternalStructure) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classFloat */ +sqInt +classFloat(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassFloat) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classFloat32Array */ +sqInt +classFloat32Array(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(nilObj); +} + + /* SpurMemoryManager>>#classFloat64Array */ +sqInt +classFloat64Array(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(nilObj); +} + + +/* No need to check this; classAtIndex: has a stricter assert: + self assert: classIndex ~= self isForwardedObjectClassIndexPun. */ + + /* SpurMemoryManager>>#classForClassTag: */ +static NoDbgRegParms sqInt +classForClassTag(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt fieldIndex; + + /* begin classAtIndex: */ + assert((classIndex >= 0) + && ((classIndex <= (tagMask())) + || ((classIndex >= (arrayClassIndexPun())) + && (classIndex <= (classIndexMask()))))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return null; + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + +/* 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState + et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable + 12 - 15 16-bit indexable + 16 - 23 byte indexable + 24 - 31 compiled method */ + + /* SpurMemoryManager>>#classFormatFromInstFormat: */ +static NoDbgRegParms sqInt +classFormatFromInstFormat(sqInt instFormat) +{ + if (instFormat >= (firstByteFormat())) { + return instFormat & -8; + } + + /* this is likely the common case */ + if (instFormat <= (sixtyFourBitIndexableFormat())) { + return instFormat; + } + if (instFormat < (firstShortFormat())) { + return instFormat & -2; + } + return instFormat & -4; +} + + +/* 22-bit class mask => ~ 4M classes */ + + /* SpurMemoryManager>>#classIndexFieldWidth */ +static sqInt +classIndexFieldWidth(void) +{ + return 22; +} + + +/* Answer objOop's classIndex from the object header. + A note on performance: + This routine uses longAt:, which does a 32-bit load on the 32-bit system, + and a 64-bit load + on the 64-bit system. Since the only 64-bit implementation(s) is/are + little-endian, and + since all of UNUSED,isPinned,isRemembered,format,isImmutable and + classIndex fit within + the least significant 32-bits we could use long32At: to access these, in + the hope that the + 32-bit access will be quicker on 64-bits by virtue of fetching half the + bits. But experiments + show that doing this does not produce any increase; at least any signal is + lost in the noise. */ + + /* SpurMemoryManager>>#classIndexOf: */ +sqInt +classIndexOf(sqInt objOop) +{ + return (longAt((void *)(objOop))) & (classIndexMask()); +} + + /* SpurMemoryManager>>#classLargeNegativeInteger */ +sqInt +classLargeNegativeInteger(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin knownClassAtIndex: */ + assert(((ClassLargeNegativeIntegerCompactIndex >= 1) && (ClassLargeNegativeIntegerCompactIndex <= (classTablePageSize())))); + return longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassLargeNegativeIntegerCompactIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classLargePositiveInteger */ +sqInt +classLargePositiveInteger(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin knownClassAtIndex: */ + assert(((ClassLargePositiveIntegerCompactIndex >= 1) && (ClassLargePositiveIntegerCompactIndex <= (classTablePageSize())))); + return longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassLargePositiveIntegerCompactIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classOrNilAtIndex: */ +sqInt +classOrNilAtIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt fieldIndex; + + assert((classIndex <= (tagMask())) + || (classIndex >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return GIV(nilObj); + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classPoint */ +sqInt +classPoint(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassPoint) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classSemaphore */ +sqInt +classSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classSmallInteger */ +sqInt +classSmallInteger(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSmallInteger) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classString */ +sqInt +classString(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord())))))); +} + + +/* 1024 entries per page (2^10); 22 bit classIndex implies 2^12 pages */ +/* self basicNew classTableMinorIndexMask */ + + /* SpurMemoryManager>>#classTableMinorIndexMask */ +sqInt +classTableMinorIndexMask(void) +{ + return (1U << (classTableMajorIndexShift())) - 1; +} + + +/* 1024 entries per page (2^10); 22 bit classIndex implies 2^12 pages */ +/* self basicNew classTablePageSize */ + + /* SpurMemoryManager>>#classTablePageSize */ +sqInt +classTablePageSize(void) +{ + return 1U << (classTableMajorIndexShift()); +} + + +/* For Cogit & bootstrap */ + + /* SpurMemoryManager>>#classTableRootObj */ +sqInt +classTableRootObj(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(hiddenRootsObj); +} + + +/* Answer the number of slots for class table pages in the hidden root + object. + */ + + /* SpurMemoryManager>>#classTableRootSlots */ +static sqInt +classTableRootSlots(void) +{ + return 1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift())); +} + + +/* Answer the classObj's identityHash to use as a tag in the first-level + method lookup cache. + */ + + /* SpurMemoryManager>>#classTagForClass: */ +sqInt +classTagForClass(sqInt classObj) +{ + sqInt err; + int hash; + + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + return ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); +} + + /* SpurMemoryManager>>#classUnsafeAlien */ +sqInt +classUnsafeAlien(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassUnsafeAlien) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#classWordArray */ +sqInt +classWordArray(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOop; + int hash; + + /* begin accessIntegerClass:withValidationFlag: */ + hash = 0; + classOop = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassWordArray) << (shiftForWord())))))); + if (((GIV(validatedIntegerClassFlags) & ValidatedClassWordArrayFlag) != 0)) { + return classOop; + } + if ((((classOop & (tagMask())) != 0)) + || ((((hash = (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask()))) == 0) + || ((classAtIndex(hash)) != classOop))) { + return GIV(nilObj); + } + GIV(validatedIntegerClassFlags) = GIV(validatedIntegerClassFlags) | ValidatedClassWordArrayFlag; + return classOop; +} + + +/* Perform an integrity/leak check using the heapMap. Set a bit at each free + chunk's header. */ + + /* SpurMemoryManager>>#clearLeakMapAndMapAccessibleFreeSpace */ +static void +clearLeakMapAndMapAccessibleFreeSpace(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + + clearHeapMap(); + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + heapMapAtWordPut(pointerForOop(objOopSqInt), 1); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } +} + + /* SpurMemoryManager>>#cloneInOldSpace:forPinning: */ +static NoDbgRegParms sqInt +cloneInOldSpaceforPinning(sqInt objOop, sqInt forPinning) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cogMethodOrMethodHeader; + sqInt fmt; + int hash; + sqInt hasYoung; + sqInt i; + sqInt newObj; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt rawHeader; + sqInt referent; + sqInt valuePointer; + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (forPinning) { + newObj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), fmt, (longAt((void *)(objOop))) & (classIndexMask())); + } + else { + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), fmt, (longAt((void *)(objOop))) & (classIndexMask())); + } + if (!newObj) { + return 0; + } + if (fmt <= 5 /* lastPointerFormat */) { + hasYoung = 0; + for (i = 0; i < numSlots; i += 1) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + } + if (((!(oop & (tagMask())))) + && ((/* begin isYoungObject: */ + assert(isNonImmediate(oop)), + oopisLessThan(oop, GIV(oldSpaceStart))))) { + hasYoung = 1; + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(i, newObj, oop)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),oop); + } + if (hasYoung) { + remember(newObj); + } + } + else { + for (i = 0; i < numSlots; i += 1) { + valuePointer = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(i, newObj, valuePointer)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + if (fmt >= (firstCompiledMethodFormat())) { + /* begin maybeFixClonedCompiledMethod: */ + assert(isOopCompiledMethod(newObj)); + rawHeader = longAt((void *)((newObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + cogMethodOrMethodHeader = ((((CogMethod *) rawHeader))->methodHeader); + + /* begin rawHeaderOf:put: */ + assert(isCompiledMethodHeader(headerWhileForwardingOf(newObj))); + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(HeaderIndex, newObj, cogMethodOrMethodHeader)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))),cogMethodOrMethodHeader); + } + if (((/* begin isYoungObject: */ + assert(isNonImmediate(objOop)), + oopisLessThan(objOop, GIV(oldSpaceStart)))) + || (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0)) { + remember(newObj); + } + } + } + if ((hash = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()))) { + /* begin setHashBitsOf:to: */ + long32Atput((void *)(newObj + 4),((((long32At((void *)(newObj + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(objOop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* begin setIsImmutableOf:to: */ + longAtput((void *)(newObj),(longAt((void *)(newObj))) | (1U << (immutableBitShift()))); + } + return newObj; +} + + /* SpurMemoryManager>>#cloneObject: */ +sqInt +cloneObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt cogMethodOrMethodHeader; + sqInt fmt; + sqInt hasYoung; + sqInt i; + sqInt newObj; + usqInt newObjUsqInt; + usqInt numBytes; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt rawHeader; + sqInt referent; + sqInt valuePointer; + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (numSlots > ((1U << (fixedFieldsFieldWidth())) - 1)) { + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + + /* begin allocateSlotsInOldSpace:format:classIndex: */ + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, /* objectBytesForSlots: */ + (numSlots + ? ((numSlots << (shiftForWord()))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), fmt, classIndex); + } + else { + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if (((numSlots) >> 56) > 0) { + newObj = null; + goto l1; + } + newObjUsqInt = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObjUsqInt = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, fmt, classIndex); + goto l1; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObjUsqInt),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(fmt) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObjUsqInt),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(fmt) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObjUsqInt % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newObj = newObjUsqInt; + /* end allocateSlots:format:classIndex: */ +l1:; + } + if (newObj) { + if (fmt <= 5 /* lastPointerFormat */) { + hasYoung = 0; + for (i = 0; i < numSlots; i += 1) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(oop & (tagMask())))) { + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + } + if (((!(oop & (tagMask())))) + && ((/* begin isYoungObject: */ + assert(isNonImmediate(oop)), + oopisLessThan(oop, GIV(oldSpaceStart))))) { + hasYoung = 1; + } + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(i, newObj, oop)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),oop); + } + if (hasYoung + && (!((/* begin isYoungObject: */ + assert(isNonImmediate(newObj)), + oopisLessThan(newObj, GIV(oldSpaceStart)))))) { + remember(newObj); + } + } + else { + for (i = 0; i < numSlots; i += 1) { + valuePointer = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(i, newObj, valuePointer)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + if (fmt >= (firstCompiledMethodFormat())) { + /* begin maybeFixClonedCompiledMethod: */ + assert(isOopCompiledMethod(newObj)); + rawHeader = longAt((void *)((newObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((rawHeader) & 7) == 1)) + || (((((usqInt)rawHeader)) < (startOfMemory())) + && ((((usqInt)rawHeader)) >= (minCogMethodAddress())))); + if ((!(rawHeader & (smallIntegerTag())))) { + cogMethodOrMethodHeader = ((((CogMethod *) rawHeader))->methodHeader); + + /* begin rawHeaderOf:put: */ + assert(isCompiledMethodHeader(headerWhileForwardingOf(newObj))); + assert((isNonImmediate(newObj)) + && (!(isForwarded(newObj)))); + assert(validStorePointerUncheckedArgs(HeaderIndex, newObj, cogMethodOrMethodHeader)); + longAtput((void *)((newObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))),cogMethodOrMethodHeader); + } + if (((/* begin isOldObject: */ + assert(isNonImmediate(newObj)), + oopisGreaterThanOrEqualTo(newObj, GIV(oldSpaceStart)))) + && (((/* begin isYoungObject: */ + assert(isNonImmediate(objOop)), + oopisLessThan(objOop, GIV(oldSpaceStart)))) + || (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0))) { + remember(newObj); + } + } + } + } + return newObj; +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. + Copy objOop into the segment beginning at segAddr, and forward it to the + copy, saving its first field in savedFirstField, and setting its marked + bit to indicate it has + been copied. If it is a class in the class table, set the copy's hash to 0 + for reassignment + on load, and mark it as a class by setting its isRemembered bit. + Answer the next segmentAddr if successful. Answer an appropriate error + code if not */ +/* Copy the object... */ + + /* SpurMemoryManager>>#copyObj:toAddr:stopAt:savedFirstFields:index: */ +static NoDbgRegParms sqInt +copyObjtoAddrstopAtsavedFirstFieldsindex(sqInt objOop, sqInt segAddr, sqInt endSeg, sqInt savedFirstFields, sqInt i) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bodySize; + sqInt contextSize; + sqInt copy; + sqInt fmt; + int hash; + sqInt header; + sqInt headerSqInt; + sqInt iSqInt; + sqInt methodHeader; + usqInt numLiterals; + usqInt numMediatedSlots; + usqInt numSlots; + sqInt oop; + sqInt sp; + sqInt toDoLimit; + sqInt valuePointer; + + assert(!((isCopiedIntoSegment(objOop)))); + bodySize = bytesInBody(objOop); + if (oopisGreaterThanOrEqualTo(segAddr + bodySize, endSeg)) { + return PrimErrWritePastObject; + } + memcpy(((void *)segAddr), ((void *)(/* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop))), bodySize); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(segAddr + (numSlotsFieldByteOffset()))); + copy = (numSlots == (numSlotsMask()) + ? segAddr + BaseHeaderSize + : segAddr); + + /* Clear remembered, mark bits of all headers copied into the segment (except classes) */ + setIsRememberedOfto(copy, 0); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(copy))); + byteAtput((void *)(copy + (markBitsByteOffset())),(byteAt((void *)(copy + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + + /* Make any objects with hidden dynamic state (contexts, methods) look like normal objects. */ + + /* begin ifAProxy:updateCopy: */ + if (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + if (/* isStillMarriedContext: */ + (((((longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(objOop)))) { + /* begin numSlotsOfMarriedContext: */ + contextSize = stackPointerIndexForFrame(frameOfMarriedContext(objOop)); + assert((CtxtTempFrameStart + contextSize) <= (numSlotsOf(objOop))); + numMediatedSlots = CtxtTempFrameStart + contextSize; + for (iSqInt = 0; iSqInt < numMediatedSlots; iSqInt += 1) { + oop = fetchPointerofMarriedContext(iSqInt, objOop); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(copy)) + && (!(isForwarded(copy)))); + assert(validStorePointerUncheckedArgs(iSqInt, copy, oop)); + longAtput((void *)((copy + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord()))))),oop); + } + } + else { + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + numMediatedSlots = CtxtTempFrameStart + contextSize; + goto l2; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numMediatedSlots = (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l2; + } + if (fmt == (forwardedFormat())) { + numMediatedSlots = 1; + goto l2; + } + if (fmt < (firstCompiledMethodFormat())) { + numMediatedSlots = 0; + goto l2; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numMediatedSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l2:; + } + + /* And make sure to nil the slots beyond the top of stack... */ + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (iSqInt = numMediatedSlots; iSqInt <= toDoLimit; iSqInt += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(copy)) + && (!(isForwarded(copy)))); + assert(validStorePointerUncheckedArgs(iSqInt, copy, GIV(nilObj))); + longAtput((void *)((copy + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord()))))),GIV(nilObj)); + } + } + if (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + methodHeader = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(copy)) + && (!(isForwarded(copy)))); + assert(validStorePointerUncheckedArgs(HeaderIndex, copy, ((((CogMethod *) methodHeader))->methodHeader))); + longAtput((void *)((copy + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))),((((CogMethod *) methodHeader))->methodHeader)); + } + } + + /* If the object is a class, zero its identityHash (which is its classIndex) and set its + isRemembered bit. It will be assigned a new hash and entered into the table on load. */ + hash = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()); + if ((hash > (lastClassIndexPun())) + && ((classOrNilAtIndex(hash)) == objOop)) { + /* begin setHashBitsOf:to: */ + long32Atput((void *)(copy + 4),((((long32At((void *)(copy + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (0)); + setIsRememberedOfto(copy, 1); + } + + /* Now forward the object to its copy in the segment. */ + valuePointer = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(savedFirstFields)) + && (!(isForwarded(savedFirstFields)))); + assert(validStorePointerUncheckedArgs(i, savedFirstFields, valuePointer)); + longAtput((void *)((savedFirstFields + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(0, objOop, copy)); + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),copy); + + /* begin markAsCopiedIntoSegment: */ + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* Answer the new end of segment */ + return segAddr + bodySize; +} + + +/* print the count of marked and unmarked objects. + In addition if 1 is set in printFlags, short-print marked objects, + and/or if 2 is set, short-print unmarked obejcts. */ +/* useful for debugging */ + + /* SpurMemoryManager>>#countMarkedAndUnmarkdObjects: */ +void +countMarkedAndUnmarkdObjects(sqInt printFlags) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt nm; + sqInt nu; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + nm = (nu = 0); + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + nm += 1; + if (((printFlags & 1) != 0)) { + shortPrintOop(obj); + } + } + else { + nu += 1; + if (((printFlags & 2) != 0)) { + shortPrintOop(obj); + } + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "n marked: "); + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)nm)); + cr(); + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "n unmarked: "); + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)nu)); + cr(); +} + + +/* Compute the current allocated bytes since last set. + This is the cumulative total in statAllocatedBytes plus the allocation + since the last scavenge. */ +/* Slang infers the type of the difference between two unsigned variables as + signed. In this case we want it to be unsigned. */ + + /* SpurMemoryManager>>#currentAllocatedBytes */ +static usqLong +currentAllocatedBytes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt use; + + use = ((/* begin totalOldSpaceCapacity */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + return (GIV(statAllocatedBytes) + (GIV(freeStart) - (((GIV(eden)).start)))) + (use - GIV(oldSpaceUsePriorToScavenge)); +} + + +/* The inner shell for scavenge, abstrascted out so globalGarbageCollect can + use it. + */ + + /* SpurMemoryManager>>#doScavenge: */ +static NoDbgRegParms void +doScavenge(sqInt tenuringCriterion) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + float fractionSurvived; + sqInt previousFSStart; + SpurNewSpaceSpace temp; + usqInt use; + + /* begin doAllocationAccountingForScavenge */ + /* begin currentAllocatedBytes */ + use = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + GIV(statAllocatedBytes) = (GIV(statAllocatedBytes) + (GIV(freeStart) - (((GIV(eden)).start)))) + (use - GIV(oldSpaceUsePriorToScavenge)); + GIV(gcPhaseInProgress) = ScavengeInProgress; + + /* begin scavenge: */ + GIV(statSurvivorCount) = 0; + GIV(tenureCriterion) = tenuringCriterion; + + /* begin logStartScavenge */ + (GIV(scavengeLogRecord).sEdenBytes = GIV(freeStart) - ((GIV(eden).start))); + (GIV(scavengeLogRecord).sPastBytes = GIV(pastSpaceStart) - ((GIV(pastSpace).start))); + (GIV(scavengeLogRecord).sRememberedSetSize = GIV(rememberedSetSize)); + (GIV(scavengeLogRecord).sRememberedSetRedZone = GIV(rememberedSetRedZone)); + (GIV(scavengeLogRecord).sRememberedSetLimit = GIV(rememberedSetLimit)); + (GIV(scavengeLogRecord).sStatTenures = GIV(statTenures)); + + /* begin strategizeToLimitRememberedTable */ + if ((GIV(tenureCriterion) == TenureByAge) + && (GIV(rememberedSetSize) >= GIV(rememberedSetRedZone))) { + GIV(tenureCriterion) = TenureToShrinkRT; + computeRefCountToShrinkRT(); + } + + /* begin logTenuringPolicy */ + (GIV(scavengeLogRecord).tTenureCriterion = GIV(tenureCriterion)); + (GIV(scavengeLogRecord).tTenureThreshold = ((GIV(tenureCriterion) == TenureByAge) + && (GIV(tenureThreshold) > ((GIV(pastSpace).start))) + ? GIV(tenureThreshold) - ((GIV(pastSpace).start)) + : 0)); + (GIV(scavengeLogRecord).tRefCountToShrinkRT = GIV(refCountToShrinkRT)); + ((void) (scavengeLoop())); + processWeaklings(); + + /* begin computeTenuringThreshold */ + fractionSurvived = (((GIV(futureSpace).limit)) == ((GIV(futureSpace).start)) + ? 0.0 + : (((double) (GIV(futureSurvivorStart) - ((GIV(futureSpace).start))) )) / (((GIV(futureSpace).limit)) - ((GIV(futureSpace).start)))); + GIV(tenureThreshold) = (fractionSurvived > 0.9 + ? (round(((((GIV(pastSpace).limit)) - ((GIV(pastSpace).start))) * (1.0 - GIV(tenuringProportion))))) + ((GIV(pastSpace).start)) + : 0); + + /* begin exchangeSurvivorSpaces */ + temp = GIV(pastSpace); + GIV(pastSpace) = GIV(futureSpace); + GIV(futureSpace) = temp; + + /* begin logEndScavenge */ + (GIV(scavengeLogRecord).eSurvivorBytes = GIV(futureSurvivorStart) - ((GIV(pastSpace).start))); + (GIV(scavengeLogRecord).eRememberedSetSize = GIV(rememberedSetSize)); + (GIV(scavengeLogRecord).eStatTenures = GIV(statTenures)); + + /* begin initFutureSpaceStart */ + previousFSStart = GIV(futureSurvivorStart); + GIV(futureSurvivorStart) = (GIV(futureSpace).start); + GIV(pastSpaceStart) = previousFSStart; + assert(oopisGreaterThanOrEqualToandLessThanOrEqualTo(GIV(pastSpaceStart), ((GIV(pastSpace)).start), ((GIV(pastSpace)).limit))); + GIV(freeStart) = ((GIV(eden)).start); + GIV(gcPhaseInProgress) = 0; + + /* begin resetAllocationAccountingAfterGC */ + GIV(oldSpaceUsePriorToScavenge) = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); +} + + +/* Reduce the number of indexable fields in objOop, a pointer object, to + nSlots. If in oldSpace, + convert the unused residual to a free chunk. If in eden, cut back + freeStart if possible. + Without changes to numSlotsForShortening:toIndexableSize: this only works + for arrayFormat, longFormat, and on 64-bits sixtyFourBitIndexableFormat, + objects. Answer the number of bytes returned to free memory, which may be + zero if no change + was possible. */ + + /* SpurMemoryManager>>#doShorten:toIndexableSize: */ +static NoDbgRegParms sqInt +doShortentoIndexableSize(sqInt objOop, sqInt indexableSize) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytesBefore; + sqInt classIndex; + sqInt copy; + sqInt delta; + usqInt followingAddress; + sqInt format; + sqInt formatSqInt; + sqInt freeChunk; + sqInt i; + sqInt instFormat; + usqInt newObj; + usqInt numBytes; + sqInt numSlots; + sqInt valuePointer; + + /* for assert checking */ + followingAddress = addressAfter(objOop); + assert(oopisLessThanOrEqualTo(followingAddress, GIV(endOfMemory))); + + /* numSlotsForShortening:toIndexableSize: */ + switch ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) { + case arrayFormat(): + case sixtyFourBitIndexableFormat(): + numSlots = indexableSize; + break; + case firstLongFormat(): + case (firstLongFormat()) + 1: + numSlots = ((indexableSize * 4) + (BytesPerWord - 1)) / BytesPerWord; + break; + default: + assertf("invalid format for shortening"); + numSlots = 0; + } + bytesBefore = bytesInBody(objOop); + delta = bytesBefore - (/* objectBytesForSlots: */ + (numSlots + ? ((((usqInt)(numSlots) << (shiftForWord())))) + ((numSlots >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize)); + + /* Since the system rounds objects up to 64-bits, losing a 32-bit + slot may not actually change the bytes occupied by the object. */ + if (!delta) { + if ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) { + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(objOop - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56))) + numSlots); + } + else { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),numSlots); + } + + /* begin updateFormatOfShortenedObject:to: */ + if (((((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) <= ((firstLongFormat()) + 1)))) { + formatSqInt = (firstLongFormat()) + (indexableSize & 1); + + /* begin setFormatOf:to: */ + assert(((formatSqInt >= 0) && (formatSqInt <= (formatMask())))); + byteAtput((void *)(objOop + (formatFieldByteOffset())),((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + formatSqInt); + } + return 0; + } + + /* Currently we can't have one word gaps in oldSpace but we can in newSpace. So only create + a copy and forward if objOop is in old space. If in newSpace we'll create a slimbridge below. */ + if ((delta <= 8 /* allocationUnit */) + && ((/* begin isOldObject: */ + assert(isNonImmediate(objOop)), + oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))))) { + /* begin normalisedFormatFor:indexableSize: */ + instFormat = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (instFormat >= (firstByteFormat())) { + formatSqInt = instFormat & -8; + goto l1; + } + + /* this is likely the common case */ + if (instFormat <= (sixtyFourBitIndexableFormat())) { + formatSqInt = instFormat; + goto l1; + } + if (instFormat < (firstShortFormat())) { + formatSqInt = instFormat & -2; + goto l1; + } + formatSqInt = instFormat & -4; + /* end classFormatFromInstFormat: */ +l1: + format = ((formatSqInt == (firstLongFormat())) + && (((indexableSize & 1) != 0)) + ? formatSqInt + 1 + : formatSqInt); + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if ((((usqInt)(numSlots)) >> 56) > 0) { + copy = null; + goto l2; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + copy = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, format, classIndex); + goto l2; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(format) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(format) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + copy = newObj; + /* end allocateSlots:format:classIndex: */ +l2: + if (!copy) { + error("shorten:toIndexableSize: attempted to shorten by allocationUnit and failed to allocate space for copy!"); + } + if (/* isPureBitsFormat: */ + (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat()))) { + memcpy(firstIndexableField(copy), firstIndexableField(objOop), numBytesOfBitsformat(copy, format)); + } + else { + for (i = 0; i < numSlots; i += 1) { + valuePointer = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(copy)) + && (!(isForwarded(copy)))); + assert(validStorePointerUncheckedArgs(i, copy, valuePointer)); + longAtput((void *)((copy + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + } + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) { + remember(copy); + } + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(objOop),(longAt((void *)(objOop))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(objOop)); + assert(!(isOopForwarded(copy))); + + /* begin isOldObject: */ + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(copy & (tagMask())))) + && (oopisLessThan(copy, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),copy); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),1); + } + return 0; + } + if ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) { + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(objOop - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56))) + numSlots); + + /* Setting an overflow slot count to 0 or 1 in newSpace creates a slimbridge. + So we must also change the normal slot count so that the object no + longer has the overflow header word, which has become the slimbridge. */ + if (numSlots <= 1) { + if (oopisLessThan(objOop, GIV(oldSpaceStart))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),numSlots); + hackSlimBridgeToat(objOop, objOop - 8 /* allocationUnit */); + } + } + } + else { + assert(numSlots < (numSlotsMask())); + + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),numSlots); + } + + /* {self addressAfter: objOop. delta. (self addressAfter: objOop) + delta. followingAddress} collect: #hex */ + delta = followingAddress - (addressAfter(objOop)); + assert((delta >= (allocationUnit())) + && ((delta % (allocationUnit())) == 0)); + if (delta == 8 /* allocationUnit */) { + assert(!((isOldObject(objOop)))); + delta = 0; + if (followingAddress == GIV(freeStart)) { + GIV(freeStart) = addressAfter(objOop); + } + else { + hackSlimBridgeToat(followingAddress, addressAfter(objOop)); + } + } + else { + freeChunk = initFreeChunkWithBytesat(delta, addressAfter(objOop)); + assert((objectAfter(objOop)) == freeChunk); + assert((addressAfter(freeChunk)) == followingAddress); + if (oopisGreaterThanOrEqualToandLessThan(objOop, GIV(oldSpaceStart), GIV(endOfMemory))) { + GIV(totalFreeOldSpace) += delta; + addToFreeListbytes(freeChunk, delta); + } + else { + delta = 0; + + /* begin setClassIndexOf:to: */ + assert((((sixtyFourBitLongsClassIndexPun()) >= 0) && ((sixtyFourBitLongsClassIndexPun()) <= (classIndexMask())))); + longAtput((void *)(freeChunk),((longAt((void *)(freeChunk))) & (~(usqIntptr_t)(classIndexMask()))) + (sixtyFourBitLongsClassIndexPun())); + + /* begin setFormatOf:to: */ + assert((((firstLongFormat()) >= 0) && ((firstLongFormat()) <= (formatMask())))); + byteAtput((void *)(freeChunk + (formatFieldByteOffset())),((byteAt((void *)(freeChunk + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (firstLongFormat())); + } + } + + /* begin normalisedFormatFor:indexableSize: */ + instFormat = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (instFormat >= (firstByteFormat())) { + formatSqInt = instFormat & -8; + goto l3; + } + + /* this is likely the common case */ + if (instFormat <= (sixtyFourBitIndexableFormat())) { + formatSqInt = instFormat; + goto l3; + } + if (instFormat < (firstShortFormat())) { + formatSqInt = instFormat & -2; + goto l3; + } + formatSqInt = instFormat & -4; + /* end classFormatFromInstFormat: */ +l3: + formatSqInt = ((formatSqInt == (firstLongFormat())) + && (((indexableSize & 1) != 0)) + ? formatSqInt + 1 + : formatSqInt); + + /* begin setFormatOf:to: */ + assert(((formatSqInt >= 0) && (formatSqInt <= (formatMask())))); + byteAtput((void *)(objOop + (formatFieldByteOffset())),((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + formatSqInt); + return delta; +} + + +/* Instantiate an instance of a compact class. ee stands for execution engine + and implies that this allocation will *NOT* cause a GC. N.B. the + instantiated object + IS NOT FILLED and must be completed before returning it to Smalltalk. + Since this + call is used in routines that do just that we are safe. Break this rule + and die in GC. + Result is guaranteed to be young. */ + + /* SpurMemoryManager>>#eeInstantiateClassIndex:format:numSlots: */ +usqInt +eeInstantiateClassIndexformatnumSlots(sqInt knownClassIndex, sqInt objFormat, sqInt numSlots) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newObj; + usqInt numBytes; + + assert((numSlots >= 0) + && ((knownClassIndex != 0) + && ((knownClassAtIndex(knownClassIndex)) != GIV(nilObj)))); + assert(((objFormat < (firstByteFormat()) + ? objFormat + : objFormat & (byteFormatMask()))) == (instSpecOfClass(knownClassAtIndex(knownClassIndex)))); + + /* begin allocateNewSpaceSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if (numSlots > 0xFFFFFFFFU) { + return null; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateNewSpaceSlots:format:classIndex:"); + return 0; + } + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),numSlots); + longAtput((void *)(GIV(freeStart) + 4),((sqInt)((usqInt)((numSlotsMask())) << (numSlotsHalfShift())))); + long64Atput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + knownClassIndex); + } + else { + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(objFormat) << (formatShift()))))) + knownClassIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + return newObj; +} + + +/* For breakpointing the end of a leak check print message */ + + /* SpurMemoryManager>>#eek */ +NeverInline void +eek(void) +{ +} + + +/* We use bridges to stitch segments together to make it appear that the heap + is one contiguous space. + Bridges at the end of oldSpace segments are maintained. Bridges at the end + of pastSpace and eden + are temporary, and are established here, depending on the current sizes of + pastSpace end eden. + + N.B. this introduces complications. Either or both pastSpace and eden may + be empty, so the + bridge from pastSpace may skip eden. pastSpace may be full, so there may + be no bridge at + the end of pastSpace. Most difficult, pastSpace could be one 64-bit word + short of full, but normal + bridges are two word objects. To make this work we introduce a hack, + objectAfterMaybeSlimBridge:limit:, which uses a fake overflow slot count + to get to the start of the next object, which is either one or two + words away, depending on whether the first object in eden has a normal or + an overflow header. */ + + /* SpurMemoryManager>>#enableObjectEnumerationFrom: */ +static NoDbgRegParms void +enableObjectEnumerationFrom(sqInt initialObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (oopisLessThan(initialObject, GIV(oldSpaceStart))) { + if (GIV(freeStart) > (((GIV(eden)).start))) { + /* begin bridgeEdenAndOldSpace */ + initSegmentBridgeWithBytesat(GIV(oldSpaceStart) - GIV(freeStart), GIV(freeStart)); + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + } + else { + /* begin bridgePastSpaceAndOldSpace */ + initSegmentBridgeWithBytesat(GIV(oldSpaceStart) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } +} + + /* SpurMemoryManager>>#ensureBehaviorHash: */ +static NoDbgRegParms sqInt +ensureBehaviorHash(sqInt aBehavior) +{ + sqInt err; + int hash; + + assert(addressCouldBeClassObj(aBehavior)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + return ((hash = (long32At((void *)(aBehavior + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(aBehavior) + ? ((err = enterIntoClassTable(aBehavior)) + ? -err + : (long32At((void *)(aBehavior + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); +} + + +/* An obj stack is a stack of objects stored in a hidden root slot, such as + the markStack or the ephemeronQueue. It is a linked list of segments, + with the hot end at the head of the list. It is a word object. The stack + pointer is in ObjStackTopx and 0 means empty. The list goes through + ObjStackNextx. We don't want to shrink objStacks, since they're used + in GC and its good to keep their memory around. So unused pages + created by popping emptying pages are kept on the ObjStackFreex list. */ + + /* SpurMemoryManager>>#ensureRoomOnObjStackAt: */ +static NoDbgRegParms sqInt +ensureRoomOnObjStackAt(sqInt objStackRootIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt freeOrNewPage; + sqInt stackOrNil; + + stackOrNil = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord())))))); + if ((stackOrNil == GIV(nilObj)) + || ((longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) >= ObjStackLimit)) { + freeOrNewPage = (stackOrNil == GIV(nilObj) + ? 0 + : longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))))); + if (freeOrNewPage) { + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(stackOrNil)) == (wordIndexableFormat())); + longAtput((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),0); + assert((!GIV(marking)) + || (isMarked(freeOrNewPage))); + } + else { + freeOrNewPage = allocateSlotsInOldSpacebytesformatclassIndex(ObjStackPageSlots, ((((usqInt)(ObjStackPageSlots) << (shiftForWord())))) + (BaseHeaderSize + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (!freeOrNewPage) { + /* Allocate a new segment and retry. This is very uncommon. But it happened to me (Clement). */ + growOldSpaceByAtLeast(ObjStackPageSlots * BytesPerOop); + freeOrNewPage = allocateSlotsInOldSpacebytesformatclassIndex(ObjStackPageSlots, ((((usqInt)(ObjStackPageSlots) << (shiftForWord())))) + (BaseHeaderSize + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (!freeOrNewPage) { + error("no memory to allocate or extend obj stack"); + } + } + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(freeOrNewPage)) == (wordIndexableFormat())); + longAtput((void *)((freeOrNewPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),0); + if (GIV(marking)) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(freeOrNewPage))); + byteAtput((void *)(freeOrNewPage + (markBitsByteOffset())),(byteAt((void *)(freeOrNewPage + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + } + } + + /* the free page list is always on the new page. */ + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(freeOrNewPage)) == (wordIndexableFormat())); + longAtput((void *)((freeOrNewPage + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord()))))),objStackRootIndex); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(freeOrNewPage)) == (wordIndexableFormat())); + longAtput((void *)((freeOrNewPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord()))))),(stackOrNil == GIV(nilObj) + ? 0 + : stackOrNil)); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(freeOrNewPage)) == (wordIndexableFormat())); + longAtput((void *)((freeOrNewPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))),0); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(objStackRootIndex, GIV(hiddenRootsObj), freeOrNewPage)); + assert(isNonImmediate(GIV(hiddenRootsObj))); + if (oopisGreaterThanOrEqualTo(GIV(hiddenRootsObj), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(freeOrNewPage & (tagMask())))) + && (oopisLessThan(freeOrNewPage, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(hiddenRootsObj) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(hiddenRootsObj)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord()))))),freeOrNewPage); + assert(isValidObjStackAt(objStackRootIndex)); + + /* Added a new page; now update and answer the relevant cached first page. */ + + /* begin updateRootOfObjStackAt:with: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(objStackRootIndex, GIV(hiddenRootsObj), freeOrNewPage)); + assert(isNonImmediate(GIV(hiddenRootsObj))); + if (oopisGreaterThanOrEqualTo(GIV(hiddenRootsObj), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(freeOrNewPage & (tagMask())))) + && (oopisLessThan(freeOrNewPage, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(hiddenRootsObj) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(hiddenRootsObj)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord()))))),freeOrNewPage); + switch (objStackRootIndex) { + case MarkStackRootIndex: + GIV(markStack) = freeOrNewPage; + break; + case WeaklingStackRootIndex: + GIV(weaklingStack) = freeOrNewPage; + break; + case MournQueueRootIndex: + GIV(mournQueue) = freeOrNewPage; + break; + default: + error("Case not found and no otherwise clause"); + } + stackOrNil = freeOrNewPage; + } + assert(isValidObjStackAt(objStackRootIndex)); + return stackOrNil; +} + + +/* Enter aBehavior into the class table and answer 0. Otherwise answer a + primitive failure code. */ + + /* SpurMemoryManager>>#enterIntoClassTable: */ +static NoDbgRegParms sqInt +enterIntoClassTable(sqInt aBehavior) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt initialMajorIndex; + sqInt majorIndex; + sqInt minorIndex; + usqInt p; + sqInt page; + sqInt toDoLimit; + usqInt toDoLimitUsqInt; + + majorIndex = ((usqInt)(GIV(classTableIndex))) >> (classTableMajorIndexShift()); + initialMajorIndex = majorIndex; + + /* classTableIndex should never index the first page; it's reserved for known classes */ + assert(initialMajorIndex > 0); + minorIndex = GIV(classTableIndex) & ((1U << (classTableMajorIndexShift())) - 1); + while (1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(majorIndex) << (shiftForWord())))))); + if (page == GIV(nilObj)) { + /* begin allocateSlotsInOldSpace:format:classIndex: */ + page = allocateSlotsInOldSpacebytesformatclassIndex(1U << (classTableMajorIndexShift()), /* objectBytesForSlots: */ + (1U << (classTableMajorIndexShift()) + ? ((((usqInt)((1U << (classTableMajorIndexShift()))) << (shiftForWord())))) + (((1U << (classTableMajorIndexShift())) >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize), arrayFormat(), arrayClassIndexPun()); + if (!page) { + return PrimErrNoMemory; + } + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((page + BaseHeaderSize) + ((1U << (classTableMajorIndexShift())) * BytesPerOop)) - 1, addressAfter(page))); + toDoLimitUsqInt = ((usqInt)(((page + BaseHeaderSize) + ((1U << (classTableMajorIndexShift())) * BytesPerOop)) - 1)); + for (p = (((usqInt)(page + BaseHeaderSize))); p <= toDoLimitUsqInt; p += 8 /* allocationUnit */) { + longAtput((void *)(p),GIV(nilObj)); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(majorIndex, GIV(hiddenRootsObj), page)); + assert(isNonImmediate(GIV(hiddenRootsObj))); + if (oopisGreaterThanOrEqualTo(GIV(hiddenRootsObj), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(page & (tagMask())))) + && (oopisLessThan(page, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(hiddenRootsObj) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(hiddenRootsObj)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(majorIndex) << (shiftForWord()))))),page); + GIV(numClassTablePages) += 1; + minorIndex = 0; + } + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (i = minorIndex; i <= toDoLimit; i += 1) { + if ((longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))))) == GIV(nilObj)) { + GIV(classTableIndex) = ((((usqInt)(majorIndex) << (classTableMajorIndexShift())))) + i; + + /* classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + assert(GIV(classTableIndex) >= (1U << (classTableMajorIndexShift()))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(i, page, aBehavior)); + assert(isNonImmediate(page)); + if (oopisGreaterThanOrEqualTo(page, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(aBehavior & (tagMask())))) + && (oopisLessThan(aBehavior, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(page + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(page); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((page + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),aBehavior); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(aBehavior + 4),((((long32At((void *)(aBehavior + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (GIV(classTableIndex) & (identityHashHalfWordMask()))); + assert((classAtIndex(rawHashBitsOf(aBehavior))) == aBehavior); + return 0; + } + } + minorIndex = 0; + + /* classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + majorIndex = ((((majorIndex + 1) & ((1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift()))) - 1)) < 1) ? 1 : ((majorIndex + 1) & ((1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift()))) - 1))); + if (majorIndex == initialMajorIndex) { + return PrimErrLimitExceeded; + } + } + return 0; +} + + /* SpurMemoryManager>>#existInstancesInNewSpaceOf: */ +static NoDbgRegParms sqInt +existInstancesInNewSpaceOf(sqInt classObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + + /* begin allNewSpaceObjectsDo: */ + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l2; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOopSqInt, GIV(freeStart))) { + assert(isBridgeOrEnumerableObjectNoAssert(objOopSqInt)); + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == classIndex) { + return 1; + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOopSqInt = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + /* end allNewSpaceEntitiesDo: */ +l2: + return 0; +} + + +/* Remove aBehavior from the class table. */ + + /* SpurMemoryManager>>#expungeFromClassTable: */ +static NoDbgRegParms void +expungeFromClassTable(sqInt aBehavior) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int classIndex; + sqInt classTablePage; + sqInt majorIndex; + int minorIndex; + + assert(isInClassTable(aBehavior)); + classIndex = (long32At((void *)(aBehavior + 4))) & (identityHashHalfWordMask()); + majorIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + minorIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(majorIndex) << (shiftForWord())))))); + assert(classTablePage != GIV(classTableFirstPage)); + assert((numSlotsOf(classTablePage)) == (classTablePageSize())); + assert((fetchPointerofObject(minorIndex, classTablePage)) == aBehavior); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(classTablePage)) + && (!(isForwarded(classTablePage)))); + assert(validStorePointerUncheckedArgs(minorIndex, classTablePage, GIV(nilObj))); + longAtput((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(minorIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* If the removed class is before the classTableIndex, set the + classTableIndex to point to the empty slot so as to reuse it asap. */ + if (classIndex < GIV(classTableIndex)) { + GIV(classTableIndex) = classIndex; + } + + /* classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + assert(GIV(classTableIndex) >= (1U << (classTableMajorIndexShift()))); +} + + /* SpurMemoryManager>>#falseObject */ +sqInt +falseObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(falseObj); +} + + /* SpurMemoryManager>>#fetchByte:ofObject: */ +sqInt +fetchByteofObject(sqInt byteIndex, sqInt objOop) +{ + return byteAt((void *)((objOop + BaseHeaderSize) + byteIndex)); +} + + /* SpurMemoryManager>>#fetchClassOfNonImm: */ +static NoDbgRegParms NeverInline sqInt +fetchClassOfNonImm(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt classTablePage; + sqInt fieldIndex; + + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + if (classIndex <= (classIsItselfClassIndexPun())) { + if (classIndex == (classIsItselfClassIndexPun())) { + return objOop; + } + + /* Answer nil to avoid the assert failure in classOrNilAtIndex: */ + if (classIndex == (isForwardedObjectClassIndexPun())) { + return GIV(nilObj); + } + } + assert(classIndex >= (arrayClassIndexPun())); + + /* begin classOrNilAtIndex: */ + assert((classIndex <= (tagMask())) + || (classIndex >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return GIV(nilObj); + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#fetchClassOf: */ +sqInt +fetchClassOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt tagBits; + + return ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); +} + + +/* In Spur an object's classIndex is the tag in all method caches. */ + + /* SpurMemoryManager>>#fetchClassTagOfNonImm: */ +static NoDbgRegParms sqInt +fetchClassTagOfNonImm(sqInt obj) +{ + return (longAt((void *)(obj))) & (classIndexMask()); +} + + /* SpurMemoryManager>>#fetchInteger:ofObject: */ +sqLong +fetchIntegerofObject(sqInt fieldIndex, sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt oop; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + return ((((oop) & 7) == 1) + ? (oop >> 3) + : (GIV(primFailCode) = PrimErrInappropriate)); + } + return (fmt < (firstShortFormat()) + ? (fmt < (firstLongFormat()) + ? long64At((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 3))))) + : long32At((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2)))))) + : (fmt < (firstCompiledMethodFormat()) + ? (fmt < (firstByteFormat()) + ? byteAt((void *)((objOop + BaseHeaderSize) + fieldIndex)) + : shortAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 1)))))) + : (GIV(primFailCode) = PrimErrUnsupported))); +} + + +/* index by 32-bit units, and return a 32-bit value. Intended to replace + fetchWord:ofObject: + */ + + /* SpurMemoryManager>>#fetchLong32:ofObject: */ +int +fetchLong32ofObject(sqInt fieldIndex, sqInt oop) +{ + return long32At((void *)((oop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2))))); +} + + /* SpurMemoryManager>>#fetchPointer:ofFreeChunk: */ +static NoDbgRegParms sqInt +fetchPointerofFreeChunk(sqInt fieldIndex, sqInt objOop) +{ + return longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#fetchPointer:ofObject: */ +sqInt +fetchPointerofObject(sqInt fieldIndex, sqInt objOop) +{ + return longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#findClassNamed: */ +sqInt +findClassNamed(char *aString) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOrNil; + sqInt i; + sqInt ignored; + sqInt j; + sqInt page; + sqInt toDoLimit; + + /* begin classTableEntriesDo: */ + for (i = 0; i < GIV(numClassTablePages); i += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNil = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNil != GIV(nilObj)) { + ignored = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + if (classNameOfIs(classOrNil, aString)) { + return classOrNil; + } + } + } + } + return null; +} + + +/* Answer, but do not remove, the largest free chunk in the free lists. */ + + /* SpurMemoryManager>>#findLargestFreeChunk */ +static sqInt +findLargestFreeChunk(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt childNode; + sqInt treeNode; + + childNode = 0; + treeNode = GIV(freeLists)[0]; + if (!treeNode) { + return null; + } + while (1) { + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(treeNode)); + assert((bytesInBody(treeNode)) >= ((numFreeLists()) * (allocationUnit()))); + childNode = longAt((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + if (!(childNode != 0)) break; + treeNode = childNode; + } + return treeNode; +} + + +/* Print the oops of all string-like things that start with the same + characters as aCString + */ +/* useful for debugging */ + + /* SpurMemoryManager>>#findStringBeginningWith: */ +void +findStringBeginningWith(char *aCString) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aCStringStrlen; + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + aCStringStrlen = strlen(aCString); + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (/* object:beginsWithString:ofSize: */ + (/* isBytes: */ + ((!(objSqInt & (tagMask())))) + && (((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((!(((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((numBytesOfBytes(objSqInt)) >= aCStringStrlen) + && ((strncmp(aCString, firstIndexableField(objSqInt), aCStringStrlen)) == 0)))) { + printHex(objSqInt); + + /* begin space */ + printChar(' '); + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)(lengthOf(objSqInt)))); + printChar(' '); + printOopShortInner(objSqInt); + cr(); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + +/* Print the oops of all string-like things that have the same characters as + aCString + */ + + /* SpurMemoryManager>>#findString: */ +void +findString(char *aCString) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aCStringStrlen; + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + aCStringStrlen = strlen(aCString); + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (/* object:equalsString:ofSize: */ + (/* isBytes: */ + ((!(objSqInt & (tagMask())))) + && (((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((!(((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((numBytesOfBytes(objSqInt)) == aCStringStrlen) + && ((strncmp(aCString, firstIndexableField(objSqInt), aCStringStrlen)) == 0)))) { + printHex(objSqInt); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + cr(); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + /* SpurMemoryManager>>#fireEphemeron: */ +static NoDbgRegParms void +fireEphemeron(sqInt ephemeron) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + + assert(isNonImmediate(ephemeron)); + format = (byteAt((void *)(ephemeron + (formatFieldByteOffset())))) & (formatMask()); + if (format == (ephemeronFormat())) { + queueMourner(ephemeron); + + /* begin setEphemeronToFired: */ + assert((isNonImmediate(ephemeron)) + && (isEphemeron(ephemeron))); + + /* begin setFormatOf:to: */ + assert((((nonIndexablePointerFormat()) >= 0) && ((nonIndexablePointerFormat()) <= (formatMask())))); + byteAtput((void *)(ephemeron + (formatFieldByteOffset())),((byteAt((void *)(ephemeron + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (nonIndexablePointerFormat())); + + /* begin signalFinalization: */ + forceInterruptCheck(); + GIV(pendingFinalizationSignals) += 1; + } + else { + assert((format == (nonIndexablePointerFormat())) + && (isonObjStack(ephemeron, GIV(mournQueue)))); + } +} + + /* SpurMemoryManager>>#firstAccessibleObject */ +static sqInt +firstAccessibleObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt savedTenuringThreshold; + + assert(GIV(nilObj) == GIV(oldSpaceStart)); + + /* flush newSpace to settle the enumeration. */ + + /* begin flushNewSpace */ + savedTenuringThreshold = GIV(tenureThreshold); + GIV(tenureThreshold) = GIV(oldSpaceStart); + scavengingGCTenuringIf(TenureByAge); + + /* begin setRawTenuringThreshold: */ + GIV(tenureThreshold) = savedTenuringThreshold; + assert((GIV(rememberedSetSize)) == 0); + assert(GIV(pastSpaceStart) == (((GIV(pastSpace)).start))); + assert(GIV(freeStart) == (((GIV(eden)).start))); + return GIV(nilObj); +} + + +/* for the message send breakpoint; selectors can be immediates. */ + + /* SpurMemoryManager>>#firstFixedFieldOfMaybeImmediate: */ +static NoDbgRegParms void * +firstFixedFieldOfMaybeImmediate(sqInt oop) +{ + return (((oop & (tagMask())) != 0) + ? ((void *)oop) + : pointerForOop(oop + BaseHeaderSize)); +} + + /* SpurMemoryManager>>#firstFixedField: */ +void * +firstFixedField(sqInt objOop) +{ + return pointerForOop(objOop + BaseHeaderSize); +} + + +/* NOTE: overridden in various simulator subclasses to add coercion to + CArray, so please duplicate any changes. + There are only two important cases, both for objects with named inst vars, + i.e. formats 2,3 & 5. + The first indexable field for formats 2 & 5 is the slot count (by + convention, even though that's off the end + of the object). For 3 we must go to the class. */ + + /* SpurMemoryManager>>#firstIndexableField: */ +void * +firstIndexableField(sqInt objOop) +{ + sqInt classFormat; + sqInt classPointer; + sqInt fmt; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= (weakArrayFormat())) { + if (fmt == (arrayFormat())) { + return pointerForOop(objOop + BaseHeaderSize); + } + + /* array starts at 0. */ + if (fmt >= (indexablePointersFormat())) { + classPointer = fetchClassOfNonImm(objOop); + + /* begin formatOfClass: */ + classFormat = ((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + return pointerForOop((objOop + BaseHeaderSize) + ((((usqInt)((classFormat & ((1U << (fixedFieldsFieldWidth())) - 1))) << (shiftForWord()))))); + } + + /* indexable with inst vars; need to delve into the class format word + otherwise not indexable */ + return 0; + } + + /* All bit objects, and indeed CompiledMethod, though this is a no-no, start at 0 */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat()))) { + return pointerForOop(objOop + BaseHeaderSize); + } + + /* otherwise not indexable */ + return 0; +} + + /* SpurMemoryManager>>#fixedFieldsOfClassFormatMask */ +sqInt +fixedFieldsOfClassFormatMask(void) +{ + return (1U << (fixedFieldsFieldWidth())) - 1; +} + + /* SpurMemoryManager>>#fixedFieldsOfClassFormat: */ +sqInt +fixedFieldsOfClassFormat(sqInt classFormat) +{ + return classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); +} + + /* SpurMemoryManager>>#fixedFieldsOf:format:length: */ +static NoDbgRegParms sqInt +fixedFieldsOfformatlength(sqInt objOop, sqInt fmt, sqInt wordLength) +{ + sqInt class; + + + /* N.B. written to fall through to fetchClassOfNonImm: et al for forwarders + so as to trigger an assert fail. */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + return 0; + } + if (fmt < (arrayFormat())) { + return wordLength; + } + class = fetchClassOfNonImm(objOop); + return (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); +} + + +/* Private helper for followField:ofObject: to avoid code duplication for + rare case. + */ + + /* SpurMemoryManager>>#fixFollowedField:ofObject:withInitialValue: */ +static NoDbgRegParms NeverInline sqInt +fixFollowedFieldofObjectwithInitialValue(sqInt fieldIndex, sqInt anObject, sqInt initialValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + + assert(isOopForwarded(initialValue)); + + /* inlined followForwarded: for speed (one less test) */ + objOop = initialValue; + do { + objOop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + } while(/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, anObject, objOop)); + assert(isNonImmediate(anObject)); + if (oopisGreaterThanOrEqualTo(anObject, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objOop & (tagMask())))) + && (oopisLessThan(objOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(anObject + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(anObject); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((anObject + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),objOop); + return objOop; +} + + +/* Answer the object the ephemeron guards. This is its first element. */ + + /* SpurMemoryManager>>#followedKeyOfEphemeron: */ +static NoDbgRegParms sqInt +followedKeyOfEphemeron(sqInt objOop) +{ + sqInt oop; + + assert((isNonImmediate(objOop)) + && (isEphemeron(objOop))); + + /* begin followOopField:ofObject: */ + oop = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + oop = fixFollowedFieldofObjectwithInitialValue(0, objOop, oop); + } + return oop; +} + + +/* Make sure the oop at fieldIndex in anObject is not forwarded (follow the + forwarder there-in if so). Answer the (possibly followed) oop at + fieldIndex. */ + + /* SpurMemoryManager>>#followField:ofObject: */ +static NoDbgRegParms sqInt +followFieldofObject(sqInt fieldIndex, sqInt anObject) +{ + sqInt objOop; + + objOop = longAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(fieldIndex, anObject, objOop); + } + return objOop; +} + + +/* Follow a forwarding pointer. This must be a loop because we cannot prevent + forwarders to + forwarders being created by lazy become. Consider the following example by + Igor Stasenko: + array := { a. b. c }. + - array at: 1 points to &a. array at: 2 points to &b. array at: 3 points + to &c + a becomeForward: b + - array at: 1 still points to &a. array at: 2 still points to &b. array + at: 3 still points to &c + b becomeForward: c. + - array at: 1 still points to &a. array at: 2 still points to &b. array + at: 3 still points to &c + - when accessing array first one has to follow a forwarding chain: + &a -> &b -> c */ + + /* SpurMemoryManager>>#followForwarded: */ +sqInt +followForwarded(sqInt objOop) +{ + sqInt referent; + + assert(isUnambiguouslyForwarder(objOop)); + referent = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + return referent; +} + + /* SpurMemoryManager>>#followMaybeForwarded: */ +static NoDbgRegParms sqInt +followMaybeForwarded(sqInt objOop) +{ + return (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + ? followForwarded(objOop) + : objOop); +} + + +/* Make sure the obj at fieldIndex in anObject is not forwarded (follow the + forwarder there-in if so). Answer the (possibly followed) obj at + fieldIndex. */ + + /* SpurMemoryManager>>#followObjField:ofObject: */ +static NoDbgRegParms sqInt +followObjFieldofObject(sqInt fieldIndex, sqInt anObject) +{ + sqInt objOop; + + objOop = longAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(fieldIndex, anObject, objOop); + } + return objOop; +} + + /* SpurMemoryManager>>#formatOfClass: */ +sqInt +formatOfClass(sqInt classPointer) +{ + return ((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); +} + + +/* 0 = 0 sized objects (UndefinedObject True False et al) + 1 = non-indexable objects with inst vars (Point et al) + 2 = indexable objects with no inst vars (Array et al) + 3 = indexable objects with inst vars (MethodContext AdditionalMethodState + et al) + 4 = weak indexable objects with inst vars (WeakArray et al) + 5 = weak non-indexable objects with inst vars (ephemerons) (Ephemeron) + 6 unused, reserved for exotic pointer objects? + 7 Forwarded Object, 1st field is pointer, rest of fields are ignored + 8 unused, reserved for exotic non-pointer objects? + 9 64-bit indexable + 10 - 11 32-bit indexable (lsb = # of unused 32-bit fields, 11 unused in 32 + bits) 12 - 15 16-bit indexable (ls2b = # of unused 16-bit fields, 14 & 15 + unused in 32-bits) + 16 - 23 byte indexable (ls3b = # of unused 8-bit fields, 20, 21, 22, 23 + unused in 32-bits) + 24 - 31 compiled method (ls3b = # of unused 8-bit fields, 20, 21, 22, 23 + unused in 32-bits) */ +/* A note on performance. Since the format field is, by design, aligned on a + byte boundary + in the fourth byte of the header (see headerForSlots:format:classIndex:) + it is accessed + as below. But it used to be accessed via + ^(self longAt: objOop) >> self formatShift bitAnd: self formatMask + This potentially involves more operations (a shift) and larger literals, + In practice clang + optimizes the long form to the byte form on x86_64, showing it is to be + preferred on at + least one architecture. On arm64 for example both forms take two + instructions. */ + + /* SpurMemoryManager>>#formatOf: */ +static NoDbgRegParms sqInt +formatOf(sqInt objOop) +{ + return (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); +} + + +/* Answer if anObject is itself forwarded, or is a pointer object containing + any references to forwarded objects. + */ + + /* SpurMemoryManager>>#forwardersIn: */ +static NoDbgRegParms sqInt +forwardersIn(sqInt anObject) +{ + sqInt i; + sqInt oop; + sqInt toDoLimit; + + if ((!((longAt((void *)(anObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + return 1; + } + toDoLimit = (numPointerSlotsOf(anObject)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + oop = longAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + return 1; + } + } + return 0; +} + + +/* for linking objecs on each free list, or, during pigCompact, doubly- + linking the free objects in address order using the xor link hack. */ + + /* SpurMemoryManager>>#freeChunkNextIndex */ +static sqInt +freeChunkNextIndex(void) +{ + return 0; +} + + +/* for organizing the tree of large free chunks. */ + + /* SpurMemoryManager>>#freeChunkParentIndex */ +static sqInt +freeChunkParentIndex(void) +{ + return 2; +} + + +/* For linking objecs on each free list, doubly-linking the free objects. + Free chunks of size 1 do not have a prev index. */ + + /* SpurMemoryManager>>#freeChunkPrevIndex */ +static sqInt +freeChunkPrevIndex(void) +{ + return 1; +} + + /* SpurMemoryManager>>#freeChunkWithBytes:at: */ +static NoDbgRegParms sqInt +freeChunkWithBytesat(sqInt bytes, sqInt address) +{ + sqInt freeChunk; + + assert(isInOldSpace(address)); + assert((segmentContainingObj(address)) == (segmentContainingObj(address + bytes))); + freeChunk = initFreeChunkWithBytesat(bytes, address); + addToFreeListbytes(freeChunk, bytes); + assert(freeChunk == (objectStartingAt(address))); + return freeChunk; +} + + +/* Free an object in oldSpace. Coalesce if possible to reduce fragmentation. */ + + /* SpurMemoryManager>>#freeObject: */ +sqInt +freeObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt bytes; + usqInt chunkBytes; + sqInt index; + sqInt next; + usqInt numSlots; + usqInt start; + + assert(isInOldSpace(objOop)); + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) { + /* begin forgetObject: */ + assert(GIV(rememberedSetSize) > 0); + assert(isRemembered(objOop)); + setIsRememberedOfto(objOop, 0); + if (!(objOop == (GIV(rememberedSet)[GIV(rememberedSetSize) - 1]))) { + index = 0; + while (index < GIV(rememberedSetSize)) { + if (objOop == (GIV(rememberedSet)[index])) { + GIV(rememberedSet)[index] = (GIV(rememberedSet)[GIV(rememberedSetSize) - 1]); + index = GIV(rememberedSetSize); + } + else { + index += 1; + } + } + } + GIV(rememberedSetSize) -= 1; + assert(GIV(rememberedSetSize) >= 0); + } + bytes = bytesInBody(objOop); + start = /* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)((start + bytes) + (numSlotsFieldByteOffset()))); + next = (numSlots == (numSlotsMask()) + ? (start + bytes) + BaseHeaderSize + : start + bytes); + if (((longAt((void *)(next))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + /* begin detachFreeObject: */ + chunkBytes = bytesInBody(next); + GIV(totalFreeOldSpace) -= chunkBytes; + unlinkFreeChunkchunkBytes(next, chunkBytes); + bytes += bytesInBody(next); + } + GIV(totalFreeOldSpace) += bytes; + return freeChunkWithBytesat(bytes, start); +} + + +/* Perform a full eager compacting GC. Answer the size of the largest free + chunk. + */ +/* for profiling */ + + /* SpurMemoryManager>>#fullGC */ +NeverInline usqLong +fullGC(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + usqLong compactionStartUsecs; + usqLong gcMarkEndUsecs; + sqInt i; + sqInt index; + usqInt numSlots; + sqInt obj; + sqInt objOop; + + GIV(needGCFlag) = 0; + GIV(gcStartUsecs) = ioUTCMicrosecondsNow(); + GIV(statMarkCount) = 0; + preGCAction(GCModeFull); + + /* begin globalGarbageCollect */ + assert(validObjStacks()); + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + + /* Mark objects /before/ scavenging, to empty the rememberedTable of unmarked roots. */ + markObjects(1); + gcMarkEndUsecs = ioUTCMicrosecondsNow(); + + /* begin forgetUnmarkedRememberedObjects */ + index = 0; + while (index < GIV(rememberedSetSize)) { + obj = GIV(rememberedSet)[index]; + if ((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + index += 1; + } + else { + setIsRememberedOfto(obj, 0); + GIV(rememberedSetSize) -= 1; + GIV(rememberedSet)[index] = (GIV(rememberedSet)[GIV(rememberedSetSize)]); + } + } + assert(GIV(rememberedSetSize) >= 0); + + /* begin setGCMode: */ + GIV(gcMode) = GCModeNewSpace; + doScavenge(MarkOnTenure); + + /* begin setGCMode: */ + GIV(gcMode) = GCModeFull; + + /* Mid-way the leak check must be more lenient. Unmarked classes will have been + expunged from the table, but unmarked instances will not yet have been reclaimed. */ + runLeakCheckerForexcludeUnmarkedObjsclassIndicesShouldBeValid(GCModeFull, 1, 1); + compactionStartUsecs = ioUTCMicrosecondsNow(); + + /* begin prepareForGlobalSweep */ + for (i = 0; i < GIV(numSegments); i += 1) { + ((GIV(segments)[i]).containsPinned = 0); + } + + /* begin bridgeAt: */ + /* begin bridgeFor: */ + address = (((((&(GIV(segments)[GIV(numSegments) - 1])))->segSize)) + ((((&(GIV(segments)[GIV(numSegments) - 1])))->segStart))) - (2 * BaseHeaderSize); + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + compact(); + + /* begin attemptToShrink */ + if ((GIV(totalFreeOldSpace) > GIV(shrinkThreshold)) + && ((GIV(totalFreeOldSpace) > GIV(growHeadroom)) + && (shrinkObjectMemory((((GIV(totalFreeOldSpace) - GIV(growHeadroom)) < GIV(growHeadroom)) ? GIV(growHeadroom) : (GIV(totalFreeOldSpace) - GIV(growHeadroom))))))) { + GIV(statShrinkMemory) += 1; + } + + /* begin setHeapSizeAtPreviousGC */ + GIV(heapSizeAtPreviousGC) = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + + /* begin resetAllocationAccountingAfterGC */ + GIV(oldSpaceUsePriorToScavenge) = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + assert(validObjStacks()); + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + assert(allObjectsUnmarked()); + runLeakCheckerFor(GCModeFull); + postGCAction(GCModeFull); + GIV(statGCEndUsecs) = ioUTCMicrosecondsNow(); + + /* begin updateFullGCStats */ + GIV(statFullGCs) += 1; + GIV(statFullGCUsecs) += GIV(statGCEndUsecs) - GIV(gcStartUsecs); + GIV(statCompactionUsecs) += GIV(statGCEndUsecs) - compactionStartUsecs; + if (GIV(gcSweepEndUsecs)) { + GIV(statSweepUsecs) += GIV(gcSweepEndUsecs) - compactionStartUsecs; + } + GIV(statMarkUsecs) += gcMarkEndUsecs - GIV(gcStartUsecs); + return (GIV(freeLists)[0] + ? bytesInBody(findLargestFreeChunk()) + : 0); +} + + /* SpurMemoryManager>>#getHeapGrowthToSizeGCRatio */ +static float +getHeapGrowthToSizeGCRatio(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(heapGrowthToSizeGCRatio); +} + + /* SpurMemoryManager>>#goodContextSize: */ +static NoDbgRegParms sqInt +goodContextSize(sqInt oop) +{ + usqInt numSlots; + usqInt numSlotsUsqInt; + + /* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + return (numSlots == SmallContextSlots) + || (numSlots == LargeContextSlots); +} + + +/* Attempt to grow memory by at least minAmmount. + Answer the size of the new segment, or nil if the attempt failed. */ + + /* SpurMemoryManager>>#growOldSpaceByAtLeast: */ +static NoDbgRegParms usqInt +growOldSpaceByAtLeast(sqInt minAmmount) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt ammount; + sqInt bytes; + sqInt headroom; + sqInt interval; + SpurSegmentInfo *segInfo; + sqInt start; + usqInt total; + + + /* statGrowMemory counts attempts, not successes. */ + /* we need to include overhead for a new object header plus the segment bridge. */ + GIV(statGrowMemory) += 1; + ammount = minAmmount + ((BaseHeaderSize * 2) + (2 * BaseHeaderSize)); + + /* round up to the nearest power of two. */ + ammount = 1ULL << (highBit(ammount - 1)); + + /* and grow by at least growHeadroom. */ + ammount = ((ammount < GIV(growHeadroom)) ? GIV(growHeadroom) : ammount); + + /* Now apply the maxOldSpaceSize limit, if one is in effect. */ + if (maxOldSpaceSize > 0) { + /* begin totalOldSpaceSize */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)); + total = GIV(totalHeapSizeIncludingBridges); + if (total >= maxOldSpaceSize) { + GIV(needGCFlag) = 1; + return null; + } + headroom = maxOldSpaceSize - total; + if (headroom < ammount) { + if (headroom < (minAmmount + ((BaseHeaderSize * 2) + (2 * BaseHeaderSize)))) { + GIV(needGCFlag) = 1; + return null; + } + ammount = headroom; + } + } + start = ioUTCMicrosecondsNow(); + if ((segInfo = addSegmentOfSize(ammount))) { + /* begin assimilateNewSegment: */ + if ((((segInfo->segSize)) + ((segInfo->segStart))) >= GIV(endOfMemory)) { + GIV(freeOldSpaceStart) = (GIV(endOfMemory) = (((segInfo->segSize)) + ((segInfo->segStart))) - (2 * BaseHeaderSize)); + } + + /* and add the new free chunk to the free list; done here + instead of in assimilateNewSegment: for the assert */ + bytes = ((segInfo->segSize)) - (2 * BaseHeaderSize); + address = (segInfo->segStart); + + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += bytes; + freeChunkWithBytesat(bytes, address); + assert((addressAfter(objectStartingAt((segInfo->segStart)))) == ((segLimit(segInfo)) - (bridgeSize()))); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCCheckFreeSpace)) == (GCCheckFreeSpace | GCCheckFreeSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + checkSegments(); + interval = (ioUTCMicrosecondsNow()) - start; + if (interval > GIV(statMaxAllocSegmentTime)) { + GIV(statMaxAllocSegmentTime) = interval; + } + return (segInfo->segSize); + } + GIV(needGCFlag) = 1; + return null; +} + + /* SpurMemoryManager>>#hasPointerFieldsNonImm: */ +static NoDbgRegParms sqInt +hasPointerFieldsNonImm(sqInt oop) +{ + sqInt format; + + format = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isAnyPointerFormat: */ + return (format <= 5 /* lastPointerFormat */) + || (format >= (firstCompiledMethodFormat())); +} + + +/* The header format in LSB is + MSB: | 8: numSlots | (on a byte boundary) + | 2 bits | (msb,lsb = {isMarked,isGrey}) + | 22: identityHash | (on a word boundary) + | 3 bits | (msb <-> lsb = {UNUSED,isPinned,isRemembered} + | 5: format | (on a byte boundary) + | 2 bits | (msb,lsb = {isImmutable,isExtraAligned}) + | 22: classIndex | (on a word boundary) : LSB + The 7 bit flags are used (or reserved) for + isExtraAligned (bit 22) + isImmutable (bit 23) + isRemembered (bit 29) + isPinned (bit 30) + UNUSED (bit 31) + isGrey (bit 54) + isMarked (bit 55) + The three bit field { UNUSED, isPinned, isRemembered } is for bits that + are never + set in young objects (forcing uses of the UNUSED bit to be for oldSPace + objects only). + This allows the remembered table to be pruned when full by using these + bits as a + reference count of newSpace objects from the remembered table. Objects + with a + high count should be tenured to prune the remembered table. */ + + /* SpurMemoryManager>>#headerForSlots:format:classIndex: */ +usqLong +headerForSlotsformatclassIndex(sqInt numSlots, sqInt formatField, sqInt classIndex) +{ + return ((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex; +} + + +/* The header format in LSB is + MSB: | 8: numSlots | (on a byte boundary) + | 2 bits | (msb,lsb = {isMarked,isGrey}) + | 22: identityHash | (on a word boundary) + | 3 bits | (msb <-> lsb = {UNUSED,isPinned,isRemembered} + | 5: format | (on a byte boundary) + | 2 bits | (msb,lsb = {isImmutable,isExtraAligned}) + | 22: classIndex | (on a word boundary) : LSB + The 7 bit flags are used (or reserved) for + isExtraAligned (bit 22) + isImmutable (bit 23) + isRemembered (bit 29) + isPinned (bit 30) + UNUSED (bit 31) + isGrey (bit 54) + isMarked (bit 55) + The three bit field { UNUSED, isPinned, isRemembered } is for bits that + are never + set in young objects (forcing uses of the UNUSED bit to be for oldSPace + objects only). + This allows the remembered table to be pruned when full by using these + bits as a + reference count of newSpace objects from the remembered table. Objects + with a + high count should be tenured to prune the remembered table. */ + + /* SpurMemoryManager>>#headerForSlots:hash:format:classIndex: */ +static NoDbgRegParms usqLong +headerForSlotshashformatclassIndex(sqInt numSlots, sqInt hash, sqInt formatField, sqInt classIndex) +{ + return (((((((usqLong) numSlots)) << (numSlotsFullShift()))) + (((((usqLong) hash)) << (identityHashFullWordShift())))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex; +} + + +/* Answer the number of extra root slots in the root of the hidden root + object. + */ + + /* SpurMemoryManager>>#hiddenRootSlots */ +static sqInt +hiddenRootSlots(void) +{ + return 8; +} + + /* SpurMemoryManager>>#hiddenRootsObject */ +static sqInt +hiddenRootsObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(hiddenRootsObj); +} + + +/* a more complex version that tells both the word reversal and the + endianness of the machine + it came from. Low half of word is e.g. 6521. Top byte is top byte of + #doesNotUnderstand: on + this machine. ($d on the Mac or $s on the PC) */ + + /* SpurMemoryManager>>#imageSegmentVersion */ +static sqInt +imageSegmentVersion(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int wholeWord; + + /* first data word, 'does' */ + wholeWord = long32At((void *)((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord()))))))) + BaseHeaderSize)); + return 68021 /* imageFormatVersion */ | (wholeWord & 0xFF000000U); +} + + +/* mask the immutable bit in the base header word */ + + /* SpurMemoryManager>>#immutableBitMask */ +#if IMMUTABILITY +sqInt +immutableBitMask(void) +{ + return 1U << (immutableBitShift()); +} +#endif /* IMMUTABILITY */ + + /* SpurMemoryManager>>#indexOf:in: */ +sqInt +indexOfin(sqInt anElement, sqInt anObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt fmtSqInt; + sqInt i; + usqInt numSlots; + usqInt numSlotsUsqInt; + + fmt = (byteAt((void *)(anObject + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(anObject)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(anObject + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(anObject - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + for (i = 0; i <= numSlots; i += 1) { + if (anElement == (longAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + return i; + } + } + } + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + return (GIV(primFailCode) = PrimErrUnsupported); + } + + /* begin numBytesOfBytes: */ + fmtSqInt = (byteAt((void *)(anObject + (formatFieldByteOffset())))) & (formatMask()); + assert(fmtSqInt >= (firstByteFormat())); + numSlots = ((((/* begin numSlotsOf: */ + assert((classIndexOf(anObject)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(anObject + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(anObject - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) << (shiftForWord()))) - (fmtSqInt & 7); + for (i = 0; i <= numSlots; i += 1) { + if (anElement == (byteAt((void *)((anObject + BaseHeaderSize) + i)))) { + return i; + } + } + } + if (fmt >= (firstShortFormat())) { + numSlots = ((usqInt)((numBytesOf(anObject)))) >> 1; + for (i = 0; i <= numSlots; i += 1) { + if (anElement == (((unsigned short) (shortAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(i) << 1))))))))) { + return i; + } + } + } + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(anObject)))) >> 3; + for (i = 0; i <= numSlots; i += 1) { + if (anElement == (long64At((void *)((anObject + BaseHeaderSize) + ((((usqInt)(i) << 3))))))) { + return i; + } + } + } + if (fmt >= (firstLongFormat())) { + numSlots = ((usqInt)((numBytesOf(anObject)))) >> 2; + for (i = 0; i <= numSlots; i += 1) { + if (anElement == (long32At((void *)((anObject + BaseHeaderSize) + ((((usqInt)(i) << 2))))))) { + return i; + } + } + } + return -1; +} + + +/* Part of reorderReversedTreeList:. Switch treeNode with newNode in + the tree, but do nothing to the list linked through freeChunkNextIndex. */ + + /* SpurMemoryManager>>#inFreeTreeReplace:with: */ +static NoDbgRegParms void +inFreeTreeReplacewith(sqInt treeNode, sqInt newNode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fieldIndex; + sqInt i; + sqInt relative; + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(newNode)); + longAtput((void *)((newNode + BaseHeaderSize) + (1U << (shiftForWord()))),0); + + /* copy parent, smaller, larger */ + for (i = 2 /* freeChunkParentIndex */; i <= 4 /* freeChunkLargerIndex */; i += 1) { + relative = longAt((void *)((treeNode + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (i == 2 /* freeChunkParentIndex */) { + if (relative) { + fieldIndex = (treeNode == (longAt((void *)((relative + BaseHeaderSize) + (3U << (shiftForWord()))))) + ? 3 /* freeChunkSmallerIndex */ + : 4 /* freeChunkLargerIndex */); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(relative)); + assert((newNode == 0) + || (isFreeObject(newNode))); + longAtput((void *)((relative + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),newNode); + } + else { + assert((GIV(freeLists)[0]) == treeNode); + GIV(freeLists)[0] = newNode; + } + } + else { + if (relative) { + assert((fetchPointerofFreeChunk(freeChunkParentIndex(), relative)) == treeNode); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(relative)); + assert((newNode == 0) + || (isFreeObject(newNode))); + longAtput((void *)((relative + BaseHeaderSize) + (2U << (shiftForWord()))),newNode); + } + } + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(newNode)); + assert((relative == 0) + || (isFreeObject(relative))); + longAtput((void *)((newNode + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),relative); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(treeNode)); + longAtput((void *)((treeNode + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),0); + } +} + + /* SpurMemoryManager>>#initialInstanceOf: */ +static NoDbgRegParms sqInt +initialInstanceOf(sqInt classObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int classIndex; + int classIndexInt; + sqInt classIndexSqInt; + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + + classIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + if (classIndex) { + /* flush instances in newSpace to settle the enumeration. */ + + /* begin flushNewSpaceInstancesOf: */ + classIndexInt = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + if (!classIndexInt) { + goto l1; + } + + /* no instances; nothing to do */ + + /* begin tenuringClassIndex: */ + GIV(tenuringClassIndex) = classIndexInt; + scavengingGCTenuringIf(TenureByClass); + assert(!(existInstancesInNewSpaceOf(classObj))); + /* end flushNewSpaceInstancesOf: */ +l1: + + /* scan old space for the first one found. */ + + /* begin allOldSpaceObjectsDo: */ + /* begin allOldSpaceObjectsFrom:do: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndexSqInt = (longAt((void *)(objOopSqInt))) & (classIndexMask()); + assert((classIndexSqInt == (segmentBridgePun())) + || ((classIndexSqInt == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objOopSqInt))) != 0) + && (classIndexSqInt < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndexSqInt >= (isForwardedObjectClassIndexPun())) { + if (classIndex == ((longAt((void *)(objOopSqInt))) & (classIndexMask()))) { + return objOopSqInt; + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l2:; + } + } + return null; +} + + /* SpurMemoryManager>>#initializeNewSpaceVariables */ +static NeverInline void +initializeNewSpaceVariables(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(freeStart) = ((GIV(eden)).start); + GIV(pastSpaceStart) = ((GIV(pastSpace)).start); + GIV(scavengeThreshold) = ((((GIV(eden)).limit)) - ((((GIV(eden).limit)) - ((GIV(eden).start))) / 64)) - (interpreterAllocationReserveBytes()); + GIV(newSpaceStart) = (((((GIV(pastSpace)).start)) < (((GIV(futureSpace)).start))) ? (((GIV(pastSpace)).start)) : (((GIV(futureSpace)).start))); + assert(GIV(newSpaceStart) < (((GIV(eden)).start))); +} + + +/* Initialize object memory variables at startup time. Assume endOfMemory at + al are + initialised by the image-reading code via + setHeapBase:memoryLimit:endOfMemory:. endOfMemory is assumed to point to + the end of the last object in the image. + Assume: image reader also initializes the following variables: + specialObjectsOop + lastHash */ +/* Catch mis-initializations leading to bad translations to C */ + + /* SpurMemoryManager>>#initializeObjectMemory: */ +static NoDbgRegParms void +initializeObjectMemory(sqInt bytesToShift) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anOop; + sqInt cameFrom; + usqInt chunkBytes; + sqInt classIndex; + sqInt classTableRoot; + sqInt field; + sqInt fieldAddr; + sqInt fieldOop; + sqInt firstObj; + sqInt followingWord; + usqInt followingWordAddress; + sqInt freeChunk; + sqInt freeListObj; + usqInt freeOldStart; + sqInt fudge; + sqInt i; + sqInt index; + sqInt j; + sqInt largeChild; + usqInt limit; + sqInt min; + sqInt nilObjPreSwizzle; + usqInt numSlots; + sqInt obj; + usqInt p; + sqInt page; + sqInt smallChild; + usqInt startOfFreeOldSpace; + usqInt toDoLimit; + sqInt treeNode; + sqInt valuePointer; + + assert(BaseHeaderSize == BaseHeaderSize); + assert((((sqInt)((maxSlotsForAlloc()) * BytesPerWord))) > 0); + initSegmentBridgeWithBytesat(2 * BaseHeaderSize, GIV(endOfMemory) - (2 * BaseHeaderSize)); + adjustSegmentSwizzlesBy(bytesToShift); + + /* image may be at a different address; adjust oops for new location */ + + /* begin adjustAllOopsBy: */ + assert(newSpaceIsEmpty()); + numSlots = byteAt((void *)(GIV(oldSpaceStart) + (numSlotsFieldByteOffset()))); + firstObj = (numSlots == (numSlotsMask()) + ? GIV(oldSpaceStart) + BaseHeaderSize + : GIV(oldSpaceStart)); + classTableRoot = oldSpaceObjectAfter(oldSpaceObjectAfter(oldSpaceObjectAfter(oldSpaceObjectAfter(firstObj)))); + nilObjPreSwizzle = GIV(oldSpaceStart) - bytesToShift; + + /* begin numSlotsOf: */ + assert((classIndexOf(classTableRoot)) > (isForwardedObjectClassIndexPun())); + GIV(numClassTablePages) = (((numSlots = byteAt((void *)(classTableRoot + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classTableRoot - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + assert(GIV(numClassTablePages) == ((classTableRootSlots()) + (hiddenRootSlots()))); + for (i = 2; i < GIV(numClassTablePages); i += 1) { + if ((longAt((void *)((classTableRoot + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))))) == nilObjPreSwizzle) { + GIV(numClassTablePages) = i; + goto l4; + } + } + /* end countNumClassPagesPreSwizzle: */ +l4: + if ((bytesToShift != 0) + || (GIV(numSegments) > 1)) { + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(GIV(oldSpaceStart) + (numSlotsFieldByteOffset()))); + obj = (numSlots == (numSlotsMask()) + ? GIV(oldSpaceStart) + BaseHeaderSize + : GIV(oldSpaceStart)); + while (oopisLessThan(obj, GIV(freeOldSpaceStart))) { + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + /* begin swizzleFieldsOfObject: */ + fieldAddr = obj + (lastPointerOfWhileSwizzling(obj)); + while (oopisGreaterThanOrEqualTo(fieldAddr, obj + BaseHeaderSize)) { + fieldOop = longAt((void *)(fieldAddr)); + if ((!(fieldOop & (tagMask())))) { + longAtput((void *)(fieldAddr),swizzleObj(fieldOop)); + } + fieldAddr -= BytesPerOop; + } + } + else { + if (classIndex == (isFreeObjectClassIndexPun())) { + /* begin swizzleFieldsOfFreeChunk: */ + field = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + if (field) { + valuePointer = swizzleObj(field); + + /* begin storeUncheckedNoAssert:ofAny:withValue: */ + longAtput((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + } + chunkBytes = bytesInBody(obj); + + /* The prevPointer is not guaranteed to be valid in older images. + updateListStartingAt: via updateFreeLists does restore the prev pointer + in all small free lists, so simply avoid swizzling it now. */ + if (chunkBytes >= 0x200 /* (numFreeLists * #allocationUnit) */) { + for (index = 2 /* freeChunkParentIndex */; index <= 4 /* freeChunkLargerIndex */; index += 1) { + field = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (field) { + valuePointer = swizzleObj(field); + + /* begin storeUncheckedNoAssert:ofAny:withValue: */ + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),valuePointer); + } + } + } + } + } + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3:; + } + } + GIV(specialObjectsOop) = swizzleObj(GIV(specialObjectsOop)); + + /* heavily used special objects */ + GIV(nilObj) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(NilObject) << (shiftForWord())))))); + GIV(falseObj) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(FalseObject) << (shiftForWord())))))); + GIV(trueObj) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TrueObject) << (shiftForWord())))))); + + /* In Cog we insist that nil, true & false are next to each other (Cogit generates tighter + conditional branch code as a result). In addition, Spur places the free lists and + class table root page immediately following them. */ + assert(GIV(nilObj) == GIV(oldSpaceStart)); + assert(GIV(falseObj) == (oldSpaceObjectAfter(GIV(nilObj)))); + assert(GIV(trueObj) == (oldSpaceObjectAfter(GIV(falseObj)))); + freeListObj = oldSpaceObjectAfter(GIV(trueObj)); + anOop = oldSpaceObjectAfter(freeListObj); + + /* begin setHiddenRootsObj: */ + GIV(hiddenRootsObj) = anOop; + assert(validClassTableRootPages()); + GIV(classTableFirstPage) = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(((numSlotsOf(GIV(classTableFirstPage))) - 1) == (classTableMinorIndexMask())); + + /* Hack fix. A bug in markAndTraceClassOf: caused the class of the first class table page + to be changed from its pun. This can be restored manually, but we do it here too. */ + if (((longAt((void *)(GIV(classTableFirstPage)))) & (classIndexMask())) != (arrayClassIndexPun())) { + /* begin setClassIndexOf:to: */ + assert((((arrayClassIndexPun()) >= 0) && ((arrayClassIndexPun()) <= (classIndexMask())))); + longAtput((void *)(GIV(classTableFirstPage)),((longAt((void *)(GIV(classTableFirstPage)))) & (~(usqIntptr_t)(classIndexMask()))) + (arrayClassIndexPun())); + } + GIV(numClassTablePages) = 1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift())); + + /* If loading an image, set the classTableIndex to the first unused slot in the class table after the first page. + Set numClassTablePages to the number of used pages. + Set classTableIndex to point at the first unused entry. First set it to the max as a sentinel. */ + GIV(classTableIndex) = ((sqInt)((usqInt)(GIV(numClassTablePages)) << (classTableMajorIndexShift()))); + for (i = 1; i < GIV(numClassTablePages); i += 1) { + if (((page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) == GIV(nilObj)) { + if ((((usqInt)(GIV(classTableIndex))) >> (classTableMajorIndexShift())) > i) { + GIV(classTableIndex) = ((sqInt)((usqInt)(((((i - 1) < 1) ? 1 : (i - 1)))) << (classTableMajorIndexShift()))); + } + GIV(numClassTablePages) = i; + assert((classOrNilAtIndex(GIV(classTableIndex))) == GIV(nilObj)); + goto l1; + } + else { + if ((((usqInt)(GIV(classTableIndex))) >> (classTableMajorIndexShift())) > i) { + j = 0; + while (j < (1U << (classTableMajorIndexShift()))) { + if ((longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord()))))))) == GIV(nilObj)) { + GIV(classTableIndex) = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + j = 1U << (classTableMajorIndexShift()); + } + j += 1; + } + } + } + } + + /* no unused slots; set it to the start of the second page. */ + if ((((usqInt)(GIV(classTableIndex))) >> (classTableMajorIndexShift())) >= GIV(numClassTablePages)) { + GIV(classTableIndex) = 1U << (classTableMajorIndexShift()); + } + assert((classOrNilAtIndex(GIV(classTableIndex))) == GIV(nilObj)); + /* end setHiddenRootsObj: */ +l1: + GIV(markStack) = swizzleObjStackAt(MarkStackRootIndex); + GIV(weaklingStack) = swizzleObjStackAt(WeaklingStackRootIndex); + GIV(mournQueue) = swizzleObjStackAt(MournQueueRootIndex); + assert(validObjStacks()); + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + + /* begin initializeFreeSpacePostLoad: */ + assert((numSlotsOf(freeListObj)) == (numFreeLists())); + assert((formatOf(freeListObj)) == (wordIndexableFormat())); + GIV(freeLists) = firstIndexableField(freeListObj); + GIV(freeListsMask) = 0; + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + if (GIV(freeLists)[i]) { + GIV(freeListsMask) = GIV(freeListsMask) | (1ULL << i); + GIV(freeLists)[i] = (swizzleObj(GIV(freeLists)[i])); + } + } + collapseSegmentsPostSwizzle(); + + /* begin updateFreeLists */ + min = 3; + for (i = min; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + updateListStartingAt(GIV(freeLists)[i]); + } + + /* Large chunks */ + + /* begin freeTreeNodesDo: */ + treeNode = GIV(freeLists)[0]; + if (!treeNode) { + goto l2; + } + cameFrom = -1; + do { + assert((bytesInBody(treeNode)) >= ((numFreeLists()) * (allocationUnit()))); + smallChild = longAt((void *)((treeNode + BaseHeaderSize) + (3U << (shiftForWord())))); + largeChild = longAt((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + assert((smallChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), smallChild)))); + assert((largeChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), largeChild)))); + + /* apply if the node has no children, or it has no large children and we're + returning from the small child, or we're returning from the large child. */ + if (((smallChild == 0) + && (largeChild == 0)) + || ((largeChild + ? cameFrom == largeChild + : cameFrom == smallChild))) { + updateListStartingAt(treeNode); + + /* and since we've applied we must move on up */ + cameFrom = treeNode; + treeNode = longAt((void *)((treeNode + BaseHeaderSize) + (2U << (shiftForWord())))); + } + else { + if ((smallChild != 0) + && (cameFrom != smallChild)) { + treeNode = smallChild; + } + else { + assert(largeChild != 0); + treeNode = largeChild; + } + cameFrom = -1; + } + } while(treeNode != 0); + /* end freeTreeNodesDo: */ +l2: + + /* begin computeFreeSpacePostSwizzle */ + GIV(totalFreeOldSpace) = totalFreeListBytes(); + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + startOfFreeOldSpace = GIV(freeOldSpaceStart); + + /* begin initializeOldSpaceFirstFree: */ + limit = GIV(endOfMemory) - (2 * BaseHeaderSize); + if (limit > startOfFreeOldSpace) { + GIV(totalFreeOldSpace) += limit - startOfFreeOldSpace; + freeOldStart = startOfFreeOldSpace; + while ((limit - freeOldStart) >= (0x100000000LL)) { + freeChunk = freeChunkWithBytesat(0x100000000LL, freeOldStart); + freeOldStart += 0x100000000LL; + assert(freeOldStart == (addressAfter(freeChunk))); + } + if (freeOldStart < limit) { + freeChunk = freeChunkWithBytesat(limit - freeOldStart, freeOldStart); + assert((addressAfter(freeChunk)) == limit); + } + } + GIV(endOfMemory) -= 2 * BaseHeaderSize; + GIV(freeOldSpaceStart) = GIV(endOfMemory); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCCheckFreeSpace)) == (GCCheckFreeSpace | GCCheckFreeSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + initializeNewSpaceVariables(); + + /* begin initializeRememberedSet */ + obj = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))); + if (obj == GIV(nilObj)) { + /* begin allocatePinnedSlots: */ + obj = allocateSlotsForPinningInOldSpacebytesformatclassIndex(0x400, (1024U << (shiftForWord())) + (BaseHeaderSize + BaseHeaderSize), sixtyFourBitIndexableFormat(), sixtyFourBitLongsClassIndexPun()); + if (obj) { + assert(isPinned(obj)); + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((obj + BaseHeaderSize) + (0x400 * BytesPerOop)) - 1, addressAfter(obj))); + toDoLimit = ((usqInt)(((obj + BaseHeaderSize) + (0x400 * BytesPerOop)) - 1)); + for (p = (((usqInt)(obj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + } + + /* begin rememberedSetObj: */ + assert(isOldObject(obj)); + assert((isNonImmediate(GIV(hiddenRootsObj))) + && (!(isForwarded(GIV(hiddenRootsObj))))); + assert(validStorePointerUncheckedArgs(RememberedSetRootIndex, GIV(hiddenRootsObj), obj)); + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord()))))),obj); + } + else { + /* begin setFormatOf:to: */ + assert((((sixtyFourBitIndexableFormat()) >= 0) && ((sixtyFourBitIndexableFormat()) <= (formatMask())))); + byteAtput((void *)(obj + (formatFieldByteOffset())),((byteAt((void *)(obj + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (sixtyFourBitIndexableFormat())); + } + + /* The Spur32to64BitBootstrap failed to set the type of rememberedSetObj to 64-bit indexability. + This is unimportant except for simulation; rememberedSet is declared as sqInt *, but to have + firstIndexableField: below answer a suitable type the format must be wordIndexableFormat. */ + assert((formatOf(obj)) == (wordIndexableFormat())); + assert(isPinned(obj)); + GIV(rememberedSet) = firstIndexableField(obj); + GIV(rememberedSetSize) = 0; + + /* begin numSlotsOf: */ + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())); + GIV(rememberedSetLimit) = (((numSlots = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + + /* begin setRememberedSetRedZone */ + fudge = ((((GIV(eden).limit)) - ((GIV(eden).start))) / BytesPerWord) / 0x400; + GIV(rememberedSetRedZone) = ((((GIV(rememberedSetLimit) * 3) / 4) < fudge) ? fudge : ((GIV(rememberedSetLimit) * 3) / 4)); + checkSegments(); + + /* begin biasForGC */ + GIV(biasForGC) = 1; + + /* These defaults should depend on machine size; e.g. too small on a powerful laptop, too big on a Pi. */ + + /* headroom when growing */ + GIV(growHeadroom) = 0x1000000; + + /* free space before shrinking */ + GIV(shrinkThreshold) = 0x2000000; + GIV(heapSizeAtPreviousGC) = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + + /* begin resetAllocationAccountingAfterGC */ + GIV(oldSpaceUsePriorToScavenge) = ((assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges) - (GIV(numSegments) * (2 * BaseHeaderSize)))) - GIV(totalFreeOldSpace); + + /* By default GC after scavenge if heap has grown by a third since the last GC */ + GIV(heapGrowthToSizeGCRatio) = 0.333333; +} + + +/* Initialize unscannedEphemerons to use the largest unused new space, + which ever is the larger. We haven't yet scavenged, so eden may be + almost full, but futureSpace is known to be empty. N.B. we *cannot* + use free old space because it may be used to grow the markStack. */ + + /* SpurMemoryManager>>#initializeUnscannedEphemerons */ +static void +initializeUnscannedEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt sizeOfFutureSpace; + sqInt sizeOfUnusedEden; + + sizeOfUnusedEden = (((GIV(eden)).limit)) - GIV(freeStart); + sizeOfFutureSpace = (((GIV(futureSpace)).limit)) - (((GIV(futureSpace)).start)); + if (sizeOfUnusedEden > sizeOfFutureSpace) { + (GIV(unscannedEphemerons).start = GIV(freeStart)); + (GIV(unscannedEphemerons).limit = ((GIV(eden)).limit)); + } + else { + (GIV(unscannedEphemerons).start = ((GIV(futureSpace)).start)); + (GIV(unscannedEphemerons).limit = ((GIV(futureSpace)).limit)); + } + (GIV(unscannedEphemerons).top = ((GIV(unscannedEphemerons).start)) - BytesPerOop); + assert(numUnscannedEphemerons() == 0); +} + + +/* print free chunks in freeTree in order. */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#inOrderPrintFreeTree:printList: */ +void +inOrderPrintFreeTreeprintList(sqInt freeChunk, sqInt printNextList) +{ + sqInt next; + + if ((next = longAt((void *)((freeChunk + BaseHeaderSize) + (3U << (shiftForWord())))))) { + inOrderPrintFreeTreeprintList(next, printNextList); + } + printFreeChunkprintAsTreeNode(freeChunk, 1); + if (printNextList) { + next = freeChunk; + while (((next = longAt((void *)((next + BaseHeaderSize) + (0U << (shiftForWord())))))) != 0) { + /* begin tab */ + printChar('\t'); + printFreeChunkprintAsTreeNode(next, 0); + } + } + if ((next = longAt((void *)((freeChunk + BaseHeaderSize) + (4U << (shiftForWord())))))) { + inOrderPrintFreeTreeprintList(next, printNextList); + } +} + + +/* in an effort to fix a compiler bug with two-way become post r3427 */ +/* Do become in place by swapping object contents. */ + + /* SpurMemoryManager>>#inPlaceBecome:and:copyHashFlag: */ +static NoDbgRegParms NeverInline void +inPlaceBecomeandcopyHashFlag(sqInt obj1, sqInt obj2, sqInt copyHashFlag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqLong hashBits; + sqLong header1; + sqLong header2; + sqInt i; + usqInt numSlots; + sqInt o1HasYoung; + sqInt o2HasYoung; + sqLong remembered; + sqInt temp1; + sqInt temp2; + sqInt toDoLimit; + + assert((numSlotsOf(obj1)) == (numSlotsOf(obj2))); + assert(((rawHashBitsOf(obj1)) == 0) + || ((classOrNilAtIndex(rawHashBitsOf(obj1))) != obj1)); + assert(((rawHashBitsOf(obj2)) == 0) + || ((classOrNilAtIndex(rawHashBitsOf(obj2))) != obj2)); + + /* swap headers, but swapping headers swaps remembered bits and hashes; + remembered bits must be unswapped and hashes may be unswapped if + copyHash is false. */ + + /* begin cleverSwapHeaders:and:copyHashFlag: */ + header1 = long64At((void *)(obj1)); + header2 = long64At((void *)(obj2)); + remembered = (header1 ^ header2) & (1U << (rememberedBitShift())); + if (remembered) { + header1 = header1 ^ remembered; + header2 = header2 ^ remembered; + } + + /* swapping headers swaps hash; if not copyHashFlag then unswap hash */ + if (!copyHashFlag) { + hashBits = (header1 ^ header2) & 0x3FFFFF00000000LL /* identityHashFullWordMask */; + if (hashBits) { + header1 = header1 ^ hashBits; + header2 = header2 ^ hashBits; + } + } + long64Atput((void *)(obj1),header2); + long64Atput((void *)(obj2),header1); + o1HasYoung = (o2HasYoung = 0); + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(obj1)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(obj1 + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj1 - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + temp1 = longAt((void *)((obj1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + temp2 = longAt((void *)((obj2 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(obj1)) + && (!(isForwarded(obj1)))); + assert(validStorePointerUncheckedArgs(i, obj1, temp2)); + longAtput((void *)((obj1 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),temp2); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(obj2)) + && (!(isForwarded(obj2)))); + assert(validStorePointerUncheckedArgs(i, obj2, temp1)); + longAtput((void *)((obj2 + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),temp1); + if (/* isYoung: */ + ((!(temp2 & (tagMask())))) + && (oopisLessThan(temp2, GIV(oldSpaceStart)))) { + o1HasYoung = 1; + } + if (/* isYoung: */ + ((!(temp1 & (tagMask())))) + && (oopisLessThan(temp1, GIV(oldSpaceStart)))) { + o2HasYoung = 1; + } + } + if (o1HasYoung + && ((/* begin isOldObject: */ + assert(isNonImmediate(obj1)), + oopisGreaterThanOrEqualTo(obj1, GIV(oldSpaceStart))))) { + fmt = (byteAt((void *)(obj1 + (formatFieldByteOffset())))) & (formatMask()); + if (!(/* isPureBitsFormat: */ + (fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat())))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj1 + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj1); + } + } + } + if (o2HasYoung + && ((/* begin isOldObject: */ + assert(isNonImmediate(obj2)), + oopisGreaterThanOrEqualTo(obj2, GIV(oldSpaceStart))))) { + fmt = (byteAt((void *)(obj2 + (formatFieldByteOffset())))) & (formatMask()); + if (!(/* isPureBitsFormat: */ + (fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat())))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj2 + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj2); + } + } + } +} + + +/* Answer the number of slots in a class. For example the instanceSizeOf: + ClassPoint is 2, for the x & y slots. The instance size of non-pointer + classes is 0. */ + + /* SpurMemoryManager>>#instanceSizeOf: */ +sqInt +instanceSizeOf(sqInt classObj) +{ + assert(addressCouldBeClassObj(classObj)); + return (((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); +} + + /* SpurMemoryManager>>#instantiateClass: */ +static NoDbgRegParms sqInt +instantiateClass(sqInt classObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classFormat; + sqInt classIndex; + sqInt err; + int hash; + sqInt instSpec; + sqInt newObj; + usqInt newObjUsqInt; + usqInt numBytes; + sqInt numSlots; + usqInt p; + usqInt toDoLimit; + + classFormat = ((longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3); + instSpec = (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); + if (!(/* isFixedSizePointerFormat: */ + (instSpec <= (nonIndexablePointerFormat())) + || (instSpec == (ephemeronFormat())))) { + return null; + } + + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classObj)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classObj) + ? ((err = enterIntoClassTable(classObj)) + ? -err + : (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + /* primitiveFailFor: */ + GIV(primFailCode) = -classIndex; + return null; + } + numSlots = classFormat & ((1U << (fixedFieldsFieldWidth())) - 1); + + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if ((((usqInt)(numSlots)) >> 56) > 0) { + newObj = null; + goto l1; + } + newObjUsqInt = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObjUsqInt = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newObj = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, instSpec, classIndex); + goto l1; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObjUsqInt),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObjUsqInt),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(instSpec) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObjUsqInt % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newObj = newObjUsqInt; + /* end allocateSlots:format:classIndex: */ +l1: + if (newObj) { + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1, addressAfter(newObj))); + toDoLimit = ((usqInt)(((newObj + BaseHeaderSize) + (numSlots * BytesPerOop)) - 1)); + for (p = (((usqInt)(newObj + BaseHeaderSize))); p <= toDoLimit; p += 8 /* allocationUnit */) { + longAtput((void *)(p),GIV(nilObj)); + } + } + return newObj; +} + + /* SpurMemoryManager>>#instSpecOfClassFormat: */ +sqInt +instSpecOfClassFormat(sqInt classFormat) +{ + return (((usqInt)(classFormat)) >> (fixedFieldsFieldWidth())) & (formatMask()); +} + + +/* This field in a class's format inst var corresponds to the 5-bit format + field stored in every object header + */ + + /* SpurMemoryManager>>#instSpecOfClass: */ +static NoDbgRegParms sqInt +instSpecOfClass(sqInt classPointer) +{ + return (((usqInt)((((longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); +} + + /* SpurMemoryManager>>#invalidCompactClassError: */ +static NoDbgRegParms void +invalidCompactClassError(const char *className) +{ + printf("\nClass %s does not have the required class index\n", className); + exit(-1); +} + + +/* Answer true if this is an indexable object with pointer elements, e.g., an + array + */ + + /* SpurMemoryManager>>#isArray: */ +sqInt +isArray(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat())); +} + + +/* Answer if objOop should be included in an allObjects...Do: enumeration. + This is for assert-checking only. */ + + /* SpurMemoryManager>>#isBridgeOrEnumerableObjectNoAssert: */ +static NoDbgRegParms int +isBridgeOrEnumerableObjectNoAssert(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + return (classIndex >= (isForwardedObjectClassIndexPun()) + ? classIndex < (GIV(numClassTablePages) * (1U << (classTableMajorIndexShift()))) + : classIndex == (segmentBridgePun())); +} + + /* SpurMemoryManager>>#isCharacterObject: */ +int +isCharacterObject(sqInt oop) +{ + return ((oop & (characterTag())) != 0); +} + + /* SpurMemoryManager>>#isCharacterValue: */ +int +isCharacterValue(sqInt anInteger) +{ + return ((anInteger >= 0) && (anInteger <= (0x3FFFFFFF))); +} + + /* SpurMemoryManager>>#isCompiledMethodFormat: */ +static NoDbgRegParms int +isCompiledMethodFormat(sqInt format) +{ + return format >= (firstCompiledMethodFormat()); +} + + +/* Answer whether the argument header has compiled method format */ + + /* SpurMemoryManager>>#isCompiledMethodHeader: */ +static NoDbgRegParms int +isCompiledMethodHeader(sqInt objHeader) +{ + return ((((usqInt)(objHeader)) >> (formatShift())) & (formatMask())) >= (firstCompiledMethodFormat()); +} + + +/* Answer whether the argument object is of compiled method format */ + + /* SpurMemoryManager>>#isCompiledMethod: */ +int +isCompiledMethod(sqInt objOop) +{ + return ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()); +} + + /* SpurMemoryManager>>#isContextHeader: */ +static NoDbgRegParms int +isContextHeader(sqInt aHeader) +{ + return (aHeader & (classIndexMask())) == ClassMethodContextCompactIndex; +} + + /* SpurMemoryManager>>#isContextNonImm: */ +static NoDbgRegParms int +isContextNonImm(sqInt oop) +{ + return ((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex; +} + + /* SpurMemoryManager>>#isContext: */ +static NoDbgRegParms sqInt +isContext(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex); +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. */ + + /* SpurMemoryManager>>#isCopiedIntoSegment: */ +static NoDbgRegParms int +isCopiedIntoSegment(sqInt anObjectInTheHeap) +{ + return ((byteAt((void *)(anObjectInTheHeap + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0; +} + + /* SpurMemoryManager>>#isEmptyObjStack: */ +static NoDbgRegParms sqInt +isEmptyObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (objStack == GIV(nilObj)) { + return 1; + } + eassert(isValidObjStack(objStack)); + return (0 == (longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))))) + && (0 == (longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))); +} + + +/* Answer if objOop should be included in an allObjects...Do: enumeration. + This is for assert-checking only. */ + + /* SpurMemoryManager>>#isEnumerableObjectNoAssert: */ +static NoDbgRegParms sqInt +isEnumerableObjectNoAssert(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + return (classIndex >= (isForwardedObjectClassIndexPun())) + && (classIndex < (GIV(numClassTablePages) * (1U << (classTableMajorIndexShift())))); +} + + /* SpurMemoryManager>>#isEphemeronFormat: */ +static NoDbgRegParms int +isEphemeronFormat(sqInt format) +{ + return format == (ephemeronFormat()); +} + + /* SpurMemoryManager>>#isEphemeron: */ +static NoDbgRegParms int +isEphemeron(sqInt objOop) +{ + sqInt format; + + assert(isNonImmediate(objOop)); + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isEphemeronFormat: */ + return format == (ephemeronFormat()); +} + + /* SpurMemoryManager>>#isFixedSizePointerFormat: */ +sqInt +isFixedSizePointerFormat(sqInt format) +{ + return (format <= (nonIndexablePointerFormat())) + || (format == (ephemeronFormat())); +} + + /* SpurMemoryManager>>#isForwardedClassTag: */ +static NoDbgRegParms int +isForwardedClassTag(sqInt classIndex) +{ + return classIndex == (isForwardedObjectClassIndexPun()); +} + + +/* Answer if objOop is that if a forwarder. Take advantage of + isForwardedObjectClassIndexPun being a power of two to generate a more + efficient test than the straight-forward + (self classIndexOf: objOop) = self isForwardedObjectClassIndexPun + at the cost of this being ambiguous with free chunks. So either never + apply this to free chunks + or guard with (self isFreeObject: foo) not. So far the idiom has been to + guard with isFreeObject: */ +/* self assert: (self isFreeObject: objOop) not. */ + + /* SpurMemoryManager>>#isForwarded: */ +int +isForwarded(sqInt objOop) +{ + return (!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))); +} + + /* SpurMemoryManager>>#isFreeObject: */ +static NoDbgRegParms int +isFreeObject(sqInt objOop) +{ + return ((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun()); +} + + /* SpurMemoryManager>>#isFreeOop: */ +static NoDbgRegParms sqInt +isFreeOop(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())); +} + + /* SpurMemoryManager>>#isImmediate: */ +int +isImmediate(sqInt oop) +{ + return ((oop & (tagMask())) != 0); +} + + /* SpurMemoryManager>>#isInClassTable: */ +static NoDbgRegParms sqInt +isInClassTable(sqInt objOop) +{ + int hash; + + hash = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()); + return (hash != 0) + && ((classAtIndex(hash)) == objOop); +} + + /* SpurMemoryManager>>#isIndexable: */ +sqInt +isIndexable(sqInt objOop) +{ + sqInt fmt; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + return /* isIndexableFormat: */ + (fmt >= (arrayFormat())) + && ((fmt <= (weakArrayFormat())) + || (fmt >= (sixtyFourBitIndexableFormat()))); +} + + /* SpurMemoryManager>>#isInEden: */ +static NoDbgRegParms sqInt +isInEden(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return oopisGreaterThanOrEqualToandLessThan(objOop, ((GIV(eden)).start), GIV(freeStart)); +} + + /* SpurMemoryManager>>#isInFutureSpace: */ +static NoDbgRegParms sqInt +isInFutureSpace(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return oopisGreaterThanOrEqualToandLessThan(address, ((GIV(futureSpace)).start), GIV(futureSurvivorStart)); +} + + +/* Answer if the given address is in ST object memory. */ + + /* SpurMemoryManager>>#isInMemory: */ +sqInt +isInMemory(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt addressUsqInt; + sqInt i; + + if (/* isInNewSpace: */ + (oopisLessThan(address, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(address, GIV(newSpaceStart)))) { + return (oopisGreaterThanOrEqualToandLessThan(address, ((GIV(eden)).start), GIV(freeStart))) + || ((oopisGreaterThanOrEqualToandLessThan(address, ((GIV(pastSpace)).start), GIV(pastSpaceStart))) + || ((GIV(gcPhaseInProgress) == ScavengeInProgress) + && (oopisGreaterThanOrEqualToandLessThan(address, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))))); + } + addressUsqInt = address; + + /* begin isInSegments: */ + for (i = 0; i < GIV(numSegments); i += 1) { + if (addressUsqInt < (((GIV(segments)[i]).segStart))) { + return 0; + } + if (addressUsqInt < ((((GIV(segments)[i]).segSize)) + (((GIV(segments)[i]).segStart)))) { + return 1; + } + } + return 0; +} + + /* SpurMemoryManager>>#isInNewSpace: */ +static NoDbgRegParms sqInt +isInNewSpace(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (oopisLessThan(objOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(objOop, GIV(newSpaceStart))); +} + + /* SpurMemoryManager>>#isInOldSpace: */ +sqInt +isInOldSpace(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return oopisGreaterThanOrEqualToandLessThan(address, GIV(oldSpaceStart), GIV(endOfMemory)); +} + + /* SpurMemoryManager>>#isInPastSpace: */ +static NoDbgRegParms sqInt +isInPastSpace(sqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return oopisGreaterThanOrEqualToandLessThan(address, ((GIV(pastSpace)).start), GIV(pastSpaceStart)); +} + + /* SpurMemoryManager>>#isLargeFreeObject: */ +static NoDbgRegParms int +isLargeFreeObject(sqInt objOop) +{ + return (bytesInBody(objOop)) >= 0x200 /* (numFreeLists * #allocationUnit) */; +} + + +/* Answer if the argument contains only indexable 64-bit double words (no + oops). See comment in formatOf: + */ + + /* SpurMemoryManager>>#isLong64s: */ +sqInt +isLong64s(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) == (sixtyFourBitIndexableFormat())); +} + + /* SpurMemoryManager>>#isMarked: */ +int +isMarked(sqInt objOop) +{ + return ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0; +} + + +/* Answer if objOop is an ephemeron that may or may not have been fired. + Unfired ephemerons have ephemeronFormat as their format. Fired ephemerons + have their format set to nonIndexablePointerFormat, so to disambiguate, + check the class is an ephemeron class in this case. */ + + /* SpurMemoryManager>>#isMaybeFiredEphemeron: */ +static NoDbgRegParms sqInt +isMaybeFiredEphemeron(sqInt objOop) +{ + sqInt format; + + if (((objOop & (tagMask())) != 0)) { + return 0; + } + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + return (format == (ephemeronFormat())) + || ((format == (nonIndexablePointerFormat())) + && ((instSpecOfClass(fetchClassOfNonImm(objOop))) == (ephemeronFormat()))); +} + + /* SpurMemoryManager>>#isNonImmediate: */ +int +isNonImmediate(sqInt oop) +{ + return (!(oop & (tagMask()))); +} + + /* SpurMemoryManager>>#isObjEphemeron: */ +static NoDbgRegParms int +isObjEphemeron(sqInt objOop) +{ + sqInt format; + + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isEphemeronFormat: */ + return format == (ephemeronFormat()); +} + + /* SpurMemoryManager>>#isObjImmutable: */ +static NoDbgRegParms sqInt +isObjImmutable(sqInt anOop) +{ + return +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(anOop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ; +} + + +/* Answer if obj is old. Require that obj is non-immediate. */ + + /* SpurMemoryManager>>#isOldObject: */ +static NoDbgRegParms int +isOldObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(isNonImmediate(objOop)); + return oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart)); +} + + +/* Answer whether the oop is an object of compiled method format */ + + /* SpurMemoryManager>>#isOopCompiledMethod: */ +sqInt +isOopCompiledMethod(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())); +} + + /* SpurMemoryManager>>#isOopForwarded: */ +sqInt +isOopForwarded(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))); +} + + /* SpurMemoryManager>>#isOopImmutable: */ +sqInt +isOopImmutable(sqInt oop) +{ + return (((oop & (tagMask())) != 0)) + || (((((usqInt)((byteAt((void *)(oop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0); +} + + /* SpurMemoryManager>>#isOopMutable: */ +sqInt +isOopMutable(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (!(((((usqInt)((byteAt((void *)(oop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0)); +} + + /* SpurMemoryManager>>#isPinned: */ +int +isPinned(sqInt objOop) +{ + return ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0; +} + + +/* Answer if the argument has only fields that can hold oops. See comment in + formatOf: + */ + + /* SpurMemoryManager>>#isPointers: */ +sqInt +isPointers(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */); +} + + +/* the inverse of isAnyPointerFormat: */ + + /* SpurMemoryManager>>#isPureBitsFormat: */ +static NoDbgRegParms sqInt +isPureBitsFormat(sqInt format) +{ + return (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat())); +} + + +/* Answer if the argument contains only indexable words (no oops). See + comment in formatOf: + */ + + /* SpurMemoryManager>>#isPureBitsNonImm: */ +static NoDbgRegParms sqInt +isPureBitsNonImm(sqInt objOop) +{ + sqInt format; + + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isPureBitsFormat: */ + return (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat())); +} + + /* SpurMemoryManager>>#isRemembered: */ +static NoDbgRegParms int +isRemembered(sqInt objOop) +{ + return ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0; +} + + +/* Maybe this should be in SpurSegmentManager only */ + + /* SpurMemoryManager>>#isSegmentBridge: */ +static NoDbgRegParms int +isSegmentBridge(sqInt objOop) +{ + return ((longAt((void *)(objOop))) & (classIndexMask())) == (segmentBridgePun()); +} + + /* SpurMemoryManager>>#isSemaphoreOop: */ +static NoDbgRegParms sqInt +isSemaphoreOop(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((!(anOop & (tagMask())))) + && (((longAt((void *)(anOop))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord()))))))))); +} + + +/* Answer if the argument contains only indexable 16-bit half words (no + oops). See comment in formatOf: + */ + + /* SpurMemoryManager>>#isShorts: */ +sqInt +isShorts(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstShortFormat())) && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= ((firstByteFormat()) - 1)))); +} + + +/* This version is private to SpurMemoryManager (for asserts, etc). It does + not take advantage of the power-of-two optimization in isForwarded:. */ + + /* SpurMemoryManager>>#isUnambiguouslyForwarder: */ +static NoDbgRegParms int +isUnambiguouslyForwarder(sqInt objOop) +{ + return ((longAt((void *)(objOop))) & (classIndexMask())) == (isForwardedObjectClassIndexPun()); +} + + +/* For debugging using printOopsSuchThat: */ + + /* SpurMemoryManager>>#isUnmarked: */ +int +isUnmarked(sqInt objOop) +{ + return !(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0); +} + + /* SpurMemoryManager>>#isValidClassTag: */ +sqInt +isValidClassTag(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOrNil; + sqInt classTablePage; + sqInt fieldIndex; + + assert(((classIndex >= 0) && (classIndex <= ((1U << (classIndexFieldWidth())) - 1)))); + if (((classIndex >= ((tagMask()) + 1)) && (classIndex <= (lastClassIndexPun())))) { + return 0; + } + + /* begin classOrNilAtIndex: */ + assert((classIndex <= (tagMask())) + || (classIndex >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classOrNil = GIV(nilObj); + goto l1; + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + classOrNil = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classOrNilAtIndex: */ +l1: + return (classOrNil != GIV(nilObj)) + && (((long32At((void *)(classOrNil + 4))) & (identityHashHalfWordMask())) == classIndex); +} + + +/* Answer if the obj stack at objStackRootIndex is valid. */ + + /* SpurMemoryManager>>#isValidObjStackAt: */ +static NoDbgRegParms sqInt +isValidObjStackAt(sqInt objStackRootIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt stackOrNil; + + stackOrNil = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord())))))); + return (stackOrNil == GIV(nilObj)) + || (isValidObjStackPagemyIndexfirstPage(stackOrNil, objStackRootIndex, 1)); +} + + +/* Just check the page itself. */ + + /* SpurMemoryManager>>#isValidObjStackPage:myIndex: */ +static NoDbgRegParms sqInt +isValidObjStackPagemyIndex(sqInt objStackPage, sqInt myx) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!(((longAt((void *)(objStackPage))) & (classIndexMask())) == (sixtyFourBitLongsClassIndexPun()))) { + GIV(objStackInvalidBecause) = "wrong class index"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (!(((byteAt((void *)(objStackPage + (formatFieldByteOffset())))) & (formatMask())) == (sixtyFourBitIndexableFormat()))) { + GIV(objStackInvalidBecause) = "wrong format"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (!((numSlotsOfAny(objStackPage)) == ObjStackPageSlots)) { + GIV(objStackInvalidBecause) = "wrong num slots"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (!(myx == (longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord()))))))))) { + GIV(objStackInvalidBecause) = "wrong myx"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (GIV(marking) + && (!(((byteAt((void *)(objStackPage + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) { + GIV(objStackInvalidBecause) = "marking but page is unmarked"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + return 1; +} + + +/* Answer if the obj stack at stackRootIndex is valid. */ + + /* SpurMemoryManager>>#isValidObjStackPage:myIndex:firstPage: */ +static NoDbgRegParms sqInt +isValidObjStackPagemyIndexfirstPage(sqInt objStackPage, sqInt myx, sqInt isFirstPage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt freeOrNextPage; + sqInt index; + char *ns; + sqInt page; + + if (!(isValidObjStackPagemyIndex(objStackPage, myx))) { + return 0; + } + freeOrNextPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + while (freeOrNextPage != 0) { + if (!isFirstPage) { + GIV(objStackInvalidBecause) = "free page on other than first page"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (freeOrNextPage == (longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))) { + GIV(objStackInvalidBecause) = "free page = next page"; + GIV(invalidObjStackPage) = freeOrNextPage; + return 0; + } + if (!(isValidObjStackPagemyIndex(freeOrNextPage, myx))) { + /* begin stretch:cat: */ + ns = malloc(((strlen(GIV(objStackInvalidBecause))) + (14)) + 2); + strcpy(ns, GIV(objStackInvalidBecause)); + GIV(objStackInvalidBecause) = ((char *) (strcat(ns, ", on next page"))); + return 0; + } + page = longAt((void *)((freeOrNextPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + if ((page == freeOrNextPage) + || (page == objStackPage)) { + GIV(objStackInvalidBecause) = "circularity in free page list"; + GIV(invalidObjStackPage) = page; + return 0; + } + freeOrNextPage = page; + } + if (isFirstPage) { + if (!(((myx >= (1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift())))) && (myx <= (((1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift()))) + 8 /* hiddenRootSlots */) - 1))))) { + GIV(objStackInvalidBecause) = "myx out of range"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + if (!((longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(myx) << (shiftForWord()))))))) == objStackPage)) { + GIV(objStackInvalidBecause) = "firstPage is not root"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + } + index = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + if (!(((index >= 0) && (index <= ObjStackLimit)))) { + GIV(objStackInvalidBecause) = "bad topx"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + freeOrNextPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + if (!freeOrNextPage) { + return 1; + } + if (freeOrNextPage == objStackPage) { + GIV(objStackInvalidBecause) = "circularity in objStack page list"; + GIV(invalidObjStackPage) = objStackPage; + return 0; + } + return isValidObjStackPagemyIndexfirstPage(freeOrNextPage, myx, 0); +} + + +/* Answer if the obj stack at objStackRootIndex is valid. */ + + /* SpurMemoryManager>>#isValidObjStack: */ +static NoDbgRegParms sqInt +isValidObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!((addressCouldBeObj(objStack)) + && ((numSlotsOfAny(objStack)) == ObjStackPageSlots))) { + GIV(objStackInvalidBecause) = "first page not obj or wrong size"; + GIV(invalidObjStackPage) = objStack; + return 0; + } + return isValidObjStackPagemyIndexfirstPage(objStack, longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))), 1); +} + + /* SpurMemoryManager>>#isWeakNonImm: */ +static NoDbgRegParms int +isWeakNonImm(sqInt objOop) +{ + sqInt format; + + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isWeakFormat: */ + return format == (weakArrayFormat()); +} + + +/* Answer if the argument has only weak fields that can hold oops. See + comment in formatOf: + */ + + /* SpurMemoryManager>>#isWeak: */ +sqInt +isWeak(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (isWeakNonImm(oop)); +} + + +/* Answer if the contains only indexable words or bytes (no oops). See + comment in formatOf: + */ +/* Note: Excludes CompiledMethods. */ + + /* SpurMemoryManager>>#isWordsOrBytesNonImm: */ +static NoDbgRegParms sqInt +isWordsOrBytesNonImm(sqInt objOop) +{ + sqInt format; + + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin isPureBitsFormat: */ + return (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat())); +} + + +/* Answer if the contains only indexable words or bytes (no oops). See + comment in formatOf: + */ +/* Note: Excludes CompiledMethods. */ + + /* SpurMemoryManager>>#isWordsOrBytes: */ +sqInt +isWordsOrBytes(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (isWordsOrBytesNonImm(oop)); +} + + +/* Answer if the argument contains only indexable 16-bit half words or 32-bit + indexable words (no oops). + See comment in formatOf: */ + + /* SpurMemoryManager>>#isWordsOrShorts: */ +sqInt +isWordsOrShorts(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= ((firstByteFormat()) - 1)))); +} + + +/* Answer if the argument contains only indexable words (no oops). See + comment in formatOf: + */ + + /* SpurMemoryManager>>#isWords: */ +sqInt +isWords(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstLongFormat())) && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= ((firstShortFormat()) - 1)))); +} + + +/* Answer if obj is young. Require that obj is non-immediate. */ + + /* SpurMemoryManager>>#isYoungObject: */ +int +isYoungObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(isNonImmediate(objOop)); + return oopisLessThan(objOop, GIV(oldSpaceStart)); +} + + +/* Answer if oop is young. */ + + /* SpurMemoryManager>>#isYoung: */ +sqInt +isYoung(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((!(oop & (tagMask())))) + && (oopisLessThan(oop, GIV(oldSpaceStart))); +} + + +/* Answer if oop is an instance of the given class. If the class has a + (non-zero) compactClassIndex use that to speed up the check. N.B. Inlining + should result in classOop not being accessed if oop's compact class index + and compactClassIndex are non-zero. */ + + /* SpurMemoryManager>>#is:instanceOf:compactClassIndex: */ +static NoDbgRegParms sqInt +isinstanceOfcompactClassIndex(sqInt oop, sqInt classOop, sqInt compactClassIndex) +{ + sqInt ccIndex; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(oop))); + ccIndex = (longAt((void *)(oop))) & (classIndexMask()); + if (compactClassIndex) { + return compactClassIndex == ccIndex; + } + else { + return classOop == (classAtIndex(ccIndex)); + } +} + + +/* useful for debugging... */ + + /* SpurMemoryManager>>#is:onObjStack: */ +sqInt +isonObjStack(sqInt oop, sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt nextPage; + + if (objStack == GIV(nilObj)) { + return 0; + } + assert((numSlotsOfAny(objStack)) == ObjStackPageSlots); + + /* There are four fixed slots in an obj stack, and a Topx of 0 indicates empty, so + if there were 6 slots in an oop stack, full would be 2, and the last 0-rel index is 5. */ + index = (longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) + ObjStackNextx; + while (index >= ObjStackFixedSlots) { + if (oop == (longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))))) { + return 1; + } + index -= 1; + } + nextPage = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + if (nextPage) { + if (isonObjStack(oop, nextPage)) { + return 1; + } + } + return 0; +} + + +/* Answer the object the ephemeron guards. This is its first element. */ + + /* SpurMemoryManager>>#keyOfEphemeron: */ +static NoDbgRegParms sqInt +keyOfEphemeron(sqInt objOop) +{ + assert((isNonImmediate(objOop)) + && (isObjEphemeron(objOop))); + return longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); +} + + +/* Answer the object the ephemeron guards. This is its first element. */ + + /* SpurMemoryManager>>#keyOfMaybeFiredEphemeron: */ +static NoDbgRegParms sqInt +keyOfMaybeFiredEphemeron(sqInt objOop) +{ + assert(isMaybeFiredEphemeron(objOop)); + return longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); +} + + /* SpurMemoryManager>>#knownClassAtIndex: */ +static NoDbgRegParms sqInt +knownClassAtIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(((classIndex >= 1) && (classIndex <= (classTablePageSize())))); + return longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(classIndex) << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#lastPointerFormat */ +static sqInt +lastPointerFormat(void) +{ + return 5; +} + + +/* Answer the byte offset of the last pointer field of the given object. + Works with CompiledMethods, as well as ordinary objects. + Does not examine the stack pointer of contexts to be sure to swizzle + the nils that fill contexts on snapshot. + It is invariant that on image load no object contains a forwarding + pointer, and the image contains no forwarders (see class comment). */ + + /* SpurMemoryManager>>#lastPointerOfWhileSwizzling: */ +static NoDbgRegParms sqInt +lastPointerOfWhileSwizzling(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt header; + sqInt headerSqInt; + usqInt numSlots; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt != (forwardedFormat())); + if (fmt <= 5 /* lastPointerFormat */) { + return ((((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1) * BytesPerOop) + BaseHeaderSize; + } + if (fmt < (firstCompiledMethodFormat())) { + return 0; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + return (((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) - 1) * BytesPerOop) + BaseHeaderSize; +} + + +/* Answer the byte offset of the last pointer field of the given object. + Works with CompiledMethods, as well as ordinary objects. */ + + /* SpurMemoryManager>>#lastPointerOf: */ +static NoDbgRegParms sqInt +lastPointerOf(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt fmt; + sqInt header; + sqInt headerSqInt; + usqInt numSlots; + sqInt sp; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt != (forwardedFormat())); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + return (((CtxtTempFrameStart - 1) + contextSize) * BytesPerOop) + BaseHeaderSize; + } + return ((((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1) * BytesPerOop) + BaseHeaderSize; + } + if (fmt < (firstCompiledMethodFormat())) { + return 0; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + return (((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) - 1) * BytesPerOop) + BaseHeaderSize; +} + + /* SpurMemoryManager>>#leakCheckFullGC */ +int +leakCheckFullGC(void) +{ + return ((checkForLeaks & GCModeFull) != 0); +} + + /* SpurMemoryManager>>#leakCheckNewSpaceGC */ +int +leakCheckNewSpaceGC(void) +{ + return ((checkForLeaks & GCModeNewSpace) != 0); +} + + +/* for the message send breakpoint; selectors can be immediates. */ + + /* SpurMemoryManager>>#lengthOfMaybeImmediate: */ +static NoDbgRegParms sqInt +lengthOfMaybeImmediate(sqInt oop) +{ + sqInt fmt; + usqInt numSlots; + usqInt numSlotsUsqInt; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin lengthOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + +/* Answer the number of indexable units in the given object. + For a CompiledMethod, the size of the method header (in bytes) should + be subtracted from the result. */ + + /* SpurMemoryManager>>#lengthOf: */ +static NoDbgRegParms sqInt +lengthOf(sqInt objOop) +{ + sqInt fmt; + usqInt numSlots; + usqInt numSlotsUsqInt; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + /* SpurMemoryManager>>#literalCountOfMethodHeader: */ +usqInt +literalCountOfMethodHeader(sqInt header) +{ + assert((((header) & 7) == 1)); + return ((header >> 3)) & AlternateHeaderNumLiteralsMask; +} + + /* SpurMemoryManager>>#literalCountOf: */ +usqInt +literalCountOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + return ((header >> 3)) & AlternateHeaderNumLiteralsMask; +} + + +/* This primitive is called from Smalltalk as... + loadSegmentFrom: aWordArray outPointers: anArray. */ +/* This primitive will load a binary image segment created by + primitiveStoreImageSegment. It expects the outPointer array to be of the + proper size, and the wordArray to be well formed. + It will return as its value the original array of roots, and the + segmentWordArray will become an + array of the loaded objects. If this primitive should fail, the + segmentWordArray will, sadly, have + been reduced to an unrecognizable and unusable jumble. But what more could + you have done + with it anyway? + + The primitive, if it succeeds, also becomes the segmentWordArray into the + array of loaded objects. + This allows fixing up of loaded objects directly, without nextObject, + which Spur doesn't support. */ + + /* SpurMemoryManager>>#loadImageSegmentFrom:outPointers: */ +static NoDbgRegParms NeverInline sqInt +loadImageSegmentFromoutPointers(sqInt segmentWordArray, sqInt outPointerArray) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt classOop; + sqInt classRef; + sqInt clone; + sqInt err; + sqInt errorCode; + sqInt errorCodeSqInt; + sqInt fillIdx; + sqInt followingWord; + usqInt followingWordAddress; + usqInt followingWordAddressUsqInt; + sqInt followingWordSqInt; + int hash; + sqInt i; + sqInt loadedObjectsArray; + usqInt mappedOop; + sqInt numLoadedObjects; + usqInt numOutPointers; + sqInt numSegObjs; + usqInt numSlots; + sqInt numSlotsSqInt; + sqInt objOop; + sqInt oldClone; + sqInt oop; + usqInt oopUsqInt; + sqInt scanClassTable; + usqInt segmentLimit; + sqInt segmentStart; + sqInt segVersion; + sqInt toDoLimit; + + /* begin numSlotsOf: */ + assert((classIndexOf(segmentWordArray)) > (isForwardedObjectClassIndexPun())); + segmentLimit = (((numSlots = byteAt((void *)(segmentWordArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(segmentWordArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if ((/* objectBytesForSlots: */ + (segmentLimit + ? ((segmentLimit << (shiftForWord()))) + ((segmentLimit >= (numSlotsMask()) + ? BaseHeaderSize + BaseHeaderSize + : BaseHeaderSize)) + : 8 /* allocationUnit */ + BaseHeaderSize)) < (8 /* allocationUnit */ + BaseHeaderSize)) { + return PrimErrBadArgument; + } + + /* version info + one object header + Verify format. If the format is wrong, word-swap (since ImageSegment data are 32-bit longs). + If it is still wrong, undo the damage and fail. */ + segVersion = longAt((void *)(segmentWordArray + BaseHeaderSize)); + if (!(((((segVersion & 0xFFFFFF) | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */)) { + reverseBytesIn32BitWordsIn(segmentWordArray); + segVersion = longAt((void *)(segmentWordArray + BaseHeaderSize)); + if (!(((((segVersion & 0xFFFFFF) | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */)) { + reverseBytesIn32BitWordsIn(segmentWordArray); + return PrimErrBadArgument; + } + } + + /* low 3 bytes */ + segmentStart = (segmentWordArray + BaseHeaderSize) + 8 /* allocationUnit */; + segmentLimit = ((segmentLimit * BytesPerOop) + segmentWordArray) + BaseHeaderSize; + + /* Notionally reverse the Byte type objects if the data is from opposite endian machine. + Test top byte. $d on the Mac or $s on the PC. Rest of word is equal. If Spur is ever + ported to big-endian machines then the segment may have to be byte/word swapped, + but so far it only runs on little-endian machines, so for now just fail if endianness is wrong. */ + if (((((usqInt)(segVersion)) >> 24) & 0xFF) != ((((usqInt)((imageSegmentVersion()))) >> 24) & 0xFF)) { + return PrimErrBadArgument; + } + + /* Reverse the byte-type objects once + Avoid having to remember by arranging that there are no young outPointers if segment is in old space. */ + + /* begin isOldObject: */ + assert(isNonImmediate(segmentWordArray)); + if (oopisGreaterThanOrEqualTo(segmentWordArray, GIV(oldSpaceStart))) { + /* begin ensureNoNewObjectsIn: */ + hash = 0; + scanClassTable = 0; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(outPointerArray)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(outPointerArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointerArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + oop = longAt((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isYoung: */ + ((!(oop & (tagMask())))) + && (oopisLessThan(oop, GIV(oldSpaceStart)))) { + clone = cloneInOldSpaceforPinning(oop, 0); + if (!clone) { + errorCode = PrimErrNoMemory; + goto l6; + } + if ((((hash = (long32At((void *)(oop + 4))) & (identityHashHalfWordMask()))) != 0) + && ((classOrNilAtIndex(hash)) == oop)) { + scanClassTable = 1; + } + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(oop),((longAt((void *)(oop))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(oop),(longAt((void *)(oop))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(oop)); + assert(!(isOopForwarded(clone))); + + /* begin isOldObject: */ + assert(isNonImmediate(oop)); + if (oopisGreaterThanOrEqualTo(oop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(clone & (tagMask())))) + && (oopisLessThan(clone, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(oop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord()))),clone); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(oop + (numSlotsFieldByteOffset())),1); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(outPointerArray)) + && (!(isForwarded(outPointerArray)))); + assert(validStorePointerUncheckedArgs(i, outPointerArray, clone)); + longAtput((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),clone); + } + } + if (scanClassTable) { + postBecomeScanClassTable(BecamePointerObjectFlag); + } + errorCode = 0; + /* end ensureNoNewObjectsIn: */ +l6: + if (errorCode) { + return errorCode; + } + } + + /* scan through mapping oops and validating class references. Defer entering any + class objects into the class table and/or pinning objects until a second pass. */ + + /* begin mapOopsAndValidateClassRefsFrom:to:outPointers: */ + /* begin numSlotsOf: */ + assert((classIndexOf(outPointerArray)) > (isForwardedObjectClassIndexPun())); + numOutPointers = (((numSlots = byteAt((void *)(outPointerArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointerArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numSegObjs = 0; + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(segmentStart + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? segmentStart + BaseHeaderSize + : segmentStart); + while (oopisLessThan(objOop, segmentLimit)) { + numSegObjs += 1; + + /* No object in the segment should be marked. If is is something is wrong. */ + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + errorCode = PrimErrInappropriate; + goto l8; + } + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + + /* validate the class ref, but don't update it until any internal classes have been added to the class table. */ + if (((classIndex & TopHashBit) != 0)) { + classIndex -= TopHashBit; + if (classIndex >= numOutPointers) { + errorCode = PrimErrBadIndex; + goto l8; + } + mappedOop = longAt((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)(classIndex) << (shiftForWord())))))); + hash = (long32At((void *)(mappedOop + 4))) & (identityHashHalfWordMask()); + if (!((hash == 0) + || ((hash > (lastClassIndexPun())) + && ((classOrNilAtIndex(hash)) == mappedOop)))) { + errorCode = PrimErrInappropriate; + goto l8; + } + } + else { + if (((oopUsqInt = ((classIndex - (firstClassIndexPun())) * 8 /* allocationUnit */) + segmentStart)) >= segmentLimit) { + errorCode = PrimErrBadIndex; + goto l8; + } + if ((long32At((void *)(oopUsqInt + 4))) & (identityHashHalfWordMask())) { + errorCode = PrimErrInappropriate; + goto l8; + } + } + + /* The class is contained within the segment. */ + toDoLimit = (numPointerSlotsOf(objOop)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + oopUsqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(oopUsqInt & (tagMask())))) { + if (((oopUsqInt & TopOopBit) != 0)) { + if (((oopUsqInt = (oopUsqInt - TopOopBit) / BytesPerOop)) >= numOutPointers) { + errorCode = PrimErrBadIndex; + goto l8; + } + mappedOop = longAt((void *)((outPointerArray + BaseHeaderSize) + ((oopUsqInt << (shiftForWord()))))); + } + else { + if (oopUsqInt & 7 /* (allocationUnit - 1) */) { + errorCode = PrimErrInappropriate; + goto l8; + } + if (((mappedOop = oopUsqInt + segmentStart)) >= segmentLimit) { + errorCode = PrimErrBadIndex; + goto l8; + } + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(i, objOop, mappedOop)); + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),mappedOop); + } + } + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, segmentLimit)) { + objOop = segmentLimit; + goto l7; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l7:; + } + errorCode = -numSegObjs; + /* end mapOopsAndValidateClassRefsFrom:to:outPointers: */ +l8: + if (errorCode > 0) { + return errorCode; + } + numLoadedObjects = -errorCode; + loadedObjectsArray = noInlineAllocateSlotsformatclassIndex(numLoadedObjects, arrayFormat(), ClassArrayCompactIndex); + if (!loadedObjectsArray) { + growOldSpaceByAtLeast((BaseHeaderSize + BaseHeaderSize) + (numLoadedObjects * BytesPerOop)); + loadedObjectsArray = noInlineAllocateSlotsformatclassIndex(numLoadedObjects, arrayFormat(), ClassArrayCompactIndex); + if (!loadedObjectsArray) { + return PrimErrNoMemory; + } + } + + /* Scan for classes contained in the segment, entering them into the class table. + Classes are at the front, after the root array and have the remembered bit set. */ + + /* begin enterClassesIntoClassTableFrom:to: */ + objOop = objectAfter(objectStartingAt(segmentStart)); + while ((oopisLessThan(objOop, segmentLimit)) + && (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0)) { + setIsRememberedOfto(objOop, 0); + if ((errorCodeSqInt = enterIntoClassTable(objOop))) { + oop = objOop; + objOop = objectAfter(objectStartingAt(segmentStart)); + while (oopisLessThan(objOop, oop)) { + expungeFromClassTable(objOop); + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, segmentLimit)) { + objOop = segmentLimit; + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + errorCode = errorCodeSqInt; + goto l3; + } + + /* begin objectAfter:limit: */ + followingWordAddressUsqInt = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddressUsqInt, segmentLimit)) { + objOop = segmentLimit; + goto l2; + } + followingWordSqInt = longAt((void *)(followingWordAddressUsqInt)); + objOop = ((((usqInt)(followingWordSqInt)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddressUsqInt + BaseHeaderSize + : followingWordAddressUsqInt); + /* end objectAfter:limit: */ +l2:; + } + errorCode = 0; + /* end enterClassesIntoClassTableFrom:to: */ +l3: + if (errorCode) { + return errorCode; + } + + /* Make a final pass, assigning class indices and/or pinning pinned objects and collecting the loaded objects in loadedObjectsArray */ + + /* begin assignClassIndicesAndPinFrom:to:outPointers:filling: */ + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(segmentStart + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? segmentStart + BaseHeaderSize + : segmentStart); + fillIdx = 0; + while (oopisLessThan(objOop, segmentLimit)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(loadedObjectsArray)) + && (!(isForwarded(loadedObjectsArray)))); + assert(validStorePointerUncheckedArgs(fillIdx, loadedObjectsArray, objOop)); + longAtput((void *)((loadedObjectsArray + BaseHeaderSize) + ((((usqInt)(fillIdx) << (shiftForWord()))))),objOop); + fillIdx += 1; + + /* In the segment, class indices are offset indexes into the segment data, + or into outPointers. See mapOopsFrom:to:outPointers:outHashes: and + newOutPointer:at:in:hashes:. */ + classRef = (longAt((void *)(objOop))) & (classIndexMask()); + classOop = (((classRef & TopHashBit) != 0) + ? longAt((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)((classRef - TopHashBit)) << (shiftForWord())))))) + : ((classRef - (firstClassIndexPun())) * 8 /* allocationUnit */) + segmentStart); + classIndex = (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask()); + if (!classIndex) { + /* begin ensureBehaviorHash: */ + assert(addressCouldBeClassObj(classOop)); + + /* eem 12/28/2021 the above asserft is too weak (and only an assert) */ + classIndex = ((hash = (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask())) + ? hash + : (objCouldBeClassObj(classOop) + ? ((err = enterIntoClassTable(classOop)) + ? -err + : (long32At((void *)(classOop + 4))) & (identityHashHalfWordMask())) + : -PrimErrBadReceiver)); + if (classIndex < 0) { + errorCode = -classIndex; + goto l5; + } + } + assert((classIndex > (lastClassIndexPun())) + && ((classOrNilAtIndex(classIndex)) == classOop)); + + /* begin setClassIndexOf:to: */ + assert(((classIndex >= 0) && (classIndex <= (classIndexMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(classIndexMask()))) + classIndex); + if ((/* isInNewSpace: */ + (oopisLessThan(objOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(objOop, GIV(newSpaceStart)))) + && (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)) { + oldClone = cloneInOldSpaceforPinning(objOop, 1); + if (oldClone) { + setIsPinnedOfto(oldClone, 1); + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(objOop),(longAt((void *)(objOop))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(objOop)); + assert(!(isOopForwarded(oldClone))); + + /* begin isOldObject: */ + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(oldClone & (tagMask())))) + && (oopisLessThan(oldClone, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),oldClone); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),1); + } + } + } + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, segmentLimit)) { + objOop = segmentLimit; + goto l4; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l4:; + } + errorCode = 0; + /* end assignClassIndicesAndPinFrom:to:outPointers:filling: */ +l5: + if (errorCode) { + return errorCode; + } + + /* Evaporate the container, leaving the newly loaded objects in place. */ + if ((byteAt((void *)(segmentWordArray + (numSlotsFieldByteOffset())))) == (numSlotsMask())) { + if (oopisLessThan(segmentWordArray, GIV(oldSpaceStart))) { + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(segmentWordArray - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56)))); + + /* rawNumSlotsOf:put: */ + byteAtput((void *)(segmentWordArray + (numSlotsFieldByteOffset())),0); + } + else { + numSlotsSqInt = 8 /* allocationUnit */ / BytesPerOop; + + /* begin rawOverflowSlotsOf:put: */ + longAtput((void *)(segmentWordArray - BaseHeaderSize),((((usqInt)((numSlotsMask())) << 56))) + numSlotsSqInt); + } + } + else { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(segmentWordArray + (numSlotsFieldByteOffset())),8 /* allocationUnit */ / BytesPerOop); + } + + /* N.B. setting the overflow slots to 1 creates a slimbridge in eden, so we also need to delete the overflow slot count in the segment itself + Finally forward the segmentWordArray to the loadedObjectsArray */ + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(segmentWordArray),((longAt((void *)(segmentWordArray))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(segmentWordArray),(longAt((void *)(segmentWordArray))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(segmentWordArray)); + assert(!(isOopForwarded(loadedObjectsArray))); + + /* begin isOldObject: */ + assert(isNonImmediate(segmentWordArray)); + if (oopisGreaterThanOrEqualTo(segmentWordArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(loadedObjectsArray & (tagMask())))) + && (oopisLessThan(loadedObjectsArray, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(segmentWordArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(segmentWordArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((segmentWordArray + BaseHeaderSize) + (0U << (shiftForWord()))),loadedObjectsArray); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(segmentWordArray + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(segmentWordArray + (numSlotsFieldByteOffset())),1); + } + runLeakCheckerFor(GCModeImageSegment); + + /* Answer the first object in the segment because this is what primitiveLoadImageSegment tests for, + even though the real result is the segmentWordArray forwarded to the loadedObjectsArray. */ + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(segmentStart + (numSlotsFieldByteOffset()))); + return (numSlots == (numSlotsMask()) + ? segmentStart + BaseHeaderSize + : segmentStart); +} + + +/* Scan the heap printing the oops of any and all objects that are instances + of aClassOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#longPrintInstancesOf: */ +void +longPrintInstancesOf(sqInt aClassOop) +{ + int classIndex; + + classIndex = (long32At((void *)(aClassOop + 4))) & (identityHashHalfWordMask()); + if (classIndex != (isFreeObjectClassIndexPun())) { + longPrintInstancesWithClassIndex(classIndex); + } +} + + +/* Scan the heap printing any and all objects whose classIndex equals the + argument. + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#longPrintInstancesWithClassIndex: */ +void +longPrintInstancesWithClassIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + longPrintOop(objOop); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + longPrintOop(objOop); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3:; +} + + +/* Scan the heap long printing the oops of any and all objects that refer to + anOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#longPrintReferencesTo: */ +void +longPrintReferencesTo(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt i; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt prntObj; + sqInt startObject; + + prntObj = 0; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + || (((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) { + if (((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + i = (literalCountOf(objSqInt)) + LiteralStart; + } + else { + if (((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + i = CtxtTempFrameStart + (fetchStackPointerOf(objSqInt)); + } + else { + /* begin numSlotsOf: */ + assert((classIndexOf(objSqInt)) > (isForwardedObjectClassIndexPun())); + i = (((numSlots = byteAt((void *)(objSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + } + } + while (((i -= 1)) >= 0) { + if (anOop == (longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + fprintf(GIV(transcript), + "%p @ %d\n", + ((void *)objSqInt), + ((int) i)); + prntObj = 1; + i = 0; + } + } + if (prntObj) { + prntObj = 0; + longPrintOop(objSqInt); + } + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + /* SpurMemoryManager>>#mapExtraRoots */ +static void +mapExtraRoots(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt oop; + + if (shouldRemapObj(GIV(specialObjectsOop))) { + GIV(validatedIntegerClassFlags) = 0; + GIV(specialObjectsOop) = remapObj(GIV(specialObjectsOop)); + } + assert(GIV(remapBufferCount) == 0); + + /* 1 to: remapBufferCount do: + [:i | | oop | + oop := remapBufferCount at: i. + ((self isImmediate: oop) or: [self isFreeObject: oop]) ifFalse: + [(self shouldRemapObj: oop) ifTrue: + [remapBuffer at: i put: (self remapObj: oop)]]]. */ + for (i = 1; i <= GIV(extraRootCount); i += 1) { + oop = (GIV(extraRoots)[i])[0]; + if (!((((oop & (tagMask())) != 0)) + || (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())))) { + if (shouldRemapObj(oop)) { + (GIV(extraRoots)[i])[0] = (remapObj(oop)); + } + } + } +} + + /* SpurMemoryManager>>#mapMournQueue */ +static NeverInline void +mapMournQueue(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt iSqInt; + sqInt mourner; + sqInt numOnThisPage; + sqInt objStackPage; + sqInt referent; + + /* begin objStack:do: */ + if (GIV(mournQueue) == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(GIV(mournQueue))); + objStackPage = GIV(mournQueue); + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + mourner = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if ((!(mourner & (tagMask())))) { + if ((!((longAt((void *)(mourner))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(mourner)); + referent = longAt((void *)((mourner + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + mourner = referent; + } + if (!(isScavengeSurvivor(mourner))) { + mourner = copyAndForwardMourner(mourner); + } + + /* we could check for change but writes are cheap with write buffers... */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objStackPage)) + && (!(isForwarded(objStackPage)))); + assert(validStorePointerUncheckedArgs(iSqInt, objStackPage, mourner)); + longAtput((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord()))))),mourner); + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l1:; +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. + Now scan, adding out pointers to the outPointersArray; all objects in + arrayOfObjects have had their first fields set to point to their copies in + segmentWordArray. Answer + the outIndex if the scan succeded. Fail if outPointers is too small and + answer -1. + + As established by copyObj:toAddr:startAt:stopAt:savedFirstFields:index:, + the marked bit is set for all objects in the segment + the remembered bit is set for all classes in the segment. + + Class indices should be set as follows (see + assignClassIndicesAndPinFrom:to:outPointers:filling:) - class indices for + classes in the segment */ + + /* SpurMemoryManager>>#mapOopsFrom:to:outPointers:outHashes: */ +static NoDbgRegParms sqInt +mapOopsFromtooutPointersoutHashes(sqInt segStart, sqInt segAddr, sqInt outPointerArray, sqInt savedOutHashes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + int hash; + sqInt heapOop; + sqInt i; + usqInt numSlots; + sqInt numSlotsSqInt; + usqInt numSlotsUsqInt; + sqInt objIndex; + sqInt objOop; + sqInt oop; + sqInt outIndex; + usqInt p; + sqInt segIndex; + sqInt toDoLimit; + usqInt toDoLimitUsqInt; + sqInt valueWord; + sqInt valueWordSqInt; + + /* objIndex is for debugging; it mirrors indices in the sender's arrayOfObjects. */ + outIndex = (objIndex = 0); + /* begin numSlotsOf: */ + assert((classIndexOf(outPointerArray)) > (isForwardedObjectClassIndexPun())); + if (((numSlots = byteAt((void *)(outPointerArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask())) { + numSlotsSqInt = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointerArray - BaseHeaderSize)))) << 8)))))) >> 8; + } + else { + numSlotsSqInt = numSlots; + } + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((outPointerArray + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1, addressAfter(outPointerArray))); + toDoLimitUsqInt = ((usqInt)(((outPointerArray + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1)); + for (p = (((usqInt)(outPointerArray + BaseHeaderSize))); p <= toDoLimitUsqInt; p += 8 /* allocationUnit */) { + longAtput((void *)(p),GIV(nilObj)); + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(segStart + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? segStart + BaseHeaderSize + : segStart); + while (oopisLessThan(objOop, segAddr)) { + heapOop = fetchClassOfNonImm(objOop); + + /* Set the classIndex of the instance. This is a segment offset (segAddr - segStart / allocationUnit) for instances of + classes within the segment, and an outPointer index (index in outPointers + TopHashBit) for classes outside the segment. */ + if ((byteAt((void *)(heapOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + oop = longAt((void *)((heapOop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(oopisGreaterThanOrEqualToandLessThan(oop, segStart, segAddr)); + segIndex = ((oop - segStart) / 8 /* allocationUnit */) + (firstClassIndexPun()); + if (((segIndex & TopHashBit) != 0)) { + return -1; + } + } + else { + hash = (long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask()); + if (/* is:outPointerClassHashFor:in:limit: */ + (((hash & TopHashBit) != 0)) + && (((hash - TopHashBit) <= outIndex) + && (heapOop == (longAt((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)((hash - TopHashBit)) << (shiftForWord())))))))))) { + segIndex = hash; + } + else { + /* begin newOutPointer:at:in:hashes: */ + if (outIndex >= ((/* begin numSlotsOf: */ + assert((classIndexOf(outPointerArray)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(outPointerArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointerArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))) { + /* no room in outPointers; fail */ + outIndex = 0; + goto l2; + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(outIndex, outPointerArray, heapOop)); + assert(isNonImmediate(outPointerArray)); + if (oopisGreaterThanOrEqualTo(outPointerArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(heapOop & (tagMask())))) + && (oopisLessThan(heapOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(outPointerArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(outPointerArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)(outIndex) << (shiftForWord()))))),heapOop); + valueWord = (long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask()); + + /* begin storeLong32:ofObject:withValue: */ + long32Atput((void *)((savedOutHashes + BaseHeaderSize) + ((((usqInt)(outIndex) << 2)))),valueWord); + long32Atput((void *)(heapOop + 4),((((long32At((void *)(heapOop + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + ((outIndex + TopHashBit) & (identityHashHalfWordMask()))); + outIndex += 1; + /* end newOutPointer:at:in:hashes: */ +l2: + if (!outIndex) { + return -1; + } + + /* no room in outPointers; fail */ + segIndex = (long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask()); + } + + /* oop is a new outPointer; allocate its oop */ + assert(((segIndex & TopHashBit) != 0)); + } + + /* oop is a class in the segment; storeImageSegmentInto:outPointers:roots: established offset is within range. + oop is an outPointer; locate or allocate its oop */ + + /* begin setClassIndexOf:to: */ + assert(((segIndex >= 0) && (segIndex <= (classIndexMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(classIndexMask()))) + segIndex); + toDoLimit = (numPointerSlotsOf(objOop)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + heapOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((!(heapOop & (tagMask())))) { + if ((byteAt((void *)(heapOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + oop = longAt((void *)((heapOop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(oopisGreaterThanOrEqualToandLessThan(oop, segStart, segAddr)); + oop -= segStart; + } + else { + hash = (long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask()); + if (/* is:outPointerClassHashFor:in:limit: */ + (((hash & TopHashBit) != 0)) + && (((hash - TopHashBit) <= outIndex) + && (heapOop == (longAt((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)((hash - TopHashBit)) << (shiftForWord())))))))))) { + oop = ((hash - TopHashBit) * BytesPerOop) + TopOopBit; + } + else { + /* begin newOutPointer:at:in:hashes: */ + if (outIndex >= ((/* begin numSlotsOf: */ + assert((classIndexOf(outPointerArray)) > (isForwardedObjectClassIndexPun())), + (((numSlotsUsqInt = byteAt((void *)(outPointerArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointerArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt)))) { + /* no room in outPointers; fail */ + outIndex = 0; + goto l3; + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(outIndex, outPointerArray, heapOop)); + assert(isNonImmediate(outPointerArray)); + if (oopisGreaterThanOrEqualTo(outPointerArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(heapOop & (tagMask())))) + && (oopisLessThan(heapOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(outPointerArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(outPointerArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((outPointerArray + BaseHeaderSize) + ((((usqInt)(outIndex) << (shiftForWord()))))),heapOop); + valueWordSqInt = (long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask()); + + /* begin storeLong32:ofObject:withValue: */ + long32Atput((void *)((savedOutHashes + BaseHeaderSize) + ((((usqInt)(outIndex) << 2)))),valueWordSqInt); + long32Atput((void *)(heapOop + 4),((((long32At((void *)(heapOop + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + ((outIndex + TopHashBit) & (identityHashHalfWordMask()))); + outIndex += 1; + /* end newOutPointer:at:in:hashes: */ +l3: + if (!outIndex) { + return -1; + } + + /* no room in outPointers; fail */ + assert((((rawHashBitsOf(heapOop)) & TopHashBit) != 0)); + oop = ((((long32At((void *)(heapOop + 4))) & (identityHashHalfWordMask())) - TopHashBit) * BytesPerOop) + TopOopBit; + } + } + + /* oop is an object in the segment. + oop is an outPointer; locate or allocate its oop */ + + /* storeUncheckedNoAssert:ofAny:withValue: */ + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),oop); + } + } + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, segAddr)) { + objOop = segAddr; + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1: + objIndex += 1; + } + return outIndex; +} + + +/* Ensure the class of the argument is marked, pushing it on the markStack if + not already marked. + And for one-way become, which can create duplicate entries in the class + table, make sure + objOop's classIndex refers to the classObj's actual classIndex. + Note that this is recursive, but the metaclass chain should terminate + quickly. + */ + + /* SpurMemoryManager>>#markAndTraceClassOf: */ +static NoDbgRegParms void +markAndTraceClassOf(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt classObj; + sqInt classTablePage; + sqInt fieldIndex; + sqInt objStack; + int realClassIndex; + + classIndex = (longAt((void *)(objOop))) & (classIndexMask()); + + /* begin classOrNilAtIndex: */ + assert((classIndex <= (tagMask())) + || (classIndex >= (arrayClassIndexPun()))); + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + classObj = GIV(nilObj); + goto l1; + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + classObj = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + /* end classOrNilAtIndex: */ +l1: + assert(objCouldBeClassObj(classObj)); + realClassIndex = (long32At((void *)(classObj + 4))) & (identityHashHalfWordMask()); + if ((classIndex != realClassIndex) + && (classIndex > (lastClassIndexPun()))) { + /* begin setClassIndexOf:to: */ + assert(((realClassIndex >= 0) && (realClassIndex <= (classIndexMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(classIndexMask()))) + realClassIndex); + } + if (!((byteAt((void *)(classObj + (markBitsByteOffset())))) & (1U << (markedBitByteShift())))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(classObj))); + byteAtput((void *)(classObj + (markBitsByteOffset())),(byteAt((void *)(classObj + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + markAndTraceClassOf(classObj); + objStack = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(classObj)); + if (((classObj & (tagMask())) != 0)) { + assert(objStack == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack)) + ? fetchPointerofObject(ObjStackNextx, objStack) + : objStack)))); + } + else { + assert(!((objStack == GIV(markStack)) + && (isWeakNonImm(classObj)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack != GIV(weaklingStack)) + || (isWeakNonImm(classObj))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(classObj, objStack); + } +} + + +/* An obj stack is a stack of objects stored in a hidden root slot, such + as the markStack or the ephemeronQueue. It is a linked list of + segments, with the hot end at the head of the list. It is a word object. + The stack pointer is in ObjStackTopx and 0 means empty. */ + + /* SpurMemoryManager>>#markAndTraceObjStack:andContents: */ +static NoDbgRegParms void +markAndTraceObjStackandContents(sqInt stackOrNil, sqInt markAndTraceContents) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt field; + sqInt index; + + if (stackOrNil == GIV(nilObj)) { + return; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(stackOrNil))); + byteAtput((void *)(stackOrNil + (markBitsByteOffset())),(byteAt((void *)(stackOrNil + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + field = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + if (field) { + markAndTraceObjStackandContents(field, markAndTraceContents); + } + field = stackOrNil; + while (1) { + field = longAt((void *)((field + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + if (!(field != 0)) break; + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(field))); + byteAtput((void *)(field + (markBitsByteOffset())),(byteAt((void *)(field + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + } + if (!markAndTraceContents) { + return; + } + + /* There are four fixed slots in an obj stack, and a Topx of 0 indicates empty, so + if there were 6 slots in an oop stack, full would be 2, and the last 0-rel index is 5. */ + index = (longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) + ObjStackNextx; + while (index >= ObjStackFixedSlots) { + /* begin followObjField:ofObject: */ + field = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + assert(isNonImmediate(field)); + if ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + field = fixFollowedFieldofObjectwithInitialValue(index, stackOrNil, field); + } + if (!(((field & (tagMask())) != 0))) { + markAndTrace(field); + } + index -= 1; + } +} + + +/* Mark the argument, and all objects reachable from it, and any remaining + objects on the mark stack. Follow forwarding pointers in the scan. */ +/* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. + The assert for this is in markAndShouldScan: */ + + /* SpurMemoryManager>>#markAndTrace: */ +NeverInline void +markAndTrace(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt field; + sqInt fmt; + sqInt format; + sqInt formatSqInt; + sqInt header; + sqInt headerSqInt; + sqInt index; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt numStrongSlots; + sqInt objOop1; + sqInt objOopSqInt; + sqInt objOopSqIntSqInt; + sqInt objStack; + sqInt objStack1; + sqInt objStackSqInt; + sqInt objToScan; + sqInt scanLargeObject; + sqInt sp; + + /* begin markAndShouldScan: */ + if (((objOop & (tagMask())) != 0)) { + return; + } + + /* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. */ + assert(!(isForwarded(objOop))); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + return; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + format = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (/* isPureBitsFormat: */ + (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat()))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + markAndTraceClassOf(objOop); + } + return; + } + + /* avoid pushing non-pointer objects on the markStack. + Avoid tracing classes of non-objects on the heap, e.g. IRC caches, Sista counters. */ + if (format == (weakArrayFormat())) { + objStack = GIV(weaklingStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objOop)); + if (((objOop & (tagMask())) != 0)) { + assert(objStack == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack)) + ? fetchPointerofObject(ObjStackNextx, objStack) + : objStack)))); + } + else { + assert(!((objStack == GIV(markStack)) + && (isWeakNonImm(objOop)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack != GIV(weaklingStack)) + || (isWeakNonImm(objOop))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objOop, objStack); + return; + } + + /* push weaklings on the weakling stack to scan later */ + if ((format == (ephemeronFormat())) + && (activeAndDeferredScan(objOop))) { + return; + } + + /* Now scan the object, and any remaining objects on the mark stack. */ + + /* begin markLoopFrom: */ + numStrongSlots = 0; + objToScan = objOop; + + /* To avoid overflowing the mark stack when we encounter large objects, we + push the obj, then its numStrongSlots, and then index the object from the stack. */ + do { + if (((objToScan & (tagMask())) != 0)) { + scanLargeObject = 1; + } + else { + /* begin numStrongSlotsOfInephemeral: */ + fmt = (byteAt((void *)(objToScan + (formatFieldByteOffset())))) & (formatMask()); + assert((fmt != (ephemeronFormat())) + || ((isImmediate(keyOfEphemeron(objToScan))) + || (isMarked(keyOfEphemeron(objToScan))))); + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(objToScan)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objToScan + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objToScan - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (arrayFormat())) { + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (indexablePointersFormat())) { + if (((longAt((void *)(objToScan))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + setTraceFlagOnContextsFramesPageIfNeeded(objToScan); + + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l3; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objToScan))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l3: + numStrongSlots = CtxtTempFrameStart + contextSize; + goto l4; + } + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (weakArrayFormat())) { + objOopSqIntSqInt = fetchClassOfNonImm(objToScan); + numStrongSlots = (((longAt((void *)((objOopSqIntSqInt + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + goto l4; + } + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (forwardedFormat())) { + numStrongSlots = 1; + goto l4; + } + if (fmt < (firstCompiledMethodFormat())) { + numStrongSlots = 0; + goto l4; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objToScan)); + headerSqInt = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numStrongSlots = numLiterals + LiteralStart; + /* end numStrongSlotsOfInephemeral: */ +l4: + scanLargeObject = numStrongSlots > 64 /* traceImmediatelySlotLimit */; + } + if (scanLargeObject) { + if (((objToScan & (tagMask())) != 0)) { + index = (objToScan >> 3); + objToScan = topOfObjStack(GIV(markStack)); + } + else { + index = numStrongSlots; + markAndTraceClassOf(objToScan); + } + while (index > 0) { + index -= 1; + field = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if ((!(field & (tagMask())))) { + if ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + field = fixFollowedFieldofObjectwithInitialValue(index, objToScan, field); + } + + /* fixFollowedField: is /not/ inlined */ + + /* begin markAndShouldScan: */ + if (((field & (tagMask())) != 0)) { + goto l1; + } + + /* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. */ + assert(!(isForwarded(field))); + if ((byteAt((void *)(field + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + goto l1; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(field))); + byteAtput((void *)(field + (markBitsByteOffset())),(byteAt((void *)(field + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + format = (byteAt((void *)(field + (formatFieldByteOffset())))) & (formatMask()); + if (/* isPureBitsFormat: */ + (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat()))) { + if (((longAt((void *)(field))) & (classIndexMask())) > (lastClassIndexPun())) { + markAndTraceClassOf(field); + } + goto l1; + } + + /* avoid pushing non-pointer objects on the markStack. + Avoid tracing classes of non-objects on the heap, e.g. IRC caches, Sista counters. */ + if (format == (weakArrayFormat())) { + objStack1 = GIV(weaklingStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + goto l1; + } + + /* push weaklings on the weakling stack to scan later */ + if ((format == (ephemeronFormat())) + && (activeAndDeferredScan(field))) { + goto l1; + } + if (index > 0) { + if ((topOfObjStack(GIV(markStack))) != objToScan) { + objStack = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objToScan)); + if (((objToScan & (tagMask())) != 0)) { + assert(objStack == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack)) + ? fetchPointerofObject(ObjStackNextx, objStack) + : objStack)))); + } + else { + assert(!((objStack == GIV(markStack)) + && (isWeakNonImm(objToScan)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack != GIV(weaklingStack)) + || (isWeakNonImm(objToScan))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objToScan, objStack); + } + objOopSqInt = (((usqInt)index << 3) | 1); + objStackSqInt = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objOopSqInt)); + if (((objOopSqInt & (tagMask())) != 0)) { + assert(objStackSqInt == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStackSqInt)) + ? fetchPointerofObject(ObjStackNextx, objStackSqInt) + : objStackSqInt)))); + } + else { + assert(!((objStackSqInt == GIV(markStack)) + && (isWeakNonImm(objOopSqInt)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStackSqInt != GIV(weaklingStack)) + || (isWeakNonImm(objOopSqInt))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objOopSqInt, objStackSqInt); + } + objToScan = field; + index = -1; +l1:; + } + } + if (index >= 0) { + objToScan = popObjStack(GIV(markStack)); + if (objToScan == objOop) { + objToScan = popObjStack(GIV(markStack)); + } + } + } + else { + index = numStrongSlots; + markAndTraceClassOf(objToScan); + while (index > 0) { + index -= 1; + field = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if ((!(field & (tagMask())))) { + if ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + field = fixFollowedFieldofObjectwithInitialValue(index, objToScan, field); + } + + /* fixFollowedField: is /not/ inlined */ + + /* begin markAndShouldScan: */ + if (((field & (tagMask())) != 0)) { + goto l2; + } + + /* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. */ + assert(!(isForwarded(field))); + if ((byteAt((void *)(field + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + goto l2; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(field))); + byteAtput((void *)(field + (markBitsByteOffset())),(byteAt((void *)(field + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + formatSqInt = (byteAt((void *)(field + (formatFieldByteOffset())))) & (formatMask()); + if (/* isPureBitsFormat: */ + (formatSqInt >= (sixtyFourBitIndexableFormat())) + && (formatSqInt < (firstCompiledMethodFormat()))) { + if (((longAt((void *)(field))) & (classIndexMask())) > (lastClassIndexPun())) { + markAndTraceClassOf(field); + } + goto l2; + } + + /* avoid pushing non-pointer objects on the markStack. + Avoid tracing classes of non-objects on the heap, e.g. IRC caches, Sista counters. */ + if (formatSqInt == (weakArrayFormat())) { + objStack1 = GIV(weaklingStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + goto l2; + } + + /* push weaklings on the weakling stack to scan later */ + if ((formatSqInt == (ephemeronFormat())) + && (activeAndDeferredScan(field))) { + goto l2; + } + objStack1 = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + if (((byteAt((void *)(field + (numSlotsFieldByteOffset())))) > 64 /* traceImmediatelySlotLimit */) + && (((numStrongSlots = numStrongSlotsOfInephemeral(field))) > 64 /* traceImmediatelySlotLimit */)) { + objOop1 = (((usqInt)numStrongSlots << 3) | 1); + objStack1 = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objOop1)); + if (((objOop1 & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(objOop1)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(objOop1))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objOop1, objStack1); + } +l2:; + } + } + objToScan = popObjStack(GIV(markStack)); + } + + /* scanning a large object. scan until hitting an unmarked object, then switch to it, if any. + scanning a small object. scan, marking, pushing unmarked referents, then switch to the top of the stack. */ + } while(objToScan); +} + + +/* Go through the unscanned ephemerons, marking the inactive ones, and + removing them from the unscanned ephemerons. Answer if any inactive + ones were found. We cannot fire the ephemerons until all are found to + be active since scan-marking an inactive ephemeron later in the set may + render a previously-observed active ephemeron as inactive. */ + + /* SpurMemoryManager>>#markInactiveEphemerons */ +static sqInt +markInactiveEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt ephemeron; + sqInt field; + sqInt fmt; + sqInt format; + sqInt formatSqInt; + sqInt foundInactive; + sqInt header; + sqInt headerSqInt; + sqInt index; + sqInt key; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt numStrongSlots; + sqInt objOop; + sqInt objOop1; + sqInt objOopSqInt; + sqInt objOopSqIntSqInt; + sqInt objStack; + sqInt objStack1; + sqInt objStackSqInt; + sqInt objToScan; + usqInt ptr; + sqInt scanLargeObject; + sqInt sp; + + foundInactive = 0; + ptr = (GIV(unscannedEphemerons).start); + while (ptr <= ((GIV(unscannedEphemerons).top))) { + objOop = (ephemeron = longAt((void *)(ptr))); + + /* begin followedKeyOfEphemeron: */ + assert((isNonImmediate(objOop)) + && (isEphemeron(objOop))); + + /* begin followOopField:ofObject: */ + key = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isOopForwarded: */ + ((!(key & (tagMask())))) + && ((!((longAt((void *)(key))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + key = fixFollowedFieldofObjectwithInitialValue(0, objOop, key); + } + if ((((key & (tagMask())) != 0)) + || (((byteAt((void *)(key + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) { + foundInactive = 1; + + /* Now remove the inactive ephemeron from the set, and trace it. + Tracing it may add more ephemerons to the set. */ + if (((GIV(unscannedEphemerons).top)) > ptr) { + longAtput((void *)(ptr),longAt((void *)((GIV(unscannedEphemerons).top)))); + } + (GIV(unscannedEphemerons).top = ((GIV(unscannedEphemerons).top)) - BytesPerOop); + + /* To get markAndTrace: to trace the ephemeron we need to clear the mark bit. */ + + /* begin markLoopFrom: */ + numStrongSlots = 0; + objToScan = ephemeron; + + /* To avoid overflowing the mark stack when we encounter large objects, we + push the obj, then its numStrongSlots, and then index the object from the stack. */ + do { + if (((objToScan & (tagMask())) != 0)) { + scanLargeObject = 1; + } + else { + /* begin numStrongSlotsOfInephemeral: */ + fmt = (byteAt((void *)(objToScan + (formatFieldByteOffset())))) & (formatMask()); + assert((fmt != (ephemeronFormat())) + || ((isImmediate(keyOfEphemeron(objToScan))) + || (isMarked(keyOfEphemeron(objToScan))))); + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(objToScan)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objToScan + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objToScan - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (arrayFormat())) { + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (indexablePointersFormat())) { + if (((longAt((void *)(objToScan))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + setTraceFlagOnContextsFramesPageIfNeeded(objToScan); + + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l3; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objToScan))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l3: + numStrongSlots = CtxtTempFrameStart + contextSize; + goto l4; + } + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (weakArrayFormat())) { + objOopSqIntSqInt = fetchClassOfNonImm(objToScan); + numStrongSlots = (((longAt((void *)((objOopSqIntSqInt + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + goto l4; + } + numStrongSlots = numSlots; + goto l4; + } + if (fmt == (forwardedFormat())) { + numStrongSlots = 1; + goto l4; + } + if (fmt < (firstCompiledMethodFormat())) { + numStrongSlots = 0; + goto l4; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objToScan)); + headerSqInt = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numStrongSlots = numLiterals + LiteralStart; + /* end numStrongSlotsOfInephemeral: */ +l4: + scanLargeObject = numStrongSlots > 64 /* traceImmediatelySlotLimit */; + } + if (scanLargeObject) { + if (((objToScan & (tagMask())) != 0)) { + index = (objToScan >> 3); + objToScan = topOfObjStack(GIV(markStack)); + } + else { + index = numStrongSlots; + markAndTraceClassOf(objToScan); + } + while (index > 0) { + index -= 1; + field = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if ((!(field & (tagMask())))) { + if ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + field = fixFollowedFieldofObjectwithInitialValue(index, objToScan, field); + } + + /* fixFollowedField: is /not/ inlined */ + + /* begin markAndShouldScan: */ + if (((field & (tagMask())) != 0)) { + goto l1; + } + + /* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. */ + assert(!(isForwarded(field))); + if ((byteAt((void *)(field + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + goto l1; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(field))); + byteAtput((void *)(field + (markBitsByteOffset())),(byteAt((void *)(field + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + format = (byteAt((void *)(field + (formatFieldByteOffset())))) & (formatMask()); + if (/* isPureBitsFormat: */ + (format >= (sixtyFourBitIndexableFormat())) + && (format < (firstCompiledMethodFormat()))) { + if (((longAt((void *)(field))) & (classIndexMask())) > (lastClassIndexPun())) { + markAndTraceClassOf(field); + } + goto l1; + } + + /* avoid pushing non-pointer objects on the markStack. + Avoid tracing classes of non-objects on the heap, e.g. IRC caches, Sista counters. */ + if (format == (weakArrayFormat())) { + objStack1 = GIV(weaklingStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + goto l1; + } + + /* push weaklings on the weakling stack to scan later */ + if ((format == (ephemeronFormat())) + && (activeAndDeferredScan(field))) { + goto l1; + } + if (index > 0) { + if ((topOfObjStack(GIV(markStack))) != objToScan) { + objStack = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objToScan)); + if (((objToScan & (tagMask())) != 0)) { + assert(objStack == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack)) + ? fetchPointerofObject(ObjStackNextx, objStack) + : objStack)))); + } + else { + assert(!((objStack == GIV(markStack)) + && (isWeakNonImm(objToScan)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack != GIV(weaklingStack)) + || (isWeakNonImm(objToScan))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objToScan, objStack); + } + objOopSqInt = (((usqInt)index << 3) | 1); + objStackSqInt = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objOopSqInt)); + if (((objOopSqInt & (tagMask())) != 0)) { + assert(objStackSqInt == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStackSqInt)) + ? fetchPointerofObject(ObjStackNextx, objStackSqInt) + : objStackSqInt)))); + } + else { + assert(!((objStackSqInt == GIV(markStack)) + && (isWeakNonImm(objOopSqInt)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStackSqInt != GIV(weaklingStack)) + || (isWeakNonImm(objOopSqInt))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objOopSqInt, objStackSqInt); + } + objToScan = field; + index = -1; +l1:; + } + } + if (index >= 0) { + objToScan = popObjStack(GIV(markStack)); + if (objToScan == ephemeron) { + objToScan = popObjStack(GIV(markStack)); + } + } + } + else { + index = numStrongSlots; + markAndTraceClassOf(objToScan); + while (index > 0) { + index -= 1; + field = longAt((void *)((objToScan + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if ((!(field & (tagMask())))) { + if ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + field = fixFollowedFieldofObjectwithInitialValue(index, objToScan, field); + } + + /* fixFollowedField: is /not/ inlined */ + + /* begin markAndShouldScan: */ + if (((field & (tagMask())) != 0)) { + goto l2; + } + + /* if markAndTrace: is to follow and eliminate forwarding pointers + in its scan it cannot be handed an r-value which is forwarded. */ + assert(!(isForwarded(field))); + if ((byteAt((void *)(field + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + goto l2; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(field))); + byteAtput((void *)(field + (markBitsByteOffset())),(byteAt((void *)(field + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + formatSqInt = (byteAt((void *)(field + (formatFieldByteOffset())))) & (formatMask()); + if (/* isPureBitsFormat: */ + (formatSqInt >= (sixtyFourBitIndexableFormat())) + && (formatSqInt < (firstCompiledMethodFormat()))) { + if (((longAt((void *)(field))) & (classIndexMask())) > (lastClassIndexPun())) { + markAndTraceClassOf(field); + } + goto l2; + } + + /* avoid pushing non-pointer objects on the markStack. + Avoid tracing classes of non-objects on the heap, e.g. IRC caches, Sista counters. */ + if (formatSqInt == (weakArrayFormat())) { + objStack1 = GIV(weaklingStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + goto l2; + } + + /* push weaklings on the weakling stack to scan later */ + if ((formatSqInt == (ephemeronFormat())) + && (activeAndDeferredScan(field))) { + goto l2; + } + objStack1 = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(field)); + if (((field & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(field)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(field))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(field, objStack1); + if (((byteAt((void *)(field + (numSlotsFieldByteOffset())))) > 64 /* traceImmediatelySlotLimit */) + && (((numStrongSlots = numStrongSlotsOfInephemeral(field))) > 64 /* traceImmediatelySlotLimit */)) { + objOop1 = (((usqInt)numStrongSlots << 3) | 1); + objStack1 = GIV(markStack); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(objOop1)); + if (((objOop1 & (tagMask())) != 0)) { + assert(objStack1 == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack1)) + ? fetchPointerofObject(ObjStackNextx, objStack1) + : objStack1)))); + } + else { + assert(!((objStack1 == GIV(markStack)) + && (isWeakNonImm(objOop1)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack1 != GIV(weaklingStack)) + || (isWeakNonImm(objOop1))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(objOop1, objStack1); + } +l2:; + } + } + objToScan = popObjStack(GIV(markStack)); + } + + /* scanning a large object. scan until hitting an unmarked object, then switch to it, if any. + scanning a small object. scan, marking, pushing unmarked referents, then switch to the top of the stack. */ + } while(objToScan); + } + else { + ptr += BytesPerOop; + } + } + return foundInactive; +} + + +/* for profiling */ +/* Mark all accessible objects. + objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged is true if all + objects are unmarked and/or if unmarked classes shoud be removed from the + class table. */ +/* If the incremental collector is running mark bits may be set; stop it and + clear them if necessary. + */ + + /* SpurMemoryManager>>#markObjects: */ +static NoDbgRegParms NeverInline void +markObjects(sqInt objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt classOrNil; + sqInt classTablePage; + DisownState *disownState; + sqInt format; + sqInt i; + sqInt j; + sqInt objOop; + sqInt oop; + sqInt result; + StackPage *thePage; + sqInt toDoLimit; + CogVMThread *vmThread; + + runLeakCheckerFor(GCModeFull); + + /* begin shutDownGlobalIncrementalGC: */ + if (objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged) { + assert(allObjectsUnmarked()); + } + initializeUnscannedEphemerons(); + + /* begin initializeMarkStack */ + ensureRoomOnObjStackAt(MarkStackRootIndex); + ensureRoomOnObjStackAt(WeaklingStackRootIndex); + GIV(marking) = 1; + + /* begin markAccessibleObjectsAndFireEphemerons */ + assert(GIV(marking)); + assert(validClassTableRootPages()); + assert(allBridgesMarked()); + + /* for debugging markAndTrace: set (MarkStackRecord := OrderedCollection new) + This must come first to enable stack page reclamation. It clears + the trace flags on stack pages and so must precede any marking. + Otherwise it will clear the trace flags of reached pages. */ + + /* begin initStackPageGC */ + if (GIV(stackPage)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + (thePage->trace = StackPageUnreached); + } + + /* begin markAndTraceHiddenRoots */ + markAndTraceObjStackandContents(GIV(markStack), 0); + markAndTraceObjStackandContents(GIV(weaklingStack), 0); + markAndTraceObjStackandContents(GIV(mournQueue), 1); + objOop = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + /* begin freeListsObj */ + assert((firstIndexableField(oldSpaceObjectAfter(GIV(trueObj)))) == GIV(freeLists)); + objOop = oldSpaceObjectAfter(GIV(trueObj)); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + + /* begin isWeakNonImm: */ + format = (byteAt((void *)(GIV(classTableFirstPage) + (formatFieldByteOffset())))) & (formatMask()); + if (format == (weakArrayFormat())) { + markAndTrace(GIV(hiddenRootsObj)); + goto l1; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(GIV(hiddenRootsObj)))); + byteAtput((void *)(GIV(hiddenRootsObj) + (markBitsByteOffset())),(byteAt((void *)(GIV(hiddenRootsObj) + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + markAndTrace(GIV(classTableFirstPage)); + for (i = 1; i < GIV(numClassTablePages); i += 1) { + objOop = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + } + /* end markAndTraceHiddenRoots */ +l1: + + /* begin markAndTraceExtraRoots */ + assert(GIV(remapBufferCount) == 0); + + /* 1 to: remapBufferCount do: + [:i| + oop := remapBuffer at: i. + ((self isImmediate: oop) or: [self isFreeObject: oop]) ifFalse: + [self markAndTrace: oop]]. */ + for (i = 1; i <= GIV(extraRootCount); i += 1) { + oop = (GIV(extraRoots)[i])[0]; + if (!((((oop & (tagMask())) != 0)) + || (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())))) { + markAndTrace(oop); + } + } + assert(validClassTableRootPages()); + + /* begin markAndTraceInterpreterOops: */ + markAndTraceStackPages(1); + markAndTraceTraceLog(); + markAndTracePrimTraceLog(); + markAndTrace(GIV(specialObjectsOop)); + if (!(((GIV(newMethod) & (tagMask())) != 0))) { + markAndTrace(GIV(newMethod)); + } + + /* begin traceProfileState */ + /* begin followForwardingPointersInProfileState */ + /* begin profileStateDoUpdating: */ + if (GIV(profileProcess)) { + if ((result = ((!((longAt((void *)(GIV(profileProcess)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileProcess)) + : 0))) { + GIV(profileProcess) = result; + } + } + if (GIV(profileMethod)) { + if ((result = ((!((longAt((void *)(GIV(profileMethod)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileMethod)) + : 0))) { + GIV(profileMethod) = result; + } + } + if (GIV(profileSemaphore)) { + if ((result = ((!((longAt((void *)(GIV(profileSemaphore)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileSemaphore)) + : 0))) { + GIV(profileSemaphore) = result; + } + } + + /* begin profileStateDo: */ + if (GIV(profileProcess)) { + markAndTrace(GIV(profileProcess)); + } + if (GIV(profileMethod)) { + markAndTrace(GIV(profileMethod)); + } + if (GIV(profileSemaphore)) { + markAndTrace(GIV(profileSemaphore)); + } +# if LRPCheck + /* The longRunningPrimitiveCheckMethod (LRPCM) is sampled in an interrupt. Be very careful with it. + If longRunningPrimitiveCheckSequenceNumber (LRPCSN) = statCheckForEvents then LRPCM has + been recenty sampled, but it must be newMethod and we don't need to trace it twice. If LRPCSN + ~= statCheckForEvents then LRPCM must be some extant object and needs to be traced. */ + sqLowLevelMFence(); + if ((GIV(longRunningPrimitiveCheckMethod) != null) + && (GIV(longRunningPrimitiveCheckSequenceNumber) != GIV(statCheckForEvents))) { + if ((!((longAt((void *)(GIV(longRunningPrimitiveCheckMethod)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + GIV(longRunningPrimitiveCheckMethod) = followForwarded(GIV(longRunningPrimitiveCheckMethod)); + } + markAndTrace(GIV(longRunningPrimitiveCheckMethod)); + } + if (GIV(longRunningPrimitiveCheckSemaphore)) { + if ((!((longAt((void *)(GIV(longRunningPrimitiveCheckSemaphore)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + GIV(longRunningPrimitiveCheckSemaphore) = followForwarded(GIV(longRunningPrimitiveCheckSemaphore)); + } + markAndTrace(GIV(longRunningPrimitiveCheckSemaphore)); + } +# endif // LRPCheck + + if (GIV(tempOop)) { + markAndTrace(GIV(tempOop)); + } + if (GIV(tempOop2)) { + markAndTrace(GIV(tempOop2)); + } + + /* V3 memory manager support */ + for (j = 1; j <= GIV(remapBufferCount); j += 1) { + oop = GIV(remapBuffer)[j]; + if (!(((oop & (tagMask())) != 0))) { + markAndTrace(oop); + } + } + + /* Per-thread state; trace each thread's own newMethods, including those in outer calls if a thread is nested in an outer disownVM:/ownVM:. */ + toDoLimit = getNumThreads(); + for (i = 1; i <= toDoLimit; i += 1) { + /* begin vmThreadAt: */ + assert(((i >= 0) && (i <= GIV(numThreads)))); + vmThread = (i > 0 + ? GIV(threads)[i] + : ((CogVMThread *) null)); + if ((atomic_load((&((vmThread->state))))) > CTMInitializing) { + if ((vmThread->boundProcess)) { + if ((!((longAt((void *)((vmThread->boundProcess)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + (vmThread->boundProcess = followForwarded((vmThread->boundProcess))); + } + markAndTrace((vmThread->boundProcess)); + } + disownState = ((DisownState *) vmThread); + do { + if ((disownState->newMethodOrNull)) { + if ((!((longAt((void *)((vmThread->newMethodOrNull)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + (vmThread->newMethodOrNull = followForwarded((vmThread->newMethodOrNull))); + } + markAndTrace((disownState->newMethodOrNull)); + } + } while(((disownState = (disownState->nestedDisownStates)))); + } + } + assert(validObjStacks()); + markWeaklingsAndMarkAndFireEphemerons(); + assert(validObjStacks()); + + /* begin expungeDuplicateAndUnmarkedClasses: */ + for (i = 1; i < GIV(numClassTablePages); i += 1) { + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNil = longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + classIndex = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + assert((classOrNil == GIV(nilObj)) + || (addressCouldBeClassObj(classOrNil))); + + /* only remove a class if it is at a duplicate entry or it is unmarked and we're expunging unmarked classes. */ + if (classOrNil == GIV(nilObj)) { + if (classIndex < GIV(classTableIndex)) { + GIV(classTableIndex) = classIndex; + } + } + else { + if ((objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged + && (!(((byteAt((void *)(classOrNil + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) + || (((long32At((void *)(classOrNil + 4))) & (identityHashHalfWordMask())) != classIndex)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(classTablePage)) + && (!(isForwarded(classTablePage)))); + assert(validStorePointerUncheckedArgs(j, classTablePage, GIV(nilObj))); + longAtput((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord()))))),GIV(nilObj)); + + /* but if it is marked, it should still be in the table at its correct index. */ + assert((objectsShouldBeUnmarkedAndUnmarkedClassesShouldBeExpunged + && (!(isMarked(classOrNil)))) + || ((classAtIndex(rawHashBitsOf(classOrNil))) == classOrNil)); + + /* If the removed class is before the classTableIndex, set the + classTableIndex to point to the empty slot so as to reuse it asap. */ + if (classIndex < GIV(classTableIndex)) { + GIV(classTableIndex) = classIndex; + } + } + } + } + } + + /* Avoid expunging the puns by not scanning the 0th page. + classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + assert(GIV(classTableIndex) >= (1U << (classTableMajorIndexShift()))); + nilUnmarkedWeaklingSlots(); + GIV(marking) = 0; +} + + +/* After the initial scan-mark is complete ephemerons can be processed. + Weaklings have accumulated on the weaklingStack, but more may be + uncovered during ephemeron processing. So trace the strong slots + of the weaklings, and as ephemerons are processed ensure any newly + reached weaklings are also traced. */ + + /* SpurMemoryManager>>#markWeaklingsAndMarkAndFireEphemerons */ +static void +markWeaklingsAndMarkAndFireEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ephemeron; + sqInt ephemeronSqInt; + sqInt field; + sqInt i; + sqInt iSqInt; + sqInt key; + sqInt keySqInt; + sqInt numOnThisPage; + sqInt numToEnumerate; + sqInt numToEnumerateOnThisPage; + sqInt numTracedWeaklings; + sqInt numUnscannedBeforeMarkAndFire; + sqInt objStackPage; + usqInt p; + sqInt size; + sqInt toDoLimit; + sqInt topIndex; + sqInt weakling; + + numTracedWeaklings = 0; + while (1) { + assert(unscannedEphemeronsContainsOnlyEphemerons()); + markAndTraceUntracedReachableStackPages(); + markAndTraceMachineCodeOfMarkedMethods(); + + /* Make sure all reached weaklings have their strong slots traced before firing ephemerons... */ + do { + /* begin markAndTraceWeaklingsFrom: */ + /* begin objStack:from:do: */ + eassert(isValidObjStack(GIV(weaklingStack))); + size = longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + objStackPage = longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + while (objStackPage != 0) { + size += ObjStackLimit; + assert((fetchPointerofObject(ObjStackTopx, objStackPage)) == ObjStackLimit); + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + numToEnumerate = size - numTracedWeaklings; + objStackPage = GIV(weaklingStack); + while (numToEnumerate > 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + numToEnumerateOnThisPage = ((numToEnumerate < numOnThisPage) ? numToEnumerate : numOnThisPage); + topIndex = (numOnThisPage + ObjStackFixedSlots) - 1; + for (iSqInt = topIndex; iSqInt >= ((topIndex - numToEnumerateOnThisPage) + 1); iSqInt += -1) { + assert(isWeak(fetchPointerofObject(iSqInt, objStackPage))); + weakling = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + assert(!((isForwarded(weakling)))); + markAndTraceClassOf(weakling); + + /* N.B. generateToByDoLimitExpression:negative:on: guards against (unsigned)0 - 1 going +ve */ + toDoLimit = (numStrongSlotsOfWeakling(weakling)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + /* begin followOopField:ofObject: */ + field = longAt((void *)((weakling + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(field & (tagMask())))) + && ((!((longAt((void *)(field))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + field = fixFollowedFieldofObjectwithInitialValue(i, weakling, field); + } + if (!((((field & (tagMask())) != 0)) + || (((byteAt((void *)(field + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) { + markAndTrace(field); + } + } + } + numToEnumerate -= numToEnumerateOnThisPage; + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + numTracedWeaklings = size; + } while((sizeOfObjStack(GIV(weaklingStack))) > numTracedWeaklings); + if (((GIV(unscannedEphemerons).top)) < ((GIV(unscannedEphemerons).start))) { + markAndTraceUntracedReachableStackPages(); + markAndTraceMachineCodeOfMarkedMethods(); + freeUntracedStackPages(); + freeUnmarkedMachineCode(); + if (((GIV(unscannedEphemerons).top)) < ((GIV(unscannedEphemerons).start))) { + return; + } + } + assert(unscannedEphemeronsContainsOnlyEphemerons()); + while (markInactiveEphemerons()) { + } + assert(unscannedEphemeronsContainsOnlyActiveEphemerons()); + numUnscannedBeforeMarkAndFire = ((((sqInt) (((GIV(unscannedEphemerons).top)) - ((GIV(unscannedEphemerons).start))))) / BytesPerOop) + 1; + + /* begin fireAllUnscannedEphemerons */ + key = 0; + assert(allUnscannedEphemeronsAreActive()); + for (p = ((GIV(unscannedEphemerons).start)); p <= ((GIV(unscannedEphemerons).top)); p += BytesPerOop) { + ephemeron = longAt((void *)(p)); + if ((isEphemeron(ephemeron)) + && ((isNonImmediate((/* begin keyOfEphemeron: */ + assert((isNonImmediate(ephemeron)) + && (isObjEphemeron(ephemeron))), + (key = longAt((void *)((ephemeron + BaseHeaderSize) + (0U << (shiftForWord())))))))) + && (!(((byteAt((void *)(key + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)))) { + fireEphemeron(ephemeron); + } + } + + /* begin markAllUnscannedEphemerons */ + assert(allUnscannedEphemeronsAreActive()); + while (((GIV(unscannedEphemerons).top)) >= ((GIV(unscannedEphemerons).start))) { + ephemeronSqInt = longAt((void *)((GIV(unscannedEphemerons).start))); + longAtput((void *)((GIV(unscannedEphemerons).start)),longAt((void *)((GIV(unscannedEphemerons).top)))); + (GIV(unscannedEphemerons).top = ((GIV(unscannedEphemerons).top)) - BytesPerOop); + + /* begin followedKeyOfMaybeFiredEphemeron: */ + assert(isMaybeFiredEphemeron(ephemeronSqInt)); + keySqInt = longAt((void *)((ephemeronSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isOopForwarded: */ + ((!(keySqInt & (tagMask())))) + && ((!((longAt((void *)(keySqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + keySqInt = fixFollowedFieldofObjectwithInitialValue(0, ephemeronSqInt, keySqInt); + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(ephemeronSqInt))); + byteAtput((void *)(ephemeronSqInt + (markBitsByteOffset())),(byteAt((void *)(ephemeronSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + markAndTrace(keySqInt); + markAndTrace(ephemeronSqInt); + } + if (numUnscannedBeforeMarkAndFire == (((((sqInt) (((GIV(unscannedEphemerons).top)) - ((GIV(unscannedEphemerons).start))))) / BytesPerOop) + 1)) { + initializeUnscannedEphemerons(); + } + } +} + + /* SpurMemoryManager>>#maxIdentityHash */ +sqInt +maxIdentityHash(void) +{ + return identityHashHalfWordMask(); +} + + +/* Almost entirely arbitrary, but we dont want 1Mb bitmaps allocated in eden. + But this choice means no check for numSlots > maxSlotsForNewSpaceAlloc + for non-variable allocations. */ + + /* SpurMemoryManager>>#maxSlotsForNewSpaceAlloc */ +sqInt +maxSlotsForNewSpaceAlloc(void) +{ + return (1U << (fixedFieldsFieldWidth())) - 1; +} + + +/* Answers if the code is installed in a class instantiating objects with the + format. Used in primitive + generation to make a quick path based on where the method is installed. + This method cannot + be used as a guarantee as there can be false positive, it's just a + heuristic. Tries to interpret the last literal of the method as a behavior + (more than 3 fields, 3rd field a Smi). + If it can be interpreted as a behavior, answers if instSpec matches the + format, else answers false. */ + + /* SpurMemoryManager>>#maybeMethodClassOf:seemsToBeInstantiating: */ +sqInt +maybeMethodClassOfseemsToBeInstantiating(sqInt methodObj, sqInt format) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt instSpec; + sqInt literal; + sqInt maybeClassObj; + sqInt maybeFormat; + sqInt offset; + + /* begin methodClassOf: */ + offset = (literalCountOf(methodObj)) - 1; + literal = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = fixFollowedFieldofObjectwithInitialValue(offset + LiteralStart, methodObj, literal); + } + maybeClassObj = ((literal != GIV(nilObj)) + && (/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + ? (assert((numSlotsOf(literal)) > ValueIndex), + followFieldofObject(ValueIndex, literal)) + : GIV(nilObj)); + if (!(((byteAt((void *)(maybeClassObj + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) { + return 0; + } + if (!((numSlotsOfAny(maybeClassObj)) > InstanceSpecificationIndex)) { + return 0; + } + maybeFormat = longAt((void *)((maybeClassObj + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord())))))); + if (!((((maybeFormat) & 7) == 1))) { + return 0; + } + instSpec = (((usqInt)(((maybeFormat >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask()); + return instSpec == format; +} + + +/* Answer the minimum number of additional slots to allocate in an object to + always be able to shorten it. + This is enough slots to allocate a minimum-sized object. */ + + /* SpurMemoryManager>>#minSlotsForShortening */ +sqInt +minSlotsForShortening(void) +{ + return 16 /* (allocationUnit * 2) */ / BytesPerOop; +} + + +/* N.B. may be called from machine code on the Smalltalk stack so this should + not call deeply. + */ + + /* SpurMemoryManager>>#newHashBitsOf: */ +static NoDbgRegParms usqInt +newHashBitsOf(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt hash; + + /* begin newObjectHash */ + assert(!((GIV(lastHash) == 0))); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 2)); + GIV(lastHash) = GIV(lastHash) ^ ((((GIV(lastHash) << 7))) & (identityHashHalfWordMask())); + GIV(lastHash) = GIV(lastHash) ^ (((GIV(lastHash)) >> 3)); + hash = GIV(lastHash); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(objOop + 4),((((long32At((void *)(objOop + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + return hash & (identityHashHalfWordMask()); +} + + /* SpurMemoryManager>>#newSpaceIsEmpty */ +static sqInt +newSpaceIsEmpty(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (GIV(freeStart) == (((GIV(eden)).start))) + && (GIV(pastSpaceStart) == (((GIV(pastSpace)).start))); +} + + /* SpurMemoryManager>>#nilObject */ +sqInt +nilObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(nilObj); +} + + +/* Nil the unmarked slots in the weaklings on the + weakling stack, finalizing those that lost references. + Finally, empty the weaklingStack. */ +/* for profiling */ + + /* SpurMemoryManager>>#nilUnmarkedWeaklingSlots */ +static NeverInline void +nilUnmarkedWeaklingSlots(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anyUnmarked; + sqInt i; + sqInt iSqInt; + sqInt nextNextPage; + sqInt nextPage; + sqInt numOnThisPage; + usqInt numSlots; + sqInt numToEnumerate; + sqInt numToEnumerateOnThisPage; + sqInt objStackPage; + sqInt referent; + sqInt size; + sqInt toDoLimit; + sqInt topIndex; + sqInt weakling; + + eassert(allOldMarkedWeakObjectsOnWeaklingStack()); + if (GIV(weaklingStack) == GIV(nilObj)) { + return; + } + + /* begin objStack:from:do: */ + eassert(isValidObjStack(GIV(weaklingStack))); + size = longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + objStackPage = longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + while (objStackPage != 0) { + size += ObjStackLimit; + assert((fetchPointerofObject(ObjStackTopx, objStackPage)) == ObjStackLimit); + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + numToEnumerate = size; + objStackPage = GIV(weaklingStack); + while (numToEnumerate > 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + numToEnumerateOnThisPage = ((numToEnumerate < numOnThisPage) ? numToEnumerate : numOnThisPage); + topIndex = (numOnThisPage + ObjStackFixedSlots) - 1; + for (i = topIndex; i >= ((topIndex - numToEnumerateOnThisPage) + 1); i += -1) { + assert(isWeak(fetchPointerofObject(i, objStackPage))); + weakling = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin nilUnmarkedWeaklingSlotsIn: */ + anyUnmarked = 0; + assert(allStrongSlotsOfWeaklingAreMarked(weakling)); + + /* N.B. generateToByDoLimitExpression:negative:on: guards against (unsigned)0 - 1 going +ve */ + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(weakling)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(weakling + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(weakling - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (iSqInt = (numStrongSlotsOfWeakling(weakling)); iSqInt <= toDoLimit; iSqInt += 1) { + referent = longAt((void *)((weakling + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if ((!(referent & (tagMask())))) { + if (((longAt((void *)(referent))) & (classIndexMask())) == (isForwardedObjectClassIndexPun())) { + referent = fixFollowedFieldofObjectwithInitialValue(iSqInt, weakling, referent); + } + if (!((((referent & (tagMask())) != 0)) + || (((byteAt((void *)(referent + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(weakling)) + && (!(isForwarded(weakling)))); + assert(validStorePointerUncheckedArgs(iSqInt, weakling, GIV(nilObj))); + longAtput((void *)((weakling + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord()))))),GIV(nilObj)); + anyUnmarked = 1; + } + } + } + if (anyUnmarked) { + /* fireFinalization: could grow the mournQueue and if so, + additional pages must be marked to avoid being GC'ed. */ + assert(GIV(marking)); + + /* begin fireFinalization: */ + if (((imageHeaderFlags & 64) != 0)) { + queueMourner(weakling); + } + + /* begin signalFinalization: */ + forceInterruptCheck(); + GIV(pendingFinalizationSignals) += 1; + } + } + numToEnumerate -= numToEnumerateOnThisPage; + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + + /* begin emptyObjStack: */ + if (GIV(weaklingStack) == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(GIV(weaklingStack))); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(GIV(weaklingStack))) == (wordIndexableFormat())); + longAtput((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))),0); + nextPage = longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + while (nextPage != 0) { + nextNextPage = longAt((void *)((nextPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(nextPage)) == (wordIndexableFormat())); + longAtput((void *)((nextPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),longAt((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))))); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(nextPage)) == (wordIndexableFormat())); + longAtput((void *)((nextPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord()))))),0); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(GIV(weaklingStack))) == (wordIndexableFormat())); + longAtput((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),nextPage); + nextPage = nextNextPage; + } + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(GIV(weaklingStack))) == (wordIndexableFormat())); + longAtput((void *)((GIV(weaklingStack) + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord()))))),0); + eassert(isValidObjStack(GIV(weaklingStack))); + /* end emptyObjStack: */ +l1:; +} + + /* SpurMemoryManager>>#noCheckClassAtIndex: */ +static NoDbgRegParms sqInt +noCheckClassAtIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classTablePage; + sqInt fieldIndex; + + fieldIndex = ((usqInt)(classIndex)) >> (classTableMajorIndexShift()); + + /* begin fetchPointer:ofObject: */ + classTablePage = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (classTablePage == GIV(nilObj)) { + return null; + } + fieldIndex = classIndex & ((1U << (classTableMajorIndexShift())) - 1); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((classTablePage + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); +} + + +/* Push an element on an objStack. Split from push:onObjStack: for testing. */ + + /* SpurMemoryManager>>#noCheckPush:onObjStack: */ +static NoDbgRegParms sqInt +noCheckPushonObjStack(sqInt objOop, sqInt objStack) +{ + sqInt topx; + + eassert(isValidObjStack(objStack)); + + /* for debugging markAndTrace: set (MarkStackRecord := OrderedCollection new) */ + topx = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + if (topx >= ObjStackLimit) { + noCheckPushonObjStack(objOop, ensureRoomOnObjStackAt(longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))))); + } + else { + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(objStack)) == (wordIndexableFormat())); + longAtput((void *)((objStack + BaseHeaderSize) + ((((usqInt)((ObjStackFixedSlots + topx)) << (shiftForWord()))))),objOop); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(objStack)) == (wordIndexableFormat())); + longAtput((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))),topx + 1); + } + return objOop; +} + + +/* Make sure the oop at fieldIndex in anObject is not forwarded (follow the + forwarder there-in if so). Answer the (possibly followed) oop at + fieldIndex. */ + + /* SpurMemoryManager>>#noFixupFollowField:ofObject: */ +static NoDbgRegParms sqInt +noFixupFollowFieldofObject(sqInt fieldIndex, sqInt anObject) +{ + sqInt objOop; + sqInt referent; + + objOop = longAt((void *)((anObject + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + referent = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + objOop = referent; + } + return objOop; +} + + +/* Allocate an object with numSlots space. If there is room beneath + scavengeThreshold allocate in newSpace, otherwise alocate in oldSpace. If + there is not room in newSpace + and a scavenge is not already scheduled, schedule a scavenge. */ + + /* SpurMemoryManager>>#noInlineAllocateSlots:format:classIndex: */ +static NoDbgRegParms NeverInline sqInt +noInlineAllocateSlotsformatclassIndex(sqInt numSlots, sqInt formatField, sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newObj; + usqInt numBytes; + + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if ((((usqInt)(numSlots)) >> 56) > 0) { + return null; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + return allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, formatField, classIndex); + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex); + } + else { + longAtput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + classIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + return newObj; +} + + /* SpurMemoryManager>>#noUnscannedEphemerons */ +static int +noUnscannedEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((GIV(unscannedEphemerons).top)) < ((GIV(unscannedEphemerons).start)); +} + + +/* Answer the number of indexable bytes in the given non-immediate pure bits + object with format. + */ + + /* SpurMemoryManager>>#numBytesOfBits:format: */ +static NoDbgRegParms sqInt +numBytesOfBitsformat(sqInt objOop, sqInt format) +{ + usqInt numBytes; + usqInt numSlots; + + assert((isPureBitsFormat(format)) + && ((formatOf(objOop)) == format)); + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (format < (firstShortFormat())) { + return (format <= (sixtyFourBitIndexableFormat()) + ? numBytes + : numBytes - (((format & 1) << 2))); + } + return (format < (firstByteFormat()) + ? numBytes - (((format & 3) << 1)) + : numBytes - (format & 7)); +} + + +/* Answer the number of indexable bytes in the given non-immediate + byte-indexable object. + */ + + /* SpurMemoryManager>>#numBytesOfBytes: */ +static NoDbgRegParms sqInt +numBytesOfBytes(sqInt objOop) +{ + sqInt fmt; + usqInt numSlots; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + return ((((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); +} + + +/* Answer the number of indexable bytes in the given non-immediate object. + Does not adjust the size of contexts by stackPointer. */ + + /* SpurMemoryManager>>#numBytesOf: */ +sqInt +numBytesOf(sqInt objOop) +{ + sqInt fmt; + usqInt numBytes; + usqInt numSlots; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numBytes = (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + numBytes = (numBytes << (shiftForWord())); + if (fmt >= (firstByteFormat())) { + return numBytes - (fmt & 7); + } + + /* bytes (the common case), including CompiledMethod */ + if (fmt <= (sixtyFourBitIndexableFormat())) { + return numBytes; + } + if (fmt >= (firstShortFormat())) { + return numBytes - (((fmt & 3) << 1)); + } + + /* fmt >= self firstLongFormat */ + return numBytes - (((fmt & 1) << 2)); +} + + +/* Answer the number of pointer fields in the given object. + Works with CompiledMethods, as well as ordinary objects. */ + + /* SpurMemoryManager>>#numPointerSlotsOf: */ +static NoDbgRegParms usqInt +numPointerSlotsOf(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt fmt; + sqInt header; + sqInt headerSqInt; + usqInt numLiterals; + usqInt numSlots; + sqInt sp; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin numPointerSlotsOf:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + return CtxtTempFrameStart + contextSize; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + return (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + } + if (fmt == (forwardedFormat())) { + return 1; + } + if (fmt < (firstCompiledMethodFormat())) { + return 0; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + return numLiterals + LiteralStart; +} + + /* SpurMemoryManager>>#numSlotsForBytes: */ +static NoDbgRegParms sqInt +numSlotsForBytes(sqInt numBytes) +{ + return (numBytes + (BytesPerWord - 1)) / BytesPerWord; +} + + +/* A private internal version of numSlotsOf: that is happy to be applied to + free or forwarded objects. + */ + + /* SpurMemoryManager>>#numSlotsOfAny: */ +static NoDbgRegParms usqInt +numSlotsOfAny(sqInt objOop) +{ + usqInt numSlots; + + numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + return (numSlots == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); +} + + +/* Answer the number of strong pointer fields in the given indexable pointer + object. + */ + + /* SpurMemoryManager>>#numSlotsOfIndexablePointerObj: */ +static NoDbgRegParms usqInt +numSlotsOfIndexablePointerObj(sqInt objOop) +{ + usqInt numSlots; + + assert((formatOf(objOop)) == (indexablePointersFormat())); + if (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + return CtxtTempFrameStart + (fetchStackPointerOf(objOop)); + } + else { + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + return (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + } +} + + +/* numSlotsOf: should not be applied to free or forwarded objects. */ + + /* SpurMemoryManager>>#numSlotsOf: */ +usqInt +numSlotsOf(sqInt objOop) +{ + usqInt numSlots; + + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + return (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); +} + + +/* Answer the number of strong pointer fields in the given object, + which is expected not to be an active ephemeron. + Works with CompiledMethods as well as ordinary objects. */ + + /* SpurMemoryManager>>#numStrongSlotsOfInephemeral: */ +static NoDbgRegParms sqInt +numStrongSlotsOfInephemeral(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt fmt; + sqInt header; + sqInt headerSqInt; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objOopSqInt; + sqInt sp; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + assert((fmt != (ephemeronFormat())) + || ((isImmediate(keyOfEphemeron(objOop))) + || (isMarked(keyOfEphemeron(objOop))))); + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (arrayFormat())) { + return numSlots; + } + if (fmt == (indexablePointersFormat())) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + setTraceFlagOnContextsFramesPageIfNeeded(objOop); + + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + return CtxtTempFrameStart + contextSize; + } + return numSlots; + } + if (fmt == (weakArrayFormat())) { + objOopSqInt = fetchClassOfNonImm(objOop); + + /* begin fixedFieldsOfClass: */ + return (((longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + } + return numSlots; + } + if (fmt == (forwardedFormat())) { + return 1; + } + if (fmt < (firstCompiledMethodFormat())) { + return 0; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + return numLiterals + LiteralStart; +} + + +/* Answer the number of strong pointer fields in the given weakling. */ + + /* SpurMemoryManager>>#numStrongSlotsOfWeakling: */ +static NoDbgRegParms sqInt +numStrongSlotsOfWeakling(sqInt objOop) +{ + sqInt objOopSqInt; + + assert((formatOf(objOop)) == (weakArrayFormat())); + objOopSqInt = fetchClassOfNonImm(objOop); + + /* begin fixedFieldsOfClass: */ + return (((longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); +} + + /* SpurMemoryManager>>#numUnscannedEphemerons */ +static sqInt +numUnscannedEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((((sqInt) (((GIV(unscannedEphemerons).top)) - ((GIV(unscannedEphemerons).start))))) / BytesPerOop) + 1; +} + + +/* Answer if objOop looks like a class object. WIth Spur be lenient if the + object doesn't + yet have a hash (i.e. is not yet in the classTable), and accept forwarding + pointers. + */ + + /* SpurMemoryManager>>#objCouldBeClassObj: */ +sqInt +objCouldBeClassObj(sqInt objOop) +{ + sqInt fieldOop; + + return (((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + && (((numSlotsOfAny(objOop)) > InstanceSpecificationIndex) + && (((fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord()))))))), + ((addressCouldBeObj(fieldOop)) + && ((((byteAt((void *)(fieldOop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + || ((/* isOopForwarded: */ + ((!(fieldOop & (tagMask())))) + && ((!((longAt((void *)(fieldOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + && (isPointers(followForwarded(fieldOop)))))) + && (((fieldOop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord()))))))), + ((addressCouldBeObj(fieldOop)) + && ((((byteAt((void *)(fieldOop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + || ((/* isOopForwarded: */ + ((!(fieldOop & (tagMask())))) + && ((!((longAt((void *)(fieldOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) + && (isPointers(followForwarded(fieldOop)))))) + && (((((longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord())))))))) & 7) == 1))))))); +} + + +/* Object parsing. + 1. all objects have at least a word following the header, for a forwarding + pointer. 2. objects with an overflow size have a preceeing word with a + saturated slotSize. If the word following + an object doesn't have a saturated size field it must be a single-header + object. If the word following + does have a saturated slotSize it must be the overflow size word. */ + + /* SpurMemoryManager>>#objectAfter: */ +sqInt +objectAfter(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + + if (oopisLessThan(objOop, GIV(oldSpaceStart))) { + if (oopisGreaterThanOrEqualToandLessThan(objOop, ((GIV(eden)).start), GIV(freeStart))) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + return GIV(freeStart); + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + } + if (oopisGreaterThanOrEqualToandLessThan(objOop, ((GIV(pastSpace)).start), GIV(pastSpaceStart))) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + return GIV(pastSpaceStart); + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + } + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(futureSurvivorStart))) { + return GIV(futureSurvivorStart); + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + } + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + return GIV(endOfMemory); + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); +} + + +/* useful for debugging */ + + /* SpurMemoryManager>>#objectBefore: */ +sqInt +objectBefore(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOopSqInt; + sqInt prev; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + prev = null; + if (oopisLessThan(objOop, GIV(oldSpaceStart))) { + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l2; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOopSqInt, GIV(freeStart))) { + if (oopisGreaterThanOrEqualTo(objOopSqInt, objOop)) { + return prev; + } + prev = objOopSqInt; + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOopSqInt = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + /* end allNewSpaceEntitiesDo: */ +l2: + return prev; + } + + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + if (oopisGreaterThanOrEqualTo(objOopSqInt, objOop)) { + return prev; + } + prev = objOopSqInt; + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3:; + } + return prev; +} + + +/* For assertions only... This ends the recursion in setting up the bridged + enumerations of new space that need objectBefore: for assertion checking. */ + + /* SpurMemoryManager>>#objectInPastSpaceBefore: */ +static NoDbgRegParms sqInt +objectInPastSpaceBefore(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt prev; + + if (GIV(pastSpaceStart) <= (((GIV(pastSpace)).start))) { + return null; + } + prev = null; + address = ((GIV(pastSpace)).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + obj = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + while (oopisLessThan(obj, GIV(pastSpaceStart))) { + if (oopisGreaterThanOrEqualTo(obj, objOop)) { + return prev; + } + prev = obj; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + obj = GIV(pastSpaceStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + return prev; +} + + +/* For enumerating objects find the header of the first object in a space. + If the object starts with an overflow size field it will start at the next + allocationUnit. c.f. numSlotsOf: */ + + /* SpurMemoryManager>>#objectStartingAt: */ +static NoDbgRegParms sqInt +objectStartingAt(sqInt address) +{ + usqInt numSlots; + + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + return (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); +} + + +/* This primitive is called from Squeak as... + arrayOfRoots uniquelyAccessibleObjects + + This primitive answers an array of the receiver and every object in its + proper tree of subParts (ie, that is not refered to from anywhere else + outside the tree). + + The primitive can fail for the following reasons with the specified + failure codes: + PrimErrNoMemory: additional allocations failed + */ + + /* SpurMemoryManager>>#objectsAccessibleFromRoots: */ +static NoDbgRegParms sqInt +objectsAccessibleFromRoots(sqInt arrayOfRootsArg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayOfObjects; + sqInt arrayOfRoots; + + runLeakCheckerFor(GCModeImageSegment); + + /* First scavenge to collect any new space garbage that refers to the graph. */ + + /* begin scavengingGC */ + scavengingGCTenuringIf(TenureByAge); + arrayOfRoots = /* updatePostScavenge: */ + ((!((longAt((void *)(arrayOfRootsArg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(arrayOfRootsArg) + : arrayOfRootsArg); + + /* Now compute the transitive closure, collecting the sequence of objects to be stored in the arrayOfObjects array. + Included in arrayOfObjects are the arrayOfRoots and all its contents. All objects have been unmarked. */ + arrayOfObjects = objectsReachableFromRoots(arrayOfRoots); + if (!arrayOfObjects) { + return (((usqInt)PrimErrNoMemory << 3) | 1); + } + + /* If objectsReachableFromRoots: answers an integer there is not enough continuous free space in which to allocate the + reachable objects. If there is sufficient free space then answer an error code to prompt a compacting GC and a retry. */ + if ((((arrayOfObjects) & 7) == 1)) { + if ((GIV(totalFreeOldSpace) - 8 /* allocationUnit */) >= ((arrayOfObjects >> 3))) { + return (((usqInt)PrimErrNeedCompaction << 3) | 1); + } + return (((usqInt)PrimErrNoMemory << 3) | 1); + } + assert(allObjectsUnmarked()); + assert(!((forwardersIn(arrayOfObjects)))); + return arrayOfObjects; +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. + Answer an Array of all the objects only reachable from the argument, an + Array of root objects, + starting with arrayOfRoots. If there is no space, answer a SmallInteger + whose value is the + number of slots required. This is used to collect the objects to include + in an image segment + on Spur, separate from creating the segment, hence simplifying the + implementation. Thanks to Igor Stasenko for this idea. */ + + /* SpurMemoryManager>>#objectsReachableFromRoots: */ +static NoDbgRegParms NeverInline sqInt +objectsReachableFromRoots(sqInt arrayOfRoots) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt count; + sqInt delta; + sqInt followingWord; + usqInt followingWordAddress; + usqInt freeChunk; + sqInt i; + sqInt iSqInt; + usqInt limit; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt oop; + sqInt oopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + usqInt ptr; + sqInt referent; + usqInt start; + usqInt startUsqInt; + sqInt toDoLimit; + sqInt toDoLimit1; + sqInt toDoLimitSqInt; + + + /* & hence start & ptr are too; limit is also because of addressAfter: */ + assert(isArray(arrayOfRoots)); + + /* Mark all objects except those only reachable from the arrayOfRoots by marking + each object in arrayOfRoots and then marking all reachable objects (from the + system roots). This leaves unmarked only objects reachable from the arrayOfRoots. + N.B. A side-effect of the marking is that all forwarders in arrayOfRoots will be followed. */ + assert(allObjectsUnmarked()); + + /* begin setGCMode: */ + GIV(gcMode) = GCModeImageSegment; + assert(!(isFreeObject(arrayOfRoots))); + byteAtput((void *)(arrayOfRoots + (markBitsByteOffset())),(byteAt((void *)(arrayOfRoots + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + toDoLimitSqInt = ((/* begin numSlotsOf: */ + assert((classIndexOf(arrayOfRoots)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(arrayOfRoots + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(arrayOfRoots - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (iSqInt = 0; iSqInt <= toDoLimitSqInt; iSqInt += 1) { + /* begin followField:ofObject: */ + oopSqInt = longAt((void *)((arrayOfRoots + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(oopSqInt & (tagMask())))) + && ((!((longAt((void *)(oopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + oopSqInt = fixFollowedFieldofObjectwithInitialValue(iSqInt, arrayOfRoots, oopSqInt); + } + if ((!(oopSqInt & (tagMask())))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oopSqInt))); + byteAtput((void *)(oopSqInt + (markBitsByteOffset())),(byteAt((void *)(oopSqInt + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + } + } + markObjects(0); + + /* After the mark phase all unreachable weak slots will have been nilled + and all active ephemerons fired. */ + assert(isEmptyObjStack(GIV(markStack))); + assert(isEmptyObjStack(GIV(weaklingStack))); + assert(noUnscannedEphemerons()); + + /* Now unmark the roots before collecting the transitive closure of unmarked objects accessible from the roots. */ + + /* begin unmarkObjectsIn: */ + toDoLimitSqInt = ((assert((classIndexOf(arrayOfRoots)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(arrayOfRoots + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(arrayOfRoots - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (iSqInt = 0; iSqInt <= toDoLimitSqInt; iSqInt += 1) { + /* begin followField:ofObject: */ + oopSqInt = longAt((void *)((arrayOfRoots + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(oopSqInt & (tagMask())))) + && ((!((longAt((void *)(oopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + oopSqInt = fixFollowedFieldofObjectwithInitialValue(iSqInt, arrayOfRoots, oopSqInt); + } + if ((!(oopSqInt & (tagMask())))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oopSqInt))); + byteAtput((void *)(oopSqInt + (markBitsByteOffset())),(byteAt((void *)(oopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + + /* Use the largest free chunk to answer the result. */ + + /* N.B. Does /not/ update totalFreeOldSpace */ + freeChunk = allocateLargestFreeChunk(); + + /* but must update so that growth in the markStack does not cause assert fails. */ + GIV(totalFreeOldSpace) -= bytesInBody(freeChunk); + ptr = (start = freeChunk + BaseHeaderSize); + limit = addressAfter(freeChunk); + count = 0; + + /* First put the arrayOfRoots; order is important. */ + noCheckPushonObjStack(arrayOfRoots, GIV(markStack)); + + /* Now collect the roots and the transitive closure of unmarked objects from them. */ + while (!(isEmptyObjStack(GIV(markStack)))) { + objOop = popObjStack(GIV(markStack)); + assert(isMarked(objOop)); + count += 1; + if (ptr < limit) { + longAtput((void *)(ptr),objOop); + ptr += BytesPerOop; + } + oop = fetchClassOfNonImm(objOop); + if (!((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (markedBitByteShift())))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + noCheckPushonObjStack(oop, GIV(markStack)); + } + if ((((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(objOop))))) { + toDoLimit = (numSlotsOfMarriedContext(objOop)) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + oop = fetchPointerofMarriedContext(i, objOop); + if (!((((oop & (tagMask())) != 0)) + || (((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + noCheckPushonObjStack(oop, GIV(markStack)); + } + } + } + else { + toDoLimit1 = (numPointerSlotsOf(objOop)) - 1; + for (i = 0; i <= toDoLimit1; i += 1) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (!((((oop & (tagMask())) != 0)) + || (((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) | (1U << (markedBitByteShift()))); + noCheckPushonObjStack(oop, GIV(markStack)); + } + } + } + } + + /* begin unmarkAllObjects */ + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + if ((byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) > (lastClassIndexPun())) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + else { + if (!(((longAt((void *)(objOopSqInt))) & (classIndexMask())) == (segmentBridgePun()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + startUsqInt = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (startUsqInt > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(startUsqInt + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? startUsqInt + BaseHeaderSize + : startUsqInt); + while (oopisLessThan(objOopSqInt, GIV(freeStart))) { + if ((byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) > (lastClassIndexPun())) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + else { + if (!(((longAt((void *)(objOopSqInt))) & (classIndexMask())) == (segmentBridgePun()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOopSqInt = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3: + + /* Now try and allocate the result */ + if ((count > ((ptr - start) / BytesPerOop)) + || ((limit != ptr) + && ((limit - ptr) <= 8 /* allocationUnit */))) { + freeObject(freeChunk); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeImageSegment)) == (GCCheckFreeSpace | GCModeImageSegment))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + + /* begin setGCMode: */ + GIV(gcMode) = 0; + return (((usqInt)count << 3) | 1); + } + + /* not enough room + can't split a single word + There's room; set the format, & classIndex and shorten. */ + + /* begin setFormatOf:to: */ + assert((((arrayFormat()) >= 0) && ((arrayFormat()) <= (formatMask())))); + byteAtput((void *)(freeChunk + (formatFieldByteOffset())),((byteAt((void *)(freeChunk + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (arrayFormat())); + + /* begin setClassIndexOf:to: */ + assert(((ClassArrayCompactIndex >= 0) && (ClassArrayCompactIndex <= (classIndexMask())))); + longAtput((void *)(freeChunk),((longAt((void *)(freeChunk))) & (~(usqIntptr_t)(classIndexMask()))) + ClassArrayCompactIndex); + + /* begin shorten:toIndexableSize: */ + assert((count >= 0) + && (count < (lengthOf(freeChunk)))); + delta = doShortentoIndexableSize(freeChunk, count); + assert((lengthOf(followMaybeForwarded(freeChunk))) == count); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + if ((!((longAt((void *)(freeChunk))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(freeChunk)); + referent = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + freeChunk = referent; + } + + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(freeChunk + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(freeChunk); + } + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeImageSegment)) == (GCCheckFreeSpace | GCModeImageSegment))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + runLeakCheckerFor(GCModeImageSegment); + + /* begin setGCMode: */ + GIV(gcMode) = 0; + return freeChunk; +} + + +/* Verify that the given oop is legitimate. Check address, header, and size + but not class. + */ + + /* SpurMemoryManager>>#okayOop: */ +static NoDbgRegParms sqInt +okayOop(sqInt signedOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt fmt; + usqInt oop; + usqLong unusedBits; + sqInt unusedBitsInYoungObjects; + + oop = ((usqInt) signedOop); + + /* address and size checks */ + if (((oop & (tagMask())) != 0)) { + return 1; + } + if (!(addressCouldBeObj(oop))) { + error("oop is not a valid address"); + return 0; + } + if (!(oopisLessThanOrEqualTo(addressAfter(oop), GIV(endOfMemory)))) { + error("oop size would make it extend beyond the end of memory"); + return 0; + } + + /* header type checks */ + if (!(((classIndex = (longAt((void *)(oop))) & (classIndexMask()))) >= (firstClassIndexPun()))) { + error("oop is a free chunk, or bridge, not an object"); + return 0; + } + if (((byteAt((void *)(oop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) + && ((byteAt((void *)((oop - BaseHeaderSize) + (numSlotsFieldByteOffset())))) != (numSlotsMask()))) { + error("oop header has overflow header word, but overflow word does not have a saturated numSlots field"); + return 0; + } + + /* format check */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if ((fmt == 6) || (fmt == 8)) { + error("oop has an unknown format type"); + return 0; + } + if ((fmt == (forwardedFormat())) != (classIndex == (isForwardedObjectClassIndexPun()))) { + error("oop has mis-matched format/classIndex fields; only one of them is the isForwarded value"); + return 0; + } + + /* specific header bit checks */ + unusedBits = (0x400000) | (0x40000000000000LL); + if ((long64At((void *)(oop))) & unusedBits) { + error("some unused header bits are set; should be zero"); + return 0; + } + unusedBitsInYoungObjects = ((1ULL << (greyBitShift())) | (1U << (pinnedBitShift()))) | (1U << (rememberedBitShift())); + if ((longAt((void *)(oop))) & unusedBitsInYoungObjects) { + error("some header bits unused in young objects are set; should be zero"); + return 0; + } + return 1; +} + + +/* useful for debugging */ +/* Object parsing. + 1. all objects have at least a word following the header, for a forwarding + pointer. 2. objects with an overflow size have a preceeing word with a + saturated slotSize. If the word following + an object doesn't have a saturated size field it must be a single-header + object. If the word following + does have a saturated slotSize it must be the overflow size word. */ + + /* SpurMemoryManager>>#oldSpaceObjectAfter: */ +sqInt +oldSpaceObjectAfter(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + return GIV(endOfMemory); + } + followingWord = longAt((void *)(followingWordAddress)); + return ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); +} + + +/* in an effort to fix a compiler bug with two-way become post r3427 */ +/* Allocate two new objects, n1 & n2. Copy the contents appropriately. + Convert obj1 and obj2 + into forwarding objects pointing to n2 and n1 respectively. No need to + check if cloneObject: + succeeds because an earlier pass over objects ensured that there is enough + memory. + */ + + /* SpurMemoryManager>>#outOfPlaceBecome:and:copyHashFlag: */ +static NoDbgRegParms NeverInline void +outOfPlaceBecomeandcopyHashFlag(sqInt obj1, sqInt obj2, sqInt copyHashFlag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt clone1; + sqInt clone2; + sqInt hash; + + clone1 = (((longAt((void *)(obj1))) & (classIndexMask())) == ClassMethodContextCompactIndex + ? cloneContext(obj1) + : cloneObject(obj1)); + clone2 = (((longAt((void *)(obj2))) & (classIndexMask())) == ClassMethodContextCompactIndex + ? cloneContext(obj2) + : cloneObject(obj2)); + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(obj1 + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* begin setIsImmutableOf:to: */ + longAtput((void *)(clone1),(longAt((void *)(clone1))) | (1U << (immutableBitShift()))); + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(obj2 + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* begin setIsImmutableOf:to: */ + longAtput((void *)(clone2),(longAt((void *)(clone2))) | (1U << (immutableBitShift()))); + } + if (copyHashFlag) { + hash = (long32At((void *)(obj1 + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(clone1 + 4),((((long32At((void *)(clone1 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + hash = (long32At((void *)(obj2 + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(clone2 + 4),((((long32At((void *)(clone2 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + } + else { + hash = (long32At((void *)(obj2 + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(clone1 + 4),((((long32At((void *)(clone1 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + hash = (long32At((void *)(obj1 + 4))) & (identityHashHalfWordMask()); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(clone2 + 4),((((long32At((void *)(clone2 + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + } + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(obj1),((longAt((void *)(obj1))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(obj1),(longAt((void *)(obj1))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(obj1)); + assert(!(isOopForwarded(clone2))); + + /* begin isOldObject: */ + assert(isNonImmediate(obj1)); + if (oopisGreaterThanOrEqualTo(obj1, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(clone2 & (tagMask())))) + && (oopisLessThan(clone2, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj1 + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj1); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj1 + BaseHeaderSize) + (0U << (shiftForWord()))),clone2); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(obj1 + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(obj1 + (numSlotsFieldByteOffset())),1); + } + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(obj2),((longAt((void *)(obj2))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(obj2),(longAt((void *)(obj2))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(obj2)); + assert(!(isOopForwarded(clone1))); + + /* begin isOldObject: */ + assert(isNonImmediate(obj2)); + if (oopisGreaterThanOrEqualTo(obj2, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(clone1 & (tagMask())))) + && (oopisLessThan(clone1, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(obj2 + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(obj2); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((obj2 + BaseHeaderSize) + (0U << (shiftForWord()))),clone1); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(obj2 + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(obj2 + (numSlotsFieldByteOffset())),1); + } + if ((((/* begin isYoungObject: */ + assert(isNonImmediate(obj1)), + oopisLessThan(obj1, GIV(oldSpaceStart)))) != ((/* begin isYoungObject: */ + assert(isNonImmediate(clone2)), + oopisLessThan(clone2, GIV(oldSpaceStart))))) + || (((/* begin isYoungObject: */ + assert(isNonImmediate(obj2)), + oopisLessThan(obj2, GIV(oldSpaceStart)))) != ((/* begin isYoungObject: */ + assert(isNonImmediate(clone1)), + oopisLessThan(clone1, GIV(oldSpaceStart)))))) { + GIV(becomeEffectsFlags) = GIV(becomeEffectsFlags) | OldBecameNewFlag; + } +} + + +/* Attempt to pin objOop, which must not be immediate. + If the attempt succeeds answer objOop's (possibly moved) oop. + If the attempt fails, which can only occur if there is no memory, answer + 0. */ + + /* SpurMemoryManager>>#pinObject: */ +sqInt +pinObject(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt oldClone; + sqInt referent; + SpurSegmentInfo *seg; + + assert(isNonImmediate(objOop)); + assert(!((isForwarded(objOop)))); + + /* We choose to clone to keep pinned objects together to reduce fragmentation, + if the object is not too large, assuming that pinning is rare and that fragmentation is a bad thing. + Too large is defined as over 1mb. The size of a 640x480x4 bitmap is 1228800. */ + + /* begin isOldObject: */ + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if ((numBytesOf(objOop)) > (0x100000)) { + setIsPinnedOfto(objOop, 1); + return objOop; + } + seg = segmentContainingObj(objOop); + if ((seg->containsPinned)) { + setIsPinnedOfto(objOop, 1); + return objOop; + } + + /* begin someSegmentContainsPinned */ + for (i = 0; i < GIV(numSegments); i += 1) { + if (((GIV(segments)[i]).containsPinned)) { + goto l1; + } + } + setIsPinnedOfto(objOop, 1); + (seg->containsPinned = 1); + return objOop; +l1:; + } + oldClone = cloneInOldSpaceforPinning(objOop, 1); + if (oldClone) { + GIV(becomeEffectsFlags) = becomeEffectFlagsFor(objOop); + setIsPinnedOfto(oldClone, 1); + + /* begin forward:to: */ + /* begin set:classIndexTo:formatTo: */ + assert((((isForwardedObjectClassIndexPun()) >= 0) && ((isForwardedObjectClassIndexPun()) <= (classIndexMask())))); + assert((((forwardedFormat()) >= 0) && ((forwardedFormat()) <= (formatMask())))); + longAtput((void *)(objOop),((longAt((void *)(objOop))) & (~(usqIntptr_t)(((((usqInt)((formatMask())) << (formatShift())))) + (classIndexMask())))) + ((isForwardedObjectClassIndexPun()) + ((((usqInt)((forwardedFormat())) << (formatShift())))))); +# if IMMUTABILITY + /* begin setIsImmutableOf:to: */ + longAtput((void *)(objOop),(longAt((void *)(objOop))) & (~(usqIntptr_t)(1U << (immutableBitShift())))); +# endif // IMMUTABILITY + + assert(isForwarded(objOop)); + assert(!(isOopForwarded(oldClone))); + + /* begin isOldObject: */ + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(oldClone & (tagMask())))) + && (oopisLessThan(oldClone, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),oldClone); + + /* For safety make sure the forwarder has a slot count that includes its contents. */ + if (!(byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) { + /* rawNumSlotsOf:put: */ + byteAtput((void *)(objOop + (numSlotsFieldByteOffset())),1); + } + + /* begin followSpecialObjectsOop */ + if ((!((longAt((void *)(GIV(specialObjectsOop)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + GIV(validatedIntegerClassFlags) = 0; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(GIV(specialObjectsOop))); + referent = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + GIV(specialObjectsOop) = referent; + } + followForwardedObjectFieldstoDepth(GIV(specialObjectsOop), 0); + + /* begin postBecomeAction: */ + spurPostBecomeAction(GIV(becomeEffectsFlags)); + if (((GIV(becomeEffectsFlags) & BecameJittedCompiledMethodFlag) != 0)) { + freeBecomeFlaggedMethods(); + } + followMovableLiteralsAndUpdateYoungReferrers(); + + /* begin nilUncoggableMethods */ + GIV(lastCoggableInterpretedBlockMethod) = (GIV(lastUncoggableInterpretedBlockMethod) = null); + assert(kosherYoungReferrers()); + GIV(gcMode) = 0; + postBecomeScanClassTable(GIV(becomeEffectsFlags)); + GIV(becomeEffectsFlags) = 0; + } + return oldClone; +} + + /* SpurMemoryManager>>#popObjStack: */ +static NoDbgRegParms sqInt +popObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt myx; + sqInt nextPage; + sqInt top; + sqInt topx; + + eassert(isValidObjStack(objStack)); + topx = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + if (!topx) { + assert((fetchPointerofObject(ObjStackNextx, objStack)) == 0); + + /* for debugging markAndTrace: set (MarkStackRecord := OrderedCollection new) */ + return null; + } + topx -= 1; + top = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)((topx + ObjStackFixedSlots)) << (shiftForWord())))))); + assert(addressCouldBeOop(top)); + + /* for debugging markAndTrace: set (MarkStackRecord := OrderedCollection new) */ + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(objStack)) == (wordIndexableFormat())); + longAtput((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))),topx); + if ((topx == 0) + && (((nextPage = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))) != 0)) { + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(nextPage)) == (wordIndexableFormat())); + longAtput((void *)((nextPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),objStack); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(objStack)) == (wordIndexableFormat())); + longAtput((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord()))))),0); + myx = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))); + + /* begin updateRootOfObjStackAt:with: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(myx, GIV(hiddenRootsObj), nextPage)); + assert(isNonImmediate(GIV(hiddenRootsObj))); + if (oopisGreaterThanOrEqualTo(GIV(hiddenRootsObj), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(nextPage & (tagMask())))) + && (oopisLessThan(nextPage, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(hiddenRootsObj) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(hiddenRootsObj)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(myx) << (shiftForWord()))))),nextPage); + switch (myx) { + case MarkStackRootIndex: + GIV(markStack) = nextPage; + break; + case WeaklingStackRootIndex: + GIV(weaklingStack) = nextPage; + break; + case MournQueueRootIndex: + GIV(mournQueue) = nextPage; + break; + default: + error("Case not found and no otherwise clause"); + } + eassert(isValidObjStack(nextPage)); + } + else { + eassert(isValidObjStack(objStack)); + } + return top; +} + + +/* Pop and return the possibly remapped object from the remap buffer. + We support this excessence for compatibility with ObjectMemory. + Spur doesn't GC during allocation. */ + + /* SpurMemoryManager>>#popRemappableOop */ +sqInt +popRemappableOop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = GIV(remapBuffer)[GIV(remapBufferCount)]; + GIV(remapBufferCount) -= 1; + return oop; +} + + +/* Scan the class table post-become (iff an active class object was becommed) + to ensure no + forwarding pointers, and no unhashed classes exist in the class table. + + Note that one-way become can cause duplications in the class table. + So if pointer objects have been becommed, scan all pages in the class + table and eliminate ay entries that have a zero hash. + doBecome:and:copyHash: has + already ensured that any becomed class has been stored at the right index. */ + + /* SpurMemoryManager>>#postBecomeScanClassTable: */ +static NoDbgRegParms void +postBecomeScanClassTable(sqInt effectsFlags) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOrNil; + sqInt i; + sqInt j; + usqInt numSlots; + sqInt page; + sqInt referent; + sqInt toDoLimit; + + assert(validClassTableRootPages()); + if (!(((effectsFlags & BecamePointerObjectFlag) != 0))) { + return; + } + for (i = 0; i < GIV(numClassTablePages); i += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + assert(!(isForwarded(page))); + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(page)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(page + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(page - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNil = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNil != GIV(nilObj)) { + if ((!((longAt((void *)(classOrNil))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(classOrNil)); + referent = longAt((void *)((classOrNil + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + classOrNil = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(j, page, classOrNil)); + assert(isNonImmediate(page)); + if (oopisGreaterThanOrEqualTo(page, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(classOrNil & (tagMask())))) + && (oopisLessThan(classOrNil, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(page + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(page); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord()))))),classOrNil); + } + if (!((long32At((void *)(classOrNil + 4))) & (identityHashHalfWordMask()))) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(page)) + && (!(isForwarded(page)))); + assert(validStorePointerUncheckedArgs(j, page, GIV(nilObj))); + longAtput((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord()))))),GIV(nilObj)); + + /* If the removed class is before the classTableIndex, set the + classTableIndex to point to the empty slot so as to reuse it asap. */ + if ((((((usqInt)(i) << (classTableMajorIndexShift())))) + j) < GIV(classTableIndex)) { + GIV(classTableIndex) = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + } + } + } + } + } + + /* classTableIndex must never index the first page, which is reserved for classes known to the VM. */ + assert(GIV(classTableIndex) >= (1U << (classTableMajorIndexShift()))); +} + + /* SpurMemoryManager>>#primitiveErrorTable */ +sqInt +primitiveErrorTable(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(PrimitiveErrorTableIndex) << (shiftForWord())))))); +} + + +/* Scan the heap printing the oops of any and all contexts that refer to + anOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printActivationsOf: */ +void +printActivationsOf(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (aMethodObj == (longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord()))))))))) { + printHex(objSqInt); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + print(" pc "); + printHex(longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))))); + cr(); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printBogons */ +void +printBogons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin printTheBogons: */ + print("bogon "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)GIV(bogon))); + cr(); + print("anomaly "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)GIV(anomaly))); + cr(); +} + + +/* Scan the heap printing the oops of any and all contexts that refer to + anOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printContextReferencesTo: */ +void +printContextReferencesTo(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt i; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + i = CtxtTempFrameStart + (fetchStackPointerOf(objSqInt)); + while (((i -= 1)) >= 0) { + if (anOop == (longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + printHex(objSqInt); + print(" @ "); + printNum(i); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + print(" pc "); + printHex(longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))))); + cr(); + i = 0; + } + } + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printEntity: */ +void +printEntity(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt n; + sqInt printFlags; + + printFlags = 0; + printHex(oop); + + /* begin space */ + printChar(' '); + if (!(addressCouldBeObj(oop))) { + print((((oop & (tagMask())) != 0) + ? "immediate" + : "unknown")); + return; + } + print((((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun()) + ? "free" + : (((longAt((void *)(oop))) & (classIndexMask())) == (segmentBridgePun()) + ? "bridge" + : ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? "forwarder" + : (((longAt((void *)(oop))) & (classIndexMask())) <= (lastClassIndexPun()) + ? ((printFlags = 1), + "pun/obj stack") + : ((printFlags = 1), + "object")))))); + + /* begin space */ + printChar(' '); + n = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + + /* begin printHexnpnp: */ + fprintf(GIV(transcript), + "%" PRIxSQINT "", + n); + print("/"); + n = bytesInBody(oop); + + /* begin printHexnpnp: */ + fprintf(GIV(transcript), + "%" PRIxSQINT "", + n); + print("/"); + printNum(bytesInBody(oop)); + if (printFlags) { + /* begin space */ + printChar(' '); + print((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 15 + ? "f:0" + : "f:")); + n = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin printHexnpnp: */ + fprintf(GIV(transcript), + "%" PRIxSQINT "", + n); + print(((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (greyBitByteShift())) + ? " g" + : " .")); + print(((((usqInt)((byteAt((void *)(oop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1 + ? "i" + : ".")); + print(((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (markedBitByteShift())) + ? "m" + : ".")); + print(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift())) + ? "p" + : ".")); + print(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())) + ? "r" + : ".")); + } + cr(); +} + + +/* Actually print all objects that have ephemeron format. */ + + /* SpurMemoryManager>>#printEphemerons */ +void +printEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt format; + sqInt nSoFar; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + nSoFar = 0; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + format = (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask()); + if (format == (ephemeronFormat())) { + printf("0x%" PRIxSQINT "", obj); + if (((nSoFar += 1)) >= 4) { + nSoFar = 0; + cr(); + } + else { + /* begin space */ + printChar(' '); + } + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + if (nSoFar > 0) { + cr(); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printForwarders */ +void +printForwarders(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == (isForwardedObjectClassIndexPun())) { + printHex(objOopSqInt); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOopSqInt, GIV(freeStart))) { + if (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == (isForwardedObjectClassIndexPun())) { + printHex(objOopSqInt); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOopSqInt = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3:; +} + + /* SpurMemoryManager>>#printForwarder:on: */ +static NoDbgRegParms void +printForwarderon(sqInt oop, FILE *aStream) +{ + fprintf(aStream, + "%p is a forwarded hdr%d slot size %" PRIuSQINT " object to %p\n", + ((void *)oop), + ((byteAt((void *)(oop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? 16 + : 8), + numSlotsOfAny(oop), + ((void *)(followForwarded(oop)))); +} + + +/* This version goes through memory, printing all free chunks. + Other versions go through the free lists. This one will show + all free chunks even if the free lists are corrupt. */ +/* useful for debugging */ + + /* SpurMemoryManager>>#printFreeChunks */ +void +printFreeChunks(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + sqInt seenNewFreeChunk; + usqInt start; + + seenNewFreeChunk = 0; + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l2; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + if (!seenNewFreeChunk) { + print("NewSpace CONTAINS FREE OBJECT(S)!!"); + cr(); + seenNewFreeChunk = 1; + } + printFreeChunk(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + /* end allNewSpaceEntitiesDo: */ +l2: + + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((longAt((void *)(objOop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + printFreeChunk(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l3:; + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printFreeChunk: */ +void +printFreeChunk(sqInt freeChunk) +{ + printFreeChunkprintAsTreeNode(freeChunk, 1); +} + + /* SpurMemoryManager>>#printFreeChunk:printAsTreeNode: */ +static NoDbgRegParms void +printFreeChunkprintAsTreeNode(sqInt freeChunk, sqInt printAsTreeNode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt n; + usqInt numBytes; + + numBytes = bytesInBody(freeChunk); + print("freeChunk "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)freeChunk)); + if (printAsTreeNode) { + print((freeChunk == (GIV(freeLists)[0]) + ? " + " + : " - ")); + n = addressAfter(freeChunk); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + } + print(" bytes "); + printNum(numBytes); + print(" next "); + n = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + if (!(isLilliputianSize(numBytes))) { + print(" prev "); + n = longAt((void *)((freeChunk + BaseHeaderSize) + (1U << (shiftForWord())))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + } + if ((numBytes >= 0x200 /* (numFreeLists * #allocationUnit) */) + && (printAsTreeNode)) { + print(" ^ "); + n = longAt((void *)((freeChunk + BaseHeaderSize) + (2U << (shiftForWord())))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + print(" < "); + n = longAt((void *)((freeChunk + BaseHeaderSize) + (3U << (shiftForWord())))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + print(" > "); + n = longAt((void *)((freeChunk + BaseHeaderSize) + (4U << (shiftForWord())))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + } + cr(); +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printFreeListHeads */ +void +printFreeListHeads(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt expectedMask; + sqInt i; + + expectedMask = 0; + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + printHex(GIV(freeLists)[i]); + if (GIV(freeLists)[i]) { + expectedMask += 1ULL << i; + } + if ((i + 1) % (4)) { + print(" "); + } + else { + cr(); + } + } + cr(); + print("mask: "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)GIV(freeListsMask))); + print(" expected: "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)expectedMask)); + cr(); +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printFreeList: */ +sqInt +printFreeList(sqInt chunkOrIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt freeChunk; + + if ((chunkOrIndex >= 0) + && (chunkOrIndex < 64 /* numFreeLists */)) { + return printFreeList(GIV(freeLists)[chunkOrIndex]); + } + freeChunk = chunkOrIndex; + while (freeChunk != 0) { + printFreeChunk(freeChunk); + freeChunk = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + } + return 0; +} + + /* SpurMemoryManager>>#printFreeObject:on: */ +static NoDbgRegParms void +printFreeObjecton(sqInt oop, FILE *aStream) +{ + fprintf(aStream, + "%p is a free chunk of size %" PRIdSQINT " 0th field:%p\n", + ((void *)oop), + bytesInBody(oop), + ((void *)(longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord()))))))); +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printFreeTree */ +void +printFreeTree(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + printFreeTreeChunk(GIV(freeLists)[0]); +} + + +/* Slang is blind-sided by the inlining of printFreeTreeChunk: into + printFreeTree. + */ + + /* SpurMemoryManager>>#printFreeTreeChunk: */ +static NoDbgRegParms void +printFreeTreeChunk(sqInt chunkOrZero) +{ + if (chunkOrZero > 0) { + printFreeTreeChunk(longAt((void *)((chunkOrZero + BaseHeaderSize) + (3U << (shiftForWord()))))); + printFreeChunk(chunkOrZero); + printFreeTreeChunk(longAt((void *)((chunkOrZero + BaseHeaderSize) + (4U << (shiftForWord()))))); + } +} + + +/* useful for VM debugging */ +/* N.B. No safety bounds checks!! We need to look e.g. at corpses. */ + + /* SpurMemoryManager>>#printHeaderOf: */ +void +printHeaderOf(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt n; + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)objOop)); + if ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) { + print(" hdr16 slotf "); + n = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + print(" slotc "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)(((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8))); + + /* begin space */ + printChar(' '); + } + else { + print(" hdr8 slots "); + n = numSlotsOfAny(objOop); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + } + printChar(' '); + printChar(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift())) + ? 'M' + : 'm')); + printChar(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (greyBitByteShift())) + ? 'G' + : 'g')); + printChar(((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift())) + ? 'P' + : 'p')); + printChar(((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())) + ? 'R' + : 'r')); + printChar(((((usqInt)((byteAt((void *)(objOop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1 + ? 'I' + : 'i')); + print(" hash "); + n = (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + print(" fmt "); + n = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + print(" cidx "); + n = (longAt((void *)(objOop))) & (classIndexMask()); + + /* begin printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)n)); + cr(); +} + + /* SpurMemoryManager>>#printImmediateObject:on: */ +static NoDbgRegParms void +printImmediateObjecton(sqInt oop, FILE *aStream) +{ + assert(isImmediate(oop)); + if ((((oop) & 7) == 1)) { + fprintf(aStream, + "%p=%" PRIdSQINT "\n", + ((void *)oop), + ((sqInt)((oop >> 3)))); + } + if (((oop & (characterTag())) != 0)) { + fprintf(aStream, + "%p=$%ld ($%lc)\n", + ((void *)oop), + ((long)(((((usqInt)oop))) >> (numTagBits()))), + ((wint_t) (((((usqInt)oop))) >> (numTagBits())))); + } + if (((oop & (smallFloatTag())) != 0)) { + fprintf(aStream, + "%p=%g\n", + ((void *)oop), + floatValueOf(oop)); + } +} + + +/* Scan the heap printing the oops of any and all objects that are instances + of aClassOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printInstancesOf: */ +void +printInstancesOf(sqInt aClassOop) +{ + int classIndex; + + classIndex = (long32At((void *)(aClassOop + 4))) & (identityHashHalfWordMask()); + if (classIndex != (isFreeObjectClassIndexPun())) { + printInstancesWithClassIndex(classIndex); + } +} + + +/* Scan the heap printing the oops of any and all objects whose classIndex + equals the argument. + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printInstancesWithClassIndex: */ +void +printInstancesWithClassIndex(sqInt classIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + printHex(objOop); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) == classIndex) { + printHex(objOop); + cr(); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3:; +} + + +/* Print the objects in the classTable that have bad hashes. */ +/* useful for debugging */ + + /* SpurMemoryManager>>#printInvalidClassTableEntries */ +void +printInvalidClassTableEntries(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOrNilSqInt; + int hash; + sqInt i; + sqInt index; + sqInt j; + sqInt page; + sqInt toDoLimit; + + hash = 0; + if (!(validClassTableRootPages())) { + print("class table invalid; cannot print"); + cr(); + return; + } + + /* begin classTableEntriesDo: */ + for (i = 0; i < GIV(numClassTablePages); i += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNilSqInt = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNilSqInt != GIV(nilObj)) { + index = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + if (((!((longAt((void *)(classOrNilSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + || ((((hash = (long32At((void *)(classOrNilSqInt + 4))) & (identityHashHalfWordMask()))) == 0) + || ((noCheckClassAtIndex(hash)) != classOrNilSqInt))) { + print("entry "); + printHex(index); + print(" oop "); + printHex(classOrNilSqInt); + print(" hash "); + printHex(hash); + print(" => "); + printHex(classAtIndex(hash)); + cr(); + } + } + } + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printMarkedOops */ +#if LLDB +void +printMarkedOops(void) +{ + printOopsSuchThat(isMarked); +} +#endif /* LLDB */ + + +/* Scan the heap printing the oops of any and all methods that implement + anOop + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printMethodImplementorsOf: */ +void +printMethodImplementorsOf(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && ((maybeSelectorOfMethod(objSqInt)) == anOop)) { + printHex(objSqInt); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + cr(); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + +/* Scan the heap printing the oops of any and all methods that refer to anOop */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printMethodReferencesTo: */ +void +printMethodReferencesTo(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt i; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + i = ((literalCountOf(objSqInt)) + LiteralStart) - 1; + while (((i -= 1)) >= 0) { + if (anOop == (longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + printHex(objSqInt); + print(" @ "); + printNum(i); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + cr(); + i = 0; + } + } + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + /* SpurMemoryManager>>#printMournQueue */ +void +printMournQueue(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt iSqInt; + sqInt mourner; + sqInt nSoFar; + sqInt numOnThisPage; + sqInt objStackPage; + + nSoFar = 0; + + /* begin objStack:do: */ + if (GIV(mournQueue) == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(GIV(mournQueue))); + objStackPage = GIV(mournQueue); + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + mourner = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + format = (((mourner & (tagMask())) != 0) + ? forwardedFormat() + : (byteAt((void *)(mourner + (formatFieldByteOffset())))) & (formatMask())); + fprintf(GIV(transcript), + "%p%s", + ((void *)mourner), + (format == (weakArrayFormat()) + ? " weak " + : (format == (ephemeronFormat()) + ? " eph?!" + : (format == (nonIndexablePointerFormat()) + ? " fired" + : " ???? ")))); + if (((nSoFar += 1)) >= 4) { + nSoFar = 0; + cr(); + } + else { + /* begin space */ + printChar(' '); + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l1: + if (nSoFar > 0) { + cr(); + } +} + + +/* useful for debugging */ + + /* SpurMemoryManager>>#printNewObjectsInObjStack: */ +void +printNewObjectsInObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt iSqInt; + sqInt n; + sqInt numOnThisPage; + sqInt obj; + sqInt objStackPage; + + n = 0; + + /* begin objStack:do: */ + if (objStack == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(objStack)); + objStackPage = objStack; + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + obj = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (/* isInNewSpace: */ + (oopisLessThan(obj, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(obj, GIV(newSpaceStart)))) { + /* begin space */ + printChar(' '); + printHex(obj); + if (!(((n += 1)) % 8)) { + cr(); + } + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l1: + if (n % 8) { + cr(); + } + fprintf(GIV(transcript), + "total %" PRIdSQINT "\n", + n); +} + + /* SpurMemoryManager>>#printNonPointerDataOf:on: */ +static NoDbgRegParms void +printNonPointerDataOfon(sqInt oop, FILE *aStream) +{ + sqInt elementsPerLine; + sqInt format; + sqInt index; + sqInt lastIndex; + usqInt numSlots; + usqInt numSlotsUsqInt; + + format = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + assert(((format >= (sixtyFourBitIndexableFormat())) && (format <= ((firstCompiledMethodFormat()) - 1)))); + + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (format <= (ephemeronFormat())) { + lastIndex = numSlots; + goto l1; + } + if (format >= (firstByteFormat())) { + lastIndex = ((numSlots << (shiftForWord()))) - (format & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (format >= (firstShortFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 1))) - (format & 3); + goto l1; + } + if (format >= (firstLongFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 2))) - (format & 1); + goto l1; + } + if (format == (sixtyFourBitIndexableFormat())) { + lastIndex = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + lastIndex = 0; + /* end lengthOf:format: */ +l1: + if (!lastIndex) { + return; + } + if (format == (sixtyFourBitIndexableFormat())) { + lastIndex = ((32 < lastIndex) ? 32 : lastIndex); + + /* 0x/16r0123456789ABCDEF x 4 = 76/80 */ + elementsPerLine = 4; + for (index = 1; index <= lastIndex; index += 1) { + fprintf(aStream, + "%18" PRIXSQINT "%c", + ((usqLong) (long64At((void *)((oop + BaseHeaderSize) + ((((usqInt)((index - 1)) << 3))))))), + (((index % elementsPerLine) == 0) + || (index == lastIndex) + ? '\n' + : ' ')); + } + return; + } + if (format < (firstShortFormat())) { + lastIndex = ((64 < lastIndex) ? 64 : lastIndex); + + /* 0x/16r12345678 x 8 = 80/88 */ + elementsPerLine = 8; + for (index = 1; index <= lastIndex; index += 1) { + fprintf(aStream, + "%10p%c", + ((void *)(((usqInt)(long32At((void *)((oop + BaseHeaderSize) + ((((usqInt)((index - 1)) << 2))))))))), + (((index % elementsPerLine) == 0) + || (index == lastIndex) + ? '\n' + : ' ')); + } + return; + } + if (format < (firstByteFormat())) { + lastIndex = ((128 < lastIndex) ? 128 : lastIndex); + + /* 0x/16r1234 x 10 = 70/80 */ + elementsPerLine = 10; + for (index = 1; index <= lastIndex; index += 1) { + fprintf(aStream, + "%6p%c", + ((void *)(shortAt((void *)((oop + BaseHeaderSize) + ((((usqInt)((index - 1)) << 1))))))), + (((index % elementsPerLine) == 0) + || (index == lastIndex) + ? '\n' + : ' ')); + } + return; + } + lastIndex = ((0x100 < lastIndex) ? 0x100 : lastIndex); + + /* 0x/16r12 x 16 = 80/96 */ + elementsPerLine = 16; + for (index = 1; index <= lastIndex; index += 1) { + fprintf(aStream, + "%4p%c", + ((void *)(byteAt((void *)((oop + BaseHeaderSize) + (index - 1))))), + (((index % elementsPerLine) == 0) + || (index == lastIndex) + ? '\n' + : ' ')); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printObjectsFrom:to: */ +void +printObjectsFromto(sqInt startAddress, sqInt endAddress) +{ + sqInt oop; + + oop = objectBefore(startAddress); + oop = (oop + ? ((objectAfter(oop)) == startAddress + ? startAddress + : oop) + : startAddress); + while (oopisLessThan(oop, endAddress)) { + if (!((((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) + || (((longAt((void *)(oop))) & (classIndexMask())) == (segmentBridgePun())))) { + printOop(oop); + } + oop = objectAfter(oop); + } +} + + +/* Scan the heap printing the oops of any and all objects whose hash equals + the argument. + */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printObjectsWithHash: */ +void +printObjectsWithHash(sqInt hash) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (((long32At((void *)(objOop + 4))) & (identityHashHalfWordMask())) == hash) { + shortPrintOop(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (((long32At((void *)(objOop + 4))) & (identityHashHalfWordMask())) == hash) { + shortPrintOop(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3:; +} + + /* SpurMemoryManager>>#printObjStackAndContents: */ +void +printObjStackAndContents(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt total; + + /* begin printObjStack:printContents: */ + if (objStack == GIV(nilObj)) { + print("nil"); + cr(); + } + else { + total = printObjStackPagemyIndexpageTypeprintContents(objStack, longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))), ObjStackMyx, 1); + fprintf(GIV(transcript), + "total %" PRIdSQINT "\n", + total); + } +} + + +/* Print the page and all pages accessible from it. Answer the total number + of objects on all non free pages. + */ + + /* SpurMemoryManager>>#printObjStackPage:myIndex:pageType:printContents: */ +static NoDbgRegParms sqInt +printObjStackPagemyIndexpageTypeprintContents(sqInt objStackPage, sqInt myx, sqInt pageType, sqInt printContents) +{ + sqInt freeOrNextPage; + sqInt index; + int isFirstPage; + int isFreePage; + int isNextPage; + sqInt n; + sqInt page; + sqInt totalContents; + + isFirstPage = pageType == ObjStackMyx; + isNextPage = pageType == ObjStackNextx; + isFreePage = pageType == ObjStackFreex; + printObjStackPagemyIndextag(objStackPage, myx, (isFirstPage + ? "head" + : (isFreePage + ? "free" + : "next"))); + if (isFirstPage + || (isNextPage)) { + /* begin tab */ + printChar('\t'); + print("topx: "); + printNum(longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))); + print(" next: "); + printHex(longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord()))))))); + if (isFirstPage) { + print(" free: "); + printHex(longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))))); + } + cr(); + } + if (isFirstPage) { + freeOrNextPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + while (freeOrNextPage != 0) { + printObjStackPagemyIndexpageTypeprintContents(freeOrNextPage, myx, ObjStackFreex, 0); + page = longAt((void *)((freeOrNextPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + if ((page == freeOrNextPage) + || (page == objStackPage)) { + print("circularity in free page list!!"); + cr(); + page = 0; + } + freeOrNextPage = page; + } + } + freeOrNextPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + totalContents = (freeOrNextPage + ? printObjStackPagemyIndexpageTypeprintContents(freeOrNextPage, myx, ObjStackNextx, printContents) + : 0); + if (printContents) { + n = 0; + index = (longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) + ObjStackNextx; + while (index >= ObjStackFixedSlots) { + /* begin space */ + printChar(' '); + printHex(longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))))); + index -= 1; + if (((n += 1)) >= 8) { + cr(); + n = 0; + } + } + if (((longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) + ObjStackNextx) >= ObjStackFixedSlots) { + cr(); + } + } + return (isFreePage + ? 0 + : totalContents + (longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))))); +} + + /* SpurMemoryManager>>#printObjStackPage:myIndex:tag: */ +static NoDbgRegParms void +printObjStackPagemyIndextag(sqInt objStackPage, sqInt myx, char *pageType) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fprintf(GIV(transcript), + "%s %p cx %d (%d) fmt %d (%d) sz %d (%d) myx: %d (%d%s\n", + pageType, + ((void *)objStackPage), + ((int) ((longAt((void *)(objStackPage))) & (classIndexMask()))), + ((int) (sixtyFourBitLongsClassIndexPun())), + ((int) ((byteAt((void *)(objStackPage + (formatFieldByteOffset())))) & (formatMask()))), + ((int) (sixtyFourBitIndexableFormat())), + ((int) (numSlotsOfAny(objStackPage))), + ObjStackPageSlots, + ((int) (longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))))), + ((int) myx), + ((byteAt((void *)(objStackPage + (markBitsByteOffset())))) & (1U << (markedBitByteShift())) + ? ") mkd" + : ") unmkd")); +} + + +/* useful for debugging */ + + /* SpurMemoryManager>>#printObjStack: */ +void +printObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt total; + + /* begin printObjStack:printContents: */ + if (objStack == GIV(nilObj)) { + print("nil"); + cr(); + } + else { + total = printObjStackPagemyIndexpageTypeprintContents(objStack, longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackMyx) << (shiftForWord())))))), ObjStackMyx, 0); + fprintf(GIV(transcript), + "total %" PRIdSQINT "\n", + total); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printOopsExcept: */ +NeverInline void +printOopsExcept(sqInt (*function)(sqInt)) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt n; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + n = 0; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (!(function(objOop))) { + n += 1; + printEntity(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (!(function(objOop))) { + n += 1; + printEntity(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3: + if (n > 4) { + printNum(n); + print(" objects"); + cr(); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printOopsFrom:to: */ +void +printOopsFromto(sqInt startAddress, sqInt endAddress) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firstNonEntity; + sqInt inEmptySpace; + sqInt lastNonEntity; + sqInt limit; + sqInt oop; + + firstNonEntity = 0; + oop = objectBefore(startAddress); + limit = (((((usqIntptr_t)endAddress)) < GIV(endOfMemory)) ? (((usqIntptr_t)endAddress)) : GIV(endOfMemory)); + oop = (oop + ? ((objectAfter(oop)) == startAddress + ? startAddress + : oop) + : startAddress); + inEmptySpace = 0; + while (oopisLessThan(oop, limit)) { + printEntity(oop); + while (1) { + oop = objectAfter(oop); + if (!((long64At((void *)(oop))) == 0)) break; + if (!inEmptySpace) { + inEmptySpace = 1; + firstNonEntity = oop; + } + lastNonEntity = oop; + } + if (inEmptySpace) { + inEmptySpace = 0; + print("skipped empty space from "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)firstNonEntity)); + print(" to "); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)lastNonEntity)); + cr(); + oop = objectStartingAt(oop); + } + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printOopsSuchThat: */ +NeverInline void +printOopsSuchThat(sqInt (*function)(sqInt)) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt n; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + n = 0; + + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if (function(objOop)) { + n += 1; + printEntity(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if (function(objOop)) { + n += 1; + printEntity(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3: + if (n > 4) { + printNum(n); + print(" objects"); + cr(); + } +} + + +/* Scan the heap printing the oops of any and all objects that refer to anOop */ +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printReferencesTo: */ +void +printReferencesTo(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt contextSize; + sqInt fmt; + sqInt followingWord; + usqInt followingWordAddress; + sqInt header; + sqInt headerSqInt; + sqInt i; + usqInt numLiterals; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt sp; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l2; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objSqInt))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l2: + i = ((usqInt) (CtxtTempFrameStart + contextSize)); + goto l3; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objSqInt)) > (isForwardedObjectClassIndexPun())); + i = (((numSlots = byteAt((void *)(objSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l3; + } + if (fmt == (forwardedFormat())) { + i = 1; + goto l3; + } + if (fmt < (firstCompiledMethodFormat())) { + i = 0; + goto l3; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objSqInt)); + headerSqInt = longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + i = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l3: + while (((i -= 1)) >= 0) { + if (anOop == (longAt((void *)((objSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + printHex(objSqInt); + print(" @ "); + printNum(i); + + /* begin space */ + printChar(' '); + printOopShortInner(objSqInt); + cr(); + i = 0; + } + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } + if (isonObjStack(anOop, GIV(markStack))) { + print("on markStack"); + cr(); + } + if (isonObjStack(anOop, GIV(weaklingStack))) { + print("on weaklingStack"); + cr(); + } + if (isonObjStack(anOop, GIV(mournQueue))) { + print("in mournQueue"); + cr(); + } + if ((((byteAt((void *)(anOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift()))) != 0) + && (isInRememberedSet(anOop))) { + print("in rememberedSet"); + cr(); + } +} + + /* SpurMemoryManager>>#printStringDataOf:on: */ +static NoDbgRegParms void +printStringDataOfon(sqInt oop, FILE *aStream) +{ + char *buffer; + sqInt byte; + sqInt i; + sqInt limit; + sqInt n; + unsigned int *wideBuffer; + int word; + + if (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())) { + buffer = ((char *) (alloca(0x400))); + n = (i = 0); + limit = (((numBytesOfBytes(oop)) < 0x100) ? (numBytesOfBytes(oop)) : 0x100); + while (n < limit) { + byte = byteAt((void *)((oop + BaseHeaderSize) + i)); + i += 1; + if ((byte < 32) + && (byte != 9)) { + buffer[n] = '<'; + n += 1; + if ((byte == 10) + || (byte == 13)) { + if (byte == 10) { + buffer[n] = 'L'; + buffer[n + 1] = 'F'; + } + else { + buffer[n] = 'C'; + buffer[n + 1] = 'R'; + } + n += 2; + } + else { + if (byte >= 10) { + buffer[n] = ((byte / 10) + (((sqInt)'0'))); + n += 1; + } + buffer[n] = ((byte % 10) + (((sqInt)'0'))); + n += 1; + } + buffer[n] = '>'; + n += 1; + } + else { + buffer[n] = byte; + n += 1; + } + } + fprintf(aStream, + "%.*s%s\n", + ((int) n), + buffer, + ((numBytesOfBytes(oop)) > limit + ? "..." + : "")); + } + else { + assert(isWordsNonImm(oop)); + wideBuffer = ((unsigned int *) (alloca(0x1000))); + n = (i = 0); + limit = (((lengthOf(oop)) < 0x100) ? (lengthOf(oop)) : 0x100); + while (i < limit) { + word = long32At((void *)((oop + BaseHeaderSize) + ((((usqInt)(i) << 2))))); + i += 1; + if ((word < 32) + && (word != 9)) { + wideBuffer[n] = '<'; + n += 1; + if ((word == 10) + || (word == 13)) { + if (word == 10) { + wideBuffer[n] = 'L'; + wideBuffer[n + 1] = 'F'; + } + else { + wideBuffer[n] = 'C'; + wideBuffer[n + 1] = 'R'; + } + n += 2; + } + else { + if (word >= 10) { + wideBuffer[n] = ((word / 10) + (((sqInt)'0'))); + n += 1; + } + wideBuffer[n] = ((word % 10) + (((sqInt)'0'))); + n += 1; + } + wideBuffer[n] = '>'; + n += 1; + } + else { + wideBuffer[n] = word; + n += 1; + } + } + fwprintf(aStream, + L"%.*ls%s\n", + ((int) n), + wideBuffer, + ((lengthOf(oop)) > limit + ? "..." + : "")); + } +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#printUnmarkedOops */ +#if LLDB +void +printUnmarkedOops(void) +{ + printOopsExcept(isMarked); +} +#endif /* LLDB */ + + +/* Print all objects that have weak array format. */ + + /* SpurMemoryManager>>#printWeaklings */ +void +printWeaklings(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt format; + sqInt nSoFar; + usqInt numSlots; + sqInt obj; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + nSoFar = 0; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + format = (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask()); + if (format == (weakArrayFormat())) { + printf("0x%" PRIxSQINT "", obj); + if (((nSoFar += 1)) >= 4) { + nSoFar = 0; + cr(); + } + else { + /* begin space */ + printChar(' '); + } + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + if (nSoFar > 0) { + cr(); + } +} + + +/* Attempt to push anEphemeron on the unscanned ephemerons stack and answer + if the attempt succeeded. Note that the ephemeron stack overflowing isn't + a disaster; it simply means treating the ephemeron as strong in this GC + cycle. */ + + /* SpurMemoryManager>>#pushOnUnscannedEphemeronsStack: */ +static NoDbgRegParms sqInt +pushOnUnscannedEphemeronsStack(sqInt anEphemeron) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(isEphemeron(anEphemeron)); + if (((GIV(unscannedEphemerons).top)) >= ((GIV(unscannedEphemerons).limit))) { + return 0; + } + longAtput((void *)((GIV(unscannedEphemerons).top = ((GIV(unscannedEphemerons).top)) + BytesPerOop)),anEphemeron); + return 1; +} + + +/* Record the given object in a the remap buffer. Objects in this buffer are + remapped when a compaction occurs. This facility is used by the + interpreter to ensure that + objects in temporary variables are properly remapped. + We support this excessence for compatibility with ObjectMemory. + Spur doesn't GC during allocation. */ + + /* SpurMemoryManager>>#pushRemappableOop: */ +void +pushRemappableOop(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(addressCouldBeOop(oop)); + GIV(remapBuffer)[(GIV(remapBufferCount) += 1)] = oop; + if (!(GIV(remapBufferCount) <= RemapBufferSize)) { + error("remapBuffer overflow"); + } +} + + +/* Add the ephemeron or weak array to the queue. */ +/* If an ephemeron it shouldn't be added twice. */ + + /* SpurMemoryManager>>#queueMourner: */ +static NoDbgRegParms void +queueMourner(sqInt anEphemeronOrWeakArray) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt format; + sqInt objStack; + + assert(!((((formatOf(anEphemeronOrWeakArray)) != (weakArrayFormat())) + && (isonObjStack(anEphemeronOrWeakArray, GIV(mournQueue)))))); + + /* it should be a weak array or an ephemeron */ + assert((isNonImmediate(anEphemeronOrWeakArray)) + && (((formatOf(anEphemeronOrWeakArray)) == (ephemeronFormat())) + || ((formatOf(anEphemeronOrWeakArray)) == (weakArrayFormat())))); + ensureRoomOnObjStackAt(MournQueueRootIndex); + + /* There is no point queueing weak arrays more than once. Note that it should be impossible + for ephemerons to be enqueued more than once since they are turned into non-ephemerons + in the relevant sender. */ + + /* begin isEphemeron: */ + assert(isNonImmediate(anEphemeronOrWeakArray)); + format = (byteAt((void *)(anEphemeronOrWeakArray + (formatFieldByteOffset())))) & (formatMask()); + if (!(format == (ephemeronFormat()))) { + if (isonObjStack(anEphemeronOrWeakArray, GIV(mournQueue))) { + return; + } + } + objStack = GIV(mournQueue); + + /* begin push:onObjStack: */ + assert(addressCouldBeOop(anEphemeronOrWeakArray)); + if (((anEphemeronOrWeakArray & (tagMask())) != 0)) { + assert(objStack == GIV(markStack)); + assert(addressCouldBeObj(topOfObjStack((0 == (fetchPointerofObject(ObjStackTopx, objStack)) + ? fetchPointerofObject(ObjStackNextx, objStack) + : objStack)))); + } + else { + assert(!((objStack == GIV(markStack)) + && (isWeakNonImm(anEphemeronOrWeakArray)))); + + /* There should only be weaklings on the weaklingStack */ + assert((objStack != GIV(weaklingStack)) + || (isWeakNonImm(anEphemeronOrWeakArray))); + } + + /* There should be no weaklings on the mark stack. */ + noCheckPushonObjStack(anEphemeronOrWeakArray, objStack); +} + + /* SpurMemoryManager>>#rawHashBitsOf: */ +int +rawHashBitsOf(sqInt objOop) +{ + return (long32At((void *)(objOop + 4))) & (identityHashHalfWordMask()); +} + + +/* Answer the tag bits for the receiver based on the method's methodClass, if + any. These bits are extended with a bit that says that the method may have + a Context receiver, i.e. + mclass is Context or its superclasses. The absence of this bit is used to + avoid expensive store checks, etc. */ + + /* SpurMemoryManager>>#receiverTagBitsForMethod: */ +sqInt +receiverTagBitsForMethod(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classContextOrSuperclass; + sqInt literal; + sqInt methodClassOrNil; + sqInt objOop; + sqInt offset; + + /* begin methodClassOf: */ + offset = (literalCountOf(aMethodObj)) - 1; + literal = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = fixFollowedFieldofObjectwithInitialValue(offset + LiteralStart, aMethodObj, literal); + } + methodClassOrNil = ((literal != GIV(nilObj)) + && (/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + ? (assert((numSlotsOf(literal)) > ValueIndex), + followFieldofObject(ValueIndex, literal)) + : GIV(nilObj)); + if (methodClassOrNil == GIV(nilObj)) { + return (1U << (numTagBits())); + } + + /* If we don't know the methodClass be pessimal + Mixins don't necessarily have a format inst var; filter out non-integer format. */ + if (((((usqInt)((((longAt((void *)((methodClassOrNil + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)))) >> (fixedFieldsFieldWidth())) & (formatMask())) == (forwardedFormat())) { + /* begin receiverTagBitsForImmediateMethodClass: */ + if (methodClassOrNil == (longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)((smallIntegerTag())) << (shiftForWord())))))))) { + return smallIntegerTag(); + } + if (methodClassOrNil == (longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)((characterTag())) << (shiftForWord())))))))) { + return characterTag(); + } + assert(methodClassOrNil == (fetchPointerofObject(smallFloatTag(), GIV(classTableFirstPage)))); + return smallFloatTag(); + } + + /* Now check if mclass is one of Context's superclasses... */ + + /* begin knownClassAtIndex: */ + assert(((ClassMethodContextCompactIndex >= 1) && (ClassMethodContextCompactIndex <= (classTablePageSize())))); + classContextOrSuperclass = longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(ClassMethodContextCompactIndex) << (shiftForWord())))))); + do { + if (classContextOrSuperclass == methodClassOrNil) { + return (1U << (numTagBits())); + } + + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classContextOrSuperclass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classContextOrSuperclass, objOop); + } + classContextOrSuperclass = objOop; + } while(classContextOrSuperclass != GIV(nilObj)); + return 0; +} + + +/* Ephemerons in the mourn queue will have been fired (had their + ephemeron-ness turned off), + but the ephemeron queue is not saved in the snapshot. So the snapshotted + ephemerons need to be unfired so they're still unqueued ephemerons in the + loaded image. + This must be undone post snapshot. */ + + /* SpurMemoryManager>>#refireQueuedEphemeronsPostSnapshot */ +static void +refireQueuedEphemeronsPostSnapshot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt iSqInt; + sqInt mourner; + sqInt numOnThisPage; + sqInt objStackPage; + + /* begin objStack:do: */ + if (GIV(mournQueue) == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(GIV(mournQueue))); + objStackPage = GIV(mournQueue); + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + mourner = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (((!(mourner & (tagMask())))) + && (((byteAt((void *)(mourner + (formatFieldByteOffset())))) & (formatMask())) == (ephemeronFormat()))) { + /* begin setFormatOf:to: */ + assert((((nonIndexablePointerFormat()) >= 0) && ((nonIndexablePointerFormat()) <= (formatMask())))); + byteAtput((void *)(mourner + (formatFieldByteOffset())),((byteAt((void *)(mourner + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (nonIndexablePointerFormat())); + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l1:; +} + + +/* Relocate all objStack pages that comprise objStack, including contents if + requested. + */ + + /* SpurMemoryManager>>#relocateObjStackForPlanningCompactor:andContents: */ +static NoDbgRegParms sqInt +relocateObjStackForPlanningCompactorandContents(sqInt objStack, sqInt relocateContents) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt finishIndex; + sqInt freeList; + sqInt fwd; + sqInt i; + sqInt next; + sqInt oop; + sqInt relocated; + sqInt result; + sqInt stackOrNil; + + next = 0; + if (objStack == GIV(nilObj)) { + return objStack; + } + assert((relocateContents + ? (rawHashBitsOf(objStack)) < ObjStackLimit + : (rawHashBitsOf(objStack)) == 0)); + stackOrNil = objStack; + freeList = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + while (1) { + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + + /* There are four fixed slots in an obj stack, and a Topx of 0 indicates empty, so + if there were 5 slots in an oop stack, full would be 2, and the last 0-rel index is 4. + Hence the last index is topx + fixed slots - 1, or topx + ObjStackNextx. The first + two slots, Topx and Myx are not object references. */ + next = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + finishIndex = ObjStackNextx + ((relocateContents + ? (long32At((void *)(stackOrNil + 4))) & (identityHashHalfWordMask()) + : 0)); + + /* begin relocateObjectsInHeapEntity:from:to: */ + for (i = ObjStackFreex; i <= finishIndex; i += 1) { + oop = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert(isMarked(oop)); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stackOrNil)) + && (!(isForwarded(stackOrNil)))); + assert(validStorePointerUncheckedArgs(i, stackOrNil, fwd)); + longAtput((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + relocated = (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(stackOrNil, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(stackOrNil + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)) + ? longAt((void *)((stackOrNil + BaseHeaderSize) + (0U << (shiftForWord())))) + : stackOrNil); + if (stackOrNil == objStack) { + result = relocated; + } + if (!(next != 0)) break; + stackOrNil = next; + } + while (freeList != 0) { + assert((numSlotsOfAny(freeList)) == ObjStackPageSlots); + next = longAt((void *)((freeList + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))); + + /* begin relocateObjectsInHeapEntity:from:to: */ + for (i = ObjStackFreex; i <= ObjStackFreex; i += 1) { + oop = longAt((void *)((freeList + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert(isMarked(oop)); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(freeList)) + && (!(isForwarded(freeList)))); + assert(validStorePointerUncheckedArgs(i, freeList, fwd)); + longAtput((void *)((freeList + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + if (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(freeList, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(freeList + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0))) { + /* fetchPointer:ofObject: */ + longAt((void *)((freeList + BaseHeaderSize) + (0U << (shiftForWord())))); + } + else { + } + freeList = next; + } + return relocated; +} + + +/* Remove the given variable location to the extra roots table. */ + + /* SpurMemoryManager>>#removeGCRoot: */ +sqInt +removeGCRoot(sqInt *varLoc) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int i; + + for (i = 1; i <= GIV(extraRootCount); i++) { + if (varLoc == (GIV(extraRoots)[i])) { + GIV(extraRoots)[i] = (GIV(extraRoots)[GIV(extraRootCount)]); + GIV(extraRootCount) -= 1; + return 1; + } + } + return 0; +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. */ + + /* SpurMemoryManager>>#return:restoringObjectsIn:savedFirstFields:and:savedHashes: */ +static NoDbgRegParms sqInt +returnrestoringObjectsInsavedFirstFieldsandsavedHashes(sqInt errCode, sqInt reachableObjectsArray, sqInt savedFirstFields, sqInt outPointersArray, sqInt savedHashes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int hash; + sqInt i; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt toDoLimit; + sqInt valuePointer; + + /* begin restoreObjectsIn:upTo:savedFirstFields: */ + /* begin numSlotsOf: */ + assert((classIndexOf(reachableObjectsArray)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(reachableObjectsArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(reachableObjectsArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + for (i = 0; i < numSlots; i += 1) { + oop = longAt((void *)((reachableObjectsArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + valuePointer = longAt((void *)((savedFirstFields + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(oop)) + && (!(isForwarded(oop)))); + assert(validStorePointerUncheckedArgs(0, oop, valuePointer)); + longAtput((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + if (oopisGreaterThanOrEqualToandLessThan(savedFirstFields, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedFirstFields); + } + + /* begin restoreObjectsIn:savedHashes: */ + toDoLimit = ((assert((classIndexOf(outPointersArray)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(outPointersArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointersArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + if (((hash = long32At((void *)((savedHashes + BaseHeaderSize) + ((((usqInt)(i) << 2))))))) > (identityHashHalfWordMask())) { + if (oopisGreaterThanOrEqualToandLessThan(savedHashes, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedHashes); + } + goto l1; + } + oop = longAt((void *)((outPointersArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(oop + 4),((((long32At((void *)(oop + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + (hash & (identityHashHalfWordMask()))); + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + if (oopisGreaterThanOrEqualToandLessThan(savedHashes, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedHashes); + } + /* end restoreObjectsIn:savedHashes: */ +l1: + freeObject(reachableObjectsArray); + runLeakCheckerFor(GCModeImageSegment); + assert(allObjectsUnmarked()); + return errCode; +} + + +/* This is part of storeImageSegmentInto:outPointers:roots:. */ + + /* SpurMemoryManager>>#return:restoringObjectsIn:upTo:savedFirstFields: */ +static NoDbgRegParms sqInt +returnrestoringObjectsInupTosavedFirstFields(sqInt errCode, sqInt reachableObjectsArray, sqInt limitOrTag, sqInt savedFirstFields) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt valuePointer; + + /* begin restoreObjectsIn:upTo:savedFirstFields: */ + numSlots = (limitOrTag == -1 + ? (/* begin numSlotsOf: */ + assert((classIndexOf(reachableObjectsArray)) > (isForwardedObjectClassIndexPun())), + (((numSlotsUsqInt = byteAt((void *)(reachableObjectsArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(reachableObjectsArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt)) + : limitOrTag); + for (i = 0; i < numSlots; i += 1) { + oop = longAt((void *)((reachableObjectsArray + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + valuePointer = longAt((void *)((savedFirstFields + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(oop)) + && (!(isForwarded(oop)))); + assert(validStorePointerUncheckedArgs(0, oop, valuePointer)); + longAtput((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(oop))); + byteAtput((void *)(oop + (markBitsByteOffset())),(byteAt((void *)(oop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + if (oopisGreaterThanOrEqualToandLessThan(savedFirstFields, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedFirstFields); + } + freeObject(reachableObjectsArray); + runLeakCheckerFor(GCModeImageSegment); + assert(allObjectsUnmarked()); + return errCode; +} + + +/* This exists to get around a compiler bug in Apple LLVM version 7.0.0 + (clang-700.1.76) that was avoiding the second comparison of segVersion + after the first byte swap. */ + + /* SpurMemoryManager>>#reverseBytesIn32BitWordsIn: */ +static NoDbgRegParms NeverInline void +reverseBytesIn32BitWordsIn(sqInt segmentWordArray) +{ + sqInt addr; + sqInt stopAddr; + + stopAddr = addressAfter(segmentWordArray); + + /* begin reverseBytesIn32BitWordsFrom:to: */ + addr = segmentWordArray + BaseHeaderSize; + while (oopisLessThan(addr, stopAddr)) { + long32Atput((void *)(addr),SQ_SWAP_4_BYTES((long32At((void *)(addr))))); + addr += 4; + } +} + + +/* Check free space integrity by setting bits in the map corresponding to all + free space objects + and checking tat no pointer field refers to a free object. anOopOrNil is + provided to filter-out + the as-yet-to-be initialized object in + primitiveShallowCopy/primitiveClone. + */ + + /* SpurMemoryManager>>#runLeakCheckerForFreeSpace:ignoring: */ +static NoDbgRegParms void +runLeakCheckerForFreeSpaceignoring(sqInt gcModes, sqInt anOopOrNil) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (((gcModes & GCCheckFreeSpace) != 0)) { + reverseDisplayFromto(16, 19); + clearLeakMapAndMapAccessibleFreeSpace(); + GIV(freeSpaceCheckOopToIgnore) = anOopOrNil; + asserta(checkHeapFreeSpaceIntegrity()); + GIV(freeSpaceCheckOopToIgnore) = null; + } +} + + /* SpurMemoryManager>>#runLeakCheckerFor: */ +static NoDbgRegParms void +runLeakCheckerFor(sqInt gcModes) +{ + /* begin inLineRunLeakCheckerFor:excludeUnmarkedObjs:classIndicesShouldBeValid: */ + if (((gcModes & checkForLeaks) != 0)) { + if (((gcModes & GCModeFull) != 0)) { + reverseDisplayFromto(0, 7); + } + else { + reverseDisplayFromto(8, 15); + } + clearLeakMapAndMapAccessibleObjects(); + asserta(checkHeapIntegrityclassIndicesShouldBeValid(0, 1)); + asserta((checkInterpreterIntegrity()) == 0); + asserta(checkStackIntegrity()); + asserta(checkCodeIntegrity(gcModes)); + if (((gcModes & GCCheckFreeSpace) != 0)) { + clearLeakMapAndMapAccessibleFreeSpace(); + asserta(checkHeapFreeSpaceIntegrity()); + } + } + return; +} + + /* SpurMemoryManager>>#runLeakCheckerFor:excludeUnmarkedObjs:classIndicesShouldBeValid: */ +static NoDbgRegParms void +runLeakCheckerForexcludeUnmarkedObjsclassIndicesShouldBeValid(sqInt gcModes, sqInt excludeUnmarkedObjs, sqInt classIndicesShouldBeValid) +{ + /* begin inLineRunLeakCheckerFor:excludeUnmarkedObjs:classIndicesShouldBeValid: */ + if (((gcModes & checkForLeaks) != 0)) { + if (((gcModes & GCModeFull) != 0)) { + reverseDisplayFromto(0, 7); + } + else { + reverseDisplayFromto(8, 15); + } + clearLeakMapAndMapAccessibleObjects(); + asserta(checkHeapIntegrityclassIndicesShouldBeValid(excludeUnmarkedObjs, classIndicesShouldBeValid)); + asserta((checkInterpreterIntegrity()) == 0); + asserta(checkStackIntegrity()); + asserta(checkCodeIntegrity(gcModes)); + if (((gcModes & GCCheckFreeSpace) != 0)) { + clearLeakMapAndMapAccessibleFreeSpace(); + asserta(checkHeapFreeSpaceIntegrity()); + } + } +} + + /* SpurMemoryManager>>#safePrintStringOf: */ +static NoDbgRegParms void +safePrintStringOf(sqInt oop) +{ + printStringOf(/* followMaybeForwarded: */ + (/* isOopForwarded: */ + ((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + ? followForwarded(oop) + : oop)); + return; +} + + +/* Run the scavenger. */ + + /* SpurMemoryManager>>#scavengingGCTenuringIf: */ +static NoDbgRegParms void +scavengingGCTenuringIf(sqInt tenuringCriterion) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt n; + sqInt probe; + + assert(GIV(remapBufferCount) == 0); + if (!(asserta(((((GIV(eden)).limit)) - GIV(freeStart)) > (interpreterAllocationReserveBytes())))) { + /* begin tab */ + printChar('\t'); + n = (((GIV(eden)).limit)) - GIV(freeStart); + + /* begin printNum: */ + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)n)); + printChar(' '); + n = interpreterAllocationReserveBytes(); + + /* begin printNum: */ + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)n)); + printChar(' '); + n = (interpreterAllocationReserveBytes()) - ((((GIV(eden)).limit)) - GIV(freeStart)); + + /* begin printNum: */ + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)n)); + cr(); + } + + /* begin checkMemoryMap */ + assert(isYoungObject(GIV(newSpaceStart))); + assert(!(isOldObject(GIV(newSpaceStart)))); + assert(isYoungObject(GIV(oldSpaceStart) - BytesPerWord)); + assert(!(isOldObject(GIV(oldSpaceStart) - BytesPerWord))); + assert(!(isYoungObject(GIV(oldSpaceStart)))); + assert(isOldObject(GIV(oldSpaceStart))); + assert(!(isYoungObject(GIV(endOfMemory)))); + assert(isOldObject(GIV(endOfMemory))); + + /* we would like the following to be true, but we either choose one boundary check for + cogMethods vs objects (isMachineCodeFrame: et al) or one boundary check for + copyAndForward:. We can't have both, and the former is likely the highest dynamic + frequency. */ + assert(!(isOldObject(minCogMethodAddress()))); + assert(!(isOldObject(maxCogMethodAddress()))); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + runLeakCheckerFor(GCModeNewSpace); + preGCAction(GCModeNewSpace); + + /* begin flushMethodCacheFrom:to: */ + probe = 0; + for (i = 1; i <= MethodCacheEntries; i += 1) { + if (GIV(methodCache)[probe + MethodCacheSelector]) { + if (((oopisGreaterThanOrEqualTo(GIV(methodCache)[probe + MethodCacheSelector], GIV(newSpaceStart))) + && (oopisLessThan(GIV(methodCache)[probe + MethodCacheSelector], GIV(oldSpaceStart)))) + || ((oopisGreaterThanOrEqualTo(GIV(methodCache)[probe + MethodCacheMethod], GIV(newSpaceStart))) + && (oopisLessThan(GIV(methodCache)[probe + MethodCacheMethod], GIV(oldSpaceStart))))) { + GIV(methodCache)[probe + MethodCacheSelector] = 0; + } + } + probe += MethodCacheEntrySize; + } + + /* would prefer this to be in mapInterpreterOops, but + compatibility with ObjectMemory dictates it goes here. */ + GIV(needGCFlag) = 0; + GIV(gcStartUsecs) = ioUTCMicrosecondsNow(); + doScavenge(tenuringCriterion); + GIV(statScavenges) += 1; + GIV(statGCEndUsecs) = ioUTCMicrosecondsNow(); + GIV(statSGCDeltaUsecs) = GIV(statGCEndUsecs) - GIV(gcStartUsecs); + GIV(statScavengeGCUsecs) += GIV(statSGCDeltaUsecs); + GIV(statRootTableCount) = GIV(rememberedSetSize); + + /* begin logScavenge */ + if (GIV(scavengeLog)) { + writeScavengeLog(); + } + postGCAction(GCModeNewSpace); + runLeakCheckerFor(GCModeNewSpace); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeNewSpace)) == (GCCheckFreeSpace | GCModeNewSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } +} + + +/* Set the dimensions of the heap, answering the start of oldSpace. edenBytes + holds the desired ``size of eden'' + which is actually the total size of new space minus the reserve. edenBytes + is then divided up between eden + and the two survivor spaces, where each survivor space is a + scavengerDenominator (one seventh) of the total. */ +/* Transcript + cr; nextPutAll: 'heapBase: '; print: baseOfHeap; nextPut: $/; nextPutAll: + baseOfHeap hex; + nextPutAll: ' memLimit '; print: memLimit; nextPut: $/; nextPutAll: + memLimit hex; + nextPutAll: ' memEnd '; print: memEnd; nextPut: $/; nextPutAll: memEnd + hex; cr; flush. */ +/* This is more than a little counter-intuitive. Eden must include + interpreterAllocationReserveBytes. + */ + + /* SpurMemoryManager>>#setHeapBase:memoryLimit:endOfMemory: */ +static NoDbgRegParms NeverInline sqInt +setHeapBasememoryLimitendOfMemory(sqInt baseOfHeap, sqInt memLimit, sqInt memEnd) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt actualEdenBytes; + sqInt previousFSStart; + sqInt reserve; + sqInt survivorBytes; + sqInt totalBytes; + + reserve = interpreterAllocationReserveBytes(); + GIV(newSpaceStart) = baseOfHeap; + GIV(oldSpaceStart) = (baseOfHeap + GIV(edenBytes)) + reserve; + totalBytes = GIV(oldSpaceStart) - GIV(newSpaceStart); + + /* begin newSpaceStart:newSpaceBytes:survivorBytes: */ + survivorBytes = ((((GIV(oldSpaceStart) - GIV(newSpaceStart)) - reserve) / 7 /* scavengerDenominator */) & ~7); + actualEdenBytes = (((totalBytes - survivorBytes) - survivorBytes) & ~7); + assert((((totalBytes - actualEdenBytes) - survivorBytes) - survivorBytes) < (allocationUnit())); + if (((survivorBytes * 2) + actualEdenBytes) > ((0x200000000LL))) { + error("requested new space size exceeds 8Gb limit imposed by ephemeron/weak implementation."); + } + + /* for tenuring we require older objects below younger objects. since allocation + grows up this means that the survivor spaces must precede eden. */ + (GIV(pastSpace).start = GIV(newSpaceStart)); + (GIV(pastSpace).limit = GIV(newSpaceStart) + survivorBytes); + (GIV(futureSpace).start = (GIV(pastSpace).limit)); + (GIV(futureSpace).limit = ((GIV(pastSpace).limit)) + survivorBytes); + (GIV(eden).start = (GIV(futureSpace).limit)); + (GIV(eden).limit = GIV(newSpaceStart) + totalBytes); + assert((((GIV(futureSpace)).limit)) <= (GIV(newSpaceStart) + totalBytes)); + assert((((((GIV(eden)).start)) % (allocationUnit())) + ((((GIV(eden)).limit)) % (allocationUnit()))) == 0); + assert((((((GIV(pastSpace)).start)) % (allocationUnit())) + ((((GIV(pastSpace)).limit)) % (allocationUnit()))) == 0); + assert((((((GIV(futureSpace)).start)) % (allocationUnit())) + ((((GIV(futureSpace)).limit)) % (allocationUnit()))) == 0); + + /* begin initFutureSpaceStart */ + previousFSStart = GIV(futureSurvivorStart); + GIV(futureSurvivorStart) = (GIV(futureSpace).start); + GIV(tenuringProportion) = 0.9; + GIV(freeStart) = ((GIV(eden)).start); + GIV(pastSpaceStart) = ((GIV(pastSpace)).start); + GIV(freeOldSpaceStart) = memEnd; + GIV(endOfMemory) = memLimit; + return baseOfHeap; +} + + /* SpurMemoryManager>>#setIsPinnedOf:to: */ +static NoDbgRegParms void +setIsPinnedOfto(sqInt objOop, sqInt aBoolean) +{ + assert((isOldObject(objOop)) + && (!(isFreeObject(objOop)))); + byteAtput((void *)(objOop + (formatFieldByteOffset())),(aBoolean + ? (byteAt((void *)(objOop + (formatFieldByteOffset())))) | (1U << (pinnedBitByteShift())) + : (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (0xFF - (1U << (pinnedBitByteShift()))))); +} + + /* SpurMemoryManager>>#setIsRememberedOf:to: */ +static NoDbgRegParms void +setIsRememberedOfto(sqInt objOop, sqInt aBoolean) +{ + assert(!((isFreeObject(objOop)))); + byteAtput((void *)(objOop + (formatFieldByteOffset())),(aBoolean + ? (byteAt((void *)(objOop + (formatFieldByteOffset())))) | (1U << (rememberedBitByteShift())) + : (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (0xFF - (1U << (rememberedBitByteShift()))))); +} + + +/* Reduce the number of indexable fields in objOop, an arrayFormat or + longFormat object, to nSlots. + Convert the unused residual to a free chunk (if in oldSpace). + Answer the number of bytes returned to free memory, which may be zero. */ + + /* SpurMemoryManager>>#shorten:toIndexableSize: */ +sqInt +shortentoIndexableSize(sqInt objOop, sqInt indexableSize) +{ + sqInt delta; + + assert((indexableSize >= 0) + && (indexableSize < (lengthOf(objOop)))); + delta = doShortentoIndexableSize(objOop, indexableSize); + assert((lengthOf(followMaybeForwarded(objOop))) == indexableSize); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + return delta; +} + + +/* useful for VM debugging */ + + /* SpurMemoryManager>>#shortPrintObjectsFrom:to: */ +void +shortPrintObjectsFromto(sqInt startAddress, sqInt endAddress) +{ + sqInt oop; + + oop = objectBefore(startAddress); + oop = (oop + ? ((objectAfter(oop)) == startAddress + ? startAddress + : oop) + : startAddress); + while (oopisLessThan(oop, endAddress)) { + if (!(((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun()))) { + shortPrintOop(oop); + } + oop = objectAfter(oop); + } +} + + +/* Answer if the oop should be scavenged.. The method is called + shouldRemapOop: for compatibility with ObjectMemory. */ + + /* SpurMemoryManager>>#shouldRemapOop: */ +sqInt +shouldRemapOop(sqInt oop) +{ + return ((!(oop & (tagMask())))) + && (shouldRemapObj(oop)); +} + + /* SpurMemoryManager>>#sizeOfObjStack: */ +static NoDbgRegParms sqInt +sizeOfObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objStackPage; + sqInt total; + + if (objStack == GIV(nilObj)) { + return 0; + } + total = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + objStackPage = objStack; + while (1) { + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + if (!(objStackPage != 0)) break; + total += ObjStackLimit; + assert((fetchPointerofObject(ObjStackTopx, objStackPage)) == ObjStackLimit); + } + return total; +} + + /* SpurMemoryManager>>#slidingCompactionInProgress */ +static int +slidingCompactionInProgress(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(gcPhaseInProgress) == SlidingCompactionInProgress; +} + + +/* *DO NOT CONFUSE THIS WITH numSlotsOf:. + This is an ObjectMemory compatibility method with questionable semantics. + Answers the number of slots in the receiver. + If the receiver is a byte object, return the number of bytes. + If the receiver is a short object, return the number of shorts. + If the receiver is a word object, return the number of words. + If the receiver is a double word object, return the number of double + words. Otherwise return the number of pointers. */ + + /* SpurMemoryManager>>#slotSizeOf: */ +sqInt +slotSizeOf(sqInt oop) +{ + sqInt fmt; + usqInt numSlots; + usqInt numSlotsUsqInt; + + if (((oop & (tagMask())) != 0)) { + return 0; + } + + /* begin lengthOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + +/* Return one of the objects in the specialObjectsArray */ + + /* SpurMemoryManager>>#splObj: */ +sqInt +splObj(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); +} + + +/* Answer the start of objOop, which is either the address of the overflow + size word, or objOop itself, depending on the size of the object. This may + be applied to any kind of object, normal, forwarders or free chunks. */ + + /* SpurMemoryManager>>#startOfObject: */ +static NoDbgRegParms usqInt +startOfObject(sqInt objOop) +{ + return ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); +} + + +/* Part of InterpreterProxy's 1.14 API */ + + /* SpurMemoryManager>>#statNumGCs */ +sqInt +statNumGCs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (GIV(statScavenges) + GIV(statIncrGCs)) + GIV(statFullGCs); +} + + +/* A renaming for the Cogit, which can't make sense of GIV(oldSpaceStart) */ + + /* SpurMemoryManager>>#storeCheckBoundary */ +usqInt +storeCheckBoundary(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(oldSpaceStart); +} + + +/* This primitive is called from Squeak as... + storeSegmentFor: arrayOfRoots into: aWordArray outPointers: + anArray. + This primitive will store a binary image segment (in the same format as + objects in the heap) of the + set of objects in arrayOfObjects. All pointers from within the set to + objects outside the set will be + copied into the array of outPointers. In their place in the image segment + will be an oop equal to the + offset in the outPointer array (the first would be 8), but with the high + bit set. + + Since Spur has a class table the load primitive must insert classes that + have instances into the + class table. This primitive marks such classes using the isRemembered bit, + which isn't meaningful + as a remembered bit in the segment. + + The primitive expects the segmentWordArray and outPointerArray to be more + than adequately long. + In this case it returns normally, and truncates the two arrays to exactly + the right size. + + The primitive can fail for the following reasons with the specified + failure codes: + PrimErrGenericFailure: the segmentWordArray is too small for the version + stamp PrimErrWritePastObject: the segmentWordArray is too small to + contain the reachable objects + PrimErrBadIndex: the outPointerArray is too small + PrimErrNoMemory: there is insufficient free space to store the array + answered by objectsReachableFromRoots:, + or the savedFirstFields and savedOutHashes arrays. + PrimErrNeedCompaction: a GC is needed to make room for the array answered + by objectsReachableFromRoots: + PrimErrLimitExceeded: there is no room in the hash field to store out + pointer indices or class references, + or the outPointerArray is larger than the max value of the hash field. + PrimErrNoModification: the segmentWordArrayArg or outPointerArrayArg are + immutable PrimErrObjectIsPinned: the segmentWordArrayArg or + outPointerArrayArg are pinned */ + + /* SpurMemoryManager>>#storeImageSegmentInto:outPointers:roots: */ +static NoDbgRegParms sqInt +storeImageSegmentIntooutPointersroots(sqInt segmentWordArrayArg, sqInt outPointersArrayArg, sqInt arrayOfRootsArg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayOfObjects; + sqInt arrayOfRoots; + sqInt delta; + usqInt endSeg; + int hash; + sqInt here; + sqInt i; + sqInt indexableSize; + sqInt newSegAddrOrError; + sqInt numClassesInSegment; + usqInt numSlots; + sqInt numSlotsSqInt; + sqInt objOop; + sqInt objOopSqInt; + sqInt outIndex; + sqInt outPointersArray; + usqInt p; + sqInt savedFirstFields; + sqInt savedOutHashes; + usqInt segAddr; + sqInt segmentWordArray; + usqInt segStart; + sqInt tempObjOop; + sqInt there; + sqInt toDoLimit; + sqInt toDoLimitSqInt; + usqInt toDoLimitUsqInt; + + if (( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(segmentWordArrayArg + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + ) + || ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(outPointersArrayArg + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else // IMMUTABILITY + 0 +# endif + )) { + return PrimErrNoModification; + } + + /* Since segmentWordArrayArg & outPointersArrayArg may get shortened, they can't be pinned. */ + if ((((byteAt((void *)(segmentWordArrayArg + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0) + || (((byteAt((void *)(outPointersArrayArg + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)) { + return PrimErrObjectIsPinned; + } + if (((/* begin numSlotsOf: */ + assert((classIndexOf(outPointersArrayArg)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(outPointersArrayArg + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(outPointersArrayArg - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > (identityHashHalfWordMask())) { + return PrimErrLimitExceeded; + } + runLeakCheckerFor(GCModeImageSegment); + + /* First scavenge to collect any new space garbage that refers to the graph. */ + + /* begin scavengingGC */ + scavengingGCTenuringIf(TenureByAge); + segmentWordArray = /* updatePostScavenge: */ + ((!((longAt((void *)(segmentWordArrayArg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(segmentWordArrayArg) + : segmentWordArrayArg); + outPointersArray = /* updatePostScavenge: */ + ((!((longAt((void *)(outPointersArrayArg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(outPointersArrayArg) + : outPointersArrayArg); + arrayOfRoots = /* updatePostScavenge: */ + ((!((longAt((void *)(arrayOfRootsArg))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(arrayOfRootsArg) + : arrayOfRootsArg); + assert(!((forwardersIn(outPointersArray)))); + assert(!((forwardersIn(arrayOfRoots)))); + + /* Now compute the transitive closure, collecting the sequence of objects to be stored in the arrayOfObjects array. + Included in arrayOfObjects are the arrayOfRoots and all its contents. All objects have been unmarked. */ + arrayOfObjects = objectsReachableFromRoots(arrayOfRoots); + if (!arrayOfObjects) { + return PrimErrNoMemory; + } + + /* If objectsReachableFromRoots: answers an integer there is not enough continuous free space in which to allocate the + reachable objects. If there is sufficient free space then answer an error code to prompt a compacting GC and a retry. */ + if ((((arrayOfObjects) & 7) == 1)) { + if ((GIV(totalFreeOldSpace) - 8 /* allocationUnit */) >= ((arrayOfObjects >> 3))) { + return PrimErrNeedCompaction; + } + return PrimErrNoMemory; + } + assert(allObjectsUnmarked()); + assert(!((forwardersIn(arrayOfObjects)))); + runLeakCheckerFor(GCModeImageSegment); + + /* Both to expand the max size of segment and to reduce the length of the + load-time pass that adds classes to the class table, move classes to the + front of arrayOfObjects, leaving the root array as the first element. */ + + /* begin moveClassesForwardsIn: */ + /* if > 0, this is the index of the first non-class past the first element. */ + numClassesInSegment = (there = 0); + toDoLimitSqInt = ((/* begin numSlotsOf: */ + assert((classIndexOf(arrayOfObjects)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(arrayOfObjects + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(arrayOfObjects - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (here = 1; here <= toDoLimitSqInt; here += 1) { + objOopSqInt = longAt((void *)((arrayOfObjects + BaseHeaderSize) + ((((usqInt)(here) << (shiftForWord())))))); + hash = (long32At((void *)(objOopSqInt + 4))) & (identityHashHalfWordMask()); + if ((hash > (lastClassIndexPun())) + && ((classOrNilAtIndex(hash)) == objOopSqInt)) { + numClassesInSegment += 1; + if (there > 0) { + tempObjOop = longAt((void *)((arrayOfObjects + BaseHeaderSize) + ((((usqInt)(there) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(arrayOfObjects)) + && (!(isForwarded(arrayOfObjects)))); + assert(validStorePointerUncheckedArgs(there, arrayOfObjects, objOopSqInt)); + longAtput((void *)((arrayOfObjects + BaseHeaderSize) + ((((usqInt)(there) << (shiftForWord()))))),objOopSqInt); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(arrayOfObjects)) + && (!(isForwarded(arrayOfObjects)))); + assert(validStorePointerUncheckedArgs(here, arrayOfObjects, tempObjOop)); + longAtput((void *)((arrayOfObjects + BaseHeaderSize) + ((((usqInt)(here) << (shiftForWord()))))),tempObjOop); + there += 1; + } + } + else { + if (!there) { + there = here; + } + } + } + + /* The scheme is to copy the objects into segmentWordArray, and then map the oops in segmentWordArray. + Therefore the primitive needs to both map efficiently originals to copies in segmentWordArray and + be able to undo any side-effects if the primitive fails because either segmentWordArray or outPointersArray + is too small. The mapping is done by having the objects to be stored in arrayOfObjects refer to their mapped + locations through their first field, just like a forwarding pointer, but without becoming a forwarder, saving their + first field in savedFirstFields, and the objects in outPointersArray pointing to their locations in the outPointersArray + through their identityHashes, saved in savedOutHashes. + Since arrayOfObjects and its savedFirstFields, and outPointersArray and its saved hashes, can be enumerated + side-by-side, the hashes can be restored to the originals. So the first field of the heap object corresponding to + an object in arrayOfObjects is set to its location in segmentWordArray, and the hash of an object in outPointersArray + is set to its index in outPointersArray plus the top hash bit. Classes in arrayOfObjects have their marked bit set. + Oops in objects in segmentWordArray are therefore mapped by accessing the original oop, and following its first + field. Class indices in segmentWordArray are mapped by fetching the original class, and testing its marked bit. + If marked, the first field is followed to access the class copy in the segment. Out pointers (objects and classes, + which are unmarked), the object's identityHash is set (eek!!) to its index in the outPointersArray. So savedOutHashes + parallels the outPointersArray. The saved hash array is initialized with an out-of-range hash value so that the first + unused entry can be identified. */ + savedFirstFields = noInlineAllocateSlotsformatclassIndex(numSlotsOf(arrayOfObjects), wordIndexableFormat(), wordSizeClassIndexPun()); + savedOutHashes = noInlineAllocateSlotsformatclassIndex(numSlotsForBytes((numSlotsOf(outPointersArray)) * 4), firstLongFormat(), thirtyTwoBitLongsClassIndexPun()); + if ((!savedFirstFields) + || (!savedOutHashes)) { + freeObject(arrayOfObjects); + if (oopisGreaterThanOrEqualToandLessThan(savedFirstFields, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedFirstFields); + } + if (oopisGreaterThanOrEqualToandLessThan(savedOutHashes, GIV(oldSpaceStart), GIV(endOfMemory))) { + freeObject(savedOutHashes); + } + return PrimErrNoMemory; + } + /* begin numSlotsOf: */ + assert((classIndexOf(savedFirstFields)) > (isForwardedObjectClassIndexPun())); + if (((numSlots = byteAt((void *)(savedFirstFields + (numSlotsFieldByteOffset()))))) == (numSlotsMask())) { + numSlotsSqInt = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(savedFirstFields - BaseHeaderSize)))) << 8)))))) >> 8; + } + else { + numSlotsSqInt = numSlots; + } + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((savedFirstFields + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1, addressAfter(savedFirstFields))); + toDoLimitUsqInt = ((usqInt)(((savedFirstFields + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1)); + for (p = (((usqInt)(savedFirstFields + BaseHeaderSize))); p <= toDoLimitUsqInt; p += 8 /* allocationUnit */) { + longAtput((void *)(p),0); + } + /* begin numSlotsOf: */ + assert((classIndexOf(savedOutHashes)) > (isForwardedObjectClassIndexPun())); + if (((numSlots = byteAt((void *)(savedOutHashes + (numSlotsFieldByteOffset()))))) == (numSlotsMask())) { + numSlotsSqInt = ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(savedOutHashes - BaseHeaderSize)))) << 8)))))) >> 8; + } + else { + numSlotsSqInt = numSlots; + } + + /* begin fillObj:numSlots:with: */ + assert(oopisLessThan(((savedOutHashes + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1, addressAfter(savedOutHashes))); + toDoLimitUsqInt = ((usqInt)(((savedOutHashes + BaseHeaderSize) + (numSlotsSqInt * BytesPerOop)) - 1)); + for (p = (((usqInt)(savedOutHashes + BaseHeaderSize))); p <= toDoLimitUsqInt; p += 8 /* allocationUnit */) { + longAtput((void *)(p),((((usqInt)(((identityHashHalfWordMask()) + 1)) << 32))) + ((identityHashHalfWordMask()) + 1)); + } + segAddr = segmentWordArray + BaseHeaderSize; + endSeg = addressAfter(segmentWordArray); + + /* Write a version number for byte order and version check. */ + if (segAddr >= endSeg) { + return PrimErrGenericFailure; + } + long32Atput((void *)(segAddr),imageSegmentVersion()); + long32Atput((void *)(segAddr + 4),imageSegmentVersion()); + segStart = (segAddr += 8 /* allocationUnit */); + assert(arrayOfRoots == (fetchPointerofObject(0, arrayOfObjects))); + + /* Copy all reachable objects to the segment, setting the marked bit for all objects (clones) in the segment, + and the remembered bit for all classes (clones) in the segment. */ + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(arrayOfObjects)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(arrayOfObjects + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(arrayOfObjects - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = 0; i <= toDoLimit; i += 1) { + /* Check that classes in the segment are addressable. Since the top bit of the hash field is used to tag + classes external to the segment, the segment offset must not inadvertently set this bit. This limit still + allows for a million or more classes. */ + if ((i == numClassesInSegment) + && ((((segAddr - segStart) / 8 /* allocationUnit */) + (lastClassIndexPun())) >= TopHashBit)) { + return returnrestoringObjectsInupTosavedFirstFields(PrimErrLimitExceeded, arrayOfObjects, i, savedFirstFields); + } + objOop = longAt((void *)((arrayOfObjects + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + assert(!(((isImmediate(objOop)) + || (isForwarded(objOop))))); + newSegAddrOrError = copyObjtoAddrstopAtsavedFirstFieldsindex(objOop, segAddr, endSeg, savedFirstFields, i); + if (oopisLessThan(newSegAddrOrError, segStart)) { + return returnrestoringObjectsInupTosavedFirstFields(newSegAddrOrError, arrayOfObjects, i, savedFirstFields); + } + segAddr = newSegAddrOrError; + } + + /* Check that it can be safely shortened. */ + if ((endSeg != segAddr) + && ((endSeg - segAddr) < (BaseHeaderSize + BytesPerOop))) { + return returnrestoringObjectsInupTosavedFirstFields(PrimErrWritePastObject, arrayOfObjects, -1, savedFirstFields); + } + + /* Now scan, adding out pointers to the outPointersArray; all objects in arrayOfObjects + have their first field pointing to the corresponding copy in segmentWordArray. */ + if (((outIndex = mapOopsFromtooutPointersoutHashes(segStart, segAddr, outPointersArray, savedOutHashes))) < 0) { + return returnrestoringObjectsInsavedFirstFieldsandsavedHashes(PrimErrBadIndex, arrayOfObjects, savedFirstFields, outPointersArray, savedOutHashes); + } + + /* no room in outPointers; fail + We're done. Shorten the results, restore hashes and return. */ + indexableSize = (segAddr - (segmentWordArray + BaseHeaderSize)) / 4; + + /* begin shorten:toIndexableSize: */ + assert((indexableSize >= 0) + && (indexableSize < (lengthOf(segmentWordArray)))); + delta = doShortentoIndexableSize(segmentWordArray, indexableSize); + assert((lengthOf(followMaybeForwarded(segmentWordArray))) == indexableSize); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + + /* begin shorten:toIndexableSize: */ + assert((outIndex >= 0) + && (outIndex < (lengthOf(outPointersArray)))); + delta = doShortentoIndexableSize(outPointersArray, outIndex); + assert((lengthOf(followMaybeForwarded(outPointersArray))) == outIndex); + + /* Don't check if checking image segments, because we will check immediately after + the shorten in storeImageSegmentInto:outPointers:roots: */ + if (!(((checkForLeaks & GCModeImageSegment) != 0))) { + runLeakCheckerFor(GCCheckShorten); + } + return returnrestoringObjectsInsavedFirstFieldsandsavedHashes(PrimNoErr, arrayOfObjects, savedFirstFields, outPointersArray, savedOutHashes); +} + + /* SpurMemoryManager>>#storeInteger:ofObject:withValue: */ +void +storeIntegerofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqLong integerValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((((((usqLong)(integerValue)) >> 60) + 1) & 15) <= 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(fieldIndex, objOop, (((usqInt)integerValue << 3) | 1))); + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),(((usqInt)integerValue << 3) | 1)); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + } + } + else { + if (fmt < (firstShortFormat())) { + if (fmt < (firstLongFormat())) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 3)))),integerValue); + } + else { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2)))),integerValue); + } + } + else { + if (fmt < (firstCompiledMethodFormat())) { + if (fmt < (firstByteFormat())) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((objOop + BaseHeaderSize) + fieldIndex),integerValue); + } + else { + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 1)))),integerValue); + } + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + } + } + } +} + + /* SpurMemoryManager>>#storeLong32:ofObject:withValue: */ +int +storeLong32ofObjectwithValue(sqInt fieldIndex, sqInt obj, sqInt valueWord) +{ + return long32Atput((void *)((obj + BaseHeaderSize) + ((((usqInt)(fieldIndex) << 2)))),valueWord); +} + + /* SpurMemoryManager>>#storePointerUnchecked:ofObject:withValue: */ +sqInt +storePointerUncheckedofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqInt valuePointer) +{ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(fieldIndex, objOop, valuePointer)); + return longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); +} + + +/* Note must check here for stores of young objects into old ones. */ +/* See SistaCogit */ + + /* SpurMemoryManager>>#storePointer:ofObject:withValue: */ +sqInt +storePointerofObjectwithValue(sqInt fieldIndex, sqInt objOop, sqInt valuePointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(validStorePointerArgs(fieldIndex, objOop, valuePointer)); + + /* begin isOldObject: */ + assert(isNonImmediate(objOop)); + if (oopisGreaterThanOrEqualTo(objOop, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(objOop); + } + } + } + + /* most stores into young objects */ + return longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); +} + + +/* Answer a new String copied from a null-terminated C string, + or nil if out of memory. */ + + /* SpurMemoryManager>>#stringForCString: */ +sqInt +stringForCString(const char *aCString) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt formatField; + sqInt len; + usqInt newObj; + sqInt newString; + usqInt numBytes; + sqInt numSlots; + + len = strlen(aCString); + numSlots = (len + (BytesPerWord - 1)) / BytesPerWord; + formatField = (firstByteFormat()) + ((8 - len) & (BytesPerWord - 1)); + + /* begin allocateSlots:format:classIndex: */ + if (numSlots >= (numSlotsMask())) { + if ((((usqInt)(numSlots)) >> 56) > 0) { + newString = null; + goto l1; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (numSlots * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (numSlots <= ((1U << (fixedFieldsFieldWidth())) - 1)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + } + newString = allocateSlotsInOldSpacebytesformatclassIndex(numSlots, numBytes, formatField, ClassByteStringCompactIndex); + goto l1; + } + if (numSlots >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),((((usqInt)((numSlotsMask())) << (numSlotsFullShift())))) + numSlots); + longAtput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + ClassByteStringCompactIndex); + } + else { + longAtput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)(formatField) << (formatShift()))))) + ClassByteStringCompactIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + newString = newObj; + /* end allocateSlots:format:classIndex: */ +l1: + if (newString) { + strncpy(((char *) (newString + BaseHeaderSize)), aCString, len); + } + return newString; +} + + +/* On load, swizzle the pointers in an obj stack. Answer the obj stack's oop. */ + + /* SpurMemoryManager>>#swizzleObjStackAt: */ +static NoDbgRegParms sqInt +swizzleObjStackAt(sqInt objStackRootIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt field; + sqInt firstPage; + sqInt index; + sqInt page; + sqInt stackOrNil; + + firstPage = (stackOrNil = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord()))))))); + if (stackOrNil == GIV(nilObj)) { + return stackOrNil; + } + do { + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + assert((fetchPointerofObject(ObjStackMyx, stackOrNil)) == objStackRootIndex); + + /* There are four fixed slots in an obj stack, and a Topx of 0 indicates empty, so + if there were 5 slots in an oop stack, full would be 2, and the last 0-rel index is 4. + Hence the last index is topx + fixed slots - 1, or topx + ObjStackNextx */ + index = (longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) + ObjStackNextx; + + /* swizzle fields including ObjStackNextx, excluding ObjStackFreex and leave field containing the next link. */ + do { + field = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (!((field == 0) + || (((field & (tagMask())) != 0)))) { + field = swizzleObj(field); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(stackOrNil)) == (wordIndexableFormat())); + longAtput((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),field); + } + } while(((index -= 1)) >= ObjStackNextx); + } while(((stackOrNil = field)) != 0); + if ((stackOrNil = longAt((void *)((firstPage + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))))) { + page = firstPage; + do { + stackOrNil = swizzleObj(stackOrNil); + + /* begin storePointer:ofObjStack:withValue: */ + assert((formatOf(page)) == (wordIndexableFormat())); + longAtput((void *)((page + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord()))))),stackOrNil); + page = stackOrNil; + } while(((stackOrNil = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(ObjStackFreex) << (shiftForWord())))))))) != 0); + } + assert(isValidObjStackAt(objStackRootIndex)); + return longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(objStackRootIndex) << (shiftForWord())))))); +} + + +/* Do an incremental GC that tenures all surviving young objects to old + space. The selector tenuringIncrementalGC refers to the V3 incremental + collector, which is the collector for young objects, equivalent in + function to the scavenger. + The Spur incremental collector is called the global incremental garbage + collector or globalIGC for short. + */ + + /* SpurMemoryManager>>#tenuringIncrementalGC */ +void +tenuringIncrementalGC(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt savedTenuringThreshold; + + /* begin flushNewSpace */ + savedTenuringThreshold = GIV(tenureThreshold); + GIV(tenureThreshold) = GIV(oldSpaceStart); + scavengingGCTenuringIf(TenureByAge); + + /* begin setRawTenuringThreshold: */ + GIV(tenureThreshold) = savedTenuringThreshold; + assert((GIV(rememberedSetSize)) == 0); + assert(GIV(pastSpaceStart) == (((GIV(pastSpace)).start))); + assert(GIV(freeStart) == (((GIV(eden)).start))); +} + + +/* This assert is tricky. push:onObjStack: may call topOfObjStack: just after + pushing an + empty page on the stack, and will ask if the second page is valid. */ + + /* SpurMemoryManager>>#topOfObjStack: */ +static NoDbgRegParms sqInt +topOfObjStack(sqInt objStack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt topx; + + eassert(isValidObjStackPagemyIndexfirstPage(objStack, fetchPointerofObject(ObjStackMyx, objStack), objStack == (fetchPointerofObject(fetchPointerofObject(ObjStackMyx, objStack), GIV(hiddenRootsObj))))); + topx = longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + if (!topx) { + assert((fetchPointerofObject(ObjStackNextx, objStack)) == 0); + return null; + } + return longAt((void *)((objStack + BaseHeaderSize) + ((((usqInt)(((topx + ObjStackFixedSlots) - 1)) << (shiftForWord())))))); +} + + +/* Answers the top of the remappable oop stack. Useful when writing loops. + We support this excessence for compatibility with ObjectMemory. + Spur doesn't GC during allocation. */ + + /* SpurMemoryManager>>#topRemappableOop */ +sqInt +topRemappableOop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(remapBuffer)[GIV(remapBufferCount)]; +} + + +/* This method both computes the actual number of free bytes by traversing + all free objects + on the free lists/tree, and checks that the tree is valid. It is used + mainly by checkFreeSpace. */ + + /* SpurMemoryManager>>#totalFreeListBytes */ +static sqInt +totalFreeListBytes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bytesInChunk; + sqInt cameFrom; + sqInt i; + sqInt largeChild; + sqInt listNode; + sqInt nextNode; + sqInt smallChild; + sqInt totalFreeBytes; + sqInt treeNodeSqInt; + + totalFreeBytes = 0; + for (i = 1; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + bytesInChunk = i * 8 /* allocationUnit */; + listNode = GIV(freeLists)[i]; + while (listNode != 0) { + totalFreeBytes += bytesInChunk; + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(listNode)); + assert(bytesInChunk == (bytesInBody(listNode))); + nextNode = longAt((void *)((listNode + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(nextNode != listNode); + listNode = nextNode; + } + } + + /* begin freeTreeNodesDo: */ + treeNodeSqInt = GIV(freeLists)[0]; + if (!treeNodeSqInt) { + goto l1; + } + cameFrom = -1; + do { + assert((bytesInBody(treeNodeSqInt)) >= ((numFreeLists()) * (allocationUnit()))); + smallChild = longAt((void *)((treeNodeSqInt + BaseHeaderSize) + (3U << (shiftForWord())))); + largeChild = longAt((void *)((treeNodeSqInt + BaseHeaderSize) + (4U << (shiftForWord())))); + assert((smallChild == 0) + || (treeNodeSqInt == (fetchPointerofFreeChunk(freeChunkParentIndex(), smallChild)))); + assert((largeChild == 0) + || (treeNodeSqInt == (fetchPointerofFreeChunk(freeChunkParentIndex(), largeChild)))); + + /* apply if the node has no children, or it has no large children and we're + returning from the small child, or we're returning from the large child. */ + if (((smallChild == 0) + && (largeChild == 0)) + || ((largeChild + ? cameFrom == largeChild + : cameFrom == smallChild))) { + bytesInChunk = bytesInBody(treeNodeSqInt); + assert((bytesInChunk / (allocationUnit())) >= (numFreeLists())); + listNode = treeNodeSqInt; + while (listNode != 0) { + /* self printFreeChunk: listNode */ + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(listNode)); + assert((listNode == treeNodeSqInt) + || ((fetchPointerofFreeChunk(freeChunkParentIndex(), listNode)) == 0)); + totalFreeBytes += bytesInChunk; + assert(bytesInChunk == (bytesInBody(listNode))); + nextNode = longAt((void *)((listNode + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(nextNode != listNode); + listNode = nextNode; + } + + /* and since we've applied we must move on up */ + cameFrom = treeNodeSqInt; + treeNodeSqInt = longAt((void *)((treeNodeSqInt + BaseHeaderSize) + (2U << (shiftForWord())))); + } + else { + if ((smallChild != 0) + && (cameFrom != smallChild)) { + treeNodeSqInt = smallChild; + } + else { + assert(largeChild != 0); + treeNodeSqInt = largeChild; + } + cameFrom = -1; + } + } while(treeNodeSqInt != 0); + /* end freeTreeNodesDo: */ +l1: + return totalFreeBytes; +} + + /* SpurMemoryManager>>#trueObject */ +sqInt +trueObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(trueObj); +} + + +/* Ephemerons in the mourn queue will have been fired (had their + ephemeron-ness turned off), + but the ephemeron queue is not saved in the snapshot. So the snapshotted + ephemerons need to be unfired so they're still unqueued ephemerons in the + loaded image. */ + + /* SpurMemoryManager>>#unfireQueuedEphemeronsForSnapshot */ +static void +unfireQueuedEphemeronsForSnapshot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt iSqInt; + sqInt mourner; + sqInt numOnThisPage; + sqInt objStackPage; + + /* begin objStack:do: */ + if (GIV(mournQueue) == GIV(nilObj)) { + goto l1; + } + eassert(isValidObjStack(GIV(mournQueue))); + objStackPage = GIV(mournQueue); + while (objStackPage != 0) { + numOnThisPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord())))))); + for (iSqInt = ((numOnThisPage + ObjStackFixedSlots) - 1); iSqInt >= ObjStackFixedSlots; iSqInt += -1) { + mourner = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + if (((!(mourner & (tagMask())))) + && (((byteAt((void *)(mourner + (formatFieldByteOffset())))) & (formatMask())) == (nonIndexablePointerFormat()))) { + /* begin setFormatOf:to: */ + assert((((ephemeronFormat()) >= 0) && ((ephemeronFormat()) <= (formatMask())))); + byteAtput((void *)(mourner + (formatFieldByteOffset())),((byteAt((void *)(mourner + (formatFieldByteOffset())))) & (0xFF - (formatMask()))) + (ephemeronFormat())); + } + } + objStackPage = longAt((void *)((objStackPage + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))); + } + /* end objStack:do: */ +l1:; +} + + +/* Unlink a free object from the free lists. Do not alter totalFreeOldSpace. + Used for coalescing. + */ + + /* SpurMemoryManager>>#unlinkFreeChunk:chunkBytes: */ +static NoDbgRegParms sqInt +unlinkFreeChunkchunkBytes(sqInt freeChunk, sqInt chunkBytes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt lilliputian; + sqInt next; + sqInt nextFreeChunk; + sqInt nextSqInt; + sqInt prev; + + index = chunkBytes / 8 /* allocationUnit */; + + /* Pathological 64 bits case - size 1 - single linked list */ + + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + if (chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */)) { + return unlinkLilliputianChunkindex(freeChunk, index); + } + prev = longAt((void *)((freeChunk + BaseHeaderSize) + (1U << (shiftForWord())))); + + /* Has prev element: update double linked list */ + if (prev) { + nextFreeChunk = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + + /* begin setNextFreeChunkOf:withValue:chunkBytes: */ + /* begin isLilliputianSize: */ + assert(chunkBytes >= (BaseHeaderSize + (allocationUnit()))); + lilliputian = chunkBytes == (BaseHeaderSize + 8 /* allocationUnit */); + + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((nextFreeChunk == 0) + || (isFreeObject(nextFreeChunk))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),nextFreeChunk); + if ((nextFreeChunk != 0) + && (!lilliputian)) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextFreeChunk)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((nextFreeChunk + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + } + return freeChunk; + } + + /* Is the beginning of a list + Small chunk */ + if ((index < 64 /* numFreeLists */) + && ((1ULL << index) <= GIV(freeListsMask))) { + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(freeChunk)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(freeChunk)) == freeChunk))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((nextSqInt = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))))); + if (nextSqInt) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(nextSqInt)); + longAtput((void *)((nextSqInt + BaseHeaderSize) + (1U << (shiftForWord()))),0); + } + return freeChunk; + } + + /* Large chunk */ + next = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))); + if (next) { + inFreeTreeReplacewith(freeChunk, next); + } + else { + unlinkSolitaryFreeTreeNode(freeChunk); + } + + /* no list; remove the interior node + list; replace node with it */ + return freeChunk; +} + + /* SpurMemoryManager>>#unlinkLilliputianChunk:index: */ +static NoDbgRegParms NeverInline sqInt +unlinkLilliputianChunkindex(sqInt freeChunk, sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt next; + sqInt nextSqInt; + sqInt node; + sqInt prev; + + + /* for profiling */ + node = GIV(freeLists)[index]; + prev = 0; + while (node != 0) { + assert(node == (startOfObject(node))); + + /* begin assertValidFreeObject: */ + assert(assertInnerValidFreeObject(node)); + next = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + if (node == freeChunk) { + if (prev) { + /* begin setNextFreeChunkOf:withValue:isLilliputianSize: */ + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + assert((next == 0) + || (isFreeObject(next))); + longAtput((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord()))),next); + } + else { + /* begin unlinkFreeChunk:atIndex:isLilliputianSize: */ + assert(((bytesInBody(freeChunk)) == (index * (allocationUnit()))) + && ((index > 1) + && ((startOfObject(freeChunk)) == freeChunk))); + + /* For some reason the assertion is not compiled correctly */ + GIV(freeLists)[index] = ((nextSqInt = longAt((void *)((freeChunk + BaseHeaderSize) + (0U << (shiftForWord())))))); + } + return freeChunk; + } + prev = node; + node = next; + } + error("freeChunk not found in lilliputian chunk free list"); + return 0; +} + + +/* Unlink a freeTreeNode. Assumes the node has no list (null next link). */ + + /* SpurMemoryManager>>#unlinkSolitaryFreeTreeNode: */ +static NoDbgRegParms void +unlinkSolitaryFreeTreeNode(sqInt freeTreeNode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fieldIndex; + sqInt larger; + sqInt parent; + sqInt smaller; + + assert((fetchPointerofFreeChunk(freeChunkNextIndex(), freeTreeNode)) == 0); + + /* case 1. interior node has one child, P = parent, N = node, S = subtree (mirrored for large vs small) + ___ ___ + | P | | P | + _/_ _/_ + | N | => | S | + _/_ + | S | + case 2: interior node has two children, , P = parent, N = node, L = smaller, left subtree, R = larger, right subtree. + add the left subtree to the bottom left of the right subtree (mirrored for large vs small) + ___ ___ + | P | | P | + _/_ _/_ + | N | => | R | + _/_ _\_ _/_ + | L | | R | | L | */ + smaller = longAt((void *)((freeTreeNode + BaseHeaderSize) + (3U << (shiftForWord())))); + larger = longAt((void *)((freeTreeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + parent = longAt((void *)((freeTreeNode + BaseHeaderSize) + (2U << (shiftForWord())))); + if (parent) { + if (smaller) { + fieldIndex = (freeTreeNode == (longAt((void *)((parent + BaseHeaderSize) + (3U << (shiftForWord()))))) + ? 3 /* freeChunkSmallerIndex */ + : 4 /* freeChunkLargerIndex */); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(parent)); + assert((smaller == 0) + || (isFreeObject(smaller))); + longAtput((void *)((parent + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),smaller); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(smaller)); + assert((parent == 0) + || (isFreeObject(parent))); + longAtput((void *)((smaller + BaseHeaderSize) + (2U << (shiftForWord()))),parent); + if (larger) { + addFreeSubTree(larger); + } + } + else { + fieldIndex = (freeTreeNode == (longAt((void *)((parent + BaseHeaderSize) + (3U << (shiftForWord()))))) + ? 3 /* freeChunkSmallerIndex */ + : 4 /* freeChunkLargerIndex */); + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(parent)); + assert((larger == 0) + || (isFreeObject(larger))); + longAtput((void *)((parent + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),larger); + if (larger) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(larger)); + assert((parent == 0) + || (isFreeObject(parent))); + longAtput((void *)((larger + BaseHeaderSize) + (2U << (shiftForWord()))),parent); + } + } + } + else { + if (smaller) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(smaller)); + longAtput((void *)((smaller + BaseHeaderSize) + (2U << (shiftForWord()))),0); + GIV(freeLists)[0] = smaller; + if (larger) { + addFreeSubTree(larger); + } + } + else { + if (larger) { + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(larger)); + longAtput((void *)((larger + BaseHeaderSize) + (2U << (shiftForWord()))),0); + } + GIV(freeLists)[0] = larger; + } + } +} + + /* SpurMemoryManager>>#unpinObject: */ +sqInt +unpinObject(sqInt objOop) +{ + assert(isNonImmediate(objOop)); + setIsPinnedOfto(objOop, 0); + return 0; +} + + /* SpurMemoryManager>>#unscannedEphemeronsContainsOnlyActiveEphemerons */ +static sqInt +unscannedEphemeronsContainsOnlyActiveEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt entry; + usqInt p; + + for (p = ((GIV(unscannedEphemerons).start)); p <= ((GIV(unscannedEphemerons).top)); p += BytesPerOop) { + entry = longAt((void *)(p)); + if (!(((!(entry & (tagMask())))) + && ((isEphemeron(entry)) + && (!(isMarked(followedKeyOfEphemeron(entry))))))) { + return 0; + } + } + return 1; +} + + /* SpurMemoryManager>>#unscannedEphemeronsContainsOnlyEphemerons */ +static sqInt +unscannedEphemeronsContainsOnlyEphemerons(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt entry; + usqInt p; + + for (p = ((GIV(unscannedEphemerons).start)); p <= ((GIV(unscannedEphemerons).top)); p += BytesPerOop) { + entry = longAt((void *)(p)); + if (!(((!(entry & (tagMask())))) + && (isEphemeron(entry)))) { + return 0; + } + } + return 1; +} + + /* SpurMemoryManager>>#updateListStartingAt: */ +static NoDbgRegParms void +updateListStartingAt(sqInt freeNode) +{ + sqInt obj; + sqInt prev; + + obj = 0; + if (!freeNode) { + return; + } + assert(!((isLilliputianSize(bytesInBody(freeNode))))); + prev = freeNode; + + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(prev)); + longAtput((void *)((prev + BaseHeaderSize) + (1U << (shiftForWord()))),0); + while (1) { + obj = longAt((void *)((prev + BaseHeaderSize) + (0U << (shiftForWord())))); + if (!(obj != 0)) break; + /* begin storePointer:ofFreeChunk:withValue: */ + assert(isFreeObject(obj)); + assert((prev == 0) + || (isFreeObject(prev))); + longAtput((void *)((obj + BaseHeaderSize) + (1U << (shiftForWord()))),prev); + prev = obj; + } +} + + +/* Check the hashes of classes in the table. The tricky thing here is that + classes may be duplicated + in the table. So each entry must be in the table at its hash, even if it + is elsewhere in the table. */ + + /* SpurMemoryManager>>#validClassTableHashes */ +static sqInt +validClassTableHashes(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classOrNilSqInt; + int hash; + sqInt i; + sqInt ignored; + sqInt j; + sqInt page; + sqInt toDoLimit; + + if (!(validClassTableRootPages())) { + return 0; + } + + /* begin classTableEntriesDo: */ + for (i = 0; i < GIV(numClassTablePages); i += 1) { + page = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + toDoLimit = (1U << (classTableMajorIndexShift())) - 1; + for (j = 0; j <= toDoLimit; j += 1) { + classOrNilSqInt = longAt((void *)((page + BaseHeaderSize) + ((((usqInt)(j) << (shiftForWord())))))); + if (classOrNilSqInt != GIV(nilObj)) { + ignored = ((((usqInt)(i) << (classTableMajorIndexShift())))) + j; + if ((!((longAt((void *)(classOrNilSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + return 0; + } + hash = (long32At((void *)(classOrNilSqInt + 4))) & (identityHashHalfWordMask()); + if (!hash) { + return 0; + } + if ((noCheckClassAtIndex(hash)) != classOrNilSqInt) { + return 0; + } + } + } + } + return 1; +} + + +/* Answer if hiddenRootsObj is of the right size with the + expected contents, and if numClassTablePages is correct. */ + + /* SpurMemoryManager>>#validClassTableRootPages */ +static sqInt +validClassTableRootPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + usqInt numSlots; + sqInt obj; + sqInt toDoLimit; + + if (!(((/* begin numSlotsOf: */ + assert((classIndexOf(GIV(hiddenRootsObj))) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(GIV(hiddenRootsObj) + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(GIV(hiddenRootsObj) - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == ((1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift()))) + 8 /* hiddenRootSlots */))) { + return 0; + } + + /* is it in range? */ + if (!((GIV(numClassTablePages) > 1) + && (GIV(numClassTablePages) <= (1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift())))))) { + return 0; + } + + /* are all pages the right size? */ + for (i = 0; i < GIV(numClassTablePages); i += 1) { + obj = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (!((addressCouldBeObj(obj)) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == (1U << (classTableMajorIndexShift()))))) { + return 0; + } + } + + /* are all entries beyond numClassTablePages nil? */ + toDoLimit = (1U << (22 /* classIndexFieldWidth */ - (classTableMajorIndexShift()))) - 1; + for (i = GIV(numClassTablePages); i <= toDoLimit; i += 1) { + if ((longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))))) != GIV(nilObj)) { + return 0; + } + } + return 1; +} + + +/* useful for debugging */ + + /* SpurMemoryManager>>#validFreeTree */ +int +validFreeTree(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return !(validFreeTreeChunkparent(GIV(freeLists)[0], 0)); +} + + /* SpurMemoryManager>>#validFreeTreeChunk: */ +static NoDbgRegParms sqInt +validFreeTreeChunk(sqInt chunk) +{ + if (!(segmentContainingObj(chunk))) { + return 0; + } + return !(validFreeTreeChunkparent(chunk, longAt((void *)((chunk + BaseHeaderSize) + (2U << (shiftForWord())))))); +} + + /* SpurMemoryManager>>#validFreeTreeChunk:parent: */ +static NoDbgRegParms const char * +validFreeTreeChunkparent(sqInt chunk, sqInt parent) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + const char *reason; + + if (!chunk) { + return null; + } + if (!(/* addressCouldBeOldObj: */ + ((chunk & (BaseHeaderSize - 1)) == 0) + && (oopisGreaterThanOrEqualToandLessThan(chunk, GIV(oldSpaceStart), GIV(endOfMemory))))) { + return "not in old space"; + } + if (((bytesInBody(chunk)) / 8 /* allocationUnit */) < 64 /* numFreeLists */) { + return "too small"; + } + if (parent != (longAt((void *)((chunk + BaseHeaderSize) + (2U << (shiftForWord())))))) { + return "bad parent"; + } + if ((segmentContainingObj(chunk)) != (segmentContainingObj(addressAfter(chunk)))) { + return "not in one segment"; + } + if ((reason = validFreeTreeChunkparent(longAt((void *)((chunk + BaseHeaderSize) + (3U << (shiftForWord())))), chunk))) { + return reason; + } + if ((reason = validFreeTreeChunkparent(longAt((void *)((chunk + BaseHeaderSize) + (4U << (shiftForWord())))), chunk))) { + return reason; + } + return null; +} + + /* SpurMemoryManager>>#validObjStacks */ +static sqInt +validObjStacks(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((GIV(markStack) == GIV(nilObj)) + || (isValidObjStack(GIV(markStack)))) + && (((GIV(weaklingStack) == GIV(nilObj)) + || (isValidObjStack(GIV(weaklingStack)))) + && ((GIV(mournQueue) == GIV(nilObj)) + || (isValidObjStack(GIV(mournQueue))))); +} + + +/* This is a hook for the CoMemoryManagers to check for valid compiled code. + It is a noop here. */ + + /* SpurMemoryManager>>#validPostBecomeArrayContents: */ +static NoDbgRegParms sqInt +validPostBecomeArrayContents(sqInt anArray) +{ + return 1; +} + + /* SpurMemoryManager>>#validStorePointerArgs:_:_: */ +static NoDbgRegParms sqInt +validStorePointerArgs(sqInt fieldIndex, sqInt objOop, sqInt valuePointer) +{ + usqInt numSlots; + + return (fieldIndex >= 0) + && ((fieldIndex < ((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))) + && (!((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))); +} + + /* SpurMemoryManager>>#validStorePointerUncheckedArgs:_:_: */ +static NoDbgRegParms sqInt +validStorePointerUncheckedArgs(sqInt fieldIndex, sqInt objOop, sqInt valuePointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + + return (fieldIndex >= 0) + && (((fieldIndex == 0) + || (fieldIndex < ((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))))) + && ((((valuePointer & (tagMask())) != 0)) + || ((/* isInHeapBounds: */ + (oopisGreaterThanOrEqualTo(valuePointer, (!(minCogMethodAddress()) + ? GIV(nilObj) + : minCogMethodAddress()))) + && (oopisLessThan(valuePointer, GIV(endOfMemory)))) + || (((fieldIndex == 0) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 0) + || (GIV(gcPhaseInProgress) == SlidingCompactionInProgress))) + || ((((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) >= (sixtyFourBitIndexableFormat())) + && ((((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask())) < (firstCompiledMethodFormat())) + || (fieldIndex > (literalCountOf(objOop))))))))); +} + + +/* 1/true = big, 0/false = little. + Answer false for little endian, true for big endian (should of course be + vmIsBigEndian; sigh) */ + + /* SpurMemoryManager>>#vmEndianness */ +sqInt +vmEndianness(void) +{ + return VMBIGENDIAN; +} + + /* SpurMemoryManager>>#whereIsMaybeHeapThing: */ +static NoDbgRegParms char * +whereIsMaybeHeapThing(sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (/* isInNewSpace: */ + (oopisLessThan(anOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(anOop, GIV(newSpaceStart)))) { + if (oopisGreaterThanOrEqualToandLessThan(anOop, ((GIV(eden)).start), GIV(freeStart))) { + return " is in eden"; + } + if (oopisGreaterThanOrEqualToandLessThan(anOop, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))) { + return " is in future space"; + } + if (oopisGreaterThanOrEqualToandLessThan(anOop, ((GIV(pastSpace)).start), GIV(pastSpaceStart))) { + return " is in past space"; + } + return " is in new space"; + } + if (oopisGreaterThanOrEqualToandLessThan(anOop, GIV(oldSpaceStart), GIV(endOfMemory))) { + if (segmentContainingObj(anOop)) { + return " is in old space"; + } + return " is between old space segments"; + } + return null; +} + + +/* Sweep all of old space, sliding unpinned marked objects down over free and + unmarked objects. + Let the segmentManager mark which segments contain pinned objects via + notePinned:. + */ + + /* SpurPlanningCompactor>>#compact */ +static NeverInline void +compact(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt finalPass; + sqInt firstPass; + sqInt followingWord; + usqInt followingWordAddress; + sqInt highestSuitableFreeBlock; + sqInt i; + sqInt largestFreeChunk; + sqInt largestFreeChunkSqInt; + usqInt numSlots; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt sizeOfEden; + sqInt spaceEstimate; + sqInt stackOrNil; + + + /* for profiling */ + /* begin initializeScanCheckingForFullyCompactedHeap */ + GIV(firstMobileObject) = (GIV(lastMobileObject) = (GIV(objectAfterLastMobileObject) = null)); + reinitializeScanFrom(GIV(hiddenRootsObj)); + if (!GIV(firstFreeObject)) { + error("uncompactable heap; no unmarked objects found"); + } + if (GIV(firstMobileObject) >= GIV(endOfMemory)) { + /* begin unmarkObjectsInFullyCompactedHeap */ + /* begin unmarkInitialImmobileObjects */ + /* begin allOldSpaceObjectsFrom:do: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objOopSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objOopSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (oopisGreaterThanOrEqualTo(objOopSqInt, GIV(firstMobileObject))) { + goto l2; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l4; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l4:; + } + /* end unmarkInitialImmobileObjects */ +l2: + + /* begin unmarkSurvivingObjectsForCompact */ + /* begin allPastSpaceObjectsDo: */ + /* begin allPastSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + address = ((GIV(pastSpace)).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + while (oopisLessThan(objOopSqInt, GIV(pastSpaceStart))) { + assert(isEnumerableObjectNoAssert(objOopSqInt)); + if ((byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + objOopSqInt = GIV(pastSpaceStart); + goto l3; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l3:; + } + return; + } + + /* begin initializeCompaction */ + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + + /* begin selectSavedFirstFieldsSpace */ + spaceEstimate = (GIV(endOfMemory) - GIV(nilObj)) / 40; + sizeOfEden = (((GIV(eden)).limit)) - (((GIV(eden)).start)); + if (spaceEstimate > sizeOfEden) { + /* begin findHighestSuitableFreeBlock: */ + if ((largestFreeChunk = findLargestFreeChunk())) { + if (((bytesInBody(largestFreeChunk)) >= spaceEstimate) + && ((((usqInt)largestFreeChunk)) > (((usqInt)(GIV(endOfMemory) - GIV(totalFreeOldSpace)))))) { + highestSuitableFreeBlock = largestFreeChunk; + goto l8; + } + } + highestSuitableFreeBlock = null; + /* end findHighestSuitableFreeBlock: */ +l8: + if (highestSuitableFreeBlock) { + if ((spaceEstimate > ((/* begin sizeOfFree: */ + assert(isFreeObject(highestSuitableFreeBlock)), + bytesInBody(highestSuitableFreeBlock)))) + && (useSegmentForSavedFirstFieldsSpace(spaceEstimate))) { + goto l5; + } + if (((/* begin sizeOfFree: */ + assert(isFreeObject(highestSuitableFreeBlock)), + bytesInBody(highestSuitableFreeBlock))) > sizeOfEden) { + /* begin useFreeChunkForSavedFirstFieldsSpace: */ + assert(validFreeTreeChunk(highestSuitableFreeBlock)); + (GIV(savedFirstFieldsSpace).start = highestSuitableFreeBlock + (4 /* freeChunkLargerIndex */ * BytesPerOop)); + (GIV(savedFirstFieldsSpace).limit = addressAfter(highestSuitableFreeBlock)); + GIV(savedFirstFieldsSpaceNotInOldSpace) = 0; + assert(!((savedFirstFieldsSpaceWasAllocated()))); + goto l5; + } + } + if (useSegmentForSavedFirstFieldsSpace(spaceEstimate)) { + goto l5; + } + } + + /* begin useEdenForSavedFirstFieldsSpace */ + (GIV(savedFirstFieldsSpace).start = ((GIV(eden)).start)); + (GIV(savedFirstFieldsSpace).limit = ((GIV(eden)).limit)); + GIV(savedFirstFieldsSpaceNotInOldSpace) = 1; + assert(!((savedFirstFieldsSpaceWasAllocated()))); + /* end selectSavedFirstFieldsSpace */ +l5: + + /* begin unpinRememberedSet */ + /* begin fetchPointer:ofObject: */ + GIV(firstFieldOfRememberedSet) = longAt((void *)(((longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord()))))))) + BaseHeaderSize) + (0U << (shiftForWord())))); + setIsPinnedOfto(longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))), 0); + + /* begin resetFreeListHeads */ + GIV(freeListsMask) = 0; + for (i = 0; i <= 0x3F /* (numFreeLists - 1) */; i += 1) { + GIV(freeLists)[i] = 0; + } + + /* begin prepareObjStacksForPlanningCompactor */ + /* begin prepareObjStackForPlanningCompactor: */ + if (GIV(markStack) == GIV(nilObj)) { + goto l7; + } + stackOrNil = GIV(markStack); + do { + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(stackOrNil + 4),((((long32At((void *)(stackOrNil + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + ((longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) & (identityHashHalfWordMask()))); + } while(((stackOrNil = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))) != 0); + /* end prepareObjStackForPlanningCompactor: */ +l7: + if (GIV(weaklingStack) == GIV(nilObj)) { + goto l9; + } + stackOrNil = GIV(weaklingStack); + do { + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(stackOrNil + 4),((((long32At((void *)(stackOrNil + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + ((longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) & (identityHashHalfWordMask()))); + } while(((stackOrNil = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))) != 0); + /* end prepareObjStackForPlanningCompactor: */ +l9: + if (GIV(mournQueue) == GIV(nilObj)) { + goto l6; + } + stackOrNil = GIV(mournQueue); + do { + assert((numSlotsOfAny(stackOrNil)) == ObjStackPageSlots); + + /* begin setHashBitsOf:to: */ + long32Atput((void *)(stackOrNil + 4),((((long32At((void *)(stackOrNil + 4))) | (identityHashHalfWordMask())) - (identityHashHalfWordMask()))) + ((longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackTopx) << (shiftForWord()))))))) & (identityHashHalfWordMask()))); + } while(((stackOrNil = longAt((void *)((stackOrNil + BaseHeaderSize) + ((((usqInt)(ObjStackNextx) << (shiftForWord())))))))) != 0); + /* end prepareObjStackForPlanningCompactor: */ +l6: + GIV(totalFreeOldSpace) = 0; + GIV(gcPhaseInProgress) = SlidingCompactionInProgress; + (GIV(savedFirstFieldsSpace).top = ((GIV(savedFirstFieldsSpace).start)) - BytesPerOop); + firstPass = 1; + while (1) { + finalPass = planCompactSavingForwarders(); + assert((validRelocationPlanInPass(finalPass)) == 0); + updatePointers(); + copyAndUnmark(firstPass); + + /* Would like to check here, but can't if multi-pass. */ + + /* Currently we do only a single pass if a normal GC, assuming that a pass will + always compact plenty of space. But we should perhaps check this assumption + by looking at the large free tree and seeing that the ratio of the largest free + chunk to the total ammount of free space is high. */ + if (finalPass + || (GIV(biasForGC))) break; + firstPass = 0; + reinitializeScanFrom(GIV(firstFreeObject)); + if (!(GIV(firstMobileObject) > GIV(firstFreeObject))) { + /* We get here when firstFreeObject is the last object in memory. + firstMobileObject may be random value from previous compaction + in this case, and heap is fully compacted. Abort compaction. + We set firstFreeObject to firstMobileObject for unmarking. */ + GIV(firstFreeObject) = GIV(firstMobileObject); + unmarkObjectsFromFirstFreeObject(); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + endCompaction(); + return; + } + + /* begin updateSavedFirstFieldsSpaceIfNecessary */ + if ((/* savedFirstFieldsSpaceInFreeChunk */ + (!GIV(savedFirstFieldsSpaceNotInOldSpace)) + && (oopisGreaterThan((GIV(savedFirstFieldsSpace).start), GIV(nilObj)))) + && (((GIV(savedFirstFieldsSpace).start)) < GIV(lastMobileObject))) { + spaceEstimate = ((GIV(savedFirstFieldsSpace).limit)) - ((GIV(savedFirstFieldsSpace).start)); + + /* begin findHighestSuitableFreeBlock: */ + if ((largestFreeChunkSqInt = findLargestFreeChunk())) { + if (((bytesInBody(largestFreeChunkSqInt)) >= spaceEstimate) + && ((((usqInt)largestFreeChunkSqInt)) > (((usqInt)(GIV(endOfMemory) - GIV(totalFreeOldSpace)))))) { + largestFreeChunk = largestFreeChunkSqInt; + goto l1; + } + } + largestFreeChunk = null; + /* end findHighestSuitableFreeBlock: */ +l1: + if (largestFreeChunk) { + /* begin useFreeChunkForSavedFirstFieldsSpace: */ + assert(validFreeTreeChunk(largestFreeChunk)); + (GIV(savedFirstFieldsSpace).start = largestFreeChunk + (4 /* freeChunkLargerIndex */ * BytesPerOop)); + (GIV(savedFirstFieldsSpace).limit = addressAfter(largestFreeChunk)); + GIV(savedFirstFieldsSpaceNotInOldSpace) = 0; + assert(!((savedFirstFieldsSpaceWasAllocated()))); + } + else { + /* begin useEdenForSavedFirstFieldsSpace */ + (GIV(savedFirstFieldsSpace).start = ((GIV(eden)).start)); + (GIV(savedFirstFieldsSpace).limit = ((GIV(eden)).limit)); + GIV(savedFirstFieldsSpaceNotInOldSpace) = 1; + assert(!((savedFirstFieldsSpaceWasAllocated()))); + } + } + (GIV(savedFirstFieldsSpace).top = ((GIV(savedFirstFieldsSpace).start)) - BytesPerOop); + } + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + endCompaction(); +} + + +/* Sweep the mobile portion of the heap, moving objects to their eventual + locations, and clearing their marked bits. + Remember to update the savedFirstFields of pointer objects, as these have + been forwarded. + Answer if the end of the heap was reached (savedFirstFieldsSpace has not + overflowed). + The enumerations in planCompactSavingForwarders, + updatePointersInMobileObjects and copyAndUnmarkMobileObjects + match. We could implement them as a single enumeration method taking + several block arguments, but arguably that + would make understanding an already tricky algorithm more difficult. + Instead we tolerate the duplication and encourage + the reader to diff the three methods to see where they diverge (e.g. via + Cmd-shift-C). */ + + /* SpurPlanningCompactor>>#copyAndUnmarkMobileObjects */ +static NeverInline sqInt +copyAndUnmarkMobileObjects(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt availableSpace; + usqInt bytes; + sqInt destObj; + sqInt finalObject; + sqInt firstField; + sqInt followingWord; + usqInt followingWordAddress; + usqInt limit; + usqInt next; + usqInt nextBytes; + sqInt nextObj; + usqInt numSlots; + usqInt obj; + usqInt objBytes; + sqInt objOop; + usqInt previousPin; + sqInt prevObj; + sqInt prevPrevObj; + SpurSegmentInfo *seg; + usqInt start; + usqInt startOfPreviousPin; + usqInt toFinger; + usqInt top; + + availableSpace = 0; + previousPin = 0; + assert(!((isMarked(GIV(firstFreeObject))))); + toFinger = /* startOfObject: */ + ((byteAt((void *)(GIV(firstFreeObject) + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? GIV(firstFreeObject) - BaseHeaderSize + : GIV(firstFreeObject)); + top = (GIV(savedFirstFieldsSpace).start); + startOfPreviousPin = 0; + finalObject = (!(GIV(lastMobileObject)) + ? GIV(nilObj) + : GIV(lastMobileObject)); + + /* begin allOldSpaceEntitiesForCompactingFrom:to:do: */ + assert(isOldObject(GIV(firstFreeObject))); + assert(oopisLessThanOrEqualTo(finalObject, GIV(endOfMemory))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstFreeObject); + limit = (oopisLessThan(finalObject, GIV(endOfMemory)) + ? addressAfter(finalObject) + : GIV(endOfMemory)); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, limit))) break; + assert((long64At((void *)(objOop))) != 0); + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + nextObj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + nextObj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1: + assert((previousPin == null + ? toFinger <= (startOfObject(objOop)) + : (isMarked(previousPin)) + && (toFinger <= startOfPreviousPin))); + assert(GIV(savedFirstFieldsSpaceNotInOldSpace) + || (toFinger < top)); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + if (!previousPin) { + previousPin = objOop; + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + } + } + else { + bytes = bytesInBody(objOop); + while ((toFinger <= startOfPreviousPin) + && ((bytes != ((availableSpace = startOfPreviousPin - toFinger))) + && ((bytes + (16)) > availableSpace))) { + /* The object does not fit in the space between toFinger and previousPin. + Move toFinger up to point at the first unmarked or mobile object after + previousPin, or, if previousPin is contiguous with o, to the start of this + object. Update previousPin to be the next pinned object above toFInger + and below this object, or nil if no such pinned object exists. + Any unfillable gaps between adjacent pinned objects will be freed. */ + if (availableSpace > 0) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += availableSpace; + freeChunkWithBytesat(availableSpace, toFinger); + } + do { + assert((isMarked(previousPin)) + && (isPinned(previousPin))); + + /* begin unmarkPinned: */ + if (((longAt((void *)(previousPin))) & (classIndexMask())) == (segmentBridgePun())) { + assert(isMarked(previousPin)); + } + else { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(previousPin))); + byteAtput((void *)(previousPin + (markBitsByteOffset())),(byteAt((void *)(previousPin + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + + /* begin notePinned: */ + assert(isPinned(previousPin)); + assert(!((isSegmentBridge(previousPin)))); + seg = segmentContainingObj(previousPin); + (seg->containsPinned = 1); + } + toFinger = addressAfter(previousPin); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(toFinger + (numSlotsFieldByteOffset()))); + previousPin = (numSlots == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + } while((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && ((((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0) + && (previousPin < objOop))); + + /* Now previousPin is either equal to o or mobile. + Move it to the next pinned object below o */ + while (!((previousPin >= objOop) + || ((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && (((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + previousPin = oldSpaceObjectAfter(previousPin); + } + if (previousPin >= objOop) { + previousPin = null; + startOfPreviousPin = 0; + } + else { + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(previousPin + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? previousPin - BaseHeaderSize + : previousPin); + } + } + firstField = longAt((void *)(top)); + + /* begin copyAndUnmarkObject:to:bytes:firstField: */ + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + numSlots = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + destObj = (numSlots == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + start = /* startOfObject:given: */ + (numSlots == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + + /* memmove must be used since the ranges may overlap. */ + memmove(((void *)toFinger), ((void *)start), bytes); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(destObj)) + && (!(isForwarded(destObj)))); + assert(validStorePointerUncheckedArgs(0, destObj, firstField)); + longAtput((void *)((destObj + BaseHeaderSize) + (0U << (shiftForWord()))),firstField); + toFinger += bytes; + if (((top += BytesPerOop)) >= ((GIV(savedFirstFieldsSpace).limit))) { + assert(((GIV(savedFirstFieldsSpace).top)) == (top - BytesPerOop)); + assert(nextObj == GIV(objectAfterLastMobileObject)); + if (!previousPin) { + previousPin = nextObj; + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(nextObj + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? nextObj - BaseHeaderSize + : nextObj); + } + + /* Create a free object for firstFreeObject to be set to on the next pass, but + do not link it into the free tree as it will be written over in that next pass. */ + if (toFinger < startOfPreviousPin) { + GIV(firstFreeObject) = initFreeChunkWithBytesat(startOfPreviousPin - toFinger, toFinger); + } + else { + GIV(firstFreeObject) = previousPin; + } + return 0; + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + objOop = nextObj; + } + freeFromupTonextObject(toFinger, GIV(endOfMemory), (!(previousPin) + ? (!(GIV(objectAfterLastMobileObject)) + ? objectAfter(GIV(firstFreeObject)) + : GIV(objectAfterLastMobileObject)) + : previousPin)); + + /* begin coalesceFrom: */ + next = 0; + if (toFinger >= GIV(endOfMemory)) { + goto l2; + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(toFinger + (numSlotsFieldByteOffset()))); + obj = (numSlots == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + while (1) { + next = oldSpaceObjectAfter(obj); + if (!(next < GIV(endOfMemory))) break; + if ((((longAt((void *)(obj))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) + && (((longAt((void *)(next))) & (classIndexMask())) == (isFreeObjectClassIndexPun()))) { + objBytes = bytesInBody(obj); + nextBytes = bytesInBody(next); + unlinkFreeChunkchunkBytes(obj, objBytes); + unlinkFreeChunkchunkBytes(next, nextBytes); + obj = freeChunkWithBytesat(objBytes + nextBytes, /* startOfObject: */ + ((byteAt((void *)(obj + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? obj - BaseHeaderSize + : obj)); + } + else { + obj = next; + } + } + /* end coalesceFrom: */ +l2: + return 1; +} + + +/* Sweep the heap, unmarking all objects and moving mobile objects to their + correct positions, + restoring their savedFirstFields. */ + + /* SpurPlanningCompactor>>#copyAndUnmark: */ +static NoDbgRegParms NeverInline void +copyAndUnmark(sqInt firstPass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + sqInt finalPass; + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + + if (firstPass) { + /* begin unmarkInitialImmobileObjects */ + /* begin allOldSpaceObjectsFrom:do: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objOopSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objOopSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (oopisGreaterThanOrEqualTo(objOopSqInt, GIV(firstMobileObject))) { + goto l2; + } + + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + /* end unmarkInitialImmobileObjects */ +l2:; + } + finalPass = copyAndUnmarkMobileObjects(); + if ((GIV(lastMobileObject)) + && ((!finalPass) + && (GIV(biasForGC)))) { + unmarkObjectsFromFirstFreeObject(); + } +} + + /* SpurPlanningCompactor>>#endCompaction */ +static void +endCompaction(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt prevObj; + sqInt prevPrevObj; + + /* begin unmarkSurvivingObjectsForCompact */ + /* begin allPastSpaceObjectsDo: */ + /* begin allPastSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + address = ((GIV(pastSpace)).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + while (oopisLessThan(objOopSqInt, GIV(pastSpaceStart))) { + assert(isEnumerableObjectNoAssert(objOopSqInt)); + if ((byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOopSqInt))); + byteAtput((void *)(objOopSqInt + (markBitsByteOffset())),(byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + objOopSqInt = GIV(pastSpaceStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + + /* begin endSlidingCompaction */ + GIV(gcPhaseInProgress) = 0; + if (GIV(rememberedSetSize) > 0) { + objOop = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(0, objOop, GIV(firstFieldOfRememberedSet))); + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),GIV(firstFieldOfRememberedSet)); + } + setIsPinnedOfto(longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))), 1); + + /* begin relocateRememberedSet */ + GIV(rememberedSet) = firstIndexableField(longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord()))))))); + if (/* savedFirstFieldsSpaceWasAllocated */ + GIV(savedFirstFieldsSpaceNotInOldSpace) + && (oopisGreaterThan((GIV(savedFirstFieldsSpace).start), GIV(nilObj)))) { + sqDeallocateMemorySegmentAtOfSize(((void *)((GIV(savedFirstFieldsSpace).start))), ((GIV(savedFirstFieldsSpace).limit)) - ((GIV(savedFirstFieldsSpace).start))); + } +} + + +/* Free from toFinger up to limit, dealing with possible intervening pinned + objects. + */ + + /* SpurPlanningCompactor>>#freeFrom:upTo:nextObject: */ +static NoDbgRegParms void +freeFromupTonextObject(usqInt initialToFinger, usqInt limit, sqInt nextObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + sqInt obj; + usqInt objStart; + SpurSegmentInfo *seg; + usqInt toFinger; + + toFinger = initialToFinger; + objStart = /* startOfObject: */ + ((byteAt((void *)(nextObject + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? nextObject - BaseHeaderSize + : nextObject); + if (toFinger < objStart) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += objStart - toFinger; + freeChunkWithBytesat(objStart - toFinger, toFinger); + } + toFinger = objStart; + while (objStart < limit) { + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(objStart + (numSlotsFieldByteOffset()))); + obj = (numSlots == (numSlotsMask()) + ? objStart + BaseHeaderSize + : objStart); + if ((((byteAt((void *)(obj + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && (((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)) { + /* begin unmarkPinned: */ + if (((longAt((void *)(obj))) & (classIndexMask())) == (segmentBridgePun())) { + assert(isMarked(obj)); + } + else { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(obj))); + byteAtput((void *)(obj + (markBitsByteOffset())),(byteAt((void *)(obj + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + + /* begin notePinned: */ + assert(isPinned(obj)); + assert(!((isSegmentBridge(obj)))); + seg = segmentContainingObj(obj); + (seg->containsPinned = 1); + } + if (toFinger < objStart) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += objStart - toFinger; + freeChunkWithBytesat(objStart - toFinger, toFinger); + } + toFinger = (objStart = addressAfter(obj)); + } + else { + objStart = addressAfter(obj); + } + } + if (limit > toFinger) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += limit - toFinger; + freeChunkWithBytesat(limit - toFinger, toFinger); + } +} + + /* SpurPlanningCompactor>>#isMobile: */ +static NoDbgRegParms sqInt +isMobile(sqInt obj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (oopisGreaterThanOrEqualToandLessThanOrEqualTo(obj, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)); +} + + +/* For asserts */ + + /* SpurPlanningCompactor>>#isPostMobile: */ +static NoDbgRegParms sqInt +isPostMobile(sqInt obj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return oopisGreaterThanOrEqualToandLessThanOrEqualTo(obj, GIV(mobileStart), GIV(lastMobileObject)); +} + + +/* Sweep the heap from firstFreeObject forwarding marked objects to where + they can be moved to, saving their forwarding pointer in + savedFirstFieldsSpace. Continue until either the end of the heap is + reached or savedFirstFieldsSpace is full. + Answer if the end of the heap was reached (savedFirstFieldsSpace has not + overflowed). + The enumerations in planCompactSavingForwarders, + updatePointersInMobileObjects and copyAndUnmarkMobileObjects + match. We could implement them as a single enumeration method taking + several block arguments, but arguably that + would make understanding an already tricky algorithm more difficult. + Instead we tolerate the duplication and encourage + the reader to diff the three methods to see where they diverge (e.g. via + Cmd-shift-C). */ + + /* SpurPlanningCompactor>>#planCompactSavingForwarders */ +static NeverInline sqInt +planCompactSavingForwarders(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt availableSpace; + usqInt bytes; + sqInt eventualLocation; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + usqInt previousPin; + sqInt prevObj; + sqInt prevPrevObj; + usqInt startOfPreviousPin; + usqInt toFinger; + usqInt top; + + availableSpace = 0; + previousPin = 0; + assert(!((isMarked(GIV(firstFreeObject))))); + toFinger = /* startOfObject: */ + ((byteAt((void *)(GIV(firstFreeObject) + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? GIV(firstFreeObject) - BaseHeaderSize + : GIV(firstFreeObject)); + top = (GIV(savedFirstFieldsSpace).start); + startOfPreviousPin = 0; + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(firstFreeObject))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstFreeObject); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + assert((previousPin == null + ? toFinger <= (startOfObject(objOop)) + : (isMarked(previousPin)) + && (toFinger <= startOfPreviousPin))); + assert(GIV(savedFirstFieldsSpaceNotInOldSpace) + || (toFinger < top)); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + if (!previousPin) { + previousPin = objOop; + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + } + } + else { + bytes = bytesInBody(objOop); + while ((toFinger <= startOfPreviousPin) + && ((bytes != ((availableSpace = startOfPreviousPin - toFinger))) + && ((bytes + (16)) > availableSpace))) { + /* The object does not fit in the space between toFinger and previousPin. + Move toFinger up to point at the first unmarked or mobile object after + previousPin, or, if previousPin is contiguous with o, to the start of this + object. Update previousPin to be the next pinned object above toFInger + and below this object, or nil if no such pinned object exists. + Any unfillable gaps between adjacent pinned objects will be freed. */ + do { + toFinger = addressAfter(previousPin); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(toFinger + (numSlotsFieldByteOffset()))); + previousPin = (numSlots == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + } while((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && ((((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0) + && (previousPin < objOop))); + + /* Now previousPin is either equal to o or mobile. + Move it to the next pinned object below o */ + while (!((previousPin >= objOop) + || ((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && (((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + previousPin = oldSpaceObjectAfter(previousPin); + } + if (previousPin >= objOop) { + previousPin = null; + startOfPreviousPin = 0; + } + else { + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(previousPin + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? previousPin - BaseHeaderSize + : previousPin); + } + } + + /* begin forwardMobileObject:to:savedFirstFieldPtr: */ + GIV(lastMobileObject) = objOop; + eventualLocation = ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + longAtput((void *)(top),longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(0, objOop, eventualLocation)); + longAtput((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord()))),eventualLocation); + toFinger += bytes; + if (((top += BytesPerOop)) >= ((GIV(savedFirstFieldsSpace).limit))) { + (GIV(savedFirstFieldsSpace).top = top - BytesPerOop); + GIV(objectAfterLastMobileObject) = oldSpaceObjectAfter(GIV(lastMobileObject)); + return 0; + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* If the heap is already fully compacted there will be no lastMobileObject... */ + if (GIV(lastMobileObject)) { + (GIV(savedFirstFieldsSpace).top = top - BytesPerOop); + GIV(objectAfterLastMobileObject) = oldSpaceObjectAfter(GIV(lastMobileObject)); + } + return 1; +} + + +/* Search for firstFreeObject and firstMobileObject from initialObject, which + is the + hiddenRootsObject on the first pass, and the objectAfterLastMobileObject + on subsequent passes). */ + + /* SpurPlanningCompactor>>#reinitializeScanFrom: */ +static NoDbgRegParms void +reinitializeScanFrom(sqInt initialObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + + GIV(firstFreeObject) = scanForFirstFreeAndFirstMobileObjectFrom(initialObject); + if (GIV(firstFreeObject)) { + GIV(mobileStart) = /* startOfObject: */ + ((byteAt((void *)(GIV(firstFreeObject) + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? GIV(firstFreeObject) - BaseHeaderSize + : GIV(firstFreeObject)); + } + if (GIV(objectAfterLastMobileObject)) { + /* begin allOldSpaceEntitiesFrom:to:do: */ + assert((isNonImmediate(GIV(firstFreeObject))) + && (isInSegments(GIV(firstFreeObject)))); + assert((isNonImmediate(GIV(objectAfterLastMobileObject))) + && (isInSegments(GIV(objectAfterLastMobileObject)))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstFreeObject); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThanOrEqualTo(objOop, GIV(objectAfterLastMobileObject)))) break; + assert((long64At((void *)(objOop))) != 0); + if (!((((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0) + || ((!(((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0)) + || (GIV(objectAfterLastMobileObject) == objOop)))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + GIV(firstMobileObject) = GIV(objectAfterLastMobileObject); + } +} + + +/* Scavenge or simply follow objOop. Answer the new location of objOop. + The send should have been guarded by a send of shouldRemapOop:. + The method is called remapObj: for compatibility with ObjectMemory. */ + + /* SpurPlanningCompactor>>#remapObj: */ +sqInt +remapObj(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt resolvedObj; + + /* begin slidingCompactionRemapObj: */ + assert(shouldRemapOop(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + resolvedObj = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(resolvedObj & (tagMask())))) + && ((!((longAt((void *)(resolvedObj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + resolvedObj = longAt((void *)((resolvedObj + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + else { + assert(!((isInFutureSpace(objOop)))); + resolvedObj = objOop; + } + if (GIV(gcPhaseInProgress) > 0) { + if (GIV(gcPhaseInProgress) == ScavengeInProgress) { + if ((/* isReallyYoung: */ + ((!(resolvedObj & (tagMask())))) + && ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(resolvedObj)), + (oopisLessThan(resolvedObj, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(resolvedObj, GIV(newSpaceStart)))))) + && (!(oopisGreaterThanOrEqualToandLessThan(resolvedObj, ((GIV(futureSpace)).start), GIV(futureSurvivorStart))))) { + return copyAndForward(resolvedObj); + } + } + else { + assert(slidingCompactionInProgress()); + if (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(objOop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0))) { + return longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + } + return resolvedObj; +} + + /* SpurPlanningCompactor>>#savedFirstFieldsSpaceWasAllocated */ +static sqInt +savedFirstFieldsSpaceWasAllocated(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(savedFirstFieldsSpaceNotInOldSpace) + && (oopisGreaterThan((GIV(savedFirstFieldsSpace).start), GIV(nilObj))); +} + + +/* Scan from initialObject, setting firstMobileObject to the first marked + object after the first free object found, or endOfMemory if none is found. + Answer the first free object found, or nil if none. */ + + /* SpurPlanningCompactor>>#scanForFirstFreeAndFirstMobileObjectFrom: */ +static NoDbgRegParms sqInt +scanForFirstFreeAndFirstMobileObjectFrom(sqInt initialObject) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firstFree; + sqInt followingWord; + usqInt followingWordAddress; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + + firstFree = 0; + GIV(firstMobileObject) = GIV(endOfMemory); + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(initialObject)); + prevPrevObj = (prevObj = null); + objOop = initialObject; + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (firstFree) { + GIV(firstMobileObject) = objOop; + return firstFree; + } + } + else { + if (!firstFree) { + firstFree = objOop; + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + return firstFree; +} + + +/* Answer if the obj should be scavenged, or simply followed. Sent via the + compactor from shouldRemapObj:. We test for being already scavenged + because mapStackPages + via mapInterpreterOops may be applied twice in the context of a global GC + where a + scavenge, followed by a scan-mark-free, and final compaction passes may + result in + scavenged fields being visited twice. */ + + /* SpurPlanningCompactor>>#shouldRemapObj: */ +sqInt +shouldRemapObj(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return /* slidingCompactionShouldRemapObj: */ + ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) + || ((GIV(gcPhaseInProgress) > 0) + && ((GIV(gcPhaseInProgress) == ScavengeInProgress + ? ((/* begin isReallyYoungObject: */ + assert(isNonImmediate(objOop)), + (oopisLessThan(objOop, GIV(oldSpaceStart))) + && (oopisGreaterThanOrEqualTo(objOop, GIV(newSpaceStart))))) + && (!(oopisGreaterThanOrEqualToandLessThan(objOop, ((GIV(futureSpace)).start), GIV(futureSurvivorStart)))) + : /* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(objOop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0))))); +} + + +/* Sweep the final immobile heap, freeing and coalescing unmarked and free + objects, and unmarking all marked objects up to the end of memory. */ + + /* SpurPlanningCompactor>>#unmarkObjectsFromFirstFreeObject */ +static void +unmarkObjectsFromFirstFreeObject(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + sqInt freeBytes; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + SpurSegmentInfo *seg; + usqInt startOfFree; + + startOfFree = 0; + freeBytes = 0; + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(firstFreeObject))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstFreeObject); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (startOfFree) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += freeBytes; + freeChunkWithBytesat(freeBytes, startOfFree); + startOfFree = null; + freeBytes = 0; + } + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + /* begin unmarkPinned: */ + if (((longAt((void *)(objOop))) & (classIndexMask())) == (segmentBridgePun())) { + assert(isMarked(objOop)); + } + else { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + + /* begin notePinned: */ + assert(isPinned(objOop)); + assert(!((isSegmentBridge(objOop)))); + seg = segmentContainingObj(objOop); + (seg->containsPinned = 1); + } + } + else { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + else { + if (!startOfFree) { + startOfFree = /* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + } + freeBytes += bytesInBody(objOop); + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + if (startOfFree) { + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += freeBytes; + freeChunkWithBytesat(freeBytes, startOfFree); + } +} + + +/* Sweep the heap, updating all objects to their eventual locations. + Remember to update the savedFirstFields of pointer objects, as these have + been forwarded. */ + + /* SpurPlanningCompactor>>#updatePointers */ +static NeverInline void +updatePointers(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt availableSpace; + usqInt bytes; + sqInt classIndex; + sqInt contextSize; + sqInt fmt; + sqInt followingWord; + usqInt followingWordAddress; + sqInt fwd; + sqInt header; + sqInt headerSqInt; + sqInt heapEntity; + sqInt i; + usqInt numLiterals; + usqInt numPointerSlots; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objOop; + sqInt objOopSqInt; + sqInt onePass; + sqInt oop; + usqInt previousPin; + sqInt prevObj; + sqInt prevPrevObj; + sqInt sp; + usqInt startOfPreviousPin; + usqInt toFinger; + usqInt top; + + if (!(GIV(lastMobileObject))) { + return; + } + assert((startOfObject(GIV(firstFreeObject))) == GIV(mobileStart)); + mapInterpreterOops(); + mapExtraRoots(); + + /* begin updatePointersInManagerHeapEntities */ + /* begin relocateObjStacksForPlanningCompactor */ + GIV(markStack) = relocateObjStackForPlanningCompactorandContents(GIV(markStack), 0); + GIV(weaklingStack) = relocateObjStackForPlanningCompactorandContents(GIV(weaklingStack), 0); + GIV(mournQueue) = relocateObjStackForPlanningCompactorandContents(GIV(mournQueue), 1); + if ((GIV(rememberedSetSize) > 0) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(GIV(firstFieldOfRememberedSet), GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(GIV(firstFieldOfRememberedSet) + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + GIV(firstFieldOfRememberedSet) = longAt((void *)((GIV(firstFieldOfRememberedSet) + BaseHeaderSize) + (0U << (shiftForWord())))); + } + heapEntity = longAt((void *)((GIV(hiddenRootsObj) + BaseHeaderSize) + ((((usqInt)(RememberedSetRootIndex) << (shiftForWord())))))); + + /* begin relocateObjectsInHeapEntity:from:to: */ + for (i = 1; i < GIV(rememberedSetSize); i += 1) { + oop = longAt((void *)((heapEntity + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert(isMarked(oop)); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(heapEntity)) + && (!(isForwarded(heapEntity)))); + assert(validStorePointerUncheckedArgs(i, heapEntity, fwd)); + longAtput((void *)((heapEntity + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + if (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(heapEntity, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(heapEntity + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0))) { + /* fetchPointer:ofObject: */ + longAt((void *)((heapEntity + BaseHeaderSize) + (0U << (shiftForWord())))); + } + else { + } + + /* begin updatePointersInSurvivingObjects */ + /* begin allPastSpaceObjectsDo: */ + /* begin allPastSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + address = ((GIV(pastSpace)).start); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + objOopSqInt = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + while (oopisLessThan(objOopSqInt, GIV(pastSpaceStart))) { + assert(isEnumerableObjectNoAssert(objOopSqInt)); + + /* begin updatePointersIn: */ + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objOopSqInt + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l3; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOopSqInt))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l3: + numPointerSlots = CtxtTempFrameStart + contextSize; + goto l2; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOopSqInt)) > (isForwardedObjectClassIndexPun())); + numPointerSlots = (((numSlots = byteAt((void *)(objOopSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOopSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l2; + } + if (fmt == (forwardedFormat())) { + numPointerSlots = 1; + goto l2; + } + if (fmt < (firstCompiledMethodFormat())) { + numPointerSlots = 0; + goto l2; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOopSqInt)); + headerSqInt = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numPointerSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l2: + for (i = 0; i < numPointerSlots; i += 1) { + oop = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert((isMarked(oop)) + || (objOopSqInt == (hiddenRootsObject()))); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOopSqInt)) + && (!(isForwarded(objOopSqInt)))); + assert(validStorePointerUncheckedArgs(i, objOopSqInt, fwd)); + longAtput((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(pastSpaceStart))) { + objOopSqInt = GIV(pastSpaceStart); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOopSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1:; + } + + /* begin updatePointersInInitialImmobileObjects */ + /* begin allOldSpaceObjectsFrom:do: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(nilObj); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objOopSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objOopSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (oopisGreaterThanOrEqualTo(objOopSqInt, GIV(firstFreeObject))) { + goto l7; + } + + /* would like to assert this, but it isn't true if more than one pass: self assert: (manager isMarked: o). */ + + /* begin updatePointersIn: */ + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objOopSqInt + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l4; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOopSqInt))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l4: + numPointerSlots = CtxtTempFrameStart + contextSize; + goto l6; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOopSqInt)) > (isForwardedObjectClassIndexPun())); + numPointerSlots = (((numSlots = byteAt((void *)(objOopSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOopSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l6; + } + if (fmt == (forwardedFormat())) { + numPointerSlots = 1; + goto l6; + } + if (fmt < (firstCompiledMethodFormat())) { + numPointerSlots = 0; + goto l6; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOopSqInt)); + headerSqInt = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numPointerSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l6: + for (i = 0; i < numPointerSlots; i += 1) { + oop = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert((isMarked(oop)) + || (objOopSqInt == (hiddenRootsObject()))); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOopSqInt)) + && (!(isForwarded(objOopSqInt)))); + assert(validStorePointerUncheckedArgs(i, objOopSqInt, fwd)); + longAtput((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l5; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l5:; + } + /* end updatePointersInInitialImmobileObjects */ +l7: + + /* begin updatePointersInMobileObjects */ + availableSpace = 0; + previousPin = 0; + assert(!((isMarked(GIV(firstFreeObject))))); + toFinger = /* startOfObject: */ + ((byteAt((void *)(GIV(firstFreeObject) + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? GIV(firstFreeObject) - BaseHeaderSize + : GIV(firstFreeObject)); + top = (GIV(savedFirstFieldsSpace).start); + startOfPreviousPin = 0; + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(firstFreeObject))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstFreeObject); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + assert((previousPin == null + ? toFinger <= (startOfObject(objOop)) + : (isMarked(previousPin)) + && (toFinger <= startOfPreviousPin))); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if ((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + if (!previousPin) { + previousPin = objOop; + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? objOop - BaseHeaderSize + : objOop); + } + + /* begin updatePointersIn: */ + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l8; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l8: + numPointerSlots = CtxtTempFrameStart + contextSize; + goto l10; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOop)) > (isForwardedObjectClassIndexPun())); + numPointerSlots = (((numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + goto l10; + } + if (fmt == (forwardedFormat())) { + numPointerSlots = 1; + goto l10; + } + if (fmt < (firstCompiledMethodFormat())) { + numPointerSlots = 0; + goto l10; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOop)); + headerSqInt = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numPointerSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l10: + for (i = 0; i < numPointerSlots; i += 1) { + oop = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert((isMarked(oop)) + || (objOop == (hiddenRootsObject()))); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOop)) + && (!(isForwarded(objOop)))); + assert(validStorePointerUncheckedArgs(i, objOop, fwd)); + longAtput((void *)((objOop + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + } + else { + bytes = bytesInBody(objOop); + while ((toFinger <= startOfPreviousPin) + && ((bytes != ((availableSpace = startOfPreviousPin - toFinger))) + && ((bytes + (16)) > availableSpace))) { + /* The object does not fit in the space between toFinger and previousPin. + Move toFinger up to point at the first unmarked or mobile object after + previousPin, or, if previousPin is contiguous with o, to the start of this + object. Update previousPin to be the next pinned object above toFInger + and below this object, or nil if no such pinned object exists. + Any unfillable gaps between adjacent pinned objects will be freed. */ + do { + toFinger = addressAfter(previousPin); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(toFinger + (numSlotsFieldByteOffset()))); + previousPin = (numSlots == (numSlotsMask()) + ? toFinger + BaseHeaderSize + : toFinger); + } while((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && ((((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0) + && (previousPin < objOop))); + + /* Now previousPin is either equal to o or mobile. + Move it to the next pinned object below o */ + while (!((previousPin >= objOop) + || ((((byteAt((void *)(previousPin + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) != 0) + && (((byteAt((void *)(previousPin + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + previousPin = oldSpaceObjectAfter(previousPin); + } + if (previousPin >= objOop) { + previousPin = null; + startOfPreviousPin = 0; + } + else { + startOfPreviousPin = /* startOfObject: */ + ((byteAt((void *)(previousPin + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? previousPin - BaseHeaderSize + : previousPin); + } + } + updatePointersInsavedFirstFieldPointer(objOop, top); + toFinger += bytes; + if (((top += BytesPerOop)) >= ((GIV(savedFirstFieldsSpace).limit))) { + assert(((GIV(savedFirstFieldsSpace).top)) == (top - BytesPerOop)); + onePass = 0; + goto l11; + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l9; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l9:; + } + assert(((GIV(savedFirstFieldsSpace).top)) == (top - BytesPerOop)); + onePass = 1; + /* end updatePointersInMobileObjects */ +l11: + if (!onePass) { + /* begin updatePointersInObjectsOverflowingSavedFirstFieldsSpace */ + /* begin allOldSpaceObjectsFrom:do: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(objectAfterLastMobileObject))); + prevPrevObj = (prevObj = null); + objOopSqInt = GIV(objectAfterLastMobileObject); + while (1) { + assert((objOopSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objOopSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOopSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objOopSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objOopSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((byteAt((void *)(objOopSqInt + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + /* begin updatePointersIn: */ + /* begin numPointerSlotsOf: */ + fmt = (byteAt((void *)(objOopSqInt + (formatFieldByteOffset())))) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(objOopSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l12; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(objOopSqInt))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l12: + numPointerSlots = CtxtTempFrameStart + contextSize; + goto l14; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(objOopSqInt)) > (isForwardedObjectClassIndexPun())); + numPointerSlots = (((numSlots = byteAt((void *)(objOopSqInt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOopSqInt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l14; + } + if (fmt == (forwardedFormat())) { + numPointerSlots = 1; + goto l14; + } + if (fmt < (firstCompiledMethodFormat())) { + numPointerSlots = 0; + goto l14; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(objOopSqInt)); + headerSqInt = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numPointerSlots = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l14: + for (i = 0; i < numPointerSlots; i += 1) { + oop = longAt((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert((isMarked(oop)) + || (objOopSqInt == (hiddenRootsObject()))); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(objOopSqInt)) + && (!(isForwarded(objOopSqInt)))); + assert(validStorePointerUncheckedArgs(i, objOopSqInt, fwd)); + longAtput((void *)((objOopSqInt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } + } + } + prevPrevObj = prevObj; + prevObj = objOopSqInt; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOopSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOopSqInt = GIV(endOfMemory); + goto l13; + } + followingWord = longAt((void *)(followingWordAddress)); + objOopSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l13:; + } + } +} + + +/* Sweep the pointer fields in obj, updating all references to mobile objects + to their eventual locations. + firstFieldPtr is supplied for mobile objects so that the saved first field + can be updated, and so that + the first field of a compiled method (which is its header, or reference to + a CogMethod holding its header) + can be retrieved. */ + + /* SpurPlanningCompactor>>#updatePointersIn:savedFirstFieldPointer: */ +static NoDbgRegParms void +updatePointersInsavedFirstFieldPointer(sqInt obj, sqInt firstFieldPtr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt contextSize; + sqInt field; + sqInt fmt; + sqInt fwd; + sqInt header; + sqInt i; + usqInt numLiterals; + sqInt numPointerSlots; + usqInt numSlots; + sqInt oop; + sqInt sp; + + fmt = (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask()); + + /* begin numPointerSlotsWhileCompactingOf:withFormat:savedFirstFieldPointer: */ + assert((firstFieldPtr) + && (isMobile(obj))); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l2; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(obj))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l2: + numPointerSlots = ((usqInt) (CtxtTempFrameStart + contextSize)); + goto l3; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())); + numPointerSlots = (((numSlots = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l3; + } + assert(!((fmt == (forwardedFormat())))); + if (fmt < (firstCompiledMethodFormat())) { + numPointerSlots = 0; + goto l3; + } + field = longAt((void *)(firstFieldPtr)); + + /* begin methodHeaderFromSavedFirstField: */ + if ((((field) & 7) == 1)) { + header = field; + goto l1; + } + assert((isNonImmediate(field)) + && (field < GIV(newSpaceStart))); + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) field))->objectHeader)); + header = ((((CogMethod *) field))->methodHeader); + /* end methodHeaderFromSavedFirstField: */ +l1: + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + numPointerSlots = numLiterals + LiteralStart; + /* end numPointerSlotsWhileCompactingOf:withFormat:savedFirstFieldPointer: */ +l3: + if ((fmt <= 5 /* lastPointerFormat */) + && (numPointerSlots > 0)) { + oop = longAt((void *)(firstFieldPtr)); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert(isMarked(oop)); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + longAtput((void *)(firstFieldPtr),fwd); + } + } + + /* excludes CompiledMethod + Relocate the saved first field; Note that CompiledMethods can be excluded since their + first field is either a SmallInteger or a reference to a CogMethod outside of oldSpace. */ + for (i = 1; i < numPointerSlots; i += 1) { + oop = longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (((!(oop & (tagMask())))) + && (/* isMobile: */ + (oopisGreaterThanOrEqualToandLessThanOrEqualTo(oop, GIV(mobileStart), GIV(lastMobileObject))) + && (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) != 0)))) { + assert((isMarked(oop)) + || (obj == (hiddenRootsObject()))); + fwd = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + assert(isPostMobile(fwd)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(obj)) + && (!(isForwarded(obj)))); + assert(validStorePointerUncheckedArgs(i, obj, fwd)); + longAtput((void *)((obj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),fwd); + } + } +} + + +/* Attempt to allocate a memory segment large enough to hold the + savedFirstFieldsSpace. Invoked when neither eden nor a large free chunk + are found to be big enough for the job. */ + + /* SpurPlanningCompactor>>#useSegmentForSavedFirstFieldsSpace: */ +static NoDbgRegParms sqInt +useSegmentForSavedFirstFieldsSpace(sqInt spaceEstimate) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt allocatedSize; + sqInt roundedSize; + void *segAddress; + + allocatedSize = 0; + roundedSize = ((spaceEstimate + 0x3FF) / 0x400) * 0x400; + if ((segAddress = sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto(roundedSize, firstGapOfSizeAtLeast(roundedSize), (&allocatedSize)))) { + (GIV(savedFirstFieldsSpace).start = ((usqIntptr_t)segAddress)); + (GIV(savedFirstFieldsSpace).limit = (((usqIntptr_t)segAddress)) + allocatedSize); + GIV(savedFirstFieldsSpaceNotInOldSpace) = 1; + assert(savedFirstFieldsSpaceWasAllocated()); + return 1; + } + + /* sent to the manager so that the simulator can increase memory to simulate a new segment */ + return 0; +} + + +/* Answer 0 if all the mobile objects from firstMobileObject to + lastMobileObject have sane forwarding addresses, and that + savedFirstFieldsSpace is of + matching capacity. Otherwise answer an error code identifying the anomaly. */ + + /* SpurPlanningCompactor>>#validRelocationPlanInPass: */ +static NoDbgRegParms sqInt +validRelocationPlanInPass(sqInt onePass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt destination; + sqInt followingWord; + usqInt followingWordAddress; + sqInt nMobiles; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt toFinger; + + nMobiles = 0; + toFinger = GIV(mobileStart); + GIV(anomaly) = null; + + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(firstMobileObject))); + prevPrevObj = (prevObj = null); + objOop = GIV(firstMobileObject); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (!((byteAt((void *)(objOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift())))) { + nMobiles += 1; + destination = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + if (!(destination >= toFinger)) { + GIV(anomaly) = objOop; + return 1; + } + toFinger += bytesInBody(objOop); + if (oopisGreaterThan(objOop, GIV(lastMobileObject))) { + GIV(anomaly) = objOop; + return 2; + } + if (objOop == GIV(lastMobileObject)) { + return ((((((GIV(savedFirstFieldsSpace).top)) + BytesPerOop) - ((GIV(savedFirstFieldsSpace).start))) / BytesPerOop) == nMobiles + ? 0 + : 3); + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* N.B. written this way so that if there are no mobiles the expression evaluates to 0 in Smalltalk /and/ in C unsigned arithmetic. */ + return ((((((GIV(savedFirstFieldsSpace).top)) + BytesPerOop) - ((GIV(savedFirstFieldsSpace).start))) / BytesPerOop) == nMobiles + ? 0 + : 4); +} + + /* SpurSegmentInfo>>#segLimit */ +static NoDbgRegParms usqInt +segLimit(SpurSegmentInfo *self_in_SpurSegmentInfo) +{ + return ((self_in_SpurSegmentInfo->segSize)) + ((self_in_SpurSegmentInfo->segStart)); +} + + /* SpurSegmentManager>>#addSegmentOfSize: */ +static NoDbgRegParms SpurSegmentInfo * +addSegmentOfSize(sqInt ammount) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt allocatedSize; + sqInt i; + sqInt idx; + sqInt lastSegIndex; + SpurSegmentInfo *newSeg; + sqInt newSegIndex; + void *segAddress; + sqInt segAddressSqInt; + sqInt segIndex; + + newSegIndex = 0; + allocatedSize = 0; + if ((segAddress = sqAllocateMemorySegmentOfSizeAboveAllocatedSizeInto(ammount, firstGapOfSizeAtLeast(ammount), (&allocatedSize)))) { + segAddressSqInt = ((usqIntptr_t)segAddress); + + /* begin insertSegmentFor: */ + assert(segAddressSqInt >= (segLimit(&GIV(segments)[0]))); + if (GIV(numSegments) == GIV(numSegInfos)) { + allocateOrExtendSegmentInfos(); + } + assert(GIV(numSegments) < GIV(numSegInfos)); + segIndex = (lastSegIndex = GIV(numSegments) - 1); + GIV(numSegments) += 1; + while (1) { + if (segAddressSqInt >= ((((GIV(segments)[segIndex]).segSize)) + (((GIV(segments)[segIndex]).segStart)))) { + segIndex += 1; + for (idx = lastSegIndex; idx >= segIndex; idx += -1) { + GIV(segments)[idx + 1] = (GIV(segments)[idx]); + } + newSegIndex = segIndex; + goto l1; + } + segIndex -= 1; + } + /* end insertSegmentFor: */ +l1: + + /* Simulation insertion code duplicates entries if newSegIndex ~= numSegments - 1 */ + newSeg = (&(GIV(segments)[newSegIndex])); + (newSeg->segStart = ((usqIntptr_t)segAddress)); + (newSeg->segSize = allocatedSize); + (newSeg->swizzle = 0); + assert(!(segmentOverlap())); + bridgeFromto((&(GIV(segments)[newSegIndex - 1])), newSeg); + bridgeFromto(newSeg, (!(newSegIndex == (GIV(numSegments) - 1)) + ? (&(GIV(segments)[newSegIndex + 1])) + : 0)); + GIV(totalHeapSizeIncludingBridges) += allocatedSize; + + /* test isInMemory: */ + for (i = 0; i < GIV(numSegments); i += 1) { + assert(isInSegments(((GIV(segments)[i]).segStart))); + assert(isInSegments((segLimit(&GIV(segments)[i])) - BytesPerWord)); + assert((!(isInSegments(segLimit(&GIV(segments)[i])))) + || ((i < (GIV(numSegments) - 1)) + && ((segLimit(&GIV(segments)[i])) == (((GIV(segments)[i + 1]).segStart))))); + assert((!(isInSegments((((GIV(segments)[i]).segStart)) - BytesPerWord))) + || ((i > 0) + && ((segLimit(&GIV(segments)[i - 1])) == (((GIV(segments)[i]).segStart))))); + } + return newSeg; + } + + /* sent to the manager so that the simulator can increase memory to simulate a new segment */ + return null; +} + + +/* Adjust swizzles by firstSegmentShift. Also computes segStarts as + they were in the image when it was written, so that oops' segments + can be determined and hence oops correctly swizzled. */ + + /* SpurSegmentManager>>#adjustSegmentSwizzlesBy: */ +static NoDbgRegParms void +adjustSegmentSwizzlesBy(sqInt firstSegmentShift) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt oldBaseAddr; + SpurSegmentInfo *segInfo; + + oldBaseAddr = GIV(oldSpaceStart) - firstSegmentShift; + for (i = 0; i < GIV(numSegments); i += 1) { + segInfo = (&(GIV(segments)[i])); + (segInfo->segStart = ((segInfo->segStart)) + oldBaseAddr); + (segInfo->swizzle = ((segInfo->swizzle)) - oldBaseAddr); + } + GIV(canSwizzle) = 1; +} + + /* SpurSegmentManager>>#allBridgesMarked */ +static sqInt +allBridgesMarked(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt bridgeObj; + sqInt i; + usqInt numSlots; + + for (i = 0; i < GIV(numSegments); i += 1) { + /* begin bridgeAt: */ + /* begin bridgeFor: */ + address = (((((&(GIV(segments)[i])))->segSize)) + ((((&(GIV(segments)[i])))->segStart))) - (2 * BaseHeaderSize); + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + bridgeObj = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + assert(isValidSegmentBridge(bridgeObj)); + if (!((byteAt((void *)(bridgeObj + (markBitsByteOffset())))) & (1U << (markedBitByteShift())))) { + return 0; + } + } + return 1; +} + + +/* Increase the number of allocated segInfos by 16. */ + + /* SpurSegmentManager>>#allocateOrExtendSegmentInfos */ +static void +allocateOrExtendSegmentInfos(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt newNumSegs; + + if (!GIV(numSegInfos)) { + GIV(numSegInfos) = 16; + GIV(segments) = calloc(GIV(numSegInfos), sizeof(SpurSegmentInfo)); + return; + } + newNumSegs = GIV(numSegInfos) + 16; + GIV(segments) = realloc(GIV(segments), newNumSegs * (sizeof(SpurSegmentInfo))); + if (!GIV(segments)) { + error("out of memory; cannot allocate more segments"); + } + memset(GIV(segments) + GIV(numSegInfos), 0, (newNumSegs - GIV(numSegInfos)) * (sizeof(SpurSegmentInfo))); + GIV(numSegInfos) = newNumSegs; +} + + /* SpurSegmentManager>>#bridgeAt: */ +static NoDbgRegParms sqInt +bridgeAt(sqInt segIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + usqInt numSlots; + + /* begin bridgeFor: */ + address = (((((&(GIV(segments)[segIndex])))->segSize)) + ((((&(GIV(segments)[segIndex])))->segStart))) - (2 * BaseHeaderSize); + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + return (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); +} + + /* SpurSegmentManager>>#bridgeFor: */ +static NoDbgRegParms sqInt +bridgeFor(SpurSegmentInfo *aSegment) +{ + sqInt address; + usqInt numSlots; + + address = (((aSegment->segSize)) + ((aSegment->segStart))) - (2 * BaseHeaderSize); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + return (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); +} + + +/* Create a bridge from aSegment to the next segment, + or create a terminating bridge if there is no next segment. */ + + /* SpurSegmentManager>>#bridgeFrom:to: */ +static NoDbgRegParms void +bridgeFromto(SpurSegmentInfo *aSegment, SpurSegmentInfo *nextSegmentOrNil) +{ + sqInt bridgeSpan; + sqInt clifton; + usqInt segEnd; + + segEnd = ((aSegment->segSize)) + ((aSegment->segStart)); + + /* clifton is where the Avon bridge begins... */ + clifton = segEnd - (2 * BaseHeaderSize); + bridgeSpan = (nextSegmentOrNil + ? (((nextSegmentOrNil->segStart)) - segEnd) + (2 * BaseHeaderSize) + : 2 * BaseHeaderSize); + assert(bridgeSpan >= 0); + initSegmentBridgeWithBytesat(bridgeSpan, clifton); + + /* the revised bridge should get us to the new segment */ + assert((addressAfter(objectStartingAt(clifton))) == ((nextSegmentOrNil == null + ? segLimit(aSegment) + : (nextSegmentOrNil->segStart)))); +} + + /* SpurSegmentManager>>#checkSegments */ +static void +checkSegments(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + assert(GIV(numSegments) >= 1); + for (i = 0; i < GIV(numSegments); i += 1) { + assert(addressCouldBeObj(((GIV(segments)[i]).segStart))); + assert(isValidSegmentBridge(bridgeAt(i))); + } + assert(((segLimit(&GIV(segments)[GIV(numSegments) - 1])) - (bridgeSize())) == (GIV(endOfMemory))); +} + + +/* The image has been loaded, old segments reconstructed, and the heap + swizzled into a single contiguous segment. Collapse the segments into one. */ + + /* SpurSegmentManager>>#collapseSegmentsPostSwizzle */ +static void +collapseSegmentsPostSwizzle(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + SpurSegmentInfo *cascade0; + + GIV(canSwizzle) = 0; + GIV(numSegments) = 1; + + /* begin computeTotalHeapSizeIncludingBridges */ + GIV(totalHeapSizeIncludingBridges) = GIV(endOfMemory) - GIV(oldSpaceStart); + cascade0 = (&(GIV(segments)[0])); + (cascade0->segStart = GIV(oldSpaceStart)); + (cascade0->segSize = GIV(totalHeapSizeIncludingBridges)); + assert(isSegmentBridge(bridgeAt(0))); + assert((numSlotsOfAny(bridgeAt(0))) == 0); +} + + +/* Answer the segment limit of the first segment followed by a gap of at + least size bytes. + */ + + /* SpurSegmentManager>>#firstGapOfSizeAtLeast: */ +static NoDbgRegParms void * +firstGapOfSizeAtLeast(sqInt size) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt bridge; + sqInt i; + usqInt numSlots; + + for (i = 0; i <= (GIV(numSegments) - 2); i += 1) { + /* begin bridgeAt: */ + /* begin bridgeFor: */ + address = (((((&(GIV(segments)[i])))->segSize)) + ((((&(GIV(segments)[i])))->segStart))) - (2 * BaseHeaderSize); + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + bridge = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + if (((bytesInBody(bridge)) - (2 * BaseHeaderSize)) >= size) { + return ((void *)((((GIV(segments)[i]).segSize)) + (((GIV(segments)[i]).segStart)))); + } + } + return ((void *)((((GIV(segments)[GIV(numSegments) - 1]).segSize)) + (((GIV(segments)[GIV(numSegments) - 1]).segStart)))); +} + + /* SpurSegmentManager>>#isEmptySegment: */ +static NoDbgRegParms sqInt +isEmptySegment(SpurSegmentInfo *seg) +{ + sqInt address; + sqInt firstObj; + usqInt numSlots; + + address = (seg->segStart); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + firstObj = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + return (((longAt((void *)(firstObj))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) + && ((addressAfter(firstObj)) == ((((seg->segSize)) + ((seg->segStart))) - (2 * BaseHeaderSize))); +} + + /* SpurSegmentManager>>#isInSegments: */ +static NoDbgRegParms sqInt +isInSegments(usqInt address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + for (i = 0; i < GIV(numSegments); i += 1) { + if (address < (((GIV(segments)[i]).segStart))) { + return 0; + } + if (address < ((((GIV(segments)[i]).segSize)) + (((GIV(segments)[i]).segStart)))) { + return 1; + } + } + return 0; +} + + +/* bridges bridge the gaps between segments. They are the last object in each + segment. + */ + + /* SpurSegmentManager>>#isValidSegmentBridge: */ +static NoDbgRegParms sqInt +isValidSegmentBridge(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((addressCouldBeObj(objOop)) + || (objOop == GIV(endOfMemory))) + && ((((longAt((void *)(objOop))) & (classIndexMask())) == (segmentBridgePun())) + && (((byteAt((void *)(objOop + (numSlotsFieldByteOffset())))) == (numSlotsMask())) + || ((numSlotsOfAny(objOop)) == 0))); +} + + +/* Answer the the next non-empty segment or nil. The size of a segment + includes that of its bridge. A segment containing just a free object and a + bridge will still + have a size of manager bridgeSize after shortening it in + prepareForSnapshot. + */ + + /* SpurSegmentManager>>#nextNonEmptySegmentAfter: */ +static NoDbgRegParms SpurSegmentInfo * +nextNonEmptySegmentAfter(sqInt i) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt nextx; + + nextx = i; + while (1) { + if (((nextx += 1)) >= GIV(numSegments)) { + return null; + } + if ((((GIV(segments)[nextx]).segSize)) > (2 * BaseHeaderSize)) { + return (&(GIV(segments)[nextx])); + } + } + return 0; +} + + +/* shorten all segments by any trailing free space. */ + + /* SpurSegmentManager>>#prepareForSnapshot */ +static NeverInline void +prepareForSnapshot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cameFrom; + SpurSegmentInfo *cascade0; + usqInt chunkBytes; + sqInt followingWord; + usqInt followingWordAddress; + usqInt freeChunk; + sqInt i; + sqInt largeChild; + sqInt newEndOfMemory; + sqInt next; + usqInt node; + SpurSegmentInfo *seg; + SpurSegmentInfo *selfOfSegSize; + sqInt smallChild; + sqInt treeNode; + + checkSegments(); + for (i = 0; i < GIV(numSegments); i += 1) { + cascade0 = (&(GIV(segments)[i])); + (cascade0->savedSegSize = ((GIV(segments)[i]).segSize)); + (cascade0->lastFreeObject = null); + } + + /* Ideally finding the lastFreeObject of each segment would be + done in some linear pass through the heap. But for now KISS. */ + + /* begin freeTreeNodesDo: */ + treeNode = GIV(freeLists)[0]; + if (!treeNode) { + goto l2; + } + cameFrom = -1; + do { + assert((bytesInBody(treeNode)) >= ((numFreeLists()) * (allocationUnit()))); + smallChild = longAt((void *)((treeNode + BaseHeaderSize) + (3U << (shiftForWord())))); + largeChild = longAt((void *)((treeNode + BaseHeaderSize) + (4U << (shiftForWord())))); + assert((smallChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), smallChild)))); + assert((largeChild == 0) + || (treeNode == (fetchPointerofFreeChunk(freeChunkParentIndex(), largeChild)))); + + /* apply if the node has no children, or it has no large children and we're + returning from the small child, or we're returning from the large child. */ + if (((smallChild == 0) + && (largeChild == 0)) + || ((largeChild + ? cameFrom == largeChild + : cameFrom == smallChild))) { + node = treeNode; + while (node != 0) { + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(node); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + next = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + next = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1: + if (((longAt((void *)(next))) & (classIndexMask())) == (segmentBridgePun())) { + seg = segmentContainingObj(node); + (seg->lastFreeObject = node); + node = 0; + } + else { + node = longAt((void *)((node + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + + /* and since we've applied we must move on up */ + cameFrom = treeNode; + treeNode = longAt((void *)((treeNode + BaseHeaderSize) + (2U << (shiftForWord())))); + } + else { + if ((smallChild != 0) + && (cameFrom != smallChild)) { + treeNode = smallChild; + } + else { + assert(largeChild != 0); + treeNode = largeChild; + } + cameFrom = -1; + } + } while(treeNode != 0); + /* end freeTreeNodesDo: */ +l2: + for (i = 0; i < GIV(numSegments); i += 1) { + if ((freeChunk = ((GIV(segments)[i]).lastFreeObject))) { + /* begin detachFreeObject: */ + chunkBytes = bytesInBody(freeChunk); + GIV(totalFreeOldSpace) -= chunkBytes; + unlinkFreeChunkchunkBytes(freeChunk, chunkBytes); + selfOfSegSize = (&(GIV(segments)[i])); + (selfOfSegSize->segSize = ((/* startOfObject: */ + ((byteAt((void *)(freeChunk + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? freeChunk - BaseHeaderSize + : freeChunk)) + (2 * BaseHeaderSize)) - (((GIV(segments)[i]).segStart))); + bridgeFromto((&(GIV(segments)[i])), (i < (GIV(numSegments) - 1) + ? (&(GIV(segments)[i + 1])) + : 0)); + } + } + + /* perhaps this should read + manager setEndOfMemory: 0; assimilateNewSegment: (segments at: numSegments - 1) */ + newEndOfMemory = ((((GIV(segments)[GIV(numSegments) - 1]).segSize)) + (((GIV(segments)[GIV(numSegments) - 1]).segStart))) - (2 * BaseHeaderSize); + + /* begin setEndOfMemory: */ + GIV(endOfMemory) = newEndOfMemory; + if (GIV(freeOldSpaceStart) > newEndOfMemory) { + GIV(freeOldSpaceStart) = newEndOfMemory; + } +} + + +/* Read numBytes of image data from f into memory at memoryBaseForImageRead. + Answer the number of bytes written. In addition, read each segment, build + up the + segment info for swizzling, while eliminating the bridge objects at the + end of each + segment that specify the distance to and the size of the subsequent + segment. */ + + /* SpurSegmentManager>>#readHeapFromImageFile:dataBytes: */ +static NoDbgRegParms sqInt +readHeapFromImageFiledataBytes(sqImageFile f, sqInt numBytes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bridge; + sqInt bridgehead; + usqInt bridgeSpan; + sqInt bytesRead; + usqInt newBase; + usqInt nextSegmentSize; + sqInt oldBase; + SpurSegmentInfo *segInfo; + sqInt totalBytesRead; + + allocateOrExtendSegmentInfos(); + + /* segment sizes include the two-header-word bridge at the end of each segment. */ + GIV(numSegments) = (totalBytesRead = 0); + + /* N.B. still must be adjusted by oldBaseAddr. */ + oldBase = 0; + newBase = GIV(oldSpaceStart); + nextSegmentSize = GIV(firstSegmentSize); + bridgehead = (GIV(firstSegmentSize) + GIV(oldSpaceStart)) - (2 * BaseHeaderSize); + while (1) { + segInfo = (&(GIV(segments)[GIV(numSegments)])); + (segInfo->segStart = oldBase); + (segInfo->segSize = nextSegmentSize); + (segInfo->swizzle = newBase - oldBase); + bytesRead = sqImageFileRead(pointerForOop(newBase), sizeof(char), nextSegmentSize, f); + if (bytesRead > 0) { + totalBytesRead += bytesRead; + } + if (bytesRead != nextSegmentSize) { + return totalBytesRead; + } + if (((GIV(numSegments) += 1)) >= GIV(numSegInfos)) { + allocateOrExtendSegmentInfos(); + } + bridge = bridgehead + BaseHeaderSize; + bridgeSpan = (byteAt((void *)(bridgehead + (numSlotsFieldByteOffset()))) + ? BytesPerOop * (((((usqInt)(((sqInt)((usqInt)((longAt((void *)(bridge - BaseHeaderSize)))) << 8)))))) >> 8) + : 0); + oldBase = (oldBase + nextSegmentSize) + bridgeSpan; + newBase = (newBase + nextSegmentSize) - (2 * BaseHeaderSize); + nextSegmentSize = ((usqInt)(long64At((void *)(bridge)))); + if (!(nextSegmentSize != 0)) break; + bridgehead = (bridgehead - (2 * BaseHeaderSize)) + nextSegmentSize; + } + + /* newBase should point just past the last bridge. all others should have been eliminated. */ + assert((newBase - (GIV(oldSpaceStart))) == (totalBytesRead - (GIV(numSegments) * (bridgeSize())))); + + /* set freeOldSpaceStart now for adjustAllOopsBy: */ + + /* begin setFreeOldSpaceStart: */ + GIV(freeOldSpaceStart) = newBase; + + /* we're done. nil firstSegmentSize for a subsequent snapshot. */ + GIV(firstSegmentSize) = null; + return totalBytesRead; +} + + +/* Restore all shortened segments to their proper size, re-freeing the + trailing space. + */ + + /* SpurSegmentManager>>#restorePostSnapshot */ +static void +restorePostSnapshot(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt bytes; + usqInt freeChunk; + sqInt i; + sqInt newEndOfMemory; + SpurSegmentInfo *seg; + + + /* Set endOfMemory first, to avoid assert fails in freeChunkWithBytes:at:. */ + seg = (&(GIV(segments)[GIV(numSegments) - 1])); + if ((seg->lastFreeObject)) { + newEndOfMemory = (((seg->savedSegSize)) + ((seg->segStart))) - (2 * BaseHeaderSize); + + /* begin setEndOfMemory: */ + GIV(endOfMemory) = newEndOfMemory; + if (GIV(freeOldSpaceStart) > newEndOfMemory) { + GIV(freeOldSpaceStart) = newEndOfMemory; + } + } + else { + assert((GIV(endOfMemory)) == ((segLimit(seg)) - (bridgeSize()))); + } + for (i = (GIV(numSegments) - 1); i >= 0; i += -1) { + seg = (&(GIV(segments)[i])); + if ((freeChunk = (seg->lastFreeObject))) { + address = (((seg->segSize)) + ((seg->segStart))) - (2 * BaseHeaderSize); + (seg->segSize = (seg->savedSegSize)); + bridgeFromto(seg, (i < (GIV(numSegments) - 1) + ? (&(GIV(segments)[i + 1])) + : 0)); + bytes = ((((seg->segSize)) + ((seg->segStart))) - address) - (2 * BaseHeaderSize); + + /* begin addFreeChunkWithBytes:at: */ + GIV(totalFreeOldSpace) += bytes; + freeChunkWithBytesat(bytes, address); + } + } + checkSegments(); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } +} + + +/* Answer the segment containing an object. This is mostly for assert + checking, but + variations on the incremental GC may use it in anger. Binary search is (of + course) marginally slower than linear search for a single segment (e.g. in + a 720k object heap, + 67.1ms vs 61.3ms, or 9.5% slower to derive the segment containing every + old space + entity), but usefully faster for many segments (e.g. 92.7ms vs 116ms, or + 20% faster + in the same heap extended with enough large arrays to require 11 segments; + and this + is pessimal; there are fewer objects at high addresses since the large + arrays are there). */ + + /* SpurSegmentManager>>#segmentContainingObj: */ +SpurSegmentInfo * +segmentContainingObj(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt high; + sqInt low; + sqInt mid; + SpurSegmentInfo *seg; + + low = 0; + mid = GIV(numSegments) / 2; + high = GIV(numSegments) - 1; + do { + seg = (&(GIV(segments)[mid])); + if (oopisGreaterThanOrEqualTo(objOop, (seg->segStart))) { + if (mid == high) { + return (oopisLessThan(objOop, ((seg->segSize)) + ((seg->segStart))) + ? seg + : 0); + } + else { + low = mid; + mid = ((mid + high) + 1) / 2; + } + } + else { + high = mid - 1; + mid = (low + mid) / 2; + } + } while(low <= high); + return null; +} + + +/* Answers true if a segment overlaps with another one. */ + + /* SpurSegmentManager>>#segmentOverlap */ +static sqInt +segmentOverlap(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt endi; + usqInt endj; + sqInt i; + sqInt j; + usqInt starti; + usqInt startj; + + for (i = 0; i < GIV(numSegments); i += 1) { + starti = ((GIV(segments)[i]).segStart); + endi = (((GIV(segments)[i]).segSize)) + (((GIV(segments)[i]).segStart)); + for (j = 0; j < GIV(numSegments); j += 1) { + startj = ((GIV(segments)[j]).segStart); + endj = (((GIV(segments)[j]).segSize)) + (((GIV(segments)[j]).segStart)); + if (!(i == j)) { + if (!((starti < startj) + || (starti >= endj))) { + return 1; + } + if (!((endi <= startj) + || (endi >= endj))) { + return 1; + } + } + } + } + return 0; +} + + +/* Answer if any shrinkage was achieved. */ + + /* SpurSegmentManager>>#shrinkObjectMemory: */ +static NoDbgRegParms int +shrinkObjectMemory(usqInt delta) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + usqInt chunkBytes; + usqInt currentEnd; + sqInt deltaSqInt; + SpurSegmentInfo *emptySeg; + sqInt freeChunk; + sqInt i; + sqInt iSqInt; + sqInt j; + sqInt k; + usqInt numSlots; + SpurSegmentInfo *seg; + SpurSegmentInfo *segInfo; + usqInt shrinkage; + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCCheckFreeSpace)) == (GCCheckFreeSpace | GCCheckFreeSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + shrinkage = delta; + while (1) { + /* begin findEmptySegNearestInSizeTo: */ + emptySeg = null; + deltaSqInt = shrinkage; + for (k = 0; k < GIV(numSegments); k += 1) { + seg = (&(GIV(segments)[k])); + if (isEmptySegment(seg)) { + if (emptySeg) { + if ((shrinkage >= (((seg->segSize)) * 0.75)) + && ((SQABS(((sqInt) (((seg->segSize)) - shrinkage)))) < deltaSqInt)) { + emptySeg = seg; + deltaSqInt = SQABS(((sqInt) (((seg->segSize)) - shrinkage))); + } + } + else { + emptySeg = seg; + } + } + } + if ((!emptySeg) + || (((emptySeg->segSize)) > shrinkage)) { + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCCheckFreeSpace)) == (GCCheckFreeSpace | GCCheckFreeSpace))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + return shrinkage < delta; + } + shrinkage -= (emptySeg->segSize); + address = (emptySeg->segStart); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + freeChunk = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin detachFreeObject: */ + chunkBytes = bytesInBody(freeChunk); + GIV(totalFreeOldSpace) -= chunkBytes; + unlinkFreeChunkchunkBytes(freeChunk, chunkBytes); + + /* begin removeSegment: */ + i = 0; + for (iSqInt = 0; iSqInt < GIV(numSegments); iSqInt += 1) { + if (((emptySeg->segStart)) == (((GIV(segments)[iSqInt]).segStart))) { + i = iSqInt; + goto l1; + } + } + error("segment not found"); + /* end indexOfSegment: */ +l1: + assert(i > 0); + GIV(totalHeapSizeIncludingBridges) -= (emptySeg->segSize); + sqDeallocateMemorySegmentAtOfSize(((void *)((emptySeg->segStart))), (emptySeg->segSize)); + for (j = i; j <= (GIV(numSegments) - 2); j += 1) { + GIV(segments)[j] = (GIV(segments)[j + 1]); + } + GIV(numSegments) -= 1; + bridgeFromto((&(GIV(segments)[i - 1])), (i <= (GIV(numSegments) - 1) + ? (&(GIV(segments)[i])) + : 0)); + segInfo = (&(GIV(segments)[GIV(numSegments) - 1])); + + /* begin setLastSegment: */ + currentEnd = (((segInfo->segSize)) + ((segInfo->segStart))) - (2 * BaseHeaderSize); + if (currentEnd <= GIV(endOfMemory)) { + GIV(endOfMemory) = currentEnd; + if (GIV(freeOldSpaceStart) > currentEnd) { + GIV(freeOldSpaceStart) = currentEnd; + } + } + } + return 0; +} + + /* SpurSegmentManager>>#swizzleObj: */ +static NoDbgRegParms sqInt +swizzleObj(sqInt objOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + assert(GIV(canSwizzle)); + for (i = (GIV(numSegments) - 1); i >= 1; i += -1) { + if (objOop >= (((GIV(segments)[i]).segStart))) { + return objOop + (((GIV(segments)[i]).swizzle)); + } + } + return objOop + (((GIV(segments)[0]).swizzle)); +} + + +/* This ``slow'' count is for asserts only. */ + + /* SpurSegmentManager>>#totalBytesInSegments */ +static usqInt +totalBytesInSegments(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + usqInt total; + + total = 0; + for (i = 0; i < GIV(numSegments); i += 1) { + total += ((GIV(segments)[i]).segSize); + } + return total; +} + + /* SpurSegmentManager>>#writeImageSegmentsToFile: */ +static NoDbgRegParms sqInt +writeImageSegmentsToFile(sqImageFile aBinaryStream) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt total; + + assert(((GIV(endOfMemory)) == (segLimit(&GIV(segments)[GIV(numSegments) - 1]))) + || (((GIV(endOfMemory)) + (bridgeSize())) == (segLimit(&GIV(segments)[GIV(numSegments) - 1])))); + if (GIV(firstSegmentSize)) { + assert(GIV(firstSegmentSize) == (((GIV(segments)[0]).segSize))); + } + assert((((GIV(segments)[0]).segSize)) > 0); + total = 0; + for (i = 0; i < GIV(numSegments); i += 1) { + if ((((GIV(segments)[i]).segSize)) > (2 * BaseHeaderSize)) { + total += writeSegmentnextSegmenttoFile((&(GIV(segments)[i])), nextNonEmptySegmentAfter(i), aBinaryStream); + } + } + return total; +} + + +/* Write the segment contents, the size of and the distance to the next + segment to aBinaryStream. + */ + + /* SpurSegmentManager>>#writeSegment:nextSegment:toFile: */ +static NoDbgRegParms sqInt +writeSegmentnextSegmenttoFile(SpurSegmentInfo *segment, SpurSegmentInfo *nextSegment, sqImageFile aBinaryStream) +{ + usqLong firstSavedBridgeWord; + sqInt nWritten; + sqInt pier1; + sqInt pier2; + usqLong secondSavedBridgeWord; + + pier1 = (((segment->segSize)) + ((segment->segStart))) - (2 * BaseHeaderSize); + pier2 = pier1 + BaseHeaderSize; + assert(isValidSegmentBridge(bridgeFor(segment))); + assert((startOfObject(bridgeFor(segment))) == pier1); + + /* Temporarily change the bridge to bridge to the next non-empty segment. + The first double word of the bridge includes the bridge size in slots, and + hence specifies the distance to the next segment. The following double + word is replaced by the size of the next segment, or 0 if there isn't one. */ + firstSavedBridgeWord = long64At((void *)(pier1)); + secondSavedBridgeWord = long64At((void *)(pier2)); + bridgeFromto(segment, nextSegment); + long64Atput((void *)(pier2),(nextSegment + ? (nextSegment->segSize) + : 0)); + nWritten = sqImageFileWrite(((void *)((segment->segStart))), 1, (segment->segSize), aBinaryStream); + long64Atput((void *)(pier1),firstSavedBridgeWord); + long64Atput((void *)(pier2),secondSavedBridgeWord); + return nWritten; +} + + /* StackInterpreter>>#accessorDepthForExternalPrimitiveMethod: */ +static NoDbgRegParms sqInt +accessorDepthForExternalPrimitiveMethod(sqInt methodObj) +{ + sqInt flags; + sqInt lit; + + assert(isLinkedExternalPrimitive(methodObj)); + lit = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + flags = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFlagsIndex) << (shiftForWord())))))); + return (((flags >> 3))) >> SpurPrimitiveAccessorDepthShift; +} + + /* StackInterpreter>>#accessorDepthForPrimitiveMethod: */ +sqInt +accessorDepthForPrimitiveMethod(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + sqInt primIndex; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (aMethodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIndex = 0; + } + return ((primIndex == PrimNumberExternalCall) + && (isLinkedExternalPrimitive(aMethodObj)) + ? accessorDepthForExternalPrimitiveMethod(aMethodObj) + : ((sqInt)((primitiveMetadataTable[primIndex]))) >> SpurPrimitiveAccessorDepthShift); +} + + +/* Answer the current activeProcess. */ +/* Now used for FFI error reaping */ + + /* StackInterpreter>>#activeProcess */ +sqInt +activeProcess(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + return longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); +} + + +/* The various poll/select calls in the VM should attempt to tally the + ammount of time spent at idle here, so as to render the uptime value + meaningful. + */ + + /* StackInterpreter>>#addIdleUsecs: */ +void +addIdleUsecs(sqInt idleUsecs) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(statIdleUsecs) += idleUsecs; +} + + +/* Add the given process to the end of the given linked list + and set the backpointer of process to its new list. */ + + /* StackInterpreter>>#addLastLink:toList: */ +static NoDbgRegParms void +addLastLinktoList(sqInt proc, sqInt aList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt lastLink; + + assert(!((isForwarded(proc)))); + assert(!((isForwarded(aList)))); + assert((fetchPointerofObject(NextLinkIndex, proc)) == (nilObject())); + + /* begin isEmptyList: */ + assert(!(isForwarded(aList))); + if ((longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(FirstLinkIndex, aList, proc)); + assert(isNonImmediate(aList)); + if (oopisGreaterThanOrEqualTo(aList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(proc & (tagMask())))) + && (oopisLessThan(proc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))),proc); + } + else { + lastLink = longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord())))))); + assert(lastLink != proc); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(NextLinkIndex, lastLink, proc)); + assert(isNonImmediate(lastLink)); + if (oopisGreaterThanOrEqualTo(lastLink, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(proc & (tagMask())))) + && (oopisLessThan(proc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(lastLink + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(lastLink); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((lastLink + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord()))))),proc); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(LastLinkIndex, aList, proc)); + assert(isNonImmediate(aList)); + if (oopisGreaterThanOrEqualTo(aList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(proc & (tagMask())))) + && (oopisLessThan(proc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord()))))),proc); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(MyListIndex, proc, aList)); + assert(isNonImmediate(proc)); + if (oopisGreaterThanOrEqualTo(proc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(aList & (tagMask())))) + && (oopisLessThan(aList, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(proc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(proc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((proc + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),aList); +} + + /* StackInterpreter>>#allOnesAsCharStar */ +static char * +allOnesAsCharStar(void) +{ + return ((char *) (((usqInt) -1))); +} + + +/* for Cogit */ + + /* StackInterpreter>>#argumentCountOfClosure: */ +sqInt +argumentCountOfClosure(sqInt closurePointer) +{ + sqInt oop; + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((closurePointer + BaseHeaderSize) + ((((usqInt)(ClosureNumArgsIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + return (oop >> 3); +} + + /* StackInterpreter>>#argumentCountOfMethodHeader: */ +usqInt +argumentCountOfMethodHeader(sqInt header) +{ + return (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; +} + + /* StackInterpreter>>#argumentCountOf: */ +usqInt +argumentCountOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin argumentCountOfMethodHeader: */ + return (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; +} + + +/* Return the address of first indexable field of resulting array object, or + fail if + the instance variable does not contain an indexable bytes or words object. */ +/* Note: May be called by translated primitive code. */ + + /* StackInterpreter>>#arrayValueOf: */ +void * +arrayValueOf(sqInt arrayOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (/* isWordsOrBytes: */ + ((!(arrayOop & (tagMask())))) + && (isWordsOrBytesNonImm(arrayOop))) { + return ((void *) (pointerForOop(arrayOop + BaseHeaderSize))); + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; +} + + +/* Returns an integer object */ + + /* StackInterpreter>>#asciiOfCharacter: */ +static NoDbgRegParms sqInt +asciiOfCharacter(sqInt characterObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (((characterObj & (characterTag())) != 0)) { + return characterObj - ((characterTag()) - (smallIntegerTag())); + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return ConstZero; +} + + /* StackInterpreter>>#assertValidExecutionPointe:r:s: */ +void +assertValidExecutionPointers(usqInt lip, char *lifp, char *lisp) +{ + assertValidExecutionPointersimbarline(lip, lifp, lisp, !((((usqInt)(longAt(lifp + FoxMethod)))) < (startOfMemory())), __LINE__); +} + + +/* Order in the stackLimit checks is important because stackLimit is smashed + by interrupts. So always check for unsmashed value first to avoid race + condition. + */ + + /* StackInterpreter>>#assertValidStackLimits: */ +static NoDbgRegParms void +assertValidStackLimits(sqInt ln) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assertl((GIV(stackLimit) == ((GIV(stackPage)->realStackLimit))) + || (GIV(stackLimit) == (allOnesAsCharStar())), ln); + assertl((((GIV(stackPage)->stackLimit)) == ((GIV(stackPage)->realStackLimit))) + || (((GIV(stackPage)->stackLimit)) == (allOnesAsCharStar())), ln); +} + + +/* convert true and false (Smalltalk) to true or false(C) */ + + /* StackInterpreter>>#booleanValueOf: */ +sqInt +booleanValueOf(sqInt obj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (obj == GIV(trueObj)) { + return 1; + } + if (obj == GIV(falseObj)) { + return 0; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; +} + + +/* Re-enter the interpreter to execute a (non-Alien) callback. */ + + /* StackInterpreter>>#callbackEnter: */ +sqInt +callbackEnter(sqInt *callbackID) +{ + warning("callbackEnter: is obsolete"); + return 0; +} + + +/* Leave from a previous callback */ + + /* StackInterpreter>>#callbackLeave: */ +sqInt +callbackLeave(sqInt cbID) +{ + warning("callbackLeave: is obsolete"); + return 0; +} + + +/* Context switch should not be allowed on every method activation. In + particular the + implementation of ensure: and ifCurtailed: depends on there being no + suspension point + on failing primitive 198 (primitiveMarkUnwindMethod, + primitiveMarkUnwindMethod). slowPrimitiveResponse used to state + ``N.B. This means there is no suspension point on primitive failure + which methods such as ensure: and ifCurtailed: rely on.'' + Rather than prevent context switch on all primitives but the ones we + really need + to be suspension points (primitiveSignal et al) we choose to allow context + switch for all but primitiveMarkUnwindMethod and + PrimNumberNoContextSwitch. */ + + /* StackInterpreter>>#canContextSwitchIfActivating:header: */ +sqInt +canContextSwitchIfActivatingheader(sqInt theMethod, sqInt methodHeader) +{ + usqInt firstBytecode; + sqInt primitiveIndex; + + /* primitiveIndexOfMethod:header: */ + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (theMethod + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + return (primitiveIndex != PrimNumberUnwindMarker) + && (primitiveIndex != PrimNumberNoContextSwitchMarker); +} + + +/* Above ObjectMemory, arg must lie in range 0-255! */ + + /* StackInterpreter>>#characterForAscii: */ +sqInt +characterForAscii(sqInt ascii) +{ + return ((((usqInt)(ascii) << (numTagBits())))) + (characterTag()); +} + + +/* Ensure that all accessible objects in the heap are okay. */ +/* useful for VM debugging */ + + /* StackInterpreter>>#checkAllAccessibleObjectsOkay */ +sqInt +checkAllAccessibleObjectsOkay(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt obj; + sqInt ok; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + ok = 1; + + /* begin allObjectsDoSafely: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + if (((longAt((void *)(obj))) & (classIndexMask())) > (lastClassIndexPun())) { + ok = ok && (checkOkayFields(obj)); + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + return ok; +} + + +/* Check for a hit of the longRunningPrimitive probe and if so attempt to + signal the + longRunningPrimitiveCheckSemaphore. Answer if a process switch occurred as + a result. */ + + /* StackInterpreter>>#checkDeliveryOfLongRunningPrimitiveSignal */ +#if LRPCheck +static NeverInline sqInt +checkDeliveryOfLongRunningPrimitiveSignal(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((GIV(longRunningPrimitiveStopUsecs) > GIV(longRunningPrimitiveStartUsecs)) + && ((GIV(longRunningPrimitiveCheckSemaphore)) + && (GIV(longRunningPrimitiveSignalUndelivered)))) { + GIV(longRunningPrimitiveSignalUndelivered) = 0; + GIV(longRunningPrimitiveGCUsecs) = ((GIV(gcStartUsecs) < GIV(longRunningPrimitiveStopUsecs)) + && (GIV(statGCEndUsecs) > GIV(longRunningPrimitiveStartUsecs)) + ? GIV(statGCEndUsecs) - GIV(gcStartUsecs) + : 0); + + /* Signal the LRP check semaphore if it is present */ + return synchronousSignal(GIV(longRunningPrimitiveCheckSemaphore)); + } + + /* a hit + but not yet delivered */ + return 0; +} +#endif /* LRPCheck */ + + +/* Note: May be called by translated primitive code. */ + + /* StackInterpreter>>#checkedIntegerValueOf: */ +sqInt +checkedIntegerValueOf(sqInt intOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((((intOop) & 7) == 1)) { + return (intOop >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } +} + + +/* Support for embedded images. Check that the first few bytes of a potential + header and answer if it + looks like something the VM can load, + The method checks the first three fields of the header (magic, header size + & data size) & the total size. + The magic number should be correct. + The header size should be correct. + The size of the data should be at least as long as the headerSize plus the + data size in the header */ +/* Need at least headerSize bytes; no point going further if not... */ + + /* StackInterpreter>>#checkImageHeaderFromBytes:AndSize: */ +sqInt +checkImageHeaderFromBytesAndSize(char *bytes, sqInt totalSize) +{ + sqInt dataSize; + sqInt headerSize; + int version; + + if (totalSize < (BytesPerWord * 16)) { + return 0; + } + version = long32At(bytes); + headerSize = long32At(bytes + 4); + dataSize = longAt(bytes + 8); + if (!((((version | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */)) { + if (!(((((SQ_SWAP_4_BYTES(version)) | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */)) { + return 0; + } + headerSize = SQ_SWAP_8_BYTES(headerSize); + dataSize = SQ_SWAP_8_BYTES(dataSize); + } + return (headerSize == (BytesPerWord * 16)) + && (totalSize >= (headerSize + dataSize)); +} + + +/* Read and verify the image file version number and answer it. + Assign through rawVersionPtr the unswapped version number. The caller can + then infer if the given image file needs to be byte-swapped by seeing if + the returned value + equals tha assigned through rawVersionPtr. + 0 is answered if no valid version could be found. */ + + /* StackInterpreter>>#checkImageVersionFrom:startingAt:assignRawVersion: */ +static NoDbgRegParms sqInt +checkImageVersionFromstartingAtassignRawVersion(sqImageFile f, squeakFileOffsetType imageOffset, sqInt *rawVersionPtr) +{ + sqInt version; + int w; + + rawVersionPtr[0] = 0; + + /* check the version number */ + sqImageFileSeek(f, imageOffset); + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + version = w; + rawVersionPtr[0] = version; + if ((((version | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */) { + return version; + } + + /* try with bytes reversed */ + sqImageFileSeek(f, imageOffset); + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + version = SQ_SWAP_4_BYTES(w); + if (!(/* isLikelyImageVersion: */ + (((rawVersionPtr[0]) / 100) == 65) + || (((rawVersionPtr[0]) / 1000) == 68))) { + rawVersionPtr[0] = version; + } + if ((((version | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */) { + return version; + } + + /* Note: The following is only meaningful if not reading an embedded image */ + if (!imageOffset) { + sqImageFileSeek(f, 0x200); + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + version = w; + if (!(/* isLikelyImageVersion: */ + (((rawVersionPtr[0]) / 100) == 65) + || (((rawVersionPtr[0]) / 1000) == 68))) { + rawVersionPtr[0] = version; + } + if ((((version | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */) { + return version; + } + + /* try skipping the first 512 bytes with bytes reversed */ + sqImageFileSeek(f, 0x200); + + /* begin getWord32FromFile:swap: */ + w = 0; + sqImageFileRead((&w), sizeof(int), 1, f); + version = SQ_SWAP_4_BYTES(w); + if (!(/* isLikelyImageVersion: */ + (((rawVersionPtr[0]) / 100) == 65) + || (((rawVersionPtr[0]) / 1000) == 68))) { + rawVersionPtr[0] = version; + } + if ((((version | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */) { + return version; + } + } + + /* try skipping the first 512 bytes (prepended by certain Mac file transfer utilities) */ + if (!(/* isLikelyImageVersion: */ + (((rawVersionPtr[0]) / 100) == 65) + || (((rawVersionPtr[0]) / 1000) == 68))) { + rawVersionPtr[0] = version; + } + return 0; +} + + +/* Perform an integrity/leak check using the heapMap. Assume + clearLeakMapAndMapAccessibleObjects has set a bit at each + object's header. Check that all oops in the interpreter's state + points to a header. Answer 0 if all checks pass. */ + + /* StackInterpreter>>#checkInterpreterIntegrity */ +static sqInt +checkInterpreterIntegrity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt flags; + + flags = 0; + if (!(checkOopIntegritynamed(GIV(specialObjectsOop), "specialObjectsOop"))) { + flags = 1; + } + + /* No longer check messageSelector; it is ephemeral, not living beyond message lookup. + (objectMemory isNonImmediate: messageSelector) ifTrue: + [(objectMemory checkOopIntegrity: messageSelector named: 'messageSelector') ifFalse: + [flags := flags + N]]. */ + if (!(checkOopIntegritynamed(GIV(newMethod), "newMethod"))) { + flags += 2; + } + + /* No longer check lkupClass; it is ephemeral, not living beyond message lookup. + (objectMemory checkOopIntegrity: lkupClass named: 'lkupClass') ifFalse: + [flags := flags + N]. */ + if (GIV(profileProcess)) { + if (!(checkOopIntegritynamed(GIV(profileProcess), "profileProcess"))) { + flags += 4; + } + } + if (GIV(profileMethod)) { + if (!(checkOopIntegritynamed(GIV(profileMethod), "profileMethod"))) { + flags += 8; + } + } + if (GIV(profileSemaphore)) { + if (!(checkOopIntegritynamed(GIV(profileSemaphore), "profileSemaphore"))) { + flags += 16; + } + } + if (GIV(tempOop)) { + if (!(checkOopIntegritynamed(GIV(tempOop), "tempOop"))) { + flags += 32; + } + } + if (GIV(tempOop2)) { + if (!(checkOopIntegritynamed(GIV(tempOop2), "tempOop2"))) { + flags += 64; + } + } + if (!(checkLogIntegrity())) { + flags += 128; + } + return flags; +} + + +/* Another version of isWidowedContext: for debugging. + This will not bereave a widowed context. */ + + /* StackInterpreter>>#checkIsStillMarriedContext:currentFP: */ +static NoDbgRegParms sqInt +checkIsStillMarriedContextcurrentFP(sqInt aContext, char *currentFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *limitFP; + char *maybeFP; + sqInt maybeFrameCtxt; + sqInt referent; + sqInt senderOop; + StackPage *thePage; + + if (!((/* isContext: */ + ((!(aContext & (tagMask())))) + && (((longAt((void *)(aContext))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)))) { + return 0; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + maybeFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(maybeFP), GIV(pages)); + limitFP = ((thePage == GIV(stackPage)) + && (currentFP) + ? currentFP + : (thePage->headFP)); + if (!((maybeFP >= limitFP) + && (((!((((sqInt)(((char *)(longAt(maybeFP + FoxSavedFP)))))) & (tagMask())))) + && ((((/* begin withSmallIntegerTags: */ + assert(((oopForPointer(((char *)(longAt(maybeFP + FoxSavedFP))))) & (BytesPerWord - 1)) == 0), + (oopForPointer(((char *)(longAt(maybeFP + FoxSavedFP))))) + (smallIntegerTag()))) == (longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) + && (/* frameHasContext: */ + ((((usqInt)(longAt(maybeFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(maybeFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((maybeFP + FoxIFrameFlags) + 2)) != 0)))))) { + return 0; + } + maybeFrameCtxt = longAt(maybeFP + FoxThisContext); + + /* On Spur we need to follow the context to check for a match, but since the VM is + only speculating about maybeFrame being a frame, and only speculating about + maybeContext being a context, we need to be sure before we can safely follow. */ + if ((!(isFree(thePage))) + && ((isFrameonPage(maybeFP, thePage)) + && ((!((longAt((void *)(maybeFrameCtxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(maybeFrameCtxt)); + referent = longAt((void *)((maybeFrameCtxt + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + maybeFrameCtxt = referent; + } + return maybeFrameCtxt == aContext; +} + + +/* Check the log for leaks. The trace log is a circular buffer of pairs of + entries. If there is an entry at traceLogIndex - 3 \\ TraceBufferSize it + has entries. If + there is something at traceLogIndex it has wrapped. */ + + /* StackInterpreter>>#checkLogIntegrity */ +static sqInt +checkLogIntegrity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt limit; + sqInt ok; + sqInt oop; + sqInt remainder; + + limit = /* safe:mod: */ + (((remainder = (GIV(traceLogIndex) - 3) % TraceBufferSize)) < 0 + ? remainder + TraceBufferSize + : remainder); + if (!(GIV(traceLog)[limit])) { + return 1; + } + if (GIV(traceLog)[GIV(traceLogIndex)]) { + limit = TraceBufferSize - 3; + } + ok = 1; + for (i = 0; i <= limit; i += 3) { + oop = GIV(traceLog)[i]; + if (!(((oop & (tagMask())) != 0))) { + if (!(checkOopIntegritynamedindex(oop, "traceLog", i))) { + ok = 0; + } + } + oop = GIV(traceLog)[i + 1]; + if (!(((oop & (tagMask())) != 0))) { + if (!(checkOopIntegritynamedindex(oop, "traceLog", i + 1))) { + ok = 0; + } + } + } + return ok; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#checkOkayInterpreterObjects: */ +sqInt +checkOkayInterpreterObjects(sqInt writeBack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int i; + sqInt ok; + sqInt oop; + sqIntptr_t oopOrZero; + + ok = 1; + ok = ok && (checkOkayFields(GIV(nilObj))); + ok = ok && (checkOkayFields(GIV(falseObj))); + ok = ok && (checkOkayFields(GIV(trueObj))); + ok = ok && (checkOkayFields(GIV(specialObjectsOop))); + ok = ok && (checkOkayFields(GIV(messageSelector))); + ok = ok && (checkOkayFields(GIV(newMethod))); + ok = ok && (checkOkayFields(GIV(lkupClass))); + for (i = 0; i < MethodCacheEntries; i += MethodCacheEntrySize) { + oopOrZero = GIV(methodCache)[i + MethodCacheSelector]; + if (oopOrZero) { + ok = ok && (checkOkayFields(GIV(methodCache)[i + MethodCacheSelector])); + ok = ok && (checkOkayFields(GIV(methodCache)[i + MethodCacheMethod])); + } + } + for (i = 1; i <= GIV(remapBufferCount); i += 1) { + oop = GIV(remapBuffer)[i]; + if (!(((oop & (tagMask())) != 0))) { + ok = ok && (checkOkayFields(oop)); + } + } + ok = ok && (checkOkayStackZone(writeBack)); + return ok; +} + + /* StackInterpreter>>#checkOkayStackPage: */ +static NoDbgRegParms sqInt +checkOkayStackPage(StackPage *thePage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + char *frameRcvrOffset; + sqInt ok; + sqInt oop; + char *theFP; + char *theSP; + + callerFP = ((char *) 0); + theSP = (thePage->headSP); + theFP = (thePage->headFP); + ok = 1; + + /* Skip the instruction pointer on top of stack of inactive pages. */ + if (!(thePage == GIV(stackPage))) { + theSP += BytesPerWord; + } + while (1) { + frameRcvrOffset = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + while (theSP <= frameRcvrOffset) { + oop = longAt(theSP); + if (!((((oop & (tagMask())) != 0)) + || (checkOkayFields(oop)))) { + ok = 0; + } + theSP += BytesPerWord; + } + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + if (!(checkOkayFields(longAt(theFP + FoxThisContext)))) { + ok = 0; + } + } + if (!(checkOkayFields(/* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod))))) { + ok = 0; + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + theFP = callerFP; + } + + /* caller ip is frameCallerContext in a base frame */ + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if (!((((oop & (tagMask())) != 0)) + || (checkOkayFields(oop)))) { + ok = 0; + } + theSP += BytesPerWord; + } + return ok; +} + + +/* Check that all objects in the stack zone are okay */ + + /* StackInterpreter>>#checkOkayStackZone: */ +static NoDbgRegParms sqInt +checkOkayStackZone(sqInt writeBack) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt ok; + StackPage *thePage; + + if (writeBack) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + ok = 1; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + ok = ok && (checkOkayStackPage(thePage)); + } + } + return ok; +} + + +/* Perform an integrity/leak check using the heapMap. Assume + clearLeakMapAndMapAccesibleObjects has set a bit at each + object's header. Scan all objects accessible from the stack + checking that every pointer points to a header. Answer if no + dangling pointers were detected. */ + + /* StackInterpreter>>#checkStackIntegrity */ +static sqInt +checkStackIntegrity(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + CogMethod *cogMethod; + char *frameRcvrOffset; + sqInt i; + sqInt methodField; + sqInt ok; + usqInt oop; + char *theFP; + StackPage *thePage; + char *theSP; + + callerFP = ((char *) 0); + ok = 1; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + if (thePage == GIV(stackPage)) { + theSP = GIV(stackPointer); + theFP = GIV(framePointer); + } + else { + theSP = (thePage->headSP); + theFP = (thePage->headFP); + } + + /* Skip the instruction pointer on top of stack of inactive pages. */ + if (!(thePage == GIV(stackPage))) { + theSP += BytesPerWord; + } + while (1) { + frameRcvrOffset = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + while (theSP <= frameRcvrOffset) { + oop = longAt(theSP); + if (((!(oop & (tagMask())))) + && ((heapMapAtWord(pointerForOop(oop))) == 0)) { + printFrameThingandFrameat("object leak in frame temp", theFP, theSP); + ok = 0; + } + theSP += BytesPerWord; + } + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + oop = longAt(theFP + FoxThisContext); + if ((((oop & (tagMask())) != 0)) + || ((heapMapAtWord(pointerForOop(oop))) == 0)) { + printFrameThingandFrameat("object leak in frame ctxt", theFP, theFP + FoxThisContext); + ok = 0; + } + if (!(/* isContext: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + printFrameThingandFrameat("frame ctxt should be context", theFP, theFP + FoxThisContext); + ok = 0; + } + if (!((/* isContext: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (((((longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)))) { + printFrameThingandFrameat("frame ctxt should be married", theFP, theFP + FoxThisContext); + ok = 0; + } + if (!((/* isContext: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && ((frameOfMarriedContext(oop)) == theFP))) { + printFrameThingandFrameat("frame ctxt should be married to this frame ", theFP, theFP + FoxThisContext); + ok = 0; + } + } + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* begin mframeHomeMethod: */ + methodField = longAt(theFP + FoxMethod); + if (methodField & MFMethodFlagIsBlockFlag) { + cogMethod = /* cmHomeMethod */ + ((((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)) >= CMMethod + ? ((CogMethod *) (((CogBlockMethod *) (methodField & MFMethodMask)))) + : (assert(CMBlock == ((((CogBlockMethod *) (methodField & MFMethodMask)))->cmType)), + ((CogMethod *) ((((usqInt)(((CogBlockMethod *) (methodField & MFMethodMask))))) - (((((CogBlockMethod *) (methodField & MFMethodMask)))->homeOffset)))))); + goto l1; + } + cogMethod = ((CogMethod *) (methodField & MFMethodMask)); + /* end mframeHomeMethod: */ +l1: + if (!(heapMapAtWord(cogMethod))) { + printFrameThingandFrameat("object leak in mframe mthd", theFP, theFP + FoxMethod); + ok = 0; + } + } + else { + oop = longAt(theFP + FoxMethod); + if ((((oop & (tagMask())) != 0)) + || ((heapMapAtWord(pointerForOop(oop))) == 0)) { + printFrameThingandFrameat("object leak in iframe mthd", theFP, theFP + FoxMethod); + ok = 0; + } + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + theFP = callerFP; + } + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + while (theSP <= ((thePage->baseAddress))) { + oop = longAt(theSP); + if (((!(oop & (tagMask())))) + && ((heapMapAtWord(pointerForOop(oop))) == 0)) { + printFrameThingandFrameat("object leak in frame arg", theFP, theSP); + ok = 0; + } + theSP += BytesPerWord; + } + } + } + return ok; +} + + +/* Version of stackPointerForMaybeMarriedContext: with no side-effects (does + not widow). + Used for assertion checking. Safe only in external primitives + (framePointer valid). + Answer the stackPointer of a Context. */ + + /* StackInterpreter>>#checkStackPointerForMaybeMarriedContext: */ +static NoDbgRegParms sqInt +checkStackPointerForMaybeMarriedContext(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + sqInt methodPointer; + sqInt sp; + sqInt spSqInt; + + if (checkIsStillMarriedContextcurrentFP(aContext, GIV(framePointer))) { + sp = checkStackPointerIndexForFrame(frameOfMarriedContext(aContext)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(aContext))); + return sp; + } + if (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + methodPointer = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* begin argumentCountOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin argumentCountOfMethodHeader: */ + return (((usqInt)(header)) >> MethodHeaderArgCountShift) & 15; + } + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + return 0; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + return (spSqInt >> 3); +} + + +/* Version of stackPointerIndexForFrame: that does not depend on writing back + head frame pointers. + Used for assertion checking. Safe only in external primitives + (framePointer valid). + Answer the 0-based index rel to the given frame. + (This is what stackPointer used to be before conversion to pointer) */ +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#checkStackPointerIndexForFrame: */ +static NoDbgRegParms sqInt +checkStackPointerIndexForFrame(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *aFrame; + char *prevFrame; + char *startFrame; + StackPage *thePage; + char *theSP; + + if (theFP == GIV(framePointer)) { + return /* stackPointerIndexForFrame:WithSP: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (((usqInt)(((theFP + FoxMFReceiver) - GIV(stackPointer)))) >> (shiftForWord())) + (((mframeCogMethod(theFP))->cmNumArgs)) + : (((usqInt)(((theFP + FoxIFReceiver) - GIV(stackPointer)))) >> (shiftForWord())) + (byteAt((theFP + FoxIFrameFlags) + 1))); + } + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + startFrame = (thePage == GIV(stackPage) + ? GIV(framePointer) + : (thePage->headFP)); + + /* begin findSPOrNilOf:on:startingFrom: */ + prevFrame = ((char *) 0); + if (startFrame == theFP) { + if (((thePage->headSP)) >= startFrame) { + /* If the SP is invalid return the pointer to the receiver field. */ + theSP = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + goto l1; + } + + /* Skip the instruction pointer on top of stack of inactive pages. */ + theSP = (thePage == GIV(stackPage) + ? (thePage->headSP) + : ((thePage->headSP)) + BytesPerWord); + goto l1; + } + aFrame = startFrame; + while (1) { + prevFrame = aFrame; + aFrame = ((char *)(longAt(aFrame + FoxSavedFP))); + if (!(aFrame != 0)) break; + if (theFP == aFrame) { + /* begin frameCallerSP: */ + assert(!(isBaseFrame(prevFrame))); + theSP = (prevFrame + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(prevFrame + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(prevFrame))->cmNumArgs) + : byteAt((prevFrame + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + goto l1; + } + } + theSP = null; + /* end findSPOrNilOf:on:startingFrom: */ +l1: + if (!theSP) { + return -1; + } + return /* stackPointerIndexForFrame:WithSP: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (((usqInt)(((theFP + FoxMFReceiver) - theSP))) >> (shiftForWord())) + (((mframeCogMethod(theFP))->cmNumArgs)) + : (((usqInt)(((theFP + FoxIFReceiver) - theSP))) >> (shiftForWord())) + (byteAt((theFP + FoxIFrameFlags) + 1))); +} + + +/* Check if aClass's name is className */ + + /* StackInterpreter>>#classNameOf:Is: */ +static NoDbgRegParms sqInt +classNameOfIs(sqInt aClass, char *className) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt i; + sqInt length; + sqInt name; + usqInt numSlots; + char *srcName; + + if (((/* begin numSlotsOf: */ + assert((classIndexOf(aClass)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(aClass + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aClass - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) <= GIV(classNameIndex)) { + return 0; + } + name = longAt((void *)((aClass + BaseHeaderSize) + ((((usqInt)(GIV(classNameIndex)) << (shiftForWord())))))); + if (!(/* isBytes: */ + ((!(name & (tagMask())))) + && (((byteAt((void *)(name + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + return 0; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(name + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + length = ((((/* begin numSlotsOf: */ + assert((classIndexOf(name)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(name + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(name - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + srcName = ((char *) (arrayValueOf(name))); + for (i = 0; i < length; i += 1) { + if (!((srcName[i]) == (className[i]))) { + return 0; + } + } + + /* Check if className really ends at this point */ + return (className[length]) == 0; +} + + /* StackInterpreter>>#clearTraceLog */ +void +clearTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + + GIV(traceLogIndex) = 0; + for (i = 0; i < TraceBufferSize; i += 1) { + GIV(traceLog)[i] = 0; + } +} + + +/* For asserts. Check that theIP maps back correctly to the context's pc. + The CallPrimitive bytecode presents a complication. */ + + /* StackInterpreter>>#context:hasValidInversePCMappingOf:in: */ +static NoDbgRegParms sqInt +contexthasValidInversePCMappingOfin(sqInt aContext, sqInt theIP, char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt encodedip; + sqInt header; + sqInt methodHeader; + sqInt methodObj; + sqInt pc; + + pc = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + encodedip = contextInstructionPointerframe(theIP, theFP); + return (pc == encodedip) + || (((methodObj = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord()))))))), + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)), + (header = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord()))))))), + (methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader)))), + (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) + && ((((encodedip >> 3)) - ((pc >> 3))) == 3 /* sizeOfCallPrimitiveBytecode: */))); +} + + +/* for Cogit */ + + /* StackInterpreter>>#copiedValueCountOfClosure: */ +sqInt +copiedValueCountOfClosure(sqInt closureObj) +{ + usqInt numSlots; + + return (((((longAt((void *)((closureObj + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1) + ? (/* begin copiedValueCountOfVanillaClosure: */ + assert(isVanillaBlockClosure(closureObj)), + ((/* begin numSlotsOf: */ + assert((classIndexOf(closureObj)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(closureObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(closureObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - ClosureFirstCopiedValueIndex) + : (/* begin copiedValueCountOfFullClosure: */ + assert(!((isVanillaBlockClosure(closureObj)))), + ((/* begin numSlotsOf: */ + assert((classIndexOf(closureObj)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(closureObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(closureObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - FullClosureFirstCopiedValueIndex)); +} + + +/* This entry point needs to be implemented for the interpreter proxy. + Since BitBlt is now a plugin we need to look up BitBltPlugin:=copyBits + and call it. This entire mechanism should eventually go away and be + replaced with a dynamic lookup from BitBltPlugin itself but for backward + compatibility this stub is provided */ + + /* StackInterpreter>>#copyBits */ +sqInt +copyBits(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void *fn; + + fn = ioLoadFunctionFrom("copyBits", "BitBltPlugin"); + if (!fn) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return ((sqInt (*)(void))fn)(); +} + + +/* This entry point needs to be implemented for the interpreter proxy. + Since BitBlt is now a plugin we need to look up + BitBltPlugin:=copyBitsFrom:to:at: and call it. This entire mechanism + should eventually go away and be + replaced with a dynamic lookup from BitBltPlugin itself but for backward + compatibility this stub is provided + */ + + /* StackInterpreter>>#copyBitsFrom:to:at: */ +sqInt +copyBitsFromtoat(sqInt x0, sqInt x1, sqInt y) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void *fn; + + fn = ioLoadFunctionFrom("copyBitsFromtoat", "BitBltPlugin"); + if (!fn) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return ((sqInt (*)(sqInt, sqInt, sqInt))fn)(x0, x1, y); +} + + /* StackInterpreter>>#couldBeProcess: */ +static NoDbgRegParms NeverInline sqInt +couldBeProcess(sqInt oop) +{ + return (addressCouldBeObj(oop)) + && ((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + && ((!(((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (((lengthOf(oop)) > MyListIndex) + && (isContext(longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))))))))); +} + + +/* Bundle up the selector, arguments and lookupClass into a Message object. + In the process it pops the arguments off the stack, and pushes the message + object. + This can then be presented as the argument of e.g. #doesNotUnderstand: */ + + /* StackInterpreter>>#createActualMessageTo: */ +static NoDbgRegParms void +createActualMessageTo(sqInt lookupClass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt argumentArray; + sqInt i; + usqInt message; + usqInt newObj; + usqInt numBytes; + + + /* This is a useful break-point */ + assert((isImmediate(GIV(messageSelector))) + || (addressCouldBeObj(GIV(messageSelector)))); + + /* begin mnuBreakpoint:receiver: */ + mnuBreakpointreceiver(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), null); + assert((GIV(argumentCount) >= 0) + && ((knownClassAtIndex(ClassArrayCompactIndex)) != GIV(nilObj))); + assert((arrayFormat()) == (instSpecOfClass(knownClassAtIndex(ClassArrayCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(GIV(argumentCount) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((GIV(argumentCount) < 1 + ? 8 /* allocationUnit */ + : GIV(argumentCount) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + argumentArray = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) GIV(argumentCount))) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + GIV(freeStart) += numBytes; + argumentArray = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l1: + + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert(((MessageLookupClassIndex + 1) >= 0) + && ((knownClassAtIndex(ClassMessageCompactIndex)) != GIV(nilObj))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(knownClassAtIndex(ClassMessageCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert((MessageLookupClassIndex + 1) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (((MessageLookupClassIndex + 1) < 1 + ? 8 /* allocationUnit */ + : (MessageLookupClassIndex + 1) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + message = 0; + goto l2; + } + } + long64Atput((void *)(newObj),((((((usqLong) (MessageLookupClassIndex + 1))) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + ClassMessageCompactIndex); + GIV(freeStart) += numBytes; + message = newObj; + /* end eeInstantiateSmallClassIndex:format:numSlots: */ +l2: + + /* Since the array is new can use unchecked stores. */ + for (i = ((GIV(argumentCount) - 1) * BytesPerOop); i >= 0; i += (-BytesPerOop)) { + longAtput((void *)((argumentArray + BaseHeaderSize) + i),popStack()); + } + + /* Since message is new can use unchecked stores. */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(message)) + && (!(isForwarded(message)))); + assert(validStorePointerUncheckedArgs(MessageSelectorIndex, message, GIV(messageSelector))); + longAtput((void *)((message + BaseHeaderSize) + ((((usqInt)(MessageSelectorIndex) << (shiftForWord()))))),GIV(messageSelector)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(message)) + && (!(isForwarded(message)))); + assert(validStorePointerUncheckedArgs(MessageArgumentsIndex, message, argumentArray)); + longAtput((void *)((message + BaseHeaderSize) + ((((usqInt)(MessageArgumentsIndex) << (shiftForWord()))))),argumentArray); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(message)) + && (!(isForwarded(message)))); + assert(validStorePointerUncheckedArgs(MessageLookupClassIndex, message, lookupClass)); + longAtput((void *)((message + BaseHeaderSize) + ((((usqInt)(MessageLookupClassIndex) << (shiftForWord()))))),lookupClass); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),message); + GIV(argumentCount) = 1; +} + + +/* Return the default number of stack pages allocate at startup. + This V3 default suits Qwaq Forums (specifically general rendering). + The Spur default reflects tuning for GC performance ast Cadence. + It is probably a bit high for normal use but QF is profligate with + processes. The actual value can be set via vmParameterAt: and/or a + preference in the ini file. */ + + /* StackInterpreter>>#defaultNumStackPages */ +static sqInt +defaultNumStackPages(void) +{ + return 50; +} + + /* StackInterpreter>>#disableSendPrinting */ +#if SEND_PRINTING +void +disableSendPrinting(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(printSends) = 0; +} +#endif /* SEND_PRINTING */ + + /* StackInterpreter>>#divorceAllFrames */ +static sqInt +divorceAllFrames(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + StackPage *aPage; + sqInt i; + + if (GIV(stackPage)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((longAt(GIV(framePointer) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((GIV(framePointer) + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(GIV(framePointer)))); + activeContext = longAt(GIV(framePointer) + FoxThisContext); + goto l1; + } + activeContext = marryFrameSP(GIV(framePointer), GIV(stackPointer) + BytesPerWord); + /* end ensureFrameIsMarried:SP: */ +l1: + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + aPage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(aPage))) { + divorceFramesIn(aPage); + } + } + + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + return activeContext; +} + + /* StackInterpreter>>#divorceFramesIn: */ +static NoDbgRegParms void +divorceFramesIn(StackPage *aStackPage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt calleeContext; + char *calleeFP; + sqInt callerContextOrNil; + sqInt theContext; + char *theFP; + sqInt theIP; + StackPage *thePage; + char *theSP; + sqInt valuePointer; + + calleeFP = ((char *) 0); + GIV(statStackPageDivorce) += 1; + theFP = (aStackPage->headFP); + theSP = (aStackPage->headSP); + theIP = longAt(theSP); + + /* theSP points at hottest item on frame's stack */ + theSP += BytesPerWord; + calleeContext = null; + while (1) { + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + theContext = longAt(theFP + FoxThisContext); + goto l1; + } + theContext = marryFrameSP(theFP, theSP); + /* end ensureFrameIsMarried:SP: */ +l1: + updateStateOfSpouseContextForFrameWithSP(theFP, theSP); + valuePointer = contextInstructionPointerframe(theIP, theFP); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(theContext)) + && (!(isForwarded(theContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, theContext, valuePointer)); + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointer); + assert((frameReceiver(theFP)) == (followFieldofObject(ReceiverIndex, theContext))); + if (calleeContext) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SenderIndex, calleeContext, theContext)); + assert(isNonImmediate(calleeContext)); + if (oopisGreaterThanOrEqualTo(calleeContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(theContext & (tagMask())))) + && (oopisLessThan(theContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(calleeContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(calleeContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((calleeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),theContext); + } + calleeContext = theContext; + calleeFP = theFP; + theIP = longAt(theFP + FoxCallerSavedIP); + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!(theFP != 0)) break; + /* theSP points at stacked hottest item on frame's stack */ + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(calleeFP))); + theSP = (calleeFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(calleeFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(calleeFP))->cmNumArgs) + : byteAt((calleeFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + + /* begin frameCallerContext: */ + assert(isBaseFrame(calleeFP)); + thePage = stackPageAtpages(pageIndexFor(calleeFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + valuePointer = callerContextOrNil; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SenderIndex, theContext, valuePointer)); + assert(isNonImmediate(theContext)); + if (oopisGreaterThanOrEqualTo(theContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(valuePointer & (tagMask())))) + && (oopisLessThan(valuePointer, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(theContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(theContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),valuePointer); + + /* The page is now free; mark it so. */ + (aStackPage->baseFP = 0); +} + + +/* Rounds negative results towards negative infinity, rather than zero. */ + + /* StackInterpreter>>#doPrimitiveDiv:by: */ +static NoDbgRegParms sqInt +doPrimitiveDivby(sqInt rcvr, sqInt arg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArg; + sqInt integerRcvr; + sqInt posArg; + sqInt posRcvr; + sqInt result; + + integerArg = 0; + integerRcvr = 0; + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + integerRcvr = (rcvr >> 3); + integerArg = (arg >> 3); + + /* begin success: */ + if (!integerArg) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return 1; + } + if (integerRcvr > 0) { + if (integerArg > 0) { + result = integerRcvr / integerArg; + } + else { + /* round negative result toward negative infinity */ + posArg = 0 - integerArg; + result = 0 - ((integerRcvr + (posArg - 1)) / posArg); + } + } + else { + posRcvr = 0 - integerRcvr; + if (integerArg > 0) { + /* round negative result toward negative infinity */ + result = 0 - ((posRcvr + (integerArg - 1)) / integerArg); + } + else { + posArg = 0 - integerArg; + result = posRcvr / posArg; + } + } + + /* begin success: */ + if (!((((((usqInt)(result)) >> 60) + 1) & 15) <= 1)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + return result; +} + + /* StackInterpreter>>#doPrimitiveMod:by: */ +static NoDbgRegParms sqInt +doPrimitiveModby(sqInt rcvr, sqInt arg) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerArg; + sqInt integerRcvr; + sqInt integerResult; + + integerArg = 0; + integerRcvr = 0; + if ((((rcvr & arg) & (smallIntegerTag())) != 0)) { + integerRcvr = (rcvr >> 3); + integerArg = (arg >> 3); + + /* begin success: */ + if (!integerArg) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + if (GIV(primFailCode)) { + return 1; + } + integerResult = integerRcvr % integerArg; + + /* ensure that the result has the same sign as the integerArg */ + if (integerArg < 0) { + if (integerResult > 0) { + integerResult += integerArg; + } + } + else { + if (integerResult < 0) { + integerResult += integerArg; + } + } + + /* begin success: */ + if (!((((((usqInt)(integerResult)) >> 60) + 1) & 15) <= 1)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + return integerResult; +} + + +/* Signal the external semaphore with the given index. Answer if a context + switch occurs as a result. Do not bounds check. This has been done in the + caller. */ + + /* StackInterpreter>>#doSignalSemaphoreWithIndex: */ +sqInt +doSignalSemaphoreWithIndex(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt sema; + sqInt xArray; + + xArray = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ExternalObjectsArray) << (shiftForWord())))))); + assert(isArray(xArray)); + + /* Note: semaphore indices are 1-based */ + sema = longAt((void *)((xArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord())))))); + assert(!(isOopForwarded(sema))); + return (/* isSemaphoreOop: */ + ((!(sema & (tagMask())))) + && (((longAt((void *)(sema))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord())))))))))) + && (synchronousSignal(sema)); +} + + +/* Presumably this exists to squash C compiler warnings about unused + variables... + */ + + /* StackInterpreter>>#dummyReferToProxy */ +struct VirtualMachine * * +dummyReferToProxy(void) +{ + return (&interpreterProxy); +} + + +/* The prim trace log is a circular buffer of entries. If there is + an entry at primTraceLogIndex \\ PrimTraceLogSize it has entries. + If there is something at primTraceLogIndex it has wrapped. */ + + /* StackInterpreter>>#dumpPrimTraceLog */ +void +dumpPrimTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt hasParameter; + sqInt i; + sqInt remainder; + + if (!(GIV(primTraceLog)[/* safe:mod: */ + (((remainder = (GIV(primTraceLogIndex) - 1) % PrimTraceLogSize)) < 0 + ? remainder + PrimTraceLogSize + : remainder)])) { + return; + } + hasParameter = 0; + if (GIV(primTraceLog)[GIV(primTraceLogIndex)]) { + for (i = GIV(primTraceLogIndex); i < PrimTraceLogSize; i += 1) { + hasParameter = printPrimLogEntryAthasParameter(i, hasParameter); + } + } + for (i = 0; i < GIV(primTraceLogIndex); i += 1) { + hasParameter = printPrimLogEntryAthasParameter(i, hasParameter); + } +} + + +/* Print the prim trace log on a specific output stream. */ +/* essential for writing crash.dmp; use export: not api, so it will be + accessible on win32 and won't be written to cointerp.h + */ + + /* StackInterpreter>>#dumpPrimTraceLogOn: */ +void +dumpPrimTraceLogOn(FILE *aStdioStream) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + FILE *savedTranscript; + + /* begin withRedirectedOutputTo:do: */ + savedTranscript = GIV(transcript); + GIV(transcript) = (aStdioStream + ? aStdioStream + : stdout); + dumpPrimTraceLog(); + GIV(transcript) = savedTranscript; +} + + +/* For marking the end of a leak check print message */ + + /* StackInterpreter>>#eekcr */ +NeverInline void +eekcr(void) +{ + cr(); +} + + /* StackInterpreter>>#enableSendPrinting */ +#if SEND_PRINTING +void +enableSendPrinting(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(printSends) = 1; +} +#endif /* SEND_PRINTING */ + + +/* Answer the caller context for a frame. If the frame has a caller + frame that doesn't have a context, then marry the caller frame. */ + + /* StackInterpreter>>#ensureCallerContext: */ +static NoDbgRegParms sqInt +ensureCallerContext(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + char *callerFP; + StackPage *thePage; + + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!callerFP) { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + return callerContextOrNil; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP))); + return longAt(callerFP + FoxThisContext); + } + return marryFrameSP( + callerFP, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFP))), + (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); +} + + +/* Ensure the image data has been updated to suit the current VM. */ + + /* StackInterpreter>>#ensureImageFormatIsUpToDate: */ +static NoDbgRegParms void +ensureImageFormatIsUpToDate(sqInt swapBytes) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt addr; + sqInt address; + sqInt classIndex; + sqInt fmt; + sqInt followingWord; + usqInt followingWordAddress; + sqInt methodHeader; + usqInt numSlots; + sqInt obj; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + sqInt stopAddr; + int swapFloatWords; + sqInt temp; + int tempInt; + sqInt wordAddr; + + if (swapBytes) { + /* begin reverseBytesInImage */ + /* begin reverseBytesInMemory */ + /* begin reverseBytesFrom:to: */ + addr = GIV(oldSpaceStart); + while (oopisLessThan(addr, GIV(endOfMemory))) { + longAtput((void *)(addr),SQ_SWAP_8_BYTES((longAt((void *)(addr))))); + addr += BytesPerWord; + } + + /* Second, return the bytes of bytes-type objects to their + orginal order, and perform any other format conversions. */ + + /* begin updateObjectsPostByteSwap */ + swapFloatWords = VMBIGENDIAN != ((!(imageHeaderFlags & 2))); + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + obj = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((obj % (allocationUnit())) == 0); + if (!(oopisLessThan(obj, GIV(endOfMemory)))) break; + assert((long64At((void *)(obj))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(obj))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(obj))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + fmt = (byteAt((void *)(obj + (formatFieldByteOffset())))) & (formatMask()); + if (fmt >= (firstByteFormat())) { + wordAddr = obj + BaseHeaderSize; + if (fmt >= (firstCompiledMethodFormat())) { + methodHeader = longAt((void *)(obj + BaseHeaderSize)); + wordAddr += (((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop; + } + + /* compiled method; start after methodHeader and literals */ + stopAddr = obj + (((((/* begin numSlotsOf: */ + assert((classIndexOf(obj)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(obj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(obj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) + BaseHeaderSize); + + /* begin reverseBytesFrom:to: */ + addr = wordAddr; + while (oopisLessThan(addr, stopAddr)) { + longAtput((void *)(addr),SQ_SWAP_8_BYTES((longAt((void *)(addr))))); + addr += BytesPerWord; + } + } + + /* oop contains bytes */ + if (fmt == (firstLongFormat())) { + if (swapFloatWords + && (((longAt((void *)(obj))) & (classIndexMask())) == ClassFloatCompactIndex)) { + temp = longAt((void *)(obj + BaseHeaderSize)); + longAtput((void *)(obj + BaseHeaderSize),longAt((void *)((obj + BaseHeaderSize) + 4))); + longAtput((void *)((obj + BaseHeaderSize) + 4),temp); + } + else { + } + } + } + prevPrevObj = prevObj; + prevObj = obj; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(obj); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + obj = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + obj = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(obj, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(obj, prevObj)); + } + if (swapFloatWords) { + /* setImageFloatsBigEndian: */ + imageHeaderFlags = ((imageHeaderFlags | 2) - 2); + } + } + else { + /* begin convertFloatsToPlatformOrder */ + if (VMBIGENDIAN == ((!(imageHeaderFlags & 2)))) { + goto l3; + } + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if (((longAt((void *)(objSqInt))) & (classIndexMask())) == ClassFloatCompactIndex) { + tempInt = long32At((void *)(objSqInt + BaseHeaderSize)); + long32Atput((void *)(objSqInt + BaseHeaderSize),long32At((void *)((objSqInt + BaseHeaderSize) + 4))); + long32Atput((void *)((objSqInt + BaseHeaderSize) + 4),tempInt); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2: + assert(oopisGreaterThan(objSqInt, prevObj)); + } + + /* setImageFloatsBigEndian: */ + imageHeaderFlags = ((imageHeaderFlags | 2) - 2); + /* end convertFloatsToPlatformOrder */ +l3:; + } +} + + /* StackInterpreter>>#establishFrameForContextToReturnTo: */ +static NoDbgRegParms char * +establishFrameForContextToReturnTo(sqInt contextToReturnTo) +{ + sqInt senderOop; + StackPage *thePage; + + if (!(/* isContext: */ + ((!(contextToReturnTo & (tagMask())))) + && (((longAt((void *)(contextToReturnTo))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + return 0; + } + if (((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (isWidowedContext(contextToReturnTo)) { + /* error: home's sender is dead; cannot return */ + return 0; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + return ((char *)(senderOop - (smallIntegerTag()))); + } + if (!(((((longAt((void *)((contextToReturnTo + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1))) { + return 0; + } + thePage = makeBaseFrameFor(contextToReturnTo); + markStackPageMostRecentlyUsed(thePage); + return (thePage->baseFP); +} + + +/* Divorce a single frame and its context. If it is not the top frame of a + stack this means splitting its stack. */ +/* stackPage needs to have current head pointers to avoid confusion. */ + + /* StackInterpreter>>#externalDivorceFrame:andContext: */ +static NoDbgRegParms void +externalDivorceFrameandContext(char *theFP, sqInt ctxt) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + sqInt callerCtx; + char *callerFP; + char *callerFP1; + sqInt callerIP; + char *callerSP; + char *frameAbove; + StackPage *newPage; + int onCurrent; + sqInt theIP; + StackPage *thePage; + StackPage *thePage1; + char *theSP; + sqInt valuePointer; + + assert((!GIV(stackPage)) + || (GIV(stackPage) == (GIV(mostRecentlyUsedPage)))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (!((onCurrent = thePage == GIV(stackPage)))) { + markStackPageNextMostRecentlyUsed(thePage); + } + theSP = findSPOfon(theFP, thePage); + updateStateOfSpouseContextForFrameWithSP(theFP, theSP); + + /* begin ensureCallerContext: */ + callerFP1 = ((char *)(longAt(theFP + FoxSavedFP))); + if (!callerFP1) { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerContextOrNil = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + callerCtx = callerContextOrNil; + goto l2; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP1 + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP1 + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP1 + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP1))); + callerCtx = longAt(callerFP1 + FoxThisContext); + goto l2; + } + callerCtx = marryFrameSP( + callerFP1, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFP))), + (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + /* end ensureCallerContext: */ +l2: + if ((frameAbove = findFrameAboveinPage(theFP, thePage))) { + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l1; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l1: + theIP = longAt(frameAbove + FoxCallerSavedIP); + frameAbove = moveFramesInthroughtoPage(thePage, frameAbove, newPage); + if (onCurrent) { + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (newPage->headSP); + GIV(framePointer) = (newPage->headFP); + } + else { + markStackPageMostRecentlyUsed(newPage); + } + assert((frameCallerContext(frameAbove)) == ctxt); + } + else { + theIP = longAt((thePage->headSP)); + } + + /* If we're divorcing the top frame we can simply peel it off. + othewise move all frames above to a new stack and then peel the frame off. */ + valuePointer = contextInstructionPointerframe(theIP, theFP); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, ctxt, valuePointer)); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),valuePointer); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SenderIndex, ctxt, callerCtx)); + assert(isNonImmediate(ctxt)); + if (oopisGreaterThanOrEqualTo(ctxt, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(callerCtx & (tagMask())))) + && (oopisLessThan(callerCtx, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(ctxt + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(ctxt); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),callerCtx); + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (callerFP) { + callerIP = longAt(theFP + FoxCallerSavedIP); + callerSP = ((/* begin frameCallerSP: */ + assert(!(isBaseFrame(theFP))), +(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)) - BytesPerWord; + longAtput(callerSP,callerIP); + + /* begin setHeadFP:andSP:inPage: */ + assert(callerSP < callerFP); + assert((callerSP < ((thePage->baseAddress))) + && (callerSP > (((thePage->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((callerFP < ((thePage->baseAddress))) + && (callerFP > (((thePage->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (thePage->headFP = callerFP); + (thePage->headSP = callerSP); + } + else { + freeStackPage(thePage); + } +} + + +/* Ensure aFramePtr is a base frame. Then we can assign its sender. + Answer the possibly moved location of the frame. */ + + /* StackInterpreter>>#externalEnsureIsBaseFrame: */ +static NoDbgRegParms char * +externalEnsureIsBaseFrame(char *aFramePtr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int onCurrent; + char *theFP; + StackPage *thePage; + + if (!(longAt(aFramePtr + FoxSavedFP))) { + return aFramePtr; + } + theFP = aFramePtr; + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + onCurrent = thePage == GIV(stackPage); + + /* Storing the frame's sender with its caller's context + has the side effect of making theFP a base frame. */ + theFP = storeSenderOfFramewithValue(theFP, ensureCallerContext(theFP)); + if (onCurrent) { + assert(GIV(stackPage) != thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (GIV(stackPage)->headSP); + GIV(framePointer) = (GIV(stackPage)->headFP); + } + else { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + assert(pageListIsWellFormed()); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + return theFP; +} + + /* StackInterpreter>>#externalInstVar:ofContext:put: */ +static NoDbgRegParms sqInt +externalInstVarofContextput(sqInt index, sqInt maybeMarriedContext, sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int onCurrentPage; + sqInt senderOop; + char *theFP; + StackPage *thePage; + + assert(isContext(maybeMarriedContext)); + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* Assign the field of a married context. */ + assert(!((isObjImmutable(maybeMarriedContext)))); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(maybeMarriedContext))))) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, maybeMarriedContext, anOop)); + assert(isNonImmediate(maybeMarriedContext)); + if (oopisGreaterThanOrEqualTo(maybeMarriedContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anOop & (tagMask())))) + && (oopisLessThan(anOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(maybeMarriedContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(maybeMarriedContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),anOop); + if (index == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(maybeMarriedContext); + } + return null; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + onCurrentPage = thePage == GIV(stackPage); + if (index) { + externalDivorceFrameandContext(theFP, maybeMarriedContext); + if (ismethodAssignmentToContextWithMachineCodePC(index, maybeMarriedContext)) { + ensureContextHasBytecodePC(maybeMarriedContext); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, maybeMarriedContext, anOop)); + assert(isNonImmediate(maybeMarriedContext)); + if (oopisGreaterThanOrEqualTo(maybeMarriedContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(anOop & (tagMask())))) + && (oopisLessThan(anOop, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(maybeMarriedContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(maybeMarriedContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((maybeMarriedContext + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),anOop); + if (index == StackPointerIndex) { + ensureContextIsExecutionSafeAfterAssignToStackPointer(maybeMarriedContext); + } + } + else { + storeSenderOfFramewithValue(theFP, anOop); + } + if (onCurrentPage) { + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (GIV(stackPage)->headSP); + GIV(framePointer) = (GIV(stackPage)->headFP); + } + else { + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(pageListIsWellFormed()); + assert(validStackPageBaseFrames()); + return 0; +} + + +/* Invoke a quick primitive. + Called under the assumption that primFunctionPtr has been preloaded */ + + /* StackInterpreter>>#externalQuickPrimitiveResponse */ +static sqInt +externalQuickPrimitiveResponse(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt localPrimIndex; + sqInt oop; + + assert(isPrimitiveFunctionPointerAnIndex()); + localPrimIndex = ((sqInt) primitiveFunctionPointer); + assert((localPrimIndex > 0xFF) + && (localPrimIndex < 520)); + + /* Quick return inst vars */ + if (localPrimIndex >= 264) { + oop = longAt((void *)(((longAt(GIV(stackPointer))) + BaseHeaderSize) + ((((usqInt)((localPrimIndex - 264)) << (shiftForWord())))))); + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),oop); + return 1; + } + + /* Quick return constants */ + if (localPrimIndex == 0x100) { + return 1; + } + if (localPrimIndex == 0x101) { + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),GIV(trueObj)); + return 1; + } + if (localPrimIndex == 258) { + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),GIV(falseObj)); + return 1; + } + if (localPrimIndex == 259) { + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),GIV(nilObj)); + return 1; + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),(((usqInt)(localPrimIndex - 261) << 3) | 1)); + return 1; +} + + +/* not inlined for breakpoint value... */ + + /* StackInterpreter>>#failUnbalancedPrimitive */ +static void +failUnbalancedPrimitive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; +} + + +/* Fetch the instance variable at the given index of the given object. Return + the address of first indexable field of resulting array object, or fail if + the instance variable does not contain an indexable bytes or words object. + */ +/* Note: May be called by translated primitive code. */ + + /* StackInterpreter>>#fetchArray:ofObject: */ +void * +fetchArrayofObject(sqInt fieldIndex, sqInt objectPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arrayOop; + + arrayOop = longAt((void *)((objectPointer + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + + /* begin arrayValueOf: */ + if (/* isWordsOrBytes: */ + ((!(arrayOop & (tagMask())))) + && (isWordsOrBytesNonImm(arrayOop))) { + return ((void *) (pointerForOop(arrayOop + BaseHeaderSize))); + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; +} + + +/* Fetch the instance variable at the given index of the given object. Return + the C double precision floating point value of that instance variable, or + fail if it is not a Float. + */ +/* Note: May be called by translated primitive code. */ + + /* StackInterpreter>>#fetchFloat:ofObject: */ +double +fetchFloatofObject(sqInt fieldIndex, sqInt objectPointer) +{ + sqInt floatOop; + + floatOop = longAt((void *)((objectPointer + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + return floatValueOf(floatOop); +} + + +/* Fetch a slot from a married context. Compute the value + of the relevant inst var from the spouse frame's state. + + This method assumes frame pointers have been written back. */ + + /* StackInterpreter>>#fetchPointer:ofMarriedContext: */ +static NoDbgRegParms sqInt +fetchPointerofMarriedContext(sqInt offset, sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + char *callerFP; + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + sqInt senderOop; + char *spouseFP; + StackPage *thePage; + + assert(isContext(aContext)); + assert((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer))); + assert(checkIsStillMarriedContextcurrentFP(aContext, GIV(framePointer))); + + /* method, closureOrNil & receiver need no special handling; only + sender, pc & stackp have to be computed for married contexts. */ + if (offset <= ReceiverIndex) { + if (!(offset <= StackPointerIndex)) { + return longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(offset) << (shiftForWord())))))); + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!offset) { + /* begin ensureCallerContext: */ + callerFP = ((char *)(longAt(spouseFP + FoxSavedFP))); + if (!callerFP) { + /* begin frameCallerContext: */ + assert(isBaseFrame(spouseFP)); + thePage = stackPageAtpages(pageIndexFor(spouseFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + return callerContextOrNil; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP))); + return longAt(callerFP + FoxThisContext); + } + return marryFrameSP( + callerFP, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(spouseFP))), + (spouseFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(spouseFP))->cmNumArgs) + : byteAt((spouseFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + } + if (offset == StackPointerIndex) { + return (((usqInt)(stackPointerIndexForFrame(spouseFP)) << 3) | 1); + } + if (offset == InstructionPointerIndex) { + return instructionPointerForFramecurrentFPcurrentIP(spouseFP, GIV(framePointer), GIV(instructionPointer)); + } + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + return ((((offset - ReceiverIndex) >= 1) && ((offset - ReceiverIndex) <= (stackPointerIndexForFrame(spouseFP)))) + ? /* temporary:in: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? ((offset - (ReceiverIndex + 1)) < ((frameNumArgs = ((mframeCogMethod(spouseFP))->cmNumArgs))) + ? longAt((spouseFP + FoxCallerSavedIP) + ((frameNumArgs - (offset - (ReceiverIndex + 1))) * BytesPerWord)) + : longAt(((spouseFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs - (offset - (ReceiverIndex + 1))) * BytesPerWord))) + : /* itemporary:in: */ + ((offset - (ReceiverIndex + 1)) < ((frameNumArgsUsqInt = byteAt((spouseFP + FoxIFrameFlags) + 1))) + ? longAt((spouseFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt - (offset - (ReceiverIndex + 1))) * BytesPerWord)) + : longAt(((spouseFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt - (offset - (ReceiverIndex + 1))) * BytesPerWord)))) + : GIV(nilObj)); +} + + +/* Return the stackPointer of a Context or BlockContext. + Does not deal with married contexts. Use only for debug + printing or object tracing functions. To obtain an accurate + stack pointer use stackPointerForMaybeMarriedContext: */ + + /* StackInterpreter>>#fetchStackPointerOf: */ +static NoDbgRegParms sqInt +fetchStackPointerOf(sqInt aContext) +{ + sqInt sp; + + sp = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + return 0; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(aContext))); + return (sp >> 3); +} + + +/* Answer if file times, including those from the FilePlugin and + FileAttributesPlugin, should be answered in UTC seconds from the epoch + rather than local seconds. */ + + /* StackInterpreter>>#fileTimesInUTC */ +int +fileTimesInUTC(void) +{ + return ((imageHeaderFlags & 0x200) != 0); +} + + /* StackInterpreter>>#findClassContainingMethod:startingAt: */ +static NoDbgRegParms sqInt +findClassContainingMethodstartingAt(sqInt meth, sqInt classObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classDict; + usqInt classDictSize; + sqInt currClass; + sqInt i; + sqInt methodArray; + usqInt numSlots; + sqInt objOop; + sqInt referent; + sqInt referentSqInt; + + if (/* isOopForwarded: */ + ((!(classObj & (tagMask())))) + && ((!((longAt((void *)(classObj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(classObj)); + currClass = longAt((void *)((classObj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(currClass & (tagMask())))) + && ((!((longAt((void *)(currClass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + currClass = longAt((void *)((currClass + BaseHeaderSize) + (0U << (shiftForWord())))); + } + } + else { + currClass = classObj; + } + do { + assert(!(isForwarded(currClass))); + if (!(addressCouldBeClassObj(currClass))) { + return GIV(nilObj); + } + + /* begin noFixupFollowField:ofObject: */ + classDict = longAt((void *)((currClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(classDict & (tagMask())))) + && ((!((longAt((void *)(classDict))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(classDict)); + referent = longAt((void *)((classDict + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + classDict = referent; + } + assert(!(isForwarded(classDict))); + + /* begin numSlotsOf: */ + assert((classIndexOf(classDict)) > (isForwardedObjectClassIndexPun())); + classDictSize = (((numSlots = byteAt((void *)(classDict + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classDict - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (classDictSize > MethodArrayIndex) { + /* begin noFixupFollowField:ofObject: */ + methodArray = longAt((void *)((classDict + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(methodArray & (tagMask())))) + && ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(methodArray)); + referentSqInt = longAt((void *)((methodArray + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + methodArray = referentSqInt; + } + assert(!(isForwarded(methodArray))); + i = 0; + while (i < (classDictSize - SelectorStart)) { + if (meth == (noFixupFollowFieldofObject(i, methodArray))) { + return currClass; + } + i += 1; + } + } + + /* begin noFixupSuperclassOf: */ + /* begin noFixupFollowField:ofObject: */ + objOop = longAt((void *)((currClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + referentSqInt = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referentSqInt & (tagMask())))) + && ((!((longAt((void *)(referentSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referentSqInt = longAt((void *)((referentSqInt + BaseHeaderSize) + (0U << (shiftForWord())))); + } + objOop = referentSqInt; + } + currClass = objOop; + } while(!(currClass == GIV(nilObj))); + return currClass; +} + + /* StackInterpreter>>#findClassOfMethod:forReceiver: */ +sqInt +findClassOfMethodforReceiver(sqInt meth, sqInt rcvr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt rclass; + sqInt tagBits; + + if ((/* addressCouldBeOop: */ + (((rcvr & (tagMask())) != 0)) + || (addressCouldBeObj(rcvr))) + && (!(/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))))) { + rclass = findClassContainingMethodstartingAt(meth, /* fetchClassOf: */ + ((tagBits = rcvr & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(rcvr))); + if (rclass != GIV(nilObj)) { + return rclass; + } + } + if (!((addressCouldBeObj(meth)) + && (((byteAt((void *)(meth + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return GIV(nilObj); + } + return findClassContainingMethodstartingAt(meth, safeMethodClassOf(meth)); +} + + +/* Answer the frame above theFP (adjacent frame nearest head end). + If theFP is the head frame answer 0. */ + + /* StackInterpreter>>#findFrameAbove:inPage: */ +static NoDbgRegParms char * +findFrameAboveinPage(char *theFP, StackPage *thePage) +{ + char *callerFP; + char *fp; + + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == theFP) { + return 0; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == theFP) { + return fp; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + return 0; +} + + +/* Answer the highest priority of runnable process without altering any + lists. Do *not* update highestRunnableProcessPriority. + Answer 0 if no runnable process can be found. */ + + /* StackInterpreter>>#findHighestPriority */ +static sqInt +findHighestPriority(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + sqInt objOop; + sqInt p; + sqInt processList; + sqInt schedLists; + + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + schedLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + p = (GIV(highestRunnableProcessPriority) + ? GIV(highestRunnableProcessPriority) + : (/* begin numSlotsOf: */ + assert((classIndexOf(schedLists)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(schedLists + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(schedLists - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))); + while (((p -= 1)) >= 0) { + processList = longAt((void *)((schedLists + BaseHeaderSize) + ((((usqInt)(p) << (shiftForWord())))))); + + /* begin isEmptyList: */ + assert(!(isForwarded(processList))); + if (!((longAt((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj))) { + return p + 1; + } + } + return 0; +} + + /* StackInterpreter>>#findHomeForContext: */ +static NoDbgRegParms sqInt +findHomeForContext(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt closureOrNil; + + if (!(/* isContext: */ + ((!(aContext & (tagMask())))) + && (((longAt((void *)(aContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + return null; + } + closureOrNil = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord())))))); + assert((isPointers(closureOrNil)) + && ((closureOrNil == (nilObject())) + || ((numSlotsOf(closureOrNil)) >= ClosureFirstCopiedValueIndex))); + if ((((closureOrNil & (tagMask())) != 0)) + || (closureOrNil == GIV(nilObj))) { + return aContext; + } + + /* robustness in the presence of bugs */ + if ((((longAt((void *)(closureOrNil))) & (classIndexMask())) != ClassBlockClosureCompactIndex) + && (((longAt((void *)(closureOrNil))) & (classIndexMask())) != ClassFullBlockClosureCompactIndex)) { + return null; + } + return findHomeForContext(longAt((void *)((closureOrNil + BaseHeaderSize) + ((((usqInt)(ClosureOuterContextIndex) << (shiftForWord()))))))); +} + + +/* See findUnwindThroughContext:. Alas this is mutually recursive with + findMethodWithPrimitive:FromFP:SP:ThroughContext: instead of iterative. + We're doing the simplest thing that could possibly work. Niceties can + wait. */ +/* Being mutually-recursive with findMethodWithPrimitive:FromFP:UpToContext: + gives the author's type inference algorithm headaches. Wimp out by + declaring the return type. + */ + + /* StackInterpreter>>#findMethodWithPrimitive:FromContext:UpToContext: */ +static NoDbgRegParms sqInt +findMethodWithPrimitiveFromContextUpToContext(sqInt primitive, sqInt senderContext, sqInt homeContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt senderContextSqInt; + sqInt senderOop; + char *startFP; + sqInt theContext; + char *theFP; + char *theFPAbove; + sqInt theMethod; + sqInt theMethodSqInt; + StackPage *thePage; + char *theSP; + + assert((senderContext == (nilObject())) + || (isContext(senderContext))); + assert((homeContext == (nilObject())) + || (isContext(homeContext))); + theContext = senderContext; + while (1) { + if (theContext == GIV(nilObj)) { + return theContext; + } + if (((((longAt((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) break; + if (theContext == homeContext) { + return 0; + } + if (!((primitive == 0) + || ((longAt((void *)((theContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord()))))))) != GIV(nilObj)))) { + /* begin followObjField:ofObject: */ + theMethod = longAt((void *)((theContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + assert(isNonImmediate(theMethod)); + if ((!((longAt((void *)(theMethod))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + theMethod = fixFollowedFieldofObjectwithInitialValue(MethodIndex, theContext, theMethod); + } + if ((primitiveIndexOf(theMethod)) == primitive) { + return theContext; + } + } + + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SenderIndex, theContext, objOop); + } + theContext = objOop; + } + if (isWidowedContext(theContext)) { + return GIV(nilObj); + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((theContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + startFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin findMethodWithPrimitive:FromFP:UpToContext: */ + theFP = startFP; + theFPAbove = startFP; + do { + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) + && (homeContext == (longAt(theFP + FoxThisContext)))) { + return 0; + } + if (!((primitive == 0) + || (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0)))) { + theMethodSqInt = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + if ((primitiveIndexOf(theMethodSqInt)) == primitive) { + if (theFP == theFPAbove) { + theSP = findSPOfon(theFP, stackPageFor(theFP)); + } + else { + /* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFPAbove))); + theSP = (theFPAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFPAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFPAbove))->cmNumArgs) + : byteAt((theFPAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + return longAt(theFP + FoxThisContext); + } + return marryFrameSP(theFP, theSP); + } + } + theFPAbove = theFP; + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + } while(theFP != 0); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFPAbove)); + thePage = stackPageAtpages(pageIndexFor(theFPAbove), GIV(pages)); + senderContextSqInt = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(senderContextSqInt)); + assert((senderContextSqInt == (nilObject())) + || (isContext(followMaybeForwarded(senderContextSqInt)))); + if (!(/* isContext: */ + ((!(senderContextSqInt & (tagMask())))) + && (((longAt((void *)(senderContextSqInt))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + return GIV(nilObj); + } + return findMethodWithPrimitiveFromContextUpToContext(primitive, senderContextSqInt, homeContext); +} + + /* StackInterpreter>>#findSelectorOfMethod: */ +sqInt +findSelectorOfMethod(sqInt aMethodOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classDict; + usqInt classDictSize; + sqInt classObj; + sqInt homeMethod; + sqInt i; + sqInt methodArray; + usqInt numSlots; + sqInt objOop; + sqInt referent; + sqInt selector; + + if (!(addressCouldBeObj(aMethodOop))) { + return GIV(nilObj); + } + if ((!((longAt((void *)(aMethodOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + return findSelectorOfMethod(followForwarded(aMethodOop)); + } + if (!(/* isOopCompiledMethod: */ + ((!(aMethodOop & (tagMask())))) + && (((byteAt((void *)(aMethodOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return GIV(nilObj); + } + homeMethod = homeMethodOf(aMethodOop); + if ((selector = maybeSelectorOfMethod(homeMethod))) { + return selector; + } + classObj = safeMethodClassOf(homeMethod); + if (addressCouldBeClassObj(classObj)) { + /* begin noFixupFollowField:ofObject: */ + classDict = longAt((void *)((classObj + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(classDict & (tagMask())))) + && ((!((longAt((void *)(classDict))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(classDict)); + referent = longAt((void *)((classDict + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + classDict = referent; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(classDict)) > (isForwardedObjectClassIndexPun())); + classDictSize = (((numSlots = byteAt((void *)(classDict + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classDict - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (classDictSize > MethodArrayIndex) { + /* begin noFixupFollowField:ofObject: */ + methodArray = longAt((void *)((classDict + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(methodArray & (tagMask())))) + && ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(methodArray)); + referent = longAt((void *)((methodArray + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + methodArray = referent; + } + i = 0; + while (i < (classDictSize - SelectorStart)) { + if (homeMethod == (noFixupFollowFieldofObject(i, methodArray))) { + /* begin noFixupFollowField:ofObject: */ + objOop = longAt((void *)((classDict + BaseHeaderSize) + ((((usqInt)((i + SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(objOop)); + referent = longAt((void *)((objOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + objOop = referent; + } + return objOop; + } + i += 1; + } + } + } + return GIV(nilObj); +} + + +/* Search for the stack pointer for theFP. This points to the hottest item on + the frame's stack. + DO NOT CALL THIS WITH theFP == localFP OR theFP == framePointer! */ + + /* StackInterpreter>>#findSPOf:on: */ +static NoDbgRegParms char * +findSPOfon(char *theFP, StackPage *thePage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *aFrame; + char *prevFrame; + char *startFrame; + char *theSP; + + assert(!((isFree(thePage)))); + startFrame = (thePage->headFP); + + /* begin findSPOrNilOf:on:startingFrom: */ + prevFrame = ((char *) 0); + if (startFrame == theFP) { + if (((thePage->headSP)) >= startFrame) { + /* If the SP is invalid return the pointer to the receiver field. */ + theSP = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + goto l1; + } + + /* Skip the instruction pointer on top of stack of inactive pages. */ + theSP = (thePage == GIV(stackPage) + ? (thePage->headSP) + : ((thePage->headSP)) + BytesPerWord); + goto l1; + } + aFrame = startFrame; + while (1) { + prevFrame = aFrame; + aFrame = ((char *)(longAt(aFrame + FoxSavedFP))); + if (!(aFrame != 0)) break; + if (theFP == aFrame) { + /* begin frameCallerSP: */ + assert(!(isBaseFrame(prevFrame))); + theSP = (prevFrame + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(prevFrame + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(prevFrame))->cmNumArgs) + : byteAt((prevFrame + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + goto l1; + } + } + theSP = null; + /* end findSPOrNilOf:on:startingFrom: */ +l1: + if (theSP) { + return theSP; + } + error("did not find theFP in stack page"); + return 0; +} + + +/* Search for either an unwind-protect (activation of method with primitive + 198, PrimNumberUnwindMarker) + or homeContext along the sender chain, which ever is found first. Return + values: 0 home context was found on sender chain with no intervening + unwind-protects nilObj home context could not be found => cannotReturn + context the context of an intervening unwind-protect implies home context + was found */ +/* Almost always (98%) the home is on the same page, in which case we know it + will be found. + */ + + /* StackInterpreter>>#findUnwindThroughContext: */ +static NoDbgRegParms sqInt +findUnwindThroughContext(sqInt homeContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxtOrNilOrZero; + sqInt onSamePage; + sqInt senderContext; + char *theFP; + char *theFPAbove; + sqInt theMethod; + sqInt theMethodSqInt; + StackPage *thePage; + char *theSP; + + onSamePage = (/* isStillMarriedContext: */ + (((((longAt((void *)((homeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(homeContext)))) + && ((pageIndexFor(GIV(framePointer))) == (pageIndexFor(frameOfMarriedContext(homeContext)))); + + /* Since nothing changes we don't need to internalize. */ + + /* begin findMethodWithPrimitive:FromFP:UpToContext: */ + theFP = GIV(framePointer); + theFPAbove = GIV(framePointer); + do { + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) + && (homeContext == (longAt(theFP + FoxThisContext)))) { + ctxtOrNilOrZero = 0; + goto l1; + } + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0))) { + theMethodSqInt = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + if ((primitiveIndexOf(theMethodSqInt)) == PrimNumberUnwindMarker) { + if (theFP == theFPAbove) { + theSP = findSPOfon(theFP, stackPageFor(theFP)); + } + else { + /* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFPAbove))); + theSP = (theFPAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFPAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFPAbove))->cmNumArgs) + : byteAt((theFPAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + ctxtOrNilOrZero = longAt(theFP + FoxThisContext); + goto l1; + } + ctxtOrNilOrZero = marryFrameSP(theFP, theSP); + goto l1; + } + } + theFPAbove = theFP; + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + } while(theFP != 0); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFPAbove)); + thePage = stackPageAtpages(pageIndexFor(theFPAbove), GIV(pages)); + senderContext = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(senderContext)); + assert((senderContext == (nilObject())) + || (isContext(followMaybeForwarded(senderContext)))); + if (!(/* isContext: */ + ((!(senderContext & (tagMask())))) + && (((longAt((void *)(senderContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + ctxtOrNilOrZero = GIV(nilObj); + goto l1; + } + ctxtOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(PrimNumberUnwindMarker, senderContext, homeContext); + /* end findMethodWithPrimitive:FromFP:UpToContext: */ +l1: + assert(!((onSamePage + && (ctxtOrNilOrZero == (nilObject()))))); + if (!ctxtOrNilOrZero) { + theMethod = longAt((void *)((homeContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + if ((primitiveIndexOf(theMethod)) == PrimNumberUnwindMarker) { + return homeContext; + } + return 0; + } + + /* If an unwind was found, can the home context be found also? No need to look if on the same page. + No need to look if cannot return (ctxtOrNilOrZero = objectMemory nilObject) */ + if (!(onSamePage + || (ctxtOrNilOrZero == GIV(nilObj)))) { + if ((findMethodWithPrimitiveFromContextUpToContext(0, ctxtOrNilOrZero, homeContext)) == GIV(nilObj)) { + return GIV(nilObj); + } + } + return ctxtOrNilOrZero; +} + + /* StackInterpreter>>#flush */ +void +flush(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fflush(GIV(transcript)); +} + + +/* methodObj is a CompiledMethod. If it contains an external primitive, + flush the function address and session ID of the CM. Answer the prim + index for the benefit of subclass overrides. */ + + /* StackInterpreter>>#flushExternalPrimitiveOf: */ +static NoDbgRegParms sqInt +flushExternalPrimitiveOf(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt headerSqInt; + sqInt lit; + usqInt numSlots; + sqInt primIdx; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + headerSqInt = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* primitiveIndexOfMethod:header: */ + if (((header & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((header >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIdx = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIdx = 0; + } + if ((primIdx == PrimNumberExternalCall) + && (((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) > 0)) { + lit = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + if ((/* isArray: */ + ((!(lit & (tagMask())))) + && (((byteAt((void *)(lit + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(lit)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(lit + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(lit - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(lit)) + && (!(isForwarded(lit)))); + assert(validStorePointerUncheckedArgs(2, lit, ConstZero)); + longAtput((void *)((lit + BaseHeaderSize) + (2U << (shiftForWord()))),ConstZero); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(lit)) + && (!(isForwarded(lit)))); + assert(validStorePointerUncheckedArgs(3, lit, ConstZero)); + longAtput((void *)((lit + BaseHeaderSize) + (3U << (shiftForWord()))),ConstZero); + } + } + + /* If not, something's broken */ + return primIdx; +} + + +/* Flush the method caches of references to oldMethod. */ + + /* StackInterpreter>>#flushMethodCacheForMethod: */ +static NoDbgRegParms void +flushMethodCacheForMethod(sqInt oldMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt probe; + + probe = 0; + for (i = 1; i <= MethodCacheEntries; i += 1) { + if ((GIV(methodCache)[probe + MethodCacheMethod]) == oldMethod) { + GIV(methodCache)[probe + MethodCacheSelector] = 0; + } + probe += MethodCacheEntrySize; + } + flushExternalPrimitiveOf(oldMethod); +} + + +/* follow pointers in the current stack frame up to theSP. */ + + /* StackInterpreter>>#followForwardedFrameContents:stackPointer: */ +static NoDbgRegParms void +followForwardedFrameContentsstackPointer(char *theFP, char *theSP) +{ + sqInt oop; + char *ptr; + sqInt referent; + char *toDoLimit; + char *toDoLimit1; + + toDoLimit1 = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + for (ptr = theSP; ptr <= toDoLimit1; ptr += BytesPerWord) { + oop = longAt(ptr); + if (((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + longAtput(ptr,followForwarded(oop)); + } + } + toDoLimit = theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord()))))); + for (ptr = ((theFP + FoxCallerSavedIP) + BytesPerWord); ptr <= toDoLimit; ptr += BytesPerWord) { + oop = longAt(ptr); + if (((!(oop & (tagMask())))) + && ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + longAtput(ptr,oop); + } + } + + /* If we're in a block then also follow fields in the stacked closure as it may be activated again. */ + if (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0)) { + assert(oop == (longAt(theFP + (frameStackedReceiverOffset(theFP))))); + followForwardedObjectFieldstoDepth(oop, 0); + } + assert(!(isForwarded(frameMethodObject(theFP)))); + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(!(isForwarded(frameContext(theFP)))); + } +} + + +/* Force an interrupt check ASAP. + Must set the stack page's limit before stackLimit to avoid + a race condition if this is called from an interrupt handler. */ + + /* StackInterpreter>>#forceInterruptCheck */ +sqInt +forceInterruptCheck(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*iccFunc)(); + StackPage *thePage; + + + /* Do _not_ set stackLimit until the stack system has been initialized. + stackLimit is the initialization flag for the stack system. */ + if (!GIV(stackLimit)) { + return null; + } + thePage = GIV(stackPage); + if ((thePage) + && (thePage != 0)) { + (thePage->stackLimit = ((char *) (((usqInt) -1)))); + } + GIV(stackLimit) = ((char *) (((usqInt) -1))); + sqLowLevelMFence(); + + /* There is a race condition if we test the function and then dereference + it a second time to call it. This is called from interrupt code but at the + same time other code could be clearing the interruptCheckChain via + setInterruptCheckChain:. */ + if (((iccFunc = interruptCheckChain))) { + iccFunc(); + } + GIV(statForceInterruptCheck) += 1; + return 0; +} + + +/* Answer the SP of the caller provided theFP is not a base frame. + This points to the hottest item on the caller frame's stack. */ + + /* StackInterpreter>>#frameCallerSP: */ +static NoDbgRegParms char * +frameCallerSP(char *theFP) +{ + assert(!(isBaseFrame(theFP))); + return (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; +} + + /* StackInterpreter>>#frameContext: */ +static NoDbgRegParms sqInt +frameContext(char *theFP) +{ + return longAt(theFP + FoxThisContext); +} + + /* StackInterpreter>>#frameOfMarriedContext: */ +static NoDbgRegParms char * +frameOfMarriedContext(sqInt aContext) +{ + sqInt senderOop; + + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + + /* begin withoutSmallIntegerTags: */ + assert((((senderOop) & 7) == 1)); + return ((char *)(senderOop - (smallIntegerTag()))); +} + + +/* Answer the offset in bytes from the the frame pointer to its stacked + receiver. The receiver of a message send or the closure of a block + activation is + always on the stack above any arguments and the frame itself. See the + diagram in StackInterpreter class>>initializeFrameIndices. */ + + /* StackInterpreter>>#frameStackedReceiverOffset: */ +static NoDbgRegParms sqInt +frameStackedReceiverOffset(char *theFP) +{ + return (FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))); +} + + +/* Answer the stacked receiver given the frame's argument count. + The receiver of a message send or the closure of a block activation is + always on the stack above any arguments and the frame itself. See the + diagram in StackInterpreter class>>initializeFrameIndices. */ + + /* StackInterpreter>>#frameStackedReceiver:numArgs: */ +static NoDbgRegParms sqInt +frameStackedReceivernumArgs(char *theFP, sqInt numArgs) +{ + return longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)(numArgs) << (shiftForWord())))))); +} + + +/* Free any untraced stack pages. */ + + /* StackInterpreter>>#freeUntracedStackPages */ +static void +freeUntracedStackPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + StackPage *thePage; + + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((!(isFree(thePage))) + && (((thePage->trace)) == StackPageUnreached)) { + assert(noMarkedContextsOnPage(thePage)); + freeStackPage(thePage); + } + assert(((thePage->trace = StackPageTraceInvalid)) != 0); + } +} + + +/* Repaint the entire smalltalk screen, ignoring the affected rectangle. Used + in some platform's code when the Smalltalk window is brought to the front + or uncovered. + */ + + /* StackInterpreter>>#fullDisplayUpdate */ +sqInt +fullDisplayUpdate(void) +{ + updateDisplayLeftTopRightBottom(0, 0, displayWidth, displayHeight); + ioForceDisplayUpdate(); + return 0; +} + + +/* Find an actual function pointer for this primitiveIndex. This is an + opportunity to specialise the prim for the relevant class (format for + example). Default for now is simply the entry in the base primitiveTable. */ + + /* StackInterpreter>>#functionPointerFor:inClass: */ +static NoDbgRegParms void +(*functionPointerForinClass(sqInt primIdx, sqInt theClass))(void) +{ + return ((((usqInt)primIdx)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primIdx]); +} + + +/* Answer an array of flags indicating various optional features of the Cog + VM. If the bit is set then... + Bit 0: supports two bytecode sets (MULTIPLEBYTECODESETS) + Bit 1: supports immutablity (IMMUTABILITY) + Bit 2: suffers from a UNIX setitimer signal-based heartbeat + Bit 3: the VM provides cross-platform bit-identical floating point + Bit 4: the VM can catch exceptions in FFI calls and answer them as + primitive failures + Bit 5: the VM has suspend primitives 568 & 578 which back up a process to + before the wait if it was waiting on a condition variable + Bit 6: the VM is a threading VM */ + + /* StackInterpreter>>#getCogVMFeatureFlags */ +static sqInt +getCogVMFeatureFlags(void) +{ + return (((usqInt)((((((1 + ( +#if IMMUTABILITY + (IMMUTABILITY + ? 2 + : 0) +#else + 0 +#endif + )) + ( +#if ITIMER_HEARTBEAT + 4 +#else + 0 +#endif + )) + ( +#if BIT_IDENTICAL_FLOATING_POINT + 8 +#else + 0 +#endif + )) + ((ioCanCatchFFIExceptions() + ? 16 + : 0))) + 32) + 64) << 3) | 1); +} + + +/* Answer the errorCode object to supply to a failing primitive method that + accepts one. + If there is a primitive error table and the primFailCode is a valid index + there-in answer + the corresponding entry in the table, otherwise simply answer the code as + an integer. + */ + + /* StackInterpreter>>#getErrorObjectFromPrimFailCode */ +static sqInt +getErrorObjectFromPrimFailCode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classIndex; + usqInt clone; + usqInt errObj; + sqInt i; + usqInt newObj; + usqInt numBytes; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objOop; + sqInt table; + sqInt valuePointer; + + if (GIV(primFailCode) > 0) { + table = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(PrimitiveErrorTableIndex) << (shiftForWord())))))); + if (GIV(primFailCode) <= ((/* begin numSlotsOf: */ + assert((classIndexOf(table)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(table + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(table - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots)))) { + /* begin followField:ofObject: */ + objOop = longAt((void *)((table + BaseHeaderSize) + ((((usqInt)((GIV(primFailCode) - 1)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(GIV(primFailCode) - 1, table, objOop); + } + errObj = objOop; + + /* If there's a clonable object in the table at that index, + answer a clone of the error object with the second slot set to the value of secondaryErrorCode. */ + if (((byteAt((void *)(errObj + (formatFieldByteOffset())))) & (formatMask())) == (nonIndexablePointerFormat())) { + /* begin cloneErrorObj: */ + /* begin numSlotsOf: */ + assert((classIndexOf(errObj)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(errObj + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(errObj - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + classIndex = (longAt((void *)(errObj))) & (classIndexMask()); + + /* begin eeInstantiateAnySmallClassIndex:format:numSlots: */ + assert((numSlots >= 0) + && ((classIndex != 0) + && ((classAtIndex(classIndex)) != GIV(nilObj)))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(classAtIndex(classIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(numSlots < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((numSlots < 1 + ? 8 /* allocationUnit */ + : numSlots * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + clone = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) numSlots)) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + classIndex); + GIV(freeStart) += numBytes; + clone = newObj; + /* end eeInstantiateAnySmallClassIndex:format:numSlots: */ +l1: + for (i = 0; i < numSlots; i += 1) { + valuePointer = longAt((void *)((errObj + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(clone)) + && (!(isForwarded(clone)))); + assert(validStorePointerUncheckedArgs(i, clone, valuePointer)); + longAtput((void *)((clone + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + if ((numSlots > 2) + && (GIV(primFailCode) == PrimErrFFIException)) { + valuePointer = positive64BitIntegerFor(((usqLong) GIV(secondaryErrorCode))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(clone)) + && (!(isForwarded(clone)))); + assert(validStorePointerUncheckedArgs(1, clone, valuePointer)); + longAtput((void *)((clone + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(exceptionPC)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(clone)) + && (!(isForwarded(clone)))); + assert(validStorePointerUncheckedArgs(2, clone, valuePointer)); + longAtput((void *)((clone + BaseHeaderSize) + (2U << (shiftForWord()))),valuePointer); + } + else { + valuePointer = signed64BitIntegerFor(GIV(secondaryErrorCode)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(clone)) + && (!(isForwarded(clone)))); + assert(validStorePointerUncheckedArgs(1, clone, valuePointer)); + longAtput((void *)((clone + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + } + errObj = clone; + } + return errObj; + } + } + return (((usqInt)GIV(primFailCode) << 3) | 1); +} + + +/* If bit 0 of the imageHeaderFlags is set then the system should use the + full screen. + */ + + /* StackInterpreter>>#getFullScreenFlag */ +int +getFullScreenFlag(void) +{ + return ((imageHeaderFlags & 1) != 0); +} + + /* StackInterpreter>>#getInterruptKeycode */ +sqInt +getInterruptKeycode(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(interruptKeycode); +} + + /* StackInterpreter>>#getInterruptPending */ +sqInt +getInterruptPending(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(interruptPending); +} + + /* StackInterpreter>>#getNextWakeupUsecs */ +usqLong +getNextWakeupUsecs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(nextWakeupUsecs); +} + + /* StackInterpreter>>#getSavedWindowSize */ +sqInt +getSavedWindowSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(savedWindowSize); +} + + +/* Answer the next 16 bit word read from aFile, byte-swapped according to the + swapFlag. + */ + + /* StackInterpreter>>#getShortFromFile:swap: */ +static NoDbgRegParms usqInt +getShortFromFileswap(sqImageFile aFile, sqInt swapFlag) +{ + unsigned short w; + + w = 0; + sqImageFileRead((&w), sizeof(unsigned short), 1, aFile); + return (swapFlag + ? ((((usqInt)(w)) >> 8) & 0xFF) | (((w & 0xFF) << 8)) + : w); +} + + +/* Answer the screen size to write to the snapshot. + If the actual screen size is zero then write the savedWindowSize instead. */ + + /* StackInterpreter>>#getSnapshotScreenSize */ +static sqInt +getSnapshotScreenSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt screenSize; + + screenSize = ioScreenSize(); + if (((screenSize & 0xFFFF) == 0) + || ((((usqInt)(screenSize)) >> 16) == 0)) { + screenSize = GIV(savedWindowSize); + } + return screenSize; +} + + +/* For Alien FFI */ + + /* StackInterpreter>>#getStackPointer */ +sqInt * +getStackPointer(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return ((sqInt *) GIV(stackPointer)); +} + + +/* return the global session ID value */ + + /* StackInterpreter>>#getThisSessionID */ +sqInt +getThisSessionID(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(globalSessionID); +} + + /* StackInterpreter>>#getTranscript */ +FILE * +getTranscript(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(transcript); +} + + +/* Handle a send fault that is due to a send using a forwarded selector. + Unforward the selector and follow the current method and special + selectors array to unforward the source of the forwarded selector. */ + + /* StackInterpreter>>#handleForwardedSelectorFaultFor: */ +static NoDbgRegParms sqInt +handleForwardedSelectorFaultFor(sqInt selectorOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt referent; + + assert(isOopForwarded(selectorOop)); + followForwardedFieldsInCurrentMethod(); + followForwardedObjectFieldstoDepth(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord())))))), 0); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(selectorOop)); + referent = longAt((void *)((selectorOop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + return referent; +} + + +/* Handle a send fault that may be due to a send to a forwarded object. + Unforward the receiver on the stack and answer its actual class. */ + + /* StackInterpreter>>#handleForwardedSendFaultForTag: */ +static NoDbgRegParms sqInt +handleForwardedSendFaultForTag(sqInt classTag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt rcvr; + sqInt referent; + sqInt tagBits; + + assert(isForwardedClassTag(classTag)); + rcvr = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* should *not* be a super send, so the receiver should be forwarded. */ + assert(isOopForwarded(rcvr)); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(rcvr)); + referent = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + rcvr = referent; + + /* stackValue:put: */ + longAtput(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord),rcvr); + followForwardedFrameContentsstackPointer(GIV(framePointer), GIV(stackPointer) + ((GIV(argumentCount) + 1) * BytesPerWord)); + if (isPointers(/* frameReceiver: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? longAt(GIV(framePointer) + FoxMFReceiver) + : longAt(GIV(framePointer) + FoxIFReceiver)))) { + followForwardedObjectFieldstoDepth(/* frameReceiver: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? longAt(GIV(framePointer) + FoxMFReceiver) + : longAt(GIV(framePointer) + FoxIFReceiver)), 0); + } + return /* fetchClassTagOf: */ + ((tagBits = rcvr & (tagMask())) + ? tagBits + : (longAt((void *)(rcvr))) & (classIndexMask())); +} + + +/* Handle a special send fault that may be due to a special selector + send accessing a forwarded object. obj is forwarded. + Unforward stack contents and and inst vars and answer obj's target. */ + + /* StackInterpreter>>#handleSpecialSelectorSendFaultFor:fp:sp: */ +static NoDbgRegParms sqInt +handleSpecialSelectorSendFaultForfpsp(sqInt obj, char *theFP, char *theSP) +{ + sqInt rcvr; + sqInt referent; + + assert(isOopForwarded(obj)); + followForwardedFrameContentsstackPointer(theFP, theSP); + rcvr = /* frameReceiver: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? longAt(theFP + FoxMFReceiver) + : longAt(theFP + FoxIFReceiver)); + if (/* isPointers: */ + ((!(rcvr & (tagMask())))) + && (((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) { + followForwardedObjectFieldstoDepth(rcvr, 0); + } + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + return referent; +} + + +/* Check for stack overflow, moving frames to another stack if so. + This should *only* be sent from checkForStackOverflow. */ + + /* StackInterpreter>>#handleStackOverflow */ +static NeverInline void +handleStackOverflow(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + char *callerFP; + char *callerFP1; + StackPage *newPage; + sqInt overflowCount; + char *overflowLimitAddress; + sqInt sourceCode; + char *theFP; + StackPage *thePage; + + callerFP = ((char *) 0); + assert(GIV(stackPointer) < ((GIV(stackPage)->realStackLimit))); + + /* begin traceStackOverflow */ + if (recordOverflowTrace()) { + sourceCode = ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? CSFromMachineCode + : CSFromInterpreter); + + /* begin recordTrace:source:thing:extra: */ + assert(sourceCode < ((1U << TraceTypeShift))); + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceStackOverflow) << TraceTypeShift))))) + sourceCode); + GIV(traceLog)[GIV(traceLogIndex) + 1] = 0; + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + } + if ((recordPrimTrace()) + && (!primTracePluginName)) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TraceStackOverflow << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + GIV(statStackOverflow) += 1; + + /* The stack has overflowed this page. If the system is executing some recursive algorithm, + e.g. fibonacci, then the system could thrash overflowing the stack if the call soon returns + back to the current page. To avoid thrashing, since overflow is quite slow, we can move + more than one frame. The idea is to record which page has overflowed, and the first + time it overflows move one frame, the second time two frames, and so on. We move no + more frames than would leave the page half occupied. */ + theFP = GIV(framePointer); + if (GIV(stackPage) == GIV(overflowedPage)) { + overflowLimitAddress = ((GIV(stackPage)->baseAddress)) - GIV(overflowLimit); + overflowCount = (GIV(extraFramesToMoveOnOverflow) += 1); + while ((((overflowCount -= 1)) >= 0) + && ((((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) < overflowLimitAddress) + && (!((longAt(callerFP + FoxSavedFP)) == 0)))) { + theFP = callerFP; + } + } + else { + GIV(overflowedPage) = GIV(stackPage); + GIV(extraFramesToMoveOnOverflow) = 0; + } + + /* begin ensureCallerContext: */ + callerFP1 = ((char *)(longAt(theFP + FoxSavedFP))); + if (!callerFP1) { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerContextOrNil = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + goto l2; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP1 + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP1 + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP1 + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP1))); + + /* frameContext: */ + goto l2; + } + marryFrameSP( + callerFP1, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFP))), + (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + /* end ensureCallerContext: */ +l2: + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l1; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l1: + moveFramesInthroughtoPage(GIV(stackPage), theFP, newPage); + + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (newPage->headSP); + GIV(framePointer) = (newPage->headFP); + assert(validInstructionPointerinFrame(GIV(instructionPointer) + 1, GIV(framePointer))); + assert((!(frameHasContext(GIV(framePointer)))) + || (isContext(frameContext(GIV(framePointer))))); +} + + +/* The stackPointer is below the stackLimit. This is either because of a + stack overflow or the setting of stackLimit to indicate a possible + interrupt. Check for stackOverflow and interrupts anddeal with each + appropriately. Answer if a context switch occurred. */ + + /* StackInterpreter>>#handleStackOverflowOrEventAllowContextSwitch: */ +static NoDbgRegParms sqInt +handleStackOverflowOrEventAllowContextSwitch(sqInt mayContextSwitch) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin checkForStackOverflow */ + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (GIV(stackPointer) < ((GIV(stackPage)->realStackLimit))) { + handleStackOverflow(); + } + + /* If the stackLimit differs from the realStackLimit then the stackLimit + has been set to indicate an event or interrupt that needs servicing. */ + if (GIV(stackLimit) == ((GIV(stackPage)->realStackLimit))) { + return 0; + } + return checkForEventsMayContextSwitch(mayContextSwitch); +} + + +/* This is a C implementation needed by ioSetMaxExtSemTableSize + and e.g. stackPageByteSize. */ + + /* StackInterpreter>>#highBit: */ +sqInt +highBit(usqInt anUnsignedValue) +{ + sqInt bitNo; + usqInt shifted; + + + /* so it shows up in senders... */ + shifted = anUnsignedValue; + bitNo = 0; +# if BytesPerWord > 4 + if (!(shifted < (0x100000000LL))) { + shifted = (shifted) >> 32; + bitNo += 32; + } +# endif + + if (!(shifted < (0x10000))) { + shifted = (shifted) >> 16; + bitNo += 16; + } + if (!(shifted < (0x100))) { + shifted = (shifted) >> 8; + bitNo += 8; + } + if (!(shifted < (16))) { + shifted = (shifted) >> 4; + bitNo += 4; + } + if (!(shifted < (4))) { + shifted = (shifted) >> 2; + bitNo += 2; + } + if (!(shifted < (2))) { + shifted = (shifted) >> 1; + bitNo += 1; + } + + /* shifted 0 or 1 now */ + return bitNo + shifted; +} + + +/* With the full block implementation a CompiledBlock's method is found along + the chain through the last literal. See CompiledBlock>>#method. Answer + the home mehtod, or nilObj if it cannot be found. */ + + /* StackInterpreter>>#homeMethodOf: */ +sqInt +homeMethodOf(sqInt aMethodOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt lastLiteral; + sqInt methodChain; + + assert(isOopCompiledMethod(aMethodOop)); + methodChain = aMethodOop; + while (1) { + lastLiteral = ultimateLiteralOf(methodChain); + if (!(/* isOopCompiledMethod: */ + ((!(lastLiteral & (tagMask())))) + && (((byteAt((void *)(lastLiteral + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return methodChain; + } + if (!(/* isOopCompiledMethod: */ + ((!(lastLiteral & (tagMask())))) + && (((byteAt((void *)(lastLiteral + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return GIV(nilObj); + } + methodChain = lastLiteral; + } + return 0; +} + + +/* If thePage is the stackPage and the stackPointer and/or the framePointer + are pointing within it, + answer if thePage's heapSP and headFP are equal to the stackPointer and + framePointer respectively. + */ + + /* StackInterpreter>>#ifCurrentStackPageHasValidHeadPointers: */ +static NoDbgRegParms sqInt +ifCurrentStackPageHasValidHeadPointers(StackPage *thePage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (thePage == GIV(stackPage)) { + if (/* addressIsInPage: */ + (((thePage->lastAddress)) < GIV(framePointer)) + && (GIV(framePointer) < ((thePage->baseAddress)))) { + if (((thePage->headFP)) != GIV(framePointer)) { + return 0; + } + } + if (/* addressIsInPage: */ + (((thePage->lastAddress)) < GIV(stackPointer)) + && (GIV(stackPointer) < ((thePage->baseAddress)))) { + if (((thePage->headSP)) != GIV(stackPointer)) { + return 0; + } + } + } + return 1; +} + + /* StackInterpreter>>#iframeMethod: */ +static NoDbgRegParms usqInt +iframeMethod(char *theFP) +{ + return longAt(theFP + FoxMethod); +} + + +/* Return the equivalent of + aClass includesBehavior: aSuperclass. + Note: written for efficiency and better inlining (only 1 temp) */ + + /* StackInterpreter>>#includesBehavior:ThatOf: */ +sqInt +includesBehaviorThatOf(sqInt aClass, sqInt aSuperclass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt theClass; + + if (aSuperclass == GIV(nilObj)) { + return 0; + } + theClass = aClass; + while (1) { + if (theClass == aSuperclass) { + return 1; + } + if (!(theClass != GIV(nilObj))) break; + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((theClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, theClass, objOop); + } + theClass = objOop; + } + return 0; +} + + +/* Initialize the stack pages and then invoke continuation. Use alloca'ed + memory so that + when we have a JIT its stack pointer will be on the native stack since + alloca allocates + memory on the stack. Certain thread systems use the native stack pointer + as the + frame ID so putting the stack anywhere else can confuse the thread system. + + This path is for the threaded VM where we may want to allocate the stack + zone on + the stack of a thread other than the main VM thread. */ + + /* StackInterpreter>>#initStackPagesAndContinueInto:with: */ +void +initStackPagesAndContinueIntowith(void (*continuation)(void *), void *argument) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt stackPageBytes; + usqIntptr_t stackZoneBytes; + void *theStackMemory; + + /* begin ensureInitializeStackZone */ + if (!GIV(pages)) { + stackPageBytes = stackPageByteSize(); + stackZoneBytes = (GIV(numStackPages) * ((sizeof(CogStackPage)) + (stackPageByteSize()))) + BytesPerWord; + theStackMemory = alloca(stackZoneBytes); + memset(theStackMemory, 0, stackZoneBytes); + initializeStacknumSlotspageSize(theStackMemory, stackZoneBytes / BytesPerWord, stackPageBytes / BytesPerWord); + } + continuation(argument); +} + + +/* the vm has to convert aFilenameString via any canonicalization and + char-mapping and put the result in aCharBuffer. + Note the resolveAliases flag - this is an awful artefact of OSX and Apples + demented alias handling. When opening a file, the flag must be true, when + closing or renaming it must be false. Sigh. + */ + + /* StackInterpreter>>#ioFilename:fromString:ofLength:resolveAliases: */ +void +ioFilenamefromStringofLengthresolveAliases(char *aCharBuffer, char *aFilenameString, sqInt filenameLength, sqInt aBoolean) +{ + sqGetFilenameFromString(aCharBuffer, aFilenameString, filenameLength, aBoolean); +} + + +/* A base frame (first frame in a stack page) is so marked by having a null + saved fp. + */ + + /* StackInterpreter>>#isBaseFrame: */ +static NoDbgRegParms int +isBaseFrame(char *theFP) +{ + return (longAt(theFP + FoxSavedFP)) == 0; +} + + /* StackInterpreter>>#isBooleanObject: */ +sqInt +isBooleanObject(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return (oop == GIV(trueObj)) + || (oop == GIV(falseObj)); +} + + +/* This virtual machine provides two primitives that call external code, + primitiveExternalCall for plugin primitives, and primitiveCalloutToFFI + for FFI calls. */ + + /* StackInterpreter>>#isCalloutPrimitiveIndex: */ +static NoDbgRegParms sqInt +isCalloutPrimitiveIndex(sqInt primIndex) +{ + return (primIndex == PrimNumberExternalCall) + || (primIndex == PrimNumberFFICall); +} + + /* StackInterpreter>>#isEmptyList: */ +static NoDbgRegParms int +isEmptyList(sqInt aLinkedList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(!(isForwarded(aLinkedList))); + return (longAt((void *)((aLinkedList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj); +} + + /* StackInterpreter>>#isFloatObject: */ +int +isFloatObject(sqInt oop) +{ + sqInt tagBits; + + return /* isFloatInstance: */ + ((tagBits = oop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(oop))) & (classIndexMask())) == ClassFloatCompactIndex); +} + + /* StackInterpreter>>#isFrame:onPage: */ +static NoDbgRegParms sqInt +isFrameonPage(char *aFrame, StackPage *aPage) +{ + char *prevFP; + char *theFP; + + assert(!((isFree(aPage)))); + + /* Walk the static chain making sure progress is being made, + and in the right direction, looking for a match with aFrame. */ + theFP = (aPage->headFP); + prevFP = theFP - BytesPerWord; + while (1) { + if (theFP == aFrame) { + return 1; + } + if (!((theFP > prevFP) + && (theFP < ((aPage->baseFP))))) break; + prevFP = theFP; + theFP = ((char *)(longAt(theFP + FoxSavedFP))); + } + return 0; +} + + +/* Answer true if the oop is kind of Integer (Small or Large). */ + + /* StackInterpreter>>#isKindOfInteger: */ +sqInt +isKindOfInteger(sqInt oop) +{ + return ((((oop) & 7) == 1)) + || (/* isLargeIntegerInstance: */ + ((!(oop & (tagMask())))) + && ((((usqInt)(((longAt((void *)(oop))) & (classIndexMask())) - ClassLargeNegativeIntegerCompactIndex))) <= 1)); +} + + /* StackInterpreter>>#isLargeIntegerObject: */ +sqInt +isLargeIntegerObject(sqInt oop) +{ + return /* isLargeIntegerInstance: */ + ((!(oop & (tagMask())))) + && ((((usqInt)(((longAt((void *)(oop))) & (classIndexMask())) - ClassLargeNegativeIntegerCompactIndex))) <= 1); +} + + /* StackInterpreter>>#isLargeNegativeIntegerObject: */ +sqInt +isLargeNegativeIntegerObject(sqInt oop) +{ + return /* isInstanceOfClassLargeNegativeInteger: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassLargeNegativeIntegerCompactIndex); +} + + /* StackInterpreter>>#isLargePositiveIntegerObject: */ +sqInt +isLargePositiveIntegerObject(sqInt oop) +{ + return /* isInstanceOfClassLargePositiveInteger: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassLargePositiveIntegerCompactIndex); +} + + /* StackInterpreter>>#isLinkedExternalPrimitive: */ +static NoDbgRegParms sqInt +isLinkedExternalPrimitive(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt headerSqInt; + sqInt literal; + usqInt numSlots; + sqInt primIdx; + sqInt targetFunctionIndex; + + literal = 0; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodObj)); + headerSqInt = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* primitiveIndexOfMethod:header: */ + if (((header & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (methodObj + ((LiteralStart + (((header >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIdx = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIdx = 0; + } + return (primIdx == PrimNumberExternalCall) + && ((((/* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((header >> 3)) & AlternateHeaderNumLiteralsMask)) > 0) + && ((isArray((literal = longAt((void *)((methodObj + BaseHeaderSize) + (1U << (shiftForWord()))))))) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(literal)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(literal + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(literal - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4) + && (((targetFunctionIndex = longAt((void *)((literal + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord()))))))), + ((((targetFunctionIndex) & 7) == 1)) + && (((targetFunctionIndex >> 3)) > 0)))))); +} + + +/* Answer if the argument, which can be any object, is a live context. */ + + /* StackInterpreter>>#isLiveContext: */ +static NoDbgRegParms sqInt +isLiveContext(sqInt oop) +{ + assert(!((isOopForwarded(oop)))); + if (!(/* isContext: */ + ((!(oop & (tagMask())))) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + return 0; + } + if ((!((longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))) & (tagMask())))) { + return ((((longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))))) & 7) == 1); + } + return !(isWidowedContext(oop)); +} + + /* StackInterpreter>>#isMarriedOrWidowedContext: */ +static NoDbgRegParms int +isMarriedOrWidowedContext(sqInt aContext) +{ + return ((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1); +} + + +/* This virtual machine provides two primitives that executes arbitrary + primitives, one + for indexed primitivces and one for named primitives. These meta + primitives are used + in the debugger to execute primitives while simulating execution. Spur + needs to know + the accessor depth for a primitive so that failures due to forwarders can + be fixed up + and retried. This method identifies such meta primitives so that + metaAccessorDepth can be substituted when appropriate. */ + + /* StackInterpreter>>#isMetaPrimitiveIndex: */ +static NoDbgRegParms sqInt +isMetaPrimitiveIndex(sqInt primIndex) +{ + return (primIndex == PrimNumberDoPrimitive) + || (primIndex == PrimNumberDoExternalCall); +} + + +/* Answer if the method is an external primitive call (prim 117) with a null + external primitive. + This is just for an assert in the CoInterpreter. */ + + /* StackInterpreter>>#isNullExternalPrimitiveCall: */ +static NoDbgRegParms sqInt +isNullExternalPrimitiveCall(sqInt aMethodObj) +{ + sqInt lit; + usqInt numSlots; + + if (!(((primitiveIndexOf(aMethodObj)) == PrimNumberExternalCall) + && ((literalCountOf(aMethodObj)) > 0))) { + return 0; + } + lit = longAt((void *)((aMethodObj + BaseHeaderSize) + (1U << (shiftForWord())))); + return (/* isArray: */ + ((!(lit & (tagMask())))) + && (((byteAt((void *)(lit + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(lit)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(lit + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(lit - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4) + && (((longAt((void *)((lit + BaseHeaderSize) + (3U << (shiftForWord()))))) == ConstZero) + || ((longAt((void *)((lit + BaseHeaderSize) + (3U << (shiftForWord()))))) == ConstMinusOne))); +} + + +/* We save slots in the method cache by using the primitiveFunctionPointer + to hold either a function pointer or the index of a quick primitive. Since + quick primitive indices are small they can't be confused with function + addresses. */ + + /* StackInterpreter>>#isPrimitiveFunctionPointerAnIndex */ +static int +isPrimitiveFunctionPointerAnIndex(void) +{ + return (((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex; +} + + /* StackInterpreter>>#isQuickPrimitiveIndex: */ +int +isQuickPrimitiveIndex(sqInt anInteger) +{ + return ((anInteger >= 0x100) && (anInteger <= 519)); +} + + +/* Reading the sender, instructionPointer and stackPointer inst vars of a + context must take + account of potentially married contexts and fetch the state from the + frame. method, + closureOrNil and receiver can safely be fetched from the context without + checking. + */ + + /* StackInterpreter>>#isReadMediatedContextInstVarIndex: */ +int +isReadMediatedContextInstVarIndex(sqInt index) +{ + return index <= StackPointerIndex; +} + + /* StackInterpreter>>#isSingleContext: */ +static NoDbgRegParms int +isSingleContext(sqInt aContext) +{ + return (!((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))) & (tagMask()))); +} + + +/* Answer if aContext is married or widowed and still married. + If a context is widowed then turn it into a single dead context. */ + + /* StackInterpreter>>#isStillMarriedContext: */ +static NoDbgRegParms sqInt +isStillMarriedContext(sqInt aContext) +{ + return (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext))); +} + + +/* Answer if aClosure is a vanilla BlockClosure as in the first Cog release, + BlockClosure laid out as + 'outerContext, startpc, numArgs. FullBlockClosure is laid out as + outerContext, method, numArgs, receiver. + So either answer true if we're not supporting FullBlockClosure, or test + the startpc/method field. */ + + /* StackInterpreter>>#isVanillaBlockClosure: */ +static NoDbgRegParms int +isVanillaBlockClosure(sqInt aClosure) +{ + assert((isPointers(aClosure)) + && ((numSlotsOf(aClosure)) >= ClosureFirstCopiedValueIndex)); + return ((((longAt((void *)((aClosure + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1); +} + + +/* Answer if the argument is married to a live frame or not. + This method is safe for use only when no frameContext fields may be + forwarded (as maybe the case when scavenging). Post become: all + frameContext fields are followed, and hence normally no following of + frameContext fields is necessary. */ +/* i.e. inline into isWidowedContext: */ + + /* StackInterpreter>>#isWidowedContextNoConvert: */ +static NoDbgRegParms sqInt +isWidowedContextNoConvert(sqInt aOnceMarriedContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt anInteger; + sqInt senderOop; + char *shouldBeFrameCallerField; + char *theFrame; + StackPage *thePage; + + assert((isContext(aOnceMarriedContext)) + && (isMarriedOrWidowedContext(aOnceMarriedContext))); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + if (!((isFree(thePage)) + || (theFrame < ((thePage->headFP))))) { + /* The frame pointer is within the bounds of a live page. + Now check if it matches a frame. */ + anInteger = longAt((void *)((aOnceMarriedContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + + /* begin withoutSmallIntegerTags: */ + assert((((anInteger) & 7) == 1)); + shouldBeFrameCallerField = ((char *)(anInteger - (smallIntegerTag()))); + if (((((char *)(longAt(theFrame + FoxSavedFP)))) == shouldBeFrameCallerField) + && (/* frameHasContext: */ + ((((usqInt)(longAt(theFrame + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFrame + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFrame + FoxIFrameFlags) + 2)) != 0))) { + assert(!(((isFrameonPage(theFrame, thePage)) + && (isForwarded(frameContext(theFrame)))))); + if ((longAt(theFrame + FoxThisContext)) == aOnceMarriedContext) { + return 0; + } + } + } + return 1; +} + + +/* Wrining any inst vars of a context must take account of potentially + married contexts + and set the state in the frame. Inst vars in subclasses don't need + mediation; subclasses + can't marry. */ + + /* StackInterpreter>>#isWriteMediatedContextInstVarIndex: */ +int +isWriteMediatedContextInstVarIndex(sqInt index) +{ + return index <= ReceiverIndex; +} + + +/* Support for external primitives. */ + + /* StackInterpreter>>#is:KindOfClass: */ +sqInt +isKindOfClass(sqInt oop, sqInt aClass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt oopClass; + sqInt tagBits; + + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while ((oopClass != GIV(nilObj)) + && ((/* isPointers: */ + ((!(oopClass & (tagMask())))) + && (((byteAt((void *)(oopClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOfAny(oopClass)) > InstanceSpecificationIndex))) { + if (oopClass == aClass) { + return 1; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + return 0; +} + + +/* Support for external primitives. */ + + /* StackInterpreter>>#is:KindOf: */ +sqInt +isKindOf(sqInt oop, char *className) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt oopClass; + sqInt tagBits; + + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while (!(oopClass == GIV(nilObj))) { + if (classNameOfIs(oopClass, className)) { + return 1; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + return 0; +} + + +/* Support for external primitives */ + + /* StackInterpreter>>#is:MemberOf: */ +sqInt +isMemberOf(sqInt oop, char *className) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oopClass; + sqInt tagBits; + + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + return classNameOfIs(oopClass, className); +} + + /* StackInterpreter>>#lengthOfNameOfClass: */ +static NoDbgRegParms sqInt +lengthOfNameOfClass(sqInt classOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + usqInt numSlots; + usqInt numSlots1; + usqInt numSlotsUsqInt; + sqInt objOop; + + /* begin numSlotsOf: */ + assert((classIndexOf(classOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(classOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (numSlots == GIV(metaclassNumSlots)) { + return lengthOfNameOfClass(longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord()))))))); + } + if (numSlots <= GIV(classNameIndex)) { + return 0; + } + objOop = longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(classNameIndex)) << (shiftForWord())))))); + + /* begin lengthOf: */ + fmt = (byteAt((void *)(objOop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(objOop + (numSlotsFieldByteOffset()))); + numSlots1 = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(objOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots1; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots1 << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots1 << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots1 << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots1; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + /* StackInterpreter>>#literal:ofMethod: */ +sqInt +literalofMethod(sqInt offset, sqInt methodPointer) +{ + return longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); +} + + +/* This entry point needs to be implemented for the interpreter proxy. + Since BitBlt is now a plugin we need to look up + BitBltPlugin:=loadBitBltFrom and call it. This entire mechanism should + eventually go away and be + replaced with a dynamic lookup from BitBltPlugin itself but for backward + compatibility this stub is provided + */ + + /* StackInterpreter>>#loadBitBltFrom: */ +sqInt +loadBitBltFrom(sqInt bb) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void *fn; + + fn = ioLoadFunctionFrom("loadBitBltFrom", "BitBltPlugin"); + if (!fn) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return ((sqInt (*)(sqInt))fn)(bb); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#longPrintOop: */ +void +longPrintOop(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aClass; + int byte; + char *className; + sqInt classPointer; + sqInt cls; + CogMethod *cogMethod; + sqInt column; + sqInt contextSize; + sqInt fieldOop; + sqInt fmt; + sqInt fmtSqInt; + sqInt header; + sqInt headerSqInt; + sqInt i; + usqInt index; + sqInt lastIndex; + usqInt lastPointer; + sqInt length; + usqInt numLiterals; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt objOop; + sqInt oopClass; + sqInt sp; + sqInt tagBits; + char *where; + + length = 0; + if (((oop & (tagMask())) != 0)) { + printImmediateObjecton(oop, GIV(transcript)); + return; + } + if (!(addressCouldBeObj(oop))) { + /* begin printCantBeObject:on: */ + fprintf(GIV(transcript), + "%p%s\n", + ((void *)oop), + ((where = whereIsMaybeCodeThing(oop)) + ? where + : (oop & 7 /* (allocationUnit - 1) */ + ? " is misaligned" + : whereIs(oop)))); + return; + } + if (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + printFreeObjecton(oop, GIV(transcript)); + return; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + printForwarderon(oop, GIV(transcript)); + return; + } + if ((cls = fetchClassOfNonImm(oop))) { + className = nameOfClasslengthInto(cls, (&length)); + fprintf(GIV(transcript), + "%p: a(n) %.*s", + ((void *)oop), + ((int) length), + className); + fprintf(GIV(transcript), + "(%" PRIxSQINT "=>%p)", + (longAt((void *)(oop))) & (classIndexMask()), + ((void *)cls)); + } + else { + fprintf(GIV(transcript), + "%p has a nil class!!\n", + ((void *)oop)); + } + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + fprintf(GIV(transcript), + " format %" PRIxSQINT "", + fmt); + if (fmt > 5 /* lastPointerFormat */) { + fprintf(GIV(transcript), + " nbytes %" PRIdSQINT "", + numBytesOf(oop)); + } + else { + if (/* isIndexableFormat: */ + (fmt >= (arrayFormat())) + && ((fmt <= (weakArrayFormat())) + || (fmt >= (sixtyFourBitIndexableFormat())))) { + /* begin lengthOf: */ + fmtSqInt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmtSqInt <= (ephemeronFormat())) { + length = numSlots; + goto l2; + } + if (fmtSqInt >= (firstByteFormat())) { + length = ((numSlots << (shiftForWord()))) - (fmtSqInt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmtSqInt >= (firstShortFormat())) { + length = ((numSlots << ((shiftForWord()) - 1))) - (fmtSqInt & 3); + goto l2; + } + if (fmtSqInt >= (firstLongFormat())) { + length = ((numSlots << ((shiftForWord()) - 2))) - (fmtSqInt & 1); + goto l2; + } + if (fmtSqInt == (sixtyFourBitIndexableFormat())) { + length = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + length = 0; + /* end lengthOf: */ +l2: + fprintf(GIV(transcript), + " size %" PRIdSQINT "", + length - (fixedFieldsOfformatlength(oop, fmt, length))); + } + } + + /* begin printHeaderTypeOf:on: */ + fprintf(GIV(transcript), + " hdr%d %c%c%c%c%c", + ((byteAt((void *)(oop + (numSlotsFieldByteOffset())))) == (numSlotsMask()) + ? 16 + : 8), + ((((usqInt)((byteAt((void *)(oop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1 + ? 'i' + : '.'), + ((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())) + ? 'r' + : '.'), + ((byteAt((void *)(oop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift())) + ? 'p' + : '.'), + ((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (markedBitByteShift())) + ? 'm' + : '.'), + ((byteAt((void *)(oop + (markBitsByteOffset())))) & (1U << (greyBitByteShift())) + ? 'g' + : '.')); + fprintf(GIV(transcript), + " hash %x\n", + (long32At((void *)(oop + 4))) & (identityHashHalfWordMask())); + if (/* isPureBitsFormat: */ + (fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat()))) { + /* This will answer false if splObj: ClassAlien is nilObject */ + + /* begin is:KindOfClass: */ + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while ((oopClass != GIV(nilObj)) + && ((/* isPointers: */ + ((!(oopClass & (tagMask())))) + && (((byteAt((void *)(oopClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOfAny(oopClass)) > InstanceSpecificationIndex))) { + if (oopClass == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord())))))))) { + fprintf(GIV(transcript), + " datasize %" PRIdSQINT " %s @ %p\n", + longAt((void *)(oop + BaseHeaderSize)), + ((longAt((void *)(oop + BaseHeaderSize))) < 0 + ? "indirect" + : (longAt((void *)(oop + BaseHeaderSize)) + ? "direct" + : "pointer")), + startOfAlienData(oop)); + return; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + classPointer = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord())))))); + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, objOop); + } + aClass = objOop; + + /* begin is:KindOfClass: */ + oopClass = /* fetchClassOf: */ + ((tagBits = oop & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(oop)); + while ((oopClass != GIV(nilObj)) + && ((/* isPointers: */ + ((!(oopClass & (tagMask())))) + && (((byteAt((void *)(oopClass + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOfAny(oopClass)) > InstanceSpecificationIndex))) { + if (oopClass == aClass) { + printStringDataOfon(oop, GIV(transcript)); + return; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((oopClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, oopClass, objOop); + } + oopClass = objOop; + } + printNonPointerDataOfon(oop, GIV(transcript)); + return; + } + + /* begin numPointerSlotsOf: */ + fmtSqInt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if (fmtSqInt <= 5 /* lastPointerFormat */) { + if ((fmtSqInt == (indexablePointersFormat())) + && (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* contexts end at the stack pointer */ + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + contextSize = 0; + goto l3; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(oop))); + contextSize = (sp >> 3); + /* end fetchStackPointerOf: */ +l3: + lastPointer = CtxtTempFrameStart + contextSize; + goto l4; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(oop)) > (isForwardedObjectClassIndexPun())); + lastPointer = (((numSlots = byteAt((void *)(oop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + goto l4; + } + if (fmtSqInt == (forwardedFormat())) { + lastPointer = 1; + goto l4; + } + if (fmtSqInt < (firstCompiledMethodFormat())) { + lastPointer = 0; + goto l4; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(oop)); + headerSqInt = longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + + /* begin literalCountOfMethodHeader: */ + assert((((header) & 7) == 1)); + numLiterals = ((header >> 3)) & AlternateHeaderNumLiteralsMask; + lastPointer = numLiterals + LiteralStart; + /* end numPointerSlotsOf: */ +l4: + lastIndex = ((0x100 < lastPointer) ? 0x100 : lastPointer); + if (lastIndex > 0) { + for (i = 1; i <= lastIndex; i += 1) { + fieldOop = longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)((i - 1)) << (shiftForWord())))))); + + /* begin space */ + printChar(' '); + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)(i - 1))); + printChar(' '); + printHex(fieldOop); + + /* begin space */ + printChar(' '); + if ((i == 1) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) { + /* begin printMethodHeaderOop: */ + /* begin isCogMethodReference: */ + assert(((((fieldOop) & 7) == 1)) + || (((((usqInt)fieldOop)) < (startOfMemory())) + && ((((usqInt)fieldOop)) >= (minCogMethodAddress())))); + if ((!(fieldOop & (smallIntegerTag())))) { + if ((cogMethod = methodFor(((void *)fieldOop)))) { + printHex(fieldOop); + + /* begin space */ + printChar(' '); + printDecodeMethodHeaderOop((cogMethod->methodHeader)); + goto l1; + } + } + printDecodeMethodHeaderOop(fieldOop); + /* end printMethodHeaderOop: */ +l1:; + } + else { + printOopShortInner(fieldOop); + } + cr(); + } + } + if (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + lastPointer = (((literalCountOf(oop)) + LiteralStart) * BytesPerOop) + 1; + + /* begin lengthOf: */ + fmtSqInt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmtSqInt <= (ephemeronFormat())) { + lastIndex = numSlots; + goto l5; + } + if (fmtSqInt >= (firstByteFormat())) { + lastIndex = ((numSlots << (shiftForWord()))) - (fmtSqInt & 7); + goto l5; + } + + /* bytes, including CompiledMethod */ + if (fmtSqInt >= (firstShortFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 1))) - (fmtSqInt & 3); + goto l5; + } + if (fmtSqInt >= (firstLongFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 2))) - (fmtSqInt & 1); + goto l5; + } + if (fmtSqInt == (sixtyFourBitIndexableFormat())) { + lastIndex = numSlots; + goto l5; + } + + /* fmt = self forwardedFormat */ + lastIndex = 0; + /* end lengthOf: */ +l5: + if ((lastIndex - lastPointer) > 0x100) { + lastIndex = lastPointer + 0x100; + } + column = 1; + for (index = lastPointer; index <= lastIndex; index += 1) { + if (column == 1) { + fprintf(GIV(transcript), + "%10p ", + ((void *)(((oop + BaseHeaderSize) + index) - 1))); + } + byte = ((int) (byteAt((void *)((oop + BaseHeaderSize) + (index - 1))))); + fprintf(GIV(transcript), + "%02x/%-+3d%c", + byte, + byte, + (column == 8 + ? '\n' + : ' ')); + if (((column += 1)) > 8) { + column = 1; + } + } + if ((lengthOf(oop)) > lastIndex) { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "..."); + } + if (((column >= 2) && (column <= 7))) { + cr(); + } + } + else { + if (lastPointer > lastIndex) { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "..."); + cr(); + } + } +} + + +/* Answer the relevant long store temp bytecode, which indicates it has a + primitive error code. + */ +/* 234 11101010 i i i i i i i i Store Temporary Variable #iiiiiiii */ +/* 129 10000001 jjkkkkkk Store (Receiver Variable, Temporary Location, + Illegal, Literal Variable) [jj] #kkkkkk */ + + /* StackInterpreter>>#longStoreBytecodeForHeader: */ +sqInt +longStoreBytecodeForHeader(sqInt methodHeader) +{ + return ((((sqLong) methodHeader)) < 0 + ? AltLongStoreBytecode + : LongStoreBytecode); +} + + +/* This method implements a simple method lookup cache. If an entry for the + given selector and classTag is found in the cache, set the values of + 'newMethod' and + 'primitiveFunctionPointer' and answer true. Otherwise, answer false. */ + + /* StackInterpreter>>#lookupInMethodCacheSel:classTag: */ +static NoDbgRegParms sqInt +lookupInMethodCacheSelclassTag(sqInt selector, sqInt classTag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt hash; + usqInt probe; + + /* begin inlineLookupInMethodCacheSel:classTag: */ + /* shift drops two low-order zeros from addresses */ + hash = selector ^ ((((usqInt)(classTag) << 2))); + + /* first probe */ + probe = hash & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == selector) + && ((GIV(methodCache)[probe + MethodCacheClass]) == classTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + return 1; + } + + /* second probe */ + probe = (((usqInt)(hash)) >> 1) & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == selector) + && ((GIV(methodCache)[probe + MethodCacheClass]) == classTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + return 1; + } + probe = (((usqInt)(hash)) >> 2) & MethodCacheMask; + if (((GIV(methodCache)[probe + MethodCacheSelector]) == selector) + && ((GIV(methodCache)[probe + MethodCacheClass]) == classTag)) { + GIV(newMethod) = GIV(methodCache)[probe + MethodCacheMethod]; + primitiveFunctionPointer = ((void (*)()) (GIV(methodCache)[probe + MethodCachePrimFunction])); + return 1; + } + return 0; +} + + /* StackInterpreter>>#lookupMethodInClass: */ +static NoDbgRegParms sqInt +lookupMethodInClass(sqInt class) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentClass; + sqInt dictionary; + sqInt found; + sqInt index; + usqInt length; + sqInt mask; + sqInt methodArray; + sqInt nextSelector; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt tagBits; + sqInt wrapAround; + + assert(addressCouldBeClassObj(class)); + + /* begin lookupBreakFor: */ + if ((breakSelectorLength <= 0) + && (/* shouldBreakForLookupIn:given: */ + (breakLookupClassTag) + && ((class == breakLookupClassTag) + || (((long32At((void *)(class + 4))) & (identityHashHalfWordMask())) == breakLookupClassTag)))) { + warning("lookup class send break (heartbeat suppressed)"); + } + currentClass = class; + while (currentClass != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + dictionary = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + assert(isNonImmediate(dictionary)); + if ((!((longAt((void *)(dictionary))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + dictionary = fixFollowedFieldofObjectwithInitialValue(MethodDictionaryIndex, currentClass, dictionary); + } + if (dictionary == GIV(nilObj)) { + /* MethodDict pointer is nil (hopefully due a swapped out stub) + -- raise exception #cannotInterpret:. */ + createActualMessageTo(class); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorCannotInterpret) << (shiftForWord())))))); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), /* fetchClassTagOf: */ + ((tagBits = 0) + ? tagBits + : (longAt((void *)(null))) & (classIndexMask()))); + return lookupMethodInClass(superclassOf(currentClass)); + } + + /* begin lookupMethodInDictionary: */ + /* begin numSlotsOf: */ + assert((classIndexOf(dictionary)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(dictionary + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(dictionary - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + mask = (length - SelectorStart) - 1; + + /* Use linear search on small dictionaries; its cheaper. + Also the limit can be set to force linear search of all dictionaries, which supports the + booting of images that need rehashing (e.g. because a tracer has generated an image + with different hashes but hasn't rehashed it yet.) */ + if (mask <= GIV(methodDictLinearSearchLimit)) { + index = 0; + while (index <= mask) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)((index + SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOop = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(index, methodArray, objOop); + } + GIV(newMethod) = objOop; + found = 1; + goto l1; + } + index += 1; + } + found = 0; + goto l1; + } + index = SelectorStart + (mask & ((((GIV(messageSelector) & (tagMask())) != 0) + ? (GIV(messageSelector) >> 3) + : (long32At((void *)(GIV(messageSelector) + 4))) & (identityHashHalfWordMask())))); + + /* It is assumed that there are some nils in this dictionary, and search will + stop when one is encountered. However, if there are no nils, then wrapAround + will be detected the second time the loop gets to the end of the table. */ + wrapAround = 0; + while (1) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (nextSelector == GIV(nilObj)) { + found = 0; + goto l1; + } + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOop = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOop & (tagMask())))) + && ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(index - SelectorStart, methodArray, objOop); + } + GIV(newMethod) = objOop; + found = 1; + goto l1; + } + index += 1; + if (index == length) { + if (wrapAround) { + found = 0; + goto l1; + } + wrapAround = 1; + index = SelectorStart; + } + } + found = 0; + /* end lookupMethodInDictionary: */ +l1: + if (found) { + return currentClass; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOopSqInt = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOopSqInt)); + if ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOopSqInt); + } + currentClass = objOopSqInt; + } + + /* Could not find #doesNotUnderstand: -- unrecoverable error. */ + if (GIV(messageSelector) == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))))) { + error("Recursive not understood error encountered"); + } + + /* Cound not find a normal message -- raise exception #doesNotUnderstand: */ + createActualMessageTo(class); + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))); + sendBreakpointclassTag(GIV(messageSelector) + BaseHeaderSize, lengthOf(GIV(messageSelector)), (long32At((void *)(class + 4))) & (identityHashHalfWordMask())); + return lookupMethodInClass(class); +} + + +/* Lookup messageSelector in class. Answer 0 on success. Answer the splObj: + index for the error selector to use on failure rather than performing MNU + processing etc. */ + + /* StackInterpreter>>#lookupMNUInClass: */ +static NoDbgRegParms sqInt +lookupMNUInClass(sqInt class) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentClass; + sqInt dictionary; + sqInt found; + sqInt index; + usqInt length; + sqInt mask; + sqInt methodArray; + sqInt nextSelector; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt wrapAround; + + /* begin lookupBreakFor: */ + if ((breakSelectorLength <= 0) + && (/* shouldBreakForLookupIn:given: */ + (breakLookupClassTag) + && ((class == breakLookupClassTag) + || (((long32At((void *)(class + 4))) & (identityHashHalfWordMask())) == breakLookupClassTag)))) { + warning("lookup class send break (heartbeat suppressed)"); + } + currentClass = class; + while (currentClass != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + dictionary = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + assert(isNonImmediate(dictionary)); + if ((!((longAt((void *)(dictionary))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + dictionary = fixFollowedFieldofObjectwithInitialValue(MethodDictionaryIndex, currentClass, dictionary); + } + if (dictionary == GIV(nilObj)) { + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOop); + } + GIV(lkupClass) = objOop; + return SelectorCannotInterpret; + } + + /* begin lookupMethodInDictionary: */ + /* begin numSlotsOf: */ + assert((classIndexOf(dictionary)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(dictionary + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(dictionary - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + mask = (length - SelectorStart) - 1; + + /* Use linear search on small dictionaries; its cheaper. + Also the limit can be set to force linear search of all dictionaries, which supports the + booting of images that need rehashing (e.g. because a tracer has generated an image + with different hashes but hasn't rehashed it yet.) */ + if (mask <= GIV(methodDictLinearSearchLimit)) { + index = 0; + while (index <= mask) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)((index + SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(index, methodArray, objOopSqInt); + } + GIV(newMethod) = objOopSqInt; + found = 1; + goto l1; + } + index += 1; + } + found = 0; + goto l1; + } + index = SelectorStart + (mask & ((((GIV(messageSelector) & (tagMask())) != 0) + ? (GIV(messageSelector) >> 3) + : (long32At((void *)(GIV(messageSelector) + 4))) & (identityHashHalfWordMask())))); + + /* It is assumed that there are some nils in this dictionary, and search will + stop when one is encountered. However, if there are no nils, then wrapAround + will be detected the second time the loop gets to the end of the table. */ + wrapAround = 0; + while (1) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (nextSelector == GIV(nilObj)) { + found = 0; + goto l1; + } + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(index - SelectorStart, methodArray, objOopSqInt); + } + GIV(newMethod) = objOopSqInt; + found = 1; + goto l1; + } + index += 1; + if (index == length) { + if (wrapAround) { + found = 0; + goto l1; + } + wrapAround = 1; + index = SelectorStart; + } + } + found = 0; + /* end lookupMethodInDictionary: */ +l1: + if (found) { + addNewMethodToCache(class); + return 0; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOopSqInt = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOopSqInt)); + if ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOopSqInt); + } + currentClass = objOopSqInt; + } + GIV(lkupClass) = class; + return SelectorDoesNotUnderstand; +} + + +/* Lookup messageSelector in class. Answer 0 on success. Answer the splObj: + index for the error selector to use on failure rather than performing MNU + processing etc. */ + + /* StackInterpreter>>#lookupOrdinaryNoMNUEtcInClass: */ +static NoDbgRegParms sqInt +lookupOrdinaryNoMNUEtcInClass(sqInt class) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentClass; + sqInt dictionary; + sqInt found; + sqInt index; + usqInt length; + sqInt mask; + sqInt methodArray; + sqInt nextSelector; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt wrapAround; + + /* begin lookupBreakFor: */ + if ((breakSelectorLength <= 0) + && (/* shouldBreakForLookupIn:given: */ + (breakLookupClassTag) + && ((class == breakLookupClassTag) + || (((long32At((void *)(class + 4))) & (identityHashHalfWordMask())) == breakLookupClassTag)))) { + warning("lookup class send break (heartbeat suppressed)"); + } + currentClass = class; + while (currentClass != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + dictionary = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + assert(isNonImmediate(dictionary)); + if ((!((longAt((void *)(dictionary))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + dictionary = fixFollowedFieldofObjectwithInitialValue(MethodDictionaryIndex, currentClass, dictionary); + } + if (dictionary == GIV(nilObj)) { + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOop); + } + GIV(lkupClass) = objOop; + return SelectorCannotInterpret; + } + + /* begin lookupMethodInDictionary: */ + /* begin numSlotsOf: */ + assert((classIndexOf(dictionary)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(dictionary + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(dictionary - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + mask = (length - SelectorStart) - 1; + + /* Use linear search on small dictionaries; its cheaper. + Also the limit can be set to force linear search of all dictionaries, which supports the + booting of images that need rehashing (e.g. because a tracer has generated an image + with different hashes but hasn't rehashed it yet.) */ + if (mask <= GIV(methodDictLinearSearchLimit)) { + index = 0; + while (index <= mask) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)((index + SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(index, methodArray, objOopSqInt); + } + GIV(newMethod) = objOopSqInt; + found = 1; + goto l1; + } + index += 1; + } + found = 0; + goto l1; + } + index = SelectorStart + (mask & ((((GIV(messageSelector) & (tagMask())) != 0) + ? (GIV(messageSelector) >> 3) + : (long32At((void *)(GIV(messageSelector) + 4))) & (identityHashHalfWordMask())))); + + /* It is assumed that there are some nils in this dictionary, and search will + stop when one is encountered. However, if there are no nils, then wrapAround + will be detected the second time the loop gets to the end of the table. */ + wrapAround = 0; + while (1) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (nextSelector == GIV(nilObj)) { + found = 0; + goto l1; + } + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == GIV(messageSelector)) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(index - SelectorStart, methodArray, objOopSqInt); + } + GIV(newMethod) = objOopSqInt; + found = 1; + goto l1; + } + index += 1; + if (index == length) { + if (wrapAround) { + found = 0; + goto l1; + } + wrapAround = 1; + index = SelectorStart; + } + } + found = 0; + /* end lookupMethodInDictionary: */ +l1: + if (found) { + addNewMethodToCache(class); + return 0; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOopSqInt = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOopSqInt)); + if ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOopSqInt); + } + currentClass = objOopSqInt; + } + GIV(lkupClass) = class; + return SelectorDoesNotUnderstand; +} + + +/* Lookup selector in class. Answer the method or nil. This is a debugging + routine. It does /not/ side-effect lookupClass or newMethod. */ + + /* StackInterpreter>>#lookupSelector:inClass: */ +sqInt +lookupSelectorinClass(sqInt selector, sqInt class) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt currentClass; + sqInt dictionary; + usqInt index; + usqInt length; + sqInt mask; + sqInt meth; + sqInt methodArray; + sqInt nextSelector; + usqInt numSlots; + sqInt objOop; + sqInt objOopSqInt; + sqInt wrapAround; + + currentClass = class; + while (currentClass != GIV(nilObj)) { + /* begin followObjField:ofObject: */ + dictionary = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + assert(isNonImmediate(dictionary)); + if ((!((longAt((void *)(dictionary))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + dictionary = fixFollowedFieldofObjectwithInitialValue(MethodDictionaryIndex, currentClass, dictionary); + } + if (dictionary == GIV(nilObj)) { + return null; + } + + /* begin lookupMethodFor:InDictionary: */ + /* begin numSlotsOf: */ + assert((classIndexOf(dictionary)) > (isForwardedObjectClassIndexPun())); + length = (((numSlots = byteAt((void *)(dictionary + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(dictionary - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + mask = (length - SelectorStart) - 1; + index = SelectorStart + (mask & ((((selector & (tagMask())) != 0) + ? (selector >> 3) + : (long32At((void *)(selector + 4))) & (identityHashHalfWordMask())))); + + /* It is assumed that there are some nils in this dictionary, and search will + stop when one is encountered. However, if there are no nils, then wrapAround + will be detected the second time the loop gets to the end of the table. */ + wrapAround = 0; + while (1) { + nextSelector = longAt((void *)((dictionary + BaseHeaderSize) + ((index << (shiftForWord()))))); + if (nextSelector == GIV(nilObj)) { + meth = null; + goto l1; + } + if (/* isOopForwarded: */ + ((!(nextSelector & (tagMask())))) + && ((!((longAt((void *)(nextSelector))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + nextSelector = fixFollowedFieldofObjectwithInitialValue(index + SelectorStart, dictionary, nextSelector); + } + if (nextSelector == selector) { + /* begin followObjField:ofObject: */ + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + assert(isNonImmediate(methodArray)); + if ((!((longAt((void *)(methodArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + methodArray = fixFollowedFieldofObjectwithInitialValue(MethodArrayIndex, dictionary, methodArray); + } + + /* begin followField:ofObject: */ + objOopSqInt = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(objOopSqInt & (tagMask())))) + && ((!((longAt((void *)(objOopSqInt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + objOopSqInt = fixFollowedFieldofObjectwithInitialValue(index - SelectorStart, methodArray, objOopSqInt); + } + meth = objOopSqInt; + goto l1; + } + index += 1; + if (index == length) { + if (wrapAround) { + meth = null; + goto l1; + } + wrapAround = 1; + index = SelectorStart; + } + } + meth = null; + /* end lookupMethodFor:InDictionary: */ +l1: + if (meth) { + return meth; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((currentClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currentClass, objOop); + } + currentClass = objOop; + } + return null; +} + + +/* Convert married contexts to widowed contexts, and in the CoInterpreter, + map any machine code pcs to bytecode pcs, and then nil all slots beyond + top of stack, so that the context can be resumed on any VM. */ + + /* StackInterpreter>>#makeContextSnapshotSafe: */ +static NoDbgRegParms void +makeContextSnapshotSafe(sqInt ctxt) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + usqInt numSlots; + sqInt toDoLimit; + sqInt toDoStart; + + assert(isContext(ctxt)); + + /* The stack pages have already been discarded. Any remaining married contexts are actually widows. */ + if (((((longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin markContextAsDead: */ + assert(isContext(ctxt)); + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs(SenderIndex, ctxt, GIV(nilObj))); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, ctxt, GIV(nilObj))); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + ensureContextHasBytecodePC(ctxt); + } + + /* Fill slots beyond top of stack with nil */ + toDoStart = (fetchStackPointerOf(ctxt)) + CtxtTempFrameStart; + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(ctxt)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(ctxt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(ctxt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (i = toDoStart; i <= toDoLimit; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs(i, ctxt, GIV(nilObj))); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } +} + + +/* make a Point xValue@yValue. + We know both will be integers so no value nor root checking is needed */ + + /* StackInterpreter>>#makePointwithxValue:yValue: */ +usqInt +makePointwithxValueyValue(sqInt xValue, sqInt yValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newObj; + usqInt numBytes; + usqInt pointResult; + + /* begin eeInstantiatePoint */ + /* begin eeInstantiateSmallClassIndex:format:numSlots: */ + assert(((YIndex + 1) >= 0) + && ((knownClassAtIndex(ClassPointCompactIndex)) != GIV(nilObj))); + assert((nonIndexablePointerFormat()) == (instSpecOfClass(knownClassAtIndex(ClassPointCompactIndex)))); + + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert((YIndex + 1) < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (((YIndex + 1) < 1 + ? 8 /* allocationUnit */ + : (YIndex + 1) * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + pointResult = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) (YIndex + 1))) << (numSlotsFullShift()))) + ((((usqInt)((nonIndexablePointerFormat())) << (formatShift()))))) + ClassPointCompactIndex); + GIV(freeStart) += numBytes; + pointResult = newObj; + /* end eeInstantiatePoint */ +l1: + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pointResult)) + && (!(isForwarded(pointResult)))); + assert(validStorePointerUncheckedArgs(XIndex, pointResult, (((usqInt)xValue << 3) | 1))); + longAtput((void *)((pointResult + BaseHeaderSize) + ((((usqInt)(XIndex) << (shiftForWord()))))),(((usqInt)xValue << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(pointResult)) + && (!(isForwarded(pointResult)))); + assert(validStorePointerUncheckedArgs(YIndex, pointResult, (((usqInt)yValue << 3) | 1))); + longAtput((void *)((pointResult + BaseHeaderSize) + ((((usqInt)(YIndex) << (shiftForWord()))))),(((usqInt)yValue << 3) | 1)); + return pointResult; +} + + +/* The prim trace log is a circular buffer of objects. If there is + an entry at primTraceLogIndex - 1 \\ PrimTraceBufferSize it has entries. + If there is something at primTraceLogIndex it has wrapped. */ + + /* StackInterpreter>>#mapPrimTraceLog */ +static void +mapPrimTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt entryOop; + sqInt i; + sqInt remainder; + + if (!(GIV(primTraceLog)[/* safe:mod: */ + (((remainder = (GIV(primTraceLogIndex) - 1) % PrimTraceLogSize)) < 0 + ? remainder + PrimTraceLogSize + : remainder)])) { + return; + } + if (GIV(primTraceLog)[GIV(primTraceLogIndex)]) { + for (i = GIV(primTraceLogIndex); i < PrimTraceLogSize; i += 1) { + entryOop = GIV(primTraceLog)[i]; + if ((entryOop != 0) + && (/* shouldRemapOop: */ + ((!(entryOop & (tagMask())))) + && (shouldRemapObj(entryOop)))) { + GIV(primTraceLog)[i] = (remapObj(entryOop)); + } + } + } + for (i = 0; i < GIV(primTraceLogIndex); i += 1) { + entryOop = GIV(primTraceLog)[i]; + if ((entryOop != 0) + && (/* shouldRemapOop: */ + ((!(entryOop & (tagMask())))) + && (shouldRemapObj(entryOop)))) { + GIV(primTraceLog)[i] = (remapObj(entryOop)); + } + } +} + + +/* The trace log is a circular buffer of pairs of entries. If there is + an entry at traceLogIndex - 3 \\ TraceBufferSize it has entries. + If there is something at traceLogIndex it has wrapped. */ + + /* StackInterpreter>>#mapTraceLog */ +static void +mapTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt limit; + sqInt oop; + sqInt remainder; + sqInt traceType; + + oop = 0; + limit = /* safe:mod: */ + (((remainder = (GIV(traceLogIndex) - 3) % TraceBufferSize)) < 0 + ? remainder + TraceBufferSize + : remainder); + if (!(GIV(traceLog)[limit])) { + return; + } + if (GIV(traceLog)[GIV(traceLogIndex)]) { + limit = TraceBufferSize - 3; + } + for (i = 0; i <= limit; i += 3) { + traceType = ((usqInt)((GIV(traceLog)[i]))) >> TraceTypeShift; + if ((/* thingIsObjectForTraceType: */ + (traceType <= TraceBlockCreation) + || (((traceType >= TraceOwnVM) && (traceType <= TraceDisownVM)))) + && ((((oop = GIV(traceLog)[i + 1])) != 0) + && (/* shouldRemapOop: */ + ((!(oop & (tagMask())))) + && (shouldRemapObj(oop))))) { + GIV(traceLog)[i + 1] = (remapObj(oop)); + } + if ((/* extraIsObjectForTraceType: */ + (traceType == TraceSend) + || (traceType == TraceContextSwitch)) + && ((((oop = GIV(traceLog)[i + 2])) != 0) + && (/* shouldRemapOop: */ + ((!(oop & (tagMask())))) + && (shouldRemapObj(oop))))) { + GIV(traceLog)[i + 2] = (remapObj(oop)); + } + } +} + + +/* The prim trace log is a circular buffer of objects. If there is + an entry at primTraceLogIndex - 1 \\ PrimTraceBufferSize it has entries. + If there is something at primTraceLogIndex it has wrapped. */ + + /* StackInterpreter>>#markAndTracePrimTraceLog */ +static void +markAndTracePrimTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt entryOop; + sqInt i; + sqInt remainder; + + if (!(GIV(primTraceLog)[/* safe:mod: */ + (((remainder = (GIV(primTraceLogIndex) - 1) % PrimTraceLogSize)) < 0 + ? remainder + PrimTraceLogSize + : remainder)])) { + return; + } + if (GIV(primTraceLog)[GIV(primTraceLogIndex)]) { + for (i = GIV(primTraceLogIndex); i < PrimTraceLogSize; i += 1) { + entryOop = GIV(primTraceLog)[i]; + if ((entryOop != 0) + && ((!(entryOop & (tagMask()))))) { + markAndTrace(entryOop); + } + } + } + for (i = 0; i < GIV(primTraceLogIndex); i += 1) { + entryOop = GIV(primTraceLog)[i]; + if ((entryOop != 0) + && ((!(entryOop & (tagMask()))))) { + markAndTrace(entryOop); + } + } +} + + +/* GC of pages. Throwing away all stack pages on full GC is simple but + dangerous because it causes us to allocate lots of contexts immediately + before a GC. + Reclaiming pages whose top context is not referenced is poor because it + would take N incrementalGCs to reclaim N unused pages. Only the page whose + top context is not referred to by the bottom context of any other page + would be + reclaimed. Not until the next GC would the page whose top contect is the + previously reclaimed page's base frame's bottom context be reclaimed. + + Better is to not mark stack pages until their contexts are encountered. We + can eagerly trace the active page and the page reachable from its bottom + context if any, and so on. Other pages can be marked when we encounter a + married context. */ + + /* StackInterpreter>>#markAndTraceStackPages: */ +static NoDbgRegParms sqInt +markAndTraceStackPages(sqInt fullGCFlag) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt context; + sqInt i; + void *pointer; + sqInt senderOop; + char *theFP; + StackPage *thePage; + StackPage *thePage1; + + + /* On an incremental GC simply consider all non-free stack pages to be roots. */ + if (!fullGCFlag) { + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(thePage))) { + markAndTraceStackPage(thePage); + } + } + return null; + } + + /* On a full GC only eagerly trace pages referenced from + the base of the active page, i.e. on the active stack. */ + if (!GIV(stackPage)) { + return null; + } + thePage = GIV(stackPage); + do { + markAndTraceStackPage(thePage); + theFP = (thePage->baseFP); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + context = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(context)); + assert((context == (nilObject())) + || (isContext(followMaybeForwarded(context)))); + if ((/* isContext: */ + ((!(context & (tagMask())))) + && (((longAt((void *)(context))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && ((((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(context)))))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + pointer = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(pointer), GIV(pages)); + assert(!(isFree(thePage))); + } + } while(((thePage->trace)) < StackPageTraced); + return 0; +} + + +/* The trace log is a circular buffer of pairs of entries. If there is an + entry at + traceLogIndex - 3 \\ TraceBufferSize it has entries. If there is something + at traceLogIndex it has wrapped. */ + + /* StackInterpreter>>#markAndTraceTraceLog */ +static void +markAndTraceTraceLog(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt limit; + sqInt oop; + sqInt remainder; + sqInt traceType; + + limit = /* safe:mod: */ + (((remainder = (GIV(traceLogIndex) - 3) % TraceBufferSize)) < 0 + ? remainder + TraceBufferSize + : remainder); + if (!(GIV(traceLog)[limit])) { + return; + } + if (GIV(traceLog)[GIV(traceLogIndex)]) { + limit = TraceBufferSize - 3; + } + for (i = 0; i <= limit; i += 3) { + traceType = ((usqInt)((GIV(traceLog)[i]))) >> TraceTypeShift; + if (/* thingIsObjectForTraceType: */ + (traceType <= TraceBlockCreation) + || (((traceType >= TraceOwnVM) && (traceType <= TraceDisownVM)))) { + oop = GIV(traceLog)[i + 1]; + if ((oop != 0) + && ((!(oop & (tagMask()))))) { + markAndTrace(oop); + } + } + if (/* extraIsObjectForTraceType: */ + (traceType == TraceSend) + || (traceType == TraceContextSwitch)) { + oop = GIV(traceLog)[i + 2]; + if ((oop != 0) + && ((!(oop & (tagMask()))))) { + markAndTrace(oop); + } + } + } +} + + +/* Trace any untraced pages */ + + /* StackInterpreter>>#markAndTraceUntracedReachableStackPages */ +static void +markAndTraceUntracedReachableStackPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt foundToBeTracedPage; + sqInt i; + StackPage *thePage; + + do { + foundToBeTracedPage = 0; + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((!(isFree(thePage))) + && (((thePage->trace)) == StackPageReachedButUntraced)) { + foundToBeTracedPage = 1; + markAndTraceStackPage(thePage); + } + } + } while(foundToBeTracedPage); +} + + +/* This is a helper for primitiveObjectPointsTo so it *does not* check the + frameContext field because that is an implicit self-reference not present + in the state . + */ + + /* StackInterpreter>>#marriedContext:pointsTo:stackDeltaForCurrentFrame: */ +static NoDbgRegParms sqInt +marriedContextpointsTostackDeltaForCurrentFrame(sqInt spouseContext, sqInt anOop, sqInt stackDeltaForCurrentFrame) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *rcvrOffset; + sqInt senderOop; + char *theFP; + StackPage *thePage; + char *theSP; + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((spouseContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (theFP == GIV(framePointer)) { + theSP = GIV(stackPointer) + (stackDeltaForCurrentFrame * BytesPerWord); + } + else { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + theSP = findSPOfon(theFP, thePage); + } + if ((((anOop) & 7) == 1)) { + if ((anOop == ((((usqInt)(/* stackPointerIndexForFrame:WithSP: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (((usqInt)(((theFP + FoxMFReceiver) - theSP))) >> (shiftForWord())) + (((mframeCogMethod(theFP))->cmNumArgs)) + : (((usqInt)(((theFP + FoxIFReceiver) - theSP))) >> (shiftForWord())) + (byteAt((theFP + FoxIFrameFlags) + 1)))) << 3) | 1))) + || (anOop == (externalInstVarofContext(InstructionPointerIndex, spouseContext)))) { + return 1; + } + } + else { + if (anOop == (/* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)))) { + return 1; + } + if (longAt(theFP + FoxSavedFP)) { + if ((/* frameHasContext: */ + ((((usqInt)(longAt((((char *)(longAt(theFP + FoxSavedFP)))) + FoxMethod)))) < (startOfMemory()) + ? ((longAt((((char *)(longAt(theFP + FoxSavedFP)))) + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt(((((char *)(longAt(theFP + FoxSavedFP)))) + FoxIFrameFlags) + 2)) != 0)) + && (anOop == (longAt((((char *)(longAt(theFP + FoxSavedFP)))) + FoxThisContext)))) { + return 1; + } + } + else { + if (anOop == (frameCallerContext(theFP))) { + return 1; + } + } + } + + /* Check stack and instruction pointer fields. + Check method and sender fields, avoiding unnecessarily reifying sender context. + Now check receiver, temps and stack contents */ + rcvrOffset = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + while (theSP <= rcvrOffset) { + if (anOop == (longAt(theSP))) { + return 1; + } + theSP += BytesPerWord; + } + + /* Finally check stacked receiver (closure field or duplicate of receiver) and arguments */ + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + rcvrOffset = theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord()))))); + while (theSP <= rcvrOffset) { + if (anOop == (longAt(theSP))) { + return 1; + } + theSP += BytesPerWord; + } + return 0; +} + + +/* Establish aContext at the base of a new stackPage, make the stackPage the + active one and set-up the interreter registers. This is used to boot the + system and bring it back after a snapshot. */ + + /* StackInterpreter>>#marryContextInNewStackPageAndInitializeInterpreterRegisters: */ +static NoDbgRegParms void +marryContextInNewStackPageAndInitializeInterpreterRegisters(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + sqInt header; + sqInt methodHeader; + StackPage *newPage; + sqInt top; + + assert(!GIV(stackPage)); + newPage = makeBaseFrameFor(aContext); + + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (newPage->headSP); + GIV(framePointer) = (newPage->headFP); + aMethodObj = longAt(((GIV(stackPage)->headFP)) + FoxMethod); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; +} + + +/* Assert-only check for leaks after external prim calls if checkForLeaks + includes the GCCheckPrimCall flag. + This is ionly really useful from a low-level C debugger, hence no + accessors for checkedPluginName. + */ + + /* StackInterpreter>>#maybeLeakCheckExternalPrimCall: */ +static NoDbgRegParms sqInt +maybeLeakCheckExternalPrimCall(sqInt aMethodObj) +{ + if ((((checkForLeaks & GCCheckPrimCall) != 0)) + && (((primitiveIndexOf(aMethodObj)) == PrimNumberExternalCall) + && (checkedPluginName == (fetchPointerofObject(0, longAt((void *)((aMethodObj + BaseHeaderSize) + (1U << (shiftForWord()))))))))) { + runLeakCheckerFor(GCCheckPrimCall); + } + return 1; +} + + +/* Answer the selector of a method, assuming its penultimate literal is + either a symbol or a pointer object whose first slot references the method + and whose second slot is a symbol (i.e. an AdditionalMethodState). If a + Symbol can't be found answer nil. This isn't satisfactory, as it puts a + lot of information + into the VM, but it is needed for adequate crash debugging at Cadence. + With full blocks as of 9/2016 the last literal of a CompiledBlock is a + back pointer + to the enclosing block or compiled method. */ + + /* StackInterpreter>>#maybeSelectorOfMethod: */ +sqInt +maybeSelectorOfMethod(sqInt methodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt maybeSelector; + usqInt numSlots; + sqInt penultimateLiteral; + sqInt ultimateLiteral; + + assert(isOopCompiledMethod(methodObj)); + ultimateLiteral = ultimateLiteralOf(methodObj); + if (/* isOopCompiledMethod: */ + ((!(ultimateLiteral & (tagMask())))) + && (((byteAt((void *)(ultimateLiteral + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) { + return maybeSelectorOfMethod(ultimateLiteral); + } + penultimateLiteral = penultimateLiteralOf(methodObj); + if (/* isWordsOrBytes: */ + ((!(penultimateLiteral & (tagMask())))) + && (isWordsOrBytesNonImm(penultimateLiteral))) { + return (((longAt((void *)(penultimateLiteral))) & (classIndexMask())) == (fetchClassTagOfNonImm(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))))) + ? penultimateLiteral + : 0); + } + return ((/* isPointers: */ + ((!(penultimateLiteral & (tagMask())))) + && (((byteAt((void *)(penultimateLiteral + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(penultimateLiteral)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(penultimateLiteral + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(penultimateLiteral - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) >= 2) + && (((longAt((void *)((penultimateLiteral + BaseHeaderSize) + (0U << (shiftForWord()))))) == methodObj) + && (((maybeSelector = longAt((void *)((penultimateLiteral + BaseHeaderSize) + (1U << (shiftForWord()))))), + (/* isWordsOrBytes: */ + ((!(maybeSelector & (tagMask())))) + && (isWordsOrBytesNonImm(maybeSelector))) + && (((longAt((void *)(maybeSelector))) & (classIndexMask())) == (fetchClassTagOfNonImm(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord()))))))))))))) + ? maybeSelector + : 0); +} + + /* StackInterpreter>>#methodArgumentCount */ +sqInt +methodArgumentCount(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(argumentCount); +} + + /* StackInterpreter>>#methodClassAssociationOf: */ +sqInt +methodClassAssociationOf(sqInt methodPointer) +{ + sqInt offset; + + offset = (literalCountOf(methodPointer)) - 1; + + /* begin literal:ofMethod: */ + return longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); +} + + +/* Answer the method class of a method which is the value of an Association + in the last literal, + or answer nil if there isn't one. + Using a read barrier here simplifies the become implementation and costs + very little + because the class index and ValueIndex of the association almost certainly + share a cache line. */ + + /* StackInterpreter>>#methodClassOf: */ +sqInt +methodClassOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt literal; + sqInt offset; + + offset = (literalCountOf(methodPointer)) - 1; + + /* begin followLiteral:ofMethod: */ + /* begin followField:ofObject: */ + literal = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = fixFollowedFieldofObjectwithInitialValue(offset + LiteralStart, methodPointer, literal); + } + return ((literal != GIV(nilObj)) + && (/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + ? (assert((numSlotsOf(literal)) > ValueIndex), + followFieldofObject(ValueIndex, literal)) + : GIV(nilObj)); +} + + /* StackInterpreter>>#methodPrimitiveIndex */ +sqInt +methodPrimitiveIndex(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + + if (!((addressCouldBeObj(GIV(newMethod))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())))) { + return -1; + } + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin primitiveIndexOfMethod:header: */ + return (((methodHeader & AlternateHeaderHasPrimFlag) != 0) + ? ((firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize), + (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8)))) + : 0); +} + + +/* Sets the return value for a method. In the CoInterpreter we replace the + cumbersome primResult machinery. */ + + /* StackInterpreter>>#methodReturnBool: */ +sqInt +methodReturnBool(sqInt boolean) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + assert(!((failed()))); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),/* booleanObjectOf: */ + (boolean + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return 0; +} + + +/* Sets the return value for a method. */ + + /* StackInterpreter>>#methodReturnFloat: */ +sqInt +methodReturnFloat(double aFloat) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + assert(!((failed()))); + + /* begin pop:thenPushFloat: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),floatObjectOf(aFloat)); + GIV(stackPointer) = sp; + return 0; +} + + +/* Sets the return value for a method. In the CoInterpreter we replace the + cumbersome primResult machinery. */ + + /* StackInterpreter>>#methodReturnInteger: */ +sqInt +methodReturnInteger(sqInt integer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + assert(!((failed()))); + + /* begin pop:thenPushInteger: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),(((usqInt)integer << 3) | 1)); + GIV(stackPointer) = sp; + return 0; +} + + +/* Sets the return value for a method */ + + /* StackInterpreter>>#methodReturnReceiver */ +sqInt +methodReturnReceiver(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + assert(!((failed()))); + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return 0; +} + + +/* Attempt to answer a ByteString for a given C string as the result of a + primitive. + */ + + /* StackInterpreter>>#methodReturnString: */ +NeverInline sqInt +methodReturnString(const char *aCString) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt result; + + assert(!((failed()))); + if (aCString) { + if ((result = stringForCString(aCString))) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); + } + else { + GIV(primFailCode) = PrimErrNoMemory; + } + } + else { + GIV(primFailCode) = PrimErrOperationFailed; + } + return 0; +} + + +/* Sets the return value for a method. In the CoInterpreter we replace the + cumbersome primResult machinery. */ + + /* StackInterpreter>>#methodReturnValue: */ +sqInt +methodReturnValue(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + assert(!((failed()))); + + /* begin pop:thenPush: */ + longAtput((sp = GIV(stackPointer) + (((GIV(argumentCount) + 1) - 1) * BytesPerWord)),oop); + GIV(stackPointer) = sp; + return 0; +} + + +/* A negative header selects the alternate bytecode set. */ + + /* StackInterpreter>>#methodUsesAlternateBytecodeSet: */ +int +methodUsesAlternateBytecodeSet(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + sqInt methodHeader; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin headerIndicatesAlternateBytecodeSet: */ + return (((sqLong) methodHeader)) < 0; +} + + +/* Answer if aMethodObj contains a primitive and uses the primitive error + code. + */ + + /* StackInterpreter>>#methodUsesPrimitiveErrorCode: */ +static NoDbgRegParms sqInt +methodUsesPrimitiveErrorCode(sqInt aMethodObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethodObj)); + header = longAt((void *)((aMethodObj + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + return ((/* primitiveIndexOfMethod:header: */ + (((methodHeader & AlternateHeaderHasPrimFlag) != 0) + ? ((firstBytecode = (aMethodObj + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize), + (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8)))) + : 0)) > 0) + && ((/* longStoreBytecodeForHeader: */ + ((((sqLong) methodHeader)) < 0 + ? AltLongStoreBytecode + : LongStoreBytecode)) == (fetchByteofObject(((((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart) * BytesPerOop) + 3 /* sizeOfCallPrimitiveBytecode: */, aMethodObj))); +} + + +/* The module with the given name was just unloaded. + Make sure we have no dangling references. */ + + /* StackInterpreter>>#moduleUnloaded: */ +EXPORT(void) +moduleUnloaded(char *aModuleName) +{ + if (!(strcmp(aModuleName, "SurfacePlugin"))) { + /* Surface plugin went away. Should never happen. But then, who knows */ + showSurfaceFn = 0; + } +} + + +/* Brain-damaged nameOfClass: for C VM. Does *not* answer Foo class for + metaclasses. Use e.g. classIsMeta: to avoid being fooled. */ + + /* StackInterpreter>>#nameOfClass: */ +static NoDbgRegParms char * +nameOfClass(sqInt classOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt maybeNameOop; + sqInt maybeThisClassOop; + usqInt numSlots; + usqInt numSlotsUsqInt; + + /* begin numSlotsOf: */ + assert((classIndexOf(classOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(classOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (numSlots == GIV(metaclassNumSlots)) { + maybeThisClassOop = longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord())))))); + if (addressCouldBeClassObj(maybeThisClassOop)) { + return nameOfClass(longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord()))))))); + } + return "bad class"; + } + if (!((numSlots > GIV(classNameIndex)) + && (((maybeNameOop = longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(classNameIndex)) << (shiftForWord()))))))), + /* isBytes: */ + ((!(maybeNameOop & (tagMask())))) + && (((byteAt((void *)(maybeNameOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))))) { + return "bad class"; + } + return firstIndexableField(maybeNameOop); +} + + +/* Brain-damaged nameOfClass: for C VM. Does *not* answer Foo class for + metaclasses. Use e.g. classIsMeta: to avoid being fooled. */ + + /* StackInterpreter>>#nameOfClass:lengthInto: */ +static NoDbgRegParms char * +nameOfClasslengthInto(sqInt classOop, sqInt *lengthPtr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt maybeNameOop; + sqInt maybeThisClassOop; + usqInt numSlots; + usqInt numSlotsUsqInt; + + /* begin numSlotsOf: */ + assert((classIndexOf(classOop)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(classOop + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classOop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (numSlots == GIV(metaclassNumSlots)) { + maybeThisClassOop = longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord())))))); + if (addressCouldBeClassObj(maybeThisClassOop)) { + return nameOfClasslengthInto(longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord())))))), lengthPtr); + } + lengthPtr[0] = 9; + return "bad class"; + } + if ((numSlots > GIV(classNameIndex)) + && (((maybeNameOop = longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(classNameIndex)) << (shiftForWord()))))))), + /* isBytes: */ + ((!(maybeNameOop & (tagMask())))) + && (((byteAt((void *)(maybeNameOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))))) { + lengthPtr[0] = (numBytesOfBytes(maybeNameOop)); + return firstIndexableField(maybeNameOop); + } + lengthPtr[0] = 9; + return "bad class"; +} + + +/* Answer true if there are no marked contexts on thePage. */ + + /* StackInterpreter>>#noMarkedContextsOnPage: */ +static NoDbgRegParms sqInt +noMarkedContextsOnPage(StackPage *thePage) +{ + char *theFP; + + assert(!(isFree(thePage))); + theFP = (thePage->headFP); + do { + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + if ((byteAt((void *)((longAt(theFP + FoxThisContext)) + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + return 0; + } + } + } while(((theFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0); + return 1; +} + + +/* Answer the number of effective pointer fields in the given context. */ + + /* StackInterpreter>>#numSlotsOfMarriedContext: */ +static NoDbgRegParms sqInt +numSlotsOfMarriedContext(sqInt aContext) +{ + sqInt contextSize; + + contextSize = stackPointerIndexForFrame(frameOfMarriedContext(aContext)); + assert((CtxtTempFrameStart + contextSize) <= (numSlotsOf(aContext))); + return CtxtTempFrameStart + contextSize; +} + + /* StackInterpreter>>#numStkPages */ +static sqInt +numStkPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(numStackPages); +} + + /* StackInterpreter>>#object:equalsString: */ +static NoDbgRegParms sqInt +objectequalsString(sqInt anOop, char *aCString) +{ + sqInt len; + + len = strlen(aCString); + return /* object:equalsString:ofSize: */ + (/* isBytes: */ + ((!(anOop & (tagMask())))) + && (((byteAt((void *)(anOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && ((!(((byteAt((void *)(anOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((numBytesOfBytes(anOop)) == len) + && ((strncmp(aCString, firstIndexableField(anOop), len)) == 0))); +} + + /* StackInterpreter>>#pcPreviousTo:inSqueakV3PlusClosuresOrSistaV1Method: */ +usqInt +pcPreviousToinSqueakV3PlusClosuresOrSistaV1Method(sqInt theIP, sqInt aMethod) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt byte; + static unsigned char extensionSizes[] = { + 2, 2, 2, 2, 3, 2, 2, 1, 1, 1, 2, 3, 3, 3, 3, 4 + }; + sqInt header; + sqInt methodHeader; + usqInt previousPC; + usqInt thisIP; + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(aMethod)); + header = longAt((void *)((aMethod + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + /* begin pcPreviousTo:inSistaV1Method: */ + thisIP = ((((literalCountOf(aMethod)) + LiteralStart) * BytesPerOop) + aMethod) + BaseHeaderSize; + assert(oopisLessThan(thisIP, theIP)); + while (oopisLessThan(thisIP, theIP)) { + previousPC = thisIP; + do { + byte = byteAt((void *)(thisIP)); + thisIP += /* sizeOfSistaV1Bytecode: */ + (byte < 224 + ? 1 + : (byte < 0xF8 + ? 2 + : 3)); + } while(((byte >= 224) && (byte <= 225))); + } + return previousPC; + } + + /* begin pcPreviousTo:inSqueakV3PlusClosuresMethod: */ + thisIP = ((((literalCountOf(aMethod)) + LiteralStart) * BytesPerOop) + aMethod) + BaseHeaderSize; + assert(oopisLessThan(thisIP, theIP)); + while (oopisLessThan(thisIP, theIP)) { + previousPC = thisIP; + byte = byteAt((void *)(thisIP)); + thisIP += (byte <= 125 + ? 1 + : (byte >= 176 + ? 1 + : (byte >= 160 + ? 2 + : (byte >= 144 + ? 1 + : (byte >= 128 + ? extensionSizes[byte - 128] + : (assertf("illegal bytecode in sizeOfSqueakV3Bytecode:"), + 1)))))); + } + return previousPC; +} + + /* StackInterpreter>>#penultimateLiteralOf: */ +static NoDbgRegParms sqInt +penultimateLiteralOf(sqInt aMethodOop) +{ + sqInt offset; + + assert(isOopCompiledMethod(aMethodOop)); + offset = (literalCountOf(aMethodOop)) - 2; + + /* begin literal:ofMethod: */ + return longAt((void *)((aMethodOop + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#popStack */ +sqInt +popStack(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt top; + + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + return top; +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#pop: */ +sqInt +pop(sqInt nItems) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(stackPointer) += nItems * BytesPerWord; + return null; +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#pop:thenPush: */ +void +popthenPush(sqInt nItems, sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + longAtput((sp = GIV(stackPointer) + ((nItems - 1) * BytesPerWord)),oop); + GIV(stackPointer) = sp; +} + + +/* Update the displayBits after a GC may have moved it. + Answer if the displayBits appear valid. The wrinkle here is that the + displayBits could be a surface handle. */ + + /* StackInterpreter>>#postGCUpdateDisplayBits */ +static sqInt +postGCUpdateDisplayBits(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void *bitsNow; + sqInt bitsOop; + sqInt displayObj; + + displayObj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheDisplay) << (shiftForWord())))))); + if (!((/* isPointers: */ + ((!(displayObj & (tagMask())))) + && (((byteAt((void *)(displayObj + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((lengthOf(displayObj)) >= 4))) { + return 0; + } + + /* begin followOopField:ofObject: */ + bitsOop = longAt((void *)((displayObj + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isOopForwarded: */ + ((!(bitsOop & (tagMask())))) + && ((!((longAt((void *)(bitsOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + bitsOop = fixFollowedFieldofObjectwithInitialValue(0, displayObj, bitsOop); + } + if ((bitsOop == GIV(nilObj)) + || ((((bitsOop) & 7) == 1))) { + return 1; + } + + /* it ain't yet set + It's a surface; our work here is done... */ + assert((addressCouldBeObj(bitsOop)) + && (isWordsOrBytes(bitsOop))); + if (!((byteAt((void *)(bitsOop + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift())))) { + if (GIV(stackPage)) { + pinObject(bitsOop); + + /* begin followOopField:ofObject: */ + bitsOop = longAt((void *)((displayObj + BaseHeaderSize) + (0U << (shiftForWord())))); + if (/* isOopForwarded: */ + ((!(bitsOop & (tagMask())))) + && ((!((longAt((void *)(bitsOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + bitsOop = fixFollowedFieldofObjectwithInitialValue(0, displayObj, bitsOop); + } + } + + /* If stackPage is nil we're snapshotting and now is not the time to pin. */ + bitsNow = firstIndexableField(bitsOop); + if (displayBits != bitsNow) { + displayBits = bitsNow; + ioNoteDisplayChangedwidthheightdepth(displayBits, displayWidth, displayHeight, displayDepth); + } + } + return 1; +} + + +/* With 0 args answers whether ioProcessEvents is enabled and being called. + With 1 arg expects a boolean which will enable ioProcessEvents if true and + disable it if false, answering its previous state. */ + + /* StackInterpreter>>#primitiveEventProcessingControl */ +EXPORT(void) +primitiveEventProcessingControl(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + int enabled; + char *sp; + + enabled = inIOProcessEvents >= 0; + if (!GIV(argumentCount)) { + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer)),/* booleanObjectOf: */ + (enabled + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return; + } + if (GIV(argumentCount) == 1) { + if ((longAt(GIV(stackPointer))) == GIV(trueObj)) { + if (inIOProcessEvents < 0) { + inIOProcessEvents = 0; + } + } + else { + if ((longAt(GIV(stackPointer))) == GIV(falseObj)) { + inIOProcessEvents = -1; + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (enabled + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; +} + + /* StackInterpreter>>#primitiveFloatEqual:toArg: */ +static NoDbgRegParms int +primitiveFloatEqualtoArg(sqInt rcvrOop, sqInt argOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + double rcvr; + double result; + sqInt tagBits; + double value; + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l2: + if (rcvr == arg) { + if ((((argOop) & 7) == 1)) { + return (((sqInt)rcvr)) == ((argOop >> 3)); + } + + /* Resolve case of ambiguity so as to have comparison of exact values */ + if ((((rcvrOop) & 7) == 1)) { + /* Same when used from bytecodePrim... + note that rcvr and arg cannot be both integer (case is already handled) */ + return ((rcvrOop >> 3)) == (((sqInt)arg)); + } + } + return rcvr == arg; +} + + /* StackInterpreter>>#primitiveFloatGreaterOrEqual:toArg: */ +static NoDbgRegParms int +primitiveFloatGreaterOrEqualtoArg(sqInt rcvrOop, sqInt argOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + double rcvr; + double result; + sqInt tagBits; + double value; + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l2: + if (rcvr == arg) { + if ((((argOop) & 7) == 1)) { + return (((sqInt)rcvr)) >= ((argOop >> 3)); + } + + /* Resolve case of ambiguity so as to have comparison of exact values */ + if ((((rcvrOop) & 7) == 1)) { + /* Same when used from bytecodePrim... + note that rcvr and arg cannot be both integer (case is already handled) */ + return ((rcvrOop >> 3)) >= (((sqInt)arg)); + } + } + return rcvr >= arg; +} + + /* StackInterpreter>>#primitiveFloatGreater:thanArg: */ +static NoDbgRegParms int +primitiveFloatGreaterthanArg(sqInt rcvrOop, sqInt argOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + double rcvr; + double result; + sqInt tagBits; + double value; + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l2: + if (rcvr == arg) { + if ((((argOop) & 7) == 1)) { + return (((sqInt)rcvr)) > ((argOop >> 3)); + } + + /* Resolve case of ambiguity so as to have comparison of exact values */ + if ((((rcvrOop) & 7) == 1)) { + /* Same when used from bytecodePrim... + note that rcvr and arg cannot be both integer (case is already handled) */ + return ((rcvrOop >> 3)) > (((sqInt)arg)); + } + } + return rcvr > arg; +} + + /* StackInterpreter>>#primitiveFloatLessOrEqual:toArg: */ +static NoDbgRegParms int +primitiveFloatLessOrEqualtoArg(sqInt rcvrOop, sqInt argOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + double rcvr; + double result; + sqInt tagBits; + double value; + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l2: + if (rcvr == arg) { + if ((((argOop) & 7) == 1)) { + return (((sqInt)rcvr)) <= ((argOop >> 3)); + } + + /* Resolve case of ambiguity so as to have comparison of exact values */ + if ((((rcvrOop) & 7) == 1)) { + /* Same when used from bytecodePrim... + note that rcvr and arg cannot be both integer (case is already handled) */ + return ((rcvrOop >> 3)) <= (((sqInt)arg)); + } + } + return rcvr <= arg; +} + + /* StackInterpreter>>#primitiveFloatLess:thanArg: */ +static NoDbgRegParms int +primitiveFloatLessthanArg(sqInt rcvrOop, sqInt argOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + double arg; + usqLong bits; + double rcvr; + double result; + sqInt tagBits; + double value; + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = rcvrOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(rcvrOop)); + bits = ((((usqInt)rcvrOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + rcvr = value; + goto l1; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + rcvr = ((double) ((rcvrOop >> 3)) ); + goto l1; + } + } + else { + if (((longAt((void *)(rcvrOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(rcvrOop + BaseHeaderSize, result); + rcvr = result; + goto l1; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + rcvr = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l1: + + /* begin loadFloatOrIntForComparisonFrom: */ + if ((tagBits = argOop & (tagMask()))) { + if (tagBits == (smallFloatTag())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(argOop)); + bits = ((((usqInt)argOop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&value), (&bits), sizeof(value)); + arg = value; + goto l2; + } + if ((numericPrimsMixComparison()) + && (tagBits == (smallIntegerTag()))) { + arg = ((double) ((argOop >> 3)) ); + goto l2; + } + } + else { + if (((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex) { + fetchFloatAtinto(argOop + BaseHeaderSize, result); + arg = result; + goto l2; + } + } + + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = 0.0; + /* end loadFloatOrIntForComparisonFrom: */ +l2: + if (rcvr == arg) { + if ((((argOop) & 7) == 1)) { + return (((sqInt)rcvr)) < ((argOop >> 3)); + } + + /* Resolve case of ambiguity so as to have comparison of exact values */ + if ((((rcvrOop) & 7) == 1)) { + /* Same when used from bytecodePrim... + note that rcvr and arg cannot be both integer (case is already handled) */ + return ((rcvrOop >> 3)) < (((sqInt)arg)); + } + } + return rcvr < arg; +} + + +/* Note: With the Squeak V3 format we now have 10 bits of primitive index, + but they are + in two places for temporary backward compatibility. The time to unpack is + negligible, since the derived primitive function pointer is stored in the + method cache. With the + Spur format we assume a 3-byte CallPrimitive with a little-endian 16-bit + primitive index. */ + + /* StackInterpreter>>#primitiveIndexOfMethod:header: */ +sqInt +primitiveIndexOfMethodheader(sqInt theMethod, sqInt methodHeader) +{ + usqInt firstBytecode; + + return (((methodHeader & AlternateHeaderHasPrimFlag) != 0) + ? ((firstBytecode = (theMethod + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize), + (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8)))) + : 0); +} + + /* StackInterpreter>>#primitiveIndexOf: */ +sqInt +primitiveIndexOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt firstBytecode; + sqInt header; + sqInt methodHeader; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin primitiveIndexOfMethod:header: */ + return (((methodHeader & AlternateHeaderHasPrimFlag) != 0) + ? ((firstBytecode = (methodPointer + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize), + (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8)))) + : 0); +} + + +/* Return the method an external primitive was defined in */ + + /* StackInterpreter>>#primitiveMethod */ +usqInt +primitiveMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return GIV(newMethod); +} + + /* StackInterpreter>>#printActivationNameForSelector:startClass: */ +static NoDbgRegParms void +printActivationNameForSelectorstartClass(sqInt aSelector, sqInt startClass) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classDict; + usqInt classDictSize; + sqInt currClass; + sqInt i; + sqInt methClass; + usqInt numSlots; + sqInt objOop; + + if (addressCouldBeObj(startClass)) { + /* begin findClassForSelector:lookupClass:do: */ + currClass = startClass; + do { + classDict = longAt((void *)((currClass + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord())))))); + + /* begin numSlotsOf: */ + assert((classIndexOf(classDict)) > (isForwardedObjectClassIndexPun())); + classDictSize = (((numSlots = byteAt((void *)(classDict + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(classDict - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + i = SelectorStart; + while (i < classDictSize) { + if (aSelector == (longAt((void *)((classDict + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))))) { + methClass = currClass; + goto l1; + } + i += 1; + } + + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((currClass + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, currClass, objOop); + } + currClass = objOop; + } while(!(currClass == GIV(nilObj))); + methClass = null; + /* end findClassForSelector:lookupClass:do: */ +l1: + if ((!methClass) + || (startClass == methClass)) { + printNameOfClasscount(methClass, 5); + printChar('>'); + if (!methClass) { + printStringOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord()))))))); + print(" "); + } + } + else { + printNameOfClasscount(startClass, 5); + printChar('('); + printNameOfClasscount(methClass, 5); + printChar(')'); + printChar('>'); + } + } + else { + print("INVALID CLASS"); + } + if (/* addressCouldBeOop: */ + (((aSelector & (tagMask())) != 0)) + || (addressCouldBeObj(aSelector))) { + if (/* isBytes: */ + ((!(aSelector & (tagMask())))) + && (((byteAt((void *)(aSelector + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) { + printStringOf(aSelector); + } + else { + /* begin printOopShort: */ + printOopShortInner(aSelector); + fflush(GIV(transcript)); + } + } + else { + print("INVALID SELECTOR"); + } +} + + /* StackInterpreter>>#printActivationNameFor:receiver:isBlock:firstTemporary: */ +static NoDbgRegParms void +printActivationNameForreceiverisBlockfirstTemporary(sqInt aMethod, sqInt anObject, sqInt isBlock, sqInt maybeMessage) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt classObj; + sqInt homeMethod; + sqInt methClass; + sqInt methodSel; + sqInt tagBits; + + if (isBlock) { + print("[] in "); + homeMethod = homeMethodOf(aMethod); + } + else { + homeMethod = aMethod; + } + methClass = findClassOfMethodforReceiver(homeMethod, anObject); + methodSel = findSelectorOfMethod(homeMethod); + if ((/* addressCouldBeOop: */ + (((anObject & (tagMask())) != 0)) + || (addressCouldBeObj(anObject))) + && ((!(/* isOopForwarded: */ + ((!(anObject & (tagMask())))) + && ((!((longAt((void *)(anObject))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) + && (addressCouldBeClassObj((classObj = /* fetchClassOf: */ + ((tagBits = anObject & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(anObject))))))) { + printNameOfClasscount(classObj, 5); + if (!((classObj == methClass) + || ((!methClass) + || (methClass == GIV(nilObj))))) { + printChar('('); + printNameOfClasscount(methClass, 5); + printChar(')'); + } + } + else { + print("INVALID RECEIVER"); + } + printChar('>'); + if (/* addressCouldBeOop: */ + (((methodSel & (tagMask())) != 0)) + || (addressCouldBeObj(methodSel))) { + if (methodSel == GIV(nilObj)) { + print("(nil)"); + } + else { + if (((methodSel & (tagMask())) != 0)) { + printChar('('); + printHex(methodSel); + printChar(')'); + } + else { + printStringOf(methodSel); + } + } + } + else { + print("INVALID SELECTOR = "); + printHex(methodSel); + } + if ((methodSel == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorDoesNotUnderstand) << (shiftForWord())))))))) + && ((addressCouldBeObj(maybeMessage)) + && ((fetchClassOfNonImm(maybeMessage)) == (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassMessage) << (shiftForWord())))))))))) { + /* print arg message selector */ + methodSel = longAt((void *)((maybeMessage + BaseHeaderSize) + ((((usqInt)(MessageSelectorIndex) << (shiftForWord())))))); + + /* begin space */ + printChar(' '); + printStringOf(methodSel); + } +} + + +/* Print all the stacks of all running processes, including those that are + currently suspended. + */ +/* useful for VM debugging */ + + /* StackInterpreter>>#printAllStacksOn: */ +void +printAllStacksOn(FILE *aStdioStream) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + FILE *savedTranscript; + + /* begin withRedirectedOutputTo:do: */ + savedTranscript = GIV(transcript); + GIV(transcript) = (aStdioStream + ? aStdioStream + : stdout); + printAllStacks(); + GIV(transcript) = savedTranscript; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printCallStack */ +void +printCallStack(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *fp; + + if ((fp = GIV(framePointer))) { + printCallStackFP(fp); + } + else { + printCallStackOf(fetchPointerofObject(SuspendedContextIndex, activeProcess())); + } +} + + /* StackInterpreter>>#printCallStackFP: */ +static NoDbgRegParms sqInt +printCallStackFP(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt context; + + context = shortReversePrintFrameAndCallers(theFP); + while (!(context == GIV(nilObj))) { + if (((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (!(checkIsStillMarriedContextcurrentFP(context, GIV(framePointer)))) { + shortPrintContext(context); + return null; + } + context = shortReversePrintFrameAndCallers(frameOfMarriedContext(context)); + } + else { + context = printContextCallStackOf(context); + } + } + return 0; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printCallStackOf: */ +sqInt +printCallStackOf(sqInt aContextOrProcessOrFrame) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt context; + + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)aContextOrProcessOrFrame)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)aContextOrProcessOrFrame)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)aContextOrProcessOrFrame)) <= (((usqInt)GIV(pages)))))))) { + return printCallStackFP(((char *)aContextOrProcessOrFrame)); + } + if (aContextOrProcessOrFrame == (activeProcess())) { + return printCallStackOf(((sqInt)GIV(framePointer))); + } + if (couldBeProcess(aContextOrProcessOrFrame)) { + return printCallStackOf(longAt((void *)((aContextOrProcessOrFrame + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))))); + } + context = aContextOrProcessOrFrame; + while (!(context == GIV(nilObj))) { + if (((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (!(checkIsStillMarriedContextcurrentFP(context, GIV(framePointer)))) { + shortPrintContext(context); + return null; + } + context = shortReversePrintFrameAndCallers(frameOfMarriedContext(context)); + } + else { + context = printContextCallStackOf(context); + } + } + return 0; +} + + /* StackInterpreter>>#printCallStackOf:currentFP: */ +static NoDbgRegParms sqInt +printCallStackOfcurrentFP(sqInt aContext, char *currFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + sqInt senderOop; + char *theFP; + StackPage *thePage; + + ctxt = aContext; + while (!(ctxt == GIV(nilObj))) { + if (((((longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (checkIsStillMarriedContextcurrentFP(ctxt, currFP)) { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (isFree(thePage)) { + fprintf(GIV(transcript), + "%p is on a free page?!\n", + theFP); + return null; + } + shortPrintFrameAndCallers(theFP); + theFP = (thePage->baseFP); + ctxt = frameCallerContext(theFP); + if ((!((longAt((void *)(ctxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + ctxt = followForwarded(ctxt); + } + } + else { + fprintf(GIV(transcript), + "widowed caller frame %p\n", + theFP); + return null; + } + } + else { + shortPrintContext(ctxt); + ctxt = longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + } + } + return 0; +} + + +/* Print the call stack on a specific output stream. */ +/* useful for VM debugging */ + + /* StackInterpreter>>#printCallStackOn: */ +void +printCallStackOn(FILE *aStdioStream) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + FILE *savedTranscript; + + /* begin withRedirectedOutputTo:do: */ + savedTranscript = GIV(transcript); + GIV(transcript) = (aStdioStream + ? aStdioStream + : stdout); + printCallStack(); + GIV(transcript) = savedTranscript; +} + + +/* Print the call stack of aContext until it links to a frame. */ + + /* StackInterpreter>>#printContextCallStackOf: */ +static NoDbgRegParms sqInt +printContextCallStackOf(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + + ctxt = aContext; + while (!((ctxt == GIV(nilObj)) + || (((((longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)))) { + shortPrintContext(ctxt); + ctxt = longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + } + return ctxt; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printContext: */ +void +printContext(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt ip; + sqInt meth; + sqInt sender; + sqInt sp; + + shortPrintContext(aContext); + sender = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + ip = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord())))))); + if ((((sender) & 7) == 1)) { + if (checkIsStillMarriedContextcurrentFP(aContext, GIV(framePointer))) { + print("married (assuming framePointer valid)"); + cr(); + } + else { + print("widowed (assuming framePointer valid)"); + cr(); + } + print("sender "); + printNum(sender); + print(" ("); + printHexPtrnp( + (/* begin withoutSmallIntegerTags: */ + assert((((sender) & 7) == 1)), + ((char *)(sender - (smallIntegerTag()))))); + printChar(')'); + cr(); + print("ip "); + printNum(ip); + print(" ("); + printHexPtrnp( + (/* begin withoutSmallIntegerTags: */ + assert((((ip) & 7) == 1)), + ((char *)(ip - (smallIntegerTag()))))); + printChar(')'); + cr(); + } + else { + print("sender "); + shortPrintOop(sender); + print("ip "); + if (ip == GIV(nilObj)) { + shortPrintOop(ip); + } + else { + printNum(ip); + print(" ("); + printNum((ip >> 3)); + + /* begin space */ + printChar(' '); + printHex((ip >> 3)); + printChar(')'); + cr(); + } + } + sp = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + print("sp "); + printNum((sp >> 3)); + print(" ("); + printHex(sp); + printChar(')'); + cr(); + print("method "); + + /* begin printMethodFieldForPrintContext: */ + meth = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + fprintf(GIV(transcript), + "%p: ", + ((void *)meth)); + printOopShortInner(meth); + if (methodHasCogMethod(meth)) { + fprintf(GIV(transcript), + " (%p)", + cogMethodOf(meth)); + } + cr(); + print("closure "); + shortPrintOop(longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(ClosureIndex) << (shiftForWord()))))))); + print("receiver "); + shortPrintOop(longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord()))))))); + sp = (sp >> 3); + sp = ((sp < ((lengthOf(aContext)) - ReceiverIndex)) ? sp : ((lengthOf(aContext)) - ReceiverIndex)); + for (i = 1; i <= sp; i += 1) { + print(" "); + printNum(i); + + /* begin space */ + printChar(' '); + shortPrintOop(longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)((ReceiverIndex + i)) << (shiftForWord()))))))); + } +} + + /* StackInterpreter>>#printDecodeMethodHeaderOop: */ +static NoDbgRegParms void +printDecodeMethodHeaderOop(sqInt methodHeaderOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin printOopShort: */ + printOopShortInner(methodHeaderOop); + fflush(GIV(transcript)); + if (((methodHeaderOop & AlternateHeaderHasPrimFlag) != 0)) { + print(" hasPrim"); + } + if (methodHeaderOop & LargeContextBit) { + print(" largeFrame"); + } + if (((methodHeaderOop >> 3)) < 0) { + print(" altSet"); + } + print(" nLits "); + printNum( + (/* begin literalCountOfMethodHeader: */ + assert((((methodHeaderOop) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeaderOop >> 3)) & AlternateHeaderNumLiteralsMask)); + print(" nArgs "); + printNum((((usqInt)(methodHeaderOop)) >> MethodHeaderArgCountShift) & 15); + print(" nTemps "); + printNum((((usqInt)(methodHeaderOop)) >> MethodHeaderTempCountShift) & 0x3F); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printExternalHeadFrame */ +void +printExternalHeadFrame(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + printFrameWithSP(GIV(framePointer), GIV(stackPointer)); +} + + /* StackInterpreter>>#printFrameAndCallers:SP:short: */ +static NoDbgRegParms sqInt +printFrameAndCallersSPshort(char *theFP, char *theSP, sqInt printShort) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!(/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + return null; + } + if (longAt(theFP + FoxSavedFP)) { + printFrameAndCallersSPshort(((char *)(longAt(theFP + FoxSavedFP))), frameCallerSP(theFP), printShort); + } + if (printShort) { + shortPrintFrame(theFP); + return null; + } + cr(); + printFrameWithSP(theFP, theSP); + return 0; +} + + /* StackInterpreter>>#printFrameOop:at: */ +static NoDbgRegParms void +printFrameOopat(char *name, char *address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt it; + + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%12s: %16p\t=" /* pst: */, + name, + ((void *)it)); + printOopShortInner(it); + cr(); +} + + /* StackInterpreter>>#printFrameOop:index:at: */ +static NoDbgRegParms void +printFrameOopindexat(char *name, sqInt idx, char *address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt it; + + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%s%10s%" PRIdSQINT ": %16p\t" /* pst: */, + (idx > 9 + ? "" + : " "), + name, + idx, + ((void *)it)); + printOopShortInner(it); + cr(); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printFramesInPage: */ +void +printFramesInPage(StackPage *thePage) +{ + printFrameAndCallersSPshort((thePage->headFP), (thePage->headSP), 0); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printFramesOnStackPageListInUse */ +void +printFramesOnStackPageListInUse(void) +{ + StackPage *page; + + page = GIV(mostRecentlyUsedPage); + do { + if (!(isFree(page))) { + print("page "); + printHexPtrnp(page); + cr(); + printFramesInPage(page); + cr(); + } + } while(((page = (page->prevPage))) != (GIV(mostRecentlyUsedPage))); +} + + /* StackInterpreter>>#printFrameThing:andFrame:at: */ +static NoDbgRegParms void +printFrameThingandFrameat(char *name, char *theFP, char *address) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt it; + + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%12s: %16p" /* pst: */, + name, + ((void *)it)); + if (it) { + if (it == GIV(nilObj)) { + fprintf(GIV(transcript), "=nil"); + } + else { + fprintf(GIV(transcript), + "=%" PRIdSQINT "", + it); + } + } + fprintf(GIV(transcript), + " frame: %p\n", + theFP); +} + + /* StackInterpreter>>#printFrameThing:at:extraString: */ +static NoDbgRegParms void +printFrameThingatextraString(char *name, char *address, char *extraStringOrNil) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt it; + + it = longAt(address); + + /* begin printFrameAddress: */ + fprintf(GIV(transcript), + "%16p:", + ((void *)address)); + fprintf(GIV(transcript), + "%12s: %16p" /* pst: */, + name, + ((void *)it)); + framePrintDescription(it); + if (extraStringOrNil) { + fprintf(GIV(transcript), + "%s", + extraStringOrNil); + } + cr(); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printFrame: */ +sqInt +printFrame(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *aFrame; + char *frameAbove; + char *prevFrame; + char *startFrame; + StackPage *thePage; + char *theSP; + + if (!(/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + if ((addressCouldBeObj(((sqInt)theFP))) + && ((isInMemory(((sqInt)theFP))) + && ((((longAt((void *)(((sqInt)theFP)))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (checkIsStillMarriedContextcurrentFP(((sqInt)theFP), GIV(framePointer)))))) { + return printFrame(frameOfMarriedContext(((sqInt)theFP))); + } + fprintf(GIV(transcript), + "%p is not in the stack zone?!\n", + theFP); + return null; + } + frameAbove = null; + if (theFP == GIV(framePointer)) { + theSP = GIV(stackPointer); + } + else { + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (isFree(thePage)) { + fprintf(GIV(transcript), + "%p is on a free page?!\n", + theFP); + return null; + } + if ((thePage != GIV(stackPage)) + && (theFP == ((thePage->headFP)))) { + theSP = (thePage->headSP); + } + else { + startFrame = ((thePage == GIV(stackPage)) + && (((GIV(framePointer) >= ((thePage->realStackLimit))) && (GIV(framePointer) <= ((thePage->baseAddress))))) + ? GIV(framePointer) + : (thePage->headFP)); + + /* begin safeFindFrameAbove:on:startingFrom: */ + prevFrame = ((char *) 0); + assert(!(isFree(thePage))); + if (startFrame == theFP) { + frameAbove = null; + goto l1; + } + aFrame = startFrame; + while (1) { + prevFrame = aFrame; + aFrame = ((char *)(longAt(aFrame + FoxSavedFP))); + if (!(aFrame != 0)) break; + if (theFP == aFrame) { + frameAbove = prevFrame; + goto l1; + } + } + frameAbove = null; + /* end safeFindFrameAbove:on:startingFrom: */ +l1: + theSP = (frameAbove + ? frameCallerSP(frameAbove) + : ((char *) null)); + } + } + if (!theSP) { + fprintf(GIV(transcript), "could not find sp; using bogus value\n"); + theSP = /* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver); + } + printFrameWithSP(theFP, theSP); + if (frameAbove) { + /* begin printFrameThing:at: */ + printFrameThingatextraString("frame pc", frameAbove + FoxCallerSavedIP, ((char *)null)); + } + return 0; +} + + +/* Print p in hex, unpadded, in the form 0x1234 (C)/16r1234 (here) */ + + /* StackInterpreter>>#printHexPtrnp: */ +static NoDbgRegParms sqInt +printHexPtrnp(void *p) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return fprintf(GIV(transcript), + "%p", + p); +} + + +/* Print n in hex, in the form ' 0x1234', padded to a width of 10 characters + in 32-bits ('0x' + 8 nibbles) or 18 characters in 64-bits ('0x' + 16 + nibbles). In the simulator use 16r as the prefix, padding to 11 characters + in 32-bits + or 19 characters in 64-bits. */ + + /* StackInterpreter>>#printHex: */ +void +printHex(usqInt n) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fprintf(GIV(transcript), + "%16p" /* pst: */, + ((void *)n)); +} + + +/* Print all methods whose penultimate literal is either selector, + or an object whose first inst var is the method and whose + second is selector (e.g. an AdditionalMethodState). */ +/* useful for VM debugging */ + + /* StackInterpreter>>#printLikelyImplementorsOfSelector: */ +void +printLikelyImplementorsOfSelector(sqInt selector) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt address; + sqInt classIndex; + sqInt followingWord; + usqInt followingWordAddress; + sqInt methodClassAssociation; + usqInt numSlots; + sqInt objSqInt; + sqInt prevObj; + sqInt prevPrevObj; + sqInt startObject; + + /* begin allObjectsDo: */ + address = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(address + (numSlotsFieldByteOffset()))); + startObject = (numSlots == (numSlotsMask()) + ? address + BaseHeaderSize + : address); + + /* begin allEntitiesFrom:do: */ + prevPrevObj = (prevObj = null); + objSqInt = startObject; + enableObjectEnumerationFrom(startObject); + while (1) { + assert((objSqInt % (allocationUnit())) == 0); + if (!(oopisLessThan(objSqInt, GIV(endOfMemory)))) break; + assert((long64At((void *)(objSqInt))) != 0); + + /* begin isEnumerableObject: */ + classIndex = (longAt((void *)(objSqInt))) & (classIndexMask()); + assert((classIndex == (segmentBridgePun())) + || ((classIndex == (isForwardedObjectClassIndexPun())) + || (((long64At((void *)(objSqInt))) != 0) + && (classIndex < (GIV(numClassTablePages) * (classTablePageSize())))))); + if (classIndex >= (isForwardedObjectClassIndexPun())) { + if ((((byteAt((void *)(objSqInt + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) + && ((maybeSelectorOfMethod(objSqInt)) == selector)) { + /* try and print the key of the method class association (the name of the implementing class) */ + methodClassAssociation = methodClassAssociationOf(objSqInt); + + /* printHexnp: */ + fprintf(GIV(transcript), + "%p", + ((void *)objSqInt)); + + /* begin space */ + printChar(' '); + printOopShortInner(((/* isPointers: */ + ((!(methodClassAssociation & (tagMask())))) + && (((byteAt((void *)(methodClassAssociation + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && ((numSlotsOf(methodClassAssociation)) >= 2) + ? longAt((void *)((methodClassAssociation + BaseHeaderSize) + (0U << (shiftForWord())))) + : methodClassAssociation)); + cr(); + } + } + prevPrevObj = prevObj; + prevObj = objSqInt; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objSqInt); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objSqInt = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objSqInt = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objSqInt, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l1: + assert(oopisGreaterThan(objSqInt, prevObj)); + } +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printMethodCache */ +void +printMethodCache(void) +{ + printMethodCacheFor(-1); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printMethodDictionaryOf: */ +void +printMethodDictionaryOf(sqInt behavior) +{ + printMethodDictionary(longAt((void *)((behavior + BaseHeaderSize) + ((((usqInt)(MethodDictionaryIndex) << (shiftForWord()))))))); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printMethodDictionary: */ +void +printMethodDictionary(sqInt dictionary) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt meth; + sqInt methodArray; + sqInt selector; + sqInt toDoLimit; + + methodArray = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(MethodArrayIndex) << (shiftForWord())))))); + toDoLimit = (numSlotsOf(dictionary)) - 1; + for (index = SelectorStart; index <= toDoLimit; index += 1) { + selector = longAt((void *)((dictionary + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + if (selector != GIV(nilObj)) { + meth = longAt((void *)((methodArray + BaseHeaderSize) + ((((usqInt)((index - SelectorStart)) << (shiftForWord())))))); + printOopShortInner(selector); + print(" => "); + printOopShortInner(meth); + print(" ("); + printHex(selector); + print(" => "); + printHex(meth); + print(")\n"); + } + } +} + + +/* Details: The count argument is used to avoid a possible infinite recursion + if classOop is a corrupted object. + */ + + /* StackInterpreter>>#printNameOfClass:count: */ +static NoDbgRegParms void +printNameOfClasscount(sqInt classOop, sqInt cnt) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + + if (!GIV(classNameIndex)) { + print("??nil cnidx??"); + return; + } + if ((!classOop) + || ((classOop == 0) + || (cnt <= 0))) { + print("bad class"); + return; + } + numSlots = numSlotsOf(classOop); + if ((numSlots == GIV(metaclassNumSlots)) + && (GIV(metaclassNumSlots) > GIV(thisClassIndex))) { + printNameOfClasscount(longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(thisClassIndex)) << (shiftForWord())))))), cnt - 1); + print(" class"); + } + else { + if (numSlots <= GIV(classNameIndex)) { + print("bad class"); + } + else { + printStringOf(longAt((void *)((classOop + BaseHeaderSize) + ((((usqInt)(GIV(classNameIndex)) << (shiftForWord()))))))); + } + } +} + + /* StackInterpreter>>#printNum: */ +static NoDbgRegParms void +printNum(sqInt n) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fprintf(GIV(transcript), + "%" PRIdSQINT "", + ((sqInt)n)); +} + + /* StackInterpreter>>#printOopShortInner: */ +static NoDbgRegParms void +printOopShortInner(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong bits; + sqInt classLookupKey; + sqInt classOop; + double f; + char *name; + sqInt nameLen; + sqInt objOop; + double result; + sqInt tagBits; + sqInt target; + + if (((oop & (tagMask())) != 0)) { + if (((oop & (characterTag())) != 0)) { + fprintf(GIV(transcript), + "$%c(%" PRIxSQINT ")", + ((int) (((((usqInt)oop))) >> (numTagBits()))), + ((((usqInt)oop))) >> (numTagBits())); + return; + } + if ((((oop) & 7) == 1)) { + fprintf(GIV(transcript), + "%" PRIdSQINT "(0x%" PRIxSQINT ")", + (oop >> 3), + (oop >> 3)); + return; + } + if (((oop & (smallFloatTag())) != 0)) { + fprintf(GIV(transcript), + "%g(0x%" PRIxSQINT ")", + noFailFloatValueOf(oop), + oop); + return; + } + fprintf(GIV(transcript), + "unknown immediate %p", + ((void *)oop)); + return; + } + if (!(addressCouldBeObj(oop))) { + print((oop & 7 /* (allocationUnit - 1) */ + ? " is misaligned" + : whereIs(oop))); + return; + } + if (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + print(" is a free chunk"); + return; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + target = followForwarded(oop); + fprintf(GIV(transcript), + " is a forwarder to %p", + ((void *)target)); + return; + } + if (/* isFloatInstance: */ + ((tagBits = oop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(oop))) & (classIndexMask())) == ClassFloatCompactIndex)) { + /* begin noFailFloatValueOf: */ + assert(isFloatInstance(oop)); + if (oop & (tagMask())) { + /* begin smallFloatValueOf: */ + /* begin smallFloatBitsOf: */ + assert(isImmediateFloat(oop)); + bits = ((((usqInt)oop))) >> (numTagBits()); + if (bits > 1) { + bits += (((usqInt)((smallFloatExponentOffset())) << ((smallFloatMantissaBits()) + 1))); + } + + /* a.k.a. ~= +/-0.0 */ + bits = ((bits << 0x3F)) + (((((usqInt)bits))) >> 1); + memcpy((&result), (&bits), sizeof(result)); + } + else { + fetchFloatAtinto(oop + BaseHeaderSize, result); + } + f = result; + + /* begin printFloat: */ + fprintf(GIV(transcript), + "%g", + f); + return; + } + classOop = fetchClassOfNonImm(oop); + if (!(addressCouldBeObj(classOop))) { + print("a ??"); + return; + } + if ((numSlotsOf(classOop)) == GIV(metaclassNumSlots)) { + printNameOfClasscount(oop, 5); + return; + } + if (oop == GIV(nilObj)) { + print("nil"); + return; + } + if (oop == GIV(trueObj)) { + print("true"); + return; + } + if (oop == GIV(falseObj)) { + print("false"); + return; + } + nameLen = lengthOfNameOfClass(classOop); + if (!nameLen) { + print("a ??"); + return; + } + name = nameOfClass(classOop); + if ((/* isBytes: */ + ((!(oop & (tagMask())))) + && (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat()))) + && (nameLen >= 6)) { + if (!(strncmp((name + nameLen) - 6, "String", 6))) { + printChar('\''); + printStringOf(oop); + printChar('\''); + return; + } + + /* strncmp is weird */ + if (!(strncmp((name + nameLen) - 6, "Symbol", 6))) { + printChar('#'); + printStringOf(oop); + return; + } + } + if ((nameLen == 9) + && ((strncmp(name, "Character", 9)) == 0)) { + printChar('$'); + printChar(((longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord()))))) >> 3)); + return; + } + fprintf(GIV(transcript), + "a(n) %.*s", + ((int) nameLen), + name); + + /* Try to spot association-like things; they're all subclasses of LookupKey */ + if ((((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */) + && (((instanceSizeOf(classOop)) == (ValueIndex + 1)) + && (isBytes(longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(KeyIndex) << (shiftForWord())))))))))) { + classLookupKey = fetchClassOfNonImm(splObj(SchedulerAssociation)); + while (1) { + if (classLookupKey == GIV(nilObj)) { + return; + } + if ((instanceSizeOf(classLookupKey)) == (KeyIndex + 1)) break; + /* begin superclassOf: */ + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classLookupKey + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classLookupKey, objOop); + } + classLookupKey = objOop; + } + if (includesBehaviorThatOf(classOop, classLookupKey)) { + /* begin space */ + printChar(' '); + printOopShortInner(longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(KeyIndex) << (shiftForWord()))))))); + print(" -> "); + fprintf(GIV(transcript), + "%p", + ((void *)(longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord()))))))))); + } + } +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printOop: */ +void +printOop(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt byte; + sqInt bytecodesPerLine; + char *className; + sqInt cls; + sqInt column; + sqInt fmt; + sqInt fmtSqInt; + sqInt index; + sqInt lastIndex; + sqInt length; + usqInt numSlots; + usqInt numSlotsUsqInt; + usqInt startIP; + sqInt tagBits; + + length = 0; + if (((oop & (tagMask())) != 0)) { + printImmediateObjecton(oop, GIV(transcript)); + return; + } + if (!(addressCouldBeObj(oop))) { + printCantBeObjecton(oop, GIV(transcript)); + return; + } + if (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + printFreeObjecton(oop, GIV(transcript)); + return; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + printForwarderon(oop, GIV(transcript)); + return; + } + if (!((cls = fetchClassOfNonImm(oop)))) { + fprintf(GIV(transcript), + "%p has a nil class!!\n", + ((void *)oop)); + return; + } + className = nameOfClasslengthInto(cls, (&length)); + fprintf(GIV(transcript), + "%p: a(n) %.*s", + ((void *)oop), + ((int) length), + className); + if (/* isFloatInstance: */ + ((tagBits = oop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(oop))) & (classIndexMask())) == ClassFloatCompactIndex)) { + fprintf(GIV(transcript), + "\n%g\n", + noFailFloatValueOf(oop)); + return; + } + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt > 5 /* lastPointerFormat */) { + fprintf(GIV(transcript), + " nbytes %" PRIdSQINT "", + numBytesOf(oop)); + } + cr(); + if (/* isPureBitsFormat: */ + (fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat()))) { + /* This will answer false if splObj: ClassAlien is nilObject */ + if (isKindOfClass(oop, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassAlien) << (shiftForWord())))))))) { + fprintf(GIV(transcript), + " datasize %" PRIdSQINT " %s @ %p\n", + longAt((void *)(oop + BaseHeaderSize)), + ((longAt((void *)(oop + BaseHeaderSize))) < 0 + ? "indirect" + : (longAt((void *)(oop + BaseHeaderSize)) + ? "direct" + : "pointer")), + startOfAlienData(oop)); + return; + } + if (isKindOfClass(oop, superclassOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteString) << (shiftForWord()))))))))) { + printStringDataOfon(oop, GIV(transcript)); + return; + } + printNonPointerDataOfon(oop, GIV(transcript)); + return; + } + startIP = (fmt >= (firstCompiledMethodFormat()) + ? (((literalCountOf(oop)) + LiteralStart) * BytesPerOop) / BytesPerWord + : numSlotsOf(oop)); + lastIndex = ((0x100 < startIP) ? 0x100 : startIP); + if (lastIndex > 0) { + for (index = 1; index <= lastIndex; index += 1) { + printHex(longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + + /* begin space */ + printChar(' '); + if (!(index % 5 /* elementsPerPrintOopLine */)) { + cr(); + } + } + if (lastIndex % 5 /* elementsPerPrintOopLine */) { + cr(); + } + } + if (((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + startIP = (((literalCountOf(oop)) + LiteralStart) * BytesPerOop) + 1; + + /* begin lengthOf: */ + fmtSqInt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmtSqInt <= (ephemeronFormat())) { + lastIndex = numSlots; + goto l1; + } + if (fmtSqInt >= (firstByteFormat())) { + lastIndex = ((numSlots << (shiftForWord()))) - (fmtSqInt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmtSqInt >= (firstShortFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 1))) - (fmtSqInt & 3); + goto l1; + } + if (fmtSqInt >= (firstLongFormat())) { + lastIndex = ((numSlots << ((shiftForWord()) - 2))) - (fmtSqInt & 1); + goto l1; + } + if (fmtSqInt == (sixtyFourBitIndexableFormat())) { + lastIndex = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + lastIndex = 0; + /* end lengthOf: */ +l1: + if ((lastIndex - startIP) > 0x100) { + lastIndex = startIP + 0x100; + } + bytecodesPerLine = 8; + column = 1; + for (index = startIP; index <= lastIndex; index += 1) { + if (column == 1) { + fprintf(GIV(transcript), + "%10p", + ((void *)(((oop + BaseHeaderSize) + index) - 1))); + } + byte = byteAt((void *)((oop + BaseHeaderSize) + (index - 1))); + fprintf(GIV(transcript), + " %02x/%-+3d", + ((int) byte), + ((int) byte)); + column += 1; + if (column > bytecodesPerLine) { + column = 1; + cr(); + } + } + if ((lengthOf(oop)) > lastIndex) { + print("..."); + } + if (!(column == 1)) { + cr(); + } + } + else { + if (startIP > 64) { + print("..."); + cr(); + } + } +} + + +/* print the entry and answer if it takes a parameter (as the following + entry) + */ + + /* StackInterpreter>>#printPrimLogEntryAt:hasParameter: */ +static NoDbgRegParms sqInt +printPrimLogEntryAthasParameter(sqInt i, sqInt hasParameter) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *className; + sqInt entryOop; + sqInt length; + sqInt methodClass; + sqInt methodSel; + sqInt value; + + length = 0; + entryOop = GIV(primTraceLog)[i]; + if (hasParameter) { + if (addressCouldBeObj(entryOop)) { + className = nameOfClasslengthInto(entryOop, (&length)); + } + else { + className = "bad class"; + length = 9; + } + fprintf(GIV(transcript), + "%.*s\n", + ((int) length), + className); + return 0; + } + if (((entryOop & (tagMask())) != 0)) { + value = (entryOop >> 3); + if (value == TraceIncrementalGC) { + fprintf(GIV(transcript), "**IncrementalGC**\n"); + } + else { + if (value == TraceFullGC) { + fprintf(GIV(transcript), "**FullGC**\n"); + } + else { + if (value == TraceCodeCompaction) { + fprintf(GIV(transcript), "**CompactCode**\n"); + } + else { + if (value == TraceStackOverflow) { + fprintf(GIV(transcript), "**StackOverflow**\n"); + } + else { + if (value == TracePrimitiveFailure) { + fprintf(GIV(transcript), "**PrimitiveFailure** "); + return 1; + } + else { + if (value == TracePrimitiveRetry) { + fprintf(GIV(transcript), "**PrimitiveRetry**\n"); + } + else { + fprintf(GIV(transcript), "???\n"); + } + } + } + } + } + } + return 0; + } + if (addressCouldBeObj(entryOop)) { + if (((byteAt((void *)(entryOop + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat())) { + className = "???"; + length = 3; + methodClass = safeMethodClassOf(entryOop); + if (methodClass != GIV(nilObj)) { + className = nameOfClasslengthInto(methodClass, (&length)); + } + methodSel = findSelectorOfMethod(entryOop); + if (methodSel == GIV(nilObj)) { + fprintf(GIV(transcript), + "%.*s>>(selector not found)\n", + ((int) length), + className); + } + else { + fprintf(GIV(transcript), + "%.*s>>#%.*s\n", + ((int) length), + className, + ((int) (numBytesOfBytes(methodSel))), + ((char *) (firstIndexableField(methodSel)))); + } + } + else { + safePrintStringOf(entryOop); + cr(); + } + } + else { + fprintf(GIV(transcript), + "%" PRIdSQINT "!!!\n", + i); + } + return 0; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printProcessStack: */ +void +printProcessStack(sqInt aProcess) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctx; + sqInt tagBits; + + cr(); + printNameOfClasscount(/* fetchClassOf: */ + ((tagBits = aProcess & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(aProcess)), 5); + + /* begin space */ + printChar(' '); + printHex(aProcess); + print(" priority "); + printNum(quickFetchIntegerofObject(PriorityIndex, aProcess)); + cr(); + ctx = followFieldofObject(SuspendedContextIndex, aProcess); + if (!(ctx == GIV(nilObj))) { + printCallStackOfcurrentFP(ctx, GIV(framePointer)); + } +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printProcsOnList: */ +sqInt +printProcsOnList(sqInt procList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firstProc; + sqInt proc; + + proc = (firstProc = followFieldofObject(FirstLinkIndex, procList)); + while (!(proc == GIV(nilObj))) { + printProcessStack(proc); + proc = followFieldofObject(NextLinkIndex, proc); + if (proc == firstProc) { + warning("circular process list!!"); + return null; + } + } + return 0; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackCallStackOf: */ +sqInt +printStackCallStackOf(sqInt aContextOrProcessOrFrame) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt context; + sqInt senderOop; + char *theFP; + + if (addressCouldBeObj(aContextOrProcessOrFrame)) { + if ((((longAt((void *)(aContextOrProcessOrFrame))) & (classIndexMask())) == ClassMethodContextCompactIndex) + && (checkIsStillMarriedContextcurrentFP(aContextOrProcessOrFrame, null))) { + return printStackCallStackOf(((sqInt)(frameOfMarriedContext(aContextOrProcessOrFrame)))); + } + if (aContextOrProcessOrFrame == (activeProcess())) { + return printStackCallStackOf(((sqInt)GIV(framePointer))); + } + if (couldBeProcess(aContextOrProcessOrFrame)) { + return printCallStackOf(longAt((void *)((aContextOrProcessOrFrame + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))))); + } + return null; + } + theFP = ((void *)aContextOrProcessOrFrame); + while (1) { + context = shortReversePrintFrameAndCallers(theFP); + if (!((((((longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && ((/* begin frameOfMarriedContext: */ + (senderOop = longAt((void *)((context + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))), + assert((((senderOop) & 7) == 1)), + (theFP = ((char *)(senderOop - (smallIntegerTag())))), + checkIsStillMarriedContextcurrentFP(context, theFP))))) { + return null; + } + } + return 0; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackPageList */ +void +printStackPageList(void) +{ + StackPage *page; + + page = GIV(mostRecentlyUsedPage); + do { + /* begin printStackPage: */ + printStackPageuseCount(page, -1); + cr(); + } while(((page = (page->prevPage))) != (GIV(mostRecentlyUsedPage))); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackPageListInUse */ +void +printStackPageListInUse(void) +{ + sqInt n; + StackPage *page; + + page = GIV(mostRecentlyUsedPage); + n = 0; + do { + if (!(isFree(page))) { + printStackPageuseCount(page, (n += 1)); + cr(); + } + } while(((page = (page->prevPage))) != (GIV(mostRecentlyUsedPage))); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackPages */ +void +printStackPages(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + StackPage *page; + + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + page = stackPageAtpages(i, GIV(pages)); + printStackPageuseCount(page, -1); + cr(); + } +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackPagesInUse */ +void +printStackPagesInUse(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt n; + + n = 0; + for (i = 0; i < GIV(numStackPages); i += 1) { + if (!(isFree(stackPageAt(i)))) { + printStackPageuseCount(stackPageAt(i), (n += 1)); + cr(); + } + } +} + + /* StackInterpreter>>#printStackPage:useCount: */ +static NoDbgRegParms void +printStackPageuseCount(StackPage *page, sqInt n) +{ + print("page "); + printHexPtrnp(page); + print(" ("); + printNum(pageIndexFor((page->realStackLimit))); + if (n >= 0) { + print(","); + printNum(n); + } + print(") (trace: "); + printNum((page->trace)); + printChar(')'); + if (isFree(page)) { + print(" (free)"); + } + if (page == (GIV(mostRecentlyUsedPage))) { + print(" (MRU)"); + } + if (((page->prevPage)) == (GIV(mostRecentlyUsedPage))) { + print(" (LRU)"); + } + cr(); + + /* begin tab */ + printChar('\t'); + print("ba: "); + printHexPtrnp((page->baseAddress)); + print(" - sl: "); + printHexPtrnp((page->realStackLimit)); + print(" - sl-so: "); + printHexPtrnp(((page->realStackLimit)) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)); + print(" - la:"); + printHexPtrnp((page->lastAddress)); + if (!(isFree(page))) { + cr(); + + /* begin tab */ + printChar('\t'); + print("baseFP "); + printHexPtrnp((page->baseFP)); + + /* cr; */ + + /* begin tab */ + printChar('\t'); + print("headFP "); + printHexPtrnp((page->headFP)); + + /* cr; */ + + /* begin tab */ + printChar('\t'); + print("headSP "); + printHexPtrnp((page->headSP)); + } + cr(); + + /* begin tab */ + printChar('\t'); + print("prev "); + printHexPtrnp(page->prevPage); + print(" ("); + printNum(pageIndexFor((((page->prevPage))->realStackLimit))); + printChar(')'); + + /* begin tab */ + printChar('\t'); + print("next "); + printHexPtrnp(page->nextPage); + print(" ("); + printNum(pageIndexFor((((page->nextPage))->realStackLimit))); + printChar(')'); + cr(); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#printStackReferencesTo: */ +void +printStackReferencesTo(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt i; + char *theFP; + StackPage *thePage; + char *theSP; + + callerFP = ((char *) 0); + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + thePage = stackPageAtpages(i, GIV(pages)); + if ((thePage->baseFP)) { + theSP = (thePage->headSP); + theFP = (thePage->headFP); + + /* Skip the instruction pointer on top of stack of inactive pages. */ + if (!(thePage == GIV(stackPage))) { + theSP += BytesPerWord; + } + while (1) { + while (theSP <= (/* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver))) { + if (oop == (longAt(theSP))) { + fprintf(GIV(transcript), + "FP:%p @ %p\n", + theFP, + theSP); + } + theSP += BytesPerWord; + } + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) + && (oop == (longAt(theFP + FoxThisContext)))) { + fprintf(GIV(transcript), + "FP:%p CTXT\n", + theFP); + } + if (oop == (longAt(theFP + FoxMethod))) { + fprintf(GIV(transcript), + "FP:%p MTHD\n", + theFP); + } + if (!(((callerFP = ((char *)(longAt(theFP + FoxSavedFP))))) != 0)) break; + theSP = (theFP + FoxCallerSavedIP) + BytesPerWord; + theFP = callerFP; + } + + /* a.k.a. FoxCallerContext */ + theSP = theFP + FoxCallerSavedIP; + while (theSP <= ((thePage->baseAddress))) { + if (oop == (longAt(theSP))) { + fprintf(GIV(transcript), + "FP:%p @ %p\n", + theFP, + theSP); + } + theSP += BytesPerWord; + } + } + } +} + + /* StackInterpreter>>#printStringOf: */ +static NoDbgRegParms void +printStringOf(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt cnt; + sqInt code; + sqInt fmt; + sqInt i; + sqInt len; + sqInt max; + + if (((oop & (tagMask())) != 0)) { + return; + } + if (!(addressCouldBeObj(oop))) { + return; + } + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + if (fmt < (firstByteFormat())) { + return; + } + cnt = ((((max = 128)) < ((len = lengthOf(oop)))) ? ((max = 128)) : ((len = lengthOf(oop)))); + i = 0; + if ((isinstanceOfcompactClassIndex(oop, longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassByteArray) << (shiftForWord())))))), GIV(classByteArrayCompactIndex))) + || (/* isLargeIntegerInstance: */ + ((!(oop & (tagMask())))) + && ((((usqInt)(((longAt((void *)(oop))) & (classIndexMask())) - ClassLargeNegativeIntegerCompactIndex))) <= 1))) { + while (i < cnt) { + printHex(byteAt((void *)((oop + BaseHeaderSize) + i))); + i += 1; + } + } + else { + while (i < cnt) { + code = byteAt((void *)((oop + BaseHeaderSize) + i)); + switch (code) { + case 10: + print(""); + break; + case 13: + print(""); + break; + default: + printChar(code); + } + i += 1; + } + } + if (len > max) { + print("..."); + } + + /* begin flush */ + fflush(GIV(transcript)); +} + + /* StackInterpreter>>#print: */ +void +print(char *s) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + fprintf(GIV(transcript), + "%s", + s); +} + + +/* Answer the list aProcess belongs to when runnable but not the active + process + */ +/* Useful for debugging!! */ + + /* StackInterpreter>>#processListForProcess: */ +sqInt +processListForProcess(sqInt aProcess) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objOop; + sqInt oop; + sqInt priority; + sqInt processLists; + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + priority = (oop >> 3); + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + processLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + return longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((priority - 1)) << (shiftForWord())))))); +} + + +/* Answer the zero-relative offset of Process' 'osErr inst var, or -1 if + absent. osErr will be set with the + platform's error after any FFI call (e.g. errno on unix or GetLastError, + WSAGetLastError et al on Win32) + which allows collecting the error without the potential for preemption + between call and error fetch, + which could result in fetching an invalid error code. */ + + /* StackInterpreter>>#processOSErrInstVarOffset */ +sqInt +processOSErrInstVarOffset(void) +{ + return (((imageHeaderFlags & 32) != 0) + ? (((imageHeaderFlags & 4) != 0) + ? 5 + : 4) + : -1); +} + + /* StackInterpreter>>#pushBool: */ +void +pushBool(sqInt trueOrFalse) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + + object = /* booleanObjectOf: */ + (trueOrFalse + ? GIV(trueObj) + : GIV(falseObj)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); +} + + +/* The receiver of a message send or the closure of a block activation is + always on the stack above any arguments and the frame itself. See the + diagram in StackInterpreter class>>initializeFrameIndices. */ + + /* StackInterpreter>>#pushedReceiverOrClosureOfFrame: */ +static NoDbgRegParms sqInt +pushedReceiverOrClosureOfFrame(char *theFP) +{ + /* begin frameStackedReceiver:numArgs: */ + return longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))); +} + + /* StackInterpreter>>#pushFloat: */ +void +pushFloat(double f) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt object; + + object = floatObjectOf(f); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); +} + + /* StackInterpreter>>#pushInteger: */ +sqInt +pushInteger(sqInt integerValue) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + /* begin push: */ + longAtput((sp = GIV(stackPointer) - BytesPerWord),(((usqInt)integerValue << 3) | 1)); + GIV(stackPointer) = sp; + return null; +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#push: */ +void +push(sqInt object) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *sp; + + longAtput((sp = GIV(stackPointer) - BytesPerWord),object); + GIV(stackPointer) = sp; +} + + +/* Append aLong to aFile in this platform's 'natural' byte order. aLong is + either 32 or 64 bits, + depending on ObjectMemory. (Bytes will be swapped, if necessary, when the + image is read + on a different platform.) Set successFlag to false if the write fails. */ + + /* StackInterpreter>>#putLong:toFile: */ +static NoDbgRegParms void +putLongtoFile(sqInt aLong, sqImageFile aFile) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objectsWritten; + + objectsWritten = sqImageFileWrite((&aLong), sizeof(sqInt), 1, aFile); + + /* begin success: */ + if (!(objectsWritten == 1)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Append the 16-bit aShort to aFile in this platform's 'natural' byte order. + (Bytes will be swapped, if necessary, when the image is read on a + different platform.) Set successFlag to false if the write fails. */ + + /* StackInterpreter>>#putShort:toFile: */ +static NoDbgRegParms void +putShorttoFile(short aShort, sqImageFile aFile) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objectsWritten; + + objectsWritten = sqImageFileWrite((&aShort), sizeof(short), 1, aFile); + + /* begin success: */ + if (!(objectsWritten == 1)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Save the given process on the scheduler process list for its priority, + adding to the back if yieldImplicitly or to the front if not + yieldImplicitly. + */ + + /* StackInterpreter>>#putToSleep:yieldingIf: */ +static NoDbgRegParms void +putToSleepyieldingIf(sqInt aProcess, sqInt yieldImplicitly) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firstLink; + sqInt objOop; + sqInt oop; + sqInt priority; + sqInt processList; + sqInt processLists; + + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + priority = (oop >> 3); + if ((GIV(highestRunnableProcessPriority) != 0) + && (priority > GIV(highestRunnableProcessPriority))) { + GIV(highestRunnableProcessPriority) = priority; + } + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + processLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + processList = longAt((void *)((processLists + BaseHeaderSize) + ((((usqInt)((priority - 1)) << (shiftForWord())))))); + if (yieldImplicitly) { + addLastLinktoList(aProcess, processList); + } + else { + /* begin addFirstLink:toList: */ + assert(!(isForwarded(processList))); + assert((fetchPointerofObject(NextLinkIndex, aProcess)) == (nilObject())); + firstLink = longAt((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + assert(firstLink != aProcess); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(FirstLinkIndex, processList, aProcess)); + assert(isNonImmediate(processList)); + if (oopisGreaterThanOrEqualTo(processList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(aProcess & (tagMask())))) + && (oopisLessThan(aProcess, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(processList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(processList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))),aProcess); + if (firstLink == GIV(nilObj)) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(LastLinkIndex, processList, aProcess)); + assert(isNonImmediate(processList)); + if (oopisGreaterThanOrEqualTo(processList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(aProcess & (tagMask())))) + && (oopisLessThan(aProcess, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(processList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(processList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((processList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord()))))),aProcess); + } + else { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(NextLinkIndex, aProcess, firstLink)); + assert(isNonImmediate(aProcess)); + if (oopisGreaterThanOrEqualTo(aProcess, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(firstLink & (tagMask())))) + && (oopisLessThan(firstLink, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aProcess + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aProcess); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord()))))),firstLink); + } + + /* a.k.a. (self isEmptyList: aList) */ + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(MyListIndex, aProcess, processList)); + assert(isNonImmediate(aProcess)); + if (oopisGreaterThanOrEqualTo(aProcess, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(processList & (tagMask())))) + && (oopisLessThan(processList, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aProcess + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aProcess); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),processList); + } +} + + +/* Append aWord32 to aFile in this platform's 'natural' byte order. aWord32 + is 32 bits, + depending on ObjectMemory. (Bytes will be swapped, if necessary, when the + image is read + on a different platform.) Set successFlag to false if the write fails. */ + + /* StackInterpreter>>#putWord32:toFile: */ +static NoDbgRegParms void +putWord32toFile(int aWord32, sqImageFile aFile) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt objectsWritten; + + objectsWritten = sqImageFileWrite((&aWord32), 4, 1, aFile); + + /* begin success: */ + if (!(objectsWritten == 1)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } +} + + +/* Return the integer value of the field without verifying that it is an + integer value! For use in time-critical places where the integer-ness of + the field can be guaranteed. + */ + + /* StackInterpreter>>#quickFetchInteger:ofObject: */ +static NoDbgRegParms sqInt +quickFetchIntegerofObject(sqInt fieldIndex, sqInt objectPointer) +{ + sqInt oop; + + oop = longAt((void *)((objectPointer + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + return (oop >> 3); +} + + +/* Anwer if images of the given format are readable by this interpreter. + Allows a virtual machine to accept selected older image formats. */ + + /* StackInterpreter>>#readableFormat: */ +sqInt +readableFormat(sqInt imageVersion) +{ + return (((imageVersion | MultipleBytecodeSetsBitmask) - MultipleBytecodeSetsBitmask)) == 68021 /* imageFormatVersion */; +} + + +/* Assuming the primFailCode is non-zero, check if the method consumes the + error code + and if so, assign it through theSP. Then zero the primFailCode. This is + infrequent code, + so keep it out of the common path. */ + + /* StackInterpreter>>#reapAndResetErrorCodeTo:header: */ +static NoDbgRegParms NeverInline void +reapAndResetErrorCodeToheader(char *theSP, sqInt methodHeader) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt initialPC; + + assert(GIV(primFailCode) != 0); + initialPC = (((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize) + 3 /* sizeOfCallPrimitiveBytecode: */; + if ((byteAt((void *)(initialPC))) == (/* longStoreBytecodeForHeader: */ + ((((sqLong) methodHeader)) < 0 + ? AltLongStoreBytecode + : LongStoreBytecode))) { + longAtput(theSP,getErrorObjectFromPrimFailCode()); + } + GIV(primFailCode) = 0; +} + + +/* callbackContext is an activation of + invokeCallback:[stack:registers:jmpbuf:]. Its sender is the VM's state + prior to the callback. Reestablish that state, + and mark calloutContext as dead. */ + + /* StackInterpreter>>#reestablishContextPriorToCallback: */ +sqInt +reestablishContextPriorToCallback(sqInt callbackContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt calloutContext; + sqInt senderOop; + char *theFP; + StackPage *thePage; + sqInt top; + + if (!(isLiveContext(callbackContext))) { + return 0; + } + calloutContext = externalInstVarofContext(SenderIndex, callbackContext); + if (!(isLiveContext(calloutContext))) { + return 0; + } + + /* We're about to leave this stack page; must save the current frame's instructionPointer. */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* Mark callbackContext as dead; the common case is that it is the current frame. + We go the extra mile for the debugger. */ + if ((!((longAt((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))) & (tagMask())))) { + /* begin markContextAsDead: */ + assert(isContext(callbackContext)); + assert((isNonImmediate(callbackContext)) + && (!(isForwarded(callbackContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, callbackContext, GIV(nilObj))); + longAtput((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(callbackContext)) + && (!(isForwarded(callbackContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, callbackContext, GIV(nilObj))); + longAtput((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (GIV(framePointer) == theFP) { + if (longAt(theFP + FoxSavedFP)) { + GIV(instructionPointer) = longAt(GIV(framePointer) + FoxCallerSavedIP); + GIV(stackPointer) = (GIV(framePointer) + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(GIV(framePointer)))->cmNumArgs) + : byteAt((GIV(framePointer) + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + GIV(framePointer) = ((char *)(longAt(GIV(framePointer) + FoxSavedFP))); + return 1; + } + else { + freeStackPage(GIV(stackPage)); + } + } + else { + externalDivorceFrameandContext(theFP, callbackContext); + + /* begin markContextAsDead: */ + assert(isContext(callbackContext)); + assert((isNonImmediate(callbackContext)) + && (!(isForwarded(callbackContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, callbackContext, GIV(nilObj))); + longAtput((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(callbackContext)) + && (!(isForwarded(callbackContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, callbackContext, GIV(nilObj))); + longAtput((void *)((callbackContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + } + } + + /* Make the calloutContext the active frame. The case where calloutContext + is immediately below callbackContext on the same page is handled above. */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((calloutContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(calloutContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((calloutContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + + /* findSPOf:on: points to the word beneath the instructionPointer, but + there is no instructionPointer on the top frame of the current page. */ + assert(thePage != GIV(stackPage)); + GIV(stackPointer) = (findSPOfon(theFP, thePage)) - BytesPerWord; + GIV(framePointer) = theFP; + assert(GIV(stackPointer) < GIV(framePointer)); + } + else { + thePage = makeBaseFrameFor(calloutContext); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + return 1; +} + + +/* Remove the first process from the given linked list. */ + + /* StackInterpreter>>#removeFirstLinkOfList: */ +static NoDbgRegParms sqInt +removeFirstLinkOfList(sqInt aList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt first; + sqInt last; + sqInt next; + + assert(!((isForwarded(aList)))); + first = longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + last = longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord())))))); + assert(!((isForwarded(first)))); + assert(!((isForwarded(last)))); + if (first == last) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aList)) + && (!(isForwarded(aList)))); + assert(validStorePointerUncheckedArgs(FirstLinkIndex, aList, GIV(nilObj))); + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aList)) + && (!(isForwarded(aList)))); + assert(validStorePointerUncheckedArgs(LastLinkIndex, aList, GIV(nilObj))); + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + next = longAt((void *)((first + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(FirstLinkIndex, aList, next)); + assert(isNonImmediate(aList)); + if (oopisGreaterThanOrEqualTo(aList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(next & (tagMask())))) + && (oopisLessThan(next, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))),next); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(first)) + && (!(isForwarded(first)))); + assert(validStorePointerUncheckedArgs(NextLinkIndex, first, GIV(nilObj))); + longAtput((void *)((first + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord()))))),GIV(nilObj)); + return first; +} + + +/* Attempt to remove a process from a linked list. Answer if the attempt + succeeded. + */ + + /* StackInterpreter>>#removeProcess:fromList: */ +static NoDbgRegParms sqInt +removeProcessfromList(sqInt aProcess, sqInt aList) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt firstLink; + sqInt lastLink; + sqInt nextLink; + usqInt numSlots; + sqInt tempLink; + + nextLink = 0; + assert(!((isForwarded(aProcess)))); + assert(!((isForwarded(aList)))); + + /* any process on the list could have been becomed, so use a read barrier... */ + + /* begin followObjField:ofObject: */ + firstLink = longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(firstLink)); + if ((!((longAt((void *)(firstLink))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + firstLink = fixFollowedFieldofObjectwithInitialValue(FirstLinkIndex, aList, firstLink); + } + + /* begin followObjField:ofObject: */ + lastLink = longAt((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(lastLink)); + if ((!((longAt((void *)(lastLink))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + lastLink = fixFollowedFieldofObjectwithInitialValue(LastLinkIndex, aList, lastLink); + } + if (aProcess == firstLink) { + /* begin followObjField:ofObject: */ + nextLink = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(nextLink)); + if ((!((longAt((void *)(nextLink))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + nextLink = fixFollowedFieldofObjectwithInitialValue(NextLinkIndex, aProcess, nextLink); + } + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(FirstLinkIndex, aList, nextLink)); + assert(isNonImmediate(aList)); + if (oopisGreaterThanOrEqualTo(aList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(nextLink & (tagMask())))) + && (oopisLessThan(nextLink, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))),nextLink); + if (aProcess == lastLink) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aList)) + && (!(isForwarded(aList)))); + assert(validStorePointerUncheckedArgs(LastLinkIndex, aList, GIV(nilObj))); + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord()))))),GIV(nilObj)); + } + } + else { + tempLink = firstLink; + + /* fail if any link doesn't look like a process... */ + while (1) { + if (!((/* isPointers: */ + ((!(tempLink & (tagMask())))) + && (((byteAt((void *)(tempLink + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(tempLink)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(tempLink + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(tempLink - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > MyListIndex))) { + return 0; + } + + /* begin followObjField:ofObject: */ + nextLink = longAt((void *)((tempLink + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(nextLink)); + if ((!((longAt((void *)(nextLink))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + nextLink = fixFollowedFieldofObjectwithInitialValue(NextLinkIndex, tempLink, nextLink); + } + if (nextLink == aProcess) break; + tempLink = nextLink; + } + nextLink = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(NextLinkIndex, tempLink, nextLink)); + assert(isNonImmediate(tempLink)); + if (oopisGreaterThanOrEqualTo(tempLink, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(nextLink & (tagMask())))) + && (oopisLessThan(nextLink, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(tempLink + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(tempLink); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((tempLink + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord()))))),nextLink); + if (aProcess == lastLink) { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(LastLinkIndex, aList, tempLink)); + assert(isNonImmediate(aList)); + if (oopisGreaterThanOrEqualTo(aList, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(tempLink & (tagMask())))) + && (oopisLessThan(tempLink, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aList + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aList); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aList + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord()))))),tempLink); + } + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aProcess)) + && (!(isForwarded(aProcess)))); + assert(validStorePointerUncheckedArgs(NextLinkIndex, aProcess, GIV(nilObj))); + longAtput((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(aProcess)) + && (!(isForwarded(aProcess)))); + assert(validStorePointerUncheckedArgs(MyListIndex, aProcess, GIV(nilObj))); + longAtput((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(MyListIndex) << (shiftForWord()))))),GIV(nilObj)); + return 1; +} + + +/* Make aProcess runnable and if its priority is higher than that of the + current process, preempt the current process. Answer if the current + process was preempted. If the current process was preempted then if + yieldImplicitly add the current process to the back of its run queue, + causing an implicit yiled to other processes on the run queue, otherwise + add the current process to the front of its run queue, hence not yielding. + Blue book behaviour is to yield implicitly but is arguably incorrect. */ + + /* StackInterpreter>>#resume:preemptedYieldingIf:from: */ +static NoDbgRegParms sqInt +resumepreemptedYieldingIffrom(sqInt aProcess, sqInt yieldImplicitly, sqInt sourceCode) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activePriority; + sqInt activeProc; + sqInt newPriority; + sqInt objOop; + sqInt oop; + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + activePriority = (oop >> 3); + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((aProcess + BaseHeaderSize) + ((((usqInt)(PriorityIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + newPriority = (oop >> 3); + if (newPriority <= activePriority) { + putToSleepyieldingIf(aProcess, 1); + return 0; + } + putToSleepyieldingIf(activeProc, yieldImplicitly); + transferTofrom(aProcess, sourceCode); + return 1; +} + + +/* In Spur two cases of primitive failure are handled specially. A primitive + may fail due to validation + encountering a forwarder. On failure, check the accessorDepth for the + primitive and if non-negative + scan the args to the depth, following any forwarders. Retry the primitive + if any are found. Hence + lazily and transparently following forwarders on primitive failure. + Additionally a primitive might fail + due to an allocation failing. Retry if external primitives have failed + with PrimErrNoMemory after running + first the scavenger and then on a subsequent failure, the global + mark-sweep collector. Hence lazily + and transparently GC on memory exhaustion. */ + + /* StackInterpreter>>#retryPrimitiveOnFailure */ +static sqInt +retryPrimitiveOnFailure(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt accessorDepth; + sqInt aMethodSelectorOrImmediate; + sqInt canRetry; + usqInt firstBytecode; + sqInt followDone; + sqInt gcDone; + sqInt header; + sqInt index; + sqInt methodHeader; + sqInt oop; + sqInt primIndex; + sqInt primitiveIndex; + sqInt referent; + sqInt retried; + sqInt retry; + sqInt scannedStackFrame; + sqInt tagBits; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primitiveIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primitiveIndex = 0; + } + assert(saneFunctionPointerForFailureOfPrimIndex(primitiveIndex)); + gcDone = 0; + followDone = (canRetry = (retried = 0)); + while (1) { + retry = 0; + if (GIV(primFailCode) == PrimErrNoMemory) { + if (((gcDone += 1)) == 1) { + canRetry = primitiveIndex == PrimNumberExternalCall; + } + if (canRetry) { + if (gcDone == 1) { + /* begin scavengingGC */ + scavengingGCTenuringIf(TenureByAge); + } + if (gcDone == 2) { + fullGC(); + } + retry = gcDone <= 2; + } + } + else { + if (!followDone) { + followDone = 1; + + /* begin checkForAndFollowForwardedPrimitiveState */ + if ((recordPrimTrace()) + && (!primTracePluginName)) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveFailure << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + aMethodSelectorOrImmediate = /* fetchClassOf: */ + ((tagBits = (longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord))) & (tagMask())) + ? longAt((void *)((GIV(classTableFirstPage) + BaseHeaderSize) + ((((usqInt)(tagBits) << (shiftForWord())))))) + : fetchClassOfNonImm(longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)))); + + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = aMethodSelectorOrImmediate; + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + assert(failed()); + retry = 0; + + /* begin primitiveIndexOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(newMethod))); + header = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + firstBytecode = (GIV(newMethod) + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize; + primIndex = (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8))); + } + else { + primIndex = 0; + } + assert((GIV(argumentCount) == (argumentCountOf(GIV(newMethod)))) + || (isMetaPrimitiveIndex(primIndex))); + + /* First things first; make sure the metadata has been followed before it is accessed to derive accessorDepth... */ + if (((/* isCalloutPrimitiveIndex: */ + (primIndex == PrimNumberExternalCall) + || (primIndex == PrimNumberFFICall)) + || (primIndex == PrimNumberDoExternalCall)) + && (unfollowFirstLiteralOfMaybeCalloutMethodprimitiveIndex(GIV(newMethod), primIndex))) { + retry = 1; + } + + /* If the primitive is one of the meta primitives PrimNumberDoPrimitive or PrimNumberDoExternalCall, then + metaAccessorDepth will have been set to nil at the start of the primitive, and to the accessor depth of the + called primitive (or external call) immediately before dispatch. Hence if primIndex is that of a meta primitive + then if metaAccessorDepth is -2, the accessor depth is that of the meta primitive, and if > -2, then + metaAccessorDepth is the accessor depth of the primitive (or external call). Similarly, if the primitive is + primitiveExternalCall then the accessor depth is that of primitiveExternalCall until primitiveFunctionPointer + is assigned, at which point the accessor depth is taken from the slot in newMethod's first literal. */ + accessorDepth = ((/* isMetaPrimitiveIndex: */ + (primIndex == PrimNumberDoPrimitive) + || (primIndex == PrimNumberDoExternalCall)) + && (GIV(metaAccessorDepth) > -2) + ? GIV(metaAccessorDepth) + : accessorDepthForPrimitiveMethod(GIV(newMethod))); + assert(((accessorDepth >= -1) && (accessorDepth <= 5))); + if (accessorDepth >= 0) { + scannedStackFrame = 0; + for (index = 0; index <= GIV(argumentCount); index += 1) { + oop = longAt((void *)(GIV(stackPointer) + (index * BytesPerWord))); + if ((!(oop & (tagMask())))) { + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + assert(index < GIV(argumentCount)); + retry = 1; + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(oop)); + referent = longAt((void *)((oop + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + oop = referent; + + /* stackValue:put: */ + longAtput((void *)(GIV(stackPointer) + (index * BytesPerWord)),oop); + if (!scannedStackFrame) { + scannedStackFrame = 1; + + /* Avoid repeated primitive failures by following all state in the current stack frame. */ + followForwardedFrameContentsstackPointer(GIV(framePointer), GIV(stackPointer) + ((GIV(argumentCount) + 1) * BytesPerWord)); + } + } + if ((accessorDepth > 0) + && ((/* hasPointerFields: */ + ((!(oop & (tagMask())))) + && (hasPointerFieldsNonImm(oop))) + && (followForwardedObjectFieldstoDepth(oop, accessorDepth)))) { + retry = 1; + } + } + } + } + if (retry + && (recordPrimTrace())) { + /* begin fastLogPrimCode: */ + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = ((((usqInt)TracePrimitiveRetry << 3) | 1)); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + } + } + if (!(retry)) break; + assert(GIV(primFailCode) != 0); + retried = 1; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + dispatchFunctionPointer(primitiveFunctionPointer); + } + return retried; +} + + +/* callbackMethodContext is an activation of + invokeCallback:[stack:registers:jmpbuf:]. Its sender is the VM's state + prior to the callback. Reestablish that state (via longjmp), + and mark callbackMethodContext as dead. */ + + /* StackInterpreter>>#returnAs:ThroughCallback:Context: */ +sqInt +returnAsThroughCallbackContext(sqInt returnTypeOop, VMCallbackContext *vmCallbackContext, sqInt callbackMethodContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aMethodObj; + sqInt calloutMethodContext; + sqInt cFramePointer; + sqInt cStackPointer; + sqInt header; + sqInt methodHeader; + sqInt senderOop; + sqInt sourceCode; + char *theFP; + StackPage *thePage; + sqInt thunkp; + sqInt top; + + assert(GIV(primFailCode) == 0); + assert((((returnTypeOop) & 7) == 1)); + assert(!(isImmediate(((sqInt)vmCallbackContext)))); + assert((addressCouldBeObj(callbackMethodContext)) + && (isContext(callbackMethodContext))); + assert(((debugCallbackPath = 0)) == 0); + if (!(((((returnTypeOop) & 7) == 1)) + && (isLiveContext(callbackMethodContext)))) { + assert(((debugCallbackPath = 1)) == 1); + return 0; + } + calloutMethodContext = externalInstVarofContext(SenderIndex, callbackMethodContext); + if (!(isLiveContext(calloutMethodContext))) { + assert(((debugCallbackPath = 2)) == 2); + return 0; + } + assert(((debugCallbackReturns += 1)) > 0); + thunkp = ((sqInt) ((vmCallbackContext->thunkp))); + + /* begin recordCallbackReturn:thunkp: */ + sourceCode = (returnTypeOop >> 3); + assert(sourceCode < ((1U << TraceTypeShift))); + GIV(traceLog)[GIV(traceLogIndex)] = (((((((usqInt)((atomic_load((&GIV(vmOwner))))) << TraceOwnerShift)))) + (((((usqInt)(TraceVMCallbackReturn) << TraceTypeShift))))) + sourceCode); + GIV(traceLog)[GIV(traceLogIndex) + 1] = (((sqInt)thunkp)); + GIV(traceLog)[GIV(traceLogIndex) + 2] = 0; + GIV(traceLogIndex) = (GIV(traceLogIndex) + 3) % TraceBufferSize; + + /* We're about to leave this stack page; must save the current frame's instructionPointer. */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* Mark callbackMethodContext as dead; the common case is that it is the current frame. + We go the extra mile for the debugger. */ + if ((!((longAt((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))))) & (tagMask())))) { + assert(((debugCallbackPath = debugCallbackPath | 4)) > 0); + + /* begin markContextAsDead: */ + assert(isContext(callbackMethodContext)); + assert((isNonImmediate(callbackMethodContext)) + && (!(isForwarded(callbackMethodContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, callbackMethodContext, GIV(nilObj))); + longAtput((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(callbackMethodContext)) + && (!(isForwarded(callbackMethodContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, callbackMethodContext, GIV(nilObj))); + longAtput((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + } + else { + assert(((debugCallbackPath = debugCallbackPath | 8)) > 0); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + assert((frameReceiver(theFP)) == (splObj(ClassAlien))); + if (GIV(framePointer) == theFP) { + assert(((debugCallbackPath = debugCallbackPath | 16)) > 0); + if (longAt(theFP + FoxSavedFP)) { + assert(((debugCallbackPath = debugCallbackPath | 32)) > 0); + GIV(instructionPointer) = longAt(theFP + FoxCallerSavedIP); + GIV(stackPointer) = (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + GIV(framePointer) = ((char *)(longAt(theFP + FoxSavedFP))); + aMethodObj = /* frameMethodObject: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(GIV(framePointer)))->methodObject) + : longAt(GIV(framePointer) + FoxMethod)); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* begin restoreCStackStateForCallbackContext: */ + cFramePointer = ((sqInt) ((vmCallbackContext->savedCFramePointer))); + cStackPointer = ((sqInt) ((vmCallbackContext->savedCStackPointer))); + + /* begin setCFramePointer:setCStackPointer: */ + assert(!(((!cFramePointer) + || (!cStackPointer)))); + GIV(CStackPointer) = cStackPointer; + GIV(CFramePointer) = cFramePointer; + assertSavedCStackPointersWellAligned(); + + /* begin assertValidExecutionPointe:r:s: */ + assertValidExecutionPointersimbarline(GIV(instructionPointer), GIV(framePointer), GIV(stackPointer), !((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory())), __LINE__); + + /* N.B. siglongjmp is defines as _longjmp on non-win32 platforms. + This matches the use of _setjmp in ia32abicc.c. */ + _longjmp((vmCallbackContext->trampoline), (returnTypeOop >> 3)); + return 1; + } + + /* calloutMethodContext is immediately below on the same page. Make it current. */ + freeStackPage(GIV(stackPage)); + } + else { + assert(((debugCallbackPath = debugCallbackPath | 64)) > 0); + externalDivorceFrameandContext(theFP, callbackMethodContext); + + /* begin markContextAsDead: */ + assert(isContext(callbackMethodContext)); + assert((isNonImmediate(callbackMethodContext)) + && (!(isForwarded(callbackMethodContext)))); + assert(validStorePointerUncheckedArgs(SenderIndex, callbackMethodContext, GIV(nilObj))); + longAtput((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord()))))),GIV(nilObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(callbackMethodContext)) + && (!(isForwarded(callbackMethodContext)))); + assert(validStorePointerUncheckedArgs(InstructionPointerIndex, callbackMethodContext, GIV(nilObj))); + longAtput((void *)((callbackMethodContext + BaseHeaderSize) + ((((usqInt)(InstructionPointerIndex) << (shiftForWord()))))),GIV(nilObj)); + } + } + + /* Make the calloutMethodContext the active frame. The case where calloutMethodContext + is immediately below callbackMethodContext on the same page is handled above. */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((calloutMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(calloutMethodContext)))) { + assert(((debugCallbackPath = debugCallbackPath | 128)) > 0); + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((calloutMethodContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + + /* findSPOf:on: points to the word beneath the instructionPointer, but + there is no instructionPointer on the top frame of the current page. */ + assert(thePage != GIV(stackPage)); + GIV(stackPointer) = (((thePage->headFP)) == theFP + ? (thePage->headSP) + : (findSPOfon(theFP, thePage)) - BytesPerWord); + GIV(framePointer) = theFP; + assert(GIV(stackPointer) < GIV(framePointer)); + } + else { + assert(((debugCallbackPath = debugCallbackPath | 0x100)) > 0); + thePage = makeBaseFrameFor(calloutMethodContext); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + } + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + aMethodObj = longAt((void *)((calloutMethodContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))); + + /* begin setMethod: */ + assert((((usqInt)aMethodObj)) >= (startOfMemory())); + GIV(method) = aMethodObj; + assert(isOopCompiledMethod(GIV(method))); + + /* begin methodUsesAlternateBytecodeSet: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(GIV(method))); + header = longAt((void *)((GIV(method) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if ((((sqLong) methodHeader)) < 0) { + GIV(bytecodeSetSelector) = 0x100; + } + else { + GIV(bytecodeSetSelector) = 0; + } + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin restoreCStackStateForCallbackContext: */ + cFramePointer = ((sqInt) ((vmCallbackContext->savedCFramePointer))); + cStackPointer = ((sqInt) ((vmCallbackContext->savedCStackPointer))); + + /* begin setCFramePointer:setCStackPointer: */ + assert(!(((!cFramePointer) + || (!cStackPointer)))); + GIV(CStackPointer) = cStackPointer; + GIV(CFramePointer) = cFramePointer; + assertSavedCStackPointersWellAligned(); + + /* N.B. siglongjmp is defined as _longjmp on non-win32 platforms. + This matches the use of _setjmp in ia32abicc.c. */ + _longjmp((vmCallbackContext->trampoline), (returnTypeOop >> 3)); + + /* NOTREACHED */ + return 1; +} + + +/* Reverse the given range of Display pixels, rounded to whole word boundary. + Used to give feedback during VM activities such as garbage collection when + debugging. It is assumed that the given word range falls entirely within + the first line of the Display. */ + + /* StackInterpreter>>#reverseDisplayFrom:to: */ +static NoDbgRegParms void +reverseDisplayFromto(sqInt startIndex, sqInt endIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt primFailCodeValue; + sqInt ptr; + unsigned int reversed; + sqInt toDoLimit; + sqInt wordEndIndex; + sqInt wordStartIndex; + + if (!(postGCUpdateDisplayBits())) { + return; + } + if ((displayBits == 0) + || (((((((sqInt)displayBits)) & (tagMask())) != 0)) + || (displayDepth <= 0))) { + return; + } + wordStartIndex = ((((startIndex < 0) ? 0 : startIndex)) * displayDepth) / 32; + wordEndIndex = ((((endIndex < displayWidth) ? endIndex : displayWidth)) * displayDepth) / 32; + toDoLimit = (((sqInt)displayBits)) + (wordEndIndex * 4); + for (ptr = ((((sqInt)displayBits)) + (wordStartIndex * 4)); ptr <= toDoLimit; ptr += 4) { + reversed = (long32At((void *)(ptr))) ^ 0xFFFFFFFFU; + long32Atput((void *)(ptr),reversed); + } + primFailCodeValue = GIV(primFailCode); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + updateDisplayLeftTopRightBottom(0, 0, displayWidth, 1); + ioForceDisplayUpdate(); + GIV(primFailCode) = primFailCodeValue; +} + + +/* Rewrite an existing entry in the method cache with a new primitive + function address. + Used by primitiveExternalCall to make direct calls to found external + prims, or quickly + fail not found external prims. */ + + /* StackInterpreter>>#rewriteMethodCacheEntryForExternalPrimitiveToFunction: */ +static NoDbgRegParms void +rewriteMethodCacheEntryForExternalPrimitiveToFunction(void (*localPrimAddress)()) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((GIV(methodCache)[GIV(lastMethodCacheProbeWrite) + MethodCacheMethod]) == GIV(newMethod)) { + GIV(methodCache)[GIV(lastMethodCacheProbeWrite) + MethodCachePrimFunction] = (((sqIntptr_t) localPrimAddress)); + } +} + + +/* Safe version of methodClassOf: that deals with malformed compiled methods, + etc, and does not fixup forwarding pointers.. */ + + /* StackInterpreter>>#safeMethodClassOf: */ +static NoDbgRegParms sqInt +safeMethodClassOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt literal; + sqInt maybeClass; + usqInt numSlots; + sqInt offset; + + offset = (literalCountOf(methodPointer)) - 1; + + /* begin literal:ofMethod: */ + literal = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(literal & (tagMask())))) + && ((!((longAt((void *)(literal))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + literal = followForwarded(literal); + } + if (!((/* isPointers: */ + ((!(literal & (tagMask())))) + && (((byteAt((void *)(literal + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) + && (((/* begin numSlotsOf: */ + assert((classIndexOf(literal)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(literal + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(literal - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) > ValueIndex))) { + return GIV(nilObj); + } + maybeClass = longAt((void *)((literal + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(maybeClass & (tagMask())))) + && ((!((longAt((void *)(maybeClass))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + maybeClass = followForwarded(maybeClass); + } + return maybeClass; +} + + +/* This is an assert function used to ensure consistency between the + primitiveFunctionPointer and the primitive index when a primitive fails in + Spur. Since Spur automagically retries + failing primitives we want to know that the primitiveFunctionPointer is + actually valid. This isn't + always possible for the `indirect'' primitives (calling a plugin + primitive, calling the ffi, using the + receiver:tryPrimitive:withArgs: primitive evaluator in the simulator. We + do the best we can. */ + + /* StackInterpreter>>#saneFunctionPointerForFailureOfPrimIndex: */ +static NoDbgRegParms sqInt +saneFunctionPointerForFailureOfPrimIndex(sqInt primIndex) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*basePrimitive)(void); + + /* begin functionPointerFor:inClass: */ + basePrimitive = ((((usqInt)primIndex)) > MaxPrimitiveIndex + ? ((void (*)(void)) 0) + : primitiveTable[primIndex]); + return (primitiveFunctionPointer == basePrimitive) + || (((/* isCalloutPrimitiveIndex: */ + (primIndex == PrimNumberExternalCall) + || (primIndex == PrimNumberFFICall)) + && (!((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex))) + || ((/* isMetaPrimitiveIndex: */ + (primIndex == PrimNumberDoPrimitive) + || (primIndex == PrimNumberDoExternalCall)) + && (GIV(metaAccessorDepth) > -2))); +} + + +/* Useful for debugging!! */ + + /* StackInterpreter>>#schedulerPointer */ +sqInt +schedulerPointer(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin fetchPointer:ofObject: */ + return longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#setBreakMNUSelector: */ +void +setBreakMNUSelector(char *aString) +{ + if ((breakSelector = aString)) { + breakSelectorLength = -(strlen(aString)); + } + else { + breakSelectorLength = MinSmallInteger; + } +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#setBreakSelector: */ +void +setBreakSelector(char *aString) +{ + if ((breakSelector = aString)) { + breakSelectorLength = strlen(aString); + } + else { + breakSelectorLength = MinSmallInteger; + } +} + + +/* If bit 0 of the imageHeaderFlags is set then the system should use the + full screen. + */ + + /* StackInterpreter>>#setFullScreenFlag: */ +void +setFullScreenFlag(sqInt aBoolean) +{ + imageHeaderFlags = (aBoolean + ? imageHeaderFlags | 1 + : ((imageHeaderFlags | 1) - 1)); +} + + /* StackInterpreter>>#setInterruptCheckChain: */ +void * +setInterruptCheckChain(void (*aFunction)()) +{ + void (*prevFunction)(); + + prevFunction = interruptCheckChain; + interruptCheckChain = aFunction; + return prevFunction; +} + + /* StackInterpreter>>#setInterruptKeycode: */ +void +setInterruptKeycode(sqInt value) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(interruptKeycode) = value; +} + + +/* This is invoked frrom the platform GUI event subsystem when the user + inputs a a char code matching interruptKeycode. Set the value and then + forceInterruptCheck so as to call the interruptCheckChain function if set. + It's important to set interruptPending *before* calling + forceInterruptCheck so that functions on the interruptCheckChain can check + interruptPending. */ + + /* StackInterpreter>>#setInterruptPending: */ +void +setInterruptPending(sqInt value) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(interruptPending) = value; + forceInterruptCheck(); +} + + /* StackInterpreter>>#setNextWakeupUsecs: */ +void +setNextWakeupUsecs(usqLong value) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(nextWakeupUsecs) = value; +} + + /* StackInterpreter>>#setSavedWindowSize: */ +void +setSavedWindowSize(sqInt value) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(savedWindowSize) = value; +} + + +/* The low space semaphore is about to be signaled. Set the signalLowSpace + flag, and force an interrupt check. Save the currently active process in + the special + objects array so that the low space handler will be able to determine the + process that first triggered a low space condition. The image's low space + handler is expected + to nil out the special objects array slot when it handles the low space + condition. */ + + /* StackInterpreter>>#setSignalLowSpaceFlagAndSaveProcess */ +static void +setSignalLowSpaceFlagAndSaveProcess(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeProc; + sqInt lastSavedProcess; + sqInt objOop; + + /* signalLowSpace: */ + GIV(signalLowSpace) = 1; + + /* begin lowSpaceThreshold: */ + GIV(lowSpaceThreshold) = 0; + + /* N.B. The threshold > 0 guard eliminates a warning when + self lowSpaceThreshold: 0 + is inlined into setSignalLowSpaceFlagAndSaveProcess */ + assert(GIV(totalFreeOldSpace) >= GIV(lowSpaceThreshold)); + lastSavedProcess = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ProcessSignalingLowSpace) << (shiftForWord())))))); + if (lastSavedProcess == GIV(nilObj)) { + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ProcessSignalingLowSpace, GIV(specialObjectsOop), activeProc)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeProc & (tagMask())))) + && (oopisLessThan(activeProc, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ProcessSignalingLowSpace) << (shiftForWord()))))),activeProc); + } + forceInterruptCheck(); +} + + /* StackInterpreter>>#setTraceFlagOnContextsFramesPageIfNeeded: */ +static NoDbgRegParms void +setTraceFlagOnContextsFramesPageIfNeeded(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void *pointer; + sqInt senderOop; + StackPage *thePage; + + if (/* isStillMarriedContextDuringGC: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContextDuringGC(aContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + pointer = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(pointer), GIV(pages)); + assert(((((thePage->trace)) >= StackPageUnreached) && (((thePage->trace)) <= StackPageTraced))); + if (!((thePage->trace))) { + (thePage->trace = StackPageReachedButUntraced); + } + } +} + + /* StackInterpreter>>#shortPrintContext: */ +static NoDbgRegParms sqInt +shortPrintContext(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt home; + sqInt rcvr; + sqInt senderOop; + char *theFP; + + if (!(/* isContext: */ + ((!(aContext & (tagMask())))) + && (((longAt((void *)(aContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + printHex(aContext); + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " is not a context"); + cr(); + return null; + } + printHex(aContext); + if (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + if (checkIsStillMarriedContextcurrentFP(aContext, GIV(framePointer))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory())) { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " M ("); + } + else { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " I ("); + } + printHex(((usqIntptr_t)theFP)); + + /* begin print: */ + fprintf(GIV(transcript), + "%s", + ") "); + } + else { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " w "); + } + } + else { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " s "); + } + if ((home = findHomeForContext(aContext))) { + rcvr = longAt((void *)((home + BaseHeaderSize) + ((((usqInt)(ReceiverIndex) << (shiftForWord())))))); + printActivationNameForreceiverisBlockfirstTemporary(longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(MethodIndex) << (shiftForWord())))))), rcvr, home != aContext, longAt((void *)((home + BaseHeaderSize) + ((((usqInt)((0 + CtxtTempFrameStart)) << (shiftForWord()))))))); + + /* begin space */ + printChar(' '); + shortPrintOop(rcvr); + } + else { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + " BOGUS CONTEXT (can't determine home)"); + cr(); + } + return 0; +} + + /* StackInterpreter>>#shortPrintFrameAndCallers: */ +static NoDbgRegParms sqInt +shortPrintFrameAndCallers(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (!(/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + return null; + } + shortPrintFrame(theFP); + if (longAt(theFP + FoxSavedFP)) { + shortPrintFrameAndCallers(((char *)(longAt(theFP + FoxSavedFP)))); + } + return 0; +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#shortPrintFramesInPage: */ +void +shortPrintFramesInPage(StackPage *thePage) +{ + printFrameAndCallersSPshort((thePage->headFP), (thePage->headSP), 1); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#shortPrintFramesOnStackPageListInUse */ +void +shortPrintFramesOnStackPageListInUse(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *page; + + page = GIV(mostRecentlyUsedPage); + do { + if (!(isFree(page))) { + /* begin print: */ + fprintf(GIV(transcript), + "%s", + "page "); + printHexPtrnp(page); + cr(); + shortPrintFramesInPage(page); + cr(); + } + } while(((page = (page->prevPage))) != (GIV(mostRecentlyUsedPage))); +} + + +/* useful for VM debugging */ + + /* StackInterpreter>>#shortPrintFrame:AndNCallers: */ +void +shortPrintFrameAndNCallers(char *theFP, sqInt n) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if ((n != 0) + && (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)theFP)) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)theFP)) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)theFP)) <= (((usqInt)GIV(pages))))))))) { + shortPrintFrame(theFP); + shortPrintFrameAndNCallers(((char *)(longAt(theFP + FoxSavedFP))), n - 1); + } +} + + /* StackInterpreter>>#shortPrintOop: */ +static NoDbgRegParms void +shortPrintOop(sqInt oop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *className; + sqInt length; + char *where; + + length = 0; + if (((oop & (tagMask())) != 0)) { + printImmediateObjecton(oop, GIV(transcript)); + return; + } + if (!(addressCouldBeObj(oop))) { + /* begin printCantBeObject:on: */ + fprintf(GIV(transcript), + "%p%s\n", + ((void *)oop), + ((where = whereIsMaybeCodeThing(oop)) + ? where + : (oop & 7 /* (allocationUnit - 1) */ + ? " is misaligned" + : whereIs(oop)))); + return; + } + if (((longAt((void *)(oop))) & (classIndexMask())) == (isFreeObjectClassIndexPun())) { + printFreeObjecton(oop, GIV(transcript)); + return; + } + if ((!((longAt((void *)(oop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + printForwarderon(oop, GIV(transcript)); + return; + } + className = nameOfClasslengthInto(fetchClassOfNonImm(oop), (&length)); + fprintf(GIV(transcript), + "%p: a(n) %.*s\n", + ((void *)oop), + ((int) length), + className); +} + + /* StackInterpreter>>#shortReversePrintFrameAndCallers: */ +static NoDbgRegParms sqInt +shortReversePrintFrameAndCallers(char *aFramePointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt caller; + char *callerFP; + char *theFP; + StackPage *thePage; + + callerFP = ((char *) 0); + if (!aFramePointer) { + return GIV(nilObj); + } + theFP = aFramePointer; + while (1) { + shortPrintFrame(theFP); + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!(callerFP != 0)) break; + theFP = callerFP; + } + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + caller = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(caller)); + assert((caller == (nilObject())) + || (isContext(followMaybeForwarded(caller)))); + if ((!((longAt((void *)(caller))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + caller = followForwarded(caller); + } + return caller; +} + + +/* Repaint the portion of the Smalltalk screen bounded by the affected + rectangle. Used to synchronize the screen after a Bitblt to the Smalltalk + Display object. + */ + + /* StackInterpreter>>#showDisplayBits:Left:Top:Right:Bottom: */ +sqInt +showDisplayBitsLeftTopRightBottom(sqInt aForm, sqInt l, sqInt t, sqInt r, sqInt b) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + if (deferDisplayUpdates + || (aForm != (longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheDisplay) << (shiftForWord()))))))))) { + return null; + } + updateDisplayLeftTopRightBottom(l, t, r, b); + return 0; +} + + +/* Signal all requested semaphores. Answer if a context switch has occurred. */ + + /* StackInterpreter>>#signalExternalSemaphores */ +static sqInt +signalExternalSemaphores(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt numSlots; + sqInt xArray; + + xArray = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ExternalObjectsArray) << (shiftForWord())))))); + return doSignalExternalSemaphores( + (/* begin numSlotsOf: */ + assert((classIndexOf(xArray)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(xArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(xArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))); +} + + +/* Answer the size of the CallPrimitive bytecode that may be used to store a + method's primitive. + */ +/* NewsqueakV4: 249 11111001 iiiiiiii jjjjjjjj Call Primitive #iiiiiiii + + (jjjjjjjj * 256) + */ +/* SistaV1: 248 11111000 iiiiiiii mssjjjjj Call Primitive #iiiiiiii + ( + jjjjj * 256) */ +/* V3+Closures: 139 11101111 iiiiiiii jjjjjjjj Call Primitive #iiiiiiii + + (jjjjjjjj * 256) + */ + + /* StackInterpreter>>#sizeOfCallPrimitiveBytecode: */ +sqInt +sizeOfCallPrimitiveBytecode(sqInt methodHeader) +{ + return 3; +} + + +/* 234 11101010 i i i i i i i i Store Temporary Variable #iiiiiiii */ +/* 129 10000001 jjkkkkkk Store (Receiver Variable, Temporary Location, + Illegal, Literal Variable) [jj] #kkkkkk */ + + /* StackInterpreter>>#sizeOfLongStoreTempBytecode: */ +sqInt +sizeOfLongStoreTempBytecode(sqInt methodHeader) +{ + return 2; +} + + +/* Return the number of indexable fields of the given object. This method is + to be called from an automatically generated C primitive. The argument is + assumed to be a pointer to the first indexable field of a words or bytes + object; the object header starts 4 bytes before that. + */ +/* Note: Only called by translated primitive code. */ + + /* StackInterpreter>>#sizeOfSTArrayFromCPrimitive: */ +sqInt +sizeOfSTArrayFromCPrimitive(void *cPtr) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + + oop = (oopForPointer(cPtr)) - BaseHeaderSize; + if (!(/* isWordsOrBytes: */ + ((!(oop & (tagMask())))) + && (isWordsOrBytesNonImm(oop)))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } + + /* begin lengthOf: */ + fmt = (byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask()); + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + return numSlots; + } + if (fmt >= (firstByteFormat())) { + return ((numSlots << (shiftForWord()))) - (fmt & 7); + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + return ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + } + if (fmt >= (firstLongFormat())) { + return ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return numSlots; + } + + /* fmt = self forwardedFormat */ + return 0; +} + + +/* Invoke a normal (non-quick) primitive. + Called under the assumption that primFunctionPointer has been preloaded. */ + + /* StackInterpreter>>#slowPrimitiveResponse */ +static int +slowPrimitiveResponse(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt nArgs; + char *savedFramePointer; + char *savedStackPointer; + + assert(!(isOopForwarded(stackValue(GIV(argumentCount))))); + assert((GIV(remapBufferCount)) == 0); + nArgs = GIV(argumentCount); + savedStackPointer = GIV(stackPointer); + savedFramePointer = GIV(framePointer); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if (/* recordPrimTraceForMethod: */ + (recordPrimTrace()) + && ((primTracePluginName + ? methodHasPrimitiveInPrimTracePlugin(GIV(newMethod)) + : 1))) { + /* begin fastLogPrim: */ + GIV(primTraceLog)[GIV(primTraceLogIndex)] = GIV(newMethod); + primTraceLogIndex(GIV(primTraceLogIndex) + 1); + } + dispatchFunctionPointer(primitiveFunctionPointer); + assert(maybeLeakCheckExternalPrimCall(GIV(newMethod))); + + /* begin maybeRetryPrimitiveOnFailure */ + if (GIV(primFailCode)) { + retryPrimitiveOnFailure(); + } + if ((!GIV(primFailCode)) + && ((GIV(framePointer) == savedFramePointer) + && (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))))) { + if (GIV(stackPointer) != (savedStackPointer + (nArgs * BytesPerWord))) { + /* This is necessary but insufficient; the result may still have been written to the stack. + At least we'll know something is wrong. */ + failUnbalancedPrimitive(); + GIV(stackPointer) = savedStackPointer; + } + } + + /* Don't fail if primitive has done something radical, e.g. perform: + If we are profiling, take accurate primitive measures */ + if (GIV(nextProfileTick) > 0) { + checkProfileTickPostPrimitive(); + } + return !GIV(primFailCode); +} + + +/* update state of active context */ + + /* StackInterpreter>>#snapshot: */ +static NoDbgRegParms sqInt +snapshot(sqInt embedded) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt activeContext; + sqInt activeProc; + CogMethod *cogMethod; + usqInt i; + usqInt initialIP; + sqInt methodHeader; + usqInt numArgs; + usqInt numTemps; + sqInt object; + sqInt objOop; + sqInt oop; + sqInt rcvr; + sqInt rcvrSqInt; + sqInt savedTenuringThreshold; + void *setMacType; + sqInt stackIndex; + + + /* For now the stack munging below doesn't deal with more than one argument. + It can, and should. */ + rcvr = 0; + if (GIV(argumentCount)) { + return (GIV(primFailCode) = PrimErrBadNumArgs); + } + + /* Need to convert all frames into contexts since the snapshot file only holds objects. */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + activeContext = voidVMStateForSnapshotFlushingExternalPrimitivesIf(1); + + /* update state of active process */ + + /* begin activeProcess */ + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + activeProc = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ActiveProcessIndex) << (shiftForWord())))))); + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, activeProc, activeContext)); + assert(isNonImmediate(activeProc)); + if (oopisGreaterThanOrEqualTo(activeProc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(activeContext & (tagMask())))) + && (oopisLessThan(activeContext, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(activeProc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(activeProc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((activeProc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),activeContext); + GIV(tempOop) = activeContext; + + /* begin garbageCollectForSnapshot */ + /* begin flushNewSpace */ + savedTenuringThreshold = GIV(tenureThreshold); + GIV(tenureThreshold) = GIV(oldSpaceStart); + scavengingGCTenuringIf(TenureByAge); + + /* begin setRawTenuringThreshold: */ + GIV(tenureThreshold) = savedTenuringThreshold; + assert((GIV(rememberedSetSize)) == 0); + assert(GIV(pastSpaceStart) == (((GIV(pastSpace)).start))); + assert(GIV(freeStart) == (((GIV(eden)).start))); + + /* begin biasForSnapshot */ + GIV(biasForGC) = 0; + fullGC(); + + /* begin biasForGC */ + GIV(biasForGC) = 1; + unfireQueuedEphemeronsForSnapshot(); + prepareForSnapshot(); + + /* begin checkFreeSpace: */ + assert(bitsSetInFreeSpaceMaskForAllFreeLists()); + assert(GIV(totalFreeOldSpace) == (totalFreeListBytes())); + if (((checkForLeaks & (GCCheckFreeSpace | GCModeFull)) == (GCCheckFreeSpace | GCModeFull))) { + runLeakCheckerForFreeSpaceignoring(GCCheckFreeSpace, null); + } + + /* Nothing moves from here on so it is safe to grab the activeContext again. */ + activeContext = GIV(tempOop); + GIV(tempOop) = 0; + if (!GIV(primFailCode)) { + /* Without contexts or stacks simulate + rcvr := self popStack. ''pop rcvr'' + self push: trueObj. + to arrange that the snapshot resumes with true. N.B. stackIndex is one-relative. */ + + /* begin quickFetchInteger:ofObject: */ + oop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + assert((((oop) & 7) == 1)); + stackIndex = (oop >> 3); + rcvr = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(((stackIndex + CtxtTempFrameStart) - 1)) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(activeContext)) + && (!(isForwarded(activeContext)))); + assert(validStorePointerUncheckedArgs((stackIndex + CtxtTempFrameStart) - 1, activeContext, GIV(trueObj))); + longAtput((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(((stackIndex + CtxtTempFrameStart) - 1)) << (shiftForWord()))))),GIV(trueObj)); + + /* now attempt to write the snapshot file */ + writeImageFileIO(); + if ((!GIV(primFailCode)) + && (!embedded)) { + /* set Mac file type and creator; this is a noop on other platforms */ + setMacType = ioLoadFunctionFrom("setMacFileTypeAndCreator", "FilePlugin"); + if (setMacType) { + ((sqInt (*)(char *, char *, char *))setMacType)(imageName, "STim", "FAST"); + } + } + + /* Without contexts or stacks simulate + self pop: 1 */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(activeContext)) + && (!(isForwarded(activeContext)))); + assert(validStorePointerUncheckedArgs(StackPointerIndex, activeContext, (((usqInt)(stackIndex - 1) << 3) | 1))); + longAtput((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord()))))),(((usqInt)(stackIndex - 1) << 3) | 1)); + } + + /* begin postSnapshot */ + restorePostSnapshot(); + refireQueuedEphemeronsPostSnapshot(); + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + if (GIV(primFailCode)) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + methodHeader = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + cogMethod = null; + + /* begin isCogMethodReference: */ + assert(((((methodHeader) & 7) == 1)) + || (((((usqInt)methodHeader)) < (startOfMemory())) + && ((((usqInt)methodHeader)) >= (minCogMethodAddress())))); + if ((!(methodHeader & (smallIntegerTag())))) { + cogMethod = ((CogMethod *) methodHeader); + methodHeader = (cogMethod->methodHeader); + cogMethod = null; + } + numTemps = (((usqInt)(methodHeader)) >> MethodHeaderTempCountShift) & 0x3F; + numArgs = (((usqInt)(methodHeader)) >> MethodHeaderArgCountShift) & 15; + + /* could new rcvr be set at point of send? */ + rcvrSqInt = longAt(GIV(stackPointer) + (numArgs * BytesPerWord)); + assert(addressCouldBeOop(rcvrSqInt)); + assert(!(isOopForwarded(rcvrSqInt))); + if (cogMethod) { + if (!((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()))) { + if (GIV(instructionPointer) >= (startOfMemory())) { + /* begin iframeSavedIP:put: */ + assert(!(isMachineCodeFrame(GIV(framePointer)))); + longAtput(GIV(framePointer) + FoxIFSavedIP,GIV(instructionPointer)); + } + GIV(instructionPointer) = ceReturnToInterpreterPC(); + } + cogMethod = null; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(instructionPointer)); + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)GIV(framePointer))); + GIV(framePointer) = GIV(stackPointer); + initialIP = ((((usqInt)(pointerForOop(GIV(newMethod))))) + ((LiteralStart + ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask))) * BytesPerOop)) + BaseHeaderSize; + if (cogMethod) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),((usqInt)cogMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + GIV(instructionPointer) = (((usqInt)cogMethod)) + ((cogMethod->stackCheckOffset)); + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + GIV(method) = GIV(newMethod); + assert(isOopCompiledMethod(GIV(method))); + assert((methodHeaderOf(GIV(method))) == methodHeader); + GIV(bytecodeSetSelector) = ((((sqLong) methodHeader)) < 0 + ? 0x100 + : 0); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + object = /* encodeFrameFieldHasContext:isBlock:numArgs: */ + (VMBIGENDIAN + ? ((1 + ((numArgs << ((BytesPerWord * 8) - 8))))) + : ((1 + ((numArgs << 8))))); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),object); + longAtput((GIV(stackPointer) -= BytesPerWord),0); + GIV(instructionPointer) = initialIP - 1; + } + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),rcvrSqInt); + + /* clear remaining temps to nil */ + for (i = (numArgs + 1); i <= numTemps; i += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(nilObj)); + } + if (((methodHeader & AlternateHeaderHasPrimFlag) != 0)) { + /* Skip the CallPrimitive bytecode, if it's there, and store the error code if the method starts + with a long store temp. Strictly no need to skip the store because it's effectively a noop. */ + if (!cogMethod) { + GIV(instructionPointer) += 3 /* sizeOfCallPrimitiveBytecode: */; + } + if (GIV(primFailCode)) { + reapAndResetErrorCodeToheader(GIV(stackPointer), methodHeader); + } + } + } + else { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(falseObj)); + } + return 0; +} + + /* StackInterpreter>>#specialSelector: */ +sqInt +specialSelector(sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + /* begin fetchPointer:ofObject: */ + return longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SpecialSelectors) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)((index * 2)) << (shiftForWord())))))); +} + + +/* Insulate the stack zone from the effects of a become. + All receivers must be unfollowed for two reasons: + 1. inst var access is direct with no read barrier + 2. super sends (always to the receiver) have no class check and so don't + trap for forwarded receivers. This is an issue for primitives that assume + their receiver + is valid and don't validate. + Super sends require an explicit check to ensure receivers in super sends + are unforwarded. + e.g. super doSomethingWith: (self become: other) forwards the receiver + self pushed on the + stack. So we could avoid following non-pointer receivers. But this is too + tricky, Instead, we + always follow receivers. + Methods must be unfollowed since bytecode access is direct with no read + barrier. But this only needs to be done if the becomeEffectsFlags indicate + that a + CompiledMethod was becommed. + The scheduler state must be followed, but only if the becomeEffectsFlags + indicate that a pointer object was becommed. */ +/* For VM profiling */ + + /* StackInterpreter>>#spurPostBecomeAction: */ +static NoDbgRegParms void +spurPostBecomeAction(sqInt theBecomeEffectsFlags) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqIntptr_t c; + sqInt first; + int i; + sqInt ipdelta; + sqInt iSqInt; + sqInt last; + sqInt list; + sqIntptr_t m; + sqInt next; + usqInt numSlots; + sqInt obj; + sqInt procLists; + sqInt referent; + sqInt result; + sqIntptr_t s; + sqInt sched; + sqInt schedAssoc; + sqInt toDoLimit; + sqInt xArray; + + if (theBecomeEffectsFlags) { + if (((theBecomeEffectsFlags & BecameActiveClassFlag) != 0)) { + /* begin flushBecommedClassesInMethodCache */ + for (i = 0; i < MethodCacheSize; i += MethodCacheEntrySize) { + c = GIV(methodCache)[i + MethodCacheClass]; + s = GIV(methodCache)[i + MethodCacheSelector]; + if ((c != 0) + && ((s != 0) + && (isForwarded(classOrNilAtIndex(c))))) { + GIV(methodCache)[i + MethodCacheClass] = 0; + GIV(methodCache)[i + MethodCacheSelector] = 0; + } + } + unlinkSendsLinkedForInvalidClasses(); + } + if (((theBecomeEffectsFlags & BecamePointerObjectFlag) != 0)) { + /* begin followForwardingPointersInScheduler */ + schedAssoc = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord())))))); + + /* the GC follows pointers in the special objects array for us. */ + assert(!(isForwarded(schedAssoc))); + + /* Make sure the active process has been followed. */ + followForwardedObjectFieldstoDepth(schedAssoc, 1); + sched = longAt((void *)((schedAssoc + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin followObjField:ofObject: */ + procLists = longAt((void *)((sched + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + assert(isNonImmediate(procLists)); + if ((!((longAt((void *)(procLists))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + procLists = fixFollowedFieldofObjectwithInitialValue(ProcessListsIndex, sched, procLists); + } + + /* Follow all links in the process list to ensure the lists are valid. */ + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(procLists)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(procLists + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(procLists - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (iSqInt = 0; iSqInt <= toDoLimit; iSqInt += 1) { + /* begin followObjField:ofObject: */ + list = longAt((void *)((procLists + BaseHeaderSize) + ((((usqInt)(iSqInt) << (shiftForWord())))))); + assert(isNonImmediate(list)); + if ((!((longAt((void *)(list))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + list = fixFollowedFieldofObjectwithInitialValue(iSqInt, procLists, list); + } + + /* begin followObjField:ofObject: */ + first = longAt((void *)((list + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(first)); + if ((!((longAt((void *)(first))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + first = fixFollowedFieldofObjectwithInitialValue(FirstLinkIndex, list, first); + } + + /* begin followObjField:ofObject: */ + last = longAt((void *)((list + BaseHeaderSize) + ((((usqInt)(LastLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(last)); + if ((!((longAt((void *)(last))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + last = fixFollowedFieldofObjectwithInitialValue(LastLinkIndex, list, last); + } + while (first != last) { + /* begin followObjField:ofObject: */ + next = longAt((void *)((first + BaseHeaderSize) + ((((usqInt)(NextLinkIndex) << (shiftForWord())))))); + assert(isNonImmediate(next)); + if ((!((longAt((void *)(next))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + next = fixFollowedFieldofObjectwithInitialValue(NextLinkIndex, first, next); + } + first = next; + } + } + + /* begin followForwardingPointersInSpecialObjectsArray */ + /* begin followSemaphoreIn:at: */ + obj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheLowSpaceSemaphore) << (shiftForWord())))))); + if ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + obj = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheLowSpaceSemaphore, GIV(specialObjectsOop), obj)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheLowSpaceSemaphore) << (shiftForWord()))))),obj); + } + + /* begin followSemaphoreIn:at: */ + obj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheInterruptSemaphore) << (shiftForWord())))))); + if ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + obj = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheInterruptSemaphore, GIV(specialObjectsOop), obj)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheInterruptSemaphore) << (shiftForWord()))))),obj); + } + + /* begin followSemaphoreIn:at: */ + obj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord())))))); + if ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + obj = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheTimerSemaphore, GIV(specialObjectsOop), obj)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord()))))),obj); + } + + /* begin followSemaphoreIn:at: */ + obj = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheFinalizationSemaphore) << (shiftForWord())))))); + if ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + obj = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheFinalizationSemaphore, GIV(specialObjectsOop), obj)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheFinalizationSemaphore) << (shiftForWord()))))),obj); + } + xArray = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ExternalObjectsArray) << (shiftForWord())))))); + if ((!((longAt((void *)(xArray))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(xArray)); + referent = longAt((void *)((xArray + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + xArray = referent; + + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ExternalObjectsArray, GIV(specialObjectsOop), xArray)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(xArray & (tagMask())))) + && (oopisLessThan(xArray, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ExternalObjectsArray) << (shiftForWord()))))),xArray); + } + toDoLimit = ((/* begin numSlotsOf: */ + assert((classIndexOf(xArray)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(xArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(xArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - 1; + for (ipdelta = 0; ipdelta <= toDoLimit; ipdelta += 1) { + /* begin followSemaphoreIn:at: */ + obj = longAt((void *)((xArray + BaseHeaderSize) + ((((usqInt)(ipdelta) << (shiftForWord())))))); + if ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(obj)); + referent = longAt((void *)((obj + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + obj = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(ipdelta, xArray, obj)); + assert(isNonImmediate(xArray)); + if (oopisGreaterThanOrEqualTo(xArray, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(obj & (tagMask())))) + && (oopisLessThan(obj, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(xArray + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(xArray); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((xArray + BaseHeaderSize) + ((((usqInt)(ipdelta) << (shiftForWord()))))),obj); + } + } + } + if (((theBecomeEffectsFlags & (BecamePointerObjectFlag + BecameCompiledMethodFlag)) != 0)) { + /* begin followForwardingPointersInProfileState */ + /* begin profileStateDoUpdating: */ + if (GIV(profileProcess)) { + if ((result = ((!((longAt((void *)(GIV(profileProcess)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileProcess)) + : 0))) { + GIV(profileProcess) = result; + } + } + if (GIV(profileMethod)) { + if ((result = ((!((longAt((void *)(GIV(profileMethod)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileMethod)) + : 0))) { + GIV(profileMethod) = result; + } + } + if (GIV(profileSemaphore)) { + if ((result = ((!((longAt((void *)(GIV(profileSemaphore)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))) + ? followForwarded(GIV(profileSemaphore)) + : 0))) { + GIV(profileSemaphore) = result; + } + } + if (((theBecomeEffectsFlags & BecameCompiledMethodFlag) != 0)) { + /* begin followForwardedMethodsInMethodCache */ + for (i = 0; i < MethodCacheSize; i += MethodCacheEntrySize) { + c = GIV(methodCache)[i + MethodCacheClass]; + s = GIV(methodCache)[i + MethodCacheSelector]; + m = GIV(methodCache)[i + MethodCacheMethod]; + if ((c != 0) + && ((s != 0) + && ((m != 0) + && (/* isOopForwarded: */ + ((!(m & (tagMask())))) + && ((!((longAt((void *)(m))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(m)); + referent = longAt((void *)((m + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + m = referent; + GIV(methodCache)[i + MethodCacheMethod] = m; + } + } + + /* begin followMethodNewMethodAndInstructionPointer */ + ipdelta = 0; + if ((!((longAt((void *)(GIV(method)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + ipdelta = (/* method:includesAddress: */ + (GIV(instructionPointer) > GIV(method)) + && (GIV(instructionPointer) < (GIV(method) + ((((numSlotsOfAny(GIV(method))) << (shiftForWord()))) + BaseHeaderSize))) + ? GIV(instructionPointer) - GIV(method) + : 0); + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(GIV(method))); + referent = longAt((void *)((GIV(method) + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + GIV(method) = referent; + if (ipdelta) { + GIV(instructionPointer) = GIV(method) + ipdelta; + } + } + if (/* isOopForwarded: */ + ((!(GIV(newMethod) & (tagMask())))) + && ((!((longAt((void *)(GIV(newMethod)))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ipdelta = ((!ipdelta) + && (/* method:includesAddress: */ + (GIV(instructionPointer) > GIV(newMethod)) + && (GIV(instructionPointer) < (GIV(newMethod) + ((((numSlotsOfAny(GIV(newMethod))) << (shiftForWord()))) + BaseHeaderSize)))) + ? GIV(instructionPointer) - GIV(newMethod) + : 0); + + /* don't relocate twice!! */ + + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(GIV(newMethod))); + referent = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + GIV(newMethod) = referent; + } + + /* maybe oop due to object-as-method */ + if (ipdelta) { + GIV(instructionPointer) = GIV(newMethod) + ipdelta; + } + } + } + } + followForwardingPointersOfReceiversInStackZone(); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackFloatValue: */ +double +stackFloatValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return floatValueOf(longAt(GIV(stackPointer) + (offset * BytesPerWord))); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackIntegerValue: */ +sqInt +stackIntegerValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerPointer; + + integerPointer = longAt(GIV(stackPointer) + (offset * BytesPerWord)); + + /* begin checkedIntegerValueOf: */ + if ((((integerPointer) & 7) == 1)) { + return (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return 0; + } +} + + +/* Answer the actual stackLimit offset in a page. Since stackPageByteSize may + have chosen to shrink a page + this may be less than stackPageFrameBytes, but it should be no more than + stackPageFrameBytes. + */ + + /* StackInterpreter>>#stackLimitBytes */ +static sqInt +stackLimitBytes(void) +{ + return (((stackPageFrameBytes()) < (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))) ? (stackPageFrameBytes()) : (((stackPageByteSize()) - (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) - (osCogStackPageHeadroom()))); +} + + +/* Ensures that the given object is a real object, not a SmallInteger. */ +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackMutableObjectValue: */ +sqInt +stackMutableObjectValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = longAt(GIV(stackPointer) + (offset * BytesPerWord)); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(oop + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + } + return oop; +} + + +/* Ensures that the given object is a real object, not a SmallInteger. */ +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackObjectValue: */ +sqInt +stackObjectValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt oop; + + oop = longAt(GIV(stackPointer) + (offset * BytesPerWord)); + if (((oop & (tagMask())) != 0)) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return oop; +} + + +/* Answer a page size that is a power-of-two and contains a useful number of + frames. Room for 256 slots for frames gives around 40 frames a page which + is a + good compromise between overflow rate and latency in divorcing a page. */ + + /* StackInterpreter>>#stackPageByteSize */ +static sqInt +stackPageByteSize(void) +{ + sqInt largeSize; + sqInt pageBytes; + sqInt smallSize; + + pageBytes = ((stackPageFrameBytes()) + (((IFrameSlots + LargeContextSlots) + 1) * BytesPerWord)) + (osCogStackPageHeadroom()); + if (!(pageBytes & (pageBytes - 1))) { + return pageBytes; + } + + /* = 0 => a power of two + round up or round down; that is the question. If rounding down reduces + the size by no more than 1/8th round down, otherwise roundup. */ + largeSize = 1ULL << (highBit(pageBytes)); + smallSize = 1ULL << ((highBit(pageBytes)) - 1); + assert((largeSize > pageBytes) + && (pageBytes > smallSize)); + return ((pageBytes - smallSize) <= (smallSize / 8) + ? smallSize + : largeSize); +} + + +/* Return the stackPointer of a Context or BlockContext. */ + + /* StackInterpreter>>#stackPointerForMaybeMarriedContext: */ +static NoDbgRegParms sqInt +stackPointerForMaybeMarriedContext(sqInt aContext) +{ + sqInt sp; + sqInt spSqInt; + + if (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(aContext)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(aContext))); + return sp; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + return 0; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + return (spSqInt >> 3); +} + + +/* Return the 0-based index rel to the given frame. + (This is what stackPointer used to be before conversion to pointer) */ +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackPointerIndexForFrame: */ +static NoDbgRegParms sqInt +stackPointerIndexForFrame(char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *thePage; + char *theSP; + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + theSP = findSPOfon(theFP, thePage); + return /* stackPointerIndexForFrame:WithSP: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? (((usqInt)(((theFP + FoxMFReceiver) - theSP))) >> (shiftForWord())) + (((mframeCogMethod(theFP))->cmNumArgs)) + : (((usqInt)(((theFP + FoxIFReceiver) - theSP))) >> (shiftForWord())) + (byteAt((theFP + FoxIFrameFlags) + 1))); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackPositiveMachineIntegerValue: */ +usqIntptr_t +stackPositiveMachineIntegerValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerPointer; + sqInt value; + + integerPointer = longAt(GIV(stackPointer) + (offset * BytesPerWord)); + + /* begin positiveMachineIntegerValueOf: */ + if ((((integerPointer) & 7) == 1)) { + value = (integerPointer >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + return value; + } + + /* don't inline the rare case */ + return positiveMachineIntegerValueOfObj(integerPointer); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackSignedMachineIntegerValue: */ +sqIntptr_t +stackSignedMachineIntegerValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerPointer; + + integerPointer = longAt(GIV(stackPointer) + (offset * BytesPerWord)); + + /* begin signedMachineIntegerValueOf: */ + if ((((integerPointer) & 7) == 1)) { + return (integerPointer >> 3); + } + return signedMachineIntegerValueOfObj(integerPointer); +} + + /* StackInterpreter>>#stackTop */ +sqInt +stackTop(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt(GIV(stackPointer)); +} + + +/* In the StackInterpreter stacks grow down. */ + + /* StackInterpreter>>#stackValue: */ +sqInt +stackValue(sqInt offset) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + return longAt(GIV(stackPointer) + (offset * BytesPerWord)); +} + + +/* Zero-relative version of CompiledMethod>>startpc. */ + + /* StackInterpreter>>#startPCOfMethod: */ +usqInt +startPCOfMethod(sqInt aCompiledMethod) +{ + return ((literalCountOf(aCompiledMethod)) + LiteralStart) * BytesPerOop; +} + + +/* Return what ST would return for at: index. */ + + /* StackInterpreter>>#stObject:at: */ +sqInt +stObjectat(sqInt array, sqInt index) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt sp; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + + hdr = long64At((void *)(array)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(array + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(array)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(array)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(array))); + stSize = sp; + goto l3; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l3; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(array))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l3: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(array)))))) { + return temporaryin(index - 1, frameOfMarriedContext(array)); + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(array)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + return (((usqInt)(byteAt((void *)((array + BaseHeaderSize) + (index - 1)))) << 3) | 1); + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(array); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + return longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << (shiftForWord())))))); + } + if (fmt >= (firstByteFormat())) { + return (((usqInt)(byteAt((void *)((array + BaseHeaderSize) + ((index + fixedFields) - 1)))) << 3) | 1); + } + if (fmt >= (firstShortFormat())) { + return (((usqInt)(((unsigned short) (shortAt((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))))))) << 3) | 1); + } + if (fmt == (sixtyFourBitIndexableFormat())) { + return positive64BitIntegerFor(long64At((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))))); + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + return (((usqInt)((((usqInt)(long32At((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + return 0; +} + + +/* Do what ST would return for at: index put: value. */ + + /* StackInterpreter>>#stObject:at:put: */ +sqInt +stObjectatput(sqInt array, sqInt index, sqInt value) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fieldIndex; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt signedValueToStore; + sqInt signedValueToStoreSqInt; + sqInt sp; + sqInt spSqInt; + sqInt stSize; + sqInt totalLength; + usqLong unsigned64BitValueToStore; + unsigned int unsignedValueToStore; + + hdr = long64At((void *)(array)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(array + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(array - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l1: + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(array)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(array)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(array))); + stSize = sp; + goto l2; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l2; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(array))); + stSize = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l2: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSize)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((array + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(array)))))) { + return temporaryinput(index - 1, frameOfMarriedContext(array), value); + } + } + if (fmt >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(array)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLength))) { + if (((((value) & 7) == 1)) + && ((((usqInt)((signedValueToStore = (value >> 3))))) <= 0xFF)) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((array + BaseHeaderSize) + (index - 1)),signedValueToStore); + return value; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return value; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l3; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l3; + } + class = fetchClassOfNonImm(array); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l3: + stSize = totalLength - fixedFields; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSize))) { + /* begin subscript:with:storing:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + fieldIndex = (index + fixedFields) - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, array, value)); + assert(isNonImmediate(array)); + if (oopisGreaterThanOrEqualTo(array, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(array + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(array); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),value); + goto l4; + } + if (fmt >= (firstByteFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((array + BaseHeaderSize) + ((index + fixedFields) - 1)),signedValueToStoreSqInt); + goto l4; + } + if (fmt >= (firstShortFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))),signedValueToStoreSqInt); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l4; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((array + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l4: + return value; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmt <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + return value; +} + + +/* Set the sender of a frame. If the frame is a base frame then this is + trivial; merely store into the FoxCallerSavedIP/FoxCallerContext field. If + not, then + split the stack at the frame, moving the frame and those hotter than it to + a new stack page. In the new stack page the frame will be the base frame + and storing trivial. Answer the possibly changed location of theFP. */ + + /* StackInterpreter>>#storeSenderOfFrame:withValue: */ +static NoDbgRegParms char * +storeSenderOfFramewithValue(char *theFP, sqInt anOop) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt callerContextOrNil; + char *callerFP; + StackPage *newPage; + int onCurrentPage; + char *theMovedFP; + StackPage *thePage; + StackPage *thePage1; + + if (!(longAt(theFP + FoxSavedFP))) { + /* begin frameCallerContext:put: */ + assert((anOop == (nilObject())) + || (isContext(anOop))); + assert(isBaseFrame(theFP)); + assert(((theFP + (frameStackedReceiverOffset(theFP))) + (2 * BytesPerWord)) == (((stackPageFor(theFP))->baseAddress))); + assert((longAt((theFP + (frameStackedReceiverOffset(theFP))) + BytesPerWord)) == (frameContext(theFP))); + longAtput((theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + (2 * BytesPerWord),anOop); + return theFP; + } + + /* begin ensureCallerContext: */ + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + if (!callerFP) { + /* begin frameCallerContext: */ + assert(isBaseFrame(theFP)); + thePage1 = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerContextOrNil = longAt((thePage1->baseAddress)); + assert(addressCouldBeObj(callerContextOrNil)); + assert((callerContextOrNil == (nilObject())) + || (isContext(followMaybeForwarded(callerContextOrNil)))); + goto l2; + } + + /* base frame, context in saved ip slot (or base of stack in Cog) */ + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(callerFP))); + + /* frameContext: */ + goto l2; + } + marryFrameSP( + callerFP, + (/* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFP))), + (theFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord)); + /* end ensureCallerContext: */ +l2: + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + onCurrentPage = thePage == GIV(stackPage); + if (!onCurrentPage) { + /* Make sure the frame's page isn't divorced when a new page is allocated. */ + markStackPageNextMostRecentlyUsed(thePage); + } + + /* begin newStackPage */ + newPage = (GIV(mostRecentlyUsedPage)->nextPage); + if (!((newPage->baseFP))) { + goto l1; + } + divorceFramesIn(newPage); + /* end newStackPage */ +l1: + theMovedFP = moveFramesInthroughtoPage(thePage, theFP, newPage); + if (onCurrentPage) { + /* begin setStackPageAndLimit: */ + assert(newPage); + GIV(stackPage) = newPage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(newPage); + } + else { + markStackPageMostRecentlyUsed(newPage); + } + assert(isBaseFrame(theMovedFP)); + + /* begin frameCallerContext:put: */ + assert((anOop == (nilObject())) + || (isContext(anOop))); + assert(isBaseFrame(theMovedFP)); + assert(((theMovedFP + (frameStackedReceiverOffset(theMovedFP))) + (2 * BytesPerWord)) == (((stackPageFor(theMovedFP))->baseAddress))); + assert((longAt((theMovedFP + (frameStackedReceiverOffset(theMovedFP))) + BytesPerWord)) == (frameContext(theMovedFP))); + longAtput((theMovedFP + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theMovedFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theMovedFP))->cmNumArgs) + : byteAt((theMovedFP + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + (2 * BytesPerWord),anOop); + return theMovedFP; +} + + +/* Answer the number of indexable fields in the given object. (i.e., what + Smalltalk would return for size). + */ +/* Note: Assume oop is not an immediate! */ + + /* StackInterpreter>>#stSizeOf: */ +sqInt +stSizeOf(sqInt oop) +{ + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt totalLength; + + hdr = long64At((void *)(oop)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(oop + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(oop - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l1; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l1; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l1; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l1; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l1: + if (/* isPureBitsFormat: */ + (fmt >= (sixtyFourBitIndexableFormat())) + && (fmt < (firstCompiledMethodFormat()))) { + return totalLength; + } + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l2; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l2; + } + class = fetchClassOfNonImm(oop); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l2: + if (fmt == (indexablePointersFormat())) { + assert(!(isContextHeader(hdr))); + } + return totalLength - fixedFields; +} + + +/* Using a read barrier here simplifies the become implementation and costs + very little because the class index and superclass almost certainly share + a cache line. */ + + /* StackInterpreter>>#superclassOf: */ +sqInt +superclassOf(sqInt classPointer) +{ + sqInt objOop; + + /* begin followObjField:ofObject: */ + objOop = longAt((void *)((classPointer + BaseHeaderSize) + ((((usqInt)(SuperclassIndex) << (shiftForWord())))))); + assert(isNonImmediate(objOop)); + if ((!((longAt((void *)(objOop))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + objOop = fixFollowedFieldofObjectwithInitialValue(SuperclassIndex, classPointer, objOop); + } + return objOop; +} + + +/* Signal the given semaphore from within the interpreter. + Answer if the current process was preempted. */ + + /* StackInterpreter>>#synchronousSignal: */ +static NoDbgRegParms sqInt +synchronousSignal(sqInt aSemaphore) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + sqLong excessSignals; + sqInt proc; + sqInt referent; + + assert((addressCouldBeObj(aSemaphore)) + && ((!(isForwarded(aSemaphore))) + && ((numSlotsOf(aSemaphore)) > ExcessSignalsIndex))); + + /* begin isEmptyList: */ + assert(!(isForwarded(aSemaphore))); + if ((longAt((void *)((aSemaphore + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj)) { + /* no process is waiting on this semaphore */ + excessSignals = fetchIntegerofObject(ExcessSignalsIndex, aSemaphore); + storeIntegerofObjectwithValue(ExcessSignalsIndex, aSemaphore, excessSignals + 1); + return 0; + } + + /* begin ensureSemaphoreUnforwardedThroughContext: */ + proc = longAt((void *)((aSemaphore + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + if ((!((longAt((void *)(proc))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + followForwardedObjectFieldstoDepth(aSemaphore, 1); + proc = longAt((void *)((aSemaphore + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord())))))); + } + assert(!((isForwarded(proc)))); + ctxt = longAt((void *)((proc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if ((!((longAt((void *)(ctxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(ctxt)); + referent = longAt((void *)((ctxt + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + ctxt = referent; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(SuspendedContextIndex, proc, ctxt)); + assert(isNonImmediate(proc)); + if (oopisGreaterThanOrEqualTo(proc, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(ctxt & (tagMask())))) + && (oopisLessThan(ctxt, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(proc + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(proc); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((proc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord()))))),ctxt); + } + return resumepreemptedYieldingIffrom(removeFirstLinkOfList(aSemaphore), (!(imageHeaderFlags & 16)), CSSynchronousSignal); +} + + /* StackInterpreter>>#tempCountOf: */ +usqInt +tempCountOf(sqInt methodPointer) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt header; + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + + /* begin temporaryCountOfMethodHeader: */ + return (((usqInt)(header)) >> MethodHeaderTempCountShift) & 0x3F; +} + + /* StackInterpreter>>#temporaryCountOfMethodHeader: */ +usqInt +temporaryCountOfMethodHeader(sqInt header) +{ + return (((usqInt)(header)) >> MethodHeaderTempCountShift) & 0x3F; +} + + /* StackInterpreter>>#ultimateLiteralOf: */ +sqInt +ultimateLiteralOf(sqInt aMethodOop) +{ + sqInt offset; + + assert(isOopCompiledMethod(aMethodOop)); + offset = (literalCountOf(aMethodOop)) - 1; + + /* begin literal:ofMethod: */ + return longAt((void *)((aMethodOop + BaseHeaderSize) + ((((usqInt)((offset + LiteralStart)) << (shiftForWord())))))); +} + + +/* Follow the first literal of either a primitiveCallout or + primitiveExternalCall primitive method. This + will be an ExternalFunction for primitiveCallout or a four element Array + for primitiveExternalCall. + This is here to avoid following all the literals in a method, which would + be slow. Remember + forwarders are unlikely, so we only want to follow what is necessary, and + for an FFI call or + external primitive only the first literal is salient. */ + + /* StackInterpreter>>#unfollowFirstLiteralOfMaybeCalloutMethod:primitiveIndex: */ +static NoDbgRegParms sqInt +unfollowFirstLiteralOfMaybeCalloutMethodprimitiveIndex(sqInt methodObj, sqInt primIndex) +{ + sqInt firstLiteral; + sqInt found; + + found = 0; + + /* inlined self literal: 0 ofMethod: methodObj for clarity... */ + firstLiteral = longAt((void *)((methodObj + BaseHeaderSize) + ((((usqInt)(LiteralStart) << (shiftForWord())))))); + if (/* isOopForwarded: */ + ((!(firstLiteral & (tagMask())))) + && ((!((longAt((void *)(firstLiteral))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + found = 1; + firstLiteral = fixFollowedFieldofObjectwithInitialValue(LiteralStart, methodObj, firstLiteral); + } + if (followForwardedObjectFieldstoDepth(firstLiteral, (primIndex == PrimNumberFFICall + ? (((sqInt)((primitiveMetadataTable[PrimNumberFFICall]))) >> SpurPrimitiveAccessorDepthShift) - 1 + : 0))) { + found = 1; + } + return found; +} + + +/* Repaint the portion of the Smalltalk screen bounded by the affected + rectangle. Used to synchronize the screen after a Bitblt to the Smalltalk + Display object. */ + + /* StackInterpreter>>#updateDisplayLeft:Top:Right:Bottom: */ +static NoDbgRegParms sqInt +updateDisplayLeftTopRightBottom(sqInt l, sqInt t, sqInt r, sqInt b) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bottom; + sqInt left; + sqInt right; + sqInt surfaceHandle; + sqInt top; + + left = ((l < 0) ? 0 : l); + right = ((r < displayWidth) ? r : displayWidth); + top = ((t < 0) ? 0 : t); + bottom = ((b < displayHeight) ? b : displayHeight); + if (!((left <= right) + && (top <= bottom))) { + return null; + } + if (((((((sqInt)displayBits))) & 7) == 1)) { + surfaceHandle = ((((sqInt)displayBits)) >> 3); + if (!showSurfaceFn) { + showSurfaceFn = ioLoadFunctionFrom("ioShowSurface", "SurfacePlugin"); + if (!showSurfaceFn) { + return (GIV(primFailCode) = PrimErrNotFound); + } + } + showSurfaceFn(surfaceHandle, left, top, right - left, bottom - top); + } + else { + assert(isNonImmediate(((sqInt)displayBits))); + ioShowDisplay(((sqInt)displayBits), displayWidth, displayHeight, displayDepth, left, right, top, bottom); + } + return 0; +} + + +/* Note that we accept anInstrPointer pointing to a callPrimitiveBytecode + at the start of a method that contains a primitive. This because methods + like Context(Part)>>reset have to be updated to skip the callPrimtiive + bytecode otherwise. */ + + /* StackInterpreter>>#validBCPC:inMethod: */ +static NoDbgRegParms sqInt +validBCPCinMethod(sqInt thePC, usqInt aMethod) +{ + return (thePC >= (((literalCountOf(aMethod)) + LiteralStart) * BytesPerOop)) + && (thePC < (numBytesOfBytes(aMethod))); +} + + +/* Note that we accept anInstrPointer pointing to a callPrimitiveBytecode + at the start of a method that contains a primitive. This because methods + like Context(Part)>>reset have to be updated to skip the callPrimtiive + bytecode otherwise. */ + + /* StackInterpreter>>#validInstructionPointer:inFrame: */ +static NoDbgRegParms sqInt +validInstructionPointerinFrame(usqInt anInstrPointer, char *fp) +{ + return validInstructionPointerinMethodframePointer(anInstrPointer, /* frameMethodObject: */ + ((((usqInt)(longAt(fp + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(fp))->methodObject) + : longAt(fp + FoxMethod)), fp); +} + + +/* Check that the base frames in all in-use stack pages have a sender and a + saved context. + */ + + /* StackInterpreter>>#validStackPageBaseFrames */ +static sqInt +validStackPageBaseFrames(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + StackPage *aPage; + sqInt i; + + for (i = 0; i < GIV(numStackPages); i += 1) { + /* begin stackPageAt: */ + aPage = stackPageAtpages(i, GIV(pages)); + if (!(isFree(aPage))) { + if (!(validStackPageBaseFrame(aPage))) { + return 0; + } + } + } + return 1; +} + + +/* Void the state associated with the long-running primitive check. + This is done when a new semaphore is installed or when it appears + that is longRunningPrimitiveCheckMethod is invalid, e.g. because it + has eben sampled in the middle of a GC. */ + + /* StackInterpreter>>#voidLongRunningPrimitive: */ +#if LRPCheck +static NoDbgRegParms NeverInline void +voidLongRunningPrimitive(char *reason) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + GIV(longRunningPrimitiveCheckMethod) = null; + GIV(longRunningPrimitiveStartUsecs) = (GIV(longRunningPrimitiveStopUsecs) = 0); + GIV(longRunningPrimitiveSignalUndelivered) = 1; + sqLowLevelMFence(); +} +#endif /* LRPCheck */ + + +/* Find the highest priority process that is ready to run and + remove it from its list. + To save time looking at many empty lists before finding a + runnable process the VM maintains a variable holding the + highest priority runnable process. If this variable is 0 then the + VM does not know the highest priority and must search all lists. + Answer nil if no process can be found. */ + + /* StackInterpreter>>#wakeHighestPriority */ +static sqInt +wakeHighestPriority(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + usqInt numSlots; + sqInt objOop; + sqInt p; + sqInt proc; + sqInt processList; + sqInt schedLists; + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + objOop = longAt((void *)(((longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SchedulerAssociation) << (shiftForWord()))))))) + BaseHeaderSize) + ((((usqInt)(ValueIndex) << (shiftForWord())))))); + + /* begin fetchPointer:ofObject: */ + schedLists = longAt((void *)((objOop + BaseHeaderSize) + ((((usqInt)(ProcessListsIndex) << (shiftForWord())))))); + p = (GIV(highestRunnableProcessPriority) + ? GIV(highestRunnableProcessPriority) + : (/* begin numSlotsOf: */ + assert((classIndexOf(schedLists)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(schedLists + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(schedLists - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))); + while (((p -= 1)) >= 0) { + processList = longAt((void *)((schedLists + BaseHeaderSize) + ((((usqInt)(p) << (shiftForWord())))))); + while (1) { + /* begin isEmptyList: */ + assert(!(isForwarded(processList))); + if ((longAt((void *)((processList + BaseHeaderSize) + ((((usqInt)(FirstLinkIndex) << (shiftForWord()))))))) == GIV(nilObj)) break; + /* Only answer processes with a runnable suspendedContext. + Discard those that aren't; the VM would crash otherwise. */ + proc = removeFirstLinkOfList(processList); + ctxt = longAt((void *)((proc + BaseHeaderSize) + ((((usqInt)(SuspendedContextIndex) << (shiftForWord())))))); + if (isLiveContext(ctxt)) { + GIV(highestRunnableProcessPriority) = p + 1; + return proc; + } + + /* This is uncommon, so we can deal with forwarders here instead of assuming there isn't. */ + if (/* isOopForwarded: */ + ((!(ctxt & (tagMask())))) + && ((!((longAt((void *)(ctxt))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + ctxt = fixFollowedFieldofObjectwithInitialValue(SuspendedContextIndex, proc, ctxt); + } + if (isLiveContext(ctxt)) { + GIV(highestRunnableProcessPriority) = p + 1; + return proc; + } + warning("evicted zombie process from run queue"); + } + } + return null; +} + + +/* Write the image header and heap contents to imageFile for snapshot. c.f. + writeImageFileIOSimulation. The game below is to maintain 64-bit alignment + for all putLong:toFile: occurrences. */ + + /* StackInterpreter>>#writeImageFileIO */ +static NeverInline sqInt +writeImageFileIO(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt bytesWritten; + sqImageFile f; + sqInt headerSize; + squeakFileOffsetType headerStart; + sqInt i; + sqInt imageBytes; + extern char imageName[]; + sqInt okToWrite; + void *sCWIfn; + + /* If the security plugin can be loaded, use it to check for write permission. + If not, assume it's ok */ + sCWIfn = ioLoadFunctionFrom("secCanWriteImage", "SecurityPlugin"); + if (sCWIfn) { + okToWrite = ((sqInt (*)(void))sCWIfn)(); + if (!okToWrite) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return null; + } + } + + /* local constants */ + headerStart = 0; + + /* 64 or 128; header size in bytes; do not change! */ + headerSize = BytesPerWord * 16; + f = sqImageFileOpen(imageName, "wb"); + if (invalidSqImageFile(f)) { + return (GIV(primFailCode) = PrimErrOperationFailed); + } + + /* could not open the image file for writing */ + + /* begin imageSizeToWrite */ + assert(newSpaceIsEmpty()); + imageBytes = 0; + for (i = 0; i < GIV(numSegments); i += 1) { + if ((((GIV(segments)[i]).segSize)) > (2 * BaseHeaderSize)) { + imageBytes += ((GIV(segments)[i]).segSize); + } + } + headerStart = sqImageFileStartLocation(f, imageName, headerSize + imageBytes); + /* Note: on Unix systems one could put an exec command here, padded to 512 bytes */; + + /* position file to start of header */ + sqImageFileSeek(f, headerStart); + putWord32toFile(/* imageFormatVersionForSnapshot */ + (GIV(multipleBytecodeSetsActive) + ? 68533 /* (imageFormatVersion bitOr: MultipleBytecodeSetsBitmask) */ + : 68021 /* imageFormatVersion */), f); + putWord32toFile(headerSize, f); + putLongtoFile(imageBytes, f); + putLongtoFile(GIV(oldSpaceStart), f); + putLongtoFile(GIV(specialObjectsOop), f); + putLongtoFile(GIV(lastHash), f); + putLongtoFile(getSnapshotScreenSize(), f); + putLongtoFile(imageHeaderFlags, f); + putWord32toFile(extraVMMemory, f); + putShorttoFile(desiredNumStackPages, f); + putShorttoFile((desiredCogCodeSize + 0x3FF) / 0x400, f); + putWord32toFile(desiredEdenBytes, f); + putShorttoFile((GIV(maxExtSemTabSizeSet) + ? ioGetMaxExtSemTableSize() + : 0), f); + putShorttoFile(GIV(the2ndUnknownShort), f); + putLongtoFile(((GIV(segments)[0]).segSize), f); + putLongtoFile(GIV(totalFreeOldSpace), f); + putLongtoFile(0, f); + putLongtoFile(0, f); + putLongtoFile(0, f); + putLongtoFile(0, f); + putLongtoFile(0, f); + assert((headerStart + headerSize) == (sqImageFilePosition(f))); + + /* position file after the header */ + sqImageFileSeek(f, headerStart + headerSize); + if (GIV(primFailCode)) { + sqImageFileClose(f); + return null; + } + + /* file write or seek failure + write the image data */ + bytesWritten = writeImageSegmentsToFile(f); + + /* begin success: */ + if (!(bytesWritten == imageBytes)) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + sqImageFileClose(f); + return 0; +} + + /* StackInterpreterPrimitives>>#cloneContext: */ +static NoDbgRegParms usqInt +cloneContext(sqInt aContext) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt cloned; + sqInt fieldIndex; + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + sqInt i; + usqInt newObj; + usqInt numBytes; + usqInt numSlots; + sqInt senderOop; + sqInt sp; + char *spouseFP; + usqInt sz; + sqInt valuePointer; + + /* begin numSlotsOf: */ + assert((classIndexOf(aContext)) > (isForwardedObjectClassIndexPun())); + sz = (((numSlots = byteAt((void *)(aContext + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aContext - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + + /* begin eeInstantiateMethodContextSlots: */ + /* begin allocateSmallNewSpaceSlots:format:classIndex: */ + assert(sz < (numSlotsMask())); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((sz < 1 + ? 8 /* allocationUnit */ + : sz * BytesPerOop)); + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateSmallNewSpaceSlots:format:classIndex:"); + cloned = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) sz)) << (numSlotsFullShift()))) + ((((usqInt)((indexablePointersFormat())) << (formatShift()))))) + ClassMethodContextCompactIndex); + GIV(freeStart) += numBytes; + cloned = newObj; + /* end eeInstantiateMethodContextSlots: */ +l1: + assert(!((cloned == 0))); + for (i = 0; i <= StackPointerIndex; i += 1) { + valuePointer = externalInstVarofContext(i, aContext); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(cloned)) + && (!(isForwarded(cloned)))); + assert(validStorePointerUncheckedArgs(i, cloned, valuePointer)); + longAtput((void *)((cloned + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + for (i = MethodIndex; i <= ReceiverIndex; i += 1) { + valuePointer = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(cloned)) + && (!(isForwarded(cloned)))); + assert(validStorePointerUncheckedArgs(i, cloned, valuePointer)); + longAtput((void *)((cloned + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + if (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + sp = (stackPointerIndexForFrame(spouseFP)) - 1; + for (i = 0; i <= sp; i += 1) { + fieldIndex = i + CtxtTempFrameStart; + valuePointer = /* temporary:in: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? (i < ((frameNumArgs = ((mframeCogMethod(spouseFP))->cmNumArgs))) + ? longAt((void *)((spouseFP + FoxCallerSavedIP) + ((frameNumArgs - i) * BytesPerWord))) + : longAt((void *)(((spouseFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs - i) * BytesPerWord)))) + : /* itemporary:in: */ + (i < ((frameNumArgsUsqInt = byteAt((spouseFP + FoxIFrameFlags) + 1))) + ? longAt((void *)((spouseFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt - i) * BytesPerWord))) + : longAt((void *)(((spouseFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt - i) * BytesPerWord))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(cloned)) + && (!(isForwarded(cloned)))); + assert(validStorePointerUncheckedArgs(fieldIndex, cloned, valuePointer)); + longAtput((void *)((cloned + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); + } + } + else { + sp = (fetchStackPointerOf(aContext)) - 1; + for (i = 0; i <= sp; i += 1) { + fieldIndex = i + CtxtTempFrameStart; + valuePointer = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)((i + CtxtTempFrameStart)) << (shiftForWord())))))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(cloned)) + && (!(isForwarded(cloned)))); + assert(validStorePointerUncheckedArgs(fieldIndex, cloned, valuePointer)); + longAtput((void *)((cloned + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),valuePointer); + } + } + return cloned; +} + + +/* Arrange to answer naked frame pointers for unmarried + senders to avoid reifying contexts in the search. */ + + /* StackInterpreterPrimitives>>#fieldOrSenderFP:ofContext: */ +static NoDbgRegParms sqInt +fieldOrSenderFPofContext(sqInt index, sqInt contextObj) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt senderOop; + char *spouseFP; + sqInt tempIndex; + + tempIndex = index - CtxtTempFrameStart; + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((contextObj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(contextObj))))) { + if (tempIndex >= (fetchStackPointerOf(contextObj))) { + return GIV(nilObj); + } + else { + return longAt((void *)((contextObj + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + } + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((contextObj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (tempIndex >= (stackPointerIndexForFrame(spouseFP))) { + return GIV(nilObj); + } + return fieldofFrame(index, spouseFP); +} + + +/* Arrange to answer naked frame pointers for unmarried + senders to avoid reifying contexts in the search. */ + + /* StackInterpreterPrimitives>>#field:ofFrame: */ +static NoDbgRegParms sqInt +fieldofFrame(sqInt index, char *theFP) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + + switch (index) { + case SenderIndex: + callerFP = ((char *)(longAt(theFP + FoxSavedFP))); + return (callerFP + ? (/* frameHasContext: */ + ((((usqInt)(longAt(callerFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(callerFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((callerFP + FoxIFrameFlags) + 2)) != 0) + ? (assert(checkIsStillMarriedContextcurrentFP(frameContext(callerFP), null)), + /* frameContext: */ + longAt(callerFP + FoxThisContext)) + : ((sqInt)callerFP)) + : frameCallerContext(theFP)); + + case StackPointerIndex: + case InstructionPointerIndex: + return ConstZero; + + case MethodIndex: + return /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + + case ClosureIndex: + return (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0) + ? frameStackedReceivernumArgs(theFP, /* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1))) + : GIV(nilObj)); + + case ReceiverIndex: + return /* frameReceiver: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? longAt(theFP + FoxMFReceiver) + : longAt(theFP + FoxIFReceiver)); + + default: + assert((((index - CtxtTempFrameStart) >= 0) && ((index - CtxtTempFrameStart) <= (stackPointerIndexForFrame(theFP))))); + return /* temporary:in: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((index - CtxtTempFrameStart) < ((frameNumArgs = ((mframeCogMethod(theFP))->cmNumArgs))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgs - (index - CtxtTempFrameStart)) * BytesPerWord)) + : longAt(((theFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs - (index - CtxtTempFrameStart)) * BytesPerWord))) + : /* itemporary:in: */ + ((index - CtxtTempFrameStart) < ((frameNumArgsUsqInt = byteAt((theFP + FoxIFrameFlags) + 1))) + ? longAt((theFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt - (index - CtxtTempFrameStart)) * BytesPerWord)) + : longAt(((theFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt - (index - CtxtTempFrameStart)) * BytesPerWord)))); + } + return 0; +} + + /* StackInterpreterPrimitives>>#isAppropriateForCopyObject: */ +static NoDbgRegParms sqInt +isAppropriateForCopyObject(sqInt oop) +{ + if (!(((byteAt((void *)(oop + (formatFieldByteOffset())))) & (formatMask())) <= 5 /* lastPointerFormat */)) { + return 0; + } + if (((longAt((void *)(oop))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + return !(/* isStillMarriedContext: */ + (((((longAt((void *)((oop + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(oop)))); + } + + /* Note there is no version in CoInterpreterPrimitives such as + (objectMemory isCompiledMethod: oop) ifTrue: + [^(self methodHasCogMethod: oop) not]. + because isPointersNonImm: excludes compiled methods and the + copy loop in primitiveCopyObject cannot handle compiled methods. */ + return 1; +} + + +/* The function has not been loaded yet. Fetch module and function name. */ + + /* StackInterpreterPrimitives>>#linkExternalCall:errInto: */ +static NoDbgRegParms void +(*linkExternalCallerrInto(sqInt externalCallLiteral, sqInt *failPtr))(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*addr)(); + sqInt fmt; + void *functionAddress; + sqInt functionLength; + sqInt functionName; + sqInt i; + sqInt index; + sqInt metadata; + sqInt moduleLength; + sqInt moduleName; + usqInt numSlots; + + metadata = 0; + moduleName = longAt((void *)((externalCallLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralModuleNameIndex) << (shiftForWord())))))); + if (moduleName == GIV(nilObj)) { + moduleLength = 0; + } + else { + if (!(/* isBytes: */ + ((!(moduleName & (tagMask())))) + && (((byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + if (failPtr) { + failPtr[0] = PrimErrBadMethod; + } + return 0; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + moduleLength = ((((/* begin numSlotsOf: */ + assert((classIndexOf(moduleName)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(moduleName + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(moduleName - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + } + functionName = longAt((void *)((externalCallLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFunctionNameIndex) << (shiftForWord())))))); + if (!(/* isBytes: */ + ((!(functionName & (tagMask())))) + && (((byteAt((void *)(functionName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())))) { + if (failPtr) { + failPtr[0] = PrimErrBadMethod; + } + return 0; + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(functionName + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + functionLength = ((((/* begin numSlotsOf: */ + assert((classIndexOf(functionName)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(functionName + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(functionName - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + + /* Spur needs metadata for the primitive, which is stored in the third slot of the literal. */ + addr = ioLoadExternalFunctionOfLengthFromModuleOfLengthMetadataInto(functionName + BaseHeaderSize, functionLength, moduleName + BaseHeaderSize, moduleLength, (&metadata)); + if (addr) { + assert(((((metadata) >> SpurPrimitiveAccessorDepthShift) >= -1) && (((metadata) >> SpurPrimitiveAccessorDepthShift) <= 5))); + functionAddress = ((void *) addr); + + /* begin addToExternalPrimitiveTable: */ + for (i = GIV(externalPrimitiveTableFirstFreeIndex); i < MaxExternalPrimitiveTableSize; i += 1) { + if (!(externalPrimitiveTable[i])) { + externalPrimitiveTable[i] = functionAddress; + index = (GIV(externalPrimitiveTableFirstFreeIndex) = i + 1); + goto l1; + } + } + + /* if no space left, return zero so it'll looked up again */ + index = 0; + /* end addToExternalPrimitiveTable: */ +l1: + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(externalCallLiteral)) + && (!(isForwarded(externalCallLiteral)))); + assert(validStorePointerUncheckedArgs(ExternalCallLiteralFlagsIndex, externalCallLiteral, (((usqInt)metadata << 3) | 1))); + longAtput((void *)((externalCallLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFlagsIndex) << (shiftForWord()))))),(((usqInt)metadata << 3) | 1)); + } + else { + index = -1; + } + + /* Store the index (or -1 if failure) back in the literal */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(externalCallLiteral)) + && (!(isForwarded(externalCallLiteral)))); + assert(validStorePointerUncheckedArgs(ExternalCallLiteralTargetFunctionIndex, externalCallLiteral, (((usqInt)index << 3) | 1))); + longAtput((void *)((externalCallLiteral + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord()))))),(((usqInt)index << 3) | 1)); + + /* If the function has been successfully loaded cache it */ + rewriteMethodCacheEntryForExternalPrimitiveToFunction((index >= 0 + ? addr + : 0)); + return addr; +} + + +/* Return a shallow copy of the receiver. + Special-case non-single contexts (because of context-to-stack mapping). + Can't fail for contexts cuz of image context instantiation code (sigh). */ +/* because of cloneContext: below */ + + /* StackInterpreterPrimitives>>#primitiveClone */ +static void +primitiveClone(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqInt newCopy; + sqInt rcvr; + + newCopy = 0; + rcvr = longAt(GIV(stackPointer)); + if (((rcvr & (tagMask())) != 0)) { + newCopy = rcvr; + } + else { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + newCopy = cloneContext(rcvr); + } + else { + if ((GIV(argumentCount) == 0) + || (!((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun()))))))) { + newCopy = cloneObject(rcvr); + } + } + if (!newCopy) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + return; + } + } + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newCopy); +} + + +/* Special version of primitiveAt for accessing contexts. + Written to be varargs for use from mirror primitives. */ + + /* StackInterpreterPrimitives>>#primitiveContextAt */ +static void +primitiveContextAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aContext; + sqInt class; + sqInt fixedFields; + sqInt fixedFieldsSqInt; + usqLong fmt; + usqLong fmtUsqLong; + unsigned char frameNumArgs; + usqInt frameNumArgsUsqInt; + sqLong hdr; + sqLong hdrSqLong; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt senderOop; + sqInt sp; + char *spouseFP; + sqInt spSqInt; + sqInt stSize; + sqInt stSizeSqInt; + sqInt totalLength; + sqInt totalLengthSqInt; + sqInt value; + + index = longAt(GIV(stackPointer)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + index = (index >> 3); + aContext = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* Duplicating much of stObject:at:put: here allows stObject:at:put: to omit tests for contexts. */ + hdr = long64At((void *)(aContext)); + if (!((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stObject:at: */ + hdrSqLong = long64At((void *)(aContext)); + fmtUsqLong = (((usqLong)(hdrSqLong)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(aContext + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aContext - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmtUsqLong <= (ephemeronFormat())) { + totalLengthSqInt = numSlots; + goto l7; + } + if (fmtUsqLong >= (firstByteFormat())) { + totalLengthSqInt = ((numSlots << (shiftForWord()))) - (fmtUsqLong & 7); + goto l7; + } + + /* bytes, including CompiledMethod */ + if (fmtUsqLong >= (firstShortFormat())) { + totalLengthSqInt = ((numSlots << ((shiftForWord()) - 1))) - (fmtUsqLong & 3); + goto l7; + } + if (fmtUsqLong >= (firstLongFormat())) { + totalLengthSqInt = ((numSlots << ((shiftForWord()) - 2))) - (fmtUsqLong & 1); + goto l7; + } + if (fmtUsqLong == (sixtyFourBitIndexableFormat())) { + totalLengthSqInt = numSlots; + goto l7; + } + + /* fmt = self forwardedFormat */ + totalLengthSqInt = 0; + /* end lengthOf:baseHeader:format: */ +l7: + if ((fmtUsqLong == (indexablePointersFormat())) + && ((hdrSqLong & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(aContext)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(aContext))); + stSizeSqInt = sp; + goto l5; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSizeSqInt = 0; + goto l5; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + stSizeSqInt = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l5: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSizeSqInt)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))))) { + value = temporaryin(index - 1, frameOfMarriedContext(aContext)); + goto l8; + } + } + if (fmtUsqLong >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(aContext)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLengthSqInt))) { + value = (((usqInt)(byteAt((void *)((aContext + BaseHeaderSize) + (index - 1)))) << 3) | 1); + goto l8; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmtUsqLong >= (sixtyFourBitIndexableFormat())) + || (fmtUsqLong == (arrayFormat()))) { + fixedFieldsSqInt = 0; + goto l6; + } + if (fmtUsqLong < (arrayFormat())) { + fixedFieldsSqInt = totalLengthSqInt; + goto l6; + } + class = fetchClassOfNonImm(aContext); + fixedFieldsSqInt = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l6: + stSizeSqInt = totalLengthSqInt - fixedFieldsSqInt; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSizeSqInt))) { + /* begin subscript:with:format: */ + if (fmtUsqLong <= 5 /* lastPointerFormat */) { + value = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << (shiftForWord())))))); + goto l8; + } + if (fmtUsqLong >= (firstByteFormat())) { + value = (((usqInt)(byteAt((void *)((aContext + BaseHeaderSize) + ((index + fixedFieldsSqInt) - 1)))) << 3) | 1); + goto l8; + } + if (fmtUsqLong >= (firstShortFormat())) { + value = (((usqInt)(((unsigned short) (shortAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 1)))))))) << 3) | 1); + goto l8; + } + if (fmtUsqLong == (sixtyFourBitIndexableFormat())) { + value = positive64BitIntegerFor(long64At((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 3)))))); + goto l8; + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + value = (((usqInt)((((usqInt)(long32At((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + goto l8; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmtUsqLong <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + value = 0; + /* end stObject:at: */ +l8: + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + } + return; + } + + /* might be an instance of a subclass */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext))))) { + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(aContext + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aContext - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l4; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l4; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l4; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l4; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l4: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l3; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l3; + } + class = fetchClassOfNonImm(aContext); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l3: + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l1; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + stSize = (spSqInt >> 3); + /* end fetchStackPointerOf: */ +l1: + if (!(((index >= 1) && (index <= stSize)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* begin subscript:with:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + value = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << (shiftForWord())))))); + goto l2; + } + if (fmt >= (firstByteFormat())) { + value = (((usqInt)(byteAt((void *)((aContext + BaseHeaderSize) + ((index + fixedFields) - 1)))) << 3) | 1); + goto l2; + } + if (fmt >= (firstShortFormat())) { + value = (((usqInt)(((unsigned short) (shortAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))))))) << 3) | 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + value = positive64BitIntegerFor(long64At((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))))); + goto l2; + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + value = (((usqInt)((((usqInt)(long32At((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + /* end subscript:with:format: */ +l2: + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + return; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!(((index >= 1) && (index <= (stackPointerIndexForFrame(spouseFP)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + value = /* temporary:in: */ + ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory()) + ? ((index - 1) < ((frameNumArgs = ((mframeCogMethod(spouseFP))->cmNumArgs))) + ? longAt((spouseFP + FoxCallerSavedIP) + ((frameNumArgs - (index - 1)) * BytesPerWord)) + : longAt(((spouseFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs - (index - 1)) * BytesPerWord))) + : /* itemporary:in: */ + ((index - 1) < ((frameNumArgsUsqInt = byteAt((spouseFP + FoxIFrameFlags) + 1))) + ? longAt((spouseFP + FoxCallerSavedIP) + ((frameNumArgsUsqInt - (index - 1)) * BytesPerWord)) + : longAt(((spouseFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgsUsqInt - (index - 1)) * BytesPerWord)))); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); +} + + +/* Special version of primitiveAtPut for accessing contexts. + Written to be varargs for use from mirror primitives. */ + + /* StackInterpreterPrimitives>>#primitiveContextAtPut */ +static void +primitiveContextAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aContext; + sqInt class; + sqInt fieldIndex; + sqInt fixedFields; + sqInt fixedFieldsSqInt; + usqLong fmt; + usqLong fmtUsqLong; + usqInt frameNumArgs; + unsigned char frameNumArgs1; + sqLong hdr; + sqLong hdrSqLong; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt senderOop; + sqInt signedValueToStore; + sqInt signedValueToStoreSqInt; + sqInt sp; + char *spouseFP; + sqInt spSqInt; + sqInt stSize; + sqInt stSizeSqInt; + sqInt totalLength; + sqInt totalLengthSqInt; + usqLong unsigned64BitValueToStore; + unsigned int unsignedValueToStore; + sqInt value; + + value = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + aContext = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* Duplicating much of stObject:at:put: here allows stObject:at:put: to omit tests for contexts. */ + hdr = long64At((void *)(aContext)); + index = (index >> 3); + if (!((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stObject:at:put: */ + hdrSqLong = long64At((void *)(aContext)); + fmtUsqLong = (((usqLong)(hdrSqLong)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(aContext + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aContext - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmtUsqLong <= (ephemeronFormat())) { + totalLengthSqInt = numSlots; + goto l6; + } + if (fmtUsqLong >= (firstByteFormat())) { + totalLengthSqInt = ((numSlots << (shiftForWord()))) - (fmtUsqLong & 7); + goto l6; + } + + /* bytes, including CompiledMethod */ + if (fmtUsqLong >= (firstShortFormat())) { + totalLengthSqInt = ((numSlots << ((shiftForWord()) - 1))) - (fmtUsqLong & 3); + goto l6; + } + if (fmtUsqLong >= (firstLongFormat())) { + totalLengthSqInt = ((numSlots << ((shiftForWord()) - 2))) - (fmtUsqLong & 1); + goto l6; + } + if (fmtUsqLong == (sixtyFourBitIndexableFormat())) { + totalLengthSqInt = numSlots; + goto l6; + } + + /* fmt = self forwardedFormat */ + totalLengthSqInt = 0; + /* end lengthOf:baseHeader:format: */ +l6: + if ((fmtUsqLong == (indexablePointersFormat())) + && ((hdrSqLong & (classIndexMask())) == ClassMethodContextCompactIndex)) { + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))) { + sp = stackPointerIndexForFrame(frameOfMarriedContext(aContext)); + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(aContext))); + stSizeSqInt = sp; + goto l8; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSizeSqInt = 0; + goto l8; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + stSizeSqInt = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l8: + if ((oopisGreaterThanOrEqualTo(index, 1)) + && ((oopisLessThanOrEqualTo(index, stSizeSqInt)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext)))))) { + temporaryinput(index - 1, frameOfMarriedContext(aContext), value); + goto l9; + } + } + if (fmtUsqLong >= (firstCompiledMethodFormat())) { + if ((oopisGreaterThanOrEqualTo(index, (((literalCountOf(aContext)) + LiteralStart) * BytesPerOop) + 1)) + && (oopisLessThanOrEqualTo(index, totalLengthSqInt))) { + if (((((value) & 7) == 1)) + && ((((usqInt)((signedValueToStore = (value >> 3))))) <= 0xFF)) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((aContext + BaseHeaderSize) + (index - 1)),signedValueToStore); + goto l9; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l9; + } + } + else { + /* begin fixedFieldsOf:format:length: */ + if ((fmtUsqLong >= (sixtyFourBitIndexableFormat())) + || (fmtUsqLong == (arrayFormat()))) { + fixedFieldsSqInt = 0; + goto l5; + } + if (fmtUsqLong < (arrayFormat())) { + fixedFieldsSqInt = totalLengthSqInt; + goto l5; + } + class = fetchClassOfNonImm(aContext); + fixedFieldsSqInt = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l5: + stSizeSqInt = totalLengthSqInt - fixedFieldsSqInt; + if ((oopisGreaterThanOrEqualTo(index, 1)) + && (oopisLessThanOrEqualTo(index, stSizeSqInt))) { + /* begin subscript:with:storing:format: */ + if (fmtUsqLong <= 5 /* lastPointerFormat */) { + fieldIndex = (index + fixedFieldsSqInt) - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, aContext, value)); + assert(isNonImmediate(aContext)); + if (oopisGreaterThanOrEqualTo(aContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),value); + goto l7; + } + if (fmtUsqLong >= (firstByteFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l7; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l7; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((aContext + BaseHeaderSize) + ((index + fixedFieldsSqInt) - 1)),signedValueToStoreSqInt); + goto l7; + } + if (fmtUsqLong >= (firstShortFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l7; + } + signedValueToStoreSqInt = (value >> 3); + if (!((signedValueToStoreSqInt >= 0) + && (signedValueToStoreSqInt <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l7; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 1)))),signedValueToStoreSqInt); + goto l7; + } + if (fmtUsqLong == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l7; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFieldsSqInt) - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l7: + goto l9; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = (fmtUsqLong <= 1 + ? PrimErrBadReceiver + : PrimErrBadIndex); + /* end stObject:at:put: */ +l9: + if (!GIV(primFailCode)) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + } + return; + } + + /* might be an instance of a subclass */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(aContext))))) { + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(aContext + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(aContext - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l3; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l3; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l3; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l3; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l3; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l3: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l2; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l2; + } + class = fetchClassOfNonImm(aContext); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l2: + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + stSize = 0; + goto l1; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(aContext))); + stSize = (spSqInt >> 3); + /* end fetchStackPointerOf: */ +l1: + if (!(((index >= 1) && (index <= stSize)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* begin subscript:with:storing:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + fieldIndex = (index + fixedFields) - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, aContext, value)); + assert(isNonImmediate(aContext)); + if (oopisGreaterThanOrEqualTo(aContext, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(value & (tagMask())))) + && (oopisLessThan(value, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(aContext + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(aContext); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),value); + goto l4; + } + if (fmt >= (firstByteFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStore = (value >> 3); + if (!((signedValueToStore >= 0) + && (signedValueToStore <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((aContext + BaseHeaderSize) + ((index + fixedFields) - 1)),signedValueToStore); + goto l4; + } + if (fmt >= (firstShortFormat())) { + if (!((((value) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + signedValueToStore = (value >> 3); + if (!((signedValueToStore >= 0) + && (signedValueToStore <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 1)))),signedValueToStore); + goto l4; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l4; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(value); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((aContext + BaseHeaderSize) + ((((usqInt)(((index + fixedFields) - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l4: + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + return; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((aContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + spouseFP = ((char *)(senderOop - (smallIntegerTag()))); + if (!(((index >= 1) && (index <= (stackPointerIndexForFrame(spouseFP)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* temporary:in:put: */ + if ((((usqInt)(longAt(spouseFP + FoxMethod)))) < (startOfMemory())) { + /* mtemporary:in:put: */ + longAtput(((index - 1) < ((frameNumArgs1 = ((mframeCogMethod(spouseFP))->cmNumArgs))) + ? (spouseFP + FoxCallerSavedIP) + ((frameNumArgs1 - (index - 1)) * BytesPerWord) + : ((spouseFP + FoxMFReceiver) - BytesPerWord) + ((frameNumArgs1 - (index - 1)) * BytesPerWord)),value); + } + else { + /* itemporary:in:put: */ + if ((index - 1) < ((frameNumArgs = byteAt((spouseFP + FoxIFrameFlags) + 1)))) { + longAtput((spouseFP + FoxCallerSavedIP) + ((frameNumArgs - (index - 1)) * BytesPerWord),value); + } + else { + longAtput(((spouseFP + FoxIFReceiver) - BytesPerWord) + ((frameNumArgs - (index - 1)) * BytesPerWord),value); + } + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); +} + + +/* The core design of the opensmalltalk-vm's closure implementation has as + its rationale + breaking the dependence of a block on the temporaries of any outer + context, principally + the home context. The scheme is explained here: + + http://www.mirandabanda.org/cogblog/2009/01/14/under-cover-contexts-and-the-big-frame-up + Hence when a frame is married the VM *does not* copy temporary variables + into the context + object, copying only arguments (if any). See marryFrame:SP: and + genGetActiveContextLarge:inBlock:. Note that if a married frame and + context are divorced (which occurs when an inst var of a context is + assigned to or when the stack page on which the frame resides is reused + for newer frames) then + the full frame contents are written to the context, bringing it fully + up-to-date so that it becomes + the implementation of the relevant method/block activation, replacing the + frame. + Except in extraordinary circumstances there is no utility in accessing the + temporary variables of a + widowed (returned-from) context. The frame and context are married as soon + as thisContext is + referenced (either explicitly, or by creating a closure, or accessing the + sender of another context). + At this point there is no guarantee that temp vars have their final value, + and so there is no point + preserving them because their values at the time of marriage will very + likely be stale by the time + the context is widowed. N.B. One could imagine having the VM write the + values of frame + temporaries back to the context if it existed, but such a scheme would be + unacceptably slow. + + While I (eem 4/23/2026) have yet to see an example where temps should be + preserved, this + mechanism is provided just in case. It must be invoked after the last + modification of a temporary. + For example, the assignments to the completionStatus temps in + BlockClosure>>#ensure: and + BlockClosure>>#ifCurtailed: (as of 11/18/2025) are accessed from *while + the contexts are still active* + from Context>>#finishIncompleteUnwindsUpTo: and hence if married the VM + will access the temps + from the frame, not the context, and hence up-to-date values will be + answered. + There is a complication. Only methods and full blocks have an easily + accessible temporary count. + The original SqueakV3PlusClosures BlockClosure uses explicit pushNil + bytecodes to establish + block-local temps, and machinery such as + StackToRegisterMappingCogit>>#scanBlock: and + BlockLocalTempCounter to compute the number of temps. Hence this primitive + is actually two, + one that takes no arguments, accesses the temp count from the receiver's + method if possible, + failing if not with the error code PrimErrUnsupported, and one that takes + the temp count as its + argument. */ + + /* StackInterpreterPrimitives>>#primitiveContextPreserveTemps */ +static void +primitiveContextPreserveTemps(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt closureRcvr; + sqInt header; + sqInt methodPointer; + usqInt numArgs; + sqInt numNonArgTemps; + usqInt numTemps; + sqInt receiverCtxt; + sqInt senderOop; + char *theFP; + char *theSP; + + receiverCtxt = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + assert(isContext(receiverCtxt)); + + /* this is required for isWidowedContext: et al to function... */ + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* If the receiver is a single (heap) context then temps are already preserved. In normal use the + receiver would be married (e.g. as in executing thisContext preserveTemps where it would be + the current frame. But the execution simulation machinery could send it to a heap context. */ + if (!(((((longAt((void *)((receiverCtxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1))) { + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + return; + } + + /* If the receiver is widowed then it's too late... */ + if (isWidowedContext(receiverCtxt)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + return; + } + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((receiverCtxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + numArgs = /* frameNumArgs: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFP))->cmNumArgs) + : byteAt((theFP + FoxIFrameFlags) + 1)); + if (GIV(argumentCount)) { + assert((methodArgumentCount()) == 1); + numTemps = longAt(GIV(stackPointer)); + if (!(((((numTemps) & 7) == 1)) + && (((((numTemps = (numTemps >> 3))) >= 0) && (numTemps <= ((LargeContextSlots - ReceiverIndex) - 1)))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + } + else { + /* is this a normal method activation and/or a full block closure activation or not? + If not, fail and allow the image to compute the temp count. */ + if (/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 3)) != 0)) { + closureRcvr = longAt(theFP + ((FoxCallerSavedIP + BytesPerWord) + ((numArgs << (shiftForWord()))))); + + /* begin isVanillaBlockClosure: */ + assert((isPointers(closureRcvr)) + && ((numSlotsOf(closureRcvr)) >= ClosureFirstCopiedValueIndex)); + if (((((longAt((void *)((closureRcvr + BaseHeaderSize) + ((((usqInt)(ClosureStartPCIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + } + + /* N.B. the temp count includes the argument count */ + methodPointer = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP))->methodObject) + : longAt(theFP + FoxMethod)); + + /* begin tempCountOf: */ + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodPointer)); + header = longAt((void *)((methodPointer + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + header = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + numTemps = (((usqInt)(header)) >> MethodHeaderTempCountShift) & 0x3F; + } + numNonArgTemps = numTemps - numArgs; + theSP = (/* frameReceiverLocation: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? theFP + FoxMFReceiver + : theFP + FoxIFReceiver)) - (numNonArgTemps * BytesPerWord); + updateStateOfSpouseContextForFrameWithSP(theFP, theSP); + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* Special version of primitiveSize for accessing contexts. + Written to be varargs for use from mirror primitives. */ + + /* StackInterpreterPrimitives>>#primitiveContextSize */ +static void +primitiveContextSize(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt sp1; + sqInt spSqInt; + sqInt sz; + sqInt totalLength; + + rcvr = longAt(GIV(stackPointer)); + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + if ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* begin stackPointerForMaybeMarriedContext: */ + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + sp1 = stackPointerIndexForFrame(frameOfMarriedContext(rcvr)); + assert((ReceiverIndex + ((sp1 >> 3))) < (lengthOf(rcvr))); + sz = sp1; + goto l3; + } + + /* begin fetchStackPointerOf: */ + spSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((spSqInt) & 7) == 1))) { + sz = 0; + goto l3; + } + assert((ReceiverIndex + ((spSqInt >> 3))) < (lengthOf(rcvr))); + sz = (spSqInt >> 3); + /* end stackPointerForMaybeMarriedContext: */ +l3:; + } + else { + sz = totalLength - fixedFields; + } + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)sz << 3) | 1)); +} + + +/* Set or clear the flag that controls whether modifications of + the Display object are propagated to the underlying + platform's screen. */ + + /* StackInterpreterPrimitives>>#primitiveDeferDisplayUpdates */ +static void +primitiveDeferDisplayUpdates(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt flag; + + if (cannotDeferDisplayUpdates) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + flag = longAt(GIV(stackPointer)); + if (flag == GIV(trueObj)) { + deferDisplayUpdates = 1; + } + else { + if (flag == GIV(falseObj)) { + deferDisplayUpdates = 0; + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; +} + + +/* Simulate an primitiveExternalCall invocation (e.g. for the Debugger). Do + not cache anything. + e.g. ContextPart>>tryNamedPrimitiveIn: aCompiledMethod for: aReceiver + withArgs: arguments */ + + /* StackInterpreterPrimitives>>#primitiveDoNamedPrimitiveWithArgs */ +static void +primitiveDoNamedPrimitiveWithArgs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*addr)(void); + sqInt argumentArray; + usqInt arraySize; + sqInt ccIndex; + usqInt firstBytecode; + sqInt fmt; + sqInt functionLength; + sqInt functionName; + sqInt header; + sqInt index; + sqInt isArray; + sqInt methodArg; + sqInt methodHeader; + sqInt moduleLength; + sqInt moduleName; + usqInt newObj; + usqInt numBytes; + usqInt numSlots; + sqInt primRcvr; + sqInt spec; + sqInt successBoolean; + sqInt top; + sqInt valuePointer; + + GIV(metaAccessorDepth) = -2; + + /* See checkForAndFollowForwardedPrimitiveState */ + argumentArray = longAt(GIV(stackPointer)); + methodArg = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (!((/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && (/* isOopCompiledMethod: */ + ((!(methodArg & (tagMask())))) + && (((byteAt((void *)(methodArg + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -2; + return; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (!(arraySize <= (LargeContextSlots - CtxtTempFrameStart))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -2; + return; + } + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(methodArg)); + header = longAt((void *)((methodArg + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((header) & 7) == 1) + ? header + : (assert((((usqInt)header)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) header))->objectHeader)), + ((((CogMethod *) header))->methodHeader))); + if (!(((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), + /* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) > 2)) { + /* primitiveFailFor: */ + GIV(primFailCode) = -3; + return; + } + spec = longAt((void *)((methodArg + BaseHeaderSize) + (1U << (shiftForWord())))); + + /* first literal */ + + /* begin isInstanceOfClassArray: */ + /* begin is:instanceOf:compactClassIndex: */ + if (((spec & (tagMask())) != 0)) { + isArray = 0; + goto l2; + } + + /* begin isClassOfNonImm:equalTo:compactClassIndex: */ + assert(!(isImmediate(spec))); + ccIndex = (longAt((void *)(spec))) & (classIndexMask()); + isArray = ClassArrayCompactIndex == ccIndex; + /* end isInstanceOfClassArray: */ +l2: + if (!(isArray + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(spec)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(spec + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(spec - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4) + && ((/* primitiveIndexOfMethod:header: */ + (((methodHeader & AlternateHeaderHasPrimFlag) != 0) + ? ((firstBytecode = (methodArg + ((LiteralStart + (((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) * BytesPerOop)) + BaseHeaderSize), + (byteAt((void *)(firstBytecode + 1))) + ((((usqInt)((byteAt((void *)(firstBytecode + 2)))) << 8)))) + : 0)) == PrimNumberExternalCall)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -3; + return; + } + if (!(((((usqInt)(methodHeader)) >> MethodHeaderArgCountShift) & 15) == arraySize)) { + /* primitiveFailFor: */ + GIV(primFailCode) = -2; + return; + } + moduleName = longAt((void *)((spec + BaseHeaderSize) + (0U << (shiftForWord())))); + if (moduleName == GIV(nilObj)) { + moduleLength = 0; + } + else { + successBoolean = /* isBytes: */ + ((!(moduleName & (tagMask())))) + && (((byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())); + + /* begin success: */ + if (!successBoolean) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(moduleName + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + moduleLength = ((((/* begin numSlotsOf: */ + assert((classIndexOf(moduleName)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(moduleName + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(moduleName - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + } + functionName = longAt((void *)((spec + BaseHeaderSize) + (1U << (shiftForWord())))); + successBoolean = /* isBytes: */ + ((!(functionName & (tagMask())))) + && (((byteAt((void *)(functionName + (formatFieldByteOffset())))) & (formatMask())) >= (firstByteFormat())); + + /* begin success: */ + if (!successBoolean) { + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + } + + /* begin numBytesOfBytes: */ + fmt = (byteAt((void *)(functionName + (formatFieldByteOffset())))) & (formatMask()); + assert(fmt >= (firstByteFormat())); + functionLength = ((((/* begin numSlotsOf: */ + assert((classIndexOf(functionName)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(functionName + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(functionName - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) << (shiftForWord()))) - (fmt & 7); + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = -3; + return; + } + addr = ioLoadExternalFunctionOfLengthFromModuleOfLengthMetadataInto(functionName + BaseHeaderSize, functionLength, moduleName + BaseHeaderSize, moduleLength, (&GIV(metaAccessorDepth))); + + /* N.B. the accessor depth is the second byte of the primitive's metadata; + the first byte is various flags (currently l.s.b. = use fast C linkage). */ + GIV(metaAccessorDepth) = (addr + ? (((usqInt)(GIV(metaAccessorDepth))) >> 8) + : -2); + if (!addr) { + /* primitiveFailFor: */ + GIV(primFailCode) = -1; + return; + } + + /* begin eeInstantiateClassIndex:format:numSlots: */ + assert((knownClassAtIndex(ClassArrayCompactIndex)) != GIV(nilObj)); + assert((arrayFormat()) == (instSpecOfClass(knownClassAtIndex(ClassArrayCompactIndex)))); + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + (4 * BytesPerOop); + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateNewSpaceSlots:format:classIndex:"); + GIV(tempOop) = 0; + goto l1; + } + } + long64Atput((void *)(newObj),((((((usqLong) 4)) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + GIV(tempOop) = newObj; + /* end eeInstantiateClassIndex:format:numSlots: */ +l1: + + /* begin popStack */ + valuePointer = (argumentArray = longAt(GIV(stackPointer))); + GIV(stackPointer) += BytesPerWord; + valuePointer = (argumentArray); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(tempOop))) + && (!(isForwarded(GIV(tempOop))))); + assert(validStorePointerUncheckedArgs(0, GIV(tempOop), valuePointer)); + longAtput((void *)((GIV(tempOop) + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + + /* begin popStack */ + valuePointer = (primRcvr = longAt(GIV(stackPointer))); + GIV(stackPointer) += BytesPerWord; + valuePointer = (primRcvr); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(tempOop))) + && (!(isForwarded(GIV(tempOop))))); + assert(validStorePointerUncheckedArgs(1, GIV(tempOop), valuePointer)); + longAtput((void *)((GIV(tempOop) + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + valuePointer = top; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(tempOop))) + && (!(isForwarded(GIV(tempOop))))); + assert(validStorePointerUncheckedArgs(2, GIV(tempOop), valuePointer)); + longAtput((void *)((GIV(tempOop) + BaseHeaderSize) + (2U << (shiftForWord()))),valuePointer); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + valuePointer = top; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(tempOop))) + && (!(isForwarded(GIV(tempOop))))); + assert(validStorePointerUncheckedArgs(3, GIV(tempOop), valuePointer)); + longAtput((void *)((GIV(tempOop) + BaseHeaderSize) + (3U << (shiftForWord()))),valuePointer); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),primRcvr); + GIV(argumentCount) = arraySize; + for (index = 1; index <= arraySize; index += 1) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + } + primitiveFunctionPointer = addr; + dispatchFunctionPointer(addr); + if (GIV(primFailCode)) { + /* begin pop: */ + GIV(stackPointer) += (arraySize + 1) * BytesPerWord; + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((GIV(tempOop) + BaseHeaderSize) + (3U << (shiftForWord()))))); + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((GIV(tempOop) + BaseHeaderSize) + (2U << (shiftForWord()))))); + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((GIV(tempOop) + BaseHeaderSize) + (1U << (shiftForWord()))))); + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((GIV(tempOop) + BaseHeaderSize) + (0U << (shiftForWord()))))); + GIV(argumentCount) = 3; + + /* Must reset primitiveFunctionPointer for checkForAndFollowForwardedPrimitiveState */ + primitiveFunctionPointer = primitiveDoNamedPrimitiveWithArgs; + + /* Hack. A nil prim error code (primErrorCode = 1) is interpreted by the image + as meaning this primitive is not implemented. So to pass back nil as an error + code we use -1 to indicate generic failure. */ + if (GIV(primFailCode) == 1) { + GIV(primFailCode) = -1; + } + } +} + + +/* Implement either ProtoObject>>tryPrimitive: primIndex withArgs: argArray + or Context>>receiver: anObject tryPrimitive: primIndex withArgs: argArray. + If this primitive fails, arrange that its error code is a negative + integer, to + distinguish between this failing and the primitive it invokes failing. */ + + /* StackInterpreterPrimitives>>#primitiveDoPrimitiveWithArgs */ +static void +primitiveDoPrimitiveWithArgs(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt argumentArray; + usqInt arraySize; + sqInt index; + usqInt numSlots; + sqInt primIdx; + sqInt rcvr; + sqInt referent; + sqInt savedNumArgs; + + GIV(metaAccessorDepth) = -2; + + /* See checkForAndFollowForwardedPrimitiveState */ + if (!(((GIV(argumentCount) >= 2) && (GIV(argumentCount) <= 3)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -PrimErrUnsupported; + return; + } + argumentArray = longAt(GIV(stackPointer)); + primIdx = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((/* isArray: */ + ((!(argumentArray & (tagMask())))) + && (((byteAt((void *)(argumentArray + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((((primIdx) & 7) == 1)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -PrimErrBadArgument; + return; + } + + /* begin numSlotsOf: */ + assert((classIndexOf(argumentArray)) > (isForwardedObjectClassIndexPun())); + arraySize = (((numSlots = byteAt((void *)(argumentArray + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(argumentArray - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots); + if (!(arraySize <= (LargeContextSlots - CtxtTempFrameStart))) { + /* primitiveFailFor: */ + GIV(primFailCode) = -PrimErrLimitExceeded; + return; + } + primIdx = (primIdx >> 3); + + /* begin functionPointerFor:inClass: */ + primitiveFunctionPointer = ((void (*)(void)) (((((usqInt)primIdx)) > MaxPrimitiveIndex + ? 0 + : primitiveTable[primIdx]))); + if (!primitiveFunctionPointer) { + primitiveFunctionPointer = primitiveDoPrimitiveWithArgs; + + /* primitiveFailFor: */ + GIV(primFailCode) = -PrimErrBadIndex; + return; + } + + /* Pop primIndex and argArray, then push args in place... */ + if (((savedNumArgs = GIV(argumentCount))) == 3) { + /* actual receiver */ + GIV(tempOop2) = longAt(GIV(stackPointer) + (3 * BytesPerWord)); + + /* receiver for primitive */ + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* begin followForwarded: */ + assert(isUnambiguouslyForwarder(rcvr)); + referent = longAt((void *)((rcvr + BaseHeaderSize) + (0U << (shiftForWord())))); + while (/* isOopForwarded: */ + ((!(referent & (tagMask())))) + && ((!((longAt((void *)(referent))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + referent = longAt((void *)((referent + BaseHeaderSize) + (0U << (shiftForWord())))); + } + rcvr = referent; + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 3 * BytesPerWord),rcvr); + } + else { + /* begin pop: */ + GIV(stackPointer) += 2 * BytesPerWord; + } + + /* ...and receiver if the three arg form */ + GIV(argumentCount) = arraySize; + index = 1; + while (index <= arraySize) { + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),longAt((void *)((argumentArray + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord()))))))); + index += 1; + } + if ((((usqIntptr_t) primitiveFunctionPointer)) <= MaxQuickPrimitiveIndex) { + externalQuickPrimitiveResponse(); + GIV(tempOop2) = 0; + return; + } + + /* We use tempOop instead of pushRemappableOop:/popRemappableOop here because in + the Cogit primitiveEnterCriticalSection, primitiveSignal, primitiveResume et al longjmp back + to either the interpreter or machine code, depending on the process activated. So if we're + executing one of these primitives, control won't actually return here and the matching + popRemappableOop: wouldn't occur, potentially overflowing the remap buffer. + Note that while recursion could occur (nil tryPrimitive: 118 withArgs: #(118 #(110 #()))) + it counts as shooting oneself in the foot. */ + + /* prim might alloc/gc */ + /* Run the primitive (sets primFailCode) */ + GIV(tempOop) = argumentArray; + GIV(metaAccessorDepth) = ((sqInt)((primitiveMetadataTable[primIdx]))) >> SpurPrimitiveAccessorDepthShift; + + /* See checkForAndFollowForwardedPrimitiveState */ + if (!(slowPrimitiveResponse())) { + if (!GIV(tempOop)) { + return; + } + + /* the primitive failed in a recursive invocation. can't fix things with no value... */ + + /* begin pop: */ + GIV(stackPointer) += arraySize * BytesPerWord; + if (savedNumArgs == 3) { + rcvr = longAt(GIV(stackPointer)); + + /* stackTopPut: */ + longAtput(GIV(stackPointer),GIV(tempOop2)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),rcvr); + } + longAtput((GIV(stackPointer) -= BytesPerWord),(((usqInt)primIdx << 3) | 1)); + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(tempOop)); + primitiveFunctionPointer = primitiveDoPrimitiveWithArgs; + GIV(argumentCount) = savedNumArgs; + } + + /* If primitive failed, then restore state for failure code */ + GIV(tempOop) = (GIV(tempOop2) = 0); +} + + +/* Call an external primitive. External primitive methods first literals are + an array of + * The module name (String | Symbol) + * The function name (String | Symbol) + * The session ID (SmallInteger) [OBSOLETE], or in Spur, the metadata + (accessorDepth and flags; Integer)) + * The function index (Integer) in the externalPrimitiveTable + For fast interpreter dispatch in subsequent invocations the + primitiveFunctionPointer in the method cache is rewritten, either to the + function itself, or to zero if the external + function is not found. This allows for fast responses as long as the + method stays in + the cache. The cache rewrite relies on lastMethodCacheProbeWrite which is + set in + addNewMethodToCache:. + Now that the VM flushes function addresses from its tables, the session ID + is obsolete, + but it is kept for backward compatibility. Also, a failed lookup is + reported specially. If a + method has been looked up and not been found, the function address is + stored as -1 + (i.e., the SmallInteger -1 to distinguish from 16rFFFFFFFF which may be + returned from + lookup), and the primitive fails with PrimErrNotFound. + */ +/* because the primitive accesses newMethod's first literal, which is checked + for explicitly in checkForAndFollowForwardedPrimitiveState + */ +/* since call may invoke a callback */ +/* since external primitive linkage is in first literal... */ +/* Check for it being a method for primitiveDoPrimitiveWithArgs. + Fetch the first literal of the method; check its an Array of length 4. + Look at the function index in case it has been loaded before */ + + /* StackInterpreterPrimitives>>#primitiveExternalCall */ +static void +primitiveExternalCall(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + void (*addr)(void); + sqInt index; + sqInt lit; + usqInt numSlots; + sqInt reasonCode; + + if (!((/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && (((literalCountOf(GIV(newMethod))) > 0) + && (((lit = longAt((void *)((GIV(newMethod) + BaseHeaderSize) + (1U << (shiftForWord()))))), + (/* isArray: */ + ((!(lit & (tagMask())))) + && (((byteAt((void *)(lit + (formatFieldByteOffset())))) & (formatMask())) == (arrayFormat()))) + && ((((/* begin numSlotsOf: */ + assert((classIndexOf(lit)) > (isForwardedObjectClassIndexPun())), + (((numSlots = byteAt((void *)(lit + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(lit - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) == 4) + && (((index = longAt((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord()))))))), + (((index) & 7) == 1))))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadMethod; + return; + } + index = (index >> 3); + + /* Check if we have already looked up the function and failed. */ + if (index < 0) { + /* Function address was not found in this session, + Void the primitive function. */ + rewriteMethodCacheEntryForExternalPrimitiveToFunction(0); + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNotFound; + return; + } + + /* Try to call the function directly */ + if ((index > 0) + && (index <= MaxExternalPrimitiveTableSize)) { + addr = externalPrimitiveTable[index - 1]; + if (addr) { + rewriteMethodCacheEntryForExternalPrimitiveToFunction(addr); + + /* begin callExternalPrimitive: */ + primitiveFunctionPointer = addr; + dispatchFunctionPointer(addr); + + /* begin maybeRetryPrimitiveOnFailure */ + if (GIV(primFailCode)) { + retryPrimitiveOnFailure(); + } + return; + } + + /* if we get here, then an index to the external prim was + kept on the ST side although the underlying prim + table was already flushed */ + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNamedInternal; + return; + } + + /* Clean up session id/metadata and external primitive index */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(lit)) + && (!(isForwarded(lit)))); + assert(validStorePointerUncheckedArgs(ExternalCallLiteralFlagsIndex, lit, ConstZero)); + longAtput((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralFlagsIndex) << (shiftForWord()))))),ConstZero); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(lit)) + && (!(isForwarded(lit)))); + assert(validStorePointerUncheckedArgs(ExternalCallLiteralTargetFunctionIndex, lit, ConstZero)); + longAtput((void *)((lit + BaseHeaderSize) + ((((usqInt)(ExternalCallLiteralTargetFunctionIndex) << (shiftForWord()))))),ConstZero); + + /* The function has not been loaded yet. Attempt to link it, cache it, and call it. */ + addr = linkExternalCallerrInto(lit, (&GIV(primFailCode))); + if (!addr) { + assert((fetchPointerofObject(ExternalCallLiteralFlagsIndex, lit)) == ConstZero); + reasonCode = (GIV(primFailCode) + ? GIV(primFailCode) + : PrimErrNotFound); + GIV(primFailCode) = reasonCode; + return; + } + + /* begin callExternalPrimitive: */ + primitiveFunctionPointer = addr; + dispatchFunctionPointer(addr); + + /* begin maybeRetryPrimitiveOnFailure */ + if (GIV(primFailCode)) { + retryPrimitiveOnFailure(); + } +} + + +/* Primitive. Search up the context stack for the next method context marked + for exception handling starting at the receiver. Return nil if none found */ + + /* StackInterpreterPrimitives>>#primitiveFindHandlerContext */ +static void +primitiveFindHandlerContext(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt handlerOrNilOrZero; + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + handlerOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(PrimNumberHandlerMarker, longAt(GIV(stackPointer)), GIV(nilObj)); + if (!handlerOrNilOrZero) { + handlerOrNilOrZero = GIV(nilObj); + } + + /* begin pop:thenPush: */ + longAtput(GIV(stackPointer),handlerOrNilOrZero); +} + + +/* Primitive. Search up the context stack for the next method context marked + for unwind + handling from the receiver up to but not including the argument. Return + nil if none found. + */ + + /* StackInterpreterPrimitives>>#primitiveFindNextUnwindContext */ +static void +primitiveFindNextUnwindContext(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt calleeContext; + sqInt handlerOrNilOrZero; + sqInt senderContext; + sqInt senderOop; + sqInt startContext; + sqInt stopContext; + char *theFP; + char *theFP1; + char *theFPAbove; + sqInt theMethod; + StackPage *thePage; + char *theSP; + + stopContext = longAt(GIV(stackPointer)); + calleeContext = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((stopContext == GIV(nilObj)) + || (/* isContext: */ + ((!(stopContext & (tagMask())))) + && (((longAt((void *)(stopContext))) & (classIndexMask())) == ClassMethodContextCompactIndex)))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* The following should never be true, but developing full blocks, early in September + 2016 we were seeing invalid invocations of this primitive.. Hence the assert: */ + assert(stopContext != calleeContext); + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((calleeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(calleeContext)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((calleeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + if (longAt(theFP + FoxSavedFP)) { + /* begin findMethodWithPrimitive:FromFP:UpToContext: */ + theFP1 = ((char *)(longAt(theFP + FoxSavedFP))); + theFPAbove = ((char *)(longAt(theFP + FoxSavedFP))); + do { + if ((/* frameHasContext: */ + ((((usqInt)(longAt(theFP1 + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP1 + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP1 + FoxIFrameFlags) + 2)) != 0)) + && (stopContext == (longAt(theFP1 + FoxThisContext)))) { + handlerOrNilOrZero = 0; + goto l1; + } + if (!(/* frameIsBlockActivation: */ + ((((usqInt)(longAt(theFP1 + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP1 + FoxMethod)) & MFMethodFlagIsBlockFlag) != 0 + : (byteAt((theFP1 + FoxIFrameFlags) + 3)) != 0))) { + theMethod = /* frameMethodObject: */ + ((((usqInt)(longAt(theFP1 + FoxMethod)))) < (startOfMemory()) + ? ((mframeHomeMethod(theFP1))->methodObject) + : longAt(theFP1 + FoxMethod)); + if ((primitiveIndexOf(theMethod)) == PrimNumberUnwindMarker) { + if (theFP1 == theFPAbove) { + theSP = findSPOfon(theFP1, stackPageFor(theFP1)); + } + else { + /* begin frameCallerStackPointer: */ + assert(!(isBaseFrame(theFPAbove))); + theSP = (theFPAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFPAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFPAbove))->cmNumArgs) + : byteAt((theFPAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + } + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP1 + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP1 + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP1 + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP1))); + handlerOrNilOrZero = longAt(theFP1 + FoxThisContext); + goto l1; + } + handlerOrNilOrZero = marryFrameSP(theFP1, theSP); + goto l1; + } + } + theFPAbove = theFP1; + theFP1 = ((char *)(longAt(theFP1 + FoxSavedFP))); + } while(theFP1 != 0); + + /* begin frameCallerContext: */ + assert(isBaseFrame(theFPAbove)); + thePage = stackPageAtpages(pageIndexFor(theFPAbove), GIV(pages)); + senderContext = longAt((thePage->baseAddress)); + assert(addressCouldBeObj(senderContext)); + assert((senderContext == (nilObject())) + || (isContext(followMaybeForwarded(senderContext)))); + if (!(/* isContext: */ + ((!(senderContext & (tagMask())))) + && (((longAt((void *)(senderContext))) & (classIndexMask())) == ClassMethodContextCompactIndex))) { + handlerOrNilOrZero = GIV(nilObj); + goto l1; + } + handlerOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(PrimNumberUnwindMarker, senderContext, stopContext); + /* end findMethodWithPrimitive:FromFP:UpToContext: */ +l1:; + } + else { + handlerOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(PrimNumberUnwindMarker, frameCallerContext(theFP), stopContext); + } + } + else { + startContext = longAt((void *)((calleeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + if (/* isContext: */ + ((!(startContext & (tagMask())))) + && (((longAt((void *)(startContext))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + handlerOrNilOrZero = findMethodWithPrimitiveFromContextUpToContext(PrimNumberUnwindMarker, startContext, stopContext); + } + else { + handlerOrNilOrZero = 0; + } + } + if (!handlerOrNilOrZero) { + handlerOrNilOrZero = GIV(nilObj); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),handlerOrNilOrZero); +} + + +/* Do a full garbage collection. In SqueakV3ObjectMemory, answer the number + of bytes available (including swap space if dynamic memory management is + supported). In Spur, answer the size of the largest free chunk. */ +/* Do a full garbage collection. In SqueakV3ObjectMemory, answer the number + of bytes available (including swap space if dynamic memory management is + supported). In Spur, answer the size of the largest free chunk. */ + + /* StackInterpreterPrimitives>>#primitiveFullGC */ +static void +primitiveFullGC(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integer; + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + integer = fullGC(); + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); +} + + /* StackInterpreterPrimitives>>#primitiveGetSetFFIExceptionHandling */ +static void +primitiveGetSetFFIExceptionHandling(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt arg; + sqInt integer; + + if (!GIV(argumentCount)) { + integer = (ioCanCatchFFIExceptions() + ? ffiExceptionResponse + : -1); + + /* begin methodReturnInteger: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)integer << 3) | 1)); + return; + } + if (!(ioCanCatchFFIExceptions())) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + if (!(GIV(argumentCount) == 1)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + arg = longAt(GIV(stackPointer)); + if (!(((((arg) & 7) == 1)) + && (((((arg = (arg >> 3))) >= -1) && (arg <= 1))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + ffiExceptionResponse = arg; + + /* begin methodReturnReceiver */ + assert(!((failed()))); + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; +} + + +/* Do a quick, incremental garbage collection and return the number of bytes + immediately available. + (Note: more space may be made available by doing a full garbage + collection. + */ +/* Do a quick, incremental garbage collection and return the number of bytes + immediately available. + (Note: more space may be made available by doing a full garbage + collection. + */ + + /* StackInterpreterPrimitives>>#primitiveIncrementalGC */ +static void +primitiveIncrementalGC(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt integerVal; + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + + /* begin scavengingGC */ + scavengingGCTenuringIf(TenureByAge); + integerVal = ((((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes())) < 0) ? 0 : ((GIV(totalFreeOldSpace) + (GIV(scavengeThreshold) - GIV(freeStart))) - (interpreterAllocationReserveBytes()))); + + /* begin pop:thenPushInteger: */ + longAtput(GIV(stackPointer),(((usqInt)integerVal << 3) | 1)); +} + + +/* because of externalInstVar:ofContext: below */ + + /* StackInterpreterPrimitives>>#primitiveInstVarAt */ +static void +primitiveInstVarAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt totalLength; + sqInt value; + + index = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 1) + && (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + return; + } + index = (index >> 3); + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l3; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l3; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l3; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l3; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l3; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l3: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l2; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l2; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l2: + if (!((index >= 1) + && (index <= fixedFields))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + value = externalInstVarofContext(index - 1, rcvr); + } + else { + /* begin subscript:with:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + value = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << (shiftForWord())))))); + goto l1; + } + if (fmt >= (firstByteFormat())) { + value = (((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + (index - 1)))) << 3) | 1); + goto l1; + } + if (fmt >= (firstShortFormat())) { + value = (((usqInt)(((unsigned short) (shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 1)))))))) << 3) | 1); + goto l1; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + value = positive64BitIntegerFor(long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 3)))))); + goto l1; + } + + /* 32bit-word type objects; for now assume no 64-bit indexable objects */ + value = (((usqInt)((((usqInt)(long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1); + /* end subscript:with:format: */ +l1:; + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); +} + + /* StackInterpreterPrimitives>>#primitiveInstVarAtPut */ +static void +primitiveInstVarAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt class; + sqInt fieldIndex; + sqInt fixedFields; + usqLong fmt; + sqLong hdr; + sqInt index; + sqInt newValue; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + sqInt signedValueToStore; + sqInt totalLength; + usqLong unsigned64BitValueToStore; + unsigned int unsignedValueToStore; + + newValue = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (((!(index & (smallIntegerTag())))) + || ((GIV(argumentCount) > 2) + && (/* isOopForwarded: */ + ((!(rcvr & (tagMask())))) + && ((!((longAt((void *)(rcvr))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + return; + } + index = (index >> 3); + hdr = long64At((void *)(rcvr)); + fmt = (((usqLong)(hdr)) >> (formatShift())) & (formatMask()); + + /* begin lengthOf:baseHeader:format: */ + /* begin lengthOf:format: */ + /* begin numSlotsOfAny: */ + numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))); + numSlots = (numSlotsUsqInt == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if (fmt <= (ephemeronFormat())) { + totalLength = numSlots; + goto l2; + } + if (fmt >= (firstByteFormat())) { + totalLength = ((numSlots << (shiftForWord()))) - (fmt & 7); + goto l2; + } + + /* bytes, including CompiledMethod */ + if (fmt >= (firstShortFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 1))) - (fmt & 3); + goto l2; + } + if (fmt >= (firstLongFormat())) { + totalLength = ((numSlots << ((shiftForWord()) - 2))) - (fmt & 1); + goto l2; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + totalLength = numSlots; + goto l2; + } + + /* fmt = self forwardedFormat */ + totalLength = 0; + /* end lengthOf:baseHeader:format: */ +l2: + + /* begin fixedFieldsOf:format:length: */ + if ((fmt >= (sixtyFourBitIndexableFormat())) + || (fmt == (arrayFormat()))) { + fixedFields = 0; + goto l1; + } + if (fmt < (arrayFormat())) { + fixedFields = totalLength; + goto l1; + } + class = fetchClassOfNonImm(rcvr); + fixedFields = (((longAt((void *)((class + BaseHeaderSize) + ((((usqInt)(InstanceSpecificationIndex) << (shiftForWord()))))))) >> 3)) & ((1U << (fixedFieldsFieldWidth())) - 1); + /* end fixedFieldsOf:format:length: */ +l1: + if (!((index >= 1) + && (index <= fixedFields))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if ((fmt == (indexablePointersFormat())) + && ((hdr & (classIndexMask())) == ClassMethodContextCompactIndex)) { + externalInstVarofContextput(index - 1, rcvr, newValue); + } + else { + /* begin subscript:with:storing:format: */ + if (fmt <= 5 /* lastPointerFormat */) { + fieldIndex = index - 1; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(fieldIndex, rcvr, newValue)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(newValue & (tagMask())))) + && (oopisLessThan(newValue, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(fieldIndex) << (shiftForWord()))))),newValue); + goto l3; + } + if (fmt >= (firstByteFormat())) { + if (!((((newValue) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l3; + } + signedValueToStore = (newValue >> 3); + if (!((signedValueToStore >= 0) + && (signedValueToStore <= 0xFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l3; + } + + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + (index - 1)),signedValueToStore); + goto l3; + } + if (fmt >= (firstShortFormat())) { + if (!((((newValue) & 7) == 1))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l3; + } + signedValueToStore = (newValue >> 3); + if (!((signedValueToStore >= 0) + && (signedValueToStore <= 0xFFFF))) { + GIV(primFailCode) = PrimErrBadArgument; + goto l3; + } + + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 1)))),signedValueToStore); + goto l3; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + unsigned64BitValueToStore = positive64BitValueOf(newValue); + if (!GIV(primFailCode)) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 3)))),unsigned64BitValueToStore); + } + goto l3; + } + + /* 32bit-word type objects */ + unsignedValueToStore = positive32BitValueOf(newValue); + if (!GIV(primFailCode)) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)((index - 1)) << 2)))),unsignedValueToStore); + } + /* end subscript:with:storing:format: */ +l3:; + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); +} + + +/* Primitive. 'Invoke' an object like a function, sending the special message + run: originalSelector with: arguments in: aReceiver. + */ + + /* StackInterpreterPrimitives>>#primitiveInvokeObjectAsMethod */ +static void +primitiveInvokeObjectAsMethod(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt lookupClassTag; + usqInt newObj; + usqInt numBytes; + usqInt runArgs; + sqInt runReceiver; + sqInt tagBits; + sqInt top; + sqInt valuePointer; + + /* begin eeInstantiateClassIndex:format:numSlots: */ + assert((GIV(argumentCount) >= 0) + && ((knownClassAtIndex(ClassArrayCompactIndex)) != GIV(nilObj))); + assert((arrayFormat()) == (instSpecOfClass(knownClassAtIndex(ClassArrayCompactIndex)))); + + /* begin allocateNewSpaceSlots:format:classIndex: */ + if (GIV(argumentCount) >= (numSlotsMask())) { + if (GIV(argumentCount) > 0xFFFFFFFFU) { + runArgs = null; + goto l1; + } + newObj = GIV(freeStart) + BaseHeaderSize; + numBytes = (BaseHeaderSize + BaseHeaderSize) + (GIV(argumentCount) * BytesPerOop); + } + else { + newObj = GIV(freeStart); + numBytes = BaseHeaderSize + ((GIV(argumentCount) < 1 + ? 8 /* allocationUnit */ + : GIV(argumentCount) * BytesPerOop)); + } + if ((GIV(freeStart) + numBytes) > GIV(scavengeThreshold)) { + if (!GIV(needGCFlag)) { + /* begin scheduleScavenge */ + GIV(needGCFlag) = 1; + forceInterruptCheck(); + } + if ((GIV(freeStart) + numBytes) > (((GIV(eden)).limit))) { + error("no room in eden for allocateNewSpaceSlots:format:classIndex:"); + runArgs = 0; + goto l1; + } + } + if (GIV(argumentCount) >= (numSlotsMask())) { + longAtput((void *)(GIV(freeStart)),GIV(argumentCount)); + longAtput((void *)(GIV(freeStart) + 4),((sqInt)((usqInt)((numSlotsMask())) << (numSlotsHalfShift())))); + long64Atput((void *)(newObj),((((((usqLong) (numSlotsMask()))) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + } + else { + long64Atput((void *)(newObj),((((((usqLong) GIV(argumentCount))) << (numSlotsFullShift()))) + ((((usqInt)((arrayFormat())) << (formatShift()))))) + ClassArrayCompactIndex); + } + + /* for header parsing we put a saturated slot count in the prepended overflow size word */ + assert((numBytes % (allocationUnit())) == 0); + assert((newObj % (allocationUnit())) == 0); + GIV(freeStart) += numBytes; + runArgs = newObj; + /* end eeInstantiateClassIndex:format:numSlots: */ +l1: + for (i = (GIV(argumentCount) - 1); i >= 0; i += -1) { + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + valuePointer = top; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(runArgs)) + && (!(isForwarded(runArgs)))); + assert(validStorePointerUncheckedArgs(i, runArgs, valuePointer)); + longAtput((void *)((runArgs + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),valuePointer); + } + + /* begin popStack */ + runReceiver = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + + /* setup send of newMethod run: originalSelector with: runArgs in: runReceiver */ + + /* begin push: */ + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(newMethod)); + longAtput((GIV(stackPointer) -= BytesPerWord),GIV(messageSelector)); + + /* original selector */ + longAtput((GIV(stackPointer) -= BytesPerWord),runArgs); + longAtput((GIV(stackPointer) -= BytesPerWord),runReceiver); + + /* stack is clean here */ + GIV(messageSelector) = longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(SelectorRunWithIn) << (shiftForWord())))))); + GIV(argumentCount) = 3; + lookupClassTag = /* fetchClassTagOf: */ + ((tagBits = GIV(newMethod) & (tagMask())) + ? tagBits + : (longAt((void *)(GIV(newMethod)))) & (classIndexMask())); + findNewMethodInClassTag(lookupClassTag); + executeNewMethod(); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; +} + + +/* Primitive. Answer an Array with the current long-running primitive method + identified by + the heartbeat, the minimum number of milliseconds it was active for, and + the milliseconds + of GC activity there-in, or nil if none. */ + + /* StackInterpreterPrimitives>>#primitiveLongRunningPrimitive */ +#if LRPCheck +EXPORT(void) +primitiveLongRunningPrimitive(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong gcms; + sqInt lrpcm; + sqLong primms; + sqInt result; + + lrpcm = 0; + sqLowLevelMFence(); + + /* Since the longRunningPrimitiveCheckMethod is sampled at + interrupt time be careful to validate it before returning it. */ + if ((GIV(longRunningPrimitiveStopUsecs) > GIV(longRunningPrimitiveStartUsecs)) + && ((((lrpcm = GIV(longRunningPrimitiveCheckMethod)))) + && ((addressCouldBeObj(lrpcm)) + && (((byteAt((void *)(lrpcm + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))))) { + result = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), 3); + primms = ((GIV(longRunningPrimitiveStopUsecs) - GIV(longRunningPrimitiveStartUsecs)) + 500) / 1000; + gcms = (GIV(longRunningPrimitiveGCUsecs) + 500) / 1000; + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(0, result, lrpcm)); + assert(isNonImmediate(result)); + if (oopisGreaterThanOrEqualTo(result, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(lrpcm & (tagMask())))) + && (oopisLessThan(lrpcm, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(result + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(result); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((result + BaseHeaderSize) + (0U << (shiftForWord()))),lrpcm); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(result)) + && (!(isForwarded(result)))); + assert(validStorePointerUncheckedArgs(1, result, (((usqInt)primms << 3) | 1))); + longAtput((void *)((result + BaseHeaderSize) + (1U << (shiftForWord()))),(((usqInt)primms << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(result)) + && (!(isForwarded(result)))); + assert(validStorePointerUncheckedArgs(2, result, ((gcms << 3) | 1))); + longAtput((void *)((result + BaseHeaderSize) + (2U << (shiftForWord()))),((gcms << 3) | 1)); + } + else { + result = GIV(nilObj); + } + + /* a hit */ + voidLongRunningPrimitive("get"); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),result); +} +#endif /* LRPCheck */ + + +/* This primitive is assumed to be fast (see e.g. + MethodDictionary>>includesKey:) so make it so. + N.B. Works correctly for cogged methods too. */ + + /* StackInterpreterPrimitives>>#primitiveObjectPointsTo */ +static void +primitiveObjectPointsTo(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + usqLong fmt; + sqLong header; + sqInt headerSqInt; + sqInt i; + sqInt methodHeader; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + char *sp; + sqInt thang; + sqInt trueOrFalse; + + thang = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (((rcvr & (tagMask())) != 0)) { + /* begin pop:thenPushBool: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),GIV(falseObj)); + return; + } + + /* Inlined version of lastPointerOf: for speed in determining if rcvr is a context. */ + header = long64At((void *)(rcvr)); + fmt = (((usqLong)(header)) >> (formatShift())) & (formatMask()); + if (fmt <= 5 /* lastPointerFormat */) { + if ((fmt == (indexablePointersFormat())) + && ((header & (classIndexMask())) == ClassMethodContextCompactIndex)) { + if (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(rcvr)))) { + trueOrFalse = marriedContextpointsTostackDeltaForCurrentFrame(rcvr, thang, 2); + + /* begin pop:thenPushBool: */ + longAtput((sp = GIV(stackPointer) + (1 * BytesPerWord)),/* booleanObjectOf: */ + (trueOrFalse + ? GIV(trueObj) + : GIV(falseObj))); + GIV(stackPointer) = sp; + return; + } + } + + /* contexts end at the stack pointer */ + numSlots = CtxtTempFrameStart + (fetchStackPointerOf(rcvr)); + } + else { + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + } + } + else { + if (fmt < (firstCompiledMethodFormat())) { + /* begin pop:thenPushBool: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),GIV(falseObj)); + return; + } + + /* no pointers + CompiledMethod: contains both pointers and bytes: */ + + /* begin methodHeaderOf: */ + assert(isCompiledMethod(rcvr)); + headerSqInt = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(HeaderIndex) << (shiftForWord())))))); + methodHeader = ((((headerSqInt) & 7) == 1) + ? headerSqInt + : (assert((((usqInt)headerSqInt)) < GIV(newSpaceStart)), + assert(nullHeaderForMachineCodeMethod() == ((((CogMethod *) headerSqInt))->objectHeader)), + ((((CogMethod *) headerSqInt))->methodHeader))); + if (methodHeader == thang) { + /* begin pop:thenPushBool: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),GIV(trueObj)); + return; + } + numSlots = ((/* begin literalCountOfMethodHeader: */ + assert((((methodHeader) & 7) == 1)), +/* literalCountOfAlternateHeader: */ + ((methodHeader >> 3)) & AlternateHeaderNumLiteralsMask)) + LiteralStart; + } + assert((((numSlots - 1) * BytesPerOop) + BaseHeaderSize) == (lastPointerOf(rcvr))); + for (i = BaseHeaderSize; i <= (((numSlots - 1) * BytesPerOop) + BaseHeaderSize); i += BytesPerOop) { + if ((longAt((void *)(rcvr + i))) == thang) { + /* begin pop:thenPushBool: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),GIV(trueObj)); + return; + } + } + + /* begin pop:thenPushBool: */ + longAtput((GIV(stackPointer) += 1 * BytesPerWord),GIV(falseObj)); +} + + /* StackInterpreterPrimitives>>#primitivePerform */ +static void +primitivePerform(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt i; + sqInt lookupClassTag; + sqInt newReceiver; + usqInt performMethod; + sqInt tagBits; + + performMethod = GIV(newMethod); + GIV(messageSelector) = longAt(GIV(stackPointer) + ((GIV(argumentCount) - 1) * BytesPerWord)); + newReceiver = longAt(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord)); + + /* NOTE: the following lookup may fail and be converted to #doesNotUnderstand:, + so we must adjust argumentCount and slide args now, so that will work. + Slide arguments down over selector */ + GIV(argumentCount) -= 1; + for (i = GIV(argumentCount); i >= 1; i += -1) { + longAtput(GIV(stackPointer) + (i * BytesPerWord),longAt(GIV(stackPointer) + ((i - 1) * BytesPerWord))); + } + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; + lookupClassTag = /* fetchClassTagOf: */ + ((tagBits = newReceiver & (tagMask())) + ? tagBits + : (longAt((void *)(newReceiver))) & (classIndexMask())); + + /* begin sendBreakpoint:receiver: */ + sendBreakpointclassTag(firstFixedFieldOfMaybeImmediate(GIV(messageSelector)), lengthOfMaybeImmediate(GIV(messageSelector)), /* fetchClassTagOf: */ + ((tagBits = newReceiver & (tagMask())) + ? tagBits + : (longAt((void *)(newReceiver))) & (classIndexMask()))); + if (printOnTrace()) { + printActivationNameForSelectorstartClass(GIV(messageSelector), classForClassTag(lookupClassTag)); + cr(); + } + findNewMethodInClassTag(lookupClassTag); + + /* Only test CompiledMethods for argument count - other objects will have to take their chances */ + if (!((/* isOopCompiledMethod: */ + ((!(GIV(newMethod) & (tagMask())))) + && (((byteAt((void *)(GIV(newMethod) + (formatFieldByteOffset())))) & (formatMask())) >= (firstCompiledMethodFormat()))) + && ((argumentCountOf(GIV(newMethod))) == GIV(argumentCount)))) { + /* Slide the args back up (sigh) and re-insert the selector. */ + + /* begin unPop: */ + GIV(stackPointer) -= 1 * BytesPerWord; + for (i = 1; i <= GIV(argumentCount); i += 1) { + longAtput(GIV(stackPointer) + ((i - 1) * BytesPerWord),longAt(GIV(stackPointer) + (i * BytesPerWord))); + } + longAtput(GIV(stackPointer) + (GIV(argumentCount) * BytesPerWord),GIV(messageSelector)); + GIV(argumentCount) += 1; + GIV(newMethod) = performMethod; + + /* Must reset primitiveFunctionPointer for checkForAndFollowForwardedPrimitiveState */ + primitiveFunctionPointer = primitivePerform; + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + executeNewMethod(); + + /* Recursive xeq affects primErrorCode */ + + /* begin initPrimCall */ + GIV(primFailCode) = 0; +} + + +/* Pin or unpin the receiver, i.e. make it immobile or mobile, based on the + argument. Answer whether the object was already pinned. N.B. pinning does + *not* prevent + an object from being garbage collected. */ + + /* StackInterpreterPrimitives>>#primitivePin */ +static void +primitivePin(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt boolean; + sqInt obj; + sqInt wasPinned; + + obj = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if ((((obj & (tagMask())) != 0)) + || ((!((longAt((void *)(obj))) & ((classIndexMask()) - (isForwardedObjectClassIndexPun())))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + boolean = longAt(GIV(stackPointer)); + if (!((boolean == GIV(falseObj)) + || (boolean == GIV(trueObj)))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if ((byteAt((void *)(obj + (formatFieldByteOffset())))) & (1U << (pinnedBitByteShift()))) { + wasPinned = GIV(trueObj); + if (boolean != wasPinned) { + setIsPinnedOfto(obj, 0); + } + } + else { + wasPinned = GIV(falseObj); + if (boolean == GIV(trueObj)) { + if ((/* isContext: */ + ((!(obj & (tagMask())))) + && (((longAt((void *)(obj))) & (classIndexMask())) == ClassMethodContextCompactIndex)) + && (/* isStillMarriedContext: */ + (((((longAt((void *)((obj + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(obj))))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if (!(pinObject(obj))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoMemory; + return; + } + } + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),wasPinned); +} + + +/* Primitive. Indicate the semaphore to be signalled for upon garbage + collection + */ + + /* StackInterpreterPrimitives>>#primitiveSetGCSemaphore */ +EXPORT(void) +primitiveSetGCSemaphore(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt index; + sqInt integerPointer; + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + index = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + index = 0; + } + if (!GIV(primFailCode)) { + GIV(gcSemaphoreIndex) = index; + + /* begin pop: */ + GIV(stackPointer) += GIV(argumentCount) * BytesPerWord; + } +} + + +/* Cause the time semaphore, if one has been registered, to be + signalled when the microsecond clock is greater than or equal to + the given tick value. A tick value of zero turns off timer interrupts. */ + + /* StackInterpreterPrimitives>>#primitiveSignalAtMilliseconds */ +static void +primitiveSignalAtMilliseconds(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqLong deltaMsecs; + sqLong limit; + sqLong msecs; + sqInt msecsObj; + sqInt sema; + + msecsObj = longAt(GIV(stackPointer)); + sema = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + msecs = positive32BitValueOf(msecsObj); + if (!GIV(primFailCode)) { + if (/* isSemaphoreOop: */ + ((!(sema & (tagMask())))) + && (((longAt((void *)(sema))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord())))))))))) { + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheTimerSemaphore, GIV(specialObjectsOop), sema)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(sema & (tagMask())))) + && (oopisLessThan(sema, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord()))))),sema); + deltaMsecs = msecs - ((ioMSecs()) & MillisecondClockMask); + limit = ((usqInt)(MillisecondClockMask)) >> 1; + + /* Handle a roll-over that could happen in between image invocation of ioMSecs and this invocation. + This will limit the maximum relative duration to MillisecondClockMask/2, about 3 days currently. + Every delay longer than that limit may lead to undefined behavior (shorten delay, or no delay at all) */ + if (deltaMsecs > limit) { + deltaMsecs -= MillisecondClockMask; + } + GIV(nextWakeupUsecs) = (deltaMsecs > 0 + ? (ioUTCMicroseconds()) + (deltaMsecs * 1000) + : ioUTCMicroseconds()); + + /* begin pop: */ + GIV(stackPointer) += 2 * BytesPerWord; + return; + } + if (sema == GIV(nilObj)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(specialObjectsOop))) + && (!(isForwarded(GIV(specialObjectsOop))))); + assert(validStorePointerUncheckedArgs(TheTimerSemaphore, GIV(specialObjectsOop), GIV(nilObj))); + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord()))))),GIV(nilObj)); + GIV(nextWakeupUsecs) = 0; + + /* begin pop: */ + GIV(stackPointer) += 2 * BytesPerWord; + return; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; +} + + +/* Cause the time semaphore, if one has been registered, to be + signalled when the microsecond clock is greater than or equal to + the given tick value. A tick value of zero turns off timer interrupts. */ + + /* StackInterpreterPrimitives>>#primitiveSignalAtUTCMicroseconds */ +static void +primitiveSignalAtUTCMicroseconds(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt sema; + usqLong usecs; + sqInt usecsObj; + + usecsObj = longAt(GIV(stackPointer)); + sema = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + usecs = positive64BitValueOf(usecsObj); + if (!GIV(primFailCode)) { + if (/* isSemaphoreOop: */ + ((!(sema & (tagMask())))) + && (((longAt((void *)(sema))) & (classIndexMask())) == (rawHashBitsOf(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassSemaphore) << (shiftForWord())))))))))) { + /* begin splObj:put: */ + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(TheTimerSemaphore, GIV(specialObjectsOop), sema)); + assert(isNonImmediate(GIV(specialObjectsOop))); + if (oopisGreaterThanOrEqualTo(GIV(specialObjectsOop), GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(sema & (tagMask())))) + && (oopisLessThan(sema, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(GIV(specialObjectsOop) + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(GIV(specialObjectsOop)); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord()))))),sema); + GIV(nextWakeupUsecs) = usecs; + + /* begin pop: */ + GIV(stackPointer) += 2 * BytesPerWord; + return; + } + if (sema == GIV(nilObj)) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(GIV(specialObjectsOop))) + && (!(isForwarded(GIV(specialObjectsOop))))); + assert(validStorePointerUncheckedArgs(TheTimerSemaphore, GIV(specialObjectsOop), GIV(nilObj))); + longAtput((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(TheTimerSemaphore) << (shiftForWord()))))),GIV(nilObj)); + GIV(nextWakeupUsecs) = 0; + + /* begin pop: */ + GIV(stackPointer) += 2 * BytesPerWord; + return; + } + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; +} + + +/* Answer a slot in an object. This numbers all slots from 1, ignoring the + distinction between + named and indexed inst vars. In objects with both named and indexed inst + vars, the named + inst vars precede the indexed ones. In non-object indexed objects (objects + that contain + bits, not object references) this primitive answers the raw integral value + at each slot. + e.g. for Strings it answers the character code, not the Character object + at each slot. */ +/* because of externalInstVar:ofContext: below */ + + /* StackInterpreterPrimitives>>#primitiveSlotAt */ +static void +primitiveSlotAt(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt fmtSqInt; + sqInt index; + sqInt numLiveSlots; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt oop; + sqInt rcvr; + sqInt value; + + index = longAt(GIV(stackPointer)); + rcvr = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if ((((usqInt)index)) < numSlots) { + if (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassMethodContextCompactIndex) { + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + numLiveSlots = (stackPointerForMaybeMarriedContext(rcvr)) + CtxtTempFrameStart; + value = ((((usqInt)index)) < numLiveSlots + ? externalInstVarofContext(index, rcvr) + : GIV(nilObj)); + } + else { + value = longAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord())))))); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),value); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + + /* begin numBytesOfBytes: */ + fmtSqInt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert(fmtSqInt >= (firstByteFormat())); + numSlots = ((((/* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) << (shiftForWord()))) - (fmtSqInt & 7); + if ((((usqInt)index)) < numSlots) { + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)(byteAt((void *)((rcvr + BaseHeaderSize) + index))) << 3) | 1)); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt >= (firstShortFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 1; + if ((((usqInt)index)) < numSlots) { + /* begin pop:thenPushInteger: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)(((unsigned short) (shortAt((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 1)))))))) << 3) | 1)); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + oop = positive64BitIntegerFor(long64At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3)))))); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt >= (firstLongFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),(((usqInt)((((usqInt)(long32At((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2)))))))) & 0xFFFFFFFFU) << 3) | 1)); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; +} + + +/* Assign a slot in an object. This numbers all slots from 1, ignoring the + distinction between named and indexed inst vars. In objects with both + named and indexed + inst vars, the named inst vars precede the indexed ones. In pure bits + indexed objects this primitive assigns a raw integral value to a slot. + N.B. *cannot* run on + Smalltalk stack because receiver may be a context. */ + + /* StackInterpreterPrimitives>>#primitiveSlotAtPut */ +static void +primitiveSlotAtPut(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt fmt; + sqInt fmtSqInt; + sqInt index; + sqInt newValue; + usqInt numSlots; + usqInt numSlotsUsqInt; + sqInt rcvr; + usqIntptr_t value; + sqInt valueSqInt; + + newValue = longAt(GIV(stackPointer)); + index = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + rcvr = longAt(GIV(stackPointer) + (2 * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (((rcvr & (tagMask())) != 0)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; + } + if ( +# if IMMUTABILITY + ((((usqInt)((byteAt((void *)(rcvr + (immutableExtraBitsByteOffset())))))) >> (immutableBitByteShift())) & 1) != 0 +# else + 0 +# endif + ) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrNoModification; + return; + } + fmt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + index = ((index >> 3)) - 1; + if (fmt <= 5 /* lastPointerFormat */) { + /* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())); + numSlots = (((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt); + if ((((usqInt)index)) < numSlots) { + if ((fmt == (indexablePointersFormat())) + && (((longAt((void *)(rcvr))) & (classIndexMask())) == ClassMethodContextCompactIndex)) { + externalInstVarofContextput(index, rcvr, newValue); + } + else { + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(index, rcvr, newValue)); + assert(isNonImmediate(rcvr)); + if (oopisGreaterThanOrEqualTo(rcvr, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(newValue & (tagMask())))) + && (oopisLessThan(newValue, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(rcvr); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << (shiftForWord()))))),newValue); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* begin positiveMachineIntegerValueOf: */ + if ((((newValue) & 7) == 1)) { + valueSqInt = (newValue >> 3); + if (valueSqInt < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + value = null; + goto l1; + } + value = valueSqInt; + goto l1; + } + + /* don't inline the rare case */ + value = positiveMachineIntegerValueOfObj(newValue); + /* end positiveMachineIntegerValueOf: */ +l1: + if (GIV(primFailCode)) { + GIV(primFailCode) = PrimErrBadArgument; + return; + } + if (fmt >= (firstByteFormat())) { + if (fmt >= (firstCompiledMethodFormat())) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + return; + } + if ((((usqInt)value)) > 0xFF) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + + /* begin numBytesOfBytes: */ + fmtSqInt = (byteAt((void *)(rcvr + (formatFieldByteOffset())))) & (formatMask()); + assert(fmtSqInt >= (firstByteFormat())); + numSlots = ((((/* begin numSlotsOf: */ + assert((classIndexOf(rcvr)) > (isForwardedObjectClassIndexPun())), +(((numSlotsUsqInt = byteAt((void *)(rcvr + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(rcvr - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlotsUsqInt))) << (shiftForWord()))) - (fmtSqInt & 7); + if ((((usqInt)index)) < numSlots) { + /* storeByte:ofObject:withValue: */ + byteAtput((void *)((rcvr + BaseHeaderSize) + index),value); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt >= (firstShortFormat())) { + if ((((usqInt)value)) > 0xFFFF) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 1; + if ((((usqInt)index)) < numSlots) { + /* storeShort16:ofObject:withValue: */ + shortAtput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 1)))),value); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt >= (firstLongFormat())) { + if ((((usqInt)value)) > 0xFFFFFFFFU) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 2; + if ((((usqInt)index)) < numSlots) { + /* storeLong32:ofObject:withValue: */ + long32Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 2)))),value); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (fmt == (sixtyFourBitIndexableFormat())) { + numSlots = ((usqInt)((numBytesOf(rcvr)))) >> 3; + if ((((usqInt)index)) < numSlots) { + /* storeLong64:ofObject:withValue: */ + long64Atput((void *)((rcvr + BaseHeaderSize) + ((((usqInt)(index) << 3)))),value); + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),newValue); + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadReceiver; + return; +} + + +/* Atomic store into context stackPointer. + Also ensures that any newly accessible cells are initialized to nil */ + + /* StackInterpreterPrimitives>>#primitiveStoreStackp */ +static void +primitiveStoreStackp(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt ctxt; + sqInt i; + sqInt integerPointer; + sqInt newStackp; + usqInt numSlots; + int onCurrentPage; + sqInt senderOop; + sqInt sp; + sqInt stackp; + char *theFP; + StackPage *thePage; + + ctxt = longAt(GIV(stackPointer) + (1 * BytesPerWord)); + + /* begin stackIntegerValue: */ + integerPointer = longAt(GIV(stackPointer)); + if ((((integerPointer) & 7) == 1)) { + newStackp = (integerPointer >> 3); + } + else { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + newStackp = 0; + } + if (!((!GIV(primFailCode)) + && (((newStackp >= 0) && (newStackp <= (((/* begin numSlotsOf: */ + assert((classIndexOf(ctxt)) > (isForwardedObjectClassIndexPun())), +(((numSlots = byteAt((void *)(ctxt + (numSlotsFieldByteOffset()))))) == (numSlotsMask()) + ? ((((usqInt)(((sqInt)((usqInt)((longAt((void *)(ctxt - BaseHeaderSize)))) << 8)))))) >> 8 + : numSlots))) - CtxtTempFrameStart)))))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + + /* begin externalWriteBackHeadFramePointers */ + assert((GIV(framePointer) - GIV(stackPointer)) < (LargeContextSlots * BytesPerOop)); + assert(GIV(stackPage) == (GIV(mostRecentlyUsedPage))); + assert(!((isFree(GIV(stackPage))))); + + /* begin setHeadFP:andSP:inPage: */ + assert(GIV(stackPointer) < GIV(framePointer)); + assert((GIV(stackPointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(stackPointer) > (((GIV(stackPage)->realStackLimit)) - (LargeContextSlots * BytesPerOop)))); + assert((GIV(framePointer) < ((GIV(stackPage)->baseAddress))) + && (GIV(framePointer) > (((GIV(stackPage)->realStackLimit)) - ((LargeContextSlots * BytesPerOop) / 2)))); + (GIV(stackPage)->headFP = GIV(framePointer)); + (GIV(stackPage)->headSP = GIV(stackPointer)); + assert(pageListIsWellFormed()); + if (/* isStillMarriedContext: */ + (((((longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(ctxt)))) { + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFP = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + if (((onCurrentPage = thePage == GIV(stackPage))) + && (theFP == GIV(framePointer))) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + return; + } + externalDivorceFrameandContext(theFP, ctxt); + if (onCurrentPage) { + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (GIV(stackPage)->headSP); + GIV(framePointer) = (GIV(stackPage)->headFP); + } + else { + assert(GIV(stackPage) == (stackPageFor(GIV(framePointer)))); + markStackPageMostRecentlyUsed(GIV(stackPage)); + } + } + + /* begin fetchStackPointerOf: */ + sp = longAt((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord())))))); + if (!((((sp) & 7) == 1))) { + stackp = 0; + goto l1; + } + assert((ReceiverIndex + ((sp >> 3))) < (lengthOf(ctxt))); + stackp = (sp >> 3); + /* end fetchStackPointerOf: */ +l1: + + /* Nil any newly accessible cells */ + for (i = (stackp + 1); i <= newStackp; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs((i + CtxtTempFrameStart) - 1, ctxt, GIV(nilObj))); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(((i + CtxtTempFrameStart) - 1)) << (shiftForWord()))))),GIV(nilObj)); + } + + /* begin storeStackPointerValue:inContext: */ + assert((ReceiverIndex + newStackp) < (lengthOf(ctxt))); + assert((isNonImmediate(ctxt)) + && (!(isForwarded(ctxt)))); + assert(validStorePointerUncheckedArgs(StackPointerIndex, ctxt, (((usqInt)newStackp << 3) | 1))); + longAtput((void *)((ctxt + BaseHeaderSize) + ((((usqInt)(StackPointerIndex) << (shiftForWord()))))),(((usqInt)newStackp << 3) | 1)); + ensureContextIsExecutionSafeAfterAssignToStackPointer(ctxt); + + /* begin pop: */ + GIV(stackPointer) += 1 * BytesPerWord; +} + + +/* Behaviour depends on argument count: + 0 args: return an Array of VM parameter values; + 1 arg: return the indicated VM parameter; + 2 args: set the VM indicated parameter. + VM parameters are numbered as follows: + 1 end (v3)/size(Spur) of old-space (0-based, read-only) + 2 end (v3)/size(Spur) of young/new-space (read-only) + 3 end (v3)/size(Spur) of heap (read-only) + 4 nil (was allocationCount (read-only)) + 5 nil (was allocations between GCs (read-write) + 6 survivor count tenuring threshold (read-write) + 7 full GCs since startup (read-only) + 8 total milliseconds in full GCs since startup (read-only) + 9 incremental GCs (SqueakV3) or scavenges (Spur) since startup (read-only) + 10 total milliseconds in incremental GCs (SqueakV3) or scavenges (Spur) + since startup (read-only) + 11 tenures of surving objects since startup or reset (read-write) + 12-20 were specific to ikp's JITTER VM, now 12 16 open for use + 13 if started, the start time in utc microseconds of the high-priority + ticker 14 if started, the number of checkHighPriorityTickees calls + 15 if started, the number of tickee calls from checkHighPriorityTickees + 16 total microseconds at idle since start-up (if non-zero) + 17 fraction of the code zone to use (Sista only; used to control code zone + use to preserve sendAndBranchData on counter tripped callback) + 18 total milliseconds in compaction phase of full GC since start-up (Spur + only) 19 scavenge threshold, the effective size of eden. When eden fills + to the threshold a scavenge is scheduled. Newer Spur VMs only. + 20 utc microseconds at VM start-up (actually at time initialization, which + precedes image load). + 21 root/remembered table size (occupancy) (read-only) + 22 root table overflows since startup (read-only) + 23 bytes of extra memory to reserve for VM buffers, plugins, etc (stored + in image file header). + 24 memory threshold above which shrinking object memory (rw) + 25 memory headroom when growing object memory (rw) + 26 interruptChecksEveryNms - force an ioProcessEvents every N milliseconds + (rw) 27 number of times mark loop iterated for current IGC/FGC (read-only) + includes ALL marking + 28 number of times sweep loop iterated for current IGC/FGC (read-only) + 29 number of times make forward loop iterated for current IGC/FGC + (read-only) 30 number of times compact move loop iterated for current + IGC/FGC (read-only) + 31 number of grow memory requests (read-only) + 32 number of shrink memory requests (read-only) + 33 number of root table entries used for current IGC/FGC (read-only) + 34 Spur: bytes allocated in total since start-up or reset (read-write) + (Used to be number of allocations done before current IGC/FGC (read-only)) + 35 number of survivor objects after current IGC/FGC (read-only) + 36 millisecond clock when current IGC/FGC completed (read-only) + 37 number of marked objects for Roots of the world, not including Root + Table entries for current IGC/FGC (read-only) + 38 milliseconds taken by current IGC (read-only) + 39 Number of finalization signals for Weak Objects pending when current + IGC/FGC completed (read-only) + 40 BytesPerOop for this image + 41 imageFormatVersion for the VM + 42 number of stack pages in use + 43 desired number of stack pages (stored in image file header, max 65535) + 44 size of eden, in bytes + 45 desired size of eden, in bytes (stored in image file header) + 46 machine code zone size, in bytes (Cog only; otherwise nil) + 47 desired machine code zone size (stored in image file header; Cog only; + otherwise nil) + 48 various header flags. See getImageHeaderFlags. + 49 max size the image promises to grow the external semaphore table to (0 + sets to default, which is 256 as of writing) + + 50 max literal count for JIT compile (stored in image file header; Cog + only; otherwise nil) + 51 nil; reserved for VM parameters that persist in the image (such as eden + above) 52 root/remembered table capacity + 53 number of segments (Spur only; otherwise nil) + 54 total size of free old space (Spur only, otherwise nil) + 55 ratio of growth and image size at or above which a GC will be performed + post scavenge + 56 number of process switches since startup (read-only) + 57 number of ioProcessEvents calls since startup (read-only) + 58 number of ForceInterruptCheck calls since startup (read-only) + 59 number of check event calls since startup (read-only) + 60 number of stack page overflows since startup (read-only) + 61 number of stack page divorces since startup (read-only) + 62 compiled code compactions since startup (read-only; Cog only; otherwise + nil) 63 total milliseconds in compiled code compactions since startup + (read-only; Cog only; otherwise nil) + 64 the number of methods that currently have jitted machine-code + 65 various VM feature flags; see getCogVMFeatureFlags + 66 the byte size of a stack page + 67 the max allowed size of old space (Spur only; nil otherwise; 0 implies + no limit except that of the underlying platform) + 68 the average number of live stack pages when scanned by GC (at + scavenge/gc/become et al) (read-write) + 69 the maximum number of live stack pages when scanned by GC (at + scavenge/gc/become et al) (read-write) + 70 the vmProxyMajorVersion (the interpreterProxy VM_MAJOR_VERSION) + 71 the vmProxyMinorVersion (the interpreterProxy VM_MINOR_VERSION) + 72 total milliseconds in full GCs Mark phase since startup (read-only) + 73 total milliseconds in full GCs Sweep phase since startup (read-only, + can be 0 depending on compactors) + 74 maximum pause time due to segment allocation + 75 whether the arithmetic primitives perform conversion in case of mixed + SmallInteger/Float (true) or fail (false) + 76 the minimum unused headroom in all stack pages; Cog VMs only + 77 whether the numweric comparison primitives perform conversion in case + of mixed SmallInteger/Float (true) or fail (false) + + Note: Thanks to Ian Piumarta for this primitive. */ + + /* StackInterpreterPrimitives>>#primitiveVMParameter */ +static void +primitiveVMParameter(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt aBool; + sqInt activeContext; + double aProportion; + sqIntptr_t arg; + sqInt argOop; + sqLong delta; + sqInt index; + usqInt limit; + int mustFlush; + usqInt oldMixedArithmeticHeaderFlags; + sqInt oop; + sqInt paramsArraySize; + sqInt result; + sqInt resultSqInt; + sqInt senderOop; + sqInt tagBits; + char *theFrame; + StackPage *thePage; + sqInt top; + sqInt value; + sqInt valuePointer; + + paramsArraySize = 77; + if (!GIV(argumentCount)) { + /* begin primitiveAllVMParameters: */ + resultSqInt = instantiateClassindexableSize(longAt((void *)((GIV(specialObjectsOop) + BaseHeaderSize) + ((((usqInt)(ClassArray) << (shiftForWord())))))), paramsArraySize); + valuePointer = positive64BitIntegerFor( + (/* begin oldSpaceSize *//* begin totalOldSpaceSize */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), + GIV(totalHeapSizeIncludingBridges))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(0, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (0U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)((GIV(freeStart) - (((GIV(eden)).start))) + (GIV(pastSpaceStart) - (((GIV(pastSpace)).start)))) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(1, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (1U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor((newSpaceCapacity()) + ((/* begin totalOldSpaceSize */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges)))); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(2, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (2U << (shiftForWord()))),valuePointer); + + /* objectMemory storePointerUnchecked: 3 ofObject: result withValue: objectMemory nilObject was allocationCount + objectMemory storePointerUnchecked: 4 ofObject: result withValue: objectMemory nilObject allocationsBetweenGCs */ + valuePointer = (((usqInt)(((sqInt)(((scavengerTenuringThreshold()) * (((GIV(pastSpace).limit)) - ((GIV(pastSpace).start)))) / (8 * BytesPerOop)))) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(5, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (5U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(6, resultSqInt, (((usqInt)GIV(statFullGCs) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (6U << (shiftForWord()))),(((usqInt)GIV(statFullGCs) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(7, resultSqInt, ((((GIV(statFullGCUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (7U << (shiftForWord()))),((((GIV(statFullGCUsecs) + 500) / 1000) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(8, resultSqInt, (((usqInt)GIV(statScavenges) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (8U << (shiftForWord()))),(((usqInt)GIV(statScavenges) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(9, resultSqInt, ((((GIV(statScavengeGCUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (9U << (shiftForWord()))),((((GIV(statScavengeGCUsecs) + 500) / 1000) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(10, resultSqInt, (((usqInt)GIV(statTenures) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (10U << (shiftForWord()))),(((usqInt)GIV(statTenures) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(11, resultSqInt, (((usqInt)eventTraceMask << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (11U << (shiftForWord()))),(((usqInt)eventTraceMask << 3) | 1)); + valuePointer = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickerStartUSecs()) +# else + ConstZero +# endif + ; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(12, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (12U << (shiftForWord()))),valuePointer); + valuePointer = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickerCount()) +# else + ConstZero +# endif + ; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(13, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (13U << (shiftForWord()))),valuePointer); + valuePointer = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickeeCallCount()) +# else + ConstZero +# endif + ; + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(14, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (14U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statIdleUsecs)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(15, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (15U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(16, resultSqInt, ConstZero)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (16U << (shiftForWord()))),ConstZero); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(17, resultSqInt, ((((GIV(statCompactionUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (17U << (shiftForWord()))),((((GIV(statCompactionUsecs) + 500) / 1000) << 3) | 1)); + valuePointer = (((usqInt)(GIV(scavengeThreshold) - (((GIV(eden)).start))) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(18, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (18U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(ioUTCStartMicroseconds()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(19, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (19U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)(rootTableCount()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(20, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (20U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(21, resultSqInt, (((usqInt)GIV(statRootTableOverflows) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (21U << (shiftForWord()))),(((usqInt)GIV(statRootTableOverflows) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(22, resultSqInt, (((usqInt)extraVMMemory << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (22U << (shiftForWord()))),(((usqInt)extraVMMemory << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(23, resultSqInt, (((usqInt)GIV(shrinkThreshold) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (23U << (shiftForWord()))),(((usqInt)GIV(shrinkThreshold) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(24, resultSqInt, (((usqInt)GIV(growHeadroom) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (24U << (shiftForWord()))),(((usqInt)GIV(growHeadroom) << 3) | 1)); + valuePointer = (((usqInt)(ioHeartbeatMilliseconds()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(25, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (25U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(26, resultSqInt, (((usqInt)GIV(statMarkCount) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (26U << (shiftForWord()))),(((usqInt)GIV(statMarkCount) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(27, resultSqInt, (((usqInt)0 /* statSweepCount */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (27U << (shiftForWord()))),(((usqInt)0 /* statSweepCount */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(28, resultSqInt, (((usqInt)0 /* statMkFwdCount */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (28U << (shiftForWord()))),(((usqInt)0 /* statMkFwdCount */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(29, resultSqInt, (((usqInt)GIV(statCompactPassCount) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (29U << (shiftForWord()))),(((usqInt)GIV(statCompactPassCount) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(30, resultSqInt, (((usqInt)GIV(statGrowMemory) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (30U << (shiftForWord()))),(((usqInt)GIV(statGrowMemory) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(0x1F, resultSqInt, (((usqInt)GIV(statShrinkMemory) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (0x1FU << (shiftForWord()))),(((usqInt)GIV(statShrinkMemory) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(32, resultSqInt, (((usqInt)GIV(statRootTableCount) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (32U << (shiftForWord()))),(((usqInt)GIV(statRootTableCount) << 3) | 1)); + valuePointer = positive64BitIntegerFor(currentAllocatedBytes()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(33, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (33U << (shiftForWord()))),valuePointer); + + /* was statAllocationCount */ + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(34, resultSqInt, (((usqInt)GIV(statSurvivorCount) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (34U << (shiftForWord()))),(((usqInt)GIV(statSurvivorCount) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(35, resultSqInt, ((((GIV(statGCEndUsecs) / 1000) & MillisecondClockMask) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (35U << (shiftForWord()))),((((GIV(statGCEndUsecs) / 1000) & MillisecondClockMask) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(36, resultSqInt, (((usqInt)0 /* statSpecialMarkCount */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (36U << (shiftForWord()))),(((usqInt)0 /* statSpecialMarkCount */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(37, resultSqInt, ((((GIV(statIGCDeltaUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (37U << (shiftForWord()))),((((GIV(statIGCDeltaUsecs) + 500) / 1000) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(38, resultSqInt, (((usqInt)GIV(statPendingFinalizationSignals) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (38U << (shiftForWord()))),(((usqInt)GIV(statPendingFinalizationSignals) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(39, resultSqInt, (((usqInt)BytesPerWord << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (39U << (shiftForWord()))),(((usqInt)BytesPerWord << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(40, resultSqInt, (((usqInt)68021 /* imageFormatVersion */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (40U << (shiftForWord()))),(((usqInt)68021 /* imageFormatVersion */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(41, resultSqInt, (((usqInt)GIV(numStackPages) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (41U << (shiftForWord()))),(((usqInt)GIV(numStackPages) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(42, resultSqInt, (((usqInt)desiredNumStackPages << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (42U << (shiftForWord()))),(((usqInt)desiredNumStackPages << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(43, resultSqInt, (((usqInt)(((GIV(eden).limit)) - ((GIV(eden).start))) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (43U << (shiftForWord()))),(((usqInt)(((GIV(eden).limit)) - ((GIV(eden).start))) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(44, resultSqInt, (((usqInt)desiredEdenBytes << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (44U << (shiftForWord()))),(((usqInt)desiredEdenBytes << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(45, resultSqInt, ((GIV(cogCodeSize) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (45U << (shiftForWord()))),((GIV(cogCodeSize) << 3) | 1)); + valuePointer = getDesiredCogCodeSize(); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(46, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (46U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(47, resultSqInt, (((usqInt)(((usqInt)(imageHeaderFlags)) >> 2) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (47U << (shiftForWord()))),(((usqInt)(((usqInt)(imageHeaderFlags)) >> 2) << 3) | 1)); + valuePointer = (((usqInt)(ioGetMaxExtSemTableSize()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(48, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (48U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(49, resultSqInt, (((usqInt)maxLiteralCountForCompile << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (49U << (shiftForWord()))),(((usqInt)maxLiteralCountForCompile << 3) | 1)); + valuePointer = (((usqInt)(((GIV(oldImageBaseAddress) != GIV(oldSpaceStart) + ? 1 + : 0)) + ((sqImageFileIsEmbedded() + ? 2 + : 0))) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(50, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (50U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)(rootTableCapacity()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(51, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (51U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(52, resultSqInt, (((usqInt)GIV(numSegments) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (52U << (shiftForWord()))),(((usqInt)GIV(numSegments) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(53, resultSqInt, ((GIV(totalFreeOldSpace) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (53U << (shiftForWord()))),((GIV(totalFreeOldSpace) << 3) | 1)); + valuePointer = floatObjectOf(getHeapGrowthToSizeGCRatio()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(54, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (54U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statProcessSwitch)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(55, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (55U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statIOProcessEvents)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(56, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (56U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statForceInterruptCheck)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(57, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (57U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statCheckForEvents)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(58, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (58U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statStackOverflow)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(59, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (59U << (shiftForWord()))),valuePointer); + valuePointer = positive64BitIntegerFor(GIV(statStackPageDivorce)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(60, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (60U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(61, resultSqInt, (((usqInt)GIV(statCodeCompactionCount) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (61U << (shiftForWord()))),(((usqInt)GIV(statCodeCompactionCount) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(0x3E, resultSqInt, ((((GIV(statCodeCompactionUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (0x3EU << (shiftForWord()))),((((GIV(statCodeCompactionUsecs) + 500) / 1000) << 3) | 1)); + valuePointer = (((usqInt)(numMethodsOfType(CMMethod)) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(0x3F, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (0x3FU << (shiftForWord()))),valuePointer); + valuePointer = getCogVMFeatureFlags(); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(64, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (64U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(65, resultSqInt, (((usqInt)GIV(bytesPerPage) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (65U << (shiftForWord()))),(((usqInt)GIV(bytesPerPage) << 3) | 1)); + valuePointer = positive64BitIntegerFor(maxOldSpaceSize); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(66, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (66U << (shiftForWord()))),valuePointer); + valuePointer = floatObjectOf(statAverageLivePagesWhenMapping()); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(67, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (67U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)(GIV(statMaxPageCountWhenMapping)) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(68, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (68U << (shiftForWord()))),valuePointer); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(69, resultSqInt, (((usqInt)VM_PROXY_MAJOR /* vmProxyMajorVersion */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (69U << (shiftForWord()))),(((usqInt)VM_PROXY_MAJOR /* vmProxyMajorVersion */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(70, resultSqInt, (((usqInt)VM_PROXY_MINOR /* vmProxyMinorVersion */ << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (70U << (shiftForWord()))),(((usqInt)VM_PROXY_MINOR /* vmProxyMinorVersion */ << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(71, resultSqInt, ((((GIV(statMarkUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (71U << (shiftForWord()))),((((GIV(statMarkUsecs) + 500) / 1000) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(72, resultSqInt, ((((GIV(statSweepUsecs) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (72U << (shiftForWord()))),((((GIV(statSweepUsecs) + 500) / 1000) << 3) | 1)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(73, resultSqInt, (((usqInt)((GIV(statMaxAllocSegmentTime) + 500) / 1000) << 3) | 1))); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (73U << (shiftForWord()))),(((usqInt)((GIV(statMaxAllocSegmentTime) + 500) / 1000) << 3) | 1)); + aBool = numericPrimsMixArithmetic(); + + /* begin booleanObjectOf: */ + valuePointer = (aBool + ? GIV(trueObj) + : GIV(falseObj)); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(74, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (74U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)(minimumUnusedHeadroom()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(75, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (75U << (shiftForWord()))),valuePointer); + valuePointer = (((usqInt)(numericPrimsMixComparison()) << 3) | 1); + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(resultSqInt)) + && (!(isForwarded(resultSqInt)))); + assert(validStorePointerUncheckedArgs(76, resultSqInt, valuePointer)); + longAtput((void *)((resultSqInt + BaseHeaderSize) + (76U << (shiftForWord()))),valuePointer); + beRootIfOld(resultSqInt); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),resultSqInt); + return; + } + if (GIV(argumentCount) > 2) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadNumArgs; + return; + } + + /* index read & checks */ + index = longAt(GIV(stackPointer) + (((GIV(argumentCount) == 1 + ? 0 + : 1)) * BytesPerWord)); + if (!((((index) & 7) == 1))) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + return; + } + index = (index >> 3); + if ((index < 1) + || (index > paramsArraySize)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadIndex; + return; + } + if (GIV(argumentCount) == 1) { + /* begin primitiveGetVMParameter: */ + switch (index) { + case 1: + result = positive64BitIntegerFor( + (/* begin oldSpaceSize *//* begin totalOldSpaceSize */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), + GIV(totalHeapSizeIncludingBridges))); + goto l1; + break; + case 2: + result = (((usqInt)((GIV(freeStart) - (((GIV(eden)).start))) + (GIV(pastSpaceStart) - (((GIV(pastSpace)).start)))) << 3) | 1); + goto l1; + break; + case 3: + result = positive64BitIntegerFor((newSpaceCapacity()) + ((/* begin totalOldSpaceSize */ + assert((totalBytesInSegments()) == GIV(totalHeapSizeIncludingBridges)), +GIV(totalHeapSizeIncludingBridges)))); + goto l1; + break; + case 6: + result = (((usqInt)(((sqInt)(((scavengerTenuringThreshold()) * (((GIV(pastSpace).limit)) - ((GIV(pastSpace).start)))) / (8 * BytesPerOop)))) << 3) | 1); + goto l1; + break; + case 7: + result = (((usqInt)GIV(statFullGCs) << 3) | 1); + goto l1; + break; + case 8: + result = ((((GIV(statFullGCUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 9: + result = (((usqInt)GIV(statScavenges) << 3) | 1); + goto l1; + break; + case 10: + result = ((((GIV(statScavengeGCUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 11: + result = (((usqInt)GIV(statTenures) << 3) | 1); + goto l1; + break; + case 12: + result = (((usqInt)eventTraceMask << 3) | 1); + goto l1; + break; + case 13: + result = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickerStartUSecs()) +# else + ConstZero +# endif + ; + goto l1; + break; + case 14: + result = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickerCount()) +# else + ConstZero +# endif + ; + goto l1; + break; + case 15: + result = +# if VM_TICKER + positive64BitIntegerFor(ioVMTickeeCallCount()) +# else + ConstZero +# endif + ; + goto l1; + break; + case 16: + result = positive64BitIntegerFor(GIV(statIdleUsecs)); + goto l1; + break; + case 17: + result = ConstZero; + goto l1; + break; + case 18: + result = ((((GIV(statCompactionUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 19: + result = (((usqInt)(GIV(scavengeThreshold) - (((GIV(eden)).start))) << 3) | 1); + goto l1; + break; + case 20: + result = positive64BitIntegerFor(ioUTCStartMicroseconds()); + goto l1; + break; + case 21: + result = (((usqInt)(rootTableCount()) << 3) | 1); + goto l1; + break; + case 22: + result = (((usqInt)GIV(statRootTableOverflows) << 3) | 1); + goto l1; + break; + case 23: + result = (((usqInt)extraVMMemory << 3) | 1); + goto l1; + break; + case 24: + result = (((usqInt)GIV(shrinkThreshold) << 3) | 1); + goto l1; + break; + case 25: + result = (((usqInt)GIV(growHeadroom) << 3) | 1); + goto l1; + break; + case 26: + result = (((usqInt)(ioHeartbeatMilliseconds()) << 3) | 1); + goto l1; + break; + case 27: + result = (((usqInt)GIV(statMarkCount) << 3) | 1); + goto l1; + break; + case 28: + result = (((usqInt)0 /* statSweepCount */ << 3) | 1); + goto l1; + break; + case 29: + result = (((usqInt)0 /* statMkFwdCount */ << 3) | 1); + goto l1; + break; + case 30: + result = (((usqInt)GIV(statCompactPassCount) << 3) | 1); + goto l1; + break; + case 0x1F: + result = (((usqInt)GIV(statGrowMemory) << 3) | 1); + goto l1; + break; + case 32: + result = (((usqInt)GIV(statShrinkMemory) << 3) | 1); + goto l1; + break; + case 33: + result = (((usqInt)GIV(statRootTableCount) << 3) | 1); + goto l1; + break; + case 34: + result = positive64BitIntegerFor(currentAllocatedBytes()); + goto l1; + break; + case 35: + result = (((usqInt)GIV(statSurvivorCount) << 3) | 1); + goto l1; + break; + case 36: + result = ((((GIV(statGCEndUsecs) / 1000) & MillisecondClockMask) << 3) | 1); + goto l1; + break; + case 37: + result = (((usqInt)0 /* statSpecialMarkCount */ << 3) | 1); + goto l1; + break; + case 38: + result = ((((GIV(statIGCDeltaUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 39: + result = (((usqInt)GIV(statPendingFinalizationSignals) << 3) | 1); + goto l1; + break; + case 40: + result = (((usqInt)BytesPerWord << 3) | 1); + goto l1; + break; + case 41: + result = (((usqInt)68021 /* imageFormatVersion */ << 3) | 1); + goto l1; + break; + case 42: + result = (((usqInt)GIV(numStackPages) << 3) | 1); + goto l1; + break; + case 43: + result = (((usqInt)desiredNumStackPages << 3) | 1); + goto l1; + break; + case 44: + result = (((usqInt)(((GIV(eden).limit)) - ((GIV(eden).start))) << 3) | 1); + goto l1; + break; + case 45: + result = (((usqInt)desiredEdenBytes << 3) | 1); + goto l1; + break; + case 46: + result = ((GIV(cogCodeSize) << 3) | 1); + goto l1; + break; + case 47: + result = getDesiredCogCodeSize(); + goto l1; + break; + case 48: + result = (((usqInt)(((usqInt)(imageHeaderFlags)) >> 2) << 3) | 1); + goto l1; + break; + case 49: + result = (((usqInt)(ioGetMaxExtSemTableSize()) << 3) | 1); + goto l1; + break; + case 50: + result = (((usqInt)maxLiteralCountForCompile << 3) | 1); + goto l1; + break; + case 51: + result = (((usqInt)(((GIV(oldImageBaseAddress) != GIV(oldSpaceStart) + ? 1 + : 0)) + ((sqImageFileIsEmbedded() + ? 2 + : 0))) << 3) | 1); + goto l1; + break; + case 52: + result = (((usqInt)(rootTableCapacity()) << 3) | 1); + goto l1; + break; + case 53: + result = (((usqInt)GIV(numSegments) << 3) | 1); + goto l1; + break; + case 54: + result = ((GIV(totalFreeOldSpace) << 3) | 1); + goto l1; + break; + case 55: + result = floatObjectOf(getHeapGrowthToSizeGCRatio()); + goto l1; + break; + case 56: + result = positive64BitIntegerFor(GIV(statProcessSwitch)); + goto l1; + break; + case 57: + result = positive64BitIntegerFor(GIV(statIOProcessEvents)); + goto l1; + break; + case 58: + result = positive64BitIntegerFor(GIV(statForceInterruptCheck)); + goto l1; + break; + case 59: + result = positive64BitIntegerFor(GIV(statCheckForEvents)); + goto l1; + break; + case 60: + result = positive64BitIntegerFor(GIV(statStackOverflow)); + goto l1; + break; + case 61: + result = positive64BitIntegerFor(GIV(statStackPageDivorce)); + goto l1; + break; + case 0x3E: + result = (((usqInt)GIV(statCodeCompactionCount) << 3) | 1); + goto l1; + break; + case 0x3F: + result = ((((GIV(statCodeCompactionUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 64: + result = (((usqInt)(numMethodsOfType(CMMethod)) << 3) | 1); + goto l1; + break; + case 65: + result = getCogVMFeatureFlags(); + goto l1; + break; + case 66: + result = (((usqInt)GIV(bytesPerPage) << 3) | 1); + goto l1; + break; + case 67: + result = positive64BitIntegerFor(maxOldSpaceSize); + goto l1; + break; + case 68: + result = floatObjectOf(statAverageLivePagesWhenMapping()); + goto l1; + break; + case 69: + result = (((usqInt)(GIV(statMaxPageCountWhenMapping)) << 3) | 1); + goto l1; + break; + case 70: + result = (((usqInt)VM_PROXY_MAJOR /* vmProxyMajorVersion */ << 3) | 1); + goto l1; + break; + case 71: + result = (((usqInt)VM_PROXY_MINOR /* vmProxyMinorVersion */ << 3) | 1); + goto l1; + break; + case 72: + result = ((((GIV(statMarkUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 73: + result = ((((GIV(statSweepUsecs) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 74: + result = (((usqInt)((GIV(statMaxAllocSegmentTime) + 500) / 1000) << 3) | 1); + goto l1; + break; + case 75: + aBool = numericPrimsMixArithmetic(); + result = (aBool + ? GIV(trueObj) + : GIV(falseObj)); + goto l1; + break; + case 76: + result = (((usqInt)(minimumUnusedHeadroom()) << 3) | 1); + goto l1; + break; + case 77: + aBool = numericPrimsMixComparison(); + result = (aBool + ? GIV(trueObj) + : GIV(falseObj)); + goto l1; + break; + default: + result = null; + goto l1; + } + /* end primitiveGetVMParameter: */ +l1: + oop = (!(result) + ? GIV(nilObj) + : result); + + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),oop); + return; + } + + /* read VM parameter; written this way to avoid branch limits in V3 bytecode set + written thus to enable Slang inlining of primitiveGetVMParameter: + write a VM parameter */ + argOop = longAt(GIV(stackPointer)); + + /* begin primitiveSetVMParameter:arg: */ + switch (index) { + case 17: + case 55: + case 68: + if (!((/* isFloatInstance: */ + ((tagBits = argOop & (tagMask())) + ? tagBits == (smallFloatTag()) + : ((longAt((void *)(argOop))) & (classIndexMask())) == ClassFloatCompactIndex)) + || ((((argOop) & 7) == 1)))) { + GIV(primFailCode) = PrimErrBadArgument; + } + break; + case 67: + /* begin positiveMachineIntegerValueOf: */ + if ((((argOop) & 7) == 1)) { + value = (argOop >> 3); + if (value < 0) { + /* begin primitiveFail */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = ((usqIntptr_t) null); + goto l2; + } + arg = ((usqIntptr_t) value); + goto l2; + } + + /* don't inline the rare case */ + arg = positiveMachineIntegerValueOfObj(argOop); + /* end positiveMachineIntegerValueOf: */ +l2:; + break; + case 75: + case 77: + /* begin booleanValueOf: */ + if (argOop == GIV(trueObj)) { + arg = 1; + goto l3; + } + if (argOop == GIV(falseObj)) { + arg = 0; + goto l3; + } + + /* begin success: */ + /* Don't overwrite an error code that has already been set. */ + if (!GIV(primFailCode)) { + GIV(primFailCode) = 1; + } + arg = null; + /* end booleanValueOf: */ +l3:; + break; + default: + arg = (argOop >> 3); + } + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + goto l6; + } + + /* assume failure, then set success for handled indices */ + + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + switch (index) { + case 5: + break; + case 6: + resultSqInt = (((usqInt)(((sqInt)(((scavengerTenuringThreshold()) * (((GIV(pastSpace).limit)) - ((GIV(pastSpace).start)))) / (8 * BytesPerOop)))) << 3) | 1); + + /* begin tenuringThreshold: */ + if (arg < 0) { + GIV(primFailCode) = PrimErrBadArgument; + goto l4; + } + aProportion = (((double) (arg * (8 * BytesPerOop)) )) / (((double) (((GIV(pastSpace).limit)) - ((GIV(pastSpace).start))) )); + + /* begin scavengerTenuringThreshold: */ + GIV(tenuringProportion) = aProportion; + GIV(tenureThreshold) = (aProportion == 0.0 + ? 0 + : (round(((((GIV(pastSpace).limit)) - ((GIV(pastSpace).start))) * (1.0 - aProportion)))) + ((GIV(pastSpace).start))); + GIV(primFailCode) = 0; + /* end tenuringThreshold: */ +l4:; + break; + case 12: + resultSqInt = (((usqInt)eventTraceMask << 3) | 1); + eventTraceMask = arg; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + break; + case 11: + if (arg >= 0) { + resultSqInt = (((usqInt)GIV(statTenures) << 3) | 1); + + /* begin statTenures: */ + GIV(statTenures) = arg; + GIV(primFailCode) = 0; + } + break; + case 17: + break; + case 23: + resultSqInt = (((usqInt)extraVMMemory << 3) | 1); + extraVMMemory = arg; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + break; + case 24: + if (arg > 0) { + resultSqInt = (((usqInt)GIV(shrinkThreshold) << 3) | 1); + + /* begin shrinkThreshold: */ + GIV(shrinkThreshold) = arg; + GIV(primFailCode) = 0; + } + break; + case 25: + if (arg > 0) { + resultSqInt = (((usqInt)GIV(growHeadroom) << 3) | 1); + + /* growHeadroom: */ + GIV(growHeadroom) = arg; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + } + break; + case 26: + if (arg >= 0) { + resultSqInt = (((usqInt)(ioHeartbeatMilliseconds()) << 3) | 1); + ioSetHeartbeatMilliseconds(arg); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + } + break; + case 34: + if (arg >= 0) { + resultSqInt = positive64BitIntegerFor(currentAllocatedBytes()); + + /* begin setCurrentAllocatedBytesTo: */ + delta = (currentAllocatedBytes()) - GIV(statAllocatedBytes); + GIV(statAllocatedBytes) = arg; + GIV(oldSpaceUsePriorToScavenge) += delta; + assert((currentAllocatedBytes()) == arg); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + } + break; + case 43: + if (((arg >= 0) && (arg <= 0xFFFF))) { + resultSqInt = (((usqInt)desiredNumStackPages << 3) | 1); + desiredNumStackPages = arg; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + } + break; + case 45: + if (arg >= 0) { + resultSqInt = (((usqInt)desiredEdenBytes << 3) | 1); + desiredEdenBytes = arg; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + } + break; + case 47: + if (((arg >= 0) && (arg <= (maxCogCodeSize())))) { + resultSqInt = (((usqInt)(getDesiredCogCodeSize()) << 3) | 1); + + /* begin setDesiredCogCodeSize: */ + desiredCogCodeSize = arg; + GIV(primFailCode) = 0; + } + break; + case 48: + if (arg >= 0) { + /* answer old value */ + resultSqInt = (((usqInt)(((usqInt)(imageHeaderFlags)) >> 2) << 3) | 1); + + /* must flush/recompile if either of these are changed */ + oldMixedArithmeticHeaderFlags = imageHeaderFlags & 0x900; + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if ((((usqInt)arg)) > 0x3FF) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrUnsupported; + goto l5; + } + imageHeaderFlags = (((imageHeaderFlags | ((0xFFC))) - ((0xFFC)))) | (((((usqIntptr_t)(arg) << 2)))); + /* end setImageHeaderFlags: */ +l5: + if ((GIV(primFailCode) == 0) + && (oldMixedArithmeticHeaderFlags != (imageHeaderFlags & 0x900))) { + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + flushMethodsWithMachineCodePrimitivesAndContinueAnswering(resultSqInt); + goto l6; + } + } + break; + case 49: + if (((arg >= 0) && (arg <= 0xFFFF))) { + resultSqInt = (((usqInt)(ioGetMaxExtSemTableSize()) << 3) | 1); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + GIV(maxExtSemTabSizeSet) = 1; + ioSetMaxExtSemTableSize(arg); + } + break; + case 50: + if (((arg >= 0) && (arg <= 0x7FFF))) { + resultSqInt = (((usqInt)((((usqInt)maxLiteralCountForCompile << 3) | 1)) << 3) | 1); + + /* begin setMaxLiteralCountForCompile: */ + maxLiteralCountForCompile = (GIV(the2ndUnknownShort) = arg); + GIV(primFailCode) = 0; + activeContext = divorceAllFramesSuchThat(isMachineCodeFrameForCogMethodWithTooManyLiterals); + ensureAllContextsHaveBytecodePCsIf(methodHasTooManyLiterals); + unlinkSendsToMethodsSuchThatAndFreeIf(cogMethodHasTooManyLiterals, 1); + + /* If the top frame was divorced then continue in the interpreter. */ + if (!(/* isStillMarriedContext: */ + (((((longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))))) & 7) == 1)) + && (!(isWidowedContext(activeContext))))) { + /* begin nilStackPage */ + assert((!GIV(stackPage)) + || ((((GIV(stackPage)->headFP)) == GIV(framePointer)) + && (((GIV(stackPage)->headSP)) == GIV(stackPointer)))); + GIV(stackPage) = null; + marryContextInNewStackPageAndInitializeInterpreterRegisters(activeContext); + + /* pop bogus machine-code instructionPointer, arguments and receiver */ + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 2) - 1) * BytesPerWord),resultSqInt); + ceInvokeInterpret(); + } + + /* If not, work out where we are and continue */ + + /* begin frameOfMarriedContext: */ + senderOop = longAt((void *)((activeContext + BaseHeaderSize) + ((((usqInt)(SenderIndex) << (shiftForWord())))))); + assert((((senderOop) & 7) == 1)); + theFrame = ((char *)(senderOop - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFrame), GIV(pages)); + assert(((thePage->headFP)) == theFrame); + + /* begin setStackPageAndLimit: */ + assert(thePage); + GIV(stackPage) = thePage; + if (GIV(stackLimit) != (((char *) (((usqInt) -1))))) { + GIV(stackLimit) = (GIV(stackPage)->stackLimit); + } + markStackPageMostRecentlyUsed(thePage); + + /* begin setStackPointersFromPage: */ + GIV(stackPointer) = (thePage->headSP); + GIV(framePointer) = (thePage->headFP); + + /* begin popStack */ + top = longAt(GIV(stackPointer)); + GIV(stackPointer) += BytesPerWord; + GIV(instructionPointer) = top; + if (GIV(instructionPointer) == (ceReturnToInterpreterPC())) { + GIV(instructionPointer) = ((usqInt)(longAt(GIV(framePointer) + FoxIFSavedIP))); + } + + /* begin pop:thenPush: */ + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),resultSqInt); + goto l6; + } + break; + case 55: + resultSqInt = floatObjectOf(getHeapGrowthToSizeGCRatio()); + + /* begin setHeapGrowthToSizeGCRatio: */ + GIV(heapGrowthToSizeGCRatio) = loadFloatOrIntFrom(argOop); + GIV(primFailCode) = 0; + break; + case 67: + if (arg >= 0) { + resultSqInt = positive64BitIntegerFor(maxOldSpaceSize); + limit = arg; + + /* begin setMaxOldSpaceSize: */ + maxOldSpaceSize = limit; + GIV(primFailCode) = 0; + } + break; + case 68: + resultSqInt = floatObjectOf(statAverageLivePagesWhenMapping()); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + if ((loadFloatOrIntFrom(argOop)) == 0.0) { + GIV(statPageCountWhenMappingSum) = (GIV(statNumMaps) = 0); + } + else { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrBadArgument; + } + break; + case 69: + if (arg >= 0) { + resultSqInt = (((usqInt)(GIV(statMaxPageCountWhenMapping)) << 3) | 1); + + /* begin statMaxPageCountWhenMapping: */ + GIV(statMaxPageCountWhenMapping) = arg; + GIV(primFailCode) = 0; + } + break; + case 74: + if (arg >= 0) { + resultSqInt = (((usqInt)((GIV(statMaxAllocSegmentTime) + 500) / 1000) << 3) | 1); + + /* begin statMaxAllocSegmentTime: */ + GIV(statMaxAllocSegmentTime) = arg; + GIV(primFailCode) = 0; + } + break; + case 75: + aBool = numericPrimsMixArithmetic(); + + /* begin booleanObjectOf: */ + resultSqInt = (aBool + ? GIV(trueObj) + : GIV(falseObj)); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + mustFlush = (numericPrimsMixArithmetic()) != arg; + + /* setPrimitiveDoMixedArithmetic: */ + imageHeaderFlags = (arg + ? imageHeaderFlags | 0x100 + : ((imageHeaderFlags | 0x100) - 0x100)); + if (mustFlush) { + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + flushMethodsWithMachineCodePrimitivesAndContinueAnswering(resultSqInt); + goto l6; + } + break; + case 77: + aBool = numericPrimsMixComparison(); + + /* begin booleanObjectOf: */ + resultSqInt = (aBool + ? GIV(trueObj) + : GIV(falseObj)); + + /* begin initPrimCall */ + GIV(primFailCode) = 0; + mustFlush = (numericPrimsMixComparison()) != arg; + + /* setPrimitiveDoMixedNumericComparison: */ + imageHeaderFlags = (arg + ? imageHeaderFlags | 0x800 + : ((imageHeaderFlags | 0x800) - 0x800)); + if (mustFlush) { + /* begin flushMethodCache */ + memset(GIV(methodCache), 0, MethodCacheSize * (sizeof(GIV(methodCache)[0]))); + + /* this for primitiveExternalMethod */ + GIV(lastMethodCacheProbeWrite) = 0; + unlinkAllSends(); + flushMethodsWithMachineCodePrimitivesAndContinueAnswering(resultSqInt); + goto l6; + } + break; + default: +; + } + + /* disable inlining... + disable inlining... + disable inlining... */ + if (GIV(primFailCode)) { + /* primitiveFailFor: */ + GIV(primFailCode) = PrimErrInappropriate; + } + else { + /* begin methodReturnValue: */ + assert(!((failed()))); + longAtput((GIV(stackPointer) += ((GIV(argumentCount) + 1) - 1) * BytesPerWord),resultSqInt); + } + /* end primitiveSetVMParameter:arg: */ +l6:; +} + + +/* Potentially crash the VM by voiding the receiver. A subsequent inst var + access in the caller's frame should indirect through a null pointer. */ + + /* StackInterpreterPrimitives>>#primitiveVoidReceiver */ +EXPORT(void) +primitiveVoidReceiver(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + longAtput(/* frameReceiverLocation: */ + ((((usqInt)(longAt(GIV(framePointer) + FoxMethod)))) < (startOfMemory()) + ? GIV(framePointer) + FoxMFReceiver + : GIV(framePointer) + FoxIFReceiver),0); +} + + +/* Prune the stack to contain only the path, removing stacked indices + and mapping frame pointers to contexts The issue here is that a + GC can occur during ensureFrameIsMarried:SP:, but frame pointers + are not valid objects. So first prune back to objects and framePointers + as integers, and then replace frame pointers as integers by contexts. */ + + /* StackInterpreterPrimitives>>#pruneStack:stackp: */ +static NoDbgRegParms void +pruneStackstackp(sqInt stack, sqInt stackp) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + char *callerFP; + sqInt finger; + char *fp; + sqInt i; + sqInt objOrFP; + sqInt oop; + char *theFP; + char *theFPAbove; + StackPage *thePage; + char *theSP; + sqInt theStack; + sqInt toDoLimit; + + finger = 1; + for (i = 2; i < stackp; i += 2) { + objOrFP = longAt((void *)((stack + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if (/* couldBeFramePointer: */ + (GIV(stackBasePlus1)) + && ((((((usqInt)(((char *) objOrFP)))) & (BytesPerWord - 1)) == 0) + && ((((((usqInt)(((char *) objOrFP)))) >= (((usqInt)(GIV(stackBasePlus1) - 1)))) && ((((usqInt)(((char *) objOrFP)))) <= (((usqInt)GIV(pages)))))))) { + objOrFP += smallIntegerTag(); + } + + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(finger, stack, objOrFP)); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(finger) << (shiftForWord()))))),objOrFP); + finger += 1; + } + toDoLimit = (lengthOf(stack)) - 1; + for (i = finger; i <= toDoLimit; i += 1) { + /* begin storePointerUnchecked:ofObject:withValue: */ + assert((isNonImmediate(stack)) + && (!(isForwarded(stack)))); + assert(validStorePointerUncheckedArgs(i, stack, GIV(nilObj))); + longAtput((void *)((stack + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord()))))),GIV(nilObj)); + } + oop = (theStack = stack); + + /* begin pushRemappableOop: */ + assert(addressCouldBeOop(oop)); + GIV(remapBuffer)[(GIV(remapBufferCount) += 1)] = oop; + if (!(GIV(remapBufferCount) <= RemapBufferSize)) { + error("remapBuffer overflow"); + } + for (i = 1; i < finger; i += 1) { + objOrFP = longAt((void *)((theStack + BaseHeaderSize) + ((((usqInt)(i) << (shiftForWord())))))); + if ((((objOrFP) & 7) == 1)) { + /* begin withoutSmallIntegerTags: */ + assert((((objOrFP) & 7) == 1)); + theFP = ((char *)(objOrFP - (smallIntegerTag()))); + + /* begin stackPageFor: */ + thePage = stackPageAtpages(pageIndexFor(theFP), GIV(pages)); + callerFP = ((char *) 0); + fp = (thePage->headFP); + if (fp == theFP) { + theFPAbove = 0; + goto l1; + } + while (((callerFP = ((char *)(longAt(fp + FoxSavedFP))))) != 0) { + if (callerFP == theFP) { + theFPAbove = fp; + goto l1; + } + fp = callerFP; + } + error("did not find theFP in stack page"); + theFPAbove = 0; + /* end findFrameAbove:inPage: */ +l1: + + /* begin frameCallerSP: */ + assert(!(isBaseFrame(theFPAbove))); + theSP = (theFPAbove + ((FoxCallerSavedIP + BytesPerWord) + ((((usqInt)((/* frameNumArgs: */ + ((((usqInt)(longAt(theFPAbove + FoxMethod)))) < (startOfMemory()) + ? ((mframeCogMethod(theFPAbove))->cmNumArgs) + : byteAt((theFPAbove + FoxIFrameFlags) + 1)))) << (shiftForWord())))))) + BytesPerWord; + + /* begin ensureFrameIsMarried:SP: */ + if (/* frameHasContext: */ + ((((usqInt)(longAt(theFP + FoxMethod)))) < (startOfMemory()) + ? ((longAt(theFP + FoxMethod)) & MFMethodFlagHasContextFlag) != 0 + : (byteAt((theFP + FoxIFrameFlags) + 2)) != 0)) { + assert(isContext(frameContext(theFP))); + objOrFP = longAt(theFP + FoxThisContext); + goto l2; + } + objOrFP = marryFrameSP(theFP, theSP); + /* end ensureFrameIsMarried:SP: */ +l2: + theStack = GIV(remapBuffer)[GIV(remapBufferCount)]; + + /* after a GC stack may no longer be a root. */ + + /* begin storePointer:ofObject:withValue: */ + assert(validStorePointerArgs(finger, theStack, objOrFP)); + assert(isNonImmediate(theStack)); + if (oopisGreaterThanOrEqualTo(theStack, GIV(oldSpaceStart))) { + if (/* isYoung: */ + ((!(objOrFP & (tagMask())))) + && (oopisLessThan(objOrFP, GIV(oldSpaceStart)))) { + /* begin possibleRootStoreInto: */ + if (!((byteAt((void *)(theStack + (formatFieldByteOffset())))) & (1U << (rememberedBitByteShift())))) { + remember(theStack); + } + } + } + + /* most stores into young objects */ + longAtput((void *)((theStack + BaseHeaderSize) + ((((usqInt)(finger) << (shiftForWord()))))),objOrFP); + } + } + + /* begin popRemappableOop */ + oop = GIV(remapBuffer)[GIV(remapBufferCount)]; + GIV(remapBufferCount) -= 1; +} + + /* StackInterpreterPrimitives>>#unmarkAfterPathTo */ +static void +unmarkAfterPathTo(void) +{ DECL_MAYBE_SQ_GLOBAL_STRUCT + sqInt followingWord; + usqInt followingWordAddress; + usqInt numSlots; + sqInt objOop; + sqInt prevObj; + sqInt prevPrevObj; + usqInt start; + + unmarkAllFrames(); + + /* begin unmarkAllObjects */ + /* begin allHeapEntitiesDo: */ + /* begin allOldSpaceEntitiesDo: */ + /* begin allOldSpaceEntitiesFrom:do: */ + assert(isOldObject(GIV(nilObj))); + prevPrevObj = (prevObj = null); + objOop = GIV(nilObj); + while (1) { + assert((objOop % (allocationUnit())) == 0); + if (!(oopisLessThan(objOop, GIV(endOfMemory)))) break; + assert((long64At((void *)(objOop))) != 0); + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + else { + if (!(((longAt((void *)(objOop))) & (classIndexMask())) == (segmentBridgePun()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfter:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(endOfMemory))) { + objOop = GIV(endOfMemory); + goto l1; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? followingWordAddress + BaseHeaderSize + : followingWordAddress); + /* end objectAfter:limit: */ +l1:; + } + + /* begin allNewSpaceEntitiesDo: */ + prevPrevObj = (prevObj = null); + + /* After a scavenge eden is empty, futureSpace is empty, and all newSpace objects are + in pastSpace. Objects are allocated in eden. So enumerate only pastSpace and eden. */ + assert((((GIV(pastSpace)).start)) < (((GIV(eden)).start))); + start = /* startAddressForBridgedHeapEnumeration */ + (GIV(pastSpaceStart) > (((GIV(pastSpace)).start)) + ? ((GIV(pastSpace)).start) + : (GIV(freeStart) > (((GIV(eden)).start)) + ? ((GIV(eden)).start) + : GIV(oldSpaceStart))); + if (start > GIV(freeStart)) { + goto l3; + } + + /* begin bridgePastSpaceAndEden */ + if (GIV(pastSpaceStart) < (((GIV(eden)).start))) { + if ((GIV(pastSpaceStart) + BaseHeaderSize) == (((GIV(eden)).start))) { + hackSlimBridgeToat(objectStartingAt(((GIV(eden)).start)), GIV(pastSpaceStart)); + + /* And carefully check the assumption */ + assert((objectAfterMaybeSlimBridgelimit(objectInPastSpaceBefore(GIV(pastSpaceStart)), GIV(nilObj))) == (objectStartingAt(((GIV(eden)).start)))); + } + else { + initSegmentBridgeWithBytesat((((GIV(eden)).start)) - GIV(pastSpaceStart), GIV(pastSpaceStart)); + } + } + + /* begin objectStartingAt: */ + numSlots = byteAt((void *)(start + (numSlotsFieldByteOffset()))); + objOop = (numSlots == (numSlotsMask()) + ? start + BaseHeaderSize + : start); + while (oopisLessThan(objOop, GIV(freeStart))) { + if ((byteAt((void *)(objOop + (markBitsByteOffset())))) & (1U << (markedBitByteShift()))) { + if (((longAt((void *)(objOop))) & (classIndexMask())) > (lastClassIndexPun())) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + else { + if (!(((longAt((void *)(objOop))) & (classIndexMask())) == (segmentBridgePun()))) { + /* begin setIsMarkedOf:to: */ + assert(!(isFreeObject(objOop))); + byteAtput((void *)(objOop + (markBitsByteOffset())),(byteAt((void *)(objOop + (markBitsByteOffset())))) & (0xFF - (1U << (markedBitByteShift())))); + } + } + } + prevPrevObj = prevObj; + prevObj = objOop; + + /* begin objectAfterMaybeSlimBridge:limit: */ + followingWordAddress = addressAfter(objOop); + if (oopisGreaterThanOrEqualTo(followingWordAddress, GIV(freeStart))) { + objOop = GIV(freeStart); + goto l2; + } + followingWord = longAt((void *)(followingWordAddress)); + objOop = ((((usqInt)(followingWord)) >> (numSlotsFullShift())) == (numSlotsMask()) + ? ((oopisLessThan(objOop, GIV(oldSpaceStart))) + && ((followingWord & 0xFFFFFFFFFFFFFFLL) == 1) + ? (followingWordAddress + BaseHeaderSize) + BaseHeaderSize + : followingWordAddress + BaseHeaderSize) + : followingWordAddress); + /* end objectAfterMaybeSlimBridge:limit: */ +l2:; + } + /* end allNewSpaceEntitiesDo: */ +l3:; +} + +/*** Exports ***/ + +static char _m[] = ""; +void* vm_exports[][3] = { + {(void*)_m, "moduleUnloaded", (void*)moduleUnloaded}, + {(void*)_m, "primitiveAddLargeIntegers\000\000\001", (void*)primitiveAddLargeIntegers}, + {(void*)_m, "primitiveAllInstances\000\000\000", (void*)primitiveAllInstances}, +#if VMInvestigations + {(void*)_m, "primitiveAllMethodsCompiledToMachineCode\000\377\000", (void*)primitiveAllMethodsCompiledToMachineCode}, +#endif /* VMInvestigations */ + {(void*)_m, "primitiveAllObjects\000\377\000", (void*)primitiveAllObjects}, +#if VMInvestigations + {(void*)_m, "primitiveBenchmarkFollowForwardersInStackZone\000\377\000", (void*)primitiveBenchmarkFollowForwardersInStackZone}, +#endif /* VMInvestigations */ +#if VMInvestigations + {(void*)_m, "primitiveBenchmarkFollowForwardersOfReceiverAndTemporariesInStackZone\000\377\000", (void*)primitiveBenchmarkFollowForwardersOfReceiverAndTemporariesInStackZone}, +#endif /* VMInvestigations */ +#if VMInvestigations + {(void*)_m, "primitiveBenchmarkJITReceiver\000\000\000", (void*)primitiveBenchmarkJITReceiver}, +#endif /* VMInvestigations */ +#if VMInvestigations + {(void*)_m, "primitiveBenchmarkScavenge\000\377\000", (void*)primitiveBenchmarkScavenge}, +#endif /* VMInvestigations */ + {(void*)_m, "primitiveBitAndLargeIntegers\000\000\001", (void*)primitiveBitAndLargeIntegers}, + {(void*)_m, "primitiveBitOrLargeIntegers\000\000\001", (void*)primitiveBitOrLargeIntegers}, + {(void*)_m, "primitiveBitShiftLargeIntegers\000\000\001", (void*)primitiveBitShiftLargeIntegers}, + {(void*)_m, "primitiveBitXorLargeIntegers\000\000\001", (void*)primitiveBitXorLargeIntegers}, + {(void*)_m, "primitiveBytecodeSetsAvailable\000\377\000", (void*)primitiveBytecodeSetsAvailable}, + {(void*)_m, "primitiveClockLogAddresses\000\000\000", (void*)primitiveClockLogAddresses}, +#if VMInvestigations + {(void*)_m, "primitiveCogitMethod\000\000\000", (void*)primitiveCogitMethod}, +#endif /* VMInvestigations */ +#if VMInvestigations + {(void*)_m, "primitiveCogStaticSymbols\000\377\000", (void*)primitiveCogStaticSymbols}, +#endif /* VMInvestigations */ + {(void*)_m, "primitiveCompareBytes\000\001\001", (void*)primitiveCompareBytes}, + {(void*)_m, "primitiveCompareWith\000\001\000", (void*)primitiveCompareWith}, +#if VMInvestigations + {(void*)_m, "primitiveContextXray\000\001\000", (void*)primitiveContextXray}, +#endif /* VMInvestigations */ + {(void*)_m, "primitiveCrashVM\000\000\000", (void*)primitiveCrashVM}, + {(void*)_m, "primitiveDisablePowerManager\000\000\000", (void*)primitiveDisablePowerManager}, + {(void*)_m, "primitiveDivideLargeIntegers\000\000\001", (void*)primitiveDivideLargeIntegers}, + {(void*)_m, "primitiveDivLargeIntegers\000\000\001", (void*)primitiveDivLargeIntegers}, + {(void*)_m, "primitiveEqualLargeIntegers\000\000\001", (void*)primitiveEqualLargeIntegers}, + {(void*)_m, "primitiveEventProcessingControl\000\000\000", (void*)primitiveEventProcessingControl}, + {(void*)_m, "primitiveExternalCallNoOp\000\377\000", (void*)primitiveExternalCallNoOp}, + {(void*)_m, "primitiveFastCNoOp\000\377\001", (void*)primitiveFastCNoOp}, + {(void*)_m, "primitiveFastCNoOpAlignedForFloats\000\377\003", (void*)primitiveFastCNoOpAlignedForFloats}, + {(void*)_m, "primitiveGetenv\000\000\000", (void*)primitiveGetenv}, + {(void*)_m, "primitiveGetLogDirectory\000\377\000", (void*)primitiveGetLogDirectory}, + {(void*)_m, "primitiveGetOwnerLog\000\001\000", (void*)primitiveGetOwnerLog}, + {(void*)_m, "primitiveGreaterOrEqualLargeIntegers\000\000\001", (void*)primitiveGreaterOrEqualLargeIntegers}, + {(void*)_m, "primitiveGreaterThanLargeIntegers\000\000\001", (void*)primitiveGreaterThanLargeIntegers}, + {(void*)_m, "primitiveHeartbeatFrequency\000\000\000", (void*)primitiveHeartbeatFrequency}, + {(void*)_m, "primitiveHighResClock\000\377\001", (void*)primitiveHighResClock}, + {(void*)_m, "primitiveImageFormatVersion\000\377\000", (void*)primitiveImageFormatVersion}, + {(void*)_m, "primitiveInterruptChecksPerMSec\000\377\000", (void*)primitiveInterruptChecksPerMSec}, + {(void*)_m, "primitiveIsBigEnder\000\377\000", (void*)primitiveIsBigEnder}, + {(void*)_m, "primitiveLessOrEqualLargeIntegers\000\000\001", (void*)primitiveLessOrEqualLargeIntegers}, + {(void*)_m, "primitiveLessThanLargeIntegers\000\000\001", (void*)primitiveLessThanLargeIntegers}, +#if LRPCheck + {(void*)_m, "primitiveLongRunningPrimitive\000\377\000", (void*)primitiveLongRunningPrimitive}, +#endif /* LRPCheck */ +#if LRPCheck + {(void*)_m, "primitiveLongRunningPrimitiveSemaphore\000\000\000", (void*)primitiveLongRunningPrimitiveSemaphore}, +#endif /* LRPCheck */ +#if VMInvestigations + {(void*)_m, "primitiveMethodIsCogged\000\000\000", (void*)primitiveMethodIsCogged}, +#endif /* VMInvestigations */ + {(void*)_m, "primitiveMethodPCData\000\000\000", (void*)primitiveMethodPCData}, + {(void*)_m, "primitiveMillisecondClockMask\000\377\000", (void*)primitiveMillisecondClockMask}, + {(void*)_m, "primitiveMinimumUnusedHeadroom\000\377\000", (void*)primitiveMinimumUnusedHeadroom}, + {(void*)_m, "primitiveModLargeIntegers\000\000\001", (void*)primitiveModLargeIntegers}, + {(void*)_m, "primitiveMultipleBytecodeSetsActive\000\000\000", (void*)primitiveMultipleBytecodeSetsActive}, + {(void*)_m, "primitiveMultiplyLargeIntegers\000\000\001", (void*)primitiveMultiplyLargeIntegers}, + {(void*)_m, "primitiveNotEqualLargeIntegers\000\000\001", (void*)primitiveNotEqualLargeIntegers}, + {(void*)_m, "primitivePathToUsing\000\000\000", (void*)primitivePathToUsing}, + {(void*)_m, "primitiveProcessBoundThreadId\000\000\000", (void*)primitiveProcessBoundThreadId}, + {(void*)_m, "primitiveProfilePrimitive\000\377\001", (void*)primitiveProfilePrimitive}, + {(void*)_m, "primitiveProfileSample\000\377\001", (void*)primitiveProfileSample}, + {(void*)_m, "primitiveProfileSemaphore\000\000\014", (void*)primitiveProfileSemaphore}, + {(void*)_m, "primitiveProfileStart\000\000\001", (void*)primitiveProfileStart}, + {(void*)_m, "primitiveQuoLargeIntegers\000\000\001", (void*)primitiveQuoLargeIntegers}, + {(void*)_m, "primitiveRemLargeIntegers\000\000\001", (void*)primitiveRemLargeIntegers}, + {(void*)_m, "primitiveScreenDepth\000\377\000", (void*)primitiveScreenDepth}, + {(void*)_m, "primitiveScreenScaleFactor\000\377\000", (void*)primitiveScreenScaleFactor}, + {(void*)_m, "primitiveSetGCSemaphore\000\000\000", (void*)primitiveSetGCSemaphore}, + {(void*)_m, "primitiveSetLogDirectory\000\001\000", (void*)primitiveSetLogDirectory}, + {(void*)_m, "primitiveSubtractLargeIntegers\000\000\001", (void*)primitiveSubtractLargeIntegers}, + {(void*)_m, "primitiveSuspendBackingUpV2\000\002\000", (void*)primitiveSuspendBackingUpV2}, +#if TestingPrimitives + {(void*)_m, "primitiveTestShortenIndexableSize\000\001\000", (void*)primitiveTestShortenIndexableSize}, +#endif /* TestingPrimitives */ + {(void*)_m, "primitiveUtcWithOffset\000\001\000", (void*)primitiveUtcWithOffset}, + {(void*)_m, "primitiveVMCurrentThreadId\000\377\000", (void*)primitiveVMCurrentThreadId}, + {(void*)_m, "primitiveVoidReceiver\000\377\000", (void*)primitiveVoidReceiver}, + {NULL, NULL, NULL} +}; diff --git a/src/spur64.cog.livetyping/interp.h b/src/spur64.cog.livetyping/interp.h new file mode 100644 index 0000000000..e18acee0ba --- /dev/null +++ b/src/spur64.cog.livetyping/interp.h @@ -0,0 +1,57 @@ +/* Automatically generated by + CCodeGeneratorGlobalStructure * VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba + (Cog-eem.511, Compiler-ct.535) + */ + +#define VM_PROXY_MAJOR 1 +#define VM_PROXY_MINOR 17 + +#define SQ_VI_BYTES_PER_WORD 8 + +#define BaseHeaderSize 8 +#define BytesPerOop 8 +#define BytesPerWord 8 + +#define PrimErrGenericFailure 1 +#define PrimErrBadReceiver 2 +#define PrimErrBadArgument 3 +#define PrimErrBadIndex 4 +#define PrimErrBadNumArgs 5 +#define PrimErrInappropriate 6 +#define PrimErrUnsupported 7 +#define PrimErrNoModification 8 +#define PrimErrNoMemory 9 +#define PrimErrNoCMemory 10 +#define PrimErrNotFound 11 +#define PrimErrBadMethod 12 +#define PrimErrNamedInternal 13 +#define PrimErrObjectMayMove 14 +#define PrimErrLimitExceeded 15 +#define PrimErrObjectIsPinned 16 +#define PrimErrWritePastObject 17 +#define PrimErrObjectMoved 18 +#define PrimErrObjectNotPinned 19 +#define PrimErrCallbackError 20 +#define PrimErrOSError 21 +#define PrimErrFFIException 22 +#define PrimErrNeedCompaction 23 +#define PrimErrOperationFailed 24 +#define PrimErrInternalError 25 +#define PrimErrUninitialized 26 +#define PrimErrFFIMarshallingError 27 + +#define MinSmallInteger -1152921504606846976 +#define MaxSmallInteger 1152921504606846975 +#define NumSmallIntegerTagBits 3 + +#define MULTIPLEBYTECODESETS 1 + +#define STACKVM 1 +#define SPURVM 1 +#define DisownVMForFFICallFlag 8 +#define DisownVMFromCallbackFlag 16 +#define COGVM 1 +#if !defined(COGMTVM) +# define COGMTVM 0 +#endif + diff --git a/src/spur64.cog.livetyping/variable_order b/src/spur64.cog.livetyping/variable_order new file mode 100644 index 0000000000..c3f2c2e8d7 --- /dev/null +++ b/src/spur64.cog.livetyping/variable_order @@ -0,0 +1,18 @@ +stackLimit +stackPointer +framePointer +CStackPointer +CFramePointer +CReturnAddress +scavengeThreshold +freeStart +needGCFlag +specialObjectsOop +primFailCode +primTraceLogIndex +primitiveFunctionPointer +newMethod +instructionPointer +argumentCount +nextProfileTick +primTraceLog diff --git a/src/spur64.cog.livetyping/vmCallback.h b/src/spur64.cog.livetyping/vmCallback.h new file mode 100644 index 0000000000..dba08606dd --- /dev/null +++ b/src/spur64.cog.livetyping/vmCallback.h @@ -0,0 +1,41 @@ +/* Automatically generated by + CCodeGeneratorGlobalStructure * VMMaker.oscog-eem.3776 uuid: 5d93f8b7-2e84-42e1-9e0b-e752dda4d9ba + (Cog-eem.511, Compiler-ct.535) + */ + +#define VM_CALLBACK_INC 1 + +typedef struct _VMCallbackContext { + void *thunkp; + sqIntptr_t *stackp; + sqIntptr_t *intregargsp; + double *floatregargsp; + void *savedCStackPointer; + void *savedCFramePointer; + union { + sqIntptr_t valword; + struct { int low, high; } valleint64; + struct { int high, low; } valbeint64; + double valflt64; + struct { void *addr; sqIntptr_t size; } valstruct; + struct { sqIntptr_t a; sqIntptr_t b; } valstructll; // x86_64 Sys V ABI value struct register return types + struct { sqIntptr_t a; double b; } valstructld; + struct { double a; sqIntptr_t b; } valstructdl; + struct { double a; double b; } valstructdd; + }rvs; + void *savedMostRecentCallbackContext; + jmp_buf trampoline; + jmp_buf savedReenterInterpreter; + } VMCallbackContext; + +// The callback return type codes +#define retword 1 +#define retword64 2 +#define retdouble 3 +#define retstruct 4 +#define retstructll 5 +#define retstructld 6 +#define retstructdl 7 +#define retstructdd 8 + +